W25Q64JV的标准/双/四SPl指令集由48条基本指令组成,这些指令通过SPI总线完全控制(见指令集表1-2)。
 指令从芯片选择(/CS)的下降沿开始。时钟输入DI输入的第一字节数据提供指令代码。DI输入上的数据在时钟的上升沿上采样,
 最高有效位(MSB)优先。指令的长度从单个字节到几个字节不等,后面可能是地址字节、数据字节、虚拟字节(无需在意),
 在某些情况下还可能是组合。指令以edge/CS的上升沿完成。每条指令的时钟相对时序图如图5至57所示。所有读取指令都可以在
 任何时钟位之后完成。但是,所有写、编程或擦除指令都必须在字节边界上完成(/CS驱动高电平,在时钟位满8位之后),
 否则该指令将被忽略。此功能进一步保护设备免受无意写入。此外,当存储器被编程或擦除时,或者当状态寄存器被写入时,
 除了读取状态寄存器之外的所有指令都将被忽略,直到编程或擦除周期完成。

读写接口都是先发送cmd,即是表中的byte1,而后发送或者接收信息,SPI访问接口这里暂不延升。
#define LOAD_CMD(a, cmd, addr)  do  {                                       \
                                        (a)[0U] = (cmd);                    \
                                        (a)[1U] = (uint8_t)((addr) >> 16U); \
                                        (a)[2U] = (uint8_t)((addr) >> 8U);  \
                                        (a)[3U] = (uint8_t)(addr);          \
                                    } while (0U)
static void W25QXX_Wt(uint8_t u8Cmd, uint32_t u32Address, const uint8_t *pu8Data, uint32_t u32DataLength)
{
    uint8_t au8Cmd[4U];
    LOAD_CMD(au8Cmd, u8Cmd, u32Address);
    W25Q_CS_ACTIVE();
    (void)BSP_W25Q_SPI_Transmit(au8Cmd, 4U);
    (void)BSP_W25Q_SPI_Transmit(pu8Data, u32DataLength);
    W25Q_CS_INACTIVE();
}
static void W25QXX_Rd(uint8_t u8Cmd, uint32_t u32Address, uint8_t *pu8Data, uint32_t u32DataLength)
{
    uint8_t au8Cmd[4U];
    LOAD_CMD(au8Cmd, u8Cmd, u32Address);
    W25Q_CS_ACTIVE();
    (void)BSP_W25Q_SPI_Transmit(au8Cmd, 4U);
    (void)BSP_W25Q_SPI_Receive(pu8Data, u32DataLength);
    W25Q_CS_INACTIVE();
}
static void W25QXX_WriteCmd(uint8_t u8Cmd, const uint8_t *pu8CmdData, uint32_t u32CmdDataLength)
{
    W25Q_CS_ACTIVE();
    (void)BSP_W25Q_SPI_Transmit(&u8Cmd, 1U);
    (void)BSP_W25Q_SPI_Transmit(pu8CmdData, u32CmdDataLength);
    W25Q_CS_INACTIVE();
}
static void W25QXX_ReadCmd(uint8_t u8Cmd, uint8_t *pu8CmdData, uint32_t u32CmdDataLength,
                         uint8_t *pu8Info, uint8_t u8InfoLength)
{
    W25Q_CS_ACTIVE();
    (void)BSP_W25Q_SPI_Transmit(&u8Cmd, 1U);
    (void)BSP_W25Q_SPI_Transmit(pu8CmdData, u32CmdDataLength);
    (void)BSP_W25Q_SPI_Receive(pu8Info, (uint32_t)u8InfoLength);
    W25Q_CS_INACTIVE();
}define W25Q_WRITE_ENABLE ((uint8_t)0x06U)
#define W25Q_VOLATILE_SR_WRITE_ENABLE ((uint8_t)0x50U)
#define W25Q_WRITE_DISABLE ((uint8_t)0x04U)
#define W25Q_READ_STATUS_REG_1 ((uint8_t)0x05U)
#define W25Q_READ_STATUS_REG_2 ((uint8_t)0x35U)
#define W25Q_WRITE_STATUS_REG ((uint8_t)0x01U)
#define W25Q_PAGE_PROGRAM ((uint8_t)0x02U)
#define W25Q_SECTOR_ERASE ((uint8_t)0x20U)
#define W25Q_BLOCK_ERASE_32K ((uint8_t)0x52U)
#define W25Q_BLOCK_ERASE_64K ((uint8_t)0xD8U)
#define W25Q_CHIP_ERASE ((uint8_t)0xC7U)
#define W25Q_ERASE_PROGRAM_SUSPEND ((uint8_t)0x75U)
#define W25Q_ERASE_PROGRAM_RESUME ((uint8_t)0x7AU)
#define W25Q_POWER_DOWN ((uint8_t)0xB9U)
#define W25Q_READ_DATA ((uint8_t)0x03U)
#define W25Q_FAST_READ ((uint8_t)0x0BU)
#define W25Q_DEVICE_ID ((uint8_t)0xABU)
#define W25Q_RELEASE_POWER_DOWN (W25Q_DEVICE_ID)
#define W25Q_MANUFACTURER_DEVICE_ID ((uint8_t)0x90U)
#define W25Q_JEDEC_ID ((uint8_t)0x9FU)
#define W25Q_READ_UNIQUE_ID ((uint8_t)0x4BU)
#define W25Q_READ_SFDP_REG ((uint8_t)0x5AU)
#define W25Q_REASE_SECURITY_REG ((uint8_t)0x44U)
#define W25Q_PROGRAM_SECURITY_REG ((uint8_t)0x42U)
#define W25Q_READ_SECURITY_REG ((uint8_t)0x48U)
#define W25Q_ENABLE_QPI ((uint8_t)0x38U)
#define W25Q_ENABLE_RESET ((uint8_t)0x66U)
#define W25Q_RESET ((uint8_t)0x99U)#define W25Q_ST_BUSY ((uint16_t)W25Q_BIT_0)
#define W25Q_ST_WEL ((uint16_t)W25Q_BIT_1)
1,使能去使能flash
static void W25QXX_WriteEnable(void)
{
    W25QXX_WriteCmd(W25Q_WRITE_ENABLE, NULL, 0U);
}
static void W25QXX_WriteDisable(void)
{
    W25QXX_WriteCmd(W25Q_WRITE_DISABLE, NULL, 0U);
}2,检测flash busy忙状态:
uint16_t W25QXX_ReadStatus(void)
{
    uint8_t  u8TempStatus;
    uint16_t u16RetStatus;
    W25QXX_ReadCmd(W25Q_READ_STATUS_REG_2, NULL, 0U, &u8TempStatus, 1U);
    u16RetStatus = u8TempStatus;
    W25QXX_ReadCmd(W25Q_READ_STATUS_REG_1, NULL, 0U, &u8TempStatus, 1U);
    u16RetStatus <<= 8U;
    u16RetStatus |= u8TempStatus;
    return u16RetStatus;
}
static void W25QXX_WaitBusy(void)
{
    while ((W25QXX_ReadStatus() & W25Q_ST_BUSY) == W25Q_ST_BUSY)
    {
        ;
    }
}3,flash的读写
static void W25QXX_WritePage(uint32_t u32Address, const uint8_t *pu8Data, uint32_t u32DataLength)
{
    W25QXX_WriteEnable();
    W25QXX_Wt(W25Q_PAGE_PROGRAM, u32Address, pu8Data, u32DataLength);
    W25QXX_WaitBusy();
}
void W25QXX_ReadData(uint32_t u32Address, uint8_t *pu8ReadBuf, uint32_t u32NumByteToRead)
{
    W25QXX_Rd(W25Q_READ_DATA, u32Address, pu8ReadBuf, u32NumByteToRead);
}
void W25QXX_WriteData(uint32_t u32Address, const uint8_t *pu8WriteBuf, uint32_t u32NumByteToWrite)
{
    uint32_t secpos;
    uint16_t secoff;
    uint16_t secremain;
    uint16_t i;
    uint8_t  *pW25QXX_BUF;
    pW25QXX_BUF = W25QXX_BUFFER;
    uint32_t u32WriteBufAddr = (uint32_t)&pu8WriteBuf;
    secpos     = u32Address / 4096U;
    secoff     = (uint16_t)(u32Address % 4096U);
    secremain  = 4096U - secoff;
    if (u32NumByteToWrite <= secremain)
    {
        secremain = (uint16_t)u32NumByteToWrite;
    }
    for(;;)
    {
        W25QXX_ReadData(secpos * 4096U, pW25QXX_BUF, 4096U); // read one sector content
        for (i = 0U; i < secremain; i++) // check if blank sector
        {
            if (pW25QXX_BUF[secoff + i] != (uint8_t)0xFFU)
            {
                break;
            }
        }
        if (i < secremain)
        {
            W25QXX_EraseSector(secpos);          // not blank, need erase
            for (i = 0U; i < secremain; i++)     // backup first
            {
                pW25QXX_BUF[i + secoff] = pu8WriteBuf[i];
            }
            W25QXX_Write_NoCheck(pW25QXX_BUF, secpos * 4096U, 4096U); // write back after erase
        }
        else
        {
            W25QXX_Write_NoCheck((const uint8_t *)u32WriteBufAddr, u32Address,secremain);
        }
        if (u32NumByteToWrite == secremain)
        {
            break;
        }
        else
        {
            secpos++;          // next sector
            secoff          = 0U;
            u32WriteBufAddr    += secremain;
            u32Address         += secremain;
            u32NumByteToWrite  -= secremain;
            if (u32NumByteToWrite > 4096U)
            {
                secremain = 4096U;
            }
            else
            {
                secremain = (uint16_t)u32NumByteToWrite;
            }
        }
    }
}
4,earse擦除flash
void W25QXX_EraseChip(void)
{
    W25QXX_WriteEnable();
    W25QXX_WaitBusy();
    W25QXX_WriteCmd(W25Q_CHIP_ERASE, NULL, 0U);
    W25QXX_WaitBusy();
}
void W25QXX_EraseSector(uint32_t u32SectorAddress)
{
    u32SectorAddress *= W25Q_SIZE_SECTOR;
    W25QXX_WriteEnable();
    W25QXX_WaitBusy();
    W25QXX_Wt(W25Q_SECTOR_ERASE, u32SectorAddress, NULL, 0U);
    W25QXX_WaitBusy();
    W25QXX_WriteDisable();
}
void W25QXX_EraseBlock(uint32_t u32BlockAddress)
{
    W25QXX_Wt(W25Q_BLOCK_ERASE_64K, u32BlockAddress, NULL, 0U);
}










![[星瞳科技]OpenMV如何进行wifi通信?](https://img-blog.csdnimg.cn/img_convert/bb7a2ca6c76a9bb0d30d44c2596273bd.jpeg)







