TIM4双通道输入捕获模式下捕获值异常问题求助
问题根源分析
你的问题核心在于同一个定时器(TIM4)不能同时为两个通道配置独立的Slave Reset触发源,加上重复初始化定时器时基、覆盖触发配置,导致两个通道的计数逻辑冲突:
- 先后调用
myPWM4_Config_1和myPWM4_Config_2时,第二个函数会重新执行TIM_TimeBaseInit覆盖定时器时基配置,还通过TIM_SelectInputTrigger(TIM4, TIM_TS_TI2FP2)把触发源从CH1改成CH2,直接导致CH1的Slave Reset逻辑失效。 - 即使两个通道都配置Slave Reset,同一个定时器的计数器只能被一个触发源复位,两个通道的上升沿会交替复位计数器,最终导致捕获值混乱,只能读到上升沿的计数。
- 配置里初始用了
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
相关产品推荐
相关产品推荐

