ABC... C
ttdefine I wiele innych linii
Wspominałem o tym w części 3. Przypominam, że komenda ifdefine nie jest instrukcją języka C. Dotyczą jej inne zasady - tutaj format linii ma znaczenie. Definicja kończy się zawsze znakiem końca linii. Tam, gdzie pojedyncza linia musiałaby być nieznośnie długa, możemy wprowadzić ukośnik „V* i poniżej kontynuować nasze makro.
Pilnuj, aby bezpośrednio za znakiem ukośniku pojawił się znak nowej linii. Spacja znajdująca się na końcu linii, którą chcemy kontynuować w linii następnej. spowoduje wygenerowanie błędu przez kompilator. Można zauważyć, że w takim przypadku nawet nasz edytor nie będzie odpowiednio kolorował składni. >
{
Parametry takie nic będą miały wpływu na wynik sumy, co oznacza, że można by ich po prostu nie używać. Jedynym celem ich wprowadzenia jest z jednej strony zwiększenie czytelności kodu, z drugiej uproszczenie sterowania. Dzięki temu dokładnie widzimy, jakie możliwości daje każda z komend. Nie trzeba zastanawiać się przykładowo, czy usunięcie danego parametru wywoła działanie odwrotne, czy brak jakiegokolwiek działania
Listing 22 LCD: komendy sterujące
// Komendy sterujące wyświetlaczem Adefine LCOC.CLS 0x01
Adefine lcdc mddfr 0x02
Adefine lcdc_0n 0x08
Adefine lcdc_0NBL3NK 0x01
Adefine lcdc_shtftr 0x04
Do prawidłowego wysterowania wyświetlacza potrzebne są nam procedury umożliwiające generowanie opóźnień od pojedynczych cykli maszynowych do kilkunastu milisekund Do tej pory pokazałem Ci dwa sposoby podejścia do problemu funkcji opóźnień. Jedną z możliwości jest wykorzystanie funkcji udostępnianych przez nagłówek <avr>delay.h>, drugą wykorzystanie systemu przerwań. Teraz podejdziemy do sprawy jeszcze inaczej: napiszemy własne procedury opóźnienia za pomocą asemblera. Jest to chyba jedyna opcja, która umożliwia stworzenie opóźnienia rzędu jednego cyklu.
Potrzebne nam funkcje opóźnień pokazuje listing 23. Moją zasadą przy tworzeniu nazw takich funkcji jest podanie jednostkowego opóźnienia oraz, gdy czas opóźnienia można wybrać za pomocą podania odpowiedniego parametru - podaję ilość bitów, jaką ma wspomniany parametr. Tak więc funkcję delayW0us8 należy czytać jako umożliwiającą generowanie opóźnienia równego t* lOOps, gdzie t jest wartością ośmiobitową. Tak więc maksymalne generowane opóźnienie to 25,6ms (dla t = 0).
Myślę, że w przedstawionych funkcjach nie ma nic tajemniczego dla osób obeznanych z asemblerem AVR-ów. Dokładniejsze wyjaśnienie tematu wstawek asemblerowych pojawi się pod koniec artykułu. Teraz do pchlego wyjaśnienia tej idei brakuje mi opisu obsługi napisów w C. Tymczasowo napisane makia oraz jedną funkcję potraktujmy jak czarną skrzynkę, która po prostu działa.
Nazwą taką określone zostały przeze mnie funkcje działające bezpośrednio na portach wyświetlacza.
Są one tworzone w zasadzie tylko po to. aby kolejne,,.wyższe” funkcje obsługi wyświetlacza mogły z nich korzystać. Właściwy program nie powinien się do nich odwoływać.
Przyjrzyj się listingowi 24. Jeśli masz dostęp do dokumentacji wyświetlacza LCD ze sterownikiem HD 44780, możesz zauważyć, że tworząc pierwsze makro oraz kolejne funkcje, po prostu przekładamy na opis w C kolejne informacje z dokumentacji. Najpierw tworzymy makro, które zajmie się generowaniem impulsu strobującego dostęp do wyświetlacza. W tabelce 3 przytaczam najważniejsze zależności na podstawie artykułu z „Elektroniki dla Wszystkich’ 12/97.
Zauważ, że 1 i 3 z nich zawsze będzie spełniona, jeśli tylko nie będziemy zmieniać stanu wyprowa-
dzeń sterujących oraz strobująccgo jednocześnie. Wynika to z szybkości działania naszego procesora, któremu jedna instrukcja zajmuje 250ns. Tylko między załączeniem i wyłączeniem sygnału E konieczne jest niewielkie opóźnienie. Bez tego impuls trwałby tylko 250ns.
Na następnym poziomie pojawia się funkcja LCDsendHaif. Pracując z wyświetlaczem z interfejsem czterobitowym, każdą daną do wyświetlacza wysyłamy podzieloną na dwie części. Zadaniem omawianej funkcji jest wysłanie jednej takiej połowy. Zakłada ona, że odpowiednie dane znajdują się na czterech najniższych bitach zmiennej data. Zauważ, żc zmienna data jest wykorzystana tutaj do przechowywania wyniku pośredniego obliczeń. Przypominam, że:
Tabela 3 Zależności czasowe przy generowaniu sygnału E.
VOid LCD3end(uint8 t Jata)
// Starsza część LCDsendHaif (data»4);
// Młodsza część LCDsendHaifCdata); delayus8(120);
1 |
Czas od ustawienia sygnałów RS i RW do uaktywnienia sygnału E |
min. 140ns |
2 |
Czas trwania impulsu E |
min. 450ns |
3 |
Czas podtrzymania sygnałów RS i RW | |
pc opadającym zboczu sygnału E |
min. 20ns |
Listing 23 LCD: część odpowiedzialna za generowanie opóźnień
//----
// Generowanie opóźnień
Adefine delay?50ns() {asir volati1e(“nop": :);}
Adefine de1ayus8(t)\
{asm volatile( \
"deldyus8_loop%=: \n\t"\
“nop \n\t”\
"dec %[ticks] \n\t"\
“brnę delayus8 loop%= \n\t”\
: :rticks]“r"Cr) );}
// DEC - lcykl, BRNĘ 2cykle, flxnop. Zegar 4MHz
VOld dclaylCOusO(uint8_l .)
while(oO)
{
delayus8(100);
-t;
>
}
fł Koniec opóźnień
//------
Listing 24 LCD: funkcje niskiego poziomu
Adefine lcd_epulse() \
{PORT(LCO_EPORT) = U<ICn_E; \ delay250ns(); \
PORT(LCD_EPORT) &= ~(1«LCD_C) ;}
void LC0serdHal£(uint8 t data)
{
data = Cdata & 0x0F) « LCD D4;
PORT(LCD_OPORT) -
(P0RT(LCD_DPCR?) & ~(OxOF«l/:n_D4)) | data;
lcd_epulseO :
38 Październik 2005 Elektronika dla Wszystkich