STM32 TIM输入捕获(IC)详解

一、输入捕获简介

IC(Inpute Capture)输入捕获
大致原理:输入捕获模式下,当通道输入引脚出现指定电平跳变,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数。
每个高级定时器和通用定时器都拥有4个输入捕获通道
重点:可配合PWMI模式,同时测量频率和占空比
重点:可配合主从触发模式,实现硬件全自动测量

输入捕获所用到的模块:

 

 二、频率测量

 

测周法原理:时基单元提供时钟,统计两个上升沿的间隔即为当前周期

  • 首先,测频法适合测量高频信号,测周法适合测量低频信号
  • 然后是测频法测 量结果更新慢一些,数值相对稳定,测周法更新的快,数据跳变也非常快
  • 当待测信号频率小于中界频率时测周法误差更小, 当待测信号频率大于中界频率时测频法误差更小
  • 三、电路如何实现测周法

    3.1、简介

    图解:从左到右,最左边,是四个通道的引脚,参考引脚定义表就能知道引脚是复用在那个引脚,引脚进来,有一个三输入的异或门,这个异或门的输入接在了通道1、2、3端口,异或门的执行逻辑是,当输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转,之后输出通过数据选择器,到达输入捕获通道1,数据选择器如果选择上面一个,那输入捕获通道一的输入就是3个引脚的异或值,如果选择下面一个,那异或门就没有用,4个通道各用各的引脚。

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

    另外,其实设计了两套滤波和边沿检测电路,第一套电路经过滤波和极性选择得到TIFP1,输入给通道1的后续电路,第二套电路,经过另一个滤波和极性选择得到TIFP2,输入给下面通道2的后续电路,同理下面TI2信号进来,也经过两套滤波和极性选择得到TI2FP1和TI2FP2,其中TI2FP1输入给上面。TI2FP2输入给本路.在这里通道1和通道2可以各走各的也可以进行一个交叉(交叉的目的1、可以灵活切换后续捕获电路的输入,2、可以把一个引脚的输入,同时映射到两个捕获单元,这也是PWMI模式的经典结构)

    然后经过预分频器就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值就会向CCR转运一次,转运的同时,会发生一个捕获事件,这个事件会在状态寄存器置标志位,同时也可以产生中断,如果需要在捕获的瞬间去处理一些事情的话就可以开启这个捕获中断

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

    3.2、详细的电路

     

     引脚进来,经过一个滤波器,滤波器的输入是TI1,就是CH1的引脚,输出的是TI1F,就是滤波后的信号,FDTS是滤波器的采样时钟来源,下面CCMR1寄存器里的ICF位可以控制滤波器的参数,(滤波器的主要工作是:以采样频率对输入信号进行采样当连续N个值为高电平,输出才为高电平,连续N个值为低电平,输出才为低电平,采样频率越低,采样个数越大,滤波效果越好)滤波后的信号通过边沿检测器捕获上升沿或者下降沿,用CCER寄存器里的CC1P位就可以选择极性了,最终得到TI1FP1触发信号,通过数据选择器进入通道1的后续捕获电路,当然,应该还有一套相同的电路得到TI1FP2触发信号,连到通道2的后续电路,同样,通道2有TI2FP1连到通道一的后续电路,通道2也还有TI2FP2,共四种连接电路
     

    3.3、主从触发模式

     

     

     

     

    CCR对CNT进行捕获之后,需要对CNT进行一次清0操作,这样每次捕获得到的值才是测周法(下文有讲解)两个上升沿(下降沿)之间的时间间隔。这个清0操作,就需要用到主从触发模式来自动完成。由输入捕获通道1的详细框图可得:经过滤波和极性选择的TI1FP1信号和经过滤波的边沿信号TI1F_ED都可以通向从模式控制器,之后便可以通过硬件电路自动完成CNT的清0操作。
      主从触发模式,即主模式、从模式和触发源选择三个功能的简称。主模式可以将定时器内部的信号映射到TRGO引脚,用于触发其他外设的操作;从模式可以接收其他外设或自身外设的一些信号,用于触发自己的一些操作(定时器的运行);触发源选择,即选择从模式的触发信号源功能,也可以认为它是从模式的一部分。

    3.4、输入捕获和PWMI结构

     下图是输出捕获模式测频率的基本结构图。下图清晰地展示了输入捕获模式测量频率的过程,同时也是编程的逻辑基础。在这里我们只使用了一个通道,所以它只能测量频率。
      首先,配置时基单元,启动寄存器,则CNT就会在时钟驱动下不断自增。我们使用CNT来计数,间接实现计时的功能。经过预分频后的时钟频率,就是测周法的标准频率F
    之后,GPIO输入一个待测的方波信号,经过TI1FP1为上升沿触发,之后数据选择器选择直连通道,分频器选择不分频。当TI1FP1出现上升沿之后,CNT的值就会被CCR1捕获;同时触发源选择模块选择TI1FP1为触发信号,从模式选择复位操作。则TI1FP1信号完成两个操作:捕获CNT的值到CCR1中、清零CNT。当然这里存在先后顺序,先进行捕获后进行清0;或者存在非阻塞的同时转移。当电路不断工作是,CCR1中的值始终是最新一个周期的计数值,即测周法的计次数 N 
     

    这里需要注意以下两点:

    1、CNT的计数值是有上限的。由于ARR最大为65535,故CNT最大也只能计65535个数。如果信号频率太低,CNT的计数值可能会溢出。

    2、从模式的触发源选择中有TI1FP1和TI2FP2,但是没有TI3和TI4的信号。所以如果要使用从模式自从清零CNT,就必须使用CH1或CH2作为输入。对于CH3和CH4,就只能开启捕获中断,在中断中手动清0了(程序会处于频繁中断的状态,比较占用软件资源)。

     例如:方波信号经过TM1_CH1通道进来,经过数据选择器(非异或)成为TI1,经过滤波操作和边沿检测成为TI1FP1,再经过数据选择器的非交叉通道成为IC1(注:选择交叉输入则输入的是TM1_CH2)。再经过是否分频操作。TI1FP1经过从触发模式让时基单元触发复位功能,当CCR1捕获到上升沿时CNT复位。

     原理解释:方波信号经过TM1_CH1通道进来,经过数据选择器(非异或)成为TI1,经过滤波操作和边沿检测成为TI1FP1和TF1FP2,两个信号边沿选择是独立的。,TI1FP1经过数据选择器的非交叉通道成为IC1(注:选择交叉输入则输入的是TM1_CH2)。TI1FP2经过第二个数据选择器的交叉通道成为IC2.再都经过是否分频操作。TI1FP1经过从触发模式让时基单元触发上升沿复位功能,当CCR1捕获到上升沿时CNT复位,CCR1记录了一个周期时间,而CCR2捕获下降沿时间即半周期。故可用来计算占空比。

    四、输入捕获测量PWM波形的频率和占空比

    4.1、输入捕获相关库函数

    1、输入捕获初始化配置

    // 输入捕获IC初始化函数
    // 输入捕获有四个通道,但是只有一个初始化函数,通道选择在结构体成员变量中,这一点与输出比较不同
    void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
     
    // 给IC初始化函数中的结构体参数赋一个默认值
    void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
     
    // 快速配置PWMI模式(自动将另一个通道配置为相反的模式)
    void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
     
    // 单独写入时基单元的PSC,第三个参数与预装载有关
    void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
     
    // 单独配置四个通道的预分频器,结构体中也可以配置,效果相同
    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的值(与输出比较中的TIM_SetComparex函数对应)
    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);
     

    2、主从触发模式配置

    // 选择输入(从模式)触发源TRGI
    void TIM_SelectInputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
     
    // 选择输入(主模式)触发源TRGO
    void TIM_SelectOutputTrigger(TIM_TypeDef* TIMx, uint16_t TIM_TRGOSource);
     
    // 选择从模式需要执行的操作
    void TIM_SelectSlaveMode(TIM_TypeDef* TIMx, uint16_t TIM_SlaveMode);

    4.2、输入捕获IC测量频率
    为了测量外部信号的频率和占空比,首先要产生一个信号以供测量。博主手头也没有信号发生器,在这里与课程中演示的操作相同,先使用PWM模块在PA0端口输出一个频率和占空比都可调的波形,之后在PA6端口测量该波形的频率和占空比。
      接线图和程序源码如下所示:

     PWM.c

    #include "stm32f10x.h"                  // Device header
    
    void PWM_Init(void)
    {
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    //	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
    //	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	TIM_InternalClockConfig(TIM2);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;		//PSC
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    	
    	TIM_OCInitTypeDef TIM_OCInitStructure;
    	TIM_OCStructInit(&TIM_OCInitStructure);
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    	
    	TIM_Cmd(TIM2, ENABLE);
    }
    
    void PWM_SetCompare1(uint16_t Compare)
    {
    	TIM_SetCompare1(TIM2, Compare);
    }
    
    void PWM_SetPrescaler(uint16_t Prescaler)
    {
    	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
    }
    

    IC.C

    #include "stm32f10x.h"                  // Device header
    
    void IC_Init(void)
    {
    	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;  //TM3_CH1引脚:PA6
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	TIM_InternalClockConfig(TIM3); //采用内部时钟72MHZ
    	
    	//时基单元配置
    	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_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;  //指定输入捕获分频器 DIV1:不分频 .二分频就是每隔两个上升沿有效一次
    	TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; 
        //输入选择,选择直连通道或者是交叉通道。配置的是TI1FP1和IC1之间的数据选择器的
    	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)+1);  //获取TIM3输入捕获寄存器_1的值。
    	//TIM_Prescaler = 72 - 1  72MHZ/72=1000000  故10^-6*TIM_GetCapture1(TIM3)=周期
    }
    

    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_SetPrescaler(720 - 1);			//Freq = 72M / (PSC + 1) / 100
    	PWM_SetCompare1(50);				//Duty = CCR / 100
    	
    	while (1)
    	{
    		OLED_ShowNum(1, 6, IC_GetFreq(), 5);
    	}
    }
    

    现象:

     

    4.3、 PWMI模式同时测量频率和占空比

    接线图和程序源码如下所示:

     PWM.C

    #include "stm32f10x.h"                  // Device header
    
    void PWM_Init(void)
    {
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    	
    //	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    //	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
    //	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    	
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;		//GPIO_Pin_15;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	
    	TIM_InternalClockConfig(TIM2);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;		//ARR
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;		//PSC
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
    	
    	TIM_OCInitTypeDef TIM_OCInitStructure;
    	TIM_OCStructInit(&TIM_OCInitStructure);
    	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
    	TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
    	TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    	TIM_OCInitStructure.TIM_Pulse = 0;		//CCR
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    	
    	TIM_Cmd(TIM2, ENABLE);
    }
    
    void PWM_SetCompare1(uint16_t Compare)
    {
    	TIM_SetCompare1(TIM2, Compare);
    }
    
    void PWM_SetPrescaler(uint16_t Prescaler)
    {
    	TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
    }
    

    IC.C

    #include "stm32f10x.h"                  // Device header
    
    void IC_Init(void)
    {
    	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;		//PSC
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 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_PWMIConfig(TIM3, &TIM_ICInitStructure);  //快速配置PWMI模式(自动将另一个通道配置为相反的模式)
        
    	//从模式配置
    	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) + 1);
    }
    
    
    //计算占空比
    uint32_t IC_GetDuty(void)
    {
    	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);
    }
    

    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_SetPrescaler(720 - 1);			//Freq = 72M / (PSC + 1) / 100
    	PWM_SetCompare1(50);				//Duty = CCR / 100
    	
    	while (1)
    	{
    		OLED_ShowNum(1, 6, IC_GetFreq(), 5);
    		OLED_ShowNum(2, 6, IC_GetDuty(), 2);
    	}
    }
    

    4.4、 测周法测量频率性能评估

     

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32 TIM输入捕获(IC)详解

    发表回复