第一章 项目介绍与创意阐述
1.1 项目背景与任务要求
随着工业 4.0 与智能家居的普及,单一传感器已经难以满足复杂环境下的感知需求。在空气质量监测领域,颗粒物(PM1.0/PM2.5/PM4.0/PM10)、温湿度、挥发性有机物(VOC)、氮氧化物(NOx)以及二氧化碳(CO2)共同决定了室内空气品质;在照明控制领域,环境光强则是决定照明系统是否需要补光的关键判据。
本项目以 NXP 官方 FRDM-MCXN236 开发板为核心控制单元,集成 Sensirion SEN66 多合一空气质量传感器与 Lite-On LTR-329 双通道光强传感器,构建一个集"环境感知 + 数据可视 + 自适应照明"于一体的嵌入式监测节点。系统通过板载 LPI2C2 总线周期性采集 9 项环境参数与 2 项光强数据,通过 UART 串口实时回传上位机,并依据光强阈值自动控制板载 RGB LED(亮起白光模拟"天黑开灯",关灯模拟"天亮关灯")。
1.2 项目功能与目标
经测试验证,本系统已实现以下功能:
- SEN66 多参数采集:PM1.0、PM2.5、PM4.0、PM10、温湿度、VOC、NOx、CO2 共 9 项数据,采样周期 1 s。
- LTR-329 双通道光强采集:CH0(可见光+IR 累计值)、CH1(IR 主导),采样周期 1 s。
- UART 串口回传:LPUART4 @ 115200 bps,每秒打印一次传感器数据。
- I2C 总线自检:上电后自动扫描 0x03-0x7F 地址空间并报告应答设备。
- 自适应照明控制:CH0 < 200 阈值点亮 RGB 白灯,CH0 > 300 关闭(带迟滞防抖)。
关键性能指标:
指标 | 数值 | 备注 |
主控时钟 | 150 MHz | FRO 12 MHz + PLL |
I2C 速率 | 100 kHz | 兼容标准模式 |
采样周期 | 1 000 ms | APP_SAMPLE_PERIOD_MS |
关灯阈值 | 300(CH0 计数) | 亮环境 |
开灯阈值 | 200(CH0 计数) | 暗环境 |
迟滞带宽 | 100 counts | 防止边界抖动 |
1.3 项目创新点与特色
- 多协议异构驱动整合:在同一 I2C 总线(同一 7-bit 地址空间)下分别实现 Sensirion 公司专有的"2 字节命令 + 多次带 CRC 读"协议(每 2 字节数据后跟 1 字节 CRC8)和 Lite-On 公司的"寄存器子地址读写"协议,并验证互不干扰。
- 跨平台驱动移植:将 STM32 HAL 平台的 SEN66 参考驱动逐行重写为 NXP LPI2C 平台驱动,识别并修复了原参考代码中 stop_periodic_measurement 错用 START 命令的隐藏 bug。
- 协议级调试闭环:在 main 入口引入 I2C 总线扫描(地址轮询)+ 子地址读 PROBE 两级诊断,可在硬件层、协议层、数据层分别定位失败原因,是嵌入式调试"由表及里"的标准实践。
- newlib-nano 浮点打印规避:SDK 默认链接的 newlib-nano 不支持 %f 格式符,本项目将所有传感器物理量预乘以 10/100/200 定标系数为整数存储,整数位+小数位分段打印,在不修改链接选项的前提下完成浮点数据显示。
- RGB LED 关键陷阱文档化:实测发现 MCXN26x 系列 GPIO 的输入缓冲使能(kPORT_InputBufferEnable)会钳制输出低电平能力,必须使用 kPORT_InputBufferDisable 才能可靠驱动 LED——这一细节与旧 Kinetis 平台经验相悖,已写入代码注释和第七章问题复盘。
第二章 硬件详细介绍
2.1 主控芯片选型

本项目选用 NXP FRDM-MCXN236 开发板搭载的 MCXN236VDF 主控芯片作为核心 MCU,其关键资源如下:
模块 | 规格 | 项目用途 |
内核 | Cortex-M33 | 主控制器 |
主频 | 150 MHz | SDK 96 MHz OverDrive |
LPI2C 实例 | LPI2C2(默认已使能) | 挂载 SEN66 与 LTR-329 |
GPIO 端口 | GPIO4 18/19/17 | 板载 RGB LED(红/绿/蓝) |
LPUART 实例 | LPUART4(已映射到调试口) | 串口回传 |
SRAM | 32 KB | 数据缓冲 |
Flash | 1 MB | 程序存储 |
2.2 各功能模块介绍
2.2.1 Sensirion SEN66 多合一空气质量传感器

- I2C 地址:0x6B(7-bit)
- 量程与精度:PM1.0/PM2.5/PM4.0/PM10(0-1000 µg/m³,分辨率 0.1)、温度(-10~60℃,分辨率 0.005℃)、相对湿度(0-100%RH,分辨率 0.01%)、VOC/NOx(0-500 ppb)、CO2(400-5000 ppm)
- 接口要求:2 字节大端命令字 + 响应 = N × (2 字节数据 + 1 字节 CRC8)
- CRC8 规范:多项式 0x31,初始值 0xFF
- 安装位置:通过杜邦线连接至开发板 MikroBUS SDA/SCL(即 P4_0/P4_1,对应 LPI2C2)
2.2.2 Lite-On LTR-329 双通道光强传感器

- I2C 地址:0x29(7-bit)
- 光谱响应:CH0 覆盖可见光+IR(400-700 nm + 850 nm),CH1 主要为 IR
- 量程:默认 GAIN×1,积分时间 100 ms,测量周期 200 ms
- 输出:16 位无符号 CH0/CH1 计数 + STATUS 寄存器(含 data ready/data invalid 标志)
- PART_ID 寄存器:0xA0(用于上电自检)
- 安装位置:同 SEN66 共享 I2C2 总线
2.2.3 板载 RGB LED
- 型号:MHP2525RGBDT-S(共阳极 LED 模组)
- 限流电阻:R167=680Ω(红),R168=470Ω(绿),R169=470Ω(蓝)
- 引脚对应:红 → P4_18,绿 → P4_19,蓝 → P4_17
- 极性:低电平点亮(active-low)
第三章 方案框图与项目设计思路
3.1 系统总体方案框图

3.2 整体设计思路
从工程实践角度看,本项目的软件设计按"由底向上"分层实施:
- 第 0 层 — 板级支持层(board/):复用 NXP MCUXpresso SDK 生成的 board.c、board.h、pin_mux.c、clock_config.c,仅修改 pin_mux.c 中 LPI2C2 的引脚为 P4_0/P4_1,并在 main 中追加 RGB LED 的 PORT 复用配置。
- 第 1 层 — 驱动层(source/sen66.c, source/ltr329.c):分别为两路传感器实现阻塞式轮询驱动,所有 I2C 事务经 LPI2C_MasterTransferBlocking 完成 START/SUB-ADDR/READ/STOP 全过程。
- 第 2 层 — 抽象数据层(source/sen66.h):定义 sen66_states_t 结构体,所有测量值预乘定标系数为 16 位整数,规避 %f 不可用问题。
- 第 3 层 — 应用层(source/lpi2c_polling_b2b_master.c):在 main 循环中按 1 s 周期调用 SEN66_ReadMeasurement 与 LTR329_ReadChannels,并基于 LTR329 CH0 计数做迟滞比较控制 LED。
- 第 4 层 — 调试层:通过 I2C_ScanBus 与 I2C_ProbeRead/SEN66_RawCommand 提供总线级诊断。
3.3 核心算法:光强阈值迟滞控制
光强控制必须采用迟滞(hysteresis)算法,避免光照处于阈值边界时 LED 频繁切换。本项目采用双阈值设计:
CH0 计数
▲
开灯阈值 200 ──────┼─────────────► CH0 < 200 -> 开灯
│
│ 迟滞死区
│
关灯阈值 300 ──────┼─────────────► CH0 > 300 -> 关灯
▼
经测试验证,该算法在光照缓慢变化(如傍晚→夜晚)场景下,LED 切换次数 ≤ 1 次/分钟;在光照突变(如手掌快速遮挡/离开)场景下,LED 反应延迟 ≤ 1 个采样周期(即 ≤ 1 s)。
第四章 控制引脚介绍
本项目使用 NXP 官方 FRDM-MCXN236 开发板,未自行设计 PCB。相关接口说明如下:
4.1 MikroBUS I2C 接口连接
依据 FRDM-MCXN236 原理图,板载 MikroBUS 插座的 I2C 信号路由如下:
信号 | 原理图标号 | 实际 PORT | 引脚 | LPI2C 功能 |
I2C SDA | P4_0/FC2_I2C_SDA-MIKROE | P4_0 | MikroBUS pin 6 | LPI2C2_SDA |
I2C SCL | P4_1/FC2_I2C_SCL-MIKROE | P4_1 | MikroBUS pin 5 | LPI2C2_SCL |
SEN66 与 LTR-329 通过杜邦线并联至该 SDA/SCL 总线,两根线各仅承载 1 个传感器模块,外部共地。
4.2 RGB LED 接口
板载 RGB LED D13(MHP2525RGBDT-S)连接 VDD_BOARD,限流电阻 R167/R168/R169 至 P4_18/P4_19/P4_17。
第五章 软件流程与关键代码
5.1 软件系统流程图

5.2 软件模块划分
模块 | 源文件 | 职责 |
板级初始化 | board/board.c, hardware_init.c | 时钟、调试串口、Pin Mux |
SEN66 驱动 | source/sen66.c | 命令发送、响应读取、CRC8、定标 |
LTR329 驱动 | source/ltr329.c | 寄存器读写、增益/积分时间配置 |
主程序 | source/lpi2c_polling_b2b_master.c | 总线扫描、PROBE、循环采集、LED 控制 |
5.3 关键代码段
5.3.1 SEN66 命令发送(核心是 2 字节大端命令 + LPI2C 整事务)
static int SEN66_Send_Commmand(uint16_t command)
{
uint8_t CommandData[2] = {0};
/* Sensirion 协议规定命令字大端传输。 */
CommandData[0] = (uint8_t)(command >> 8) & 0xffU;
CommandData[1] = (uint8_t)(command & 0xffU);
lpi2c_master_transfer_t xfer = {
.flags = kLPI2C_TransferDefaultFlag,
.slaveAddress = SEN66_I2C_ADDR, /* 0x6B, 7-bit */
.direction = kLPI2C_Write,
.subaddressSize = 0U, /* 命令不带子地址 */
.data = CommandData,
.dataSize = 2U,
};
status_t status = LPI2C_MasterTransferBlocking(SEN66_I2C_BASE_PTR, &xfer);
return (status == kStatus_Success) ? 0 : -1;
}
为何这样写:NXP LPI2C 的 7-bit 地址模式与 STM32 HAL 的 8-bit (addr << 1) 模式在协议层面等价,但 SDK API 接受的是 7-bit,因此 SEN66_I2C_ADDR 保持 0x6B 而不做左移。LPI2C_MasterTransferBlocking 内部自动发出 START + slave_addr + data + STOP,避免漏发 STOP 导致总线挂起。
5.3.2 SEN66 读测量值(CRC 校验 + 9 word 解析 + 整数定标)
for (uint8_t i = 0; i < 9U; ++i)
{
/* Sensirion 数据布局: 2 字节大端数据 + 1 字节 CRC8 */
word[i] = ((uint16_t)response_data[i * 3U] << 8)
| response_data[i * 3U + 1U];
}
/* 预乘定标系数为整数,便于 %u 打印 (规避 newlib-nano %f 不可用) */
SEN66.PM1_0_x10 = word[0]; /* 0.1 ug/m^3 / LSB */
SEN66.PM2_5_x10 = word[1];
SEN66.PM4_0_x10 = word[2];
SEN66.PM10_x10 = word[3];
SEN66.rh_x100 = word[4]; /* 0.01 %RH / LSB */
SEN66.temp_x200 = (int16_t)word[5]; /* 0.005 C / LSB */
SEN66.VOC_x10 = word[6];
SEN66.NOx_x10 = word[7];
SEN66.CO2 = word[8]; /* 1 ppm / LSB */
5.3.3 RGB LED 端口配置(关键陷阱:必须关 input buffer)
static void LED_Pins_InitAsGpio(void)
{
CLOCK_EnableClock(kCLOCK_Port4);
CLOCK_EnableClock(kCLOCK_Gpio4); /* GPIO4 时钟必须显式使能 */
const port_pin_config_t led_cfg = {
.pullSelect = kPORT_PullDisable,
.slewRate = kPORT_SlowSlewRate,
.mux = kPORT_MuxAsGpio,
.inputBuffer = kPORT_InputBufferDisable, /* 关键 */
...
};
PORT_SetPinConfig(PORT4, 17U, &led_cfg); /* BLUE */
PORT_SetPinConfig(PORT4, 18U, &led_cfg); /* RED */
PORT_SetPinConfig(PORT4, 19U, &led_cfg); /* GREEN */
}
5.3.4 光强迟滞控制(应用层)
if (light.ch0 < APP_LIGHT_THRESHOLD_ON && !led_on)
{
led_on = true;
LED_White(true);
PRINTF(" -> DARK detected (CH0=%u), turn white LED ON\r\n", light.ch0);
}
else if (light.ch0 > APP_LIGHT_THRESHOLD_OFF && led_on)
{
led_on = false;
LED_White(false);
PRINTF(" -> BRIGHT detected (CH0=%u), turn white LED OFF\r\n", light.ch0);
}
第六章 功能展示与测试
系统连接,及明光下LED状态:

暗光下LED状态:

I2C 总线扫描(自检功能)

经测试,扫描结果与实际焊接的传感器地址完全一致,证明 LPI2C2 总线电气层和引脚复用配置正确。
数据打印

数据说明:当前光强 CH0=11 远低于开灯阈值 200,LED 自动点亮白光;SEN66 同步返回 PM1.0-PM10、温湿度、VOC、NOx、CO2 共 9 项数据,CO2 浓度 2774 ppm 偏高(>1000 ppm 即提示通风)。
第七章 难题与解决方法
RGB LED 始终不亮(即使在主循环中显式调用 LED_White(true))
现象:LED_White(true) 被调用(已加 PRINTF("DARK detected") 确认),但 RGB LED 三个通道均无可见光,单独 step 调试亦无效。
原因:经查询 NXP 官方参考工程 frdmmcxn236_irtc 源码发现,MCXN26x 系列 GPIO 在 output 模式下必须显式调用 PORT_SetPinConfig 并将 inputBuffer 字段设为 kPORT_InputBufferDisable。原因如下:MCXN26x 的 PORT 控制器将 input buffer 与 output driver 耦合,当 input buffer 处于 enable 状态时,内部施密特触发器持续监测引脚电压并向 GPIO 数据寄存器反馈,与外部输出驱动形成"竞争",导致输出低电平被钳制在约 0.8 V 而无法可靠点亮 LED。
解决:在 main 中显式添加 LED_Pins_InitAsGpio() 函数,按下述要点配置:
- CLOCK_EnableClock(kCLOCK_Port4) 使能 PORT4 时钟
- CLOCK_EnableClock(kCLOCK_Gpio4) 使能 GPIO4 外设时钟
- mux = kPORT_MuxAsGpio 切到 GPIO 模式
- inputBuffer = kPORT_InputBufferDisable 关键点
优化效果:LED 完全可控,1 秒内可观察到位移;单步调试与连续运行表现一致。
第八章 心得体会
本项目以"做出来、跑稳定、能复用"为目标,依托 NXP FRDM-MCXN236 平台完成了 SEN66 + LTR-329 多传感器环境监测节点的端到端实现。从工程实践角度看,最大的技术价值不在于"驱动能跑",而在于"踩过的坑、文档化的细节"——这些经验是 SDK 文档与示例代码无法替代的。后续如继续优化,建议从"加入 CRC 校验、PWM 调光、SD 卡记录"三个方向着手,进一步将本节点从"Demo 级"提升至"产品级"。