Lösung zu Übung 8: Erzeugen eines Sinussignals von 440Hz über einen PWM gepulsten Digitalausgang
Vorüberlegungen
Was soll erreicht werden?
- Was muß erfüllt sein, damit ein Sinussignal von 440Hz über einen PWM-Ausgang erzeugt werden kann?
- Bekanntermaßen müssen in einer Periode eine Vielzahl an Pulsen unterschiedlicher Länge geschickt werden, damit ein glatter Verlauf zustande kommt.
- Angenommen, eine Periode soll in 100 Pulse zergliedert werden.
- Dann müßte ein PWM-Signal der Frequenz 44000Hz erzeugt werden.
- Da aber mit jedem Puls auch noch eine andere Pulsbreite geschickt werden soll und das Hauptprogramm diese Pulsbreitenänderung immer mindestens zwischen zwei Pulsen durchführen muß, sollte die PWM-Frequenz noch höher sein.
- Beispielsweise könnte man sich vorstellen, dass in dem Zeitraum zwischen zwei Pulsbreitenänderungen immer 5 Pulse gesendet werden.
- Damit wäre eine PWM-Frequenz von 220000Hz notwendig.
|
Was ist möglich?
- Der zu verwendende Lautsprecher ist bei Pin27, OC2, PD7 angeschlossen.
- Das bedeutet, er kann entweder über Bit 7 des digitalen Ports D angesteuert werden, oder über den PWM-Ausgang von Timer 2
- Ein Blick in das Datenblatt des ATmega32 bei Timer 2 zeigt:
- Die minimale Vorteilung ist N=1.
- Bei dem schnellsten PWM-Mode "Fast PWM Mode" errechnet sich die Frequenz des PWM-Signals nach f=fquarz/(2*N*256)
- Bei 9,216MHz Quarzfrequenz ergibt sich so f=18000Hz.
|
Versuch einen Kompromiß zu finden
- Angesichts der Begrenztheit der maximalen PWM-Frequenz soll ein Kompromiß gesucht werden.
- Eine vielleicht noch ausreichende Teilung der Sinus-Periode könnten 16 Schritte sein.
- In diesem Fall benötigt man nur noch eine Mindest PWM-Frequenz von 7040Hz.
- Mit den möglichen 18000Hz würde man in einem der 16 Teilschritten dann 2 bis 3 Pulse schicken können.
- Nun muß man berücksichtigen, dass bei diesem Vorhaben Schmutzeffekte auftreten können:
- Ist die Pulsbreitenänderung nicht gut mit der PWM-Frequenz synchronisiert, so können Schwebungen und/oder Knackgeräusche entstehen.
- Am günstigsten wäre es, wenn die PWM-Frequenz und die Pulsbreiten-Änderungsfrequenz weit im unhörbaren Frequenzbereich lägen.
- Dann wäre ein zumindest vom Höreindruck sauberes Signal zu erwarten.
- Bei dem hier angestrebten Kompromiß ist zu erwarten, dass das Signal nicht besonders sauber ist.
|
Einrichten der PWM-Breiten-Änderung
- Im Gegensatz zu der PWM-Frequenz muß der Verlauf der PWM-Breitenänderung in jedem Fall genau mit einem ganzen Vielfachen der gewünschten Frequenz von 440Hz erfolgen.
- In unserem Fall wird eine Frequenz von genau 7040Hz benötigt.
- Ist dies realisierbar?
- Es wurde die Technik mit einer Warteschleife am Ende der Endlosschleife erarbeitet.
- Die Anwendung dieser Technik setzt voraus, dass die restlichen Befehle der Endlosschleife insgesamt weniger Zeit benötigen, als die eingesetzte Wartezeit.
- Wahrscheinlich scheidet deshalb schon die Methode aus, bei der der Sinuswert zu jedem Teilschritt berechnet wird. Eine Lookup-Tabelle ist sicher nortwendig.
- Zudem kann mit dieser Methode nicht jede beliebige Frequenz realisiert werden.
- Timer 1 wurde bereits im 16Bit-Modus eingesetzt und hatte damit eine maximale Oberschranke von hexadezimal 0xFFFF, also dezimal 16383.
- Als Vorteilung möglich sind hier die Werte 1,8,64,256 und 1024.
- Mit 9216000/7040 ergibt sich eine gewünschte Teilung von 1309,0909..
- Bei einer Vorteilung durch 8 verbleibt eine Restteilung von 163,636.
- Setzt man in die Warteschleife den nächst gelegenen ganzzahligen Wert 164 ein, erreicht man dann eine Sinus-Frequenz von 439,024Hz.
- Dies entspricht dem Kammerton zur Zeit des Barock und soll uns jetzt einmal von der Genauigkeit genügen.
- Es gibt auch einen Mode, bei dem der maximale Zählerwert (TOP) durch einen Eintrag in das Register OCR1A bestimmt wird. Dies läßt eine größere Anzahl an Frequenzeinstellungen zu.
- Alternativ kann auch nach einem Quarz gesucht werden, der bessere Werte liefert.
|
Umsetzung des Programms
Lösung Übung 8 - 440Hz.zip
Vorausberechnung der Sinuswerte mit einem Scilab-Skript:
x=zeros(16,0);
t=0;
for i=0:1:15
t=(i*2.0*%pi)/16;
x=round( 0.5*(sin(t)+1.0)*255 )
end
Code 0-1: Scilab-Skript sinus_berechnen.sce
Mikrocontroller-Programm zur Erzeugung des Sinussignals
#include <avr/io.h>
int main(void)
{
int i=0;
//Die 16 im Voraus berechneten und im Bereich 0..255 skalierten Sinuswerte:
unsigned char sinus[16] = {128,176,218,245,255,245,218,176,
128,79,37,10,0,10,37,79};
DDRD |= 0b10000000;
//Konfigurieren des Timers 2 als PWM-Ausgang im Fast-PWM-Mode, OC2 set on compare match,
//Vorteilung 1:
TCCR2 = (0<<FOC2) | (1<<WGM20) | (1<<COM21) | (0<<COM20)
| (1<<WGM21) | (0<<CS22) | (0<<CS21) | (1<<CS20);
//Konfigurieren des Timers 1 als normaler Zähler,
//Vorteilung 8:
TCCR1A = (0<<COM1A1) | (0<<COM1A0) | (0<<COM1B1) | (0<<COM1B0)
| (0<<FOC1A) | (0<<FOC1B) | (0<<WGM11) | (0<<WGM10);
TCCR1B = (0<<ICNC1) | (0<<ICES1) | (0<<WGM13) | (0<<WGM12)
| (0<<CS12) | (1<<CS11) | (0<<CS10);
while(true)
{
OCR2 = sinus[i];
while(TCNT1<164);
TCNT1 = 0;
i++;
i%=16;
}
return 0;
}
Code 0-2: Mikrocontroller-Programm, um ein 439Hz Sinussignal zu erzeugen.