ATtiny13-Simulation eines mechanischen Morse-Bugs

von Ralf Beesner, DK5BU                

Elektronik-Labor  Mikrocontroller


Im Alter neigt man zu Sentimentalitäten, und da die meisten Telegrafie-Freunde in Ehren ergraut sind, erinnern sie sich an die mechanischen halbautomatischen Tasten (Bugs), die sie vor Jahrzehnten in den Schrank gestellt hatten.

Bugs erzeugen die Punkte mit einem schwingenden Pendel, während die Striche einzeln von Hand getastet werden müssen. Die Punkte werden nicht automatisch auf die richtige Länge vervollständigt, sondern brechen ab, sobald der Hebel in die Ruhelage gebracht wird. Beides führt zu einem charakteristischen Bug-Sound, den man nun wieder häufiger auf Kurzwelle hört.

So hatte ich ebenfalls meinen knapp 50 Jahre alten Hi-Mound BK-100 hervor gekramt und benutze ihn fast täglich in einer Bug-Sideswiper-Runde per Internet-Telegrafie sowie auf KW, wenn ich dort jemanden mit Bug-Sound höre. Daraus entstand die Idee, für portable Einsätze eine elektronische Nachbildung der Bug-Eigenheiten auf einem ATtiny 13 zu programmieren. Für diesen “Bug” benötigt man keine waagrechte, feste Tischfläche; man kann ihn einfach in die linke Hand nehmen und mit der rechten Hand bedienen.

Die Nachbildung der Bug-Eigenheiten ist aufwendiger als ein herkömmlicher elektronischer Keyer im Plain-Iambic-Mode. Bei dem fragt man Punkt- und Strichkontakt im Leerlauf ständig ab, aber während der Punkt- oder Stricherzeugung nicht mehr. Da zählt nur ein Timer runter, bis das Zeichen-Element vollständig ist (fire and forget). Bei einer elektronischen Bug-Nachbildung muss der ATtiny ständig in einer Abfrageschleife rotieren, um auf das Loslassen des Tasten-Hebels zu reagieren. Außerdem sollte meine elektronische Bug-Nachbildung nicht mechanisch getastet werden, sondern mit robusten, simplen und leichten kapazitiven Sensorpaddles arbeiten.

Auf dem obigen Bild sieht man den Versuchs-Aufbau mit einigen nicht genutzten Bauteilen; ein “ordentliche” Version soll noch folgen.


Hardware

Schaltplan


Streifenleiterplatinen-Layout, Blick von oben. Rotbraun: Drahtbrücken, blau: Leiterbahnen auf der Unterseite

Die Schaltung kommt mit nur 8 Bauteilen aus. PB2 muss für die Sensor-Routine unbeschaltet bleiben. Das Geschwindigkeitspoti liegt daher am Reset-Pin bzw. am vierten ADC-Eingang. Der Nachteil ist, dass man an diesen ADC-Eingang nur Spannungen anlegen darf, die größer als die halbe Betriebsspannung sind, damit kein Reset ausgelöst wird. Außerdem ist am Reset-Pin ständig ein interner Pullup-Widerstand aktiv. Ich habe aus der Not eine (bauteilsparende) Tugend gemacht: das Poti erspart den ohnehin erforderlichen Reset-Taster, wenn man es auf weniger als etwa 20% dreht.

PB1 gibt das Sender-Tastsignal aus. Er steuert einen Schalttransistor für die Tastleitung eines KW-Transceivers an. Die Basis ist gegen HF-Einstreuungen mit 10 nF abgeblockt . PB3 und PB4 sind die Sensor-Eingänge; zwei Widerstände in den Sensor-Leitungen verbessern laut Atmel-Appnote die Festigkeit gegen HF-Einstrahlung.


Sensor-Flächen

Die beiden Sensorflächen bestehen aus Streifenleiterplatinen. Sie sind Rücken an Rücken verschraubt bzw. verlötet (im hinteren Teil, der auf Masse liegt, die eigentlichen Sensorflächen sind davon durch Unterbrechungen isoliert). Dazwischen liegen 2 Lagen kupferfreie Hartpapier-Lochrasterplatte, die den Abstand vergrößern und die kapazitive Kopplung vermindern sollen.


Die Größe der aktiven Sensorflächen beträgt 9 * 9 Löcher (siehe Foto eines anderen Sensor-Elbugs): Man kann die Abmessungen etwas vergrößern, da die Software nach jedem Reset die Leerlauf-Kapazität neu ermittelt. Wenn man allzu sehr von den Abmessungen abweicht, könnte die aus dem Leerlauf-Wert abgeleitete Ansprech-Schwelle nicht mehr passen. Bei einem anderen Keyer hatte ich die Flächen 10 * 14 Löcher groß gemacht und vier kupferfreie Hartpapier-Lochraster-Streifen dazwischen gelegt. Letzteres vermindert die gegenseitige kapazitive Beeinflussung noch weiter.


So arbeitet die Sensor-Routine:

Der ATtiny hat nur einen ADC, aber vier ADC-Pins. Sie werden per Registerbefehl (“AdMux”) mittels interner FET-Schalter einzeln auf den ADC durchgeschaltet. Am eigentlichen ADC-Eingang hängt ein kleiner interner “Sample&Hold”-Kondensator (da eine Messung in mehreren Teilschritten erfolgt, soll er die Messspannung jeweils kurz zwischenspeichern).

Der Sample&Hold-Kondensator wird zunächst entladen, indem der ADC mit dem Befehl “AdMux” auf PB2 durchgeschaltet wird. PB2 wurde am Anfang des Programms als Ausgang mit Pegel “low” konfiguriert.

Dann wird einer der beiden Sensorpins als Ausgang umgeschaltet und auf “high” gelegt. Der Pin und die externe Sensorfläche bilden einen Kondensator, der so auf Betriebsspannung geladen wird. Der Pin wird (nach kurzer Wartezeit) als hochohmiger Eingang umkonfiguriert und “floatet” dann. Die Parasitär-Kapazität ist aber noch auf Betriebsspannung geladen.

Nun wird der floatende Pin per “GetADC”-Befehl an den ADC-Eingang durchgeschaltet und lädt den Sample&Hold-Kondensator des ADC auf, bis sich die Spannungen angeglichen haben. Da sich die Ladung nun auf zwei Kondensatoren verteilt, sinkt die Spannung. Sie wird dann gemessen.

Je nach Parasitär-Kapazität des Sensors fließt eine unterschiedliche Ladungsmenge auf den Sample&Hold-Kondensator ab, die Spannungen und damit die gemessenen ADC-Werte unterscheiden sich.

Im Leerlauf liegen sie z.B. um 600. Wird der Sensor berührt, steigt die resultierende Kapazität aus Sensorpin, Sensorfläche und menschlichem Finger an. Es wird mehr Ladung gespeichert. Wird sie an den Sample&Hold-Kondensator abgegeben, hebt sie die ADC-Werte gegenüber dem Leerlauf um z.B. 150 Schritte an.


Gleitender Mittelwert für die Sensor-Zustände

Die Ausgangssignale der Sensoren “flattern” etwas. Je nachdem, wie gross die von den Fingern berührte Fläche ist, variieren die gemessenen ADC-Werte. Bei einem herkömmlichen elektronischen Keyer fällt das nicht groß auf. Da die Sensoren bei der Bug-Nachbildung viel häufiger abgefragt werden und auch die Übergänge zwischen “gedrückt” und “nicht gedrückt” erfasst werden, gibt es bei einer einfachen “Ja/Nein”-Auswertung unzumutbare Kratzgeräusche.

Lösung: zwei Variable “C” und “D” werden bei jeder ADC-Abfrage um eins erhöht oder vermindert, so dass sich gleitende Mittelwerte ergeben. Sie ändern sich nur relativ langsam und eine Umschaltung erfolgt erst, wenn die Lage eindeutig ist. Die Obergrenze des gleitenden Mittelwertes (Variable “Prelldauer”) ist 16; die Umschaltschwelle “Schwelle” ist 12. Sie werden ziemlich am Anfang des Quelltextes als Konstanten definiert und man kann damit experimentieren. Mehrere “If-Then”-Abfragen sorgen dafür, dass die beiden Variablen “C” und “D” nicht überlaufen können, sondern sich zwischen 1 und Prelldauer bewegen.

Ist die Punkt-Schwelle überschritten, wird die Punktlänge “Dit_cnt” auf das Maximum “Dit” gesetzt und heruntergezählt. Ist Dit_cnt = 1, wird zwischen Punkt und (Punkt)-Pause umgeschaltet (“toggle”-Befehl) und Dit_cnt wieder auf Dit gesetzt.

Ein wenig “Vodoo” mit der Variablen “Ditpart” soll das Bug-Verhalten besser simulieren: Drückt man bei einem mechanischen Bug den Hebel auf die Punkt-Seite, wird der Kontakt nicht sofort geschlossen, sondern das Punkt-Pendel muss sich erst einmal in Bewegung setzen. “Ditpart” bildet diese Totzeit nach.

Die Behandlung des Strich-Sensors ist simpel: Bei Überschreiten der Strich-Schwelle wird einfach der Ausgangs-Pin des ATtiny aktiviert.


Einstellbare Punktlänge

Am Poti-Abgriff liegt eine drehwinkel-abhängige Spannung. Der gemessene ADC-Wert wird von einer Konstanten abgezogen, um die Drehrichtung des Potis umzukehren; höhere ADC-Werte führen so zu einer Verkürzung der Punktdauer. Anschließend wird der Wert durch 4 geteilt. Die Division erfolgt ressourcensparend per Schiebe-Befehl. Nach Addition einer Konstante ergibt sich aus den Operationen ein Einstellbereich von ca. 15 - 35 WpM bzw. 75 -175 ZpM.


Automatische Abschaltung

Normalerweise soll ein Watchdog einen Reset auslösen, wenn er nicht schnell genug periodisch durch das gerade aktive Programm zurückgesetzt wird. Dadurch soll gewährleistet werden, dass sich ein Programm nicht unbemerkt für längere Zeit “aufhängen” kann. Man kann den Watchdog aber auch mit Registerbefehlen zu einem Timer umkonfigurieren (und zusätzlich auf die maximale Ansprechzeit von 8 sec setzen). Durch Ausprobieren stellte sich heraus, dass Bascom den (undokumentierten?) Befehl “On Watchdog” versteht, mit dem man sehr einfach die Interrupt-Routine anspringen kann (wie bei einem normalen Timer-Interrupt). In der Bascom-Doku hatte ich ihn nicht gefunden. Die Variable “offtimer” wird in der Interrupt-Routine ca. alle 8 Sekunden inkrementiert. Nach 127 Durchläufen wird zunächst der ADC abgeschaltet (sonst verbleibt ein Ruhestrom), alle Ausgänge als Eingang geschaltet und der Controller in Powerdown geschickt. Damit die Taste sich nicht ungewollt während der Benutzung abschaltet, wird “offtimer” in der Bug-Strich-Routine bei jedem gesendeten Strich auf Null zurückgesetzt.


Weitere Erläuterungen zur Software

Die Ansprechschwelle der Sensoren wird bei Programmstart festgelegt. Der ADC-Wert am Punktsensor wird ein Mal gemessen und der Messwert um 96 erhöht. Der Punktsensor darf in dieser Zeit nicht berührt werden! Falls das doch mal versehentlich passiert sein sollte, muss man erneut einen Reset auslösen, indem man das Geschwindigkeits-Poti kurz auf Minimum dreht.

Wird das Geschwindigkeits-Poti rasch aus dem Reset auf Maximum gedreht, wird die Variable “Bugmode” auf 0 gesetzt; dies erzeugt auch auf der Punktseite einen Dauerton bei Berührung der Sensorfläche. Der Bug arbeitet nun im Sideswiper-Modus. Dieser lässt sich nur durch einen Reset verlassen.


Der gesamte Quelltext:


' Sensor-Bug mit Entprellung
' Wenn man unmittelbar nach Reset (unterer Anschlag Poti) auf Maximum (oberer Anschlag Poti) dreht,
' wird der Sideswiper-Modus aktiviert. Zurueck in Bug-Mode nach neuem Reset

' Sprache: Bascom-AVR-Basic Version 2.0.7.9 Demo/Freeware
' Lizenz: WtfPL (https://de.wikipedia.org/wiki/WTFPL)

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

$Regfile = "ATtiny13.dat"
$Crystal = 1200000                                          ' Taktfrequenz: 1,2 MHz
$Hwstack = 8                                                ' Platz fuer nur 4 Unterprogramme reserviert, spart SRAM
$Swstack = 0                                                ' wird nicht gebraucht
$Framesize = 0                                              ' dito

Dim C As Byte                                               ' gleitender Mittelwert fuer Dah-Erkennung
Dim D As Byte                                               ' gleitender Mittelwert fuer Dit-Erkennung
Dim Anadit As Word                                          ' ADC-Wert fuer Erkennung Dit-Sensor
Dim Anadah As Word
Dim Refana As Word ' speichert nach Reset den Sensor-Schwellwert Dim Offtimer As Byte ' Zaehler fuer Abschaltautomatik Dim Dit As Word ' Punktlaenge Dim Dit_cnt As Word ' wird bis Punktlaenge gesetzt und dann runtergezaehlt Dim Ditpart As Word ' halbe Punktlaenge fuer simuliertes Einschwingen des Bug-Punkt-Pendels Dim Pause As Byte ' Pause=0 : Punkt wird gesendet; Pause=1 : Punktpause Dim Bugmode As Byte

Acsr.acd = 1 ' Analog-Komparator ausschalten, spart etwas Strom Config Adc = Single , Prescaler = Auto ' AD-Converter konfigurieren und starten Start Adc

Ddrb = &B0000_0110 ' DDRB.0 ungenutzt; DDRB.1 Tast-Ausgang; DDRB.2 Sensor-ADC-Referenz Portb = &B000_0000 ' PB1=1 erzeugt Punkt nach (Anzeige Reset-Ende) Didr0 = &B00111111 ' Digitaleingaenge abschalten; spart Strom On Watchdog Onwdt ' Interrupt-Routine fuer Watchdog-interrupt ' Watchdog als Interrupt-Quelle konfigurieren - WDCR: 7=WDTIF 6=WDIE 5=WDP3 4=WDCE 3=WDE 2=WDP2 1=WDP1 0=WDP0 Wdtcr = &B0001_1111 ' Schreiben auf WDTCR Vorbereiten Wdtcr = &B0110_0001 ' Interrupt statt Reset, Watchdog- Timer auf 8 sec setzen Sreg.7 = 1 ' Interrupts global freigeben Waitms 300 ' Vorlaufzeit ADC; auch Wartezeit fuer Erkennung Sideswiper-Modus Gosub DitSensor ' einmal ADC-Leerlaufwert am Punktsensor messen (Anpassung an Sensor) Refana = Anadit ' Leerlauf-Wert Shift Anadit, Right, 2 ' 25% Refana = Refana + Anadit ' Refana 25% groesser als Leerlaufwert Const Prelldauer = 16 ' 32 ' obere Grenze fuer gleitenden Mittelwert Const Schwelle = (Prelldauer / 2) + Prelldauer / 4 ' Umschaltschwelle Gosub Adclesen

Do Do GoSub DahSensor ' ADC-Wert abholen If Anadah > Refana Then Incr C ' gleitenden Mittelwert erhoehen Else Decr C ' gleitenden Mittelwert vermindern End If If C < 2 Then ' Ueberlauf verhindern C = 1 End If If C > Prelldauer Then ' Ueberlauf verhindern C = Prelldauer
End If If C > Schwelle Then ' oberer Hysteresewert ueberschritten PortB.1 = 1 Offtimer = 0 ' Abschalt-Timer zuruecksetzen Else ' unterer Hysteresewert unterschritten PortB.1 = 0 End If Loop Until C < Schwelle

Do GoSub DitSensor
If Anadit > Refana Then Incr D ' gleitenden Mittelwert erhoehen Else Decr D ' gleitenden Mittelwert vermindern End If If D < 2 Then ' Ueberlauf verhindern D = 1 End If If D > Prelldauer Then ' Ueberlauf verhindern D = Prelldauer
End If If D > Schwelle Then ' oberer Hysteresewert ueberschritten If Bugmode = 1 Then ' Bug Mode If Dit_cnt <= 1 Then ' wenn Dit_cnt runtergezaehlt Gosub ADClesen ' ggf. neue Speed abholen Dit_cnt = Dit ' Dit_cnt wieder auf Maximum setzen Toggle Pause ' jedes Mal zwischen Punkt und Pause zwischen Punkten umschalten End If If Pause = 0 Then ' Punkt soll rausgehen PortB.1 = 1 Else ' also waehrend Punkt-Pause PortB.1 = 0 End If Decr Dit_cnt
Else ' Sideswiper-Modus PortB.1 = 1 ' bewirkt Dauerton statt Punkten End If End If If D < Schwelle Then ' unterer Hysteresewert unterschritten PortB.1 = 0 Dit_cnt = Ditpart ' simuliert Einschwingen des Punkt-Pendels Pause = 255 ' uebler workaround, Teil des o.a. Einschwingens. 255 bewirkt, End If ' dass Pause nur ein Mal auf ungleich 1 (entspr. 0) gesetzt wird Loop until D < Schwelle
PortB.1 = 0 Loop Dahsensor: Admux = 1 ' ADC1 (Referenz) an ADC-Eingang legen Portb.4 = 1 ' Pullup an Strichsensor ein Ddrb.4 = 1 ' Strichsensor ist nun Ausgang Waitus 32 Ddrb.4 = 0 ' Strichsensor wieder Eingang Portb.4 = 0 ' Pullup an Strichsensor aus Anadit = Getadc(2) ' Strichsensor auslesen Return Ditsensor: Admux = 1 ' ADC1 (Referenz) an ADC-Eingang legen ; der muss auf Low liegen (bereits zu Anfang fest konfiguriert) Portb.3 = 1 ' Pullup an Punktsensor ein Ddrb.3 = 1 ' Punktsensor nun Ausgang --> Sensorpin laden Waitus 32 Ddrb.3 = 0 ' Punktsensor wieder Eingang Portb.3 = 0 ' Pullup an Punktsensor aus --> Sensorpin floatet Anadah = Getadc(3) ' Punktsensor auslesen Return Adclesen: Dit = Getadc(0) ' Poti an ADC1/PB22 If Dit < 960 Then Bugmode = 1 End If Shift Dit , Right, 2 ' Teilung / 4 -> Werte nun bis 255 Dit = Dit + 32 ' nun bis 287 Dit = 384 - Dit ' nutzbarer Bereich nun: 15 - 35 WpM Ditpart = Dit ' wird genutzt fuer fuer simuliertes Einschwingen des Punktpendels Shift Ditpart , Right, 2 ' Teilung / 2 Ditpart = Ditpart + 32 Return Onwdt: Incr Offtimer ' alle 8 sec wird Offtimer incrementiert If Offtimer > 126 Then ' nach 127 * 8 sec. (17 Min) erfolgt Powerdown Stop Adc
DDRB = 0 Mcucr = &B0011_0000 ' Bit 4=1 und Bit 3=0: Powerdown Bit 5=1: Sleep enable !Sleep ' Assembler-Befehl statt Basic-Befehl "Powerdown"; spart ein paar Bytes End If Return End

Hinweise zur Nutzung:

Das Geschwindigkeits-Poti hat 3 Funktionen: Bei Stellung auf und nahe dem Minimum wird ein Reset ausgelöst. Unmittelbar danach werden die Sensor-Leerlauf-Werte ermittelt. Die Sensorflächen dürfen in diesem Moment nicht berührt werden! Im mittleren Einstellbereich wird die Punktgeschwindigkeit variiert. Dreht man das Poti rasch vom unteren Anschlag zum oberen Anschlag, schaltet der Bug in den Sideswiper-Modus um (auch Cootie-Mode genannt) und verbleibt in ihm bis zum nächsten Reset.

Die Taste sollte an einem System mit sauberen Masse-Verhältnissen betrieben werden. In geschlossenen Räumen sollte die Niederspannungs-Masse auf Null-Potential liegen (z.B. über eine Verbindung mit dem Schutzleiter). Viele Netzteile mit Zwei-Leiter-Anschluss legen die Niederspannungs-Masse nicht auf Null-Potential, sondern heben sie über Entstör-Kondensatoren hochohmig auf die halbe Netzspannung an. Eine solche mit 50 Hz “floatende” Masse bringt die Sensor-Elektronik durcheinander.


Update; eine Korrektur, zwei Verbesserungen:

1. Dit und Dah waren in der Software dem jeweils falschen Pin zugeordnet (ist nun oben korrigiert).

2. Ein Funkfreund (Günter, DL5SDC) hatte für die Schaltung eine "richtige" doppelseitige Platine entworfen und fertigen lassen. Die Sensorflächen hatten deutlich mehr Kapazität, so dass die relative Kapazitäts-Erhöhung beim Berühren der Sensorfläche nur noch gering war und z.T. im allgegenwärtigen 50 Hz-Störfeld "unterging". Bei mir klappte es noch mit einer Schwelle, die 3% über dem Leerlaufwert der Sensorflächen lag, bei Günter nicht mehr.

Günter wies mich auf eine Website hin, die eine elegante logarithmische Glättung von Messwerten beschreibt. Diese wird mit einem simplen Shift-Befehl realisiert, der auch auf sehr kleinen Mikrocontrollern kaum Ressourcen verbraucht:

https://www.ibrtses.com/embedded/exponential.html

Die Skizze fand ich unverständlich und die Erklärung recht umständlich. In einfacheren Worten: Man teilt z.B. ADC-Werte (0...1023) durch 16 und addiert sie laufend auf eine Hilfsvariable. Von dieser Hilfsvariablen zieht man jedes Mal ein Sechzehntel ihres bisherigen Wertes ab. Die Hilfsvariable folgt auf diese Weise langsam den schwankenden ADC-Werten. Der Verlauf ist logarithmisch und entspricht dem Kurvenverlauf an einem RC-Tiefpass.

Die Hilfsvariable (Ditavg, Dahavg) bewegt sich in den Grenzen des ADC-Outputs, kann also nicht überlaufen bzw. unterlaufen. Die Division durch 16 erfolgt durch den simplen, Ressourcen sparenden Befehl " Shift Hilfsvariable, right, 4 ". Damit funktioniert die Glättung sehr viel zuverlässiger als mit der bisherigen Version, auch an der Platine mit der großen Sensorkapazität.

2. Die Sub-Routinen Dahsensor und Ditsensor funktionierten hier 3 Jahre lang "gut genug". Zufällig habe ich erst jetzt entdeckt, dass sie weitaus größere Pegel-Unterschiede zwischen Leerlauf und Berührung liefern, wenn die beiden Befehlszeilen, die vor dem Auslesen des ADC stehen, vertauscht werden - also erst PortB.x = 0, dann DDRB.x = 0 .

Nach dieser Änderung ist die Differenz der ADC-Werte so groß ( z.B. ca. 60 zu 700 statt ca. 520 zu 800), dass es nicht mehr sinnvoll ist, die Schwellenwerte aus einer Leerlauf-Messung zu errechnen; man kann sie einfach als Konstante definieren.

Bei geänderten Abmessungen der Sensorflächen kann man die zu Anfang des Programms definierte Konstante Refana = 128 anpassen, wenn die Sensoren zu rasch oder zu träge auslösen, die Konstante E = 4 .




Download des Update-Quellcodes:  0623-t13-sensor-bug.zip


Elektronik-Labor  Mikrocontroller