1. Simplify LVGL source code
Except for the examples folder, src folder, lv_conf_template.h and lvgl.h files, all other files can be deleted.
lv_conf_template.h was changed to lv_conf.h.
2 Add files to the project
2.1 Create a new LVGL related folder
2.2 Adding groups in MDK
Create the following group in MDK
LVGL/exanples/porting
LVGL/src/core
LVGL/src/draw
LVGL/src/extra
LVGL/src/font
LVGL/src/gpu
LVGL/src/hal
LVGL/src/misc
LVGL/src/widgets
Adding files from LVGL
3 Adaptation of LVGL related interfaces
3.1 LVGL time base adaptation
Use a timer to set a 1MS interrupt
Create a new tim_delay_ms.c and implement a TIM3 timing 1ms interrupt function in it.
#include "tim_delay_ms.h"
#include "lvgl.h"
/**
* [url=home.php?mod=space&uid=159083]@brief[/url] Timer3 initialization configuration.
*/
void tim3_init(uint16_t Period, uint16_t Prescaler)
{
TIM_TimeBaseInitType timInitStruct;
NVIC_InitType NVIC_InitStructure;
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_TIM3, ENABLE);
timInitStruct.Period = Period;
timInitStruct.Prescaler = Prescaler;
timInitStruct.ClkDiv = 0;
timInitStruct.CntMode = TIM_CNT_MODE_UP;
TIM_InitTimeBase(TIM3, &timInitStruct);
TIM_ConfigInt(TIM3, TIM_INT_UPDATE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
TIM_Enable(TIM3, ENABLE);
}
/**
* @brief TIM3 interrupt process function each 100us.Get the TIM2->CNT value, and Calculate the number of pulses by
the difference of two times. Then take the delta pules into the buffer of last channel and toggle to new channel.
*/
void TIM3_IRQHandler(void)
{
if (TIM_GetIntStatus(TIM3, TIM_INT_UPDATE) != RESET)
{
TIM_ClrIntPendingBit(TIM3, TIM_INT_UPDATE);
lv_tick_inc(1); /* lvgl 的 1ms 心跳 */
}
}
Timing header file
#ifndef __TIM_DELAY_H__
#define __TIM_DELAY_H__
#include "n32a455.h"
void tim3_init(uint16_t Period, uint16_t Prescaler);
#endif
3.2 Display driver configuration
3.2.1 To modify two macros, set the resolution
#define MY_DISP_HOR_RES 320
#define MY_DISP_VER_RES 480
3.2.2 Add LCD initialization function
static void disp_init(void)
{
LCD_Init();
}
3..2.3 Adaptation management function
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *color_p)*/
GUI_DrawPoint(x, y,color_p->full);
color_p++;
}
}
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
Here is the GUI_DrawPoint parameter:
void GUI_DrawPoint(unsigned short x,unsigned short y,unsigned short color)
color_p is a structure pointer
typedef union {
struct {
#if LV_COLOR_16_SWAP == 0
uint16_t blue : 5;
uint16_t green : 6;
uint16_t red : 5;
#else
uint16_t green_h : 3;
uint16_t red : 5;
uint16_t blue : 5;
uint16_t green_l : 3;
#endif
} ch;
uint16_t full;
} lv_color16_t;
lv_color16_t is a union, so you can directly fill color_p->full
/**
* [url=home.php?mod=space&uid=1307177]@File[/url] lv_port_disp_templ.c
*
*/
/*Copy this file as "lv_port_disp.c" and set this value to "1" to enable content*/
#if 1
/*********************
* INCLUDES
*********************/
#include "lv_port_disp_template.h"
#include <stdbool.h>
#include <stdio.h>
/*********************
* DEFINES
*********************/
#ifndef MY_DISP_HOR_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen width, default value 320 is used for now.
#define MY_DISP_HOR_RES 320
#endif
#ifndef MY_DISP_VER_RES
#warning Please define or replace the macro MY_DISP_HOR_RES with the actual screen height, default value 240 is used for now.
#define MY_DISP_VER_RES 480
#endif
/**********************
* TYPEDEFS
**********************/
/**********************
* STATIC PROTOTYPES
**********************/
static void disp_init(void);
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p);
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
// const lv_area_t * fill_area, lv_color_t color);
/**********************
* STATIC VARIABLES
**********************/
/**********************
* MACROS
**********************/
/**********************
* GLOBAL FUNCTIONS
**********************/
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
// disp_init();
LCD_Init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
static lv_disp_draw_buf_t draw_buf_dsc_1;
static lv_color_t buf_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_2[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_2, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/
// static lv_disp_draw_buf_t draw_buf_dsc_3;
// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
// static lv_color_t buf_3_2[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*Another screen sized buffer*/
// lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2,
// MY_DISP_VER_RES * LV_VER_RES_MAX); /*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;
/*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1;
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
/**********************
* STATIC FUNCTIONS
**********************/
/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{
/*You code here*/
LCD_Init();
}
volatile bool disp_flush_enabled = true;
/* Enable updating the screen (the flushing process) when disp_flush() is called by LVGL
*/
void disp_enable_update(void)
{
disp_flush_enabled = true;
}
/* Disable updating the screen (the flushing process) when disp_flush() is called by LVGL
*/
void disp_disable_update(void)
{
disp_flush_enabled = false;
}
/*Flush the content of the internal buffer the specific area on the display
*You can use DMA or any hardware acceleration to do this operation in the background but
*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *color_p)*/
GUI_DrawPoint(x, y,color_p->full);
color_p++;
}
}
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
/*OPTIONAL: GPU INTERFACE*/
/*If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color*/
//static void gpu_fill(lv_disp_drv_t * disp_drv, lv_color_t * dest_buf, lv_coord_t dest_width,
// const lv_area_t * fill_area, lv_color_t color)
//{
// /*It's an example code which should be done by your GPU*/
// int32_t x, y;
// dest_buf += dest_width * fill_area->y1; /*Go to the first line*/
//
// for(y = fill_area->y1; y <= fill_area->y2; y++) {
// for(x = fill_area->x1; x <= fill_area->x2; x++) {
// dest_buf[x] = color;
// }
// dest_buf+=dest_width; /*Go to the next line*/
// }
//}
#else /*Enable this file at the top*/
/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
4.main file
Initialization requires calling lv_init(); and in while, calling lv_timer_handler(), the LVGL timer.
/**
* @brief Main program
*/
int main(void)
{
/* System clocks configuration ---------------------------------------------*/
RCC_Configuration();
uart_print_init();
TIM_Configuration();
lv_init();
lv_port_disp_init();
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_label_set_text(label,"Hello Alientek!!!");
lv_obj_center(label);
while (1)
{
lv_timer_handler(); /* LVGL计时器 */
}
}
5 Download
The main frequency is 144, and the SPI2/SPI3 interface is only 18Mbps
Some hot melt glue
My speed reading is so slow, I am optimizing it.