<acronym id="s8ci2"><small id="s8ci2"></small></acronym>
<rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
<acronym id="s8ci2"></acronym>
<acronym id="s8ci2"><center id="s8ci2"></center></acronym>
0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

STM32驅動FLASH(W25Q64)

冬至子 ? 來源:TECHTIMES ? 作者:霽風AI ? 2023-10-24 09:50 ? 次閱讀

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配置。

圖片

1.jpg

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

1.2 SPI 通訊的通訊時序

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

我們以讀取 FLASH 的狀態寄存器的時序圖分析一下,時序圖也是書寫軟件模擬時序的依據。

圖片

如上圖,我們知道書寫 FLASH (來自華邦 W25X 手冊)支持的是模式 0 (CPOL = 0 && CPHA == 0) 和 模式 3(CPOL = 1 && CPHA == 1)

CS、SCK、MOSI 信號都由主機控制產生,而 MISO 的信號由從機產生,主機通過該信號線讀取從機的數據。MOSI 與 MISO 的信號只在 CS 為低電平的時候才有效,在 SCK 的每個時鐘周期 MOSI 和 MISO 傳輸一位數據。

1.2.1. 通訊的起始和停止信號

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

1.2.2. 數據有效性

SPI 使用 MOSI 及 MISO 信號線來傳輸數據,使用 SCK 信號線進行數據同步。

圖片

MOSI 及 MISO 數據線在 SCK 的每個時鐘周期傳輸一位數據。數據傳輸時,MSB 先行或 LSB 先行并沒有作硬性規定,但要保證兩個 SPI 通訊設備之間使用同樣的協定,一般都會采用圖中的 MSB 先行模式。

觀察上圖,可知模式 0 和 3 都是在上升沿讀取數據。

示例:FLASH 讀取 JEDEC_ID (0x9F),SPI 模式 0,,頻率 f = 1MHz。

圖片

讀取 JEDEC_ID 時,FLASH 回復的一個字:0xC8。

圖片

1.2.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 的時鐘通過 APB2ENR 的第 12 位來設置。其次要設置 SPI1 的相關引腳為 復用輸出 ,這樣才會連接到 SPI1 上否則這些 IO 口還是默認的狀態,也就是標準輸入輸出口。這里我們使用的是 PA5、 PA6、 PA7 這 3 個(SCK、 MISO、 MOSI、CS 使用軟件管理方式),所以設置這三個為 復用 IO 。

宏定義:

#define SPIM1_GPIO_PORT        GPIOA

#define SPIM1_CLK_IO    (GPIO_Pin_5)
#define SPIM1_MISO_IO    (GPIO_Pin_6)
#define SPIM1_MOSI_IO    (GPIO_Pin_7)

#define FLASH_CS_IO             (GPIO_Pin_2)
#define FLASH_CS_0()            (GPIO_ResetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))      
#define FLASH_CS_1()             (GPIO_SetBits(SPIM1_GPIO_PORT, FLASH_CS_IO))

#define RCC_PCLK_SPIM1_GPIO     RCC_APB2Periph_GPIOA
#define RCC_PCLK_SPIM1_HD       RCC_APB2Periph_SPI1

IO 配置:

//--------------------------------------------------------------------------------------------------------
//    函 數 名: spi_gpio_init
//    功能說明: SPI 硬件IO初始化
//    形    參:     spi_chl:SPIM 通道
//    返 回 值: 無
//    日    期: 2020-03-12
//    備    注:采用 Unix like 方式
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_gpio_init(uint8_t spi_chl)
{
    GPIO_InitTypeDef gpio_config_init;

    if (spi_chl == 1)
    {
        RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_GPIO, ENABLE);        //開啟SPIM1 GPIO時鐘、

//        gpio_config_init.GPIO_Pin       = SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO; //SPIM1_CLK_IO IO初始化
        gpio_config_init.GPIO_Pin       = SPIM1_CLK_IO | SPIM1_MOSI_IO;
        gpio_config_init.GPIO_Mode      = GPIO_Mode_AF_PP;  //復用推挽輸出
        gpio_config_init.GPIO_Speed     = GPIO_Speed_50MHz;

        GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);

        gpio_config_init.GPIO_Pin       = SPIM1_MISO_IO;    //SPIM1_MISO_IO IO初始化
        gpio_config_init.GPIO_Mode      = GPIO_Mode_IN_FLOATING;  //MISO浮空輸入
        gpio_config_init.GPIO_Speed     = GPIO_Speed_50MHz;
        GPIO_Init(SPIM1_GPIO_PORT, &gpio_config_init);

        GPIO_SetBits(SPIM1_GPIO_PORT, SPIM1_CLK_IO | SPIM1_MISO_IO | SPIM1_MOSI_IO);    //IO初始狀態都設置為高電平
    }       
}

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;

1.jpg

初始化的范例格式為:

//--------------------------------------------------------------------------------------------------------
//    函 數 名: spi_master_init
//    功能說明: SPI 硬件配置參數初始化
//    形    參:     spi_chl:SPIM 通道
//    返 回 值: 無
//    日    期: 2020-03-12
//    備    注:采用 Unix like 方式
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_init(uint8_t spi_chl)
{
    SPI_InitTypeDef  spi_config_init;
#if 1  
    if(spi_chl == 1)
    {   
        spi_flash_gpio_init();  //spi flash cs 初始化
//        sd_gpio_init(); //spi sd cs 初始化
//        nrf24l01_gpio_init();//spi nrf24l01 cs 初始化

        spi_gpio_init(1);   //spi gpio 初始化

        RCC_APB2PeriphClockCmd(RCC_PCLK_SPIM1_HD, ENABLE);  //SPI1時鐘使能

        spi_config_init.SPI_Direction           = SPI_Direction_2Lines_FullDuplex;  //設置SPI單向或者雙向的數據模式:SPI設置為雙線雙向全雙工
        spi_config_init.SPI_Mode                = SPI_Mode_Master;      //設置SPI工作模式:設置為主SPI
        spi_config_init.SPI_DataSize            = SPI_DataSize_8b;      //設置SPI的數據大小:SPI發送接收8位幀結構
        spi_config_init.SPI_CPOL                = SPI_CPOL_Low;     //選擇了串行時鐘的穩態:空閑時鐘低
        spi_config_init.SPI_CPHA                = SPI_CPHA_1Edge;   //數據捕獲(采樣)于第1個時鐘沿
        spi_config_init.SPI_NSS                 = SPI_NSS_Soft;//SPI_NSS_Soft;      //NSS信號由硬件(NSS管腳)還是軟件(使用SSI位)管理:內部NSS信號有SSI位控制
        spi_config_init.SPI_BaudRatePrescaler   = SPI_BaudRatePrescaler_256;        //定義波特率預分頻的值:波特率預分頻值為256
        spi_config_init.SPI_FirstBit            = SPI_FirstBit_MSB; //指定數據傳輸從MSB位還是LSB位開始:數據傳輸從MSB位開始
        spi_config_init.SPI_CRCPolynomial       = 7;    //CRC值計算的多項式

        SPI_Init(SPI1, &spi_config_init);  //根據SPI_InitStruct中指定的參數初始化外設SPIx寄存器

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

//        spi_master_send_recv_byte(1, 0xFF); //啟動傳輸  

    }
#endif
}

2.3 SPI 傳輸數據

通信接口需要有發送數據和接受數據的函數,固件庫提供的發送數據函數原型為:

void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);

往 SPIx 數據寄存器寫入數據 Data,從而實現發送。

固件庫提供的接受數據函數原型為:

uint16_t SPI_I2S_ReceiveData(SPI_TypeDef* SPIx) ;

這從 SPIx 數據寄存器讀出接收到的數據。

收發單個字節數據:

//--------------------------------------------------------------------------------------------------------
//    函 數 名: spi_master_send_recv_byte
//    功能說明: SPI 收發數據
//    形    參:     spi_chl:SPIM 通道
//                send_byte:發送的數據
//    返 回 值: 無
//    日    期: 2020-03-14
//    備    注:采用 Unix like 方式
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t spi_master_send_recv_byte(uint8_t spi_chl, uint8_t spi_byte)
{        
    uint8_t time = 0;

    if (spi_chl == 1)               
    {
        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) //檢查指定的SPI標志位設置與否:發送緩存空標志位
        {
            time++;
            if(time >200)
            {
                return false;
            }
        }             
        SPI_I2S_SendData(SPI1, spi_byte); //通過外設SPIx發送一個數據

        time = 0;

        while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET)//檢查指定的SPI標志位設置與否:接受緩存非空標志位
        {
            time++;
            if(time >200)
            {
                return false;
            }
        }                               
            return SPI_I2S_ReceiveData(SPI1); //返回通過SPIx最近接收的數據 
    }
    else 
    {
        return false;
    }
}

收發多個字節數據:

//--------------------------------------------------------------------------------------------------------
//    函 數 名: spi_master_send_some_bytes
//    功能說明: SPI 發送多個字節數據
//    形    參:     spi_chl:SPIM 通道
//                pbdata:發送的數據首地址
//                send_length:發送數據長度
//    返 回 值: 無
//    日    期: 2020-03-12
//    備    注:采用 Unix like 方式
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_send_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t send_length)
{
    uint16_t i = 0;

    for (i = 0; i < send_length; i++)
    {
        spi_master_send_recv_byte(spi_chl, pbdata[i]);
    }

//    while (send_length--)
//    {
//        spi_master_send_byte(spi_chl, *pbdata++);
//    }

}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: spi_master_recv_some_bytes
//    功能說明: SPI 接收多個字節數據
//    形    參:     spi_chl:SPIM 通道
//                pbdata:接收的數據首地址
//                send_length:接收數據長度
//    返 回 值: 無
//    日    期: 2020-03-12
//    備    注:采用 Unix like 方式
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void spi_master_recv_some_bytes(uint8_t spi_chl, uint8_t *pbdata, uint16_t recv_length)
{
    uint8_t *temp_data = pbdata;

    while (recv_length--)
    {
        *temp_data++ = spi_master_send_recv_byte(spi_chl, 0xFF);    //發送 0xff 為從設備提供時鐘
    }

}

2.4 查看 SPI 傳輸狀態

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

SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE);

3. SPI FLASH 操作

3.1 宏定義部分

#define  FLASH_WRITE_ENABLE_CMD         0x06
#define  FLASH_WRITE_DISABLE_CMD        0x04
#define  FLASH_READ_SR_CMD                0x05
#define  FLASH_WRITE_SR_CMD                0x01
#define  FLASH_READ_DATA                0x03
#define  FLASH_FASTREAD_DATA            0x0b
#define  FLASH_WRITE_PAGE                0x02
#define  FLASH_ERASE_PAGE                  0x81
#define  FLASH_ERASE_SECTOR               0x20
#define     FLASH_ERASE_BLOCK              0xd8
#define     FLASH_ERASE_CHIP               0xc7
#define  FLASH_POWER_DOWN                0xb9
#define  FLASH_RELEASE_POWER_DOWN       0xab
#define  FLASH_READ_DEVICE_ID              0x90
#define  FLASH_READ_JEDEC_ID              0x9f

#define     FLASH_SIZE   (1*1024*1024)  // 1M字節
#define        PAGE_SIZE           8192    // 256 bytes
#define     SECTOR_SIZE     512  // 4-Kbyte
#define        BLOCK_SIZE      32  // 64-Kbyte 

#define PAGE_LEN        255  //一頁256字節

3.2 中間層函數封裝

注明: 此部分函數的封裝是為了統一硬件 SPI 和軟件模擬 SPI 接口。

//--------------------------------------------------------------------------------------------------------
//    函 數 名: hal_spi_send_bytes
//    功能說明: SPI 發送數據,包含軟件和硬件通信方式
//    形    參:     mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
//                pbdata:發送數據的首地址
//                send_length:發送數據長度
//    返 回 值: 執行狀態(true or false)
//    日    期: 2020-03-12
//    備    注: 中間層封裝底層接口
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_send_bytes(uint8_t mode, uint8_t *pbdata, uint16_t send_length)
{
    if (mode == 0)
    {
        for (uint16_t i = 0; i < send_length; i++)
        {
            Spi_WriteByte(pbdata[i]);
        }

        return true;
    }
    else if (mode == 1)
    {
        spi_master_send_some_bytes(1, pbdata, send_length);

//        for (uint16_t i = 0; i < send_length; i++)
//        {
//            spi_master_send_recv_byte(1, pbdata[i]);
//        }

        return true;
    }
    else 
    {
        return false;
    }

}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: hal_spi_recv_bytes
//    功能說明: SPI 接收數據,包含軟件和硬件通信方式
//    形    參:     mode:通信方式選擇(0:軟件SPI;1:硬件SPI)
//                pbdata:發送數據的首地址
//                send_length:發送數據長度
//    返 回 值: 執行狀態(true or false)
//    日    期: 2020-03-12
//    備    注: 中間層封裝底層接口
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t hal_spi_recv_bytes(uint8_t mode, uint8_t *pbdata, uint16_t recv_length)
{
    if (mode == 0)
    {
        for (uint16_t i = 0; i < recv_length; i++)
        {
             *pbdata++ = Spi_ReadByte();    //軟件模擬SPI
        }   

        return true;
    }
    else if (mode == 1)
    {
        spi_master_recv_some_bytes(1, pbdata, recv_length);    //硬件SPI

//        for (uint16_t i = 0; i < recv_length; i++)
//        {
//            *pbdata++ = spi_master_send_recv_byte(1, 0xFF);
//        }

        return true;
    }
    else 
    {
        return false;
    }

}

3.3 FLASH 部分

__align(4) uint8_t g_DataTmpBuffer[0x1000] = {0};

#define SectorBuf  g_DataTmpBuffer

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteEnable
//    功能說明: 寫使能,置位 WEL 位 WEL 位(WEL-- >1)
//    形    參: 無
//    返 回 值: 無
//    日    期: 2020-03-07
//    備    注: 
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteEnable(void)
{
    uint8_t command = FLASH_WRITE_ENABLE_CMD;

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);//開啟寫使能
    FLASH_CS_HIGH;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteDisable
//    功能說明: 寫失能,復位 WEL 位(WEL-- >0)
//    形    參: 無
//    返 回 值: 無
//    日    期: 2020-03-07
//    備    注: 
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteDisable(void)
{
    uint8_t command = FLASH_WRITE_DISABLE_CMD;
    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    // Spi_WriteByte(FLASH_WRITE_DISABLE_CMD);  //開啟寫失能 04h
    FLASH_CS_HIGH;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteSR
//    功能說明: 讀狀態寄存器
//    形    參: 無
//    返 回 值: 無
//    日    期: 2020-03-07
//    備    注: 多用于檢查 BUSY 位
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint8_t Flash_ReadSR(void)
{
    uint8_t ucTmpVal = 0;
    uint8_t command = FLASH_READ_SR_CMD;

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //05h
    hal_spi_recv_bytes(SPI_COMM_MODE, &ucTmpVal, 1);

    // ucTmpVal = Spi_ReadByte();

    FLASH_CS_HIGH;

    return ucTmpVal;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteSR
//    功能說明: 寫狀態寄存器
//    形    參:     _ucByte:寫入狀態寄存器的數值
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSR(uint8_t _ucByte)
{
    uint8_t command = FLASH_WRITE_SR_CMD;

    Flash_WriteEnable();    
    Flash_WaitNobusy();

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //01h
    hal_spi_send_bytes(SPI_COMM_MODE, &_ucByte, 1); //寫入一個字節
    FLASH_CS_HIGH;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WaitNobusy
//    功能說明: 檢查 FLASH BUSY 位狀態
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 調用Flash_ReadSR(),判斷狀態寄存器的R0位,執行結束操作清零
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WaitNobusy(void)
{
    //FLASH_READ_SR_CMD 指令的發送,有的FLASH僅需發送一次,FLASH自動回復,有的FLASH無法自動回復,需要循環一直發送等待
    while(((Flash_ReadSR()) & 0x01)==0x01); //等待BUSY位清空
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_FastReadByte
//    功能說明: flash 都數據(快速讀?。篎ast read operate at the highest poossible frequency)
//    形    參:     ucpBuffer:數據存儲區首地址
//                _ulReadAddr: 要讀出Flash的首地址
//                _usNByte: 要讀出的字節數(最大65535B)
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 從_ulReadAddr地址,連續讀出_usNByte長度的字節
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_ReadSomeBytes(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
    uint8_t command = FLASH_READ_DATA;
    uint8_t temp_buff[3] = {0};

    temp_buff[0] = (uint8_t)(_ulReadAddr > > 16);
    temp_buff[1] = (uint8_t)(_ulReadAddr > > 8);
    temp_buff[2] = (uint8_t)(_ulReadAddr > > 0);

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);

    hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);

    // Spi_WriteByte(FLASH_READ_DATA);  //連續讀取數據 03h
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>16));   //寫入24位地址
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>8));
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>0));

    // while(_usNByte--)
    // {
    //  *ucpBuffer = Spi_ReadByte();
    //  ucpBuffer++;
    // }

    FLASH_CS_HIGH;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_FastReadByte
//    功能說明: flash 都數據(快速讀?。篎ast read operate at the highest poossible frequency)
//    形    參:     ucpBuffer:數據存儲區首地址
//                _ulReadAddr: 要讀出Flash的首地址
//                _usNByte: 要讀出的字節數(最大65535B)
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 從_ulReadAddr地址,連續讀出_usNByte長度的字節
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_FastReadByte(uint8_t *ucpBuffer, uint32_t _ulReadAddr, uint16_t _usNByte)
{
    uint8_t command = FLASH_FASTREAD_DATA;
    uint8_t temp_buff[3] = {0};

    temp_buff[0] = (uint8_t)(_ulReadAddr > > 16);
    temp_buff[1] = (uint8_t)(_ulReadAddr > > 8);
    temp_buff[2] = (uint8_t)(_ulReadAddr > > 0);

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);

    hal_spi_recv_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);

    // Spi_WriteByte(FLASH_FASTREAD_DATA);//快速讀取數據 0bh
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>16));//寫入24位地址
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>8));
    // Spi_WriteByte((uint8_t)(_ulReadAddr >>0));
    // Spi_WriteByte(0xFF);//等待8個時鐘(dummy byte)
    // while(_usNByte--)
    // {
    //  *ucpBuffer = Spi_ReadByte();
    //  ucpBuffer++;
    // }

    FLASH_CS_HIGH;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WritePage
//    功能說明: flash 寫數據(按頁寫入,一頁256字節,寫入之前FLASH地址上必須為0xFF)
//    形    參:     ucpBuffer:數據存儲區首地址
//                _ulWriteAddr: 要讀寫入Flash的首地址
//                _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: _ulWriteAddr,連續寫入_usNByte長度的字節
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WritePage(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
    uint8_t command = FLASH_WRITE_PAGE;
    uint8_t temp_buff[3] = {0};

    temp_buff[0] = (uint8_t)(_ulWriteAddr > > 16);
    temp_buff[1] = (uint8_t)(_ulWriteAddr > > 8);
    temp_buff[2] = (uint8_t)(_ulWriteAddr > > 0);

    Flash_WriteEnable();    //寫使能
    Flash_WaitNobusy(); //等待寫入結束

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);

    hal_spi_send_bytes(SPI_COMM_MODE, ucpBuffer, _usNByte);

    // Spi_WriteByte(FLASH_WRITE_PAGE); //02h
    // Spi_WriteByte((uint8_t)(_ulWriteAddr >>16));  //寫入24位地址
    // Spi_WriteByte((uint8_t)(_ulWriteAddr >>8));
    // Spi_WriteByte((uint8_t)(_ulWriteAddr >>0));
    // while(_usNByte--)
    // {
    //  Spi_WriteByte(*ucpBuffer);  //SPI 寫入單個字節
    //  ucpBuffer++;
    // }

    FLASH_CS_HIGH;

    Flash_WaitNobusy(); //等待寫入結束
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteNoCheck
//    功能說明: flash 寫數據(不帶擦除,寫入之前必須確保寫入部分FLASH的數據全為0xFf,否則寫入失敗)
//    形    參:     ucpBuffer:數據存儲區首地址
//                _ulWriteAddr: 要讀寫入Flash的首地址
//                _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: _ulWriteAddr,連續寫入_usNByte長度的字節,程序帶FLASH數據檢查寫入
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteNoCheck(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
    uint16_t PageByte = 256 - _ulWriteAddr % 256;//單頁剩余可寫字節數

    if(_usNByte <= PageByte)    //不大于256字節
    {
        PageByte = _usNByte;
    }

    while(1)
    {
        Flash_WritePage(ucpBuffer, _ulWriteAddr, PageByte);
        if(_usNByte == PageByte)    //寫入結束
            break;
        else
        {
            ucpBuffer += PageByte;  //下一頁寫入的數據
            _ulWriteAddr += PageByte;   //下一頁寫入的地址
            _usNByte -= PageByte;   //待寫入的字節數遞減
            if(_usNByte > 256)
            {
                PageByte = 256;
            }
            else
            {
                PageByte = _usNByte;
            }
        }
    }
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WriteSomeBytes
//    功能說明: flash 寫數據
//    形    參:     ucpBuffer:數據存儲區首地址
//                _ulWriteAddr: 要讀寫入Flash的首地址
//                _usNByte: 要寫入的字節數(最大65535B = 64K 塊)
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: _ulWriteAddr,連續寫入_usNByte長度的字節,程序帶FLASH數據檢查寫入
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WriteSomeBytes(uint8_t *ucpBuffer, uint32_t _ulWriteAddr, uint16_t _usNByte)
{
    uint32_t ulSecPos = 0;              //得到扇區位置
    uint16_t usSecOff = 0;              //扇區偏移
    uint16_t usSecRemain = 0;       //剩余扇區
    uint32_t i = 0;

    ulSecPos = _ulWriteAddr / 4096;//地址所在扇區(0--511)
    usSecOff = _ulWriteAddr % 4096;//扇區內地址偏移
    usSecRemain = 4096 - usSecOff;//扇區除去偏移,還剩多少字節

    if(_usNByte <= usSecRemain) //寫入數據大小 < 剩余扇區空間大小
    {
        usSecRemain = _usNByte;
    }

    while(1)
    {
        Flash_ReadSomeBytes(SectorBuf, ulSecPos*4096, 4096);//讀出整個扇區的內容
        for (i = 0; i < usSecRemain; i++)   //校驗數據
        {
            if (SectorBuf[usSecOff + i] != 0xFF)//儲存數據不為0xFF,需要擦除
                break;
        }

        if(i < usSecRemain) //需要擦除
        {
            Flash_EraseSector(ulSecPos);    //擦除這個扇區
            for(i = 0; i < usSecRemain; i++)    //保存寫入的數據
            {
                SectorBuf[usSecOff + i] = ucpBuffer[i];
            }
            Flash_WriteNoCheck(SectorBuf, ulSecPos*4096, 4096); //寫入整個扇區(扇區=老數據+新寫入數據)
        }
        else
        {
            Flash_WriteNoCheck(ucpBuffer, _ulWriteAddr, usSecRemain);//不需要擦除,直接寫入扇區
        }
        if(_usNByte == usSecRemain) //寫入結束
        {
            Flash_WriteDisable();
            break;
        }
        else
        {
            ulSecPos++;     //扇區地址增加1
            usSecOff = 0;       //扇區偏移歸零
            ucpBuffer += usSecRemain;   //指針偏移
            _ulWriteAddr += usSecRemain;    //寫地址偏移
            _usNByte -= usSecRemain;    //待寫入的字節遞減

            if(_usNByte > 4096)
            {
                usSecRemain = 4096; //待寫入一扇區(4096字節大小)
            }
            else
            {
                usSecRemain = _usNByte;     //待寫入少于一扇區的數據
            }
        }

    }

}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_ErasePage
//    功能說明: flash erase page
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 有的 FLASH 支持
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_ErasePage(uint32_t _ulPageAddr)
{
    _ulPageAddr *= 256;

    Flash_WriteEnable();
    Flash_WaitNobusy();

    FLASH_CS_LOW;
    Spi_WriteByte(FLASH_ERASE_PAGE);    //頁擦除指令
    Spi_WriteByte((uint8_t)(_ulPageAddr >>16));  //寫入24位地址
    Spi_WriteByte((uint8_t)(_ulPageAddr >>8));
    Spi_WriteByte((uint8_t)(_ulPageAddr >>0));
    FLASH_CS_HIGH;

    Flash_WaitNobusy(); //等待寫入結束
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_EraseSector
//    功能說明: flash erase sector
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 1扇區 = 4K Bytes
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseSector(uint32_t _ulSectorAddr)
{
    uint8_t command = FLASH_ERASE_SECTOR;
    uint8_t temp_buff[3] = {0};

    temp_buff[0] = (uint8_t)(_ulSectorAddr > > 16);
    temp_buff[1] = (uint8_t)(_ulSectorAddr > > 8);
    temp_buff[2] = (uint8_t)(_ulSectorAddr > > 0);

    _ulSectorAddr *= 4096;  //1個扇區 4 KBytes

    Flash_WriteEnable();
    Flash_WaitNobusy();

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[0], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[1], 1);
    hal_spi_send_bytes(SPI_COMM_MODE, &temp_buff[2], 1);


//    Spi_WriteByte(FLASH_ERASE_SECTOR);  //20h
//    Spi_WriteByte((uint8_t)(_ulSectorAddr >>16));    //寫入24位地址
//    Spi_WriteByte((uint8_t)(_ulSectorAddr >>8));
//    Spi_WriteByte((uint8_t)(_ulSectorAddr));
    FLASH_CS_HIGH;

    Flash_WaitNobusy(); //等待寫入結束
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_EraseBlock
//    功能說明: flash erase block 
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 1塊 = 64K Bytes
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseBlock(uint32_t _ulBlockAddr)
{
    uint8_t command = FLASH_ERASE_BLOCK;
    _ulBlockAddr *= 65536;  //塊地址,一塊64K

    Flash_WriteEnable();
    Flash_WaitNobusy();

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>16), 1);
    hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>8), 1);
    hal_spi_send_bytes(SPI_COMM_MODE, (uint8_t *)(_ulBlockAddr >>0), 1);

    // Spi_WriteByte(FLASH_ERASE_BLOCK);    //d8h
    // Spi_WriteByte((uint8_t)(_ulBlockAddr >>16));  //寫入24位地址
    // Spi_WriteByte((uint8_t)(_ulBlockAddr >>8));
    // Spi_WriteByte((uint8_t)(_ulBlockAddr));
    FLASH_CS_HIGH;

    Flash_WaitNobusy(); //等待寫入結束
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_EraseChip
//    功能說明: flash erase chip , it makes flash  recovery FF
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 軟件模擬SPI
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_EraseChip(void)
{
    uint8_t command = FLASH_ERASE_CHIP;

    Flash_WriteEnable();    //flash芯片寫使能
    Flash_WaitNobusy(); //等待寫操作完成

    FLASH_CS_LOW;
    hal_spi_recv_bytes(SPI_COMM_MODE, &command, 1);
    // Spi_WriteByte(FLASH_ERASE_CHIP); //c7h
    FLASH_CS_HIGH;

    Flash_WaitNobusy(); //等待寫入結束
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_PowerDown
//    功能說明: flash into power down mode 
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//  備    注: 軟件模擬SPI
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_PowerDown(void)
{
    uint8_t command = FLASH_POWER_DOWN; 

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    // Spi_WriteByte(FLASH_POWER_DOWN); //b9h
    FLASH_CS_HIGH;
    Sys_delay_us(3);    // cs go high , need to delay 3us
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_WakeUp
//    功能說明: wake up flash from power down mode or hign performance mode
//    形    參: no
//    返 回 值: no
//    日    期: 2020-03-07
//    備    注: 軟件模擬SPI
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
void Flash_WakeUp(void)
{
    uint8_t command = FLASH_RELEASE_POWER_DOWN; 

    FLASH_CS_LOW;
    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1);
    // Spi_WriteByte(FLASH_RELEASE_POWER_DOWN);//abh
    FLASH_CS_HIGH;
    Sys_delay_us(3);    //CS go high , need delay 3us
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_ReadDeviceID
//    功能說明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
//    形    參: 無
//    返 回 值: ulJedId:FLASH ID 3字節
//    日    期: 2020-03-06
//    備    注: 軟件模擬SPI
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint16_t Flash_ReadDeviceID(void)
{
    uint8_t command = FLASH_READ_DEVICE_ID;
    uint16_t usFlashId = 0;
    uint8_t temp_buff[3] = {0};

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //90h
    hal_spi_send_bytes(SPI_COMM_MODE, temp_buff, 3);    //寫入24位地址;假地址
    hal_spi_recv_bytes(SPI_COMM_MODE, temp_buff, 2);

    // Spi_WriteByte(FLASH_READ_DEVICE_ID); //90h
    // Spi_WriteByte(0x00);//寫入24位地址;假地址
    // Spi_WriteByte(0x00);
    // Spi_WriteByte(0x00); //如果0x01,先輸出 Device ID
    // usFlashId |= Spi_ReadByte()< 
    // usFlashId |= Spi_ReadByte();

    FLASH_CS_HIGH;

    usFlashId = (uint16_t)(temp_buff[0] < < 8) | (temp_buff[1] < < 0);

    return usFlashId;
}

//--------------------------------------------------------------------------------------------------------
//    函 數 名: Flash_ReadJEDECID
//    功能說明: 讀取FLASH ID(manufacturer ID-1Byte + Device ID-2Byte:type+density)
//    形    參: 無
//    返 回 值: ulJedId:FLASH ID 3字節
//    日    期: 2020-03-06
//    備    注: 軟件模擬SPI
//    作    者: by 霽風AI
//--------------------------------------------------------------------------------------------------------
uint32_t Flash_ReadJEDECID(void)
{
    uint8_t command = FLASH_READ_JEDEC_ID;
    uint32_t flash_jed_id = 0;
    uint8_t recv_buff[3] = {0};

    FLASH_CS_LOW;

    hal_spi_send_bytes(SPI_COMM_MODE, &command, 1); //9fh
    hal_spi_recv_bytes(SPI_COMM_MODE, recv_buff, 3);

    FLASH_CS_HIGH;

    flash_jed_id = (recv_buff[0] < < 16) | (recv_buff[1] < < 8) | (recv_buff[2] < < 0);

return flash_jed_id;

}
聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • FlaSh
    +關注

    關注

    10

    文章

    1550

    瀏覽量

    146646
  • STM32
    +關注

    關注

    2239

    文章

    10671

    瀏覽量

    348738
  • SRAM芯片
    +關注

    關注

    0

    文章

    64

    瀏覽量

    11950
  • 狀態寄存器
    +關注

    關注

    0

    文章

    37

    瀏覽量

    7001
  • w25Q64
    +關注

    關注

    1

    文章

    15

    瀏覽量

    2932
收藏 人收藏

    評論

    相關推薦

    W25Q64如何與STM32F107RET6相連接

    我想問下W25Q64如何與STM32F107RET6相連接,我是個新手,才剛學會布線,看了2個芯片的datasheet也不會連接求大神指教
    發表于 08-02 16:41

    請問怎么將SD卡中的圖片燒寫到W25q64中?

    怎樣將SD卡中的圖片燒寫到W25q64中呢想給液晶屏做個開機圖片,以前圖片是放在SD中的,剛好也有例程,所以不難弄,但是怎樣將圖片燒寫到W25Q64中呢??
    發表于 04-12 06:36

    W25Q64串行FLASH基礎知識大小

    W25Q64串行FLASH基礎知識大?。?M(Byte)(128塊(Block),每塊64K字節,每塊16個扇區(Sector),每個扇區4K字 節,每個扇區16頁,每頁256個字節)特點
    發表于 07-22 09:32

    如何去實現stm32W25Q64程序代碼呢

    如何去實現stm32W25Q64程序代碼呢?
    發表于 12-17 06:46

    W25Q64是什么?怎樣去使用W25Q64

    W25Q64是什么?怎樣去使用W25Q64呢?Flash與EEPROM的區別有哪些呢?
    發表于 12-20 06:32

    介紹W25Q64驅動函數

    可說的。關于FATFS的移植下一篇文章介紹。本篇文章主要介紹W25Q64驅動函數。W25Q64容量是64Mbit的flash,
    發表于 01-26 07:53

    使用外部flash(w25q64)掛載fatfs總是會出現壞文件是為什么

    使用外部flash(w25q64)掛載fatfs文件系統。使用過程中總是會出現壞文件,且無法刪除,也導致包含壞文件的文件夾也無法刪除
    發表于 09-22 10:21

    STM32H750 QSPI讀W25Q64出錯是什么原因?

    最近在調STM32H750 通過QSPI接口讀寫W25Q64,利用fal和sfud驅動,初始化沒有問題,但是對flash進行讀寫測試時,發現寫到32768地址的時候總是會出錯,更換
    發表于 02-23 13:47

    STM32H750 QSPI讀W25Q64出錯是什么原因?

    最近在調STM32H750 通過QSPI接口讀寫W25Q64,利用fal和sfud驅動,初始化沒有問題,但是對flash進行讀寫測試時,發現寫到32768地址的時候總是會出錯,更換
    發表于 02-23 13:49

    W25Q40換成W25Q64模塊是否能正常工作?

    如果將閃存芯片W25Q40(4Mbit)換成W25Q6464Mbit),模塊是否能正常工作?而且,最有趣的是,是否支持在 NodeMcu 中處理文件? PS 如何確定可用于錄制文件的閃存容量?
    發表于 04-28 07:33

    STM32Cube-18】使用硬件QSPI讀寫SPI FlashW25Q64

    本篇詳細的記錄了如何使用STM32CubeMX配置STM32L431RCT6的硬件QSPI外設與 SPI Flash 通信(W25Q64)。
    發表于 12-01 21:06 ?12次下載
    【<b class='flag-5'>STM32</b>Cube-18】使用硬件QSPI讀寫SPI <b class='flag-5'>Flash</b>(<b class='flag-5'>W25Q64</b>)

    STM32單片機基礎18——使用硬件QSPI讀寫SPI FlashW25Q64

    本篇詳細的記錄了如何使用STM32CubeMX配置STM32L431RCT6的硬件QSPI外設與 SPI Flash 通信(W25Q64)。1. 準備工作硬件準備開發板首先需要準備一個
    發表于 12-02 10:21 ?18次下載
    <b class='flag-5'>STM32</b>單片機基礎18——使用硬件QSPI讀寫SPI <b class='flag-5'>Flash</b>(<b class='flag-5'>W25Q64</b>)

    剖析STM32F103讀寫W25Q64

    可說的。關于FATFS的移植下一篇文章介紹。本篇文章主要介紹W25Q64驅動函數。W25Q64容量是64Mbit的flash,
    發表于 12-02 11:21 ?36次下載
    剖析<b class='flag-5'>STM32</b>F103讀寫<b class='flag-5'>W25Q64</b>

    Linux驅動開發-編寫W25Q64(Flash)驅動

    本篇文章就介紹如何在Linux系統下編寫W25Q64芯片的驅動,完成數據存儲,W25Q64支持標準SPI總線,當前驅動程序底層的代碼寫了兩種方式,一種是采用內核提供的SPI子系統框架,
    的頭像 發表于 09-17 15:09 ?2714次閱讀
    Linux<b class='flag-5'>驅動</b>開發-編寫<b class='flag-5'>W25Q64</b>(<b class='flag-5'>Flash</b>)<b class='flag-5'>驅動</b>

    STM32驅動W25Q64讀寫數據資料

    STM32驅動W25Q64讀寫數據資料
    發表于 04-12 14:28 ?23次下載
    亚洲欧美日韩精品久久_久久精品AⅤ无码中文_日本中文字幕有码在线播放_亚洲视频高清不卡在线观看
    <acronym id="s8ci2"><small id="s8ci2"></small></acronym>
    <rt id="s8ci2"></rt><rt id="s8ci2"><optgroup id="s8ci2"></optgroup></rt>
    <acronym id="s8ci2"></acronym>
    <acronym id="s8ci2"><center id="s8ci2"></center></acronym>