一、硬件介绍
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模块,实现更多功能的扩展;
我要赚赏金
