输入捕获简介
从框图中可以看出,输入捕获和输出比较,一个通道共用一个寄存器,共用一个引脚,所以输入捕获和输出比较一个通道只能使用其中一个功能。
IC(Input Capture)输入捕获
- 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。(就是当输入引脚产生上升沿,输入捕获电路会捕捉到电平跳变,控制CNT锁存到CCR中。)
- 每个高级定时器和通用定时器都拥有4个输入捕获通道。
- 可配置为
PWMI
模式(PWM的输入模式),同时测量频率和占空比。 - 可配合主从触发模式,实现硬件全自动测量。
频率测量
测频法适用于高频的测量,测周法适用低频的测量
高频和低频的分界线就是中界频率,比中界频率低就适用测周法,比中界频率高就使用测频法。
测频法:适使用外部中断或者定时器外部时钟对外界的信号进行计次,然后定时器定时(假设1s)对外部信号进行计算,几个信号频率就是几。
框图解释
最左边就是四个输入,前三个输入端都接到了异或门上,当其中一个引脚跳变,异或门就输出1,之后通过数据选择器到达输入捕获通道1,如果数据选择器选择上面一个,那么输入捕获通道1的输入就是3个引脚的异或值,如果选择下面一个,4个通道就各用各的引脚。
异或门的作用是为三相无刷电机服务的,无刷电机有3个霍尔传感器检测转自的位置,可以根据转子的位置进行换相。有了这个异或门就可以在前三个通道接上无刷电机的霍尔传感器,定时器就作为无刷电机的接口定时器去驱动换相电路工作。
输入信号输入到了输入滤波器和边缘检测器。输入滤波器可以对信号进行滤波,避免一些高频的毛刺信号误触发。边缘检测器这就和外部中断那里是一样的,可以选择高电平触发或者低电平触发。当出现指定的电平时,电压检测电路就会触发后续电路执行动作。
框图中其实是设计了两套滤波和边缘检测电路。
第一套电路经过滤波和极性选择得到TI1FP1
输入给通道一的后续电路。第二条电路经过另一个滤波和极性选择得到TI1FP2
输给下面通道二的后续电路。同理,下面TI2
信号进来也经过两套滤波和即兴选择,得到TI2FP1
和TI2FP2
。其中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计数值可能会溢出。
从模式触发源选择也只有TI1FP1
和TI2FP2
,没有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);
}