Skip to main content
Visitor II
July 27, 2020
Question

where can I get a slave SPI by DMA reference code

  • July 27, 2020
  • 8 replies
  • 1572 views

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

Thanks in advance

    This topic has been closed for replies.

    8 replies

    Visitor II
    July 27, 2020

    For which MCU?

    Visitor II
    July 27, 2020

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

    Visitor II
    July 27, 2020

    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 */
    }

    Visitor II
    July 27, 2020

    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);
    } 

    JSanz.1Author
    Visitor II
    July 28, 2020

    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.1Author
    Visitor II
    July 28, 2020

    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);

    }

    Visitor II
    July 28, 2020

    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.

    JSanz.1Author
    Visitor II
    July 29, 2020

    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