唠几句,记录下移植笔记
新项目用到LVGL,也是首次接触GUI库,所以Emmmm,,,学呗!!!
之前都是直接在LCD屏上画点、画线、画圆、画个矩形、画个多边形、显示个字符串、显示张图片而已,没有用过GUI库,在网上找了点学习资料,然后把LVGL库的用法整体过了一遍。
寻思着搞个屏练习一下,然后趁着周末把SPI屏(IFI9488/ 480*320)驱动起来,甚至还优化了绘图部分的程序,下面是部分优化记录
方式一:移植淘宝卖家提供的Demo
串口日志:刷整屏用时3s
system Init complate!
RUNNING time: 3032
方式二:优化1(库函数)
串口日志:刷整屏用时1.2s
system Init complate!
RUNNING time: 1221
方式三:优化2(寄存器:spi写)
串口日志:刷整屏用时0.33s
system Init complate!
RUNNING time: 333
最终版
串口日志:刷整屏用时0.24s
system Init complate!
RUNNING time: 239
还有优化的空间,据说用ESP32的SPI能达到80M,那肯定很爽,但是不折腾了,重点还是LVGL部分。
把画点部分代码贴出来吧,这里没有上DMA哦
#define RGB_R(n) (n>>8)&0xF8
#define RGB_G(n) (n>>3)&0xFC
#define RGB_B(n) n<<3
#define LCD_PUSH_GRAM(n) \
do{ \
*((__IO uint8_t*)&hspi2.Instance->DR) = RGB_R(n); \
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)); \
*((__IO uint8_t*)&hspi2.Instance->DR) = RGB_G(n); \
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)); \
*((__IO uint8_t*)&hspi2.Instance->DR) = RGB_B(n); \
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE)); \
}while(0U)
//在指定位置写入一个指定像素的点数据
static inline void _LCD_send_pixel(uint16_t x, uint16_t y, uint16_t color){
//制定位置
LCD_CS_L;
LCD_DC_L;
*((__IO uint8_t*)&hspi2.Instance->DR) = 0x2A; //setx-cmd
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
LCD_DC_H;
*((__IO uint8_t*)&hspi2.Instance->DR) = x>>8; //x-start
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = x&0x00FF;
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = x>>8; //x-end
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = x&0x00FF;
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
LCD_DC_L;
*((__IO uint8_t*)&hspi2.Instance->DR) = 0x2B; //sety-cmd
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
LCD_DC_H;
*((__IO uint8_t*)&hspi2.Instance->DR) = y>>8; //y-start
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = y&0x00FF;
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = y>>8; //y-end
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
*((__IO uint8_t*)&hspi2.Instance->DR) = y&0x00FF;
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
LCD_DC_L;
*((__IO uint8_t*)&hspi2.Instance->DR) = 0x2C; //start write gram
while(!__HAL_SPI_GET_FLAG(&hspi2, SPI_FLAG_TXE));
LCD_DC_H;
LCD_PUSH_GRAM(color);
LCD_CS_H;
}
void LCD_send_pixel(uint16_t x, uint16_t y, uint16_t color){
_LCD_send_pixel(x, y, color);
}
//清屏
void LCD_Clear(uint16_t Color){
uint32_t i,m;
LCD_SetWindows(0,0,lcddev.width-1,lcddev.height-1);
uint32_t tickstart = HAL_GetTick();
LCD_CS_L;
LCD_DC_H;
for(i=0;i
这种事情最好是找 LVGL官网 或 LVGL GitHub库,不过最好用的还是国内源 LVGL Gitee库
git checkout v7.8.0
这里用的V7.8.0版本,V8.1.0版本把握不住哈哈哈
重点关注这几个文件或文件夹即可
将 lvgl 库添加到 keil 中,记得添加路径哦
port 为接口文件,其中
lv_port_disp
/lv_port_indev
/lv_port_fs
分别对应 LCD 屏绘制,触摸、鼠标、编码器等输入设备,还有文件系统,这里只使能lv_port_disp
将画点函数,放到对应的接口函数中
可将LCD 初始化放到 lvgl 控件初始化函数中
在 main 函数中,初始化LVGL和 lvgl 的心跳程序
#define USE_LVGL 1
// main函数
int main(void){
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_DMA_Init();
MX_TIM6_Init();
MX_TIM7_Init();
MX_USART1_UART_Init();
MX_IWDG_Init();
MX_SPI1_Init();
MX_SPI2_Init();
MX_SPI3_Init();
#if USE_LVGL
lv_init();
lv_port_disp_init(); // 显示器初始化
// lv_port_indev_init(); // 输入设备初始化(如果没有实现就注释掉)
// lv_port_fs_init(); // 文件系统设备初始化(如果没有实现就注释掉)
slim_index(); //小控件测试
// slim_index2(); //仪表盘控件测试
#else
LCD_Init();
#endif
printf("system Init complate!\n");
while (1) {
#if USE_LVGL
lv_tick_inc(50);
lv_task_handler();
#else
for(uint16_t i=0;i<320;i++){
LCD_send_pixel(i, i, RED);
}
for(uint16_t i=0;i<320;i++){
LCD_send_pixel(i, 320-i, RED);
}
HAL_Delay(1000);
HAL_IWDG_Refresh(&hiwdg);
#endif
}
}
测试函数
lv_obj_t *bar1;
lv_obj_t *btn2;
lv_obj_t *label_bar1;
lv_obj_t *slider1;
lv_obj_t *led1;
static void slim_index(void){
lv_obj_t *scr = lv_scr_act(); //获取屏幕对象
//彩色标签
#if 1
lv_obj_t * label1 = lv_label_create(scr, NULL);
lv_label_set_recolor(label1, true);
lv_label_set_long_mode(label1, LV_LABEL_LONG_SROLL_CIRC); /*Circular scroll*/
lv_obj_set_width(label1, 120);
lv_label_set_text(label1, "#ff0000 Hello# #00ff00 world ! slim su.#");
lv_obj_align(label1, NULL, LV_ALIGN_IN_TOP_LEFT, 20, 5);
lv_label_set_anim_speed(label1, 80);
#endif
//圆弧
#if 1
static lv_style_t style_arc1_bg, style_arc1_indic;
lv_style_init(&style_arc1_bg);
lv_style_set_line_width(&style_arc1_bg, LV_STATE_DEFAULT, 10);
lv_style_set_line_color(&style_arc1_bg, LV_STATE_DEFAULT, LV_COLOR_MAKE(0, 255, 0));
lv_style_init(&style_arc1_indic);
lv_style_set_line_width(&style_arc1_indic, LV_STATE_DEFAULT, 10);
lv_style_set_line_color(&style_arc1_indic, LV_STATE_DEFAULT, LV_COLOR_MAKE(255, 0, 0));
lv_obj_t *arc1 = lv_arc_create(scr, NULL);
lv_obj_set_size(arc1, 100, 100);
lv_obj_set_pos(arc1, 5, 50);
lv_arc_set_bg_angles(arc1, 0, 360);
lv_arc_set_angles(arc1, 0, 90);
lv_obj_add_style(arc1, LV_ARC_PART_BG, &style_arc1_bg);
lv_obj_add_style(arc1, LV_ARC_PART_INDIC, &style_arc1_indic);
static lv_style_t style_arc2_bg, style_arc2_indic;
lv_style_init(&style_arc2_bg);
lv_style_set_bg_opa(&style_arc2_bg, LV_STATE_DEFAULT, 0); //设置透明度
lv_style_set_border_width(&style_arc2_bg, LV_STATE_DEFAULT, 0); //边框线宽
lv_style_set_line_width(&style_arc2_bg, LV_STATE_DEFAULT, 15);
lv_style_set_line_color(&style_arc2_bg, LV_STATE_DEFAULT, LV_COLOR_SILVER);
lv_style_init(&style_arc2_indic);
lv_style_set_line_width(&style_arc2_indic, LV_STATE_DEFAULT, 15);
lv_style_set_line_color(&style_arc2_indic, LV_STATE_DEFAULT, LV_COLOR_PURPLE);
lv_obj_t *arc2 = lv_arc_create(scr, NULL);
lv_obj_set_size(arc2, 100, 100);
lv_obj_set_pos(arc2, 120, 50);
lv_arc_set_bg_angles(arc2, 0, 360);
lv_arc_set_angles(arc2, 90, 270);
lv_obj_add_style(arc2, LV_ARC_PART_BG, &style_arc2_bg);
lv_obj_add_style(arc2, LV_ARC_PART_INDIC, &style_arc2_indic);
lv_obj_t *label2 = lv_label_create(arc2, NULL); // 在Arc控件上创建一个标签
lv_obj_align(label2, arc2, LV_ALIGN_CENTER, 20, 0); // 对齐到Arc控件中心
lv_label_set_text(label2, "2"); // 设置标签文本
// lv_obj_set_event_cb(arc2, arc_event_handler);
#endif
//进度条
#if 1
static lv_style_t style_bar_label1;
lv_style_init(&style_bar_label1);
lv_style_set_text_font(&style_bar_label1, LV_STATE_DEFAULT, &lv_font_montserrat_18);
bar1 = lv_bar_create(scr, NULL);
lv_obj_set_size(bar1, 100, 15);
lv_bar_set_range(bar1, 0, 100);
lv_obj_set_style_local_bg_color(bar1, LV_BAR_PART_BG, LV_STATE_DEFAULT, LV_COLOR_ORANGE);
lv_obj_set_style_local_bg_color(bar1, LV_BAR_PART_INDIC, LV_STATE_DEFAULT, LV_COLOR_GREEN);
lv_bar_set_anim_time(bar1, 200);
lv_bar_set_value(bar1, 80, LV_ANIM_ON);
lv_obj_align(bar1, scr, LV_ALIGN_IN_TOP_MID, 40, 20);
label_bar1 = lv_label_create(scr, NULL);
lv_obj_add_style(label_bar1, LV_LABEL_PART_MAIN, &style_bar_label1);
lv_obj_align(label_bar1, bar1, LV_ALIGN_OUT_RIGHT_MID, 10, 0);
lv_label_set_text(label_bar1, "80%");
#endif
//复选框
#if 1
static lv_style_t style_checkbox1;
lv_style_init(&style_checkbox1);
lv_style_set_text_font(&style_checkbox1, LV_STATE_DEFAULT, &lv_font_montserrat_18);
lv_style_set_bg_opa(&style_checkbox1, LV_STATE_DEFAULT, LV_OPA_100);
lv_style_set_bg_color(&style_checkbox1, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
lv_obj_t *checkbox1 = lv_checkbox_create(scr, NULL);
lv_obj_set_width(checkbox1, 120);
lv_obj_set_pos(checkbox1, 240, 150);
lv_obj_add_style(checkbox1, LV_CHECKBOX_PART_BG, &style_checkbox1);
lv_checkbox_set_text(checkbox1, "CheckBox");
#endif
//滑块
#if 0
slider1 = lv_slider_create(scr, NULL);
lv_obj_set_size(slider1, 100, 10);
lv_slider_set_range(slider1, 0, 100);
lv_slider_set_anim_time(slider1, 200);
lv_bar_set_value(slider1, 20, LV_ANIM_ON);
lv_obj_align(slider1, scr, LV_ALIGN_IN_TOP_RIGHT, -20, 20);
// lv_obj_set_event_cb(slider1, arc_event_handler);
#endif
//按钮
#if 1
lv_obj_t *btn1 = lv_btn_create(scr, NULL);
lv_obj_set_pos(btn1, 240, 80);
lv_obj_set_size(btn1, 100, 40);
lv_obj_t *label_btn1 = lv_label_create(scr, NULL);
lv_obj_align(label_btn1, btn1, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn1, "key1");
// lv_obj_set_event_cb(btn1, arc_event_handler);
btn2 = lv_btn_create(scr, NULL);
lv_obj_set_pos(btn2, 220, 220);
lv_obj_set_size(btn2, 100, 40);
lv_btn_set_checkable(btn2, true);
lv_btn_set_state(btn2, true);
lv_obj_t *label_btn2 = lv_label_create(scr, NULL);
lv_obj_align(label_btn2, btn2, LV_ALIGN_CENTER, 0, 0);
lv_label_set_text(label_btn2, "key2");
// lv_obj_set_event_cb(btn2, arc_event_handler);
#endif
//预加载
#if 1
static lv_style_t style_spinner1_bg, style_spinner1_indic;
lv_style_init(&style_spinner1_bg);
lv_style_set_line_width(&style_spinner1_bg, LV_STATE_DEFAULT, 15);
lv_style_set_line_color(&style_spinner1_bg, LV_STATE_DEFAULT, LV_COLOR_MAGENTA);
lv_style_init(&style_spinner1_indic);
lv_style_set_line_width(&style_spinner1_indic, LV_STATE_DEFAULT, 15);
lv_style_set_line_color(&style_spinner1_indic, LV_STATE_DEFAULT, LV_COLOR_NAVY);
lv_obj_t *spinner1 = lv_spinner_create(scr, NULL);
lv_obj_set_size(spinner1, 80, 80);
lv_obj_set_pos(spinner1, 10, 180);
lv_obj_add_style(spinner1, LV_SPINNER_PART_BG, &style_spinner1_bg);
lv_obj_add_style(spinner1, LV_SPINNER_PART_INDIC, &style_spinner1_indic);
static lv_style_t style_spinner2_bg, style_spinner2_indic;
lv_style_init(&style_spinner2_bg);
lv_style_set_line_width(&style_spinner2_bg, LV_STATE_DEFAULT, 0);
lv_style_init(&style_spinner2_indic);
lv_style_set_line_width(&style_spinner2_indic, LV_STATE_DEFAULT, 8);
lv_style_set_line_color(&style_spinner2_indic, LV_STATE_DEFAULT, LV_COLOR_MAROON);
lv_obj_t *spinner2 = lv_spinner_create(scr, NULL);
lv_obj_set_size(spinner2, 60, 60);
lv_obj_set_pos(spinner2, 120, 180);
lv_spinner_set_type(spinner2, LV_SPINNER_TYPE_FILLSPIN_ARC);
lv_obj_add_style(spinner2, LV_SPINNER_PART_BG, &style_spinner2_bg);
lv_obj_add_style(spinner2, LV_SPINNER_PART_INDIC, &style_spinner2_indic);
#endif
//线条
#if 1
static lv_style_t style_line1;
lv_style_init(&style_line1);
lv_style_set_line_width(&style_line1, LV_STATE_DEFAULT, 10);
lv_style_set_line_color(&style_line1, LV_STATE_DEFAULT, LV_COLOR_MAKE(255, 100, 100));
lv_style_set_line_rounded(&style_line1, LV_STATE_DEFAULT, true); //圆角
static lv_point_t line_points[] = {{360, 60},{450,300}};
lv_obj_t *line1 = lv_line_create(scr, NULL);
lv_obj_add_style(line1, LV_LINE_PART_MAIN, &style_line1);
lv_line_set_points(line1, line_points, 2);
lv_obj_set_pos(line1, 0, 0); //点的坐标以此为原点
#endif
//led
#if 1
static lv_style_t style_led1;
lv_style_init(&style_led1);
lv_style_set_bg_color(&style_led1, LV_STATE_DEFAULT, LV_COLOR_YELLOW);
led1 = lv_led_create(scr, NULL);
lv_obj_set_pos(led1, 330, 215);
lv_obj_set_size(led1, 50, 50);
lv_led_off(led1);
lv_obj_add_style(led1, LV_LED_PART_MAIN, &style_led1);
#endif
}
static void slim_index2(void){
lv_obj_t *scr = lv_scr_act(); //获取屏幕对象
//仪表盘,这里就不再过多的设置样式了,反正样式设计函数调用都差不多
lv_obj_t *gauge1 = lv_gauge_create(scr, NULL);
lv_gauge_set_range(gauge1, 0, 80);
lv_obj_set_size(gauge1, 220, 220);
lv_gauge_set_scale(gauge1, 270, 21, 5);
lv_obj_align(gauge1, scr, LV_ALIGN_CENTER, 0, 0);
}
//没用调试触摸,所以一些控件用定时器做设置咯
extern lv_obj_t *arc;
extern lv_obj_t *bar1;
extern lv_obj_t *btn2;
extern lv_obj_t *label_bar1;
extern lv_obj_t *led1;
extern TIM_HandleTypeDef htim6;
extern TIM_HandleTypeDef htim7;
extern IWDG_HandleTypeDef hiwdg;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){
static uint8_t i = 0;
char str[10] = {0};
if (htim == (&htim6)){ //5s
HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_0);
i += 10;
i = (i>100)?0:i;
if((bar1 != NULL) && (btn2 != NULL)){
lv_bar_set_value(bar1, i, LV_ANIM_ON);
lv_btn_toggle(btn2);
lv_led_toggle(led1);
sprintf(str, "%d%%",i);
lv_label_set_text(label_bar1, str);
}
}
if (htim == (&htim7)){ //1s
HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_3);
HAL_IWDG_Refresh(&hiwdg);
}
}
PS:单个控件动画还是挺流畅的,多个控件同时动画的时候确实有些卡顿,但是没有展示的这么严重,应该是视频转GIF,丢帧有些严重。不过功能倒是实现了,嗨森!!!下一步移植触摸驱动,然后搞到ESP32上试试能不能跑个80M的spi