cancel
Showing results for 
Search instead for 
Did you mean: 

STM32L4 qspi: reading registers

Posted on March 14, 2018 at 13:28

Hello there,

I am using STM32L4 series MCU and I am trying to interface with a M25Q Micron memory using QSPI. Following the examples given from ST I have noticed that there are no HAL functions allowing registers reading, that is:

  • READ STATUS REGISTER (05h),
  • READ FLAG STATUS REGISTER (70h),
  • READ NONVOLATILE CONFIGURATION REGISTER (B5h).

and etc.

In the examples the &sharpdefines for those commands are defined, but never used. Looking into the stm32l4xx_hal_qspi.h/c files, there is no read register command. All I could find is AutoPolling, which I am not sure either it works.

Does those functionalities need to be implemented by myself? I would appreciate all help.

#quad-spi #qspi-flash
43 REPLIES 43

The CubeL4 code trees should be full of working examples, for working boards.

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

@John​ Last time i checked this code worked (havent used it for a while):

/**
 * @brief	Pools a command and receives data. Finishes after the \ref mask and \ref match are OK.
 * @param 	cmd: command to auto pool with.
 * @param	bytes: amount of bytes to pool.
 * @param 	mask: masking bits.
 * @param 	match: match bits.
 * @return	HAL_OK on success.
 */
HAL_StatusTypeDef qspi_autoPool(const qspiCmd_t cmd, const uint32_t bytes,
		const uint32_t mask, const uint32_t match)
{
	// check how many status bytes to read
	if ((bytes < QSPI_MIN_STATUS_BYTES) || (bytes > QSPI_MAX_STATUS_BYTES))
		return HAL_ERROR;
 
	QSPI_CommandTypeDef command;
	QSPI_AutoPollingTypeDef config;
 
	qspi_setCommonCmdAndCfg(&command, &config);
 
	// Configure automatic polling mode to wait for memory ready
	command.Instruction		= cmd;
	command.AddressMode		= QSPI_ADDRESS_NONE;
 
	config.Match			= match;
	config.Mask				= mask;
	config.StatusBytesSize	= bytes;
 
	if (util_isDmaAvailable()) // wait in non blocking mode
	{
		osSemaphoreWait(qspi.semaStat, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
		if (HAL_OK != HAL_QSPI_AutoPolling_IT(qspi.hqspi, &command, &config))
			return HAL_ERROR;
 
		osSemaphoreWait(qspi.semaStat, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
		osSemaphoreRelease(qspi.semaStat);
 
		return HAL_OK;
	}
 
	return HAL_QSPI_AutoPolling(qspi.hqspi, &command, &config, HAL_QPSI_TIMEOUT_DEFAULT_VALUE);
}

John3
Associate II

Hi,

Thank you so much for the reply.

I am able to read ID (READ_ID_CMD2    0x9F) . I am getting 0xC2 0x20 0x18.

Now I am trying to write and read some data to/from MT25QL128 memory using Quad spi.

I am using API: "BSP_QSPI_Write" to write 256 bytes of data at 0x90000000 address and API "BSP_QSPI_Read" to read data from memory. But I am getting all 0xFF data.

Could you please help me to figure out the issue.

Thanks,

John

Hi,

The address 0x90000000 is only valid when operating in XIP mode. You are operating with the memory "manually". It means if you want to write something to the beginning of the memory, you need to address the address 0x00000000.

John3
Associate II

Hi,

Thank you so much,

I tried to write and read from address 0x00000000 also getting 0xFF. what could be the issue?

Thanks,

John

Hi,
By default all bytes in the flash menory is set to 0xFF. After writing something other than 0xFF to the memory you should be able to read it back instead of 0xFF's. Maybe there is an error in your read/ write procedures?
John3
Associate II

Hi,

I am using STMcubeL4 example code for read/write operation.

Below is the code using to write/read data. I am able to read ID.

uint8_t BSP_QSPI_Init(void)

{

 QSPIHandle.Instance = QUADSPI;

 /* Call the DeInit function to reset the driver */

 if (HAL_QSPI_DeInit(&QSPIHandle) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 /* QSPI initialization */

 QSPIHandle.Init.ClockPrescaler   = 1; /* QSPI clock = 80MHz / (ClockPrescaler+1) = 40MHz */

 QSPIHandle.Init.FifoThreshold   = 4;

 QSPIHandle.Init.SampleShifting   = QSPI_SAMPLE_SHIFTING_HALFCYCLE;

 QSPIHandle.Init.FlashSize     = POSITION_VAL(MT25Q128A_FLASH_SIZE) - 1;

 QSPIHandle.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;

 QSPIHandle.Init.ClockMode     = QSPI_CLOCK_MODE_0;

 //QSPIHandle.Init.FlashID      = QSPI_FLASH_ID_1;

 //QSPIHandle.Init.DualFlash     = QSPI_DUALFLASH_DISABLE;

  

 if (HAL_QSPI_Init(&QSPIHandle) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 /* QSPI memory reset */

 if (QSPI_ResetMemory(&QSPIHandle) != QSPI_OK)

 {

  return QSPI_NOT_SUPPORTED;

 }

 /* Configuration of the dummy cucles on QSPI memory side */

 if (QSPI_DummyCyclesCfg(&QSPIHandle) != QSPI_OK)

 {

  return QSPI_NOT_SUPPORTED;

 }

  

 if (QSPI_TestReadId(&QSPIHandle) != QSPI_OK)

 {

  return QSPI_NOT_SUPPORTED;

 }

 for (int i = 0; i < 16; i++)

  wbuf[i] = i;

 if (BSP_QSPI_Write(&wbuf[0], 0x00000000, 16) != QSPI_OK)

 {

  return QSPI_ERROR;

 }

 if (BSP_QSPI_Read(&rbuf[0], 0x00000000, 16) != QSPI_OK)

 {

  return QSPI_ERROR;

 }

 return QSPI_OK;

}

uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)

{

 QSPI_CommandTypeDef sCommand;

 uint32_t end_addr, current_size, current_addr;

 /* Calculation of the size between the write address and the end of the page */

 current_addr = 0;

 while (current_addr <= WriteAddr)

 {

  current_addr += MT25Q128A_PAGE_SIZE;

 }

 current_size = current_addr - WriteAddr;

 /* Check if the size of the data is less than the remaining place in the page */

 if (current_size > Size)

 {

  current_size = Size;

 }

 /* Initialize the adress variables */

 current_addr = WriteAddr;

 end_addr = WriteAddr + Size;

 /* Initialize the program command */

 sCommand.InstructionMode  = QSPI_INSTRUCTION_1_LINE;

 sCommand.Instruction    = EXT_QUAD_IN_FAST_PROG_CMD;

 sCommand.AddressMode    = QSPI_ADDRESS_4_LINES;

 sCommand.AddressSize    = QSPI_ADDRESS_24_BITS;

 sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

 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;

 /* Perform the write page by page */

 do

 {

  sCommand.Address = current_addr;

  sCommand.NbData = current_size;

  /* Enable write operations */

  if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK)

  {

   return QSPI_ERROR;

  }

  /* Configure the command */

  if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

  {

   return QSPI_ERROR;

  }

  /* Transmission of the data */

  if (HAL_QSPI_Transmit(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

  {

   return QSPI_ERROR;

  }

  /* Configure automatic polling mode to wait for end of program */

  if (QSPI_AutoPollingMemReady(&QSPIHandle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)

  {

   return QSPI_ERROR;

  }

  /* Update the address and size variables for next page programming */

  current_addr += current_size;

  pData += current_size;

  current_size = ((current_addr + MT25Q128A_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : MT25Q128A_PAGE_SIZE;

 }

 while (current_addr < end_addr);

 return QSPI_OK;

}

/**

 * @brief Reads an amount of data from the QSPI memory.

 * @param pData: Pointer to data to be read

 * @param ReadAddr: Read start address

 * @param Size: Size of data to read

 * @retval QSPI memory status

 */

uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)

{

 QSPI_CommandTypeDef sCommand;

 /* Initialize the read command */

 sCommand.InstructionMode  = QSPI_INSTRUCTION_1_LINE;

 sCommand.Instruction    = QUAD_INOUT_FAST_READ_CMD;

 sCommand.AddressMode    = QSPI_ADDRESS_4_LINES;

 sCommand.AddressSize    = QSPI_ADDRESS_24_BITS;

 sCommand.Address      = ReadAddr;

 sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

 sCommand.DataMode     = QSPI_DATA_4_LINES;

 sCommand.DummyCycles    = MT25Q128A_DUMMY_CYCLES_READ_QUAD;

 sCommand.NbData      = Size;

 sCommand.DdrMode      = QSPI_DDR_MODE_DISABLE;

 sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

 sCommand.SIOOMode     = QSPI_SIOO_INST_EVERY_CMD;

 /* Configure the command */

 if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 /* Reception of the data */

 if (HAL_QSPI_Receive(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 return QSPI_OK;

}

Could you please help to check write and read API, is there anything wrong ?

Thanks,

john

Make sure the dummy cycles are set correctly for your memory type. Also make sure if you are reading in 1 bit command mode that 4 bits commands was not turned on. You can read status to check that. Sorry but dont ha e the time to check all thia code.
John3
Associate II

Hi,

Now I am able to read 0x88 data not the data I am writing it. I have checked all dummy cycle and command mode, but still no luck.

Please help to check below code:

/**

 * @brief Writes an amount of data to the QSPI memory.

 * @param pData: Pointer to data to be written

 * @param WriteAddr: Write start address

 * @param Size: Size of data to write

 * @retval QSPI memory status

 */

uint8_t BSP_QSPI_Write(uint8_t *pData, uint32_t WriteAddr, uint32_t Size)

{

 QSPI_CommandTypeDef sCommand;

 uint32_t end_addr, current_size, current_addr;

 /* Calculation of the size between the write address and the end of the page */

 current_addr = 0;

 while (current_addr <= WriteAddr)

 {

  current_addr += MT25Q128A_PAGE_SIZE;

 }

 current_size = current_addr - WriteAddr;

 /* Check if the size of the data is less than the remaining place in the page */

 if (current_size > Size)

 {

  current_size = Size;

 }

 /* Initialize the adress variables */

 current_addr = WriteAddr;

 end_addr = WriteAddr + Size;

 /* Initialize the program command */

 sCommand.InstructionMode  = QSPI_INSTRUCTION_1_LINE;

 sCommand.Instruction    = PAGE_PROG_CMD;

 sCommand.AddressMode    = QSPI_ADDRESS_4_LINES;

 sCommand.AddressSize    = QSPI_ADDRESS_24_BITS;

 sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

 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;

 /* Perform the write page by page */

 do

 {

  sCommand.Address = current_addr;

  sCommand.NbData = current_size;

  /* Enable write operations */

  if (QSPI_WriteEnable(&QSPIHandle) != QSPI_OK)

  {

   return QSPI_ERROR;

  }

  /* Configure the command */

  if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

  {

   return QSPI_ERROR;

  }

  /* Transmission of the data */

  if (HAL_QSPI_Transmit(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

  {

   return QSPI_ERROR;

  }

  /* Configure automatic polling mode to wait for end of program */

  if (QSPI_AutoPollingMemReady(&QSPIHandle, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != QSPI_OK)

  {

   return QSPI_ERROR;

  }

  /* Update the address and size variables for next page programming */

  current_addr += current_size;

  pData += current_size;

  current_size = ((current_addr + MT25Q128A_PAGE_SIZE) > end_addr) ? (end_addr - current_addr) : MT25Q128A_PAGE_SIZE;

 }

 while (current_addr < end_addr);

 return QSPI_OK;

}

/**

 * @brief Reads an amount of data from the QSPI memory.

 * @param pData: Pointer to data to be read

 * @param ReadAddr: Read start address

 * @param Size: Size of data to read

 * @retval QSPI memory status

 */

uint8_t BSP_QSPI_Read(uint8_t *pData, uint32_t ReadAddr, uint32_t Size)

{

 QSPI_CommandTypeDef sCommand;

 /* Initialize the read command */

 sCommand.InstructionMode  = QSPI_INSTRUCTION_1_LINE;

 sCommand.Instruction    = READ_CMD;

 sCommand.AddressMode    = QSPI_ADDRESS_4_LINES;

 sCommand.AddressSize    = QSPI_ADDRESS_24_BITS;

 sCommand.Address      = ReadAddr;

 sCommand.AlternateByteMode = QSPI_ALTERNATE_BYTES_NONE;

 sCommand.DataMode     = QSPI_DATA_4_LINES;

 sCommand.DummyCycles    = MT25Q128A_DUMMY_CYCLES_READ_QUAD;

 sCommand.NbData      = Size;

 sCommand.DdrMode      = QSPI_DDR_MODE_DISABLE;

 sCommand.DdrHoldHalfCycle = QSPI_DDR_HHC_ANALOG_DELAY;

 sCommand.SIOOMode     = QSPI_SIOO_INST_EVERY_CMD;

 /* Configure the command */

 if (HAL_QSPI_Command(&QSPIHandle, &sCommand, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 /* Reception of the data */

 if (HAL_QSPI_Receive(&QSPIHandle, pData, HAL_QPSI_TIMEOUT_DEFAULT_VALUE) != HAL_OK)

 {

  return QSPI_ERROR;

 }

 return QSPI_OK;

}

Thanks,

John

John3
Associate II

Hi Lukasz,

Still I am not able to read and write from qspi MT25ql128 serial flash.

could you please help me out or share example code?

Thanks,

John