概述
输入捕获中断函数:
回想起刚学stm32的时候,看到正点原子哥的输入捕获中断函数代码,让我心生畏惧啊,但是还是硬着头皮去读,勉勉强强的看懂了。今天再次回顾一下这个经典的代码,真的是受益匪浅,赞叹原子哥的思路清晰,大家看下文的时候,可以把程序代码复制到另外一个窗口,交替切换着看,这样阅读体验更佳!
现在用自己的粗浅理解,来给大家解读一下这个经典的输入捕获中断代码:
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH1_CAPTURE_VAL=0XFFFF;
}else TIM2CH1_CAPTURE_STA++;
}
}
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
{
if(TIM2CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH1_CAPTURE_STA|=0X80; //标记捕获成功
TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH1_CAPTURE_STA=0; //清空
TIM2CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM2,0);
TIM2CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
}
}
}
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
}
首先来说一下大家可能的疑惑:为什么要再写一个中断函数来处理输入捕获值,而不是直接读取CCR锁存值,直接读取很方便啊?
其实这个问题很简单,就是CNT溢出的问题,试想以下这样的情况:我现在要捕获一个高电平的脉宽,但是这个高电平持续时间很长,长到它持续了65537个CNT计数值的时间,但是CNT计数我们知道最大只有65535(12位寄存器),因此就会导致计数器溢出,我们最终得到的CCR锁存器的因为溢出变成了1(65535是最大值,那65536就会溢出变为0,因此65537就是1),那显然不对啊。所以上面的这个中断函数就是来解决计数器溢出的这个问题而服务的。
好了现在就来解读一下上述程序是如何解决这个问题的:
首先,原子哥自己定义了两个“寄存器”,用来辅助中断函数的8位TIM2CH1_CAPTURE_STA寄存器和16位TIM2CH1_CAPTURE_VAL寄存器,其中TIM2CH1_CAPTURE_VAL来存放最终捕获到高电平的CCR寄存器锁存值。而TIM2CH1_CAPTURE_STA是标志位寄存器,各位意义如下:
来分析一下各个位的具体作用:
bit6:这个位用来判断在本次中断之前是否已经捕获到高电平
bit7:这个标志位用来判断是否完成了一次CCR锁存(即成功捕获了一次高电平)
bit5~0:这个标志位记录了定时器的溢出次数,是本程序区别于直接读取CCR寄存器的捕获方法的关键
注意:以上两个寄存器是原子哥自己定义的,并不是stm32标准库里的寄存器,是需要自己去定义两个全局变量来作为“伪寄存器”的!!
好了,现在准备工作就做完了,接下来,我就假设一个很长时间的脉冲,这个脉冲会导致CNT计数器溢出,我们现在根据程序来一步步的捕获它:
首先,脉冲来临之前,我们定义的标志位各个位都是0,然后脉冲来了,由于我们输入捕获初始化程序设置的是上升沿捕获,所以由于脉冲的来临触发捕获事件第一次进入中断函数,
if((TIM2CH1_CAPTURE_STA&0X80)==0)
TIM2CH1_CAPTURE_STA的第7位等于零才能执行下面的中断操作,这是执行中断函数的大前提,如果这个条件不成立,则会直接跳出中断。因为TIM2CH1_CAPTURE_STA初始值是0所以这个条件目前是成立的
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
由于本次中断并不是因为CNT计数器溢出进入的,所以这一句if条件不成立,直接跳出if嵌套,
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
然后就执行这一句,判断是否是因为捕获事件进入中断的,当时是咯!所以就执行if里面的语句
if(TIM2CH1_CAPTURE_STA&0X40)
在if里面,首先判断TIM2CH1_CAPTURE_STA第六位是否为1,也就是判断在本次中断之前的是否已经捕获到了高电平,因为我们本次中断就是因为捕获到上升沿(高电平)而进入的,并且是第一次进入,所以这一句if不成立
然后就会去执行else里面的内容了,因此else里面就是来处理一个脉冲的上升沿进入中断要执行的操作;在else里面,主要完成TIM2CH1_CAPTURE_STA,TIM2CH1_CAPTURE_VAL和CNT初始化(即清零)操作;再将TIM2CH1_CAPTURE_STA第六位置1,标记捕获到了高电平;同时调用TIM_OC1PolarityConfig函数设置为下降沿捕获,为下一次捕获脉冲下降沿做准备
;这个else里面的操作有点多,但是都很简单,希望大家仔细理解一下
TIM2CH1_CAPTURE_STA=0; //清空
TIM2CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM2,0);
TIM2CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //设置为下降沿捕获
以上操作完成之后,本次中断函数执行结束,然后CNT就会一直自增记录脉冲时间,这里我已经假设过了,这个脉冲的时间会导致CNT溢出,溢出的次数未知,所以在接下来,会一直触发溢出中断知道下一个下降沿来临,也就是说在下一个下降沿来临之前,会执行中断函数中下面的这些代码:
if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH1_CAPTURE_VAL=0XFFFF;
}else TIM2CH1_CAPTURE_STA++;
}
}
这里我再啰嗦一句哈,防止有些朋友不理解,因为是溢出事件导致的溢出中断,所以
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)
这个if条件是不会成立的,这就是不会执行这个if条件里面的所有语句的原因;
好了,那现在我们就把主要精力放在这段代码中:
if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
{
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH1_CAPTURE_VAL=0XFFFF;
}else TIM2CH1_CAPTURE_STA++;
}
}
每次因为溢出事件进入中断函数;第一个if是始终成立的,我们目前一直都还没有成功捕获。然后判断更新中断标志位这个就不说了,肯定是成立的。
if(TIM2CH1_CAPTURE_STA&0X40) //已经捕获到高电平了
这一句是判断TIM2CH1_CAPTURE_STA的第6位是否为1,目前也是成立的,我们在第一次中断函数中已经把这个为置1了
继续往下看,重点来了
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH1_CAPTURE_VAL=0XFFFF;
}else TIM2CH1_CAPTURE_STA++;
这几句是处理溢出问题的关键,if的条件希望大家仔细思考一下,有什么用,我先告诉大家它目前暂时还不成立,因为if不成立,所以程序执行else,TIM2CH1_CAPTURE_STA++,也就是TIM2CH1_CAPTURE_STA从0位开始自增,然后,本次中断结束
接着,下一次中断又来了,同样按照上一段落的执行逻辑,TIM2CH1_CAPTURE_STA自增,本次中断程序结束;接着,下下次TIM2CH1_CAPTURE_STA自增,本次中断程序结束…
我觉都大家应该能思考出来这个条件是TIM2CH1_CAPTURE_STA的0~5位全为1,也就是它的0到5位已经没办法自增了,那这个时候,说明溢出次数已经是2的6次方了。我觉得没有什么设备的脉冲能有这么长时间,说明脉冲异常了,检测不到下降沿了,因此,就强制结束捕获,在if中将TIM2CH1_CAPTURE_STA第7位置1,强行表示成功捕获了一次,在主函数中通过这个标志位做相应处理,这个标志位我在下文会强调,大家先继续往下看
if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
{
TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
TIM2CH1_CAPTURE_VAL=0XFFFF;
}
现在大家应该明白了这个中断函数是如何处理溢出事件了吧。
OK那现在就假设这个脉冲下降沿来了,因为我们在第一次中断中已经把边沿检测更改为下降沿捕获了,因此这个下降沿就会被捕获,然后CCR锁存此时的CNT值,然后进入中断函数,进入中断函数会执行下面这些代码:
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
{
if(TIM2CH1_CAPTURE_STA&0X40) //捕获到一个下降沿
{
TIM2CH1_CAPTURE_STA|=0X80; ///标记捕获成功
TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
}else //还未开始,第一次捕获上升沿
{
TIM2CH1_CAPTURE_STA=0; //清空
TIM2CH1_CAPTURE_VAL=0;
TIM_SetCounter(TIM2,0);
TIM2CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
}
}
在第一个中断函数中,我们已经把TIM2CH1_CAPTURE_STA的第6位置1了,表示已经捕获到高电平了,所以
if(TIM2CH1_CAPTURE_STA&0X40)
这个if条件是成立的
故程序会执行下面这些代码:
TIM2CH1_CAPTURE_STA|=0X80; //标记捕获成功
TIM2CH1_CAPTURE_VAL=TIM_GetCapture1(TIM2);//将CCR锁存值存放在TIM2CH1_CAPTURE_VAL中
TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
首先将TIM2CH1_CAPTURE_STA的第7位置1,表示成功捕获到了一个脉冲,然后将本次下降沿的CCR锁存值放在TIM2CH1_CAPTURE_VAL变量中,最后将输入捕获的边沿检测设置为上升沿,为下一次捕获做准备。
这里我详细说明一下TIM2CH1_CAPTURE_STA的第7位,这个位是捕获成功的标志,我们需要在main函数中不断检测这个位是否置1,一旦置1,就表明成功捕获了一个脉冲,我们就可以根据TIM2CH1_CAPTURE_VAL显示的CCR锁存值和TIM2CH1_CAPTURE_STA前6位显示的溢出次数快速计算出高电平的时间了,但是一定要手动清零该位,否则无法开启下一次捕获,前文已提到,这个位是执行中断的大前提!
OK,本文结束,谢谢大家耐心看到这里,再见哦~
最后
以上就是害怕小松鼠为你收集整理的输入捕获中断函数分析解读输入捕获中断函数:的全部内容,希望文章能够帮你解决输入捕获中断函数分析解读输入捕获中断函数:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复