首页 > 其他分享 >STM32入门之定时器输入捕获部分

STM32入门之定时器输入捕获部分

时间:2024-09-25 21:23:51浏览次数:11  
标签:TIM3 定时器 入门 信号 STM32 TIM TimeBaseInitStruct 频率 GPIO

        IC输入捕获,与输出比较一样也有有四个通道,分别与输出比较共用4个CCR寄存器,通过通道输入电平信号,我们可以检测到电平跳变,然后将CNT的值锁存到CCR寄存器中,实现测量频率,占空比,用于电机测速等。

那我们首先来介绍一下测量频率的原理d19c29339cf64a9fb2c46f2ca3757792.jpg

         频率测量的方法有测频法和测周法两种。

  1. 测频法:根据频率的定义(在1s内出现周期的次数),在闸门时间内,计数器对上升沿记次得到N,如果T取1s,那么记次得到的N就是频率。如果T取0.5s,那么频率就是2N。
  2. 测周法:就是测量1个周期的时间,然后取倒数。利用单片机内部的标准时钟产生一个标准频率的信号,这个信号会驱动CNT记次,在两个上升沿之间CNT记次为N,那么每记一个数的时间就是1/fc,记N个数的时间就是N/fc。最后取倒数,就是频率。848a1d70e05b43b2b805d74985e0a933.jpg

         通过以上介绍也可得知,测频法适用于高频信号,在1s内最好多出现几个上升沿。测周法适用于低频信号,1个周期的时间尽量长一点。这样我们可以使记次N尽量大一些,然后减少正负1误差(在记次时会出现计数器记到一半的情况,这时候N就会随机舍去或者取1,称为正负1误差),当N尽量大时,1对N的影响就会减弱了。

        这里还有1点需要注意

  1. 使用测频法测量频率时,测量的间隔时长是Ts,也就是我们设置的时长,也就是说,每隔Ts才会产生测量结果跳变,如果在Ts内频率有变化,其实测量结果取的是平均值。
  2. 使用测周法测量频率时,是每隔1个周期的时间就产生一次测量结果跳变,一般我们信号都是几百几千赫兹,所以这个跳变会比较剧烈。

        这里有一个中界频率,也就是说,当信号频率处于中界频率时,测频法与测周法的N误差是一样的。这个频率怎么算呢?

        我们可以通过测频法的fx公式把N算出来,再把测周法的N算出来,两个相等时。可以得出fx,这个fx就是中界频率。

输入捕获理论框图

d896b43a0de5412f9c95ff62de799e6c.jpg

        通过框图可知,信号TI1通过GPIO引脚输入后,会通过一个滤波器先进行一次滤波,这里的滤波器实际上就是一个事件计数器,只有在连续记录到N个事件后才会产生输出跳变,也就是说,只有在连续采集到N个高电平信号后,才会输出高电平。这样可以去除一些干扰毛刺,这里的滤波参数可以通过CCMR1的ICF位进行设置,一般来说,滤波频率越小,采样点N越大,滤波效果越好。具体配置如下图。70706b4af09f40608e2714dc6dc1bd35.jpg

         这里介绍一下,FDTS是驱动滤波器的时钟信号,是由内部时钟分频得来的,可以在时基单元里面配置好。

        经过滤波后,得到信号TI1F,经过边缘检测器,可以选择是上升沿触发还是下降沿触发。这里通过CCER的CC1P进行配置。

        经过边缘检测后,得到信号TI1FP1或者TI1FP2(不过这个信号是走通道2的),之后通过配置CCMR1的CCIS位选择走通道1(通道2的滤波边缘检测信号TI2FP1也可以走通道1),同时TI1FP1还可以触发从模式,实现硬件自动化CNT清零。精准测量每两个上升沿之间的N,便于计算频率。

        通过通道1之后,还可以通过ICPS位进行分频选择,如果是测量频率,就选择不分频就好了。

        最后通过CC1E位使能一下输出,就可以把CNT的值锁存在CCR里面了。

        这里就大概介绍了我们在实现代码时需要配置到哪些部分了。b4b0b1cd2d684ba3bfeb817afa9cab78.jpg

         接下来就介绍一下上文提到的从模式,其实还有一个主模式,它们是一一对应的。885dfa814aea4d64b7fe53d96e139582.jpg

         主模式就是定时器将内部信号,映射到TRGO,从而控制一些外设的响应。

        从模式就是接受自身或者其他外设的信号,用于触发定时器响应。这里的激励信号由上表可得知,有ITR0……ETRF,只有这些信号可以触发从模式。也就是说,只有TI1FP1与TI2FP2可以触发从模式,实现自动清零功能。通道3与通道4要软件手动清零。我们要实现清零功能,只需要选择从模式的Reset即可。


        接下来还有1种模式是PWMI模式,利用这种模式,我们可以实现同时测量频率与占空比的功能。2c6493bd4b2b48e29e2cb07d256560ef.jpg

         观察以上框图得知,其实只是变动了右下角的一部分,也就是多了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的值就可以了。

这里我们主要来看输入捕获电路如何配置。422bf2c57e2b49c5b3e0da0b53a6d251.jpg

         根据这个框图一步一步来。

  1. 第一步:初始化GPIOA与定时器3的时钟,并配置好相关参数,选择PA0口,模式为上拉输入模式。
  2. 第二步:选择内部时钟源为定时器3。
  3. 第三步:配置时基单元参数,时钟驱动(FDTS)选择1分频即可,为了避免计数器溢出问题,ARR设置为65535,PSC设置为72,方便计算测量频率(F=分频后的标准频率/N)。
  4. 第四步:配置输入捕获单元,滤波器配置为OXF,最大滤波,边缘检测选择上升沿,只测频率的话,模式选择为为直连通道即可。分频选择1分频,也就是不分频。
  5. 第五步:使用从模式的自动清零功能,也就是配置TRGI的触发源为TI1FP1,选择从模式为Reset(计数器清零)。
  6. 第六步:使能计数器。计数器使能后,即使没有信号也会自动++,但是也不影响我们测量频率,因为当有上升沿信号来时,计数器会清零重新记次,直到下一次上升沿来临。
  7. 第七步:写一个计算频率的函数,根据公式,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

相关文章

  • FPGA Verilog基本语句(语法)FPGA入门
    本篇文章主要写了在Verilog环境下,FPGA基本语法和数据类型。可以通过导航键快速进入assign语句、always语句等其他内容!对于Verilog(FPGA):module     ...                   ==》构成主体endmodulemodule模块名(【端口......
  • #define#include 定义 预处理入门详解(C语言)
    本篇博客会解答如下问题:1.#include<>与""的区别2.头文件中ifdef/ifndef/define/endif分别的作用是什么3.defined定义需要加';'吗4.#pragmeonce是干什么用的5.define定义常量6.define定义宏本篇博客共为2800余字,问题都在博客当中做得回答,目录有部分问题快捷键......
  • SqlEs快速入门
    可以参考demo:https://github.com/czcuestc/sqles/tree/master/sqles-demo1,使用SqlEs很简单,首先创建实体类,通过注解配置索引,示例如下:@Document@Setting(maxResultWindow=1000)publicclassTestEntityimplementsSerializable{privatestaticfinallongserialVers......
  • 基于 STM32 和 Modbus 协议的公路隧道照明环境数据采集系统设计
    一、项目概述在现代高速公路的隧道照明系统中,实时监控环境参数(如照度、温度和湿度)是确保安全与高效运营的重要环节。本项目旨在设计一个多通道数据采集器,能够实时采集隧道内的照明及环境数据,并通过工业标准的Modbus协议将数据传输到上位机进行监控和分析。技术栈关键词......
  • Go从入门到放弃之map(字典)
    字典声明和初始化Go语言中提供的映射关系容器为map,map也是个无序集合,底层不会按照元素添加顺序维护元素的存储顺序Go语言中 map的定义语法如下:KeyType:表示键的类型。ValueType:表示键对应的值的类型。map[KeyType]ValueTypemap类型的变量默认初始值为nil,需要使用make()函数来......
  • Go从入门到放弃之数组、切片
    一、数组数组的声明和初始化在Go语言中,数组是固定长度的、同一类型的数据集合。数组中包含的每个数据项被称为数组元素,一个数组包含的元素个数被称为数组的长度。在Go语言中,你可以通过 [] 来标识数组类型,但需要指定长度和元素类型,使用时可以修改数组成员,但是数组大小不可变化......
  • Go从入门到放弃之数据类型
    数据类型概述Go支持的数据类型基本数据类型布尔类型:bool整型:int8、byte、int16、int、uint、uintptr等浮点类型:float32、float64复数类型:complex64、complex128字符串:string字符类型:rune错误类型:error复合类型指针(pointer)数组(arr......
  • Go从入门到放弃之指针
    要搞明白Go语言中的指针需要先知道3个概念:指针地址、指针类型和指针取值指针地址和指针类型每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。Go语言中使用&字符放在变量前面对变量进行“取地址”操作。Go语言中的值类型(int、float、bool、string、array、struct)都......
  • 入门网络安全工程师要学习哪些内容
       大家都知道网络安全行业很火,这个行业因为国家政策趋势正在大力发展,大有可为!但很多人对网络安全工程师还是不了解,不知道网络安全工程师需要学什么?知了堂小编总结出以下要点。网络安全工程师是一个概称,学习的东西很多,具体学什么看自己以后的职业定位。如果你以后想成为......
  • 入门网络安全工程师要学习哪些内容
       大家都知道网络安全行业很火,这个行业因为国家政策趋势正在大力发展,大有可为!但很多人对网络安全工程师还是不了解,不知道网络安全工程师需要学什么?知了堂小编总结出以下要点。网络安全工程师是一个概称,学习的东西很多,具体学什么看自己以后的职业定位。如果你以后想成为......