Nie jesteśmy w stanie poprawnie wygenerować sygnału sinusoidalnego za pomocą mikrokontrolera MC68HC908GP32 . Opis PWM zaczyna się na stronie 349. Częstotliwość taktowania wynosi 2,4 MHz, a my wykorzystaliśmy PWM 7 kHz, używając preskalera i ustawiając moduł modulo na 350 w następujący sposób:
T1SC = 0x60; // Prescaler: Div entre 64
//Counter modulo = 0x015E = 350
T1MODH = 0x01; // High
T1MODL = 0x5E; // Low
Wyjście PWM jest filtrowane przez następujący filtr RLC, a następnie DC jest usuwany za pomocą nasadki serii 1uF. Częstotliwość odcięcia jest znacznie poniżej 7 kHz PWM.
Najpierw próbowaliśmy użyć LUT, które próbki zostały wygenerowane przy użyciu tej strony (100 próbek, amplituda = 250). Obejmuje to jeden okres.
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};
Szerokość następującego impulsu jest obliczana dla każdego cyklu PWM:
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(;;);
}
Po podłączeniu do zakresu otrzymujemy następujący sygnał. Nie jesteśmy w stanie uniknąć tego dziwnego szczytu w pobliżu minimum.
Podczas przybliżania tego piku możemy zobaczyć, jak wyjście PWM (w górę) jest w rzeczywistości nieprawidłowe.
Więc po tym, jak przez jakiś czas bawiliście się i nie mogąc się go pozbyć, próbowaliśmy obliczyć sygnał sinusoidalny w MCU, zamiast mocno kodować wartość dla każdej próbki. Dodaliśmy następujący kod w głównej funkcji, tuż przed wszystkimi ustawieniami licznika:
for(i=0;i<99;i++) {
temp=100*(sin(2*3.14159*i/100)+1);
seno[i]=(int)temp;
}
Ale wyniki nawet nie wyglądają jak sinusoidy:
Po wielu godzinach zmagania się z tym nie udało nam się znaleźć naszego błędu. Będziemy wdzięczni za radę.
źródło
Odpowiedzi:
Na dole strony 350 arkusza danych mikrokontrolera wspomniano, że zapisanie małej wartości do rejestru wartości timera podczas przerwania przepełnienia może spowodować, że następne przerwanie zostanie uruchomione tylko podczas następnej iteracji PWM, ponieważ licznik kontynuuje liczenie, podczas gdy wykonywana jest procedura przerwania.
Potwierdza to fakt, że wartość pwm jest utrzymywana na wysokim poziomie przez cały okres zegara pwm + to, co wygląda jak długość timera (w oparciu o otaczające długości). Wartość zapisywana do rejestru długości timera jest prawdopodobnie bliska zeru w momencie błędu, więc jest całkiem realne, że licznik przekroczył mniejszą wartość podczas przerwania i uruchomiłby się dopiero w następnym cyklu.
Można to naprawić, zwiększając minimalny poziom sinusoidy do poziomu wyższego niż czas potrzebny do wykonania ISR lub zmieniając mechanizm ustalania nowego poziomu. Góra strony 351 zawiera szczegółowe informacje na temat tego, jak można to zrobić.
źródło