Skip to content
examples of blinking the STM32F103C8 "blue pill" development board (LED on PC13) using various methods ⛺
C Assembly Other
Branch: master
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.


Blink an STM32F103C8 "blue pill" development board (LED on PC13) using various methods:

Size of each binary for comparison:

pill_blink $ find . -name '*.bin' -exec ls -l {} \;
-rwxr-xr-x  1 admin  staff  3496 Dec 22 20:58 ./cubemx/build/pill_blink.bin
-rwxr-xr-x  1 admin  staff  1088 Dec 22 22:43 ./opencm/pill_blink.bin
-rwxr-xr-x  1 admin  staff   608 Dec 22 23:00 ./opencm-registers/pill_blink.bin
-rwxr-xr-x  1 admin  staff   440 Dec 23 00:10 ./bare-metal/pill_blink.bin

Blinking example

How to build these examples? First, install an ARM compiler, I use the GNU ARM Embedded Toolchain installed via Homebrew as follows: brew cask install gcc-arm-embedded. See each individual project directory for additional instructions; all are using the make tool for building.

Where to acquire a blue pill? Search on Aliexpress or eBay for "stm32", look for the cheapest. I use these:

How to flash the firmwares? You can use any USB-to-serial adapter, but for development I prefer to flash another blue pill as a Black Magic Probe since it can be used both for uploading via serial and debugging via SWD, see: Converting an STM32F103 board to a Black Magic Probe. Assuming you want to do this too, connect the Black Magic Probe blue pill to the target blue pill as follows:

Black Magic Probe blue pill target blue pill
A2 (TX) A10
A3 (RX) A9
A5 (SWCLK) SWD header CK
B14 (SWDIO) SWD header O
5V 5V

If using a regular USB-to-serial adapter, connect only A10 and A9 and ground. For flashing the firmware I use stm32loader, example:

python -p /dev/cu.usbmodemE3C896E3 -e -w -v bare-metal/pill_blink.bin

How to debug? Use a dongle capable of the Serial Wire Debug protocol, connect to the right-angle header on the blue pill board, assuming you're using a BMP (see above), connect using GDB:

arm-none-eabi-gdb -ex "target extended-remote /dev/cu.usbmodemE3C896E1" -ex "monitor swdp_scan"

then attach with att 1 and you should be able to continue, examine registers and memory, and step through as usual. To load symbols, build with make CFLAGS+=-g and load the elf with file.

+ arm-none-eabi-gdb -ex 'target extended-remote /dev/cu.usbmodemE3C896E1' -ex 'monitor swdp_scan'
Available Targets:
No. Att Driver
 1      STM32F1 medium density
(gdb) att 1
Attaching to Remote target
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x08000194 in ?? ()
(gdb) file pill_blink/bare-metal/pill_blink.elf
A program is being debugged already.
Are you sure you want to change the file? (y or n) y
Reading symbols from pill_blink/bare-metal/pill_blink.elf...done.
(gdb) c
Program received signal SIGINT, Interrupt.
0x0800017e in reset_handler () at pill_blink.c:9
9	        for (int i = 0; i < 1000000; ++i) __asm__("nop");

Any simpler? Yes, see also Arduino for STM32 at, for a friendlier Arduino-compatible STM32 platform where digitalWrite() can toggle the LED, but these examples are primarily intended as experiments in lower-level embedded programming without Arduino, for background see also: STM32 Blue Pill ARM development board first look: from Arduino to bare metal programming.

See also accompanying blog post: JTAG/SWD debugging via Black Magic Probe on an STM32 blue pill and blinking a LED using STM32CubeMX, libopencm3, and bare metal C



You can’t perform that action at this time.