0
votes

I am currently learning on STM32F0k6 board (ARM Cortex M0) using DMA and SPI. I transmit a 72 bytes data from memory to peripheral, but after each transmission, during 86us, my signal is 0 logic (image bellow).

signal from oscilloscope

I am using STM32CubeIDE software and for data transmission I used HAL_SPI_Transmit_DMA() function. Data is correctly transmitted, but I want the second data to be transmitted immediately after the first one ( in other words, I want to avoid the 86 us time).

What approach should I adopt? Do you suppose that an interrupt occurs after each transmission?

Here is my code:

    uint8_t buffer[72] ={
        0b11111111, 0b11111111, 0b00000000, 0b11111111, 0b11111111, 0b00000000, 0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111111, 0b11111111, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,
        0b11111110, 0b00000000, 0b00000000, 0b11111111, 0b00000000, 0b00000000, 0b11111111, 0b11111111, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111111, 0b11111111, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,
        0b11111110, 0b00000000, 0b00000000, 0b11111111, 0b00000000, 0b00000000, 0b11111111, 0b11111111, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111111, 0b11111111, 0b00000000,0b11111110, 0b00000000, 0b00000000,0b11111110, 0b00000000, 0b00000000
};

int main(void)
{
  HAL_Init();
  SystemClock_Config();

  MX_GPIO_Init();
  MX_DMA_Init();
  MX_SPI1_Init();

  
  while (1)
  {
      for(unit8_t i =0 ; i <=3; i++)
      {
          HAL_SPI_Transmit_DMA(&hspi1, buffer, 72);
          HAL_SPI_Transmit_DMA(&hspi1, buffer, 72);
      }
      
      // wait 0.5 seconds
      HAL_Delay(500);
  }
}

HAL_SPI_Transmit_DMA() function:

 /**
  * @brief  Transmit an amount of data in non-blocking mode with DMA.
  * @param  hspi pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @param  pData pointer to data buffer
  * @param  Size amount of data to be sent
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SPI_Transmit_DMA(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
{
  HAL_StatusTypeDef errorcode = HAL_OK;

  /* Check tx dma handle */
  assert_param(IS_SPI_DMA_HANDLE(hspi->hdmatx));

  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

  if (hspi->State != HAL_SPI_STATE_READY)
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if ((pData == NULL) || (Size == 0U))
  {
    errorcode = HAL_ERROR;
    goto error;
  }

  /* Set the transaction information */
  hspi->State       = HAL_SPI_STATE_BUSY_TX;
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pTxBuffPtr  = (uint8_t *)pData;
  hspi->TxXferSize  = Size;
  hspi->TxXferCount = Size;

  /* Init field not used in handle to zero */
  hspi->pRxBuffPtr  = (uint8_t *)NULL;
  hspi->TxISR       = NULL;
  hspi->RxISR       = NULL;
  hspi->RxXferSize  = 0U;
  hspi->RxXferCount = 0U;

  /* Configure communication direction : 1Line */
  if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
  {
    SPI_1LINE_TX(hspi);
  }

#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

  /* Set the SPI TxDMA Half transfer complete callback */
  hspi->hdmatx->XferHalfCpltCallback = SPI_DMAHalfTransmitCplt;

  /* Set the SPI TxDMA transfer complete callback */
  hspi->hdmatx->XferCpltCallback = SPI_DMATransmitCplt;

  /* Set the DMA error callback */
  hspi->hdmatx->XferErrorCallback = SPI_DMAError;

  /* Set the DMA AbortCpltCallback */
  hspi->hdmatx->XferAbortCallback = NULL;

  CLEAR_BIT(hspi->Instance->CR2, SPI_CR2_LDMATX);
  /* Packing mode is enabled only if the DMA setting is HALWORD */
  if ((hspi->Init.DataSize <= SPI_DATASIZE_8BIT) && (hspi->hdmatx->Init.MemDataAlignment == DMA_MDATAALIGN_HALFWORD))
  {
    /* Check the even/odd of the data size + crc if enabled */
    if ((hspi->TxXferCount & 0x1U) == 0U)
    {
      CLEAR_BIT(hspi->Instance->CR2, SPI_CR2_LDMATX);
      hspi->TxXferCount = (hspi->TxXferCount >> 1U);
    }
    else
    {
      SET_BIT(hspi->Instance->CR2, SPI_CR2_LDMATX);
      hspi->TxXferCount = (hspi->TxXferCount >> 1U) + 1U;
    }
  }

  /* Enable the Tx DMA Stream/Channel */
  if (HAL_OK != HAL_DMA_Start_IT(hspi->hdmatx, (uint32_t)hspi->pTxBuffPtr, (uint32_t)&hspi->Instance->DR,
                                 hspi->TxXferCount))
  {
    /* Update SPI error code */
    SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_DMA);
    errorcode = HAL_ERROR;

    hspi->State = HAL_SPI_STATE_READY;
    goto error;
  }

  /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }

  /* Enable the SPI Error Interrupt Bit */
  __HAL_SPI_ENABLE_IT(hspi, (SPI_IT_ERR));

  /* Enable Tx DMA Request */
  SET_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);

error :
  /* Process Unlocked */
  __HAL_UNLOCK(hspi);
  return errorcode;
}
1
Most likely DMA is waiting for the peripheral to signal that it has space for more data. SPI uses the MISO line as a BUSY signal.stark
What does the DSO screenshot show? The MOSI signal? Does a block of about 40us cover 72 bytes? Are you running SPI at 10MBit?Codo
the spi controller should not be goverend by miso, if miso is a valid high or low all the time that is fine. the spi controller should clock data out independent of miso.old_timer
@Codo I use SPI in Transmit Only Master. MOSI line is shown on DSO. Yes, 40us is timeframe for the 72 bytesIust
@all thank you for answers. I want to mention that I don't use MISO lineIust

1 Answers

0
votes

Most people would agree that the STM32 HAL library is poorly document. The documentation of HAL_SPI_Transmit_DMA:

  • does not mention that it will fail if there is already an operation in progress on the SPI bus
  • does not mention what happens when the SPI transfer has completed
  • fails to mention many other things.

Your code tries to submit 6 times 72 bytes to the SPI bus and then waits for half a second. I'm not sure how many of these 6 transfer are started at all as most will fail because there is still an SPI operation in progress. Test the return value for HAL_BUSY.

Just a single DMA transfer can be submitted at any time. When the transfer is completed, an interrupt occurs and some code is executed, which at least puts the SPI bus back into the ready state so the next DMA transfer can start.

I'm not sure what exactly you want to achieve. For the moment, I'm assuming you want to transfer many blocks of data as quickly as possible. So you have several options:

  1. Hook into the DMA transfer completed interrupt handler, i.e. implement HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi). This callback will be called when the transfer has completed and you can immediately submit the next transfer. You will still have a considerable gap between transfers as executing the interrupt handler takes time.

  2. Configure a ring buffer and additionally hook into the half transfer completed handler (HAL_SPI_TxHalfCpltCallback). That way the DMA transfer can go on continuously and every half transfer you are notified and can refill half the ring buffer. It's not trivial though.

Other MCUs have the option to chain several DMA transfers, i.e. a next transfer is started immediately without executing code. I don't think STM32 MCUs support that.