STM32单个定时器四通道输入捕获

    以前就纠结过能不能一个定时器进行多路的输入捕获,因为毕竟输出四路的PWM是轻松随意的,当时大概想了一下觉得可能会比较麻烦就一直没去尝试,最近组里的同学做方波测频和测占空比遇到了问题,又提到了这个,今天仔细想了一下有了思路就写程序然后上板子试了一下,解决了这个问题。

    首先我们先看单路的输入捕获是怎么实现的

    定时器的初始化函数:

//定时器2通道1输入捕获配置
//arr:自动重装值
//psc:时钟预分频数
void TIM2_Cap_Init(u16 arr,u16 psc)
{		 
	RCC->APB1ENR|=1<<0;   	//TIM2 时钟使能 
	RCC->APB2ENR|=1<<2;    	//使能PORTA时钟  
	 
	GPIOA->CRL&=0XFFFFFFF0;	//PA0 清除之前设置  
	GPIOA->CRL|=0X00000008;	//PA0 输入   
	GPIOA->ODR|=0<<0;		//PA0 下拉
	  
 	TIM2->ARR=arr;  		//设定计数器自动重装值   
	TIM2->PSC=psc;  		//预分频器 

	TIM2->CCMR1|=1<<0;		//CC1S=01 	选择输入端 IC1映射到TI1上
 	TIM2->CCMR1|=1<<4; 		//IC1F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
 	TIM2->CCMR1|=0<<10; 	//IC2PS=00 	配置输入分频,不分频 

	TIM2->CCER|=0<<1; 		//CC1P=0	上升沿捕获
	TIM2->CCER|=1<<0; 		//CC1E=1 	允许捕获计数器的值到捕获寄存器中

	TIM2->DIER|=1<<1;   	//允许捕获中断				
	TIM2->DIER|=1<<0;   	//允许更新中断	
	TIM2->CR1|=0x01;    	//使能定时器2
	MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2	   
}

    输入捕获的中断函数:

//捕获状态
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到高电平;1,已经捕获到高电平了.
//[5:0]:捕获高电平后溢出的次数
u8  TIM2CH1_CAPTURE_STA=0;	            //输入捕获状态		    				
u16	TIM2CH1_CAPTURE_VAL;	            //输入捕获值
//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{ 		    
	u16 tsr;
	tsr=TIM2->SR;
 	if((TIM2CH1_CAPTURE_STA&0X80)==0)    //还未成功捕获	
	{
		if(tsr&0X01)//溢出
		{	    
			if(TIM2CH1_CAPTURE_STA&0X40)    //已经捕获到高电平了
			{
				if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)  //高电平太长了
				{
					TIM2CH1_CAPTURE_STA|=0X80;        //标记成功捕获了一次
					TIM2CH1_CAPTURE_VAL=0XFFFF;
				}else TIM2CH1_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x02)//捕获1发生捕获事件
		{	
			if(TIM2CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
			    TIM2CH1_CAPTURE_VAL=TIM2->CCR1;	//获取当前的捕获值.
	 			TIM2->CCER&=~(1<<1);		//CC1P=0 设置为上升沿捕获
			}else  							//还未开始,第一次捕获上升沿
			{ 
				TIM2CH1_CAPTURE_VAL=0;
				TIM2CH1_CAPTURE_STA=0X40;	//标记捕获到了上升沿
				TIM2->CNT=0;				//计数器清空
				TIM2->CCER|=1<<1; 			//CC1P=1 设置为下降沿捕获 
			}		    
		}			     	    					   
 	}
	TIM2->SR=0;//清除中断标志位 	    
}

    主函数读取并显示:

int main(void)
{			 
	u32 temp=0;  	
 	Stm32_Clock_Init(9);			//系统时钟设置
	uart_init(72,9600);	 			//串口初始化为9600
	delay_init(72);	   	 			//延时初始化 
	LED_Init();		  				//初始化与LED连接的硬件接口
 	TIM1_PWM_Init(899,36-1); 			//不分频。PWM频率=72000/(899+1)=80Khz
 	TIM2_Cap_Init(0XFFFF,72-1);		//以1Mhz的频率计数 
   	while(1)
	{
 		delay_ms(10);
		LED0_PWM_VAL++;
		if(LED0_PWM_VAL==300)LED0_PWM_VAL=0;	 		 
		if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
		{
			temp=TIM2CH1_CAPTURE_STA&0X3F;
			temp*=65536;					//溢出时间总和
			temp+=TIM2CH1_CAPTURE_VAL;		//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);	//打印总的高点平时间
 			TIM2CH1_CAPTURE_STA=0;			//开启下一次捕获
 		}
	}
}

    具体的原理我就不细说了,如果不了解可以去找一下原子的教程,那里面讲得是很详细的,要实现四通道的输入捕获初始化是肯定要修改的,但是最为重要的是修改中断函数中的内容。

    在单路输出中,中断的执行流程是,当新开始一次捕获并且捕获到高电平(就是上升沿),将TIM2->CNT寄存器置0,并且将捕获电平变为低电平(去捕获下降沿),表示新开始一次捕获,然后如果持续高电平使定时器溢出,TIMCH1_CAPTURE_STA会自增1,然后捕获到下降沿以后就完成了一次捕获,这时去读取当前CCR寄存器的值,然后在加上溢出次数*65536得到的就是整个高电平器件定时器总计数值,通过计算计数频率就可以得到高脉冲的时间。

    四通道输入捕获原理

    要实现四路的输入捕获如果每开始一次新的捕获就将CNT值置零是肯定不可以的,一个定时器只有一个CNT寄存器这样会是捕获完全混乱,所以,我的解决方法是:用一个变量Date1来存当开始一次新的捕获时CNT寄存器的值,然后中间步骤和单通道是完全相同的,完成捕获后将那时CCR寄存器的值写到Date2。计算总计数值就是溢出次数*65536+Date2-Date1

    四路输入捕获初始化函数

    注意:我这里将TIM2进行了部分重映射,在不进行重映射的情况下(PA0,PA1,PA2,PA3)通道四无法触发捕获中断)进行部分重映射(PA0,PA1,PB10,PB11)后就可以正常使用,我觉得可能是板子的问题

//定时器2通道1-4输入捕获配置
//arr:自动重装值
//psc:时钟预分频数
void TIM2_Cap_Init(u16 arr,u16 psc)
{		 
	RCC->APB1ENR|=1<<0;   	//TIM2 时钟使能 
	RCC->APB2ENR|=1<<2;    	//使能PORTA时钟  
	RCC->APB2ENR|=1<<3;    	//使能PORTB时钟  
	
	RCC->APB2ENR|=1<<0;		//AFIO时钟使能
	AFIO->MAPR|=2<<8;		//TIM2部分映射
	
	GPIOA->CRL&=0XFFFFFF00;	//PA0,PA1清除之前设置  
	GPIOA->CRL|=0X00000088;	//PA0,PA1输入  
	GPIOA->ODR|=0<<1;		//PA0,PA1下拉
	GPIOB->CRH&=0xFFFF00FF;	//PB10,PB11清除之前设置
	GPIOB->CRH|=0x00008800;	//PB10,PB11输入
	GPIOB->ODR|=0<<11;		//PB10,PB11下拉
 	TIM2->ARR=arr;  		//设定计数器自动重装值   
	TIM2->PSC=psc;  		//预分频器 
	//CH1
	TIM2->CCMR1|=1<<0;		//CC1S=01 	选择输入端 IC1映射到TI1上
 	TIM2->CCMR1|=1<<4; 		//IC1F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
 	TIM2->CCMR1|=0<<2; 		//IC1PS=00 	配置输入分频,不分频 
	TIM2->CCER|=0<<1; 		//CC1P=0	上升沿捕获
	TIM2->CCER|=1<<0; 		//CC1E=1 	允许捕获计数器的值到捕获寄存器中
	//CH2
	TIM2->CCMR1|=1<<8;		//CC2S=01 	选择输入端 IC1映射到TI1上
 	TIM2->CCMR1|=1<<12; 	//IC2F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
 	TIM2->CCMR1|=0<<10; 	//IC2PS=00 	配置输入分频,不分频 
	TIM2->CCER|=0<<5; 		//CC2P=0	上升沿捕获
	TIM2->CCER|=1<<4; 		//CC2E=1 	允许捕获计数器的值到捕获寄存器中
	//CH3
	TIM2->CCMR2|=1<<0;		//CC3S=01 	选择输入端 IC1映射到TI1上
 	TIM2->CCMR2|=1<<4; 		//IC3F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
 	TIM2->CCMR2|=0<<2; 		//IC3PS=00 	配置输入分频,不分频 
	TIM2->CCER|=0<<9; 		//CC3P=0	上升沿捕获
	TIM2->CCER|=1<<8; 		//CC3E=1 	允许捕获计数器的值到捕获寄存器中
	//CH4
	TIM2->CCMR2|=1<<8;		//CC4S=01 	选择输入端 IC1映射到TI1上
 	TIM2->CCMR2|=1<<12; 	//IC4F=0001 配置输入滤波器 以Fck_int采样,2个事件后有效
 	TIM2->CCMR2|=0<<10; 	//IC4PS=00 	配置输入分频,不分频
	TIM2->CCER|=0<<13; 		//CC4P=0	上升沿捕获
	TIM2->CCER|=1<<12; 		//CC4E=1 	允许捕获计数器的值到捕获寄存器中
	

	//中断使能
	TIM2->DIER|=1<<1;   	//允许捕获1中断		
	TIM2->DIER|=1<<2;   	//允许捕获2中断	
	TIM2->DIER|=1<<3;   	//允许捕获3中断	
	TIM2->DIER|=1<<4;   	//允许捕获4中断	
	
	TIM2->DIER|=1<<0;   	//允许更新中断	
	TIM2->CR1|=0x01;    	//使能定时器2
	MY_NVIC_Init(2,0,TIM2_IRQn,2);//抢占2,子优先级0,组2	   
}

    中断服务函数:

//捕获状态
//[7]:0,没有成功的捕获;1,成功捕获到一次.
//[6]:0,还没捕获到高电平;1,已经捕获到高电平了.
//[5:0]:捕获高电平后溢出的次数
//CH1
u8  TIM2CH1_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM2CH1_CAPTURE_Date2;	//数据2
u16 TIM2CH1_CAPTURE_Date1;	//数据1
//CH2
u8  TIM2CH2_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM2CH2_CAPTURE_Date2;	//数据2
u16 TIM2CH2_CAPTURE_Date1;	//数据1
//CH3
u8  TIM2CH3_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM2CH3_CAPTURE_Date2;	//数据2
u16 TIM2CH3_CAPTURE_Date1;	//数据1
//CH4
u8  TIM2CH4_CAPTURE_STA=0;	//输入捕获状态		    				
u16	TIM2CH4_CAPTURE_Date2;	//数据2
u16 TIM2CH4_CAPTURE_Date1;	//数据1

//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{ 		    
	u16 tsr;
	tsr=TIM2->SR;
	//CH1中断处理
 	if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{
		if(tsr&0X01)//溢出
		{	    
			if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH1_CAPTURE_Date2=0XFFFF;
				}else TIM2CH1_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x02)//捕获1发生捕获事件
		{	
			if(TIM2CH1_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH1_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
			    TIM2CH1_CAPTURE_Date2=TIM2->CCR1;	//获取当前的捕获值.
	 			TIM2->CCER&=~(1<<1);			//CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{ 
				TIM2CH1_CAPTURE_Date2=0;
				TIM2CH1_CAPTURE_STA=0X40;		//标记捕获到了上升沿
				TIM2CH1_CAPTURE_Date1=TIM2->CCR1;
				TIM2->CCER|=1<<1; 				//CC1P=1 设置为下降沿捕获 
			}		    
		}			     	    					   
 	}
	//CH2中断处理
	if((TIM2CH2_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{
		if(tsr&0X01)//溢出
		{	    
			if(TIM2CH2_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH2_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH2_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH2_CAPTURE_Date2=0XFFFF;
				}else TIM2CH2_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x04)//捕获1发生捕获事件
		{	
			if(TIM2CH2_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH2_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
			    TIM2CH2_CAPTURE_Date2=TIM2->CCR2;	//获取当前的捕获值.
	 			TIM2->CCER&=~(1<<5);			//CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{ 
				TIM2CH2_CAPTURE_Date2=0;
				TIM2CH2_CAPTURE_STA=0X40;		//标记捕获到了上升沿
				TIM2CH2_CAPTURE_Date1=TIM2->CCR2;
				TIM2->CCER|=1<<5; 				//CC1P=1 设置为下降沿捕获 
			}		    
		}			     	    					   
 	}
	//CH3中断处理
	if((TIM2CH3_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{
		if(tsr&0X01)//溢出
		{	    
			if(TIM2CH3_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH3_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH3_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH3_CAPTURE_Date2=0XFFFF;
				}else TIM2CH3_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x08)//捕获1发生捕获事件
		{	
			if(TIM2CH3_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH3_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
			    TIM2CH3_CAPTURE_Date2=TIM2->CCR3;	//获取当前的捕获值.
	 			TIM2->CCER&=~(1<<9);			//CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{ 
				TIM2CH3_CAPTURE_Date2=0;
				TIM2CH3_CAPTURE_STA=0X40;		//标记捕获到了上升沿
				TIM2CH3_CAPTURE_Date1=TIM2->CCR3;
				TIM2->CCER|=1<<9; 				//CC1P=1 设置为下降沿捕获 
			}		    
		}			     	    					   
 	}
	//CH4中断处理
	if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{
		if(tsr&0X01)//溢出
		{	 
			if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH4_CAPTURE_Date2=0XFFFF;
				}else TIM2CH4_CAPTURE_STA++;
			}	 
		}
		if(tsr&0x10)//捕获1发生捕获事件
		{	
			if(TIM2CH4_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH4_CAPTURE_STA|=0X80;		//标记成功捕获到一次高电平脉宽
			    TIM2CH4_CAPTURE_Date2=TIM2->CCR4;	//获取当前的捕获值.
	 			TIM2->CCER&=~(1<<13);			//CC1P=0 设置为上升沿捕获
			}else  								//还未开始,第一次捕获上升沿
			{ 
				TIM2CH4_CAPTURE_Date2=0;
				TIM2CH4_CAPTURE_STA=0X40;		//标记捕获到了上升沿
				TIM2CH4_CAPTURE_Date1=TIM2->CCR4;
				TIM2->CCER|=1<<13; 				//CC1P=1 设置为下降沿捕获 
			}		    
		}			     	    					   
 	}
	
	
	TIM2->SR=0;//清除中断标志位 	    
}

       主函数(显示四路信号高点平的时间):

int main(void)
{	
	u32 temp1=0,temp2=0,temp3=0,temp4=0;
	Stm32_Clock_Init(9);					  	//初始化系统时钟		
	delay_init(72);								//初始化延时	
	uart_init(72,115200);						//串口初始化
	TIM1_PWM_Init(1000-1,36-1); 					//不分频。PWM频率=72000/(899+1)=80Khz
	TIM2_Cap_Init(0XFFFF,72-1);					//以2Mhz的频率计数 
	LED_Init();
	TIM1_CH1_PWM_VAL=100;
	while(1)  
  {		 
		if(TIM2CH1_CAPTURE_STA&0X80)			//成功捕获到了一次高电平
		{
			temp1=TIM2CH1_CAPTURE_STA&0X3F;
			temp1*=65536;						//溢出时间总和
			temp1+=TIM2CH1_CAPTURE_Date2;		
			temp1-=TIM2CH1_CAPTURE_Date1;		//得到总的高电平时间
			printf("TIM2_CH1_HIGH:%d us\r\n",temp1);		//打印总的高点平时间
			TIM2CH1_CAPTURE_Date1=0;
 			TIM2CH1_CAPTURE_STA=0;				//开启下一次捕获
 		}
		if(TIM2CH2_CAPTURE_STA&0X80)			//成功捕获到了一次高电平
		{
			temp2=TIM2CH2_CAPTURE_STA&0X3F;
			temp2*=65536;						//溢出时间总和
			temp2+=TIM2CH2_CAPTURE_Date2;		
			temp2-=TIM2CH2_CAPTURE_Date1;		//得到总的高电平时间
			printf("TIM2_CH2_HIGH:%d us\r\n",temp2);		//打印总的高点平时间
			TIM2CH2_CAPTURE_Date1=0;
 			TIM2CH2_CAPTURE_STA=0;				//开启下一次捕获
 		}
		if(TIM2CH3_CAPTURE_STA&0X80)			//成功捕获到了一次高电平
		{
			temp3=TIM2CH3_CAPTURE_STA&0X3F;
			temp3*=65536;						//溢出时间总和
			temp3+=TIM2CH3_CAPTURE_Date2;		
			temp3-=TIM2CH3_CAPTURE_Date1;		//得到总的高电平时间
			printf("TIM2_CH3_HIGH:%d us\r\n",temp3);		//打印总的高点平时间
			TIM2CH3_CAPTURE_Date1=0;
 			TIM2CH3_CAPTURE_STA=0;				//开启下一次捕获
		}
		if(TIM2CH4_CAPTURE_STA&0X80)			//成功捕获到了一次高电平
		{
			temp4=TIM2CH4_CAPTURE_STA&0X3F;
			temp4*=65536;						//溢出时间总和
			temp4+=TIM2CH4_CAPTURE_Date2;		
			temp4-=TIM2CH4_CAPTURE_Date1;		//得到总的高电平时间
			printf("TIM2_CH4_HIGH:%d us\r\n",temp4);		//打印总的高点平时间
			TIM2CH4_CAPTURE_Date1=0;
 			TIM2CH4_CAPTURE_STA=0;				//开启下一次捕获
		}
		printf("\r\n");
		LED=!LED;
		delay_ms(10);
  }  
}

    

猜你喜欢

转载自blog.csdn.net/a568713197/article/details/84798138