Mikrocontroller – Ein Anfängerhandbuch – Einführung in Interrupts – Verwendung des Timer/Counters als Beispiel
Teilen
Microcontroller - Ein Anfängerleitfaden - Einführung in Interrupts - Verwendung des Timer/Zählers als Beispiel
Da wir uns noch gut mit Timern und Zählern auskennen, werden wir anhand des Zählers untersuchen, wie Interrupts funktionieren. Interrupts verbessern die Nutzung von Mikrocontrollern erheblich. Interrupts lassen Ihre Programme auf die Hardware der Mikrocontroller reagieren, was eine Reaktion von der Schaltung/Umgebung außerhalb des Mikrocontrollers sein kann.
Grundsätzlich ist ein Interrupt genau das, was sein Name besagt ... er unterbricht den normalen Programmfluss, um etwas anderes zu tun (einen anderen Codeblock, den Sie programmieren). Stellen Sie sich vor, Ihr Programm blinkt gerade LEDs oder so etwas, und Sie möchten, dass es auf, sagen wir, einen PIR-Sensor (Passive Infrarot) (an einen Interrupt-Pin angeschlossen) reagiert und zu einem speziellen Codeblock wechselt, um, sagen wir, einen Piepton von einem Summer (an einen anderen Pin angeschlossen) zu erzeugen. Wenn Sie dem Mikrocontroller dies mitteilen, wird er das Programm sofort anhalten und zu dem Code wechseln, der zu diesem Interrupt gehört (einen Piepton erzeugen). Nachdem der Interrupt-Code ausgeführt wurde, wird das Programm genau dort fortgesetzt, wo es aufgehört hat. In einigen Fällen wären Interrupts eine Alternative zum Polling von etwas, was Programmierzyklen erfordert. Zum Beispiel, Ihr Programm soll nur piepen, wenn eine Person den PIR-Sensor passiert. Ihr Programm könnte diesen Pin immer wieder testen, um zu sehen, ob er im endlosen Loop (while(1)) einen hohen Wert liefert. Stattdessen entfernen Sie den gesamten Polling-Programmcode zum Testen des Pins auf einen hohen Wert und lassen den Interrupt automatisch zum Piep-Code wechseln, wenn der Mikrocontroller den hohen Wert erkennt.
Welche Arten von Interrupts stehen für den Arduino Mikrocontroller zur Verfügung? Interrupts können für Ereignisse wie eine Zählnummer, einen Pin, der seinen Zustand ändert (von niedrig auf hoch oder hoch auf niedrig), den Empfang von Informationen über serielle Kommunikation oder die Durchführung einer Analog-Digital-Wandlung eingerichtet werden. Hier ist eine vollständige Liste der Interrupt-Vektoren, die Sie verwenden können. Andere Tutorials werden viele dieser Interrupt-Vektoren nutzen.
Wir werden den Timer/Zähler als Beispiel verwenden. Wir werden den Timer/Zähler über eine Zahl informieren, die der TCNT1 (der Zähler) erreichen muss. Die Zahl, die übereinstimmen soll, wird in ein Register namens OCR1A (Output Compare Register) geschrieben. Die "1" steht für den spezifischen Zähler, den wir verwenden, nämlich die 16-Bit-Version. Wir haben zwei OCRs, A und B. Wir werden A für dieses Beispiel verwenden. Aber wissen wir, ob der Zähler auf Null zurückgesetzt wird, wenn die Übereinstimmung erfolgt? Wir möchten definitiv, dass TCNT1 auf Null zurückgeht, damit der Zähler von vorne beginnt und wir eine weitere Übereinstimmung bei unserer vorgesehenen Zahl erhalten, aber dies wird nicht im Code wie letztes Mal (erinnern Sie sich an TCNT1 = 0;) gemacht. Diesmal möchten wir einen weiteren Schalter im Steuerregister TCCR1B aktivieren, genannt WGM12 (Waveform Generation Mode für Timer 1). Die #2 in diesem Schalter steht nur dafür, welcher WGM es ist, da es mehrere gibt. Wir werden auch wieder CS10 und CS11 verwenden, um die Prescaling auf 64 einzustellen, wie im Einführungsvideo zu Timern.
Dann muss der Timer/Zähler wissen, dass wir die Interrupt-Funktion verwenden möchten. Dies geschieht über das TIMSK (Timer/Counter Interrupt Mask Register). Wir müssen nur einen Schalter in diesem Register aktivieren: den OCIE1A (Output Compare A Match Interrupt Enable) Schalter. Sobald die in OCR1A eingegebene Zahl vom Zähler erreicht wird, wird das Programm unterbrochen, um die LED umzuschalten. Damit ein Interrupt ausgelöst wird, müssen wir die globalen Interrupts "sei()" aktivieren, dann müssen wir den Interrupt für den Timer/Zähler aktivieren, und schließlich muss die Interrupt-Service-Routine (ISR) erstellt werden. Die Interrupt-Service-Routine ist genau wie die Funktionen, die im Beispiel „Das Knopfspiel“ erstellt wurden. Die Interrupt-Service-Routine ist einfach ein Codeblock außerhalb der Hauptroutine und beginnt mit ISR mit dem Vektor in Klammern "(Vektor)", gefolgt vom Codeblock in geschweiften Klammern "{Code}". Für den Timer/Zähler (16-Bit-Version) und die Tatsache, dass wir die A-Version des OCR1 verwenden, lautet der Vektorname: TIMER1_COMPA_vect. Die Routine wäre also: ISR(TIMER1_COMPA_vect) { code to execute }.
Wir haben im Einführungsvideo zu Timern eine Zahl verwendet, die einer Sekunde entsprach: 15625. Diese werden wir für OCR1A verwenden. Die Zahl sollte jedoch die Indizierung ab 0 berücksichtigen, daher müssen wir stattdessen die Zahl 15624 verwenden.
Fassen wir all diese Informationen in einem Programm zusammen:
#include
int main(void)
{
DDRB |= 1<
TCCR1B |= 1<
while(1)
{
}
ISR(TIMER1_COMPA_vect)
{
Hier ist die Pseudocode-Version:
- Einige grundlegende Informationen zur korrekten Konfiguration unseres Mikrocontrollers einbinden.
- Die Interrupt-Bibliothek einbinden, die alle benötigten Interrupt-Funktionen enthält.
- Die Hauptroutine starten (die eingerückten Schritte ausführen)
- Globale Interrupts aktivieren
- Da wir eine LED verwenden, den entsprechenden Pin als Ausgang aktivieren
- Den Timer einschalten und 64 Prescaling verwenden (64 Takte überspringen)
- Das Output Compare Register aktivieren und eine Zahl für eine Sekunde einstellen
- Endlosschleife
- Dieses Mal kein Code in der Endlosschleife nötig
- Hauptroutine beenden
- Interrupt-Routine starten (für den korrekten Vektor)
- Den Pin umschalten, an den die LED angeschlossen ist
- Interrupt-Routine beenden (zurück zum Punkt, an dem das Programm unterbrochen wurde)
- Wenn du dich für eine Auswahl entscheidest, wird die Seite komplett aktualisiert.
- Wird in einem neuen Fenster geöffnet.