QSPI peripheral state remains busy when using DMA
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