Professional Documents
Culture Documents
Programowanie procesorów
w języku C część 16
Dziś czeka nas dużo pracy połączonej z dobrą 12 bitów na każdy piksel. Ma to dwie ogrom- Dodajemy teraz prostą funkcję inicjacji pa-
zabawą. Poznaliśmy już pamięć programu ne zalety: możemy dowolnie ustawić, pod lety na podstawie danych z pamięci progra-
procesora oraz prostszą część zagadnienia którym indeksem kryje się jaki kolor oraz da- mu. Pokazuje ją listing 203. Potrzebna nam
umieszczania zmiennych w pamięci danych. je nam możliwość wybrania dowolnych 256 będzie także funkcja czyszczenia wyświetla-
Dziś zajmiemy się zewnętrzną przestrzenią kolorów spośród 4096. Ma także wady: trans- cza – skorzystamy z funkcji memset – iden-
adresową, wspomnimy o działaniu pamięci misja jest wolniejsza niż w trybie 8 bitów, tycznie robiliśmy to już w przypadku po-
EEPROM zawartej w mikrokontrolerze AVR, a sama paleta zajmie dodatkowe 512B pamię- przedniego wyświetlacza.
a na końcu zapoznamy się z interesującym za- ci. Jak się okaże, w świetle naszego pierwsze- Bardziej rozbudowana będzie funkcja od-
gadnieniem dynamicznego przydzielania pa- go programu, zalety przeważają. świeżania wyświetlacza. Przedstawiam ją na
mięci. Przy okazji przedstawię kilka nowych Listing 202 pokazuje dodany bufor wy- listingu 204. W jednym przebiegu pętli prze-
„sztuczek” dotyczących programowania w C świetlacza oraz paletę kolorów. Paleta zostaje syłamy dwa piksele. Jest to związane z tym,
jako takim i GCC w szczególności, jednak ze w pamięci wewnętrznej, będzie przez to ob-
względu na rozmiary kodów przedstawiam sługiwana szybciej. Listing 202 Zmienne w module wyświetlacza
przede wszystkim fragmenty ciekawe lcd_pixel lcd_buffer[LCD_SX*LCD_SY] EXMEM;
uint16_t lcd_rgb[256]; // XXXXRRRRGGGGBBBB
oraz te, które dotyczą bezpośrednio za-
gadnienia. Reszta zostanie omówiona Listing 200 Dodanie sekcji. exram w pliku makefile
ogólnie, tak aby samodzielne ich napi- LDFLAGS += -Wl,--section-start=.exram=0x800500 Listing 203 Inicjacja palety
sanie było możliwe, jednak nie wystar- void lcd_loadRGB_P(const
prog_uint16_t* pRGB, uint16_t size)
czy już tylko przepisanie podanych li- Listing 201 Makro upraszczające dostęp do. exram {
stingów, aby całość zaczęła działać. #define EXMEM __attribute__ ((section (".exram"))) memcpy_P(lcd_rgb, pRGB, size);
}
Pełne kody, jak zwykle, dostępne są w
Elportalu.
E l e k t ro n i k a d l a Ws z y s t k i c h 39
Programowanie
40 E l e k t ro n i k a d l a Ws z y s t k i c h
Programowanie
ABC... GCC
Konfiguracja sekcji w pamięci RAM
oraz dostęp do pamięci zewnętrznej
E l e k t ro n i k a d l a Ws z y s t k i c h 41
Programowanie
co już wskazaliśmy w poprzedniej części – odwołanie się bezpośrednio do bufora wy- W ten sposób przekonaliśmy się, że obsłu-
dwa piksele wymagają trzech bajtów i dane świetlacza. Później znajduje się tablica kolo- ga zewnętrznej pamięci danych przebiega
w nich rozłożone są w taki sposób, że łatwiej rów naszego płomienia. Makro LCD_palRGB praktycznie identycznie jak pamięci wewnętrznej
przesyłać je jako jeden element. Na koniec zamienia poszczególne składowe na 12-bito-
wysyłamy instrukcję NOP, o czym także była wy kolor. Podobne makra mamy już przero- Listing 206 Przed funkcją main()
mowa przy okazji omawiania wyświetlacza. bione. void before_main(void)
To wszystko, czego w tej chwili potrzebu- Na listingu 206 widzimy, co dzieje się __attribute__((naked))
jemy w naszej bibliotece. Do bufora wyświe- przed funkcją main. To umożliwia nam włą- __attribute__((section(„.init3“)));
tlacza „dobierzemy się” bezpośrednio w pro- czenie pamięci zewnętrznej bardzo wcześnie. void before_main(void)
{
gramie. Na listingu 207 przedstawiam zawartość pętli MCUCR = 1<<SRE;
rysowania płomienia. Nie przedstawiam ini- SFIOR = 1<<XMBK | 1<<XMM0 /*A15*/;
„Podpalanie” wyświetlacza cjacji portów, włączenia wyświetlacza oraz // Ustawienie A15 na 0
DDRC = 0x80;
Za pomocą naszej nowej biblioteki stworzy- ładowania palety. PORTC &= ~(1<<7);
}
my program realizujący realistyczną animację Na początku generowanych jest 80 (dobra-
płomienia. Wbrew pozorom, stworzenie reali- no eksperymentalnie) punktów o maksymal-
Listing 207 Zawartość pętli rysowania
stycznie wyglądającego płomienia nie wyma- nej jasności. Pozycja zmienia się tak, aby
ga wielkich mocy obliczeniowych ani żad- punkt zmieścił się w odstępie 2 pikseli od lcd_Update();
nych zdolności obsługi programów graficz- bocznych brzegów wyświetlacza i na wyso- // Podsycanie ognia
uint8_t n;
nych. Udowodnimy to, generując ładną wizu- kości od 2 do 6 pikseli od jego dolnej krawę- for(n=0; n<80; n++)
alizację ognia za pomocą naszego zestawu. dzi. {
// Losowanie pozycji
Nasz płomień będzie korzystał ze specy- Algorytm rozmywania wydaje się oczywi- uint8_t x = rand() % (LCD_SX-4) + 2;
ficznej palety, która poszczególnym tempera- sty. W pierwszej chwili może dziwić dodawa- uint8_t y = LCD_SY – rand() % 5 - 3;
// Wypisanie piksela
turom będzie przypisywać odpowiedni kolor. nie 2 do wskaźnika ppix na koniec zewnętrz- lcd_Pixel(x, y, FIRE_MAX);
Najniższa temperatura będzie miała indeks 0, nej pętli. Jest to związane z tym, że nie obli- }
będzie to kolor czarny. Im wyższa temperatu- czamy wartości pierwszego i ostatniego pik- // Rozmywanie obrazu
ra, tym większe natężenie czerwonego, póź- sela w linii (przypominam, że są one cały czas lcd_pixel *ppix = lcd_buffer+LCD_SX+1;
for(uint8_t y=1; y<LCD_SY-1; y++)
niej rośnie składowa zielona – da to przejście czarne) i musimy je pominąć. {
od czerwieni do koloru żółtego, ostatecznie Dodatkowo, przy rozmywaniu, jeśli nie for(uint8_t x=1;
x<LCD_SX-1; x++, ppix++)
dodajemy składową niebieską – nasz ogień mamy do czynienia z minimalną temperaturą, {
w „najcieplejszym” miejscu będzie miał kolor minimalnie obniżamy temperaturę punktu lcd_pixel temp;
temp = *ppix + *(ppix+LCD_SX+1) +
biały. Ogień jest „podsycany” na spodzie wy- płomienia. Ten element został wprowadzony *(ppix+LCD_SX-1) +
świetlacza. Podsycanie nie jest równomierne, po pierwszych eksperymentach i zaowocował *(ppix+LCD_SX);
if(temp != 0)
tylko rozbłyska i przygasa w sposób z grubsza bardziej postrzępionym szczytem „płomie- temp -=
= 1;
*ppix = temp / 4;
losowy. Jest to pole do wykorzystania oma- nia”. }
wianej w jednej z ramek funkcji rand. Ciepło ppix += 2;
}
płomienia rozchodzi się w górę, przy czym
następuje jego stopniowe rozmycie. Odpo- Listing 205 Zmienne w pliku main.c
wiedni efekt da nam obliczanie koloru pikse- extern lcd_pixel lcd_buffer[];
la zgodnie z rysunkiem 71. Zauważ, że dzię- prog_uint16_t g_fireRGB[] =
ki sprytnej sztuczce z paletą, obliczenia prze- {
LCD_palRGB(0, 0, 0),
prowadzamy niejako na wartościach tempera- ...
tury, a nie na poszczególnych składowych ko- LCD_palRGB(15, 0, 0),
...
loru. Co więcej, jeśli ograniczymy ilość kolo- LCD_palRGB(15, 15, 0),
rów do 64, obliczenia będzie można wykony- ...
LCD_palRGB(15, 15, 15),
wać na liczbach ośmiobitowych – to bardzo };
przyspiesza działanie programu. Dzielenie
przez 4 także jest bardzo wygodne – kompila-
tor zoptymalizuje ją na przesunięcie logiczne. ABC... GCC Fot. 11 Płomień na wyświetlaczu
Dla uproszczenia obliczeń pozostawiamy Maksymalny rozmiar
z każdej strony generowanego obrazka 1 pik- obsługiwanej zmiennej
ABC... GCC
sel czarny – tutaj nie rysujemy, tylko korzy-
stamy z tych pikseli przy obliczeniach. Przy tworzeniu zmiennych trzeba pilnować, aby nie Pętla do-while (0)
Listing 205 pokazuje początek programu przekroczyć maksymalnego rozmiaru zmiennej, jaki – alternatywa dla goto
AVR-GCC jest w tanie obsłużyć. Maksymalny rozmiar
w pliku main. Pierwsza linia umożliwia nam pojedynczej zmiennej wynosi 32767B. Ograniczenie to Część wczytująca dane przesyłane przez komputer zo-
wynika z przyjętego typu zmiennej wskazującej rozmiar stała umieszczona we wnętrzu pętli do-while. Jednak jej
size_t. Jest to liczba 16-bitowa ze znakiem. Mimo tego, warunek został ustawiony na 0. Jaki to ma sens? Ciało
Rys. 71 Zasada rozmywania płomienia
że rozmiar nie może być ujemny, niektóre funkcje zwra- takiej funkcji będzie zawsze wykonane tylko raz. Jednak
cają wartość ujemną, aby zaznaczyć wystąpienie błędu. zyskujemy jedną znakomitą możliwość: możemy w do-
Zaznaczam, że ograniczenie dotyczy rozmiaru pojedyn- wolnym momencie wyskoczyć z ciała pętli (jeśli, na
czej zmiennej. W programie mogą pojawić się, na przy- przykład, komputer nie przesłał żądania programowa-
kład, dwie tablice o rozmiarze 30kB każda i zostaną one nia) bez użycia instrukcji goto (patrz część 11). Po pro-
prawidłowo obsłużone (jeśli tylko mamy fizycznie od- stu użyjemy instrukcji break. Ta ciekawa sztuczka jest
powiednią ilość pamięci). często stosowana i warto o niej pamiętać. Ostatecznie,
W pliku <stdint.h> znajduje się definicja stałej często tak napisany kod jest bardziej przejrzysty.
SIZE_MAX oznaczającej maksymalny rozmiar poje- Listing nie jest prezentowany w artykule. Kod do-
dynczej zmiennej. stępny na stronie Elportalu.
42 E l e k t ro n i k a d l a Ws z y s t k i c h
– ze względu na architekturę AVR miejsce Pamięć EEPROM – dodaje- danymi, zostaną one umieszczone w pliku
umieszczenia zmiennej od strony kodu nie ma my możliwość konfiguracji .eep. Modyfikację pokazuje listing 209.
znaczenia. Zmienia się jedynie czas dostępu. Nasz płomień wygląda bardzo efektownie, ale Do funkcji obsługujących wyświetlacz do-
Uzyskany efekt wygląda naprawdę bardzo fajnie byłoby mieć możliwość jego dowolnej damy funkcję umożliwiającą wczytanie no-
atrakcyjnie, o czym przekonuje częściowo fo- konfiguracji bez konieczności każdorazowe- wej palety – patrz listing 210. Porównaj wi-
tografia 11. Jednak ruchoma animacja spra- go kompilowania programu. Pomoże nam doczny tutaj kod z listingiem 203. Sposoby
wia znacznie lepsze wrażenie. w tym nieulotna pamięć danych typu obsługi pamięci programu i EEPROM są bar-
EEPROM. Przejrzyj mówiącą o niej ramkę... dzo podobne.
i zabieramy się do pracy. W celu obsługi transmisji użyjemy modu-
ABC... GCC Idea, od strony programowania, będzie jak łu rs, który pojawił się po raz pierwszy już
Dostęp do pamięci EEPROM najprostsza: jeśli przy uruchamianiu układu w części 8. Dalej nie będę pisał o inicjacji
przytrzymamy którykolwiek przycisk, proce- portu UART ani dokładnie omawiał sposobu
Z punktu widzenia AVR-GCC obszar pamięci EEPROM sor będzie czekał na transmisję. Odbywa się transmisji – wszystko to już znamy. Skupiam
jest specjalną sekcją. Aby zaznaczyć, że mamy do czy- ona z prędkością 1200 bodów. Komputer mu- się natomiast na obsłudze pamięci EEPROM.
nienia z kolejnym obszarem adresowym, do adresu sek-
cji umieszczonej w tej pamięci dodajemy przesunięcie
si przesłać ciąg „fire”, na co program odpowie Ważny dla nas fragment pokazuje listing
0x810000. Zmienną możemy umieścić w obszarze pa- potwierdzeniem „@”. Teraz przesyłamy in- 211. Przy korzystaniu z biblioteki
mięci EEPROM jak niżej: formację o ilości danych do nowej palety i za- <avr/eeprom.h> dostęp do pamięci EEPROM
int zmienna __attribute__ raz po niej 16-bitowe wartości palety. Ilość jest dość intuicyjny. Na łamach Elporatalu
( (section ('.eeprom') ))) = 12; ; danych i same dane przesyłane są prosto – znajdzie się nie tylko pełny kod programu, ale
albo prościej, korzystając z makra znajdującego się
w pliku <avr/eeprom.h>:
w postaci binarnej. Jednocześnie są one odsy- także program służący do tworzenia i przesy-
int zmienna EEMEM = 12; ; łane w celu kontroli. łania palety płomienia z poziomu komputera
Przykładowa wartość 12 zostanie zapisana w odpo- Pierwsze, co zmienimy, to zamiast tablicy PC.
wiednim miejscu pliku eep. Jeśli nie nadamy zmiennej umieszczonej w pamięci programu, stworzy- Gdyby podczas działania programu poja-
wartości, będzie ona domyślnie wyzerowana. my specjalną strukturę w pamięci EEPROM. wiły się problemy z komunikacją, pomóc mo-
Przy obszarze pamięci EEPROM natkniemy się na
podobny problem, jaki mieliśmy przy umieszczaniu ta-
Będzie ona zawierała dane palety oraz maksy- że przełączenie procesora na zewnętrzny
blic w pamięci programu. Zmienne takie obsługujemy za malną temperaturę płomienia (najwyższy in- kwarc 8MHz. W czasie eksperymentów z we-
pomocą specjalnych funkcji. Proste funkcje umożliwia- deks palety). Jeśli strukturę tę zainicjujemy wnętrznym generatorem RC problemów ta-
jące obsługę obszaru EEPROM deklarowane są w pliku kich nie stwierdzono.
<avr/eeprom.h>. Wszystkie dostępne tutaj funkcje dzia-
Listing 208 Zarodek zmiennej pseudolosowej
łające na pamięci EEPROM przed swoim wykonaniem
Pamięć przydzielana
dynamicznie
czekają na zakończenie poprzedniej operacji zapisu. uint16_t *pmem;
Może to zatrzymać wykonywanie programu na około uint16_t seed = 0;
8,5ms.
uint16_t n; Odstawiamy już naszą animację płomienia.
pmem = (uint16_t*)0;
eeprom_is_ready() – zwraca 0, jeśli pamięć EEPROM for(n=0; n<(RAMEND+1)/2; n++) Można oczywiście nad nią pracować i uzy-
jest zajęta, { skać jeszcze ciekawszy efekt. Jednak wyczer-
eeprom_busy_wait() – czeka, aż pamięć EEPROM za- seed ^= *pmem++;
} paliśmy możliwości dydaktyczne tego przy-
kończy zapis, srand(seed);
eeprom_read_byte (addr), eeprom_read_word (addr) – kładu. Zajmiemy się teraz bardzo ciekawym
funkcje zwracają bajt oraz słowo dostępne pod podanym zagadnieniem. Stworzymy prostą przeglądar-
adresem w pamięci EEPROM, Listing 209 Dane w pamięci EEPROM kę obrazków. Obrazki będziemy przesyłać
eeprom_read_block (ram, eeprom, n) – odczytuje n baj- z komputera, i zakładamy, że pamięć o nich
struct
tów z pamięci EEPROM i zapisuje je w buforze w pa- { ma być zachowana tylko do chwili wyłącze-
mięci RAM, uint8_t max_power;
eeprom_write_byte (addr, value), eeprom_write_word uint16_t fireRGB[64]; nia zasilania. Co jednak dla nas istotne, każdy
(ddr, value) – funkcje zapisują bajt oraz słowo pod poda-
}eeprom EEMEM = obrazek będzie mógł mieć inny rozmiar.
{
ny adres w pamięci EEPROM, 45, Oznacza to, że do naszego układu możemy
eeprom_write_block (ram, eeprom, n) – zapisuje n baj- { przesłać, na przykład, 3 obrazki o rozmiarze
tów z pamięci RAM do pamięci EEPROM. LCD_palRGB(0, 0, 0),
... całego wyświetlacza albo 10 o rozmiarze
}
Struktury w pamięci EEPROM };
50x50 każdy. Jak sobie z tym poradzić?
a pojedyncze zmienne W tym przypadku wspomoże
Dobrym zwyczajem jest tworzenie w pamięci EEPROM Listing 210 Wczytanie palety z pamięci EEPROM
nas wbudowana w AVR-GCC
tylko jednej struktury zawierającej wszystkie dane. Jeśli możliwość dynamicznej aloka-
umieścimy w pamięci pojedyncze zmienne, nie mamy void lcd_loadRGB_EE(const uint16_t* peeRGB, uint16_t size)
cji pamięci. Wszystko, co ko-
{
pewności, czy zostaną one rozmieszczone w kolejności
eeprom_read_block(lcd_rgb, peeRGB, size); nieczne, opisują dwie kolejne
zgodnej z kolejnością ich definicji. Co więcej, kolejna }
wersja WinAVR może rozłożyć je w inny sposób. Staje
ramki. Tutaj od razu zabieramy
się to ważne, jeśli stworzymy nowszą wersję oprogramo- się do działania.
wania i zechcemy umożliwić jego wgranie bez kasowa- Listing 211 Zapis konfiguracji do pamięci EEPROM
nia nastaw zawartych w pamięci EEPROM. Jeśli w pa-
uint8_t count; Program „przeglądarka”
mięci EEPROM znajdzie się tylko jedna struktura, count = rs_get(); Nasz nowy program będzie działał z wy-
mamy pewność, że rozmieszczenie danych się nie zmie- eeprom_write_byte(&eeprom.max_power, count-1);
ni. Jeśli pojawi się konieczność dodania nowych opcji – rs_put(count); świetlaczem w trybie ośmiobitowym.
dopisujemy je na końcu struktury, tak aby nie kolidowa- W module wyświetlacza rezygnujemy
// Pobieranie danych
ły ze starymi ustawieniami: uint8_t n; więc ze zmiennej zawierającej paletę. Ko-
struct for(n=0; n<count; n++)
{
nieczne jest jednak dodanie kilku linii na
{ końcu procedury inicjacji naszego modu-
uint16_t rgb = (uint16_t)rs_get()<<8;
int dana1= =0,, dana2= =5;; rs_put(rgb>>8);
... rgb |= rs_get();
łu. Pokazuje je listing 212. Uprości się
}eeprom EEMEM; ; eeprom_write_word(&eeprom.fireRGB[n], rgb); znacznie funkcja odmalowywania wy-
rs_put((uint8_t)rgb);
Przykłady: listing 209 do 211. }
świetlacza – wystarczy teraz wysłać za-
wartość bufora (listingu nie pokazuję).
E l e k t ro n i k a d l a Ws z y s t k i c h 43
Programowanie
Pracę ułatwi nam funkcja lcd_Draw, która ry- cza alfanumerycznego. Funkcja system_run tu bez jakichkolwiek zmian.
suje na podanym miejscu ekranu obrazek została zmodyfikowana, aby konfigurować 3. Moduł rs. Rozbudowany, aby poprawić
o podanym rozmiarze, czerpiąc jego dane odpowiednio aktualny sprzęt. Dodano tutaj bezpieczeństwo działania funkcji rs_get.
z pamięci RAM. Funkcja jest prosta w imple- obsługę zdarzeń portu UART, zgodnie z ram- W ten sposób jesteśmy gotowi do pisania
mentacji, tak więc i tym razem listing nie po- ką w lewym dolnym rogu strony 46. w programie nowego modułu zajmującego się
jawia się na łamach kursu. 2. Moduł kbd z części 11. Dodany do projek- dynamiczną obsługa obrazków.
W module wyświetlacza to koniec zmian.
Nasz nowy program składał się będzie jednak
ABC... C
z większej liczby modułów, które napisaliśmy void*calloc (size_t n, size_t size)
już w czasie trwania kursu: Dynamiczne przydzielanie pamięci Zajmuje blok pamięci na n obiektów, każdy o rozmiarze
size. Inicjuje pamięć zerami. Zwraca wskaźnik na blok pa-
1. Moduł system z części 11. Usunięte zostały Specyfikacja ANSI-C określa zestaw funkcji do obsługi mięci albo NULL, jeśli polecenie nie może być wykonane.
z niego jedynie funkcje dotyczące wyświetla- pamięci przydzielanej dynamicznie. Dla wszystkich funk- void*malloc (size_t size)
cji działających na wskaźnikach, wskaźnik do obszaru Funkcja zajmuje blok pamięci o rozmiarze size. Nie ini-
przydzielonego dynamicznie powinien być widziany i ob- cjuje go żadną wartością. Zwracane wartości jak w calloc.
sługiwany tak samo jak wskaźnik na zwyczajną zmienną. void*realloc (void *p, size_t size)
Listing 212 Inicjacja trybu 256 kolorów LCD O tym, że w AVR-GCC rzeczywiście tak jest, mogliśmy Umożliwia zmianę rozmiaru (zarówno w górę, jak
lcd_Command(LCD_COLMOD); przekonać się przy okazji części 8 gdy stosowaliśmy sztucz- i w dół) bloku pamięci przydzielonego wcześniej. Zwraca
lcd_Data(LCD_COL8bpp); kę aby utworzyć w sposób „statyczny” zmienną FILE. wskaźnik na nowy blok danych. Zależnie od implementa-
// Paleta dla trybu 8bpp Działanie cji może być równy p albo może wskazywać inny blok.
lcd_Command(LCD_RGBSET);
// R W języku C pamięć dynamiczna umieszczona jest na Tak czy inaczej dane zawarte w p znajdują się w obszarze
lcd_Data(0x00); // 0 „stercie”. Sterta ta rośnie przy zajmowaniu kolejnych blo- wskazywanym przez zwracaną wartość. Jeśli rozmiar blo-
lcd_Data(0x02); // 1 ków pamięci. To czy sterta ma możliwość zmniejszania ku jest zwiększany, dodany obszar nie jest inicjowany –
lcd_Data(0x04); // 2
lcd_Data(0x06); // 3
swojej objętości w przypadku zwalniania pamięci zależy zawiera przypadkowe dane.
lcd_Data(0x08); // 4 od implementacji. Tak samo jak problem fragmentacji pa- Funkcja może zwrócić NULL. Oznacza to, że zmiana
lcd_Data(0x0A); // 5 mięci. Można go uniknąć w dużych komputerach z MMU rozmiaru nie może być wykonana. Może być to spowodo-
lcd_Data(0x0C); // 6 (jednostkami zarządzania pamięcią). Jednak jak wyniknie wane przykładowo brakiem pamięci. W takim przypadku
lcd_Data(0x0F); // 7
// G z następnej ramki, w przypadku prościutkiego AVR’a mu- zawartość bloku p nie ulega zmianie.
Identycznie jak dla R simy o tym pamiętać. free (void* p)
// B Obsługa Zwalnia pamięć przydzieloną dynamicznie. Parametr p
lcd_Data(0x00); // 0
lcd_Data(0x03); // 1 Do obsługi pamięci dynamicznej służą cztery funkcje musi być wskaźnikiem zwróconym przez calloc albo mal-
lcd_Data(0x07); // 2 z biblioteki standardowej – konieczne jest dołączenie loc. W innym przypadku zachowanie funkcji jest nieokre-
lcd_Data(0x0F); // 3
pliku <stdlib.h>. ślone. Nie zwraca wartości.
44 E l e k t ro n i k a d l a Ws z y s t k i c h
Moduł imglist ment. To ciekawa konstrukcja. Jednak w na-
Listing 218 Dodawanie elementu do listy
Nasze dynamiczne obrazki będą obsługiwane szym przypadku zwracanie właśnie wskaźni-
przez dodatkowy moduł nazwany imglist. ka na wskaźnik daje ogromne zalety: Zobacz, int imgl_AddImg(uint8_t sx, uint8_t sy)
{
Idea jest podobna, jak przedstawiona w ramce jak intuicyjna staje się funkcja dodawania ele- IMGLIST_DATA *pdata=NULL;
o dynamicznym przydzielaniu pamięci lista mentu do listy, widoczna na listingu 218. Sta- IMGLIST_DATA **pplast;
int index;
wolnych bloków. Spójrz na listing 215. Za- ła IMGL_ACCESLAST_INDEX ma po pro- // Sprawdzenie rozmiaru
if(sx == 0 || sy == 0)
warta tutaj struktura zawiera wskaźnik na na- stu bardzo dużą wartość. Sprawia to, że funk- return -2;
stępną strukturę opisującą kolejny obrazek. cja przeszukująca zatrzyma się na ostatnim // Zajmowanie pamięci
pdata = malloc(
W ostatnim obrazku pole to jest ustawione na elemencie tablicy. Zwrócony przez nią wskaź- (uint16_t)sx*(uint16_t)sy
NULL, co oznacza koniec danych. nik wskazuje albo na nasz wskaźnik początku + sizeof(IMGLIST_DATA));
if(pdata == NULL)
W strukturze tej korzystamy z tablicy nie- tablicy (jeśli brak w niej elementów), albo na return -1;
kompletnej, o której mowa w odpowiedniej pole pnext ostatniego obrazka. Niezależnie od // Ustawienie zmiennych struktury
pdata->
>pnext = NULL;
ramce. tego, z którym przypadkiem mamy do czynie- pdata->
>sx = sx;
Jedyną zmienną zdeklarowaną na stałe nia, dopisanie nowego elementu odbywa się pdata->
>sy = sy;
// Pobieram wskaźnik koniec
w module, jest wskaźnik początku listy w identyczny sposób. pplast =
(pierwszego obrazka), widoczny na listingu imgl_Access(
Listing 217 Przeszukiwanie listy IMGL_ACCESLAST_INDEX, &index);
216. *pplast = pdata;
Sercem modułu jest, widoczna na listingu static IMGLIST_DATA** imgl_Access
return index;
(int index, int* pindex)
217, funkcja przeszukująca listę i zwracająca { }
wskaźnik do wskaźnika na znaleziony ele- int n=0;
// Wskaźnik na aktualny obiekt
IMGLIST_DATA **ppdata = &imgl_pStart; Listing 219 Usuwanie elementu z listy
E l e k t ro n i k a d l a Ws z y s t k i c h 45
Programowanie
R E K L A M A
46 E l e k t ro n i k a d l a Ws z y s t k i c h