Compiling software for the F103 is very easy but – as so often – does take a little preparation. In this case, we will need set up a few build files and make sure they are correct for the F103.
But first, let’s create a new directory and “chdir” into it:
mkdir ~/myf103apps
cd ~/myf103apps
Then you need these 3 files, which can all be downloaded from GitHub:
Makefile.include
- contains a few settings to select the F103rules.mk
- the main rules for “make”, copied from the libopencm3 projectstm32f103.ld
- memory map and sizes for the F103 variant we’re using
These files don’t have to be adjusted or changed in any way, they just need to be present.
Now we can finally create a first demo, and of course the first one to try is going to be the “Hello world” of Physical Computing: blinking the on-board LED - so let’s get moving!
Step 1 - Create a directory inside the one you just set up, called
“blink
”, and cd
again:
mkdir blink
cd blink
Step 2 - Create a small, project-specific “Makefile”, with this contents:
BINARY = blink
LDSCRIPT = ../stm32f103.ld
SCRIPT_DIR=..
default: $(BINARY).bin
include ../Makefile.include
Step 3 - Write the actual source code, in this case “blink.c
”, with this
contents:
#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>
int main (void) {
rcc_periph_clock_enable(RCC_GPIOA);
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
for (;;) {
gpio_toggle(GPIOA, GPIO1);
// create a small delay by looping for a while
for (int i = 0; i < 1000000; ++i) __asm("");
}
}
Step 4 - Compile this new application by entering the following command:
make
Step 5 - Upload the generated blink.bin
file to the Blue Pill:
This can be done as described in the previous article.
That’s it! - To make changes, simply edit blink.c
and repeat steps 4 & 5.
Some notes:
- this “edit, make, upload” cycle is the traditional way of building code from the command line, and it’s no different for embedded µC’s
- there’s no IDE: you can work with whichever editor you prefer - enjoy the freedom!
- the
Makefile
defines the rules used by themake
command - but most of these build rules are in the two files we prepared earlier:../Makefile.include
and../rules.mk
- the above
blink.c
example is typical for a libopencm3-based program: include the files needed for the functions you’re going to call, then define amain
function, which sets up the I/O pins and other hardware, and then enters an infinite loop doing whatever the program is supposed to do - the
rcc_*
andgpio_*
functions are part of libopencm3 - you need to visit the main site and browse the documentation to see what’s available - be sure to look under the STM32F103 section, since not all µC’s have the same capabilities - if you have a Black Magic Probe, ST-Link, or OpenOCD hooked up, you can add
some settings to your Makefile to automatically upload as last step of
make
- it’s easy to create additional programs, or variants, or little tests: simply
create additional directories next to
blink
, each with their own Makefile and source code - for a similar Blink example, see the one on GitHub - it contains slightly more code, because it also works on a different board, called the HyTiny
Although it’s nice to see it working, a blinking LED isn’t going to keep you excited for long…
So let’s try something a little more complex: we periodically send out a message
to the serial port, and when a <CR>
(Carriage Return) character is received,
we’ll toggle the on-board LED. In addition, we’ll write the code in C++ instead
of C, and we’ll split up the code to use setup()
and loop()
functions, in
the same way as the Arduino world is used to doing.
To reproduce this experiment, you need to make a copy of the “echo
” area on
GitHub.
The application-specific code is now in a file called “echo.cpp
” (the rest is
in “main.cpp
”):
#include <libopencm3/stm32/gpio.h>
#include <stdio.h>
// defined in main.cpp
extern int serial_getc ();
extern uint32_t millis();
void setup () {
// LED on HyTiny F103 is PA1, LED on BluePill F103 is PC13
gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO1);
gpio_set_mode(GPIOC, GPIO_MODE_OUTPUT_2_MHZ,
GPIO_CNF_OUTPUT_PUSHPULL, GPIO13);
}
void loop () {
printf("\nHit <enter> to toggle the LED (%lu) ...", millis());
// the following loop takes roughly one second
for (int i = 0; i < 1650000; ++i) {
if (serial_getc() == '\r') {
gpio_toggle(GPIOA, GPIO1);
gpio_toggle(GPIOC, GPIO13);
}
}
}
Note the convenience of having a “grown-up” printf()
at our disposal. This
makes it very easy to generate good-looking text output on the serial port (it
also increases the size of the generated application quite considerably, alas).
As before, you can enter make
to build this, and then upload echo.bin
to the
Blue Pill (or HyTiny). Here is a sample session using Folie for uploading:
$ make
CXX main.cpp
CXX echo.cpp
LD echo.elf
OBJCOPY echo.bin
text data bss dec hex filename
28528 2220 64 30812 785c echo.elf
$ folie -r
Select the serial port:
1: /dev/cu.Bluetooth-Incoming-Port
2: /dev/cu.usbmodem3430DC31
? 2
Enter '!help' for additional help, or ctrl-d to quit.
[connected to /dev/cu.usbmodem3430DC31]
!u echo.bin
30748b +V22 #0410 R .W .E writing: 121/121 done.
Hit <enter> to toggle the LED (0) ...
Hit <enter> to toggle the LED (1008) ...
Hit <enter> to toggle the LED (2020) ...
Hit <enter> to toggle the LED (3032) ...
Note that Folie switches into terminal mode once the upload is complete, so the serial output immediately starts showing up. Hitting the Enter key toggles the LED, as expected. It works!
In case you’re wondering: while called “echo”, this example doesn’t really echo characters at all! It is left as an exercise for the reader to change the above example into one which does…