关于嵌入式学习随笔->11《STM32CubeMX应用2-LED(通用I/O与定时中断配置)》
STM32CubeMX中配置GPIO基本操作
普通I/O口配置
应用HAL库实现LED的闪烁。
1、通过原理图查看自己的板子中关于LED的原理图。
2、在STM32CubeMX中找到对应的引脚,配置模式。如果只是普通I/O这里可以选择GPIO_Output。
PF14:GPIO_Output 普通引脚延时闪烁
配置名字引脚名
定时中断配置
PE11:TIM1_CH2 (用定时器中断的方法实现闪烁)
设置分频值和,重载值。
配置中断优先级
配置好后生成代码。
HAL库相关程序讲解
打开对应的程序可以查看相应生成的代码。
函数1:HAL_GPIO_WritePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)
例:根据电路原理图点亮LED。
HAL_GPIO_WritePin(LED_G_GPIO_Port,LED_G_Pin,GPIO_PIN_RESET);
函数2:HAL_Init()
HAL_StatusTypeDef HAL_Init(void) { /* Configure Flash prefetch, Instruction cache, Data cache */ #if (INSTRUCTION_CACHE_ENABLE != 0U) __HAL_FLASH_INSTRUCTION_CACHE_ENABLE(); #endif /* INSTRUCTION_CACHE_ENABLE */ #if (DATA_CACHE_ENABLE != 0U) __HAL_FLASH_DATA_CACHE_ENABLE(); #endif /* DATA_CACHE_ENABLE */ #if (PREFETCH_ENABLE != 0U) __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); #endif /* PREFETCH_ENABLE */ /* Set Interrupt Group Priority */ HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); /* Use systick as time base source and configure 1ms tick (default clock after Reset is HSI) */ HAL_InitTick(TICK_INT_PRIORITY); /* Init the low level hardware */ HAL_MspInit(); /* Return function status */ return HAL_OK; }
HAL_Init()
其中
__weak HAL_StatusTypeDef HAL_InitTick(uint32_t TickPriority) { /* Configure the SysTick to have interrupt in 1ms time basis*/ if (HAL_SYSTICK_Config(SystemCoreClock / (1000U / uwTickFreq)) > 0U) { return HAL_ERROR; } /* Configure the SysTick IRQ priority */ if (TickPriority < (1UL << __NVIC_PRIO_BITS)) { HAL_NVIC_SetPriority(SysTick_IRQn, TickPriority, 0U); uwTickPrio = TickPriority; } else { return HAL_ERROR; } /* Return function status */ return HAL_OK; }
HAL_InitTick(uint32_t TickPriority)
是一个若定义函数,在其他地方没有定义的的话,默认定时周期1ms,并使SysTick开始工作。每当抵达定时器递减到0,会触发中断,进入SysTick 中断处理函数(SysTick_IRQn)。
在stm32f4xx_it.c中。
void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ }
SysTick_Handler(void)
在其中调用了弱函数
__weak void HAL_IncTick(void) { uwTick += uwTickFreq; }
HAL_IncTick(void)
uwTick存储的是从stm32的滴答定时器初始化依赖所经过的时间,给整个程序提供了一个绝对的时间基准。HAL_Delay函数就是通过uwTick的值完成的。
其中在HAL库中可以通过
__weak uint32_t HAL_GetTick(void) { return uwTick; }
HAL_GetTick(void)
来获取uwTick的值。
函数3:HAL_Delay(uint32_t Delay)
__weak void HAL_Delay(uint32_t Delay) { uint32_t tickstart = HAL_GetTick(); uint32_t wait = Delay; /* Add a freq to guarantee minimum wait */ if (wait < HAL_MAX_DELAY) { wait += (uint32_t)(uwTickFreq); } while((HAL_GetTick() - tickstart) < wait) { } }
HAL_Delay(uint32_t Delay)
是以滴答定时器的时间为基准定时的。如果需要更加短的时间延时需要自己重新写。
函数4:void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
/** * @brief Toggles the specified GPIO pins. * @param GPIOx Where x can be (A..K) to select the GPIO peripheral for STM32F429X device or * x can be (A..I) to select the GPIO peripheral for STM32F40XX and STM32F427X devices. * @param GPIO_Pin Specifies the pins to be toggled. * @retval None */ void HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_PIN(GPIO_Pin)); if ((GPIOx->ODR & GPIO_Pin) == GPIO_Pin) { GPIOx->BSRR = (uint32_t)GPIO_Pin << GPIO_NUMBER; } else { GPIOx->BSRR = GPIO_Pin; } }
HAL_GPIO_TogglePin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
定时中断相关函数
在stm32f4xx_it.c中
/******************************************************************************/ /* STM32F4xx Peripheral Interrupt Handlers */ /* Add here the Interrupt Handlers for the used peripherals. */ /* For the available peripheral interrupt handler names, */ /* please refer to the startup file (startup_stm32f4xx.s). */ /******************************************************************************/ /** * @brief This function handles TIM1 update interrupt and TIM10 global interrupt. */ 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(&htim1); /* USER CODE BEGIN TIM1_UP_TIM10_IRQn 1 */ /* USER CODE END TIM1_UP_TIM10_IRQn 1 */ }
TIM1_UP_TIM10_IRQHandler(void)
为自动生成的中断处理函数。
该函数调用了 HAL 库提供的 HAL_TIM_IRQHandler 这一函数
============================================================================== ##### IRQ handler management ##### ============================================================================== [..] This section provides Timer IRQ handler function. @endverbatim * @{ */ /** * @brief This function handles TIM interrupts requests. * @param htim TIM handle * @retval None */ void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim) { /* Capture compare 1 event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC1) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC1) != RESET) { { __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC1); htim->Channel = HAL_TIM_ACTIVE_CHANNEL_1; /* Input capture event */ if ((htim->Instance->CCMR1 & TIM_CCMR1_CC1S) != 0x00U) { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->IC_CaptureCallback(htim); #else HAL_TIM_IC_CaptureCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } /* Output compare event */ else { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->OC_DelayElapsedCallback(htim); htim->PWM_PulseFinishedCallback(htim); #else HAL_TIM_OC_DelayElapsedCallback(htim); HAL_TIM_PWM_PulseFinishedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; } } } /* Capture compare 2 event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC2) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC2) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC2); htim->Channel = HAL_TIM_ACTIVE_CHANNEL_2; /* Input capture event */ if ((htim->Instance->CCMR1 & TIM_CCMR1_CC2S) != 0x00U) { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->IC_CaptureCallback(htim); #else HAL_TIM_IC_CaptureCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } /* Output compare event */ else { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->OC_DelayElapsedCallback(htim); htim->PWM_PulseFinishedCallback(htim); #else HAL_TIM_OC_DelayElapsedCallback(htim); HAL_TIM_PWM_PulseFinishedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; } } /* Capture compare 3 event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC3) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC3) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC3); htim->Channel = HAL_TIM_ACTIVE_CHANNEL_3; /* Input capture event */ if ((htim->Instance->CCMR2 & TIM_CCMR2_CC3S) != 0x00U) { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->IC_CaptureCallback(htim); #else HAL_TIM_IC_CaptureCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } /* Output compare event */ else { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->OC_DelayElapsedCallback(htim); htim->PWM_PulseFinishedCallback(htim); #else HAL_TIM_OC_DelayElapsedCallback(htim); HAL_TIM_PWM_PulseFinishedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; } } /* Capture compare 4 event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_CC4) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_CC4) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_CC4); htim->Channel = HAL_TIM_ACTIVE_CHANNEL_4; /* Input capture event */ if ((htim->Instance->CCMR2 & TIM_CCMR2_CC4S) != 0x00U) { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->IC_CaptureCallback(htim); #else HAL_TIM_IC_CaptureCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } /* Output compare event */ else { #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->OC_DelayElapsedCallback(htim); htim->PWM_PulseFinishedCallback(htim); #else HAL_TIM_OC_DelayElapsedCallback(htim); HAL_TIM_PWM_PulseFinishedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } htim->Channel = HAL_TIM_ACTIVE_CHANNEL_CLEARED; } } /* TIM Update event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_UPDATE) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_UPDATE) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_UPDATE); #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->PeriodElapsedCallback(htim); #else HAL_TIM_PeriodElapsedCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } } /* TIM Break input event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_BREAK) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_BREAK) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_BREAK); #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->BreakCallback(htim); #else HAL_TIMEx_BreakCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } } /* TIM Trigger detection event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_TRIGGER) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_TRIGGER) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_IT_TRIGGER); #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->TriggerCallback(htim); #else HAL_TIM_TriggerCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } } /* TIM commutation event */ if (__HAL_TIM_GET_FLAG(htim, TIM_FLAG_COM) != RESET) { if (__HAL_TIM_GET_IT_SOURCE(htim, TIM_IT_COM) != RESET) { __HAL_TIM_CLEAR_IT(htim, TIM_FLAG_COM); #if (USE_HAL_TIM_REGISTER_CALLBACKS == 1) htim->CommutationCallback(htim); #else HAL_TIMEx_CommutCallback(htim); #endif /* USE_HAL_TIM_REGISTER_CALLBACKS */ } } }
HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
在 HAL_TIM_IRQHandler 对各个涉及中断的寄存器进行了处理之后,会自动调用中断回调 函数 HAL_TIM_PeriodElapsedCallback,该函数使用__weak 修饰符修饰,即用户可以在别 处重新声明该函数,调用时将优先进入用户声明的函数。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim == &htim1) { //你的中断中要处理的函数 } }
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
相关计算
如果想要得到周期为 500 毫秒的定时器,则可以按照进阶学习介绍的公式来对分频值和重 载值进行设定。回到 Pinout&Configuration 标签页下,对应 TIMx_PSC 寄存器的 Prescaler 项和对应 TIMx_ARR 寄存器的 Counter Period 项。500ms 对应的频率为 2Hz,为了得到 2Hz 的频率,可以将分频值设为 16799,重载值设为 4999,则可以计算出定时器触发频率。