Funpack5-4 - 基于 FRDM-MCXN236 的空气质量监测仪
该项目使用了FRDM-MCXN236,实现了空气质量监测的设计,它的主要功能为:1.8寸LCD屏幕驱动、SEN66传感器读取。
标签
Funpack活动
开发板
NXP
SEN66
FuShenxiao
更新2026-10-19
3

项目简介

这次使用 NXP FRDM-MCXN236 开发板、Sensirion SEN66 环境传感器和一块 1.8 英寸 ST7735 LCD,制作了一台桌面空气质量监测仪。

设备可以同时读取并显示:

  • PM1.0、PM2.5、PM4.0、PM10
  • 相对湿度和温度
  • VOC 指数和 NOx 指数
  • CO2 浓度

LCD 包含两个界面:

  1. 总览界面:显示运行计时和九项实时测量值;
  2. 曲线界面:显示最近 106 个有效采样点的温度、湿度变化。

按下开发板上的 SW2 可以在两个界面之间切换。全部测量值还会通过板载虚拟串口输出,便于记录和后续分析。

硬件组成与连线

NXP FRDM-MCXN236开发板

FRDM-MCXN236是恩智浦(NXP)推出的一款紧凑型、可扩展的高性价比开发板。该开发板的核心是一颗 MCX N236 微控制器,搭载 Arm Cortex-M33 内核,主频最高可达 150 MHz,能够高效地将工作负载分配到模拟与数字外设上。其接口分配如下。

image.png

Sensirion SEN66 环境传感器

这是 Sensirion 的一款环境传感器,可以检测 PH 值、温湿度、氮氧化合物、CO2 等信息

image.png

其控制方式采用的 IIC,接口引脚定义如数据手册中所示

image.png

因此其与开发板的连接方式如下表所示

SEN66 端子

FRDM-MCXN236

MCXN236 功能

说明

VDD

3V3

—

—

GND

GND

—

必须与开发板和 LCD 共地

SDA

P1_16

LPI2C5 / FC5_P0

I2C 数据线

SCL

P1_17

LPI2C5 / FC5_P1

I2C 时钟线

1.8 寸 LCD 屏幕

该 1.8寸 LCD 屏幕使用的驱动芯片为 ST7735S,采用的通信方式为 SPI,由于 ST7735S 只接收显示数据,因此本项目不使用 MISO。LCD 驱动采用 RGB565 色彩格式,设置 LPSPI3 时钟为 12 MHz。LCD 屏幕与开发板连接方式如下表所示。

ST7735 端子

FRDM-MCXN236

软件定义

作用

VCC

3V3

—

LCD 逻辑与显示供电

GND

GND

—

公共地

SCL

P1_0

LPSPI3 时钟

SPI 时钟

SDA

P1_1

LPSPI3 数据输出

MCU 向 LCD 发送数据

CS

P1_3

GPIO1_3

片选,低电平有效

RST

P4_13

GPIO4_13

LCD 硬件复位

DC

P5_7

GPIO5_7

命令/数据选择

BLK

背光控制端

—

背光,默认连接3V3,因此不接引脚

串口与按键

串口用于记录当前获取到的环境参数,串口参数设置为 115200 8N1,电脑连接开发板 USB 口之后,可以通过虚拟串口查看数据;按键用于实现页面切换

功能

引脚/通道

说明

调试串口

LPUART4,P1_8/P1_9

FRDM 板载调试器已连接到 USB 虚拟串口,通常无需再接 USB-UART

页面切换

SW2,P0_20

板载按键,内部上拉,按下时为低电平

最终硬件连接如下图所示

IMG_20261011_212941.jpg

系统结构

硬件连接与数据流

连接方式和数据传递如下图所示

image.png

软件启动流程

image.png

主循环与页面切换

image.png

SEN66 通信原理

I2C 地址与命令

SEN66 的 7 位 I2C 地址为 0x6B,本项目使用 100kbit/s 标准模式。

程序使用的主要命令如下:

命令

数值

作用

Start Measurement

0x0021

启动连续测量

Data Ready

0x0202

查询新数据是否就绪

Read Measurements

0x0300

读取全部测量结果

发送命令时先发送高字节,再发送低字节:

static status_t SEN66_WriteCommand(uint16_t command)
{
uint8_t commandBytes[2] = {
(uint8_t)(command >> 8U),
(uint8_t)command
};

return SEN66_Transfer(kLPI2C_Write,
commandBytes,
sizeof(commandBytes));
}

返回数据与 CRC-8

一次完整测量返回 27 字节,对应 9 个测量量。SEN66 每两个数据字节附带一个 CRC:

MSB | LSB | CRC | MSB | LSB | CRC | ...

CRC-8 的初始值为 0xFF,多项式为 0x31。驱动逐个检查 3 字节数据组,任何一组校验失败都会放弃本次数据:

static uint8_t SEN66_CalculateCrc(const uint8_t *data)
{
uint8_t crc = 0xFFU;

for (uint8_t byte = 0U; byte < 2U; byte++)
{
crc ^= data[byte];
for (uint8_t bit = 0U; bit < 8U; bit++)
{
crc = (crc & 0x80U) != 0U ?
(uint8_t)((crc << 1U) ^ 0x31U) :
(uint8_t)(crc << 1U);
}
}
return crc;
}

数据解析与单位换算

驱动把每个大端序数据字还原为 16 位整数:

static uint16_t SEN66_GetWord(const uint8_t *data,
uint32_t wordIndex)
{
uint32_t offset = wordIndex * 3U;
return (uint16_t)(((uint16_t)data[offset] << 8U) |
data[offset + 1U]);
}

原始值的换算关系为:

数据

换算方法

显示精度

PM1.0/2.5/4.0/10

原始值 ÷ 10

1 位小数,µg/m³

湿度

原始值 ÷ 100

2 位小数,%RH

温度

原始值 ÷ 200

2 位小数,°C

VOC/NOx 指数

原始值 ÷ 10

1 位小数

CO2

不缩放

ppm

代码没有使用浮点格式化,而是通过整数除法和余数生成小数文本。这样可以减小嵌入式程序体积,也避免引入浮点 printf。

传感器用 0xFFFF 或 0x7FFF 表示未知值,界面与串口统一输出 ---,避免把无效值误认为真实测量结果。

I2C 总线恢复与故障诊断

I2C 从设备如果在 MCU 复位期间仍处于发送状态,可能持续拉低 SDA,导致控制器一直认为总线忙。

初始化 LPI2C5 之前,程序暂时把 SDA/SCL 切换为 GPIO:

  1. 释放 SDA 和 SCL;
  2. 当 SDA 为低时,最多输出 9 个 SCL 脉冲;
  3. 生成一次 STOP 条件;
  4. 恢复 FC5 引脚复用并初始化 LPI2C5。
for (uint32_t pulse = 0U;
pulse < 9U && GPIO_PinRead(GPIO1, SEN66_SDA_PIN) == 0U;
pulse++)
{
SEN66_DrivePinLow(SEN66_SCL_PIN);
SEN66_BitDelay();
SEN66_ReleasePin(SEN66_SCL_PIN);
SEN66_BitDelay();
}

启动失败时,程序还会扫描 0x08 到 0x77,并通过 GPIO 模拟地址阶段再次检查 0x6B 的 ACK。

ST7735 显示原理与刷新优化

基本写屏流程

ST7735 使用 RGB565,每个像素占 2 字节。写入矩形区域时依次发送:

  1. CASET (0x2A):设置列范围;
  2. RASET (0x2B):设置行范围;
  3. RAMWR (0x2C):写入像素数据。

字符块传输

字符串传输使用的方法是先在 RAM 中生成一个字符的 RGB565 像素块,再一次性发送:

ST7735_SetAddressWindow(x, y, width, height);
ST7735_WriteData(pixels, pixelIndex);

这样每个字符由多次小传输变为一次块传输,界面刷新速度明显提升。

只刷新数据区域

如果每秒调用 FillScreen() 再重画所有内容,人眼会看到明显闪烁。当前策略是:

  • 切换页面时才绘制整页静态框架;
  • 总览页的标签只绘制一次;
  • 时间和测量值使用等宽背景字符直接覆盖原位置;
  • 曲线页每次只绘制“上一个点到最新点”的两条线段;
  • 106 点满后只清除曲线内部,不清除标题和坐标轴。

固定宽度数值区域的实现如下:

static void DrawValue(uint16_t y, const char *value)
{
char paddedValue[14];

snprintf(paddedValue, sizeof(paddedValue), "%-13s", value);
ST7735_DrawString(44U, y, paddedValue,
ST7735_COLOR_WHITE,
ST7735_COLOR_BLACK,
1U);
}

双页面与温湿度曲线

SW2 消抖

SW2 接在 P0_20,生成的 pin mux 已将其配置为上拉输入,因此按下时读到低电平。

程序每 100 ms 轮询一次,并要求电平稳定 30 ms 才确认状态变化。函数只在“稳定松开 → 稳定按下”时返回一次事件,长按不会连续切页。

if ((rawPressed != stablePressed) &&
((now - changedAt) >= 30U))
{
stablePressed = rawPressed;
return stablePressed;
}

历史数据缓存

程序使用两个 106 点数组保存原始温度和湿度:

static int16_t g_temperatureHistory[106];
static int16_t g_humidityHistory[106];

坐标映射

曲线区域温度范围设置为 -10~50 °C,湿度范围设置为 0~100 %RH。

屏幕 Y 坐标向下增大,因此换算公式需要反向:

screenY = bottom -
(value - minimum) * (bottom - top) /
(maximum - minimum);

温度使用绿色,湿度使用青色。相邻采样点通过 Bresenham 直线算法连接,避免浮点计算。

运行计时与主循环

顶部 HH:MM:SS 是设备本次上电后的运行计时,模拟 RTC 的功能。

SysTick 每 1 ms 进入一次中断:

void SysTick_Handler(void)
{
g_rtcMilliseconds++;
}

显示时将毫秒数换算为时、分、秒。只有秒值变化时才重画时间字符。

主循环的逻辑为:

检查 SW2 并切换页面
↓
查询 SEN66 Data Ready
↓
读取 27 字节并校验 CRC
↓
换算并更新当前页面
↓
通过 UART 输出格式化数值

实现效果

数据显示和运行时间显示界面

IMG_20261011_213148.jpg

温湿度变化曲线界面

IMG_20261011_213126.jpg

心得体会

本次活动让我对 NXP 系列的 MCU 的开发有了更深的理解,包括工具配置、SDK的查看和调用,进一步熟练了对类似项目的开发流程。

附件下载
sensor.zip
程序工程代码
SPF-90828.pdf
FRDM-MCXN236 开发板电路原理图
Sensirion_Datasheet_SEN6x.pdf
SEN66 数据手册
UM12041.pdf
FRDM-MCXN236 用户手册
团队介绍
傅沈骁是一名UEFI开发工程师
评论
0 / 100
查看更多
硬禾服务号
关注最新动态
0512-67862536
info@eetree.cn
江苏省苏州市苏州工业园区新平街388号腾飞创新园A2幢815室
苏州硬禾信息科技有限公司
Copyright © 2024 苏州硬禾信息科技有限公司 All Rights Reserved 苏ICP备19040198号