移植准备

  1. 基于梁山派屏幕扩展板mcu屏幕源码Screen_MCU移植
  2. 下载lvgl 8.3版本源码下载地址:https://github.com/lvgl/lvgl
  3. 参考文档:https://docs.lvgl.io/8/porting/project.html

    移植步骤

    删除源码

    删除源码中不需要的文件夹,仅保留如下内容

  4. demos : lvgl综合案例

  5. examples :单个功能案例
  6. src : 源代码
  7. lv_conf_template.h : 重要的配置文件,里面存在非常多的开关
  8. lvgl.h : 头文件

将上述内容存放刀名为lvgl的文件夹中

2.导入lvgl到项目screen_mcu中

  1. 在screen_mcu项目中新建third_party文件夹
  2. 将第1个步骤中的lvgl文件夹拷入其中
  3. 将lvgl文件夹中的lv_conf_tempalate.h修改为lv_conf.h
  4. 把lv_conf.h的条件编译指令#if 0修改成#if 1

    3.keil添加分组和头文件

  5. 使用keil打开screen_mcu项目,添加如下分组

image.png

  1. third_party/lvgl/example/porting
  2. third_party/lvgl/src/core
  3. third_party/lvgl/src/draw
  4. third_party/lvgl/src/extra
  5. third_party/lvgl/src/font
  6. third_party/lvgl/src/gpu
  7. third_party/lvgl/src/hal
  8. third_party/lvgl/src/misc
  9. third_party/lvgl/src/widgets
  1. 添加LVGL相关的.c文件到相应分组,如下:

image.png

  1. 添加头文件路径

image.png

  1. 开启C99模式

    3. 移植显示

  2. 把lv_port_disp_template.c/h的条件编译指令#if 0修改成#if 1

  3. lv_port_disp_template.h中包含输出设备驱动头文件lcd.h
  4. lv_port_disp_template.h中宏定义水平和竖直分辨率(默认横屏)

    1. #define MY_DISP_HOR_RES 240 //水平分辨率
    2. #define MY_DISP_VER_RES 280 //垂直分辨率
  5. 修改 lv_port_disp_template.c中 的 lv_port_disp_init 函数

配置图形数据缓冲模式
在lv_port_disp_init函数中选择一种缓冲模式,注释掉其它两种模式
image.png
修改宽高

  1. disp_drv.hor_res = lcddev.width;
  2. disp_drv.ver_res = lcddev.height;

image.png

  1. 在disp_flush函数中配置打点输出
    1. static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
    2. {
    3. ST7789_Fill(area->x1,area->y1,area->x2,area->y2,(uint16_t*)color_p);
    4. /*IMPORTANT!!!
    5. *Inform the graphics library that you are ready with the flushing*/
    6. lv_disp_flush_ready(disp_drv);
    7. }

4. 移植触摸

  1. 把lv_port_indev_tempalte.c/h的条件编译#if 0修改成# if 1
  2. 在lv_port_indev_tempalte.c中裁剪输入设备
  • 只保留touchpad_xxx相关的方法,删除其它方法
  • lv_port_indev_init中只保留touchpad相关的代码
  • 在touchpad_init方法中执行触摸屏初始化CST816T_Init();
    1. /*Initialize your touchpad*/
    2. static void touchpad_init(void)
    3. {
    4. GT1151_Init(); //触摸屏初始化
    5. }
  1. 配置触摸检测函数

    1. /*Return true is the touchpad is pressed*/
    2. static bool touchpad_is_pressed(void)
    3. {
    4. /*Your code comes here*/
    5. return CST816T_is_pressed();
    6. // return false;
    7. }
  2. 配置坐标获取函数

    1. /*Get the x and y coordinates if the touchpad is pressed*/
    2. static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
    3. {
    4. /*Your code comes here*/
    5. CST816T_get_xy((uint16_t*)x,(uint16_t*)y);
    6. //(*x) = 0;
    7. //(*y) = 0;
    8. }

提示
lv_port_indev_template.c修改之后代码如下:

  1. /**
  2. * @file lv_port_indev_templ.c
  3. *
  4. */
  5. /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
  6. #if 1
  7. /*********************
  8. * INCLUDES
  9. *********************/
  10. #include "lv_port_indev_template.h"
  11. //#include "../../lvgl.h"
  12. #include "cst816t.h"
  13. /*********************
  14. * DEFINES
  15. *********************/
  16. /**********************
  17. * TYPEDEFS
  18. **********************/
  19. /**********************
  20. * STATIC PROTOTYPES
  21. **********************/
  22. static void touchpad_init(void);
  23. static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
  24. static bool touchpad_is_pressed(void);
  25. static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
  26. static void mouse_init(void);
  27. static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
  28. static bool mouse_is_pressed(void);
  29. static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);
  30. static void keypad_init(void);
  31. static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
  32. static uint32_t keypad_get_key(void);
  33. static void encoder_init(void);
  34. static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
  35. static void encoder_handler(void);
  36. static void button_init(void);
  37. static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
  38. static int8_t button_get_pressed_id(void);
  39. static bool button_is_pressed(uint8_t id);
  40. /**********************
  41. * STATIC VARIABLES
  42. **********************/
  43. lv_indev_t * indev_touchpad;
  44. lv_indev_t * indev_mouse;
  45. lv_indev_t * indev_keypad;
  46. lv_indev_t * indev_encoder;
  47. lv_indev_t * indev_button;
  48. static int32_t encoder_diff;
  49. static lv_indev_state_t encoder_state;
  50. /**********************
  51. * MACROS
  52. **********************/
  53. /**********************
  54. * GLOBAL FUNCTIONS
  55. **********************/
  56. void lv_port_indev_init(void)
  57. {
  58. /**
  59. * Here you will find example implementation of input devices supported by LittelvGL:
  60. * - Touchpad
  61. * - Mouse (with cursor support)
  62. * - Keypad (supports GUI usage only with key)
  63. * - Encoder (supports GUI usage only with: left, right, push)
  64. * - Button (external buttons to press points on the screen)
  65. *
  66. * The `..._read()` function are only examples.
  67. * You should shape them according to your hardware
  68. */
  69. static lv_indev_drv_t indev_drv;
  70. /*------------------
  71. * Touchpad
  72. * -----------------*/
  73. /*Initialize your touchpad if you have*/
  74. touchpad_init();
  75. /*Register a touchpad input device*/
  76. lv_indev_drv_init(&indev_drv);
  77. indev_drv.type = LV_INDEV_TYPE_POINTER;
  78. indev_drv.read_cb = touchpad_read;
  79. indev_touchpad = lv_indev_drv_register(&indev_drv);
  80. }
  81. /**********************
  82. * STATIC FUNCTIONS
  83. **********************/
  84. /*------------------
  85. * Touchpad
  86. * -----------------*/
  87. /*Initialize your touchpad*/
  88. static void touchpad_init(void)
  89. {
  90. /*Your code comes here*/
  91. CST816T_Init();
  92. }
  93. /*Will be called by the library to read the touchpad*/
  94. static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
  95. {
  96. static lv_coord_t last_x = 0;
  97. static lv_coord_t last_y = 0;
  98. /*Save the pressed coordinates and the state*/
  99. if(touchpad_is_pressed()) {
  100. touchpad_get_xy(&last_x, &last_y);
  101. data->state = LV_INDEV_STATE_PR;
  102. }
  103. else {
  104. data->state = LV_INDEV_STATE_REL;
  105. }
  106. /*Set the last pressed coordinates*/
  107. data->point.x = last_x;
  108. data->point.y = last_y;
  109. }
  110. /*Return true is the touchpad is pressed*/
  111. static bool touchpad_is_pressed(void)
  112. {
  113. /*Your code comes here*/
  114. return CST816T_is_pressed();
  115. // return false;
  116. }
  117. /*Get the x and y coordinates if the touchpad is pressed*/
  118. static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
  119. {
  120. /*Your code comes here*/
  121. CST816T_get_xy((uint16_t*)x,(uint16_t*)y);
  122. //(*x) = 0;
  123. //(*y) = 0;
  124. }
  125. #else /*Enable this file at the top*/
  126. /*This dummy typedef exists purely to silence -Wpedantic.*/
  127. typedef int keep_pedantic_happy;
  128. #endif

6.添加测试案例

测试音乐界面

  1. keil添加lvgl/demos/music文件夹下所有.c文件
  2. 添加相关头文件路径

    1. ..\..\third_party\lvgl\demos
    2. ..\..\third_party\lvgl\demos\music
  3. 修改lv_conf.h中的

define LV_USE_DEMO_MUSIC 0改为#define LV_USE_DEMO_MUSIC 1
#define LV_FONT_MONTSERRAT_12 0改为#define LV_FONT_MONTSERRAT_12 1
#define LV_FONT_MONTSERRAT_16 0改为#define LV_FONT_MONTSERRAT_16 1

  1. #define LV_USE_DEMO_MUSIC 0
  2. 改为
  3. #define LV_USE_DEMO_MUSIC 1
  4. #define LV_FONT_MONTSERRAT_12 0
  5. 改为
  6. #define LV_FONT_MONTSERRAT_12 1
  7. #define LV_FONT_MONTSERRAT_16 0
  8. 改为
  9. #define LV_FONT_MONTSERRAT_16 1

7.main.c主函数

  1. #include "gd32f4xx.h"
  2. #include "systick.h"
  3. #include <stdio.h>
  4. #include "main.h"
  5. #include "bsp_basic_timer.h"
  6. #include "lcd.h"
  7. #include "touch.h"
  8. #include "lvgl.h"
  9. #include "lv_demo_music.h"
  10. #include "lv_conf.h"
  11. #include "lv_port_disp_template.h"
  12. #include "lv_port_indev_template.h"
  13. int main(void)
  14. {
  15. nvic_priority_group_set(NVIC_PRIGROUP_PRE2_SUB2); // 优先级分组
  16. systick_config();
  17. lv_init();
  18. lv_port_disp_init();
  19. lv_port_indev_init();
  20. lv_demo_music();
  21. while(1)
  22. {
  23. lv_tick_inc(1);
  24. lv_timer_handler();
  25. delay_1ms(5);
  26. }
  27. }

8.提高堆栈内存大小

修改startup_gd32f450_470.s中的Stack_Size

  1. 原始:
  2. Stack_Size EQU 0x00000400
  3. 修改后:
  4. Stack_Size EQU 0x00000800

9. 提供时钟

在Hardware下创建timer文件夹,文件夹下定义bsp_basic_timer.h和bsp_basic_timer.c文件,内容如下
bsp_basic_timer.h

  1. #ifndef _BSP_BASIC_TIMER_H
  2. #define _BSP_BASIC_TIMER_H
  3. #include "gd32f4xx.h"
  4. #include "systick.h"
  5. #include "stdio.h"
  6. #include "lvgl.h"
  7. #define BSP_TIMER_RCU RCU_TIMER5 // 定时器时钟
  8. #define BSP_TIMER TIMER5 // 定时器
  9. #define BSP_TIMER_IRQ TIMER5_DAC_IRQn // 定时器中断
  10. #define BSP_TIMER_IRQHANDLER TIMER5_DAC_IRQHandler // 定时器中断服务函数
  11. //#define BSP_TIMER_RCU RCU_TIMER2 // 定时器时钟
  12. //#define BSP_TIMER TIMER2 // 定时器
  13. //#define BSP_TIMER_IRQ TIMER2_IRQn // 定时器中断
  14. //#define BSP_TIMER_IRQHANDLER TIMER2_IRQHandler // 定时器中断服务函数
  15. void basic_timer_config(uint16_t pre,uint16_t per); // 基本定时器配置
  16. #endif /* BSP_BASIC_TIMER_H */

bsp_basic_timer.c

  1. #include "bsp_basic_timer.h"
  2. //#include "bsp_led.h"
  3. /************************************************
  4. 函数名称 : basic_timer_config
  5. 功 能 : 基本定时器配置
  6. 参 数 : pre:时钟预分频值
  7. per:周期
  8. *************************************************/
  9. void basic_timer_config(uint16_t pre,uint16_t per)
  10. {
  11. /* 一个周期的时间T = 1/f, 定时时间time = T * 周期
  12. 设预分频值位pre,周期位per
  13. time = (pre + 1) * (per + 1) / psc_clk
  14. */
  15. timer_parameter_struct timere_initpara; // 定义定时器结构体
  16. /* 开启时钟 */
  17. rcu_periph_clock_enable(BSP_TIMER_RCU); // 开启定时器时钟
  18. /* CK_TIMERx = 4 x CK_APB1 = 4x50M = 200MHZ */
  19. rcu_timer_clock_prescaler_config(RCU_TIMER_PSC_MUL4); // 配置定时器时钟
  20. timer_deinit(BSP_TIMER); // 复位定时器
  21. /* 配置定时器参数 */
  22. timere_initpara.prescaler = pre-1; // 时钟预分频值 0-65535 psc_clk = CK_TIMER / pre
  23. timere_initpara.alignedmode = TIMER_COUNTER_EDGE; // 边缘对齐
  24. timere_initpara.counterdirection = TIMER_COUNTER_UP; // 向上计数
  25. timere_initpara.period = per-1; // 周期
  26. /* 在输入捕获的时候使用 数字滤波器使用的采样频率之间的分频比例 */
  27. timere_initpara.clockdivision = TIMER_CKDIV_DIV1; // 分频因子
  28. /* 只有高级定时器才有 配置为x,就重复x+1次进入中断 */
  29. timere_initpara.repetitioncounter = 0; // 重复计数器 0-255
  30. timer_init(BSP_TIMER,&timere_initpara); // 初始化定时器
  31. /* 配置中断优先级 */
  32. nvic_irq_enable(BSP_TIMER_IRQ,3,2); // 设置中断优先级为 3,2
  33. /* 使能中断 */
  34. timer_interrupt_enable(BSP_TIMER,TIMER_INT_UP); // 使能更新事件中断
  35. /* 使能定时器 */
  36. timer_enable(BSP_TIMER);
  37. }
  38. /************************************************
  39. 函数名称 : BSP_TIMER_IRQHandler
  40. 功 能 : 基本定时器中断服务函数
  41. 参 数 : 无
  42. 返 回 值 : 无
  43. 作 者 : LC
  44. *************************************************/
  45. void BSP_TIMER_IRQHANDLER(void)
  46. {
  47. /* 这里是定时器中断 */
  48. if(timer_interrupt_flag_get(BSP_TIMER,TIMER_INT_FLAG_UP) == SET)
  49. {
  50. timer_interrupt_flag_clear(BSP_TIMER,TIMER_INT_FLAG_UP); // 清除中断标志位
  51. /* 执行功能 */
  52. lv_tick_inc(1);
  53. }
  54. }

定义之后,keil添加.c文件和头文件即可

错误处理

错误一

.\Objects\GD32F450.axf: Error: L6218E: Undefined symbol __aeabi_assert (referred from qrcodegen.o).
解决方案:
设置如下即可
350.png