[ARM入门] STM32F103用Cube和HAL库构建定时器捕获

[复制链接]
1187|0
手机看帖
扫描二维码
随时随地手机跟帖
潜力变实力|  楼主 | 2022-1-12 17:57 | 显示全部楼层 |阅读模式
STM32F103用Cube和HAL库构建定时器捕获
作者:雾里白马啸西风  日期:2021年12月10日星期五

之前百度过用定时器捕获计算脉宽,大家普遍反映麻烦,且捕获程序实例不多,大家不知如何下手。部分工程师建议用外部中断来替代捕获,作者之前也采用了这个建议。最近有时间摸索,拿出开发板做了实验,花了一天时间,结果不错。以前从网络里学到很多ARM编程技巧,今天也反哺一下。

1.     先上用CubeMx设计的时钟配置图
poYBAGG0lrmAGRpUAAF1dDxCMfU753.png
pYYBAGG0lrqALY7GAAFulivvjWY512.png
我的开发板带8M外接晶振,因此我选HSE,经过PLL倍频。选择8倍频,因为后续做边沿捕获时,计算时间的话,需要除以8M*8倍频,可以直接移位。
这样配置得到时钟:
1)     SysClk:64MHz,系统频率
2)     HCLK:65MHz,提供给高速总线AHB的时钟信号
3)     PCLK:提供给低速总线APB的时钟信号
a)       PClk1:32MHz,而和APB1连接的tiM2~TIM7的时钟是64MHz
b)       PClk2:32MHz,而和APB2连接的TIM1和TIM8的时钟是64MHz
4)     FClk:64MHz,提供给CPU内核的时钟信号

2.     在CubeMx设置定时器和捕获通道
先上图,再说明
pYYBAGG0mPqALRPGAAHqfNbG6U8332.png
我选择了TIM3,以及它的第三通道,这个TIM3_CHANNEL3对应的管脚是PB0。大家可以自由选择通道和对应的管脚。
参数配置和中断使能配置好,其他默认就行。
pYYBAGG0mPqAabGVAACEee1zJRo986.png
poYBAGG0mPuAUMDZAAA5WT3EqSE602.png
说明一下,
1)     此处定时器设置的Prescaler为0,实际+1,相当于PCLK除以1,不作分频了。
2)     计数周期65535个脉冲,从0开始算实际一个周期应该是65536个。
3)     auto-reload preload为Enable状态
4)     捕获设置极性为上升沿,这个可以在软件程序中改写,按需要为下降沿或上升沿。

3.     在CubeMx项目管理中设置
poYBAGG0lruAUWnWAAEqvI9tssM257.png
我用的是KEIL 5,因此选择MDK-ARM V5.
在代码生成器设置时,如下,我选择只拷贝必需的库文件,并且单独生成外围部件文件。
pYYBAGG0lryAIVstAAH67a_1DYg372.png

4.     点击生成代码按钮,生成后,打开“Open Project”,进入Keil环境。

5.     在Keil环境里面,已经有tim.c生成。
pYYBAGG0mP2AZ9gJAAGOl95Tsc0357.png

选择打开tim.c文件,找到MX_TIM3_Init(void),在函数末尾处输入开启输入捕获和定时器的中断
poYBAGG0mP6AWdq6AADn7VFbB6M851.png
  /*USER CODE BEGIN TIM3_Init 2 */
  if(HAL_TIM_Base_Start_IT(&htim3) != HAL_OK)
   Error_Handler();
  if(HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_3) != HAL_OK)
   Error_Handler();
  /*USER CODE END TIM3_Init 2 */
至此,已经配置好输入上升沿捕获的配置和中断,后面的任务是获取输入脉冲的脉宽了。我的项目是对输入的高电平脉宽进行测量

6.     在tim.c文件末尾,添加以下内容对输入脉冲进行处理
pYYBAGG0mP-AfgL8AAMLN2FR5mk314.png
/* USERCODE BEGIN 1 */
unsignedchar bAcquireTimeTransfer2PCFlag, bCaptureControlFlag;
uint32_tTim3IRQNum = 0, TotalTimeStart, TotalTimeEnd, TotalAcquireTimeUs;
externuint32_t u32TimeRFreq;

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef*htim)
{
      if( (TIM3 == htim->Instance) &&(HAL_TIM_ACTIVE_CHANNEL_3 == htim->Channel) )
      {
             bCaptureControlFlag =HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0); //PB0输入
             if(bCaptureControlFlag) // HighLevel
             {
                    Tim3IRQNum = 0;
                    TotalTimeStart =HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3); //捕获上升沿时的计数器读数
                    bAcquireTimeTransfer2PCFlag= FALSE;
//捕获上升沿后,马上改变极性为Falling,以捕获脉冲下降沿
__HAL_TIM_SET_CAPTUREPOLARITY(htim, TIM_CHANNEL_3,TIM_INPUTCHANNELPOLARITY_FALLING);
             }
             else// Low Level
             {
                    TotalTimeEnd =HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_3);
                    TotalAcquireTimeUs =  65536 * Tim3IRQNum + TotalTimeEnd -TotalTimeStart;
                    TotalAcquireTimeUs >>=6;
                    bAcquireTimeTransfer2PCFlag= TRUE;

                    //change POLARITY;
                    __HAL_TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_3, TIM_INPUTCHANNELPOLARITY_RISING); // Rising Edge
             }

             __HAL_TIM_CLEAR_IT(htim,TIM_IT_CC3);
      }
}

voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
      if(TIM3 == htim->Instance)
      {
             __HAL_TIM_CLEAR_IT(htim,TIM_IT_UPDATE);
             if(bCaptureControlFlag)Tim3IRQNum++;
      }
}
/* USERCODE END 1 */

1)       HAL_TIM_IC_CaptureCallback和HAL_TIM_PeriodElapsedCallback是重构函数,HAL库里自带,但只是个壳,需要自己重新编写内容。
2)       之所以要使用HAL_TIM_PeriodElapsedCallback溢出回调函数,是因为脉冲有长短,以us计算,会超过65536/64M=1024us.累计高电平脉冲内的溢出次数,就可以测算长脉宽。
3)       捕获脉冲时间计算问题:
a)       捕获得知的是多少个时钟脉冲,以1KHz脉宽计算,高电平部分是500us,捕获共计32000个时钟脉冲。
b)       Tim3的时钟是64MHz,这个可以用函数测得u32TimerFreq= HAL_RCC_GetPCLK1Freq()<<1;
c)       由于Prescaler=0,不分频,因此时长t=32000/(64M/(Prescaler+1))=500us.

7.     编译通过,烧写开发板,用1KHz脉冲做试验。实测高电平部分为500us,项目顺利完成。
poYBAGG0mQCAFO0MAAAiOwxH-hc135.png











使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则