51单片机 - I2C读写EEPROM


1> 实验目的

实现51单片机对EEPROM - AT24C02,1字节的读写操作;
通过串口打印到串口终端;

小技巧:
理解I2C,他待会拉高,待会拉低,挺烦人,
可以换个角度思考下,为什么他这样设计;

1


2> AT24C02-灵魂4问?


2.1> 长啥样?

2.1.1> 实物图

2.1

2.1.2> 引脚图

1


2.2> 能干哈?

AT24C02是EEPROM存储器,可以形象的看作是个【本本】, 用于记录数据
11

或者想象成一个存储格子

1

型号含义:
332

AT24C02:
32

8Byte X 32 X 8bit = 2048bit;
每个字节的地址为 0x00~0xFF;



2.3> 咋干的?


2.3.1> 内部框图

3.1


2.3.2> AT24C02设备地址

在这里插入图片描述

高4位固定位“1010”,
A2~A0由外部电路配置,拉高为1,拉低为0;
从机只有一个器件时, 通常都拉低,所以为【1010 000X】;


2.3.3> AT24C02存储单元地址

23

AT24C02 存储单元地址范围:0x00 ~ 0xFF;共256字节;


2.4> 咋用他?

2.4.1> 硬件设计

3.1


2.4.2> 程序设计

单片机 通过I2C通信来读写,AT24C02


3> I2C 通信协议

IIC总线(Inter-Integrated Circuit)

传输方式:串行;
传输方向:半双工;
同步方式:同步;

速度:
标准模式:100Kbit/s;
快速模式:400Kbit/s;
高速模式:3.4Mbit/s;


3.1> 写1个字节

流程图:
1


时序图:
311


3.2> 读1个字节

流程图:
2.2

注意:读写控制为,写为“0”,读为1;

随机读1字节,时序图:
322


3.3> 起停信号

231

起始信号:SCL高电平期间,SDA产生下降沿,表示开始传输数据;

停止信号:SCL高电平期间,SDA产生上升沿,表示结束传输数据;


3.4> 主机写:数据信号

34

3.4

在每个SCL的上升沿,【主机】51单片机将SDA数据写入【从机】AT24C02;
所以在SCL的上升沿到来前,需要将数据在SDA上放好;
第8个SCL下降沿,【主机】51单片机要释放SDA,为AT24C02拉低SDA做准备;


3.5> 主机读:数据信号

64

【从机】AT24C02,在每个SCL的下降沿,向SDA写数据;
那么【主机】51单片机就可以在上SCL的上升沿和高电平期间读数据;


3.6> 应答信号

234

主机:SCL高电平期间,向SDA写0应答,写1非应答;
从机:SCL高电平期间,向SDA写0应答,写1非应答;

这样设计很合理, 把总线拉低,代表有响应,就是应答信号,
总线拉高,总线空闲,正好就是非应答,不回应了;



4> AT24C02时序参数(1000KHz)

4

写周期最大为5ms


42
43



5> 程序设计


5.1> 实现功能

实现对AT24C02,1字节的读写操作模块;
通过串口打印到串口终端;


5.2> 编程思路

参考其他人代码,总结;

52

1》 实现,启动,停止,读写数据,应答,单个操作代码,然后拼装整个时序;
2》 IIC时序时一层代码,对AT24C02的读写是上一层;


5.3> I2C时序-底层驱动

/**
 * @brief	: I2C start condition 
 */
void I2C_Start(void)
{
	I2C_SDA = 1;	// 初始化
	I2C_SCL = 1;
	
	I2C_SDA = 0;	// SCL高电平期间,SDA拉低,产生下降沿;
	I2C_SCL = 0;	
}


/**
 * @brief : I2C Stop condition
 */
void I2C_Stop(void)
{
	I2C_SDA = 0;
	
	I2C_SCL = 1;
	I2C_SDA = 1;	// SCL高电平期间,SDA产生上升沿; 
}

/**
 * @brief	:Master wait Acknowledge of Slave
 */
void I2C_Wait_Ack(void)
{
	I2C_SCL = 1;
	// 判断AT24C02是否拉低,暂时忽略
	I2C_SCL = 0;
}

/**
 * @brief	:Master send No Acknowledge to Slave
 */
void I2C_Write_NAck(void)
{
	I2C_SDA = 1;
	
	I2C_SCL = 1;
	I2C_SCL = 0;

}


/**
 * @brief	: Master write byte
 * @dat		: 8-bit data
 */
void I2C_Write_Byte(uchar8_t dat)
{
	uchar8_t i = 0;
	
	for (i = 0; i < 8; i++) {
		
		I2C_SDA = dat >> 7;	 // 先发高位bit7
		dat = dat << 1;
		
		I2C_SCL = 1;
		I2C_SCL = 0;
	}

	I2C_SDA = 1;	// 51单片机释放总线
}

/**
 * @brief	: Master Read byte
 * @retval	: 8-bit data from AT24C02
 */
uchar8_t I2C_Read_Byte(void)
{
	uchar8_t i = 0;
	uchar8_t dat = 0;
	
	for (i = 0; i < 8; i++) {
		I2C_SCL = 1;
		
		dat = dat << 1;
		dat |= I2C_SDA;		// 先接收高位
		
		I2C_SCL = 0;
	}
	
	return dat;
}

5.4> EEPROM 读写程序

/**
 * @brief	    : Master wrie byte
 * @device_addr	: device address Byte
 * @word_addr	: Word address Byte
 * @dat			: 8-bit Data word
 */
void EEPROM_Write_Byte(uchar8_t device_addr, uchar8_t word_addr, uchar8_t dat)
{
	I2C_Start();
	
	I2C_Write_Byte(device_addr);
	I2C_Wait_Ack();
	
	I2C_Write_Byte(word_addr);
	I2C_Wait_Ack();
	
	I2C_Write_Byte(dat);
	I2C_Wait_Ack();
	
	I2C_Stop();
}
/**
 * @brief	    : Master random read byte
 * @device_addr	: device address Byte
 * @word_addr	: Word address Byte
 * @retval		: 8-bit Data word from AT24C02
 */
uchar8_t EEPROM_Read_Byte(uchar8_t device_addr, uchar8_t word_addr)
{
	uchar8_t dat = 0;
	
	/* 第1阶段:伪写 */
	I2C_Start();
	
	I2C_Write_Byte(device_addr);
	I2C_Wait_Ack();
	
	I2C_Write_Byte(word_addr);
	I2C_Wait_Ack();
	
	/* 第2阶段:读数据 */
	I2C_Start();
	
	I2C_Write_Byte(device_addr | 0x01);  // read
	I2C_Wait_Ack();
	
	dat = I2C_Read_Byte();
	I2C_Write_NAck();
	
	I2C_Stop();
	
	return dat;
}


6> 实验波形-逻辑分析仪

6.1> 写时序

6

0x0F是为方便观察应答信号,写的值;


6.2> 应答信号细节

1
不用逻辑分析仪抓下波形,还就真不知道!有意思


6.3> 读1字节时序

63


6.4> AT24C02输出数据

64


6.5> 读写时间间隔

65



7> 视频链接

🔗 B站视频

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是51单片机EEPROM读写程序的基本框架: ```c #include<reg52.h> #include<intrins.h> #define uchar unsigned char #define uint unsigned int sbit SDA=P2^0; //定义SDA引脚 sbit SCL=P2^1; //定义SCL引脚 void delay_5us() //延时函数 { _nop_(); } void start() //I2C起始信号 { SDA=1; SCL=1; delay_5us(); SDA=0; delay_5us(); SCL=0; delay_5us(); } void stop() //I2C停止信号 { SDA=0; SCL=1; delay_5us(); SDA=1; delay_5us(); } void ack() //I2C应答信号 { SDA=0; SCL=1; delay_5us(); SCL=0; delay_5us(); } void no_ack() //I2C非应答信号 { SDA=1; SCL=1; delay_5us(); SCL=0; delay_5us(); } uchar read_byte() //I2C读取一个字节 { uchar i,data; SDA=1; for(i=0;i<8;i++) { SCL=1; delay_5us(); data<<=1; data|=SDA; SCL=0; delay_5us(); } return data; } void write_byte(uchar data) //I2C写入一个字节 { uchar i; for(i=0;i<8;i++) { SDA=data&0x80; SCL=1; delay_5us(); SCL=0; delay_5us(); data<<=1; } } void write_eeprom(uchar addr,uchar data) //写入EEPROM { start(); //起始信号 write_byte(0xa0); //写入器件地址,0xa0代表写入模式 ack(); //等待应答 write_byte(addr); //写入存储地址 ack(); //等待应答 write_byte(data); //写入数据 ack(); //等待应答 stop(); //停止信号 } uchar read_eeprom(uchar addr) //读取EEPROM { uchar data; start(); //起始信号 write_byte(0xa0); //写入器件地址,0xa0代表写入模式 ack(); //等待应答 write_byte(addr); //写入存储地址 ack(); //等待应答 start(); //起始信号 write_byte(0xa1); //写入器件地址,0xa1代表读取模式 ack(); //等待应答 data=read_byte(); //读取数据 no_ack(); //发送非应答信号 stop(); //停止信号 return data; } void main() { uchar addr=0x01; //定义存储地址 uchar data=0x55; //定义写入数据 uchar read_data; //定义读取数据 write_eeprom(addr,data); //写入EEPROM read_data=read_eeprom(addr); //读取EEPROM while(1); } ``` 以上程序通过I2C总线实现了对51单片机EEPROM进行读写操作。程序中的read_eeprom和write_eeprom函数分别对EEPROM进行读取和写入操作,其中的0xa0和0xa1是器件地址,表示写入和读取模式,具体的地址需要根据实际情况进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值