Mikrokontrolery_ARM_cz13.pdf

(235 KB) Pobierz
109-110_arm_cz13.indd
K U R S
Mikrokontrolery z rdzeniem ARM,
część 13
Porty GPIO
W poprzednich odcinkach zajmowaliśmy się układami peryferyjnymi
mającymi bezpośredni wpływ na pracę rdzenia mikrokontrolera.
Omówiliśmy także przykładowy plik startowy konfigurujący powyższe
układy oraz inicjalizujący pamięć mikrokontrolera zgodnie ze
standardem ANSI C/C++.
Tematem bieżącego odcinka będą porty wejścia–wyjścia (GPIO)
mikrokontrolerów LPC213x, które umożliwiają bezpośrednie
sterowanie układami podłączonymi do wyprowadzeń mikrokontrolera.
tlacza LCD. Została ona zadeklarowana
następująco:
void PortSend( unsigned char data, bo-
ol cmd= false );
Jako parametr data przekazujemy in-
strukcję lub daną, którą chcemy wysłać
do wyświetlacza LCD. Gdy parametr
cmd przyjmie wartość false , oznacza to,
że liczba przekazana jako data będzie
zinterpretowana jako znak do wyświe-
tlenia, w przeciwnym przypadku przesła-
na dana będzie stanowić rozkaz. W ję-
zyku C++ możemy deklarować metody
i funkcje z parametrami domyślnymi.
W przypadku, gdy wywołamy funkcję
bez drugiego argumentu parametr cmd
przyjmie wartość false, natomiast gdy
drugi parametr będzie określony pod-
czas wywołania, argument domyślny
będzie ignorowany. Mechanizm ten zo-
stał stworzony w celu zastąpienia funk-
cji ze zmienną listą argumentów (…)
znaną z języka C. Pozwala on zapewnić
większą kontrolę nad przekazywany-
mi argumentami. Działanie tej metody
jest następujące. Najpierw sygnał E jest
ustawiany w stan 0, w efekcie czego
wyświetlacz ignoruje wszystkie stany
pojawiające się na liniach danych wy-
świetlacza. Linie D0..D7 wyświetlacza
LCD są zerowane poprzez ustawienie
bitów 16:23 w rejestrze IO1CLR. Do
portu IO1SET przesyłana jest zawartość
zmiennej data przesuniętej o 16 bitów
w lewo. W wyniku tych dwóch opera-
cji linie P1.16..P1.23 przyjmują wartość
zgodną z zawartością zmiennej data bez
zmiany pozostałych bitów portu.
//E=0
LCDCCLR = E;
//Data = 0;
LCDDCLR = DMASK;
//Wyslij dane
LCDDSET = (( unsigned int )data) <<
16;
Program drugi – wyświetlacz
LCD
Kolejnym programem jaki napisze-
my w ramach ćwiczeń z portami GPIO
będą procedury obsługi znakowego wy-
świetlacza LCD (HD44180). Procedury
te będziemy intensywnie wykorzystywać
w dalszej części kursu. W różnych cza-
sopismach o tematyce elektronicznej ob-
sługa znakowego wyświetlacza LCD była
poruszana wielokrotnie, dlatego aby nie
powielać tych samych schematów tym
razem biblioteka ta zostanie napisana
w nieco odmienny sposób za pomocą
programowania obiektowego C++. W ze-
stawie ZL6ARM linie D0.D7 LCD pod-
łączone są do portu P1.16…P1.23. Linia
E jest podłączona do portu P0.30, na-
tomiast RS do portu P0.31. W zestawie
niestety nie przewidziano możliwości
sterowania linią R/W przez co niemoż-
liwe jest odczytywanie stanu wyświetla-
cza, dlatego po wysłaniu każdego znaku
i rozkazu musimy odczekać pewien czas
tak aby wybrana operacja została wyko-
nana. Prawie wszystkie komendy wy-
konywane są w czasie do 120 ms poza
rozkazem czyszczenia wyświetlacza, któ-
ry może zająć maksymalnie 4,8 ms. Za
obsługę LCD odpowiedzialna jest klasa
CLcdDisp , której deklaracja znajduje się
w pliku CLcdDisp.h, natomiast definicja
została umieszczona w pliku CLcdDisp.
c . Metody (funkcje) i obiekty (zmienne)
zadeklarowane z modyfikatorem private
mogą być używane tylko wewnątrz kla-
sy, co zapewnia ukrycie ich przed użyt-
kownikiem końcowym. W sekcji tej za-
pisano stałe związane z wyświetlaczem
LCD, takie jak przypisanie bitów odpo-
wiedzialnych za linie E i RW wyświe-
tlacza oraz stałe związane z komendami
kontrolera LCD.
//Funkcja opozniajaca
void Delay( unsigned int del);
//Wysyla do portu
void PortSend( unsigned char
data, bool cmd= false );
//Pin E P0.30
static const unsigned int E =
0x40000000;
//Pin RW P0.31
static const unsigned int RS =
0x80000000;
//Maska danych
static const unsigned int DMASK =
0x00FF0000;
//Domyslne sprzetowe
static const unsigned int DELAY_HW
= 15;
//Opoznienie komend
static const unsigned int DELAY_CMD
= 3000;
//Opoznienie dla CLS
static const unsigned int DELAY_CLS
= 30000;
//Komendy wyswietlacza
enum {CLS_CMD=0x01,HOME_
CMD=0x02,MODE_CMD=0x04,ON_CMD=0x08,
SHIFT_CMD=0x10,FUNC_CMD=0x20,CGA_
CMD=0x40,DDA_CMD=0x80};
//Komenda MODE
enum {MODE_R=0x02,MODE_L=0,MODE_
MOVE=0x01};
//Komenda SHIFT
enum {SHIFT_DISP=0x08,SHIFT_
R=0x04,SHIFT_L=0};
//Komenda FUNC
enum {FUNC_8b=0x10,FUNC_4b=0,FUNC_
2L=0x08,
FUNC1L=0,FUNC_5x10=0x4,FUNCx7=0};
};
Umieszczono tu także dwie me-
tody: Delay , odpowiedzialną za ge-
nerowanie opóźnień oraz PortSend
wysyłającą bajt danych do wyświe-
tlacza Lcd . Pętla opóźniająca zosta-
ła napisana w asemblerze, aby było
możliwe dokładne określenie czasu
jej wykonania. Jako argument meto-
dy podajemy liczbę, która następnie
jest ładowana do któregoś z rejestrów
ogólnego przeznaczenia w którym na-
stępuje cykliczne odejmowanie liczby
jeden, aż do momentu, gdy rejestr
ten osiągnie wartość 0.
void CLcdDisp::Delay( unsigned int
del)
{
asm volatile
(
“dloop%=:”
“subs %[del],%[del],#1\t\n”
“bne dloop%=\t\n”
: :[del]”r”(del)
);
}
Po przesłaniu danych na linie D0...
D7 następuje ustawienie linii RS w od-
powiedni stan w zależności od tego, czy
dane przesłane na magistrale zinterpre-
towane zostaną jako rozkaz (stan wy-
soki), albo znak do wyświetlenia (stan
niski)
//Skasuj lub ustaw RS
if (cmd) LCDCCLR = RS;
else LCDCSET = RS;
Następnie na linii E generowany
jest dodatni impuls, w wyniku którego
następuje zapisanie danych lub instruk-
cji do wyświetlacza LCD.
Metoda PortSend służy do wysyłania
pojedynczego bajtu danych do wyświe-
Elektronika Praktyczna 12/2006
109
197730177.001.png
K U R S
//Ustaw Enable
LCDCSET = E;
Delay(DELAY_HW);
//Skasuje enable
LCDCCLR = E;
Wszystkie metody zadeklarowane
jako public dostępne są dla użytkowni-
ka i stanowią zewnętrzny interfejs kla-
sy. Klasa CLcdDisp zawiera następujące
składowe publiczne:
public :
CLcdDisp();
~CLcdDisp();
void Write( const char *str);
void Write( char zn);
void Write( unsigned int licz);
//Wyczysc wyswietlacz
void Clear( void );
//Zalacz wylacz kursor
void SetCursor( unsigned char cmd);
void GotoXY( unsigned char
x, unsigned char y);
template < class T> CLcdDisp& opera-
tor <<(T obj)
{
Write(obj);
return * this ;
}
CLcdDisp& operator <<(pos obj)
{
GotoXY(obj.mx,obj.my);
return * this ;
}
CLcdDisp jest domyślnym konstruk-
torem klasy i jest on wywoływany pod-
czas tworzenia nowego obiektu danej
klasy. W konstruktorze napisano proce-
durę inicjalizacji wyświetlacza LCD. Ini-
cjalizacja rozpoczyna się od ustawienia
linii RS, E i D0...D7 oraz odczekania
kilkudziesięciu milisekund na ustabilizo-
wanie napięcia zasilającego:
//Konstruktor klasy obslugi wyswie-
tlacza LCD
CLcdDisp::CLcdDisp()
{
//Linie E i RS jako wyjsciowe
LCDCDIR |= E|RS;
LCDCCLR = E|RS;
//Linia danych jako wyjsciowa
LCDDDIR |= DMASK;
Delay(100000);
Następnie trzykrotnie wysyłana
jest komenda ustawiająca wyświetlacz
w tryb 8-bitowy:
PortSend(FUNC_CMD|FUNC_8b, true );
Delay(DELAY_CLS);
PortSend(FUNC_CMD|FUNC_8b, true );
Delay(DELAY_CMD);
PortSend(FUNC_CMD|FUNC_8b, true );
Delay(DELAY_CMD);
po czym następuje ustawienie wy-
świetlacza tak, aby pracował w rozdziel-
czości 5x7, załączenie wyświetlacza,
wyczyszczenie oraz ustawienie kursora
w pozycji początkowej. Kolejnymi me-
todami publicznymi są metody Write
służące do wypisania na wyświetlaczu
pojedynczego znaku, łańcucha tekstowe-
go, oraz liczby stałoprzecinkowej. Uważ-
nego Czytelnika może zdziwić fakt, że
metody o takiej samej nazwie zadekla-
rowane są kilkukrotnie. Jest to kolejna
zaleta języka C++, w którym możemy
deklarować funkcję i metody o takich sa-
mych nazwach. Kompilator w zależności
od argumentu przekazanego do metody
wywoła odpowiednią funkcję Write . Np.
jeżeli napiszemy lcd.Write(100) , zostanie
wywołana metoda Write , której argument
jest typu int . Poszczególne metody są
bardzo podobne, przedstawię tutaj me-
todę Write wypisująca łańcuch tekstowy.
void CLcdDisp::Write( const char
*str)
{
while (*str)
{
PortSend(*str++);
Delay(DELAY_CMD);
}
}
umożliwia tworzenie operacji łań-
cuchowych. W programie stworzono
także dodatkową klasę pos , której
przesłanie do klasy wyświetlacza
LCD spowoduje ustawienie kursora
na wybranej pozycji. Definicja tej
klasy jest następująca:
class pos
public :
pos( unsigned char x, unsigned char
y):mx(x),my(y) {}
unsigned char mx,my;
};
Działanie tej metody polega na
odczytaniu pojedynczego znaku, prze-
pisaniu jego zawartości do wyświe-
tlacza LCD za pomocą metody Port-
Send oraz odczekaniu około 40 ms na
przesłanie znaku. Następnie wskaź-
nik jest zwiększany o jeden i wysy-
łany jest kolejny znak. Dzieje się
tak do czasu, gdy zostanie wykry-
ty znak 0 będący symbolem końca
łańcucha. Metoda Clear() umożliwia
wyczyszczenie zawartości wyświe-
tlacza, natomiast metoda GotoXY()
umożliwia przejście do wybranej po-
zycji kursora. W języku C++ możemy
zmieniać znaczenie operatorów, co
nosi nazwę przeciążania operatorów.
Korzystając z tej techniki napiszemy
własne wersje operatora << umoż-
liwiające wypisywanie liczb i zmien-
nych na przykład tak: lcd <<
„Zmienna= „ << zm; Napiszemy
także bardzo prostą klasę pos, której
przekazanie do obiektu klasy wyświe-
tlacza LCD spowoduje przesunięcie
kursora na wybraną pozycję np. tak:
lcd << pos(1,2) << „2 li-
nia”; Wszystkie operatory korzysta-
ją z wcześniej zdefiniowanych metod
Write() oraz GotoXY() i są zdefinio-
wane w deklaracji klasy zapewniając
rozwinięcie ich w miejscu wywołania.
Operator wysyłający dane do strumie-
nia zdefiniowano wsposób następują-
cy:
template < class T> CLcdDisp& operator
<<( const T &obj)
{
Write(obj);
return * this ;
}
Zastosowano tutaj kolejną ce-
chę języka C++ mianowicie funkcję
wzorcową. Mechanizm ten umożliwia
zadeklarowanie tylko jednej funkcji
niezależnie od argumentów jakie ona
przyjmuje. Po prostu w momencie
wywołania funkcji z danym parame-
trem, kompilator na etapie kompila-
cji tworzy daną funkcję zamieniając
T na konkretny typ danych na przy-
kład int. W wyniku tej czynności nie
musimy pisać trzech osobnych wersji
operatora dla każdego typu danych:
char* , int , char . Operator zwraca
wskaźnik do klasy obiektu LCD, co
Klasa ta zawiera tylko dwa pola
określające pozycję kursora na wy-
świetlaczu oraz konstruktor, który
przyjmuje jako argumenty pozycję
kursora oraz przepisuje je do mx
oraz my .
Dla obiektu klasy pos stworzony
jest osobny operator <<, który wy-
wołuje metodę GotoXY() przesuwając
kursor wyświetlacza LCD do odpo-
wiedniej pozycji zawartej w zmien-
nych mx , my .
CLcdDisp& operator <<( const pos
&obj)
{
GotoXY(obj.mx,obj.my);
return * this ;
}
W pliku testlcd.cpp znajduje się
bardzo prosty programik korzystający
z klasy CLcdDisp , wypisujący na wy-
świetlaczu LCD stan wciśniętego kla-
wisza S1..S4.
CLcdDisp cout;
//Funkcja glowna main
int main( void )
{
cout << “Witaj !”;
cout << pos(1,2) << “IO0PIN=”;
unsigned int sk;
while (1)
{
sk = (~IO0PIN >> 4) & 0x0f;
cout << pos(8,2)<< sk << „ „;
}
}
110
Elektronika Praktyczna 12/2006
Działanie programu rozpoczyna się
od utworzenia obiektu klasy CLcdDisp
o nazwie cout . W funkcji main() wypisy-
wany jest napis powitalny, a następnie
program wchodzi w pętlę nieskończoną,
która odczytuje stan klawiszy S1...S4
oraz przepisuje ich zawartość do zmien-
nej sk , maskując pozostałe nie istotne
bity. Następnie na pozycji 8,2 wypisy-
wany jest stan zmiennej sk . Pomimo,
że mechanizmy tworzące operatory są
trochę zawiłe, korzystanie z samej bi-
blioteki obsługi wyświetlacza LCD jest
bardzo proste. Czytelnikom znającym
język C++ proponuję napisanie klasy
o nazwie clear , której przekazanie do
klasy CLcdDisp za pomocą operatora
>> spowoduje wyczyszczenie wyświe-
tlacza LCD.
Lucjan Bryndza, EP
lucjan.bryndza@ep.com.pl
197730177.002.png
Zgłoś jeśli naruszono regulamin