24系列EEPROM/FRAM通用接口
1.写在前面
“24系列”的EEPROM,一般地我们认为是以i2c为通信接口的一系列串行EEPROM,各大半导体厂商出产的该系列EEPROM都遵循这个规则,而且电路和控制程序上也兼容。如AT24C02、ST24C02等。
FRAM(铁电存储器)是近几年来比较热门的一项存储技术,并且有相关实际产品的应用。FRAM具有ROM和RAM的特点,擦写寿命长、读写速度快、读写功耗极低。24系列的FRAM在电路和程序员兼容24系列的EEPROM,但由于FRAM写速度远高于EEPROM,因此不再需要传统EEPROM“页写”之间的延时或者总线空闲检查的操作,这也是两者程序的主要差别。当然存在延时也可以,并不影响数据的正确性,只是降低写效率。
除此之外,还有“25系列”的EEPROM,该系列则是以spi串行控制接口,在读写时序上稍有差别,具体页写算法可以和24系列的一致。相应地,也有25系列的FRAM,这块用得少,具体差别还有待验证。说个题外话,个人觉得spi接口的EEPROM并无太大优势,甚至有点浪费资源。虽然spi传输速度可以高于i2c,但EEPROM的写速度实在太低了,像i2c这样的低速总线仍需“等待”,所以使用spi并无优势,还占用多个IO口。但,spi接口的FRAM就有优势了,FRAM读写速度远高于EEPROM,存储数据量较大时,可以优先考虑spi接口的FRAM。
2.接口抽象
2.1 目的
统一接口,屏蔽底层差异,方便移植。使用时只需设置好具体EEPROM型号、从地址、页写延时函数、写保护函数等即可驱动起来,让用户将更多时间花在业务功能上。
2.2 使用范围
1)MCU裸机程序上。
2)RTOS(实时系统)上。
3)其他。
2.3 实现功能
1)根据型号自动识别页大小、片容量大小。
2)带页写算法,自动翻页,自动识别非整齐页。
3)兼容EEPROM和FRAM。
2.4 API
2.4.1 设备对象
在介绍API前,有个关键的结构体“_24cxx_dev_t”,在“_24cxx_dev.h”中,每个EEPROM外设必须对应一个该结构体,一般地我们参考Linux的叫法称为“设备对象”或者“文件句柄”。该结构体需要程序员初始化,指定相关参数或者函数指针实例化。后面的读、写、擦除函数接口的第一个参数必须都是传入该对象地址(指针),如果对象为空或者未初始化,则返回错误。
/*24cxx eeprom devcie struct*/
typedef struct
{
struct i2c_dev_device *i2c_dev; /*i2c bus device struct*/
uint8_t slave_addr; /*eeprom i2c addr*/
_24_model_t model; /*eeprom model*/
void(*wp)(uint8_t ctrl); /*protect of write function*/
void(*page_write_delay)(void); /*there is a delay in continuous writin for EEPROM,FRAM not need*/
}_24cxx_dev_t;
1)参数一“i2c_dev”,为控制EEPROM的i2c总线设备,可参考“i2c抽象/模拟i2c”文章,使用的是模拟i2c ,也可以根据定义使用硬件i2c;
2)参数二“slave_addr”,为EEPROM的器件从地址,7bit地址,不包括读写位;
3)参数三“model”,为具体EEPROM的型号,该类型为自定义的枚举型,枚举了24c01—24c1024的型号的EEPROM/FRAM;
4)参数四“wp”,为写保护函数,如果EEPROM的写保护引脚“WP”通过MCU控制,则需实现该函数;实现“WP”引脚IO翻转即可;
5)参数五“page_write_delay”,为页写延时函数,程序中采用了页写算法,EEPROM如果连续页写,则需要实现该函数, 否则写会出错;FRAM则不需要。
2.4.2 读EEPROM
int16_t _24cxx_read(_24cxx_dev_t *pdev,uint32_t addr, uint8_t *pbuf, uint32_t size);
参数 | 功能描述 |
---|---|
pdev | EEPROM/FRAM设备对象 |
addr | 起始地址 |
pbuf | 读取的数据(缓存) |
size | 读取数据大小(字节) |
_24cxx_write(_24cxx_dev_t *pdev,uint32_t addr,uint8_t *pbuf,uint32_t size);
参数 | 功能描述 |
---|---|
pdev | EEPROM/FRAM设备对象 |
addr | 起始地址 |
pbuf | 待写的数据 |
size | 待写数据大小(字节) |
原则上EEPROM并不存在“擦除”操作,“擦除”一般指用于flash写入前的操作。但特殊场合下,需赋予EEPROM默认值,如赋值0或者0xff,此时可以使用擦除操作。为了节约内存,该函数内部使用的是单字节擦除,效率不高,如有需擦除大片空间,建议直接调用写函数进行擦除。
int16_t _24cxx_erase(_24cxx_dev_t *pdev,uint32_t addr,uint8_t data, uint32_t size);
参数 | 功能描述 |
---|---|
pdev | EEPROM/FRAM设备对象 |
addr | 起始地址 |
data | 擦除为默认值 |
size | 待擦除空间大小(字节) |
调用函数时,都会返回一个状态码,无错误返回0。具体状态码可以查看“_24cxx_dev.h”中的定义。
/*operation state*/
enum _24CXX_ERROR
{
_24CXX_OK = 0,
_24CXX_ERR_I2C_WR = 1,
_24CXX_ERR_DEV_NONE = 2,
_24CXX_ERR_PAGE_WRITE = 3,
_24CXX_ERR_PAGE_SIZE = 4,
_24CXX_ERR_CHIP_SIZE = 5,
};
3.使用例子
目前测试并且通过的EEPROM/FRAM型号有AT24C02、AT24C16、FM24C16。其他型号暂未测试过,如广大网友测试发现问题欢迎指出。
3.1 EEPROM系列
在测试工程下的“drivers”目录下,源码文件是“at24cxx.c”,主要初始化实现一个“_24cxx_dev_t”设备对象。
static void page_write_delay(void)
{
uint16_t i;
i = 0xFFFF;
while(i--);
}
/**
* @brief device init
*/
const _24cxx_dev_t at24cxx_dev =
{
&i2c1_dev, /*i2c device*/
0x50, /*eeprom address*/
_24C16_E, /*eeprom model,eg AT24C16*/
0, /*no write protect*/
page_write_delay, /*delay function,must!*/
};
1)“i2c1_dev”为i2c总线设备对象,在“i2c抽象/模拟i2c”文章提及,不再赘述;
2)0x50为器件从地址,不包括读写位;
3)器件具体型号;
4)写保护函数,这里无,赋值NULL(0);
5)页写延时函数。
这一步完成,即可通过上述的API接口读、写EEPROM,具体demo查看工程源码。
3.1.1 写保护函数
常见的使用方式都是直接将EEPROM的WP引脚拉低,没有保护功能。如果WP引脚通过MCU控制,则需要使用上写保护函数。以为STM32为例,假设PA0控制器件的WP引脚,写保护函数实现如下,IO口时钟、方式、状态等寄存器需在初始化时配置。在初始化设备对象时,赋值到写保护函数指针。
static void wp_fun(uint8_t ctrl)
{
if(ctrl == 0)
{
GPIO_ResetBits(GPIOA,GPIO_Pin_0);
}
else
{
GPIO_SetBits(GPIOA,GPIO_Pin_0);
}
}
3.2 FRAM系列
FRAM控制接口兼容传统EEPROM,程序上差异不大,主要是页写函数可以去掉,设置为NULL(0)即可。以FM24CL16为例,实现如下。
/**
* @brief device init
*/
const _24cxx_dev_t fm24clxx_dev =
{
&i2c1_dev, /*i2c device*/
0x50, /*fram address*/
_24C16_E, /*eeprom model,eg FM24C16*/
0, /*no write protect*/
0, /*delay fun,FRAM no need*/
};
4.相关文章
[1] i2c抽象/模拟i2c:
https://blog.csdn.net/qq_20553613/article/details/78878211
[2] EEPROM页写算法:
https://blog.csdn.net/qq_20553613/article/details/78550427
5.源码