1
前言
点击蓝字 关注UP
前几期我们介绍了STM32CubeMX中关于定时器的各个配置参数及其功能,本期我们来介绍各个功能的具体代码实现。
分别是: ** 定时器中断,PWM模式,输出捕获模式,比较输出模式,强制输出模式 ** 。
进行时钟配置,这里主频是170MHZ,定时器时钟为170MHZ。
2
定时器中断
打开CubeMX,配置定时器分频系数为170-1,周期计数值为1000,主频为170MHZ。由此我们可以计算一次溢出的为:
170M/(170+1)/1000 = 1000即1ms一次。
开启定时器中断以确保我们的定时器中断回调函数能被正常触发。
接着选择好编译器和文件路径和文件名( 不要用中文路径)生成工程。
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim1); /* USER CODE END 2 */
添加启动定时器并启用中断的函数,参数为我们开启的定时器句柄。
//开启定时器并开启中断 HAL_TIM_Base_Start_IT(&htim1); //开启定时器不开启中断 //HAL_TIM_Base_Start(&htim1);
当然也有不启用中断的,后者仅仅开启定时器计数功能,但是会发生定时器溢出产生事件更新,不过不会触发中断请求。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ static int number = 0; if(htim->Instance == TIM1) // 判断是否是 TIM1 触发的中断 { // 1ms触发一次 number++; if(number==1000) { //1ms*1000 = 1s HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_5); number = 0;//清零 } }}
接着我们需要 重写定时器中断回调函数
来实现我们的功能,由于我们定时器是1ms触发一次,因此我们使用一个计数器来判断触发的次数。当我们定时时间到达1s时,使我们的LED灯翻转。
我们可以看到,中断回调函数在底层被一个虚函数定义,这段注释告诉用户,不要修改这个函数的默认实现。如果需要自定义的回调行为,应该在用户的代码中重新实现这个回调函数。
当我们在程序中需要修改定时器触发时间的时候,我们可以重新修改定时器的参数并进行初始化。
htim1.Init.Prescaler = new_prescaler;//新的分频系数htim1.Init.Period = new_arr;//新的周期值HAL_TIM_Base_Init(&htim1); // 重新初始化定时器HAL_TIM_Base_Start_IT(&htim1); // 重新启动定时器
下面要用。
3
PWM模式
上面我们设置了定时器的单周期参数为1000,接着我们设置PWM的比较值(Output compare preload)为500,那么占空比就是: **
500/1000 = 50% ** 。
接着生成我们的代码。
/* USER CODE BEGIN 2 */ HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); /* USER CODE END 2 */
** 启动定时器PWM,参数分别为定时器句柄和通道 ** 。
这样子我们通道一对应的IO就可以输出方波了。
htim1.Init.Prescaler = new_prescaler;//新的分频系数htim1.Init.Period = new_arr;//新的周期值HAL_TIM_Base_Init(&htim1); // 重新初始化定时器HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1); // 重新启动PWM
通过修改定时器的计数频率来修改PWM的频率。
/* USER CODE BEGIN 2 */ __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, duty); //修改占空比 /* USER CODE END 2 */
通过修改通道的比较值CCR来改变PWM的占空比 = CCR/ARR.
4
输入捕获
通道配置为输入捕获之后,需要注意捕获模式是上升沿捕获、下降沿捕获还是双边捕获。
这里要开启中断,我们需要使用中断回调函数。
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动定时器并使能中断
调用开启输入捕获的函数,接着重写输入捕获的回调函数。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ static uint32_t last_capture = 0; // 上次捕获的计数值 uint32_t capture_value = 0; if (htim->Instance == TIM1) // 判断是否是定时器1触发的中断 { capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 读取捕获 // 计算捕获值之间的时间差(信号周期或脉冲宽度) if (capture_value > last_capture) { uint32_t pulse_width = capture_value - last_capture; // 计算脉冲宽度 last_capture = capture_value; // 更新上次捕获值 } }}
当外部信号来临的时候,我们可以读取通道值来获得当前的计数值。通过比较两次的计数值可以知道两个信号之间的触发事件。
这种方法我们也可以用来测量PWM的高低电平时间。通过修改触发方式, ** 当上升沿触发的时候,修改触发方式为下降沿,就可以知道高电平的时间 **
。反过来就可以知道低电平的时间。
// 输入捕获中断回调函数void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ capture_value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 读取捕获值 if (htim->Instance == TIM1) // 判断是否是定时器2触发的中断 { if (capture_value > last_capture) // 如果是上升沿 { // 处理上升沿捕获逻辑 printf("上升沿时间: %lun", capture_value); // 修改下一次捕获为下降沿触发 TIM_IC_InitTypeDef sConfigIC = {0}; sConfigIC.ICPolarity = TIM_ICPOLARITY_FALLING; // 设置为下降沿触发 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接从输入端口获取信号 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不进行预分频 sConfigIC.ICFilter = 0; // 无输入滤波器 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); // 配置通道1为下降沿触发 } else // 如果是下降沿 { // 处理下降沿捕获逻辑 printf("下降沿时间: %lun", capture_value); // 修改下一次捕获为上升沿触发 TIM_IC_InitTypeDef sConfigIC = {0}; sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 设置为上升沿触发 sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 直接从输入端口获取信号 sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 不进行预分频 sConfigIC.ICFilter = 0; // 无输入滤波器 HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1); // 配置通道1为上升沿触发 } last_capture = capture_value; // 更新上次捕获值 }}
5
比较输出
比较输出的配置和PWM模式的差不多。
HAL_TIM_OC_Start(&htim1, TIM_CHANNEL_1); // 启动通道1的输出比较
利用该函数开启通道的比较输出的功能,它除了能输出PWM波之外,还可以在比较值的时候触发中断回调函数。
void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim){ if (htim->Instance == TIM1) // 检查是否是TIM1的比较输出中断 { }}
并且,当我们把模式修改为电平翻转之后,可以让通道实现方波输出。
6
强制输出
强制输出模式胜在可以使用软件强制修改引脚高低电平。
利用如下函数实现强制输出。
// 配置强制输出模式为强制低电平 __HAL_TIM_OC_SET_FORCED_ACTION(&htim1, TIM_CHANNEL_1, TIM_OCFORCE_LOW); // 或者配置强制输出模式为强制高电平 // __HAL_TIM_OC_SET_FORCED_ACTION(&htim1, TIM_CHANNEL_1, TIM_OCFORCE_HIGH);
通过在定时器溢出、比较事件或外部触发事件发生时,强制定时器输出高电平或低电平,可以灵活地控制STM32微控制器的输出行为。