STM32cubemx定时外部模式测量10M以上频率

news2024/11/24 16:02:02

STM32cubemx定时外部模式测量10M以上频率

本文讲解利用定时器的外部时钟功能,巧妙测量高频外部信号频率。范围可以到高达30M以上。

所需工具:

  • 开发板:STM32F103RCT6
  • STM32CubeMX
  • IDE: Keil-MDK

文章目录

  • STM32cubemx定时外部模式测量10M以上频率
    • 原理讲解
      • 定时器的时钟
      • 定时器的外部时钟
      • 突破频率
    • 工程建立
      • 时钟树
      • 定时器配置
      • 串口配置
      • 代码生成
    • 代码编写
      • 串口重定向
      • 脉冲计数
    • 硬件连接
    • 运行结果
    • 练习
    • 后记

原理讲解

定时器的时钟

我们在正常使用TIM定时器的时候,在cubemx里面的时钟树里,随便点击配置,就可以选择好定时器的时钟。比如下面这个情况:

img

通过时钟树,给挂在APB2时钟上的定时器,提供了72M的时钟。内部是如何产生时钟的呢,我们这里不用关心,不在本文讨论范围,只要知道有一个72M的时钟输入给了定时器TIM作为时钟源。

形象一点说,是一个72M、占空比50%、高电平3.3V、低电平0V的PWM输入给了定时器。每当定时器检测到PWM的上升沿时,就会在CNT计数值寄存器中加一。

下面我们举例说明,有一个1k的内部时钟,输入给定时器。如下图

image-20230603165825032

当TIM检测到时钟的上升沿的时候,CNT寄存器就会加一。我们知道,当CNT计算到ARR寄存器里面存放的自动重装载值的时候,就会归零。如果ARR为1000。那么CNT的计数值成如下关系:

image-20230603170109275

因为内部时钟是1khz,CNT寄存器每1ms增加1,当计数到1000时,满足ARR的值,就会重新开始装载。

很好,现在开动我们的小脑筋。假如我们并不知道内部时钟是多少。我们可以如何通过CNT得知它的频率呢?

读者可以在这打住,想一下这个问题应该如何解决。后面会揭晓答案。

我们可以先将ARR值,设置的足够大,比如65536吧,避免计数值达到ARR后,重新装载从0计数。我们可以每1秒去读取CNT的值,读取出当前的值后,把CNT寄存器清零,重新开始计数。可以想象,每隔1秒读出来的CNT就是1秒内,定时器收到的内部时钟上升沿,上升沿的个数不就是时钟频率嘛!(呕吼~~狂喜)当前例子下,可以想象每次读取出来都是1000。

有了这个思路,我们不就可以测量定时器时钟源的频率吗?要是这个时钟源可以选择外部信号就好了,通过IO口引脚输入给定时器作为时钟源。欸!巧了,定时器还真有外部时钟引脚!

定时器的外部时钟

下图是从参考手册中,截取的定时器框图:

QQ图片20230603171436

TIMX_EXR就是外部时钟引脚,如果你看到这个框图犯嘀咕,好麻烦啊,其他都是什么乱七八糟的方框、旁边的ITR0、TRC是干什么的?不用担心,我们来对图进行一下化简:

914C47CEEC0B42B77177D684639B7C75

定时器引出一个引脚,这个引脚对应着一个IO口,我们把外部的PWM信号,输入给这个引脚:TIMx_EXR。pwm信号输入到上升沿检测中后,再连接CNT寄存器,对每个上升沿进行计数加一。

现在我们把ARR设置为65536,ETR引脚外接60k的PWM信号。每1秒去读取CNT寄存器的计数数值,读取完成后清空。可以想象,每次读取出来CNT都是60000,对应着这一秒内,CNT检测到60k个上升沿,PWM频率为60k。

可是!如果频率超过65536hz,CNT计数值打到ARR后自动清零,我们不就读不到数据了嘛?欲知后事如何,且听下节分说。

突破频率

CNT达到ARR后,确实会更新,从零计数。不过,会产生定时器中断嘛。我们可以定义一个变量COUNT,当中断产生时,变量就加一,这个变量便表明,记录了多少个轮回,有多少个ARR。

假设:ARR为50k,外部输入PWM频率为10.001M。我们每1秒读取时,会读取到COUNT为2000,CNT寄存器值为1000。那么可以推算出来PWM频率为:
A R R ∗ C O U N T + C N T = 50000 ∗ 2000 + 1000 = 10.001 M ARR*COUNT+CNT=50000*2000+1000=10.001M ARRCOUNT+CNT=500002000+1000=10.001M
需要注意的是,每次读取后,不光要清空CNT寄存器,还需要清空COUNT变量。

工程建立

时钟树

image-20230603190613506

image-20230603190625608

定时器配置

本例程使用到两个定时器。首先是定时器2,时钟源选择外部时钟。

image-20230603190513559

可以看到PA0便是定时器2对应的外部时钟引脚,我们后面只需要把外部PWM信号,连接到PA0,就可以测量频率了。

image-20230603190914960

开启定时器2的中断,用来在计数值超过65536时,产生更新中断,在中断中对变量进行加一,表明此时计数了一个周期。

image-20230603191028836

定时器3用来产生1s的中断,每隔一秒在中断中读取当前频率。

image-20230603190653410

image-20230603191955032

串口配置

image-20230603192104481

代码生成

QQ图片20230603192419

QQ图片20230603192436

代码编写

串口重定向

在usart.c靠末尾的地方,编写下面函数

#include <stdio.h>

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}

int fgetc(FILE *f)
{
  uint8_t ch = 0;
  HAL_UART_Receive(&huart1, &ch, 1, 0xffff);
  return ch;
}

QQ图片20230603192340

在main.c中,包含头文件:

#include "stdio.h"

QQ图片20230603192910

QQ图片20230603194008

脉冲计数

首先定义用来计数的变量,变量含义在注释中说明。

uint32_t FQ; // 用来存放频率

uint16_t CNT_2 = 0; // 超出65536位后 用这个记录超过的次数
uint16_t CNT_1 = 0; // 0-65536内用这个记录

QQ图片20230603192836

接着开启定时器2和3。

HAL_TIM_Base_Start_IT(&htim3); // 启动定时器3进行1S定时

HAL_TIM_Base_Start_IT(&htim2); // 启动定时器2进行外部脉冲计数

QQ图片20230603192839

因为定时器2、3开启有先后顺序,所以第一次测量到的频率不能使用

接下来便是本文的重要部分,读取计数值:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim == (&htim3)) // 定时器中断1S触发一次
    {
        CNT_1 = __HAL_TIM_GET_COUNTER(&htim2); // 读取CNT计数值
        //		CNT_1 = TIM2->CNT;// 读取CNT寄存器这样也可以
        FQ = CNT_1 + CNT_2 * (__HAL_TIM_GET_AUTORELOAD(&htim2) + 1); // 获取频率值
        __HAL_TIM_SET_COUNTER(&htim2, 0);// 设置CNT为0
        printf("cnt:%u\n", FQ); // 打印频率值   1S打印一次
        CNT_1 = 0;  
        CNT_2 = 0; 
    }
    if (htim == (&htim2)) // 计数值计满,产生更新中断
    {
        CNT_2++; // 记满后,+1,标志多了一个满量程数
    }
}

image-20230603193436367

此处的频率计算公式如下:
F Q = C N T 1 + C N T 2 ∗ ( 65535 + 1 ) FQ=CNT_1+CNT_2*(65535+1) FQ=CNT1+CNT2(65535+1)

__HAL_TIM_GET_AUTORELOAD(&htim2)读取的是TIM2的ARR寄存器值,因为计数都是从0开始,所以要加一。比如ARR为999,从0计算到999,实际计算了999+1次。

硬件连接

引脚连接对象释义
PA0信号发生器的正极TIM2的外部时钟引脚
GND信号发生器的负极供地

因为我的开发板板载ch340,这里并不需要外接ch340串口助手。

IMG_20230603_195910

运行结果

信号发生器产生12M的方波信号,0-3.3V。串口信息如下:

image-20230603200553300

可以看到,cnt成功测量出来了外接信号的频率,误差0.004%。

实际上,因为定时器内部的边沿检测部分存在,定时不光可以测量PWM信号的频率,还可以直接测量正弦、三角波等频率,幅度也不一定要0-3.3V!

比如我测量一个1M,1V-2V的三角波:

IMG_20230603_202058

image-20230603202037889

正如前面所提到的,刚开始运行时,前两个数据是不能用的,需要扔掉,后续测量出来信号频率是999957hz,误差0.0043%。

如下是我测试出来的,能测量频率的不同波形、不同幅度的信号。

  • 方波(8M以内的极限值,再往上,高电平尽量高,低电平尽量低)
    • 高电平1.6——3V(3.3是上限,建议用3V以内)
    • 低电平-200mv——1.3v。
  • 正弦(1M以内)
    • 高电平1.7V——3V
    • 低电平-200mv——1.3v
  • 三角(1k以内)
    • 高电平1.7V——3V
    • 低电平-200mv——1.3v

下表是我用H750测试出来的表单,H7的测试精度略高于F1

频率误差
1k0
10k0
1M22
10M212
15M317
我的信号发生器发不出更高的————

练习

  1. 复现本文例程,测量不同幅值频率下,方波、正弦等波形的信号频率。
  2. 该方法的测量范围高,但是测量时间长,有没有办法略微牺牲精度,换取更快的频率测量?

后记

本文章收录于:

唐承乾的电赛小站

本文为系列文章中的冰山一角,欢迎进入小站查看。

配套程序:

STM32cubemx定时外部模式测量10M以上频率例程 gitee

QQ图片20230603203816

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

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

相关文章

【Java技术专题】「Guava开发指南」手把手教你如何进行使用Guava工具箱进行开发系统实战指南(Optional篇)

Guava相关的介绍 Guava工程包含了许多被Google的Java项目广泛依赖的核心库。我们希望通过本文档为Guava中最流行和最强大的功能提供更具可读性和解释性的说明。 本教程是中级教程&#xff0c;适合 Guava 中级开发者的进阶学习。 学习Guava前的准备工作 学习目标和计划&#xf…

Python学习笔记 - 探索集合Set

尊敬的各位&#xff0c;我是Mr.数据杨&#xff0c;非常高兴和你们一起走进Python的世界。今天&#xff0c;让我们一起探讨Python中神奇的“集合”。你们是否读过《三国演义》&#xff1f;你们应该记得那场赫赫有名的“桃园三结义”吧&#xff1f;那让我们以此为例&#xff0c;来…

Windows动态链接库的生成和使用

工程需要&#xff0c;最近在编一组Windows上的动态链接库给Python调用。之前做过Linux下C动态库的编译&#xff0c;并提供给Python调用&#xff0c;Windows下的编译跟Linux还是有些差距&#xff0c;因此花了一点时间跑通&#xff0c;在这里记录一下。 为了完整对比&#xff0c…

Hive on Spark环境搭建

Hive 引擎简介 Hive 引擎包括&#xff1a;默认 MR、tez、spark 最底层的引擎就是MR &#xff08;Mapreduce&#xff09;无需配置&#xff0c;Hive运行自带 Hive on Spark&#xff1a;Hive 既作为存储元数据又负责 SQL 的解析优化&#xff0c;语法是 HQL 语法&#xff0c;执行…

(2.54mm)TSM-120-04-S-DV-P-TR方形接线柱针脚、ADRF5545ABCPZN(通用)射频前端 SPDT

TSM-120-04-S-DV-P-TR (2.54mm) 表面安装.025"方形接线柱针脚是板对板连接器&#xff0c;有单排、双排或三排方形接线柱端子可供选择&#xff0c;带直通、直角或混合技术引脚。这些高度可靠的坚固针脚有垂直和水平两种方向&#xff0c;在混合气流 (MFG) 环境中可使用10年。…

【C语言实现简易ATM】上个C语言程序设计课,我成产品经理了?

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;C语言程序设计实验项目 如果本文对你有所帮助的话&#xff0c;还希望可以点赞&#x1f44d;收藏&#x1f4c2;支持一下…

2023年京东618满300减50是全品类吗?满减叠卷怎么用?

2023年京东618满300减50是全品类吗?满减叠卷怎么用? 京东平台上有着比较多的卖家开店&#xff0c;在对店铺进行运营的过程中&#xff0c;很多卖家都会参与平台的一些活动&#xff0c;这样能够有效的将产品推广出去&#xff0c;对于618大促活动也是属于其中活动之一&#xff0…

代码随想录第52天

1.最长递增子序列 接下来&#xff0c;我们依然用动规五部曲来详细分析一波&#xff1a; dp[i]的定义 本题中&#xff0c;正确定义dp数组的含义十分重要。 dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度 为什么一定表示 “以nums[i]结尾的最长递增子序” &…

软考A计划-电子商务设计师-专业英语

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例 &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分享&am…

高压侧电流检测电路及仿真

高压侧电流检测电路仿真 电路图如下 主要设计思想&#xff0c;通过两组电阻将高压侧的共模电压降到运放&#xff08;此处也可以使用单电源运放&#xff09;的工作电压范围内。然后在进行二次放大。主要放大倍数取决于第二级放大侧电阻比值。因此如果需要减小功耗&#xff0c;可…

JAVA数组基础

目录 一、使用方式 1-动态初始化 ①先声明数组 ② 创建数组 ③分配方式 二、使用方式 2-静态初始化&#xff08;直接在声明的同时初始化{ } &#xff09; 三、数组使用注意事项和细节 四、数组两种初始化方式都是将内存空间分配到堆上面的 一、使用方式 1-动态初始化 …

助力金融科技创新 同创永益受邀参加2023 INNO CHINA中国产业创新大会

2023 INNO CHINA中国产业创新大会-金融科技创新论坛于2023年5月10日在北京大学中关村新园召开。本次金融科技创新论坛由北大创新评论主办&#xff0c;同创永益受邀参加。来自银行、保险、证券等金融行业的多位专家、学者齐聚大会现场&#xff0c;共同探讨金融科技发展新趋势、金…

复合型人才

一、为什么需要复合型人才 在社会的大环境影响下&#xff0c;不同行业所展现的交叉属性越来越强&#xff0c;因此单一型人才已经不满足当前的企业需求&#xff0c;复合型人才逐渐成为市场上的主流&#xff1a;即不仅要“精”通&#xff0c;更要“全”面。 人才需求是由市场的…

长尾词挖掘,如何选择精准的长尾词优化?

长尾词的挖掘也是一门大学问&#xff0c;它存在多种不同的方法。最常用的方法是把关键词直接放搜索引擎的搜索框搜索和使用长尾词挖掘工具这两种。 以运动水壶为例。 关键词直接放搜索引擎的搜索框搜索&#xff0c;结果如下&#xff1a; 使用长尾词挖掘工具&#xff0c;…

【C++笔记总结】面向对象编程——封装 |C++

文章目录 前言一、类的封装1.1、公有&#xff0c;私有&#xff0c;保护1.2、类的定义和类的实现相分离1.3、构造函数&#xff0c;析构函数&#xff0c;拷贝构造函数1.4、静态数据成员和静态成员函数1.5、友元函数&#xff0c;友元类 二、类的实现——对象2.1、对象的静态分配&a…

【SpringCloud——Elasticsearch(中)】

一、DSL查询语法以及整合JAVA代码使用 以下操作案例均基于上篇的hotel索引库及其数据进行。 1、查询基本语法 GET /indexName/_search {"query": {"查询类型":{"查询条件":"条件值"}} } 2、查询所有 2.1、DSL语句 #查询所有 GET …

Java并发编程面试题——线程池

目录 1.什么是线程池&#xff1f;有什么优缺点&#xff1f;2.创建线程池的方式有哪些&#xff1f;2.1.通过 Executor 框架的工具类 Executors 来创建不同类型的线程池2.2.使用 ThreadPoolExecutor 类自定义线程池2.3.注意事项 3.自定义线程池时有哪些参数&#xff1f;它们各有说…

笔试强训 Day 7

选择题&#xff1a; 1.在&#xff08;&#xff09;情况下适宜采用 inline 定义内联函数 A 函数体含有循环语句 B 函数体含有递归语句C 函数代码少、频繁调用 D 函数代码多&#xff0c;不常调用 复习一下内联函数 在编译阶段&#xff0c;会将内联函数展开 —— 将函数调用替换成…

四、若依(前后端分离)项目构建docker 镜像

若依(前后端分离&#xff09;项目构建docker 镜像 1. 构建好ruoyi-admin.jar包&#xff0c;上传到服务器项目目录下 2. 创建conf目录将若依项目&#xff08;Spring boot &#xff09;配置文件修改好&#xff0c;上传存入conf目录 注意&#xff1a;这里的地址不能写127.0.0.1和…

ur5在gazebo中仿真的官方源码浅析

一 复现 好久之前初学rosgazebo机械臂仿真的时候总有些懵&#xff0c;用的是ur5机械臂&#xff0c;现在回过头来看好像看懂了一些&#xff0c;故重新理清了一下功能包的逻辑&#xff0c;方便查阅。 官方源码 本文参考 ubuntu16.04安装UR3/UR5/UR10机械臂的ROS驱动并实现gazebo…