Computing stuff tied to the physical world

Developing in C/C++ for STM32

Let’s now use the ARMinARM setup to try out a few of the included example programs.

CMSIS

The CMSIS library is supplied by STM, as ARM partner and µC manufacturer:

$ cd arminarm/src/CMSIS_StdPeriph/examples/leds/
$ make
mkdir -p build/src/
arm-none-eabi-gcc -c -O0 -g -Wall -I. -mcpu=cortex-m3 -mthumb [...]
[...]
$ arminarm -f build/leds.bin 
flashing build/leds.bin using stm32flash
[...]
Wrote and verified address 0x08005a64 (100.00%) Done.

Resetting device... done.

That’s it, the red LED should now be flashing (occasionally, you may have to reset the STM32F103 chip before it lets you flash new code onto it, using: “arminarm -r“).

The sample code for the above looks as follows (edited a bit for brevity):

#include <board.h>

static void delay (uint32_t ms) {
    ms *= 1440; // 3360=168MHz, 1440=72MHz
    while (ms--)
        __NOP();
}

int main (void) {
  LEDS_Init();
  while (1) {
    static uint32_t counter = 0;
    ++counter;
    LEDS_Off();
    LEDS_Set(LED[counter % 2]);
    delay(250);
  }
}

Easy stuff, as you can see. Unfortunately, there seems to be quite a bit of runtime code overhead in this CMSIS setup – some 22 KB for this trivial code, as you can see here:

$ arm-none-eabi-size build/leds.elf 
   text    data     bss     dec     hex filename
  22076    1064       4   23144    5a68 build/leds.elf

But then again, one could argue that it doesn’t matter with 512 KB available flash memory.

Maple

The libmaple code was developed by LeafLabs, who have now moved on to other projects:

$ cd arminarm/src/libmaple/examples/
$ ls
blinky.cpp                   test-fsmc.cpp
debug-dtrrts.cpp             test-print.cpp
exti-interrupt-callback.cpp  test-ring-buffer-insertion.cpp
exti-interrupt.cpp           test-serial-flush.cpp
freertos-blinky.cpp          test-serialusb.cpp
fsmc-stress-test.cpp         test-servo.cpp
i2c-mcp4725-dac.cpp          test-session.cpp
mini-exti-test.cpp           test-spi-roundtrip.cpp
qa-slave-shield.cpp          test-systick.cpp
serial-echo.cpp              test-timers.cpp
spi_master.cpp               test-usart-dma.cpp
test-bkp.cpp                 vga-leaf.cpp
test-dac.cpp                 vga-scope.cpp

Unfortunately there is no ready-made demo, but we can get an idea from “blinky.cpp“:

#include <wirish/wirish.h>

void setup () {
    pinMode(BOARD_LED_PIN, OUTPUT);
}

void loop () {
    togglePin(BOARD_LED_PIN);
    delay(100);
}

__attribute__((constructor)) void premain() {
    init();
}

int main (void) {
    setup();
    while (true)
        loop();
    return 0;
}

The code mimics the way the Arduino runtime works, by calling setup() and loop(), which as you can see is nothing magical at all. The result presumably blinks the LED again.

There are quite a few more advanced examples, and most (if not all) should also work on the ARMinARM board since it’s the same processor as the Maple boards by LeafLabs.

libOpenCM3

The libOpenCM3 project has excellent support for the STM32 µCs, but also supports a range of others from TI, NXP, Energy Micro, and more. It’s actively being maintained and developed further in true open-source style, on GitHub. There are many examples from the libOpenCM3 community, which can also be found on GitHub. The API docs are generated with DoxyGen and can be found here.

Here is the blink demo, as provided in the ARMinARM setup:

$ cd arminarm/src/libopencm3-prj/projects/blink/
$ make V=1
Using ../../libopencm3/ path to library
arm-none-eabi-gcc -Os -g -Wextra -Wshadow [...]
arm-none-eabi-gcc --static -nostartfiles [...]
arm-none-eabi-objcopy -Obinary blink.elf blink.bin
$ arminarm -f blink.bin 
flashing blink.bin using stm32flash
[...]
Resetting device... done.

And again the LED is blinking! But this time, the code is dramatically smaller:

$ arm-none-eabi-size blink.elf 
   text    data     bss     dec     hex filename
   1212      12       0    1224     4c8 blink.elf

As it should be: unused pieces are no longer included in the build. This probably leads to faster compiles, and definitely to faster uploads. Here is the blink code itself:

#include <libopencm3/stm32/rcc.h>
#include <libopencm3/stm32/gpio.h>

static void clock_setup (void) {
  rcc_clock_setup_in_hse_8mhz_out_72mhz();
  rcc_periph_clock_enable(RCC_GPIOB);
}

static void gpio_setup (void) {
  gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_50_MHZ,
          GPIO_CNF_OUTPUT_PUSHPULL, GPIO0);
}

int main (void) {
  int i;
  clock_setup();
  gpio_setup();
  while (1) {
    gpio_toggle(GPIOB, GPIO0);
    for (i = 0; i < 8000000; i++)
      __asm__("nop");
  }
  return 0;
}

There’s slightly more detail to get everything set up, but all of it is nicely wrapped in pretty self-explanatory functions. With this slightly more verbose style comes considerably more flexibilty to tweak – i.e. using different clocks or setting the I/O pins to a different mode.

Other options

We’re not limited to the above, though. There are also FreeRTOS and ChibiOS, which are real-time OS’es supporting multiple tasks, and extensive communication mechanisms between them. While some of this may seem overly complex (and surely can be!), simple examples show that the extra functionality need not be an issue. See this blinking LED demo in ChibiOS, for example.

But since these runtimes are not (yet?) part of the ARMinARM setup, you’ll need to dive in a bit deeper and install the necessary code yourself. This is usually quite simple, BTW.

So there you have it: a few different runtimes, all leading to that “hello world” of Physical Computing, i.e. a blinking LED. Note that each of these examples is in C, not C++. Full support for C++ is probably present, but you’ll need to investigate and explore further.

But hey, 10..20 lines of code just to make an LED blink? Can’t we do better than that?