一、项目概述
本项目基于 NXP FRDM-MCXA153 开发板(Cortex-M33 @ 96 MHz),以 HC-SR04 兼容超声波模块 作为距离传感器,通过 SSD1306 OLED 显示屏 实时显示测距结果,最终实现一台结构完整、运行稳定的简易超声波测距仪。
实现的功能
超声波测距:4 cm ~ 400 cm 范围,厘米级显示精度
OLED 实时刷新:距离数值
串口同步回显:MCU-Link 虚拟串口 115200 波特率输出原始数据
二、系统总体框图
┌─────────────────┐ Trig (GPIO) ┌──────────────┐ │ │ ──────────────────────> │ │ │ │ │ HC-SR04 │ │ │ <────────────────────── │ 超声波模块 │ │ FRDM-MCXA153 │ Echo (CTIMER IN7) │ │ │ │ └──────────────┘ │ (主控 MCU) │ │ │ SDA / SCL (I2C) ┌──────────────┐ │ │ ──────────────────────> │ │ │ │ │ SSD1309 OLED │ │ │ <───────────────────── │ 128×64 I2C │ │ │ │ 显示屏 │ │ │ └──────────────┘ │ │ │ │ USB Type-C ┌──────────────┐ │ │ <──────────────────────> │ 电脑 / 串口 │ │ │ (MCU-Link 虚拟串口) │ 调试终端 │ └─────────────────┘ └──────────────┘
整个系统的信号链路非常清晰:MCU 触发超声波模块 → 定时器捕获回响脉宽 → 换算成距离值 → 分别送到 OLED 屏和串口输出
三、硬件设计与接线
3.1 物料清单
| 1 | 主控板 | NXP FRDM-MCXA153 | — | 1 |
| 2 | 超声波模块 | HC-SR04 兼容 | GPIO + 输入捕获 | 1 |
| 3 | OLED 显示屏 | SSD1306 驱动,128×64 | I2C | 1 |
3.2 接线表
| 超声波触发 | P3_14 | Trig | GPIO 推挽输出,10 μs 高脉冲触发 |
| 超声波回响 | P3_15 (CT_INP7) | Echo | CTIMER0 通道 7 输入捕获 |
| I2C 数据线 | P3_28 (LPI2C0_SDA) | OLED SDA | LPI2C0 数据总线 |
| I2C 时钟线 | P3_27 (LPI2C0_SCL) | OLED SCL | LPI2C0 时钟总线 |
四、软件设计
4.1 软件架构分层
┌─────────────────────────────────┐ │ 应用层(主循环) │ │ 测距状态机 + 显示刷新 + LED │ ├─────────────────────────────────┤ │ 驱动层 │ │ ┌─────────┐ ┌────────┐ ┌────┐│ │ │ultra_sr04│ │oled_ss1309│ │led││ │ └─────────┘ └────────┘ └────┘│ ├─────────────────────────────────┤ │ MCUXpresso SDK BSP │ │ GPIO / CTIMER / LPI2C / LPUART│ └─────────────────────────────────┘
4.2 超声波驱动(CTIMER 输入捕获)
这部分在过程帖里已经详细讲过了,核心思路就是:
GPIO 给 10 μs 高脉冲触发测距
Echo 上升沿 → CTIMER 记录时间戳 t1
Echo 下降沿 → CTIMER 记录时间戳 t2
脉宽 = t2 - t1,距离 = 脉宽(μs) × 0.017 cm
4.3 SSD1306 OLED 驱动(LPI2C 轮询模式)
I2C 通信协议要点:
器件地址:0x3C(7 位地址,R/W 位由硬件自动处理)
控制字节:第 6 位(Co)= 0 表示后续全是数据,= 1 表示后面跟命令
发命令:控制字节 = 0x00
发显存数据:控制字节 = 0x40
总线速率:400 kHz Fast Mode
OLED 驱动核心函数:
/**
* 函 数:OLED写命令
* 参 数:Command 要写入的命令值,范围:0x00~0xFF
* 返 回 值:无
*/
void OLED_WriteCommand(uint8_t Command)
{
uint8_t transferData[2] = {0x00U, Command};
lpi2c_master_transfer_t transfer = {0};
transfer.slaveAddress = 0x3CU;
transfer.direction = kLPI2C_Write;
transfer.data = transferData;
transfer.dataSize = sizeof(transferData);
(void)LPI2C_MasterTransferBlocking(LPI2C0_PERIPHERAL, &transfer);
}
/**
* 函 数:OLED写数据
* 参 数:Data 要写入数据的起始地址
* 参 数:Count 要写入数据的数量
* 返 回 值:无
*/
void OLED_WriteData(uint8_t *Data, uint8_t Count)
{
uint8_t transferData[129];
lpi2c_master_transfer_t transfer = {0};
transferData[0] = 0x40U;
memcpy(&transferData[1], Data, Count);
transfer.slaveAddress = 0x3CU;
transfer.direction = kLPI2C_Write;
transfer.data = transferData;
transfer.dataSize = (size_t)Count + 1U;
(void)LPI2C_MasterTransferBlocking(LPI2C0_PERIPHERAL, &transfer);
}字模用的是 8×16 ASCII 字库,直接从数组里查表取模,往显存里塞就行。
4.4 主循环逻辑
while (1)
{
HCSR04_TriggerMeasurement();
/* Keep a 200 ms trigger period; CTIMER interrupts handle capture/timeout. */
SDK_DelayAtLeastUs(HCSR04_MEASURE_INTERVAL_US, SystemCoreClock);
/* Fail safe if the expected 30 ms match interrupt did not arrive. */
if ((s_hcsr04State == kHcsr04WaitingForRise) ||
(s_hcsr04State == kHcsr04WaitingForFall))
{
CTIMER_StopTimer(HCSR04_CAPTURE_TIMER);
s_hcsr04State = kHcsr04TimedOut;
}
if (s_hcsr04State == kHcsr04Complete)
{
/* distance in tenths of a centimeter: ticks * 343 / 192,000 */
uint32_t distanceTenthsCm = (s_pulseWidthTicks * 343U) / 192000U;
PRINTF("Echo pulse: %u ticks, distance: %u.%u cm\r\n",
(unsigned int)s_pulseWidthTicks,
(unsigned int)(distanceTenthsCm / 10U),
(unsigned int)(distanceTenthsCm % 10U));
OLED_ClearArea(0, 32, 128, 16);
OLED_Printf(0, 32, OLED_8X16, "%u.%u cm",
(unsigned int)(distanceTenthsCm / 10U),
(unsigned int)(distanceTenthsCm % 10U));
}
else
{
PRINTF("HCSR04 timeout (>30 ms)\r\n");
OLED_ClearArea(0, 32, 128, 16);
OLED_ShowString(0, 32, "No echo", OLED_8X16);
}
OLED_UpdateArea(0, 32, 128, 16);
}五 运行效果

▲ 近距离(约 4 cm)
▲ 中距离(约 40 cm)
我要赚赏金
