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

Zdroj: SOOM.cz [ISSN 1804-7270]
Autor: Prog0el
Datum: 30.12.2015
Hodnocení/Hlasovalo: 1.44/9

Tentokrát o proměnných a operací s nimi. Proměnné jsou místa v paměti, které mají danou délku a formát dat a podle těchto kritérií v jazyce C rozlišujeme několik typů proměnných podobně, jako číselné obory v matematice. Bez použití proměnných by bylo čistě na programátorovi, aby si pamatoval kde má jaká data uložena a hlavně aby program zabezpečil proti nechtěnému přepsání jiných buněk paměti.

Všimněte si, že v tabulce celočíselných proměnných je ke každému datovému typu doplněn jakýsi ekvivalent s předřazeným klíčovým slovem „unsigned“. Ono tajemné slovo před datovým typem znamená, že se jedná o proměnnou bez znaménkového bitu, jinými slovy pro data máme k dispozici celý rozsah proměnné, nicméně, můžeme zaznamenávat pouze čísla kladná a to v celém rozsahu dle pravidla n=2k, kde „n“ je počet hodnot, které je možno zaznamenat, „2“ je základ binární soustavy a „k“ je délka proměnné v bitech. Mějte na paměti, že počet hodnot, které může proměnná nabývat, je včetně „0“, tj. pro zjištění maximální zaznamenatelné hodnoty do proměnné, musíme uvažovat výraz nc=2k-1.

Proměnné bez klíčového slova před datovým typem, nebo, mají-li předřazeno slovo „signed“, které se nemusí povinně uvádět, se považují za proměnné se znaménkovým bitem, což je bit, který rozhoduje o tom, zda bude následující blok dat reprezentovat kladné či záporné číslo. Kdybychom zase uvažovali vzorec pro počet stavů, které můžou být zaznamenány, dostaneme následující vzorec: ns=2k-1

Platí zde fakt, že při změně jednoho z bitů proměnné na znaménkový, dojde ke zmenšení počtu hodnot části s absolutní hodnotou na polovinu, avšak počet hodnot výsledného čísla zůstane zachován- viz tabulka níže.

Způsoby ukládání racionálních čísel si detailně probereme později, protože je v následujících dílech nebudeme ještě dlouhou dobu potřebovat. Obecně se racionálním číslům snažíme při programování vyhýbat kvůli nepřesnostem, které vyplývají z principu ukládání dat v těchto proměnných. Datový typ float se skládá z jednoho znaménkového bitu, osmi bitů pro exponent a 23 bitů pro mantisu, z čehož vyplývá, že způsob uložení dat se od celočíselných proměnných liší. Číslo uložené v proměnné se vypočítá následovně: x=a*2r, kde „a“ je mantisa a „r“ je exponent.


celočíselné proměnné
klíčové slovorozsahdélka [B]
char-128 ÷ 1271
unsigned char0 ÷ 2551
int-32768 ÷ 327672
unsigned int0 ÷ 655352
long int-4294967296 ÷ 42949672954
unsigned long int0 ÷ 42949672954
racionální proměnné
float -3.4*(10^38) ÷ 3.4*(10^38)4
double-3.4*(10^38) ÷ 3.4*(10^38)4

*Odlišností jazyka C pro počítače a jazyka lehce modifikované verze označované jako „GCC“ je mimo jiné fakt, že není rozdíl mezi datovými typy float a double. V klasickém C je datový typ double dvojnásobně přesný a bývá obvykle o délce 8B.

Deklarace proměnných v jazyce C

Dosud jsme se seznámili pouze s datovými typy proměnných, které bychom mohli definovat jako množiny proměnných se stejným formátem dat. Konkrétních proměnná má svou adresu v paměti a v prostředí jazyka C také svůj název, se kterým se programátorovi pracuje mnohem lépe v porovnání s použitím pouhých adres v paměti. Na příkladu níže si uvedeme příklad deklarace proměnné typu unsigned char.

unsigned char pocet_sovich_hnizd;
// Proběhla deklarace proměnné „pocet_sovich_hnizd“

Důležitým faktem je, že jména proměnných nesmí být složena ze specifických symbolů a klíčových slov, kterými jsou například slova: if, while, for, char, ... Jazyk C je tzv. „Case Sensitive“, což znamená, že rozlišuje malá a velká písmena (na rozdíl od HTML) a na tuto skutečnost je také nutno přihlížet při volbě jmen proměnných. Použití diakritiky není doporučeno, jelikož není zaručeno, že ji kompilátor zpracuje korektně.

Aritmetické operace

Aritmetické operace v jazyce C nepředstavují žádný problém a zapisují se obdobně jako v matematice. Obecný způsob zápisu je proměnná, kam chceme ukládat výsledek následovaná znakem přiřazení „=“ za kterým následují dvě proměnné nebo čísla či jejich vzájemné kombinace oddělené znakem příslušné operace. V tabulce níže je uveden přehled aritmetických operací a jejich příslušných znaků:


aritmetické operace
operaceznakpříklad
Sčítání+a=a+b;
Odčítání-a=b-c;
Násobení*a=2*b;
Dělení/a=a/b;
Zbytek po celočíselném dělení („modulo“)%a=a%b;

Všimněte si, že do aritmetických operací s čísly, či proměnnými je možno zahrnout proměnnou, do které bude výsledek uložen, což se z hlediska matematiky může zdát jako nesmysl.

Mějte na paměti, že pokud při sčítání překročíte maximální zaznamenatelnou hodnotu konkrétní proměnné, dojde k přetečení a zbytek přičítaného čísla se přičte k „0“. Příkladem může být například následující operace s proměnou typu unsigned char:

a=250;
a=a+10;
// Výsledkem bude číslo „4“

U odčítání platí podobná pravidla, která si vysvětlíme rovnou na příkladu:

a=10;
a=a-20;
// Výsledkem bude číslo „246“

Pamatujte si, tedy, že čísla v proměnných je třeba uvažovat na číselné ose tvořenou uzavřenou kružnicí a nikoliv tedy přímkou.

Násobení nepředstavuje žádné obtíže, tj. pouze uvedu příklad:

unsigned char a=20;
a=a*5;
// Výsledkem je číslo „100“

Dělení je v jazyce C celočíselné, což znamená, že výsledkem je pouze číslo bez informace o zbytku po dělení. Zbytek po celočíselném dělení nám poskytuje funkce modulo. Užívá se stejně, jako kdybychom chtěli dělit dvě čísla avšak s tím rozdílem, že použijeme znak „%“ (viz tabulka výše).

a=5/3;
// Výsledkem bude číslo „1“ 

a=5%3;
// Výsledkem bude číslo „2“

Při přetečení u násobení, dělení a zbytku po celočíselném dělení se zaznamenají pouze bity, které se do dané proměnné zapsatelné a zbylé bity jsou ignorovány.

Zkrácené tvary

Aritmetické operace můžeme zapisovat zkráceně, abychom ušetřili čas a zvýšili přehlednost našeho zdrojového kódu.

a++;    //a=a+1; (inkrementace)
a--;    //a=a-1; (dekrementace)
a+=10;  //a=a+10;
a-=10;  //a=a-10;
a*=10;  //a=a*10;
a/=10;  //a=a/10;

Bitové operace a logické funkce

Doposud jsme si vysvětlovali operace, které sice známe z běžné matematiky, ale v mikroprocesorové technice bychom brzy narazili na problém, že nám v určitých případech, zejména při modifikaci hodnot části konfiguračních registrů, nedostačuje. Bitové operace spadají do tzv. „Booleovy algebry logiky“, která operuje pouze se stavy „1“-TRUE a „0“-FALSE a logické funkce zpracovávají čísla po jednotlivých bitech ve stejných vahách, protože původně byly logické funkce určeny pro vstupy i výstupy jednobitových dat. Níže je tabulka bitových operací jazyka C včetně kombinací, které můžou nastat za daných podmínek na vstupech (pravdivostní tabulka):


Logické funkce
názevzkratkaoperátorS0S1S2S3
Logický součinAND&0&0=00&1=01&0=01&1=1
Logický součetOR|0|0=00|1=11|0=11|1=1
NegaceNOT~~0=1~1=0
NonekvivalenceXOR^0^0=00^1=11^0=11^1=0
Posuny
Posun vlevo-<<----
Posun vpravo->>----

Jak již jsem zmínil, při použití bitové operace na více, než 1bitové číslo se zpracovává každý bit zvlášť, což ilustruje následující tabulka na funkci AND:


Číslo bitu76543210
proměnná 111110000
operátor&&&&&&&&
proměnná 200111100
přiřazení========
výsledek00110000

Pokud budete číst tabulku po sloupcích, všimnete si, že se jedná o stejné operace jako v tabulce logických funkcí výše. Je nutno poznamenat fakt, že pokud je jedno číslo kratší, než druhé, tak je využito od nejnižšího bitu a chybějící bity jsou doplněny nulami.

Abyste si byli schopni zapamatovat, co která funkce reprezentuje, nabízí se zde jejich definice:

unsigned char a;
unsigned char b=0x0F;
unsigned char a=0b11110000|b;
//a bude rovno 0xFF (0x0F=0b00001111)

Jistě jste si všimli, že v předchozí tabulce jsem zmínil posun vlevo/vpravo. Nejedná se o funkci spadající do booleovy algebry logiky, avšak její funkce je často využívána.

Většina kompilátorů využívá pro úsporu času bitový posun kdykoliv to je možné, avšak nikdy na tento fakt nespoléhejte a v případě potřeby raději použijte přímo bitový posun.

Syntaxe příkazu je číslo, které chceme posouvat následované operátorem posuvu vlevo/vpravo, za kterým se nalézá číslo, o kolik míst chceme posouvat; celý příkaz ukončíme středníkem.

unsigned char a=16;
a=a>>2; 
/* Posune číslo 16 (0b00010000) o dvě místa doprava, výsledkem bude 4 (0b00000100) */

Praktický příklad

Tento příklad již bude poněkud více efektní, než v prvním díle; jedná se o efekt tzv. „běžící had“, který bude využívat výše popsané operace. Jelikož jsem ještě nezmiňoval podmínky a cykly, budu se jim snažit vyhnout. Podmínky a cykly budou náplní již příštího dílu a následující kód by se jimi zjednodušil, což si budeme demonstrovat později.

Program nejprve inicializuje port B jako výstup a následně se spustí hlavní smyčka programu. V hlavní smyčce programu se zapíše do třech nejnižších bitů logická 1. Tato trojbitová skupina logických 1 reprezentuje „hada“. Pomocí bitových posunů vlevo a čekacích smyček zajistíme odsun hada vlevo až do jeho zmizení. Posléze se nastaví první, druhý a následně třetí nejvyšší bit do logické 1 (záměrně jsem zde pro demonstraci použil funkci OR) a k dalším posunům hada směrem doprava se využije bitový posun vpravo. Jakmile dojde k úplnému zmizení hada napravo, celý cyklus se opakuje po ukončení čekací smyčky.

Sestavíme si v nepájivém kontaktním poli následující obvod:

Podle návodu z prvního dílu připojte programátor a spusťte kompilaci stiskem tlačítka „F5“. Zkontrolujte si, zda máte nastaven zdroj taktovacího signálu na interní RC oscilátor o frekvenci 8MHz.

Zdrojový kód běžícího hada poskytuji ke stažení.

Reference:

https://gcc.gnu.org/wiki/avr-gcc
http://www.atmel.com/images/doc8453.pdf