Support all the things

I started out libkakapo as a basic set of drivers for one specific board, with the intention that there would be little abstraction of the real hardware configuration. The point behind doing this is to allow users to step away from a heavily-abstracted framework like Arduino into something which exposes a bit more of how things work.

The drivers in libkakapo are intended to be boilerplate, rather than abstracting. But the early implementation of it was too focused on one specific board with one specific MCU. This resulted in using a simple integer for what instance of a specific digital interface we wanted (eg, the “first” USART was 0.. whatever “first” meant).

I wasn’t very happy with this, esp given “first” was unclear, as was second and so forth. It made no sense when I was not trying to hide many other details to hide the hardware names, but a solution to make it friendly while fast wasn’t forth coming quickly.

The fast thing is important. If you look at many abstracted interfaces, they boil down to mapping between a friendly name or identifier and the actual resource. digitalWrite() in Arduino hides the real port and pin names, so it needs mappings to deal with this.

If a mapping is inefficient, that’s fine in setup but a real problem on regularly called code, especially in interrupts. Mapping from a number to a port is easy, but mapping a port to a number is harder. It seemed to boil down to an expensive series of if statements or an excessive number of defines or something.

I had kicked the idea of fixing that to touch, and got on with other drivers. But recently during beta testing with Nicegear, it’s become a bit more obvious the simple integer doesn’t work. I’ve built a beta board for them with a different MCU from the expected production run, using a 128A4U instead of a 64D4. The board physically supports it, so it was an easy thing to do.

But now libkakapo is stuck, do I fork it for diferent MCUs or support many, and then there’s this problem with that simple integer.

In the end, the best choice was to go back to what decisions I was making about abstraction. Exposing the hardware names was the right thing, I just needed a way to map it efficiently in both directions.

The solution was to use an enum type which listed all the hardware names (actually a similar name, so it wouldn’t overlap with avr-libc defines), and use this as an index into an array. The whole explanation is in this github ticket.

The upshot is we get to use hardware names, and the ISRs which depend on it can select the right port handler immediately, instead of a long series of if statements. Now it looks a lot better and an annoying bit of abstraction is removed. And it’s much easier to support almost any XMEGA MCU now.

Leave a Reply