让大家最快的了解I2C通信、I2C驱动和I2C读写,如有错误,还请指正。
百度网盘下载地址 http://pan.baidu.com/s/1kT7GBDd
DOC文档中有图片,博文的图片就不传了。
一 I2C 概述
I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,是微电子通信控制领域广泛采用的一种总线标准,串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s。具有接口线少,控制方式简单,广泛的应用在各种电路中。比如对E2PROM(AT2402 芯片)的读写,公司使用的是ARM+FPGA的结构使用的是I2C通信,所以I2C起到了很重要的作用。
二 I2C 总线协议简述
I2C总线只有两根线,SCK(时钟) SDA(数据),I2C 是基于multi master机制,一条总线上可允许多个master。
图 I2C开始停止时序
I2C 总线数据的读写如图
图 I2C读写分析
I2C在读的时候先是一个START信号 再传输7位设备地址(E2PROM的设备地址 原理图上有) 先传输的是最高位最后是最低位,第8位是R/W位,0表示写,1表示读。第9位是ACK应答位,在数据成功的读写后,ACK会置高表示成功。传输完7位设备地址后接着同样传输寄存器地址(E2PROM的寄存器地址)可能是 1 byte ,2 byte 等,再是写入的 N byte数据 ,最后发送STOP信号结束通信。要强调的是 一般先写入设备地址,再写入寄存器地址,再写入数据。有少数的是 寄存器地址和数据在一块,只需要写入设备地址和数据。还要说明的是 这里使用是 7位的寻址,还有10位的寻址,10位的寻址是兼容7位的。
好了,原理性的东西有这些就够了。
三 I2C linux驱动分析
我们从最上层开始,抓住I2C读写的主线进行分析,让过程简单易懂。
图 I2C读写调用过程
首先在用户模式下打开一个I2C设备获得文件句柄,然后使用read write进行读写,在驱动中(drivers/i2c/i2c_dev.c)I2C驱动中file_operations结构体指向了i2cdev_read和i2cdev_write, 同时在 drivers/i2c/i2c_dev.c文件中有
static int __init i2c_dev_init(void);
static void __exit i2c_dev_exit(void);
驱动程序加载卸载函数,在 i2c_dev_init中 使用register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops); 注册了主设备号位89的设备。
在 i2c_dev_exit 函数中使用了 unregister_chrdev对设备卸载(这些是不是很熟悉)。
我们继续看 i2c_dev_read函数,在函数中调用了i2c_master_recv ,同样 i2c_dev_write函数调用的是 i2c_master_send, i2c_dev_write和 i2c_master_send位于 drivers/i2c/i2c-core.c.
在 i2c_dev_write和 i2c_master_send函数中都会使用 i2c_transfer完成数据传输,i2c_transfer 函数用于进行I2C适配器和I2C设备之间的一组消息交互,i2c_master_send函数和i2c_master_recv 函数会调用i2c_transfer()函数分别完成一条写消息和一条读消息。在 i2c_tansfer函数中有struct i2c_adapter *adap 结构体,adap->algo->master_xfer 指向不同平台的I2C实现函数,我们这里用是的 IT AM335X系列,最后就是调用的 位于 /drivers/i2c/bussess/i2c-omap.c 文件中的omap_i2c_xfer函数,不同的平台最后调用的函数不一样。在 omap_i2c_xfer函数中调用omap_i2c_xfer_msg完成底层的操作。
上面从函数调用的过程分析了I2C的读写,下面我们从层次上分析I2C驱动。
Linux的I2C构架分为三个部分:
1)I2C core框架
提供了核心数据结构的定义和相关接口函数,用来实现I2C适配器驱动和设备驱动的注册、注销管理,以及I2C通信方法上层的、与具体适配器无关的代码,为系统中每个I2C总线增加相应的读写方法。I2C core框架具体实现在/drivers/i2c目录下的i2c-core.c和i2c-dev.c
2) I2C总线驱动
定义描述具体I2C总线适配器的i2c_adapter数据结构、实现在具体I2C适配器上的I2C总线通信方法,并由i2c_algorithm数据结构进行描述。 经过I2C总线驱动的的代码,可以为我们控制I2C产生开始位、停止位、读写周期以及从设备的读写、产生ACK等。
I2C总线驱动具体实现在/drivers/i2c目录下busses文件夹。例如:Linux I2C GPIO总线驱动为i2c_gpio.c. I2C总线算法在/drivers/i2c目录下algos文件夹。例如:Linux I2C GPIO总线驱动算法实现在i2c_algo_bit.c.
3) I2C 设备驱动
是对具体I2C硬件驱动的实现。I2C 设备驱动通过I2C适配器与CPU通信。其中主要包含i2c_driver和i2c_client数据结构,i2c_driver结构对应一套具体的驱动方法,例如:probe、remove、suspend等,需要自己申明。i2c_client数据结构由内核根据具体的设备注册信息自动生成,设备驱动根据硬件具体情况填充。具体使用下面介绍。
I2C 设备驱动具体实现放在在/drivers/i2c目录下chips文件夹。
最后用上图表示所有的层次关系。
好了,驱动大致知道这么多久可以了。有关I2C驱动的更详细的介绍
http://www.360doc.com/content/13/0715/21/9171956_300228309.shtml
四 I2C 读写
有了I2C的驱动,我们对I2C的读写使用open read write ioctl close就可以了。
需要包含的头文件
#include <linux/i2c-dev.h>
#include <linux/i2c.h>
//打开I2C设备
int fd_i2c_dev = open("/dev/i2c-2",O_RDWR);
//I2C初始化
//使用7位地址
ioctl(fd_dev,I2C_TENBIT,0);
//失败重复次数 2 次
ioctl(fd_dev,I2C_RETRIES,2);
//超时50 ms
ioctl(fd_dev,I2C_TIMEOUT,50);
封装i2c_read和i2c_write读写函数
/*
*>
struct i2c_msg i2c_msg[2];
struct i2c_rdwr_ioctl_data ioctl_data;
ioctl_data.msgs = i2c_msg;
ioctl_data.nmsgs = 2;
//write 8-bit register address
tmpbuf = register_addr;
i2c_msg[0].addr = i2c_devaddr;
i2c_msg[0].flags = 0;
i2c_msg[0].buf = &tmpbuf;
i2c_msg[0].len = 1;
//read date from i2c store in tmpbuf
i2c_msg[1].addr = i2c_devaddr;
i2c_msg[1].flags = 1;
i2c_msg[1].buf = buf;
i2c_msg[1].len = count;
int rt_val = 0;
rt_val = ioctl(fd_i2c,0x707,&ioctl_data);
if ( rt_val < 0 ){
printf(" read i2c error in i2c_read_A8 function \n");
}
return rt_val;
}
/*
*>
static unsigned char tmpbuf[1024] = { '\0' };
tmpbuf[0] = register_addr;
if ( count >= 1024 ){
return -1;
}
memcpy(tmpbuf+1,buf,count);
rt_val = ioctl(fd_i2c,I2C_SLAVE,i2c_devaddr);
if ( rt_val < 0 ){
printf(" ioctl i2c in ioctl function m_i2c.c\n");
}
rt_val = write(fd_i2c,tmpbuf,count+1);
if ( rt_val < 0 ){
printf(" write i2c error in i2c_write_A8 function m_i2c.c \n");
}
return rt_val;
}
好了直接调用 i2c_read_A8就可以进行I2C读操作了
eg:
//从设备地址0x10 寄存器地址0x1 读1个字节
char tmp_buf;
i2c_write_A(0x10,0x1,1,&tmp_buf,1);