Friday, May 24, 2013

Peripheral Template Library - attempt to efficiently capture common microcontroller algorithms in C++

There were not many posts in this year, partly because I decided to concentrate on writing code and hacking things instead. One of the project I find myself hacking regularly during last half-year is a library modestly called Peripheral Template Library.

Ever since initial amazement with Arduino warn off, it became clear that while it provides concise and easy to use (to learn, to understand) API, it is to schematic, implemented too inefficiently, libraries have reuse, compatibility and configurability problems, and all those things are not portable across really wide MCU range and otherwise not production ready (because of all those efficiency, reuse, etc. issue), but rather a toybox.

Trying to implement things without Arduino overhead, there was growing dissatisfaction with the need to slash code when moving from one MCU to another (due to economical and availability issues, or plain for creative reasons). It became clear the need to abstract things away, but without losing efficiency. The only way to achieve that is metaprogramming. As nowadays nobody writes MCU software in LISP, I turned to C++ - after almost 10 years break I guess.

Many embedded programmers shrug at C++, and well, I did that for any C++ at all for quite some time. But modern C++ is not that "object-oriented syntactic sugar" which your old Turbo C++ 1.0 was (yeah, last one which could run on your old XT, because 2.0 required 286 and protected mode memory extender). Modern C++ is multi-paradigm language, of which "runtime" "object orientation" is just small part. The core part is however compile-time type-based metaprogramming template engine, that's something which allows to achieve aforementioned abstraction/efficiency ratio (and we don't talk here about "C" efficiency here, but something closer to Assembler level).

That's how PTL was born. The idea was to wrap the most core part of any MCU - GPIO (General Purpose Input/Output), and on top of it implement support for standard communication protocols - UART, SPI, I2C, 1-Wire, etc. Everything implemented as templated classes, so can be easy specialized to use hardware blocks of particular MCU. Generalized cross-MCU was one of top goals from the start, i.e. more attention was given to design and implementation of classes which capture basic MCU functionality which is available in any MCU, rather than adhoc peculiarities of particular MCU. The latter can be wrapped too of course - on demand.

Past GPIO and communication protocols, handling timers and delays was second goal. Support for CPU-based delay - both (perfect-cycle) static and dynamic was implemented, then basic timer interface was added, with delay on top of it, with all 3 kinds of delays providing the same interface and thus fully replaceable and combinable.

What comes out of it is available at https://github.com/pfalcon/PeripheralTemplateLibrary . MSP430, AVR, STM32 and EFM32 are supported (all to the different level though, but the basic functionality as described above is available for all by now). The grand aim is to design framework to support all possible (and impossible) MCUs ;-).

There're still log of refactors to do and functional block of individual MCUs to wrap. Some aspects, like clock tree management needs more though and design. Then, there's a need to add bindings/drivers for specific devices, I did that for few already, but of course that's area where I won't be able to compete with existing code, so I'm looking into finding good codebases to port.

License-wise, Peripheral Template Library is released under LGPL3 license, but I'm already thinking about adding linking exception and/or switch to BSD-like license altogether.

Here's a usual LED blinker example:

#include <gpio.hpp>
#include <cpu.hpp>
#include <board.hpp>
#include <delay_static.hpp>
#include <delay_time.hpp>

using namespace PTL;

typedef TimeDelay<board::freq, StaticDelay> delayer;

int main()
{
    cpu::init(cpu::DEFAULT);
    board::LED::port::enable();
    board::LED::output();
    while (true) {
        board::LED::high();
        delayer::delay_ms(500);
        board::LED::low();
        delayer::delay_ms(500);
    }
}

This will compile and work out of the box on any of supported boards - specific LED to use (board::LED) is configured in board config header.

Let me know what you think about PTL. For me, it's definitely exciting, innovative area - not many libs provide such efficient support with such wide heterogeneous coverage, and you really do architecturing and design here, not just dumb boring code. (I admit that partly PTL effort is driven by dissatisfaction of technical inefficiencies, imperfection and warts in "one level bigger" Linux world.)

6 comments:

  1. Hi! I've just started embedded programming and, as a C++ programmer, my first reaction was to start writing a template library to automate the small patterns I was noticing... and a few days later I found this, all the work done! PTL looks really great, I'll keep exploring it and learning from it. Thanks!

    ReplyDelete
  2. I've been trying to use the uart_echo example in the MSP430 Launchpad without any success. I'm trying to connect from Linux. Your post from March/2012 worked wonders with the TI example, but I can't make it work with PTL's uart. I noticed it needs to receive a character first, so I tried echo > /deb/ttyACM0 while running hexdump on another console, tried changing the baudrates (both in the uart typedef and in stty), even tried minicom, but nothing works. Can you give me some pointers? Thanks!

    ReplyDelete
  3. Hi Reis, I'm glad that you were able to find PTL, it was my idea to let folks who're interested in C++ & MCUs to be able to try something and maybe extend it, instead of starting from scratch again and again. I appreciate your feedback, docs definitely are lacking so far, I at least need to write down some "Getting Started" instructions. Response to you specific issue goes in next comment.

    ReplyDelete
  4. Ok, so regarding uart_echo. First of all what you should know is that there's unfortunate pin swappery between different MCUs of MSP430 Value Line family. You can read more about it here: http://pfalcon-oe.blogspot.com/2012/07/ti-launchpad-14-vs-15-pin-swappery.html . Summing up, "TI Software UART" and "MSP430 Hardware UART" have flipped RXD/TXD signals. As I usually test with both UARTs, one day I got bored to switch jumpers, and decided that PTL examples will default Software UART on the same pins as Hardware one. Caveat: this works for Launchpad 1.5 only, v1.4 supports only "Software" anyway. So, please check you version, and then put RXD/TXD jumpers to be perpedicular to other 3. That should resolve it. Port speed should be 9600 (max supported by Launchpad). Yes, you should initiate transfer by sending any character first, because Launchpad's USB-UART implementation is buggy and leads to buffer overflow in Linux with unattended sending from Launchpad. Btw, currently msp430 in PTL is default to Hardware UART, see hw_config_msp430.hpp for how to change that.

    Let me know if that helped. Also, you may be interested to scheme thru previous posts tagged msp430: http://pfalcon-oe.blogspot.com/search/label/msp430

    ReplyDelete
  5. Btw, I usually use picocom to communicate with Launchpad:

    picocom -b 9600 /dev/ttyACM0

    Of course, any other serial term will work. Configuring serial device with stty and then using echo/cat/hexdump works too of course, but a little bit trickier ;-).

    ReplyDelete
  6. Changed the jumpers and it worked right away. Thanks a lot! The code is quite well written, it was very easy to get started just by reading the examples and the headers. I feel that I'm already doing quite a lot only after a few days.

    ReplyDelete