So aktualisieren Sie eine Variable in einem ISR mithilfe von Timern


8

Ich versuche die Frequenz von Timer3 mit einem Zähler zu überprüfen. Der als flüchtig deklarierte Wert des Zählers wird im ISR inkrementiert und jede Sekunde wird die Summe in der Hauptschleife angezeigt und der Wert auf Null zurückgesetzt.

Der Timer wurde korrekt eingestellt. (Wenn ich einen 3-Hz-Timer wähle, kann ich sehen, dass die LED blinkt.)

Das Problem

Der Zähler wird nicht erhöht. Hier ist die Ausgabe:

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

CODE

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

BEARBEITEN Dieser Timer wird verwendet, um einen Anlog-Wert von einem Beschleunigungsmesser zu lesen und in einem Array von float zu speichern. Aber im Moment bin ich bei diesem Update-Problem festgefahren.

LÖSUNG 1 Danke an 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++;
}

LÖSUNG 2 Dank an 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++;
}

Und wenn Sie die digitalWriteZeile auskommentieren, blinkt die LED etwa einmal pro Sekunde (alle 0,66 s)?
Ricardo

Ja, wenn ich die LED auskommentiere digitalWriteund OCR3B = 5;auf ungefähr diese Frequenz blinke.
UserK

Dann ist es ein Geheimnis. Haben Sie versucht , die Kommentierung aus cont = 0;innerhalb der Schleife? Was passiert dann?
Ricardo

1
Versuchen Sie, die Frequenz zu erhöhen. Ich denke, Ihre if-Anweisung löscht den Zähler möglicherweise öfter, als der Interrupt irgendwie aufgerufen wird. Aber dann sollten Sie mehr in der Ausgabe sehen. Lassen Sie es auch länger laufen (z. B. 1 Minute) und fügen Sie die Ergebnisse ein. Wenn Sie die Frage aktualisieren, belassen Sie die alte Ausgabe, damit Ihre Frage sinnvoll ist (ohne den Bearbeitungsverlauf).
Ricardo

1
Ich vermute, dass die Interrupt-Routine nur einmal aufgerufen und dann deaktiviert wird. Ich habe irgendwo gelesen, dass die Interrupts deaktiviert sind, wenn ein Interrupt-Code ausgeführt wird, und in einigen Fällen müssen Sie ihn erneut aktivieren, aber ich bin mir wirklich nicht sicher, ob dies der Fall ist. Hoffentlich kommt jemand, der mehr kenntnisreich ist, zu unserer Rettung ...
Ricardo

Antworten:


5

Im CTC-Modus ist die Spitze OCR3Anicht OCR3B!

Danach TIMSK3 |= (1 << OCIE3B);sollte auch geändert werden TIMSK3 |= (1 << OCIE3A);, und ISR(TIMER3_COMPB_vect)zuISR(TIMER3_COMPA_vect)

Für 3Hz OCR3Asollte 5208 sein, nicht 20.

Technisch TCCR3B |= (1 << WGM12);sollte seinTCCR3B |= (1 << WGM32);


Bei Ihrer Konfiguration wird der Zähler nicht aktualisiert und jede Sekunde wird der Satz "Setup abgeschlossen" (geschrieben in der Funktion setup ()!) Angezeigt. Wirklich komisches Verhalten.
UserK

Gelöst mit TIMSK3 |= (1 << OCIE3B);. Danke Gerben! Bitte ändern Sie Ihre Antwort und ich werde sie als Lösung akzeptieren.
UserK

1
Ich habe die Erwähnung vergessen, dass Sie auch die Änderung des ISR-Vektors benötigen. ISR(TIMER3_COMPB_vect)sollte sein ISR(TIMER3_COMPA_vect). Wenn kein ISR definiert ist, wird der AVR wie von Ihnen selbst zurückgesetzt. Ich bin froh, dass du es zum Laufen gebracht hast.
Gerben

3

Es scheint, dass meine Antwort auf diese Frage zuvor unvollständig war, danke für den Hinweis, dass der CTC-Modus nur mit OCR3A Gerben funktioniert. Ich entschuldige mich dafür, dass ich eine Antwort nicht getestet habe, bevor ich sie poste.

Angesichts der Informationen nur in dieser Frage ist Gerbens Antwort vollständig, aber da Ihre andere Frage impliziert, dass Sie OCR3A aufgrund der Servobibliothek nicht verwenden können, werde ich ein wenig hinzufügen. (Ich habe diese Antwort auch bearbeitet)

Sie können das Verhalten des CTC-Modus emulieren, indem Sie TCNT3 in Ihrer Interrupt-Routine auf 0 setzen. Denken Sie daran, die Zeile zu entfernen, die den CTC-Modus in Ihrem Code aktiviert.

Ich habe Ihren Code mit diesem ISR getestet:

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

und diese Konfiguration der Zeitgeberregister

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

Ich bin mir nicht sicher, ob dies bei hohen Frequenzen etwas ungenauer ist als bei CTC, aber bei 3 Hz hat es perfekt funktioniert. Beachten Sie, dass 5208 der richtige OCR-Wert war, nicht 20 (erneut dank Gerben).


Ich habe Ihren Code ausprobiert, aber der Zähler wird nicht erhöht. Ich habe das TCNT3=0;im ISR () hinzugefügt und //TCCR3B |= (1 << WGM32);im Setup () entfernt, wie Sie sagten. Ich habe auch versucht, die cont=0;Zeile zu kommentieren, aber nichts hat sich geändert
UserK

1
Stellen Sie sicher, dass der Code auf jede andere Weise mit dem übereinstimmt, was in der Frage angegeben ist. Versuchen Sie, Ihre Schleife auf einfach zu ändern println(cont); delay(1000);. Auch schließen Sie noch die Bits mit cli () und TCCR3A usw. richtig ein?
BrettAM

OK danke. Bei 800 Hz ist immer noch genau!
UserK
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.