STM32cubemx定时外部模式测量10M以上频率 | 您所在的位置:网站首页 › tim中的文件在哪 › STM32cubemx定时外部模式测量10M以上频率 |
STM32cubemx定时外部模式测量10M以上频率
本文讲解利用定时器的外部时钟功能,巧妙测量高频外部信号频率。范围可以到高达30M以上。 所需工具: 开发板:STM32F103RCT6STM32CubeMXIDE: Keil-MDK 文章目录 STM32cubemx定时外部模式测量10M以上频率原理讲解定时器的时钟定时器的外部时钟突破频率 工程建立时钟树定时器配置串口配置代码生成 代码编写串口重定向脉冲计数 硬件连接运行结果练习后记 原理讲解 定时器的时钟我们在正常使用TIM定时器的时候,在cubemx里面的时钟树里,随便点击配置,就可以选择好定时器的时钟。比如下面这个情况: 通过时钟树,给挂在APB2时钟上的定时器,提供了72M的时钟。内部是如何产生时钟的呢,我们这里不用关心,不在本文讨论范围,只要知道有一个72M的时钟输入给了定时器TIM作为时钟源。 形象一点说,是一个72M、占空比50%、高电平3.3V、低电平0V的PWM输入给了定时器。每当定时器检测到PWM的上升沿时,就会在CNT计数值寄存器中加一。 下面我们举例说明,有一个1k的内部时钟,输入给定时器。如下图 当TIM检测到时钟的上升沿的时候,CNT寄存器就会加一。我们知道,当CNT计算到ARR寄存器里面存放的自动重装载值的时候,就会归零。如果ARR为1000。那么CNT的计数值成如下关系: 因为内部时钟是1khz,CNT寄存器每1ms增加1,当计数到1000时,满足ARR的值,就会重新开始装载。 很好,现在开动我们的小脑筋。假如我们并不知道内部时钟是多少。我们可以如何通过CNT得知它的频率呢? 读者可以在这打住,想一下这个问题应该如何解决。后面会揭晓答案。 我们可以先将ARR值,设置的足够大,比如65536吧,避免计数值达到ARR后,重新装载从0计数。我们可以每1秒去读取CNT的值,读取出当前的值后,把CNT寄存器清零,重新开始计数。可以想象,每隔1秒读出来的CNT就是1秒内,定时器收到的内部时钟上升沿,上升沿的个数不就是时钟频率嘛!(呕吼~~狂喜)当前例子下,可以想象每次读取出来都是1000。 有了这个思路,我们不就可以测量定时器时钟源的频率吗?要是这个时钟源可以选择外部信号就好了,通过IO口引脚输入给定时器作为时钟源。欸!巧了,定时器还真有外部时钟引脚! 定时器的外部时钟下图是从参考手册中,截取的定时器框图: TIMX_EXR就是外部时钟引脚,如果你看到这个框图犯嘀咕,好麻烦啊,其他都是什么乱七八糟的方框、旁边的ITR0、TRC是干什么的?不用担心,我们来对图进行一下化简: 定时器引出一个引脚,这个引脚对应着一个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 ARR∗COUNT+CNT=50000∗2000+1000=10.001M 需要注意的是,每次读取后,不光要清空CNT寄存器,还需要清空COUNT变量。 工程建立 时钟树本例程使用到两个定时器。首先是定时器2,时钟源选择外部时钟。 可以看到PA0便是定时器2对应的外部时钟引脚,我们后面只需要把外部PWM信号,连接到PA0,就可以测量频率了。 开启定时器2的中断,用来在计数值超过65536时,产生更新中断,在中断中对变量进行加一,表明此时计数了一个周期。 定时器3用来产生1s的中断,每隔一秒在中断中读取当前频率。 在usart.c靠末尾的地方,编写下面函数 #include 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; }在main.c中,包含头文件: #include "stdio.h"首先定义用来计数的变量,变量含义在注释中说明。 uint32_t FQ; // 用来存放频率 uint16_t CNT_2 = 0; // 超出65536位后 用这个记录超过的次数 uint16_t CNT_1 = 0; // 0-65536内用这个记录接着开启定时器2和3。 HAL_TIM_Base_Start_IT(&htim3); // 启动定时器3进行1S定时 HAL_TIM_Base_Start_IT(&htim2); // 启动定时器2进行外部脉冲计数因为定时器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,标志多了一个满量程数 } }此处的频率计算公式如下: 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串口助手。 信号发生器产生12M的方波信号,0-3.3V。串口信息如下: 可以看到,cnt成功测量出来了外接信号的频率,误差0.004%。 实际上,因为定时器内部的边沿检测部分存在,定时不光可以测量PWM信号的频率,还可以直接测量正弦、三角波等频率,幅度也不一定要0-3.3V! 比如我测量一个1M,1V-2V的三角波: 正如前面所提到的,刚开始运行时,前两个数据是不能用的,需要扔掉,后续测量出来信号频率是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 频率误差1k010k01M2210M21215M317我的信号发生器发不出更高的———— 练习 复现本文例程,测量不同幅值频率下,方波、正弦等波形的信号频率。该方法的测量范围高,但是测量时间长,有没有办法略微牺牲精度,换取更快的频率测量?外部计数本质是测量外部的上升沿个数,CNT达到ARR值时,会产生更新中断,这个功能能否在其他场景下起作用呢?ARR是可以动态更改的。 后记本文章收录于: 唐承乾的电赛小站 本文为系列文章中的冰山一角,欢迎进入小站查看。 配套程序: STM32cubemx定时外部模式测量10M以上频率例程 gitee |
CopyRight 2018-2019 实验室设备网 版权所有 |