输入捕获模式测频率与PWMI模式测占空比

 TIM输入捕获_tz得像个小孩的博客-CSDN博客

后续


目录

一、输入捕获的步骤

二、函数介绍

三、程序

1、PWM.c(输入捕获测频率)

2、IC.c

3、main.c

 4、IC.c(PWMI模式检测占空比和频率)

5、mian.c


一、输入捕获的步骤

第一步,RCC开启时钟,把GPIO和TIM的时钟打开

第二步,GPIO初始化,把GPIO配置成输入模式(上拉模式或者浮空输入模式)

第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行

第四步,配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器这些参数

第五步,选择从模式的触发源,选择TI1FP1,这里调用一个库函数,给一个参数就行了

第六步,选择触发之后执行的操作,执行Reset操作,这里也是调用一个库函数

最后,使用Tim_Cmd函数,开启时钟

当我们需要读取周期的频率时,直接读取CCR寄存器,然后按照fc/N,计算一下就行了


PWM频率=更新频率=72M/(PSC+1)/(ARR+1)

占空比=CRR/(ARR+1)

调节ARR会影响到占空比,调节PSC,不会影响占空比,比较方便

固定ARR为100-1,调节PSC来改变PWM频率,CCR的数值直接就是占空比

二、函数介绍

1、TIM_ICInit,使用结构体配置输入捕获单元的函数,OC_Init有四个通道,而IC_Init是通用一个函数,结构体里额外有一个参数,可以用来选择具体配置哪个通道。

2、TIM_PWMIConfig,用于初始化输入捕获单元的,第1个函数是单一的配置一个通道,此函数可以快速配置两个通道,可以把外设电路结构配置成PWMI模式

3、TIM_ICStructInit,给输入捕获结构体赋一个初始值

4、TIM_SelectInputTrigger,选择输入触发源TRGI

5、TIM_SelectOutputTrigger 选择输出触发源TRGO

6、TIM_SelectSlaveMode 选择从模式

7、TIM_SetIC(1234)Prescaler,分别单独配置通道1、2、3、4的分频器

8、TIM_GetCapture(1234),分别读取4个通道的CCR,对应TIM_SetCompare(1234),都是读写CCR寄存器,输出比较模式下,CCR是只写的,要用SetCompare写入,输入捕获模式下,CCR是只读的,要用GetCapture读出。

三、程序

1、PWM.c(输入捕获测频率)

#include "stm32f10x.h"                  // Device header

void PWM_Init(void)
{
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);//注意是开启APB1的时钟函数,因为TIM2是APB1总线的外设
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//选择时基单元的时钟
	
	  GPIO_InitTypeDef GPIO_InitStructure;
	  GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;//复用推挽输出
	  GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
	  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_OCNPolarity = TIM_OCPolarity_High ;//设置输出比较的极性
		TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//设置输出使能
		TIM_OCInitStructure.TIM_Pulse = 0;//设置CCR
		TIM_OC1Init(TIM2, &TIM_OCInitStructure);	
		//周期ARR;预分频PSC;CCR这三个值,共同决定输出PWM的周期和占空比
			
		TIM_Cmd(TIM2,ENABLE);//启动定时器

}

void PWM_Setcompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);
}//通过Setcompare1改变哦那个到1的占空比

void PWM_SetPrescaler(uint16_t Prescaler)
{
		TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}//通过SetPresaler改变频率

指定时器预分频器的重装模式

  1. TIM_PSCReloadMode_Update,预分频器在更新事件重装
  2. TIM_PSCRcloadMode_Immediate,预分频器立刻重装

意思就是写入的值,是立刻生效,还是更新事件生效

立刻生效,可能会再值改变时产生切断波形的现象,比如PWM一个周期刚过去一半,立刻生效了,立刻切断当前波形,开始新的一个周期。

更新事件生效,就是会有一个缓存器,延迟参数的写入时间,等一个周期结束了,在更新事件时,再统一改变参数,保证每个周期的完整。

2、IC.c

#include "stm32f10x.h"                  // Device header


void IC_Init(void)
{

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,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(GPIOB,&GPIO_InitStructure);
	
		TIM_InternalClockConfig(TIM4);
		
		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 预分频器的值,决定了测周法的标准频率fc,这里等于1MHz
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,是高级计数器才有的
	    TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
	
		TIM_ICInitTypeDef TIM_ICInitStructure;
		TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//ICInit函数只有一个,需要靠此参数指定配置哪个通道
		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(TIM4,&TIM_ICInitStructure);
	
		TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);//触发源选择,
		TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);//选择从模式
		
		TIM_Cmd(TIM4,ENABLE);
}

uint32_t IC_GetFreq(void)
{
			return 1000000 /( TIM_GetCapture1(TIM4) +1);
            //return 1000000 /( TIM_GetCapture1(TIM4) +1);
            //如果GetCapture1不加1,则显示有差错
}

程序种的IC初始化中的TIM_ICFilter,是滤波器。如果信号有噪声和毛刺,可以增大滤波器参数,可以有效避免干扰。滤波器和预分频器的区别:滤波器计次不会改变信号的原有频率,一半滤波器的采样频率都会远高于信号频率,只会滤出高频噪声,使信号更平滑,1kHz滤波后仍然使1kHz,信号频率不会变化;分频器就是队信号本身进行计次,会改变频率。

GetCapture1中的+1的原因:

如果不加1,OLED显示屏中显示的频率不是1000Hz,而是1001Hz。

启动定时器后,CNT会在内部时钟的驱动下不断自增,即使没有信号,也会不断自增,信号来的时候,从模式作用下自动清零。

想要查看频率时,需要读取CCR,进行计算

3、main.c

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

//uint8_t i;//定义一个16位得全局变量Num

int main(void)
{
    OLED_Init(); 
	  PWM_Init();
		IC_Init();
		
	OLED_ShowString(1, 1, "Freq:00000Hz");
	
	  PWM_SetPrescaler(720 - 1);						//频率Freq=72M/(PSC+1)/(ARR+1)
		PWM_Setcompare1(50);				//占空比Duty=CRCR/(ARR+1)
		
		
	while(1)
	{
			OLED_ShowNum(1, 6, IC_GetFreq(), 5);
	}
}


 

图3-1 程序显示效果图

 4、IC.c(PWMI模式检测占空比和频率)

#include "stm32f10x.h"                  // Device header


void IC_Init(void)
{

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,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(GPIOB,&GPIO_InitStructure);
	
		TIM_InternalClockConfig(TIM4);
		
		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 预分频器的值,决定了测周法的标准频率fc,这里等于1MHz
		TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,是高级计数器才有的
	  TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
	
		TIM_ICInitTypeDef TIM_ICInitStructure;		
		TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//ICInit函数只有一个,需要靠此参数指定配置哪个通道
		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_PWMIConfig(TIM4,&TIM_ICInitStructure);

//		TIM_ICInit(TIM4,&TIM_ICInitStructure);
//		TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;//ICInit函数只有一个,需要靠此参数指定配置哪个通道
//		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(TIM4,&TIM_ICInitStructure);
	
		TIM_SelectInputTrigger(TIM4,TIM_TS_TI1FP1);//触发源选择,
		TIM_SelectSlaveMode(TIM4,TIM_SlaveMode_Reset);//选择从模式
		
		TIM_Cmd(TIM4,ENABLE);
}

uint32_t IC_GetFreq(void)
{
			return 1000000 /( TIM_GetCapture1(TIM4) + 1);
}

uint32_t IC_GetDuty(void)
{
			return (TIM_GetCapture2(TIM4) + 1 ) * 100 / ( TIM_GetCapture1(TIM4) + 1);
}

PWMI模式在上一章节有介绍过,可以翻一下上面那个链接

PWMI模式需要使用两个通道,所以我们要配置两个IC初始化,在代码中,我也写出来,并注释掉了。极性的选择则需要选择跟上一个通道相反的极性,通道选择2通道。

TIM_PWMIConfig,只需要传输一个通道的参数,在函数里,会自动把剩下的一个通道初始化成相反的配置,比如传入通道1,直连,上升沿;函数就会顺带配置通道2,交叉,下降沿。可以快捷地把电路配置成PWMI模式的标准结构。这个函数只支持通道1和通道2。

使用了 TIM_PWMIConfig,这样就可以减少我们代码上的负担,但是注意只能支持1、2通道。

5、mian.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");
	OLED_ShowString(2, 1, "Duty:00%");
	
	  	PWM_SetPrescaler(720 - 1);				//频率Freq=72M/(PSC+1)/(ARR+1)
			PWM_SetCompare1(80);		        	//占空比Duty=CRCR/(ARR+1)
		
	while(1)
	{
			OLED_ShowNum(1, 6, IC_GetFreq(), 5);
			OLED_ShowNum(2, 6, IC_GetDuty(), 2);
	}
}

 3-2 程序2效果图

测频率的性能:

  1. 范围:目前我们给的标准频率是1MHz,计数器最大只能计到65535,所以所测量的最低频率是1M/65535,这个值算一下大概是15Hz。如果信号频率再低,计数器就要溢出了。所以最低的频率就是15Hz。如果想再降低频率,可以将预分频再加大一点,这样标准频率就更低,所支持测量的最低频率也就更低,这是测量频率的下限。    最大频率,没有最大的界限,随着待测频率的增大,误差也会逐渐增大。最大频率则是看我们对误差的要求。
  2. 正负1误差可认为1/计数值

这个程序使用的是测周法,测量的是较大的频率,则想测量小频率就使用测频法。

  • 3
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tz得像个小孩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值