这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let'sdo|2026年第2期】成果帖:带存在性检测的智能超声波测距仪

共1条 1/1 1 跳转至

【let'sdo|2026年第2期】成果帖:带存在性检测的智能超声波测距仪

菜鸟
2026-09-03 20:13:57     打赏

一、项目介绍

大家好。这是我在 "Let's do 2026 第二期·简易超声波测距仪实践" 里完成的项目——一个带存在性检测的智能超声波测距仪

跟常规测距仪不一样的地方在于:我没有让超声波模块一直空转测距,而是加了一颗毫米波存在性检测传感器做前置判断。有物体进入检测区,才唤醒超声波去测精确距离;测完的结果通过屏幕和串口两路输出,还能在上位机上实时画出距离曲线。

这个"门控"结构不只是为了省那点电。超声波每次测距都要发一串 40kHz 声波脉冲,同一个房间里两台设备会互相拍频,对着硬墙的回声也会串扰。让毫米波来决定什么时候该测,没有目标时让空气保持安静,读数反而更稳。

开箱贴里我原本的设想是让毫米波直接参与桌面级测距。过程贴调通之后发现这条路走不通:C4001 的量程是 1.2m–35m,定位是室内存在性检测,近距离的精度和分辨率撑不起桌面级的精细测量。于是重新规划成现在这个分工——毫米波管"有没有",超声波管"有多远",各自干最擅长的事。


二、系统框图

mcxa153_diagram.png

触发链:C4001 持续上报存在帧 → 驱动解析出存在位 → 应用层判断 → 有目标则触发 US-100 发 10μs 脉冲、测回波脉宽换算距离 → 屏幕显示 + 串口输出 → 上位机绘图。


三、硬件介绍

3.1 主控:FRDM-MCXA153

恩智浦的紧凑型评估板,板载 MCX A153(Cortex-M33 @ 96MHz),带板载 MCU-Link 调试器,USB 供电即可直接用,扩展接口齐全。拿来做这种多传感器融合的小型原型上手成本很低。

有一个参数需要特别记住:片上 SRAM 只有 24KB。这是整个项目最硬的约束,后面显示方案的设计完全是被它逼出来的。

3.2 存在性检测:DFRobot C4001(SEN0609)

基于 24GHz 毫米波、FMCW 调制,检测范围最大 25 米,波束角 100°×40°,工作温度 -40℃~85℃,尺寸 26×30mm。

选它做"触发开关"的理由很实在:在 presence 模式下,对静止不动的人也能持续判定为"存在"。这正是 PIR 热释电和"距离阈值"这类简单方案最容易误判的场景——人坐着不动,PIR 就当没人。而且毫米波受光照、粉尘影响小,桌面环境下很稳。

3.3 精确测距:US-100 超声波

外形和常见的 HC-SR04 几乎一样,但多了几个实用特性:

  • 宽电压供电 2.4V–5.5V,3V 和 5V 系统都能直接用

  • 量程 2cm–450cm(实际 10cm–250cm 效果最好),精度 0.3cm + 1%

  • 测量角度小于 15°,工作电流仅 2mA

  • 双工作模式,靠背面跳线切换:拔掉跳线是 HC-SR04 兼容模式(Trig/Echo 引脚);装上跳线是 UART 模式(9600 波特率,发 0x55 读回两字节毫米距离,发 0x50 读回温度)

本项目用的是拔掉跳线的 Trig/Echo 模式,理由是这个模式对时序的掌控更直接,也便于写成标准的 Zephyr sensor 驱动。

3.4 显示:2.4 寸 ST7789V TFT

240×320 分辨率,RGB565 色深,SPI 接口。用来做人机界面,实时显示测量结果。


四、电路连接

三个模块全部走软件模拟接口,没有占用任何一个硬件外设控制器:

FRDM-MCXA153                          外设
─────────────                        ──────────────────────────
P3_28 ──── SCK  ────────────────┐
P3_27 ──── SDA/MOSI ────────────┤
P2_5  ──── CS   ────────────────┼──▶  2.4" ST7789V TFT
P3_14 ──── DC   ────────────────┤     (MISO 不接,write-only)
P3_15 ──── RES  ────────────────┘     BLK ── 3V3 (背光常亮)
3V3   ──── VCC   /  GND ── GND

P1_1  ──── RX  ─────────────────┐
P1_2  ◀─── TX  ─────────────────┼──▶  C4001 毫米波 (9600 8N1)
3V3   ──── VIN  /  GND ── GND   ┘     OUT 脚不用,见 5.1

P3_6  ──── Trig ────────────────┐
P3_7  ◀─── Echo ──[ 分压 ]──────┼──▶  US-100 超声波
5V    ──── VCC  /  GND ── GND   ┘     (背面跳线帽拔掉 = HC-SR04兼容模式)

两个必须注意的点

① US-100 的 Echo 输出是 5V,而 MCXA153 的引脚不耐 5V,必须加电平转换或分压电阻,直接接会损伤 IO。

② C4001 的收发方向容易接反:传感器的 TX 是 MCU 的 rx。在设备树里 rx-gpios 挂的是传感器的 TX 线。接反的表现和"传感器坏了"一模一样——命令发得出去,一个字节也回不来。

引脚冲突处理

板级设备树默认使能了一批外设,它们的 pinctrl 会在初始化时抢走引脚,必须在 overlay 里关掉:

关掉的节点占用的引脚冲突对象
lpuart2P3_14 / P3_15屏幕的 DC / RES
lpspi0P1_0..P1_3毫米波的 tx / rx
flexpwm0_pwm0P3_6 / P3_7超声波的 Trig / Echo

五、设计思路:一条触发链

整个系统的工作流是一条链,下面按环节拆开讲,也顺带说明用到了板子的哪些资源。

5.1 存在检测环节

C4001 持续做存在性检测。这里有个选型决定值得说明:

模块提供了一个 OUT 引脚,直接输出"有没有目标"的电平,接一个 GPIO 就能用,最省事。但我最终没有用 OUT,而是走串口读。原因是 OUT 只给一个裸的开关量,而串口能拿到完整的上报帧,并且可以在初始化时把检测窗口、触发距离、灵敏度、确认延时、消失延时这些参数全部写进模块的 flash——OUT 引脚的行为本身就取决于这些参数,不配置的话只能用默认值。

C4001 的串口协议是纯 ASCII,像个小型命令行终端:命令以 rn 结尾、参数空格分隔,成功回 Done、失败回 Error,还会打印提示符 DFRobot:/>。启动后模块不问自答地持续吐帧:

$DFHPD,<detected>, , , *                                presence 模式
$DFDMD,<count>,<idx>,<range>,<speed>,<energy>, , *      speed 模式

需要注意帧尾的 * 只是分隔符,不是 NMEA 校验和,这些帧没有任何差错检测

麻烦的是,MCXA153 上我选的 P1_1/P1_2 没有 LPUART 功能,所以这个串口本身也得软件模拟(Zephyr 的 zephyr,uart-bitbang)。发送和接收各占一个 ctimer 而不是共用——模块可能在我们还没发完命令时就开始推帧,共用定时器只能半双工。

5.2 测距环节

一旦判定有目标,MCU 立刻唤醒 US-100 开始测距。流程是:

  1. 保证距上次触发有 60ms 静默期(数据手册要求,否则上一次的余波会被误当成这次的回波)

  2. 使能 Echo 引脚的双边沿 GPIO 中断

  3. 在 Trig 引脚上打一个不小于 10μs 的高电平脉冲

  4. 等信号量

回波捕获在中断里完成:上升沿记录时间戳,下降沿计算脉宽、关中断、放行信号量。

距离由回波高电平持续时间算出:

$$d = frac{c cdot t}{2}$$

其中 $t$ 是回波高电平时间,$c$ 是声速。因为超声波走的是一个来回,所以要除以 2 得到单程距离。

声速本身跟温度有关:

$$c approx 331.4 + 0.6,T quad (text{m/s})$$

$T$ 是摄氏温度。本项目当前取 20℃ 干燥空气的 343 m/s 作为固定值,代码里写成整数运算:

distance_mm = echo_us * 343 / 2000;

关于温度补偿需要说清楚:US-100 在 UART 模式下可以读温度(发 0x50)、做声速补偿,这是它相对 HC-SR04 的一个明显优势。但本项目用的是 Trig/Echo 兼容模式,这个模式下模块只输出原始回波脉冲,温度补偿并未启用。这是当前实现的一个已知不足,详见第十一节。

还有一个超时判定:无目标时模块会输出约 38ms 的脉冲,而额定最大 4m 也只有 23ms,所以 ≥36ms 一律判为超出量程。

5.3 显示与输出环节

算出距离后,MCU 做两件事:刷新 TFT 显示当前距离,以及通过串口把数据发出去,上位机接收后实时绘制距离随时间变化的曲线。

显示这块的核心矛盾是:240×320 的 RGB565 整帧是 150KB,而片上 SRAM 只有 24KB。差了六倍多,所以根本不存在帧缓冲,也不存在"画好再刷"这条路。

方案是只保留一个字形大小的暂存区,一个字符一个字符地贴到屏幕中央:

  • 5×7 点阵字库(列优先,每列一字节),放大 6 倍 → 每个字形 30×42 像素

  • 暂存区 30×42×2 = 2520 字节,这是全程唯一的像素缓冲

  • 开机清屏时,同一块内存当成 240×5 的横条,分 64 次推出去

  • 脏字符检测:只重画和当前屏幕内容不同的那一位

最后这条不是优化癖。实测模拟 SPI 吞吐 68 KB/s,每个字形要 37ms,四位全刷 147ms;主循环周期是 250ms,四位全刷就吃掉 59%。有了脏字符检测,实际通常只重画 1–2 位。

5.4 用到的板载资源小结

资源用途
GPIO超声波 Trig 输出 / Echo 双边沿中断;模拟 SPI 的 5 根线;模拟 UART 的收发两线
ctimer0 / ctimer1模拟 UART 的发送位时钟 / 接收位时钟(9600 → 104μs 一位)
系统时钟计数k_cycle_get_32() 在 Echo 中断里做脉宽计时
LPUART0控制台与数据流输出,经板载调试器的 USB 虚拟串口,115200

需要说明的是,本项目没有使用硬件输入捕获、硬件 SPI 或硬件 UART 来对接三个外设——脉宽用 GPIO 中断加时间戳测量,屏幕和毫米波串口都是软件模拟的。代价是 CPU 参与度高,收获是引脚分配几乎完全自由,也不受"哪几个脚才有这个功能"的限制。

5.5 应用层逻辑

主循环 250ms 一拍,用绝对时间睡眠而不是相对延时——否则测量和重绘的耗时会累加进每个周期,间隔越走越偏。

next += PERIOD_MS;

c4001_sample_get(radar, &sample);
if (sample.frames_total == 0) {
    /* 本拍没帧:存在位是陈旧的而不是 false,保持上次的状态 */
} else {
    present = sample.presence;
}

if (!present) {
    digits_show(display, "");        /* 空白,超声波完全不触发 */
} else if (hcsr04_read_distance_mm(sonar, &distance_mm) < 0) {
    digits_show(display, "----");    /* 有目标但没回波,不显示过期数字 */
} else {
    digits_show(display, 厘米数);
}

k_sleep(K_TIMEOUT_ABS_MS(next));

三种显示状态的设计是有讲究的:没目标 → 空白,有目标但测不到 → ----,正常 → 数字。绝不用上一次的读数顶替,屏幕不能声称一个它并不掌握的距离。

存在判定的迟滞交给传感器自己做(confirm-delay-ms / disappear-delay-ms),应用层不再做防抖。


六、软件实现

6.1 开发环境与工程结构

基于 Zephyr 4.4。Zephyr 对 NXP 主流板子适配齐全,west boards | grep frdm 里就有 frdm_mcxa153,不需要自己做 board 移植。

但三个模块 Zephyr 里都没有现成驱动,全部自己写,放在树外模块 my_modules 里:

zephyrproject/
├── my_modules/                     # 树外驱动模块
│   ├── zephyr/module.yml           #   dts_root: . 让 bindings 可见
│   ├── dts/bindings/
│   │   ├── vnd,hcsr04.yaml
│   │   └── vnd,c4001-uart.yaml
│   ├── include/my_module/          #   对外头文件
│   └── drivers/{hcsr04, c4001}/
├── my_app/presence_ranger/         # 本项目应用
│   ├── boards/frdm_mcxa153.overlay
│   ├── src/{main.c, digits.c, digits.h}
│   └── prj.conf
└── patches/                        # 打在 zephyr/ 上的本地补丁

my_modules 不在 west manifest 里,应用的 CMakeLists.txt 要显式引入:

list(APPEND ZEPHYR_EXTRA_MODULES ${CMAKE_CURRENT_SOURCE_DIR}/../../my_modules)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})

6.2 超声波驱动(vnd,hcsr04)

完整代码在过程贴里贴过,这里只讲结构要点:

  • 配置与数据分离,Zephyr 驱动的惯例:hcsr04_config 存编译期不变的(引脚、超时、测量间隔,从设备树读),hcsr04_data 存运行期状态。

  • Echo 中断平时是关着的,只在一次测量期间打开。这样杂散边沿不会触发 ISR,也不给系统带来无谓的中断负载——三个软件模拟外设共存时这点很重要。

  • 暴露成标准 sensor API(SENSOR_CHAN_DISTANCE),另外提供便捷函数 hcsr04_read_distance_mm()。

6.3 毫米波驱动(vnd,c4001-uart)

  • 中断驱动的接收状态机。模块不问自答地持续推帧,不能用轮询。ISR 把字节收进 ring buffer,解析器按 $ 开始、* 结束切帧;保留字段是单个空格,解析失败就跳过——这是正确行为,没有值可发布。

  • 配置必须正确"括起来":sensorStop → setRunApp 切应用 → 再 sensorStop → 写参数 → saveConfig 提交到模块 flash → sensorStart。漏掉 saveConfig 的话,下次上电全丢。其中 setRunApp 和 resetSystem 是不回应的,只能盲等。

  • 区间平均。模块上报速率远高于应用读取速率,驱动把两次读取之间收到的所有帧做平均再发布,而不是只取最新一帧。既用上全部数据,也平滑了帧间抖动。

  • 双模式支持,由设备树 work-mode 选择;不支持的通道返回 -ENOTSUP 而不是读回 0,模式配错在第一次 fetch 就暴露。

对外接口一次性取全部字段,避免多次 sensor_channel_get 把不同帧的数据拼在一起:

struct c4001_sample {
    bool     presence;      /* 两种模式都有 */
    uint8_t  count;
    int32_t  range_mm;      /* 以下仅 speed 模式 */
    int32_t  speed_mmps;    /* 负数表示正在靠近 */
    uint32_t energy;
    uint16_t frames;        /* 本区间有目标的帧数 */
    uint16_t frames_total;  /* 本区间收到的总帧数,0 表示读数是陈旧的 */
};

frames_total == 0 这个字段很关键:它把"真的没目标"和"链路静默、读数已过期"区分开了。

6.4 位拼 UART 的三个补丁

Zephyr 上游的 uart_bitbang 在这块板子上需要三个修复(放在 patches/,都是实测出来的):

  1. 定时器 phandle 解析:驱动假定定时器节点下有个叫 counter 的子节点(LPC55xx 是这个结构),但 MCXA153 的 ctimer 是扁平的,导致拿到 NULL、整个 UART 起不来。

  2. 接收采样点对到位中心:原实现在起始位下降沿启动计数器、一个位时间后触发,于是每次采样都压在数据位的前沿,时钟误差余量是 +100%/−0%。实测症状是收到的字节 bit7 一律被置 1。改成首次 1.5 个位时间、之后每 1 个位时间,采样落在位中心,容错变成 ±50%。

  3. 发送中途不要重启计数器

west update 会把 zephyr/ 恢复到 manifest 版本、丢掉本地修改,所以每次 update 之后要重新打一遍。


七、主要参数(均为实测)

资源占用

项目占用容量比例
Flash67912 B128 KB51.8%
SRAM14632 B24 KB59.5%

显示性能

项目实测值
模拟 SPI 吞吐68 KB/s(约 550 kbit/s)
全屏清屏 (153600 B)2244 ms
单个字形 (2520 B)37 ms
四位全刷147 ms

传感器与系统

项目数值
超声波量程 / 精度2–450 cm(最佳 10–250 cm)/ 0.3cm + 1%
超声波触发脉冲 / 间隔 / 超时10 μs / 60 ms / 50 ms
声速取值343 m/s(20℃ 固定值,未做温度补偿)
毫米波波特率 / 位时间9600 8N1 / 104 μs,采样容错 ±52 μs
毫米波 presence 帧率约 1 Hz(实测 250ms 窗口有帧率 25%)
毫米波 speed 帧率约 10 Hz
毫米波检测窗口 / 触发距离60–600 cm / 400 cm
确认延时 / 消失延时50 ms / 2000 ms(模块默认 15000ms,太长)
主循环周期250 ms(每秒 4 次刷新)

presence 和 speed 两种模式的帧率差了一个数量级,这点在设计轮询周期时必须考虑,否则会误判成"雷达掉线"(见第十节踩坑 6)。


八、如何开启运行

打补丁

cd zephyr
git apply ../patches/uart_bitbang-mcxa153.patch

编译

source .venv/bin/activate
west build -b frdm_mcxa153 ./my_app/presence_ranger -d build/presence_ranger -p always

烧录

板载 MCU-Link 调试器,USB 直连即可:

west flash -d build/presence_ranger --runner linkserver

注意 pyocd 默认没有 MCXA153 的 target pack(会报 Target type mcxa153 not recognized),用 LinkServer 或 J-Link。

查看输出与上位机绘图

控制台走 lpuart0,经板载调试器的 USB 虚拟串口输出,115200 8N1。数据流是两列,每 250ms 一行,不管发生什么都输出一行,所以行间隔恒定、不带时间戳也能按时间画图:

# presence,distance_mm
0,0        ← 没有目标,超声波不触发
1,342      ← 有目标,超声波测到 342 mm
1,0        ← 有目标,但没有回波(软表面/斜面/超出量程)

距离为 0 的两种情况用 presence 列区分。VOFA+ 的 FireWater 协议或 Serial Studio 都能直接吃这个格式,# 开头的是注释行,两边都会忽略,接上就能看到实时曲线。

prj.conf 里有一条容易漏的:

CONFIG_LOG_PRINTK=n

不关的话 printk 会走日志子系统被批量刷出,明明是均匀产生的数据会三四行挤在一起到达,画图就废了。驱动日志照旧走日志系统。


九、实现步骤

整个开发是按"先单独调通,再逐个合并"推进的,这个方法在最后排错时救了命。

  1. 环境与板级确认 —— west boards | grep frdm 确认 frdm_mcxa153 已适配,不需要做 board 移植。

  2. 单独调通超声波 —— 建 my_app/hcsr04,只有一个设备。这是最简单的一个,先把树外模块的构建链路(ZEPHYR_EXTRA_MODULES → Kconfig → dts/bindings)跑通。

  3. 单独调通毫米波 —— 建 my_app/c4001。这一步最费劲,位拼 UART 的三个补丁都是在这里挖出来的。

  4. 单独调通屏幕 —— 建 my_app/lcdtest,复用最终的 digits.c 而不是另写一份测试代码,这样测的就是真代码。测试序列专门设计成能肉眼判故障:8888(点亮字形能点的所有像素,mdac 错会显示在错误的角落或镜像)→ ----(最细的笔画,时钟相位问题最先暴露)→ 全黑(清屏和寻址窗口有问题会有残留)→ 计数(验证脏字符重画路径)。

  5. 两两组合 —— 雷达+屏幕、雷达+超声波,各验一次。

  6. 三个合并

  7. 加显示和串口输出,完成应用层逻辑

单设备工程都保留着,出问题随时可以退回去对照。这不是仪式感——第十节里几乎每个 bug 都是靠"退回单设备 vs 组合"的对照跑出来的。


十、调试踩坑记录

这部分可能是整个项目最有价值的内容。

坑 1:tx/rx 在设备树里接反

现象:nothing received at all,三次重试全超时。

排查:写了个探针程序,用内部上拉/下拉各读一次引脚——被外部硬拉高说明有线且对端有电,跟着拉走说明悬空。

教训:这个判据有陷阱。接到对端输入脚(高阻)的好线,量出来也是"悬空",我一度据此判定"线没接",是错的。真正定位靠的是意识到"P3_27 原本是 rx,线挪到 P1_2 之后角色跟着走"——引脚的角色跟着线走,不跟着引脚号顺序走

坑 2:lpuart2 抢走屏幕的 DC/RES

板级设备树默认使能 lpuart2,pinctrl 占着 P3_14/P3_15。因为控制台的关系 LPUART 驱动一定会编进来,所以这个抢占是真实发生的。对比之下 lpspi0/lpspi1 虽然节点也是 okay,但 CONFIG_SPI_MCUX_LPSPI 没开、驱动根本没编进固件,属于纸面冲突——但还是一并关掉了,免得以后改 prj.conf 时被偷走引脚。

坑 3:最需要重试的那次 sensorStop 偏偏没有重试

驱动里写了注释:"已经在 streaming 的模块会偶尔丢掉命令而不回应——实测如此,不是理论——所以要重试",并给第一个 sensorStop 加了三次重试。但配置序列里还有第二个 sensorStop,它紧跟在 setRunApp 后面,而注释自己写着这条命令"会让模块保持运行"——也就是说第二个 stop 面对的模块必定在 streaming,是最需要重试的那一个,却没有。

抽成统一的重试函数之后,日志里能看到 attempt 1/3 后自愈,初始化从必挂变成通过。

坑 4:sensorStart 的 Done 是假的

修好坑 3 之后初始化过了,但雷达就是不推帧。

根因很隐蔽:saveConfig 会让模块忙着写自己的 flash,紧跟着的 sensorStart 落在这个窗口里被丢掉,而 saveConfig 迟到的那句 Done 正好被当成 sensorStart 的应答。于是整个对话错开一位、每条命令都"成功"、模块其实从没启动。

Done 根本不能作为"启动成功"的证据。唯一可靠的证据是数据帧本身。 改成发完 sensorStart 之后等一个真实上报帧,等不到就重发。

坑 5:ring buffer 有两个消费者

ISR 在缓冲区满时会 ring_buf_get 丢弃最旧的字节来腾地方——也就是说 ISR 不只是生产者,同时也是消费者,而线程侧的解析函数也在 get,两者之间没有任何互斥。Zephyr 的 ring_buf 只保证单生产者单消费者,索引被搞坏之后读永远返回空,而且不报任何错

触发条件是"主线程长时间不排空缓冲区",正好对应开机时那 2.2 秒的全屏清屏。加 irq_lock 解决。

坑 6:把 1Hz 当成了 10Hz——一个自己造出来的假故障

这个坑我绕了最久,值得完整记下来。

现象:三个模块合并后,串口刷满 no radar frames this interval,看起来像雷达在多设备环境下挂掉了。而单独跑、两两组合跑都正常。

错误的排查方向:我据此得出"三设备共存有干扰"的结论,然后去追 SPI 串扰、中断延迟、ring buffer 竞态,改了一堆东西。

真相:presence 模式的 $DFHPD 帧率约 1 Hz,而 speed 模式的 $DFDMD 是 10 Hz。我按 10 Hz 想当然把轮询周期定成 250ms,于是 75% 的窗口里合法地没有帧,每次都打一条警告。

更要命的是,我用来做对照的那几个单设备/两两组合工程全都是 speed 模式(10Hz,每个窗口都有帧),所以那张"雷达+屏幕 ✅ / 雷达+超声波 ✅ / 三个一起 ❌"的表格根本不是在比设备组合,是在比帧率。

真正的证据一直摆在日志里:警告的时间间隔只有 250ms 和 500ms 两种。如果链路真的断了,间隔应该全是 250ms;那些 500ms 意味着中间那一拍是有帧的。统计下来 21 秒 85 拍里有 21 拍收到帧,正好 25%,换算就是 1Hz。

教训:现象出现的场景(多设备)和根因所在的位置(帧率假设)可以毫无关系。对照实验必须严格控制变量——我的对照组换了模式却没意识到,等于对照了个寂寞。另外,告警阈值要按被观测对象的真实节奏来定,不能按自己的想当然。

最后把告警改成"连续 20 拍(5 秒)静默才报一次",这才是真正的链路丢失信号。


十一、功能演示

系统稳定运行时,控制台只有两行,之后就进入静默的数据流输出——这正是正常工作的样子:

*** Booting Zephyr OS build v4.4.0 ***
[00:00:04.906] <inf> main: ready: radar gates the ultrasonic, 250 ms period
[00:00:04.907] <inf> main: target present, ranging

关于存在检测这一环的说明

C4001 基于毫米波,灵敏度很高,能感知到检测区内人体的细微动作。正因为足够灵敏,在实际房间环境里,只要有人在场基本就会持续判定为"存在"、维持唤醒,所以这一环节不太好单独做"有人/无人"的切换演示——演示的人自己就在检测区里。

它在系统里承担的是前置触发的角色:有目标存在就唤醒后端的超声波测距,没有目标时让超声波保持待命、不做无谓的发声。下面直接演示被唤醒之后的精确测距和数据输出部分。


十二、总结与不足

完成情况

命题要求的超声波测距功能完整实现,并在此基础上加了毫米波存在门控和本地显示。三个外设全部走软件模拟接口,没有占用任何一个硬件外设控制器。两个自写驱动(vnd,hcsr04、vnd,c4001-uart)都按标准 Zephyr 驱动模型写,配置走设备树,可以直接复用到别的工程。

不足与后续方向

  1. 温度补偿没有启用。 US-100 的 UART 模式可以读温度、做声速补偿,这是它相对 HC-SR04 的明显优势,但和当前的 Trig/Echo 模式是靠跳线帽二选一的。声速在 0℃ 到 30℃ 之间差约 6%,做了补偿能明显提精度。要么换到 UART 模式(得重写一份驱动),要么外挂一颗独立温度传感器按 $c = 331.4 + 0.6T$ 动态修正——后者改动更小,是下一步优先做的。

  2. 毫米波的距离信息完全没用上。 现在只用了它的存在位。它在 speed 模式下能给出距离和径向速度,理论上可以和超声波做真正的数据融合——超声波定近距离、毫米波定远距离和运动趋势。没做的原因是两种模式不能同时跑,需要动态切换应用,而每次切换都要重新走一遍完整的配置和 flash 提交流程,代价太大。

  3. 模拟 SPI 的 68 KB/s 限制了显示的丰富度。 现在只显示四位数字。要做曲线、动画之类的,得换到硬件 LPSPI(板上有 LPSPI0/LPSPI1,只是引脚要重新规划),能快一到两个数量级。

  4. SRAM 只剩 40%。 想加 LVGL 之类的 GUI 库基本不可能,24KB 这个量级只能自己画。

  5. "低功耗"目前只是"不做无谓测量"。 没有目标时超声波不触发,省掉了发射能耗和声学串扰,但 MCU 本身仍在 250ms 一拍地轮询,没有进入任何睡眠模式。真要做低功耗,可以改用 C4001 的 OUT 引脚做 GPIO 唤醒源,配合 Zephyr 的 PM 子系统让 MCU 在无目标时进低功耗态。

一点体会

这个项目大部分时间不是花在写驱动上,而是花在定位那些"看起来像 A、实际是 B"的问题上。第十节六个坑里,有四个的现象和根因隔着好几层。

最有用的两个习惯:一是每个外设先单独建一个工程调通并保留,出问题能立刻退回去做对照;二是不要相信"应该是这样",去看实际的字节。坑 4 里那 102 个字节 dump 出来是 ff + DFRobot:/> + 完整的数据帧——除了第一个毛刺字节以外全都是干净的,这一眼就把"波特率不对""电气干扰"这些猜测全部排除了,把范围收缩到协议握手层面。如果只看驱动打印的那句 wrong baud rate?,方向就完全跑偏了。

最后,感谢 DigiKey 得捷EEPW 提供的这次机会,谢谢大家。



共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]