Arduino-Multitasking - Teil eins

Größere und bessere Designs

Sobald Sie die grundlegenden blinkenden LEDs , einfachen Sensoren und Servos beherrschen, können Sie mit größeren und besseren Projekten beginnen. Normalerweise beinhaltet dies, Teile einfacherer Skizzen zusammenzufügen und zu versuchen, sie zusammenzuarbeiten. Das erste, was Sie feststellen werden, ist, dass einige dieser Skizzen, die für sich alleine perfekt funktionieren, nicht mit anderen übereinstimmen.

Arduino ist ein sehr einfaches Modul ohne Betriebssystem, das nur ein Programm ausführen kann. Im Gegensatz zu Ihrem PC oder Raspberry Pi kann Arduino nicht mehrere Programme laden und ausführen.

Das bedeutet nicht, dass wir nicht viele Aufgaben in Arduino erledigen können. Wir müssen nur eine andere Methode anwenden. Wir haben kein Betriebssystem, um zu helfen, also müssen wir es selbst in die Hand nehmen.

Arduino-Multitasking

Verzögerung beseitigen ()

Das erste, was Sie beim Spielen mit Arduino gelernt haben, ist wahrscheinlich, die obige Verzögerungsfunktion zu verwenden. Die Funktion delay () ist nicht kompliziert, aber sie gerät in Schwierigkeiten, wenn zusätzliche Funktionen hinzugefügt werden. Das Problem ist, dass diese Funktion das " besetzte Warten " ist, das die CPU dominiert.

Beim Aufruf dieser Funktion können Sie nicht auf Eingaben reagieren, keine Daten verarbeiten und keine Ausgaben verändern. Es blockiert den gesamten Prozessor. Wenn also ein Teil Ihres Codes diese Funktion verwendet, wird während dieser Zeit alles andere angehalten.

Erinnerst du dich an Blink?

 / *
Blinken
Schaltet eine LED wiederholt für eine Sekunde ein und dann für eine Sekunde aus.

Dieser Beispielcode ist gemeinfrei.
* /

// An Pin 13 ist auf den meisten Arduino-Boards eine LED angeschlossen.
// gib ihm einen Namen:
int led = 13;

// Die Setup-Routine wird einmal ausgeführt, wenn Sie Reset drücken:
void setup () {
// den digitalen Pin als Ausgang initialisieren.
PinMode (LED, AUSGANG);
}

// die Schleifenroutine läuft immer und immer wieder:
Leere Schleife () {
DigitalWrite (LED, HIGH); // LED einschalten (HIGH ist der Spannungspegel)
Verzögerung (1000); // warte mal eine Sekunde
DigitalWrite (LED, LOW); // Schalten Sie die LED aus, indem Sie die Spannung LOW machen
Verzögerung (1000); // warte mal eine Sekunde
}

Ein einfacher Blink-Sketch steckt fast immer in der delay()-Funktion. Während dieser Zeit kann die CPU also nichts anderes tun.

Hast du deinen Sweep vergessen?

Sweep verwendet die Funktion delay (), um seine Geschwindigkeit zu steuern. Wenn Sie versuchen, eine einfache Blinkskizze mit einem Beispiel eines Sweep -Servos zu kombinieren, werden Sie feststellen, dass die LED abwechselnd blinkt und sich das Servo bewegt. Dies wird jedoch nicht gleichzeitig geschehen.

 #include 

// An Pin 13 ist auf den meisten Arduino-Boards eine LED angeschlossen.
// gib ihm einen Namen:
int led = 13;

Servo myservo; // Servoobjekt erstellen, um ein Servo zu steuern
// Zwölf Servoobjekte können auf den meisten Boards erstellt werden

intpos = 0; // Variable zum Speichern der Servoposition

Void-Setup ()
{
// den digitalen Pin als Ausgang initialisieren.
PinMode (LED, AUSGANG);
meinservo.attach (9); // verbindet das Servo an Pin 9 mit dem Servoobjekt
}

leere Schleife ()
{
DigitalWrite (LED, HIGH); // LED einschalten (HIGH ist der Spannungspegel)
Verzögerung (1000); // warte mal eine Sekunde
DigitalWrite (LED, LOW); // Schalten Sie die LED aus, indem Sie die Spannung LOW machen
Verzögerung (1000); // warte mal eine Sekunde

for (pos = 0; pos <= 180; pos + = 1) // geht von 0 Grad auf 180 Grad
{// in 1-Grad-Schritten
myservo.write (pos); // Servo anweisen, in die Position in Variable 'pos' zu gehen
Verzögerung (15); // wartet 15 ms, bis das Servo die Position erreicht
}
for (pos = 180; pos> = 0; pos - = 1) // geht von 180 Grad auf 0 Grad
{
myservo.write (pos); // Servo anweisen, in die Position in Variable 'pos' zu gehen
Verzögerung (15); // wartet 15ms auf den Servo

Wie überprüfen wir also die Verzögerung, ohne die Verzögerungsfunktion zu verwenden?

Verwenden Sie die Funktion millis () in Verzögerung

Eine einfache Technik, um eine Verzögerung zu implementieren, besteht darin, einen Plan zu erstellen und auf die Uhr zu achten. Anstatt sich auf die Verzögerungsfunktion zu konzentrieren, überprüfen Sie einfach systematisch die Zeit, um zu wissen, wann Sie handeln müssen. In der Zwischenzeit können andere Aufgaben die CPU verwenden. Ein sehr einfaches Beispiel dafür ist ein BlinkyWithoutDelay- Sketch mit der Entwicklungsumgebung.

Das Anschlussdiagramm für den obigen Code ist unten dargestellt:

Arduino Multitasking - Anschlussplan

Flimmern ohne Verzögerung

Hier ist ein Beispielcode für BlinkWithoutDelay:

 / * Blinzeln ohne Verzögerung

Schaltet eine Leuchtdiode (LED) ein und aus, die mit einem digitalen verbunden ist
pin, ohne die Funktion delay () zu verwenden. Dies bedeutet, dass anderer Code
gleichzeitig laufen können, ohne durch den LED-Code unterbrochen zu werden.

Die Rennbahn:
* LED von Pin 13 an Masse angeschlossen.
* Hinweis: Bei den meisten Arduinos befindet sich bereits eine LED auf der Platine
Das ist an Pin 13 angeschlossen, daher wird für dieses Beispiel keine Hardware benötigt.


2005 erstellt
von David A. Mellis
geändert am 8. Februar 2010
von Paul Stoffregen

Dieser Beispielcode ist gemeinfrei.


http://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
* /

// Konstanten ändern sich nicht. Hier verwendet, um
// Pin-Nummern setzen:
const int ledPin = 13; // die Nummer des LED-Pins

// Variablen ändern sich:
int ledState = NIEDRIG; // ledState zum Setzen der LED
lange vorherigeMillis = 0; // speichert das letzte Mal, als die LED aktualisiert wurde

// Die folgenden Variablen sind lang, weil die Zeit, gemessen in Millisekunden,
// wird schnell zu einer größeren Zahl, als in einem int gespeichert werden kann.
langes Intervall = 1000; // Blinkintervall (Millisekunden)

void setup () {
// setze den digitalen Pin als Ausgang:
PinMode (ledPin, AUSGANG);
}

leere Schleife ()
{
// Hier würden Sie Code einfügen, der die ganze Zeit ausgeführt werden muss.

// prüfen, ob es Zeit ist, die LED zu blinken; das heißt, wenn die
// Unterschied zwischen der aktuellen Zeit und dem letzten Mal, als Sie geblinzelt haben
// Die LED ist größer als das gewünschte Intervall
// LED blinken.
unsigned long currentMillis = millis ();

if (aktuelleMillis - vorherigeMillis> Intervall) {
// Speichern Sie das letzte Mal, als Sie die LED geblinkt haben
vorherigeMillis = aktuelleMillis;

// Wenn die LED aus ist, schalten Sie sie ein und umgekehrt:
if (ledState == NIEDRIG)
ledState = HOCH;
anders
ledState = NIEDRIG;

// setze die LED mit dem ledState der Variablen:
digitalWrite (ledPin, ledState);
}
}

Macht es irgendeinen Sinn?

Auf den ersten Blick erscheint BlinkWithoutDelay nicht sehr interessant. Es sieht nach einer komplizierteren Möglichkeit aus, die LEDs einzuschalten . BlinkWithoutDelay präsentiert jedoch ein sehr wichtiges Konzept, das als State Machine bekannt ist.

Sie müssen sich nicht mehr auf die Funktion delay () verlassen, um ein blinkendes LED-Programm zu erstellen. BlinkWithoutDelay merkt sich den aktuellen LED-Status und wann er sich zuletzt geändert hat. Nach jeder Schleife überprüft er die millis()-Clock, um zu sehen, ob er den Status der LEDs erneut ändern sollte.

Willkommen in der Maschine

Schauen wir uns eine etwas interessantere Variante von blink an, die unterschiedliche Ein- und Ausschaltzeiten hat. Wir nannten es "FlashWithoutDelay"

 // Diese Variablen speichern das Blinkmuster
// und der aktuelle Zustand der LED

int ledPin = 13; // die Nummer des LED-Pins
int ledState = NIEDRIG; // ledState zum Setzen der LED
unsigned long previousMillis = 0; // speichert das letzte Mal, als die LED aktualisiert wurde
lange OnTime = 250; // Millisekunden Einschaltzeit
lange OffTime = 750; // Millisekunden Auszeit

Void-Setup ()
{
// setze den digitalen Pin als Ausgang:
PinMode (ledPin, AUSGANG);
}

leere Schleife ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState == HIGH) && (currentMillis - previousMillis> = OnTime))
{
ledState = NIEDRIG; // Schalte es aus
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis> = OffTime))
{
ledState = HOCH; // Mach es an
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
}

Status + Maschine = Zustandsmaschine

Beachten Sie, dass wir Variablen haben, die verfolgen, ob die LED ein- oder ausgeschaltet ist. Es gibt auch diejenigen, die die letzte Veränderung beobachten, die stattgefunden hat. Es ist Teil der Maschine namens Status .

Wir haben auch einen Code, der den Status überwacht und entscheidet, wann und wie er geändert werden muss. Dies ist der Teil, der Maschine genannt wird. Nach jeder Schleife "starten wir die Maschine" und sie aktualisiert den Status.

Als nächstes prüfen wir, wie Sie die Maschinen verbinden, indem Sie sie gleichzeitig aktivieren.

Ziel - zwei auf einmal

Es ist Zeit für Multitasking! Schließen Sie zuerst eine weitere LED an, wie im folgenden Diagramm gezeigt:

Arduino Multitasking - Diagramm zum Anschließen der LEDs

Dann erstellen wir einen weiteren Spielautomaten für die zweite LED, die mit völlig anderen Geschwindigkeiten blinkt. Wenn Sie zwei separate Maschinen verwenden, blinken diese beiden LEDs, ohne voneinander abhängig zu sein.

 // Diese Variablen speichern das Blinkmuster
// und der aktuelle Zustand der LED

int ledPin1 = 12; // die Nummer des LED-Pins
int ledState1 = NIEDRIG; // ledState zum Setzen der LED
unsigned long previousMillis1 = 0; // speichert das letzte Mal, als die LED aktualisiert wurde
lange OnTime1 = 250; // Millisekunden Einschaltzeit
lange OffTime1 = 750; // Millisekunden Auszeit

int ledPin2 = 13; // die Nummer des LED-Pins
int ledState2 = NIEDRIG; // ledState zum Setzen der LED
unsigned long previousMillis2 = 0; // speichert das letzte Mal, als die LED aktualisiert wurde
lange OnTime2 = 330; // Millisekunden Einschaltzeit
lange OffTime2 = 400; // Millisekunden Auszeit

Void-Setup ()
{
// setze den digitalen Pin als Ausgang:
PinMode (ledPin1, AUSGANG);
PinMode (ledPin2, AUSGANG);
}

leere Schleife ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState1 == HIGH) && (currentMillis - previousMillis1> = OnTime1))
{
ledState1 = NIEDRIG; // Schalte es aus
vorherigeMillis1 = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin1, ledState1); // Aktualisieren Sie die tatsächliche LED
}
sonst wenn ((ledState1 == LOW) && (aktuelleMillis - vorherigeMillis1> = OffTime1))
{
ledState1 = HOCH; // Mach es an
vorherigeMillis1 = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin1, ledState1); // Aktualisieren Sie die tatsächliche LED
}

if ((ledState2 == HIGH) && (currentMillis - previousMillis2> = OnTime2))
{
ledState2 = NIEDRIG; // Schalte es aus
vorherigeMillis2 = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin2, ledState2); // Aktualisieren Sie die tatsächliche LED
}
sonst wenn ((ledState2 == LOW) && (aktuelleMillis - vorherigeMillis2> = OffTime2))
{
ledState2 = HOCH; // Mach es an
vorherigeMillis2 = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin2, ledState2); // Aktualisieren Sie die tatsächliche LED
}
}

Vielen Dank! Kann ich ein anderes bekommen?

Sie können weitere Automaten hinzufügen, solange Sie über genügend Speicher oder GPIO-Pins verfügen. Jeder Spielautomat hat seine eigene Flimmergeschwindigkeit. Bearbeiten Sie als Aufgabe den obigen Code, um einen dritten Spielautomaten hinzufügen zu können.

  • Kopieren Sie zunächst alle Statusvariablen und den Code eines der Automaten.
  • Benennen Sie dann jede Variable erneut, um Widerspruch mit der ersten Maschine zu vermeiden.

Es ist nicht so schwer zu tun. Das wiederholte Umschreiben des Codes scheint jedoch ziemlich zeitaufwändig zu sein. Das muss doch anders gehen!

Es gibt bessere Methoden, damit umzugehen. Es gibt Programmiertechniken, die einfacher und effektiver sind.

Klasse

Schauen wir uns noch einmal unsere letzte Skizze an. Wie Sie sehen können, ist es sehr eintönig. Für jede blinkende LED wird fast wortwörtlich derselbe Code kopiert. Das einzige, was sich (leicht) ändert, ist der Name der Variablen.

Dieser Code ist der beste Kandidat für die objektorientierte Programmierung (OOP)

OOP in der Schleife

Die Arduino-Sprache ist eine Variation der C++-Sprache, die objektorientierte Programmierung unterstützt. Mit der OOP -Sprache können wir alle Statusvariablen und Funktionen einer blinkenden LED in einer C++- Klasse sammeln.

Es ist nicht so schwer, da wir bereits den gesamten Code geschrieben haben. Wir müssen es nur als Klasse neu packen.

Bestimmung der Klasse:

Wir beginnen mit der Deklaration der Klasse "Flasher":

Dann fügen wir alle Variablen von FlashWithoutDelay hinzu. Sie sind Teil der Klasse, daher kennen wir sie als Member-Variablen .

 Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde
};

Dann fügen wir einen Konstruktor hinzu. Sie trägt den gleichen Namen wie die Klasse und hat die Aufgabe, alle Variablen zu initialisieren.

 Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde

// Konstruktor - erstellt einen Flasher
// und initialisiert die Mitgliedsvariablen und den Zustand
öffentlich:
Flasher (int pin, lang an, lang aus)
{
ledPin = Stift;
PinMode (ledPin, AUSGANG);

OnTime = ein;
OffTime = aus;

ledState = NIEDRIG;
vorherigeMillis = 0;
}
};

Schließlich verwandeln wir die Schleife in eine Member-Funktion namens "Update ()".

 Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde

// Konstruktor - erstellt einen Flasher
// und initialisiert die Mitgliedsvariablen und den Zustand
öffentlich:
Flasher (int pin, lang an, lang aus)
{
ledPin = Stift;
PinMode (ledPin, AUSGANG);

OnTime = ein;
OffTime = aus;

ledState = NIEDRIG;
vorherigeMillis = 0;
}

void Aktualisieren ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState == HIGH) && (currentMillis - previousMillis> = OnTime))
{
ledState = NIEDRIG; // Schalte es aus
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis> = OffTime))
{
ledState = HOCH; // Mach es an
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
}
};

Indem wir unseren Code in eine Flasher-Klasse umgewandelt haben, haben wir alle Variablen ( status ) und Funktionen ( machine ) der blinkenden LED eingegrenzt.

Jetzt verwenden wir dies:

Erstellen Sie für jede LED, die wir zum Leuchten bringen möchten, ein Beispiel der Flasher-Klasse, indem Sie den Konstruktor aufrufen. Jedes Mal, wenn wir die Schleife durchlaufen, müssen wir Update () für jedes Flasher-Beispiel aufrufen.

Es ist nicht mehr notwendig, den gesamten Maschinencode zu wiederholen. Wir können nur nach weiteren Beispielen der Flasher-Klasse fragen!

 Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde

// Konstruktor - erstellt einen Flasher
// und initialisiert die Mitgliedsvariablen und den Zustand
öffentlich:
Flasher (int pin, lang an, lang aus)
{
ledPin = Stift;
PinMode (ledPin, AUSGANG);

OnTime = ein;
OffTime = aus;

ledState = NIEDRIG;
vorherigeMillis = 0;
}

void Aktualisieren ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState == HIGH) && (currentMillis - previousMillis> = OnTime))
{
ledState = NIEDRIG; // Schalte es aus
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die aktuelle LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis> = OffTime))
{
ledState = HOCH; // Mach es an

vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die aktuelle LED
}
}
};


Led1 Blinker (12.100.400);
Led2 Blinker (13, 350, 350);

Void-Setup ()
{
}

leere Schleife ()
{
led1.Update ();
led2.Update ();
}

Weniger ist mehr!

Endlich - jede zusätzliche LED benötigt nur zwei Codezeilen!

Der Code ist kürzer und besser lesbar. Und da es keinen doppelten Code gibt, muss weniger kompiliert werden! Das alles spart Speicherplatz für andere Projekte!

Was können wir noch tun?

Lassen Sie uns die gleichen Prinzipien auf die Servocodes anwenden und loslegen.

Verbinden Sie zuerst die beiden Servos auf dem Steckbrett, wie im Bild unten gezeigt. Schließen Sie dann auch die dritte LED an.

Arduino Multitasking - Diagramm zum Anschließen von Servos und LEDs

Hier ist der Standard-Servo- Sweep -Code. Beachten Sie, dass es unsere unerwünschte Verzögerungsfunktion () aufruft. Wir werden Teile dieses Codes verwenden, um den Spielautomaten "Sweeper" zu bauen.

 // Sweep
// von BARRAGAN
// Dieser Beispielcode ist gemeinfrei.


#include

Servo myservo; // Servoobjekt erstellen, um ein Servo zu steuern
// Es können maximal acht Servoobjekte erstellt werden

intpos = 0; // Variable zum Speichern der Servoposition

Void-Setup ()
{
meinservo.attach (9); // verbindet das Servo an Pin 9 mit dem Servoobjekt
}


leere Schleife ()
{
for (pos = 0; pos <180; pos + = 1) // geht von 0 Grad auf 180 Grad
{// in 1-Grad-Schritten
myservo.write (pos); // Servo anweisen, in die Position in Variable 'pos' zu gehen
Verzögerung (15); // wartet 15 ms, bis das Servo die Position erreicht
}
for (pos = 180; pos> = 1; pos- = 1) // geht von 180 Grad auf 0 Grad
{
myservo.write (pos); // Servo anweisen, in die Position in Variable 'pos' zu gehen
Verzögerung (15); // wartet 15 ms, bis das Servo die Position erreicht
}
}

Die folgende Sweeper -Klasse fasst die Sweep -Aktion zusammen, verwendet jedoch die Funktion millis(), um die Verzögerung festzulegen.

Wir müssen auch die Funktionen Attach () und Detach () hinzufügen, Um das Servo an einen bestimmten Pin zu binden:

 Klasse Kehrmaschine
{
Servo-Servo; // das Servo
intpos; // aktuelle Servoposition
int-Inkrement; // Inkrement zum Bewegen für jedes Intervall
int updateInterval; // Intervall zwischen Aktualisierungen
unsigned long lastUpdate; // Letzte Aktualisierung der Position

öffentlich:
Sweeper (int-Intervall)
{
updateInterval = Intervall;
Schrittweite = 1;
}

void Anhängen (int pin)
{
servo.attach (Stift);
}

void Trennen ()
{
servo.detach ();
}

void Aktualisieren ()
{
if ((millis () - lastUpdate)> updateInterval) // Zeit zum Aktualisieren
{
lastUpdate = millis ();
Pos + = Inkrement;
servo.write (pos);
Serial.println (pos);
if ((pos> = 180) || (pos <= 0)) // Ende des Sweeps
{
// umgekehrte Richtung
Inkrement = -Inkrement;
}
}
}
};

Wie viel?

Jetzt können wir so viele Flasher und Sweeper erstellen, wie wir wollen.

Jedes Flasher -Beispiel erfordert 2 Codezeilen:

  • eins, um ein Exempel zu statuieren
  • der zweite, um eine Aktualisierungsschleife auszulösen

Jedes Sweeper -Beispiel erfordert nur 3 Codezeilen:

  • eins, um ein Exempel zu statuieren
  • die andere, um das Servo an den Stift zu binden
  • und eine dritte, um eine Aktualisierung in einer Schleife auszulösen

 #include 

Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde

// Konstruktor - erstellt einen Flasher
// und initialisiert die Mitgliedsvariablen und den Zustand
öffentlich:
Flasher (int pin, lang an, lang aus)
{
ledPin = Stift;
PinMode (ledPin, AUSGANG);

OnTime = ein;
OffTime = aus;

ledState = NIEDRIG;
vorherigeMillis = 0;
}

void Aktualisieren ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState == HIGH) && (currentMillis - previousMillis> = OnTime))
{
ledState = NIEDRIG; // Schalte es aus
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis> = OffTime))
{
ledState = HOCH; // Mach es an
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die tatsächliche LED
}
}
};

Klasse Kehrmaschine
{
Servo-Servo; // das Servo
intpos; // aktuelle Servoposition
int-Inkrement; // Inkrement zum Bewegen für jedes Intervall
int updateInterval; // Intervall zwischen Aktualisierungen
unsigned long lastUpdate; // Letzte Aktualisierung der Position

öffentlich:
Sweeper (int-Intervall)
{
updateInterval = Intervall;
Schrittweite = 1;
}

void Anhängen (int pin)
{
servo.attach (Stift);
}

void Trennen ()
{
servo.detach ();
}

void Aktualisieren ()
{
if ((millis () - lastUpdate)> updateInterval) // Zeit zum Aktualisieren
{
lastUpdate = millis ();
Pos + = Inkrement;
servo.write (pos);
Serial.println (pos);
if ((pos> = 180) || (pos <= 0)) // Ende des Sweeps
{
// umgekehrte Richtung
Inkrement = -Inkrement;
}
}
}
};


Led1 Blinker (11, 123, 400);
Led2 Blinker (12, 350, 350);
LED3-Blinker (13.200.222);

Kehrmaschine Kehrmaschine1 (15);
Kehrmaschine Kehrmaschine2 (25);

Void-Setup ()
{
Serial.begin (9600);
Kehrmaschine1.Befestigen (9);
Kehrmaschine2.Befestigen (10);
}


leere Schleife ()
{
Sweeper1.Update ();
Sweeper2.Update ();

led1.Update ();
led2.Update ();
led3.Update ();
}

Im Moment haben wir 5 unabhängige Tasks, die nonstop ohne Unterbrechungen laufen. Und unsere loop()-Funktion hat nur 5 Codezeilen!

Alle zusammen!

Wir wollen auch Ihren Input

Ein weiteres Problem mit delay()-basiertem Timing ist, dass Benutzereingaben, wie etwa das Drücken einer Taste , oft ignoriert werden. Das ist weil dass der Prozessor den Zustand der Schaltfläche nicht überprüfen kann, wenn er sich in der Verzögerungsfunktion () befindet. Mit einer auf der Funktion millis() basierenden Verzögerung kann der Prozessor regelmäßig den Zustand von Schaltflächen und anderen Eingaben überprüfen. Dies ermöglicht es uns, komplexe Programme zu erstellen, die viele Dinge gleichzeitig tun können und dennoch reaktionsschnell sind.

Wir werden dies demonstrieren, indem wir unserer Schaltung eine Schaltfläche hinzufügen, wie im Bild gezeigt:

Arduino Multitasking - Anschlussdiagramm mit einem Knopf

Der folgende Code überprüft den Status der Schaltfläche bei jedem Durchlauf der Schleife. Led1 und Sweeper2 werden nicht aktualisiert, wenn die Taste gedrückt wird.

 #include 


Klasse Blinker
{
// Klassenmitgliedsvariablen
// Diese werden beim Start initialisiert
int ledPin; // die Nummer des LED-Pins
lange OnTime; // Millisekunden Einschaltzeit
lange OffTime; // Millisekunden Auszeit

// Diese behalten den aktuellen Zustand bei
int ledState; // ledState zum Setzen der LED
unsigned long previousMillis; // speichert das letzte Mal, als die LED aktualisiert wurde

// Konstruktor - erstellt einen Flasher
// und initialisiert die Mitgliedsvariablen und den Zustand
öffentlich:
Flasher (int pin, lang an, lang aus)
{
ledPin = Stift;
PinMode (ledPin, AUSGANG);

OnTime = ein;
OffTime = aus;

ledState = NIEDRIG;
vorherigeMillis = 0;
}

void Aktualisieren ()
{
// prüfen, ob es Zeit ist, den Zustand der LED zu ändern
unsigned long currentMillis = millis ();

if ((ledState == HIGH) && (currentMillis - previousMillis> = OnTime))
{
ledState = NIEDRIG; // Schalte es aus
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die aktuelle LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis> = OffTime))
{
ledState = HOCH; // Mach es an
vorherigeMillis = aktuelleMillis; // Denk an die Zeit
digitalWrite (ledPin, ledState); // Aktualisieren Sie die aktuelle LED
}
}
};

Klasse Kehrmaschine
{
Servo-Servo; // das Servo
intpos; // aktuelle Servoposition
int-Inkrement; // Inkrement zum Bewegen für jedes Intervall
int updateInterval; // Intervall zwischen Aktualisierungen
unsigned long lastUpdate; // Letzte Aktualisierung der Position

öffentlich:
Sweeper (int-Intervall)
{
updateInterval = Intervall;
Schrittweite = 1;
}

void Anhängen (int pin)
{
servo.attach (Stift);
}

void Trennen ()
{
servo.detach ();
}

void Aktualisieren ()
{
if ((millis () - lastUpdate)> updateInterval) // Zeit zum Aktualisieren
{
lastUpdate = millis ();
Pos + = Inkrement;
servo.write (pos);
Serial.println (pos);
if ((pos> = 180) || (pos <= 0)) // Ende des Sweeps
{
// umgekehrte Richtung
Inkrement = -Inkrement;
}
}
}
};


Led1 Blinker (11, 123, 400);
Led2 Blinker (12, 350, 350);
LED3-Blinker (13.200.222);

Kehrmaschine Kehrmaschine1 (15);
Kehrmaschine Kehrmaschine2 (25);

Void-Setup ()
{
Serial.begin (9600);
Kehrmaschine1.Befestigen (9);
Kehrmaschine2.Befestigen (10);
}


leere Schleife ()
{
Sweeper1.Update ();

if (digitalRead (2) == HIGH)
{
Sweeper2.Update ();
led1.Update ();
}

led2.Update ();
led3.Update ();
}

Die 3 LEDs blinken in ihrem eigenen Tempo. Die 2 Kehrmaschinen laufen auch mit ihrer eigenen Geschwindigkeit. Aber wenn die Taste gedrückt wird, stoppen Sweeper2 und LED1 , bis wir die Taste loslassen.

Arduino Multitasking - Betrieb

Wir haben jetzt 5 Benutzereingabejobs, die unabhängig voneinander ausgeführt werden. Es gibt keine Verzögerungen, die den Prozessor blockieren. Und unser effizienter objektorientierter Programmiercode lässt viel Spielraum für Erweiterungen!

Schlussfolgerungen:

In dieser Anleitung haben wir Ihnen gezeigt, dass der Arduino kann viele verschiedene Aufgaben erfüllen und trotzdem auf äußere Ereignisse reagieren.

  • Wir haben gelernt, wie man die Zeit mit millis () anstelle von delay () misst, wodurch der Prozessor andere Dinge tun konnte.
  • Wir haben gelernt, Aufgaben als Automaten zu definieren, die gleichzeitig mit anderen Automaten, aber unabhängig von ihnen laufen können.
  • Wir haben herausgefunden, wie man diese Automaten in C++-Klassen einbindet, um den Code einfach und prägnant zu halten.

Diese Techniken verwandeln Ihren Arduino nicht in einen Supercomputer. Sie werden Ihnen jedoch dabei helfen, das Beste aus diesem kleinen, aber erstaunlich leistungsstarken Modul herauszuholen.

In Teil Zwei bauen wir mit diesen Techniken. Wir werden auch andere Möglichkeiten entdecken, den Arduino auf externe Ereignisse reagieren zu lassen, während mehrere Aufgaben ausgeführt werden.

Teil zwei ->

Quelle: https://learn.adafruit.com/multi-tasking-the-arduino-part-1

Wir freuen uns auf die Zusammenarbeit mit Ihnen!