JEDEC有一个针对Nor Flash的标准,当然不是所有Nor Flash都符合这个标准.比如国产GD你说兼容不兼容,我就懒得说了.
下面我说的是镁光的65nm闪存N25Q128A,支持SFDP的.此身份证总长9 DWORD.为了更准确验证,我们应该写个程序.因为不是所有Flash都支持QSPI,DSPI所以用单线的验证下.特别是有些Flash必须人工写入某寄存器才能开启QSPI的...

指令固定是0x5A,因为事先假设不知道Flash型号,然后发24bit地址,8个dummy.然后返回长度是9个DWORD,这是根据SPDF的官方手册(JEDEC)写的,9 * 4 = 36Byte,然后0x0F - 0x2F是保留不使用的,所以总长是84个字节.不排除以后出现超过9个DWORD,那么程序就得改了,因为9 DWORD后面暂时没内容,所以暂时以9 DWORD来判断.先写个获取代码.
void BSP_QSPI_Read_SPDF(uint8_t *pData)
{
QSPI_CommandTypeDef sCommand;
/* Initialize the read command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = 0x5A;
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
sCommand.Address = 0x00000000;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.DummyCycles = 8;
sCommand.NbData = 84;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the command */
QSPI_Command(&sCommand);
/* Reception of the data */
QSPI_Receive(pData);
}
pData就是返回的数据,记得不要导致内存溢出了.明显,签名字节已经获取到了.

然后我做成个结构体,这样方便我自己访问.
typedef struct
{
uint32_t Signature;
uint16_t Revision;
uint8_t Number;
uint8_t RESERVED0;
uint8_t Parameter_ID;
uint8_t Parameter_Minor_Rev;
uint8_t Parameter_Major_Rev;
uint8_t Parameter_Length;
uint32_t PTP;
uint32_t dwData[9];
} BSP_QSPI_SFDP_TypeDef;
dwData就是真正简历部分,要通过PTP寻址.然后我的读取函数改成.
void BSP_QSPI_Read_SPDF(BSP_QSPI_SFDP_TypeDef *pID)
{
QSPI_CommandTypeDef sCommand;
uint8_t *pRxBuffPtr = (uint8_t *)pID;
/* Initialize the read command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = 0x5A;
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
sCommand.Address = 0x00000000;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.DummyCycles = 8;
sCommand.NbData = 16;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the command */
QSPI_Command(&sCommand);
/* Reception of the data */
QSPI_Receive(pRxBuffPtr);
pID->PTP = pID->PTP & 0xFFFFFF;
/* 此处获得对应位置指针. */
/* Initialize the read command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = 0x5A;
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_24_BITS;
sCommand.Address = pID->PTP;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.DummyCycles = 8;
sCommand.NbData = 36;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the command */
QSPI_Command(&sCommand);
/* Reception of the data */
QSPI_Receive(pRxBuffPtr + 0x10);
}
读取得到.

参考解码手册:
JESD216
解码第一个WORD(其他差不多):

主要代码:
#include "N25Q128.h"
#include "SEGGER_RTT.h"
BSP_QSPI_ID_TypeDef pId;
BSP_QSPI_SFDP_TypeDef pSFDP;
void SPIFFS_Main(void)
{
BSP_QSPI_Init();
BSP_QSPI_RDID(&pId);
BSP_QSPI_Read_SPDF(&pSFDP);
SEGGER_RTT_printf(0, "SFDP Detect!
");
if((pSFDP.dwData[0] & 0x03) == 0x01)
{
SEGGER_RTT_printf(0, "4KB Erase Support:YES
");
}
else
{
SEGGER_RTT_printf(0, "4KB Erase Support:NO
");
}
if((pSFDP.dwData[0] & 0x04) == 0x04)
{
SEGGER_RTT_printf(0, "64B Write Buffer:YES
");
}
else
{
SEGGER_RTT_printf(0, "64B Write Buffer:NO
");
}
if((pSFDP.dwData[0] & 0x08) == 0x08)
{
SEGGER_RTT_printf(0, "WRITE ENABLE command required for writing to volatile status registers:YES
");
}
else
{
SEGGER_RTT_printf(0, "WRITE ENABLE command required for writing to volatile status registers:NO
");
}
if((pSFDP.dwData[0] & 0x10) == 0x10)
{
SEGGER_RTT_printf(0, "WRITE ENABLE command code select for writing to volatile status register:YES
");
}
else
{
SEGGER_RTT_printf(0, "WRITE ENABLE command code select for writing to volatile status register:NO
");
}
if(pSFDP.dwData[0] & 0xFF00)
{
SEGGER_RTT_printf(0, "4KB ERASE command code:0x%02X
", (pSFDP.dwData[0] & 0xFF00) >> 8);
}
if((pSFDP.dwData[0] & 0x10000) == 0x10000)
{
SEGGER_RTT_printf(0, "Supports 1-1-2 fast read:YES
");
}
else
{
SEGGER_RTT_printf(0, "Supports 1-1-2 fast read:NO
");
}
if((pSFDP.dwData[0] & 0x60000) == 0x00000)
{
SEGGER_RTT_printf(0, "Address Bytes:3 Byte
");
}
else if((pSFDP.dwData[0] & 0x60000) == 0x20000)
{
SEGGER_RTT_printf(0, "Address Bytes:3 Byte + 4Byte
");
}
else if((pSFDP.dwData[0] & 0x60000) == 0x40000)
{
SEGGER_RTT_printf(0, "Address Bytes:4Byte
");
}
if((pSFDP.dwData[0] & 0x80000) == 0x80000)
{
SEGGER_RTT_printf(0, "Supports Double Transfer Rate (DTR) Clocking:YES
");
}
else
{
SEGGER_RTT_printf(0, "Supports Double Transfer Rate (DTR) Clocking:NO
");
}
if((pSFDP.dwData[0] & 0x100000) == 0x100000)
{
SEGGER_RTT_printf(0, "Supports (1-2-2) Fast Read:YES
");
}
else
{
SEGGER_RTT_printf(0, "Supports (1-2-2) Fast Read:NO
");
}
if((pSFDP.dwData[0] & 0x200000) == 0x200000)
{
SEGGER_RTT_printf(0, "Supports (1-4-4) Fast Read:YES
");
}
else
{
SEGGER_RTT_printf(0, "Supports (1-4-4) Fast Read:NO
");
}
if((pSFDP.dwData[0] & 0x400000) == 0x400000)
{
SEGGER_RTT_printf(0, "Supports (1-1-4) Fast Read:YES
");
}
else
{
SEGGER_RTT_printf(0, "Supports (1-1-4) Fast Read:NO
");
}
for(;;)
{
vTaskDelay(1);
}
}