第一步,RCC开启时钟,把GPIO和TIM的时钟打开
第二步,GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入或者浮空输入模式
第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
第四步,配置输入捕获单元.包括滤波器、极性、直连通道还是交叉通道、分频器这些参数
第五步,选择从模式的触发源,触发源选择为TI1FP1,这里调用一个库函数,给一个参数就行了
第六步,选择触发之后执行的操作,执行Reset操作,这里也是调用一个库函数就行了
最后,当这些电路都配置好之后.调用TIM_Cmd函数,开启定时器
当我们需要读取最新一个周期的频率时,直接读取CCR寄存器,然后按照fe/N,计算一下就行了
相关配置函数
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
第二个参数就是包含各个配置的结构体
输入捕获和输出比较都有4个通道,输入捕获OCInit,4个通道,每个通道单独占一个函数。
而ICInit,4个通道是共用一个函数的。在结构体里会额外有一个参数,可以用来选择具体是配置哪个通道。
因为可能有交又通道的配置,所以函数合在一起比较方便。
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
用于初始化输入捕获单元的。
与TIM_ICInit不同的是,TIM_ICInit只是单一的配置一个通道,而这个函数,可以快速配置两个通道,把外设电路结构配置成我们PPT这里展示的PWMI模式
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
可以给输入捕获结构体赋一个初始值
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择输入触发源TRGI,对应从模式的触发源选择,调用这个函数,就能选择从模式的触发源了
比如本次要用的TI1FP1
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
选择输出触发源TRGO,对应选择主模式输出的触发源
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
选择从模式。
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
分别单独配置通道1、2、3、4的分频器,这个参数结构体里也可以配置,是一样的效果
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
分别读取4介通道的CCR,和SetCompare1、2、3、4是对应的,读写的都是CCR寄存器
输出比较模式下,CCR是只写的,要用SetCompare写入,
输入捕获模式下,CCR是只读的,要用GetCapture读出
输入捕获测量频率
void IC_Init(void)
{
//第一步,RCC开启时钟,把GPIO和TIM的时钟打开
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//查引脚定义表,TIM3的通道1和通道2对应PA6和PA7;通道3和通道4,对应PBO和PB1
//本次代码计划用TIM3的通道1引脚,所以引脚就是PA6;
//第二步,GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入或者浮空输入模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //GPIO_Pin_15;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
TIM_InternalClockConfig(TIM3);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSC //这是标准频率,由72MHz/预分频器(PSC)直接生成,不用输出,不需要ARR的处理
//需要根据你信号频率的分布范围来调整,暂时先给72-1,这样标准频率就是72M/72=1MHz。
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
//第四步,配置输入捕获单元.包括滤波器、极性、直连通道还是交又通道、分频器这些参数,初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitstructure;
TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;//选择通道
TIM_ICInitstructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,滤波器计次,并不会改变信号的原有频率, 般滤波器的采样频率都会远高于信号频率,所有它只会滤除高频噪声,使信号更平滑,1KHz滤波之后仍然是1KHz”信号频率不会变化,
//分频器就是对信号本身进行计次了,会改变频率。1KHz 2分频之后就是500Hz,4分频就是250Hz
TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性,对应的就是图里的这个边沿检测、极性选择的部分了,选择是上升沿触发还是下降沿触发。
TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//对应 触发信号分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推。
TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择触发信号从哪个引脚输入,配置数据选择器的
TIM_ICInit(TIM3,&TIM_ICInitstructure);
//第五步配置TRGI的触发源为TI1FP1
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
//第六步,配置从模式为Reset
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
TIM_Cmd(TIM3,ENABLE);
}
当我们启动定时器之后,CNT就会在内部时钟的驱动下不断自增,即使没有信号过来它也会不断自增,不过这也没关系,
因为有信号来的时候,它就会在从模式的作用下自动清零,并不会影响测量。
计算频率
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3)+1); //xxhz
}
PWMI模式测频率占空比
大部分与上文代码差不多,两种配置方式。
手动配置通道二测量占空比
//第四步,配置输入捕获单元.包括滤波器、极性、直连通道还是交又通道、分频器这些参数,初始化输入捕获单元
TIM_ICInitTypeDef TIM_ICInitstructure;
TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;//选择通道
TIM_ICInitstructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,滤波器计次,并不会改变信号的原有频率, 般滤波器的采样频率都会远高于信号频率,所有它只会滤除高频噪声,使信号更平滑,1KHz滤波之后仍然是1KHz”信号频率不会变化,
//分频器就是对信号本身进行计次了,会改变频率。1KHz 2分频之后就是500Hz,4分频就是250Hz
TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性,对应的就是图里的这个边沿检测、极性选择的部分了,选择是上升沿触发还是下降沿触发。
TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//对应 触发信号分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推。
TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择触发信号从哪个引脚输入,配置数据选择器的
TIM_ICInit(TIM3,&TIM_ICInitstructure);
//通道二占空比测量
TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitstructure.TIM_ICFilter = 0xF;
TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;
TIM_ICInit(TIM3,&TIM_ICInitstructure);
使用官方提供的函数实现
TIM_ICInitTypeDef TIM_ICInitstructure;
TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;//选择通道
TIM_ICInitstructure.TIM_ICFilter = 0xF;//配置输入捕获的滤波器,滤波器计次,并不会改变信号的原有频率, 般滤波器的采样频率都会远高于信号频率,所有它只会滤除高频噪声,使信号更平滑,1KHz滤波之后仍然是1KHz”信号频率不会变化,
//分频器就是对信号本身进行计次了,会改变频率。1KHz 2分频之后就是500Hz,4分频就是250Hz
TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性,对应的就是图里的这个边沿检测、极性选择的部分了,选择是上升沿触发还是下降沿触发。
TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//对应 触发信号分频器,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推。
TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择触发信号从哪个引脚输入,配置数据选择器的
TIM_ICInit(TIM3,&TIM_ICInitstructure);
TIM_PWMIConfig(TIM3,&TIM_ICInitstructure);//通道二 交叉 下降沿
计算频率和占空比
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3)+1); //xxhz
}
uint32_t IC_GetDuty(void)
{
return (TIM_GetCapture2(TIM3)*100+1) / (TIM_GetCapture1(TIM3)+1); //乘100后,返回值的范围为0~100对应占空比0%~100%
}
通道二下降沿触发,一个周期内从开始到高电平结束的时间,通道一计两个相邻上升沿之间的时间,即一整个周期的时间。两个相除即占空比。
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
uint8_t i;
int main(void)
{
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:000000000Hz");
OLED_ShowString(2,1,"Duty:000%");
PWM_SetPrescaler(720-1); // Freg = 72M / (PSC + 1) / (ARR + 1) = 72M/(PSC+1)/100 =1000hz
PWM_SetCompare1(80); // Duty = CCR / 100 = 50%
while (1)
{
OLED_ShowNum(1,6,IC_GetFreq(),9);
OLED_ShowNum(2,6,IC_GetDuty(),3);
}
}
误差分析
我们来研究一下这个测频率的性能,首先是测频率的范围,目前我们给的标准频率是1Mhz计数器最大只能计到65535,所以所测量的最低频率是1MHz/6535,这个值算一下,大概是15Hz,如果信号频率再低,就是就要溢出了,所以最低频率就是15Hz左右,那如果想再降低一些最低频率的限制呢? 可以把这个预分频再加大点,这样标准频率就更低,所支持测量的最低频率也就更低。这是测量频率的下限,然后是测量频率的上限,就是支持的最大频率。这个最大频率并没有一个明显的界限,因为随着待测频率的增大,误差也会逐渐增大。如果非要找一个频率上限,那应该就是标准频率1MHz,超过1MHz,信号频率比标准频率还高,那肯定测不了的,但是这个1MHz的上限并没有意义,因为信号频率接近1MHz时误差已经非常大了,所以最大频率要看你对误差的要求。 ±1误差记100个数,误差1个,相对误差就是1%。 计1000个数,误差1个,相对误差就是1‰。 所以±1误差可以认为是1/计数值。在这里,如果你要求误差等于1‰时,频率为上限,那这个上限就是1M除1000=1000Hz,如果要求误差,可以到1%,那频率上限就是1M除100=10KHz,这就是频率的上限。 如果想提高频率的上限,那我们在这里就要把PSC给降低一些,提高标准频率上限就会提高。 除此之外,如果频率还要更高呢,那我们就要考虑一下测频法了。 测频法适合高频,测周法适合低频,这里是测周法,所以对于非常高的频率还是交给测频法来解决。 除了±1误差外,在实际测量的时候还会有晶振误差。比如stm32的晶振不是那么准,在这次几百几万次之后,误差积累起来也会造成一些影响。 当然,目前我们这个现象是自己测量自己不存在经证误差,所以数值还是非常稳定的。如果你要测量别的信号,那数值可能就会有些抖动了,后期可以再做一些滤波处理。
点击数:12