Je na čase vymést pavučiny a utřít prach z vašich znalostí elektřiny. Zopakujte si zákony panů Gustava Roberta Kirchhoffa a Georga Ohma, protože se v dalších řádcích vrhneme na ovládání vstupně výstupních portů Atmelu. Nejdůležitější periferii vůbec. Protože všechno co Atmel dělá, dělá pomocí portů. A nic z toho co se děje vně Atmelu není program, všechno je to fyzika a elektronika. Připravte se prosím na to že budeme hodně mávat rukama a jen velmi málo programovat.
Ještě než začnu, rád bych jen vyjasnil názvosloví. Vývod (slangově také třeba pin) je ta kovová nožička, která vede z pouzdra mikrokontroléru, ta nožička kterou připojujete někam do obvodu. Anglicky může také nést označení GPIO (General purpose output / input), ale názvosloví v tomto ohledu není příliš jednotné. Vývody se pro jednodušší zacházení sdružují do portů a tvoří skupiny typicky po osmi (nebo po šestnácti). U Atmelů jsou porty pojmenovány písmeny. Takže existuje Port A, Port B, Port C atd. U menších atmelů nejsou porty plně obsazeny. Jinak řečeno třeba Atmega8 má celý Port D a Port B, ale Portu C jeden vývod chybí. Větší atmely jako třeba Atmega16 mají čtveřici portů, tedy 32 vývodů. Každý vývod má svůj název. Vývody portu C se jmenují PC0,PC1, ... až PC7. Číslování je tedy od nuly. Sdružování vývodů do portů má své výhody. S portem se dá zacházet jako s celkem a program tak může chytře používat celou osmici vývodů (ne však nutně). Každý vývod můžete konfigurovat nezvávisle na ostatních, takže vám nic nebrání pracovat s vývody individuálně. Valná většina čipů rodiny AVR má shodné ovládání portů a stejné možnosti. Těch možností není mnoho. Každý pin procesoru může být buď vstup nebo výstup. A když je vstupem můžete u něj zapnout interní pull-up rezistor. Což je prostě rezistor připojený mezi vývod a napájení čipu. Až na pár jemných nuancí je to vlastně všechno co vstupně výstupní porty umí. Možnosti si předvedeme třeba na čipu Atmega16A.
Tři nejnebezpečnější lidé na světě jsou: Technik, který provedl změnu v programu, uživatel, který dostal nápad a programátor, který drží v ruce pájku. Očekávám, že nejčastější čtenáři budou spadat do té třetí kategorie a proto nebude od věci si hned zezačátku, dokud jste čerství vysvětlit pár základních faktů, které platí tvrdě a neúprosně. V datasheetu ke každému integrovanému obvodu je sekce "Absolute Maximum Ratings", kterou je dobré si přečíst a brát ji vážně. Proto se na ni mrkneme společně."Absolute Maximum Ratings" jasně říká, že na žádném pinu kromě Resetu nesmí napětí překročit hodnotu Ucc+0.5V. Napětí Ucc je napájecí napětí čipu. Tedy při napájení 5V nesmíte přivést na vývod větší napětí jak 5.5V. Podobný limit existuje i pro záporné napětí. Na žádný z pinů nesmite přivést napětí nižší jak -0.5V. Každý pin má totiž proti GND a proti Ucc ochrannou diodu. Ta ale slouží pouze k ochraně před statickým nábojem a nebude schopná odvádět větší proudy vzniklé přepětím na kterémkoli vývodu. Reset pin snese až 13V. Tato schopnost ale slouží k HVP programování a z hlediska aplikace je slušné pracovat s Resetem jako s běžným pinem. Dalším limitem je maximální proud výstupu, ten by neměl překročit 40mA. Posledním limitem, který datasheet uvádí je celkový proud všemi napájecími piny. Ten činí 200mA u PDIP pouzder (ty s nožičkami) a 400mA u SMD a MLF pouzder. Že tento limit není problém překročit může ilustrovat nasledující případ. Určitě znáte takové ty dětské aplikace pro jednočip v podobě blikacího vánočního stromečku. Připojíte k Atmelu 30 LED a blikáte s nimi jak zběsilí, do každé LED pouštíte 15mA aby nesvítila jako bludička. Když si teď proudy sečtete zjistíte že 30x15mA dělá bezmála půl ampéry. Půl ampéry, která teče napájením Atmelu a porušuje to co se nikdy porušovat nesmí. Přirozeně některé překročení těchto limitů Atmel snese. Ze zkušenosti bych mohl referovat o mnohém porušení limitů, které nevedlo ke zničení čipu. Třeba o Tině, která vykonávala program a blikala LEDkou i při 12V napájení. Nikdy se ale na něco takového nespoléhejte a vždy si dobře rozmyslete zda používáte vstupy a výstupy v souladu s datasheetem. Murphyho zákony jsou neúprosné a může se vám stát, že při ladění aplikace na nepájivém poli uděláte chybu, která se ale neprojeví a vy si jí nevšimnete. Třeba přivedete 5V na vstup čipu s napájením Ucc=3.3V. Necháte vyrobit desky plošných spojů, osadíte je a nebudete se stačit divit proč vám najednou Atmely hoří :)
Vstup Atmelu zaručeně vnímá jako log.1 napětí které je větší než 0.6Ucc a jako log.0 napětí které je nižší jak 0.2Ucc. Může se stát, že vstup bude správně pracovat i mimo tyto limity, ale datasheet vám to nezaručuje. Zajímat vás tyto hodnoty bodou hlavně v situacích kdy budete chtít aby váš Atmel běžící s 5V napájením přijímal data z 3.3V systému. Vývody XTAL a Reset mají úrovně drobně jinačí, v případě potřeby je dohledáte v datasheetu v sekci "Electrical Characteristics". Do vstupů může téct jistý malý zbytkový proud. Datasheet ho charakterizuje jako menší než 1uA, ale z měření vím, že to typicky bývá o několik řádů menší. Jedinou vyjímkou je situace kdy na vstup přivádíte nějákou analogovou hodnotu, tedy mezi limity pro log.1 a log.0. Pak může do vstupů téct nějáký měřitelný proud. Takovým sitaucím by jste se měli vyhnout. V případě že na pin potřebujete přivádět analogovou hodnotu využijte možnosti vypnout vstupní buffer (jinak řečeno odpojit celou vstupní logickou část pinu). Jak to provést diskutuji na konci článku o komparátorech.
Výstup Atmelu má push-pull topologii. To znamená, že v log.1 dokáže dodávat proud a v log.0 dokáže odebírat proud z vnějšího obvodu. Výstupy jsou dost "tvrdé" na to aby mohly přímo ovládat LED (přes rezistor přirozeně !). "Tvrdost" výstupu se pozná podle toho jak moc protákající proud ovlivňuje jeho výstupní napětí. Ideálně tvrdý výstup má nulový výstupní odpor a ať skrze něj protéká jakýkoli proud, je jeho napětí stálé. Reálné porty ale tak moc tvrdé nejsou. Jak se proudové zatížení projeví na jejich napětí shrnují grafy v datasheetu v sekci 28.0.7. Z nich je patrné, že výstupní odpor se pohybuje okolo 25Ohmů při 5V napájení a 50Ohmů při napájení 2.7V. to znamená, že pokud zatížíte čip napájený 5V proudem 20mA, bude výstupní napěťová úroveň v log.1 přibližně 4.5V. Podobné paramery platí i pro opačnou situaci, kdy držíte výstup v log.0 a vnějším obvodem vám do výstupu teče proud. Tady je na místě malá poznámka o výkonu. Jestliže protéká výstupem proud 20mA a je na něm úbytek 0.5V (má tedy vnitřní odpor 25 Ohm) je na něm i výkonová ztráta P=U*I = 0.5*0.02 = 10mW. Ta se projevuje uvnitř atmelu a pokud podobným způsobem zatížíte více výstupů, může ztrátový výkon přispívat k ohřevu pouzdra. To vás většinou zajímat nebude, ale pokud budete využívat vnitřní teplotní senzor je potřeba k tomu přihlédnout.
Na každém vývodu nakonfigurovaném jako vstup je možné zapnout vnitřní pull-up rezistor. Ten vám v mnohých aplikacích ušetří pracné připojování pull-up rezistoru z vnějšku. Jeho hodnota je podle datasheetu mezi 20-50kOhm, ale můžete si ji snadno změřit přesně. Stačí pull-up rezistor zapnout a pak na vývod připojit proti zemi známý rezistor s hodnotou řádově v desítkách kOhm. Ten vytvoří s interním pull-up rezistorem dělič, na vašem rezistoru změříte napětí a s trochou matematiky se dopracujete k hodnotě interního pull-up rezistoru. Druhou možností je měřit přímo ohmmetrem, ale pak dbejte na správnou polaritu a připojte plus měřáku na Ucc. Mínus měřáku pak na příslušný vývod. Já na Attiny2313A naměřil hodnoty v rozptylu od 35KOhm do 36kOhm. Na resetu jsem pak naměřil 69kOhm (datasheet uvádí 30-85kOhm). Reset má pevný pull-up rezistor, takže ho nemůžete vypnout. Reset musíte držet v neutrálním stavu v log.1 (restartujete log.0), takže tuto vlastnost oceníte. Obecně díky internímu pull-up rezistoru se vám značně zjednodušuje návrh obvodů. Tlačítka, klávesnice, enkodéry, to všechno díky tomu můžete připojovat přímo na vývody Atmelu. Typicky také obvody připojené k externímu přerušení budou vyžadovat pull-up rezistor protože mají výstupy s otevřeným kolektorem. Díky tomu je jich pak možné připojovat na jeden vstup Atmelu více.
Ovládání najdete snad v každém návodu na internetu. Takže budu stručný. Každý port má registr DDRx (Data Direction Register), kde si za "x" dosaďte písmenný název portu (A,B,C...). Každý bit v tomto registru ovládá jeden z vývodů portu. Zápisem log.1 do příslušného bitu DDRx je příslušný pin nastaven jako výstup. Příklad: Jestliže chci PB3 nastavit jako výstup musím zapsat do DDRB registru log.1 na třetí místo (při číslování od nuly !). Například kombinace DDRB=0b00010010 nastaví PB1 a PB4 jako výstupy. Po restartu čipu jsou všechny DDRx vynulované, tedy všechny vývody jsou vstupem. Druhý registr nese název PORTx, kde si za x dosaďte písmenné označení portu. Tedy například PORTB, PORTC atd. Každý bit v tomto registru se opět váže k příslušnému vývodu portu (stejně jako u DDRx). Role každého bitu v registru PORTx je dvojí a záleží na tom jestli je daný pin nastaven jako vstup nebo jako výstup. Jestliže je pin nastaven jako vstup tak hodnota příslušného bitu v PORTx registru zapíná nebo vypíná interní pull-up rezistor. Log.1 ho zapíná, log.0 vypíná. Chci-li tedy zapnout pull-up rezistor na pinu PA0, musím do DDRA zapsat na nultou pozici nulu (konfigurace jako vstup) a pak do PORTA na nultou pozici jedničku (zapínám pull-up). Pokud je vývod nastaven jako výstup, pak hodnota v registru PORTx rozhoduje o tom zda na výstupu bude log.1 nebo log.0. Zapsáním log.1 na nultou pozici v PORTA i DDRA nastavíte PA0 jako výstup s log.1 (tedy Ucc). Pro každý pin tedy existují čtyři varianty. Vstup, vstup s pull-up rezistorem, výstup s log.0 a výstup s log.1. Předveďme si to na několika příkladech.
DDRA = 0b00000010; PORTA = 0b100000011;
DDRA = (1<<DDA1); PORTA = (1<<PORTA7) | (1<<PORTA1) | (1<<PORTA0);
DDRA = (1<<1); PORTA = (1<<7) | (1<<1) | (1<<0);
PORTA = PORTA | (1<<PORTA7) | (1<<PORTA5);
PORTA |= (1<<PORTA7) | (1<<PORTA5);
PORTA |= (1<<7) | (1<<5);
PORTA |= 0b10100000;
Opačná situace nastane když chcete vynulovat některý bit v registru. K tomu slouží konstrukce
PORTA = PORTA & ~((1<<PORTA7) | (1<<PORTA5));
PORTA &=~((1<<PORTA7) | (1<<PORTA5));
PORTA &= 0b01011111;
PORTB ^= (1<<PORTB7);
x = PINC;
PINB = (1<<PIN7) | (1<<PIN5);
PINB = 0b10100000;
PORTA |= (1<<PORTA7) | (1<<PORTA5);
if(PINA & (1<<PINA4)){ // udělej něco když je na PA4 log.1 }
if(!(PINA & (1<<PINA4))){ // udělej něco když je na PA4 log.0 }
PORTD &=~(1<<PORTD1); // pokud je PD1 výstup, je na něm držena log.0 DDRD |= (1<<DDD1); // zápis log.0 na "otevřený kolektor" DDRD &=~(1<<DDD1); // zápis log.1 na "otevřený kolektor"
Většina pinů může mít ještě alternativní funkci. Alternativní funkce pinů jsou uvedeny na začátku datasheetu v sekci "Pin Configurations". Tedy na obrázku, kde je rozpis vývodů Atmelu a jsou to ty názvy, které jsou v závorkách. Pokud je alternativních funkcí více, jsou odděleny lomítkem. Pro přehlednost uvedu stručný výčet těchto alternativních funkcí pro Atmega16.
V následujících odstavcích si Vám dovolím nabídnout malou kuchařku na typické problémy s nimiž se můžete při práci s mikrokontroléry setkat.
Můžete využít například některou z variant ze schematu č.1. Sepnutím tranzistoru (log.1 z Atmelu) se výstup uzemní. Rozepnutím tranzistoru (log.0 z Atmelu) se skrze pull-up rezistor (R1 nebo R3) přivede na výstup napětí (v tomto případě 12V). Celek tedy invertuje logickou úroveň. Z výstupu nesmí odetékat přiliš velký proud, protože takto postavený zdroj napětí je "měkký" a jeho výstupní napětí s odebíraným proudem klesá. Měkkost zdroje určuje hodnota pullup rezistoru (R1 respektive R3). Čím je jeho hodnota menší tím je výstup tvrdší a tím větší proud lze dodávat aniž by došlo ke znatelnému poklesu napětí na výstupu. Problém je v tom, že se zmenšováním odporu roste odběr ve stavu kdy je tranzistor sepnut (tedy kdy je výstup v log.0). Nelze tedy odpor zmenšovat donekonečna. Pokud potřebujete spínat trochu větší proud a tyto varianty vám nevyhovují, najdete postup v další kapitole. Varianta s bipolárním tranzistorem A) vyžaduje ještě rezistor R2, který omezuje proud do báze. Varianta B) s unipolárním tranzistorem je v tomto jenodušší a navíc její výstupní napětí dokáže dosáhnout nižších hodnot jak předchozí varianta. Bohužel ale varianta B) s FET nemusí být funkční při nízkém pracovním napětí (Atmega16A může pracovat už od 2.7V a jiné varianty už od 1.8V). Tak nízké napětí pak nemusí stačit na sepnutí tranzistoru a v takovém případě se musíte vrátit k variantě A). Pokud potřebujete transformovat logické výstupy například ze 3V na 5V určitě se poohlédněte po nějákém integrovaném obvodu, zjednoduší vám to práci (já s oblibou používám 74LVC4245). Přirozeně vám ale nikdo nebrání některou z těchto variant použít. Počítejte ale s citelným omezením na maximální provozní frekvenci. Jako ilustrace může posloužit záznam na obrázku č.1. Tranzistor BC548 v zapojení A) s pullup rezistorem o hodnotě 330R a odporem do báze 1k má značné spoždění při přechodu ze sepnutého stavu do rozepnutého. Spoždění přibližně 1.5us není pro většinu aplikací kritické, ale při komunikaci mezi logickým obvody, kde se počítá se spožděním hrubě pod mikrosekundu by to mohlo mít neblahé následky. A to byly hodnoty odporů voleny s ohledem na minimalizaci spoždění.
Pokud je stačí spínat v dolní větvi (tedy kladný konec zátěže je připojen ke zdroji) je situace jednodušší (viz schéma č.2) a můžete použít variantu A) nebo B). U varianty A) si ale pohlídejte aby proud do báze dostačoval požadovanému proudu kolektorem a příslušně tomu zmenšete rezistor R2. Zesilovací činitel signálních tranzistorů bývá mezi 100-500, takže ke spínání čtvrt ampéry stačí pustit do báze pár miliampér. U výkonových bipolárních tranzistorů bývá zesilovací činitel 20-50 a pro sepnutí proudu například 3A může být nutné pustit do báze proud větší jak 60mA a ten z výstupu atmelu nedostanete. V takovém případě můžete použít "darlington" tranzistor (E). Vždy ale dávejte pozor na výkonové zatížení a s ním spojený ohřev. Pokud takových výstupů potřebujete víc, můžete využít tranzistorových polí ULN2003 (nebo podobných). Tuto variantu uvítáte třeba při buzení multiplexovaného sedmisegmentového displeje. Varianta B) žádnými úbytky sice netrpí, ale zase je potřeba dávat pozor na provoz při malém napětí Atmelu, kdy by nemuselo dojít ke kompletnímu otevření tranzistoru. Zvláště výkonové MOSFETy s provozním napětím 60V a vyšší potřebují ke spolehlivému sepnutí třeba 4.5V. Ke spínání v horní větvi obecně poslouží varianty C) a D). Opět je na místě pouvažovat zda nepoužít integrovaný obvod - tzv. High side switch, který typicky obsahuje proudový limit, ochranu proti přehřátí a odpojení vstupu. Zvláště pokud hrozí že dojde ke zkratování výstupu. Na trhu jich je k dostání široká paleta, například jednoduchý obvod BTS3110 nebo inteligentější obvody jako například BTS5090 se schopností navíc měřit proud zátěží.
Výjmečně může nastat případ kdy potřebujete spínat větší proudy v horní větvi a shodou okolností má napájecí napětí pro zátěž stejnou hodnotu jako napájecí napětí čipu. Často se tím setkáte třeba při bateriovém napájení. Pak můžete využít variant F) a G) ze schematu č.3. Přivedením log.0 na vstup obvodu dojde k sepnutí tranzistoru a proud začne protékat do zátěže. Tak jak ve všech předchozím případech platí že provoz unipolárního tranzisotoru (varianta F) začne být problematický při nižších provozních napětích (3V a menší) a naopak provoz bipolárního tranzistoru (varianta G) vyžaduje zajistit při vyšších výstupních proudech (jak dejme tomu 0.5A) nutný proud z báze.
Následující variantu vám příliš nedoporučuji, ale uznávám, že může v určitých situacích ušetřit součástky. Teoreticky můžete k posílení proudu spojit několik výstupů dohromady například tak jak je na schematu č.4. V takovém případě musím zdůraznit že vás datasheet varuje že by celkový součet proudů neměl překročit 100mA (všimněte si že nepíšu nesmí, ale pouze neměl). To je ale ta nejmenší komplikace. Jakmile takto vývody spojíte a nakonfigurujete je jako výstupy musíte zařídit aby na nich vždy byly stejné hodnoty. Jinak řečeno nesmí se vám stát že jeden z výstupů bude v log.0 a druhý v log.1, v takovém případě by nastal zkrat a překročili by jste proudový limit pro vývody. Musíte to tedy v programu pečlivě ošetřit. Z toho také plyne že smíte sdružovat výstupy jen v rámci jednoho portu. Pokud by vás napadlo spojit třeba PD7 a PC0 jak je na schématu (s označením chybného zapojení), vězte že pak už nedokážete zaručit aby na obou portech byla stejná hodnota. Jakmile se totiž rozhodnete změnit hodnotu například na portu C, nemůžete ve stejný okamžik měnit hodnotu i na portu D ... (samozřejmě i to lze zachránitmalou oklikou. Nejprve jeden port přenastavíte jako vstup, pak na druhém změníte výstupní hodnotu, pak ji změníte na prvním a pak první zase překonfigurujete ze vstupu na výstup.)
Skoro jistě budete chtít vybavit svou aplikaci různými spínači nebo tlačítky, které budou typicky sloužit pro ovládání lidskou obsluhou. Nejjednodušší způsob jak je můžete připojit k mikrokontroléru (schema č.5), spočívá v jejich připojení takzvaně "proti zemi". Jeden konec tlačítka připojíte na zem a druhý konec na libovolný vývod Atmelu. Poté uvnitř atmelu zapnete interní pull-up rezistor a vše je připraveno. Jestliže tlačítko není stisknuto, dostane se na vstup skrze interní pull-up rezistor napětí Ucc, tedy log.1. Jestliže obsluha tlačítko stiskne, spojí se vybraný vývod Atmelu se zemí a bude na něm log.0. Vzhledem k tomu, že stisk tlačítka lidskou rukou je vždy relativně pomalý, má program spoustu času zjistit, že k němu došlo. Člověk je schopný stisknout tlačítko nanejvýš tak 5 krát za sekundu, program má tedy řádově desítky až stovky milisekund času. Díky tomu stačí aby program kontroloval stisk talčítka jen několikrát za sekundu. Když si ale zkusíte takto jednoduše tlačítko k Atmelu připojit, budete nemile překvapeni výsledkem. Mechanický kontakt bude vytvářet zákmity. Jakmile prst začne působit na hmatník tlačítka, začnou se kontakty uvnitř tlačítka přibližovat. Během přibližování se ale jemně chvějí, a jakmile se přiblíží na velmi malou vzdálenost tak se vlivem chvění mohou několikrát po sobě spojit a zase rozpojit. Případně se nespojit dokonale a na krátkou chvíli vznikne mezi kontakty tlačítka přechodový odpor. Tyto jevy vyústí ve velmi nepěkný průběh napětí, který můžete vidět na obrázku x1 a kterému se česky říká zákmity (anglicky bouncing).
Jestliže by program počítal stisky tlačítka dostatečně rychle (a programy umí počítat velmi rychle), dokázal by mezi stisky tlačítek započítat i zákmity. To je přirozeně nežádoucí, protože program by pak jeden stisk tlačítky vyhodnotil jako několik stisků. Tyto problémy je možné ošetřit mnoha cestami. Softwarové ošetření zákmitů spočívá v tom že necháte program záměrně sledovat tlačítka jen jednou za nějáký relativně dlouhý čas, dejme tomu 20ms. Softwarové ošetření se vám ale nemusí zamlouvat (třeba z důvodů lenosti :D ) a tak si můžete pomoci hardwarovým řešením. Stačí tlačítko přemostit kondenzátorem o vhodné hodnotě. Během krátkých zákmitů se přes pull-up rezistor nestihne filtrační kondenzátor nabít a zákmity se tak odfiltrují. Protože interní pull-up rezistor má hodnotu přibližně 35kOhm, zvolil jsem pro ukázku kondenzátor o kapacitě 100nF. Doba nabíjení kondenzátoru by v takovém případě měla být řádově 4ms. Dá se tedy odhadovat, že během zákmitů dlouhých řádově desítky mikrosekund se kondenzátor stihne nabít na napětí řádově desítky mV. Taková napětí bude mikrokontrolér spolehlivě považovat za log.0 a zákmity tak budou ošetřeny. Tomuto procesu se anglicky říká debouncing. Na obrázku x2 je pak vidět výsledek takového ošetření. Záměrně jsem stiskl tlačítko dvakrát po sobě co nejrychleji aby bylo vidět, že se během nich dokáže kondenzátor spolehlivě nabít a žádný ze stisků tím neutrpí.
Aby byl princip činnosti obvodu co nejzřetelnější, záměrně jsem mačkal tlačítko "špatně". Výsledek těchto pokusů vidíte na obrázku x3. Velké množství velmi nepěkných zákmitů nepřerostlo hodnotu přibližně 650mV. Z datasheetu Atmelu vyčtete že maximální úroveň napětí, při které čip vstup zaručeně vyhodnotí jako log.0 je 0.2Vcc, při napájení 3.3V je to tedy 660mV. Naše zákmity jsou v tomto případě nebezpěčně blízko. Bylo by tedy vhodnější zvolit kapacitu filtračního kondenzátoru raději vyšší, například 1uF.
Protože kapitola byla hodně teoretická, jste určitě hladoví po nějákém příkladu. Tak alespoň jeden jsem pro vás připravil. Jedná se o jednoduchý program, detekujicí stisk tlačítka. Na výstup PA0 vyvedeme LED diodu, která se bude přepínat s každým stiskem. Rutinu detekujicí pouze jeden stisk tlačítka budete jistě potřebovat, takže bude žádoucí trochu si ji okomentovat. Tlačítko připojíme na pin PA2, který nakonfigurujeme jako vstup s interním pull-up rezistorem. Pak necháme program skenovat stav 3 bitu v registru PINA, který obsahuje stav na vstupu PA2. Pomocí proměnné stav_tlacitka pak budeme detekovat sestupnou hranu na PA2. Jakmile k ní dojde, přepneme LED a vynulováním proměnné stav_tlacitka zabráníme tím dalšímu vstup do těla podmínky. Teprve až dojde k uvolnění tlačítka tak zápisem jedničky do stav_tlacitka podmínku opět odemčeme. Všimněte si použití maker. Při ovládání různých logických vstupů a výstupu je velmi vhodné využívat makra (a dobře je pojmenovávat). Jednak kvůli tomu, že je pak kód čitelnější a srozumitelnější. A hlavně proto, že je pak snazší měnit pozice pinů. Pokud bych chtěl LED přesunout na vývod PA5, stačilo by mi změnit makro LED_PREPNI a přirozeně i inicializaci portu A. Pokud bych makro nepoužil, musel bych ručně projít celý zdrojový kód a všechny příkazy ovládání LED ručně přepsat. Je skoro jisté, že bych během této úpravy některé řádky přehlédl. Ještě mnohem lepší by bylo pomocí maker vyřešit i inicializaci portů, ale nebudeme příklad komplikovat. Zkuste si na příkladu mimo jiné i co se stane když ošetření zákmitů neprovedete.
// A) Vstup tlačítka (dektece stisku) #include <avr/io.h> #define TLACITKO (PINA & (1<<PINA2)) #define LED_PREPNI PORTA ^= (1<<PORTA1) char stav_tlacitka=1; int main(void){ DDRA = (1<<DDA1); // PA0 výstup, PA2-PA7 vstupy PORTA = (1<<PORTA2); // na PA2 zapínáme pull-up while(1){ if((!TLACITKO) && stav_tlacitka){ LED_PREPNI; // prepni stav LED1 (na PA1) stav_tlacitka = 0; // "zamkni" tlacitko az do uvolneni (dektece sestupne hrany) } if(TLACITKO){ stav_tlacitka = 1; // "odemci" tlacitko (evidentne bylo uvolneno) } } }
Vzhledem k rychle se rozrustajícímu křemíkovému nebi je asi vhodné probrat pár zakázaných postupů.
1. Nikdy nespojujte dva výstupy (jedině pokud pracují s otevřeným kolektorem)
Pokud totiž jeden z nich bude v log.0 a jeden v log.1 dojde defakto ke zkratu a skoro jistě překročíte limit proudu pro vývod.
2. Nikdy nepřipojujte výstup na jakékoli pevné napětí opět hrozí překročení limitu proudu pro vývod.
3. Nikdy nepřipojujte na výstup LED bez ochranného odporu ... ale tohle snad patří do školky :)
4. Nikdy nepřipojujte na vstupy vyšší napětí než je dovoleno. Nezapomínejte na to hlavně pokud pracujete s nižším napětím. Stačí si nevšimnout, že na Atmel napájený 3V cpete výstup z 5V logiky.
5. Nikdy nepokládejte Atmel na polystyrenovou nebo igelitovou podložku, statická elektřina je nebezpečná.
Kdysi jsem narazil na vtipná videa poukazující na klasické chyby při zacházení se vstupy / výstupy mikrokontroléru. Jestli si s jejich ovládáním moc nevěříte, mrkněte na ně zde.
Home
V1.1
By Michal Dudka (m.dudka@seznam.cz)