[原创] 【兆易GD32H759I-EVAL】--8.LVGL移植

dirty   2024-6-1 00:43 楼主

      本篇讲述兆易GD32H759I-EVAL开发板LVGL移植,并通过按键切换界面显示。

一.了解LVGL并做准备

1.了解LVGL

      LVGL(轻量级和通用图形库)是一个免费和开源的图形库,它提供了创建嵌入式GUI所需的一切,具有易于使用的图形元素,美丽的视觉效果和低内存占用。

主要特性

  • 丰富且强大的模块化图形组件:按钮 (buttons)、图表 (charts)、列表 (lists)、滑动条 (sliders)、图片 (images) 等

  • 高级的图形引擎:动画、抗锯齿、透明度、平滑滚动、图层混合等效果

  • 支持多种输入设备:触摸屏、 键盘、编码器、按键等

  • 支持多显示设备

  • 不依赖特定的硬件平台,可以在任何显示屏上运行

  • 配置可裁剪(最低资源占用:64 kB Flash,16 kB RAM)

  • 基于UTF-8的多语种支持,例如中文、日文、韩文、阿拉伯文等

  • 可以通过类CSS的方式来设计、布局图形界面(例如:Flexbox、Grid)

  • 支持操作系统、外置内存、以及硬件加速(LVGL已内建支持STM32 DMA2D、NXP PXP和VGLite)

  • 即便仅有单缓冲区(frame buffer)的情况下,也可保证渲染如丝般顺滑

  • 全部由C编写完成,并支持C++调用

  • 支持Micropython编程,参见:LVGL API in Micropython

  • 支持模拟器仿真,可以无硬件依托进行开发

  • 丰富详实的例程

  • 详尽的文档以及API参考手册,可线上查阅或可下载为PDF格式

  • 在 MIT 许可下免费和开源

配置要求:

      基本上,每个能够驱动显示器的现代控制器都适合运行 LVGL。 最低要求是:

  • 16、32 或 64 位微控制器或处理器

  • 建议使用 >16 MHz 时钟速度

  • 闪存/ROM: > 64 kB 用于非常重要的组件 (> 建议使用 180 kB)

  • RAM:
    • 静态 RAM 使用量:~2 kB,取决于使用的功能和对象类型

    • 堆: > 2kB (> 建议使用 8 kB)

    • 动态数据(堆): > 2 KB (> 如果使用多个对象,建议使用 16 kB). 在 lv_conf.h 文件中配置 LV_MEM_SIZE 生效。

    • 显示缓冲区:> “水平分辨率”像素(推荐 >10 × 10ד 水平分辨率”)

    • MCU或外部显示控制器中的一个帧缓冲区

  • C99 或更新的编译器

2.LVGL资源获取。这里使用LVGL v8.3,获取地址https://github.com/lvgl/lvgl/tree/release/v8.3

 

二.LVGL移植与应用

1.LVGL使用到的文件资源如下:

1_LVGL使用到的文件.jpg
图1:LVGL移植用到的文件

2.修改源文件

      这里修改lv_conf_template.h为lv_conf.h,lv_port_disp_template.c/h为lv_port_disp.c/h,lv_port_indev_template.c/h为lv_port_indev.c/h,并使能#if 1  /**/ #endif。屏分辨率480*272,宏定义之。

#define LV_HOR_RES_MAX      480
#define LV_VER_RES_MAX      272
 

3.工程添加源文件

2_LVGL源文件添加.jpg
图2:LVGL文件工程添加

4.工程添加头文件路径

3_LVGL头文件路径添加.jpg
图3:LVGL头文件路径添加

5.添加定时器为 LVGL 提供心跳

void TIMER1_IRQHandler(void)
{
    // static uint16_t count=0;
    
 
    if(SET == timer_interrupt_flag_get(TIMER1, TIMER_INT_FLAG_UP))
    {
        /* clear channel 0 interrupt bit */
        timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_UP);
        
        // count++;
        // if(count>=10000)
        // {
        //     count=0;
        //     printf("Run lv_tick_inc\r\n");
        // }
    
        lv_tick_inc(1);
    }
}

void timer_config(void)
{
    timer_parameter_struct timer_initpara;
    
    rcu_periph_clock_enable(RCU_TIMER1);
    timer_deinit(TIMER1);

    /* TIMER0 configuration */
    timer_initpara.prescaler         = 2999;//299;
    timer_initpara.alignedmode       = TIMER_COUNTER_EDGE;
    timer_initpara.counterdirection  = TIMER_COUNTER_UP;
    timer_initpara.period            = 99;
    timer_initpara.clockdivision     = TIMER_CKDIV_DIV1;
    timer_initpara.repetitioncounter = 0;
    timer_init(TIMER1, &timer_initpara);
    
    timer_enable(TIMER1);
    timer_interrupt_flag_clear(TIMER1, TIMER_INT_FLAG_UP);
    timer_interrupt_enable(TIMER1, TIMER_INT_UP);
    
    nvic_irq_enable(TIMER1_IRQn, 1, 1);

}

6.DAM初始化。主要可用来将数据发送到TLIframe buff中。这里个人使用了用于不用DMA两种方式,后面会有所体现。

void dma_config(void)
{
    dma_multi_data_parameter_struct dma_init_parameter;
    /* peripheral clock enable */
    rcu_periph_clock_enable(RCU_DMA1);
    /* DMA peripheral configure */
    dma_deinit(DMA1,DMA_CH0);
    dma_init_parameter.periph_width = DMA_PERIPH_WIDTH_16BIT;
    dma_init_parameter.periph_inc = DMA_PERIPH_INCREASE_ENABLE;
    dma_init_parameter.memory_width = DMA_MEMORY_WIDTH_16BIT;
    dma_init_parameter.memory_inc = DMA_MEMORY_INCREASE_ENABLE;
    dma_init_parameter.memory_burst_width = DMA_MEMORY_BURST_4_BEAT;
    dma_init_parameter.periph_burst_width = DMA_PERIPH_BURST_4_BEAT;
    dma_init_parameter.critical_value = DMA_FIFO_4_WORD;
    dma_init_parameter.circular_mode = DMA_CIRCULAR_MODE_DISABLE;
    dma_init_parameter.direction = DMA_MEMORY_TO_MEMORY;
    dma_init_parameter.priority = DMA_PRIORITY_ULTRA_HIGH;
    dma_multi_data_mode_init(DMA1,DMA_CH0,&dma_init_parameter);
    nvic_irq_enable(DMA1_Channel0_IRQn, 0, 0);
}

7.LCD初始化

      LCD初始化,图层使能加载。

void lcd_config(void)
{
    /* configure the GPIO of TLI */
    tli_gpio_config();
    /* configure TLI peripheral */
    tli_config();
}

void lcd_init(void)
{
    tli_layer_enable(LAYER0);
    tli_layer_disable(LAYER1);
    tli_reload_config(TLI_RL_FBR);
    /* enable TLI */
    tli_enable();

}

      图层参数配置如下

4_图层参数.jpg
图4:图层参数配置

8.LCD显示驱动移植

      LVGL 的显示驱动文件主要修改 lv_prot_disp.h 和 lv_prot_disp.c 文件.开发板外挂一颗32MB SDRAM,代码中将 TLI中 frame buffer 定义到地址SDRAM_DEVICE0_ADDR 即0x0c000000

lv_port_disp_init修改如下,这里选用第一个。

5_修改.jpg
图5:lv_port_disp_init修改

      disp_flush实现如下,这里通过宏LVGL_DMA选择用于不用DMA传输数据。

/*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*/
        #if (!LVGL_DMA)
        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)*/
                memcpy((uint32_t)&my_fb[y * LV_HOR_RES_MAX +x],(uint32_t)color_p,2);//1
                // memcpy((&my_fb[y * LV_HOR_RES_MAX +x],color_p,2);//1
                color_p++;
            }
        }
        #else
        if(area->x2 < 0) 
        {
            return;
        }
        if(area->y2 < 0) 
        {
            return;
        }
        if(area->x1 > LV_HOR_RES_MAX - 1)
        {
            return;
        }
        if(area->y1 > LV_VER_RES_MAX - 1)
        {
            return;
        }
        /*Truncate the area to the screen*/
        int32_t act_x1 = area->x1 < 0 ? 0 : area->x1;
        int32_t act_y1 = area->y1 < 0 ? 0 : area->y1;
        int32_t act_x2 = area->x2 > LV_HOR_RES_MAX - 1 ? LV_HOR_RES_MAX - 1 : area->x2;
        int32_t act_y2 = area->y2 > LV_VER_RES_MAX - 1 ? LV_VER_RES_MAX - 1 : area->y2;
        x1_flush = act_x1;
        y1_flush = act_y1;
        x2_flush = act_x2;
        y2_fill = act_y2;
        y_fill_act = act_y1;
        buf_to_flush = color_p;
        dma_transfer((uint32_t)buf_to_flush, (uint32_t)&my_fb[y_fill_act * LV_HOR_RES_MAX +x1_flush],(x2_flush - x1_flush + 1));

        #endif

    }

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

9.DMA传输数据如下

6_DMA_buffer.jpg
图6:DMA传输数据

10.lv_conf.h配置

      这里可将LV_MEM_SIZE适当改大,根据需要使能字体大小宏,根据需要使能组件例程,这里是能了LV_USE_DEMO_WIDGETS和LV_USE_DEMO_MUSIC。

11.main函数

int main(void)
{
    BaseType_t ret;
    
    /* enable the CPU cache */
    cache_enable();
    /* initialize the LEDs */
    test_status_led_init();

    /* configure systick */
    systick_config();

    /* flash the LEDs for 2 time */
    led_flash(2);

    /* configure USART0 */
    usart_config();

    /* configure TAMPER key */
    gd_eval_key_init(KEY_TAMPER, KEY_MODE_EXTI);

    /* output a message on hyperterminal using printf function */
    printf("\r\n =================  LVGL =================   \r\n");

    /**********************LCD Application**********************/
    
    /* config the EXMC access mode */
    exmc_synchronous_dynamic_ram_init(EXMC_SDRAM_DEVICE0);
    printf("\r\nSDRAM Init \r\n");

    timer_config();
    
    lcd_config();
    lcd_init();
    /* configure the GPIO of SPI touch panel */
    touch_panel_gpio_configure();
    #if (LVGL_DMA)
    delay_1ms(50);
    dma_config();
    delay_1ms(1000);
    #endif

    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();

    // lv_demo_music();
    lv_demo_widgets();

    while(1)
    {
        delay_1ms(5);
        lv_task_handler();
    }
    
}

12.按键切换显示。默认显示的经典画面lv_demo_widgets,按键切换显示音乐界面。

/*!
    \brief      this function handles external lines 10 to 15 interrupt request
    \param[in]  none
    \param[out] none
    \retval     none
*/
void EXTI10_15_IRQHandler(void)
{
    static uint8_t gui_show=0;

    
    if(RESET != exti_interrupt_flag_get(EXTI_13)) 
    {
        printf("Key Pressed\r\n");
        exti_interrupt_flag_clear(EXTI_13);

        lv_demo_music(); 
             
    }
}

三.测验

      编译烧录后,LCD显示widgets界面见下图。按按键后,切换播放音乐动画界面,见下视频。

8_widgets显示.jpg
图7:LCD显示

     

 

      至此,实现兆易GD32H759I-EVAL开发板.LVGL移植。      

 

LCD_Music_Show

 

本帖最后由 dirty 于 2024-6-1 10:15 编辑

回复评论 (3)

1 来自 2楼 dirty 

LVGL移植工程代码

本帖最后由 dirty 于 2024-6-24 23:40 编辑
点赞  2024-6-24 22:37

兆易GD32H759I-EVAL开发板.LVGL移植成功,赞一个

点赞  2024-6-2 09:03
引用: Jacktang 发表于 2024-6-2 09:03 兆易GD32H759I-EVAL开发板.LVGL移植成功,赞一个

也是弄了一段时间,中间有遇到些问题,最终解决移植成功。工程代码稍晚点时候分享出来,可供大家参考学习。

点赞  2024-6-3 09:08
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复