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

TIM4双通道输入捕获模式下捕获值异常问题求助

问题根源分析

你的问题核心在于同一个定时器(TIM4)不能同时为两个通道配置独立的Slave Reset触发源,加上重复初始化定时器时基、覆盖触发配置,导致两个通道的计数逻辑冲突:

  1. 先后调用myPWM4_Config_1和myPWM4_Config_2时,第二个函数会重新执行TIM_TimeBaseInit覆盖定时器时基配置,还通过TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2)把触发源从CH1改成CH2,直接导致CH1的Slave Reset逻辑失效。
  2. 即使两个通道都配置Slave Reset,同一个定时器的计数器只能被一个触发源复位,两个通道的上升沿会交替复位计数器,最终导致捕获值混乱,只能读到上升沿的计数。
  3. 配置里初始用了TIM_ICPolarity_BothEdge但实际用单沿切换的逻辑没问题,但触发源冲突是核心问题。
解决方案

1. 合并通道配置,避免重复初始化定时器

把CH1和CH2的配置合并到同一个函数里,只初始化一次TIM4的时基、GPIO和NVIC,分别配置两个通道的输入捕获,取消Slave Reset模式,改用软件逻辑计算周期和高电平时间。

2. 修改中断服务函数逻辑

通过全局状态变量记录每个通道的上升/下降沿状态,手动计算两次上升沿的间隔为周期,上升沿到下降沿的间隔为高电平时间。

修改后的代码示例

合并后的TIM4配置函数

void myPWM4_DualChannel_Config(tmyPWMInput *pwm1, tmyPWMInput *pwm2)
{
    RCC_ClocksTypeDef __RCC_CLOCK;

    myPWMInputCtor(pwm1);
    myPWMInputCtor(pwm2);

    RCC_GetClocksFreq(&__RCC_CLOCK);
    pwm1->clock = __RCC_CLOCK.PCLK1_Frequency * 2; // APB1定时器时钟
    pwm2->clock = __RCC_CLOCK.PCLK1_Frequency * 2;

    // 使能时钟
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    // 配置GPIO PD12 (CH1) 和 PD13 (CH2)
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_TIM4);
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource13, GPIO_AF_TIM4);

    // 配置NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    TIM4_IRQHandler_ptf = TIM4_IRQ_PWM;

    // 初始化定时器时基(只执行一次)
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_TimeBaseStructure.TIM_Period = 0xFFFF;
    TIM_TimeBaseStructure.TIM_Prescaler = 840 - 1;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
    TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);

    // 配置CH1输入捕获(初始上升沿触发)
    TIM_ICInitTypeDef TIM_CH1_ICInitStructure;
    TIM_CH1_ICInitStructure.TIM_Channel = TIM_Channel_1;
    TIM_CH1_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_CH1_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_CH1_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_CH1_ICInitStructure.TIM_ICFilter = 0x0;
    TIM_ICInit(TIM4, &TIM_CH1_ICInitStructure);

    // 配置CH2输入捕获(初始上升沿触发)
    TIM_ICInitTypeDef TIM_CH2_ICInitStructure;
    TIM_CH2_ICInitStructure.TIM_Channel = TIM_Channel_2;
    TIM_CH2_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;
    TIM_CH2_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    TIM_CH2_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;
    TIM_CH2_ICInitStructure.TIM_ICFilter = 0x0;
    TIM_ICInit(TIM4, &TIM_CH2_ICInitStructure);

    // 启用CC1和CC2中断
    TIM_ITConfig(TIM4, TIM_IT_CC1 | TIM_IT_CC2, ENABLE);

    TIM_ClearFlag(TIM4, TIM_FLAG_Update);
    TIM_Cmd(TIM4, ENABLE);
}

修改后的中断服务函数

// 全局状态变量,记录通道的捕获状态
uint8_t is_rising_ch1 = 1;
uint8_t is_rising_ch2 = 1;
uint16_t last_capture_ch1 = 0; // 记录上一次CH1的捕获值
uint16_t last_capture_ch2 = 0; // 记录上一次CH2的捕获值

// 中断服务函数核心逻辑
if (TIM_GetITStatus(TIM4, TIM_IT_CC1) != RESET) {
    tmyPWMInput *pPWM4_1 = &myCTRL.myHW.myPortDCfg.myPWM4_1;
    TIM_ClearITPendingBit(TIM4, TIM_IT_CC1);

    uint16_t current_capture = TIM_GetCapture1(TIM4);
    if (is_rising_ch1) {
        // 上升沿:计算周期(处理计数器溢出)
        if (current_capture >= last_capture_ch1) {
            pPWM4_1->FullPeriod = current_capture - last_capture_ch1;
        } else {
            pPWM4_1->FullPeriod = (0xFFFF - last_capture_ch1) + current_capture + 1;
        }
        last_capture_ch1 = current_capture;
        // 切换为下降沿触发
        TIM_OC1PolarityConfig(TIM4, TIM_ICPolarity_Falling);
        is_rising_ch1 = 0;
    } else {
        // 下降沿:计算高电平时间
        if (current_capture >= last_capture_ch1) {
            pPWM4_1->HighPeriod = current_capture - last_capture_ch1;
        } else {
            pPWM4_1->HighPeriod = (0xFFFF - last_capture_ch1) + current_capture + 1;
        }
        last_capture_ch1 = current_capture;
        // 切换为上升沿触发
        TIM_OC1PolarityConfig(TIM4, TIM_ICPolarity_Rising);
        is_rising_ch1 = 1;
        // 计算占空比和频率
        if (pPWM4_1->FullPeriod != 0) {
            myPWMCalc(pPWM4_1);
        }
    }
}

if (TIM_GetITStatus(TIM4, TIM_IT_CC2) != RESET) {
    tmyPWMInput *pPWM4_2 = &myCTRL.myHW.myPortDCfg.myPWM4_2;
    TIM_ClearITPendingBit(TIM4, TIM_IT_CC2);

    uint16_t current_capture = TIM_GetCapture2(TIM4);
    if (is_rising_ch2) {
        // 上升沿:计算周期
        if (current_capture >= last_capture_ch2) {
            pPWM4_2->FullPeriod = current_capture - last_capture_ch2;
        } else {
            pPWM4_2->FullPeriod = (0xFFFF - last_capture_ch2) + current_capture + 1;
        }
        last_capture_ch2 = current_capture;
        // 切换为下降沿触发
        TIM_OC2PolarityConfig(TIM4, TIM_ICPolarity_Falling);
        is_rising_ch2 = 0;
    } else {
        // 下降沿:计算高电平时间
        if (current_capture >= last_capture_ch2) {
            pPWM4_2->HighPeriod = current_capture - last_capture_ch2;
        } else {
            pPWM4_2->HighPeriod = (0xFFFF - last_capture_ch2) + current_capture + 1;
        }
        last_capture_ch2 = current_capture;
        // 切换为上升沿触发
        TIM_OC2PolarityConfig(TIM4, TIM_ICPolarity_Rising);
        is_rising_ch2 = 1;
        // 计算占空比和频率
        if (pPWM4_2->FullPeriod != 0) {
            myPWMCalc(pPWM4_2);
        }
    }
}

关键修改点说明

  • 取消Slave Reset模式:改用软件记录上一次捕获值,手动计算周期和高电平时间,避免定时器计数器被两个通道交替复位。
  • 合并配置函数:只初始化一次TIM4时基,避免重复配置覆盖参数。
  • 使用TIM_OCxPolarityConfig切换捕获极性:比重新调用TIM_ICInit更高效,也避免覆盖其他通道配置。
  • 处理定时器溢出:当计数器溢出时,周期计算需要考虑0xFFFF - 上一次值 + 当前值 + 1的情况。

内容的提问来源于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 21:29:50