Arduino Uno Timer Interrupts

Arduino Timer Interrupts – Arduino Register programmieren

Wenn du eine regelmäßige Frequenz mit dem Arduino erreichen willst, kannst du einfach die delay() Funktion verwenden. Diese pausiert das Programm des Arduinos für die entsprechende Zeitspanne. Wenn die Anforderungen höher sind, kannst du auch millis() oder nanos() als Timer verwenden. Die delay() und millis() Funktionen sind wahrscheinlich für die meisten Anwendungen ausreichend, aber wenn du nicht das ganze Programm pausieren oder eine 100% exakte Taktzeit erreichen willst, macht es Sinn, Arduino Timer Interrupts zu verwenden. Wir erklären was Timer Interrupts sind und wie man sie benutzt. Den Arduino Code findest du am Ende des Beitrags.

Das könnte dich auch interessieren: Raspberry Pi Timer programmieren in C

Liste der Komponenten

Was ist eigentlich ein Timer?

Ein Timer ist im Grunde nichts anderes als ein bestimmtes Register im Mikrocontroller, das hardwaregesteuert kontinuierlich um 1 erhöht (oder verringert) wird. Anstatt Anweisungen im Programm zu kodieren, die regelmäßig ausgeführt werden und ein Register um 1 erhöhen, macht der Mikrocontroller das ganz von alleine!

Dies wird nützlich, wenn eine Aktion bei bestimmten Zählerwerten ausgeführt wird. Einer dieser ‚bestimmten Zählerstände‘ ist zum Beispiel der Überlauf. Das Zählregister eines Timers kann nicht beliebig lange inkrementiert werden. Z.B. ist der höchste Zählerstand, den ein 8-Bit-Timer erreichen kann, 2^8 – 1 = 255. Der nächste Schritt ist nicht 256, sondern es kommt zu einem Überlauf, der den Timer wieder auf 0 setzt. Das ist die ganze Magie! Wir können den Controller so konfigurieren, dass ein Interrupt ausgelöst wird, wenn der Timer überläuft. Anschließend können wir einen Code in das Arduino Programm schreiben, der im Falle eines Interrupts ausgeführt wird. Zum Beispiel können wir eine LED zum Leuchten bringen oder einen bestimmten Sensorwert abfragen.

Arduino Uno Mikrocontroller ATMEGA328P

Microcontroller ATMEGA328P Nerd Corner Arduino Uno Timer Interrupts

Der ATMEGA328P-Mikrocontroller ist das Herzstück des Arduino Uno-Boards. (ACHTUNG: Der Arduino Mega z.B. hat einen anderen Mikrocontroller!) Der ATMEGA328P Mikrocontroller hat 3 Timer (Datenblatt), die teilweise in Arduino-Funktionen und/oder teilweise in Bibliotheken verwendet werden. Das Überschreiben der Timer-Register kann daher zu Komplikationen mit bestehenden Timer-Funktionen wie millis(), micros() oder delay() führen und sollte mit Vorsicht verwendet werden. Die 3 Timer sind Timer0 (8Bit), Timer1 (16Bit) und Timer2 (8Bit).

  • 8 Bit-Timer0: wird genutzt für die Funktionen millis(), micros(), delay() und für PWM an Pin D5 und D6
  • 16 Bit-Timer1: wird z. B. für die Bibliothek Servo, VirtualWire, TimerOne und für PWM an Pin D9 und D10 genutzt
  • 8 Bit Timer2: wird genutzt für Funktion tone() und für PWM an Pin D3 und D11

Wie variiert man die Taktgeschwindigkeit?

Der Systemtakt des Arduino Uno beträgt 16 MHz (CPU-Frequenz). Das bedeutet, dass Timer0, Timer1 und Timer2 16 Millionen Mal pro Sekunde hochlaufen. Die 8-Bit-Timer zählen z. B. jeweils von 0 bis 255. Bei 256 tritt ein Überlauf auf und die Timer beginnen wieder bei 0. Das bedeutet 16000000/256 = 62500 Überläufe pro Sekunde (62,5kHz Taktrate). Das ist für die meisten Timer-Anwendungen wahrscheinlich zu schnell!

Deshalb gibt es einen Trick, um die Taktraten zu verlangsamen. Man verwendet einen sogenannten Vorteiler (Prescaler). Ein Vorteiler kann auf die Werte 1, 8, 64, 256 oder 1024 eingestellt werden. Er ermöglicht es, den Systemtakt (16MHz) durch den gewählten Faktor zu teilen und eine niedrigere Taktrate für die Timer einzustellen. Ein Vorteiler von 1024 würde z. B. die Timer-Register nur beim 1024sten Systemtakt um 1 erhöhen. Das wären 16000000/1024=15625 Inkremente pro Sekunde und damit bei einem 8-Bit-Timer 15625/256= 61,035 Überläufe pro Sekunde (~61 Hz Taktrate des Timers).

Anwendungsbeispiel: LED soll mit 50 Hz blinken

Im Folgenden wird die Ansteuerung von Arduino Timer Interrupts mit dem 16-Bit Timer1 gezeigt. Damit soll eine LED im 50 Hz-Takt aufleuchten. Schaltplan, Arduino-Code und Bilder sind ebenfalls enthalten. (Die Vorgehensweise für den 8-Bit-Timer0 und Timer2 ist analog.) Für den zeitgesteuerten Impuls benötigen Sie den sogenannten „CTC-Modus“.

Im CTC-Modus („Clear Timer on Compare Mode“) wird der Zähler gelöscht, wenn der Wert des Zählers (TNCT1) entweder mit dem Wert des OCR1A-Registers oder dem Wert des ICR1-Registers (in unserem Fall OCR1A) übereinstimmt. Das OCR1A-Register bestimmt also den Maximalwert des Zählers und damit seine Auflösung.

Der 16 Bit Timer1 braucht die folgenden Register:

  • Timer Counter Register 1: TCNT1
  • Output Compare Register A: OCR1A
  • Timer Counter Control Register A: TCCR1A
  • Timer Counter Control Register B: TCCR1B
  • Timer/Counter Interrupt Mask Register: TIMSK1
  • (Für Timer0 und Timer2 würden die entsprechenden Register TCNT0 bzw. TCNT2 heißen)

Berechnung des OCR1A Registers für Arduino Timer Interrupts

Der Wert des Registers OCR1A ist abhängig von der gewünschten Interruptfrequenz und dem gewählten Vorteiler. Es gilt die folgende Formel:

Formula for Arduino Timer Interrupt Frequency calculation OCR1A register

Wir setzen unsere Spezifikationen in die Formel ein:

  • CPU-Frequenz Arduino Uno: 16.000.000 Hz
  • Gewünschte Interruptfrequenz: 50 Hz (= 20 ms Periodendauer)
  • Möglicher Vorteiler: 1, 8, 64, 256 oder 1024

Berechnungsbeispiel mit Vorteiler 1024:
OCR1A= (16.000.000 / (1024 * 50)) – 1 = 311,5

Berechnungsbeispiel mit Vorteiler 8:
OCR1A= (16,000,000 / (8 * 50)) – 1 = 39,999

ACHTUNG: Der OCR1A-Wert muss kleiner als 65,536 (2^16 ) sein!

Daher kann ein Vorteiler 8 NICHT die 10 Hz Interruptfrequenz erreichen:
OCR1A= (16.000.000 / (8 * 10)) – 1 = 199.999

Der Wert 199,999 ist größer als das Register mit 65,536, daher muss ein anderer Vorteiler verwendet werden, um 10 Hz zu erreichen

Stattdessen ein Vorteiler 64 für 10 Hz Interruptfrequenz:
OCR1A= (16.000.000 / (64 * 10)) – 1 = 24.999

Wird nun ein Timer1-Interrupt ausgelöst, springt der Programmablauf in eine zu erstellende Interrupt-Service-Routine „ISR(TIMER1_COMPA_vect)“. (Siehe Arduino Code)

Bit Kombination für die gewünschten Vorteiler

Arduino Uno timer interrupts Prescaler

Arduino Timer Interrupts Code für die 50 Hz Frequenz

void setup() {

  pinMode(11,OUTPUT);  //LED pin (to blink in 50Hz frequency)
  
//START TIMER SETUP
//TIMER SETUP for highly preceise timed measurements 

  cli();//stop all interrupts

  // turn on CTC mode
  TCCR1A = 0;// set entire TCCR1A register to 0
  TCCR1B = 0;// same for TCCR1B
  TCCR1B |= (1 << WGM12);

  // Set CS11 bit for prescaler 8
  TCCR1B |= (1 << CS11); 
  
  //initialize counter value to 0;
  TCNT1  = 0;
  
  // set timer count for 50Hz increments
  OCR1A = 39999;// = (16*10^6) / (50*8) - 1  
  
  // enable timer compare interrupt
  TIMSK1 |= (1 << OCIE1A);
  
  sei();//allow interrupts
  //END TIMER SETUP
}



ISR(TIMER1_COMPA_vect) {//Interrupt at frequency of 50 Hz
 //write your timer code here

 digitalWrite(11,HIGH);
 delay(15);
 digitalWrite(11,LOW);
}




void loop() {

//when the timer is over, your program will stop in the loop function and jump to the timer code. 
//After the timer code it will jump back to the point where it left the loop function
}

Arduino Timer Interrupt LED sketch

3 Gedanken zu “Arduino Timer Interrupts – Arduino Register programmieren

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Cookie Consent mit Real Cookie Banner