文章目录
-
- [一、I2C 基础](#一、I2C 基础)
-
- [1.1 简介](#1.1 简介)
- [1.2 I2C 总线核心知识](#1.2 I2C 总线核心知识)
- [1.3 I2C 接口资源示例](#1.3 I2C 接口资源示例)
- [二、I2C 设备树标准写法](#二、I2C 设备树标准写法)
-
- [2.1 设备树常见路径](#2.1 设备树常见路径)
- [2.2 控制器节点的标准写法](#2.2 控制器节点的标准写法)
- [2.3 从设备节点的标准写法](#2.3 从设备节点的标准写法)
- [2.4 注意事项](#2.4 注意事项)
- [三、I2C 调试工具 i2c-tools](#三、I2C 调试工具 i2c-tools)
-
- [3.1 安装 i2c-tools](#3.1 安装 i2c-tools)
- [3.2 扫描总线与设备](#3.2 扫描总线与设备)
- [3.3 读取寄存器](#3.3 读取寄存器)
- [3.4 写入寄存器](#3.4 写入寄存器)
- [四、I2C 应用层开发实例](#四、I2C 应用层开发实例)
-
- [4.1 标准编程流程](#4.1 标准编程流程)
- [4.2 通信驱动示例](#4.2 通信驱动示例)
- [4.3 理解通信原理](#4.3 理解通信原理)
- [4.4 HM832IS 功率传感器实例](#4.4 HM832IS 功率传感器实例)
- 常见问题排查
-
- [问题 1:`ls /dev/i2c-*` 无设备节点](#问题 1:
ls /dev/i2c-*无设备节点) - [问题 2:`i2cdetect` 扫描不到设备](#问题 2:
i2cdetect扫描不到设备) - [问题 3:I2C 读写超时或数据乱码](#问题 3:I2C 读写超时或数据乱码)
- [问题 4:设备树配置后不生效](#问题 4:设备树配置后不生效)
- [问题 1:`ls /dev/i2c-*` 无设备节点](#问题 1:
一、I2C 基础
1.1 简介
ZYNQ 系列是 Xilinx 常用的嵌入式异构平台,PS 端集成 ARM 处理器与常用外设控制器,I2C 总线广泛用于连接传感器、EEPROM、OLED、陀螺仪等低速外设。ZYNQ I2C 开发常见问题如下所示。
- Vivado 中未使能 PS I2C 或 MIO 引脚分配错误;
- PetaLinux 设备树未使能 I2C 控制器
/dev/i2c-*设备节点不存在;i2cdetect扫描不到设备;- SCL/SDA 接反、上拉电阻缺失、供电异常;
- 应用层代码读写寄存器异常;
- 设备树修改后未重新编译 BOOT.BIN / image.ub。
本文从 I2C 设备树配置、i2c-tools 调试、C 语言应用层设计,给出项目中常见的开发流程。
1.2 I2C 总线核心知识
I2C 是两线制同步串行通信总线,仅需 SCL(时钟线)、SDA(数据线)和 GND(共地线),即可实现多设备通信,支持 7 位 / 10 位设备地址。
ZYNQ PS 端通常集成多个 I2C 控制器,常见特点如下所示。
- 支持主模式通信;
- 支持标准模式 100kHz、快速模式 400kHz,具体上限以芯片 TRM 和板级设计为准;
- 引脚通过 MIO 复用,需要在 Vivado PS 配置或板级 pinctrl 中确认;
- Linux 下通常生成
/dev/i2c-0、/dev/i2c-1等设备节点。
注意:Zynq 的 I2C 引脚不是固定 40Pin 定义,必须根据开发板原理图和 Vivado MIO 分配确认。
1.3 I2C 接口资源示例
| 控制器 | 常见设备节点 | 说明 |
|---|---|---|
| PS I2C0 | /dev/i2c-0 |
Zynq-7000 / ZynqMP 常用 PS I2C |
| PS I2C1 | /dev/i2c-1 |
具体是否引出以开发板原理图为准 |
不同开发板的 MIO 分配不同,SCL/SDA 对应引脚必须以原理图和 Vivado 配置为准。
二、I2C 设备树标准写法
Zynq 平台 I2C 设备树的组织方式很直接:控制器节点 负责描述总线本身(地址、中断、时钟、引脚复用、总线速率),从设备子节点负责描述挂在这条总线上的具体芯片(地址、驱动匹配字符串、中断、私有参数)。
2.1 设备树常见路径
- Zynq-7000 系列
bash
kernel/arch/arm/boot/dts/zynq-*.dts
- ZynqMP 系列
bash
kernel/arch/arm64/boot/dts/xilinx/zynqmp-*.dts
- PetaLinux 工程
bash
project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi
2.2 控制器节点的标准写法
控制器节点中已由板级 DTS 定义的属性(如 #address-cells、#size-cells、pinctrl-0)在 system-user.dtsi 中通常不需要重复。覆写只针对项目中需要改变或新增的部分,以一个 "完整" 的 I2C 控制器节点为例,如下所示。
dts
# ZYNQ 的 zynq-7000.dtsi 里已经定义了 i2c0、i2c1 节点
i2c0: i2c@e0004000 {
compatible = "cdns,i2c-r1p10";
status = "disabled";
clocks = <&clkc 38>;
interrupt-parent = <&intc>;
interrupts = <0 25 4>;
clock-frequency = <400000>;
reg = <0xe0004000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
};
i2c1: i2c@e0005000 {
compatible = "cdns,i2c-r1p10";
status = "okay";
clocks = <&clkc 39>;
interrupt-parent = <&intc>;
interrupts = <0 48 4>;
clock-frequency = <400000>;
reg = <0xe0005000 0x1000>;
#address-cells = <1>;
#size-cells = <0>;
temp-sensor@48 {
compatible = "nst,nst175";
reg = <0x48>;
};
};
aliases {
ethernet0 = &gem0;
i2c0 = &i2c1; /* 用 label 引用,不写路径字符串 */
};
&i2c0 {
status = "disabled";
clock-frequency = <400000>;
};
&i2c1 {
status = "okay";
clock-frequency = <400000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c0_default>;
/* 从设备挂在这里 */
temp-sensor@48 {
compatible = "nst,nst175";
reg = <0x48>;
};
};
设备树说明:
- Zynq-7000 的 PS I2C 控制器(
e0004000/e0005000是 Zynq-7000 TRM 里 I2C0/I2C1 的固定基址),compatible = "cdns,i2c-r1p10"对应 Cadence I2C IP。 - 中断号对照(Zynq-7000):I2C0 → SPI 25(
0x19),I2C1 → SPI 48(0x30),与 TRM 一致。 status:"okay"使能控制器。ZynqMP 的zynqmp.dtsi中 I2C 节点默认通常是"disabled",必须在板级或system-user.dtsi中覆写。clock-frequency:总线时钟,单位 Hz。Zynq PS I2C 常用100000(100 kHz)或400000(400 kHz)。调试阶段建议先保持 100 kHz,确认通信正常后再尝试 400 kHz。#address-cells = <1>和#size-cells = <0>:这对属性定义从设备子节点reg的格式。I2C 从设备的reg只表示"设备地址"这一个值,因此#address-cells为 1、#size-cells为 0。没有这对属性,子节点的reg无法被正确解析。
2.3 从设备节点的标准写法
从设备是控制器节点的直接子节点,这是 Linux 内核文档明确规定的 I2C 设备声明方式:I2C 从设备不在硬件层面自动枚举,必须通过设备树子节点静态声明,总线注册时由 i2c-core 自动实例化。最小从设备节点只需要两个属性,如下所示。
dts
&i2c0 {
status = "okay";
sensor@48 {
compatible = "vendor,device-name";
reg = <0x48>;
};
eeprom@50 {
compatible = "atmel,24c256";
reg = <0x50>;
};
mpu6050@68 {
compatible = "invensense,mpu6050";
reg = <0x68>;
};
};
compatible:驱动匹配字符串。内核中对应的 I2C 驱动通过of_match_table与此字符串匹配,匹配成功后该从设备才会被绑定到驱动。写法必须是"厂商前缀,型号",并且要和内核源码中驱动实际使用的 compatible 字符串一致,不能自造。reg:I2C 从设备的 7 位地址 。这是最容易出错的地方:芯片数据手册常给出 8 位"写地址"(如0xA0),设备树中必须去掉最低的 R/W 位,写0x50,而不是0xA0。节点名称中的@后缀通常与reg保持一致(如sensor@48对应reg = <0x48>),便于阅读。
如果从设备有中断输出连接到 SoC,还需要在子节点中添加中断相关属性,如下所示。
dts
sensor@48 {
compatible = "vendor,device-name";
reg = <0x48>;
interrupt-parent = <&intc>;
interrupts = <...>;
};
⚠️ 注意:
interrupt-parent指向中断控制器(ZYNQ 通常为&intc),interrupts的格式取决于具体 SoC 的中断描述符。如果从设备没有中断引脚,这两项不需要写。- 除以上通用属性外,从设备节点还可以包含驱动定义的私有属性,例如 EEPROM 的
pagesize、size,OLED 的width、height,传感器的reset-gpios等。这些属性由对应绑定文档定义,按需添加即可。 - PetaLinux 下推荐在用户设备树中覆写,而不是直接修改内核源码树中的 DTS。
一个实用的 bring-up 技巧:如果只是想在硬件调试阶段确认地址可达,可以先写一个只有 reg、没有 compatible 的子节点。这样不会触发驱动绑定,但该地址仍然可以被 i2cdetect 探测到,适合在驱动尚未确认 compatible 字符串时快速验证物理链路。
2.4 注意事项
reg写成 8 位地址:这是最常见的错误。设备树中的 I2Creg始终是 7 位地址,不带 R/W 位。- 控制器节点缺少
#address-cells/#size-cells:如果板级 DTS 已经定义,覆写时可以省略;但如果在自建的控制器节点中从头写,必须显式声明,否则子节点reg无法被正确解释。 pinctrl-0引用了不存在的 label:Zynq 的 pinctrl label 依芯片系列和板级 DTS 而不同,pinctrl_i2c0_default不是通用固定名称。不确定时先查板级 DTS 中实际使用的 label,或者干脆不写pinctrl-0,依赖已有配置。compatible与驱动不匹配:I2C 从设备的compatible必须与内核驱动中的of_match_table一致。不确定时在内核源码的drivers/对应驱动中搜索 compatible 字符串,或查阅Documentation/devicetree/bindings/下的绑定文档。- 修改设备树后未强制重编:PetaLinux 中如果只执行
petalinux-build而没有触发 device-tree 包重新编译,烧录的镜像可能仍然携带旧设备树。可在修改system-user.dtsi后先执行petalinux-build -c device-tree -x distclean,再完整编译,避免排查时误判。
三、I2C 调试工具 i2c-tools
3.1 安装 i2c-tools
(一)软件包移植
(1)从 内核官网 下载或 git 克隆,如下所示。
-
源码
git clone git://git.kernel.org/pub/scm/utils/i2c-tools/i2c-tools.git
-
压缩包
bash
# 阿里地址
# https://mirrors.edge.kernel.org/pub/software/utils/i2c-tools/
# 解压
tar -vxf i2c-tools-4.3.tar.gz
(2)指定交叉编译工具链并编译,有两种常用方式,如下所示。
- 方式 A:通过
make参数传入(推荐,不改源码)
假设交叉编译器前缀是 arm-linux-gnueabihf-,直接执行以下命令,如下所示。
bash
# 动态编译
make CC=arm-linux-gnueabihf-gcc
# 静态编译
make CC=arm-linux-gnueabihf-gcc USE_STATIC_LIB=1
- 方式 B:修改 Makefile
打开源码目录下的 Makefile 文件,找到 CC ?= gcc 和 AR ?= ar 等行,将前缀改为对应的交叉工具链,如下所示。
makefile
CC = arm-linux-gnueabihf-gcc
AR = arm-linux-gnueabihf-ar
STRIP = arm-linux-gnueabihf-strip
保存 Makefile 文件,执行以下命令编译,如下所示。
bash
# 动态编译
make
# 静态编译
make USE_STATIC_LIB=1
采用
make编译的可执行程序在运行时会依赖编译生成的动态库,采用make USE_STATIC_LIB=1静态编译的可执行程序在运行时不需要动态库。嵌入式板子的库环境可能不全,静态编译可以避免依赖问题。
编译完成后,工具通常在 tools/ 目录下,核心可执行文件有五个:i2cdetect、i2cdump、i2cget、i2cset、i2ctransfer,将这些可执行文件复制到板子的 /usr/sbin 或 /usr/bin 目录。
(二)PetaLinux 集成
在 PetaLinux 中添加 i2c-tools,最标准的方法是通过图形化配置菜单将其集成到根文件系统(rootfs)中。PetaLinux 没有像 apt 那样的在线包管理器,所有软件都需要在构建阶段打包进镜像。
- 方法一:通过 petalinux-config 菜单配置(推荐)
(1)在 PetaLinux 工程根目录下,执行 petalinux-config 命令进入 Rootfs 配置菜单,如下所示。
bash
petalinux-config -c rootfs
(2)定位并启用 i2c-tools,根据 PetaLinux 版本,菜单路径可能略有差异,请依次尝试,保存配置后退出菜单,如下所示。
- 路径 A(常见于多数版本) :
Filesystem Packages→base→i2c-tools,找到i2c-tools后,按Y键将其选中为[*]状态。 - 路径 B(部分版本) :
Filesystem Packages→console→tools→i2c-tools,同样,选中并保存。
补充建议 :如果菜单中
i2c-tools项下还有dev和dbg等子包,建议一并勾选,它们包含额外的调试辅助工具。
3.2 扫描总线与设备
在 linux 系统下通过 i2cdetect 命令扫描 I2C 总线列出系统中存在的 I2C 适配器或扫描某条总线上挂载了哪些设备,如下所示。
- 基本语法
bash
# -y 忽略提示
# -a 不忽略内核中有驱动的外设
# -q|-r 通常忽略
# I2CBUS I2C 总线,取值为 0 、 1 、 2 等整数
# FIRST LAST 通常忽略
i2cdetect [-y] [-a] [-q|-r] I2CBUS [FIRST LAST]
- 示例 1
bash
# 列出 I2C 适配器下的所有 I2C 设备
i2cdetect -l
# 输出示例会显示 i2c-0、i2c-1 等适配器
# i2c-0 i2c Cadence I2C at e0005000 I2C adapter
- 示例 2
bash
# 扫描 I2C0 总线上挂载了哪些设备
# i2cdetect -y <总线号>
sudo i2cdetect -y 0
3.3 读取寄存器
在 linux 系统下通过 i2cget 命令读取单个寄存器,如下所示。
- 基本语法
bash
# -f 强制读取,默认情况下如果设备在内核有驱动则不支持应用层读取数据
# -y 忽略提示
# -a 通常忽略
# I2CBUS I2C 总线,取值为 0 、 1 、 2 等整数
# CHIP-ADDRESS 设备地址
# DATA-ADDRESS 寄存器地址
# MODE b 写入寄存器地址后直接重启总线切换为读模式读取数据,中间没有停止信号,c 相对与 b ,在产生重启信号
# 前还产生了停止信号,然后在读数据, w 与 c 一样,只不过一次性读取 2Byte
i2cget [-f] [-y] [-a] I2CBUS CHIP-ADDRESS [DATA-ADDRESS [MODE]]
- 示例
bash
# 读取 MPU6050 的 `WHO_AM_I` 寄存器 `0x75`
sudo i2cget -f -y 0 0x68 0x75
# 正常通常返回 0x68
3.4 写入寄存器
在 linux 系统下通过 i2cset 命令写入单个寄存器,如下所示。
- 基本语法
bash
# -f 强制写入,默认情况下如果设备在内核有驱动则不支持应用层写入数据
# -y 忽略提示
# -m MASK 通常忽略
# -r 通常忽略
# -a 通常忽略
# I2CBUS I2C 总线,取值为 0 、 1 、 2 等整数
# CHIP-ADDRESS 设备地址
# DATA-ADDRESS 寄存器地址
# VALUE 写入的数据
i2cset [-f] [-y] [-m MASK] [-r] [-a] I2CBUS CHIP-ADDRESS DATA-ADDRESS [VALUE]
- 示例
bash
# 向 MPU6050 寄存器写入数据
sudo i2cset -f -y 0 0x68 0x6B 0x00
# 将 `PWR_MGMT_1` 写为 `0x00`,用于唤醒 MPU6050
排查技巧:扫描不到设备时,优先检查供电、GND、SCL/SDA 是否接反、上拉电阻、设备地址和 I2C 设备树状态。
四、I2C 应用层开发实例
4.1 标准编程流程
Linux 用户层 I2C 编程的核心是围绕 i2c-dev 驱动暴露的 /dev/i2c-N 字符设备节点来操作总线。整体流程可以清晰地分为以下几步。
(一)确认并打开设备节点
首先需确保内核已加载 i2c-dev 模块,并能在 /dev 下看到对应的总线节点(如 /dev/i2c-0),之后以读写方式打开它,如下所示。
c
int fd = open("/dev/i2c-0", O_RDWR);
if (fd < 0) {
perror("Failed to open i2c bus");
// 错误处理
}
(二)设置从机地址
在与具体从设备通信前,通常需要先告诉驱动你要和哪个地址的设备对话。这通过 ioctl 的 I2C_SLAVE 或 I2C_SLAVE_FORCE 命令完成,如下所示。
c
if (ioctl(fd, I2C_SLAVE, slave_addr) < 0) {
// 错误处理
}
注意 :这个设置是一次性 的。之后如果通过
read/write进行普通传输,驱动会一直使用这个地址,直到你再次调用ioctl更改它。
(三)数据传输(三种方式)
I2C 数据传输方式的选择取决于具体项目的需求和设备协议,如下所示。
- 方式一:
read/write(简单原始传输)
适用于最简单的时序。先设置好从机地址,再直接调用read或write函数。例如,要读取一个寄存器,通常是先write寄存器地址,再read数据。这种方式需要分两次系统调用完成,在这两次调用之间,总线控制权可能会被其他进程抢占。 - 方式二:
I2C_RDWR(复合/原子传输,推荐)
这是最灵活、最推荐的方式。通过调用ioctl(fd, I2C_RDWR, &rdwr_data)函数提交一个struct i2c_msg数组 ,可以在一次系统调用中完成 "先写后读" 等多个时序步骤,中间不会释放总线,对时序敏感的设备更安全。 - 方式三:SMBus 协议
这是一套基于 I2C 的标准化协议,兼容性更好,适合许多标准传感器。SMBus = I2C 的物理层 + 标准化的命令/数据帧格式 + 更严格的时序与可靠性约束。通过调用ioctl(fd, I2C_SMBUS, &args)函数使用,它规定了command + length + data的固定帧格式、必须 ACK、35ms 超时和可选 PEC;
(四)关闭设备
通信结束后,调用 close(fd) 函数关闭文件描述符,释放资源。
4.2 通信驱动示例
(一)资源管理
Linux 用户层操作 I2C 依赖内核的 i2c-dev 驱动。它把每个 I2C 适配器(总线)注册成一个字符设备,如下所示。
text
/dev/i2c-0 次设备号 0
/dev/i2c-1 次设备号 1
...
- 设备描述符管理
c
#define I2C_DEV_MAX_NUM 5
struct i2c_dev_info {
int major;
int fd;
pthread_mutex_t i2c_dev_mutex;
};
static struct i2c_dev_info g_i2c_dev_info[I2C_DEV_MAX_NUM] = {
{ .major = 0, .fd = -1, .i2c_dev_mutex = PTHREAD_MUTEX_INITIALIZER },
{ .major = 1, .fd = -1, .i2c_dev_mutex = PTHREAD_MUTEX_INITIALIZER },
{ .major = 2, .fd = -1, .i2c_dev_mutex = PTHREAD_MUTEX_INITIALIZER },
{ .major = 3, .fd = -1, .i2c_dev_mutex = PTHREAD_MUTEX_INITIALIZER },
{ .major = 4, .fd = -1, .i2c_dev_mutex = PTHREAD_MUTEX_INITIALIZER },
};
/* 保护 open/close 对 fd 状态的修改 */
static pthread_mutex_t g_i2c_open_mutex = PTHREAD_MUTEX_INITIALIZER;
static int i2c_index_valid(enum I2C_INDEX index)
{
return (unsigned)index < I2C_DEV_MAX_NUM;
}
- 打开设备
c
int i2c_open(enum I2C_INDEX index)
{
int fd;
char dev_path[DRIVER_PATH_MAX] = {0};
int ret = 0;
if (!i2c_index_valid(index))
return ERR_INVALID_PARA;
pthread_mutex_lock(&g_i2c_open_mutex);
if (g_i2c_dev_info[index].fd != -1) {
pthread_mutex_unlock(&g_i2c_open_mutex);
return 0;
}
// major 在这里实际是总线编号,对应 /dev/i2c-N 的 N
snprintf(dev_path, sizeof(dev_path), "/dev/i2c-%d", g_i2c_dev_info[index].major);
fd = open(dev_path, O_RDWR);
if (fd < 0) {
Ru_Log_Error("can't open i2c dev %s: %s", dev_path, strerror(errno));
ret = DRV_ERR_OPEN_FAILED;
goto out;
}
g_i2c_dev_info[index].fd = fd;
out:
pthread_mutex_unlock(&g_i2c_open_mutex);
return ret;
}
- 关闭设备
c
int i2c_close(enum I2C_INDEX index)
{
int fd;
if (!i2c_index_valid(index))
return ERR_INVALID_PARA;
pthread_mutex_lock(&g_i2c_open_mutex);
/* 先拿设备锁,确保没有正在进行的传输 */
pthread_mutex_lock(&g_i2c_dev_info[index].i2c_dev_mutex);
fd = g_i2c_dev_info[index].fd;
g_i2c_dev_info[index].fd = -1;
pthread_mutex_unlock(&g_i2c_dev_info[index].i2c_dev_mutex);
pthread_mutex_unlock(&g_i2c_open_mutex);
if (fd != -1)
close(fd);
return 0;
}
(二)设备加/解锁
- 加锁
c
/*
* 获取某条总线的设备锁,并把 fd 返回给调用者。
* 成功返回 0;失败返回 ERR_INVALID_PARA。
* 调用者必须在使用完 fd 后调用 i2c_unlock(index)。
*/
static int i2c_lock(enum I2C_INDEX index, int *fd)
{
if (!i2c_index_valid(index))
return ERR_INVALID_PARA;
pthread_mutex_lock(&g_i2c_dev_info[index].i2c_dev_mutex);
if (g_i2c_dev_info[index].fd == -1) {
pthread_mutex_unlock(&g_i2c_dev_info[index].i2c_dev_mutex);
return ERR_INVALID_PARA;
}
*fd = g_i2c_dev_info[index].fd;
return 0;
}
- 解锁
c
static void i2c_unlock(enum I2C_INDEX index)
{
pthread_mutex_unlock(&g_i2c_dev_info[index].i2c_dev_mutex);
}
(三)数据传输
- 方式一:
read/write
c
int i2c_read(enum I2C_INDEX index, U16 slave_addr, U8 *recv_data, U32 recv_len)
{
int fd;
int ret;
ssize_t n;
if (recv_len > 0 && recv_data == NULL)
return ERR_INVALID_PARA;
ret = i2c_lock(index, &fd);
if (ret != 0)
return ret;
ret = ioctl(fd, I2C_SLAVE_FORCE, slave_addr);
if (ret < 0) {
ret = -errno;
i2c_unlock(index);
return ret;
}
n = read(fd, recv_data, recv_len);
if (n < 0) {
ret = -errno;
i2c_unlock(index);
return ret;
}
if ((U32)n != recv_len) {
i2c_unlock(index);
return -EIO;
}
i2c_unlock(index);
return 0;
}
- 方式二:
I2C_RDWR
c
/*
* 核心传输接口:一次 I2C_RDWR 提交 "写 + 读"。
* - write_len == 0:只读
* - read_len == 0:只写
* - 两者都非 0:先写后读,内核一次 i2c_transfer 完成,原子性最好
* 成功返回 0,失败返回负错误码(-errno)或 ERR_INVALID_PARA。
*/
int i2c_rdwr_xfer(enum I2C_INDEX index, U16 slave_addr, U8 *write_buf, U32 write_len,
U8 *read_buf, U32 read_len)
{
int fd;
int ret;
int nmsgs = 0;
struct i2c_msg msgs[2];
struct i2c_rdwr_ioctl_data rdwr;
if (slave_addr > 0x3ff)
return ERR_INVALID_PARA;
if (write_len > 0 && write_buf == NULL)
return ERR_INVALID_PARA;
if (read_len > 0 && read_buf == NULL)
return ERR_INVALID_PARA;
if (write_len > UINT16_MAX || read_len > UINT16_MAX)
return ERR_INVALID_PARA;
ret = i2c_lock(index, &fd);
if (ret != 0)
return ret;
if (write_len > 0) {
msgs[nmsgs].addr = slave_addr;
msgs[nmsgs].flags = 0;
msgs[nmsgs].len = (__u16)write_len;
msgs[nmsgs].buf = write_buf;
nmsgs++;
}
if (read_len > 0) {
msgs[nmsgs].addr = slave_addr;
msgs[nmsgs].flags = I2C_M_RD;
msgs[nmsgs].len = (__u16)read_len;
msgs[nmsgs].buf = read_buf;
nmsgs++;
}
if (nmsgs == 0) {
i2c_unlock(index);
return 0;
}
rdwr.msgs = msgs;
rdwr.nmsgs = nmsgs;
ret = ioctl(fd, I2C_RDWR, &rdwr);
i2c_unlock(index);
if (ret < 0)
return -errno;
if (ret != nmsgs)
return -EIO;
return 0;
}
/*
* 读寄存器:i2c_write_read 的薄封装。
* 不再调用 I2C_SLAVE_FORCE,I2C_RDWR 使用 msgs[].addr。
* 成功返回 0,失败返回负错误码。
*/
int i2c_msg_read(enum I2C_INDEX index, U8 dev_addr, U8 reg_addr, U8 *recv_data, int length)
{
U8 reg = reg_addr;
if (length <= 0 || recv_data == NULL)
return ERR_INVALID_PARA;
return i2c_rdwr_xfer(index, dev_addr, ®, 1, recv_data, (U32)length);
}
- 方式三:SMBus 协议
c
int i2c_smbus_access(int fd, char read_write, U8 addr,
int size, union i2c_smbus_data *data)
{
struct i2c_smbus_ioctl_data args;
int err;
args.read_write = read_write;
args.command = addr;
args.size = size;
args.data = data;
err = ioctl(fd, I2C_SMBUS, &args);
if (err == -1)
return -errno;
return 0;
}
int i2c_smbus_read(enum I2C_INDEX index, U8 dev_addr, U8 reg_addr, U8 *recv_data, int length)
{
int fd;
int ret;
int i;
union i2c_smbus_data data;
if (length <= 0 || recv_data == NULL)
return ERR_INVALID_PARA;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
ret = i2c_lock(index, &fd);
if (ret != 0)
return ret;
ret = ioctl(fd, I2C_SLAVE_FORCE, dev_addr);
if (ret < 0) {
ret = -errno;
i2c_unlock(index);
return ret;
}
data.block[0] = (__u8)length;
ret = i2c_smbus_access(fd, I2C_SMBUS_READ, reg_addr,
length == I2C_SMBUS_BLOCK_MAX ?
I2C_SMBUS_I2C_BLOCK_BROKEN : I2C_SMBUS_I2C_BLOCK_DATA,
&data);
if (ret < 0) {
i2c_unlock(index);
return ret;
}
/* 防止设备返回长度超过调用者缓冲区 */
if (data.block[0] > (__u8)length) {
i2c_unlock(index);
return -EIO;
}
for (i = 1; i <= data.block[0]; i++)
recv_data[i - 1] = data.block[i];
ret = (int)data.block[0];
i2c_unlock(index);
return ret;
}
int i2c_smbus_write(enum I2C_INDEX index, U8 dev_addr, U8 reg_addr, U8 *values, int length)
{
int fd;
int ret;
int i;
union i2c_smbus_data data;
if (length <= 0 || values == NULL)
return ERR_INVALID_PARA;
if (length > I2C_SMBUS_BLOCK_MAX)
length = I2C_SMBUS_BLOCK_MAX;
for (i = 1; i <= length; i++)
data.block[i] = values[i - 1];
data.block[0] = (__u8)length;
ret = i2c_lock(index, &fd);
if (ret != 0)
return ret;
ret = ioctl(fd, I2C_SLAVE_FORCE, dev_addr);
if (ret < 0) {
ret = -errno;
i2c_unlock(index);
return ret;
}
ret = i2c_smbus_access(fd, I2C_SMBUS_WRITE, reg_addr,
length == I2C_SMBUS_BLOCK_MAX ?
I2C_SMBUS_I2C_BLOCK_BROKEN : I2C_SMBUS_I2C_BLOCK_DATA,
&data);
i2c_unlock(index);
if (ret < 0)
return ret;
return length;
}
4.3 理解通信原理
(一)I2C 协议的最小单元
一次 I2C 传输在物理层由主机(SoC 的 I2C 控制器)产生 SCL 时钟,SDA 上出现以下内容。
text
START | 7 位地址 + R/W | ACK | data0 | ACK | data1 | ACK | ... | STOP
- START:SCL 高时 SDA 由高变低。
- 地址帧 :7 位从机地址左移 1 位,最低位是 R/W:
0写,1读。 - ACK:每 8 位后,接收方把 SDA 拉低一个时钟,表示 "收到"。
- NACK:接收方不拉低,通常表示"无此设备"或"不再需要数据"。
- STOP:SCL 高时 SDA 由低变高。
- Repeated START:不发 STOP,直接再发 START,用于 "写地址 → 读数据" 不释放总线。
所有 i2c_msg 最终都会被内核翻译成上面这些帧。
(二)I2C 的总线时序
I2C 的总线时序只发生在内核调用适配器 master_xfer() 函数的时候,如下所示。
open("/dev/i2c-N"):只建立 fd 到内核i2c_adapter的通道,不产生总线波形。ioctl(fd, I2C_SLAVE_FORCE, addr):只修改client->addr这个软件状态,不产生 START/地址/ACK。read()/write():各提交 1 个i2c_msg,产生一次单消息传输。ioctl(fd, I2C_RDWR, &rdwr):提交多个i2c_msg,内核调用一次i2c_transfer()函数完成,消息之间通常用 repeated START,适合 "写寄存器地址再读数据"。ioctl(fd, I2C_SMBUS, &args):内核把 SMBus 协议翻译成一组i2c_msg,最终仍调用i2c_transfer()函数。
构造
i2c_msg→ 内核i2c_transfer()→ 适配器master_xfer()→ SCL/SDA 上出现 START、地址、ACK、数据、STOP。
(1)用户层调用 open("/dev/i2c-N", O_RDWR) 函数,内核 i2cdev_open() 函数主要做以下操作。
- 根据次设备号找到
struct i2c_adapter *adap; - 分配
struct i2c_client *client; client->adapter = adap;- 把
client挂到file->private_data。
#mermaid-svg-DAOugw8Io8TeWEds{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-DAOugw8Io8TeWEds .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-DAOugw8Io8TeWEds .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-DAOugw8Io8TeWEds .error-icon{fill:#552222;}#mermaid-svg-DAOugw8Io8TeWEds .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-DAOugw8Io8TeWEds .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-DAOugw8Io8TeWEds .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-DAOugw8Io8TeWEds .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-DAOugw8Io8TeWEds .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-DAOugw8Io8TeWEds .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-DAOugw8Io8TeWEds .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-DAOugw8Io8TeWEds .marker{fill:#333333;stroke:#333333;}#mermaid-svg-DAOugw8Io8TeWEds .marker.cross{stroke:#333333;}#mermaid-svg-DAOugw8Io8TeWEds svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-DAOugw8Io8TeWEds p{margin:0;}#mermaid-svg-DAOugw8Io8TeWEds .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-DAOugw8Io8TeWEds .cluster-label text{fill:#333;}#mermaid-svg-DAOugw8Io8TeWEds .cluster-label span{color:#333;}#mermaid-svg-DAOugw8Io8TeWEds .cluster-label span p{background-color:transparent;}#mermaid-svg-DAOugw8Io8TeWEds .label text,#mermaid-svg-DAOugw8Io8TeWEds span{fill:#333;color:#333;}#mermaid-svg-DAOugw8Io8TeWEds .node rect,#mermaid-svg-DAOugw8Io8TeWEds .node circle,#mermaid-svg-DAOugw8Io8TeWEds .node ellipse,#mermaid-svg-DAOugw8Io8TeWEds .node polygon,#mermaid-svg-DAOugw8Io8TeWEds .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-DAOugw8Io8TeWEds .rough-node .label text,#mermaid-svg-DAOugw8Io8TeWEds .node .label text,#mermaid-svg-DAOugw8Io8TeWEds .image-shape .label,#mermaid-svg-DAOugw8Io8TeWEds .icon-shape .label{text-anchor:middle;}#mermaid-svg-DAOugw8Io8TeWEds .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-DAOugw8Io8TeWEds .rough-node .label,#mermaid-svg-DAOugw8Io8TeWEds .node .label,#mermaid-svg-DAOugw8Io8TeWEds .image-shape .label,#mermaid-svg-DAOugw8Io8TeWEds .icon-shape .label{text-align:center;}#mermaid-svg-DAOugw8Io8TeWEds .node.clickable{cursor:pointer;}#mermaid-svg-DAOugw8Io8TeWEds .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-DAOugw8Io8TeWEds .arrowheadPath{fill:#333333;}#mermaid-svg-DAOugw8Io8TeWEds .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-DAOugw8Io8TeWEds .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-DAOugw8Io8TeWEds .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DAOugw8Io8TeWEds .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-DAOugw8Io8TeWEds .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DAOugw8Io8TeWEds .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-DAOugw8Io8TeWEds .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-DAOugw8Io8TeWEds .cluster text{fill:#333;}#mermaid-svg-DAOugw8Io8TeWEds .cluster span{color:#333;}#mermaid-svg-DAOugw8Io8TeWEds div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-DAOugw8Io8TeWEds .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-DAOugw8Io8TeWEds rect.text{fill:none;stroke-width:0;}#mermaid-svg-DAOugw8Io8TeWEds .icon-shape,#mermaid-svg-DAOugw8Io8TeWEds .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-DAOugw8Io8TeWEds .icon-shape p,#mermaid-svg-DAOugw8Io8TeWEds .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-DAOugw8Io8TeWEds .icon-shape .label rect,#mermaid-svg-DAOugw8Io8TeWEds .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-DAOugw8Io8TeWEds .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-DAOugw8Io8TeWEds .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-DAOugw8Io8TeWEds :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} open(/dev/i2c-N)
i2cdev_open
找到 i2c_adapter
分配 i2c_client
file->private_data = client
无总线波形
此时 client->addr 还没有实际意义,SDA/SCL 上没有任何波形。
(2)用户层调用 ioctl(fd, I2C_SLAVE_FORCE, slave_addr) 函数,I2C_SLAVE_FORCE 只改软件状态,内核中它只设置当前 fd 对应 i2c_client 的默认从地址,如下所示。
c
case I2C_SLAVE_FORCE:
client->addr = arg;
return 0;
- 不产生 START;
- 不发地址帧;
- 不产生 ACK;
- 对后续
read()/write()函数有效; - 对
I2C_RDWR无效 ,因为I2C_RDWR使用每个i2c_msg.addr。
#mermaid-svg-lj2Sn76AwuY0S2Ts{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-lj2Sn76AwuY0S2Ts .error-icon{fill:#552222;}#mermaid-svg-lj2Sn76AwuY0S2Ts .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-lj2Sn76AwuY0S2Ts .marker{fill:#333333;stroke:#333333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .marker.cross{stroke:#333333;}#mermaid-svg-lj2Sn76AwuY0S2Ts svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-lj2Sn76AwuY0S2Ts p{margin:0;}#mermaid-svg-lj2Sn76AwuY0S2Ts .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster-label text{fill:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster-label span{color:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster-label span p{background-color:transparent;}#mermaid-svg-lj2Sn76AwuY0S2Ts .label text,#mermaid-svg-lj2Sn76AwuY0S2Ts span{fill:#333;color:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .node rect,#mermaid-svg-lj2Sn76AwuY0S2Ts .node circle,#mermaid-svg-lj2Sn76AwuY0S2Ts .node ellipse,#mermaid-svg-lj2Sn76AwuY0S2Ts .node polygon,#mermaid-svg-lj2Sn76AwuY0S2Ts .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .rough-node .label text,#mermaid-svg-lj2Sn76AwuY0S2Ts .node .label text,#mermaid-svg-lj2Sn76AwuY0S2Ts .image-shape .label,#mermaid-svg-lj2Sn76AwuY0S2Ts .icon-shape .label{text-anchor:middle;}#mermaid-svg-lj2Sn76AwuY0S2Ts .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .rough-node .label,#mermaid-svg-lj2Sn76AwuY0S2Ts .node .label,#mermaid-svg-lj2Sn76AwuY0S2Ts .image-shape .label,#mermaid-svg-lj2Sn76AwuY0S2Ts .icon-shape .label{text-align:center;}#mermaid-svg-lj2Sn76AwuY0S2Ts .node.clickable{cursor:pointer;}#mermaid-svg-lj2Sn76AwuY0S2Ts .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .arrowheadPath{fill:#333333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lj2Sn76AwuY0S2Ts .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-lj2Sn76AwuY0S2Ts .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lj2Sn76AwuY0S2Ts .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster text{fill:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts .cluster span{color:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-lj2Sn76AwuY0S2Ts .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-lj2Sn76AwuY0S2Ts rect.text{fill:none;stroke-width:0;}#mermaid-svg-lj2Sn76AwuY0S2Ts .icon-shape,#mermaid-svg-lj2Sn76AwuY0S2Ts .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-lj2Sn76AwuY0S2Ts .icon-shape p,#mermaid-svg-lj2Sn76AwuY0S2Ts .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-lj2Sn76AwuY0S2Ts .icon-shape .label rect,#mermaid-svg-lj2Sn76AwuY0S2Ts .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-lj2Sn76AwuY0S2Ts .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-lj2Sn76AwuY0S2Ts .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-lj2Sn76AwuY0S2Ts :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ioctl(fd, I2C_SLAVE_FORCE, addr)
client->addr = addr
仅修改软件状态
无 START / 地址 / ACK
(3)用户层调用 read() 函数,内核 i2cdev_read() 函数调用 i2c_master_recv() 函数构造 i2c_msg,如下所示。
c
struct i2c_msg msg = {
.addr = client->addr,
.flags = I2C_M_RD,
.len = recv_len,
.buf = recv_data,
};
i2c_transfer(client->adapter, &msg, 1);
- 波形
text
START | addr<<1 | 1(R) | ACK | data0 | ACK | data1 | ACK | ... | NACK | STOP
主机发 START 后先发 addr<<1|1(7 位从地址加读位),从机回 ACK 表示地址匹配并准备发送数据;随后从机逐个驱动 data0、data1......到 SDA,主机每收到一个字节就回 ACK 表示 "继续发",... 表示按读取长度重复 "从机发数据 + 主机回 ACK";当主机收到最后一个字节后回 NACK 表示 "不要再发了",然后发 STOP 释放总线。
- 时序图
从设备 I2C 适配器 内核 i2c-dev 用户层 从设备 I2C 适配器 内核 i2c-dev 用户层 #mermaid-svg-NfLCh9nwIyAO1kBQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NfLCh9nwIyAO1kBQ .error-icon{fill:#552222;}#mermaid-svg-NfLCh9nwIyAO1kBQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NfLCh9nwIyAO1kBQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NfLCh9nwIyAO1kBQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NfLCh9nwIyAO1kBQ .marker.cross{stroke:#333333;}#mermaid-svg-NfLCh9nwIyAO1kBQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NfLCh9nwIyAO1kBQ p{margin:0;}#mermaid-svg-NfLCh9nwIyAO1kBQ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfLCh9nwIyAO1kBQ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NfLCh9nwIyAO1kBQ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-NfLCh9nwIyAO1kBQ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-NfLCh9nwIyAO1kBQ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-NfLCh9nwIyAO1kBQ .sequenceNumber{fill:white;}#mermaid-svg-NfLCh9nwIyAO1kBQ #sequencenumber{fill:#333;}#mermaid-svg-NfLCh9nwIyAO1kBQ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-NfLCh9nwIyAO1kBQ .messageText{fill:#333;stroke:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfLCh9nwIyAO1kBQ .labelText,#mermaid-svg-NfLCh9nwIyAO1kBQ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .loopText,#mermaid-svg-NfLCh9nwIyAO1kBQ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-NfLCh9nwIyAO1kBQ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-NfLCh9nwIyAO1kBQ .noteText,#mermaid-svg-NfLCh9nwIyAO1kBQ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-NfLCh9nwIyAO1kBQ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfLCh9nwIyAO1kBQ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfLCh9nwIyAO1kBQ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-NfLCh9nwIyAO1kBQ .actorPopupMenu{position:absolute;}#mermaid-svg-NfLCh9nwIyAO1kBQ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-NfLCh9nwIyAO1kBQ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-NfLCh9nwIyAO1kBQ .actor-man circle,#mermaid-svg-NfLCh9nwIyAO1kBQ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-NfLCh9nwIyAO1kBQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} read(fd, buf, len) i2c_master_recv(client, buf, len) i2c_transfer(adap, msg1, 1) START + addr + R ACK 读数据 data + ACK/NACK STOP 返回读字节数或错误 n 或 -errno
如果地址发出去没有 ACK,用户层可能收到 -ENXIO 或 -EREMOTEIO。
(4)用户层调用 write() 函数,内核 i2cdev_write() 函数调用 i2c_master_send() 函数构造 i2c_msg,如下所示。
c
struct i2c_msg msg = {
.addr = client->addr,
.flags = 0,
.len = send_len,
.buf = send_data,
};
i2c_transfer(client->adapter, &msg, 1);
- 波形
text
START | addr<<1 | 0(W) | ACK | data0 | ACK | data1 | ACK | ... | STOP
主机发 START 后先发 addr<<1|0(7 位从地址加写位),从机回 ACK 表示地址匹配;随后主机连续发送 data0、data1......每个字节后从机都回 ACK 表示收到,... 表示按写入长度重复 "数据字节 + ACK";所有数据发完后主机发 STOP 释放总线。
- 时序图
从设备 I2C 适配器 内核 i2c-dev 用户层 从设备 I2C 适配器 内核 i2c-dev 用户层 #mermaid-svg-nUMINRHjoa2waFiZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-nUMINRHjoa2waFiZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-nUMINRHjoa2waFiZ .error-icon{fill:#552222;}#mermaid-svg-nUMINRHjoa2waFiZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-nUMINRHjoa2waFiZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-nUMINRHjoa2waFiZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-nUMINRHjoa2waFiZ .marker.cross{stroke:#333333;}#mermaid-svg-nUMINRHjoa2waFiZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-nUMINRHjoa2waFiZ p{margin:0;}#mermaid-svg-nUMINRHjoa2waFiZ .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nUMINRHjoa2waFiZ text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-nUMINRHjoa2waFiZ .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nUMINRHjoa2waFiZ .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-nUMINRHjoa2waFiZ .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-nUMINRHjoa2waFiZ .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-nUMINRHjoa2waFiZ #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-nUMINRHjoa2waFiZ .sequenceNumber{fill:white;}#mermaid-svg-nUMINRHjoa2waFiZ #sequencenumber{fill:#333;}#mermaid-svg-nUMINRHjoa2waFiZ #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-nUMINRHjoa2waFiZ .messageText{fill:#333;stroke:none;}#mermaid-svg-nUMINRHjoa2waFiZ .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nUMINRHjoa2waFiZ .labelText,#mermaid-svg-nUMINRHjoa2waFiZ .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-nUMINRHjoa2waFiZ .loopText,#mermaid-svg-nUMINRHjoa2waFiZ .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-nUMINRHjoa2waFiZ .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-nUMINRHjoa2waFiZ .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-nUMINRHjoa2waFiZ .noteText,#mermaid-svg-nUMINRHjoa2waFiZ .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-nUMINRHjoa2waFiZ .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nUMINRHjoa2waFiZ .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nUMINRHjoa2waFiZ .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-nUMINRHjoa2waFiZ .actorPopupMenu{position:absolute;}#mermaid-svg-nUMINRHjoa2waFiZ .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-nUMINRHjoa2waFiZ .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-nUMINRHjoa2waFiZ .actor-man circle,#mermaid-svg-nUMINRHjoa2waFiZ line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-nUMINRHjoa2waFiZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} write(fd, buf, len) i2c_master_send(client, buf, len) i2c_transfer(adap, msg1, 1) START + addr + W ACK data0 ACK data1 ACK STOP 返回写字节数或错误 n 或 -errno
(5)I2C_RDWR 写寄存器 + 读数据是 I2C 最常用的复合事务:先写寄存器地址,再用 repeated START 切到读,取回数据 。因为一个 i2c_msg 只能表示一个方向(flags = 0 是写,flags = I2C_M_RD 是读),所以需要两个 i2c_msg,如下所示。
- 波形
text
START | addr+W | ACK | reg | ACK | repeated START | addr+R | ACK | data | NACK | STOP
主机先发 START 和
addr+W(从地址加写位),从机回 ACK 表示地址匹配;接着主机发reg(要读的寄存器地址),从机回 ACK;然后主机不发 STOP,而是发 repeated START 把方向切到读,再发addr+R(同一从地址加读位),从机回 ACK 后返回一个数据字节data;主机收到该字节后回 NACK,表示 "这是最后一个字节,不要再发了",最后发 STOP 释放总线。
- 结构体
c
struct i2c_msg msgs[2] = {
{ .addr = addr, .flags = 0, .len = 1, .buf = ® },
{ .addr = addr, .flags = I2C_M_RD, .len = length, .buf = recv },
};
read/write与I2C_RDWR的差异
read/write 依赖 I2C_SLAVE_FORCE 设置的 client->addr,每次只能发一个方向、一个 i2c_msg,写寄存器再读要两次系统调用、中间可能插入其他传输,如下所示。
#mermaid-svg-7pjqhG2IaXUL2GTf{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7pjqhG2IaXUL2GTf .error-icon{fill:#552222;}#mermaid-svg-7pjqhG2IaXUL2GTf .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7pjqhG2IaXUL2GTf .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7pjqhG2IaXUL2GTf .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7pjqhG2IaXUL2GTf .marker.cross{stroke:#333333;}#mermaid-svg-7pjqhG2IaXUL2GTf svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7pjqhG2IaXUL2GTf p{margin:0;}#mermaid-svg-7pjqhG2IaXUL2GTf .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster-label text{fill:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster-label span{color:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster-label span p{background-color:transparent;}#mermaid-svg-7pjqhG2IaXUL2GTf .label text,#mermaid-svg-7pjqhG2IaXUL2GTf span{fill:#333;color:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf .node rect,#mermaid-svg-7pjqhG2IaXUL2GTf .node circle,#mermaid-svg-7pjqhG2IaXUL2GTf .node ellipse,#mermaid-svg-7pjqhG2IaXUL2GTf .node polygon,#mermaid-svg-7pjqhG2IaXUL2GTf .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7pjqhG2IaXUL2GTf .rough-node .label text,#mermaid-svg-7pjqhG2IaXUL2GTf .node .label text,#mermaid-svg-7pjqhG2IaXUL2GTf .image-shape .label,#mermaid-svg-7pjqhG2IaXUL2GTf .icon-shape .label{text-anchor:middle;}#mermaid-svg-7pjqhG2IaXUL2GTf .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7pjqhG2IaXUL2GTf .rough-node .label,#mermaid-svg-7pjqhG2IaXUL2GTf .node .label,#mermaid-svg-7pjqhG2IaXUL2GTf .image-shape .label,#mermaid-svg-7pjqhG2IaXUL2GTf .icon-shape .label{text-align:center;}#mermaid-svg-7pjqhG2IaXUL2GTf .node.clickable{cursor:pointer;}#mermaid-svg-7pjqhG2IaXUL2GTf .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7pjqhG2IaXUL2GTf .arrowheadPath{fill:#333333;}#mermaid-svg-7pjqhG2IaXUL2GTf .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7pjqhG2IaXUL2GTf .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7pjqhG2IaXUL2GTf .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7pjqhG2IaXUL2GTf .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7pjqhG2IaXUL2GTf .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7pjqhG2IaXUL2GTf .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster text{fill:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf .cluster span{color:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7pjqhG2IaXUL2GTf .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7pjqhG2IaXUL2GTf rect.text{fill:none;stroke-width:0;}#mermaid-svg-7pjqhG2IaXUL2GTf .icon-shape,#mermaid-svg-7pjqhG2IaXUL2GTf .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7pjqhG2IaXUL2GTf .icon-shape p,#mermaid-svg-7pjqhG2IaXUL2GTf .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7pjqhG2IaXUL2GTf .icon-shape .label rect,#mermaid-svg-7pjqhG2IaXUL2GTf .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7pjqhG2IaXUL2GTf .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7pjqhG2IaXUL2GTf .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7pjqhG2IaXUL2GTf :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} I2C_SLAVE_FORCE + read/write
ioctl 设置 client->addr
read 或 write
一次 i2c_transfer 只有 1 个 msg
写寄存器再读需两次系统调用
两次之间总线锁释放
I2C_RDWR 每个 i2c_msg 自带地址和方向,一次 ioctl 提交多个 msg、内核一次 i2c_transfer 用 repeated START 完成,因而能做原子的 "写寄存器 + 读数据" 复合事务,如下所示。
#mermaid-svg-hl4LbwEMAxQL0eeW{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hl4LbwEMAxQL0eeW .error-icon{fill:#552222;}#mermaid-svg-hl4LbwEMAxQL0eeW .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hl4LbwEMAxQL0eeW .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hl4LbwEMAxQL0eeW .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hl4LbwEMAxQL0eeW .marker.cross{stroke:#333333;}#mermaid-svg-hl4LbwEMAxQL0eeW svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hl4LbwEMAxQL0eeW p{margin:0;}#mermaid-svg-hl4LbwEMAxQL0eeW .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster-label text{fill:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster-label span{color:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster-label span p{background-color:transparent;}#mermaid-svg-hl4LbwEMAxQL0eeW .label text,#mermaid-svg-hl4LbwEMAxQL0eeW span{fill:#333;color:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW .node rect,#mermaid-svg-hl4LbwEMAxQL0eeW .node circle,#mermaid-svg-hl4LbwEMAxQL0eeW .node ellipse,#mermaid-svg-hl4LbwEMAxQL0eeW .node polygon,#mermaid-svg-hl4LbwEMAxQL0eeW .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hl4LbwEMAxQL0eeW .rough-node .label text,#mermaid-svg-hl4LbwEMAxQL0eeW .node .label text,#mermaid-svg-hl4LbwEMAxQL0eeW .image-shape .label,#mermaid-svg-hl4LbwEMAxQL0eeW .icon-shape .label{text-anchor:middle;}#mermaid-svg-hl4LbwEMAxQL0eeW .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hl4LbwEMAxQL0eeW .rough-node .label,#mermaid-svg-hl4LbwEMAxQL0eeW .node .label,#mermaid-svg-hl4LbwEMAxQL0eeW .image-shape .label,#mermaid-svg-hl4LbwEMAxQL0eeW .icon-shape .label{text-align:center;}#mermaid-svg-hl4LbwEMAxQL0eeW .node.clickable{cursor:pointer;}#mermaid-svg-hl4LbwEMAxQL0eeW .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hl4LbwEMAxQL0eeW .arrowheadPath{fill:#333333;}#mermaid-svg-hl4LbwEMAxQL0eeW .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hl4LbwEMAxQL0eeW .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hl4LbwEMAxQL0eeW .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hl4LbwEMAxQL0eeW .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hl4LbwEMAxQL0eeW .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hl4LbwEMAxQL0eeW .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster text{fill:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW .cluster span{color:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hl4LbwEMAxQL0eeW .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hl4LbwEMAxQL0eeW rect.text{fill:none;stroke-width:0;}#mermaid-svg-hl4LbwEMAxQL0eeW .icon-shape,#mermaid-svg-hl4LbwEMAxQL0eeW .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hl4LbwEMAxQL0eeW .icon-shape p,#mermaid-svg-hl4LbwEMAxQL0eeW .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hl4LbwEMAxQL0eeW .icon-shape .label rect,#mermaid-svg-hl4LbwEMAxQL0eeW .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hl4LbwEMAxQL0eeW .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hl4LbwEMAxQL0eeW .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hl4LbwEMAxQL0eeW :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} I2C_RDWR
每个 i2c_msg 自带 addr
一次 ioctl 提交多个 msg
一次 i2c_transfer 完成
repeated START
写寄存器再读是原子的
- 总结
| 对比项 | I2C_SLAVE_FORCE + read/write |
I2C_RDWR |
|---|---|---|
| 从地址来源 | client->addr |
每个 msg.addr |
| 一次系统调用 | 只能一个方向、一个 msg | 可多个 msg、多方向、多地址 |
| 写寄存器再读 | 两次系统调用,中间可能插入其他传输 | 一次 i2c_transfer,repeated START,原子 |
| 是否需要用户层锁 | 需要保护"设地址 + 读写" | 仍建议锁 fd,但地址不依赖 client->addr |
| 灵活性 | 低 | 高 |
| 推荐场景 | 简单单次读写 | 寄存器读、复杂时序、多消息组合 |
I2C_RDWR的原子传输时序图
从设备 I2C 适配器 内核 i2c-dev 用户层 从设备 I2C 适配器 内核 i2c-dev 用户层 #mermaid-svg-IiU8mMVO8UGiIno1{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IiU8mMVO8UGiIno1 .error-icon{fill:#552222;}#mermaid-svg-IiU8mMVO8UGiIno1 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IiU8mMVO8UGiIno1 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IiU8mMVO8UGiIno1 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IiU8mMVO8UGiIno1 .marker.cross{stroke:#333333;}#mermaid-svg-IiU8mMVO8UGiIno1 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IiU8mMVO8UGiIno1 p{margin:0;}#mermaid-svg-IiU8mMVO8UGiIno1 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IiU8mMVO8UGiIno1 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IiU8mMVO8UGiIno1 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-IiU8mMVO8UGiIno1 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-IiU8mMVO8UGiIno1 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-IiU8mMVO8UGiIno1 .sequenceNumber{fill:white;}#mermaid-svg-IiU8mMVO8UGiIno1 #sequencenumber{fill:#333;}#mermaid-svg-IiU8mMVO8UGiIno1 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-IiU8mMVO8UGiIno1 .messageText{fill:#333;stroke:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IiU8mMVO8UGiIno1 .labelText,#mermaid-svg-IiU8mMVO8UGiIno1 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .loopText,#mermaid-svg-IiU8mMVO8UGiIno1 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-IiU8mMVO8UGiIno1 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-IiU8mMVO8UGiIno1 .noteText,#mermaid-svg-IiU8mMVO8UGiIno1 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-IiU8mMVO8UGiIno1 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IiU8mMVO8UGiIno1 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IiU8mMVO8UGiIno1 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-IiU8mMVO8UGiIno1 .actorPopupMenu{position:absolute;}#mermaid-svg-IiU8mMVO8UGiIno1 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-IiU8mMVO8UGiIno1 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-IiU8mMVO8UGiIno1 .actor-man circle,#mermaid-svg-IiU8mMVO8UGiIno1 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-IiU8mMVO8UGiIno1 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} msgs0: 写 reg msgs1: 读 data alt ret == nmsgs ret \< 0 ioctl(fd, I2C_RDWR, &rdwr) i2cdev_ioctl_rdwr() i2c_transfer(adap, msgs, 2) START + addr + W ACK reg ACK repeated START + addr + R ACK 读数据 data + ACK/NACK STOP 返回完成的 msg 数 成功 -errno
关键点:
I2C_RDWR不依赖client->addr;- 内核用一次
i2c_transfer(adap, msgs, nmsgs); - 两个 msg 之间不发 STOP,而是 repeated START;
- 中间不会被其他 I2C 传输插入,原子性更好。
(8)锁保护的是 "协议事务",需要锁住 I2C_SLAVE_FORCE + read,因为 I2C_SLAVE_FORCE 改的是 client->addr,read 用的是 client->addr,如下所示。
- 用户层加锁
c
pthread_mutex_lock(&g_i2c_dev_info[index].i2c_dev_mutex);
ret = ioctl(fd, I2C_SLAVE_FORCE, slave_addr);
n = read(fd, recv_data, recv_len);
pthread_mutex_unlock(&g_i2c_dev_info[index].i2c_dev_mutex);
- 加锁前时序图
I2C 总线 共享 fd 线程 B 线程 A I2C 总线 共享 fd 线程 B 线程 A #mermaid-svg-W5lPSuotFWrIQkK3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W5lPSuotFWrIQkK3 .error-icon{fill:#552222;}#mermaid-svg-W5lPSuotFWrIQkK3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W5lPSuotFWrIQkK3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W5lPSuotFWrIQkK3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W5lPSuotFWrIQkK3 .marker.cross{stroke:#333333;}#mermaid-svg-W5lPSuotFWrIQkK3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W5lPSuotFWrIQkK3 p{margin:0;}#mermaid-svg-W5lPSuotFWrIQkK3 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-W5lPSuotFWrIQkK3 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-W5lPSuotFWrIQkK3 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-W5lPSuotFWrIQkK3 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-W5lPSuotFWrIQkK3 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-W5lPSuotFWrIQkK3 .sequenceNumber{fill:white;}#mermaid-svg-W5lPSuotFWrIQkK3 #sequencenumber{fill:#333;}#mermaid-svg-W5lPSuotFWrIQkK3 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-W5lPSuotFWrIQkK3 .messageText{fill:#333;stroke:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-W5lPSuotFWrIQkK3 .labelText,#mermaid-svg-W5lPSuotFWrIQkK3 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .loopText,#mermaid-svg-W5lPSuotFWrIQkK3 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-W5lPSuotFWrIQkK3 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-W5lPSuotFWrIQkK3 .noteText,#mermaid-svg-W5lPSuotFWrIQkK3 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-W5lPSuotFWrIQkK3 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-W5lPSuotFWrIQkK3 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-W5lPSuotFWrIQkK3 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-W5lPSuotFWrIQkK3 .actorPopupMenu{position:absolute;}#mermaid-svg-W5lPSuotFWrIQkK3 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-W5lPSuotFWrIQkK3 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-W5lPSuotFWrIQkK3 .actor-man circle,#mermaid-svg-W5lPSuotFWrIQkK3 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-W5lPSuotFWrIQkK3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 没有锁时地址可能被覆盖 设 client->>addr = 0x50 设 client->>addr = 0x51 read() 实际使用 client->>addr = 0x51
- 加锁后时序图
I2C 总线 共享 fd 线程 B 线程 A I2C 总线 共享 fd 线程 B 线程 A #mermaid-svg-8XrYDycztwHh95H5{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8XrYDycztwHh95H5 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8XrYDycztwHh95H5 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8XrYDycztwHh95H5 .error-icon{fill:#552222;}#mermaid-svg-8XrYDycztwHh95H5 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8XrYDycztwHh95H5 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8XrYDycztwHh95H5 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8XrYDycztwHh95H5 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8XrYDycztwHh95H5 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8XrYDycztwHh95H5 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8XrYDycztwHh95H5 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8XrYDycztwHh95H5 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8XrYDycztwHh95H5 .marker.cross{stroke:#333333;}#mermaid-svg-8XrYDycztwHh95H5 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8XrYDycztwHh95H5 p{margin:0;}#mermaid-svg-8XrYDycztwHh95H5 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8XrYDycztwHh95H5 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-8XrYDycztwHh95H5 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8XrYDycztwHh95H5 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-8XrYDycztwHh95H5 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-8XrYDycztwHh95H5 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-8XrYDycztwHh95H5 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-8XrYDycztwHh95H5 .sequenceNumber{fill:white;}#mermaid-svg-8XrYDycztwHh95H5 #sequencenumber{fill:#333;}#mermaid-svg-8XrYDycztwHh95H5 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-8XrYDycztwHh95H5 .messageText{fill:#333;stroke:none;}#mermaid-svg-8XrYDycztwHh95H5 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8XrYDycztwHh95H5 .labelText,#mermaid-svg-8XrYDycztwHh95H5 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-8XrYDycztwHh95H5 .loopText,#mermaid-svg-8XrYDycztwHh95H5 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-8XrYDycztwHh95H5 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-8XrYDycztwHh95H5 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-8XrYDycztwHh95H5 .noteText,#mermaid-svg-8XrYDycztwHh95H5 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-8XrYDycztwHh95H5 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8XrYDycztwHh95H5 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8XrYDycztwHh95H5 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-8XrYDycztwHh95H5 .actorPopupMenu{position:absolute;}#mermaid-svg-8XrYDycztwHh95H5 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-8XrYDycztwHh95H5 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-8XrYDycztwHh95H5 .actor-man circle,#mermaid-svg-8XrYDycztwHh95H5 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-8XrYDycztwHh95H5 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} lock 设 client->>addr = 0x50 read() 使用 0x50 传输 unlock lock 设 client->>addr = 0x51 read() 使用 0x51 传输 unlock
注意:这是进程内锁 ,跨进程的总线互斥由内核
i2c_transfer()函数中的适配器锁保证。
(9)SMBus 块读与写时序与波形,如下所示。
- 读波形
text
START | addr+W | ACK | command(reg) | ACK |
repeated START | addr+R | ACK | length | ACK | data0 | ACK | ... | NACK | STOP
主机先以写方向发
START + addr+W,从机回 ACK 表示地址匹配;主机再发command(reg)(通常是要读的寄存器/命令字节),从机回 ACK;随后主机不发 STOP,而是发 repeated START 把方向切到读,再发addr+R,从机回 ACK 后先返回一个length字节 ,告诉主机后面有多少个数据字节,主机回 ACK 表示继续;接着从机逐个返回data0、data1......,每收到一个字节主机回 ACK,...表示按length重复"数据字节 + 主机 ACK";主机收到最后一个数据字节后回 NACK 表示"不要再发了",最后发 STOP 释放总线。
- 读时序图
从设备 I2C 适配器 内核 i2c-dev 用户层 从设备 I2C 适配器 内核 i2c-dev 用户层 #mermaid-svg-JuABdeYj0nNJAbe9{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-JuABdeYj0nNJAbe9 .error-icon{fill:#552222;}#mermaid-svg-JuABdeYj0nNJAbe9 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-JuABdeYj0nNJAbe9 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-JuABdeYj0nNJAbe9 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-JuABdeYj0nNJAbe9 .marker.cross{stroke:#333333;}#mermaid-svg-JuABdeYj0nNJAbe9 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-JuABdeYj0nNJAbe9 p{margin:0;}#mermaid-svg-JuABdeYj0nNJAbe9 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JuABdeYj0nNJAbe9 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-JuABdeYj0nNJAbe9 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-JuABdeYj0nNJAbe9 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-JuABdeYj0nNJAbe9 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-JuABdeYj0nNJAbe9 .sequenceNumber{fill:white;}#mermaid-svg-JuABdeYj0nNJAbe9 #sequencenumber{fill:#333;}#mermaid-svg-JuABdeYj0nNJAbe9 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-JuABdeYj0nNJAbe9 .messageText{fill:#333;stroke:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JuABdeYj0nNJAbe9 .labelText,#mermaid-svg-JuABdeYj0nNJAbe9 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .loopText,#mermaid-svg-JuABdeYj0nNJAbe9 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-JuABdeYj0nNJAbe9 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-JuABdeYj0nNJAbe9 .noteText,#mermaid-svg-JuABdeYj0nNJAbe9 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-JuABdeYj0nNJAbe9 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JuABdeYj0nNJAbe9 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JuABdeYj0nNJAbe9 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-JuABdeYj0nNJAbe9 .actorPopupMenu{position:absolute;}#mermaid-svg-JuABdeYj0nNJAbe9 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-JuABdeYj0nNJAbe9 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-JuABdeYj0nNJAbe9 .actor-man circle,#mermaid-svg-JuABdeYj0nNJAbe9 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-JuABdeYj0nNJAbe9 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} ioctl(fd, I2C_SMBUS, &args) i2c_smbus_xfer() i2c_transfer(adap, 翻译后的 msgs, n) START + addr + W ACK command(reg) ACK repeated START + addr + R ACK 读 length + data data + ACK/NACK STOP 返回结果 data.block 或 -errno
- 写波形
text
START | addr<<1 | 0(W) | ACK | command(reg) | ACK | length | ACK | data0 | ACK | data1 | ACK | ... | dataN | ACK | STOP
主机发
START后以写方向发addr<<1|0,从机回 ACK 表示地址匹配;主机再发command(reg)(通常是要写的寄存器/命令字节),从机回 ACK;接着主机发length字节,告诉从机后面要写多少个数据字节,从机回 ACK;然后主机连续发送data0、data1......dataN,...表示按length重复"数据字节 + 从机 ACK",每个数据字节后都由从机回 ACK;所有数据发完后主机发STOP释放总线。
- 写时序图
从设备 I2C 适配器 内核 i2c-dev 用户层 从设备 I2C 适配器 内核 i2c-dev 用户层 #mermaid-svg-74zjo0WF87pJw8Eg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-74zjo0WF87pJw8Eg .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-74zjo0WF87pJw8Eg .error-icon{fill:#552222;}#mermaid-svg-74zjo0WF87pJw8Eg .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-74zjo0WF87pJw8Eg .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-74zjo0WF87pJw8Eg .marker{fill:#333333;stroke:#333333;}#mermaid-svg-74zjo0WF87pJw8Eg .marker.cross{stroke:#333333;}#mermaid-svg-74zjo0WF87pJw8Eg svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-74zjo0WF87pJw8Eg p{margin:0;}#mermaid-svg-74zjo0WF87pJw8Eg .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-74zjo0WF87pJw8Eg text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-74zjo0WF87pJw8Eg .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-74zjo0WF87pJw8Eg .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-74zjo0WF87pJw8Eg .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-74zjo0WF87pJw8Eg .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-74zjo0WF87pJw8Eg #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-74zjo0WF87pJw8Eg .sequenceNumber{fill:white;}#mermaid-svg-74zjo0WF87pJw8Eg #sequencenumber{fill:#333;}#mermaid-svg-74zjo0WF87pJw8Eg #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-74zjo0WF87pJw8Eg .messageText{fill:#333;stroke:none;}#mermaid-svg-74zjo0WF87pJw8Eg .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-74zjo0WF87pJw8Eg .labelText,#mermaid-svg-74zjo0WF87pJw8Eg .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-74zjo0WF87pJw8Eg .loopText,#mermaid-svg-74zjo0WF87pJw8Eg .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-74zjo0WF87pJw8Eg .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-74zjo0WF87pJw8Eg .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-74zjo0WF87pJw8Eg .noteText,#mermaid-svg-74zjo0WF87pJw8Eg .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-74zjo0WF87pJw8Eg .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-74zjo0WF87pJw8Eg .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-74zjo0WF87pJw8Eg .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-74zjo0WF87pJw8Eg .actorPopupMenu{position:absolute;}#mermaid-svg-74zjo0WF87pJw8Eg .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-74zjo0WF87pJw8Eg .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-74zjo0WF87pJw8Eg .actor-man circle,#mermaid-svg-74zjo0WF87pJw8Eg line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-74zjo0WF87pJw8Eg :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} command=reg block0=length block1..=data alt 适配器有 smbus_xfer 适配器无 smbus_xfer ioctl(fd, I2C_SMBUS, &args) i2c_smbus_xfer() algo->>smbus_xfer() i2c_smbus_xfer_emulated() i2c_transfer(adap, msg, 1) START + addr + W ACK command(reg) ACK length ACK data0 ACK ... ACK dataN ACK STOP 返回结果 0 或 -errno
(四)ACK/NACK 与错误码的对应
#mermaid-svg-Roa168IHjWrYfkCN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Roa168IHjWrYfkCN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Roa168IHjWrYfkCN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Roa168IHjWrYfkCN .error-icon{fill:#552222;}#mermaid-svg-Roa168IHjWrYfkCN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Roa168IHjWrYfkCN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Roa168IHjWrYfkCN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Roa168IHjWrYfkCN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Roa168IHjWrYfkCN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Roa168IHjWrYfkCN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Roa168IHjWrYfkCN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Roa168IHjWrYfkCN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Roa168IHjWrYfkCN .marker.cross{stroke:#333333;}#mermaid-svg-Roa168IHjWrYfkCN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Roa168IHjWrYfkCN p{margin:0;}#mermaid-svg-Roa168IHjWrYfkCN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Roa168IHjWrYfkCN .cluster-label text{fill:#333;}#mermaid-svg-Roa168IHjWrYfkCN .cluster-label span{color:#333;}#mermaid-svg-Roa168IHjWrYfkCN .cluster-label span p{background-color:transparent;}#mermaid-svg-Roa168IHjWrYfkCN .label text,#mermaid-svg-Roa168IHjWrYfkCN span{fill:#333;color:#333;}#mermaid-svg-Roa168IHjWrYfkCN .node rect,#mermaid-svg-Roa168IHjWrYfkCN .node circle,#mermaid-svg-Roa168IHjWrYfkCN .node ellipse,#mermaid-svg-Roa168IHjWrYfkCN .node polygon,#mermaid-svg-Roa168IHjWrYfkCN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Roa168IHjWrYfkCN .rough-node .label text,#mermaid-svg-Roa168IHjWrYfkCN .node .label text,#mermaid-svg-Roa168IHjWrYfkCN .image-shape .label,#mermaid-svg-Roa168IHjWrYfkCN .icon-shape .label{text-anchor:middle;}#mermaid-svg-Roa168IHjWrYfkCN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Roa168IHjWrYfkCN .rough-node .label,#mermaid-svg-Roa168IHjWrYfkCN .node .label,#mermaid-svg-Roa168IHjWrYfkCN .image-shape .label,#mermaid-svg-Roa168IHjWrYfkCN .icon-shape .label{text-align:center;}#mermaid-svg-Roa168IHjWrYfkCN .node.clickable{cursor:pointer;}#mermaid-svg-Roa168IHjWrYfkCN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Roa168IHjWrYfkCN .arrowheadPath{fill:#333333;}#mermaid-svg-Roa168IHjWrYfkCN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Roa168IHjWrYfkCN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Roa168IHjWrYfkCN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Roa168IHjWrYfkCN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Roa168IHjWrYfkCN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Roa168IHjWrYfkCN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Roa168IHjWrYfkCN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Roa168IHjWrYfkCN .cluster text{fill:#333;}#mermaid-svg-Roa168IHjWrYfkCN .cluster span{color:#333;}#mermaid-svg-Roa168IHjWrYfkCN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Roa168IHjWrYfkCN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Roa168IHjWrYfkCN rect.text{fill:none;stroke-width:0;}#mermaid-svg-Roa168IHjWrYfkCN .icon-shape,#mermaid-svg-Roa168IHjWrYfkCN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Roa168IHjWrYfkCN .icon-shape p,#mermaid-svg-Roa168IHjWrYfkCN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Roa168IHjWrYfkCN .icon-shape .label rect,#mermaid-svg-Roa168IHjWrYfkCN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Roa168IHjWrYfkCN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Roa168IHjWrYfkCN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Roa168IHjWrYfkCN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否
适配器发出地址/数据
从机是否拉低 SDA
ACK,继续传输
NACK
i2c_transfer 返回错误
用户层收到 -ENXIO / -EREMOTEIO / -EIO
| 现象 | 总线含义 | 常见错误码 |
|---|---|---|
| 地址帧后从机拉低 SDA | ACK,设备存在 | 正常 |
| 地址帧后无 ACK | 无此地址设备或设备未上电 | -ENXIO、-EREMOTEIO |
| 数据字节后无 ACK | 从机拒收或总线异常 | -EIO、-EREMOTEIO |
ioctl(I2C_RDWR) 返回 < nmsgs |
部分消息失败 | -EIO 或 -errno |
read/write 返回字节数不符 |
传输不完整 | -EIO |
(三)总结
在 I2C 通信编程中,用户层到总线的整体调用链和关系,如下所示。
- 调用链图
#mermaid-svg-TDzxbmlcUG48VIY2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TDzxbmlcUG48VIY2 .error-icon{fill:#552222;}#mermaid-svg-TDzxbmlcUG48VIY2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TDzxbmlcUG48VIY2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TDzxbmlcUG48VIY2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TDzxbmlcUG48VIY2 .marker.cross{stroke:#333333;}#mermaid-svg-TDzxbmlcUG48VIY2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TDzxbmlcUG48VIY2 p{margin:0;}#mermaid-svg-TDzxbmlcUG48VIY2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster-label text{fill:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster-label span{color:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster-label span p{background-color:transparent;}#mermaid-svg-TDzxbmlcUG48VIY2 .label text,#mermaid-svg-TDzxbmlcUG48VIY2 span{fill:#333;color:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 .node rect,#mermaid-svg-TDzxbmlcUG48VIY2 .node circle,#mermaid-svg-TDzxbmlcUG48VIY2 .node ellipse,#mermaid-svg-TDzxbmlcUG48VIY2 .node polygon,#mermaid-svg-TDzxbmlcUG48VIY2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TDzxbmlcUG48VIY2 .rough-node .label text,#mermaid-svg-TDzxbmlcUG48VIY2 .node .label text,#mermaid-svg-TDzxbmlcUG48VIY2 .image-shape .label,#mermaid-svg-TDzxbmlcUG48VIY2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-TDzxbmlcUG48VIY2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TDzxbmlcUG48VIY2 .rough-node .label,#mermaid-svg-TDzxbmlcUG48VIY2 .node .label,#mermaid-svg-TDzxbmlcUG48VIY2 .image-shape .label,#mermaid-svg-TDzxbmlcUG48VIY2 .icon-shape .label{text-align:center;}#mermaid-svg-TDzxbmlcUG48VIY2 .node.clickable{cursor:pointer;}#mermaid-svg-TDzxbmlcUG48VIY2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TDzxbmlcUG48VIY2 .arrowheadPath{fill:#333333;}#mermaid-svg-TDzxbmlcUG48VIY2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TDzxbmlcUG48VIY2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TDzxbmlcUG48VIY2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TDzxbmlcUG48VIY2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TDzxbmlcUG48VIY2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TDzxbmlcUG48VIY2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster text{fill:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 .cluster span{color:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TDzxbmlcUG48VIY2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TDzxbmlcUG48VIY2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-TDzxbmlcUG48VIY2 .icon-shape,#mermaid-svg-TDzxbmlcUG48VIY2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TDzxbmlcUG48VIY2 .icon-shape p,#mermaid-svg-TDzxbmlcUG48VIY2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TDzxbmlcUG48VIY2 .icon-shape .label rect,#mermaid-svg-TDzxbmlcUG48VIY2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TDzxbmlcUG48VIY2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TDzxbmlcUG48VIY2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TDzxbmlcUG48VIY2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} I2C_SLAVE_FORCE + read/write
I2C_RDWR
I2C_SMBUS
用户层 i2c_xfer / read / write / SMBus
传输类型
构造 1 个 i2c_msg
构造 n 个 i2c_msg
构造 SMBus 参数
read() / write()
ioctl(fd, I2C_RDWR, &rdwr)
ioctl(fd, I2C_SMBUS, &args)
内核 i2cdev_read / i2cdev_write
内核 i2cdev_ioctl_rdwr
内核 i2cdev_ioctl_smbus
i2c_master_recv / i2c_master_send
i2c_transfer(adap, msgs, nmsgs)
i2c_smbus_xfer / i2c_smbus_xfer_emulated
适配器 algo->master_xfer()
SCL/SDA 波形:START、地址、ACK、数据、STOP
| 用户层代码 | 内核路径 | 总线动作 |
|---|---|---|
open("/dev/i2c-N") |
i2cdev_open |
无 |
ioctl(fd, I2C_SLAVE_FORCE, addr) |
i2cdev_ioctl |
无,只改 client->addr |
read(fd, buf, len) |
i2c_master_recv → i2c_transfer(1 msg) |
START addr+R data... NACK STOP |
write(fd, buf, len) |
i2c_master_send → i2c_transfer(1 msg) |
START addr+W data... STOP |
ioctl(fd, I2C_RDWR, msgs) |
i2cdev_ioctl_rdwr → i2c_transfer(n msg) |
START addr+W reg ACK repeated START addr+R data NACK STOP |
ioctl(fd, I2C_SMBUS, args) |
i2c_smbus_xfer / i2c_smbus_xfer_emulated |
标准 SMBus 时序,最终也是 i2c_transfer |
用户层 mutex |
用户层 | 保护 client->addr 和复合事务不被线程拆开 |
- 关系图
#mermaid-svg-jQiDWUbqaeRKqaeT{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jQiDWUbqaeRKqaeT .error-icon{fill:#552222;}#mermaid-svg-jQiDWUbqaeRKqaeT .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jQiDWUbqaeRKqaeT .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jQiDWUbqaeRKqaeT .marker.cross{stroke:#333333;}#mermaid-svg-jQiDWUbqaeRKqaeT svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jQiDWUbqaeRKqaeT p{margin:0;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge{stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 text{fill:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth--1{stroke-width:17;}#mermaid-svg-jQiDWUbqaeRKqaeT .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-0{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-0{stroke-width:14;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-1{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-1{stroke-width:11;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 text{fill:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-2{stroke-width:8;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-3{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-3{stroke-width:5;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-4{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-4{stroke-width:2;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-5{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-5{stroke-width:-1;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-6{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-6{stroke-width:-4;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-7{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-7{stroke-width:-7;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-8{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-8{stroke-width:-10;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-9{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-9{stroke-width:-13;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 polygon,#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 text{fill:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .node-icon-10{font-size:40px;color:black;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .edge-depth-10{stroke-width:-16;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled circle,#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:lightgray;}#mermaid-svg-jQiDWUbqaeRKqaeT .disabled text{fill:#efefef;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-root rect,#mermaid-svg-jQiDWUbqaeRKqaeT .section-root path,#mermaid-svg-jQiDWUbqaeRKqaeT .section-root circle,#mermaid-svg-jQiDWUbqaeRKqaeT .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-jQiDWUbqaeRKqaeT .section-root text{fill:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-root span{color:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .section-2 span{color:#ffffff;}#mermaid-svg-jQiDWUbqaeRKqaeT .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-jQiDWUbqaeRKqaeT .edge{fill:none;}#mermaid-svg-jQiDWUbqaeRKqaeT .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-jQiDWUbqaeRKqaeT :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} I2C 用户层通信
打开设备
open /dev/i2c-N
只建立 fd 到 adapter 的通道
无总线波形
设置从地址
I2C_SLAVE_FORCE
只改 client->addr
无 START/ACK
单消息传输
read
write
每次 1 个 i2c_msg
一次 i2c_transfer
多消息原子传输
I2C_RDWR
每个 msg 自带 addr
一次 i2c_transfer 提交多个 msg
repeated START
适合写寄存器再读
SMBus
I2C_SMBUS
内核翻译成 i2c_msg
最终走 i2c_transfer
错误
ENXIO
EREMOTEIO
EIO
对应 NACK 或传输不完整
I2C 通信的 "编码本质" 是构造 i2c_msg,交给内核 i2c_transfer() 函数,由适配器 master_xfer() 函数在 SCL/SDA 上产生 START、地址、ACK、数据和 STOP。I2C_SLAVE_FORCE 只是设置默认地址,I2C_RDWR 才是多消息原子事务的核心。
4.4 HM832IS 功率传感器实例
(一)硬件连接
- 从机地址
从机地址由 A0/A1 引脚决定(7 位地址),必须与硬件原理图匹配,如下所示。
| A1 | A0 | 7 位地址 | 十六进制 |
|---|---|---|---|
| GND | GND | 1000000 | 0x40(常用) |
| GND | VS | 1000001 | 0x41 |
| GND | SDA | 1000010 | 0x42 |
| GND | SCL | 1000011 | 0x43 |
| VS | GND | 1000100 | 0x44 |
| ... | ... | ... | ... |
⚠️ 这里 HM832IS A0/A1 引脚均接 GND,故从机地址为 0x40。
- 采样电阻
| 最大电流 | 推荐采样电阻 | 分流电压(满量程) |
|---|---|---|
| 1 A | 20 mΩ | 20 mV |
| 5 A | 10 mΩ | 50 mV |
| 15 A | 2 mΩ | 30 mV |
| 50 A | 1 mΩ | 50 mV |
分流电压满量程 81.92 mV,建议工作点不超过 80%,以留出余量。采样电阻为 1.667mΩ,最大电流 15A。
(二)软件架构与流程
- 架构图
#mermaid-svg-WkDHHJdSA8G6wthC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-WkDHHJdSA8G6wthC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-WkDHHJdSA8G6wthC .error-icon{fill:#552222;}#mermaid-svg-WkDHHJdSA8G6wthC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-WkDHHJdSA8G6wthC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-WkDHHJdSA8G6wthC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-WkDHHJdSA8G6wthC .marker.cross{stroke:#333333;}#mermaid-svg-WkDHHJdSA8G6wthC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-WkDHHJdSA8G6wthC p{margin:0;}#mermaid-svg-WkDHHJdSA8G6wthC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-WkDHHJdSA8G6wthC .cluster-label text{fill:#333;}#mermaid-svg-WkDHHJdSA8G6wthC .cluster-label span{color:#333;}#mermaid-svg-WkDHHJdSA8G6wthC .cluster-label span p{background-color:transparent;}#mermaid-svg-WkDHHJdSA8G6wthC .label text,#mermaid-svg-WkDHHJdSA8G6wthC span{fill:#333;color:#333;}#mermaid-svg-WkDHHJdSA8G6wthC .node rect,#mermaid-svg-WkDHHJdSA8G6wthC .node circle,#mermaid-svg-WkDHHJdSA8G6wthC .node ellipse,#mermaid-svg-WkDHHJdSA8G6wthC .node polygon,#mermaid-svg-WkDHHJdSA8G6wthC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-WkDHHJdSA8G6wthC .rough-node .label text,#mermaid-svg-WkDHHJdSA8G6wthC .node .label text,#mermaid-svg-WkDHHJdSA8G6wthC .image-shape .label,#mermaid-svg-WkDHHJdSA8G6wthC .icon-shape .label{text-anchor:middle;}#mermaid-svg-WkDHHJdSA8G6wthC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-WkDHHJdSA8G6wthC .rough-node .label,#mermaid-svg-WkDHHJdSA8G6wthC .node .label,#mermaid-svg-WkDHHJdSA8G6wthC .image-shape .label,#mermaid-svg-WkDHHJdSA8G6wthC .icon-shape .label{text-align:center;}#mermaid-svg-WkDHHJdSA8G6wthC .node.clickable{cursor:pointer;}#mermaid-svg-WkDHHJdSA8G6wthC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-WkDHHJdSA8G6wthC .arrowheadPath{fill:#333333;}#mermaid-svg-WkDHHJdSA8G6wthC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-WkDHHJdSA8G6wthC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-WkDHHJdSA8G6wthC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WkDHHJdSA8G6wthC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-WkDHHJdSA8G6wthC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WkDHHJdSA8G6wthC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-WkDHHJdSA8G6wthC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-WkDHHJdSA8G6wthC .cluster text{fill:#333;}#mermaid-svg-WkDHHJdSA8G6wthC .cluster span{color:#333;}#mermaid-svg-WkDHHJdSA8G6wthC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-WkDHHJdSA8G6wthC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-WkDHHJdSA8G6wthC rect.text{fill:none;stroke-width:0;}#mermaid-svg-WkDHHJdSA8G6wthC .icon-shape,#mermaid-svg-WkDHHJdSA8G6wthC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-WkDHHJdSA8G6wthC .icon-shape p,#mermaid-svg-WkDHHJdSA8G6wthC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-WkDHHJdSA8G6wthC .icon-shape .label rect,#mermaid-svg-WkDHHJdSA8G6wthC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-WkDHHJdSA8G6wthC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-WkDHHJdSA8G6wthC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-WkDHHJdSA8G6wthC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} HM832IS 硬件
平台 I2C 接口层 (i2c_api.c)
传感器驱动层 (hm832is.c)
应用层 (main.c)
周期采集
数据打印
业务逻辑
初始化 hm832is_init
校准计算
寄存器读写
单位换算
i2c_write
i2c_read
i2c_rdwr_xfer
寄存器 0x00~0xFF
I²C 从机 0x40
- 初始化流程图
#mermaid-svg-jnuanAN3T0VzhqDj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-jnuanAN3T0VzhqDj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-jnuanAN3T0VzhqDj .error-icon{fill:#552222;}#mermaid-svg-jnuanAN3T0VzhqDj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-jnuanAN3T0VzhqDj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-jnuanAN3T0VzhqDj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-jnuanAN3T0VzhqDj .marker.cross{stroke:#333333;}#mermaid-svg-jnuanAN3T0VzhqDj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-jnuanAN3T0VzhqDj p{margin:0;}#mermaid-svg-jnuanAN3T0VzhqDj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-jnuanAN3T0VzhqDj .cluster-label text{fill:#333;}#mermaid-svg-jnuanAN3T0VzhqDj .cluster-label span{color:#333;}#mermaid-svg-jnuanAN3T0VzhqDj .cluster-label span p{background-color:transparent;}#mermaid-svg-jnuanAN3T0VzhqDj .label text,#mermaid-svg-jnuanAN3T0VzhqDj span{fill:#333;color:#333;}#mermaid-svg-jnuanAN3T0VzhqDj .node rect,#mermaid-svg-jnuanAN3T0VzhqDj .node circle,#mermaid-svg-jnuanAN3T0VzhqDj .node ellipse,#mermaid-svg-jnuanAN3T0VzhqDj .node polygon,#mermaid-svg-jnuanAN3T0VzhqDj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-jnuanAN3T0VzhqDj .rough-node .label text,#mermaid-svg-jnuanAN3T0VzhqDj .node .label text,#mermaid-svg-jnuanAN3T0VzhqDj .image-shape .label,#mermaid-svg-jnuanAN3T0VzhqDj .icon-shape .label{text-anchor:middle;}#mermaid-svg-jnuanAN3T0VzhqDj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-jnuanAN3T0VzhqDj .rough-node .label,#mermaid-svg-jnuanAN3T0VzhqDj .node .label,#mermaid-svg-jnuanAN3T0VzhqDj .image-shape .label,#mermaid-svg-jnuanAN3T0VzhqDj .icon-shape .label{text-align:center;}#mermaid-svg-jnuanAN3T0VzhqDj .node.clickable{cursor:pointer;}#mermaid-svg-jnuanAN3T0VzhqDj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-jnuanAN3T0VzhqDj .arrowheadPath{fill:#333333;}#mermaid-svg-jnuanAN3T0VzhqDj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-jnuanAN3T0VzhqDj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-jnuanAN3T0VzhqDj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jnuanAN3T0VzhqDj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-jnuanAN3T0VzhqDj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jnuanAN3T0VzhqDj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-jnuanAN3T0VzhqDj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-jnuanAN3T0VzhqDj .cluster text{fill:#333;}#mermaid-svg-jnuanAN3T0VzhqDj .cluster span{color:#333;}#mermaid-svg-jnuanAN3T0VzhqDj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-jnuanAN3T0VzhqDj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-jnuanAN3T0VzhqDj rect.text{fill:none;stroke-width:0;}#mermaid-svg-jnuanAN3T0VzhqDj .icon-shape,#mermaid-svg-jnuanAN3T0VzhqDj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-jnuanAN3T0VzhqDj .icon-shape p,#mermaid-svg-jnuanAN3T0VzhqDj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-jnuanAN3T0VzhqDj .icon-shape .label rect,#mermaid-svg-jnuanAN3T0VzhqDj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-jnuanAN3T0VzhqDj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-jnuanAN3T0VzhqDj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-jnuanAN3T0VzhqDj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
否
是
否
是
否
是
否
是
hm832is_init 入口
- 保存参数
i2c / addr7 / shuntRes
2. 读制造商 ID 0xFE
通信成功?
返回 -2
通信失败
ID == 0x5959?
3. 计算校准值
CAL = 0.00512 / (LSB × R)
4. 写校准寄存器 0x05
写入成功?
返回 -3
校准写入失败
5. 回读校准值
反推 currentLSB / powerLSB
回读成功?
6. 写配置寄存器 0x00 = 0x4527
写入成功?
返回 -4
配置写入失败
7. 延时 30 ms
等待首次转换完成
8. calibrated = 1
返回 0 成功
- 采集流程图
#mermaid-svg-SV55wJYdRNkjMPuQ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-SV55wJYdRNkjMPuQ .error-icon{fill:#552222;}#mermaid-svg-SV55wJYdRNkjMPuQ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-SV55wJYdRNkjMPuQ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-SV55wJYdRNkjMPuQ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-SV55wJYdRNkjMPuQ .marker.cross{stroke:#333333;}#mermaid-svg-SV55wJYdRNkjMPuQ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-SV55wJYdRNkjMPuQ p{margin:0;}#mermaid-svg-SV55wJYdRNkjMPuQ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster-label text{fill:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster-label span{color:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster-label span p{background-color:transparent;}#mermaid-svg-SV55wJYdRNkjMPuQ .label text,#mermaid-svg-SV55wJYdRNkjMPuQ span{fill:#333;color:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ .node rect,#mermaid-svg-SV55wJYdRNkjMPuQ .node circle,#mermaid-svg-SV55wJYdRNkjMPuQ .node ellipse,#mermaid-svg-SV55wJYdRNkjMPuQ .node polygon,#mermaid-svg-SV55wJYdRNkjMPuQ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-SV55wJYdRNkjMPuQ .rough-node .label text,#mermaid-svg-SV55wJYdRNkjMPuQ .node .label text,#mermaid-svg-SV55wJYdRNkjMPuQ .image-shape .label,#mermaid-svg-SV55wJYdRNkjMPuQ .icon-shape .label{text-anchor:middle;}#mermaid-svg-SV55wJYdRNkjMPuQ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-SV55wJYdRNkjMPuQ .rough-node .label,#mermaid-svg-SV55wJYdRNkjMPuQ .node .label,#mermaid-svg-SV55wJYdRNkjMPuQ .image-shape .label,#mermaid-svg-SV55wJYdRNkjMPuQ .icon-shape .label{text-align:center;}#mermaid-svg-SV55wJYdRNkjMPuQ .node.clickable{cursor:pointer;}#mermaid-svg-SV55wJYdRNkjMPuQ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-SV55wJYdRNkjMPuQ .arrowheadPath{fill:#333333;}#mermaid-svg-SV55wJYdRNkjMPuQ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-SV55wJYdRNkjMPuQ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-SV55wJYdRNkjMPuQ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SV55wJYdRNkjMPuQ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-SV55wJYdRNkjMPuQ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SV55wJYdRNkjMPuQ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster text{fill:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ .cluster span{color:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-SV55wJYdRNkjMPuQ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-SV55wJYdRNkjMPuQ rect.text{fill:none;stroke-width:0;}#mermaid-svg-SV55wJYdRNkjMPuQ .icon-shape,#mermaid-svg-SV55wJYdRNkjMPuQ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-SV55wJYdRNkjMPuQ .icon-shape p,#mermaid-svg-SV55wJYdRNkjMPuQ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-SV55wJYdRNkjMPuQ .icon-shape .label rect,#mermaid-svg-SV55wJYdRNkjMPuQ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-SV55wJYdRNkjMPuQ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-SV55wJYdRNkjMPuQ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-SV55wJYdRNkjMPuQ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
是
采集周期到达
读总线电压 0x02
V = raw × 1.25 mV
读电流寄存器 0x04
I = raw × currentLSB
读功率寄存器 0x03
P = raw × powerLSB
数据合理?
上报错误 / 重试
输出 / 存储 / 上报
等待下一周期
- 完整数据流程图
#mermaid-svg-AepMqecj8moStzmv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AepMqecj8moStzmv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AepMqecj8moStzmv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AepMqecj8moStzmv .error-icon{fill:#552222;}#mermaid-svg-AepMqecj8moStzmv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AepMqecj8moStzmv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AepMqecj8moStzmv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AepMqecj8moStzmv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AepMqecj8moStzmv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AepMqecj8moStzmv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AepMqecj8moStzmv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AepMqecj8moStzmv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AepMqecj8moStzmv .marker.cross{stroke:#333333;}#mermaid-svg-AepMqecj8moStzmv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AepMqecj8moStzmv p{margin:0;}#mermaid-svg-AepMqecj8moStzmv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AepMqecj8moStzmv .cluster-label text{fill:#333;}#mermaid-svg-AepMqecj8moStzmv .cluster-label span{color:#333;}#mermaid-svg-AepMqecj8moStzmv .cluster-label span p{background-color:transparent;}#mermaid-svg-AepMqecj8moStzmv .label text,#mermaid-svg-AepMqecj8moStzmv span{fill:#333;color:#333;}#mermaid-svg-AepMqecj8moStzmv .node rect,#mermaid-svg-AepMqecj8moStzmv .node circle,#mermaid-svg-AepMqecj8moStzmv .node ellipse,#mermaid-svg-AepMqecj8moStzmv .node polygon,#mermaid-svg-AepMqecj8moStzmv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AepMqecj8moStzmv .rough-node .label text,#mermaid-svg-AepMqecj8moStzmv .node .label text,#mermaid-svg-AepMqecj8moStzmv .image-shape .label,#mermaid-svg-AepMqecj8moStzmv .icon-shape .label{text-anchor:middle;}#mermaid-svg-AepMqecj8moStzmv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AepMqecj8moStzmv .rough-node .label,#mermaid-svg-AepMqecj8moStzmv .node .label,#mermaid-svg-AepMqecj8moStzmv .image-shape .label,#mermaid-svg-AepMqecj8moStzmv .icon-shape .label{text-align:center;}#mermaid-svg-AepMqecj8moStzmv .node.clickable{cursor:pointer;}#mermaid-svg-AepMqecj8moStzmv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AepMqecj8moStzmv .arrowheadPath{fill:#333333;}#mermaid-svg-AepMqecj8moStzmv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AepMqecj8moStzmv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AepMqecj8moStzmv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AepMqecj8moStzmv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AepMqecj8moStzmv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AepMqecj8moStzmv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AepMqecj8moStzmv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AepMqecj8moStzmv .cluster text{fill:#333;}#mermaid-svg-AepMqecj8moStzmv .cluster span{color:#333;}#mermaid-svg-AepMqecj8moStzmv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AepMqecj8moStzmv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AepMqecj8moStzmv rect.text{fill:none;stroke-width:0;}#mermaid-svg-AepMqecj8moStzmv .icon-shape,#mermaid-svg-AepMqecj8moStzmv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AepMqecj8moStzmv .icon-shape p,#mermaid-svg-AepMqecj8moStzmv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AepMqecj8moStzmv .icon-shape .label rect,#mermaid-svg-AepMqecj8moStzmv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AepMqecj8moStzmv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AepMqecj8moStzmv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AepMqecj8moStzmv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 应用层
驱动层
I²C 总线
HM832IS 寄存器
HM832IS 内部 ADC
采样层
物理层
负载电流 I
(0~15 A)
供电电压 VBUS
(0~36 V)
采样电阻 Rshunt
1.667 mΩ
分流电压 V_SHUNT
I × R = 0~25 mV
ΔΣ ADC
500kHz 采样
16bit 量化
0x01 分流电压
2.5 µV/LSB
0x02 总线电压
1.25 mV/LSB
0x04 电流
500.1 µA/LSB
0x03 功率
12.503 mW/LSB
0x05 校准寄存器
CAL=6142
0x00 配置寄存器
0x4527
i2c_write_read
写指针+Sr+读
hm832is_read_reg
currentLSB
powerLSB
hm832is_read_voltage
hm832is_read_current
hm832is_read_power
float v, i, p
printf / 上报
- 数据换算链路图(核心公式)
#mermaid-svg-wB5oQzMzFaYAXdX2{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wB5oQzMzFaYAXdX2 .error-icon{fill:#552222;}#mermaid-svg-wB5oQzMzFaYAXdX2 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wB5oQzMzFaYAXdX2 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .marker.cross{stroke:#333333;}#mermaid-svg-wB5oQzMzFaYAXdX2 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wB5oQzMzFaYAXdX2 p{margin:0;}#mermaid-svg-wB5oQzMzFaYAXdX2 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster-label text{fill:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster-label span{color:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster-label span p{background-color:transparent;}#mermaid-svg-wB5oQzMzFaYAXdX2 .label text,#mermaid-svg-wB5oQzMzFaYAXdX2 span{fill:#333;color:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .node rect,#mermaid-svg-wB5oQzMzFaYAXdX2 .node circle,#mermaid-svg-wB5oQzMzFaYAXdX2 .node ellipse,#mermaid-svg-wB5oQzMzFaYAXdX2 .node polygon,#mermaid-svg-wB5oQzMzFaYAXdX2 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .rough-node .label text,#mermaid-svg-wB5oQzMzFaYAXdX2 .node .label text,#mermaid-svg-wB5oQzMzFaYAXdX2 .image-shape .label,#mermaid-svg-wB5oQzMzFaYAXdX2 .icon-shape .label{text-anchor:middle;}#mermaid-svg-wB5oQzMzFaYAXdX2 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .rough-node .label,#mermaid-svg-wB5oQzMzFaYAXdX2 .node .label,#mermaid-svg-wB5oQzMzFaYAXdX2 .image-shape .label,#mermaid-svg-wB5oQzMzFaYAXdX2 .icon-shape .label{text-align:center;}#mermaid-svg-wB5oQzMzFaYAXdX2 .node.clickable{cursor:pointer;}#mermaid-svg-wB5oQzMzFaYAXdX2 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .arrowheadPath{fill:#333333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wB5oQzMzFaYAXdX2 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-wB5oQzMzFaYAXdX2 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wB5oQzMzFaYAXdX2 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster text{fill:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 .cluster span{color:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-wB5oQzMzFaYAXdX2 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-wB5oQzMzFaYAXdX2 rect.text{fill:none;stroke-width:0;}#mermaid-svg-wB5oQzMzFaYAXdX2 .icon-shape,#mermaid-svg-wB5oQzMzFaYAXdX2 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-wB5oQzMzFaYAXdX2 .icon-shape p,#mermaid-svg-wB5oQzMzFaYAXdX2 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-wB5oQzMzFaYAXdX2 .icon-shape .label rect,#mermaid-svg-wB5oQzMzFaYAXdX2 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-wB5oQzMzFaYAXdX2 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-wB5oQzMzFaYAXdX2 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-wB5oQzMzFaYAXdX2 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 物理量
V_SHUNT = I × Rshunt
15A × 1.667mΩ = 25 mV
V_BUS
直接测量 0~36V
分流寄存器 raw
= V_SHUNT / 2.5µV
= 10000
总线寄存器 raw
= V_BUS / 1.25mV
= 9596
校准寄存器
CAL = 6142
用于功率
电流寄存器 raw
= (raw1 × CAL) / 2048
= 29980 → 超量程?
实际: 6179
功率寄存器 raw
= (raw3 × raw2) / 20000
= 2966
V = raw2 × 1.25mV
= 11.995 V
I = raw3 × currentLSB
= 6179 × 500.1µA
= 3.090 A
P = raw4 × powerLSB
= 2966 × 12.503mW
= 37.08 W
输出
(三)功率采集功能实现
hm832is_app.h头文件
c
/**
* @file hm832is_app.h
* @brief HM832IS 应用层采集接口(基于已有 i2c_write / i2c_read)
*/
#ifndef HM832IS_APP_H
#define HM832IS_APP_H
#include "i2c_api.h"
/* 平台类型(若已在 i2c_api.h 中定义则删除这三行) */
typedef unsigned char U8;
typedef unsigned short U16;
typedef unsigned int U32;
/* ======================= 寄存器地址 ======================= */
#define HM832IS_REG_CONFIG 0x00
#define HM832IS_REG_SHUNT_V 0x01
#define HM832IS_REG_BUS_V 0x02
#define HM832IS_REG_POWER 0x03
#define HM832IS_REG_CURRENT 0x04
#define HM832IS_REG_CALIBRATION 0x05
#define HM832IS_REG_MASK_ENABLE 0x06
#define HM832IS_REG_ALERT_LIMIT 0x07
#define HM832IS_REG_MANUFACTURER 0xFE
#define HM832IS_REG_DIE_ID 0xFF
/* ======================= 内部常数 ======================= */
#define HM832IS_INTERNAL_SCALE 0.00512f
#define HM832IS_BUS_V_LSB 0.00125f /* 1.25 mV */
#define HM832IS_SHUNT_V_LSB 0.0000025f /* 2.5 µV */
/* 默认配置:连续模式,分流+总线,1.1ms 转换,4 次平均 */
#define HM832IS_CONFIG_CONTINUOUS 0x4527
/* 默认从机地址(A0=A1=GND) */
#define HM832IS_DEFAULT_ADDR 0x40
/* ======================= 硬件参数 ======================= */
#define HM832IS_SHUNT_RES_OHM 0.001667f /* 采样电阻 1.667 mΩ */
#define HM832IS_MAX_CURRENT_A 15.0f /* 最大电流 15 A */
#define HM832IS_CURRENT_LSB 0.0005f /* 推荐 500 µA/bit */
/* ======================= 派生参数 ======================= */
/* CAL = 0.00512 / (0.0005 × 0.001667) ≈ 6142 */
/* currentLSB = 0.00512 / (6142 × 0.001667) ≈ 500.1 µA/bit */
/* powerLSB = 500.1 µA × 25 = 12.503 mW/bit */
/* 满量程电流 = 32767 × 500.1 µA ≈ 16.38 A */
/* 满量程功率(12V)= 16.38 × 12 ≈ 196.6 W */
/* ======================= 设备对象 ======================= */
typedef struct {
I2C_INDEX i2c; /* 平台 I2C 总线索引 */
U16 addr7; /* 7 位从机地址 */
float shuntRes; /* 采样电阻(Ω) */
float currentLSB; /* 电流 LSB(A/bit) */
float powerLSB; /* 功率 LSB(W/bit) */
int calibrated; /* 校准完成标志 */
} hm832is_dev;
/* ======================= 公开接口 ======================= */
int hm832is_init(hm832is_dev *dev, I2C_INDEX i2c, U16 addr7, float shuntRes);
int hm832is_read_voltage(hm832is_dev *dev, float *voltageV);
int hm832is_read_current(hm832is_dev *dev, float *currentA);
int hm832is_read_power(hm832is_dev *dev, float *powerW);
int hm832is_read_shunt(hm832is_dev *dev, float *shuntV);
int hm832is_read_reg(hm832is_dev *dev, U8 reg, U16 *value);
int hm832is_write_reg(hm832is_dev *dev, U8 reg, U16 value);
#endif /* HM832IS_APP_H */
hm832is_app.c文件
c
/**
* @file hm832is_app.c
* @brief HM832IS 应用层采集实现(复用平台 i2c_write / i2c_read / i2c_rdwr_xfer)
*/
#include "hm832is_app.h"
#include <stdio.h>
#include <string.h>
/* 延时宏:若平台无 usleep 请替换 */
#include <unistd.h>
#define HM832IS_DELAY_MS(ms) usleep((ms) * 1000)
/* ======================= 寄存器读写 ======================= */
/**
* @brief 写 16 位寄存器(大端)
* 报文: [reg][data_H][data_L] → 一次 i2c_write
*/
int hm832is_write_reg(hm832is_dev *dev, U8 reg, U16 value)
{
U8 buf[3] = {
reg,
(U8)(value >> 8),
(U8)(value & 0xFF)
};
return (i2c_write(dev->i2c, dev->addr7, buf, 3) == 0) ? 0 : -1;
}
/**
* @brief 读 16 位寄存器(组合传输,无 STOP)
* 报文: i2c_write_read(reg, 1) → 读 2 字节
*/
int hm832is_read_reg(hm832is_dev *dev, U8 reg, U16 *value)
{
U8 rbuf[2] = { 0, 0 };
if (i2c_rdwr_xfer(dev->i2c, dev->addr7,
®, 1,
rbuf, 2) != 0) {
return -1;
}
*value = ((U16)rbuf[0] << 8) | rbuf[1];
return 0;
}
/* ======================= 初始化 ======================= */
int hm832is_init(hm832is_dev *dev, I2C_INDEX i2c, U16 addr7, float shuntRes)
{
if (!dev || shuntRes <= 0.0f) {
return -1;
}
memset(dev, 0, sizeof(*dev));
dev->i2c = i2c;
dev->addr7 = addr7;
dev->shuntRes = shuntRes;
/* 1. 验证通信 */
U16 mfr = 0;
if (hm832is_read_reg(dev, HM832IS_REG_MANUFACTURER, &mfr) != 0) {
printf("[HM832IS] read MFR failed\n");
return -2;
}
printf("[HM832IS] Manufacturer ID: 0x%04X\n", mfr);
if (mfr != 0x5959) {
printf("[HM832IS] device not found at 0x%02X\n", addr7);
return -2;
}
U16 die = 0;
if (hm832is_read_reg(dev, HM832IS_REG_DIE_ID, &die) == 0) {
printf("[HM832IS] Die ID: 0x%04X\n", die);
}
/* 2. 写入校准值:使用 500 µA/bit */
float initLSB = HM832IS_CURRENT_LSB; /* 0.0005f */
U16 calVal = (U16)(HM832IS_INTERNAL_SCALE / (initLSB * shuntRes));
if (calVal == 0) {
calVal = 1;
}
if (hm832is_write_reg(dev, HM832IS_REG_CALIBRATION, calVal) != 0) {
printf("[HM832IS] write calibration failed\n");
return -3;
}
/* 3. 回读校准值反推实际 LSB */
U16 actualCal = 0;
if (hm832is_read_reg(dev, HM832IS_REG_CALIBRATION, &actualCal) != 0 ||
actualCal == 0) {
printf("[HM832IS] readback calibration failed\n");
return -3;
}
dev->currentLSB = HM832IS_INTERNAL_SCALE / (actualCal * shuntRes);
dev->powerLSB = dev->currentLSB * 25.0f;
printf("[HM832IS] Cal=%u, currentLSB=%.7f A/bit, powerLSB=%.6f W/bit\n",
actualCal, dev->currentLSB, dev->powerLSB);
/* 4. 写配置寄存器 */
if (hm832is_write_reg(dev, HM832IS_REG_CONFIG,
HM832IS_CONFIG_CONTINUOUS) != 0) {
printf("[HM832IS] write config failed\n");
return -4;
}
HM832IS_DELAY_MS(30);
dev->calibrated = 1;
printf("[HM832IS] init OK\n");
return 0;
}
/* ======================= 数据读取 ======================= */
int hm832is_read_voltage(hm832is_dev *dev, float *voltageV)
{
U16 raw = 0;
if (!dev || !voltageV) return -1;
if (hm832is_read_reg(dev, HM832IS_REG_BUS_V, &raw) != 0) return -1;
*voltageV = (float)raw * HM832IS_BUS_V_LSB;
return 0;
}
int hm832is_read_current(hm832is_dev *dev, float *currentA)
{
U16 raw = 0;
if (!dev || !currentA || !dev->calibrated) return -1;
if (hm832is_read_reg(dev, HM832IS_REG_CURRENT, &raw) != 0) return -1;
*currentA = (float)raw * dev->currentLSB;
return 0;
}
int hm832is_read_power(hm832is_dev *dev, float *powerW)
{
U16 raw = 0;
if (!dev || !powerW || !dev->calibrated) return -1;
if (hm832is_read_reg(dev, HM832IS_REG_POWER, &raw) != 0) return -1;
*powerW = (float)raw * dev->powerLSB;
return 0;
}
int hm832is_read_shunt(hm832is_dev *dev, float *shuntV)
{
U16 raw = 0;
if (!dev || !shuntV) return -1;
if (hm832is_read_reg(dev, HM832IS_REG_SHUNT_V, &raw) != 0) return -1;
*shuntV = (float)(int16_t)raw * HM832IS_SHUNT_V_LSB;
return 0;
}
- 测试示例
c
/**
* @file main.c
* @brief HM832IS 周期采集应用
*/
#include "hm832is_app.h"
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#define SAMPLE_INTERVAL_MS 10 /* 采集周期 10ms */
#define MAX_RETRY 3 /* 连续失败重试次数 */
static volatile sig_atomic_t g_running = 1;
static void sig_handler(int sig)
{
(void)sig;
g_running = 0;
}
int main(void)
{
hm832is_dev dev;
int fail_count = 0;
signal(SIGINT, sig_handler);
signal(SIGTERM, sig_handler);
if (hm832is_init(&dev, I2C_DEV, HM832IS_DEFAULT_ADDR,
HM832IS_SHUNT_RES_OHM) != 0) {
printf("[APP] HM832IS init failed\n");
return -1;
}
printf("\n[APP] Start sampling, press Ctrl+C to exit\n");
printf("--------------------------------------------------------------\n");
printf("%-8s %-12s %-12s %-12s\n",
"Time", "Voltage(V)", "Current(A)", "Power(W)");
printf("--------------------------------------------------------------\n");
int sample = 0;
while (g_running) {
float v = 0.0f, i = 0.0f, p = 0.0f;
if (hm832is_read_voltage(&dev, &v) == 0 &&
hm832is_read_current(&dev, &i) == 0 &&
hm832is_read_power(&dev, &p) == 0) {
printf("[%04d] %-12.3f %-12.3f %-12.3f\n",
sample, v, i, p);
sample++;
fail_count = 0;
} else {
if (++fail_count >= MAX_RETRY) {
printf("[WARN] %d consecutive failures, reinit device\n", fail_count);
if (hm832is_init(&dev, I2C_DEV, HM832IS_DEFAULT_ADDR,
SHUNT_RES_OHM) != 0) {
printf("[APP] Reinit failed, continue\n");
}
fail_count = 0;
}
}
usleep(SAMPLE_INTERVAL_MS * 1000);
}
printf("\n[APP] Exiting...\n");
return 0;
}
预期输出:
text
[HM832IS] Manufacturer ID: 0x5959
[HM832IS] Die ID: 0x2726
[HM832IS] Cal=6142, currentLSB=0.0005001 A/bit, powerLSB=0.012503 W/bit
[HM832IS] init OK
[APP] Start sampling, press Ctrl+C to exit
--------------------------------------------------------------
Time Voltage(V) Current(A) Power(W)
--------------------------------------------------------------
[0000] 11.965 3.312 39.843
[0001] 11.964 3.310 39.810
[0002] 11.966 3.313 39.845
常见问题排查
问题 1:ls /dev/i2c-* 无设备节点
| 序号 | 可能原因 | 对应解决 |
|---|---|---|
| 1 | Vivado 中未使能 PS I2C | 检查 Vivado PS 配置,使能对应 I2C 控制器 |
| 2 | MIO 引脚未分配或冲突 | 检查 MIO 分配,确认无引脚冲突 |
| 3 | 设备树中 I2C 节点仍为 status = "disabled" |
检查 system-user.dtsi,将对应 I2C 节点改为 status = "okay" |
| 4 | 内核未包含 I2C 控制器驱动 | 检查内核配置,确认包含对应 I2C 控制器驱动 |
| 5 | 修改设备树后未更新启动镜像 | 重新编译并更新 BOOT.BIN / image.ub |
问题 2:i2cdetect 扫描不到设备
| 序号 | 可能原因 | 对应解决 |
|---|---|---|
| 1 | SCL/SDA 接反 | 核对原理图和接线,确认 SCL/SDA 未接反 |
| 2 | 外设未供电或 GND 未共地 | 确认 3.3V 供电正常,并确保 GND 共地 |
| 3 | 设备地址错误 | 确认 MPU6050 地址为 0x68(或 0x69,取决于 AD0) |
| 4 | 缺少上拉电阻 | 增加 4.7kΩ 上拉电阻 |
| 5 | 总线被其他设备占用 | 排查总线上其他设备,必要时断开逐一验证 |
| 6 | I2C 时钟过高 | 降低时钟到 100kHz 后重新扫描 |
问题 3:I2C 读写超时或数据乱码
| 序号 | 可能原因 | 对应解决 |
|---|---|---|
| 1 | 时钟频率过高 | 降低 I2C 时钟频率 |
| 2 | 上拉电阻缺失或阻值不合适 | 增加或调整合适的上拉电阻 |
| 3 | 线缆过长、干扰严重 | 缩短杜邦线,远离干扰源 |
| 4 | 电平不匹配 | 检查外设电平,必要时做电平转换 |
| 5 | 寄存器地址或读写时序错误 | 先用 i2cget/i2cset 验证寄存器读写,再核对时序 |
问题 4:设备树配置后不生效
| 序号 | 可能原因 | 对应解决 |
|---|---|---|
| 1 | 修改的不是实际编译使用的 DTS | 确认实际编译使用的 DTS 路径,修改正确文件 |
| 2 | system-user.dtsi 未被包含 |
检查板级 DTS 是否包含 system-user.dtsi |
| 3 | pinctrl label 写错 | 确认节点 label 与板级 DTS 一致,修正 pinctrl label |
| 4 | 设备树编译报错 | 检查 PetaLinux 设备树编译日志,修复报错 |
| 5 | 未更新启动镜像 | 重新执行 petalinux-build 并更新启动介质 |