FRDM-MCXN236 环境监测系统 - 活动报告
一、项目介绍
本项目基于 NXP FRDM-MCXN236 开发板,实现了一个完整的环境数据采集与监测系统。系统通过 I2C 总线连接 BME280/BMP280 环境传感器,实时采集温度、湿度和气压数据,并通过串口输出监测结果。系统具备数据滤波、历史记录、统计分析和阈值报警等功能,满足工业级环境监测需求。
项目特点:
- 基于 Zephyr RTOS 实时操作系统,具备良好的可移植性和扩展性
- 支持双传感器自动检测(I2C 地址 0x76 和 0x77)
- 实现滑动平均滤波算法,提高数据稳定性
- 提供多级数据处理:原始数据、滤波数据、统计数据、历史数据
- LED 状态指示:绿灯闪烁表示正常运行,红灯常亮表示故障或阈值报警
- 数据刷新周期 1 秒,满足实时监测要求
二、硬件介绍
2.1 FRDM-MCXN236 开发板
2.2 BME280/BMP280 环境传感器
2.3 硬件连接

I2C 连接(通过 Arduino 接口):
- SDA(数据线): Arduino D14 → PIO1_16 → flexcomm5_lpi2c5
- SCL(时钟线): Arduino D15 → PIO1_17 → flexcomm5_lpi2c5
- VDD: 3.3V 电源
- GND: 地线
串口连接(板载 MCU-Link):
- UART4: flexcomm4_lpuart4,波特率 115200 8N1
- 调试接口: USB 连接到 PC,通过 J10 接口
LED 状态指示:
- 绿色 LED(led1): 正常运行时周期性闪烁
- 红色 LED(led0): 故障或阈值报警时常亮
三、方案框图与设计思路
3.1 系统架构框图

3.2 数据流处理流程

3.3 设计思路
1. 模块化设计
- 采用功能分层架构:硬件抽象层 → 驱动层 → 应用层
- 每个功能封装为独立函数,便于测试和维护
- 使用 Zephyr 设备树管理硬件配置,无需硬编码地址
2. 数据处理策略
- 滑动平均滤波: 减少传感器瞬时噪声,提高数据稳定性
- 环形缓冲存储: 固定内存占用,适合长时间运行
- 统计分析: 提供最小/最大/平均值,便于趋势分析
- 阈值报警: 六个独立阈值(温度高/低、湿度高/低、气压高/低)
3. 实时性保证
- 采用固定周期起点调度算法,避免采集时间累积漂移
- 周期为 1000ms,满足 ≤1 秒的刷新要求
- 检测周期超限情况,输出警告信息
4. 兼容性设计
- 自动检测传感器类型(BME280 或 BMP280)
- 支持双地址传感器(0x76 和 0x77)
- BMP280 无湿度通道时自动降级,不影响温度和气压监测
5. 诊断功能
- 启动时执行 I2C 总线扫描,检测所有设备
- 读取传感器芯片 ID,验证通信正常
- 对于 BMP280,输出校准数据和原始 ADC 值,便于故障排查
四、调试软件介绍
4.1 开发工具链
构建系统:
- Zephyr RTOS: v3.x,提供 RTOS 内核、驱动和构建系统
- CMake: 跨平台构建工具
- West: Zephyr 的元构建工具,管理依赖和构建流程
- GCC Arm: arm-none-eabi-gcc 14.2,C99 标准
调试工具:
- pyOCD: 开源调试器,支持 MCU-Link
- CMSIS-Pack: NXP.MCXN236_DFP v26.06.00
- 串口终端: PuTTY / Tera Term / minicom,115200 8N1
4.2 构建与烧录
构建命令:
west build -b frdm_mcxn236/mcxn236 -p auto
烧录命令:
west flash -d build -r pyocd
串口监控:
# Windows
# 连接到 COM 端口,波特率 115200
# Linux/macOS
minicom -D /dev/ttyACM0 -b 115200
4.3 配置选项 (Kconfig)
系统提供可配置参数,通过 menuconfig 或 prj.conf 修改:
配置项 | 默认值 | 说明 |
|---|---|---|
ENV_SAMPLE_PERIOD_MS | 1000 | 采样周期(毫秒) |
ENV_FILTER_WINDOW_SIZE | 5 | 滤波窗口大小 |
ENV_HISTORY_SIZE | 60 | 历史数据存储点数 |
ENV_STATS_INTERVAL_SAMPLES | 60 | 统计输出间隔(样本数) |
ENV_TEMP_MIN_MILLI_C | 0 | 低温报警阈值(0°C) |
ENV_TEMP_MAX_MILLI_C | 40000 | 高温报警阈值(40°C) |
ENV_HUMIDITY_MIN_MILLI_PERCENT | 20000 | 低湿报警阈值(20%) |
ENV_HUMIDITY_MAX_MILLI_PERCENT | 80000 | 高湿报警阈值(80%) |
ENV_PRESSURE_MIN_PA | 90000 | 低压报警阈值(900hPa) |
ENV_PRESSURE_MAX_PA | 105000 | 高压报警阈值(1050hPa) |
五、软件流程图
5.1 主程序流程

5.2 传感器数据处理流程

六、关键代码介绍
6.1 传感器数据读取
/**
* @brief Read all environmental channels from a sensor.
*/
static int read_environment_data(const struct device *device,
environment_data_t *data)
{
struct sensor_value temperature = {0};
struct sensor_value humidity = {0};
struct sensor_value pressure = {0};
bool humidity_available = false;
int result = 0;
// 触发传感器采集(强制模式下每次都需要触发)
result = sensor_sample_fetch(device);
if (0 != result)
{
return result;
}
// 读取温度通道
result = sensor_channel_get(device, SENSOR_CHAN_AMBIENT_TEMP, &temperature);
if (0 != result)
{
return result;
}
// 读取湿度通道(BMP280 会返回 -ENOTSUP)
result = sensor_channel_get(device, SENSOR_CHAN_HUMIDITY, &humidity);
if ((0 != result) && (-ENOTSUP != result))
{
return result;
}
humidity_available = (0 == result);
// 读取气压通道
result = sensor_channel_get(device, SENSOR_CHAN_PRESS, &pressure);
if (0 != result)
{
return result;
}
// 转换为 milli-units(便于整数运算)
data->temperature_milli_c = (int32_t)sensor_value_to_milli(&temperature);
data->humidity_valid = humidity_available;
if (data->humidity_valid)
{
data->humidity_milli_percent = (int32_t)sensor_value_to_milli(&humidity);
}
// Zephyr 的气压单位是 kPa,milli-kPa 数值上等于 Pa
data->pressure_pa = (int32_t)sensor_value_to_milli(&pressure);
return 0;
}
设计要点:
- 使用 Zephyr 传感器 API,具备良好的可移植性
sensor_sample_fetch()触发一次 ADC 转换(强制模式)- 对湿度通道返回
-ENOTSUP进行特殊处理,兼容 BMP280 - 使用 milli-units 避免浮点运算,提高效率
6.2 滑动平均滤波实现
/**
* @brief Add a sample to the moving-average filter.
*/
static int filter_environment_data(environment_filter_t *filter,
const environment_data_t *input,
environment_data_t *output)
{
size_t divisor = 0U;
// 如果窗口已满,移除最旧样本的贡献
if (CONFIG_ENV_FILTER_WINDOW_SIZE == filter->sample_count)
{
const environment_data_t *oldest = &filter->samples[filter->next_index];
filter->temperature_sum -= oldest->temperature_milli_c;
if (oldest->humidity_valid)
{
filter->humidity_sum -= oldest->humidity_milli_percent;
filter->humidity_sample_count--;
}
filter->pressure_sum -= oldest->pressure_pa;
}
else
{
filter->sample_count++;
}
// 添加新样本
filter->samples[filter->next_index] = *input;
filter->temperature_sum += input->temperature_milli_c;
if (input->humidity_valid)
{
filter->humidity_sum += input->humidity_milli_percent;
filter->humidity_sample_count++;
}
filter->pressure_sum += input->pressure_pa;
filter->next_index = (filter->next_index + 1U) % CONFIG_ENV_FILTER_WINDOW_SIZE;
// 计算平均值
divisor = filter->sample_count;
output->temperature_milli_c = (int32_t)(filter->temperature_sum / (int64_t)divisor);
output->humidity_valid = (0U < filter->humidity_sample_count);
if (output->humidity_valid)
{
output->humidity_milli_percent =
(int32_t)(filter->humidity_sum / (int64_t)filter->humidity_sample_count);
}
output->pressure_pa = (int32_t)(filter->pressure_sum / (int64_t)divisor);
return 0;
}
设计要点:
- 使用环形缓冲区,固定内存占用
- 保存历史样本和累加和,O(1) 时间复杂度
- 湿度通道单独计数,处理 BMP280 无湿度的情况
- 使用 64 位累加和,避免溢出
6.3 阈值报警判定
/**
* @brief Calculate alarm flags for a filtered sample.
*/
static uint32_t get_environment_alarm_flags(const environment_data_t *data)
{
uint32_t alarm_flags = 0U;
if (NULL == data)
{
return ALARM_TEMPERATURE_LOW | ALARM_TEMPERATURE_HIGH |
ALARM_HUMIDITY_LOW | ALARM_HUMIDITY_HIGH |
ALARM_PRESSURE_LOW | ALARM_PRESSURE_HIGH;
}
// 温度阈值判定
if (CONFIG_ENV_TEMP_MIN_MILLI_C > data->temperature_milli_c)
{
alarm_flags |= ALARM_TEMPERATURE_LOW;
}
if (CONFIG_ENV_TEMP_MAX_MILLI_C < data->temperature_milli_c)
{
alarm_flags |= ALARM_TEMPERATURE_HIGH;
}
// 湿度阈值判定(仅在有效时)
if (data->humidity_valid &&
(CONFIG_ENV_HUMIDITY_MIN_MILLI_PERCENT > data->humidity_milli_percent))
{
alarm_flags |= ALARM_HUMIDITY_LOW;
}
if (data->humidity_valid &&
(CONFIG_ENV_HUMIDITY_MAX_MILLI_PERCENT < data->humidity_milli_percent))
{
alarm_flags |= ALARM_HUMIDITY_HIGH;
}
// 气压阈值判定
if (CONFIG_ENV_PRESSURE_MIN_PA > data->pressure_pa)
{
alarm_flags |= ALARM_PRESSURE_LOW;
}
if (CONFIG_ENV_PRESSURE_MAX_PA < data->pressure_pa)
{
alarm_flags |= ALARM_PRESSURE_HIGH;
}
return alarm_flags;
}
设计要点:
- 使用位掩码表示多个报警状态
- 阈值通过 Kconfig 配置,便于调整
- 湿度报警仅在传感器支持时生效
- 红灯点亮逻辑:
alarm_active || acquisition_failed
6.4 固定周期调度
// 主循环中的调度逻辑
next_sample_ms = k_uptime_get(); // 初始化起点
while (true)
{
int64_t acquisition_time_ms = k_uptime_get();
// 处理所有传感器
for (sensor_index = 0U; sensor_index < ARRAY_SIZE(g_sensor_states); sensor_index++)
{
if (!g_sensor_states[sensor_index].active)
{
continue;
}
result = process_environment_sensor(&g_sensor_states[sensor_index],
acquisition_time_ms,
&alarm_active);
// 错误处理...
}
// 更新 LED 状态...
// 固定周期调度:下一周期 = 当前周期 + 周期时间
next_sample_ms += CONFIG_ENV_SAMPLE_PERIOD_MS;
remaining_ms = next_sample_ms - k_uptime_get();
if (0LL < remaining_ms)
{
k_msleep((int32_t)remaining_ms); // 休眠到下一周期
}
else
{
// 处理超限情况
printk("Warning: acquisition cycle overrun by %lld ms\n",
(long long)(-remaining_ms));
next_sample_ms = k_uptime_get(); // 重新同步
}
}
设计要点:
- 使用固定周期起点,避免时间累积漂移
next_sample_ms += PERIOD而非next_sample_ms = current + PERIOD- 检测周期超限,输出警告并重新同步
- 保证长时间运行时采集周期的精确性
6.5 I2C 总线诊断
/**
* @brief Scan the I2C bus for devices using all standard 7-bit addresses.
*/
static size_t scan_i2c_bus(const struct device *bus)
{
size_t found_count = 0U;
uint8_t address = I2C_SCAN_FIRST_ADDRESS;
printk("I2C scan start: 7-bit addresses 0x%02x..0x%02x\n",
I2C_SCAN_FIRST_ADDRESS, I2C_SCAN_LAST_ADDRESS);
// 扫描所有标准 7 位地址
for (address = I2C_SCAN_FIRST_ADDRESS;
address <= I2C_SCAN_LAST_ADDRESS;
address++)
{
// 发送零长度写事务,仅检测 ACK
int result = i2c_write(bus, NULL, 0U, address);
if (0 == result)
{
printk("I2C scan: found device at 0x%02x\n", address);
found_count++;
}
}
printk("I2C scan complete: %u device(s) found\n",
(unsigned int)found_count);
if (0U == found_count)
{
printk("I2C scan: no ACK; check VDD/GND, pull-ups, "
"SDA/SCL pins and sensor address\n");
}
return found_count;
}
设计要点:
- 启动时自动扫描,便于诊断连接问题
- 扫描范围 0x03..0x77(标准 7 位地址)
- 使用零长度写事务,仅检测地址 ACK
- 提供详细的故障排查提示
七、功能展示与说明
7.1 系统启动输出
I2C scan start: 7-bit addresses 0x03..0x77
I2C scan: found device at 0x76
I2C scan: found device at 0x77
I2C scan complete: 2 device(s) found
I2C diagnostic 0x77: ACK, chip ID=0x60
I2C diagnostic 0x76: ACK, chip ID=0x58
BMP280 calibration: 6e 6d a2 67 32 00 f3 8c ca d5 d0 0b 27 0e 1e ff ...
BMP280 raw: adc_P=524288 (0x80000) adc_T=524288 (0x80000)
BMP280 status: status=0x00 ctrl_meas=0x27
Environment sensor 0 ready: bme280@77
Environment sensor 1 ready: bme280@76
Environment monitor started: period=1000 ms filter=5 history=60
说明:
- I2C 扫描发现两个设备(0x76 和 0x77)
- 0x77 芯片 ID 为 0x60(BME280),0x76 芯片 ID 为 0x58(BMP280)
- BMP280 的原始 ADC 值为 0x80000,表示传感器异常(需要检查硬件)
- 两个传感器都已初始化成功
7.2 正常运行数据输出
[1023 ms] bme280@77 raw T=23.450 C RH=45.230 % P=1013.25 hPa | avg T=23.448 C RH=45.215 % P=1013.26 hPa
[2024 ms] bme280@77 raw T=23.455 C RH=45.240 % P=1013.24 hPa | avg T=23.451 C RH=45.225 % P=1013.25 hPa
[3025 ms] bme280@77 raw T=23.460 C RH=45.235 % P=1013.26 hPa | avg T=23.453 C RH=45.228 % P=1013.25 hPa

数据解读:
- 时间戳:
[1023 ms]表示系统运行时间 - 设备名称:
bme280@77表示 I2C 地址为 0x77 的传感器 - 原始数据:
raw T=23.450 C温度 23.450°C - 原始湿度:
RH=45.230 %相对湿度 45.230% - 原始气压:
P=1013.25 hPa气压 1013.25 百帕 - 滤波数据:
avg T=23.448 C滤波后温度(5点平均) - 精度: 温度 0.001°C,湿度 0.001%,气压 0.01 hPa
7.5 统计数据输出
STATS bme280@77 samples=60 stored=60 avg T=23.456 C RH=45.234 % P=1013.27 hPa
RANGE bme280@77 T=23.420..23.490 C RH=45.200..45.280 % P=1013.20..1013.35 hPa
说明:
- 样本数:
samples=60表示已采集 60 个样本 - 存储数:
stored=60表示历史缓冲区已满(可存储 60 点) - 平均值: 温度 23.456°C,湿度 45.234%,气压 1013.27 hPa
- 范围: 温度 23.420..23.490°C,变化 0.07°C
- 每 60 个样本(默认 60 秒)输出一次
7.6 LED 状态指示
正常运行:
- 绿色 LED: 每秒闪烁一次(每次采集后翻转状态)
- 红色 LED: 关闭

- 绿色 LED: 继续闪烁
- 红色 LED: 常亮
