Estoy intentando cambiar la frecuencia de salida de PWM aproximadamente una vez por milisegundo utilizando un dsPIC33FJ256GP710, y tengo resultados mixtos. Primero intenté esto:
#include <p33fxxxx.h>
_FOSCSEL(FNOSC_PRIPLL);
_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT);
_FWDT(FWDTEN_OFF);
static unsigned int PWM_TABLE[7][2] =
{
{132, 66}, {131, 66}, {130, 65}, {129, 65}, {128, 64}, {127, 64}, {126, 63} // Compare, 50% duty
};
static int curFreq = 0;
int main(void)
{
int i;
PLLFBD = 0x009E; // Set processor clock to 32 MHz (16 MIPS)
CLKDIV = 0x0048;
LATCbits.LATC1 = 0; // Make RC1 an output for a debug pin
TRISCbits.TRISC1 = 0;
LATDbits.LATD6 = 0; // Make RD6/OC7 an output (the PWM pin)
TRISDbits.TRISD6 = 0;
T2CONbits.TON = 0; // Disable Timer 2
OC7CONbits.OCM = 0b000; // Turn PWM mode off
PR2 = PWM_TABLE[curFreq][0]; // Set PWM period
OC7RS = PWM_TABLE[curFreq][1]; // Set PWM duty cycle
OC7CONbits.OCM = 0b110; // Turn PWM mode on
T2CONbits.TON = 1; // Enable Timer 2
while (1)
{
for (i = 0; i < 3200; i++) {} // Delay roughly 1 ms
curFreq = (curFreq + 1) % 7; // Bump to next frequency
PR2 = PWM_TABLE[curFreq][0]; // Set PWM period
OC7RS = PWM_TABLE[curFreq][1]; // Set PWM duty cycle
LATCbits.LATC1 = !LATCbits.LATC1; // Toggle debug pin so we know what's happening
}
}
El resultado es que PWM se retira durante aproximadamente 4 ms en lo que parece ser un intervalo repetible, aproximadamente alineado con mi conmutador de pin de depuración (en otras palabras, cuando el código está jugando con el período y los registros del ciclo de trabajo). Adjuntaré una foto de mi traza de alcance. El canal 1 es PWM y el canal 2 es el pin de depuración que se alterna cuando el código intenta ajustar la frecuencia.
De todos modos, comencé a pensar en los reinversores del temporizador e hice algunas búsquedas en algunos foros. Se me ocurrieron algunas ideas basadas en algunas publicaciones que leí. La mejor idea parecía ser habilitar la interrupción del temporizador 2, desactivar el modo PWM en su interior y solo cambiar los registros de período y ciclo de trabajo dentro de la interrupción del temporizador 2. Entonces, escribí esto:
#include <p33fxxxx.h>
_FOSCSEL(FNOSC_PRIPLL);
_FOSC(FCKSM_CSDCMD & OSCIOFNC_OFF & POSCMD_XT);
_FWDT(FWDTEN_OFF);
static int curFreq = 0;
static unsigned int PWM_TABLE[7][2] =
{
{132, 66}, {131, 66}, {130, 65}, {129, 65}, {128, 64}, {127, 64}, {126, 63} // Compare, duty
};
int main(void)
{
int i, ipl;
PLLFBD = 0x009E; // Set processor clock to 32 MHz (16 MIPS)
CLKDIV = 0x0048;
LATCbits.LATC1 = 0; // Make RC1 an output for a debug pin
TRISCbits.TRISC1 = 0;
LATDbits.LATD6 = 0; // Make RD6/OC7 an output (the PWM pin)
TRISDbits.TRISD6 = 0;
OC7CONbits.OCM = 0b000; // Turn PWM mode off
OC7RS = PWM_TABLE[curFreq][1]; // Set PWM duty cycle
PR2 = PWM_TABLE[curFreq][0]; // Set PWM period
OC7CONbits.OCM = 0b110; // Turn PWM mode on
T2CONbits.TON = 0; // Disable Timer 2
TMR2 = 0; // Clear Timer 2 register
IPC1bits.T2IP = 1; // Set the Timer 2 interrupt priority level
IFS0bits.T2IF = 0; // Clear the Timer 2 interrupt flag
IEC0bits.T2IE = 1; // Enable the Timer 2 interrupt
T2CONbits.TON = 1; // Enable Timer 2
while (1)
{
for (i = 0; i < 1600; i++) {} // Delay roughly 1 ms
SET_AND_SAVE_CPU_IPL(ipl, 2); // Lock out the Timer 2 interrupt
curFreq = (curFreq + 1) % 7; // Bump to next frequency
RESTORE_CPU_IPL(ipl); // Allow the Timer 2 interrupt
LATCbits.LATC1 = !LATCbits.LATC1; // Toggle debug pin so we know what's happening
}
}
void __attribute__((__interrupt__)) _T2Interrupt(void)
{
T2CONbits.TON = 0; // Disable Timer 2
TMR2 = 0; // Clear Timer 2 register
OC7CONbits.OCM = 0b000; // Turn PWM mode off
OC7RS = PWM_TABLE[curFreq][1]; // Set the new PWM duty cycle
PR2 = PWM_TABLE[curFreq][0]; // Set the new PWM period
OC7CONbits.OCM = 0b110; // Turn PWM mode on
IFS0bits.T2IF = 0; // Clear the Timer 2 interrupt flag
T2CONbits.TON = 1; // Enable Timer 2
}
Esto parece ser más estable por lo que puedo decir sobre mi alcance antiguo, pero ahora la forma de onda ya no tiene forma regular (el ciclo de trabajo parece ser inexplicablemente inconsistente) y si me esfuerzo lo suficiente, puedo convencerme que todavía veo un milisegundo de abandono de PWM cuando mi alcance se establece en una base de tiempo de 5 o 10 milisegundos.
Es ciertamente mejor de lo que era, y puedo seguir jugando con la esperanza de corregir la forma de onda irregular producida por el segundo bit de código, pero mi pregunta es:
¿Hay una forma "correcta" de hacer esto? ¿O al menos una manera mejor que el camino en el que estoy?
Cualquier ayuda sería completamente apreciada.
Seguimiento del alcance http://www.freeimagehosting.net/uploads/c132216a28.jpg