HAL库STM32常用外设教程(六)——定时器 输入捕获

HAL_TIM_IC_CaptureCallback 函数 HAL_TIM_IC_CaptureCallback`是STM32 HAL库中处理输入捕获中断的核心回调函数。1. **DMA辅助捕获**:使用`HAL_TIM_IC_Start_DMA()`避免中断频繁触发。E --> F[触发HAL_TIM_IC_CaptureCallback] 阅读详情
AI摘要

本文围绕STM32定时器输入捕获展开,介绍了输入捕获原理及相关HAL驱动,通过输入捕获检测方波占空比、PWM频率和占空比、用定时器ETR方式计算PWM脉冲数三个示例,详细讲解了STM32CubeMx设置、程序设计及测试结果,还对实验中出现的问题进行反思。


前言

1、STM32F407ZGT6
2、STM32CubeMx软件
3、keil5
内容简述:
通篇文章将涉及以下内容,如有错误,欢迎指出:
定时器有关输入捕获的HAL库驱动程序
(1)CubeMx配置
(2)TIM驱动程序
(3)输入捕获检测方波占空比
(4) 输入捕获检测PWM频率和占空比
(5)用定时器ETR方式计算PWM脉冲数


一、输入捕获原理及相关驱动

1.1 输入捕获原理

  输入捕获(intput capture)就是检测输入通道输入方波信号的跳变沿,并将发生跳变沿时的计数器的值锁存到CCR。使用输入捕获功能可以检测方波信号周期,从而计算方波信号的频率,也可以检测方波的占空比。其基本原理可以看成下面的几个步骤:

1、输入捕获通道配置:首先,需要选择一个特定的计时器作为输入捕获的计时源,并将其中的一个或多个通道配置为输入捕获模式。

2、捕获触发条件设置:根据需要,在输入捕获通道上设置触发条件,例如上升沿、下降沿或两者都有的边缘触发。

3、 外部信号检测:当外部信号满足触发条件(如上升沿)时,输入捕获通道会记录当前计时器的计数值并生成相应的中断或标志。

4、 捕获值获取:在中断服务程序或适当的时间点,可以读取输入捕获通道的捕获寄存器,获取记录的捕获值。

5、计算参数:通过对捕获值进行处理和计算,可以得到所需的参数,如脉冲宽度、频率、周期等。
下面将通过用输入捕获功能计算按键按下时高电平的占空比、输入捕获计算PWM的周期和频率两个例子讲解定时器输入捕获的用法。

1.2 输入捕获相关的HAL驱动

  输入捕获相关的HAL函数如表2.1 所示。这里仅列出了相关函数名,简要说明其功能相关函数在stm32f4xx_hal_tim.h中。
                 表2.1输入捕获相关HAL库函数

函数名功能描述
HAL_TIM_IC_Init()输入捕获初始化,需先执行HAL_TIM_Base_Init()进行定时器初始化
HAL_TIM_IC_ConfigChannel()输入通道配置
HAL_TIM_IC_Start()启动输入捕获,需要先执行HAL_TIM_Base_start()启动定时器
HAL_TIM_IC_Stop()停止输入捕获
HAL_TIM_IC_Start_IT()以中断方式启动输入捕获,需要先执行HAL_TIM_Base_start_IT()启动定时器
HAL_TIM_IC_Stop_IT()停止输入捕获
HAL_TIM_IC_GetState()返回定时器状态,与HAL_TIM_Base_GetState()功能相同
__HAL_TIM_SET_CAPTUREPOLARITY()设置捕获输入极性,上跳沿、下降沿或双边捕获
__HAL_TIM_SET_COMPARE()设置比较寄存器CCR的值
__HAL_TIM_GET_COMPARE()读取比较寄存器CCR的值
HAL_TIM_IC_CaptureCallback()产生捕获事件时的回调函数

二、 输入捕获检测方波占空比

2.1 原理

  如图2.1所示,如果两个上跳沿的捕获发生在同一个计数周期内,两个计数值分别为CCR1和CCR2,则方波的周期为CCR2-CCR1个计数周期,根据定时器的周期就可以计算出方波周期和频率。如果方波周期超过定时器的计数周期,或两次输入捕获发生在相邻的两个定时周期内,如图2.2中CCR2和CCR3,则只需将计数器的计数周期和UEV事件发生次数考虑进去即可,根据CCR2和CCR3计算的脉冲周期应该是 ARR - CCR1 + CCR2。
  输入捕获还可以对输入设置滤波,滤波系数0 ~ 15,用于输入抖动时的处理。输入捕获还可以设置与分频系数 N,数值N的取值为1、2、4或8,表示发生了N个时间才执行一次的捕获。

在这里插入图片描述

                   图2.1 捕获PWM高电平示意图(1)在这里插入图片描述
                   图2.2 捕获PWM高电平示意图(2)

  本次示例通过按键去提供一次高电平,测试按键按下时的高电平时间,所以需要将TIM输入捕获的引脚接到按键的引脚上,可以在CubeMx里面将按键引脚配置成定时器输入捕获模式(如图2.3)
通过观察开发板原理图,如图2.3 到 图2.5所示,可以发现KEY_UP按键的引脚可以实现配成TIM2_CH1的输入捕获通道。
在这里插入图片描述

                   图2.3 CubeMx里按键引脚配置

在这里插入图片描述
                   图2.4 按键原理图(1)

在这里插入图片描述                   图2.5 按键原理图(2)

2.2 STM32CubeMx设置

1、TIM配置
  如图所示,按照图2.6 中的步骤将TIM2的通道1配置输入捕获模式,还需要使能TIM2中断(在中断里面计数高电平脉冲)。
在这里插入图片描述                     图2.6 CubeMx里TIIM2输入捕获设置

在这里插入图片描述
                     图2.7 使能TIM2中断(1)

在这里插入图片描述
                     图2.8 使能TIM2中断(2)

  因为需要通过串口将高电平时间计算出来,所以实验也需要配置好串口,本次用到的是串口1
在这里插入图片描述
                     图2.9 使能TIM2中断(2)
配置完成后生成工程。

2.3 程序设计

  STM32CubeMx配置结束后,生成工程。先进行初步分析STM32CubeMx生成的工程,以便更好的理解STM32CubeMx里面的配置。
在这里插入图片描述
                     图2.10 Counter Settings
如图所示,逐行解释上图片中标记的代码

  • htim2.Instance = TIM2;
      将定时器设置为TIM2。
  • htim2.Init.Prescaler = 84-1; 
       设置预分频器的值为83(实际值减去1)。预分频器用于将输入时钟频率分频,从而得到更低的计数频率。在这里,输入时钟频率将被除以84,获得的计数频率为84Mhz /(84-1 +1) = 1MHz。
  • htim2.Init.CounterMode = TIM_COUNTERMODE_UP; 
       将计数模式设置为向上计数模式。在向上计数模式下,计数器从0递增到自动重载值。
  • htim2.Init.Period = 0xFFFFFFFF;  
      设置自动重载寄存器的值为0xFFFFFFFF(32位最大值)。这意味着计数器将从0递增到0xFFFFFFFF,然后重新从0开始循环。
  • htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
      设置时钟分频系数为不分频(TIM_CLOCKDIVISION_DIV1)。时钟分频用于进一步分频计数器的时钟频率。
  • htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
       启用自动重载预装载功能。当该功能启用时,新的自动重载值在下一个更新事件时生效,以避免在计数器工作时意外更改自动重载值。

在这里插入图片描述
                     图2.10 Counter Settings设置

  • sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
       这段代码的作用是将定时器的时钟源设置为内部时钟。sClockSourceConfig是一个结构体,其中的ClockSource成员用于配置定时器的时钟源。通过将ClockSource成员设置为TIM_CLOCKSOURCE_INTERNAL,代码指定定时器使用内部时钟作为计数时钟源。

在这里插入图片描述
               图2.11 TRGO Parameters和Input Capture Channel设置

  • sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
       配置定时器的主输出触发源为 TIM_TRGO_RESET,表示在每次定时器重置(复位)时触发主输出触发事件。具体说,当定时器的计数值达到最大值并发生溢出时,定时器将自动复位到初始值。此时,如果配置了主输出触发源为 TIM_TRGO_RESET,就会产生一个触发事件,可以用来同步其他外设或执行相关的操作

  • sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
       禁用主从模式,即该定时器不会作为其他定时器的主定时器。

  • sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
      设置输入捕获通道的触发极性为上升沿触发。

  • sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
      配置输入捕获通道的输入源选择为直接触发模式。捕获选择器设置为 TIM_ICSELECTION_DIRECTTI 时,定时器会直接读取输入端的电平变化,并进行相应的计数和捕获操作。这通常适用于需要测量输入信号的上升沿和下降沿的情况。
      当将输入捕获选择器设置为 TIM_ICSELECTION_INDIRECTTI 时,定时器会先通过一个互补触发器(Complementary Trigger)来产生一个间接触发信号。然后,根据该触发信号的边沿变化来触发输入捕获操作。这种设置通常适用于需要测量输入信号的互补边沿(例如正边沿和负边沿)或需要使用输入信号的互补版本进行计数的情况。

  • sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
      设置输入捕获通道的输入分频器预分频系数为1,TIM_ICPSC_DIV1指定输入信号的频率不进行预分频,直接用于输入捕获。

  • sConfigIC.ICFilter = 0;
      将输入捕获通道的滤波器配置为无滤波器。ICFilter参数表示滤波器的设置值,0表示禁用滤波器,即不对输入信号应用任何滤波。通过将ICFilter设置为0,可以确保输入捕获模式下的输入信号不受滤波器的影响,即直接获取输入信号的原始状态。这适用于需要实时响应输入信号变化或对输入信号精确测量的情况。

  了解了上面的 tim.c 里面的基础配置后,开始在生成的工程上编写用户代码 实现输入捕获功能。
(1)首先,需要对实现“串口重定向”,printf可以将指定字符打印到电脑的显示器上,但是要想使用printf在单片机中通过串口打印出来,就需要重定向到串口配置上。
注:如果用到的是串口2,将“串口重定向中的”USART1换成USART2即可。
在这里插入图片描述

/* 串口重定向 */
int fputc(int ch,FILE *f)
{
	while(!(USART1->SR &(1<<7)));
	USART1->DR = ch;
	return ch;
}	

(2)进行相关的变量定义。

在这里插入图片描述

uint32_t tim_value=0; /* 储存计数器的记录值 */ 
uint32_t tim_over=0;  /* 计数器溢出的个数 */
uint32_t time;   	  /* 高电平持续时间 */	

/* 捕获状态 */
typedef enum{
		idle	= 0,  /* 空闲 */
		runing, 	  /* 运行*/
		end			  /* 结束*/
}IC_STATE;

IC_STATE ic_state;	  /* 定义结构体变量 */

(3)启动定时器TIM2的中断模式,并开始输入捕获功能。

在这里插入图片描述

	HAL_TIM_Base_Start_IT(&htim2);																					/* 启动TIM2定时器的中断模式  */
	HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);															/* 以中断方式开启TIM2的输入捕获模式  */

(4) HAL_TIM_PeriodElapsedCallback() 函数是定时器计数溢出回调函数。当定时器的计数达到最大值并发生溢出时,该回调函数会被调用。在这段代码中,首先判断定时器是否为TIM2,然后检查状态是否为正在运行状态(ic_state == running)。如果满足条件,则将定时器溢出值(tim_over)自增一次。
HAL_TIM_IC_CaptureCallback() 函数是定时器输入捕获回调函数。它在定时器TIM2的输入捕获事件发生时被调用。在这段代码中,同样首先判断定时器是否为TIM2。然后根据当前状态判断执行不同的操作:

  • 如果状态为idle(空闲),则表示开始进行输入捕获。将状态设置为running(正在运行),计数值清零,将通道1的捕获极性设置为下降沿捕获,并将定时器的计数值也设置为0。
  • 如果状态不为idle,表示之前已经开始了输入捕获过程,此时需要停止输入捕获功能,将状态设置为end(结束),获取此时的计数值,然后将通道1的捕获极性设置为上升沿捕获。

在这里插入图片描述

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim2){								    /* 判断是否为定时器TIM2 */
		 if(ic_state == runing){					    /* 判断是否为是 正在运行状态 */
				tim_over ++;					        /* 定时器溢出值计数 */
		 }
	}
}
	
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim2){							         /* 判断是否为定时器TIM2 */
		 if(ic_state == idle){							 /* 判断状态是不是 空闲 */
				ic_state = runing;						 /* 将状态设置为 正在运行 */
				tim_value = 0;							 /* 计数值清零 */
				__HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING); /* 将TIM2的通道1捕获极性设置为下降沿捕获 */
			 __HAL_TIM_SET_COUNTER(&htim2,0);		     /* 将TIM2的计数值设置为0 */
		 }else{								             /* 此时不是空闲状态 */
				HAL_TIM_IC_Stop_IT(&htim2,TIM_CHANNEL_1);/* 停止定时器2输入捕获功能 */
				ic_state = end;						     /* 将状态设置为 结束 */
			  tim_value = HAL_TIM_ReadCapturedValue(&htim2,TIM_CHANNEL_1);	 /* 获取此时计数值 */
		 	 __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING);/* 将TIM2的通道1捕获极性设置为上升沿捕获 */
		 }
	}
}

2.4 示例结果

  接上串口后观察打印结果,如图2.12所示,其显示的高电平持续时间和图2.13中示波器测出来的相同。
请添加图片描述
                     图2.12 通过串口打印出的高电平时间

请添加图片描述
                     图2.13 通过示波器捕获的按键高电平时间

三、 输入捕获检测PWM频率和占空比

3.1 原理

  如图3.1所示,想要测出PWM的周期和频率,必须知道PWM的占空比和周期(周期的倒数 = 频率),从这个角度去考虑,要进行三次捕获才能实现要求。如果三个上跳沿的捕获发生在同一个计数周期内,如图3.1,发生三次捕获的计数值分别为CCR0,CCR1和CCR2,则方波的周期为CCR2-CCR1个计数周期,方波的高电平周期是CCR2-CCR1个计数周期,根据定时器的周期就可以计算出方波占空比和频率。如果方波周期超过定时器的计数周期,或两次输入捕获发生在相邻的两个定时周期内,如图3.2,发生3次捕获的计数值是CC0、CCR1和CCR2,则只需将计数器的计数周期和UEV事件发生次数N考虑进去即可,计算的脉冲周期应该是 ARR * N + CCR2 - CCR1。
注:在程序中为了更方便的进行计数和计算,在发生第一次上升沿捕获时,通过__HAL_TIM_SET_COUNTER()函数将定时器的值设置为0,相当于CCR0 = 0,这点在理解程序时需要注意。

在这里插入图片描述
                  图3.1 定时器输入捕获测量PWM(1)

在这里插入图片描述
                   图3.2 定时器输入捕获测量PWM(2)

3.2 STM32CubeMx设置

  因为此处需要产生一个PWM,此处用TIM2产生一个频率为1K的PWM方波,如何产生PWM请参考这篇文章: HAL库STM32常用外设教程(一)—— 定时器 输出PWM

  将定时器TIM3的通道1配置成输入捕获通道,如图3.3。
在这里插入图片描述
                   图3.3 定时器输入捕获配置

  使能定时器TIM3中断,如图3.4和3.5。

在这里插入图片描述
                   图3.4 定时器中断使能(1)

在这里插入图片描述
                 图3.5 定时器中断使能(2)

3.3 程序设计

(1)参考 2.3节 先进行 串口重定向,完成后再进行下面相关变量的定义。
在这里插入图片描述

uint16_t ccr1_cnt = 0;                   /* 发生第一次下升沿捕获时CCR1的值 */
uint16_t ccr2_cnt = 0;					 /* 发生第二次上升沿捕获时CCR1的值 */
uint16_t Period_cnt = 0;				 /* 发生计数器溢出事件的次数(过渡用) */
uint16_t Period_cnt1 =0;				 /* 发生计数器溢出事件的次数--计数1 */
uint16_t Period_cnt2 = 0;				 /* 发生计数器溢出事件的次数--计数2 */
uint16_t ic_flag = 0;					 /* 输入捕获标志 */
uint16_t end_flag = 0;					 /* 捕获结束标志 */
float frequency = 0;				     /* 频率 */
float duty_cycle = 0;				     /* 占空比 */

(2)
在这里插入图片描述① 启动定时器TIM2通道1的PWM输出功能以及定时器TIM3的输入捕获功能。

	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);        /* 启动定时器2通道1的PWM输出功能 */     
	HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);		/* 启动定时器3的输入捕获功能 */  

② 这段代码的主要作用是获取定时器输入捕获的PWM信号的频率和占空比,并将其输出到串口进行显示。
  首先,通过 HAL_Delay(500) 函数进行延时,等待一段时间(这里是500毫秒)。然后,通过判断 end_flag 是否为1来确定是否进行了输入捕获,即判断是否完成了三次输入捕获过程、如果 end_flag 为1,表示完成了三次输入捕获,可以进行频率和占空比的计算。
  其中,Period_cnt1表示定时器TIM3第一次捕获到下降沿时溢出次数, ccr1_cnt 表示第一次捕获到下降沿时的CCR寄存器值。 Period_cnt2 表示定时器TIM3第二次捕获到上升沿时溢出次数, ccr2_cnt 表示第二次捕获到上升沿时的CCR寄存器值。

计算占空比的公式为:

(Period_cnt1 * 65536 + ccr1_cnt + 1) / (Period_cnt2 * 65536 + ccr2_cnt + 1) * 100

(Period_cnt1 * 65536 + ccr1_cnt + 1) : 高电平时间
(Period_cnt2 * 65536 + ccr2_cnt + 1): 一个周期时间
注:
Ⅰ、65535是CubeMx里面设置的自动重装载(ARR)的值 ;
Ⅱ、涉及到的 “+1” 是因为计数时时从0开始计数的。

计算频率的公式为:

1000000 / (Period_cnt2 * 65536 + ccr2_cnt + 1)

  其中1000000 = 1MHz是因为总线时钟频率为84MHz,预分频系数(PSC)为84 - 1,故得出此时TIM3的时钟频率为

84MHz /(84-1 +1)=84000 000 /(84-1 +1) = 1000 000

最后,通过printf语句打印输出计算得到的频率和占空比。完成打印后,将end_flag置为0,准备进行下一次输入捕获。

注:在printf函数的格式字符串中,%%表示一个百分号字符。

		HAL_Delay(500);																										 /* 500ms打印一次 */
		if(end_flag){																											 /* 判断是否结束 */
			duty_cycle=(float)(Period_cnt1 * 65536 + ccr1_cnt + 1) * 100 /(Period_cnt2 * 65536 + ccr2_cnt + 1);  /* 计算占空比 */
  		frequency=1000000 / (float)(Period_cnt2 * 65536 + ccr2_cnt + 1); /* 计算频率 */
			printf("\r\n 频率 = %.2f Hz,占空比 = %.2f %%",frequency,duty_cycle);
			end_flag = 0;																										 /* 将捕获结束标志置0(准备下一次捕获) */
		}

(3)
① HAL_TIM_PeriodElapsedCallback() 函数是定时器计数溢出回调函数。当定时器的计数达到最大值并发生溢出时,该回调函数会被调用。在这段代码中,Period_cnt变量用于记录定时器计数溢出的次数。每次回调函数被调用时,Period_cnt会自增一次,以表示又经过了一段时间。
② HAL_TIM_IC_CaptureCallback() 函数是定时器输入捕获回调函数。它在定时器的输入捕获事件发生时被调用。在这段代码中,主要针对定时器TIM3进行处理。根据ic_flag的不同值,可以确定当前处于输入捕获的哪个阶段。

  • 阶段一:第一次捕获到上升沿时,将相关参数清零,并将输入捕获设置为等待第二个阶段。
  • 阶段二:第一次捕获到下降沿时,获取存放在CCR寄存器的捕获值(CCR1),并记录计时器溢出次数1。然后将输入捕获设置为等待第三个阶段。
  • 阶段三:第二次捕获到上升沿时,获取存放在CCR寄存器的捕获值(CCR2),并记录计时器溢出次数2。完成两次输入捕获后,将输入捕获设置为等待第一个阶段,并将end_flag置为1,表示完成一次PWM的输入捕获。
    在这里插入图片描述
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){    /* 定时器计数溢出回调函数 */
		Period_cnt ++;								            /* 定时器计数溢出时间次数 */
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim){	    /* 定时器输入捕获回调函数 */
		if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){		    /* 判断是不是定时器TIM1 */
			if(end_flag == 0){							        /* 判断结束标志是不是0,在打印期间不进行捕获,防止打印时相关数值改变 */
				switch(ic_flag){							    /* 判断此时处于捕获的第几个阶段 */
					case 0:									    /* 阶段一:第一次捕获到上升沿 */
					{
						__HAL_TIM_SET_COUNTER(&htim3,0);	    /* 将定时器3计数设置为0 */
						ccr1_cnt = 0;							/* 将相关参数清0 */
						ccr2_cnt =0;
						Period_cnt = 0;
						Period_cnt1 = 0;
						Period_cnt2 = 0;
						__HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_FALLING);/* 设置成下降沿捕获 */
						ic_flag = 1;							/* 捕获设置为等待第二个阶段 */
						break;
					}
						case 1:									/* 阶段二:第一次捕获到下降沿 */
					{
						ccr1_cnt = __HAL_TIM_GET_COMPARE(&htim3,TIM_CHANNEL_1);/* 获取存放在CCR寄存器的值(捕获值CCR1) */
						Period_cnt1 = 	Period_cnt;				/* 获取计时器溢出次数1 */
						__HAL_TIM_SET_CAPTUREPOLARITY(&htim3,TIM_CHANNEL_1,TIM_INPUTCHANNELPOLARITY_RISING); /* 设置成上升沿捕获 */
						ic_flag = 2;							/* 捕获设置等待为第三个阶段 */
						break;
					}
					case 2:										/* 阶段三:第二次捕获到上升沿 */
					{
						ccr2_cnt = __HAL_TIM_GET_COMPARE(&htim3,TIM_CHANNEL_1);/* 取存放在CCR寄存器的值(捕获值CCR2) */
						Period_cnt2 = Period_cnt;				/* 获取计时器溢出次数2 */
						ic_flag = 0;							/* 捕获设置等待为第一个阶段 */
						end_flag = 1;							/* 完成一次捕获,将标志置1 */
						break;
					}			
				}
			}
		}
}

3.3 示例结果

  接上串口后观察打印结果,如图3.7和图3.8 所示,其显示的PWM频率域PWM占空比和图2.13中示波器测出来的相同。

请添加图片描述
                 图3.6 接线

在这里插入图片描述
                   图3.7 串口结果显示

请添加图片描述

                   图3.8 示波器显示

四、用定时器ETR方式计算PWM脉冲数

4.1 ETR计算脉冲数原理

   通过示例2能够计算PWM的占空比和频率,如果单纯的计算PWM数,就不得不提到定时器的另一个模式:ETR(External Trigger)模式。在这种模式下,定时器的计数器会在PWM信号发生时启动计数。计数器会开始累积时钟脉冲的数量,这个数量与PWM信号的脉冲数成正比。
在这里插入图片描述
                     图4.1 定时器ETR模式框图

4.2 STM32CubeMx设置

(1)因为此处需要产生一个PWM,此处用TIM2产生一个频率为1K的PWM方波,如何产生PWM请参考这篇文章:HAL库STM32常用外设教程(一)—— 定时器 输出PWM ,TIM2的配置如图4.2所示。
在这里插入图片描述
                     图4.2 定时器TIM2 STM32CubeMx配置

(2)TIM3设置成ETR模式,如图4.3所示。然后使能TIM3中断,如图4.4所示

在这里插入图片描述
                     图4.3 定时器TIM3 STM32CubeMx配置

在这里插入图片描述
                     图4.4 使能定时器TIM3 中断

(2)通过TIM6设置成1s的计时,如图4.6所示,之所以设置成1s进行计时是因为PWM频率的定义为:
  在单位时间内,PWM信号中的脉冲从起始到结束再到下一个脉冲的周期性重复次数。PWM频率通常以赫兹(Hz)为单位。
  通俗来讲,1s内产生 x 个脉冲,其频率就为 x 赫兹。
但是有一点需要注意,ETR模式是用来测量脉冲个数的,本次示例通过定时1s去测量ETR的数值(脉冲的个数)来计算脉冲的频率,此方法是不严谨的,因为脉冲的频率可能是不断变化的,举例来说:
  在图4.5中,一定时间内ETR测到的PWM1和PWM2的脉冲个数都是3个,如果按照本次示例中的测量方法得出的结论是PWM1和PWM2的频率是一样的(一定时间内脉冲个数是一样的),所以这种方式只能计算频率不变的脉冲频率。
在这里插入图片描述
                     图4.5 两种PWM

在这里插入图片描述
                     图4.6 定时器TIM6 STM32CubeMx配置

  然后使能定时器6中断,如图4.7所示。需要在TIM6的溢出中断里面进行计数,在“NVIC”里再次检测中断TIM3和TIM6的中断是否开启。如图4.8所示。
在这里插入图片描述
                     图4.7 使能定时器TIM6 中断

在这里插入图片描述
                     图4.8 STM32Cube"NVIC"设置

4.3 程序设计

(1)参考 2.3节 先进行 串口重定向,完成后再进行下面相关变量的定义。
在这里插入图片描述

uint32_t count_over = 0; 	/* 定时器计数溢出次数 */
uint32_t count_cnt = 0;		/* 定时器计数值 */
uint32_t count = 0;				/* 1s 内脉冲总数 */

uint8_t printf_flag = 0;

(2)启动TIM2定时器的PWM输出,以中断方式启动TIM3定时器和TIM6定时器。
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/03caa9092ff845fa866b6cf8250423d9.png

	HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);  /* 启动定时器TIM2的PWM输出 */
	HAL_TIM_Base_Start_IT(&htim3);						/* 以中断方式启动定时器TIM3 */
	HAL_TIM_Base_Start_IT(&htim6);						/* 以中断方式启动定时器TIM6 */

(3)在定时器TIM3的中断服务例程里面进行溢出计数,在定时器6的中断服务例程里面通过函数 __HAL_TIM_GET_COUNTER()进行读出此时的CNT值,将打印标志置1,此时主循环检测到该标志置1后就可以进行打印,并将溢出值清0,定时器3的计数值清零,为下一秒计算脉冲频率做准备。

在这里插入图片描述

void TIM3_IRQHandler(void)
{
  /* USER CODE BEGIN TIM3_IRQn 0 */

  /* USER CODE END TIM3_IRQn 0 */
  HAL_TIM_IRQHandler(&htim3);
    /* USER CODE BEGIN TIM3_IRQn 1 */
	count_over ++; 
  /* USER CODE END TIM3_IRQn 1 */
}
void TIM6_DAC_IRQHandler(void)
{
  /* USER CODE BEGIN TIM6_DAC_IRQn 0 */
	
	count_cnt =  __HAL_TIM_GET_COUNTER(&htim3);   /* 获取此时的CNT值 */
	printf_flag = 1;							  /* 将打印标志置1 */
	count_over = 0;								  /* 溢出计数清0 */
	__HAL_TIM_SET_COUNTER(&htim3,0);			/* 将定时器3的计数值清0 */
	
  /* USER CODE END TIM6_DAC_IRQn 0 */
  HAL_TIM_IRQHandler(&htim6);
  /* USER CODE BEGIN TIM6_DAC_IRQn 1 */

  /* USER CODE END TIM6_DAC_IRQn 1 */
}

(4)在主循环里面通过判断 printf_flag标志将PWM频率打印出来,之所以放到主循环里面,是因为中断里面遵循“快进快出”的原则,在中断里面打印会占用过多的时间。

count = count_over * 65535 + count_cnt;

  上述公式是计算的PWM的脉冲数,因为本次示例中自动重装载值(ARR)的数值设置的是65535,所以每溢出一次计数65535个,也是公式中count_over * 65535的由来。

在这里插入图片描述

		if(printf_flag){								    /* 判断中断标志是否被置1 */
			 count = count_over * 65535 + count_cnt;		/* 计算1s内产生的脉冲数,即脉冲频率 */
			 printf("频率为 %d Hz \n\r",count);
			 printf_flag = 0;								/* 将中断标志清0,为下一次打印做准备 */
		}	

4.4 示例结果

  通过杜邦线将PA0引脚和PD2引脚相连,就可以在串口工具里面看到输出的频率,本次示例为验证输出的是否正确,在PA0引脚上又接了一个示波器,观察输出的波形,从图4.10和4.11可以看出,产生PWM的频率是1KHz,捕获到的频率也是1KHz。
请添加图片描述
                    图4.9 接线

在这里插入图片描述
                    图4.10 串口输出频率

请添加图片描述
                    图4.11 示波器显示

4.5 问题反思

(1)在做该实验时,搞错了一个函数,将 __HAL_TIM_GET_COUNTER()
函数错用成了 __HAL_TIM_GET_COMPARE()函数,下面补充一下这两个函数的作用

① __HAL_TIM_GET_COUNTER()函数

  • 作用:用于获取定时器的当前计数器值。
  • 示例:
uint32_t counterValue = __HAL_TIM_GET_COUNTER(&htim);
  • 这个宏返回当前定时器的计数器寄存器(CNT)的值。在定时器工作时,计数器值不断增加,通过这个宏可以读取当前的计数值,用于查看定时器整体的计数状态。

② __HAL_TIM_GET_COUNTER()函数

  • 作用:用于获取定时器的比较寄存器值。。
  • 示例:
uint32_t compareValue = __HAL_TIM_GET_COMPARE(&htim, TIM_CHANNEL_1);
  • 这个宏返回指定通道(channel)的比较寄存器(Capture/Compare Register,通常缩写为CCR的值。比较寄存器通常用于与计数器值进行比较,从而确定何时触发中断或改变输出状态,用于查看与特定通道相关联的比较状态。这通常与定时器的PWM生成、捕获比较等操作有关。

(2)在进行 “ printf ” 打印时,第一次‘’将其放在了TIM6定时器的中断服务例程里面,结果发现打印出来的频率都是996Hz,然后考虑到了中断里面“快进快出”原则 ,通过置标志的方式将打印内容放在了while循环里面,打印出的结果是1000Hz。在单片机中,这一原则有助于确保中断服务程序的及时响应和高效执行,该原则一般包括两个方面:

① 尽量减小中断服务程序的执行时间:

  • 中断服务程序(ISR - Interrupt Service Routine)应该尽量简洁而高效。在中断服务程序中,应避免长时间的计算、复杂的数据处理等操作。
  • 长时间执行的中断服务程序会导致其他中断被阻塞,这可能影响系统的实时性和响应性。
  • 应该专注于在中断服务程序中完成必要的最小工作量,将复杂和耗时的任务移到主循环中或其他地方处理。

②尽量减小中断响应时间:

  • 中断响应时间是指从中断发生到中断服务程序开始执行的时间。较短的中断响应时间有助于提高系统的实时性。
  • 在设计中,可以通过适当配置中断控制器、合理选择中断优先级、以及优化硬件和软件的交互,来缩短中断响应时间。
  • 另外,及时清除中断标志位也是减小中断响应时间的一部分,确保中断服务程序能够迅速响应新的中断。

五、总结

  上述主要讲述了定时器输入捕获的原理及其三个应用示例,包括每个示例的HAL库配置、程序编写以及测试结果。在学习输入捕获时,应该多去琢磨其捕获的原理,可以通过画图去研究其过程,例如图3.1和图3.2,明白该在哪一个跳变沿进行捕获等。


参考书籍和文章:
1、《STM32Cube高效开发教程(基础篇)》王维波
2、《STM32F4xx中文参考手册》
3、《STM32F407 探索者开发指南》
4、【STM32】标准库与HAL库对照学习教程十–输入捕获实验
5、[018] [STM32] 定时器 基本定时/输出比较/输入捕获功能详解与HAL库编程
6、STM32频率测量
7、第五节:STM32输入捕获(用CubeMx学习STM32)
8、cube配置定时器ETR2模式测频实验
9、STM32的ETR引脚计数功能
10、基于STM32定时器ETR信号的应用示例


  生活总是这样,不能叫人处处都满意。但我们还要热情的活下去。人活一生,值得爱的东西很多,不要因为一个不满意,就灰心。

——《人生》

STM32 hal库使用笔记(三)定时器—输入捕获和计数脉冲 利用定时器输入捕获实现LED翻转;按键充当外部时钟源,实现LED翻转,简单原理测试。 阅读详情

相关推荐

【STM32】标准库与HAL库对照学习系列教程大全

STM32标准库与HAL库对照学习教程系列大全

修成真的博客 1万+

正点原子stm32f7 HAL库教程输入捕获中断程序理解

正点原子 STM32F7 HAL库教程实验九——输入捕获实验一章,用输入捕获测量高电平的长度,输入捕获中断回调函数 HAL_TIM_IC_CaptureCallback 和更新中断回调函数 HAL_TIM_PeriodElapsedCallback 的逻辑让人费解,而且原子有的注释不是很准确。现分析如下,以备遗忘。 1.输入捕获原理 用输入捕获测量一段高电平持续时间的原理如图,先设置上升沿触发中断,当捕获到上升沿时,让定时器重新计数,并设置下降沿触发中断。当下降沿到来时,记录下此时定时器的值CCRx2。在高

qq_16055183的博客 3959

STM32F4系列HAL库配置定时器实验——输入捕获

STM32F4系列HAL库配置定时器实验——输入捕获 输入捕获简单讲解 输入捕获模式可以用来测量脉冲宽度或者测量频率。我们以测量周期和频率为例,用一个简图来说明输入捕获的原理 假定定时器工作在向上计数模式, 图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为 上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器

azs0504的博客 9284

STM_HAL_TIM_IC_(输入捕获,捕获PWM波)

TM32微控制器提供了输入捕获(Input Capture)功能,这是一种用于精确测量外部信号脉冲宽度和周期的强大技术。输入捕获通常与定时器(如TIM)的高级控制定时器(TIM1和TIM8)或通用定时器(TIM2至TIM5)配合使用。

qq_59527512的博客 3673

【STM32】HAL库 STM32CubeMX教程八---定时器输入捕获_cubemx 捕获

也就是说连续N次采样检测,如果都是高电平,则说明这是一个有效的电平信号,这样便可以过滤掉那些因为某些而干扰产生的一些信号。STM32为了更好的优化使用,TIMx_CH1通道1捕捉到的信号可以传输到IC1,TIMx_CH1捕捉到的信号也可以连接到IC2,TIMx_CH2捕捉到的信号也可以连接到IC2,也可以连接到IC2。其中:fCK_INT是定时器的输入频率,fDTS是根据TIMx_CR1的CKD[1:0]的设置来确定的。4.捕获到上升沿后,定时器当前计数值存入capture_buf[0],改为捕获下降沿;

2401_87555477的博客 2676

STM32CubeMX——定时器输入捕获

通用定时器在基本定时器的基础上增加了一个外部引脚,可以实现输入捕获与输出比较等功能。 高级定时器TIM1与TIM8相比于通用定时器增加了电机控制相关的功能。 输入捕获一般应用在两个方面,一个方面是计算脉冲跳变沿时间的方式测量脉宽与频率,另一方面是用 PWM 输入的方式测量脉宽与频率。 ...

lbjbs的博客 4323

【STM32】HAL库 STM32CubeMX教程八---定时器输入捕获

前言: 本系列教程将外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片:STM32F407ZET6/STM32F103ZET6 2、STM32CubeMx软件 3、IDE: MDK-Keil软件 4、STM32F1xx/STM32F4xxHAL库 5 知识概括: 通过本篇博客您将学到: SMT32定时器输...

Z小旋 11万+

STM32_HAL库—TIM定时器输入捕获中断

(一)CubeMX设置: 1. 晶振配置(72M) 2. 输入捕获引脚配置 3. 触发模式配置 4. 引脚触发配置 5. 主函数调用函数开启输入捕获中断 __HAL_TIM_SET_CAPTUREPOLARITY(&htim3, TIM_CHANNEL_2, TIM_INPUTCHANNELPOLARITY_FALLING); //开启输入捕获中断,设置下降沿触发中断 HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_2); //启动输入捕获 6.

1万+

【STM32】HAL库 STM32CubeMX教程六----定时器中断

前言: 今天我们来学习定时器,32的定时器有着非常丰富的功能,输入捕获/输出比较,PWM,中断等等。是我们学习STM32最频繁使用到的外设之一,所以一定要掌握好,这节我们讲解定时器中断,本系列教程将对应外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片:STM32F407ZET6/STM32F103ZET6 2、ST...

Z小旋 22万+

STM32 HAL库实现定时器输入捕获技术指南

STM32硬件抽象层(HAL)库为开发者提供了一套与硬件无关的编程接口,从而简化了编程过程。HAL库的设计目的是为了提供标准且统一的硬件访问方式,允许开发者不必深入了解硬件底层,即可实现对STM32微控制器的编程。在嵌入式系统中,定时器是极为重要的组件之一,它的作用是提供一个精确的时间基准。STM32的定时器通过一个可编程的预分频器来分频系统时钟,再利用该时钟来计数,从而实现精确的时间测量和时间事件的产生。

weixin_35732273的博客 1160

华大HC32F460RS485通信实验

华大HC32F460RS485通信实验

ZhuoWuKeJi的博客 1798

【STM32】标准库与HAL库对照学习教程十--输入捕获实验

【STM32】标准库与HAL库对照学习教程十--输入捕获实验一、前言二、准备工作三、输入捕获介绍1、简介2、原理四、输入捕获结构1、映射表2、结构图五、输入捕获寄存器位(1)ICF[3:0](2)CC1S[1:0](3)ICPS[1:0](4)CC1P(5)CC1E六、硬件电路七、标准库配置输入捕获1、配置步骤2、配置工程3、实验程序4、实验效果八、HAL库配置输入捕获1、使用cubemx生成工程2、相关函数3、实验程序4、实验效果 一、前言 本篇文章是对定时器功能中的输入捕获功能的讲解与配置,本篇从输入捕

修成真的博客 1万+

手把手教你用Python实现双三次插值(附OpenCV对比测试)

本文深入解析了双三次插值算法的数学原理,并提供了从零开始的Python实现教程。通过与OpenCV官方实现的对比测试,详细评估了不同插值算法(包括最邻近差值、双线性插值)在图像质量与计算性能上的差异,帮助开发者掌握高质量图像缩放的核心技术。

weixin_29310605的博客 293

STM32标准库+HAL库 | 输入捕获测量PWM的脉冲频率+占空比

在众多的仪器仪表类产品如示波器、逻辑分析仪,医疗设备、智能手表及工业控制的电机设备开发中,经常需要测量PWM输入波形的频率及占空比或TIM定时器脉冲计数值等数据。因此掌握TIM输入捕获是在各个行业从事嵌入式开发的一项非常基本的技能。当然,在一些大学生的省级、国家级的电子综合设计竞赛中,也经常考察对TIM、PWM的配置使用,由此可见掌握TIM-PWM的重要性。

weixin_49337111的博客 1万+

瑞萨单片机,把CS文件转换为CC

1、打开CC的软件: 点击坐上角的"START" 点击"Create New Project"的"GO": 导入工程: 如下图设置后,点击"Create" 2、进入到CC的开发界面: 可以看到原启动库文件以asm文件的方式直接出现在工程中: 删除新生成的main.c,保留导入的r_main.c(以你自己的代码为准): 注意添加所有的头文件路径: 点击"...

weixin_37611121的博客 1227

用STM32F103单片机控制电调制动无刷电机

用电调控制无刷电机制作遥控飞艇 蓝牙控制 调控电调 无刷电机 用电调控制无刷电机制作遥控飞艇 代码区 mian.c #include "delay.h" #include "sys.h" #include "usart.h" #include "usart3.h" #include "timer.h" #include "led.h" #include "key.h" int main(v...

子非鱼 的博客 2万+

STM32HAL库快速入门教程——常用外设学习(1)

控制引脚输出高、低电平,或直接驱动、或间接驱动LED、继电器等期间实现开关功能;芯片接收外部输入高、低电平,以此确定某些外部设备(例如按钮、开关)状态,做出相应操作。PSC(预分频系数),ARR(自动重装系数),CCR(捕获/比较系数)PWM的频率计算公式为:Freq=时钟频率(72MHZ)/ (PSC + 1) / (ARR + 1);PWM模式1(向上计数) :计数器从0计数加到自动重装载值(TIMx_ARR),然后重新从0开始计数,并且产生一个计数器溢出事件。

2302_80169672的博客 3082

阿里云大模型ACP认证模拟考试(二)

摘要:在阿里云大模型高级工程师 ACP 认证暂无真题题库的现状下,模拟题具有重要意义。其依照真实考试设计,涵盖题型、题量与分值分布,助力考生熟悉考试框架,合理分配答题时间精力,提升效率。同时,模拟题紧扣官方课程,能帮助考生精准定位知识盲点,如大模型架构原理和通义千问应用实践等重点内容,以便针对性完善知识体系。此外,通过练习模拟题,考生可掌握不同题型的解题思维与技巧,快速洞察复杂题目本质,提高答题准确率。

davidkorhenn的博客 2067

STM32 HAL库 STM32CubeMX -- TIM(定时器输入捕获)

STM32 HAL库 STM32Cube MX 配置 定时器输入捕获 输入捕获功能框图介绍 代码详解

Dir_x的博客 1万+

通过Python实现物联网金橙子自动打标

对于自家公司使用金橙子打标卡时,为了与生产线或者PLC联动等,往往需要对金橙子打标卡进行二次开发,而在二次开发中不得不涉及到MarkEzd.dll这个文件,而这个dll文件网上版本多不说,一般都没法直接使用,最后还不得不花大价钱购买专用库。而本文针对这个问题,涉法通过python来绕过MarkEzd.dll文件,直接进行自动化控制EzCad来实现联动,可应用于实际生产。文章最后拓展一下以物联网的形式,实现手机平台等登录网页,输入想要的印字内容后,远程打印的案例。

lylelo的博客 3365

isaaclab无法识别NVIDIA显卡,如何解决?

🏆本文收录于 《全栈Bug调优(实战版)》 专栏,该专栏专注于分享我在真实项目开发中遇到的各类疑难Bug及其深层成因,并系统提供高效、可复现的解决思路和实操方案。无论你是刚入行的新手开发者,还是拥有多年项目经验的资深工程师,本专栏都将为你提供一条系统化、高质量的问题排查与优化路径,助力你加速成长,攻克技术壁垒,迈向技术价值最大化与职业发展的更高峰🚀!    📌 特别说明: 文中部分技术问题来源于真实生产环境及网络公开案例,均经过精挑细选与系统化整理,并结合多位一线资深架构师和工程师多年实战经验沉淀,提

**My Coding Family** 1144

STM32 HAL库-定时器中断

STM32F407 有众多的定时器,其中包括 2 个基本定时器(TIM6 和 TIM7)、10 个通用定时器(TIM2 ~ TIM5、TIM9 ~TIM14)、2 个高级控制定时器(TIM1 和 TIM8),这些定时器彼此完全独立,不共享任何资源。选择定时器,配置时钟源,设置预分频系数,计数模式,设置自动重装载值。

宁静致远的博客 1万+

多模态脑区细分图谱 HCP-MMP v1.0, Glasser et al. 2016 皮层和皮下区域,简写和全称对应关系

人类连接组计划多模态脑区细分图谱(HCP-MMP)提供了对人类大脑皮层区域的非常有用的细分(Glasser et al. 2016a, b)。图谱是多模态的,因为每个区域都是根据四个标准的组合定义的,包括结构(T1加权/T2加权髓鞘含量和皮层厚度图)、静息态功能连接、基于任务的激活和拓扑地形组织。HCP-MMP v1.0图谱包含每个半球180个皮层区域,基于210名参与者的分析。

从事脑科学核磁共振方法学研究,在Nature communications等权威期刊发表研究论文,熟练掌握磁共振处理方法和统计学方法,欢迎大家和我交流。 2613

[018] [STM32] 定时器 基本定时/输出比较/输入捕获功能详解与HAL库编程

**计数模块** - 核心计数器:对计数时钟CK_CNT进行二次计数 - 计数器寄存器`TIMx_CNT`:存放核心计数器运行时的当前计数值 **自动重载模块** 由自动重载寄存器`TIMx_ARR`组成,根据定时器不同的**计数模式**: - **递增**计数模式:`TIMx_ARR`值为核心计数器的计数**终值** - **递减**计数模式:`TIMx_ARR`值为核心计数器的计数**初值**

柯西的彷徨的博客 1万+

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

基于HAL库的STM32单定时器多路输入捕获测量PWM的频率和占空比实现(状态机方式实现)

先有了这篇文章实现了单定时器的多通道测量频率,以外部时钟的方式可测量任意频率的方波),奈何不能多路测试PWM波的频率,于是有了本文。

昊月光华的博客 8157

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

【STM32】HAL库 STM32CubeMX教程---定时器输入捕获

前言: 本系列教程将外设原理,HAL库与STM32CubeMX结合在一起讲解,使您可以更快速的学会各个模块的使用 所用工具: 1、芯片:STM32F407ZET6/STM32F103ZET6 2、STM32CubeMx软件 3、IDE: MDK-Keil软件 4、STM32F1xx/STM32F4xxHAL库 5 知识概括: 通过本篇博客您将学到: SMT32定时器输入...

良知犹存的博客 6541
上一篇: HAL库STM32常用外分享(五)—— 定时器 输出比较
下一篇: HAL库STM32常用外设教程(七)—— ADC
泰然码事
博客等级 码龄5年 427粉丝 15原创
评论 15
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值