[原创] 【HC32F4A0开发板】移植LVGL wiget_demo

lugl4313820   2023-3-1 23:05 楼主

【目的】移植lvgl并且运行lvgl的widget示例

【 HC32F4A0开发板】触摸测试 这篇帖子的基础上,进行移植

1、到lvgl下载官方源码,也可以拷贝原子的8.2源码进行移植,我这里是利用原子的源代码进行移植。

2、在M:\HC32F4A0_DDL_Rev2.1.0的midwares下面新建GUI以及GUI_APP

3、在GUI下面新建lvgl文件夹,建成后将l源码下面的lvgl.h、lvgl_conf_template以及src文件夹、examples下的porting文件夹拷到lvgl文件夹下面。

image.png   4、把lvgl_conf_template.h更名为lvgl_conf.h

5、在keil下添如下目录:

image.png   6、/lvgl/src/core 分组中添加 core 文件夹下的全部.c 文件,如下图所示:

image.png   7、往/lvgl/src/draw 组中添加 draw 文件夹下除 nxp_pxp、nxp_vglite、sdl 和

stm32_dma2d 文件夹之外的全部.c 文件,如下图所示:

image.png   8、往 /lvgl/src/extra 组中添加 extra 文件夹下除了 lib 文件夹之外的全部.c 文件,如下图所示:

image.png   9、往 /lvgl/src/font 组中添加 font 文件夹下的全部.c 文件,如下图所示:

image.png   10、往 lvgl/src/gpu 组中添加 draw/stm32_dma2d 和 draw/sdl文件夹下的全部.c文件,如下图所示:

image.png   11、往 lvgl/src/hal 组中添加 hal 文件夹下的全部.c 文件,如下图所示:

image.png   12、往/lvgl/src/misc 组中添加 misc 文件夹下的全部.c 文件,如下图所示:

image.png   13、往 lvgl/src/widgets 组中添加 widgets 文件夹下的全部.c 文件,如下图所示:

image.png   14、/lvgl/examples/porting 组添加 /LVGL/GUI/lvgl/examples/porting目录下的 lv_port_disp_template.c 和 lv_port_indev_template.c 文件,如下图所示:

image.png   15、将源码的demos文件夹拷到GUI_APP目录下面:

image.png

16、将 demos/widgets下的全部C文件加到GUI_APP/widgets下面:

image.png   到些文件的拷贝结束。【注意】上面有些是文件夹里有文件夹的,小细心添回,如果后面编译报错,根据报错提示,逐一添加。

17、移植 LVGL 只需要添加关键的头文件路径即可,因为 lvgl.h 文件已经为我们省去了很多包含头文件的操作,添加的头文件路径如下图所示:

image.png   18、由于我们前面已经添加了显示、触摸、定时器,所以这一步省略,如果没有的请自行添加。

19、修改工程文件

  1. )在timer0.c中加入#include "lvgl.h",在定时器中断里加入心跳包:
  2. image.png   配置显示屏、触摸输入驱动
    打开 /lvgl/examples/porting 分组中的 lv_port_disp_template.c/h(显示屏相关)
    和 lv_port_indev_template.c/h(触摸输入相关)文件,将这 4 个文件中的条件编译指令#if 0 都
    修改成#if 1,如下源码所示:
    修改前:
    #if 0 /* lv_port_disp_template.c/h 和 lv_port_indev_template.c/h */
    修改后:
    #if 1 /* 把#if 0 修改成#if 1 */
  3. 修改 lv_port_disp_template.c 文件
    该文件用于配置显示屏,它可以将用户的底层显示驱动与 LVGL的显示驱动衔接起来。我们打开官方提供的 lv_port_disp_template.c 文件,包含 LCD 驱动头文件,然后初始化 LCD 以及配置打点函数,修改后的源码如下:
    /**
     * @File  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 "../../lvgl.h"
    #include "ev_hc32f4a0_lqfp176_nt35510.h"
    #include "main.h"
    /*********************
     *      DEFINES
     *********************/
    #define USE_SRAM 0 /* 使用外部 sram 为 1,否则为 0 */
    /**********************
     *      TYPEDEFS
     **********************/
    
    #define MY_DISP_HOR_RES (480) /* 屏幕宽度 */
    #define MY_DISP_VER_RES (800) /* 屏幕高度 */
    /**********************
     *  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();
    
    /*-----------------------------
     * 创建一个绘图缓冲区
     *----------------------------*/
     /**
     * LVGL 需要一个缓冲区用来绘制小部件
     * 随后,这个缓冲区的内容会通过显示设备的 flush_cb(显示设备刷新函数) 复制到显示设备上
     * 这个缓冲区的大小需要大于显示设备一行的大小
     *
     * 这里有 3 中缓冲配置:
     * 1. 单缓冲区:
     * LVGL 会将显示设备的内容绘制到这里,并将他写入显示设备。
     *
     * 2. 双缓冲区:
     * LVGL 会将显示设备的内容绘制到其中一个缓冲区,并将他写入显示设备。
     * 需要使用 DMA 将要显示在显示设备的内容写入缓冲区。
     * 当数据从第一个缓冲区发送时,它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。
     * 这样使得渲染和刷新可以并行执行。
    *
     * 3. 全尺寸双缓冲区
     * 设置两个屏幕大小的全尺寸缓冲区,并且设置 disp_drv.full_refresh = 1。
     * LVGL 将始终以 'flush_cb' 的形式提供整个渲染屏幕,只需更改帧缓冲区的地址。
     */
        /* 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_2;
    
        /*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)
    {
    	  BSP_LCD_IO_Init();
        BSP_LCD_RSTCmd(EIO_PIN_RESET); /* RST# to low */
        DDL_DelayMS(50UL);
        BSP_LCD_RSTCmd(EIO_PIN_SET);  /* RST# to high */
        DDL_DelayMS(50UL);
    
        /* Initialize NT35510 LCD */
        BSP_NT35510_Init();
    
        /* Clear LCD screen */
     //   BSP_NT35510_Clear(LCD_COLOR_BLACK);
    
        /* Turn on LCD backlight */
        BSP_LCD_BKLCmd(EIO_PIN_SET);
    
        /* Set LCD cursor */
    
    }
    
    /*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)
    {
        /*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)*/
    					
    						BSP_NT35510_WritePixel(x, y, color_p->full);
                color_p++;
            }
        }
    	/* 重要!!!LCD 驱动函数,在指定区域内填充指定颜色块 */
    		//lcd_color_fill(area->x1,area->y1,area->x2,area->y2,(uint16_t*)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.

    修改 lv_port_indev_template.c 文件该文件用于配置输入设备,例如:触摸屏、鼠标、键盘、编码器、按键等,它可以将用户的底层输入设备驱动与 LVGL的输入驱动衔接起来。这里我们使用触摸屏作为输入设备,具体的配置源码如下所示:
    static void touchpad_init(void);
    static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
    static bool touchpad_is_pressed(void);
    static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);
    
    
    
    /**********************
     *  STATIC VARIABLES
     **********************/
    lv_indev_t * indev_touchpad;
    
    
    static int32_t encoder_diff;
    static lv_indev_state_t encoder_state;
    
    /**********************
     *      MACROS
     **********************/
    
    /**********************
     *   GLOBAL FUNCTIONS
     **********************/
    
    
    void lv_port_indev_init(void)
    {
        /**
         * Here you will find example implementation of input devices supported by LittelvGL:
         *  - Touchpad
         *  - Mouse (with cursor support)
         *  - Keypad (supports GUI usage only with key)
         *  - Encoder (supports GUI usage only with: left, right, push)
         *  - Button (external buttons to press points on the screen)
         *
         *  The `..._read()` function are only examples.
         *  You should shape them according to your hardware
         */
    
        static lv_indev_drv_t indev_drv;
    
        /*------------------
         * Touchpad
         * -----------------*/
    
        /*Initialize your touchpad if you have*/
        //touchpad_init();
    
        /*Register a touchpad input device*/
        lv_indev_drv_init(&indev_drv);
        indev_drv.type = LV_INDEV_TYPE_POINTER;
        indev_drv.read_cb = touchpad_read;
        indev_touchpad = lv_indev_drv_register(&indev_drv);
    
    }
    
    /**********************
     *   STATIC FUNCTIONS
     **********************/
    
    /*------------------
     * Touchpad
     * -----------------*/
    
    /*Initialize your touchpad*/
    static void touchpad_init(void)
    {
        /*Your code comes here*/
    	 
    }
    
    /*Will be called by the library to read the touchpad*/
    static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
    {
        /*Save the pressed coordinates and the state*/
        if(touchpad_is_pressed()) {
            data->state = LV_INDEV_STATE_PR;
        }
        else {
            data->state = LV_INDEV_STATE_REL;
        }
    
        /*Set the last pressed coordinates*/
        data->point.x = touchdat.stcPoint.u16X;
        data->point.y = touchdat.stcPoint.u16Y;
    }
    
    /*Return true is the touchpad is pressed*/
    static bool touchpad_is_pressed(void)
    {
    	TOUCHPAD_Read(&touchdat);
    	if(touchdat.enPointPress == SET)
    	{
    		//DDL_Printf("sta\r\n");
    		return true;
    		
    	}
        return false;
    }
    
    /*Get the x and y coordinates if the touchpad is pressed*/
    static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
    {
        /*Your code comes here*/
    		static uint16_t u16LastX = 0U;
        static uint16_t u16LastY = 0U;
    
        /*Save the pressed coordinates and the state*/
        if (touchpad_is_pressed()) {
            BSP_GT9XX_GetXY(GT9XX_POINT1, &u16LastX, &u16LastY);
                                 
        } 
        (*x) = u16LastX;
        (*y) = u16LastY;
    }
    
    
    
    #else /*Enable this file at the top*/
    
    /*This dummy typedef exists purely to silence -Wpedantic.*/
    typedef int keep_pedantic_happy;
    #endif

    20、编译测试函数

    int32_t main(void)
    {
    		uint8_t show_str[40] = {0}; 
        /* Register write enable for some required peripherals. */
        LL_PERIPH_WE(LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM);
        /* BSP Clock initialize */
        BSP_CLK_Init();
    
        /* Expand IO init */
        BSP_IO_Init();
        /* BSP LED initialize */
        BSP_LED_Init();
    		
        /* Printf init */
        DDL_PrintfInit(BSP_PRINTF_DEVICE, BSP_PRINTF_BAUDRATE, BSP_PRINTF_Preinit);
        /* Matrix KEY row init */
        /* Initialize LCD touch pad */
        
        /* Initialize NT35510 LCD */
        //BSP_NT35510_Init();
    	 lv_init(); /* lvgl 系统初始化 */
    	 lv_port_disp_init(); /* lvgl 显示接口初始化,放在 lv_init()的后面 */
    	 lv_port_indev_init(); /* lvgl 输入接口初始化,放在 lv_init()的后面 */
    
    		init_timer0();
        /* Register write protected for some required peripherals. */
        LL_PERIPH_WP(LL_PERIPH_EFM | LL_PERIPH_FCG | LL_PERIPH_GPIO | LL_PERIPH_PWC_CLK_RMU | LL_PERIPH_SRAM);
    
    		lv_demo_widgets();			// 这里是widgets的demo函数
        /* Add your code here */
        for (;;) {
    
    		lv_timer_handler(); /* LVGL 管理函数相当于 RTOS 触发任务调度函数 */
    			DDL_DelayMS(1UL);
        }
    }

    到这里移植就到一段落了,下面提一些注意事项:

    1、要打开字体函数,否则会报下面的错误:

    image.png   image.png   2、小华的有些外设打开需要在hc32f4xx_conf.h里面打开开关,不要会显示文件找不到。

    image.png  最后面的显示效果如下:

    7401f603b609612d3f8e15256b2db2b.jpg  

    LVGL测试

本帖最后由 lugl4313820 于 2023-3-1 23:06 编辑

回复评论 (2)

流畅的lvgl

优化显示,前面因为是像素填充的,现在修改为块填充。

1、修改NT35510的调置块的函数:

/**
 * @brief  NT35510_SetRegion 设置显示块的坐标
 * @param  [in] pstcLCD:                LCD controller
 * @param  u16startXpos:                     Specifies the sartX position.
 * @param  u16endXpos:                     Specifies the endX position.
 * @param  u16startYpos:                     Specifies the startY position.
 * @param  u16endYpos:                     Specifies the endY position.
 * @retval None
 */
//设置横坐标指令:0x2A00~0x2A03;
//设置纵坐标指令:0x2B00~0x2B03;
#define NT35110SC 0x2A00
#define NT35110EC 0x2A02
#define NT35110SP 0x2B00
#define NT35110EP 0x2B02
void NT35510_SetRegion(stc_lcd_controller_t *pstcLCD, 
                                                    uint16_t u16startXpos,
                                                    uint16_t u16endXpos,
                                                    uint16_t u16startYpos, 
                                                    uint16_t u16endYpos)
{
    /* Set cursor */

        NT35510_WriteRegData(pstcLCD, NT35110SC, (u16startXpos >> 8));
        NT35510_WriteRegData(pstcLCD, NT35110SC +1U, (u16startXpos & 0xFFU));

        NT35510_WriteRegData(pstcLCD, NT35110EC, (u16endXpos >> 8));
        NT35510_WriteRegData(pstcLCD, NT35110EC+1U, (u16endXpos & 0xFFU));

        NT35510_WriteRegData(pstcLCD,NT35110SP, (u16startYpos >> 8));
        NT35510_WriteRegData(pstcLCD,NT35110SP+1U, (u16startYpos & 0xFFU));

        NT35510_WriteRegData(pstcLCD,NT35110SP+2U, (u16endYpos >> 8));
        NT35510_WriteRegData(pstcLCD,NT35110SP+3U, (u16endYpos & 0xFFU));

}

2、修改lv_port_disp中的显示函数:

/*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)
{
    /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/

    int32_t x;
    int32_t y;
        BSP_NT35510_SetRegion(area->x1,area->x2,area->y1,area->y2);
        BSP_NT35510_PrepareWriteRAM();
    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)*/
                        BSP_NT35510_WriteData(color_p->full);
                    //    BSP_NT35510_WritePixel(x,y,color_p->full);
            color_p++;
        }
    }
    /* 重要!!!LCD 驱动函数,在指定区域内填充指定颜色块 */
        //lcd_color_fill(area->x1,area->y1,area->x2,area->y2,(uint16_t*)color_p);

    /*IMPORTANT!!!
     *Inform the graphics library that you are ready with the flushing*/
    lv_disp_flush_ready(disp_drv);
}

经过这样优化后,就可以流畅的显示了。

 

点赞  2023-3-5 20:53

楼主啊,我所用的硬件和你一样,我按照你的方法移植为什么屏幕没有被点亮啊

点赞  2023-8-22 19:56
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复