IC输入捕获,与输出比较一样也有有四个通道,分别与输出比较共用4个CCR寄存器,通过通道输入电平信号,我们可以检测到电平跳变,然后将CNT的值锁存到CCR寄存器中,实现测量频率,占空比,用于电机测速等。
那我们首先来介绍一下测量频率的原理
频率测量的方法有测频法和测周法两种。
- 测频法:根据频率的定义(在1s内出现周期的次数),在闸门时间内,计数器对上升沿记次得到N,如果T取1s,那么记次得到的N就是频率。如果T取0.5s,那么频率就是2N。
- 测周法:就是测量1个周期的时间,然后取倒数。利用单片机内部的标准时钟产生一个标准频率的信号,这个信号会驱动CNT记次,在两个上升沿之间CNT记次为N,那么每记一个数的时间就是1/fc,记N个数的时间就是N/fc。最后取倒数,就是频率。
通过以上介绍也可得知,测频法适用于高频信号,在1s内最好多出现几个上升沿。测周法适用于低频信号,1个周期的时间尽量长一点。这样我们可以使记次N尽量大一些,然后减少正负1误差(在记次时会出现计数器记到一半的情况,这时候N就会随机舍去或者取1,称为正负1误差),当N尽量大时,1对N的影响就会减弱了。
这里还有1点需要注意
- 使用测频法测量频率时,测量的间隔时长是Ts,也就是我们设置的时长,也就是说,每隔Ts才会产生测量结果跳变,如果在Ts内频率有变化,其实测量结果取的是平均值。
- 使用测周法测量频率时,是每隔1个周期的时间就产生一次测量结果跳变,一般我们信号都是几百几千赫兹,所以这个跳变会比较剧烈。
这里有一个中界频率,也就是说,当信号频率处于中界频率时,测频法与测周法的N误差是一样的。这个频率怎么算呢?
我们可以通过测频法的fx公式把N算出来,再把测周法的N算出来,两个相等时。可以得出fx,这个fx就是中界频率。
输入捕获理论框图
通过框图可知,信号TI1通过GPIO引脚输入后,会通过一个滤波器先进行一次滤波,这里的滤波器实际上就是一个事件计数器,只有在连续记录到N个事件后才会产生输出跳变,也就是说,只有在连续采集到N个高电平信号后,才会输出高电平。这样可以去除一些干扰毛刺,这里的滤波参数可以通过CCMR1的ICF位进行设置,一般来说,滤波频率越小,采样点N越大,滤波效果越好。具体配置如下图。
这里介绍一下,FDTS是驱动滤波器的时钟信号,是由内部时钟分频得来的,可以在时基单元里面配置好。
经过滤波后,得到信号TI1F,经过边缘检测器,可以选择是上升沿触发还是下降沿触发。这里通过CCER的CC1P进行配置。
经过边缘检测后,得到信号TI1FP1或者TI1FP2(不过这个信号是走通道2的),之后通过配置CCMR1的CCIS位选择走通道1(通道2的滤波边缘检测信号TI2FP1也可以走通道1),同时TI1FP1还可以触发从模式,实现硬件自动化CNT清零。精准测量每两个上升沿之间的N,便于计算频率。
通过通道1之后,还可以通过ICPS位进行分频选择,如果是测量频率,就选择不分频就好了。
最后通过CC1E位使能一下输出,就可以把CNT的值锁存在CCR里面了。
这里就大概介绍了我们在实现代码时需要配置到哪些部分了。
接下来就介绍一下上文提到的从模式,其实还有一个主模式,它们是一一对应的。
主模式就是定时器将内部信号,映射到TRGO,从而控制一些外设的响应。
从模式就是接受自身或者其他外设的信号,用于触发定时器响应。这里的激励信号由上表可得知,有ITR0……ETRF,只有这些信号可以触发从模式。也就是说,只有TI1FP1与TI2FP2可以触发从模式,实现自动清零功能。通道3与通道4要软件手动清零。我们要实现清零功能,只需要选择从模式的Reset即可。
接下来还有1种模式是PWMI模式,利用这种模式,我们可以实现同时测量频率与占空比的功能。
观察以上框图得知,其实只是变动了右下角的一部分,也就是多了TI1FP2信号的输入,这里TI1FP2会捕获到上升沿与下降沿之间的CNT值,并锁存到CCR2中,但是这个捕获并不会触发计数器清零,所以计数器仍然会继续计数,直到下一个上升沿来临,CNT值锁存到CCR1中,计数器清零。这里CCR2/CCR1就是占空比,实际上就是占用了两个通道,利用两个CCR的值来确定频率与占空比。通道1测频率,通道2测占空比。通道2的TI2FP1与TI2FP2信号同理。
接下来我们就来实现测量频率的功能吧。
这里的信号源我们就选择用单片机的TIM2的输出比较通道1(PA0)来输出一个1KHZ的PWM波形,再用TIM3的输入捕获通道1(PA6)来测量,就用一根杜邦线把他们连接在一起就好了。
PWM的波形输出这里就不再赘述了,和之前的是一样的,改一下ARR与PSC,CCR的值就可以了。
这里我们主要来看输入捕获电路如何配置。
根据这个框图一步一步来。
- 第一步:初始化GPIOA与定时器3的时钟,并配置好相关参数,选择PA0口,模式为上拉输入模式。
- 第二步:选择内部时钟源为定时器3。
- 第三步:配置时基单元参数,时钟驱动(FDTS)选择1分频即可,为了避免计数器溢出问题,ARR设置为65535,PSC设置为72,方便计算测量频率(F=分频后的标准频率/N)。
- 第四步:配置输入捕获单元,滤波器配置为OXF,最大滤波,边缘检测选择上升沿,只测频率的话,模式选择为为直连通道即可。分频选择1分频,也就是不分频。
- 第五步:使用从模式的自动清零功能,也就是配置TRGI的触发源为TI1FP1,选择从模式为Reset(计数器清零)。
- 第六步:使能计数器。计数器使能后,即使没有信号也会自动++,但是也不影响我们测量频率,因为当有上升沿信号来时,计数器会清零重新记次,直到下一次上升沿来临。
- 第七步:写一个计算频率的函数,根据公式,F=标准频率/N。标准频率也就是分频后的频率为1MHZ,N为计数器的值,这个值已经被锁存到CCR里面了,所以我们只需要调用库函数来获取CCR的值即可。也就是1000000/TIM_GetCapturel(TIM3),最后返回这个值,在循环里面不断刷新显示这个值即可。
void IC_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6 ;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode= TIM_CounterMode_Up ;
TIM_TimeBaseInitStruct.TIM_Period=65536-1;
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel=TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter=0xf;
TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising ;
TIM_ICInitStruct.TIM_ICPrescaler=72;
TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;
TIM_ICInit(TIM3, &TIM_ICInitStruct);
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);
TIM_Cmd(TIM3,ENABLE);
}
uint32_t IC_Get(void)
{
return 1000000/TIM_GetCapture1(TIM3);
}
最后就是同时测量频率和占空比的功能了。这里就在以上代码的基础上,在初始化输入捕获单元部分加一句代码即可。我们使用PWMI模式来完成交叉通道。即TIM_PWMIConfig(TIM3,&ICInitStructure),这样我们初始化了通道1,这个函数就会把通道2配置为相反的参数,我们读取到的CCR2值就是高电平部分。这时候只需要读取CCR1(整个周期部分)的值,就可以知道占空比了。我们写一个函数来封装一下这个过程即可。
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_CounterMode= TIM_CounterMode_Up ;
TIM_TimeBaseInitStruct.TIM_Period=65536-1;
TIM_TimeBaseInitStruct.TIM_Prescaler=72-1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter=0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStruct);
uint32_t IC_GetH(void)
{
return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000HZ");
OLED_ShowString(2,1,"Duty:000%");
PWM_SetPrescaler(720-1);
PWM_SetCompare1(50);
while(1)
{
OLED_ShowNum(1,6,IC_Get(),5);
OLED_ShowNum(2,6,IC_GetH(),3);
}
}
最后在主程序中调用一下即可。
标签:TIM3,定时器,入门,信号,STM32,TIM,TimeBaseInitStruct,频率,GPIO From: https://blog.csdn.net/2301_79862041/article/details/142414356图均源自网络