Wielozadaniowość Arduino - część pierwsza

Większe i lepsze projekty

Jeśli opanowałeś już podstawowe migające diody LED, proste czujniki i serwomechanizmy, jesteś gotowy zacząć przygodę z większymi i lepszymi projektami. Zazwyczaj wiąże się to z łączeniem części prostszych szkiców i próbą ich wspólnego działania. Pierwszą odkrytą przez Ciebie rzeczą będzie to, że niektóre z tych szkiców, które osobno działają idealnie, nie zgrywają się z innymi.

 

Arduino to bardzo prosty moduł bez systemu operacyjnego, który może uruchomić tylko jeden program. W przeciwieństwie do Twojego osobistego komputera lub Raspberry Pi, Arduino nie ma możliwości ładowania i uruchamiania wielu programów. 

 

Nie oznacza to, że nie możemy wykonać wielu zadań w Arduino. Musimy po prostu użyć innej metody. Nie mamy systemu operacyjnego do pomocy, więc musimy wziąć sprawę we własne ręce.

 

Wielozadaniowość Arduino

Pozbądź się funkcji delay()

Pierwsze, czego się nauczyłeś podczas zabawy z Arduino, to najprawdopodobniej użycie powyższej funkcji (delay-opóźnienie). Funkcja delay() nie jest skomplikowana, ale sprawia ona problemy gdy dodaje się dodatkowe funkcje. Kłopotem jest to, że owa funkcja, jest "aktywnym czekaniem" (z ang. busy wait), która dominuje procesor. 

 

Podczas wywoływania tej funkcji, nie możesz reagować na wejścia, nie możesz przetwarzać żadnych danych i nie możesz zmienić jakiegokolwiek wyjścia. Blokuje ona cały procesor. Więc, jeśli któraś część Twojego kodu korzysta z tej funkcji, na ten czas, wszystko inne zostaje zatrzymane.

 

Pamiętasz Blink?

 

/*
Blink
Turns on an LED on for one second, then off for one second, repeatedly.

This example code is in the public domain.
*/

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

// the setup routine runs once when you press reset:
void setup() {
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
}

// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second
}

Prosty szkic Blink jest prawie cały czas w funkcji delay(). Więc w tym czasie, procesor nie może robić nic innego.

 

Nie zapomniałeś o sweep?

Sweep używa funkcji delay(), aby kontrolować swoją prędkość. Jeśli spróbujesz połączyć podstawowy szkic blink z przykładem serwomechanizmu sweep, zauważysz, że naprzemiennie miga dioda i porusza się serwo. Nie będzie jednak działo się to jednocześnie.

 

#include 

// Pin 13 has an LED connected on most Arduino boards.
// give it a name:
int led = 13;

Servo myservo; // create servo object to control a servo
// twelve servo objects can be created on most boards

int pos = 0; // variable to store the servo position

void setup()
{
// initialize the digital pin as an output.
pinMode(led, OUTPUT);
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}

void loop()
{
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
delay(1000); // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
delay(1000); // wait for a second

for (pos = 0; pos <= 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for (pos = 180; pos >= 0; pos -= 1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo

 

Więc jak skontrolujemy opóźnienie bez użycia funkcji delay?

Użycie funkcji millis() w opóźnieniu

Prostą techniką wdrożenia opóźnienia jest zrobienie planu i obserwowanie zegarka. Zamiast skupiać się na funkcji delay, po prostu systematycznie sprawdzasz czas, aby wiedzieć kiedy działać. W międzyczasie, inne zadania mogą korzystać z procesora. Bardzo prostym tego przykładem jest szkic BlinkyWithoutDelay ze środowiskiem programistycznym. 

 

Schemat podłączenia dla powyższego kodu znajduje się poniżej:

  

Wielozadaniowość Arduino - schemat podłączenia

 

Migotanie bez opóźnień 

Oto przykładowy kod BlinkWithoutDelay:

 

/* Blink without Delay

Turns on and off a light emitting diode(LED) connected to a digital
pin, without using the delay() function. This means that other code
can run at the same time without being interrupted by the LED code.

The circuit:
* LED attached from pin 13 to ground.
* Note: on most Arduinos, there is already an LED on the board
that's attached to pin 13, so no hardware is needed for this example.


created 2005
by David A. Mellis
modified 8 Feb 2010
by Paul Stoffregen

This example code is in the public domain.


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

// constants won't change. Used here to
// set pin numbers:
const int ledPin = 13; // the number of the LED pin

// Variables will change:
int ledState = LOW; // ledState used to set the LED
long previousMillis = 0; // will store last time LED was updated

// the follow variables is a long because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long interval = 1000; // interval at which to blink (milliseconds)

void setup() {
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}

void loop()
{
// here is where you'd put code that needs to be running all the time.

// check to see if it's time to blink the LED; that is, if the
// difference between the current time and last time you blinked
// the LED is bigger than the interval at which you want to
// blink the LED.
unsigned long currentMillis = millis();

if(currentMillis - previousMillis > interval) {
// save the last time you blinked the LED
previousMillis = currentMillis;

// if the LED is off turn it on and vice-versa:
if (ledState == LOW)
ledState = HIGH;
else
ledState = LOW;

// set the LED with the ledState of the variable:
digitalWrite(ledPin, ledState);
}
}

Czy ma to jakiś sens?

Na pierwszy rzut oka, BlinkWithoutDelay nie wydaje się zbytnio interesujące. Wygląda jak bardziej skomplikowany sposób na włączenie diod LED. Jednakże, BlinkWithoutDelay prezentuje bardzo ważny koncept znany jako Automat skończony (z ang. State Machine).

Nie musisz już polegać na funkcji delay(), żeby stworzyć program migających diod. BlinkWithoutDelay zapamiętuje obecny status diod LED i ostatni czas, w którym się on zmienił. Po każdej pętli, sprawdza on zegar millis(), aby wiedzieć czy powinien ponownie zmienić status diod LED.

 

Witaj w maszynie

Zwróćmy uwagę na trochę bardziej interesujący wariant blink, który posiada różne czasy włączenia i wyłączenia. Nazwaliśmy go "FlashWithoutDelay"

 

// These variables store the flash pattern
// and the current state of the LED

int ledPin = 13; // the number of the LED pin
int ledState = LOW; // ledState used to set the LED
unsigned long previousMillis = 0; // will store last time LED was updated
long OnTime = 250; // milliseconds of on-time
long OffTime = 750; // milliseconds of off-time

void setup()
{
// set the digital pin as output:
pinMode(ledPin, OUTPUT);
}

void loop()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}

Status + Mashine = State Machine

Zauważ, że mamy zmienne, które śledzą czy dioda LED jest włączona czy wyłączona. Są też takie, które obserwują ostatnio zaszłą zmianę. Jest to część Automatu zwana Statusem.

 

Posiadamy również kod, który czuwa nad statusem i decyduje kiedy i jak trzeba go zmienić. To jest część zwana Maszyną. Po każdej pętli, "uruchamiamy maszynę", a ona aktualizuje status.

 

Następnie, sprawdzimy jak połączysz automaty jednocześnie je uruchamiając. 

 

Cel - dwa na raz

Czas na wielozadaniowość! Najpierw podłącz kolejną diodę LED, tak jak na schemacie poniżej:

 

Wielozadaniowość Arduino - schemat podłączenia diod LED

 

Następnie stworzymy kolejny automat dla drugiej diody LED, która miga z zupełnie innymi szybkościami. Użycie dwóch oddzielnych automatów sprawi, że te dwie diody będą migać, nie będąc od siebie zależne.

 

// These variables store the flash pattern
// and the current state of the LED

int ledPin1 = 12; // the number of the LED pin
int ledState1 = LOW; // ledState used to set the LED
unsigned long previousMillis1 = 0; // will store last time LED was updated
long OnTime1 = 250; // milliseconds of on-time
long OffTime1 = 750; // milliseconds of off-time

int ledPin2 = 13; // the number of the LED pin
int ledState2 = LOW; // ledState used to set the LED
unsigned long previousMillis2 = 0; // will store last time LED was updated
long OnTime2 = 330; // milliseconds of on-time
long OffTime2 = 400; // milliseconds of off-time

void setup()
{
// set the digital pin as output:
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
}

void loop()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState1 == HIGH) && (currentMillis - previousMillis1 >= OnTime1))
{
ledState1 = LOW; // Turn it off
previousMillis1 = currentMillis; // Remember the time
digitalWrite(ledPin1, ledState1); // Update the actual LED
}
else if ((ledState1 == LOW) && (currentMillis - previousMillis1 >= OffTime1))
{
ledState1 = HIGH; // turn it on
previousMillis1 = currentMillis; // Remember the time
digitalWrite(ledPin1, ledState1); // Update the actual LED
}

if((ledState2 == HIGH) && (currentMillis - previousMillis2 >= OnTime2))
{
ledState2 = LOW; // Turn it off
previousMillis2 = currentMillis; // Remember the time
digitalWrite(ledPin2, ledState2); // Update the actual LED
}
else if ((ledState2 == LOW) && (currentMillis - previousMillis2 >= OffTime2))
{
ledState2 = HIGH; // turn it on
previousMillis2 = currentMillis; // Remember the time
digitalWrite(ledPin2, ledState2); // Update the actual LED
}
}

Dziękuję serdecznie! Czy mogę dostać następny?

Możesz dodawać więcej automatów, dopóki będziesz mieć wystarczająco pamięci lub pinów GPIO. Każdy automat posiada swoją własną szybkość migotania. Jako zadanie, zedytuj powyższy kod, aby móc dodać trzeci automat. 

 

  • Po pierwsze, skopiuj wszystkie zmienne statusu i kod jednego z automatów.
  • Potem, nazwij ponownie każdą zmienną, aby uniknąć sprzeczności z pierwszą maszyną.

 

Nie jest to trudne do zrobienia. Lecz ciągłe przepisywanie kodu wydaje się raczej czasochłonne. Musi być jakiś inny sposób by to zrobić!

 

Istnieją lepsze metody radzenia sobie z tym. Są to techniki programowania, które są prostsze i bardziej efektywne. 

 

Klasy

Spójrzmy jeszcze raz na nasz ostatni szkic. Jak widzisz, jest on bardzo monotonny. Ten sam kod jest kopiowany niemalże słowo w słowo dla każdej migoczącej diody LED. Jedyną rzeczą, która się zmienia (w niewielkim stopniu) to nazwa zmiennych. 

 

Kod ten jest najlepszym kandydatem do programowania obiektowego (OOP, z ang. Object Oriented Programming)

 

OOP w pętli

Język Arduino jest odmianą języka C++, który obsługuje programowanie obiektowe. Korzystając z języka OOP możemy zebrać wszystkie zmienne statusu i funkcje migającej diody LED w klasie C++.

  

Nie jest to trudne do zrobienia, ponieważ już posiadamy cały napisany kod. Musimy tylko przepakować go jako klasę.

 

Określanie klasy:

Zaczynamy zadeklarowaniem klasy "Flasher":

  

Następnie dodajemy wszystkie zmienne z FlashWithoutDelay. Są one częścią klasy, więc znamy je jako zmienne składowe (member variables).

 

class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated
};

Potem dodajemy konstruktor (constructor). Ma on tę samą nazwę co klasa, a jego zadaniem jest inicjować wszystkie zmienne.

 

class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated

// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);

OnTime = on;
OffTime = off;

ledState = LOW;
previousMillis = 0;
}
};

 

Na koniec, zmieniamy pętlę w funkcję składową nazwaną "Update()". 

 

class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated

// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);

OnTime = on;
OffTime = off;

ledState = LOW;
previousMillis = 0;
}

void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};

Zmieniając nasz kod w klasę Flasher, zwęziliśmy wszystkie zmienne (status) i funkcje (maszyna) migającej diody LED.

 

A teraz użyjmy tego:

Dla każdej diody LED, którą chcemy zaświecić, tworzymy przykład klasy Flasher przywołując konstruktor. Przy każdym przejściu przez pętlę, musimy przywołać Update() dla każdego przykładu Flashera. 

 

Już nie trzeba powtarzać całego kodu automatu. Pozostaje nam tylko prosić o kolejne przykłady klasy Flasher!

 

class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated

// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);

OnTime = on;
OffTime = off;

ledState = LOW;
previousMillis = 0;
}

void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on

previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};


Flasher led1(12, 100, 400);
Flasher led2(13, 350, 350);

void setup()
{
}

void loop()
{
led1.Update();
led2.Update();
}

 

Mniej oznacza więcej!

Nareszcie - każda dodatkowa dioda LED wymaga tylko dwóch linijek kodu!

 

Kod ten jest krótszy i łatwiejszy w odczytaniu. A ponieważ nie ma zduplikowanego kodu, jest mniej do skompilowania! To wszystko oszczędza pamięć na inne projekty!

 

Co jeszcze możemy zrobić?

Zastosujmy te same zasady do kodów serwomechanizmów i zacznijmy działać. 

 

Najpierw, połącz ze sobą dwa serwa na płytce stykowej, tak ja na rysunku poniżej. Następnie, podłącz również trzecią diodę LED.

 

Wielozadaniowość Arduino - schemat podłączenia serw i diod LED

  

Oto standardowy kod sweep serwa. Zauważ, że przywołuje on niechcianą przez nas funkcję delay(). Weźmiemy części tego kodu, aby zbudować automat "Sweeper".

 

// Sweep
// by BARRAGAN
// This example code is in the public domain.


#include

Servo myservo; // create servo object to control a servo
// a maximum of eight servo objects can be created

int pos = 0; // variable to store the servo position

void setup()
{
myservo.attach(9); // attaches the servo on pin 9 to the servo object
}


void loop()
{
for(pos = 0; pos < 180; pos += 1) // goes from 0 degrees to 180 degrees
{ // in steps of 1 degree
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
for(pos = 180; pos>=1; pos-=1) // goes from 180 degrees to 0 degrees
{
myservo.write(pos); // tell servo to go to position in variable 'pos'
delay(15); // waits 15ms for the servo to reach the position
}
}

 

Poniższa klasa Sweeper streszcza akcję sweep, ale używa funkcji millis() do ustawienia opóźnienia. 

 

Musimy także dodać funkcje Attach() i Detach(), żeby powiązać serwo z konkretnym pinem:

 

class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position

public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}

void Attach(int pin)
{
servo.attach(pin);
}

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

void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};

 

Jak dużo?

Teraz możemy tworzyć tyle egzemplarzy Flasherów Sweeperów, ile tylko chcemy.

 

Każdy przykład Flashera wymaga 2 linii kodu:

  • jedną, aby zadeklarować przykład
  • drugą, żeby przywołać aktualizację w pętli

 

Każdy przykład Sweepera wymaga tylko 3 linii kodu:

  • jedną, aby zadeklarować przykład
  • drugą, żeby powiązać serwo z pinem
  • i trzecią, aby przywołać aktualizację w pętli

 

#include 

class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated

// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);

OnTime = on;
OffTime = off;

ledState = LOW;
previousMillis = 0;
}

void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};

class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position

public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}

void Attach(int pin)
{
servo.attach(pin);
}

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

void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};


Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);

Sweeper sweeper1(15);
Sweeper sweeper2(25);

void setup()
{
Serial.begin(9600);
sweeper1.Attach(9);
sweeper2.Attach(10);
}


void loop()
{
sweeper1.Update();
sweeper2.Update();

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

 

W tej chwili mamy 5 niezależnych zadań działających non-stop, bez zakłóceń. A nasza funkcja loop() posiada tylko 5 linijek kodu! 

Wszystko razem!

Chcemy też Twojego wejścia

Kolejnym problemem z taktowaniem bazującym na funkcji delay(), jest to, że wejście użytkownika, takie jak wciśnięcie przycisku, jest często ignorowane. Dzieje się tak dlatego, że procesor nie potrafi sprawdzić stanu przycisku kiedy jest w funkcji delay(). Z opóźnieniem na bazie funkcji millis(), procesor może regularnie sprawdzać stan przycisków i innych wejść. To pozwala nam zbudować złożone programy, które mogą robić wiele rzeczy na raz i nadal reagować.

 

Zademonstrujemy to dodając przycisk do naszego obwodu, tak jak na rysunku:

 

Wielozadaniowość Arduino - schemat podłączenia z przyciskiem

 

Poniższy kod sprawdzi stan przycisku na każdym przejściu pętli. Led1 sweeper2 nie będą aktualizowane podczas wciśnięcia przycisku.

 

#include 


class Flasher
{
// Class Member Variables
// These are initialized at startup
int ledPin; // the number of the LED pin
long OnTime; // milliseconds of on-time
long OffTime; // milliseconds of off-time

// These maintain the current state
int ledState; // ledState used to set the LED
unsigned long previousMillis; // will store last time LED was updated

// Constructor - creates a Flasher
// and initializes the member variables and state
public:
Flasher(int pin, long on, long off)
{
ledPin = pin;
pinMode(ledPin, OUTPUT);

OnTime = on;
OffTime = off;

ledState = LOW;
previousMillis = 0;
}

void Update()
{
// check to see if it's time to change the state of the LED
unsigned long currentMillis = millis();

if((ledState == HIGH) && (currentMillis - previousMillis >= OnTime))
{
ledState = LOW; // Turn it off
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
else if ((ledState == LOW) && (currentMillis - previousMillis >= OffTime))
{
ledState = HIGH; // turn it on
previousMillis = currentMillis; // Remember the time
digitalWrite(ledPin, ledState); // Update the actual LED
}
}
};

class Sweeper
{
Servo servo; // the servo
int pos; // current servo position
int increment; // increment to move for each interval
int updateInterval; // interval between updates
unsigned long lastUpdate; // last update of position

public:
Sweeper(int interval)
{
updateInterval = interval;
increment = 1;
}

void Attach(int pin)
{
servo.attach(pin);
}

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

void Update()
{
if((millis() - lastUpdate) > updateInterval) // time to update
{
lastUpdate = millis();
pos += increment;
servo.write(pos);
Serial.println(pos);
if ((pos >= 180) || (pos <= 0)) // end of sweep
{
// reverse direction
increment = -increment;
}
}
}
};


Flasher led1(11, 123, 400);
Flasher led2(12, 350, 350);
Flasher led3(13, 200, 222);

Sweeper sweeper1(15);
Sweeper sweeper2(25);

void setup()
{
Serial.begin(9600);
sweeper1.Attach(9);
sweeper2.Attach(10);
}


void loop()
{
sweeper1.Update();

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

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

 

3 diody LED będą migać we własnym tempie. 2 sweepery też będą działać z własną szybkością. Ale po wciśnięciu przycisku, sweeper2 led1 zostaną zatrzymane, dopóki nie zwolnimy przycisku. 

 

wielozadaniowość Arduino - działanie

 

Mamy teraz 5 zadań z wejściem użytkownika, działających niezależnie. Nie ma żadnych opóźnień, które zablokują procesor. A nasz wydajny kod programowania obiektowego pozostawia dużo miejsca na rozszerzenia!

 

Wnioski:

W tym przewodniku pokazaliśmy, że Arduino potrafi wykonywać wiele różnych zadań i nadal reagować na zewnętrzne zdarzenia. 

 

  • Nauczyliśmy się jak mierzyć czas za pomocą funkcji millis() zamiast delay(), co pozwoliło procesorowi robić inne rzeczy.
  • Dowiedzieliśmy się jak określać zadania jako automaty, które mogą działać w tym samym czasie co inne automaty, ale niezależnie od nich.
  • Odkryliśmy jak zawrzeć te automaty w klasach C++, aby kod pozostał prosty i zwięzły.

 

Techniki te nie zmienią Twojego Arduino w superkomputer. Jednakże, pomogą Ci wydobyć to co najlepsze z tego małego, ale zadziwiająco potężnego modułu.

W części drugiej, będziemy budować korzystając z tych technik. Odkryjemy też inne sposoby na to, aby Arduino reagowało na zewnętrzne zdarzenia utrzymując przy tym działanie wielu zadań.  

 

część druga ->

 

Źródło: https://learn.adafruit.com/multi-tasking-the-arduino-part-1

zapraszamy do współpracy!