一、硬件介绍
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、OLED屏幕
Grove - OLED 黄蓝显示屏 0.96 (SSD1315) 是一款蓝色和黄色双色显示屏,分辨率为 128 * 64;

主要特性

二、环境搭建
详见【快速环境搭建】

三、功能实现
1、硬件搭建
将超声波测距模块连接到开发板的PMOD接口的对应引脚上,以及将OLED屏幕模块连接到板载的I2C接口上;
实物搭建效果:


2、功能效果
效果:MCU获取到超声波模块检测到的数据后,在OLED屏幕上实时显示数据;
超声波模块工作原理
通过外部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
时序图

主要运行流程:
时钟/引脚/外设/串口等初始化 → SysTick计数器 → 触发11us脉冲 → 等ECHO边沿中断记录时间 → 根据脉宽换算距离 → 屏幕显示距离数值;

四、代码编写
1、在之前`led blinky`模板中,使用 `Config Tools` 图形化工具,继续配置相关功能;

2、引脚功能配置
在之前的基础上,配置 LPI2C0_SDA、LPI2C0_SCL 功能引脚;
GPIO3_12:LED_RED
GPIO3_27:ECHO_PIN (开启 双边沿触发)
GPIO3_28:TRIG_PIN
GPIO1_0:SDA
GPIO1_1:SCL

3、时钟配置
使用内部RC时钟源
系统主频 96Mhz
FRO_HF_clock / LPUART0_clock:96Mhz
开启LPI2C0时钟:48Mhz;

4、外设功能配置
配置LPI2C0:100kHz

5、开启I2C SDK组件

在 Driver Configuration 路径下勾选lpi2c

6、在当前工程文件下移植`u8g2库`,用于屏幕功能的开发使用;
将下载解压后的u8g2文件夹中csrc文件夾复制到当前工程中;

主要相关代码
`u8g2_hal.c` / `u8g2_hal.h` :U8G2库与开发板I2C接口适配;
`main.c`:初始化(时钟/引脚/外设/串口 → HC-SR04 → SysTick → U8G2)+ 主循环(测距显示);
超声波测距:TRIG触发、ECHO双边沿IO中断、SysTick计时距离转换、中值滤波;
屏幕显示:
第一行显示标题:距离显示 ;
第二行显示距离值:数字 ;
第三行显示单位:cm ;
u8g2_hal.c:
...
#define U8G2_I2C_ADDR 0x3C /* 屏幕地址 */
static uint8_t s_buf[64];
static uint8_t s_len;
/* SysTick 微秒延时 */
static void hal_delay_us(uint32_t us)
{
uint32_t start = SysTick->VAL;
uint32_t ticks = us * 96u;
while (((start - SysTick->VAL) & 0xFFFFFFu) < ticks)
{
}
}
uint8_t u8x8_byte_lpi2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
uint8_t *p = (uint8_t *)arg_ptr;
uint8_t i;
(void)u8x8;
switch (msg)
{
case U8X8_MSG_BYTE_INIT:
case U8X8_MSG_BYTE_SET_DC:
break;
case U8X8_MSG_BYTE_START_TRANSFER:
s_len = 0;
break;
case U8X8_MSG_BYTE_SEND:
for (i = 0; i < arg_int; i++)
{
if (s_len < sizeof(s_buf))
{
s_buf[s_len++] = p[i];
}
}
break;
case U8X8_MSG_BYTE_END_TRANSFER:
if (s_len > 0)
{
lpi2c_master_transfer_t xfer;
memset(&xfer, 0, sizeof(xfer));
xfer.slaveAddress = U8G2_I2C_ADDR;
xfer.direction = kLPI2C_Write;
xfer.subaddress = 0;
xfer.subaddressSize = 0;
xfer.data = s_buf;
xfer.dataSize = s_len;
xfer.flags = kLPI2C_TransferDefaultFlag;
(void)LPI2C_MasterTransferBlocking(LPI2C0_PERIPHERAL, &xfer);
}
s_len = 0;
break;
default:
return 0;
}
return 1;
}
uint8_t u8x8_gpio_and_delay_lpi2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
(void)u8x8;
(void)arg_ptr;
switch (msg)
{
case U8X8_MSG_DELAY_MILLI:
{
uint32_t ms = arg_int;
while (ms-- > 0)
{
hal_delay_us(1000u);
}
break;
}
case U8X8_MSG_DELAY_10MICRO:
hal_delay_us(10u);
break;
case U8X8_MSG_DELAY_100NANO:
hal_delay_us(1u);
break;
case U8X8_MSG_DELAY_NANO:
break;
case U8X8_MSG_GPIO_AND_DELAY_INIT:
break;
default:
return 0;
}
return 1;
}
u8g2_hal.h:
#ifndef _U8G2_HAL_H_
#define _U8G2_HAL_H_
#include "u8g2.h"
/* I2C 数据收发回调函数 */
uint8_t u8x8_byte_lpi2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
/* U8G2 延时与GPIO引脚回调函数 */
uint8_t u8x8_gpio_and_delay_lpi2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
#endif /* _U8G2_HAL_H_ */
main.c:
...
// 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;
u8g2_t u8g2;
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--;
}
}
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;
}
/* ---- 标题汉字 ---- */
static const uint8_t zh16x16_data[4][32] = {
{0x00, 0x00, 0x7D, 0xFE, 0x45, 0x00, 0x45, 0x00, 0x45, 0x00, 0x7D, 0xFC, 0x11, 0x04, 0x11, 0x04,
0x5D, 0x04, 0x51, 0x04, 0x51, 0xFC, 0x51, 0x00, 0x5D, 0x00, 0xE1, 0x00, 0x01, 0xFE, 0x00, 0x00}, /* 距 */
{0x02, 0x00, 0x01, 0x00, 0xFF, 0xFE, 0x00, 0x00, 0x14, 0x50, 0x13, 0x90, 0x14, 0x50, 0x1F, 0xF0,
0x01, 0x00, 0x7F, 0xFC, 0x42, 0x04, 0x44, 0x44, 0x4F, 0xE4, 0x44, 0x24, 0x40, 0x14, 0x40, 0x08}, /* 离 */
{0x00, 0x00, 0x1F, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x1F, 0xF0, 0x10, 0x10, 0x10, 0x10, 0x1F, 0xF0,
0x04, 0x40, 0x44, 0x44, 0x24, 0x44, 0x14, 0x48, 0x14, 0x50, 0x04, 0x40, 0xFF, 0xFE, 0x00, 0x00}, /* 显 */
{0x00, 0x00, 0x3F, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFE, 0x01, 0x00,
0x01, 0x00, 0x11, 0x10, 0x11, 0x08, 0x21, 0x04, 0x41, 0x02, 0x81, 0x02, 0x05, 0x00, 0x02, 0x00}, /* 示 */
};
/* 用 U8G2 绘制 16x16 汉字 */
static void draw_zh(u8g2_t *u8g2, uint8_t x, uint8_t y, uint8_t idx)
{
static uint8_t xbm[32];
uint8_t i, b;
for (i = 0; i < 32; i++)
{
uint8_t v = zh16x16_data[idx][i];
uint8_t r = 0;
for (b = 0; b < 8; b++)
{
if ((v >> b) & 1U)
{
r |= (uint8_t)(1U << (7 - b));
}
}
xbm[i] = r;
}
u8g2_DrawXBM(u8g2, x, y, 16, 16, xbm);
}
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 计数器初始化
u8g2_Setup_ssd1306_i2c_128x64_noname_f(&u8g2, U8G2_R0,
u8x8_byte_lpi2c,
u8x8_gpio_and_delay_lpi2c);
u8g2_InitDisplay(&u8g2);
u8g2_SetPowerSave(&u8g2, 0);
u8g2_ClearBuffer(&u8g2);
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\r\n");
dist = 0.0f;
}
snprintf(str, sizeof(str), "%.2f", dist);
u8g2_FirstPage(&u8g2);
do
{
/* 标题:顶部 */
draw_zh(&u8g2, 32, 0, 0); /* 距 */
draw_zh(&u8g2, 48, 0, 1); /* 离 */
draw_zh(&u8g2, 64, 0, 2); /* 显 */
draw_zh(&u8g2, 80, 0, 3); /* 示 */
/* 数值:中间 */
u8g2_SetFont(&u8g2, u8g2_font_ncenB24_tr);
{
int dw = u8g2_GetStrWidth(&u8g2, str);
u8g2_DrawStr(&u8g2, (128 - dw) / 2, 44, str);
}
/* 单位:底部 */
u8g2_SetFont(&u8g2, u8g2_font_ncenB14_tr);
{
int cw = u8g2_GetStrWidth(&u8g2, "cm");
u8g2_DrawStr(&u8g2, (128 - cw) / 2, 62, "cm");
}
} while (u8g2_NextPage(&u8g2));
delay_ms(200);
}
}五、程序烧录
1、连接USB数据线至开发板MUC-LINK端口;
2、右键当前工程项目;
3、点击 Flash the Selected Target 烧录程序到开发板上;

六、效果演示

我要赚赏金
