cancel
Showing results for 
Search instead for 
Did you mean: 

SPI overrun

Francisco Exp�sito
Associate II

Posted on November 07, 2016 at 13:32

Hello everyone,

I'm trying to read an external SPI flash using DMA but I occasionally get an SPI overrun error.

I've managed to track down the error: I get no overrun errors when I disable the LTDC IRQ. And more specifically, when I disable the following line placed on the IRQ handler:

HAL_LTDC_ProgramLineEvent(hltdc, 0);

My microcontroller is a STM32F429 and priorities are defined as follows:

HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 2, 0); // RX
HAL_NVIC_SetPriority(DMA2_Stream5_IRQn, 3, 0); // TX
HAL_NVIC_SetPriority(SPI1_IRQn, 4, 0);
HAL_NVIC_SetPriority(LTDC_IRQn, 5, 0);

Any ideas?

Thank you very much for your time. #spi-dma-ltdc-line-interrupt

1 ACCEPTED SOLUTION

Accepted Solutions

Posted on January 10, 2018 at 14:33

FYI to overcome the problem, apart from other optimizations around DMA/SPI driver, we changed a bitHAL_LTDC_LineEvenCallback implementation. The HAL_LTDC_ProgramLineEvent()function is called once during initialization. We only writeCFBAR and SRCR registers and manually reenable the line interrupt.

Original callback (I have removed irrelevant lines):

void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdcp) {
[...]
for (layer = 0; layer < GUI_NUM_LAYERS; layer++) {
if (layer_prop[layer].pending_buffer >= 0) {
[...]
HAL_LTDC_SetAddress(hltdcp, Addr, layer);
pFrontBuffer = Addr;
__HAL_LTDC_RELOAD_CONFIG(hltdcp);
[...]
}
}
LTDC_FrameEvent(hltdcp);
HAL_LTDC_ProgramLineEvent(hltdcp, 0);
}

New code:

void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdcp) {
 [...]
 for (layer = 0; layer < GUI_NUM_LAYERS; layer++) {
 if (layer_prop[layer].pending_buffer >= 0) {
 LTDC_LayerCfgTypeDef *pLayerCfg;
 [...]
 pLayerCfg = &(hltdc.LayerCfg[layer]);
 /* Reconfigure the Address */
 pLayerCfg->FBStartAdress = Addr;
 LTDC_LAYER(&hltdc, layer)->CFBAR = (pLayerCfg->FBStartAdress);
 [...]
 hltdcp->Instance->SRCR = LTDC_SRCR_IMR;
 [...]
 }
 }
 __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI);
}

MM

View solution in original post

7 REPLIES 7
slimen
Senior
Posted on November 17, 2016 at 11:02

Hello,

Maybe you should share your code to more understand the issue that you are facing.

Regards

Matthew M
Associate II
Posted on August 29, 2017 at 22:54

Hello,

My first post here .

This is quite old thread but maybe somebody will find my answer(and possibly somenew questions) useful...

I am recently struggling with, as it eventually appeared after many frustrating hours, exactly the same problem as OP presented. I am using DMA2 + SPI5 on STM32F429 platform for quite fast TX/RX transfers and occasionally I receive overrun flags and DMA RX stream is unable to finish the job (NDTR remains with 1 byte left). Meanwhile the LTDC is refreshing at ~60 FPS (which implies the LTDC IRQ rate).

I am still investigating the roots of the problem but it looks like some nasty APB2 bus stall during LTDC register access. The APB2 bus is shared betweenLTDC and SPI peripheral, according to the RM. Normally the register access takes few CPU cycles.For example in my 'test setup' I measured ~10 CPU cycles spent on LTDC_ICR access (interrupt clear register). The score is biased by 2-3 cycles because of measurement method but it's irrelevant. The point is that for some reason accessing e.g. LTDC_LIPCR (you can find it in

HAL_LTDC_ProgramLineEvent when you look under the hood)

takes more than 100 CPU cycles. If DMA TX stream already pushed the data to SPI and DMA RX stream is unable to receive pending data due to bus wait state I can imagine that for fast SPI clocks the overrun may occur. The DMA priority doesn't matter since priority cannot overcome existing bus transfer (at least in the APB protocol as I remember).

This is my best theory I have and now I am searching for a workaround (I would appreciate any advice, of course ). I was thinking about suspending the TX DMA transfer and resuming it after all the LTDC operations but I am not sure how to do it reliably (according to

https://community.st.com/thread/42406-dma-stm32f4xx-aborted-transfers

that might be problematic).

I believe so many cycles appear due to clock synchronization between APB2 and LCD_CLK domains, which I actually observed by changing LCD PLL (faster LCD_CLK = less cycles to program LTDC registers = shorter bus stall). When you disable LCD PLL completely (or disable LTDC through RCC) the write access latency becomes normal, i.e. the same as for LTDC_ICR register (putting aside writing to disabled peripheral doesn't make sense).

As I said I am still investigating the problem, but maybe soon I will be able to provide some not-too-messy test code for STM32F429 discovery board.

Best regards,

Mateusz

Jan Waclawek
Senior II
Posted on January 10, 2018 at 07:15

Did you properly set group priority/subpriority? The default is that all bits go to subpriority only, thus interrupts can't nest (in ARM parlance, preempt each other).

JW

Posted on January 10, 2018 at 09:21

Hi

,

My group priority is defined as follows:

HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); // 4 bits for pre-emption priority and 0 bits for subpriority�?

If I am not wrong, priorities are correctly set.

answer makes sense to me: SPI gets no access to APB2 when itis already being used by LTDC, causing the overrun.

What do you think?

Thank you very much!

Posted on January 10, 2018 at 13:25

I've just noted

mmaciag

's reply above. That's it - LTDC_LIPCR is in pixel-clock domain (LCD_CLK), thus - according to RM0090,

LTDC reset and clocks chapter, write access to it takes (6 xPCKL2 period + 5x LCD_CLK period). Depending on pixel clock and SPI data rate, this may take longer than one SPI frame, leading to overrun.

I had a look at the examples in Cube, and

HAL_LTDC_ProgramLineEvent()

in them is always called with 0 (that's what's written into LTDC_LIPCR). I don't quite understand why that function is then called at all; but even if it's needed for some reason in the ISR, simply don't write to LTDC_LIPCR in it.

Would there be any reason to change LTDC_LIPCR on the fly, SPI on other APB must then be used, or - as Matthew said above - some fancy mechanism to stop/start SPI transfers if that's possible in the given application.

JW

Posted on January 10, 2018 at 14:33

FYI to overcome the problem, apart from other optimizations around DMA/SPI driver, we changed a bitHAL_LTDC_LineEvenCallback implementation. The HAL_LTDC_ProgramLineEvent()function is called once during initialization. We only writeCFBAR and SRCR registers and manually reenable the line interrupt.

Original callback (I have removed irrelevant lines):

void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdcp) {
[...]
for (layer = 0; layer < GUI_NUM_LAYERS; layer++) {
if (layer_prop[layer].pending_buffer >= 0) {
[...]
HAL_LTDC_SetAddress(hltdcp, Addr, layer);
pFrontBuffer = Addr;
__HAL_LTDC_RELOAD_CONFIG(hltdcp);
[...]
}
}
LTDC_FrameEvent(hltdcp);
HAL_LTDC_ProgramLineEvent(hltdcp, 0);
}

New code:

void HAL_LTDC_LineEvenCallback(LTDC_HandleTypeDef *hltdcp) {
 [...]
 for (layer = 0; layer < GUI_NUM_LAYERS; layer++) {
 if (layer_prop[layer].pending_buffer >= 0) {
 LTDC_LayerCfgTypeDef *pLayerCfg;
 [...]
 pLayerCfg = &(hltdc.LayerCfg[layer]);
 /* Reconfigure the Address */
 pLayerCfg->FBStartAdress = Addr;
 LTDC_LAYER(&hltdc, layer)->CFBAR = (pLayerCfg->FBStartAdress);
 [...]
 hltdcp->Instance->SRCR = LTDC_SRCR_IMR;
 [...]
 }
 }
 __HAL_LTDC_ENABLE_IT(&hltdc, LTDC_IT_LI);
}

MM

Posted on January 12, 2018 at 09:28

Hi

mmaciag

,

Thank you very much for your answer! I'll check your code and I'll let you know my results.

Thank you

Waclawek.Jan

for your contribution!

Regards