czwartek, 6 czerwca 2019

[1] STM32F4 - Biblioteka LL - Przerwania zewnętrzne oraz przerwania od licznika

W tym poście chciałbym opisać integrację timera oraz przerwań zewnętrznych obsługujących biblioteki LL.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]

Cube Mx:


Ustawienia Timera:


Konfiguracja przycisku:


Uruchomienie przerwań:


Po uruchomieniu wszystkich potrzebnych funkcji należy się upewnić, że zostały uruchomione biblioteki LL:


Przerwania od przycisku:


Inicjalizacja diod oraz przycisku podłączonego do przerwania:

  1. static void MX_GPIO_Init(void)
  2. {
  3.   LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
  4.   LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  5.   /* GPIO Ports Clock Enable */
  6.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
  7.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  8.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
  9.   /**/
  10.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15);
  11.   /**/
  12.   LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
  13.   /**/
  14.   EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0;
  15.   EXTI_InitStruct.LineCommand = ENABLE;
  16.   EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
  17.   EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING_FALLING;
  18.   LL_EXTI_Init(&EXTI_InitStruct);
  19.   /**/
  20.   LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_NO);
  21.   /**/
  22.   LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT);
  23.   /**/
  24.   GPIO_InitStruct.Pin = LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15;
  25.   GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  26.   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  27.   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  28.   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  29.   LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  30. }

Przerwanie od przycisku wygenerowane jest na zbocze narastające jak i opadające.

Uruchomienie przerwań jeśli nie uruchomiliśmy ich w Cubex poprzez zaznaczenie opcji EXTI line0 interrupt:

  1. static void EnableBtnInterrupt()
  2. {
  3.     NVIC_SetPriority(EXTI0_IRQn,0);
  4.     NVIC_ClearPendingIRQ(EXTI0_IRQn);
  5.     NVIC_EnableIRQ(EXTI0_IRQn);
  6. }

Jeśli zostały uruchomione przerwania to funkcja obsługująca przerwanie zostanie zdefiniowana w pliku stm32f4xx_it.h. Natomiast funkcja inicjalizująca piny będzie zawierała procedurę uruchomienia przerwań:

  1. static void MX_GPIO_Init(void)
  2. {
  3.   LL_EXTI_InitTypeDef EXTI_InitStruct = {0};
  4.   LL_GPIO_InitTypeDef GPIO_InitStruct = {0};
  5.   /* GPIO Ports Clock Enable */
  6.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOH);
  7.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOA);
  8.   LL_AHB1_GRP1_EnableClock(LL_AHB1_GRP1_PERIPH_GPIOD);
  9.   /**/
  10.   LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15);
  11.   /**/
  12.   LL_SYSCFG_SetEXTISource(LL_SYSCFG_EXTI_PORTA, LL_SYSCFG_EXTI_LINE0);
  13.   /**/
  14.   EXTI_InitStruct.Line_0_31 = LL_EXTI_LINE_0;
  15.   EXTI_InitStruct.LineCommand = ENABLE;
  16.   EXTI_InitStruct.Mode = LL_EXTI_MODE_IT;
  17.   EXTI_InitStruct.Trigger = LL_EXTI_TRIGGER_RISING;
  18.   LL_EXTI_Init(&EXTI_InitStruct);
  19.   /**/
  20.   LL_GPIO_SetPinPull(GPIOA, LL_GPIO_PIN_0, LL_GPIO_PULL_NO);
  21.   /**/
  22.   LL_GPIO_SetPinMode(GPIOA, LL_GPIO_PIN_0, LL_GPIO_MODE_INPUT);
  23.   /**/
  24.   GPIO_InitStruct.Pin = LL_GPIO_PIN_12|LL_GPIO_PIN_13|LL_GPIO_PIN_14|LL_GPIO_PIN_15;
  25.   GPIO_InitStruct.Mode = LL_GPIO_MODE_OUTPUT;
  26.   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW;
  27.   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL;
  28.   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO;
  29.   LL_GPIO_Init(GPIOD, &GPIO_InitStruct);
  30.   /* EXTI interrupt init*/
  31.   NVIC_SetPriority(EXTI0_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  32.   NVIC_EnableIRQ(EXTI0_IRQn);
  33. }

Obsługa przerwania:

  1. void EXTI0_IRQHandler(void)
  2. {
  3.     if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_0) != RESET)
  4.     {
  5.         LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_0);
  6.         GPIO_EXTI_Callback_Line0();
  7.     }
  8. }
  9. void GPIO_EXTI_Callback_Line0(void)
  10. {
  11.     LL_GPIO_TogglePin(GPIOD, LL_GPIO_PIN_12);
  12.     LL_GPIO_TogglePin(GPIOD, LL_GPIO_PIN_13);
  13.     LL_GPIO_TogglePin(GPIOD, LL_GPIO_PIN_14);
  14.     LL_GPIO_TogglePin(GPIOD, LL_GPIO_PIN_15);
  15. }

W obsłudze przerwania czyścimy flagę informującą o wystąpieniu przerwania po czym przechodzimy do funkcji która zajmuje się obsługą diod po kliknięciu przycisku:

Timer:


Procedura uruchomienia timera wygląda następująco:

  1. static void MX_TIM2_Init(void)
  2. {
  3.   /* USER CODE BEGIN TIM2_Init 0 */
  4.   /* USER CODE END TIM2_Init 0 */
  5.   LL_TIM_InitTypeDef TIM_InitStruct = {0};
  6.   /* Peripheral clock enable */
  7.   LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_TIM2);
  8.   /* TIM2 interrupt Init */
  9.   NVIC_SetPriority(TIM2_IRQn, NVIC_EncodePriority(NVIC_GetPriorityGrouping(),0, 0));
  10.   NVIC_EnableIRQ(TIM2_IRQn);
  11.   /* USER CODE BEGIN TIM2_Init 1 */
  12.   /* USER CODE END TIM2_Init 1 */
  13.   TIM_InitStruct.Prescaler = 159;
  14.   TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP;
  15.   TIM_InitStruct.Autoreload = 25999;
  16.   TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1;
  17.   LL_TIM_Init(TIM2, &TIM_InitStruct);
  18.   LL_TIM_DisableARRPreload(TIM2);
  19.   LL_TIM_SetClockSource(TIM2, LL_TIM_CLOCKSOURCE_INTERNAL);
  20.   LL_TIM_SetTriggerOutput(TIM2, LL_TIM_TRGO_UPDATE);
  21.   LL_TIM_DisableMasterSlaveMode(TIM2);
  22.   /* USER CODE BEGIN TIM2_Init 2 */
  23.   /* USER CODE END TIM2_Init 2 */
  24. }

W przerwaniu od Timera sprawdzam czy flaga została ustawiona, następnie ją czyszczę i przechodzę do procedury zdeklarowanej w przerwaniu.

  1. void TIM2_IRQHandler(void)
  2. {
  3.   /* USER CODE BEGIN TIM2_IRQn 0 */
  4.     if(LL_TIM_IsActiveFlag_UPDATE(TIM2))
  5.     {
  6.         LL_TIM_ClearFlag_UPDATE(TIM2);
  7.         TIM2_Callback();
  8.     }
  9.   /* USER CODE END TIM2_IRQn 0 */
  10. }

Funkcja odpowiedzialna za obsługę przerwania od timera. Jej zadanie jest dosyć proste. Zapala poszczególne diody, gdy dojdzie do zapalenia 4 diod czeka dwa cykle, wyłącza wszystkie diody i przechodzi do ponownego zapalania.

  1. void TIM2_Callback(void)
  2. {
  3.     if(Tim2_DisplayDiodeCounter == 0)
  4.     {
  5.       LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_12);
  6.     }
  7.     else if(Tim2_DisplayDiodeCounter == 1)
  8.     {
  9.       LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_13);
  10.     }
  11.     else if(Tim2_DisplayDiodeCounter == 2)
  12.     {
  13.       LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_14);
  14.     }
  15.     else if(Tim2_DisplayDiodeCounter == 3)
  16.     {
  17.       LL_GPIO_SetOutputPin(GPIOD, LL_GPIO_PIN_15);
  18.     }
  19.     else if(Tim2_DisplayDiodeCounter == 6)
  20.     {
  21.         LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_12);
  22.         LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_13);
  23.         LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_14);
  24.         LL_GPIO_ResetOutputPin(GPIOD, LL_GPIO_PIN_15);
  25.     }
  26.     Tim2_DisplayDiodeCounter++;
  27.     if(Tim2_DisplayDiodeCounter>6){
  28.         Tim2_DisplayDiodeCounter=0;
  29.     }
  30. }

Cały projekt można pobrać z dysku Google pod tym linkiem.