STM32定时器----多通道PWM捕获

由于项目需要利用一个定时器捕获2通道的PWM输入,所以近两天研究了一下多通道PWM的捕获。

功能实现:在头文件中修改对应通道的宏定义的值(1或者0),开启对应通道的PWM捕获。

                可通过修改对应宏定义,更改定时器中断间隔,以及中断函数中的溢出次数,以此

                决定定时器所能捕获的最低以及最高频率。


代码如下:

/***********************************头文件********************************/

typedef struct
{
	  u16 Ccr1;         //缓存CCR寄存器的值
	  u16 Ccr2;
	  u16 Ccr3;
	  u16 Ccr4;
	  u16 Ic1_Over;     //记录ARR溢出次数
	  u16 Ic2_Over;
      u16 Ic3_Over;
	  u16 Ic4_Over;
	  u16 Ic1_Fre;      //缓存频率值
	  u16 Ic2_Fre;
	  u16 Ic3_Fre;
	  u16 Ic4_Fre;
	
}TIM_ICS;   //TIM3捕获结构体

//TIM捕获通道开启宏定义,1:开启 0:关闭
#define  TIM_ICCH1        1    
#define  TIM_ICCH2        1
#define  TIM_ICCH3        1
#define  TIM_ICCH4        1

//最小捕获频率:72000000/(120*60000)=10Hz
#define  TIM_ICARR        (60000-1)          //重装载寄存器填充值
#define  TIM_ICPSC        0                  //分频系数  
#define  TIM_ICFIT        0x0f               //定时器捕获滤波系数  
#define  TIM_OverF        (120-1)            //最大ARR溢出次数

#define  TIM_ICSR         (u16 *)&TIM3->SR   //选择定时器的SR寄存器地址
#define  TIM_ICCCR        (u16 *)&TIM3->CCR1 //选择定时器的CCR寄存器地址


/***********************************C文件********************************/

//TIM3捕获结构体变量
TIM_ICS TIM_Ic;    

//TIM_IcChannel 用来开启TIM相应通道,且用在中断函数判断中断状态
u16 TIM_IcChannel = 1+(TIM_ICCH1<<1)+(TIM_ICCH2<<2)+(TIM_ICCH3<<3)+(TIM_ICCH4<<4);

void TIM3_PWMICInit(void)
{
	u8 i=0;
	
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
	TIM_ICInitTypeDef  TIM_ICInitStruct;
	NVIC_InitTypeDef  NVIC_InitStruct;
	GPIO_InitTypeDef  GPIO_InitStruct;
	
	/***********************NVIC配置*****************************/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
	
	NVIC_InitStruct.NVIC_IRQChannel = TIM3_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2;
	
	NVIC_Init(&NVIC_InitStruct);
	
	/**********************TIM3 GPIO配置*****************************/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;

	/**********************初始化TIM3*******************************/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = TIM_ICARR;      
	TIM_TimeBaseInitStruct.TIM_Prescaler = TIM_ICPSC;     

	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
	
	/**********************初始化TIM3 IC*******************************/
	TIM_ICInitStruct.TIM_ICFilter = TIM_ICFIT;
	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
	TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
	TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI ;

   	if(TIM_ICCH1==1)
	{
	  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6;
	  GPIO_Init(GPIOA,&GPIO_InitStruct);
	  TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
	  TIM_ICInit(TIM3, &TIM_ICInitStruct);
	}
	
	if(TIM_ICCH2==1)
	{
	  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_7;
	  GPIO_Init(GPIOA,&GPIO_InitStruct);
	  TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
	  TIM_ICInit(TIM3, &TIM_ICInitStruct);
	}
	
	 if(TIM_ICCH3==1)
       {
	  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	  GPIO_Init(GPIOB,&GPIO_InitStruct);
	  TIM_ICInitStruct.TIM_Channel = TIM_Channel_3;
	  TIM_ICInit(TIM3, &TIM_ICInitStruct);	
	}
	
	if(TIM_ICCH4==1)
	{
	  GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
	  GPIO_Init(GPIOB,&GPIO_InitStruct);
	  TIM_ICInitStruct.TIM_Channel = TIM_Channel_4;
	  TIM_ICInit(TIM3, &TIM_ICInitStruct);
	}

	TIM_ITConfig(TIM3, TIM_IcChannel,ENABLE);
	TIM_Cmd(TIM3, ENABLE);
}
void TIM3_IRQHandler(void)
{
	u8  j = 0;
	u16 i = 0, *pTimFre, *pTimCcrT, *pTimOver, *pTimSR, *pTimCCR;
	
        pTimSR  = TIM_ICSR;  //缓存TIM3 SR寄存器地址
	
	for(i=1;i<16;i<<=1)  //判断触发的中断类型
	{
		  //例如开启CH3其他通道关闭,其他通道在SR寄存器对应的中断状态标志位会一直为1
		  //所以在这里,除了利用i判断SR寄存器外,还需要通过TIM_IcChannel滤除不需要的中断标志位
		  //详情参考 http://blog.csdn.net/a3748622/article/details/79075892
	    if( ((*pTimSR&i)&TIM_IcChannel) > 0 ) break;			
		  j++;
	}
	
	if(j>0)j--; //将j的取值由 0 1 2 3 4变为 0 0 1 2 3,符合以下结构体地址偏移
	
	pTimCCR  = TIM_ICCCR+2*j; //根据触发通道,缓存TIM3 CCR寄存器地址
	pTimFre  = &TIM_Ic.Ic1_Fre+j;      
	pTimCcrT = &TIM_Ic.Ccr1+j;
	pTimOver = &TIM_Ic.Ic1_Over+j;

	
	if(i==1)   //发生更新中断,处理ARR溢出记录
	{
		  
		  for(j=0;j<4;j++)
		 {
		        *(pTimOver+j)+=1;
			if(*(pTimOver+j)>TIM_OverF) { *(pTimOver+j) = 0; *(pTimCcrT+j) = 0; *(pTimFre+j) = 0; } 
		 }
		 *pTimSR &= ~i;       //清除中断标志位	
	}
	else       //发生CCR捕获中断,计算频率
	{
		         if(*pTimCcrT< 1 )    //第一次触发捕获中断
			{			    
			       *pTimCcrT  = *pTimCCR;		
                           if(*pTimCcrT <1) *pTimCcrT += 1; 
			       *pTimOver = 0;   //溢出记录清零
			}
			else   //第二次触发捕获中断
			{
			       *pTimFre= 72000000/( *pTimCCR - *pTimCcrT +  *pTimOver*60000 );	
                               *pTimCcrT  = 0;						
			}	
			*pTimSR &= ~i;       //清除中断标志位
         }
	
}






实际效果如下图:



FRE3与FRE4为CH3与CH4,连接的是X8以及X8互补输出(TIM1高级定时器),

FRE1连接X11,FRE2连接X17。


发现2K以下频率时,捕获较为准确,偶尔1HZ跳动。但是超过2K之后偶尔10HZ跳动,5K则有几十HZ跳动。

这是因为当前定时器滤波系数为0x0F,改为0,实测10K下跳动几个Hz。

#define  TIM_ICFIT        0x0f               //定时器捕获滤波系数


NOTE:  此程序捕获得出频率较为稳定,但是频率偶尔会出现很大的跳动




猜你喜欢

转载自blog.csdn.net/a3748622/article/details/79083292