您好,欢迎来到汇智旅游网。
搜索
您的当前位置:首页STM32和STM32CubeMX定时器输入捕获 保姆级教程

STM32和STM32CubeMX定时器输入捕获 保姆级教程

来源:汇智旅游网

各位,我正在做一个无人小车的电调,电调需要根据接收到的PWM信号来驱动马达转动,从而实现电子调速的功能。那么,既然需要根据接收到的PWM信号来做控制,那么就需要知道接收到的PWM信号的脉宽和周期,占空比之类的了。那么也就需要用到定时器输入捕获了。因此,我就来写个STM32定时器输入捕获的保姆级教程。

1. STM32CubeMX配置

我用的是STM32G474VET6的芯片,上面的定时器比较多,我就随便选两个用用了。1个产生PWM波,一个来输入捕获这个PWM波。

我用TIM4来产生PWM波,用TIM2来捕获。

TIM4的配置为

这个产生PWM信号很简单。我用TIM4产生2路PWM,用的Channel1和Channel4, 系统时钟是170M,所以分频做170-1,分频到1M,也就是1us.

电调不知道为什么,要用14ms的周期的PWM信号,所以,我设置的PWM周期是14ms.产生1路1000us的波和1路1300us的波。

TIM2的配置为

用TIM2的Channel3来做输入捕获。

也设置成1us, 重载计数器ARR设成65535,因为TIM4最大只能到65535,就配成一样的。其他的就是上升沿采样,把中断打开。

 我还用GPIO产生一个波形,所以也配置了一个GPIO口。

好了,CubeMX的配置就完成了,下面写代码。

2.代码

代码很简单。先声明一些数据保存和计算用的变量

uint16_t my_ccr1_cnt= 0;
uint16_t my_ccr2_cnt = 0;
uint8_t my_ic_flag = 0;
uint32_t my_low_width = 0;
uint32_t my_high_width=0;
uint16_t my_period=0;
uint32_t my_period_cnt=0;

然后,启动TIM2和TIM4

  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_1);
  HAL_TIM_PWM_Start(&htim4, TIM_CHANNEL_4);

  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_3);

注意,TIM2的基本定时器中断也要打开,因为ARR溢出中断是由基本定时器产生的。

接着,我在主循环里,用GPIO产生了一个10s翻转的方波。

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {

    HAL_GPIO_TogglePin(M3_M4_RUN_GPIO_Port, M3_M4_RUN_Pin);
    __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 1700);
    HAL_Delay(5000);
    __HAL_TIM_SET_COMPARE(&htim4, TIM_CHANNEL_4, 1000);
    HAL_Delay(5000);
    
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

好了,下面是中断服务函数了。

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance == TIM2)
  {
    my_period_cnt++;
   // usb_printf("period_cnt = %d\r\n", my_period_cnt);
  }
}

这个中断服务函数的目的是,当输入信号太慢的时候,ARR会计数溢出,看看他溢出了多少回。

原理如下,图画的有点丑,将就看看吧

 输入捕捉的中断服务函数

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  if(htim->Instance == TIM2)
  {
    if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_3)
    {
      if(my_ic_flag == 0) // 第一次上升沿
      {
        __HAL_TIM_SET_COUNTER(&htim2, 0);
        my_ccr1_cnt = 0;
        my_ccr2_cnt = 0;
        my_high_width = 0;
        my_low_width = 0;
        my_period_cnt = 0;

        __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_FALLING);
        my_ic_flag = 1;
      }
      else if(my_ic_flag == 1)  // 下降沿
      {
        my_ccr1_cnt = __HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_3);
        __HAL_TIM_SET_COUNTER(&htim2, 0);
        __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING);
        my_high_width = my_period_cnt*65536 + my_ccr1_cnt;
        
        my_ic_flag = 2;
        usb_printf("period_cnt= %d, high width = %d\r\n", my_period_cnt, my_high_width);
        my_period_cnt = 0;
      }
      else if(my_ic_flag == 2) // 上升沿
      {
        my_ccr2_cnt = __HAL_TIM_GET_COMPARE(&htim2, TIM_CHANNEL_3);
        __HAL_TIM_SET_COUNTER(&htim2, 0);
        __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_FALLING);
        my_low_width = my_period_cnt*65536 + my_ccr2_cnt;
        my_period = my_high_width + my_low_width;
        //my_period_cnt=0;
        my_ic_flag = 1;

        usb_printf("period_cnt =%d, low width = %d\r\n", my_period_cnt, my_low_width);
        my_period_cnt = 0;

        //usb_printf("period = %d\r\n", my_period);
      }
      else
      {
        /* error */
      }
    }
  }
}

说明一下,第一次上升沿时,所有的输入捕捉相关的变量都清零。然后把采样极性改成下降沿。把输入捕捉标志改成1.

在下降沿中断时(输入捕捉标志==1)

将计数值存到ccr1_cnt; 将CNT清零,采样极性改成上升沿,计算高极性宽度。ARR溢出一次代表65536us。 将溢出计数器清零,输入捕捉标志改成2。

在上升沿中断时(输入捕捉标志==2)

将计数值存到ccr2_cnt;将CNT清零,采样极性改成下降沿,计算低极性宽度。计算周期的长度。将溢出计数器清零,输入捕捉标志改成1。那么,以后就在输入捕捉标志1和2之间跳了。第一次上升沿只有一次。

道理就这么简单。

3. 看看程序的结果

这是用GPIO口产生的10s左右翻转的方波。

我将这个方波接入TIM2的Channel3通道,打印出来的捕获结果是

应该说,还是比较准的。

好了,大功告成,亲个嘴儿。 

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- hzar.cn 版权所有 赣ICP备2024042791号-5

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务