stm32F407定时器Sample
stm32的定时器,顾名思义用于定时任务,难点是需要根据定时器的时钟进行计算预分频和计数器。 本例程是以两个定时器,分配控制两个LED灯定时点亮,但是这两个LED点亮间隔时间不同。
一、系统定时器
stm32F407拥有14个定时器,
- 高级定时器:TIM1,TIM8,APB2
- 基本定时器:TIM6,TIM7,APB1
- 通用定时器:TIM2-TIM5,TIM9-TIM14,APB1(TIM9-11,APB2)
二、硬件
任务:通使用定时器控制间隔时间,通过外部中断控制LED灯的闪烁间隔时间
- LED1<-->PF7:0.5秒(500ms)闪烁一次
- LED2<-->PF8:1秒(1000ms)闪烁一次
三、定时器原理
stm32的定时器有两个核心的概念:分频器(Prescaler)和计数器(Counter Period),当分频器溢出后会触发计数器,计数器溢出后,即定时的时间到了,触发外部中断。
- 分频器是16位,即数值在0-65535之间;
- 计数器有16位,也有32位,图形化的配置工具中有提示,如TIM2的计数器是32位的,TIM3的计数器是16位的。
- 间隔时间计算公式: (Prescaler+1 ) X (Counter Period+1) X 1/ 定时器时钟频率,输入的是1khz。
- 如:
1khz*32000*500/32M=500ms
(32M是设置的时钟输出频率,如果不是这个,需要另外计算,原理一样)
四、配置
- 时钟(RCC):时钟全部输出频率为32M(初步这样设置,还不了解具体的计时器是挂在那个APB下
- GPIO:PF7/PF8全部设置为gpio_output,初始输出电频为low
- 定时器:Timers标签下,计时器参数在
Parameter Settings
下设置,同时为了使计时器能驱动LED,要启用计时器的NVIC功能,在NVIC Settings
下设置开启1. TIM2: * Clock source:internal clock * Prescaler:32000-1=31999 * Counter Mode:up * Counter Period:500-1=499 2. TIM3: * Clock source:internal clock * Prescaler:32000-1=31999 * Counter Mode:up * Counter Period:1000-1=999
五、调用流程
定时器的调用流程同外部冲断类似,同时通过调用回调函数实现处理功能。
main.c-->tim.c-->stm32f4xx_it.c-->stm32f4xx_hal_tim.c-->main.c
六、代码
tim.c
负责定义计时器,及TIM的初始化,配置定时器的参数。这里定义和初始化的计时器,在main.c中进行调用。
tim.c文件在项目的src目录下
定义计时器(这里定义两个计时器)
TIM_HandleTypeDef htim2; TIM_HandleTypeDef htim3;
定时器初始化(这个代码是TIM2的初始化代码)
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 31999;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 499;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
stm32f4xx_it.c
stm32f4xx_it.c文件在项目src目录下
这里的功能是计时器调用外部中断
引入在
tim.c
定义的定时器变量extern TIM_HandleTypeDef htim2; extern TIM_HandleTypeDef htim3;
生成定时器的中断调用函数,此函数调用定时器中断处理函数(这里是TIM2定时器)
void TIM2_IRQHandler(void) { /* USER CODE BEGIN TIM2_IRQn 0 */ /* USER CODE END TIM2_IRQn 0 */ HAL_TIM_IRQHandler(&htim2); /* USER CODE BEGIN TIM2_IRQn 1 */ /* USER CODE END TIM2_IRQn 1 */ }
stm32f4xx_hal_tim.c
stm32f4xx_hal_tim.c文件在driver的src目录下
- 在此文件中找到上面调用的函数
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim)
- 在
HAL_TIM_IRQHandler
函数中找到时间间隔回调函数HAL_TIM_PeriodElapsedCallback
找到
HAL_TIM_PeriodElapsedCallback
的函数原型,这是一个虚函数,我们需要实现的功能,需要在main.c文件中,重载这个函数__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { /* Prevent unused argument(s) compilation warning */ UNUSED(htim); /* NOTE : This function should not be modified, when the callback is needed, the HAL_TIM_PeriodElapsedCallback could be implemented in the user file */ }
main.c
在main文件中,重写这个函数,实现我们要用的功能,这里是TIM定时器反转LED的电平。
重写
HAL_TIM_PeriodElapsedCallback
函数,实现反转LED的电平void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance==TIM2) { HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_7); } if(htim->Instance==TIM3) { HAL_GPIO_TogglePin(GPIOF, GPIO_PIN_8); } }
启动定时器
在mian.c文件的main方法中,系统各项功能初始化完毕后,通过定时器启动函数启动定时器。 启动函数传入的定时器的指针,变量需要用到&符号
HAL_TIM_Base_Start_IT(&htim2);
HAL_TIM_Base_Start_IT(&htim3);
七、其他
此例程是按照教程的,全部输出时钟设置的32M,暂时没有使用其他时钟频率- STM32CubeIDE功能非常强大,配置后会自动生成代码,只需要在main.c文件的中重写定时器回调函数和启用定时器即可
八、补充(定时器间隔时间的计算)
前面的定时器配置完全是按照教程来的,把系统时钟频率全部设置为32M,但是,STM32F407开发板的芯片时钟最高频率是168M,而且实际应用中,也不可能把系统所有输出的时钟频率设置为32M,重新研究时钟配置和定时器间隔时间的计算。
- 系统时钟设置为168M,如:sysclk,hclk的时钟全部配置为168M,其他各输出时钟由图形化工具自动计算;
- apb1的预分频系数配置为
/4
,apb2的预分频系数配置为/2
- 前文中已经提到TIM2-TIM5定时器是挂在apb1下的,因此需要根据apb1的预分频系数来计算输出的时钟
- 查看时钟树或者查看IDE的时钟图形化配置,可以知道apb1外设时钟是42M(168/4=42),apb1的定时器时钟是84M(168/4*2=84M);
抄录一段文字
当 APB1 的时钟分频数为 1 的时候,TIM2~7 以及 TIM12~14 的时钟为 APB1 的时钟,而如果 APB1 的时钟分频数不为 1,那么 TIM2~7 以及 TIM12~14 的时钟频率将为 APB1时钟的两倍。
根据第4、5点,可以知道TIM2,TIM3的时钟频率是84M,这个时钟频率是我们计算间隔定时器频率
- 定时器间隔0.5秒(500ms)计算:
1khz*(4999+1)*(8399+1)/84Mhz=500ms
- 定时器间隔1秒(1000ms)计算:
1khz*(9999+1)*(8399+1)/84Mhz=1000ms
- 在实际应用中,可以按照以上方法根据系统配置的时钟,计算定时器的间隔时间