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

如何用TIM4双通道正确读取PWM占空比?异常问题排查

问题分析与解决

核心问题根源

  1. 同一定时器配置被覆盖:myPWM4_Config_1和myPWM4_Config_2都对TIM4进行了完整配置(时基、PWMI模式、触发源等),后调用的配置会完全覆盖前者,导致两路PWM共享同一套捕获逻辑,占空比计算必然一致。
  2. 中断捕获寄存器读取错误:CC2中断分支中错误读取了TIM_GetCapture1()和TIM_GetCapture2(),对于通道2的捕获逻辑,应该读取对应通道的捕获寄存器。
  3. 无信号超时机制缺失:未检测通道是否长时间无输入,断开信号后仍保留旧的计算值。

解决方案

方案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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 19:32:32