Wer morsen kann, ist klar im Vorteil
von Ralf Beesner, DK5BU
Elektronik-Labor Projekte AVR
Die beschränkten Ressourcen des Attiny13 (nur 5 I/O-Ports) machen es umständlich, Messergebnisse oder Zustandsänderungen auszugeben. In vielen Programmbeispielen erfolgt die Ausgabe über eine Software- COM- Schnittstelle, aber für sie wird ein PC mit einem Terminalprogramm benötigt.
Ideal ist die Signalisierung per Tonsignal. Man benötigt nur
einen Piezo- Schallwandler, und es wird nur ein Ausgang des Attiny13 belegt.
Aus dem alten 68er- Spruch "Wer lesen kann, ist klar im
Vorteil", den ich als Schüler zu hören bekam, wird so "Wer Morsen
kann, ist klar im Vorteil".
Die vorliegende Schaltung misst die Spannung an einem
Spannungsteiler aus Festwiderstand und Kaltleiter, rechnet den Spannungswert in
Grad Celsius um und gibt ihn in Morsezeichen aus.
Codierung von Morsezeichen
Prinzip der Codierung:
- 1 Byte pro Zeichen, niederwertiges Bit zuerst
- hat das Bit den Wert 0: Morsepunkt (dit)
- hat das Bit den Wert 1: Morsestrich (dah)
- da Morsezeichen unterschiedlich lang sind,
ist ein Ende- Zeichen erforderlich; es ist ebenfalls ein 1 - Bit.
- Beispiel Morsezeichen "`a"' (dit
dah): \verb &B00000110 \
- Beispiel Morsezeichen "`9"' (dah
dah dah dah dit): \verb &B00101111 \
- Auslesen der codierten Zeichen:
niederwertigstes Bit auslesen, alle Bits um 1 Stelle nach rechts schieben,
niederwertigstes Bit auslesen. Ist der Wert des gesamten Byte nur noch 1, ist
das Zeichen komplett.
Hardware
Der Spannungsteiler besteht aus einem Vorwiderstand und
einem Kaltleiter KTY81. Dieser hat bei der Nominaltemperatur 25 Grad Celsius
einen Widerstand von 1000 Ohm, bei 0 Grad 815 Ohm.
Der Widerstandsverlauf des KTY81 ist nicht linear, sondern
deutlich ansteigend (vermutlich eine e-Funktion). In einer
Spannungsteilerschaltung wird die Nichtlinearität jedoch in gewissen Grenzen
kompensiert. Ich habe daher den Widerstands- und den Spannungsverlauf aus dem
Datenblatt in eine OpenOffice-Kalkulationstabelle übertragen (siehe xls-Datei),
den Vorwiderstand variiert und durch Ausprobieren einen günstigen Wert gesucht.
Der Mittelabgriff des Spannungsteilers liegt an PB4 / ADC0
(Pin3). Das obere Ende des Spannungsteilers liegt nicht direkt an Plus, sondern
wird zwecks Stromersparnis über PB3 (Pin2) nur für die Dauer der Messung
geschaltet (auch der ADC wird nur für die Dauer der Messung aktiviert). Der
Piezo-Schallwandler liegt an PB0/OC0A (Pin5).
Software
Das Programmlistung ist ausführlich kommentiert, daher nur
eine Übersicht:
- Timer0 als
Tonquelle konfigurieren
- Auslesen des ADC,
Umwandlung in Celsiusgrade, "Interpolieren" und Zerlegung in
Einzelziffern
- Erzeugung der
Morsezeichen
Um diesen Programmkern ist noch eine Zählschleife mit der
Variablen "E" gelegt, die dafür sorgt, dass das Thermometer die
Temperatur regelmäßig ausgibt (alle 15 - 20 Minuten).
' Das Programm liest den ADC aus und gibt die Werte als Morse- Text per NF-Ton aus (ca alle 15 Minuten).
' Der ADC ist mit einem Spannungsteiler aus Kaltleiter KTY81-120 und Widerstand 1420 Ohm
' beschaltet; der Analogwert wird in Temperatur umgerechnet. Der Spannungsteiler liegt
' aus Stromspargründen nicht direkt an der Betriebsspannung, sondern wird über PB.3 geschaltet.
' Da der Attiny13 nur einen Timer hat und der für die Tonerzeugung genutzt wird, dient als weiterer
' Timer der Watchdog. Er läßt sich auf max. 8 sec Ansprechzeit konfigurieren. Da diese Zeit recht
' kurz ist, wird sie über eine Zählschleife verlängert; bei jedem Ansprechen wird die Variable E
' hochgezählt. Überschreitet sie einen (konfigurierbaren) Wert, startet die Temperaturmessung neu.
' Standardmäßig löst der Watchdog Resets aus. Die Variable E würde dadurch jeweils auf den
' Anfangswert gesetzt und die Schleife nicht funktionieren. Abhilfe: Der Watchdog läßt sich (etwas
' umständlich) auf Interruptbetrieb konfigurieren.
' Der Attiny13 ist auf 16 kHz geflasht (Stromersparnis).
' Bei anderem Takt muß $crystal, Timer0-Prescale und 'Const Tonhoehe' geändert werden
' Erläuterungen zum Morseteil:
'
' Codierung der Morsezeichen: 1 Byte pro Zeichen, niederwertiges Bit zuerst.
' Ist der Bit-Wert 0: Morsepunkt (dit)
' Ist der Bit-Wert 1: Moersestrich (dah)
' Da Morsezeichen unterschiedlich lang sind, ist das Ende- Zeichen ebenfalls ein 1-Bit.
'
' Beispiel Morsezeichen "a" (dit dah): &B00000110
' Beispiel Morsezeichen "9" (dah dah dah dah dit): &B00101111
' Auslesen der codierten Zeichen:
' niederwertigstes Bit auslesen, Byte um 1 Stelle nach rechts schieben, niederwertigstes Bit
' auslesen. Ist der Gesamtwert des Bytes nur noch 1, ist das Zeichen komplett.
' Timer0 erzeugt den Mithörton. Pin PB0 wird als Timer0-Ausgang OC0A verwendet.
' Pin PB3 schaltet den Spannungsteiler nur während der Messung an Spannung (Stromersparnis).
' Pin PB4 ist der Analogeingang.
'
' Änderungen Version 5 -> Version 6a:
' Spannungsteiler- Widerstand von 1 kOhm auf 1420 Ohm geändert
' Die Umrechnung AD- Wert -> Temperatur erfaßt jetzt auch Minusgrade.
' Da der Zusammenhang zwischen AD- Wert und Temperatur nicht ganz linear ist, wird in 3 Stufen interpoliert
' (der 0-Grad-Basiswert 373 wird um jeweils 1 erhöht)
'
' Die Pulldown- Widerstände an PB1 und PB2 wurden entbehrlich durch Setzen des Registers DIDR0
'
' Da der Flashspeicher knapp wurde, sind einige Befehle durch kompaktere Varianten ersetzt.
' - z.B. statt " x = x+1 " : " incr x "
' - Powerdown durch direktes Beschreiben des Registers und Assembler- Befehl "Sleep"
' Die Behandlung von Leerzeichen wurde in der Morseroutine weggelassen
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Grundeinstelllungen:
$regfile = "ATtiny13.dat"
$crystal = 16000 ' Taktfrequenz: 128 kHz / 8
$hwstack = 8 ' nur max 4 geschachtelte Unterprogramme; spart SRAM
$swstack = 0 ' nicht benötigt; spart SRAM
$framesize = 0 ' dito
Baud = 9600
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Konstanten
' vom Nutzer anzupassen:
Const Dit = 40 ' Länge eines Punkts, daraus ergibt sich die Morsegeschwindigkeit
Const Tonhoehe = 14 ' Tonhöhe
Const Msg_laenge = 20 ' Anzahl der DataBytes; maximal 47
Const Wdzaehler = 128 ' Multiplikator,um Watchdog- Zeit zu vervielfachen.
Const Basis = 370 ' AD- Wert bei 0 Grad C
' ' theoretisch 373, muß etwas angepaßt werden, wenn
' ' das Morsethermometer ungenau ist
'
' hier nichts ändern:
Const Dah = Dit * 3 ' Länge eines Strichs
Const Wort = Dit * 8 ' Länge der Pause zwischen Wörtern
Const Korr1 = Basis + 60 ' Korrektur bei > 30 Grad
Const Korr2 = Basis + 79 ' Korrektur bei > 40 Grad, gilt bis etwa 45 Grad
Const Korr3 = Basis + 98 ' Korrektur bei < 50 Grad
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Variablen:
Dim I As Byte ' wird in Zählschleife verwendet
Dim N As Byte ' Aktueller Morsebuchstabe
Dim M As Word ' wird fürs Auslesen der Morsebits verwendet
Dim L As Byte ' niederwertigstes Bit von M verwendet
Dim Ana As Word ' ADC
Dim Zehner As Word ' Zehnerstelle des umgerechneten Analogwertes
Dim Einer As Word ' Einerstelle des umgerechneten Analogwertes
Dim Msg(msg_laenge) As Byte ' Feld für die Bakentext- Bytes
Dim E As Byte ' Multiplikator, um Watchdog- Timer zu verlängern
Dim Frost As Bit ' wird für die Ausgabe des Vorzeichens "-" benötigt
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Restore Message ' Feld einlesen
For I = 1 To Msg_laenge
Read Msg(i)
Next
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Initialisierung des Programms:
'
Acsr.acd = 0 ' Analog-Komparator ausschalten, spart etwas Strom
Ddrb = &B00001000 ' Pin PB3 als Ausgang konfigurieren (1) , Rest ist Eingang (0)
Portb = &B00000000 ' keine PullUp-Widerstände
Didr0 = &B00011111 ' Digital- Eingänge abschalten, da keiner benötigt wird
' ' spart Strom im Powerdown und verhindert, daß Eingänge floaten
' ' und dabei exzessiv Strom ziehen
'
' Morseton mit Timer0 erzeugen:
' Timer0 : Vorteilung(prescale) = 1 ; Bei Compare -match Toggle Pin Oc0a / Pb0
'
' 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 = 1 , Compare A = Toggle , Clear Timer = 1
Ocr0a = Tonhoehe
'
'-------------------------------------------------------------------------------
' Hauptprogramm:
E = Wdzaehler ' damit der Meßvorgang nach einem Reset sofort startet
Do
Incr E ' Watchdog- Verlängerer um 1 erhöhen
If E > Wdzaehler Then ' Watchdog- Verlängerer; Bedingung erfüllt
Wdtcr.6 = 0 ' stoppt Watchdog; Watchdog- Interrupts würden die Morseausgabe stören
For N = 11 To 14 ' wir senden "temp"
Gosub Morse
Next N
Gosub Messen ' wir fragen den AD- Wandler ab
If Frost = 1 Then ' unter 0 Grad
For N = 16 To 20 ' wir senden "minus"
Gosub Morse
Next N
Waitms Wort
End If
N = Zehner + 1 ' Zehnerstelle des Messwerts
Gosub Morse ' wir senden die Zehnerstelle
N = Einer + 1 ' Einerstelle des Messwerts
Gosub Morse ' wir senden die Einerstelle
Waitms Wort
N = 15 ' wir senden "c"
Gosub Morse
E = 0 ' Watchdog- Verlängerer zurücksetzen
End If
' direkte Registermanipulation, da der Bascom- Befehl "Config Watchdog" nur den Resetmodus kennt. Die Vorbereitungs-
' prozedur ist nötig, da einzelne Bits des Watchdogregisters gegen (unbeabsichtige) Veränderungen besonders geschützt sind.
Sreg.7 = 0 ' Interrupts sperren
Wdtcr = &B00111001 ' Schreiben auf WDTCR Vorbereiten
Mcusr.3 = 1 ' Schreiben auf WDTCR vorbereiten
Wdtcr = &B01100001 ' Interrupt statt Reset, Watchdog- Timer auf 8 sec setzen
Sreg.7 = 1 ' Interrupts wieder freigeben
' Powerdown; direkte Registermanipulation spart Flash
Mcucr = &B00110000
!Sleep
Loop
' Auslesen des ADC
Messen:
Portb.3 = 1 ' Spannung an Spannungsteiler legen
Config Adc = Single , Prescaler = Auto , Reference = Avcc
Waitms Wort ' Wortlänge warten, bis sich alles eingeschwungen hat
Start Adc
Ana = Getadc(2)
If Ana > Basis Then ' Plusgrade
Ana = Ana - Basis
If Ana > Korr1 Then ' Kennlinien- Korrektur
Incr Ana
End If
If Ana > Korr2 Then ' Kennlinien- Korrektur
Incr Ana
End If
If Ana > Korr3 Then ' Kennlinien- Korrektur
Incr Ana
End If
Else ' Minusgrade
Frost = 1
Ana = Basis - Ana
End If
Shift Ana , Right ' entspricht " Ana = Ana / 2 " spart aber Flash
Zehner = Ana / 10 ' Zerlegung in Einzelzahlen
Einer = Ana Mod 10
Stop Adc ' Strom sparen
Portb.3 = 0 ' Spannungsteiler stromlos machen; Strom sparen
Return
' Morsezeichenerzeugung:
Morse:
M = Msg(n) ' aktuelles Zeichen aus Kette rauspicken
' Wird in diesem Programm nicht benötigt:
' 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
Ddrb.0 = 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
Ddrb.0 = 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
'- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
' Codierung der Morsezeichen. Bei einem Prozessor mit viel SRAM würde man die Zeichentabelle
' in ein Array einlesen, den in Morse zu codierenden Text in einen String packen und diesen
' String zeichenweise abarbeiten.
'
' Da der Attiny13 nur wenig SRAM hat, ist der Weg hier anders: Der zu codierende Text ist ein
' Array mit 16 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 #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
Message:
Data #0 , #1 , #2 , #3 , #4 , #5 , #6 , #7 , #8 , #9
Data #t , #e , #m , #p , #c , #m , #i , #n , #u , #s