Funpack5-4 - 基于 FRDM-MCXN236 的 BME690 环境监测仪
该项目使用了BME690,实现了环境监测仪的设计,它的主要功能为:温湿度大气压力环境质量等参数。
标签
环境监测
BME690
Funpack5-4
永不止步
更新2026-10-19
2

一、项目介绍

本次项目实践主要利用 FRDM-MCXN236 开发板开展,核心目标是基于该开发板的 MCXN236 主控与板载 LPI2C2 总线,实现一颗 Bosch BME690 环境传感器的多参数采集与打印任务。在实际操作中,我们充分利用开发板所具备的 I2C 接口、调试串口、板载 RGB LED 与板载按键这几项关键硬件资源。通过编写相应的程序代码实现基本要求——周期性读取 BME690 的温度、湿度、压力、气体阻值四项物理量,并通过 UART 调试串口按 1 Hz 频率向主机稳定输出,同时通过板载 RGB LED 与一个按键完成"启动/停止打印"的交互。

这一任务的实现具有很重要的实践意义(活动的主题),通过具体的功能落地展示开发板所搭载的 MCU 的 I2C 主机驱动能力与多任务调度思维。通过这一次完整"传感器 → 主控 → 显示 + 上报"的最小闭环,开发者能够直观地看到数据通过 I2C 在传感器与开发板之间传输、再通过串口在开发板与主机之间传输的过程,从而深入理解嵌入式 I2C 通信的基本原理。同时,该任务对于开发者而言,是一个极为宝贵的实践机会。它为开发者搭建了一个从"读 datasheet"到"写 driver"到"在主循环里组织起来"的完整链路。在完成这一任务的过程中,开发者需要进行原型设计,构思如何利用开发板的资源实现目标(要想快速实现可以直接通过 Bosch 官方的 bme69x demo 进行)。通过这样的实践锻炼,开发者能够提升自己在嵌入式系统开发方面的技能和经验,了解 NXP FRDM 系列开发板的特定生态,为今后开展更复杂的物联网 / 传感器项目奠定坚实的基础。

二、硬件介绍
0

FRDM-MCXN236 是 NXP 提供的一款基于 MCXN236 的紧凑型开发板,具备灵活的扩展性和丰富的硬件接口,适合快速进行原型设计和开发。开发板集成了多个接口模块,使得硬件设计和外设连接更加简便。MCXN236 是一颗具备强大处理能力的 Cortex-M33 内核 MCU(96 MHz 主频,144 KB SRAM),支持多种通信协议,具备低功耗特点,适合各种嵌入式应用。开发板提供了丰富的外设接口,包括 LPUART、LPI2C、LPSPI 等,方便用户根据不同应用需求进行扩展。板载 MCU-Link 调试器,支持直接调试 MCU 程序,支持虚拟串口,咱们本次的串口打印就是用的调试器的虚拟串口和 PC 的串口调试助手进行通信的。
0

本次任务的外设只有一颗——Bosch BME690。BME690 是 BME680 的换代型号,单颗芯片集成了温度、相对湿度、大气压、气体(VOC)四路传感器,I2C / SPI 双接口,3.3 V 单电源供电,与本开发板逻辑电平完全兼容。模块尺寸小、连线少,非常适合评估板阶段的环境监测样机。它通过开发板 MikroBUS 兼容头 引脚(即板载 I2C2 总线)外挂,按 4 根杜邦线接好 3V3 / GND / SDA / SCL 即可工作。

三、开发板资源

我们回归到整个开发板的具体资源内容:
0

本次实现的功能主要依托两个板载模块:LPI2C2(挂 BME690)和 LPUART4(虚拟串口,往主机发数据)。所以我们有必要先了解一下板载的 MCU 与这两个外设的连接情况:
0

3.1 I2C 总线资源

板载 I2C2 连接到 MikroBUS 兼容头。这两个引脚在芯片内部对应 LPI2C2 外设的(FC2_P0 / FC2_P1),由 LP_FLEXCOMM2 复用控制。也就是说,使用 I2C2 必须先把 FLEXCOMM2 的功能切换到 LPI2C 模式,再单独给 LPI2C2 配时钟、解除复位,最后才能 LPI2C_MasterInit。

实际在调用 SDK 提供的 master polling demo 时,demo 自己的 BOARD_InitBootPins() 并没有完成 LPI2C2 的全部初始化——它只配了引脚复用、没给 FLEXCOMM2 接时钟、没解除 LPI2C2 复位、没切功能复用。我们本项目在 app_init_lpi2c2() 里把这四步手动补上,才让 I2C2 真正工作起来。

3.2 UART 总线资源

调试串口由板载 MCU-Link 的 VCOM 功能提供。LPUART4 挂在 LP_FLEXCOMM4 上,对应芯片引脚 PIO1_8 (A1, TX) / PIO1_9 (B1, RX)。BOARD_InitDebugConsole() 在 BOARD_InitHardware() 内部已经帮我们接好 12 MHz 时钟并把 FLEXCOMM4 切到 LPUART 模式,所以 printf 类的调试输出直接可用。

3.3 RGB LED 资源

板载 RGB LED 是三个独立 LED(红 P4_18 / 蓝 P4_17 / 绿 P4_19)共阳极接 VDD_BOARD,每一路经限流电阻到对应 GPIO 引脚,低电平点亮(active-low)。三色可以单独控制组合出 7 种有效颜色(红 / 绿 / 蓝 / 黄 / 青 / 品红 / 白 + 全灭)。本项目只用红和绿两色——红灯表示"待机、未启动",绿灯表示"采集中、正在打印"。

3.4 按键资源

板载 SW2 接在 P0_20 脚(PORT0 pin 20),按下拉低、未按上拉,板子已为该引脚配了外部上拉电阻。SW3 在本项目中不使用。本项目用一个 SW2 即可完成"启动/停止打印"的二态切换。

四、开发环境

MCUXpresso IDE 为开发人员带来一个易于使用的基于 Eclipse 的开发环境,适用于基于 Arm® Cortex®-M 内核的恩智浦® MCU,包括通用、跨界和无线 MCU。MCUXpresso IDE 提供高级编辑、编译和调试功能,增加了 MCU 专用的调试视图、代码跟踪和分析、多核调试和集成配置工具。咱们这一次的功能开发就是用官方提供的 MCUXpresso IDE基于VScode的插件,工程用 CMake + ninja 在 SDK 配套工具链下直接命令行构建。

软件层面依赖三块:

  • MCUXpresso SDK(driver.lpi2c + driver.lpflexcomm + driver.gpio + fsl_debug_console);
  • Bosch 上游 BME69x_SensorAPI(即 bme69x.c / bme69x.h / bme69x_defs.h,直接 vendored 进工程,不修改);
  • 自写 port_bme690.c 适配层——把 MCX N 的 LPI2C 驱动桥接到 Bosch 上游 API 的 read/write/delay_us 三个回调上。

五、开发过程

我们基于 frdmmcxn236_lpi2c(SDK 自带的 lpi2c polling b2b master demo)工程做底子,把 main.c 改成 BME690 应用。
0

这样可以省掉自己新建工程、配置时钟、配置调试串口的麻烦——这些 SDK 模板工程已经帮我们做完了。我们只需要把 BME690 驱动集成进来,再把 main 函数换成我们的状态机即可。

5.1 集成 BME690 驱动

  • bme69x.c / bme69x.h / bme69x_defs.h(Bosch 上游 API,不动)
  • port_bme690.c / port_bme690.h(MCX N 平台适配层)

然后在 CMakeLists.txt 的 mcux_add_source 块中追加这两个 .c 文件:


mcux_add_source(
SOURCES lpi2c_polling_b2b_master.c
bme69x.c
port_bme690.c
)

5.2 LPI2C2 初始化的"四个必做步骤"

BOARD_InitHardware() 调了 BOARD_InitBootPins() + BOARD_InitBootClocks() + BOARD_InitDebugConsole(),但没有为 LPI2C2 配时钟、释放复位、切功能复用。如果跳过下面这四步,后续每次 LPI2C_MasterTransferBlocking 都会返回 kStatus_LPI2C_PinLowTimeout (900),P1/P2 引脚上没有任何 I2C 波形:


static void app_init_lpi2c2(void)
{
lpi2c_master_config_t masterConfig;

/* 1. 给 FLEXCOMM2 接 12 MHz 时钟 */
CLOCK_AttachClk(kFRO12M_to_FLEXCOMM2);
CLOCK_SetClkDiv(kCLOCK_DivFlexcom2Clk, 1u);

/* 2. 释放 LPI2C2 复位,否则写不进寄存器 */
RESET_ClearPeripheralReset(kFC2_RST_SHIFT_RSTn);

/* 3. 把 FLEXCOMM2 切到 LPI2C 模式 */
LP_FLEXCOMM_Init(2U, LP_FLEXCOMM_PERIPH_LPI2C);

/* 4. 初始化主机,100 kHz */
LPI2C_MasterGetDefaultConfig(&masterConfig);
masterConfig.baudRate_Hz = 100000U;
LPI2C_MasterInit(EXAMPLE_I2C_MASTER, &masterConfig, LPI2C_MASTER_CLOCK_FREQUENCY);
}

EXAMPLE_I2C_MASTER 是在 master/app.h 里宏定义为 (LPI2C_Type *)EXAMPLE_I2C_MASTER_BASE,而 EXAMPLE_I2C_MASTER_BASE = LPI2C2_BASE。

5.3 port 层写 BME690 寄存器的"坑"

BME69x 的写接口在协议层有一个非常容易踩的细节:上游 bme69x_set_regs 假设目标寄存器的写是"1 个寄存器地址 + N 个数据字节"的单笔 I2C 事务。但 MCX N LPI2C 的"subaddress + data"路径会在地址字节和 payload 之间插一个 STOP,破坏 BME690 期望的"Single uninterrupted write"。

因此 port_bme690.c 的写桥必须把 [reg_addr | data...] 拼到栈上、关掉 subaddress 字段、走无子地址的写传输:


static BME69X_INTF_RET_TYPE bme690_i2c_write(uint8_t reg_addr,
const uint8_t *reg_data,
uint32_t len, void *intf_ptr)
{
lpi2c_master_transfer_t xfer;
uint8_t tmp[1U + 32U]; /* register + up to 32 payload bytes */
uint32_t total;

if ((intf_ptr == NULL) || ((reg_data == NULL) && (len != 0U))) return 1;
if (len > (sizeof(tmp) - 1U)) return 1; /* safety guard */

tmp[0] = reg_addr;
for (uint32_t i = 0U; i < len; i++) tmp[1U + i] = reg_data[i];
total = 1U + len;

xfer.flags = kLPI2C_TransferDefaultFlag;
xfer.slaveAddress = *(const uint8_t *)intf_ptr; /* = 0x76 */
xfer.direction = kLPI2C_Write;
xfer.subaddress = 0U; /* 关键:不开 subaddress 路径 */
xfer.subaddressSize = 0U;
xfer.data = tmp;
xfer.dataSize = total;

status_t status = LPI2C_MasterTransferBlocking(BME690_I2C_BASE, &xfer);
return (status == kStatus_Success) ? BME69X_INTF_RET_SUCCESS : 1;
}

5.4 主循环与状态机

我们将程序编译并下载到板子后,按下 SW2 即可看到主机串口助手开始按 1 Hz 频率收到形如下面这一行的字符串:


T=24.71 C | P=1013.18 hPa | H=58.2 % | Gas=121.823 kOhm | IAQ=84

我们在往回看一下,对于工程的基本逻辑就是"先做 I2C 适配层把 BME690 接上,再做按键 IRQ 把模式切起来,再做 LED 同步状态,最后主循环按 1 Hz 读 + 打印"。本次的 4 个核心模块就形成了完整链路:


int main(void)
{
BOARD_InitHardware();
app_init_lpi2c2(); /* 上面 5.2 的 4 步 */
SysTick_Config(SystemCoreClock / 1000U); /* 1ms tick 给 LED 闪烁用 */

bme_rslt = port_bme690_init();
/* init 失败时打印一次错误,不阻塞启动 */

app_led_init(); /* 配 PORT4 PCR + 默认全灭 */
app_button_init(); /* 配 PORT0 PCR + 下降沿 IRQ */

for (;;)
{
if (s_sw2Event) { s_sw2Event = 0U; app_handle_sw2(); ... } /* 消费按键 */

if (s_mode == kMode_Standby) { app_led_apply(0, 0, 0); delay; continue; }

/* RUN_PRINT: 1 Hz 读 + 打印 */
port_bme690_read(&sample);
if (rslt == BME69X_OK && sample.valid)
app_print_sample(&sample);
app_led_apply(...);
SDK_DelayAtLeastUs(1 * 1000 * 1000, SystemCoreClock);
}
}

5.5 RGB LED 与按键控制

按下 SW2 时,串口会立刻出现 K 字符(ISR 里的诊断输出),主循环消费事件后打印:


[Mode] SW2 -> RUN_PRINT
=================================================
FRDM-MCXN236 + BME690 environment monitor
=================================================
...
[Mode] SW2 -> STANDBY

再按一次 SW2 回到 STANDBY,红灯亮、停止打印。整套交互"红灯 = 待机 / 绿灯 = 运行"完全靠一个按键完成。

测试过程的串口打印:
0

六、心得体会

本次活动的任务相对来说比较贴近实际工程,我们这次实现的功能覆盖了嵌入式传感器节点的几个核心环节——总线驱动、平台适配、应用状态机、外设交互。通过本次的开发我们发现,如果基于 NXP 官方的开发板,程序开发过程其实非常便捷——对于官方的开发板,板级 SDK 已经把基本的引脚复用、时钟、调试串口都帮我们配好,直接调用就行,我们可以把精力集中在"应用逻辑"和"业务代码"上。

最后,本次开发让我对"传感器 + 主控 + 上报"的完整链路有了真实的体验,从"看 datasheet 写 driver"到"主循环里把 4 个模块拼起来"是嵌入式 IoT 项目最常见的工作流。希望这份记录对后续学习者有参考价值。

附件下载
frdmmcxn236_lpi2c.zip
团队介绍
无
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号