- 物联网
- 嵌入式
- 操作系统
- 实时系统
【免费下载链接】RIOT
RIOT - The friendly OS for IoT
导读
本文围绕 RIOT OS 仓库中的 JC42 驱动测试应用 展开,讲解如何在 RIOT 上验证 JEDEC JC42.4 标准兼容温度传感器(如 mcp9804、MAX6604)的工作状态。你将掌握测试应用的三个核心配置参数(TEST_I2C、TEST_I2C_ADDR、TEST_I2C_SPEED)的含义与覆盖方式、预期输出格式,以及驱动底层寄存器读写与温度换算的实现原理,并了解该测试在哪些板上无法运行。
JC42 测试应用概述
RIOT 为每个传感器驱动都配有对应的驱动测试(driver test)应用,tests/drivers/jc42就是专门用于验证 JC42 兼容温度传感器是否正常工作的测试工程。官方 README 对该测试的定位非常明确:验证 jc42 兼容温度传感器是否正常工作。
从驱动头文件可知,jc42 驱动所面向的是与 JEDEC JC42.4 规范兼容的一类温度传感器,常见的型号包括 Microchip 的 mcp9804 和 Maxim 的 MAX6604。MCU 与这类传感器之间通过 I2C 接口通信,驱动只负责读取温度值;虽然传感器本身还支持报警线(alarm wire)等特性,但该驱动并未实现,这一点在头文件的描述中也有明确说明。
测试应用的文件结构非常精简,一共只有四个文件:
- tests/drivers/jc42/main.c:测试主程序
- tests/drivers/jc42/Makefile:构建与参数配置入口
- tests/drivers/jc42/Makefile.ci:CI 板卡排除列表
- tests/drivers/jc42/README.md:测试说明文档
三个核心配置参数
根据 README,测试应用需要通过编译宏注入三个参数:
| 宏 | 含义 | 说明 |
|---|---|---|
TEST_I2C | 使用的 I2C 外设编号 | 指定传感器挂接在哪个 I2C 控制器上 |
TEST_I2C_ADDR | 传感器 I2C 从机地址 | 通常为0x18 |
TEST_I2C_SPEED | I2C 总线速率 | 通常为I2C_SPEED_NORMAL |
(注意:README 原文将TEST_I2C_SPEED的说明误写为 "The sensor address",实际含义是 I2C 总线速率,这一点可从 main.c 的宏检查与 Makefile 的默认值定义得到确认。)
这三个参数在 main.c 中是以强制要求的形式出现的——如果编译时没有定义任何一个宏,编译器会直接报错终止构建:
#ifndef TEST_I2C #error "TEST_I2C not defined" #endif #ifndef TEST_I2C_ADDR #error "TEST_I2C_ADDR not defined" #endif #ifndef TEST_I2C_SPEED #error "TEST_I2C_SPEED not defined" #endifMakefile 中的默认值与注入机制
尽管 main.c 强制要求宏存在,tests/drivers/jc42/Makefile 还是通过?=提供了安全默认值,确保直接编译也能通过:
USEMODULE += jc42 USEMODULE += xtimer # set default device parameters in case they are undefined TEST_I2C ?= 0 TEST_I2C_ADDR ?= 0x18 TEST_I2C_SPEED ?= I2C_SPEED_NORMAL # export parameters CFLAGS += -DTEST_I2C=$(TEST_I2C) CFLAGS += -DTEST_I2C_ADDR=$(TEST_I2C_ADDR) CFLAGS += -DTEST_I2C_SPEED=$(TEST_I2C_SPEED)这里的机制值得注意:?=意味着只有在命令行或环境中未定义对应变量时才采用默认值。如果你在构建时通过make TEST_I2C_ADDR=0x1A指定了地址,那么TEST_I2C_ADDR就会保持你传入的值;随后这三个变量被转换为-D编译选项,以宏的形式注入到 main.c 的编译单元中。
该 Makefile 同时引用了jc42驱动模块和xtimer模块——前者提供传感器读写能力,后者为测试循环的 1 秒定时提供支持。
与驱动默认参数的差异
值得留意的细节是:测试应用的默认速率为I2C_SPEED_NORMAL,而驱动自身的默认配置(jc42_params.h)使用的却是I2C_SPEED_FAST:
#ifndef JC42_PARAM_I2C_DEV #define JC42_PARAM_I2C_DEV I2C_DEV(0) #endif #ifndef JC42_PARAM_ADDR #define JC42_PARAM_ADDR (0x18) #endif #ifndef JC42_PARAM_SPEED #define JC42_PARAM_SPEED I2C_SPEED_FAST #endif也就是说,在普通产品代码中驱动默认以 fast 模式(400 kHz 档)工作;而测试应用为了兼容性考虑,默认回退到 normal 模式。JC42.4 规范本身同时支持两种速率,因此这两种选择都是合规的。实际接线时,请以你手上板子的 I2C 引脚分配和传感器的上拉电阻配置为准来决定采用哪种速率。
测试流程:初始化、轮询与结果输出
main.c 的主函数逻辑非常清晰,分为三个阶段:
1. 构造参数并初始化
jc42_t dev; jc42_params_t params = { .i2c = TEST_I2C, .addr = TEST_I2C_ADDR, .speed = TEST_I2C_SPEED, }; puts("JC42 temperature sensor test application\n"); /* initialize the sensor */ printf("Initializing sensor..."); if (jc42_init(&dev, ¶ms) == 0) { puts("[OK]"); } else { puts("[Failed]"); return 1; }jc42_params_t结构体由i2c(I2C 设备)、speed(总线速率)、addr(从机地址)三个字段组成,定义见 drivers/include/jc42.h。初始化失败时测试程序直接以返回码 1 退出。
2. 每秒读取一次温度
int16_t temperature; while (1) { printf("Testing sensor communication..."); if (jc42_get_temperature(&dev, &temperature) == 0) { puts("[OK]"); } else { puts("[Failed]"); return 1; } ... xtimer_msleep(1000); }测试采用无限循环方式:每次先打印Testing sensor communication...,调用jc42_get_temperature读取一次温度,成功后输出[OK]与温度值,然后通过xtimer_msleep(1000)休眠 1 秒。
3. 以两位小数格式打印温度
驱动返回的温度单位是百分之一摄氏度(centi-degree),即2500表示25.00 °C。main.c 先处理符号,再拆分整数部分和小数部分输出:
bool negative = (temperature < 0); if (negative) { temperature = -temperature; } /* display results */ printf("temperature: %c%d.%02d C\n", (negative) ? '-' : ' ', temperature / 100, temperature % 100);预期输出
README 对预期结果的描述是:传感器应持续(每 1 秒)输出温度,精度为两位小数。一次正常运行的串口输出形如:
JC42 temperature sensor test application Initializing sensor...[OK] Testing sensor communication...[OK] temperature: 25.31 C Testing sensor communication...[OK] temperature: 25.37 C ...驱动源码级原理:寄存器与温度换算
测试应用只是表面,真正支撑它的是 drivers/jc42/jc42.c 这个驱动实现。理解其内部机制有助于排查"为什么测试失败"之类的问题。
寄存器地图
JC42 内部头文件定义了 JC42 规范的寄存器地址:
| 寄存器 | 地址 | 用途 |
|---|---|---|
JC42_REG_CAP | 0x00 | 能力寄存器 |
JC42_REG_CONFIG | 0x01 | 配置寄存器 |
JC42_REG_TEMP_UPPER | 0x02 | 上限温度报警阈值 |
JC42_REG_TEMP_LOWER | 0x03 | 下限温度报警阈值 |
JC42_REG_TEMP_CRITICAL | 0x04 | 临界温度报警阈值 |
JC42_REG_TEMP | 0x05 | 温度读数(核心数据) |
JC42_REG_MANID | 0x06 | 厂商 ID |
JC42_REG_DEVICEID | 0x07 | 器件 ID |
其中 0x00 和 0x01 是 JC42.4 规范要求的"兼容地址"(capability 与 config 必须对所有器件可见),这也是驱动能够通过读取 config 寄存器来探测设备是否存在的依据。
初始化即探测
jc42_init并不做复杂的配置写操作,而是通过读取配置寄存器来轮询设备是否存在:
int jc42_init(jc42_t* dev, const jc42_params_t* params) { uint16_t config; dev->i2c = params->i2c; dev->addr = params->addr; /* Poll the device, fail if unavailable */ if (jc42_get_config(dev, &config) != 0) { return JC42_NODEV; } return JC42_OK; }这意味着:只要传感器在线且 I2C 通信正常,初始化就会成功;如果地址写错、接线断开或 I2C 外设号配错,jc42_get_config会返回非零值,测试程序便会打印[Failed]并以状态码 1 退出。这与测试中"Initializing sensor..."环节直接对应。
温度换算公式
jc42_get_temperature是测试循环每次都要调用的核心函数:
int jc42_get_temperature(const jc42_t* dev, int16_t* temperature) { struct { signed int x:12;} s; uint16_t tmp; /* Read temperature */ if (jc42_get_register(dev, JC42_REG_TEMP, &tmp) != 0) { return JC42_NODEV; } tmp = ntohs(tmp); /* Convert fixed point to uint16_t */ *temperature = ((s.x = tmp)*100)>>4; return JC42_OK; }其换算逻辑可以拆解为:
- 从 0x05 寄存器读取 16 位原始值;
- 用
ntohs做字节序转换(I2C 上是 big-endian,本地可能为 little-endian); - 利用一个 12 位有符号位域把高 12 位解析为带符号定点数(符号位 + 11 位整数/小数,LSB 代表 1/16 °C);
×100 >> 4等价于× (100/16),即把 1/16 °C 的 LSB 换算为百分之一摄氏度。
例如原始值0x0188(12 位定点数 24.5)经换算后得到2450,即 24.50 °C。负温度(如 -10.25 °C)由 12 位有符号位域处理为负数,再由 main.c 的符号逻辑在打印时恢复-号。这也解释了为什么测试输出能做到稳定的两位小数精度。
底层 I2C 访问
驱动通过i2c_acquire/i2c_release保护总线访问,并调用 RIOT 外设层的i2c_read_regs/i2c_write_regs完成寄存器读写;任何一步失败都会返回JC42_NODEV(-2)。错误码体系定义在 drivers/include/jc42.h:
JC42_OK= 0:成功JC42_NOI2C= -1:I2C 错误JC42_NODEV= -2:设备不存在或通信失败
SAUL 集成:不止测试,还能接入传感器抽象层
该驱动还提供了 SAUL(Sensor Actuator Ubiquitous Layer)适配,见 jc42_saul.c。它把温度读取包装为SAUL_SENSE_TEMP类型的端点,单位UNIT_TEMP_C、scale = -2(与百分之一摄氏度一一对应),通过jc42_temperature_saul_driver导出。这意味着同一颗传感器不仅能跑本文的测试,还能通过 SAUL 统一接口被saul_reg、shell 命令乃至上层应用统一读取。
构建与运行
前置条件
测试需要一块支持 I2C 且内存足够的板子。测试的 CI 排除列表 Makefile.ci 明确列出了atmega8因内存不足(BOARD_INSUFFICIENT_MEMORY)而无法运行此测试——这是该测试对硬件资源的最低要求信号,选板时建议参考。
编译命令
在 RIOT 根目录下,直接对测试应用执行标准构建流程:
# 使用默认参数构建(TEST_I2C=0, TEST_I2C_ADDR=0x18, TEST_I2C_SPEED=I2C_SPEED_NORMAL) make -C tests/drivers/jc42 BOARD=nucleo-f401re flash term # 覆盖 I2C 设备号与从机地址 make -C tests/drivers/jc42 BOARD=nucleo-f401re TEST_I2C=1 TEST_I2C_ADDR=0x1A flash term命令行传入的变量会优先于 Makefile 中的默认值,最终以-D宏的形式进入编译。flash负责烧录,term打开串口终端以便观察每秒一次的温度输出。
快速验证要点
- 初始化打印
[OK]说明传感器在线、地址正确; - 循环中打印
[OK]且温度值稳定在物理合理范围内,说明读数链路正常; - 若出现
[Failed],按JC42_NODEV语义依次检查:I2C 外设编号是否与板卡接线一致、从机地址(默认 0x18,常见型号可通过引脚配置改为 0x19~0x1F)是否匹配、I2C 速率是否超出传感器支持范围。
小结
tests/drivers/jc42虽然只是一个体积小巧的驱动测试应用,却完整覆盖了 JC42 传感器验证的全部关键环节:通过三个编译宏完成 I2C 设备、地址与速率的灵活配置,初始化时以读配置寄存器方式探测设备,随后以 1 秒周期读取温度并以两位小数输出。结合 jc42.c 的 12 位有符号定点数换算与 jc42_internal.h 的寄存器地图,开发者既能快速验证硬件,也能在出现异常时定位到通信层或换算层的问题。对于需要把该传感器接入 SAUL 体系的场景,jc42_saul.c 还提供了开箱即用的温度端点,实现从"裸驱动测试"到"统一传感层"的无缝衔接。
- 物联网
- 嵌入式
- 操作系统
- 实时系统
【免费下载链接】RIOT
RIOT - The friendly OS for IoT
相关推荐
Binci完全指南:如何用Docker容器化你的开发工作流,告别环境配置烦恼
Binci完全指南:如何用Docker容器化你的开发工作流,告别环境配置烦恼 Binci是一款强大的开发工作流容器化工具,它能够帮助开发者将开发环境和任务流程通
物联网嵌入式操作系统实时系统RIOT OS 中 HIH6130 温湿度传感器驱动测试:从编译参数到数据读取的完整指南
RIOT OS 中 HIH6130 温湿度传感器驱动测试:从编译参数到数据读取的完整指南 导读 HIH6130 是 Honeywell HumidIcon 系列
物联网嵌入式操作系统实时系统RIOT 中 SCD30 CO2/温湿度传感器驱动测试应用深度解析:从编译运行到源码原理
RIOT 中 SCD30 CO2/温湿度传感器驱动测试应用深度解析:从编译运行到源码原理 导读 本文围绕 RIOT 仓库中 tests/drivers/scd3
物联网嵌入式操作系统实时系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考