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 (vea a continuación por qué; esto es irrelevante para la pregunta). TIMER0
genera una señal de reloj utilizando Fast PWM OC0B
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
hace girar una línea de datos para generar un pulso de borrado 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 borrado y un pulso de bloqueo 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 evidente en la traza del analizador lógico. Así es como se ve el ciclo en la main()
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 por razones de brevedad ). Mi problema es que después de que se sendSerial()
completa, mientras espera fLatch
que se establezca en bajo (procesado), el temporizador de reloj se acelera. Aquí está la traza del analizador lógico (recorté las áreas donde la misma señal continúa para hacer el gráfico más pequeño):
En el lado izquierdo, los canales 0 y 1 muestran el final de la cola de los datos SPI que se envían. También a la izquierda, en el canal 4, puede ver un pulso de borrado. 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 en un factor de aproximadamente 4. Eventualmente, se realizan el pulso de borrado y el pulso de enganche (canales 3 y 4, tercio derecho de la imagen), y ahora el pulso de reloj vuelve a su frecuencia regular, y los datos en serie son enviado de nuevo. Intenté sacar la delay_ms(1);
línea en main()
, pero se obtienen los mismos resultados. ¿Que esta pasando? Debo señalar que el ATmega está sincronizado con un cristal de 20 Mhz y luego se ralentiza 64 veces usando el siguiente código:
CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);
Para qué es esto: 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.
Para una depuración rápida, intentaría hacer lo mismo usando Arduino Library para TLC5940 y ver si se está acelerando o no. Si funciona con la biblioteca, puede verificar su fuente y compararla con la suya. Como está familiarizado con AVR, debe convertir fácilmente la fuente de Arduino en AVR nativo.
En caso de que no sepa cómo cargar bocetos de Arduino compilados en AVR: cuando compila su boceto, crea un archivo hexadecimal (puede ver la ubicación exacta del archivo activando el modo detallado en la configuración). Puedes subir ese hexágono a tu AVR con tu programador favorito.
Espero eso ayude
Blup1980
angelagrande
Blup1980
PedroJ
angelagrande
sendSerial()
es mi código que envía datos a través de SPI: no toca losTCCR
registros (control del temporizador).Blup1980
angelagrande