Skip to content

W25Qxx 芯片的驱动方法

W25Qxx 芯片是一种 SPI 接口的 Flash 芯片,其内部存储空间为 若干 M 字节,支持 16 位地址读写。 常用的型号有 W25Q16(2M 字节)、W25Q32(4M 字节)、W25Q64(8M 字节)等。W25Qxx 芯片支持多种指令,包括读指令、写指令、擦除指令等,可以通过 SPI 接口发送指令来操作芯片。

一.W25Qxx 芯片的硬件连接

以下是 W25Qxx 芯片测试用的引脚定义,实际使用过程中根据实际硬件进行修改。

w25qxx_1

二.W25Qxx 芯片的驱动方法

  1. 初始化 W25Qxx 芯片,判断芯片是否正常.

    • 通过读取 W25Qxx 芯片的 ID,判断是否正确.
    • 不同厂家在读取 ID时,会有差异, 一般我们直接读取#define W25X_JedecDeviceID 0x9F 这个地址的值,这个值的三个字节分别表示: 制造商ID、设备ID高字节和设备ID低字节.
    • 判断 ID 是否正确,如果正确则返回 0,否则返回错误码. ID的第三个字节表示 Flash 容量大小,不同的容量大小对应的 Flash 容量大小不同,我们如果使用 16M 的 Flash 容量大小,因此第三字节的值为 0x17. 现请列举所有的 W25Qxx 芯片的 第三字节的值
    c
     #define W25X_JedecDeviceID   0x9F
     #define W25X_ReleasePowerDown   0xAB
    
     uint16_t W25QXX_ReadID(void) {
         uint16_t flashID = 0;
         // uint8_t data[3];
         W25QXX_CS(0); // 使能器件
             // 通过读取 W25X_ManufactDeviceID 这种方式,不能厂家返回的值并不一样.所以需要使用 W25X_JedecDeviceID
             // W25QXX_WriteByte(W25X_ManufactDeviceID); // 发送读取ID命令
             // W25QXX_WriteByte(0x00);                  // 发送24位地址
             // W25QXX_WriteByte(0x00);
             // W25QXX_WriteByte(0x00);
         W25QXX_WriteByte(W25X_JedecDeviceID); // 发送读取ID命令
         W25QXX_ReadByte();          // 制造商ID
         W25QXX_ReadByte();          // 设备ID高字节
         flashID = W25QXX_ReadByte();          // 设备ID低字节
         W25QXX_CS(1);                         // 取消片选
         // printf("Flash ID: 0x%02X  0x%02X   0x%02X\r\n", data[0], data[1], data[2]);
         return flashID;
     }
    
     /**
     * @brief  唤醒W25QXX
     * @param
     * @retval
     */
     void W25QXX_WAKEUP(void) {
         W25QXX_CS(0);                            // 使能器件
         W25QXX_WriteByte(W25X_ReleasePowerDown); // 发送唤醒命令
         W25QXX_CS(1);                            // 取消片选
         HAL_Delay(1);                            // 等待TRES1
     }
    
     void W25QXX_Init(void) {
         uint8_t retry = 0;
    
         W25QXX_CS(1);    // 取消片选
         W25QXX_WAKEUP(); // 唤醒
    
         for (retry = 0; retry < 3; retry++) // 尝试3次
         {
             uint16_t flashID = W25QXX_ReadID();
             if (flashID != 0) // W25Q128的ID
             {
             //   printf("Flash ID: 0x%02X \r\n", flashID);
             //   printf("W25QXX INIT SUCCESS! \r\n");
             break;
             }
         }
    
         if (retry == 3) // 检测失败
         {
             // 可以在这里添加错误处理代码
             printf("试了3次,还是找不到设备");
         }
     }


2. 其它相关的操作函数包括:

```c
    /* 函数声明 */
    void W25QXX_Init(void);
    void W25QXX_CS(uint8_t level);
    uint8_t W25QXX_ReadByte(void);
    void W25QXX_WriteByte(uint8_t txData);
    uint16_t W25QXX_ReadID(void);
    void W25QXX_Read(uint8_t* pBuffer, uint32_t ReadAddr, uint16_t NumByteToRead);
    void W25QXX_Write(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
    void W25QXX_Write_Page(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
    void W25QXX_Write_NoCheck(uint8_t* pBuffer, uint32_t WriteAddr, uint16_t NumByteToWrite);
    void W25QXX_Erase_Sector(uint32_t SectorAddr);
    void W25QXX_Erase_Block32K(uint32_t BlockAddr);
    void W25QXX_Erase_Block64K(uint32_t BlockAddr);
    void W25QXX_Erase_Chip(void);
    void W25QXX_Wait_Busy(void);
    void W25QXX_WriteEnable(void);
    void W25QXX_WriteDisable(void);
    uint8_t W25QXX_ReadStatusReg1(void);
    uint8_t W25QXX_ReadStatusReg2(void);
    uint8_t W25QXX_ReadStatusReg3(void);
    void W25QXX_WriteStatusReg(uint8_t status);
    void W25QXX_PowerDown(void);
    void W25QXX_WAKEUP(void);

三. W25Qxx 芯片的核心概念

W25Q64共有8M字节的存储空间,W25Q64的存储结构由页、扇区和块三级组织构成:‌
  1. 页(Page)‌:每页大小为256字节,是写入的最小单位。
  2. 扇区(Sector)‌:每个扇区大小为4 KB(包含16页)。),是擦除的最小单位。
  3. ‌块(Block)‌:每个块大小为64 KB(包含16个扇区)。),是较大规模擦除操作的单位。
  4. ‌具体数量计算如下:‌ 总容量8 MByte(8,388,608字节)可分解为:
    ‌页总数‌:8,388,608字节 ÷ 256字节/页 = ‌32,768页‌。
    ‌扇区总数‌:8,388,608字节 ÷ 4,096字节/扇区 = ‌2,048扇区‌(或通过页数计算:32,768页 ÷ 16页/扇区)。
    ‌块总数‌:8,388,608字节 ÷ 65,536字节/块 = ‌128块‌(或通过扇区数计算:2,048扇区 ÷ 16扇区/块)。

四.擦除和写入的规则

  1. 最小的写入单位: 字节。你可以写入单个或多个字节。
  2. 最小的擦除单位: 扇区。这是最关键的限制!W25Q64 不支持以字节为单位擦除。
    • 标准扇区 (Sector): 4KB。这是最常用的擦除单位。
    • 块 (Block): 32KB 或 64KB。用于大范围擦除,效率更高。
    • 整片 (Chip): 擦除整个芯片。
  3. 操作顺序:
    • 如果目标地址所在扇区的数据已经是全0xFF,则可以直接写入。
    • 如果目标地址所在扇区存有非0xFF的数据,则必须:
      1. 将整个扇区(例如4KB)的数据读出来,暂存到MCU的RAM中。
      2. 擦除整个扇区,可能会耗时较长
      3. 在RAM中修改需要更新的数据。
      4. 将修改后的整个扇区数据(4KB)重新写入该扇区。

五.W25Qxx 芯片的核心代码

六.W25Qxx 工程完整代码