Falls jemand ähnliches vorhat: das Zauberwort heißt DDS. Damit läßt sich
sehr elegant jede beliebige Frequenz erzeugen.
Suche hier im Forum.
Bei www.ad.com gibt es sehr schöne Erklärungen zum Prinzip.
Bei kleinen Frequenzen <10kHz kann man das super-einfach in Software
gießen:
#define TabSize 64
char SinTab[TabSize]=(128, 130, ... hier die Sinuswerte eingeben );
long frequ=123456L; //als Beispiel
void PWM-ISR(void){ //Interrupt-Routine
union DDS(long LONG, char BYTE[4]);
//BYTE[3] sind die obersten 8 Bits (maschinenabhängig)
DDS.LONG+=frequ;
//mit Modulo (alternativ bei Vielfachen von 2 mit &(TabSize-1) )
// auf die Tabellengröße beschränken
PWM-REG1=SinTab[ DDS.BYTE[3] % TabSize];
//Cos-Wert um 90° versetzt
PWM-REG2=SinTab[(DDS.BYTE[3]+TabSize/4) % TabSize];
}
Das funktioniert so:
DDS.LONG ist ein 32-Bit-Zähler, der wird imaginär in zwei Hälften
geteilt:
in z.B.
16 Vorkomma- und 16 Nachkomma-Bits oder
8 Vorkomma- und 24 Nachkomma-Bits.
Die Vorkomma-Bits ergeben einen Index in die Sinus-Tabelle.
Wenn frequ nun z.B. 2^NachkommaBits ist, ergibt die Addition
DDS.LONG+=frequ;
jedes Mal genau einen Überlauf in die VorkommaBits, d.h. bei jedem
PWM-Cycle wird genau der nächste Tabelleneintrag ausgegeben.
Das wiederum bedeutet, dass nach TabSize Cyclen die Tabelle einmal
ausgegeben wird, also genau eine Schwingung.
Folglich ist deren Frequenz PWMfrequenz / TabSize.
Ist nun frequ z.B. 2^(Nachkommabits-1) = (2^Nachkommabits) / 2
dann dauert das ganze doppelt so lange, weil erst zwei Additionen
stattfinden müssen, bis es einen Übertrag in die VorkommaBits gibt.
Also wird die Ausgangs-Frequenz auch nur halb so hoch sein.
Allgemein gesagt:
AusgangsFrequenz = PWMfrequenz / TabSize * frequ / 2^NachkommaBits
Für die Routine kann man die PWM-ISR verwenden, muss man nicht.
Genausogut ist jede andere periodisch mit ausreichend hoher Frequenz
aufgerufene Routine.
Man kann dem Rechner noch ein wenig Arbeit ersparen, indem man die
Tabelle 1,25 mal so lange macht (das erste Viertel nochmal wiederholt)
und sich dafür die 2. Modulo-Berechnung erübrigt.
PWM-REG1 = SinTab[char index = DDS.BYTE[3] % TabSize];
//Cos-Wert um 90° versetzt
PWM-REG2 = SinTab[index + TabSize/4 ];
oder indem man zwei Tabellen für Sin und Cos verwendet:
PWM-REG1 = SinTab[char index = DDS.BYTE[3] % TabSize];
PWM-REG2 = CosTab[index];
Bei einer 64-er Tabelle kann man auch 6 Vorkomma- und 28 NachkommaBits
nehmen, dadurch kann man die Frequenz 4mal so genau einstellen.
PWM-REG1 = SinTab[char index = DDS.BYTE[3] >> 2];
PWM-REG2 = CosTab[index];
Jetzt kommt's noch dicker: Wie kann ich die Frequenz linear wobbeln?
geht wieder ganz einfach:
long delta=wobbel;
DDS.LONG+=frequ+=delta; //eine Super-Konstruktion
if(frequ>= obere){delta=-wobbel;}
else
if(frequ<=untere){delta= wobbel;}
Finde ich einfach genial.