I2C(Inter-Integrated Circuit)是一种广泛用于嵌入式系统的串行通信协议,允许多个设备通过两根数据线进行通信。在 Linux 系统中,I2C 设备通常挂载在 /dev 目录下,使用如 /dev/i2c-X 这样的设备文件进行访问。通过系统调用和特定的 I2C 操作命令,应用层程序可以与 I2C 设备进行读写操作。
本教程将介绍如何使用 Linux 提供的 I2C 接口进行编程,详细讲解关键的结构体、系统调用以及常用的 I2C 工具,以帮助开发者更好地理解和操作 I2C 设备。
1. 关键结构体
1.1 struct i2c_msg
i2c_msg 是 I2C 消息的描述结构体,用于定义一次 I2C 通信中的单个传输消息。它可以是读或写操作,并包含目标设备的地址、数据缓冲区和数据长度。
struct i2c_msg { __u16 addr; __u16 flags; __u16 len; __u8 *buf; };
|
主要标志位:
I2C_M_RD: 表示读操作。如果未设置此标志,则表示写操作。
I2C_M_TEN: 表示使用 10 位地址模式。
I2C_M_STOP: 表示在消息结束时生成 STOP 信号。
1.2 struct i2c_rdwr_ioctl_data
i2c_rdwr_ioctl_data 结构体用于通过 ioctl 系统调用执行 I2C 读写操作。它包含一个 i2c_msg 数组和消息的数量。
struct i2c_rdwr_ioctl_data { struct i2c_msg *msgs; __u32 nmsgs; };
|
2. I2C 应用编程概述
在用户空间中,I2C 编程的核心操作是:
- 打开 I2C 设备:通过
open() 函数打开 /dev/i2c-X 设备文件。
- 设置从设备地址:使用
ioctl() 系统调用设置目标从设备的地址。
- 数据读写:通过
read() 和 write() 进行简单的读写操作,或使用 ioctl() 进行复杂的多字节传输。
2.1 打开 I2C 设备
使用 open() 函数打开 I2C 设备文件,例如 /dev/i2c-1:
int file; file = open("/dev/i2c-1", O_RDWR); if (file < 0) { perror("Failed to open the i2c bus"); exit(1); }
|
2.2 设置从设备地址
通过 ioctl() 设置与哪个从设备进行通信:
int addr = 0x50; if (ioctl(file, I2C_SLAVE, addr) < 0) { perror("Failed to acquire bus access and/or talk to slave"); exit(1); }
|
I2C_SLAVE 是常用的 ioctl 命令,用于设置 I2C 从设备地址。
2.3 读写 I2C 数据
2.3.1 写入数据
使用 write() 函数将数据写入 I2C 设备:
unsigned char buffer[1] = { 0xA0 }; if (write(file, buffer, 1) != 1) { perror("Failed to write to the i2c bus"); }
|
2.3.2 读取数据
使用 read() 函数从 I2C 设备读取数据:
unsigned char buffer[1]; if (read(file, buffer, 1) != 1) { perror("Failed to read from the i2c bus"); } else { printf("Data read: 0x%02x\n", buffer[0]); }
|
2.4 使用 ioctl 进行复杂读写操作
对于更复杂的读写操作(如多字节传输),可以使用 I2C_RDWR 命令结合 i2c_rdwr_ioctl_data 来实现。
2.4.1 写入多个字节
假设要向寄存器 0x10 写入两个字节的数据:
unsigned char outbuf[3] = { 0x10, 0x01, 0x02 }; struct i2c_msg messages[1]; messages[0].addr = addr; messages[0].flags = 0; messages[0].len = 3; messages[0].buf = outbuf;
struct i2c_rdwr_ioctl_data ioctl_data; ioctl_data.msgs = messages; ioctl_data.nmsgs = 1;
if (ioctl(file, I2C_RDWR, &ioctl_data) < 0) { perror("Failed to write to the i2c bus"); }
|
2.4.2 读取寄存器数据
通常,读取寄存器数据需要先写入寄存器地址,再读取其数据。例如,读取寄存器 0x10:
unsigned char reg = 0x10; unsigned char inbuf[1];
struct i2c_msg messages[2]; messages[0].addr = addr; messages[0].flags = 0; messages[0].len = 1; messages[0].buf = ®
messages[1].addr = addr; messages[1].flags = I2C_M_RD; messages[1].len = 1; messages[1].buf = inbuf;
struct i2c_rdwr_ioctl_data ioctl_data; ioctl_data.msgs = messages; ioctl_data.nmsgs = 2;
if (ioctl(file, I2C_RDWR, &ioctl_data) < 0) { perror("Failed to read from the i2c bus"); } else { printf("Register 0x10 read: 0x%02x\n", inbuf[0]); }
|
3. 常用的 ioctl 命令
I2C_SLAVE: 设置从设备地址。
I2C_RDWR: 执行多条 I2C 消息(读或写),对应 i2c_rdwr_ioctl_data。
4. 完整的 I2C 应用层示例
以下是一个完整的示例程序,展示如何通过 I2C 接口读取和写入数据:
#include <stdio.h> #include <fcntl.h> #include <sys/ioctl.h> #include <linux/i2c-dev.h> #include <unistd.h>
int main() { int file; int addr = 0x50;
if ((file = open("/dev/i2c-1", O_RDWR)) < 0) { perror("Failed to open the i2c bus"); return 1; }
if (ioctl(file, I2C_SLAVE, addr) < 0) { perror("Failed to acquire bus access and/or talk to slave"); return 1; }
unsigned char outbuf[3] = { 0x10, 0x01, 0x02 };
if (write(file, outbuf, 3) != 3) { perror("Failed to write to the i2c bus"); return 1; }
unsigned char reg = 0x10; unsigned char inbuf[1];
struct i2c_msg messages[2]; messages[0].addr = addr; messages[0].flags = 0; messages[0].len = 1; messages[0].buf = ®
messages[1].addr = addr; messages[1].flags = I2C_M_RD; messages[1].len = 1; messages[1].buf = inbuf;
struct i2c_rdwr_ioctl_data ioctl_data; ioctl_data.msgs = messages; ioctl_data.nmsgs = 2;
if (ioctl(file, I2C_RDWR, &ioctl_data) < 0) { perror("Failed to read from the i2c bus"); return 1; } else { printf("Register 0x10 read: 0x%02x\n", inbuf[0]); }
close(file); return 0; }
|
i2c-tools 是一组用于调试 I2C 设备的工具集,其中包括 i2cdetect、i2cget、i2cset 和 i2cdump 等常用工具。以下是这些工具的使用介绍。
5.1 i2cdetect
i2cdetect 用于扫描 I2C 总线,查看哪些设备在响应。
5.2 i2cget
i2cget 用于读取 I2C 设备寄存器的数据。
5.3 i2cset
i2cset 用于向 I2C 设备写入数据。
i2cset -y 1 0x50 0x00 0xFF
|
5.4 i2cdump
i2cdump 用于读取 I2C 设备的所有寄存器,并以十六进制输出。
6. 总结
I2C 是嵌入式系统中常见的通信协议,在 Linux 中提供了丰富的用户空间接口来与设备交互。通过使用 open()、read()、write() 和 ioctl() 等系统调用,开发者可以轻松对 I2C 设备进行操作。此外,i2c-tools 提供了一组命令行工具,能够帮助开发者快速调试和测试 I2C 总线设备。在嵌入式开发和硬件调试中,这些工具和接口都是非常重要的。
如需进一步了解 i2c-tools 的源码和开发方法,你可以访问 i2c-tools 官方源码仓库。