Als mir die Idee einer Morseuhr in den Sinn kam,
dachte ich
eigentlich an eine "stilechte" Lösung, die nicht nur Morse
ausgibt,
sondern die auch vollständig mit einer Morsetaste gesteuert wird.
Das war mir
zunächst zu aufwendig, und so entstanden die Morseuhren 1 und 2,
die über DIP-Schalter vorkonfiguriert werden und die
gewünschten Einstellungen nach einem
Reset aus den Dipschalter-Stellungen übernehmen.
Die ursprüngliche Idee hat sich aber gehalten, und mit
meinem Morseterminal hatte ich bereits die Subroutinen für die Morse-Eingabe
beisammen. So war es nicht mehr weit bis zur vollständigen Lösung der
Ursprungsidee. Da noch Platz im Flash war, ist noch eine "fat
version" mit Weckalarm dazugekommen.
Außerdem zeigte sich, dass man nicht zwingend einen 32 kHz-
Uhrenquarz und einen ATMega- Mikrocontroller mit speziellem Lowpower-Uhrenmodus
einsetzen muss; auch mit einem Standardquarz 3,6864 MHz und einem ATtiny 45 lässt
sich der Grund- Stromverbrauch im Idle- Modus auf Werte drücken, die für einen
Batteriebetrieb mit Mignonzellen akzeptabel sind (etwa 0,2 mA, also 1,8 Ah /
Jahr bei 3 V).
Hardware
Die Betriebspannung beträgt 3 Volt, sie wird aus 2
Mignonzellen gewonnen. Die Platine ist so bemessen, dass sie mit 2 Schrauben
auf der Rückseite eines Zweier-Batteriehalters befestigt werden kann.
Der Quarz wird ohne die im Datenblatt empfohlenen
Bürdekapazitäten (12 -22 pF) betrieben. Der Oszillator schwingt trotzdem
sicher, die Frequenz ist lediglich ein paar hundert Hz zu hoch. Das ist aber
durchaus erwünscht, denn damit läuft die Uhr stets etwas zu schnell; es ist
einfacher, sie per Software durch Einlegen von ein paar µsec Wartezeit pro
Sekunde oder durch einige Sekunden Wartezeit um Mitternacht etwas langsamer zu
machen.
Bedienung
Die Uhr wird vollständig über Morse- Eingaben gesteuert.
Nach Einlegen der Batterien gibt sie zunächst die Uhrzeit 0 Uhr aus. Das
Viertelstunden- Schlagwerk (im folgenden als "Gong" bezeichnet) ist
eingeschaltet.
Folgende Befehle stehen zur Verfügung; sie bestehen aus
einem Zeichen:
Die Zeitsetz- Befehle erwarten eine vierstellige Zahl
(Eingabe ohne Zwischenraum und ohne Zwischenzeichen); die Ein- Ausschaltbefehle
erwarten "0" oder "1", und die Morsegeschwindigkeit ist als
2-stellige Zahl einzugeben.
Ist die Eingabe der Zahl(en) vollständig, wird (bzw. werden)
sie wiederholt.
Wurden nicht Ziffern, sondern andere Zeichen eingegeben,
erfolgt sofort die Ausgabe "RPT" (repeat), ist die Anzahl der Ziffern
zu gering, erfolgt nach einer gewissen Wartezeit ebenfalls die Ausgabe
"RPT". Die Uhr fällt in beiden Fällen in den Idle-Modus zurück; das
heißt, vor einer erneuten Eingabe der Zahl(en) muss zunächst das Kommando neu
eingegeben werden.
In der Morsegeschwindigkeits- Subroutine wird zusätzlich
geprüft, ob die Geschwindigkeit in einem sinnvollen Bereich liegt (zwischen 10
und 30 WPM). Ist dies nicht der Fall, wird "RPT" ausgegeben und die
Geschwindigkeit auf 20 WPM zurückgesetzt, damit die Uhr bedienbar bleibt.
Es erfolgt aber keine vollständige Prüfung auf
Plausibilität; Zeiteingaben wie "1299" sind möglich; die Zeichen
werden ja nach der Eingabe wiederholt und der Nutzer muss prüfen, ob die
Eingabe sinnvoll war. Lediglich Zeiteingaben, die größer als 23:59 Uhr sind,
werden verworfen und es wird "RPT" ausgegeben.
Einige
Erläuterungen zur Software
Das wichtigste Unterprogramm ist die Interrupt- Routine, die
ein mal pro Sekunde durch den Timer ausgelöst wird. Sie addiert die Sekunden
und rechnet sie in (Tages-) Minuten um; ist ein Tag verstrichen (1440 min.),
werden die Tagesminuten im Hauptprogramm wieder auf Null gesetzt.
Die Zeitberechnung ist in der Interrupt- Routine
angesiedelt, damit auch dann, wenn der Mikrocontroller gerade ausgelastet ist
(z.B. mit den relativ lange dauernden Morseausgaben), keine Minuten- oder
Stundenwechsel verloren gehen können. Die Tagesminuten sind sehr schnell
errechnet; die Interrupt- Routine ist daher sehr kurz, sie stellt aber dennoch
eine eindeutige Zeitinformation bereit.
Das Hauptprogramm ruft nur kurz die Zeitberechnungen und die
Tastaturabfragen auf und fällt dann bis zum nächsten Interrupt in den Idle-Mode.
Damit die Uhr trotzdem verzögerungsfrei auf Tastendrücke reagiert, sind für die
beiden Tasten- Eingänge "Pin Change Interrupts" aktiviert.
Kann man den Powerdown- Modus nutzen, werden fast alle
Funktionsblöcke des Mikrocontrollers durch einen einzigen Registereintrag
abgeschaltet. Im (hier wegen des Quarztaktes notwendigen) Idle-Modus bleiben
die meisten Funktionsblöcke jedoch wach und verbrauchen Strom; man muss sie
einzeln abschalten. Genutzt habe ich die Register PRR und DIDR0 - vielleicht
gibt es noch weitere Möglichkeiten der Stromeinsparung.
Die meisten Unterprogramme habe ich aus älteren Projekten
kopiert: die Subroutinen Zeitberechnung, Gong (früher: Schlagwerk), Zeitausgabe
und Morse stammen aus der Morseuhr 2. Die Routine "Decodekeyer"
stammt aus dem Projekt Morse-Terminal. Sie sind dort näher erläutert.
Hinzugekommen sind:
Die Subroutine Menue. Eine Select - Case- Struktur
vergleicht eingegebene Zeichen mit dem ASCII- Wert der Befehlszeichen " ?
,Z,G,C,M " und verzweigt in weitere Unterprogramme. Andere Zeichen werden
ignoriert.
Die Routinen Zeitsetzen und Speed erwarten die Eingabe
mehrstelliger Zahlen. Diese werden im Unterprogramm Readnumbers erfasst, auf
Zulässigkeit geprüft und nach Eingabe der letzen erwarteten Ziffer zur
Kontrolle und Bestätigung ausgegeben.
Der Befehl "G" würde eigentlich mit einer
Bitvariable (0 oder1) auskommen; aus Vereinfachungsgründen ist es jedoch ein
Byte; alles andere als "1" gilt als 0"(für: abgeschaltet).
Die zusätzlichen Funktionsblöcke der "fat version"
sind hoffentlich ausreichend durch die Kommentare im Quelltext erklärt.
Ausblick
Mit geringen Änderungen der Software und Umflashen von
Fusebits kann man die Uhr prinzipiell mit einem 32 kHz- Uhrenquarz betreiben.
Jedoch ließ sich der Mikrocontroller nach Umstellung auf den langsamen Takt
nicht mehr flashen - weder ließ sich ein Programm aufspielen, noch ließen sich
die Fusebits wieder auf den Betrieb mit einem Quarz oder mit dem internen RC-
Oszillator zurücksetzen. Retten ließ sich der Mikrocontroller nur noch mit
einem HV-Programmer.
Das schien mir für eine Bauanleitung dann doch zu
"wacklig". Schade, denn der Stromverbrauch in den Idle- Phasen betrug
mit dem 32 kHz- Takt nur noch 0,08 mA.
' Attiny-45-Morseuhr Version 1.0 fat
'
' gibt alle 15 min. die Minutenzahl aus und zur vollen Stunde die Stundenzahl
' ("es ist X Uhr")
'
' ausserdem kann die Zeit durch Eingabe von "T" unmittelbar abgefragt werden
'
' Befehlsliste:
'
' ? Auflistung aller Befehle
' Z Zeit setzen
' T Zeit abfragen
' W Weckzeit setzen
' A 1/0 Alarm ein /aus
' E Alarm Ende
' G 1/0 Gong (Schlagwerk) ein/aus
' M Morsegeschwindigkeit setzen
' C Check -> Ausgabe von Alarmstatus und Gongstatus
' K Korrektur ; N Sekunden Wartezeit um Mitternacht ; N = 1 ...9
'
' Pinbelegung:
'
' PB0 Buzzer
' PB1 Punktkontakt
' PB2 Strichkontakt
' PB3 und PB4: Quarz
'
'
' Quarz schwingt mit 3,686400 MZz; Oszillator wird duch 8 geteilt (Takt ist dann 460800 Hz)
'
' Timer1- Vorteiler teilt durch 2048, Timer 1 dann durch 225 -> Sekundenimpulse
'
' Nach 60 Interrupts ist eine Minute um; mit Minuten wird weitergerechnet.
'
' Da der Quarz ohne Bürdekondensatoren betrieben wird, ist der Timer grundsätzlich etwas zu schnell
' Ausgleich durch Wait- Befehl in der Interrupt- Routine ( 200 µsec * 86400 sec/Tag = 17,28 sec/Tag )
'
' Fusebits: lfuse: 0x7d hfuse: 0xdf (Taktquelle Quarzoszillator, Teiler 1/8 , kein Brown-Out)
'
'----------------------------------------------------------
$regfile = "Attiny45.dat"
$crystal = 460800 ' 3,6864 MHz / 8
$hwstack = 32
$swstack = 8
$framesize = 24
' Variablen für Zeitberechnung
Dim S As Word ' Sekunden
Dim T As Word ' Tagesminuten 0 ..... 1440
Dim Korr As Byte
Dim Mi As Word ' Minuten- Rest zur vollen Stunde
Dim V As Word ' Minuten- Rest zur vollen Viertelstunde
Dim H As Word ' Stunden
Dim Zh As Word ' Zehnerstelle Stunden
Dim Eh As Word ' Einerstelle Stunden
Dim Zmi As Word ' Zehnerstelle Minuten
Dim Emi As Word ' Einerstelle Minuten
Dim Wmi As Word ' Minuten Weckzeit
Dim Wh As Word ' Stunden Weckzeit
Dim Wzh As Word ' Zehnerstelle Weckstunden
Dim Weh As Word ' Einerstelle Weckstunden
Dim Wzmi As Word ' Zehnerstelle Weckminuten
Dim Wemi As Word ' Einerstelle Weckminuten
Dim Ta As Word ' Alarmzeit in Tagesminuten
Dim Hilf As Word ' Hilfsgroesse für Tagesminutenberechnung
Dim Alarm As Byte ' Ein- / Ausschalter Alarm
Dim Gong As Byte ' Ein- / Ausschalter Gong
' Variablen für Morsezeichen- Erzeugung
Dim I As Byte ' wird in Zählschleife verwendet
Dim N As Word ' Aktueller Morsebuchstabe
Dim M As Byte ' wird fürs Auslesen der Morsebits verwendet
Dim L As Byte ' wird für niederwertigstes Bit von M verwendet
' Variablen für Morsezeichen- Decodierung
Dim E As Byte ' resultierendes ACCII-Byte der Decodierroutine
Dim D As Byte ' Morsebyte in der Decodierroutine
Dim K As Byte ' Byte, in dem eine "1" geshiftet wird
Dim J As Byte ' Zählvariable für tolerante Pausenprüfung
Dim P As Byte ' Punktspeicher-Bit der Decodierroutine
' Variablen für Ziffern- Eingabe
Dim G(4) As Byte ' nimmt die Ziffern auf
Dim R As Byte ' Anzahl angeforderter Ziffern
Dim W As Byte ' Zähler für Eingabewiederholung der Ziffern
Dim Notbremse As Byte ' Timer für Ausstieg aus unvollständigen Eingaben
Dim Txtstring As String * 84
Dim Txt(84) As Byte At Txtstring Overlay
' Variablen für Morsezeichen- Timing
Dim Punktlaenge1 As Word ' Punktlänge in "Empfangsrichtung"
Dim Punktlaenge2 As Word ' Punktlänge in "Senderichtung"
Dim Punktachtel As Word
Dim Strichlaenge1 As Word ' Strichlänge
Dim Strichlaenge2 As Word
Dim Wortpause1 As Word ' Länge Wortpause
Dim Wortpause2 As Word
Mcucr.sm0 = 0 ' mcucr für Idle konfigurieren
Mcucr.sm1 = 0 ' mcucr Idle zweites Bit
Pcmsk.1 = 1 ' PCINT für PB1 freigeben
Pcmsk.2 = 1 ' PCINT für PB2 freigeben
Gimsk.pcie = 1 ' PCints aktivieren
Sreg.7 = 1 ' Interrupts generell freigeben
Portb = &B0000110 ' Pullups für Strich- und Punktkontakt
Ddrb = &B00000000 ' erstmal alles Eingang
' Stromspar- Funktionen nutzen
Didr0 = &B00011001 ' digitale Eingänge ausser PB1 und PB2 abschalten
Acsr.acd = 0 ' Analog-Komparator ausschalten
Prr.prusi = 1 ' usi abschalten (power reduction usi)
Prr.pradc = 1 ' adc abschalten (power reduction adc)
' 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 = Timer , Prescale = 64 , Compare A = Toggle , Clear Timer = 1
' 1- Sekunden- Interrupts mit Timer 1 erzeugen
Config Timer1 = Timer , Prescale = 2048
Enable Timer1
On Timer1 Ontimer1 ' Interrupt-Routine für Timer2-Overflow
Const Tonhoehe1 = 12
Const Tonhoehe2 = 18
Buzzer Alias Ddrb.0
Punktkontakt Alias Pinb.1
Strichkontakt Alias Pinb.2
'----------------------------------------------------------
Init:
'--- vorläufige Werte -------
Punktlaenge1 = 80 ' Punktlänge in "Empfangsrichtung"
Punktlaenge2 = 100 ' Punktlänge in "Senderichtung"
Punktachtel = 10
Strichlaenge1 = Punktlaenge1 * 3 ' Strichlänge
Strichlaenge2 = Punktlaenge2 * 3
Wortpause1 = Punktlaenge1 * 5 ' Länge Wortpause
Wortpause2 = Punktlaenge2 * 5
Gong = 1
Gosub Zeitausgabe ' Kontrollausgabe nach Reset
'--- Hauptschleife -----------
Do
Prr.prtim0 = 0 ' Timer0 anschalten
Mcucr.se = 0 ' Sleeepmode verbieten
Mcucr.pcie = 0 ' Tasteninterrupts verbieten
If T > 1439 Then ' Prüfen auf Tageswechsel
T = 0
S = 0
If Korr > 0 Then ' Korr muß groesser 0 sein, sonst bleibt die Uhr im Waitbefehl hängen
Wait Korr ' Feinabgleich durch Befehl "Korr", falls die Uhr etwas zu schnell läuft
End If
End If
Gosub Zeitberechnung
If Gong = 1 Then
Gosub Gong
End If
If Alarm = 1 Then
Gosub Alarmsub
End If
Gosub Menue
' Idle- Modus vorbereiten
Prr.prtim0 = 1 ' Timer0 abschalten
Mcucr.pcie = 1 ' Pin Change Interrupt enable
Mcucr.se = 1 ' Sleep enable
!SLEEP
Loop
'------- Zeitberechnungen und Ausgabe der Zeiten ---------
Zeitberechnung:
H = T / 60 ' Uhrzeit auf Stunden gerundet
Mi = T Mod 60 ' Minuten- Rest (zur vollen Stunde)
Zh = H / 10 ' Zehnerstelle Stunden
Eh = H Mod 10 ' Einerstelle Stunden
Zmi = Mi / 10 ' Einerstelle Minuten
Emi = Mi Mod 10 'Einerstelle Minuten
V = T Mod 15 ' V = 0 bei vollen Viertelstunden
Return
Weckzeitberechnung:
Wh = Ta / 60 ' Uhrzeit auf Stunden gerundet
Wmi = Ta Mod 60 ' Minuten- Rest (zur vollen Stunde)
Wzh = Wh / 10 ' Zehnerstelle Stunden
Weh = Wh Mod 10 ' Einerstelle Stunden
Wzmi = Wmi / 10 ' Einerstelle Minuten
Wemi = Wmi Mod 10 'Einerstelle Minuten
Return
Gong:
If S < 2 Then ' unterdrückt mehrfache Ausgabe
If V = 0 Then ' volle Viertelstunde
If Mi = 0 Then ' Sonderfall: volle Stunde
Txtstring = " ES IST "
Gosub Sendstring
If Zh > 0 Then ' wenn zweistellige Stundenzahl
M = Zh ' Zehnerstunde senden
Gosub Sendnumber
End If
M = Eh ' Einerstunde senden
Gosub Sendnumber
Txtstring = " UHR "
Gosub Sendstring
Else ' zur 15., 30., 45. Minute nur Minutenausgabe
M = Zmi ' Zehnerminute
Gosub Sendnumber
M = Emi ' Einerminute
Gosub Sendnumber
End If
End If
End If
Return
Zeitausgabe: ' wenn Taster gedrückt wurde
Txtstring = " ES IST "
Gosub Sendstring
If Zh > 0 Then
M = Zh ' Zehnerstelle Stunden
Gosub Sendnumber
End If
M = Eh ' Einerstelle Stunden
Gosub Sendnumber
Txtstring = " UHR "
Gosub Sendstring
If Zmi > 0 Then
M = Zmi ' Zehnerstelle Minuten
Gosub Sendnumber
End If
M = Emi ' Einerstelle Minuten
Gosub Sendnumber
Txtstring = " MIN"
Gosub Sendstring
Return
Weckzeitaugabe:
Gosub Weckzeitberechnung
M = Wzh ' Zehnerstelle Stunden
Gosub Sendnumber
M = Weh ' Einerstelle Stunden
Gosub Sendnumber
M = Wzmi ' Zehnerstelle Minuten
Gosub Sendnumber
M = Wemi ' Einerstelle Minuten
Gosub Sendnumber
Return
Alarmsub:
If T = Ta Then
Txtstring = "SOS"
Gosub Sendstring
If Punktkontakt = 0 Then ' Alarm abschalten mit "E", "I", "S" usw.
Alarm = 0
End If
End If
Return
'------- Aufbereitung der zu sendenden Morsezeichen ------
Sendnumber:
M = M + 48 ' Ascii- Wert 0: 048 ; Ascii- Wert 9: 057
M = Lookup(m , Morsetabelle) ' Morsezeichen aus Tabelle holen
Gosub Morse
Return
Sendstring:
For N = 1 To Len(txtstring) ' Textstring ausgeben
M = Txt(n)
M = Lookup(m , Morsetabelle) ' Morsezeichen aus Tabelle holen
Gosub Morse
Next T
Return
'------- Erzeugung der zu sendenden Morsezeichen -------
Morse:
Ocr0a = Tonhoehe1
If M = 0 Then ' Sonderfall: Leerzeichen;
Waitms Wortpause1
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
Buzzer = 1
If L = 1 Then
Waitms Strichlaenge1 ' ist das Bit 1 -> dah
Else
Waitms Punktlaenge1 ' ist das Bit 0 -> dit
End If
Buzzer = 0
Waitms Punktlaenge1 ' Pause innerhalb des Morsezeichens
Shift M , Right , 1 ' Bits um eine Stelle nach rechts shiften
Next I
Zeichenende:
Waitms Strichlaenge1 ' Pause zwischen Morsezeichen
Return
'--- Hier beginnt die Auswertung der Morse- Eingaben --------
'------ Menü ( Befehle bestehen aus 1 Zeichen ) -----
Menue:
Gosub Decodekeyer
Select Case E
Case 65 ' 65 : Ascii - Wert "A"
Txtstring = " ALARM?"
Gosub Sendstring
R = 1
Gosub Readnumbers
If G(1) = 1 Then
Alarm = 1
Else
Alarm = 0
End If
Case 71 ' 71 : Ascii - Wert "G"
Txtstring = " GONG?"
Gosub Sendstring
R = 1
Gosub Readnumbers
If G(1) = 1 Then
Gong = 1
Else
Gong = 0
End If
Case 67 ' 67 : Ascii- Wert "C"
Gosub Check
Case 75 ' 75 : Ascii- Wert "K"
Txtstring = " KORRSEK?"
Gosub Sendstring
R = 1
Gosub Readnumbers
Korr = G(1) + 1
Case 77 ' 77 : Ascii - Wert "M"
Txtstring = " WPM?"
Gosub Sendstring
Gosub Speed
Case 84 ' 84 : Ascii - Wert "T"
Gosub Zeitausgabe
Case 87 ' 87 : Ascii - Wert "W"
Txtstring = " WECKZEIT?"
Gosub Sendstring
Gosub Weckzeitsetzen
Case 90 ' 90 : Ascii - Wert "Z"
Txtstring = " ZEIT?"
Gosub Sendstring
Gosub Zeitsetzen
Case 63 ' 63 : Ascii - Wert "?"
Txtstring = "BEFEHLE: ALARM CHECK ENDE GONG KORRSEK MORSESPEED TIME WECKZEIT ZEITSETZEN"
Gosub Sendstring
End Select
Return
' ----- hier folgen die Subroutinen, die durch das Menü aufgerufen werden ------
Zeitsetzen:
S = 0 ' Sekunden auf 0 setzen
R = 4
Gosub Readnumbers
Hilf = G(3) * 10 ' G(3) Zehnerstelle Minuten
T = G(4) + Hilf ' G(4) Einerstelle Minuten
Hilf = G(2) * 60 ' G(2) Einerstelle Stunden
T = T + Hilf
Hilf = 600 * G(1) ' G(1) Zehnerstelle Stunden
T = T + Hilf ' T Tagesminuten
If T > 1439 Then ' später als 23.59 Uhr
T = 1 ' Workaround, damit der Gong nicht anspricht
Txtstring = " RPT"
Gosub Sendstring
End If
Return
Weckzeitsetzen:
R = 4
Gosub Readnumbers
Hilf = G(3) * 10
Ta = G(4) + Hilf
Hilf = G(2) * 60
Ta = Ta + Hilf
Hilf = 600 * G(1)
Ta = Ta + Hilf ' Ta: Tagesminuten Alarmzeit
If Ta > 1439 Then
Txtstring = " RPT"
Gosub Sendstring
End If
Return
Speed:
R = 2
Gosub Readnumbers
' Aus dem abgefragten Wert neue Geschwindigkeit berechnen
Calcspeed:
G(1) = 10 * G(1) ' Zehnerstelle WPM
G(1) = G(1) + G(2) ' Einerstella addieren
Punktlaenge1 = 1320 / G(1) ' aus WPM Punkitlänge errechnen
Strichlaenge1 = Punktlaenge1 * 3
Wortpause1 = Punktlaenge1 * 5
Punktlaenge2 = 3 * Punktlaenge1 ' Sendegeschwindigkeit soll nur 2/3 der
Punktlaenge2 = Punktlaenge2 / 2 ' Empfangsgeschwindigkeit sein
Punktachtel = Punktlaenge2 / 8
If Punktachtel = 0 Then
Punktachtel = 1
End If
Strichlaenge2 = Punktlaenge2 * 3
Wortpause2 = Punktlaenge2 * 5
If G(1) < 10 Or G(1) > 30 Then ' auf plausiblen Wert prüfen
G(1) = 2
G(2) = 0
Goto Calcspeed ' Neuberechnung mit Default- Werten
End If
Return
Check:
Txtstring = " ALARM "
Gosub Sendstring
M = Alarm
Gosub Sendnumber
Txtstring = " GONG "
Gosub Sendstring
M = Gong
Gosub Sendnumber
Txtstring = " KORRSEK "
Gosub Sendstring
M = Korr
Gosub Sendnumber
Txtstring = " WECKZEIT "
Gosub Sendstring
Gosub Weckzeitaugabe
Return
'------ Morseingabe- Auswerteroutinen -------
Readnumbers:
I = 0
Notbremse = 0
Do
If Punktkontakt = 0 Or Strichkontakt = 0 Then ' Punkt - oder Strichkontakt gedrückt
I = I + 1
Gosub Decodekeyer
G(i) = E ' jeweils Ascii- Wert einstellige Zahl abholen
Select Case G(i)
Case 48 To 57
G(i) = G(i) - 48 ' auf Zahleneingabe prüfen
Case Else
Txtstring = "RPT"
Gosub Sendstring
Return
End Select
End If
Notbremse = Notbremse + 1
Waitms 20
If Notbremse > 250 Then ' Ausstieg nach längerer Eingabepause (5 sec)
Txtstring = "RPT"
Gosub Sendstring
Return
End If
Loop Until I = R
Waitms 200
For W = 1 To R ' zur Kontrolle die Zahlen ausgeben
M = G(w)
Gosub Sendnumber
Next
Return
'------- Interpretation der eingegebenen Einzel- Morsezeichen ----
Decodekeyer: ' Prinzip: eine 1 wird in der Variablen "K" von links nach rechts geschoben.
' nur wenn Strich gedrückt wurde, wird die 1 in die Variable D übernommen
Ocr0a = Tonhoehe2
D = 0
K = 1
Do
If Punktkontakt = 0 Then ' Punktkontakt gedrückt
Buzzer = 1 ' Ton an
Waitms Punktlaenge2
Buzzer = 0 ' Ton aus
Waitms Punktlaenge2
Shift K , Left ' Byteinhalt wandert 1 Stelle nach links, von rechts wird eine "0" eingeschoben
End If
Mark:
If Strichkontakt = 0 Then ' Strichkontakt gedrückt
Buzzer = 1 ' Ton an
Waitms Strichlaenge2
D = D + K ' die 1 für den Strich wird in die Variable "D" übernommen
Shift K , Left ' Byteinhalt wandert 1 Stelle nach rechts, von links eine "0" eingeschoben
If Punktkontakt = 0 Then
P = 1 ' wenn Punktkontakt ebenfalls gedrückt
End If ' Punktspeicher setzen
Buzzer = 0 ' Ton aus
Waitms Punktlaenge2
If P = 1 Then ' Punktspeicher prüfen
Buzzer = 1 ' Ton an
Waitms Punktlaenge2
Buzzer = 0 ' Ton aus
Waitms Punktlaenge2
Shift K , Left ' Byteinhalt wandert 1 Stelle nach links, rechts wird 0 eingeschoben
P = 0 ' Punktspeicher zurücksetzen
Goto Mark ' Prüfen, ob Strichkontakt bereits wieder gedrückt
End If
End If
J = 0
Do ' wir warten max. halbe Punktlänge, um unexakte Gebeweise abzufangen
If Punktkontakt = 0 Or Strichkontakt = 0 Then ' vorzeitiger Ausstieg, wenn Punktkontakt oder Strichkontakt gedrückt
Exit Do
End If
Waitms Punktachtel
J = J + 1
Loop Until J = 4 ' Abbruch bei halber Punktlänge
Loop Until Punktkontakt = 1 And Strichkontakt = 1 ' kein Kontakt mehr gedrückt; Morsezeichen fertig
D = D + K ' eine 1 wird als Endbit angehängt
E = Lookdown(d , Morsetabelle , 91) ' inverse Lookup in der Tabelle, um den zugehörigen Ascii- Wert zu ermitteln
E = E - 1 ' Data- Elemente werden ab 1 gezählt, Ascii- Tabelle beginnt bei Null
Return
'--- Interrupt- Routine Sekundentakt ------
Ontimer1: ' Timer1-Overflow-Interrupt-Routine
Waitus 200 ' Ausgleich, da die Uhr wegen der weggelassenen Bürdekondensatoren grundsätzlich
' etwas zu schnell läuft
Tcnt1 = 31 ' Preset auf 31, damit der Timer 225 Takte bis Ueberlauf zählt
S = S + 1
If S > 59 Then ' Minutenwechsel
S = 0
T = T + 1
End If
Return
'--- Morsezeichen- Tabelle --------------------------
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 , 0 , 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 - 127 -> Muell