Sinus-Signalerzeugung mit PWM

16

Mit einem MC68HC908GP32- Mikrocontroller kann kein ordnungsgemäßes Sinussignal generiert werden . Die PWM-Beschreibung beginnt ab Seite 349. Die Taktfrequenz beträgt 2,4 MHz, während wir 7 kHz PWM verwendet haben, indem wir den Prescaler verwendet und das Timer-Modulo wie folgt auf 350 eingestellt haben:

T1SC = 0x60;    // Prescaler: Div entre 64
//Counter modulo = 0x015E = 350
T1MODH = 0x01;   // High
T1MODL = 0x5E;   // Low

Der PWM-Ausgang wird durch das folgende RLC-Filter gefiltert, und dann wird DC unter Verwendung einer 1uF-Kappe der Serie entfernt. Die Grenzfrequenz liegt weit unter PWMs 7kHz.

Bildbeschreibung hier eingeben

Zunächst haben wir versucht, eine LUT zu verwenden, deren Samples mit dieser Site erstellt wurden (100 Samples, Amplitude = 250). Dies umfasst eine einzelne Periode.

 int seno[100]={ 125, 133, 141, 148, 156, 164, 171, 178, 185, 192, 198, 205, 211, 216, 221, 226, 231, 235, 238, 241, 244, 246, 248, 249, 250, 250, 250, 249, 248, 246, 244, 241, 238, 235, 231, 226, 221, 216, 211, 205, 198, 192, 185, 178, 171, 164, 156, 148, 141, 133, 125, 117, 109, 102, 94, 86, 79, 72, 65, 58, 52, 45, 39, 34, 29, 24, 19, 15, 12, 9, 6, 4, 2, 1, 0, 0, 0, 1, 2, 4, 6, 9, 12, 15, 19, 24, 29, 34, 39, 45, 52, 58, 65, 72, 79, 86, 94, 102, 109, 117}; 

Die Breite des folgenden Impulses wird in jedem PWM-Zyklus berechnet:

interrupt 4 void rsi_t1ch0 (void)
{
    //-- disable interruption flag
    T1SC0&=(~0x80);
    //-- pwm to '0' 
    PTB&=0xFD;

    //some sensor measures are done here.... 100 out of the 350 cycles are left for this                
}
/************************************************************/
/* TIM1 overflow rutine                                     */
/************************************************************/
interrupt 6 void rsi_ov1 (void)
{

    T1SC&=(~0x80);
    //-- set PWM to 1
    PTB|=0x02;
    T1CH0H = ((seno[fase])>>8);   // high bits
    T1CH0L = (seno[fase])&0xFF;   // low bits
    fase+=1;
    if (fase >= 99)
      fase=0;
}

void main(void)
{
float temp;
    int i;

    CONFIG1|=0x01;  
    DDRB=0xFF;      //-- Port B is set as output
    PTB=0x00;       
    //Timer setup
    T1SC = 0x60;    // Prescaler: Div by 64  
    T1MODH = 0x01;   //Counter modulo
    T1MODL = 0x5E;  
    T1SC0 = 0x50;  //Comparator setup
    //-- Initial width
    T1CH0H = 0x00;
    T1CH0L = 0x53;

    EnableInterrupts;
    T1SC&=~(0x20); //Run timer forever
    for(;;);   
}

Wenn Sie es in das Oszilloskop einstecken, erhalten Sie das folgende Signal. Wir sind nicht in der Lage, diesen seltsamen Gipfel nahe dem Minimum zu vermeiden.

Bildbeschreibung hier eingeben

Wenn wir um diesen Peak zoomen, können wir sehen, dass die PWM-Ausgabe (nach oben) tatsächlich falsch ist.

Bildbeschreibung hier eingeben

Nachdem wir eine Weile herumgespielt haben und es nicht schaffen, es loszuwerden, haben wir versucht, das Sinussignal in der MCU zu berechnen, anstatt den Wert für jedes Sample hart zu codieren. Wir haben den folgenden Code in die Hauptfunktion eingefügt, kurz vor der Einrichtung des Zählers:

 for(i=0;i<99;i++) {
     temp=100*(sin(2*3.14159*i/100)+1);
     seno[i]=(int)temp;
 }

Die Ergebnisse sehen jedoch nicht einmal wie eine Sinuskurve aus:

Bildbeschreibung hier eingeben

Nachdem wir stundenlang damit zu kämpfen hatten, konnten wir unseren Fehler nicht finden. Wir würden uns über einen Rat freuen.

Serge
quelle
Könnten Sie die gesamte Liste der PWM-Werte veröffentlichen?
PJC50
@ pjc50 Hier ist es: pastebin.com/sNyA0Hki . Vielen Dank.
Serge
Versuchen Sie, alle "0" -Werte in der Mitte durch "1" zu ersetzen. Ich vermute, dass 0 Ihnen dieses breite hohe Signal eher als ein niedriges Signal gibt, das Sie wollen.
pjc50
@Serge - Bitte inline die Daten. Die Paste könnte verschwinden und es wäre schlecht, einen Teil der Frage zu verlieren. Aber bitte formatieren Sie es so, dass es nicht so viel Platz beansprucht. Vielen Dank.
Trygve Laugstøl
Es ist eindeutig nicht der Filter - viel Glück - sieht aus, als ob Ihre Tabelle beschädigt wird oder Sie den Zeiger darauf verlieren.
Andy aka

Antworten:

16

Am Ende der Seite 350 des Datenblattes des Mikrocontrollers wird erwähnt, dass das Schreiben eines kleinen Wertes in das Timerwertregister während des Überlaufinterrupts dazu führen kann, dass der nächste Interrupt erst bei der nächsten PWM-Iteration ausgelöst wird, da der Timer weiterhin zählt, während der Interruptroutine wird ausgeführt.

Ein nicht synchronisiertes Schreiben in die TIM-Kanalregister zum Ändern eines Impulsbreitenwerts kann zu einer fehlerhaften Operation für bis zu zwei PWM-Perioden führen. Wenn Sie beispielsweise einen neuen Wert schreiben, bevor der Zähler den alten Wert erreicht, aber nachdem der Zähler den neuen Wert erreicht hat, wird ein Vergleich während dieser PWM-Periode verhindert. Die Verwendung einer TIM-Überlaufunterbrechungsroutine zum Schreiben eines neuen, kleineren Impulsbreitenwerts kann auch dazu führen, dass der Vergleich fehlschlägt. Die TIM kann den neuen Wert übergeben, bevor er geschrieben wird.

Dies wird durch die Tatsache bestätigt, dass der pwm-Wert für eine gesamte pwm-Taktperiode +, wie die Timer-Länge aussieht (basierend auf den Umgebungslängen), hoch gehalten wird. Der Wert, der in das Zeitgeberlängenregister geschrieben wird, liegt zum Zeitpunkt des Fehlers wahrscheinlich nahe bei 0, so dass es durchaus möglich ist, dass der Zähler während des Interrupts den kleineren Wert überschritten hat und erst im folgenden Zyklus auslösen würde.

Dies kann behoben werden, indem der sinusförmige Mindestpegel auf einen Pegel erhöht wird, der höher ist als die Zeit, die zum Ausführen des ISR benötigt wird, oder indem der Mechanismus geändert wird, mit dem der neue Pegel eingestellt wird. Am Anfang der Seite 351 erfahren Sie, wie dies erfolgen kann.

Stanri
quelle
1
Ich weiß nicht, wie ich das überspringen könnte, wenn ich das Datenblatt lese. Vielen Dank!
Serge