这个实验大概在一个多月前就做过了,但是没写博客,现在开始做红外遥控实验需要用到输入捕获变化沿,但是这部分内容忘得一干二净了。所以重新回顾下该实验。
抛开书本,输入捕获其是就是可以捕获从定时器使能开始到触发上升沿或下降沿的时间。通俗的说就是定时器的计时是通过计数器来实现的,而硬件上又可以检测到对应管脚的变化沿,通过触发来计算时间。
t1就是从开始到触发到了上升沿的时间,t2就是从定时器开始到触发到了下降沿。ARR就是计数器的满值,我们本次实验设置为0xffffffff,向上计数,所以t1就是计数了CCRx1个定时器周期的时间。
在此图中我们要计算的是上升沿到下降沿之间的时间,也就是t2-t1的区间。
计数器值最大不超过0xffffffff,但是到达最大值只能记一次循环,然后再从0开始计数。
cubemx配置TIM5CH1为输入捕获,本次实验我们要获取到KEY_UP的高电平脉冲时间,所以根据管脚选择了TIM5CH1。
需要特别说明的是分频系数选择了90,那是为了计算时间方便,因为定时器的时钟APB1为90MHZ,如果分频90的话定时器时钟就是10MHZ。也就是说触发一次定时器,为1s/10M=1us。
由于要计算时间,所以需要用到中断来辅助计数,使能中断。
KEY_UP的IO口配置为复用功能,下拉。
生成代码后,我们先开启定时器
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1); //开启TIM5的捕获通道1,并且开启捕获中断
__HAL_TIM_ENABLE_IT(&htim5,TIM_IT_UPDATE); //使能更新中断
之后了解两个中断服务函数,第一个函数是输入捕获中断回调函数,当有变化沿的时候就会进入该函数。
//定时器输入捕获中断处理回调函数,该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
第二个函数是定时器的计数器计满一个周期触发的中断回调函数,也就是计数器从0到达0xffffffff的时候进入该函数。有两种情况会进入该函数,一种情况是定时器开启后在没有触发变化沿的时候也会在每次计数器值满的时候进入该中断;另一种情况是在上升沿触发后,高电平保持时间超过了计数器的周期而进入的中断,我们需要在这种情况下对进入中断的次数进行累加以算出高电平脉冲总时间。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
//定时器输入捕获中断处理回调函数,该函数在HAL_TIM_IRQHandler中会被调用
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行
{
if(htim == &htim5)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获完整的高电平脉冲。(防止成功捕获了一次高电平脉冲后,还未等到main处理计算时间,又捕获到了一次变化沿。)
{
if(TIM5CH1_CAPTURE_STA&0X40) //触发了变化沿,而之前已经有了上升沿,那么一定是下降沿,那么认为完整捕获了一次高电平持续时间
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽
TIM5CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1);//获取当前的捕获值.
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);//配置TIM5通道1上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM5CH1_CAPTURE_STA=0; //清空
TIM5CH1_CAPTURE_VAL=0;
TIM5CH1_CAPTURE_STA|=0X40; //标记触发到了上升沿
__HAL_TIM_DISABLE(&htim5); //关闭定时器5,来重新配置定时器为捕获下降沿。等下次捕获到了下降沿的时候就是完整的高电平脉冲
__HAL_TIM_SET_COUNTER(&htim5,0);
TIM_RESET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1); //一定要先清除原来的设置!!
TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);//定时器5通道1设置为下降沿捕获
__HAL_TIM_ENABLE(&htim5); //使能定时器5
}
}
}
}
在第一个上升沿的时候会进入该函数,然后设置为下降沿触发,并开启定时器。在下降沿到来的时候会再次进入,标记已经完整捕获了高电平脉冲。
//更新捕获状态
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到低电平;1,已经捕获到低电平了.
//[5:0]:捕获低电平后溢出的次数(对于32位定时器来说,1us计数器加1,溢出时间:4295秒)
uint8_t TIM5CH1_CAPTURE_STA=0; //输入捕获状态
uint32_t TIM5CH1_CAPTURE_VAL; //输入捕获值(TIM2/TIM5是32位)
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim5)
{
if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if(TIM5CH1_CAPTURE_STA&0X40)//已经到高电平了
{
if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F) //已经到了软件设计的最大高电平持续时间了,认为已经捕获了完整的高电平持续时间
{
TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获了,其实这个时候并没有检测到下降沿
TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;
}
else //一般情况会到这个位置,让循环数+1
TIM5CH1_CAPTURE_STA++;
}
}
}
}
该函数是定时器完成一周期就进入一次,我们判定在上升沿触发后计次数,没有触发上升沿的时候是不会计次数的。
//TIM5 输入捕获实现按键KEY_UP按下的时间
if(TIM5CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平
{
temp=TIM5CH1_CAPTURE_STA&0X3F; //TIM5CH1_CAPTURE_STA的0~5位代表高电平持续的定时器周期数
temp*=0XFFFFFFFF; //溢出时间总和,计算时间需要将这个周期数乘以计数器满值(0xffffffff)
temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间,还需要在上面的基础上加上不满一整个周期的计数值,得到总的高电平持续时间
printf("HIGH:%lld us\r\n",temp);//打印总的高点平时间,因为TIM5的周期定为1us(APB1为90MHZ, 分频90,那么就是1/(90M/90)=0.000001s)
TIM5CH1_CAPTURE_STA=0; //开启下一次捕获
}
在main()函数中,不断查询定时器是否完成了一次完整的高电平脉冲捕获,如果完成了,那么我们就可以计算这次高电平脉冲持续的时间了,见上。
结论:输入捕获的确很精准,但是使用逻辑上有些复杂,牵扯到的函数太多,不方便使用。如果想要测量高电平脉冲时间,完全可以通过管脚的输入中断来判断是高电平,然后打开定时器,在再次触发中断是低电平的时候计算时间。
坛城 发布了26 篇原创文章 · 获赞 0 · 访问量 2977 私信 关注