We are unable to generate a sine signal properly using a MC68HC908GP32 microcontroller. PWM description begins in page 349. Clock frequency is 2.4MHz, while we have used 7 kHz PWM by using the prescaler and setting the timer modulo to 350 as follows:
T1SC = 0x60; // Prescaler: Div entre 64
//Counter modulo = 0x015E = 350
T1MODH = 0x01; // High
T1MODL = 0x5E; // Low
The PWM output is filter by the following RLC filter, and then DC is removed using a series 1uF cap. The cutoff frequency is way below PWM's 7kHz.
First, we have tried using a LUT, which samples were generated using this site (100 samples, amplitude = 250). This comprises a single period.
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};
The width of the following pulse is computed every PWM cycle:
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(;;);
}
When pluggin it into the scope, we get the following signal. We are unable to avoid that strange peak near the minimum.
When zooming around that peak, we can see how the PWM output (up) is in fact incorrect.
So, after messing around for a while and being unable to get rid of it, we have tried computing the sine signal in the MCU, instead of hard coding the value for each sample. We have added the following code in the main function, just before all the counter setup:
for(i=0;i<99;i++) {
temp=100*(sin(2*3.14159*i/100)+1);
seno[i]=(int)temp;
}
But results don't even look like a sinusoid:
After hours struggling with it, we haven't been able to find our mistake. We would appreciate a piece of advice.
Best Answer
On the bottom of page 350 of the microcontroller datasheet, it mentions that writing a small value to the timer value register during the overflow interrupt might cause the next interrupt to be triggered only on the next pwm iteration, since the timer continues to count while the interrupt routine is being executed.
This is confirmed by the fact that the pwm value is held high for one entire pwm clock period + what looks like the timer length (based on the surrounding lengths). The value being written to the timer length register is probably close to 0 at the time of error, so it is quite viable that the counter has passed the smaller value during the interrupt, and would only trigger on the following cycle.
This could be fixed by increasing the sinusoid minimum level to a level higher than the time it takes to execute the ISR, or changing the mechanism by which the new level is set. The top of page 351 details how this may be done.