第
8
章
I2
C
协议
I2
C/SMBus
是什么?
I2
C
是广泛用于桌面和笔记本电脑中的串行总线,用于处理器和一些外设之间的接口,这些外设包括
EEPROM
、音频编解码器
以及监控温度、供电电压等参数的专用芯片。此外,
I2
C
也在嵌入式设备中大行其道,用于和
RTC
,智能电池电路、多路复用器,端口扩展卡,光收发器,以及其它类似设备之间的通信。由于
I2
C
被大量的微控制器所支持,在当前的市场上可找到大量便宜的
I2
C
设备。
I2
C
和
SMBus
为主-从协议,其通信双方为主机适配器(主控制器)和客户设备(从设备)。主机控制器在桌面电脑上通常为南桥芯片组的一部分;而在嵌入式设备上,通常为微控制器的一部分。图
8.1
显示了在
PC
兼容硬件上
I2
C
总线的例子。
图
8.1. PC
兼容硬件上
的
I2
C/SMBus.
I2
C
及其子集
SMBus
最初分别为
Philips
和
Intel
所开发,均为
2
线接口
。这
2
根线为时钟线和双向数据线,分别被称为串行时钟(
Serial Clock
,
SCL
)和串行数据(
Serial Data
,
SDA
)。由于
I2
C
总线仅需要一对总线,因此在电路板上占用了更少的空间。因此带来的问题是带宽较窄。
I2
C
在标准模式下支持最高
100Kbps
的传输率,在快速模式下最高可达
400Kbps
(然而,
SMBus
最高仅支持
100Kbps
)。因此它们仅适用于慢速设备。即使
I2
C
支持双向数据交换,因为仅有一根数据线,故通信是半双工的。
I2
C
和
SMBus
设备使用
7
位地址。协议也支持
10
位地址,但很多设备仅响应
7
位地址,故在总线上最多有
127
个设备。源于协议的主-从特性,设备地址也称为从地址。
I2
C
核心
I2
C
核心由主机适配器驱动和客户驱动可利用的函数和数据结构组成。核心中的公共代码简化了驱动开发者的工作。核心也间接使客户驱动独立于主机适配器,以使客户设备即使用于采用不同
I2
C
主机适配器的电路板上,亦可保持客户驱动运行如常。核心层的此机制及其好处也可在内核中其它的很多设备驱动类中发现,如
PCMCIA
,
PCI
和
USB
等。
除了核心,内核的
I2
C
底层由如下组成:
·
I2
C
主机适配器的设备驱动。属于总线驱动,通常由适配器驱动和算法(
algorithm
)驱动组成。前者利用后者和
I2
C
总线交互。
·
I2C
客户设备的设备驱动。
·
i2c-dev
,允许在用户模式下实现
I2
C
客户驱动。
你更可能的是实现客户驱动,而不是适配器或
algorithm
驱动,因为相比于
I2
C
主机适配器,有多得多的
I2C
设备。因此在本章中,我们将主要讨论客户驱动。
图
8.2
展示了
Linux
的
I2
C
子系统。它显示了
I2
C
内核模块和
I2
C
总线上的主机适配器和客户设备的交互。
图
8.2. Linux I2
C
子系统
由于
SMBus
是
I2
C
的子集,因此仅使用
SMBus
指令和你的设备交互的驱动可工作于
SMBus
和
I2
C
适配器。表
8.1
列出了
I2
C
核心提供的和
SMBus
兼容的数据传输流程。
总线事务
在实现驱动例子之前,通过放大镜观察导线
,让我们来更好的理解
I2
C
协议。清单
8.1
展示了和
I2
C EEPROM
交互的代码片断,以及在总线上发生的相应的事务。这些事务是在运行代码片断时通过相连的
I2
C
总线分析仪捕获的。这些代码使用的时用户模式的
I2
C
函数(在第
19
章
“
用户空间的驱动
”
中,我们将讨论更多的用户模式的
I2
C
编程)。
清单
8.1. I2
C
总线上的事务
Code View: /* ... */ /* ioctl(smbus_fp, 0x50, slave);
/* Write a byte (0xAB) at memory offset 0 on the EEPROM */ i2c_smbus_write_byte_data(smbus_fp, 0, 0xAB);
/* /* Read a byte from offset 0 on the EEPROM */ res = i2c_smbus_read_byte_data(smbus_fp, 0);
/*
|
设备例子:
EEPROM
我们的第一个客户驱动例子时
I2
C
总线上的
EEPROM
设置,如图
8.1
所示。几乎所有的笔记本和桌面电脑都有类似的
EEPROM
,用于存储
BIOS
配置信息。例子中的
EEPROM
有两个
memory bank
。相对应于每个
memory bank
,驱动提供
/dev
接口:
/dev/eep/0
和
/dev/eep/1
。应用程序在这些节点上操作,和
EEPROM
交换数据。
每个
I2
C/SMBus
客户设备都分配有一个从地址,作为设备标识。例子中的
EEPROM
有两个从地址,
SLAVE_ADDR1
和
SLAVE_ADDR2
,分别对应于两个
memory bank
。
例子中驱动所使用的
I2
C
指令和
SMBus
兼容,因此它可以工作于
I2
C
和
SMBus EEPROM
。
初始化
正如所有的驱动类那样,
I2
C
客户驱动也有
init()
入口点。初始化用于分配数据结构,向
I2
C
核心注册驱动,将
sysfs
和
Linux
设备模型联系在一起。这些在清单
8.2
中完成。
清单
8.2.
初始化
EEPROM
驱动
Code View: /* Driver entry points */ static struct file_operations eep_fops = { };
static dev_t dev_number; static struct class *eep_class;
/* Per-device client data structure for each
struct eep_bank { };
#define NUM_BANKS 2 #define BANK_SIZE 2048
struct ee_bank *ee_bank_list;
/* int __init eep_init(void) {
|