i2c驱动程序总结
51单片机的I2C底层驱动程序(IO口模拟)

51单片机的I2C底层驱动程序(IO口模拟)/*Title:I2C for 80C51Author:yuyouliang51单片机(本人使用STC89C52单片机,12T模式)的I2C驱动程序,使用逻辑分析仪对该协议进行分析,发现波形比较美观,SCL 的频率在70KHz左右(11.0592M晶振),低于标准的100K,可以适应大多数的I2C器件。
如果感觉速度过快或过慢,可以自行修改延时。
希望可以给读者一个参考,给读者一些帮助!*//*i2c.h文件 */#ifndef __I2C_H_#define __I2C_H_sbit SCL = P2^1;sbit SDA = P2^0;void start_i2c(); //启动I2C总线:SCL高电平期间,SDA由高变低void stop_i2c(); //停止I2C总线:SCL高电平期间,SDA由低变高void send_i2c(unsigned char c); //主机发送一个字节,先发送最高位unsigned char receive_i2c(); //主机接收一个字节,先接收最高位void master_ack(bit ack); //主机非应答信号(填参数0)或应答信号(填参数1)void slave_ack(); //等待从机应答信号#endif/* i2c.c文件 */#include#include#include#define nop() _nop_()void start_i2c() //启动I2C总线:SCL高电平期间,SDA由高变低{SDA=1;SCL=1;nop();nop();nop();nop();SDA=0;SCL=0;}void stop_i2c() //停止I2C总线,SCL高电平期间,SDA由低变高{SDA=0;SCL=1;nop();nop();nop();nop();SDA=1;}void slave_ack() //等待从机应答信号,如果从机迟迟没有应答,则结束总线。
Linux下I2C驱动介绍

1、I2C概述I2C是philips公司提供的外设总线,I2C有两条数据线,一条是串行数据线SDA、一条是时钟线SCL,使用SDA和SCL实现了数据的交换,便于布线。
I2C总线方便用在EEPROM、实时钟、小型LCD等与CPU外部的接口上。
2、Linux下的驱动思路Linux系统下编写I2c驱动主要有两种方法:一种是把I2C当做普通字符设备来使用;另一种利用Linux下驱动的体系结构来实现。
第一种方法:优点:思路比较直接,不用花费大量时间去了解Linux系统下I2C体系结构缺点:不仅对I2C设备操作要了解,还有了解I2C的适配器操作不仅对I2C设备器和设备操作需要了解,编写的驱动移植性差,内核提供的I2C设备器都没有用上。
第二种方法:第一种的优点就是第二种的缺点,第一种的缺点就是第二种的优点。
3、I2C框架概述Linux的I2C体系结构分为3部分:1)I2C核心I2C核心提供了I2C总线驱动和设备驱动的注册和注销的方法,I2C 通信方法(algorithm)上层,与具体适配器无关的代码,检测设备上层的代码等。
2)I2C总线驱动I2C总线驱动是对I2C硬件体系结构中适配器端的实现,适配器可以直接受CPU来控制。
3)I2C设备驱动I2C设备驱动是对I2C硬件体系结构中设备端的实现,设备端挂在受CPU控制的适配器上,通过I2C适配器与CPU交换数据。
Linux下的I2C体系结构:1)Linux下的I2C体系结构4、I2C设备驱动编写方法首先让我们明白适配器驱动的作用是让我们能够通过它发出标准的I2C时序,在linux内核源代码中driver/I2C/buss包含一些适配器的驱动,例如s3c2410的驱动I2C-s3c2410.c,适配器被加载到内核中,接下的任务就是实现设备驱动的编写。
编写设备驱动的方法主要分为两种方法:第一种:利用设备提供的I2C-dev.c来实现I2C适配器设备文件,然后通过上层应用程序来操作I2C设备器来控制I2C设备。
详细讲解RT-Thread I2C设备驱动框架及相关函数

详细讲解RT-Thread I2C设备驱动框架及相关函数本应用笔记以驱动I2C接口的6轴传感器MPU6050为例,说明了如何使用I2C设备驱动接口开发应用程序,并详细讲解了RT-Thread I2C设备驱动框架及相关函数。
1 本文的目的和结构1.1 本文的目的和背景I2C(或写作i2c、IIC、iic)总线是由Philips公司开发的一种简单、双向二线制(时钟SCL、数据SDA)同步串行总线。
它只需要两根线即可在连接于总线上的器件之间传送信息,是半导体芯片使用最为广泛的通信接口之一。
RT-Thread中引入了I2C设备驱动框架,I2C 设备驱动框架提供了基于GPIO模拟和硬件控制器的2种底层硬件接口。
1.2 本文的结构本文首先描述了RT-Thread I2C设备驱动框架的基本情况,然后详细描述了I2C设备驱动接口,并使用I2C设备驱动接口编写MPU6050的驱动程序,并给出了在正点原子STM32F4探索者开发板上验证的代码示例。
2 I2C设备驱动框架简介在使用MCU进行项目开发的时候,往往需要用到I2C总线。
一般来说,MCU带有I2C 控制器(硬件I2C),也可以使用MCU的2个GPIO自行编写程序模拟I2C总线协议实现同样的功能。
RT-Thread提供了一套I/O设备管理框架,它把I/O设备分成了三层进行处理:应用层、I/O 设备管理层、底层驱动。
I/O设备管理框架给上层应用提供了统一的设备操作接口和I2C 设备驱动接口,给下层提供的是底层驱动接口。
应用程序通过I/O设备模块提供的标准接口访问底层设备,底层设备的变更不会对上层应用产生影响,这种方式使得应用程序具有很好的可移植性,应用程序可以很方便的从一个MCU移植到另外一个MCU。
本文以6轴惯性传感器MPU6050为例,使用RT-Thread I2C设备驱动框架提供的GPIO模拟I2C控制器的方式,阐述了应用程序如何使用I2C设备驱动接口访问I2C设备。
I2C之AT24C04总结

I2C之AT24C04总结济南职业学院电子工程系朱志强1、AT24C04介绍2、AT24C04之准备工作3、AT24C04之小试牛刀4、对应源程序2010年7月28日1、AT24C04介绍关于I2C的介绍,这里就不用说了,直接介绍24C04了。
24C04是4K位串行CMOSE2PROM。
引脚的认识:SCL串行时钟引脚SDA串行数据/地址A0、A1、A2器件地址输入端WP写保护(WP管脚连接到Vcc,所有的内容都被写保护(只能读)。
当WP管脚连接到Vss或悬空,允许器件进行正常的读/写操作。
)2、AT24C04之准备工作首先,我们先查看一下实验板上面的接线图。
如图1所示。
图124c04连接图我们要注意的第一点是器件地址全部是0,即接地处理。
第二点是读写保护WP 接地,意味着我们可以随意存取。
第三点是我们要用到的引脚连接到了P3^6和P3^7上。
在这里还要提醒一下,就是引脚上一定要有上拉电阻!阻值在470~1k都可以的,具体的数值可以参考相关的手册。
在程序里我们需要先做以下定义:sbitAT24C04_SCL=P3^7;sbitAT24C04_SDA=P3^6;在写这个程序的时候,要使用到键盘,不用太多按键,我们暂时只用四个。
把实验板上面的跳线JP8接到“-”端上,使第一行的按键变为独立键盘就可以了。
线路图如图2所示。
图2键盘部分电路图键盘这部分我就不说了吧,直接附上我用到的这部分程序,在我的程序中,并没有判断按键是否松开,而是使用的延时,这样的好处是一直按着按键,数据会一直在变化,要不然,频繁的按真的很累人。
转到按键程序对于里面用到的延时函数,一个是US级延时函数,一个是ms级延时函数,分别调用一下是延时2us和1ms。
对于显示部分吧,使用的就是LCD1602显示了。
这部分程序参见这里。
显示程序说完了这些,准备的就差不多了,我们可以对着PDF写AT24C04程序了。
3、AT24C04之小试牛刀我们打破PDF中的介绍顺序,按照实际写程序时的顺序分开分析。
I2C详解——精选推荐

I2C详解1、基本概念主机初始化发送,产⽣时钟信号和终⽌发送的器件从机被主机寻址的器件发送器发送数据到总线的器件接收器从总线接收数据的器件多主机同时有多于⼀个主机尝试控制总线但不破坏报⽂仲裁是⼀个在有多个主机同时尝试控制总线,但只允许其中⼀个控制总线并使报⽂不被破坏的过程同步两个或多个器件同步时钟信号的过程2、硬件结构每⼀个I2C总线器件内部的SDA、SCL引脚电路结构都是⼀样的,引脚的输出驱动与输⼊缓冲连在⼀起。
其中输出为漏极开路的场效应管、输⼊缓冲为⼀只⾼输⼊阻抗的同相器。
这种电路具有两个特点:(1)由于SDA、SCL 为漏极开路结构,借助于外部的上拉电阻实现了信号的“线与”逻辑;(2)引脚在输出信号的同时还将引脚上的电平进⾏检测,检测是否与刚才输出⼀致。
为“时钟同步”和“总线仲裁”提供硬件基础。
3、时钟同步如果从机希望主机降低传送速度可以通过将SCL主动拉低延长其低电平时间的⽅法来通知主机,当主机在准备下⼀次传送发现SCL的电平被拉低时就进⾏等待,直⾄从机完成操作并释放SCL线的控制控制权。
这样以来,主机实际上受到从机的时钟同步控制。
可见SCL 线上的低电平是由时钟低电平最长的器件决定;⾼电平的时间由⾼电平时间最短的器件决定。
这就是时钟同步,它解决了I2C总线的速度同步问题。
4、主机发送数据流程(1)主机在检测到总线为“空闲状态”(即SDA、SCL 线均为⾼电平)时,发送⼀个启动信号“S”,开始⼀次通信的开始(2)主机接着发送⼀个命令字节。
该字节由7 位的外围器件地址和1 位读写控制位R/W 组成(此时R/W=0)(3)相对应的从机收到命令字节后向主机回馈应答信号ACK(ACK=0)(4)主机收到从机的应答信号后开始发送第⼀个字节的数据(5)从机收到数据后返回⼀个应答信号ACK(6)主机收到应答信号后再发送下⼀个数据字节(7)当主机发送最后⼀个数据字节并收到从机的ACK 后,通过向从机发送⼀个停⽌信号P结束本次通信并释放总线。
i2c 调试总结

i2c 调试总结I2C(Inter-Integrated Circuit)是一种串行通信总线,广泛应用于各种电子设备中,用于连接微控制器、传感器、存储器等器件。
I2C 调试是电子设备开发过程中必不可少的一环,以下是对I2C调试的一些总结:1. 理解I2C通信原理:- I2C总线由两根线组成:SDA(数据线)和SCL(时钟线)。
-通信过程中,主设备控制SCL,而SDA的数据传输由主从设备共同控制。
-常用的I2C速率有标准模式(100kHz)、快速模式(400kHz)、高速模式(3.4MHz)和超高速模式(5MHz以上)。
2. 选择正确的I2C地址:-每个I2C设备都有一个或多个唯一的地址,确保在编程时使用了正确的地址。
-有时设备可能允许通过配置引脚来改变其I2C地址。
3. 检查硬件连接:-确保SDA和SCL线正确连接,没有短路或断路。
-确保上拉电阻(如果有的话)正确连接,并且阻值适当。
-如果使用外部I2C总线扩展器或集线器,请检查其连接和配置。
4. 初始化配置:-根据所使用的硬件和软件平台,正确配置I2C接口,包括设置正确的速率、选择从设备地址等。
-在嵌入式系统中,可能需要编写或修改初始化代码来配置I2C控制器。
5. 软件调试:-使用调试工具,如串口输出、逻辑分析仪或示波器等,来监控SDA和SCL的波形。
-检查I2C通信的起始条件、地址发送、数据读写、停止条件等是否符合I2C协议规范。
6. 电源和接地:-确保所有设备有稳定的电源供应,并且接地良好。
-电源不稳或接地不良可能导致通信失败或设备损坏。
7. 固件和驱动程序:-确保使用的固件或驱动程序与硬件兼容,并且没有已知的bug。
-如果可能,尝试使用官方提供的示例代码或库文件。
8. 检查从设备:-如果使用多个从设备,请单独测试每个设备,以排除设备故障的可能性。
-检查从设备的电源、接地和初始化设置。
9. 外部干扰:-某些情况下,外部电磁干扰可能影响I2C通信。
I2C操作时序问题总结(内附大量实用代码和详解)

I2C 总线在单片机操作中用到的很多。
特别是以I2C总线进行数据和命令传输的器件,比如AT24C02存储芯片等。
特此做了相关操作过程中经常用到的操作和一些操作的解释。
相信看完之后,肯定会对I2C总线有深刻的理解。
I2C总线操作(从高位开始进行读写操作)写操作时序启动之后先进行一个字节的指令写入操作,然后进行应答;在进行字节数据的传送;然后再进行应答;I2C读操作时序基本上与写操作相同,不同的是读操作只需进行指令的写入,不写数据(应该不绝对),最后主机产生非应答信号,结束数据的读取;在对E2PROM(24C02)进行操作时,写入写操作指令后,然后写入需要操作的存储器地址号,最后写入数据。
且每个存储器地址只能赋值一次,重复对该存储器地址赋值会使前一个数据丢失。
读操作过程中需对将指令改写为读指令,在读取数据时需要写入指令指明需要读出数据时的存储器地址号下面是对24C02的写操作和读操作void write_add(uchar address,uchar date){start();write_byte(0xa0); //写指令respons();write_byte(address); //写入要操作的存储器地址respons();write_byte(date); //写入存储器数据respons();stop();}uchar read_add(uchar address){uchar date;start();write_byte(0xa0); //写入指令respons();write_byte(address); //写入读取操作时,要读取的存储器地址respons();stop();start();write_byte(0xa1); //写入指令,进行读操作respons();date=read_byte(); //进行读取数据norespons();stop();return date;}void main(){init();write_add(23,0xcc); //写入存储器地址号为23中,数据为0xcc delay1(100);P1=read_add(23); //读入存储器地址号为23中的数据,并将数据赋值给P1口,通过数码管显示while(1); //停留在此处}I2C串行总线的操作程序起始信号(时钟线为高,数据线由高变低):void AT24C04_Start(){SDA = 1; //拉高数据线 SCL = 1; //拉高时钟线 Delay5us(); //延时SDA = 0; //产生下降沿 Delay5us(); //延时SCL = 0; //拉低时钟线}结束信号:(时钟线为高,数据线由低变高)void AT24C04_Stop(){SDA = 0; //拉低数据线 SCL = 1; //拉高时钟线 Delay5us(); //延时SDA = 1; //产生上升沿 Delay5us(); //延时}字节传输:(每个字节为8位,一个字节带一个相应位)发送数据:void AT24C04_SendByte(BYTE dat){BYTE i;for (i=0; i<8; i++) //8位计数器{dat <<= 1; //移出数据的最高位SDA = CY; //送数据口SCL = 1; //拉高时钟线Delay5us(); //延时SCL = 0; //拉低时钟线,以便下一次传送数据 Delay5us(); //延时}AT24C04_RecvACK();}接收数据:BYTE AT24C04_RecvByte(){BYTE i;BYTE dat = 0;SDA = 1; //使能内部上拉,准备读取数据for (i=0; i<8; i++) //8位计数器{dat <<= 1;SCL = 1; //拉高时钟线Delay5us(); //延时dat |= SDA; //读数据SCL = 0; //拉低时钟线以便下一个数据传送 Delay5us(); //延时}return dat;数据响应:每次数据传输成功后,接收器件发送一个应答信号,当第九个信号产生时,产生应答信号的器件将SDA拉低。
I2C通信原理及程序详细讲解

I2C通信原理及程序详细讲解I2C(Inter-Integrated Circuit)是一种串行通信协议,常用于连接微控制器、传感器和其他外部设备。
I2C通信协议由荷兰飞利浦公司于1982年开发,它使用两根信号线(SDA和SCL)进行数据传输。
I2C通信协议采用主从结构,一个主设备(如微控制器)可以连接多个从设备(如传感器)。
主从设备之间通过SDA和SCL线进行数据传输。
SDA线是双向数据线,用于传输数据,SCL线是时钟线,用于同步数据传输。
I2C通信协议中,设备的地址是一个重要概念。
每个设备都有一个唯一的地址,通过该地址可以选择和通信特定的设备。
地址由7个位组成,其中最高位是固定的,并取决于设备是主设备还是从设备。
如果最高位为0,则表示该设备是主设备;如果最高位为1,则表示该设备是从设备。
通过以下步骤,让我们详细了解如何在I2C总线上进行通信。
1.初始化I2C总线:在程序开始时,需要初始化I2C总线。
这通常包括初始化SDA和SCL引脚,设置时钟频率等。
具体的初始化步骤取决于使用的硬件和软件环境。
2.发送开始信号:开始信号表示I2C数据传输的开始。
它由主设备发送,并且SDA线从高电平转为低电平时发出。
发送开始信号后,SDA线上的数据将被解释为地址数据。
3.发送设备地址:主设备发送一个包含设备地址和读/写位(R/W)的数据字节。
设备地址是唯一的,并且由主设备选择。
读/写位指示从设备是要读取数据还是写入数据。
4.等待从设备响应:主设备发送设备地址后,会等待从设备的响应。
从设备将响应一个应答位(ACK)来确认地址接收成功。
如果收到ACK位,则继续进行下一步,否则可能是设备未连接或通信错误。
5.发送数据:主设备发送数据给从设备。
数据可以是命令、配置或实际数据,具体取决于应用场景。
发送数据的方式是将每个数据字节传输到SDA线上,并在每个数据字节后发送一个ACK位。
6.接收数据:从设备将数据发送给主设备。
数据可以是传感器读数、存储器数据等。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
I2C驱动程序总结由于6410没有合适的i2c参考驱动,本总结主要基于TQ2440的AT24C02一、TQ2440电路二、I2c驱动程序分析1写入口函数at24cxx_init()在入口函数中主要有一个增加设备驱动的函数i2c_add_driver(),该函数会调用i2c_register_driver(THIS_MODULE, 驱动名字)完成注册。
2写出口函数at24cxx_exit()在出口函数里主要有一个删除设备驱动的函数i2c_del_driver(),这个函数功能有从设备驱动链表中删除驱动,卸载注册的驱动等3修饰入口函数和出口函数module_init(at24cxx_init); 这样,使用insmod命令加载驱动模块时,入口函数at24cxx_init()就会被调用module_exit(at24cxx_exit); 这样,使用rmmod命令加载驱动模块时,入口函数at24cxx_exit()就会被调用4写I2C驱动主要是:分配一个i2c_driver结构体设置填充i2c_driver结构体注册(前面1、2、3步)staticstruct i2c_driver at24cxx_driver={.driver = {.name = “at24cxx”,}.attach_adapter = at24cxx_attach,.detach_client = at24cxx_detach};函数at24cxx_attach()和at24cxx_detach需要自己构造5构造at24cxx_attach()函数和at24cxx_detach()函数Static int at24cxx_attach(struct i2c_adapter *adapter){return i2c_probe(adapter,&addr_data,at24cxx_detect);}I2c_probe函数主要是通过适配器adapter发送结构体addr_data的地址,这个地址就是i2c设备的地址,需要自己根据硬件原理图定义的,如果收到回应,就是说有这个i2c设备,就调用at24cxx_detect处理,这个函数需要自己去构造的。
class_device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "at24cxx");i2c_detach_client(client);kfree(i2c_get_clientdata(client));卸载函数主要是把申请的主设备号注销掉,注销掉注册的设备,解除适配器上的设备驱动,最后释放申请的设备空间。
6、构造addr_data结构体static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END };这里需要注意的是,I2C 设备的设备地址为7 位地址,不算第八位的R/W控制位,由电路图和i2c的数据手册可得出设备的地址。
所以i2c设备的地址为1010000,即0x50staticstruct i2c_client_address_data addr_data = {.normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备*/.probe = ignore,.ignore = ignore,//.forces = forces, /* 强制认为存在这个设备*/};7、构造at24cxx_detect()函数收到回应说明有i2c设备,然后调用at24cxx_detect()函数,在函数里面主要是分配一个i2c_client结构体,然后设置填充该结构体。
at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); //分配空间at24cxx_client->addr = address; //i2c设备的地址at24cxx_client->adapter = adapter; //分配一个适配器at24cxx_client->driver = &at24cxx_driver; //指向驱动strcpy(at24cxx_client->name, "at24cxx"); //设备的名字i2c_attach_client(at24cxx_client); //把设备驱动依附在适配器adapter上major = register_chrdev(0, "at24cxx", &at24cxx_fops); //注册,新的注册方法不再这样注册了cls = class_create(THIS_MODULE, "at24cxx");class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /*在/dev目录下生成at24cxx */ 以后收发数据要用到该结构体的8、构造file_operations结构体中的各成员函数主要构造.read函数、.write函数a.构造读函数staticssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset)首先要从应用程序中获得要读的地址copy_from_user(&address, buf, 1);/* 读AT24CXX时,要先把要读的存储空间的地址发给它*/msg[0].addr = at24cxx_client->addr; /* 目的*/msg[0].buf = &address; /* 要读的地址*/msg[0].len = 1; /* 地址=1 byte */msg[0].flags = 0; /* 表示写*//* 然后启动读操作*/msg[1].addr = at24cxx_client->addr; /* 源*/msg[1].buf = &data; /* 目的*/msg[1].len = 1; /* 数据=1 byte */msg[1].flags = I2C_M_RD; /* 表示读*/ret = i2c_transfer(at24cxx_client->adapter, msg, 2);if (ret == 2){copy_to_user(buf, &data, 1);return 1;}调用i2c传输函数,这会按i2c协议进行,然后把读到的数据返回给用户。
b、构造写函数copy_from_user(val, buf, 2);/* 数据传输三要素: 源,目的,长度*/msg[0].addr = at24cxx_client->addr; /* 目的*/msg[0].buf = val; /* 源*/msg[0].len = 2; /* 地址+数据=2 byte */msg[0].flags = 0; /* 表示写*/ret = i2c_transfer(at24cxx_client->adapter, msg, 1);第一句是从用户获得要写的数据,下面的msg就是设备地址、要写的地址,长度,写最后就是调用传输函数进行写。
9、填充file_operations结构体staticstructfile_operations at24cxx_fops = {.owner = THIS_MODULE,.read = at24cxx_read,.write = at24cxx_write,};这样在调用read函数时,就会调用at24cxx_read,在调用write函数时,就会调用at24cxx_write三、问题本总结的基于Linux2.6.22内核,与本开发板用的3.0.1内核的驱动有差别,3.0.1内核的很多i2c驱动都归到混杂设备驱动中去了四、I2C驱动程序#include <linux/kernel.h>#include <linux/init.h>#include <linux/module.h>#include <linux/slab.h>#include <linux/jiffies.h>#include <linux/i2c.h>#include <linux/mutex.h>#include <linux/fs.h>#include <asm/uaccess.h>static unsigned short ignore[] = { I2C_CLIENT_END };static unsigned short normal_addr[] = { 0x50, I2C_CLIENT_END }; /* 地址值是7位*//* 改为0x60的话, 由于不存在设备地址为0x60的设备, 所以at24cxx_detect不被调用*/static unsigned short force_addr[] = {ANY_I2C_BUS, 0x60, I2C_CLIENT_END};static unsigned short * forces[] = {force_addr, NULL};staticstruct i2c_client_address_data addr_data = {.normal_i2c = normal_addr, /* 要发出S信号和设备地址并得到ACK信号,才能确定存在这个设备*/.probe = ignore,.ignore = ignore,//.forces = forces, /* 强制认为存在这个设备*/};staticstruct i2c_driver at24cxx_driver;staticint major;staticstruct class *cls;struct i2c_client *at24cxx_client;staticssize_t at24cxx_read(struct file *file, char __user *buf, size_t size, loff_t * offset){unsigned char address;unsigned char data;struct i2c_msg msg[2];int ret;/* address = buf[0]* data = buf[1]*/if (size != 1)return -EINVAL;copy_from_user(&address, buf, 1);/* 数据传输三要素: 源,目的,长度*//* 读AT24CXX时,要先把要读的存储空间的地址发给它*/msg[0].addr = at24cxx_client->addr; /* 目的*/msg[0].buf = &address; /* 源*/msg[0].len = 1; /* 地址=1 byte */msg[0].flags = 0; /* 表示写*//* 然后启动读操作*/msg[1].addr = at24cxx_client->addr; /* 源*/msg[1].buf = &data; /* 目的*/msg[1].len = 1; /* 数据=1 byte */msg[1].flags = I2C_M_RD; /* 表示读*/ret = i2c_transfer(at24cxx_client->adapter, msg, 2);if (ret == 2){copy_to_user(buf, &data, 1);return 1;}elsereturn -EIO;}staticssize_t at24cxx_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) {unsigned char val[2];struct i2c_msg msg[1];int ret;/* address = buf[0]* data = buf[1]*/if (size != 2)return -EINVAL;copy_from_user(val, buf, 2);/* 数据传输三要素: 源,目的,长度*/msg[0].addr = at24cxx_client->addr; /* 目的*/msg[0].buf = val; /* 源*/msg[0].len = 2; /* 地址+数据=2 byte */msg[0].flags = 0; /* 表示写*/ret = i2c_transfer(at24cxx_client->adapter, msg, 1);if (ret == 1)return 2;elsereturn -EIO;}staticstructfile_operations at24cxx_fops = {.owner = THIS_MODULE,.read = at24cxx_read,.write = at24cxx_write,};staticint at24cxx_detect(struct i2c_adapter *adapter, int address, int kind){printk("at24cxx_detect\n");/* 构构一个i2c_client结构体: 以后收改数据时会用到它*/at24cxx_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);at24cxx_client->addr = address;at24cxx_client->adapter = adapter;at24cxx_client->driver =&at24cxx_driver;strcpy(at24cxx_client->name, "at24cxx");i2c_attach_client(at24cxx_client);major = register_chrdev(0, "at24cxx", &at24cxx_fops);cls = class_create(THIS_MODULE, "at24cxx");class_device_create(cls, NULL, MKDEV(major, 0), NULL, "at24cxx"); /* /dev/at24cxx */return 0;}staticint at24cxx_attach(struct i2c_adapter *adapter){return i2c_probe(adapter, &addr_data, at24cxx_detect);}staticint at24cxx_detach(struct i2c_client *client){printk("at24cxx_detach\n");class_device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "at24cxx");i2c_detach_client(client);kfree(i2c_get_clientdata(client));return 0;}/* 1. 分配一个i2c_driver结构体*/ /* 2. 设置i2c_driver结构体*/ staticstruct i2c_driver at24cxx_driver = { .driver = {.name = "at24cxx",},.attach_adapter = at24cxx_attach,.detach_client = at24cxx_detach, };staticint at24cxx_init(void){i2c_add_driver(&at24cxx_driver);return 0;}static void at24cxx_exit(void){i2c_del_driver(&at24cxx_driver); }module_init(at24cxx_init);module_exit(at24cxx_exit);MODULE_LICENSE("GPL");。