我們在前面開發過AT24CXX系列EEPROM存儲器,它使用的是I2C接口。不過有時候我們也會使用SPI接口的EEPROM存儲器。在這一篇我們將來討論AT25XXX系列EEPROM存儲器的驅動設計、實現及使用。
1、功能概述
??AT25XXX系列EEPROM存儲器采用SPI接口,因其操作簡單且性價比高,常用于數據保存。出于開發面向AT25XXX系列EEPROM存儲器操作的驅動目標,我們先來了解一下AT25XXX系列EEPROM存儲器的基本情況。
1.1、硬件描述
??AT25XXX系列EEPROM存儲器擁有從1K到2M的多種容量。AT25XXX系列EEPROM存儲器采用SPI通訊接口。盡管容量跨度很大,但都采用相同的封裝形式。具體的引腳排布及定義如下:
?為了更好地理解AT25XXX系列EEPROM存儲器的操作過程,我們對CS、WP以及HOLD引腳做一個簡單說明。
??首先來看一看CS引腳,CS為芯片選擇引腳,低電平有效,AT25XXX系列EEPROM存儲器被選中。當設備未被選中時,串行數據輸入(SI)引腳將不接受數據,串行輸出(SO)引腳將保持高阻抗狀態。為了確保穩定的操作,CS引腳應在電源啟動時跟隨VCC。因此建議使用小于或等于10 kΩ的上拉電阻器連接到VCC。在電源啟動后,要實現對AT25XXX系列EEPROM存儲器的任何操作,都需要先將CS下拉到低電平。
??接下來我們看一看WP引腳,WP引腳為AT25XXX系列EEPROM存儲器的寫保護引腳。當寫保護(WP)引腳保持高電位時,AT25XXX系列EEPROM存儲器允許正常的讀/寫操作。當寫保護(WP)引腳為低電平時,WPEN位設置為邏輯“1”時,所有對狀態寄存器的寫操作都被禁止。但如果內部寫周期已經啟動,那么寫保護(WP)引腳變為低電平對狀態寄存器的任何寫操作都沒有影響。當狀態寄存器中的WPEN位設置為邏輯“0”時,寫保護(WP)引腳的功能被屏蔽。
??最后我們來看一看HOLD引腳,暫停串行輸入(HOLD)引腳低電平有效。暫停串行輸入(HOLD)引腳與芯片選擇(CS)引腳一起使用來暫停AT25XXX系列EEPROM存儲器。當設備被選中,一個串行序列正在進行中,HOLD可以用來暫停與主設備的串行通信,而不需要重新設置串行序列。此時,串行數據輸入(SI)引腳的輸入將被忽略,而串行數據輸出(SO)引腳將處于高阻抗狀態。
1.2、通訊接口
??AT25XXX系列EEPROM存儲器采用SPI通訊接口。AT25XXX系列EEPROM存儲器由主機控制器(通常稱為SPI主機)發送的一組指令控制。與AT25XXX系列EEPROM存儲器的通信必須由SPI主設備(如微控制器)發起。SPI主設備必須在串行數據時鐘(SCK)引腳上為AT25XXX系列EEPROM存儲器生成串行時鐘。AT25XXX系列EEPROM存儲器總是作為一個從屬操作,因為SCK總是一個輸入。主機與AT25XXX系列EEPROM存儲器通訊的拓撲圖如下所示:
??SPI主機通過SPI總線與AT25XXX系列EEPROM存儲器通信,SPI總線由四條信號線組成:芯片選擇(CS)、串行數據時鐘(SCK)、串行數據輸入(SI)和串行數據輸出(SO)。SPI協議定義了總共四種操作模式(模式0、模式1、模式2或模式3),每種模式在SCK極性和相位以及極性和相位如何控制SPI總線上的數據流方面有所不同。AT25XXX系列EEPROM存儲器支持兩種最常見的模式,SPI模式0和3。
1.3、命令操作
??AT25XXX系列EEPROM存儲器被設計成直接與同步串行外圍接口(SPI)接口。AT25XXX系列EEPROM存儲器使用一個8位指令寄存器。所有的指令、地址和數據首先由高位開始傳送,然后由高到低依次進行。指令列表及其操作代碼如下:
? 從上表我們知道,除了操作存儲區域外,還可以操作狀態寄存器。AT25XXX系列EEPROM存儲器包括一個8位狀態寄存器。狀態寄存器位調節設備的各種特性。這些位可以通過指令進行更改。具體的結構如下:
狀態寄存器除了反映當前的狀態外,實際上有些位還有配置功能。關于致謝為的屬性及具體含義如下
? 通過寫狀態寄存器(WRSR)指令可以配置寫保護使能和寫保護的區域。塊寫保護位(BP1、BP0)決定了存儲陣列的寫保護區域。兩位決定了陣列保護的四個級別,分別是:沒有一個內存陣列被保護;上四分之一地址范圍內存陣列被保護;上半部分地址范圍內存陣列被保護;所有的內存陣列都是寫保護的,這意味著所有的地址位都是只讀的。塊寫保護級別和相應的狀態寄存器控制位關系如下:
? 而寫保護使能 (WPEN)位用于啟用或禁用寫保護 (WP) 引腳。當WPEN位設置為邏輯“0”時,寫入EEPROM數組的能力取決于塊寫保護(BP1、BP0)位的值。寫入狀態寄存器的權限是由WEL位控制的。當WPEN位設置為邏輯“1”時,狀態寄存器是只讀的。當WP引腳低且WPEN位設置為邏輯“1”時,硬件寫保護就啟用了。當設備被硬件寫保護時,對狀態寄存器的寫操作,包括塊寫保護、WEL和WPEN位,以及對塊寫保護位所選擇的內存陣列中的段的寫操作被禁用。當啟用硬件寫保護時,只允許對未受塊保護的內存段進行寫。當WP引腳為高電平或WPEN位邏輯為“0”時,硬件寫保護被禁用。當硬件寫保護被禁用時,只允許對未被塊保護的內存段進行寫。當WPEN位被硬件寫保護時,只要WP引腳保持低,它就不能被設置回邏輯“0”。寫保護的關系如下所示:
??AT25XXX系列EEPROM存儲器擁有從1K到2M的不同容量,尋址范圍的不同所需的地址位數也不相同。地址位數根據容量從7位到18位不等,分別對應1到3個字節。具體的容量與地址位關系如下:
??需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存儲器需要9為地址,但在實際操作時只用了1個字節來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
2、驅動設計與實現
??我們已經了解了AT25XXX存儲器的基本功能及讀寫方式,接下來我們將開發操作AT25XXX系列EEPROM存儲器的驅動程序。
2.1、對象定義
??在使用一個對象之前我們需要獲得一個對象。同樣的我們想要AT25XXX系列EEPROM存儲器就需要先定義AT25XXX系列EEPROM存儲器的對象。
2.1.1、對象的抽象
??我們要得到AT25XXX系列EEPROM存儲器對象,需要先分析其基本特性。一般來說,一個對象至少包含兩方面的特性:屬性與操作。接下來我們就來從這兩個方面思考一下AT25XXX系列EEPROM存儲器的對象。
??先來考慮屬性,作為屬性肯定是用于標識或記錄對象特征的東西。我們來考慮AT25XXX系列EEPROM存儲器對象屬性。首先AT25XXX系列EEPROM存儲器有多種型號,不同型號在容量、地址位數等方面都有較大差異。為了區別不同類型的AT25XXX系列EEPROM存儲器,我們將類型作為對象的屬性。另外每一個AT25XXX對象都有一個狀態寄存器,它標識了AT25XXX對象的當前狀態,所以我們也將它作為對象的屬性。
??接著我們還需要考慮AT25XXX系列EEPROM存儲器對象的操作問題。我們總是要對AT25XXX對象進行數據讀寫,但讀寫操作使用SPI接口依賴于具體的硬件平臺,所以我們將數據讀寫作為對象的操作。片選信號、寫保護以及hold信號均依賴于具體的硬件定義來實現,所以我們將其作為對象的操作。還有用于時序控制的延時,其也要根據具體的軟硬件平臺來實現,所以我們也將其定義為對象的操作。
??根據上述我們對AT25XXX系列EEPROM存儲器的分析,我們可以定義AT25XXX系列EEPROM存儲器的對象類型如下:
/*定義AT25XXX對象類型*/
typedef struct At25Object {
uint8_t status;//狀態寄存器
At25ModeType mode;//設備類型
void (*Read)(uint8_t *rData,uint16_t rSize); //讀數據操作指針
void (*Write)(uint8_t *wData,uint16_t wSize); //寫數據操作指針
void (*Delayms)(volatile uint32_t nTime); //毫秒延時操作指針
void (*ChipSelect)(AT25xxxCSType cs);//使用SPI接口時,片選操作
void (*WP)(AT25WPType wp);//寫保護操作
void (*Hold)(AT25HoldType hold);//保持信號
}At25ObjectType;
2.1.2、對象初始化
??我們知道,一個對象僅作聲明是不能使用的,我們需要先對其進行初始化,所以這里我們來考慮AT25XXX系列EEPROM存儲器對象的初始化函數。一般來說,初始化函數需要處理幾個方面的問題。一是檢查輸入參數是否合理;二是為對象的屬性賦初值;三是對對象作必要的初始化配置。據此我們設計AT25XXX系列EEPROM存儲器對象的初始化函數如下:
/* 初始化AT25XXX對象 */
void At25xxxInitialization(At25ObjectType *at,//AT25XXX對象實體
At25ModeType mode,//AT25XXX對象類型
AT25Read read,//讀AT25XXX對象操作指針
AT25Write write,//寫AT25XXX對象操作指針
AT25Delayms delayms,//延時操作指針
AT25ChipSelect cs//片選操作函數指針
)
{
if((at==NULL)||(read==NULL)||(write==NULL)||(delayms==NULL))
{
return;
}
at->Read=read;
at->Write=write;
at->Delayms=delayms;
if(cs!=NULL)
{
at->ChipSelect=cs;
}
else
{
at->ChipSelect=AT25ChipSelectDefault;
}
if(mode>=AT25Number)
{
return;
}
at->mode=mode;
if(modememAddLength=AT258BitMemAdd;
}
else if(modememAddLength=AT2516BitMemAdd;
}
else
{
at->memAddLength=AT2524BitMemAdd;
}
ReadStatusForAT25xxx(at);
//寫允許
SetWriteEnableLatchForAT25xxx(at);
uint8_t cmd;
//使能寫保護,保護全部區域
cmd=at->status|AT25_WPEN|AT25_BPALL;
WriteStatusForAT25xx(at,cmd);
ReadStatusForAT25xxx(at);
}
2.2、對象操作
??我們已經完成了AT25XXX系列EEPROM存儲器對象類型的定義和對象初始化函數的設計。但我們的主要目標是獲取對象的信息,接下來我們還要實現面向AT25XXX系列EEPROM存儲器的各類操作。
2.2.1、讀數據操作
??讀取AT25XXX系列EEPROM存儲器需要先將CS線拉低以選擇設備,爾后發送READ(0x03)指令,在后發送要讀的寄存器地址。一旦接收完寄存器地址,后續的信號將被忽略。然后返回指定地址的數據。讀AT25XXX系列EEPROM存儲器數據的操作時序如下:
??根據上述時序圖,我們可以編寫讀AT25XXX系列EEPROM存儲器數據的程序如下:
/*從AT25xxx讀取數據*/
void ReadDatasFromAT25xxx(At25ObjectType *at,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++]=AT25_READ;
if(at->memAddLength==AT258BitMemAdd)
{
data[index++]=(uint8_t)regAddress;
if(at->mode==AT25040B)
{
temp=(uint8_t)(regAddress>>8);
data[0]|=((temp&0x01)<<3);
}
}
else if(at->memAddLength==AT2516BitMemAdd)
{
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;
}
temp=(uint8_t)(regAddress®AddMask[at->mode]);
if((rSize<=pageBytes[at->mode])&&(rSize<=(pageBytes[at->mode]-temp)))
{
size=rSize;
}
else
{
size=pageBytes[at->mode]-temp;
}
at->ChipSelect(AT25CS_Enable);
at->Write(data,index);
at->Delayms(1);
at->Read(rData,size);
at->ChipSelect(AT25CS_Disable);
}
??如果只需要讀取一個字節,那么在讀取一個字節后CS信號需在讀取后恢復高電平。如果想讀取多個字節,那么CS信號必須持續低電平,在存儲器內部字節地址將自動遞增,數據將繼續移位。當到達最高地址時,地址計數器將滾動到最低階地址,從而允許在一個連續的讀取循環中讀取整個內存,而不管起始地址是什么。
2.2.2、寫數據操作
??寫AT25XXX系列EEPROM存儲器,必須執行兩條單獨的指令。首先,必須通過寫使能(WREN)指令使設備能夠寫。然后,可以執行寫序列。如果設備沒有啟用寫(WREN),設備將忽略寫指令。在內部寫周期中,除了RDSR指令外,所有命令都將被忽略。寫AT25XXX系列EEPROM存儲器的時序如下:
??根據上述時序圖,我們可以編寫寫AT25XXX系列EEPROM存儲器數據的程序如下:
/*向AT25xxx寫入數據*/
void WriteDatasToAT25xxx(At25ObjectType *at,uint16_t regAddress,uint8_t *wData,uint16_t wSize)
{
uint8_t data[wSize+4];
uint16_t index=0;
uint8_t temp;
uint16_t size=0;
data[index++]=AT25_WRITE;
if(at->memAddLength==AT258BitMemAdd)
{
data[index++]=(uint8_t)regAddress;
if(at->mode==AT25040B)
{
temp=(uint8_t)(regAddress>>8);
data[0]|=((temp&0x01)<<3);
}
}
else if(at->memAddLength==AT2516BitMemAdd)
{
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;
}
temp=(uint8_t)(regAddress®AddMask[at->mode]);
if((wSize<=pageBytes[at->mode])&&(wSize<=(pageBytes[at->mode]-temp)))
{
size=wSize;
}
else
{
size=pageBytes[at->mode]-temp;
}
for(int i;idata[index++]=wData[i];
}
if(((at->status)&0x02)!=0x02)
{
SetWriteEnableLatchForAT25xxx(at);
}
if(((at->status)&0x0C)!=0x00)
{
WriteStatusForAT25xx(at,at->status|AT25_BPNONE);
}
at->ChipSelect(AT25CS_Enable);
at->Write(data,index);
at->ChipSelect(AT25CS_Disable);
WriteStatusForAT25xx(at,at->status|AT25_BPALL);
}
2.2.3、狀態寄存器操作
??前面我們已經詳細描述過狀態寄存器的格式以及每一位的定義。有一些位是只讀的,有一些位是可讀寫的,記下來我們實現針對狀態寄存器中的操作。
(1)、讀狀態寄存器
??讀狀態寄存器(RDSR)指令提供對狀態寄存器的訪問。RDSR指令可以確定設備的狀態以及塊寫保護(BP1, BP0)位表示所使用的內存陣列保護的范圍。讀取狀態寄存器的方法是拉低CS信號,然后發送0x05操作碼。操作碼完成后,設備將返回8位狀態寄存器值。具體時序如下:
??根據上述時序圖,我們可以編寫讀AT25XXX系列EEPROM存儲器狀態寄存器的程序如下:
/*讀AT25xxx狀態寄存器*/
void ReadStatusForAT25xxx(At25ObjectType *at)
{
uint8_t opCode=AT25_RDSR;
uint8_t status;
at->ChipSelect(AT25CS_Enable);
at->Write(&opCode,1);
at->Delayms(1);
at->Read(&status,1);
at->ChipSelect(AT25CS_Enable);
at->status=status;
}
(2)、寫狀態寄存器
??寫狀態寄存器(WRSR)指令使SPI主機能夠更改狀態寄存器的選定位。在WRSR指令開始之前,必須執行一條WREN指令,將WEL位設置為邏輯“1”。WREN指令完成后,可以執行WRSR指令。在WRSR指令之后,AT25XXX系列EEPROM存儲器將不會響應除RDSR以外的命令,直到自動計時的內部寫周期完成。寫周期結束后,狀態寄存器中的WEL位復位為邏輯“0”。具體的時序圖如下:
??根據上述時序圖,我們可以編寫寫AT25XXX系列EEPROM存儲器狀態寄存器的程序如下:
/*寫AT25xxx狀態寄存器*/
void WriteStatusForAT25xx(At25ObjectType *at,uint8_t cmd)
{
uint8_t data[2];
data[0]=AT25_WRSR;
data[1]=cmd;
if(((at->status)&0x02)!=0x02)
{
SetWriteEnableLatchForAT25xxx(at);
}
if((((at->status)&AT25_WPEN)!=AT25_WPEN)&&(at->WP!=NULL))
{
at->WP(AT25WP_Disable);
}
at->ChipSelect(AT25CS_Enable);
at->Write(data,2);
at->ChipSelect(AT25CS_Disable);
ReadStatusForAT25xxx(at);
if(at->WP!=NULL)
{
at->WP(AT25WP_Enable);
}
}
??WRSR指令對狀態寄存器的第6位、第5位、第4位、第1位和第0位沒有影響。只有第7位、第3位和第2位可以通過WRSR指令進行更改。這些可修改的位是寫保護使能(WPEN)和塊保護(BP1, BP0)位。這三個位元是非易失性位元,具有與常規EEPROM單元相同的特性和功能。當電源從設備中移除時,它們的值被保留。
2.2.4、寫操作使能與失能操作
??通過寫使能(WREN)指令和寫失能(WRDI)指令實現對狀態寄存器和EEPROM陣列的寫操作的啟用和禁用。這些功能改變了狀態寄存器中WEL位的狀態。
(1)寫操作啟用
??狀態寄存器的寫能門閂(WEL)位必須在每個寫狀態寄存器(WRSR)和寫入內存陣列(Write)指令之前設置為邏輯“1”。這是通過向AT25XXX系列EEPROM存儲器發送一條WREN(0x06)指令來完成的。首先,CS引腳被拉低以選擇設備,然后發送WREN指令。然后CS信號被拉高,并將狀態寄存器中的WEL位更新為邏輯“1”。具體的操作時序如下:
?
根據上述時序圖,我們可以編寫AT25XXX系列EEPROM存儲器操作啟用的程序如下:
/* AT25XXX設置寫使能所存器*/
void SetWriteEnableLatchForAT25xxx(At25ObjectType *at)
{
uint8_t opCode=AT25_WREN;
at->ChipSelect(AT25CS_Enable);
at->Write(&opCode,1);
at->ChipSelect(AT25CS_Enable);
ReadStatusForAT25xxx(at);
}
(2)、寫操作禁用
??為了防止誤寫,寫禁用(WRDI)指令(0x04)通過將WEL位設置為邏輯“0”來禁用所有編程模式。WRDI指令與WP引腳的狀態無關。具體的操作時序如下圖所示:
?
?根據上述時序圖,我們可以編寫AT25XXX系列EEPROM存儲器操作禁用的程序如下:
/* AT25XXX復位寫使能所存器*/
void ResetWriteEnableLatchForAT25xxx(At25ObjectType *at)
{
uint8_t opCode=AT25_WRDI;
at->ChipSelect(AT25CS_Enable);
at->Write(&opCode,1);
at->ChipSelect(AT25CS_Enable);
ReadStatusForAT25xxx(at);
}
3、驅動的使用
??在上一節我們設計并實現了AT25XXX系列EEPROM存儲器的驅動程序,而這一節我們將設計一個簡單的應用來驗證這一驅動程序。
3.1、聲明并初始化對象
??使用基于對象的操作我們需要先得到這個對象,所以我們先要使用前面定義的AT25XXX系列EEPROM存儲器對象類型聲明一個AT25XXX系列EEPROM存儲器對象變量,具體操作格式如下:??At25ObjectType at25;??聲明了這個對象變量并不能立即使用,我們還需要使用驅動中定義的初始化函數對這個變量進行初始化。這個初始化函數所需要的輸入參數如下:
At25ObjectType *at,AT25XXX對象實體
At25ModeType mode,AT25XXX對象類型
AT25Read read,讀AT25XXX對象操作指針
AT25Write write,寫AT25XXX對象操作指針
AT25Delayms delayms,延時操作指針
AT25ChipSelect cs,片選操作函數指針
??對于這些參數,對象變量我們已經定義了。而對象類型為枚舉,根據實際使用的AT25XXX系列EEPROM存儲器來選擇就好了。主要的是我們需要定義幾個函數,并將函數指針作為參數。這幾個函數的類型如下:
/* 定義讀數據操作函數指針類型 */
typedef void (*AT25Read)(uint8_t *rData,uint16_t rSize);
/* 定義寫數據操作函數指針類型 */
typedef void (*AT25Write)(uint8_t *wData,uint16_t wSize);
/* 定義延時操作函數指針類型 */
typedef void (*AT25Delayms)(volatile uint32_t nTime);
/* 定義使用SPI接口時,片選操作函數指針類型 */
typedefvoid (*AT25ChipSelect)(AT25xxxCSType cs);
??對于這幾個函數我們根據樣式定義就可以了,具體的操作可能與使用的硬件平臺有關系。片選操作函數用于多設備需要軟件操作時,如采用硬件片選可以傳入NULL即可。具體函數定義如下:
/*讀AT25寄存器值*/
static void ReadDataFromAT25(uint8_t *rData,uint16_t rSize)
{
HAL_SPI_Receive (&at25hspi,rData,rSize,1000);
}
/*寫AT25寄存器值*/
static void WriteDataToAT25(uint8_t *wData,uint16_t wSize)
{
HAL_SPI_Transmit (&at25hspi,wData,wSize,1000);
}
/*片選操作*/
void ChipSelectForAT25(AT25xxxCSType cs)
{
if(cs==AT25CS_Enable)
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);
}
}
??對于延時函數我們可以采用各種方法實現。我們采用的STM32平臺和HAL庫則可以直接使用HAL_Delay()函數。于是我們可以調用初始化函數如下:
At25xxxInitialization(&at25,//AT25XXX對象實體
AT25M01,//AT25XXX對象類型
ReadDataFromAT25,//讀AT25XXX對象操作指針
WriteDataToAT25,//寫AT25XXX對象操作指針
HAL_Delay,//延時操作指針
ChipSelectForAT25//片選操作函數
);
3.2、基于對象進行操作
??我們定義了對象變量并使用初始化函數給其作了初始化。接著我們就來考慮操作這一對象獲取我們想要的數據。我們在驅動中已經將獲取數據并轉換為轉換值的比例值,接下來我們使用這一驅動開發我們的應用實例。
/*AT25XXX數據讀寫操作*/
void AT25ReadWriteData(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;
/*從AT25XXX讀取單個字節,從隨機地址讀取*/
readByte=ReadByteFromAT25xxx(&at25,regAddress);
/*向AT25XXX寫入單個字節*/
WriteByteToAT25xxx(&at25,regAddress,writeByte);
/*從AT25XXX讀取多個字節,從指定地址最多到所在頁的結尾*/
ReadBytesFromAT25xxx(&at25,regAddress,rData,rSize);
/*向AT25XXX寫入多個字節,從指定地址最多到所在頁的結尾*/
WriteBytesToAT25xxx(&at25,regAddress,wData,wSize);
}
4、應用總結
??在本篇片中我們討論并設計了AT25XXX系列EEPROM存儲器的驅動程序,并據此設計了一個簡單的驗證應用。無論是寫數據還是讀數據均可順利執行,說明我們的驅動設計是正確的。
??需要注意的是,4K(512x8)容量的AT25XXX系列EEPROM存儲器需要9為地址,但在實際操作時只用了1個字節來裝載地址,最高位(第9位)地址借用了操作碼的第4位來傳送。
??在使用驅動時需注意,采用SPI接口的器件需要考慮片選操作的問題。如果片選信號是通過硬件電路來實現的,我們在初始化時給其傳遞NULL值。如果是軟件操作片選則傳遞我們編寫的片選操作函數。
??在這一驅動設計的過程中我們并未驗證讀寫數據的正確性,事實上如果是比較重要的數據我們可以為其添加驗證,如CRC驗證等。
評論
查看更多