9 votos

AVR temporizador speedup en el ATmega328

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):

enter image description here

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.

1voto

Para una rápida depuración, me gustaría tratar de hacer lo mismo con Arduino Biblioteca para TLC5940 y a ver si es llegar rápido o no. Si funciona con la biblioteca, usted puede comprobar su origen y comparar con la tuya. Ya estás familiarizado con el AVR, usted debe convertir fácilmente Arduino origen a los nativos de AVR.

Sólo en caso de que usted no sabe cómo subir compilado Arduino bocetos de AVR: Cuando se compila el boceto, se crea un archivo hexadecimal (se puede ver la ubicación exacta del archivo de inflexión en el modo detallado en la configuración). Usted puede cargar el hex para el AVR con su favorito de programador.

Espero que ayude

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X