Arduino Timer Interrupts

Mit Timer-Interrupts können Sie eine Aufgabe in ganz bestimmten Zeitintervallen ausführen, unabhängig davon, was in Ihrem Code sonst noch vor sich geht. In dieser Anleitung erkläre ich, wie ein Interrupt im Clear Timer im Compare Match- oder CTC-Modus eingerichtet und ausgeführt wird. Fahren Sie direkt mit Schritt 2 fort, wenn Sie nach Beispielcode suchen.

Normalerweise führt das Arduino beim Schreiben einer Arduino-Skizze alle in der Funktion loop () {} gekapselten Befehle in der Reihenfolge aus, in der sie geschrieben wurden. Es ist jedoch schwierig, Ereignisse in der Schleife () zeitlich festzulegen. Einige Befehle dauern länger als andere, andere hängen von bedingten Anweisungen ab (if, while ...), und einige Funktionen der Arduino-Bibliothek (wie digitalWrite oder analogRead) bestehen aus vielen Befehlen. Mit Arduino-Timer-Interrupts können Sie die normale Abfolge von Ereignissen, die in der Funktion loop () stattfinden, in genau festgelegten Zeitintervallen kurz anhalten, während Sie einen separaten Befehlssatz ausführen. Sobald diese Befehle ausgeführt wurden, nimmt der Arduino wieder dort auf, wo er sich in der Schleife befand ().

Interrupts sind nützlich für:

Messung eines eingehenden Signals in gleichmäßigen Abständen (konstante Abtastfrequenz)

Berechnung der Zeit zwischen zwei Ereignissen

Senden eines Signals einer bestimmten Frequenz

Regelmäßige Überprüfung auf eingehende serielle Daten

viel mehr...

Es gibt einige Möglichkeiten, Interrupts durchzuführen. Im Moment werde ich mich auf den Typ konzentrieren, den ich am nützlichsten / flexibelsten finde, nämlich Timer im Vergleichsvergleichs- oder CTC-Modus löschen. Außerdem werde ich in dieser Anleitung speziell über die Timer des Arduino Uno (und jedes anderen Arduino mit ATMEL 328/168 ... Lilypad, Duemilanove, Diecimila, Nano ...) schreiben. Die hier vorgestellten Hauptideen gelten auch für Mega- und ältere Boards, aber das Setup ist etwas anders und die folgende Tabelle ist spezifisch für ATMEL 328/168.

Schritt 1: Prescaler und das Compare Match Register

Der Uno verfügt über drei Timer mit den Namen timer0, timer1 und timer2. Jeder der Timer hat einen Zähler, der bei jedem Tick der Timeruhr erhöht wird. CTC-Timer-Interrupts werden ausgelöst, wenn der Zähler einen bestimmten Wert erreicht, der im Vergleichsübereinstimmungsregister gespeichert ist. Sobald ein Timer-Zähler diesen Wert erreicht, wird er beim nächsten Tick der Timer-Uhr gelöscht (auf Null zurückgesetzt) ​​und zählt dann wieder bis zum Vergleichsübereinstimmungswert hoch. Durch Auswahl des Vergleichsübereinstimmungswerts und Einstellen der Geschwindigkeit, mit der der Timer den Zähler erhöht, können Sie die Häufigkeit von Timer-Interrupts steuern.

Der erste Parameter, den ich diskutieren werde, ist die Geschwindigkeit, mit der der Timer den Zähler erhöht. Der Arduino-Takt läuft mit 16 MHz. Dies ist die schnellste Geschwindigkeit, mit der die Timer ihre Zähler erhöhen können. Bei 16 MHz repräsentiert jeder Tick des Zählers 1 / 16.000.000 Sekunden (~ 63 ns), sodass ein Zähler 10 / 16.000.000 Sekunden benötigt, um einen Wert von 9 zu erreichen (Zähler sind 0 indiziert), und 100 / 16.000.000 Sekunden, um einen Wert zu erreichen von 99.

In vielen Situationen werden Sie feststellen, dass das Einstellen der Zählergeschwindigkeit auf 16 MHz zu schnell ist. Timer0 und Timer2 sind 8-Bit-Timer, was bedeutet, dass sie einen maximalen Zählerwert von 255 speichern können. Timer1 ist ein 16-Bit-Timer, was bedeutet, dass er einen maximalen Zählerwert von 65535 speichern kann. Sobald ein Zähler sein Maximum erreicht, wird er auf Null zurückgesetzt (Dies wird als Überlauf bezeichnet). Dies bedeutet, dass bei 16 MHz, selbst wenn wir das Vergleichsübereinstimmungsregister auf den maximalen Zählerwert setzen, Interrupts alle 256 / 16.000.000 Sekunden (~ 16us) für die 8-Bit-Zähler und alle 65.536 / 16.000.000 (~ 4 ms) Sekunden für den auftreten 16 Bit Zähler. Dies ist natürlich nicht sehr nützlich, wenn Sie nur einmal pro Sekunde unterbrechen möchten.

Stattdessen können Sie die Geschwindigkeit der Inkrementierung des Timer-Zählers mithilfe eines sogenannten Prescaler steuern. Ein Vorteiler bestimmt die Geschwindigkeit Ihres Timers gemäß der folgenden Gleichung:

(Timer-Geschwindigkeit (Hz)) = (Arduino-Taktrate (16 MHz)) / Prescaler

Ein 1-Vorteiler erhöht also den Zähler bei 16 MHz, ein 8-Vorteiler erhöht ihn bei 2 MHz, ein 64-Vorteiler = 250 kHz und so weiter. Wie in den obigen Tabellen angegeben, kann der Vorteiler gleich 1, 8, 64, 256 und 1024 sein. (Ich werde die Bedeutung von CS12, CS11 und CS10 im nächsten Schritt erläutern.)

Jetzt können Sie die Interruptfrequenz mit der folgenden Gleichung berechnen:

Interruptfrequenz (Hz) = (Arduino-Taktrate 16.000.000 Hz) / (Vorteiler * (vergleiche Matchregister + 1))
Die +1 ist da drin, weil das Vergleichsübereinstimmungsregister mit Null indiziert ist

Wenn Sie die obige Gleichung neu anordnen, können Sie nach dem Vergleichsübereinstimmungsregisterwert suchen, der Ihre gewünschte Interruptfrequenz ergibt:

Vergleichsregister vergleichen = [16.000.000 Hz / (Vorteiler * gewünschte Interruptfrequenz)] - 1
Denken Sie daran, dass bei Verwendung der Timer 0 und 2 diese Zahl kleiner als 256 und für Timer1 kleiner als 65536 sein muss

Wenn Sie also jede Sekunde einen Interrupt wünschen (Frequenz von 1 Hz):
Vergleichsregister vergleichen = [16.000.000 / (Vorteiler * 1)] -1
Mit einem Prescaler von 1024 erhalten Sie:
Vergleichsregister vergleichen = [16.000.000 / (1024 * 1)] -1
= 15.624
da 256 <15.624 <65.536, müssen Sie timer1 für diesen Interrupt verwenden.

Schritt 2: Strukturieren von Timer-Interrupts


Der Timer-Setup-Code wird in der Funktion setup () {} in einer Arduino-Skizze ausgeführt.

Der Code zum Einrichten von Timer-Interrupts ist ein wenig entmutigend, aber eigentlich nicht so schwer. Ich kopiere so ziemlich nur den gleichen Hauptcodeblock und ändere den Vorteiler und vergleiche das Übereinstimmungsregister, um die richtige Interruptfrequenz einzustellen.

Die Hauptstruktur des Interrupt-Setups sieht folgendermaßen aus:
 ////www.instructables.com/id/Arduino-Timer-Interrupts/ void setup () cli (); // Interrupts stoppen // Timer0-Interrupt auf 2 kHz setzen TCCR0A = 0; // gesamtes TCCR0A-Register auf 0 TCCR0B setzen = 0; // gleich für TCCR0B TCNT0 = 0; // Zählerwert auf 0 initialisieren // Vergleichsübereinstimmungsregister für 2-kHz-Inkremente setzen OCR0A = 124; // = (16 * 10 ^ 6) / (2000 * 64) - 1 (muss <256 sein) // CTC-Modus TCCR0A einschalten // Setup beenden 
Beachten Sie, wie sich der Wert von OCR # A (der Vergleichsübereinstimmungswert) für jede dieser Timer-Einstellungen ändert. Wie im letzten Schritt erläutert, wurde dies nach folgender Gleichung berechnet:

Vergleichsregister vergleichen = [16.000.000 Hz / (Vorteiler * gewünschte Interruptfrequenz)] - 1
Denken Sie daran, dass bei Verwendung der Timer 0 und 2 diese Zahl kleiner als 256 und für Timer1 kleiner als 65536 sein muss

Beachten Sie auch, wie sich die Einstellungen zwischen den drei Timern in der Zeile, in der der CTC-Modus aktiviert ist, geringfügig unterscheiden:
TCCR0A | = (1 << WGM01); // für timer0
TCCR1B | = (1 << WGM12); // für Timer1
TCCR2A | = (1 << WGM21); // für Timer2
Dies folgt direkt aus dem Datenblatt des ATMEL 328/168.

Beachten Sie abschließend, wie das Setup für die Vorskalierer den Tabellen im letzten Schritt folgt (die Tabelle für Timer 0 wird oben wiederholt).
TCCR2B | = (1 << CS22); // CS # 2 Bit für 64 Prescaler für Timer 2 setzen
TCCR1B | = (1 << CS11); // Setze CS # 1 Bit für 8 Prescaler für Timer 1
TCCR0B | = (1 << CS02) | (1 << CS00); // Setze CS # 2 und CS # 0 Bits für 1024 Prescaler für Timer 0

Beachten Sie im letzten Schritt, dass es für die verschiedenen Timer unterschiedliche Vorskalierungsoptionen gibt. Zum Beispiel hat timer2 nicht die Option eines 1024-Vorteilers.

Die Befehle, die Sie während dieser Timer-Interrupts ausführen möchten, befinden sich in der folgenden Arduino-Skizze:
ISR (TIMER0_COMPA_vect) {// ändere die 0 für Timer1 auf 1 und für Timer2 auf 2
// Befehle hier unterbrechen
}}
Dieses Codebit sollte sich außerhalb der Funktionen setup () und loop () befinden. Versuchen Sie auch, die Interrupt-Routine so kurz wie möglich zu halten, insbesondere wenn Sie mit hoher Frequenz unterbrechen. Es kann sogar sinnvoll sein, die Ports / Pins des ATMEL-Chips direkt zu adressieren, anstatt die Funktionen digitalWrite () und digitalRead () zu verwenden. Weitere Informationen dazu finden Sie hier.

Beispiel: In der folgenden Skizze werden 3 Timer-Interrupts eingerichtet und ausgeführt:

 // Timer-Interrupts // von Amanda Ghassaei // Juni 2012 ////www.instructables.com/id/Arduino-Timer-Interrupts/ / * * Dieses Programm ist freie Software; Sie können es unter den Bedingungen der GNU General Public License, wie sie von der Free Software Foundation veröffentlicht wurde, weitergeben und / oder ändern. entweder Version 3 der Lizenz oder * (nach Ihrer Wahl) eine spätere Version. * * / // Timer-Setup für Timer0, Timer1 und Timer2. // Für Arduino Uno oder jedes Board mit ATMEL 328/168 .. Diecimila, Duemilanove, Lilypad, Nano, Mini ... // Dieser Code aktiviert alle drei Arduino Timer Interrupts. // timer0 unterbricht bei 2 kHz // timer1 unterbricht bei 1 Hz // timer2 unterbricht bei 8 kHz // Speichervariablen boolean toggle0 = 0; boolean toggle1 = 0; boolean toggle2 = 0; void setup () // setze Pins als Ausgänge pinMode (8, OUTPUT); PinMode (9, OUTPUT); PinMode (13, OUTPUT); cli (); // Interrupts stoppen // Timer0-Interrupt auf 2 kHz setzen TCCR0A = 0; // gesamtes TCCR2A-Register auf 0 setzen TCCR0B = 0; // gleich für TCCR2B TCNT0 = 0; // Zählerwert auf 0 initialisieren // setzen Vergleichsregister für 2-kHz-Inkremente vergleichen OCR0A = 124; // = (16 * 10 ^ 6) / (2000 * 64) - 1 (muss <256 sein) // CTC-Modus einschalten TCCR0A // Setup beenden ISR (TIMER0_COMPA_vect) { // Timer0 Interrupt 2kHz schaltet Pin 8 um // erzeugt eine Pulswelle mit einer Frequenz von 2kHz / 2 = 1kHz (dauert zwei Zyklen, um die volle Welle hoch und dann niedrig zu schalten) if (toggle0) {digitalWrite (8, HIGH); toggle0 = 0; } else {digitalWrite (8, LOW); toggle0 = 1; }} ISR (TIMER1_COMPA_vect) {// Timer1-Interrupt 1 Hz schaltet Pin 13 (LED) um // erzeugt eine Pulswelle mit einer Frequenz von 1 Hz / 2 = 0, 5 kHz (dauert zwei Zyklen, um die volle Welle hoch und dann niedrig umzuschalten), wenn (umschaltet1) digitalWrite (13, HIGH); toggle1 = 0; } else {digitalWrite (13, LOW); toggle1 = 1; }} ISR (TIMER2_COMPA_vect) {// Timer1-Interrupt 8 kHz schaltet Pin 9 um // erzeugt eine Pulswelle mit einer Frequenz von 8 kHz / 2 = 4 kHz (dauert zwei Zyklen, um die volle Welle hoch und dann niedrig zu schalten) if (toggle2) {digitalWrite (9, HOCH); toggle2 = 0; } else {digitalWrite (9, LOW); toggle2 = 1; }} void loop () {// hier andere Dinge tun} 

Die obigen Bilder zeigen die Ausgänge dieser Timer-Interrupts. Fig. 1 zeigt eine Rechteckwelle, die bei 1 kHz zwischen 0 und 5 V oszilliert (Timer0-Interrupt), Fig. 2 zeigt die an Pin 13 angebrachte LED, die sich für eine Sekunde einschaltet und dann für eine Sekunde ausschaltet (Timer1-Interrupt), Fig. 3 zeigt eine Pulswellenoszillation zwischen 0 und 5 V bei einer Frequenz von 4 kHz (Timer2-Interrupt).

Schritt 3: Beispiel 1: Fahrrad-Tachometer

In diesem Beispiel habe ich einen Fahrrad-Tacho mit Arduino-Antrieb hergestellt. Es funktioniert, indem ein Magnet am Rad angebracht und die Zeit gemessen wird, die benötigt wird, um an einem am Rahmen angebrachten Magnetschalter vorbeizukommen - die Zeit für eine vollständige Umdrehung des Rads.

Ich stelle Timer 1 so ein, dass er alle ms (Frequenz von 1 kHz) unterbricht, um den Magnetschalter zu messen. Wenn der Magnet am Schalter vorbeigeht, ist das Signal vom Schalter hoch und die Variable "Zeit" wird auf Null gesetzt. Befindet sich der Magnet nicht in der Nähe des Schalters, wird "Zeit" um 1 erhöht. Auf diese Weise ist "Zeit" eigentlich nur ein Maß für die Zeit in Millisekunden, die seit dem letzten Durchgang des Magneten durch den Magnetschalter vergangen ist. Diese Informationen werden später im Code verwendet, um die Drehzahl und die Geschwindigkeit des Fahrrads zu berechnen.

Hier ist der Code, der timer1 für 1-kHz-Interrupts einrichtet

cli (); // Interrupts stoppen
// Timer1 Interrupt auf 1kHz setzen
TCCR1A = 0; // setze das gesamte TCCR1A-Register auf 0
TCCR1B = 0; // dasselbe für TCCR1B
TCNT1 = 0; // Zählerwert auf 0 initialisieren
// Timeranzahl für 1-kHz-Schritte einstellen
OCR1A = 1999; // = (16 · 10 & supmin; & sup6;) / (1000 · 8) - 1
// musste 16 bit timer1 für diesen bc 1999> 255 verwenden, konnte aber mit größerem prescaler auf timer 0 oder 2 umschalten
// CTC-Modus einschalten
TCCR1B | = (1 << WGM12);
// CS11-Bit für 8 Prescaler setzen
TCCR1B | = (1 << CS11);
// Timer Compare Interrupt aktivieren
TIMSK1 | = (1 << OCIE1A);
sei (); // Interrupts zulassen

Hier ist der vollständige Code, wenn Sie einen Blick darauf werfen möchten:
 // Fahrradtachometer // von Amanda Ghassaei 2012 ////www.instructables.com/id/Arduino-Timer-Interrupts/ ////www.instructables.com/id/Arduino-Timer-Interrupts/ / * * Dies Programm ist freie Software; Sie können es unter den Bedingungen der GNU General Public License, wie sie von der Free Software Foundation veröffentlicht wurde, weitergeben und / oder ändern. entweder Version 3 der Lizenz oder * (nach Ihrer Wahl) eine spätere Version. * * / // Beispielberechnungen // Reifenradius ~ 13, 5 Zoll // Umfang = pi * 2 * r = ~ 85 Zoll // Höchstgeschwindigkeit von 35 km / h = ~ 616 Zoll / Sekunde // Höchstgeschwindigkeit = ~ 7, 25 # Schilf A0 definieren / / Pin mit Leseschalter verbunden // Speichervariablen float radius = 13.5; // Reifenradius (in Zoll) - ÄNDERN SIE DIESES FÜR IHR EIGENES FAHRRAD int reedVal; lange Zeit = 0; // Zeit zwischen einer vollen Umdrehung (in ms) float mph = 0, 00; Schwimmerumfang; boolesche Hintergrundbeleuchtung; int maxReedCounter = 100; // min Zeit (in ms) einer Umdrehung (zum Entprellen) int reedCounter; void setup () {reedCounter = maxReedCounter; Umfang = 2 * 3, 14 * Radius; PinMode (1, OUTPUT); // tx PinMode (2, OUTPUT); // Schalter für Hintergrundbeleuchtung PinMode (Reed, INPUT); // Redd Switch CheckBacklight (); Serial.write (12); // clear // TIMER SETUP- Der Timer-Interrupt ermöglicht präzise zeitgesteuerte Messungen des Reed-Schalters // Weitere Informationen zur Konfiguration von Arduino-Timern finden Sie unter //arduino.cc/playground/Code/Timer1 cli ( ); // Interrupts stoppen // Timer1-Interrupt auf 1 kHz setzen TCCR1A = 0; // gesamtes TCCR1A-Register auf 0 setzen TCCR1B = 0; // Gleiches gilt für TCCR1B TCNT1 = 0; // Zählerwert auf 0 initialisieren; // Timer-Anzahl für 1-kHz-Inkremente einstellen OCR1A = 1999; // = (16 * 10 ^ 6) / (1000 * 8) - 1 // CTC-Modus einschalten TCCR1B | = (1 << WGM12); // CS11-Bit für 8 Prescaler setzen TCCR1B | = (1 << CS11); // Timer-Vergleichsinterrupt aktivieren TIMSK1 | = (1 <0) {// reedCounter nicht negativ werden lassen reedCounter - = 1; // reedCounter dekrementieren}}} else {// wenn der Reed-Schalter geöffnet ist, wenn (reedCounter> 0 ) {// reedCounter nicht negativ werden lassen reedCounter - = 1; // reedCounter dekrementieren}} if (Zeit> 2000) {mph = 0; // Wenn noch keine neuen Impulse vom Reed-Switch-Reifen vorhanden sind, setzen Sie mph auf 0} else {time + = 1; // Timer erhöhen}} void displayMPH () {Serial.write (12); // clear Serial.write ("Speed ​​="); Serial.write (13); // eine neue Zeile starten Serial.print (mph); Serial.write ("MPH"); //Serial.write("0.00 MPH "); } void loop () {// drucke mph einmal pro Sekunde displayMPH (); Verzögerung (1000); checkBacklight (); }} 

Schritt 4: Beispiel 2: Serielle Kommunikation

Dieses Projekt ist ein 4x4-Tastenfeld mit Hintergrundbeleuchtung. Das Projekt stellt über USB eine Verbindung zu meinem Computer her, sendet Informationen zu den Tasten an den Computer und empfängt Informationen zum Aufleuchten der LEDs. Hier ist ein Video:



Für dieses Projekt habe ich timer2-Interrupts verwendet, um regelmäßig zu überprüfen, ob serielle Daten eingehen, diese zu lesen und in der Matrix "ledData []" zu speichern. Wenn Sie sich den Code ansehen, werden Sie sehen, dass die Hauptschleife der Skizze tatsächlich dafür verantwortlich ist, die Informationen in ledData zu verwenden, um die richtigen LEDs zu beleuchten und den Status der Tasten zu überprüfen (eine Funktion namens "shift" ( ) "). Die Interrupt-Routine ist so kurz wie möglich - sie prüft nur auf eingehende Bytes und speichert sie entsprechend.

Hier ist das Setup für timer2:

cli (); // Interrupts stoppen
// setze timer2 Interrupt alle 128us
TCCR2A = 0; // setze das gesamte TCCR2A-Register auf 0
TCCR2B = 0; // dasselbe für TCCR2B
TCNT2 = 0; // Zählerwert auf 0 initialisieren
// Vergleichsübereinstimmungsregister für 7, 8-kHz-Inkremente setzen
OCR2A = 255; // = (16 · 10 & supmin; & sup6;) / (7812, 5 · 8) - 1 (muss <256 sein)
// CTC-Modus einschalten
TCCR2A | = (1 << WGM21);
// CS21-Bit für 8 Prescaler setzen
TCCR2B | = (1 << CS21);
// Timer Compare Interrupt aktivieren
TIMSK2 | = (1 << OCIE2A);
sei (); // Interrupts zulassen

Hier ist die komplette Arduino-Skizze:
 // TASTENTEST mit 74HC595 und 74HC165 und serieller Kommunikation // von Amanda Ghassaei // Juni 2012 ////www.instructables.com/id/Arduino-Timer-Interrupts/ / * * Dieses Programm ist freie Software; Sie können es unter den Bedingungen der GNU General Public License, wie sie von der Free Software Foundation veröffentlicht wurde, weitergeben und / oder ändern. entweder Version 2 der Lizenz oder * (nach Ihrer Wahl) eine spätere Version. * * / // Diese Firmware sendet Daten mit dem maxmsp-Patch "Beat Slicer" hin und her. // Pin-Verbindungen #define ledLatchPin A1 #define ledClockPin A0 #define ledDataPin A2 #define buttonLatchPin 9 #define buttonClockPin 10 #define buttonDataPin A3 / / Schleifenvariablen Byte i; Byte j; Byte k; Byte ledByte; // Speicher für LED-Zustände, 4 Byte Byte ledData [] = {0, 0, 0, 0}; // Speicher für Schaltflächen, 4 Byte Byte buttonCurrent [] = {0, 0, 0, 0}; Byte buttonLast [] = {0, 0, 0, 0}; Byte buttonEvent [] = {0, 0, 0, 0}; Byte buttonState [] = {0, 0, 0, 0}; // Schaltflächenentprellungszähler - 16 Bytes Byte buttonDebounceCounter [4] [4]; void setup () = (1 << WGM21); // CS21-Bit für 8 Prescaler TCCR2B setzen // buttonCheck - Überprüft den Status einer bestimmten Taste. // Diese Buttoncheck-Funktion wird größtenteils von Brian Crabtree und Joe Lake Void ButtonCheck (Byte-Zeile, Byte-Index) {if (((buttonCurrent [Zeile] ^ buttonLast [Zeile]) & (1 << Index)) aus der Monome 40h-Firmware kopiert. ) && // wenn sich der aktuelle physische Schaltflächenstatus vom ((buttonCurrent [row] ^ buttonState [row]) & (1 << index)) {// letzten physischen Schaltflächenstatus UND dem aktuellen entprellen Zustand, wenn (buttonCurrent [Zeile] & (1 << Index)) // wenn der aktuelle physische Tastenstatus gedrückt wird buttonEvent [Zeile] = 1 << Index; // ein neues Schaltflächenereignis sofort in die Warteschlange stellen buttonState [row] else {buttonDebounceCounter [row] [index] = 12; } // ansonsten war die Taste zuvor gedrückt und jetzt wurde // losgelassen, also setzen wir unseren Entprellungszähler. } else if (((buttonCurrent [Zeile] ^ buttonLast [Zeile]) & (1 << Index)) == 0 && // wenn der aktuelle physische Schaltflächenstatus mit (buttonCurrent [Zeile] ^ buttonState [Zeile] identisch ist ) & (1 <0 && --buttonDebounceCounter [row] [index] == 0) {// wenn der Entprellungszähler // auf 0 dekrementiert wurde (was bedeutet, dass // die Schaltfläche für // kButtonUpDefaultDebounceCount / aktiv war) / iterations /// buttonEvent [row] = 1 << index; // ein Ereignis zur Änderung des Schaltflächenstatus in die Warteschlange stellen, wenn (buttonCurrent [row] & (1 << index)) = (1 << index); else {buttonState [ Zeile] & = ~ (1 << Index);}}}} void shift () {für (i = 0; i <4; i ++) {buttonLast [i] = buttonCurrent [i]; Byte dataToSend = (1 < > 3; // Latchpin Low DigitalWrite (buttonLatchPin, LOW); für (k = 0; k <4; k ++) {buttonCheck (i, k); if (buttonEvent [i] <  1) & 3; Byte ledx = (ledByte >> 3) & 3; if (ledstate) = 8 >> ledx; sonst {ledData [ledy] & = ~ (8 >> ledx); }} // end if serial available} // end do while (Serial.available ()> 8); } void loop () {shift (); // aktualisiert LEDs und empfängt Daten von Schaltflächen} 

Laden Sie den folgenden MaxMSP-Patch herunter (er wird auch in Max Runtime ausgeführt).

Anhänge

  • beat slicer.zip herunterladen

Schritt 5: Beispiel 3: DAC

In diesem Projekt habe ich einen Timer-Interrupt verwendet, um eine Sinuswelle einer bestimmten Frequenz vom Arduino auszugeben. Ich habe einen einfachen 8-Bit-R2R-DAC an die digitalen Pins 0-7 gelötet. Dieser DAC wurde aus 10k- und 20k-Widerständen aufgebaut, die in einem mehrstufigen Spannungsteiler angeordnet sind. Ich werde mehr über den Aufbau des DAC in einer anderen Anleitung veröffentlichen. Im Moment habe ich die obigen Fotos beigefügt.
Ich habe den Ausgang des DAC an ein Oszilloskop angeschlossen. Wenn Sie Hilfe zum Verwenden / Lesen des Oszilloskops benötigen, lesen Sie dieses Tutorial. Ich habe den folgenden Code auf das Arduino geladen:
 // 63Hz Sinuswelle // von Amanda Ghassaei 2012 ////www.instructables.com/id/Arduino-Timer-Interrupts/ / * * Dieses Programm ist freie Software; Sie können es unter den Bedingungen der GNU General Public License, wie sie von der Free Software Foundation veröffentlicht wurde, weitergeben und / oder ändern. entweder Version 3 der Lizenz oder * (nach Ihrer Wahl) eine spätere Version. * * / // sendet eine 63-Hz-Sinuswelle an das Arduino PORTD DAC float t = 0; void setup () = (1 << CS21); // Timer-Vergleichsinterrupt aktivieren TIMSK2 ISR (TIMER2_COMPA_vect) {// Inkrementiere t t + = 1; if (t == 628) {// 40 kHz / 628 = ~ 63 Hz t = 0; }} void loop () {// Sinuswelle der Frequenz ~ 63Hz // Sinuswerte zwischen 0 und 255 an PORTD senden PORTD = Byte (127 + 127 * sin (t / 100)); }} 
Ich habe einen Timer-Interrupt eingerichtet, der die Variable t mit einer Frequenz von 40 kHz erhöht. Sobald t 627 erreicht, wird es auf Null zurückgesetzt (dies geschieht mit einer Frequenz von 40.000 / 628 = 63 Hz). Währenddessen sendet der Arduino in der Hauptschleife einen Wert zwischen 0 (00000000 in Binär) und 255 (11111111 in Binär) an die digitalen Pins 0 bis 7 (PORTD). Dieser Wert wird mit folgender Gleichung berechnet:

PORTD = Byte (127 + 127 * sin (t / 100));

Wenn t von 0 auf 627 erhöht wird, durchläuft die Sinusfunktion einen vollständigen Zyklus. Der an PORTD gesendete Wert ist eine Sinuswelle mit einer Frequenz von 63 Hz und einer Amplitude von 127, die um 127 schwingt. Wenn diese über den 8-Bit-Widerstandsleiter DAC gesendet wird, gibt sie ein oszillierendes Signal um 2, 5 V mit einer Amplitude von 2, 5 V und einer Frequenz von 63 Hz aus.

Die Frequenz der Sinuswelle kann durch Multiplizieren des (t / 100) -Terms mit 2 verdoppelt, durch Multiplizieren mit 4 vervierfacht werden ...
Beachten Sie auch, dass die Sinuswelle nicht korrekt ausgegeben wird, wenn Sie die Frequenz des Timer-Interrupts durch Verringern des Vorteilers oder von OCR2A zu stark erhöhen. Dies liegt daran, dass die sin () - Funktion rechenintensiv ist und bei hohen Interruptfrequenzen nicht genügend Zeit zur Ausführung hat. Wenn Sie Hochfrequenz-Interrupts verwenden, anstatt während der Interrupt-Routine eine Berechnung durchzuführen, sollten Sie in Betracht ziehen, Werte in einem Array zu speichern und diese Werte einfach mit einem Index aufzurufen. Ein Beispiel dafür finden Sie in meinem Arduino-Wellenformgenerator. Durch Speichern von 20.000 Sin-Werten in einem Array konnte ich Sinuswellen mit einer Abtastfrequenz von 100 kHz ausgeben.

Schritt 6: Timer- und Arduino-Funktionen

Eine letzte Sache, die Sie beachten sollten: Bestimmte Timer-Setups deaktivieren tatsächlich einige der Funktionen der Arduino-Bibliothek. Timer0 wird von den Funktionen millis () und delay () verwendet. Wenn Sie timer0 manuell einrichten, funktionieren diese Funktionen nicht richtig.
Zusätzlich übernehmen alle drei Timer die Funktion analogWrite (). Durch manuelles Einrichten eines Timers funktioniert analogWrite () nicht mehr.

Wenn es einen Teil Ihres Codes gibt, den Sie nicht unterbrechen möchten, sollten Sie cli () und sei () verwenden, um Interrupts global zu deaktivieren und zu aktivieren.

Weitere Informationen hierzu finden Sie auf der Arduino-Website.

Ähnlicher Artikel