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