1、 http:/ http:/ 单片机C语言快速上手,欢迎进入我们的网站下载全套完整教程,请点击:注:本文档出自51 单片机C语言快速上手,欢迎进入我们的网站下载全套完整教程,请点击:http:/ I2C 总线接口技术的应用 I2C 总线接口技术的应用 什么是 I2C 总线?I2C(InterIntegrated Circuit)总线是一种由 PHILIPS 公司开发的两线式串行总线,用于连接微控制器及其外围设备。也可以简单地理解为 I2C是微控制器与外围芯片的一种通讯协议。在不同的书籍中,可能会称为 I2C,IIC,或者 I 平方 C,但是概念也是一样的,只是叫法不同。一I2C 总线特点 I2
2、C 总线特点 I2C 总线的优点非常多,其中最主要体现在 1:硬件结构上具有相同的接口界面;2:电路接口的简单性;3:软件操作的一致性。I2C 总线占用芯片的引脚非常的少,只需要两组信号作为通信的协议,一条为数据线(SDA),另一条为时钟线(SCL)。因此减少了电路板的空间和芯片管脚的数量,所以降低了互联成本。总线的长度可高达 25 英尺,并且能够以 10Kbps 的最大传输速率支持 40 个组件。I2C 总线还具备了另一个优点,就是任何能够进行发送和接收数据的设备都可以成为主控机。当然,在任何时间点上只能允许有一个主控机。图 5-20(总线连接图)二二I2C 总线工作原理总线工作原理 图 5
3、-20 为 I2C 总线的连接图。I2C 总线是由数据线 SDA 和时钟线 SCL 构成的串行总线,可发送和接收数据。在单片机与被控 IC 之间,最高传送速率 100kbps。http:/ http:/ I2C 器件均并联在这条总线上,就像电话线网络一样不会互相冲突,要互相通信就必须拨通其电话号码,每一个 I2C 模块都有唯一地址。并接在 I2C 总线上的模块,既可以是主控器(或被控器),也可以是发送器(或接收器),这取决于它所要完成的功能。I2C 总线在传送数据过程中共有四种类型信号,它们分别是:起始信号、停止信号应答信号与非应答信号。三三I2C 总线数据的传送规则总线数据的传送规则 起始信
4、号:在 I2C 总线工作过程中,当 SCL 为高电平时,SDA 由高电平向低电平跳变,定义为起始信号,起始信号由主控机产生。如图 5-21 所示 图 5-21(开始信号)停止信号:当 SCL 为高电平时,SDA 由低电平向高电平跳变,定义为停止信号,此信号也只能由主控机产生。如图 5-22 所示。图 5-22(停止信号)应答信号:I2C 总线传送的每个字节为 8 位,受控的器件在接收到 8 位数据后,在第 9 个脉冲必须输出低电平作为应答信号,同时,要求主控器在第 9 个时钟脉冲位上释放 SDA 线,以便受控器发出应答信号,将 SDA 拉低,表示接收数据的应答(如图 5-23 所示)。若果在第
5、 9 个脉冲收到受控器的非应答信号(如图 5-24 所示),则表示停止数据的发送或接收。http:/ http:/ 5-23(应答信号)5-24(非应答信号)其次,每启动一次总线,传输的字节数没有限制。主控件和受控器件都可以工作于接收和发送状态。总线必须由主器件控制,也就是说必须由主控器产生时钟信号 起始信号停止信号。在时钟信号为高电平期间,数据线上的数据必须保特稳定,数据线上的数据状态仅在时钟为低电平的期间才能改变(如图 5-25),而当时钟线为高电平的期间,数据线状态的改变被用来表示起始和停止条件(如图 5-21 与 5-22 所示)。图 5-25(数据的有效性)图 5-26 为总线的完整
6、时序,在这里有一点要加以说明的,当主控器接收数据时,在最后一个数据字节,必须发送一个非应答信号,使受控器释放数据线,以便主控器产生一个停止信号来终止总线的数据传送。http:/ http:/ 5-26(总线的完整时序)下面我们来看一下关于 I2C 总线的读操作与写操作:图 5-27(总线写格式)写操作就是主控器件向受控器件发送数据,如图 5-27 所示。首先,主控器会对总线发送起始信号,紧跟应该是第一个字节的 8 位数据,但是从地址只有 7 位,所谓从地址就是受控器的地址,而第 8 位是受控器约定的数据方向位,“0”为写,从图5-26 中我们可以清楚地看到发送完一个 8 位数之后应该是一个受控
7、器的应答信号。应答信号过后就是第二个字节的 8 位数据,这个数多般是受控器件的寄存器地址,寄存器地址过后就是要发送的数据,当数据发送完后就是一个应答信号,每启动一次总线,传输的字节数没有限制,一个字节地址或数据过后的第 9 个脉冲是受控器件应答信号,当数据传送完之后由主控器发出停止信号来停止总线。图 5-28(总线读格式)http:/ http:/ 5-29 为其功能引脚的排列,而 5-30为引脚的功能列表。读操作指受控器件向主控器件发送数,其总线的操作格式如图 5-28。首先,由主控器发出起始信号,前两个传送的字节与写操作相同,但是到了第二个字节之后,就要从新启动总线,改变传送数据的方向,前
8、面两个字节数据方向为写,即“0”;第二次启动总线后数据方向为读,即“1”;之后就是要接收的数据。从图 5-28 的写格式中我们可以看到有两种的应答信号。一种是受控器的,另一种是主控器的。前面三个字节的数据方向均指向受控器件,所以应答信号就由受控器发后出。但是后面要接收的N 个数据则是指向主控器件,所以应答信号应由主控器件发出,当 N 个数据接收完成之后,主控器件应发出一个非应答信号,告知受控器件数据接收完成,不用再发送。最后的停止信号同样也是由主控器发出。四支持四支持 I2C 总线的器件总线的器件 在当今市面上有部分的单片机是内置硬件 I2C 总线的,用户只需要设置好内部相关的寄存器就可以灵活
9、地运用它。但是如果不内置硬件 I2C,我们在使用过程中可以用普通的 I/O 端口进行模拟。如实验板的 STC89C52 芯片,如果要用到 I2C 协议就必需要用到软件模拟。而在非单片机类的芯片当中,如时钟芯片 PCF8563,存储器 24Cxx 系列等都是使用 I2C 协议进行数据的操作,而我们实验板用的则是 24C02。下面我们先来介绍一下 24C02 的引脚功能和内部结构,然后再介绍如何利用单片机模拟 I2C 协议与 24C02进行数据的传送。五五I2C 器件器件 24C02 24C02 是一个 2K 串行 CMOS E2PROM,内部含有 256 个 8 位字节,该器件通过I2C 总 线
10、 接 口 进 行 操作。图 5-29(24Cxx引脚排列)管脚名称 功能 A0 A1 A2 这三个引脚用于多个器件同时使用时设置区分器件地址,当这些引脚悬空时默认为 0。在同一总线中最多可同时使用 8 个 24C02 器件。如果总线只 有一个 24C02 器件被寻址,这三个地址可悬或接地。http:/ http:/ 双向串行数据/地址管脚,用于器件所有数据的发送或接收,SDA 是一个 开漏输出管脚。SCL 串行时钟。串行时钟输入管脚用于产生器件所有数据发送或接收的时钟,这是一个输入管脚 WP 写保护引脚。当 WP 引脚连接到 VCC 时芯片里面的内容为只读内容而不能进行写操作。而当 WP 引脚
11、连接到 VSS 时芯片里面的内容可进行正常 的读/写操作。VCC+1.8V6.0V 工作电压 VSS 地 图 5-30(24Cxx引脚功能)六六24C02 的从地址的从地址 图 5-31(24C02 从器件地址)图 5-32(实验电路)http:/ http:/ 5-31 中我们可以看到从地址的高 4 位是固定的,而低 4 位是可根据 A0 A1 A2 引脚的接法与数据的方向位来确定的,其中 R/W 为“0”时表示写,为“1”时表示读。而图 5-32 为实验板电路,A0 A1 A2 均接地,即为 000。所以在实验板中 24C02 器件的写从地址为 1010 0000(转为十六进制数为 0 x
12、a0),读从地址为 1010 0001(转为十六进制数为 0 xa1)。动手实验(动手实验(8):):实验目的:利用 I2C 总线协议,试验 24C02 断电数据保存的特性。实验内容:下面有两个程序,其中程序一为把 0 x55,0 xaa 这两个数写进 24C02 器件中;然后把实验板断电一会儿后再利用程序二把 24C02 的数据读出来,同时发送到串口调试器中,试看数据在断电期间是否得到保存。程序一:(附光盘的 i2c_W 文件夹中)#include#include#define uchar unsigned char#define uint unsigned int#define Write
13、 0 xa0/写操作#define Read 0 xa1/读操作 sbit SCL=P26;/时钟引脚 sbit SDA=P25;/数据引脚 sbit led=P10;void delay_ms(unsigned int time)/延时 1 毫秒程序,n 是形式参数 unsigned int i,j;for(i=time;i0;i-)/i 不断减 1,一直到 i0 条件不成立为止 for(j=112;j0;j-)/j 不断减 1,一直到 j0 条件不成立为止 ;void lay(void)/延时函数,约延时 10us _nop_();_nop_();_nop_();_nop_();_nop_
14、();http:/ http:/ Start_Cond(void)/启动总线 SCL=0;lay();SCL=1;/发送起始条件时钟信号 lay();SDA=1;/发送起始条件数据信号 lay();/起始条件信号大于 4.7us SDA=0;lay();SCL=0;/锁住总线,准备发送数据 void Stop_Cond(void)/停止总线 SDA=0;/发送结束条件数据信号 SCL=0;lay();SCL=1;/发送结束条件时钟信号 lay();/停止条件信号大于 4.7us SDA=1;lay();SDA=0;void Ack(void)/应答信号 SCL=0;/准备发送应答信号 SDA=
15、0;lay();SCL=1;/时钟信号保持高电平为大于 4.7us lay();SCL=0;/时钟线置低电平 lay();SDA=1;/结束应答信号 http:/ http:/ NoAck(void)/非应答信号 SDA=0;/准备发送非应答信号 SCL=0;lay();SDA=1;/时钟线与数据线为高电平,则为非应答信号 SCL=1;lay();/保持高电平为大于 4.0us SCL=0;/结束非应答信号 lay();SDA=0;void Write8Bit(uchar Word)/向总线写入 word 变量的 8 位数据 uchar i,temp;temp=Word;for(i=0;i8;
16、i+)SCL=0;/当 SCL 为低电平时允许数据改变 temp=1;/移位 SDA=CY;/把移出的数据发送到总线上 SCL=1;/当时钟线为 1 时,数据必顺保持稳定 SCL=0;/每发送 8 位数据要跟随一个时钟信号,等待从机作出应答 lay();SCL=1;uchar Read8Bit(void)uchar i,Result;Result=0;for(i=0;i8;i+)SCL=0;/时钟为低电平,准备接收数据 lay();/时钟低电平大于 4.7us http:/ http:/ Result=(Result1)|SDA;/从数据线读取数据 return Result;/返回从总线上读
17、取到的数据 /*Read_Flash 函数,Array 为接收到数据所存放的地址,nAdd 为要发 送的器件子地址,nLen 为所读取数据的长度*/void Read_Flash(uchar*Array,uchar nAdd,uchar nLen)/从总线读取多个字节函数 Start_Cond();/启动总线 Write8Bit(Write);/发送器件地址 Write8Bit(nAdd);/发送器件子地址 Start_Cond();/从新启动总线 Write8Bit(Read);/读器件地址 while(-nLen)*Array=Read8Bit();/读 8 位数据 Array+;/Arr
18、ay 指向下一个要存放数据的地址 Ack();/每接收到 8 位数据,跟随一个应答信号 *Array=Read8Bit();/最后再读多 8 位 NoAck();/发送非应答信号,停止读取数据 Stop_Cond();/停止总线 /*Write_Flash 函数,Array 为要发送数据的地址,nAdd 为要发 送的器件子地址,nLen 为所发送数据的长度*/void Write_Flash(uchar*Array,uchar nAdd,uchar nLen)/发送多个字节到总线函数 http:/ http:/ i;Start_Cond();/启动总线 Write8Bit(Write);/发送
19、器件地址 Write8Bit(nAdd);/发送器件子地址 for(i=0;inLen;i+)Write8Bit(*Array);/写 8 位数据到总线 Array+;/Array 指向下一个要发送的数据 Stop_Cond();/结束总线 void main(void)uchar send_da2;/存放要发送到 24c02 器件的数据 send_da0=0 x55;send_da1=0 xaa;led=0;/点亮二极管 /*从 0 x00 的子地址写 send_da 数组的 2 个数据*/Write_Flash(send_da,0 x00,2);delay_ms(1000);led=1;/
20、当操作完成之后息灭二极管 while(1);程序二:(附光盘的 i2c_R 文件夹中)#include#include#define uchar unsigned char#define uint unsigned int#define Write 0 xa0/写操作#define Read 0 xa1/读操作 sbit SCL=P26;/时钟引脚 sbit SDA=P25;/数据引脚 sbit led=P10;void delay_ms(unsigned int time)/延时 1 毫秒程序,n 是形式参数 http:/ http:/ int i,j;for(i=time;i0;i-)/i
21、 不断减 1,一直到 i0 条件不成立为止 for(j=112;j0;j-)/j 不断减 1,一直到 j0 条件不成立为止 ;void lay(void)/延时函数,约延时 10us _nop_();_nop_();_nop_();_nop_();_nop_();void Start_Cond(void)/启动总线 SCL=0;lay();SCL=1;/发送起始条件时钟信号 lay();SDA=1;/发送起始条件数据信号 lay();/起始条件信号大于 4。7us SDA=0;lay();SCL=0;/锁住总线,准备发送数据 void Stop_Cond(void)/停止总线 SDA=0;/发
22、送结束条件数据信号 SCL=0;lay();SCL=1;/发送结束条件时钟信号 lay();/停止条件信号大于 4。7us SDA=1;lay();http:/ http:/ Ack(void)/应答信号 SCL=0;/准备发送应答信号 SDA=0;lay();SCL=1;/时钟信号保持高电平为大于 4。7us lay();SCL=0;/时钟线置低电平 lay();SDA=1;/结束应答信号 void NoAck(void)/非应答信号 SDA=0;/准备发送非应答信号 SCL=0;lay();SDA=1;/时钟线与数据线为高电平,则为非应答信号 SCL=1;lay();/保持高电平为大于 4
23、。0us SCL=0;/结束非应答信号 lay();SDA=0;void Write8Bit(uchar Word)/向总线写入 word 变量的 8 位数据 uchar i,temp;temp=Word;for(i=0;i8;i+)SCL=0;/当 SCL 为低电平时允许数据改变 temp=1;/移位 SDA=CY;/把移出的数据发送到总线上 SCL=1;/当时钟线为 1 时,数据必顺保持稳定 http:/ http:/ 8 位数据要跟随一个时钟信号,等待从机作出应答 lay();SCL=1;uchar Read8Bit(void)uchar i,Result;Result=0;for(i=
24、0;i8;i+)SCL=0;/时钟为低电平,准备接收数据 lay();/时钟低电平的同大于 4。7us SCL=1;/当时钟线为高电平时,数据线上的数据有效 Result=(Result1)|SDA;/从数据线读取数据 return Result;/返回从总线上读取的数据 /*Read_Flash 函数,Array 为接收到数据所存放的地址,nAdd 为要发 送的器件子地址,nLen 为所读取数据的长度*/void Read_Flash(uchar*Array,uchar nAdd,uchar nLen)/从总线读取多个字节函数 Start_Cond();/启动总线 Write8Bit(Wri
25、te);/发送器件地址 Write8Bit(nAdd);/发送器件子地址 Start_Cond();/从新启动总线 Write8Bit(Read);/读器件地址 while(-nLen)*Array=Read8Bit();/读 8 位数据 Array+;/Array 指向下一个要存放数据的地址 Ack();/每接收到 8 位数据,跟随一个应答信号 http:/ http:/ 8 位 NoAck();/发送非应答信号,停止读取数据 Stop_Cond();/停止总线 /*Write_Flash 函数,Array 为要发送数据的地址,nAdd 为要发 送的器件子地址,nLen 为所发送数据的长度*
26、/void Write_Flash(uchar*Array,uchar nAdd,uchar nLen)/发送多个字节到总线函数 uchar i;Start_Cond();/启动总线 Write8Bit(Write);/发送器件地址 Write8Bit(nAdd);/发送器件子地址 for(i=0;inLen;i+)Write8Bit(*Array);/写 8 位数据到总线 Array+;/Array 指向下一个要发送的数据 Stop_Cond();/结束总线 void uart(void)/串行口初始化函数,晶振为 11。0592MHZ,波特率为 9600 SCON=0 x40;/选择工作方
27、式 1 PCON=0 x00;/不选用波特率培增 REN=1;/充许接收 TI=0;/清零发送标志位 RI=0;/清零接收标志位 TMOD=0 x20;/先用定时器 1 的工作方式 2 TH1=0 xfd;/对定时器 1 高位赋初值,TL1=0 xfd;/对定时器 1 低位赋初值,TR1=1;/开启定时器 1 void send(uchar _data)/串口发送函数 http:/ http:/ while(TI=0);/等待数据发送完成 TI=0;/清发送标志位 void main(void)uchar incept2;/存放从 24c02 器件读到的数据 uart();/串口初始化。led
28、=0;/*从 0 x00 的子地址读取两个数据存放到 incept 数组中*/Read_Flash(incept,0 x00,2);send(incept0);/把 incept 数组发送到串口调试器中去 send(incept1);delay_ms(1000);led=1;/当操作完成之后息灭二极管 while(1);实验步骤:1打开光盘第 5 章/i2c_W/i2c_W.uv2 工程文件,对程序进行编译,链接,调试产生 i2c_W.hex 烧写文件。将烧写文件烧写到 STC89C52 中去。当 LED灯息灭后表示写数据完成,将实验板断电一会儿。(i2c_W 就是上面的程序一)2.打开光盘第
29、 5 章/i2c_R/i2c_R.uv2 工程文件,对程序进行编译,链接,调试产生 i2c_R.hex 烧写文件。将烧写文件烧写到 STC89C52 中去。(i2c_R就是上面的程序二)3打开串口调试器,选择波特率为 9600,选择十六进制接收,然后打开串口。4按“JP1”使单片机重新上电。当 LED 灯息灭之后,就可以在串口调试器的接收区见到 0 x550 xaa 两个数据(如下图 5-32 所示)。http:/ http:/ 5-32 从上面动手实验我们可以清楚地了解到,我们把电路系统中的重要数据写进了 24C02 数据存储器中去,即使断电,数据还是可以保存在器件当中。这样就可以避免了当电
30、路 系统断电后数据丢失的败端。自我练习:自行编写一个程序,工程名命名为“i2c”。在同一个程序当中,先从 0 x00的地址开始写 5 个数据“0 x01,0 x02,0 x03,0 x04,0 x05”,然后再从 0 x00地址开始读 5 个数据,把读到的数据发送到串口调试器中去。从而测试 I2C总线协议的读写是否成功。程序的设计思路:1在程序的一开始应定义两个数组,一个为存放从器件读到 的数据:incept5;而另一个为存放要写到器件的数据:send_da5,同时存放写到器件的数组应初始化为:“0 x01,0 x02,0 x03,0 x04,0 x05”。2在没有读数据之前把 incept5的五个元素利用串口函数发送到调试器。3利用 Write_Flash(send_da,0 x00,5)函数把数据写进 24C02 数据存储器中去。http:/ http:/ 24C02 器件 中 读 出,同 时 发 送 到 串 口 调 试 器 中 去。对 比incept5数组在读 24C02 的前后就可以得知数据是否读写成功。(以上练习附光盘中)