Morseuhr V3

von Ralf Beesner, DK5BU


 

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

 Ein Quarz muss beim Attiny 45 an die Eingänge PB3 und PB4 angeschlossen werden. Der Buzzer liegt an PB0; für den Strich- und den Punktkontakt bleiben daher nur PB1 und PB2. Außer dem Mikrocontroller, dem Quarz, dem Buzzer und den beiden Tastern sind nur noch ein Abblock- Kondensator für die Betriebsspannung und ein Lautstärketrimmer vorhanden. Der Reset wird hoffentlich selten benötigt; er ist auf dem Bild des Prototypen noch mit einem Mikrotaster beschaltet, im Schaltplan und Platinen- Vorschlag aber nur als Kontaktfläche ausgeführt, die bei Bedarf mit einem leitenden Gegenstand gebrückt wird.

 

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:

In der "fat version" kommen hinzu

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.

Download:  
T45-morseuhr-update.zip  
(Update 19.3.11, verbesserte Feineinstellung der Ganggenauigkeit)
Dieser Artikel als PDF
' 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