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

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

Autor: Prog0el   
23.1.2016

V minulém díle jsem zmínil, že popisovaný praktický příklad by bylo možno realizovat jednodušeji s použitím cyklů. V tomto díle se naučíme stěžejní část jazyka C, která se souhrnně popisuje jako „větvení programu“.


Podmínka „if“

Podmínka je blok kódu vymezený složenými závorkami, který se provede pouze tehdy, když je výsledkem výrazu v argumentu podmínky if logická 1, čili pravda. Celý blok se přeskočí, pokud podmínka neplatí.

Příklad
  1. if (a==10) {
  2.   a++;
  3. }

Výše uvedená podmínka provede inkrementaci proměnné „a“ pouze tehdy, když je proměnná „a“ rovna 10. Všimněte si dvojího „rovnítka“, jelikož se nejedná o operátor přiřazení, ale o logickou operaci rovnosti. V jazyce C platí, že 0 je chápána, jako logická 0 a vše ostatní je považováno jako logická 1 (tedy i záporná čísla).

V podmínkách a cyklech můžeme používat následující „relační“ operátory:


Relační operátory
operátorvýznam
==rovnost
!=nerovnost
>větší než
<</td>menší než
>=větší nebo rovno
<=menší nebo rovno

Použití operátoru přiřazení v podmínkách či cyklech vždy navrací chybu kompilace.

Velice často potřebujeme sloučit více podmínek do jedné, čehož můžeme dosáhnout více metodami. První a zároveň méně vhodnou metodou je využití „vnořování“ podmínek, což je metoda, při které se v bloku kódu vymezeného jednou podmínkou (často se také používá termín „tělo podmínky“) nachází další podmínka či podmínky.

Příklad
  1. if (a==10) {
  2.   if (b>5) {
  3.     b--;
  4.   }
  5. }

Pokud se podíváme na podmínky z pohledu mikroprocesoru, který program vykonává, tak nejdříve ověří platnost první podmínky a pouze pokud platí, ověří platnost další podmínky. Požadovaná akce, v našem případě dekrementace proměnné „b“, se tedy provede pouze, pokud platí a==10 a zároveň b>5.

Poněkud elegantnějším řešením je použití tzv. logických operátorů a podmínky sloučit do jedné. Náš předem uvedený příklad bychom touto metodou mohli upravit na následující tvar:

Příklad
  1. if((a==10)&&(b>5)){
  2.   b--;
  3. }

Všimněte si, že jsem zase použil zdvojený tvar operátoru, který již znáte z předchozího dílu. Je nutno si uvědomit, že z podmínky „a==10“ a podmínky „b>5“ vystupuje pouze jednobitový údaj TRUE nebo FALSE. S těmito údaji pracuje logický operátor, jehož výstupem je zase pouze TRUE/ FALSE. Závorky na oddělení dílčích podmínek výrazně přispívají ke zlepšení štábní kultury vašeho zdrojového kódu.


logické operátory
operátorvýznam
&&logický AND („Všechny podmínky musí být v platnosti“)
||logický OR („Alespoň jedna podmínka je v platnosti“)

Rozšíření podmínky „if“ o „else“ a „else if“

Při programování se vám může často stát, že potřebujete ošetřit případy, kdy daná podmínka neplatí, ale platí podmínka jiná, či žádná zadaná podmínka není v platnosti. K tomuto účelu se používá klíčové slovo „else“, či jeho modifikace „else if“.

Klíčové slovo „else if“ lze v takřka neomezeném množství používat jako vymezení části kódu, který se provede, pokud neplatí podmínka či podmínky v předcházející podmínce „if“ a zároveň platí podmínka v argumentu větvení „else if“.

Klíčové slovo „else“ lze, stejně jako podmínku if, v daném větvení použít pouze jednou a tělo bloku takto vymezeného se provede pouze tehdy, pokud neplatí žádný argument podmínky „if“ či podmínek „else if“ tento výraz předcházející.

Příklad
  1. if (a==10) {
  2.   a--;
  3. } else if (a<10) {
  4.   a++;
  5. } else if (a>20) {
  6.   a=0;
  7. } else {
  8.   a=0xFF;
  9. }

Cyklus „while“

Cykly se, na rozdíl od podmínek, opakují, dokud platí podmínka uvedená jako argument. V našich předchozích příkladech jsme se již s cyklem „while“ setkali a to konkrétně za účelem realizace hlavní smyčky programu, která se stále opakuje. Argumentem v cyklu bylo číslo „1“, které fungovalo, jako logická funkce verum. Kdyby v argumentu bylo číslo „0“, cyklus by se nikdy neopakoval a neměl by smysl- vznikl by tzv. „nedosažitelný kód“.

Avšak mnohem častěji budete zapisovat cykly „while“ s podmínkou, která se tvoří zcela totožně jako v případě podmínek „if“, tedy pomocí již zmíněných „relačních operátorů“.

Příklad
  1. unsigned char a=10;
  2. unsigned char b;
  3. while (a!=0) {
  4.   a--;
  5.   b+=2;
  6. }

Výsledkem tohoto příkladu bude b=20 a a=0. Cyklus se opakuje tak dlouho, dokud „a“ není rovno 0.

Průběh cyklu lze modifikovat klíčovým slovem „break“, který přeruší vykonávání cyklu i přes to, že podmínka stále platí. Klíčové slovo „continue“ zapříčiní návrat na začátek cyklu k části ověřování platnosti podmínky. Tyto dva příkazy lze zapsat do cyklu samostatně, program bude fungovat, avšak opět by vznikl „nedosažitelný kód“, jelikož by jejich použití na ničem nezáviselo. Z tohoto důvodu se nejčastěji do cyklu while vnoří podmínka if, v jejímž těle se vyskytují ona zmíněná klíčová slova.

Příklad
  1. while (a<100) {
  2.   if(b==2){
  3.     continue;
  4.   } else if (b==99) {
  5.     break;
  6.   }
  7.   a++;
  8. }

Cyklus „for“

Cyklus „for“ je velice často používán k dosažení daného počtu iterací části kódu. Stejně tak, jako přepínač byl zjednodušením posloupnosti podmínek „if“, tak i „for“ lze nahradit, a to konkrétně cyklem „while“, jak si ukážeme níže.

Příklad
  1. for (i=0; i<10; i++) {
  2.   //Kód umístěný zde se bude opakovat desetkrát
  3. }
  4.  
  5. //Náhrada cyklem „while“:
  6. i=0;
  7. while (i<10) {
  8.   i++;
  9. }

V argumentu cyklu „for“ se nejdříve nachází inicializace proměnné, které se v tomto případě říká „iterátor“ a je zvykem používat proměnnou s názvem „i“, avšak lze použít samozřejmě i jiné jméno proměnné. Mějte na paměti, že při inicializaci dochází k přiřazení; proto je použit znak „=“ namísto „==“. V případě, že chcete z nějakých důvodů ponechat původní hodnotu proměnné a počítat s ní v cyklu „for“, nahraďte první argument pouhým středníkem.

Následuje podmínka, která vymezuje, při jaké hodnotě iterátoru se tělo funkce provede. Poslední částí je definice akce, která se provede při iteraci, tedy, zda se iterátor inkrementuje či dekrementuje.

Přepínač „switch“

Představte si situaci, kdy máte nějakou proměnnou a potřebujete, aby se při určité její hodnotě provedl blok kódu. S dosavadními vědomostmi, které jste v tomto seriálu nabyli, byste jistě pomysleli na podmínku „if“. V případě potřeby provést odlišné bloky kódu, například pro deset hodnot proměnných, by po sobě následovalo deset podmínek „if(x==...“, což by bylo značně nepřehledné a zdlouhavé, co se týče času vykonávání této části kódu.

Řešením tohoto problému je zmíněný „přepínač“, který má daný výraz v argumentu a pak následují „případy“, které se vykonají, pokud se výraz v argumentu rovná číselné hodnotě pro ně uvedené a následně se zbytek přepínače přeskočí.

Příklad
  1. switch (x) {
  2.   case 0:
  3.     //jestliže se x rovná 0
  4.     break; //Každý případ musí být ukončen příkazem „break;“
  5.   case1:
  6.     //jestliže se x rovná 1
  7.     break;
  8.   case2:
  9.     //jestliže se x rovná 2
  10.     break;
  11.   default:
  12.     //Blok kódu, který se provede, když žádné předchozí případy nebyly vykonány.
  13.     break;
  14. }

Port jako vstup

Programy, které jsme v našem seriálu doposud psali, jednaly samy za sebe, bez možnosti, aby uživatel do běhu programu nějakým způsobem zasahoval, což změní následující odstavce.

V prvním díle jsem se zmínil o registru „DDRx“, pomocí kterého můžeme nastavit požadované piny daného portu (jeho písmenným kódem nahradíme malé „x“) do módu vstup (log.0) či výstup (log.1). Využíváme shody číslování pinů portu a bitů registru „DDRx“ potažmo „PORTx“. V případě, že je daný pin nastaven jako vstup, tak se mění funkce konfiguračního registru „PORTx“, který v módu „výstup“ nastavoval logickou hodnotu výstupu. V módu „vstup“ dochází k aktivaci (log.1) či deaktivaci (log.0) tzv. „interních pull-up rezistorů“.

Pull-up rezistor je připojen mezi daný vstup a kladnou větev napájení (+5V); zajišťuje na vstupu log. 1. Opakem je „pull-down“ rezistor, který je připojen k záporné větvi a udržuje log. 0 na vstupu.

Pull-up či pull-down rezistor je velice užitečný při eliminaci rušení a často je nezbytný při připojování jiných integrovaných obvodů k mikrokontroléru, které mají na výstupu namísto polomostu otevřený kolektor, což si ukážeme později.

Příklad
  1. DDRB = 0b11110000;
  2. PORTB = 0b00000000;
  3. /*
  4. U nejvyšších čtyř bitů byly aktivovány interní pull-up rezistory a u nejnižších čtyř bitů byla tato funkcionalita deaktivována. Deaktivací připojení interních pull-up rezistorů v módu „ je vstup nastaven do stavu vysoké impedance a je náchylný k rušení.
  5. */

Samotné čtení hodnoty daného portu se děje čtením registru „PINx“ dle uvedených pravidel o číslování pinů/ bitů. Pokud požadujeme znalost hodnoty pouze některých bitů, budeme muset použít funkci „AND“, což si ukážeme v praktickém příkladu.

Ošetření vstupu a zákmity

V předchozí stati jsem lehce nastínil ochranu vstupu proti rušení technikou zajištění logické úrovně na vstupu. Při řešení eliminace rušení je nutno předem vědět, k čemu bude vstup určen; například, zda bude vstupem AD převodníku, či tlačítka na klávesnici. V tomto díle se budu zabývat pouze připojením tlačítka, které nám bude zatím stačit.

Nejdříve bude jistě vhodné definovat, co je tlačítko čistě z elektrotechnického hlediska. Tlačítko je elektrotechnická součástka, která na základě silového působení na jeho hmatník vodivě spojí či rozpojí dva a více kontaktů, které jsou opatřeny vývody. Rozeznáváme čtyři stavy, které na tlačítku mohou nastat: „Sepnuto“, „rozepnuto“, „spínání“ a „rozepínání“. Poslední dva pojmy příliš nebudeme uvažovat, jelikož nejsme v silnoproudé elektrotechnice a rizika vzniku obloukových výbojů nás nemusí zajímat. Budeme se tedy zajímat pouze o stavy „sepnuto“, „rozepnuto“ a události blízké jejich nástupu.

Zmíněné události jsou nazývány „zákmity“ a způsobují, že při stisku spínače dojde k několikanásobnému sepnutí a rozepnutí, než dojde k ustálení vodivého spojení kontaktů. Tento jev probíhá v řádech několika milisekund, nicméně mikrokontrolér je dostatečně rychlý na to, aby tento jev považoval za několikanásobný stisk tlačítka. Zákmity jsou jedním ze dvou jevů, který budeme ošetřovat; druhým jevem je již nastíněné rušení na vstupu.

Pokud bychom zapojili tlačítko na pin mikrokontroléru nastavený jako vstup s deaktivovanými pull-up rezistory a chtěli jeho stisk detekovat přítomností napájecího napětí na vstupu (viz schéma výše), narazili bychom na problém.

Problémem by bylo pravděpodobné rušení na vstupu v případě, že není tlačítko sepnuto, protože logická úroveň je v tomto konkrétním zapojení zajištěna pouze při stisku tlačítka. Problém by vyřešil „pull-down“ rezistor, avšak ten bychom v případě použití mikrokontroléru AVR museli připojit externě a proto se uchýlíme k realizaci s „pull-up“ rezistorem.

Rezistor R1 je připojen pro lepší představu, jak je „pull-up“ rezistor zapojen, avšak v reálném schématu je vynechán, pokud je aktivován interně. Tímto jednoduchým krokem jsme hardwarově definovali logickou úroveň v případě, že není tlačítko stisknuto (log.1). V případě, že je tlačítko stisknuto, vstup je připojen na nulový potenciál a bit PD7 je v log.0.

Teď již bývá vyřešit problematiku zákmitů, kterou lze řešit hardwarově připojením kondenzátoru paralelně s tlačítkem; což ilustruje následující schéma:

Další možností je softwarové řešení ošetření zákmitů, které spočívá v těchto krocích:

  • Zjistit log. hodnotu na vstupu
  • Pokud není klidová (log.1):
    • Vyčkat několik desítek milisekund
    • Zjistit log. hodnotu na vstupu
    • Pokud i nadále není log. hodnota klidová, tlačítko může být považováno za stisknuté.

Realizace v jazyce C vypadá následovně:

Příklad
  1. if ((PIND&0b10000000)==0) {
  2.   _delay_ms(25);
  3.   if((PIND&0b10000000)==0){
  4.     //Kód, který se má vykonat v případě stisku tlačítka umístěte sem.
  5.   }
  6. }

V podmínkách bylo využito logické funkce „AND“, která vymezila, že pouze změna na nejvyšším bitu do log. 1 může vyvrátit pravdivost podmínky v argumentu. S velikostí časové prodlevy doporučuji experimentovat; osvědčilo se mi použití zpoždění mezi 10÷30ms pro tlačítka s rychlou odezvou na stisk a přibližně desetinásobné zpoždění pro tlačítka chráněná proti nechtěnému krátkému stisku.

Výsledné schéma zapojení tlačítka k mikrokontroléru:

Z hlediska realizace samotného mechanismu uvnitř spínače jsou nejčastěji používané kontakty „spínací“, které jsem popsal v této kapitole. V praxi se ještě vyskytují kontakty rozpínací, které při silovém působení na hmatník dva kontakty naopak rozpojí a v klidu jsou spojeny.

Praktický příklad

Tentokráte budeme mít hned dva praktické příklady. Prvním bude slibovaná demonstrace efektu „běžící had“ s využitím větvení programu a druhým bude obvod pro ovládání LED diody tlačítkem.

Světelný had

Použití větvení programu umožňuje úsporu programové paměti a v neposlední řadě také zlepšuje čitelnost zdrojového kódu. K popisovanému efektu se již více nebudu vracet, jelikož byl předmětem minulého dílu. Ve druhé verzi softwaru tohoto efektu si povšimněte syntaxe jednotlivých větvení a pokuste se „z hlavy“ o simulaci běhu programu (kolikrát se který cyklus zopakuje a co se tím změní). Tuto dovednost jistě využijete při další práci nejen s mikrokontroléry AVR, ale v programování obecně a to konkrétně při hledání chyb.

Soubor bezici_had-2.c ke stažení.

Ovládání LED diody tlačítkem

V tomto praktickém příkladu již využijeme znalosti o tlačítcích a ošetřování chyb, které na nich mohou vzniknout. Cílem bude zařízení, které bude schopno přepínat tři režimy provozu LED diody „LED1“:

  • LED zhasnuta
  • LED trvale svítí
  • LED bliká

Popisované zařízení většina z vás zná jako bezpečnostní světlo na jízdní kolo, které velmi často tuto funkcionalitu nabízí.

Přiložený soubor sw_led.c obsahuje program, který v nekonečné smyčce programu čte stav tlačítka a v případě, že je tlačítko stisknuto, dojde ke změně módu svitu. Zdánlivě nesmyslný se může zdát cyklus „while((PINB&0b00000010)==0){}“, který v případě stisku tlačítka neustále opakuje prázdnou smyčku, pomocí které čeká, dokud nebude tlačítko uvolněno a zabraňuje tak opakování změny módu při držení tlačítka.

Kód je dostatečně okomentovaný, proto by jeho pochopení nemělo způsobovat potíže. Kdyby se i přes to nějaké problémy s porozuměním vyskytnuly, neváhejte mě kontaktovat.

Schéma zapojení

Foto realizace na nepájivém kontaktním poli


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

Social Bookmarking

     





Hodnocení/Hlasovalo: 1/3

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