cancel
Showing results for 
Search instead for 
Did you mean: 

QSPI of STM32L496A

Vsv.1
Associate II

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

 }

24 REPLIES 24

@David Garcia​ . Woww, Thanks for the much elaborated reply.

I too checking in the similar fashion.

The difference is in the below areas,

  1. I enables the Buff read mode and ECC enable to status register 2(0x18).
  2. To unprotect everything writing (0x00) to status register 1.
  3. checking with a data buffer of 2.5K to block 0, page 0(2048 bytes) page1(remaining),using 0xD8 for erase, 0x34 for program and 0x6B for read.

I will check with the other commands and come up with the result/findings.

Regards,

SV

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

​Hi @David Garcia​ ,

I got some time to test it thoroughly now and here are the results,

  1. When I write and read back one page(page 0), i.e.2048 Bytes - everything is correct.
  2. Then if I go out of the box and read any other page (page1, page2..page64.....page256) I can see a copy of same data written to page0. i.e. Writing to one page reflects in every other page.
  3. Also , If I writes to a page(erase->write->read and verified data), then power-cycle and read(without erase or write), the whole data appears to be 0xFF(i.e. The data not retains ).

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

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

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;

}

  1. @David Garcia​ , now I noticed you wrote in step 13 ( Program execute (0x10) with the address of the block you've just erased and the page 0 (this address goes in the data field).). Does that means I need to send "HAL_QSPI_Command" two times, first with block address and then with page address. Please check the above write function. If so, will it be applicable to erase as well.

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

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;

}

/********************************************************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;

}

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.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..

@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)