RM新时代网站-首页

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

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

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

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

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

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

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

mian函數(shù):

1#define sFLASH_ID 0xEF3015(前面加個(gè)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 檢測(cè)到 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)測(cè)試成功!\n\r”);

}

else

{

printf(“\r\n 2M 串行 flash(W25X16)測(cè)試失?。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()對(duì)寫入和讀取的數(shù)據(jù)進(jìn)行匹配,匹配成功則把標(biāo)志變量 TransferStatus1賦值為 PASSED(自定義的枚舉變量);

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

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

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

PS:

讀取器件ID信息可以知道設(shè)備與主機(jī)是否能夠正常工作,也便于區(qū)分不同的器件,可以在使用的FLASH用戶數(shù)據(jù)手冊(cè)找到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個(gè)引腳都設(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(); //不用的時(shí)候就拉高

/這里是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ù)手冊(cè)》以及《STM32參考手冊(cè)》,把PA5(SCK),PA6(MISO),PA7(MOSI)設(shè)置成復(fù)用推挽輸出,因?yàn)镻A4(NSS)是使用軟件模式,所以設(shè)置為通用退完輸出。

SPI模式初始化:

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

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),這兩個(gè)模式最大的區(qū)別就是SPI的SCK信號(hào)線時(shí)序,SCK的時(shí)序是由通訊中的主機(jī)產(chǎn)生的,如果配置成從機(jī)模式,STM32的SPI模塊將接收外來(lái)的SCK信號(hào)。(這里STM32作為SPI通訊主機(jī),所以設(shè)置成 SPI_Mode_Master)。

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;

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

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

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

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

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

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

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

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

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

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;

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

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

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;

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

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;

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

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

SPI_InitStructure.SPI_CRCPolynomial = 3;

這里是設(shè)置SPI的CEC校驗(yàn)的多項(xiàng)式,如果使用到CRC校驗(yàn)時(shí),就是用這個(gè)成員的參數(shù)(多項(xiàng)式),來(lái)計(jì)算CRC的值。(這里的FLASH不支持CRC校驗(yàn),所以賦值為3其實(shí)沒(méi)意義)

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

PS:

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

#define SPI_FLASH_CS_HIGH() GPIO_SetBits(GPIOA, GPIO_Pin_4)

實(shí)際上這個(gè)宏就是用來(lái)把 PA4(NSS)引腳拉高,從而禁止SPI通訊

#define SPI_FLASH_CS_LOW() GPIO_ResetBits(GPIOA, GPIO_Pin_4)

如果要需要使用的時(shí)候,就直接拉低就行了這樣就可以開(kāi)始通訊了

控制FLASH的命令:

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

這些指令,對(duì)主機(jī)來(lái)說(shuō),只是它遵守最基本的通訊協(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。表示此命令由這四個(gè)字節(jié)組成,其中dummy意為任意編碼,即這幾個(gè)字節(jié)必須發(fā)送數(shù)據(jù),但這些數(shù)據(jù)是任意的,命令列表中帶括號(hào)的字節(jié)數(shù)據(jù)表示由FLASH返回給主機(jī)的響應(yīng),可以看到Device ID命令的第5個(gè)字節(jié)為從機(jī)返回的響應(yīng),(ID7~ID0),即返回設(shè)備的ID號(hào)。

使用DeviceID命令時(shí)的時(shí)序圖

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

u32 SPI_FLASH_ReadDeviceID(void)

{

u32 Temp = 0;

/使用的時(shí)候就拉低/

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();

把片選拉低,開(kāi)始通訊

SPI_FLASH_SendByte(W25X_DeviceID);

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

SPI_FLASH_SendByte(Dummy_Byte);

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

Temp = SPI_FLASH_SendByte(Dummy_Byte);

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

SPI_FLASH_CS_HIGH();

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

這樣就完成了讀取FLASH ID,這里有一個(gè)相對(duì)底層的函數(shù)SPI_FLASH_SendByte(),它實(shí)現(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)用庫(kù)函數(shù) SPI_I2S_GetFlagStatus()等待發(fā)送數(shù)據(jù)寄存器清空;

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

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

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

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

讀取廠商ID:

u8 SPI_FLASH_ReadID(void)

{

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

SPI_FLASH_CS_LOW();//拉低開(kāi)始通訊

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

}

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

讀廠商ID時(shí)序圖

擦除FLASH內(nèi)容:

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

void SPI_FLASH_SectorErase(u32 SectorAddr)

{

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

SPI_FLASH_WriteEnable();

SPI_FLASH_WaitForWriteEnd();

/*這里開(kāi)始是FLASH擦除操作*/

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_SectorErase);

/*這里是擦除一個(gè)扇區(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ū)擦除時(shí)序,其中的第一個(gè)字節(jié)為扇區(qū)擦除命令編碼(20h),緊跟其后的為要進(jìn)行擦除的,根據(jù)FLASH的說(shuō)明,整個(gè)存儲(chǔ)矩陣分為塊區(qū)和扇區(qū),每塊(Block)的大小為64KB,每個(gè)扇區(qū)(Sector)的大小為4KB,對(duì)存儲(chǔ)矩陣進(jìn)行擦除時(shí),最小的單位為扇區(qū)。

寫使能:

void SPI_FLASH_WriteEnable(void)

{

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_WriteEnable);

SPI_FLASH_CS_HIGH();

123

}

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

讀FLASH狀態(tài):

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

void SPI_FLASH_WaitForWriteEnd(void)

{

u8 FLASH_Status = 0;

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadStatusReg);

/*一直檢測(cè)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

}

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

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

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

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

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

{

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

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

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

}

對(duì)數(shù)組進(jìn)行分頁(yè)后,就調(diào)用SPI_FLASH_PageWrite()對(duì)數(shù)據(jù)進(jìn)行按頁(yè)寫入(是不是和I2C寫入EEPROM的寫函數(shù)一樣(連行數(shù)都差不多-_-?。涣私獾脑捒梢匀タ粗暗腎2C部分)

底層寫操作:SPI_FLASH_PageWrite()

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

{

/寫使能/

SPI_FLASH_WriteEnable();

/拉低開(kāi)始通訊/

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個(gè)字節(jié)的數(shù)據(jù)(SPI_FLASH_PerWritePageSize = 256),在發(fā)送完數(shù)據(jù)之后,記得調(diào)用SPI_FLASH_WaitForWriteEnd()等待FLASH內(nèi)部寫時(shí)序完成再推出函數(shù)。

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

對(duì)于讀取數(shù)據(jù),發(fā)送一個(gè)命令后,可以無(wú)限制的一直把整個(gè)FLASH的數(shù)據(jù)都讀取完,直到讀取的數(shù)據(jù)量足夠了,就拉高片選信號(hào)以表示讀取數(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ā)送一個(gè)讀取數(shù)據(jù)命令 Read Data(03h),接著發(fā)送24位讀數(shù)據(jù)起始地址,STM32再通過(guò)D0線接收數(shù)據(jù),并使用指針的方式記錄起來(lái)


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

    關(guān)注

    10

    文章

    1633

    瀏覽量

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

    關(guān)注

    17

    文章

    1706

    瀏覽量

    91502
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    【S32K146 RT-Thread】 使用SFUD組件驅(qū)動(dòng)spi flash

    【S32K146 RT-Thread】 使用SFUD組件驅(qū)動(dòng)spi flash
    的頭像 發(fā)表于 11-21 01:05 ?261次閱讀
    【S32K146 RT-Thread】<b class='flag-5'>之</b> 使用SFUD組件驅(qū)動(dòng)<b class='flag-5'>spi</b> <b class='flag-5'>flash</b>

    Air201模組教程:SPI通信與外部Flash的連接之道!

    今天我們要學(xué)習(xí)的是Air201定位模組LuatOS快速入門,本文將通過(guò)Air201+擴(kuò)展板讀寫外部flash的演示,教你使用SPI示例,可根據(jù)實(shí)際需求靈活應(yīng)用。
    的頭像 發(fā)表于 11-18 00:23 ?219次閱讀
    Air201模組教程:<b class='flag-5'>SPI</b>通信與外部<b class='flag-5'>Flash</b>的連接之道!

    LuatOS入門課|Air201模組與SPI Flash的交互藝術(shù)

    今天我會(huì)把我了解的關(guān)于如何使用SPI讀寫外部Flash教給大家,本文將通過(guò)Air201+擴(kuò)展板讀寫外部flash的演示,教你使用SPI示例,可根據(jù)實(shí)際需求靈活應(yīng)用。
    的頭像 發(fā)表于 11-17 09:51 ?253次閱讀
    LuatOS入門課|Air201模組與<b class='flag-5'>SPI</b> <b class='flag-5'>Flash</b>的交互藝術(shù)

    瀚海微SD NANDSD 協(xié)議(36)SPI模式

    簡(jiǎn)介 SPI模式由基于flash的SD存儲(chǔ)卡提供的輔助通信協(xié)議組成。 這種模式是SD存儲(chǔ)卡協(xié)議的一個(gè)子集,設(shè)計(jì)用于與SPI通道通信,通常在摩托羅拉(以及最近一些其他供應(yīng)商)的微控制器中發(fā)現(xiàn)。 接口
    的頭像 發(fā)表于 10-08 10:13 ?292次閱讀
    瀚海微SD NAND<b class='flag-5'>之</b>SD 協(xié)議(36)<b class='flag-5'>SPI</b>模式

    物聯(lián)網(wǎng)行業(yè)存儲(chǔ)方案詳解_SPI NOR Flash

    物聯(lián)網(wǎng)系統(tǒng)中為什么要使用SPI NOR FLASH 物聯(lián)網(wǎng)系統(tǒng)中使用SPI NOR FLASH的原因主要基于其獨(dú)特的性能特點(diǎn)和在嵌入式系統(tǒng)中的廣泛應(yīng)用。以下是詳細(xì)的分析: 1、高可靠性
    的頭像 發(fā)表于 09-24 14:39 ?276次閱讀
    物聯(lián)網(wǎng)行業(yè)存儲(chǔ)方案詳解_<b class='flag-5'>SPI</b> NOR <b class='flag-5'>Flash</b>

    開(kāi)發(fā)攻略STM32調(diào)試端口的映射

    最近有一個(gè)項(xiàng)目用到了STM32F103RB系列單片機(jī),由于引腳數(shù)量較少,不得不使用到了單片機(jī)的PB3和PB4兩個(gè)引腳。而這兩個(gè)引腳剛好又是STM32系列的JTAG調(diào)試引腳,如果要用于普通IO的功能
    的頭像 發(fā)表于 08-03 08:11 ?428次閱讀
    開(kāi)發(fā)攻略<b class='flag-5'>之</b><b class='flag-5'>STM32</b>調(diào)試端口的映射

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

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

    【GD32F303紅楓派開(kāi)發(fā)板使用手冊(cè)】第二十講 SPI-SPI NAND FLASH讀寫實(shí)驗(yàn)

    通過(guò)本實(shí)驗(yàn)主要學(xué)習(xí)以下內(nèi)容: ?SPI通信協(xié)議,參考19.2.1東方紅開(kāi)發(fā)板使用手冊(cè) ?GD32F303 SPI操作方式,參考19.2.2東方紅開(kāi)發(fā)板使用手冊(cè) ?NAND FLASH基本原理 ?
    的頭像 發(fā)表于 06-20 09:50 ?884次閱讀
    【GD32F303紅楓派開(kāi)發(fā)板使用手冊(cè)】第二十講 <b class='flag-5'>SPI-SPI</b> NAND <b class='flag-5'>FLASH</b>讀寫實(shí)驗(yàn)

    【GD32F470紫藤派開(kāi)發(fā)板使用手冊(cè)】第十一講 SPI-SPI NOR FLASH讀寫實(shí)驗(yàn)

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

    STM32F207擦除片內(nèi)FLASH,退出DEBUG無(wú)法執(zhí)行下一步程序是怎么回事?

    由于項(xiàng)目需求,需要擦除片內(nèi)指定空間,然后從SPI_FLASH中加載程序運(yùn)行 問(wèn)題如下:: 在DEBUG模式下,執(zhí)行擦除程序后,則退出DEBUG,無(wú)法執(zhí)行下一步程序 擦除代碼如下: 1
    發(fā)表于 04-23 07:46

    數(shù)位相框電源模組設(shè)計(jì)

    電子發(fā)燒友網(wǎng)站提供《數(shù)位相框電源模組設(shè)計(jì).pdf》資料免費(fèi)下載
    發(fā)表于 04-17 14:46 ?0次下載

    FATFS對(duì)SPI_FLASH新建文件、刪除文件或者修改文件后電腦無(wú)法識(shí)別,為什么?

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

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

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

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

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

    stm32f103 flash模擬eeprom

    ,STM32F103的Flash存儲(chǔ)器可以模擬EEPROM的功能,在本文中我們將詳細(xì)介紹如何使用STM32F103的Flash存儲(chǔ)器來(lái)實(shí)現(xiàn)EEPROM。 概述 EEPROM(Elect
    的頭像 發(fā)表于 01-09 11:21 ?2027次閱讀
    RM新时代网站-首页