Avtor: dr. Simon Vavpotič
2018_259_35
Microchip Harmony je zagotovo najpomembnejši programski okvir za vse, ki se navdušujemo nad mikrokontrolerji in digitalnimi procesorji podjetja Microchip. Je tudi pomemben vgradni del razvojnega okolja MPLAB X IDE, ki nekajkrat pohitri in poenostavi delo programerjev.
V preteklem nadaljevanju smo preučili, kako deluje PIC32 kot gostitelj naprav USB, navidezni spojnik, ali pa kot večpredstavna periferna enota, denimo mikrofon USB. Pogledali smo tudi, kako vzpostavimo sistemsko konzolo, ki omogoča izpisovanje kontrolnih sporočil preko vmesnika UART.
Tokrat se bomo nekoliko podrobneje lotili še podpore za operacijski sistem za delo v realnem času. Na primeru FreeRTOS bomo omenili samo osnovne principe delovanja in zgradbo RTOS. Zakaj ravno FreeRTOS? Zato, ker je eden izmed RTOS, ki so zastonj in ga podpira tudi Microchip Harmony Configurator. Več o FreeRTOS najdemo v referenčnem priročniku FreeRTOS na spletni strani: http://www.FreeRTOS.org. Za konec še o posnemanju delovanja 8-bitnega ZX Spectruma s PIC32MX460F512L, ki nazorno pokaže prednosti uporabe RTOS.
Ta vsebina je samo za naročnike
Operacijski sistem za delo v realnem času
Že Microchip Harmony vsebuje elemente operacijskega sistema, ki jih s pridom uporabljamo v svojih aplikacijah. Vendar manjka jedro operacijskega sistema. Tu se Microchip Harmony naslanja na izdelke drugih proizvajalcev. Microchip Harmony Configurator podpira programske knjižnice operacijskih sistemov: FreeRTOS, OpenRTOS, µC/OS, TreadX in embOS. Microchip Harmony Configurator je napredni generator skeletne programske kode. Dovolj je nekaj kljukic v drevesni strukturi nastavitev in skeletna programska koda je pripravljena.
Poglejmo, kako začnemo projekt z več vzporednimi nalogami ali aplikacijami: Najprej med programskimi knjižnicami Microchipovih partnerskih proizvajalcev (angl. Third Party Libraries) izberemo želeni operacijski sistem za delo v realnem času (RTOS, angl. Real Time Operating System). Mi smo izbrali FreeRTOS, za katerega je na voljo največ primerov uporabe v knjižnici Microchip Harmony.
Sledi določitev števila aplikaciji, ki jih lahko smatramo tudi kot naloge za operacijski sistem. Nekoliko nerodno je, ker moramo pri temi »skočiti« skoraj iz dna drevesa nastavitev na vrh, v nastavitve aplikacije. Tu določimo ime aplikacije in v nastavitvah za RTOS še prednost njenega izvajanja. Podamo tudi oceno dolžine naloge ter zakasnitev med posameznimi izvajanji.
V nastavitvah okvira Harmony izberemo še nastavitve za RTOS za sistemsko nalogo, ki skrbi za vzdrževanje ne abstrakcijskega nivoja operacijskega sistema (OSAL, angl. Operating System Abstraction Layer). Enako kot pri aplikacijah, tudi tu nastavimo velikost naloge, prednost naloge in zakasnitev med zaporednimi klici naloge iz dodeljevalnika nalog.
Ostale nastavitve izvedemo podobno, kot če ne bi uporabili RTOS. Ni odveč opozorilo, da bomo nastavitve za RTOS zaman iskali v drevesu nastavitev, če prej ne izberemo različice in vrste RTOS pod Third Party Libraries!
Kako deluje dodeljevalnik nalog?
Programski dodeljevalnik nalog tvori jedro vsakega operacijskega sistema. Dodeljevalnik nalog skrbi za preklapljanje med konteksti različnih nalog, ki jih izvaja PIC32. V preteklih nadaljevanjih smo za preklapljanje med nalogami skrbeli eksplicitno s koračnim programiranjem. Dodeljevalnik nalog za vsako nalogo izvaja nekaj časa, nakar preklopi kontekst in izvaja drugo nalogo ter s tem nadaljuje, dokler delno ne izvede vseh aktivnih nalog s seznama. Pri tem upošteva prioritete izvajanj posameznih nalog. Nato ponovno izvaja naloge od začetka. Velja omeniti, da lahko nalogo z višjo prioriteto izvaja večkrat, ali dlje časa, medtem, ko nalogo z nižjo prioriteto izvaja krajši čas, oziroma manjkrat v časovni enoti (npr. v 1 sekundi).
Tak način delovanja lahko dodeljevalnik nalog doseže le, če deluje kot prekinitveni program z najvišjo prednostjo, ki se periodično proži med izvajanje posameznih nalog. Kakovost delovanja dodeljevalnika nalog močno vpliva na kakovost delovanja celotne programske kode v PIC32. Pomembna je tudi, pravilna nastavitev parametrov izvajanja posameznih nalog. S tem dodelimo več procesorskega časa pomembnejšim nalogam in manj procesorskega časa manj pomembnim nalogam.
Ustvarjanje novih nalog
S Harmony Configuratorjem lahko dokaj enostavno izdelamo skeletno programsko kodo za celotno rešitev, v kateri je vsaka aplikacija naloga. Vendar tak način programiranja pogosto ne omogoča dovolj velikega izkoristka. Prilagojen je programerjem, ki so vajeni koračnega programiranja. Če želimo učinkoviteje programirati, moramo naloge kreirati tudi znotraj posamezne aplikacije. To je, denimo v programih za Microsoft Windows, povsem običajno.
Primer ustvarjanja naloge najdemo v datoteki system_tasks.c, ki jo samodejno izdela Harmony Configurator. V funkciji SYS_Tasks je po en klic procedure xTaskCreate za vsako aplikacijo pa tudi za vsako sistemsko nalogo. Na primer, klic procedure: » xTaskCreate((TaskFunction_t) _SYS_Tasks,”Sys Tasks”,1024, NULL, 1, NULL);« sistemsko nalogo, ki vzdržuje OSAL, klic procedure: » xTaskCreate((TaskFunction_t) _APP1_Tasks,”APP1 Tasks”, 1024, NULL, 1, NULL);« pa naloge za prvo aplikacijo. Pri tem so v obeh primerih kot vhodni parametri navedene vrednosti, ki smo jih podali v drevesu nastavitev za posamezno nalogo.
Če želimo ustvariti novo nalogo znotraj aplikacije, moramo to narediti brez generatorja kode. Najprej pripravimo funkcijo, ki se bo izvajala kot naloga, nato pa jo s klicem xTaskCreate uvrstimo na seznam nalog, ki jih izvaja dodeljevalnik nalog.
Po kreiranju vseh inicialnih nalog sledi še klic procedure za zagon dodeljevalnika nalog: vTaskStartScheduler, ki zažene dodeljevalnik nalog in s tem omogoči večopravilnost. Povejmo še, da so inicialne naloge tiste, ki jih ustvarimo pred zagonom dodeljevalnika nalog in se začnejo izvajati šele po zagonu dodeljevalnika nalog. Naloge, ki jih ustvarimo med delovanjem dodeljevalnika nalog, so uvrščene v čakalno vrsto in jih dodeljevalnik nalog zažene takoj, ko pridejo na vrsto glede na njihovo prioriteto.
Upravljanje nalog
FreeRTOS ima veliko funkcij, s katerimi lahko upravljamo že ustvarjene naloge. Če želimo začasno zaustaviti izvajanje naloge, uporabimo proceduro vTaskSuspend, ki ji kot edini parameter navedemo ročico naloge, katere izvajanje želimo prekiniti. Nalogo, ki je v stanju zaustavitve ponovno zaženemo s klicem procedure vTaskResume, kjer kot edini parameter podamo njeno ročico.
Procedura vTaskDelay, po drugi strani, zaustavi izvajanje naloge za vnaprej določeno število obravnav dodeljevalnika nalog. Posebnost je vrednost 0, ki pomeni enako kot proženje procedure taskYIELD, ki preda nadzor dodeljevalniku nalog in s tem predčasno konča obravnavo naloge. taskYIELD uporabljamo takrat, ko PIC32 z izvajanjem naloge ne bi opravljal koristnega dela. Zato se lahko namesto omenjene naloge izvaja druge naloge po prioritetnem vrstnem redu. Vendar nalogo ponovno izvede v naslednjem prehodu. Pomembna je tudi procedura vTaskDelayUntil, ki se razlikuje po tem, da je zakasnitev izvajanja naloge konstantna, kar omogoča bolj konstantno frekvenco izvajanja naloge.
vTaskDelete izbriše nalogo s seznama nalog in deluje ravno nasprotno od procedure xTaskCreate. Izbrisane naloge s prej omenjenimi ukazi ne moremo ponovno zagnati. S proceduro xTaskCreate je potrebno je ustvariti novo nalogo.
Seznam vseh nalog, ki jih trenutno izvaja dodeljevalnik nalog lahko dobimo z ukazom vTaskList, ki vrne kazalec na seznam vseh nalog, ki se trenutno izvajajo. Pri tem so v seznamu za vsako nalogo poleg njenega imena navedeni tudi stanje (npr. »R«: pripravljena, angl. ready), prioriteta (0 je najvišja), najmanjšo velikost razpoložljivega sklada med delovanjem naloge in številko ročice naloge.
Druge funkcije RTOS
FreeRTOS se po osnovni funkcionalnosti ne razlikuje dosti od drugih večopravilnih operacijskih sistemov, saj podpira tudi delovanje semaforjev, čakalnih vrst, časovnikov ter beleženje in spremljanje sistemskih skupin dogodkov.
Semaforji so programske strukture, ki omogočajo sinhronizacijo delovanja nalog in usklajevanje dostopov do sistemskih sredstev. Funkcija xSemaphoreCreateBinary ustvari nov binarni semafor in vrne njegovo ročico. Vsak semafor porabi majhno količino RAM-a, ki hrani njegovo stanje. Semaforjevo začeto stanje je »prazno«. Če želimo semafor prepustiti (drugim) nalogam, moramo uporabiti funkcijo xSemaphoreGive, ki sprosti semafor, tako da ga lahko druge naloge prevzamejo s klicem funkcije xSemaphoreTake. Funkcija xSemaphoreTake hkrati omogoča tudi preverjanje razpoložljivosti semaforja, saj vrača vrednosti pdPASS in pdFAIL. V prvem primeru, je dovoljen prevzem semaforja in se izvrši, v drugem pa je semafor zaseden in prevzem ni dovoljen, kar pomeni, da je potrebno čakati.
Naslednji način komunikacije med nalogami so čakalne vrste. Novo čakalno vrsto ustvarimo z ukazom xQueueCreate, ki vrne ročico čakalne vrste. Kot vhodne parametre xQueueCreate določimo dolžino čakalne vrste, oziroma največje število čakajočih postavk in velikost vsake postavke. Postavka je polje bajtov. Njihov pomen zavisi od aplikacije, ki uporablja čakalno vrsto. Branje postavk iz čakalne vrste izvajamo z ukazoma: xQueueReceive, ki vrne postavko, ki je bila najdlje v čakalni vrsti (iz čela čakalne vrste), in xQueuePeek, ki se razlikuje po tem, da postavko ne odstrani iz čakalne vrste. xQueuePeek torej omogoča le vpogled v čakalno vrsto, ne pa tudi brisanje že prebrane postavke iz čakalne vrste. Pisanje v čakalno vrsto izvajamo s fukcijami xQueueSend, xQueueSendToFront in xQueueSentToBack. xQueueSentToBack in xQueueSent delujeta enako in postavita novo postavko na konec vrste, kar pomeni, da bi pri branju najkasneje obravnavana. xQueueSendToFront vrine novo postavko na čelo čakalne vrste, kar pomeni, da bo prva prebrana. To omogoča hitro obravnavo postavk s prednostjo.
Časovniki delujejo podobno kot v operacijskih sistemih za osebne računalnike. Njihova namena sta periodično klicanje kratkih procedur, ki se morajo stalno izvajati, in enkratni klici procedur, ki se izvedejo po določenem časovnem intervalu. Nov časovnik ustvarimo s funkcijo xTimerCreate, kjer podamo ime časovnika, dolžino periode, opredelimo samodejno ponavljanje izvajanja in kazalec na proceduro, ki jo časovnik izvaja. Časovnik zaženemo s funkcijo xTimerStart in zaustavimo s funkcijo xTimerStop.
Dogodki so namenjeni beleženju delovanja jedra operacijskega sistema in aplikaciji. Z ukazom xEventGroupCreate lahko ustvarimo tudi novo skupino dogodkov. Nov zapis v skupino dogodkov tvorimo s funkcijo xEventGroupSetBits, s katero postavimo posamezne bite, ki opredeljujejo različne dogodke. Stanje dogodkov v skupini dogodkov lahko preberemo z xEventGroupGetBits. Obakrat je vhodni parameter ročica skupine dogodkov.
Primeri uporabe FreeRTOS v Microchip Harmony
V Microchip Harmony najdemo za FreeRTOS štiri primere uporabe. Osnovni primer (basic_freertos) prikazuje tvorbo nalog in se poigra z LED enega od podprtih Starter Kitov. Lahko ga tudi dokaj enostavno predelamo za lastno strojno okolje. Mi so ga preizkusili s PIC32MZ EC Starter Kitom. Vsekakor ni toliko pomemben vizualni učinek, pomembnejša je programska koda, ki to omogoča utripanje LED z različnimi ritmi. Vsako LED krmili po ena naloga. Preko njihovega utripanja lahko v grobem vidimo, kako naloge sodelujejo med seboj. Vsekakor pa je nujen tudi vpogled v programsko kodo, da bi bolje razumeli njeno delovanje.
Naslednji trije primeri uporabe: cdc_com_port_dual, cdc_msd_basic in tcp_ip_client_server so bolj praktični in kažejo prednosti integracije FreeRTOS pred koračnim programiranjem. Nekoliko škoda je le, da so se pisci primerov odločili le združevati več koračnih aplikacij, denimo dve aplikaciji za podporo delovanju navideznih serijskih vmesnikov preko USB, ne pa tudi, da bi aplikacije napisali na novo. Koračno programiranje tako ostaja na ravni aplikacije, medtem ko je vsaka aplikacija za operacijski sistem svoja naloga.
Aplikacije sodelujejo med seboj preko mehanizmov, ki jih omogoča operacijski sistem. Denimo, prvi primer ustvari dvoje navideznih serijskih vrat, ki medsebojno komunicirata preko dveh terminalskih oken. Karkoli vpišemo preko prvih vrat, se prenese na druga serijska vrata in obratno. Delovanje nazorno pokažemo z dvema terminalskima oknoma (npr. uporabimo lahko zastonjski program TeraTerm). Ves tekst, ki ga vpišemo v eno terminalsko okno, se izpiše v drugem in obratno. Hitrost komunikacije moramo nastaviti na 115200 baudov. Pri tem je prenos podatkov 8-biten, brez paritete in z enim stop bitom.
Primer tcp_ip_client je namenjen prikazu delovanja spletnega strežnika vzporedno z novimi storitvami, ki so dodane preko vrat 9760 itn. Z uporabo programskega orodja Telnet ali podobnega, lahko ročno preverimo sodelovanje med strežniki in odjemalci, ki jih vzpostavimo preko ukazne konzole. Pri tem ukazna konzola teče vzporedno s spletnim strežnikom. Vsekakor je primer nazoren prikaz zmogljivosti PIC32. Morda velja opozorilo le tistim, ki uporabljate starejše mikrokontrolerje PIC32MX, saj jim utegne primanjkovati pomnilnika.
Še bolj zanimiv je primer posnemanja ZX Spectruma, ki je v celoti zasnovan na FreeRTOS.
Posnemanje ZX Spectruma s PIC32
Da 32-bitni PIC32 ni kar tako, se lahko prepričamo tudi iz dokaj nenavadnih aplikacij. Ena izmed njih je posnemovalnik 8-bitnega ZX-Spectruma, ki temelji na operacijskem sistemu FreeRTOS. Na spletni strani http://blog.flyingpic24.com/wp-content/uploads/2014/05/ZX+Spectrum.zip je celotna izvorna koda za razvojno ploščo MicroElectronika Multimedia platform (MMB-MX4) za PIC32 s PIC32MX460F512L in barvnim prikazovalnikom TFT z ločljivostjo 320 x 240 pik. Prevedemo jo lahko še v starem Microchip MPLAB IDE pod pogojem, da smo namestili tudi Microchip Libraries for Applications (MLA). Vključuje naslednje komponente: FreeRTOS, Programsko knjižnico USB library, podporo za tipkovnico USB (razvojna plošča je gostiteljica standardne tipkovnice za PC), podporo za datotečni sistem MMD File System, podporo za vmesnik za kartice SD card in grafično knjižnico Graphics library (v2.0). Vse so del razvojne omenjene plošče, razen tipkovnice, ki jo moramo povezati preko vtičnice USB.
Posnemovalnik ZX Spectruma sestavljajo tri glavne datoteke izvorne kode: TaskZ80.c vsebuje neskončno programsko zanko, v kateri posnema delovanje preko 700 ukazov mikroprocesorja Z80. PIC32 lahko tako interpretira ukaze iz originalnega ROM ZX Spectruma. Vsebuje tudi past, s katero je nadomeščen datotečni sistem originalnega vmesnika za tračno enoto. Namesto tega uporablja datotečni sistem na kartici SD.
taskUSB.c vključuje vso funkcionalnost gostitelja standardne tipkovnice (za PC) in posnemanja tipkovnice ZX Spectruma. Kode standardne tipkovnice se pretvorijo v vrednosti vhodno/izhodne matrike tipkovnice ZX Spectruma, preko katere je Z80 zajemal stanje tipk. Tako je posnemanje izvedeno na najnižjem, strojnem nivoju, in ni bojazni, da kateri od programov za ZX Spectrum ne bi pravilno deloval.
taskLCD.c vključuje vse preslikave strojne logike ZX Spectruma na LCD z ločljivostjo QVGA. Posnemovalnik uporablja periodično osveževanje vsebine pomnilnika LCD iz izravnalnika v RAM PIC32. Pri tem izvaja pretvorbo vsebine posameznih vrstic in posnema barve. Frekvenco osveževanja je mogoče nastaviti tako, da posnemovalnik deluje realistično.
V praksi se izkaže, da je PIC32 malenkost hitrejši od ZX Spectruma. To je dokaz, da ima PIC32 zrelo in nadvse uporabno mikroarhitekturo, ki bo obstala še vrsto let. V veliko pomoč pri njegovem programiranju pa je programska knjižnica Microchip Harmony in njena prednica, Microchip Library for Applications.
Za konec
V nadaljevanki Harmonija razvoja in programiranja smo v enajstih delih spoznali razvojno okolje Microchip Harmony, ki je ključno pri programiranju mikrokontrolerjev PIC in vsebuje vrsto pripomočkov, ki znatno olajšajo načrtovanje, organizacijo in začetek programiranja kode Microchipovih mikrokontrolerjev. Čeprav ima programer tudi možnost, da določene dele kode sprogramira mimo okvira Microchip Harmony, je malo verjetno, da se bo sam lotil programiranja kompleksnejših funkcij, kot je podpora delovanju vmesnika USB. Microchip Harmony je tu naravnost odlična in nepogrešljiva možnost za hitro gradnjo kakovostnih rešitev.