Seriell-Morse-Wandler
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