Wie erstelle ich einen Timer-Interrupt mit Arduino?

9

Ich versuche mit Arduino einen Zeitverzögerungs-Interrupt zu erstellen. Ich möchte die Funktion interrupts () verwenden, da es sich um einen internen Interrupt handelt.

Beispiel: Nehmen wir an, ich möchte ein Licht ein- und ausschalten lassen, nur mit dem Zeitpunkt des Interrupts.

Es gibt Beispielcode, der jedoch externe Interrupts verwendet (attachInterrupt ()). Ich möchte weiterhin die internen Interrupts verwenden.

Aleatory
quelle
2
Ich denke, dass der Punkt, den auch Kortuk gezeigt hat, ist, dass attachInterrupt eine abstrakte Sache ist, Sie
hängen
Dieser Artikel könnte Ihnen helfen. engblaze.com/…
Seth Archer Brown

Antworten:

10

Noah Stahls Blog zeigt ein Beispiel für das Blinken einer LED mit Timer2 . Mit diesem und dem Datenblatt sollten Sie in der Lage sein, es an den Interrupt anzupassen, den Sie verwenden möchten - dh an den Interrupt, dessen normale Funktion Sie sich am meisten leisten können, aufzugeben oder zu ändern bereit sind. Timer2 wird normalerweise für einige PWM-Funktionen verwendet.

Sein Beispiel zitiert den ATmega2560; Ich kann bestätigen, dass es auch mit einem ATmega328p funktioniert. Schauen Sie sich auf seiner Website nach nützlicheren Beispielen für Arduino-Interrupts um.

Bearbeiten:

Hier ist meine leicht bearbeitete - meistens in den Kommentaren - Version von Noahs Code. Rufen Sie Timer2init () über die Funktion Arduino setup () auf, nachdem Sie alle zugehörigen Datenstrukturen oder Hardware initialisiert haben, da das Timing - und das Unterbrechen - sofort beginnt.

F / ex, ich habe es verwendet, um eine dreistellige 7-Segment-Anzeige zu multiplexen, also habe ich vor dem Initialisieren des Timers die Anzeige-E / A-Register initialisiert und die Anzeigedaten an der Stelle ausgeblendet, an der der ISR danach suchen wird.

Es gibt eine Tabelle in den Kommentaren einiger nützlicher Zeitdaten aus dem Datenblatt und meiner eigenen Berechnungen als Referenz, um ein anderes Zeitschema einzurichten.

Das Makro ISR () sorgt dafür, dass anstelle des Ein- und Ausstiegs einer normalen Funktion ein Interrupt-Ein- und Ausstiegscode für einen ISR erstellt und mit dem richtigen Interrupt-Vektor verknüpft wird. Der Rest dieser Funktion ist 1) der Code, der bei jedem Interrupt ausgeführt werden soll, und 2) der Codecode zum Zurücksetzen des Timers für den nächsten Interrupt.

Wie geschrieben, sollte dies in eine .pde- oder .ino-Skizze (oder eine .cpp-Datei, wenn Sie Eclipse verwenden, f / ex) eingefügt werden. Die Skizze muss # LEDPIN definieren und setup () muss Timer2init () aufrufen. Die Schleifenfunktion kann leer sein oder nicht; Die LED sollte beim Download zu blinken beginnen (im wahrsten Sinne des Wortes, nachdem Timer2init () aufgerufen wurde).

/*
 * From sample interrupt code published by Noah Stahl on his blog, at:
 * http://arduinomega.blogspot.com/p/arduino-code.html
 * 
 */


/*** FUNC

Name:           Timer2init

Function:       Init timer 2 to interrupt periodically. Call this from 
                the Arduino setup() function.

Description:    The pre-scaler and the timer count divide the timer-counter
                clock frequency to give a timer overflow interrupt rate:

                Interrupt rate =  16MHz / (prescaler * (255 - TCNT2))

        TCCR2B[b2:0]   Prescaler    Freq [KHz], Period [usec] after prescale
          0x0            (TC stopped)     0         0
          0x1                1        16000.        0.0625
          0x2                8         2000.        0.500
          0x3               32          500.        2.000
          0x4               64          250.        4.000
          0x5              128          125.        8.000
          0x6              256           62.5      16.000
          0x7             1024           15.625    64.000


Parameters: void

Returns:    void

FUNC ***/

void Timer2init() {

    // Setup Timer2 overflow to fire every 8ms (125Hz)
    //   period [sec] = (1 / f_clock [sec]) * prescale * (255-count)
    //                  (1/16000000)  * 1024 * (255-130) = .008 sec


    TCCR2B = 0x00;        // Disable Timer2 while we set it up

    TCNT2  = 130;         // Reset Timer Count  (255-130) = execute ev 125-th T/C clock
    TIFR2  = 0x00;        // Timer2 INT Flag Reg: Clear Timer Overflow Flag
    TIMSK2 = 0x01;        // Timer2 INT Reg: Timer2 Overflow Interrupt Enable
    TCCR2A = 0x00;        // Timer2 Control Reg A: Wave Gen Mode normal
    TCCR2B = 0x07;        // Timer2 Control Reg B: Timer Prescaler set to 1024
}



/*** FUNC

Name:       Timer2 ISR

Function:   Handles the Timer2-overflow interrupt

Description:    Maintains the 7-segment display

Parameters: void

Returns:    void

FUNC ***/

ISR(TIMER2_OVF_vect) {
    static unsigned int led_state = 0; // LED state

    led_state = !led_state;         // toggles the LED state
    digitalWrite(TOGGLE_PIN, led_state);

    TCNT2 = 130;     // reset timer ct to 130 out of 255
    TIFR2 = 0x00;    // timer2 int flag reg: clear timer overflow flag
};
JRobert
quelle
(@Kortuk: Der Kommentar, auf den Sie sich beziehen, war meine Beobachtung mehrerer Kommentatoren hier und richtete sich nicht an Sie persönlich und war unnötig. Ich entschuldige mich und habe ihn entfernt.) Ich habe meine Antwort erweitert, wie Sie vorgeschlagen haben, und ich hoffe, dass es so ist Jetzt nicht nur demonstrativ, sondern auch lehrreich. Es enthält Kommentare, die ich für meinen eigenen Gebrauch in den Code geschrieben habe (dh: Wenn ich sie in 6 Monaten verstehen kann, kann es auch jemand anderes), sowie einige Anweisungen zur Verwendung in den Code Antworten. Vielen Dank für Ihre Vorschläge.
JRobert
Beachten Sie, dass für timer0 und timer1 keine Vorskalen von 32 und 128 verfügbar sind (mindestens mit atmega328).
Tuupola
Das ist gut zu wissen - danke. Ich benutze dies für Timer2 (bis jetzt) ​​und es ist im Grunde ein Drop-In.
JRobert
5

Die Funktion attachInterrupt () hängt tatsächlich einen Interrupt an eine externe Statusänderung an einem Pin an. Es gibt keine anderen Optionen.

Auf derselben Seite werden die Modusoptionen wie folgt aufgelistet:

Modus definiert, wann der Interrupt ausgelöst werden soll. Vier Kontanten sind als gültige Werte vordefiniert:

  • LOW , um den Interrupt auszulösen, wenn der Pin niedrig ist.
  • ÄNDERN , um den Interrupt auszulösen, wenn der Pin seinen Wert ändert
  • RISING wird ausgelöst, wenn der Pin von niedrig nach hoch wechselt.
  • FALLING für, wenn der Stift von hoch nach niedrig geht.

Es tut mir leid, dass ich schlechte Nachrichten überbringe, das ist eines der ersten Dinge, nach denen ich auch gesucht habe.

Kortuk
quelle
Ich denke, dass er meint, dass er einen internen Timer anstelle eines externen Geräts verwenden möchte ... aber ich kenne Arduino nicht sehr gut, daher kann ich nicht sagen, ob es möglich ist
Clabacchio
@clabacchio, ich sage, dass die einzige Option die Verwendung eines externen Triggers ist, es gibt keine interne Timer-Funktion.
Kortuk
Ah, gut :) aber haben die Arduino-Boards wenigstens Timer?
Clabacchio
Ja, so erreichen sie Dinge wie Verzögerung.
Kortuk
1
@ icarus74 ATMega328 hat wirklich 3 Timer (einer ist 16b und zwei sind 8b), aber alle werden von Arduino verwendet. Eine wird für Funktionen wie delay () und millis () verwendet und alle drei werden für PWM verwendet (weitere Informationen finden Sie in der Funktion 'init ()', Datei 'wiring.c' in Arduino IDE).
Vasco
2

Dieser Artikel über PWM wird viele Ihrer Zweifel bezüglich der Verwendung von Arduino-Timern beseitigen. Auf Arduino gibt es zwei 8-Bit-Timer und einen 16-Bit-Timer. Es gibt keine High-Level-API zum direkten Anschließen der ISR-Funktion an die Timer, die mit dem Arduino SDK (dh als Standardbibliothek) geliefert wird, sondern eine etwas niedrigere Methode zum Einstellen von Sonderfunktionsregistern und Bitarithmetik / Operationen auf sie. Es gibt jedoch eine vom Benutzer bereitgestellte Bibliothek namens Timer One .

icarus74
quelle
Es sind tatsächlich mehrere verschiedene Timerkombinationen möglich, je nachdem, auf welches Arduino Bezug genommen wird. Die Antwort ist irreführend.
Scheinbar so
@SeeminglySo, möchten Sie näher darauf eingehen? Wenn Sie über Arduino-Hardware sprechen, beachten Sie, dass die Antwort im Kontext der Frage und auch zu dem Zeitpunkt steht, zu dem die Frage gestellt wird.
icarus74
Das Arduino Mega (ATmega1280 basiert) wurde am 26. März 2009 veröffentlicht, und das Mega 2560 (ATmega2560) wurde am 24. September 2010 veröffentlicht, beide lange bevor diese Frage gestellt wurde. Beide Mikrocontroller haben mehr als den in der Antwort angegebenen 2x 8-Bit- und 1x 16-Bit-Timer / Zähler.
Scheinbar so
Die meisten Interaktionen, die ich bisher gesehen habe, beziehen sich eindeutig auf Arduino und bedeuten so etwas wie Duemilanove oder Uno, dh die auf der 328-Serie basierenden Boards. Andere Boards wurden von der uP-Serie Nr. oder Mega, Nano, Micro usw. Wie auch immer, ich werde die Korrektur demütig akzeptieren. In diesem Zusammenhang ist eine Klarstellung besser.
icarus74
1

Arduino verwendet alle drei Timer in ATMega328. Timer1(16 Bit) wird für Funktionen wie delay()und millis()und für die PWM-Ausgabe an den Pins 5 und 6 verwendet. Die anderen beiden Timer - Timer0und Timer2werden für die PWM-Ausgabe an den Pins 3, 9, 10, 11 verwendet.

Es gibt also keine Arduino-Funktion für Timer-Interrupts. Aber es gibt einen Weg. Mit diesem Code können Sie den Timer-Interrupt aktivieren für Timer2:

ISR(TIMER2_OVF_vect) {
  // Interrupt routine.
}

void setup() {
  // Enable Timer2 interrupt.
  TIMSK2 = (0<<OCIE2A) | (1<<TOIE2);
}

void loop() {
  // Your main loop.
}

Ich habe diesen Code ohne Test geschrieben, daher ist es möglich, dass ich einen Fehler gemacht habe. Überprüfen Sie in diesem Fall das Datenblatt, S.156 .

Wenn Sie die Timerfrequenz (Prescaler) ändern möchten, ändern Sie einfach das Register TCCR2A. Weitere Informationen finden Sie im Datenblatt auf Seite 153. Wenn Sie jedoch die Frequenz des Timers ändern, ändern Sie auch die Frequenz des PWM-Signals an zwei Ausgangspins!

vasco
quelle
AFAIK auf ATmega328 Timer0und Timer2sind 8 Bit und nur Timer116 Bit.
Tuupola
Nein, das ist nicht richtig. Es ist Timer0, nicht Timer1, der für delay () und millis () sowie für die PWM-Ausgabe an den Pins 5 und 6 verwendet wird. Timer0 ist ein 8-Bit-Timer. Siehe zB Arduino Timer und Interrupts .
Peter Mortensen