Showing results for 
Search instead for 
Did you mean: 

Linker Time Optimization introduces glitch/noise into Timer DMA controlled PWM

Associate II


I have an application on an STM32F042 microcontroller, and it uses Timer 2 configured as PWM output and is transferred over DMA.

To fit the whole code into the small 32 KB flash memory, I am forced to use -flto (Linker time optimization). However by doing so, it causes weird behavior of the aforementioned output, which I'll try to describe:

Normally, it is used to modulate a pulse which complies the protocol used to address WS2812B digital addressable LEDs. It's a protocol fixed at 800 kHz with a logical '0' being a pulse with short on-time (duty cycle) and a logical '1' bit being a pulse with long on-time. During idle time, it's set to low (0V).

The weird behavior (measured with an oscilloscope) is now that only when LTO is turned on during compiling & linking, the PWM output randomly is set to high (3.3V) for a pulse lasting several milliseconds. The pulse therefore is significantly longer than a complete 'actual' 800 kHz data transfer, which lasts T = 6 LEDs * 3 Colors * 8 Bit * (1/800 KHz) << 1 ms. Without LTO it doesn't happen.

I have a feeling that LTO messes up interrupt routines, DMA priorities and the likely, but I neither have a strategy, nor the experience to find what caused the glitch and how to fix it, and therefore asking you for advice. Have you experienced similar?

For the record, these are the other peripherals used:

ADC1 (+Interrupt), I2C1, SPI1 (DMA +Callback Interrupt), Timer 16 (+Interrupt), UART2 (+Interrupt), USB CDC as VCP (+Interrupt)

Any help is greatly appreciated.

I'm using SW4STM32 IDE on macOS.


So, you want to race on you family car, use nitro, it blows up the engine and you are asking what's the magical trick to keep it going?

LTE is an aggressive optimization step and - as optimization options with poorly written code often do -it most likely reveals a bug (e.g.missing volatile) in either your application or Cube/HAL you forgot to mention you are using.

You've been already told the solution: ditch Cube and rewrite the application.

Our find the bug using the usual debugging procedures (form hypotheses based on observation, perform experiments on appropriately instrumented code, observe results and feed back, etc.), fix it and pray there are no more to jump up later.


You have a point here indeed, thank you.

Yes, everything is HAL generated by CubeMX and I'll try to LL most of the stuff now to manually strip down code size.


Check that every variable used both in interrupt handlers and in the main code has the volatile qualifier. Including data buffers filled or consumed by DMA.

HAL declarations and functions use volatile quite randomly, the authors were apparently told that they need to have more volatile variables, but they don't have the faintest clue what volatile does and why it is needed. Sometimes doing even weirder things like expecting pointer parameters as uint32_t... wtf??

The solution to the problem of incompetent software engineers at ST? Hide the -flto setting in the compiler panel, hoping that no one would notice.

A temporarily stuck PWM output could also be a symptom of a missing preload setting in the timer. Set the TIM2->CR1|=TIM_CR1_ARPE, and/or the OC*PE bit for the misbehaving timer channel.