i2c驱动程序总结
- 格式:docx
- 大小:53.99 KB
- 文档页数:8
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() //等待从机应答信号,如果从机迟迟没有应答,则结束总线。
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设备驱动框架及相关函数本应用笔记以驱动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总结济南职业学院电子工程系朱志强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详解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(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总线进行数据和命令传输的器件,比如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(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.接收数据:从设备将数据发送给主设备。
数据可以是传感器读数、存储器数据等。
从网上找的几个资料进行了整理I2C(Inter-Integrated Circuit)总线是由PHILIPS公司开发的两线式串行总线,用于连接微控制器及其外围设备。
是微电子通信控制领域广泛采用的一种总线标准。
它是同步通信的一种特殊形式,具有接口线少,控制方式简单,器件封装形式小,通信速率较高等优点。
I2C总线特征1、只要求两条总线线路:一条串行数据线SDA(双向),一条串行时钟线SCL;2、每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机/从机关系软件设定地址,主机可以作为主机发送器或主机接收器;3、它是一个真正的多主机总线,如果两个或更多主机同时初始化,数据传输可以通过冲突检测和仲裁防止数据被破坏;4、串行的8 位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s,高速模式下可达3.4Mbit/s;5、连接到相同总线的IC 数量只受到总线的最大电容400pF 限制。
I2C总线术语发送器:发送数据到总线的器件;接收器:从总线接收数据的器件;主机:初始化发送产生时钟信号和终止发送的器件;从机:被主机寻址的器件;多主机:同时有多于一个主机尝试控制总线但不破坏传输;仲裁:是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并使传输不被破坏的过程;同步:两个或多个器件同步时钟信号的过程。
I2C总线位传输由于连接到I2C 总线的器件有不同种类的工艺(CMOS、NMOS、双极性),逻辑0(低)和逻辑1(高)的电平不是固定的,它由电源VCC的相关电平决定,每传输一个数据位就产生一个时钟脉冲。
为了避免总线信号的混乱,要求各设备连接到总线的输出端必须是开漏输出或集电极开路输出的结构。
I2C总线结构图如上图所示,I2C是OC或OD输出结构,使用时必须在芯片外部进行上拉,上拉电阻R的取值根据I2C总线上所挂器件数量及I2C总线的速率有关,一般是标准模式下R 选择10kohm,快速模式下R选取1kohm,I2C总线上挂的I2C器件越多,就要求I2C的驱动能力越强,R的取值就要越小,实际设计中,一般是先选取4.7kohm上拉电阻,然后在调试的时候根据实测的I2C波形再调整R的值。
I2C之AT24C04总结济南职业学院电子工程系朱志强1、AT24C04介绍2、AT24C04之准备工作3、AT24C04之小试牛刀4、对应源程序2010年7月28日1、AT24C04介绍关于I2C的介绍,这里就不用说了,直接介绍24C04了。
24C04是4K位串行CMOS E2PROM。
引脚的认识:SCL 串行时钟引脚SDA 串行数据/地址A0、A1、A2 器件地址输入端WP 写保护(WP 管脚连接到Vcc,所有的内容都被写保护(只能读)。
当WP 管脚连接到Vss 或悬空,允许器件进行正常的读/写操作。
)2、AT24C04之准备工作首先,我们先查看一下实验板上面的接线图。
如图1所示。
图1 24c04连接图我们要注意的第一点是器件地址全部是0,即接地处理。
第二点是读写保护WP接地,意味着我们可以随意存取。
第三点是我们要用到的引脚连接到了P3^6和P3^7上。
在这里还要提醒一下,就是引脚上一定要有上拉电阻!阻值在470~1k 都可以的,具体的数值可以参考相关的手册。
在程序里我们需要先做以下定义:sbit AT24C04_SCL=P3^7;sbit AT24C04_SDA=P3^6;在写这个程序的时候,要使用到键盘,不用太多按键,我们暂时只用四个。
把实验板上面的跳线JP8接到“-”端上,使第一行的按键变为独立键盘就可以了。
线路图如图2所示。
图2 键盘部分电路图键盘这部分我就不说了吧,直接附上我用到的这部分程序,在我的程序中,并没有判断按键是否松开,而是使用的延时,这样的好处是一直按着按键,数据会一直在变化,要不然,频繁的按真的很累人。
转到按键程序对于里面用到的延时函数,一个是US级延时函数,一个是ms级延时函数,分别调用一下是延时2us和1ms。
对于显示部分吧,使用的就是LCD1602显示了。
这部分程序参见这里。
显示程序说完了这些,准备的就差不多了,我们可以对着PDF写AT24C04程序了。
3、AT24C04之小试牛刀我们打破PDF中的介绍顺序,按照实际写程序时的顺序分开分析。
总结IIC协议学习心得无论在51或者stm32的学习中,我们都会使用到IIC总线的数据传送,从当初51到现在的stm32的学习。
iic一直都是只会简单的使用并未很仔细的去研究中间每一个过程或者时序。
今天用了一天的时间去理解IIC。
当然这都是建立在以往的学习经验之中而得。
一下都是我自己的一些理解和对照程序而写的一些IIC的使用过程。
iic总线时序理解:24C04引脚:NC (1)VCC(8)A1 (2)WP (7)A2 (3)SCL(6)GBD(4)SDA(5)注:SCL设置start信号和stop信号,由于此时是STM32对24c02发送信号,所以SCL和SDA设置为输出状态,即:SDA_OUT(); 由时序图可知:START:SCL=1,SDA=1,delay(),SDA=0,delay(),SCL=0,//时钟线为高,数据线从高到低的跳变STOP :SCL=0,SDA=0,delay(),SCL=1,delay(),SDA=1,//时钟线为高,数据线从低到高的跳变STM32程序:void IIC_Start(void){SDA_OUT(); //sda线输出IIC_SDA=1;IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form highto lowdelay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据}void IIC_Stop(void){SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1;IIC_SDA=1;//发送I2C总线结束信号delay_us(4);}iic每成功传送完一个字节后,接收器都必须产生一个应答信号,应答器件在第九个时钟周期讲SDA拉低,表示已经接收到8位,此时对于24c02有两种可能的工作方式,第一种是24c02工作于读,另一种则是工作于写。
I2C 通信:起始条件:SCL为高电平,SDA电平从高变低,这一变化即完成了通信的起始条件。
起始条件和数据通信间,通常会有延时要求具体的指标见设备说明。
数据传输阶段:一字节需要9个时钟周期;且每一位需要一个时钟周期;如上图所示,ADDRESS为目标设备的地址,R/ w为通信的的方向位;"1"时表示读,即后续的数据由目标设备发出,主机进行接收;"0"时表示写,即后续的数据由主机发出目标设备进行接收。
当ACK信号为"0"时,说明接收成功;为"1"时,说明接收失败。
每个字节的传输都是由高位(MSB)到低位(LSB)依次进行传输。
在数据通信过程中,总是由数据接收方发出ACK信号。
终止阶段:当主机完成数据通信,并终止本次传输时会发出终止信号。
当SCL 是高电平时,SDA电平由低变高,这个变化意味着传输终止。
注:每个时钟周期的高电平期间,SDA的数据状态达到稳定。
下面给出了模拟I2C总线进行读写的伪代码,用以说明如何使用GPIO 实现I2C通信:int i2c_start() /* I2C起始条件*/{//初始化GPIO口set_gpio_direction(SDA, OUTP); //设置SDA方向为输出set_gpio_direction (SCL, OUTP); //设置SCL方向为输出set_gpio_value(SDA, 1); //设置SDA为高电平set_gpio_value(SCL, 1); //设置SCL为高电平delay(); //延时//起始条件set_gpio_value(SDA, 0); //SCL为高电平时,SDA由高变低delay(); //适当延时}void i2c_stop() /* I2C终止条件*/{set_gpio_value(SCL, 1);set_gpio_direction(SDA, OUTP);set_gpio_value(SDA, 0);delay();set_gpio_value(SDA, 1); //SCL高电平时,SDA由低变高}/* I2C读取ACK信号(写数据时使用)返回值:0表示ACK信号有效;非0表示ACK信号无效*/unsigned char i2c_read_ack(){unsigned char r;set_gpio_direction(SDA, INP); //设置SDA方向为输入set_gpio_value(SCL,0); // SCL变低r = get_gpio_value(SDA); //读取ACK信号delay();set_gpio_value(SCL,1); // SCL变高delay();return r;}/* I2C发出ACK信号(读数据时使用) */int i2c_send_ack(){set_gpio_direction(SDA, OUTP); //设置SDA方向为输出set_gpio_value(SCL,0); // SCL变低set_gpio_value(SDA, 0); //发出ACK信号delay();set_gpio_value(SCL,1); // SCL变高delay();}void i2c_write_byte(unsigned char b) /* I2C字节写*/{int i;set_gpio_direction(SDA, OUTP); //设置SDA方向为输出for (i=7; i>=0; i--){set_gpio_value(SCL, 0); // SCL变低delay();set_gpio_value(SDA, b & (1<<i)); //从高位到低位依次发送数据set_gpio_value(SCL, 1); // SCL变高delay();}i2c_read_ack(); //检查目标设备的ACK信号}/* I2C字节读*/unsigned char i2c_read_byte(){int i;unsigned char r = 0;set_gpio_direction(SDA, INP); //设置SDA方向为输入for (i=7; i>=0; i--){set_gpio_value(SCL, 0); // SCL变低delay();r = (r <<1) | get_gpio_value(SDA); //高位到低位依次数据读取set_gpio_value(SCL, 1); // SCL变高delay();}i2c_send_ack(); //向目标设备发送ACK信号return r;}/* I2C读操作addr:目标设备地址buf:读缓冲区len:读入字节的长度*/void i2c_read(unsigned char addr, unsigned char* buf, int len){int i;unsigned char t;i2c_start(); //起始条件,开始数据通信//发送地址和数据读写方向t = (addr << 1) | 1; //低位为1,表示读数据i2c_write_byte(t);//读入数据for (i=0; i<len; i++)buf[i] = i2c_read_byte();i2c_stop(); //终止条件,结束数据通信}/* I2C写操作addr:目标设备地址buf:写缓冲区len:写入字节的长度*/void i2c_write (unsigned char addr, unsigned char* buf, int len){int i;unsigned char t;i2c_start(); //起始条件,开始数据通信//发送地址和数据读写方向t = (addr << 1) | 0; //低位为0,表示写数据i2c_write_byte(t);//写入数据for (i=0; i<len; i++)i2c_write_byte(buf[i]);i2c_stop(); //终止条件,结束数据通信}。
camera驱动调试常见问题总结在嵌⼊式系统,如⼿机等平台上使⽤的Camera sensor通常是由类似I2C这样的总线进⾏寄存器控制,由CPU端的Controller提供所需的驱动时序,通常⽀持YUV和RGB等数据格式。
有的Sensor需要由CPU进⾏图像处理⼯作,有的Sensor⾃⼰会集成图像处理芯⽚,完成⼀些基础的图像处理⼯作,还有些⾼像素的Sensor甚⾄⾃⼰完成JPEG的编码⼯作。
因为硬件的多样性,我所遇到的问题可能和你的原因现象都不尽相同,分析内容仅供参考。
Sensor端I2C总线没有响应症状所有输⼊电压和时钟信号都正常,往I2C总线上写⼊读取寄存器数据的命令后,sensor没有响应,没有数据从I2C总线上输出。
分析因为测量发现⼀切输出信号都正常,所以往往都会怀疑Sensor硬件存在问题,不过99%的情况,实际的原因总是因为I2C总线的ID值没有设置对,导致设备不响应命令。
据我的观察,每次⼀个新的⼯程师在调试Sensor的时候⼏乎都会遇上这个问题。
之所以这么容易设置错误的原因,是因为通常Camera Sensor的Spec上所写的I2C ID号,还包含了最后⼀位读写⽅向位。
⽽这⼀位在I2C总线的定义中,严格来说,不属于ID的⼀部分,所以Linux I2C的驱动API中的调⽤参数⾥的ID号,通常是不考虑这⼀位的,读写⽅向位会在具体的读写操作中,在寄存器中进⾏设置。
解决例如Spec上会写 读写寄存器操作 I2C ID 分别为 0x64和0x65,实际调⽤API时应该使⽤0x32作为该设备的I2C ID图像中有不断变化的细密的⽔平条纹症状与荧光灯的频闪造成的⼤⾯积的滚动⽔平条纹不同,表现出来的是⼀个像素⾼的⽔平条纹状躁点,位置不固定,数量⽐较多,⽽且随光线强弱有⼀定的变化分析因为设置某些sensor寄存器的时候,会影响到这些⽔平条纹的颜⾊,所以基本上排除是在数据传输过程中板⼦对数据造成的⼲扰,也排除接触不良的可能性,应该是数据在sensor内部已经存在这些⽔平条纹。
SMBus协议总结本文将对SMBUS协议进行总结。
本文针对该协议的所有修订版(包括1.0,1.1,2.0)。
具体的协议特性将在本文最后进行介绍。
一些I2C适配器仅仅兼容SMBus协议(System Management Bus,系统管理总线)。
而SMBus只是I2C协议的子集。
幸运的是,大部分设备都使用相同的子集,故可以将它们放置同一个SMBus中。
如果你为一个I2C设备写驱动程序,而该设备仅仅使用I2C协议的子集,那么推荐你尽可能使用SMBus协议的命令。
这样这个驱动程序既可以在SMBus适配器上使用,也可以在I2C适配器上使用(当在I2C适配器上时,SMBus格式会自动转换为I2C格式,而SMBus适配器大多无法处理单纯的I2C格式)。
下文将列举SMBus的操作时序,以及执行该时序的函数。
注意,一下使用的函数名称可能会因不同的SMBus协议规格而不尽相同。
每个处理都对应一个标识。
在调用处理函数是,驱动程序应该总要检查一次对应的标识以确保底层的I2C适配器支持改处理。
具体详见。
各个字符的描述功能标识:I2C_FUNC_SMBUS_QUICK即可实现操作,对其他设备,可以在使用SMBus指定了寄存器地址后使用该函数实现对同一个寄存器的重复读取。
功能标识:I2C_FUNC_SMBUS_READ_BYTE功能标识:I2C_FUNC_SMBUS_READ_BYTE_DATA注意:还有一个读取字寄存器的函数i2c_smbus_read_word_swapped,使用于当寄存器两个字节排列方式不同时的情况。
虽然与SMBus不兼容,但使用得十分普遍。
功能标识:I2C_FUNC_SMBUS_WRITE_WORD_DATA 注意:还有一个写字寄存器的函数i2c_smbus_write_word_swapped,使用于当寄存器两个字节排列方式不同时的情况。
虽然与SMBus不兼容,但使用得十分普遍。
功能标识:flag: I2C_FUNC_SMBUS_PROC_CALL该函数向设备读取多于32字节的块数据。
⾯试被问到IIC,总结。
Linux3.5内核中,IIC。
1 i2c_add_driver2 i2c_register_driver3 a. at24cxx_driver放⼊i2c_bus_type的drv链表4并且从dev链表⾥取出能匹配的i2c_client并调⽤probe5 driver_register678 b. 对于每⼀个适配器,调⽤__process_new_driver9对于每⼀个适配器,调⽤它的函数确定address_list⾥的设备是否存在10如果存在,再调⽤detect进⼀步确定、设置,然后i2c_new_device11/* Walk the adapters that are already present */12 i2c_for_each_dev(driver, __process_new_driver);13 __process_new_driver14 i2c_do_add_adapter15/* Detect supported devices on that bus, and instantiate them */16 i2c_detect(adap, driver);17for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {18 err = i2c_detect_address(temp_client, driver);19/* 判断这个设备是否存在:简单的发出S信号确定有ACK */20if (!i2c_default_probe(adapter, addr))21return0;2223 memset(&info, 0, sizeof(struct i2c_board_info));24 info.addr = addr;2526// 设置info.type27 err = driver->detect(temp_client, &info);2829 i2c_new_device使⽤读写IIC器件的函数接⼝:/***I2c_Master_发送-在主传输模式下发出单个I2c消息*@客户端:从设备的句柄*@buf:将写⼊从系统的数据*@计数:要写⼊的字节数,必须⼩于64K,因为msg.len是u16*返回负的errno,或者返回写⼊的字节数。
一、I2C通信协议 (2)1、I2C 协议: (2)2、I2C时序 (3)3、I2C协议中文版本 (7)二、I2C协议中遇到的BUG分析 (8)1、项目A72A中时序问题 (8)2、T03 Light sensor 无ACK问题 (8)3、S26I 电池以及F01电池问题 (9)4、现象ACK后面的半高的小毛刺分析 (10)5、T05C G-sensor无数据问题 (11)三、I2C协议使用注意事项 (12)1、I2C level shift 普通MOS以及level shift IC (12)2、I2C使用注意事项以及bug总结 (15)文档整理人:袁刚2011年12月2日一、I2C通信协议1、I2C 协议:I2C 总线支持任何IC 生产过程(NMOS、CMOS、双极性)。
两线的串行数据SDA和串行时钟SCL 线在连接到总线的器件间传递信息,每个器件都有一个唯一的地址识别(无论是微控制器、LCD驱动器、存储器或键盘接口),而且都可以作为一个发送器或接收器,由器件的功能决定,很明显LCD驱动器只是一个接收器,而存储器则既可以接收又可以发送数据。
除了发送器和接收器外器件,在执行数据传输时也可以被看作是主机或从机,如下表1。
主机是初始化总线的数据传输并产生允许传输的时钟信号的器件,此时任何被寻址的器件都被认为是从机。
表格1① I2C总线的一些特征:只要求两条总线线路:一条串行数据线SDA一条串行时钟线SCL每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机从机关系软件设定地址主机可以作为主机发送器或主机接收器它是一个真正的多主机总线如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏串行的8位双向数据传输位速率在标准模式下可达100kbit/s,快速模式下可达400kbit/s 高速模式下可达3.4Mbit/s片上的滤波器可以滤去总线数据线上的毛刺波保证数据完整连接到相同总线的IC 数量只受到总线的最大电容400pF 限制②I2C总线的传输:I2C 总线是一个多主机的总线,这就是说可以连接多于一个能控制总线的器件到总线,由于主机通常是微控制器,让我们考虑以下数据在两个连接到I2C 总线的微控制器之间传输的情况。
I2c总线总结I2c总线是一种双线制双向传输的总线,由数据线SDA与时钟线SCL组成。
在总线备用时,双线都必需保持高电平状态,只有关闭总线时才将时钟线SCL钳位在低电平。
总线在数据传输时,当时钟线为高电平时数据线上的数据才是有效的且不允许改变,只有时钟为低电平时数据才允许变化。
Ic2总线拥有线“与”的功能,只有一条线上所有的输出为高电平时该线才能为高电平。
标准i2c模式下数据传输速率为100Kbit/s,快速模式下为400Kbit/s,高速模式下为3.4Mbit/s。
实际传输中可选择不同的数据传输速率,同时也可以采用延长时钟线SCL低电平周期来控制数据传输速率。
连接到总线的IC 数量只受到该总线的最大电容400pF 限制。
状态:过程状态I2c总线总线各各过程状态1.总线起始信号:时钟线为高电平,数据线从高电平变为低电平,启动i2c总线。
兼容I2C 总线的器件在接收到起始或重复起始条件时必须复位它们的总线逻辑以接收对比下面的寻址地址。
2.数据传输的开始—地址读写位:开始信号之后,发送7位的寻址地址和1位的R/W位‘0’表示写操作‘1’表示读操作。
如果一次传输中数据传输方向发送改变,则可重复发送起始信号Sr与地址读写信号以更改数据传输方向。
*数据传输等待情况:如果从机要完成一些其他功能后(例如一个内部中断服务程序)才能接收或发送下一个完整的数据字节,可以使时钟线SCL在完成应答信号后(如图中英文标示)保持低电平迫使主机进入等待状态。
当从机准备好接收下一个数据字节并释放时钟线SCL 后,数据传输继续。
3.应答信号:总线上每传输一个字节数据后都必须有应答信号,应答信号所对应的时钟信号由主控器提供,同时发送器释放数据线,使其处于高电平,以便接收器获取总线发送应答信号。
应答信号在第九个时钟位上出现,接受器输出低电平为应答信号A输出高电平则为非应答信号/A。
*如果被控制器由于某种原因不能产生应答时,必须释放总线将数据线置为高电平,这时主控器可发送一个停止信号终止总线传输。
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");。