Goal
I want to render something valid (a button, bar, square, etc) on my display using LVGL so I can begin development.
Tools
- STM32 Nucleo-F401RE
- NHD-C12864A1Z display (ST7565 driver)
Bringup Process
- Copied over driver code and lv_drv_conf template (modified for my system) from https://github.com/lvgl/lv_drivers/blob/master/display/ST7565.c
- Copied lv_port_disp.c/h and modified as follows:
...
#ifndef MY_DISP_HOR_RES
#define MY_DISP_HOR_RES 128
#endif
#ifndef MY_DISP_VER_RES
#define MY_DISP_VER_RES 64
#endif
...
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
static lv_disp_draw_buf_t disp_buf;
static uint8_t gbuf[ST7565_HOR_RES * ST7565_VER_RES / 8]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&disp_buf, gbuf, NULL, ST7565_HOR_RES * ST7565_VER_RES / 8); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
/*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
/*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
disp_drv.rounder_cb = my_rounder_cb;
disp_drv.set_px_cb = my_set_px_cb;
/*Set a display buffer*/
disp_drv.draw_buf = &disp_buf;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
static void disp_init(void)
{
st7565_init();
}
...
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
st7565_flush(area->x1, area->x2, area->y1, area->y2, color_p);
lv_disp_flush_ready(disp_drv);
}
}
void my_rounder_cb(lv_disp_drv_t * disp_drv, lv_area_t * area)
{
/* Update the areas as needed. Can be only larger.
* For example to always have lines 8 px height:*/
// area->x1 = area->x1 & ~(0x7);
// area->x2 = area->x2 | (0x7);
area->y1 = area->y1 & 0x07;
area->y2 = (area->y2 & 0x07) + 8;
}
void my_set_px_cb(lv_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)
{
/* Write to the buffer as required for the display.
* Write only 1-bit for monochrome displays mapped vertically:*/
buf += buf_w * (y >> 3) + x;
if(lv_color_brightness(color) > 128) (*buf) |= (1 << (y % 8));
else (*buf) &= ~(1 << (y % 8));
}
- Followed LittlevGL on a Monochrome OLED | LVGL’s Blog and Porting LittlevGL for a monochrome display | by Mattia Maldini | Medium for above functions, along with setting up lv_conf.h. I believe this is the only code I modified/added in the lv_conf.h file:
#define LV_HOR_RES_MAX (128)
#define LV_VER_RES_MAX (64)
/*Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888)*/
#define LV_COLOR_DEPTH 1
#define LV_COLOR_CHROMA_KEY lv_color_hex(0x00ff00)
#define LV_ANTIALIAS 0
#define LV_USE_ANIMATION 0
#define USE_LV_GPU 0
- Added this to main:
lv_init();
lv_port_disp_init();
lv_obj_t * btn = lv_btn_create(lv_scr_act()); /*Add a button the current screen*/
lv_obj_set_pos(btn, 10, 10); /*Set its position*/
lv_obj_set_size(btn, 40, 20); /*Set its size*/
...
while (1)
{
lv_task_handler();
lv_tick_inc(1);
HAL_Delay(1);
}
Problem
My display (tested and working properly using vendor-provided sample code) shows noise/random pixels, but the same pixels every time. I believe I’ve tracked down the problem. The flush function from this driver code that I copied breaks out if y2 < 0. Once I removed the conditional the display started receiving noise, otherwise nothing is sent to the display with the code I currently have. y2 is -1 by the time it reaches this function due to this code block in get_max_row:
if(h_tmp <= 0) {
LV_LOG_WARN("Can't set draw_buf height using the round function. (Wrong round_cb or to "
"small draw_buf)");
return 0;
}
Increasing the buffer size (for both the gbuf buffer in my code and the size_in_px_cnt input parameter when initializing lv_disp_draw_buf) from (ST7565_HOR_RES * ST7565_VER_RES / 8) to (ST7565_HOR_RES * ST7565_VER_RES) did not solve the issue.