Projekte bei denen Wavedateien
abgespielt werden gibt es viele. Bei den meisten ist die dazu
implementierte Funktionalität jedoch ein fester Bestandteil der
Firmware und dadurch nur oft schwer zu verstehen und oft auch nicht
gerade einfach in seine eigenes Projekt zu übernehmen. Zudem ist je
nach verwendeten Mikrocontroller die Ausgabemöglichkeit der
Audiodaten unterschiedlich, was aber unter Umständen nicht auf den
ersten Blick ersichtlich ist. Die erste Alpha-Version der hier
vorgestellten Library, welche auf GitHub
verfügbar ist, besitzt im Moment die im folgenden beschriebenen
Eigenschaften.
Eigenschaften der Library:
- Die Library läuft momentan nur auf AVR-Controllern der Mega-Reihe (getestet mit ATMega328p).
- Es werden nur Wave-Daten unterstützt die im Flash oder EEPROM abgelegt sind (muss per Preprozessor-Einstellung festgelegt werden).
- Die Wave-Daten müssen im PCM-Format (8Bit, Mono, 8kHz Sampling-Rate) und ohne den 44Byte Header im Flash oder EEPPROM abgelegt worden sein.
- Die Daten werden per Pulse Width Modulation (8Bit) an den Pins PD5 und PD6 ausgegeben. Die Basisfrequenz liegt hier bei 62,5 kHz.
- Timer0 wird dazu verwendet das PWM-Signal (Fast PWM Modus) zu generieren.
- Timer2 wird dazu verwendet die Wave-Daten Byte-weise, im Takt der Sampling-Rate, an die beiden Output Compare Register (A/B) des Timer0 zu übergeben.
Die Library besitzt zudem Funktionen mit denen es
möglich ist, die Hard- und Software zu initialisieren und den
Abspielvorgang zu starten, stoppen und pausieren. Wenn die
Preprozessor-Einstellung für Flash angegeben wurde, kann ein im
Repository liegendes Shellscript im tools
Verzeichnis dazu verwendet die benötigte C-Datei für die Firmware
zu erzeugen (es wird SoX
benötigt). Zu beachten ist dabei, dass aufgrund des in C intern
verwendeten Datentyps (16Bit signed
integer) für den Index
von Arrays, die Maximale Anzahl von Samples in der Wave-Datei den
maximal möglichen Index von 32767 nicht überschreiten darf.
Ablauf der Ausgabe:
Der Vorgang zum abspielen gestaltet sich wie folgt. Der Timer0
(8Bit) wird so eingestellt, dass dieser mit einen Vorteiler von 1
(voller CPU-Takt) im nicht invertierenden Fast PWM-Modus läuft und
der TOP-Wert für den Zähler des Timers ist dabei der Wert 0xff. Der
Counter des Timers läuft also immer von 0 bis 255, was eine Frequenz von 62,5kHz, ergibt.
Ist bei einem Zählvorgang der TOP-Wert erreicht
bzw. überschritten, findet ein Überlauf statt, der veranlasst, dass
beiden Pins PD5/6 auf High gesetzt werden. Der Counter fängt wieder
bei 0 an hoch zu zählen.
Auf Low werden die beiden Pins gesetzt wenn jeweils
ein Vergleichswert zum aktuellen Counter-Wert erreicht wird. Diese
beiden verwendeten Vergleichswerte sind im Fall des Timer0 die
Register OCR0A/B. Durch diesen Vergleichswert kann also die Zeit in
256 Schritten festgelegt werden, wie lange das PWM-Signal, innerhalb
einer Periode, auf High oder Low gesetzt ist.
Der zweite Timer (Timer2) ist auch ein 8Bit Timer,
der jedoch nich im PWM-Modus läuft. Er wurde so eingestellt, dass er
mit einem Prescaler von 8 auf 2MHz läuft und bei einem
Vergleichswert von 250 (eingestellt über das Register OCR2A, was
exakte 8kHz ergeben und somit die Unterstützte Samplingrate der
Wave-Daten von 8kHz entspricht) einen Interrupt auslöst der eine
Interruptroutine aufruft. Gleichzeitig wird der Counter des Timer
wieder auf 0 gesetzt, der darauf wieder hoch zählt. In der eben
genannten Interrupt-Routine wird je nach Konfiguration der Lib, ein
Byte nach dem anderen, entweder aus den Wave-Daten aus dem Flash oder
EEPROM nachgeladen und in die beiden oben genannten
Vergleichsregister OCR0A/B geschrieben. Dies führt dazu, das der
Timer0 nachdem nächsten Überlauf, durch erreichen des
Counter-Wertes 0x00, das Tastverhältnis direkt ausgibt.
Dieser Wechsel des PWM-Verhältnisses, im Takt der
Sampling-Rate, ergibt eine Veränderung der mittleren
Ausgangsspannung des PWM-Signals und ist damit (wenn auch recht
kantigen) das wiederhergestellte Ausgangssignal aus dem die
PCM-Kodierte Wavedatei erzeugt wurde. Dies liegt daran, da bei der
Pulse Code Modulation, ein Audiosignal mit einer festen Sampling-Rate
(in unserem Fall 8kHz) abgetastet wird. Die Abtasten bedeutet
schlicht, dass in einem festgelegten Takt der momentan anliegende
Spannungswert an der Datenleitung (in unserem Fall 8Bit-Wert breit)
abgefragt wird. Es ergibt sich also eine folge von Werten, aus denen
das erfasste Signal wieder rekonstruiert werden kann.
Das ganze oben beschriebene Vorgang ist natürlich
nur recht kurz und simpel beschrieben. Er soll lediglich nur einen
Überblick über den Vorgang geben. Fehler und Ungenauigkeiten können
also enthalten sein und ich bin über jede Anregung oder Korrektur
dankbar.
Verwendete Hardware:
Als Hardwarebasis zum testen verwende ich ein
Minimexle der Version 3, bei dem
ich ein anderes Quarz (16MHz statt 18,432MHz) und einen anderen AVR
(ATMega328p statt einem ATMega88) verwende. Durch den AVR mit
größerem Flash-Speicher (32kB) sind die weiteren Möglichkeiten in
der Firmware wesentlich weniger eingeschränkt als beim Mega88. Zudem
besitzt das Minimexle ein Display mehrere Taster und einen Summer. So
kann hier die Funktionalität der Lib durch ein simples
Benutzerinterface angeboten und auch ohne angeschlossene
Kopfhörer etc. die Wave-Daten abgespielt werden können.
Und dadurch, dass beim Minimexle jeder der beiden verwendeten
PWM-Kanäle mit einem RC-Tiefpass
(erster Ordnung) ausgestattet ist, bevor diese an 3,5"
Klinken-Buchsen ausgeführt werden, können einfache Lautsprecher,
wie man sie aus PC-Gehäusen als PC-Speaker kennt, oder sehr einfache
Kopfhörer sollten ohne Probleme an diese Buchse angeschlossen
werden.
Warnhinweis:
Von der Verwendung von aktiven Kopfhörern, Stereoanlagen, Verstärkern
oder ähnlichem rate ich jedoch aufgrund der Signaleigenschaften des
PWM-Signals dringend ab. Es versteht sich von selbst, dass ich
keinerlei Verantwortung für Schäden jeder Art durch Verwendung dieser Software übernehme.
Für die Zukunft ist angedacht, den Header der
Wave-Dateien zur Konfiguration der Timer heranzuziehen, weitere
Sampling-Raten zu unterstützen und die Anzahl der verwendeten Timer
auf einen zu reduzieren. Zudem soll der zu verwendende Timer
auswählbar sein und falls die PWM-Pins, dieses Timers, anderweitig
verwendet wurden, ein Software PWM-Modus verfügbar sein der für
beliebige Pins, an einem beliebigen Port, betrieben werden kann,
usw...
Weitere Links:
- Grundlegendes zur Tonerzeugung mit Hilfe von Mikrocontrollern. - http://www.mikrocontroller.net/articles/Klangerzeugung
- Umwandlung von Wave-Dateien in C-Code - http://www.mikrocontroller.net/topic/132749
- Converting PCM to PWM audio. - http://www.eetindia.co.in/STATIC/PDF/200809/EEIOL_2008SEP19_SIG_TA_02.pdf?SOURCES=DOWNLOAD
Keine Kommentare:
Kommentar veröffentlichen