Modul LM3S800 a GPIO - blikáme LEDkami
Základní funkcí každého mikrokontroléru je interakce s okolím. Pokud bychom nemohli mikrokontrolérem ovlivnit nebo zjistit stav jeho okolí, bylo by nám veškeré počítačování k ničemu. V této části se budeme věnovat ovládání výstupů.
Probereme si elektrické vlastnosti výstupů a dimenzování jejich zátěže tak, abychom výstup nezničili. Použití výstupů si ukážeme na třech příkladech. V prvním příkladu budeme blikat dvěma LED diodami, ve druhém budeme ovládat sedmisegmentový LED displej a ve třetím se naučíme zapisovat na dvouřádkový znakový LCD displej. Příklady jsou jednoduché, ale přesto jsou probrané základy potřebné téměř v každé aplikaci.
V tomto příkladu použijeme piny mikrokontroléru jako výstupy, jejichž stav ovládáme přímo v programu. K pinům připojíme dvě LED diody a zkusíme několik režimů blikání. Hodnotu předřadného odporu LED diod nadimenzujeme tak, aby dioda svítila a přitom neohrozila funkčnost výstupního pinu a mikrokontroléru. Abychom mohli s diodami blikat, musíme měřit čas, po který dioda svítí nebo nesvítí. K tomu nám poslouží čekací smyčka ve zpožďovací funkci.
Elektrické zapojení
Elektrické vlastnoti výstupních obvodů mikrokontroléru LM3S800 jsou detailně popsány v datasheetu obvodu a základní vlastnosti je třeba před připojením periferií nastudovat.
Schéma připojení LED diod k pinům modulu LM3S800 je na obrázku. LED diody jsou součástí prototypovací desky BaseBoard, takže nepotřebujeme žádné další součástky. Pouze si vysvětlíme hodnotu předřadných odporů a na jaké napětí diody můžeme připojit. Dle dokumentace k mikrokontroléru LM3S800 může být součet proudů procházející všemi výstupními piny maximálně 100 mA. U každého pinu lze nastavit omezení proudu na 2 mA, 4 mA nebo 8 mA.
Spočítáme, jaký proud poteče LED diodou, pokud s ní do série bude zapojen odpor 470 Ω. V dokumentaci LM3S800 je uvedeno minimální výstupní napětí na pinu při logické jedničce Uoh = 2,4 V. Úbytek napětí na LED diodě v propustném směru je Ud = 1,85 V. Proud procházející LED diodou je shodný s proudem procházejícím předřadným odporem a spočteme ho dle následujícího vzorce:
Hodnota výstupního napětí je však uvedena jako minimální a měřením bylo zjištěno výstupní napětí Uoh = 2,9 V. Při tomto napětí by byl proud diodou 2,2 mA. Když nahlédneme do dokumentace svítivé diody, můžeme z VA charakteristiky vyčíst, že maximální proud diodou je 20 mA. Pokud bychom chtěli, aby LED dioda svítila jako divá a pouštěli do ní větší proud, nemohli bychom ji ovládat přímo pinem mikrokontroléru, ale museli bychom použít tranzistor jako zesilovač proudu. Po zapojení diody je však vidět, že svítí dostatečně i při tak malém proudu. Dle dokumentace můžeme z pinu odebírat proud až 8 mA, ale to bychom museli před diodu zapojit menší odpor dle vzorečku:
Pokud bychom chtěli zvýšit svit diod napětím 5 V, museli bychom připojit diody k výstupu v režimu open-drain. To probereme v dalším příkladu.
Obrázek ukazuje zapojení příkladu. Dejte si pozor na polaritu napájení, jinak žádná úskalí v tomto zapojení nejsou. Ve všech příkladech používáme takové piny mikrokontroléru, abychom měli volné JTAG rozhraní a mohli ladit programy pomocí debuggeru.
Kód programu
Než začneme psát program pro tento příklad, musíme si vytvořit funkční projekt. Postup vytvoření nového projektu je popsán v části, která se zabývá konfigurací Eclipse.
Konfigurace projektu od začátku je poměrně složitá, proto zvolíme jednodušší cestu a naklonujeme si projekt jednoduchého příkladu. Popis klonování projektu také najdete v části, která popisuje konfiguraci Eclipse.
Na obrázku je seznam všech souborů, které tvoří program této aplikace. Některé soubory jsou nové, ale většina je použita z jednoduchého příkladu.
Soubor vector.c obsahuje obsluhu výjimek, v souboru mcu_init.c je kód inicializace hodin procesoru.
Soubor helpers.c obsahuje zpožďovací smyčku, kterou budeme potřebovat pro řízení intervalů mezi blikání diod.
Základní funkcionalita je obsažena v souboru blink-2led.c, ke kterému patří hlavičkový soubor blink-2led.h. V souboru blink-2led.h jsou kromě deklarací funkcí nadefinovány symbolické názvy pro porty a piny mikrokontroléru, které jsme použili pro ovládání diod.
Pokud bude potřeba připojit LED diody na jiné piny mikrokontroléru, provedeme to jednoduchou změnou pouze v tomto hlavičkovém souboru.
/* pin pro LED1 */ #define LED1_PIN GPIO_PIN_0 /* pin pro LED2 */ #define LED2_PIN GPIO_PIN_1 /* LED jsou pripojeny k portu E */ #define LED_PORT GPIO_PORTE_BASE /* bit registru RCGC2 pro zapnuti hodin portu E */ #define LED_PORT_CLK RCGC2_GPIOE
Činnost programu je patrná z obsahu funkce main(). Obsah volaných funkcí si popíšeme podrobně v následujícím textu.
int main (void) { mcu_init(); blink_init(); while (1) { blink(20, 250, ALTER); blink(20, 250, SYNC); } return 0; }
- mcu_init() vychází z našeho jednoduchého příkladu. Obsahuje volání funkce clock_init(), která nastaví takt procesoru na 50MHz. Abychom tuto funci mohli použít beze změny ve všech dalších příkladech, odebrali jsme z ní funkci gpio_init(). Inicializaci portů provedeme ve funkci blink_init(), která je ve zdrojovém souboru blink-2led.c
- blink_init() nastaví piny portu jako výstupní a připojí zdroj hodin do příslušného portu
- blink() bliká LED diodami v různých režimech
Funkce blink_init() se postará o inicializaci pinů mikrokontroléru, kterými budeme ovládat LED diody. Z důvodu spotřeby není po resetu přiveden hodinový kmitočet do žádné periferie mikrokontroléru. První příkaz funkce zapne použitý port.
void blink_init() { /* privede zdroj hodin do portu */ MM_REG(SYSCTL_RCGC2) |= LED_PORT_CLK; short_delay(2); /* nastavi GPIO jako vystup */ MM_REG(LED_PORT + GPIO_O_DIR) |= (LED1_PIN | LED2_PIN); }
Než začneme pracovat s registry portu, musíme po připojení hodin do portu počkat dle dokumentace minimálně 3 systémové tiky, což zajistí funkce short_delay(). Nakonec zápisem jedničky do registru GPIO_O_DIR nastavíme vstupně/výstupní brány jako výstupy. Čisla bitů a port jsou definované v hlavičkovém souboru blink-2led.h.
Mikrokontroléry TI Stellaris umožňují přímou adresaci výstupních pinů. Adresování probíhá tak, že k bázové adrese portu přičteme hodnotu pozice jednoho nebo více bitů, které chceme adresovat, posunutou o dva bity doleva. Pro pohodlnější práci s adresováním pinů jsme si vytvořili makro SET_GPIO_PINS, které, dle hodnoty parametru value, nastavuje piny mikrokontroléru do nuly nebo do jedničky.
/* Makro SET_GPIO_PINS * Nastavi piny portu dle hodnoty value * base - bazova adresa portu * pins - piny, ktere mají být nastaveny na hodnotu value * value - kdyz value = 0 piny jsou nastaveny do log. 0 - když value > 0 piny jsou nastaveny do log. 1 */ #define SET_GPIO_PINS(base, pins, value) \ MM_REG((base + GPIO_O_DATA + (pins << 2))) = (value != 0 ? pins : 0)
Abychom diodami mohli blikat, musíme umět změřit dobu potřebnou pro jejich svit a zhasnutí. Použijeme funkci short_delay(), kterou už máme k dispozici.
Abychom mohli používat pro nastavení prodlev rozumné jednotky, vytvoříme si pomocná makra DELAY_MS a DELAY_US pro generování prodlevy v milisekundách a mikrosekundách.
/* frekvence 50 MHz strojového cyklu procesoru v Hz */ #define SYS_CLK 50000000L /* pocet strojovych cyklu v jedne iteraci short_delay */ #define SHDLY_CYCLES 4 /* pocet strojových cyklu v jedne mikrosekunde */ #define US_CYCLES (SYS_CLK / 1000000) /* pocet strojovych cyklu dle počtu mikrosekund */ #define CYCLES_US(us) ((US_TICKS * us) / CYCLE_TICKS) /* zpozdeni v mikrosekundach */ #define DELAY_US(us) short_delay(CYCLES_US(us)) /* zpozdeni v milisekundach */ #define DELAY_MS(ms) short_delay(CYCLES_US(ms * 1000))
Celý výpočet vychází z frekvence 50 MHz, na kterou jsou nastaveny hodiny mikrokontroléru. Při změně frekvence systémových hodin mikrokontroléru stačí změnit hodnotu symbolu SYS_CLK.
Symbol SHDLY_CYCLES definuje počet strojových cyklů, které jsou potřeba k provedení jedné iterace funkce short_delay(). Počty strojových cyklů jednotlivých instrukcí Thumb-2 jsou uvedeny v referenčním manuálu procesoru Cortex-M3. Zpoždění implementované pomocí čekací smyčky můžeme použít jen proto, že máme vypnutá všechna přerušení., Každé přerušení by nám do měření času vnášelo chybu. V dalších příkladech si ukážeme implementaci zpoždění pomocí časovače SysTick, nyní nám ale zpoždění pomocí čekací smyčky postačí.
Funkce blink() se stará o blikání LED diod. Nejdříve se vynuluje proměnná status, podle které se budou zapínat a vypínat diody. Následuje cyklus, který se provede právě tolikrát, jaká je hodnota parametru count. Na začátku každého cyklu proměnnou status negujeme, abychom změnili stav diod. Následuje přepínač, který se řídí obsahem parametru mode. V případě hodnoty SYNC se nastaví obě diody na stejnou hodnotu. V případě ALTER odpovídá stav jedné diody proměnné status a druhá dioda má stav opačný. Na konci cyklu je zpoždění, které se provede voláním funkce short_delay() přes makro DELAY_MS(). Parametr delay definuje délku zpoždění v milisekundách.
void blink (int count, int delay, int mode) { int status = 0; while (count-- > 0) { status = !status; switch(mode) { case SYNC: SET_GPIO_PIN(LED_PORT, LED1_PIN, status); SET_GPIO_PIN(LED_PORT, LED2_PIN, status); break; case ALTER: SET_GPIO_PIN(LED_PORT, LED1_PIN,status); SET_GPIO_PIN(LED_PORT, LED2_PIN, !status); break; } DELAY_MS(delay); } }