Temat 3: Implementacja programu korzystającego z interfejsu UART, realizacja obsługi przerwań Celem ćwiczenia jest zapoznanie się ze sposobem obsługi przerwań generowanych przez źródła zewnętrzne i wbudowany w μc interfejs szeregowy. W trakcie zajęć, realizowana jest również asynchroniczna komunikacja z PC poprzez interfejs UART. 1. Hierarchia systemu obsługi przerwań Układy STM32F10x zostały wyposażone w sprzętowy kontroler przerwań NVIC (Nested Vectored Interrupt Controller). Umożliwia on obsługę przerwań pochodzących od urządzeń zewnętrznych, jak i od wbudowanych zasobów peryferyjnych. Do konfiguracji priorytetów (poziomów) obsługi przerwań wykorzystano 4 bity kodujące: główny priorytet grupowy (PreemptionPriority) i priorytety podrzędne, tj. podpriorytety (Subpriority). W efekcie, w zależności od wybranej liczby bitów (z wykorzystaniem funkcji NVIC_PriorityGroupConfig() ), przeznaczonych do kodowania priorytetów grupowych (NVIC_PriorityGroup0.. NVIC_PriorityGroup3), dostępne są różne zestawy poziomów przerwań przedstawione w Tab.1. Tab.1 Zestaw priorytetów dostępnych dla μc serii STM32F1xx Liczba priorytetów NVIC_PriorityGroup0 NVIC_PriorityGroup1 NVIC_PriorityGroup2 NVIC_PriorityGroup3 głównych 0 (0b) 2 (1b) 4 (2b) 8 (3b) podrzędnych 16 (4b) 8 (3b) 4 (2b) 2 (1b) Przy obsłudze wywołanego przerwania obowiązują poniższe zasady: Pierwszeństwo ma przerwanie o najniższym numerze grupy (najwyższy priorytet), tj. jest obsługiwane w pierwszej kolejności i powoduje wywłaszczenie obsługi przerwań o niższych priorytetach. W przypadku przerwań z tej samej grupy, kolejność obsługi określona jest przez podpriorytet (im niższy numer, tym wyższy reżim obsługi), lecz nie obowiązuje zasada wywłaszczenia obsługi w ramach tej samej grupy. Przy wywołaniu przerwań o identycznych priorytetach, pierwsze zostanie obsłużone to, które posiada niższy numer przerwania (tablica wektorów zdefiniowana w pliku stm32f10x.h w sekcji STM32 specific Interrupt Numbers ). 2. Inicjalizacja kontrolera i obsługa przerwań Szablon projektu wykorzystywany podczas ćwiczeń laboratoryjnych kieruje tablicę wektorów przerwań do pamięci RAM, więc przed włączeniem ich obsługi należy ustawić adres dostępu z wykorzystaniem funkcji NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0). W przypadku przechowywania tablicy w pamięci FLASH należy użyć argumentu NVIC_VectTab_FLASH. Następnie tryb pracy przerwania jest ustawiany poprzez wywołanie funkcji NVIC_Init(), której argumentem jest referencja do struktury NVIC_InitTypeDef określającej parametry danego przerwania. Pola struktury są następujące:
NVIC_IRQChannel (określa źródło przerwania), NVIC_IRQChannelPreemptionPriority (priorytet grupowy), NVIC_IRQChannelSubPriority (podpriorytet), NVIC_IRQChannelCmd (ENABLE, DISABLE). Przykładowo, gdy chcemy uzyskać obsługę przerwania wywołanego przez interfejs transmisji szeregowej USART1 po odebraniu znaku ASCII (z maksymalnym priorytetem wykonania), należy wywołać funkcję inicjalizującą NVIC podaną poniżej. void NVIC_Configuration(void) NVIC_InitTypeDef NVIC_InitStructure; //deklaracja struktury NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0); //adres do tablicy NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //grupa priorytetów NVIC_InitStructure.NVIC_IRQChannel= USART1_IRQn; //wypełnienie pól struktury NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; NVIC_InitStructure.NVIC_IRQChannelSubPriority=0; NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; NVIC_Init(&NVIC_InitStructure); //wywołanie funkcji inicjalizującej NVIC W efekcie wywołania przerwania realizowana jest funkcja jego obsługi, której nagłówek i kod należy umieścić odpowiednio w plikach stm32f10x_it.h i stm32f10x_it.c projektu.unikatowa nazwa funkcji obsługującej przerwanie pochodzące od danego źródła jest zdefiniowana w pliku startup_stm32f10x_xx.s. Na przykład, przy obsłudze przerwania pochodzącego od interfejsu USART1 należy utworzyć funkcję o nagłówku void USART1_IRQHandler(void) zawierającą kod realizujący pożądany algorytm. Przy czym należy pamiętać, aby po dokonaniu obsługi skasować odpowiedni znacznik przerwania (np. dla USART z użyciem funkcji USART_ClearITPendingBit() ). 3. Konfiguracja obsługi przerwania zewnętrznego Oprócz przerwań generowanych wewnątrz układu μc ich źródłem mogą być urządzenia zewnętrzne dołączone do portów GPIO, wówczas są to przerwania typu EXTI (EXTernalInterrupt). Pierwszym krokiem przy realizacji obsługi przerwania pochodzącego z bitu (bitów) GPIO jest odpowiednia inicjalizacja portu (zgodnie z opisem podanym w poprzedniej instrukcji laboratoryjnej). Następnie jest definiowane źródło przerwania zewnętrznego przez wywołanie funkcji GPIO_EXTILineConfig(), której argumentami są identyfikatory portu GPIO i bitu. Ponadto, inicjalizacja przerwań EXTI wymaga dodatkowej konfiguracji poprzez wywołanie funkcji EXTI_Init() z argumentem referencji do struktury typu EXTI_InitTypeDef. Pola tej struktury są następujące:
EXTI_Line EXTI_Mode (EXTI_Linex, x=1..15, numer bitu portu), (EXTI_Mode_Interrupt, EXTI_Mode_Event), EXTI_Trigger (EXTI_Trigger_Rising, EXTI_Trigger_Falling, EXTI_Trigger_Rising_Falling), EXTI_LineCmd (ENABLE, DISABLE). Oprócz powyższych operacji przy konfiguracji kontrolera NVIC należy podać odpowiedni kanał jako źródło sygnału przerwania: NVIC_IRQChannel (EXTIx_IRQn, x=0..4, EXTI9_5_IRQn, EXTI15_10_IRQn). PA0 PB0 PC0 PD0 PE0 EXTI0_IRQn PA4 PB4 PC4 PD4 PE4 EXTI4_IRQn PA5..9 PB5..9 PC5..9 PD5..9 PE5..9 EXTI9_5_IRQn PA10..15 PB10..15 PC10..15 PD10..15 PE10..15 EXTI15_10_IRQn Rys.1 Struktura kanałów przerwań pochodzących od bitów portów GPIO Dla układów STM32F1xx dostępnych jest siedem kanałów sygnału przerwań od pinów GPIO, ich struktura została zilustrowana na Rys.1. Bity portów GPIOA..GPIOE o pozycjach 0..4 generują cztery niezależne przerwania w kanałach EXTIx_IRQn o adekwatnym numerze x. Natomiast pozostałe sygnały przerwań zostały pogrupowane na pochodzące od bitów 5..9 (kanał EXTI9_5_IRQn) oraz od bitów 10..15 (kanał EXTI15_10_IRQn). Z uwagi na wspólne kanały przerwań, pierwszą operacją realizowaną przy ich obsłudze powinna być identyfikacja źródła przerwania z użyciem funkcji EXTI_GetITStatus(), której argumentem jest identyfikator linii portu EXTI_Line0..EXTI_Line19. Oczywiście, na końcu odpowiedniej funkcji obsługi przerwania (EXTIx_IRQHandler(), x=0..4, EXTI9_5_IRQHandler(), EXTI15_10_IRQHandler() ) należy umieścić komendę kasowania znacznika wywołania, tj. EXTI_ClearITPendingBit(). Poniżej jest umieszczony przykładowy kod funkcji wywoływanej przez przerwanie, którego źródłem jest bit 12 portu GPIO. void EXTI15_10_IRQHandler(void) if(exti_getitstatus(exti_line12)!= RESET) //tutaj należy umieścić algorytm obsługi EXTI_ClearITPendingBit(EXTI_Line12); //kasowanie znacznika
4. Transmisja szeregowa asynchroniczna z wykorzystaniem interfejsu UART Bit startu Bity danych Bit (nie)parzystości D 0 D 1 D 2 D 3 D 4 D 5 D 6 D 7 ER Bit(y) stopu Rys.2 Protokół szeregowej transmisji danych przez interfejs UART Idea protokołu transmisji szeregowej asynchronicznej przez interfejs UART została zilustrowana na Rys.2. Transmisja jest uaktywniana po wykryciu bitu startu (H->L), następnie są przesyłane stany logiczne ośmiu bitów danych D 0..D 7 i bitu kontroli błędów transmisji ER (bit parzystości lub nieparzystości), koniec transmisji jest wyznaczany przesłaniem od 1 do 2 bitów stopu (H). Warto dodać, że dla standardu RS232 poziom logiczny odpowiadający stanowi wysokiemu TTL to ok.-12v, a niskiemu ok.+12v (konwersję z poziomów TTL można uzyskać np. za pomocą układu MAX232). Układy μc serii STM32F10x posiadają do pięciu wbudowanych interfejsów UART, przy czym trzy z nich (USART1..USART3) obsługują również transmisję synchroniczną. Pierwszym krokiem przy konfiguracji tego interfejsu jest włączenie odpowiedniego zegara taktującego za pomocą funkcji RCC_APB2PeriphClockCmd() z parametrem wskazującym wybrany UART. Następnie resetujemy urządzenie wywołując funkcję RCC_APB2PeriphResetCmd() dwukrotnie (pierwszy raz z parametrem ENABLE, a następnie DISABLE). Teraz można przejść do wypełnienia pól struktury typu USART_InitTypeDef, którą przesyłamy jako argument funkcji inicjalizującej USART_Init(). Pola struktury są następujące : USART_BaudRate USART_HardwareFlowControl USART_Mode (prędkość transmisji podawana w bps), (USART_HardwareFlowControl_None, USART_HardwareFlowControl_RTS, USART_HardwareFlowControl_CTS, USART_HardwareFlowControl_RTS_CTS), (USART_Mode_Rx, USART_Mode_Tx), USART_Parity (USART_Parity_No, USART_Parity_Even, USART_Parity_Odd), USART_StopBits (USART_StopBits_1, USART_StopBits_0_5, USART_StopBits_2, USART_StopBits_1_5), USART_WordLength (USART_WordLength_8b, USART_WordLength_9b). Po wywołaniu funkcji inicjalizującej, USART można załączyć za pomocą USART_Cmd() i w przypadku wykorzystywania przerwań należy je odblokować przez wywołanie funkcji USART_ITConfig(). Zestaw funkcji obsługujących układy USART, wraz z ich krótkim opisem, jest umieszczony w module stm32f10x_usart.c biblioteki API (którą należy dołączyć do projektu edytując plik stm32f10x_conf.h).
Przykładowo, w celu odczytu odebranego znaku korzystamy z funkcji USART_ReceiveData(), natomiast do wysyłania służy USART_SendData(). Zadanie 1 Napisać program zliczający liczbę przyciśnięć drążka joysticka Joy1 wykorzystujący przerwanie generowane przez urządzenie zewnętrzne (EXTI) po wykryciu zbocza opadającego sygnału. Po uruchomieniu, program powinien czekać na pierwsze wciśnięcie drążka, a następnie zliczać ich liczbę (równą liczbie wywołań przerwania) przez kolejne 2 sekundy. W efekcie, po upłynięciu czasu pomiaru, program ma załączyć tyle diod D0..D7 ile zarejestrował przyciśnięć (lub wszystkie w przypadku, gdy było ich więcej niż 8). W pierwszej wersji, program należy zrealizować bez wprowadzania zabezpieczeń przeciwko drganiom styków, a następnie z pętlą opóźniającą odczyt stanu przycisku, zapewniającą poprawę efektów. Połączenia na płycie ZL30ARM: Anody LED D0..D7 Joy1 TACT T GPIO PA0..PA7 GPIOPB15 Wskazówki: - Do komunikacji pomiędzy modułami stm32f10x_it.c i main.c można skorzystać z ośmiobitowej zmiennej globalnej (extern), której liczba ustawionych bitów odpowiada liczbie wykrytych wciśnięć. Przydatne fragmenty kodu źródłowego: void EXTI15_10_IRQHandler(void) if(exti_getitstatus(exti_line15)!= RESET) : EXTI_ClearITPendingBit(EXTI_Line15); Zadanie 2 Program umożliwiający sterowanie (załączanie/wyłączanie) poprzez interfejs RS232 (9600bps, brak sprzętowej kontroli transmisji, 8 bitów danych i brak bitu (nie)parzystości) ośmioma urządzeniami (LED0..LED7) z konsoli terminala szeregowego uruchomionego w PC (np. Br@y++ Terminal v1.9b jest dostępny na stronie przedmiotu). Program ma wykorzystywać przerwania generowane przez blok USART1 w momencie odebrania znaku. Przyjąć, że komendy sterujące wykorzystują następujące zbiory znaków ASCII: Z 1 = a.. h włączające oraz Z 2 = 1.. 8 wyłączające kolejne urządzenia (LED0..LED7). Nadesłanie błędnego znaku (nieobsługiwany kod) ma wywoływać krótki sygnał dźwiękowy (fala prostokątna o częstotliwości ~1kHz podawana na Spk1 na ~0.5s). Połączenia na płycie ZL30ARM: Anody LED D0..D7 GPIO PA0..PA7 Głośnik SPK Con7 GPIO PA8 CON7 MAX232 RxD USART1 RX (GPIO PA10) Płytę ZL30ARM połączyć z PC kablem DB9M/DB9F4 (bez przeplotu, przedłużacz portu COM)
Przydatne fragmenty kodu źródłowego: void Beep( I uint32_t time) uint32_t i; for (i=0;i<time;i++) GPIO_ResetBits(GPIOA,GPIO_Pin_8); Delay_it(2500); GPIO_SetBits(GPIOA,GPIO_Pin_8); Delay_it(2500); Zadanie 3 Program przesyłający poprzez interfejs RS232 (9600bps, brak sprzętowej kontroli transmisji, 8 bitów danych i brak bitu (nie)parzystości) znaki wybierane na klawiaturze matrycowej KAmodKB4x4 do konsoli terminala uruchomionego w PC (np. Br@y++ Terminal v1.9b jest dostępny na stronie przedmiotu). Program ma wykorzystywać przerwania generowane przez linie kolumn klawiatury (COL1..COL4) aktywowane zboczem opadającym. Połączenia na płycie ZL30ARM: Kolumny klawiaturycol1..col4 GPIO PB0,PB1,PB2,PB3 Wiersze klawiatury ROW1..ROW4 GPIO PB4,PB5,PB6,PB7 CON7 MAX232 TxD USART1 TX (GPIO PA9) Płytę ZL30ARM połączyć z PC kablem DB9M/DB9F4 (bez przeplotu, przedłużacz portu COM) Wskazówki: - Po wykryciu przerwania, funkcja obsługi sprawdza wybrany wiersz przez detekcję linii (ROW1..ROW2) transmitującej stan niski. - Z uwagi na alternatywne wykorzystanie linii PB3 i PB4 (standardowo służą do transmisji interfejsem JTAG), pierwsza wersja programu powinna uwzględniać jedynie klawisze z kwadratu o przekątnej 48# (tj.col1..col3, ROW2..ROW4). - Po uruchomieniu pierwszej wersji programu, należy zmodyfikować go do wersji finalnej przez dodanie obsługi pozostałych klawiszy (COL4, ROW1) i wyłączenie interfejsu JTAG z wykorzystaniem funkcji GPIO_PinRemapConfig(GPIO_Remap_SWJ_Disable,ENABLE); oraz załączenie bloku AFIO RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);. Przydatne fragmenty kodu źródłowego: void EXTI0_IRQHandler(void) if(exti_getitstatus(exti_line0)!= RESET) : EXTI_ClearITPendingBit(EXTI_Line0 EXTI_Line1 EXTI_Line2 EXTI_Line3);
Literatura [1] http://platforma.polsl.pl/rau3/ [2] Marek Galewski, "STM32 - Aplikacje i ćwiczenia w języku C", ISBN 978-83-60233-82-5, Wydawnictwo BTC, Legionowo 2011. [3] Krzysztof Paprocki, "Mikrokontrolery STM32 w praktyce", ISBN 978-83-60233-52-8, Wydawnictwo BTC, Legionowo 2009, 2011 wyd. I poprawione. [4] http://pl.wikipedia.org/wiki/rs-232 [5] Reference Manual-RM0008