基于STM32的频率计(采用输入捕获的方式)

2024-03-25 08:30

本文主要是介绍基于STM32的频率计(采用输入捕获的方式),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

参考博客:STM32 HAL库STM32脉冲宽度和周期测量
感谢大佬!!!

前言

之前拿外部中断做了一个频率计,范围到了1MHz,但是精确度并不是很高,误差在%0.5左右(看了一下那些大佬的频率计,基本上都是25MHz的量程范围,误差也是远低于%0.5)。
再结合外部中断的知识,发现除非采用matlab进行数据拟合(应该就是每一个频率节点都进行数据的校对),不然精度是无法提高的,而且对于一块STM32F103单片机而言,将时钟频率配置到最大的72MHz,1MHz的频率也应该是外部中断的极限了,所以我决定再用输入捕获的方法尝试一下。

原理

定时器的输入捕获的功能就是检测上升沿或者下降沿之间的时间间隔(这是我自己的理解可能有点不太严谨),然后就是根据定时器的配置的具体的理解:
在这里插入图片描述
第一个框是预装载值,第二个是满载值
这样的话 这个定时器的频率就是 单片机的频率(我这里配置的是72MHz)/预装载值+1 就是1MHz 那每次定时的周期就是1us,言下之意就是如果如果说检测的频率高于1MHz,就靠这样的配置是远远不够的。(而且输入捕获其实也是十分的消耗 cpu的资源的,他也和外部中断一样要不断的进入中断,对于一个100khHz的信号,1s就要进入中断100k次,这样就意味着,不光单片机无法实现其他的功能,而且中断的回调函数也必须十分精简不然会有相当大的误差)。
我的想法是:先检测信号的上升沿,当上升沿到来之后再将输入捕获改变成下降沿捕获,再进行数据处理得到相关的数据。

HAL库输入捕获相关的函数

__HAL_TIM_SET_COUNTER( 定时器地址 , 设置的数值 );
我用的是定时器2,然后用这个函数执行定时器2的计数值清零的操作,所以我是这样用的:
__HAL_TIM_SET_COUNTER(&htim2, 0);

TIM2_SetCapturePolarity(捕获触发模式);
我这个函数来改变输入捕获的模式(上升沿模式和下降沿模式)
TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_RISING); // 设置为上升沿触发
TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_FALLING); // 设置为下降沿触发

HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1)
这个是读取当时的数值,也比较的通俗易懂我这里就不详细介绍了。

代码部分

void TIM2_Poll(void)
{switch (TIM2_CAPTURE_STA){case 0:{TIM2_TIMEOUT_COUNT = 0;__HAL_TIM_SET_COUNTER(&htim2, 0);											// 清除定时器2现有计数memset(TIM2_CAPTURE_BUF, 0, sizeof(TIM2_CAPTURE_BUF));						// 清除捕获计数TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_RISING);					// 设置为上升沿触发HAL_TIM_Base_Start_IT(&htim2);												// 启动定时器更新中断HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1);									// 启动捕获中断TIM2_CAPTURE_STA++;break;}case 4:{double  high  = TIM2_CAPTURE_BUF[1] - TIM2_CAPTURE_BUF[0];double  cycle = TIM2_CAPTURE_BUF[2] - TIM2_CAPTURE_BUF[0];float frq = 1.0 / (((float)cycle) / 1000000.0);TIM2_CAPTURE_STA++;printf("高电平持续时间  %.3f ms\r\n", high / 1000);//printf("周期          : %. ms\r\n", cycle / 1000.0);printf("频率           %.3f Hz\r\n", frq);TIM2_CAPTURE_STA=0;break;}default:break;}
}
/// 定时器2时间溢出回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == htim2.Instance){TIM2_TIMEOUT_COUNT++;										// 溢出次数计数}
}///< 输入捕获回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == htim2.Instance){switch (TIM2_CAPTURE_STA){case 1:{TIM2_CAPTURE_BUF[0] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + TIM2_TIMEOUT_COUNT * 0xFFFF;TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_FALLING);					// 设置为下降沿触发TIM2_CAPTURE_STA++;break;}case 2:{TIM2_CAPTURE_BUF[1] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + TIM2_TIMEOUT_COUNT * 0xFFFF;TIM2_SetCapturePolarity(TIM_INPUTCHANNELPOLARITY_RISING);					// 设置为上升沿触发TIM2_CAPTURE_STA++;break;}case 3:{TIM2_CAPTURE_BUF[2] = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1) + TIM2_TIMEOUT_COUNT * 0xFFFF;HAL_TIM_IC_Stop_IT(htim, TIM_CHANNEL_1);									// 停止捕获HAL_TIM_Base_Stop_IT(&htim2);												// 停止定时器更新中断TIM2_CAPTURE_STA++;break;}default:break;}}
}

结合函数的说明,这个还是挺好理解的。(重点还是对定时器那预装载值和满载值的理解)

问题

现在串口的输出过于频繁了(本来可以再用一个定时器中断来实现的,但是外部中断那一章节也用的是这个方法,我这次就没用了,之后会用dma来实现,这个更加合理)。
频率精度很高,但是测量范围只有50kHz(看了一些文章,好像输入捕获也可以到1MHz,还在分析是什么原因。。。)

后来打算

昨天找到了这篇文章 我觉得很赞:
使用 STM32 测量频率和占空比的几种方法
之后还是想继续完善频率计,打算采用使用外部时钟计数器来实现。

这篇关于基于STM32的频率计(采用输入捕获的方式)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/844422

相关文章

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

Android里面的Service种类以及启动方式

《Android里面的Service种类以及启动方式》Android中的Service分为前台服务和后台服务,前台服务需要亮身份牌并显示通知,后台服务则有启动方式选择,包括startService和b... 目录一句话总结:一、Service 的两种类型:1. 前台服务(必须亮身份牌)2. 后台服务(偷偷干

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

JS 实现复制到剪贴板的几种方式小结

《JS实现复制到剪贴板的几种方式小结》本文主要介绍了JS实现复制到剪贴板的几种方式小结,包括ClipboardAPI和document.execCommand这两种方法,具有一定的参考价值,感兴趣的... 目录一、Clipboard API相关属性方法二、document.execCommand优点:缺点:

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c

Deepseek使用指南与提问优化策略方式

《Deepseek使用指南与提问优化策略方式》本文介绍了DeepSeek语义搜索引擎的核心功能、集成方法及优化提问策略,通过自然语言处理和机器学习提供精准搜索结果,适用于智能客服、知识库检索等领域... 目录序言1. DeepSeek 概述2. DeepSeek 的集成与使用2.1 DeepSeek API

CSS弹性布局常用设置方式

《CSS弹性布局常用设置方式》文章总结了CSS布局与样式的常用属性和技巧,包括视口单位、弹性盒子布局、浮动元素、背景和边框样式、文本和阴影效果、溢出隐藏、定位以及背景渐变等,通过这些技巧,可以实现复杂... 一、单位元素vm 1vm 为视口的1%vh 视口高的1%vmin 参照长边vmax 参照长边re