如何用TIM4双通道正确读取PWM占空比?异常问题排查
问题分析与解决
核心问题根源
- 同一定时器配置被覆盖:
myPWM4_Config_1和myPWM4_Config_2都对TIM4进行了完整配置(时基、PWMI模式、触发源等),后调用的配置会完全覆盖前者,导致两路PWM共享同一套捕获逻辑,占空比计算必然一致。 - 中断捕获寄存器读取错误:CC2中断分支中错误读取了
TIM_GetCapture1()和TIM_GetCapture2(),对于通道2的捕获逻辑,应该读取对应通道的捕获寄存器。 - 无信号超时机制缺失:未检测通道是否长时间无输入,断开信号后仍保留旧的计算值。
解决方案
方案1:使用两个独立定时器(推荐)
将两路PWM分别分配到两个独立定时器(比如TIM4通道1和TIM3通道1),避免资源冲突。以下是适配后的关键代码:
TIM4通道1(PD12)配置(保留原逻辑,确保独占TIM4)
void myPWM4_Config_1(tmyPWMInput *me) { RCC_ClocksTypeDef __RCC_CLOCK; myPWMInputCtor(me); RCC_GetClocksFreq(&__RCC_CLOCK); me->clock = __RCC_CLOCK.PCLK1_Frequency * 2; //APB1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); me->GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; me->GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; me->GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; me->GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; me->GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOD, &me->GPIO_InitStructure); GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4); TIM4_IRQHandler_ptf = TIM4_IRQ_PWM; me->NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; me->NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; me->NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; me->NVIC_InitStructure.NVIC_IRQChannelCmd = 1; NVIC_Init(&me->NVIC_InitStructure); me->TIM_TimeBaseStructure.TIM_Period = 0xFFFF; me->TIM_TimeBaseStructure.TIM_Prescaler = 840 - 1; me->TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; me->TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1; me->TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM4, &me->TIM_TimeBaseStructure); me->TIM_CH1_ICInitStructure.TIM_Channel = TIM_Channel_1; me->TIM_CH1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; me->TIM_CH1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; me->TIM_CH1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; me->TIM_CH1_ICInitStructure.TIM_ICFilter = 0x0; TIM_PWMIConfig(TIM4, &me->TIM_CH1_ICInitStructure); TIM_SelectInputTrigger(TIM4, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM4, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM4, TIM_MasterSlaveMode_Enable); TIM_ARRPreloadConfig(TIM4, ENABLE); TIM_UpdateRequestConfig(TIM4, TIM_UpdateSource_Regular); TIM_ITConfig(TIM4, TIM_IT_CC1, ENABLE); TIM_ClearFlag(TIM4, TIM_FLAG_Update); TIM_Cmd(TIM4, ENABLE); }
TIM3通道1(PD13,修改引脚复用)配置
void myPWM3_Config_2(tmyPWMInput *me) { RCC_ClocksTypeDef __RCC_CLOCK; myPWMInputCtor(me); RCC_GetClocksFreq(&__RCC_CLOCK); me->clock = __RCC_CLOCK.PCLK1_Frequency * 2; //APB1 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE); me->GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13; me->GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; me->GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; me->GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; me->GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; GPIO_Init(GPIOD, &me->GPIO_InitStructure); GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM3); //复用为TIM3 TIM3_IRQHandler_ptf = TIM3_IRQ_PWM; me->NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; me->NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; me->NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //调整子优先级避免冲突 me->NVIC_InitStructure.NVIC_IRQChannelCmd = 1; NVIC_Init(&me->NVIC_InitStructure); me->TIM_TimeBaseStructure.TIM_Period = 0xFFFF; me->TIM_TimeBaseStructure.TIM_Prescaler = 840 - 1; me->TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; me->TIM_TimeBaseStructure.TIM_ClockDivision= TIM_CKD_DIV1; me->TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; TIM_TimeBaseInit(TIM3, &me->TIM_TimeBaseStructure); me->TIM_CH1_ICInitStructure.TIM_Channel = TIM_Channel_1; me->TIM_CH1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; me->TIM_CH1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; me->TIM_CH1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; me->TIM_CH1_ICInitStructure.TIM_ICFilter = 0x0; TIM_PWMIConfig(TIM3, &me->TIM_CH1_ICInitStructure); TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1); TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset); TIM_SelectMasterSlaveMode(TIM3, TIM_MasterSlaveMode_Enable); TIM_ARRPreloadConfig(TIM3, ENABLE); TIM_UpdateRequestConfig(TIM3, TIM_UpdateSource_Regular); TIM_ITConfig(TIM3, TIM_IT_CC1, ENABLE); TIM_ClearFlag(TIM3, TIM_FLAG_Update); TIM_Cmd(TIM3, ENABLE); }
独立的中断处理函数
void TIM4_IRQHandler( void ) { if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET) { tmyPWMInput *pPWM4_1 = &myCTRL.myHW.myPortDCfg.myPWM4_1; TIM_ClearITPendingBit(TIM4, TIM_IT_CC1); pPWM4_1->HighPeriod = TIM_GetCapture1(TIM4); pPWM4_1->FullPeriod = TIM_GetCapture2(TIM4); pPWM4_1->lastCaptureTick = HAL_GetTick(); //记录捕获时间用于超时检测 myPWMCalc(pPWM4_1); } } void TIM3_IRQHandler( void ) { if (TIM_GetITStatus(TIM3, TIM_IT_CC1) != RESET) { tmyPWMInput *pPWM3_2 = &myCTRL.myHW.myPortDCfg.myPWM3_2; TIM_ClearITPendingBit(TIM3, TIM_IT_CC1); pPWM3_2->HighPeriod = TIM_GetCapture1(TIM3); pPWM3_2->FullPeriod = TIM_GetCapture2(TIM3); pPWM3_2->lastCaptureTick = HAL_GetTick(); myPWMCalc(pPWM3_2); } }
增加无信号超时处理
在tmyPWMInput结构体中添加uint32_t lastCaptureTick字段,然后在主循环中检测超时:
#define PWM_TIMEOUT_MS 100 // 根据最低PWM频率调整 void mainLoop(void) { tmyPWMInput *pPWM4_1 = &myCTRL.myHW.myPortDCfg.myPWM4_1; tmyPWMInput *pPWM3_2 = &myCTRL.myHW.myPortDCfg.myPWM3_2; if (HAL_GetTick() - pPWM4_1->lastCaptureTick > PWM_TIMEOUT_MS) { pPWM4_1->DutyCycle = 0; pPWM4_1->Frequency = 0; } if (HAL_GetTick() - pPWM3_2->lastCaptureTick > PWM_TIMEOUT_MS) { pPWM3_2->DutyCycle = 0; pPWM3_2->Frequency = 0; } // 其他业务逻辑 }
关键注意事项
- 确保定时器引脚复用配置正确,需对应STM32 datasheet中的引脚功能定义。
- 中断优先级配置需避免冲突,同一抢占优先级下可通过子优先级区分不同定时器中断。
- 超时时间需设置为低于目标PWM的最小周期,避免误判正常信号为超时。
内容的提问来源于stack exchange,提问作者Aiman
相关产品推荐
相关产品推荐

