tinnu 发表于 2020-9-13 23:27

【GD32450I-EVAL】LittleVGL显示部分移植

本帖最后由 tinnu 于 2020-9-13 23:30 编辑

<p><span style="font-size:24px;"><span style="font-family:楷体;">(一)Littlevgl</span></span><br />
Littlevgl是目前最火的开源嵌入式GUI之一,相比于emwin,其界面更加柔和,基于MIT协议,这个协议非常宽松,可以商用闭源,而emwin商用则是需要付费,并且源码也是封闭的。另外,在移植的便利性上Littlevgl也远比emwin更有优势。<br />
LittleVGL最新已经更新到V7系列,我这次就移植最新的这个版本。<br />
Littlevgl下载地址:https://gitee.com/mirrors/lvgl?_from=gitee_search<br />
WIKI(里面有API手册下载地址):https://docs.lvgl.io/v7/en/html/</p>

<p>&nbsp;</p>

<p><span style="font-size:24px;"><span style="font-family:楷体;">(二)基本移植</span></span><br />
1-把原本的littlevgl文件夹删掉,把Git下来的src里面的文件全部拷进去<br />
#define LV_HOR_RES_MAX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(LCD_WIDTH)<br />
#define LV_VER_RES_MAX &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(LCD_HEIGHT)<br />
其中 LCD_WIDTH 的值在后面的驱动文件里面定义。</p>

<p>&nbsp;</p>

<p>2-把GPU禁用掉:</p>

<pre>
<code>/* 1: Enable GPU interface*/
#define LV_USE_GPU              0   /*Only enables `gpu_fill_cb` and `gpu_blend_cb` in the disp. drv- */
#define LV_USE_GPU_STM32_DMA2D  0
/*If enabling LV_USE_GPU_STM32_DMA2D, LV_GPU_DMA2D_CMSIS_INCLUDE must be defined to include path of CMSIS header of target processor
e.g. "stm32f769xx.h" or "stm32f429xx.h" */
#define LV_GPU_DMA2D_CMSIS_INCLUDE</code></pre>

<p><br />
3-添加宏</p>

<pre>
<code>LV_CONF_INCLUDE_SIMPLE=1</code></pre>

<p>&nbsp;</p>

<p>4-添加一个 littlevgl_support.c 文件<br />
添加显示刷新函数:</p>

<pre>
<code class="language-cpp">void lv_port_disp_init(void)
{
    /*-------------------------
   * Initialize your display
   * -----------------------*/
    tli_gpio_config();
    tli_config_twolayout();
    tli_layer_enable(LAYER0);
    tli_reload_config(TLI_REQUEST_RELOAD_EN);
    tli_enable();

    /*-----------------------------------
   * Register the display in LittlevGL
   *----------------------------------*/

    lv_disp_drv_t disp_drv;      /*Descriptor of a display driver*/
    lv_disp_drv_init(&amp;disp_drv); /*Basic initialization*/

    /*Set up the functions to access to your display*/
    static lv_disp_buf_t disp_buf_2;
    lv_disp_buf_init(&amp;disp_buf_2, buf2_1, buf2_2, LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL);   /*Initialize the display buffer*/
    disp_drv.buffer = &amp;disp_buf_2;

    disp_drv.hor_res = LCD_WIDTH;
    disp_drv.ver_res = LCD_HEIGHT;

    /*Used in buffered mode (LV_VDB_SIZE != 0in lv_conf.h)*/
    disp_drv.flush_cb = DEMO_FlushDisplay;

    /*Finally register the driver*/
    lv_disp_drv_register(&amp;disp_drv);
}</code></pre>

<p>&nbsp;</p>

<p><span style="font-size:24px;"><span style="font-family:楷体;">(三)平台驱动移植</span></span><br />
1-缓存设置<br />
Littlevgl一般设置双缓冲,按照480*272*4=510K这个大小来说,本芯片IK结尾,只有256K SRAM肯定是不够了,如果是II结尾的那颗或许还可以。<br />
如果按照256K理论上也可以设置双240*240*2大小的缓冲区,但这256K实际上是分片的。<br />
</p>

<p>keil上面也是默认分成两个区</p>

<p><br />
经过试验,单个缓冲似乎不能跨区,粗略测试过后,最大的缓冲区就是270*180*2*2了。<br />
因此在 littlevgl_support.h 文件中添加:</p>

<pre>
<code>#define LCD_WIDTH 270
#define LCD_HEIGHT 180
#define LCD_FB_BYTE_PER_PIXEL 1</code></pre>

<p>在 littlevgl_support.c 文件中添加全局变量:</p>

<pre>
<code>static lv_color_t buf2_1; /*A buffer for 10 rows*/
static lv_color_t buf2_2; /*An other buffer for 10 rows*/</code></pre>

<p>如果是用memset方式在开辟,则需要设置LCD_WIDTH * LCD_HEIGHT * LCD_FB_BYTE_PER_PIXEL*2大小,因为现在使用的是RGB565格式,每位需要16位2字节空间。</p>

<p>&nbsp;</p>

<p>2-初始化<br />
gpio的初始化见上一贴:<a href="https://bbs.eeworld.com.cn/thread-1140937-1-1.html" target="_blank"> 【GD32450I-EVAL】TLI-RGB屏幕驱动初探</a></p>

<p>tli的初始化需要根据缓冲区做一点改变,刚好设置为 LCD_WIDTH * LCD_HEIGHT 的显示范围:</p>

<pre>
<code class="language-cpp">static void tli_config_twolayout(void)
{
    tli_parameter_struct               tli_init_struct;
    tli_layer_parameter_struct         tli_layer_init_struct;

    rcu_periph_clock_enable(RCU_TLI);
    tli_gpio_config();
    /* configure PLLSAI to generate TLI clock */
    if(ERROR == rcu_pllsai_config(192, 2, 3)){
      while(1);
    }
    rcu_tli_clock_div_config(RCU_PLLSAIR_DIV8);
   
    rcu_osci_on(RCU_PLLSAI_CK);
   
    if(ERROR == rcu_osci_stab_wait(RCU_PLLSAI_CK)){
      while(1);
    }

    /* configure TLI parameter struct */
    tli_init_struct.signalpolarity_hs = TLI_HSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_vs = TLI_VSYN_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_de = TLI_DE_ACTLIVE_LOW;
    tli_init_struct.signalpolarity_pixelck = TLI_PIXEL_CLOCK_TLI;
    /* LCD display timing configuration */
    tli_init_struct.synpsz_hpsz = HORIZONTAL_SYNCHRONOUS_PULSE - 1;
    tli_init_struct.synpsz_vpsz = VERTICAL_SYNCHRONOUS_PULSE - 1;
    tli_init_struct.backpsz_hbpsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1;
    tli_init_struct.backpsz_vbpsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1;
    tli_init_struct.activesz_hasz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH - 1;
    tli_init_struct.activesz_vasz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT - 1;
    tli_init_struct.totalsz_htsz = HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH + ACTIVE_WIDTH + HORIZONTAL_FRONT_PORCH - 1;
    tli_init_struct.totalsz_vtsz = VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH + ACTIVE_HEIGHT + VERTICAL_FRONT_PORCH - 1;
    /* configure LCD background R,G,B values */
    tli_init_struct.backcolor_red = 0xFF;
    tli_init_struct.backcolor_green = 0xFF;
    tli_init_struct.backcolor_blue = 0xFF;
    tli_init(&amp;tli_init_struct);

    /* TLI layer1 configuration */
    /* TLI window size configuration */
    tli_layer_init_struct.layer_window_leftpos = 20 + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH;
    tli_layer_init_struct.layer_window_rightpos = (20 + LCD_WIDTH + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
    tli_layer_init_struct.layer_window_toppos = 30 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
    tli_layer_init_struct.layer_window_bottompos = (30 + LCD_HEIGHT + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
    /* TLI window pixel format configuration */
    tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
    /* TLI window specified alpha configuration */
    tli_layer_init_struct.layer_sa = 255;
    /* TLI window blend configuration */
    tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
    tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
    /* TLI layer default alpha R,G,B value configuration */
    tli_layer_init_struct.layer_default_alpha = 0;
    tli_layer_init_struct.layer_default_blue = 0xFF;
    tli_layer_init_struct.layer_default_green = 0xFF;
    tli_layer_init_struct.layer_default_red = 0xFF;
    /* TLI layer frame buffer base address configuration */
    tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)buf2_1;
    tli_layer_init_struct.layer_frame_line_length = ((LCD_WIDTH * 2) + 3);
    tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_WIDTH * 2);
    tli_layer_init_struct.layer_frame_total_line_number = LCD_HEIGHT;
    tli_layer_init(LAYER1, &amp;tli_layer_init_struct);

    /* TLI layer0 configuration */
    tli_layer_init_struct.layer_window_leftpos = 20 + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH;
    tli_layer_init_struct.layer_window_rightpos = (20 + LCD_WIDTH + HORIZONTAL_SYNCHRONOUS_PULSE + HORIZONTAL_BACK_PORCH - 1);
    tli_layer_init_struct.layer_window_toppos = 40 + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH;
    tli_layer_init_struct.layer_window_bottompos = (40 + LCD_HEIGHT + VERTICAL_SYNCHRONOUS_PULSE + VERTICAL_BACK_PORCH - 1);
    tli_layer_init_struct.layer_ppf = LAYER_PPF_RGB565;
    /* TLI window specified alpha configuration */
    tli_layer_init_struct.layer_sa = 255;
    /* TLI layer default alpha R,G,B value configuration */
    tli_layer_init_struct.layer_default_blue = 0xFF;
    tli_layer_init_struct.layer_default_green = 0xFF;
    tli_layer_init_struct.layer_default_red = 0xFF;
    tli_layer_init_struct.layer_default_alpha = 0xFF;
    /* TLI window blend configuration */
    tli_layer_init_struct.layer_acf1 = LAYER_ACF1_PASA;
    tli_layer_init_struct.layer_acf2 = LAYER_ACF2_PASA;
    /* TLI layer frame buffer base address configuration */
    tli_layer_init_struct.layer_frame_bufaddr = (uint32_t)buf2_2;
    tli_layer_init_struct.layer_frame_line_length = ((LCD_WIDTH * 2) + 3);
    tli_layer_init_struct.layer_frame_buf_stride_offset = (LCD_WIDTH * 2);
    tli_layer_init_struct.layer_frame_total_line_number = LCD_HEIGHT;
    tli_layer_init(LAYER0, &amp;tli_layer_init_struct);
    tli_dither_config(TLI_DITHER_ENABLE);
}</code></pre>

<p>3-每帧回调刷新,通过显示不同的层实现</p>

<pre>
<code>static void DEMO_FlushDisplay(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    if (color_p == buf2_1)
    {
      tli_layer_disable(LAYER0);
      tli_layer_enable(LAYER1);
      tli_reload_config(TLI_REQUEST_RELOAD_EN);
    }
    else if (color_p == buf2_2)
    {
      tli_layer_disable(LAYER1);
      tli_layer_enable(LAYER0);
      tli_reload_config(TLI_REQUEST_RELOAD_EN);
    }

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

<p>&nbsp;</p>

<p>4-显示的图像,创建一个居中的进度条</p>

<pre>
<code>//例程入口
void lvAdd_sliderTest()
{
    lv_obj_t * slider = lv_slider_create(lv_scr_act(), NULL);
    lv_obj_set_width(slider, LV_DPI*1.5);
    lv_obj_align(slider, NULL, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_event_cb(slider, cb_sliderTest);
    lv_slider_set_range(slider, 0, 50);
    // Create a label below the slider
    slider_label = lv_label_create(lv_scr_act(), NULL);
    lv_label_set_text(slider_label, "0");
    lv_obj_set_auto_realign(slider_label, true);
    lv_obj_align(slider_label, slider, LV_ALIGN_OUT_BOTTOM_MID, 0, 0);

}</code></pre>

<p><br />
5-主函数调用</p>

<pre>
<code>int main(void)
{
static __IO uint32_t timingdelaylocal = 0U;
    gd_eval_led_init(LED1);
    systick_config();
    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();
    lvAdd_sliderTest();

    while(1){
        delay_ms(10);
        lv_tick_inc(10);
    lv_task_handler();
    }
}</code></pre>

<p>&nbsp;</p>

<p><span style="font-size:24px;"><span style="font-family:楷体;">(四)堆栈问题</span></span><br />
第一次编译通过之后,在板子上运行始终卡死在初始化阶段,具体是卡死在theme的初始化配置上:</p>

<pre>
<code class="language-cpp">lv_theme_t * th = LV_THEME_DEFAULT_INIT(LV_THEME_DEFAULT_COLOR_PRIMARY, LV_THEME_DEFAULT_COLOR_SECONDARY,
                                            LV_THEME_DEFAULT_FLAG,
                                            LV_THEME_DEFAULT_FONT_SMALL, LV_THEME_DEFAULT_FONT_NORMAL, LV_THEME_DEFAULT_FONT_SUBTITLE, LV_THEME_DEFAULT_FONT_TITLE);</code></pre>

<p>这个问题困扰了很久没能解决,非常郁闷,之前也曾经成功地移植过LittleVGL到其他平台,但都是基于eclipse的开发环境,很多配置并不相同,在修改过编译选项无果后,偶然之下发现LittleVGL 的主页有这么一点:<br />
<br />
仔细一看,startup文件下默认配置的栈堆都只有0x400,都改成0x2000后就能成功运行了。</p>

<p>&nbsp;</p>

<p><span style="font-size:24px;"><span style="font-family:楷体;">(五)效果</span></span></p>

<p>现在显示出来了,后面移植触摸驱动</p>

<p>&nbsp;</p>

<p>工程:</p>

okhxyyo 发表于 2020-9-14 11:35

<p><a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html" target="_blank">兆易GD32450I-EVAL</a></p>

<p>汇总贴:<a href="https://bbs.eeworld.com.cn/thread-1140981-1-1.html">https://bbs.eeworld.com.cn/thread-1140981-1-1.html</a></p>

宋元浩 发表于 2020-9-14 09:35

<p>期待楼主后续评测</p>
页: [1]
查看完整版本: 【GD32450I-EVAL】LittleVGL显示部分移植