Instrukcja do ćwiczenia: Współpraca mikrokontrolera z klawiaturą Materiał do samodzielnego opracowania: elementy języka C: typy danych i ich deklarowanie, operatory, instrukcje, pętle, funkcje definiowanie, deklarowanie i wywoływanie, tablice i wskaźniki, pliki nagłówkowe, dyrektywy kompilatora: include, define. 1. Klawiatura W celu komunikacji z użytkownikiem, w układach wykorzystujących mikrokontrolery stosuje się klawiatury (rys. 1). Rys. 1. Klawiatura 16 przyciskowa [1]. Klawiatury są najczęściej zbudowane z łączników o działaniu chwilowym (przycisków), zwierających 2 styki. Na wejścia mikrokontrolera podaje się napięcie 0 V lub 5V w zależności od stanu styków łącznika (rys. 2). Rys. 2. Sposób podłączenia 4 przycisków do mikrokontrolera
2 Oporniki R ograniczają prąd płynący ze źródła zasilania przy zwartych stykach łącznika. Liczba łączników determinuje liczbę wejść mikrokontrolera, koniecznych do obsługi klawiatury. W przypadku dużej liczby klawiszy (powyżej kilku) stosuje się tzw. klawiaturę matrycową. Poszczególne łączniki zwierają linie zwane wierszami z liniami zwanymi kolumnami. Zarówno wiersze jak i kolumny są podłączone do wyprowadzeń mikrokontrolera. Tym sposobem ilość wyprowadzeń mikrokontrolera koniecznych do obsługi klawiatury jest mniejsza od liczby klawiszy (rys. 3). Rys. 3. Obsługa klawiatury matrycowej 2. Odczyt danych z wejść mikrokontrolera W celu umożliwienia odczytu zmiany stanu łącznika klawiatury konieczne jest skonfigurowanie wybranych wyprowadzeń mikrokontrolera jako wejść. Do rejestru DDRX należy wpisać stan 0 (X = {A, B, C, D}) [2]. Np. zapisanie do 5 bitu rejestru DDRA zera logicznego konfiguruje wyprowadzenie mikrokontrolera PA5 jako wejście. Na rys. 4 pokazano przykładowo składniki rejestru DDRA. Rys. 4. Składniki rejestru DDRA[3]. Rezystory R (20 50 kω) pokazane na rys. 2 i 3 (zwane podciągającymi) są wbudowane w mikrokontroler [3]. Ich uaktywnienie uzyskuje się poprzez wpisanie do odpowiedniego bitu () rejestru PORT wartości logicznej jeden. Na rys. 5 pokazano poszczególne bity rejestru PORTA. Rys. 5. Składniki rejestru PORTA [3].
Po skonfigurowaniu wyprowadzenia mikrokontrolera jako wejście, możliwy jest odczyt jego stanu logicznego w rejestrze PINX. Na rys. 6 pokazano przykładowo składniki rejestru PINA. 3 Rys. 6. Składniki rejestru PINA [3]. Sterowanie funkcją i stanem logicznym wyprowadzeń mikrokontrolera w ramach pozostałych portów odbywa się analogicznie. Literę A w nazwie rejestru należy zamienić na B, C lub D. 3. Użyteczne funkcje języka C a) Wiadomości podstawowe W celu poprawienia przejrzystości programu za pomocą dyrektywy kompilatora define można nadać dowolnemu elementowi programu własną nazwę: np.: #define zastępujący_ciąg_znaków zastępowany_ciąg_znaków #define WEJSCIE_0 PB0 Zamiast odwoływać się w programie do małointuicyjnej nazwy PB0 oznaczającej 0 wyprowadzenie portu B, można stosować nazwę SEGMENT_A jednoznacznie określającej co jest sterowane z tego wyprowadzenia. Przy stosowaniu w programie funkcji generujących opóźnienie czasowe wymaga się podania kompilatorowi częstotliwości taktowania procesora. Służy do tego makro: #define F_CPU 16000000 Liczba występująca powyżej to częstotliwość rezonatora kwarcowego w [Hz]. Z praktyki programowania mikrokontrolerów wynika, że wszelkie konfiguracje części sprzętowej mikrokontrolera (wejścia, wyjścia, przetwornika A/C, itd.) powinny być umieszczone w oddzielnym pliku (tzw. nagłówkowym *.h) i dołączane w momencie kompilacji do właściwego pliku z tekstem programu. Owo dołączanie uzyskuje się stosując dyrektywę kompilatora: #include nazwa.pliku Nazwa pliku obejmuje jego rozszerzenie (*.h). Do zapisywania w rejestrze zera lub jedynki logicznej służy makro _BV(...) [3]. Aby z niego skorzystać należy na początku programu dołączyć plik nagłówkowy io.h znajdujący się w katalogu z bibliotekami /avr: #include <avr/io.h> Ustawienie jedynki logicznej na wyprowadzeniu portu X uzyskuje się za pomocą: = _BV(PX) Dla kilku wyprowadzeń jednocześnie instrukcja powinna wyglądać:
4 gdzie: X = {A, B, C, D}, = {0 7}, np. DDRX = _BV(PX1) _BV(PX2) _BV(PX3) PORTB = _BV(PB0) ustawia stan 1 na wyjściu 0 portu B. Stan niski ustawia się za pomocą instrukcji dla kilku wyprowadzeń jednocześnie: &= ~(_BV(PX)) &= ~(_BV(PX1)) & ~(_BV(PX2)) & ~(_BV(PX3)) Konfigurację wyprowadzenia portu X jako wejścia uzyskuje się instrukcją: jako wyjścia: DDRX &= ~(_BV(PX)) DDRX = _BV(PX) Odczyt stanu logicznego wejścia wykonuje się za pomocą makr: bit_is_clear(pinx,px) zwracającego wartość 1 jeżeli stan logiczny rejestru PINX, w polu jest równy 0, oraz: bit_is_set(pinx,px) zwracającego wartość 1 jeżeli stan logiczny rejestru PINX, w polu jest równy 1. Powyższe makra będą dostępne w programie po dołączeniu biblioteki io.h: #include <avr/io.h> Do odmierzania czasu i uzyskiwania opóźnienia czasowego przydatna jest funkcja: void _delay_ms(float liczba_ms) Aby z niej skorzystać należy dołączyć plik delay.h dyrektywą: #include <avr/delay.h> b) Odczyt danych z klawiatury matrycowej Do komunikacji z klawiaturą matrycową można wykorzystać funkcje z biblioteki RKlibAVR autorstwa Roberta Krzysztofa [4]. Do projektu należy dołączyć plik kbd.h dyrektywą: #include "kbd.h"
5 Ponadto należy dołączyć konfigurację części sprzętowej umieszczonej w pliku config.h: // linie kolumn klawiatury #define KBD_PORT_col1 #define KBD_BIT_col1 #define KBD_PORT_col2 #define KBD_BIT_col2 #define KBD_PORT_col3 #define KBD_BIT_col3 #define KBD_PORT_col4 #define KBD_BIT_col4 // linie wierszy klawiatury #define KBD_PORT_row1 #define KBD_BIT_row1 #define KBD_PORT_row2 #define KBD_BIT_row2 #define KBD_PORT_row3 #define KBD_BIT_row3 #define KBD_PORT_row4 #define KBD_BIT_row4 W pliku makefile należy umieścić następujący kod (zastępując całkowicie istniejący tekst): # Nazwa pliku z funkcją main() - BEZ ROZSZERZENIA! TARGET = nazwa_pliku_bez_rozszerzenia # Lista plików, których zmiana powoduje przebudowanie projektu CONFIG = config.h # Lista plików źródłowych w języku C SRC = $(TARGET).c # Lista plików źródłowych w asemblerze (rozszerzenie S - DUŻE S!) ASRC = # typ mikrokontrolera MCU = atmega32 # Format pliku wyjściowego (srec, ihe) FORMAT = ihe # Poziom optymalizacji (0, 1, 2, 3, s) # (Uwaga: 3 nie zawsze jest najlepszym wyborem) OPT = s # Katalog z bibliotekami użytkownika USRLIB = C:/WinAVR-20100110/lib # Lista plików źródłowych bibliotek w języku C SRCLIB = include $(USRLIB)/kbd/sources
6 include $(USRLIB)/delay/sources # Dodatkowe biblioteki # # Minimalna wersja printf #LDFLAGS += -Wl,-u,vfprintf -lprintf_min # # Zmiennoprzecinkowa wersja printf (wymaga biblioteki matematycznej) #LDFLAGS += -Wl,-u,vfprintf -lprintf_flt # # Biblioteka matematyczna #LDFLAGS += -lm include $(USRLIB)/avr_make Do inicjalizacji klawiatury matrycowej służy funkcja: void KBD_init (void) Odczyt kodu wciśniętego klawisza można uzyskać za pomocą funkcji: u08 KBD_read (void) gdzie: u08 liczba całkowita ośmiobitowa (0 do 2 8 1). Konwersję kodu klawisza na łańcuch znaków dla wyświetlacza LCD można uzyskać za pomocą funkcji: char* itoa (int wartosc, char *lancuch_zakow, int notacja) gdzie: wartosc liczba do konwersji, *lancuch_zakow liczba przekształcona na łańcuch znaków, notacja system liczbowy (2, 8, 10, 16). Funkcja wymaga dołączenia do projektu biblioteki stdlib.h: #include <stdlib.h> c) Sterowanie wyświetlacza LCD Do sterowania wyświetlacza LCD zostaną wykorzystane funkcje z biblioteki tavrlib autorstwa Tomasza Wasilczyka [5]. Biblioteka objęta jest licencją GNU LGPL v3. W folderze z zadaniem muszą być dostępne pliki hd44780.c, hd44780.h i macros.h należy je skopiować z katalogu tavrlib. Drugi z plików należy dołączyć do programu dyrektywą: #include "hd44780.h" W pliku zawierającym konfigurację części sprzętowej config.h należy umieścić następujący kod: // pod którym portem jest szyna danych #define HD44780_DATA_GPIO X //port wyświetlacza X = {A, B,C, D} // jaką część portu zajmuje szyna danych: // jeżeli wyświetlacz dołączono do wyprowadzeń portu 4 7 to Y = 1 // jeżeli wyświetlacz dołączono do wyprowadzeń portu 0 3 to Y = 0 #define HD44780_DATA_HIGHHALFBYTE Y
7 //Do jakiego portu X i nr wyprowadzenia podłączono linię RS wyświetlacza: #define HD44780_RS_GPIO X #define HD44780_RS_BIT //j. w. ale llinia E wyświetlacza: #define HD44780_E1_GPIO X #define HD44780_E1_BIT //parametry wyświetlacza w znakach #define HD44780_WIDTH 16 #define HD44780_HEIGHT 2 Plik konfiguracyjny config.h powinien być dołączony w programie (#include...) wcześniej niż plik hd44780.h. Inicjalizacji wyświetlacza dokonuje się na początku programu za pomocą funkcji: void hd44780_init(void) Kasowanie znaków z wyświetlacza umożliwia funkcja: void hd44780_clear (void) Wyświetlenie znaków na konkretnym polu wyświetlacza umożliwia funkcja: void hd44780_goto (uint8_t, uint8_t y) gdzie: - żądana kolumna, y - żądany wiersz, uint8_t liczba całkowita bez znaku (typ danej). Wyświetlenie ciągu znaków (łańcucha znaków zdefiniowanego jako tablica znaków) uzyskuje się dzięki funkcji: void hd44780_putstr (char *str, uint8_t length) gdzie: *str wskaźnik do tablicy znaków, lenght ilość znaków do wyświetlenia. lenght = -1 funkcja wyświetla wszystkie znaki z tablicy. Jeżeli 4. Przebieg ćwiczenia W zestawie uruchomieniowym ZL3AVR wyprowadzenia klawiatury są dostępne na złączach JP23 i JP3. Pierwsze złącze obejmuje wyprowadzenia wierszy i kolumn (W1 W4, K1 K4). Klawiatura, składająca się z 16 przycisków (S1 S16) znajduje się w prawej dolnej części zestawu. Za pomocą złącza JP3 można ograniczyć liczbę klawiszy do 4 (S1, S5, S9, S13) zwierając po jednym styku każdego z przycisków do masy (GND). Klawiatura nie ma wbudowanych rezystorów podciągających, należy je uaktywnić na wejściach mikrokontrolera. Przykładowe zadania do wykonania: a) Napisać i uruchomić program, w którym naciśniecie przycisku spowoduje zaświecenie diody LED. b) Napisać i uruchomić program, w którym naciśniecie przycisku spowoduje zgaszenie diody LED. c) Napisać i uruchomić program, w którym cztery przyciski sterują dwiema diodami LED (włącz / wyłącz).
8 d) Napisać i uruchomić program, który spowoduje wypisanie na wyświetlaczu LCD kodu klawisza klawiatury matrycowej. W p. c należy wykorzystać wyświetlacz LCD znajdujący się w prawej, górnej części zestawu ZL3AVR. Jego wyprowadzenia są dostępne na złączu JP29 (LCD4bit). Wyprowadzenia złącza JP29 należy podłączyć do wyprowadzeń wybranego portu mikrokontrolera. Następnie należy poinformować kompilator o dokonanym wyborze wpisując odpowiednie ustawienia w pliku z konfiguracją sprzętową (patrz p. 3 c)). Potencjometr PR1 służy do regulacji kontrastu wyświetlacza. 5. Literatura [1] http://www.adatronik.com.pl/klawiatura44.jpg [2] 8-bit Microcontroller with 32K Bytes In-System Programmable Flash Atmega32 Atmega32L, 2503O AVR 07/09, www.atmel.com, [3] avr-libc 1.6.7, Generated by Doygen 1.5.6 Wed Jan 6 12:08:48 2010, c:\winavr-20100110\doc\avr-libc\, [4] R. Krzysztof, Dokumentacja biblioteki RKlibAVR, http://www.isaa.pl/download/doc_details/89-rklibavr-uniwersalna-biblioteka-dla-avr-gcc. [5] T. Wasilczyk, Dokumentacja biblioteki tavrlib, http://tavrlib.wasilczyk.pl. Wersja z dn. 22.02.2011 r.