Morseuhr zwo


von Ralf Beesner, DK5BU
Elektronik-Labor   Projekte   AVR 



Zwei Exemplare der Morseuhr laufen bei mir seit einigen Monaten klaglos (eine im Büro, eine in der Bastelecke).  Einige Funktionalitäten habe ich jedoch vermisst und nun in der "Morseuhr zwo" realisiert:
- neben der "Schlagwerk- Funktion" zusätzlich (auf Knopfdruck) die Ausgabe der minutengenauen Uhrzeit
- Schlagwerk abschaltbar
- Stellen der Uhr nicht nur zur vollen Stunde, sondern im Minutenraster
- einstellbare Geschwindigkeit und Tonhöhe der Morseausgabe
- Resettaster abschaltbar, damit man nicht versehentlich die Uhr auf eine falsche Zeit stellt

Erforderliche Hardware- Ergänzungen:

Da der Mikrocontroller noch zahlreiche unbeschaltete Portpins aufwies, habe ich die zusätzlichen Einstell-Funktionen über Dipschalter realisiert.
Zwar wäre es komfortabler, Geschwindigkeit und Tonhöhe stufenlos über zwei Trimmwiderstände an AD- Wandler- Eingängen einzustellen, aber "löttechnisch" einfacher war es, die Portpin- Blöcke mit "Mäuseklavieren" zu beschalten, die internen Pullup- Widerstände zu aktivieren und die Portpins mit den Dipschaltern gegen Masse zu ziehen.
Das Stellen der Uhr erfolgt weiterhin per Mäuseklavier. Für die Stunden (8...23 Uhr) blieb es bei dem 4-bit-Dipschalter an PB2... PB5, die Minuten werden über einen 6-bit-Dipschalter an PC0...PC5 vorgewählt (die Stellzeit wird - wie gehabt - nach einem Reset übernommen).
Die Geschwindigkeitseinstellung erfolgt über die 3 niederwertigsten Schalter eines 4-bit- Mäuseklaviers an PD1 ... PD3, also in 8 Stufen. Der 4. Schalter dient dazu, das Schlagwerk ein- bzw. auszuschalten.
An PD5 ... PD7 liegen 3 bit eines 4- bit-Dipschalters. Sie geben die Tonhöhe vor (also ebenfalls in 8 Stufen), der 4. Schalter liegt in Reihe mit dem Reset- Taster.
An Pin B0 liegt der Taster, mit dem man die spontane Uhrzeitausgabe auslöst.
Die Stromversorgung kann alternativ mit einer 3V-Lithiumzelle, 3 Mignonzellen oder (wie auf dem Versuchsaufbau) mit einem kleinen 3,6 V- Akku erfolgen. Sicherung nicht vergessen !


Prozessor
Bei Reichelt sind die Preise für ATmega8-16 im DIL- Gehäuse förmlich explodiert (von EUR 1,35 auf z.Zt. EUR 3,40). Ich habe daher die "Morseuhr zwo" primär für den preiswerteren ATmega48 konzipiert.



Beim ATmega48 wurde die Zahl der Register vergrößert und Registerbits wurden "logischer" zusammengefasst. Einige Funktionen finden sich also unter abweichenden Registernamen wieder.

Außerdem gibt es neben den externen Interrupts INT0 und INT1 zahlreiche Pin Change Interrupts, die den Controller ebenfalls aus dem Tiefschlaf- Modus holen können. Der Uhrzeit- Abruf- Knopf an PB0 würde beim ATmega8 nur ein mal pro Sekunde gepollt, nachdem der Sekundentimer den ATmega8 geweckt hat - man müßte den Knopf also bis zu einer Sekunde niederdrücken. Beim AImega 48 (und seinen größeren Verwandten) kann man für PB0 den Pin Change Interrupt aktivieren, so dass auch ein kurzer Druck auf den Uhrzeit- Abruf- Knopf nicht verloren geht. 

Zentrale Änderungen in der Software

Die "Morseuhr eins" befasste sich nicht mit Minuten, sondern zählte lediglich Sekunden und Viertelstunden.  "Morseuhr zwei" ist auf Minutenzählung umgestellt (es gibt aber für das Schlagwerk immer noch die Viertelstunden- Auswertung).



Die Aufsummierung der Sekunden zu Minuten wird in der Interrupt- Routine erledigt, da das Hauptprogramm zu lange mit der Morseausgabe beschäftigt sein könnte.
Nach einem Reset werden die "Uhrzeit- Mäuseklaviere" und die Einstellungen für Tonhöhe und Geschwindigkeit abgefragt. Anschließend wird die Uhrzeit zur Kontrolle ausgegeben.
Im Hauptprogramm ist die Abfrage des Tasters und des Schlagwerk- Schalters hinzugekommen.
Je nach Ergebnis der Abfrage wird ggf. in drei Unterprogramme gesprungen - zwei für die spontane Uhrzeitaugabe ("Speed" und "Zeitausgabe", eines für die Schlagwerkausgabe. Beide bedienen sich der Subroutine für die Morse- Ausgabe.
Nach Drücken des Uhrzeit- Augabe- Tasters werden Tonhöhe und Geschwindigkeit nur ein mal abgefragt (Änderungen wirken sich also erst bei der nächsten Zeitausgabe aus).






Siehe auch: Die Vorgänger-Morseuhr

Download:
PDF-Version des Artikels
Firmware und Bascom-Quelltext

'Morseuhr Version 7b

' gibt alle 15 min. die Minutenzahl aus und zur vollen Stunde die Stundenzahl
' ("es ist X Uhr")

' seit Version 4b: Feintuning Sekundentakt eingebaut (beim Tageswechsel Wartezeit oder Preset
' der Sekundenvariable auf Werte > 0 )

' Ab Version 5 wird die Uhrzeit zusätzlich minutengenau auf Knopfdruck ausgegeben.
'
' Die Uhrzeit wird per Mäuseklavier voreingestellt und die Uhr dann durch einen
' Reset gestartet.
'
' Dipschalter:
' Die Stunde wird binär per 4-bit "Mäuseklavier" an PB 2 .... PB 5 auf 8.00 Uhr bis 23.00
' vorgewählt.
' Die Minute wird binär per 6-bit- "Mäuseklavier" an PC 0 ... PC 5 vorgewählt
' Die Telegrafiergeschwindigkeit wird über ein "Mäuseklavier" an PB1 ....PB3 in 8 Stufen
' eingestellt
' PB4: Schlagwerk ein/aus (bei "aus" wird nur noch auf Knopfdruck die Zeit ausgegeben)
' Die Tonhöhe wird über ein "Mäuseklavier" an PB5 ....PB7 in 8 Stufen eingestellt
' Dipschalter 4 liegt in Reihe mit dem Reset-Taster; mit ihm kann man den Resettaster
' abschalten, um ein versehentliches Auslösen zu verhindern
'
' Der Tongeber (Buzzer) liegt an PB1
' Die "Plaudertaste", mit der man die Uhrzeit minutengenau abrufen kann, liegt an PB0 / PCINT0.
'
'
' Der Mega48 läuft in einem speziellen Uhrenbetrieb. Er wird mit 1 MHz getaktet
' (interner RC- Oszillator), Der Quarzoszillator ist mit einem 32768Hz-Uhren-
' quarz beschaltet, der den Timer 2 asynchron taktet. Timer 2 löst ein Mal pro
' Sekunde einen Interrupt aus.

' Nach 60 Interrupts ist eine Minute um; mit Minuten wird weitergerechnet.

' Die Konfiguration des Timer2- Uhrenmodus und Erzeugung des Sekundenimpulses
' ist dem "AVR Mikrocontroller-Lehrbuch" von Roland Walter entnommen

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


$regfile = "m48def.dat"
$crystal = 1000000 ' Der AVR-Takt kommt vom RC-Oszillator


On Timer2 Ontimer2 ' Interrupt-Routine für Timer2-Overflow



Dim S As Word ' Sekunden
Dim T As Word ' Tagesminuten 0 ..... 1440
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 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

Dim Dit As Word ' Länge eines Punkts; daraus ergibt sich die Morsegeschwindigkeit
Dim Dah As Word ' Länge eines Strichs
Dim Wort As Word ' Länge der Pause zwischen Wörtern
Dim Tonhoehe As Word ' Tonhöhe

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



Acsr.acd = 1 ' Analogen Komparator ausschalten
Assr.as2 = 1 ' Timer2- Asynchron- Betrieb
Tccr2b = &B00000101 ' Vorteiler für Timer2 auf 128 setzen
Timsk2.toie2 = 1 ' Timer2-Overflow-Interrupt an
Smcr = &B00000110 ' Powersave-Modus aktivieren

Pcicr.0 = 1 ' Pin Change Interrupt Enable 0
Pcmsk0.0 = 1 ' Pin Change Mask PCINT0 (Interrupt durch Pin Change an PB0)

Sreg.7 = 1 ' Interrupts global einschalten
Ddrb = &B00000000 ' alle Pins sind Eingänge
Portb = &B11111111 ' Pullups, damit die Eingänge nicht floaten
Ddrc = &B00000000 ' alle Pins sind Eingänge
Portc = &B11111111 ' Pullups, damit die Eingänge nicht floaten
Ddrd = &B00000000 ' alle Pins sind Eingänge
Portd = &B11111111 ' Pullups, damit die Eingänge nicht floaten

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




' Tonsignal an OC1A / Port B1 erzeugen; Signal wird unten durch Umschalten des DDRB im Takt der Zeichen geschaltet
'
Config Timer1 = Timer , Compare A = Toggle , Prescale = 64 , Clear Timer = 1


'
Buzzer Alias Ddrb.1
Taster Alias Pinb.0
Schlagw Alias Pind.4




Init:


' Startzeit aus den Dip- Schaltern auslesen; zunächst die Stunden

T = Not Pinb ' invertieren, weil gesetzte Dipschalter 0 statt 1 ergeben
T = T And &B00111100 ' die Schalterbits filtern - > Tagesminute = 0,4,8 .... 28 T = T * 15 ' Tagesminute = 0, 60, 120 ....
T = T * 15 ' Tagesminute = 0, 60, 120 ....
T = T + 480 ' T = 480, 540, 600 -> Startzeit 8,9,10 ....,22,23 Uhr

' nun die Minuten

I = Not Pinc ' invertieren, weil gesetzte Dipschalter 0 statt 1 ergeben
I = I And &B00111111 ' die untersten 6 Bits ausfiltern
T = T + I ' zu den Stunden addieren


' zur Kontrolle einmalig die voreingestellte Zeit ausgeben

Gosub Speed
Gosub Zeitberechnung
Gosub Zeitausgabe





Do ' Hauptschleife
Smcr.se = 0 ' Sleeepmode verbieten
Mcucr.pud = 0 ' Pullups aktivieren
If T > 1439 Then ' Prüfen auf Tageswechsel
T = 0
' S = 0
S = 2 ' Feinabgleich- Beispiel, falls die Uhr etwas zu langsam läuft
' Wait 2 ' Feinabgleich- Beispiel, falls die Uhr etwas zu schnell läuft
End If

Gosub Zeitberechnung
If Schlagw = 0 Then ' Schlagwerk nur aktiv, wenn Pind.4 auf low liegt
Gosub Schlagwerk
End If

If Taster = 0 Then 'Prüfung, ob Zeitausgabe- Taster gedrückt
Gosub Speed ' ein Mal pro Zeitabruf wird Geschwindigkeit /Tonhöhe abgefragt
Gosub Zeitausgabe
End If

Mcucr.pud = 1 ' Pullups deakivieren; die können bis zu 1 mA ziehen ...
Smcr.se = 1 'Sleep-Modus erlauben
!Sleep 'In den Sleep-Modus gehen

Loop




Speed:

Dit = Not Pind ' invertieren
Dit = Pind And &B00001110 ' liegt zwischen 0 und 15, Schrittweite 2
Shift Dit , Left , 2 ' liegt nun zwischen 0 und 56, Schrittweite 8
Dit = Dit + 40 ' Geschwindigkeitsbereich 40 .. 96
Dah = Dit * 3
Wort = Dit * 8

Tonhoehe = Not Pind
Tonhoehe = Pind And &B11100000 ' zwischen 0 und 224, Schrittweite 32
Shift Tonhoehe , Right , 3 ' nun zwischen 0 und 28, Schrittweite 4
Tonhoehe = Tonhoehe + 8 ' nun zwischen 8 und 40, Schrittweite 4
Ocr1a = Tonhoehe
Return



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





Schlagwerk:

If S < 2 Then ' unterdrückt mehrfache Ausgabe
If V = 0 Then
If Mi = 0 Then ' Sonderfall: volle Stunde
For N = 10 To 16 ' String "es ist "
Gosub Morse
Next
If Zh > 0 Then
N = Zh
Gosub Morse
End If
N = Eh
Gosub Morse
For N = 16 To 19 ' string " uhr"
Gosub Morse
Next
Else ' zur 15., 30., 45. Minute nur Minutenausgabe
N = Zmi
Gosub Morse
N = Emi
Gosub Morse
End If
End If
End If
Return


Zeitausgabe: ' wenn Taster gedrückt wurde

For N = 10 To 16 ' String "es ist "
Gosub Morse
Next
If Zh > 0 Then
N = Zh ' Zehnerstelle Stunden
Gosub Morse
End If
N = Eh ' Einerstelle Stunden
Gosub Morse
For N = 16 To 20 ' String " uhr "
Gosub Morse
Next
If Zmi > 0 Then
N = Zmi ' Zehnerstelle Minuten
Gosub Morse
End If
N = Emi ' Einerstelle Minuten
Gosub Morse
For N = 20 To 23 ' String " min"
Gosub Morse
Next

Return





' Morsezeichenerzeugung:

Morse:

M = Lookup(n , Tdata)

If M = 0 Then ' Sonderfall: Leerzeichen;
Waitms Wort
Goto Sign_end
End If

For I = 1 To 8
If M = 1 Then ' Das Byte hat nur noch den Wert 1; Zeichenende!
Goto Sign_end
End If
L = M And &B0000001 ' niederwertigstes Bit lesen
Buzzer = 1 ' Ausgänge einschalten
If L = 1 Then
Waitms Dah ' ist das Bit 1 -> dah
Else
Waitms Dit ' ist das Bit 0 -> dit
End If
Buzzer = 0 ' Ausgänge abschalten
Waitms Dit ' Pause innerhalb des Morsezeichens
Shift M , Right , 1 ' Bits um eine Stelle nach rechts shiften
Next I
Sign_end:
Waitms Dah ' Pause zwischen Morsezeichen
Return

End


'----------------------------------------------------------
Ontimer2: 'Timer2-Overflow-Interrupt-Routine

S = S + 1
If S > 59 Then ' Minutenwechsel
' If S > 14 Then ' Testbetrieb; Minutenwechsel nach 15 sec.
S = 0
T = T + 1
End If
' Toggle Portd.0 ' Test,ob die Sekundenimpulse kommen
Return



'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Codierung der Morsezeichen. Bei einem Prozessor mit viel SRAM würde man alle Zeichen
' in ein Array einlesen (Übersetzungstabelle), den eigentlichen Bakentext in einen String packen
' und diesen String zeichenenweise abarbeiten.
'
' Da der Attiny13 nur ca. 64 Byte SRAM hat, ist nur Platz für etwa 50 Bytes. Daher ist der Weg hier
' anders: Der Bakentext ist ein Array mit max. 52 Elementen, in dem die auszusendenden Zeichen stehen
' allerdings in einer leicht lesbaren schreibweise.
' Die Übersetzungstabelle ist mit Konstanten realisiert. Nur die wirklich benötigten
' Zeichen werden (beim kompilieren des Programmes) umgewandelt.


Const #a = &B00000110
Const #b = &B00010001
Const #c = &B00010101
Const #d = &B00001001
Const #e = &B00000010
Const #f = &B00010100
Const #g = &B00001011
Const #h = &B00010000
Const #i = &B00000100
Const #j = &B00011110
Const #k = &B00001101
Const #l = &B00010010
Const #m = &B00000111
Const #n = &B00000101
Const #o = &B00001111
Const #p = &B00010110
Const #q = &B00011011
Const #r = &B00001010
Const #s = &B00001000
Const #t = &B00000011
Const #u = &B00001100
Const #v = &B00011000
Const #w = &B00001110
Const #x = &B00011001
Const #y = &B00011101
Const #z = &B00010011
Const #ae = &B00011010
Const #oe = &B00010111
Const #ue = &B00011100
Const #0 = &B00111111
Const #1 = &B00111110
Const #2 = &B00111100
Const #3 = &B00111000
Const #4 = &B00110000
Const #5 = &B00100000
Const #6 = &B00100001
Const #7 = &B00100011
Const #8 = &B00100111
Const #9 = &B00101111

Const Leerz = 0
Const Anfuehrz = &B01010010
Const Kl_auf = &B00101101
Const Kl_zu = &B01101101
Const Plus = &B00101010
Const Komma = &B01110011
Const Minus = &B01100001
Const Punkt = &B01101010
Const Slash = &B00101001
Const Dopp_pkt = &B01000111
Const Gleich = &B00110001
Const Fragez = &B01001100
Const Ende = 255


Tdata:
Data #0 , #1 , #2 , #3 , #4 , #5 , #6 , #7 , #8 , #9
Data #e , #s , Leerz , #i , #s , #t , Leerz , #u , #h , #r , Leerz , #m , #i , #n


Elektronik-Labor   Projekte   AVR