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
Hi,
I still think your number of lines for address/ data or dummy cycles are not correct. Also its not easy to help since you are not using the formatting tools provided in this forums as well as pasting the whole code for no reason. Leave only the needed functions of interest and format them using the code block.
John3
Associate II

Hi,

Ok thanks. I have checked all address, data and dummy cycles but didn't find anything wrong. Below I am sharing MT25ql128 data sheet command details and code I am using to write and read from serial flash. I have tried both READ & FAST READ command and dummy cycles as 0 and 10 respectively but in both cases getting 0x88 data.

0690X000006CDubQAG.png

/**

 * @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_4_LINES;

 sCommand.Instruction    = PAGE_PROG_CMD; // 0x02

 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_4_LINES;

 sCommand.Instruction    = READ_CMD; //0x03

 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    = 0;

 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, am I doing anything wrong in terms of address, data and dummy cycles.

Thanks,

John

Is this a custom board? How well validated is the design?

Define specific L4 part involved.

Define pins used in the QSPI interface.

Define pins and USART available for debugging output.

Define clock source being used for HSE, or MSI/PLL settings.

Please also note that there is a code pasting tool "</>" see icon below, and also a paper clip icon for attaching files.

Don't attach a dozen files. Where appropriate ZIP up a complete/viable project an attach as a single file.

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

Reading the status registers of the external memory in the mode of performing a write operation or erasing data is a very, very fast process for the user and insanely long for the processor. You read the status registers yourself, but by this time their state is already changing - this is a very fast process for a living person.

It sounds strange, but if you want to surprise the transients, then you have to read the status registers in a loop, and save the data to the log. So that you can read them later.

Also.

In order not to load the processor in the standby mode in the useless cycle of software reading of status registers, the QSPI peripheral block has a stream reading mode for data from one register, with automatic comparison of the result of reading through a mask. The mask has two registers - the bits that are significant for the operation are selected first (the remaining bits do not affect the result), the second register contains an expression for comparison. Registers have 32 bits. When reading data - they are stored and constantly shifted to a full byte, so you can catch the state of one-two-three-four bytes.

There are memory chips that give up one significant byte of the state - then you need to place 4 copies of this state in the comparison mask.

So there are memory chips that give from two to four bytes of status.

 At multiple quantities you need to have copies. When not a multiple of everything is much more fun. The state BEFORE the event is placed in the higher bytes, the lower one - AFTER the event. The comparison is continuous, and fully automatic. The trigger will automatically trigger an interrupt when a match event occurs.

And yet - you have to read the documentation for two chips: an external memory chip and a microprocessor. In order to programmatically access the periphery registers QSPI.

John3
Associate II
void SystemClock_Config(void)
{
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
 
  /* Enable HSE Oscillator and activate PLL with HSE as source   */
  /* (Default MSI Oscillator enabled at system reset remains ON) */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 1;
  RCC_OscInitStruct.PLL.PLLN = 20;
  RCC_OscInitStruct.PLL.PLLR = 2;
  RCC_OscInitStruct.PLL.PLLP = 7;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    /* Initialization Error */
    while(1);
  }
 
  /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2
     clocks dividers */
  RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
  {
    /* Initialization Error */
    while(1);
  }
}
 
 __weak void HAL_QSPI_MspInit(QSPI_HandleTypeDef *hqspi)
{
  /* Prevent unused argument(s) compilation warning */
  GPIO_InitTypeDef GPIO_InitStruct;
 
  /*##-1- Enable peripherals and GPIO Clocks #################################*/
  /* Enable the QuadSPI memory interface clock */
  __HAL_RCC_QSPI_CLK_ENABLE();
  /* Reset the QuadSPI memory interface */
  __HAL_RCC_QSPI_FORCE_RESET();
  __HAL_RCC_QSPI_RELEASE_RESET();
 
  /* Enable GPIO clocks */
  __HAL_RCC_GPIOE_CLK_ENABLE();
 
  /* QSPI CS GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_11;
  GPIO_InitStruct.Mode      = GPIO_MODE_AF_PP;
  GPIO_InitStruct.Pull      = GPIO_PULLUP;
  GPIO_InitStruct.Speed     = GPIO_SPEED_HIGH;
  GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* QSPI CLK GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_10;
  GPIO_InitStruct.Pull      = GPIO_NOPULL;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* QSPI D0 GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_12;
  GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* QSPI D1 GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_13;
  GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* QSPI D2 GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_14;
  GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
 
  /* QSPI D3 GPIO pin configuration  */
  GPIO_InitStruct.Pin       = GPIO_PIN_15;
  GPIO_InitStruct.Alternate = GPIO_AF10_QUADSPI;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  
  /* GPIO to power up qspi flash */
  GPIO_InitStruct.Mode  = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull  = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
  GPIO_InitStruct.Pin = GPIO_PIN_9;
  HAL_GPIO_Init(GPIOE, &GPIO_InitStruct);
  HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
}

Hi all,

Noted. Many thanks for the response.

I am checking both docs in details.

Define specific L4 part involved

  • STM32L486VG

Define pins used in the QSPI interface -

  • QSPI_NCS    QUADSPI_BK1_NCS PE11
  •   QSPI_D1     QUADSPI_BK1_IO1 PE13
  •   QSPI_D2     QUADSPI_BK1_IO2 PE14
  •   QSPI_D0     QUADSPI_BK1_IO0 PE12
  •   QSPI_CLK    QUADSPI_CLK   PE10
  •   QSPI_D3     QUADSPI_BK1_IO3 PE15
  • 3V3 PE9

Define pins and USART available for debugging output.

  • Yes, we have USART 1 for debugging purpose

Define clock source being used for HSE, or MSI/PLL settings.

  • I have define in pasting tools.

Thanks,

John

John3
Associate II

Hi,

Could anyone please help me to solve the issue?

Thanks,

John

John3
Associate II

Hi all,

After reading both documents of  external memory chip and a microprocessor, I am able to write and read data . but I am getting an first 2 bytes of data always as 0xFF.

what could cause an issue.

Thanks,

John

Hi,
Good to hear. Remember tl provide solution on the forums for the others. As for your current problem, probably inproper amount of command, address or data lines. Double check them in the datasheet.
John3
Associate II

Hi,

I am able to write and read the data from mt25ql memory. but the problem is I am able to read correct only when I erase the block I am reading from. I have disabled the reset memory command, even if I enable also gets same response.

I am not sure why I am getting an issue without calling "BSP_QSPI_Erase_Block" API.

I have pasted the code sequence:

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;
  
  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 cycles on QSPI memory side */
  if (QSPI_DummyCyclesCfg(&QSPIHandle) != QSPI_OK)
  {
    return QSPI_NOT_SUPPORTED;
  }
  if (QSPI_TestReadId(&QSPIHandle) != QSPI_OK)
  {
    return QSPI_NOT_SUPPORTED;
  }
  //works only when call  BSP_QSPI_Erase_Block()
  for (int i = 0; i < 256; i++)
    wbuf[i] = i;
  if (BSP_QSPI_Write(&wbuf[0], 0x00000000, 256) != QSPI_OK)
  {
    return QSPI_ERROR;
  }
  if (BSP_QSPI_Read(&rbuf[0], 0x00000000, 256) != QSPI_OK)
  {
    return QSPI_ERROR;
  }
  return QSPI_OK;
}

Thanks,

Jonh

John3
Associate II

Hi Luka,

I have fixed the write and read data from mt25ql memory. I am able to write and read data from mt25ql memory.

Currently I am operating with the memory manually, mean to say beginning the address of memory from 0x00000000. I want to change the starting address from 0x90000000, as i know I need to change operating mode in XIP mode during power on time.

What are the other things need to be take care when I operate in XIP mode.

Thanks,

John