Programowanie
W ten oto sposób kończymy prostą obsługę wyświetlacza. Napisane przez nas funkcje nic dają w tej chwili ogromnych możliwości, jednak działają. Zajmują przy tym stosunkowo niewiele zasobów. Stanowią także świetną bazę do dalszego rozwoju. Ważne jest, że za sobą mamy już część sprawiającą często największy problem — sprawiliśmy, że wyświetlacz zaczął się „odzywać”.
Na koniec umieszczam obiecaną ramkę na temat jednego ze sposobów tworzenia wstawek asemblerowych. Ramka ta zamyka w pewien sposób jedną całą część cyklu. Mam nadzieję, żc do tej pory oswoiłem Cię ze składnią C, pewnymi specyficznymi elementami dotyczącymi GCC oraz programowania mikrokontro-
Icrów w tym języku. Jeśli to właśnie razem udało nam się osiągnąć - to znakomicie.
Jeśli zainteresowanie będzie wystarczająco duże, dalsze części kursu będą kontynuowane już na nowej płytce prototypowej, która da nam większe możliwości. Coraz większy nacisk kładziony będzie na konkretne algorytmy, obsługę ciekawych elementów, że wspomnę, dla zaostrzenia apetytu, o działającym już na moim stole wyświetlaczu kolorowym z NOKU 3510i.
Osoby, którym zależy przede wszystkim na informacjach o C jako takim, a nie na algorytmach, zainteresuje zapewne wiadomość, że informacji o tym języku zostało jeszcze bardzo dużo i nieznacznej zmianie ulegnie tylko sposób ich przedstawiania.
Wiem już dziś, że pierwsza część kursu została pozytywnie oceniona, chciałbym poznać Twoje zdanie na temat całości przedstawionego do tej pory materiału. To czy kurs będzie kontynuowany, jaką przyjmie dalej formę, zależeć może od Ciebie. Opinię, tak pozytywną jak i negatywną, uwagi o czym chciałbyś przeczytać, a co Cię nie zainteresowało, możesz przesłać na adres redakcyjny edw@edw.com.pl. Na forum Elportalu, w dziale „Uwagi na temat bieżącego numeru”, zostanie utworzony także odpowiedni temat.
Radosław Koppel
radon law. koppel@.cdW. com.p
Łańcuchy w pamięci programu
Pamiętasz może, jak w części 3 pisałem o wykorzystaniu pamięci programu? Zauważyliśmy wtedy, że niektóre zc stałych zajmują zarówno pamięć danych, jak i pamięć programu. Dokładnie ten sam problem dotyczy napisów. Przy korzystaniu z napisów tak jak miało to miejsce do tej pory zawsze przed jego użyciem w pamięci RAM musi zostać utworzona zmienna tymczasowa, do której, przed wykorzystaniem, skopiowane zostaną dane z pamięci ROM. Jak łatwo się domyślić, jest to rozwiązanie nieekonomiczne. Spróbujmy, tak jak robiliśmy to poprzednio, dostać się bezpośrednio do pamięci programu.
Od strony funkcji, która ma napis bezpośrednio z pamięci przyjąć, będziemy go odczytywać w podobny sposób, jak dostawaliśmy się do tablic przy pierwszym spotkaniu z pamięcią programu: wykorzystamy makro pgm_read_byte. Jedyną nowością będzie więc stworzenie stosownego napisu.
Tytułowe makro jest zdefiniowane w pliku <civr\pgmspace.h>. Tworzy ono napis w pamięci programu i zwraca wskaźnik do niego. Zapobiega umieszczeniu kopii napisu w pamięci programu. Jeśli mamy już stworzoną funkcję, która działa na napisie bezpośrednio z pamięci programu, jej wywołanie może przebiegać jak niżej: LCDscr_p(PSTR(‘‘Witaj ! ”)) ;
Jeśli nasza funkcja zostanie już przystosowana do operowania na pamięci programu, pominięcie makra PSTR jest błędem.
Uwaga: pamiętaj, że jeśli inicjujesz zmienną łańcuchową umieszczoną w pamięci programu, użycie makra PSTR jest błędem! Jedyny sens jego wykorzystania pokazany został na wcześniejszym przykładzie. Łańcuchy w pamięci programu obsługujemy tak samo, jak obsługiwaliśmy tablice:
prog_char g_napispgm[] =
"Łańcuch z FLASHa"; //globalna!
LCDstr_P(g_napispgm);
Zgodnie z tym, o czym wspominałem w części 3, funkcje manipulujące łańcuchami mają swoje specjalne wersje zdeklarowane w nagłówku <avrpgmspace.h>. Znajdujące się tam funkcje mają przyrostek _P. Działają przy założeniu, że element źródłowy umieszczono w pamięci programu. Oczywiście dotyczy to tych funkcji, dla których ma to sens, przykładowo: slrcpy_P - kopiuje z pamięci programu do pamięci danych, strcmp_P - porównuje napis z pamięci danych z napisem z pamięci programu... Nie istnieją jednak funkcje takie jak strlwr_P czy strrev_P.
ABC... C Inlino asembler
Na samym początku zaznaczam, że sposób pisania wstawek asemblerowych każdy producent kompilatora rozwiązuje we własnym zakresie. Oznacza to. że sposób ich tworzenia w innym niż AVR-GCC kompilatorze będzie przebiega! w inny sposób.
Przedstawione tutaj informacje z konieczności nie wyczerpują tematu. Nie opisuję tutaj także asemblera jako takiego, a przedstawiam jedynie informację, jak korzystać z niego w naszym kompilatorze. Jeśli będziesz potrzebował więcej wiedzy na przedstawiony temat - odsyłam Cię do dokumentacji WinAVR.
Ogólna składnia do stworzenia wstawki asemblerowej jest następująca: asm(“kod”
:wyj ści e twejście
[:tracone rejestry]);
gdzie:
„kod” oznacza asemblerowy kod wstawki.
Piszemy go w formie łańcucha znaków. Tekst ten zostanie następnie przekazany do kompilatora asemblera.
wyjście - parametry wyjściowe. To pojęcie może być trochę mylące. Oznacza ono, że kompilator po wykonaniu programu skopiuje dane z wskazanych tutaj miejsc do wybranych zmiennych. Jeśli jednak chcemy we wstawce pisać przykładowo do portu - port ten nie będzie parametrem wyjściowym! Podany adres portu należy uznać za parametr wejściowy, wejście lista parametrów wejściowych. Jeśli to konieczne, kompilator skopiuje przed wykonaniem programu odpowiednie dane do podanych miejsc.
tracone rejestry - część, która zwykle może być pominięta. Tutaj informujemy kompilator, jakie dodatkowe rejestry traci nasza wstawka.
Najlepiej będzie od razu wykorzystać przykład zaczerpnięty z dokumentacji:
char a;
asm(“i n %0, %1"
:"i”C_sfr_io_addr(port3)));
Zadaniem tego prostego programu jest wczytanie danej z portu B oraz jej umieszczenie w zmiennej a. Na przykładzie tego programu widzimy składnię parametrów wejściowych oraz wyjściowych. Między znakami cudzysłowii umieszczamy informację, gdzie znajdzie się odpowiednia dana. W naszym przypadku „r” oznacza rejestr. Umieszczony przed nim modyfikator „=” oznacza, żc do podanego parametru będziemy jedynie pisać - nie będziemy odczytywać z niego wartości. Modyfikatora tego używa się prawie zawsze dla parametrów wyjściowych. „I” oznacza stałą liczbę w zakresie 0-63. Następnie w nawiasie nadajemy parametrom odpowiednią wartość. Nasz zapis oznacza, że wybrany rejestr ma być po zakończeniu wstawki skopiowany do zmiennej u - w praktyce, ponieważ zmienna a jest zmienną lokalną - po prostu w wstawce zostanie wykorzystany ten sam rejestr, który przechowuje naszą zmienną. Naszej stałej nadajemy wartość adresu, odpowiedniego portu w :"=r"(a) przestrzeni TO. Ponieważ PORTB jest tłumaczony na adres w przestrzeni adreso-
Elektronika dla Wszystkich Październik2005 47