1. 程式人生 > >SPI專題(二)——STM32驅動FLASH(W25Q64)

SPI專題(二)——STM32驅動FLASH(W25Q64)

1.硬體連線

W25Q64 將 8M 的容量分為 128 個塊(Block),每個塊大小為 64K 位元組,每個塊又分為 16個扇區(Sector),每個扇區 4K 個位元組。 W25Q64 的最少擦除單位為一個扇區,也就是每次必須擦除 4K 個位元組。操作需要給 W25Q64 開闢一個至少 4K 的快取區,對 SRAM 要求比較高,要求晶片必須有 4K 以上 SRAM 才能很好的操作。

這裡寫圖片描述

W25Q64 的擦寫週期多達 10W 次,具有 20 年的資料儲存期限,支援電壓為 2.7~3.6V,W25Q64 支援標準的 SPI,還支援雙輸出/四輸出的 SPI,最大 SPI 時鐘可以到 80Mhz(雙輸出時相當於 160Mhz,四輸出時相當於 320M)。

1.1 硬體連線

與 STM32 的引腳連線如下:這裡是使用SPI1配置。

這裡寫圖片描述

STM32引腳 對應SPI功能
PA2 片選CS
PA5 時鐘SCK
PA6 MISO
PA7 MOSI

STM32 的 SPI 功能很強大, SPI 時鐘最多可以到 18Mhz,支援 DMA,可以配置為 SPI 協議或者 I2S 協議(僅大容量型號支援)。

1.2 SPI通訊的通訊時序

SPI協議定義了通訊的起始和停止訊號、資料有效性、時鐘同步等環節。

這裡寫圖片描述

這是一個主機的通訊時序。NSS、SCK、MOSI 訊號都由主機控制產生,而 MISO 的訊號由從機產生,主機通過該訊號線讀取從機的資料。MOSI 與 MISO 的訊號只在 NSS 為低電平的時候才有效,在 SCK的每個時鐘週期 MOSI和 MISO傳輸一位資料。

1.通訊的起始和停止訊號

在圖中的標號1處,NSS 訊號線由高變低,是 SPI 通訊的起始訊號。NSS 是每個從機各自獨佔的訊號線,當從機在自己的 NSS 線檢測到起始訊號後,就知道自己被主機選中了,開始準備與主機通訊。在圖中的標號6處,NSS 訊號由低變高,是 SPI 通訊的停止訊號,表示本次通訊結束,從機的選中狀態被取消。

2.資料有效性

SPI使用 MOSI及 MISO訊號線來傳輸資料,使用 SCK訊號線進行資料同步。MOSI及MISO 資料線在 SCK 的每個時鐘週期傳輸一位資料,且資料輸入輸出是同時進行的。資料傳輸時,MSB 先行或 LSB 先行並沒有作硬性規定,但要保證兩個 SPI通訊裝置之間使用同樣的協定,一般都會採用圖中的 MSB先行模式。

觀察圖中的2345標號處,MOSI及 MISO的資料在 SCK的上升沿期間變化輸出,在SCK 的下降沿時被取樣。即在 SCK 的下降沿時刻,MOSI 及 MISO 的資料有效,高電平時表示資料“1”,為低電平時表示資料“0”。在其它時刻,資料無效,MOSI及MISO為下一次表示資料做準備。SPI每次資料傳輸可以 8 位或 16 位為單位,每次傳輸的單位數不受限制。

1.3 STM32 SPI外設

STM32 的 SPI 外設可用作通訊的主機及從機,支援最高的 SCK 時鐘頻率為 f pclk /2(STM32F103型號的晶片預設 f pclk1 為 72MHz,f pclk2 為 36MHz),完全支援 SPI協議的 4種模式,資料幀長度可設定為 8 位或 16 位,可設定資料 MSB 先行或 LSB 先行。它還支援雙線全雙工(前面小節說明的都是這種模式)、雙線單向以及單線模式。

SPI架構:

這裡寫圖片描述

通訊引腳 :

SPI的所有硬體架構都從圖中左側 MOSI、MISO、SCK及 NSS線展開的。STM32晶片有多個SPI外設,它們的SPI通訊訊號引出到不同的GPIO引腳上,使用時必須配置到這些指定的引腳。

2.軟體配置

這裡使用 STM32 的 SPI1 的主模式,SPI 相關的庫函式和定義分佈在檔案 stm32f10x_spi.c 以及標頭檔案 stm32f10x_spi.h 中。

2.1配置相關引腳的複用功能,使能 SPI1 時鐘

第一步就要使能 SPI1 的時鐘, SPI1 的時鐘通過 APB2ENR 的第 12 位來設定。其次要設定 SPI1 的相關引腳為複用輸出,這樣才會連線到 SPI1 上否則這些 IO 口還是預設的狀態,也就是標準輸入輸出口。這裡我們使用的是 PA5、 6、 7 這 3 個(SCK、 MISO、 MOSI,
CS 使用軟體管理方式),所以設定這三個為複用 IO


    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2P%riphGPHOA|RCC_APB2Periph_SPI1%r52C%521ENABLE ); 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

2.2初始化 SPI1,設定 SPI1 工作模式

接下來初始化 SPI1,設定 SPI1 為主機模式,設定資料格式為 8 位,然設定 SCK 時鐘極性及取樣方式。並設定 SPI1 的時鐘頻率(最大 18Mhz),以及資料的格式(MSB 在前還是LSB 在前)。這在庫函式中是通過 SPI_Init 函式來實現。
函式原型:

void SPI_Init(SPI_TypeDef* SPIx, SPI_InitTypeDef* SPI_InitStruct);

第一個引數是 SPI 標號,第二個引數結構體型別 SPI_InitTypeDef 為相關屬性設定。

SPI_InitTypeDef 的定義如下:

typedef struct
{
uint16_t SPI_Direction;
uint16_t SPI_Mode;
uint16_t SPI_DataSize;
uint16_t SPI_CPOL;
uint16_t SPI_CPHA;
uint16_t SPI_NSS;
uint16_t SPI_BaudRatePrescaler;
uint16_t SPI_FirstBit;
uint16_t SPI_CRCPolynomial;
}SPI_InitTypeDef;
引數 解釋
SPI_Direction 設定 SPI 的通訊方式,可以選擇為半雙工,全雙工,以及序列發和序列收方式
SPI_Mode 設定 SPI 的主從模式,主機模式 (SPI_Mode_Master),從機模式 (PI_Mode_Slave)。
SPI_DataSiz 資料為 8 位還是 16 位幀格式選擇項。SPI_DataSize_8b(8 位),SPI_DataSize_16b (16位)
SPI_CPOL 設定時鐘極性
SPI_CPHA 設定時鐘相位,也就是選擇在串行同步時鐘的第幾個跳變沿(上升或下降)資料被取樣,可以為第一個或者第二個條邊沿採集
SPI_NSS 設定 NSS 訊號由硬體(NSS 管腳)還是軟體控制
SPI_BaudRatePrescaler 設定 SPI 波特率預分頻值也就是決定 SPI 的時鐘的引數 ,從不分頻道 256 分頻 8 個可選值 ,選擇 256 分頻值SPI_BaudRatePrescaler_256, 傳輸速度為 36M/256=140.625KHz。
SPI_FirstBit 設定資料傳輸順序是 MSB 位在前還是 LSB 位在前。SPI_FirstBit_MSB (高位在前)
SPI_CRCPolynomial 設定 CRC 校驗多項式,提高通訊可靠性,大於 1 即可

初始化的範例格式為:

    SPI_InitTypeDef  SPI_InitStructure;

SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //設定SPI單向或者雙向的資料模式:SPI設定為雙線雙向全雙工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //設定SPI工作模式:設定為主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //設定SPI的資料大小:SPI傳送接收8位幀結構
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;     //選擇了序列時鐘的穩態:時鐘懸空高
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //資料捕獲(取樣)於第二個時鐘沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS訊號由硬體(NSS管腳)還是軟體(使用SSI位)管理:內部NSS訊號有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定義波特率預分頻的值:波特率預分頻值為256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //指定資料傳輸從MSB位還是LSB位開始:資料傳輸從MSB位開始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值計算的多項式
    SPI_Init(SPI1, &SPI_InitStructure);  //根據SPI_InitStruct中指定的引數初始化外設SPIx暫存器

2.3使能 SPI1

初始化完成之後使能 SPI1 通訊,在使能 SPI1 之後,就可以開始 SPI 通訊了。使能 SPI1 的方法為:

SPI_Cmd(SPI1, ENABLE); //使能 SPI 外設

2.4 SPI 傳輸資料

通訊介面需要有傳送資料和接受資料的函式,韌體庫提供的傳送資料函式原型為:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

往 SPIx 資料暫存器寫入資料 Data,從而實現傳送。

韌體庫提供的接受資料函式原型為:

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

這從 SPIx 資料暫存器讀出接收到的資料。

2.5檢視 SPI 傳輸狀態

在 SPI 傳輸過程中,要判斷資料是否傳輸完成,傳送區是否為空等等狀態,
通過函式 SPI_I2S_GetFlagStatus 實現的,判斷髮送是否完成的方法是:

SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);

3.軟體設計

3.1 SPI實現

SPI_InitTypeDef  SPI_InitStructure;

void SPI1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_SPI1, ENABLE ); 

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  //複用推輓輸出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_SetBits(GPIOA,GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7);

    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;  //設定SPI單向或者雙向的資料模式:SPI設定為雙線雙向全雙工
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;       //設定SPI工作模式:設定為主SPI
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;       //設定SPI的資料大小:SPI傳送接收8位幀結構
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;     //選擇了序列時鐘的穩態:時鐘懸空高
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;    //資料捕獲(取樣)於第二個時鐘沿
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;       //NSS訊號由硬體(NSS管腳)還是軟體(使用SSI位)管理:內部NSS訊號有SSI位控制
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;        //定義波特率預分頻的值:波特率預分頻值為256
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;  //指定資料傳輸從MSB位還是LSB位開始:資料傳輸從MSB位開始
    SPI_InitStructure.SPI_CRCPolynomial = 7;    //CRC值計算的多項式
    SPI_Init(SPI1, &SPI_InitStructure);  //根據SPI_InitStruct中指定的引數初始化外設SPIx暫存器

    SPI_Cmd(SPI1, ENABLE); //使能SPI外設

    SPI1_ReadWriteByte(0xff);//啟動傳輸      
}   
/************************************************/
//SPI 速度設定函式
//SpeedSet:
//SPI_BaudRatePrescaler_2   2分頻   (SPI [email protected] 72M)
//SPI_BaudRatePrescaler_8   8分頻   (SPI [email protected] 72M)
//SPI_BaudRatePrescaler_16  16分頻  (SPI [email protected] 72M)
//SPI_BaudRatePrescaler_256 256分頻 (SPI [email protected] 72M)

void SPI1_SetSpeed(u8 SpeedSet)
{
    SPI_InitStructure.SPI_BaudRatePrescaler = SpeedSet ;
    SPI_Init(SPI1, &SPI_InitStructure);
    SPI_Cmd(SPI1,ENABLE);
} 

/************************************************/
//SPIx 讀寫一個位元組
//TxData:要寫入的位元組
//返回值:讀取到的位元組
u8 SPI1_ReadWriteByte(u8 TxData)
{       
    u8 retry=0;                 
    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標誌位設定與否:傳送快取空標誌位
        {
        retry++;
        if(retry>200)return 0;
        }             
    SPI_I2S_SendData(SPI1, TxData); //通過外設SPIx傳送一個數據
    retry=0;

    while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//檢查指定的SPI標誌位設定與否:接受快取非空標誌位
        {
        retry++;
        if(retry>200)return 0;
        }                               
    return SPI_I2S_ReceiveData(SPI1); //返回通過SPIx最近接收的資料                     
}

3.2 Flash讀寫

u16 SPI_FLASH_TYPE=W25Q64;//預設就是25Q64
//4Kbytes為一個Sector
//16個扇區為1個Block
//W25X16
//容量為2M位元組,共有32個Block,512個Sector 

//初始化SPI FLASH的IO口
void SPI_Flash_Init(void)
{

    GPIO_InitTypeDef GPIO_InitStructure;

  RCC_APB2PeriphClockCmd(   RCC_APB2Periph_GPIOA, ENABLE );

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4;  //SPI CS
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;  //複用推輓輸出
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_3|GPIO_Pin_4);
    SPI1_Init();           //初始化SPI
    SPI1_SetSpeed(SPI_BaudRatePrescaler_4); //設定為18M時鐘,高速模式
    SPI_FLASH_TYPE=SPI_Flash_ReadID();//讀取FLASH ID.
}  
/************************************************/
//讀取SPI_FLASH的狀態暫存器
//BIT7  6   5   4   3   2   1   0
//SPR   RV  TB BP2 BP1 BP0 WEL BUSY
//SPR:預設0,狀態暫存器保護位,配合WP使用
//TB,BP2,BP1,BP0:FLASH區域防寫設定
//WEL:寫使能鎖定
//BUSY:忙標記位(1,忙;0,空閒)
//預設:0x00
u8 SPI_Flash_ReadSR(void)   
{  
    u8 byte=0;   
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_ReadStatusReg);    //傳送讀取狀態暫存器命令    
    byte=SPI1_ReadWriteByte(0Xff);             //讀取一個位元組  
    SPI_FLASH_CS=1;                            //取消片選     
    return byte;   
} 

/************************************************/
//寫SPI_FLASH狀態暫存器
//只有SPR,TB,BP2,BP1,BP0(bit 7,5,4,3,2)可以寫!!!
void SPI_FLASH_Write_SR(u8 sr)   
{   
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_WriteStatusReg);   //傳送寫取狀態暫存器命令    
    SPI1_ReadWriteByte(sr);               //寫入一個位元組  
    SPI_FLASH_CS=1;                            //取消片選             
}   

/************************************************/
//SPI_FLASH寫使能  
//將WEL置位   
void SPI_FLASH_Write_Enable(void)   
{
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_WriteEnable);      //傳送寫使能  
    SPI_FLASH_CS=1;                            //取消片選             
} 

/************************************************/
//SPI_FLASH寫禁止  
//將WEL清零  
void SPI_FLASH_Write_Disable(void)   
{  
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_WriteDisable);     //傳送寫禁止指令    
    SPI_FLASH_CS=1;                            //取消片選             
}               

/************************************************/
//讀取晶片ID W25X16的ID:0XEF14
u16 SPI_Flash_ReadID(void)
{
    u16 Temp = 0;     
    SPI_FLASH_CS=0;                 
    SPI1_ReadWriteByte(0x90);//傳送讀取ID命令     
    SPI1_ReadWriteByte(0x00);       
    SPI1_ReadWriteByte(0x00);       
    SPI1_ReadWriteByte(0x00);                  
    Temp|=SPI1_ReadWriteByte(0xFF)<<8;  
    Temp|=SPI1_ReadWriteByte(0xFF);  
    SPI_FLASH_CS=1;                 
    return Temp;
}               

/************************************************/
//讀取SPI FLASH  
//在指定地址開始讀取指定長度的資料
//pBuffer:資料儲存區
//ReadAddr:開始讀取的地址(24bit)
//NumByteToRead:要讀取的位元組數(最大65535)
void SPI_Flash_Read(u8* pBuffer,u32 ReadAddr,u16 NumByteToRead)   
{ 
    u16 i;                                                      
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_ReadData);         //傳送讀取命令   
    SPI1_ReadWriteByte((u8)((ReadAddr)>>16));  //傳送24bit地址    
    SPI1_ReadWriteByte((u8)((ReadAddr)>>8));   
    SPI1_ReadWriteByte((u8)ReadAddr);   
    for(i=0;i<NumByteToRead;i++)
    { 
        pBuffer[i]=SPI1_ReadWriteByte(0XFF);   //迴圈讀數  
    }
    SPI_FLASH_CS=1;                            //取消片選             
}  

/************************************************/
//SPI在一頁(0~65535)內寫入少於256個位元組的資料
//在指定地址開始寫入最大256位元組的資料
//pBuffer:資料儲存區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的位元組數(最大256),該數不應該超過該頁的剩餘位元組數!!!   
void SPI_Flash_Write_Page(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)
{
    u16 i;  
    SPI_FLASH_Write_Enable();                  //SET WEL 
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_PageProgram);      //傳送寫頁命令   
    SPI1_ReadWriteByte((u8)((WriteAddr)>>16)); //傳送24bit地址    
    SPI1_ReadWriteByte((u8)((WriteAddr)>>8));   
    SPI1_ReadWriteByte((u8)WriteAddr);   
    for(i=0;i<NumByteToWrite;i++)
        {
            SPI1_ReadWriteByte(pBuffer[i]);//迴圈寫數  
        }
    SPI_FLASH_CS=1;                            //取消片選 
    SPI_Flash_Wait_Busy();                     //等待寫入結束
} 

/************************************************/
//無檢驗寫SPI FLASH 
//必須確保所寫的地址範圍內的資料全部為0XFF,否則在非0XFF處寫入的資料將失敗!
//具有自動換頁功能 
//在指定地址開始寫入指定長度的資料,但是要確保地址不越界!
//pBuffer:資料儲存區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的位元組數(最大65535)
//CHECK OK
void SPI_Flash_Write_NoCheck(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{                    
    u16 pageremain;    
    pageremain=256-WriteAddr%256; //單頁剩餘的位元組數                
    if(NumByteToWrite<=pageremain)
        pageremain=NumByteToWrite;//不大於256個位元組
    while(1)
    {      
        SPI_Flash_Write_Page(pBuffer,WriteAddr,pageremain);
        if(NumByteToWrite==pageremain)
            break;//寫入結束了
        else //NumByteToWrite>pageremain
        {
            pBuffer+=pageremain;
            WriteAddr+=pageremain;  

            NumByteToWrite-=pageremain;           //減去已經寫入了的位元組數
            if(NumByteToWrite>256)
                pageremain=256; //一次可以寫入256個位元組
            else pageremain=NumByteToWrite;       //不夠256個位元組了
        }
    };      
} 

/************************************************/
//寫SPI FLASH  
//在指定地址開始寫入指定長度的資料
//該函式帶擦除操作!
//pBuffer:資料儲存區
//WriteAddr:開始寫入的地址(24bit)
//NumByteToWrite:要寫入的位元組數(最大65535)          
u8 SPI_FLASH_BUF[4096];//一個扇區大小
void SPI_Flash_Write(u8* pBuffer,u32 WriteAddr,u16 NumByteToWrite)   
{ 
    u32 secpos;
    u16 secoff;
    u16 secremain;     
    u16 i;    

    secpos=WriteAddr/4096;//扇區地址 0~511 for w25x16
    secoff=WriteAddr%4096;//在扇區內的偏移
    secremain=4096-secoff;//扇區剩餘空間大小   

    if(NumByteToWrite<=secremain)
        secremain=NumByteToWrite;//不大於4096個位元組
    while(1) 
    {   
        SPI_Flash_Read(SPI_FLASH_BUF,secpos*4096,4096);//讀出整個扇區的內容
        for(i=0;i<secremain;i++)//校驗資料
        {
            if(SPI_FLASH_BUF[secoff+i]!=0XFF)break;//需要擦除     
        }
        if(i<secremain)//需要擦除
        {
            SPI_Flash_Erase_Sector(secpos);//擦除這個扇區
            for(i=0;i<secremain;i++)       //複製
            {
                SPI_FLASH_BUF[i+secoff]=pBuffer[i];   
            }
            SPI_Flash_Write_NoCheck(SPI_FLASH_BUF,secpos*4096,4096);//寫入整個扇區  

        }
        else 
            SPI_Flash_Write_NoCheck(pBuffer,WriteAddr,secremain);//寫已經擦除了的,直接寫入扇區剩餘區間.                 
        if(NumByteToWrite==secremain)
            break;//寫入結束了
        else//寫入未結束
        {
            secpos++;//扇區地址增1
            secoff=0;//偏移位置為0    

            pBuffer+=secremain;  //指標偏移
            WriteAddr+=secremain;//寫地址偏移       
            NumByteToWrite-=secremain;              //位元組數遞減
            if(NumByteToWrite>4096)secremain=4096;  //下一個扇區還是寫不完
            else secremain=NumByteToWrite;          //下一個扇區可以寫完了
        }    
    };       
}

/************************************************/
//擦除整個晶片
//整片擦除時間:
//W25X16:25s 
//W25X32:40s 
//W25X64:40s 
//等待時間超長...
void SPI_Flash_Erase_Chip(void)   
{                                             
    SPI_FLASH_Write_Enable();                  //SET WEL 
    SPI_Flash_Wait_Busy();   
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_ChipErase);        //傳送片擦除命令  
    SPI_FLASH_CS=1;                            //取消片選             
    SPI_Flash_Wait_Busy();                     //等待晶片擦除結束
}   

/************************************************/
//擦除一個扇區
//Dst_Addr:扇區地址 0~511 for w25x16
//擦除一個山區的最少時間:150ms
void SPI_Flash_Erase_Sector(u32 Dst_Addr)   
{   
    Dst_Addr*=4096;
    SPI_FLASH_Write_Enable();                  //SET WEL     
    SPI_Flash_Wait_Busy();   
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_SectorErase);      //傳送扇區擦除指令 
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>16));  //傳送24bit地址    
    SPI1_ReadWriteByte((u8)((Dst_Addr)>>8));   
    SPI1_ReadWriteByte((u8)Dst_Addr);  
    SPI_FLASH_CS=1;                            //取消片選             
    SPI_Flash_Wait_Busy();                     //等待擦除完成
}  

/************************************************/
//等待空閒
void SPI_Flash_Wait_Busy(void)   
{   
    while ((SPI_Flash_ReadSR()&0x01)==0x01);   // 等待BUSY位清空
}  

/************************************************/
//進入掉電模式
void SPI_Flash_PowerDown(void)   
{ 
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_PowerDown);        //傳送掉電命令  
    SPI_FLASH_CS=1;                            //取消片選             
    delay_us(3);                               //等待TPD  
}   

/************************************************/
//喚醒
void SPI_Flash_WAKEUP(void)   
{  
    SPI_FLASH_CS=0;                            //使能器件   
    SPI1_ReadWriteByte(W25X_ReleasePowerDown);   //  send W25X_PowerDown command 0xAB    
      SPI_FLASH_CS=1;                            //取消片選               
    delay_us(3);                               //等待TRES1
}   

參考:

1.原子庫函式手冊