当前位置: 首页 > news >正文

GD32_定时器输入捕获波形频率

GD32_定时器输入捕获波形频率(多通道轮询)

之前项目上用到一个使用定时器捕获输入采集风扇波形频率得到风扇转速的模块,作为笔记简单记录以下当时的逻辑结构和遇到的问题,有需要参考源码、有疑问或需要提供帮助的可以留言告知 。


前言

提示: 测试基于GD32F103CBT6硬件平台,标准的108MHz系统时钟, 使用标准库GD32F10x_Firmware_Library_V1.0.0提示:(提示:此库坑多,外设编号从1开始,与用户手册略有出入、慎用!)


一、定时器输入捕获原理

定时器输入捕获模式可以用来测量脉冲宽度或者测量频率,我们以测量频率为例,用一个简图来说明输入捕获的原理:
在这里插入图片描述

如图示,斜线表示向上计数的定时器的计数值,ARR表示定时器的自动重装载值,定时计数器由0递加到这里就会发生溢出,并重0重新开始计数。将输入捕获配置为上升沿捕获,当检测到一个波形的上升沿时候,触发第一次捕获中断,T1时刻会采集计数器当前CNT值并保存记为CCRx1,当再次出现上升沿时触发第二次捕获,T2时刻会再次采集计数器当前CNT值并保存记为CCRx2,理想状态波形周期就是T2 -T1。但是如果波形较长可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。 T1~T2之间CNT计数的次数等于: (ARR - CCRx1)+( N * ARR)+ CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 T1 -T2的时间长度,即波形周期以及频率。

二、外设配置

GD32的定时器,我们使用通用定时器Timer1的,使用的GPIO为PB10和PB11,根据用户手册,需要进行功能重映射才能使其分别对应Timer1的通道2和通道3。在定时器初始化中使用函数GPIO_PinRemapConfig()即可;
在这里插入图片描述

使用的主时钟频率为108MHz,定时器的重装载值寄存器为16位,最大为65535,当定时器时钟分频系数为108分频时候,相当于定时器每65.535ms会溢出一次,如下是整个定时器PWM输入捕获的配置方式,目的是分别对通道3和通道4上的两个风扇进行脉冲周期数据采集和计算,详细内容参见代码注释:

void FanPwm_Input_Init(void)
{TIMER_BaseInitPara  sTIM_TimeBaseStructure;NVIC_InitPara   NVIC_InitStructure;TIMER_ICInitPara sTIM_ICInitCaptureStructure;/*初始化Timer2输入捕获*/RCC_APB1PeriphClock_Enable(RCC_APB1PERIPH_TIMER2, ENABLE);      //实际是复位Timer1(手册与固件库有误)TIMER_DeInit(TIMER2);                                           //复位GPIO_PinRemapConfig(GPIO_FULL_REMAP_TIMER2,ENABLE);             //PB10和PB11是全映射复用sTIM_TimeBaseStructure.TIMER_Period 			= 65535;                 //计数器自动重装值sTIM_TimeBaseStructure.TIMER_Prescaler 			= 107;                   //计数器时钟预分频值,计数器时钟等于 PSC 时钟除以 (PSC+1)sTIM_TimeBaseStructure.TIMER_ClockDivision 		= TIMER_CDIV_DIV1;   	 //设置时钟分割:fDTS = fTIMER_CKsTIM_TimeBaseStructure.TIMER_CounterMode 		= TIMER_COUNTER_UP;      //TIM向上计数模式   TIMER_BaseInit(TIMER2, &sTIM_TimeBaseStructure);                    	 //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位sTIM_ICInitCaptureStructure.TIMER_ICSelection 	= TIMER_IC_SELECTION_DIRECTTI;  //通道x配置为输入, ISx 映射在 CIxFEx 上sTIM_ICInitCaptureStructure.TIMER_ICPrescaler 	= TIMER_IC_PSC_DIV1;            //时钟分频sTIM_ICInitCaptureStructure.TIMER_ICPolarity 	= TIMER_IC_POLARITY_RISING;     //上升沿捕获sTIM_ICInitCaptureStructure.TIMER_ICFilter 		= 0x00;                         //不滤波sTIM_ICInitCaptureStructure.TIMER_CH 			= TIMER_CH_3;                   //PB10对应Timer2的通道3(手册是通道2,标准库有错位)TIMER_ICInit(TIMER2,&sTIM_ICInitCaptureStructure);                            sTIM_ICInitCaptureStructure.TIMER_CH = TIMER_CH_4;                              //PB11对应Timer2的通道4(手册是通道3,标准库有错位)TIMER_ICInit(TIMER2,&sTIM_ICInitCaptureStructure);                            NVIC_InitStructure.NVIC_IRQ 					= TIMER2_IRQn;          //TIM2中断NVIC_InitStructure.NVIC_IRQPreemptPriority 		= 0;     				//Q抢占优先级优先级0级NVIC_InitStructure.NVIC_IRQSubPriority			= 5;        			//副优先级2级NVIC_InitStructure.NVIC_IRQEnable 				= ENABLE;         		//IRQ通道被使能NVIC_Init(&NVIC_InitStructure);   TIMER_INTConfig(TIMER2,TIMER_INT_UPDATE,ENABLE);   						//使能定时器溢出中断,暂不使能通道捕获中断TIMER_Enable(TIMER2, ENABLE);                       					//使能定时器外设,暂不使能中断
}

三、逻辑结构

配置完成后,会分别轮询打开两个通道的输入捕获中断,当脉冲的第一个上升沿触发对应通道的输入捕获中断时,会捕获第一个定时器计数值并将变量WaveEgde 置为1 ,说明此时已经采集到第一个上升沿。当WaveEgde = 1的情况下,当再次触发中断时候会判断是定时器溢出中断还是上升沿触发的输入捕获中断,如果是定时器溢出,说明定时器重新计数了,会将录有效溢出次数+1;如果发生上升沿捕获中断,则说明第二个上升沿到来,会捕获第一个定时器计数值且已采集到一个完整波形周期。数据采集结束,关闭该通道的输入捕获中断,并将捕获完成标记位置位,变量WaveEgde 置为0(无采集状态)。然后根据是否有有效溢出次数,选择计算方式计算两次捕获总的时间差。整个采集流程如图所示:
在这里插入图片描述
以上是一个通道的采集流程,使用多个通道采集时采集流程基本原理一致,但是如果同时打开多个通道的捕获中断,会导致某些通道的数据差异非常大,这个差异随着增加的通道数量越多而变得明显。因为如果通道数量太多,会在集中一段时间内频繁进中断导致数据采集不准确,所以我们使用轮询方式打开通道的捕获中断,轮询间隔时间设置为200ms,这段逻辑代码如下所示:

定义了捕获数据结构体,所有的数据都保存在此结构体中

typedef struct
{uint8_t WaveEgde;                   //Eegd = 0,表示当前处于低电平,Egde = 1,表示当前处于高电平uint8_t ucFinishFlag;               //捕获结束标记位uint16_t ucCaptureRisingVal[2];     //输入捕获值,[0]:第1次触发捕获值,[1]:第2次触发捕获值uint32_t ucUpdateCnt;               //记录溢出次数uint32_t ulFanSpeed;                //风扇转速uint32_t ulFrequency;               //输入波形频率
}Capture_DateType;Capture_DateType WaveCap[FAN_Count_Num];            //定义两个风扇的捕获数据结构体

这里只给出了Timer2的中断回调函数中的逻辑结构

/**********************************************************************1-函数名:Fan_PwmI_IRQFunction*2-函数功能:Timer2的中断回调函数*3-输入参数:无*4-返回值:无*5-输入全局变量:无*6-输出全局变量:无*7-创建者与创建日期: Awen_ 2023-9-24**********************************************************************/
void Fan_PwmI_IRQFunction(void)
{if(TIMER_GetIntBitState(TIMER2,TIMER_INT_UPDATE) != RESET){	/*定时器溢出中断*/TIMER_ClearIntBitState(TIMER2,TIMER_INT_UPDATE);PwmInput_Timer_Update_Handler();}if(TIMER_GetIntBitState(TIMER2,TIMER_INT_CH3) != RESET){/*风扇1通道输入捕获中断*/TIMER_ClearIntBitState(TIMER2,TIMER_INT_CH3);PwmInput_Capture(FAN_NO_0);}if(TIMER_GetIntBitState(TIMER2,TIMER_INT_CH4) != RESET){/*风扇2通道输入捕获中断*/TIMER_ClearIntBitState(TIMER2,TIMER_INT_CH4);PwmInput_Capture(FAN_NO_1);}else{}
}
/**********************************************************************1-函数名:PwmInput_Timer_Update_Handler*2-函数功能:判断是否是有效溢出*3-输入参数:无*4-返回值:无*5-输入全局变量:无*6-输出全局变量:无*7-创建者与创建日期:Awen_ 2023-9-24**********************************************************************/
static void PwmInput_Timer_Update_Handler(void)
{uint8_t Index = 0;for(Index = 0;Index < FAN_Count_Num;Index++)		//FAN_Count_Num:总风扇个数{if(WaveCap[Index].WaveEgde == HIGH_LEVEL)		//是否是高电平状态{WaveCap[Index].ucUpdateCnt++;				//有效溢出 }else{}  }  
}
/**********************************************************************1-函数名:PwmInput_Capture*2-函数功能:输入捕获中断中的处理*3-输入参数:无*4-返回值:无*5-输入全局变量:无*6-输出全局变量:无*7-创建者与创建日期:Awen_ 2023-9-24**********************************************************************/
static void PwmInput_Capture(uint8_t Channel)
{  /*是否是第一次捕获*/if(WaveCap[Channel].WaveEgde == LOW_LEVEL){/*第一次捕获*/WaveCap[Channel].ucCaptureRisingVal[0] = FanPwm_Input_GetCapture(Channel); //第一次捕获CNTWaveCap[Channel].WaveEgde = HIGH_LEVEL;		//高电平状态  }else if(WaveCap[Channel].WaveEgde == HIGH_LEVEL){/*第二次捕获*/WaveCap[Channel].ucCaptureRisingVal[1] = FanPwm_Input_GetCapture(Channel); //第二次捕获CNTFanPwm_Input_EnableINT(Channel, DISABLE);	//关闭中断WaveCap[Channel].WaveEgde = LOW_LEVEL;		//恢复到低电平状态   WaveCap[Channel].ucFinishFlag = TRUE;		///捕获完成标记位}else{WaveCap[Channel].WaveEgde = LOW_LEVEL;FanPwm_Input_EnableINT(Channel, DISABLE);}
}

最后频率的计算方式

static void FanPwmI_SpeedCalcul()
{uint8_t Index = 0;uint32_t Freq_vallue;for(Index = 0;Index < FAN_Count_Num;Index++){if(WaveCap[Index].ucFinishFlag == TRUE)		//判断采集完成标记位{if(WaveCap[Index].ucUpdateCnt > 0)			//是否有有效溢出{/*算式1*/Freq_vallue =(0xFFFF - WaveCap[Index].ucCaptureRisingVal[0]) + ((WaveCap[Index].ucUpdateCnt - 1) * 0xFFFF) + WaveCap[Index].ucCaptureRisingVal[1];}else{/*算式2*/Freq_vallue =WaveCap[Index].ucCaptureRisingVal[1] - WaveCap[Index].ucCaptureRisingVal[0];}WaveCap[Index].ulFrequency = 1000000 / (Freq_vallue + 1);			//频率计算WaveCap[Index].ulFanSpeed = WaveCap[Index].ulFrequency * 30;  		//根据风扇手册计算转速            }else{}WaveCap[Index].ucFinishFlag = FALSE;				//采集完成标记位复位WaveCap[Index].ucUpdateCnt = 0;					//有效溢出计数复位}      
}  

总结

输入捕获还可采集波形占空比,其原理相同,只需要在第一次捕获之后改为下降沿触发,采集到第一个上升沿到第一个下降沿的CNT值,然后再设置为上升沿触发,采集一个完整波形周期CNT值,然后计算得到占空比。比较高效的做法是:硬件上使用Timer的两个通道的GPIO同时连到需要采集的波形管脚上,一个通道采集上升沿得到整个波形的周期,另一个通道采集下降沿得到波形高电平时长,再计算占空比。

相关文章:

GD32_定时器输入捕获波形频率

GD32_定时器输入捕获波形频率&#xff08;多通道轮询&#xff09; 之前项目上用到一个使用定时器捕获输入采集风扇波形频率得到风扇转速的模块&#xff0c;作为笔记简单记录以下当时的逻辑结构和遇到的问题&#xff0c;有需要参考源码、有疑问或需要提供帮助的可以留言告知 。…...

单窗口单IP适合炉石传说游戏么?

游戏道具制作在炉石传说中是一个很有挑战的任务&#xff0c;但与此同时&#xff0c;它也是一个充满机遇的领域。在这篇文章中&#xff0c;我们将向您展示如何在炉石传说游戏中使用动态包机、多窗口IP工具和动态IP进行游戏道具制作。 作者与主题的关系&#xff1a;作为一名热爱炉…...

win11安装docekr、docker-compose

1.docker安装 下载地址&#xff1a;Install Docker Desktop on Windows | Docker Docs 出问题别慌&#xff0c;看清楚提示信息&#xff0c;cmd更新wsl&#xff0c;什么是wsl&#xff0c;百度好好理解一下哦 2.docker-compose安装 还是去官方看看怎么说的&#xff0c;然后跟着处…...

Postman的简单使用

Postman简介 官网 Postman是Google公司开发的一款功能强大的网页调试与发送HTTP请求&#xff0c;并能运行测试用例的Chrome插件 使用Postman进行简单接口测试 新建测试 → 选择请求方式 → 请求URL&#xff0c;下面用百度作为例子&#xff1a; 参考文档 [1] Postman使用教程…...

信号继电器驱动芯片(led驱动芯片)

驱动继电器需要配合BAV99&#xff08;防止反向脉冲&#xff09;使用 具体应用参考开源项目 电阻箱 sbstnh/programmable_precision_resistor: A SCPI programmable precision resistor (github.com) 这个是芯片的输出电流设置 对应到上面的实际开源项目其设置电阻为1.5K&…...

IDEA配置HTML和Thymeleaf热部署开发

IDEA配置HTML和Thymeleaf热部署开发 1.项目配置2. IDEA配置3. 使用 需求&#xff1a;现在我们在开发不分离项目的时候&#xff08;SpringBootThmeleaf&#xff09;经常会改动了类或者静态html文件就需要重启一下服务器&#xff0c; 这样不仅时间开销很大&#xff0c;而且经常重…...

Nginx动静分离

为了加快网站的解析速度&#xff0c;可以把动态页面和静态页面由不同的服务器来解析&#xff0c;加快解析速度。降低原来单个服务器的压力。 在动静分离的tomcat的时候比较明显&#xff0c;因为tomcat解析静态很慢&#xff0c;其实这些原理的话都很好理解&#xff0c;简单来说&…...

Spring中AOP详解

目录 一、AOP的概念 二、AOP的底层实现原理 2.1 JDK的动态代理 2.1.1 invocationhandler接口 2.1.2 代理对象和原始类实现相同的接口 interfaces 2.1.3 类加载器ClassLoador 2.1.4 编码实现 2.2 Cglib动态代理 2.2.1 Cglib动态代理编码实现 三、AOP如何通过原始对象的id获取到代…...

Unity DOTS系列之Filter Baking Output与Prefab In Baking核心分析

最近DOTS发布了正式的版本, 我们来分享一下DOTS里面Baking核心机制&#xff0c;方便大家上手学习掌握Unity DOTS开发。今天给大家分享的Baking机制中的Filter Baking Output与Prefab In Baking。 对啦&#xff01;这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础…...

Matlab读写操作

随机生成一个3*3矩阵&#xff0c;对矩阵进行按列升序排列 >> Arand(3,3); >> [B, ~] sort(A, 2); >> B B 0.4898 0.6797 0.70940.4456 0.6551 0.75470.1626 0.2760 0.6463在不同数值类型下显示π的值 1、默认数值类型 >> p_defa…...

Android 开发技巧:音乐播放器的后台处理【Service、Handler、MediaPlayer】

给定部分完成的MusicPlayer项目&#xff0c;实现其中未完成的service部分&#xff1a; 1、创建MusicService类&#xff0c;通过service组件实现后台播放音乐的功能&#xff1b; 2、在MainActivity中通过ServiceConnection连接MusicService&#xff0c;实现对音乐播放的控制&…...

使用Windows平台的Hyper-V虚拟机安装CentOS7的详细过程

Hyper-V虚拟机安装CentOS7 前言常见Linux系统CentOSUbuntuDebianKaliFedoraArch LinuxMintManjaroopenSUSE Hyper-V开启Hyper-V打开Hyper-V Hyper-V的使用新建虚拟机开始安装分区配置开始安装 修改yum源为阿里源 前言 作为一名开发者&#xff0c;就服务器而言&#xff0c;接触最…...

某马机房预约系统 C++项目(二) 完结

8.4、查看机房 8.4.1、添加机房信息 根据案例&#xff0c;我们还是先在computerRoom.txt中直接添加点数据 //几机房 机器数量 1 20 2 50 3 1008.4.2、机房类创建 ​ 同样我们在头文件下新建一个computerRoom.h文件 添加如下代码&#xff1a; #pragma once #include<i…...

npm 安装到指定文件夹

创建一个文件夹&#xff0c;用vscode或者cmd打开&#xff0c; 执行 npm install --prefix ./ 路径 包名&#xff0c; npm install --prefix ./ 包名 &#xff0c; 就会将包安装在当前文件夹&#xff0c; 例如&#xff1a; npm install --prefix ./ -g oppo-minigame…...

自建的离散傅里叶变换matlab程序实现及其与matlab自带函数比较举例

自建的离散傅里叶变换matlab程序实现及其与matlab自带函数比较举例 在matlab中有自带的离散傅里叶变换程序&#xff0c;即fft程序&#xff0c;但该程序是封装的&#xff0c;无法看到源码。为了比较清楚的了解matlab自带的实现过程&#xff0c;本文通过自建程序实现matlab程序&…...

Vue图片路径问题(动态引入)

vue项目中我们经常会遇到动态路径的图片无法显示的问题&#xff0c;以下是静态路径和动态路径的常见使用方法。 1.静态路径 在日常的开发中&#xff0c;图片的静态路径通过相对路径和绝对路径的方式引入。 相对路径&#xff1a;以.开头的&#xff0c;例如./、../之类的。就是…...

项目部署Linux步骤

1、最小化安装centos7-环境准备 安装epel-release 安装epel-release&#xff0c;因为有些rpm包在官方库中找不到。前提是保证可以联网 yum install -y epel-release 修改IP net-tools net-tool&#xff1a;工具包集合&#xff0c;包含ifconfig等命令 yum install -y net-…...

UG\NX二次开发 在资源栏(左侧面板)中添加按钮

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 感谢粉丝订阅 感谢 apolloryd 订阅本专栏,非常感谢。 简介 UG\NX二次开发 在资源栏(左侧面板)中添加按钮,下面提供了帮助说明,在 UGOPEN 文件夹下有示例。 C++语言在UG二次…...

Proteus仿真--量程自动切换数字电压表(仿真+程序)

本文主要介绍基于51单片机的量程自动切换数字电压表Proteus仿真设计&#xff08;完整仿真源文件及代码见文末链接&#xff09; 简介 硬件电路主要分为单片机主控模块、AD转换模块、量程选择模块以及数码管显示模块 &#xff08;1&#xff09;单片机主控模块&#xff1a;单片…...

​如何使用ArcGIS Pro制作一张地形图

01数据来源 本教程所使用的数据是从水经微图中下载的DEM数据&#xff0c;除了DEM数据&#xff0c;常见的GIS数据都可以从水经微图中下载&#xff0c;你可以通过关注“水经注GIS”&#xff0c;然后在后台回复“微图”即可获取软件下载地址&#xff0c;当然也可以直接在水经注…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端

&#x1f31f; 什么是 MCP&#xff1f; 模型控制协议 (MCP) 是一种创新的协议&#xff0c;旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议&#xff0c;它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

ffmpeg(四):滤镜命令

FFmpeg 的滤镜命令是用于音视频处理中的强大工具&#xff0c;可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下&#xff1a; ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜&#xff1a; ffmpeg…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...

JVM暂停(Stop-The-World,STW)的原因分类及对应排查方案

JVM暂停(Stop-The-World,STW)的完整原因分类及对应排查方案,结合JVM运行机制和常见故障场景整理而成: 一、GC相关暂停​​ 1. ​​安全点(Safepoint)阻塞​​ ​​现象​​:JVM暂停但无GC日志,日志显示No GCs detected。​​原因​​:JVM等待所有线程进入安全点(如…...

3-11单元格区域边界定位(End属性)学习笔记

返回一个Range 对象&#xff0c;只读。该对象代表包含源区域的区域上端下端左端右端的最后一个单元格。等同于按键 End 向上键(End(xlUp))、End向下键(End(xlDown))、End向左键(End(xlToLeft)End向右键(End(xlToRight)) 注意&#xff1a;它移动的位置必须是相连的有内容的单元格…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...