Académique Documents
Professionnel Documents
Culture Documents
Niniejsza darmowa publikacja zawiera jedynie fragment penej wersji caej publikacji.
http://witmir.pl
Stycze 2011
ATNEL
WYDAWNICTWO
Mikrokontrolery AVr
jzyk
podstAwy progrAMowAniA
Mirosaw Karda
Ksika przeznaczona jest dla elektronikw i hobbystw, ktrzy chc szybko, w oparciu o interesujce przykady, pozna jzyk C przeznaczony dla mikrokontrolerw AVR i nauczy si pisa dla nich programy. Jest to jzyk wysokiego poziomu o nieograniczonych moliwociach, pozwala rwnie atwo i wygodnie dokonywa pocze z jzykiem maszynowym asembler. W sposb przystpny opisana zostaa take architektura oraz moliwoci samych mikrokontrolerw AVR wchodzcych w skad dwch rodzin: ATmega i ATtiny. Prezentowany materia podzielony jest na trzy czci. Pierwsza obejmuje zagadnienia zwizane z budow mikrokontrolerw, druga to wykad na temat podstaw samego jzyka, a trzecia zawiera szereg wicze wraz z kodami rdowymi, komentarzami i bogatymi opisami.
Wydawnictwo ATNEL ul. Jasna 15/38 70-777 Szczecin fax: 91 4635 683 http://www.atnel.pl e-mail: biuro@atnel.pl Wydanie I
Wszystkie znaki wystpujce w tekcie s zastrzeonymi znakami firmowymi bd towarowymi ich wacicieli. Autor oraz wydawnictwo Atnel dooyli wszelkich stara, by publikowane tu informacje byy kompletne i rzetelne. Nie bior jednak adnej odpowiedzialnoci ani za ich wykorzystanie, ani za zwizane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz wydawnictwo Atnel nie ponosz take adnej odpowiedzialnoci za ewentualne szkody wynike z wykorzystania informacji zawartych w ksice. Wszelkie prawa zastrzeone. Nieautoryzowane rozpowszechnianie caoci lub fragmentw niniejszej publikacji w jakiejkolwiek postaci jest zabronione. Wykonywanie kopii caoci lub fragmentw ksiki bd doczonej pyty DVD metod kserograficzn, fotograficzn, a take kopiowanie ksiki lub pyty DVD na nonikach filmowych, magnetycznych, elektronicznych lub na nieutoryzowanych stronach internetowych powoduje naruszenie praw autorskich niniejszej publikacji.
Przedmowa
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 1 Wstp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2 Zaczynamy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.1 Pierwszy pusty program w C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2 Od programu do procesora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.2.1 Kompilacja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 2.2.2 rodowisko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.3 Programator sprztowy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.4 Programowanie procesora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2.5 Uruchamiamy AVR Studio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.2.6 Platforma sprztowa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 3 Procesory AVR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.1 Informacje oglne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 3.2 Programowanie ISP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 3.3 Sposoby taktowania procesorw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 3.3.1 Wewntrzny oscylator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.3.2 Zewntrzny rezonator kwarcowy . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.3.3 Zewntrzny oscylator RC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 3.3.4 Zewntrzny generator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.4 Zagadnienia zwizane z zasilaniem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 3.5 Ukad resetu mikrokontrolera AVR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.6 Wewntrzne moduy procesorw AVR . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.6.1 Pami FLASH, RAM, EEPROM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 3.6.2 Przerwania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.6.3 Timery sprztowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 3.6.3.1 Podstawowe tryby pracy Timerw . . . . . . . . . . . . . . . . . . . 42 3.6.3.1.1 Tryb zwykego LICZNIKA . . . . . . . . . . . . . . . . . . . . 42 3.6.3.1.2 Tryb CTC jeden z najwaniejszych . . . . . . . . . . 44 3.6.3.1.3 Tryb PWM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 3.6.4 Przetwornik ADC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 3.6.5 Modu komparatora analogowego . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 3.6.6 Modu UART/USART, (czyli RS232) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 3.6.7 Modu SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.6.8 Modu TWI, (czyli I2C) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.6.9 Watchdog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.10 Tryby oszczdzania energii . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 3.6.11 FUSE BITS (ustawienia konfiguracji AVR) . . . . . . . . . . . . . . . . . . . . . . 54 3.6.12 LOCK BITS (zabezpieczenia AVR) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Spis treci
Strona | 3
Strona | 4
3.6.13 Bootloader niesamowite moliwoci . . . . . . . . . . . . . . . . . . . . . . . . 56 4 Podstawy jzyka C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.1 Zagadnienia oglne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.1.1 Komentarze . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 4.1.2 Definicja a deklaracja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.1.3 Wyraenia logiczne (warunki) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.2 Najwaniejsze instrukcje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.2.1 Instrukcja warunkowa If , else . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 4.2.2 Ptla while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 4.2.3 Ptla do..while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.2.4 Ptla for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 4.2.5 Instrukcja break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.2.6 Instrukcja switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 4.2.7 Instrukcja continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 4.2.8 Nawiasy klamrowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.2.9 Instrukcja goto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 4.3 Typy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 4.3.1 Systematyka typw jzyka C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.3.1.1 Typy zoone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 4.3.1.2 Zakres widocznoci zmiennych . . . . . . . . . . . . . . . . . . . . . . 76 4.3.1.3 Typ void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 4.3.1.4 Specyfikator const . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 4.3.1.5 Specyfikator volatile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 4.3.1.6 Specyfikator register . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 4.3.1.7 Instrukcja Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80 4.3.1.8 Typy wyliczeniowe enum . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 4.3.2 Stae w jzyku C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.3.2.1 Stae jako liczby cakowite . . . . . . . . . . . . . . . . . . . . . . . . . 85 4.3.2.2 Stae jako liczby zmiennoprzecinkowe . . . . . . . . . . . . . . . 86 4.3.2.3 Stae znakowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 4.3.2.4 Stae tekstowe, stringi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 4.4 Operatory . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 4.4.1 Arytmetyczne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 4.4.1.1 Modulo, czyli % . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 4.4.1.2 Inkrementacja i dekrementacja ++ -- . . . . . . . . . . . . . . . . 91 4.4.1.3 Operator przypisania = . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 4.4.2 Operatory Logiczne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4.4.2.1 Operatory relacji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93 4.4.2.2 Suma || oraz iloczyn && logiczny . . . . . . . . . . . . . . . . . . . . . 94 4.4.2.3 Negacja wykrzyknik ! . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 4.4.2.4 Operatory bitowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Strona | 5
4.4.3 Pozostae operatory przypisania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 4.4.4 Operator pobierania adresu & . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 4.4.5 Wyraenie warunkowe ? : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 4.4.6 Operator sizeof() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 4.4.7 Priorytety operatorw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 4.4.8 Operatory rzutowania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106 4.5 Funkcje *** . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 4.5.1 Wynik dziaania funkcji jak to dziaa? . . . . . . . . . . . . . . . . . . . . . . 110 4.5.2 Stos ujarzmianie potwora . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 4.5.3 Przekazywanie argumentw przez warto . . . . . . . . . . . . . . . . . . . 114 4.5.4 Funkcje typu inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 4.5.5 Zakresy widocznoci nazw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 4.5.5.1 Zakres globalny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 4.5.5.2 Zakres lokalny i zmienne automatyczne . . . . . . . . . . . . . 123 4.5.5.3 Zmienne i funkcje statyczne . . . . . . . . . . . . . . . . . . . . . . . . . 124 4.5.6 Funkcje w rnych plikach projektu . . . . . . . . . . . . . . . . . . . . . . . . . . 126 4.6 Preprocesor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.6.1 Dyrektywa #define . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 4.6.2 Makrodefinicje . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134 4.6.3 Dyrektywa #undef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 4.6.4 Operator ## - sklejanie nazw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.6.5 Operator zamiany na string # . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 4.6.6 Dyrektywy kompilacji warunkowej . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 4.6.7 Dyrektywy #ifdef oraz #ifndef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 4.6.8 Dyrektywy #error i pozostae . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 4.6.9 Dyrektywa #include . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 4.7 Tablice . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 4.7.1 Tablice wielowymiarowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 4.7.2 Tablica jako argument funkcji . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 4.7.3 Tablice znakowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 4.8 Wskaniki . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 4.9 Struktury, unie, pola bitowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 4.9.1 Struktury . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 4.9.2 Unie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 4.9.3 Poczenie struktury z uni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 4.9.4 Pola bitowe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 5 Warsztaty zajcia praktyczne . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 5.1 Przygotowanie procesora do pracy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 5.2 Migoczca dioda LED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 5.3 Obsuga klawiszy typu micro-switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 5.4 Multipleksowanie LED - przerwania . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
Strona | 6
5.5 5.6 5.7
7 8
Wywietlacz LCD (hd44780) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 Sterowanie PWM (kolorowa dioda RGB) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 223 Pomiar napicia za pomoc ADC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 5.7.1 Klawiatura analogowa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 246 5.7.2 Rnicowy pomiar napicia - amperomierz . . . . . . . . . . . . . . . . . . . . . 246 5.8 Komunikacja RS232 / RS485 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 5.8.1 Inicjalizacja, kalibracja . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 257 5.8.2 UART, przerwania, bufor cykliczny . . . . . . . . . . . . . . . . . . . . . . . . . . . . 266 5.9 Odczyt-zapis magistrali I2C (RTC, EEPROM) . . . . . . . . . . . . . . . . . . . . . . . . . . . 277 5.9.1 RTC sprztowa obsuga I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 278 5.9.2 Programowa implementacja I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 285 5.9.3 EEPROM I2C . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289 5.10 Modu SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 5.10.1 Sprztowa obsuga SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291 5.10.2 Programowa obsuga SPI . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 297 5.11 Magistrala 1Wire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 299 5.12 Odbir kodw RC5 w podczerwieni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307 5.13 Sterowanie silnikami DC . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316 5.14 Silnik krokowy unipolarny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320 5.15 Silnik krokowy bipolarny . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 5.16 Odczyt/zapis kart pamici SD (FAT) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330 5.16.1 FatFS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333 5.16.2 PetitFS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348 FuseBity MkAvrCalculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 6.16.1 Fusebity, Lockbity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 356 6.16.2 MkAvrCalculator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360 Bootloader . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 368 Projekty . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 8.1 Pilot na podczerwie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 371 8.2 Modu Bluetooth (BTM-112/222) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 8.3 ciemniacz pynna regulacja mocy 230V . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 8.4 Wstp do systemw czasu rzeczywistego . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395 8.5 Obsuga stosu AVR - TCP/IP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417 8.5.1 Karta sieciowa ethernet ENC28J60 . . . . . . . . . . . . . . . . . . . . . . . . . . 419 8.5.2 Serwer http . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422 8.5.3 Sterownik urzdze protok UDP . . . . . . . . . . . . . . . . . . . . . . . . . . 430 8.6 Programator USBASP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454 rodowisko ECLIPSE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 455
Przedmowa
Strona | 7
Stale rosnce zainteresowanie jzykiem C, dla mikrokontrolerw serii AVR firmy ATMEL, powoduje due zapotrzebowanie na wszelkiego rodzaju kursy, poradniki, e-booki czy te ksiki. Z tymi ostatnimi jest niestety bardzo sabo. a to dlatego, e po prostu nie istniaa dotd adna pozycja, ktra dotyczyaby wanie jzyka C oraz rodziny AVR. Postanowiem napisa t ksik, by pomc wszystkim, chccym pozna od podstaw tajniki tego uniwersalnego jzyka programowania. Ma ona za zadanie,w moliwie najprostszy sposb wprowadzi do wiata C take te osoby, ktre do tej pory nie miay adnego kontaktu z programowaniem i stoj na rozdrou, prbujc zdecydowa si, jakiego jzyka zacz si uczy, aby efektywnie i szybko programowa mikrokontrolery. Dlaczego C? W zamierzchych czasach, gdy powstaway pierwsze mikroprocesory, rozwj oprogramowania by cile zwizany ze specyficznym jzykiem maszynowym kadego mikroprocesora. Powodowao to konieczno pisania programw dla cile okrelonych urzdze. Jzyki najniszego poziomu, asemblery bazuj na mnemonikach, ktre zastpuj prawdziwy jzyk numeryczny zrozumiay dla procesora. Jednak, aby nie trzeba byo pisa programw w postaci cigu cyfr w systemie szesnastkowym typu: 0x3A, 0x1B, 0x41, 0x05, co miaoby spowodowa zaadowanie np. liczby 22 do okrelonej komrki pamici RAM, mona posugiwa si mnemonikami takich rozkazw. Dziki czemu powyszy cig cyfr zastpi mona w asemblerze poleceniem o wieleprzyjaniejszym dla oka, np.: MOV BUFOR, 22. Kompilator asemblera przetumaczy sam tak mnemonik na cig cyfr zrozumiay dla konkretnego mikrokontrolera, ktre zostay przedstawione wyej. Reasumujc, asembler jest najnisz form kodu maszynowego, dajcego si zrozumie przez czowieka. Pisanie programw w czystym asemblerze jest jak najbardziej moliwe i jeli kto ma wieloletnie dowiadczenie, pozwala to na osiganie znacznej wydajnoci programu napisanego w ten sposb. Jak wspomniaem, aby efektywnie i dobrze pisa programy w jzyku najniszego poziomu, trzeba powici wiele lat na nauk, a pomimo to nadal pisanie wikszych programw staje si bardzo uciliwe, dugotrwae oraz wymaga czasu na przetestowanie i sprawdzenie wszelkiego rodzaju bdw. Na dodatek program napisany w specyficznym kodzie maszynowym jednego procesora bdzie bardzo trudny do przeniesienia na inny typ. Czasem bdzie to w ogle niemoliwe i spowoduje konieczno napisania programu od pocztku. W zwizku z powyszym ogromny wkad pracy w napisanie programu zostaje niejednokrotnie zniweczony, gdy przychodzi zmiana zaoe i konieczno zastosowania w urzdzeniu innego typu mikroprocesora, a czasu na modyfikacj i sprawdzenie dziaania jest niewiele. W takim momencie bardzo pomocny okazuje si jzyk C. Jest to jzyk oglnego przeznaczenia, ktry moe pracowa na kadym mikrokontrolerze, dla ktrego stworzony jest kompilator C. W dzisiejszych czasach praktycznie nie ma procesorw, ktrych nie mona byoby programowa w C, za to zdarzaj si ju takie przypadki, gdzie producent wrcz nie dostarcza asemblera do swoich produktw, w zamian dajc tylko kompilator C. Dziki C mona: szybko i atwo porusza si midzy rnymi rodzinami mikrokontrolerw, o wiele szybciej, efektywniej i wydajniej pisa i testowa programy, a take tworzy kod, ktry jest o wiele atwiejszy do nauki, zrozumienia i zapamitania.
Strona | 8
1 Wstp
Odkd poznaem jzyk C, byem oczarowany jego moliwociami, prostot i logik programowania. Obecnie zauwaam specyficzne podejcie wielu osb, ktre po pierwszych prbach samodzielnej nauki C szybko si zniechcaj z powodu rzekomej duej trudnoci i zawioci zasad tego jzyka. Tymczasem prawdziwym powodem jest nierzadko brak literatury opisujcej zasady jzyka C w oparciu o praktyczne przykady, dziki ktrym mona z marszu rozwizywa du cz swoich pocztkowo przyziemnych problemw. Rzadko, kiedy ksika na temat jzyka C, a szczeglnie w aspekcie programowania mikrokontrolerw AVR, jest pisana dla osb, ktre nie maj jeszcze adnego dowiadczenia z programowaniem w ogle. Sporo dowiadcze do napisania tej ksiki zebraem podczas prowadzenia kursw jzyka AVR GCC dla procesorw AVR. Zatem jednym z celw, do ktrych d w tej ksice, jest prba przyblienia i zainteresowania tym niezwykle przyjemnym i atwym jzykiem osb, ktre wanie stoj na rozdrou i musz podj ciki wybr. W ktr stron pj, aby w efektywny i atwy sposb programowa ca rodzin mikrokontrolerw AVR Jzyk C czsto traktowany jest jako narzdzie dla specjalistw a nie amatorw, hobbystw itp. Postaram si, wic przeama te mity i udowodni, e kady po przeczytaniu tej ksiki bdzie potrafi napisa samodzielnie przynajmniej proste programy ze zrozumieniem podstawowych zasad tego jzyka. Poniewa jednak jzyka C ciko uczy si od strony praktycznej w oderwaniu od sprztu, czyli w naszym konkretnym przypadku od mikrokontrolerw serii, AVR, dlatego konieczne bdzie take przyblienie zasad dziaania procesorw tej rodziny. Wikszo przykadw bdzie odwoywaa si do mikrokontrolerw serii ATmega, ale postaram si pokaza, e dziki temu, i korzysta bdziemy z C to zaprogramowanie mikrokontrolerw z serii ATtiny nie bdzie si praktycznie niczym rnio. Jedyne rnice, jakie wystpi w tym przypadku, to pewne ograniczenia wynikajce z moliwoci sprztowych. Dziki powyszym zaoeniom ksika ta skierowana jest do bardzo szerokiego grona czytelnikw, ktrzy usilnie poszukuj wszelkich informacji na te tematy. Bd stara si uywa prostego, czasem potocznego jzyka, aby przybliy bardziej skomplikowane zagadnienia. Na pierwszy rzut oka struktura ksiki moe wyda si nieco chaotyczna, gdy nie opisuj w niej po kolei caych zagadnie w oderwaniu od siebie. Nie znajdzie si tu pierwszej czci, w ktrej bdzie w kilku kolejnych rozdziaach opisana rodzina mikrokontrolerw AVR. Nie znajdzie si kolejnej, gdzie bdzie opisany czysty jzyk C i nie znajdzie nastpnych rozdziaw osobno traktujcych o rodowiskach programistycznych, o programatorach czy o sposobach wgrywania programw fizycznie do mikrokontrolera. Przyjem zaoenie, i ksika bdzie napisana w postaci kursu, jaki zwykle serwuj uczestnikom na zajciach, gdzie wykady z teorii przeplatane s z praktyk, czyli tzw. warsztatami, na ktrych pod okiem instruktora kady moe uczy si, pisa czy testowa wasne programy. Pozwolio mi to na pynne przechodzenie z tematu na temat tak, aby w jak najprostszy sposb wprowadzi czytelnika do wiata mikrokontrolerw AVR oraz ich programowania. Moe wic nie w osobnych dziaach, ale w pewnej logicznej kolejnoci bd stara si podawa informacje tak, aby jak najszybciej mona byo je przyswaja. W sposb, ktry sprawdzi si w praktyce. Potraktuj t ksik jak dobrego przewodnika w trakcie przeprawy przez dungl, jak mog si wydawa zakresy szczegowej wiedzy z wielu dziedzin elektroniki cyfrowej i programowania.
Strona | 58
W jzyku C stosujemy tzw. wolny format jeli chodzi o pisanie kodu. Nie obowizuj tu reguy jak w innych jzykach, gdzie trzeba si ogranicza do pisania rozkazw w jednej linii. Nie ma tu adnych przymusw. Wszystko, co chcemy zapisa, moe si znale w kadym miejscu linii, a nawet mona to samo rozpisa na kilka linijek. Zwizane jest to z tym, e koniec instrukcji, jak wydajemy, jest okrelony przez rednik, ktry stawiamy na kocu, a nie przez to, e koczy si linia programu. Wewntrz instrukcji moe znajdowa si dowolna ilo tzw. biaych znakw, do ktrych zaliczamy spacje czy tabulatory. S one ignorowane przez kompilator. Z tego wzgldu nie ma rnicy w tym, jak zapiszemy ponisz lini - moemy to zrobi tak:
int main(void) {return 0;}
lub tak:
int main(void) {
return
// koniec programu
4.1.1 Komentarze
Biae znaki s ignorowane przez kompilator, su one jedynie programicie. Syszae zapewne przy okazji pisania kodw programu o tzw. wciciach. Dobrze napisany kod jest wtedy, gdy ma stosowane wcicia. Bez nich kod staje si mao czytelny i bardzo ciko wrci do jego analizy po duszym czasie. Zauwaye powyej w jednym z przykadw dwie charakterystyczne linie, w ktrych wida tzw. komentarze. To opisy, ktre mona wstawi do kodu w celu zwikszenia czytelnoci programowanych zagadnie. Jeli w dowolnym miejscu linii kompilator napotka dwa znaki // nastpujce po sobie, to ignoruje wszystkie kolejne a do koca tej linii. Inna forma do oznaczania caego bloku linii, w ktrych chcemy umieci opisy, moe by zawarta pomidzy dwoma znacznikami, gdzie jeden rozpoczyna blok /* natomiast drugi */ koczy taki blok. Zapamitaj, e komentarze w jzyku C s bardzo istotnym elementem. Program napisany bez adnych komentarzy czy krtkich chocia objanie, nie jest napisany w dobrym stylu programistycznym. Stosuj komentarze zawsze, gdy przygotowujesz skomplikowane procedury, funkcje czy obliczenia tak, aby stanowio to uatwienie dla ciebie, gdy po duszym czasie wrcisz do analizy kodu. Komentarze take s istotne dla innych osb, ktre bd miay moliwo zapoznania si z kodem rdowym twojego programu.
Strona | 59
Przykady Definicji: int b1; int c2 = 5; uint16_t tab[20]; int max(int a, int b) { return (a>b) ? a : b; } 1. 2. 3. 4. Tworzy zmienn b1, zajmuje dla niej pami (w jzyku AVR GCC) bd to dwa bajty, oraz informuje kompilator, e identyfikator b1 oznacza zmienn typu int. Tworzy zmienn c2, zajmuje dla niej pami, zostaje ona zainicjalizowana wartoci 5, oraz informuje kompilator, e c2 oznacza zmienn typu int. Tworzy tablic tab, zajmuje dla niej pami 40 bajtw oraz informuje kompilator, e identyfikator tab jest tablic dwubajtowych elementw bez znaku. Tworzy funkcj max, zajmuje dla niej pami lecz tym razem w obszarze pamici programu FLASH, umieszcza w niej program funkcji, oraz informuje kompilator, e funkcja max jest funkcj zwracajc warto typu int a take o tym, e przyjmuje ona dwa argumenty take o typie int.
Nazwy zmiennych i funkcji mona tworzy dowolnie, ale z pewnymi ograniczeniami: nie mog one by nazwami sw kluczowych uywanych przez kompilator oraz nie mog zaczyna si od cyfry. Nazwy mog by pisane zarwno wielkimi jak i maymi literami, jednak trzeba o tym pamita, poniewa jeli zdefiniujemy zmienn o nazwie Rozmiar (zaczyna si du liter), to pniej w kodzie kompilator nie rozpozna tej nazwy, jeli napiszesz j tak: rozmiar.
Strona | 60
Nie znajc wartoci zmiennych x lub a nie jestemy w stanie oceni czy te wyraenia s prawdziwe czy faszywe. Jeeli jednak powiem, tobie teraz, e x=7 natomiast a=10, to jeste w stanie szybko stwierdzi, e: 1. 2. 3. Wyraenie jest prawdziwe poniewa 7 jest mniejsze od 50 Wyraenie jest faszywe poniewa 7 nie rwna si 10 Wyraenie jest prawdziwe poniewa 7 jest rne od 10
Zaraz, zaraz ale skd bdzie o tym wiedzia mikrokontroler. Okazuje si, e to nie bdzie dla niego adnym problemem. Jeli mikrokontroler napotka np. taki warunek ( x < 50 ), to najpierw podobnie jak my dokona obliczenia i na tej podstawie sprawdzi, czy jest on prawdziwy, czy faszywy. Zmienna x przecie musiaa by gdzie wczeniej zdefiniowana w programie, std bdzie znana jej warto w momencie, gdy dojdzie do sprawdzania warunku. ZAPAMITAJ! Warto zero jest zawsze rozumiana, jako stan: fasz Warto inna ni zero jest zawsze rozumiana, jako stan: prawda Dziki temu w wyniku operacji a=(5<10) kompilator przydzieli zmiennej a warto jeden, natomiast w wyniku operacji a=(25<10) zmienna a przyjmie warto zero. Dziki powyszemu, zamiast w instrukcji sterujcej wpisywa warunek sprawdzajcy czy np. warto x jest wiksza od zera w tradycyjny sposb: (x>0), mona zapisa to samo w prostszy (x). Poniewa zgodnie z powyszymi definicjami prawdy i faszu w jzyku C, warunek (x) bdzie speniony (prawdziwy) tylko wtedy, gdy x bdzie wiksze od zera. Przy zaoeniu oczywicie, e korzystamy z typu liczby cakowitej bez znaku. W zwizku z tym warunek zapisany z kolei w ten sposb (1) bdzie zawsze prawdziwy (speniony).
Zaczniemy od kluczowych instrukcji, bez ktrych nie mona byoby napisa adnego programu.
W jzyku C instrukcja if (co oznacza po polsku jeli) moe wystpowa w dwch postaciach:
if(warunek) instrukcja if(warunek) instrukcja1 else instrukcja2
Strona | 61
warunek, ktry moe by dowolnym wyraeniem, tylko wtedy zostanie wykonana instrukcja wystpujca w dalszej czci. Druga posta stosowana jest do tzw. rozgazie programu. Oznacza, e jeli bdzie speniony warunek to zostanie wykonana instrukcja1, a jeli warunek nie bdzie speniony, to zostanie wykonana instrukcja2. Symbolicznie oznaczona instrukcja moe stanowi zarwno jedn instrukcj programu, co mona zapisa w kodzie tak:
if(x<50) wysokosc=0; else wysokosc=1;
ale moe take oznacza kilka instrukcji, tyle e wtedy musimy je zebra pomidzy nawiasami klamrowymi {}
if(x<50) { wysokosc=0; y=0; } else { wysokosc=100; y=20; z=33; }
W pierwszej prostszej postaci w zalenoci od warunku (x<50) bya przydzielana rna warto do zmiennej o nazwie wysokosc. W drugiej postaci w zalenoci od spenionego warunku lub nie, ustawilmy pewne wartoci kilku rnym zmiennym, dlatego zastosowalimy nawiasy klamrowe ograniczajce odpowiednio pierwsz i drug (po else) sekcj warunku. Naley wspomnie take, i instrukcje if mog by zagniedone. Spjrzmy na kod poniej. Wida na nim dwie instrukcje warunkowe zagniedone, a dokadniej mwic, zagniedona jest instrukcja if(warunek_2). Zostaa ona tutaj specjalnie wyrniona szarym kolorem ramki. Kolejnym wyrnikiem, jaki wystpuje w kodzie programu, s wcicia tabulatorw. Widzimy, e cay zagniedony warunek jest przesunity w prawo. Bez takich wci analiza kodu programu byaby prawie niemoliwa, a przynajmniej bardzo, ale to bardzo utrudniona.
if(warunek_1) { if(warunek_2) { //instrukcje } } else { }
// instrukcje
Wiemy jednak, e nawiasy klamrowe nie zawsze musz wystpowa, moe doj w takich sytuacjach do sporych problemw szczeglnie, jeli nie zastosujemy w odpowiedni sposb wci w programie.
Strona | 62
if(warunek_1) if(warunek_2) instrukcja1; else { instrukcja2; }
Jak przeanalizowa taki kod? Do ktrego warunku odnosi si instrukcja else? Dla kompilatora jest to jasne jak soce, poniewa wystpuje zasada, e jeli brak nawiasw klamrowych przed instrukcj else, to odnosi si ona zawsze do najbliszej poprzedzajcej j instrukcji if. Zobaczmy jednak, jak naley to zapisa tak, abymy take my mogli to spokojnie i bez bdw analizowa. Znowu wane s wcicia.
if(warunek_1) if(warunek_2) instrukcja1; else { instrukcja2; }
Myl, e teraz take dla ciebie na pocztku drogi w C bdzie to bardzo przejrzysty zapis. Nie martw si, jeli do tej pory miae problemy ze zrozumieniem rnego rodzajw kodw programw napisanych w C przez inne osoby. Nie znae jeszcze zasad, jakie rzdz skadni, a na dodatek moge natkn si na programy pisane bez wci przez niedowiadczone osoby lub takie, ktre ju co potrafi, ale uwaaj, e wcicia nie s im potrzebne. Jednak takie podejcie, uwierz mi, zawsze prdzej czy pniej skoczy si le. Bywaj pewne formy, gdzie musi nastpi wybr wielowariantowy za pomoc wielu instrukcji if else. W takich sytuacjach mona pomin tabulatory (wcicia), o ile bdzie to np. taki prosty blok:
if(warunek_1) instrukcja1; else if(warunek_2) instrukcja2; else if(warunek_3) instrukcja3; else if(warunek_4) instrukcja4; kolejne_instrukcje;
Taki blok analizujemy nastpujco: jeli speniony jest warunek_1, wykonaj instrukcj1, zakocz dziaanie bloku i przejd do kolejnych instrukcji programu. Jeli jednak warunek_1 nie jest speniony, to sprawd warunek_2, jeli jest speniony, to zakocz dziaanie bloku i przejd do kolejnych instrukcji programu. Jeli warunek_2 nie jest speniony, to sprawd warunek_3 i tak dalej. Tego typu bloki konstrukcji wielopoziomowego wyboru od razu mog skojarzy si z pomysem zastosowania tego mechanizmu do oprogramowania wielopoziomowego MENU dla uytkownika. Rzeczywicie, przy prostej budowie menu mona z tego korzysta. Jednak niedugo poznamy specjaln instrukcj, ktra jeszcze wygodniej pozwala nam organizowa wielopoziomowe wybory w kodzie programu.
Strona | 63
Dodam jeszcze, e instrukcje warunkowe if mog sprawdza warunki zoone, tzn. skadajce si z wielu warunkw bd oblicze. Przyjrzymy si temu bliej ,gdy bdziemy omawia operatory. Wtedy lepiej zrozumiesz zapis typu:
if ( (x>50 && x<100) || (x==5) ) instrukcja1;
Na razie powiem tylko, e instrukcja1 zostanie tylko wtedy wykonana, jeli zmienna x zawiera si w przedziale od 51 do 99 lub jest rwna 5. Znaki && oraz || to wanie operatory.
Oznacza to, e dopki warunek bdzie speniony (prawda), dopty bdzie wykonywana instrukcja. Zgodnie ze skadni jzyka, o ktrej pisalimy wyej, pojedyncz instrukcj mona zastpi dowolnym blokiem wielu instrukcji tyle, e trzeba je umieci wewntrz nawiasw klamrowych {}. Mona wic w ramach jednej ptli zapisa wiele instrukcji w ten sposb:
x=0; while(x<10) {
// // // //
// instrukcjaN
x=x+1;
Zawarto ptli bdzie wykonana dziesiciokrotnie. Zauwa, e przed rozpoczciem ptli przypisalimy zmiennej x warto zero. Zatem warunek (x<10) jest speniony i zostan wykonane kolejno instrukcje wewntrz nawiasw klamrowych. Ostatnia instrukcja spowoduje zwikszenie wartoci x o jeden, po czym znowu nastpi sprawdzenie warunku. Jako e x rwny bdzie 1, to i tym razem warunek zostanie speniony. Blok instrukcji bdzie dotd wykonywany, dopki zmienna x w wyniku zwikszania zawartoci o jeden nie osignie w kocu wartoci rwnej dziesi. W takiej sytuacji warunek (x<10) nie bdzie ju prawdziwy/ speniony i ptla nie wykona instrukcji zawartych w nawiasach klamrowych. Rozpocznie si wykonywanie kolejnych instrukcji programu. Bardzo czsto stosuje si w programach tzw. ptl nieskoczon. Chodzi o to, aby wykonywa pewien blok instrukcji bez koca. Mona wtedy posuy si konstrukcj:
while(1) { }
// instrukcje
Strona | 64
Zgodnie z tym co mwilimy o prawdzie i faszu w jzyku C, warto wiksza od zera bdzie zawsze oznacza prawd. Zatem warunek (1) bdzie zawsze speniony, poniewa liczba 1 jest wiksza od zera i symbolizuje w tym warunku prawd. Zauwa, prosz, istot dziaania tej ptli. Ot, zawsze przed jej pierwszym wykonaniem sprawdzany jest warunek. Gdyby nie by on speniony (prawdziwy), to nigdy nie doszoby do wykonania instrukcji w jej wntrzu.
Konstrukcja do while oznacza z angielskiego Rb Dopki i pozwala na realizacj innego rodzaju ptli programowej. Jej forma to: do instrukcja1 while(warunek);
Po analizie oznacza to, rb (wykonuj) instrukcj1, dopki bdzie speniony warunek. Jak zwykle te pojedyncz instrukcj moemy zastpi blokiem wielu instrukcji umieszczonych wewntrz nawiasw klamrowych.
do {
Instrukcja1; Instrukcja2; Instrukcja3; } while(warunek); Zauwa, e w odrnieniu od omawianej wyej zwykej ptli while, tutaj mamy do czynienia z sytuacj, w ktrej najpierw wykonywana jest instrukcja1 lub blok instrukcji, a dopiero na kocu sprawdzany warunek. Zatem w pierwszym przebiegu tej ptli zostan zawsze wykonane instrukcje w jej wntrzu.
Ten typ ptli programowej wykonywany jest zdecydowanie najczciej w rnych programach. Posiada ona posta: for( init ; wyraenie_warunkowe ; krok) tre_ptli;
init oznacza instrukcj bd grup instrukcji, ktre su do inicjalizacji pracy ptli. W praktyce najczciej bdziesz stosowa tu pojedyncz instrukcj. wyraenie_warunkowe tak jak to zwykle bywao w instrukcjach warunkowych, bdzie obliczane przed kadym wykonaniem pojedynczego obiegu ptli. Jeli wyraenie/warunek bdzie speniony, to przebieg ptli zostanie wykonany, jeli przestanie by prawdziwy, to przebieg nie zostanie wykonany. krok to instrukcja wpywajca na licznik wykonywania ptli. Jest ona realizowana za kadym razem na zakoczenie pojedynczego obiegu ptli tu przed ponownym sprawdzeniem wyraenia warunkowego na pocztku ptli. W praktyce bdzie to wygldao tak:
for(i=0;i<10;i=i+1) { instrukcja1; }
Strona | 65
W powyszym przykadzie sekcj init stanowi instrukcja i=0. Inicjalizujemy w ten sposb zmienn i, ktra bdzie odpowiedzialna za iteracj (wielokrotnie powtarzaln czynno). Sekcja wyraenie_warunkowe to w naszym przypadku warunek i<10. Zatem ptla bdzie si wykonywaa do momentu, dokd warunek bdzie prawdziwy. Jako e zmienna i zostaa zainicjalizowana wartoci zero, mona powiedzie, e pierwszy przebieg ptli zostanie na pewno wykonany, gdy warunek taki bdzie prawdziwy. Dziki sekcji krok, ktra u nas ma posta i=i+1 wiemy, e za kadym przebiegiem ptli, pod koniec wykonywania kadego jej obiegu zmienna i bdzie zwikszana o jeden. Co za tym idzie, mona miao wywnioskowa, e ptla taka wykona si 10 razy. Ile razy wykonana zostaaby ptla for zapisana w poniszy sposb?
for(i=0;i<10;i=i+2) instrukcja1;
Tylko pi razy, poniewa warto zmiennej i w sekcji krok, jest zmieniana w wikszym tempie. Tym razem i=i+2. Zatem wyraenie_warunek bdzie spenione tylko wtedy, gdy wartoci zmiennej i bd wynosiy kolejno: 0, 2, 4, 6, 8. Mam nadziej, e ten krtki opis da tobie duo do mylenia i jeli przypadkiem znasz ptle for z innych jzykw programowania, to miao stwierdzisz, e skadnia tej ptli w jzyku C jest zdecydowanie najlepsza. Daje ogrom moliwoci i nie wprowadza wielu ogranicze. Dodatkow ciekawostk jest to, e w jzyku C mona miao pomija niektre bd wszystkie czci skadowe ptli, pozostawiajc jedynie znaki rednikw, ktre je oddzielaj. Zatem poniszy zapis:
for(;;) { }
// instrukcje;
Czsto spotkasz, jako ptl nieskoczon. Opuszczenie sekcji wyraenie_warunek jest zawsze rwnoznaczne w tym przypadku z tym, jakby warunek by zawsze speniony. Mona take skorzysta z zapisu:
for(i=5;x>20;) instrukcja;
W takim przypadku mamy do czynienia z inicjalizacj zmiennej i w ptli, nastpnie zostaje sprawdzany warunek (x>20), ktry wcale nie musi by zwizany ze zmienn typu iteracyjnego, czyli i. Natomiast pominlimy w ogle sekcj krok. Oznacza to, e ptla bdzie pracowa w zalenoci od tego, co wewntrz niej bdzie si dziao z wartoci zmiennej x. Jak wspominaem wczeniej, sekcja inicjalizacji bd sekcja krok mog skada si z kilku instrukcji oddzielonych od siebie przecinkiem. Nie naduywaj jednak takich konstrukcji ze wzgldu na moliwo znacznego zmniejszenia czytelnoci kodu programu. Przykad:
for(i=0,k=10;i<10;i=i_1,k=k-1) { // instrukcje ptli }
Strona | 66
W tym przypadku dostrzeesz, i zmienna i suy do iteracji, natomiast niejako dodatkowo mona wykorzysta sekcje ptli do cyklicznych dziaa z innymi zmiennymi, ktre mog by przydatne wewntrz ptli. Kada ptla moe take zosta przerwana w dowolnym momencie za pomoc specjalnej instrukcji, o ktrej powiem za chwil.
Poznalimy ju wczeniej tak konstrukcj ptli nieskoczonej z uyciem ptli while, jednak rwnie dobrze moglibymy zastosowa konstrukcj for(;;) zamiast while(1). Tak czy inaczej wewntrz za kadym obiegiem sprawdzany jest jaki warunek, i jeli zostanie on speniony, wykonywanie obiegu ptli zostanie natychmiast przerwane. Nie wykona si w jej wntrzu ju adna nastpna instrukcja.
Strona | 67
Wyglda to moe troszk skomplikowanie na pierwszy rzut oka, ale to tylko zudzenie, zapewniam ci. Ju wyjaniam, co to wszystko po kolei oznacza. To potne narzdzie w jzyku C. Instrukcja rozpoczyna si od sprawdzenia naszego przecznika, ktrym jest wyraenie. Oznacza to, e w nawiasach okrgych moe wystpi sama zmienna np. x, ale rwnie dobrze moe wystpi wyraenie matematyczne, ktrego wynik bdzie przecznikiem. Nastpnie wewntrz nawiasw klamrowych mamy sekcje o nazwie case lub default, dziki ktrym moemy zdecydowa, jakie instrukcje chcemy wykona w zalenoci od konkretnych wartoci naszego wyraenia/przecznika. Po swku case zawsze podajemy warto przecznika, jaka nas interesuje, co oznacza, e jeli przecznik bdzie mia w momencie wejcia w instrukcj switch tak warto, to instrukcje wystpujce w kolejnych liniach po swku case zostan wykonane, jeli inn warto, to pominite i zostanie rozpatrzona kolejna pozycja case. Po kadym pakiecie instrukcji nastpujcych po sprawdzeniu okrelonej wartoci przecznika case, moe wystpi instrukcja break. Tylko dlatego ujem j w powyszym schematycznym w przykadzie w nawiasy kwadratowe, aby zakomunikowa, e instrukcja break moe w tym miejscu wystpowa, ale nie musi. Nie jest to obligatoryjne. Jednak, jeli jej nie ma, to zostan wykonane kolejne instrukcje zawarte instrukcji nastpnych sekcji case, switch. Moe to spowodowa, e cao nie zareaguje tylko na jeden przecznik, a na kilka. Zatem jeli zaley nam na wykonaniu instrukcji dotyczcych tylko jednego przecznika, to najczciej bdziemy blok rozpoczynajcy si od swka case koczyli rozkazem break, ktry przerwie dalsze wykonywanie instrukcji zawartych w switch, poniewa uznajemy, i inne s niepotrzebne w tym momencie. W praktyce moe to wyglda tak:
x=2; switch(x) { case 0: czas=10; break; case 1: czas=23; break; case 2: czas=38; break; case 3: czas=42; break; default: czas=0;
Krciutko przeanalizujemy, co stanie si w wyniku dziaania powyszego kodu programu. Na pocztku bd rcznie, bd w wyniku wykonania jakiej funkcji, zmienna x przyjmuje warto rwn dwa. Rozpoczyna si teraz instrukcja switch sprawdzajca warto zmiennej x, penicej dla nas rol przecznika, od ktrego chcemy spowodowa, aby z kolei zmienna czas przyja pewn konkretn warto. Zakadamy take, e jeli zmienna x nie osignie
Strona | 68
adnej z zaoonych wartoci, to zmienna czas domylnie zostanie wyzerowana. Po wejciu w instrukcj switch za pomoc pierwszego swka case, sprawdzamy, czy nasz przecznik, jakim jest warto zmiennej x, nie posiada wartoci zero. Jeli nie, to zignorowane zostan kolejne linijki programu a do napotkania kolejnego momentu, w ktrym pojawi si swko case. Oznacza to bdzie, e po raz kolejny sprawdzamy, czy nasz przecznik nie posiada wartoci rwnej jeden. Jeli nie, to program przeskakuje do kolejnego swka case, ktre tym razem sprawdza, czy x rwna si dwa? Zgadza si, jak wida przed instrukcj switch, zmienna x jest rwna dwa. W takim razie, rozpoczn si wykonywa kolejne instrukcje, ktre znajduj si po tym wanie sprawdzeniu swkiem case. W naszym przypadku jest to tylko jedna instrukcja, ale mona rwnie dobrze w kolejnych liniach napisa ich wicej. Tutaj mona, ale nie trzeba, koniecznie stosowa do bloku instrukcji, nawiasw klamrowych. Zauwa jednak, e na zakoczenie tych instrukcji zostaje wykonana instrukcja break. Powoduje ona zakoczenie dziaania caoci. O to nam chodzio. Aby w zalenoci od konkretnej wartoci zmiennej x odpowiednio ustawi zmienn czas.
Dodajmy na koniec, e gdyby warto zmiennej x bya rna od 0, 1, 2, 3 (bo takie wartoci zostaj sprawdzane za pomoc swek case), to zrealizowana zostaaby sekcja instrukcji na kocu po swku default. W naszym przypadku zmienna czas zostaaby wyzerowana. Jeli taka sekcja case lub default wystpuje na samym kocu, to zbdne jest ju uycie instrukcji break.
Oczywicie rodzaj ptli moe by dowolny, rwnie dobrze w tym przykadzie moglibymy zastosowa while() czy te dowhile(). Jak to dziaa? Ot zakadajc, e jeli warunek nie jest speniony, to dokadnie w kadym obiegu ptli wykonywane s wszystkie instrukcje od 1 do 5. Jeli jednak w konkretnym czy te w wielu przebiegach warunek zacznie by speniony/prawdziwy, to instrukcje od 4 do 5 s cakowicie pomijane. Mona powiedzie, e instrukcja continue powoduje przejcie na sam koniec ptli. Efekt bdzie taki, jakby cao zostaa wykonana, nastpuje zakoczenie obiegu, po czym zostaje sterowanie przekazane znowu na pocztek ptli, gdzie sprawdzane s jej warunki pracy.
Strona | 69
I sposb
instrukcje;
II sposb
while(1) { Instrukcje; }
III sposb
Kady sposb jest generalnie prawidowy, gdy zawiera odpowiednie wcicia. Jednak warto zdecydowa si na jeden z nich taki, ktry tobie bdzie sprawia najmniej problemw. Dla mnie najlepszym sposobem, jakiego najczciej korzystam, gdy pisz wasne kody, jest ten trzeci. Powiem wicej, eby unikn pomyek zwizanych z pisaniem dugiego kodu programu i zagniedonych instrukcji, po ktrych stosuj klamry, zawsze podchodz do tego wanie tak. Po napisaniu instrukcji warunkowej czy ptli wciskam klawisz ENTER, po czym rwno pod rozpoczynajc si instrukcj stawiam otwarty nawias klamrowy, ponownie klikam klawisz ENTER (nawet dwukrotnie) i wstawiam zamknity nawias klamrowy rwniutko pod tym otwartym powyej. Dopiero wtedy przenosz kursor pomidzy oba nawiasy i zaczynam wpisywa kod programu pomidzy nimi. Dziki temu rzadko myl si, jeli chodzi o stosowanie tych nawiasw. Dodam, e niektre zaawansowane rodowiska jak np. ECLIPSE, opisane przeze mnie wyej czynnoci wykonuj za mnie automatycznie! Oznacza to, e gdy po napisaniu instrukcji warunkowej lub ptli wcisn raz klawisz ENTER, to automatycznie pod spodem umieszczone zostaj od razu dwa nawiasy klamrowe a kursor umiejscawia si wraz z poprzedzajcym go tabulatorem/wciciem w linii pomidzy nimi, dziki czemu bez uciliwych wyej opisanych czynnoci przystpuj do pisania kodu. Inne rodowiska i edytory oferuj jeszcze inne narzdzia/gadety wspomagajc prac programisty w tym zakresie. Dlatego pisanie programu w zwykym lub lekko zaawansowanym programie typu notatnik, ktry oferuje tylko kolorowanie skadni, bywa w dzisiejszych czasach bardzo uciliwe.
Strona | 70
goto etykieta .. .. .. etykieta: instrukcje;
4.3 Typy
Etykieta to dowolna aczkolwiek niezarezerwowana nazwa, po ktrej musi wystpi znak dwukropka. Dziaanie jest banalnie proste, sprowadza si do tego, e jeli program napotka t instrukcj, to wykonuje skok do miejsca, ktre wskazywane jest przez etykiet. Wane, e etykieta musi znajdowa si w odpowiednim zakresie widocznoci. O zakresach widocznoci, wicej powiem w nastpnych rozdziaach. Wspominaem jednak, e bywaj sytuacje, gdzie moemy prawie bez adnego wstydu z niej skorzysta. Co wcale nie oznacza, e bez niej sytuacja jest bez wyjcia. Wszystko zaley od inwencji twrczej programisty jak zwykle. Zatem wyobra sobie, na razie czysto teoretycznie, e masz wielokrotnie zagniedone ptle wraz z zagniedonymi warunkami if() lub funkcjami switch() wewntrz nich. Przychodzi jednak taki moment, e bezwarunkowo musisz opuci te wszystkie ptle bez koniecznoci wielokrotnego uywania rozkazu break, ktry ju znasz. Wtedy mona sign po instrukcj goto, za pomoc ktrej jednym prostym sposobem, przenosisz sterowanie programu cakowicie na koniec takiego dugiego zagniedonego bloku instrukcji. Jednak zawsze tylko w ramach widocznoci. Napomkn tylko, e np. nie mona wykona skoku goto pomidzy rnymi funkcjami. To wanie stanowi ograniczenie zakresu jej widocznoci.
Przechodzimy do omwienia jednego z najbardziej istotnych zagadnie jzyka C, ktrego zrozumienie posiada fundamentalne znaczenie dla dalszej nauki. Traktujc to zbyt pobienie wyrzdzibym ci krzywd. Postaraj si uwanie przeczyta i dobrze zrozumie oraz zapamita podane tutaj informacje. Bez nich ani rusz w dalszej naszej drodze. Kada nazwa, jaka wystpuje w jzyku C (poza nazwami etykiet np. przy instrukcjach goto), zanim zostanie uyta w programie, musi koniecznie zosta zdeklarowana. Tak naprawd wspominalimy ju o deklaracjach i rnicach pomidzy definicjami w rozdziale Deklaracja a Definicja. Przyjrzyjmy si temu nieco bliej. Zamy, e kompilator napotka na swojej drodze zapis typu:
a = b + c;
Wystpuje tu operacja dodawania oraz podstawienie wyniku tej operacji do zmiennej a. Kompilator musi, zatem uruchomi wewntrzne procedury, ktre bd mogy dokona stosownych oblicze. Jednak dla rnych dziaa matematycznych i nie tylko matematycznych, mog wystpowa rne procedury. Poza tym nawet, jeli w tym przypadku bdzie to dziaanie matematyczne (dodawanie), to kompilator musi wiedzie, jakiego typu s te zmienne. Inaczej bdzie bowiem wykonywa dodawanie liczb cakowitych bez znaku, inaczej dodawanie liczb cakowitych ze znakiem, inaczej dodawanie liczb zmiennoprzecinkowych lub mieszanych, jeszcze inaczej liczb o rnych moliwych zakresach wielkoci. Jeli liczby a oraz b bd si mieciy np. w zakresie od 0 do 255, to bdzie oznacza, e ich wartoci mona zapisa tylko w jednym bajcie. Jednak ju wynik takiej operacji, jak si domylasz, moe by wikszy ni 255, wic bdzie musia zosta zapisany w zmiennej skadajcej si z dwch bajtw. Jednak skd nasz biedny kompilator moe wiedzie na podstawie tylko zapisu w formie pokazanej powyej, jakich operacji ma uy, skoro nie powiedzielimy mu wprost, na jakich
Strona | 71
typach danych/zmiennych ma operowa i zacza ju konkretne procedury matematyczne? Musimy wczeniej zadeklarowa, a jeli obliczenia maj by wykonane podczas dziaania programu w oparciu o pami RAM mikrokontrolera, to musimy zmienne zdefiniowa. Pamitajc, e definicja zmiennej jest rwnowana z jej deklaracj. Dobrze spjrzmy, w jakiej postaci mona poda te wszystkie informacje kompilatorowi.
int a; uint8_t b=188, c=220; a = b+c;
Prosz bardzo, po dokonaniu takiego zapisu, kompilator nie pinie ju swka o bdach podczas przeprowadzania kompilacji tak napisanego kodu programu. Wyjanijmy sobie, jakich operacji dokonujemy w kolejnych liniach. Umiejtno takiej analizy to podstawa. Aby pisa program, ktry bdzie zrozumiay dla kompilatora, musisz si nauczy myle jak kompilator, w pewnym zakresie przynajmniej. A zatem:
1. Nastpuje deklaracja zmiennej a, ktra mwi, e zmienna a bdzie oznaczaa liczb cakowit ze znakiem w rozmiarze wynoszcym dwa bajty. Jednak, poniewa jest to przede wszystkim konkretna definicja, to zostaje zarezerwowane miejsce w pamici RAM mikrokontrolera o wielkoci dwch bajtw. To w tym miejscu kompilator bdzie przetrzymywa podczas ycia caego programu zawarto zmiennej a. Definicja ta nie powoduje jednak ustawienia wstpnej wartoci tej zmiennej. Zatem moe ona by przypadkowa albo moe by automatycznie inicjalizowana wartoci zero. (Niedugo dowiesz si, kiedy przypadkowa, a kiedy automatyczna ). Nastpuje na podobnej zasadzie jak wyej definicja oraz deklaracja zmiennych o nazwie b oraz c. Jednoczenie zostaje dla nich zarezerwowana pami. Po jednym bajcie na kad, co zwizane jest z typem uint8_t specyficznym dla kompilatora AVR GCC. Jednoczenie obydwie zmienne zostaj zainicjalizowane wartociami 188 oraz 200. Ta linijka programu to ju nie deklaracja ani nie definicja. To jest ju konkretna instrukcja programu. W wyniku jej dziaania kompilator podczy procedury, ktre wykonaj najpierw dodawanie liczb cakowitych bez znaku o rozmiarze jednego bajta, a nastpnie procedur, ktra wynik tego dodawania umieci w zmiennej a. To nic, e zmienna a posiada inny typ. Wane, e typ int potrafi pomieci liczb wiksz od 255. Jak widzisz, to programista, czyli ty musi dba o to, jakimi typami danych/zmiennych si posuguje!. Pamitaj o tym na zawsze.
2.
3.
Reasumujc, jeszcze raz przypomn bardzo istotn rnic pomidzy deklaracj a definicj za pomoc nieco innych sw. Musi to do ciebie dotrze w peni.
Strona | 72
char Typy do przechowywania kodw znakw alfanumerycznych
W istocie typ char nie suy tylko do przechowywania kodw znakw alfanumerycznych, moe on przechowywa liczby cakowite podobnie jak unsigned short int. Jednak na pocztku postaraj si kojarzy go z kodami znakw alfanumerycznych. (To moja propozycja nie tylko na potrzeby tej publikacji, ale take dla uatwienia wejcia w wiat C). Wszystkie powysze typy mog wystpowa w dwch wariantach, liczby ze znakiem i bez znaku. Co oznacza, e do poszczeglnych typw mona dodawa znaczniki: signed oraz unsigned, np.:
signed int liczba cakowita ze znakiem unsigned int liczba cakowita bez znaku podobnie z typem alfanumerycznym: signed char liczba cakowita reprezentujca ze znakiem unsigned char liczba cakowita reprezentujca znak alfanumeryczny bez znaku
W przypadku typu char wyszo nam w opisie troszk takie maso malane, ale ju wyjaniam, o co chodzi. Wszdzie, gdzie mwimy o znaku czyli signed oraz unsigned mamy na myli typy, ktre mog przechowywa tylko liczby dodatnie i ujemne te oznaczone signed, natomiast te oznaczone unsigned mog przechowywa tylko liczby dodatnie.
Typy do przechowywania i pracy z liczbami zmiennoprzecinkowymi
float double
Pozwalaj one pracowa na liczbach rzeczywistych o rnej precyzji. Z tym, e ze wzgldu na ograniczenia moliwoci maych mikrokontrolerw, do jakich zalicza si rodzina AVR, pozosta wprawdzie typ double, aby bya zgodno ze standardem, jednake jego precyzja jest dokadnie taka sama jak typu float.
Typy do przechowywania i pracy z wartociami logicznymi
bool
Zmienne tego typu mog przechowywa tylko dwie wartoci, prawd lub fasz. W praktyce mona takim zmiennym przypisywa wartoci oznaczone, jako false lub true. Co z kolei i tak na kocu sprowadza si do tego, e zmienna tego typu i tak bdzie tak posiadaa warto zero albo jeden. W zwizku z czym niezbyt czsto uywa si tych typw. Tym bardziej, e wie si to z koniecznoci zaczania do programu oddzielnej biblioteki zwanej stdbool.h.
Strona | 73
Nazwa Typ char cakowity unsigned char cakowity short int cakowity unsigned short int cakowity long int cakowity unsigned long int cakowity long long int cakowity long long unsigned int cakowity int cakowity unsigned int cakowity float rzeczywisty double rzeczywisty bool logiczny Dla oznaczenia braku danych void pusty Zakres -128127 0255 -3276832767 065535 -2^312^31-1 02^32-1 -2^632^63-1 02^64-1 = short int = unsigned short int 6 znakw precyzji 10 znakw precyzji 1 logiczny 0 Bajty 1 1 2 2 4 4 8 8 2 2 4 8 2
1 Jzyk o ktrym mwimy, AVR GCC nie obsuguje formatu liczb zmiennoprzecinkowych podwjnej precyzji. Jednak ze wzgldu na zgodno ze standardem mona deklarowa zmienne typu double tyle, e kompilator potraktuje je jakby byy to zmienne typu float.
Zakres 0 to 255 -128 to 127 0 to 65535 -32768 do 32767 0 do 4294967295 -2147483648 do 2147483647 0 do 1.8*1019 -9.2*1018 do 9.2*1018
W standardowym jzyku C wystpuj jeszcze inne typy, jednak na razie nie bdziemy o nich wspomina, gdy nie wszystkie dotycz naszych mikrokontrolerw. Przedstawi raczej zestawienie typw wbudowanych, z jakimi bdziemy mieli do czynienia korzystajc z naszej wersji kompilatora AVR GCC. Bardzo istotn i pozytywn cech jzyka C jest to, e mamy moliwo definiowana zmiennych w locie. C to oznacza? Najpierw odwoam si do innych jzykw, by moe miae moliwo poznania wczeniej niektrych. Okazuje si, bowiem, e najczciej w innych
Strona | 74
jzykach, wystpuje konieczno definiowania zmiennych na pocztku bloku kodu programu czy bloku funkcji itp. Na szczcie w jzyku C nie ma takich ogranicze, co oznacza, e moemy definiowa zmienne w dowolnym miejscu kodu programu! Poniej przykad:
uint8_t a=5,b=6; uint16_t c; c=a+b; int z; z=c+a+b;
Jak wida zmienn o nazwie z typu int zdefiniowalimy niejako po drodze. Przeciwnicy jzyka C twierdz, e to wprowadza baagan w kodzie i trudnoci z jego analizowaniem. Moim zdaniem myl si. (Tak p artem, p serio) Po prostu zazdroszcz, e nie maj takich moliwoci. Wybierajc standard kompilacji na ten o nazwie ISO C99 + GNU Extensions (-std=gnu99), otrzymujemy take bardzo ciekaw moliwo definiowania np. zmiennej iteracyjnej w ptli for podczas jej inicjalizacji. Przykad:
for(uint8_t i=0;i<10;I=I+1) instrukcja;
Jak widzisz definicja zmiennej i mieci si w znanej ci ju sekcji inicjalizacyjnej ptli typu for.
[] * ()
- pozwala utworzy tablic obiektw danego typu - (gwiazdka) pozwala utworzy wskanik - pozwala utworzy funkcj zwracajc warto danego typu
Wyobra sobie, e potrzebujesz zdefiniowa 50 zmiennych jednobajtowych typu uint8_t, ktre zostan zainicjalizowane okrelonymi wartociami pocztkowymi. Musiaby napisa albo 50 linii kodu, albo co najmniej kilkanacie, gdzie w kadej zdefiniowa po kilka takich zmiennych. W przypadku pomyki szybka zmiana kodu byaby mczarni. Czy nie lepiej byoby, gdyby mia moliwo uoenia jeden po drugim w formie tablic takich elementw typu uint8_t? Na pewno tak! Ale rwnie dobrze mona zdeklarowa tablic elementw dowolnego typu. Trzeba tylko zgodnie z tym, co pisaem wyej, do nazwy typu prostego doda dwa nawiasy kwadratowe, a pomidzy nimi okreli ilo elementw, aby kompilator wiedzia, ile pamici musi zarezerwowa. W przypadku 50 elementw typu uint8_t bdzie to 50 bajtw, a jak si domylasz, w przypadku 50 elementw uint16_t bd int bdzie to 100 bajtw. Zapiszemy to tak:
uint8_t tablica1[50]; int tablica2[50];
Strona | 75
Poprzez dodanie nawiasw kwadratowych utworzylimy typ zoony, jakim s tablice, przechowujce wiele elementw jednego typu. W przypadku powyszych definicji musiae poda koniecznie liczb elementw, jednak gdy chcemy (a mamy tak moliwo) od razu zainicjalizowa je konkretnymi wartociami, to moemy, aczkolwiek nie musimy, podawa w nawiasach kwadratowych iloci elementw. Kompilator obliczy sobie t ilo na podstawie podanych wartoci, jakimi bdziesz potrzebowa zainicjalizowa takie tablice. Poniej przykady:
uint8_t tab1[] = {1,2,3,4,5}; int tab2[] = {433,1200,20,0,30,288};
Wida z powyszego, e zmienna tablicowa o nazwie tab1 bdzie posiadaa pi elementw jednobajtowych, ktre zostan zainicjalizowane po kolei wartociami od 1 do 5. Natomiast tab2 bdzie posiadaa 6 elementw dwubajtowych, zainicjalizowanych kolejno liczbami podanymi w nawiasach klamrowych. Proste, prawda? Wiesz ju, jak tworzy i inicjalizowa tablice w jzyku C. Dla jasnoci mgby take dokona zapisu:
uint8_t tab1[5] = {1,2,3,4,5};
Jednak gdyby si pomyli i w inicjalizacji wpisaby nie 5 a wicej elementw, to wtedy kompilator ostrzegby ci o tej sytuacji wyranie.
uint8_t tab1[5] = {1,2,3,4,5,6,7,8};
Taka sytuacja jak powyej spowodowaaby bd w trakcie kompilacji, dlatego najczciej, jeli inicjalizujemy tablic w momencie definiowania, pomijamy take ilo elementw w nawiasach kwadratowych. Zobacz, w jak prosty sposb definiujemy tablice acuchw przy uyciu staych tekstowych, o ktrych pisaem wyej:
char bufor[100]; char napis2[] = Nowy tekst;
Pierwsza tablica znakw o nazwie bufor zostaa zdefiniowana i zarezerwowane zostao na jej potrzeby 100 bajtw przez kompilator. Jednak nie dokonalimy inicjalizacji. Poniewa chcemy zainicjalizowa drug tablic, to nie wpisujemy iloci bajtw, gdy zostan one wyliczone na podstawie dugoci znakw tekstu plus jeden na znak null. Oznacza to, e w tym konkretnym przypadku na tablic o nazwie napis2, kompilator zarezerwuje 11 bajtw. (10 Znakw tekstu oraz jeden znak null). Zapytasz zapewne od razu, gdzie taka tablica znakw zostanie zarezerwowana, w jakiej pamici RAM, FLASH, czy EEPROM? Jeli nie zostanie podany aden dodatkowy specyfikator standardu AVR GCC, to zawsze zostanie domylnie zarezerwowane miejsce w pamici RAM. O ile czasem potrzeba nam buforw na znaki czy teksty, na ktrych bdziemy wykonywali rne operacje w pamici RAM, co oczywiste. To jednak czsto potrzebowa bdziemy, aby zdefiniowa pewne acuchy znakw, teksty na stae w pamici FLASH albo w pamici EEPROM, eby mona byo pniej programowo podmienia ich zawarto wg wasnego uznania. Np. jakie napisy na wywietlaczu LCD itd. Jak wspomniaem, aby dokona rezerwacji w innej pamici ni RAM, trzeba uy specjalnych specyfikatorw. Spowoduje to jednoczenie, e odczyt takich danych z pamici FLASH i EEPROM bdzie wyglda inaczej ni z pamici RAM. A jeszcze inaczej bdziemy dokonywali
Strona | 76
modyfikacji ich zawartoci, czyli zapisu do pamici EEPROM. Wybiegajc jednak troszeczk w przyszo, poka ci, jak prosto mona umieci napis w tych rodzajach pamici nieulotnych.
char tab1[] EEMEM = Napis w pamici EEPROM; char tab2[] PROGMEM = Tekst w pamici FLASH;
Wystarczyo posuy si pisanymi du liter specyfikatorami EEMEM lub PROGMEM. Prawda, e proste? Wprawdzie bdzie to si wizao jeszcze z podczeniem odpowiednich bibliotek za pomoc plikw nagwkowych jak: eeprom.h dla specyfikatora EEMEM i operacji na pamici EEPROM oraz pgmspace.h dla specyfikatora PROGMEM i operacji odnonie odczytu z pamici FLASH. W tej chwili tylko to sygnalizuj, za jaki czas powrcimy w szczegach do tych tematw, poniewa bd nam bardzo potrzebne. Na temat typw zoonych, jak wskaniki i funkcje, porozmawiamy dokadniej w dalszych czciach ksiki.
To w wyniku jego analizy moemy okreli po kolei, co si dzieje w nastpujcy sposb. (Pewne informacje zawarem ju, jak widzisz, w przydatnych komentarzach). Zmienne k oraz w posiada bd zakres globalny w pliku, w ktrym znajduje si ta cz kodu programu. Moe jeszcze nie wiesz, ale kod programu moe znajdowa si w wielu
Strona | 77
plikach. Jednak zasig globalny w tym momencie nie odnosi si do caego projektu, czyli wszystkich plikw programu, a tylko i wycznie do tego pliku (o ile nie zmienimy tego stanu rzeczy za pomoc specjalnych specyfikatorw, o czym pniej). Zakres globalny w ramach pliku oznacza, e zmienna taka jest widoczna i nadaje si do uycia (odczyt lub zapis, o ile nie jest typu const), we wszystkich funkcjach programu! Jak widzisz, uywamy zmiennej globalnej o nazwie k w funkcji main(), natomiast zmiennej w take wewntrz funkcji max(). Nie ma z tym najmniejszego problemu, kompilator nie zgasza adnych zastrzee. Wewntrz funkcji main()definiujemy kilka zmiennych, ktre nazywam ju w komentarzach zmiennymi lokalnymi. Oznacza to, e zakres ich widocznoci znacznie si ograniczy. Podobnie wewntrz funkcji max() zdefiniowana jest zmienna lokalna o nazwie z. Wchodzc w szczegy wyjaniam, e np. zmienne lokalne zdefiniowane wewntrz funkcji main() bd dostpne tylko i wycznie dla dowolnych instrukcji programu take tylko wewntrz funkcji main(), nigdzie poza ni. Zatem gdybymy prbowali si w jakikolwiek sposb odwoa do ktrej z nich w innej funkcji np. max() to kompilator zgosiby bd i przerwa kompilacj. Podobnie ze zmienn lokaln o nazwie z zdefiniowan wewntrz funkcji max(), nie jestemy w stanie z niej skorzysta poza t funkcj, czyli w funkcji main() albo dowolnej innej, jeli by taka jeszcze wystpowaa. Prba uycia take skoczyaby si bdem w trakcie kompilacji. Aby dokoczy analiz tego programu, dodajmy, e wida take powyej gwnej funkcji programu deklaracj funkcji max(). Dziki temu kompilator analizujc kod od gry, linia po linii, gdy natrafi na odwoanie si w kodzie (wewntrz funkcji main) do funkcji max(), bdzie wiedzia, e taka istnieje. Zadeklarowalimy j w tym celu wczeniej. Natomiast poniej funkcji main()widzimy ju definicj funkcji max(), czyli cay kod programu, jaki ona zawiera. Reasumujc: Zmienne globalne to te, ktre zdefiniujemy na pocztku kodu programu, przed ciaem funkcji main(), bd zawsze miay globalny zakres widocznoci. Kada funkcja programu bdzie miaa do nich dostp. Zmienne lokalne to te, ktre zdefiniowane zostan wewntrz kadej z funkcji,w tym take funkcji main(). Bd one widoczne tylko dla instrukcji kodu programu zawartych wewntrz funkcji, w ktrych s zdefiniowane. Wystpuj jeszcze inne typy zmiennych, jak np. takie ze specyfikatorem static, ale o tym pniej.
Strona | 78
Teraz krtkie wyjanienie do powyszych linii kodu programu. W pierwszej i drugiej wykonalimy definicj wskanika o nazwie wsk oraz p, ktre pokazuj nam na obiekt nieznanego typu. Napisaem obiekt, poniewa rwnie dobrze taki wskanik bdzie mg pniej posuy do pokazywania na zmienn dowolnego typu podstawowego lub zoonego albo nawet na funkcj programu. W trzeciej linijce pierwszy specyfikator void ten przez nazw funkcji mwi o tym, e zdefiniowana w ten sposb funkcja nie bdzie zwraca adnego wyniku. Natomiast specyfikator void pomidzy nawiasami okrgymi mwi, e do tej funkcji nie bd przekazywane adne argumenty.
Od tej pory moesz uywa zmiennej wspczynnik oraz PI ale tylko i wycznie w trybie do odczytu. Gdy tylko sprbujesz nawet przez pomyk zmieni zawarto jednej z tych zmiennych, od razu kompilator zareaguje bdem w trakcie przeprowadzania kompilacji. Zwrc uwag na dodatkow kwesti. Wprawdzie nie znasz jeszcze zagadnie zwizanych z preprocesorem. Jednak istnieje pewna dyrektywa tego preprocesora o nazwie #define. Dziki niej moglibymy uzyska bardzo podobny efekt, jeli chodzi o moliwo zdefiniowana pewnych staych wartoci, o jakich mwiem powyej. Oznacza to, e uywajc zapisu z przykadu poniej:
#define wspolczynnik 45 #define PI 3.14
otrzymujemy pozornie identyczn sytuacj. Od tej pory moemy si posugiwa identyfikatorami wspolczynnik oraz PI na podobnej zasadzie. Istniej jednak podstawowe rnice, o ktrych warto wiedzie, gdy moe to si okaza bardzo przydatne w trakcie pisania rnych programw. Czasem warto bdzie skorzysta ze specyfikatora const, a czasem wystarczy #define. Oceni si to samemu, kiedy posidzie si odpowiedni wiedz i praktyk w tym zakresie. Czym jednak z praktycznego punktu widzenia rni si dla nas deklaracja zmiennej za pomoc #define od definicji ze specyfikatorem const? W oparciu o informacje podane wczeniej na temat rnic pomidzy deklaracj a definicj zmiennej ju powiniene dostrzec podstawow rnic. Ot definicja zmiennej/staej ze specyfikatorem const od razu rezerwuje miejsce w pamici na t zmienn. Natomiast sama deklaracja za pomoc dyrektywy #define tego nie czyni. Dyrektywa #define powoduje z punktu widzenia kompilatora zadeklarowanie staej dosownej, zatem kompilacja odbywa si w ten sposb, e w kadym miejscu, gdzie kompilator napotka nazw zadeklarowan za pomoc dyrektywy #define po prostu podstawi w to miejsce konkretn sta warto, ktra widnieje w tej deklaracji.
Strona | 79
Kolejna rnica w tym, e stae definiowane przy uyciu const bd mogy uzyskiwa rne zakresy widocznoci, w zalenoci od tego, w jakim miejscu zostan zdefiniowane. Natomiast stae zdeklarowane z uyciem #define bd zawsze widoczne dla kompilatora w caym programie. Jeeli spotkasz si z sytuacjami, kiedy warto bdzie ukrywa zasig swoich staych, wtedy signiesz po const. Kolejna rnica polega na tym, e staa zdefiniowana za pomoc const ma swoje odzwierciedlenie w pamici mikrokontrolera, a co za tym idzie, mona do niej odwoa si za pomoc wskanika. Tymczasem staa zadeklarowana poprzez #define, jako e nie rezyduje w pamici, nigdy nie bdzie dostpna poprzez wskanik. Czasem moe to by bardzo potrzebne. Mam tylko nadziej, e starasz si ledzi dokadnie, w jakich momentach uywam sowa deklaracja, a w jakich definicja. Nie robi tego przypadkowo i zamiennie, poniewa kade z tych okrele ma swoje istotne znaczenie. Teraz widzisz, jak wane jest i ile rzeczy si wie z dobrym zrozumieniem tego zagadnienia.
Kiedy powinnimy go stosowa? Zawsze w takich sytuacjach, gdy chcemy, aby kompilator nie optymalizowa dostpu do takiej zmiennej. Co si takiego wie z optymalizacj zmiennych bez przydomka volatile? Kompilator nie widzc tego przydomku zakada, e taka zmienna nigdy nie bdzie moga si zmieni bez wiedzy kompilatora. Zaoenie to czyni ju na etapie kompilacji i w wyniku tego czsto, jeli np. w jakiej funkcji ma wykonywa operacje na teje zmiennej, pozwala sobie na optymalizacj, majc na celu przypieszenie dziaania programu. W mikrokontrolerach jest to szczeglnie istotne. Optymalizacja taka polega najczciej na tym, e bezporednio po wejciu do funkcji main() czy jakiejkolwiek innej, kompilator zapamituje sobie zawarto komrki tej pamici w podrcznym i wolnym rejestrze mikrokontrolera. Zwykle ma spory zapas takich wolnych rejestrw. Dziki temu w dalszej czci funkcji ju nigdy nie odwouje si do zawartoci tej komrki pamici, tylko operuje na zawartoci rejestru. Dopiero przy wyjciu z funkcji oczywicie przy wyjciu z innej funkcji ni main() dokona zapisu aktualizowanej w rejestrze zawartoci bezporednio do tej komrki. A poniewa ptla gwna programu main() nigdy si nie koczy to mona przypuszcza, e cay czas bdzie program si odwoywa i pracowa tylko w oparciu o ten rejestr, eby szybciej wykonywa dziaania. Jest to z jednej strony bardzo poyteczne i podane. Jednak czasami wprowadzasz do programu procedur obsugi jakiego przerwania, ktra take bdzie miaa za zadanie wykonywa operacje na tej samej zmiennej. I jeli nie bdzie ona opatrzona przydomkiem volatile, to kompilator spowoduje, e w trakcie wejcia w przerwanie, zmienna trafi do innego rejestru, na nim dokonane zostan stosowne aktualizacje a przy wyjciu z procedury obsugi przerwania, zawarto rejestru trafi znowu do komrki pamici tej zmiennej. No i katastrofa! Bo przecie w ptli gwnej main() program nigdy nie zajrzy, jak wspomniaem wyej, do tej komrki w zwizku z umieszczeniem jej zawartoci w podrcznym rejestrze. W takiej sytuacji zawsze dziaa na podrcznym rejestrze, do ktrego raz wczyta t zmienn. Zatem ptla gwna nigdy si nie dowie o tym, e w co (np. procedura przerwania) podmienio zawarto tej komrki i zacznie dochodzi do przedziwnych bdw w programie, ktrych przyczyny trudno bdzie si od razu domyli.
Strona | 80
Dlatego, jeli wiesz, e niektre zmienne bd zapisywane i odczytywane zarwno w funkcjach, ale i procedurach przerwa mikrokontrolera, czyli mog si zmienia w rnym czasie, to trzeba wymusi, aby kompilator nie optymalizowa dostpu do takich zmiennych. Musi za kadym razem, gdy chce wykona na nich operacj, odwoa si bezporednio do zawartoci komrek pamici, gdzie te zmienne rezyduj i to ich zawarto modyfikowa wedle potrzeb, a nie jaki podrczny rejestr penicy rol bufora. Brak takiej optymalizacji wpynie wprawdzie na to, e dostp do zmiennej bdzie nieco wolniejszy ni do rejestru (co mogoby by w wielu przypadkach spor wad), ale dlatego nie wszystkie zmienne definiujemy jako volatile. Tylko te, ktrych jak byo powiedziane na pocztku, zawarto moe by ulotna, czyli zmienia si bez wiedzy kompilatora, np. w procedurach obsugi przerwa.
Z uwagi jednak na to, e wszystkie rejestry mikrokontrolerw AVR maj posta jednego bajtu. Tylko niektre mog by traktowane w poczeniu, jako rejestry indeksowe, nie ma wikszego sensu, aby dodawa przydomek register zmiennym, ktre posiadaj rozmiar wikszy ni jeden bajt ostatecznie dwa bajty.
Strona | 81
Bardzo czsto programistom nie chce si wpisywa specyficznych typw dla AVR GCC i mikrokontrolerw AVR jak: uint8_t, uint16_t czy te uint32_t. Dlatego te czsto w tych programach znajdziesz uyt instrukcj typedef w celu utworzenia nowych typw o nazwach jak wyej: u08, u16 czy u32. Dziki temu nie trzeba tyle pisa za kadym razem przy definicji zmiennej. Wystarczy na pocztku programu napisa:
typedef uint8_t u08; typedef uint16_t u16; typedef uint32_t u32;
Oznacza to, e wanie zdefiniowalimy na wasne potrzeby trzy nowe typy o nazwach jak w przykadzie, dziki czemu jeli w dalszej czci kodu programu zdefiniujemy zmienne korzystajc z tych typw, zostan one potraktowane dokadnie jak zmienne typu, od ktrego pochodzi nasz nowo utworzony typ. Jednym sowem zmienna typu u08 bdzie tak naprawd zmienn typu uint8_t i analogicznie nastpne. Nie tylko w taki celach przydatne bywa definiowane nowych typw. Np. bdziesz w programie bardzo czsto posugiwa si zmiennymi, ktra bd miay np. za zadanie przechowywa warto poziomu oleju. Definicje takich zmiennych bd si pojawiay w wielu funkcjach i miejscach programu. Charakterystyczne dla nich bdzie to, e zawsze zawieraj liczb cakowit ze znakiem z zakresu int. Ciko bdzie jednak pniej analizowa program, ktry posiada zdefiniowan spor ilo zmiennych, nazwy s rne i jak na pierwszy rzut oka, szybko wyapa, ktre z nich przechowuj ten poziom oleju? Odpowied jest prosta, wystarczy zdefiniowa nowy typ:
typedef int P_olej;
Od tej pory bdziesz mg spokojnie w rnych czciach programu definiowa rne zmienne a patrzc na ich typ bdziesz w mig wiedzia, do jakiego celu je utworzye.
P_olej poziom1; P_olej poziom2; P_olej wskaznik3; P_olej miarka2;
Przyznaj, e to bardzo przydatna rzecz. Naturalnie tego polecenia mona take uywa w zwizku z typami zoonymi, jak np. wskaniki. Przykady:
typedef int * wskaznik_do _int; typedef char * napis;
pniej definicje
wskanik_do_int p1; napis komunikat;
// czyli: int * p1 // czyli: char * komunikat
Bardziej docenisz to zagadnienie z powyszego przykadu, gdy dowiesz si duo wicej na temat samych wskanikw oraz moliwoci ich stosowania. Zapewniam ci wyprzedajc fakty, e wskaniki to jedno z najlepszych narzdzi jzyka C,
Strona | 82
cho trzeba przyzna, e take nieco skomplikowane i na pocztku ciko je zrozumie bez dobrego wytumaczenia i przykadw. Nie martw si, postaram si, aby jak najszybciej i jak najwicej zrozumia te zagadnienia w dalszych rozdziaach.
Gdzie w miejsce nazwa typu w naszym konkretnym przypadku wprowadzimy nazw np. menu_poz a w nawiasach klamrowych podasz tylko wartoci opisowe dla poszczeglnych poziomw menu z tradycyjnej tabelki, np. tak jak poniej. Jednak w przykadzie z oczywistych wzgldw wpisz tylko kilka wartoci. Przyjmijmy zaoenie, e zbudowalimy zegar oraz utworzylimy menu gwne, z ktrego mona przej do kolejnych poziomw aby ustawi czas, ustawi dat czy te ustawi alarm (budzik).
enum menu_poz {mglowne, mczas, mdata, malarm};
Prosz bardzo, wanie w pierwszej linii powyszego przykadu zdefiniowalimy nowy typ wyliczeniowy o nazwie menu_poz, a nastpnie w kodzie programu moemy ju zdefiniowa konkretn zmienn o nazwie w tym przypadku idx. Dokonujemy jednoczenie jej inicjalizacji (UWAGA!), nie za pomoc staej liczbowej, lecz za pomoc wartoci, ktra istnieje w
Strona | 83
naszym typie wyliczeniowym. Zatem moemy ju wyrzuci nasz karteczk z rozpisan tabelk, bd usun tabelk z opisu w pliku. Wicej si nam ona nie przyda. Ale zapytasz zapewne, co si stanie, jeli teraz zechcemy doda now pozycj w naszym menu? Ale nic prostszego, zamy, e kolejna pozycja menu powinna umoliwia ustawienia parametrw zegara. Taki nasz setup.
enum menu_poz {mglowne, msetup, mczas, mdata, malarm};
Zauwa, e specjalnie wstawiem t pozycj gdzie w rodek naszych wartoci typw, bo zaoenie jest takie, i kolejn pozycj po menu gwnym ma by wanie setup. Jednak istnieje w tym przypadku zupena dowolno, mona doda na kocu bez adnych konsekwencji, tak jak to byo, gdy prowadzilimy swoje tabelki z opisami.
enum menu_poz {mglowne, mczas, mdata, malarm, msetup};
Mam nadziej, e dostrzegasz teraz ogromne moliwoci tego mechanizmu? Moesz si jednak zastanawia, co tak naprawd bdzie zawiera zmienna idx, jeli przypiszemy do niej dowoln warto typu wyliczeniowego. Ju wyjaniam. Zasada jest prosta, domylnie, jeeli sami nie wprowadzimy zmian, rozpoczyna si numerowanie wartoci od zera. W zwizku z tym pozycja mglowne=0, mczas=1, mdata=2, malarm=3, setup=4. Jednak programista ma moliwo wpywu na t numeracj. Wystarczy dokona takiego zapisu:
enum menu_poz {mglowne, mczas=7, mdata, malarm=43, msetup};
Spowoduje to, e teraz: mglowne mczas mdata malarm setup = 0 (domylnie gdy nie przydzielilimy rcznie adnej wartoci) = 7 (wida wyej dlaczego) = 8 (poniewa numeracja bdzie dalej biega od poprzedniego numeru) = 43 = 44
Pamitaj, e zmienn naszego nowego typu moesz posugiwa si take, jak zwyk liczb. Dozwolone s ponisze dziaania:
enum menu_poz idx; uint8_t a,b=10; idx = malarm; a = b + idx;
W wyniku takiego dziaania zmienna a bdzie miaa warto 53. Poniewa do zmiennej idx przypisalimy malarm, ktry zgodnie z przykadem wyej posiada zdefiniowan warto = 43. Natomiast zmienn b zainicjalizowalimy liczb 10. Zatem wynikiem b + idx jest liczba 53. Okazuje si take, e kompilator AVR GCC zezwala na przypisanie do zmiennej idx take innych wartoci tzn. czysto liczbowych za pomoc staych
Strona | 84
lub zmiennych. Niektre inne kompilatory nie zezwalaj na tak operacj generujc bd w trakcie kompilacji. Czy taki mechanizm bdzie ci potrzebny ocenisz ju sam. W kadym razie mona zastosowa ponisze dziaania:
enum menu_poz idx; uint8_t a=10; idx = 122; idx = a + 87;
Wykorzystanie tego zaley ju tylko od twojej inwencji twrczej. Podam jednak jeszcze jeden przykad popularnego zastosowania dla typu enum. Pniej w czci praktycznej, gdy zajmiemy si oprogramowaniem ukadu scalonego, RTC, ktry jest zegarem czasu rzeczywistego, okae si, e mona z niego odczyta, jaki mamy aktualnie numer dnia tygodnia. Jak si dowiesz, wystpuje tam taka zaleno, e odczytana liczba 0 odpowiada poniedziakowi, 1 to wtorek, 2 roda, 3 to czwartek, 4 pitek, 5 sobota oraz 6 niedziela. Gdy napiszesz program do obsugi takiego zegarka, zapewne bdziesz chcia atwo i szybko pokazywa nazwy dni tygodnia na wasnym wywietlaczu LCD. Jednak za kadym razem bdziesz musia pamita powysze przypisania w rnych procedurach, gdzie bdzie trzeba sprawdza, jaki jest aktualnie dzie tygodnia. Aby sobie uatwi ycie, zastosujesz jednak typ wyliczeniowy enum.
enum t_dzien {pon, wto, sro, czw, pia, sob, nie};
pniej utworzysz zmienn bd zmienne, w ktrych bdziesz przetrzymywa dni tygodnia, ale nie bdziesz musia si ju posugiwa na pami cyframi kolejnych dni. Teraz bdziesz uywa ju wygodnych i atwych do zapamitania nazw.
enum t_dzien dzien = sro;
Strona | 85
Jak wida w zalenoci od dnia tygodnia mona wykona rne czynnoci w programie, symbolizuj to rne numery instrukcji w przykadzie. Mam nadziej, e wyczerpujco przedstawiem to wane narzdzie, jakim jest typ wyliczeniowy.
Przypomn, e ju w wielu przykadach powyej skorzystalimy ze staych dosownych. Byy to jak do tej pory stae liczbowe cakowite. Gdy pisalimy np. definicj zmiennej int a=122; to liczba 122, ktr zainicjalizowalimy zmienn, jest wanie pierwszym przykadem staej dosownej.
Nie musz chyba przypomina, jak stosowa zapis szesnastkowy, licz na to, e ju wiesz dokadnie, o co w tym chodzi. Zwrc jedynie uwag, e jzyku C, jeli chcemy przedstawi liczb w postaci szesnastkowej, to w odrnieniu od postaci dziesitnej musimy j poprzedzi znakami 0x (zero oraz x). Dodam jeszcze, e mona w zapisie szesnastkowym inaczej zwanym hexadecymalnym uywa zarwno duych jak i maych liter. Dla kompilatora bdzie to zupenie obojtne. Trzeba sobie jednak zdawa spraw, do jakiego typu konkretnie kompilator zalicza domylnie stae bdce liczbami cakowitymi. Domylnie, jeli napiszemy np. liczb 200, zostanie ona zakwalifikowana jakby bya typu int. Jeli oczywicie chcemy poda, jako sta liczb, ktra wykracza poza zakres int (patrz Tabela.1), to wtedy zostanie uznana, oczywicie automatycznie, za kolejny wikszy typ, czyli np. long int. Mamy jednak moliwo aby wiadomie da zna, aby np. liczb 200 traktowa od razu jako typ long int. Wystarczy, e na kocu takiej liczby postawimy literk L. Moe to by maa lub dua litera, jednak ze wzgldu na czytelno lepiej posugiwa si du. Wtedy zapis bdzie wyglda tak 200L, 0L, 33L itd.
Strona | 86
Mamy take wpyw na to, czy kompilator ma przyjmowa sta, jako liczb bez znaku, (jako unsigned). Wtedy musimy na kocu zastosowa literk u. Przykad: 223u, 10u, 1234u itd. Mona take czy literk u z literk L, wtedy okrelamy, e chodzi nam o typ unsigned long int, przykady: 200uL, 1000000uL, 855uL itd.
W naszym standardzie jzyka AVR GCC ze wzgldu na opisane wyej ograniczenia bd zawsze traktowane jako typ float z nalen mu precyzj. Generalnie, musisz pamita, eby jak najrzadziej korzysta w ogle z typw zmiennoprzecinkowych. Wie si to z tym, e mikrokontrolery AVR nie posiadaj rozkazw na poziomie kodu maszynowego, ktre mogyby dokonywa oblicze bezporednio na takich liczbach. Zatem wszelkie operacje wymagaj zastosowania przez kompilator dosy sporych objtociowo bibliotek programowych, ktre zajmuj spore iloci pamici programu, a take pamici RAM mikrokontrolera. Dlatego czsto pocztkujcy adepci jzyka AVR GCC s bardzo zdziwieni, e jeli na pewnym etapie tworzenia programu zastosuj chocia jedn zmienn typu float, na ktrej zechc wykona operacje matematyczne, to od razu po kompilacji okazuje si, e drastycznie wzroso zuycie pamici programu FLASH oraz czsto take pamici RAM naszego mikrokontrolera. O tym, jak sobie z tym radzi i jak unika typu float bdzie pniej, szczeglnie w trakcie wicze.
W pierwszych trzech przypadkach mamy do czynienia z literami, a w kolejnych dwch ze znakami cyfr, jeszcze w kolejnych dwch stae reprezentujce znak plus oraz hash. Wykorzystujemy je wtedy, gdy chcemy zainicjalizowa jak nowo zdefiniowan zmienn, np.:
char znak = A; char p = a;
Oczywicie mikrokontroler nie potrafi przechowywa znaku A czy te znaku cyfry 6. Za to potrafi podstawi do tych zmiennych kody ASCII tych znakw. W tym przypadku zmienna znak bdzie zawieraa tak naprawd liczb 65 a zmienna p liczb 97. S to dokadne kody znakw z tabeli ASCII. Ale to nie wszystko, poniewa nie wszystkie znaki ASCII jestemy w stanie wpisa w postaci znaku w jakimkolwiek edytorze. Wemy na przykad znany ci dobrze znak ASCII o nazwie ENTER. Posiada on kod = 13. Ale s take inne niedrukowalne na ekranie znaki. Okrela
Strona | 87
si je mianem znakw specjalnych i w jzyku C mamy do nich dostp w prosty sposb. Poniej krtkie zestawienie niektrych znakw specjalnych.
\b \f \n \r \t \v \a Backspace Form feed New line carriage Return Tabulator Vertical tabulator Alarm
Posuyem si nazwami angielskimi, poniewa i tak najczciej takimi si posugujemy. Bardzo czsto bdziemy uywa znakw \r czyli nasz znak ENTER (kod 13) oraz \n, czyli znak nowej linii (kod 10). eby przypisa do zmiennej taki znak posuymy si take apostrofami w ktrych zamkniemy taki znak specjalny:
char znak = \r;
Od tej pory zmienna znak bdzie przechowywaa kod, czyli liczb 13. Poniewa jednak do zapisu znakw specjalnych musimy uywa ukonika \ oraz apostrofw, to pojawi si pewien kopot, jeli bdziemy chcieli podstawi do zmiennej czy dokona porwnania w warunku, znaku apostrofa, ukonika, ale te jeszcze kilku innych znakw. Dlatego podam jeszcze kolejn krtk list znakw specjalnych, tzn. jak naley je zapisywa w kodzie.
\\ \ \ \? \0 ukonik apostrof cudzysw znak zapytania null, czyli znak o kodzie zero
Oczywicie takie znaki take musimy otoczy apostrofami, wic czasem wyjd dziwne konstrukcje np. w przypadku znaku apostrofa, bdziemy musieli napisa tak: char znak=\. Jednak to jest jedyny prawidowy zapis z uyciem znaku specjalnego. Mamy do dyspozycji jednak jeszcze jeden sposb przedstawiania konkretnego znaku ASCII wewntrz apostrofw. Moemy go poda bezporednio jako liczb, ale tylko w zapisie hexadecymalnym lub semkowym. Najczciej bdziemy si posugiwa jeli ju zapisem hexadecymalnym. Pozwala on nam na reprezentacj kadego znaku ASCII bez wyjtku. Aby mc zaprezentowa znak w postaci hexadecymalnej, musimy uy prefixu/zapisu: \x, ktry bdzie poprzedza warto szesnastkow. Przykad:
char = \x41; co jest rwnoznaczne char = A; poniewa liczba 0x41 to dziesitnie 65, natomiast 65 jest kodem ASCII duej literky A.
W skrcie mwimy na takie cigi znakw po prostu stringi. Wewntrz takiego cigu znakw moemy bez problemu wstawi znaki specjalne opisane powyej, np.:
Pierwsza linia tekstu \r\n Druga linia tekstu
Jak widzisz wstawiem dwa znaki po sobie jeden to znak ENTER \r a drugi to znak nowej linii \n. Dziki temu, gdybymy taki cig znakw przesali np. do terminala, spowodowaby to, e pojawiyby si na jego ekranie dwie linie tekstu zamiast jednej. Tekst Pierwsza linia tekstu wywietlony zostaby w pierwszej linii, nastpnie znaki specjalne spowodowayby przejcie do pocztku nowej linii oraz wywietlenia w niej kolejnej czci tekstu Druga linia tekstu. Jak widzisz na pocztku pozostaaby spacja, ktr wstawiem specjalnie w tekcie stringa aby wyranie uwidoczni znaki specjalne. Nie trzeba oczywicie stosowa takich spacji. Stringi moemy zapisywa na wiele rnych sposobw. Jeden ju znamy, mona wstawia do rodka znaki specjalne. Jeli na przykad chcemy zapisa bardzo dugi cig znakw, ktry nie mieci nam si w jednej linii w oknie edytora, moemy go rozbi na czci w poniszy sposb, stawiajc rednik na samym kocu:
char tab[] = Przykad linii, w ktrej wystpuje bardzo dugi tekst i nie mieci si w jednej linii;
Wprawdzie jeszcze nie wiesz co oznacza zapis tab[], ale wane, aby pamita, e rednik postawiony dopiero na kocu trzeciej linii spowoduje, i kompilator poczy wszystkie wystpujce w kadej linii acuchy w jeden dugi. Teraz najwaniejsza rzecz: jakiego typu s stae typu C-string? Rozpatrzmy to na kolejnym krciutkim przykadzie:
Procesor
Bdzie typem const char[9]. Zapewne zdziwi ciebie bardzo skd wzia si tutaj liczba 9, skoro nasz acuch ma tylko 8 znakw? Ju wyjaniam. Rzeczywicie tekst w cudzysowach posiada tylko 8 znakw, jednak po nich zgodnie ze standardem C-string, musi wystpi znak null (fizycznie liczba zero). Zatem cznie taki string musi zawiera 9 znakw. Oczywicie, jeli wpiszemy inny tekst, to zawsze w nawiasie kwadratowym pojawi si liczba znakw tekstu plus jeden. Mam nadziej, e pamitasz, i specyfikator const mwi o tym, i zmienna taka nie moe by pniej w kodzie w aden sposb modyfikowana. Jeli sprbujesz tego dokona, to kompilator wywietli bd i uniemoliwi przeprowadzenie kompilacji. Oznacza to tak naprawd, e kompilator musi gdzie umieci w pamici takie stae. Zarezerwowa na nie miejsce. Mamy oczywicie moliwo zdecydowania, w jakiej pamici stae te maj by umieszczone. Jednak odpowiednie specyfikatory wskazujce na pami FLASH, pami RAM lub EEPROM poznamy pniej. Wane jest, e raz zarezerwowany obszar na dowoln sta nie moe by w pniejszym terminie zmieniany przez program. Std specyfikator const.
Niniejsza darmowa publikacja zawiera jedynie fragment penej wersji caej publikacji.
http://witmir.pl