No podemos generar una señal sinusoidal correctamente utilizando un microcontrolador MC68HC908GP32 . La descripción de PWM comienza en la página 349. La frecuencia del reloj es de 2.4MHz, mientras que hemos usado PWM de 7 kHz usando el preescalador y configurando el módulo del temporizador a 350 de la siguiente manera:
T1SC = 0x60; // Prescaler: Div entre 64
//Counter modulo = 0x015E = 350
T1MODH = 0x01; // High
T1MODL = 0x5E; // Low
La salida PWM se filtra mediante el siguiente filtro RLC, y luego se elimina la CC con una tapa de serie 1uF. La frecuencia de corte está muy por debajo de los 7kHz de PWM.
Primero, hemos intentado usar un LUT, cuyas muestras se generaron usando este sitio (100 muestras, amplitud = 250). Esto comprende un solo período.
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};
El ancho del siguiente pulso se calcula en cada ciclo 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(;;);
}
Cuando lo conectamos al alcance, obtenemos la siguiente señal. No podemos evitar ese pico extraño cerca del mínimo.
Al hacer zoom alrededor de ese pico, podemos ver cómo la salida PWM (arriba) es de hecho incorrecta.
Entonces, después de jugar un rato y no poder deshacernos de él, hemos intentado calcular la señal sinusoidal en la MCU, en lugar de codificar el valor de cada muestra. Hemos agregado el siguiente código en la función principal, justo antes de toda la configuración del contador:
for(i=0;i<99;i++) {
temp=100*(sin(2*3.14159*i/100)+1);
seno[i]=(int)temp;
}
Pero los resultados ni siquiera parecen una sinusoide:
Después de horas luchando con eso, no hemos podido encontrar nuestro error. Agradeceríamos un consejo.
fuente
Respuestas:
En la parte inferior de la página 350 de la hoja de datos del microcontrolador, menciona que escribir un valor pequeño en el registro del valor del temporizador durante la interrupción por desbordamiento podría provocar que la próxima interrupción se active solo en la siguiente iteración pwm, ya que el temporizador continúa contando mientras el Se está ejecutando la rutina de interrupción.
Esto se confirma por el hecho de que el valor de pwm se mantiene alto durante todo un período de reloj pwm + lo que parece la duración del temporizador (en función de las longitudes circundantes). El valor que se escribe en el registro de longitud del temporizador probablemente sea cercano a 0 en el momento del error, por lo que es bastante viable que el contador haya pasado el valor más pequeño durante la interrupción, y solo se activará en el siguiente ciclo.
Esto podría solucionarse aumentando el nivel mínimo sinusoide a un nivel superior al tiempo que lleva ejecutar el ISR, o cambiando el mecanismo por el cual se establece el nuevo nivel. La parte superior de la página 351 detalla cómo se puede hacer esto.
fuente