开箱贴之后拖了好一阵子,中间被几个坑卡住,终于把这个超声波测距仪跑通了。这篇帖子记录一下实际的实现过程、遇到的问题,以及最终效果。
先放结果:测距范围实测约 4cm ~ 180cm,精度在 ±1cm 以内,LCD 每 200ms 刷新一次。距离值稳定,没有明显的跳变。

接线情况:
| HC-SR04 | VCC | 5V(Arduino 排针) | 模块需 5V 供电 |
| HC-SR04 | Trig | P2_3 | 3.3V 可直接触发 |
| HC-SR04 | Echo | P2_4 | 经电阻分压(1kΩ + 2kΩ) |
| LCD | VSS / K | GND | |
| LCD | VDD / A | 5V | 背光串 100Ω 限流 |
| LCD | V0 | 10kΩ 电位器中点 | 调对比度 |
| LCD | RS | P2_5 | |
| LCD | RW | GND | 只写模式 |
| LCD | E | P2_6 | |
| LCD | D4~D7 | P2_7、P3_0、P3_1、P3_2 | 4-bit 模式 |
Echo 的分压是必须的。HC-SR04 输出 5V 电平,MCXA153 的 GPIO 耐压是 3.3V。1kΩ 串在 Echo 和 GPIO 之间,2kΩ 下拉到地,分压后约 3.33V,刚好在安全范围内。
超声波测距
HC-SR04 的时序很简单:Trig 拉高至少 10µs 触发,然后 Echo 输出高电平,持续时间就是声波往返时间。距离换算用 距离(cm) = Echo高电平时间(µs) / 58。
计时方案用 CTimer。MCXA153 没有 MRT 外设,但 CTimer 完全可以胜任微秒级计时。SDK 里有现成的 ctimer 示例可以参考。我开了 CTimer 的输入捕获通道,捕获 Echo 的上升沿和下降沿,两个时间戳相减就是高电平持续时间。不过实际调试时发现输入捕获的配置有点绕,后来简化成了 GPIO 中断 + CTimer 读计数值的方式,更直观也好调试。
测距周期用了 200ms。HC-SR04 官方建议测量周期不低于 60ms,避免上一次的回波干扰下一次触发。200ms 留了足够余量,LCD 刷新也不会闪。
LCD 驱动
NHD-0116AZ 用的是 ST7066U 控制器,兼容 HD44780 指令集,4-bit 模式只需要 6 根信号线(RS、E、D4-D7),RW 接地。4-bit 模式下每个字节要发两次:先高 4 位,再低 4 位,每次都要给 E 一个脉冲。
初始化序列是第一个坑。HD44780 上电后默认是 8-bit 模式,要切到 4-bit 必须按特定顺序发送命令,而且对延时敏感。我一开始没严格按手册的延时来,LCD 直接没反应。后来老老实实加了 40ms 上电延时,然后按 0x33 → 0x32 → 0x28 → 0x0C → 0x06 → 0x01 的顺序初始化,一次就亮了。
显示内容很简单,就一行:DIST: 123.4 cm。超过量程时显示 OVER RANGE,无回波显示 NO ECHO。
下面是主程序:
int main(void) {
BOARD_InitBootPins();
BOARD_InitBootClocks();
CTimer_Init(); // 微秒计时
LCD_Init(); // 4-bit 模式初始化
Ultrasonic_Init(); // Trig 输出、Echo 中断
LCD_Print("DIST: ---.- cm");
while (1) {
uint32_t pulse = Ultrasonic_Measure(); // 返回高电平 µs
if (pulse == 0) {
LCD_Print("NO ECHO ");
} else if (pulse > 12000) { // 约 200cm
LCD_Print("OVER RANGE ");
} else {
float dist = pulse / 58.0f;
char buf[17];
snprintf(buf, sizeof(buf), "DIST: %5.1f cm", dist);
LCD_Print(buf);
}
SDK_DelayAtLeastUs(200000, 96000000); // 200ms
}
}Ultrasonic_Measure 里:Trig 拉高 10µs 再拉低,然后等 Echo 中断更新脉冲宽度,超时(约 30ms 无回波)就返回 0
static void i2c_start(void)
{
sda_high(); scl_high(); i2c_delay();
sda_low(); i2c_delay();
scl_low(); i2c_delay();
}
static uint8_t i2c_write_byte(uint8_t data)
{
for (uint8_t i = 0; i < 8; i++) {
scl_low();
if (data & 0x80) sda_high(); else sda_low();
data <<= 1;
i2c_delay();
scl_high();
i2c_delay();
}
scl_low();
sda_dir_input();
i2c_delay();
scl_high();
i2c_delay();
uint8_t ack = sda_read();
scl_low();
sda_dir_output();
i2c_delay();
return ack;
}FRDM-MCXA153 做这个项目挺顺手的。CTimer 的微秒计时精度够用,Arduino 排针引出 5V 和 GPIO 很方便。板子自带的 MCU-Link 调试器省了外接调试器,VS Code 里直接断点单步,调时序问题的时候帮了大忙。
这次超声波测距仪算是把 GPIO、定时器、LCD 驱动串了一遍,后面打算把板载的 P3T1755 温度传感器也用上,做一个“距离 + 温度”双显示的小工具,毕竟声速也受温度影响,理论上可以做温度补偿。
我要赚赏金
