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