XX. "Multitasking"
aneb více úloh současně
V první lekci Blikáme LEDkou jsem zadal úkol napsat program na současné blikání dvou LED různou frekvencí. Abyste to byli schopni zvládnout s dosavadními znalostmi, tak byly periody blikání zvoleny "hezky". Pokud to budou libovolné hodnoty, úloha se podstatně ztíží. Řešení nejen této úlohy je tématem dnešní lekce.
|
Motivace
- Potřeba současného zpracovávání více úloh
- Oddělení nezávislých částí programu
- Vytvoření samostatných softwarových opakovatelně použitelných modulů
Vánoční stromeček - 7 "současně" běžících úloh
Přímočaré řešení
Vrátíme se tedy k původní úloze a ukážeme si "jednoduché" řešení.
Úkolem je blikat současně dvěmi LED. Přičemž jedna bliká s periodu 1s (0,5s svítí
a 0,5s je zhasnutá) a druhá s periodou 0,5s (0,25s svítí a 0,25s je zhasnutá).
Požadovaný průběh signálů na výstupech mikrokontroleru si můžeme znázornit
v grafu:
|
Z obrázku vidíme, že:
v čase 0s: | zapneme obě LED |
v čase 0,25s: | vypneme LED2 |
v čase 0,5s: | vypneme LED1 a zapneme LED2 |
v čase 0,75s: | vypneme LED2 |
V čase 1s: | zapneme obě LED (jako v čase 0s ... děj se začíná opakovat) |
Musíme tedy popsat průběh jednoho cyklu (trvajícího 1s). Program by tedy mohl vypadat takto:
Nevýhody
- Při změně hodnot period blikání, nutno program předělat.
- Pokud budou požadované hodnoty period blikání nesoudělná čísla, pak „neřešitelné“.
- Program nic rozumného nedělá.
- - samotná operace rozsvícení nebo zhasnutí LED trvá několik mikrosekund a pak mikrokontroler 250 milisekund čeká (nic užitečného nedělá). Přitom by mohl vykonávat nějakou jinou úlohu.
Řešení
Od teď je
delay()
sprosté slovo!
A ta se ve slušné společnosti nepoužívají !!!
Polling (dotazování)
Jednou z jednoduchých a velmi elegantních metod je tzv. polling (dotazování). Program tak, jak jsme
už zvyklí, probíhá ve smyčce. Testuje příznaky událostí (podmínky, kdy má něco nastat) a
pokud je podmínka splněna, tak provede nějakou akci. Takto se může "současně" zpracovávat
větší počet úloh, které jsou na sobě nezávislé.
Ukážeme si to příkladu našich dvou blikajících LED. Podmínkou pro změnu stavu výstupu řídicího
LED je dosažení určitého času. Využijeme toho, že v arduinu "běží" čas
(funkce millis() vrací počet ms od "spuštění" programu). Tzn. stačí porovnat aktuální
čas s určenou hodnotou a víme, zda se má stav LED změnit.
Nejprve si to ukážeme pro jednu led:
1. vytvoříme si proměnnou (časovou značku), do které si uložíme čas, od kterého budeme čas měřit |
2. ve smyčce Loop kontroluji, zda čas od nastavení časové značky už dosáhl požadované doby |
3. pokud ano, provedu akci a posunu časovou značku o požadovanou periodu |
Tento program, dělá to samé, co náš úplně první program. S tím rozdílem, že
mikrokontroler nikde nečeká a tedy může dělat i jinou činnost.
Pro více současných akcí, uděláme pro každou událost vlastní časovou značku,
jinak vše zůstane stejné.
Vyčlenění částí kódu do funkcí
- Program pro Arduino je rozdělen na část nastavení (setup) a opakovaně prováděnou část (loop).
- Z částí kódu jednotlivých úloh uděláme funkce, které provádějí odpovídající činnost .
- Tím dojde k výraznému zpřehlednění programu.
Rozdělení úloh do souborů
Funkce jednotlivých úloh umístíme do samostatných souborů.
- Program se zkrátí
- Realizaci těchto úloh můžeme používat opakovaně (tj. vytvořit si vlastní knihovnu).
- Vlastní kód je v souboru XXX.cpp
- V hlavičkovém souboru XXX.h jsou definice, pro „přístup z hlavního programu“.
led5.ino
led.cpp
led.h
#include "led.h"
void setup() {
LED_nastav(500);
}
void loop() {
LED_delej();
}
void setup() {
LED_nastav(500);
}
void loop() {
LED_delej();
}
#include "led.h"
#define LED 3
uint32_t _led_cas; // promenna s aktualni hodnotou casove znacky (32b)
uint16_t _led_perioda; // promenna s aktualni hodnotou periody (16b)
void LED_nastav(uint16_t x) {
pinMode(LED, OUTPUT); // nastaveni pinu 2 jako vystup
_led_perioda = x; // ulozeni periody
_led_cas = millis(); // ulozeni aktualniho casu jako casove znacky
}
void LED_delej() {
if(millis() - _led_cas >= _led_perioda) { // je-li doba od posledni akce >= perioda
digitalWrite(LED, !digitalRead(LED)); // zmen hodnotu vystupu
_led_cas += _led_perioda; // posun casovou znacku o periodu
}
}
#define LED 3
uint32_t _led_cas; // promenna s aktualni hodnotou casove znacky (32b)
uint16_t _led_perioda; // promenna s aktualni hodnotou periody (16b)
void LED_nastav(uint16_t x) {
pinMode(LED, OUTPUT); // nastaveni pinu 2 jako vystup
_led_perioda = x; // ulozeni periody
_led_cas = millis(); // ulozeni aktualniho casu jako casove znacky
}
void LED_delej() {
if(millis() - _led_cas >= _led_perioda) { // je-li doba od posledni akce >= perioda
digitalWrite(LED, !digitalRead(LED)); // zmen hodnotu vystupu
_led_cas += _led_perioda; // posun casovou znacku o periodu
}
}
#ifndef __LED_H__
#define __LED_H__
#include <arduino.h>
void LED_nastav(uint16_t x);
void LED_delej();
#endif
#define __LED_H__
#include <arduino.h>
void LED_nastav(uint16_t x);
void LED_delej();
#endif
Zapouzdření do objektů
- Problémem je, pokud potřebujeme realizovat dvě stejné úlohy na jiných datech (např. pinech).
- Jedno z řešení je použití objektů.
- Vytvoříme si odpovídající počet objektů daného typu. Použitím jejich metod se vždy přistupuje ke správným datům.
led7.ino
led_blikani.cpp
led_blikani.h
#include "led_blikani.h"
LED led1(500, 3);
LED led2(40, 4);
LED led3(1100, 2);
LED led4(2400, 5);
void setup() {
}
void loop() {
led1.delej();
led2.delej();
led3.delej();
led4.delej();
}
LED led1(500, 3);
LED led2(40, 4);
LED led3(1100, 2);
LED led4(2400, 5);
void setup() {
}
void loop() {
led1.delej();
led2.delej();
led3.delej();
led4.delej();
}
#include "led_blikani.h"
#define ON 0
#define OFF 1
LED::LED(uint16_t _perioda, uint8_t _pin) {
pin = _pin;
perioda = _perioda;
pinMode(pin, OUTPUT);
digitalWrite(pin, OFF);
}
void LED::delej() {
if (millis() - cas >= perioda) {
digitalWrite(pin, !digitalRead(pin));
cas += perioda;
}
}
#define ON 0
#define OFF 1
LED::LED(uint16_t _perioda, uint8_t _pin) {
pin = _pin;
perioda = _perioda;
pinMode(pin, OUTPUT);
digitalWrite(pin, OFF);
}
void LED::delej() {
if (millis() - cas >= perioda) {
digitalWrite(pin, !digitalRead(pin));
cas += perioda;
}
}
#ifndef __LED_BLIKANI_H__
#define __LED_BLIKANI_H__
#include <Arduino.h>
class LED {
public:
LED(uint16_t, uint8_t);
void delej();
private:
uint32_t cas;
uint16_t perioda;
uint8_t pin;
};
#endif
#define __LED_BLIKANI_H__
#include <Arduino.h>
class LED {
public:
LED(uint16_t, uint8_t);
void delej();
private:
uint32_t cas;
uint16_t perioda;
uint8_t pin;
};
#endif
Zdrojové kódy jednotlivých příkladů
[1] led 1
[2] led 2
[3] led 3
[4] led 4
[5] led 5
[6] led 6
[7] led 7
[8] led + sonar