这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let'sdo|2026年第2期】简易超声波测距仪-基础任务实现

共1条 1/1 1 跳转至

【let'sdo|2026年第2期】简易超声波测距仪-基础任务实现

菜鸟
2026-08-31 00:29:49     打赏

1. 实现方案

本任务使用三条独立链路:

HC-SR04 TRIG/ECHO --GPIO--> MCXA153 --LPUART0/MCU-Link--> 电脑
                                  |
                                  +--LPI2C0--> SSD1306 OLED

HC-SR04 不是串口传感器。MCU 给 TRIG 输出至少 10 us 的高电平,模块在 ECHO 输出与声波往返时间成正比的高电平脉冲。Zephyr 内置 hc_sr04 驱动完成 GPIO 配置、边沿测量和距离换算;应用通过 sensor_sample_fetch() 和 sensor_channel_get() 获取距离。

2. 硬件连接

模块引脚FRDM-MCXA153备注
OLED VCC3V3不建议接 5 V
OLED GNDGND共地
OLED SDAArduino D14 / P1_8LPI2C0 SDA
OLED SCLArduino D15 / P1_9LPI2C0 SCL
HC-SR04 VCC5 V按模块规格供电
HC-SR04 GNDGND与 MCU 共地
HC-SR04 TRIGArduino D2 / P2_4MCU 输出
HC-SR04 ECHOArduino D7 / P3_1MCU 输出

电脑通过 USB 连接开发板的 J15,J15 对应 MCU-Link 虚拟串口,不需要再占用 Arduino UART 引脚。串口终端设置为 115200, 8 data bits, no parity, 1 stop bit。

3. 设备树配置

应用中的 boards/frdm_mcxa153.overlay 完成两项声明:

  1. 在 lpi2c0 下声明 solomon,ssd1306,地址为 0x3c,分辨率为 128x64。

  2. 声明 hc-sr04 节点,TRIG 为 gpio2[4],ECHO 为 gpio3[1]。

如果 OLED 扫描到的地址是 0x3d,把 reg = <0x3c>; 改为 reg = <0x3d>;。如果屏幕控制器是 SH1106,应将 compatible 改为 Zephyr 支持的 SH1106 绑定,并按实际分辨率调整参数。

4. Kconfig 配置

prj.conf 打开了:

  • CONFIG_SENSOR、CONFIG_HC_SR04:传感器 API 和 HC-SR04 驱动

  • CONFIG_I2C、CONFIG_DISPLAY:I2C 和显示框架

  • CONFIG_CHARACTER_FRAMEBUFFER:在单色 OLED 上显示字符

  • CONFIG_CONSOLE、CONFIG_UART_CONSOLE、CONFIG_PRINTK:向电脑发送串口日志

5. 构建、烧录和观察输出

构建和烧录可直接使用工程现有的 Zephyr 工作区配置,本文重点放在应用代码本身。烧录后打开 J15 对应的 MCU-Link 虚拟串口(115200、8N1),按下 RESET 即可观察输出:

Ultrasonic OLED demo started
SW2 toggles the display unit: cm / m
distance_raw=234 mm distance_filtered=234 mm range=cm

6. 代码实现分析

应用主体位于 src/main.c,代码按照“设备初始化—采样—数据处理—输出显示—周期等待”的流程运行。

6.1 通过设备树获取外设

#define SENSOR_NODE DT_NODELABEL(hcsr04)
#define DISPLAY_NODE DT_CHOSEN(zephyr_display)
#define BUTTON_NODE DT_ALIAS(sw0)

static const struct device *const g_sensor = DEVICE_DT_GET(SENSOR_NODE);
static const struct device *const g_display = DEVICE_DT_GET(DISPLAY_NODE);
static const struct gpio_dt_spec g_button = GPIO_DT_SPEC_GET(BUTTON_NODE, gpios);

代码不直接写死 GPIO 寄存器,而是通过设备树节点获得设备对象:

  • hcsr04 对应 overlay 中的 HC-SR04 节点,驱动负责 TRIG 输出和 ECHO 脉宽测量;

  • zephyr_display 指向 SSD1306 OLED;

  • sw0 是板卡 DTS 中的 SW2 按键别名。

device_is_ready() 在运行时检查驱动是否初始化成功,避免在设备不可用时继续访问硬件。

6.2 HC-SR04 测距调用

int result = sensor_sample_fetch(g_sensor);
if (result == 0) {
    result = sensor_channel_get(g_sensor,
                                SENSOR_CHAN_DISTANCE,
                                &distance);
}

sensor_sample_fetch() 触发一次测量。Zephyr 的 HC-SR04 驱动会输出约 10 us 的 TRIG 脉冲,然后通过 ECHO 的上升沿和下降沿计算高电平时间。sensor_channel_get() 将结果写入 struct sensor_value。

sensor_value 使用整数部分 val1 和微单位部分 val2 表示,因此程序将其转换为毫米:

int32_t raw_mm = (int32_t)distance.val1 * 1000
               + distance.val2 / 1000;

串口保留毫米单位,便于判断传感器的原始输出和定位接线问题。

6.3 五点滑动平均滤波

#define FILTER_WINDOW 5
static int32_t g_filter_values[FILTER_WINDOW];

moving_average_mm() 使用环形数组保存最近 5 次有效采样。每次写入后遍历当前有效样本求平均:

filtered = (sample[n] + sample[n-1] + ... + sample[n-4]) / N

刚启动时 N 小于 5,只对已经采集到的样本求平均,因此不会因为数组尚未填满而引入额外的零值。滤波后的数值用于 OLED 显示,同时串口同时输出 raw 和 filtered 便于对比滤波效果。

6.4 OLED 初始化和刷新

display_init() 依次完成以下工作:

  1. 检查 SSD1306 设备状态;

  2. 设置单色像素格式;

  3. 初始化 Zephyr Character Framebuffer(CFB);

  4. 选择设备支持的字体;

  5. 清屏并关闭显示 blanking。

每次测量后,display_distance() 先清除上一帧,再按固定坐标绘制四行内容:

cfb_framebuffer_clear(g_display, false);
cfb_print(g_display, "Distance:", 0, 0);
cfb_print(g_display, distance_line, 0, 16);
cfb_print(g_display, range_line, 0, 32);
cfb_print(g_display, "Filter: AVG5", 0, 48);
cfb_framebuffer_finalize(g_display);

短量程模式直接将毫米转换为一位小数的厘米,例如 234 mm 显示为 23.4 cm;长量程模式显示为米并保留三位小数,例如 234 mm 显示为 0.234 m。超过当前模式的最大值时显示 OUT OF RANGE。

6.5 SW2 按键中断和消抖

static volatile bool g_long_range;

SW2 配置为 GPIO 中断,按键有效沿触发 button_pressed()。回调通过 k_uptime_get_32() 记录时间,只有相邻两次触发间隔达到 250 ms 才切换 g_long_range,从而抑制机械按键抖动。

该变量只改变显示单位和显示量程,不会改变 HC-SR04 的硬件测量范围:

  • false:显示 0-100 cm;

  • true:显示 0-4 m。

6.6 主循环和异常处理

主循环每 250 ms 执行一次测距。测量成功时依次完成单位转换、滑动平均、串口打印和 OLED 刷新;驱动返回错误时打印错误码,并在 OLED 上显示 Sensor error,不会把错误数据当成 0 cm。

整体数据流如下:

TRIG/ECHO -> sensor_sample_fetch()
          -> sensor_channel_get()
          -> 毫米换算 -> AVG5 滤波
          -> UART 输出 + OLED 显示
          -> 延时 250 ms 后重复


串口数据查看:

image.png

屏幕显示信息:

image.png


共1条 1/1 1 跳转至

回复

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