这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【let'sdo|2026年第2期】简易超声波测距仪实践【成果:超声波数据监控面

共1条 1/1 1 跳转至

【let'sdo|2026年第2期】简易超声波测距仪实践【成果:超声波数据监控面板】

助工
2026-09-13 21:25:52     打赏


一、硬件介绍

1、产品特点

FRDM-MCXA153是一款NXP推出的基于Arm Cortex-M33内核(96MHz)开发板,搭载128KB带ECC纠错Flash与32KB SRAM,板载MCU-Link高速调试器,双USB‑C分别用于调试通信与设备功能,集成RGB指示灯、PMOD接口、用户按键、温度传感器等资源;



主要特性

内核:Arm Cortex-M33(96MHz),无FPU / TrustZone

Flash:128KB(ECC 校验,1 位纠错、2 位检错)

SRAM:32KB,其中8KB 支持 ECC


芯片运行功耗:59μA/MHz

掉电模式(SRAM 全保留):6.5μA

深度掉电休眠:0.39μA(394nA)


集成 MCU-Link 片上调试器(主控 LPC55S69)

RGB三色LED

3个用户按键(Reset、WakeUp、ISP)

I3C/I2C 数字温度传感器(P3T1755)


系统框图



2、引脚示意图



3、超声波测距模块

Unit Ultrasonic-IO 是一款GPIO接口的超声波测距传感器;

硬件采用RCWL-9620超声波测距单芯片搭配16mm探头,能够实现2cm-450 cm范围内的精准测距;



主要特性


4、无线功能模块(ESP32C5)

使用的是XIAO ESP32C5开发板,集成ESP32C5 32位双核处理器芯片,具有双频Wi-Fi 6子系统(2.4 GHz 和 5 GHz);



功能引脚示意图


5、供电模块(Unit TimerPWR)

Unit TimerPWR是一款定时供电单元,具备“充放电、定时开关”等主要功能;

通过Grove接口为外部设备提供 5V/800mA的电源输出;


内置INA3221传感器,可实时监控电源输入输出的电流和电压;

待机电流:DC4.2V@36.67uA;


主要特性

- RTC 循环定时控制

- 电流 / 电压监测




二、环境搭建

详见【快速环境搭建



三、功能实现

1、硬件搭建

将超声波测距模块连接到开发板的PMOD接口的对应引脚上,以及将ESP32C5的D7(RX)连接到开发板的P3_15(TX)引脚上;

TimerPWR通过外部锂电池同时给开发板和ESP32C5提供供电,并与ESP32C5通过I2C引脚进行通信;


实物搭建效果:




2、功能效果

效果:主要通过FRDM-MCXA153开发板、超声波测距模块(Ultrasonic-IO)、无线功能模块(ESP32C5)、电源供电模块(TimerPWR)实现超声波测距WIFI监测系统;

MCXA153采集超声波模块测距数据,并将滤波后的数据通过串口转发给ESP32C5,ESP32C5同时通过I2C读取TimerPWR电源监测的数据,最后将相关数据、控制功能通过HTTP Server显示到Web UI界面上进行实时监控(供电状态监测、超声波测距显示、WIFI远程控制等)


超声波模块工作原理

通过外部MCU给模块Trig脚一个大于10uS的高电平脉冲;

模块会给出一个与距离等比的高电平脉冲信号,可根据脉宽时间“T”算出当前距离;


距离 = T * C / 2 (C为声速)


声速温度公式:c ≈ 331.5 + 0.61 * t

例:25℃:

c ≈ 331.5 + 0.61 * 25 = 346.75 m/s


时序图



主要运行流程

MCXA153采集超声波数据 → 串口发送数据 → ESP32C5数据推送到网页实时显示与控制;


Web UI界面

整体UI界面效果



上半部分

开关控制: 控制OLED屏幕、5V供电输出、定时模式的开启与关闭;

定时设置:支持单次 / 循环两种运行定时模式,可分别单独设置运行与停止时长(最长255小时);

电池状态:实时显示当前锂电池电压、电流及充放电状态;

输出负载:实时显示输出端的电压、电流及供电设备的实时功耗;



下半部分

续航 / 充电时间:根据电池的实际容量 (1200mAh),以及当前电池充/放电电流,计算放电状态下的续航时间 / 充电状态下充满电所需的时间;

超声波距离数据:下方图像显示最近30s内的超声波监测到的数据变化趋势;

同时显示当前超声波模块历史监测到的最远、最近、以及当前的实时距离;



四、代码编写

1、在之前工程模板中,使用 `Config Tools` 图形化工具,继续配置相关功能;


2、引脚功能配置

在之前的基础上,配置 `LPUART2_TXD`功能引脚;

P3_15:LPUART2_TXD

串口波特率115200;



主要相关代码

main.c(MCXA153):将超声波数据通过LPUART2进行传输;

Web UI界面(ESP32C5):通过Arduino IDE实现相关功能代码(显示 / 控制相关数据);

main.c
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "peripherals.h"
#include "clock_config.h"
#include "fsl_adapter_gpio.h"
#include "fsl_lpuart.h"
#include <stdio.h>
#include <string.h>

// TRIG = P3_28
#define HC_SR04_TRIG_HANDLE BOARD_INITPINS_TRIG_PIN_handle
// ECHO = P3_27
#define HC_SR04_ECHO_HANDLE BOARD_INITPINS_ECHO_PIN_handle

volatile uint32_t echo_start_tick = 0U;
volatile uint32_t echo_end_tick = 0U;
volatile uint8_t echo_capture_done = 0U;

void Systick_FreeCounter_Init(void)
{
    SysTick->LOAD = 0xFFFFFFU;
    SysTick->VAL = 0U;
    SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk | SysTick_CTRL_ENABLE_Msk;
}

/*
 * ECHO 引脚中断回调
 * 由 GPIO3_IRQHandler 自动分发并自动清中断标志
 * 读引脚电平判断上升/下降沿
 */
static void HC_SR04_Echo_Callback(void *param)
{
    uint8_t level;
    (void)param;
    (void)HAL_GpioGetInput(HC_SR04_ECHO_HANDLE, &level);
    if (level != 0U)
    {
        echo_start_tick = SysTick->VAL;
        echo_capture_done = 0U;
    }
    else
    {
        echo_end_tick = SysTick->VAL;
        echo_capture_done = 1U;
    }
}

void HC_SR04_Gpio_Init(void)
{
    /* 注册 ECHO 中断回调 */
    (void)HAL_GpioInstallCallback(HC_SR04_ECHO_HANDLE, HC_SR04_Echo_Callback, NULL);
    /* 双边沿中断触发 */
    (void)HAL_GpioSetTriggerMode(HC_SR04_ECHO_HANDLE, kHAL_GpioInterruptEitherEdge);
}

/*
 * 基于 SysTick 的延时
 * 1us = 96 ticks;SysTick->VAL 递减计数
 */
static void delay_us(uint32_t us)
{
    uint32_t start = SysTick->VAL;
    uint32_t ticks = us * 96U;
    while (((start - SysTick->VAL) & 0xFFFFFFU) < ticks)
    {
    }
}

static void delay_ms(uint32_t ms)
{
    while (ms > 0U)
    {
        delay_us(1000U);
        ms--;
    }
}

/* LPUART2 TX发送数据(P3_15, 115200)  */
static void LPUART2_SendString(const char *s)
{
    LPUART_WriteBlocking(LPUART2_PERIPHERAL, (const uint8_t *)s, strlen(s));
}

void HC_SR04_Trigger(void)
{
    (void)HAL_GpioSetOutput(HC_SR04_TRIG_HANDLE, 1U);
    delay_us(11); /* TRIG ≥10us 精准延时 */
    (void)HAL_GpioSetOutput(HC_SR04_TRIG_HANDLE, 0U);
}
/* 超声波模块获取数据 */
float HC_SR04_ReadDistance(void)
{
    uint32_t delta_tick;
    uint32_t pulse_us;
    uint32_t timeout = 0U;

    echo_capture_done = 0U;
    HC_SR04_Trigger();

    while ((echo_capture_done == 0U) && (timeout < 300000U))
    {
        timeout++;
    }

    if (echo_capture_done == 0U)
    {
        return 0.0f;
    }

    if (echo_end_tick > echo_start_tick)
    {
        delta_tick = (0xFFFFFFU - echo_start_tick) + echo_end_tick;
    }
    else
    {
        delta_tick = echo_start_tick - echo_end_tick;
    }
    pulse_us = delta_tick / 96U;
    float distance_cm = ((float)pulse_us * 0.034f) / 2.0f;
    return distance_cm;
}

/* 3点中值滤波 */
static float Median_Filter_3(float a, float b, float c)
{
    if (a > b)
    {
        float t = a;
        a = b;
        b = t;
    }
    if (b > c)
    {
        float t = b;
        b = c;
        c = t;
    }
    if (a > b)
    {
        float t = a;
        a = b;
        b = t;
    }
    return b;
}

int main(void)
{
    float dist;
    char str[16];

    BOARD_InitBootClocks();      // 96MHz
    BOARD_InitPins();            // 引脚功能初始化
    BOARD_InitBootPeripherals(); // 外设功能初始化
    BOARD_InitDebugConsole();    // 板载串口功能初始化

    HC_SR04_Gpio_Init();        // 超声波模块初始化
    Systick_FreeCounter_Init(); // SysTick 计数器初始化

    while (1)
    {
        /* 3点中值滤波 */
        dist = Median_Filter_3(HC_SR04_ReadDistance(),
                               HC_SR04_ReadDistance(),
                               HC_SR04_ReadDistance());

        /* 模块有效量程 2cm ~ 450cm */
        if ((dist >= 2.0f) && (dist <= 450.0f))
        {
            PRINTF("Distance = %.2f cm\r\n", dist);
        }
        else
        {
            PRINTF("No echo(timeout)\r\n");
            dist = 0.0f;
        }
        snprintf(str, sizeof(str), "%.2f\r\n", dist);
        LPUART2_SendString(str);	// 传输数据
        delay_ms(200);
    }
}



Arduino部分:

#include "Wire.h"  
#include "m5_unit_timerpwr.hpp"  
#include <WiFi.h>  
#include <AsyncTCP.h>  
#include <ESPAsyncWebServer.h>  
  
#define PWR_SDA D4  
#define PWR_SCL D5  

M5UnitTimerPWR timer_pwr;  
  
const char* ssid     = "WIFI_SSID";  
const char* password = "WIFI_PASS";  
  
AsyncWebServer server(80);  
  
// 开关状态  
bool oledState    = true;  
bool outputState  = true;  
bool timerMode    = false;  
bool userCycleMode= false;  
// 定时参数  
uint8_t g_on_h=0, g_on_m=0, g_on_s=10;  
uint8_t g_off_h=0, g_off_m=0, g_off_s=10;  
// 超声波距离数据
int distance = -1;          // 当前距离
int maxDistance = 0;        // 最远距离
int minDistance = 99999;    // 最近距离
bool hasDistance = false;   // 是否已收到有效数据
String serialBuf = "";      // 串口缓冲
// 电池信息  
float batV,batI,groveV,groveI;  
bool isCharge;  

  
...  
HTML 部分      
...  
  
void setup(){
  Serial.begin(115200);
  delay(300);
  Serial0.begin(115200);        // 串口0:接收超声波模块数据
  WiFi.begin(ssid,password);
  while(WiFi.status()!=WL_CONNECTED){ delay(500); Serial.print("."); }
  Serial.println();
  timer_pwr.begin(&Wire,TIMERPWR_ADDR,PWR_SDA,PWR_SCL,400000U);	// 供电模块初始化
  delay(800);
  timer_pwr.set_cycle_sleep(false);
  oledState     = timer_pwr.get_oled_backlight();
  outputState   = timer_pwr.get_grove_5v_output();
  userCycleMode = false;
  timerMode     = false;
  server.on("/",HTTP_GET,[](AsyncWebServerRequest *req){
    req->send(200,"text/html",dashboard_html);
  });
  server.on("/data",HTTP_GET,[](AsyncWebServerRequest *req){
    String onTimeStr  = formatDuration(g_on_h,g_on_m,g_on_s);
    String offTimeStr = formatDuration(g_off_h,g_off_m,g_off_s);
    String json="{";
      //电源供电信息
    json += "\"charge\":"+String(isCharge?"true":"false");
    json += ",\"batV\":"+String(batV,2);
    json += ",\"batI\":"+String(batI,2);
    json += ",\"groveV\":"+String(groveV,2);
    json += ",\"groveI\":"+String(groveI,2);
    // 超声波距离数据
    json += ",\"distance\":"+String(distance);
    json += ",\"maxDistance\":"+String(hasDistance?maxDistance:-1);
    json += ",\"minDistance\":"+String(hasDistance?minDistance:-1);
    json += ",\"oled\":"+String(oledState?"true":"false");
    json += ",\"output\":"+String(outputState?"true":"false");
    json += ",\"timerMode\":"+String(timerMode?"true":"false");
    json += ",\"cycleMode\":"+String(userCycleMode?"true":"false");
    json += ",\"onTime\":\""+onTimeStr+"\"";
    json += ",\"offTime\":\""+offTimeStr+"\"";
    json += "}";
    req->send(200,"application/json",json);
  });
    
  ...
      
  server.begin();
  Serial.print("地址: http://");
  Serial.println(WiFi.localIP());
}
  
void loop(){
  // 接收超声波距离数据
  while (Serial0.available()) {
    char c = Serial0.read();
    if (c == '\n') {
      serialBuf.trim();
      if (serialBuf.length() > 0) {
        int val = serialBuf.toInt();
        distance = val;
        if (!hasDistance || val > maxDistance) maxDistance = val;
        if (!hasDistance || val < minDistance) minDistance = val;
        hasDistance = true;
      }
      serialBuf = "";
    } else if (c != '\r') {
      serialBuf += c;
    }
  }
    
  int16_t t_batV   = timer_pwr.get_battery_voltage();
  int16_t t_batI   = timer_pwr.get_battery_current();
  int16_t t_groveV = timer_pwr.get_grove_voltage();
  int16_t t_groveI = timer_pwr.get_grove_current();
  if(t_batV   != -1) batV   = (float)t_batV   / 100.0f;
  if(t_batI   != -1) batI   = (float)t_batI   / 100.0f;
  if(t_groveV != -1) groveV = (float)t_groveV / 100.0f;
  if(t_groveI != -1) groveI = (float)t_groveI / 100.0f;
  isCharge    = timer_pwr.get_battery_charge_status();
  oledState   = timer_pwr.get_oled_backlight();
  outputState = timer_pwr.get_grove_5v_output();
  delay(300);
}




五、程序烧录

1、连接USB数据线至开发板MUC-LINK端口;

2、右键当前工程项目;

3、点击 Flash the Selected Target 烧录程序到开发板上;





六、效果演示






七、总结

在本次的开发过程中,主要是在VSCode IDE开发环境中搭配NXP插件完成相关功能的开发,通过MCUXpresso Config Tools的图形化配置工具(类似于ST的CubeMX工具),可以很快速进行引脚、时钟、外设等功能的配置,无需再繁琐的手动进行相关功能的代码初始化、配置等;在实际的使用过程中,虽然开发板本身不具备WIFI、BLE等无线功能,不过有一个PMOD扩展接口,后续可以用来连接不同功能的PMOD模块,实现更多功能的扩展;







共1条 1/1 1 跳转至

回复

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