Multitasking



Idáig egy-egy feladatott végrehajtó projekteket mutattam be, de mi van akkor ha egy projekten belül több eszközt vagy feladatott kell kezelni egyszerre. Ekkor jön képbe a multitasking.
Mi is a multitasking? Kettő vagy több feladat párhuzamos futtatása egy rendszeren belül.
A lényeg, hogy olyan látszatot keltünk mintha egyszerre több feladat futna egy időben, pedig csak a futó feladatokból időosztással kis részeket hajtatunk végre gyors egymásutánban.

Az Arduino programozásánál egy pár dolgot be kell tartani ahhoz, hogy egy multitaskinghoz hasonlító feladatkezelést elérjünk. Nézzük is ezeket a szabályokat:
Meg kell szüntetni projekten belül a késleltetéseket. A késleltető utasításokkal a program futását állítjuk le egy bizonyos ideig, ezt egy több eszközt vezérlő progival már nem tehetjük meg, hisz
egy késleltetéssel minden eszköz kezelését leállítjuk. De mi a megoldás, hisz késleltetés időzítés kell?

Egy kicsit bonyolultabb módszerrel kell megoldani ezt a problémát, az elvárás az , hogy a program folyamatosan fusson mégis minden időben hajtódjon végre. Kézen fekvő módszer erre az időzítés:
egy rutin folyamatosan figyeli az időt és ha a megfelelő intervallum eltelt végrehajtja a hozzá tartozó függvényt. Az Arduino rendelkezik beépített időmérő függvénnyel(millis()) ami az indulás óta eltelt időt méri, erre már rá tudjuk építeni a figyelő rutinokat.

Egy egyszerű kapcsolással lemodellezzük a problémát:


A kód:
void setup(){
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
}
void loop(){

digitalWrite(4,HIGH);
delay(5000);
digitalWrite(4,LOW);
delay(1000);
digitalWrite(5,HIGH);
delay(1000);
digitalWrite(5,LOW);
delay(1000);
}

Két led 220-370ohmos előtét ellenállással a 4, 5 kimenetre kötve.
Az egyik led folyamatosan villog 5sec bekapcsolva 1sec-ig kikapcsolva, a másik led 1sec be 1sec ki. A probléma itt jelentkezik, az első led késleltetései miatt a második led csak akkor kapcsol be ha az első led késleltetésein túl van. Tehát egyszerre a kettő nem működik csak egymás után sorban.


Most nézzük ugyan ezt késleltetések kiváltásával:

int state1 = LOW,state2=HIGH; //állapot jelzésre
long prevido1,prevido2; //előző idő eltárolására

void setup(){
pinMode(4,OUTPUT);
pinMode(5,OUTPUT);
}

void loop(){
long ido = millis();//ido tároló
if((ido>prevido1+1000)&&(state1==LOW))
{
state1=HIGH;
digitalWrite(4,state1);
prevido1=ido;
}
if((ido>prevido1+5000)&&(state1==HIGH)){
state1=LOW;
digitalWrite(4,state1);
prevido1=ido;
}
if((ido>prevido2+1000)&&(state2==LOW))
{
state2=HIGH;
digitalWrite(5,state2);
prevido2=ido;
}
else if((ido>prevido2+1000)&&(state2==HIGH)){
state2=LOW;
digitalWrite(5,state2);
prevido2=ido;
}
}

Mint látható jócskán felduzzadt a programunk, viszont elértük a két led látszólagos egyszerre működését.
A programba figyeljük az idő múlását és a megfelelő időpontokban kapcsoljuk a megfelelő kimenetet. A program folyamatosan fut, nincs megakasztva.

Vizsgáljuk meg az egyik led vezérlését:

if((ido>prevido1+1000)&&(state1==LOW))

Az if ciklus első kifejezése összehasonlítja a jelenlegi időt(ido) az előző ciklushasználat idejével(prevido1) plusz még hozzá adódik a késleltetés ideje(+1000), ha a mostani idő több mint
az előző idő plusz késleltetés akkor igazzá válik a kifejezés. Az if ciklus második kifejezése a led
utolsó állapotát(state1) ellenőrzi, a LOW állapot a bekapcsoló ciklust indítja el.

{
state1=HIGH;//a led állapotjelző átkapcsolása
digitalWrite(4,state1);// kiíratás a ledre
prevido1=ido;//idő különbség nullázása
}

if((ido>prevido1+5000)&&(state1==HIGH)){

A másodi if ciklus ugyan úgy megvizsgálja az eltelt időt csak itt 5000ms múlva válik igazzá a kifejezés(led bekapcsolt állapota) és ehhez adódik hozzá a led magas állapota ami ha igaz kikapcsolja a ledet.

state1=LOW;//állapotjelző alacsony szintre állítása
digitalWrite(4,state1);//kiíratás a ledre
prevido1=ido;//idő különbség nullázása
}

Minden egyes lednél ezt a kódrészletet ismételgetjük, csak a kimenetek és az állapot jelzők
változnak. Persze két lednél ez még elmegy, de mi van akkor ha többet szeretnénk így üzemeltetni.
Evvel a módszerrel egy nagyon hosszú kód alakul ki amit lehet, hogy már a memória sem lesz képes kezelni, hála a c++-nak van rá egy hatékonyabb módszer: az Objektum Orientált Programozás
röviden OOP.

A lényege, hogy az adott eszköz (led) tulajdonságait (változóit, függvényeit) összegyűjti egy osztályba, ami egy hívó függvénnyel aktiválódik és végrehajtódik a megadott értékekkel.

A mi oldalunkról ez annyit tesz, hogy a led villogását (időzítését) csak egyszer kell leírnunk egy
osztályba, aztán csak ezt hívogatjuk a megfelelő led paraméterekkel.

Most nézzük a megvalósítást:
Első az osztálynév és az osztály változók megadása

class LedBlink
{
//ez után jönnek az osztály tagváltozóinak megadása(deklarálás)
int ledlab;
long bekapcs;
long kikapcs;
//aztán az állapotjelző változók
int state;
unsigned long prevido;
};


A következő lépés egy konstruktor létrehozása. A konstruktor ugyan azt a nevet kapja mint az osztály, a feladata viszont az osztály változóinak értékadása, inicializálása.

class LedBlink
{
int ledlab;
long bekapcs;
long kikapcs;
int state;
unsigned long prevido;


//konstruktor létrehozása
public:
// a led blinkkel kérjük be a setup függvényből a megfelelő értékeket
LedBlink(int lab, long be, long ki)
{
//meghatározzuk az osztályváltozók értékeit
ledlab = lab;
pinMode(ledlab, OUTPUT);
bekapcs = be;
kikapcs = ki;

state = LOW;
prevido = 0;
}
};


Következő lépésben létrehozzuk a függvény törzset, ami majd az elvégzendő feladatokat tartalmazza.

class LedBlink
{
int ledlab;
long bekapcs;
long kikapcs;
int state;
unsigned long prevido;

public:
LedBlink(int lab, long be, long ki)
{
ledlab = lab;
pinMode(ledlab, OUTPUT);
bekapcs = be;
kikapcs = ki;

state = LOW;
prevido = 0;
}
//függvény törzs

void frissites()
{
//pillanatnyi idő eltárolása
unsigned long ido = millis();
//idő és led állapot ellenőrzése
if((state == HIGH) && (ido – prevido >= bekapcs))
{
//bekapcsolás
state = LOW;
prevido = ido;
digitalWrite(ledlab, state);
}
else if ((state ==LOW) && (ido – prevido>= kikapcs))
{
//kikapcsolás
state = HIGH;
prevido = ido;
digitalWrite(ledlab, state);
}
}
};

Ez teljesen ugyan az mint az első programban a loop ciklus tartalmazott, de itt csak egyszer írjuk le,
mert minden egyes újabb lednél csak az alapértékeket változtatjuk és úgy hívjuk újra az osztályt.

Most nézzük a teljes progit az inicializálásokkal és hívásokkal együtt:

class LedBlink
{

int ledlab;
long bekapcs;
long kikapcs;

int state;
unsigned long prevido;

public:
LedBlink(int lab, long on, long off)
{
ledlab = lab;
pinMode(ledlab, OUTPUT);

bekapcs = on;
kikapcs = off;

state = LOW;
prevido = 0;

}


void frissites()
{

unsigned long ido = millis();

if((state == HIGH) && (ido - prevido >= bekapcs))
{
state = LOW;
prevido = ido;
digitalWrite(ledlab, state);
}
else if ((state == LOW) && (ido - prevido >= kikapcs))
{
state = HIGH;
prevido = ido;
digitalWrite(ledlab, state);
}
}

};

//itt adjuk meg a kezdőértékeket
LedBlink led1(4, 1000, 100);
LedBlink led2(5, 250, 135);

void setup()
{
}

void loop()
{
//a frissítések meghívása
led1.frissites();
led2.frissites();
}

Erről a dologról kicsit bővebben : https://learn.adafruit.com/multi-tasking-the-arduino-part-1?view=all#


A teljes progi esetleg bemásolva okozhat fennakadásokat ezért mellékelem az inot is:

multi-tasking.ino