You are on page 1of 3

Programowanie

Programowanie procesorów
w jzyku C cz 18 – podsumowanie
Wykonywanie Opisana przed chwil droga to przejcie naj- Spróbujmy razem przeanalizowa dziaa-
dalekiego skoku pierw po czarnych a póniej po czerwonych nie programu. Funkcja main dokonuje stan-
Do tej pory nauczylimy si wykorzystywa strzakach. Zobacz jednak, e zaznaczyem dardowej inicjacji. Wypisujemy na pierwszej
instrukcje goto. Instrukcja ta ma jednak to do te du niebiesk strzak. Omija ona linii wywietlacza sówko „START” – wiemy
siebie, e umoliwia skok tylko we wntrzu wszystkie porednie sprawdzenia wy-
funkcji. ANSI C posiada mechanizm, który stpienia bdów. Jest to duo ele- Listing 238 Przykad wykorzystania dalekiego skoku
pozwala na wykonywanie skoków w obrbie gantsze rozwizanie. Dodatkowo jest
caego programu. Oferowany mechanizm jest bardziej efektywne, poniewa omija- void funkcja_2(void)
bardzo wygodny w szczególnych przypad- my cige sprawdzanie wartoci { DrawLine_P(PSTR(„f2 - start“));
kach. zwracanych przez funkcje poredni-
Wyobramy sobie sytuacj, e wywouje- czce w transmisji. longjmp(err_jmp, 33);

my funkcj transmisji ramki danych, która Moliwo wykonania takiego DrawLine_P(PSTR(„f2 - end“));
wielokrotnie wywouje funkcj transmisji baj- skoku daj nam funkcje setjmp oraz }
tu jako dwóch bajtów ASCII, wywoujc longjmp. Wyjanienie zasady ich void funkcja_1(void)
dwa razy funkcj transmisji jednego znaku. dziaania znajduje si w odpowied- {
DrawLine_P(PSTR(„f1 - start“));
Teraz pojawia si, problem: z jakiego powo- niej ramce. Jeli zawarte tam wyja-
du znak nie moe by wysany. W jaki sposób nienie pozostawia jakie niejasnoci, funkcja_2();
wykryjemy powstanie bdu? Konieczne b- zobacz kod na listingu 238. Listing DrawLine_P(PSTR(„f1 - end“));
dzie sprawdzanie wystpienia bdu w kolej- ten, dla przejrzystoci pozbawiony }
nych funkcjach: Funkcja wysyania znaku jest wielu elementów. Korzystamy
zwróci bd, wykryje to funkcja wysyania z naszej biblioteki obsugi LCD, któ- // START
bajtu w formacie ASCII i przekae do funkcji r rozbudowalimy o funkcje pisania. int main(void)
{
transmisji ramki, która wreszcie zwróci bd Funkcja DrawLine_P, przedstawiona int ret; // warto zwrócona przez setjmp
do funkcji wywoujcej. na listingu 239, ma za zadanie wypi-
// Inicjacja wyprowadze, SPI oraz wywietlacza
Troszk to zamotane? Tak wanie powin- sa informacj w linii i przesun we- (...)
no brzmie. Wszystko wyjani rysunek 72. wntrzny „kursor” na lini nastpn.
// Czyszczenie wywietlacza
Na listingach brakuje funkcji befo- lcd_Cls(0x03);
Rys. 72 Propagacja bdów w re_main, której zadaniem jest wcze- DrawLine_P(PSTR(„START“));
kolejnych wywoaniach funkcji nie pamici zewntrznej.
// Miejsce gdzie wróc wywoywane funkcje
ret = setjmp(err_jmp);

if(ret != 0)
{
// Wypisuj informacj o bdzie
lcd_Printf(0, g_linia*8, PSTR(„Error: %d“),
0xfc, 0,
LCD_TM_FLASH | LCD_TM_TRANSPARENT, ret);
++g_linia;
lcd_Update();
}
else
{
// setjmp wanie ustawio adres powrotu
funkcja_1();
}

DrawLine_P(PSTR(„END“));
ABC... C powrót z funkcji zwizany jest z wykonaniem skoku, zwraca-
na warto równa jest wartoci podanej jako drugi parametr for(;;) {}
setjmp i longjmp – jak to dziaa funkcji longjmp.
Co wane, skok longjmp moe odbywa si tylko
return 0;
Dwie tytuowe funkcje, zdeklarowane w pliku <setjmp.h> wstecz. To znaczy, e ustawienia zapisane w buforze s wa-
}
umoliwiaj wykonywanie skoków przez cay program. Ska- ne tak dugo, jak funkcja, która je ustawia, nie zostanie za-
kanie to wykonuje si w do specyficzny sposób. Obie funk- ko czona. Zako czenie nastpuje przez wykonanie instrukcji
cje korzystaj ze specjalnej zmiennej typu jmp_buf. Wywo- return albo wykonanie skoku przez longjmp. Jest to zwiza-
anie funkcji setjmp zapisuje do niej aktualny stan procesora. ne z tym, e jeli wskanik stosu zostanie przesunity poniej
Obejmuje to rejestry, które nie s odtwarzane przez wywoy- pozycji zapamitanej poprzednio, dane w nim zawarte mog
zosta nadpisane i nie bd prawidowe. Listing 239 Pomocnicza funkcja wypisania linii
wan funkcj (s to rejestry r2-r17 oraz r28 i r29 – patrz
cz 12), rejestr SREG oraz aktualn pozycj wierzchoka Uwaga: pewien problem dotyczy zmiennych automa-
stosu. tycznych (wewntrznych zmiennych funkcji, przechowywa- void DrawLine_P(prog_char *str)
Póniejsze wywoanie funkcji longjmp, przy podaniu nych w rejestrach). Jeli zmienimy zawarto takiej zmiennej {
jako pierwszy parametr zmiennej, która wczeniej zostaa midzy wywoaniem funkcji setjmp a longjmp, jej zawar- lcd_DrawText(0, g_linia*8, str, 0xfc, 0,
ustawiona przez setjmp, spowoduje nie tylko skok pod odpo- to moe by nieokrelona. Nie sposób okreli, czy zmien- LCD_TM_FLASH | LCD_TM_TRANSPARENT);
wiedni adres, ale take przywrócenie stanu procesora. na ta jest w rejestrze, którego kopia znajduje si w jmp_buf, ++g_linia;
Funkcja setjmp zwraca warto 0, jeli wanie j czy na stosie, odoona tam ju po zmianie. lcd_Update();
wywoano i odpowieni stan zosta zapisany do bufora. Jeli Przykad: listing 238 }

E l e k t ro n i k a d l a Ws z y s t k i c h 41
Programowanie
teraz, gdzie wszystko si rozpoczo. Nastp-
nie dochodzimy do miejsca oznaczonego ABC... C albo w pliku makefile dopisujc do zmiennej CDEFS
kolorem ótym. Zapisywany jest stan proce- Diagnostyka – makro assert warto -DNDEBUG.
sora. Poniewa funkcja setjmp zostaa wanie
wywoana – zwraca 0, przechodzimy do cz- Plik <assert. h> definiuje praktycznie tylko jedno makro: Znak koca linii
makro assert. Makro to ma nastpujc posta: Makro assert jako znak koca linii wysya jedynie symbol
ci, w której wywoujemy funkcj funkcja_1.
assert (warunek); ‘\n’ – nowa linia. Naley to uwzgldni w ustawieniach
Funkcja ta wypisuje informacj, e rozpocz- Assert pochodzi w tym przypadku z angielskiego „za- terminala, albo tak napisa funkcj wysyajca znak, aby
a swoje dziaanie, a nastpnie wywouje dru- pewni”. I sowo to oddaje jego dziaanie. Jeli warunek w chwili wykrycia znaku ‘\n’ wysyaa wczeniej symbol
g funkcj. Std, po wypisaniu kolejnej infor- jest speniony, nasze makro nie zmienia przebiegu progra- ‘\r’ – powrotu karetki.
macji o rozpoczciu dziaania funkcji, wyko- mu. Jednak w przypadku, gdy wynikiem sprawdzenia wa-
nujemy daleki skok. Program rozpoczyna runku bdzie fasz, makro wypisze, na standardowe wyj- Specyfika mikrokontrolera
cie bdu, informacj która moe mie nastpujc for- i AVR-GCC
swoje wykonywanie od miejsca, gdzie wywo- m: W systemie mikroprocesorowym problemem moe by
alimy instrukcje setjmp. Teraz jednak zwró- Assertion failed: (x < LCD_SX), function main, line 172. potrzeba istnienia standardowego wyjcia bdu. Jednak,
cona warto to nie zero, lecz liczba 33 – dru- a wykonywanie programu zostanie zatrzymane przez w duym systemie, czsto bdziemy mieli specjalnie wy-
gi argument funkcji longjmp. Tym razem wy- wywoanie funkcji abort. prowadzony port sucy do przeprowadzenia debugowa-
konujemy cz kodu odpowiedzialn za wy- Jak widzisz, wypisana informacja daje peen zbiór da- nia – wtedy uycie assert’a stanie si bardzo przyjemne.
nych jakich potrzebujemy aby namierzy problem. AVR-GCC ma to do siebie, e aby informacja o b-
pisanie zwróconej wartoci i funkcja main si Makro assert stosuje si zwykle tylko w fazie urucha- dzie zostaa wypisana, przed doczeniem nagówka
koczy. miania programu. Jzyk C zapewnia moliwo jego wy- <assert. h> musi pojawi si linia:
Spróbuj uruchomi program tak jak jest czenia w procesie kompilacji. W tym celu, przed do- #define __ASSERT_USE_STDERR
oraz po usuniciu funkcji longjmp. Efekty czeniem pliku <assert. h> naley zdefiniowa sta NDE- W innym przypadku jedyne co bdzie wykonane to za-
przedstawiaj fotografie 15 i 16. Widzisz ju BUG. Definicj tak najlepiej umieci w pliku nagów- trzymanie programu, bez wypisania informacji o bdzie.
kowym który doczamy do wszystkich plików programu, Przykad: listing 240
wygod stosowania mechanizmu dalekiego
skoku? To nie musi by koniecznie metoda na
obsug bdów – wszystko zaley od pomysu. Listing 240 Sposób uycia makra assert
Debugowanie // Statyczne tworzenie „pliku“. Omawiane w czci 11
Ostatnim, standardowym elemen- static FILE g_rsf_temp =
FDEV_SETUP_STREAM(rs_put, rs_get, _FDEV_SETUP_RW);
tem C, jaki chc Ci przedstawi, #define g_rsf (&g_rsf_temp)
(...)
jest makro assert. Makro to okazu- // START programu
je si niezwykle przydatne w przy- int main(void)
padku pisania duego kodu, tym { // Ustawienie wyjcia bdów
bardziej e zajmuje ono troch pa- stderr = g_rsf;
// Konfiguracja portu RS
mici programu. Jednak jego za- RS_SET_BAUD(2400);
stosowanie moe da nieocenione UCSR0C = 1<<URSEL0 | 1<<UCSZ01 | 1<<UCSZ00;
UCSR0A = 0;
usugi w przypadku wystpienia UCSR0B = 1<<RXEN0 | 1<<TXEN0;
problemów, gdy bdziemy poszu- (...)
// Podsycanie ognia
kiwa ich róda. uint8_t n;
Stosowanie makra assert, na for(n=0; n<80; n++)
{
Fot. 15 Przebieg dziaania programu przykadzie naszego programu ge- (...)
nerujcego symulacj ognia, poka- assert(x != LCD_SX/2-1);
bez funkcji longjmp }
zuje listing 240. Program przery- (...)
Fot. 16 Przebieg dziaania programu wamy w chwili, gdy wylosowana
z funkcj longjmp zostanie pozy-
cja x na rod-
ku wywietla-
cza. Nie ma to
wikszego
sensu prak-
tycznego, ale
umoliwia za- Rys. 73 Dziaanie makra assert
obserwowanie dziaania testowanego makra.
Na czerwono zostay oznaczone linie ko-
nieczne do ustawienia standardowego wyjcia Errata
bdu. W czasie ponaddwuletniego prowadzenia
Rysunek 73 pokazuje dane wysane przez kursu ustrzegem si wszystkich wpadek.
procesor, przechwycone przez program termi- Dziki uwagom Czytelników w listach oraz
nala. na forum udao si znale  trzy bdy wyma-
Mona spotka si z uyciem instrukcji da- Sensowne byoby na przykad spraw- gajce naprostowania. Co ciekawe, wszystkie
lekiego skoku w celu nadania programowi dzanie, czy parametry x oraz y, przekazy- problemy skumuloway si w czci 5 kursu.
pewnych cech wielowtkowoci. Nie jest to wane do funkcji lcd_Pixel, mieszcz si we Pó niej zwykle byy konsekwentnie kontynu-
najelegantsza droga i trzeba wtedy pamita waciwym zakresie. Wykonanie tego zada- owane.
o problemie bdów stosu przy skoku do przo- nia za pomoc makra assert da nam moli- Pojawiy si pewne drobne problemy ze
du – wzmianka o tym pojawia si przy okazji wo testowania ich prawidowoci w cza- stosowaniem makra PSTR. Przy wywoywa-
omówienia funkcji. Istniej pewniejsze i bar- sie uruchamiania i szybkie usunicie testo- niu naszych funkcji pojawiao si ostrzeenie:
dziej eleganckie metody pisania wielowtko- wania w programie bdcym ostateczn warning: passing arg 1 of `LCDstr_P’
wych programów. wersj. discards qualifiers from pointer target type.

42 E l e k t ro n i k a d l a Ws z y s t k i c h
Programowanie
Ostrzeenie to informuje nas, e wysanie ar- W efekcie, jeli wywoujemy delayus8 w p- w trybie omiobitowym tylko dwa razy – we-
gumentu pierwszego do funkcji wymaga po- tli, tylko w pierwszym przebiegu warto dug dokumentacji konieczne jest przesanie
zbycia si kwalifikatora przypisanego do opónienia bdzie prawidowa. Aby temu jej trzykrotnie w czasie inicjacji.
wskanika. W naszym przypadku jest to kwa- zapobiec, mona w ostatniej linii, jako typ pa- Jak wielu Czytelników miao okazj si
lifikator typu const. Do tej pory radzilimy rametru, zamiast „r” poda „+r”, co oznacza przekona, dotychczasowe kody zazwyczaj
sobie z problemem, wykonujc rzutowanie parametr zarówno wejciowy, jak i wyjcio- dziaay dobrze. Po poprawkach powinny
wyniku dziaania makra PSTR. Znacznie lep- wy. W takim przypadku musi znale si on dziaa zgodnie z oczekiwaniami.
szym rozwizaniem jest nieco inne deklaro- za pierwszym dwukropkiem. Ponadto moe- W tym miejscu dzikuj wszystkim za
wanie i definiowanie funkcji piszcej. Odpo- my podstawi tutaj element takiego typu, aby cenne uwagi.
wiednie podejcie pokazuje listing 241. moliwe byo zapisanie do niego wartoci –
Zmiany zaznaczyem kolorem. W tym przy- tak wic nie moemy poda w miejscu t war- Podsumowanie
padku rzutowanie nie bdzie potrzebne. toci staej. Problem ten rozwizuj, na dwa Dzisiejsza cz, bdca skadank kilku
Take w czci 5 pojawio si nasze makro róne sposoby, dwa kolejne listingi. W wik- drobniejszych, ale uytecznych informacji,
generujce opónienie. Dla przypomnienia szoci przypadków taki zapis nie zwiksza zamyka ostatecznie materia zaplanowany na
pokazuj je na listingu 242. iloci generowanego kodu – poprzednio kom- cykl kursu. W czasie trwania kursu zebrao si
W tym przypadku problem polega na tym, pilator automatycznie przypisywa podawan troch informacji, wci przekadanych na na-
e kompilator nie otrzymuje informacji, e za- warto do rejestru. stpny miesic, ze wzgldu na brak miejsca.
warto rejestru ticks zostanie utracona. I po raz trzeci, i ostatni w czci 5, zajli- Teraz materia zosta zamknity.
my si interfejsem popularnego wy- Kurs z krótkiego, w zamierzeniu, cyklu
wietlacza alfanumerycznego. Okaza- przerodzi si w spore kompendium wiedzy.
Listing 241 Prawidowa funkcja piszca o si, e napisana wtedy funkcja ini- Zabierajc si do pisania pierwszej czci, nie
void LCDstr_P( const prog_char* str)
cjacji wywietlacza w tryb czterobito- spodziewaem si przedstawienia takiej iloci
{ wy zawiera bdy, które w niekorzyst- materiau. Du przyjemno sprawia mi tym
(...)
}
nych przypadkach uniemoliwiajce bardziej zainteresowanie Czytelników i fakt,
poprawne uruchomienie wywietlacza. e wród mikroprocesorowych projektów, po-
Listing 242 Nie do koca prawidowa funkcja opónienia
Prawidow funkcj pokazuje li- jawiajcych si na amach EdW, widz take
sting 245. Bdy byy dwa. Powany, projekty pisanie w C.
#define delayus8(t)\ polegajcy na pominiciu faktu, e Sympatyków cyklu ucieszy zapewne wia-
{asm volatile( \
„delayus8_loop%=: \n\t”\ funkcja sendHalf przesya na wyjcie domo, e o ile jest to nasze ostatnie spotka-
„nop \n\t“\ modsz, a nie starsz poówk poda- nie w ramach regularnego kursu C, nie jest
„dec %[ticks] \n\t”\
„brne delayus8_loop%= \n\t”\ nego parametru. W tym przypadku ostatnim ogólnie.
: :[ticks]“r“(t) );}
wystarczy odpowiednie przesunicie
podawanych komend. Reszta funkcji
Listing 243 Poprawione makro opónienia
korzysta z niej prawidowo. Drugim Radosaw Koppel
#define delayus8(t)\ bdem byo wywoanie konfiguracji radoslaw.koppel@elportal.pl
{ \
uint8_t t_ = t; \
asm volatile( \
„delayus8_loop%=: \n\t”\
„nop \n\t”\
„dec %[ticks] \n\t”\
„brne delayus8_loop%= \n\t”\
: [ticks]”+r”(t_): );} R E K L A M A

Listing 244 Realizacja opónienia jako funkcji rozwijalnej


static inline void delayus8(uint8_t t)
{
asm volatile(
„delayus8_loop%=: \n\t”
„nop \n\t”
„dec %[ticks] \n\t”
„brne delayus8_loop%= \n\t”
: [ticks]”+r”(t): );
}

Listing 245 Poprawiona inicjacja wywietlacza


void lcd_init(void)
{
delay100us8(150);
PORT(LCD_RSPORT) &= ~(1<<LCD_RS);
lcd_sendHalf( (LCDC_FUNC|LCDC_FUNC8b) >> 4 );
delay100us8(41);
lcd_sendHalf( (LCDC_FUNC|LCDC_FUNC8b) >> 4 );
delay100us8(2);
lcd_sendHalf( (LCDC_FUNC|LCDC_FUNC8b) >> 44 );
>>
delay100us8(2);
lcd_sendHalf( (LCDC_FUNC|LCDC_FUNC4b) >> 4 );
delay100us8(2);
// Teraz jest ju 4b, koniec sendHalf
lcd_command(LCDC_FUNC|LCDC_FUNC4b|
LCDC_FUNC2L|LCDC_FUNC5x7);
lcd_command(LCDC_ON);
lcd_cls();
lcd_command(LCDC_MODE|LCDC_MODER);
lcd_command(LCDC_ON|LCDC_ONDISPLAY);
}

E l e k t ro n i k a d l a Ws z y s t k i c h 43

You might also like