在上一篇文章《TIM输出比较(STM32标准库学习笔记)》中,我们学习了如何利用定时器的输出比较功能产生PWM波形,实现了对电机、LED等外设的控制。然而在实际工程中,我们不仅需要“输出”波形,往往还需要“测量”外部信号的频率、占空比等参数,例如读取编码器转速、检测传感器脉冲、分析通信波形等。这时,定时器的另一项重要功能——输入捕获(Input Capture)就派上了用场。本文将继续基于STM32标准库,系统讲解TIM输入捕获的原理、频率测量方法、硬件结构、配置流程以及两个实战实例,帮助大家掌握用定时器测量外部信号的核心技能。

如果觉得有收获可以为我的文章点点小赞,这将激励我创作更好的内容!谢谢!

一、TIM输入捕获

1.1 输入捕获简介

IC(Input Capture)输入捕获

输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数

每个高级定时器和通用定时器都拥有4个输入捕获通道

可配置为PWMI模式,同时测量频率和占空比

可配合主从触发模式,实现硬件全自动测量

1.2 频率测量

测频法:在闸门时间T内,对上升沿计次,得到N,则频率

fx = N / T

测周法:两个上升沿内,以标准频率fc计次,得到N ,因为周期的倒数就是频率,则频率

fx = fc / N

测频法与测周法在应用的区别:

1.测频法适合测量高频信号,测周法适合测量低频信号;

2.测频法测量结果更新的慢一些,数值相对稳定;测周法更新的快,数据跳变也非常快

3.测频法自带一个均值滤波,如果在闸门时间内波形频率有变化,得到这一段时间的平均频率;测周法结果值会受噪声的影响,波动比较大

中界频率:测频法与测周法误差相等的频率点

测频法的实现方法:

我们用之前学过的外设就可以实现,我们之前写过,对射式红外传感器计次、定时器外部时钟,稍加改进即可。比如对射式红外传感器计次,每来一个上升沿计次+1,那我们再用一个定时器,定一个1s的定时中断,在中断里,每隔1s取一下计次值,同时清0计次,为下一次做准备,这样每次读取的计次值就直接是频率。

测周法的实现方法:

我们可以配置上升沿触发捕获,每来一个上升沿,CNT转运到CCR一次,又因为这个CNT计数器是由内部的标准时钟驱动的,所以CNT的数值,其实就可以用来记录两个上升沿之间的时间间隔,这个时间间隔就是周期,再取个倒数,就是测周法测量的频率了

1.3 输入捕获的各部分电路

1.4 输入捕获通道

采样频率越低,采样个数N越大,滤波效果就越好

1.5 主从触发模式

1.6 输入捕获基本结构

配置输入捕获的流程:

第一步,RCC开启时钟,打开GPIO和TIM的时钟;

第二步,GPIO初始化,把GPIO配置成输入模式,一般选择上拉输入模式或是浮空输入模式;

第三步,配置时基单元,让CNT计数器在内部时钟的驱动下自增运行;

第四步,配置输入捕获单元,配置滤波器、极性、直连通道、交叉通道和分频器的参数;

第五步,选择从模式的触发源,触发源选择为TI1FP1(库函数);

第六步,选择触发之后执行的操作,执行Reset操作(库函数);

最后,以上内容都配置好后,调用TIM_Cmd函数,开启定时器,

1.7 PWMI基本结构

配置流程与配置输入捕获相似,修改TIM_ICInitStructure.TIM_ICSelection配置为输入TI1FP1和TI1FP2的交叉信号

1.8 实例一 6.6.1 输入捕获模式测频率

用到的库函数:

//配置输入捕获单元,输入捕获单元初始化

void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);

//把外设电路结构配置成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);

void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);

//分别读取4个通道的CCR

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);

//选择从模式,输入捕获模式下,CCR是只读的,要用GetCapture读出

void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);

IC.c↓

-----------------------------

#include "stm32f10x.h"                  // Device header
/**
函    数:输入捕获初始化
参    数:无
返 回 值:无
*/
void IC_Init(void)
{
/开启时钟/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);			//开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/GPIO初始化/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;									//GPIO_Pin_6
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/配置时钟源/
TIM_InternalClockConfig(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_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_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);				//触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);					//从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/TIM使能/
TIM_Cmd(TIM3,ENABLE);
}
/**
函    数:获取输入捕获的频率
参    数:无
返 回 值:捕获得到的频率
*/
uint32_t IC_GetFreq(void)
{
return 1000000 / ( TIM_GetCapture1(TIM3)+1 );	//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

-----------------------------

main.c↓

-----------------------------

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
uint8_t i;//定义for循环的变量
int main(void)
{
/模块初始化/
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000Hz");

PWM_SetPrescaler(720-1);	//Freq = 72M / (PSC+1) / (ARR+1)
PWM_SetCompare1(50);			//Duty = CCR / (ARR+1)

while(1)
{
	OLED_ShowNum(1,6,IC_GetFreq(),5);
}
}

-----------------------------

1.9 实例二 6.7.1 PWMI模式测频率占空比

IC.c↓

-----------------------------

#include "stm32f10x.h"                  // Device header
/**
函    数:输入捕获初始化
参    数:无
返 回 值:无
*/
void IC_Init(void)
{
/开启时钟/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);			//开启TIM3的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/GPIO初始化/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;									//GPIO_Pin_6
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
/配置时钟源/
TIM_InternalClockConfig(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_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_PWMIConfig(TIM3,&TIM_ICInitStructure);//将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式
/选择触发源及从模式/
TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);				//触发源选择TI1FP1
TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);					//从模式选择复位
//即TI1产生上升沿时,会触发CNT归零
/TIM使能/
TIM_Cmd(TIM3,ENABLE);
}
/**
函    数:获取输入捕获的频率
参    数:无
返 回 值:捕获得到的频率
*/
uint32_t IC_GetFreq(void)
{
return 1000000 / ( TIM_GetCapture1(TIM3)+1 );	//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
/**
函    数:获取输入捕获的占空比
参    数:无
返 回 值:捕获得到的占空比
*/
uint32_t IC_GetDuty(void)
{
return ( TIM_GetCapture2(TIM3)+1 ) *100 / (TIM_GetCapture1(TIM3)+1 );//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可
}

-----------------------------

main.c↓

-----------------------------

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
uint8_t i;//定义for循环的变量
int main(void)
{
/模块初始化/
OLED_Init();
PWM_Init();
IC_Init();
OLED_ShowString(1,1,"Freq:00000Hz");
OLED_ShowString(2,1,"Duty:00%");

PWM_SetPrescaler( 3600 - 1 );	//Freq = 72M / (PSC+1) / (ARR+1)
PWM_SetCompare1(39);			//Duty = CCR / (ARR+1)

while(1)
{
	OLED_ShowNum(1,6,IC_GetFreq(),5);
	OLED_ShowNum(2,6,IC_GetDuty(),2);
}
}

-----------------------------

二、总结

本文围绕 STM32 定时器的输入捕获功能展开,系统梳理了从基本原理到实际应用的完整链路。输入捕获的核心在于:当通道输入引脚出现指定电平跳变时,将当前 CNT 的值锁存到 CCR 中,从而实现对 PWM 波形频率、占空比、脉冲间隔和电平持续时间等参数的测量。

在频率测量方面,文章对比了测频法与测周法两种思路:测频法在闸门时间内对上升沿计次,适合测量高频信号,结果更新较慢但数值稳定,且自带均值滤波效果;测周法则以标准频率对两个上升沿之间的时间间隔计次,适合测量低频信号,更新快但受噪声影响波动较大。两者误差相等的频率点称为中界频率,实际应用中可根据信号频率范围灵活选择。

在硬件结构上,文章详细介绍了输入捕获的各部分电路、通道配置、主从触发模式以及输入捕获基本结构,并给出了完整的配置流程:从 RCC 开启时钟、GPIO 初始化、时基单元配置,到输入捕获单元参数设置、从模式触发源选择与 Reset 操作,最后调用 TIM_Cmd 开启定时器。PWMI 模式则通过交叉信号配置,可同时测量频率和占空比,进一步提升了测量效率。

最后,通过两个实战实例——输入捕获模式测频率和 PWMI 模式测频率占空比,完整演示了库函数的使用方法与代码实现,帮助读者将理论知识落地到实际工程中。掌握输入捕获与 PWMI 模式,是深入理解定时器应用、实现信号测量与波形分析的重要基础。

都看到这了点点赞吧!

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐