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同时连到需要采集的波形管脚上,一个通道采集上升沿得到整个波形的周期,另一个通道采集下降沿得到波形高电平时长,再计算占空比。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118500.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Event

前言 ApplicationEvent 与 ApplicationListener 应用 实现 基于注解 事件过滤 异步事件监听 好处及应用场景 源码阅读 总结 1前言 ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口提供的。如果将实现了 ApplicationListener …

【AI视野·今日Robot 机器人论文速览 第五十八期】Thu, 19 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Thu, 19 Oct 2023 Totally 25 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers InViG: Benchmarking Interactive Visual Grounding with 500K Human-Robot Interactions Authors Hanbo Zhang, Jie Xu, Yuch…

Qt生成PDF报告

文章目录 一、示意图二、实现部分代码总结 一、示意图 二、实现部分代码 //! 生成测试报告 void MainWindow::createPdf(QString filename, _pdf_msg_& msg, const QMap<QString, int>& ok, const QMap<QString, int>& err) {//QDir dir;if(!dir.exis…

Vue快速入门

一、概述 1.是一套前端框架&#xff0c;可免除原生JavaScript中的DOM操作&#xff0c;基于MVVM思想&#xff0c;实现数据双向绑定。 实现由MVC——>MVVM的转换 二、入门 1.新建HTML页面&#xff0c;引入Vue.js文件 2.在JS代码区&#xff0c;创建Vue核心对象&#xff0c;进行…

嵌入式软件工程师面试题——2025校招专题(三)

说明&#xff1a; 面试题来源于网络书籍&#xff0c;公司题目以及博主原创或修改&#xff08;题目大部分来源于各种公司&#xff09;&#xff1b;文中很多题目&#xff0c;或许大家直接编译器写完&#xff0c;1分钟就出结果了。但在这里博主希望每一个题目&#xff0c;大家都要…

生产环境元空间内存溢出(OOM)的问题排查

一、现象 2023.10.17下午收到业务反馈&#xff0c;说是接口调用超时&#xff0c;进件系统和核心系统调用外数系统接口时等待过久&#xff0c;引起系统异常。然后我们看了下接口调用的日志&#xff0c;确实接口的响应时间在五十秒左右。我们自己测试了下&#xff0c;发现也是这…

leetcode 503. 下一个更大元素 II、42. 接雨水

下一个更大元素 II 给定一个循环数组 nums &#xff08; nums[nums.length - 1] 的下一个元素是 nums[0] &#xff09;&#xff0c;返回 nums 中每个元素的 下一个更大元素 。 数字 x 的 下一个更大的元素 是按数组遍历顺序&#xff0c;这个数字之后的第一个比它更大的数&…

【c#】2022创建WEB API接口教程demo

c#创建WEB API接口 创建WEB API接口结果图涉及到的技术设计流程创建WEB API接口 结果图 涉及到的技术 设计流程 1、创建WEB api项目,使用控制器和penapi勾选上,第一次创建项目时没有勾选,因为感觉没啥用。后面跑项目的时候,要把接口用swagger去直接生成的时候,还是需要…

自然语言处理---Transformer机制详解之BERT模型介绍

1 BERT简介 BERT是2018年10月由Google AI研究院提出的一种预训练模型. BERT的全称是Bidirectional Encoder Representation from Transformers.BERT在机器阅读理解顶级水平测试SQuAD1.1中表现出惊人的成绩&#xff1a;全部两个衡量指标上全面超越人类&#xff0c;并且在11种不…

Postman的简单使用

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

异常的处理和HTTP状态码的分类

在爬虫过程中&#xff0c;可能会遇到各种异常情况&#xff0c;如网络连接错误、网页解析错误、请求超时等。为了提高爬虫的稳定性和容错性&#xff0c;需要对这些异常进行处理。 异常处理是通过捕获和处理异常来解决程序中出现的错误情况。在爬虫中&#xff0c;常见的异常处理…

RCE 远程代码执行漏洞分析

RCE 漏洞 1.漏洞描述 Remote Command/Code Execute 远程命令执行/远程代码执行漏洞 这种漏洞通常出现在应用程序或操作系统中&#xff0c;攻击者可以通过利用漏洞注入恶意代码&#xff0c;并在受攻击的系统上执行任意命令。 2.漏洞场景 PHP 代码执行PHP 代码注入OS 命令执…

Kettle循环结果集中的数据并传入SQL组件【或转换】里面

简介&#xff1a;在尝试使用了结果集的Demo循环后&#xff0c;进入到生产还是有一点问题的&#xff0c;以下是各个组件的分解解释、遇到的问题&#xff0c;以及解决问题的思路&#xff0c;最后文章的最后会把完整的Ktr文件放出来。记得收藏点赞喔&#xff01; 先来看张图~来自…

OSPF的网络类型

1.3配置OSPF的网络类型 1.3.1实验3&#xff1a;配置P2P网络类型 实验需求 实现单区域OSPF的配置实现通过display命令查看OSPF的网络类型 实验拓扑 实验拓扑如图1-11所示 图1-11 配置P2P网络类型 实验步骤 步骤1&#xff1a;[1] 配置IP地址 路由器R1[2] 的配置 <Huawe…

html iframe 框架有哪些优缺点?

目录 前言&#xff1a; 用法&#xff1a; 理解&#xff1a; 优点&#xff1a; 嵌套外部内容&#xff1a; 独立性&#xff1a; 分离安全性&#xff1a; 跨平台兼容性&#xff1a; 方便维护&#xff1a; 缺点&#xff1a; 性能开销&#xff1a; 用户体验问题&#xf…

vue项目中内嵌iframe,打包上线时候iframe地址如何写?

vue项目中内嵌iframe&#xff0c;打包上线时候iframe地址如何写 一、项目结构1.内嵌的iframe文件位置2.打包后的iframe的位置 二、代码 前提描述&#xff0c;项目是用webpack打包的&#xff0c;内嵌一个完整的js小组件 一、项目结构 1.内嵌的iframe文件位置 2.打包后的iframe的…

图论05-【无权无向】-图的广度优先BFS遍历-路径问题/检测环/二分图/最短路径问题

文章目录 1. 代码仓库2. 单源路径2.1 思路2.2 主要代码 3. 所有点对路径3.1 思路3.2 主要代码 4. 联通分量5. 环检测5.1 思路5.2 主要代码 6. 二分图检测6.1 思路6.2 主要代码6.2.1 遍历每个联通分量6.2.2 判断相邻两点的颜色是否一致 7. 最短路径问题7.1 思路7.2 代码 1. 代码…

听GPT 讲Rust源代码--library/std(2)

File: rust/library/std/src/sys_common/wtf8.rs 在Rust源代码中&#xff0c;rust/library/std/src/sys_common/wtf8.rs这个文件的作用是实现了UTF-8编码和宽字符编码之间的转换&#xff0c;以及提供了一些处理和操作UTF-8编码的工具函数。 下面对这几个结构体进行一一介绍&…

wkhtmltoimage/wkhtmltopdf 使用实践

1. 介绍 wkhtmltopdf/wkhtmltoimage 用于将简单的html页面转换为pdf或图片&#xff1b; 2.安装 downloads 2.1. mac os 下载64-bit 版本然后按照指示安装, 遇到 untrust developers 时&#xff0c;需要在 Settings -> Privacy 处信任下该安装包。 2.2. debian # 可用…

【完美世界】被骂国漫之耻,石昊人设战力全崩,现在真成恋爱世界了

【侵权联系删除】【文/郑尔巴金】 深度爆料&#xff0c;《完美世界》动漫第135集预告片已经更新了&#xff0c;但是网友们对此却是一脸槽点。从预告中可以看出&#xff0c;石昊在和战王战天歌的大战中被打成重伤&#xff0c;最后云曦也被战天歌抓住。在云曦面临生死危机的时候…