Seriell-Morse-Wandler


von Ralf Beesner, DK5BU
Elektronik-Labor   Projekte   AVR 




Serielle Schnittstellen gelten zwar als veraltet, sind aber trotzdem nicht totzukriegen. Zahlreiche Geräte geben ihre Daten immer noch über serielle Schnittstellen aus.

Geht es nur um sehr geringe Datenmengen, kann man auf einen Terminal- PC als Ausgabegerät verzichten und stattdessen die Informationen mit einem Microcontroller aufbereiten und auf einem LCD- Display anzeigen - im Web findet man einige Projekte, die sich damit beschäftigen. Viel "cooler" finde ich jedoch, die seriellen Informationen in Morsezeichen umzuwandeln und akustisch auszugeben.

Ich habe einige Programme beigefügt; das erste ist für den ATmega8. An Hardware werden neben dem obligatorischen Pegelwandler für die serielle Schnittstelle (z.B. MAX 232) nur ein Piezoschwinger an PB3 und zwei  Potentiometer an PC0/ADC0 und PC1/ADC1 (Tonhöhe und Morsegeschwindigkeit) benötigt.

Mit minimalen Änderungen läuft es auch auf dem ATmega88. Das zweite Programm ist auf die Atmega88- Hardware des Franzis- Lernpakets "Mikrocontroller-Technik mit Bascom" zugeschnitten. Auch hier ist der Piezoschwinger an PB3 anzuschließen; das bereits auf der Platine vorhandene Poti (an ADC7) dient zur Geschwindigkeitseinstellung. Die Tonhöhe ist über eine Konstante fest eingestellt. Der USB-Anschluss bildet die serielle Schnittstelle. Allerdings ist Hyperterm als Terminalprogramm ungeeignet, weil es die DTR- Leitung aktiviert und damit den ATmega88 auf Reset schaltet. Das interne Bascom- Terminalprogramm funktioniert jedoch.

Die restlichen 3 Programme sind für den ATtiny13 und die Hardware des Franzis- Lernpakets "Mikrocontroller" gedacht. Es muss lediglich ein Piezoschwinger an PB0 angeschlossen werden; Tonhöhe und Geschwindigkeit sind konstant.


Gemeinsamkeiten und Unterschiede der Programme

Der zentrale Teil - Umwandeln von Ascii- Zeichen in Morsesignale - erfolgt ähnlich wie bei meinen Projekten "Morsebake", "Morsethermometer" und "Morseuhr". Da hier nicht nur eine Teilmenge des Morsealphabets benutzt wird, sondern alle Zeichen, ist die Umcodierung abweichend gelöst. Die Umwandlungstabelle steckt in mehreren DATA- Zeilen, in denen mit dem Befehl "Lookup" nachgeschlagen wird.

Es wäre auch möglich, diese Daten bei Programmstart in eine Array-Variable von 91 bzw. 122 Bytes einzulesen und dort nachzuschlagen, aber RAM ist ja besonders bei den kleinen Microcontrollern knapp und kostbar.

Da die Morseausgabe sehr langsam erfolgt und die Bytes sehr viel schneller über die serielle Schnittstelle "eintrudeln", müssen die Daten zwischengespeichert werden. Ist ein Hardware-UART vorhanden, nimmt einem BASCOM die Arbeit ab: mit dem Befehl "Config Serial" kann man einen Buffer von max. 254 Zeichen konfigurieren und muss nur noch Interrupts allgemein freigeben, da BASCOM für das Puffern im Hintergrund eine Interrupt-Routine einrichtet.

Beim Atmega8 und ATmega88 ist man also "fein raus"; beim ATtiny13 muss man jedoch den Software- UART benutzen, und der stellt keine Buffer zur Verfügung.

Das Programm "T13-Seriell-Morse-Wandler-1.bas" demonstriert das Problem: mit "GET" und "PUT" kann man sehr einfach Bytes von der Software- UART abholen und dort wieder ablliefern, aber während die Morseausgabe läuft, wird der Software- UART nicht abgefragt - die zwischenzeitlich eintrudelnden Bytes gehen ins Leere.


Buffern "mit BASCOM- Bordmitteln"

Im Programm "T13-Seriell-Morse-Wandler-2.bas" habe ich versucht, den Buffer "zu Fuß" zu bauen.

Der RX- Pin des Software-UART (PB2) wird mit einem Pin Change Interrupt überwacht. Ändert sich das Potential, weil gerade ein neues Startbit einläuft, verzweigt das Programm kurz in eine Interruptroutine. Sie nimmt das Zeichen entgegen, gibt es ggf. als Echo auf dem TX- Pin des Terminals gleich wieder aus und hängt es an einen String namens "Pufferstring" an. Das geht so schnell, dass die parallel laufende Morseausgabe nicht merklich verzögert wird.

Im Hauptprogramm, der Routine "Zeichen_lesen", wird das älteste "linke" Zeichen des Pufferstrings ausgelesen und dann der Pufferinhalt um eine Stelle nach "links" geschoben. Zuvor wird jedoch die Länge des Pufferstrings geprüft, ist er leer, muss das Programm "auf die Bremse treten".

Leider benötigt die Interrupt- Routine 32 Bytes des knappen SRAM, da sie alle 32 Register auf den Stack retten muss. Daher ist  der Puffer sehr klein (nur 15 Bytes).


Eigentlich ist die Verwendung eines Strings ein Umweg, da es ja Bytes sind, die gepuffert werden sollen. Aber Strings lassen sich mit BASCOM leichter bearbeiten, da ist die Hin- und Rückwandlung in Kauf zu nehmen.

In Roland Walters "AVR Mikrocontroller Lehrbuch" habe ich einen Weg gefunden, zumindest die Rückwandlung mit Hilfe einer Overlay- Variable bytesparend zu umgehen (sein Beispiel  beschreibt einen Buffer für die Senderichtung eines Hardware-UART). Im Programm "T13-Seriell-Morse-Wandler-2-overlay.bas" ist das sinngemäß übernommen und spart ein Byte SRAM und ein paar Prozent Flash.

Das Buffern mit BASCOM- Befehlen ist allerdings recht langsam; bei den weiter unten beschriebenen Experimenten mit der Kommandozeile "verschluckte" sich der ATtiny13 an den zu rasch eintreffenden Zeichen (nach Umstellung der Taktrate von 1,2 MHz auf 4,8 MHz funktionierte es jedoch). Die Eingabeumleitung legt auch nicht die RTS- Leitung und die DTR- Leitung auf High; man muss den ATtiny13 (bzw. den Eingang des Spannungsreglers)  extern mit 5 V speisen.


Weitere Hinweise:

Die Programme  wurden unter Windows XP  im Zusammenspiel mit dem Windows- Standard- Programm "Hyperterminal" getestet. 9600 bit/s 8N1, "doofes" TTY, nur CR, kein LF, kein lokales Echo (der Mikrocontroller sendet deshalb die Zeichen zurück, damit sie auf dem Terminal erscheinen)

Je nach Terminalprogramm bzw. serieller Hardware  wird es erforderlich sein, im Seriell-Morse-Wandler das Echo abzuschalten (auszukommentieren) und/oder mit dem Zeilenendezeichen zu experimentieren.

Windows schleppt ein paar nette DOS- Altlasten mit sich herum (die letztendlich aus der UNIX- Welt stammen). Auf der Kommandozeile kann man per Ausgabeumleitung Texte oder die Inhalte von Dateien auf die serielle Schnittstelle umleiten.  So kann man mit dem Befehl

 "echo test 12345 > com1"

den String "test 12345" auf den Morsewandler senden. Mit

"cat 'Dateiname' > com1"

kann man sich auch eine Datei in Morse vorlesen lassen, und in Batchdateien lassen sich kleine Programme zusammenfügen, die ihre Ausgaben jeweils auf den Morsewandler leiten.

Solche Spielchen ermöglicht Windows anscheinend nur mit echten seriellen Schnittstellen.

Falls es mit einem echten Com- Port Probleme gibt, sollte man unter "Systemsteuerung" -  "System" - "Gerätemanager" - "Hardware" - "Anschlüsse ..." - "Kommunikationsanschluss (COM1) - Eigenschaften" nachschauen, ob dort 9600 bit/s 8N1, Flussteuerung:keine"  eingestellt ist.

Unter Linux ist es recht einfach, Hardware- Meldungen auf den Morsewandler zu geben. Zum Beispiel kann man sich den Ladezustand der Notebook- Batterie mit

cat /proc/acpi/battery/CMB1/state | grep remaining > /dev/ttyS0

 in Morse ausgeben lassen; der Vorgang läßt sich auch leicht automatisieren und zu festen Zeiten wiederholen. 

Unter Linux funktioniert die Ausgabeumleitung eigentlich auch mit seriellen USB- Schnittstellen, allerdings nur, wenn lediglich RXD, TXD und Masse angeschlossen werden. Bei der Hardware des "`Franzis- Lernpakets BASCOM"' tritt auch unter Linux das Problem auf, dass die DTR- Leitung Resets auslöst.



'
' Programm gibt über die serielle Schnittstelle eingehende Zeichen akustisch als Mosecode aus.

' Pin PB0 wird als Timer0-Ausgang OC0a verwendet, Ton wird durch Umschalten des
' Pins als Eingang / Ausgang ein- und ausgeschaltet
'
' Pin PB1 und PB2 bilden einen Software- UART (eingegebene Zeichen werden als Echo auf das Terminal zurückgesendet)
' Pin PB3 schaltet Echo um (wird PB3 auf Masse gezogen, wird das an PB1 empfangene Byte an PB2 wieder ausgegeben)
'
' Version mit Terminalbuffer: Pegeländerungen am seriellen Eingang lösen einen Interrupt aus. Die Zeichen werden
' im Hintergrund in einen FIFO geschrieben und durch die Morseroutine Zug um Zug ausgelesen.
' Die Interruptroutine benötigt leider 32 Byte SRAM. Der FIFO darf daher nur 16 Zeichen groß sein.
'
' Der FIFO nutzt die Overlay- Funktionalität. Der FIFO wird mit den komfortablen String- Manipulationsbefehlen
' bearbeitet; das Auslesen des ältesten Zeichens im FIFO erfolgt jedoch direkt als Byte statt als 1-Zeichen-String;
' das erspart die Umwandlung String -> Byte
'
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
'
' Grundeinstelllungen:
$regfile = "ATtiny13.dat"
$crystal = 1200000
$hwstack = 40 ' wegen Interrupt-Routine
$swstack = 0 ' wird nicht gebraucht
$framesize = 0 ' dito
Baud = 9600

'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

'Variablen und Konstanten dimensionieren:
'

Dim Recbyte As Byte
Dim Recchar As String * 1
Dim I As Byte 'Für temporären Gebrauch als Schleifenzähler
Dim M As Byte 'Index des aktuellen Morsebuchstabens in unserer Tabelle
Dim L As Byte ' für niederwertigstes Bit beim bitweisen Erzeugen des Morsezeichens

Dim Pufferstring As String * 16
Dim Puffer(16) As Byte At Pufferstring Overlay

Const Tonhoehe = 20 'Tonhöhe
Const Punktlaenge = 100 'Punktlänge
Const Strichlaenge = Punktlaenge * 3 'Strichlänge
Const Wortpause = Punktlaenge * 5 'Länge Wortpause


'
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
'Initialisierung des Programms:
'
'

' Morseton mit Timer0 erzeugen:
'
' Timer0 zählt das Register TCNT0 hoch. Es wird mit Register OCR0A verglichen.
' Wenn TCNT0 = OCR0A, wird Output OC0A/PB0 umgeschaltet (getoggled)
' Timer0 erzeugt so Dauerton; er wird jedoch nur an den Ausgangspin durchgeschaltet,
' wenn das Datenrichtungs- Register DDRB.0 = 1 ist. Bei 0 wird der Ton unterdrückt.
'
Config Timer0 = Counter , Prescale = 64 , Compare A = Toggle , Clear Timer = 1
Ocr0a = Tonhoehe


Acsr.acd = 0 'Analog-Komparator ausschalten, spart etwas Strom
Pcmsk.2 = 1 ' PCINT für Port B.2 freigeben
Gimsk.pcie = 1 ' PCints aktivieren
Sreg.7 = 1 ' Interrupts gernerell freigeben

Portb = &B00011111 ' floatende Eingänge vermeiden

Open "comb.2:9600,8,n,1,inverted" For Input As #1 ' Software- UART Input an PortB.2
Open "comb.1:9600,8,n,1,inverted" For Output As #2 ' Software- UART Output an PortB.1

On Pcint0 Serielle_lesen


'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -



Main:


Do

Gosub Zeichen_lesen

Loop



Zeichen_lesen:


If Puffer(1) = 0 Then ' Puffer leer
Goto Zeichen_lesen
End If

M = Asc(puffer(1)) ' Ascii- Wert des untersten Zeichens im Puffer ermitteln
M = Lookup(m , Morsetabelle) ' Morsezeichen aus Tabelle holen
Gosub Morse
Pufferstring = Mid(pufferstring , 2) ' das unterste Zeichen im Pufferstring entfernen

Return



' Morsezeichenerzeugung:

Morse:

If M = 0 Then ' Sonderfall: Leerzeichen;
Waitms Wortpause
Goto Zeichenende
End If

For I = 1 To 8
If M = 1 Then ' Das Byte hat nur noch den Wert 1; Zeichenende!
Goto Zeichenende
End If
L = M And &B00000001 ' niederwertigstes Bit lesen
Ddrb.0 = 1
If L = 1 Then
Waitms Strichlaenge ' ist das Bit 1 -> dah
Else
Waitms Punktlaenge ' ist das Bit 0 -> dit
End If
Ddrb.0 = 0
Waitms Punktlaenge ' Pause innerhalb des Morsezeichens
Shift M , Right , 1 ' Bits um eine Stelle nach rechts shiften
Next I
Zeichenende:
Waitms Strichlaenge ' Pause zwischen Morsezeichen

Return



End


Serielle_lesen:

Pcmsk.2 = 0 ' Solange Bits reinkommen, PCINT sperren
Get #1 , Recbyte ' Byte lesen
Put #2 , Recbyte ' Byte auf Terminal senden (Echo)
Recchar = Chr(recbyte) ' Byte in ASCII- Zeichen wandeln
Pufferstring = Pufferstring + Recchar ' ASCII- Zeichen ans Ende des Pufferstrings anhängen
Pcmsk.2 = 1 ' PCINT wieder freigeben

Return



'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Morsetabelle:

'Ascii 0 - 32 ; hier wird nur Linefeed in <kn> umgesetzt

Data 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , &B00101101 , 0 , , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0
Data 0 , 0 , 0 , 0 , 0 , 0 , 0


' Ascii 32 - 47 -> _!"#$%&'()*+,-./
Data 0 , 0 , &B01010010 , 0 , 0 , 0 , &B00101010 , 0 , &B00101101 , &B01101101 , 0 , &B00101010 , &B01110011 , &B01100001
Data &B01101010 , &B00101001

'Ascii 48 - 57 -> 0123456789
Data &B00111111 , &B00111110 , &B00111100 , &B00111000 , &B00110000 , &B00100000 , &B00100001 , &B00100011 , &B00100111 , &B00101111

'Ascii 58 - 64 -> :;<=>?@
Data &B01000111 , &B01110011 , &B00101101 , &B00110001 , &B01101101 , &B01001100 , 0

'Ascii 65 - 90 ABCDEFGHIJKLMNOPQRSTUVWXYZ
Data &B00000110 , &B00010001 , &B00010101 , &B00001001 , &B00000010 , &B00010100 , &B00001011 , &B00010000 , &B00000100
Data &B00011110 , &B00001101 , &B00010010 , &B00000111 , &B00000101 , &B00001111 , &B00010110 , &B00011011 , &B00001010
Data &B00001000 , &B00000011 , &B00001100 , &B00011000 , &B00001110 , &B00011001 , &B00011101 , &B00010011

'Ascii 91 - 96 -> Muell

Data 0 , 0 , 0 , 0 , 0 , 0

' Ascii 97 - 122 abcdrfghijklmnopqrstuvwxyz

Data &B00000110 , &B00010001 , &B00010101 , &B00001001 , &B00000010 , &B00010100 , &B00001011 , &B00010000 , &B00000100
Data &B00011110 , &B00001101 , &B00010010 , &B00000111 , &B00000101 , &B00001111 , &B00010110 , &B00011011 , &B00001010
Data &B00001000 , &B00000011 , &B00001100 , &B00011000 , &B00001110 , &B00011001 , &B00011101 , &B00010011

'Ascii 123 - 127 -> Muell

Download:
Quelltexte und Hexfiles für Tiny13, Mega8 und  Mega88

Dieser Artikel als PDF


Elektronik-Labor   Projekte   AVR