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); } }
原因分析
- 浮点数运算截断误差:
dat是uint16_t类型,dat*0.50会先转为浮点数计算,再赋值给整数类型的捕获比较寄存器(CCR),当dat为奇数时会发生截断,导致实际CCR值不是精确的dat/2,占空比偏离预期,且不同dat的截断程度不同,表现为占空比随频率变化。 - 自动重载预加载未启用:初始化中
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE,修改自动重载值(ARR)后不会立即生效,需等待下一次更新事件,导致ARR与CCR不同步,出现短暂的占空比异常。 - 不必要的重复调用:主函数
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);
验证要点
- 频率计算:TIM1频率公式为
频率 = TIM_CLK / ((Prescaler + 1) * (ARR + 1))(向下计数模式),确认TIM_CLK(系统时钟或定时器时钟)配置正确,计算结果与示波器测量值一致。 - 占空比测量:用示波器测量互补PWM的CH1和CH1N通道,确认占空比稳定在目标值(如50%),不受频率变化影响。
- 死区时间检查:确保死区时间设置合理,避免上下桥臂直通,死区时间不会改变整体占空比,但会影响PWM边缘的切换时序。
内容的提问来源于stack exchange,提问作者Rahul chowdhary
相关产品推荐
相关产品推荐

