Porting LittlevGL for a monochrome display

Leveraging LvGL’s capabilities on a low resource project

Microchip’s attempt at GUI support. Needless to say, it’s barely functional.

Monochrome Configuration

/* Maximal horizontal and vertical resolution to support by the library.*/
#define LV_HOR_RES_MAX (240)
#define LV_VER_RES_MAX (128)
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB233
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 1
/* Enable anti-aliasing (lines, and radiuses will be smoothed) */
#define LV_ANTIALIAS 0
/*1: Enable the Animations */
#define LV_USE_ANIMATION 0
/* Enable GPU optimization */
#define USE_LV_GPU 0

Library Initialization

    static uint8_t gbuf[8*240];
static lv_disp_buf_t disp_buf;

lv_disp_buf_init(&disp_buf, gbuf, NULL, 8*240);

lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
disp_drv.buffer = &disp_buf; /*Set an initialized buffer*/
disp_drv.flush_cb = my_flush_cb; /*Callback*/
disp_drv.set_px_cb = my_set_px_cb; /*Callback*/
disp_drv.rounder_cb = my_rounder; /*Callback*/
lv_disp_t * disp;
disp = lv_disp_drv_register(&disp_drv); /*Register the driver and save the created display objects*/
/*Input device*/
lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = my_input_read;
/*Register the driver in LittlevGL and save the created input device object*/
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);
In this example pixels are grouped by vertical bytes; each “segment” draws an 8-bit column.
bool my_input_read(lv_indev_drv_t * drv, lv_indev_data_t*data)
{
data->point.x = Touch_Coord[0];
data->point.y = Touch_Coord[1];
data->state = f_touch_detected ? LV_INDEV_STATE_PR : LV_INDEV_STATE_REL;
return false; /*No buffering now so no more data read*/
}

Runtime

while (1) {
lv_task_handler();
lv_tick_inc(1);
delay_ms(1);
}

Porting LvGL

flush_cb

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
int row = area->y1, col;
unsigned int address = (unsigned int)row * HW_COLUMNS + area->x1/8;
uint8_t *buffer;
int linelen = (area->x2 - area->x1)/8;

buffer = (uint8_t*) color_p;

for (row = area->y1; row <= area->y2; row++) {
flush_one_row(address, buffer, linelen);
buffer += linelen+1;
address += HW_COLUMNS;
}

lv_disp_flush_ready(disp_drv);
}

rounder_cb

void my_rounder(struct _disp_drv_t * disp_drv, lv_area_t *a)
{
a->x1 = a->x1 & ~(0x7);
a->x2 = a->x2 | (0x7);
}

set_px_cb

void my_set_px_cb(struct _disp_drv_t * disp_drv, uint8_t * buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
{
buf += buf_w/8 * y;
buf += x/8;
if(lv_color_brightness(color) > 128) {(*buf) |= (1 << (7 - x % 8));}
else {(*buf) &= ~(1 << (7 - x % 8));}
}

A small example

lv_obj_t *label2, *label1;
static void btn_event_cb(lv_obj_t * btn, lv_event_t event)
{
if(event == LV_EVENT_RELEASED) {
lv_label_set_text(label1, "RELEASED");
} else if (event == LV_EVENT_PRESSED) {
lv_label_set_text(label1, "CLICKED");
}
}
int main (void)
{
ConfigureOscillator();
InitializeSystem();

lv_init();

static lv_disp_buf_t disp_buf;
lv_disp_buf_init(&disp_buf, gbuf, NULL, 8*240);
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.buffer = &disp_buf;
disp_drv.flush_cb = my_flush_cb;
disp_drv.set_px_cb = my_set_px_cb;
disp_drv.rounder_cb = my_rounder;

lv_disp_t * disp;
disp = lv_disp_drv_register(&disp_drv);

lv_indev_drv_t indev_drv;
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb =my_input_read;
lv_indev_t * my_indev = lv_indev_drv_register(&indev_drv);

lv_obj_t * scr = lv_disp_get_scr_act(NULL);
lv_theme_t * th = lv_theme_mono_init(0, NULL);
/* Set the mono system theme */
lv_theme_set_current(th);

/*Create a Label on the currently active screen*/
label1 = lv_label_create(scr, NULL);
lv_label_set_text(label1, "");
lv_obj_set_pos(label1,30, 30);// position, position);
/*Create a button on the currently loaded screen*/
lv_obj_t * btn1 = lv_btn_create(scr, NULL);
lv_obj_set_event_cb(btn1, btn_event_cb); /*Set function to be called when the button is released*/
//lv_obj_align(btn1, label2, LV_ALIGN_OUT_BOTTOM_LEFT, 0, 20); /*Align below the label*/
lv_obj_set_pos(btn1, 30, 50);

/*Create a label on the button (the 'label' variable can be reused)*/
label2 = lv_label_create(btn1, NULL);
lv_label_set_text(label2, "Click me!");
lv_obj_set_size(btn1, 100, 20);
while (1) {
lv_task_handler();
lv_tick_inc(1);
delay_ms(1);
}
}

Additional Notes

Computer Science Master from Alma Mater Studiorum, Bologna; interested in a wide range of topics, from functional programming to embedded systems.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store