初学I2C的时候,因为第一次接触到这么一种驱动体系,与之前学习的普通字符设备驱动、platform驱动还是有一些区别的。在看内核的I2C有关源代码的时候确实感到不太能理清楚它的框架,还好网上的大神们分享的知识让我学到了很多。因此现在先对I2C有一个大致的学习,往后在做到相关的项目时再来加深、巩固。
1、I2C基础知识
1.1 概述
IIC (也称I2C)即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司在八十年代初设计出来的,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。
1.2 i2c总线的信号线
I2C总线只有两根双向信号线。一根是数据线SDA,另一根是时钟线SCL。I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。
从上图可以看到,I2C是可以挂很多设备的。
既然只有这两根“简单”的线,那么它们是怎么控制设备的通信呢?
请看说明:
起始信号(start):当SCL为高期间,SDA由高到低的跳变;启动信号是一种电平跳变时序信号,而不是一个电平信号。
停止信号(stop):当SCL为高期间,SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
起始信号和停止信号都由主机发出,在起始信号开始后,总线就处于被占用状态;在停止信号产生后,总线就处于空闲状态。连接到I2C总线上的设备,如果具有I2C总线的硬件接口,很容易监测到起始和停止止信号。
1.3 字节传送与ACK
规定传送的字节长度为8bit,每一个被传送字节后面必须跟一个应答位ACK(即一帧共9个bit)。ACK由接收器产生。
ACK为低电平时,规定是有效应答,表示接收器成功接收到该字节。ACK为高电平时,就是无效应答,即表示接收器未成功收到该字节。对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。
1.4 数据的有效性
I2C进行数据传输时,SCL为高电平期间,SDA上的数据必须保持稳定,此时的数据才是有效的。在SCL是低电平期间,SDA的高电平、低电平状态才允许变化。这是为了保证数据传输时的稳定,否则可能误发了停止信号而导致数据未传完就终止了。
1.5 I2C总线的寻址
I2C总线协议规定,使用7位寻址字节(寻址字节是起始信号后的第一个字节)。
D7~D1位组成从机的地址。D0位是数据传送方向位。”0”表示主机向从机写数据,”1”表示主机向从机读数据。
当然,现在I2C为了满足更大的寻址空间和更快数据传输速率,它也进行了加强。增强到了快速模式(400Kbits/s)和10位寻址,以及有了高速模式,其速度可达3.4Mbits/s。使得I2C能支持高速串行传输应用,如:EEPROM和Flash存储器。
2、Linux下的I2C驱动子系统
I2C体系结构分为三个部分:I2C总线驱动、I2C核心层、I2C设备驱动
I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可由CPU控制,甚至可以直接集成在CPU内部。包含了i2c_adapter、i2c_algorithm和控制I2C适配器产生通信信号的函数。通过I2C总线驱动的代码,我们可以控制I2C适配器以主控方式产生开始位、停止位读写周期,以及以从设备方式被读写,产生ACK等。
I2C核心层提供了I2C总线驱动和设备驱动的注册、注销方法,I2C通信方法上层的、与具体适配器无关的代码以及探测设备、检测设备地址的上层代码等。
I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备一般挂接在受CPU控制的I2C适配器上,通过I2C适配器与CPU交换数据。包含了i2c_driver和i2c_client。
看图就比较清晰了:
在硬件上,可能有一个或一个以上的设备可以支持I2C,同样,也会有一个或多个适配器。在上图可以看到,一个适配器可以控制多个从设备。所以对于这些设备,我们可以有两种方法来操作它们:1.直接编写从设备的驱动来操作它。2.通过操作适配器来操作从设备。
在我们的s3c2440上,有其对应的I2C适配器驱动(i2c-s3c2410.c 基于platform实现)。——I2C总线驱动。
挂在I2C总线上的从设备是AT24C02,也就是EEPROM,它也有自己的一个驱动(at24.c)。——I2C设备驱动。
在linux内核源代码中的driver下包含有I2C的目录:
i2c-core.c这个文件实现了I2C核心的功能以及/proc/bus/i2c*接口。
i2c-dev.c实现了I2C适配器设备文件的功能,每一个I2C适配器都被分配一个设备。通过适配器访设备时的主设备号都为89,次设备号为0-255。
I2c-dev.c并没有针对特定的设备而设计,只是提供了通用的read(),write(),和ioctl()等接口,应用层可以借用这些接口访问挂接在适配器上的I2C设备的存储空间或寄存器,并控制I2C设备的工作方式。
busses文件夹这个文件中包含了一些I2C总线的驱动,如针对S3C2410,S3C2440,S3C6410等处理器的I2C控制器驱动为i2c-s3c2410.c.
algos文件夹实现了一些I2C总线适配器的algorithm。
一些具体的结构体,函数分析可以参考:http://blog.csdn.net/wangpengqi/article/details/17711165
以及:http://blog.csdn.net/fulinus/article/details/9008191
3、s3c2440上EEPROM的操作
3.1 内核添加支持
make menuconfig
选择如下选项:
Device Drivers —>
<*> I2C support —>
— I2C support
[*] Enable compatibility bits for old user-space
<*> I2C device interface
< > I2C bus multiplexing support
[*] Autoselect pertinent helper modules
I2C Hardware Bus support —>
[ ] I2C Core debugging messages
[ ] I2C Algorithm debugging messages
[ ] I2C Bus debugging messages
Device Drivers —>
[*] Misc devices —>
EEPROM support —>
3.2 代码添加
[tangbin@localhost linux-3.0]$ vim arch/arm/mach-s3c2440/mach-smdk2440.c
添加如下代码:
#include <linux/i2c/at24.h>
static struct at24_platform_data at24c02 = {
.byte_len = SZ_2K / 8,
.page_size = 8,
.flags = 0,
};
static struct i2c_board_info __initdata smdk_i2c_devices[] = {
/* more devices can be added using expansion connectors */
{
I2C_BOARD_INFO("24c02", 0x50),
.platform_data = &at24c02,
},
};
在smdk2440_machine_init函数中添加:
i2c_register_board_info(0,smdk_i2c_devices,ARRAY_SIZE(smdk_i2c_devices));
3.3 AT24C02的设备地址
查看底板原理图,以及帮助手册可知:
地址为0b 01010000(0x50)。
手册中at24c02的设备地址是0b 1 0 1 0 0 0 0 R/W, 其最低位是读写标志位,在Linux中,I2C设备地址的最高位为0,而低七位地址就是手册中去掉R/W的剩余7位。因此,地址为0b 01010000(0x50)。
3.4 开发板查看
重新make编译内核后,烧到开发板,在开发板上可以查看:
>: cd sys/devices/platform/s3c2440-i2c/i2c-0/0-0050/
>: ls
driver eeprom modalias name power subsystem uevent
>:
>>: cat name
24c02
>: cat eeprom
!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~>:
编写I2C设备驱动的两种方法:
1.利用系统给我们提供的i2c-dev.c来实现一个i2c适配器的设备文件。在用户空间操作I2C适配器控制I2C设备。
2.使用I2C设备自己的驱动来操作。
两种方法的详细使用可以参考:http://blog.csdn.net/u010944778/article/details/46807737
这篇博文提供了两个实例,并且对其中的一些问题也提出了解答,如:AT24C02的相关知识,读写分析等。对我的启发很大,学习到不少!重点推荐!!