news 2026/9/20 2:17:37

RIOT OS 中 HM330X 激光颗粒物传感器测试应用实战:编译、运行与输出解读

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RIOT OS 中 HM330X 激光颗粒物传感器测试应用实战:编译、运行与输出解读

RIOT OS 中 HM330X 激光颗粒物传感器测试应用实战:编译、运行与输出解读

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

导读

本文围绕 RIOT OS 仓库中的 tests/drivers/hm330x/README.md 展开,详细介绍 HM330X(HM-3300/3600 激光粉尘传感器)在 RIOT 上的官方测试应用:从编译选型(hm3301 / hm3302 两种型号)、烧录运行,到每秒一次的传感器数据读取与表格化输出的完整解读。读完本文,你将掌握该测试程序的构建方式、两种型号输出格式的区别(尤其是 hm3302 独有的粒子数统计),并理解测试代码背后的 I2C 数据帧解析、CRC 校验与参数配置等底层驱动原理。

一、测试应用概览:它验证什么

tests/drivers/hm330x/目录是 RIOT OS 为 HM330X 颗粒物传感器提供的官方测试应用,其 README 开宗明义地写道:

This is a simple test application for the HM330X I2C particle sensor. The test will read and print the sensor data every second.

即:这是一个基于I2C 接口的 HM330X 颗粒物传感器测试程序,运行后每秒读取一次传感器数据并打印输出。同时 README 特别强调了一条关键限制:

Note that particle counting is only available with hm3302.

——粒子计数(number concentration)功能仅 hm3302 型号支持,hm3301 只输出 PM 质量浓度。

该测试目录共包含 4 个文件:

文件作用
main.c测试主程序:初始化传感器并按秒循环读取、打印
Makefile构建配置:选择板卡与驱动型号(hm3301/hm3302)
Makefile.ciCI 用的板卡内存约束说明
README.md测试应用说明与运行输出样例

二、编译与运行:如何选择 hm3301 / hm3302

测试应用的 Makefile 非常简洁,核心构建逻辑如下:

BOARD ?= nrf52840-mdk include ../Makefile.drivers_common DRIVER ?= hm3301 USEMODULE += $(DRIVER) USEMODULE += ztimer_usec USEMODULE += ztimer_msec USEMODULE += fmt USEMODULE += fmt_table include $(RIOTBASE)/Makefile.include

关键点解读:

  • DRIVER ?= hm3301:默认编译hm3301驱动模块。如需启用 hm3302 的粒子计数功能,编译时通过命令行覆盖即可,例如make DRIVER=hm3302
  • 默认板卡nrf52840-mdk:可用BOARD=...覆盖为其他支持 I2C 的板卡;
  • 依赖模块ztimer_usec/ztimer_msec(驱动复位时序与测试轮询延时使用)、fmt/fmt_table(输出表格化打印);
  • Makefile.ci 中声明了atmega8因内存不足(BOARD_INSUFFICIENT_MEMORY)不适合运行本测试。

编译运行(以默认配置为例):

make -C tests/drivers/hm330x flash term # 切换为 hm3302 型号: make -C tests/drivers/hm330x DRIVER=hm3302 flash term

将传感器接到板卡 I2C 总线(默认I2C_DEV(0),地址0x40)并复位后,串口终端便会开始以每秒一次的频率刷新输出。

三、主程序流程:初始化、循环读取与打印

测试的核心逻辑在 main.c 中,流程清晰,可分三步:

1. 初始化传感器(使用默认参数)

hm330x_t dev; print_str("+------------Initializing------------+\n"); /* initialize the sensor with default configuration parameters */ if (hm330x_init(&dev, &hm330x_params[0])) { print_str("Initialization failed\n"); return 1; }

这里直接使用驱动提供的默认参数数组hm330x_params[0](定义于 hm330x_params.h),初始化失败则打印错误并退出。

2. 打印表头(按型号区分)

#if IS_USED(MODULE_HM3302) /* 打印 6 列 PM 浓度 + 6 列粒子数,共 12 列 */ #else /* 仅打印 6 列 PM 浓度 */ #endif

通过IS_USED(MODULE_HM3302)宏判断当前编译的是哪个型号,从而决定表头宽度。

3. 每秒循环读取并打印

hm330x_data_t data; while (1) { ztimer_sleep(ZTIMER_MSEC, 1 * MS_PER_SEC); /* read the data and print them on success */ if (hm330x_read(&dev, &data) == 0) { print("|", 1); print_col_u32_dec(data.mc_pm_1, 7); /* ... 依次打印 amc_pm_1 / amc_pm_2p5 / amc_pm_10, 以及 hm3302 专属的 nc_pm_0p3 ~ nc_pm_10 ... */ } else { print_str("Could not read data from sensor\n"); } }

循环体先ztimer_sleep(ZTIMER_MSEC, 1 * MS_PER_SEC)等待 1 秒,再调用hm330x_read()读取数据;成功则用fmt_tableprint_col_u32_dec()按固定列宽打印,失败则提示 "Could not read data from sensor"。

四、输出格式深度解读:两套浓度与六档粒子数

README 给出了hm3302 与 hm3301 两种型号的完整运行输出,下面逐一解析。这是理解传感器数据模型的最佳入口。

4.1 hm3302 输出(完整样例,逐行保留)

main(): This is RIOT! (Version: 2021.04-devel-1342-g93325-pr_driver_hm330x) HM330X test application +------------Initializing------------+ +------------------------+------------------------+----------------------------------------------+ | Standard concentration | Atmospheric Environment| # Particles in 0.1l air of diameter >= | | PM1.0 | PM2.5 | PM10.0 | PM1.0 | PM2.5 | PM10.0 | 0.3µm | 0.5µm | 1.0µm | 2.5µm | 5.0µm | 10µm | +-------+-------+--------+-------+-------+--------+-------+-------+-------+-------+-------+------+ | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0| | 5| 7| 7| 5| 7| 7| 0| 0| 0| 0| 0| 0|

hm3302 的表格分为三个区域:

  • Standard concentration(标准/室内校准浓度)mc_pm_1mc_pm_2p5mc_pm_10
  • Atmospheric Environment(大气环境浓度)amc_pm_1amc_pm_2p5amc_pm_10
  • # Particles in 0.1l air(0.1 升空气中的粒子数):按粒径下界分为0.3µm / 0.5µm / 1.0µm / 2.5µm / 5.0µm / 10µm六档,对应数据结构中的nc_pm_0p3nc_pm_0p5nc_pm_1nc_pm_2p5nc_pm_5nc_pm_10

上述字段全部定义在 drivers/include/hm330x.h 的hm330x_data_t结构体中,其中mc_*为浓度(单位 µg/m³),nc_*为粒子数浓度(单位 #/cm³)。

4.2 hm3301 输出(完整样例,逐行保留)

main(): This is RIOT! (Version: ) HM330X test application +------------Initializing------------+ +------------------------+------------------------+ | Standard concentration | Atmospheric Environment| | PM1.0 | PM2.5 | PM10.0 | PM1.0 | PM2.5 | PM10.0 | +-------+-------+--------+-------+-------+--------+ | 8| 11| 11| 8| 11| 11| | 8| 11| 11| 8| 11| 11| | 8| 11| 11| 8| 11| 11| | 9| 13| 13| 9| 13| 13| | 9| 13| 13| 9| 13| 13| | 9| 13| 13| 9| 13| 13| | 9| 13| 13| 9| 13| 13| | 9| 13| 13| 9| 13| 13|

hm3301 仅输出两组 PM 浓度(室内/标准 与 大气环境),不含粒子数统计列——这正是 README 强调 "particle counting is only available with hm3302" 的直接体现。

4.3 两个型号的差异本质

从测试程序与驱动代码可以推断:是否输出粒子数完全由编译期宏MODULE_HM3302决定。在 main.c 中,表头打印与数据列打印都被#if IS_USED(MODULE_HM3302)包裹;在驱动解析 hm330x.c 中,nc_pm_*六个字段同样仅在MODULE_HM3302使能时从数据帧解析。因此默认DRIVER=hm3301编译时,输出只有 6 列浓度数据。

五、源码级原理:I2C 数据帧、CRC 与字段解析

测试程序能正确打印,依赖 drivers/hm330x/hm330x.c 中扎实的驱动实现。理解了它,也就理解了为什么输出表格长这样。

5.1 通信基础(常量定义)

hm330x_constants.h 定义了三个关键常量:

#define HM330X_I2C_ADDRESS (0x40U) /* I2C 器件地址 */ #define HM330X_DATA_LENGTH (29U) /* 一次读取的数据帧长度(字节) */ #define HM330X_CMD_I2C_MODE (0x88U) /* 切换 I2C 模式的命令字节 */

5.2 初始化:复位引脚与 I2C 模式切换

hm330x_init()(hm330x.c)的执行顺序为:

  1. 校验并保存参数;
  2. 若配置了reset_pin,初始化为输出并执行hm330x_reset()——该函数将复位引脚拉低约 10 µs(HM330X_RESET_TIME_US)再拉高,实现对传感器的硬件复位;
  3. 若配置了set_pin,初始化为输出并拉高(使能器件);
  4. 调用_set_i2c_mode()向地址0x40写入命令字节0x88,把传感器切换到 I2C 模式。

需要说明的是:HM330X 本身支持 UART 与 I2C 两种模式,而 RIOT 驱动只实现 I2C 通路(详见 drivers/include/hm330x.h 中的 About 说明),因此初始化时通过命令字节显式切换到 I2C 模式。初始化失败时返回值含义为:GPIO 初始化失败返回-EIO,I2C 模式设置失败返回-EPROTO

5.3 读取:29 字节数据帧的校验与拆解

hm330x_read()(hm330x.c)是核心:

uint8_t buf[HM330X_DATA_LENGTH] = { 0 }; if (i2c_read_bytes(dev->params.i2c, HM330X_I2C_ADDRESS, buf, HM330X_DATA_LENGTH, 0)) { return -EPROTO; } /* calculate crc */ uint8_t crc = 0; for (uint8_t i = 0; i < HM330X_DATA_LENGTH - 1; i++) { crc += buf[i]; }
  • 一次 I2C 突发读取29 字节(数据帧前 2 字节为帧头,真正的测量数据从偏移 4 开始);
  • CRC 校验:对前 28 个字节求和,与第 29 个字节(校验字节)比对;不匹配时打印crc mismatch调试信息。注意当前实现只做告警、不拒绝数据;
  • 字段拆解mc_*amc_*nc_*共 12 个 16 位无符号整数,均按大端序(高字节在前)从帧中取出,例如mc_pm_1 = (buf[4] << 8) | buf[5],依次对齐到偏移 4~27。

这也解释了测试表格的列顺序:先室内/标准浓度(mc),再大气环境浓度(amc),最后(hm3302 专属)六档粒子数(nc)。

5.4 低功耗接口

驱动还提供了hm330x_sleep()/hm330x_wakeup()(hm330x.c):通过拉低/拉高set_pin(Set/Enable 引脚,高有效)控制器件睡眠与唤醒,便于在电池供电节点上按需采样。

六、参数配置:默认值与覆盖方式

测试程序直接使用hm330x_params[0],其默认值定义在 hm330x_params.h:

参数默认值说明
HM330X_PARAM_I2C_DEVI2C_DEV(0)使用的 I2C 总线
HM330X_PARAM_RESET_PINGPIO_UNDEF复位引脚(低有效),默认未接线
HM330X_PARAM_SET_PINGPIO_UNDEFSet/Enable 引脚(高有效),默认未接线
HM330X_SAUL_INFO{ .name = "hm330x" }SAUL 注册名称

所有参数均可在板级或应用级通过宏覆盖(#ifndef ... #endif结构),例如在应用Makefile中追加CFLAGS += -DHM330X_PARAM_I2C_DEV=I2C_DEV(1)即可更换 I2C 总线。

此外,传感器的两套 PM 浓度(室内校准 vs 大气环境)如何取舍,由 Kconfig 中的HM330X_INDOOR_ENVIRONMENT配置项决定,对应头文件中的默认宏CONFIG_HM330X_INDOOR_ENVIRONMENT 1(hm330x.h)。该选项主要影响 SAUL 读数接口返回哪一组浓度值。

七、延伸:SAUL 集成与上层应用

测试程序展示的是最直接的驱动 API 用法。在实际应用中,HM330X 还通过 hm330x_saul.c 接入 RIOT 的SAUL(Sensor Actuator Uber Layer)传感器抽象层,从而可以配合saulshell 命令或auto_init使用:

  • PM 浓度读数以UNIT_GPM3(µg/m³,scale 为 -6)发布,注册为SAUL_SENSE_PM类型,包括mc_pm_1/mc_pm_2p5/mc_pm_10三个驱动条目;
  • hm3302 的粒子数读数以UNIT_CPM3(#/cm³,scale 为 4)发布,注册为SAUL_SENSE_COUNT类型;
  • 浓度读数会根据CONFIG_HM330X_INDOOR_ENVIRONMENT决定读取mc_*(室内)还是amc_*(大气)字段。

因此,同一份驱动既可以在 tests/drivers/hm330x 这类裸 API 测试中逐秒打印原始数据,也可以通过 SAUL 无缝接入gnrc网络栈或其他物联网应用,实现空气质量数据的上报。

八、总结

本文以 tests/drivers/hm330x/README.md 为骨架,完整继承了该文档的两份真实运行输出,并深入其背后的实现:

  • 构建要点DRIVER ?= hm3301决定型号,make DRIVER=hm3302启用粒子计数;
  • 输出模型:mc(室内/标准浓度)、amc(大气环境浓度)、nc(仅 hm3302 的六档粒子数)三组数据,对应 hm330x_data_t 结构体;
  • 底层原理:I2C 地址0x40、29 字节数据帧、累加和 CRC、大端序字段拆解(hm330x.c);
  • 扩展能力:参数可覆盖的默认配置(hm330x_params.h)与 SAUL 抽象层集成。

对需要快速验证 HM330X 传感器硬件、或以此为模板编写自有采样任务的开发者来说,该测试应用是 RIOT OS 中最直接的参考实现。

【免费下载链接】RIOTRIOT - The friendly OS for IoT项目地址: https://gitcode.com/GitHub_Trending/riot/RIOT

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/20 2:17:29

从个人能力到组织资产:AI助手与提示词工程的落地实践

开头先聊个现象&#xff1a;团队里总有那么一个人&#xff0c;别人搞不定的问题到他手里三分钟就有答案&#xff0c;汇报材料写得又快又准&#xff0c;领导问什么都对答如流。你问他怎么做到的&#xff0c;他说“就是用了AI助手”。然后呢&#xff1f;然后就没有然后了。他换部…

作者头像 李华
网站建设 2026/9/20 2:14:57

Grok Bot与OpenClaw实战:AI智能体如何真正替人打杂

刚过去的这段时间&#xff0c;AI 智能体算是彻底火出圈了。但说实话&#xff0c;市面上大部分号称"智能助理"的产品&#xff0c;用起来总觉得差点意思&#xff1a;你让它帮你查资料&#xff0c;它给你甩一堆链接&#xff1b;你让它帮你订个会议室&#xff0c;它说&qu…

作者头像 李华
网站建设 2026/9/20 2:14:42

Notepad++安全下载安装指南:防捆绑、验哈希、适配Win11

1. 这不是“随便下一个记事本”——Notepad下载安装背后的真实需求图谱你搜“Notepad下载安装”&#xff0c;大概率不是想装个能打字的软件。我干这行十多年&#xff0c;每天看几百条真实用户提问&#xff0c;发现90%以上的人点开这个搜索词时&#xff0c;心里真正想的是&#…

作者头像 李华
网站建设 2026/9/20 2:14:23

VSCODE Ctrl+左键跳转失灵?语言服务器与索引配置全解析

1. 问题定位&#xff1a;先搞清楚“跳不了”到底卡在哪一层VSCODE 里Ctrl左键点函数名、类名、变量名&#xff0c;本该直接跳到定义处&#xff0c;结果要么毫无反应&#xff0c;要么底部状态栏弹出一句“正在初始化重新扫描工作区”&#xff0c;要么跳到一个空文件、错误位置&a…

作者头像 李华