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

相关文章

gradle第三方Jar包依赖统一管理方式

《gradle第三方Jar包依赖统一管理方式》:本文主要介绍gradle第三方Jar包依赖统一管理方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录背景实现1.顶层模块build.gradle添加依赖管理插件2.顶层模块build.gradle添加所有管理依赖包

Linux之systemV共享内存方式

《Linux之systemV共享内存方式》:本文主要介绍Linux之systemV共享内存方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、工作原理二、系统调用接口1、申请共享内存(一)key的获取(二)共享内存的申请2、将共享内存段连接到进程地址空间3、将

Maven中引入 springboot 相关依赖的方式(最新推荐)

《Maven中引入springboot相关依赖的方式(最新推荐)》:本文主要介绍Maven中引入springboot相关依赖的方式(最新推荐),本文给大家介绍的非常详细,对大家的学习或工作具有... 目录Maven中引入 springboot 相关依赖的方式1. 不使用版本管理(不推荐)2、使用版本管理(推

C#使用StackExchange.Redis实现分布式锁的两种方式介绍

《C#使用StackExchange.Redis实现分布式锁的两种方式介绍》分布式锁在集群的架构中发挥着重要的作用,:本文主要介绍C#使用StackExchange.Redis实现分布式锁的... 目录自定义分布式锁获取锁释放锁自动续期StackExchange.Redis分布式锁获取锁释放锁自动续期分布式

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Spring Boot读取配置文件的五种方式小结

《SpringBoot读取配置文件的五种方式小结》SpringBoot提供了灵活多样的方式来读取配置文件,这篇文章为大家介绍了5种常见的读取方式,文中的示例代码简洁易懂,大家可以根据自己的需要进... 目录1. 配置文件位置与加载顺序2. 读取配置文件的方式汇总方式一:使用 @Value 注解读取配置方式二

JAVA保证HashMap线程安全的几种方式

《JAVA保证HashMap线程安全的几种方式》HashMap是线程不安全的,这意味着如果多个线程并发地访问和修改同一个HashMap实例,可能会导致数据不一致和其他线程安全问题,本文主要介绍了JAV... 目录1. 使用 Collections.synchronizedMap2. 使用 Concurren

C# foreach 循环中获取索引的实现方式

《C#foreach循环中获取索引的实现方式》:本文主要介绍C#foreach循环中获取索引的实现方式,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录一、手动维护索引变量二、LINQ Select + 元组解构三、扩展方法封装索引四、使用 for 循环替代

将Java程序打包成EXE文件的实现方式

《将Java程序打包成EXE文件的实现方式》:本文主要介绍将Java程序打包成EXE文件的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录如何将Java程序编程打包成EXE文件1.准备Java程序2.生成JAR包3.选择并安装打包工具4.配置Launch4

springboot上传zip包并解压至服务器nginx目录方式

《springboot上传zip包并解压至服务器nginx目录方式》:本文主要介绍springboot上传zip包并解压至服务器nginx目录方式,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录springboot上传zip包并解压至服务器nginx目录1.首先需要引入zip相关jar包2.然