嵌入式開發–賽普拉斯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 0x05data[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);//寫狀態寄存器 0x01data[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;//讀存儲器 0x02data[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);//寫存儲器 0x02data[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,此時再去讀取數據才是正確的,否則會有錯誤的風險。