Melodiegenerator mit Highspeed-PWM-Ausgabe mit Tiny15...

Aufgrund dieses Threads im Mikrocontroller-Forum habe ich mir Gedanken gemacht, wie man die Soundausgabe und die anderen Eigenschaften dieses Melodiegenerators verbessern kann. Ziel war ein sinusähnlicher Ton mit abklingender Amplitude. Erreicht wurden folgende Eigenschaften:

Schaltung

Es sind 4 Taster am Tiny15 angeschlossen, mit denen 4 verschiedene Melodien aufgerufen werden können. Damit der Tiny15 beim Betätigen eines Tasters über den Low-Level-Interrupt an Int0 aus dem Power-Down-Mode geweckt werden kann, sind drei Dioden eingebaut, die bei jedem Tastendruck den Int-Eingang mit auf GND ziehen. Bei längeren Leitungen zu den Tastern empfiehlt es sich, NPN-Transistoren an den Eingängen vorzusehen, die den AVR-Pin auf GND ziehen (siehe rechtes Bild). Die Klingeltaster werden dann gegen +4,8V geschaltet, nicht mehr gegen GND.

Schaltbild s_melo4.png  Ersatz der Taster durch Transistoren

Der Ausgang wird mit einem komplementären Emitterfolger (als Impedanzwandler) verstärkt, da dieser einen geringen Ruhestrom aufweist. Der Lautsprecher ist gleichstromfrei über einen Elko 470µF angeschlossen. An PB3 (ADC2) ist über einen (im Vergleich zum internen PullUp) niederohmigen Wiederstand eine Reihenschaltung von 2 LEDs gegen GND angeschlossen, die als Z-Diode missbraucht werden. Bei eingeschaltetem internen PullUp fällt an ihnen eine konstante Spannung von 3,22V ab. Diese beeinflusst das Einlesen des Tasters nicht und erlaubt einen Vergleich der Betriebsspannung (NiCd-Akku 4,8V) als ADC-Referenzspannung mit dem an ADC2 fest anliegenden Spannungsabfall über den LEDs. Dies ermöglicht eine einfache Akku-Überwachung mit Alarm-Sequenz, die an darauf hinweist, den Akku mal wieder zu laden.


Programm

Das Programm wurde mittels AVR-Studio in AVR-Assembler geschrieben. Dabei gliedert sich das Programm in mehrere Teile, die teils in Interrupts abgearbeitet werden. Zwischen den Interrupts wird der Tiny15 bei laufender Soundausgabe in den Sleepmode IDLE versetzt, außerhalb der Soundausgabe wird der Sleepmode Power-Down aufgerufen, was den Standby-Stromverbrauch reduziert.

Gliederung des Programms:

Der Ablauf beim Betätigen eines Tasters ist folgender:


Ermittlung des Akkuzustandes

Bei jedem Melodieaufruf werden die internen PullUp-Widerstände aller Taster eingeschaltet. An PB3 (ADC2) ist aber neben dem Taster auch noch eine Reihenschaltung aus 2 LEDs angeschlossen, an denen bei aktivem PullUp eine von der Betriebsspannung des AVRs unabhängige Spannung von etwa 3,22V abfällt.

Diese feste Spannung wird nun jede Millisekunde eingelesen, wobei als Referenzspannung die Betriebsspannung des Tiny15 genutzt wird, die ja die Akkuspannung ist und bei leerer werdendem Akku geringer wird. Es wird also nicht die übliche Messmethode angewendet, bei der eine variable Spannung gegen eine konstante Referenzspannung gemessen wird, sondern umgekehrt, es wird eine feste Spannung gegen eine variable Referenzspannung gemessen. Das ergibt

bei vollem Akku (4,8V): 256 * 3,22V / 4,8V = 172
bei leerem Akku (4,2V): 256 * 3,22V / 4,2V = 196
als ADC-Wert bei 8 Bit Auflösung.

Der eingelesene ADC-Wert wird nun auf Plausiblität geprüft und verworfen, falls er zu klein ist, weil der Taster an PB3 betätigt ist. Ist der Wert plausibel, dann wird daraus der Mittelwert über 256 Messungen ermittelt. Die Mittelwertberechnung erfolgt, indem bei jedem neuen Wert 1/256 des momentanen Mittelwertes vom Mittelwert subtrahiert wird und danach 1/256 des neuen Wertes zum Mittelwert addiert wird. Das liest sich komplizierter als es ist, den der Bruch '1/256' wird durch einfaches Byteshifting erreicht. Als ASM-Code sieht das dann so aus:

 sub volt0,volt             ;1/256 vom Mittelwert
 sbc volt,null              ;incl. Übertrag subtrahieren
 add volt0,wl               ;1/256 des eingelesenen Wertes
 adc volt,null              ;mit Übertrag addieren
Diese Methode ist sehr ressourcensparend und benötigt zur Ausführung nur 4 Takte. Als Startwert für den Mittelwert wird in der Reset-Routine der Grenzwert eingetragen, der den Alarm auslöst. Somit bewegt sich der Mittelwert von Anfang an in die entsprechende Richtung (Akku voll oder Akku leer). Es werden etwa 1000 Messungen pro Sekunde gemacht, von denen aber nur die letzten 256 relevant sind.

Ist die Melodie zu Ende (Ende-Kennung 0), wird der Mittelwert mit dem Grenzwert verglichen und ggf. die Alarm-Melodie ausgegeben. Damit keine Endlosschleife entsteht, hat die Alarm-Melodie eine eigene Ende-Kennung, bei deren Auftreten die Tonausgabe ohne Akkutest beendet wird.

Leider ist die Ruhestromaufnahme immernoch etwas hoch, da über den PullUp für Int0 und die Diode etwas Strom über die LEDs fließt. Abhilfe schafft der Verzicht auf den Taster an PB3 und das Weglassen der Diode. Das bedeutet aber auch Verzicht auf eine Melodie, es können dann nur mit 3 Tastern 3 Melodien aufgerufen werden. Aber wer braucht schon 4 Klingeltaster an seiner Türklingel, meist reicht ja einer oder zwei.


Vereinbarte Werte in den Daten...

Jeder Datensatz besteht aus 2 Bytes. Das erste Byte ist der Timer-Reload-Wert und bestimmt die Frequenz, das zweite Byte bestimmt die Dauer (in Zehntelsekunden). Sehr kleine Timer-Reload-Werte sind unbrauchbar, da benachbarte Frequenzen zu weit auseinander liegen. Daher können die kleinen Werte als Steuerkommandos interpretiert werden. Vereinbart zur Steuerung sind:

Jede Melodie sollte mit dem individuellen Dämpfungswert beginnen, auch innerhalb einer Melodie kann die Dämpfung verändert werden, indem man einfach einen neuen Dämpfungswert einträgt.