Abstrakt: Portace uC/OS-II na platformu FITKit
Více o uC/OS-II je možno nalézt na http://www.micrium.com/page/products/rtos/os-ii
uC/OS-II je jádro preemptivního real-time multitasking operačního systému založeného na prioritách probíhajících procesů. Je vytvořen společností Micros a je poskytován zdarma pro výukové účely. Je především určen pro vestavěné systémy. Protože se jedná o operační systém schopný běžet na různých mikroprocesorových platformách, je pro jeho zprovoznění nutné provést portaci pro konkrétní mikrokontroler.
K portaci na konkrétní platformu slouží tzv. procesorově specifické zdrojové soubory (os_cpu.h, os_cpu_c.c a os_cpu_a.s). Ty je nutné upravit pro konkrétní mikrokontroler především z důvodu jejich rozdílné instrukční sady a složení registrů.
Hlavičkový soubor os_cpu.h
obsahuje procesorově a implementačně specifické konstanty (definované direktivami #define
), makra a definice datových typů (typedef
). Datové typy jsou definovány především z důvodu jejich různých délek na různých platformách.
typedef unsigned char BOOLEAN; typedef unsigned char INT8U; /* Unsigned 8 bit quantity */ typedef signed char INT8S; /* Signed 8 bit quantity */ typedef unsigned int INT16U; /* Unsigned 16 bit quantity */ typedef signed int INT16S; /* Signed 16 bit quantity */ typedef unsigned long INT32U; /* Unsigned 32 bit quantity */ typedef signed long INT32S; /* Signed 32 bit quantity */ typedef float FP32; /* Single precision floating point */ typedef double FP64; /* Double precision floating point */ typedef unsigned int OS_STK; /* Each stack entry is 16-bit wide */ typedef unsigned int OS_CPU_SR; /* Define size of CPU status register (SR = 16 bits) */
Dále tento soubor obsahuje další velice významnou definici následujících maker:
#define OS_ENTER_CRITICAL() (cpu_sr = OSCPUSaveSR()) /* Disable interrupts */ #define OS_EXIT_CRITICAL() (OSCPURestoreSR(cpu_sr)) /* Enable interrupts */
Jedná se o makra umožňující zablokování a odblokování systému přerušení při vstupu do kritické sekce, neboť přerušení při výskytu v kritické sekci není žádoucí. Proto každý vstup do kritické sekce by měl být uvozen voláním OS_ENTER_CRITICAL()
a ukončen OS_EXIT_CRITICAL()
.
Protože však pouhé zablokování a odblokování přerušení může způsobit jejich nežádoucí odblokování (před vstupem do kritické sekce byla přerušení zablokována, OS_EXIT_CRITICAL()
však může provést jejich nechtěné odblokování), je vhodnějším řešením při volání OS_ENTER_CRITICAL()
uložit obsah registru SR (Status Register), zablokovat přerušení a při opuštění kritické sekce obnovit stav přerušení na základě uloženého SR registru.
Hlavičkový soubor os_cpu.h
obsahuje také neméně důležitou konstantu OS_STK_GROWTH, která určuje, jakým způsobem budou data ukládána na zásobník. V případě nastavení této konstanty na 0, bude zápis do paměti prováděn od nižších po vyšší adresy, pokud bude nastavena na 1, pak naopak. Pro potřeby MSP430 je použito #define OS_STK_GROWTH 1
.
OS_TASK_SW()
je makro vyvolávající přepnutí kontextu. Při přepnutí kontextu je potřeba uložit registry pozastaveného procesu a obnovení registrů procesu, který bude následně probíhat. Je důležité také vědět, že přepnutí kontextu se chová obdobně jako přerušení. Pokud tedy cílová architektura podporuje softwarová přerušení, je možno OS_TASK_SW()
vyvolat tímto způsobem. V takovém případě se ve vektoru přerušení musí nacházet procedura OSCtxSw()
definovaná v os_cpu_a.s
. Pro platformy nepodporující softwarová přerušení a náš případ, je možné pouze definovat OS_TASK_SW()
jako volání procedury OSCtxSw()
:
#define OS_TASK_SW() OSCtxSw()
OSTickISR()
. Dále jsou v tomto souboru definovány těla funkcí OSCPUSaveSR()
a OSCPURestoreSR()
, které jsou volány již vysvětlenými makry OS_ENTER_CRITICAL()
a OS_EXIT_CRITICAL()
.
OSStartHighRdy()
připraví ke spuštění úlohu s nejvyšší prioritou při startu OS. Tato funkce musí zavolat OSTaskSwHook()
(probíhá de facto přepnutí kontextu), musí nastavit OSRunning
na TRUE
, identifikující běěh jádra, a samozřejmě přepnout na spouštěný proces načtením odpovídajícího zásobníku (registr R1 je na MSP430 Stack Pointer).
.global OSStartHighRdy .type OSStartHighRdy,@function OSStartHighRdy: CALL #OSTaskSwHook MOV.B #1, &OSRunning ; kernel running MOV.W r1, &OSISRStkPtr ; save interrupt stack MOV.W &OSTCBHighRdy, R13 ; load highest ready task stack MOV.W @R13, r1 POPALL RETI ; emulate return from interrupt
OSCtxSw()
je funkcí umožňující přepnutí kontextu. Její funkce již byla vysvětlena spolu s OS_TASK_SW()
. Při přepnutí kontextu je však potřeba uložit registr R2 (Status Register) a obecné registry R4 až R15 na zásobník, uložit současný Stack Pointer (registr R1) do tabulky TCB, zavolat OSTaskSwHook()
, protože probíhá přepnutí kontextu, vybrat proces s vyšší prioritou a obnovit jeho registry uložené na jeho zásobníku.
.global OSCtxSw .type OSCtxSw, @function OSCtxSw: PUSH.W r2 PUSHALL MOV.W &OSTCBCur, R13 ; OSTCBCur->OSTCBStkPtr = SP MOV.W r1, 0(R13) CALL #OSTaskSwHook MOV.B &OSPrioHighRdy, R13 ; OSPrioCur = OSPrioHighRdy MOV.B R13, &OSPrioCur MOV.W &OSTCBHighRdy, R13 ; OSTCBCur = OSTCBHighRdy MOV.W R13, &OSTCBCur MOV.W @R13, r1 ; SP = OSTCBHighRdy->OSTCBStkPtr POPALL RETI ; Return from interrupt.
OSIntCtxSw()
je funkcí způsobující přepnutí kontextu z obsluhy přerušení. Chová se obdobně jako OSCtxSw()
s tím rozdílem, že neukládá současný kontext.
.global OSIntCtxSw .type OSIntCtxSw, @function OSIntCtxSw: CALL #OSTaskSwHook MOV.B &OSPrioHighRdy, R13 ; OSPrioCur = OSPrioHighRdy MOV.B R13, &OSPrioCur MOV.W &OSTCBHighRdy, R13 ; OSTCBCur = OSTCBHighRdy MOV.W R13, &OSTCBCur MOV.W @R13, r1 ; SP = OSTCBHighRdy->OSTCBStkPtr POPALL RETI ; return from interrupt
Funkce OSTickISR()
je jednou z esenciálních součástí uC/OS-II, neboť je potřeba pro funkčnost časovačů a zpoždění v systému. Jedná se o obsluhu přerušení vyvolaném hardwarovým časovačem (Watchdog Timer).
Protože se jedná o obsluhu přerušení, je potřeba na začátku uložit všechny obecné registry R4 až R15 na zásobník, vyřadit časové přerušení. Pokud nedošlo k žádnému dalšímu přerušení, uložíme SP současné úlohy a načteme SP pro obsluhu tohoto přerušení. Dále je potřeba OS informovat o tom, že k přerušení došlo - k tomu slouží globální proměnná OSIntNesting
. Následuje opětovné povolení časového a obecných přerušení. Poté je volána funkce OSTimeTick
, což je obslužná rutina pro zpracování hodinového tiknutí. Po jejím průběhu je zapotřebí opět vyřadit obecná přerušení a zavolat OSIntExit()
, která mimo jiné sníží OSIntNesting
. Pokud se tato proměnná nsáledně rovná nule, všechna přerušení byla obsloužena a my můžeme obnovit SP původní úlohy. Nakonec proběhne obnova obecných registrů R4 až R15 a návrat z obsluhy přerušení.
.global vector_fff4 vector_fff4: ; wd timer ISR PUSHALL BIC.B #0x01, &0 ; disable wd timer interrupt CMP.B #0, &OSIntNesting ; if (OSIntNesting == 0) JNE WDT_ISR_1 MOV.W &OSTCBCur, R13 ; save task stack MOV.W r1, 0(R13) MOV.W &OSISRStkPtr, r1 ; load interrupt stack WDT_ISR_1: INC.B &OSIntNesting ; increase OSIntNesting BIS.B #0x01, &0 ; enable wd timer interrupt EINT ; enable general interrupt to allow for interrupt nesting CALL #OSTimeTick ; call ticks routine DINT ; IMPORTANT: disable general interrupt BEFORE calling OSIntExit() CALL #OSIntExit CMP.B #0, &OSIntNesting ; if (OSIntNesting == 0) JNE WDT_ISR_2 MOV.W &OSTCBHighRdy, R13 ; restore task stack SP MOV.W @R13, r1 WDT_ISR_2: POPALL RETI ; return from interrupt
Funkci nemáme pojmenovanou jako OSTickISR()
, ale vector_fff4
. To je z toho důvodu, že MSP430 má na adrese FFF4h
uloženu právě obsluhu přerušení od Watchdog časovače.
Funkce OSCPUSaveSR()
a OSCPURestoreSR()
jsou již popsané metody pro uložení Status Registru a vyřazení/obnovení systému přerušení při vstupu do kritických sekcí. Předpokládáme, že přes R15 bude předáván výsledek funkce OSCPUSaveSR()
a přijímán parametr funkce OSCPURestoreSR()
.
.global OSCPUSaveSR .type OSCPUSaveSR, @function OSCPUSaveSR: MOV.W r2, R15 DINT RET .global OSCPURestoreSR .type OSCPURestoreSR, @function OSCPURestoreSR: MOV.W R15, r2 RET
Soubor os_cpu_c.c
obsahuje definice funkcí OSTaskStkInit()
, OSTaskCreateHook()
, OSTaskDelHook()
, OSTaskSwHook()
, OSTaskStatHook()
a OSTimeTickHook()
. Nicméně pro funkčnost uC/OS-II je nezbytně nutné implementovat pouze jednu z nich - OSTaskStkInit()
.
OSTaskStkInit()
je funkcí, která inicializuje zásobník při vytváření úlohy a to tak, že simuluje chování "jako by právě nastalo přerušení" - na zásobníku je uložena adresa kódu úlohy, Status Registr a obecné registry R4 až R15. Předpokládáme, že přes R15 je předáván parametr úlohy.
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *p_arg, OS_STK *ptos, INT16U opt) { INT16U *top; opt = opt; top = (INT16U *)ptos; top--; *top-- = (INT16U)(task); /* Interrupt return pointer */ *top-- = (INT16U)0x0008; /* Status register */ *top-- = (INT16U)(p_arg); /* Pass 'p_arg' through register R15 */ *top-- = (INT16U)0x1414; *top-- = (INT16U)0x1313; *top-- = (INT16U)0x1212; *top-- = (INT16U)0x1111; *top-- = (INT16U)0x1010; *top-- = (INT16U)0x0909; *top-- = (INT16U)0x0808; *top-- = (INT16U)0x0707; *top-- = (INT16U)0x0606; *top-- = (INT16U)0x0505; *top = (INT16U)0x0404; return ((OS_STK *)top); }
Ostatní funkce definované v tomto souboru slouží pro potřeby uživatelů uC/OS-II rozšířit funkcionalitu OS, jejich těla tedy mohou zůstat prázdná. Pro naše potřeby je nebylo třeba více rozvíjet, přesto zde však popíšeme, kdy je jaká funkce volána.
OSTaskCreateHook()
je volána kdykoli je vytvořena nová úloha. Je volána již po vytvoření PCB, avšak ještě před spuštěním plánovače úloh. Taktéž je ještě vyřazen systém přerušení. Jako parametr je této funkci předán ukazatel na PCB vytvářené úlohy. Tato funkce je generována pouze v případě, že je nastavena konstanta OS_CPU_HOOKS_EN na 1 v hlavičkovém souboru os_cfg.h
.
OSTaskDelHook()
je volána kdykoli je úloha odstraněna ze systému. Obdobně jako OSTaskCreateHook()
, i tato funkce obdrží jako parametr ukazatel na PCB právě odstraňované úlohy. Tato funkce je generována pouze v případě, že je nastavena konstanta OS_CPU_HOOKS_EN na 1 v hlavičkovém souboru os_cfg.h
.
OSTaskSwHook()
je volána vždy, když dochází k přepnutí kontextu. Tato funkce má přístup ke globálním proměnným obsahujícím ukazatele na PCB aktuální úlohy a úlohy, na kterou se kontext přepíná. Je jisté, že při volání této funkce jsou vždy vyřazena přerušení. Tato funkce je generována pouze v případě, že je nastavena konstanta OS_CPU_HOOKS_EN na 1 v hlavičkovém souboru os_cfg.h
.
OSTaskStatHook()
je volána každou sekundu funkcí OSTaskStat()
(statistická funkce), čímž lze rozšířit její funkcionalitu. Tato funkce je generována pouze v případě, že je nastavena konstanta OS_CPU_HOOKS_EN na 1 v hlavičkovém souboru os_cfg.h
.
OSTimeTickHook()
je volána funkcí OSTimeTick()
při každém sytémovém tiknutí, resp. těsně před ním. Tato funkce je generována pouze v případě, že je nastavena konstanta OS_CPU_HOOKS_EN na 1 v hlavičkovém souboru os_cfg.h
.
Statistická úloha je úloha OS, která umožňuje shromažďování statistik běhu operačního systému, resp. využití mikroprocesoru. Tato hodnota je uložena v proměnné OSCPUUsage
, jednotkou jsou procenta.
Pokud je statistická úloha využita v systému, je potřeba, aby byla vytvořena voláním funkce OSStatInit()
pouze v jedné a vždy první úloze OS. Tato úloha bývá označována jako "startovací úloha" a v jejím těle jsou kromě statistické úlohy a její samotné funkčnosti vytvořeny také další úlohy. Ukázka, jak je volána, bude nastíněna v popisu vytvořené ukázkové aplikace nad uC/OS-II.
Na tomto místě je nutné podotknout, že statistická úloha je generována pouze v případě, že je konstanta OS_TASK_STAT_EN nastavena na 1. Dále je nutné zmínit, že na mikrokontroleru MSP430F168, což odpovídá verzi FITKit 1.2, se nepodařilo statistickou úlohu zprovoznit. Protože však MSP430F168 obsahuje velice malou programovou paměť a není reálné zprovoznit nějakou výrazně užitečnou a rozsáhlou aplikaci nad uC/OS-II, tímto problémem se nebudeme dále zabývat.
Protože se podařilo rozchodit uC/OS-II na platformách FITKit 1.2 i 2.0 (ač pro 1.2 s jistými omezeními), jsou pro jednotlivé verze FITKitu aplikace rozdílné. Kód aplikace se nachází v souboru app.c
. Protože překladový systém QDevKit neumí na základě verze FITKitu vybrat soubory, jsou obě aplikace definovány v tomto souboru a direktivami #if
je při překladu vybírána správná aplikace pro konkrétní platformu. To sice vede k méně přehlednému kódu, zachováme však funkčnost obou dvou verzí platformy FITKit.
Tuto aplikaci implementovali Ing. Jiří Novotňák a Ing. Tomáš Novotný v rámci projektu do předmětu ROS. Tuto aplikaci jsme se tedy rozhodli ponechat pro verzi FITKitu 1.2, neboť, jak jsme již zmínili, jiná, složitější aplikace vyvinout pro MSP430F168 nejde, pro ukázku funkčnosti uC/OS-II však postačuje.
Aplikace zpracovává dvě úlohy - jedna bliká zelenou diodou D5, druhá červenou. Obě diody blikají s různou frekvencí a časovými rozestupy.
/* * Uloha blikajici se zelenou LED diodou (D5) * Kazdych 100ms prepina stav diody */ void green_task(void *data) { P1OUT &= ~BIT0; for(;;) { OSTimeDlyHMSM(0,0,0,100); P1OUT ^= BIT0; } } /* * Uloha blikajici s cervenou LED diodou (D6) * Kazdou sekundu blikne na 100ms */ void red_task(void *data) { P5OUT |= BIT0; for(;;) { OSTimeDlyHMSM(0,0,0,900); P5OUT &= ~BIT7; OSTimeDlyHMSM(0,0,0,100); P5OUT |= BIT7; } }
Pro inicializaci hardwaru (především clock system) je napsána funkce my_initialize_hardware()
, která je inspirovaná kódem z knihovny libfitkit
.
Funkce main()
programu vypadá pro tuto aplikaci následovně:
int main() { // Inicializace HW my_initialize_hardware(); // Smer pinu diod P1DIR |= BIT0; // D5 P5DIR |= BIT7; // D6 // Vypnuti diod P1OUT |= BIT0; P5OUT |= BIT7; // Inicializace OS a uloh OSInit(); // Uloha blikani zelene diody OSTaskCreate(green_task, (void *)0, (void *)&TaskGreenLEDStk[TASK_STK_SIZE - 1], 0); // Uloha blikani cervene diody OSTaskCreate(red_task, (void *)0, (void *)&TaskRedLEDStk[TASK_STK_SIZE - 1], 1); // Spusteni watchdogu WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL; // Timer mode, predeleni 32768 IE1 |= WDTIE; // Povoleni general interrupt _EINT(); // Start multitaskingu OSStart(); return 0; }
Z hlediska uC/OS-II jsou důležitá volání OSInit()
, OSTaskCreate()
a OSStart()
. OSInit()
provede inicializaci operačního systému a jeho základních úloh. Voláním OSTaskCreate()
jsou do systému přidány úlohy blikání zelené a červené diody. OSStart()
pak provede spuštění multitaskingu. Je důležité, aby před spuštěním multitaskingu byla povolena obecná přerušení a spuštěn Watchdog časovač! Pokud tomu tak nebude, pak uC/OS-II nebude fungovat správně.
Pro vytvoření ukázkové aplikace na FITKit 2.0 jsme se rozhodli využít knihovny libfitkit
, což na FITKitu 1.2 nebylo možné z důvodu malé kapacity MSP430F168. Ukázková aplikace tedy bude schopna zasílat zprávy na terminál, reagovat na stisk určitých kláves, ovládat svícení diod D5 a D6 a ovládat výpis na displeji. Navíc, pokud je v souboru os_cfg.h
nastavena konstanta OS_TASK_STAT_EN na 1, bude spuštěna statistická funkce a po stisku určité klávesy budou údaje o zatížení mikroprocesoru vypisovány na displej/terminál. Při vytváření aplikace jsme se částečně inspirovali u ukázkové aplikace nad FreeRTOS.
Stav, v jakém se aplikace nachází, budeme ukládat do globálních proměnných:
volatile char last_ch; unsigned char c_pressed = 0; // priznak stisku klavesy 'C' unsigned char d_pressed = 0; // priznak stisku klavesy 'D' unsigned char load_pressed = 0; // priznak stisku klavesy '*' unsigned char previous_c_pressed = 0; // priznak zpracovaneho stisku klavesy 'C' unsigned char light_red = 1; // priznak stisku klavesy 'A' - dioda D6 unsigned char light_green = 1; // priznak stisku klavesy 'B' - dioda D5
První úlohou, kterou bude OS zpracovávat, bude práce s klávesnicí. Ta bude v případě stisku klávesy 'A' rozsvěcet/zhasínat červenou diodu D6 a odesílat o tom zprávu na terminál, při stisku klávesy 'B' rozsvěcet/zhasínat zelenou diodu D5 a odesílat o tom zprávu na terminál. Při stisku klávesy 'C' bude nastavován příznak jejího stisknutí a bude o tom odeslána zpráva na terminál - klávesa 'C' bude ovládat zapnutí/vypnutí displeje. Při stisku klávesy 'D' bude vybíráno, zda na displej bude vypisován údaj o rozsvícených/zhasnutých diodách nebo zda bude vypisována zátěž mikroprocesoru vypočítaná statistickou úlohou (pokud je generována). Při stisku klávesy '*' bude nastaven příznak generování zátěže v rámci zátěžové úlohy.
void keyboardTask(void *param) { param = param; char ch; last_ch = 0; keyboard_init(); // inicializace klavesnice for (;;) { ch = key_decode(read_word_keyboard_4x4()); // dekodovani znaku z klavesnice if (ch != last_ch) { last_ch = ch; if (ch != 0) { switch (ch) { case 'A': // stisk klavesy 'A' set_led_d6(light_red); // nastaveni rozsviceni/zhasnuti diody if (light_red == 1) { // dioda byla rozsvicena term_send_str_crlf("'A': Turn on red light D6."); } else { // dioda byla zhasnuta term_send_str_crlf("'A': Turn off red light D6."); } // nastaveni pristiho stavu diody light_red = (light_red == 0 ? 1 : 0); break; case 'B': // stisk klavesy 'B' set_led_d5(light_green); // rozsviceni/zhasnuti diody if (light_green == 1) { // dioda je rozsvicena term_send_str_crlf("'B': Turn on green light D5."); } else { // dioda je zhasnuta term_send_str_crlf("'B': Turn off green light D5."); } // nastaveni pristiho stavu diody light_green = (light_green == 0 ? 1 : 0); break; case 'C': // stisk klavesy 'C' c_pressed = (c_pressed == 0 ? 1 : 0); // nastaveni priznaku if (c_pressed) { // stisknuta klavesa -> vypnuty displej term_send_str_crlf("'C': Display OFF"); } else { // spusteny displej term_send_str_crlf("'C': Display ON"); } break; case 'D': d_pressed = (d_pressed == 0 ? 1 : 0); // nastaveni priznaku if (d_pressed) { // stisknuta klavesa -> vypis statistik term_send_str_crlf("'D': CPU Usage Monitoring ON"); } else { // vypnuty vypis statistik term_send_str_crlf("'D': CPU Usage Monitoring OFF"); } break; case '*': load_pressed = (load_pressed == 0 ? 1 : 0); // nastaveni priznaku if (load_pressed) { // stisknuta klavesa -> zatezova uloha term_send_str_crlf("'*': Load task ON"); } else { // vypnuta zatezova uloha term_send_str_crlf("'*': Load task OFF"); } break; default: break; } } } OSTimeDlyHMSM(0,0,0,100); // zpozdeni 100ms } }
Druhá úloha bude mít za úkol ovládání displeje na základě příznaků stisknutých kláves. Na základě stisku kláves/stavu diod bude na displej implicitně vypisovat, v jakém stavu se diody nachází (např. D5:OFF D6:ON). V případě, že je nastaven příznak po stisknutí klávesy 'C', displej je považován za vypnutý a nevypisuje se na něj nic. V případě, že je nastaven příznak klávesou 'D', a není displej vypnutý klávesou 'C', je na displej místo údajů o diodách vypisován stav zatížení mikroprocesoru (např. CPU 98%). V případě, že je nastaven příznak klávesy '*', je řízení displeje ponecháno na zátěžové úloze loadTask()
.
void displayTask(void *param) { LCD_init(); // inicializace LCD for (;;) { if (d_pressed) { // nastaven priznak 'D' #if OS_TASK_STAT_EN // je generovana statisticka uloha char result[20]; sprintf (result, "CPU %d%c", OSCPUUsage, '\%'); // format vystupu stat.funkce if (!c_pressed && !load_pressed) { // pokud neni priznak 'C' a '*' if (previous_c_pressed) previous_c_pressed = 0; LCD_write_string(result); // vypis na displej } term_send_str_crlf(result); // odeslani dat na terminal #else if (!c_pressed && !load_pressed) { if (previous_c_pressed) previous_c_pressed = 0; LCD_write_string("STATS DISABLED"); } term_send_str_crlf("STATS DISABLED"); #endif OSTimeDlyHMSM(0,0,1,0); } else { // neni nastaven 'D' if (!c_pressed && !load_pressed) { // neni nastaven 'C' ani '*' if (previous_c_pressed) previous_c_pressed = 0; // vypis udaju o diodach if (light_red == 0 && light_green == 0) { LCD_write_string("D6:ON D5:ON"); } else if (light_red == 1 && light_green == 0) { LCD_write_string("D6:OFF D5:ON"); } else if (light_red == 1 && light_green == 1) { LCD_write_string("D6:OFF D5:OFF"); } else { LCD_write_string("D6:ON D5:OFF"); } OSTimeDlyHMSM(0,0,0,500); // zpozdeni 0.5s } if (load_pressed) // je nastaven priznak '*' zatezove ulohy { // tato uloha nebude nyni obsluhovat displej OSTimeDlyHMSM(0,0,0,500); // zpozdeni 0.5s } } if (c_pressed) { // je nastaven priznak vypnuteho displeje (klavesa 'C') if (previous_c_pressed == 0) // pokud jsme nastaveni priznaku identifikovali { // poprve LCD_write_string(" "); // cisteni displeje previous_c_pressed = 1; // priste uz cistit nebudeme, setrime } OSTimeDlyHMSM(0,0,0,500); // zpozdeni 500ms } } }
Třetí úloha, zátěžová úloha loadTask()
, je v aplikaci pro demonstraci funkčnosti statistické funkce. Tato úloha, pokud je nastaven příslušný příznak, generuje zátěž mikročipu rychlým obnovováním textu na displeji (to je činěno každých 50ms).
void loadTask(void *param) { for (;;) { while (load_pressed) // dokud je nastaven priznak stisknute '*' { if (!c_pressed) { // pokud neni vypnuty displej if (previous_c_pressed) previous_c_pressed = 0; // pokud je treba, prehod stav displeje #if OS_TASK_STAT_EN // je generovana statisticka funkce char result[32]; sprintf(result, "!!!LOAD TASK!!! CPU %d%c", OSCPUUsage, '\%'); LCD_write_string(result); #else // neni generovana statisticka funkce LCD_write_string("!!!LOAD TASK!!! STATS DISABLED"); #endif } OSTimeDlyHMSM(0,0,0,50); // zpozdeni 50ms } OSTimeDlyHMSM(0,0,1,0); // zpozdeni 1s } }
V aplikaci je ještě definována třetí úloha. Tato úloha je označena jako startTask()
a je v ní, pokud je to vyžadováno, inicializována statistická funkce. V těle této funkce jsou také inicializovány ostatní úlohy. Samotná úloha jinak nic neprovádí.
Pozn.: Maximum úloh v aplikaci se nastavuje v souboru os_cfg.h
konstantou OS_MAX_TASKS
.
void startTask(void *param) { #if OS_TASK_STAT_EN OSStatInit(); // inicializace statisticke ulohy #endif OSTaskCreate(keyboardTask, (void *)0, (void *)&TaskKeyStk[TASK_STK_SIZE - 1], 1); // uloha zpracovani klavesnice OSTaskCreate(displayTask, (void *)0, (void *)&TaskDisplayStk[TASK_STK_SIZE - 1], 2); // uloha zpracovani displeje OSTaskCreate(loadTask, (void *)0, (void *)&TaskLoadStk[TASK_STK_SIZE - 1], 3); // zatezova uloha for (;;) { OSTimeDlyHMSM(0,0,1,0); // zpozdeni 1s } }
Funkce main()
celé aplikace pak vypadá takto:
int main() { // Inicializace HW initialize_hardware(); // Inicializace OS a uloh OSInit(); OSTaskCreate(startTask, (void *)0, (void*)&TaskStartStk[TASK_STK_SIZE - 1], 0); // Spusteni watchdogu WDTCTL = WDTPW | WDTTMSEL | WDTCNTCL; // Timer mode, predeleni 32768 IE1 |= WDTIE; // Povoleni general interrupt _EINT(); // Start multitaskingu OSStart(); return 0; }
Inicializace HW je ponechána v tomto případě na funkci initialize_hardware()
z knihovny libfitkit
. Na této funkci main()
se ale od té předchozí pro FITKit 1.2 mnoho neliší - jediným podstatným rozdílem je vytvoření úloh OS až v rámci úlohy startTask()
.
Pro správný chod aplikace je nutné mít adresář se zdrojovými kódy umístěný v svn adresáři FITKitu, v /apps/demo_msp
. Pro zprovoznění aplikace na FITKitu stačí přeložit aplikaci, naprogramovat MCU a FPGA a poté spustit terminálový program.
Tímto bychom chtěli poděkovat za pomoc a konzultace Ing. Jiřímu Novotňákovi a Ing. Tomáši Novotnému, kteří již dříve s uC/OS-II pracovali a poskytli nám k dispozici výsledky jejich práce, na které jsme mohli navázat a tím tento projekt rozvinout. Taktéž děkujeme Ing. Josefu Strnadelovi, Ph.D., za poskytnuté studijní materiály.
Labrosse J. Jean: uC/OS-II Real-Time Kernel
Texas Instruments: MSP430x1xx Family User's Guide
Texas Instruments: MSP430x2xx Family User's Guide