cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H7 Workaround for DMA data transfer > SPI fifo-buffer

HansPLJ
Associate III

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 

 

1 ACCEPTED SOLUTION

Accepted Solutions

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

View solution in original post

8 REPLIES 8

> it will not work

Elaborate.

JW

SofLit
ST Employee

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

To give better visibility on the answered topics, please click on "Accept as Solution" on the reply which solved your issue or answered your question.
PS:
1 - This is NOT an online support (https://ols.st.com) but a collaborative space.
2 - Please be polite in your reply. Otherwise, it will be reported as inappropriate and you will be permanently blacklisted from my help.
HansPLJ
Associate III

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 

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

#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

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

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?

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