cancel
Showing results for 
Search instead for 
Did you mean: 

HAL_SPI_Receive_DMA returns HAL_BUSY

akoluacik
Senior

I am trying to implement an EEPROM driver which uses DMA. So far, I can implement polling and interrupt version. HAL_SPI_Transmit_DMA works OK, but HAL_SPI_Receive_DMA returns HAL_BUSY, so even the first reading process seams OK, it actually doesn't and I cannot read the bytes that I wrote just before the reading operation.

/* Private variables ---------------------------------------------------------*/
 SPI_HandleTypeDef hspi2;
DMA_HandleTypeDef hdma_spi2_tx;
DMA_HandleTypeDef hdma_spi2_rx;

int main(void)
{
  /* USER CODE BEGIN 1 */
	uint8_t data[40];
	uint8_t output_data[40];
  /* USER CODE END 1 */
 /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI2_Init();
  /* USER CODE BEGIN 2 */
  fill_data(data, 40);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  for (int var = 0; var < some_byte_number; ++var) {
		eeprom_write_func(address, data);
		address++;
	  }
	  
	  address = 0x0000;
	  for (int var = 0; var < some_byte_number; ++var) {
		eeprom_read_func(address, output_data);
		address++;
	  }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

static void MX_SPI2_Init(void)
{

  /* USER CODE BEGIN SPI2_Init 0 */

  /* USER CODE END SPI2_Init 0 */

  /* USER CODE BEGIN SPI2_Init 1 */

  /* USER CODE END SPI2_Init 1 */
  /* SPI2 parameter configuration*/
  hspix.Instance = SPIx;
  hspix.Init.Mode = SPI_MODE_MASTER;
  hspix.Init.Direction = SPI_DIRECTION_2LINES;
  hspix.Init.DataSize = SPI_DATASIZE_8BIT;
  hspix.Init.CLKPolarity = SPI_POLARITY_LOW;
  hspix.Init.CLKPhase = SPI_PHASE_1EDGE;
  hspix.Init.NSS = SPI_NSS_SOFT;
  hspix.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
  hspix.Init.FirstBit = SPI_FIRSTBIT_MSB;
  hspix.Init.TIMode = SPI_TIMODE_DISABLE;
  hspix.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
  hspix.Init.CRCPolynomial = 10;
  if (HAL_SPI_Init(&hspix) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN SPI2_Init 2 */

  /* USER CODE END SPI2_Init 2 */

}

 

uint8_t eeprom_read_function(uint16_t address, uint8_t *output)
{
   uint8_t spi_status;
   uint8_t dmarx_status;
   uint8_t dmatx_status;
    // Some configuration of eeprom
    if (HAL_SPI_Receive_DMA(hspix, output, 1) != HAL_OK) // read 1 byte
    {
        spi_status = HAL_SPI_GetState(&hspix);  // -> returns HAL_BUSY
        dmarx_status = HAL_DMA_GetStatus((&hspix)->hdmarx)); // -> returns HAL_OK
        dmatx_status = HAL_DMA_GetStatus((&hspix)->hdmatx)); // -> returns HAL_OK
    }
}

 I give some portion of the code. As I mentioned, even my writing function seems OK( the DMA functions don't returns HAL_BUSY or sth equivalent), the read function returns HAL_BUSY in the first iteration. Both my read and write functions are in DMA mode which are being operated in normal mode, data width for both peripheral and memory is word, and memory increment is enabled while peripheral increment is disabled.

8 REPLIES 8
TDK
Guru

HAL_BUSY indicates it isn't finished with the previous operation.

DMA operates asynchronously. You'll need to wait for data to be populated and the operation to complete before you can return it.

If you feel a post has answered your question, please click "Accept as Solution".

Thanks for the respose. But I don't know how to wait? Is it enough to use HAL_Delay? I actually tried with HAL_Delay(5000); and it also didn't worked.

TDK
Guru

Use blocking functions like HAL_SPI_Receive if you want to wait until the operation is complete.

If you feel a post has answered your question, please click "Accept as Solution".

You mean I should use HAL_SPI_Transmit instead of HAL_SPI_Transmit_DMA? I should add HAL_SPI_Transmit just after HAL_SPI_Transmit_DMA?

TDK
Guru

> You mean I should use HAL_SPI_Transmit instead of HAL_SPI_Transmit_DMA?

Yes.

If you feel a post has answered your question, please click "Accept as Solution".

Thank you but I need to use DMA version since it is a requirement. I believe there is a way to hande this.

Hey,

I realized that my TxCpltCallback function is called after HAL_SPI_Transmit_DMA, however, this not true after HAL_SPI_Receive_DMA. Furthermore, ERRIE bit is enabled in CR2 register. Why could it be? There is no choice but blocking functions?

Edit: One more thing I want to ask that I have real doubts about how to send command to EEPROM. Should I use normal version of SPI(HAL_SPI_Transmit) or DMA version(HAL_SPI_Transmit_DMA)?
And I've seen so much suggestion of using TransmitReceive instead of using Transmit and receive seperately? How should I use TransmitReceive if I want to write data from EEPROM(I have to first send WRITE command than the value(s) I want to write)?

Pavel A.
Evangelist III

How should I use TransmitReceive if I want to write data from EEPROM(I have to first send WRITE command than the value(s) I want to write)?

The device (eeprom) likely has two "phases" of read: first, you send some command with the address. This is a write, and the returned MISO data is ignored. Then you clock out (MOSI) dummy data and receive the input from the device. For writes, you only write - first the command, then data. Returned data is ignored. Please read the device (eeprom) document. If still not clear - read it again.