基于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

相关文章

gitlab安装及邮箱配置和常用使用方式

《gitlab安装及邮箱配置和常用使用方式》:本文主要介绍gitlab安装及邮箱配置和常用使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1.安装GitLab2.配置GitLab邮件服务3.GitLab的账号注册邮箱验证及其分组4.gitlab分支和标签的

C++中零拷贝的多种实现方式

《C++中零拷贝的多种实现方式》本文主要介绍了C++中零拷贝的实现示例,旨在在减少数据在内存中的不必要复制,从而提高程序性能、降低内存使用并减少CPU消耗,零拷贝技术通过多种方式实现,下面就来了解一下... 目录一、C++中零拷贝技术的核心概念二、std::string_view 简介三、std::stri

Linux脚本(shell)的使用方式

《Linux脚本(shell)的使用方式》:本文主要介绍Linux脚本(shell)的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录概述语法详解数学运算表达式Shell变量变量分类环境变量Shell内部变量自定义变量:定义、赋值自定义变量:引用、修改、删

python判断文件是否存在常用的几种方式

《python判断文件是否存在常用的几种方式》在Python中我们在读写文件之前,首先要做的事情就是判断文件是否存在,否则很容易发生错误的情况,:本文主要介绍python判断文件是否存在常用的几种... 目录1. 使用 os.path.exists()2. 使用 os.path.isfile()3. 使用

Mybatis的分页实现方式

《Mybatis的分页实现方式》MyBatis的分页实现方式主要有以下几种,每种方式适用于不同的场景,且在性能、灵活性和代码侵入性上有所差异,对Mybatis的分页实现方式感兴趣的朋友一起看看吧... 目录​1. 原生 SQL 分页(物理分页)​​2. RowBounds 分页(逻辑分页)​​3. Page

Linux链表操作方式

《Linux链表操作方式》:本文主要介绍Linux链表操作方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、链表基础概念与内核链表优势二、内核链表结构与宏解析三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势六、典型应用场景七、调试技巧与

Linux实现线程同步的多种方式汇总

《Linux实现线程同步的多种方式汇总》本文详细介绍了Linux下线程同步的多种方法,包括互斥锁、自旋锁、信号量以及它们的使用示例,通过这些同步机制,可以解决线程安全问题,防止资源竞争导致的错误,示例... 目录什么是线程同步?一、互斥锁(单人洗手间规则)适用场景:特点:二、条件变量(咖啡厅取餐系统)工作流

RedisTemplate默认序列化方式显示中文乱码的解决

《RedisTemplate默认序列化方式显示中文乱码的解决》本文主要介绍了SpringDataRedis默认使用JdkSerializationRedisSerializer导致数据乱码,文中通过示... 目录1. 问题原因2. 解决方案3. 配置类示例4. 配置说明5. 使用示例6. 验证存储结果7.

Python程序打包exe,单文件和多文件方式

《Python程序打包exe,单文件和多文件方式》:本文主要介绍Python程序打包exe,单文件和多文件方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录python 脚本打成exe文件安装Pyinstaller准备一个ico图标打包方式一(适用于文件较少的程

Python验证码识别方式(使用pytesseract库)

《Python验证码识别方式(使用pytesseract库)》:本文主要介绍Python验证码识别方式(使用pytesseract库),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全... 目录1、安装Tesseract-OCR2、在python中使用3、本地图片识别4、结合playwrigh