外设驱动库开发笔记25:FM25xxx FRAM存储器驱动
在我們的項目中,時常會有參數(shù)或數(shù)據(jù)需要保存。鐵電存儲器的優(yōu)良性能和操作方便常常被我們選用。FM25xxx FRAM存儲器就是我們經(jīng)常使用到的一系列鐵電存儲器,這一篇我們將討論FM25xxx FRAM存儲器的驅(qū)動設計、實現(xiàn)及使用。
1、功能概述
FM25xxx FRAM存儲器是非易失性存儲器,采用了先進的鐵電存儲。鐵電隨機存取存儲器或F-RAM是非易失性的,其讀寫操作與RAM類似。它提供了151年的可靠數(shù)據(jù)保留,同時消除了由串行閃存、EEPROM和其他非易失性存儲器引起的復雜性、開銷和系統(tǒng)級可靠性問題。
1.1、硬件描述
FM25xxx系列FRAM存儲器擁有從4K到1M的各種容量。雖然不同型號的FM25xxx系列FRAM存儲器內(nèi)部存儲矩陣存在差異,但都采用了相同的封裝和引腳排布。具體的引腳分布如下圖:
FM25xxx FRAM存儲器的CS信號低電平有效,就是說CS信號處于低電平時,該設備被選中。當CS信號處于高電平時,設備進入低功耗待機模式,忽略其他輸入,并對輸出進行測試。當CS信號處于低電平時,設備內(nèi)部激活SCK信號。CS的下降沿必須在每個操作碼之前出現(xiàn)。
FM25xxx FRAM存儲器的WP引腳低電平有效。當WPEN設置為“1”時,WP信號低電平可以防止對狀態(tài)寄存器的寫操作。這很重要,因為其他寫保護特性是通過狀態(tài)寄存器控制的。如果不使用此引腳,則必須將其連接到VDD。在FM25040中,WP引腳可以阻止對部件的所有寫操作。
當主機CPU必須中斷另一個任務的內(nèi)存操作時,使用HOLD引腳。HOLD引腳處于低電平時,當前操作暫停。設備忽略SCK或CS上的任何轉(zhuǎn)換。所有等待的轉(zhuǎn)換必須在SCK低的時候發(fā)生。如果不使用此引腳,則必須將其連接到VDD。
FM25xxx FRAM存儲器與串行閃存和EEPROM不同,以總線速度執(zhí)行寫操作,不存在寫延遲。數(shù)據(jù)在每個字節(jié)成功傳輸?shù)皆O備后立即寫入內(nèi)存數(shù)組。下一個總線周期可在不需要進行數(shù)據(jù)輪詢的情況下開始。此外,與其他非易失性存儲器相比,FM25xxx FRAM存儲器具有較強的寫持久性。FM25xxx FRAM存儲器能夠支持1014個讀/寫周期,或比EEPROM多1億倍的寫周期。
這些功能使FM25xxx FRAM存儲器非常適合需要頻繁或快速寫入的非易失性內(nèi)存應用程序。從數(shù)據(jù)采集(寫入周期的數(shù)量可能至關重要)到要求工業(yè)控制(串行閃存或EEPROM的長寫入時間可能導致數(shù)據(jù)丟失)。FM25xxx FRAM存儲器采用高速SPI總線,提高了F-RAM技術(shù)的高速寫入能力。
1.2、通訊接口
FM25xxx FRAM存儲器采用SPI通訊接口。FM25xxx FRAM存儲器由主機控制器(通常稱為SPI主機)發(fā)送的一組指令控制。與FM25xxx FRAM存儲器的通信必須由SPI主設備(如微控制器)發(fā)起。SPI主設備必須在串行數(shù)據(jù)時鐘(SCK)引腳上為FM25xxx FRAM存儲器生成串行時鐘。FM25xxx FRAM存儲器總是作為一個從屬操作,因為SCK總是一個輸入。主機與FM25xxx FRAM存儲器通訊的拓撲圖如下所示:
FM25xxx FRAM存儲器是一個SPI從設備,運行速度高達20 MHz。這種高速串行總線為SPI主機提供高性能的串行通信。許多常見的微控制器有硬件SPI端口,允許直接接口。使用普通的端口引腳來模擬端口是非常簡單的,因為微控制器不需要。FM25xxx FRAM存儲器支持SPI模式0(0, 0)和SPI模式3(1, 1)。
1.3、操作模式
FM25xxx FRAM存儲器被設計成直接與同步串行外圍接口(SPI)接口。FM25xxx FRAM存儲器使用一個8位指令寄存器。所有的指令、地址和數(shù)據(jù)首先由高位開始傳送,然后由高到低依次進行。指令列表及其操作代碼如下:
從上表我們知道,除了操作存儲區(qū)域外,還可以操作狀態(tài)寄存器。FM25xxx FRAM存儲器包括一個8位狀態(tài)寄存器。狀態(tài)寄存器位調(diào)節(jié)設備的各種特性。這些位可以通過指令進行更改。具體的結(jié)構(gòu)如下:
狀態(tài)寄存器除了反應當前的狀態(tài)外,實際上有些位還有配置功能。關于致謝為的屬性及具體含義如下:
通過寫狀態(tài)寄存器(WRSR)指令可以配置寫保護使能和寫保護的區(qū)域。塊寫保護位(BP1、BP0)決定了存儲陣列的寫保護區(qū)域。兩位決定了陣列保護的四個級別,分別是:沒有一個內(nèi)存陣列被保護;上四分之一地址范圍內(nèi)存陣列被保護;上半部分地址范圍內(nèi)存陣列被保護;所有的內(nèi)存陣列都是寫保護的,這意味著所有的地址位都是只讀的。塊寫保護級別和相應的狀態(tài)寄存器控制位關系如下:
而寫保護使能 (WPEN)位用于啟用或禁用寫保護 (WP) 引腳。當WPEN位設置為邏輯“0”時,寫入EEPROM數(shù)組的能力取決于塊寫保護(BP1、BP0)位的值。寫入狀態(tài)寄存器的權(quán)限是由WEL位控制的。當WPEN位設置為邏輯“1”時,狀態(tài)寄存器是只讀的。當WP引腳低且WPEN位設置為邏輯“1”時,硬件寫保護就啟用了。當設備被硬件寫保護時,對狀態(tài)寄存器的寫操作,包括塊寫保護、WEL和WPEN位,以及對塊寫保護位所選擇的內(nèi)存陣列中的段的寫操作被禁用。當啟用硬件寫保護時,只允許對未受塊保護的內(nèi)存段進行寫。當WP引腳為高電平或WPEN位邏輯為“0”時,硬件寫保護被禁用。當硬件寫保護被禁用時,只允許對未被塊保護的內(nèi)存段進行寫。當WPEN位被硬件寫保護時,只要WP引腳保持低,它就不能被設置回邏輯“0”。寫保護的關系如下所示:
FM25xxx FRAM存儲器擁有從4K到1M的不同容量,尋址范圍的不同所需的地址為數(shù)據(jù)不相同。地址位數(shù)根據(jù)容量從8位到17位不等,分別對應1到3個字節(jié)。具體的容量與地址位關系如下:
需要注意的是,4K(512x8)容量的FM25xxx FRAM存儲器需要9為地址,但在實際操作時只用了1個字節(jié)來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
2、驅(qū)動設計與實現(xiàn)
我們已經(jīng)了解了FM25xxx FRAM存儲器的通訊接口、存儲格式等,再次基礎上我們將設計并實現(xiàn)FM25xxx FRAM存儲器的驅(qū)動程序。
2.1、對象的定義
在使用一個對象之前我們需要獲得一個對象。同樣的我們想要FM25xxx FRAM存儲器就需要先定義FM25xxx FRAM存儲器的對象。
2.1.1、對象的抽象
我們要得到FM25xxx FRAM存儲器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下FM25xxx FRAM存儲器的對象。
先來考慮屬性,作為屬性肯定是用于標識或記錄對象特征的東西。我們來考慮FM25xxx FRAM存儲器對象屬性。首先每一個FM25xxx FRAM存儲器都有一個狀態(tài)寄存器,該狀態(tài)寄存器指示了寫保護的控制和狀態(tài)信息,所以我們將其作為對象的屬性以標識FM25xxx FRAM存儲器的狀態(tài)。因為不同型號的FM25xxx FRAM存儲器擁有不同的存儲容量及尋址范圍,為了區(qū)分不同設備和地址寬度我們將設備類型及數(shù)據(jù)地址的長度均作為對象的屬性。
接著我們還需要考慮FM25xxx FRAM存儲器對象的操作問題。我們要對FM25xxx FRAM存儲器進行讀寫,但讀寫都需要同過具體的SPI接口進行,這依賴于具體的硬件平臺,所以我們將針對SPI端口的讀寫作為對象的操作。FM25xxx FRAM存儲器還有一個寫保護引腳用于設置內(nèi)部存儲器的寫保護問題,有一個片選信號應交用于選中操作設備,有一個Hold引腳用于操作控制,這些引腳的信號改變同樣依賴于硬件平臺來實現(xiàn),所以我們也將它們作為對象的操作。在進行相關操作時,我們需要控制時序,則需要使用延時操作,但延時處理總是依賴于具體的軟硬件平臺,所以我們將延時處理作為對象的操作。
根據(jù)上述我們對FM25xxx FRAM存儲器的分析,我們可以定義FM25xxx FRAM存儲器的對象類型如下:
/* 定義FM25C對象類型 */ typedef struct FM25Object {uint8_t status;????????????????????????? //狀態(tài)寄存器FM25ModeType mode;?????????? //設備類型FM25MemAddLengthType memAddLength;?????????? //寄存器地址長度void (*Read)(uint8_t *rData,uint16_t rSize);?????? //讀數(shù)據(jù)操作指針void (*Write)(uint8_t *wData,uint16_t wSize);??? //寫數(shù)據(jù)操作指針void (*WP)(FM25WPType wp);??? //寫保護操作void (*ChipSelect)(FM25CSType cs);???? //片選信號void (*Hold)(FM25HoldType hold);????????????????? //保持信號void (*Delayms)(volatile uint32_t nTime);?????? //延時操作指針 }FM25ObjectType;2.1.2、對象初始化
我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮FM25xxx FRAM存儲器對象的初始化函數(shù)。一般來說,初始化函數(shù)需要處理幾個方面的問題。一是檢查輸入?yún)?shù)是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據(jù)此我們設計FM25xxx FRAM存儲器對象的初始化函數(shù)如下:
/*FM25對象初始化*/ void Fm25cxxInitialization(FM25ObjectType *fram,???? //FM25xxx對象實體FM25ModeType mode,??? //設備類型Fm25Read read,??????????????? //讀FM25xxx對象操作指針Fm25Write write,???????????? //寫FM25xxx對象操作指針Fm25Delayms delayms,?? //延時操作指針Fm25WP wp,??????????????????? //寫保護操作函數(shù)指針Fm25ChipSelect cs,????????? //片選信號函數(shù)指針Fm25Hold hold???????????????? //保持信號操作函數(shù)指針) {if((fram==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL)){return;}fram->Read=read;fram->Write=write;fram->Delayms=delayms;if(cs!=NULL){fram->ChipSelect=cs;}else{fram->ChipSelect=FM25ChipSelectDefault;}if(mode>=FM25Number){return;}fram->mode=mode;if(mode<FM25C160B){fram->memAddLength=FM258BitMemAdd;}else if(mode<FM25V10){fram->memAddLength=FM2516BitMemAdd;}else{fram->memAddLength=FM2524BitMemAdd;}ReadStatusForFM25xxx(fram);//寫允許SetWriteEnableLatchForFM25xxx(fram);uint8_t cmd;//使能寫保護,保護全部區(qū)域cmd=fram->status|FM25_WPEN|FM25_BPALL;WriteStatusForFM25xx(fram,cmd);ReadStatusForFM25xxx(fram); }2.2、寫使能操作
我們已經(jīng)完成了FM25xxx FRAM存儲器對象類型的定義和對象初始化函數(shù)的設計。但我們的主要目標是獲取對象的信息,接下來我們還要實現(xiàn)面向FM25xxx FRAM存儲器的各類操作。
2.2.1、設置寫使能鎖存器
FM25xxx FRAM存儲器在啟動后,寫操作是被禁用的。發(fā)送WREN操作碼允許用戶為寫操作發(fā)出后續(xù)操作碼。這包括寫入狀態(tài)寄存器(WRSR)和寫入內(nèi)存(WRITE)。發(fā)送WREN操作碼會導致設置內(nèi)部寫啟用鎖存器。狀態(tài)寄存器中的標志位WEL表示鎖存器的狀態(tài)。WEL =“1”表示允許寫操作。經(jīng)過WRDI、WRSR或?qū)懖僮骱?#xff0c;WEL位將自動清除,這可以防止在沒有其他WREN命令的情況下進一步寫入狀態(tài)寄存器或F-RAM存儲陣列。WREN命令總線時序如下所示:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器設置寫使能鎖存器的程序如下:
/* 設置寫使能所存器*/ void SetWriteEnableLatchForFM25xxx(FM25ObjectType *fram) {uint8_t opCode=FM25_WREN;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->ChipSelect(FM25CS_Enable);ReadStatusForFM25xxx(fram); }2.2.2、復位寫使能鎖存器
WRDI命令通過清除寫使能鎖存器禁用所有寫活動。用戶可以通過讀取狀態(tài)寄存器中的WEL位并驗證WEL是否等于“ 0”來確認寫操作是否被禁用。WRDI命令總線時序如下所示:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器復位寫使能鎖存器的程序如下:
/* 復位寫使能所存器*/ void ResetWriteEnableLatchForFM25xxx(FM25ObjectType *fram) {uint8_t opCode=FM25_WRDI;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->ChipSelect(FM25CS_Enable);ReadStatusForFM25xxx(fram); }2.3、操作狀態(tài)寄存器
FM25Cxx系列存儲器的狀態(tài)寄存器不只是用來指示狀態(tài),還用于配置寫保護。寫狀態(tài)寄存器受到WEL、WPEN、WP三重寫保護,具體可見前面的寫保護限制圖。
2.3.1、讀狀態(tài)寄存器
RDSR命令可以通過總線獲取狀態(tài)寄存器的內(nèi)容。讀取狀態(tài)寄存器提供有關寫保護特性的當前狀態(tài)的信息。按照RDSR操作碼,FM25Cxx系列存儲器將返回一個包含狀態(tài)寄存器內(nèi)容的字節(jié)。RDSR命令的總線時序如下:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器讀狀態(tài)寄存器的程序如下:
/*讀FM25xxx狀態(tài)寄存器*/ void ReadStatusForFM25xxx(FM25ObjectType *fram) {uint8_t opCode=FM25_RDSR;uint8_t status;fram->ChipSelect(FM25CS_Enable);fram->Write(&opCode,1);fram->Delayms(1);fram->Read(&status,1);fram->ChipSelect(FM25CS_Enable);fram->status=status; }2.3.2、寫狀態(tài)寄存器
WRSR命令允許SPI總線主寫入狀態(tài)寄存器,并根據(jù)需要設置WPEN、BP0和BP1位,從而更改寫保護配置。在發(fā)出WRSR命令之前,WP引腳必須處于高電平或非活動狀態(tài)。在發(fā)送WRSR命令之前,用戶必須發(fā)送一個WREN命令來啟用寫操作。執(zhí)行WRSR命令是一個寫操作,因此清除寫啟用鎖存器。WRSR命令的總線時序如下:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器寫狀態(tài)寄存器的程序如下:
/*寫FM25xxx狀態(tài)寄存器*/ void WriteStatusForFM25xx(FM25ObjectType *fram,uint8_t cmd) {uint8_t data[2];data[0]=FM25_WRSR;data[1]=cmd;if(((fram->status)&0x02)!=0x02){SetWriteEnableLatchForFM25xxx(fram);}if((((fram->status)&FM25_WPEN)!=FM25_WPEN)&&(fram->WP!=NULL)){fram->WP(FM25WP_Disable);}fram->ChipSelect(FM25CS_Enable);fram->Write(data,2);fram->ChipSelect(FM25CS_Disable);ReadStatusForFM25xxx(fram);if(fram->WP!=NULL){fram->WP(FM25WP_Enable);} }狀態(tài)寄存器中的寫保護啟用位(WPEN)控制硬件寫保護(WP)引腳的效果。當WPEN位設置為“0”時,WP引腳的狀態(tài)將被忽略。當WPEN位設置為“1”時,WP引腳上的一個低電平信號會阻止對狀態(tài)寄存器的寫入。因此,只有當WPEN =“1”和WP =“0”時才寫保護狀態(tài)寄存器。
2.4、操作存儲數(shù)據(jù)
FM25xxx FRAM存儲器的SPI接口具有很高的時鐘頻率,突出了F-RAM技術(shù)的快速寫入能力。與串行閃存和EEPROM不同,FM25xxx FRAM存儲器可以以總線速度執(zhí)行順序?qū)懭?#xff0c;可以執(zhí)行任意數(shù)量的順序?qū)懖僮鳌?/span>
2.4.1、寫數(shù)據(jù)操作
FM25xxx FRAM存儲器所有對內(nèi)存的寫入都以WREN操作碼開始。寫入操作碼之后是一個存儲地址,不同容量因?qū)ぶ贩秶煌刂返臑閿?shù)也不相同。后續(xù)字節(jié)是按順序?qū)懭氲臄?shù)據(jù)字節(jié)。只要總線主機繼續(xù)發(fā)出時鐘并將CS信號保持在低電平,地址就會在內(nèi)部遞增。如果到達最后一個地址,計數(shù)器將滾動到0x0000。寫數(shù)據(jù)操作命令的總線時序如下:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器寫數(shù)據(jù)存儲器的程序如下:
/*向FM25xxx寫入數(shù)據(jù)*/ void WriteBytesToFM25xxx(FM25ObjectType *fram,uint32_t regAddress,uint8_t *wData,uint16_t wSize) {uint8_t data[wSize+4];uint8_t temp;uint16_t index=0;data[index++]=FM25_WRITE;if(fram->memAddLength==FM258BitMemAdd){data[index++]=(uint8_t)regAddress;if((fram->mode==FM25L04B)||(fram->mode==FM25040B)){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(fram->memAddLength==FM2516BitMemAdd){data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}else{data[index++]=(uint8_t)(regAddress>>16);data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}for(int i;i<wSize;i++){data[index++]=wData[i];}if(((fram->status)&0x02)!=0x02){SetWriteEnableLatchForFM25xxx(fram);}if(((fram->status)&0x0C)!=0x00){WriteStatusForFM25xx(fram,fram->status|FM25_BPNONE);}fram->ChipSelect(FM25CS_Enable);fram->Write(data,index);fram->ChipSelect(FM25CS_Disable);WriteStatusForFM25xx(fram,fram->status|FM25_BPALL); }2.4.2、讀數(shù)據(jù)操作
在FM25xxx FRAM存儲器的CS信號為低電平時,總線控制器可以發(fā)出一個讀操作碼。READ命令后面是一個存儲地址,包含READ操作的第一個字節(jié)的地址。當操作碼和地址發(fā)出后,設備將在接下來的8個時鐘上讀出數(shù)據(jù)。在讀取數(shù)據(jù)字節(jié)期間忽略信息輸入。后續(xù)字節(jié)是按順序讀出的數(shù)據(jù)字節(jié),只要總線時鐘存在且CS信號為低電平,地址就會在內(nèi)部遞增。如果到達最后一個地址,計數(shù)器將滾動到0x0000。讀數(shù)據(jù)操作命令的總線時序如下:
根據(jù)上述時序圖,我們可以設計FM25xxx FRAM存儲器讀數(shù)據(jù)存儲器的程序如下:
/*從FM25xxx讀取數(shù)據(jù)*/ void ReadBytesFromFM25xxx(FM25ObjectType *fram,uint32_t regAddress,uint8_t *rData,uint16_t rSize) {uint8_t data[4];uint16_t index=0;uint8_t temp;uint16_t size=0;data[index++]=FM25_READ;if(fram->memAddLength==FM258BitMemAdd){data[index++]=(uint8_t)regAddress;if((fram->mode==FM25L04B)||(fram->mode==FM25040B)){temp=(uint8_t)(regAddress>>8);data[0]|=((temp&0x01)<<3);}}else if(fram->memAddLength==FM2516BitMemAdd){data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}else{data[index++]=(uint8_t)(regAddress>>16);data[index++]=(uint8_t)(regAddress>>8);data[index++]=(uint8_t)regAddress;}fram->ChipSelect(FM25CS_Enable);fram->Write(data,index);fram->Delayms(1);fram->Read(rData,rSize);fram->ChipSelect(FM25CS_Disable); }3、驅(qū)動的使用
我們設計了FM25xxx FRAM存儲器的驅(qū)動程序,這個驅(qū)動程序是否能夠按我們的期望有效工作呢?我們需要驗證一下,下面我們將設計一個驗證驅(qū)動的簡單應用。
3.1、聲明并初始化對象
使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的FM25xxx FRAM存儲器對象類型聲明一個FM25xxx FRAM存儲器對象變量,具體操作格式如下:
FM25ObjectType fm25;
聲明了這個對象變量并不能立即使用,我們還需要使用驅(qū)動中定義的初始化函數(shù)對這個變量進行初始化。這個初始化函數(shù)所需要的輸入?yún)?shù)如下:
FM25ObjectType *fram,FM25xxx對象實體
FM25ModeType mode,設備類型
Fm25Read read,讀FM25xxx對象操作指針
Fm25Write write,寫FM25xxx對象操作指針
Fm25Delayms delayms,延時操作指針
Fm25WP wp,寫保護操作函數(shù)指針
Fm25ChipSelect cs,片選信號函數(shù)指針
Fm25Hold hold,保持信號操作函數(shù)指針
對于這些參數(shù),對象變量我們已經(jīng)定義了。而設備類型為枚舉,根據(jù)實際使用設備情況選擇就好了。主要的是我們需要定義幾個函數(shù),并將函數(shù)指針作為參數(shù)。這幾個函數(shù)的類型如下:
/* 定義讀數(shù)據(jù)操作函數(shù)指針類型 */ typedef void (*Fm25Read)(uint8_t *rData,uint16_t rSize);/* 定義寫數(shù)據(jù)操作函數(shù)指針類型 */ typedef void (*Fm25Write)(uint8_t *wData,uint16_t wSize);/* 定義延時操作函數(shù)指針類型 */ typedef void (*Fm25Delayms)(volatile uint32_t nTime);/* 定義寫保護操作函數(shù)指針類型 */ typedef void (*Fm25WP)(FM25WPType wp);/* 定義片選操作函數(shù)指針類型 */ typedef void (*Fm25ChipSelect)(FM25CSType cs);/* 定義保持操作函數(shù)指針類型 */ typedef void (*Fm25Hold)(FM25HoldType hold);對于這幾個函數(shù)我們根據(jù)樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。片選操作函數(shù)用于多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數(shù)定義如下:
/*讀FM25寄存器值*/ static void ReadDataFromFM25(uint8_t *rData,uint16_t rSize) {HAL_SPI_Receive (&fm25hspi,rData,rSize,1000); }/*寫FM25寄存器值*/ static void WriteDataToFM25(uint8_t *wData,uint16_t wSize) {HAL_SPI_Transmit (&fm25hspi,wData,wSize,1000); }/*片選操作*/ void ChipSelectForFM25(FM25CSType cs) {if(cs==FM25CS_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);} }/*寫保護操作*/ void WriteProtectedForFM25(FM25WPType wp) {if(wp==FM25WP_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_5,GPIO_PIN_SET);} }/*保持信號操作*/ void HoldForFM25(FM25HoldType hold) {if(hold==FM25Hold_Enable){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOA,GPIO_PIN_6,GPIO_PIN_SET);} }對于延時函數(shù)我們可以采用各種方法實現(xiàn)。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數(shù)。于是我們可以調(diào)用初始化函數(shù)如下:
Fm25cxxInitialization(&fm25,????? //FM25xxx對象實體FM25V10,?????????? //設備類型ReadDataFromFM25,????????????? //讀FM25xxx對象操作指針WriteDataToFM25,???????????????? //寫FM25xxx對象操作指針HAL_Delay, //延時操作指針WriteProtectedForFM25,??????? //寫保護操作函數(shù)指針ChipSelectForFM25,??????? //片選信號函數(shù)指針HoldForFM25?????????????????? //保持信號操作函數(shù)指針);3.2、基于對象進行操作
我們定義了對象變量并使用初始化函數(shù)給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數(shù)據(jù)。我們在驅(qū)動中已經(jīng)將獲取數(shù)據(jù)并轉(zhuǎn)換為轉(zhuǎn)換值的比例值,接下來我們使用這一驅(qū)動開發(fā)我們的應用實例。
/*FM25XXX數(shù)據(jù)讀寫操作*/ void FM25ReadWriteData(void) {uint16_t regAddress=0x02;uint8_t readByte;uint8_t writeByte=0x0A;uint8_t rData[2];uint16_t rSize=2;uint8_t wData[]={0x5A,0xA5};uint16_t wSize=2;/*從FM25XXX讀取單個字節(jié),從隨機地址讀取*/readByte=ReadByteFromFM25xxx(&fm25,regAddress);/*向FM25XXX寫入單個字節(jié)*/WriteByteToFM25xxx(&fm25,regAddress,writeByte);/*從FM25XXX讀取多個字節(jié),從指定地址最多到所在頁的結(jié)尾*/ReadBytesFromFM25xxx(&fm25,regAddress,rData,rSize);/*向FM25XXX寫入多個字節(jié),從指定地址最多到所在頁的結(jié)尾*/WriteBytesToFM25xxx(&fm25,regAddress,wData,wSize); }4、應用總結(jié)
在這一篇中,我們實現(xiàn)了FM25xxx FRAM存儲器的驅(qū)動程序,并在次驅(qū)動程序的基礎上設計了簡單的驗證應用。無論是寫數(shù)據(jù)還是讀數(shù)據(jù)均可順利執(zhí)行,說明我們的驅(qū)動設計是正確的。
FM25xxx FRAM存儲器與其他非易失性內(nèi)存技術(shù)不同,F-RAM沒有有效的寫延遲。由于底層內(nèi)存的讀和寫訪問時間相同,因此用戶不會在總線上體驗延遲。整個內(nèi)存周期的時間比一個總線時鐘還短。因此,任何操作(包括讀或?qū)?#xff09;都可以在寫之后立即執(zhí)行。
需要注意的是,4K(512x8)容量的FM25xxx FRAM存儲器需要9為地址,但在實際操作時只用了1個字節(jié)來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
在使用驅(qū)動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現(xiàn)的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數(shù)。
源碼下載:https://github.com/foxclever/ExPeriphDriver
歡迎關注:
?
總結(jié)
以上是生活随笔為你收集整理的外设驱动库开发笔记25:FM25xxx FRAM存储器驱动的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 外设驱动库开发笔记35:迪文触摸屏驱动
- 下一篇: python实现视频关键帧提取(基于帧间