关于E2PROM
使用E2PROM可以保存数据,特点就是掉电不丢失。
在我们CT107D开发板上所使用的的器件是AT24C02,一个容量大小是2Kb/s,也就是256字节的E2PROM。
24C02是一个基于I2C通信协议的器件。
24C01/02/04/08/16 是低工作电压的1K/2K/4K/8K/16K 位串行电可擦除只读存储器,内部组织为128/256/512/1024/2048 个字节,每个字节8 位,该芯片被广泛应用于低电压及低功耗的工商业领域。
- 串行时钟信号引脚(SCL):在SCL 输入时钟信号的上升沿将数据送入EEPROM器件,并在时钟的下降沿将数据读出。
- 串行数据输入/输出引脚(SDA):SDA 引脚可实现双向串行数据传输。该引脚为开漏输出,可与其它多个开漏输出器件或开集电极器件线或连接。
- 器件/页地址脚(A2,A1,A0):A2、A1 和A0 引脚为24C01与24C02 的硬件连接的器件地址输入引脚。24C01在一个总线上最多可寻址八个1K 器件,24C02 在一个总线上最多可寻址八个2K 器件,A2、A1 和A0 内部必须连接。
- 24C04 仅使用A2、A1 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址四个4K 器件。A0 引脚内部未连接。
- 24C08 仅使用A2 作为硬件连接的器件地址输入引脚,在一个总线上最多可寻址两个8K 器件。A0和A1 引脚内部未连接。
- 24C16 未使用作为硬件连接的器件地址输入引脚,在一个总线上最多可连接一个16K器件。A0、A1 和A2 引脚内部未连接。
- 写保护(WP)引脚:24C01/02/04/08/16 具有用于硬件数据写保护功能的引脚。当该引脚接GND时,允许正常的读/写操作。当该引脚接VCC 时,芯片启动写保护功能。
E2PROM读写操作时序
1、E2PROM写数据流程
1.1、写字节(停止信号前ACK)
- 首先是I2C的起始信号,接着跟上首字节,也就是I2C的器件地址,并且在读写方向上选择“写”操作。
- 发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储在哪个位置,此刻写的就是哪个地址。
- 发送要存储的数据第一字节、第二字节、......注意在写数据的过程中,E2PROM每个字节都会回应一个“应答位0”,老告诉我们写E2PROM数据成功,如果没有回应答位,说明写入不成功。
在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1,当加到0xFF后,再写一个字节,地址就会溢出又变成0x00。
写数据的时候需要注意,E2PROM是先写到缓冲区,然后再“搬运到”到掉电非易失区。所以这个过程需要一定的时间,AT24C02这个过程是不超过5ms!如果在这个时候去让它应答是没有响应的!
所以,当我们在写多个字节时,写入一个字节之后,再写入下一个字节之前,必须等待E2PROM再次相应才可以。
2、E2PROM读数据流程
2.1、读当前地址(停止信号前NAK)
2.2、读随机地址(停止信号前NAK)
2.3、读连续地址(停止信号前NAK)
- 首先是I2C的起始信号,接着跟上首字节,也就是I2C的器件地址,并在在读写方向上选择“写”操作。注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。
- 发送要读取的数据的地址,注意是地址而非存在E2PROM中的数据,通知E2PROM要哪个分机的信息。
重新发送I2C的起始信号和器件地址,并且在方向位选择“读”操作。在这三步中,每一个字节实际上都是在“写”,所以每一个字节E2PROM都会回应一个“应答位0”。
读取从器件发回的数据,读一个字节,如果还想继续读写一个字节,就发送一个“应答位ACK(0)”,如果不想读了,告诉E2PROM不想要数据了,就发送一个“非应答位NAK(1)”。
和写操作一样,每读一个字节,地址就会自动加1,如果想继续往下读,给E2PROM一个ACK(0)低电平,那再继续给SCL完整的时序,E2PROM会继续往外送数据。如果不想读了,直接给一个NAK(1)高电平即可。
E2PROM程序
先说一下官方提供的iic.c里面的一个雷区。
根据datasheet,
I2C 通信分为低速模式 100kbit/s、快速模式 400kbit/s 和高速模式3.4Mbit/s。因为所有的 I 2 C 器件都支持低速,但却未必支持另外两种速度,所以作为通用的I 2 C 程序这里选择了 100k 这个速率来实现,也就是说实际程序产生的时序必须小于等于 100k的时序参数。由datasheet,很明显也就是要求 SCL 的高低电平持续时间都不短于 5us。
但是我们使用的是1T模式,_nop_()只占用一个机器周期,所以我们要写很多的_nop_(),没错就是这么多,一共33个。
#define somenop {_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_(); _nop_(); _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();_nop_();}
单字节写入
这个程序中我们虽然有去响应ACK,但是并未处理。因为我们仅仅是写一个字节,再次上电时间远远大于5ms,所以不必处理ACK。
在这里有几个要点:
- 在本例中单片机是主机,24C02 是从机;
- 无论是读是写,SCL 始终都是由主机控制的;
- 写的时候应答信号由从机给出,表示从机是否正确接收了数据;
- 读的时候应答信号则由主机给出,表示是否继续读下去。
/*******************************************************************************
* 函数名 :Write_E2PROM
* 输入值 :unsigned char add, unsigned char dat
* 返回值 :none
* 作者 :小默haa
* 时间 :2019年2月24日
* 功能描述:在E2PROM某个地址写入数据
* 备注 :add为E2PROM数据地址,dat为需要写入数据
*******************************************************************************/
void Write_E2PROM(unsigned char add, unsigned char dat)
{
IIC_Start();
IIC_SendByte(0xa0); //发送器件地址
IIC_WaitAck();
IIC_SendByte(add); //发送操作地址
IIC_WaitAck();
IIC_SendByte(dat); //写一字节
IIC_WaitAck();
IIC_Stop();
somenop;
}
单字节读取
/*******************************************************************************
* 函数名 :Read_E2PROM
* 输入值 :unsigned char add
* 返回值 :unsigend char d
* 作者 :小默haa
* 时间 :2019年2月24日
* 功能描述:读取E2PROM某个地址的数据
* 备注 :add为E2PROM数据地址,d为往外传送的数据
*******************************************************************************/
unsigned char Read_E2PROM(unsigned char add)
{
unsigned char d;
IIC_Start();
IIC_SendByte(0xa0); //发送器件地址
IIC_WaitAck();
IIC_SendByte(add); //发送要操作的地址
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1); //发送读操作
IIC_WaitAck();
d = IIC_RecByte(); //读一字节
IIC_Ack(0);
IIC_Stop();
return d;
}