I2C_周立功标准驱动程序_c代码
- 格式:doc
- 大小:37.50 KB
- 文档页数:14
I2C驱动程序( C51 )作者:佚名来源:不详录入:Admin更新时间:2008-7-27 16:02:03点击数:4【字体:】来源:网上转载作者:佚名//****************************I2C驱动程序****************************// void Set_SCL(uchar i) //定义MAX6959时钟线为PA^7{if(i) PORTA |=0x80;else PORTA &=0x7f;}void Set_SDA(uchar i) //定义MAX6959数据线为PA^6{if(i) PORTA |=0x40;else PORTA &=0xbf;}void Start() //I2C总线的开始条件{Set_SDA(1); //SDA=1;Set_SCL(1); //SCL=1;delay(100); //等待时钟信号变高Set_SDA(0); //SDA=0;delay(100);Set_SCL(0); // SCL=0;}void Stop() //I2C总线结束条件{Set_SDA(0); //SDA=0;delay(100);Set_SCL(1); //SCL=1;delay(100); //等待时钟信号变低Set_SDA(1); //SDA=1;delay(100);Set_SCL(0); //SCL=0;}void Ack() //I2C总线应答{Set_SDA(0); //SDA=0;delay(50);Set_SCL(1); //SCL=1;delay(100);Set_SCL(0); //SCL=0;delay(50);Set_SDA(1); //SDA=1;}void Nack() //I2C总线应答非{Set_SDA(1); //SDA=1;delay(50);Set_SCL(1); //SCL=1;delay(100);Set_SCL(0); //SCL=0;delay(50);Set_SDA(0); //SDA=0;}void Check_Ack() //I2C总线应答检查{Set_SDA(1); //SDA=1;Set_SCL(1); //SCL=1;Ack_Flag=0; //清除应答标志;DDRA&=0xbf; //设置SDA为输入if(!((PINA & 0x40)>>6)){delay(50);Set_SCL(0); //SCL=0;delay(100);}else{Ack_Flag=1; //收到应答;delay(50);Set_SCL(0); //SCL=0;delay(100);}DDRA |=0xff; //重新设置SDA为输出}void Write_Bit0() //写1bit数据:0{Set_SDA(0); //SDA=0;Set_SCL(1); //SCL=1;delay(100);Set_SCL(0); //SCL=0;}void Write_Bit1() //写1bit数据:1 {Set_SDA(1); //SDA=1;Set_SCL(1); //SCL=1;delay(100);Set_SCL(0); //SCL=0;Set_SDA(0); //SDA=0;}void Write_Byte(uchar Data) //写一字节数据Data {uchar i;for(i=0;i<8;i++){if(Data & 0x80) Write_Bit1(); //传输位从高位到低位,先传最高位else Write_Bit0();Data <<=1;} //数据左移}uchar Read_Byte() //读一字节数据:返回值即读的数据{uchar Read_Data=0xff; // Read_Data为读入的数据,初始值为0xffuchar j;for (j=0;j<8;j++){Set_SDA(1);Set_SCL(1);DDRA &=0xbf;if((PINA & 0x40)==0){Read_Data <<=1;Read_Data=(Read_Data & 0xfe);Set_SCL(0);}else{Read_Data <<=1;Read_Data =( Read_Data | 0x01);Set_SCL(0);}}DDRA |=0x40;return(Read_Data);}//***************************写一字节数据****************************// //向从地址为Slave的IIC器件的Address地址写一字节数据Datavoid Write_1byte(uchar Slave,uchar Address,uchar Data){Start(); //开始Write_Byte(Slave); //写I2C器件地址do Check_Ack(); //检查应答位while(Ack_Flag==1);Write_Byte(Address); //送寄存器地址do Check_Ack(); //检查应答位while(Ack_Flag==1);Write_Byte(Data); //写入一字节数据do Check_Ack(); //检查应答位while(Ack_Flag==1);Stop(); //停止}//***************************写两字节数据****************************// //向从地址为Slave的IIC器件的Address地址开始写两字节数据Data0,Data1 void Write_2byte(uchar Slave,uchar Address,uchar Data0,uchar Data1){Start(); //开始Write_Byte(Slave); //写I2C器件地址do Check_Ack(); //检查应答位while(Ack_Flag==1);Write_Byte(Address); //送寄存器地址do Check_Ack(); //检查应答位while(Ack_Flag==1);Write_Byte(Data0);do Check_Ack(); //检查应答位while(Ack_Flag==1);Write_Byte(Data1);do Check_Ack(); //检查应答位while(Ack_Flag==1);Stop(); //停止信号}//***************************读一字节数据****************************////从从地址为Slave的IIC器件的Address地址处读一字节数据uchar Read_1byte(uchar Slave,uchar Address){uchar data0,x;do{Start(); //开始信号Write_Byte(Slave); //写I2C器件地址Check_Ack(); //检查应答位}while(Ack_Flag);do{Write_Byte(Address); //写寄存器地址Check_Ack(); //检查应答位}while(Ack_Flag);do{Start(); //开始Write_Byte(Slave+1); //写I2C器件地址(+1表示为读操作)Check_Ack(); //检查应答位}while(Ack_Flag);data0=Read_Byte(); //读1字节Nack(); //读完发送一个Not Ack应答Stop(); //停止return data0; //返回读取数据}。
I2C总线规范目录1序言 (3)1.1 版本1.0-1992 (3)1.2 版本2.0-1998 (3)1.3 版本2.1-2000 (3)1.4 购买Philips的I2C总线元件 (3)2I2C总线使设计人员和厂商都得益 (3)2.1 设计人员的得益 (4)2.2 厂商的得益 (5)3介绍I2C总线规范 (6)4I2C总线的概念 (6)5总体特征 (7)6位传输 (7)6.1 数据的有效性 (7)6.2 起始和停止条件 (8)7传输数据 (9)7.1 字节格式 (9)7.2 响应 (9)8仲裁和时钟发生 (10)8.1 同步 (10)8.2 仲裁 (10)8.3 用时钟同步机制作为握手 (11)97位的地址格式 (12)107位寻址 (13)10.1 第一个字节的位定义 (13)10.1.1 广播呼叫地址 (14)10.1.2 起始字节 (15)10.1.3 CBUS的兼容性 (16)11标准模式I2C总线规范的扩展 (16)12快速模式 (17)13Hs模式 (17)13.1 高速传输 (17)13.2 Hs模式的串行数据传输格式 (19)13.3 从F/S模式切换到Hs模式以及返回 (20)13.4 低速模式中的快速模式器件 (21)13.5 串行总线系统的混合速度模式 (21)13.5.1 在混合速度总线系统中的F/S模式传输 (22)13.5.2 在混合速度总线系统中的Hs模式传输 (22)13.5.3 混合速度总线系统中电桥的时序要求 (24)1410位寻址 (24)14.1 头两个字节位的定义 (24)14.2 10位寻址的格式 (24)14.3 广播呼叫地址和10位寻址的起始字节 (26)15I/O级和总线线路的电气规范和时序 (26)15.1 标准和快速模式器件 (26)15.2 Hs模式器件 (28)16I2C总线器件到总线线路的电气连接 (30)16.1 标准模式I2C总线器件电阻R p和R S的最大和最小值 (31)17应用信息 (33)17.1 快速模式I2C总线器件的斜率控制输出级 (33)17.2 快速模式I2C总线器件的开关上拉电路 (34)17.3 总线线路的配线方式 (34)17.4 快速模式I2C总线器件电阻R p和R S的最大和最小值 (35)17.5 Hs模式I2C总线器件的电阻R p和R S的最大和最小值 (35)18F/S模式I2C总线系统的双向电平转换器 (35)18.1 连接逻辑电平不同的器件 (36)18.1.1 电平转换器的操作 (36)19Philips提供的开发工具 (37)20支持的文献 (37)1序言1.1 版本1.0-19921992 I2C总线规范的这个版本有以下的修正• 删除了用软件编程从机地址的内容因为实现这个功能相当复杂而且不被使用• 删除了低速模式实际上这个模式是整个I2C总线规范的子集不需要明确地详细说明• 增加了快速模式它将位速率增加4倍到达400kbit/s快速模式器件都向下兼容即它们可以在0~100kbit/s的I2C总线系统中使用• 增加了10位寻址允许1024个额外的从机地址• 快速模式器件的斜率控制和输入滤波改善了EMC性能注意100kbit/s的I2C总线系统或100kbit/s器件都没有改变1.2 版本2.0-1998I2C总线实际上已经成为一个国际标准在超过100种不同的IC上实现而且得到超过50家公司的许可但是现在的很多应用要求总线速度更高电源电压更低这个更新版的I2C总线规范满足这些要求而且有以下的修正• 增加了高速模式Hs模式它将位速率增加到3.4Mbit/s Hs模式的器件可以和I2C总线系统中快速和标准模式器件混合使用位速率从0~3.4Mbit/s• 电源电压是2V或更低的器件的低输出电平和滞后被调整到符合噪声容限的要求而且保持和电源电压更高的器件兼容• 快速模式输出级的0.6V 6mA要求被删除• 新器件的固定输入电平被总线电压相关的电平代替• 增加了双向电平转换器的应用信息1.3 版本2.1-2000I2C总线规范的V2.1版有以下微小的修改• 在Hs模式的重复起始条件后可以延长时钟信号SCLH见13.2节的图2225和32• Hs模式中的一些时序参数变得更随意见表6和表71.4 购买Philips的I2C总线元件购买Philips的I2C元件同时传递了一个在Philips的I2C专利下在I2C系统使用元件使系统符合由Philips定义的I2C规范的许可证2I2C总线使设计人员和厂商都得益在消费者电子电讯和工业电子中看上去不相关的设计里经常有很多相似的地方例如几乎每个系统都包括• 一些智能控制通常是一个单片的微控制器• 通用电路例如LCD驱动器远程I/O口RAM EEPROM或数据转换器• 面向应用的电路譬如收音机和视频系统的数字调谐和信号处理电路或者是音频拨号电话的DTMF发生器为了使这些相似之处对系统设计者和器件厂商都得益而且使硬件效益最大电路最简单Philips开发了一个简单的双向两线总线实现有效的IC之间控制这个总线就称为Inter IC或I2C总线现在Philips 包括超过150种CMOS和双极性兼容I2C总线的IC可以执行前面提到的三种类型的功能所有符合I2C 总线的器件组合了一个片上接口使器件之间直接通过I2C总线通讯这个设计概念解决了很多在设计数字控制电路时遇到的接口问题下面是I2C总线的一些特征• 只要求两条总线线路一条串行数据线SDA一条串行时钟线SCL• 每个连接到总线的器件都可以通过唯一的地址和一直存在的简单的主机从机关系软件设定地址主机可以作为主机发送器或主机接收器• 它是一个真正的多主机总线如果两个或更多主机同时初始化数据传输可以通过冲突检测和仲裁防止数据被破坏• 串行的8位双向数据传输位速率在标准模式下可达100kbit/s快速模式下可达400kbit/s高速模式下可达3.4Mbit/s• 片上的滤波器可以滤去总线数据线上的毛刺波保证数据完整• 连接到相同总线的IC数量只受到总线的最大电容400pF限制图1是两个I2C总线应用的例子2.1 设计人员的得益符合I2C总线的IC允许系统设计快速向前推进直接从功能结构图到原型此外由于它们直接剪贴到I2C总线没有任何额外的外部接口所以允许简单地通过从或者向总线剪贴或不剪贴IC 来修改或升级原型系统符合I2C总线的IC还有一些功能特别吸引设计人员• 结构图的功能模块与实际的IC对应设计快速从结构图向最后的原理图推进• 不需要设计总线接口因为I2C总线接口已经集成在片上• 集成的寻址和数据传输协议允许系统完全由软件定义• 相同类型的IC经常用于很多不同的应用• 由于设计人员快速熟悉了用兼容I2C总线的IC表示经常使用的功能模块使设计时间减少• 在系统中增加或删除IC不会影响总线的其他电路• 故障诊断和调试都很简单故障可被立即寻迹• 通过聚集一个可再使用的软件模块的库减少软件开发时间除了这些优点外符合I2C总线的CMOS IC还向设计者在特别吸引的可移植装置和电池供电系统方面提供了特殊的功能它们都有• 极低的电流消耗• 抗高噪声干扰• 电源电压范围宽• 工作的温度范围广图1 I 2C 应用的两个例子a 高性能的高度集成电视bDECT 无绳电话基站2.2 厂商的得益符合I 2C 总线的IC 不只帮助了设计者它们也使设备厂商得到很多益处因为• 简单的两线串行I 2C 总线将互联减到最小因此IC 的管脚更少而且PCB 的线路也减少结果使PCB 更小和更便宜• 完全完整的I 2C 总线协议不需要地址译码器和其他胶合逻辑• I 2C 总线的多主机功能允许通过外部连接到生产线快速测试和调整最终用户的设备•符合I 2C 总线的IC 提供SO 小型VSO 超小型以及DIL 封装甚至减少了IC 的空间要求这些只是一些益处另外兼容I 2C 总线的IC 通过允许简单地构造设备变量和保持设计是最新的简易升级功能增加了系统设计的灵活性这样整个装置系列可以围绕一个基本的模型开发新设备的升级或者功能增强的模型即扩展的存储器远程控制等等可以简单地通过剪贴相应的IC 到总线上产生如果需要更大的ROM 只需要从我们广泛的IC 中选择一个有更大ROM 的微控制器就可以了由于新的IC 要取代旧的增加新功能到装置或者提升它的性能只要简单地从总线上移去过时的IC然后换上它的后续IC 就可以了3介绍I2C总线规范对于面向8位的数字控制应用譬如那些要求用微控制器的要建立一些设计标准• 一个完整的系统通常由至少一个微控制器和其他外围器件例如存储器和I/O扩展器组成• 系统中不同器件的连接成本必须最小• 执行控制功能的系统不要求高速的数据传输• 总的效益由选择的器件和互连总线结构的种类决定产生一个满足这些标准的系统需要一个串行的总线结构尽管串行总线没有并行总线的数据吞吐能力但它们只要很少的配线和IC连接管脚然而总线不仅仅是互连的线还包含系统通讯的所有格式和过程串行总线的器件间通讯必须有某种形式的协议避免所有混乱数据丢失和妨碍信息的可能性快速器件必须可以和慢速器件通讯系统必须不能基于所连接的器件否则不可能进行修改或改进应当设计一个过程决定哪些器件何时可以控制总线而且如果有不同时钟速度的器件连接到总线必须定义总线的时钟源所有这些标准都在I2C总线的规范中4I2C总线的概念I2C总线支持任何IC生产过程NMOS CMOS双极性两线――串行数据SDA和串行时钟SCL线在连接到总线的器件间传递信息每个器件都有一个唯一的地址识别无论是微控制器LCD驱动器存储器或键盘接口而且都可以作为一个发送器或接收器由器件的功能决定很明显LCD驱动器只是一个接收器而存储器则既可以接收又可以发送数据除了发送器和接收器外器件在执行数据传输时也可以被看作是主机或从机见表1主机是初始化总线的数据传输并产生允许传输的时钟信号的器件此时任何被寻址的器件都被认为是从机表1 I2C总线术语的定义术语描述发送器发送数据到总线的器件接收器从总线接收数据的器件主机初始化发送产生时钟信号和终止发送的器件从机被主机寻址的器件多主机同时有多于一个主机尝试控制总线但不破坏报文仲裁是一个在有多个主机同时尝试控制总线但只允许其中一个控制总线并使报文不被破坏的过程同步两个或多个器件同步时钟信号的过程I2C总线是一个多主机的总线这就是说可以连接多于一个能控制总线的器件到总线由于主机通常是微控制器让我们考虑以下数据在两个连接到I2C总线的微控制器之间传输的情况见图2这突出了I2C总线的主机从机和接收器发送器的关系应当注意的是这些关系不是持久的只由当时数据传输的方向决定传输数据的过程如下1假设微控制器A要发送信息到微控制器B• 微控制器A主机寻址微控制器B从机• 微控制器A主机发送器发送数据到微控制器B从机接收器• 微控制器A终止传输2如果微控制器A想从微控制器B接收信息• 微控制器A主机寻址微控制器B从机• 微控制器A 主机接收器从微控制器B 从机发送器接收数据 •微控制器A 终止传输甚至在这种情况下主机微控制器A 也产生定时而且终止传输连接多于一个微控制器到I 2C 总线的可能性意味着超过一个主机可以同时尝试初始化传输数据为了避免由此产生混乱发展出一个仲裁过程它依靠线与连接所有I 2C 总线接口到I 2C 总线如果两个或多个主机尝试发送信息到总线在其他主机都产生的情况下首先产生一个1的主机将丢失仲裁仲裁时的时钟信号是用线与连接到SCL 线的主机产生的时钟的同步结合关于仲裁的更详细信息请参考第8章图2 使用两个微控制器的I 2C 总线配置举例在I 2C 总线上产生时钟信号通常是主机器件的责任当在总线上传输数据时每个主机产生自己的时钟信号主机发出的总线时钟信号只有在以下的情况才能被改变慢速的从机器件控制时钟线并延长时钟信号或者在发生仲裁时被另一个主机改变5总体特征SDA 和SCL 都是双向线路都通过一个电流源或上拉电阻连接到正的电源电压见图3当总线空闲时这两条线路都是高电平连接到总线的器件输出级必须是漏极开路或集电极开路才能执行线与的功能I 2C 总线上数据的传输速率在标准模式下可达100kbit/s 在快速模式下可达400kbit/s 在高速模式下可达3.4Mbit/s 连接到总线的接口数量只由总线电容是400pF 的限制决定关于高速模式主机器件的信息请参考第13章6位传输由于连接到I 2C 总线的器件有不同种类的工艺CMOS NMOS 双极性逻辑0低和1高的电平不是固定的它由V DD 的相关电平决定见第15章的电气规范每传输一个数据位就产生一个时钟脉冲6.1 数据的有效性SDA 线上的数据必须在时钟的高电平周期保持稳定数据线的高或低电平状态只有在SCL 线的时钟信号是低电平时才能改变见图4DEVICE 1DEVICE 2图3 标准模式器件和快速模式器件连接到I 2C 总线data line stable;data validchange of data allowedSDASCL图4 I 2C 总线的位传输6.2 起始和停止条件 在I 2C 总线中唯一出现的是被定义为起始S 和停止P 条件见图5的情况其中一种情况是在SCL 线是高电平时SDA 线从高电平向低电平切换这个情况表示起始条件当SCL 是高电平时SDA 线由低电平向高电平切换表示停止条件起始和停止条件一般由主机产生总线在起始条件后被认为处于忙的状态在停止条件的某段时间后总线被认为再次处于空闲状态总线的空闲状态将在第15章详细说明如果产生重复起始Sr条件而不产生停止条件总线会一直处于忙的状态此时的起始条件S和重复起始Sr条件在功能上是一样的见图10因此在本文档的剩余部分符号S 将作为一个通用的术语既表示起始条件又表示重复起始条件除非有特别声明的Sr如果连接到总线的器件合并了必要的接口硬件那么用它们检测起始和停止条件十分简便但是没有这种接口的微控制器在每个时钟周期至少要采样SDA 线两次来判别有没有发生电平切换SDASCLPSTOP conditionSDASCLSSTART condition图5 起始和停止条件7传输数据7.1 字节格式发送到SDA线上的每个字节必须为8位每次传输可以发送的字节数量不受限制每个字节后必须跟一个响应位首先传输的是数据的最高位MSB见图6如果从机要完成一些其他功能后例如一个内部中断服务程序才能接收或发送下一个完整的数据字节可以使时钟线SCL保持低电平迫使主机进入等待状态当从机准备好接收下一个数据字节并释放时钟线SCL后数据传输继续在一些情况下可以用与I2C总线格式不一样的格式例如兼容CBUS的器件甚至在传输一个字节时用这样的地址起始的报文可以通过产生停止条件来终止此时不会产生响应见10.1.3节7.2 响应数据传输必须带响应相关的响应时钟脉冲由主机产生在响应的时钟脉冲期间发送器释放SDA线高在响应的时钟脉冲期间接收器必须将SDA线拉低使它在这个时钟脉冲的高电平期间保持稳定的低电平见图7当然必须考虑建立和保持时间在第15章详细说明通常被寻址的接收器在接收到的每个字节后除了用CBUS地址开头的报文必须产生一个响应见10.1.3节当从机不能响应从机地址时例如它正在执行一些实时函数不能接收或发送从机必须使数据线保持高电平主机然后产生一个停止条件终止传输或者产生重复起始条件开始新的传输如果从机接收器响应了从机地址但是在传输了一段时间后不能接收更多数据字节主机必须再一次终止传输这个情况用从机在第一个字节后没有产生响应来表示从机使数据线保持高电平主机产生一个停止或重复起始条件如果传输中有主机接收器它必须通过在从机不产生时钟的最后一个字节不产生一个响应向从机发送器通知数据结束从机发送器必须释放数据线允许主机产生一个停止或重复起始条件图6 I2C总线的数据传输图7 I 2C 总线的响应8仲裁和时钟发生8.1 同步所有主机在SCL 线上产生它们自己的时钟来传输I 2C 总线上的报文数据只在时钟的高电平周期有效因此需要一个确定的时钟进行逐位仲裁时钟同步通过线与连接I 2C 接口到SCL 线来执行这就是说SCL 线的高到低切换会使器件开始数它们的低电平周期而且一旦器件的时钟变低电平它会使SCL 线保持这种状态直到到达时钟的高电平见图8但是如果另一个时钟仍处于低电平周期这个时钟的低到高切换不会改变SCL 线的状态因此SCL 线被有最长低电平周期的器件保持低电平此时低电平周期短的器件会进入高电平的等待状态C LK 1CLK 2SCLstart counting图8 仲裁过程中的时钟同步当所有有关的器件数完了它们的低电平周期后时钟线被释放并变成高电平之后器件时钟和SCL 线的状态没有差别而且所有器件会开始数它们的高电平周期首先完成高电平周期的器件会再次将SCL 线拉低这样产生的同步SCL 时钟的低电平周期由低电平时钟周期最长的器件决定而高电平周期由高电平时钟周期最短的器件决定8.2 仲裁主机只能在总线空闲的时侯启动传输两个或多个主机可能在起始条件的最小持续时间t HD;STA内产生一个起始条件结果在总线上产生一个规定的起始条件当SCL 线是高电平时仲裁在SDA 线发生这样在其他主机发送低电平时发送高电平的主机将断开它的数据输出级因为总线上的电平与它自己的电平不相同仲裁可以持续多位它的第一个阶段是比较地址位有关的寻址信息请参考第10章和第14章如果每个主机都尝试寻址相同的器件仲裁会继续比较数据位如果是主机发送器或者比较响应位如果是主机接收器因为I 2C 总线的地址和数据信息由赢得仲裁的主机决定在仲裁过程中不会丢失信息丢失仲裁的主机可以产生时钟脉冲直到丢失仲裁的该字节末尾由于Hs 模式的主机有一个唯一的8位主机码因此一般在第一个字节就可以结束仲裁见第13章 如果主机也结合了从机功能而且在寻址阶段丢失仲裁它很可能就是赢得仲裁的主机在寻址的器件因此丢失仲裁的主机必须立即切换到它的从机模式图9显示了两个主机的仲裁过程当然可能包含更多的内容由连接到总线的主机数量决定此时产生DATA1的主机的内部数据电平与SDA 线的实际电平有一些差别如果关断数据输出这就意味着总线连接了一个高输出电平这不会影响由赢得仲裁的主机初始化的数据传输DATA 1DATA 2SDASCL图9 两个主机的仲裁过程由于I 2C 总线的控制只由地址或主机码以及竞争主机发送的数据决定没有中央主机总线也没有任何定制的优先权必须特别注意的是在串行传输时当重复起始条件或停止条件发送到I 2C 总线的时侯仲裁过程仍在进行如果可能产生这样的情况有关的主机必须在帧格式相同位置发送这个重复起始条件或停止条件也就是说仲裁在不能下面情况之间进行• 重复起始条件和数据位 • 停止条件和数据位 • 重复起始条件和停止条件从机不被卷入仲裁过程8.3 用时钟同步机制作为握手 时钟同步机制除了在仲裁过程中使用外还可以用于使能接收器处理字节级或位级的快速数据传输在字节级的快速传输中器件可以快速接收数据字节但需要更多时间保存接收到的字节或准备另一个要发送的字节然后从机以一种握手过程见图6在接收和响应一个字节后使SCL 线保持低电平迫使主机进入等待状态直到从机准备好下一个要传输的字节在位级的快速传输中器件例如对I 2C 总线有或没有限制的微控制器可以通过延长每个时钟的低电平周期减慢总线时钟从而任何主机的速度都可以适配这个器件的内部操作速率在Hs 模式中握手的功能只能在字节级使用见第13章97位的地址格式数据的传输遵循图10所示的格式在起始条件S后发送了一个从机地址这个地址共有7位紧接着的第8位是数据方向位R/W0表示发送写1表示请求数据读数据传输一般由主机产生的停止位P终止但是如果主机仍希望在总线上通讯它可以产生重复起始条件Sr和寻址另一个从机而不是首先产生一个停止条件在这种传输中可能有不同的读写格式结合图10 完整的数据传输可能的数据传输格式有• 主机发送器发送到从机接收器传输的方向不会改变见图11• 在第一个字节后主机立即读从机见图12在第一次响应时主机发送器变成主机接收器从机接收器变成从机发送器第一次响应仍由从机产生之前发送了一个不响应信号A的主机产生停止条件• 复合格式见图13传输改变方向的时侯起始条件和从机地址都会被重复但R/W位取反如果主机接收器发送一个重复起始条件它之前应该发送了一个不响应信号A注意1复合格式可以用于例如控制一个串行存储器在第一个数据字节期间要写内部存储器的位置在重复起始条件和从机地址后数据可被传输2自动增加或减少之前访问的存储器位置等所有决定都由器件的设计者决定3每个字节都跟着一个响应位在序列中用A或A模块表示4兼容I2C总线的器件在接收到起始或重复起始条件时必须复位它们的总线逻辑甚至在这些起始条件没有根据正确的格式放置它们也都期望发送从机地址5起始条件后面立即跟着一个停止条件报文为空是一个不合法的格式图11 主机发送器用7位地址寻址从机接收器传输方向不变图12 在第一个字节后主机立即读从机图13 复合格式107位寻址I2C总线的寻址过程是通常在起始条件后的第一个字节决定了主机选择哪一个从机例外的情况是可以寻址所有器件的广播呼叫地址使用这个地址时理论上所有器件都会发出一个响应但是也可以使器件忽略这个地址广播呼叫地址的第二个字节定义了要采取的行动这个过程将在10.1.1节详细介绍有关10位寻址的信息请参考第14章10.1 第一个字节的位定义第一个字节的头7位组成了从机地址见图14最低位LSB是第8位它决定了报文的方向第一个字节的最低位是0表示主机会写信息到被选中的从机1表示主机会向从机读信息当发送了一个地址后系统中的每个器件都在起始条件后将头7位与它自己的地址比较如果一样器件会任务它被主机寻址至于是从机接收器还是从机发送器都由R/W位决定图14 起始条件后的第一个字节从机地址由一个固定和一个可编程的部分构成由于很可能在一个系统中有几个同样的器件从机地址的可编程部分使最大数量的这些器件可以连接到I2C总线上器件可编程地址位的数量由它可使用的管脚决定例如如果器件有4个固定的和3个可编程的地址位那么相同的总线上共可以连接8个相同的器件I2C总线委员会协调I2C地址的分配进一步的信息可以从最后列出的Philips代理商处获得保留的两组8位地址0000XXX和1111XXX的用途见表2从机地址的11110XX位组合保留给10位寻址见第14章。
I2C 总线实验(实时时钟、EEPROM 和ZLG7290 的实验)一.实验目的加深用户对I2C 总线的理解,熟悉I2C 器件的使用,提供用户实际开发的能力。
二.实验设备及器件IBM PC 机一台DP-51PROC 单片机综合仿真实验仪一台三.实验内容进行I2C 总线控制的实时时钟、EEPROM、ZLG7290 键盘LED 控制器实验。
四.实验要求熟练掌握I2C 总线的控制,灵活运用I2C 主控器软件包,深刻理解实时时钟、EEPROM、ZLG7290 键盘LED 控制的各种功能。
五.实验步骤1.使用导线连接D5 区的SCL、SDA 到A2 区的P16、P17(SCL~P16、SDA~P17),连接D5 区的RST_L、INT_KEY 到A2 区的P10、INT0(RST_L~P10、INT_KEY~INT0),短接D5 区的JP1 跳线。
2.把模拟I2C 软件包“VIIC_C51.C”文件加入到Keil C51 的项目中,程序源文件的开头包含“VIIC_C51.H”头文件。
修改VIIC_C51.C 文件中的sbit SDA=P1^7;和sbit SCL=P1^6;。
图3.21 RTC原理图3.使用函数ISendStr(uchar sla,uchar suba,uchar *s,uchar no)对PCF8563T实时时钟进行设置初始时间,再使用IRcvStr(uchar sla,uchar suba,uchar*s,uchar no)对PCF8563T 实时时钟的时间进行读取。
EEPROM 原理图4.使用函数ISendStr(uchar sla,uchar suba,uchar *s,uchar no);对24WC02EEPROM 进行写入,再使用IRcvStr(uchar sla,uchar suba,uchar *s,uchar no);对24WC02 EEPROM 进行读取。
ZLG7290 原理图5.对ZLG7290 键盘LED 控制器的操作也同理,只是在程序开始的地方增加复位操作和程序中间增加查询是否有键按下。
#include "ctiic.h"#include "delay.h"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////控制I2C速度的延时voidCT_Delay(void){delay_us(10);}//电容触摸芯片IIC接口初始化voidCT_IIC_Init(void){RCC->APB2ENR|=1<<3; //先使能外设IO PORTB时钟RCC->APB2ENR|=1<<7; //先使能外设IO PORTF时钟GPIOB->CRL&=0XFFFFFF0F; //PB1 推挽输出GPIOB->CRL|=0X00000030;GPIOF->CRH&=0XFFFFFF0F; //PF9 推挽输出GPIOF->CRH|=0X00000030;GPIOB->ODR|=1<<1; //PB1 输出高GPIOF->ODR|=1<<9; //PF9 输出高}//产生IIC起始信号voidCT_IIC_Start(void){CT_SDA_OUT(); //sda线输出CT_IIC_SDA=1;CT_IIC_SCL=1;CT_Delay();CT_IIC_SDA=0;//START:when CLK is high,DATA change form high to lowCT_Delay();CT_IIC_SCL=0;//钳住I2C总线,准备发送或接收数据}//产生IIC停止信号voidCT_IIC_Stop(void){CT_SDA_OUT();//sda线输出CT_IIC_SCL=1;CT_Delay();CT_IIC_SDA=0;//STOP:when CLK is high DATA change form low to highCT_IIC_SDA=1;//发送I2C总线结束信号CT_Delay();}//等待应答信号到来//返回值:1,接收应答失败// 0,接收应答成功u8 CT_IIC_Wait_Ack(void){u8ucErrTime=0;CT_SDA_IN(); //SDA设置为输入CT_IIC_SDA=1;delay_us(1);CT_IIC_SCL=1;delay_us(1);while(CT_READ_SDA){ucErrTime++;if(ucErrTime>250){CT_IIC_Stop();return 1;}}CT_IIC_SCL=0;//时钟输出0return 0;}//产生ACK应答voidCT_IIC_Ack(void){CT_IIC_SCL=0;CT_SDA_OUT();CT_IIC_SDA=0;CT_Delay();CT_IIC_SCL=1;CT_Delay();CT_IIC_SCL=0;}//不产生ACK应答voidCT_IIC_NAck(void){CT_IIC_SCL=0;CT_SDA_OUT();CT_IIC_SDA=1;CT_Delay();CT_IIC_SCL=1;CT_IIC_SCL=0;}//IIC发送一个字节//返回从机有无应答//1,有应答//0,无应答voidCT_IIC_Send_Byte(u8 txd){u8 t;CT_SDA_OUT();CT_IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){CT_IIC_SDA=(txd&0x80)>>7;txd<<=1;CT_IIC_SCL=1;CT_Delay();CT_IIC_SCL=0;CT_Delay();}}//读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 CT_IIC_Read_Byte(unsigned char ack){u8i,receive=0;CT_SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){CT_IIC_SCL=0;delay_us(30);CT_IIC_SCL=1;receive<<=1;if(CT_READ_SDA)receive++;}if (!ack)CT_IIC_NAck();//发送nACKelse CT_IIC_Ack(); //发送ACKreturn receive;}#ifndef __MYCT_IIC_H#define __MYCT_IIC_H#include "sys.h"//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////IO方向设置#define CT_SDA_IN() {GPIOF->CRH&=0XFFFFFF0F;GPIOF->CRH|=8<<4;}#define CT_SDA_OUT() {GPIOF->CRH&=0XFFFFFF0F;GPIOF->CRH|=3<<4;}//IO操作函数#define CT_IIC_SCL PBout(1) //SCL#define CT_IIC_SDA PFout(9) //SDA#define CT_READ_SDA PFin(9) //输入SDA//IIC所有操作函数void CT_IIC_Init(void); //初始化IIC的IO口void CT_IIC_Start(void); //发送IIC开始信号void CT_IIC_Stop(void); //发送IIC停止信号void CT_IIC_Send_Byte(u8 txd); //IIC发送一个字节u8 CT_IIC_Read_Byte(unsigned char ack); //IIC读取一个字节u8 CT_IIC_Wait_Ack(void); //IIC等待ACK信号void CT_IIC_Ack(void); //IIC发送ACK信号void CT_IIC_NAck(void); //IIC不发送ACK信号#endif。
#define _24cXX_H/* Includes ----------------------------------------------------------------*/#include "stm32f10x.h"#include "value.h"//#include "stdbool.h"/* Define ------------------------------------------------------------------*//* EEPROM Addresses defines *///注:32 64 的字地址是16位2个字节如果使用32或64请简单修改驱动即可#define WC24cXX 0x00 // 器件地址写#define RC24cXX 0x01 // 器件地址读#define USE_24C08 //使用24C08#ifdef USE_24C02#define MAXSIZE24cXX 256 // 总容量Bytes //级联时请修改本参数和硬件驱动#define BLOCK_SIZE 256 // 块容量Bytes#define I2C_PAGESIZE 8 // 8个字节每页#endif#ifdef USE_24C04#define MAXSIZE24cXX 512 // 总容量Bytes //级联时请修改本参数和硬件驱动#define BLOCK_SIZE 256 // 块容量Bytes#define I2C_PAGESIZE 16 // 16个字节每页#endif#ifdef USE_24C08#define MAXSIZE24cXX 1024 // 总容量Bytes //级联时请修改本参数和硬件驱动#define BLOCK_SIZE 256 // 块容量Bytes#define I2C_PAGESIZE 16 // 16个字节每页/* user define */#define YBCV_ADDR_0 0x0000 //定义仪表控制数据结构体的EEPROM存储地址0#define YBCV_ADDR_1 0x0200 //定义仪表控制数据结构体的EEPROM存储地址1#define EEPROM_VERIFY YB_CTRL_V ALE_SIZE //EEPROM仪表通道修正参数存储地址#endif#ifdef USE_24C16#define MAXSIZE24cXX 2048 // 总容量Bytes#define I2C_PAGESIZE 16 // 16个字节每页#endif#define MAXSIZE24cXX 4096 // 总容量Bytes //级联时请修改本参数和硬件驱动#define BLOCK_SIZE 4096 // 块容量Bytes#define I2C_PAGESIZE 32 // 16个字节每页#endif#ifdef USE_24C64#define MAXSIZE24cXX 8192 // 总容量Bytes //级联时请修改本参数和硬件驱动#define BLOCK_SIZE 8192 // 块容量Bytes#define I2C_PAGESIZE 32 // 16个字节每页#endif#define I2CInit I2C_GPIO_Config#define SCL(a) if (a) \GPIO_SetBits(GPIOB, GPIO_Pin_10);\else \GPIO_ResetBits(GPIOB,GPIO_Pin_10)#define SDA(a) if (a) \GPIO_SetBits(GPIOB, GPIO_Pin_11);\else \GPIO_ResetBits(GPIOB,GPIO_Pin_11)#define SCLO GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_10)#define SDAO GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11)/* Private ------------------------------------------------------------------*//* Public -------------------------------------------------------------------*//*uint idata ucSendBuffer[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};uint idata ucReceData;uint idata ucReceiveBuffer[8];//从器件中读出的多字节数据暂存区*//* Function Declaration -----------------------------------------------------*/extern bool I2C2_Init(void);//I2C初始化//extern bool I2C_ByteWrite(u8* pBuffer, u8 WriteAddr); //向24cXX中写入1个字节extern bool I2C_PageWrite(u8* pBuffer, u8 BlockCode, u16 WriteAddr, u8 n); //24cXX 页写(不超过一页)extern bool I2C_BlockWrite(u8* pBlock, u8 BlockCode, u16 WriteAddr, u16 n);//24cXX数据块写(不超过BLOCK_SIZE个字节)extern bool I2C_BufferWrite(u8* pBuffer, u16 WriteAddr, u16 n); //24cXX数据写(不超过MAXSIZE24cXX个字节)extern bool I2C_BufferRead(u8* pBuffer, u16 ReadAddr, u16 n); //从24cXX中读出N 字节数据(不超过MAXSIZE24cXX个字节)//extern void I2C_EE_WaitEepromStandbyState(void); //等待24CXX内部写周期结束#endif /*_24cXX_H*//******************** (C) COPYRIGHT 2015 XXXXX *********************************** 文件名:24cXX.c* 描述:本函数是xx项目的24cXX的读写函数* 平台:Keil 4 MDK \ stm32 3.5.0库* 库版本:基于野火相关资料及程序上优化修改* 作者:天涯月下红颜醉* 时间:2015.4.19******************************************************************************* ***//* Includes ------------------------------------------------------------------*/#include "24cXX.h"#include "value.h"#include "systick.h"#include <stdlib.h>/** 函数名:I2C2_Init* 描述:I2C2初始化* 输入:无* 输出:无* 调用:内部调用*/bool I2C2_Init(void){bool s = true;GPIO_InitTypeDef GPIO_InitStructure;/* 使能与I2CGPIO 有关的时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);/* PB10-I2C2_SCL、PB11-I2C2_SDA*/GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 普通开漏输出GPIO_Init(GPIOB, &GPIO_InitStructure);SDA(1);SCL(1);Delay_nop();Delay_nop();if(!SDAO) s = false;if(!SCLO) s = false;SDA(0);Delay_nop();Delay_nop();if(SDAO) s = false;SCL(0);Delay_nop();SDA(0);SCL(0);Delay_nop();Delay_nop();if(SDAO) s = false;if(SCLO) s = false;SCL(1);Delay_nop();Delay_nop();SDA(1);return s;}/********开启24cXX的I2C总线********/static bool I2CStart(void){SDA(1);SCL(1);Delay_nop();Delay_nop();if(!SDAO)return false; //SDA线为低电平则总线忙,退出SDA(0);Delay_nop();Delay_nop();if(SDAO)return false; //SDA线为高电平则总线出错,退出SCL(0);Delay_nop();return true;}/********关闭24cXX的I2C总线*******/static void I2CStop(void){SDA(0);SCL(0);Delay_nop();Delay_nop();SCL(1);Delay_nop();Delay_nop();SDA(1);}/*********发送ACK*********/static void I2CAck(void){SDA(0);SCL(0);Delay_nop();// Delay_nop();SCL(1);Delay_nop();// Delay_nop();SCL(0);}/*********发送NO ACK*********/static void I2CNoAck(void){SDA(1);SCL(0);Delay_nop();// Delay_nop();SCL(1);Delay_nop();// Delay_nop();SCL(0);}/*********读取ACK信号*********/static bool I2CWaitAck(void) //返回为:1=有ACK,0=无ACK{SCL(0);SDA(1); //设置SDA为输入Delay_nop();// Delay_nop();SCL(1);Delay_nop();// Delay_nop();if(SDAO){SCL(0);return false;}SCL(0);return true;}/************MCU向24cXX发送一个字节数据*************/ static void I2CSendByte(u8 demand) //数据从高位到低位//{u8 i=8;while(i--){SCL(0);Delay_nop();SDA((bool)(demand&0x80));demand<<=1;Delay_nop();// Delay_nop();SCL(1);Delay_nop();// Delay_nop();}SCL(0);}/*********MCU从24cXX读入一字节数据*********/static u8 I2CReceiveByte(void) //数据从高位到低位//{u8 i=8;u8 ddata=0;SDA(1); //设置SDA为输入while(i--){ddata<<=1; //数据从高位开始读取SCL(0);Delay_nop();// Delay_nop();SCL(1);Delay_nop(); //从高位开始ddata|=SDA;ddata<<=1// Delay_nop();if(SDAO){ddata|=0x01;}}SCL(0);return ddata;}/** 函数名:I2C_EE_WaitEepromStandbyState* 描述:Wait for EEPROM Standby state* 输入:无* 输出:无* 返回:无* 调用:*/static void I2C_EE_WaitEepromStandbyState(u8 BlockCode){int i = 50;do{Delay_us(100);I2CStart();I2CSendByte(BlockCode | WC24cXX);//发送器件地址写}while(I2CWaitAck() == 0 && i-- > 0);I2CStop();}/****************向24cXX中写入1个字节****************/ /*static bool I2C_ByteWrite(u8* pBuffer, u8 WriteAddr){I2CStart();//启动I2CI2CSendByte(WC24cXX);//发送器件地址写return false;I2CSendByte(WriteAddr);if(I2CWaitAck() == 0)return false;I2CSendByte(*pBuffer);if(I2CWaitAck() == 0)return false;I2CStop();return true;}*//** 函数名:I2C_PageWrite* 描述:在EEPROM的一个写循环中可以写多个字节,但一次写入的字节数* 不能超过EEPROM页的大小。
I2C_周立功标准驱动程序_c代码1. /****************************************Copyright(c)**************************************************2. ** 广州周立功单片机发展有限公司3. ** 研究所4. ** 产品一部5. **6. **7. **8. **--------------文件信息--------------------------------------------------------------------------------9. **文件名: I2c.c10. **创建人: 陈明计11. **最后修改日期: 2003年7月21日12. **描述: μCOS-II下LPC210x的I2c主模式底层驱动 13. **14. **--------------历史版本信息----------------------------------------------------------------------------15. ** 创建人: 陈明计16. ** 版本: v1.017. ** 日期: 2003年7月8日18. ** 描述: 原始版本19. **20. **------------------------------------------------------------------------------------------------------21. ** 修改人: 陈明计22. ** 版本: v1.123. ** 日期: 2003年7月21日24. ** 描述: 根据正式文档更改寄存器名25. **26. **--------------当前版本修订------------------------------------------------------------------------------27. ** 修改人:28. ** 日期:29. ** 描述:30. **31. **------------------------------------------------------------------------------------------------------32.************************************************************************ ********************************/33.34. #define IN_I2C35. #include "config.h"36. static uint8 *I2cBuf;37. static OS_EVENT *I2cSem;38. static OS_EVENT *I2cMbox;39. static int16 I2cNbyte;40. static uint8 I2cAddr;41.42. #define I2C_WRITE_END 1 /* 写完成 */ 43. #define I2C_READ_END 2 /* 读完成 */ 44. #define I2C_NOT_GET_BUS 4 /* 丢失仲裁 */ 45. #define I2C_ACK_ERR 8 /* 接收ACK错误 */46.47.48./*********************************************************************** **********************************49. ** 函数名称: I2cInit50. ** 功能描述: 初始化I2c(主模式)51. ** 输入: FI2c:I2c总线频率52. **53. ** 输出:TRUE :成功54. ** FALSE:失败55. ** 全局变量: I2cSem,I2cMbox56. ** 调用模块: OSSemCreate57. **58. ** 作者: 陈明计59. ** 日期: 2003年7月8日60. **-------------------------------------------------------------------------------------------------------61. ** 修改人: 陈明计62. ** 日期: 2003年7月10日63. **-------------------------------------------------------------------------------------------------------64. ** 修改人: 陈明计65. ** 日期: 2003年7月21日66. **------------------------------------------------------------------------------------------------------67.************************************************************************ ********************************/68. uint8 I2cInit(uint32 FI2c)69. {70. VICIntEnClr = 1 << 9; /* 禁止能I2c中断 */ 71. if (FI2c <= 400000)72. {73. PINSEL0 = (PINSEL0 & 0xffffff0f) | 0x50; /* 选择管脚为I2c */ 74. I2CONCLR = 0x6C; /* 清除控制寄存器 */ 75. I2SCLH = (Fpclk / FI2c + 1) / 2; /* 设置高电平时间 */ 76. I2SCLL = (Fpclk / FI2c) / 2; /* 设置低电平时间 */ 77. I2cSem = OSSemCreate(1); /* 信号量用于互斥操作总线 */ 78.I2cMbox = OSMboxCreate(NULL); /* 消息邮箱用于中断向任务传递操作结果 */79. if (I2cMbox == NULL)80. {81. return FALSE;82. }83. if (I2cSem != NULL)84. {85. return TRUE;86. }87. }88. return FALSE;89. }90.91./*********************************************************************** **********************************92. ** 函数名称: __I2cWrite93. ** 功能描述: 读I2C,但不发送STOP信号94. ** 输入: Addr:从机地址95. ** Data:将要写的数据96. ** 写的数据数目97. ** 输出:发送的数据字节数98. **99. ** 全局变量: I2cAddr,I2cNbyte,I2cBuf100. ** 调用模块: OSMboxPend101. **102. ** 作者: 陈明计103. ** 日期: 2003年7月8日104. **-------------------------------------------------------------------------------------------------------105. ** 修改人: 陈明计106. ** 日期: 2003年7月21日107. **------------------------------------------------------------------------------------------------------108.************************************************************************ ********************************/109. uint8 __I2cWrite(uint8 Addr, uint8 *Data, int16 NByte) 110. { 111. uint8 err;112. unsigned int Rt;113.114. I2cAddr = Addr & 0xfe; /* 存储发送地址 */ 115. I2cNbyte = NByte; /* 存储写字节数 */ 116. I2cBuf = Data; /* 存储写的数据的指针 */ 117.I2CONSET = 0x24; /* 设置为主机,并启动总线 */ 118.119. Rt = (unsigned int) OSMboxPend(I2cMbox, 0, &err); /* 等待操作结束 */ 120. return Rt;121. }122.123.124./*********************************************************************** **********************************125. ** 函数名称: I2cWrite126. ** 功能描述: 向I2C从器件写数据127. ** 输入: Addr:从机地址128. ** Data:指向将要写的数据的指针129. ** NByte:写的数据数目130. ** 输出:发送的数据字节数131. **132. ** 全局变量: I2cSem,I2cNbyte133. ** 调用模块: OSSemPend,__I2cWrite,OSSemPost134. **135. ** 作者: 陈明计136. ** 日期: 2003年7月8日137. **-------------------------------------------------------------------------------------------------------138. ** 修改人: 陈明计139. ** 日期: 2003年7月10日140. **-------------------------------------------------------------------------------------------------------141. ** 修改人: 陈明计142. ** 日期: 2003年7月21日143. **------------------------------------------------------------------------------------------------------144.************************************************************************ ********************************/145. uint16 I2cWrite(uint8 Addr, uint8 *Data, int16 NByte) 146. {147. uint8 err;148.149. OSSemPend(I2cSem, 0, &err);150.151. I2CONCLR = 0x6C;152. I2CONSET = 0x40; /* 使能I2c */ 153. VICIntEnable = 1 << 9; /* 使能I2c中断 */ 154.155. if (__I2cWrite(Addr, Data, NByte) == I2C_WRITE_END) 156. { 157. I2CONSET = 1 << 4; /* 发送停止信号 */ 158. I2CONCLR = 0x28; /* 清除标志 */ 159. }160.161. VICIntEnClr = 1 << 9; /* 禁止能I2c中断 */ 162.163. OSSemPost(I2cSem);164. return (NByte - I2cNbyte);165. }166.167./*********************************************************************** **********************************168. ** 函数名称: I2cRead169. ** 功能描述: 从I2c从器件读数据170. ** 输入: Addr:从机地址171. ** Ret:指向返回数据存储位置的指针172. ** Eaddr:扩展地址存储位置173. ** EaddrNByte:扩展地址字节数,0为无174. ** ReadNbyte:将要读取的字节数目175. ** 输出:已读取的字节数176. **177. ** 全局变量: I2cSem,I2cAddr,I2cNbyte,I2cBuf178. ** 调用模块: OSSemPend,__I2cWrite,OSMboxPend,OSSemPost 179. ** 180. ** 作者: 陈明计181. ** 日期: 2003年7月8日182. **-------------------------------------------------------------------------------------------------------183. ** 修改人: 陈明计184. ** 日期: 2003年7月21日185. **------------------------------------------------------------------------------------------------------186.************************************************************************ ********************************/187. int16 I2cRead(uint8 Addr, uint8 *Ret, uint8 *Eaddr, int16 EaddrNByte, int16 ReadNbyte)188. {189. uint8 err;190.191. OSSemPend(I2cSem, 0, &err);192.193. I2CONCLR = 0x6C;194. I2CONSET = 0x40; /* 使能I2c */ 195. VICIntEnable = 1 << 9; /* 使能I2c中断 */ 196.197. if (EaddrNByte > 0)198. {199. if (__I2cWrite(Addr, Eaddr, EaddrNByte) != I2C_WRITE_END) 200. {201. return -1;202. }203. }204.205. I2cAddr = Addr | 0x01; /* 存储发送地址 */ 206. I2cNbyte = ReadNbyte; /* 存储读字节数 */ 207. I2cBuf = Ret; /* 存储读到的数据 */ 208. I2CONCLR = 0x28;209. I2CONSET = 0x24; /* 设置为主机,并启动总线 */ 210. VICIntEnable = 1 << 9; /* 使能I2c中断 */ 211.212. OSMboxPend(I2cMbox, 0, &err); /* 等待操作结束 */ 213.214. VICIntEnClr = 1 << 9; /* 禁止能I2c中断 */ 215.OSSemPost(I2cSem);216. return (ReadNbyte - I2cNbyte);217. }218.219.220./*********************************************************************** **********************************221. ** 函数名称: I2c_Exception222. ** 功能描述: I2c中断服务程序223. ** 输入: 无224. **225. ** 输出: 无226. **227. ** 全局变量: I2cAddr,I2cBuf,I2cNbyte,I2cMbox228. ** 调用模块: OSMboxPost229. **230. ** 作者: 陈明计231. ** 日期: 2003年7月8日232. **-------------------------------------------------------------------------------------------------------233. ** 修改人: 陈明计234. ** 日期: 2003年7月21日235. **------------------------------------------------------------------------------------------------------236.************************************************************************ ********************************/237. void I2c_Exception(void)238. {239. OS_ENTER_CRITICAL();240. switch(I2STAT & 0xf8)241. {242. case 0x08: /* 已发送起始条件,与0x18相同处理 */ 243. // break;244. case 0x10: /* 已发送重复起始条件 */ 245. I2DAT = I2cAddr; /* 发送地址 */ 246. I2CONCLR = 0x28; /* 清除标志 */ 247. break;248. case 0x18: /* 已发送SLA+W,并已接收应答 */ 249. I2DAT =*I2cBuf++;250. I2cNbyte--;251. I2CONCLR = 0x28; /* 清除标志 */ 252. break;253. case 0x28: /* 已发送I2C数据,并接收到应答 */ 254. if (I2cNbyte > 0)255. {256. I2DAT = *I2cBuf++;257. I2cNbyte--;258. I2CONCLR = 0x28; /* 清除标志 */ 259. }260. else261. {262. OSMboxPost(I2cMbox, (void *)I2C_WRITE_END); 263. VICIntEnClr =1 << 9; /* 禁止能I2c中断 */ 264. }265. break;266. case 0x20: /* 已发送SLA+W;已接收非ACK, 与0x48处理相同 */ 267. // break;268. case 0x30: /* 已发送I2DAT中的数据字节;已接收非ACK, 与0x48处理相同 */269. // break;270. case 0x48: /* 已发送SLA+R;已接收非ACK */271. I2CONSET = 1 << 4; /* 发送停止信号 */ 272. OSMboxPost(I2cMbox, (void *)I2C_ACK_ERR); 273. I2CONCLR = 0x28; /* 清除标志 */ 274. break;275. case 0x38: /* 在SLA+R/W或数据字节中丢失仲裁 */ 276. OSMboxPost(I2cMbox, (void *)I2C_NOT_GET_BUS); 277. I2CONCLR = 0x28; /* 清除标志 */ 278. break;279. case 0x40: /* 已发送SLA+R;已接收ACK */ 280. if (I2cNbyte <= 1) 281. {282. I2CONCLR = 1 << 2; /* 下次发送非应答信号 */ 283. }284. else285. {286. I2CONSET= 1 << 2; /* 下次发送应答信号 */ 287. }288. I2CONCLR = 0x28; /* 清除标志 */ 289. break;290. case 0x50: /* 已接收数据字节;已发送ACK */ 291. *I2cBuf++ =I2DAT; /* 接收数据 */ 292. I2cNbyte--;293. if (I2cNbyte <= 1)294. {295. I2CONCLR = 1 << 2; /* 下次发送非应答信号 */ 296. }297. I2CONCLR = 0x28; /* 清除标志 */ 298. break;299. case 0x58: /* 已接收数据字节;已返发送ACK */ 300. *I2cBuf =I2DAT; /* 接收数据 */ 301. I2cNbyte--;302. I2CONSET= 1 << 4; /* 结束总线 */ 303. OSMboxPost(I2cMbox, (void *)I2C_READ_END); 304. I2CONCLR = 0x28; /* 清除标志 */ 305. break;306. default:307. I2CONCLR = 0x28; /* 清除标志 */ 308. break;309. }310.311. VICVectAddr = 0; /* 通知中断控制器中断结束 */ 312.OS_EXIT_CRITICAL();313. }314./*********************************************************************** **********************************315. ** End Of File316.************************************************************************ ********************************/。