stm32 笔记 PWM输入模式测量脉宽和占空比原理

文章详细介绍了如何使用STM32的TIM3定时器通道1通过捕获模式测量PWM脉宽,包括上升沿和下降沿的捕获配置,以及如何通过CubeMX进行相关设置。在测量占空比部分,讨论了将上升沿配置为主模式,下降沿配置为从模式的方法,以计算占空比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、PWM 输入模式测量脉宽

1.1 测量脉宽简介

在测量占空比之前,我们先一步一步来,先让 STM32 可以测量脉宽。

TIM3_CH1(tim3 定时器通道 1)捕获模式测量脉宽步骤如下:

1.输入捕获到 PWM 上升沿触发

2.发送中断,通知用户此时被触发,用户获得当前计数器值

3.计数器清零,然后继续计数...

让 STM32 芯片一直重复这三步即可不断地测量出当前的 PWM 脉宽。

1.2 测量脉宽原理

首先我们可以使用 PWM 的复位 Reset 模式,它复合我们之前所要求的功能。具有捕捉上升沿和计数器复位的能力。

如果我们使用 TIM_CH1,可以产生分别是TI1FP1 与TI1FP2 这一对信号。他们的功能是分别是捕捉上升沿和下降沿。

其原理是:

二者都是来自同一 TI1 输入通道,经过输入滤波和边沿检测器后所产生的具有相同特征的信号,然后映射到不同的输入捕捉通道,本质上还是同一路信号。

TI1FP1,是来自于通道TI1,经过滤波器后将接到捕捉比较通道 IC1;

TI1FP2,是来自于通道TI1,经过滤波器后将接到捕捉比较通道 IC2;

由这张图得知,所谓 IC1 就是上升沿信号,TI1FP1会捕获到。IC2 是下降沿信号,TI1FP2 会捕捉到。

1.3 cubeMX 配置

 

①和②在上文已经详细介绍,不再赘述

③我们使用内部时钟

④重点来了!我们捕捉的通道是 TIM_CH1 如果我们走红色这条线,即可用后面的 TI1FP1 捕捉到上升沿。使用直接捕获模式(input capture direct mode)即可。如果需要捕获 TI1FP2 的下降沿则需要使用 重映射输入捕获(input capture direct from remap)。

 

⑤配置分频系数,我的系统时钟是 80mhz,分频 80 则为 1mhz,所以计数速度是 每秒1000000次。

⑥计数器最大值为 65535,所以测量最大的脉宽则为 65536/1000000 = 0.065536 秒。取倒数大约为 15.25hz 所以请不要测量超过这个值的脉宽,否则就会溢出清零,导致测量不准。

⑦默认值,配置为上升沿计数。

1.3 cubeMX 生成的代码解析

/*STM32cubeMX 生成的 TIM3 初始化代码:*/
/* TIM3 init function */
void PWM_TIM3_CHANNEL_1_Init(void)
{

  /* USER CODE BEGIN TIM3_Init 0 */

  /* USER CODE END TIM3_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};

  /* USER CODE BEGIN TIM3_Init 1 */

  /* USER CODE END TIM3_Init 1 */
  htim3.Instance = TIM3;		//指定需要配置的定时器
  htim3.Init.Prescaler = 79;	//定时器的预分频系数
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;		
  htim3.Init.Period = 65535;	//定时器计数周期值
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;	//定时器分频因子
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;	//计数达到指定值后,定时器是否自动重装载计数值
  if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;	//使用STM32单片机内部时钟
  if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_IC_Init(&htim3) != HAL_OK)	//进行初始化
  {
    Error_Handler();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;	//设置定时器模式 复位模式
  sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;	//使用TI1FP1触发
  sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING; //上升沿触发
  sSlaveConfig.TriggerFilter = 0;//滤波
  if (HAL_TIM_SlaveConfigSynchro(&htim3, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim3, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM3_Init 2 */

  /* USER CODE END TIM3_Init 2 */

}

输入GPIO口的配置:

大致是设置复用端口配置、时钟相关配置。比较简单就不全都注释了。

让我困惑的是为什么这个捕获 PWM 的引脚被配置成 GPIO_MODE_AF_PP 复用推挽输出,难道不应该是输入吗?

/*STM32cubeMX 生成的 TIM3 初始化代码:*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_baseHandle->Instance==TIM3)
  {
  /* USER CODE BEGIN TIM3_MspInit 0 */

  /* USER CODE END TIM3_MspInit 0 */
    /* TIM3 clock enable */
    __HAL_RCC_TIM3_CLK_ENABLE();

    __HAL_RCC_GPIOB_CLK_ENABLE();
    /**TIM3 GPIO Configuration
    PB4     ------> TIM3_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* TIM3 interrupt Init */
    HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM3_IRQn);
  /* USER CODE BEGIN TIM3_MspInit 1 */

  /* USER CODE END TIM3_MspInit 1 */
  }
}

1.4 需要我们写的获取值的代码

经过下降沿后会自动触发中断,在回调函数中判断下是 TIM3通道1发出的,就可以调用HAL_TIM_ReadCapturedValue() 取值了。

HAL_TIM_Base_Start(&htim3);
/* 启动定时器通道输入捕获并开启中断 */
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);	

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	
	if(htim->Instance==TIM3)
	{
				if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
			{
				PWM1_T_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1;
			}
	}

}

二、测量占空比

2.1 测量占空比分析

基于之前测量脉宽的逻辑,如果我们把测量上升沿配置成主模式,测量下降沿配置成从模式。STM32将会是这样的逻辑:

1.在上升沿复位两个计数器

2.下降沿暂停下降沿计数器

3.再次上升沿复位两个计数器

不断重复....

在之前测量脉宽我们使用了 TI1FP1 线路测量上升沿,不过测量下降沿则需要 TI1FP2 辅助,将其信号类似“转发”到 TIM_CH2 线路中 (下图红色线路)。也就是说,当我们在 TIM_CH1 同时捕捉上升沿和下降沿,就会占用 TIM_CH2 线路。

2.2 cubeMX 配置

基于之前的配置,我们需要将①通道2配置为从模式;在②中,将这个配置为下降沿触发。

2.3 需要我们写的代码

节约篇幅不再解释 cubeMX 生成的代码,直接贴出我们需要写的代码:

//输入捕获中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	
	  if(htim->Instance==TIM3)
  {
				if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
			{
				PWM1_T_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1)+1; //捕获脉宽
				PWM1_Duty = (float)PWM1_D_Count/PWM1_T_Count; //捕获占空比
			}
				else if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_2)
			{
				PWM1_D_Count =  HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2)+1;//捕获下降沿
			}					
	
	}

}

<think>嗯,用户想了解STM32PWM输入模式配置方法和示例代码。首先,我需要回忆一下PWM输入模式的基本概念。PWM输入模式通常用于测量外部信号的频率和占空比,对吧?STM32的定时器有这个功能,比如高级定时器或通用定时器。 接下来,我需要确定用户可能使用的具体型号,比如STM32F4系列,但可能用户需要通用的配置步骤。PWM输入模式通常涉及两个通道,一个用来捕获周期,另一个捕获度。这时候需要配置定时器的输入捕获功能,并设置为从模式为复位模式,这样当第一个边沿触发时,定时器计数器复位,从而可以测量周期。 然后,配置步骤大概包括:开启定时器时钟,配置GPIO为输入模式,初始化定时器的时基单元,设置输入捕获通道,配置触发器和从模式,开启中断,最后启动定时器。用户可能还需要示例代码,这部分需要确保语法正确,比如使用HAL库的函数,如HAL_TIM_IC_Start_IT来启动输入捕获中断。 可能的问题点包括是否正确配置了触发极性,是否设置了正确的从模式,以及中断处理函数中如何计算频率和占空比。需要提醒用户注意定时器时钟的分频设置,避免计数器溢出。同时,需要引用提供的参考资料,比如PWMSTM32中的使用指南,提到定时器的配置方法。 用户可能还关心如何验证配置是否正确,比如通过示波器或调试信息输出测量的频率和占空比。此外,相关问题的生成需要考虑用户可能的后续疑问,比如误差处理、高级定时器的区别、多通道配置等。 最后,确保回答结构清晰,分步骤说明配置流程,并提供代码示例,同时检查LaTeX格式是否正确,数学表达式用$...$,独立公式用$$...$$,并添加引用标识。</think>### STM32 PWM输入模式配置详解 PWM输入模式STM32定时器的重要功能,用于精确测量外部PWM信号的周期和占空比,其原理是通过两个输入捕获通道分别记录信号周期和[^1]。 #### 一、配置步骤(以TIM2为例) 1. **外设时钟使能** ```c __HAL_RCC_TIM2_CLK_ENABLE(); __HAL_RCC_GPIOA_CLK_ENABLE(); ``` 2. **GPIO配置** ```c GPIO_InitTypeDef GPIO_InitStruct = {0}; GPIO_InitStruct.Pin = GPIO_PIN_0; // TIM2_CH1 GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; GPIO_InitStruct.Pull = GPIO_PULLDOWN; GPIO_InitStruct.Alternate = GPIO_AF1_TIM2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); ``` 3. **定时器基础配置** $$ TIM \ base \ clock = \frac{SYSCLK}{(PSC+1)} $$ ```c TIM_HandleTypeDef htim2; htim2.Instance = TIM2; htim2.Init.Prescaler = 83; // 84MHz/84=1MHz htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 0xFFFF; HAL_TIM_IC_Init(&htim2); ``` 4. **输入捕获配置** ```c TIM_IC_InitTypeDef sICConfig; sICConfig.ICPolarity = TIM_ICPOLARITY_RISING; sICConfig.ICSelection = TIM_ICSELECTION_DIRECTTI; sICConfig.ICPrescaler = TIM_ICPSC_DIV1; sICConfig.ICFilter = 0; HAL_TIM_IC_ConfigChannel(&htim2, &sICConfig, TIM_CHANNEL_1); ``` 5. **从模式配置** ```c TIM_SlaveConfigTypeDef sSlaveConfig; sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET; sSlaveConfig.InputTrigger = TIM_TS_TI1FP1; HAL_TIM_SlaveConfigSynchro(&htim2, &sSlaveConfig); ``` #### 二、中断处理 ```c void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if(htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1){ uint32_t period = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); uint32_t duty = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); float frequency = 1e6 / period; // 单位Hz float duty_cycle = (duty * 100.0) / period; } } ``` #### 三、启动测量 ```c HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_2); ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值