一. 什么是IIC协议
IIC(Inter-Integrated Circuit)协议是一种同步串行通信接口,它采用半双工工作模式,即在同一时间只能进行单向的数据传输。总线由两条信号线组成:一条是数据线SDA(Serial Data Line),用于传输数据;另一条是时钟线SCL(Serial Clock Line),由主设备提供时钟信号,以确保所有连接到总线的设备同步进行数据交换。
在I2C总线上,每个从设备都有一个唯一的地址,主设备通过发送这个地址来选择与其通信的目标设备。由于支持多主控功能,多个具备主控能力的设备可以在同一总线上竞争控制权,并通过硬件仲裁机制避免冲突。
由于其物理接口简单且占用线路少,I2C被广泛应用于嵌入式系统和电子设备中,方便连接各种低速外设,例如传感器、存储器、时钟芯片等。同时,I2C支持多种速度等级,能满足不同应用环境对数据传输速率的需求。
IIC总线结构:
① 由时钟线SCL和数据线SDA组成,并且都接上拉电阻,确保总线空闲状态为高电平
② 总线支持多设备连接,允许多主机存在,每个设备都有一个唯一的地址
③ 连接到总线上的数目受总线的最大电容400pf限制
④ 数据传输速率:标准模式100k bit/s 快速模式400k bit/s 高速模式3.4Mbit/s
二. IIC协议时序
起始信号(S):当SCL为高电平时,SDA从高电平变为低电平
停止信号(P):当SCL为高电平时,SDA从低电平变为高电平
应答信号:上拉电阻影响下SDA默认为高,而从机拉低SDA就是确认收到数据即ACK,否则NACK
2.1 起始信号的相应代码
//产生IIC起始信号 void IIC_Start(void) { SDA_OUT(); //sda线输出 IIC_SDA=1; IIC_SCL=1; delay_us(); IIC_SDA=0; delay_us(); IIC_SCL=0; //钳住I2C总线,准备发送或接收数据 }
2.2 停止信号的相应代码
//产生IIC停止信号 void IIC_Stop(void) { SDA_OUT();//sda线输出 IIC_SCL=0; IIC_SDA=0; delay_us(); IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号 delay_us(); }
2.3 应答信号的相应代码
u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us();//主机释放SDA线 IIC_SCL=1;delay_us(); while(READ_SDA)//SCL高电平读取SDA状态 { ucErrTime++; if(ucErrTime>250) { IIC_Stop();//SDA高电平表示从机nack return 1; } } IIC_SCL=0;//SCL低电平表示结束ACK检查 return 0; } //产生ACK应答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0;//数据线为低电平,表示应答 delay_us(); IIC_SCL=1; delay_us(); IIC_SCL=0; } //不产生ACK应答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1;//数据线为高电平,表示非应答 delay_us(); IIC_SCL=1; delay_us(); IIC_SCL=0; }
2.4 发送一字节数据的相应代码
//IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输 for(t=0;t<8;t++) { if((txd&0x80)>>7)//检测最高位是否为1 IIC_SDA=1; else IIC_SDA=0; delay_us(); IIC_SCL=1; delay_us(); IIC_SCL=0; delay_us(); txd<<=1; //轮到次高位 } IIC_SDA=1; //发送完成,主机释放SDA线(应答信号) }
2.5 读取一字节数据的相应代码
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(); IIC_SCL=1; receive<<=1; //高位先输出,先收到的数据位要左移 if(READ_SDA)receive++;//置1 delay_us(); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }
三. AT24C02
3.1认识AT24C02
AT24C02是一款基于EEPROM技术的存储芯片,它拥有2K比特(即256字节)的存储容量。这款存储器采用I2C(Inter-Integrated Circuit)总线协议进行通信,通过I2C接口,主机可以对AT24C02进行读写操作,以保存和加载系统配置等信息,在系统上电时能够快速恢复到之前的状态,从而提高了系统的稳定性和灵活性。
EEPROM(Electrically Erasable Programmable Read-Only Memory)是一种非易失性存储器,它的关键特性是在断电后仍能保持存储的数据不丢失。
AT24C02的原理图:
A0/1/2 : 设备地址决定引脚
WP : 写保护引脚(接高电平只读,接地允许读和写)
SCL : 时钟线
SDA : 数据线
24C02由32页(page)组成且每页8Byte:
即可以通过字节(256byte)访问,也可以通过页访问。
3.2 AT24C02通讯地址
写操作地址:0xA0 读操作地址:0xA1
3.3 AT24C02的读写操作
3.3.1 写操作
AT24C02支持字节写模式和页写模式。
字节写模式:一个地址一个数据进行写入。(支持这种)
页写模式: 连续写入数据。只需要写一个地址,连续写入数据时地址会自增,但存在页的限制,超出一页时,超出数据覆盖原先写入的数据。但读会自动翻页。(比如要写入9字节,8个字节已被写满,还剩1字节则会覆盖掉页头的数据)
AT24C02写时序
时序描述:
注意:
EEPROM比较慢,必须等到10ms后再写下一个字节。
相关代码:
//在AT24CXX指定地址写入一个数据 //WriteAddr :写入数据的目的地址 //DataToWrite:要写入的数据 void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite) { /*1.起始信号*/ IIC_Start(); /*2.发送通信地址(写操作)*/ IIC_Send_Byte(0XA0); /*3.等待应答型号*/ IIC_Wait_Ack(); /*4.发送要写入的地址*/ IIC_Send_Byte(WriteAddr); /*5.等待应答型号*/ IIC_Wait_Ack(); /*6.发送要写入的数据*/ IIC_Send_Byte(DataToWrite); /*7.等待应答型号*/ IIC_Wait_Ack(); /*8.停止信号*/ IIC_Stop(); /*等待EEPROM写入完成,才可以写入下一字节*/ delay_ms(10); } /*连续写操作*/ //在AT24CXX里面的指定地址开始写入指定个数的数据 //WriteAddr :开始写入的地址 对24c02为0~255 //pBuffer :数据数组首地址 //NumToWrite:要写入数据的个数 void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) { while(NumToWrite--) { AT24CXX_WriteOneByte(WriteAddr,*pBuffer); WriteAddr++; pBuffer++; } }
3.3.2 读操作
AT24C02支持当前地址读模式,随机地址读模式和顺序读模式。
当前读模式: 基于上一次读/写操作的最后位置继续读出数据。
随机地址读模式:指定地址读出数据。(支持这种)
顺序读模式: 连续读出数据。
AT24C02读时序
时序描述:
//在AT24CXX指定地址读出一个数据 //ReadAddr:开始读数的地址 //返回值 :读到的数据 u8 AT24CXX_ReadOneByte(u16 ReadAddr) { u8 temp=0; /*1.起始信号*/ IIC_Start(); /*2.发送通信地址(写操作)*/ IIC_Send_Byte(0XA0); /*3.等待应答型号*/ IIC_Wait_Ack(); /*4.发送要读取的地址*/ IIC_Send_Byte(ReadAddr); /*5.等待应答型号*/ IIC_Wait_Ack(); /*6.起始信号*/ IIC_Start(); /*7.发送通信地址(读操作)*/ IIC_Send_Byte(0XA1); /*8.等待应答型号*/ IIC_Wait_Ack(); /*9.读取数据,并发送非应答信号*/ temp=IIC_Read_Byte(0); /*10.停止信号*/ IIC_Stop(); return temp; } /*连续读操作*/ //在AT24CXX里面的指定地址开始读出指定个数的数据 //ReadAddr :开始读出的地址 对24c02为0~255 //pBuffer :数据数组首地址 //NumToRead:要读出数据的个数 void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) { while(NumToRead) { *pBuffer++=AT24CXX_ReadOneByte(ReadAddr++); NumToRead--; } }