IIC通信协议:
双向二线制同步串行总线,只需要两根线即可在总线上器件之间传送信息,两根线分别是SDA和SCL
SDA:双向数据线,为OD门,与其它任意数量的OD与OC门成"线与"关系。
SCL:上升沿将数据输入到每个EEPROM器件中;下降沿驱动EEPROM器件输出数据。(边沿触发)
(在读写的时候,SCL = 1时,SDA保持数据,不能在获取数据;SCL = 0,SDA 可以改变,可以获取读或者写的一个字节。)
SDA(串行数据线)和SCL(串行时钟线)都是双向I/O线,接口电路是开楼输出,需通过上拉电阻接电源VCC,当总线空闲时,两根线都是高电平,连接总线的外同器件都是CMOS器件,输出级也是开漏电路,在总线上消耗的电流很小,因此,总线上扩展的器件数量主要由电容负载来决定,因为每个器件的总线接口都有一定的等效电容.而线路中电容会影响总线传输速度.当电容过大时,有可能造成传输错误.所以,其负载能力为400pF,因此可以估算出总线允许长度和所接器件数量。
特征:
1、在硬件上,I2C总线只需要一根数据线和一根时钟线两根线,总线接口已经集成在芯片内部,而且片上接口电路的滤波器可以滤去总线数据上的毛刺.因此I2C总线简化了硬件电路PCB布线,降低了系统成本,提高了系统可靠性。因为12C芯片除了这两根线和少量中断线,与系统再没有连接的线,用户常用IC可以很容易形成标准化和模块化,便于重复利用。
2、I2C总线是一个真正的多主机总线,如果两个或多个主机同时初始化数据传输,可以通过冲突检测和仲裁防止数据破坏,每个连接到总线上的器件都有唯一的地址,任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机。数据传输和地址设定由软件设定,非常灵活。总线上的器件增加和删除不影响其他器件正常工作。
3、I2C总线可以通过外部连线进行在线检测,便于系统故障诊断和调试,故障可以立即被寻址,软件也利于标准化和模块化,缩短开发时问。
4、连接到相同总线上的IC数量只受总线最大电容的限制,串行的8位双向数据传输位速率在标准模式下可达100Kbit/s,快速模式下可达400Kbit/s,高速模式下可达3.4Mbit/s。
5、)总线具有极低的电流消耗.抗高噪声干扰,增加总线驱动器可以使总线电容扩大10倍,传输距离达到15m;兼容不同电压等级的器件,工作温度范围宽。
1、主设备和从设备
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件,在总线上主和从、发和收的关系是不恒定的,而取决于此时数据传送方向。
系统中的所有外围器件都具有一个7位的"从器件专用地址码",其中高4位为器件类型(比如说AT24C02就是1010),由生产厂家制定,低3位为器件引脚定义地址,由使用者定义。主控器件通过地址码建立多机通信的机制,因此I2C总线省去了外围器件的片选线,这样无论总线上挂接多少个器件,其系统仍然为简约的二线结构。
2、数据传输
发送到SDA上的每个字节必须的8位,每次传输可以发送的字节数量不受限制。每个字节后面都有一个响应位。
首先传输的是数据的最高位(MSB),如果从机要完成一些其他功能后才能接收或发送下一个完整的数据字节,可以使时钟SCL保持低电平,迫使主机进入等待状态,当从机准备好接收下一个数据字节并释放时钟线SCL后数据传输继续。
3、工作原理
如果从机希望主机降低传送速度可以通过将SCL主动拉低延长其低电平时间的方法来通知主机,当主机在准备下一次传送发现SCL的电平被拉低时就进行等待,直至从机完成操作并释放SCL线的控制控制权。这样以来,主机实际上受到从机的时钟同步控制。可见SCL线上的低电平是由时钟低电平最长的器件决定;高电平的时间由高电平时间最短的器件决定。这就是时钟同步,它解决了I2C总线的速度同步问题。
主机发送数据
1)主机在检测到空闲状态(SDA = 1,SCL = 1)时,发送一个启动信号S,开始第一次通信
2)主机发送一个字节,包含了7位的外围设备地址和1位读写控制位R/W组成,0为写,1位读
3)收到命令的从机返回一个ACK确认字节
4)主机接收到从机反馈的命令之后,发送一个存储地址
5)接受到的从机跳到相应的存储地址,然后返回一个ACK确认字节
6)主机收到应答字节之后,开始发送第一个字节的数据
7)从机收到后返回一个ACK
8)主机收到后在继续发下一个字节的数据
9)当主机发送了最后一个字节并且收到了从机的ACK后,发送一个停止信号P结束本次通信并释放总线,从机接到了P之后也退出与主机之间的通信。
注意:
①主机通过发送地址码与对应的从机建立了通信关系,而挂接在总线上的其它从机虽然同时也收到了地址码,但因为与其自身的地址不相符合,因此提前退出与主机的通信;
②主机的一次发送通信,其发送的数据数量不受限制。主机是通过 P 信号通知发送的结束,从机收到 P 信号后退出本次通信;
③主机的每一次发送后都是通过从机的 ACK 信号了解从机的接收状况,如果应答错误则重发。
主机接收数据的流程
1)主机发送了启动信号之后,接着发送命令字节(R/W)
2)从机收到地址字节后,返回一个应答信号并向主机发送数据
3)主机收到数据后向从机返回一个确认字节
4)从机接受了确认字节之后,再向主机发送数据
5)当主机收到了最后一个数据之后,向从机发送一个“非应答”(ACK = 1),从机收到非应答之后便停止发送
6)主机发送了非应答之后,向从机发送一个停止信号P,释放总线,结束通信
4、起始信号和终止信号
起始信号:当SCL置1,SDA由高到低的转换,启动信号是一种电平跳变时序信号,而不是一个电平信号。
终止信号:当SCL为高,SDA由低到高的跳变,停止信号是一种电平跳变时序信号,而不是一个电平信号。
5、应答信号
发送器每发送一个字节,就砸时钟脉冲9期间释放数据线,由接收器反馈一个应答信号,应答信号为低电平时,规定为有效应答位,表示接收器已经成功的接受了该字节;应答信号为高电平时,规定为非应答位,一般表示接收器接受该字节没有成功。
对于反馈有效应答位ACK的要求是:接收器在第9个时钟脉冲之前的低电平将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在他接受最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送并释放SDA线,以便主控接收器发送一个停止信号P
6、数据的有效性
SCL为高电平时,SDA上的数据保持稳定,SCL为低电平时,SDA数据可以发生变化
数据在SCL上升沿之前就要准备好,数据是在SCL上升沿的时候打入到EEPROM中的
EEPROM:
电可擦除可编程的读写存储器,掉电后数据不丢失的存储芯片,可以在电脑上或者和专用设备上擦著已有的信息,重新编程
AT24C20是一个2k串行的CMOS EEPROM,内部含有256个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗。AT24C02有一个8字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。在单片机上的应用广泛, 可以实现掉电数据不丢失功能。
例程:
#include #include"./delay/delay.h" #define succ 0 #define err 1 #define LCDPORT P0 #define LCD_WRITE_DATA 1 #define LCD_WRITE_COM 0 sbit RS = P2^4; sbit RW = P2^5; sbit E = P2^6; sbit SCL = P2^0; sbit SDA = P2^1; bit ack = 0; /*启示信号*/ void iic_start() { SDA = 1;//保证在SCL=1时有SDA的下降沿,所以先进行SDA =1 SCL = 1; delay_us(1); SDA = 0; delay_us(1); SCL = 0;//SCL低电平,使主机处于等待状态 } /*终止信号*/ void iic_stop() { SDA = 0; SCL = 1; delay_us(1); SDA = 1; delay_us(1); SCL = 0; } /*应答子函数*/ void iic_ack() { SDA = 0; SCL = 1; delay_us(1); SCL = 0; } void iic_noack() { SDA = 1; SCL = 1; delay_us(1); SCL = 0; } bit iic_send_byte(unsigned char byte) { unsigned char i; for(i = 0; i < 8; i++) { SDA = byte & 0x80; SCL = 1;//保持数据不变,将数据打入到EEPROM delay_us(1); SCL = 0;//数据可变 byte <<= 1; } SCL = 1;//准备接受应答信号,在SCL高电平期间,SDA拉低 SDA = 1; delay_us(1); if(0 == SDA) { ack = 1;//数据传输正确,产生应答 } else { ack = 0; } SCL = 0;//SCL等待,SDA等待 return ack; } /*接收字节*/ unsigned char iic_rcv_byte() { unsigned char a; unsigned char temp = 0; unsigned char i; SDA = 1;//在上升沿到来之前,做好准备 for(i = 0; i < 8; i++) { SCL = 0; delay_us(1); SCL = 1;//上升沿,表示可以接受数据啦 if(SDA) { a = 0x01; } else { a = 0x0;//SDA为低电平时,读到的数据为0 } temp |= (a << (7-i)); delay_us(1); } SCL = 0; return temp; } unsigned char AT24C02_send_str(unsigned char devaaddr,unsigned char roomaddr,unsigned char *s,unsigned num) { unsigned char i; iic_start(); iic_send_byte(devaaddr);//EEPROM的地址 if(0 == ack) { return err; } iic_send_byte(roomaddr); if(0 == ack) { return err; } for(i = 0; i < num ; i++) { iic_send_byte(*s); if(0 == ack) { return err; } s++; } iic_stop(); return succ; } unsigned char AT24C02_rcv_str(unsigned char devaddr,unsigned char roomaddr,unsigned char *s,unsigned num) { unsigned char i; iic_start(); iic_send_byte(devaddr); if(0 == ack) { return err; } iic_send_byte(roomaddr); if(0 == ack) { return err; } iic_start(); iic_send_byte(devaddr+1); for(i = 0; i < num-1; i++) { *s = iic_rcv_byte(); iic_ack(); s++; } *s = iic_rcv_byte(); iic_noack(); iic_stop(); return succ; } void lcd_write(unsigned char byte,unsigned char flag) { if(flag) { RS = 1; } else { RS = 0; } RW = 0; E = 1; LCDPORT = byte; delay_us(10); E = 0; } void lcd_init() { delay_ms(15); lcd_write(0x38,LCD_WRITE_COM); delay_ms(5); lcd_write(0x38,LCD_WRITE_COM); delay_ms(5); lcd_write(0x38,LCD_WRITE_COM); delay_ms(5); lcd_write(0x38,LCD_WRITE_COM); delay_ms(5); lcd_write(0x38,LCD_WRITE_COM); delay_ms(5); lcd_write(0x08,LCD_WRITE_COM); delay_ms(5); lcd_write(0x01,LCD_WRITE_COM); delay_ms(5); lcd_write(0x06,LCD_WRITE_COM); delay_ms(5); lcd_write(0x0c,LCD_WRITE_COM); delay_ms(5); } void dis_lcd_write(unsigned char x,unsigned char y,unsigned char byte) { unsigned char i = 0; /*byte*/ if(y == 0) { lcd_write(0x80+x,LCD_WRITE_COM); lcd_write(byte,LCD_WRITE_DATA); } if(y == 1) { lcd_write(0x80+0x40+x,LCD_WRITE_COM); lcd_write(byte,LCD_WRITE_DATA); } } void dis_lcd_src(unsigned char x,unsigned char y,unsigned char *src) { if(y == 0) { lcd_write(0x80+x,LCD_WRITE_COM); } if(y == 1) { lcd_write(0x80+0x40+x,LCD_WRITE_COM); } while(*src != '