跳转到内容
新建笔记

SH1107 I2C 驱动

IIC子系统mpu6050驱动实验 | 全志f1c100s(200s) | f1c200s iic-oled dts参考 | I2C子系统之驱动 | linux驱动移植-I2C驱动移植

image-20240502161427072

驱动iic

image-20240502155025948

I2c

iic设备树

I2C控制器和I2C设备驱动是Linux系统中处理I2C总线通信的两个不同组件,它们各自承担着不同的职责: I2C控制器(I2C Controller)

  1. 定义:I2C控制器是物理硬件,通常集成在SoC(System on Chip)中,负责实现I2C协议的物理层和链路层的功能。
  2. 作用:
    • 管理I2C总线上的通信,包括生成时钟信号、管理数据传输等。
    • 控制数据在I2C总线上的发送和接收,包括起始条件、数据位、应答位和停止条件。
    • 可以支持多个I2C设备连接到同一个I2C总线上,通过不同的地址进行区分。
  3. 驱动程序:I2C控制器通常需要一个驱动程序来与其交互。这个驱动程序负责将操作系统的指令转换为对I2C控制器硬件的操作。

I2C设备驱动(I2C Device Driver)

  1. 定义:I2C设备驱动是软件组件,用于控制连接到I2C总线上的特定设备,如传感器、触摸屏等。
  2. 作用:
    • 为操作系统提供接口,以便操作系统可以与I2C设备进行交互。
    • 实现特定I2C设备的高级操作,如读取传感器数据、配置设备参数等。
    • 管理设备的状态,处理错误情况,并提供必要的缓冲和数据转换。
  3. 匹配过程:I2C设备驱动通过匹配过程识别连接到I2C总线上的特定设备,然后加载相应的驱动程序来控制该设备。

区别

  • 硬件与软件:I2C控制器是硬件,而I2C设备驱动是软件。
  • 功能范围:I2C控制器负责实现I2C协议的基本通信功能,而I2C设备驱动则负责特定设备的高级控制和数据处理。
  • 抽象层次:I2C控制器提供了一个较低层次的抽象,处理的是I2C协议的基本信号和数据传输;I2C设备驱动则提供了一个较高层次的抽象,处理的是特定设备的功能和操作。
  • 驱动匹配:I2C控制器驱动通过platform_driver结构体注册,而I2C设备驱动通过i2c_driver结构体注册。它们通过不同的匹配机制与相应的硬件设备关联。

简而言之,I2C控制器是实现I2C通信的物理硬件,而I2C设备驱动是控制这些硬件设备在软件层面上操作的程序。两者共同协作,使得操作系统能够与I2C总线上的设备进行有效通信。

image-20240502180842820

您提到的内容涉及到Linux操作系统中的设备驱动模型,特别是在处理I2C(Inter-Integrated Circuit)总线及其设备时的工作原理。下面我将尝试以更清晰的方式解释这个过程:

  1. I2C控制器驱动注册:
    • I2C控制器是一个硬件设备,负责管理I2C总线上的通信。
    • 在Linux中,每个硬件设备都需要一个驱动程序来控制它。驱动程序通过platform_driver结构体注册到系统中。
    • platform_driver结构体中的match()函数用于确定该驱动程序是否适用于某个特定的硬件平台设备(platform_device)。
    • 当match()函数返回成功时,platform_driver的probe()函数会被调用,这标志着I2C控制器驱动程序开始初始化并注册I2C控制器。
  2. I2C设备驱动注册:
    • I2C设备,如触摸屏,是连接到I2C总线上的设备。
    • I2C设备驱动程序通过i2c_driver结构体注册到系统中。
    • 类似于I2C控制器驱动,i2c_driver的match()函数用于确定驱动程序是否适用于特定的I2C设备。
    • 当匹配成功,i2c_driver的probe()函数会被调用,这标志着I2C设备驱动程序开始初始化并注册I2C设备。
  3. 总线和设备的关系:
    • 在Linux中,设备通过它们所连接的总线被识别和枚举。例如,I2C控制器驱动程序提供了一个I2C总线,而I2C设备则连接到这个总线上。
    • i2c_adapter代表了I2C总线,它是连接I2C控制器和I2C设备之间的桥梁。
    • i2c_client代表了连接到I2C总线上的设备,如触摸屏。
  4. 设备树(Device Tree):
    • 在现代Linux系统中,设备树是一种用于描述硬件设备及其连接方式的机制。
    • 设备树中的节点对应于系统中的硬件设备,包括I2C控制器和I2C设备。
    • 设备树允许操作系统以一种更灵活和可扩展的方式来识别和配置硬件设备。
  5. 总结:
    • Linux通过总线和设备驱动模型来管理硬件设备。
    • I2C控制器驱动和I2C设备驱动通过匹配和探测过程注册到系统中。
    • I2C总线和设备通过i2c_adapter和i2c_client在Linux中被识别和管理。
    • 设备树提供了一种描述硬件设备及其连接的方式,使得操作系统可以更有效地管理硬件。

(0)准备

1)i2c-tool:使用

终端窗口
i2cdetect -y -r 0 #执行 i2cdetect -y -r 0 命令后,该工具会扫描 I2C 总线 0 上的所有设备,并输出一个表格,显示每个可能的 I2C 地址上的设备。
i2cdetect -F 0 #:查询i2c总线的功能,参数i2cbus表示i2c总线
i2cdetect -V #:打印软件的 -
i2cdetect -l #:检测当前系统有几组i2c总线
i2cget -y 0 0x3c 0x00 # 调用 i2cget 工具,用于从 I2C 设备读取数据
i2cget # 调用 i2cget 工具,用于从 I2C 设备读取数据,I2C 总线 0,设备的地址0x3c,要读取的寄存器或命令的地址,这里是 0x00
i2cset -f -m 0xff -r 1 0x38 0x04 0x03 # 0x38设备地址,0x04要写入的寄存器,0x03要写入的值,从结果来看没有写入成功

SH1107

无标题
// 对外数据结构
struct i2c_driver — 代表一个I2C设备驱动
struct i2c_client — 代表一个I2C从设备
struct i2c_board_info — 从设备创建的模版
I2C_BOARD_INFO — 创建I2C设备的宏,包含名字和地址
struct i2c_algorithm — 代表I2C传输方法
struct i2c_bus_recovery_info — I2C总线恢复信息?内核新加入的结构,不是很清楚。
//对外函数操作
module_i2c_driver — 注册I2C设备驱动的宏定义
i2c_register_board_info — 静态声明(注册)I2C设备,可多个
i2c_verify_client — 如果设备是i2c_client的dev成员则返回其父指针,否则返回NULL。用来校验设备是否为I2C设备
i2c_lock_adapter — I2C总线持锁操作,会找到最根源的那个i2c_adapter。说明你的模块必须符合GPL协议才可以使用这个接口。后边以GPL代表。
i2c_unlock_adapter — 上一个的反操作,GPL
i2c_new_device — 由i2c_board_info信息声明一个i2c设备(client),GPL
i2c_unregister_device — 上一个的反操作,GPL。
i2c_new_dummy — 声明一个名为dummy(指定地址)的I2C设备,GPL
i2c_verify_adapter — 验证是否是i2c_adapter
i2c_add_adapter — 声明I2C适配器,系统动态分配总线号。
i2c_add_numbered_adapter — 同样是声明I2C适配器,但是指定了总线号,GPL
i2c_del_adapter — 卸载I2C适配器
i2c_del_driver — 卸载I2C设备驱动
i2c_use_client — i2c_client引用数+1
i2c_release_client — i2c_client引用数-1
__i2c_transfer — 没有自动持锁(adapter lock)的I2C传输接口
i2c_transfer — 自动持锁的I2C传输接口
i2c_master_send — 单条消息发送
i2c_master_recv — 单条消息接收
i2c_smbus_read_byte — SMBus “receive byte” protocol
i2c_smbus_write_byte — SMBus “send byte” protocol
i2c_smbus_read_byte_data — SMBus “read byte” protocol
i2c_smbus_write_byte_data — SMBus “write byte” protocol
i2c_smbus_read_word_data — SMBus “read word” protocol
i2c_smbus_write_word_data — SMBus “write word” protocol
i2c_smbus_read_block_data — SMBus “block read” protocol
i2c_smbus_write_block_data — SMBus “block write” protocol
i2c_smbus_xfer — execute SMBus protocol operations

简单地测试SH1107:

终端窗口
# 设备树dtsi中添加
i2c0: i2c@1C27000 {
compatible = "allwinner,sun6i-a31-i2c";
reg = <0x01C27000 0x400>;
interrupts = <7>;
clocks = <&ccu CLK_BUS_I2C0>;
resets = <&ccu RST_BUS_I2C0>;
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
};
i2c0_pins: i2c0 {
pins = "PE11", "PE12";
function = "i2c0";
};
# dts中添加
&i2c0 {
pinctrl-0 = <&i2c0_pins>;
pinctrl-names = "default";
status = "okay";
oledscreen: sh1107@3c {
compatible = "solomon,sh1107";
reg = <0x3c>;
//interrupt-parent = <&pio>;
//interrupts = <0 22 IRQ_TYPE_EDGE_FALLING>;
//gpios = <&pio 4 2 GPIO_ACTIVE_HIGH>; // PE2 for reset, 4 is the bank number, 2 is the pin number
width = <128>;
height = <80>;
};
# 如下oled.sh
i2cset -y 0 0x3c 0x00 0xae
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0x10
i2cset -y 0 0x3c 0x00 0x20
i2cset -y 0 0x3c 0x00 0x81
i2cset -y 0 0x3c 0x00 0x6f
i2cset -y 0 0x3c 0x00 0xa0
i2cset -y 0 0x3c 0x00 0xc0
i2cset -y 0 0x3c 0x00 0xa4
i2cset -y 0 0x3c 0x00 0xa6
i2cset -y 0 0x3c 0x00 0xd5
i2cset -y 0 0x3c 0x00 0x91
i2cset -y 0 0x3c 0x00 0xd9
i2cset -y 0 0x3c 0x00 0x22
i2cset -y 0 0x3c 0x00 0xdb
i2cset -y 0 0x3c 0x00 0x3f
i2cset -y 0 0x3c 0x00 0xa8
i2cset -y 0 0x3c 0x00 0x4f
i2cset -y 0 0x3c 0x00 0xd3
i2cset -y 0 0x3c 0x00 0x68
i2cset -y 0 0x3c 0x00 0xdc
i2cset -y 0 0x3c 0x00 0x00
i2cset -y 0 0x3c 0x00 0xad
i2cset -y 0 0x3c 0x00 0x8a
i2cset -y 0 0x3c 0x00 0xaf
i2cset -y 0 0x3c 0x00 0xaf

2)理解I2C设备驱动框架,主要围绕四个结构体去分析

  • struct i2c_algorithm:提供I2C协议的实现的操作,如master_xfer实现数据收发的最基本方法。
  • struct i2c_adapter:每一个i2c_adapter都代表一个I2C物理接口,一个cpu可以有多个I2C接口(i2c_adapter),i2c_algorithm就是为i2c_adapter提供I2C协议的实现。每增加一个I2C接口,即是向i2c_bus_type (i2c_core.c)注册一个i2c_adapter
  • struct i2c_driver:代表着一类I2C从机设备的驱动,比如:at24cxx的驱动,不同类型的I2C从机需要注册不同的i2c_driver,如:ssd1306的驱动不同于at24cxx的驱动。每增加一个类型的I2C从机设备,都要向i2c_bus_type (i2c_core.c)注册一个i2c_driver
  • struct i2c_client:代表具体的某一个I2C从机设备,如:at24cxx系列的设备,有at24c01,at24c02等,每增加一个at24cxx设备,都要注册一个i2c_client。只有I2C从机设备被探测到,i2c_client才会被注册。

这四者的关系可以分为:i2c_algorithm和i2c_adapter一起驱动I2C总线,i2c_driver和i2c_client一起实现设备驱动。 注:linux目前只支持I2C主机模式。内核源码提供i2c-algo-bit.c和i2c-gpio.c共同实现IO模拟I2C,i2c-algo-bit.c提供了一个i2c_algorithm,i2c-gpio.c提供了一个i2c_adapter。i2c_driver由驱动开发者根据特定的设备提供。

3)linux中相关结构体定义

struct i2c_client {
unsigned short flags; /* 标志,见下文 */
#define I2C_CLIENT_PEC 0x04 /* 使用分组错误检查 */
#define I2C_CLIENT_TEN 0x10 /* 我们有一个十位的芯片地址 */
/* 必须等于下面的 I2C_M_TEN */
#define I2C_CLIENT_SLAVE 0x20 /* 我们是从设备 */
#define I2C_CLIENT_HOST_NOTIFY 0x40 /* 我们想要使用 I2C 主机通知 */
#define I2C_CLIENT_WAKE 0x80 /* 对于 board_info;为真表示可以唤醒 */
#define I2C_CLIENT_SCCB 0x9000 /* 使用 Omnivision SCCB 协议 */
/* 必须匹配 I2C_M_STOP|IGNORE_NAK */
unsigned short addr; /* 芯片地址 - 注意:7 位 */
/* 地址存储在 _LOWER_ 7 位中 */
char name[I2C_NAME_SIZE]; /* 名称 */
struct i2c_adapter *adapter; /* 我们所在的适配器 */
struct device dev; /* 设备结构 */
int init_irq; /* 初始化时设置的中断 */
int irq; /* 设备发出的中断 */
struct list_head detected; /* 检测到的链表 */
#if IS_ENABLED(CONFIG_I2C_SLAVE)
i2c_slave_cb_t slave_cb; /* 从模式回调 */
#endif
void *devres_group_id; /* 探测 devres 组的 ID */
};
/*
主要成员说明如下:
✔ addr:设备地址,&i2c中的设备节点的<reg>
✔ name:设备名称,I2C 子节点中的节点名@前面的便是 I2C 设备名
✔ adapter:适配器
✔ dev:设备对象
*/
struct i2c_driver {
unsigned int class; /* 类别 */
int (*probe)(struct i2c_client *client, const struct i2c_device_id *id); /* 探测函数 */
int (*remove)(struct i2c_client *client); /* 移除函数 */
int (*probe_new)(struct i2c_client *client); /* 新探测函数 */
void (*shutdown)(struct i2c_client *client); /* 关闭函数 */
void (*alert)(struct i2c_client *client, enum i2c_alert_protocol protocol, unsigned int data); /* 警报函数 */
int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); /* 命令函数 */
struct device_driver driver; /* 驱动结构 */
const struct i2c_device_id *id_table; /* ID 表 */
int (*detect)(struct i2c_client *client, struct i2c_board_info *info); /* 探测函数 */
const unsigned short *address_list; /* 地址列表 */
struct list_head clients; /* 客户端链表 */
};
/*
重点关注probe、remove、shutdown、id_table、driver
*/

✨ 驱动匹配:

const struct of_device_id *of_match_table;

const struct platform_device_id *id_table;

  • id_table:成员是一个 table,用来告诉驱动此时可以匹配多个设备
  • of_match_table: 当 of_match_table 中的成员与设备树中的 compatible 属性相同时,此时内核便会将此驱动和设备树生成的 platform_device 设备绑定,并注册驱动
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx,xxxx"},
{ },
};
MODULE_DEVICE_TABLE(of, xxx_of_match);

该匹配表给出了能够匹配的设备,当设备树中的 compatible 属性值与之相同时,此时就能够触发 probe 并进行驱动的绑定。上面出现了一个 MODULE_DEVICE_TABLE 宏,该宏的作用是告知内核有 xxx_of_match 这个匹配表,这样的好处是当有其他设备动态加载的时候,能够匹配该驱动。

✨ 驱动注册:

static struct i2c_driver xxx_driver = {
.driver = {
.name = "xxx",
.owner = THIS_MODULE,
},
.probe = xxx_probe,
.remove = xxx_remove,
.id_table = xxx_id,
};
// 可以这样来注册该驱动:
module_i2c_driver(xxx_driver);

3)I2C 数据传输

Linux 内核为 I2C 数据传输实现了两套通信方式,一种是简单的 I2C 也叫传统的 I2C 通信方式,另一种为 SMBus(System Management Bus)的通信方式

①传统的 I2C 通信方式

// 内核提供了两个函数供驱动开发者使用
static inline int i2c_master_send(const struct i2c_client *client,const char*buf, int count)
static inline int i2c_master_recv(const struct i2c_client *client, char*buf, int count)
// 从其接口的参数列表可以看到,其使用方式基本可以知道如何使用了,例如发送几个字节的数据到客户端中,可以这样写:
static int i2c_write_le16(struct i2c_client *client, unsigned word)
{
u8 buf[2] = { word & 0xff, word >> 8, };
int status;
status = i2c_master_send(client, buf, 2);
return (status < 0) ? status : 0;
}
// 上面使用了 i2c_master_send 函数来实现数据的发送,第一个参数为 I2C 客户端地址,如果有些函数的形参没有 I2C client,这是有很多种方式获取 I2Cclient,其中一种最简单粗暴的方式就是定义一个*client 指针,该指针在 probe 函数中初始化,这样就可以在其他函数中直接获取 i2c client了。
//当然还有其他方式,例如可以通过 to_i2c_driver()该函数来使用 dev 得到 client,其内部实现使用了 container_of 操作。需要接收数据时,可以使用 i2c_master_recv 函数,例如现在需要接收几个字节的数据,可以这样:
static int adxl34x_i2c_read_block(struct device *dev,unsigned char reg, int count,void*buf)
{
struct i2c_client *client = to_i2c_client(dev);
int ret;
ret = i2c_master_send(client, &reg, 1);
if (ret < 0)
return ret;
ret = i2c_master_recv(client, buf, count);
if (ret < 0)
return ret;
if (ret != count)
return -EIO;
return 0;
}
// 上面利用了 to_i2c_client 函数来将 dev 转换为 client,这个函数实际上就是container_of,获取到了 client 后,就可以先发送寄存器地址,然后读取数据了。
// Linux 内核还提供了一种传输方式,该传输方式即可以发送数据,也可以接收数据:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
/*
adap:i2c 适配器(主机),即i2c总线,可以通过 client 来得到
msgs:i2c 消息
num:消息的个数
可以看到,该函数是的参数不再是简单的 buffer 了,而是 i2c_msg 结构体,先看i2c_msg 这个结构体:
*/
struct i2c_msg {
__u16 addr; // 目标设备的地址
__u16 flags; // 标志位,用于指定消息的特定属性或行为
// 下面是flags可能包含的标志定义
#define I2C_M_RD 0x0001 /* 读操作,保证是0x0001 */
#define I2C_M_TEN 0x0010 /* 10位地址,仅在支持I2C_FUNC_10BIT_ADDR时使用 */
#define I2C_M_DMA_SAFE 0x0200 /* DMA安全,仅在内核空间使用 */
#define I2C_M_RECV_LEN 0x0400 /* 接收长度,仅在支持I2C_FUNC_SMBUS_READ_BLOCK_DATA时使用 */
#define I2C_M_NO_RD_ACK 0x0800 /* 不发送读确认,仅在支持I2C_FUNC_PROTOCOL_MANGLING时使用 */
#define I2C_M_IGNORE_NAK 0x1000 /* 忽略不应答(NAK),仅在支持I2C_FUNC_PROTOCOL_MANGLING时使用 */
#define I2C_M_REV_DIR_ADDR 0x2000 /* 反转方向地址,仅在支持I2C_FUNC_PROTOCOL_MANGLING时使用 */
#define I2C_M_NOSTART 0x4000 /* 不发送开始信号,仅在支持I2C_FUNC_NOSTART时使用 */
#define I2C_M_STOP 0x8000 /* 发送停止信号,仅在支持I2C_FUNC_PROTOCOL_MANGLING时使用 */
__u16 len; // 消息的长度,即buf中字节的数量
__u8 *buf; // 指向消息数据的指针
};
// 上面是 i2c_msg 结构体的具体内容,其中 addr 表示 I2C 客户端地址,即从设备地址;
// flags 表示信息标志,即传输方向,0表示写数据,1(I2C_M_RD)读数据,还有其他标志位请参考上面的结构体中的宏;
// len 表示传输数据长度,buf表示传输数据过程中数据保存的地址。下面来看一个简单的例子:
int i2c_write_and_read_data(i2c_client *client, unsigned int *send_buf, unsigned int *recv_buf)
{
struct i2c_msg msgs[2];
msgs[0].addr = client->addr; //获取客户端(从机地址)
msgs[0].len = sizeof(send_buf);
msgs[0].buf = &send_buf;
msgs[0].flags = 0; //传输为发送数据
msgs[1].addr = client->addr; //获取客户端(从机地址)
msgs[1].len = 10;
msgs[1].buf = recv_buf;
msgs[1].flags = I2C_M_RD; //传输为接收数据
ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (ret < 0)
return ret;
else if (ret != ARRAY_SIZE(msgs))
return -EIO;
return 0;
}
// 上面的代码实现了数据的发送和接收,需要注意的是,发送数据和接收数据的客户端地址可以通过 client 获取,但是这里有一个前提,就是需要在 probe 函数中设置 client 地址数据,读者应该还有印象,在上面的 probe 函数中提到过 i2c_set_clientdata 这个函数,该函数可以将获得的客户端的一些数据地址保存到 client中的空指针中,这样需要数据时就可以直接通过 client 来获取了。

②系统管理总线数据传输(SMBus)

SMBus 是 1995 年由 Intel 提出的,该总线与 I2C 总线基本一致,但在 I2C 总线上做了拓展,用过 AVR 单片机的读者可能会对 TWI 总线不陌生,TWI 总线也是在I2C 总线的基础上做了拓展,总的来说,SMBus 和 TWI 总线都是兼容 I2C 总线的,反过来则不然。

/**
* 读取I2C设备一个字节的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @return 返回读取到的一个字节数据,或者在出错时返回负错误码。
*/
s32 i2c_smbus_read_byte(const struct i2c_client *client);
/**
* 向I2C设备写入一个字节的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param value 要写入的一个字节数据。
* @return 在成功时返回0,出错时返回负错误码。
*/
s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value);
/**
* 读取I2C设备指定寄存器的一个字节数据。
* 需要先发送一个命令字节,通常是寄存器的地址。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要读取的寄存器的命令或地址。
* @return 返回读取到的一个字节数据,或者在出错时返回负错误码。
*/
s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command);
/**
* 向I2C设备的指定寄存器写入一个字节的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要写入的寄存器的命令或地址。
* @param value 要写入的一个字节数据。
* @return 在成功时返回0,出错时返回负错误码。
*/
s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value);
/**
* 读取I2C设备指定寄存器的一个字(两个字节)数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要读取的寄存器的命令或地址。
* @return 返回读取到的一个字(两个字节)数据,或者在出错时返回负错误码。
*/
s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command);
/**
* 向I2C设备的指定寄存器写入一个字(两个字节)的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要写入的寄存器的命令或地址。
* @param value 要写入的一个字(两个字节)数据。
* @return 在成功时返回0,出错时返回负错误码。
*/
s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command, u16 value);
/**
* 从I2C设备的指定寄存器读取多个字节的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要读取的寄存器的命令或地址。
* @param values 指向一个字节数组的指针,用于存储读取的数据。
* @return 在成功时返回读取的字节数,出错时返回负错误码。
*/
s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command, u8 *values);
/**
* 向I2C设备的指定寄存器写入多个字节的数据。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要写入的寄存器的命令或地址。
* @param length 要写入的字节数。
* @param values 指向包含要写入数据的字节数组的指针。
* @return 在成功时返回0,出错时返回负错误码。
*/
s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
/**
* 从I2C设备的指定寄存器读取多个字节的数据,使用I2C协议的Block Read。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要读取的寄存器的命令或地址。
* @param length 要读取的字节数。
* @param values 指向一个字节数组的指针,用于存储读取的数据。
* @return 在成功时返回读取的字节数,出错时返回负错误码。
*/
s32 i2c_smbus_read_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, u8 *values);
/**
* 向I2C设备的指定寄存器写入多个字节的数据,使用I2C协议的Block Write。
* @param client 指向i2c_client结构的指针,表示I2C设备。
* @param command 要写入的寄存器的命令或地址。
* @param length 要写入的字节数。
* @param values 指向包含要写入数据的字节数组的指针。
* @return 在成功时返回0,出错时返回负错误码。
*/
s32 i2c_smbus_write_i2c_block_data(const struct i2c_client *client, u8 command, u8 length, const u8 *values);
// 示例
char buf[2] = {0xC5};
ret = i2c_smbus_write_i2c_block_data(client, 0xA3 , 1, buf);
// 的第一个参数为 i2c 客户端,第二个参数为寄存器地址,第三个参数为写入数据的长度(单位字节),第四个参数为写入数据地址。

(1)设备驱动地址

The TWI Controller includes the following features:  Software-programmable for Slave or Master  Support Repeated START signal  Multi-master systems supported  Allow 10-bit addressing with TWI bus

设备驱动的地址就是有10-bit的编址能力,编制通过TWI Slave Address Register实现。

(2)