Zpět na seznam článků     Číst komentáře (2)     Verze pro tisk

Programování mikrokontrolérů architektury AVR (4)

Autor: Prog0el   
10.2.2016

V tomto díle bude vysvětlena další stěžejní část programovacího jazyka C, kterou jsou funkce. Naučíme se obsluhovat asynchronní události pomocí přerušení a popíšeme si také přerušovací systém mikrokontrolérů AVR. Datový výstup již nebudou pouze jednotlivé LED diody, ale sedmisegmentová zobrazovací jednotka, kterou se naučíme řídit.


Funkce

Doposud jsme pracovali s funkcemi, které již někdo nadeklaroval před námi a zprostředkoval nám je pomocí knihoven, které jsem popsal již dříve. Nyní se podíváme na to, jak nadeklarovat vlastní funkce a k čemu je můžeme použít. Funkce nám nahrazují případné opakující se sekvence kódu odskokem na adresu zmiňované funkce. V praxi to tedy znamená, že pokud v průběhu programu zavoláme funkci (například _delay_ms(20);) dojde k odskoku na adresu funkce, funkce se provede, případně vrátí návratovou hodnotu a program pokračuje tam, kde byl přerušen odskokem.

  1. unsigned char funkce(unsigned char argument){
  2. //Tělo funkce
  3. argument =argument % 2;
  4. return argument;
  5. }

První klíčové slovo (v našem případě unsigned char) definuje datový typ návratové hodnoty funkce následované jménem funkce- to je, stejně jako u proměnných, téměř libovolné až na použití klíčových slov jazyka C a diakritiky. V závorkách je uveden argument funkce, tedy data, která funkci předáváme. V v těle funkce vymezeném složenými závorkami se nachází vlastní kód, který chceme, aby se po zavolání funkce provedl. Posledním příkazem musí být klíčové slovo „return“ následované návratovou hodnotou ve formě proměnné, čísla, či výrazu. Pokud uvedeme klíčové slovo datového typu „void“, pak není datový typ definován, což se často využívá zejména u funkcí bez návratových hodnot. Funkce deklarujeme vždy mimo jiné funkce, tedy i mimo funkci main. Pokud je funkce nadeklarována, nic nebrání jejímu použití:

Příklad
  1. unsigned char x;
  2. x=funkce(5);
  3. //Proměnné „x“ bude přiřazena návratová hodnota funkce
  1. unsigned char x;
  2. x=funkce(funkce_2(5));
  3. //Provede se kód „funkce_2“ a posléze se její návratová hodnota
  4.  //využije jako argument funkce „funkce“.
  5.  
  1. unsigned char x;
  2. x=funkce(funkce (funkce (5)));

Přes to, že využití rekurzivních funkcí j, například při výpočtech faktoriálů čísel užitečné, nelze jejich využití doporučit začátečníkům, jelikož může dojít k přetečení paměti na odskoky- dojde k tzv. „stack overflow“, jehož podstatu zde zatím nebudu řešit.

Mějte na paměti, že proměnné deklarované v těle funkce existují pouze po dobu vykonávání kódu v těle této funkce a následně zanikají.

Přerušení

Přerušením myslíme přerušení vykonávání právě vykonávaného programu odskokem na adresu tzv. „vektoru přerušení“ na kterém je uložena adresa počátku kódu, který se má vykonat po zaznamenání události vedoucí k vyvolání přerušení. Takovou událostí může být přetečení registru, náběžná či sestupná hrana na pinu, příjem dat na sériové lince atd. Po tom, co se provede daný kód zakončený instrukcí „reti“ a dojde k návratu na místo v paměti, ve kterém bylo přerušení vyvoláno. Instrukci „reti“ (programovací jazyk assembler) samozřejmě nemusíme řešit, jelikož ji za nás vloží překladač. Jediné, co jako programátoři musíme provést, je pomocí konfiguračního registru přerušení nastavit, nadeklarovat rutinu přerušení a pomocí instrukce „sei“ z jazyka assembler přerušení povolit. Pro úplnost uvedu také instrukci „cli“, která přerušení zakáže. Místo zmiňovaných instrukcí lze využít i sedmého bitu registru SREG, ale touto možností se nebudu zabývat. V datasheetu je uvedeno, že jsou obě možné metody globálního povolení přerušení ekvivalentní.

Externí přerušení

Externí přerušení je, na mikrokontrolérech AVR, možno vyvolat při vzestupné či sestupné hraně, jakékoliv změně logické hodnoty či za stavu logické 0 na pinu externího přerušení. Mějte na mysli, že při aktivaci módu přerušení, které reaguje na hranu, je nezbytné, aby spouštěcí impuls trval déle, než jedna perioda taktovacího signálu mikroprocesoru.

Mikrokontrolér AT Mega8 je vybaven dvěma piny externího přerušení, které se ve schematické značce označují „INT0“ (PD2) a „INT1“ (PD3). U jiných typů mikrokontrolérů AVR se můžete setkat i s více piny externího přerušení.

Jak již jsem zmínil, změna konfigurace periferií se děje pomocí konfiguračních registrů, z nichž známe zatím pouze „DDRx“ a PORTx. První registr, který budeme k práci s přerušovacím systémem mikrokontroléru AVR potřebovat, bude registr GICR (General Interrupt Control Register), ze kterého nás budou zajímat pouze dva nejvyšší bity. Zapsáním logické 1 do bitu 7 registru GICR dojde k aktivaci externího přerušení INT1 a stejným způsobem je ovládáno externí přerušení INT0 pomocí druhého nejvyššího bitu zmiňovaného registru. Zbylé piny nás nemusejí zajímat, jelikož některé z nich jsou nevyužity a zbylé dva jsou využívány při práci s bootloaderem, což není tématem tohoto dílu. Je nezbytné pamatovat na to, že pomocí registru GICR neprovádíme globální povolení přerušení, tedy po zapsání logické 1 na příslušné bity přerušovací systém pracovat bez globálního povolení nebude.

Dalším registrem, který budeme potřebovat, je MCUCR (Micro Controller Unit Control Register). Zajímat nás bude pouze nejnižší nibble registru, pomocí kterého se konfiguruje, na jakou událost na daném vstupu bude přerušovací systém mikrokontroléru reagovat vyvoláním přerušení. Nejnižší dvojice bitů slouží ke konfiguraci externího přerušení INT0. Bity číslo dva a tři slouží ke konfiguraci INT1. Máme možnost využít čtyř módů: 00-log.0; 01-jakákoliv změna; 10-sestupná hrana; 11-vzestupná hrana. Detaily zmíněných módů jsem popsal výše.

Poslední registr, který zmíním je tzv. příznakový registr GIFR (General Interrupt Flag Register), který nás informuje o tom, zda proběhla událost vyvolávající přerušení bez ohledu na to, zda je, či není přerušení aktivováno. Na nejvyšším bitu se nachází příznak externího přerušení INT1 a na druhém nejvyšším bitu příznak přerušení INT0. Pokud proběhla daná událost, pak bude konkrétní bit nastaven do logické 1 a po provedení kódu rutiny přerušení bude automaticky nastaven zpět do výchozí hodnoty logické 0. Pokud bychom chtěli příznak vymazat manuálně, pak je nutno na příslušné bity zapsat logickou 1, čímž se příznak vymaže. Mazání příznaku přerušení může být výhodné v případě, kdy chceme zabezpečit, aby se po aktivaci přerušení rutina přerušení nespustila.¨

  1. //Umístěno ve funkci (nejčastěji main, ale není podmínkou)
  2. GICR=0b01000000;
  3. MCUCR|=0b00000011;
  4. GIFR|=0b010000000;
  5. asm(“sei“);
  6.  
  7. //Samotná rutina přerušení musí být, stejně jako ostatní deklarace funkcí,
  8. //umístěna mimo ostatní funkce.
  9. isr(INT0_vect){
  10. //Kód, který se má provést po vyvolání přerušení
  11. }

Řízení sedmisegmentového displaye

Sedmisegmentový zobrazovač je uskupení sedmi LED diod do tvaru znaku "8", které jsou zalité v jednom pouzdře a zpravidla mají společně zapojenou jednu z elektrod- tedy anodu nebo katodu. Často je přidán také osmý člen, kterým je desetinná tečka. Těmto jednotlivým LED diodám říkáme "segmenty", které jsou označeny písmenným kódem, který je u většiny výrobců sjednocen:

Na skutečnost, zda používáme zobrazovač se společnou katodou či anodou musíme pohlížet, jelikož se sebou nese odlišnosti při řízení těchto displayů. V případě, že používáme zobrazovač se společnou anodou, který je častější, připojíme společnou anodu k napájení a jednotlivé katody buďto připojíme k zemi (příslušná LED svítí) nebo připojíme na napájení (příslušná LED nesvítí). U zobrazovačů se společnou katodou se používá opačný postup. Společná katoda se připojí k zemi a jednotlivé anody se připojují k napájení, aby svítily.

Zobrazovače se vyrábějí také ve víceciferném provedení, kdy jsou pospojovány pouze anody či katody LED diod, které náleží jedné cifře. Řízení probíhá tak, že se pospojují všechny vývody segmentů stejného písmenného kódu (tedy A-A, B-B,...) a vždy se nastaví podmínky na vývodech A-D, případně DP pro cifru, jejíž společnou elektrodu připojíme, čímž cifru aktivujeme. Nějaký čas vyčkáme a společnou elektrodu odpojíme, nastavíme podmínky pro další cifru a připojíme společnou elektrodu další cifry. Lidské oko nepostřehne, že nesvítí všechny cifry současně za podmínky, že bude dostatečně vysoká obnovovací frekvence (V řádu desítek Hz). Výhodou pro nás je úspora I/O pinů mikroprocesoru a zjednodušení v zapojení. Nevýhodou je to, že pokud bychom měli program, který má časově náročné úseky, pak by mohlo docházet k poblikávání zobrazovače. Velmi často se využívá přerušení vyvolané po přetečení čítače, kterému do rutiny přerušení napíšeme algoritmus zajišťující výpis na zobrazovač.

Pro úplnost uvedu metodu statického řízení segmisegmentového zobrazovače, která spočívá v trvalém připojení všech společných elektrod k napájení či zemi, která ovšem vyžaduje zvlášť řídit každý jednotlivý segment každé cifry. Zmiňované řešení má výhodu v tom, že v případě použití časově náročného programu, jak jsem uvedl předtím, nebude v důsledku tohoto řešení docházet k poblikávání zobrazovače, nicméně budeme potřebovat větší počet I/O pinů. Dalším možným řešením je použití tzv. Řadiče, který samotný proces zobrazování provádí zcela samostatně, pouze mu předáváme data k výpisu či řízení. Řadiče se využívají velmi často ve formě specializovaného integrovaného obvodu, mikrokontroléru či je na modulu s displayem přímo integrován, což je případ většiny LCD zobrazovačů, které jistě budou předmětem nadcházejících dílů.

Ukázkový program- čítač impulzů

Čítač impulzů je zařízení, které může být využito například jako počítadlo tahů při hře šachů, na sportovních akcích či, po doplnění optozávorou, jako počítadlo průchodů osob objektem.

Program bude v nekonečné smyčce vypisovat na displej hodnotu proměnné, která bude inkrementována detekcí impulzu na vstupu externího přerušení. Vstup !RESET využijeme pro připojení tlačítka, kterým budeme provádět nulování. Jak jistě vidíte, tato aplikace již má i nějaké praktické uplatnění, než předchozí, ryze demonstrativní příklady.

Popis aplikace začneme uvedením schématu zapojení, ve kterém si budeme muset objasnit smysl některých elektronických komponentů, které jsme v tomto seriálu doposud nemuseli znát.

Ve schématu můžeme vidět kondenzátor C1, který slouží k filtraci napájecího napětí, který je v konečné realizaci nezbytný, nicméně při testování nenáročných aplikací v kontaktním poli jej lze vynechat. Rezistory R3-R9 slouží k omezení proudu tekoucího zobrazovačem, aby se jednotlivé LED diody nezničily. Rezistor R1 slouží jako pull-up rezistor pro ochranu mikrokontroléru před resetem do počátečních podmínek zajištěním logické 1 na vstupu !RESET. Tentokrát nebudeme používat zmiňovaný pin pouze při programování, ale také při nulování našeho počítadla. Po stisku tlačítka „S1“ dojde ke skoku na adresu 0 v programové paměti a program začíná od začátku; konfigurační registry jsou nastaveny do výchozích hodnot. Vstup externího přerušení INT0 je nastaven do módu rakce na sestupnou hranu signálu, která se objeví po stisku tlačítka „S2“ chráněného proti překmitům pomocí kondenzátoru „C1“.

Prvky označené jako „Q1“ a „Q2“ se nazývají bipolární tranzistory. Tranzistor je opatřen třemi vývody: emitor, báze a kolektor. Ve schématu je kolektor připojen na +5V a emitor k anodě zobrazovače. Tranzistor se v tomto zapojení chová jako elektronický spínač, který se otevře v případě, že zbylým vývodem- bází, protéká proud. Proud protékající bází je omezen rezistory R1 a R2 a jeho výpočet zde nebudu rozebírat, jelikož s programováním nijak nesouvisí a případní zájemci naleznou jistě dost literatury s tímto tématem související. Tranzistor lze provozovat také v režimu analogového zesilovače, který je schopen například zesilovat audiosignál apod. Zapojení vývodů nejběžnějšího bipolárního tranzistoru BC547 je uvedeno níže. Jedná se o tranzistor „NPN“; existuje totiž i typ „PNP“, který je polarizován naopak a ve schematické značce má šipku u emitoru směřující dovnitř.

Průběh programu

Program na začátku nastaví konfigurační registry přerušovacího systému, nastaví odpovídající bity jako výstupy a aktivuje pull-up rezistory tam, kde je potřeba. V nekonečné smyčce programu dochází k neustálému vypisování hodnoty proměnné „pocet_impulzu“ na LED display. Hlavní smyčka programu je přerušována externím přerušením za účelem inkrementace proměnné „počet_impulzu“. V případě, že by mělo dojít ke stavu, kdy by tato proměnná měla nabýt hodnotu vyšší, než 99, podmínka v rutině přerušení ji automaticky nastaví na hodnotu 0xFF, čímž je zajištěno, že nedojde k přetečení proměnné a samovolnému čítání od nuly. Pokud dojde k detekci neplatné hodnoty proměnné „pocet_impulzu“, na display se rozsvítí pouze segmenty „G“ jako detekce chyby. Jak již bylo uvedeno, nulování čítače se provádí tlačítkem „S1“, které vyvolá reset. Pro reset je v tabulce vektorů přerušení, uvedené v datasheetu, přiřazen vektor 0, což je zmiňovaný skok na počátek programu.

V programu byla deklarována funkce „zobraz()“, sloužící k zobrazení daného čísla na danou cifru a funkce „cislo_disp()“, která navrací buďto jednotkovou či desítkovou cifru vstupního čísla v závislosti na proměnné určené k nastavení cifry umístěné v argumentu.

Všechny potřebné detaily fungování programu se dozvíte v přiloženém zdrojovém kódu ukázkového programu.

Fotografie výsledné aplikace:

Zdrojový kód praktického příkladu je k dispozici ke stažení.


Líbil se Vám článek?
Budeme potěšeni, pokud vás zaujme také reklamní nabídka

Social Bookmarking

     





Hodnocení/Hlasovalo: 1.75/4

1  2  3  4  5    
(známkování jako ve škole)