在我們的項目中,時常會有參數(shù)或數(shù)據(jù)需要保存。鐵電存儲器的優(yōu)良性能和操作方便常常被我們選用。FM25xxx FRAM存儲器就是我們經(jīng)常使用到的一系列鐵電存儲器,這一篇我們將討論FM25xxx FRAM存儲器的驅(qū)動設(shè)計、實現(xiàn)及使用。
1 、功能概述
FM25xxx FRAM存儲器是非易失性存儲器,采用了先進(jìn)的鐵電存儲。鐵電隨機存取存儲器或F-RAM是非易失性的,其讀寫操作與RAM類似。它提供了151年的可靠數(shù)據(jù)保留,同時消除了由串行閃存、EEPROM和其他非易失性存儲器引起的復(fù)雜性、開銷和系統(tǒng)級可靠性問題。
1.1 、硬件描述
FM25xxx系列FRAM存儲器擁有從4K到1M的各種容量。雖然不同型號的FM25xxx系列FRAM存儲器內(nèi)部存儲矩陣存在差異,但都采用了相同的封裝和引腳排布。具體的引腳分布如下圖:
FM25xxx FRAM存儲器的CS信號低電平有效,就是說CS信號處于低電平時,該設(shè)備被選中。當(dāng)CS信號處于高電平時,設(shè)備進(jìn)入低功耗待機模式,忽略其他輸入,并對輸出進(jìn)行測試。當(dāng)CS信號處于低電平時,設(shè)備內(nèi)部激活SCK信號。CS的下降沿必須在每個操作碼之前出現(xiàn)。
FM25xxx FRAM存儲器的WP引腳低電平有效。當(dāng)WPEN設(shè)置為“1”時,WP信號低電平可以防止對狀態(tài)寄存器的寫操作。這很重要,因為其他寫保護(hù)特性是通過狀態(tài)寄存器控制的。如果不使用此引腳,則必須將其連接到VDD。在FM25040中,WP引腳可以阻止對部件的所有寫操作。
當(dāng)主機CPU必須中斷另一個任務(wù)的內(nèi)存操作時,使用HOLD引腳。HOLD引腳處于低電平時,當(dāng)前操作暫停。設(shè)備忽略SCK或CS上的任何轉(zhuǎn)換。所有等待的轉(zhuǎn)換必須在SCK低的時候發(fā)生。如果不使用此引腳,則必須將其連接到VDD。
FM25xxx FRAM存儲器與串行閃存和EEPROM不同,以總線速度執(zhí)行寫操作,不存在寫延遲。數(shù)據(jù)在每個字節(jié)成功傳輸?shù)皆O(shè)備后立即寫入內(nèi)存數(shù)組。下一個總線周期可在不需要進(jìn)行數(shù)據(jù)輪詢的情況下開始。此外,與其他非易失性存儲器相比,F(xiàn)M25xxx FRAM存儲器具有較強的寫持久性。FM25xxx FRAM存儲器能夠支持1014個讀/寫周期,或比EEPROM多1億倍的寫周期。
這些功能使FM25xxx FRAM存儲器非常適合需要頻繁或快速寫入的非易失性內(nèi)存應(yīng)用程序。從數(shù)據(jù)采集(寫入周期的數(shù)量可能至關(guān)重要)到要求工業(yè)控制(串行閃存或EEPROM的長寫入時間可能導(dǎo)致數(shù)據(jù)丟失)。FM25xxx FRAM存儲器采用高速SPI總線,提高了F-RAM技術(shù)的高速寫入能力。
1.2 、通訊接口
FM25xxx FRAM存儲器采用SPI通訊接口。FM25xxx FRAM存儲器由主機控制器(通常稱為SPI主機)發(fā)送的一組指令控制。與FM25xxx FRAM存儲器的通信必須由SPI主設(shè)備(如微控制器)發(fā)起。SPI主設(shè)備必須在串行數(shù)據(jù)時鐘(SCK)引腳上為FM25xxx FRAM存儲器生成串行時鐘。FM25xxx FRAM存儲器總是作為一個從屬操作,因為SCK總是一個輸入。主機與FM25xxx FRAM存儲器通訊的拓?fù)鋱D如下所示:
FM25xxx FRAM存儲器是一個SPI從設(shè)備,運行速度高達(dá)20 MHz。這種高速串行總線為SPI主機提供高性能的串行通信。許多常見的微控制器有硬件SPI端口,允許直接接口。使用普通的端口引腳來模擬端口是非常簡單的,因為微控制器不需要。FM25xxx FRAM存儲器支持SPI模式0(0, 0)和SPI模式3(1, 1)。
1.3 、操作模式
FM25xxx FRAM存儲器被設(shè)計成直接與同步串行外圍接口(SPI)接口。FM25xxx FRAM存儲器使用一個8位指令寄存器。所有的指令、地址和數(shù)據(jù)首先由高位開始傳送,然后由高到低依次進(jìn)行。指令列表及其操作代碼如下:
從上表我們知道,除了操作存儲區(qū)域外,還可以操作狀態(tài)寄存器。FM25xxx FRAM存儲器包括一個8位狀態(tài)寄存器。狀態(tài)寄存器位調(diào)節(jié)設(shè)備的各種特性。這些位可以通過指令進(jìn)行更改。具體的結(jié)構(gòu)如下:
狀態(tài)寄存器除了反應(yīng)當(dāng)前的狀態(tài)外,實際上有些位還有配置功能。關(guān)于致謝為的屬性及具體含義如下:
通過寫狀態(tài)寄存器(WRSR)指令可以配置寫保護(hù)使能和寫保護(hù)的區(qū)域。塊寫保護(hù)位(BP1、BP0)決定了存儲陣列的寫保護(hù)區(qū)域。兩位決定了陣列保護(hù)的四個級別,分別是:沒有一個內(nèi)存陣列被保護(hù);上四分之一地址范圍內(nèi)存陣列被保護(hù);上半部分地址范圍內(nèi)存陣列被保護(hù);所有的內(nèi)存陣列都是寫保護(hù)的,這意味著所有的地址位都是只讀的。塊寫保護(hù)級別和相應(yīng)的狀態(tài)寄存器控制位關(guān)系如下:
而寫保護(hù)使能 (WPEN)位用于啟用或禁用寫保護(hù) (WP) 引腳。當(dāng)WPEN位設(shè)置為邏輯“0”時,寫入EEPROM數(shù)組的能力取決于塊寫保護(hù)(BP1、BP0)位的值。寫入狀態(tài)寄存器的權(quán)限是由WEL位控制的。當(dāng)WPEN位設(shè)置為邏輯“1”時,狀態(tài)寄存器是只讀的。當(dāng)WP引腳低且WPEN位設(shè)置為邏輯“1”時,硬件寫保護(hù)就啟用了。當(dāng)設(shè)備被硬件寫保護(hù)時,對狀態(tài)寄存器的寫操作,包括塊寫保護(hù)、WEL和WPEN位,以及對塊寫保護(hù)位所選擇的內(nèi)存陣列中的段的寫操作被禁用。當(dāng)啟用硬件寫保護(hù)時,只允許對未受塊保護(hù)的內(nèi)存段進(jìn)行寫。當(dāng)WP引腳為高電平或WPEN位邏輯為“0”時,硬件寫保護(hù)被禁用。當(dāng)硬件寫保護(hù)被禁用時,只允許對未被塊保護(hù)的內(nèi)存段進(jìn)行寫。當(dāng)WPEN位被硬件寫保護(hù)時,只要WP引腳保持低,它就不能被設(shè)置回邏輯“0”。寫保護(hù)的關(guān)系如下所示:
FM25xxx FRAM存儲器擁有從4K到1M的不同容量,尋址范圍的不同所需的地址為數(shù)據(jù)不相同。地址位數(shù)根據(jù)容量從8位到17位不等,分別對應(yīng)1到3個字節(jié)。具體的容量與地址位關(guān)系如下:
需要注意的是,4K(512x8)容量的FM25xxx FRAM存儲器需要9為地址,但在實際操作時只用了1個字節(jié)來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
2 、驅(qū)動設(shè)計與實現(xiàn)
我們已經(jīng)了解了FM25xxx FRAM存儲器的通訊接口、存儲格式等,再次基礎(chǔ)上我們將設(shè)計并實現(xiàn)FM25xxx FRAM存儲器的驅(qū)動程序。
2.1 、對象的定義
在使用一個對象之前我們需要獲得一個對象。同樣的我們想要FM25xxx FRAM存儲器就需要先定義FM25xxx FRAM存儲器的對象。
2.1.1 、對象的抽象
我們要得到FM25xxx FRAM存儲器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下FM25xxx FRAM存儲器的對象。
先來考慮屬性,作為屬性肯定是用于標(biāo)識或記錄對象特征的東西。我們來考慮FM25xxx FRAM存儲器對象屬性。首先每一個FM25xxx FRAM存儲器都有一個狀態(tài)寄存器,該狀態(tài)寄存器指示了寫保護(hù)的控制和狀態(tài)信息,所以我們將其作為對象的屬性以標(biāo)識FM25xxx FRAM存儲器的狀態(tài)。因為不同型號的FM25xxx FRAM存儲器擁有不同的存儲容量及尋址范圍,為了區(qū)分不同設(shè)備和地址寬度我們將設(shè)備類型及數(shù)據(jù)地址的長度均作為對象的屬性。
接著我們還需要考慮FM25xxx FRAM存儲器對象的操作問題。我們要對FM25xxx FRAM存儲器進(jìn)行讀寫,但讀寫都需要同過具體的SPI接口進(jìn)行,這依賴于具體的硬件平臺,所以我們將針對SPI端口的讀寫作為對象的操作。FM25xxx FRAM存儲器還有一個寫保護(hù)引腳用于設(shè)置內(nèi)部存儲器的寫保護(hù)問題,有一個片選信號應(yīng)交用于選中操作設(shè)備,有一個Hold引腳用于操作控制,這些引腳的信號改變同樣依賴于硬件平臺來實現(xiàn),所以我們也將它們作為對象的操作。在進(jìn)行相關(guān)操作時,我們需要控制時序,則需要使用延時操作,但延時處理總是依賴于具體的軟硬件平臺,所以我們將延時處理作為對象的操作。
根據(jù)上述我們對FM25xxx FRAM存儲器的分析,我們可以定義FM25xxx FRAM存儲器的對象類型如下:
/* 定義FM25C對象類型 */
typedef struct FM25Object {
uint8_t status; //狀態(tài)寄存器
FM25ModeType mode; //設(shè)備類型
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); //寫保護(hù)操作
void(*ChipSelect)(FM25CSType cs); //片選信號
void(*Hold)(FM25HoldType hold); //保持信號
void(*Delayms)(volatile uint32_t nTime); //延時操作指針
}FM25ObjectType;
2.1.2 、對象初始化
我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進(jìn)行初始化,所以這里我們來考慮FM25xxx FRAM存儲器對象的初始化函數(shù)。一般來說,初始化函數(shù)需要處理幾個方面的問題。一是檢查輸入?yún)?shù)是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據(jù)此我們設(shè)計FM25xxx FRAM存儲器對象的初始化函數(shù)如下:
/*FM25對象初始化*/
void Fm25cxxInitialization(FM25ObjectType *fram, //FM25xxx對象實體
FM25ModeTypemode, //設(shè)備類型
Fm25Readread, //讀FM25xxx對象操作指針
Fm25Writewrite, //寫FM25xxx對象操作指針
Fm25Delaymsdelayms, //延時操作指針
Fm25WPwp, //寫保護(hù)操作函數(shù)指針
Fm25ChipSelectcs, //片選信號函數(shù)指針
Fm25Holdhold //保持信號操作函數(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(modememAddLength=FM258BitMemAdd;
}
elseif(modememAddLength=FM2516BitMemAdd;
}
else
{
fram->memAddLength=FM2524BitMemAdd;
}
ReadStatusForFM25xxx(fram);
//寫允許
SetWriteEnableLatchForFM25xxx(fram);
uint8_tcmd;
//使能寫保護(hù),保護(hù)全部區(qū)域
cmd=fram->status|FM25_WPEN|FM25_BPALL;
WriteStatusForFM25xx(fram,cmd);
ReadStatusForFM25xxx(fram);
}
2.2 、寫使能操作
我們已經(jīng)完成了FM25xxx FRAM存儲器對象類型的定義和對象初始化函數(shù)的設(shè)計。但我們的主要目標(biāo)是獲取對象的信息,接下來我們還要實現(xiàn)面向FM25xxx FRAM存儲器的各類操作。
2.2.1 、設(shè)置寫使能鎖存器
FM25xxx FRAM存儲器在啟動后,寫操作是被禁用的。發(fā)送WREN操作碼允許用戶為寫操作發(fā)出后續(xù)操作碼。這包括寫入狀態(tài)寄存器(WRSR)和寫入內(nèi)存(WRITE)。發(fā)送WREN操作碼會導(dǎo)致設(shè)置內(nèi)部寫啟用鎖存器。狀態(tài)寄存器中的標(biāo)志位WEL表示鎖存器的狀態(tài)。WEL =“1”表示允許寫操作。經(jīng)過WRDI、WRSR或?qū)懖僮骱?,WEL位將自動清除,這可以防止在沒有其他WREN命令的情況下進(jìn)一步寫入狀態(tài)寄存器或F-RAM存儲陣列。WREN命令總線時序如下所示:
根據(jù)上述時序圖,我們可以設(shè)計FM25xxx FRAM存儲器設(shè)置寫使能鎖存器的程序如下:
/* 設(shè)置寫使能所存器*/
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 、復(fù)位寫使能鎖存器
WRDI命令通過清除寫使能鎖存器禁用所有寫活動。用戶可以通過讀取狀態(tài)寄存器中的WEL位并驗證WEL是否等于“ 0”來確認(rèn)寫操作是否被禁用。WRDI命令總線時序如下所示:
根據(jù)上述時序圖,我們可以設(shè)計FM25xxx FRAM存儲器復(fù)位寫使能鎖存器的程序如下:
/* 復(fù)位寫使能所存器*/
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),還用于配置寫保護(hù)。寫狀態(tài)寄存器受到WEL、WPEN、WP三重寫保護(hù),具體可見前面的寫保護(hù)限制圖。
2.3.1 、讀狀態(tài)寄存器
RDSR命令可以通過總線獲取狀態(tài)寄存器的內(nèi)容。讀取狀態(tài)寄存器提供有關(guān)寫保護(hù)特性的當(dāng)前狀態(tài)的信息。按照RDSR操作碼,F(xiàn)M25Cxx系列存儲器將返回一個包含狀態(tài)寄存器內(nèi)容的字節(jié)。RDSR命令的總線時序如下:
根據(jù)上述時序圖,我們可以設(shè)計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ù)需要設(shè)置WPEN、BP0和BP1位,從而更改寫保護(hù)配置。在發(fā)出WRSR命令之前,WP引腳必須處于高電平或非活動狀態(tài)。在發(fā)送WRSR命令之前,用戶必須發(fā)送一個WREN命令來啟用寫操作。執(zhí)行WRSR命令是一個寫操作,因此清除寫啟用鎖存器。WRSR命令的總線時序如下:
根據(jù)上述時序圖,我們可以設(shè)計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)寄存器中的寫保護(hù)啟用位(WPEN)控制硬件寫保護(hù)(WP)引腳的效果。當(dāng)WPEN位設(shè)置為“0”時,WP引腳的狀態(tài)將被忽略。當(dāng)WPEN位設(shè)置為“1”時,WP引腳上的一個低電平信號會阻止對狀態(tài)寄存器的寫入。因此,只有當(dāng)WPEN =“1”和WP =“0”時才寫保護(hù)狀態(tài)寄存器。
2.4 、操作存儲數(shù)據(jù)
FM25xxx FRAM存儲器的SPI接口具有很高的時鐘頻率,突出了F-RAM技術(shù)的快速寫入能力。與串行閃存和EEPROM不同,F(xiàn)M25xxx FRAM存儲器可以以總線速度執(zhí)行順序?qū)懭?,可以?zhí)行任意數(shù)量的順序?qū)懖僮鳌?/p>
2.4.1 、寫數(shù)據(jù)操作
FM25xxx FRAM存儲器所有對內(nèi)存的寫入都以WREN操作碼開始。寫入操作碼之后是一個存儲地址,不同容量因?qū)ぶ贩秶煌刂返臑閿?shù)也不相同。后續(xù)字節(jié)是按順序?qū)懭氲臄?shù)據(jù)字節(jié)。只要總線主機繼續(xù)發(fā)出時鐘并將CS信號保持在低電平,地址就會在內(nèi)部遞增。如果到達(dá)最后一個地址,計數(shù)器將滾動到0x0000。寫數(shù)據(jù)操作命令的總線時序如下:
根據(jù)上述時序圖,我們可以設(shè)計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);
}
}
elseif(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(inti;idata[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é)的地址。當(dāng)操作碼和地址發(fā)出后,設(shè)備將在接下來的8個時鐘上讀出數(shù)據(jù)。在讀取數(shù)據(jù)字節(jié)期間忽略信息輸入。后續(xù)字節(jié)是按順序讀出的數(shù)據(jù)字節(jié),只要總線時鐘存在且CS信號為低電平,地址就會在內(nèi)部遞增。如果到達(dá)最后一個地址,計數(shù)器將滾動到0x0000。讀數(shù)據(jù)操作命令的總線時序如下:
根據(jù)上述時序圖,我們可以設(shè)計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ū)動的使用
我們設(shè)計了FM25xxx FRAM存儲器的驅(qū)動程序,這個驅(qū)動程序是否能夠按我們的期望有效工作呢?我們需要驗證一下,下面我們將設(shè)計一個驗證驅(qū)動的簡單應(yīng)用。
3.1 、聲明并初始化對象
使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的FM25xxx FRAM存儲器對象類型聲明一個FM25xxx FRAM存儲器對象變量,具體操作格式如下:
FM25ObjectTypefm25;
聲明了這個對象變量并不能立即使用,我們還需要使用驅(qū)動中定義的初始化函數(shù)對這個變量進(jìn)行初始化。這個初始化函數(shù)所需要的輸入?yún)?shù)如下:
FM25ObjectType*fram,F(xiàn)M25xxx對象實體
FM25ModeTypemode,設(shè)備類型
Fm25Read read,讀FM25xxx對象操作指針
Fm25Write write,寫FM25xxx對象操作指針
Fm25Delaymsdelayms,延時操作指針
Fm25WP wp,寫保護(hù)操作函數(shù)指針
Fm25ChipSelectcs,片選信號函數(shù)指針
Fm25Hold hold,保持信號操作函數(shù)指針
對于這些參數(shù),對象變量我們已經(jīng)定義了。而設(shè)備類型為枚舉,根據(jù)實際使用設(shè)備情況選擇就好了。主要的是我們需要定義幾個函數(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)(volatileuint32_t nTime);
/* 定義寫保護(hù)操作函數(shù)指針類型 */
typedef void (*Fm25WP)(FM25WPType wp);
/* 定義片選操作函數(shù)指針類型 */
typedef void(*Fm25ChipSelect)(FM25CSType cs);
/* 定義保持操作函數(shù)指針類型 */
typedef void (*Fm25Hold)(FM25HoldTypehold);
對于這幾個函數(shù)我們根據(jù)樣式定義就可以了,具體的操作可能與使用的硬件平臺有關(guān)系。片選操作函數(shù)用于多設(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);
}
}
/*寫保護(hù)操作*/
void WriteProtectedForFM25(FM25WPTypewp)
{
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, //設(shè)備類型
ReadDataFromFM25, //讀FM25xxx對象操作指針
WriteDataToFM25, //寫FM25xxx對象操作指針
HAL_Delay,//延時操作指針
WriteProtectedForFM25, //寫保護(hù)操作函數(shù)指針
ChipSelectForFM25, //片選信號函數(shù)指針
HoldForFM25 //保持信號操作函數(shù)指針
);
3.2 、基于對象進(jìn)行操作
我們定義了對象變量并使用初始化函數(shù)給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數(shù)據(jù)。我們在驅(qū)動中已經(jīng)將獲取數(shù)據(jù)并轉(zhuǎn)換為轉(zhuǎn)換值的比例值,接下來我們使用這一驅(qū)動開發(fā)我們的應(yīng)用實例。
/*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 、應(yīng)用總結(jié)
在這一篇中,我們實現(xiàn)了FM25xxx FRAM存儲器的驅(qū)動程序,并在次驅(qū)動程序的基礎(chǔ)上設(shè)計了簡單的驗證應(yīng)用。無論是寫數(shù)據(jù)還是讀數(shù)據(jù)均可順利執(zhí)行,說明我們的驅(qū)動設(shè)計是正確的。
FM25xxx FRAM存儲器與其他非易失性內(nèi)存技術(shù)不同,F(xiàn)-RAM沒有有效的寫延遲。由于底層內(nèi)存的讀和寫訪問時間相同,因此用戶不會在總線上體驗延遲。整個內(nèi)存周期的時間比一個總線時鐘還短。因此,任何操作(包括讀或?qū)懀┒伎梢栽趯懼罅⒓磮?zhí)行。
需要注意的是,4K(512x8)容量的FM25xxx FRAM存儲器需要9為地址,但在實際操作時只用了1個字節(jié)來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
在使用驅(qū)動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現(xiàn)的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數(shù)。
源碼下載: https://github.com/foxclever/ExPeriphDriver
評論