Cómo actualizar una variable en un ISR usando temporizadores

8

Estoy tratando de verificar la frecuencia de Timer3 usando un contador. El valor del contador, declarado como volátil, se incrementa en el ISR y cada segundo la suma se muestra en el bucle principal y el valor se restablece a cero.

El temporizador se ha configurado correctamente. (Si elijo un temporizador de 3Hz, puedo ver el led parpadeando)

El problema

El contador no se incrementa. Aquí está la salida:

Setup Completed
tick: 1
tick: 0
tick: 0
tick: 0

CÓDIGO

volatile int cont = 0;

void setup()
{
  Serial.begin(9600);

  pinMode(13, OUTPUT);

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B = 20; // 800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM12);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();

  Serial.println("Setup completed");
}

void loop()
{
  if (millis() % 1000 == 0)
  {
    Serial.print(" tick: ");
    Serial.println(cont);
    cont = 0;
  }
}

ISR(TIMER3_COMPB_vect)
{
  //digitalWrite(13, digitalRead(13) ^ 1);
  cont++;
}

EDITAR Este temporizador se usa para leer un valor anlog de un acelerómetro y almacenarlo en una matriz de flotante. Pero en este momento estoy atascado en este problema de actualización.

SOLUCIÓN 1 Gracias a Gerben

volatile int cont = 0;

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

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3A = 20; // 20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  delay(1000);
  Serial.println(cont);
  cont = 0;
}

ISR(TIMER3_COMPB_vect)
{
  cont++;
}

SOLUCIÓN 2 Gracias a BrettM

volatile int cont = 0;

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

  // Initialize Timer
  cli();          // disable global interrupts
  TCCR3A = 0;     // set entire TCCR3A register to 0
  TCCR3B = 0;     // same for TCCR3B

  // set compare match register to desired timer count: 800 Hz
  OCR3B =  20; //800Hz 5; // 3 Hz
  // turn on CTC mode:
  //TCCR3B |= (1 << WGM32);
  // Set CS10 and CS12 bits for 1024 prescaler:
  TCCR3B |= (1 << CS30) | (1 << CS32);
  // enable timer compare interrupt:
  TIMSK3 |= (1 << OCIE3B);
  // enable global interrupts:
  sei();
  Serial.println("Setup completed");
}

void loop()
{
  Serial.println(cont); 
  cont = 0;
  delay(1000);

}

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}
UserK
fuente
¿Y si descomenta la digitalWritelínea, ve que el LED parpadea aproximadamente una vez por segundo (cada 0.66s)?
Ricardo
Sí, si descomento digitalWritey configuro OCR3B = 5;el led parpadea aproximadamente a esa frecuencia.
UserK
Entonces es un misterio. ¿Has intentado comentar el cont = 0;interior del bucle? ¿Qué pasa entonces?
Ricardo
1
Intenta aumentar la frecuencia. Creo que su declaración if puede estar limpiando el contador más de lo que se llama la interrupción, de alguna manera. Pero entonces debería ver más en la salida. También deje que se ejecute por más tiempo (digamos 1 minuto) y pegue los resultados. Además, cuando actualice la pregunta, deje la salida anterior para que su pregunta tenga sentido (sin el historial de edición).
Ricardo
1
Sospecho que la rutina de interrupción se llama solo una vez y luego se desactiva. Leí en alguna parte que las interrupciones están deshabilitadas cuando se está ejecutando un código de interrupción, y en algunos casos tienes que volver a habilitarlo, pero realmente no estoy seguro de si ese es el caso. Esperemos que alguien más informado venga a rescatarnos ...
Ricardo

Respuestas:

5

En modo CTC, la parte superior es OCR3A, OCR3B¡ no !

Después de eso TIMSK3 |= (1 << OCIE3B);también se debe cambiar a TIMSK3 |= (1 << OCIE3A);, y ISR(TIMER3_COMPB_vect)aISR(TIMER3_COMPA_vect)

Para 3Hz, OCR3Adebe ser 5208, no 20.

Técnicamente TCCR3B |= (1 << WGM12);debería serTCCR3B |= (1 << WGM32);

Gerben
fuente
Con su configuración, el contador no se actualiza y cada segundo se muestra la oración "Instalación completada" (¡escrita en la función de configuración ()!). Comportamiento realmente extraño.
UserK
Resuelto usando TIMSK3 |= (1 << OCIE3B);. Gracias Gerben! Modifique su respuesta y la aceptaré como solución.
UserK
1
Olvidé la mención, también necesitas cambiar el vector ISR. ISR(TIMER3_COMPB_vect)debería ser ISR(TIMER3_COMPA_vect). Si no se define un ISR, el AVR se reiniciará, como lo estaba experimentando. Me alegra que lo hayas hecho funcionar.
Gerben
3

Parece que mi respuesta a esta pregunta estaba incompleta anteriormente, gracias por señalar que el modo CTC solo funciona con OCR3A Gerben. Pido disculpas por no probar una respuesta antes de publicarla.

Dada la información solo en esta pregunta, la respuesta de Gerben está completa, pero dado que su otra pregunta implica que no puede usar OCR3A debido a la biblioteca Servo, agregaré un poco. (También he editado esa respuesta)

puede emular el comportamiento del modo CTC configurando TCNT3 en 0 en su rutina de interrupción. Recuerde eliminar la línea que activa el modo CTC en su código.

He probado tu código con este ISR:

ISR(TIMER3_COMPB_vect)
{
  TCNT3 = 0;
  cont++;
}

y esta configuración de los registros del temporizador

OCR3B = 5208; // 800Hz 5; // 3 Hz
// Set CS10 and CS12 bits for 1024 prescaler:
TCCR3B |= (1 << CS30) | (1 << CS32);
// enable timer compare interrupt:
TIMSK3 |= (1 << OCIE3B);

Esto podría ser un poco menos preciso a altas frecuencias que CTC, no estoy seguro, pero a 3Hz funcionó perfectamente. Tenga en cuenta que 5208 era el valor de OCR correcto, no 20 (nuevamente gracias a Gerben).

BrettAM
fuente
He intentado su código pero el contador no se incrementa. Agregué el TCNT3=0;en el ISR () y eliminé //TCCR3B |= (1 << WGM32);en la configuración () como dijiste. También he intentado comentar la cont=0;línea pero nada ha cambiado
UserK
1
Asegúrese de que el código coincida con lo publicado en la pregunta de cualquier otra manera. Intenta cambiar tu ciclo a solo println(cont); delay(1000);. También todavía incluye los bits con cli () y TCCR3A, etc. ¿correcto?
BrettAM
OK gracias. ¡A 800 Hz sigue siendo precisa!
UserK