AVR timer speedup on ATmega328

9

Cuando se ejecuta en un prescaler de reloj de 64 en ATmega328, uno de mis temporizadores se acelera por razones desconocidas en un momento determinado de la ejecución.

Estoy usando dos temporizadores en ATmega328 para generar el reloj necesario para TLC5940 (vea más abajo por qué; esto es inmaterial a la pregunta). TIMER0 genera una señal de reloj usando Fast PWM en 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 divide una línea de datos para generar un pulso de supresión cada 256% de ciclos de co_de 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

TIMER0 llama a un ISR en desbordamiento (cada 256 ciclos). El ISR genera manualmente un impulso de supresión y un impulso de retención 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 retraso TIMER2 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 bucle en la función nop() : envíe algunos datos en serie, espere a que ISR se encargue del enclavamiento y vuelva a hacerlo:

for (;;) {
    if (!fLatch) {
        sendSerial();
        fLatch = 1;
        _delay_ms(1);
    }
    nop();
}

main() hace algunos envíos SPI ( código en pastebin por brevedad, ). Mi problema es que después de que sendSerial() se complete, mientras se espera que sendSerial() se establezca en bajo (procesado), el temporizador de aceleración se acelera. Aquí está la traza del analizador lógico (recorté las áreas donde la misma señal continúa reduciendo el tamaño del gráfico):

Enelladoizquierdo,loscanales0y1muestranelfinaldelacoladelosdatosSPIqueseenvían.Tambiénalaizquierda,enelcanal4,puedeverunpulsodesupresión.Enelcanal2,elpulsotemporizadorsuenacomoseespera.Alrededordedondeestáelespacioenlaimagen,fLatchseestableceenfLatchdentrodelarutina1.Ypocodespués,main()seaceleraenaproximadamenteunfactorde4.Finalmente,serealizanelpulsodesupresiónyelpulsodeenclavamiento(canales3y4,tercioderechodelaimagen),yahoraelpulsoderelojreanudasufrecuenciaregular,yLosdatosenserieseenvíandenuevo.IntentésacarlalíneaTIMER0endelay_ms(1);,peroseobtienenlosmismosresultados.¿Queestapasando?DebotenerencuentaqueelATmegaestásincronizadoconuncristalde20MHzyluegosedesaceleró64xconelsiguientecódigo:

CLKPR=1<<CLKPCE;CLKPR=(0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);

Paraquésirve:estoyexperimentandoconelcontroldel controlador LED TLC5940 : estos chips requieren un reloj externo más un restablecer al final del ciclo de reloj.

    
pregunta angelatlarge

1 respuesta

1

Para una depuración rápida, intentaría hacer lo mismo utilizando Arduino Library for TLC5940 y veré si se está obteniendo rápido o no Si funciona con la biblioteca, puede verificar su fuente y compararla con la suya. Ya que está familiarizado con AVR, debería convertir fácilmente la fuente Arduino a 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 a tu AVR con tu programador favorito.

Espero que ayude

    
respondido por el superkeci

Lea otras preguntas en las etiquetas