Generación de señal sinusoidal usando PWM

No podemos generar una señal sinusoidal correctamente con un microcontrolador MC68HC908GP32 . La descripción de PWM comienza en la página 349. La frecuencia del reloj es de 2,4 MHz, mientras que hemos usado PWM de 7 kHz usando el preescalador y configurando el módulo del temporizador en 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 con el siguiente filtro RLC, y luego se elimina la CC usando un límite de serie de 1uF. La frecuencia de corte está muy por debajo de los 7kHz de PWM.

ingrese la descripción de la imagen aquí

Primero, hemos intentado usar un LUT, cuyas muestras se generaron usando este sitio (100 muestras, amplitud = 250). 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 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(;;);   
}

Al enchufarlo en el alcance, obtenemos la siguiente señal. No podemos evitar ese pico extraño cerca del mínimo.

ingrese la descripción de la imagen aquí

Al hacer zoom alrededor de ese pico, podemos ver cómo la salida PWM (hacia arriba) es incorrecta.

ingrese la descripción de la imagen aquí

Entonces, después de perder el tiempo por un tiempo 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:

ingrese la descripción de la imagen aquí

Después de horas luchando con él, no hemos podido encontrar nuestro error. Agradeceríamos un consejo.

¿Podría publicar la lista completa de valores de PWM?
@pjc50 Aquí está: pastebin.com/sNyA0Hki . Muchísimas gracias.
Intente reemplazar todos los valores "0" en el medio con "1"; Sospecho que 0 le da esa señal alta y amplia en lugar de una señal baja que desea.
@Serge: inserte los datos en línea. La pasta podría desaparecer y sería malo perder una parte de la pregunta. Pero formatéalo para que no ocupe tanto espacio. Gracias.
Claramente no es el filtro, buena suerte, parece que su tabla se está corrompiendo o pierde el puntero.
@pjc50 Gracias. He intentado cambiar 1s a 0s, obteniendo esto . Luego intenté aplicar un desplazamiento a toda la señal (es decir, usando T1CH0H = ((seno[fase]+10)>>8);y T1CH0L = (seno[fase]+10)&0xFF;), obteniendo un resultado similar . Simplemente no puedo encontrar ninguna lógica detrás de esto.
@trygvis Gracias por el consejo. He actualizado la pregunta.
Intente esto y háganos saber los resultados: Vuelva al método LUT, pero genere valores con un rango de 1 a 254, no de 0 a 255, y vuelva a intentarlo.
Además, no es necesario generar una LUT para todo el ciclo, se puede hacer usando un cuarto de ciclo (con un rango de 1 a 127). Los ciclos de cuartos sucesivos se pueden derivar usando 255 - valuey indexMax - indexcombinaciones.
¿Puede imprimir los valores para T1CH0Hy T1CH0Lsobre la serie, para que pueda ver los valores reales escritos en los registros PWM?
Para fines de solución de problemas: duplique la longitud de senomodo que contenga dos ondas completas y luego fasecuente hasta 199 en lugar de 99. Si la distorsión aún aparece en cada ciclo, es causada por los valores escritos en los canales PWM. Siga el consejo de @AnindoGhosh para disminuir la amplitud.

Respuestas (1)

En la parte inferior de la página 350 de la hoja de datos del microcontrolador, se menciona que escribir un valor pequeño en el registro de valor del temporizador durante la interrupción de desbordamiento podría causar que la siguiente interrupción se active solo en la siguiente iteración de pwm, ya que el temporizador continúa contando mientras el se está ejecutando la rutina de interrupción.

Una escritura no sincronizada en los registros del canal TIM para cambiar un valor de ancho de pulso podría causar una operación incorrecta hasta por dos períodos de PWM. Por ejemplo, escribir un nuevo valor antes de que el contador alcance el valor anterior pero después de que el contador alcance el nuevo valor evita cualquier comparación durante ese período de PWM. Además, el uso de una rutina de interrupción de desbordamiento de TIM para escribir un nuevo valor de ancho de pulso más pequeño puede hacer que se pierda la comparación. El TIM puede pasar el nuevo valor antes de que se escriba.

Esto se confirma por el hecho de que el valor de pwm se mantiene alto durante un período completo de reloj de pwm + lo que parece ser la duración del temporizador (basado en las longitudes circundantes). El valor que se escribe en el registro de duración del temporizador probablemente esté cerca de 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 active en el ciclo siguiente.

Esto podría solucionarse aumentando el nivel mínimo de la sinusoide a un nivel superior al tiempo que lleva ejecutar la ISR, o cambiando el mecanismo mediante el cual se establece el nuevo nivel. La parte superior de la página 351 detalla cómo se puede hacer esto.

No sé cómo podría omitir eso al leer la hoja de datos. ¡Gracias!