UNIVERZITA TOMÁŠE BATI VE ZLÍNĚ FAKULTA APLIKOVANÉ INFORMATIKY PROGRAMOVÁNÍ MIKROPOČÍTAČŮ CVIČENÍ 5 Práce s přerušením, zpracování stisku tlačítka Jan Dolinay Petr Dostálek Zlín 2013 Tento studijní materiál vznikl za finanční podpory Evropského sociálního fondu (ESF) a rozpočtu České republiky v rámci řešení projektu: MODERNIZACE VÝUKOVÝCH MATERIÁLŮ A DIDAKTICKÝCH METOD
2 Cvičení 5 Práce s přerušením, zpracování stisku tlačítka STRUČNÝ OBSAH CVIČENÍ: Princip přerušení Ukázkový program blikání LED řízeno tlačítkem Úkoly k procvičení VSTUPNÍ ZNALOSTI: Toto cvičení předpokládá znalosti z předchozích cvičení a z přednášky zabývající se přerušením. CÍL: Na tomto cvičení si ukážeme, jak lze v programech využít přerušení. Cílem cvičení je naučit se využívat přerušení a to konkrétně přerušení vzniklé při stisku tlačítka (změna logické úrovně na vstupu) a přerušení od časovače. Cvičení se vztahuje k těmto otázkám Přerušovací systém mikropočítače HC08 princip obsluhy přerušení, typy přerušení
3 Řešené příklady Příklad 1 Blikání LED ovládané tlačítkem, přerušení od klávesnice Úkol: Vytvořte program, který bude blikat LED1 s periodou cca 2s (1s svítí a 1s nesvítí). Tlačítkem SW1 bude možno blikání zastavit a znovu spustit. Řešení V tomto programu využijeme přerušení od klávesnice (modul KBI) pro detekci stisku tlačítka. Blikání LED budeme realizovat pomocný cyklu (smyčky) tak, že střídavě změníme stav bitu číslo 0 na portu F (kde je připojena LED1). Přitom mezi každým průchodem cyklem je prodleva asi 1 sekunda aby LED vždy cca 1 sekundu svítila a 1 sekundu pak nesvítila. Bez této prodlevy by bylo rozsvěcení a zhasínání tak rychlé, že by žádné blikání nebylo vidět. Využití přerušení zaručuje okamžitou reakci na stisk tlačítka i když se hlavní program právě nachází v čekací smyčce. Pro prodlevu použijeme podprogram cekej, který jsme viděli už dříve. Princip programu je znázorněn na následujících vývojových diagramech. Obr. 1 Vývojový diagram příkladu na obsluhu stisku tlačítka pomocí přerušení
4 Na obrázku je nalevo (žlutě) vývojový diagram hlavního programu a napravo (zeleně) vývojový diagram pro podprogram, který je spuštěn, když dojde k přerušení tj. obsluha přerušení. Celkově je program navržen tak, že v hlavním programu se v závislosti na hodnotě proměnné blikej buď neprovádí nic, nebo se rozvětvuje a zhasíná LED1. Proměnná blikej je měněna při stisku tlačítka v obsluze přerušení. V hlavním programu nejprve inicializujeme port F, kde jsou připojeny LED diody, tj. nastavíme příslušný vývod (pin) portu jako výstupní a vypneme pull-up rezistory. Podobně nakonfigurujeme příslušný vývod portu A (kde je připojeno tlačítko) jako vstupní a zapneme pull-up rezistor. Dále inicializujeme modul KBI tak, aby při změněně logické úrovně na tomto pinu bylo vyvoláno přerušení. Poté povolíme přerušení instrukcí CLI a do proměnné blikej nastavíme hodnotu 1, která znamená, že blikání je zapnuto. Po spuštění programu tedy bude LED blikat a prvním stiskem tlačítka SW1 se blikání vypne. Dalším stiskem pak zapne, dalším vypne atd. Jak se vidět v dalším bloku vývojového diagramu hlavního programu (žlutý), testuje se pak hodnota proměnné blikej a jestliže není rovna jedné, program se vrací zpátky na test, tj. neprovedou se zbylé dva bloky, které zajišťují blikání LED, viz větev NE. Z toho plyne, že jestliže je blikej = 0, LED nebliká, jestliže je blikej = 1, LED bliká. Blikání je vytvořeno tak, že rozsvítíme LED a pozastavíme program na cca 1 sekundu. Potom LED zhasneme a opět program pozastavíme na 1 sec. Pak se vracíme zpět na začátek a vše se opakuje. LED tedy vždy 1s svítí a 1s nesvítí. Pokud někdo stiskne tlačítko, vyvolá se přerušení a provede se podprogram KBI_int, který je znázorněn vývojovým diagramem vpravo (zeleně). V tomto podprogramu se neguje nejnižší bit proměnné blikej. Tedy jestliže v ní byla hodnota 1, nastaví se nyní hodnota 0 (a LED nebude blikat). Jestliže naopak bude v proměnné blikej 0, nastaví se negací na 1 a LED začne opět blikat. Poté se čeká cca 10 ms na doznění zákmitů tlačítka a nakonec se vynuluje příznak probíhajícího přerušení zápisem do bitu KBACK v registru KBI1SC (viz popis registrů modulu KBI výše) a obsluha přerušení končí. Použité instrukce a direktivy EOR exkluzivní logický součet (XOR) mezi registrem A a paměťovou buňkou
5 Direktivy ORG určení umístění kódu nebo dat v paměti mikropočítače. DC.W viz obsluha přerušení, nasměrování. Vysvětlení programu Zdrojový kód programu je rozdělen do čtyř částí, které jsou na výpisech na obr. 2 až 4. ; hlavní program MOV #$ff,ptfd ; inicializace datoveho registru -> LED zhasnuty MOV #%00000001,PTFDD ; PTF0 vystupni rezim - zde je pripojena LED1 MOV #0,PTFPE ; pull-up rezistory portu F deaktivovany MOV #%00000110,KBI1SC ; povoleni preruseni od KBI, sestupna hrana vyvola preruseni MOV #%00010000,KBI1PE ; PTA4 rezim KBI, ostatni rezim GPIO MOV #1,blikej ; zapni blikani CLI ; povol preruseni nav1 LDA blikej ; A <- blikej feed_watchdog CMP #0 BEQ nav1 ; pokud je blikej==0, neblikame LDA PTFD AND #m_led1_on STA PTFD ; rozsvit LED1 JSR cekej ; cekej 1s LDA PTFD ORA #m_led1_off STA PTFD ; zhasni LED1 JSR cekej ; cekej 1s BRA nav1 ; nekonecna smycka Obr. 2 kód hlavního programu Na začátku pomocí instrukcí MOV provádíme inicializaci portu F (stejně jako v minulém příkladu, pro ovládání LED1 pomocí příslušného výstupu). Potom nastavujeme obvod pro generování přerušení KBI a to tak, aby přerušení vyvolala sestupná hrana na bitu 4 portu A (kde je připojeno tlačítko SW1). Srovnejte hodnoty použité u instrukcí MOV s významem odpovídajících bitů v registrech KBI1SC a KBI1PE na obrázcích v dokumentaci mikropočítače. Následuje test proměnné blikej. Jestliže je její hodnota rovna nule, vrací se program na návěští nav1 (skok BEQ) a nedostane se tedy do kódu, který ovládá blikání LED.
6 Jestliže blikej není nulová, rozsvítí se LED1. To se provede třemi instrukcemi stejně jako v minulém příkladu: LDA PTFD AND #m_led1_on STA PTFD Tedy nahraje se do reg. A obsah celého portu F, provede se logický součin (AND) s maskou, který zajistí, že bit kde je připojena LED1 se vynuluje (a tím se LED rozsvítí, viz minulá lekce), ale ostatní bity nebudou změněny. Výsledek AND je uložen do reg. A a my jej třetí instrukcí (STA PTFD) uložíme na port F a tím LED1 rozsvítíme. Následuje prodleva cca 1 sekunda zavoláním podprogramu cekej. Poté je LED1 zhasnuta pomocí tří instrukcí, opět jako v minulé lekci. Pak se opět cca 1s čeká zavoláním podprogramu cekej. Nakonec se program vrací zpět na návěští nav1 a testuje stav proměnné blikej. To vše se opakuje v nekonečné smyčce, viz instrukce nepodmíněného skoku BRA nav1. ; cekej podprogram pro pozastaveni vykonavani programu na cca 1s cekej PSHH ; uloz pouzivane registry na zasobnik PSHX PSHA LDA #18 n2 LDHX #$FFFF n1 AIX #-1 ; H:X = H:X+1 (pozor, nenastavuje priznaky) feed_watchdog ; reset watchdogu CPHX #0 ; je H:X = 0? BNE n1 ; pokud ne, skoc na n1 DECA ; A = A - 1 BNE n2 ; je A=0? Pokud ne, skoc na n2 PULA PULX PULH ; obnov puvodni obsah registru ze zasobniku RTS ; navrat z podprogramu Obr. 3 Kód podprogramu cekej Na obr. 3 je podprogram cekej, který slouží k pozdržení běhu programu přibližně o 1 sekundu a to pomocí čekací smyčky. Takovýto způsob čekání není příliš elegantní, prozatím nám ale bude stačit. Později si vysvětlíme, jak lze pracovat s časem pomocí časovače. Podprogram cekej se skládá ze dvou vnořených smyček. Nejprve se do registru A nahrává číslo 18. Registr A zde slouží jako počitadlo pro vnější smyčku, takže vnitřní smyčka využívající registru H:X se provede osmnáctkrát. Kód vnitřní smyčky se pak opakuje tak dlouho, dokud je v registru H:X číslo větší než nula. Protože do H:X nahráváme číslo FFFF
7 hexa tj. 65536 dekadicky, probíhá tato smyčka právě tolikrát. Uvnitř smyčky se nic užitečného neděje, pouze se resetuje watchdog a dekrementuje počitadlo. Tento podprogram vložíme v našem souboru až na konec zdrojového kódu, až za kód, který vygeneroval průvodce novou aplikací. Kód z následujících výpisů pak vkládáme také na konec, ještě za kód podprogramu cekej. ; cekej10ms podprogram pro pozastaveni vykonavani programu na cca 10ms cekej10ms PSHH ; uloz pouzivane registry na zasobnik PSHX LDHX #$3000 n3 AIX #-1 ; H:X = H:X-1 (pozor, nenastavuje priznaky) feed_watchdog ; reset watchdogu CPHX #0 ; je H:X = 0? BNE n3 ; pokud ne, skoc na n1 PULX PULH ; obnov puvodni obsah registru ze zasobniku RTS ; navrat z podprogramu Obr. 4 kód podprogramu cekej10ms Na obr. 4 je podprogram pro čekání 10 ms, který se používá v obsluze přerušení. Princip je stejný jako u předchozího podprogramu cekej. Zde ovšem vystačíme s jednou smyčkou a do reg. H:X nahrajeme pouze číslo 3000 hexa. Provedení tohoto podprogramu pak trvá přibližně 10 ms, což je dostatečná doba abychom odfiltrovali případné zákmity tlačítka. Proč toto čekání vůbec používáme je vysvětleno níže v popisu kódu obsluhy přerušení. ; obsluha preruseni od KBI KBI_int PSHH ; uloz registr H na zasobnik!!! LDA blikej EOR #%00000001 ; neguj 0.bit promenne blikej STA blikej JSR cekej10ms ; pockej na ustaleni tlacitka BSET 2,KBI1SC ; potvrzeni prijmu preruseni PULH ; obnov registr H ze zasobniku!!! RTI ; návrat z obsluhy preruseni ; nasmerovani vektoru preruseni KBI na nasi obsluhu ORG $FFD2 ; adresa vektoru preruseni od KBI DC.W KBI_int ; vyplneni vektoru adresou nasi obsluhy Obr. 5 kód obsluhy přerušení
8 Na obr. 5 je kód obsluhy přerušení. Tento kód je proveden, jestliže nastane přerušení od modulu KBI, tj. jestliže někdo stiskne tlačítko SW1. Nejprve se uloží na zásobník registr H, který není automaticky ukládám procesorem před provedením obsluhy přerušení (na rozdíl od ostatních registrů). Toto neuložení registru H je vlastnost procesorů HC08 zavedená z důvodů zpětné kompatibility. Pro správnou funkci programu by ovšem obsluha přerušení měla uchovat obsah všech pracovních registrů a tak je dobrým zvykem v každé obsluze přerušení na HC08 uložit na začátku obsah registru H na zásobník a na konci jej opět obnovit. Bit 0 proměnné blikej se invertuje pomocí operace XOR (exkluzivní logický součet). Tato logická operace s uvedenou maskou zajistí, že jestliže je nultý bit v proměnné blikej nastaven na 1, výsledek XOR s maskou bude 0 (nultý bit v masce je také 1 a XOR stejných operandů dá výsledek 0). Naopak, pokud bude v proměnné blikej nula, výsledek XOR s maskou bude 1. Nejnižší bit proměnné blikej je tedy při každém vykonání obsluhy přerušení invertován. Tím je zajištěno, že při opakovaných stiscích tlačítka se přepíná mezi stavem, kdy LED1 bliká a kdy nebliká. Čekání 10 ms (JSR cekej10ms) slouží k odfiltrování zákmitů tlačítka. Bez tohoto čekání by obsluha přerušení skončila příliš brzy a přerušení by mohlo být vyvoláno znovu ještě zákmitem od stejného stisku tlačítka, který už byl obsloužen. Obsluha přerušení je speciálním typem podprogramu a končí proto instrukcí RTI, nikoliv RTS jako běžný podprogram. Poslední část kódu, nasměrování vektoru přerušení KBI na naši obsluhu, zajišťuje uložení adresy naší obsluhy přerušení (kteoru jsme pojmenovlai KBI_int) na adresu vektoru přerušení KBI tj. na adresu FFD2 hexa. K tomu je použita direktiva ORG, která definuje, na jakou adresu v paměti se má uložit kód za touto direktivou následující. Jinak řečeno, touto direktivou dáváme překladači příkaz, aby následující kód vložil do paměti mikropočítače na adresu, kterou zadáme. Obvykle tuto direktivu nepoužíváme a necháváme překladač aby kód i data našeho programu umístil do paměti podle libosti, protože toto umístění není pro nás důležité. V případě změny vektorů přerušení ale potřebujeme uložit adresu naší obsluhy na přesně danou adresu. Kód zapsaný za direktivou ORG bude tedy uložen do paměti mikropočítače od adresy $FFD2. A tímto kódem je adresa našeho podprogramu KBI_int, která je sem vložena direktivou DC.W (definuj konstantní word), viz popis použitých instrukcí a direktiv na začátku této lekce. Princip vektoru přerušení si můžeme objasnit takto: Náš podprogram KBI_int (který se má zavolat, když nastane přerušení po stisku tlačítka), je umístěn někde v paměti mikropočítače. Jak se procesor dozví, že až nastane příslušné přerušení, má provést právě naši obsluhu? (jinými slovy že má provést skok na podprogram KBI_int?). K tomu slouží tzv. vektory přerušení, což jsou určité předem definované místa (adresy) v paměti, ze kterých procesor
9 zjišťuje adresy obsluh přerušení pro jednotlivé typy přerušení (např. od klávesnice, od časovače atd.). Pro přerušení od klávesnice je tato adresa $FFD2, což lze zjistit v dokumentaci k mikropočítači. Když nastane přerušení, procesor se podívá na adresu, která odpovídá tomuto přerušení, a tam najde odkaz na podprogram, který se má vykonat. Jestliže tedy na adresu $FFD2 uložíme adresu našeho podprogramu, bude pak tento podprogram vykonán když nastane přerušení od klávesnice. Aby program mohl fungovat, musíme ještě definovat masky a proměnnou blikej: blikej DS.B 1 m_led1_on EQU %11111110 m_led1_off EQU %00000001 Testování programu Před nahráním programu do vývojového kitu nezapomeňte provést restart do režimu monitoru, jako to bylo popsáno v minulé lekci, pomocí tlačítek SW4 a Reset. Program nekrokujte, nýbrž spusťte plnou rychlostí a vyzkoušejte jeho funkci.
10 Příklady k procvičení 1. Upravte program tak, aby při stisku tlačítek SW2, SW3 a SW4 se zapínalo/vypínalo blikání také pro LED2, LED3 a LED4. Máme tedy zajistit, aby tlačítka SW2, SW3 a SW4 zapínala/vypínala blikání pro diody LED2, LED3 a LED4 stejně jako to dělá SW1 pro LED1 v ukázkovém programu výše. 2. Přidejte možnost změny periody blikání pomocí tlačítek SW2 a SW3. Tlačítkem SW2 se perioda zmenší, stiskem SW3 se zvětší. K tomu upravte podprogram cekej tak, aby jako vstupní parametr dostal délku požadované prodlevy parametr využijte jako číslo nahrané do reg. A před návěštím n2 (počet opakování vnitřního cyklu, výchozí hodnota 18) Doplňující zdroje [1] Freescale: Firemní dokumentace pro mikropočítače HCS08, dostupné online: http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=s08gb&nodeid= 01624684491437EDD5