STM32–TIM输入捕获
一、输入捕获简介
1. IC(Input Capture)的定义与作用
“IC(Input Capture)输入捕获”,全称是 “Input Capture”,直译为输入捕获。在输入捕获模式下,当通道输入引脚出现指定电平跳变时(上升沿或者下降沿),当前定时器计数器(CNT)的值将被锁存到捕获 / 比较寄存器(CCR)中。这一功能使得它能够测量多种参数,比如 PWM 波形的频率、占空比、脉冲间隔(频率)、电平持续时间等。
2. 输入捕获通道数量
每个高级定时器和通用定时器都拥有 4 个输入捕获通道。这为用户提供了更多的测量选择和灵活性,可以同时对多个外部信号进行捕获和分析。其中4个输入捕获和输出比较通道,共用4个CCR寄存器,另外它们的CH1到CH4,4个通道的引脚,也是共用的,所以对于同一个定时器,输入捕获和输出比较只能使用其中一个,不能同时使用。输入捕获电路,通用定时器和高级定时器没有区别,都是一样的,然后基本定时器,没有输入捕获的功能
3. PWMI 模式及优势
可配置为 PWMI (PWM输入模式)模式,在这种模式下能够同时测量频率和占空比。这一特性在实际应用中非常有用,例如在电机控制中,可以同时监测电机的转速和控制信号的占空比,以便更好地调整电机的运行状态。
4. 主从触发模式与硬件全自动测量
可配合主从触发模式,实现硬件全自动测量。主从触发模式可以使定时器之间相互协作,通过设置合适的触发源和触发方式,可以实现自动化的测量过程,减少了软件干预的需求,提高了测量的效率和准确性。
5. 输入捕获和输出比较的比较
输出比较,引脚是输出端口,输入捕获,引脚是输入端口;输出比较,是根据CNT和CCR的大小关系来执行输出动作,输入捕获,是接收到输入信号,执行CNT锁存到CCR的动作。
二、频率测量
那既然要测量PWM信号的频率等参数,我们还是有必要先了解一下频率测量的相关知识。
这里有一个频率逐渐降低的方波波形,越往左,频率越高,越往右,频率越低,这里信号都是只有高低电平的数字信号,对于STM32测频率而言,它也是只能测量数字信号的,如果你需要测量一个正弦波,那还需要搭建一个信号预处理电路,最简单的就是用运放搭一个比较器,把正弦波转换为数字信号,再输入给STM32就行了。
如果你测量的信号电压非常高,那还要考虑一下隔离的问题,比如用一些隔离放大器、电压互感器等元件,隔离高压端和低压端,保证电路的安全。
1. 测量频率的方法:
测频法
原理:测频法是在闸门时间T内,对输入信号的上升沿进行计次来测量频率的方法。若计次得到的数值为N,则待测信号的频率fx=N/T。这里的闸门时间T是一个预先设定的时间段,在此期间对上升沿进行计数。 例如,如果闸门时间T设定为1秒,在这1秒内对一个信号的上升沿计次得到N=50,则该信号的频率fa=50/1=50Hz。
适用情况:则频法适用于测量较高频率的信号。对于高频信号,在相对较短的时间内会有较多的上升沿出现。通过设置合适的闸门时间,可以在这个时间段内获得足够多的上升沿计数,从而提高测量的佳确性。测频法测量结果更新的慢一些,数值相对稳定,测频法测量的是在闸门时间内的多个周期,所以它自带一个均值滤波,如果在闸门时间内波形频率有变化,那得到的其实是这一段时间的平均频率,所以测频法结果更新慢,测量结果是一段时间的平均值,值比较平滑。
测周法
原理:测周法是基于在两个上升沿内,以标准频率fc进行计次来测量待测信号频率的方法。核心公式推导:测周法基于两个上升沿内,以标准频率fc计次来确定待测信号频率。设标准频率计次得到的数值为N,从原理上来说,待测信号周期Tx等于标准频率fc的N个周期,即Tx=N/fc。根据频率和周期的倒数关系,可得= fc/N。待测信号的频率fx=fc/N。 这里利用了一个已知的标准频率信号,在待测信号的两个上升沿之间对标准频率信号进行计次操作。例如,如果标准频率fc=100kHz,在待测信号两个上升沿之间对标准频率信号计次得到IV=1000,那么待测信号的频率fx = 100000/1000 = 100Hz。
适用情况:
测周法适用于测量较低频率的信号。因为对于低频信号,两个上升沿之间的时间间隔较长,利用标准频率信号在这个较长的时间间隔内进行计次,可以更精确地测量低频信号的频率。
测周法更新的快,数据跳变也非常快,它只测量一次结果周期,就能出一次结果,所以出结果的速度取决于待测信号的频率T,一般而言,待测信号都是几百几千Hz,所以一般情况下,测周法结果更新更快,但是由于它只测量一个周期,所以结果值会受噪声的影响,波动比较大
中界频率
定义
中界频率是测频法与测周法误差相等的频率点,其计算公式为。其中是标准频率,是闸门 时间。
意义及应用
在实际频率测量中,中界频率起到了指导选择测量方法的作用。当待测信号的频率高于中界频率时,采用测频法更为准确;当待测信号的频率低于中界频率时,采用测周法更为准确。
2. 测周法的在电路中的实现
一、整体结构与信号路径
这个电路以定时器(TIMx)为核心,包含多个输入通道(TIMx_CH1 – TIMx_CH4)。每个通道都有对应的信号处理模块,如异或门(当三个输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转)、边沿检测器、输入滤波器、预分频器等。
二、测周法实现步骤
1. 测周法的实现
配置上升沿触发捕获,每来一个上升沿,CNT转运到CCR一次,又因为这个CNT计数器是由内部的标准时钟驱动的,所以CNT的数值,其实就可以用来记录两个上升沿之间的时间间隔,这个时间间隔,就是周期,再取个倒数,就是测周法测量的频率了,
三、输入捕获的电路结构
1. 输入捕获通道的粗略解释
-
异或门与数据选择器
-
最左边是四个通道的引脚,待测信号通过霍尔传感器等外部设备接入到 TIMx 的输入通道(如 TIMx_CH1),进来后有一个三输入的异或门,异或门的输入连接了通道 1、2、3 端口,输出引脚产生一次电平翻转之后输出通过数据选择器到达输入捕获通道。
-
数据选择器如果选择上面一个,输入捕获通道一的输入就是三个引脚的异或值;如果选择下面一个,异或门就没有用,四个通道各用各的引脚。这个设计主要是为三相无刷电机服务,定时器可作为无刷电机的接口,驱动幻象电路工作。
-
输入滤波器和边缘检测器
-
输入信号经过输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发。边缘检测器与外部中断那里类似,可以选择高电平触发或者低电平触发,当出现指定的电平时,边缘检测电路就会触发后续电路执行动作。
-
这里设计了两套滤波和边缘检测电路,第一套电路经过滤波和极性选择,得到 TI1FP1 输入给通道一的后续电路;第二套电路经过另一个滤波和极性选择,得到 TI1FP2 输给下面通道二的后续电路。同理,TI2 信号进来也经过两套滤波和极性选择,得到 TI2FP1 和 TI2FP2。
-
下面的TRC信号也可以选择作为捕获部分的输入。
-
交叉连接的作用和目的
-
两个信号进来可以选择各走各的,也可以选择进行交叉。这样做的目的主要有两个:一是可以灵活切换后续捕获电路的输入,一个通道灵活切换两个引脚;二是可以把一个引脚的输入同时映射到两个捕获单元,这也是 PWM 模式的经典结构。第一个捕获通道使用上升沿触发用来捕获周期,第二个通道使用下降沿触发用来捕获占空比,两个通道同时对一个硬件进行捕获,就可以同时测量频率和占空比。
-
预分频器与捕获电路
-
输入信号进行滤波和极性选择后就来到了预分频器,预分频器,每个通道各有一个,可以选择对前面的信号分频,分频之后的触发信号就可以触发捕获电路进行工作。每来一个触发信号,CNT 的值就会向 CCR 转运一次,转运的同时会发生一个捕获事件,这个事件会在状态计算器标注位,同时也可以产生中断。如果需要在捕获的瞬间处理一些事情,可以开启这个捕获中断。
2. 输入捕获通道一的详细框图
-
滤波器工作原理
-
引脚进来先经过一个滤波器,滤波器的输入是 TI1,滤波后的信号是 TI1F。FDTs 是滤波器的采样时钟来源,CCM21 计算器里的 ICF 位可以控制滤波器的参数。
-
数字滤波器是由一个事件计数器组成,它记录到 N 个事件后,会产生一个输出的跳变。简单理解这个滤波器工作原理就是以采样频率对输入信号进行采样,当连续 N 个值都为高电平,输出才为高电平;连续 N 个值都为低电平,输出才为低电平。如果信号出现高频抖动,导致连续采样 N 个值不全都一样,那输出就不会变化,这样就可以达到滤波的效果。采样频率越低,采样个数 N 越大,滤波效果就越好。
-
边缘检测器与触发信号
-
滤波之后的信号通过边缘检测器捕获上升沿或者下降沿,用 CCMR1 计算器里的 CC1P 位就可以选择极性。最终得到 TI1FP1 触发信号,通过数据选择器进入通道一后续的捕获电路。
-
当然这里实际应该还有一套一样的电路,得到TI1FP2触发信号,连通到通道2的后续电路,这里并没有画出来,同样,通道2有TI2FP1,连通到通道1的后续,通道2也还有TI2FP2,连通到通道2的后续,总共四种连接方式
-
数据选择器与分频器
-
经过数据选择器进入后续捕获部分电路,CCER 位可以对数据选择进行控制。选择之后,SAPS 可以配置这里的分频器,可以选择不分频、二分频、四分频、八分频。最后 CC1E 为控制输出使能或失能,如果输入端产生指定边缘信号,经过层层电路,到达这里就可以让这里 CNT 的值转运到 CCR 里面来。
-
自动清零 CNT
-
TI1FP1 信号和 TI1 的边沿信号(TI1_ED)都可以通向重模式控制器,比如 TI1FP1 信号的上升沿触发捕获,那通过这里 TI1FP1 还可以同时触发重模式,这个从模式里面就有电路可以自动完成 CNT 的清零工作。
3. 输入捕获通道的注意事项
-
CNT 的值有上限,一般设置为最大 65535,那 CNT 最大也只能记 65535 个数。如果信号频率太低,CNT 计数值可能会溢出。
-
重模式的触发源选择只有 TI1FP1 和 TI2FP2,没有 TI3 和 TI 4的信号。所以如果想使用重模式自动清理 CNT 就只能用通道一和通道二,对于通道三和通道四就只能开启捕获中断,在中断里手动清理,不过这样程序就会处于频繁中断的状态,比较消耗软件资源。
四、主从触发模式
-
主从触发模式的组成
-
主从触发模式是主模式、从模式和触发源选择这三个功能的简称。主模式可以将定时器内部的信号映射到 TRGO 引脚,用于触发别的外设;从模式就是接收其他外设或者自身外设的一些信号,用于控制自身定时器的运行,也就是被别的信号控制;触发源选择就是选择从模式的触发信号源。
-
触发源的选择:选择指定的一个信号,得到TRGI,TRGI去触发从模式,从模式可以在列表里,选择一项操作来自动执行,
-
主从触发模式的用途
-
如果想让 TI1FP1 信号自动触发 CNT 清零,可以将触发源选择选中这里的 TI1FP1,从模式执行的操作选择执行 reset 的操作,这样 TI1FP1 的信号就可以自动触发重模式,重模式自动清零 CNT,实现硬件全自动测量。
五、输入捕获的基本结构
一、整体结构概述
输入捕获基本结构主要由时基单元、触发源选择、输入捕获单元等部分组成。其中时基单元包括自动重装器(ARR)和预分频器(PSC)以及计数单元(CNT)。输入捕获单元主要负责对外部输入信号进行捕获和处理。在这个结构中由于只使用了一个通道,所以只能测量频率。
二、时基单元
-
自动重装器(ARR):决定了计数器(CNT)的计数上限。当 CNT 达到 ARR 的值时,会根据设置进行相应的操作,比如重新从 0 开始计数。
-
预分频器(PSC):对输入时钟进行分频,以控制 CNT 的计数速度。通过调整 PSC 的值,可以改变计数频率,从而适应不同的输入信号频率范围。
-
计数 CNT:就会在预分频之后的这个时钟驱动下,不断进行累加计数,其值会在特定条件下被捕获到捕获 / 比较寄存器(CCR1)中。当接收到特定的触发信号时,CNT 可能会被重置为 0,比如从模式下的 Reset 信号触发。这里的CNT是测周法用于计数计时的东西。经过预分频之后这个位置的时钟频率,就是驱动CNT的标准频率fc(72M/预分频系数)。
三、输入捕获单元 1
-
GPIO、滤波、边沿检测和极性选择:外部输入方波信号首先通过 GPIO 引脚进入系统。滤波部分可以对输入信号进行滤波处理,去除高频噪声和干扰。边沿检测用于检测输入信号的上升沿或下降沿等特定边沿。极性选择则可以根据需要选择对特定极性的边沿进行响应。
-
TI1FP1:经过滤波、边沿检测和极性选择后的信号被标记为 TI1FP1,选择TI1FP1为上升沿触发。这个信号可以作为触发源,用于触发后续的捕获操作。
-
分频和捕获 / 比较器(CCR1):TI1FP1 信号输入选择直连的通道,分频器选择不分频,然后输入到捕获 / 比较器中。当满足特定条件时,检测到上升边沿,此时 CNT 的值会被捕获到 CCR1 中。由于目前该结构只能测量频率,可能是通过多次捕获 CNT 的值,并根据时间间隔来计算输入信号的频率。
四、触发源选择
触发源选择部分允许用户选择不同的触发信号来控制 CNT 的操作。CNT中的值被转运到CCR中之后选择外部输入信号经过处理后的信号( TI1FP1)作为触发源,触发 CNT 的重置操作。这样可以实现对输入信号的特定响应,以便进行精确的测量和控制。
五、从模式的选择
选择完触发源之后,从模式选择复位操作,这样TI1FP1的上升沿,也会通过上面这一路,去触发CNT清零。但是这里存在先后顺序,肯定得是先转运CNT的值到CCR里去,再触发从模式给CNT清零,或者是非阻塞的同时转移,CNT的值转移到CCR,同时0转移到CNT里面去,总之,肯定不会是先清零,再捕获,要不然捕获值肯定都是0了。
补充:左上角电平变化图详解
第一个上升沿:CCR1=CNT,就是把CNT的值转运到CCR1里面去,这是输入捕获自动执行的,然后CNT=0,清零计数器,这是从模式自动执行的。
CNT++:CNT在标准时钟的驱动下,不断自增,并且由于之前清零过,所以CNT就是从上升沿开始,从0开始计数,一直++,直到,下一次上升沿来临。
第二个上升沿:执行相同的操作,注意,第二次捕获的时候,这个CNT是不是就是从第一个上升沿到第二个上升沿的计数值,这个计数值就自动放在CCR1里面。
后面的周期重复相同的过程,在经历CNT++后迎来第三个上升沿,这是CCR中又刷新为第二个周期的计数值,所以CCR1的值,始终保持为最新一个周期的计数值,这个计数值就是频率测量里面的N。
所以我们如果想读取频率,只需要读取CCR1得到N,再计算fc/N,就行了,当我们不想知道时,整个电路全自动的测量,不需要占用任何软件资源。
六、PWMI基本结构
PWMI基本结构 ,使用了两个通道同时捕获一个引脚,可以同时测量周期和占空比。
上部分的结构和输入捕获部分的结构一样,下面部分多了一个通道。
首先,TI1FP1配置上升沿触发,触发捕获和清零CNT,正常捕获周期,这时我们再来一个TI1FP2,配置为下降沿触发,通过交叉通道,去触发通道2的捕获单元,此时参考左上角电平图,最开始上升沿,CCR1捕获,同时清零CNT,之后CNT一直++,然后,在下降沿这个时刻,触发CCR2捕获,所以这时CCR2的值,就是CNT高电平期间的计数值。CCR2捕获,并不触发CNT清零,所以CNT继续++,直到下一次上升沿,CCR1捕获周期,CNT清零,,此时CCR1就是一整个周期的计数值,CCR2就是高电平期间的计数值,用CCR2/CCR1,就是占空比了。
七、输入捕获模式测频率的代码详解
1. 工程建立
直接复制“6-3PWM驱动LED呼吸灯“”的代码,以便生成待测信号。
2. 对PWM模块的改进
目前这个模块的逻辑是初始化 TIM2 的通道 1, 产生一个 PWM 波形,输出引脚是PA0,然后通过下面这个SetCompare1的函数,可以调节CCR1寄存器的值,从而控制PWM的占空比,但是目前PWM的频率,是在初始化里写好了的,是固定的,运行时调节不方便,所以我们在最后再加一个函数,用来便捷地调节PWM频率。
调节PWM频率函数的编写
通过公式,我们知道PWM频率=更新频率=72M/(PSC+1)(ARR+1),所以PSC和ARR都可以调节频率,但是占空比CCR/(ARR+1),所以通过ARR调节频率,还同时会影响到占空比,而通过PSC调节频率,不会影响占空比,显然比较方便,所以固定ARR=100-1,通过调节PSC来改变PWM频率,另外ARR为100-1,CCR的数值直接就是占空比。
配置参数的小技巧:一般我们可以根据分辨率的要求,先确定好ARR,这样PSC决定频率,CCR决定占空比。
初始化后单独修改PSC的函数:
1. 函数的创建:void PWM SetPrescaler(uint16 t Prescaler)
2. 功能的实现:调用库函数里单独写入PSC的函数,在TIM.c中找到函数TIM_PrescalerConfig,就是单独写入PSC的函数,因为这个函数还有一个重装模式的参数,所以它并不叫SetPrescaler,而叫PrescalerConfig。
3. 参数:
-
第一个TIMX,我们使用的是定时器2,所以给TIM2;
-
第二个Prescaler,就是要写入PSC的值,直接把外层函数的定义的uint16 t Prescaler参数传进去;
-
第三个ReloadMode,就是重装模式,根据定义的查找可以知道其可以为以下几个值;第一个Update,预分频器在更新事件重装(写入的值立即生效);第二个lmmediate,预分频器立即重装(写入的值在更新事件生效)。立刻生效,可能会在值改变时产生切断波形的现象,那在更新事件生效,就是会有一个缓存器,延迟参数的写入时间 ,等一个周期结束了,在更新事件时,再统一改变参数,保证每个周期的完整。在这里的代码中由于代码要求不高,所以选择哪个参数都可以。
/**
* 函 数:PWM设置PSC
* 参 数:Prescaler 要写入的PSC的值,范围:0~65535
* 返 回 值:无
* 注意事项:PSC和ARR共同决定频率,此函数仅设置PSC的值,并不直接是频率
* 频率Freq = CK_PSC / (PSC + 1) / (ARR + 1)
*/
void PWM_SetPrescaler(uint16_t Prescaler)
{
TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate); //设置PSC的值
}
这样PWM模块就修改好了,通过SetPrescaler改变频率,通过SetCompare1改变通道1的占空比。
3. 建立输入捕获模块
TIM中需要使用的库函数
TIM_ICInit (TIM_TypeDef TIMx, TIM_ICInitTypeDef TIM_ICInitStruct)
-
功能:用于初始化定时器的输入捕获功能。输入捕获功能可以在输入信号的特定边沿(上升沿、下降沿或双边沿)触发时,记录定时器的当前计数值,从而可以测量输入信号的频率、周期等参数。
-
参数:
-
TIMx
:指向要进行初始化的定时器的指针。 -
TIM_ICInitStruct
:指向一个TIM_ICInitTypeDef
类型的结构体,该结构体包含了要设置的输入捕获通道的参数,如触发边沿、输入滤波器等。 -
作用:常用于测量外部信号的频率、周期等参数,或者作为外部事件的计数器。
-
注意:输入捕获和输出比较都有4个通道,OCInit,4个通道,每个通道单独占一个函数,而lCInit,4个通道是共用一个函数的,在结构体里会额外有一个参数,可以用来选择具体是配置哪个通道,因为可能有交叉通道的配置,所以函数合在一起比较方便,
TIM_PWMIConfig (TIM_TypeDef TIMx, TIM_ICInitTypeDef TIM_ICInitStruct)
-
功能:可能是用于配置定时器的 PWM 输入模式。在 PWM 输入模式下,定时器可以测量外部 PWM 信号的频率和占空比等参数。并且可以快速配置两个通道。
-
参数:
-
TIMx
:指向要进行配置的定时器的指针。 -
TIM_ICInitStruct
:指向一个TIM_ICInitTypeDef
类型的结构体,该结构体可能包含了与 PWM 输入模式相关的参数,如触发边沿、输入滤波器等。 -
作用:用于测量外部 PWM 信号的参数,或者与其他设备进行 PWM 信号的同步。
TIM_ICStructinit(TIM_ICInitTypeDef* TIM_ICInitstruct)
- 功能:TIM_ICStructInit 函数用于初始化 TIM_ICInitTypeDef 类型的结构体。该结构体是用于配置定时器的输入捕获通道相关参数的。
- 参数:它接受一个 TIM_ICInitTypeDef 类型的结构体指针作为参数,即指向要进行初始化的结构体。
-
作用:
设置默认值
简化配置过程
TIM_SelectinputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
-
功能:TIM_SelectInputTrigger 函数用于选择定时器从模式的输入触发源(TRGI)。它决定了定时器在什么条件下开始计数或执行其他相关操作,与输入捕获和定时器的同步等功能密切相关。
- 参数:该函数接受两个参数:
第一个参数是要配置的定时器,例如 TIM2、TIM3 等,它指定了操作的对象是哪个定时器。
第二个参数是触发源的选择,通常是一些预定义的枚举值,例如 TIM_TS_TI1FP1 等。这些值对应着不同的触发源,可能来自定时器的输入捕获通道或者其他相关引脚的信号。
3. 作用:
-
确定计数起始条件
-
通过选择合适的触发源,可以确定定时器从什么时候开始计数。例如,当选择某个与输入捕获通道相关的触发源时,定时器可能会在输入捕获通道检测到特定边沿(如上升沿或下降沿)的信号时开始计数。
-
实现定时器同步
-
在一些复杂的应用中,可能需要多个定时器协同工作或者与外部信号同步。TIM_SelectInputTrigger 函数可以帮助实现这种同步。例如,一个定时器可以根据另一个定时器的输出信号或者外部输入信号作为触发源来启动计数,从而保证它们之间的操作具有一定的时序关系。
-
适配不同应用需求
-
不同的应用场景可能需要不同的触发源。比如在测量输入信号频率时,可能需要根据输入信号本身的特性来选择合适的触发源,以便准确地捕获信号的相关信息。该函数提供了灵活的触发源选择方式,以满足各种应用需求。
TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource)
1. 功能
该函数用于选择定时器(由参数TIMx
指定)的输出触发源(TRGO)。定时器的输出触发可以用于触发其他外设或定时器,实现同步操作。
2. 参数
-
TIM_TypeDef* TIMx
:指向要进行配置的定时器的指针。可以是 STM32 中特定的定时器,如TIM2
、TIM3
等。 -
uint16_t TIM_TRGOSource
:这是一个 16 位的无符号整数,用于指定输出触发源。它是一个枚举类型的值,可能的取值包括但不限于以下几种: -
TIM_TRGOSource_Update
:选择定时器更新事件作为输出触发源,即每当定时器更新(计数器达到自动重装载值并重新开始计数)时,触发输出信号。 -
TIM_TRGOSource_OC1
:选择定时器通道 1 的输出比较事件作为输出触发源。 -
TIM_TRGOSource_OC2
、TIM_TRGOSource_OC3
、TIM_TRGOSource_OC4
等:分别对应定时器的其他输出比较通道的输出比较事件作为触发源。 -
TIM_TRGOSource_Input1
:选择定时器输入通道 1 的输入捕获事件作为输出触发源。
3. 作用
-
实现同步操作:在多个定时器或外设需要同步工作的情况下,通过选择合适的输出触发源,可以确保它们在特定的时间点进行操作。例如,可以使用一个定时器的输出触发来启动另一个定时器的计数,或者触发一个外部设备的特定操作。
-
控制外设:通过将定时器的输出触发连接到其他外设,可以实现对这些外设的精确控制。例如,可以使用定时器的输出触发来控制电机驱动器、DAC(数模转换器)等外设的更新或启动操作。
-
灵活配置:提供了多种不同的输出触发源选择,使得开发者可以根据具体的应用需求进行灵活配置。可以根据系统的要求选择最适合的触发源,以实现高效的同步和控制操作。
TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode)
1. 功能
该函数用于选择指定定时器(由参数TIMx
指定)的从模式。从模式允许定时器在特定的触发条件下进行特定的操作,实现定时器之间的同步或者与外部信号的同步。
2. 参数
-
TIM_TypeDef* TIMx
:指向要进行配置的定时器的指针,比如TIM2
、TIM3
等 STM32 中的定时器。 -
uint16_t TIM_SlaveMode
:这是一个 16 位的无符号整数,用于指定从模式的类型。常见的取值有: -
TIM_SlaveMode_Reset
:在触发信号出现时,将定时器的计数器复位为 0。 -
TIM_SlaveMode_Gated
:定时器的计数仅在触发信号为高电平时进行,触发信号为低电平时,定时器停止计数。 -
TIM_SlaveMode_Trigger
:在触发信号出现时,启动定时器计数。
3. 作用
-
定时器同步:在多个定时器需要同步工作的情况下,可以使用从模式来确保它们在特定的触发条件下同时开始计数或者执行其他操作。例如,可以将一个定时器设置为主定时器,其他定时器设置为从定时器,并选择合适的从模式,使得从定时器在主定时器的特定事件(如更新事件、输出比较事件等)触发下进行同步操作。
-
与外部信号同步:可以将定时器与外部信号进行同步,例如外部脉冲信号或者其他外设的输出信号。通过选择合适的从模式,可以使定时器在外部信号的特定边沿或者特定状态下进行计数或者执行其他操作,从而实现与外部信号的同步。
-
灵活控制定时器:提供了多种不同的从模式选择,使得开发者可以根据具体的应用需求灵活地控制定时器的行为。例如,在需要精确控制定时器计数的开始和停止时间的应用中,可以选择合适的从模式来实现这种控制。
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)
1. 功能
这四个函数分别用于设置特定定时器(由参数TIMx
指定)的不同输入捕获通道(通道 1、通道 2、通道 3 和通道 4)的预分频系数。通过调整预分频系数,可以改变输入捕获对输入信号的采样频率和分辨率,从而更好地适应不同频率的输入信号以及特定的测量需求。
2. 参数解释
-
TIM_TypeDef* TIMx
:指向要进行配置的定时器的指针。在 STM32 中,可以是不同的定时器,如TIM2
、TIM3
等。 -
uint16_t TIM_ICPSC
:预分频系数的值。这是一个 16 位无符号整数,可以取不同的值来确定输入捕获的预分频比例。例如,可能的取值有TIM_ICPSC_DIV1
(不分频)、TIM_ICPSC_DIV2
(2 分频)、TIM_ICPSC_DIV4
等。
3. 各函数具体作用
-
void TIM_SetIC1Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
: -
专门设置定时器
TIMx
的输入捕获通道 1 的预分频系数。 -
可以根据输入信号的频率和测量精度要求,选择合适的预分频系数。如果输入信号频率较高,可以选择较大的预分频系数来降低采样频率,防止定时器溢出;如果需要更高的测量精度,可以选择较小的预分频系数以提高采样频率。
-
void TIM_SetIC2Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
: -
用于设置定时器
TIMx
的输入捕获通道 2 的预分频系数。 -
与通道 1 的作用类似,但针对的是不同的输入捕获通道。根据连接到该通道的输入信号的特性进行预分频系数的调整。
-
void TIM_SetIC3Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
: -
设置定时器
TIMx
的输入捕获通道 3 的预分频系数。 -
同样,根据输入信号的情况选择合适的预分频系数,以确保准确地捕获输入信号的特征。
-
void TIM_SetIC4Prescaler(TIM_TypeDef* TIMx, uint16_t TIM_ICPSC)
: -
对定时器
TIMx
的输入捕获通道 4 进行预分频系数的设置。 -
为连接到该通道的输入信号提供合适的采样频率和分辨率。
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)
一、功能
这些函数用于获取特定定时器(由参数TIMx
指定)的不同输入捕获通道(通道 1、通道 2、通道 3 和通道 4)的当前捕获值(CCR)。输入捕获功能通常用于测量外部信号的频率、周期等参数,通过读取捕获值可以确定输入信号在特定时刻的状态。
二、参数解释
-
TIM_TypeDef* TIMx
:指向要查询的定时器的指针。在 STM32 微控制器中,可以是不同的定时器实例,如TIM2
、TIM3
等。
三、各函数具体作用
-
uint16_t TIM_GetCapture1(TIM_TypeDef* TIMx)
: -
获取定时器
TIMx
的输入捕获通道 1 的当前捕获值。 -
这个捕获值通常是定时器计数器在输入信号的特定边沿(如上升沿或下降沿)被触发时的计数值。通过连续读取这个值,可以计算输入信号的周期、频率等参数。
-
在视频中,可能会展示如何使用这个函数来测量外部信号的频率。例如,在两次输入捕获事件之间,记录下通道 1 的捕获值的变化,然后根据定时器的时钟频率和捕获值的差值来计算输入信号的周期和频率。
-
uint16_t TIM_GetCapture2(TIM_TypeDef* TIMx)
: -
功能与
TIM_GetCapture1
类似,但针对定时器TIMx
的输入捕获通道 2。 -
可以用于同时测量多个不同的输入信号,或者在需要对不同输入信号进行比较和分析的情况下使用。
-
uint16_t TIM_GetCapture3(TIM_TypeDef* TIMx)
和uint16_t TIM_GetCapture4(TIM_TypeDef* TIMx)
: -
分别获取定时器
TIMx
的输入捕获通道 3 和通道 4 的当前捕获值。 -
提供了更多的输入捕获通道选择,以满足复杂应用场景的需求。例如,可以同时测量多个不同频率的输入信号,或者对多个输入信号进行同步测量。
其中TIM_GetCapture和TIM_SetICPrescaler是相对应的,读写的都是CCR寄存器,输出比较模式下,CCR是只写的,要用SetCompare写入;输入捕获模式下,CCR是只读的,要用GetCapture读出
输入捕获初始化函数(void IC_Init(void))
1. RCC开启时钟,把GPIO和TIM的时钟打开:
这两行代码分别开启了 TIM3 定时器和 GPIOA 的时钟,使后续可以对这些模块进行配置和操作。
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
代码中需要利用TIM2开启时钟,所以这里输入捕获定时器改成TIM3,TIM3也是APB1的外设,所以函数还是APB1的这个。
用PA6的通道1,所以开启GPIOA的时钟。
2. GPIO 初始化:
定义一个GPIO_InitTypeDef
类型的结构体GPIO_InitStructure
,用于配置 GPIO 引脚。
设置引脚模式为上拉输入(GPIO_Mode_IPU
),这样在没有外部输入信号时,引脚处于高电平状态。
指定要配置的引脚为GPIO_Pin_6
,即 GPIOA 的 PA6 引脚。
设置引脚的速度为 50MHz,这是一个较高的速度设置,适用于快速输入信号。
使用GPIO_Init
函数将配置应用到 GPIOA 端口。
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA6引脚初始化为上拉输入
3. 时钟源配置:
这行代码选择 TIM3 的时钟源为内部时钟。如果不调用这个函数,TIM3 默认也使用内部时钟,这里明确指定以确保配置的准确性。
4. 时基单元初始化:
配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
定义一个TIM_TimeBaseInitTypeDef
类型的结构体TIM_TimeBaseInitStructure
,用于配置 TIM3 的时基单元。
设置时钟分频为不分频(TIM_CKD_DIV1
),这个参数主要用于配置滤波器时钟,对时基单元的基本功能影响较小。
选择计数器模式为向上计数(TIM_CounterMode_Up
),定时器从 0 开始计数,直到达到计数周期后重新从 0 开始计数。
设置计数周期为 65536 – 1,这个值决定了定时器的最大计数值,设置大一些防止计数溢出。
设置预分频器为 72 – 1,用于调整定时器的计数频率,这个值决定了测周法的标准频率fc,72M/预分频,就是计数器自增的频率,就是计数标准频率,暂时先给72-1,这样标准频率就是72M/72=1MHz。
由于这里使用的可能不是高级定时器,所以将重复计数器设置为 0。
使用TIM_TimeBaseInit
函数将配置应用到 TIM3 的时基单元。
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM3的时基单元
5. 输入捕获初始化:
定义一个TIM_ICInitTypeDef
类型的结构体TIM_ICInitStructure
,用于配置 TIM3 的输入捕获通道。(包括滤波器、极性、直连通道还是交叉通道、分频器这些参数)
选择要配置的通道为定时器通道 1(TIM_Channel_1
)。
设置输入滤波器参数为 0xF,这个参数可以过滤输入信号的抖动,提高捕获的准确性,数越大滤波效果越好。(注意滤波器不会改变信号频率)
设置触发极性为上升沿触发捕获(TIM_ICPolarity_Rising
),即当输入信号出现上升沿时触发捕获操作。
设置捕获预分频为不分频(TIM_ICPSC_DIV1
),这样每次输入信号变化都可以触发捕获,不分频就是每次触发都有效,2分频就是每隔一次有效一次,以此类推,这里选择每次触发都有效。(对信号本身进行计次会改变频率)
设置输入信号交叉为直通(TIM_ICSelection_DirectTI
),即输入信号不进行交叉处理。
使用TIM_ICInit
函数将配置应用到 TIM3 的输入捕获通道。
/*输入捕获初始化*/
TIM_ICInitTypeDef TIM_ICInitStructure; //定义结构体变量
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择配置定时器通道1
TIM_ICInitStructure.TIM_ICFilter = 0xF; //输入滤波器参数,可以过滤信号抖动
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //极性,选择为上升沿触发捕获
TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //捕获预分频,选择不分频,每次信号都触发捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //输入信号交叉,选择直通,不交叉
TIM_ICInit(TIM3, &TIM_ICInitStructure); //将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
6. 选择触发源及从模式:
触发源选择为TI1FP1,这里调用一个库函数,给一个参数就行了
使用TIM_SelectInputTrigger
函数选择 TIM3 的触发源为TIM_TS_TI1FP1
,这是一个特定的触发源,可以根据实际需求进行调整。(第二个参数从八个可选触发源中选择)
使用TIM_SelectSlaveMode
函数选择 TIM3 的从模式为复位模式(TIM_SlaveMode_Reset
),当指定的触发源产生上升沿时,会触发 TIM3 的计数器归零。
/*选择触发源及从模式*/
TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); //触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); //从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
7. TIM 使能,开启定时器:
这行代码使能 TIM3,让定时器开始运行,开始对输入信号进行捕获和计数。
/*TIM使能*/
TIM_Cmd(TIM3, ENABLE); //使能TIM3,定时器开始运行
当启动定时器之后CNT就会在内部时钟的驱动下,不断自增。即使没有信号过来也会不断自增,但是如果有信号过来就会在从模式的作用下自动清零,并不会影响测量
当我们需要读取最新一个周期的频率时,直接读取CCR寄存器,然后按照fc/N,计算一下就行了。
为了方便计算所以再写一个函数:
获取输入捕获频率的函数(uint32_t IC_GetFreq(void)):
定义一个函数IC_GetFreq
,用于获取输入捕获的频率。
通过调用TIM_GetCapture1(TIM3)
获取 TIM3 通道 1 的捕获值。
使用测周法计算频率,公式为fx = fc / N
,其中fc
是已知的时钟频率(这里假设为 1000000Hz),N
是捕获值。这里对捕获值进行加 1 操作是为了避免当捕获值为 0 时出现除零错误,但在实际应用中如果确定捕获值不会为 0,也可以不执行加 1 的操作。
目前我们这个函数返回的是最新一个周期的频率值,单位是Hz
/**
* 函 数:获取输入捕获的频率
* 参 数:无
* 返 回 值:捕获得到的频率
*/
uint32_t IC_GetFreq(void)
{
return 1000000 / (TIM_GetCapture1(TIM3) + 1); //测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
4. 主函数代码详解
1. 头文件包含
stm32f10x.h
是 STM32F10x 系列微控制器的标准头文件,包含了所有与芯片相关的寄存器定义、结构体声明以及宏定义等,是访问芯片硬件资源的基础。
Delay.h
可能是一个自定义的延时函数头文件,用于在程序中实现延时操作。
OLED.h
是与 OLED 显示屏相关的头文件,包含了对 OLED 显示屏进行初始化和操作的函数声明。
PWM.h
是与 PWM(脉冲宽度调制)功能相关的头文件,包含了 PWM 初始化和相关设置函数的声明。
IC.h
是与输入捕获功能相关的头文件,包含了输入捕获初始化和相关操作函数的声明。
2. main
函数
2.1 模块初始化
OLED_Init();
:调用 OLED 初始化函数,对 OLED 显示屏进行初始化操作。这可能包括设置 OLED 的显示模式、初始化相关的 GPIO 引脚用于数据传输和控制信号,以及配置显示的参数等。
PWM_Init();
:执行 PWM 模块的初始化。这个过程可能涉及到选择合适的定时器用于 PWM 功能,配置定时器的时钟源、计数模式、预分频系数等参数,以及设置 PWM 通道的相关属性,如极性、输出模式等。
IC_Init();
:对输入捕获模块进行初始化,初始化整个电路。这包括配置用于输入捕获的 GPIO 引脚为输入模式,选择合适的定时器并设置其时钟源、计数模式、预分频系数等,同时配置输入捕获通道的触发边沿、输入选择方式、预分频系数和滤波器等参数,还可能包括开启输入捕获中断以及设置中断优先级。
2.2 显示静态字符串
OLED_ShowString(1, 1, "Freq:00000Hz");
:在 OLED 显示屏的第 1 行第 1 列显示字符串Freq:00000Hz
。这个函数是通过向 OLED 发送相应的字符编码数据,并根据 OLED 的显示规则进行显示。
2.3 设置 PWM 参数(这里的的PWM模块用于将待测信号输出到PA0,PA0又通过导线将信号传给PA6,PA6是TIM3的通道1,通道1通过输入捕获模块,测量得到频率)
PWM_SetPrescaler(720 - 1);
:设置 PWM 的预分频器值。根据公式Freq = 72M / (PSC + 1) / 100
,这里将预分频器设置为720 - 1
,可以计算出相应的 PWM 频率。预分频器的作用是对系统时钟进行分频,以得到合适的定时器计数频率,从而实现所需的 PWM 频率。
PWM_SetCompare1(50);
:设置 PWM 通道 1 的比较值。根据公式Duty = CCR / 100
,这里将比较值设置为 50,用于确定 PWM 的占空比。比较值决定了在一个 PWM 周期内,输出高电平的时间比例,从而实现对脉冲宽度的调制。
2.4 主循环中的操作
while (1)
:进入一个无限循环,不断刷新显示频率。
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
:在 OLED 显示屏的第 1 行第 6 列显示输入捕获模块测得的频率值。IC_GetFreq()
函数用于获取输入捕获模块测量到的频率,OLED_ShowNum
函数则将这个频率值以数字形式显示在 OLED 上,显示的宽度为 5 位数字。
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void)
{
/*模块初始化*/
OLED_Init(); //OLED初始化
PWM_Init(); //PWM初始化
IC_Init(); //输入捕获初始化
/*显示静态字符串*/
OLED_ShowString(1, 1, "Freq:00000Hz"); //1行1列显示字符串Freq:00000Hz
/*使用PWM模块提供输入捕获的测试信号*/
PWM_SetPrescaler(720 - 1); //PWM频率Freq = 72M / (PSC + 1) / 100
PWM_SetCompare1(50); //PWM占空比Duty = CCR / 100
while (1)
{
OLED_ShowNum(1, 6, IC_GetFreq(), 5); //不断刷新显示输入捕获测得的频率
}
}
八、PWMI模式测频率占空比
1. 工程建立
复制“6-6 输出捕获模式测频率”文件夹,并改名为“6-7 PWMI模式测频率占空比”。
2. 对PWM模块的升级
输入捕获初始化的部分,需要进行升级,配置成两个通道同时捕获同一个引脚的模式。
1. 法一:将原先的通道初始化部分在复制一份:
TIM_ICInitStructure.TIM_Channel = TIM_Channel_2;
TIM_ICInitstructure.TIM_ICSelection =TIM_ICSelection IndirectTI;
TIM_ICPolarity_Falling;
2. 法二:使用库函数快捷更改
void TIM_PWMIConfiq(TIM_TypeDef* TIMX,TIM_ICInitTypeDef* TIM_ICInitstruct)
功能:
该函数用于对定时器的 PWMI 模式进行配置,使得定时器能够对外部输入的 PWMI 信号进行准确测量,获取其频率和占空比等参数。可以快捷地把电路配置成PWMI模式的标准结构。
参数:
第一个参数通常是要配置的定时器指针,比如 TIM2、TIM3 等,指定了操作的对象是哪个定时器。
第二个参数可能是一个包含 PWMI 模式配置信息的结构体指针。只需要传入一个通道的参数。在函数中会自动把剩下的一个通道初始化成相反的配置。比如我这里传入通道1,直连,上升沿,那函数里面就会顺带配置通道2,交叉,下降沿;如果我传入通道2,直连,上升沿,函数就会顺带配置通道1,交叉,下降沿
注意:
这个函数只支持通道1和通道2的配置,不要传入通道3和通道4。
获取占空比的函数(uint32_t IC_GetFreq(void)):
高电平的计数值放在CCR2中,整个周期的计数值放在CCR1中,我们用CCR2/CCR1,就能得到占空比了。
这个函数用于计算输入捕获通道的占空比。在 PWM 输入模式或类似的应用场景中,通过测量定时器在不同边沿触发时的计数值,可以计算出输入信号的占空比。
-
return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3)+1);
: -
TIM_GetCapture2(TIM3)
:这个函数调用获取定时器 TIM3 的输入捕获通道 2 的当前捕获值(CCR2)。 -
TIM_GetCapture1(TIM3)
:获取定时器 TIM3 的输入捕获通道 1 的当前捕获值(CCR1)。 -
+1
的操作可能是为了避免出现除以零的情况,确保在计算占空比时分母不为零。 -
整体计算方式是先计算通道 2 的捕获值与通道 1 的捕获值的比例(此时这个是的范围是0~1),然后乘以 100 将其转换为百分比形式,可以得到整数,得到输入信号的占空比。
3. 主函数代码详解
1. 模块初始化
OLED_Init():
功能:对 OLED 显示屏进行初始化操作。
PWM_Init():
功能:初始化 PWM 模块。
IC_Init():
功能:对输入捕获模块进行初始化。
2. 显示静态字符串
OLED_ShowString(1, 1, "Freq:00000Hz");
功能:在 OLED 显示屏的第 1 行第 1 列显示字符串Freq:00000Hz
。
视频可能介绍的实现方式是:通过调用 OLED 显示库中的函数,该函数内部可能是根据 OLED 的显示原理,将字符串的每个字符转换为对应的编码(如 ASCII 码),然后通过数据引脚逐位发送到 OLED 显示屏的控制器,控制器根据编码在指定位置显示字符。
OLED_ShowString(2, 1, "Duty:00%");
功能:在 OLED 显示屏的第 2 行第 1 列显示字符串Duty:00%
。
实现方式与上述类似,用于提示后续显示的是占空比信息。
3. 设置 PWM 参数
PWM_SetPrescaler(720 - 1);
功能:设置 PWM 的预分频器值。
原理:根据公式Freq = 72M / (PSC + 1) / 100
,这里将预分频器设置为720 - 1
,可以计算出相应的 PWM 频率。视频中可能会详细解释这个公式的推导过程以及预分频器的作用。预分频器的作用是对系统时钟(这里假设为 72MHz)进行分频,以得到合适的定时器计数频率,从而实现所需的 PWM 频率。
PWM_SetCompare1(50);
功能:设置 PWM 通道 1 的比较值。
原理:根据公式Duty = CCR / 100
,这里将比较值设置为 50,用于确定 PWM 的占空比。视频中可能会说明 CCR(比较寄存器的值)与占空比的关系,以及如何通过设置比较值来控制 PWM 输出的脉冲宽度。即占空比为 50%,意味着在一个 PWM 周期内,输出高电平的时间占整个周期的一半。
4. 主循环中的操作
while (1)
功能:这是一个无限循环,确保程序持续运行,不断更新显示内容。强调在嵌入式系统中,主循环是程序的核心部分,大多数操作都在这个循环中不断重复执行。
OLED_ShowNum(1, 6, IC_GetFreq(), 5);
功能:在 OLED 显示屏的第 1 行第 6 列显示输入捕获模块测得的频率值。
OLED_ShowNum(2, 6, IC_GetDuty(), 2);
功能:在 OLED 显示屏的第 2 行第 6 列显示输入捕获模块测得的占空比。
作者:Tanecious.