Skip to main content
Graduate
January 13, 2025
Solved

STM32H7 Workaround for DMA data transfer > SPI fifo-buffer

  • January 13, 2025
  • 4 replies
  • 1651 views

Hi! 

I am working on a project where we are using DMA driven SPI-transmissions. I am using LPTIM1 to synchronize readouts of multiple SPI busses.

The setup works when the transmissions is equal or below 128 bits (for SPI1-SPI3). 

When HAL_DMA_MuxSyncConfigTypeDef.RequestNumber is above 4 (at DMA_PDATAALIGN_WORD and DMA_MDATAALIGN_WORD) it will not work. 

From what I can understand this is due to the size of the fifo registers of the SPI-peripheral. 

Is there a workaround for this? 

This is my current setup:

DMA_InitTypeDef txDMAinit;
DMA_InitTypeDef rxDMAinit;

spiHandle.Init.Mode = SPI_MODE_MASTER;
spiHandle.Init.Direction = SPI_DIRECTION_2LINES;
spiHandle.Init.DataSize = SPI_DATASIZE_32BIT;
spiHandle.Init.CLKPolarity = SPI_POLARITY_LOW;
spiHandle.Init.CLKPhase = SPI_PHASE_1EDGE;
spiHandle.Init.NSS = SPI_NSS_HARD_OUTPUT;
spiHandle.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_16;
spiHandle.Init.FirstBit = SPI_FIRSTBIT_MSB;
spiHandle.Init.TIMode = SPI_TIMODE_DISABLE;
spiHandle.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
spiHandle.Init.CRCPolynomial = 0x0;
spiHandle.Init.NSSPMode = SPI_NSS_PULSE_ENABLE;
spiHandle.Init.NSSPolarity = SPI_NSS_POLARITY_LOW;
spiHandle.Init.FifoThreshold = SPI_FIFO_THRESHOLD_01DATA;
spiHandle.Init.TxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
spiHandle.Init.RxCRCInitializationPattern = SPI_CRC_INITIALIZATION_ALL_ZERO_PATTERN;
spiHandle.Init.MasterSSIdleness = SPI_MASTER_SS_IDLENESS_04CYCLE;
spiHandle.Init.MasterInterDataIdleness = SPI_MASTER_INTERDATA_IDLENESS_00CYCLE;
spiHandle.Init.MasterReceiverAutoSusp = SPI_MASTER_RX_AUTOSUSP_DISABLE;
spiHandle.Init.MasterKeepIOState = SPI_MASTER_KEEP_IO_STATE_DISABLE;
spiHandle.Init.IOSwap = SPI_IO_SWAP_DISABLE;

if(HAL_SPI_Init(&m_spiHandle)!= HAL_OK)
{
return StatusCode::STATUS_ERROR;
}

txDMAinit.Direction = DMA_MEMORY_TO_PERIPH;
txDMAinit.PeriphInc = DMA_PINC_DISABLE;
txDMAinit.MemInc = DMA_MINC_ENABLE;
txDMAinit.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
txDMAinit.MemDataAlignment = DMA_MDATAALIGN_WORD;
txDMAinit.Mode = DMA_CIRCULAR;
txDMAinit.Priority = DMA_PRIORITY_MEDIUM;
txDMAinit.FIFOMode = DMA_FIFOMODE_ENABLE;
txDMAinit.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;

rxDMAinit.Direction = DMA_PERIPH_TO_MEMORY;
rxDMAinit.PeriphInc = DMA_PINC_DISABLE;
rxDMAinit.MemInc = DMA_MINC_ENABLE;
rxDMAinit.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
rxDMAinit.MemDataAlignment = DMA_MDATAALIGN_WORD;
rxDMAinit.Mode = DMA_CIRCULAR;
rxDMAinit.Priority = DMA_PRIORITY_MEDIUM;
rxDMAinit.FIFOMode = DMA_FIFOMODE_ENABLE;
rxDMAinit.FIFOThreshold = DMA_FIFO_THRESHOLD_1QUARTERFULL;

pSyncConfig.SyncSignalID = HAL_DMAMUX1_SYNC_LPTIM1_OUT;
pSyncConfig.SyncPolarity = HAL_DMAMUX_SYNC_FALLING;
pSyncConfig.SyncEnable = ENABLE;
pSyncConfig.EventEnable = DISABLE;
pSyncConfig.RequestNumber = 8;

hdma_spi1_tx.Instance = DMA1_Stream4;
hdma_spi1_tx.Init = txDMAinit;
hdma_spi1_tx.Init.Request = DMA_REQUEST_SPI1_TX;

HAL_DMA_Init(&hdma_spi1_tx);

if((HAL_DMAEx_ConfigMuxSync(&hdma_spi1_tx, &pSyncConfig)) != HAL_OK)
{
return StatusCode::STATUS_ERROR;
}

__HAL_LINKDMA(&m_spiHandle,hdmatx,hdma_spi1_tx);

hdma_spi1_rx.Instance = DMA1_Stream1;
hdma_spi1_rx.Init = rxDMAinit;
hdma_spi1_rx.Init.Request = DMA_REQUEST_SPI1_RX;

HAL_DMA_Init(&hdma_spi1_rx);

if((HAL_DMAEx_ConfigMuxSync(&hdma_spi1_rx, &pSyncConfig)) != HAL_OK)
{
return StatusCode::STATUS_ERROR;
}

__HAL_LINKDMA(&m_spiHandle,hdmarx,hdma_spi1_rx);

It only works for pSyncConfig.RequestNumber <= 4. 

Any suggestions for a workaround would be appreciated!

Best regards, 

Hans-Petter 

 

    This topic has been closed for replies.
    Best answer by waclawek.jan

    I don't see anything suspicious, but I am not familiar with the 'H7.

    Does the SPI work in expected way if you don't use the DMAMUX synchronization?

    (Btw. I don't think you'd need synchronization at the Rx path).

    JW

    4 replies

    Super User
    January 13, 2025

    > it will not work

    Elaborate.

    JW

    Technical Moderator
    January 13, 2025

    @HansPLJ please use </> button to paste a code. See this post. I've edited your post

    HansPLJAuthor
    Graduate
    January 14, 2025

    Thanks for your reply! I will try to elaborate

    When Im using pSyncConfig.RequestNumber = 4, I am getting the expected behavior. At the current stage I am looping back MOSI to MISO and I am reading what I am sending. This is happening at the rate of LPTIM sync event at 16 kHz. 

    After setting up the SPI, DMA and LPTIM I am using this function: 

    #define SIZE_BUFFER 96
    uint32_t __attribute__((section (".axi_sram1"))) RxBuff[SIZE_BUFFER];
    uint32_t __attribute__((section (".axi_sram1"))) TxBuff[SIZE_BUFFER];
    
    HAL_SPI_TransmitReceive_DMA(&hal.spi1.getHandle(), (uint8_t*)TxBuff, (uint8_t*)RxBuff, (uint16_t)SIZE_BUFFER);
    HAL_LPTIM_PWM_Start(&hal.lptim1.getHandle(), LPTIM1_PERIODS, LPTIM1_PULSE_PERIODS);

    From oscilloscope:

    HansPLJ_1-1736837959208.png

    Live watch of the rxBuffer

    HansPLJ_0-1736837925174.png

    My aim is to be able to do read more than 4 words at each LPTIM event without using interrupts.

    When I adjust the pSyncConfig.RequestNumber = 6. 

    I see in the oscilloscope that the readout is occuring at HW level: 

    HansPLJ_2-1736839533353.png

    In the live watch the buffer is only receiving the first 4 words:

    HansPLJ_3-1736839629212.png

    In the SPI SR the peripheral is flagging overrun (OVR). There are no error flags in the ISR register of the DMA peripheral. 

    Is there a way I can change the settings in the DMA or SPI to have the DMA handle the data before overrun?

    If you need more information, let me know. 

    Best regards, 

    Hans-Petter 

    Visitor II
    January 19, 2025
    #define SIZE_BUFFER 96
    uint32_t __attribute__((section (".axi_sram1"))) RxBuff[SIZE_BUFFER];
    uint32_t __attribute__((section (".axi_sram1"))) TxBuff[SIZE_BUFFER];

    "axi_ram" is normally defined in domain D1 (look in your .ld file).
    SPI1 and DMA1 can't read/write to D1. You must create your buffers in ram D2 and take the cache into account when writing/reading through the CPU.

    hgl

    HansPLJAuthor
    Graduate
    January 20, 2025

    I have tested using both AXI_SRAM(D1) and SRAM(D2). Both is working once I removed the synchronization from the Rx -line. 

    There is nodes between the D1 and D2 (AXI to AHB): 

    HansPLJ_0-1737363210047.png

    Using D2 memory is maybe more efficient?

    Super User
    January 14, 2025

    I don't see anything suspicious, but I am not familiar with the 'H7.

    Does the SPI work in expected way if you don't use the DMAMUX synchronization?

    (Btw. I don't think you'd need synchronization at the Rx path).

    JW

    HansPLJAuthor
    Graduate
    January 20, 2025

    Hi JW! 

    Thanks for your input! 

    I removed the synchronization for the Rx-path and it looks like that did the trick!

    My guess is that it was overriding that the fifo threshold is initiating DMA-transaction?  

    Best regards,

    HP

    Super User
    January 20, 2025

    Sorry, I'm not sure why did that help - as I've said, I don't use the 'H7 and am not familiar with its SPI, which is very different from SPI in other STM32.

    JW