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

共2条 1/1 1 跳转至

【let'sdo|2026年第2期】简易超声波测距仪实践-2,过程贴

助工
2026-08-03 16:49:04     打赏

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

1,先在vs code中安装好 MCUXpresso for VS Code 插件

4.png

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

5.png

3,需要勾选安装以下组件:

  • MCUXpresso SDK Developer:包含CMake、Python等依赖。

  • LinkServer:用于板载调试器的软件包。

  • ARM GNU Toolchain:ARM的GCC编译器。

6.png

下载完成如图

7.png

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

11.png

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

12.png

13.png

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

14.png

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

        我的思路是:

16.png

        直接使用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,说是用于调节对比度的,需要接可变电阻来实现不同电压实现不同显示程度,我没有可变电阻,所以在程序写好之后一直不能显示,使用逻辑分析仪一直排查,最后发现接地能显示了。。。

image.png

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

image.png

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

        image.png

        显示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)
    {
    
    }
}

        显示结果:

微信图片_20260803164002_160_71.jpg





关键词: 超声波     LCD     PCF8574     过程贴    

专家
2026-08-04 06:49:43     打赏
2楼

谢谢分享


共2条 1/1 1 跳转至

回复

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