Programowanie
Programowanie
Rys. 26 przebieg obliczeń w funkcji LCDsendHalf.
W C parametry do funkcji przekazywane są przez wartość. Oznacza to, że wpisanie wartości do zmiennej bądąccj parametrem, nie będzie miało wpływu na zmienną w miejscu wywołania. Doskonale obrazuje to listing 24. Gdyby wewnątrz funkcji LCDsend wartość w zmiennej data została zmieniona przez funkcję LCDsendHalf, wysłanie młodszej części komendy nie przebiegałoby prawidłowo.
Zasadę działania funkcji LCDsendHalf najlepiej przedstawi rysunek 26. Interesujące informacje znajdziesz także w ramce ABC... C omawiającej optymalizację przy dostępie do portów oraz zmiennych.
Funkcja LCDsend zajmuje się przesłaniem dalej starszej połowy a następnie młodszej wysyłanej danej/instrukcji. Przed zakończeniem oczekiwane jest 120|xs. Jest to czas wykonywania większości komend. Jednocześnie nie istnieją komendy, które wykonywałyby się szybciej.
Kolejne potrzebne nam funkcje przedstawia listing 25. Zgodnie z dokumentacją wysłanie komendy od wysłania danej różni się wyzerowaniem lub ustawieniem linii RS wyświetlacza. W najprostszej wersji program właściwy może korzystać już z przedstawionych dwóch funkcji. Dla wygody dodane zostały oddzielne funkcje powodujące czyszczenie wyświetlacza oraz powrót kursora na pozycję zerową. Są to jedyne dwie komendy wymagające dłuższego niż 120ps czasu oczekiwania.
D7 D6 ds D4 Dane wyświetlacza
Na łisitngu 26 znajduje się funkcja inicjująca wyświetlacz do pracy. Jest to bezpośrednie przeniesienie na C grafu znajdującego się w „F.lektronice dla Wszystkich” 1/98. W tym miejscu po raz ostatni używana jest instrukcja wysyłająca tylko połowę danych, co jest konieczne do czasu, aż interfejs nie zostanie ustawiony w tryb czterobitowy.
Zauważ, jak wygodne jest wydawanie komend przy korzystaniu ze zdefiniowanych wcześniej stałych. Ostatnia linia inicjacji może być w praktyce usunięta lub zmieniona na przykład tak, aby kursor pozostawał niewidoczny.
Listing 25 LCD: wysyian
port(lcd_rsport) 6
LCDsend(command); > |
Listing 26 LCD: inicjacja wyświetlacza | |
void LCDdata(uint8_t data) |
void LCDinit(void) | |
PORT (LCD RS PORT) |= 1<<LCD RSJ |
delaylQOus8(150); | |
LCDsend(data); |
PORT (LCD RSPORT) &= ~(1«LCC RS); | |
LCDsendHalf(LCDC FUNC|XCDC FDNCSb)J | ||
void LCDcls(void) |
delayl00us8(41); LCDsendHalf(LCDC FUNC|LCDC FUNCSb); |
Iayl00us8(2); LCDsendHalf(LCDC_F delayl00us8(2);
// Teraz jest już
void LCDhome(VOid)
LCDcommand(LCDC_ON); LCDclsO i
LCDcommand(LCDC_MODE|
ABC... C
optymalizacja w dostępie do zmiennych i w dostępie do portów
Przyjrzyjmy sięjeszcze raz listingowi 24. Spróbuj zamienić zapis:
data = (data & OxOF) « LCD_D4; na odpowiednik zapisany w oddzielnych działa-
data &= OxOF; data «= LCD_D4 J Jeśli teraz skompilujesz program, okaże się, że nie zmieni! on swojego rozmiaru. Jak to jest możliwe? Pamiętaj, że zmienne w czasie obliczeń (za wyjątkiem volatile) przechowywane są w rejestrach. Zmienne lokalne w zasadzie zupełnie poza rejestrami nie istnieją! Tak więc, rozpisując kod, zrobiliśmy dokładnie to samo,
co wcześniej zrobił kompilator. Pójdźmy jednak
data |= OxFO; data &= OxOF; data «= LCD_D4;
Dodaliśmy linię, tworzącą nadmiarowe obliczenia, które jednak nie mogą wpłynąć na wynik (ustawiamy bity, które zaraz zostaną wyzerowane). Po kompilacji znowu przekonasz się, że rozmiar kodu nie uległ zmianie! Kompilator zauważa, że pierwsza instrukcja nigdy nie zmieni wyniku i pomija ją. Okazuje się więc, żc przeprowadzając obliczenia za P0F pomocą zwyczajnych zmiennych, mamy dość dużą swobodę zapisu obliczeń. Kompilator zwykle postara się, aby identyczny wynik uzyskać najprostszą drogą.
Może więc domyślasz się, co się stanie, jeśli pozostawimy samą linię:
data «= LCD_D4;
Odpowiedź znajdziesz na rysunku 26. Inaczej sprawa ma się z portami. Tutaj kompilator nie może założyć, że ważny jest dla nas tylko końcowy wynik. W (akim przypadku niemożliwe stałoby się generowanie sekwencji na portach, ponieważ kompilator wypisałby tylko ostatnią z wartości. Pod względem dostępu porty można traktować w zasadzie jako zmienną volati!e. Tak więc zamiana naszego zapisu do portu na pojedyncze działania: (lcd_dport) &= ~(0x0f«lcd_d4); (lcd_dport) |= data;
spowoduje zwiększenie rozmiaru wygenerowanego kodu. Na wyj ściach procesora rzeczywiście najpierw wyprowadzenia D4-D7 zostaną wyzerowane, a następnie pojawi się na nich właściwa wartość.
Elektronika dla Wszystkich Październik 2005 39