IIC通信结构
AT24C02接线图
时序图
上图可知最小脉宽大于4.7us可兼容所有情况,每个写周期要大于10mS;
IIC的通信信号:
开始与结束信号
当SCL为1,SDA由高变低为开始,由低变高为结束;
代码:
void Start()
{
SDA = 1;
SCL = 1;
Delay10us(1);
SDA = 0;
Delay10us(1);
SCL = 0;
Delay10us(1);
}
void Stop()
{
SDA = 0;
Delay10us(1);
SCL = 1;
Delay10us(1);
SDA = 1;
Delay10us(1);
SCL = 0;
Delay10us(1);
}
void Delay10us(u16 x) //延时10us
{
while(x--);
}
应答信号
在SCL为高时,SDA得到低电平为有效应答。
代码:
主机发完8位数据后,从机会发个0应答;
主机接收从机的8位数据后,主机要发个0应答;
应答为1则认为错误。
//主机发送后,接收从机应答
void Slave_Ack()
{
SCL = 1;
Delay10us(1);
if(SDA == 1)
return;
SCL = 0;
Delay10us(1);
}
//主机接收从机的8位数据后,主机要发个0应答
void Master_Ack(bit ack) //1:有应答,0:无应答
{
SCL = 0;
Delay10us(1);
SDA = ~ack;
Delay10us(1);
SCL = 1;
Delay10us(1);
SCL = 0;
Delay10us(1);
SDA = 1;
Delay10us(1);
}
数据信号
SCL为0时,SDA改变数据,SCL为1时,SDA保持不变,供设备读取数据;
代码:
发送和接收数据:
void Write_Byte(u8 dat)
{
u8 i;
for(i=0;i<8;i++)
{
SCL = 0;
Delay10us(1);
SDA = dat & 0x80;
Delay10us(1);
SCL = 1;
Delay10us(1);
dat <<= 1;
}
SCL = 0;
Delay10us(1);
SDA = 1;
Delay10us(1);
}
u8 Read_Byte()
{
u8 i,rdata;
SCL = 0;
Delay10us(1);
SDA = 1;
Delay10us(1);
for(i=0;i<8;i++)
{
Delay10us(1);
SCL = 1;
Delay10us(1);
rdata = rdata<<1;
rdata = rdata | SDA;
SCL = 0;
}
Delay10us(1);
return rdata;
}
写1字节
从上图可知,信号依次分别为:
Start——开始
DEVICE ADDRESS——设备地址
ACK——从机应答0
WORD ADDRESS——设备内的字节地址
ACK——从机应答0
DATA——发送的8位数据
ACK——从机应答0
STOP——结束
DEVICE ADDRESS由高4位下图决定
A0、A1、A2由硬件连接决定,高为1,低为0;
R/W位,当下一步要写数据时,置0;当下一步读取数据时,置1;
WORD ADDRESS的值由用户决定,最大值按照芯片决定
AT24C02A——2K (256 x 8)——0~256
AT24C04A——4K (512 x 8)——0~512
AT24C08A——8K (1024 x 8)——0~1024
AT24C16A——16K (2048 x 8)——0~2048
DATA由用户决定,范围0~255
代码:
void Write_Addr(u8 addr,u8 dat)
{
Start();
Write_Byte(0xae); //设备地址:1010-111-0
Slave_Ack();
Write_Byte(addr); //设备内地址:例如100
Slave_Ack();
Write_Byte(dat); //待存的数据:例如110
Slave_Ack();
Stop();
Delay10us(1000); //1次写周期要大于10ms延时
}
读取1字节
从上图可知,信号依次分别为:
Start——开始
DEVICE ADDRESS——设备地址
ACK——从机应答0
WORD ADDRESS——设备内的字节地址
ACK——从机应答0
——————以上部分与写操作一样————————
Start——开始
DEVICE ADDRESS——设备地址——下一步要读取数据所以R/W位需置1
ACK——从机应答0
DATA——接收的8位数据
ACK——主机应答1
STOP——结束
代码:
u8 Read_Addr(u8 addr)
{
u8 r_data;
Start();
Write_Byte(0xae); //设备地址:1010-111-0
Slave_Ack();
Write_Byte(addr);
Slave_Ack();
Start();
Write_Byte(0xaf); //设备地址:1010-111-1
Slave_Ack();
r_data = Read_Byte();
Master_Ack(0);
Stop();
return r_data;
}
页写
at24c02支持页写,可提高效率;
与1字节写的区别在于写完1个字节并接受到从机应答后,可继续发送字节,直到发送Stop为止。
但不同规格芯片的页写范围不同
AT24C02A——2K (256 x 8)
AT24C04A——4K (512 x 8)
AT24C08A——8K (1024 x 8)
AT24C16A——16K (2048 x 8)
数据手册原文:在收到每个数据字后,数据字地址较低的三个(2K)或四个(4K、8K、16K)位在内部递增。较高的数据字地址位不增加,保留内存页行位置。当内部生成的字address到达页面边界时,下一个字节被放在同一页面的开头。如果传送到EEPROM的数据字超过8个(2K)或16个(4K、8K、16K),则数据字地址将滚转,并覆盖之前的数据。
代码:
为解决跨页问题,代码在每次地址低3位为1时(16字节的要改为低4位为1时),结束发送,重新开始一个新的写操作。
//跨页写
//addr:写的数据地址
//s:输出的数据
//num:数据长度
void Write_Span_Page(u8 addr , u8 *s , u8 num)
{
while(num>0)
{
Start();
Write_Byte(0xae);
Slave_Ack();
Write_Byte(addr);
Slave_Ack();
while(num>0)
{
Write_Byte(*s++);
Slave_Ack();
addr++;
num--;
if(!(addr & 0x07)) break; //低3位写满换下一页
}
Stop();
Delay10us(1000);
}
}
页读
页读与1字节读取的区别为,读取完后要回复Ack0,并继续接受下个数据,直到回复Ack1并发Stop。
页读与页写一样有页字节限制;
代码:
void Read_Span_Page(u8 addr ,u8 *s , u8 num)
{
while(num>0)
{
Start();
Write_Byte(0xae);
Slave_Ack();
Write_Byte(addr);
Slave_Ack();
Start();
Write_Byte(0xaf);
Slave_Ack();
while(num>0)
{
*s = Read_Byte();
s++;
addr++;
num--;
if(!(addr & 0x07))
{
Master_Ack(0); //无应答
break; //低3位读完换下一页
}
else
{
Master_Ack(1);
}
}
Stop();
}
}
代码中的相关定义:
sbit SDA = P2^1;
sbit SCL = P2^0;
#define u8 unsigned char
#define u16 unsigned short