Here is a part of the code that is a good representation of the rest. Basically it creates four identical parent objects (“bootslots”) next to each other on a screen-sized background image. On these parent objects different child objects are created, here represented through a button with a label. There are a lot more styles and objects, but they are all created more or less in the same way.
#define DISP_WIDTH 1440
#define DISP_HEIGHT 900
#define NUM_PIXELS (DISP_WIDTH*DISP_HEIGHT)
/******* Big arrays in external RAM *******/
static lv_color_t __attribute__((__section__(".lvgldrawbuffsection1"))) drawBuffer1[NUM_PIXELS] = {0};
static lv_color_t __attribute__((__section__(".lvgldrawbuffsection2"))) drawBuffer2[NUM_PIXELS] = {0};
static uint8_t __attribute__((__section__(".bootBackgroundBuffer"))) u8_bootscreen_lvglBackground[NUM_PIXELS*3] = {0};
// Array containing the image the LVGL-objects are drawn to:
static uint16_t __attribute__((__section__(".displayimage2"))) pu16_noBootDevice_image[NUM_PIXELS];
/******** LVGL-background objects *******/
static lv_obj_t *parent_black_backGround;
static lv_obj_t *parent_animation_backGround;
static lv_obj_t *parent_sleepMode_backGround;
static lv_obj_t *parent_bootScreen_backGround;
static lv_obj_t *bootscreenBackgroundImage_obj;
/******** LVGL-styles *******/
static lv_style_t style_btn;
static lv_style_t style_buttonLabel_black;
static lv_style_t style_bootslotContent;
/******** LVGL-objects and variables for boot-slots *******/
typedef struct
{
lv_obj_t *bootslot_obj;
lv_obj_t *bootbutton_obj;
lv_obj_t *additionalInfo_obj;
lv_obj_t *greenSquare_obj;
lv_obj_t *gray_slotMask_obj;
lv_obj_t *slotNumber_obj;
} st_bootslotContent_t;
static st_bootslotContent_t st_bootslotContent1 = {0, 0, 0, 0, 0, 0};
static st_bootslotContent_t st_bootslotContent2 = {0, 0, 0, 0, 0, 0};
static st_bootslotContent_t st_bootslotContent3 = {0, 0, 0, 0, 0, 0};
static st_bootslotContent_t st_bootslotContent4 = {0, 0, 0, 0, 0, 0};
/******** dieplay-driver variables ********/
static lv_disp_drv_t disp_drv;
static lv_disp_draw_buf_t displayDrawBuffrer;
static lv_disp_t *disp;
/******* variables for flushing drawbuffers *******/
typedef enum
{
e_LVGL_UPDATE_BACKGROUNDSCREEN,
e_LVGL_UPDATE_OBJECT_BOOTSCREEN,
} e_displayObject_t;
typedef struct
{
lv_color_t* p_drawBuffer;
uint16_t width;
uint16_t height;
uint32_t x1_flush;
uint32_t x2_flush;
uint32_t y1_flush;
uint32_t y2_flush;
e_displayObject_t object_to_diplay;
} st_drawBuffer_t;
static st_drawBuffer_t activeDrawBuffer;
/******* background image for main screen *******/
static lv_img_dsc_t BB_Background_Image = {
.header.cf = LV_IMG_CF_RGB565A8,
.header.always_zero = 0,
.header.reserved = 0,
.header.w = DISP_WIDTH,
.header.h = DISP_HEIGHT,
.data_size = NUM_PIXELS*3,
.data = u8_bootscreen_lvglBackground,
};
typedef enum
{
e_BOOTSLOT1 = 0,
e_BOOTSLOT2,
e_BOOTSLOT3,
e_BOOTSLOT4
} e_bootSlot_t;
void lvgl_init_graphic_lib (void)
{
lv_init();
// drawBuffer1 and drawBuffer2 are both screen-sized (1440 x 900)
lv_disp_draw_buf_init(&displayDrawBuffrer, drawBuffer1, drawBuffer2, NUM_PIXELS);
lv_disp_drv_init(&disp_drv);
disp_drv.draw_buf = &displayDrawBuffrer;
disp_drv.flush_cb = flush_cb;
disp_drv.hor_res = DISP_WIDTH;
disp_drv.ver_res = DISP_HEIGHT;
disp = lv_disp_drv_register(&disp_drv);
// All styles initialized:
lvgl_init_styles();
// The background-image for one of the screens is formatted for LVGL:
lvgl_create_bootscreen_backGround();
// Creation of backgrounds of different screens:
lvgl_set_parent_backGrounds();
lvgl_create_bootslotContent_objects();
}
void lvgl_init_styles(void)
{
/* Initialize styles for LVGL-created bootbutton */
lv_style_init(&style_btn);
lv_style_set_radius(&style_btn, 0);
lv_style_set_bg_opa(&style_btn, LV_OPA_COVER);
lv_style_set_bg_color(&style_btn, lv_color_black());
lv_style_set_pad_all(&style_btn, 0);
lv_style_set_outline_pad(&style_btn, 0);
lv_style_set_outline_width(&style_btn, 0);
lv_style_set_border_color(&style_btn, COLOR_CLASSIFICATION_OPEN);
lv_style_set_border_opa(&style_btn, LV_OPA_COVER);
lv_style_set_border_width(&style_btn, 1);
lv_style_set_shadow_ofs_x(&style_btn, 0);
lv_style_set_shadow_ofs_y(&style_btn, 0);
lv_style_set_shadow_spread(&style_btn, 0);
lv_style_set_text_color(&style_btn, lv_color_white());
lv_style_init(&style_buttonLabel_black);
lv_style_set_text_font(&style_buttonLabel_black, &BB_Font_20);
lv_style_set_text_letter_space(&style_buttonLabel_black, 1);
lv_style_set_text_color(&style_buttonLabel_black, lv_color_make(204, 204, 204));
lv_style_set_bg_color(&style_buttonLabel_black, lv_color_black());
lv_style_set_bg_opa(&style_buttonLabel_black, LV_OPA_COVER);
lv_style_init(&style_bootslotContent);
lv_style_set_bg_opa(&style_bootslotContent, LV_OPA_TRANSP);
lv_style_set_pad_all(&style_bootslotContent, 0);
lv_style_set_border_width(&style_bootslotContent, 0);
lv_style_set_radius(&style_bootslotContent, 0);
lv_style_set_shadow_width(&style_bootslotContent, 0);
lv_style_set_outline_width(&style_bootslotContent, 0);
}
// Converts 16-Bit-image to 8-Bit with alpha channel (both RGB565)
void lvgl_create_bootscreen_backGround (void)
{
uint32_t j = 0;
// Turn boot-background to uint8 to be compatible with LVGL
for (int i = 0; i < NUM_PIXELS*2; i=i+2)
{
u8_bootscreen_lvglBackground[i] = pu16_noBootDevice_image[j] & 0xFF; // Copy Low Byte
u8_bootscreen_lvglBackground[i+1] = pu16_noBootDevice_image[j] >> 8; // Copy High Byte
j++;
}
// Copy alpha channel to array
memset(&u8_bootscreen_lvglBackground[NUM_PIXELS*2], 0xFF, NUM_PIXELS);
}
void lvgl_set_parent_backGrounds (void)
{
parent_animation_backGround = lv_obj_create(NULL);
lv_obj_set_style_bg_color(parent_animation_backGround, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(parent_animation_backGround, LV_OPA_COVER, LV_PART_MAIN);
parent_black_backGround = lv_obj_create(NULL);
lv_obj_set_style_bg_color(parent_black_backGround, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(parent_black_backGround, LV_OPA_COVER, LV_PART_MAIN);
parent_sleepMode_backGround = lv_obj_create(NULL);
lv_obj_set_style_bg_color(parent_sleepMode_backGround, lv_color_black(), LV_PART_MAIN);
lv_obj_set_style_bg_opa(parent_sleepMode_backGround, LV_OPA_COVER, LV_PART_MAIN);
// main background with background image:
parent_bootScreen_backGround = lv_obj_create(NULL);
bootscreenBackgroundImage_obj = lv_img_create(parent_bootScreen_backGround);
lv_img_set_src(bootscreenBackgroundImage_obj, &BB_Background_Image);
}
void BBDisplay_load_backGroundScreen_Bootscreen (void)
{
activeDrawBuffer.object_to_diplay = e_LVGL_UPDATE_BACKGROUNDSCREEN;
lv_scr_load(parent_bootScreen_backGround);
}
// 4 identical objects created on different positions of the main screen
void lvgl_create_bootslotContent_objects (void)
{
st_bootslotContent_t *pst_bootslotContent;
int16_t bootslotPosition_x = 0;
int16_t bootslotPosition_y = 0;
for (int i = 0; i < 4; i++)
{
switch(i)
{
case (e_BOOTSLOT1):
pst_bootslotContent = &st_bootslotContent1;
bootslotPosition_x = 55;
bootslotPosition_y = 378;
break;
case (e_BOOTSLOT2):
pst_bootslotContent = &st_bootslotContent2;
bootslotPosition_x = 389;
bootslotPosition_y = 378;
break;
case (e_BOOTSLOT3):
pst_bootslotContent = &st_bootslotContent3;
bootslotPosition_x = 724;
bootslotPosition_y = 378;
break;
case (e_BOOTSLOT4):
pst_bootslotContent = &st_bootslotContent4;
bootslotPosition_x = 1058;
bootslotPosition_y = 378;
break;
default:
break;
}
pst_bootslotContent->bootslot_obj = lv_obj_create(bootscreenBackgroundImage_obj);
lv_obj_add_style(pst_bootslotContent->bootslot_obj, &style_bootslotContent, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(pst_bootslotContent->bootslot_obj, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_set_pos(pst_bootslotContent->bootslot_obj, bootslotPosition_x, bootslotPosition_y); // absolute position on screen
lv_obj_set_size(pst_bootslotContent->bootslot_obj, 330, 391);
lv_obj_set_scrollbar_mode(pst_bootslotContent->bootslot_obj, LV_SCROLLBAR_MODE_OFF);
pst_bootslotContent->bootbutton_obj = lv_obj_create(pst_bootslotContent->bootslot_obj);
lv_obj_add_style(pst_bootslotContent->bootbutton_obj, &style_bootslotContent, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(pst_bootslotContent->bootbutton_obj, LV_OPA_TRANSP, LV_PART_MAIN);
lv_obj_align(pst_bootslotContent->bootbutton_obj, LV_ALIGN_CENTER, 0, -90); // position relative to bootslot_obj
lv_obj_set_size(pst_bootslotContent->bootbutton_obj, BOOTBUTTON_WIDTH, BOOTBUTTON_HEIGHT);
lv_obj_set_scrollbar_mode(pst_bootslotContent->bootbutton_obj, LV_SCROLLBAR_MODE_OFF);
/* .....
other objects created in the same manner as children of pst_bootslotContent->bootslot_obj
*/
}
}
uint32_t lvgl_check_bootslotNumber (uint8_t num_bootSlot)
{
uint32_t bootslotAddress = 0;
switch (num_bootSlot)
{
case (e_BOOTSLOT1):
bootslotAddress = (uint32_t)&st_bootslotContent1;
break;
case (e_BOOTSLOT2):
bootslotAddress = (uint32_t)&st_bootslotContent2;
break;
case (e_BOOTSLOT3):
bootslotAddress = (uint32_t)&st_bootslotContent3;
break;
case (e_BOOTSLOT4):
bootslotAddress = (uint32_t)&st_bootslotContent4;
break;
}
return bootslotAddress;
}
void lvgl_show_bootButton (uint8_t num_bootSlot)
{
st_bootslotContent_t *pst_bootslotContent;
profile_t *pst_bootslotProfile;
char pc_buttonText[31] = {0};
activeDrawBuffer.object_to_diplay = e_LVGL_UPDATE_OBJECT_BOOTSCREEN;
// Check which of the 4 identical parent objects the child object is drawn to:
pst_bootslotContent = (st_bootslotContent_t *)lvgl_check_bootslotNumber(num_bootSlot);
switch (num_bootSlot)
{
case e_BOOTSLOT1:
pst_bootslotProfile = &st_bootslotProfile_slot1;
lv_style_set_border_color(&style_btn, lv_color_make(0x72, 0x7F, 0x54));
strcpy(pc_buttonText, "TEXT 1");
break;
case e_BOOTSLOT2:
pst_bootslotProfile = &st_bootslotProfile_slot2;
lv_style_set_border_color(&style_btn, lv_color_white());
strcpy(pc_buttonText, "TEXT 2");
break;
case e_BOOTSLOT3:
pst_bootslotProfile = &st_bootslotProfile_slot3;
lv_style_set_border_color(&style_btn, lv_color_make(0x42, 0x70, 0x96));
strcpy(pc_buttonText, "TEXT 3");
break;
case e_BOOTSLOT4:
pst_bootslotProfile = &st_bootslotProfile_slot4;
lv_style_set_border_color(&style_btn, lv_color_make(0xCC, 0x6F, 0x00));
strcpy(pc_buttonText, "TEXT 4");
break;
default:
__NOP();
break;
}
uint32_t num_children = lv_obj_get_child_cnt(pst_bootslotContent->bootbutton_obj);
if (num_children == 0) // First time drawing object
{
lv_obj_t* btn = lv_obj_create(pst_bootslotContent->bootbutton_obj);
lv_obj_set_pos(btn, 0, 0);
lv_obj_set_size(btn, 305, 46);
lv_obj_add_style(btn, &style_btn, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_t *bootButton_label = lv_label_create(btn);
lv_label_set_text(bootButton_label, NULL);
lv_label_set_text(bootButton_label, pc_buttonText);
lv_obj_center(bootButton_label);
lv_obj_add_style(bootButton_label, &style_buttonLabel_black, LV_PART_MAIN | LV_STATE_DEFAULT);
lv_obj_set_style_bg_opa(bootButton_label, LV_OPA_TRANSP, LV_PART_MAIN);
}
else // When object already has been drawn -> only hiding and showing
{
lv_obj_t *parent_button = NULL;
lv_obj_t *button_label = NULL;
parent_button = lv_obj_get_child(pst_bootslotContent->bootbutton_obj, 0);
if (parent_button != NULL)
{
button_label = lv_obj_get_child(parent_button, 0);
}
// Refresh button-label text
if (button_label != NULL)
{
lv_label_set_text(button_label, NULL);
lv_label_set_text(button_label, pc_buttonText);
}
// Refresh button border color
if (parent_button != NULL)
{
lv_obj_refresh_style(parent_button, LV_PART_MAIN, LV_STYLE_PROP_ANY);
}
if (lv_obj_has_flag_any (pst_bootslotContent->bootbutton_obj, LV_OBJ_FLAG_HIDDEN) == true)
{
lv_obj_clear_flag (pst_bootslotContent->bootbutton_obj, LV_OBJ_FLAG_HIDDEN);
}
}
}
void lvgl_remove_bootButton (uint8_t num_bootSlot)
{
st_bootslotContent_t *pst_bootslotContent;
activeDrawBuffer.object_to_diplay = e_LVGL_UPDATE_OBJECT_BOOTSCREEN;
pst_bootslotContent = (st_bootslotContent_t *)lvgl_check_bootslotNumber(num_bootSlot);
if (lv_obj_has_flag_any (pst_bootslotContent->bootbutton_obj, LV_OBJ_FLAG_HIDDEN) == false)
{
lv_obj_add_flag (pst_bootslotContent->bootbutton_obj, LV_OBJ_FLAG_HIDDEN);
}
}
void flush_cb(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *buf)
{
activeDrawBuffer.x1_flush = area->x1;
activeDrawBuffer.y1_flush = area->y1;
activeDrawBuffer.x2_flush = area->x2;
activeDrawBuffer.y2_flush = area->y2;
activeDrawBuffer.p_drawBuffer = buf;
lvgl_flushbuffer_to_display();
lv_disp_flush_ready(disp);
}
void lvgl_flushbuffer_to_display (void)
{
switch (activeDrawBuffer.object_to_diplay)
{
case e_LVGL_UPDATE_BACKGROUNDSCREEN:
__NOP(); // Load the background without drawing it to the display
break;
case e_LVGL_UPDATE_OBJECT_BOOTSCREEN:
lvgl_draw_object_to_display ((uint16_t *)pu16_noBootDevice_image);
break;
default:
__NOP();
break;
}
}
void lvgl_draw_object_to_display (uint16_t *image_to_modify)
{
int32_t x, y;
for(y = activeDrawBuffer.y1_flush; y <= activeDrawBuffer.y2_flush; y++)
{
uint32_t row = y * DISP_WIDTH;
for(x = activeDrawBuffer.x1_flush; x <= activeDrawBuffer.x2_flush; x++)
{
image_to_modify[row+x] = activeDrawBuffer.p_drawBuffer->full;
activeDrawBuffer.p_drawBuffer++;
}
}
}
// test program:
void main (void)
{
lvgl_init_graphic_lib();
// LVGL-timer handler called eery 5 ms (LVGL-tick-handler every 1 ms in the SysTick-callback)
start_LVGL_RTOS_task();
Timer_Set (TIMER_LVGL_HANDLER, 5, e_TASK_LVGL, true);
BBDisplay_load_backGroundScreen_Bootscreen();
// Bootslots drawn fo the 1st time -> here memory allocation error occurs sometimes
lvgl_show_bootButton(e_BOOTSLOT1);
lvgl_show_bootButton(e_BOOTSLOT2);
lvgl_show_bootButton(e_BOOTSLOT3);
lvgl_show_bootButton(e_BOOTSLOT4);
// DELAY
lvgl_remove_bootButton(e_BOOTSLOT1);
lvgl_remove_bootButton(e_BOOTSLOT2);
lvgl_remove_bootButton(e_BOOTSLOT3);
lvgl_remove_bootButton(e_BOOTSLOT4);
// DELAY
// Now only the OBJ_HIDDEN-flags are deleted:
lvgl_show_bootButton(e_BOOTSLOT1);
lvgl_show_bootButton(e_BOOTSLOT2);
lvgl_show_bootButton(e_BOOTSLOT3);
lvgl_show_bootButton(e_BOOTSLOT4);
}
// Endless loop for the LVGL-RTOS-thread (lv_timer_handler called every 5 ms)
static void task_lvgl_run()
{
BaseType_t xStatus;
lvgl_event_data_t lvgl_event_data;
// Delete all events received during init
xQueueReset(xQueue_lvgl_event);
/* Create a mutex type semaphore. */
lvgl_timerHandler_Semaphore = xSemaphoreCreateMutex();
while (1)
{
xStatus = xQueueReceive(xQueue_lvgl_event, &lvgl_event_data , pdMS_TO_TICKS(TASK_LVGL_QUEUE_TICKS_TO_WAIT));
if (xStatus == pdTRUE)
{
pv_EventData = lvgl_event_data.pv_Data;
switch (lvgl_event_data.e_Event)
{
case EV_TIMER_LVGL_HANDLER_EXPIRED:
if (xSemaphoreTake(lvgl_timerHandler_Semaphore, (TickType_t) 5) == pdTRUE)
{
lv_timer_handler();
xSemaphoreGive(lvgl_timerHandler_Semaphore);
}
break;
default:
LOG(e_WARN, "Unknown event %i", (int) lvgl_event_data.e_Event);
break;
}
}
}
}