STM32 Blue Pill Baremetal Programming

STM32 Blue Pill Programming using libopencm3

A comprehensive guide to getting started with programming STM32 Blue Pill microcontroller and Cortex M3 microcontrollers using libopencm3 library

Rohit Nimkar
6 min readNov 18, 2022

What if you want to evolve from an embedded enthusiast/hobbyist into a professional😎 developer but don’t want to leave the comfort of Arduino, I have a solution for you🥳.

libopencm3 provides you with prebuilt APIS like Arduino while retaining the joy of programming on the register level. The complete project is built inside of Visual Studio Code and avoids using any ugly-looking IDE😇.

STM32 Blue Pill with with libopencm3
Programming STM32 Blue Pill with libopencm3

Blinking a led on an STM32F1 Blue pill micro-controller with libopencm3 is simpler than any alternative for professional developers.

In theHello World of embedded world, blinking led is super easy to implement by using a SysTick timer as a time base. The project is implemented using a library called libopencm3. In a blog post, you will get to know about libopencm3 and how to use it in our projects.

libopencm3

The libopencm3 project aims to create an open-source firmware library for various ARM Cortex-M3 microcontrollers. The library supports most subsystems on STM32F1 and other MCU series. It uses gcc-arm-none-eabi as the toolchain and make as the build system. Project-specific steps related to configuration and building are given in the sections below.

The library provides the most user-friendly and easy-to-use environment for budding programmers. The APIs are as easy to understand as the ones provided by the Arduino framework, but at the same time introduce you to the world of register-level programming.

The APIs provided by the library, unlike Arduino, indicate what’s actually happening behind closed doors. Visual studio code with its IntelliSense and code navigation features opens those closed doors. Now you can actually look into the source code of the library to get better insights into your hardware.

The template project available with libopencm3 is simple but devs are left in the dark about the build process. I have created a new MakeFile from scratch which contains everything there is to know about the build process.

So, let’s get started with the programming part…

SysTick Timer

The processor has a 24-bit system timer, SysTick, that counts down from the reload value to zero, reloads, and counts down on the subsequent clocks.
System Tick Time (SysTick) generates interrupt requests on regular basis. This allows an os to perform context switching to support multitasking. For applications that do not require an OS, the SysTick can be used for timekeeping, time measurement, or as an interrupt source for tasks that need to be executed regularly.

Control flow of the program

The complete flow diagram of the SysTick timer is shown as well explained below. It displays the dependency between the various signals and respective input and output flags.

control flow diagram for SysTick timer
SysTick: control flow diagram

Set clock source

systick_set_clocksource(STK_CSR_CLKSOURCE_AHB);

API systick_set_clocksource(uint8_t clksource) sets the clock source for the SysTick timer. The clock source can be either the AHB clock or the same clock divided by 8. The clock source is set to AHB by passing STK_CSR_CLKSOURCE_AHB to the clksource value. To set the clock source to AHB/8, pass STK_CSR_CLKSOURCE_AHB_DIV8 the value as clksource .

Program the reload value:

systick_set_reload(rcc_ahb_frequency / 1000 - 1);

API systick_set_reload(uint32_t value) sets the automatic to reload value for the timer. The counter is set to the reload value when the counter starts and after it reaches zero.
The SysTick counter value might be undefined upon the startup. To get predictable behavior, it is a good idea to set or clear the counter after the set reload. To set the timer to tick every millisecond, rcc_ahb_frequency / 1000 - 1 the value should be passed to the API.
This value is set to 1 less than the number of clock cycles needed for the interrupt as the timer counts both reload value as well as zero. e.g. If the SysTick interrupt is required every 100 clock pulses, set STK_CVR to 99.

Clear Current value

systick_clear();

The API systick_clear(void) clears the current value register which in turn resets the STK_COUNTFLAG bit in the status register. It resets the STK_CVR register. (A write operation with any value resets the register.)

Enable tick interrupt

systick_interrupt_enable(void);

The API systick_interrupt_enable(void) enables SysTick to interrupt. Every time the timer overflows/underflows interrupt service routine is executed by the MCU.
It sets the STK_CSR_TICKINT bit (2) of the STK_CSR register.

Start timer

systick_counter_enable();

The API sets the STK_CSR_ENABLE bit of STK_CSR register. When STK_CSR is set to 1, the counter loads the STK_RVR value to the STK_CVR register and then counts down. On reaching 0, it sets the STK_CSR_COUNTFLAG to 1 and optionally asserts the SysTick depending on the value of STK_CSR_TICKINT. It then loads the STK_RVR value again and begins counting.

Important functions

This section lists the functions defined by the program.

GPIO setup

GPIO initialization

The functions enable the peripheral clock for the GPIO Port C. The clock needs to be enabled before performing any operations on the peripheral.
It configures the Pin13 of Port C in push-pull output mode at 2 MHz speed. We have selected the lowest speed available for the application.

SysTick Init

SysTick initialization

The function performs the following steps:

  1. Set clock source as AHB
  2. Clear current value register
  3. Set the reload value register
  4. Enable tick interrupt
  5. Start the timer

Detailed explanations about the steps and internal workings of the APIs are discussed in the Control Flow section of this article.

Interrupt handler and delay function

Code about the delay and interrupt service routine

The above snippet defines a _millis to store the count of tick interrupts. It is declared as volatile as it is being accessed in both threads as well as handler modes.
The interrupt handler function increments the value of _millis on every interruption.
The delay function uses the _millis variable to provide the required delay to the program

Main function

The entry point of the program

The main function invokes the initialization functions for GPIO as well as the SysTick timer. It then runs in an infinite loop to toggle the LEDconnected to PC13 every 500 milliseconds.

Output

After successfully building and flashing the code on the STM32 Blue Pill board the onboard led connected to PC13 can be seen blinking at intervals of 500ms.

Getting started with the project

The source code for this article is available on the following repository.

The project has multiple variants including freeRTOS, Zephyr RTOS, Pure Baremetal, Arduino, etc. Click on the opencm3 variant.

If you are new to bare-metal programming using visual studio code then the following article will help you get started.

About the author

I am an embedded systems engineer working with ARM Cortex-M microcontrollers. I work on freelance projects in embedded systems as well as UI/UX design and development.

I love to read and write about Linux, Embedded C++/C, RTOS, and Operating Systems internals. Do follow me on LinkedIn for more such interesting content.

--

--

Rohit Nimkar

Know a little about coding but aim to self employ myself from it.