输入捕获和PWMI测量频率和占空比
本文最后更新于27天前,其中的信息可能已经有所发展或是发生改变。

第一步,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的晶振不是那么准,在这次几百几万次之后,误差积累起来也会造成一些影响。 当然,目前我们这个现象是自己测量自己不存在经证误差,所以数值还是非常稳定的。如果你要测量别的信号,那数值可能就会有些抖动了,后期可以再做一些滤波处理。

点击数:1

    暂无评论

    发送评论 编辑评论

    
    				
    |´・ω・)ノ
    ヾ(≧∇≦*)ゝ
    (☆ω☆)
    (╯‵□′)╯︵┴─┴
     ̄﹃ ̄
    (/ω\)
    ∠( ᐛ 」∠)_
    (๑•̀ㅁ•́ฅ)
    →_→
    ୧(๑•̀⌄•́๑)૭
    ٩(ˊᗜˋ*)و
    (ノ°ο°)ノ
    (´இ皿இ`)
    ⌇●﹏●⌇
    (ฅ´ω`ฅ)
    (╯°A°)╯︵○○○
    φ( ̄∇ ̄o)
    ヾ(´・ ・`。)ノ"
    ( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
    (ó﹏ò。)
    Σ(っ °Д °;)っ
    ( ,,´・ω・)ノ"(´っω・`。)
    ╮(╯▽╰)╭
    o(*////▽////*)q
    >﹏<
    ( ๑´•ω•) "(ㆆᴗㆆ)
    😂
    😀
    😅
    😊
    🙂
    🙃
    😌
    😍
    😘
    😜
    😝
    😏
    😒
    🙄
    😳
    😡
    😔
    😫
    😱
    😭
    💩
    👻
    🙌
    🖕
    👍
    👫
    👬
    👭
    🌚
    🌝
    🙈
    💊
    😶
    🙏
    🍦
    🍉
    😣
    Source: github.com/k4yt3x/flowerhd
    颜文字
    Emoji
    小恐龙
    花!
    上一篇
    下一篇