cancel
Showing results for 
Search instead for 
Did you mean: 

QSPI peripheral state remains busy when using DMA

BBagl.1
Associate

Hi,

I'm trying to use QSPI to interface with a flash memory chip (GD25Q16). I can read and write to memory and status registers using blocking code, but I'd like to use DMA to decrease the CPU load. However, the QSPI handle's State gets stuck in HAL_QSPI_STATE_BUSY_INDIRECT_RX after the first DMA transfer. Manually resetting this state makes every QSPI xfer return HAL_ERROR instead of BUSY.

Additionally, HAL_QSPI_RxCpltCallback() is redefined in my code but never gets called; DMA1_Channel3_IRQHandler() (with HAL_DMA_IRQHandler() inside it) also never gets called.

Here's a code snippet:

void test(void)  // called in main.c's main function
{
	HAL_StatusTypeDef read1, read2;
	uint8_t buf1[2] = {0xAA, 0xAA};     // init these as known values
	uint8_t buf2[2] = {0xAA, 0xAA};
	QSPI_WriteEnable(&hqspi1);     // blocking mode (not DMA) - works fine, returns HAL_OK
	read1 = QSPI_ReadStatusRegisters_DMA(&hqspi1, buf1);     // uses DMA; returns HAL_BUSY, but buf returns the expected first byte
	read2 = QSPI_ReadStatusRegisters_DMA(&hqspi1, buf2);     // no QSPI xfer will work (DMA or otherwise), read2 = HAL_BUSY
}
 
HAL_StatusTypeDef QSPI_WriteEnable(QSPI_HandleTypeDef *hqspi)
{
	QSPI_CommandTypeDef sCommand;
	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;
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
	sCommand.DataMode  = QSPI_DATA_NONE;
	sCommand.Instruction = GD25Q16_WRITE_ENABLE_CMD;
	sCommand.DummyCycles = 0;
	return HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
}
 
HAL_StatusTypeDef QSPI_ReadStatusRegisters_DMA(QSPI_HandleTypeDef *hqspi, uint8_t *buf)
{
	uint8_t temp[1] = { 0 };
	QSPI_CommandTypeDef sCommand;
	HAL_StatusTypeDef retval = HAL_OK;
 
	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;
	sCommand.InstructionMode = QSPI_INSTRUCTION_1_LINE;
	sCommand.AddressMode = QSPI_ADDRESS_NONE;
 	sCommand.DataMode  = QSPI_DATA_1_LINE;
	sCommand.Instruction = GD25Q16_READ_STATUS_REG_CMD;
	sCommand.NbData   = 1;
	sCommand.DummyCycles = 0;
 
 	retval |= HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
	retval |= HAL_QSPI_Receive_DMA(hqspi, buf);     // Returns the expected value; however, hqspi1.State remains HAL_QSPI_STATE_BUSY_INDIRECT_RX
//  retval |= HAL_QSPI_Receive(hqspi, buf, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);    // Using this instead of Receive_DMA function works perfectly
 
	HAL_Delay(100);
 
	sCommand.Instruction = GD25Q16_READ_STATUS_REG_1_CMD;
	retval |= HAL_QSPI_Command(hqspi, &sCommand, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
	retval |= HAL_QSPI_Receive_DMA(hqspi, buf);     // returns HAL_BUSY
//  retval |= HAL_QSPI_Receive(hqspi, temp, HAL_QSPI_TIMEOUT_DEFAULT_VALUE);
	HAL_Delay(100);
	buf[1] = temp[0];
	return retval;
}

Also, here is my QSPI initialization function. I used the Device Configuration Tool in STM32CubeIDE. I've tried screwing around with the parameters but can't get it to work.

static void MX_QUADSPI1_Init(void)
{
 
  /* USER CODE BEGIN QUADSPI1_Init 0 */
  /* USER CODE END QUADSPI1_Init 0 */
 
  /* USER CODE BEGIN QUADSPI1_Init 1 */
 
  /* USER CODE END QUADSPI1_Init 1 */
  /* QUADSPI1 parameter configuration*/
  hqspi1.Instance = QUADSPI;
  hqspi1.Init.ClockPrescaler = 255;
  hqspi1.Init.FifoThreshold = 1;
  hqspi1.Init.SampleShifting = QSPI_SAMPLE_SHIFTING_NONE;
  hqspi1.Init.FlashSize = 23;
  hqspi1.Init.ChipSelectHighTime = QSPI_CS_HIGH_TIME_1_CYCLE;
  hqspi1.Init.ClockMode = QSPI_CLOCK_MODE_0;
  hqspi1.Init.FlashID = QSPI_FLASH_ID_1;
  hqspi1.Init.DualFlash = QSPI_DUALFLASH_DISABLE;
  if (HAL_QSPI_Init(&hqspi1) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN QUADSPI1_Init 2 */
 
  /* USER CODE END QUADSPI1_Init 2 */
 
}

I have DMA enabled in the Device Configuration Tool (DMA1 Channel 3, Peripheral to Memory, low priority, normal mode, synchronization and event disabled) with the QUADSPI global interrupt enabled, priority 5.

Thank you for any advice

2 REPLIES 2

Seems excessive, pick something that properly relates CPU speed to QSPI device speed

hqspi1.Init.ClockPrescaler = 255;

Does seem an entire waste of time to DMA 1 byte, likely to be done before you return from the call. Should wait for completion before ploughing forward.

Need to enable interrupts in the NVIC somewhere, often in the QUADSPI init code, setting up and associating/linking the DMA, enabling the interrupt.

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

I'm currently using a very slow clock for debugging so I know the issue won't be the clock's fault. I previously had the prescaler set to 5. Going slower shouldn't cause any issues (and I just retested it with a prescaler of 5 to be sure and got the same results).

Using DMA with 1 byte is just for testing; once I know I can actually use DMA to change a single byte, I'll likely go back to blocking mode for things like the status registers. However, writing thousands of bytes at a time will require DMA.

All I did to enable interrupts was check the QUADSPI global interrupt box in the device configuration tool. I believe that's all that was required for other peripherals using DMA.

0693W000003Ot69QAC.png0693W000003Ot64QAC.png

Also, these lines are created in the NVIC initialization code:

  HAL_NVIC_SetPriority(DMA1_Channel3_IRQn, 5, 0);
  HAL_NVIC_EnableIRQ(DMA1_Channel3_IRQn);

Importantly, I've found that if I change the priority of 5 to anything less, then HAL_DMA_IRQHandler does get called.