1. 程式人生 > >STM32之SPI_FLASH(例項)

STM32之SPI_FLASH(例項)

本例項用的是STM32F103VET6平臺,它有3個SPI介面(這裡使用SPI1),各訊號線連線到FLASH(型號:W25X16)的CS,CLK,DO,DIO線,以實現SPI通訊,對FLASH進行讀寫.
(這裡採用主模式,全雙工通訊,通過查詢傳送資料暫存器和接收資料暫存器狀態確保通訊正常)
這裡寫圖片描述

mian函式:
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",FlashID,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 寫入的資料為:%s \r\t", Tx_Buffer);

    SPI_FLASH_BufferRead(Rx_Buffer, FLASH_ReadAddress, BufferSize);
    printf("\r\n 讀出的資料為:%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);

}
mian函式的流程:
1,呼叫 USART1_Config() 初始化串列埠;
2,呼叫 SPI_FLASH_Init() 初始化SPI模組;
3,呼叫 SPI_FLASH_ReadDeviceID 讀取FLASH器件生產廠商的ID資訊;
4,呼叫 SPI_FLASH_ReadID 讀取FLASH器件的裝置ID資訊;
5,如果讀取ID正確,則呼叫 SPI_FLASH_SectorErase()把FLASH內容擦除,擦除後呼叫 SPI_FLASH_BufferWrite()向FLASH寫入資料,然後再呼叫 SPI_FLASH_BufferRead()從剛剛寫入的地址中讀出資料,最後呼叫 Buffercmp()對寫入和讀取的資料進行匹配,匹配成功則把標誌變數 TransferStatus1賦值為 PASSED(自定義的列舉變數);
6,根據標誌量 TransferStatus1判斷FLASH資料的:擦除,寫入,讀取是否正常,分情況輸出到終端;
7,如果讀取FLASH的ID資訊錯誤,則直接向終端輸出檢測不到FLASH資訊;
8,最後呼叫 SPI_Flash_PowerDown()函式關閉 FLASH裝置的電源(因為資料寫入到FLASH後並不會因斷電而丟失,所以需要使用的時候再開啟FLASH電源);
PS:


這裡寫圖片描述
這裡寫圖片描述
讀取器件ID資訊可以知道裝置與主機是否能夠正常工作,也便於區分不同的器件,可以在使用的FLASH使用者資料手冊找到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個引腳都設定好

/
/!< 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設定部分/
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初始化:
根據《STM32資料手冊》以及《STM32參考手冊》,把PA5(SCK),PA6(MISO),PA7(MOSI)設定成複用推輓輸出,因為PA4(NSS)是使用軟體模式,所以設定為通用退完輸出.
這裡寫圖片描述

SPI模式初始化:
對於初始化,是需要根據通訊的裝置FLASH的SPI特性來決定的,下面成員分析:
SPI_InitStructure.SPI_Direction= SPI_Direction_2Lines_FullDuplex;
這裡設定通訊模式,這裡設定成全雙工模式(可以在keil環境下查詢其他模式)

SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
這裡是設定工作模式,STM32的SPI裝置可以gon工作在主機模式(SPI_Mode_Master)或從機模式(SPI_Mode_Slave),這兩個模式最大的區別就是SPI的SCK訊號線時序,SCK的時序是由通訊中的主機產生的,如果配置成從機模式,STM32的SPI模組將接收外來的SCK訊號.(這裡STM32作為SPI通訊主機,所以設定成 SPI_Mode_Master).

SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
這個是設定SPI每次通訊的資料大小(稱為資料幀)為8位還是16位(從FLASH的資料手冊可以查到,這裡的FLASH的資料幀大小為8為,所以要把STM32的SPI模組設定相同的)

SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;&SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
這兩個成員是配置SPI的時鐘極性(CPOL)和時鐘相位(CPHA),這兩個配置影響到SPI的通訊模式,要設定成符合將要互相通訊的裝置的要求.
CPOL:可以取 SPI_CPOL_High(SPI 通訊空閒時 SCK 為高電平)或者SPI_CPOL_Low(SPI 通訊空閒時 SCK 為低電平);
CPHA:可以取 SPI_CPHA_1Edge(在 SCK 的奇數邊沿採集資料)或者SPI_CPHA_2Edge (在 SCK 的偶數邊沿採集資料);

這裡寫圖片描述
查詢這個FLASH的使用手冊,可以瞭解到這個FLASH支援以SPI的模式0和模式3通訊.
模式0:在SPI空閒時,SCK為低電平,奇數邊沿取樣;
模式3:在SPI空閒時,SCK為高電平,偶數變異采樣;
所以這裡配置成模式3,把CPOL賦值為SPI_CPOL_High(SPI空閒時SCK為高電平),把CPHA賦值為SPI_CPHA_2Edge(在SCK的偶數邊沿超級資料)

SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
這裡是配置NSS引腳的使用模式,可以選擇為硬體模式(SPI_NSS_Hard)與軟體模式(SPI_NSS_Soft),在硬體模式中的SPI片選由硬體自動產生,而軟體模式則需要手動把相應的FPIO埠拉高或拉低產生非片選和片選訊號(如果外界條件允許,硬體模式還會自動將STM32的SPI設定為主機)
這裡是由軟體產生模式,所以賦值為SPI_NSS_Soft.

SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
這裡是設定波特率分頻值,分頻後的時鐘為SPI的SCK訊號線的時鐘頻率,這個成員可以設定為fpclk的2,4,6,8,32,64,128,256分頻.這裡設定為4分頻

SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
所有序列的通訊協議都會有MSB先行(高位資料在前)還是LSB先行(地位資料在前)的問題,STM32的SPI模組可以通過對這個結構體成員,對這個特性程式設計控制.
根據FLASH的通訊時序,這裡設定為MSB先行(SPI_FirstBit_MSB)

SPI_InitStructure.SPI_CRCPolynomial = 3;
這裡是設定SPI的CEC校驗的多項式,如果使用到CRC校驗時,就是用這個成員的引數(多項式),來計算CRC的值.(這裡的FLASH不支援CRC校驗,所以賦值為3其實沒意義)

配置完這些結構體成員後,呼叫 SPI_Init()把這些引數寫入到暫存器中,然後呼叫SPI_Cmd()使能SPI1外設。
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的命令:
因為不同的裝置,都會相應的有不同的指令,如 EEPROM 中會把第一個資料解釋為儲存矩陣的地址(實質就是指令)。而 FLASH 則定義了更多的指令,有寫指令,讀指令,讀 ID 指令等等。
這些指令,對主機來說,只是它遵守最基本的通訊協議傳送出的資料。但裝置把這些資料解釋成不同的意義(指令編碼),所以才成為指令。在我們配置好 STM32 的協議模組後,想要控制裝置,就要遵守相應裝置所定義的命令規則。
這裡寫圖片描述
這裡寫圖片描述

指 令 表 中 的 A0~A23 指 地 址 ; M0~M7 為 器 件 的 制 造 商 ID(MANUFACTURER ID);D0~D7 為資料。

讀取FLASH ID:
在命令列表可以瞭解到讀取裝置 ID 的命令(Device ID)編碼為 ABh、dummy、dummy、dummy。表示此命令由這四個位元組組成,其中dummy意為任意編碼,即這幾個位元組必須傳送資料,但這些資料是任意的,命令列表中帶括號的位元組資料表示由FLASH返回給主機的響應,可以看到Device ID命令的第5個位元組為從機返回的響應,(ID7~ID0),即返回裝置的ID號.

這裡寫圖片描述
使用DeviceID命令時的時序圖
可以看到主機首先通過MOSI線(即FLASH的DIO線)傳送第一個位元組為ABh編碼,緊接著三個位元組的dummy編碼,然後FLASH就忽略DIO線上的訊號,通過MISO線(即FLASH的DO線)把它的FLASH裝置ID傳送給主機.

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傳送一個命令位元組編碼:W25X_DeviceID (這裡定義的巨集為:0XAB)

SPI_FLASH_SendByte(Dummy_Byte);
根據指令表,傳送完指令後,後面要接著傳送三個位元組的dummy_Byte(這裡巨集定義為:0xff,設定為其他也無所謂)

Temp = SPI_FLASH_SendByte(Dummy_Byte);
在前面傳送完三個位元組的 Dummy_Byte後,在第五個位元組,FLASH通過DIO埠輸出它的器件ID,所以這裡再呼叫一次SPI_FLASH_SendByte(Dummy_Byte)接收返回值,賦值給Temp.

SPI_FLASH_CS_HIGH();
把片選拉高,結束通訊

這樣就完成了讀取FLASH ID,這裡有一個相對底層的函式SPI_FLASH_SendByte(),它實現了利用SPI傳送和接收資料的功能
u8 SPI_FLASH_SendByte(u8 byte)
{
/等待發送資料暫存器清空/
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET);
/傳送資料/
SPI_I2S_SendData(SPI1, byte);
/等待接收資料暫存器為非空/
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET);
/返回接收到的數值/
return SPI_I2S_ReceiveData(SPI1);
}
流程:
1,呼叫庫函式 SPI_I2S_GetFlagStatus()等待發送資料暫存器清空;
2,傳送資料暫存器準備好後,呼叫庫函式SPI_I2S_SendData()向從機發送資料;
3,呼叫庫函式SPI_I2S_GetFlagStatus()等待接收資料暫存器非空;
4,接收暫存器非空時,呼叫SPI_I2S_ReceiveData()獲取接收暫存器中的資料並作為函式的返回值,這個資料即由從機發送給主機的資料;
這是最底層的傳送資料和接收資料的函式,利用了庫函式的標誌檢測確保通訊正常。

讀取廠商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;    

}
這個函式和之前的讀取裝置ID流程也是型別的,差別在於傳送一個位元組的命令編碼JEDEC ID(9Fh)之後,從機就通過D0線返回廠商ID以及0~16位的裝置ID。

這裡寫圖片描述
讀廠商ID時序圖

擦除FLASH內容:
扇區擦除(根據FLASH的儲存原理,在寫入資料前,要先對儲存區域進行擦除,也叫預寫)

void SPI_FLASH_SectorErase(u32 SectorAddr)
{
/寫使能並且判斷FLASH狀態/
SPI_FLASH_WriteEnable();
SPI_FLASH_WaitForWriteEnd();

/*這裡開始是FLASH擦除操作*/
SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_SectorErase);
/*這裡是擦除一個扇區,也就是4KB*/
SPI_FLASH_SendByte((SectorAddr & 0xFF0000) >> 16);

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

SPI_FLASH_SendByte(SectorAddr & 0xFF);

SPI_FLASH_CS_HIGH();
/*再次判斷FLASH狀態確保可以執行下一次操作*/
SPI_FLASH_WaitForWriteEnd();    

}
這裡寫圖片描述
這是扇區擦除時序,其中的第一個位元組為扇區擦除命令編碼(20h),緊跟其後的為要進行擦除的,根據FLASH的說明,整個儲存矩陣分為塊區和扇區,每塊(Block)的大小為64KB,每個扇區(Sector)的大小為4KB,對儲存矩陣進行擦除時,最小的單位為扇區.

寫使能:
void SPI_FLASH_WriteEnable(void)
{
SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_WriteEnable);

SPI_FLASH_CS_HIGH();

}

這裡寫圖片描述
這裡根據寫使能命令時序,只要傳送命令WriteEnable(06h)就行了。

讀FLASH狀態:
在擦除操作之前,需要呼叫SPI_FLASH_WaitForWriteEnd()來確保FLASH不忙碌的時候,才傳送命令或者資料,通過讀取FLASH的狀態暫存器來獲知他的工作狀態.

void SPI_FLASH_WaitForWriteEnd(void)
{
u8 FLASH_Status = 0;

SPI_FLASH_CS_LOW();

SPI_FLASH_SendByte(W25X_ReadStatusReg);
/*一直檢測FLASH狀態暫存器狀態,直到Bit0位(BUSY位)為0)*/
do
{
    FLASH_Status = SPI_FLASH_SendByte(Dummy_Byte);
}while((FLASH_Status & WIP_Flag) == SET);

SPI_FLASH_CS_HIGH();

}

這裡寫圖片描述
整個函式實質是不斷的迴圈檢測FLASH狀態暫存器的Busy位,知道FLASH的內部寫時序完成,從而確保下一通訊操作正常.主機通過傳送讀狀態暫存器命令Read Status Register(05h 編碼),返回的為他的8為狀態寄存的值.

這裡寫圖片描述
檢測FLASH的狀態暫存器的Bit0(BUSY位),當FLASH在執行內部寫時序的時候,除了讀狀態暫存器命令,其他的一切命令他都會忽略,並且BUSY位保持為1,所以我們需要等待BUSY位為0的時候,再向FLASH傳送其他命令.

向FLASH寫入資料:
對FLASH寫入資料,最小單位是256位元組,廠商把這個單位曾為頁.寫入時,一般也只有頁寫入的方式,所以為了方便的把一個很長的資料寫入到FLASH時,一般需要進行轉換,把資料按頁分好,再寫入到FLASH中(類似於I2C對EEPROM的頁寫入,只是頁的大小不同而已).

void SPI_FLASH_BufferWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0, temp= 0;
/這裡劃分好資料需要寫多少頁,寫地址,寫大小/
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);
        }
    }
}

}
對陣列進行分頁後,就呼叫SPI_FLASH_PageWrite()對資料進行按頁寫入(是不是和I2C寫入EEPROM的寫函式一樣(連行數都差不多-_-!),不瞭解的話可以去看之前的I2C部分)

底層寫操作:SPI_FLASH_PageWrite()
void SPI_FLASH_PageWrite(u8* pBuffer, u32 WriteAddr, u16 NumByteToWrite)
{
/寫使能/
SPI_FLASH_WriteEnable();
/拉低開始通訊/
SPI_FLASH_CS_LOW();
/傳送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規定的256/
if(NumByteToWrite > SPI_FLASH_PerWritePageSize)
{
NumByteToWrite = SPI_FLASH_PerWritePageSize;
//printf(“\n\r Err: SPI_FLASH_PageWrite too large!”);
}
/這裡才是寫真是資料/
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();
}

這裡寫圖片描述
傳送完寫入命令Page Program(編碼 02h)及地址之後,可以連續寫入最多256個位元組的資料(SPI_FLASH_PerWritePageSize = 256),在傳送完資料之後,記得呼叫SPI_FLASH_WaitForWriteEnd()等待FLASH內部寫時序完成再推出函式.

從FLASH讀取資料:
對於讀取資料,傳送一個命令後,可以無限制的一直把整個FLASH的資料都讀取完,直到讀取的資料量足夠了,就拉高片選訊號以表示讀取資料結束.

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

這裡寫圖片描述
首先發送一個讀取資料命令 Read Data(03h),接著傳送24位讀資料起始地址,STM32再通過D0線接收資料,並使用指標的方式記錄起來.

PS:
通過以上的函式,就可以實現對FLASH的擦除,寫入和讀取操作.STM32與SPI-FLASH通訊步驟:
1,配置I/O埠,使能GPIO;
2,根據將要進行通訊器件的SPI模式配置STM32的SPI,使能SPI時鐘
3,配置好SPI後,根據各種FLASH定義的命令控制,進行讀寫操作(注意:在寫操作之前要先進行儲存扇區的擦除操作,擦除操作前要先發出寫使能命令!!)