stm32学习-输入捕获测量频率

由于没有信号生成器,所以我是自己在引脚PA6生成特定的频率和占空比,再通过PA0读出频率和占空比。(有条件的同学可以拿信号发生器试试)

接线图

频率生成

前面有讲到PWM初始化配置的代码和函数,下面是原文链接,只需要更改一下GPIO口和定时器通道,再加上改变PSC的库函数就可以。

原文链接:https://blog.csdn.net/m0_74246768/article/details/139050032

PWM.c

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;		//PSC
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	TIM_OCInitTypeDef TIM_OCInitStructure;
	TIM_OCStructInit(&TIM_OCInitStructure);
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
	
	TIM_Cmd(TIM2, ENABLE);
}

void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}

void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}

这里补充一下改变PSC值的库函数。

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
作用是单独写预分频值(PSC)/单独改变PSC的值。

我代码中设置的是1KHz频率,大家可以根据下面的公式自行调整参数进行频率设置。

PWM相关公式             

PWM 频率:   Freq = CK_PSC / (PSC + 1) / (ARR + 1)
PWM 占空比:   Duty = CCR / (ARR + 1)
PWM分辨率:  Reso = 1 / (ARR + 1)
根据公式可以看出,想要在不变动占空比的条件下改变频率,需要通过调节PSC调节频率。(如果使用ARR调节频率会改变占空比)
设置ARR=100-1,可使设置的CCR的数值直接等于占空比,用起来比较直观。(需要的同学可以带到上面的公式验算一遍( CK_PSC=72MHz))
计算思路:根据分辨率先算出ARR,PSC决定频率,CCR决定占空比

 输入捕获配置

初始化配置

1.RCC开启时钟(打开GPIO和TIM的时钟打开)

(1)void RCC_APB2PeriphResetCmd(uint32_t RCC_APB2Periph, FunctionalState NewState);
(2)void RCC_APB1PeriphResetCmd(uint32_t RCC_APB1Periph, FunctionalState NewState);
根据外设挂载的总线选择使用的函数。

2.GPIO初始化,将GPIO配置为输入模式(一般选择上拉输入或浮空输入)

void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
作用:配置GPIO初始化

3.配置时基单元(使CNT在内部时钟的驱动下自增运行)

这一部分前面文章有写,不懂的可以参考一下上一篇的内容,此处不多赘述了。
                        
原文链接:https://blog.csdn.net/m0_74246768/article/details/139048136

4.配置输入捕获单元(包括滤波器、极性、分频器等参数)

(1)void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:用结构体配置输入捕获单元的函数

TIMx:选择哪个定时器

TIM_ICInitStruct:包含各个配置的结构体

补充:输入捕获和输出比较都有四个通道,前面文章有写到输出比较的四个通道的库函数,有4个。而输入捕获只有一个函数,是4个通道共用一个函数,在结构体内会额外有个参数去选择配置哪个通道

(2)void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
和上一个函数一样都是初始化输入捕获单元的,上一个函数只能配置一个通道,而这个函数可以快速配置两个通道,把外设结构配置成PWMI模式,这个模式我们下一篇会讲到,这里先不细说。

两个函数二选一就可以。这篇文章我们使用的是TIM_ICInit。

5.选择从模式的触发源(触发源选择为TI1FP1,调用一个库函数后直接给一个参数就行了)

void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
作用:选择输入触发源TRGI。        

6.选择触发之后执行的操作(执行Reset操作,直接调用库函数即可)

void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
作用:选择从模式。

7.调用TIM_Cmd函数定时器。

void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
作用:启动定时器。

配置好后,当我们需要读取最新一个周期的频率时,直接读取CCR寄存器,按照fc/N(测周法)计算一下即可。

获取输入频率

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);
作用:分别读取四个通道的CCR。和下面的TIM_SetCompare函数作用是一样的,都是读取CCR寄存器。输入比较模式下,CCR是只写,用TIM_SetCompare写入;输入捕获模式下,CCR只读,要用TIM_GetCapture读出。

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);
void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);
void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);
void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

IC.c

#include "stm32f10x.h"                  // Device header

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_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
	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;//配置滤波器,数越大,滤波效果越好(每个数对应的采样频率和采样次数在参考手册里有讲)
	//滤波器计次不会改变信号的原有频率
	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//极性选择
	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分频器
	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//选择触发信号从哪个引脚输入
	TIM_ICInit(TIM3, &TIM_ICInitStructure);
	
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
	
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);
//1000000是1MHz,PSC为72-1,根据公式计算可得出72Mhz/72/CCR+1=1MHz/CCR
}

IC.h

#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);

#endif

初始化输入捕获单元

void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
作用:给输入捕获结构体赋初始值。

void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
作用:选择主模式输出触发源TRGO.。

例题:输入捕获测量频率

通过PWM模块将待测信号输出到PA0,PA0通过导线输入到PA6,PA6是TIM3的通道1,通道1通过输入捕获模块测量得到频率,在主循环中不断刷新显示频率,用OLED显示频率。

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"

int main(void)
{
	OLED_Init();
	PWM_Init();
	IC_Init();
	
	OLED_ShowString(1, 1, "Freq:00000Hz");
	
	PWM_SetPrescaler(720 - 1);			//Freq = 72M / (PSC + 1) / 100
	PWM_SetCompare1(50);				//Duty = CCR / 100
	
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);
	}
}

其他重要库函数

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的分频器。(这个参数结构体里面也可以配置,效果是一样的)

测量频率的三种方法

这里补充一下频率测量的三种方法:

测频法:在闸门时间 T 内,对上升沿计次,得到 N ,则频率

^f{_{m}}=N/T

测周法:两个上升沿内,以标准频率 f c 计次,得到 N ,则频率

^f{_{m}}=^f{_{c}}/N

中界频率:测频法与测周法误差相等的频率点

^f{_{m}}=\sqrt{^f{_{c}}/T}

当被测频率小于中界频率时,使用测周法。

当被测频率大于中界频率时,使用测频法 。

小C是这么说的,不过看起来不好理解。我看网课是这么理解的。

测频法就是相当于在1s时间内对上升沿计次(每来一个上升沿,其实就是来了几个周期),所以在1s时间内,来了多少上升沿,就是多少个周期,那它的频率也就是多少Hz        (频率的定义是1s内出现了多少个周期,频率就是多少Hz)如果是2s的闸门时间,就是计次值除2,;0.5s就是计次值乘2.

测周法的基本原理是周期的倒数是频率,我们测出一个周期的时间,然后取个倒数就是频率,方法是捕获信号的两个上升沿,测量一下两个上升沿之间持续的时间(测量时间的方法实际上也是定时器计次)

使用已知的标准频率fc的计次时钟来驱动计数器,从一个上升沿开始计,计数器从0开始一直记到下一个上升沿停止,计一个数的时间是1/fc,计N个数就是N/fc,再取个倒数就是频率。

把测周法和测频法的N提取出来,令两个N相等(也就是令他们误差相等),把fx求出来,就能得到中界频率的公式。

此处如果有不懂可以去B站搜江科大的32课程,讲的非常详细。

  • 22
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值