2020-09-17 04:56 AM
Hi I have interfaced STM32L4 qspi with Winbond(W25N01GV). I am using HAL api's. The problem is erase/write to a page(2KB) address(say 0x0) repeats to the page after 512 KB(Say 0x80000) and so on.
This is a 128 MiB NAND flash and erase command(D8h) only available for erasing 128KiB block. But what I experienced like it is erasing 2KiB.
I will put my initialization below. I dig so much time on it to find where I goes wrong,
hqspi.Instance = QUADSPI;
hqspi.Init.ClockPrescaler = 2;
hqspi.Init.FifoThreshold = 4;
hqspi.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
hqspi.Init.FlashSize = 26;
hqspi.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
hqspi.Init.ClockMode = QSPI_CLOCK_MODE_0;
hqspi.Init.FlashID = QSPI_FLASH_ID_1;
hqspi.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
if (HAL_QSPI_Init(&hqspi) != HAL_OK)
{
Error_Handler();
}
Solved! Go to Solution.
2020-10-13 08:44 AM
@David Garcia . Woww, Thanks for the much elaborated reply.
I too checking in the similar fashion.
The difference is in the below areas,
I will check with the other commands and come up with the result/findings.
Regards,
SV
2020-10-13 09:01 AM
I skip your first step because my memory, by default, already has the value 0x18.
In the status register 1, I only leave the TB bit at 1 (0x04), but according to the table 7.4 of the datasheet, if the BP bits are 0, the value of TB does not matter.
Note that the 0x34 instruction does not reset the data buffer, that is, if you write only 500 bytes, the rest remain with the data that you programmed on the last page. However, the instruction 0x32 does reset the rest of the buffer.
Regards
2020-10-13 12:53 PM
Hi @David Garcia ,
I got some time to test it thoroughly now and here are the results,
I have now checked with 0x32 and 0xEB respectively , result is same.
[Note: The earlier behavior is (1. A data written to Page 0 appears in all the pages after every 512 KiB sections .(i.e. data written to 0x0 appears, in 0x80000, 0x100000 and so on. But data were retained after power-cycle and data @ one page not repeats in all other page, but repeats in a page after 512KiB).
Regards,
SV
2020-10-13 01:22 PM
Show the exact command forms you're sending, repeating blocks indicate you're not indexing through them correctly.
Power cycling will clear the internal RAM buffer.
2020-10-13 10:10 PM
Yes @Community member . It looks like , it is writing to and reading from internal buffer of NAND, not from the actual page.
Here how I do my write function,
uint8_t QuadWriteW25QSPIData(uint32_t ulAddr, uint8_t *pData, uint32_t ulSize)
{
QSPI_CommandTypeDef sCommand = {0};
uint32_t ulCount = 0;
uint32_t ulOffset = 0;
uint32_t ulAddress = 0;
uint8_t ucStatus = 0;
uint32_t ulDebug = 0;
/* Initialize the program command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
//sCommand.Instruction = QUAD_PAGE_PROG_FAST_CMD;
sCommand.Instruction = QUAD_PAGE_PROG_CMD;
sCommand.AddressMode = QSPI_ADDRESS_1_LINE;
sCommand.AddressSize = QSPI_ADDRESS_16_BITS;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = QSPI_ALTERNATE_BYTES_32_BITS;
sCommand.AlternateBytes = W25N01GV_ALT_BYTES_NO_PE_MODE;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
while (ulSize > 0)
{
ulCount = ulSize;
if ((ulAddr/W25N01GV_PAGE_SIZE) != ((ulAddr+ulSize)/W25N01GV_PAGE_SIZE))
{
ulCount = W25N01GV_PAGE_SIZE - (ulAddr % W25N01GV_PAGE_SIZE);
}
/* Enable write operations */
if (W25QSPIWriteEnable(&hqspi) != QSPI_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)return QSPI_ERROR;
//Set column address
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) % W25N01GV_PAGE_SIZE;
/*sCommand.Instruction = QUAD_PAGE_PROG_FAST_CMD;*/ //0x34h
sCommand.Instruction = QUAD_PAGE_PROG_CMD; //0x32h
sCommand.Address = ulAddress;
sCommand.NbData = ulCount;
sCommand.DummyCycles = 0;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.AlternateBytesSize = QSPI_ALTERNATE_BYTES_32_BITS;
sCommand.DataMode = QSPI_DATA_4_LINES;
ulDebug=ulAddress;
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Transmission of the data */
if (HAL_QSPI_Transmit(&hqspi,(uint8_t *)pData+ulOffset, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)return QSPI_ERROR;
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) / W25N01GV_PAGE_SIZE;// Page address
sCommand.Address = QSPI_ADDRESS_NONE;
/*sCommand.Address = ulAddress;*/
sCommand.Instruction = W25N01GV_PROG_EXE; //0x10
sCommand.DummyCycles = 8;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
/*sCommand.DataMode = QSPI_DATA_NONE;*/
sCommand.DataMode = QSPI_ADDRESS_1_LINE;
sCommand.NbData = ulAddress;
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)
{
CPrintf("Error Writing to \tPage:%d\tColumn:0x%x\n\r",ulAddress,ulDebug);
return QSPI_ERROR;
}
ucStatus=ReadQSPIStatusRegister(W25N01_QSPI_STAT_REG) & W25N01GV_PFAIL_BIT;
if(ucStatus)
{
CPrintf("Write Fail for \tPage:%d\tColumn:0x%x\n\r",ulAddress,ulDebug);
return QSPI_ERROR;
}
ulSize -= ulCount;
ulAddr += ulCount;
ulOffset += ulCount;
}
return QSPI_OK;
}
2020-10-14 12:07 AM
In the program execute you have to use the function HAL_QSPI_Command to send the command and immediately you have to use the function HAL_QSPI_Transmit and the data to transmit is the page address defined in the datasheet. The page address are two bytes: the six least significant bits are the page number and the rest are the block number (the 4 most significant bits are not used).
int QspiFlashProgramExecute(QSPI_HandleTypeDef *hqspi, uint16_t block, uint8_t page)
{
QSPI_CommandTypeDef command;
uint8_t block_add[2];
command.InstructionMode = QSPI_INSTRUCTION_1_LINE;
command.Instruction = 0x10; // 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;
uint32_t aux = CalculatePageAddress(block, page);
block_add[0] = (uint8_t) (aux >> 8);
block_add[1] = (uint8_t) aux;
if (HAL_QSPI_Command(hqspi, &command, 1000U) != HAL_OK)
{
UartSendText(&uart3, "ERROR HAL_QSPI_Command in QspiFlashProgramExecute\r");
return -1;
}
if (HAL_QSPI_Transmit(hqspi, block_add, 1000U) != HAL_OK)
{
UartSendText(&uart3, "ERROR HAL_QSPI_Transmit in QspiFlashProgramData\r");
return -1;
}
return 0;
}
Regards
2020-10-14 01:02 PM
Hi @David Garcia . Thanks for the code. I tried as what you have suggested and no luck for me. Now what I read is all FF, with/without erase or after writing anything. I writes/read/erases the address 0x0(so block 0, page 0, column 0). I shall copy my Erase , read and write functions here, If you can review it and find where I goes wrong, that will be much helpful.
/******************************************************ERASE*******************************************************/
/**
* @brief Erases the specified block of the QSPI memory.
* @param BlockAddress : page address 128KB from page
* @retval QSPI memory status
*/
uint8_t W25QSPIBlockErase(uint32_t ulPageAddress)
{
QSPI_CommandTypeDef sCommand = {0};
volatile uint8_t ucStatus;
uint8_t ucSplitAddr[2] = {0};
ulPageAddress = ulPageAddress*W25N01_PAGE_BLOCK_COUNT;
/* Enable write operations */
if (W25QSPIWriteEnable(&hqspi) != QSPI_OK)
{
return QSPI_ERROR;
}
/* Initialize the erase command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = BLOCK_ERASE_CMD; //D8H
sCommand.AddressMode = QSPI_ADDRESS_NONE;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
ucSplitAddr[0] = (uint8_t)(ulPageAddress>>8);
ucSplitAddr[1] = (uint8_t) ulPageAddress;
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_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/*Send the Address*/
if (HAL_QSPI_Transmit(&hqspi,(uint8_t *)ucSplitAddr, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != HAL_OK) //Checking busy bit
{
return QSPI_ERROR;
}
ucStatus = ReadQSPIStatusRegister(W25N01_QSPI_STAT_REG);
if (ucStatus & (EFAIL_FLAG))
{
//Erase Failure
ucStatus = 0;
#ifdef DEBUG_PRINT_ENABLED
CPrintf( "Block %d: Page:%d erase error.\n\r", ulPageAddress,ulPageAddress*W25N01_PAGE_BLOCK_COUNT);
#endif
return QSPI_ERROR;
}
return QSPI_OK;
}
/*************************************************WRITE*******************************************************************/
/**
* @brief Writes an amount of data to the QSPI memory in QUAD mode.
* @param pData : Pointer to data to be written
* @param ulAddr : Write start address
* @param ulSize : Size of data to write
* @retval QSPI memory status
*/
uint8_t QuadWriteW25QSPIData(uint32_t ulAddr, uint8_t *pData, uint32_t ulSize)
{
QSPI_CommandTypeDef sCommand = {0};
uint32_t ulCount = 0;
uint32_t ulOffset = 0;
uint32_t ulAddress = 0;
uint8_t ucStatus = 0;
uint32_t ulDebug = 0;
uint8_t ucSplitAddr[2] = {0};
while (ulSize > 0)
{
ulCount = ulSize;
if ((ulAddr/W25N01GV_PAGE_SIZE) != ((ulAddr+ulSize)/W25N01GV_PAGE_SIZE))
{
ulCount = W25N01GV_PAGE_SIZE - (ulAddr % W25N01GV_PAGE_SIZE);
}
/* Enable write operations */
if (W25QSPIWriteEnable(&hqspi) != QSPI_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)return QSPI_ERROR;
//Set column address
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) % W25N01GV_PAGE_SIZE;
/* 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 = ulAddress;
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.DummyCycles = 0;
sCommand.NbData = ulCount;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
ulDebug = ulAddress; //column addr for Debug print
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Transmission of the data */
if (HAL_QSPI_Transmit(&hqspi,(uint8_t *)pData+ulOffset, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)return QSPI_ERROR;
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) / W25N01GV_PAGE_SIZE;// Page address
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = W25N01GV_PROG_EXE;
sCommand.Address = QSPI_ADDRESS_NONE;
sCommand.DataMode = QSPI_DATA_1_LINE;
sCommand.NbData = 2;
sCommand.DummyCycles = 8;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
ucSplitAddr[0] = (uint8_t)(ulAddress>>8);
ucSplitAddr[1] = (uint8_t)ulAddress;
/* Configure the command */
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if (HAL_QSPI_Transmit(&hqspi,(uint8_t *)ucSplitAddr, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)
{
CPrintf("Error Writing to \tPage:%d\tColumn:0x%x\n\r",ulAddress,ulDebug);
return QSPI_ERROR;
}
ucStatus=ReadQSPIStatusRegister(W25N01_QSPI_STAT_REG) & W25N01GV_PFAIL_BIT;
if(ucStatus)
{
CPrintf("Write Fail for \tPage:%d\tColumn:0x%x\n\r",ulAddress,ulDebug);
return QSPI_ERROR;
}
#ifdef DEBUG_PRINT_ENABLED
CPrintf("Writing to \tPage:%d\tColumn:0x%x\n\r",ulAddress,ulDebug);
#endif
ulSize -= ulCount;
ulAddr += ulCount;
ulOffset += ulCount;
}
return QSPI_OK;
}
2020-10-14 01:03 PM
/********************************************************READ************************************************************/
/**
* @brief Reads an amount of data from the QSPI memory in QUAD mode.
* @param pData : Pointer to data to be written
* @param ulAddr : Write start address
* @param ulSize : Size of data to write
* @retval QSPI memory status
*/
uint8_t QuadReadW25QSPIdata(uint32_t ulAddr, uint8_t *pData, uint32_t ulSize)
{
uint32_t ulAddress = 0;
uint32_t ulCount = 0;
uint32_t ulOffset = 0;
QSPI_CommandTypeDef sCommand = {0};
uint8_t ucStatus = 0;
uint32_t ulDebugAddr = 0;
uint8_t ucSplitAddr[2] = {0};
while (ulSize > 0)
{
ulCount = ulSize;
if ((ulAddr/W25N01GV_PAGE_SIZE) != ((ulAddr+ulSize)/W25N01GV_PAGE_SIZE))
{
ulCount = W25N01GV_PAGE_SIZE - (ulAddr % W25N01GV_PAGE_SIZE);
}
//Set page address
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) / W25N01GV_PAGE_SIZE;
ulDebugAddr=ulAddress;
/* Initialize the read command */
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = 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 = W25N01GV_DUMMY_CYCLES_READ; //8
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
ucSplitAddr[0] = (uint8_t)(ulAddress>>8);
ucSplitAddr[1] = (uint8_t)ulAddress;
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if (HAL_QSPI_Transmit(&hqspi,(uint8_t *)ucSplitAddr, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
if(W25QSPIWait4Idle() != QSPI_OK)
{
return QSPI_ERROR;
}
//Set column address
ulAddress = (ulAddr % W25N01GV_FLASH_SIZE) % W25N01GV_PAGE_SIZE;
sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
sCommand.Instruction = QUAD_INOUT_READ_CMD;//0xEB
sCommand.AddressMode = QSPI_ADDRESS_4_LINES;
sCommand.AddressSize = QSPI_ADDRESS_16_BITS;
sCommand.Address = ulAddress;
sCommand.DummyCycles = W25N01GV_DUMMY_CYCLES_4READ; //4
sCommand.DataMode = QSPI_DATA_4_LINES;
sCommand.NbData = ulCount;
sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;
sCommand.DdrMode = QSPI_DDR_MODE_DISABLE;
sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;
sCommand.SIOOMode = QSPI_SIOO_INST_EVERY_CMD;
if (HAL_QSPI_Command(&hqspi, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
/* Reception of the data */
if (HAL_QSPI_Receive(&hqspi, (uint8_t *)(pData+ulOffset), HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)
{
return QSPI_ERROR;
}
ucStatus=ReadQSPIStatusRegister(W25N01_QSPI_STAT_REG) & W25N01GV_READ_ECC_CHECK;
#ifdef DEBUG_PRINT_ENABLED
if(ucStatus)
{
CPrintf("ECC Read ERROR@\tPage:%d\tColumn:0x%x\n\r",ulDebugAddr,ulAddress);
return QSPI_ERROR;
}
else
{
CPrintf("Reading Done @\tPage:%d\tColumn:0x%x\n\r",ulDebugAddr,ulAddress);
}
#endif
ulSize -= ulCount;
ulAddr += ulCount;
ulOffset += ulCount;
}
return QSPI_OK;
}
2020-10-14 01:14 PM
The 0x10 (PROGRAM EXECUTE) expects a 24-bit address, the top 8-bits being a dummy to generate the "dummy-cycles" before the address, rather than after. ie 24-bit address, no dummy, no data
Or as David does it, no address, 8-dummy, the 16-bit of data containing the page address.
2020-10-14 01:39 PM
@Community member . Yes, It will be like that for where ever the page address comes to play , ryt?. I have now modified it as what Dave suggested(command instruction and address as data transmit). Will check with 24b addressing as you said (so not needed dummy and data)