0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

淺談STM32之SPI_FLASH之應(yīng)用實例

ss ? 來源:未知 ? 作者:沈丹 ? 2018-10-07 11:29 ? 次閱讀
SPI Flash
首先它是個Flash,F(xiàn)lash是什么東西就不多說了(非易失性存儲介質(zhì)),分為NOR和NAND兩種(NOR和NAND的區(qū)別本篇不做介紹)。SPI一種通信接口。那么嚴(yán)格的來說SPI Flash是一種使用SPI通信的Flash,即,可能指NOR也可能是NAND。但現(xiàn)在大部分情況默認(rèn)下人們說的SPI Flash指的是SPI NorFlash。早期Norflash的接口是parallel的形式,即把數(shù)據(jù)線和地址線并排與IC的管腳連接。但是后來發(fā)現(xiàn)不同容量的Norflash不能硬件上兼容(數(shù)據(jù)線和地址線的數(shù)量不一樣),并且封裝比較大,占用了較大的PCB板位置,所以后來逐漸被SPI(串行接口)Norflash所取代。同時不同容量的SPI Norflash管腳也兼容封裝也更小。,至于現(xiàn)在很多人說起NOR flash直接都以SPI flash來代稱。
NorFlash根據(jù)數(shù)據(jù)傳輸?shù)奈粩?shù)可以分為并行(Parallel,即地址線和數(shù)據(jù)線直接和處理器相連)NorFlash和串行(SPI,即通過SPI接口和處理器相連)NorFlash;區(qū)別主要就是:1、SPI NorFlash每次傳輸一bit位的數(shù)據(jù),parallel連接的NorFlash每次傳輸多個bit位的數(shù)據(jù)(有x8和x16bit兩種); 2、SPI NorFlash比parallel便宜,接口簡單點,但速度慢。
NandFlash是地址數(shù)據(jù)線復(fù)用的方式,接口標(biāo)準(zhǔn)統(tǒng)一(x8bit和x16bit),所以不同容量再兼容性上基本沒什么問題。但是目前對產(chǎn)品的需求越來越小型化以及成本要求也越來越高,所以SPI NandFlash漸漸成為主流,并且采用SPI NANDFlash方案,主控也可以不需要傳統(tǒng)NAND控制器,只需要有SPI接口接口操作訪問,從而降低成本。另外SPI NandFlash封裝比傳統(tǒng)的封裝也小很多,故節(jié)省了PCB板的空間。
怎么用說白了對于Flash就是讀寫擦,也就是實現(xiàn)flash的驅(qū)動。先簡單了解下spi flash的物理連接。
之前介紹SPI的時候說過,SPI接口目前的使用是多種方式(具體指的是物理連線有幾種方式),Dual SPI、Qual SPI和標(biāo)準(zhǔn)的SPI接口(這種方式肯定不會出現(xiàn)在連接外設(shè)是SPI Flash上,這玩意沒必要全雙工),對于SPI Flash來說,主要就是Dual和Qual這兩種方式。具體項目具體看了,理論上在CLK一定的情況下, 線數(shù)越多訪問速度也越快。我們項目采用的Dual SPI方式,即兩線。

本實例用的是STM32F103VET6平臺,它有3個SPI接口(這里使用SPI1),各信號線連接到FLASH(型號:W25X16)的CS,CLK,DO,DIO線,以實現(xiàn)SPI通訊,對FLASH進(jìn)行讀寫。

(這里采用主模式,全雙工通訊,通過查詢發(fā)送數(shù)據(jù)寄存器和接收數(shù)據(jù)寄存器狀態(tài)確保通訊正常)

mian函數(shù):

1#define sFLASH_ID 0xEF3015(前面加個1,免得變大)

u32 DeviceID;

u32 FlashID;

int main(void)

{

/115200 8-N-1/

USART1_Config();

SPI_FLASH_Init();

DeviceID = SPI_FLASH_ReadDeviceID();

Delay(200);

FlashID = SPI_FLASH_ReadID();

printf(“\r\n FlashID is 0x%X, Manufacturer Device ID is 0x%X\r\n”,F(xiàn)lashID,DeviceID);

if(FlashID == sFLASH_ID)

{

printf(“\r\n 檢測到 flash W25X16 !\r\n”);

SPI_FLASH_SectorErase(FLASH_SectorToErase);

SPI_FLASH_BufferWrite(Tx_Buffer, FLASH_WriteAddress, BufferSize);

printf(“\r\n 寫入的數(shù)據(jù)為:%s \r\t”, Tx_Buffer);

SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);

printf(“\r\n 讀出的數(shù)據(jù)為:%s \r\n”, Tx_Buffer);

TransferStatus1 = Buffercmp(Tx_Buffer, Rx_Buffer, BufferSize);

if( PASSED == TransferStatus1)

{

printf(“\r\n 2M 串行 flash(W25X16)測試成功!\n\r”);

}

else

{

printf(“\r\n 2M 串行 flash(W25X16)測試失敗!\n\r”);

}

}

else

{

printf(“\r\n 獲取不到 W25X16 ID!\n\r”);

}

SPI_Flash_PowerDown();

while(1);

1234567891011121314151617181920212223242526272829303132333435363738

}

mian函數(shù)的流程:

1,調(diào)用 USART1_Config() 初始化串口;

2,調(diào)用 SPI_FLASH_Init() 初始化SPI模塊;

3,調(diào)用 SPI_FLASH_ReadDeviceID 讀取FLASH器件生產(chǎn)廠商的ID信息;

4,調(diào)用 SPI_FLASH_ReadID 讀取FLASH器件的設(shè)備ID信息;

5,如果讀取ID正確,則調(diào)用 SPI_FLASH_SectorErase()把FLASH內(nèi)容擦除,擦除后調(diào)用 SPI_FLASH_BufferWrite()向FLASH寫入數(shù)據(jù),然后再調(diào)用 SPI_FLASH_BufferRead()從剛剛寫入的地址中讀出數(shù)據(jù),最后調(diào)用 Buffercmp()對寫入和讀取的數(shù)據(jù)進(jìn)行匹配,匹配成功則把標(biāo)志變量 TransferStatus1賦值為 PASSED(自定義的枚舉變量);

6,根據(jù)標(biāo)志量 TransferStatus1判斷FLASH數(shù)據(jù)的:擦除,寫入,讀取是否正常,分情況輸出到終端;

7,如果讀取FLASH的ID信息錯誤,則直接向終端輸出檢測不到FLASH信息;

8,最后調(diào)用 SPI_Flash_PowerDown()函數(shù)關(guān)閉 FLASH設(shè)備的電源(因為數(shù)據(jù)寫入到FLASH后并不會因斷電而丟失,所以需要使用的時候再開啟FLASH電源);

PS:

讀取器件ID信息可以知道設(shè)備與主機(jī)是否能夠正常工作,也便于區(qū)分不同的器件,可以在使用的FLASH用戶數(shù)據(jù)手冊找到ID表

SPI的初始化:

void SPI_FLASH_Init(void)

{

SPI_InitTypeDef SPI_InitStructure;

GPIO_InitTypeDef GPIO_InitStructure;

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE);

/這里是GPIO初始化部分,將4個引腳都設(shè)定好/

/!《 Configure SPI_FLASH_SPI pins: SCK /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI pins: MISO /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI pins: MOSI /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/!《 Configure SPI_FLASH_SPI_CS_PIN pin: SPI_FLASH Card CS pin /

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

GPIO_Init(GPIOA, &GPIO_InitStructure);

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH(); //不用的時候就拉高

/這里是SPI設(shè)置部分/

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;

SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

SPI_InitStructure.SPI_CRCPolynomial = 3;

SPI_Init(SPI1, &SPI_InitStructure);

/* Enable SPI1 */

SPI_Cmd(SPI1, ENABLE);

}

GPIO初始化:

根據(jù)《STM32數(shù)據(jù)手冊》以及《STM32參考手冊》,把PA5(SCK),PA6(MISO),PA7(MOSI)設(shè)置成復(fù)用推挽輸出,因為PA4(NSS)是使用軟件模式,所以設(shè)置為通用退完輸出。

SPI模式初始化:

對于初始化,是需要根據(jù)通訊的設(shè)備FLASH的SPI特性來決定的,下面成員分析:

SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;

這里設(shè)置通訊模式,這里設(shè)置成全雙工模式(可以在keil環(huán)境下查找其他模式)

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;

這里是設(shè)置工作模式,STM32的SPI設(shè)備可以gon工作在主機(jī)模式(SPI_Mode_Master)或從機(jī)模式(SPI_Mode_Slave),這兩個模式最大的區(qū)別就是SPI的SCK信號線時序,SCK的時序是由通訊中的主機(jī)產(chǎn)生的,如果配置成從機(jī)模式,STM32的SPI模塊將接收外來的SCK信號。(這里STM32作為SPI通訊主機(jī),所以設(shè)置成 SPI_Mode_Master)。

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

這個是設(shè)置SPI每次通訊的數(shù)據(jù)大?。ǚQ為數(shù)據(jù)幀)為8位還是16位(從FLASH的數(shù)據(jù)手冊可以查到,這里的FLASH的數(shù)據(jù)幀大小為8為,所以要把STM32的SPI模塊設(shè)置相同的)

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;&SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;

這兩個成員是配置SPI的時鐘極性(CPOL)和時鐘相位(CPHA),這兩個配置影響到SPI的通訊模式,要設(shè)置成符合將要互相通訊的設(shè)備的要求。

CPOL:可以取 SPI_CPOL_High(SPI 通訊空閑時 SCK 為高電平)或者SPI_CPOL_Low(SPI 通訊空閑時 SCK 為低電平);

CPHA:可以取 SPI_CPHA_1Edge(在 SCK 的奇數(shù)邊沿采集數(shù)據(jù))或者SPI_CPHA_2Edge (在 SCK 的偶數(shù)邊沿采集數(shù)據(jù));

查詢這個FLASH的使用手冊,可以了解到這個FLASH支持以SPI的模式0和模式3通訊。

模式0:在SPI空閑時,SCK為低電平,奇數(shù)邊沿采樣;

模式3:在SPI空閑時,SCK為高電平,偶數(shù)變異采樣;

所以這里配置成模式3,把CPOL賦值為SPI_CPOL_High(SPI空閑時SCK為高電平),把CPHA賦值為SPI_CPHA_2Edge(在SCK的偶數(shù)邊沿超級數(shù)據(jù))

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

這里是配置NSS引腳的使用模式,可以選擇為硬件模式(SPI_NSS_Hard)與軟件模式(SPI_NSS_Soft),在硬件模式中的SPI片選由硬件自動產(chǎn)生,而軟件模式則需要手動把相應(yīng)的FPIO端口拉高或拉低產(chǎn)生非片選和片選信號(如果外界條件允許,硬件模式還會自動將STM32的SPI設(shè)置為主機(jī))

這里是由軟件產(chǎn)生模式,所以賦值為SPI_NSS_Soft.

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

這里是設(shè)置波特率分頻值,分頻后的時鐘為SPI的SCK信號線的時鐘頻率,這個成員可以設(shè)置為fpclk的2,4,6,8,32,64,128,256分頻。這里設(shè)置為4分頻

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

所有串行的通訊協(xié)議都會有MSB先行(高位數(shù)據(jù)在前)還是LSB先行(地位數(shù)據(jù)在前)的問題,STM32的SPI模塊可以通過對這個結(jié)構(gòu)體成員,對這個特性編程控制。

根據(jù)FLASH的通訊時序,這里設(shè)置為MSB先行(SPI_FirstBit_MSB)

SPI_InitStructure.SPI_CRCPolynomial = 3;

這里是設(shè)置SPI的CEC校驗的多項式,如果使用到CRC校驗時,就是用這個成員的參數(shù)(多項式),來計算CRC的值。(這里的FLASH不支持CRC校驗,所以賦值為3其實沒意義)

配置完這些結(jié)構(gòu)體成員后,調(diào)用 SPI_Init()把這些參數(shù)寫入到寄存器中,然后調(diào)用SPI_Cmd()使能SPI1外設(shè)。

PS:

SPI_FLASH_CS_HIGH()這個實際是上一個自定義的宏:

#define SPI_FLASH_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4)

實際上這個宏就是用來把 PA4(NSS)引腳拉高,從而禁止SPI通訊

#define SPI_FLASH_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4)

如果要需要使用的時候,就直接拉低就行了這樣就可以開始通訊了

控制FLASH的命令:

因為不同的設(shè)備,都會相應(yīng)的有不同的指令,如 EEPROM 中會把第一個數(shù)據(jù)解釋為存儲矩陣的地址(實質(zhì)就是指令)。而 FLASH 則定義了更多的指令,有寫指令,讀指令,讀 ID 指令等等。

這些指令,對主機(jī)來說,只是它遵守最基本的通訊協(xié)議發(fā)送出的數(shù)據(jù)。但設(shè)備把這些數(shù)據(jù)解釋成不同的意義(指令編碼),所以才成為指令。在我們配置好 STM32 的協(xié)議模塊后,想要控制設(shè)備,就要遵守相應(yīng)設(shè)備所定義的命令規(guī)則。

指 令 表 中 的 A0~A23 指 地 址 ; M0~M7 為 器 件 的 制 造 商 ID(MANUFACTURER ID);D0~D7 為數(shù)據(jù)。

讀取FLASH ID:

在命令列表可以了解到讀取設(shè)備 ID 的命令(Device ID)編碼為 ABh、dummy、dummy、dummy。表示此命令由這四個字節(jié)組成,其中dummy意為任意編碼,即這幾個字節(jié)必須發(fā)送數(shù)據(jù),但這些數(shù)據(jù)是任意的,命令列表中帶括號的字節(jié)數(shù)據(jù)表示由FLASH返回給主機(jī)的響應(yīng),可以看到Device ID命令的第5個字節(jié)為從機(jī)返回的響應(yīng),(ID7~ID0),即返回設(shè)備的ID號。

使用DeviceID命令時的時序圖

可以看到主機(jī)首先通過MOSI線(即FLASH的DIO線)發(fā)送第一個字節(jié)為ABh編碼,緊接著三個字節(jié)的dummy編碼,然后FLASH就忽略DIO線上的信號,通過MISO線(即FLASH的DO線)把它的FLASH設(shè)備ID發(fā)送給主機(jī)。

u32 SPI_FLASH_ReadDeviceID(void)

{

u32 Temp = 0;

/使用的時候就拉低/

SPI_FLASH_CS_LOW();

/* Send “RDID ” instruction */

SPI_FLASH_SendByte(W25X_DeviceID);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

SPI_FLASH_SendByte(Dummy_Byte);

/* Read a byte from the FLASH */

Temp = SPI_FLASH_SendByte(Dummy_Byte);

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

return Temp;

}

SPI_FLASH_CS_LOW();

把片選拉低,開始通訊

SPI_FLASH_SendByte(W25X_DeviceID);

向FLASH發(fā)送一個命令字節(jié)編碼:W25X_DeviceID (這里定義的宏為:0XAB)

SPI_FLASH_SendByte(Dummy_Byte);

根據(jù)指令表,發(fā)送完指令后,后面要接著發(fā)送三個字節(jié)的dummy_Byte(這里宏定義為:0xff,設(shè)置為其他也無所謂)

Temp = SPI_FLASH_SendByte(Dummy_Byte);

在前面發(fā)送完三個字節(jié)的 Dummy_Byte后,在第五個字節(jié),F(xiàn)LASH通過DIO端口輸出它的器件ID,所以這里再調(diào)用一次SPI_FLASH_SendByte(Dummy_Byte)接收返回值,賦值給Temp.

SPI_FLASH_CS_HIGH();

把片選拉高,結(jié)束通訊

這樣就完成了讀取FLASH ID,這里有一個相對底層的函數(shù)SPI_FLASH_SendByte(),它實現(xiàn)了利用SPI發(fā)送和接收數(shù)據(jù)的功能

u8 SPI_FLASH_SendByte(u8 byte)

{

/等待發(fā)送數(shù)據(jù)寄存器清空/

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);

/發(fā)送數(shù)據(jù)/

SPI_I2S_SendData(SPI1, byte);

/等待接收數(shù)據(jù)寄存器為非空/

while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);

/返回接收到的數(shù)值/

return SPI_I2S_ReceiveData(SPI1);

}

流程:

1,調(diào)用庫函數(shù) SPI_I2S_GetFlagStatus()等待發(fā)送數(shù)據(jù)寄存器清空;

2,發(fā)送數(shù)據(jù)寄存器準(zhǔn)備好后,調(diào)用庫函數(shù)SPI_I2S_SendData()向從機(jī)發(fā)送數(shù)據(jù);

3,調(diào)用庫函數(shù)SPI_I2S_GetFlagStatus()等待接收數(shù)據(jù)寄存器非空;

4,接收寄存器非空時,調(diào)用SPI_I2S_ReceiveData()獲取接收寄存器中的數(shù)據(jù)并作為函數(shù)的返回值,這個數(shù)據(jù)即由從機(jī)發(fā)送給主機(jī)的數(shù)據(jù);

這是最底層的發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的函數(shù),利用了庫函數(shù)的標(biāo)志檢測確保通訊正常。

讀取廠商ID:

u8 SPI_FLASH_ReadID(void)

{

u32 Temp = 0, Temp0 = 0, Temp1 = 0, Temp2 = 0;

SPI_FLASH_CS_LOW();//拉低開始通訊

SPI_FLASH_SendByte(W25x_JedecDeviceID);

Temp0 = SPI_FLASH_SendByte(Dummy_Byte);

Temp1 = SPI_FLASH_SendByte(Dummy_Byte);

Temp2 = SPI_FLASH_SendByte(Dummy_Byte);

Temp = (Temp0 《《 16) | (Temp1 《《 8) | (Temp2);

SPI_FLASH_CS_HIGH();

return Temp;

123456789101112131415

}

這個函數(shù)和之前的讀取設(shè)備ID流程也是類型的,差別在于發(fā)送一個字節(jié)的命令編碼JEDEC ID(9Fh)之后,從機(jī)就通過D0線返回廠商ID以及0~16位的設(shè)備ID。

讀廠商ID時序圖

擦除FLASH內(nèi)容:

扇區(qū)擦除(根據(jù)FLASH的儲存原理,在寫入數(shù)據(jù)前,要先對存儲區(qū)域進(jìn)行擦除,也叫預(yù)寫)

void SPI_FLASH_SectorErase(u32 SectorAddr)

{

/寫使能并且判斷FLASH狀態(tài)/

SPI_FLASH_WriteEnable();

SPI_FLASH_WaitForWriteEnd();

/*這里開始是FLASH擦除操作*/

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_SectorErase);

/*這里是擦除一個扇區(qū),也就是4KB*/

SPI_FLASH_SendByte((SectorAddr & 0xFF0000) 》》 16);

SPI_FLASH_SendByte((SectorAddr & 0xFF00) 》》 8);

SPI_FLASH_SendByte(SectorAddr & 0xFF);

SPI_FLASH_CS_HIGH();

/*再次判斷FLASH狀態(tài)確??梢詧?zhí)行下一次操作*/

SPI_FLASH_WaitForWriteEnd();

1234567891011121314

}

這是扇區(qū)擦除時序,其中的第一個字節(jié)為扇區(qū)擦除命令編碼(20h),緊跟其后的為要進(jìn)行擦除的,根據(jù)FLASH的說明,整個存儲矩陣分為塊區(qū)和扇區(qū),每塊(Block)的大小為64KB,每個扇區(qū)(Sector)的大小為4KB,對存儲矩陣進(jìn)行擦除時,最小的單位為扇區(qū)。

寫使能:

void SPI_FLASH_WriteEnable(void)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_WriteEnable);

SPI_FLASH_CS_HIGH();

123

}

這里根據(jù)寫使能命令時序,只要發(fā)送命令WriteEnable(06h)就行了。

讀FLASH狀態(tài):

在擦除操作之前,需要調(diào)用SPI_FLASH_WaitForWriteEnd()來確保FLASH不忙碌的時候,才發(fā)送命令或者數(shù)據(jù),通過讀取FLASH的狀態(tài)寄存器來獲知他的工作狀態(tài)。

void SPI_FLASH_WaitForWriteEnd(void)

{

u8 FLASH_Status = 0;

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadStatusReg);

/*一直檢測FLASH狀態(tài)寄存器狀態(tài),直到Bit0位(BUSY位)為0)*/

do

{

FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);

}while((FLASH_Status & WIP_Flag) == SET);

SPI_FLASH_CS_HIGH();

12345678910

}

整個函數(shù)實質(zhì)是不斷的循環(huán)檢測FLASH狀態(tài)寄存器的Busy位,知道FLASH的內(nèi)部寫時序完成,從而確保下一通訊操作正常。主機(jī)通過發(fā)送讀狀態(tài)寄存器命令Read Status Register(05h 編碼),返回的為他的8為狀態(tài)寄存的值。

檢測FLASH的狀態(tài)寄存器的Bit0(BUSY位),當(dāng)FLASH在執(zhí)行內(nèi)部寫時序的時候,除了讀狀態(tài)寄存器命令,其他的一切命令他都會忽略,并且BUSY位保持為1,所以我們需要等待BUSY位為0的時候,再向FLASH發(fā)送其他命令。

向FLASH寫入數(shù)據(jù):

對FLASH寫入數(shù)據(jù),最小單位是256字節(jié),廠商把這個單位曾為頁。寫入時,一般也只有頁寫入的方式,所以為了方便的把一個很長的數(shù)據(jù)寫入到FLASH時,一般需要進(jìn)行轉(zhuǎn)換,把數(shù)據(jù)按頁分好,再寫入到FLASH中(類似于I2C對EEPROM的頁寫入,只是頁的大小不同而已)。

void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

{

u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp= 0;

/這里劃分好數(shù)據(jù)需要寫多少頁,寫地址,寫大小/

Addr = WriteAddr % SPI_FLASH_PageSize;

count = SPI_FLASH_PageSize - Addr;

NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;

NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

if (Addr == 0)

{

if(NumOfPage == 0)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite);

}

else

{

while(NumOfPage--)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageSize);

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

else

{

if(NumOfPage == 0)

{

if(NumOfSingle 》 count)

{

temp = NumOfSingle - count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, temp);

}

else

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumByteToWrite)

}

}

else

{

NumByteToWrite -= count;

NumOfPage = NumByteToWrite / SPI_FLASH_PageSize;

NumOfSingle = NumByteToWrite % SPI_FLASH_PageSize;

SPI_FLASH_PageWrite(pBuffer, WriteAddr, count);

WriteAddr += count;

pBuffer += count;

while (NumOfPage--)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, SPI_FLASH_PageS

WriteAddr += SPI_FLASH_PageSize;

pBuffer += SPI_FLASH_PageSize;

}

if(NumOfSingle != 0)

{

SPI_FLASH_PageWrite(pBuffer, WriteAddr, NumOfSingle);

}

}

}

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556

}

對數(shù)組進(jìn)行分頁后,就調(diào)用SPI_FLASH_PageWrite()對數(shù)據(jù)進(jìn)行按頁寫入(是不是和I2C寫入EEPROM的寫函數(shù)一樣(連行數(shù)都差不多-_-?。?,不了解的話可以去看之前的I2C部分)

底層寫操作:SPI_FLASH_PageWrite()

void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)

{

/寫使能/

SPI_FLASH_WriteEnable();

/拉低開始通訊/

SPI_FLASH_CS_LOW();

/發(fā)送PageProgram(02h))/

SPI_FLASH_SendByte(W25X_PageProgram);

/* Send WriteAddr high nibble address byte to write to */

SPI_FLASH_SendByte((WriteAddr & 0xFF0000) 》》 16);

/* Send WriteAddr medium nibble address byte to write to */

SPI_FLASH_SendByte((WriteAddr & 0xFF00) 》》 8);

/* Send WriteAddr low nibble address byte to write to */

SPI_FLASH_SendByte(WriteAddr & 0xFF);

/這里是判斷寫大小是否符合FLASH規(guī)定的256/

if(NumByteToWrite 》 SPI_FLASH_PerWritePageSize)

{

NumByteToWrite = SPI_FLASH_PerWritePageSize;

//printf(“\n\r Err: SPI_FLASH_PageWrite too large!”);

}

/這里才是寫真是數(shù)據(jù)/

while (NumByteToWrite–)

{

/* Send the current byte */

SPI_FLASH_SendByte(*pBuffer);

/* Point on the next byte to be written */

pBuffer++;

}

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

/* Wait the end of Flash writing */

SPI_FLASH_WaitForWriteEnd();

}

發(fā)送完寫入命令Page Program(編碼 02h)及地址之后,可以連續(xù)寫入最多256個字節(jié)的數(shù)據(jù)(SPI_FLASH_PerWritePageSize = 256),在發(fā)送完數(shù)據(jù)之后,記得調(diào)用SPI_FLASH_WaitForWriteEnd()等待FLASH內(nèi)部寫時序完成再推出函數(shù)。

從FLASH讀取數(shù)據(jù):

對于讀取數(shù)據(jù),發(fā)送一個命令后,可以無限制的一直把整個FLASH的數(shù)據(jù)都讀取完,直到讀取的數(shù)據(jù)量足夠了,就拉高片選信號以表示讀取數(shù)據(jù)結(jié)束。

void SPI_FLASH_BufferRead(u8* pBuffer, u32 ReadAddr, u16 NumByteToRead)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadData);

/* Send ReadAddr high nibble address byte to read from */

SPI_FLASH_SendByte((ReadAddr & 0xFF0000) 》》 16);

/* Send ReadAddr medium nibble address byte to read from */

SPI_FLASH_SendByte((ReadAddr& 0xFF00) 》》 8);

/* Send ReadAddr low nibble address byte to read from */

SPI_FLASH_SendByte(ReadAddr & 0xFF);

while (NumByteToRead–)

{

/* Read a byte from the FLASH */

*pBuffer = SPI_FLASH_SendByte(Dummy_Byte);

/* Point to the next location where the byte read will be saved*/

pBuffer++;

}

/* Deselect the FLASH: Chip Select high */

SPI_FLASH_CS_HIGH();

}

首先發(fā)送一個讀取數(shù)據(jù)命令 Read Data(03h),接著發(fā)送24位讀數(shù)據(jù)起始地址,STM32再通過D0線接收數(shù)據(jù),并使用指針的方式記錄起來


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • FlaSh
    +關(guān)注

    關(guān)注

    10

    文章

    1598

    瀏覽量

    147338
  • SPI
    SPI
    +關(guān)注

    關(guān)注

    17

    文章

    1669

    瀏覽量

    90733
收藏 人收藏

    評論

    相關(guān)推薦

    spi_flash期間的計時器中斷導(dǎo)致崩潰怎么解決?

    spi_flash_erase_sector(...); spi_flash_write(...); 如果在閃存訪問期間發(fā)生計時器中斷,ESP 似乎會崩潰并重新啟動。 當(dāng)然,這可以通過在訪問 Flash 時禁用中斷來
    發(fā)表于 07-12 11:54

    【GD32F470紫藤派開發(fā)板使用手冊】第十一講 SPI-SPI NOR FLASH讀寫實驗

    通過本實驗主要學(xué)習(xí)以下內(nèi)容: ?SPI簡介 ?GD32F470 SPI簡介 ?SPI NOR FLASH——GD25Q32ESIGR簡介 ?使用GD32F470
    的頭像 發(fā)表于 05-17 09:57 ?1357次閱讀
    【GD32F470紫藤派開發(fā)板使用手冊】第十一講 <b class='flag-5'>SPI-SPI</b> NOR <b class='flag-5'>FLASH</b>讀寫實驗

    FATFS對SPI_FLASH新建文件、刪除文件或者修改文件后電腦無法識別,為什么?

    FATFS對SPI_FLASH新建文件、刪除文件或者修改文件后電腦無法識別,而且會斷開連接,需要重新插拔,這是什么問題呢
    發(fā)表于 04-09 07:06

    STM32F407ZGT6 spi flash片選引腳無法被拉低的原因?怎么解決?

    我用的芯片是STM32F407ZGT6,RTThread版本5.0.2,spi flash掛載在spi1總線,設(shè)備號是spi10 int
    發(fā)表于 02-20 07:13

    stm32 flash寫數(shù)據(jù)怎么存儲的

    stm32 flash寫數(shù)據(jù)怎么存儲的? STM32是一款廣泛應(yīng)用于嵌入式系統(tǒng)開發(fā)的微控制器,它的Flash存儲器是其中一個重要的組成部分。在本文中,我將詳細(xì)介紹
    的頭像 發(fā)表于 01-31 15:46 ?1982次閱讀

    數(shù)字隔離芯片電平轉(zhuǎn)換應(yīng)用

    數(shù)字隔離芯片電平轉(zhuǎn)換應(yīng)用
    的頭像 發(fā)表于 12-05 11:09 ?2144次閱讀
    數(shù)字隔離芯片<b class='flag-5'>之</b>電平轉(zhuǎn)換應(yīng)用

    ART-Pi外部SPI Flash使用elmfatfs文件系統(tǒng)流程記錄

    ARTPI 有一個外部 SPI Flash,空間為 16-Mbytes,使用的是 ARTPI 的 SPI1 接口。
    的頭像 發(fā)表于 11-21 11:39 ?1155次閱讀
    ART-Pi外部<b class='flag-5'>SPI</b> <b class='flag-5'>Flash</b>使用elmfatfs文件系統(tǒng)流程記錄

    STM32CubeIDE實用技巧配置Heap空間

    STM32CubeIDE實用技巧配置Heap空間
    的頭像 發(fā)表于 10-31 17:30 ?979次閱讀
    <b class='flag-5'>STM32</b>CubeIDE實用技巧<b class='flag-5'>之</b>配置Heap空間

    STM32Flash寫了保護(hù)怎么辦?STM32如何設(shè)置讀保護(hù)和解除讀保護(hù)?

    STM32Flash寫了保護(hù)怎么辦?STM32如何設(shè)置讀保護(hù)和解除讀保護(hù)? 一、STM32Flash寫了保護(hù)怎么辦? 當(dāng)
    的頭像 發(fā)表于 10-29 17:24 ?1.1w次閱讀

    講講STM32單片機(jī)Flash的讀保護(hù)和寫保護(hù)

    講講STM32單片機(jī)Flash的讀保護(hù)和寫保護(hù)
    的頭像 發(fā)表于 10-26 15:52 ?7417次閱讀
    講講<b class='flag-5'>STM32</b>單片機(jī)<b class='flag-5'>Flash</b>的讀保護(hù)和寫保護(hù)

    為什么STM32Flash地址要設(shè)置到0x08000000?

    為什么STM32Flash地址要設(shè)置到0x08000000?
    的頭像 發(fā)表于 10-26 15:50 ?1404次閱讀
    為什么<b class='flag-5'>STM32</b>的<b class='flag-5'>Flash</b>地址要設(shè)置到0x08000000?

    STM32實例-待機(jī)喚醒實驗

    STM32實例-待機(jī)喚醒實驗
    的頭像 發(fā)表于 10-26 15:48 ?1627次閱讀
    <b class='flag-5'>STM32</b><b class='flag-5'>實例</b>-待機(jī)喚醒實驗

    巧用 STM32CubeIDE 編譯警告

    巧用 STM32CubeIDE 編譯警告
    的頭像 發(fā)表于 10-25 16:33 ?834次閱讀
    巧用 <b class='flag-5'>STM32</b>CubeIDE <b class='flag-5'>之</b>編譯警告

    淺談STM32SD卡

    STM32SD卡
    的頭像 發(fā)表于 10-19 18:28 ?1489次閱讀
    <b class='flag-5'>淺談</b><b class='flag-5'>STM32</b><b class='flag-5'>之</b>SD卡

    MCU加密

    MCU加密
    的頭像 發(fā)表于 09-26 17:01 ?818次閱讀
    MCU<b class='flag-5'>之</b>加密