Blinking an led on an STM32F1 Blue pill micro-controller with libopencm3 is simpler than any alternative for professional developers.
The Hello 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 this medium blog post: Programming STM32 Blue Pill using libopencm3, you will get to know about libopencm3 and how to use in our projects.
The libopencm3 project aims to create an open-source firmware library for various ARM Cortex-M 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.\
To read more about the library visit libopencm3 on GitHub.
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.
The SysTick register can only be accessed using word access.
-
Program the reload value:
The reload value can be loaded by setting theSTK_RVR
(Reload Value Register) register. 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, setSTK_CVR
to 99.The following snippet consfigures SysTick for 1 ms interrupt.
systick_set_reload(rcc_ahb_frequency / 1000 - 1);
-
Clear current value:
This register can be accessed using theSTK_CVR
(Current Value Register) variable. Bits 24:31 are reserved and 24-bit value can be read from bits 23:0. Writing any value this register sets it to zero along with settingSTK_CSR_COUNTFLAG
to zero.
The following API provided by libopencm3 achieves this tasks for the developer.systick_clear();
-
Select clock source-
Clock source can be set using theSTK_CSR_CLKSOURCE
(Clock source) bit (2) of theSTK_CSR
(Control and Status Register) register.
0 - AHB/8
1 - Processor Clock (AHB)
libopencm3 has given us the following API for configuring the clock source for SysTick.```CPP systick_set_clocksource(STK_CSR_CLKSOURCE_AHB); ```
-
Enable Tick interrupt-
To enable the Tick interrupt set theSTK_CSR_TICKINT
bit (2) of theSTK_CSR
register.\ The following API is provided by libopencm3 for this task.systick_interrupt_enable();
-
Start SysTick timer-
SysTick timer can be started by calling the following API function.STK_CSR_ENABLE
bit (0) of theSTK_CSR
register enables the counter. WhenSTK_CSR
is set to 1, the counter loads theSTK_RVR
value to theSTK_CVR
register and then counts down. On reaching 0, it sets theSTK_CSR_COUNTFLAG
to 1 and optionally asserts theSysTick
depending on the value ofSTK_CSR_TICKINT
. It then loads theSTK_RVR
value again and begins counting.
libopencm3 has provided the following API to enable SysTick timer.systick_counter_enable();
-
-
void rcc_periph_clock_enable(enum rcc_periph_clken clken): Enables the clock source for the peripherals.
To enable the clock source for GPIO Port C, passRCC_GPIOC
value toclken
input paramter of the API. -
void systick_set_clocksource(uint8_t clocksource): Sets the clock source for 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 passingSTK_CSR_CLKSOURCE_AHB
to theclocksource
value. -
void systick_clear(void): Clears SysTick current value register.
SysTick counter value is cleared and the counter flag is reset. -
systick_set_reload(uint32_t value): Sets the automatic reload value for SysTick 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 the predictable behavior, it is a good idea to set or clear the counter after set reload. To set the timer to tick every millisecond,rcc_ahb_frequency / 1000 - 1
value should be passed to the API. -
systick_interrupt_enable(void): Enables SysTick interrupt.
-
void systick_counter_enable(void): Enables SysTick counter.
-
gpio_set_mode(uint32_t gpioport, uint8_t mode, uint8_t cnf, uint16_t gpios): Set GPIO poin mode.
Sets the mode (input/output) and configuration (analog/digital and open drain/push pull), for a set of GPIO pins on a GPIO port. In the codegpioport
isGPIOC
,mode
isGPIO_MODE_OUTPUT_2_MHZ
for setting the pin as output with speed as 2MHz,cnf
asGPIO_CNF_OUTPUT_PUSHPULL
for setting it as output in push-pull mode forpin
GPIO13
. -
gpio_toggle(uint32_t gpioport, uint16_t gpios): Toggle a group of Pins.
The toggling is not atomic but but non-toggled pins are not affected. To toggle Pin13 of GPIO Port C, passGPIOC
asgpioport
andGPIO13
asgpios
. -
sys_tick_handler(void): Interrupt service routine for SysTick handler.
Increments theticks
variable. -
delay(uint32_t ms): Generates blocking delay. Executes
_NOP
instructions tillms
milliseconds are over.
This project configures the SysTick timer and uses it to generate a time-accurate delay for blinking an LED. The onboard LED connected to pin C13 blinks every 500ms.
-
make
Make utility is required for configuring and building this project. You can install make on Linux by running the command:sudo apt install build-essential
-
gcc-arm-none-eabi toolchain
ARM cross-platform toolchain is required to build applications for arm MCUs. The toolchain can be installed by running the following command:sudo apt install gcc-arm-none-eabi
-
openocd
It is an Open On Circuit Debugging tool used to flash and debug arm microcontrollers. You can install openocd on Linux by running the command:sudo apt install openocd -y
-
Cortex Debug extension
This extension for VSCode helps debug the application on Blue Pill. The contents of registers as well as memory are visible in the context menu. Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.ext install marus25.cortex-debug
-
libopencm3
After cloning this repository, clone the libopencm3 submodule and build for the required microcontrollers.git submodule update --init pushd libopencm3 make TARGETS="stm32/f1" popd
src
directory contains all source files for the projectinclude
directory contains all header files for the projectlibopencm3
directory contains source and header files for the library
stm32f1.ld
- linker script specific to the blue pill boardsrc\main.cpp
- application code bodySTM32F103.svd
- contains the description of the system contained in Arm Cortex-M processor-based microcontrollers, in particular, the memory-mapped registers of peripherals.Makefile
- main root level makefile for the project, which builds the binary
Running the project is super easy. Just clone, build and flash.
-
Using HTTPS
git clone https://github.com/csrohit/bluepill-systick.git cd bluepill-systick/opencm3 git submodule update --init
-
Using ssh
git clone git@github.com:csrohit/bluepill-systick.git cd bluepill-systick/opencm3 git submodule update --init
All the configuration required for building this project is given below.
-
Build output directory In
Makefile
, the output directory can be configured using the variableBUILD_DIR
. -
Build type In
Makefile
, the build type can be configured using the variableBUILD_TYPE
. Possible values areDebug
andRelease
. -
Binary name In
Makefile
, the output directory can be configured using the variableTARGET
. ** update the above info in.vscode/launch.json
as well for debugging to work.
- Before building the project, remember to build the libopencm3 library for the specific micro-controller, without it project build will fail.
pushd libopencm3
make TARGETS="stm32/f1"
popd
- Run the following command in the terminal to generate flashable binaries for the blue pill board. Build files will be written to Build Output Directory as configured.
make all
- Connect ST-Link to PC and blue pill board using SWD headers.
- Put the blue pill board in programming mode.
- Run the following to flash the board with binary.
make flash
The Onboard led connected to Pin C13 can be observed to be blinking every 500ms.
Click on the Run and Debug
option in the VsCode sidebar. Then launch the Cortex Debug
target.
Happy debugging...