1> 实验目的
实现51单片机对EEPROM - AT24C02,1字节的读写操作;
通过串口打印到串口终端;
小技巧:
理解I2C,他待会拉高,待会拉低,挺烦人,
可以换个角度思考下,为什么他这样设计;
2> AT24C02-灵魂4问?
2.1> 长啥样?
2.1.1> 实物图
2.1.2> 引脚图
2.2> 能干哈?
AT24C02是EEPROM存储器,可以形象的看作是个【本本】, 用于记录数据
或者想象成一个存储格子
型号含义:
AT24C02:
8Byte X 32 X 8bit = 2048bit;
每个字节的地址为 0x00~0xFF;
2.3> 咋干的?
2.3.1> 内部框图
2.3.2> AT24C02设备地址
高4位固定位“1010”,
A2~A0由外部电路配置,拉高为1,拉低为0;
从机只有一个器件时, 通常都拉低,所以为【1010 000X】;
2.3.3> AT24C02存储单元地址
AT24C02 存储单元地址范围:0x00 ~ 0xFF;共256字节;
2.4> 咋用他?
2.4.1> 硬件设计
2.4.2> 程序设计
单片机 通过I2C通信来读写,AT24C02
3> I2C 通信协议
IIC总线(Inter-Integrated Circuit)
传输方式:串行;
传输方向:半双工;
同步方式:同步;
速度:
标准模式:100Kbit/s;
快速模式:400Kbit/s;
高速模式:3.4Mbit/s;
3.1> 写1个字节
流程图:
时序图:
3.2> 读1个字节
流程图:
注意:读写控制为,写为“0”,读为1;
随机读1字节,时序图:
3.3> 起停信号
起始信号:SCL高电平期间,SDA产生下降沿,表示开始传输数据;
停止信号:SCL高电平期间,SDA产生上升沿,表示结束传输数据;
3.4> 主机写:数据信号
在每个SCL的上升沿,【主机】51单片机将SDA数据写入【从机】AT24C02;
所以在SCL的上升沿到来前,需要将数据在SDA上放好;
第8个SCL下降沿,【主机】51单片机要释放SDA,为AT24C02拉低SDA做准备;
3.5> 主机读:数据信号
【从机】AT24C02,在每个SCL的下降沿,向SDA写数据;
那么【主机】51单片机就可以在上SCL的上升沿和高电平期间读数据;
3.6> 应答信号
主机:SCL高电平期间,向SDA写0应答,写1非应答;
从机:SCL高电平期间,向SDA写0应答,写1非应答;
这样设计很合理, 把总线拉低,代表有响应,就是应答信号,
总线拉高,总线空闲,正好就是非应答,不回应了;
4> AT24C02时序参数(1000KHz)
写周期最大为5ms
5> 程序设计
5.1> 实现功能
实现对AT24C02,1字节的读写操作模块;
通过串口打印到串口终端;
5.2> 编程思路
参考其他人代码,总结;
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> 写时序
0x0F是为方便观察应答信号,写的值;
6.2> 应答信号细节
不用逻辑分析仪抓下波形,还就真不知道!有意思