本内容基于江协科技STM32视频学习之后整理而得。
1. 输入捕获IC
1.1 输入捕获IC简介
- 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数;
- 每个高级定时器和通用定时器都拥有4个输入捕获通道;
- 可配置为PWMI模式(即PWM输入模式),同时测量频率和占空比;
- 可配合主从触发模式,实现硬件全自动测量;
1.2 频率测量
频率的定义:1s内出现了多少个重复的周期
对于STM32测频率而言,也是只能测量数字信号的频率。如果需要测量一个正弦波,还需要搭建一个信号预处理电路,最简单的就是用运放搭一个比较器,把正弦波转换为数字信号,再输入给STM32就行了。如果测量的电压信号非常高,那还需要考虑一下隔离的问题,比如用一些隔离放大器、电压互感器等元件,隔离高压端和低压端,保证电路的安全。总之,经过处理最终输入给STM32的信号,要是这样的高低电平信号,高电平3.3V,低电平0V。
-
测频法原理:
频率的定义就是1s内出现了多少个重复的周期,那频率就是多少Hz。 -
测周法原理
周期的倒数是频率。捕获信号的两个上升沿,测量一下这之间持续的时间就行了。测量时间的方法实际上也是定时器计次,使用一个已知的标准频率fc的计次时钟来驱动计数器,从一个上升沿开始计,计数器从0开始,一直计到下一个上升沿,停止。计一个数的时间是1/fc,计N个数,时间就是N/fc,N/fc就是周期,再取个倒数,就得到了公式fx=fc/N。 -
测频法和测周法优缺点
测频法适合测量高频信号,测周法适合测量低频信号。测频法在闸门时间内,最好多出现一些上升沿,计次数量多一些,这样有助于减小误差。测周法要求信号频率低一些,低频信号周期比较长,计次就会比较多,有助于减小误差。
测频法测量结果更新的慢一些,数值相对稳定。测周法更新的快,数据跳变的也非常快。测频法测量的是在闸门时间内的多个周期,所以它自带一个均值滤波,如果在闸门时间内波形频率有变化,那得到的其实是这一段时间的平均频率。如果闸门时间选为1s,那么每隔1s才能得到一次结果,所以测频法结果更新慢,测量结果是一段时间的平均值,值比较平滑。测周法是只测量一个周期,就能出一次结果,所以出结果的速度取决于待测信号的频率,一般而言,待测信号都是几百几千Hz,所以一般情况下,测周法结果更新更新更快,但由于它只测一个周期,所以结果值会受噪声的影响,波动比较大。 -
中界频率
对于测频法和测周法,计次数量N要尽量大一些,N越大,相对误差越小,因为在这些方法中,计次可能会存在正负1误差。比如测频法,在闸门时间内,并不是每个周期信号都是完整的,比如在最后时间里,可能有一个周期刚出现一半,闸门时间就到了,那这只有半个周期,只有舍弃掉或者当做一整个周期来看,因为计次只有整数,不可能计次0.5个数吧。那在这个过程,就会出现多计一个,或者少计一个的情况,这就叫做正负1误差。在测周法中,用标准频率fc计次,有像这样可能,一个数刚数到一半,计时就结束了。那这半个数也只能舍弃或者按一整个数来算了,这里也会出现正负1误差。所以正负1误差是这两种方法固有的误差,要想减小正负1误差的影响,就只能尽量多计一些数。当计次N比较大时,正负1对N的影响就会很小。
那当有一个频率,测频法和测周法计次的N相同,就说明误差相同,这不就是中界频率。对应图上,当待测信号频率小于中界频率时,测周法误差更小,选用测周法更合适,当待测信号频率大于中界频率时,测频法误差更小,选用测频法更合适。 -
测频法应用:
之前的对射式红外传感器计次、定时器外部时钟代码稍加改进就是测频法。比如对射式红外传感器计次,每来一个上升沿计次+1,再用一个定时器,定一个1s的定时中断,在中断里,每隔1s取一下计次值,同时清0计次,为下一次做准备,这样每次读取的计次值就直接是频率(闸门时间T是1s,计次值是N,所以频率=计次值N/1=N)。对应定时器外部时钟代码也是如此,每隔1s取一下计次,就能实现测频法测量频率的功能了。
1.3 输入捕获通道
-
异或门
TIMx_CH1引脚进来,有一个三输入的异或门,异或门的执行逻辑是当三个输入引脚的任何一个电平翻转时,输出引脚就产生一次电平翻转,之后输出通过数据选择器,到达输入捕获通道1。数据选择器如果选择上面一个,那输入捕获通道1的输入就是3个引脚的异或值;如果选择下面一个,那异或门就没有用,4个通道各用各的引脚。
设计这个异或门其实还是为三相无刷电机服务的,无刷电机有3个霍尔传感器检测转子的位置,可以根据转子的位置进行换相,有了这个异或门,就可以在前三个通道上接上无刷电机的霍尔传感器,然后这个定时器就作为无刷电机的接口定时器,去驱动换相电路工作。 -
输入滤波器和边沿检测器
然后信号到达输入滤波器和边沿检测器,输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发。
边沿检测器就和外部中断那里一样了,可以选择高电平触发或低电平触发,当出现指定的电平时,边沿检测电路就会触发后续电路执行动作。另外这里,它其实是设计了两套滤波和边沿检测电路。第一套电路,经过滤波和极性选择,得到TI1FP1,输入给通道1的后续电路;第二套电路,经过另一个滤波和极性选择,得到TI1FP2,输入给下面通道2的后续电路。同理,下面TI2信号进来,也经过两套滤波和极性选择,得到TI2FP1和TI2FP2,其中TI2FP1输入给上面,TI2FP2输入给下面。在这里两个信号进来,可以选择各走各的,也可以选择进行一个交叉,让CH2引脚输入给通道1,或者CH1引脚输入给通道2。这里进行交叉的原因,第一个目的是可以灵活切换后续捕获电路的输入;第二个也就是主要目的,就是可以把一个引脚的输入,同时映射到两个捕获单元,这也是PWMI模式的经典结构。第一个捕获通道使用上升沿触发,用来捕获周期,第二个通道使用下降沿触发,用来捕获占空比,两个通道同时对一个引脚进行捕获,就可以同时测量频率和占空比。
一个通道灵活切换两个引脚,和两个通道同时捕获一个引脚,这就是交叉一下的作用和目的。下面的通道3和通道4也是一样的结构。下面的TRC信号也可以作为捕获部分的输入,TRC是来源于其他定时器和CH1引脚的时钟选择(外部时钟模式1的部分输入选择),该设计也是为了无刷电机的驱动。 -
预分频器
输入信号经过滤波和极性选择后,就来到了预分频器。预分频器是每个通道各有一个,可以选择对前面的信号进行分频。 -
捕获/比较寄存器
分频之后的触发信号就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值就会向CCR转运一次,转运的同时会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断,如果需要在捕获的瞬间,处理一些事情的话,就可以开启这个捕获中断。这就是整个电路的工作流程。
测周法:比如我们可以配置上升沿触发捕获,每来一个上升沿,CNT转运到CCR一次,又因为这个CNT计数器是由内部的标准时钟驱动的,所以CNT的数值,其实就可以用来记录两个上升沿之间的时间间隔,这个时间间隔就是周期,再取个倒数,就是测周法测量的频率了。
- 在频率测量图中的测周法的实现
就是:上升沿用于触发输入捕获,CNT用于计数计时,每来一个上升沿,取一下CNT的值,自动存在CCR里,CCR捕获到的值,就是计数值N,CNT的驱动时钟就是fc,fc/N就得到了待测信号的频率。每次捕获之后都要把CNT清0,这样下次上升沿再捕获的时候,取出的CNT才是两个上升沿的时间间隔,这个在一次捕获后自动将CNT清零的步骤,可以用主从触发模式自动来完成。
- 引脚进来先通过一个滤波器,滤波器的输入是TI1,就是CH1的引脚,输出的TI1F,是滤波后的信号;fDTS是滤波器的采样时钟来源;CCMR1寄存器里的ICF位可以控制滤波器的参数;
- 滤波器工作原理:以采样频率对输入信号进行采样,当连续N个值都为高电平,输出才为高电平,连续N个值都为低电平,输出才为低电平。如果信号出现抖动,导致连续采样N个值不全都一样,则输出不会变化,就可以达到滤波的效果。采样频率越低,采样个数N越大,滤波效果就越好。
- 滤波后的信号通过边沿检测器捕获上升沿或下降沿;用CCER寄存器里的CC1P位,可以选择极性,最终得到TI1FP1触发信号,通过数据选择器,进入通道1后续的捕获电路。CC1S位可以对数据选择器进行选择,ICPS位可以配置分频器,可以选择不分频、2分频、4分频、8分频;CC1E位,控制输出使能或失能。如果使能了输出,输入端产生指定边沿信号,经过层层电路,达到输出,就可以让CNT值,转运到CCR里,每捕获一次CNT的值,都要把CNT清零一下,以便于下一次的捕获。在这里硬件电路就可以在捕获之后自动完成CNT的清零工作。
- 如何自动清零CNT:TI1FP1信号和TI1的边沿信号都可以通向从模式控制器,从模式里有电路可以自动完成CNT的清零。
1.4 主从触发模式
主模式、从模式、触发源选择的简称
- 主模式:可以将定时器内部的信号,映射到TRGO引脚,用于触发别的外设。
- 从模式:接收其他外设或者自身外设的一些信号,用于控制自身定时器的运行,也就是被别的信号控制。
- 触发源选择:就是选择从模式的触发信号源的。选择指定的一个信号,得到TRGI。TRGI来触发从模式,从模式在列表中选择一项操作来自动执行。
使TI1FP1信号自动触发CNT清零:触发源选择TI1FP1,从模式选择执行Reset操作。这样TI1FP1的信号就可以自动触发从模式,从模式自动清零CNT。
1.5 输入捕获基本结构
该图只触发了一个通道,因此只能测量频率。
时基单元配置好,启动定时器,CNT在预分频之后的时钟驱动下,不断自增。CNT就是测周法用来计数计时的。经过预分频之后的时钟频率就是驱动CNT的标准频率fc,标准频率fc=72M/预分频系数。
输入捕获通道1的GPIO口,输入一个方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输入选择直连的通道,分频器选择不分频。当TI1FP1转为上升沿之后,CNT的当前计数值转运到CCR1里。同时触发源选择,选中TI1FP1为触发信号,从模式选择复位操作。这样TI1FP1的上升沿也会通过上面一路(触发源选择-从模式)去触发CNT清零。先转运CNT的值到CCR里,再触发从模式给CNT清零。或者是非阻塞的同时转移,CNT的值转移到CCR,同时0转移到CNT里。
- 左上角的图:
信号出现一个上升沿,CCR1=CNT,就是把CNT的值转运到CCR1里面去,这是输入捕获自动执行的;然后CNT=0,清零计数器,这是从模式自动执行的。然后在一个周期之内,CNT在标准时钟的驱动下,不断自增,并且由于之前清零过了,所以CNT就是从上升沿开始,从0开始计数,一直++,直到下一次上升沿来临,然后执行相同的操作,CCR1=CNT,CNT=0。第二次捕获的时候,CNT就是从第一个上升沿到第二个上升沿的计数值,这个计数值就自动放在CCR1中,然后下一个周期,继续同样的过程,CNT从0开始自增,直到下一个上升沿,这时CCR1刷新为第二个周期的计数值,然后不断重复这个过程。所以当这个电路工作时,CCR1的值始终保持为最新一个周期的计数值,这里的CCR1的值就是N,然后fc/N就是信号的频率。所以当我们想要读取信号的频率时,只需要读取CCR1得到N,再计算fc/N,就行了。当不需要读取的时候,整个电路全自动的测量,不需要占用任何软件资源。
注意事项:CNT的值是有上限的,ARR一般设置为最大65535,则CNT最大也只能计65535个数,如果信号频率太低,CNT计数值可能会溢出。还有就是从模式的触发源选择,只有TI1FP1和TI2FP2,没有TI3和TI4的信号,所以如果想使用从模式自动清零CNT,就只能用通道1和通道2,对于通道3和通道4就只能开启捕获中断,在中断里手动清零了。
1.6 PWMI基本结构
PWMI模式使用两个通道同时捕获一个引脚,可以同时测量周期和占空比。
TI1FP1配置上升沿触发,触发捕获和清零CNT,正常地捕获周期。
TI1FP2配置下降沿触发,通过交叉通道,去触发通道2的捕获单元。
左上角图:最开始上升沿,CCR1捕获,同时清零CNT,之后CNT一直++,然后在下降沿这个时刻,触发CCR2捕获,所以这时CCR2的值就是CNT从第一个上升沿到第一个下降沿的计数值,就是高电平期间的计数值,CCR2捕获,并不触发CNT清零,所以CNT继续++,直到下一次上升沿,CCR1捕获周期,CNT清零。
这样执行之后,CCR2是高电平期间的CNT值,CCR1是一整个周期的计数值。
占空比=CCR2/CCR1,这就是PWMI模式,使用两个通道来捕获频率和占空比的思路。可以两个通道同时捕获第一个引脚的输入,这样通道2的前面一部分就没有用到(滤波器、边沿检测和极性选择),也可以配置两个通道同时捕获第二个引脚的输入,这样就是使用TI2FP1和TI2FP2这两个引脚了。
2. 输入捕获库函数及代码
2.1 输入捕获库函数
// 单独写入PSC的函数
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
// 用结构体配置输入捕获单元的函数,4个通道共用一个函数。
// 该函数是单一的配置一个通道
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
// PWMI模式,可以快速配置两个通道
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
// 给输入捕获结构体赋一个初始值
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
// 选择输入触发源TRGI,从模式的触发源选择
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
// 选择主模式输出的触发源TRGO
void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
// 选择从模式
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);
// 分别单独配置通道1、2、3、4的分频器
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC);
// 分别读取4个通道的CCR,和TIM_SetCompare是对应的,读写的都是CCR寄存器
// 输出比较模式下,CCR是只写的,要用SetCompare写入,
// 输入捕获模式下,CCR是只读的,要用GetCapture读出,
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx);
uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx);
2.2 6-6 输入捕获模式测频率
2.2.1 硬件连接
实现功能:测周法测频率。先用PWM模块,在PA0引脚输出一个频率和占空比可调的PWM波形。测量波形的输入口是PA6,PA0通过导线输入到PA6引脚,这样就能测量自己PWM模块产生波形的频率了。PA6是TIM3的通道1,通道1通过输入捕获模块,测量得到频率。在主循环里,通过不断修改PWM波形的PSC和CCR值来更改频率,并在OLED上输出频率值。
2.2.2 硬件运行结果
2.2.3 代码流程
-
配置PWM-------在PA0引脚输出一个频率和占空比可调的PWM波形
- RCC开启时钟,GPIO和TIM2时钟
- 配置GPIO,为复用推挽输出
- 配置时基单元,为TIM2时钟,内部时钟源。设置ARR = 100 - 1
- 配置输出比较单元
- 启动定时器--------以上是PWM初始化部分
- 调用TIM_SetCompare1函数设置CCR
- 调用TIM_PrescalerConfig函数设置PSC。
-
输入捕获单元IC-------PA6通过测量PWM波形的频率
- RCC开启时钟,把GPIO和TIM3的时钟打开。因为是PA6引脚所以是TIM3_CH1。
- GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入或浮空输入模式
- 配置时基单元,让CNT计数器在内部时钟的驱动下自增运行。PSC = 72 - 1;ARR = 65536 - 1,设置为最大。
- 配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器等参数,用结构体实现
- 选择从模式的触发源,触发源选择为TI1FP1,调用一个库函数,给一个参数实现
- 选择触发之后执行的操作,执行Reset操作,调用库函数实现
- 以上都配置好之后,调用TIM_Cmd函数,开启定时器
当需要读取最新一个周期的频率时,直接读取CCR寄存器,按照fc/N计算
-
获取频率
- 由于PSC = 72 - 1;因此测周法的标准频率 fc = 72M / (PSC + 1) = 1MHz,N =CCR的值,通过TIM_GetCapture1函数获取。频率 fx = fc / N。
-
main
- 通过更改PWM波形的PSC和CCR的值,来不断测频率
- 若设PWM的PSC = 720 - 1, CCR = 50。由于ARR = 100 - 1,所以Freq = 72M / (PSC + 1)(ARR + 1) = 1000Hz, Duty = CCR / (ARR + 1) = 50%
- 测周法:fc=72M/(PSC+1),Freq=fc / N = fc / CCR
2.2.4 代码
- PWM.c
#include "stm32f10x.h" // Device header
void