Ćwiczenia 6,7: Zastosowania liczników SysTick, RTC oraz uniwersalnych Celem ćwiczeń 6 i 7 jest zapoznanie się z konfiguracją i zastosowaniem timerów. W szczególności studenci poznają sposoby okresowego wywoływania funkcji, pomiaru czasu oraz sterowania z modulacją PWM. 1. Klasyfikacja liczników w układach STM32F10x Układy STM32F10x zostały wyposażone są w kilka odmian liczników o odmiennych zastosowaniach i możliwościach: 1) SysTick podstawowych licznik systemowy do odmierzania czasu 2) TIM1, TIM2,.. liczniki uniwersalne o różnych funkcjach, generujące różne przerwania 3) Watchdog liczniki czuwające 4) RTC zegar czasu rzeczywistego Liczniki TIMx różnią się między sobą funkcjami i zakresem zastosowań. 2. Konfiguracja liczników do realizacji wybranych zadań 2.1 Timer SysTick SysTick stanowi 24.-bitowy licznik zliczający w dół od zadanej wartości. Osiągnięcie wartości zero powoduje wygenerowanie przerwania i rozpoczęcie nowego cyklu odliczania, co pozwala na odmierzanie określonych odcinków czasu. (Uwaga: licznik, timer te nazwy będą w dalszej części instrukcji używane wymiennie). W celu wykorzystania licznika do pomiaru czasu należy określić źródło zegara dla licznika oraz wartość początkową wpisaną do licznika. Domyślną wartością częstotliwości zliczania jest częstotliwość HCLK (72 MHz). Częstotliwość zliczania można zmniejszyć do wartości ośmiokrotnie mniejszej (9 MHz) poleceniem: SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8) Drugi parametr wpływający na częstotliwość przerwań wartość początkowa, ustawiana jest w liczniku za pomocą polecenia SysTick_Config. W celu wywołania przerwania co czas t 0 należy ustawić wartość 9 000 000 * t 0. Realizowane cyklicznie zadanie należy wprowadzić do funkcji obsługi przerwania SysTick_Handler(). Przykładowo, zadanie polegające na cyklicznym przełączaniu stanu bitu co 2 sekundy, może być zrealizowane następująco: int main(void) { /* konfiguracja GPIO, SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); SysTick_Config(9000);... /* obsługa przerwania w stm32f10x.it.c */ void SysTick_Handler(void) { if (TDelay==2000) { TDelay =0; GPIO_WriteBit(GPIOA, GPIO_Pin_8, (BitAction) (1-GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8 )); TDelay++;
2.2 Liczniki uniwersalne Podstawową funkcją liczników uniwersalnych jest odliczanie czasu. Są one jednak wyposaŝone w szereg funkcji poszerzających zakres ich zastosowań. MoŜliwe jest zliczanie w górę, w dół lub naprzemiennie. Pozwalają one na generowanie impulsów o określonej długości, generowanie impulsów PWM oraz pomiar współczynnika wypełnienia. MoŜliwe jest zliczanie impulsów zewnętrznych. Liczniki uniwersalne moŝna łączyć wzajemnie, mogą takŝe współpracować z innymi elementami mikrokontrolera. Rys. 1 Schemat blokowy licznika TIM1 Rys. 2 Generowanie zdarzeń w liczniku Update i CC
Liczniki uniwersalne (schemat blokowy rys. 1) mogą być taktowane sygnałem APB2( TIM1) lub APB1 (pozostałe). Sygnał taktujący jest dzielony w liczniku o podziale od 1 do 2^16=65536, co pozwala z zegara 72 MHz uzyskać częstotliwości w granicach od najmniejszej 1100Hz do pełnych 72 MHz. Ponadto zastosowany ośmiobitowy dzielnik powtórzeń pozwala zmniejszyć częstotliwość zgłaszanych zdarzeń. Liczniki mogą zgłaszać zdarzenia Update po zakończeniu cyklu zliczania (maksimum przy zliczaniu w górę, zero przy zliczaniu w dół, maksimum lub zero przy zliczaniu w górę i w dół). Liczniki uniwersalne posiadają 4 kanały pozwalające na porównanie ich stanu z zadaną wartością odniesienia. Dla poszczególnych kanałów wartości te zapisywane są w rejestrze CCR (Capture Compare Register). Po osiągnięciu wartości odniesienia licznik zgłasza zdarzenie CC ( rys 2). Kanały od 1 do 3 posiadają po dwa wyjścia z układem generowania czasu martwego. MoŜna ustawiać polaryzację tych wyjść i przesunięcia w czasie między zmianami stanu tych 2 wyjść. Konfiguracja dla wybranych problemów zostanie pokazana na przykładach. 2.2.1 Cykliczne wywoływanie funkcji Rozwiązanie zostanie przedstawione na problemie przełączania stanu bitu (patrz p.2.1). Niezbędne jest włączenie w pliku stm32f10x_conf.h biblioteki obsługującej liczniki: stm32f10x_tim.h i dodanie tej biblioteki do grupy StdPeriphDrv, skonfigurowanie przerwań (NVIC: przerwanie TIM1_UP_IRQn) i uniwersalnych wejść /wyjść GPIO oraz włączenie zegara dla TIM1: RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE); W pliku main.c wywoływana będzie funkcja konfiguracji licznika, której przykład znajduje się poniżej, natomiast przełączanie bitu odbywać się będzie za pomocą funkcji obsługi przerwania TIM1_UP_IRQn. Funkcja konfiguracji licznika: void TIM_Config(void) { TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Prescaler=7199; //Dzielnik przez 7200 daje częstotliwość 10kHz TIM_TimeBaseStructure.TIMPeriod=20000; //okres licznika odpowiadający 2 sekundom TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // ustawienie układu czasu martwego TIM_TimeBaseStructure.TIM_RepetitionCounter=0; // kaŝde przepełnienie generuje UP event TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up; // zliczanie w górę TIM_TimeBaseInit(TIM1,& TIM_TimeBaseStructure); TIM_ITConfig(TIM1, TIM_IT_Update,ENABLE); // właczenie przerwania od przepełnienia TIM_Cmd(TIM1, ENABLE); // włączenie licznika W pliku stm32f10x_it.c naleŝy uzupełnić funkcję obsługi przerwania: void_tim1_up_irqhandler() { if (TIM_GetITStatus(TIM1, TIM_IT_Update)!= RESET) { TIM_ClearITPendingBit(TIM1, TIM_IT_Update); GPIO_WriteBit(CPIOA, GPIO_Pin_8, (BitAction) (1-GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8 )); 2.2.2 Konfiguracja kanału CC W celu ustawienia przerwania od zdarzenia CC naleŝy uzupełnić konfigurację licznika o parametry dla wybranego kanału i uzupełnić funkcję obsługi przerwania. Dodatkowe linie w pliku TIM_Config pozwalające na wywołanie przerwania z kanału CC1 po 1 sekundzie od startu licznika są następujące: TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCInitStructure.TIM_OCMode = TIM_OCModeTiming; // tryb kanału TIM_OCInitStructure.TIM_OutputState = TIM_ OutputState_Enable; // właczenie sygnału TIM_OCInitStructure.TIM_Pulse =10000; // poziom CC w taktach = 1 sekunda TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High; //polaryzacja wyjścia TIM_OC1Init(TIM1, &TIM_OCInitStructure); TIM_OC1PreloadConfig(TIM1, TIM_OCPreload_Disable); //ustawienie parametrów bez opóźn.
TIM_ITConfig(TIM1, TIM_IT_CC1, ENABLE); // właczenie przerwania z kanału 1 W pliku stm32f10x_it.c naleŝy uzupełnić funkcję: void TIM1_CC_IRQHanler (void) { if (TIM_GetITStatus(TIM1, TIM_IT_CC1)!= RESET); { TIM_ClearITPendingBit(TIM1, TIM_IT_CC1);...... 2.2.3 Generowanie sygnału PWM Modulacja szerokości impulsów PWM moŝe być wykorzystana do sterowania mocą dostarczaną do obciąŝenia. Działanie takiej regulacji moŝna zaobserwować np. na diodach LED sterowanych zmodulowanym sygnałem. Poprzez włączenie trybu funkcji alternatywnych za pomocą bitów B8 i B9 moŝna sterować diody LED (([2], str.347). PoniŜsze fragmenty kodu ilustrują sposób konfiguracji licznika 4 do sterowania diodami LED. TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_OCInitTypeDef TIM_OCInitStructure; TIM_TimeBaseStructure.TIM_Period = 9999u1; TIM_TimeBaseStructure.TIM_Prescaler = 0; TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); /* PWM1 Mode configuration: Channel1 */ TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 5000u1; TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; TIM_OC3Init(TIM4, &TIM_OCInitStructure); TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable); /* PWM1 Mode configuration: Channel2 */ TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; TIM_OCInitStructure.TIM_Pulse = 3000u1; TIM_OC4Init(TIM4, &TIM_OCInitStructure); TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable); TIM_ARRPreloadConfig(TIM4, ENABLE); /* TIM4 enable counter */ TIM_Cmd(TIM4, ENABLE); RCCAPB!PeriphClockCmd(RCC_APB1Periph_TIM4, Enable); Zmiana jasności diody moŝe odbywać się np. jak poniŝej: TIM4->CCR3 = TIM4->CCR3+100; 2.2.4 Zliczanie impulsów zewnętrznych Zliczanie impulsów zewnętrznych moŝliwe jest poprzez wejścia uniwersalne, przy czym informacje o alternatywnych funkcjach poszczególnych bitów moŝna znaleźć w dokumentacji ([2], str.347). Przykładowo Bit A0 stanowi zewnętrzne wejście taktujące licznika drugiego TIM2_CH1_ETR. W celu konfiguracji tego licznika do zliczania przyciśnięć klucza podłączonego do PA0, w konfiguracji licznika po wypełnieniu pól struktury (preskaler, okres, tryb itd.) i zainicjowaniu licznika naleŝy zainicjować zewnętrzne taktowanie poleceniem: TIM_TIExternalClockConfig(TIM2, TIM_TS_TI1FP1, TIM_ICPolarity_Falling,OxF); i włączyć licznik TIM_Cmd(TIM2, ENABLE); Liczba wciśnięć moŝe być odczytana poleceniem:tim_getcapture1(tim2). 2.2.5 Wykorzystanie licznika do eliminacji drgań styków przycisków. Problem rozwiązywany jest przez powtórne sprawdzenie stanu przycisku po określonym czasie od wciśnięcia. JeŜeli stan linii jest nadal utrzymany to przyjmuje się, Ŝe przyciks został wciśnięty. Do
pomiaru czasu wykorzystany zostanie licznik (w przykładzie TIM2). W momencie wciśnięcia klawisza generowane jest przerwanie EXTI0_IRQn i uruchamiany zostaje licznik który generuje przerwanie po czasie 60 ms. Po przepełnieniu licznika w funkcji TIM2_IRQHandler kasowany jest znacznik przerwania, wyłączane jest licznik TIM_CMd(..,DISABLE) oraz przerwanie TIM_ITConfig(..,..,DISABLE) i następuje sprawdzenie stanu linii do której dołączony jest przycisk. W zaleŝności od stanu ustawia się odpowiednią zmienną. ([2], str. 160). 2.3 Zegar czasu rzeczywistego RTC Wbudowany zegar RTC pozwala na okresowe odczytywanie czasu i wywoływanie funkcji poprzez przerwania. W celu uruchomienia zegara RTC niezbędne jest włączenie bibliotek: stm32f10x_bkp.h, stm32f10x_pwr.h oraz stm32f10x_rtc.h. Należy więc w pliku stm32f10x_conf.h usunąć znaczniki komentarza przed poleceniem #include dla odpowiednich pozycji oraz włączyć do projektu te pliki (do grupy StdPeriphDrv). Przykładowa kod funkcji konfigurującej RTC podany jest poniżej: void RTC_Config(void); { PWR_Backup AccessCmd(ENABLE); RCC_LSEConfig(RCC_LSE_ON); while (RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET); RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE); RCC_RTCCLKCmd(ENABLE); RTC_WaitForSynchro(); RTC_WaitForLastTask(); // ustawienie preskalera z okresem 1 sekundy RTC_SetPrescaler(32767); RTC_WaitForLastTask(); // Włączenie przerwania sekundowego RTC_ITConfig(RTC_IT_SEC, ENABLE); RTC_WaitForLastTask(); RCC_ClearFlag(); Niezbędne jest także włączenie sygnału taktującego dla układów BKP i PWR: RCC_APB1PeriphClockCmd(RCC_APB1Periph_ BKP RCC_APB1Periph_PWR,, ENABLE); Po takiej konfiguracji można przystąpić do realizacji typowych zadań z wykorzystaniem zegara RTC. 2.3.1 Cykliczne wywoływanie funkcji. Załóżmy, że należy okresowo co 1 sekundę zmieniać stan wyjścia bitu 8 portu A. Można to zrealizować w oparciu o przerwanie generowane przez RTC. Należy więc skonfigurować port A oraz kontroler przerwań NVIC. W pliku stm32f10x_it.c należy uzupełnić funkcję obsługi przerwania: void RTC_IRQHandler(void) { if (RTC_GetITStatus(RTC_IT_SEC)!= RESET) { RTC_WaitFotLastTask(); RTC_ClearITPendingBit(RTC_IT_SEC); GPIO_WriteBit(CPIOA, GPIO_Pin_8, (BitAction) (1-GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_8 )); 2.3.2 Czas pracy systemu Wartość czasu pracy systemu w sekundach zwraca funkcja RTC_GetCounter(); W celu podaniu wartości czasu w godzinach, minutach i sekundach wartość czasu należy przeliczyć: volatile unsigned long int czasrtc; unsigned long int godziny, minuty,sekundy; Static u nsugned char czasrtctext(17)=( 0/0 ); czas_rtc= RTC_GetCounter(); sekundy= czas_rtc%60; minuty= czas_rtc/60; godziny=minuty/60;... // wartości te można przekształcić na tekst: sprintf((char *) czasrtctekst, %3i:%02i:%02i\0, godziny, minuty,sekundy);
Zadanie 1 (1) W oparciu o licznik SysTick zrealizować układ przełączający cztery diody LED, przy czym pierwsza dioda powinna przełączać się co 0,5 sekundy, druga co 1 sekundę, trzecia co 2 sekundy i ostatnia co 4 sekundy. (2) Należy rozbudować układ tak by możliwa była regulacja szybkości przełączania diod za pomocą 2 przycisków. Zadanie 2 Za pomocą modulacji PWM zrealizować regulację jasności świecenia diod. (1) Trzy diody powinny świecić z różną jasnością (2) Wprowadzić regulację jasności za pomocą przycisków. Zadanie 3 Zrealizować pomiar czasu od uruchomienia układu w postaci minuty:sekundy. Wyniki pomiaru powinny być wyświetlane za pomocą jednego z dostępnych w układzie wyświetlaczy lub przesyłane na terminal za pomocą złączą RS232. 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] Reference Manual-RM0008