系列文章目录

STM32单片机系列专栏

C语言术语和结构总结专栏


文章目录

1. IC输入捕获

2. 频率测量

3. 主模式、从模式、触发源选择

4. 输入捕获基本结构

5. PWMI模式

6. 代码示例

6.1 PWM.c

6.2 PWM.h

6.3 IC.c

6.4 IC.h

6.5 完整工程文件


输出比较可以看下面这篇文章:

STM32定时器的OC比较和PWM

1. IC输入捕获

输入捕获(Input Capture,简称IC)是微控制器中定时器功能之一,主要用于精确地捕获外部事件(如信号的边沿)发生的时刻。在STM32中,IC功能通过定时器来实现,可以对外部输入信号的上升沿或下降沿进行时间的捕获,通过读取捕获时定时器的计数器(CNT)的值,从而知道外部事件发生的确切时刻。并且当前CNT的值将被锁存到CCR中。

输入捕获功能有以下功能:

  • 测量两个事件之间的时间间隔。
  • 测量PWM波形的频率、占空比、脉冲间隔,电平持续时间等。
  • 测量外部信号的脉宽。

每个高级定时器和通用定时器都有4个输入捕获通道。可配置为PWMI模式(PWM输入模式),同时测量频率和占空比,也可以配合主从触发模式,实现硬件全自动测量。这两个模式的组合,可以实现全自动检测,不需要中断也不影响主程序,从而减轻CPU的负担。

注意因为输入捕获和输出比较使用的为同一个寄存器,所以输出捕获和输出比较只能使用一个,不能同时使用。

2. 频率测量

STM32的TIM输入捕获和PWMI详解-LMLPHP测频法(适合高频信号):在闸门时间T内(通常为1s),对上升沿计次,得到N,则频率

fx = N / T  在单位时间内出现的脉冲数量越多,脉冲的频率就越高。

测周法(适合低频信号):两个上升沿内持续的时间(因为周期的倒数就是频率),使用一个已知的标准频率fc的计次时钟来驱动计数器,从第一个上升沿到下一个上升沿,得到N ,则频率

fx = = fc / N   在给定的周期内,脉冲数量保持不变时,周期越长,每个脉冲的宽度就越宽。

中界频率:测频法与测周法误差相等的频率点

fm = √(fc  / T)   这个指标是用来描述在脉冲调制中,信号的中心频率位置。

3. 主模式、从模式、触发源选择

主模式可以将定时器内部的信号映射到TRGO引脚,用于触发其他的外设。

从模式可以接收其他外设或自身外设的信号,用于控制自身定时器的运行,也就是被其他的信号控制。

触发源选择是选择从模式的触发信号源,选择一个指定的信号,得到TRGI,通过TRGI去触发从模式。从模式可以在列表中选择一项操作来自动执行。例如:如果想让TI1FP1信号自动触发CNT清零,触发源选择TI1FP1,从模式选择Reset,实现硬件全自动。

STM32的TIM输入捕获和PWMI详解-LMLPHP

主模式:

  • 复位 - 主输出TRGO不激活。
  • 使能 - 定时器计数器使能时TRGO被激活。
  • 更新 - 每当定时器更新事件发生时,比如计数器溢出时,TRGO被激活。
  • 比较匹配 - 比如当定时器的比较匹配事件发生时,如CCR1匹配时,TRGO被激活。
  • 比较 - OC1REF的比较匹配事件激活TRGO。
  • 比较 - OC4REF的比较匹配事件激活TRGO。

从模式:

  • 关闭从模式,定时器不受外部信号控制。
  • 编码器模式1,使用内部TRGI(定时器内部触发输入)作为时钟源,通常与编码器的输出相连,用于计数编码器的脉冲。
  • 编码器模式2,同上,但使用两个内部触发输入,允许同时检测方向和速度。
  • 编码器模式3,同上,但使用两个内部触发输入,允许同时检测方向和速度,并且可以检测到位置信息。
  • 复位模式,当TRGI发生时,定时器计数器会被复位。
  • 门控模式,TRGI信号作为启动和停止计数的使能信号。
  • 触发模式,定时器仅在TRGI信号发生时更新其寄存器,并开始计数。
  • 外部时钟模式1,使用TRGI作为定时器的时钟源。

4. 输入捕获基本结构

STM32的TIM输入捕获和PWMI详解-LMLPHP

当配置好时基单元后,启动定时器,此时CNT就会在预分频器之后的时钟驱动下,不断自增。预分频器之后的时钟频率。就是驱动CNT的标准频率fc。

下面的GPIO输入方波信号,经过滤波器和边沿检测,选择TI1FP1为上升沿触发,之后输出选择直连的通道,分频器选择不分频。当TI1FP1出现上升沿之后,CNT(最大65535)的当前计数值会进入CCR1中。同时触发源选择。选择TI1FP1为触发信号,从模式选择复位模式,从而清零CNT。

5. PWMI模式

STM32的TIM输入捕获和PWMI详解-LMLPHP

这部分前面和输入捕获一样,但后面使用了两个通道,可以同时测量周期和占空比,TI1FP2为下降沿触发。这样CCR1就是一整个周期的计数值,CCR2是高电平之间的计数值。此时用CCR2除以CCR1 就是占空比。

6. 代码示例

第一步:开始RCC时钟,打开将要使用的TIM外设时钟和GPIO外设时钟。

第二步:GPIO初始化,把GPIO配置为输入模式,一般为上拉输入或浮空输入模式。

第三步:配置时基单元,让CNT计数器在内部时钟的驱动下自增运行,PSC预分频器、CNT计数器和ARR自动重装载,也包括包括时钟源选择。

第四步:配置输入捕获单元,包括滤波器、极性、直连通道/交叉通道、分频器。

第五步:选择从模式的触发源(TI1FP1)

第六步:选择触发后执行的操作,这里执行Reset操作。

第七步:调用TIM_Cmd函数,开启定时器。

下面的代码实现功能为: 通过PWMI得到时钟频率和占空比。

6.1 PWM.c

要使用的库函数文件依然为:stm32f10x_tim.h,拖到最下面,在这里可以找到定时器TIM需要使用到的函数。

#include "stm32f10x.h" 

//PWM初始化
void PWM_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//GPIO重映射
//	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);			//开启AFIO的时钟,重映射必须先开启AFIO的时钟
//	GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);			//将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//	GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);		//将JTAG引脚失能,作为普通GPIO引脚使用
	
	//GPIO初始化
	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);							//将PA0引脚初始化为复用推挽输出	
																	//受外设控制的引脚,均需要配置为复用模式		
	
	//配置时钟源
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	//时基单元初始化
	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_TimeBaseInit,配置TIM2的时基单元
	
	//输出比较初始化
	TIM_OCInitTypeDef TIM_OCInitStructure;							//定义结构体变量
	TIM_OCStructInit(&TIM_OCInitStructure);							//结构体初始化,若结构体没有完整赋值
																	//则最好执行此函数,给结构体所有成员都赋一个默认值
																	//避免结构体初值不确定的问题
	TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;				//输出比较模式,选择PWM模式1
	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_OC1Init,配置TIM2的输出比较通道1
	
	//TIM使能
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

//PWM设置CCR
void PWM_SetCompare1(uint16_t Compare)
{
	TIM_SetCompare1(TIM2, Compare);		//设置CCR1的值
}

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

RCC_APB1PeriphClockCmd

  • TIM2 代表定时器2,它是STM32的一个基础硬件定时器。在STM32的某些系列中,TIM2连接到的是APB1总线。
  • ENABLE 是一个宏定义,用来开启某项功能,这里用来开启TIM2的时钟。如果传递 DISABLE 则会关闭外设的时钟。
  • 简单来说,RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); 这行代码的作用是开启连接到APB1总线的定时器2(TIM2)的时钟。只有开启了时钟,程序中关于TIM2的其他功能(如计时、计数、PWM发生等)才能正常工作。

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

  • GPIO的初始化中,选择AF_PP复用推挽输出,因为对于普通的开漏推挽输出,引脚的控制权是来自于输出数据寄存器的,如果想用定时器来控制引脚,就需要使用复用开漏/推挽输出模式。

 

6.2 PWM.h

接着是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

6.3 IC.c

在这段代码中,我们设置了一个STM32的定时器TIM3以输入捕获(IC)模式运行。代码的主要目的是初始化TIM3以捕获来自外部信号(从PWM波)的频率和占空比。

#include "stm32f10x.h"

//输入捕获初始化
void IC_Init(void)
{
	//开启时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);			//开启TIM3的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	//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);							//将PA6引脚初始化为上拉输入
	
	//配置时钟源
	TIM_InternalClockConfig(TIM3);		//选择TIM3为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	//时基单元初始化
	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_TimeBaseInit,配置TIM3的时基单元
	
	//PWMI模式初始化
	TIM_ICInitTypeDef TIM_ICInitStructure;							//定义结构体变量
	TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;				//选择配置定时器通道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使能
	TIM_Cmd(TIM3, ENABLE);			//使能TIM3,定时器开始运行
}

//获取输入捕获的频率
uint32_t IC_GetFreq(void)
{
	return 1000000 / (TIM_GetCapture1(TIM3) + 1);		//测周法得到频率fx = fc / N,这里不执行+1的操作也可以
}

//获取输入捕获的占空比
uint32_t IC_GetDuty(void)
{
	return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1);	//占空比Duty = CCR2 / CCR1 * 100,这里不执行+1的操作也可以
}

6.4 IC.h

同样这部分引用声明一下即可

#ifndef __IC_H
#define __IC_H

void IC_Init(void);
uint32_t IC_GetFreq(void);
uint32_t IC_GetDuty(void);

#endif

 

6.5 完整工程文件

STM32通过PWMI模式测频率占空比

05-08 09:53