F429-HAL-高级定时器-输入捕获(2026/7/26) 目录一、输入捕获 Init 字段逐个解析第一层时基TIM_TimeBaseInitTypeDef① Instance — 用哪个定时器② Prescaler — 计时精度不是频率③ Period (ARR) — 兜底上限④⑤⑥ CounterMode / ClockDivision / RepetitionCounter第二层IC_InitTIM_IC_InitTypeDef4 个字段⑦ ICPolarity — 捕获哪个边沿⑧ ICSelection — 信号来源关键⑨ ICPrescaler — 分频⑩ ICFilter — 数字滤波第三层从模式高级定时器独有⑪ SlaveMode RESET⑫ InputTrigger TI1FP1跟 PWM 输出对比二、完整流程从引脚波形到频率、占空比波形进来事件① — 上升沿到达事件② — 下降沿到达事件③ — 第二个上升沿到达一张时序图串起来从事件③往后进入稳态为什么只有 4 行代码三、踩坑HAL_TIM_IC_Start 和 _IT 不能混用现象根因结论高级定时器 — 输入捕获TIM8基于 TIM3_8 工程TIM3PA6发 PWM → 杜邦线 → TIM8PC6收。一、输入捕获 Init 字段逐个解析输入捕获也分两层时基管计时精度 IC_Init管捕获行为TIM8 再加第三层从模式管自动复位。第一层时基TIM_TimeBaseInitTypeDefTIM_HandleTypeDef htim8; htim8.Instance TIM8; // ① htim8.Init.Prescaler 180 - 1; // ② 计时精度 htim8.Init.Period 0xFFFF; // ③ 兜底上限 htim8.Init.CounterMode TIM_COUNTERMODE_UP; // ④ 向上计数 htim8.Init.ClockDivision TIM_CLOCKDIVISION_DIV1; // ⑤ 输入滤波时钟 htim8.Init.RepetitionCounter 0; // ⑥ 不重复 HAL_TIM_IC_Init(htim8);字段值在输入捕获里的含义Prescaler180-1TIM8_CLK180MHz/1801MHz每 1μs CNT1Period(ARR)65535兜底上限。从模式 RESET 会提前清 CNTARR 基本不生效CounterModeUP0→1→2→... 上升沿 → 清 0 → 再来① Instance — 用哪个定时器TIM8。F429 有 TIM1 和 TIM8 两个高级定时器。选 TIM8 是为了用高级定时器独有的从模式 RESET省掉软件减法。② Prescaler — 计时精度不是频率模式Prescaler 作用PWM 输出分频定 PWM 频率输入捕获分频定计时精度TIM8_CLK 180MHzAPB290MHz预分频≠1 所以 ×2 PSC 180-1 → CNT 每 1μs 加 11μs 分辨率测 1KHz 信号周期 1000μs误差 ±1μs ±0.1%。③ Period (ARR) — 兜底上限0xFFFF 65535。CNT 数到这才会产生更新事件。从模式 RESET 每次上升沿都清 CNTCNT 根本到不了 65535。填最大值就是别来烦我。④⑤⑥ CounterMode / ClockDivision / RepetitionCounter跟 PWM 输出一样。ClockDivision只在ICFilter0时才生效。第二层IC_InitTIM_IC_InitTypeDef4 个字段TIM_IC_InitTypeDef ic {0}; ic.ICPolarity TIM_ICPOLARITY_RISING; // ⑦ 捕获哪个边沿 ic.ICSelection TIM_ICSELECTION_DIRECTTI; // ⑧ 信号来源 ic.ICPrescaler TIM_ICPSC_DIV1; // ⑨ 每 N 个边沿捕获一次 ic.ICFilter 0; // ⑩ 数字滤波⑦ ICPolarity — 捕获哪个边沿选项效果RISING上升沿触发捕获FALLING下降沿触发捕获BOTH两个边沿都触发CH1 RISING周期起点CH2 FALLING高电平结束点。同一个引脚进来两个通道各抓一个边沿。⑧ ICSelection — 信号来源关键选项含义用在哪DIRECTTI直连CH1 用自己的引脚PC6→TI1→IC1CH1上升沿INDIRECTTI交叉CH2 借 CH1 的引脚PC6→TI1→IC2CH2下降沿TRC内部触发定时器级联PC6 引脚 │ ▼ TI1 ──→ IC1CH1RISINGDIRECTTI │ └──→ IC2CH2FALLINGINDIRECTTICH2 没有自己的引脚它复用 CH1 的信号。一根线两个通道两个边沿。⑨ ICPrescaler — 分频选项含义DIV1每个边沿都捕获测占空比必须用这个DIV2/4/8每 N 个边沿才捕获一次只测频率时用⑩ ICFilter — 数字滤波0 不滤波。短杜邦线信号干净不需要。长线或电机环境开 4~6。第三层从模式高级定时器独有TIM_SlaveConfigTypeDef slave {0}; slave.SlaveMode TIM_SLAVEMODE_RESET; // ⑪ 触发时 CNT 清零 slave.InputTrigger TIM_TS_TI1FP1; // ⑫ 触发源 CH1 上升沿 HAL_TIM_SlaveConfigSynchronization(htim8, slave);⑪ SlaveMode RESET没有 RESET CNT 一直累加 0→1→2...→65535永不停 IC1Value 这一次上升沿的 CNT 周期 这次的 IC1Value - 上次的 IC1Value ← 软件减 ​ 有 RESET CNT 每次上升沿自动归零 IC1Value 从上一个上升沿到现在走了多少 tick ← 直接就是周期⑫ InputTrigger TI1FP1触发源 CH1 的滤波后上升沿信号。CH1 检测到有效上升沿 → 硬件同时做两件事锁存 CCR1 清 CNT。跟 PWM 输出对比字段TIM3 PWM 输出TIM8 输入捕获Prescaler分频定 PWM 频率分频定计时精度Period(ARR)PWM 频率核心参数0xFFFF兜底用CNT 行为0→ARR→0 反复0→上升沿→清0→上升沿→清0CCR 方向写设占空比读记录时间戳多出的结构体TIM_OC_InitTypeDef7 字段TIM_IC_InitTypeDef4 字段 从模式Init 函数HAL_TIM_PWM_InitHAL_TIM_IC_Init二、完整流程从引脚波形到频率、占空比TIM8 预先配置好后一切由硬件自动完成软件只在周期结束时去读结果。波形进来PC6 引脚杜邦线从 PA6 飞过来___/\_______/ ↑ ↑ ↑ 事件① 事件② 事件③事件① — 上升沿到达硬件干的事检测到 PC6 0→1CCR1 ← CNT 当前值锁存到 CH1 捕获寄存器CNT ← 0从模式 RESET为下一周期开始计时触发 TIM8_CC 中断软件干的事ISR → HAL → CaptureCallbackif (Channel 1)✅ 进入IC1Value ReadCapturedValue(CH1)// 读 CCR1IC2Value ReadCapturedValue(CH2)// 读 CCR2发现 IC1Value 0 → 跳过计算等下一次第一次上升沿没有有效数据——因为还没经历过完整的下降沿IC2Value 是旧值或 0。事件② — 下降沿到达硬件干的事检测到 PC6 1→0CCR2 ← CNT 当前值锁存到 CH2 捕获寄存器此时 CNT 已从事件①后从 0 开始计了触发 TIM8_CC 中断软件干的事if (Channel 2)❌ 不处理返回下降沿只做硬件锁存软件不干活。CCR2 里存着从最近一次上升沿到现在的 tick 数高电平脉宽。事件③ — 第二个上升沿到达硬件干的事检测到 PC6 0→1CCR1 ← CNT 当前值 完整周期的 tick 数从事件①到现在CNT 没被清零过CNT ← 0为下一周期重新计时触发 TIM8_CC 中断软件干的事if (Channel 1)✅IC1Value ReadCapturedValue(CH1)→1000← 一整圈走了 1000 tickIC2Value ReadCapturedValue(CH2)→500← 高电平走了 500 tick两个值都有效 → 计算占空比 500 / 1000 × 100% 50% 频率 1,000,000 / 1000 1000Hz一张时序图串起来___/\_______/ ↑Rising ↓Falling ↑Rising 事件① 事件② 事件③ CNT: 0→ →500 → →1000 →0→... ├─ CCR2500 ─┤ │ │ │ │ ├── CCR11000 ────────┤ │ │ │ │ 回调跳过 │ 回调 ✅ 计算 │ (第一次没 │ IC1Value1000 │ 有完整数 │ IC2Value500 │ 据) │ Duty50%, Freq1000Hz从事件③往后进入稳态事件③ → 事件⑤ → 事件⑦ → ... 每次上升沿 硬件锁存 CCR1 → CNT 归零CCR2 早已在下降沿锁存好了 软件读 CCR1周期、读 CCR2脉宽→ 计算 → printf之后的每个上升沿都有完整数据——上一个下降沿已经把 CCR2 更新好了。为什么只有 4 行代码TIM8_IC1Value HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 一步读周期 TIM8_IC2Value HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2); // 一步读脉宽 TIM8_DutyCycle (float)TIM8_IC2Value * 100.0f / (float)TIM8_IC1Value; // 一步除 TIM8_Frequency 1000000.0f / (float)TIM8_IC1Value; // 一步除从模式 RESET 已经把减法内嵌到硬件里了——CCR1 和 CCR2 天然对齐同一个周期。没有 RESET 的话// 没有从模式的版本多两行 period this_rising - last_rising; // ← 要减 pulse this_falling - last_rising; // ← 要减三、踩坑HAL_TIM_IC_Start和_IT不能混用现象CH1 用HAL_TIM_IC_Start_ITCH2 用HAL_TIM_IC_Start想省一次中断结果 CH2 硬件捕获不工作占空比始终为 0。根因HAL 源码里两个函数对htim-State的处理不同// _Start 版非中断——有状态检查 HAL_TIM_IC_Start(htim, Channel) { if (htim-State ! HAL_TIM_STATE_READY) // ← 卡在这里 return HAL_BUSY; // CH1 的 _IT 把 State 改过了 ... } // _IT 版中断——没有状态检查直接开干 HAL_TIM_IC_Start_IT(htim, Channel) { TIM_CCxChannelCmd(htim-Instance, Channel, TIM_CCx_ENABLE); // 开硬件捕获 __HAL_TIM_ENABLE_IT(htim, TIM_IT_CCx); // 开中断 return HAL_OK; }_IT不检查 State_Start检查。同一个句柄上先调_IT(CH1)改了 State再调_Start(CH2)直接被 HAL_BUSY 拒绝CCR2 硬件捕获根本没使能。结论同一个定时器句柄上所有通道必须统一风格。要用都_ITCH2 多进一次中断无所谓——回调里if (Channel 1)一行就过滤掉了。