定时器输入捕获
插播一条串口
之前学串口部分的时候,偷懒没学用printf()输出,今天要打印高电平持续时间的时候,直接重写了fputc函数。结果一直困在printf的死循环里出不来。找了半天发现居然是!!对printf重定向后,避免使用半主机模式而导致库函数程序无法运行,使用微库Micro LIB可以避免半主机模式,在keil中点击Options for Target…,勾选Use Micro LIB,点击OK即可。
勾选完这个选项之后,我们发现串口可以用printf()语句顺利的输出啦。
定时器捕获的原理
关于原理部分可以参考一篇博客链接: link。里面详细介绍了各个寄存器的作用。
代码实现
基本框架
首先定义四个变量,状态变化时的一个计数值、存储计数器的记录值、计数器溢出的个数和最后总的高电平的时间。
uint8_t TIM5_CH1_Edge=0; //状态变化时,计数值
uint32_t TIM5_CH1_VAL=0; //储存计数器的记录值
uint32_t TIM5_CH1_OVER=0; //计数器溢出的个数
uint32_t time; //高电平持续时间
然后在主函数前面打开定时器的中断以及打开输入捕获。
HAL_TIM_Base_Start_IT(&htim5); //打开定时器中断
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1); //打开输入捕获
在主函数的循环中开始编写,如果状态变化两次,即一次高电平跳变和一次低电平跳变。那么就进入下一轮的捕获当中,将状态变化时的计数值置零。然后用溢出值乘以重装载值加上记录值就是高电平持续的时间。然后在重新打开输入捕获。
if(TIM5_CH1_Edge == 2)
{
TIM5_CH1_Edge = 0; //重新开始捕获
time = TIM5_CH1_VAL + TIM5_CH1_OVER*0xffff;
printf("高电平持续时间为 %d ms\r\n", time/1000);
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1); //打开输入捕获
}
然后编写定时器溢出中断回调函数。当定时器出现溢出时,计数器溢出的个数加一。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if(TIM5_CH1_Edge == 1)
{
TIM5_CH1_OVER++; //定时器溢出值增加
}
}
}
然后是捕获跳变的中断函数。如果捕获到上升沿跳变,则状态变化的计数器加一,并且定时器的溢出值清零。同时将捕获极性设置为下降沿,然后将计数器的值设置为0。
如果捕获的时下降沿则,马上关闭定时器,并且将状态变化的计数器再加一,即为二。同时读取捕获通道的值。在设置捕获通道的极性为上升沿捕获。
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if(TIM5_CH1_Edge == 0) //捕获到上升沿
{
TIM5_CH1_Edge++; //进入捕获下降沿状态
TIM5_CH1_OVER = 0; //定时器溢出值清零
__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING); //设置捕获极性为下降沿
__HAL_TIM_SET_COUNTER(&htim5,0); //设置定时器CNT计数器的值为0
}
else //捕获到下升沿
{
HAL_TIM_IC_Stop_IT(&htim5,TIM_CHANNEL_1); //关闭定时器5
TIM5_CH1_Edge++; //进入到主函数状态
TIM5_CH1_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1); //读取捕获通道的值
__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING); //设置捕获极性为上降沿
}
}
}
编译之后即可以看到如下结果。