[PROJEKT] STM32Cube graficzny konfigurator STM32
Wysoką częstotliwość taktowania uzyskuje się w układzie powielania częstotliwości z pętlą PLL (Main PLL), gdzie wejściowa częstotliwość jest najpierw powielana przez 336, a potem dzielona przez 2. Sygnał z wyjścia układu PLL podawany jest na selektor System Clock Mux.
Oprócz konfigurowania ścieżki sygnału taktowania zakładka Clock Configuration pokazuje na bieżąco jakie częstotliwości są ustawione dla wszystkich bloków mikrokontrolera: rdzenia, magistrali AHB, modułów peryferyjnych APAB1, APB2 itp.
Po wygenerowaniu kodu ustawienia z zakładki Clock Configurator są zapisywane w funkcji SystemClockConfig umieszczonej w pliku main.c – listing 3.
List. 3. Ustawienia taktowania mikrokontrolera
/** System Clock Configuration */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; HAL_RCC_OscConfig(&RCC_OscInitStruct); RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_PCLK1 |RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5); }
Moduł STM32F4DISCOVERY ma zamontowane 4 diody LED podłączone do linii portu PD:
- zielona jest podłączona do portu PD12,
- pomarańczowa jest podłączona do portu PD13,
- czerwona jest podłączona do portu PD14,
- niebieska jest podłączona do portu PD15.
Jeżeli w projekcie wybierzemy wsparcie tego modułu, to w oknie Pinout wyprowadzenia tych linii portów zostaną zdefiniowane jako linie wyjściowe i opisane etykietami z nazwami diod – rysunek 8.
Spróbujemy teraz wykonać tak projekt by cyklicznie zapalał i gasił jedną lub kilka diod. Do migania diodami LED będzie nam potrzebne odliczanie opóźnienia. Można to zrobić programowo, ale lepszym wyjściem jest użycie licznika i systemu przerwań. Do odliczania opóźnień wybrałem licznik TIM10. Żeby go użyć, w zakładce Pinout trzeba wybrać TIM10 i zaznaczyć Activated. Wtedy ikona TIM10 pojawi się z okienku Control zakładki Configuration – rysunek 9.
Wybrany licznik TIM 10 trzeba teraz skonfigurować. Jak pokazano na rysunku 7, liczniki są taktowane częstotliwością 84 MHz z APB1. TIM10 jest 16-bitowym licznikiem z funkcją automatycznego przeładowania oraz z 16-bitowym preskalerem – rysunek 10.
W trybie zliczania UP (upcounting) licznik zlicza od zera do wartości zapisanej do rejestru Auto-reload register. Kiedy zawartość licznika i rejestru Auto – reload są sobie równe, to rejestr licznika jest zerowany i odliczanie rozpoczyna się od nowa. W czasie zerowania licznika może być zgłaszane przerwanie.
Za pomocą STM32CubeMX skonfigurujemy licznik TIM10 tak by zgłaszał przerwanie co 1 ms, czyli 1000 razy na sekundę.
W pierwszym kroku trzeba ustawić zliczanie w górę (UP) i preskaler TIM10 na 82, tak by na wejściu rejestru licznika była częstotliwość równa 1 MHz. Jeżeli teraz do Auto-reload register wpiszemy wartość 1000, to licznik będzie się przepełniał z częstotliwością 1 MHz/1000 = 1 kHz, czyli co 1 ms. W zakładce Configuration klikamy na ikonkę TIM10 i otwiera się okno TIM10 Settings. W zakładce Parameter Settings ustawiamy:
- wartość preskalera =82
- tryb zliczania UP
- okres zliczania =1000
Żeby licznik zgłaszał przerwanie trzeba to ustawić z w zakładce NVIC Settings – TIM10 Update Interrupt and TOM10 global Interrupt Enabled. Oba ustawienia zostały pokazane na rysunku 11.
Na ich podstawie STM32CubeMX wygeneruje kod zawierający funkcję inicjalizacji licznika TIM10 pokazany na listingu 4.
List. 4. Inicjalizacja licznika TIM10
void MX_TIM10_Init(void) { htim10.Instance = TIM10; htim10.Init.Prescaler = 82; htim10.Init.CounterMode = TIM_COUNTERMODE_UP; htim10.Init.Period = 1000; htim10.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; HAL_TIM_Base_Init(&htim10); __TIM10_CLK_ENABLE(); }
Funkcję obsługi przerwania pokazano na listingu 5.
List. 5. Funkcja obsługi przerwania od TIM10
void TIM1_UP_TIM10_IRQHandler(void) { /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */ /* USER CODE END TIM1_UP_TIM10_IRQn 0 */ HAL_TIM_IRQHandler(&htim10); /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */ } /* USER CODE END TIM1_UP_TIM10_IRQn 1 */ }
Należy pamiętać o ważnej rzeczy: kod generowany przez STM32CubeMX zawiera jedynie procedury konfigurujące układy peryferyjne. Jeżeli chcemy by te układy zaczęły działać użytkownik musi je samodzielnie programowo uruchomić wywołując odpowiednią funkcję. W naszym przypadku timer zostanie skonfigurowany, ale nie będzie zliczać impulsów wejściowych. Żeby wszystko działało musimy wywołać funkcję HAL_TIM_Base_Start_IT(&htim10); uruchamiającą licznik i przerwania.
Na końcu pozostaje w obsłudze przerwania zapalać i gasić wybrana diodę. Ponieważ przerwania jest zgłaszane co 1 ms, to trzeba odliczyć 1000 przerwań by dioda zapalała się i gasła co 1 sekundę. Do tego celu zostanie użyta zmienna globalna blink – listing 6.
List. 6. Procedura przerwania od przepełnienia TIM10
int blink=0; void TIM1_UP_TIM10_IRQHandler(void) { /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 0 */ /* USER CODE END TIM1_UP_TIM10_IRQn 0 */ HAL_TIM_IRQHandler(&htim10); /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */ ++blink; if(blink==1000) { HAL_GPIO_TogglePin(GPIOD, GPIO_PIN_15); blink=0; } /* USER CODE END TIM1_UP_TIM10_IRQn 1 */ }