Timer und Multitasking auf Arduino

Bild

Heute werden wir über ein so relevantes Thema wie Timer und Multitasking auf Arduino sprechen. Der Grund für das Schreiben dieses Artikels waren die Vorlesungen von Oleg Artamonov @olartamonov für MIREA- Studenten innerhalb der Samsung IoT Academy , oder besser gesagt, Olegs Aussage, Zitat (2. Vorlesung, 1:13:08):
"Es gibt zum Beispiel Aufgaben, für die die meisten Arduino-Treiber, insbesondere Anfänger, unterbrochen werden können. Bitten Sie sie, mit fünf verschiedenen LEDs mit unterschiedlichen Frequenzen und Perioden zu blinken, damit für jede LED eine andere Periode individuell geändert werden kann ..."

Nach Olegs Aussagen zu urteilen, hat er ein sehr falsches Verständnis von Arduino im Allgemeinen und von „Arduino“ im Besonderen. Das Blinken mit fünf LEDs in den von ihm angegebenen Modi ist für Arduino eine absolut triviale Aufgabe, und für Arduino Mega Server ist es überhaupt keine Aufgabe, sondern ein echtes Missverständnis - Multitasking wird durch seine regulären Mittel organisiert, die leicht Hunderte verschiedener Einheiten (LEDs, Servos, Schrittmotoren usw.) steuern. d.) in Echtzeit.

Lassen Sie uns gemeinsam herausfinden, wie Multitasking auf Arduino organisiert werden kann, und gleichzeitig den MIREA- Schülern helfen, die Stereotypen der Wahrnehmung zu beseitigen, die ihnen in Bezug auf das soziokulturelle und technologische Phänomen unserer Zeit namens Arduino auferlegt wurden.

Vorträge von Oleg Artamonov


Wir müssen Tribut zollen, Olegs Vorträge selbst sind gut - sie bieten viele nützliche und gut strukturierte Informationen über Mikrocontroller, und ich würde jedem, der sich für dieses Thema interessiert, empfehlen, sich mit ihnen vertraut zu machen. Der einzige Nachteil dieser Vorträge schien mir offener Techno-Snobismus in Bezug auf Arduino zu sein, der in ihnen als „Prügelknabe“ auftritt.

Insbesondere machte Oleg in allen Vorträgen zwingende Aussagen über die Ungeeignetheit von Arduino für den Aufbau komplexer Multitasking-Systeme, was einfach der Wahrheit und der tatsächlichen Praxis widerspricht.

Auf Arduino können Sie erstaunliche Multitasking-Systeme erstellen, in denen Dutzende und Hunderte von Einheiten (LEDs, Sensoren, Aktoren, Servos, Schrittmotoren, drahtlose und drahtgebundene Schnittstellen usw.) gleichzeitig im (Pseudo-, natürlich) Multitasking-Modus arbeiten.

Wir werden für Beispiele nicht weit gehen. Hier ist das Projekt des Wintergartens („Smart Greenhouse“), in dem die folgenden Einheiten in Echtzeit im Multitasking-Modus arbeiten:

Bild

Die Topologie eines verteilten nRF24-Controllers mit einer Vielzahl verbundener Geräte in Echtzeit. Der Benutzer befasst sich nur mit der „Basis“, die Arbeit des nRF24-Partners ist für ihn völlig transparent. Und ja, das ist Arduino.

An der "Basis":

- 7 Servos
- 9 Schrittmotoren
- 6 Relais
- 3 Bodenfeuchtesensoren
- 2 Lichtsensoren
- Wasserstandsensor
- Feuchtigkeits- und Lufttemperatursensor

Auf nRF24 des Remote-Teils:

- 12 Bodenfeuchtesensoren
- 12 Relais
- 3 Schrittmotoren
- 2 Lichtsensoren
- Wasserstandsensor

Darüber hinaus arbeitet der nRF24 selbst in Echtzeit zwischen den beiden verteilten Teilen des Systems und der Ethernet-Schnittstelle des Servers und der Server-Engine, die eine Webbenutzeroberfläche des Systems bereitstellt.

Insgesamt funktionieren in Echtzeit im Multitasking-Modus mindestens 60 Entitäten auf dem 8-Bit-Mega (und dies berücksichtigt nicht die vielen Dienste des AMS-Betriebssystems selbst, mit denen sich die Anzahl der Entitäten 100 nähert). Was offensichtlich nicht mit der Aussage übereinstimmt, dass "echtes Multitasking auf dem Arduino unmöglich ist und es problematisch ist, selbst mit fünf LEDs darauf zu blinken".

Ein paar Worte zur Verteidigung von Arduino


(Obwohl es offensichtlich ist, dass Arduino als soziokulturelles und technologisches Phänomen mit einer millionenschweren Armee von Fans und vielen tausend erstaunlichen Projekten keinen Schutz benötigt.)

Ich habe viele Male gesagt und werde noch einmal wiederholen, dass Arduino in seiner Softwarekomponente tatsächlich nur eine der möglichen Abstraktionsebenen (wie jede andere) mit ihren eigenen Vor- und Nachteilen ist. Und es gibt absolut keinen Unterschied für den Benutzer, was sich in seinem kleinen Stück Silizium „dreht“ - das „reine“ Arduino, RTOS, RIOT OS, AMS oder eine andere mathematische Abstraktion der Darstellung und Verwaltung der Eisenressourcen des Controllers.

Es ist wichtig, dass der Benutzer seine Probleme löst - damit der Controller die Pflanzen bewässert, das Licht einschaltet, die Vorhänge steuert usw. Das Hauptproblem liegt nicht in den in der Entwicklung verwendeten Werkzeugen, sondern in der Fähigkeit, sie zu verwenden, und, kitschig, in der Vorstellungskraft und der technischen Vision des Entwicklers selbst.

Wie funktioniert es


Multitasking auf Mikrocontrollern kann für sich genommen auf unterschiedliche Weise organisiert werden. In diesem Fall werden wir über das einfachste sprechen - Prozesse wechseln sich ab und geben es freiwillig weiter, nachdem sie ihr Zeitquantum verwendet haben. Diese Methode ist natürlich nicht ohne offensichtliche Mängel, aber wie sie sagen, ist die Praxis ein Kriterium der Wahrheit und hat sich unter realen Bedingungen bewährt: Sie wird sowohl in Standard- Arduino Mega Server- Distributionen als auch in vielen Projekten auf AMS Pro verwendet . Und diese Systeme arbeiten rund um die Uhr und haben die Betriebszeit in vielen Monaten störungsfreien Betriebs bestätigt.

Bild

Dies ist ein Hinweis auf ungefähr hundert Entitäten eines verteilten nRF24-Systems, die unabhängig in Echtzeit verwaltet werden. Achten Sie auf die letzten beiden CPU-Anzeigen - selbst beim 8-Bit-Mega ist die Prozessorlast Null (dh das System ist vollständig frei).

Ein bisschen über Timer


Um die Steuerung komplexer Systeme zu organisieren, reicht es nicht aus, nur die Steuerung zwischen Prozessen zu übertragen, und neben der automatischen Steuerungsübertragung an AMS werden verschiedene Arten von Zeitgebern verwendet: zyklisch, zyklisch mit einer bestimmten Anzahl von Wiederholungen (Stapel), einzeln, zufällig, gemischt usw. All dies ist organisiert native Mittel von Arduino und verwendet keine Interrupts oder direkte Programmierung von Mikrocontroller-Timern (aber Interrupts werden natürlich vom System "für ihren beabsichtigten Zweck" verwendet).

Was wiederum direkt der Aussage widerspricht: "Es wird genug Eisen-Timer für 3 LEDs geben, von da an werden Arduinisten Probleme haben." Nicht starten. Alle Arten von Timern stehen uns in beliebiger Menge zur Verfügung. Und wenn gewünscht, können wir uns noch neuer und willkürlich exotischer machen.

Hauptfall


Der Hauptfall für diese Art von Multitasking-Organisation besteht darin, den sogenannten "nicht blockierenden" Code zu erstellen, dh Code, der die delay () - Funktion nicht verwendet, wodurch das Programm einfach für eine bestimmte Zeit angehalten wird.

Echtzeit


Das beschriebene Verfahren zur Implementierung von Multitasking kann als "Soft-Realtime" bezeichnet werden. Die typische Verzögerungszeit im System beträgt 10 ms (die Spitzenverzögerungen können jedoch viel länger und nicht standardisiert sein). Dies führt zu bekannten Einschränkungen des Anwendungsspektrums dieser Lösung, ist jedoch für die meisten "Haushalts" -Aufgaben (und nicht nur) perfekt geeignet (siehe Beispiel oben).

Wenn Sie eine strengere Echtzeitsteuerung benötigen, erfordert dies eine spezielle Optimierung des Codes für eine bestimmte Aufgabe, eine Umstrukturierung der Architektur oder in extremen Fällen die Zuweisung eines separaten Controllers für bestimmte Funktionen. Hervorheben eines separaten Effekt- Controllers für einen intelligenten LED-Streifen.

Dies ist eine allgemeine theoretische Beschreibung der Arbeit des Multitasking in Arduino im Allgemeinen und in AMS im Besonderen. Nun wenden wir uns der Betrachtung praktischer Beispiele zu.

Zyklus-Timer



Bild

Betrachten Sie die Implementierung der einfachsten zyklischen Timer. Dies sind Timer (in der Terminologie von AMS-Zyklen), die in bestimmten, vordefinierten Zeitintervallen aktiviert werden und zur Aktivierung zyklischer Prozesse verwendet werden.

Im Allgemeinen ist es besser, Timer programmgesteuert als Objekte zu entwerfen, aber in der Standardauslieferung von Arduino Mega Server werden diese Timer als Funktionen implementiert. Daher werden wir sie zunächst in dieser Hypostase berücksichtigen.

Die Verwendung von zyklischen Timern ist sehr einfach: Setzen Sie einfach den Code, der regelmäßig ausgeführt werden muss, in die Klammern der if-Anweisung. Wenn Sie ein anderes Triggerintervall verwenden müssen, verwenden Sie einfach die gewünschte Variable anstelle von cycle1s. Sie können so viele verschiedene Zyklen ausführen, wie Sie möchten - selbst auf einem 8-Bit-Mega-System können Hunderte solcher Timer problemlos verarbeitet werden (natürlich müssen Sie sich daran erinnern, dass der aufgerufene Code nicht blockiert).

  if (cycle1s) {
    // ,   , ,  
  }

. :

// Cycles
bool cycle1s  = false;
bool cycle5s  = false;
bool cycle20s = false;
bool cycle30s = false;
bool cycle1m  = false;
bool cycle3m  = false;
bool cycle5m  = false;

.

«Timers»:

/*
  Module Timers
  part of Arduino Mega Server project
*/

// Cycles
unsigned long timeSec;
unsigned long timer1s;
unsigned long timer5s;
unsigned long timer20s;
unsigned long timer30s;
unsigned long timer1m;
unsigned long timer3m;
unsigned long timer5m;

void timersInit() {
  unsigned long uptimeSec = millis() / 1000;
  timeSec  = uptimeSec;
  timer1s  = uptimeSec;  
  timer5s  = uptimeSec; 
  timer20s = uptimeSec;
  timer30s = uptimeSec;
  timer1m  = uptimeSec;
  timer3m  = uptimeSec;
  timer5m  = uptimeSec;
}

void timersWorks() {
  timeSec = millis() / 1000;
    if (timeSec - timer1s  >=  1)  {
                                    timer1s  = timeSec; cycle1s  = true;
    if (timeSec - timer5s  >=  5)  {timer5s  = timeSec; cycle5s  = true;}
    if (timeSec - timer20s >= 20)  {timer20s = timeSec; cycle20s = true;}
    if (timeSec - timer30s >= 30)  {timer30s = timeSec; cycle30s = true;}
    if (timeSec - timer1m  >= 60)  {timer1m  = timeSec; cycle1m  = true;}
    if (timeSec - timer3m  >= 180) {timer3m  = timeSec; cycle3m  = true;}
    if (timeSec - timer5m  >= 300) {timer5m  = timeSec; cycle5m  = true;}
  }
}

void eraseCycles() {
  cycle1s  = false;
  cycle5s  = false;
  cycle20s = false;
  cycle30s = false;
  cycle1m  = false;
  cycle3m  = false;
  cycle5m  = false;
}

. - , . , , .

void loop() {
  timersWorks();

  //   

  eraseCycles();
}


, , . myCycle.

, :

/*
  myCycle Library
  part of Arduino Mega Server project
*/

#ifndef _MYCYCLE_H
#define _MYCYCLE_H

#define MS_500       500
#define MS_01S      1000
#define MS_02S      2000
#define MS_05S      5000
#define MS_10S     10000
#define MS_13S     13000
#define MS_17S     17000
#define MS_20S     20000
#define MS_30S     30000
#define MS_01M     60000
#define MS_03M    180000
#define MS_05M    300000
#define MS_01H   3600000
#define MS_06H  21600000
#define MS_12H  43200000
#define MS_01D  86400000

class myCycle {
  private:
    bool          _go;
    bool          _active;
    unsigned long _start;
    unsigned long _period;
  
  public:
    myCycle(unsigned long per, bool act);
    void reInit(unsigned long per, bool act);
    void reStart();
    bool check();
    bool go();
    void clear();
    
    // active
    bool active();
    void setActive(bool act);
    // period
    unsigned long period();
    void setPeriod(unsigned long per);
}; // class myCycle

#endif // _MYCYCLE_H

:

/*
  myCycle Library
  part of Arduino Mega Server project
*/

#include "myCycle.h"
#include <Arduino.h>

myCycle::myCycle(unsigned long per, bool act) {
  _go     = false;
  _active = act;
  _period = per;
  _start  = millis();
}

// Methods

void myCycle::reInit(unsigned long per, bool act) {
  _go     = false;
  _active = act;
  _period = per;
  _start  = millis();
}

void myCycle::reStart() {
  _start = millis();
}

bool myCycle::check() {
  if (millis() - _start >= _period) {
    _start = millis();
    if (_active) {
      _go = true;
    }
  }
  return _go;
}

bool myCycle::go() {
  return _go;
}

void myCycle::clear() {
  _go = false;
}

// Active

bool myCycle::active() {
  return _active;
}

void myCycle::setActive(bool act) {
  _active = act;
}

// Period

unsigned long myCycle::period() {
  return _period;
}

void myCycle::setPeriod(unsigned long per) {
  _period = per;
}

«» : « ».

:

:

#include "myCycle.h"

:

// Cycles
myCycle cycle500(MS_500, true);
myCycle cycle2s(MS_02S, true);
myCycle cycle5s(MS_05S, true);

:

void timersWorks() {
  cycle500.check();
  cycle2s.check();
  cycle5s.check();
}

void eraseCycles() {
  cycle500.clear();
  cycle2s.clear();
  cycle5s.clear();
}

:

void loop() {
  timersWorks();

  //   

  if (cycle5s.go()) {
    Serial.println(F("cycle5s!"));
  }

  eraseCycles();
}

, , . , / , , , .

Arduino



image

, — . , . , AMS .

()


, . , 3 nRF24. 3 .

/ . .


«», - - .


- , , .

, , . , , , 20 , «», «» , . , «» .

, . — Arduino Mega Server .


, , / , « » . .

— , 8- .


, , Arduino - — — , .

, Arduino Mega Server .


, , Arduino , , , Arduino () .

, , .

P.S.
, 0.17 Arduino Mega Server Arduino Mega, Due, 101 M0, . , , .

Source: https://habr.com/ru/post/de413779/


All Articles