Não conseguimos gerar um sinal senoidal adequadamente usando um microcontrolador MC68HC908GP32 . A descrição do PWM começa na página 349. A frequência do relógio é de 2,4 MHz, enquanto usamos o PWM de 7 kHz usando o prescaler e configurando o módulo do temporizador para 350 da seguinte maneira:
T1SC = 0x60; // Prescaler: Div entre 64
//Counter modulo = 0x015E = 350
T1MODH = 0x01; // High
T1MODL = 0x5E; // Low
A saída PWM é filtrada pelo seguinte filtro RLC e o DC é removido usando uma tampa de 1uF da série. A frequência de corte está bem abaixo dos 7kHz do PWM.
Primeiro, tentamos usar um LUT, cujas amostras foram geradas usando este site (100 amostras, amplitude = 250). Isso compreende um único 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};
A largura do pulso a seguir é calculada a 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(;;);
}
Ao conectá-lo ao escopo, obtemos o seguinte sinal. Não conseguimos evitar esse pico estranho próximo ao mínimo.
Ao ampliar esse pico, podemos ver como a saída PWM (para cima) está de fato incorreta.
Portanto, depois de mexer um pouco e sem conseguir nos livrar dele, tentamos calcular o sinal senoidal no MCU, em vez de codificar o valor de cada amostra. Nós adicionamos o seguinte código na função principal, imediatamente antes de toda a configuração do contador:
for(i=0;i<99;i++) {
temp=100*(sin(2*3.14159*i/100)+1);
seno[i]=(int)temp;
}
Mas os resultados nem parecem sinusóides:
Depois de horas lutando com isso, não conseguimos encontrar nosso erro. Gostaríamos de receber um conselho.
fonte
Respostas:
Na parte inferior da página 350 da folha de dados do microcontrolador, ele menciona que gravar um pequeno valor no registro do valor do timer durante a interrupção de estouro pode fazer com que a próxima interrupção seja acionada apenas na próxima iteração pwm, uma vez que o timer continua a contar enquanto o rotina de interrupção está sendo executada.
Isso é confirmado pelo fato de que o valor pwm é mantido alto por um período inteiro do relógio pwm + o que parece com a duração do temporizador (com base nos comprimentos circundantes). O valor que está sendo gravado no registro de duração do timer provavelmente está próximo de 0 no momento do erro, portanto, é bastante viável que o contador tenha passado o valor menor durante a interrupção e seria acionado apenas no ciclo a seguir.
Isso pode ser corrigido aumentando o nível mínimo do senoide para um nível superior ao tempo necessário para executar o ISR ou alterando o mecanismo pelo qual o novo nível é definido. A parte superior da página 351 detalha como isso pode ser feito.
fonte