STM32 Tips and Tricks

A collection of obscure or helpful tips, tricks, and hacks for using STM32s.

Canonical URL:

Please help:

A general comment

The ST documentation is very DRY (Don’t repeat yourself). For example, if one is working on DMA and reads just the DMA section of the datasheet, crucial related information will be missed because it is in other sections. A good idea is to search the entire document for the topic (e.g DMA) and read the notes from other sections for example RCC and DCACHE.

Datasheets and Reference Manuals

STM32F405 / 415 / 407 / 417: Datasheet, Reference Manual

General Reminders


During initialisation:

    /* Turn on the watchdog timer, stopped in debug halt */
    IWDG->KR = 0x5555;
    IWDG->PR = 3;
    IWDG->KR = 0xCCCC;

Somewhere in your main loop:

        /* Clear the watchdog timer */
        IWDG->KR = 0xAAAA;

Peripheral Enabling

Many of the peripherals including DMA need to have a clock enabled before they can be configured. If using CubeMX, the configuration code is automatically generated. There is a known issue though. The init code may be issued in the wrong order, thereby preventing configuration from succeeding. See

Debug access during sleep

Before sleeping or during initialisation:


    /* Allow debug access during WFI sleep */

Analog Watchdog

The ADC analog watchdog is a very useful peripheral that allow an interrupt to be generated when the ADC value goes out of bounds. What is the source of the data for comparison? One might assume it is the final ADC output data. One would be wrong. In the Over-Sampling section of the ADC documentation there is a small note stating that the analog watchdog is fed the values from the oversampler accumulator before any right shifting. This means that the bounds that one sets must be left shifted appropriately.


The F7 has a DCACHE that needs careful management.

Either disable it entirely: in initialisation,


Or place all DMA buffers into DTCM (the default in ChibiOS, no need to specify the section normally):

uint8_t mybuf[1024] __attribute__((section(".ram3")));

Or place all DMA buffers into SRAM2 and disable the DCACHE on SRAM2:

uint8_t mybuf[1024] __attribute__((section(".ram2")));

In ChibiOS, add #define STM32_SRAM2_NOCACHE TRUE to mcuconf.h, or during initialisation:

/* Enable the memory protection unit and set the
 * first region to SRAM2 with DCACHE disabled.
MPU->RNR = 7;
MPU->RBAR = 0x2007C000;
    (3<<24) |       /* (AP) Access permission: RW, RW */
    (4<<19) |       /* (TEX) outer cache policy: no cache */
    (0<<16) |       /* (B) inner cache policy (1/2): no cache */
    (0<<17) |       /* (C) inner cache policy (2/2): no cache */
    (1<<18) |       /* (S) shared */
    (13<<1) |       /* (SIZE) 2^(13+1) = 2^14 = 16K bytes */
    (1<<0);         /* (ENABLE) */
MPU->CTRL = (1<<2) | (1<<0);

Or, manage the cache manually with the following commands:

/* Write-through: commit anything written to cache into RAM.
 * Call before a DMA transfer from RAM.

/* Invalidate: purge anything in the cache so it will be reloaded from RAM.
 * Call before a DMA transfer to RAM.

/* Clean and invalidate the DCACHE. */

Helpful references: NuttX, ChibiOS

STM32 and ChibiOS

SysHalt error LED

Light up an LED when a SysHalt occurs (e.g. due to a failed assertion).

In chconf.h, find CH_CFG_SYSTEM_HALT_HOOK(reason) and replace the empty body with something like:

#define CH_CFG_SYSTEM_HALT_HOOK(reason) { \
    GPIOA->ODR |= (1<<5); \

where GPIOA5 (in this case) is an LED.

Low power sleep during idle

Go to WFI sleep mode when no threads are running (requires idle thread). In chconf.h:

/* Enable wait-for-interrupt sleep during idle */

Consider enabling debug access during sleep (see above) when this is enabled.

STM32F7: Disable DCACHE on SRAM2

In mcuconf.h:

#define STM32_SRAM2_NOCACHE                 TRUE

Reset on clock security system failure

If clocking from some external source, it can be nice to enable the CSS so if the external source fails you can do something about it. Best thing to do is perhaps a reset:

/* The clock security system calls this if HSE goes away.
 * Best bet is probably to reset everything.
void NMI_Handler(void) {

To enable CSS:


Stop NetworkManager trying to talk to USB-CDC devices on Ubuntu

In /etc/udev/rules.d/blabla.rules, put:

ATTRS{idVendor}=="1d50", ATTRS{idProduct}=="6018", ENV{ID_MM_DEVICE_IGNORE}="1"

Modify vendor and product IDs as appropriate (black magic probe shown here).

STM32 and libopencm3

gpio_get returns bits in original position

If you’re using gpio_get and assigning its return value to something, don’t forget it keeps all bits in their original position, so you may need to shift the result down to get a 0 or 1. Thanks, @jamescoxon!

STM32 and GDB


This allows the embedded code to access stdin/stdout and files on the host computer, over the debug link. Link with --specs=rdimon.specs -lrdimon, or in ChibiOS update your Makefile:

# user libraries
ULIBS += --specs=rdimon.specs -lrdimon```

Then in your main.c add this prototype:

extern void initialise_monitor_handles(void);

Finally call initialise_monitor_handles() somewhere. This will hang forever if you do not have a debugger attached, so you might want to check if the stm32 is under debug first:

if(CoreDebug->DHCSR & 1) {

Now you can call printf, scanf, fopen, etc from your code and they will target the attached host.

Displaying call stack after a HardFault

By default GDB doesn’t know how to view the call stack that lead to a HardFault, but we can help it. Stick this anywhere:

/* On hard fault, copy HARDFAULT_PSP to the sp reg so gdb can give a trace */
register void *stack_pointer asm("sp");
void HardFault_Handler(void) {
    asm("mrs %0, psp" : "=r"(HARDFAULT_PSP) : :);
    stack_pointer = HARDFAULT_PSP;

Optionally include GPIOA->ODR |= 1; if you have an error LED that can be lit when this occurs.


STM32 and Rust

Quickstart template for Cortex-M projects

Guide to embedded devleopment in Rust