Středoškolská odborná činnost 2006/2007 Mikrokontroléry AVR

Transkript

Středoškolská odborná činnost 2006/2007 Mikrokontroléry AVR
Středoškolská odborná činnost 2006/2007
Obor 10 - elektrotechnika, elektronika, telekomunikace a technická informatika
Mikrokontroléry AVR, programování a praktické použití
Autor:
Adam Bařtipán
GZW Rakovník,
Náměstí Jana Žižky 186,
269 01 Rakovník,
3. ročník
Konzultant práce:
Ing. Vladimír Bláha
(Beltes Rakovník)
Zadavatel práce:
GZW Rakovník
Rakovník, 2007
Středočeský kraj
1
Tímto prohlašuji, že jsem soutěžní práci vypracoval samostatně pod vedením Ing. Vladimíra
Bláhy a uvedl v seznamu literatury veškerou použitou literaturu a další informační zdroje
včetně internetu.
V Rakovníku dne 15.3.2007
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
vlastnoruční podpis autora
2
Obsah
Úvod...........................................................................................................................................3
Metodika.....................................................................................................................................4
Rodina AVR................................................................................................................................5
Základní programování...............................................................................................................7
Ovládání výstupu..........................................................................................................8
Ovládání výstupu pomocí tlačítka..............................................................................11
Čekací smyčky............................................................................................................13
smyčky čekající po určitou dobu......................................................................13
smyčky čekající na určitou událost...................................................................15
Přístup k 16-bit registrům...........................................................................................15
Efektivní volba proměnné..........................................................................................15
Přerušení.....................................................................................................................16
Ovládání periferií......................................................................................................................18
Čítače..........................................................................................................................18
Osmibitový čítač....................................................................................................18
Šestnáctibitový čítač..............................................................................................21
PWM......................................................................................................................24
Závěr.........................................................................................................................................27
Resumé.....................................................................................................................................28
Seznam použité literatury.........................................................................................................29
Internetové zdroje.....................................................................................................................30
Přílohy......................................................................................................................................31
Přehled modelů AVR..................................................................................................31
Tabulka režimů PWM.................................................................................................31
Ukázky katalogových listů AVR.................................................................................31
3
Úvod
Největším objevem ve 20. století, co se výpočetní techniky týče, byl bezesporu
procesor. Tato elektronická součástka dokázala i ve svých raných stádiích vývoje řešit logické
operace mnohonásobně rychleji než člověk. Prvním procesorem byl Intel 4004, měl
čtyřbitovou architekturu, jeho primární určení bylo pro kalkulátory. Vývoj šel dále a člověk
pokračoval ve zjednodušování své práce, což kladlo stále větší nároky na výpočetní výkon
procesorů. Stoupala jak taktovací frekvence procesorů, tak počet bitů, se kterými byl schopen
procesor pracovat v jednom strojovém cyklu. Výsledkem tohoto trendu byl první domácí
stolní počítač typu PC, který měl procesor, pevný disk pro program, paměť RAM pro
ukládání dat, se kterými je právě pracováno, a disketovou mechaniku pro ukládání výsledků
práce. Tento počítač měl také velké množství periferních obvodů pro komunikaci s
okolím(paralelní port, sériový port, grafickou kartu, atd.). Dnes sice vývoj pokročil o hodně
dále, ale standard IBM PC je stále stejný.
Obvody AVR nesou označení mikrokontroléry, těm se také dá říkat jednočipové
mikropočítače. Jednočipový znamená, že vše je obsaženo na jediném čipu, tedy v jednom
integrovaném obvodu. Mikropočítač je označení pro samostatnou funkční jednotku s
procesorem, tedy počítač typu IBM PC je také mikropočítač. Mikropočítače AVR mají téměř
stejné složení jako IBM PC, mají FLASH paměť pro uložení programu, paměť RAM, paměť,
EEPROM pro uložení výsledků práce a také velké množství periferních obvodů. Jediným
rozdílem je, že první IBM PC vážilo několik desítek kilogramů, bylo mnohonásobně
pomalejší a zabíralo spoustu místa. Naproti tomu mikrokontrolér AVR má zanedbatelnou
váhu, je malý a především velice rychlý.
Téma práce jsem si vybral, jelikož se mikrokontroléry zabývám ve svém volném čase
a konkrétně AVR proto, že v nich vidím velký potenciál. Dnes se používají především v
průmyslové technice - automatizaci, ale neodmyslitelnou roli hrají také v robotice, zábavní
technice a všeobecně v elektronice.
Po maturitě bych rád šel studovat mikroprocesorovou a mikropočítačovou techniku na
vysokou školu, jelikož je to odvětví, ve kterém bych chtěl později i pracovat.
4
Metodika
Vzhledem k tomu, že jsem nenašel téměř žádnou vhodnou literaturu v českém jazyce,
měl jsem k dispozici velkou většinu podkladů pouze v angličtině. Nemohu ale říci, že by mi
to příliš vadilo, v tomto odvětví tomu tak bylo vždy. Největším zdrojem informací pro mě
byly katalogové listy jednotlivých obvodů a Referenční příručka avr-libc(popis jazyka C pro
AVR). Velkou studnicí informací mi byl také internet.
Všechny programy, které v práci uvádím, jsou psány v jazyce C a kompilovány
pomocí avr-gcc s tím, že v programech jsou použity funkce z knihovny avr-libc. Uvádím to
proto, že zde existuje i několik jiných kompilátorů jazyka C pro AVR a také velké množství
knihoven. Každý kompilátor má svá specifika a nepodstatným parametrem je také cena, já
jsem zvolil avr-gcc, jelikož je jednak jako jeden z mála kompatibilní s klasickým C, navíc je
zdarma a volně šiřitelný pod licencí GPL, stejně tak knihovna avr-libc. Navíc je zde
kompletní vývojové prostředí pro systém MS Windows® jménem WinAVR.
Práce je členěna do dvou hlavních oddílů, v jednom popisuji způsob programování a
základní stavbu programu pro AVR. Ve druhé části popisuji způsob obsluhy některých
periferií, jelikož periferní obvody jsou to, co dává každému mikrokontroléru velkou
flexibilitu použití.
V práci se snažím vždy vysvětlovat, jaký příkaz k čemu slouží a případné možnosti
jeho náhrady, ať už pomocí rozboru programu nebo pomocí komentáře přímo ve zdrojových
kódech.
5
Rodina AVR
Roku 1997 uvedla na trh firma ATMEL nové osmibitové mikrokontroléry AVR, od
ostatních typů osmibitových mikrokontrolérů se liší tato rodina především tím, že používá
architekturu RISC, díky níž dosahuje jádro AVR vysokých výpočetních výkonů oproti svým
konkurentům.
Dalším důležitým faktorem, kterým se odlišuje od ostatních osmibitových
mikrokontrolérů, je optimalizovaný návrh pro programování v jazyce C. Možnost
programování v ostatních jazycích samozřejmě zůstává.
Kompletní instrukční sada čítá 135 instrukcí. Toto číslo ovšem není u všech modelů
stejné, nižší modely mají 118 instrukcí a ty nejnižší dokonce 89. Díky nízkému počtu
instrukcí, danému použitím architektury RISC(Reduced Instruction Set Computer), je
umožněno vykonávání velké většiny instrukcí v jednom hodinovém cyklu. Z tohoto faktu
plyne, že při maximální taktovací frekvenci 20MHz dokáže jádro AVR dosáhnout výkonu až
20MIPS(Milion Instruction Per Second), což je výkon například na úrovni procesoru
i80486SX, který byl samozřejmě dvaatřicetibitový. Toto porovnání je poněkud nepřesné,
jelikož použití obou obvodů je odlišné, i80486 dokázal například počítat podstatně rychleji
než AVR výpočty s plovoucí desetinou čárkou.
Velké oblibě mezi elektroniky se AVR těší především pro interní flash paměť a
možnost sériového programování. Flash paměť se v mikrokontroléru používá pro uchovávání
programu, je důležitá především kvůli možnosti opakovaného smazání a opětovného zapsání
nového programu. Obě operace trvají velice krátce, typicky několik sekund, doba je úměrně
závislá na velikosti paměti. Výrobce garantuje 10 000 cyklů smazání/zápis, u nižších modelů
pouze 1000. Sériové programování je výhodné díky jednoduchosti nahrávání programu z PC
do mikrokontroléru.
Většina modelů obsahuje kromě programové flash paměti také RAM a EEPROM,
ochuzeny o ně jsou pouze některé nejnižší modely z řady ATtiny. Paměť RAM zde slouží k
rychlým operacím s daty, naopak paměť EEPROM slouží, kromě uložení programu(pokud se
z nějakého důvodu rozhodneme neukládat ho do flash), spíše k ukládání dat, se kterými se již
dále nebude pracovat, například naměřené hodnoty.
Velice významnou roli při hledání a řešení chyb v programu hraje rozhraní JTAG,
které umožňuje kontrolu chování mikrokontroléru. Opět není dostupné u nejnižších modelů.
Mikrokontroléry AVR také nabízejí bohatou výbavu, co se týče periferií, jsou to
například čítače, časovače, PWM(Pulse-width modulation), AD konvertory, SPI, TWI(twowire interface, někdy též označované jako I2C), watchdog časovač, AD komparátory, interní
6
kalibrovaný oscilátor, RS232 a jiné.
Pod zkratkou AVR můžeme rozumět „Advanced Virtual RISC“, ale byla také vybrána
podle jmen autorů AVR architektury - Alf-Egil Bogen a Vegard Wollan. Bohužel prameny
neuvádějí, který výklad této zkratky je primární.
7
Základní programování
Budu se zde zabývat programováním v jazyce C, mikrokontroléry AVR se dají
samozřejmě programovat i v jiných programovacích jazycích, ale já jsem si vybral právě
jazyk C, protože je, dle mého názoru, nejkomfortnějším a umožňuje, na rozdíl od assembleru,
snadný přechod na jiný typ mikrokontroléru případně procesoru. Je to především proto, že
jádro AVR bylo navrženo pro programování v tomto jazyce. Pro pochopení příkladů
předpokládám u čtenářů alespoň částečnou znalost jazyka C na úrovni mírně pokročilý.
Vysvětlovat obecné programování v C by bylo mimo rozsah mé práce. Přesto si myslím, že
zde uvedené ukázkové programy jsou natolik jednoduché, že je pochopí i čtenář, který ovládá
některý jiný vyšší programovací jazyk.
K nahrání programu do AVR je potřeba speciální zařízení zvané programátor. Existuje
jich velké množství, některé jednodušší a levnější, jiné složitější a dražší. Úplně nejjednodušší
programátor se dá postavit téměř zadarmo, představuje ho pět drátů z paralelního portu PC k
mikrokontroléru, ovládací program je freeware. Obecně platí, že čím složitější programátor,
tím větší komfort při programování poskytuje. Existuje zde také velké množství komerčně
vyráběných programátorů, jejich ceny se různí podle výrobce a schopností.
Pro jednoduchost zde uvedu pouze jedno testovací schéma, na kterém ukáži všechny
příklady ovládání mikrokontroléru. Předesílám, že ve schématu neuvádím konkrétní hodnoty
součástek, jelikož jsou pro každý obvod různé. Všechny potřebné informace se dočtete v
katalogových listech k mikrokontroléru. Hodnoty předřadných rezistorů diod a tranzistorů
neuvádím ze stejného důvodu, vězte ale, že velikost předřadného rezistoru pro diodu se
pohybuje přibližně kolem 470Ω a pro tranzistor zhruba 10kΩ. Hodnoty berte jako orientační,
za případnou škodu neručím.
Testovací schéma:
8
Ovládání výstupu
Způsob, jakým se ovládají výstupní piny, ukáži na rozsvěcení a zhasínání LED diody.
Vzhledem k tomu, že zatím nechci program komplikovat čekacími smyčkami, ukážu
programy dva. Jeden který rozsvítí LED diodu a druhý který ji zhasne.
Veškeré ovládání AVR je založeno na registrech, ty mohou být buď osmibitové nebo
šestnáctibitové, proto si nejprve ukážeme, jak nastavit hodnotu celého registru najednou a jak
nastavit jednotlivé bity registru nezávisle na sobě. Jelikož jazyk C je velice propracovaný,
existuje spousta možností, jak toho dosáhnout, já ukáži pouze některé z nich.
AVR má vstupně výstupní(dále jen V/V) linky organizovány do bloků po maximálně
osmi linkách. Každá linka má vyhrazený jeden bit v ovládacích registrech, ty jsou
následující:
DDRX - data direction register
- bity určují, zda jsou linky vstupní nebo výstupní
- počet bitů podle počtu linek, maximálně 8
- místo X je velké písmeno označující port
PORTX - port X data register
- zápisem do tohoto registru se okamžitě přesune hodnota na
jednotlivé piny brány X za předpokladu, že jsou nastaveny jako
výstupní, v opačném případě zápis 1 do bitů nastavuje interní
pull-up rezistory na příslušných pinech
- počet bitů podle počtu linek, maximálně 8
- místo X je velké písmeno označující port
PINX - pin X input pins adress
- jednotlivé bity určují hodnotu na vstupních linkách
- počet bitů podle počtu linek, maximálně 8
- místo X je velké písmeno označující port
Ukázkový program rozsvícení LED diody:
#include <avr\io.h>
int main(void)
{
/*nastavení všech pinů PB jako výstup*/
DDRB = 0xFF;
/************************************************
znázornění registru DDRB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
9
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*nastavení log. 0 na všech pinech PORTB*/
PORTB = 0x00;
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | - hodnota bitu
+-------------------------------+
************************************************/
/*PB0 = log. 0, tudíž LED nesvítí*/
/*nastaví log. 1 na nultém pinu PORTB*/
PORTB = (1 << PB0);
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*PB0 = log. 1, proto LED svítí*/
return 0;
}
Z uvedeného programu bych chtěl zmínit pouze řádku PORTB = (1 << 0);, je to
nastavení registru PORTB na 1 (01 hexadecimálně, 00000001 binárně), tento způsob je pouze
jedena z mnoha možností, jak nastavit hodnotu některého bitu v registru, pro ilustraci uvedu
několik dalších možností se stejným efektem:
PORTB = 0x01;
-zapsání hexadecimální hodnoty do registru
PORTB = 0b00000001;
-zapsání binární hodnoty do registru
PORTB |= _BV(PB0);
-nastavení bitu 0 v registru pomocí makra _BV(), které je definováno
jako _BV(bit) (1 << (bit)), což jsem vlastně použil v programu
V programu jsem použil symbolickou konstantu PB0, vězte, že tento název pinu je
definován v hlavičkových souborech každého mikrokontroléru, který má PORTB, stejně jako
například PB1, PB6 nebo PC3 pro PORTC. Hodnota této konstanty pouze určuje pořadí bitu
10
v registru. Platí tedy, že PB0 = 0, PB1 = 1, PC3 = 3, atd. Toto je zavedeno pouze z důvodu
větší přehlednosti a lepší orientace v kódu.
Ukázkový program zhasnutí LED diody:
#include <avr\io.h>
int main(void)
{
/*nastavení všech pinů PB jako výstup*/
DDRB = 0xFF;
/************************************************
znázornění registru DDRB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*berme v úvahu, že z předešlého příkladu je PB0 = 1*/
PORTB = (1 << PB0);
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*PB0 = log. 1, proto LED svítí*/
/*nastavení log. 0 na PB0*/
PORTB &= ~(1 << PB0);
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | - hodnota bitu
+-------------------------------+
************************************************/
/*PB0 = log. 0, tudíž LED nesvítí*/
return 0;
}
Pro tento program platí vše co pro předešlý, je s ním naprosto analogický, zastavím se
pouze u řádky PORTB &= ~(1 << PB0);, která může být na první pohled poněkud
nesrozumitelná. Nejprve ji tedy rozepíši na PORTB = PORTB & ~(1 << PB0). Místo 1 <<
PB0 mohu napsat 1 << 0, z čehož vyplývá, že hodnota tohoto výrazu je 00000001 binárně.
11
Operátor ~ je tzv. jedničkový doplněk, což znamená, že vrací číslo, ve kterém jsou na místech
nul v operandu jedničky a obráceně, dalo by se říci, že binární číslo otočí „naruby“. Pokud je
tedy hodnota operandu 00000001, jako v tomto případě, operátor ~ vrací 11111110. Operátor
& značí logickou konjunkci, takže pokud je PORTB = 00000001, tak musí platit, že: PORTB
= 0b00000001 & 0b11111110, z čehož plyne, že PORTB má po této operaci hodnotu
0b00000000.
Shrnutí:
Pokud je potřeba nastavit hodnotu celého registru, je vhodné použít přiřazení
hodnoty celému registru, ne jednotlivým bitům. Pokud není potřeba nastavovat
všechny bity registru, je vhodné nastavovat jednotlivé bity zvlášť.
Ovládání výstupu pomocí tlačítka
Jak jsem se již zmínil, každá V/V linka, jak napovídá název, se dá nastavit také jako
vstup. V této kapitole si ukážeme, jak ovládat výstup pomocí připojeného tlačítka, na výstupu
bude opět pro jednoduchost připojena LED dioda.
Ukázkový program:
#include <avr\io.h>
int main(void)
{
/*nastavení portu D jako vstup*/
DDRD = 0x00;
/************************************************
znázornění registru DDRD:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | - hodnota bitu
+-------------------------------+
************************************************/
/*povolení interních pull-up rezistorů na portu D*/
PORTD=0xFF;
/************************************************
znázornění registru PORTD:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
12
/*nastavení portu B jako výstup*/
DDRB = 0xFF;
/************************************************
znázornění registru DDRB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*nekonečná smyčka, program se opakuje, dokud je připojeno
napájecí napětí a AVR není v reset stavu*/
while(1)
{
/*zjištění stavu PD0*/
if(bit_is_clear(PIND, 0))
{/*pokud je na PD0 log. 0, provede se následující kód*/
/*rozsvícení LED diody*/
PORTB = (1 << PB0);
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | - hodnota bitu
+-------------------------------+
************************************************/
/*čekání na puštění tlačítka*/
loop_until_bit_is_set(PIND, 0);
}
else
{/*pokud je na PD0 log. 1, provede se následující kód*/
/*zhasnutí LED diody*/
PORTB &= ~(1 << PB0);
/************************************************
znázornění registru PORTB:
+-7---6---5---4---3---2---1---0-+ - číslo bitu
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | - hodnota bitu
+-------------------------------+
************************************************/
/*čekání na stisknutí tlačítka*/
loop_until_bit_is_clear(PIND, 0);
}
}
return 0;
}
13
Činnost programu je triviální, v nekonečném cyklu kontroluje stav na pinu PD0, kde
je připojeno tlačítko. Pokud stiskneme tlačítko, přivedeme na vstup PD0 log. 0, což má za
následek změnu bitu PIND0 v registru PIND. V důsledku toho se splní podmínka
bit_is_clear(PIND, 0), takže se vykoná kód, který rozsvítí LED diodu. Dioda bude svítit tak
dlouho,
jak
dlouho
budeme
držet
tlačítko,
protože
čekací
smyčka
loop_until_bit_is_set(PIND, 0) se ukončí až ve chvíli, kdy tlačítko pustíme. V tu chvíli se
změní podmínka bit_is_clear(PIND, 0) na nepravdivou a provede se kód pro zhasnutí diody
po kterém se spustí opět čekací smyčka loop_until_bit_is_set(PIND, 0), která se ukončí až ve
chvíli. kdy opět stiskneme tlačítko.
Funkce loop_until_bit_is_set(), loop_until_bit_is_clear(), bit_is_set() a bit_is_clear()
jsou standardní funkce definované v hlavičkových souborech avr-libc. Co dělají, je patrné z
anglických názvů, přesto:
bit_is_set(PORTX, BITx)
-vrací 1 pokud je bit určený parametry = 1
bit_is_clear(PORTX, BITx)
-vrací 1 pokud je bit určený parametry = 0
loop_until_bit_is_set(PORTX, BITx)
- čeká na vynulování bitu v registru
loop_until_bit_is_clear(PORTX, BITx)
- čeká na nastavení bitu v registru
Pro nastavení V/V linky jako vstup nebo výstup se používají bity v registrech DDRX,
v příkladu je použit vždy jeden port jako vstupní a jeden jako výstupní, ale jde samozřejmě
nastavovat jednotlivé piny portu samostatně. Pokud chceme linku jako výstup, nastavíme její
bit v DDR registru na 1, pokud chceme linku jako vstup, nastavíme její bit na 0.
Čekací smyčky
Vzhledem k tomu, že v určitých případech je nežádoucí, aby se následující část kódu
provedla okamžitě po provedení předcházející, existují zde čekací smyčky. Dají se rozdělit do
dvou skupin:
smyčky čekající po určitou dobu
Příkladem může být následující funkce delay():
14
void delay(unsigned int cas)
{
unsigned int i, j;
for(i=0; i < 1000; i++)
for(j=0; j < cas; j++);
}
Jejím úkolem je udělat velký počet prázdných cyklů, které zpozdí vykonání kódu,
který následuje po ní. Vetšinou se nepoužívá pro přesné časy, k tomu účelu mají AVR tzv.
čítač/časovač. Své použití najde například pokud chceme blikat LED diodou, jak ukazuje
následující program:
#include <avr\io.h>
/*čekací smyčka*/
void delay(unsigned int cas)
{
unsigned int i, j;
for(i=0; i < 1000; i++)
for(j=0; j < cas; j++);
}
int main(void)
{
/*nastavení portu B jako výstup*/
DDRB = 0xFF;
/*nekonečná smyčka*/
while(1)
{
/*nastavení PB0 na log. 1*/
PORTB = (1 << PB0);
/*zpoždění 500 000 prázdných cyklů*/
delay(500);
/*nastavení PB0 na log. 0*/
PORTB &= ~(1 << PB0);
/*zpoždění 500 000 prázdných cyklů*/
delay(500);
}
return 0;
}
15
Činnost programu by měla být zřejmá. Rozsvítí se LED dioda, počká se 500 000
prázdných cyklů, zhasne LED, počká se 500 000 prázdných cyklů a program se vrací na
počátek. Samozřejmě se doba čekání různí podle taktovací frekvence AVR, takže při 8MHz
bude čekání 2x kratší než při 4MHz.
smyčky čekající na určitou událost
Zde se zmíním pouze o dvou základních - loop_until_bit_is_set(registr, bit) a
loop_until_bit_is_clear(registr, bit). Jejich úkolem je pozastavit vykonávání kódu dokud není
nastaven resp. vynulován daný bit v registru. Jejich použití je patrné z kapitoly Ovládání
výstupu pomocí tlačítka, proto zde nebudu uvádět ukázkový kód. Jejich typické použití je
právě při obsluze stisku tlačítka, kde zabraňují opakovanému provedení kódu při jediném
stisku tlačítka. Dalším typickým použitím je čekání na nastavení popřípadě vynulování
příznakového bitu, což je zapotřebí například při obsluze UART rozhraní(RS232), kde se při
vysílání řetězce kontroluje vyprázdnění vysílacího bufferu pomocí bitu UDRE v registru
USR.
Přístup k 16-bit registrům
Při pohledu do katalogových listu k jakémukoliv mikrokontroléru AVR, je patrné, že
obsahuje kromě osmibitových i šestnáctibitové registry. Šestnáctibitové registry jsou v
katalogových listech vždy rozděleny na dva osmibitové - horní byte a spodní byte. Tyto dva
registry se jmenují jako původní, pouze na konci mají přidáno písmeno H(HIGH - horní byte)
nebo L(LOW - spodní byte). V jazyce C se ale při přístupu k šestnáctibitovému registru, na
rozdíl od assembleru, nemusíme zdržovat inicializací každého osmibitového registru zvlášť,
můžeme přistupovat rovnou k šestnáctibitovému, takže místo:
TCNT1H = 0xFF;
TCNT1L = 0xFF;
píšeme:
TCNT1 = 0xFFFF;
Efektivní volba proměnné
Vzhledem k tomu, že mikrokontroléry AVR mají jen dosti omezené množství paměti
RAM, je důležité hlavně ve složitějších programech volit vhodnou velikost proměnné. Proto
16
jsou uměle vytvořeny typy proměnných int_x_t a uint_x_t, kde místo x je počet bitů, který
může být 8, 16, 32 a 64. Následující tabulka ukazuje maximální hodnoty proměnné typu
integer, kterých může nabývat:
Typ
int
int_8_t
int_16_t
int_32_t
int_64_t
Popis
- 16-bit, se znaménkem, <-32 768, 32 767>
- 8-bit, se znaménkem, <-128, 127>
- 16-bit, se znaménkem, <-32 768, 32 767>
- 32-bit, se znaménkem, <-2 147 483 648, 2 147 483 647>
- 64-bit, se znaménkem, <≈ -9e18, ≈ 9e18>
uint_8_t
uint_16_t
uint_32_t
uint_64_t
-
8-bit, bez znaménka, <0, 255>
16-bit, bez znaménka, <0, 65 535>
32-bit, bez znaménka, <0, 4 294 967 295>
64-bit, bez znaménka, <0, ≈ 18e18>
Přerušení
Představme si situaci, kdy potřebujeme pravidelně, jednou za určitý časový interval,
provést nějakou část programu, ale zároveň se musí vykonávat zbytek programu. K takovým
účelům slouží přerušení(angl. interrupt). Zaručuje, že pokud se vyskytne nějaká událost,
provede se její programová obsluha okamžitě, bez ohledu na aktuální stav programu.
Možná by bylo vhodné předvést si to na příkladu: Představme si, že máme za úkol
napsat program pro mikrokontrolér, který řídí napouštění nádrže a aktuální stav zobrazuje na
LCD display. Nádrž má být napuštěna po okraj. K mikrokontroléru je tedy připojeno ovládání
čerpadla, čidlo určující průtok vody čerpadlem, čidlo, které sepne ve chvíli, kdy je voda u
okraje nádrže, a display. Bez použití přerušení bychom pravděpodobné řešili program
použitím nekonečné smyčky, ve které by se prováděly kroky:
1. kontrola hladiny vody, pokud je u okraje, vypni čerpadlo a skoč na bod 3.
2. spusť čerpadlo
3. spočítej průtok
4. spočítej výšku hladiny
5. spočítej aktuální objem
6. přepočti objem na procenta
7. zobraz informace na display
8. vrať se na začátek programu
17
Jak je vidět, program obsahuje velké množství výpočtů, které zabírají většinu času v
programu. Lehce by se mohlo stát, že by během těchto výpočtů mohla nádrž přetéci(berme v
úvahu výkonné čerpadlo a nízkou taktovací frekvenci mikrokontroléru).
Jak je vidět, někdy je potřeba upřednostnit vykonání určité části programu před jinou.
Z toho důvodu existují přerušení. Program by vypadal stejně, jen by místo bodu 1. bylo:
0. obsluha přerušení - pokud je sepnutý hladinový spínač, vypni čerpadlo
Bod jsem označil jako 0., abych zdůraznil, že nepatří do hlavní smyčky programu. Číslování
bodů by se samozřejmě zmenšilo o 1 a 7(původně 8). bod by obsahoval skok na bod
1(původně 2).Takto upravený program by fungoval tak, že ve chvíli, kdy se např. na vstupu
INT0 objeví log. 1, nezávisle na aktuální pozici v programu, přeruší svou činnost a vypne
čerpadlo. Po vypnutí čerpadla(provedení obsluhy přerušení) se program vrací na místo, kde se
byl přerušen a pokračuje dále.
Přerušení
mohou
být
interní(vyvolané
interním
periferním
obvodem)
nebo
externí(přivedením log. 1 na vstupní pin INTx).
Před použitím jakéhokoliv přerušení se musí nejdříve globálně povolit, toho
dosáhneme funkcí sei(), která nemá žádné parametry. Každé přerušení má svůj bit, který se
musí nastavit, aby bylo možné ho používat. Při přerušení programu se neukládá hodnota
status registru, který obsahuje informace o aktuálním stavu procesoru a také důležité
informace pro aktuální výpočet. Proto je dobré si před obsluhou přerušení uložit hodnotu
registru do proměnné a na konci obsluhy ji tam zas vrátit.
Někdy je také potřeba zaručit, že vykonání kritického kódu nepřeruší dokonce ani
přerušení. K tomu slouží funkce cli(), opět bez parametrů. Toto je potřeba například při zápisu
do vnitřní EEPROM paměti, kdy velice záleží na časování jednotlivých signálů a při
přerušení by hrozilo selhání zápisu.
U AVR může být velké množství různých druhů přerušení, opět záleží na typu
mikrokontroléru. Každé přerušení má svou vlastní prioritu, která určuje pořadí vykonávání.
Bližší informace o konkrétním přerušení najdete v katalogových listech v sekci interrupt
vectors. Čím nižší číslo, tím vyšší priorita.
18
Ovládání periferií
Mikrokontroléry AVR mohou obsahovat velké množství nejrůznějších periferních
obvodů, bohužel není v rozsahu této práce je popisovat všechny, proto jsem si vybral jen ty,
které považuji za nejdůležitější. Na následujících příkladech demonstruji jejich ovládání.
Předpokládám, že po shlédnutí těchto ukázek by měl být čtenář schopen s pomocí
katalogových listů konkrétního mikrokontroléru používat kterýkoliv jeho periferní obvod,
postup je analogický s následujícími příklady.
Čítače
AVR zpravidla obsahuje alespoň jeden osmibitový a jeden šestnáctibitový
čítač/časovač(dále jen čítač). Počet bitů udává číslo, při kterém čítač přeteče. Pro osmibitový
je to tedy číslo 255, pro šestnáctibitový 65535, tato mez se může posouvat libovolně směrem
dolů. Zdroj hodinového impulsu pro čítač může být buď externí nebo interní. Pokud je zdroj
interní, je jím hodinový takt procesoru, který může být dělen 8, 64, 256 nebo 1024 v
předděličce. Každý čítač má své registry, kterými se ovládá.
Osmibitový čítač
Blokový diagram:
Pro obsluhu tohoto čítače slouží registry:
TCNTx - obsahuje aktuální hodnotu čítače x
TCCRx - ovládací registr, místo x je číslo čítače
- obsahuje bity CSx0, CSx1, CSx2
19
- tabulka nastavení hodinového vstupu:
CSx2 CSx1 CSx0 Popis
0
0
0 žádný zdroj signálu(čítač zastaven)
0
0
1 fclk
0
1
0 fclk/8
0
1
1 fclk/64
1
0
0 fclk/256
1
0
1 fclk/1024
1
1
0 externí vstup signálu z pinu Tx, čítá při sestupné hraně
1
1
1 externí vstup signálu z pinu Tx, čítá při vzestupné hraně
poznámka: fclk = frekvence jádra
TIFR - registr obsahující příznaky přetečení všech čítačů
TIMSK - registr s maskami přerušení všech čítačů
Ukázkový program obsluhy osmibitového čítače:
#include <avr/io.h>
#include <avr/interrupt.h>
#define sbi(PORT,BIT) PORT|=_BV(BIT)
#define cbi(PORT,BIT) PORT&=~_BV(BIT)
/*na PB0 je připojená LED dioda*/
#define LED PB0
/*obsluha přerušení, blikání LED diody se střídou 1:1*/
ISR(TIMER0_OVF_vect)
{
if(bit_is_clear(PORTB, LED))
{
sbi(PORTB, LED);
}
else
{
cbi(PORTB, LED);
}
}
int main(void)
{
/*nasstavení portu B jako výstup*/
DDRB = 0xFF;
/*povolení přerušení pro čítač 0*/
TIMSK = (1 << TOIE0);
/*nastavení počáteční hodnoty čítače 0*/
TCNT0 = 0x00;
/*nastavení předděličky na 1024 a spuštění čítače*/
TCCR0 = (1 << CS00) | (1 << CS02);
/*povolení přerušení programu*/
sei();
/*nekonečná smyčka, vše obstarává přerušení*/
while(1) {};
}
return 0;
20
Schéma zapojení:
Takto by mohl vypadat obslužný program a schéma pro blikání LED diodou. Ve
chvíli, kdy dojde k přetečení čítače, se v podmínce rozhodne, zda je na PB0 log. 0 nebo log. 1
a nastaví se hodnota opačná, z čehož plyne , že LED dioda bliká se střídou 1:1. Frekvence, při
které bude LED dioda blikat, se dá vypočítat podle vzorce f = (fclk / 1024)/255, kde f je
výsledná frekvence v Hz a fclk je hodinový takt mikrokontroléru v Hz.
Šestnáctibitový čítač
Blokový diagram:
21
Pro obsluhu tohoto čítače slouží registry:
TCNTx - obsahuje aktuální hodnotu čítače x
- šestnáctibitový registr rozdělen do dvou osmibitových - TCNTxL a
TCNTxH
TCCRx - šestnáctibitový ovládací registr, místo x je číslo čítače, rozdělen do
dvou osmibitových TCCRxA a TCCRxB
- obsahuje bity CSx0, CSx1, CSx2 pro nastavení zdroje signálu
- tabulka nastavení hodinového vstupu je stejná jako pro osmibitový
časovač
OCRxA/B - dva šestnáctibitové registry obsahující číslo, s kterým se
porovnává aktuální stav čítače, rozdělen do dvou
šestnáctibitových registrů OCRxA - OCRxAL, OCRxAH a
OCRxB - OCRxBL, OCRxBH (každý šestnáctibitový registr je
rozdělen do dvou osmibitových)
22
ICRx - šestnáctibitový registr uchovávající stav časovače při vzestupné hraně
signálu na měřícím pinu, rozdělen do dvou osmibitových registrů
ICRxL a ICRxH
TIFR - registr obsahující příznaky přetečení všech čítačů
TIMSK - registr s maskami přerušení všech čítačů
Ukázkový program obsluhy osmibitového čítače:
#include <avr\io.h>
#include <avr\interrupt.h>
#include <stdlib.h>
#include "lcd.h"
#define VSTUP PB0
/*frekvence jádra*/
#define FCPU 8000000
/*počet přetečení čítače*/
uint16_t preteceni;
/*čas vzestupné a sestupné hrany*/
uint16_t vzestupna, sestupna;
/*délka impulsu*/
uint32_t doba;
/*obsluha přerušení přetečení čítače*/
ISR(TIMER1_OVF_vect)
{
preteceni++;
}
/*obsluha přerušení zachycení signálu čítače*/
ISR(TIMER1_CAPT_vect)
{
/*zjišťování zda je na vstupu log. 0 nebo log. 1*/
if (VSTUP) //log. 1
{
/*uložení času vzestupné hrany*/
vzestupna = ICR1;
/*nastavení spoušťění čítače při sestupné hraně*/
TCCR1B = TCCR1B & 0xBF;
23
/*vynulování počítadla přetečení*/
preteceni = 0;
}
else
{
/*uložení času sestupné hrany*/
sestupna = ICR1;
/*vzestupná hrana spouští další čítání*/
TCCR1B = TCCR1B | 0x40;
/*délka trvání log. 1 ve strojových cyklech*/
doba = (uint32_t)sestupna - (uint32_t)vzestupna +
(uint32_t)preteceni;
/*přepočet délky trvání log. 1 na milisekundy*/
doba_ms = doba / (FCPU / 1000);
/*vymazání obsahu LCD displeje*/
lcd_clear();
/*převod čísla na řetězec a zobrazení na LCD displeji*/
lcd_puts(itoa(doba_ms));
}
}
int main(void)
{
/*inicializace LCD displeje*/
lcd_init();
/*povolení přerušení přetečení a přerušení zachycení signálu*/
TIMSK=0x24;
/*spuštění čítače na frekvenci CPU,
ochrany proti rušení
a spuštění zachycování signálu při vzestupné hraně*/
TCCR1B=0xC1;
/*povolení přerušení*/
sei();
/*nekonečná smyčka programu, vše obstarává přerušení*/
while(1) {};
return 0;
}
Schéma zapojení:
24
Program neustále kontroluje stav pinu PB0 a ve chvíli, kdy se na něm objeví log. 1 se
zapíše čas do proměnné vzestupna, jakmile se stav na vstupu změní na log. 0, zapíše se čas do
proměnné sestupna. V proměnné preteceni je uložen počet přetečení čítače, než se na vstupu
změnil stav. Délka impulsu se spočítá podle jednoduchého vzorce doba = sestupná vzestupná + počet přetečení. Tato doba je ovšem ve strojových cyklech, nikoli v
milisekundách, proto se musí ještě převést na milisekundy vydělením frekvencí procesoru v
kHz. Výsledná hodnota je uložena do proměnné doba_ms, která je posléze zobrazena na LCD
displeji.
PWM
PWM(Pulse-width modulation) je způsob, jakým se ovládá výkon připojeného
zařízení. Výkon se mění podle střídy signálu, který generuje AVR.
Jako ukázkový program a zapojení jsem si vybral příklad, ve kterém pomocí dvou
tlačítek budeme řídit rychlost otáčení elektromotoru. PWM je v podstatě součástí
osmibitových a šestnáctibitových čítačů/časovačů, ale jeho ovládání je natolik specifické, že
mu věnuji samostatný oddíl v mé práci.
Pro obsluhu PWM slouží registry:
TCNTx - obsahuje aktuální hodnotu čítače x
- šestnáctibitový registr rozdělen do dvou osmibitových TCNTxL a
TCNTxH
25
TCCRx - šestnáctibitový ovládací registr, místo x je číslo čítače, rozdělen do
dvou osmibitových TCCRxA a TCCRxB
- obsahuje bity CSx0, CSx1, CSx2 pro nastavení zdroje signálu
- tabulka nastavení hodinového vstupu je stejná jako pro osmibitový
časovač
OCRxA/B - dva šestnáctibitové registry obsahující číslo, s kterým se
porovnává aktuální stav čítače, rozdělen do dvou
šestnáctibitových registrů OCRxA - OCRxAL, OCRxAH a
OCRxB - OCRxBL, OCRxBH (každý šestnáctibitový registr je
rozdělen do dvou osmibitových)
Ukázkový program obsluhy kanálu PWM:
#include <avr\io.h>
int main(void)
{
/*nastavení portu D jako vstup*/
DDRD=0x00;
/*povolení interních pull-up rezistorů na portu D*/
PORTD=0xFF;
/*nastavení portu B jako výstup*/
DDRB=0xFF;
/*nastavení počáteční střídy na 50%*/
OCR1A=127;
/*nastavení osmibitového neinvertovaného PWM*/
TCCR1A=0x91;
/*start časovače bez předděličky*/
TCCR1B=0x01;
while(1)
{
/*zjištění stavu PD0*/
if(bit_is_clear(PIND, 0))
{
/*zvýšení výkonu*/
OCR1A+=10;
/*čekání na puštění tlačítka*/
loop_until_bit_is_set(PIND, 0);
}
/*zjištění stavu PD1*/
if(bit_is_clear(PIND, 1))
26
{
/*snížení výkonu*/
OCR1A-=10;
/*čekání na puštění tlačítka*/
loop_until_bit_is_set(PIND, 1);
}
}
return 0;
}
Schéma zapojení:
Následující příklad ukazuje řízení otáček motoru pomocí rychlého PWM. Obsluha se
provádí pomocí dvou tlačítek, kterými se zvyšují resp. snižují otáčky. Stiskem tlačítka se
mění hodnota registru OCR1A, se kterým se porovnává aktuální hodnota čítače, v případě, že
se hodnota registru rovná aktuálnímu stavu čítače, se na výstupu OC1A objeví log. 1, ta trvá
až do doby, kdy hodnota čítače dosáhne TOP, což je v tomto případě hodnota 255, jelikož
používáme osmibitové PWM. Ve chvíli, kdy se TCNT1(hodnota čítače) = TOP, se na výstupu
OC1A nastaví log. 0.
Mikrokontroléry AVR mohou produkovat tři druhy PWM - rychlé PWM, fázově
korektní PWM a fázově i frekvenčně korektní PWM. Bližší informace se nacházejí v příloze.
27
Závěr
Hlavním zájmem dnešní elektroniky již dávno nejsou relé a žárovky, ještě před dvaceti
lety tomu tak převážně bylo, jenže dnes, když se někdo zajímá o elektroniku a chtěl by se jí i
živit, musí se zároveň zajímat i o programování. A není to programování ledajaké, kupříkladu
programátor normálních stolních PC má práci zjednodušenou o to, že vždy píše program jen
pro jeden typ procesorů - x86(případně x86_64 nebo x86 EMT64, pokud má být program
optimalizovaný), postup programování je stále stejný, knihovny s funkcemi jsou také stejné.
Naproti tomu elektronik-programátor musí umět programovat poměrně velké množství
rozličných druhů architektur procesorů, přičemž každý rok se objeví několik nových. Stejně
jako v počítačovém světě i zde se každoročně zvyšuje výkon, ale ne tak bezhlavě. Výpočetní
výkon procesoru/mikrokontroléru není v řadě případů rozhodující, mnohdy záleží spíše na
odběru(bateriově napájená zařízení), na mezních pracovních podmínkách(průmysl), nebo na
vybavenosti periferií, kdy musí zařízení být schopno komunikovat s velkým počtem ostatních
zařízení pomocí různých sběrnic a protokolů. Samozřejmě při rozhodování hrají také velkou
roli ceny takových obvodu, které se dnes stále snižují, ale vzhledem k tomu, že se stále
objevují nové a nové obvody, které se snaží vyhovět nejnáročnějším požadavkům zákazníků,
platíme stále více a více, pokud chceme být o krok napřed oproti konkurenci. Například dnes
už je na světě nová dvaatřicetibitová architektura AVR, na které mohou běžet operační
systémy jako linux(existuje i verze pro 8-bit AVR) nebo Windows CE®. Možná se mýlím, ale
zdá se mi, že „druhá vlna“ počítačové revoluce je již v plném proudu. Za pár let bychom
mohli být překvapeni, kam se poděla architektura x86 na poli stolních počítačů.
28
Resumé
English
My work is intended mostly for people interested in modern electronics. I endevoured
to describe techniques as simple as possible, but also to offer all essential information. It was
not easy, though I believe I did it well. I hope this work will be also usefull for more skilled
person.
Czech
Má ročníková práce by měla být určena především lidem se zájmem o moderní
elektroniku. Snažil jsem se popisovat postupy co nejjednodušším způsobem, ale zároveň tak,
aby v popisu nechyběly žádné podstatné informace. Skloubit tyto dvě snahy nebylo vždy
lehké, ale já přesto věřím, že se mi to povedlo. Zároveň ale doufám, že si z mé práce odnese
nové informace i člověk, který se programování mikrokontrolérů již nějaký čas věnuje.
29
Seznam použité literatury
1. Vladimír Váňa - Mikrokontroléry Atmel AVR programování v jazyce C, r. v. 2003, BEN,
ISBN 80-7300-102-0
2. Jiří Pinker - Mikroprocesory a mikropočítače, r. v. 2004, BEN, ISBN 80-7300-110-1
3. Atmel Corporation - datasheets(katalogové listy), http://www.atmel.com/products/AVR/
4. AVR Libc team - AVR Libc user manual 1.4.4, http://www.nongnu.org/avr-libc/
30
Internetové zdroje
1.http://www.atmel.com/products/AVR/
- webové stránky výrobce mikrokontrolérů (poslední aktualizace 7.3.2007)
2. http://www.nongnu.org/avr-libc/
- webové stránky knihovny AVR Libc (poslední aktualizace 23.1.2007)
3. http://en.wikipedia.org/
- anglická verze internetové encyklopedie, vysvětlení většiny odborných termínů,
které jsou v práci použity
4. http://cs.wikipedia.org/
- česká verze internetové encyklopedie, vysvětlení části odborných termínů, které jsou
v práci použity
5. http://www.avrfreaks.net/
- webové stránky věnující se AVR, domovská stránka kompileru AVR GCC (poslední
aktualizace 12.3.2007)
6. http://avr.hw.cz/
- český server informující o AVR, informace jsou bohužel poměrně neaktuální
(poslední aktualizace 6.1.2001)
31
Přílohy
Přehled modelů AVR
-soubor model_table.xls na doprovodném CD
Tabulka režimů PWM
-soubor PWM_table.xls na doprovodném CD
Ukázky katalogových listů AVR
-soubory v adresáři katalogové listy na doprovodném CD
32

Podobné dokumenty

vysoké učení technické v brně laditelná smyčková

vysoké učení technické v brně laditelná smyčková Není však tolik výhodné dělat příliš dlouhé antény Yagi protože jejich zisk nestoupá s počtem prvků lineárně a při dalším zvětšování by bylo zvýšení zisku zanedbatelné. Jsou však i jiné antény zalo...

Více

Robotika základy - technika.junior.cz

Robotika základy - technika.junior.cz Stavba robotů zahrnuje tři propojené, ale relativně nezávislé okruhy: návrh a výrobu mechanické konstrukce, návrh a zapojení elektroniky a programování. Proto je dobré roboty stavět v týmech, kde s...

Více

trolstudio

trolstudio V souboru H\TROLDEF.H je makro enable(), disable () podmíněno definováním makra _INTERRUPT_CTRL_. Toto makro není implicitně definováno a pro aplikaci skrývá možnost globálně zakázat přerušení. Pok...

Více

1. Nebeská brána

1. Nebeská brána NG\åGRSLOYãLFKQLYEDUXQDUi]]WLFKOL DWLFKRSURt]MHKRUR]WHVHQHMKODV -DNYiPSLMGXGR6W-DPHV,QILUPDU\ XYLGtPKQHGåHQXVYRX OHåHWSLNU\WRXEtORXSODFKWRX DMDNE\VH]GiORQDSRKOHG...

Více

avr kniha - Svetelektro.com

avr kniha - Svetelektro.com • Piny sú chránené diódami voči VCC a GND Funkcie na generovanie náhodných čísel, triedenie, • Pin možno zaťažiť prúdom až 40mA a to v oboch hľadanie, celočíselná matematika, prevody reťazcov sta...

Více

31 SCS - České vysoké učení technické v Praze

31 SCS - České vysoké učení technické v Praze Pokud bylo stisknuto tlačítko Zvýšit jas, je zavolána procedura UP1. Ta zajišťuje obsluhu tlačítka Zvýšit jas. Po prodlevě 300ms je zavolána procedura INCR, která je zodpovědná přímo za obsluhu ink...

Více

Jednoduchý SSB transceiver pro pásmo 14MHz

Jednoduchý SSB transceiver pro pásmo 14MHz K nastavení potřebujeme alespoň sinus. generátor 14,2MHz a diodový detektor nebo osciloskop. Připojíme generátor do svorkovnice J1 a detektor či osciloskop na pin 1 cívky L3. Nastavíme co nejvyšší ...

Více