频率测量是个最基本的且常见的工业需求.
但是这种简单的需求却不是那么的好实现. 总体来看, 目前的单片机还是有很大的改进空间.
很少有频率测量能够覆盖所有的频率范围.
而使用 STM32F103 性能有限.
根据待测频率, 我分成低中高, 三个阶段. 分别对应着3种不同的测量方法.
低频 1hz- 200khz,
测量方法, STM32 时钟计数器的输入捕获中断函数测量法.
测量原理
输入捕获模式可以用来测量脉冲宽度或者测量频率。
STM32 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。
原理:假定定时器工作在向上计数模式,图中 t1~t2 时间,就是我们需要测量的高电平时间。同样的, 也可以用来测两个下降沿也就是一个周期的间隔时间 . 主要原理还是计算两个边沿之间的计数器的差.
测量方法如下:首先设置定时器通道 x 为 上升沿捕获,这样t1 时刻,STM32就会用硬件捕获到当前的 CNT 值到CCRx,产生中断,然后在程序中读取CCRx即可.
注意: 我曾经想在取得CCRx后, 立即设置CNT=0, 使得CNT从0开始重新计数.下次捕获t2的时候直接读取到的CCRx中就是时间差, 但是实验证明这里 不能立即清零 CNT, ,因为这里从进入中断 到判断完毕再到设置CNT为0的过程中,CNT已经计数很多了, 这个时候清0,无疑会丢失从进入中断到设置CNT为0,这部分的时间的计数值. 最后会导致数值不准确,时钟频率越高, 丢失的个数越大. 所以这里只能每次读取CCRx的数值 .
这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值到CCRx2。这样,根据定时器的计数频率,我们就可以算出 t2 - t1 的时间,从而得到高电平脉宽。 在 t1 ~ t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。如图14.1.1所示,t1 ~ t2之间,CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度。
此种方法要注意的几个点是,
- TIM定时器的计数器CNT是16位的(有的是32位的). 超出65536 将会溢出.需要在溢出时在中断函数中增加一个软件计数即可. (当然也可以降低计数器的时钟, 但是不建议.这样会降低测量精度)
- 定时器在计数中, 最好不要更改CNT值, 只读取即可.
- 当被测的频率高到一定程度时, 每次跳变系统都会进入中断, 当中断函数里面的处理过长时, 将会导致系统罢工. 所以这里应该要在中断中增加一个额外的停止TIM时钟计数的功能. 防止系统频繁进入中断. 在while 循环中定时开启即可. 一般情况下, 不需要实时计算频率. 定时1秒计算一次频率即可.
中频测量
输入捕获+DMA
使用DMA, 自动读取N个 TIM的CNT到数组中, 然后定时1秒去计算一次CNT之间的差, 取个平均值. 这样不用频繁的进入系统中断. DMA的速度比软件中断要稍微快那么一点, CPU的开销也小了很多.
理论上只要DMA搬运的速度跟得上新数据产生的速度, 就能测到频率.
此种方法注意事项:
1.因为用DMA搬运CNT数值, 所以无法处理CNT溢出问题.需要在软件中处理.溢出的数值. 例如抛弃掉异常值.
2.软件计算时应当停止DMA搬运, 否则可能覆盖正在计算的数值. 最好将DMA设置为一轮,不要循环搬运. 计算完毕再开启DMA搬运.爱心小提示.开启和停止之间要有时间间隙.给DMA搬运的时间.
3.并非所有的TIM的所有通道都支持DMA搬运输入捕获值, 据我所知,STM32F103 只有TIM1的CH1通道是支持DMA搬运输入捕获值的. 用时需要先用STM32CubeIDE 查看支持DMA搬运输入捕获值的定时器通道有哪些…
高频测量
对于更高频率的频率测量.
1. ADC + 软件测量
2. FFT
时间有限, 有空再写.