由于项目需要利用一个定时器捕获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: 此程序捕获得出频率较为稳定,但是频率偶尔会出现很大的跳动