cancel
Showing results for 
Search instead for 
Did you mean: 

Data forwarding between SPI1 and SPI2

JasurDovurov
Associate II

Hi,

device: STM32G031F6

What efficient way could forward RX data of SPI2 to TX of SPI2, and RX of SPI1 to TX of SPI2?

And also, this device itself has a fixed buffer to send over SPI2 TX to another master device.

I think this will require some kind of synchronization between these two SPIs, or adding queue-based functionality to share buffer data between tthe wo SPIs.

SPI2 config: Full Duplex Slave, RX/TX circular DMA configured

SPI1 config: Ful duplex Master, RX/TX circular DMA configured, Baudrate=8MHz.

I configured DMA as a circular buffer because SPI2 missed some random requests from another master device. But with the circular mode, it never missed any data requests.

Below you can see my simple Thread functions start SPI transmissions

void StartSpiSlaveBusTask(void const *argument)
{
  SpiSlaveDevice(BufferSlaveTX, BufferSlaveRX, TILES_DATA_BUFFER_SIZE);
  for (;;)
  {
    DELAY_MS(1);
  }
  osThreadTerminate(spiSlaveBusTaskHandler);
}
void StartSpiMasterBusTask(void const *argument)
{
  SpiMasterDevice(BufferMasterTX, BufferMasterRX, TILES_DATA_BUFFER_SIZE);
  for (;;)
  {
    DELAY_MS(1);
  }
  osThreadTerminate(spiMasterBusTaskHandler);
}

Below SPI bus handler functions

// holds a reference to the comms task, so that the interrupt can wake it when an SPI transfer is complete
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
TaskHandle_t slave_task_handle;
TaskHandle_t master_task_handle;
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK
 
/**
 * @brief function handles spi2 bus data flow
 *
 * @param TxBuffer
 * @param RxBuffer
 * @param len
 * @return TransferState
 */
TransferState SpiSlaveDevice(uint8_t *TxBuffer, uint8_t *RxBuffer, uint16_t len)
{
    HAL_StatusTypeDef ret = HAL_ERROR; // default to error, so we can see, if value actually gets updated by HAL
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
    // save a reference to this thread, so that the interrupt handler can wake it up
    slave_task_handle = xTaskGetCurrentTaskHandle();
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK
    ret = HAL_SPI_TransmitReceive_DMA(&hspi2, (uint8_t *)TxBuffer, (uint8_t *)RxBuffer, len);
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
    // suspend the thread until SPI transfer is complete
    ulTaskNotifyTake(1, portMAX_DELAY);
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK
    if (ret != HAL_OK || ret != HAL_BUSY)
    {
        return TRANSFER_ERR;
    }
    return TRANSFER_END;
}
/**
 * @brief function handles spi1 bus data flow
 *
 * @param TxBuffer
 * @param RxBuffer
 * @param len
 * @return TransferState
 */
TransferState SpiMasterDevice(uint8_t *TxBuffer, uint8_t *RxBuffer, uint16_t len)
{
    HAL_StatusTypeDef ret = HAL_ERROR; // default to error, so we can see, if value actually gets updated by HAL
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
    // master a reference to this thread, so that the interrupt handler can wake it up
    master_task_handle = xTaskGetCurrentTaskHandle();
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK
    ret = HAL_SPI_TransmitReceive_DMA(&hspi1, (uint8_t *)TxBuffer, (uint8_t *)RxBuffer, len);
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
    // suspend the thread until SPI transfer is complete
    ulTaskNotifyTake(1, portMAX_DELAY);
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK
    if (ret != HAL_OK || ret != HAL_BUSY)
    {
        return TRANSFER_ERR;
    }
    return TRANSFER_END;
}
/**
 * @brief Callback when the DMA transfer is complete.
 * It just calls NotifyGive to wake the comms thread when it returns
 *
 * @param hspi
 */
#ifndef CONFIG_POLLING_SPI_DR_STATE_CHECK
uint8_t c = 0;
void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi)
{
    if (hspi->Instance == SPI2)
    {
        BaseType_t slaveTaskWoken = 0;
        vTaskNotifyGiveFromISR(slave_task_handle, &slaveTaskWoken);
        portYIELD_FROM_ISR(slaveTaskWoken);
    }
    else
    {
        BaseType_t masterTaskWoken = 0;
        vTaskNotifyGiveFromISR(master_task_handle, &masterTaskWoken);
        portYIELD_FROM_ISR(masterTaskWoken);
    }
}
#endif //>!CONFIG_POLLING_SPI_DR_STATE_CHECK

I tried adding buffer copying functions into HAL_SPI_TxRxCpltCallback ISR, but copying the data at the right point is probably slow and sometimes it missed starting from the header because the DMA buffer is circular.

the microcontroller has another task also, that's why I need to implement the most efficient way.

Please share any ideas you have, Thanks

3 REPLIES 3
MM..1
Chief II

DMA have two callbacks half complete and full complete. Try use it with circullar.

But condition to work is timing and speeds ...

Many thanks for your reply, it actually makes more sense to me,

I already try to implement DMA callback functions in my userspace to check, but my callbacks have never been called somehow, my callbacks are

```void XferCpltCallback(DMA_HandleTypeDef *DmaHandle)``` and

```void XferErrorCallback(DMA_HandleTypeDef *DmaHandle)```

.

But I have DMA interrupt functions in the stm32g0xx_it.c,

and I also inspected that DMA callbacks are already in use in the hal driver side.

stm32g0xx_hal_spi.c

  /* 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;
 
..................................................................................................
 
static void SPI_DMATransmitCplt(DMA_HandleTypeDef *hdma)
{
  SPI_HandleTypeDef *hspi = (SPI_HandleTypeDef *)(((DMA_HandleTypeDef *)hdma)->Parent); /* Derogation MISRAC2012-Rule-11.5 */
  uint32_t tickstart;
 
  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();
 
  /* DMA Normal Mode */
  if ((hdma->Instance->CCR & DMA_CCR_CIRC) != DMA_CCR_CIRC)
  {
    /* Disable ERR interrupt */
    __HAL_SPI_DISABLE_IT(hspi, SPI_IT_ERR);
 
    /* Disable Tx DMA Request */
    CLEAR_BIT(hspi->Instance->CR2, SPI_CR2_TXDMAEN);
 
    /* Check the end of the transaction */
    if (SPI_EndRxTxTransaction(hspi, SPI_DEFAULT_TIMEOUT, tickstart) != HAL_OK)
    {
      SET_BIT(hspi->ErrorCode, HAL_SPI_ERROR_FLAG);
    }
 
    /* Clear overrun flag in 2 Lines communication mode because received data is not read */
    if (hspi->Init.Direction == SPI_DIRECTION_2LINES)
    {
      __HAL_SPI_CLEAR_OVRFLAG(hspi);
    }
 
    hspi->TxXferCount = 0U;
    hspi->State = HAL_SPI_STATE_READY;
 
    if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
    {
      /* Call user error callback */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
      hspi->ErrorCallback(hspi);
#else
      HAL_SPI_ErrorCallback(hspi);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
      return;
    }
  }
  /* Call user Tx complete callback */
#if (USE_HAL_SPI_REGISTER_CALLBACKS == 1U)
  hspi->TxCpltCallback(hspi);
#else
  HAL_SPI_TxCpltCallback(hspi);
#endif /* USE_HAL_SPI_REGISTER_CALLBACKS */
}

I think all I have is DMA interrupts as I mentioned above, right?

please can you share some examples for using callbacks, I tried some examples and they did not work for me.

many thanks, I appreciate your support!

All examples is in C:\Users\you\STM32Cube\Repository\STM32Cube_FW_G0_V1.5.0\Projects\NUCLEO-G031K8\ or other vrsion.

Yes in demo example isnt used half callbacks , but you simply add to main and overide weak from h file. C:\Users\you\STM32Cube\Repository\STM32Cube_FW_G0_V1.5.0\Drivers\STM32G0xx_HAL_Driver\Inc\stm32g0xx_hal_spi.h