大家好,欢迎来到IT知识分享网。
嵌入式开发–赛普拉斯cypress的铁电存储器FM25CL64B
简介
FM25CL64B是赛普拉斯cypress出品的一款铁电存储器,这种存储器最大的优势是可以像RAM一样随机存储,和按字节写入,也可以像ROM一样掉电仍然可以保存数据,是一种相当优秀的新型存储器,但是容量不能做得很大,只适合保存一些重要数据。
重要参数及解读如下:
64K位,即8K字节
100T的读写次数,这意味着即使对于同一单元,每毫秒读取或写入一次,也需要3170年才能消耗完这个次数,而我们对存储器的访问几乎不可能达到这样的频率,也不可能只访问一个数据单元,所以它的读写次数寿命虽然有限,但可以不在考虑之列。
151年的数据保存周期,除了极其特殊用途,基本上是足够的了,毕竟我们之前用的光盘和现在FLASH,数据保存周期也才10年而已。
SPI接口,频率支持到20M,可直接硬件替换SPI口的EEPROM,SPI支持MODO_0 (CPOL = 0, CPHA = 0)即时钟空闲时为低,第1个跳变沿采样数据;和MODO_3(CPOL = 1, CPHA = 1)即时钟空闲时为高,第2个跳变沿采样数据。看手册是自动识别,但我没有测试。
受保护以避免写入的区域为:1/4或1/2或全片保护可选
8脚的SOP或DFN封装
-40~85度温度
芯片框图
封装
内部工作框图:
硬件电路图
我接的是MCU的SP1口,如下:
CubeMX初始化设置
PA15设置为GPIO输出,速度设为High,由软件控制这个片选引脚。
Motorola格式,16位数据,MSB,CPOL和CPHA参照之前的说明,CRC校验关闭,软件片选。
软件片选的原因是ST的SPI接口中,NSS不是通常意义上的片选, 一旦开启SPI,这个引脚就只能为低,不能为高。而且,如果SPI总线上还有其他设备,也只能用软件片选。
需要注意的是Baud Rate这里,不能超过20M
芯片操作
控制码
控制码共有6个,如下图
分别对应的是
WREN 写使能
WRDI 写禁止
RDSR 读状态寄存器
WRSR 写状态寄存器
READ 读操作
WRITE 写操作
其他操作无意义或者说不开放,后面会讲到。
写使能
这是8位命令。
在写入数据之前需要先进行写使能操作,否则无法写入
同时,在SO数据线上,无数据响应,也就是说写使能是单向操作,芯片没有回应。
具体时序如下:
写禁止WRDI
这是8位命令。
执行本命令后,对芯片的写操作无效。
同样的,在执行本命令时,SO无回应。
8位操作码
对于前面2个8位操作码,由于我们在CubeMX中,将SPI设置为16位数据,那么无法完成对8位操作码,即1字节的操作。
例如:当发送WRDI(0x04)时,解决方案有3个:
1 发送重复的操作指令,发送0x0404来实现,即2次同样的操作。
2 发送0x0004,或0x0400,由于0x00不是操作码,芯片不会对其响应。
3 发送8位操作码时,修改SPI的寄存器,以实现8位操作。发送完成后再改为16位模式。
我用的是方案1,实测工作正常,就不再折腾寄存器了。
读状态寄存器
16位操作命令
前8位是操作码RDSR(0x05),后8位SI可以没有任何数据,有也不影响。
同时,在后8位时,SO会有数据输出,结束后在SPI的DR寄存器可以看到结果,也就是状态寄存器SR的数据。
如果此时SO上没有观察到波形,说明配置有错误。
写状态寄存器
16位操作命令,前8位是操作码0x01,后8位是需要写入状态寄存器的值。
SO线没有波形输出。
写保护
需要注意的是,向SR写入数据时,读取的不一定和写入相同。这与写保护的相关设置有关联,而且只有BIT1,2,3,7位是有效位,其他的位读出来永远是0。
具体请查看写保护的相关部分,我是向状态寄存器写0,然后读到的值应该是0x02,也就是说开启写操作,而且状态寄存器和存储空间均不保护,处于可写的状态。
写入完成后,将WEL位设置为0即可,如下图是相关的位,与保护区域的表格。
读数据和写数据
这两个操作类似,就放到一块说了。
这是32位操作码,
开始的8位,是操作码
接下来的8位,是地址码的高8位,由于本芯片是64K位,即8K字节,也就是说,地址码的有效位是3+8=11位,所以本区段 只有3个数据位是有效位。当然对于不同容量的芯片来说,有效位数是不一样的。
第3个8位,是地址码的低8位
最后8位,是数据位,写数据时,在SI线上有波形,读数据时,在SO上有波形。
HOLD引脚
HOLD引脚是用来暂停当前操作的,必须在时钟信号为低期间,HOLD引脚才能为低,
此时时钟,SI这两根线可以任意变化
需要退出HOLD状态前,时钟必须为低,SO可任意。
当HOLD拉高后,继续之前的操作。
个人感觉这个功能不太实用,只有当软件模拟SPI时才用得上。硬件SPI时,有折腾时序的时间,SPI的活都干完了
不用这个功能的话,本引脚接高即可。
WP引脚
硬件写保护,提供对状态寄存器的保护。
该引脚为高时,禁止对状态寄存器写入。可防止误操作。
不用这个功能的话,本引脚接高即可。
写入次数分析
以下是对同一个数据单元进行重复读写的寿命评估。
第1列是SPI时钟频率
第2列是每秒可以访问的次数
第3列是每年的访问次数
第4列是可以不间断写入多少年
可以看出,即使以本芯片所能支持的最高速度20MHz,来不间断的对同一数据单元进行读写访问,仍然可以维持85年,这个寿命是妥妥的够了。
更何况我们不可能总是读写同一个数据单元,而且不可能以如此高的频率进行数据访问。
所以芯片的寿命是可以放心的。
相关代码
LL_FRAM.h文件的代码
#ifndef __LL_FRAM_H__ #define __LL_FRAM_H__ #include "LL_define.h" #include "spi.h" //状态寄存器 X表示无意义,默认值是0 // BIT7 BIT6 BIT5 BIT4 BIT3 BIT2 BIT1 BIT0 // WPEN X(0) X(0) X(0) BP1 BP0 WEL X(0) // WEL: 1为写使能,0为禁止写 // BP0,BP1: 块保护 // WPEN: #define FRAM_WREN 0x06 //写入使能,即设置WEL位 在写操作(WRSR和WRITE)之前,必须先发本命令 #define FRAM_WRDI 0x04 //写入禁止,即清除WEL位 本操作会禁止写操作(WRSR和WRITE),此后即使发送(WRSR和WRITE)也无效 #define FRAM_RDSR 0x05 //读状态寄存器 #define FRAM_WRSR 0x01 //写状态寄存器,前序操作为FRAM_WREN #define FRAM_READ 0x03 //读 #define FRAM_WRITE 0x02 //写,前序操作为FRAM_WREN #define SPI1_NSS(n) (n?HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_SET):HAL_GPIO_WritePin(SPI1_NSS_GPIO_Port, SPI1_NSS_Pin, GPIO_PIN_RESET)) extern u8 spi_read_data; //spi读取的数值 u8 LL_fram_read_sr(void); u8 LL_fram_write_sr(u8 value); u8 LL_fram_read(u16 addr, u8 ret_data); u8 LL_fram_write(u16 addr, u8 value); #endif
LL_FRAM.c文件的代码
#include "LL_FRAM.h" u8 spi_read_data=0; u8 spi_tx_data[10] = {
0x06}; u8 LL_fram_read_sr(void) //读状态寄存器,其返回值是状态寄存器的内容 {
u8 data[4]; u8 ret; //写使能 data[0] = FRAM_WREN; data[1] = FRAM_WREN; SPI1_NSS(0); HAL_SPI_Transmit(&hspi1, data, 1, 100); SPI1_NSS(1); //读状态寄存器,必须先有写使能操作FRAM_WREN 0x05 data[0] = 0; data[1] = FRAM_RDSR; SPI1_NSS(0); HAL_SPI_TransmitReceive(&hspi1, data, &ret, 1, 10); SPI1_NSS(1); return ret; } u8 LL_fram_write_sr(u8 value) //写状态寄存器,其函数的参数是向状态寄存器写入的内容 {
u8 data[4]; u8 ret; //写使能 data[0] = FRAM_WREN; data[1] = FRAM_WREN; SPI1_NSS(0); HAL_SPI_Transmit(&hspi1, data, 1, 100); SPI1_NSS(1); //写状态寄存器 0x01 data[0] = value; data[1] = FRAM_WRSR; SPI1_NSS(0); HAL_SPI_TransmitReceive(&hspi1, data, &spi_read_data, 1, 10); SPI1_NSS(1); return 0; } //读操作 //addr:读取地址 //ret_data: 读取的数据保存在此处 u8 LL_fram_read(u16 addr, u8 ret_data) {
u8 data[4]; u8 ret; u8 state; //读存储器 0x02 data[0] = addr>>8; data[1] = FRAM_READ; data[2] = 0x00; data[3] = (u8)(addr&0x00ff); SPI1_NSS(0); state = HAL_SPI_TransmitReceive(&hspi1, data, &spi_read_data, 2, 20); //(&hspi1, data, &ret, 2, 10); SPI1_NSS(1); if(state == 0) {
spi_read_data = (u8)hspi1.Instance->DR; ret = 0; } else {
ret = state; } return ret; } //写操作 //addr:写入地址 //ret_data: 写入的数据 u8 LL_fram_write(u16 addr, u8 value) {
u8 data[4]; u8 ret; //写使能 data[0] = FRAM_WREN; data[1] = FRAM_WREN; SPI1_NSS(0); HAL_SPI_Transmit(&hspi1, data, 1, 100); SPI1_NSS(1); //写存储器 0x02 data[0] = addr>>8; data[1] = FRAM_WRITE; data[2] = value; data[3] = addr&0xff; SPI1_NSS(0); HAL_SPI_TransmitReceive(&hspi1, data, &ret, 2, 10); SPI1_NSS(1); return ret; }
测试读写
while(1) {
LL_fram_write_sr(0x000); //取消保护,以准备写入 if(LL_fram_read_sr() == 0x02) //读取状态寄存器,应该是0x02 {
for(i=0; i<8*1024; i++) {
LL_fram_write(i,(u8)i); j++; temp = LL_fram_read(i, spi_read_data); if(temp == 0) {
if(spi_read_data != (u8)i) {
//读出的数据与写入的数据不相等 LED_ERR(1); goto begin; } } else {
//LL_fram_read()函数返回值不是0,表示运行出错,DR寄存器内的数据不能保证正确性 LED_RUN(1); goto begin; j++; } } } } begin:
这一段函数不停的执行写入和读取操作,并进行比对。
如果SPI操作异常,则亮起LED_RUN灯,并跳转到begin处,
如果读取的结果,和写入的数据比对错误,则亮起LED_ERR灯,并跳转到begin处。
经长时间运行(截止发文,已6小时以上),发现正常运行时工作正常,未出现错误跳转。
带上仿真器全速运行时,会不时跳转到LED_RUN(1)处,也就是说SPI函数的运行不正确,显然是由于连接了仿真器造成的。但是不会跳转到LED_ERR(1)处,也就是说不会出现读取的数据不正确的情况。
总结
为了保证数据正确,应当判断LL_fram_read()函数的返回值,应当是0,此时再去读取数据才是正确的,否则会有错误的风险。
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/153879.html