虽然平时用得最多的是Keil,但面对NXP这套生态,在Keil下如何开发不是很了解。还是直接选用官方推荐的MCUXpresso for VS Code。工欲善其事必先利其器,使用vs code开发,那就先安装好环境。个人建议使用虚拟机来开发。
1,先在vs code中安装好 MCUXpresso for VS Code 插件

2,打开该组件后,再打开 Open MCUXpresso Installer功能

3,需要勾选安装以下组件:
MCUXpresso SDK Developer:包含CMake、Python等依赖。
LinkServer:用于板载调试器的软件包。
ARM GNU Toolchain:ARM的GCC编译器。

下载完成如图

4,导入存储库,即FRDM-MCXA153的SDK与示例



5,导入成功了,环境已经搭建好,接下来编译和烧录简单示例 hello_world 验证环境是否正常。


烧录运行,能正常打印,表明环境正常

6,接下来实现超声波测距,该模块测距原理可以借鉴大佬的文章STM32之HC-SR04超声波测距传感器模块 - 技术栈
我的思路是:

直接使用hello_world示例进行修改,可以修改hello_world文件名,也需要改动相关配置文件,以防找不到更名后的文件。
例如: hello_world.c -> ultrasonic_ranging.c
CMakeLists.txt下的hello_world.c -> ultrasonic_ranging.c
frdmmcxa153hello_world -> frdmmcxa153ultrasonic_ranging
frdmmcxa153board_files.cmake文件中的 hello_world 全部改成 ultrasonic_ranging
frdmmcxa153hello_world.mex -> frdmmcxa153ultrasonic_ranging.mex
之后,执行Clean Project与 Pristine Build/Rebuild Project就能正常编译了
1),先实现定时计数功能,修改BOARD_InitHardware,增加定时器时钟
// prj.conf,增加定时功能 CONFIG_MCUX_COMPONENT_driver.ctimer=y
// frdmmcxa153ultrasonic_ranginghardware_init.c
void BOARD_InitHardware(void)
{
CLOCK_EnableClock(kCLOCK_GateGPIO3);
/* CTimer functional clock needs to be greater than or equal to SYSTEM_CLK */
CLOCK_SetClockDiv(kCLOCK_DivCTIMER1, 1u);
CLOCK_AttachClk(kFRO_HF_to_CTIMER1);
BOARD_InitPins();
BOARD_InitBootClocks();
BOARD_InitDebugConsole();
}// ultrasonic_ranging.c
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_ctimer.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define TIMER CTIMER1 // 使用定时器1
/*******************************************************************************
* Prototypes
******************************************************************************/
static void Timer_Start_Timing(void);
static void Timer_Stop_Timing(void);
static uint32_t Timer_GetTimestampUs(void);
static void Timer_DelayUs(uint32_t us);
static void Timer_DelayMs(uint32_t ms);
int main(void)
{
ctimer_config_t config;
/* Init hardware*/
BOARD_InitHardware();
PRINTF("MCUX SDK version: %srn", MCUXSDK_VERSION_FULL_STR);
CTIMER_GetDefaultConfig(&config);
uint32_t timer_clk = CLOCK_GetCTimerClkFreq(1U); // 96,000,000
PRINTF("CTIMER1 input frequency: %u Hzrn", timer_clk);
// 计算预分频值:96MHz / 96 = 1MHz
// 预分频值 = (输入频率 / 1,000,000) - 1
// 对于96MHz: 96,000,000 / 1,000,000 - 1 = 96 - 1 = 95
config.prescale = (timer_clk / 1000000UL) - 1;
PRINTF("Prescaler value: %urn", config.prescale);
PRINTF("Counting frequency: %u Hz (1 tick = 1 us)rn", timer_clk / (config.prescale + 1));
// 初始化定时器
CTIMER_Init(TIMER, &config);
Timer_Start_Timing();
PRINTF("time1: %urn", Timer_GetTimestampUs());
Timer_DelayMs(2000);
PRINTF("time2: %urn", Timer_GetTimestampUs());
Timer_Stop_Timing();
while (1)
{
}
}
// 开始计时
static void Timer_Start_Timing(void)
{
// 计数清零
TIMER->TC = 0;
// 开始计数
CTIMER_StartTimer(TIMER);
}
// 停止计时
static void Timer_Stop_Timing(void)
{
CTIMER_StopTimer(TIMER);
}
// 获取微秒时间戳(每计数1=1us)
static uint32_t Timer_GetTimestampUs(void)
{
return CTIMER_GetTimerCountValue(TIMER);
}
// 基于当前定时器的微秒延时
static void Timer_DelayUs(uint32_t us)
{
uint32_t start = CTIMER_GetTimerCountValue(TIMER);
while ((CTIMER_GetTimerCountValue(TIMER) - start) < us);
}
// 毫秒延时
static void Timer_DelayMs(uint32_t ms)
{
Timer_DelayUs(ms * 1000);
}输出的日志看时间上是差不多
14:34:46:146 -> MCUX SDK version: 2026.06.00
14:34:46:167 -> CTIMER1 input frequency: 96000000 Hz
14:34:46:167 -> Prescaler value: 95
14:34:46:167 -> Counting frequency: 1000000 Hz (1 tick = 1 us)
14:34:46:167 -> time1: 0
14:34:48:148 -> time2: 2000955
2),实现超声波测距
frdmmcxa153ultrasonic_rangingpin_mux.h
#ifndef _PIN_MUX_H_
#define _PIN_MUX_H_
#if defined(__cplusplus)
extern "C" {
#endif
// Trig 引脚 P1_12
/* Symbols to be used with GPIO driver */
#define BOARD_INITPINS_TRIG_GPIO GPIO1 /*!<@brief GPIO peripheral base pointer */
#define BOARD_INITPINS_TRIG_GPIO_PIN 12U /*!<@brief GPIO pin number */
#define BOARD_INITPINS_TRIG_GPIO_PIN_MASK (1U << 12U) /*!<@brief GPIO pin mask */
/* Symbols to be used with PORT driver */
#define BOARD_INITPINS_TRIG_PORT PORT1 /*!<@brief GPIO peripheral base pointer */
#define BOARD_INITPINS_TRIG_PORT_PIN 12U /*!<@brief GPIO pin number */
#define BOARD_INITPINS_TRIG_PORT_PIN_MASK (1U << 12U) /*!<@brief GPIO pin mask */
// Echo 引脚 P1_10
/* Symbols to be used with GPIO driver */
#define BOARD_INITPINS_ECHO_GPIO GPIO1 /*!<@brief GPIO peripheral base pointer */
#define BOARD_INITPINS_ECHO_GPIO_PIN 10U /*!<@brief GPIO pin number */
#define BOARD_INITPINS_ECHO_GPIO_PIN_MASK (1U << 10U) /*!<@brief GPIO pin mask */
/* Symbols to be used with PORT driver */
#define BOARD_INITPINS_ECHO_PORT PORT1 /*!<@brief GPIO peripheral base pointer */
#define BOARD_INITPINS_ECHO_PORT_PIN 10U /*!<@brief GPIO pin number */
#define BOARD_INITPINS_ECHO_PORT_PIN_MASK (1U << 10U) /*!<@brief GPIO pin mask */
void BOARD_InitBootPins(void);
void BOARD_InitPins(void);
#if defined(__cplusplus)
}
#endif
#endif /* _PIN_MUX_H_ */frdmmcxa153ultrasonic_rangingpin_mux.c,值得注意的是ECHO是需要Port设置的,不然读取不到电平变化,也就没法触发中断,尽管使用逻辑分析仪看是有变化的。TRIG可以不用port设置。
#include "fsl_common.h"
#include "fsl_port.h"
#include "pin_mux.h"
#include "fsl_gpio.h"
void BOARD_InitBootPins(void)
{
BOARD_InitPins();
}
void BOARD_InitPins(void)
{
/* Write to GPIO1: Peripheral clock is enabled */
CLOCK_EnableClock(kCLOCK_GateGPIO1);// ECHO TRIG
/* Write to PORT0: Peripheral clock is enabled */
CLOCK_EnableClock(kCLOCK_GatePORT0);// UART
/* Write to PORT1: Peripheral clock is enabled */
CLOCK_EnableClock(kCLOCK_GatePORT1);// ECHO TRIG
/* GPIO1 peripheral is released from reset */
RESET_ReleasePeripheralReset(kGPIO1_RST_SHIFT_RSTn);
/* LPUART0 peripheral is released from reset */
RESET_ReleasePeripheralReset(kLPUART0_RST_SHIFT_RSTn);
/* PORT0 peripheral is released from reset */
RESET_ReleasePeripheralReset(kPORT0_RST_SHIFT_RSTn);
/* PORT1 peripheral is released from reset */
RESET_ReleasePeripheralReset(kPORT1_RST_SHIFT_RSTn);
const port_pin_config_t port0_2_pin51_config = {/* Internal pull-up resistor is enabled */
kPORT_PullUp,
/* Low internal pull resistor value is selected. */
kPORT_LowPullResistor,
/* Fast slew rate is configured */
kPORT_FastSlewRate,
/* Passive input filter is disabled */
kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
kPORT_OpenDrainDisable,
/* Low drive strength is configured */
kPORT_LowDriveStrength,
/* Normal drive strength is configured */
kPORT_NormalDriveStrength,
/* Pin is configured as LPUART0_RXD */
kPORT_MuxAlt2,
/* Digital input enabled */
kPORT_InputBufferEnable,
/* Digital input is not inverted */
kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
kPORT_UnlockRegister};
/* PORT0_2 (pin 51) is configured as LPUART0_RXD */
PORT_SetPinConfig(PORT0, 2U, &port0_2_pin51_config);
// debug串口配置
const port_pin_config_t port0_3_pin52_config = {/* Internal pull-up resistor is enabled */
kPORT_PullUp,
/* Low internal pull resistor value is selected. */
kPORT_LowPullResistor,
/* Fast slew rate is configured */
kPORT_FastSlewRate,
/* Passive input filter is disabled */
kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
kPORT_OpenDrainDisable,
/* Low drive strength is configured */
kPORT_LowDriveStrength,
/* Normal drive strength is configured */
kPORT_NormalDriveStrength,
/* Pin is configured as LPUART0_TXD */
kPORT_MuxAlt2,
/* Digital input enabled */
kPORT_InputBufferEnable,
/* Digital input is not inverted */
kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
kPORT_UnlockRegister};
/* PORT0_3 (pin 52) is configured as LPUART0_TXD */
PORT_SetPinConfig(PORT0, 3U, &port0_3_pin52_config);
// 配置Trig与Echo 引脚模式
gpio_pin_config_t Echo_config = {
kGPIO_DigitalInput,
0,
};
gpio_pin_config_t Trig_config = {
kGPIO_DigitalOutput,
0,
};
GPIO_PinInit(BOARD_INITPINS_ECHO_GPIO, BOARD_INITPINS_ECHO_GPIO_PIN, &Echo_config);
GPIO_PinInit(BOARD_INITPINS_TRIG_GPIO, BOARD_INITPINS_TRIG_GPIO_PIN, &Trig_config);
const port_pin_config_t PORT_ECHO = {/* Internal pull-up/down resistor is disabled */
kPORT_PullDisable,
/* Low internal pull resistor value is selected. */
kPORT_LowPullResistor,
/* Fast slew rate is configured */
kPORT_FastSlewRate,
/* Passive input filter is disabled */
kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
kPORT_OpenDrainDisable,
/* Low drive strength is configured */
kPORT_LowDriveStrength,
/* Normal drive strength is configured */
kPORT_NormalDriveStrength,
/* Pin is configured as P1_10 */
kPORT_MuxAlt0,
/* Digital input enabled */
kPORT_InputBufferEnable,
/* Digital input is not inverted */
kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
kPORT_UnlockRegister};
/* PORT3_12 (pin 38) is configured as P3_12 */
PORT_SetPinConfig(BOARD_INITPINS_ECHO_PORT, BOARD_INITPINS_ECHO_PORT_PIN, &PORT_ECHO);
// Trig 可以不需要port
PORT_SetPinConfig(BOARD_INITPINS_TRIG_PORT, BOARD_INITPINS_TRIG_PORT_PIN, &PORT_ECHO);
}frdmmcxa153ultrasonic_rangingapp.h
#ifndef _APP_H_ #define _APP_H_ #define BOARD_ECHO_IRQ GPIO1_IRQn #define BOARD_ECHO_IRQ_HANDLER GPIO1_IRQHandler void BOARD_InitHardware(void); #endif /* _APP_H_ */
ultrasonic_ranging.c
/*******************************************************************************
* Include
******************************************************************************/
#include "fsl_gpio.h"
#include "fsl_device_registers.h"
#include "fsl_debug_console.h"
#include "board.h"
#include "app.h"
#include "fsl_ctimer.h"
#include "pin_mux.h"
/*******************************************************************************
* Definitions
******************************************************************************/
#define TIMER CTIMER1 // 使用定时器1
/*******************************************************************************
* Prototypes
******************************************************************************/
static void Timer_init(void);
static void Timer_Start_Timing(void);
// static void Timer_Stop_Timing(void);
static uint32_t Timer_GetTimestampUs(void);
static void Timer_DelayUs(uint32_t us);
static void Timer_DelayMs(uint32_t ms);
uint32_t Ultrasonic_Ranging_GetUm(void);
/*******************************************************************************
* Variables
******************************************************************************/
volatile uint32_t echo_start_time = 0;
volatile uint32_t echo_pulse_width = 0;
volatile uint8_t echo_ready = 0;
/*******************************************************************************
* Code
******************************************************************************/
/*!
* @brief Main function
*/
int main(void)
{
/* Init hardware*/
BOARD_InitHardware();
Timer_init();
PRINTF("MCUX SDK version: %srn", MCUXSDK_VERSION_FULL_STR);
while (1)
{
uint32_t dist = Ultrasonic_Ranging_GetUm();
if (dist > 0) {
PRINTF("distance: %u umrn", dist);
} else {
PRINTF("measurement timeout!rn");
}
Timer_DelayMs(500);
}
}
/*****************************超声波部分***************************************/
// 定时器初始化
static void Timer_init(void)
{
ctimer_config_t config;
CTIMER_GetDefaultConfig(&config);
uint32_t timer_clk = CLOCK_GetCTimerClkFreq(1U); // 96,000,000
PRINTF("CTIMER1 input frequency: %u Hzrn", timer_clk);
// 计算预分频值:96MHz / 96 = 1MHz
config.prescale = (timer_clk / 1000000UL) - 1;
PRINTF("Prescaler value: %urn", config.prescale);
PRINTF("Counting frequency: %u Hz (1 tick = 1 us)rn", timer_clk / (config.prescale + 1));
// 配置中断前先清除标志
GPIO_GpioClearInterruptFlags(BOARD_INITPINS_ECHO_GPIO, 1U << BOARD_INITPINS_ECHO_GPIO_PIN);
// 设置 echo 中断为双边沿触发
GPIO_SetPinInterruptConfig(BOARD_INITPINS_ECHO_GPIO, BOARD_INITPINS_ECHO_GPIO_PIN,
kGPIO_InterruptEitherEdge);
EnableIRQ(BOARD_ECHO_IRQ);
// 初始化定时器
CTIMER_Init(TIMER, &config);
Timer_Start_Timing();
}
// 开始计时
static void Timer_Start_Timing(void)
{
TIMER->TC = 0;
CTIMER_StartTimer(TIMER);
}
// // 停止计时
// static void Timer_Stop_Timing(void)
// {
// CTIMER_StopTimer(TIMER);
// }
// 获取微秒时间戳(每计数1=1us)
static uint32_t Timer_GetTimestampUs(void)
{
return CTIMER_GetTimerCountValue(TIMER);
}
// 基于当前定时器的微秒延时
static void Timer_DelayUs(uint32_t us)
{
uint32_t start = CTIMER_GetTimerCountValue(TIMER);
while ((CTIMER_GetTimerCountValue(TIMER) - start) < us);
}
// 毫秒延时
static void Timer_DelayMs(uint32_t ms)
{
Timer_DelayUs(ms * 1000);
}
// 获取超声测距数据,因无法打印浮点数,直接输出微米
uint32_t Ultrasonic_Ranging_GetUm(void)
{
// 清空上次测量数据
echo_ready = 0;
echo_pulse_width = 0;
// 触发超声波模块
GPIO_PortSet(BOARD_INITPINS_TRIG_GPIO, BOARD_INITPINS_TRIG_GPIO_PIN_MASK);
Timer_DelayUs(15);
GPIO_PortClear(BOARD_INITPINS_TRIG_GPIO, BOARD_INITPINS_TRIG_GPIO_PIN_MASK);
// 等待测量完成,超时时间 50ms(最大测距约 8.5米)
uint32_t start_wait = Timer_GetTimestampUs();
while (!echo_ready) {
if ((Timer_GetTimestampUs() - start_wait) > 50000) {
return 0.0f; // 超时
}
}
// 计算距离:脉冲宽度(us) * 声速(cm/us) / 2
// 343.4 m/s = 0.03434 cm/us, 声速公式:v=331.4+0.6T(T为环境温度,单位°C)。基于20°C
// 距离 = 脉冲宽度 * 0.03434 / 2 = 脉冲宽度 * 0.01717 (cm)
// return distance;
uint32_t distance_um = (uint32_t)(echo_pulse_width * 1717 / 10);// 输出微米
return distance_um;
}
// Echo引脚中断处理
void BOARD_ECHO_IRQ_HANDLER(void)
{
/* Clear external interrupt flag. */
GPIO_GpioClearInterruptFlags(BOARD_INITPINS_ECHO_GPIO, 1U << BOARD_INITPINS_ECHO_GPIO_PIN);
// 读取当前引脚电平判断边沿
if (GPIO_PinRead(BOARD_INITPINS_ECHO_GPIO, BOARD_INITPINS_ECHO_GPIO_PIN) == 1U)
{
// 上升沿:开始计时
echo_start_time = Timer_GetTimestampUs();
}
else
{
// 下降沿:计算脉冲宽度
uint32_t end_time = Timer_GetTimestampUs();
if (end_time > echo_start_time) {
echo_pulse_width = end_time - echo_start_time;
echo_ready = 1;
}
}
SDK_ISR_EXIT_BARRIER;
} 结果如下,距离大致准确,更加精确得考虑温度方面的影响,官方说10-250cm是比较准确。
distance: 515100 um
distance: 252399 um
distance: 135986 um
distance: 91859 um
distance: 307514 um
distance: 1790487 um
distance: 1782246 um
distance: 1821908 um
3),实现LCD屏显示,使用PCF8574模块来转接LCD屏,然后使用I2C连接 FRDM-MCXA153,开始我是直接使用16个IO直连LCD的,发现接地全黑,悬空与接3V啥也没有,看到有其他大佬使用PCF8574来转接,我也购买一个来试试。连接示意图如下,值得注意的是,对于LCD上V0的这个IO,说是用于调节对比度的,需要接可变电阻来实现不同电压实现不同显示程度,我没有可变电阻,所以在程序写好之后一直不能显示,使用逻辑分析仪一直排查,最后发现接地能显示了。。。

使用I2C来连接,使用J2扩展上的P1_8-SDA, P2_9-SCL来连接

查看数据手册,需要复用功能实现I2C,如果是软件模拟那倒是使用一般IO功能即可。

显示LCD部分代码:
frdmmcxa153ultrasonic_ranginghardware_init.c
// 在BOARD_InitHardware 中增加 I2C0的时钟 /* Attach peripheral clock */ CLOCK_SetClockDiv(kCLOCK_DivLPI2C0, 1u); CLOCK_AttachClk(kFRO12M_to_LPI2C0);
frdmmcxa153ultrasonic_rangingpin_mux.c
// 在 BOARD_InitPins 中增加 I2C0 引脚配置,.mux = kPORT_MuxAlt3 - 复用功能
// I2C配置
const port_pin_config_t port1_9_SCL_config = {/* Internal pull-up resistor is enabled */
.pullSelect = kPORT_PullUp,
/* Low internal pull resistor value is selected. */
.pullValueSelect = kPORT_LowPullResistor,
/* Fast slew rate is configured */
.slewRate = kPORT_FastSlewRate,
/* Passive input filter is disabled */
.passiveFilterEnable = kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
.openDrainEnable = kPORT_OpenDrainDisable,
/* Low drive strength is configured */
.driveStrength = kPORT_LowDriveStrength,
/* Normal drive strength is configured */
.driveStrength1 = kPORT_NormalDriveStrength,
/* Pin is configured as LPI2C0_SCL */
.mux = kPORT_MuxAlt3,
/* Digital input enabled */
.inputBuffer = kPORT_InputBufferEnable,
/* Digital input is not inverted */
.invertInput = kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
.lockRegister = kPORT_UnlockRegister};
/* PORT1_9 is configured as LPI2C0_SCL */
PORT_SetPinConfig(PORT1, 9U, &port1_9_SCL_config);
const port_pin_config_t port1_8_SDA_config = {/* Internal pull-up resistor is enabled */
.pullSelect = kPORT_PullUp,
/* Low internal pull resistor value is selected. */
.pullValueSelect = kPORT_LowPullResistor,
/* Fast slew rate is configured */
.slewRate = kPORT_FastSlewRate,
/* Passive input filter is disabled */
.passiveFilterEnable = kPORT_PassiveFilterDisable,
/* Open drain output is disabled */
.openDrainEnable = kPORT_OpenDrainDisable,
/* Low drive strength is configured */
.driveStrength = kPORT_LowDriveStrength,
/* Normal drive strength is configured */
.driveStrength1 = kPORT_NormalDriveStrength,
/* Pin is configured as LPI2C0_SDA */
.mux = kPORT_MuxAlt3,
/* Digital input enabled */
.inputBuffer = kPORT_InputBufferEnable,
/* Digital input is not inverted */
.invertInput = kPORT_InputNormal,
/* Pin Control Register fields [15:0] are not locked */
.lockRegister = kPORT_UnlockRegister};
/* PORT1_8 is configured as LPI2C0_SDA */
PORT_SetPinConfig(PORT1, 8U, &port1_8_SDA_config);frdmmcxa153ultrasonic_rangingapp.h
#define EXAMPLE_I2C_MASTER LPI2C0 #define LPI2C_BAUDRATE 100000U #define LPI2C_MASTER_CLOCK_FREQUENCY CLOCK_GetLpi2cClkFreq() #define PCF8574T_I2C_ADDRESS 0x27U // PCF8574T地址 #define LCD_COLS 16 // 每行最大列数 /* 定义连接到PCF8574T的LCD引脚位掩码 */ #define LCD_PIN_RS (1 << 0) // P0 连接到 LCD RS #define LCD_PIN_RW (1 << 1) // P1 连接到 LCD R/W (建议直接接地) #define LCD_PIN_EN (1 << 2) // P2 连接到 LCD E #define LCD_PIN_BL (1 << 3) // P3 连接到 LCD 背光 (可选) #define LCD_PIN_D4 (1 << 4) // P4 连接到 LCD DB4 #define LCD_PIN_D5 (1 << 5) // P5 连接到 LCD DB5 #define LCD_PIN_D6 (1 << 6) // P6 连接到 LCD DB6 #define LCD_PIN_D7 (1 << 7) // P7 连接到 LCD DB7
ultrasonic_ranging.c
/*****************************LCD部分***************************************/
/* 向PCF8574写入一个字节的数据 */
static status_t PCF8574_WriteByte(uint8_t data)
{
status_t status;
status = LPI2C_MasterStart(EXAMPLE_I2C_MASTER, PCF8574T_I2C_ADDRESS, kLPI2C_Write);
if (status != kStatus_Success) {
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return status;
}
status = LPI2C_MasterSend(EXAMPLE_I2C_MASTER, &data, 1);
if (status != kStatus_Success) {
LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return status;
}
status = LPI2C_MasterStop(EXAMPLE_I2C_MASTER);
return status;
}
/* 发送一个4位半字节到LCD */
static void LCD_SendNibble(uint8_t nibble, uint8_t is_command)
{
uint8_t data = 0x00;
// 1. 构造数据字节
if (nibble & 0x01) data |= LCD_PIN_D4;
if (nibble & 0x02) data |= LCD_PIN_D5;
if (nibble & 0x04) data |= LCD_PIN_D6;
if (nibble & 0x08) data |= LCD_PIN_D7;
if (!is_command) {
data |= LCD_PIN_RS;
}
// RW强制为0 (写模式),如果硬件R/W直接接地,这行可以去掉
data &= ~LCD_PIN_RW;
// 背光开启
data |= LCD_PIN_BL;
// 2. 产生使能脉冲: E pin 高 -> 低
// 第一步: 数据保持稳定,E=1
PCF8574_WriteByte(data | LCD_PIN_EN);
Timer_DelayUs(5); // 使用CTIMER延时,至少5us
// 第二步: E=0,锁存数据
PCF8574_WriteByte(data & ~LCD_PIN_EN);
Timer_DelayUs(2); // 保持时间
}
/* 向LCD发送一个完整字节 (4位模式) - 使用CTIMER延时 */
static void LCD_WriteByte(uint8_t byte, uint8_t is_command)
{
// 先发送高4位
LCD_SendNibble((byte >> 4) & 0x0F, is_command);
// 再发送低4位
LCD_SendNibble(byte & 0x0F, is_command);
/* 根据命令类型,增加必要的延时 */
if (is_command) {
if (byte == 0x01 || byte == 0x02) { // 清屏或归位
Timer_DelayMs(2); // 使用CTIMER毫秒延时
} else {
Timer_DelayUs(100);
}
} else {
Timer_DelayUs(50);
}
}
/* I2C初始化 */
static void LCD_I2C_init(void)
{
lpi2c_master_config_t masterConfig;
LPI2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Hz = LPI2C_BAUDRATE;
LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY);
PRINTF("I2C initialized at %d Hzrn", LPI2C_BAUDRATE);
}
/* 标准4-bit LCD初始化 */
static void LCD_Device_Init(void)
{
PRINTF("LCD Init Start...rn");
// ===== Step 1: 上电等待 =====
Timer_DelayMs(50);
PRINTF(" Step 1: Power-on wait donern");
// ===== Step 2: 发送3次0x30唤醒 =====
for (int i = 0; i < 3; i++) {
LCD_SendNibble(0x03, 1); // 发送 0x3 (高4位)
Timer_DelayMs(5);
PRINTF(" Step 2.%d: Wakeup (0x3) donern", i+1);
}
// ===== Step 3: 设置为4-bit模式 =====
LCD_SendNibble(0x02, 1); // 发送 0x2 (高4位)
Timer_DelayUs(100);
PRINTF(" Step 3: 4-bit mode setrn");
// ===== 从此开始使用完整的8位命令 =====
// ===== Step 4: Function Set =====
LCD_WriteByte(0x28, 1); // 4-bit, 2行, 5x8
PRINTF(" Step 4: Function Set (0x28)rn");
// ===== Step 5: Display OFF =====
LCD_WriteByte(0x08, 1); // 先关闭显示
PRINTF(" Step 5: Display OFFrn");
// ===== Step 6: Clear Display =====
LCD_WriteByte(0x01, 1); // 清屏
Timer_DelayMs(2); // 等待清屏完成
PRINTF(" Step 6: Clear Displayrn");
// ===== Step 7: Entry Mode Set =====
LCD_WriteByte(0x06, 1); // 地址递增
PRINTF(" Step 7: Entry Mode Setrn");
// ===== Step 8: Display ON =====
LCD_WriteByte(0x0C, 1); // 显示开,光标关
PRINTF(" Step 8: Display ONrn");
PRINTF("LCD Init Complete!rn");
}
/* 设置光标位置 */
static void LCD_SetCursor(uint8_t row, uint8_t col)
{
uint8_t address;
if (row == 0) {
address = 0x00 + col;
} else {
address = 0x40 + col;
}
LCD_WriteByte(0x80 | address, 1);
}
/* 打印字符串,支持自动换行 (最多显示16字节,超过8字节自动换到第二行) */
static void LCD_PrintString(char *str)
{
uint8_t len = 0;
uint8_t col = 0;
// 计算字符串长度
while (str[len] != 0) {
len++;
}
// 如果长度 > 16,只显示前16个字符
if (len > LCD_COLS) {
len = LCD_COLS;
}
// 如果长度 > 8,从第二行开始显示
if (len > 8) {
// 先显示第一行 (前8个字符)
LCD_SetCursor(0, 0);
for (col = 0; col < 8; col++) {
LCD_WriteByte(str[col], 0);
}
// 再显示第二行 (剩余字符)
LCD_SetCursor(1, 0);
for (col = 8; col < len; col++) {
LCD_WriteByte(str[col], 0);
}
} else {
// 长度 <= 8,直接显示在第一行
LCD_SetCursor(0, 0);
for (col = 0; col < len; col++) {
LCD_WriteByte(str[col], 0);
}
}
}
int main(void)
{
/* Init hardware*/
BOARD_InitHardware();
Timer_init();
PRINTF("MCUX SDK version: %srn", MCUXSDK_VERSION_FULL_STR);
LCD_I2C_init();
// 2. 初始化LCD (通过PCF8574T)
LCD_Device_Init();
// 3. 显示信息
LCD_PrintString("Hello, EEPW!");
while (1)
{
}
} 显示结果:

我要赚赏金
