Electronics > Microcontrollers

Creating a portable and small I/O lib.

<< < (6/6)

Please stop about those AVR's. I know that architecture is being stretched beyond it's limit.
I do find it relevant here as an example of optimizations that can not be relied upon, and I partly agree with the remark of cv007 of "use a faster uC". That is the most important reason I went looking for another uC family. But his use case is also very different. He writes separate projects, while I have libraries that I want to be compatible among different uC architectures.

And that brings it back to the remark from SiliconWizard: "Abstract at the functional level, rather then the MCU level". For a simple I/O pin both abstractions are pretty much the same, but when it becomes more complicated (communication stack with buffers, ISR's, DMA, etc) there are just too many differences between microcontrollers to be able to use the same library code on different uC's. ISR's work differently, peripherals need to be configured differently. Some may use DMA, while others don't have DMA etc.

One way used here is: a) Use only a generic subset of device capability and b), Divide the driver code into hardware specific and generic function layers. Things like io ports, timers, uarts and perhaps more, can be abstracted in that way.  For example, the simplest case of io ports, use a lookup table, perhaps an array of structures, containing the addresses of the various registers, and their initialisation values, input, or output etc. Then force access indirectly with generic r/w drivers through the table. This works well if, for example, the port register set is not contiguous in memory, as was the case for a Renessas cpu used on one project. Write a generic set of read, write, byte or bit, whatever functions to access the ports. To change the port or even cpu, just edit / modify the table to suit. Define a table for each port, indexed with an enum port id. then perhaps another with all the setup for all the ports. Of course, there is a speed penalty, but current embedded devices have more than enough throughput so that it can be ignored most of the time. You can always write a driver to direct access the port for that special case. Same can be done for timers and uarts, if the drivers are written right. Gets a bit more complicated where interrupts are involved, but  the isr code for that can be isolated into a separate cpu specific module. That approach has saved a load of effort here in the past and makes proven upper layer code much more reusable. Partitioning and layering are key and things like classic os design can be a great example on how to do it right, even for the smallest of projects...


--- Quote ---Please stop about those AVR's.
--- End quote ---
But ... the cost of even poor abstraction goes down on most of the newer CPUs.
digitalWrite() in Arduino "sucks" because the trivial optimized case is a single instruction, and the abstracted case is something like 30 instructions.
But on most of the ARMs, MIPS, or RISCV, it'll take 3 or 4 instructions for the trivial case, and maybe a dozen for a more abstracted version...
4x slower for a useful abstraction is a lot more plateable than 30x slower...


[0] Message Index

[*] Previous page

There was an error while thanking
Go to full version
Powered by SMFPacks Advanced Attachments Uploader Mod