title: HC-SR04 超声测距为什么必须用「输入捕获 + 从模式复位」?把 Echo 的每一段路都走一遍
tags: [STM32, 定时器, 输入捕获, HC-SR04, 超声测距, 从模式控制器]
---
# 前言
初学 HC-SR04,最容易卡住的不是接线,而是三个问题:
1. **Echo 的脉冲到底是谁产生的?**
2. **它进了 STM32 之后走了哪些路?**
3. **为什么非要开「从模式复位」和两个捕获通道,一个不够吗?**
这篇笔记把这三件事一次性讲透。核心只有一句话:
> **Trig 是 STM32 发出去的"发令枪",Echo 是 HC-SR04 自己产生的"秒表",而 STM32 的定时器只负责给这块秒表拍两张照片。**
---
# 一、先分清两个脉冲,别搞混
| 脉冲 | 谁产生 | 走到哪 | 特征 |
|---|---|---|---|
| **Trig 触发脉冲** | STM32 的 **PA0**(推挽输出) | → HC-SR04 的 **Trig** 脚 | 软件拉高 **≥ 10 µs** 再拉低 |
| **Echo 回响脉冲** | **HC-SR04 模块内部自己产生** | 从 **Echo** 脚输出 → STM32 | 高电平宽度 = 声波往返时间,最长约 **38 ms** |
一句话记牢:
> **Trig 引脚用于开启测量。要开启一次测量,就得给 Trig 一个脉冲。**
> **Echo 的脉冲是 HC-SR04 模块自己产生的,从 Echo 脚输出。**
所以 STM32 的活儿只有一件:**把 Echo 的高电平有多宽,量出来。**
---
# 二、从声波到引脚:Echo 是怎么被"造"出来的
```
PA0 输出 10µs Trig 脉冲
│
▼
HC-SR04 内部
├─ ① 发射头 T 发出 8 个 40 kHz 超声波脉冲
├─ ② 同时把 Echo 拉高(开始"计时")
├─ ③ 声波遇障碍物反射,被接收头 R 收到
└─ ④ 收到回波 → Echo 拉低("计时"结束)
```
- **Echo 高电平持续时间 = 声波"去 + 回"的总时间**
- 距离换算:
$$
\text{距离} = \frac{\text{Echo 高电平时间} \times 340\,\text{m/s}}{2}
$$
> 除以 2 是因为声波走了一个来回。这是唯一需要软件做的换算。
# 四、Echo 进入 STM32 之后的完整路线
## 4.1 一句话版
> **Echo 引脚 → 定时器 CH1 输入 → 输入滤波 → 边沿检测 → TI1FP1 → 一分为二:**
> **一路触发从模式控制器把 CNT 清零,同时 CH1 捕获上升沿存 CCR1;**
> **另一路把信号送到 CH2,等 Echo 下降沿到来时捕获 CNT 到 CCR2。**
## 4.2 框图版
```
┌──────────── CNT(计数器)────────────┐
│ ▲ │
│ ┌────┴────┐ ┌───────┐ │
│ │ PSC │ │ ARR │ │
│ └─────────┘ └───────┘ │
└──────┬──────────────────┬───────────┘
│ 捕获 │ 捕获
┌────▼────┐ ┌────▼────┐
│ CCR1 │ │ CCR2 │
└────▲────┘ └────▲────┘
│ 拍照 │ 拍照
Echo ──► CH1 ──► 输入滤波 ──► 边沿检测 ──┐ │
│ │ │
│ ┌────┴──┴────┐
│ │ 捕获分频 │
│ │ ÷1/2/4/8 │
│ └────────────┘
│
└──► 上升沿脉冲 ──┐
├─► 信号选择 ─► TRC
下降沿脉冲 ──┘ │
▼
┌──────────────────┐
│ 从模式控制器 │
│ (复位模式 Reset) │
└────────┬─────────┘
│
清空 CNT = 0
```
关键点:**Echo 接的是 TIM 的 CH1(复用输入),Trig 接的是普通 GPIO 输出(PA0)。**
"发送"和"接收"分开走,不要试图拿同一个引脚同时干两件事。
---
# 五、再把输入通道拆成 6 个节点(这张图最值钱)
从引脚到 CCR 寄存器,信号其实过了**四个阶段、六个节点**:
```
CH1引脚 ──①──► [输入滤波] ──②──► [边沿检测] ──③④──► [信号选择/TRC] ──⑤──► [捕获分频] ──⑥──► CCR1
第一阶段 第二阶段 第三阶段 第四阶段
```
底下六个波形就是同一根 Echo 信号在不同节点上的样子:**越往右越干净、越窄、越稀疏。**
## 第一阶段:输入滤波(节点 ① → ②)
```
fCK_INT ──► ÷1/2/4/8 ──► fDTS ──┐
▼
CH1引脚 ──────────────► [ 输入滤波 ] ──► 节点②
```
- **作用**:消抖、去毛刺。节点①里那段密集抖动,到节点②被整块吃掉,只留真正的边沿。
- **原理**:不是 RC 模拟滤波,而是**数字采样**——用 `fDTS` 去连续采输入电平,要求**连续 N 次采样都相同**(N 由 `IC1F[3:0]` 决定),输出才翻转。
- **两个可调项**(都在这一级):
- 前级 DTS 分频 `÷1 / ÷2 / ÷4 / ÷8` → 决定**采样快慢**
- 滤波长度 `IC1F` → 决定**要连续采几次相同才算数**
- **代价**:滤波越强,边沿延迟越大。
> ⚠️ **别搞混**:这里是 **fDTS 分频**;后面第四阶段还有一个**捕获分频**。两者位置不同、作用完全不同。
## 第二阶段:边沿检测(节点 ② → ③④)
信号在这一级被**一分为二**:
| 输出 | 含义 | 节点 |
|---|---|---|
| **上升沿脉冲** | 检到上升沿的**那一刻**产生一个脉冲 | 节点③ |
| **下降沿脉冲** | 检到下降沿的**那一刻**产生一个脉冲 | 节点④ |
- 输出的**不是电平,而是瞬间脉冲**,所以节点③④看起来又窄又尖。
- 这一级同时也分出 **CH2 的上升/下降沿脉冲**(即 `TI1FP2`)——这就是"一个引脚能同时被两个通道使用"的由来。
- 选哪个沿,由 `CC1P` / `CC1NP` 这类极性位决定。
## 第三阶段:信号选择 / TRC(节点 ④ → ⑤)
```
上升沿脉冲 ─┐
├─► [ 直接 / 间接 ] ─► TRC ─► 从模式控制器
下降沿脉冲 ─┘
```
- **直接 vs 间接**:就是**是否再取反一次**。直接 = 原样送出;间接 = 反相送出。
- **TRC**:触发选择器,决定到底拿哪一路(`TI1FP1` / `TI1FP2` / `TI2FP1` …)去驱动从模式控制器。
- **节点⑤**:就是最终真正送进定时器、去"命令" CNT 动作的那个脉冲。
## 第四阶段:捕获分频(节点 ⑤ → ⑥)
```
节点⑤ ──► [ 分频 ÷1/2/4/8 ] ──► 节点⑥ ──► CC1 捕获事件
```
- 由 `IC1PSC` 配置:**来 N 个边沿才真正捕获一次**。
- 图中配的是 **÷2**:所以红色圆圈里,**两个脉冲只留了一个**,节点⑥明显比节点⑤稀疏。
- **测距必须配 ÷1**,否则漏掉边沿,脉宽直接算错。
## 收尾:拍照 + 清零
```
节点⑥ ──► 捕获事件
├─► 【拍照】把此刻 CNT 的值拷进 CCR1
└─► 【清零】从模式控制器把 CNT 打回 0
```
- **拍照**:CCR 只负责"记录此刻计数值",像按快门。
- **清零**:复位模式(Reset Mode)下,CNT 被硬件打回 0,重新开始计时。
- 两者**由同一个沿、在硬件上同时发生**。
---
# 六、核心机制:上升沿存 CCR1,下降沿存 CCR2
这是整个方案的心脏,一张图说清:
```
上升沿 下降沿
│ │
▼ ▼
通道1 ──► ┌──────┐ ┌──────────┐
│ CCR1 │ │ CCR2 │ ◄── 通道2
└──────┘ └──────────┘
▲ ▲
│ │
┌──────────┴────────────────────────┴──────────┐
│ CNT 阶梯计数 │
└──────────────────────────────────────────────┘
```
- **遇到上升沿就保存到 CCR1 中**
- **遇到下降沿就保存到 CCR2 中**
于是:
```
脉宽 = CCR2 - CCR1
```
如果用**复位模式**,上升沿会把 CNT 同时清零,所以 **CCR1 恒等于 0**,直接读 CCR2 就是微秒数:
```
距离(cm) = CCR2 / 58 (1 计数 = 1µs 时)
```
---
# 七、为什么要用「从模式复位」?一个通道不行吗?
理论上用两个通道分别捕获两个沿,然后做减法也能算。但复位模式有三个实打实的好处:
1. **省掉减法,也天然防溢出**
上升沿把 CNT 归零,脉宽直接就是 CCR2 的值。同时最大计数值被压到"一个 Echo 周期"内,16 位计数器不容易溢出。
2. **硬件级测量,不受中断延迟影响**
捕获值是硬件瞬间锁存的,软件晚几十微秒再去读,**测出来的时间依然是准的**。用软件中断记时间戳则会被中断延迟和优先级抖动污染。
3. **一次触发、两路协同,全程零 CPU 干预**
CH1 管"起跑"(清零 + 记起点),CH2 管"终点"(记终点),一个定时器同时完成清零和两端捕获。
---
# 八、配置速查表
| 阶段 | 寄存器位 | 作用 | 测距推荐值 |
|---|---|---|---|
| 一阶段 DTS 分频 | `IC1F` 中 DTS 位 | 采样时钟快慢 | 视噪声,一般 ÷1 或 ÷2 |
| 一阶段 滤波长度 | `IC1F[3:0]` | 连续采样确认次数 | `0x3` ~ `0x7`(轻度滤波) |
| 二阶段 边沿极性 | `CC1P/CC1NP`、`CC2P/CC2NP` | 选捕获哪个沿 | CH1 上升沿、CH2 下降沿 |
| 三阶段 触发源 | `TS[2:0]` | TRC 选谁 | `TI1FP1` |
| 三阶段 从模式 | `SMS[2:0]` | 触发后干什么 | **Reset mode(复位模式)** |
| 四阶段 捕获分频 | `IC1PSC` / `IC2PSC` | 几个沿捕获一次 | **÷1** |
| 时基 | `PSC` / `ARR` | 计数频率与范围 | `PSC = 71`(1 µs 计数)、`ARR = 65535` |
> 72 MHz / (71+1) = **1 MHz**,即 **1 个计数 = 1 µs**,最大量程 65535 µs ≈ 65.5 ms,刚好覆盖 38 ms。
---
# 九、实践踩坑清单
1. **Trig 必须 ≥ 10 µs**:太短模块不响应,Echo 一直保持低电平。
2. **Trig 别忘了拉低**:只拉高不拉低,模块不会启动一次新的测量。
3. **捕获分频必须是 ÷1**:配成 ÷2 会漏掉一半边沿,测出来直接翻倍。
4. **一定要加超时保护**:前方无障碍物时 Echo 可能长时间不拉低,程序不能死等下降沿——用 CC2 超时或 CNT 溢出判定。
5. **PA0 的双重身份**:作 Trig 时是 GPIO 推挽输出;如果 Echo 也接到某个定时器通道,配置时要分清"输出模式"和"输入捕获模式"不能在同一引脚的同一通道上同时生效。
6. **警惕滤波延迟**:滤波长度配得过大,会引入几微秒的固定偏差,近距离测量时误差明显。
---
# 十、全文一句话总结
> **Trig 用 PA0 推挽输出一个 ≥10 µs 的脉冲开启测量;HC-SR04 自己产生 Echo 高电平脉冲,宽度正比于距离;Echo 进定时器 CH1,依次经过输入滤波、边沿检测、信号选择(TRC)、捕获分频(÷1);上升沿触发从模式复位把 CNT 清零、同时存进 CCR1,下降沿通过 CH2 存进 CCR2;`CCR2 - CCR1` 就是高电平时间,除以 58 就是厘米数。**
注:学生自己复习用,仅供参考/