Cuando se ejecuta en el reloj predivisor de 64 en el ATmega328, uno de mis temporizadores velocidades de hasta por razones desconocidas, en un momento determinado de la ejecución.
Estoy usando dos temporizadores en el ATmega328 para generar el reloj necesita TLC5940 (ver más abajo sobre por qué; esto es irrelevante para la cuestión). TIMER0
genera una señal de reloj usando PWM Rápido en OC0B
y se establece como sigue:
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
twiddles de una línea de datos para generar una supresión de pulso cada 256 TIMER0
ciclos y se establece como sigue:
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
llamadas de un ISR en el desbordamiento (cada 256 ciclos). El ISR manualmente genera una supresión de pulso, y un enganche de pulso 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 de arriba es sólo para tomar el pulso más evidente en la lógica de la traza del analizador. Aquí está lo que el bucle en el main()
función se parece a: enviar algunos datos en serie, esperar para ISR para cuidar de la retención, y luego hacerlo de nuevo:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
hace algunos SPI envía (código de en pastebin para que en aras de la brevedad). Mi problema es que después de la sendSerial()
completa, mientras se espera fLatch
a ser baja (procesados) el reloj temporizador se acelera. He aquí la lógica de la traza del analizador (I picado las áreas donde la señal se sigue para hacer el gráfico más pequeño):
En el lado izquierdo, los canales 0 y 1 indican el final de la cola de SPI datos que se envían. También en la izquierda, en el canal 4, se puede ver una supresión de pulso. En el canal 2 el reloj de pulso navegan a lo largo de la esperada. A la derecha alrededor de donde la brecha en la imagen es, fLatch
se establece en 1
dentro de la main()
rutina. Y poco después, TIMER0
velocidades de hasta por un factor de aproximadamente 4. Finalmente, la supresión del pulso y el enclavamiento de pulso se llevan a cabo (canales 3 y 4, el tercio derecho de la imagen), y ahora el reloj de pulso reanuda su frecuencia, y la serie de datos es enviado de nuevo. Traté de sacar la delay_ms(1);
línea main()
, pero los mismos resultados se obtienen. ¿Qué está pasando? Debo señalar que el ATmega es desplazado fuera un cristal de 20Mhz y, a continuación, ralentizado por 64x usando el siguiente código:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Qué es esto: estoy experimentando con el control de la TLC5940 conductor del LED: estos chips requieren de un reloj externo, además de un reset al final del ciclo de reloj.