STM32WB QSPI FLASH - Winbond(W25N01GV)
Hello,
I have been trying to make a driver for the Winbond(W25N01GV) NAND flash over QSPI but I've been failing so far. Can somebody give me some ideas about what I am doing wrong?

#define UNPACK_UINT16_TO_2_BYTES(num) {(uint8_t) (((num) & 0xFF00) >> 8), (uint8_t) ((num) & 0x00FF)}
#define PACK_2_BYTES_TO_UINT16(bytes) ((((uint16_t) *(bytes)) << 8) + *((bytes)+1))
bool isBusy(void)
{
uint8_t reg_data = 0;
QSPI_CommandTypeDef com;
com.InstructionMode = QSPI_INSTRUCTION_1_LINE; // QSPI_INSTRUCTION_...
com.Instruction = W25N01GV_STATUS_REG;
com.AddressMode = QSPI_ADDRESS_1_LINE;
com.AddressSize = QSPI_ADDRESS_8_BITS;
com.Address = SR3_STATUS_REGISTER_ADDRESS;
com.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
com.AlternateBytes = QSPI_ALTERNATE_BYTES_NONE;
com.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
com.DummyCycles = 0;
com.DataMode = QSPI_DATA_1_LINE;
com.NbData = 1;
com.DdrMode = QSPI_DDR_MODE_DISABLE;
//com.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
com.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &com, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return true;
}
if (HAL_QSPI_Receive(&hqspi, ®_data, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return true;
}
bool bai = (reg_data & 0x01U);
return bai;
//return false;
}
uint8_t QspiFlashProgramExecute(uint16_t address)
{
while(isBusy())
HAL_Delay(1);
QSPI_CommandTypeDef command;
uint8_t block_add[2] = UNPACK_UINT16_TO_2_BYTES(address);
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.Instruction = 0x10U; // Program Execute
command.AddressMode = QSPI_ADDRESS_NONE;
command.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
command.DummyCycles = 8;
command.DataMode = QSPI_DATA_1_LINE;
command.NbData = 2;
command.DdrMode = QSPI_DDR_MODE_DISABLE;
//command.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
command.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &command, 1000U) != HAL_OK)
{
//UartSendText(&uart3, "ERROR HAL_QSPI_Command in QspiFlashProgramExecute\r");
return QSPI_ERROR;
}
if (HAL_QSPI_Transmit(&hqspi, block_add, 1000U) != HAL_OK)
{
//UartSendText(&uart3, "ERROR HAL_QSPI_Transmit in QspiFlashProgramData\r");
return QSPI_ERROR;
}
return QSPI_OK;
}
bool W25N_WriteEnable(bool enable)
{
while(isBusy())
HAL_Delay(1);
QSPI_CommandTypeDef com;
com.InstructionMode = QSPI_INSTRUCTION_1_LINE; // QSPI_INSTRUCTION_...
com.Instruction = enable ? W25N01GB_WRITE_ENABLE : W25N01GB_WRITE_DISABLE;
com.AddressMode = QSPI_ADDRESS_NONE;
com.AddressSize = QSPI_ADDRESS_NONE;
//com.Address = 0x0U;
com.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
com.AlternateBytes = QSPI_ALTERNATE_BYTES_NONE;
com.AlternateBytesSize = QSPI_ALTERNATE_BYTES_NONE;
com.DummyCycles = 0;
com.DataMode = QSPI_DATA_NONE;
com.NbData = 0;
com.DdrMode = QSPI_DDR_MODE_DISABLE;
//com.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
com.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &com, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
HAL_Delay(1);
return QSPI_OK;
}
/******************************************************ERASE*******************************************************/
uint8_t W25QSPIBlockErase(uint16_t blockAddress)
{
QSPI_CommandTypeDef sCommand = { 0 };
uint8_t ucSplitAddr[2] = UNPACK_UINT16_TO_2_BYTES(blockAddress);
blockAddress = blockAddress * W25N01GB_PAGE_BLOCK_COUNT_PER_PAGE;
/* Enable write operations */
while(isBusy())
HAL_Delay(1);
if (W25N_WriteEnable(true) != QSPI_OK)
{
return QSPI_ERROR;
}
/* Initialize the erase command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = W25N01GB_BLOCK_ERASE_CMD; //D8h
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.NbData = 2;
sCommand.DummyCycles = 8;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
//sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Send the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/*Send the Address*/
if (HAL_QSPI_Transmit(&hqspi, (uint8_t*) ucSplitAddr, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
while (W25Q_IsBusy() == W25Q_BUSY)
HAL_Delay(1);
return QSPI_OK;
}
//this function will write to a page from it's first address
uint8_t W25N0_QuadWritePage(uint16_t page, uint8_t *pData, uint16_t size)
{
if(size > W25N01GV_PAGE_SIZE)
{
return QSPI_ERROR;
}
QSPI_CommandTypeDef sCommand;
while (isBusy())
HAL_Delay(1);
/* Enable write operations */
if (W25N_WriteEnable(true) != QSPI_OK)
{
return QSPI_ERROR;
}
/* Initialize the program command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = QUAD_PAGE_PROG_CMD; //32h
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_16_BITS;
sCommand.Address = page;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.NbData = size;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
while (W25Q_IsBusy() == W25Q_BUSY)
HAL_Delay(1);
/* Transmission of the data */
if (HAL_QSPI_Transmit(&hqspi, (uint8_t*) pData, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
while (W25Q_IsBusy() == W25Q_BUSY)
HAL_Delay(1);
QspiFlashProgramExecute(page);
return QSPI_OK;
}
uint8_t W25N0_QuadReadPage(uint16_t pageAddr, uint8_t *pData, uint16_t size)
{
if (size > W25N01GV_PAGE_SIZE)
{
return QSPI_ERROR;
}
QSPI_CommandTypeDef sCommand = { 0 };
uint8_t block_add[2] = UNPACK_UINT16_TO_2_BYTES(pageAddr);
while (isBusy())
HAL_Delay(1);
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = W25N01GV_PAGE_READ_CMD; //13h
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.NbData = 2;
sCommand.DummyCycles = 8;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if (HAL_QSPI_Transmit(&hqspi, block_add, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
while (W25Q_IsBusy() == W25Q_BUSY)
HAL_Delay(1);
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = W25N01GV_QUAD_INOUT_READ_CMD; //0xEB
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.AddressSize = QSPI_ADDRESS_16_BITS;
sCommand.Address = pageAddr;
sCommand.DummyCycles = 4; //4
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.NbData = size;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Reception of the data */
if (HAL_QSPI_Receive(&hqspi, (uint8_t*) pData, HAL_QSPI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
}
