RTOS eseményjelzők (Signals)
A fejezet tartalma:- Várakozás eseményjelzőre
- Eseményjelző küldése
- Eseményjelző törlése
- Eseményjelző küldése az első programszálnak
Az eseményjelzők számát az mbed forráskódjának rtx/TARTGET_CORTEX_M/cmsis_os.h állománya definiálja:
define osFeature_Signals 16 //maximum number of Signal Flags available per thread
Eszerint programszálanként legfeljebb
16 eseményjelzőt használhatunk! Várakozás eseményjelzőre
Az egy vagy több eseményjelzőre történő várakozást a Thread objektumosztály signal_wait() statikus publikus taggfüggvényének hívásával indíthatjuk. Ennek első, kötelező paramétere egy bitmaszk, amely megmondja, hogy mely eseményjelzőkre akarunk várakozni.osEvent signal_wait(int32_t
signals, uint32_t timeout=osWaitForever ) |
---|
Várakozás egy, vagy több
eseményjelzőre, megadott, vagy korlátlan ideig. Paraméterek: signals - bitmaszk, mely kijelöli, hogy mely eseményjelzőkre várakozunk. Ha a megadott érték nulla, akkor bármelyik eseményjelző beérkezése véget vet a várakozásnak. timeout - ezredmásodpercekben megadott maximális várakozási idő. Ha nullát adunk meg, akkor nincs várakozás, ha pedig osWaitForever a megadott érték, akkor bármennyi ideig tarthat a várakozás (ez utóbbi az alapértelmezett beállítás, ha nem adjuk meg a timeout paramétert). Visszatérési érték: A függvény visszatérési értéke az eseményjelző bitcsoport, vagy hiba esetén a megfelelő hibakód.
|
Eseményjelző küldése
A szemaforral ellentétben az eseményjelző küldése "címzetten" történik, azaz egy bizonyos programszálnak küldjük. Ezért az eseményjelző, mint kommunikációs eszköz, olyan esetekben használható, amikor a küldő programszál (vagy megszakítási rutin) tudja, hogy "kinek" kell küldeni.Az eseményjelző küldése a Thread objektumpéldányok (a megcímzett programszál) signal_set() metódusával végezhető:
int32_t signal_set( int32_t
signals) |
---|
Paraméterek: signals - bitmaszk, amely kijelöli, hogy melyik eseményjelzőt, vagy eseményjelzőket akarjuk elküldeni. Visszatérési érték: A függvény visszatérési értéke a küldés előtti állapotot jellemző eseményjelző bitcsoport, vagy hibás paraméter esetén a 0x80000000 hibakód. Megjegyzés: Ez a függvény megszakításból is hívható. |
Eseményjelző törlése
Bár az eseményjelzőre várakozó programszál által "felhasznált" eseményjelző bitek automatikusan törlődnek, az mbed API lehetőséget nyújt az elküldött eseményjelzők független törlésére is. Az eseményjelző törlése a Thread objektumpéldányok (a megcímzett programszál) signal_clr() metódusával végezhető:int32_t signal_clr( int32_t
signals) |
---|
Paraméterek: signals - bitmaszk, amely kijelöli, hogy melyik eseményjelzőt, vagy eseményjelzőket akarjuk törölni. Visszatérési érték: A függvény visszatérési értéke a küldés előtti állapotot jellemző eseményjelző bitcsoport, vagy hibás paraméter esetén a 0x80000000 hibakód. Megjegyzés: Ez a függvény megszakításból nem hívható meg! |
Mintapélda: Programszálak szinkronizálása eseményjelzővel
Az alábbi program az mbed Handbook mintapéldája. Ebben az első programszál (a main() függvény) rendszeres időközönként elküld egy eseményjelzőt a második programszálnak (thread2), amely mindaddig várakozik, amíg az eseményjelző meg nem érkezik. Minden eseményjelző megérkezésekor átbillenti LED1 állapotát.Hardver követelmények:
- FRDM-KL25Z kártya
#include "mbed.h"
#include "rtos.h"
DigitalOut led(LED1);
void led_thread(void const *argument)
{
while (true) {
// Signal flags that are reported as event are automatically cleared.
osEvent evt = Thread::signal_wait(0x1); //Wait for a signal
led = !led; //toggle LED1 state
}
}
int main (void)
{
Thread thread2(led_thread);
while (true) {
Thread::wait(1000);
thread2.signal_set(0x1); //Send a signal for thread2
}
}
A programban a main() függvény törzse az első programszál, a második
programszál törzsének pedig a led_thread
függvényt definiáltuk. Az eseményjelzőre történő várakozást a Thread::signal_wait()
függvényhívással indíthatjuk (a 0x1 bitmaszk a legkisebb sorszámú
jelzőt jelöli ki használatra). Az eseményjelző küldésénél a thread2.signal_set() függvényhívást kell használnunk. Itt thread2, mint az objektumpéldány azonosítója szabja meg, hogy "kinek" küldjük az eseményjelzőt. A függvényhívásnál megadott paraméter (0x1) ugyanaz, mint amit a másik programszálnál megadtunk a várakozás indításánál, tehát a fenti hívás hatására legkisebb helyiértékű biten tárolt jelzőbit lesz 1-be állítva. Természetesen, ha itt más bitmaszkot használnánk (pl. 0x2, vagy 0x8), akkor a thread2 programszálat nem tudnánk felkelteni Csipkerózsika álmából!
Mintapélda: Várakozás
több, vagy bármely eseményjelzőre
Bővítsük ki az előző programot a kapott eseményjelzők és/vagy hibakódok
kiíratásával, s próbáljunk több, másik, vagy bármilyen eseményjelzőre
várakozni!Hardver követelmények:
- FRDM-KL25Z kártya
#include "mbed.h"
#include "rtos.h"
DigitalOut led(LED1);
void led_thread(void const *argument) {
while (true) {
// Signal flags that are reported as event are automatically cleared.
osEvent evt = Thread::signal_wait(0); //Wait for any signal
switch(evt.status) {
case osOK:
printf("osOK\n"); //no error or event occurred
break;
case osEventSignal:
printf("osEventSignal = %#05x\n",evt.value.signals); //signal event occurred
break;
case osEventTimeout:
printf("osEventTimeout\n"); //timeout occurred
break;
case osErrorValue:
printf("osErrorValue\n"); //value of a parameter is out of range
break;
default:
printf("Unknown error flag: %#08x\n",(uint32_t)evt.status);
break;
};
led = !led;
}
}
int main (void) {
int32_t signal_mask = 0x1;
Thread thread2(led_thread);
while (true) {
Thread::wait(1000);
thread2.signal_set(signal_mask);
signal_mask <<=1;
if(signal_mask > 0x8000) signal_mask=0x1;
}
}
Megjegyzések:- Ha a Thread::signal_wait() függvényhívás visszatérési értékét egy osEvent típusú struktúrában eltároljuk (a fenti programban az evt nevű változóba), akkor annak status nevű eleme adja meg a visszatérés utáni hibakódot, a value.signals eleme pedig az eseményjelzők aktuális állapotát tartalmazza.
- Ha a Thread::signal_wait() függvényhívás első paramétere (a bitmaszk) nulla, akkor bármelyik eseményjelző beérkezése véget vet a várakozásnak. A fenti program módosítatlan változata sorra küldi az 1, 2, 4, 8, stb. bitmaszkkal megadott eseményjelzőket, s thread2 mindegyiket elfogadja.
- Ha a Thread::signal_wait() függvényhívás első paramétere (a bitmaszk) egynél több helyiértéken tartalmaz egyest (pl. 0x3), akkor a várakozás addig tart, amíg az összes megkívánt eseményjelző be nem érkezik.
- Az eseményjelzők küldése történhet egyesével, vagy csoportosan is. Utóbbi esetben a thread2.signal_set(signal_mask) függvényhívásban a signal_mask nevű bitmaszk egynél több helyiértéken kell, hogy egyest tartalmazzon.
- Töltsük le a 2. listán látható programot, indítsuk el, s egy terminálablakban figyeljük a kírásokat!
- Módosítsuk a programot úgy, hogy Thread::signal_wait(2,0) álljon benne! A függvényhívás első paramétere a 0x2 eseményjelző figyelését írja elő, a második paraméter szerint a várakozási idő nulla. Ekkor az osOK hibakód lesz a visszatérési érték, ha a fenti függvényhívás kiadásakor még nincs beállítva a 0x2 eseményjelző.
- Módosítsuk a programot úgy, hogy Thread::signal_wait(2,500) álljon benne! Ekkor az osEventTimeout hibakód lesz a visszatérési érték, ha a 0x2 eseményjelző 500 ezredmásodpercen belül nem kerül beállításra.
- Módosítsuk a programot úgy, hogy Thread::signal_wait(0x10000) álljon benne! Ez érvénytelen bitmaszk (a 16 biten túlterjeszkedik), ezért az osErrorValue hibakód lesz a visszatérési érték.
- Módosítsuk a programot úgy, hogy Thread::signal_wait(0x5), vagy bármilyen egynél több biten 1-et tartalmazó szám álljon benne! Ekkor csak az összes előírt eseményjelző beérkezése után szűnik meg a várakozás (LED1 ritkábban vált állapotot).
- Módosítsuk a programot úgy, hogy thread2.signal_set(signal_mask)
függvényhívásnál 0x5, vagyis ugyanaz legyen a bitmaszk, amit az 5.
feladatnál a Thread::signal_wait()
paramétereként
megadtunk! Ekkor egyszerre több eseményjelzőre várunk, de azok
egyszerre, csoportos küldéssel meg is érkeznek, ezért LED1 ugyanolyan
ütemben villog, mint az 1. feladatnál (állapotváltás 1
másodpercenként).
Eseményjelző küldése az első programszálnak
Komoly gonddal kell szembenéznünk, ha a main() függvényben, vagyis az alapértelmezett első programszálban indítunk várakozást eseményjelzőre. Hogyan tudunk az első programszálra hivatkozni, amikor annak nincs explicit módon megadott, Thread típusú neve? Nos, a megoldást a Thread objektumosztály gettid() statikus publikus tagfüggvénye nyújtja számunkra, amellyel lekérdezhető a futó programszál azonosítója. A lekérdezést természetesen a main() függvényben kell végezni, s az eredményt egy globális változóban kell eltárolni, hogy a többi programszál is hozzáférhessen.A programszálak azonosítója osThreadId típusú. Ezt a típust a fentebb már említett cmsis_os.h definiálja, s lényegében egy programszál vezérlő blokkra mutató pointer. Ilyen struktúrára mutató pointer lesz tehát a Thread::gettid() függvényhívás értéke, amelyet az általunk deklarált mainThreadID nevű változóban tárolunk el.
A következő problémánk el lesz: hogyan hívjuk meg a névtelen első programszál signal_set() metódusát? Itt az mbed API alatti rétegekbe kell belenyúlni, s a CMSIS RTOS API kínál egy olyan függvényhívási lehetőséget: osSignalSet(), amelynek egy osThreadID típusú azonosító átadható, s vele a kívánt eseményjelző, vagy eseményjelző csoport beállítása elvégezhető. Szintaktikája:
int32_t osSignalSet(osThreadId
thread_id, int32_t
signals) |
---|
Paraméterek: thread_id - a megcímzett programszál azonosítója, melyet pl. Thread::gettid() hívással kaphatunk meg. signals - bitmaszk, amely kijelöli, hogy melyik eseményjezőt, vagy eseményjelzőket akarjuk elküldeni. Visszatérési érték: A függvény visszatérési értéke a küldés előtti állapotot jellemző eseményjelző bitcsoport, vagy hibás paraméter esetén a 0x80000000 hibakód. Megjegyzés: Ez a függvény megszakításból is hívható. |
Mintapélda:
Eseményjelző küldése az első programszálnak
Az alábbi mintaprogram az első példánk megfordítása: itt a main()
függvényben várunk eseményjelzőre és kapcsolgatjuk LED1-et, míg a
második programszálban periodikusan (egy másodpercnyi várakozás után)
küldünk eseményjelzőt a fő programszálnak. Hardver követelmények:
- FRDM-KL25Z kártya
#include "mbed.h"
#include "rtos.h"
DigitalOut led(LED1);
osThreadId mainThreadID;
void signal_thread(void const *argument) {
while (true) {
Thread::wait(1000);
osSignalSet(mainThreadID, 0x1);
}
}
int main (void) {
mainThreadID = Thread::gettid();
Thread thread(signal_thread);
while (true) {
// Signal flags that are reported as event are automatically cleared.
osSignalWait(0x1, osWaitForever);
led = !led;
}
}