基于单片机的I2C总线与AT24C01实验 I2C总线通信原理讲解

描述

概述

在单片机应用系统中,串行通信总线技术是非常重要的通信手段。常用的串行总线通信方式包括异步串行通信 UART、I2C(Inter IC BUS)、单总线(One WIRE BUS)以及 SPI 总线(Serial Peripheral Interface BUS)等。单片机的串口通信为 UART 的一种,DS18B20 的通信方式为单总线。采用 I2C 总线通信方式的常用器件包括 E2PROM 存储器件 AT24C01 以及 AD/DA 器件 PCF8951,这章内容主要讲解 I2C 总线通信工作原理并结合 AT24C01 进行应用试验。

13.1 I2C 总线通信原理

I2C(Inter-Integrated Circuit)总线是由 PHILIP S 公司开发的两线式串行通信总线,由于连接主机以及外围设备。两根数据线一个为时钟线 SCL,另一根为数据线 SDA,可实现数据的发送或接收。通常将 I2C 通信速率分为:低速模式 100Kbit/s、快速模式 400Kbit/s 以及高速模式 3.4Mbit/s,I2C 器件为向下兼容模式,一般所用 I2C 器件均支持低速模式。I2C 通信器件典型电路如下图所示:
AT24C01

如上图所示,在 I2C 总线上挂载多个外围器件,总线与电源之间配置了上拉电阻,使所有器件之间形成了“线与”的逻辑关系,任何一个器件将总线拉低,总线将保持低电平,因此任意一个器件都可以当成主设备或者从设备。

I2C 通信最底层的时序操作包含四种类型的信号,所用基于 I2C 总线的外围器件都是在这五种底层信号的基础上进行数据的读写,这五种信号分别是:

1) 起始信号;
2. 停止信号;
3. 写字节信号;
4. 读字节并发送应答信号;
5. 读字节并发送非应答信号。

13.1.1 I2C 通信起始、停止信号

起始信号,功能为通知 I2C 器件可以开始进行数据操作,操作时序为:当 SCL 为高电平时,SDA 由高电平向低电平跳变。停止信号,功能为通知 I2C 器件数据操作已结束,操作时序为:当 SCL 为高电平时,SDA 由低电平向高电平跳变。时序如下图所示:
AT24C01
I2C 起始信号、停止信号时序 C 语言函数如下所示:

void Delay_I2C(void) 
{//延时函数,设置传输速率
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//总线起始信号
void Start_I2C(void)
{
	//SCL高电平期间,拉低SDA
	SCL_I2C = 0;
	SDA_I2C = 1;//在SCL低电平期间先将SDA拉高,为起始信号做准备
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 0;//拉低SDA,发送起始信号
	Delay_I2C();	
	SCL_I2C = 0;
}
//总线停止信号
void Stop_I2C(void)
{
	//SCL高电平期间,拉高SDA
	SCL_I2C = 0;
	SDA_I2C = 0;//在SCL低电平期间先将SDA拉低,为停止信号做准备
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 1;//拉高SDA,发送停止信号
	Delay_I2C();	
	SCL_I2C = 0;	
}

13.1.2 I2C 写字节信号

I2C 写字节信号,功能为向总线写入 1 字节的数据,操作时序下图所示:
AT24C01

在写入数据的过程中,数据顺序为从高位到低位,最先写入的数据为 bit7,依次到 bit0 共 8 位数据。如果接收器件收到了上述 1 字节的数据,会在 SCL 的第 9 个周期的高电平期间将 SDA 拉低为“0”,这个第 9 位数据称为应答位 ACK,作用为通知主机已经收到了 1 字节的数据。因此,在主机程序中通过 ACK 位判断 1 字节数据是否写入成功。在写数据的过程中要求,数据在 SCL 高电平期间要保持 SDA 数据稳定,在 SCL 低电平期间,SDA 可由高电平变为低电平或者低电平变为高电平,如下图所示。
AT24C01
I2C 写字节信号 C 语言函数代码如下图所示:

//I2C写入字节dat,返回应答信号
bit Wr_I2C(unsigned char dat)
{
	bit ack; //存储应答位
	unsigned char mask;	//探测字节内某一位值的掩码变量

	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		if((mask & dat)==0) SDA_I2C=0;
		else                SDA_I2C=1;

		Delay_I2C();
		SCL_I2C = 1;
		Delay_I2C();		
		SCL_I2C = 0;  //完成一位的传送
	}

	SDA_I2C=1;	//主机释放总线
	Delay_I2C();
	SCL_I2C = 1;
	ack = SDA_I2C;//获取应答位
	Delay_I2C();
	SCL_I2C = 0;

	return ack; //返回0写入成功,返回1写入失败
}

13.1.3 I2C 读字节并发送应答信号

I2C 读字节并发送应答信号时序与图 13-4 基本相同,只不过 bit7-bit0 由 I2C 从器件给出,在 SCL 高电平期间主机将数据读取,第 9 位应答信号 ACK 由主机给出,ACK 为“0”表示主机后续还要继续读取数据,为“1”时主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:

//I2C读操作,并发送应答信号
unsigned char RdACK_I2C(void)
{
	unsigned char mask;	//探测字节内某一位值的掩码变量
	unsigned char dat;

	 SDA_I2C=1;//确保主机释放SDA
	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
		else           dat |=  mask;//否则置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=0;	 //8位数据传送完后,拉低SDA发送应答信号
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

13.1.4 I2C 读字节并发送非应答信号

与读字节并发送应答信号相同,唯一的区别为主机发出非应答信号,即 ACK=1,主机不再读取后续数据,可以结束通信。C 语言函数如下图所示:

//I2C读操作,并发送非应答信号
unsigned char RdNAK_I2C(void)
{
	unsigned char mask;	//探测字节内某一位值的掩码变量
	unsigned char dat;

	 SDA_I2C=1;//确保主机释放SDA
	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
		else           dat |=  mask;//否则置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=1;	  //8位数据传送完后,拉高SDA发送非应答信号
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

将上述 5 个底层 I2C 总线操作函数放到文件“Drive_I2C.c”以及“Drive_I2C.h”。

“Drive_I2C.h”完整代码如下:

#ifndef __I2C_H__
#define __I2C_H__

extern void Start_I2C(void); //起始信号
extern void Stop_I2C(void);	 //停止信号
extern unsigned char RdACK_I2C(void); //读字节并发送应答信号
extern unsigned char RdNAK_I2C(void); //读字节并发送非应答信号
extern bit Wr_I2C(unsigned char dat); //读字节信号

#endif

“Drive_I2C.c”完整代码如下:

#include< reg52.h >
#include< intrins.h >


sbit SCL_I2C = P2^0;//总线管脚定义
sbit SDA_I2C = P2^1;

void Delay_I2C(void) 
{//延时函数,设置传输速率
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//总线起始信号
void Start_I2C(void)
{
	//SCL高电平期间,拉低SDA
	SCL_I2C = 0;
	SDA_I2C = 1;//在SCL低电平期间先将SDA拉高,为起始信号做准备
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 0;//拉低SDA,发送起始信号
	Delay_I2C();	
	SCL_I2C = 0;
}
//总线停止信号
void Stop_I2C(void)
{
	//SCL高电平期间,拉高SDA
	SCL_I2C = 0;
	SDA_I2C = 0;//在SCL低电平期间先将SDA拉低,为停止信号做准备
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();		
	SDA_I2C = 1;//拉高SDA,发送停止信号
	Delay_I2C();	
	SCL_I2C = 0;	
}
//I2C写入字节dat,返回应答信号
bit Wr_I2C(unsigned char dat)
{
	bit ack; //存储应答位
	unsigned char mask;	//探测字节内某一位值的掩码变量

	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		if((mask & dat)==0) SDA_I2C=0;
		else                SDA_I2C=1;

		Delay_I2C();
		SCL_I2C = 1;
		Delay_I2C();		
		SCL_I2C = 0;  //完成一位的传送
	}

	SDA_I2C=1;	//主机释放总线
	Delay_I2C();
	SCL_I2C = 1;
	ack = SDA_I2C;//获取应答位
	Delay_I2C();
	SCL_I2C = 0;

	return ack; //返回0写入成功,返回1写入失败
}

//I2C读操作,并发送非应答信号
unsigned char RdNAK_I2C(void)
{
	unsigned char mask;	//探测字节内某一位值的掩码变量
	unsigned char dat;

	 SDA_I2C=1;//确保主机释放SDA
	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
		else           dat |=  mask;//否则置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=1;	  //8位数据传送完后,拉高SDA发送非应答信号
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

//I2C读操作,并发送应答信号
unsigned char RdACK_I2C(void)
{
	unsigned char mask;	//探测字节内某一位值的掩码变量
	unsigned char dat;

	 SDA_I2C=1;//确保主机释放SDA
	for(mask=0x80;mask!=0;mask > >=1)//从高位依次到低位
	{
		Delay_I2C();	
		SCL_I2C = 1;
		if(SDA_I2C==0) dat &= ~mask;//为0时,dat对应位清零
		else           dat |=  mask;//否则置1
		 Delay_I2C();
		 SCL_I2C = 0;
	 }
	SDA_I2C=0;	 //8位数据传送完后,拉低SDA发送应答信号
	Delay_I2C();
	SCL_I2C = 1;
	Delay_I2C();
	SCL_I2C = 0;

	return dat;
}

13.1.5 I2C 一次通信时序

所有基于 I2C 总线通信设备都是以上面 5 条最底层操作为基础的,完成一次完整的 I2C 通信时序如下图所示:
AT24C01

如图所示,一次完整的 I2C 总线通信至少包含起始信号 、一次字节读或写,或者多次读或写,以及停止信号。在起始信号与停止信号之间读或写的具体内容与 I2C 器件本身的上层通信协议有关。接下来我们将讲解基于 I2C 总线通信技术的 E2PROM 存储器 AT24C01 的上层通信协议以及具体使用实例。

13.2 E2PROM 存储器 AT24C01 应用

AT24C01 是 Atmel 公司生产的一款 E2PROM 数据存储器,容量为 128 字节,具有掉电不丢失的功能。在单片机中也有存储器,一种为数据存储器 RAM,一种为程序存储器,在掉电的情况下 RAM 内的数据会丢失,而程序存储器一般不支持在线编程。然而在很多应用场合我们希望把运行过程中重要的数据存储下来,而在掉电的情况下数据不丢失。AT24C01 能满足这样的要求,它与单片之间通过 I2C 总线通信实现信息的交换。
AT24C01

下面介绍一下芯片管脚,A0~A2 为地址输入引脚,SDA、SCL 为 I2C 总线接口,VCC,GND 分别为电源和地,WP 为写保护管脚,当 WP 接高电平时,禁止外部对它进行写数据,只能读取它的数据。如上图所示,将 WP 接地,单片机即可对它进行读也可以写。

13.2.1 AT24C01 单字节写通信

AT24C01 写字节如下图所示:
AT24C01

如上图所示,AT24C01 的写字节时序步骤如下:

1)起始信号;
2. 写器件地址;
3. 写存储地址;
4. 写存储数据;
5. 停止信号。

当 I2C 总线上挂载多个从器件时,单片机通过器件地址来区别器件,那在我们开发板上的 AT24C01 的器件地址是多少呢?AT24C01 器件地址如下所示:
AT24C01
如上图所示,地址的高 4 位为“1010”固定值,即 0x5,后四位分别由 A2-A0、R/W 决定,在 RY-51 开发板上将器件的 A2-A0 都接地,如原理图所示,因此为“000”,最后一位为读写方向位,当 R/W=0 时,表示我们接下来写数据,当 R/W=1 时,表示我们接下来要读数据。很显然我们这里是要写数据,因此 R/W=0。合并起来,要写的地址为 0x50。第 3 步为写存储器地址,AT24C01 总共有 128 字节的存储器,它的地址分别为 0x00~0x80,因此可以选择任一地址存入数据。第 4 步为写存储数据,即为你写存储的 8 位数据。总结上述,往 AT24C01 写入一个字节数据函数如下所示:

//往AT24C01地址addr写入单字节数据dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
	Start_I2C();
	Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
	Wr_I2C(addr);  	//写入要操作的地址addr
	Wr_I2C(dat);		//向addr写入数据dat
	Stop_I2C();
}

13.2.2 AT24C01 单字节读通信

随机读取 AT24C01 单字节通信如下图所示:
AT24C01
随机读取单字节数据通信协议如上图所示,步骤如下:

1) 起始信号
2. 写器件地址,方向为写;
3. 写存储器地址 addr;
4. 起始信号;
5. 写器件地址,方向为读;
6. 读单字节数据,并发送非应答信号
7. 停止信号。

第 1 步至第 3 步为告诉 AT24C01 我将从地址 addr 处读取数据,第 4 步到第 6 步为读取存储器地址 addr 处的数据,并告诉 AT24C01 后面不再继续读数据了,第 7 步结束本次通信。具体函数代码如下图所示:

//读取AT24C01存储地址addr处的数据
unsigned char RdByte_AT24C01(unsigned char addr)
{
	unsigned char dat;	

	Start_I2C();
	Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
	Wr_I2C(addr); //写入要操作的地址addr
	Start_I2C();
	Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
	dat = RdNAK_I2C();//从地址addr读出数据,读出数据后不应答E2Prom
	Stop_I2C();

	return dat;
}

到这里我们便完成了对 AT24C01 单字节的读、写通信函数,按照惯例我们将函数封装到“Drive_AT 24C01.c”、“Drive_AT 24C01.h”,如下。

Drive_AT 24C01.h 代码:

#ifndef __AT24C01_H__
#define __AT24C02_H__

extern void WrByte_AT24C01(unsigned char addr,unsigned char dat);//写单字节
extern unsigned char RdByte_AT24C01(unsigned char addr); //读单字节
extern void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//写多字节
extern void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len);//读多字节

#endif

Drive_AT 24C01.c 代码:

#include< reg52.h >
#include"Drive_I2C.h"

//往AT24C01地址addr写入单字节数据dat
void WrByte_AT24C01(unsigned char addr,unsigned char dat)
{
	Start_I2C();
	Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
	Wr_I2C(addr);  	//写入要操作的地址addr
	Wr_I2C(dat);		//向addr写入数据dat
	Stop_I2C();
}
//读取AT24C01存储地址addr处的数据
unsigned char RdByte_AT24C01(unsigned char addr)
{
	unsigned char dat;	

	Start_I2C();
	Wr_I2C(0x50< < 1);//通知地址50的器件,接下来写操作
	Wr_I2C(addr); //写入要操作的地址addr
	Start_I2C();
	Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
	dat = RdNAK_I2C();//从地址addr读出数据,读出数据后不应答E2Prom
	Stop_I2C();

	return dat;
}
//多字节写
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(len > 0)//检测上一次是否完成所以数据写操作
	{	
		while(1)
		{//循环检测器件应答信号
			Start_I2C();
			if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
			Stop_I2C();//没收到应答,发送停止信号,继续循环检测
		}
		Wr_I2C(addr); //写入要操作的初始地址addr
		
		while(len > 0)
		{
			Wr_I2C(*str++);//写入一个字节,并将字符串指针指向下一个字符
			len--;//字符数减1
			addr++;//存储地址加1	
			if(0 == (addr & 0x07))//检测是否到达了下一页的起始地址,
				break;			//即上一个字节已经写到页的最后边界了
				           		//跳出停止继续写,每页的起始地址后3位为0
								//因此判断addr后3为是否为0即可	   
		}
		Stop_I2C();	
	}			
}
//多字节读
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(1)
	{//循环检测器件应答信号
		Start_I2C();
		if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
		Stop_I2C();//没收到应答,发送停止信号,继续循环检测
	}
	Wr_I2C(addr); //写入要操作的初始地址addr
    Start_I2C();//再次发送起始信号
	Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
	while(len > 1)
	{
		*str++ = RdACK_I2C();//读字节并应答
		len--;
	}
	*str = RdNAK_I2C();//最后一个字节,读字节并非应答
	Stop_I2C();		
}
//寻址AT24C01
bit Addressing_AT24C01(unsigned char addr)
{
	bit ack;

	Start_I2C();
	ack =  Wr_I2C(addr< < 1);
	Stop_I2C();

	return ack;
}

如上所示,所有的代码都是以 I2C 通信的 5 个底层函数为基础的,因此我们需要将“Drive_I2C.h”文件包含到代码中。

13.2.3 AT24C01 单字节读写应用

下面我们建立一个工程,写一个实例来展示单片机对 AT24C01 的读写应用。应用的功能为首先往 AT24C01 存储器地址 0x08 处写入数据 110,然后从该处把数据读出来显示在 1602 液晶上,以此来验证读写操作的正确性,主文件“MainAT24C01.c”代码如下所示:

#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01头文件
#include"Drive_1602.h"

#define uchar unsigned char
#define  uint unsigned int

sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6;	
uchar str[10]=0;
void main()
{
	uchar dat=0;
	Init_1602();
	P0 = 0xff;//关闭所有数码管
	WE = 1;
	WE = 0;

	//往AT24C01存储器地址0x08处写入数字110
	WrByte_AT24C01(0x08,110);
	Disp_1602_str(1,2,"AT24C02 test!");
	//读取AT24C01存储器地址0x08处的数据
	dat = RdByte_AT24C01(0x08);

	str[0]=dat/100+'0';
	str[1]=dat%100/10+'0';
	str[2]=dat%10+'0';
	//将数据显示在1602的第2行第6列处
	Disp_1602_str(2,6,str);

	while(1);
}

将工程编译后,下载到开发板验证功能的正确性。

AT24C01 对写时序有一个特殊的要求,当完成一次数据通信后,需要延迟 tWR 才能开始下一次起始信号如下图所示,tWR 为 AT24C01 内部处理数据时间,查询 AT24C01 数据手册可知为 10ms。因此,我们在主程序代码第 19 行与第 22 行之间插入了第 20 行代码达到延时的目的,我们可以将第 20 行代码挪到后面测试一下效果。
AT24C01

13.2.4 AT24C01 多字节写通信

根据上面的介绍,大家很容易发现每隔 10ms 才能进行一次正常的写数据操作是非常浪费时间的,尤其是在进行多个字节写操作的时候。AT24C01 提供了另外一种多字节的写模式,为页操作模式。首先介绍一下 AT24C01 的内部存储器的分页结构。AT24C01 总共有 128 个字节的存储空间,总共分为 16 页,每一页总有 8 各字节。第一页的地址范围为 0x00~0x07,依次往下均分为 16 页。页操作模式通信时序如下图所示:

AT24C01

如上如所示,首先发送起始信号、写器件地址、写起始地址,紧接着写入多个字节,写完一个字节,器件内部会将地址自动加 1,最后结束信号。这里需要注意的是,连续写的多个字节必须在同一页内,不能进行跨页连续读写,因此一次通信周期内最多可以写入 8 个字节的数据。如果我们需要写的数据很多,而一页写不下怎么办?首先,启动页写通信,在写的过程中判断是否要写到页的边界了,当到达页边界后停止该次页写通信,再重新发起页写通信将剩余的数据写入,这样便实现了任意个字节的写入,具体函数如下图所示。其中,str 为需要写入的字符串,addr 为写入 AT24C01 的起始地址,len 为写入字符个数。多字节写数据代码如下:

//多字节写
void WrStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(len > 0)//检测上一次是否完成所以数据写操作
	{	
		while(1)
		{//循环检测器件应答信号
			Start_I2C();
			if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
			Stop_I2C();//没收到应答,发送停止信号,继续循环检测
		}
		Wr_I2C(addr); //写入要操作的初始地址addr
		
		while(len > 0)
		{
			Wr_I2C(*str++);//写入一个字节,并将字符串指针指向下一个字符
			len--;//字符数减1
			addr++;//存储地址加1	
			if(0 == (addr & 0x07))//检测是否到达了下一页的起始地址,
				break;			//即上一个字节已经写到页的最后边界了
				           		//跳出停止继续写,每页的起始地址后3位为0
								//因此判断addr后3为是否为0即可	   
		}
		Stop_I2C();	
	}			
}

13.2.5 AT24C01 多字节读通信

多字节读时序如下图所示,与单字节类似,只是在第一个数据紧接着着读取多个数据,这里要注意的是只有最后一个字节的数据发送非应答信号,前面的数据均发送应答信号,这个也很好理解,应为我们都到最后一个字节时要告诉 AT24C01 我们不再继续读数据了,因此发送非应答信号。

AT24C01
多字节读取函数代码如下图所示:

//多字节读
void RdStr_AT24C01(unsigned char *str,unsigned char addr,unsigned char len)
{
	while(1)
	{//循环检测器件应答信号
		Start_I2C();
		if(0 == Wr_I2C(0x50< < 1)) break;//收到应答,跳出循环
		Stop_I2C();//没收到应答,发送停止信号,继续循环检测
	}
	Wr_I2C(addr); //写入要操作的初始地址addr
    Start_I2C();//再次发送起始信号
	Wr_I2C((0x50< < 1)|0x01);//通知地址50的器件,接下来读操作
	while(len > 1)
	{
		*str++ = RdACK_I2C();//读字节并应答
		len--;
	}
	*str = RdNAK_I2C();//最后一个字节,读字节并非应答
	Stop_I2C();		
}

将上述的多字节写、多字节读取函数添加到驱动文件“Drive_AT 24C01.c”、“Drive_AT 24C01.h”中,后续应用中只需要将文件添加到项目中,调用相关的函数就可以了。

13.2.6 AT24C01 多字节读写应用

本小节实现 AT24C01 多字节的读写应用,先向 AT24C01 连续写入多字节数据,然后将数据读出显示在 1602 液晶显示模块上,验证读写的正确性。主程序代码如下所示:

#include < reg52.h >
#include"Drive_AT24C01.h" //包含AT24C01头文件
#include"Drive_1602.h"

#define uchar unsigned char
#define  uint unsigned int

sbit DU = P2^7;//数码管段选、位选引脚定义
sbit WE = P2^6;	

uchar str1[]="AT24c01 Wr Str!";
uchar str2[20];

void main()
{
	uchar dat=0;
	P0 = 0;//关闭所有数码管
	WE = 1;
	WE = 0;

	Init_1602();//1602初始化

	WrStr_AT24C01(str1,0x05,16);//写入16个字节
	RdStr_AT24C01(str2,0x05,16);//读取16个字节

	Disp_1602_str(1,1,str2); //将数据从第一行第一列开始显示

	while(1);
}

如代码所示,从地址 0x05 开始写入 16 个字节的数据,由于 AT24C01 为 8 个字节为一页,因此本次为横跨了 3 页的写操作,有效的验证的多字节的写操作。

13.3 本章小结

本章详细介绍了I2C总线的通信原理,以及驱动函数的编写,AT24C01芯片的使用。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分