Gendriver - motivation
The other day I hooked up my brand new SSD1306 oled to a PI board. I
thought it'd be easy to test it and develop a prototype of my software
before I move the whole thing on to a 8 bit MCU. I thought I'd find a simple
lib that talks to the display, drawing pixels, not trying to do anything
fancy.
How wrong I was!
For PI, there are examples written in C or in C++, that depend on driver libs
for i2c. There are python implementations requiring a hundred megabyte of
python and 3rd party i2c libs installed. For 8 bit MCUs the situation is
similar: exmaples working only in arduino environemnt, or huge display
framework projects where my specific device is only a tiny submodule.
The common aspect of most examples I've found:
- they are unreasonably large
- they are coupled with low level drivers (e.g. i2c access)
- they are coupled with high level code (e.g. font renderer)
- ... or both, because they are part of some big framework
The motivation behind gendriver is to provide a set of middle layer drivers
wihtout the above shortcomings:
- provide only the device-specific code, let the caller deal with:
- low level (e.g. how to send bytes over i2c), through the simplest API possible
- high level (e.g. what do we draw on a display)
- avoid growing yet another framework:
- each driver is a single, c99 .h file the user needs to #include
- there's no code reuse thus no internal dependencies within gendriver; single file literally means one single file to deal with
- there's no external dependency, no arduino or the latest greatest PI gpio lib pushed onto your projects for using a middle layer driver
- drivers are instantiated, which means a set of (typically static inline) functions are generated for a specific device:
- allows different drivers work together
- allows multiple instances of the same driver (for multiple physical devices of the same kind) work easily
- cheap on MCU: all instantionation and configuration happening in compile time
- cheap on MCU: any optimizing C compiler will automatically throw out unused parts of the code (e.g. unused options)
- allows the same middle layer driver use different low level drivers for communicating over different ports within the same code
The major drawbacks of this model are all related to the fact each project
needs to #include and configure a local instance for the very specific
purpose of the project:
- it is harder to provide turn-key ready, fancy examples
- it is impossible to provide a link-time reusable generic driver lib (e.g. a .so or .a), since reuse happens on compile time with #include