atmega timer comportamiento extraño

0

Estoy usando atmega8 que se ejecuta en 1MHz . Escribí una función que cuenta los milisegundos y estaba funcionando perfectamente.

Sin embargo, cuando modifiqué el código para contar microsegundos, el resultado fue extraño para mi ojo, por ejemplo. En lugar de parpadear el LED cada segundo, se encendió durante un segundo y luego se apagó permanentemente.

¿Hay algo de lo que no esté al tanto? ¿Podría estar relacionado con el hecho de que timer2 es 8bit (suceden algunos desbordamientos)?

El timer.c (los fragmentos comentados están funcionando)

#define F_CPU 16000000UL

#include <avr/interrupt.h>
#include <util/atomic.h>
#include "../include/timers.h"

volatile unsigned long milliseconds;
volatile unsigned long microseconds;

ISR (TIMER2_COMP_vect)
{
    microseconds += 50;

    if (microseconds % 1000 == 0) {
        milliseconds++;
    }
}

void timers_init(void)
{
    TCCR2 |= (1 << WGM21);

    //Set the prescaler to 8
    TCCR2 |= (1 << CS21);

    //OCRn = [ (Clock / Prescaler) * Seconds ] - 1
    OCR2 = 99;

    TIMSK |= (1 << OCIE2);

    sei();
}

unsigned long timers_millis(void)
{
    unsigned long millis;

    ATOMIC_BLOCK(ATOMIC_FORCEON) {
        millis = milliseconds;
    }

    return millis;
}

unsigned long timers_micros(void)
{
    unsigned long micros;

    ATOMIC_BLOCK(ATOMIC_FORCEON) {
        micros = microseconds;
    }

    return micros;
}

Y llamando al indicador luminoso LED

while (1) {
    if (timers_micros() % 1000000 == 0) {
        leds_on(LEDS_REGISTER_PORT, LEDS_PORT_LEFT);
    } else {
        leds_off(LEDS_REGISTER_PORT, LEDS_PORT_LEFT);
    }

    if (timers_millis() % 1000 == 0) {
        leds_on(LEDS_REGISTER_PORT, LEDS_PORT_RIGHT);
    } else {
        leds_off(LEDS_REGISTER_PORT, LEDS_PORT_RIGHT);
    }
}

ACTUALIZAR

El código anterior se ha actualizado para reflejar los cambios en el software / hardware.

He actualizado el hardware con un oscilador 16MHz externo, configuré los bits de fusible, modifiqué las macros, por ejemplo. F_CPU en consecuencia. Ahora, solo quería comprobar si mi contador funciona como sospecho pero falló: el led RIGHT que adquiere timers_millis() y se supone que se enciende cada segundo funciona bien, pero el otro led LEFT parpadea al azar. El único patrón con LEFT es que si se activa, RIGHT también está activado.

Me he quedado sin ideas y soluciones. ¿Qué podría estar mal con mi software / hardware y cómo puedo contar los microsegundos con precisión ?

    
pregunta sitilge

3 respuestas

1

El problema está en esta parte del código:

while (1) {
    if (timers_millis() % 1000 == 0) {
        leds_on(LEDS_REGISTER_PORT, LEDS_PORT_RIGHT);
    } else {
        leds_off(LEDS_REGISTER_PORT, LEDS_PORT_RIGHT);
    }
}

timers_millis() % 1000 es solo 0 para múltiplos de 1000. Usted enciende los leds por solo un milisegundo antes de apagarlos nuevamente. Lo que querría es tomar el contador de milisegundos el período deseado y luego verificar si el resto está en la primera mitad o en la segunda mitad. Durante un período de 2 segundos, deberías comprobar si el resto está en el primer o segundo segundo:

if (timers_millis() % 2000 < 1000) // 0000..0999 ms
    // turn on
else                               // 1000..1999 ms
    // turn off
    
respondido por el FRob
1

Con el reloj de la CPU de 1 MHz y el TIMER2 funcionando a 1 MHz, tiene 256 ciclos de CPU por desbordamiento del temporizador.

if (microseconds % 1000 == 0) {
    milliseconds++;
}

El % 1000 probablemente requerirá mucho más de 256 ciclos de CPU, por lo que perderá un par de interrupciones de desbordamiento. Además, gastará casi todo el tiempo de CPU en el ISR porque, en el momento en que finaliza el ISR, el siguiente desbordamiento ya está pendiente, lo que activará al instante el ISR nuevamente.

¿Qué hay de lo siguiente?

  1. crea un contador de microsegundos de uint16_t
  2. en el ISR de desbordamiento del temporizador, haga

    microsegundos + = 256; if (microsegundos > = 1000) {   microsegundos = microsegundos - 1000;   milisegundos ++; }

  3. timer_microseconds () devuelve milliseconds * 1000 + microseconds .

respondido por el JimmyB
0

A menos que esté ejecutando su ave a 256Mhz o más rápido, no obtendrá ninguna interrupción de la misma.

En su lugar, eche un vistazo a varias implementaciones de cronometraje, como el arduino, y vea cómo se pueden hacer las resoluciones secundarias.

Escribí sobre un conjunto de rutinas (puedo publicar el enlace más tarde) pero esencialmente se basa en un contador de 32 bits sobre flujo aumentado por el contador del temporizador.

Dependiendo del temporizador utilizado, obtienes 16 ticks por nosotros.

    
respondido por el dannyf

Lea otras preguntas en las etiquetas