• Introducing modm-devices: hardware descriptions for AVR and STM32 devices

    For the last 2 years Fabian Greif and I have been working on a secret project called modm: a toolkit for data-driven code generation. In a nutshell, we feed detailed hardware description data for almost all AVR and STM32 targets into a code generator to create a C++ Hardware Abstraction Layer (HAL), startup & linkerscript code, documentation and support tools.

    This isn’t exactly a new idea, after all very similar ideas have been floating around before, most notably in the Linux Kernel with its Device Tree (DT) effort. In fact, modm itself is based entirely on xpcc which matured the idea of data-driven HAL generation in the first place.

    However, for modm we focused on what goes on behind the scenes: how to acquire detailed target description data and how to use it with reasonable effort. We now have a toolbox that transcends its use as our C++ HAL generator and instead can be applied generically to any project in any language (*awkwardly winks at the Rust community*). That’s pretty powerful stuff.

    So let me first ease you into this topic with some historic background and then walk you through the data sources we use and the design decisions of our data engine. All with plenty of examples for you to follow along, just stay well clear of those hairy yaks in the distance.

    Read on →

  • The Curious Case of xpcc's Error Model

    In hindsight it is quite apparent that xpcc and therefore also the @RCA_eV robot code was missing a good error model. Until now xpcc’s way of dealing with failures included using static_assert at compile time and returning error codes at runtime whenever it was deemed necessary. We never considered runtime assertions, nor catching hardware errors like the ARM Cortex-M Fault exceptions. We crashed and burned, a few times literally.

    So what can we do that is simple to use and efficient on AVR and Cortex-M devices, but still powerful enough to be useful? It’s time we thought about our error model.

    Read on →

  • State of the Blog

    Over the last years I worked a lot on xpcc and other @RCA_eV related projects and their documentation has served well enough for capturing my written output.

    But now my interests have diversified a little and I’m working on things are that don’t fit into one coherent project anymore. So I’ve decided to move my content from blog.xpcc.io over here and talk about embedded development more in general. Let’s see how this goes.

  • Computing and Asserting Baudrate Settings at Compile Time

    Prescaler and baudrate calculations are a tricky topic. I have had many situations where the baudrate turned out to be off by a couple of percent, which was enough to render my serial output streams unreadable. Sure, calculating the baudrate error beforehand would have saved me some hours of useless debugging, however, that would require understanding the often complicated mathematical formula hidden somewhere in the depths of the datasheet describing the prescaler vs. baudrate relationship.

    And that seemed to be more work than just using a logic analyzer to measure the resulting error. Of course, this felt like using a sledgehammer to crack a nut and it was neither a fast nor practical solution.

    I think there exists a better solution and I think it can be done using pure C++. This solution needs to be able to:

    1. compute the best possible prescaler settings for the desired baudrate, and
    2. notify me when the desired baudrate cannot be achieved without unresonable error.

    Read on →

  • Typesafe Register Access in C++

    When you are writing software for microcontrollers, reading and writing hardware registers becomes second nature. Registers and bit mappings are typically “modeled” using C preprocessor defines, and usually provided to you by your cross compiler toolchain in device specific header files.

    Setting up and toggling PG13 on the STM32F4 this way looks rather… unreadable:

    // set push-pull, output
    GPIOG->OSPEEDR = (GPIOG->OSPEEDR & ~(3 << 26)) | (3 << 26);
    GPIOG->MODER   = (GPIOG->MODER & ~(3 << 26)) | (1 << 26);
    GPIOG->OTYPER &= ~(1 << 13);
    GPIOG->PUPDR  &= ~(1 << 13);
    
    while(true)
    {
        GPIOG->ODR ^= (1 << 13);  // toggle
        // delay
    }
    

    It did not really dawn on me how primitive this concept was until I was forced to model a memory map myself for one of our many device drivers. Since I have never been a friend of using the C preprocessor in C++ unless absolutely necessary, it seemed like a good opportunity to research how best to implement this in pure C++.

    Read on →