基于51单片机IIC通信的AT24C02学习笔记
引言
最近在学习几种串行通信协议,感觉收获很多,这篇文章是学习IIC总线协议的第一篇文章,以后还会再写一篇关于PCF8591 IIC通信的ADDA转换芯片的文章.
关于IIC总线
IIC 即Inter-Integrated Circuit(集成电路总线),这种总线类型是由飞利浦半导体公司在八十年代初设计出来的一种简单、双向、二线制、同步串行总线,主要是用来连接整体电路(ICS) ,IIC是一种多向控制总线,也就是说多个芯片可以连接到同一总线结构下,同时每个芯片都可以作为实时数据传输的控制源。主要包括启始、停止、读、写、应答信号。这种方式简化了信号传输总线接口。
IIC总线上可以挂多个器件,而每个器件都有唯一的地址,这样可以标识通信目标。数据的通信的方式采用主从方式,主机负责主动联系从机,而从机则被动回应数据。
关于AT24C02
AT24C02是一个2K位串行CMOS E2PROM, 内部含有256个8位字节,CATALYST公司的先进CMOS技术实质上减少了器件的功耗。AT24C02有一个8字节页写缓冲器。该器件通过IIC总线接口进行操作,有一个专门的写保护功能。在单片机上的应用广泛, 可以实现掉电数据不丢失功能。
IIC总线数据有效性的规定
SCL=1期间,SDA必须保持稳定,SCL=0时,SDA才允许改变。
IIC 总线的几种信号
1.起始和终止信号:
从时序图可以看出,起始信号为,SCL=1,SDA下降沿;终止信号为SCL=1,SDA为上升沿,保持的时间是有限制的:
具体的程序实现如下:
起始信号:SDA=1保持时间大于4。7us,随后SDA=0保持时间大于4us
1
2
3
4
5
6
7
8
9
|
/*SDA下降沿*/
void start()
{
SDA
= 1; //顺序不能反了,先是SDA=1;
SCL
= 1;
delay();
SDA
= 0;
delay();
}
|
终止信号:SDA=0保持时间大于4us,随后SDA=1保持时间大于4。7us
1
2
3
4
5
6
7
8
9
|
/*SDA上升沿*/
void end()
{
SDA
= 0; //顺序不能反了,先是SDA=0;
SCL
= 1;
delay();
SDA
= 1;
delay();
}
|
2.应答信号:
传送的每一个字节必须保证是8位长度。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答信号(即一帧共有9位)。
如:主机往总线上发送一个字节的数据后,释放总线,从机会把总线拉低(即应答信号),以表示这一字节发送成功
同理主机从总线上读取完一个字节的数据后,主机会把总线拉低,"告诉"从机这一字节的数据接收成功
程序实现
应答信号:
1
2
3
4
5
6
7
8
9
|
void ack()
{
uchar
i = 0;
SCL
= 1;
delay();
while (SDA
== 1 && i < 250)i++;
SCL
= 0; //SCL=0,SDA可以改变
delay();
}
|
3.AT24C02的器件地址:
根据数据手册和原理图可以看出器件地址为:0x90/0x91其中最后一位是读写方向位,若下一个字节为向总线上发送数据,则为0x90,若下一个字节是从总线上读取数据,则为0x91。
4.往总线上写一字节的数据
程序实现:先写最高位,通过左移运算符,将一字节的数据一位一位的传送到总线上,其中CY存储的是左移后的进位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
void write_byte(uchar
dat)
{
uchar
i;
for (i
= 0; i < 8; i++)
{
dat
= dat << 1; //如1011
0011 左移一位为0110 0110
SCL
= 0; //SDL=0,才能往SDA送数据,或从SDA读数据
delay();
SDA
= CY; //上面左移后进位为1
delay();
SCL
= 1;
delay();
}
SCL
= 0;
//
delay();
SDA
= 1; //释放SDA
delay();
}
|
5.从总线上读取一个字节的数据
程序实现:带返回值,先读高位,然后通过移位运算符,一位一位读入
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
uchar
read_byte()
{
uchar
dat = 0, tmp, i;
SCL
= 0;
delay();
for (i
= 0; i < 8; i++)
{
SCL
= 1; //让SDA保持稳定,准备读取
delay();
tmp
= SDA; //读取此时SDA的状态
dat
= dat << 1;
dat
= dat | tmp;
delay();
SCL
= 0; //释放SDA总线,为下一次读取做准备
delay();
}
return dat;
}
|
AT24C02读写操作
1.通过以上几种信号的组合,可以向AT24C02指定单元地址写一字节的数据,可以看出,读写顺序为:起始,写器件地址,应答,写单元地址,应答,写数据,应答,终止。
程序实现:此函数无返回值,有两个形参, 内存单元地址,范围0-255;要写的数据,如0xfe;
1
2
3
4
5
6
7
8
9
10
11
|
void write_AT24C02(uchar
unit_addr, uchar dat)
{
start();
write_byte(AT24C02_ADDR
+ 0); //下一个字节为写,所以是+0
ack();
write_byte(unit_addr);
ack();
write_byte(dat);
ack();
end();
}
|
2.从AT24C02任意单元地址读取数据,可以看出读写顺序为:起始,写器件地址+0(下一个字节为写),应答,写单元地址,应答,起始,写器件地址+1(下一个字节为读取),读取数据,终止。
具体程序实现:此函数有返回值,一个形参;单元地址,范围:0-255;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
uchar
read_AT24C02(uchar unit_addr)
{
uchar
dat;
/*先写数据*/
start();
write_byte(AT24C02_ADDR
+ 0); //下一个字节为写
ack();
write_byte(unit_addr);
ack();
start();
write_byte(AT24C02_ADDR
+ 1); //下一个字节为读
ack();
dat
= read_byte();
end();
return dat;
}
|
延时函数:
1
2
3
4
|
void delay() //短暂5us延时
{
;;
}
|
IIC初始化:
void I2C_init() //初始化 { SDA = 1; delay(); SCL = 1; delay(); }
具体应用
有了以上几个函数,我们就可以写主函数了, 功能是向内存单元211,写入数据0xae,然后在从内存单元211中读取出来,送给P1口led显示.注意:写完后要延时一会才能读取,否则不能成功读取.
1
2
3
4
5
6
7
|
void main()
{
write_AT24C02(211,
0xae);
delay_10ms(); //写完后,必须延时一定的时间才可以读取,否则不行
P1
= read_AT24C02(211);
while (1);
}
|