cancel
Showing results for 
Search instead for 
Did you mean: 

where can I get a slave SPI by DMA reference code

JSanz.1
Associate III

I've been looking into STM32Cube_FW_L4_V1.15.0 and I can't found any.

Thanks in advance

9 REPLIES 9
S.Ma
Principal

For which MCU?

prain
Senior III

you can also check examples for other MCU family. They all are based on HAL so the API is similar

S.Ma
Principal

I'm having a custom SPI Slave mode (daisy chained MCUs), however, here are some extract in case it helps:

Configuration: SPI bidir 4 wires, EXTI on NSS edges to know when message is about to be received and received.

As transmittion length is unknown, DMA circular is helping oversized packets and no DMA interrupt.

You shall prepare the data to send to the master before the transaction, while the NSS rise edge will tell you that your feedback has been pushed through while master's incoming message is ready for grab.

Init:

HAL_StatusTypeDef SPIP_SlaveConfigureSpi(SPI_HandleTypeDef *hspi){
  hspi->Instance               = SPI2;
  hspi->Init.Direction         = SPI_DIRECTION_2LINES;
  hspi->Init.CLKPhase          = SPI_PHASE_1EDGE;
  hspi->Init.CLKPolarity       = SPI_POLARITY_LOW;
  hspi->Init.DataSize          = SPI_DATASIZE_16BIT;
  hspi->Init.FirstBit          = SPI_FIRSTBIT_MSB;
  hspi->Init.TIMode            = SPI_TIMODE_DISABLE;
  hspi->Init.CRCCalculation    = SPI_CRCCALCULATION_DISABLE;
  hspi->Init.CRCPolynomial     = 7;
  hspi->Init.CRCLength         = SPI_CRC_LENGTH_8BIT;
  hspi->Init.NSS               = SPI_NSS_SOFT;
  hspi->Init.NSSPMode          = SPI_NSS_PULSE_DISABLE;
  hspi->Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_2;//8;
  hspi->Init.Mode = SPI_MODE_SLAVE;
  return (HAL_SPI_Init(hspi));
}
 
 
void SPI_SlaveConfigureDma(SPI_HandleTypeDef *hspi){
    /* Configure the DMA handler for Transmission process */
    pSPI->hdma_tx->Instance                 = DMA1_Channel1;//DMA1_Channel5;
    pSPI->hdma_tx->Init.Request             = DMA_REQUEST_SPI2_TX;//DMA_REQUEST_1;
    pSPI->hdma_tx->Init.Direction           = DMA_MEMORY_TO_PERIPH;
    pSPI->hdma_tx->Init.PeriphInc           = DMA_PINC_DISABLE;
    pSPI->hdma_tx->Init.MemInc              = DMA_MINC_ENABLE;
    pSPI->hdma_tx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
    pSPI->hdma_tx->Init.MemDataAlignment    = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
    pSPI->hdma_tx->Init.Mode                = DMA_CIRCULAR;
    pSPI->hdma_tx->Init.Priority            = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(pSPI->hdma_tx);
    __HAL_LINKDMA(hspi, hdmatx, *(pSPI->hdma_tx));    /* Associate the initialized DMA handle to the the SPI handle */
 
    /* Configure the DMA handler for Reception process */
    pSPI->hdma_rx->Instance                 = DMA1_Channel2;//DMA1_Channel4;
    pSPI->hdma_rx->Init.Request             = DMA_REQUEST_SPI2_RX;//DMA_REQUEST_1;
    pSPI->hdma_rx->Init.Direction           = DMA_PERIPH_TO_MEMORY;
    pSPI->hdma_rx->Init.PeriphInc           = DMA_PINC_DISABLE;
    pSPI->hdma_rx->Init.MemInc              = DMA_MINC_ENABLE;
    pSPI->hdma_rx->Init.PeriphDataAlignment = /*DMA_PDATAALIGN_BYTE;*/ DMA_PDATAALIGN_HALFWORD;
    pSPI->hdma_rx->Init.MemDataAlignment    = /*DMA_MDATAALIGN_BYTE;*/ DMA_MDATAALIGN_HALFWORD;
    pSPI->hdma_rx->Init.Mode                = DMA_CIRCULAR;
    pSPI->hdma_rx->Init.Priority            = DMA_PRIORITY_HIGH;
    HAL_DMA_Init(pSPIP->hdma_rx);
    __HAL_LINKDMA(hspi, hdmarx, *(pSPI->hdma_rx));    /* Associate the initialized DMA handle to the the SPI handle */
}

S.Ma
Principal

When NSS EXTI fall edge (with master brief delay for slave ISR to kick in):

  if(HAL_SPI_TransmitReceive_DMA(pSPIP->hspi, pSPIP->pParallelTxBuffer, pSPIP->pParallelRxBuffer, sizeof(SPIP_TX.Buffer)>>1/*ByteCount>>1*/) != HAL_OK) // this should stop at 1024 bytes, no interrupts
     TrapError();

Once the NSS is rising edge EXTI, you can read the received master payload.

Last, when NSS goes high, if the SPI has FIFO (like STM32L4R) you will need to flush the HW FIFOs by SW reset the cell:

void SPIP_SlaveNukeStart(SPI_t* pSPI) {
  HAL_SPI_DeInit(pSPI->hspi); // nuke the state machine back to original state
  // first, we reset the cell (yes!)
  /*RCC->AHB1RSTR |= 1<<0; // DMA1
  NOPs(2);
  RCC->AHB1RSTR &= ~(1<<0);*/
  NOPs(2);
  RCC->APB1RSTR1 |= (1<<14);// SPI2
  NOPs(2);
  RCC->APB1RSTR1 &= ~(1<<14);
  SPI_SlaveConfigureSpi(pSPI->hspi);
  SPI_SlaveConfigureDma(pSPI->hspi);
}  

TDK
Guru

Here's one:

https://github.com/STMicroelectronics/STM32CubeL4/tree/master/Projects/NUCLEO-L476RG/Examples/SPI/SPI_FullDuplex_ComDMA

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

Thanks to everyone and it's true, I forgot to tell the CPU, that it is STM32L431K

I'm going to read and try to understand how it works. This is the first time that I use a DMA engine.

Whenever I understood, when the SPI receives stream data, the DMA reads and moves the Rx shift register to a receive buffer until the NSS goes up.

In transmission mode, the DMA engine sends the transmit buffer to the Tx shift register until the transmission buffer is flushed.

JSanz.1
Associate III

Hi all,

I been trying to activate the dma spi1 to work but I can't get it.

I have a device that send a random spi message from 4 to 1024 bytes to check the spi receiver every ten seconds.

I have a break point to all interrupt functions and any message active any interrupt and I don't know what I do wrong.

I'm baset on the previous code and I add (in bold) what I thought it shold active the interrupts.

typedef struct

{

DMA_HandleTypeDef *hdma_tx;

DMA_HandleTypeDef *hdma_rx;

SPI_HandleTypeDef *hspi;

}SPI_t;

SPI_t _spi = {&hdma_spi1_rx, &hdma_spi1_tx, &hspi1};

void SPI_SlaveConfigureDma(SPI_HandleTypeDef *hspi) {

  /* Configure the DMA handler for Transmission process */

  _spi.hdma_tx->Instance         = DMA1_Channel3;

  _spi.hdma_tx->Init.Request       = DMA_REQUEST_3; //DMA_REQUEST__TX;

  _spi.hdma_tx->Init.Direction      = DMA_MEMORY_TO_PERIPH;

  _spi.hdma_tx->Init.PeriphInc      = DMA_PINC_DISABLE;

  _spi.hdma_tx->Init.MemInc       = DMA_MINC_ENABLE;

  _spi.hdma_tx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  _spi.hdma_tx->Init.MemDataAlignment  = DMA_MDATAALIGN_HALFWORD;

  _spi.hdma_tx->Init.Mode        = DMA_CIRCULAR;

  _spi.hdma_tx->Init.Priority      = DMA_PRIORITY_HIGH;

  _spi.hdma_tx->XferCpltCallback     = TXferCpltCallback;   //!< DMA transfer complete callback

  _spi.hdma_tx->XferHalfCpltCallback   = TXferHalfCpltCallback; //!< DMA Half transfer complete callback

  _spi.hdma_tx->XferErrorCallback    = TXferErrorCallback;  //!< DMA transfer error callback

  _spi.hdma_tx->XferAbortCallback    = TXferAbortCallback;

  CLEAR_BIT(_spi.hdma_tx->Instance->CCR, DMA_CCR_EN);

  SET_BIT(_spi.hdma_tx->Instance->CCR, DMA_CCR_TCIE);

  SET_BIT(_spi.hdma_tx->Instance->CCR, DMA_CCR_HTIE);

  SET_BIT(_spi.hdma_tx->Instance->CCR, DMA_CCR_TEIE);

  SET_BIT(_spi.hdma_tx->Instance->CCR, DMA_CCR_EN);

  HAL_DMA_Init(_spi.hdma_tx);

  __HAL_LINKDMA(hspi, hdmatx, *(_spi.hdma_tx));  /* Associate the initialized DMA handle to the the SPI handle */

  /* Configure the DMA handler for Reception process */

  _spi.hdma_rx->Instance         = DMA1_Channel2;

  _spi.hdma_rx->Init.Request       = DMA_REQUEST_2; //DMA_REQUEST_SPI1_RX;

  _spi.hdma_rx->Init.Direction      = DMA_PERIPH_TO_MEMORY;

  _spi.hdma_rx->Init.PeriphInc      = DMA_PINC_DISABLE;

  _spi.hdma_rx->Init.MemInc       = DMA_MINC_ENABLE;

  _spi.hdma_rx->Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;

  _spi.hdma_rx->Init.MemDataAlignment  = DMA_MDATAALIGN_HALFWORD;

  _spi.hdma_rx->Init.Mode        = DMA_CIRCULAR;

  _spi.hdma_rx->Init.Priority      = DMA_PRIORITY_HIGH;

  _spi.hdma_rx->XferCpltCallback     = RXferCpltCallback;   //!< DMA transfer complete callback

  _spi.hdma_rx->XferHalfCpltCallback   = RXferHalfCpltCallback; //!< DMA Half transfer complete callback

  _spi.hdma_rx->XferErrorCallback    = RXferErrorCallback;  //!< DMA transfer error callback

  _spi.hdma_rx->XferAbortCallback    = RXferAbortCallback;

  // enable dma interrups

  CLEAR_BIT(_spi.hdma_rx->Instance->CCR, DMA_CCR_EN);

  SET_BIT(_spi.hdma_rx->Instance->CCR, DMA_CCR_TCIE);

  SET_BIT(_spi.hdma_rx->Instance->CCR, DMA_CCR_HTIE);

  SET_BIT(_spi.hdma_rx->Instance->CCR, DMA_CCR_TEIE);

  SET_BIT(_spi.hdma_rx->Instance->CCR, DMA_CCR_EN);

  HAL_DMA_Init(_spi.hdma_rx);

  __HAL_LINKDMA(hspi, hdmarx, *(_spi.hdma_rx));  /* Associate the initialized DMA handle to the the SPI handle */

  // reciver interrupts

  // clear pendig interrupt

  SET_BIT(_spi.hdma_rx->DmaBaseAddress->IFCR, DMA_ISR_GIF3);

  SET_BIT(_spi.hdma_rx->DmaBaseAddress->IFCR, DMA_ISR_TCIF3);

  SET_BIT(_spi.hdma_rx->DmaBaseAddress->IFCR, DMA_ISR_HTIF3);

  SET_BIT(_spi.hdma_rx->DmaBaseAddress->IFCR, DMA_ISR_TEIF3);

  HAL_DMA_Start_IT(_spi.hdma_rx, (uint32_t)&hspi->Instance->DR, (uint32_t)_spiDataIn, 1024);

}

S.Ma
Principal

I don't use DMA interrupt at all. I use EXTI on NSS because the incoming message length is unknown.

(use a GPIO on NSS so the EXTI can be done by HAL).

In the example above, all useless interrupts are activated...

DMA is circular so if the master sends 10kbyte, it won't crash.

I see.

This what I was confused, I thought the spi managed by dma itself detect the NSS rise up at the end of message and generate an interrupt.

I'll go to work in this direction.

thanks very much