Atmega328 fast PWM (modo 7) del temporizador 0 no funciona como se esperaba

0

Estoy parpadeando un LED en PORTB5 usando el temporizador 0 en el modo 7. El tiempo entre parpadeos se calcula como $$ \ frac {(\ text {OCROA} +1) \ times \ text {prescaler} \ times \ text { timerCount}} {\ text {FCPU}} $$ \ $ FCPU = 16MHz \ $. Con \ $ OCROA = 124 \ $, \ $ prescaler = 1024 \ $ y \ $ timerCount = 125 \ $, el tiempo resulta en \ $ 1 \ espacio segundo \ $ y esto funciona como se esperaba. Pero con \ $ OCROA = 16 \ $, \ $ prescaler = 1 \ $ y \ $ timerCount = 2956793 \ $, esperaría que el tiempo sea \ $ ~ 3.14 \ segundos de espacio \ $, pero estoy obteniendo algo \ $ 12 \ espacio segundos \ $.

Código que funciona como se esperaba:

#include <avr/interrupt.h>
#include <stdint.h>

volatile uint32_t timerCount = 0;
ISR(TIMER0_COMPA_vect)
{
        ++timerCount;
}

int main(void)
{
        DDRB |= 1 << DDB5;
        TIMSK0 |= 1 << OCIE0A;
        TCCR0A |= 1 << WGM00 | 1 << WGM01;
        TCCR0B |= 1 << WGM02;
        OCR0A = 124;
        sei();
        TCCR0B |= 1 << CS02 | 1 << CS00;
        while(1)
        {
        if(timerCount >= 125)
                {
                        PORTB ^= 1 << PORTB5;
                        timerCount = 0;
                }
        }
        return 0;
}

Código que no funciona como se esperaba:

#include <avr/interrupt.h>
#include <stdint.h>

volatile uint32_t timerCount = 0;
ISR(TIMER0_COMPA_vect)
{
        ++timerCount;
}

int main(void)
{
        DDRB |= 1 << DDB5;
        TIMSK0 |= 1 << OCIE0A;
        TCCR0A |= 1 << WGM00 | 1 << WGM01;
        TCCR0B |= 1 << WGM02;
        OCR0A = 16;
        sei();
        TCCR0B |= 1 << CS00;
        while(1)
        {
        if(timerCount >= 2956793)
                {
                        PORTB ^= 1 << PORTB5;
                        timerCount = 0;
                }
        }
        return 0;
}

¿Cuál podría ser el problema con este último?

    
pregunta Suba Thomas

1 respuesta

2

En su segundo ajuste, el temporizador se incrementa una vez por cada ciclo de reloj (el preescalador es 1), pero solo cuenta a 16. El AVR ejecuta una instrucción por ciclo de reloj (excepto las sucursales), por lo que hay menos de 16 instrucciones disponibles entre cada interrupción PWM.

El tiempo de ejecución del ISR es más largo que esto (también puede ser variable) y eso hace que su código pierda periódicamente las interrupciones, extendiendo el tiempo aparente que toma contar hasta su valor objetivo. Para solucionar el problema, debe usar el prescaler y contar hasta un valor más alto, o si no puede hacerlo (si está usando el generador de hardware PWM), considere usar OCR1B con una cuenta más larga para generar las interrupciones. .

También debe desactivar las interrupciones cuando borra timerCount porque este es un valor de 32 bits que requiere múltiples instrucciones para actualizar en una plataforma de 8 bits.

    
respondido por el Jon

Lea otras preguntas en las etiquetas