I have to write a low-level driver to manage a tft display for an embedded device. Device is powered with a PIC24, 64K of ram and about 128K of program memory.
My intent is to write a generic graphics library where i will put all primitive graphics functions and a driver to send command and data to display.
Graphics lib uses the driver to access the display, but the display tecnology can change so the driver must be easily reimplementabile.
What is best way to write reusable code in this kind of scenario?
----------------- ---------------
| | | |
| | | |
| GFX_LIB | =====> | DRIVER | ====> DISPLAY
| | | |
| | | |
----------------- -----/----\----
/ \
/ \
---------- ----------
| | | |
| TYPE A | | TYPE B |
| | | |
---------- ----------
More details
This is a piece of code of my gfx lib
#include "Graphics.h"
void gfx_Init(uint16_t width, uint16_t height, uint8_t rotation){
gfx_displayWidth=width;
gfx_displayHeight=height;
gfx_rotation=rotation;
displayDriverInit();
}
void gfx_setPixel(uint16_t x, uint16_t y, uint32_t color){
displayDriverSendCommand(CHANGE_COORDINATE);
displayDriverSendData(x);
displayDriverSendData(y);
displayDriverSendCommand(SET_COLOR);
displayDriverSendData(color);
}
This is an hypothetical implementation of my graphics library.
Now, if i change driver, i'm really happy if i can reuse my gfx lib, and rewrite only display driver routines. What the best way to achieve this?
Thank you.
It depends a bit on what functionality your drivers will offer.
A minimal API could be declared like this:
typedef void* gfx;
typedef struct init_options {
int display_width, display_height;
//..etc..
};
gfx* gfx_init( init_options* opts );
void gfx_free( gfx* gfx );
// blit something onto sceeen
void gfx_blit( gfx* gfx, void* buffer, int buf_w, int buf_h, int trg_x, int trg_y, int trg_w, int trg_h );
// if you have double buffering (probably a good idea)
void gfx_swap( gfx* gfx );
Initialization, shutdown, mode-switching and blitting pixels onto screen may be all you need. Maybe you should also provide a fast SetPixel() routine. Everything else can be implemented in some kind of "GfxUtil" library, based on this.
Driver selection and implementation of this API may be handled by just linking the right library (where each library contains an actual driver, containing the actual code for blitting and so on).
EDIT
To have some dynamically replaceable code, your driver could be a struct with function pointers to actual implementation of driver specific code:
// common driver struct, used by gfx api
typedef struct driver {
void (*init)( driver *driver, init_options* );
void (*setpixel)( driver *driver, int x, int y, color color );
//...
};
// driver constructor
driver* create_foo_driver() {
driver* d = malloc( sizeof(driver) );
memset(d,0,sizeof(driver));
d->init = foo_init;
d->setpixel = foo_setpixel;
return d;
}
// some driver-specific code ...
void foo_setpixel( driver *driver, int x, int y, color color ) {
// actual implementation
}
// API code calling driver code
void gfx_setpixel( gfx* gfx, int x, int y, color color ) {
driver* driver = ((gfx_impl)gfx)->driver; // .. just an example
driver->setpixel( driver, x, y, color );
}