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
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
Plik z chomika:
xp7
Inne pliki z tego folderu:
Mikrokontrolery_ARM_cz1.pdf
(898 KB)
Mikrokontrolery_ARM_cz2.pdf
(1412 KB)
Mikrokontrolery_ARM_cz3.pdf
(738 KB)
Mikrokontrolery_ARM_cz4.pdf
(1264 KB)
Mikrokontrolery_ARM_cz5.pdf
(517 KB)
Inne foldery tego chomika:
C%MOS
Mikrokontrolery AVR
Mikrokontrolery dla początkujących - Piotr Górecki
Mikrokontrolery i układy cyfrowe
Mikrokontrolery to takie proste - EDW
Zgłoś jeśli
naruszono regulamin