代码改变世界

完整教程:十七、STM32的TIM(八)(TIM输入捕获)

2025-11-29 12:01  tlnshuju  阅读(0)  评论(0)    收藏  举报

介绍:本章主要讲解了 STM32C8T6定时器(TIM) 模块,对 TIM 输入捕获功能 进行了简要说明,并列举了几种常用的测量信号频率的方法。此外,还结合具体实例,详细分析了程序的设计思路与实现过程,帮助读者更好地理解输入捕获在实际应用中的使用方式。

目录

一、介绍

 1.1 简介

 1.2 频率测量

 1.3代码设计思路

二、接线图

三、函数接口

四、代码实现

五、程序现象


一、介绍

 1.1 简介

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

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

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

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

 1.2 频率测量

        1.测量的都是高低电平。

        2.测频法:在闸门时间T内,对上升沿计次,得到N,则频率。(= / f_x=N / T)(适合频率高一点

        3.测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率。(= / f_x=f_c  / N)(适合频率低一点

        4.中界频率:测频法与测周法误差相等的频率点。(= / f_m=√(f_c  / T))

那么这个公式是如何来的呢?就是上面两个公式把N提出来求的解。
如果频率大于中界频率,使用测周法会更好,反之用测频法。

 1.3代码设计思路

        1. 使用测频法:

        我们可以采用“测频法”来获取信号频率,这种方法在之前章节"九、STM32的外部中断(三)”中已经讲过:通过外部中断在信号的上升沿进行计数,同时配合一个1 秒定时器中断实现频率统计。

        具体做法如下:

        在外部中断中对每次上升沿进行计数;

        启动一个周期为 1 秒的定时器中断;

        在定时器中断中读取当前的计数值,这个值就表示信号在 1 秒内的脉冲次数,也就是频率(Hz);

        随后清零计数器,为下一个周期测量做准备。

        为了提高测量稳定性,还可以进行多次采样,对结果进行平均处理,以进一步降低误差。

        2. 测周法:

        接下来代码讲解。

二、接线图

三、函数接口

        1.TIM_PrescalerConfig:这个函数主要用于设置 PSC(预分频器) 的值。之所以不去修改 ARR(自动重装载寄存器),是因为改变 ARR 会直接影响 PWM 的占空比,从而导致输出波形发生变化。为了在保持占空比不变的情况下调整频率,我们选择通过修改 PSC 的值来实现。

四、代码实现

(借鉴十四、STM32的TIM(五)(PWM驱动LED呼吸灯)

        1.PWM_SetPrescaler

void PWM_SetPrescaler(uint16_t Prescaler)
{
	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
}

        2.初始化

void IC_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启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引脚初始化为上拉输入
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	/*时基单元初始化*/
	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的时基单元
	/*输入捕获初始化*/
	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的输入捕获通道
	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

        2.获取频率

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

        3.主函数

int main(void)
{
	OLED_Init();
	PWM_Init();
	IC_Init();
	OLED_ShowString(1, 1, "Freq:00000Hz");
	PWM_SetPrescaler(720 - 1);
	PWM_SetCompare1(50);
	while (1)
	{
		OLED_ShowNum(1, 6, IC_GetFreq(), 5);
	}
}

五、程序现象

       程序运行后的效果是:显示屏上会实时显示 TIM2 的输出频率,测量误差在 ±1 之间。之所以在 IC_GetFreq 函数中进行加一操作,是为了修正显示误差,使屏幕上显示的频率值更加准确、符合预期。

At the end of this article, applaud yourself again!!