STM32学习笔记7-TIM输入捕获模式

发布于:2025-08-12 ⋅ 阅读:(18) ⋅ 点赞:(0)

第三部分是定时器的输入捕获功能和主从触发模式,来实现测量方波频率

输入捕获和输出比较是相互共用的,对应的四个引脚也是同理,但是不能同时使用

输入捕获

IC(Input Capture)输入捕获

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

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

可配置为PWMI模式(PWM输入模式),同时测量频率和占空比

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

频率测量

默认是数字信号,越往左频率越低,默认为上边沿;若要测量一个正弦波。需要搭建一个信号预处理电路,把正弦波转换为数字信号,再输入到STM32上;如果测量的信号电压非常高,则需要考虑隔离的问题(隔离问题指如何处理高电压信号与低压控制电路(如STM32)之间的电气连接

正弦波:正弦波是一种连续的周期性波形,其电压(或电流)随时间按正弦函数规律变化,作模拟信号中的基准信号

方波:高电平波形通常指数字信号中的方波(Square Wave)脉冲波(Pulse Wave),其特征是电压在高电平(如 5V)和低电平(如 0V)之间瞬时切换。

  • 测频法(图左边):在闸门时间T内,对上升沿计次,得到N,则频率,

                                                        fx=N / T

此方法适合于频率较多的时候,结果更新慢,但是稳定,相当于自带一个滤波

  • 测周法(图右边):两个上升沿内,以标准频率fc计次,得到N ,则频率

                                                    fx=fc / N

此方法适合于频率较少的时候,因为只测量一个周期,所以结果更新快,但是会受到噪音等影响

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

                                                    fm=fc / T

测频法与测周法误差——正负1误差:指的是如果满足了计次N,但是波形并没有走,还留有,那会进行舍弃(-1)或者补全(+1)的操作。所以要使N的数大一点,误差就小。

中界频率来决定频率大小,从而合理的使用测频法还是测周法

电路实现测周法:

        以四个引脚,通过引脚定义表来确认引脚的复用位置; 异或门:当三个输入引脚的任何一个有电平翻转,输出引脚则产生一个电平翻转,之后通过数据选择器,到达输入通道(如果选择了上面的通道,则会用三个引脚的异或值;如果选择下面的,则没有异或门。设计异或门:是为了三相无刷电机服务的),进入通道:输入滤波器对信号进行滤波,边沿检测器:选择高电平触发或者低电平触发;在通道里选择不同的滤波通道(一般有2个) ,形成交叉连接(1.可灵活切换后续的输入;2.(主要)可以把一个引脚的输入,同时映射到两个捕获单元,属于PWMI模式),之后就进入预分频器进行分频,分频后的触发信号,就可以触发捕获电路的工作,每来一个信号,CNT的值就向CCR转运一次。转运的同时,会发生一个捕获事件,此事件会在状态寄存器置标志位,同时也可以产生中断,在这里捕获之后会自动完成CNT的清零工作(通过以下图中的TI1FP1和TI1F_ED信号通过从模式中。从模式有能力自动进行清零)如果需要在捕获的瞬间处理事情的话,就可以开启捕获中断。

输入捕获通道

引脚进来,输入TI1经过一个滤波器,fDITS是滤波器的采样时钟来源,下面CCMR1寄存器里的ICF位可以控制滤波器的参数,这个滤波器原理:通过多个高电平后,才产生为高电平,若是一会高一会低,则产生一道稳定的电平,滤波后的信号为TI1F,通过边沿检测器进行=捕获上升沿或下降沿,用CC1P位选择极性,最终得到TI1FP1触发信号,通过数据选择器进入后续的捕获电路(有两个这样的电路通道)

主从触发模式:

分为:主模式、从模式和触发源选择

主模式:可以将定时器的内部信号映射到TRGO引脚,用于触发别的外设

从模式:接受其他外设或自身外设的信号,用于控制自身定时器的的运行,会被别的信号控制

触发源选择:选择从模式的触发信号源的,也可以理解是从模式的一个部分

TI1FP1信号通过TRGI选择Reset,这样就可以是CNT自动清零

输入捕获基本结构

PWMI基本结构


接线图

6-6 输入捕获模式测频率

目前是初始化了TIM2通道的1,产生了一个PWM的波形;通过单数调节CCR的值从而控制占空比,但是目前的PWM的频率是初始化中固定了的,所以需要单独调节PWM的频率=72MHz/(PSC+!)/(ARR+1),由于占空比=CCR/(ARR+1),改ARR会变两个,所以改PSC最合适。


  1. 开启RCC时钟,把GPIO和TIM的时钟打开
  2. GPIO初始化,设置成输入模式
  3. 配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
  4. 配置输入捕获单元
  5. 选择从模式的触发源,触发源选择为TI1FP1
  6. 选择触发之后执行的操作,Reset操作
  7. 调用TIM_Cmd函数,开启定时器

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//配置PSC参数

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); 配置初始化输入单元的函数,只能一个通道一个通道的配置

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct); 初始化输入单元,可以快速配置两个通道,会自动把剩下的一个通道初始化为相反的配置,只支持通道1和2

void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);  可以给捕获结构体赋一个初始值

void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource); 选择输入触发源TRGI,选择从模式的触发源的操作

void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource); 选择输出触发源TRGO,选择主模式的触发源

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

//分别读取四个通道的CCR,与之前的SetCompare函数对应

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

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void){
	OLED_Init();

	PWM_Init();
	IC_Init();
	//创建新的模块,来捕获输入
	
	OLED_ShowString(1,1,"Freq:00000Hz");
		PWM_SetPSC(720-1); //目前频率是1kHz
	PWM_set(50); //目前占空比是50%
	while(1){
		OLED_ShowNum(1,6,IC_GetFreq(),5);
	}
}
IC.c
#include "stm32f10x.h"                  // Device header

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
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,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}

6-7 PWMI模式测频率占空比

main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "IC.h"
int main(void){
	OLED_Init();

	PWM_Init();
	IC_Init();
	//创建新的模块,来捕获输入
	
	OLED_ShowString(1,1,"Freq:00000Hz");
		OLED_ShowString(2,1,"Duty:00%");
		PWM_SetPSC(720-1); //目前频率是1kHz
	PWM_set(50); //目前占空比是50%
	while(1){
		OLED_ShowNum(1,6,IC_GetFreq(),5);
		OLED_ShowNum(2,6,IC_GetDuty(),3);
	}
}

IC.c

#include "stm32f10x.h"                  // Device header

/**
  * 函    数:输入捕获初始化
  * 参    数:无
  * 返 回 值:无
  */
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的时基单元
	
	/*输入捕获初始化	升级为一个引脚对2个通道:1.是分出两个结构体变量;2.使用函数TIM_PWMIConfig*/
	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_ICInitStructure.TIM_Channel = TIM_Channel_2;				//选择配置定时器通道1
//	TIM_ICInitStructure.TIM_ICFilter = 0xF;							//输入滤波器参数,可以过滤信号抖动
//	TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Falling;		//极性,选择为上升沿触发捕获
//	TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;			//捕获预分频,选择不分频,每次信号都触发捕获
//	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;	//输入信号交叉,选择直通,不交叉
//	TIM_ICInit(TIM3, &TIM_ICInitStructure);							//将结构体变量交给TIM_ICInit,配置TIM3的输入捕获通道
	TIM_PWMIConfig(TIM3,&TIM_ICInitStructure);
	/*选择触发源及从模式*/
	TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);					//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);					//从模式选择复位
																	//即TI1产生上升沿时,会触发CNT归零
	
	/*TIM使能*/
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

/**
  * 函    数:获取输入捕获的频率
  * 参    数:无
  * 返 回 值:捕获得到的频率
  */
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可
}
//获取占空比
uint32_t IC_GetDuty(void){
	//高电平的计数存在CCR2里,整个周期的计数在CCR2里
	return TIM_GetCapture2(TIM3)+1 *100/TIM_GetCapture1(TIM3)+1; 
}

网站公告

今日签到

点亮在社区的每一天
去签到