STM32工作笔记0064---输入捕获实验

技术交流QQ群【JAVA,C++,Python,.NET,BigData,AI】:170933152 

咱们已经讲解了两个实验,一个是上面的定时器中断实验.

一个是下面的比较试验,也就是PWM输出实验.

扫描二维码关注公众号,回复: 11621276 查看本文章

然后今天看左边的输入捕获实验:

可以看到这里的,这个输入捕获的实验,这里

的时钟,还是由内部RC振荡器提供的,内部时钟来源,然后给右边的时基电路提供时钟来源

然后再下面的部分就是输入捕获的电路.

这里,讲解一下输入捕获的原理.

也就是他这里做了这样一个工作,

假设这里没有经过分频,也就是每次跳变都会检测上升沿,下降沿:

这个电路通过检测TIMX_CHX上的边沿信号,注意,这里的边沿信号,有上边沿信号和下边沿信号,

比如当跳变,产生有上边沿信号的时候,他会把这个上边沿信号的值,也就是TIMX_CNT这个值,

存入到TIMX_CCR寄存器中去.来完成一次捕获.

当产生下边沿的时候,也会把当前的值TIMX_CNT存入到,TIMX_CCR寄存器中去,这样

来完成一次捕获.

那么这里的,一个下降沿的捕获的值,减去,一个上升沿的捕获的值,一定程度上,就可以作为,这个高电平持续的时长,

也可以叫波的宽度.

或者,同样的也可以是低电平持续的时间.

这就是这个输入捕获的基本的工作过程.

接下来,拆解来看一下,

首先比如这里咱们用的是通道1,那么,信号经过通道1,以后通过滤波器,等下会说滤波器有什么用,然后

信号再经过边沿检测器,然后可以看到,比如这里检测到的是TI1F_Rising上升沿,经过选择器后0,TI1FP1,

然后,再经过选择器,这里标注01 10 11的地方,然后再经过分频器,最终输出一个信号,当输出信号的时候,

会触发中断,把当前捕获的值,TIMX_CNT存入到TIMX_CCR寄存器中去.

然后接下来,把这个图分成下面四个部分再进行讲解.

接下来先看滤波这里:

实际上,首先这里有个时钟,这个时钟就是FCK_INT,这个时钟就是来源于前面说的RC时钟振荡器,

这个时钟,

然后:

然后可以看到这里有个FDTS,这个和FCK_INT实际上是有关系的,他们的关系可以通过

TIMX_CR1寄存器的CKD这个位来设置,比如这个寄存器设置为00的时候,那么实际上

FDTS=FCK_INT这个时钟,注意FDTS是滤波用的时钟,而FCK_INT这个时钟是内部振荡器提供的时钟.

这个就是TIMX_CR1这个寄存器,可以看到有个CKD这个位.

注意这里:10 TDTS=4* TCK_INT 这个意思,实际上是

TDTS=1/4 * TCK_INT ,注意这里是四分之一,因为后面说了这个是分频比.

所以,这里的TIMX_CR1寄存器的的CKD位,配置的实际上就是FDTS和FCK_INT的关系,

这个FDTS的时钟就是滤波器的时钟频率,然后

可以看到TIMX_CCMR1这个寄存器,这个寄存器的IC1F位配置的是0011的时候,

并且这个时候设置IC1位,设置的是让它上升沿的时候捕获,那么比如说这里信号达到上升沿的时候,

在捕获到上升沿的时候,会以FCK_INT的频率,注意这个频率实际上经过分频的也可以说是FDTS的频率,

也就是,连续采样8次通道1上面的电平,如果8次采样都是高电平,才说明这是一个有效的

触发,这个时候就会触发输入捕获中断,这样做的目的其实就是为了防止信号抖动,来做个滤波的.

这里IC1F,输入捕获1滤波器可以通过文档查到,可以看设置是0011的时候,这里N=8

注意,可以看到这里,上升沿的时候,设置了IC1F以后,可以使用FDTS这个被FCK_INT被分频后的频率,被连续监测8次,

如果都是高电平,才确定是个有效的触发,这个实际上是一个防抖的操作.所以这里的滤波的概念,就是把电平的抖动,给

通过滤波滤掉.

这里实际上设置的就是,上升沿捕获,还是下降沿捕获,

通过设置TIMX_CCER寄存器的CC1P这个位,来设置,是上升沿的时候去捕获呢,还是下降沿的时候去捕获.

这里TIMX_CCMR1通过设置CC1S位来实现,设置为00的时候,就是把CC1通道设置为是输出的高低电平,

然后如果01的话,那么CC1这个通道就被设置为了输入,同时,IC1也就是最终输入的数据,他的来源设置在

定时器的TI1上,如果设置为10的话,CC1通道也就是最终捕获输入的数据,他的数据来源就把设置在

TI2这个定时器的通道上.

这里要注意,CC1是一个数据的出口,也就是说,一个定时器有4个通道,那么数据会通过

这4个通道,最终数据都会走到CC1这里.

这里也就是说可以通过配置实现,IC1的值是来至于TI2,还是TI1,还是TI3..等,这个是可以通过配置寄存器TIMX_CCMR

寄存器的CC1S位来进行配置的.

不过一般的情况就是TI1定时器通道就对应IC1,TI2通道就对应IC2,一般不去做特殊的配置.

这里的意思是,分频器,以通道1为例,也就是说,如果这里通过寄存器TIMX_CCMR1的ICPS位的1到0位,来配置

配置为01的时候,也就是说每2个事件触发一次捕获,意思就是说,跟上图中一样,每检测到两个上升沿的时候,再

触发一次中断.

也就是检测到第一次上升沿的时候,没有动作,再检测到第二个上升沿的时候才触发一次中断.

这里也就是一个分频的概念.

这个很好理解,就是说,可以配置TIMX_DIER寄存器,比如这里配置,CC1E,注意这里的CC1指的是哪个通道,

CC1就是通道1,E是使能的意思,也就是说配置了TIMX_DIER这个寄存器的CC1E这个位的话,就相当于

设置了设置允许捕获,还是不允许捕获.

如果设置了上升沿捕获,那么同时这里也设置了开启允许捕获,那么在不分频的情况下,每次上升沿都会

触发捕获中断.去获取电平值.

这里就是如何在文档中查看对应的引脚.

比如这里定时器5的通道2对应的PA1,定时器5的通道3对应的是PA2引脚.

这个对应的引脚要可以通过文档查出来.

接下来看看代码,对于输入捕获这个是如何配置的

首先来看看,要用到的初始化通道的函数:

这里第一个参数是选择哪个定时器,第二个参数是一个结构体,用来定义

第一个是设定定时器的通道是1,2,3,4,

第二个是设置捕获极性,高电平有效,还是低电平有效

第三个参数是设置映射关系,前面说的,IC1这个通道的数据,可以是定时器的TI1通道过来的,也可以是定时器TI2通道过来

的这个取决于设置.而这个地方就是设置这个的.下图中设置的:

第四个参数是分频系数.也就是下面的设置

第五个参数是滤波器..

 

然后下面,还有个极性设置,就是说设置是捕获上升沿,还是下降沿.

上面这个函数就是设置极性,就是上升沿捕获,还是下降沿捕获

他的参数有,第一个是哪个定时器,第二个是配置是上升沿捕获,还是下降沿捕获什么的

然后还可以去获取,捕获值,也就说,

也就说,捕获到的值,会存入到这个捕获比较寄存器,咱们可以读取,这个捕获比较寄存器的值.

接下来,要总结一下这个步骤

步骤是

1.初始化定时器和通道对应的IO口的时钟

2.初始化IO口,模式是输入模式.

3.然后初始化定时器的装载值ARR,和分频系数PSC这样就能确定,定时器的周期

4.然后初始化捕获通道,什么上升沿捕获,下降沿捕获什么的

5.然后开启捕获中断.

6.去使能定时器,

7.编写中断函数

说一下这个实验:

这里比如可以捕获WK_UP按键的电平,当按下电平的时候,为高电平

当松开按键的时候是低电平,然后

如果我们设置上升沿捕获,那么上升沿的时候,比如捕获值是0,那么下降沿的时候再去捕获

捕获值是A,那么用(A-0)然后再乘以一个周期时间也就是每个时钟周期的时间1/F

就是这个高电平持续的时间.

比如频率是72mhz,也就是震动72000 000次用1s,那么震动一次,也就是一个周期的时间是1/72000000

单单的通过上面说的方法去计算,这个高电平的持续时间,实际上是不对的,

注意可能会有上面的这样的情况,比如,假设如果一个高电平持续的时间非常长的话,比如长到,比

定时器从0计数到ARR的值,还要长,那么这个时候,比如第一次从0计数到ARR的值的时候,还是高电平

那么第二次从0计数到ARR值的时候,还是高电平,第三次从0计数到ARR值一半的时候,这个时候高电平结束了,

那么这个时候,因为第一次上升沿记录的值是0,然后第三次计数的值是ARR指的一半,这样计算的持续的时间

就成了ARR的一半减去0然后再乘以周期时间,那么很明显这是不对的,因为实际的持续时间应该是

(ARR的值+ARR的值+ARR的值的一半)再乘以周期,这样才能得出正确的高电平的持续时间.

所以针对这个问题,我们就做了一个更严谨的操作.

是这样实现的,用了这样一个8位的变量,

bit7是用来记录捕获完成的标志,bit6用来记录捕获到高电平的标志,

bit5到0,用来记录捕获高电平后定时器的次数.

那么这样的话,就是,每次溢出的时候,bit5到0位就会加1,捕获到高电平的时候也就是上部横着的部分的时候bit6就会是1,然后

捕获到下降沿的的时候bit7就会置1.

通过记录这几个值,就可以准确的计算出高电平持续的时间了.

上面是一个简单的解释,接下来,咱们看着代码,再来看

打开输入捕获实验的代码

首先看timer.h

这里主要看这个TIM5_Cap_Init()这个函数

定时器5通道1输入捕获的配置

和,下面这个定时器5通道,中断服务程序.

首先看这个:

TIM5_Cap_Init()这个函数,

这里,可以看到,首先

void TIM5_Cap_Init(u16 arr,u16 psc)
{     
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
       NVIC_InitTypeDef NVIC_InitStructure;

    //1.首先使能定时器和GPIO的时钟.
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);    //使能TIM5时钟
     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA时钟
    
    //2.然后设置GPIOA的PA0为输入模式,因为
    //这里WK_UP按键,松开的时候是低电平,按下的时候是高电平
    //所以这里需要下拉
    GPIO_InitStructure.GPIO_Pin  = GPIO_Pin_0;  //PA0 清除之前设置  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //PA0 输入  
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_ResetBits(GPIOA,GPIO_Pin_0);                         //PA0 下拉
    
   


}

关于这里设置下拉输入,以前按键的实验时候也有讲过.

然后再去看一下:

初始化定时器5这个部分,之前已经说过了,这里就不多说了.

这里,设置TIM5的输入捕获参数

这个是重点说的

其实主要是设置上面的4个部分,

1.设置滤波器,是获取8次高电平才算是有效电平什么的,

2.然后设置边沿检测器,来设置上边沿触发,还是下边沿触发,

3.然后设置映射,是直接就过去,还是配置IC1的数据来源设置成TI2等

4.设置分频器,设置分频规则,是不分频还是怎么样

然后再看一下这个输入捕获初始化函数:  

    //初始化TIM5输入捕获参数
    //1.可以看到,每个参数 TIM_Channel_1 第一个设置通道,确定用哪个通道,比如
    //这里使用通道1
	TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //CC1S=01 	选择输入端 IC1映射到TI1上
    //2.设置是上升沿捕获,还是下降沿捕获
  	TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;	//上升沿捕获
    //3.这里TIM_ICSelection_DirectTI就是把通道1,CC1S直接映射到
    //TI1这个定时器的通道1上._UnDirectTI,是把通道1映射到定时器的其他通道上
  	TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
    //4.这里TIM_ICPSC_DIV1,这是不分频也就是每个上升沿都会被捕获
    //然后如果这地方是2,那么就是捕获到2个上升沿的时候,会触发中断.
    //还有4,8什么的
  	TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;	 //配置输入分频,不分频 
    //5.这里不滤波的意思是,不进行,对滤波进行设置,比如,连续采集8次都是高电平,才认为是有效
    //的,这个操作.
  	TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
  	TIM_ICInit(TIM5, &TIM5_ICInitStructure);

接下来,

	//中断分组初始化 
    //1.开始设置中断分组初始化,设置中断定时器5的中断
	NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;  //TIM5中断
    //2.设置中断抢占优先级2
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
	//3.设置响应优先级
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级0级
	//4.设置优先级通道使能
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
	//5.初始化中断优先级
    NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器 
	
    //1.然后这里再配置,第一个是定时器,第二个是配置中断类型
    //首先配置更新中断,就是说向上产生了ARR溢出会触发中断
    //然后就是CC1,输入捕获中断,捕获到电平后的中断值
	TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断 ,允许CC1IE捕获中断	

这样设置以后再去,使能定时器5,然后,相应的这个PA0口就开始捕获了.

   	TIM_Cmd(TIM5,ENABLE ); 	//使能定时器5

然后这里,如果设置了上升沿捕获,那么上升沿的时候就会触发捕获中断,然后

下降沿的时候,就会有下降沿中断

然后如果向上溢出了,还会有ARR溢出的,更新中断.

所以这个中断函数会复杂一些.

其实就是按照上面说的那个情况进行封装的.

u8  TIM5CH1_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值

//1.定时器5中断服务程序	 
void TIM5_IRQHandler(void)
{ 
    //2.这个0x80,也就是1000 0000 也就是bit8是0 的时候
    //也就是捕获完成标志位bit7还是0的时候,也就是还没有成功捕获
 	if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{	  
        //3.这里判断是不是更新溢出了,如果是溢出的话,也就是不是RESET的时候
		if (TIM_GetITStatus(TIM5, TIM_IT_Update) != RESET)
		 
		{	    
           //4.如果是更新溢出了,那么已经捕获到了高电平了
           //
			if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
			{   
                //6.这里如果捕获到高电平,并且还没有成功捕获一次的
                //话,也就是bit7还是0的时候,那么就一直++
                //++的如果超过bit0到bit5了也就是0X 0011 1111
                //那么这里就认为这个高电平实在是持续时间太长了,这个时候
                //因为再+1就开始占用第6位了,bit6了,所以这个时候就认为
                //他已经实现了一次完整的捕获,就把,
                //bit7最高位设置为1,也就是0X 1000 0000
				if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM5CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
                    //7.然后当前值,设置为1111 1111
                    //这个初始值,也就是ARR的值.
					TIM5CH1_CAPTURE_VAL=0XFFFF;
				}else{
                     //5.如果已经捕获到高电平了的话,那么就
                     //记录一下捕获到高电平的次数
                        TIM5CH1_CAPTURE_STA++;
                      } 
			}	 
		}
     //8.如果这里发生了捕获事件的话,那么
	if (TIM_GetITStatus(TIM5, TIM_IT_CC1) != RESET)//捕获1发生捕获事件
		{	
            //9.判断这个上次是不是0x 0100 0000
            //bit6是1,也就是上次捕获到的是高电平,那么
            //触发捕获中断的时候,就应该是下降沿捕获
			if(TIM5CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	
                //10.每当捕获到一次下降沿的时候,就认为
                //成功捕获了一次上升沿了
                //就把bit7位置1  			
				TIM5CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
                //11.然后读取捕获比较寄存器的值,也就是计数到哪里了
				TIM5CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5);
                //12.然后因为这次是下降沿,所以这里再把这里设置为上升沿捕获
                //
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{
               //13.这里如果捕获的不是下降沿就说明捕获的是上升沿
               //那么,这里捕获到上升沿的时候,就要把当前值清空为0,
               //然后把记录状态的变量也设置成0
               //然后因为是上升沿捕获,所以下次就开启下降沿捕获就可以了
				TIM5CH1_CAPTURE_STA=0;			//清空
				TIM5CH1_CAPTURE_VAL=0;
	 			TIM_SetCounter(TIM5,0);
				TIM5CH1_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
		   		TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
			}		    
		}			     	    					   
 	}
 
    //14.因为每次中断会把中断位置1,所以这里使用完以后还要把中断位清除掉.
    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update); //清除中断标志位
 
}

然后这里,TIM5_Cap_Init(),这个第一个参数设置ARR的值,因为咱们这里

定时器是16位定时器,所以最大值就是0XFFFF

然后第二个是频率,咱们知道输入的频率是72Mhz所以,这里设置预分频系数为72M的话,

其实就是以1Mhz的频率进行计数.

extern u8  TIM5CH1_CAPTURE_STA;		//输入捕获状态		    				
extern u16	TIM5CH1_CAPTURE_VAL;	//输入捕获值	
 int main(void)
 {		
 	u32 temp=0; 
	delay_init();	    	 //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);	 //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
	uart_init(115200);	 //串口初始化为115200
 	LED_Init();			     //LED端口初始化
 
 	TIM3_PWM_Init(899,0); 		//不分频。PWM频率=72000/(899+1)=80Khz
    //1.上面都是以前的代码,上一讲的,不明白可以再看看上一讲的
    //下面这里是
    //然后这里,TIM5_Cap_Init(),这个第一个参数设置ARR的值,因为咱们这里
    //定时器是16位定时器,所以最大值就是0XFFFF
    //然后第二个是频率,咱们知道输入的频率是72Mhz所以,这里设置预分频系数为72M的话,
    //其实就是以1Mhz的频率进行计数.
 	TIM5_Cap_Init(0XFFFF,72-1);	//以1Mhz的频率计数 
   	while(1)
	{
 		delay_ms(10);
        //2.下面是上一讲的内容,输出PWM波的
		TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);

		if(TIM_GetCapture2(TIM3)==300)TIM_SetCompare2(TIM3,0);	
		//3.这里捕获到一次上升沿以后,就表示完成了一次捕获,bit7
        //位变成1了,完成一次捕获以后.
 		if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
		{
            //4.这里首先要获取TIM5CH1_CAPTURE_STA,变量的
            //低bit5到bit0也就是,高电平溢出了几次
			temp=TIM5CH1_CAPTURE_STA&0X3F;
            //5.高电平溢出的个数*,因为计数器,会从0数到0xffff,算是溢出
            //而65536这个数就是0xffff的值,然后从0数到65536这一个周期是
            //1ms,所以溢出次数所花的时间是temp*65536
            //
			temp*=65536;//溢出时间总和
			//6.然后,还得加上最后一次的值
            //最后一次计数到了多少的值
            //这样就是总共的时间
            temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);//打印总的高点平时间
            //7.获取完高电平所持续的时间以后,把TIM5CH1_CAPTURE_STA
            //这个变量清0,开启下一次捕获
			TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
		}
	}
 }

然后来测试一下:

编译下载:

注意因为这里会把高电平持续的时间,打印到串口,所以

打开串口调试工具.

这个灯闪烁.是因为代码里面有PWM的输出,

然后,这个时候如果我按下KEY0的话,WK_UP的时候,这样就会产生

高电平,而产生高电平,就会有上升沿,下降沿,这样就会被,定时器捕获到,

最终,一次捕获完成以后,就会把高电平持续的时间,通过串口打印出来.

可以看到情况.按下按键的时候,就发生了捕获,连续按下3次就会有连续3次的捕获.

猜你喜欢

转载自blog.csdn.net/lidew521/article/details/108298077