Strotmann.de
06 Nov 2011

amForth Morse Code

Konfiguration der Arduino Pins und die Basis-Wörter des Morse-Programms. Dieser Teil wird für jedes der drei Morse-Programme benutzt:

\ 2011-10-26  EW
\ 2011-11-02  CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)

\ make marker loads:
\   lib/misc.frt
\   lib/bitnames.frt
\   lib/ans94/marker.frt
\   ../../amforth/releases/4.2/core/devices/atmega328p/atmega328p.frt

marker --base--

decimal

PORTD 5 portpin: led1
PORTD 6 portpin: led2

PORTD 3 portpin: bz


\ --- buzzer -------------------------------------------------

\ 2 ms T_period =^= 500 Hz
\ Ton-Ausgabe
: buzz ( cycles -- )
  0 ?do 
    bz low 
    1ms 
    bz high 
    1ms 
  loop
;

\ Warten ohne Ton
: gap  ( cycles -- )
  0 ?do 
     bz high 
     1ms 
     bz high 
     1ms 
   loop
;

\ LED fuer 3ms blinken lassen
: blink ( cycles -- )
  led1 high
  0 ?do  
     1ms 1ms 1ms 
  loop
  led1 low
;


\ Umschalter zwischen Morse-Ton und Morse-LED
Edefer transmit
: piepser   ['] buzz  is transmit ;
: blinker   ['] blink is transmit ;

decimal
: kurz    50 transmit 50 gap ;     \ Signal kurz
: lang   150 transmit 50 gap ;    \ Signal lang

: Zend   100 gap ;           \ Pause zwischen Zeichen
: Wend   300 gap ;          \ Pause zwischen Worten

: init
  led1 pin_output led1 low
  led2 pin_output led2 low
  bz   pin_output

  piepser
;

Simple Implementierung

Die erste Implementierung ist sehr gradlinig mit einem Forth-Wort für jeden Morse-Buchstaben und einer grossen IF-THEN Konstruktion im Hauptteil:

\ 2011-10-26  EW
\ 2011-11-02  CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)

marker --morse--

\ je ein Forth-Wort pro Morse-Buchstabe
: _A  kurz lang Zend ;
: _B  lang kurz kurz kurz Zend ;
: _C  lang kurz lang kurz ;
: _D  lang kurz kurz Zend ;
: _E  kurz Zend ;
: _F  kurz kurz lang kurz Zend ;
: _G  lang lang kurz Zend ;
: _H  kurz kurz kurz kurz Zend ;
: _I  kurz kurz Zend ;
: _J  kurz lang lang lang Zend ;
: _K  lang kurz lang Zend ;
: _L  kurz lang kurz kurz Zend ;
: _M  lang lang Zend ;
: _N  lang kurz Zend ;
: _O  lang lang lang Zend ;
: _P  kurz lang lang kurz Zend ;
: _Q  lang lang kurz lang Zend ;
: _R  kurz lang kurz Zend ;
: _S  kurz kurz kurz Zend ;
: _T  lang Zend ;
: _U  kurz kurz lang Zend ;
: _V  kurz kurz kurz lang Zend ;
: _W  kurz lang lang Zend ;
: _X  lang kurz kurz lang Zend ;
: _Y  lang kurz lang lang Zend ;
: _Z  lang lang kurz kurz Zend ;


\ Variable zum Speichern des original 'emit'
variable o-emit

\ neues Emit zum Ausgeben von Zeichen als Morse-Code
: morseemit ( key -- )
  dup o-emit @ execute        ( original 'emit' aufrufen )
  dup [char] a = if _A then
  dup [char] b = if _B then
  dup [char] c = if _C then
  dup [char] d = if _D then
  dup [char] e = if _E then
  dup [char] f = if _F then
  dup [char] g = if _G then
  dup [char] h = if _H then
  dup [char] i = if _I then
  dup [char] j = if _J then
  dup [char] k = if _K then
  dup [char] l = if _L then
  dup [char] m = if _M then
  dup [char] n = if _N then
  dup [char] o = if _O then
  dup [char] p = if _P then
  dup [char] q = if _Q then
  dup [char] r = if _R then
  dup [char] s = if _S then
  dup [char] t = if _T then
  dup [char] u = if _U then
  dup [char] v = if _V then
  dup [char] w = if _W then
  dup [char] x = if _X then
  dup [char] y = if _Y then
  dup [char] z = if _Z then
  dup bl           = if Wend then  ( Leerzeichen = Wortende )
  drop
  Zend   ( Zeichenende )
;

\ neues 'morseemit' in 'emit' eintragen
: morse
  ['] emit defer@ o-emit !
  ['] morseemit is emit
;

\ original 'emit' wieder herstellen
: endmorse
  o-emit @ is emit 
;

Implementierung mit einer Sprungtabelle

In dieser Version des Morse-Programms nutzen wir immer noch Forth-Wörter für die Signale, aber ersetzten die Grosse IF-THEN Verzweigung durch eine Spring-Tabelle. Die Sprung-Tabelle enhält 256 Einträge für die 256 ASCII-Zeichen. In den Tabellen-Einträgen befindet sich die Adresse des jeweiligen Forth-Worts (Execution Token = xt), welches die Signale für den Morse-Code ausgibt. Ist ein Tabelleneintrag '0' so ist dieser Eintrag nicht gefüllt und es wird kein Morse-Code ausgegeben.

\ 2011-10-26  EW
\ 2011-11-02  CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
\ 2nd version

marker --morse--

: _A  kurz lang Zend ;
: _B  lang kurz kurz kurz Zend ;
: _C  lang kurz lang kurz ;
: _D  lang kurz kurz Zend ;
: _E  kurz Zend ;
: _F  kurz kurz lang kurz Zend ;
: _G  lang lang kurz Zend ;
: _H  kurz kurz kurz kurz Zend ;
: _I  kurz kurz Zend ;
: _J  kurz lang lang lang Zend ;
: _K  lang kurz lang Zend ;
: _L  kurz lang kurz kurz Zend ;
: _M  lang lang Zend ;
: _N  lang kurz Zend ;
: _O  lang lang lang Zend ;
: _P  kurz lang lang kurz Zend ;
: _Q  lang lang kurz lang Zend ;
: _R  kurz lang kurz Zend ;
: _S  kurz kurz kurz Zend ;
: _T  lang Zend ;
: _U  kurz kurz lang Zend ;
: _V  kurz kurz kurz lang Zend ;
: _W  kurz lang lang Zend ;
: _X  lang kurz kurz lang Zend ;
: _Y  lang kurz lang lang Zend ;
: _Z  lang lang kurz kurz Zend ;

\ erstelle Tabelle fuer execution token
variable mtable 256 cells allot

\ loesche Tabelle
mtable 256 cells erase

\ Hilfswort zum fuellen der Tabelle
: >mtable ( xt c -- )
  cells mtable + !
;

\ Tabelle zur kompilierzeit fuellen
' _A char a >mtable
' _B char b >mtable
' _C char c >mtable
' _D char d >mtable
' _E char e >mtable
' _F char f >mtable
' _G char g >mtable
' _H char h >mtable
' _I char i >mtable
' _J char j >mtable
' _K char k >mtable
' _L char l >mtable
' _M char m >mtable
' _N char n >mtable
' _O char o >mtable
' _P char p >mtable
' _Q char q >mtable
' _R char r >mtable
' _S char s >mtable
' _T char t >mtable
' _U char u >mtable
' _V char v >mtable
' _W char w >mtable
' _X char x >mtable
' _Y char y >mtable
' _Z char z >mtable
' Wend   bl >mtable

variable o-emit

: morseemit ( key -- )
  dup o-emit @ execute ( altes emit ausfuehren )
  255 and              ( sicherstellen das wir nur 0-255 Werte bekommen )
  cells mtable + @       ( execution token holen ) 
  dup if execute       ( wenn > 0 dann ausfuehren ) 
      else drop then   ( ansonsten wegwerfen )
;

: morse
  ['] emit defer@ o-emit !
  ['] morseemit is emit
;

: endmorse
  o-emit @ is emit 
;

gepackte Morse-Code Signale

Morse-Code Signale als Forth-Wörter speichern ist einfach, verbraucht aber recht viel Flash-Speicher. Morse-Code Signale können in je einem Byte (8 bit) gespeichert werden. In der dritten Version des Morse-Programms werden bis zu fünf Morse-Signale in den oberen 5 Bits gespeichert ( 0 = kurz, 1 = lang ), die unteren 3 Bits speichern die Anzahl der Signale im Morse-Code. Hierdurch reduziert sich der Speicherverbrauch des Programms.

Die Tabelle mit dem gepackten Morse-Code wird vom Forth-Interpreter zur Kompilierzeit erstellt und verbraucht bei der Ausführung keine extra Rechenzeit:

\ 2011-10-26  EW
\ 2011-11-04  CS
\ arduino duemilanove + danger shield
\ morse code stuff (Potsdam/Augsburg/Oberhausen)
\ 3nd version

marker --morse--

\ erstelle Tabelle fuer gepackte morse daten
variable mtable 256 allot

\ loesche Tabelle
mtable 256 erase

\ Hilfswort zum fuellen der Tabelle
: >mtable ( gepackter-morsecode c -- )
  mtable + c!
;

\ Hilfswort zum packen vor morse code
: pack ( #zeichen code -- pcode )
  3 lshift swap 7 and or
;

: unpack ( pcode -- #zeichen code )
  dup 7 and swap 3 rshift 
;

: binary 2 base ! ;

\ Tabelle zur kompilierzeit fuellen
binary 00010 decimal 2 pack char a >mtable
binary 00001 decimal 4 pack char b >mtable
binary 01010 decimal 4 pack char c >mtable
binary 00001 decimal 3 pack char d >mtable
binary 00000 decimal 1 pack char e >mtable
binary 00100 decimal 4 pack char f >mtable
binary 00011 decimal 3 pack char g >mtable
binary 00000 decimal 4 pack char h >mtable
binary 00000 decimal 2 pack char i >mtable
binary 01110 decimal 4 pack char j >mtable
binary 00101 decimal 3 pack char k >mtable
binary 00010 decimal 4 pack char l >mtable
binary 00011 decimal 2 pack char m >mtable
binary 00001 decimal 2 pack char n >mtable
binary 00111 decimal 3 pack char o >mtable
binary 00110 decimal 4 pack char p >mtable
binary 01101 decimal 4 pack char q >mtable
binary 00010 decimal 3 pack char r >mtable
binary 00000 decimal 3 pack char s >mtable
binary 00001 decimal 1 pack char t >mtable
binary 00001 decimal 3 pack char u >mtable
binary 00001 decimal 4 pack char v >mtable
binary 00011 decimal 3 pack char w >mtable
binary 01001 decimal 4 pack char x >mtable
binary 01011 decimal 4 pack char y >mtable
binary 00011 decimal 4 pack char z >mtable

variable o-emit

: domorse ( code #signale -- ) 
  0 ?do                ( Schleife Anzahl der Signale )
    dup 1 and          ( erstes bit maskieren )
    if lang            ( bit gesetzt = lang )
    else kurz then     ( sonst kurz )
    2/                 ( bits nach rechts schieben )
  loop
  drop Zend            ( Zeichenende )
;

: morseemit ( key -- )
  dup o-emit @ execute ( altes emit ausfuehren )
  255 and              ( sicherstellen das wir nur 0-255 Werte bekommen )
  dup bl = if          ( leerzeichen? )
    Wend               ( Wortende zeit warten )
  then 
  mtable + c@          ( gepackten morse code holen )
  unpack               ( entpacken )
  domorse              ( signale ausgeben )
;

: morse
  ['] emit defer@ o-emit !
  ['] morseemit is emit
;

: endmorse
  o-emit @ is emit 
;

Aufgaben:

  • Wie kann das Morse-Code Programm weiter optimiert werden (weniger Speicherverbrauch)?
  • erweitere das Programm, so das auch Zifferns als Morse-Code ausgegeben werden
  • erweitere das Programm, so das auch Sonderzeichen als Morse-Code ausgegeben werden

Antworten auf diese Aufgaben können auf Mastodon gepostet werden. Bitte Geduld wenn ich nicht sofort antworte, ich kann leider nicht immer online sein.

Other posts
Creative Commons License
strotmann.de by Carsten Strotmann is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License .