cancel
Showing results for 
Search instead for 
Did you mean: 

STM32U5 : GPDMA sort of double buffering with LLI and buffer looping

lleo_49340
Associate

Hello,

I'm working on a led display solution and I'm using a STM32U5 with GPDMA to pilot daisy-chained shift-registers through GPIOs. I could have used the SPI interface but I'm driving several differents chains of shift registers and I prefer to make bit-banging with DMA > GPIO.BSRR registers.

 

Everything is working but sometimes we can observe glitches in the led display. We think that is because we are modifying the buffer sent by the DMA at the middle of a frame. Currently, there is not synchronisation between the application and the DMA transfers.

 

 

We think it would be better to use double buffering, one for storing the current display data, and one for the future data to be displayed. We don't want to update very often the data to be displayed so we called sometimes a function that do the job of updating the content to be displayed, so ideally the DMA should loop on the same buffer and switches to the second buffer only when the function is called.

In the following schematic, I've tried to represent our needs :

lleo_49340_0-1726495934600.png

Do you have any idea how to do this task ? I'm not sure it is actually the "normal operation" of a double buffering system because we need to loop on the same LLi until the SW switches to the 2nd LLi and vice versa.

The best solution for me would be without interrupting the application (we don't use Half and Complete Transfer IT) and do the switch automatically at the end of one LLi to the 2nd LLi (to start on a fresh new updated buffer).

 

Best regards.

Lilian.

 

2 REPLIES 2
Sarra.S
ST Employee

Hello @lleo_49340

You can configure the DMA to loop through the same buffer and switch to the second buffer only when a software trigger occurs 

1/ Define two LLIs, one for each buffer 

 

DMA_LinkedListTypeDef LLI0;
DMA_LinkedListTypeDef LLI1;

// Initialize LLI0
LLI0.SAR = (uint32_t)buffer1;
LLI0.DAR = (uint32_t)&GPIOx->BSRR;
LLI0.BlockSize = BUFFER_SIZE;
LLI0.NextLLI = &LLI0; // Loop on itself

// Initialize LLI1
LLI1.SAR = (uint32_t)buffer2;
LLI1.DAR = (uint32_t)&GPIOx->BSRR;
LLI1.BlockSize = BUFFER_SIZE;
LLI1.NextLLI = &LLI1; // Loop on itself

 

2/ Then configure you TIM to generate periodic DMA transfers, using: __HAL_TIM_ENABLE_DMA(&htim, TIM_DMA_UPDATE);

3/ for switching between buffers, you can use a flag to indicate when to switch, poll DMA status (DMA_IsTransferComplete()) and change the "NextLLI" pointer of the current LLI to point to the other LLI.

 

if (DMA_GetCurrentLLI() == &LLI0)//(which corresponds to buffer1).
LLI0.NextLLI = &LLI1; // change LLI pointer to point to LLI1

 

 

 

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.

lleo_49340
Associate

Hi,

thank you for your answer Sarra. After I wrote you, I came to the same conclusion that I should change on the fly the pointer of the next LLI but I wasn't sure it was possible.

I've made some tests and it is working :)

I think we are gonna use more and more the LinkedList functionnality, we can automate a lot of things (or boring tasks) with this mechanism !!

 

Best regards.

Lilian