STM32输入捕获(附代码)

输入捕获简介

从框图中可以看出,输入捕获和输出比较,一个通道共用一个寄存器,共用一个引脚,所以输入捕获和输出比较一个通道只能使用其中一个功能。
在这里插入图片描述
IC(Input Capture)输入捕获

  • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。(就是当输入引脚产生上升沿,输入捕获电路会捕捉到电平跳变,控制CNT锁存到CCR中。)
  • 每个高级定时器和通用定时器都拥有4个输入捕获通道。
  • 可配置为PWMI模式(PWM的输入模式),同时测量频率和占空比。
  • 可配合主从触发模式,实现硬件全自动测量。

频率测量

在这里插入图片描述
测频法适用于高频的测量,测周法适用低频的测量
高频和低频的分界线就是中界频率,比中界频率低就适用测周法,比中界频率高就使用测频法。

测频法:适使用外部中断或者定时器外部时钟对外界的信号进行计次,然后定时器定时(假设1s)对外部信号进行计算,几个信号频率就是几。

框图解释

在这里插入图片描述
最左边就是四个输入,前三个输入端都接到了异或门上,当其中一个引脚跳变,异或门就输出1,之后通过数据选择器到达输入捕获通道1,如果数据选择器选择上面一个,那么输入捕获通道1的输入就是3个引脚的异或值,如果选择下面一个,4个通道就各用各的引脚。

异或门的作用是为三相无刷电机服务的,无刷电机有3个霍尔传感器检测转自的位置,可以根据转子的位置进行换相。有了这个异或门就可以在前三个通道接上无刷电机的霍尔传感器,定时器就作为无刷电机的接口定时器去驱动换相电路工作。

输入信号输入到了输入滤波器和边缘检测器。输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发。边缘检测器这就和外部中断那里是一样的,可以选择高电平触发或者低电平触发。当出现指定的电平时,电压检测电路就会触发后续电路执行动作。
框图中其实是设计了两套滤波和边缘检测电路。

第一套电路经过滤波和极性选择得到TI1FP1输入给通道一的后续电路。第二条电路经过另一个滤波和极性选择得到TI1FP2输给下面通道二的后续电路。同理,下面TI2信号进来也经过两套滤波和即兴选择,得到TI2FP1TI2FP2。其中TI2FP1输给上面,TI2FP2输给下面,在这里两个信号进来可以选择各走各的,也可以选择进行一个交叉,让CH2引脚输入给通道一或者CH1引脚输入给通道二。

那这里为什么要进行交叉连接呢?
第一个目的可以灵活切换后续捕获电路的输入,比如你一会儿想以CH1作为输入。一会儿想以CH2作为输入,这样就可以通过这个数据选择器灵活地进行选择。
第二个目的,也是他交叉的主要目的,就是可以把一个引脚的输入同时映射到两个捕获单元,这也是PWMI模式的经典结构。第一个捕获通道使用上升沿触发用来捕获周期。第二个通道使用下降沿触发用来捕获占空比,两个通道,同时对一个引脚进行捕获,就可以同时测量频率和占空比。这就是PWMI模式。
一个通道灵活切换两个引脚和两个通道同时捕获一个引脚,这就是这里交叉一下的作用和目的。
同样,下面通道三和通道四也是一样的结构,可以选择各自独立连接。也可以选择进行交叉。

继续往后看输入信号进行滤波和极性选择后就来到了预分频器。预分频器每个通道各有一个可以选择对前面的信号进行分频,分频之后的触发信号就可以触发捕获电路进行工作,每来一个触发信号,CNT的值就会像CCR转运一次。转运的同时会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断。如果需要在捕获的瞬间处理一些事情的话,就可以开启这个捕获中断。这就是整个电路的工作流程。

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

因为CNT是从0开始进行自增的,自增到某个值触发上升沿了,那么这个值就转运到了CCR,此时就知道过了多久来一次上升沿了。CNT的驱动时钟就是fc,fc / N就是待测频率了。假设fc是100HZ,如果N是50就说明计数了50,来一次上升沿,此时捕获到的频率就是2HZ。

每次捕获之后我们都要把CNT清零一下。这样下次上升沿在捕获的时候取出的CNT才是两个上升沿的时间间隔。

细节

在这里插入图片描述

上图是输入捕获通道1的一个更详细的框图。引脚进来还是先经过一个滤波器,滤波器的输入是TI1,就是CH1的引脚。输出的TI1F就是滤波后的信号,f DTS是滤波器的采样时钟来源。下面CCMR1寄存器里的ICF位可以控制滤波器的参数。
那这个滤波器具体是怎么工作的呢?可以看一下手册。
在这里插入图片描述

简单理解,这个滤波器工作原理就是以采样频率对输入信号进行采样,当连续N个值都为高电平输出才会高电平,连续N个值都为低电平输出才为低电平。如果你信号出现高频抖动导致连续采样N个值不全都一样,那输出就不会变化,这样就可以达到滤波的效果。采样频率越低,采样个数N越大,滤波效果就越好。下面那些描述,就是每个参数对应的采样频率和采样个数。在实际应用中,如果波形噪声比较大,就可以把这个参数设置大一些,这样就可以过滤噪声了。为了能够采样,滤波器的频率一般都是远高于信号频率的。

滤波之后的信号通过边缘检测器捕获上升沿或者下降沿,用这个CCER寄存器里的CC1P位就可以选择极性了,最终得到TI1FP1触发信号,通过数据选择器进入通道一后续的捕获电路。当然这里实际应该还有一套一样的电路,得到TI1FP2触发信号,连通道通道二的后续电路,这里并没有画出。同样通道二有TI2FP1连通到通道一的后续,通道二也还有TI2FP2连通到通道二的后续,总共是4种连接方式。

然后经过数据选择器进入后续捕获部分电路。CC1S位可以对数据选择器进行选择,之后ICPS位可以配置这里的分频器可以选择不分频,二分频,四分频,八分频。最后CC1E位控制输出使能或失能。如果使能了输出,输入端产生指定边沿信号,经过层层电路到达IC1PS,就可以让CNT的值转运的CCR里面来。另外刚才说了每捕获一次CNT的值,都要把CNT清零一下,以便于下一次的捕获。在这里硬件电路就可以在捕获之后自动完成CNT的清零工作。

如何自动清零CN呢?
这里这个TI1FP1信号和TI1_ED(TI1的边沿信号)都可以通向从模式控制器,比如TI1FP1信号的上升沿触发捕获,通过TI1FP1还可以同时触发从模式,这个从模式里面就有电路可以自动完成CNT的清零。

主从触发模式

主从触发模式就是主模式,从模式,触发源选择这三个模式的简称。
在这里插入图片描述
主模式可以将定时器内部的信号映射到TRGO引脚上用于触发别的外设。
从模式是用于接收其他外设或者自身外设的一些信号。用于控制自身定时器的运行,也就是被别的信号控制,所以这部分叫从模式。
触发源选择就是选择从模式的触发信号源的。可以认为是从模式的一部分。触发源选择选择一个指定的信号得到TRGI。TRGI去触发从模式,从模式可以从列表中选择一项操作来自动执行。

如果想完成刚刚说的,想让TI1FP1信号自动触发CNT清零。触发源选择就可以选择TI1FP1,从模式选择Reset的操作。这样TI1FP1的信号就可以自动触发从模式,从模式自动清零CNT。

主从模式也可以在手册中看他们的寄存器描述,有很多功能。

输入捕获与PWMI基本结构

在这里插入图片描述
从图中可以看出,标准频率fc = 72M / 预分频系数
需要注意的是,ARR重装寄存器最大是65535,如果信号频率太低,CNT计数值可能会溢出。
从模式触发源选择也只有TI1FP1TI2FP2,没有TI3和TI4的信号。对于通道3和通道4,只能开启捕获中断在中断中手动清零,但是这样程序就会频繁处于中断的状态,比较消耗软件资源。

在这里插入图片描述
PWMI模式使用了两个通道同时捕获一个引脚,可以同时测量周期和占空比。
TI1FP1配置上升沿触发,触发捕获通道和清零CNT正常的捕获周期。TI1FP2配置为下降沿触发,通过交叉通道去触发通道2的捕获单元。
看左上角可以知道,下降沿在CCR2捕获计数的时候,记录了高电平的时间,此时并不触发CNT清零,然后上升沿结束后,再次清零,重新开始。此时就记录了高电平的时间和整个周期的时间。用CCR2 / CCR1就可以得到占空比。

库函数介绍

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

因为存在交叉通道的配置,所以ICInit配置四个通道共用一个函数。

void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

可以快速的配置两个通道。

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

选择主模式的输出的触发源。

void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);

选择从模式

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

这四个函数分别单独配置通道1,2,3,4的分频器,结构体中也可以配置。

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

分别读取4个通道的CCR。这四个函数和TIM_SetCompare函数是对应的,读写的都是CCR寄存器。输出比较模式下,CCR是只写的,要用TIM_SetCompare写入。输入捕获模式下,CCR是只读的,要用TIM_GetCapture读出。

代码

输出捕获测频率

void IC_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	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);
	
	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;//1MHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,这是高级定时器的,我们配置TIM2,所以直接给0
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_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);
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
	
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / TIM_GetCapture1(TIM3);
}

将图中所有通道打通即可实现测频率。

PWMI模式测频率占空比

可以使用将结构体赋值一份,修改相应的参数进行配置。
但是ST公司提供了库函数可以直接配置。但是只支持通道一和通道二相互配置,通道三和四不支持。

void IC_Init()
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	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);
	
	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;//1MHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,这是高级定时器的,我们配置TIM2,所以直接给0
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);
	
	TIM_ICInitTypeDef TIM_ICInitStructure;
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_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_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
	TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
	
	TIM_Cmd(TIM3, ENABLE);
}

uint32_t IC_GetFreq(void)
{
	return 1000000 / TIM_GetCapture1(TIM3);
}

uint32_t IC_GetDuty(void)
{
	return TIM_GetCapture2(TIM3) * 100 / TIM_GetCapture1(TIM3);
}
### STM32 PWM 输入捕获教程 #### 实现PWM输入捕获的关键要素 对于STM32而言,要实现PWM输入捕获功能,主要依赖于内部的定时器模块。TIM1位于APB2总线上,被分类为高级定时器;而TIM2、TIM3以及TIM4则处于APB1总线之上,属于通用定时器类别[^2]。 为了完成这一目标,需配置特定的GPIO引脚作为外部信号输入端口,并将其连接到选定用于捕获操作的定时器通道上。之后,通过设定相应的寄存器来启动上升沿或下降沿触发模式下的计数过程,在检测到脉冲宽度变化时自动记录时间戳并更新捕捉寄存器中的数值。 #### 初始化函数定义 在`PWM.h`头文件中已经预先声明了一些必要的初始化接口: ```c #ifndef __PWM_H #define __PWM_H void PWM_Init(void); void PWM_SetCompare1(uint16_t Compare); void PWM_SetPrescaler(uint16_t Prescaler); #endif /* __PWM_H */ ``` 这些函数提供了基本的功能支持,比如设置预分频系数和比较值等参数调整能力[^1]。 #### 配置PWM输入捕获的具体步骤 下面给出一段基于上述描述编写的C语言源码片段,展示了如何利用STM32CubeMX工具生成的基础框架进一步定制化开发环境来进行PWM输入捕获的操作实例: ```c #include "stm32f1xx_hal.h" // 假设使用 TIM2_CH1 进行输入捕获 static uint32_t capture_value; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){ if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { // 获取当前捕获到的时间戳 capture_value = __HAL_TIM_GET_COMPARE(htim, TIM_CHANNEL_1); // 处理获取的数据... } } int main(){ MX_GPIO_Init(); MX_TIM2_Init(); // 启用中断服务程序 HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); while(1){ // 主循环体可以放置其他任务逻辑 } } ``` 这段代码实现了对来自指定通道(此处假设为TIM2的第一个通道)上的PWM波形周期长度测量,并通过回调机制处理每次成功捕获事件后的响应动作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值