You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32可变频率互补PWM占空比随频率变化问题求助

可变频率互补PWM占空比随频率变化的解决方案

问题描述

STM32新手尝试实现TIM1通道的可变频率互补PWM,期望调整频率时占空比保持固定,但实际修改频率后占空比同步变化。相关代码如下:

TIM1初始化代码

static void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};
  TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 0;
  htim1.Init.CounterMode = TIM_COUNTERMODE_DOWN;
  htim1.Init.Period = 400;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
  if (HAL_TIM_PWM_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 180;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
  sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
  if (HAL_TIM_PWM_ConfigChannel(&htim1, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
  sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
  sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_3;
  sBreakDeadTimeConfig.DeadTime = 130;
  sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
  sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
  sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
  if (HAL_TIMEx_ConfigBreakDeadTime(&htim1, &sBreakDeadTimeConfig) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */
  HAL_TIM_MspPostInit(&htim1);

}

频率修改函数

void pwmData(uint16_t dat){
__HAL_TIM_SET_AUTORELOAD(&htim1,dat);

__HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1,dat*0.50);
}

主函数

int main(void)
{
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_TIM1_Init();
  MX_ADC1_Init();
  MX_TIM2_Init();
  HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
  HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1);
  HAL_ADC_Start_DMA(&hadc1, uADCVal, 6);
  HAL_TIM_Base_Start_IT(&htim2);
  HAL_TIM_Base_Start_IT(&htim1);
  while (1)
  {
  if (flagu) {
    AD1 += uADCVal[0];
    AD2 += uADCVal[1];
    AD3 += uADCVal[2];
    AD4 += uADCVal[3];
    AD5 += uADCVal[4];
    AD6 += uADCVal[5];
    if (count >= 500) {
        uADC1 = AD1/500;
        AD1=0;
        uADC2 = AD2/500;
        volt = (float)uADC2*0.00080244078;
        AD2=0;
        uADC3 = AD3/500;
        temp2 =(float)uADC3*0.99;
        AD3=0;
        uADC4 = AD4/500;
        AD4=0;
        uADC5 = AD5/500;
        AD5=0;
        uADC6 = AD6/500;
        AD6=0;
        count=0;
    }
    flagu=0;
}
  pwmData(uADC1);
  }
}

原因分析

  1. 浮点数运算截断误差:dat是uint16_t类型,dat*0.50会先转为浮点数计算,再赋值给整数类型的捕获比较寄存器(CCR),当dat为奇数时会发生截断,导致实际CCR值不是精确的dat/2,占空比偏离预期,且不同dat的截断程度不同,表现为占空比随频率变化。
  2. 自动重载预加载未启用:初始化中htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE,修改自动重载值(ARR)后不会立即生效,需等待下一次更新事件,导致ARR与CCR不同步,出现短暂的占空比异常。
  3. 不必要的重复调用:主函数while(1)循环中每次都调用pwmData(uADC1),但uADC1仅在count>=500时更新,重复设置相同值虽不影响功能,但会增加无效操作。

解决方案

1. 改用整数运算计算CCR值

避免浮点数截断,直接用整数除法计算固定占空比对应的CCR值,保证精度:

  • 若需50%占空比,对于偶数dat,dat/2即可;对于奇数dat,若要向上取整用(dat + 1)/2,向下取整用dat/2。

2. 启用自动重载预加载

修改TIM1初始化配置,启用自动重载预加载,确保ARR和CCR修改后同步生效:

htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;

3. 优化主函数调用逻辑

仅在uADC1更新后调用pwmData,减少无效操作:
将pwmData(uADC1)移至uADC1更新的代码块内。

4. 可选:手动触发更新事件(若未启用预加载)

如果不启用预加载,修改ARR和CCR后需手动触发更新事件,确保参数立即生效:

__HAL_TIM_GENERATE_EVENT(&htim1, TIM_EVENTSOURCE_UPDATE);

修改后的代码

修改后的TIM1初始化(关键部分)

// ...
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; // 启用预加载
// ...

修改后的频率修改函数

void pwmData(uint16_t dat){
    // 先设置CCR,再设置ARR,预加载模式下会同步更新
    __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dat / 2); // 50%占空比,整数运算无截断误差
    __HAL_TIM_SET_AUTORELOAD(&htim1, dat);
    // 若未启用预加载,添加以下代码触发更新
    // __HAL_TIM_GENERATE_EVENT(&htim1, TIM_EVENTSOURCE_UPDATE);
}

修改后的主函数(关键部分)

// ...
if (count >= 500) {
    uADC1 = AD1/500;
    AD1=0;
    uADC2 = AD2/500;
    volt = (float)uADC2*0.00080244078;
    AD2=0;
    uADC3 = AD3/500;
    temp2 =(float)uADC3*0.99;
    AD3=0;
    uADC4 = AD4/500;
    AD4=0;
    uADC5 = AD5/500;
    AD5=0;
    uADC6 = AD6/500;
    AD6=0;
    count=0;
    pwmData(uADC1); // 仅在uADC1更新后调用
}
// ...
// 删除while(1)循环末尾的pwmData(uADC1);

验证要点

  1. 频率计算:TIM1频率公式为 频率 = TIM_CLK / ((Prescaler + 1) * (ARR + 1))(向下计数模式),确认TIM_CLK(系统时钟或定时器时钟)配置正确,计算结果与示波器测量值一致。
  2. 占空比测量:用示波器测量互补PWM的CH1和CH1N通道,确认占空比稳定在目标值(如50%),不受频率变化影响。
  3. 死区时间检查:确保死区时间设置合理,避免上下桥臂直通,死区时间不会改变整体占空比,但会影响PWM边缘的切换时序。

内容的提问来源于stack exchange,提问作者Rahul chowdhary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 00:35:02