Instrukcja do ćwiczenia: Pomiar napięcia i szeregowa transmisja danych z uŜyciem mikrokontrolera 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. Przetwornik analogowo-cyfrowy Przetwornik analogowo – cyfrowy zamienia sygnał analogowy (ciągły) na sygnał cyfrowy [1]. Proces przetwarzania analogowo-cyfrowego obejmuje próbkowanie, kwantyzację i kodowanie. Poprzez próbkowanie sygnału wejściowego określa i zapamiętuje się jego wartość chwilową w ustalonych odstępach czasu. Wartość ta w wyniku kwantyzacji zostaje przyporządkowywana jednej ze zbioru wartości dyskretnych. Kodowanie oznacza zapis wartości dyskretnej za pomocą ciągu ser i jedynek (niskich i wysokich stanów logicznych). Mikrokontroler ATMega32 został wyposaŜony w przetwornik A/C o rozdzielczości 10 bitów (wartość chwilowa sygnału ciągłego moŜe być przyporządkowana do jednej z 210 = 1024 wartości dyskretnych). Wejście przetwornika jest dołączone do ośmiokanałowego multipleksera, pozwalającego na pomiar sygnałów dołączonych do wyprowadzeń portu PA (rys.1). Rys. 1. Schemat wyprowadzeń procesora ATMega 32 [2] Czas przetwarzania wbudowanego przetwornika moŜe wynosić do 13 do 260 µs [2]. Z tego względu przetwornik ten nie nadaje się do pomiaru sygnałów szybkozmiennych. 2. Uniwersalny układ transmisji szeregowej (USART) Uniwersalny, synchroniczno - asynchroniczny układ nadajnik - odbiornik (ang. USART Universal Synchronous and Asynchronous Receiver and Transmitter [2]) pozwala na wymianę 2 danych mikrokontroler – otoczenie. Wymiana danych odbywa się w trybie szeregowym. Transmisja rozpoczyna się bitem startowym, a kończy bitem stopu (rys. 2). Kontrola bitu parzystości pozwala na detekcję błędów transmisji. Bit parzystości przyjmuje wartość 1 jeśli suma bitów danych jest liczbą parzystą. Rys. 2. Schemat pakietu (ramki) danych w transmisji szeregowej [2]: St – bit startowy, Sp – bit stopu, 1 … 8 – bity danych, P – bit kontroli parzystości (opcjonalny), IDLE – stan oczekiwania USART komunikuje się z otoczeniem za pomocą dwóch wyprowadzeń: RXD – odbiornik i TXD – nadajnik. Dołączenie mikrokontrolera do komputera PC odbywa się za pomocą portu szeregowego (tzw. kabel null – modem zakończony wtykami dziewięciostykowymi DB 9). Ze względu na obowiązujące standardy, port szeregowy PC uŜywa poziomów napięć ± 15 V, natomiast mikrokontroler 0, 5 V. Konieczne jest dopasowanie poziomów napięć pomiędzy mikrokontrolerem, a PC. Te rolę spełnia układ scalony MAX232 (rys.3). Rys. 3. Schemat ideowy konwertera poziomów napięć z układem MAX232 Przed nawiązaniem połączenia poprzez port szeregowy oba urządzenia powinny mieć skonfigurowane takie same parametry transmisji, tj. prędkość transmisji (300 – 115200 bit/s), ilość bitów danych (4…8), kontrola parzystości (lub brak), ilość bitów stopu (1 … 2), rodzaj sterowania przepływem danych. 3. Konfiguracja przetwornika A/C W celu uruchomienia wbudowanego w mikrokontroler przetwornika A/C naleŜy wykonać szereg czynności konfiguracyjnych w odpowiedniej kolejności. Oznacza to zapisanie w pewnych rejestrach mikrokontrolera odpowiednich stanów logicznych [2]. Pierwszą czynnością jest wybór wyprowadzenia portu A, które będzie pełniło rolę wejścia przetwornika. Do określonego bitu 3 rejestru DDRA naleŜy wpisać wartość 0. Na rys. 4 pokazano przykładowo składniki (poszczególne bity) rejestru DDRA. Rys. 4. Składniki (bity) rejestru DDRA[2]. Następnie naleŜy wybrać częstotliwość pracy przetwornika za pomocą bitów ADPSx (gdzie x={0… 7}) w rejestrze ADCSRA (rys. 5). Częstotliwość pracy powinna znajdować się w przedziale 50 … 200 kHz. Rys. 5. Składniki rejestru ADCSRA [2]. Uzyskuje się ją przez podział częstotliwości zegara mikrokontrolera (16 MHz) przez 2 … 128 i przez 13 (średnio 13 cykli zegarowych trwa przetwarzanie A/C). Ustawienia bitów ADPSx naleŜy wykonać zgodnie z tab. 1. Tab. 1 Wybór częstotliwości taktowania przetwornika A/C [2] W automatycznym trybie pracy naleŜy ustalić automatyczne wyzwalanie pomiaru za pomocą zapisania wartości 1 do bitu ADATE rejestru ADCSRA (rys. 5), oraz wyzerowanie bitów ADTSx rejestru SFIOR (rys. 6), wybierając tryb free running (tab. 2.). Rys. 6. Składniki rejestru SFIOR Kolejną czynnością jest wybranie aktywnego wejścia multipleksera, poprzez zapis bitów MUXx (tab. 3) w rejestrze ADMUX (rys. 7). Wybór powinien dotyczyć tego samego wejścia, które zostało ustawione w rejestrze DDRA. Zapis bitów REFSx w tym samym rejestrze pozwala wybrać napięcie odniesienia dla przetwornika: REFS1;REFS0 = 01 – napięcie zasilania mikrokontrolera 4 (+5 V), REFS1;REFS0 = 11 – źródło napięcia odniesienia wbudowane w mikrokontroler (+ 2,56 V). Tab. 2. Ustawienia bitów ADTSx dla wyzwalania automatycznego przetwornika A/C [2] Tab. 2. Ustawienia bitów MUXx multipleksera przetwornika A/C [2] Rys. 7. Składniki rejestru ADMUX. Następnie naleŜy włączyć przetwornik A/C poprzez wpisanie logicznej 1 do bitu ADEN w rejestrze ADCSRA (rys. 5) i rozpocząć pracę automatyczną poprzez nadanie wartości 1 bitowi ADSC w tym samym rejestrze. Wynik przetwarzania odczytuje się bezpośrednio z rejestru ADC, po zakończeniu przetwarzania danej próbki sygnału wejściowego. Zakończenie przetwarzania jest sygnalizowane ustawieniem wartości 1 bitu ADIF w rejestrze ADCSRA. Jeśli wspomniany bit ma wartość 0, oznacza to, Ŝe przetwarzanie A/C jest w toku. Odczytanie w takim przypadku rejestru ADC daje błędny wynik. Wartość zmierzonego napięcia moŜna uzyskać ze wzoru [2]: wynik_przetwarzania = ADC/1024.0*VREF gdzie: VREF – wartość napięcia odniesienia, ustawiona za pomocą bitów REFS1, REFS0 (2,56 lub 5,0 V). W manualnym trybie pracy zbędne jest ustawianie bitów ADATE i ADTSx, Nadanie wartości 1 bitowi ADSC powoduje jednokrotne zadziałanie przetwornika A/C. Zakończenie 5 przetwarzania jest sygnalizowane wyzerowaniem tego bitu. Ponadto po zakończeniu przetwarzania naleŜy zapisać do ADSC wartość 0. Odczyt wyniku przetwarzania moŜe nastąpić po sprawdzeniu wartości ADSC (musi być 0). Do konfiguracji i uruchomienia przetwornika A/C naleŜy uŜyć funkcji języka C opisanych w p. 4 a. 4. 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ę: #define zastępujący_ciąg_znaków zastępowany_ciąg_znaków np.: #define WEJSCIE_0 PB0 Zamiast odwoływać się w programie do małointuicyjnej nazwy PA0 oznaczającej 0 wyprowadzenie portu B, moŜna stosować nazwę WEJSCIE_PA0 jednoznacznie określającej jaka jest rola 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 np.: config.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 x portu X uzyskuje się za pomocą: PORTX |= _BV(PXx) Dla kilku wyprowadzeń jednocześnie instrukcja powinna wyglądać: DDRX |= _BV(PXx1)|_BV(PXx2)|_BV(PXx3) gdzie: X = {A, B, C, D}, x = {0 … 7}, np. PORTB |= _BV(PB0) 6 ustawia stan 1 na wyjściu 0 portu B. Stan niski ustawia się za pomocą instrukcji PORTX &= ~(_BV(PXx)) dla kilku wyprowadzeń jednocześnie: PORTX &= ~(_BV(PXx1)) & ~(_BV(PXx2)) & ~(_BV(PXx3)) Konfigurację wyprowadzenia x portu X jako wejścia uzyskuje się instrukcją: DDRX &= ~(_BV(PXx)) jako wyjścia: DDRX |= _BV(PXx) Odczyt stanu logicznego wejścia wykonuje się za pomocą makr: bit_is_clear(PINX,PXx) zwracającego wartość 1 jeŜeli stan logiczny rejestru PINX, w polu x jest równy 0, oraz: bit_is_set(PINX,PXx) zwracającego wartość 1 jeŜeli stan logiczny rejestru PINX, w polu x 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) Konfiguracja i obsługa USART Do konfiguracji i obsługi transmisji danych moŜna wykorzystać funkcje z biblioteki RKlibAVR autorstwa Roberta Krzysztofa [4]. Do projektu naleŜy dołączyć plik kbd.h dyrektywą: #include "uart.h" Do projektu naleŜy dołączyć konfigurację części sprzętowej umieszczonej w pliku config.h: #define F_CPU 16000000 #define UART_BAUD 19200 #define UART_MAX_GETSTR 8 //częstotliwość zegara w Hz // prędkość transmisji danych //bufor dla odbieranego łańcucha znaków Ponadto w pliku makefile naleŜy umieścić następujący kod (zastępując istniejący tekst): 7 # 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, ihex) FORMAT = ihex # 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)/uart/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 W celu kasowania danych wysyłanych przez port szeregowy i drukowania nowych danych w następnej linii terminala w programie naleŜy umieścić następujące definicje: // tablica zawierająca znaki nowej linii prog_char ANSI_NEWLINE[] = {'\n','\r',0}; // j.w. ale czyszcząca ekran terminala prog_char ANSI_CLEAR[] = {27,'[','H',27,'[','2','J',0}; // makra przejscia do nowej linii 8 //i kasowania linii w teminalu #define CLEAR() UART_putstr_P(ANSI_CLEAR) #define NEWLINE() UART_putstr_P(ANSI_NEWLINE) Dzięki temu kasowanie linii uzyskuje się komendą CLEAR(), a przejście do nowej linii komendą NEWLINE(). Do inicjowania USART słuŜy funkcja: void UART_init (void) Odczyt pojedynczych znaków lub łańcucha znaków z portu szeregowego umoŜliwiają funkcje: int UART_getchar ( void ) void UART_getstr ( char * s ) Wysyłanie pojedynczych znaków lub łańcucha znaków z portu szeregowego umoŜliwiają funkcje: int UART_putchar ( char c ) void UART_putstr ( char * s ) Funkcja: void UART_putU08 ( u08 value ) wysyła przez port szeregowy dziesiętną reprezentację liczby całkowitej. c) Sterowanie wyświetlacza LCD Wizualizacja wyników pomiarów przetwornika A/C moŜe być przeprowadzona z uŜyciem 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 //Do jakiego portu X i nr wyprowadzenia x podłączono linię RS wyświetlacza: #define HD44780_RS_GPIO X #define HD44780_RS_BIT x //j. w. ale llinia E wyświetlacza: 9 #define HD44780_E1_GPIO X #define HD44780_E1_BIT x //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 x, uint8_t y) gdzie: x - Ŝą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 d) Przetwarzanie wartości liczbowej na łańcuch znaków Wynik przetwarzania przetwornika A/C obliczony wg wzoru podanego w p. 3 jest liczbą zmiennoprzecinkową (w formacie float lub double). Wspomniana w p. 4 lit. C funkcja biblioteczna: void hd44780_putStr (char *str, uint8_t length) słuŜąca do wyświetlania danych na wyświetlaczu LCD wymaga jako pierwszego argumentu wskaźnika do tablicy znaków: char *str. W związku z tym istnieje konieczność zamiany liczby zmiennoprzecinkowej na łańcuch znaków. Do tego celu moŜna uŜyć funkcji: char* dtostrf (double _val, signed char _width, unsigned char _prec, char _s) gdzie: char* – wskaźnik do tablicy znaków, _val – liczba do konwersji, _width – ilość znaków w wynikowej tablicy znaków _s, _prec – ilość miejsc po przecinku. Podając parametr _width naleŜy przewidzieć w razie konieczności dwa dodatkowe miejsca na punkt dziesiętny „ , ” i znak minus. Funkcja zwraca wskaźnik do tablicy znaków _s. UŜycie funkcji dtostrf wymaga obecności w pliku makefile dyrektywy: MATH_LIB = -lm. 5. Przebieg ćwiczenia W celu uruchomienia przetwornika A/C do gniazda CINCH J4 (Line IN (DC)) naleŜy połączyć z wyprowadzeniem VCC złącza JP9 (Zasilanie). Następnie wyprowadzenie „Tlum.” 10 złącza JP7 (ADC) naleŜy dołączyć do wybranego w programie wejścia przetwornika (PA0 … PA7). Potencjometr PR4 umoŜliwia zmianę wartości napięcia podawanego na wejście przetwornika A/C w zakresie 0 … 5 V. Przykładowe zadania do wykonania: a) Napisać i uruchomić program, na wyświetlaczu LCD będzie prezentowana wartość rejestru ADC przetwornika A/C (dwa warianty: tryb manualny i automatyczny pracy przetwornika). b) Napisać i uruchomić program, na wyświetlaczu LCD będzie prezentowana wartość napięcia zmierzonego przez przetwornik A/C (dwa warianty: tryb manualny i automatyczny pracy przetwornika). 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. Uruchomienie transmisji szeregowej musi być poprzedzone połączeniem zworką obu wyprowadzeń złącza JP4 (RxD Enable) i podłączeniem kabla null – modem do portu szeregowego komputera PC i złącza J6 (COM) zestawu ZL3AVR. Nastepnie na komputerze PC naleŜy uruchomić program Termite lub HyperTerminal (Menu Start\ Programy\ Akcesoria\ Komunikacja\ HyperTerminal) i ustawić te same parametry transmisji danych, które zostały uŜyte w programie dla mikrokontrolera (typowe ustawienia rys. 8 – program Termite). Po uruchomieniu prawidłowo skonstruowanego programu na mikrokontrolerze na ekranie monitora komputera PC powinny się pojawić znaki odbierane z portu szeregowego. Przykładowe zadania do wykonania: c) Napisać i uruchomić program, w wyniku którego na ekranie monitora komputera PC będzie wyświetlany łańcuch znaków wysyłany przez mikrokontroler. d) Napisać i uruchomić program, w wyniku którego na ekranie monitora komputera PC będzie wyświetlany wynik pomiaru napięcia przez przetwornik A/C. e) Napisać i uruchomić program, w wyniku którego na ekranie monitora komputera PC i jednocześnie na wyświetlaczu LCD zestawu ZL3AVR będzie wyświetlany wynik pomiaru napięcia przez przetwornik A/C. Rys. 8. Ustawienia parametrów transmisji szeregowej 6. Literatura [1] M. Rusek, J. Pasierbiński, „Elementy i układy elektroniczne w pytaniach i odpowiedziach”, WNT 1999, [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 Doxygen 1.5.6 Wed Jan 6 12:08:48 2010, c:\Winavr-20100110\doc\avr-libc\, 11 [4] [5] R. Krzysztof, Dokumentacja biblioteki RKlibAVR, http://www.isaa.pl/download/doc_details/89-rklibavr-uniwersalna-biblioteka-dla-avr-gcc, T. Wasilczyk, Dokumentacja biblioteki tAvrLib, http://tAvrLib.wasilczyk.pl. Wersja z dn. 21.01.2012 r.