Manejo de desbordamiento del temporizador y comparación de interrupciones en ATMega328 (Arduino)

Estoy tratando de simular el modo PMW en múltiples pines en el software controlando un Timer2.

Estoy usando el valor máximo del preescalador para obtener aproximadamente 60 pulsos por segundo cuando el temporizador cuenta hasta su valor máximo. El valor de OCR2A se utiliza para activar el momento real dentro del ciclo completo del temporizador. La interrupción de desbordamiento se usa para encender el LED (PIN13) y la interrupción de comparación se usa para reiniciar el LED y apagarlo.

Cuando lo ejecuto, veo que el LED parpadea pero muy tenuemente y su brillo no se ve afectado por el valor CUT_OFF. Si desactivo el LED apagado en la interrupción de comparación y solo cambio el LED en desbordamiento, veo que parpadea a un ritmo razonable que coincide con el informe final impreso en el puerto serie (número de interrupciones en 5 segundos). El informe final también muestra que ambas interrupciones se llaman el número correcto de veces.

Aquí está el código que uso:

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

#define LED_PORT (13)

#define CUT_OFF (100)

volatile unsigned long time1 = 0;
volatile unsigned long time2 = 0;

ISR(TIMER2_OVF_vect) {
  PORTB |= 0x20;
  time1++;
}

ISR(TIMER2_COMPA_vect) {
  PORTB &= ~0x20;
  time2+=2;
}

void startTimer() {
  cli();
  // enable timer interrupt overflow + reg a
  TIMSK2 = _BV(OCIE2A) | _BV(TOIE2);
  // counter
  TCNT2  = 0x00;
  // cut off value
  OCR2A  = CUT_OFF;
  // mode - normal + prescaler 1024
  TCCR2A = 0x00;
  TCCR2B = _BV(CS22) | _BV(CS21) | _BV(CS20);
  // async mode off
  ASSR  &= ~(_BV(AS2) | _BV(EXCLK));
  sei();
}

void stopTimer() {
  cli();
  // disable interrupts
  TIMSK2 = 0x00;
  // mode (disconnect clock source)
  TCCR2A = 0x00;
  TCCR2B = 0x00;
  // async mode off
  ASSR  &= ~((1<<AS2) | (1<<EXCLK));
  sei();
}

void blinkLed(uint8_t times) {
  for(uint8_t i=0;i<times;i++) {
    digitalWrite(LED_PORT, HIGH);
    delay(500);
    digitalWrite(LED_PORT, LOW);
    delay(500);
  }
}

void setup() {
  pinMode(LED_PORT, OUTPUT); 
  Serial.begin(9600);
}

unsigned long iterations = 0;

#define MAX_ITERATIONS (1000000)

void loop() {
  blinkLed(3);
  startTimer();
  delay(5000);
  stopTimer();
  blinkLed(2);

  Serial.println("Iterations finished!");
  Serial.print("Timer1=");
  Serial.println(time1);
  Serial.print("Timer2=");
  Serial.println(time2);

  while(1);  
}

¿Hay algún problema con mi comprensión de cuándo se llaman las interrupciones?

  • COM2A - cuando alcanzamos el valor CUT_OFF
  • OVF cuando el contador llega a 255 y cambia a 0

o hay un problema con la configuración del temporizador o el manejo de interrupciones?

Actualización: ¿Podría estar el problema en el uso de digitalWrite() en las interrupciones? Actualización 2: cambiar digitalWrite para dirigir la manipulación de bits de PORTB produce un solo destello probablemente en la interrupción inicial y el LED muy tenue posterior, por lo que no es el único problema.

El LED parpadeante de PS es solo una prueba, por lo que usar PWM integrado no es una opción.

¿Has leído la nota de la aplicación de Atmel sobre los temporizadores AVR? atmel.com/dyn/resources/prod_documents/doc2505.pdf
@JobyTaffey He leído la nota mencionada anteriormente, pero aún no puedo encontrar la explicación del comportamiento que veo. Se llama a ambas interrupciones, el período de interrupción no depende del valor de salida de comparación, pero el tiempo entre interrupciones es incorrecto.

Respuestas (1)

No estoy completamente seguro de por qué , pero el uso del modo pwm rápido en lugar del modo normal parece solucionar este problema.

Use el mismo código, pero reemplazando

TCCR2A = 0x00;

con

TCCR2A = _BV(WGM20)|_BV(WGM21);

También preguntaría por qué parece estar borrando el modo asíncrono al entrar y salir del modo pwm, pero no parece afectar nada.

La descripción del modo PWM establece explícitamente el uso de 2 interrupciones para rastrear los momentos en que comienza el próximo período y cuándo se debe cambiar el valor de salida. Pero cambia OCRxn al modo de doble búfer y solo puede cambiarlo al final del ciclo. Lo que quería hacer es capturar algunos momentos irregulares dentro de un período recargando el registro de comparación y reiniciando el controlador después de cada ciclo. Así que descarta completamente el modo PWM para mí. Parece que volveré a comparar la interrupción y la recarga de registros, pero olvídate de la interrupción de desbordamiento y solo carga el valor SUPERIOR una vez que se filtran todos los pasos intermedios.
@Brog, nadie se arriesgó a hacer un representante para explicar por qué eso lo soluciona, así que como el primero en responder, aquí hay un representante.
@Brog, estaba borrando el modo asíncrono al ingresar a pwm solo para asegurarme de que esté configurado en algo sensato, porque no es fácil averiguar en qué estado las librerías de arduino pueden dejarlas. Al final, miré las bibliotecas de servo y parece que recargan manualmente el registro varias veces dentro de un ciclo ignorando la interrupción de desbordamiento como planeé hacer. Parece que tampoco lograron trabajar con el desbordamiento.