Programování AVR v jazyce C
Seznámení s jazykem C
Tento článek v žádném případě nemá ambice suplovat učebnici jazyka C. Jeho cílem je ukázat tento jazyk vývojářům, kteří zatím programují v jazyce symbolických instrukcí (asembleru). Vyšší jazyky obecně přinášejí rychlejší vývoj aplikace, na druhé straně ovšem neposkytují plnou kontrolu (konkrétní podoba programu - sekvence instrukcí - závisí na nastavení překladače, optimalizacích a podobně) - nelze předem určit přesný počet instrukcí a tím ladit časování programu.
Proměnné, typy
Norma jazyka C (na rozdíl třeba od Pascalu nebo Javy) nedefinuje šířku (počet bitů) jednotlivých typů. Proto se používají platformně nezávislé definice, které pak definují příslušné knihovny pro daný hardware.
int8_t a; // osmibitová znaménková proměnná
uint8_t b; // osmibitová neznaménková proměnná
Registry reprezentující vstupně-výstupní porty, kontrolní registry... se z hlediska jazyka C chovají rovněž jako proměnné, např. nastavení všech bitů portu A:
PORTA = 0xff; // 11111111
V případě GCC je možno využít jeho nestandardní rozšíření jazyka a psát PORTA = 0b11111111.
main() - vstupní bod programu
Vstupním bodem programu je funkce main(). Ta je vyvolána (zjednodušeně řečeno) po zapnutí jednočipu. Na rozdíl od programování pro "dospělý počítač" zde neřešíme předávání parametrů z příkazové řádky. Z (mě neznámého důvodu) se ale udává stejně zbytečná návratová hodnota.
int main(void) {
// tady se začíná vykonávat program
pracuj();
for(;;); // nekonečná smyčka
}
if - větvení programu
Výraz rozhodující o větvení (podmínka) je splněna, pokud má nenulovou hodnotu.
if (podminka) {
// tento kód je vykonán, pokud podmínka je splněna
splneno();
} else {
// tento kód je vykonán, pokud podmínka není splněna
nesplneno();
}
while - cyklus
Kód uzavřený v cyklu je vykonánán, dokud je podmínka splněna. Existuje ve svou variantách, s podmínkou na začátku a na konci. Nejzásadnější rozdíl je, že cyklus s podmínkou na konci je vykonán alespoň jednou.
while (podminka) {
// tento kód je vykonáván, dokud je podmínka splněna
pracuj();
}
do {
// tento kód je vykonáván, dokud je podmínka splněna
pracuj();
} while (podminka)
for - cyklus
Cyklus s inicializací před vstupem a akcí po každém průchodu se zapisuje zjednodušeně pomocí konstrukce for. Hlavním použitím je cyklus s daným počtem opakování. Zápis pomocí příkazu while
uint8_t i = 0;
while (i < 10) {
pracuj();
i++;
}
tak lze zapsat
for (uint8_t i = 0; i < 10; i++) {
pracuj();
}
Bitové operace
Jazyk C nedefinuje syntaxi pro práci s jednotlivými bity. Nastavovat jednotlivé bity však potřebujeme při práci s výstupy jednočipu. Proto musíme jako "berličku" použít bitové operace s celými proměnnými (případně registry).
Bitový posun
Pro bitové operace potřebujem tzv. masku - hodnotu, která má i-tý bit nastavený a ostatní shozené. Na to perfektně poslouří operace bitového posuvu.
uint8_t maska = 1 << 3; // posun o 3 bity doprava: 00000001 -> 00001000
Nastavení bitu
Pro nastavování bitu poslouží bitové nebo - operace, která nastaví bit, jemuž odpovídající bit v masce má hodnotu 1, ostatní bity se nezmění.
uint8_t data = 3; // 00000011
data |= maska; // nastavení třetího bitu: 00000011 | 00001000 -> 00001011
Bitová negace
Negace překlopí hodnotu všech bitů v proměnné
maska = ~maska; // překlopení všech bitů masky: 000001000 -> 11110111
Shození bitu
Pro shození bitu poslouží bitové a - operace, která shodí bit, jemuž odpovídající bit v masce má hodnotu 0, ostatní bity se nezmění.
data &= maska; // shození třetího bitu: 00001011 & 11110111 -> 00000011
Překlopení bitu
Pro shození bitu poslouží operace exkluzivní nebo - operace, která překlopí bit, jemuž odpovídající bit v masce má hodnotu 1, ostatní bity se nezmění.
maska = (1 << 1) | (1 << 2); // 00000110
data ^= maska; // překlopení prvního a druhého bitu: 00000011 ^ 00000110 -> 00000101
Obsluha přerušení
Přerušení je asynchronní událost (nastává nezávisle na části programu, která je právě vykonávána. Typickým příkladem je přijetí znaku ze sériové linky. Obsluha přerušení je funkce, která je vyvolána, pokud přerušení nastane. Deklaruje se pomocí makra ISR, ve starších kódech SIGNAL a INTERRUPT
ISR(SIG_UART_RECV) {
// obsluha vyvolaná po přijetí znaku ze sériové linky
pracuj();
}
maska = (1 << 1) | (1 << 2); // 00000110
data ^= maska; // překlopení prvního a druhého bitu: 00000011 ^ 00000110 -> 00000101
Obsluha komplexnějších periférií
Pro obsluhu komplexnějších periférií (sériová linka, alfanumerický displej...) je vhodné použít některou z již hotových knihoven, například AvrLib, na jejíž stránce jsou uvedeny i ukázkové části kódu.
