Ao executar no relógio prescaler de 64 no ATmega328, um dos meus timers acelera por motivos desconhecidos em um momento específico da execução.
Estou usando dois temporizadores no ATmega328 para gerar o clock necessário para o TLC5940 (veja abaixo o porquê; isso é irrelevante para a pergunta). TIMER0
gera um sinal de relógio usando o Fast PWM ativado OC0B
e é configurado da seguinte maneira:
TCCR0A = 0
|(0<<COM0A1) // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
|(0<<COM0A0) //
|(1<<COM0B1) // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
|(0<<COM0B0)
|(1<<WGM01) // Bits 1:0 – WGM01:0: Waveform Generation Mode
|(1<<WGM00)
;
TCCR0B = 0
|(0<<FOC0A) // Force Output Compare A
|(0<<FOC0B) // Force Output Compare B
|(1<<WGM02) // Bit 3 – WGM02: Waveform Generation Mode
|(0<<CS02) // Bits 2:0 – CS02:0: Clock Select
|(1<<CS01)
|(0<<CS00) // 010 = clock/8
;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;
TIMER2
ajusta uma linha de dados para gerar um pulso de apagamento a cada 256 TIMER0
ciclos e é configurada da seguinte maneira:
ASSR = 0;
TCCR2A = 0
|(0<<COM2A1) // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
|(0<<COM2A0) //
|(0<<COM2B1) // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
|(0<<COM2B0)
|(0<<WGM21) // Bits 1:0 – WGM01:0: Waveform Generation Mode
|(0<<WGM20)
;
TCCR2B = 0
|(0<<FOC2A) // Force Output Compare A
|(0<<FOC2B) // Force Output Compare B
|(0<<WGM22) // Bit 3 – WGM02: Waveform Generation Mode
|(1<<CS22) // Bits 2:0 – CS02:0: Clock Select
|(0<<CS21)
|(0<<CS20) // 100 = 64
;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
|(1<<TOIE2); // Timer/Counter0 Overflow Interrupt Enable
TIMER2
chama um ISR em excesso (a cada 256 ciclos). O ISR gera manualmente um pulso de apagamento e um pulso de travamento, se necessário:
volatile uint8_t fLatch;
ISR(TIMER2_OVF_vect) {
if (fLatch) {
fLatch = 0;
TLC5940_XLAT_PORT |= (1<<TLC5940_XLAT_BIT); // XLAT -> high
for (int i=0;i<10;i++)
nop();
TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT); // XLAT -> high
}
// Blank
TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
for (int i=0;i<10;i++)
nop();
TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}
O nop()
atraso no código acima é apenas para tornar o pulso mais aparente no rastreamento do analisador lógico. Aqui está a main()
aparência do loop na função: envie alguns dados seriais, aguarde o ISR cuidar da trava e faça novamente:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
faz alguns envios SPI ( código em pastebin por uma questão de brevidade ). Meu problema é que, após a sendSerial()
conclusão, enquanto espera para fLatch
ser definido como baixo (processado), o temporizador acelera. Aqui está o rastreio do analisador lógico (recortei as áreas em que o mesmo sinal continua a diminuir o gráfico):
No lado esquerdo, os canais 0 e 1 mostram o final dos dados SPI sendo enviados. Também à esquerda, no canal 4, você pode ver um pulso em branco. No canal 2, o pulso do relógio toca conforme o esperado. Bem ao redor de onde está a lacuna na imagem, fLatch
é definido como 1
dentro da main()
rotina. E logo depois TIMER0
acelera cerca de um fator de 4. Eventualmente, o pulso de apagamento e o pulso de travamento são executados (canais 3 e 4, terço direito da imagem), e agora o pulso de clock retoma sua frequência regular e os dados seriais são enviado novamente. Tentei eliminar a delay_ms(1);
linha main()
, mas os mesmos resultados são obtidos. O que está acontecendo? Devo observar que o ATmega é cronometrado em um cristal de 20Mhz e depois diminuído de 64x usando o seguinte código:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Para que serve: Estou experimentando o controle do driver de LED TLC5940 : esses chips exigem um relógio externo mais uma redefinição no final do ciclo de clock.
sendSerial()
é o meu código que envia dados via SPI: ele não toca nos TCCR
registros (controle do temporizador).