十七、STM32的TIM(八)(TIM输入捕获)
介绍:本章主要讲解了 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;				//选择配置定时器通道1TIM_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);					//触发源选择TI1FP1TIM_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!!
