【江协STM32】6-5 TIM输入捕获、输入捕获模式测频率&PWMI模式测频率占空比

1. 输入捕获

1.1 输入捕获简介 

  • IC(Input Capture)输入捕获
  • 输入捕获模式下,当通道输入引脚出现指定电平跳变时,当前CNT的值将被锁存到CCR中,可用于测量PWM波形的频率、占空比、脉冲间隔、电平持续时间等参数
  • 每个高级定时器和通用定时器都拥有4个输入捕获通道
  • 可配置为PWMI模式,同时测量频率和占空比
  • 可配合主从触发模式,实现硬件全自动测量
  • 通用定时器框图(输入捕获电路为CCR左侧部分)
  • TIMx_CH1、TIMx_CH2、TIMx_CH3三个输入引脚后有一个异或门,当三个输入引脚的任何一个有电平翻转时,输出引脚就产生一次电平翻转。异或门的输出通过数据选择器到达输入捕获通道1,如果数据选择器选择上面一路,那么输入捕获1的输入就是3个引脚的异或值;如果选择下面一路,则异或门不起作用。这部分功能主要用于控制三相无刷电机。
  • 输入滤波器和边沿检测器每一路实际上有两套,第一套电路,经过滤波和极性选择得到TI1FP1(TI1 Filter Polarity 1),输入给通道1的后续电路;第二套电路,经过另一个滤波和极性选择得到TI1FP2,输入给通道2的后续电路。因此可以实现交叉输入,使CH2引脚输入给通道1,CH1引脚输入给通道2。这样做的目的,一方面,可以灵活切换后续捕获电路的输入;另一方面,可以将一个引脚的输入同时映射到两个捕获单元,这也是PWMI模式的经典结构。
  • TRC信号也可以作为捕获部分的输入,来源于框图上部,设计目的也是用于无刷电机的驱动。
  • 分频后的触发信号就可以触发捕获电路进行工作了,每来一个触发信号,CNT的值就会向CCR转运一次,转运的同时发生捕获事件CC1I,该事件会在状态寄存器标志位,同时可以产生中断。
  •  1.2 输入捕获通道

  • TI1来源于CH1引脚输入,fDTS是滤波器的采样时钟来源,TI1F是滤波后的信号
  • CCMR1寄存器的ICF位可以控制滤波器的参数(参数见手册14.4.7)
  • CCER寄存器的CC1P位可以选择极性(上升沿还是下降沿),最终得到TI1FP1触发信号,通过数据选择器,进入通道1后续的捕获电路
  • 实际上还有一套相同的输入滤波器和边沿检测器电路,下图未画出
  • CCMR1寄存器的CC1S位可以对数据选择器进行选择
  • CCMR1寄存器的ICPS位可以配置分频器,不分频、2分频、4分频、8分频
  • CCER寄存器的CC1E位可以控制输出使能或失能
  • 每捕获一次CNT的值,均需要将CNT清零,以便下一次捕获,清零动作可以通过TI1FP1和TI1F_ED触发从模式实现
  •  1.3 主从触发模式

  • 主从触发模式就是主模式、从模式和触发源选择三个功能的简称
  • 主模式可以将定时器内部的信号,映射到TRGO引脚,用于触发别的外设
  • 从模式是接收其他外设或者自身外设的信号,用于控制自身定时器的运行
  • 触发源选择用来选择从模式的触发信号源
  •     如果想让TI1FP1信号自动触发CNT清零,那么触发源选择就选中TI1FP1,从模式执行Reset操作,实现硬件的全自动测量。

    1.4 输入捕获基本结构

    经过预分频后的时钟频率,就是驱动CNT的标准频率fc = 72M / ( PSC + 1)。

    测周法流程:

        GPIO输入的信号经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输入选择直连的通道,分频器选择不分频。当TI1FP1出现上升沿后,CNT的当前计数值转运到CCR1,同时触发源选择选中TI1FP1为触发信号,从模式选择Reset。(先转运值,再触发CNT清零)

    GPIO输入方波信号:出现上升沿,CNT的值转运到CCR1(CCR1 = CNT),这是输入捕获自动执行的。然后CNT = 0,清零计数器,这是从模式自动执行的。

    注意事项:

  • ARR一般设置为最大值65535,CNT最大只能计65535个数,如果信号频率太低,CNT计数值可能会溢出。
  • 从模式的触发源选择,只有TI1FP1和TI1FP2,没有TI3和TI4的信号,所以如果想使用从模式自动清零CNT,就只能用通道1和通道2。对于通道3和通道4,只能通过开启捕获中断,在中断里手动清零。
  • 输入捕获通道1的GPIO口输入方波信号

    1.5 PWMI基本结构

    PWMI模式使用两个通道同时捕获一个引脚,可以同时测量周期和占空比。

    TI1FP1配置上升沿触发,触发捕获和清零CNT,用于捕获周期。TI1FP2配置下降沿触发。

    GPIO输入方波信号:开始,上升沿,CCR1捕获,同时清零CNT,之后CNT++。在下降沿时刻,触发CCR2捕获,不触发CNT清零,CNT的值保存到CCR2,此时的CNT值就是高电平期间的CNT值,之后CNT++。直到下一次上升沿,CCR1捕获周期,CNT清零。CCR2/CCR1即为占空比。

    输入捕获通道1的GPIO口输入方波信号

    2. 频率测量

        STM32测频率只能测量数字信号。如果测量正弦波,还需要信号预处理电路,把正弦波转换为数字信号。

  • 测频法:在闸门时间T内,对上升沿计次,得到N,则频率 f_x=\mathrm{N/T}
    适合测量高频信号,当待测信号频率大于中界频率时,测频法误差更小,选用测频法更合适。
    用之前学过的外设可以实现(对射式红外传感器计次、定时器外部时钟)
  • 测周法:两个上升沿内,以标准频率fc计次,得到N ,则频率 f_x=f_c/\mathrm{N}
    适合测量低频信号,当待测信号频率小于中界频率时,测周法误差更小,选用测周法更合适。
  • 中界频率:测频法与测周法误差相等的频率点 f_m=\sqrt{f_c/\mathrm{T}}
  • 使用测周法测量频率,每次捕获后,CNT都需要清零,这个步骤可以用主从触发模式自动完成。 

    3. 输入捕获模式测频率

    3.1 接线图

        测量信号的输入引脚是PA6,待测的PWM信号是STM32自己生成的,输出引脚是PA0。如果接信号发生器,记得共地。

    3.2 代码 

    输出待测PWM信号:

        需要在原有PWM生成函数中增加一个用来便捷调节PWM频率的函数,调节ARR会同时影响频率和占空比,调节PSC只影响频率。因此计划固定ARR为100-1,通过调节PSC来改变PWM频率。
    需要用到单独写入PSC的函数:TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)

    PWM.c

    #include "stm32f10x.h"                  // Device header
    
    void PWM_Init(void)
    {
        //	1、初始化时钟RCC TIM2
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//	TIM2是APB1总线的外设
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        
    	//  4、配置GPIO
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	//	选择时基单元的时钟源
    	TIM_InternalClockConfig(TIM2);//	因为定时器上电后默认就是使用内部时钟,所以此行可省略
    	
    	//	2、配置时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//	指定时钟分频,设置的是输入滤波的采样频率,与时基单元关系不大。这里随便选一个
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//	计数器模式,选择向上计数
    	//	计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)=72M/(PSC+1)/(ARR+1)
    	//	注意:PSC和ARR的取值都要在0~65535之间
    	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//	自动重装器(ARR)的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//	预分频器(PSC)的值
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//	重复计数器的值(高级定时器才有,不是CNT),不需要用,直接给0
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//	刚初始化完会生成一次更新事件,会使Num刚复位就显示1
    	//	手动清除一次更新事件,避免Num从1开始
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);//	手动清除更新中断标志位,避免刚初始化完就进中断
    	
    	//  3、配置输出比较单元
        TIM_OCInitTypeDef TIM_OCInitStructure;
        TIM_OCStructInit(&TIM_OCInitStructure);// 给结构体赋初始值。防止修改为高级定时器时,由于参数设置不完整出现异常。
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//    输出比较极性。高极性,就是极性不翻转,REF波形直接输出
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//   输出状态。使能
        TIM_OCInitStructure.TIM_Pulse = 50;// 设置CCR寄存器的值
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    	
    	//	5、运行控制
    	TIM_Cmd(TIM2, ENABLE);//	启动定时器
    }
    
    //  通过运行中更改CCR值来调节占空比,使LED呈现呼吸灯效果
    //  注意:设置CCR的值不是直接改变占空比,占空比是CCR和ARR+1共同决定的。因为此时ARR+1=100,所以CCR的值等于占空比
    void PWM_SetCompare1(uint16_t Compare)
    {
        TIM_SetCompare1(TIM2, Compare);//   调节CCR
    }
    
    //  调节PSC。用来调整频率
    void PWM_SetPrescaler(uint16_t Prescaler)
    {
        TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
    }
    

    PWM.h 

    #ifndef __PWM_H
    #define __PWM_H
    
    void PWM_Init(void);
    void PWM_SetCompare1(uint16_t Compare);
    void PWM_SetPrescaler(uint16_t Prescaler);
    
    #endif
    

    输入捕获初始化步骤:

    1. RCC开启GPIO和TIM时钟
    2. GPIO初始化,配置成输入模式(一般选择上拉输入或浮空输入)
    3. 配置时基单元,使CNT计数器在内部时钟的驱动下自增运行
    4. 配置输入捕获单元,包括滤波器、极性、直连通道还是交叉通道、分频器
    5. 选择从模式TRGI触发源(TI1FP1)
    6. 选择触发后执行的操作(Reset)
    7. 调用TIM_Cmd函数,开启定时器

    查阅引脚定义表,TIM3的通道1、通道2、通道3和通道4,分别对应PA6、PA7、PB0和PB1

    初始化捕获单元有两个函数:TIM_ICInit(配置一个通道)和TIM_PWMIConfig(配置两个通道)

    根据前面的分析,ARR设置最大值65536-1,防止计数溢出。PSC决定了测周法的标准频率fc

    读取CCR值的函数: TIM_GetCapture1(TIM_TypeDef* TIMx)

    PWM.c

    #include "stm32f10x.h"                  // Device header
    
    void PWM_Init(void)
    {
        //	1、初始化时钟RCC TIM2
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//	TIM2是APB1总线的外设
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        
    	//  4、配置GPIO
    	GPIO_InitTypeDef GPIO_InitStructure;
    	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//	复用推挽输出
    	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
    	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    	GPIO_Init(GPIOA, &GPIO_InitStructure);
    	//	选择时基单元的时钟源
    	TIM_InternalClockConfig(TIM2);//	因为定时器上电后默认就是使用内部时钟,所以此行可省略
    	
    	//	2、配置时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//	指定时钟分频,设置的是输入滤波的采样频率,与时基单元关系不大。这里随便选一个
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//	计数器模式,选择向上计数
    	//	计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)=72M/(PSC+1)/(ARR+1)
    	//	注意:PSC和ARR的取值都要在0~65535之间
    	TIM_TimeBaseInitStructure.TIM_Period = 100 - 1;//	自动重装器(ARR)的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1;//	预分频器(PSC)的值
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//	重复计数器的值(高级定时器才有,不是CNT),不需要用,直接给0
    	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//	刚初始化完会生成一次更新事件,会使Num刚复位就显示1
    	//	手动清除一次更新事件,避免Num从1开始
    	TIM_ClearFlag(TIM2, TIM_FLAG_Update);//	手动清除更新中断标志位,避免刚初始化完就进中断
    	
    	//  3、配置输出比较单元
        TIM_OCInitTypeDef TIM_OCInitStructure;
        TIM_OCStructInit(&TIM_OCInitStructure);// 给结构体赋初始值。防止修改为高级定时器时,由于参数设置不完整出现异常。
        TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
        TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//    输出比较极性。高极性,就是极性不翻转,REF波形直接输出
        TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;//   输出状态。使能
        TIM_OCInitStructure.TIM_Pulse = 50;// 设置CCR寄存器的值
    	TIM_OC1Init(TIM2, &TIM_OCInitStructure);
    	
    	//	5、运行控制
    	TIM_Cmd(TIM2, ENABLE);//	启动定时器
    }
    
    //  通过运行中更改CCR值来调节占空比,使LED呈现呼吸灯效果
    //  注意:设置CCR的值不是直接改变占空比,占空比是CCR和ARR+1共同决定的。因为此时ARR+1=100,所以CCR的值等于占空比
    void PWM_SetCompare1(uint16_t Compare)
    {
        TIM_SetCompare1(TIM2, Compare);//   调节CCR
    }
    
    //  调节PSC。用来调整频率
    void PWM_SetPrescaler(uint16_t Prescaler)
    {
        TIM_PrescalerConfig(TIM2, Prescaler, TIM_PSCReloadMode_Immediate);
    }
    

    PWM.h

    #ifndef __PWM_H
    #define __PWM_H
    
    void PWM_Init(void);
    void PWM_SetCompare1(uint16_t Compare);
    void PWM_SetPrescaler(uint16_t Prescaler);
    
    #endif
    

    IC.c

    #include "stm32f10x.h"                  // Device header
    
    void IC_Init(void)
    {
        //	1、初始化时钟RCC TIM3
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//	TIM2是APB1总线的外设
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        
    	//  2、配置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);
    	//	选择时基单元的时钟源
    	TIM_InternalClockConfig(TIM3);//	因为定时器上电后默认就是使用内部时钟,所以此行可省略
    	
    	//	3、配置时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//	指定时钟分频,设置的是输入滤波的采样频率,与时基单元关系不大。这里随便选一个
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//	计数器模式,选择向上计数
    	//	计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)=72M/(PSC+1)/(ARR+1)
    	//	注意:PSC和ARR的取值都要在0~65535之间
    	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//	自动重装器(ARR)的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//	预分频器(PSC)的值
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//	重复计数器的值(高级定时器才有,不是CNT),不需要用,直接给0
    	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//	刚初始化完会生成一次更新事件,会使Num刚复位就显示1
        
        //  4、初始化输入捕获单元
        TIM_ICInitTypeDef TIM_ICInitStruct;
        TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;// 配置哪个通道
        TIM_ICInitStruct.TIM_ICFilter = 0xF;// 配置输入捕获的滤波器
        TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;//   边沿检测,选择极性。上升沿触发
        TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;//  触发信号分频器。不分频就是每次触发均有效,2分频就是每隔一次有效一次
        TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;//  选择触发信号从哪个引脚输入,可以选择直连通道或交叉通道
        TIM_ICInit(TIM3, &TIM_ICInitStruct);
        
        //  5、配置TRGI触发源为TI1FP1
        TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
        
        //  6、配置从模式为Reset
        TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
        
        //  7、调用TIM_Cmd函数,开启定时器
        TIM_Cmd(TIM3, ENABLE);
    }
    
    //  计算频率
    uint32_t IC_GetFreq(void)
    {
        return 1000000 / TIM_GetCapture1(TIM3);//  fx=fc/N,fc=72M/(PSC+1)
    }
    

    IC.h 

    #ifndef __IC_H
    #define __IC_H
    
    void IC_Init(void);
    uint32_t IC_GetFreq(void);
    
    #endif
    

    main.c

    #include "stm32f10x.h"                  // Device 
    #include "Delay.h"
    #include "OLED.h"
    #include "PWM.h"
    #include "IC.h"
    
    uint8_t i;
    
    int main(void)
    {
    		OLED_Init();
            OLED_ShowString(1,1,"Freq:00000Hz");
            IC_Init();
            PWM_Init();
            //  输出待测信号至PA0
            PWM_SetPrescaler(720 - 1);// Freq = 72M / (PSC+1) / 100
            PWM_SetCompare1(50);//  Duty = CCR / 100
    		while(1)
    		{
                OLED_ShowNum(1, 6, IC_GetFreq(), 5);
    		}
    }
    

    其他引用的头文件和c代码可在此处查阅:OLED.h(【江协STM32】4 OLED调试工具,第5节)、  Delay.h(【江协STM32】3-2 LED闪烁&LED流水灯&蜂鸣器,第1.3节)

    程序下载后测量频率为1001Hz,多了1Hz,可能是因为计数刚到1000Hz的那个数时,信号刚好跳变,由于电路结构或其他原因,导致这一个数刚好没计到,产生了误差。可以在程序补上误差,把IC.c中的IC_GetFreq函数改为:

    //  计算频率
    uint32_t IC_GetFreq(void)
    {
        return 1000000 / (TIM_GetCapture1(TIM3) + 1);//  fx=fc/N,fc=72M/(PSC+1)
    }

    4. PWMI模式测频率占空比

    4.1 接线图

    4.2 代码

    对上一个程序进行修改。

    第4步配置输入捕获单元,需要配置成两个通道同时捕获同一个引脚的模式,可以使用TIM_PWMIConfig函数。使用该函数,比如配置通道1,直连,上升沿,函数会自动配置通道2,交叉,下降沿。此函数只用于通道1和通道2的配置,不能操作通道3和通道4。

    IC.c

    #include "stm32f10x.h"                  // Device header
    
    void IC_Init(void)
    {
        //	1、初始化时钟RCC TIM3
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//	TIM2是APB1总线的外设
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
        
    	//  2、配置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);
    	//	选择时基单元的时钟源
    	TIM_InternalClockConfig(TIM3);//	因为定时器上电后默认就是使用内部时钟,所以此行可省略
    	
    	//	3、配置时基单元
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//	指定时钟分频,设置的是输入滤波的采样频率,与时基单元关系不大。这里随便选一个
    	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//	计数器模式,选择向上计数
    	//	计数器溢出频率:CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)=72M/(PSC+1)/(ARR+1)
    	//	注意:PSC和ARR的取值都要在0~65535之间
    	TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1;//	自动重装器(ARR)的值
    	TIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1;//	预分频器(PSC)的值
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//	重复计数器的值(高级定时器才有,不是CNT),不需要用,直接给0
    	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//	刚初始化完会生成一次更新事件,会使Num刚复位就显示1
        
        //  4、初始化输入捕获单元
        TIM_ICInitTypeDef TIM_ICInitStruct;
        TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;// 配置哪个通道
        TIM_ICInitStruct.TIM_ICFilter = 0xF;// 配置输入捕获的滤波器
        TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;//   边沿检测,选择极性。上升沿触发
        TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;//  触发信号分频器。不分频就是每次触发均有效,2分频就是每隔一次有效一次
        TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;//  选择触发信号从哪个引脚输入,可以选择直连通道或交叉通道
        //TIM_ICInit(TIM3, &TIM_ICInitStruct);//    只初始化通道1
        TIM_PWMIConfig(TIM3, &TIM_ICInitStruct);//  对于同一个CH1输入,同时初始化捕获通道1和2
        
        //  5、配置TRGI触发源为TI1FP1
        TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);
        
        //  6、配置从模式为Reset
        TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);
        
        //  7、调用TIM_Cmd函数,开启定时器
        TIM_Cmd(TIM3, ENABLE);
    }
    
    //  计算频率
    uint32_t IC_GetFreq(void)
    {
        return 1000000 / (TIM_GetCapture1(TIM3) + 1);//  fx=fc/N,fc=72M/(PSC+1)
    }
    
    //  计算占空比
    uint32_t IC_GetDuty(void)
    {
        return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);// CCR2 / CCR1
        //  显示范围为0~1,如果想显示整数,可以将其扩大100倍
        //  经过实测,CCR总是少一个数,所以加1补上误差
    }
    

    IC.h

    #ifndef __IC_H
    #define __IC_H
    
    void IC_Init(void);
    uint32_t IC_GetFreq(void);
    uint32_t IC_GetDuty(void);
    
    #endif
    

    main.c

    #include "stm32f10x.h"                  // Device 
    #include "Delay.h"
    #include "OLED.h"
    #include "PWM.h"
    #include "IC.h"
    
    uint8_t i;
    
    int main(void)
    {
    		OLED_Init();
            OLED_ShowString(1,1,"Freq:00000Hz");
            OLED_ShowString(2,1,"Duty:00%");
            IC_Init();
            PWM_Init();
            //  输出待测信号至PA0
            PWM_SetPrescaler(7200 - 1);// Freq = 72M / (PSC+1) / 100
            PWM_SetCompare1(87);//  Duty = CCR / 100
    		while(1)
    		{
                OLED_ShowNum(1, 6, IC_GetFreq(), 5);
                OLED_ShowNum(2, 6, IC_GetDuty(), 2);
    		}
    }
    

    测频率的范围:目前的标准频率fc = 72M / (PSC + 1) = 1MHz,计数器最大计数到65535,所以测量的最低频率是1M / 65535 ≈ 15Hz。如果想要降低最低频率,可以增大PSC。

    作者:冰糖雪莲IO

    物联沃分享整理
    物联沃-IOTWORD物联网 » 【江协STM32】6-5 TIM输入捕获、输入捕获模式测频率&PWMI模式测频率占空比

    发表回复