Cuando se ejecuta en el preescalador de reloj de 64 en ATmega328, uno de mis temporizadores se acelera por razones desconocidas en un momento particular de la ejecución.
Estoy usando dos temporizadores en ATmega328 para generar el reloj que necesita TLC5940 (ver más abajo sobre por qué; esto no tiene importancia para la pregunta). TIMER0
genera una señal de reloj usando Fast PWM OC0B
activado y se configura de la siguiente manera:
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
gira una línea de datos para generar un impulso de supresión cada 256 TIMER0
ciclos y se configura de la siguiente manera:
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
llama a un ISR en caso de desbordamiento (cada 256 ciclos). El ISR genera manualmente un pulso de supresión y un pulso de enclavamiento si es necesario:
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);
}
El nop()
retraso en el código anterior es solo para hacer que el pulso sea más aparente en el rastreo del analizador lógico. Así main()
es como se ve el bucle en la función: envíe algunos datos en serie, espere a que ISR se encargue del enganche y luego vuelva a hacerlo:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
hace algunos envíos SPI ( código en pastebin en aras de la brevedad ). Mi problema es que después de sendSerial()
completar, mientras espero fLatch
que se configure en bajo (procesado), el temporizador de reloj se acelera. Aquí está el rastro del analizador lógico (corté las áreas donde la misma señal continúa haciendo que el gráfico sea más pequeño):
En el lado izquierdo, los canales 0 y 1 muestran el final de cola de los datos SPI que se envían. También a la izquierda, en el canal 4, puede ver un pulso en blanco. En el canal 2, el pulso de reloj avanza como se esperaba. Justo donde está el espacio en la imagen, fLatch
se establece 1
dentro de la main()
rutina. Y poco después se TIMER0
acelera aproximadamente un factor de 4. Finalmente, se realizan el pulso de supresión y el pulso de enclavamiento (canales 3 y 4, el tercio derecho de la imagen), y ahora el pulso de reloj reanuda su frecuencia regular, y los datos en serie son enviado de nuevo. Intenté sacar la delay_ms(1);
línea main()
, pero se obtienen los mismos resultados. ¿Que esta pasando? Debo señalar que el ATmega tiene un cristal de 20Mhz y luego se ralentiza 64x usando el siguiente código:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Para qué sirve: Estoy experimentando con el control del controlador LED TLC5940 : estos chips requieren un reloj externo más un reinicio al final del ciclo de reloj.
sendSerial()
es mi código que envía datos a través de SPI: no toca los TCCR
registros (control del temporizador).