Morseuhr zwo
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