Come aggiornare una variabile in un ISR usando i timer


8

Sto cercando di controllare la frequenza di Timer3 utilizzando un contatore. Il valore del contatore, dichiarato come volatile, viene incrementato nell'ISR e ogni secondo la somma viene mostrata nel loop principale e il valore resettato a zero.

Il timer è stato impostato correttamente. (Se scelgo un timer da 3Hz posso vedere il led che lampeggia)

Il problema

Il contatore non è incrementato. Ecco l'output:

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

CODICE

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++;
}

EDIT Questo timer viene utilizzato per leggere un valore anlog da un accelerometro e memorizzarlo in un array di float. Ma al momento sono bloccato su questo problema di aggiornamento.

SOLUZIONE 1 Grazie 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++;
}

SOLUZIONE 2 Grazie 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++;
}

E se togli il commento alla digitalWritelinea vedi il LED lampeggiare circa una volta al secondo (ogni 0,66 secondi)?
Ricardo,

Sì, se rimuovo il commento digitalWritee imposto OCR3B = 5;il led lampeggia a circa quella frequenza.
UtenteK

Quindi è un mistero. Hai provato a commentare l' cont = 0;interno del ciclo? Cosa succede allora?
Ricardo,

1
Prova ad aumentare la frequenza. Penso che la tua dichiarazione if possa cancellare il contatore più spesso di quanto viene chiamato l'interrupt, in qualche modo. Ma poi dovresti vederne di più nell'output. Inoltre, se eseguito più a lungo (diciamo 1 minuto) e incolla i risultati. Inoltre, quando aggiorni la domanda, lascia il vecchio output in modo che la tua domanda abbia un senso (senza la cronologia delle modifiche).
Ricardo,

1
Ho il sospetto che la routine di interrupt venga chiamata una sola volta e poi sia disabilitata. Ho letto da qualche parte che gli interrupt sono disabilitati quando è in esecuzione un codice di interrupt e in alcuni casi è necessario riattivarlo, ma non sono davvero sicuro che sia così. Speriamo che qualcuno in più venga in nostro aiuto ...
Ricardo,

Risposte:


5

In modalità CTC il massimo è OCR3Ano OCR3B!

Dopodiché TIMSK3 |= (1 << OCIE3B);dovrebbe essere modificato anche in TIMSK3 |= (1 << OCIE3A);e ISR(TIMER3_COMPB_vect)inISR(TIMER3_COMPA_vect)

Per 3Hz, OCR3Adovrebbe essere 5208, non 20.

Tecnicamente TCCR3B |= (1 << WGM12);dovrebbe essereTCCR3B |= (1 << WGM32);


Con la tua configurazione il contatore non viene aggiornato e ogni secondo viene mostrata la frase "Setup completato", (scritto nella funzione setup ()!). Comportamento davvero strano.
UtenteK

Risolto usando TIMSK3 |= (1 << OCIE3B);. Grazie Gerben! Modifica la tua risposta e la accetterò come soluzione.
UtenteK

1
Ho dimenticato la menzione che è necessario anche cambiare il vettore ISR. ISR(TIMER3_COMPB_vect)dovrebbe essere ISR(TIMER3_COMPA_vect). Se un ISR non è definito, l'AVR si ripristinerà da solo, come si stava verificando. Sono contento che tu abbia funzionato.
Gerben,

3

Sembra che la mia risposta a questa domanda sia stata precedentemente incompleta, grazie per aver sottolineato che la modalità CTC funziona solo con OCR3A Gerben. Mi scuso per non aver testato una risposta prima di pubblicarla.

Date le informazioni solo in questa domanda, la risposta di Gerben è completa, ma poiché l'altra domanda implica che non è possibile utilizzare OCR3A a causa della libreria Servo, aggiungerò un po '. (Ho anche modificato quella risposta)

puoi emulare il comportamento della modalità CTC impostando TCNT3 su 0 nella tua routine di interrupt. Ricorda di rimuovere la riga che attiva la modalità CTC nel tuo codice.

Ho testato il tuo codice con questo ISR:

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

e questa configurazione dei registri del timer

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

Questo potrebbe essere un po 'meno preciso alle alte frequenze rispetto al CTC, non ne sono sicuro, ma a 3Hz ha funzionato perfettamente. Si noti che 5208 era il valore OCR corretto, non 20 (di nuovo grazie a Gerben).


Ho provato il tuo codice ma il contatore non è incrementato. Ho aggiunto l' TCNT3=0;ISR () e rimosso //TCCR3B |= (1 << WGM32);nel setup () come hai detto. Ho anche provato a commentare la cont=0;riga, ma nulla è cambiato
UserK l'

1
Assicurati che il codice corrisponda a ciò che è pubblicato nella domanda in ogni altro modo. Prova a cambiare il loop in just println(cont); delay(1000);. Inoltre stai ancora includendo i bit con cli () e TCCR3A ecc. Corretti?
BrettAM,

Ok grazie. A 800 Hz è ancora preciso!
UtenteK
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.