ChibiOS/RT

By | 2013-09-16

ChibiOS is an open source RTOS for embedded systems. It’s comparable with FreeRTOS; but for my money is a nicer project.

  • Comes with a hardware abstraction layer – i.e. device drivers (kind of). That means we target the ChibiOS API rather than a particular platform. A lot of manufacturers supply a device layer specific to their chip; ChibiOS goes a step further – including a platform-neutral device layer. FreeRTOS has no HAL.
  • Static memory allocation. FreeRTOS supports dynamic allocation only; ChibiOS supports static and dynamic memory allocation. This can be important if you’re really memory constrained; and also ensures you don’t get fragmentation of your heap storage.
  • I get the feeling there is a better community around ChibiOS; it’s more inclusive and faster moving. It’s certainly got less of a corporate feel, concentrating a lot more on the code.

Begin then by downloading the ChibiOS source. You can get it from the ChibiOS website, or use the following git mirror.

$ git clone https://github.com/ChibiOS-Upstream/ChibiOS-RT

ChibiOS is convenient because it comes with startup code and a build environment that’s pretty easy to customise. We’ll begin by building one of the demo applications.

$ cd demos/ARMCM4-STM32F407-DISCOVERY

Now edit the Makefile to point at your compiler (I’d suggest the ARM-maintained gcc). Here’s my modification:

-TRGT = arm-none-eabi-
+TRGT = /opt/gcc-arm-none-eabi-4_7-2013q2/bin/arm-none-eabi-

A simple make should then create you a binary in build/ch.bin.

Now, we’ll want to start our own project. Copy the contents of this demo directory to some empty directory outside of the ChibiOS tree (your own projects aren’t part of ChibiOS so don’t pollute it with your code). Then edit the Makefile again.

 # Define project name here
-PROJECT = ch
+PROJECT = chibios-test
 
 # Imported source files and paths
-CHIBIOS = ../..
+CHIBIOS = ../../../YOUR/PATH/TO/ChibiOS/

Before we even start using the multitasking facilities of ChibiOS its HAL let’s us easily write portable programs. Delete the main.c that you copied from the demo and we’ll start with a clean slate.

#include <ch.h>
#include <hal.h>

#define GPIOD_LED_ORANGE      GPIOD_LED3

int main(void)
{
    halInit();
    chSysInit();

    palSetPadMode(GPIOD, GPIOD_LED_ORANGE, PAL_MODE_OUTPUT_PUSHPULL);

    while(1){
        palSetPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
        palClearPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
    }
}

Note that you will still need the ChibiOS configuration headers, chconf.h, halconf.h, and mcuconf.h. These are pretty self-explanatory and simply enable and disable various ChibiOS components.

Build the binary and then flash it with openocd (available in Debian).

openocd \
    -f /usr/share/openocd/scripts/board/stm32f4discovery.cfg \
    -c "init" \
    -c "reset halt" \
    -c "sleep 100" \
    -c "wait_halt 2" \
    -c "echo \"--- Writing build/chibios-test.bin\"" \
    -c "flash write_image erase build/chibios-test.bin 0x08000000" \
    -c "sleep 100" \
    -c "echo \"--- Verifying\"" \
    -c "verify_image build/chibios-test.bin 0x08000000" \
    -c "sleep 100" \
    -c "echo \"--- Done\"" \
    -c "resume" \
    -c "shutdown"
Open On-Chip Debugger 0.7.0 (2013-08-04-10:13)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.sourceforge.net/doc/doxygen/bugs.html
srst_only separate srst_nogate srst_open_drain connect_deassert_srst
Info : This adapter doesn't support configurable speed
Info : STLINK v2 JTAG v14 API v2 SWIM v0 VID 0x0483 PID 0x3748
Info : Target voltage: 2.876056
Info : stm32f4x.cpu: hardware has 6 breakpoints, 4 watchpoints
target state: halted
target halted due to debug-request, current mode: Thread 
xPSR: 0x01000000 pc: 0x080001f0 msp: 0x20000400
--- Writing build/chibios-test.bin
auto erase enabled
Info : device id = 0x10016413
Info : flash size = 1024kbytes
wrote 16384 bytes from file build/chibios-test.bin in 1.023979s (15.625 KiB/s)
--- Verifying
verified 7580 bytes in 0.142001s (52.129 KiB/s)
--- Done
shutdown command invoked

It might seem large at 7580 bytes for a simple LED flasher, but remember that this is 7580 bytes for ChibiOS too – which we’re using very little of. For interest you will find the assembly listing (which includes a lot of debug sections) in build/lst/main.lst.

It’s perhaps not clear why the above is so magical. Let’s look again:

    while(1){
        palSetPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
        palClearPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
    }

Think about this in the context of our normal way of writing embedded software. If we wanted to flash an LED in a larger application, we would have to keep a timer, store a separate state and ensure that we always got back to the flasher code in our main loop for the decision to be made about whether it’s time to change state. When we’re using an RTOS, we can write routines without regard (within reason) to what else needs doing. This LED flasher code would be the same whether this is all the program is doing, or if it has ten other things to do.

Let’s see that facility in action.

#include <ch.h>
#include <hal.h>

#define UNUSED(x) (void)(x)

#define GPIOD_LED_ORANGE      GPIOD_LED3
#define GPIOD_LED_GREEN       GPIOD_LED4
#define GPIOD_LED_BLUE        GPIOD_LED6

static WORKING_AREA(waThread1, 128);
static msg_t Thread1(void *arg)
{
    UNUSED(arg);

    chRegSetThreadName("blinker");

    while (TRUE) {
        palSetPad(GPIOD, GPIOD_LED_BLUE);
        chThdSleepMilliseconds(333);
        palClearPad(GPIOD, GPIOD_LED_BLUE);
        chThdSleepMilliseconds(333);
    }

    return 0;
}

int main(void)
{
    halInit();
    chSysInit();

    palSetPadMode(GPIOD, GPIOD_LED_ORANGE, PAL_MODE_OUTPUT_PUSHPULL);
    palSetPadMode(GPIOD, GPIOD_LED_BLUE, PAL_MODE_OUTPUT_PUSHPULL);

    chThdCreateStatic(waThread1, sizeof(waThread1), NORMALPRIO, Thread1, NULL);

    while(1){
        palSetPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
        palClearPad(GPIOD, GPIOD_LED_ORANGE);
        chThdSleepMilliseconds(500);
    }
}

This is exactly as before, but we’ve added an additional thread on top of the so-called main thread, flashing the blue LED at a slightly different rate. Both flashers are simple, linear code. Have a think about how much infrastructure we would have to implement without ChibiOS just to do this simple task.

Leave a Reply