【stm32】输入捕获

输入捕获简介:

IC(Input Capture)输入捕获:

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

输入捕获和输出比较的区别:

输入捕获:
• 接收到输入信号执行CNT锁存到CCR的动作
输出比较:
• 根据CNT和CCR的大小关系来执行输出动作
在这里插入图片描述
• 每个高级定时器和通用定时器都拥有4个输入捕获通道
• 可配置为PWMI模式,同时测量频率和占空比
• 可配合主从触发模式,实现硬件全自动测量

频率测量:

在这里插入图片描述

测频法:

•测频法适合测量高频信号,在闸门时间内,最好要多出现一些上升沿,计次数量多一些,有助减少误差。
•测频法结果更新较慢,数值相对稳定
实现思路:
• 之前的对射式红外传感器计次计次,每来一个上升沿计次1,再用有一个定时器,每隔1s中断,再中断中,每隔1s取一次计次值,同时清0计次,为下一次做准备,这样每次读取的计次值就是频率
• 定时器外部时钟也同理,每隔1s取一次计次,就能实现
正负一误差:
在闸门时间内,并不是每个周期信号都是完整的,比如在最后时间里,可能有一个周期刚出现一半,闸门时间就到了,但这只有半个周期,只能把它舍弃或当做一整个周期来看,这样就会多计一个或少计一个
在这里插入图片描述

测周法:

• 测周法适合测量低频信号,低频信号,周期比较长,计次比较多,有助减少误差
• 测周法结果更新的快,数值跳变也很快
实现思路:
测量两个上升沿的时间
正负一误差:
标准频率fc频率计次,在最后时刻,有可能一个数记到一半,计时就结束了,那这半个数也只能舍弃或按一整个数算了
在这里插入图片描述

中界频率:

• 正负一误差是上面两种方式都固有误差,要想减少正负一误差的影响,就只能尽量多计一些数(N),当计次N比较大时。正负一误差对N的影响就会很小。所以总结就是N越大,正负一误差对我们的影响越少,那当有一个频率,测频法和测周法计次的N相同,就说明误差相同,这就是中界频率。
• 当待测频率大于中界频率时,适用测频法,当待测频率小于中界频率时,适用测周法。

主从触发模式:

在这里插入图片描述

基本结构:

输入捕获基本结构:

斯柯达
• 只使用一个通道,只能测频率。
• 时基单元:把时机单元配置好,启动定时器,CNT就会在标准时钟频率的驱动下不断自增。(标准时钟频率=72M/预分频系数)
• 输入捕获通道:输入捕获通道1的GPIO口,输入一个方波信号,经过滤波器和边沿检测,选择TIFP1为上升沿触发,之后选择直连的通道,分频器选择不分频,从模式触发源选择,选中TI1FP1为触发信号,从模式选择复位操作,这样TI1FP1的上升沿,就会触发CNT的清零操作
• 执行: TI1FP1出现上升沿后,将CNT的当前计数值转运到CCR1中,再触发从模式给CNT清零,或者是非阻塞的同时转移,然后进行下一轮的捕获。
• 只要读取CCR1中的值,再fc/N就可以得到频率
• 频率过低,CNT(最大65535)可能会溢出
• 从模式只能在输入捕获通道1和输入捕获通道2使用,对于通道3和通道4,只能在中断中清零。

PWMI基本结构:

在这里插入图片描述
• 使用两个通道,能测频率和占空比。
• 时基单元:把时机单元配置好,启动定时器,CNT就会在标准时钟频率的驱动下不断自增。(标准时钟频率=72M/预分频系数)
• 输入捕获通道1:输入捕获通道1的GPIO口,输入一个方波信号,经过滤波器和边沿检测,选择TIFP1为上升沿触发,之后选择直连的通道,分频器选择不分频,从模式触发源选择,选中TI1FP1为触发信号,从模式选择复位操作,这样TI1FP1的上升沿,就会触发CNT的清零操作
• 输入捕获通道2:选择TIFP2为下升沿触发,通过交叉通道,触发通道2的捕获单元
• 执行: 在上升沿,将CNT的当前计数值转运到CCR1中,再触发从模式给CNT清零,或者是非阻塞的同时转移,在下降沿,将CNT的当前计数值转运到CCR2中,然后进行下一轮的捕获。
• 只要读取CCR1中的值,再fc/N就可以得到频率
•读取CCR1和CCR2中的值,再CCR2/CCR1就可以得到占空比

代码实现:

信号源:

pwm初始化:

• PWM 频率: Freq = CK_PSC / (PSC + 1) / (ARR + 1)
• PWM 占空比: Duty = CCR / (ARR + 1)
• PWM 分辨率: Reso = 1 / (ARR + 1)
CK_PSC为72M,PSC为预分频器,ARR为自动重装器,CCR为捕获/比较器
改变PSC和ARR都可以调节频率,但改变ARR调节频率会影响到占空比,所以选择PSC调节频率

void PWM_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);
	
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef PWM_Init;
	PWM_Init.TIM_ClockDivision=TIM_CKD_DIV1;
	PWM_Init.TIM_CounterMode=TIM_CounterMode_Up;
	PWM_Init.TIM_Period=100 - 1;
	PWM_Init.TIM_Prescaler=720-1;
	PWM_Init.TIM_RepetitionCounter=0;
	
	TIM_TimeBaseInit(TIM2,&PWM_Init);
	
	TIM_OCInitTypeDef TIM_OCInitStruct;
	TIM_OCStructInit(&TIM_OCInitStruct);
	TIM_OCInitStruct.TIM_OCMode=TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OCNPolarity=TIM_OCPolarity_High;
	TIM_OCInitStruct.TIM_OutputState=TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_Pulse=0;
	TIM_OC1Init(TIM2,&TIM_OCInitStruct);
	
	TIM_Cmd(TIM2,ENABLE);
}

pwm占空比:

void PWM_SetComparel_1(uint16_t CCR)
{

	TIM_SetCompare1(TIM2,a);

}

pwm频率:

void PWM_SetPrescaler(uint16_t PSC)
{
	TIM_PrescalerConfig(TIM2, PSC, TIM_PSCReloadMode_Immediate);		//设置PSC的值
}

pwm输出:

PWM_Init();
PWM_SetPrescaler(720-1);//Freq = 72M / (PSC + 1) / 100  //1000hz
PWM_SetComparel_1(50);//Duty = CCR / 100        //占空比为50%

输入捕获配置:

输入捕获配置的基本步骤:

第一步:RCC开启时钟,打开GPIO和TMI的时钟
第二步:GPIO初始化,配置成输入模式,一般选择上拉输入或者浮空输入模式
第三步:配置时基单元,让CNT计数器在内部时钟的驱动下自增运行
第四步:配置输入捕获单元,包括滤波器、级联、直联通道还是交叉通道、分频器等参数
第五步:选择从模式的触发源,触发源选择为TI1FP1
第六步:选择触发之后执行RESET操作
第七步:开启定时器
读取

第一步:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3的RCC时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的RCC时钟
第二步:
PIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);//配置PA16为上拉输入模式

输入捕获引脚详见:这里

第三步:
	TIM_InternalClockConfig(TIM3);//选择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;
	TIM_TimeBaseInitStructure.TIM_Prescaler=72 - 1;//标准频率=72M/72=1M
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
第四步:
初始化捕获单元:
初始化结构体:

• 初始化结构体原型:

typedef struct
{

  uint16_t TIM_Channel;      //指定要配置的通道
  uint16_t TIM_ICPolarity;   //选择极性
  uint16_t TIM_ICSelection;  //指定信号输入通道
  uint16_t TIM_ICPrescaler;  //触发信号分频器(注:此参数会改变信号的频率)
  uint16_t TIM_ICFilter;     //配置输入捕获的滤波器
} TIM_ICInitTypeDef;
初始化函数:

/*
	TIMx:选择要配置哪个定时器的捕获单元,其中x可以是1 2 3 4 5 8 9 12或15。
	TIM_ICInitStruct:指向TIM_ICInitTypeDef类型结构体的指针
*/
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)//配置指定定时器的指定输入捕获通道
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct)///此函数同时会把另一个通道配置为相反的配置(注:此函数只能配置输入捕获通道1或输入捕获通道2)
第五步:
/*
	TIMx:选择要将哪个定时器配置为从模式,其中x可以是1 2 3 4 5 8 9 12或15。
	TIM_InputTriggerSource:选择从模式的触发源
*/
void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)
第六步:
/*
	TIMx:指定一个从模式定时器
	TIM_SlaveMode:指定要执行的操作
*/
void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode)
第七步:
TIM_Cmd(TIM3,ENABLE);
• 读取:
• 读取频率:

想要获取最新一个周期的频率时,直接读取CCR寄存器,然后按照fc除N计算即可(fc,标准频率=72M/预分频系数)

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3)+1);//标准频率1Mhz/N
}
• 读取占空比:

用CRR1的值(整个周期的时间)除以CCR2(周期中高电平的时间)

uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}

代码示例:

void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3的RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的RCC时钟
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//配置PA16为上拉输入模式
	
	
	TIM_InternalClockConfig(TIM3);//选择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;
	TIM_TimeBaseInitStructure.TIM_Prescaler=72 - 1;//标准频率=72M/72=1M
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitstructure;//定义结构体变量
	TIM_ICInitstructure.TIM_Channel=TIM_Channel_1;//选择配置定时器3的捕获通道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_Cmd(TIM3,ENABLE);//使能TIM3,定时器开始运行
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3)+1);
}
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}

PWMI模式:

void IC_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM3的RCC时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//开启GPIOA的RCC时钟
	
	GPIO_InitTypeDef GPIO_InitStruct;
	GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IPU;
	GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
	GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz;
	GPIO_Init(GPIOA,&GPIO_InitStruct);//配置PA16为上拉输入模式
	
	
	TIM_InternalClockConfig(TIM3);//选择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;
	TIM_TimeBaseInitStructure.TIM_Prescaler=72 - 1;//标准频率=72M/72=1M
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
	TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitstructure;//定义结构体变量
	TIM_ICInitstructure.TIM_Channel=TIM_Channel_1;//选择配置定时器3的捕获通道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_PWMIConfig(TIM3,&TIM_ICInitstructure);//将结构体变量交给TIM_PWMIConfig,配置TIM3的输入捕获通道
											//此函数同时会把另一个通道配置为相反的配置,实现PWMI模式
	
	TIM_SelectInputTrigger(TIM3,TIM_TS_TI1FP1);//触发源选择TI1FP1
	TIM_SelectSlaveMode(TIM3,TIM_SlaveMode_Reset);//从模式选择复位
												//即TI1产生上升沿时,会触发CNT归零
	
	TIM_Cmd(TIM3,ENABLE);//使能TIM3,定时器开始运行
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3)+1);
}
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3)+1)*100/(TIM_GetCapture1(TIM3)+1);
}

测频率的性能:

• 测频率的范围:目前给标准频率1MHZ时,计数器最大只能到65535,所以测量的最低频率是1M/65535,大概是 15HZ,若频率再低,则溢出,此时可以增大预分屏,就可以使得标准频率更低
• 测量的最高频率没有上限,但是频率越大,误差越大。如果非要找一个频率上限,就是1MHZ,因为超过标准频 率,时误差非常大
• 如果要求误差等于1/1,000时,频率为上限,则这个上限就是1兆除1,000=1,000HZ
• 如果要求误差可以到1%,那频率上限就是1MHZ除100=10KHZ
• 如果想提高频率的上限,就要把PSC给降低一些,提高标准频率,上限制就会提高
若要更高的频率,则用测频法

  • 50
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值