qinyunti 发表于 2022-12-6 17:24

【微雪RP2040双核开发板】LVGL移植

<div class='showpostmsg'><h1>&nbsp;</h1>

<h1><b>准备</b></h1>

<p >基于样例的c工程进行</p>

<p >git clone <a href="https://github.com/lvgl/lvgl.git"><u>https://github.com/lvgl/lvgl.git</u></a></p>

<p >下载代码到RP2040-LCD-1.28\c\lib目录下</p>

<p >&nbsp;</p>

<h1 ><b>移植过程</b></h1>

<h2 ><b>配置文件lv_conf.h</b></h2>

<p >复制</p>

<p >lvgl/lv_conf_template.h</p>

<p >改名字为lv_conf.h</p>

<p >将#if 0改为1,即直接#include &ldquo;lv_conf.h&rdquo;</p>

<p >将lv_conf.h添加到工程头文件包含路径。</p>

<h2 ><b>底层驱动模板</b></h2>

<p >lvgl/examples/porting</p>

<p >下有对应的模板文件,分别是显示,文件系统和输入设备的驱动模板。</p>

<p >lv_port_disp_template.c/h</p>

<p >lv_port_fs_template.c/h</p>

<p >lv_port_indev_template.c/h</p>

<p >暂时只移植显示,所以复制lv_port_disp_template.c/h到RP2040-LCD-1.28\c</p>

<p >改名字为</p>

<p >lv_port_disp.c/h</p>

<p >将.c和.h里面的#if 0改为1</p>

<p >.c中#include &quot;lv_port_disp_template.h&quot;改为#include &quot;lv_port_disp.h&quot;</p>

<h2 ><b>HAL层模板</b></h2>

<p >lvgl/src/hal</p>

<p >下</p>

<p >我们直接使用不修改</p>

<p >lv_hal_disp.c/h</p>

<p >lv_hal_indev.c/h</p>

<p >lv_hal_tick.c/h</p>

<p >lv_hal.h</p>

<h2 ><b>添加文件</b></h2>

<p >如下</p>

<p > &nbsp;</p>

<p >RP2040-LCD-1.28/c/CMakeLists.txt中</p>

<p >add_subdirectory(./lib/lvgl)</p>

<p >添加lvgl的源码</p>

<p >&nbsp;</p>

<p >然后调用RP2040-LCD-1.28/c/lib/lvgl/CMakeLists.txt</p>

<p >最终调用</p>

<p >RP2040-LCD-1.28/c/lib/lvgl/env_support/cmake/custom.cmake</p>

<h2 ><b>需要移植的代码</b></h2>

<p >移植比较简单,直接使用底层驱动模板根据实际实现修改lv_port_disp.c,并配置lv_conf.h即可。</p>

<h3 ><b>头文件包含模式</b></h3>

<p >需要在工程中定义宏</p>

<p >#define LV_LVGL_H_INCLUDE_SIMPLE 1</p>

<p >这样需要将lvgl.h所在路径配置为工程头文件包含路径。</p>

<p >代码中直接#include &quot;lvgl.h&quot;即可</p>

<p >否则是#include &quot;../../lvgl.h&quot;</p>

<p >&nbsp;</p>

<p >lib/lvgl/env_support/cmake/custom.cmake中</p>

<p >该宏默认是使能的</p>

<p >option(LV_LVGL_H_INCLUDE_SIMPLE</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&quot;Use #include \&quot;lvgl.h\&quot; instead of #include \&quot;../../lvgl.h\&quot;&quot; ON)</p>

<h3 ><b>分辨率配置</b></h3>

<p >lv_port_disp.h中</p>

<p >#define MY_DISP_HOR_RES 240</p>

<p >#define MY_DISP_VER_RES 240</p>

<h3 ><b>初始化</b></h3>

<p >lv_port_disp.c中</p>

<p >实现disp_init</p>

<p >即调用自己的初始化函数,如果在其他地方初始化了,该函数实现为空函数体即可。</p>

<h3 ><b>缓冲区</b></h3>

<p >lv_port_disp.c中</p>

<p >lv_port_disp_init</p>

<p >注释掉/* Example for 2) */</p>

<p >/* Example for 3) also set disp_drv.full_refresh = 1 below*/对应的代码</p>

<p >使用/* Example for 1) */</p>

<p >lv_port_disp_init</p>

<p >该函数调用disp_init实现初始化</p>

<h3 ><b>刷新显示</b></h3>

<p >lv_port_disp.c中包含#include &quot;LCD_1in28.h&quot;</p>

<p >disp_flush</p>

<p >/*put_px(x, y,&nbsp;color_p)/改为</p>

<p >lcd_draw_point(x,y,color_p-&gt;full);</p>

<p >并调用LcdPush();</p>

<p >&nbsp;</p>

<div class="parsedown-markdown">
<p>static&nbsp;void&nbsp;disp_flush(lv_disp_drv_t&nbsp;* disp_drv, const&nbsp;lv_area_t&nbsp;* area, lv_color_t&nbsp;* color_p)</p>

<p >{</p>

<p >&nbsp; &nbsp; if(disp_flush_enabled) {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/</p>

<p >&nbsp;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; volatile&nbsp;int32_t&nbsp;x;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; volatile&nbsp;int32_t&nbsp;y;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; printf(&quot;%d&nbsp;%d&nbsp;%d&nbsp;%d\r\n&quot;,area-&gt;y1,area-&gt;y2,area-&gt;x1,area-&gt;x2);</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; for(y&nbsp;= area-&gt;y1; y&nbsp;&lt;= area-&gt;y2; y++) {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; for(x&nbsp;= area-&gt;x1; x&nbsp;&lt;= area-&gt;x2; x++) {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*Put a pixel to the display. For example:*/</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*put_px(x, y, *color_p)*/</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; lcd_draw_point(x,y,color_p-&gt;full);</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; color_p++;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; }</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; LcdPush();</p>

<p >&nbsp; &nbsp; }</p>

<p >&nbsp;</p>

<p >&nbsp; &nbsp; /*IMPORTANT!!!</p>

<p >&nbsp; &nbsp; &nbsp;*Inform the graphics library that you are ready with the flushing*/</p>

<p >&nbsp; &nbsp; lv_disp_flush_ready(disp_drv);</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<p >lib/LCD/LCD_1in28.c中实现</p>

<div class="parsedown-markdown">
<p>static&nbsp;uint16_t&nbsp;s_disbuffer_au16;</p>

<p >&nbsp;</p>

<p >void&nbsp;lcd_draw_point(int32_t&nbsp;x,int32_t&nbsp;y,uint16_t&nbsp;color)</p>

<p >{</p>

<p >&nbsp; &nbsp; s_disbuffer_au16 = (color&gt;&gt;8) | ((color&amp;0xFF)&lt;&lt;8);</p>

<p >}</p>

<p >&nbsp;</p>

<p >void&nbsp;LcdPush(void)</p>

<p >{</p>

<p >&nbsp; &nbsp; LCD_1IN28_SetWindows(0, 0, LCD_1IN28_WIDTH, LCD_1IN28_HEIGHT);</p>

<p >&nbsp; &nbsp; DEV_Digital_Write(LCD_DC_PIN, 1);</p>

<p >&nbsp; &nbsp; spi_write_blocking(SPI_PORT, (uint8_t&nbsp;*)&amp;s_disbuffer_au16, LCD_1IN28_HEIGHT*LCD_1IN28_WIDTH*2);</p>

<p >&nbsp; &nbsp; ///UWORD j;</p>

<p >&nbsp; &nbsp; ///for(j = 0; j &lt; LCD_1IN28_HEIGHT; j++){</p>

<p >&nbsp; &nbsp; // &nbsp; &nbsp;DEV_SPI_Write_nByte((uint8_t *)&amp;s_disbuffer_au16, LCD_1IN28_WIDTH*2);</p>

<p >&nbsp; &nbsp; //}</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >lib/LCD/LCD_1in28.h中申明</p>

<div class="parsedown-markdown">
<p>void&nbsp;lcd_draw_point(int32_t&nbsp;x,int32_t&nbsp;y,uint16_t&nbsp;color);</p>

<p >void&nbsp;LcdPush(void);</p>
</div>

<p >&nbsp;</p>

<h3 ><b>颜色深度</b></h3>

<p >lv_conf.h中</p>

<p >#define LV_COLOR_DEPTH 16</p>

<p >typedef LV_CONCAT3(lv_color, LV_COLOR_DEPTH, _t) lv_color_t;</p>

<p >则lv_color_t类型为lv_color_16_t</p>

<p>&nbsp;</p>

<p>&nbsp;</p>

<h3 ><b>堆大小配置</b></h3>

<p >lv_conf.h中</p>

<p >#define LV_MEM_SIZE (10U * 1024U) /* &nbsp;*/</p>

<p >按需提供堆大小,过大可能编译不过,过小可能影响创建对象。</p>

<h3 ><b>时间滴答</b></h3>

<p >如果在lv_conf.h中指定LV_TICK_CUSTOM为1则需要用户提供相关接口</p>

<p >LV_TICK_CUSTOM_SYS_TIME_EXPR用于获取当前毫秒值</p>

<p >和头文件LV_TICK_CUSTOM_INCLUDE</p>

<p >&nbsp;</p>

<p >否则使用lvgl/src/hal/lv_hal_tick.c的实现</p>

<p >每隔x毫秒调用lv_tick_inc(x),使用内部计数器定时。</p>

<p >周期调用lv_tick_inc更新时间滴答,比如专门在某个定时线程中</p>

<div class="parsedown-markdown">
<p>bool timer_callback(repeating_timer_t *rt)</p>

<p >{</p>

<p >&nbsp;&nbsp;&nbsp;&nbsp;lv_tick_inc(5);</p>

<p >}</p>

<p >add_repeating_timer_ms(5,timer_callback,0,0);</p>
</div>

<p >&nbsp;</p>

<h3 ><b>日志</b></h3>

<p >lv_conf.h中</p>

<p >#define LV_USE_LOG 0</p>

<p >改为</p>

<p >#define LV_USE_LOG 1使能日志</p>

<p >#define LV_LOG_LEVEL LV_LOG_LEVEL_TRACE设置日志,等级</p>

<p >LV_LOG_LEVEL_TRACE表示所有信息都打印</p>

<p >如果#define LV_LOG_PRINTF 0</p>

<p >则需要调用设置lv_log_register_print_cb()打印函数</p>

<p >否则使用printf</p>

<p >#define LV_LOG_USE_TIMESTAMP 1</p>

<p >使能打印时间</p>

<p >其他的模块打印使能</p>

<h3 ><b>字体配置</b></h3>

<p >lv_conf.h</p>

<p >中按需使能对应的字体,如果有对应编译错误信息根据提示使能</p>

<p >#define LV_FONT_MONTSERRAT_12 1</p>

<p >#define LV_FONT_MONTSERRAT_14 1</p>

<p >#define LV_FONT_MONTSERRAT_16 1</p>

<h3 ><b>工程配置</b></h3>

<p >RP2040-LCD-1.28\c\CMakeLists.txt中</p>

<p >添加</p>

<div class="parsedown-markdown">
<p>add_subdirectory(./lib/lvgl)</p>

<p >include_directories(./lib/lvgl)</p>

<p >include_directories(.)</p>

<p >add_definitions(-DLV_LVGL_H_INCLUDE_SIMPLE=1)</p>

<p >add_executable(main</p>

<p >main.c lv_port_disp.c</p>

<p >)</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >target_link_libraries(main examples lvgl LCD QMI8658 GUI Fonts Config pico_stdlib hardware_spi hardware_i2c )</p>

<p >&nbsp;</p>

<p >examples/CMakeLists.txt中</p>

<p >include_directories(../lib/lvgl)</p>

<p >add_definitions(-DLV_LVGL_H_INCLUDE_SIMPLE=1)</p>
</div>

<p >&nbsp;</p>

<h2 ><b>测试</b></h2>

<p >创建一个按钮</p>

<p >examples/LCD_1in28_test.c</p>

<div class="parsedown-markdown">
<p>#include&nbsp;&quot;LCD_Test.h&quot;</p>

<p >#include&nbsp;&quot;LCD_1in28.h&quot;</p>

<p >#include&nbsp;&quot;QMI8658.h&quot;</p>

<p >#include&nbsp;&lt;stdio.h&gt;</p>

<p >#include&nbsp;&quot;pico/stdlib.h&quot;</p>

<p >#include&nbsp;&quot;hardware/adc.h&quot;</p>

<p >#include&nbsp;&quot;draw.h&quot;</p>

<p >#include&nbsp;&quot;lvgl.h&quot;</p>

<p >#include&nbsp;&quot;lv_port_disp.h&quot;</p>

<p >&nbsp;</p>

<p >bool&nbsp;timer_callback(repeating_timer_t&nbsp;*rt)</p>

<p >{</p>

<p >&nbsp; &nbsp; lv_tick_inc(5);</p>

<p >&nbsp; &nbsp; ///printf(&quot;%lld\r\n&quot;,time_us_64());</p>

<p >}</p>

<p >&nbsp;</p>

<p >static&nbsp;void&nbsp;btn_event_cb(lv_event_t&nbsp;* e)</p>

<p >{</p>

<p >&nbsp; &nbsp; lv_event_code_t&nbsp;code&nbsp;= lv_event_get_code(e);</p>

<p >&nbsp; &nbsp; lv_obj_t&nbsp;* btn&nbsp;= lv_event_get_target(e);</p>

<p >&nbsp; &nbsp; if(code&nbsp;== LV_EVENT_CLICKED) {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; static&nbsp;uint8_t&nbsp;cnt&nbsp;= 0;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; cnt++;</p>

<p >&nbsp;</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; /*Get the first child of the button which is the label and change its text*/</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; lv_obj_t&nbsp;* label&nbsp;= lv_obj_get_child(btn, 0);</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; lv_label_set_text_fmt(label, &quot;Button: %d&quot;, cnt);</p>

<p >&nbsp; &nbsp; }</p>

<p >}</p>

<p >&nbsp;</p>

<p >/**</p>

<p >&nbsp;* Create a button with a label and react on click event.</p>

<p >&nbsp;*/</p>

<p >void&nbsp;lv_example_get_started_1(void)</p>

<p >{</p>

<p >&nbsp; &nbsp; lv_color_t&nbsp;color;</p>

<p >&nbsp; &nbsp; color.full=(uint16_t)0xF800;</p>

<p >&nbsp; &nbsp; lv_obj_t&nbsp;* btn&nbsp;= lv_btn_create(lv_scr_act());&nbsp;&nbsp; &nbsp; /*Add a button the current screen*/</p>

<p >&nbsp; &nbsp; lv_obj_set_style_text_font(btn, &amp;lv_font_montserrat_24&nbsp;,0);</p>

<p >&nbsp; &nbsp; lv_obj_set_style_text_color(btn,color,0);</p>

<p >&nbsp; &nbsp; lv_obj_set_pos(btn, 120-100, 120-50);&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*Set its position*/</p>

<p >&nbsp; &nbsp; lv_obj_set_size(btn, 200, 100);&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*Set its size*/</p>

<p >&nbsp; &nbsp; lv_obj_add_event_cb(btn, btn_event_cb, LV_EVENT_ALL, NULL);&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*Assign a callback to the button*/</p>

<p >&nbsp;</p>

<p >&nbsp; &nbsp; lv_obj_t&nbsp;* label&nbsp;= lv_label_create(btn);&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;/*Add a label to the button*/</p>

<p >&nbsp; &nbsp; lv_label_set_text(label, &quot;Hello LVGL&quot;);&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; /*Set the labels text*/</p>

<p >&nbsp; &nbsp; lv_obj_center(label);</p>

<p >}</p>

<p >&nbsp;</p>

<p >struct&nbsp;repeating_timer&nbsp;timer;</p>

<p >&nbsp;</p>

<p >int&nbsp;LCD_1in28_test(void)</p>

<p >{</p>

<p >&nbsp; &nbsp; if&nbsp;(DEV_Module_Init() != 0)</p>

<p >&nbsp; &nbsp; {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; return&nbsp;-1;</p>

<p >&nbsp; &nbsp; }</p>

<p >&nbsp; &nbsp; adc_init();</p>

<p >&nbsp; &nbsp; adc_gpio_init(29);</p>

<p >&nbsp; &nbsp; adc_select_input(3);</p>

<p >&nbsp; &nbsp; LCD_1IN28_Init(VERTICAL);</p>

<p >&nbsp; &nbsp; LCD_1IN28_Clear(WHITE);</p>

<p >&nbsp; &nbsp; DEV_SET_PWM(60);</p>

<p >&nbsp; &nbsp; add_repeating_timer_ms(5,timer_callback,0,&amp;timer);</p>

<p >&nbsp; &nbsp; ///sleep_ms(5000);</p>

<p >&nbsp; &nbsp; lv_init();</p>

<p >&nbsp; &nbsp; lv_port_disp_init();</p>

<p >&nbsp; &nbsp; lv_example_get_started_1();</p>

<p >&nbsp; &nbsp; while(1)</p>

<p >&nbsp; &nbsp; {</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; sleep_us(1000);&nbsp;/* 必须又sleep 否则定时器不会回调 */</p>

<p >&nbsp; &nbsp; &nbsp; &nbsp; lv_task_handler(); &nbsp;</p>

<p >&nbsp; &nbsp; }</p>

<p >&nbsp;</p>

<p >&nbsp; &nbsp; DEV_Module_Exit();</p>

<p >&nbsp; &nbsp; return&nbsp;0;</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<p >效果如下</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<h3 ><b>镜像处理</b></h3>

<p >写点函数交换xy即可</p>

<p >&nbsp;</p>

<h3 ><b>颜色处理</b></h3>

<p >16位565模式,高低字节需要交换下</p>

<div class="parsedown-markdown">
<p>void&nbsp;lcd_draw_point(int32_t&nbsp;x,int32_t&nbsp;y,uint16_t&nbsp;color)</p>

<p >{</p>

<p >&nbsp; &nbsp; s_disbuffer_au16 = (color&gt;&gt;8) | ((color&amp;0xFF)&lt;&lt;8);</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<h2 ><b>编译</b></h2>

<p >cd build</p>

<p >export PICO_SDK_PATH=&quot;/home/lhj/pico-setup/pico/pico-sdk&quot; &amp;&amp; cmake ..</p>

<p >Make</p>

<p >不编译demo,example</p>

<p >export PICO_SDK_PATH=&quot;/home/lhj/pico-setup/pico/pico-sdk&quot; &amp;&amp; cmake -DLV_CONF_BUILD_DISABLE_EXAMPLES=1 -DLV_CONF_BUILD_DISABLE_DEMOS=1 ..</p>

<p >&nbsp;</p>

<h2 ><b>坑</b></h2>

<p >不要用dor循环书信屏幕,直接调用spi_write_blocking</p>

<p >for循环编译器会优化导致逻辑错误,可能并不会按照循环次数循环,而是卡死。</p>

<div class="parsedown-markdown">
<p>void&nbsp;LcdPush(void)</p>

<p >{</p>

<p >&nbsp; &nbsp; LCD_1IN28_SetWindows(0, 0, LCD_1IN28_WIDTH, LCD_1IN28_HEIGHT);</p>

<p >&nbsp; &nbsp; DEV_Digital_Write(LCD_DC_PIN, 1);</p>

<p >&nbsp; &nbsp; spi_write_blocking(SPI_PORT, (uint8_t&nbsp;*)&amp;s_disbuffer_au16, LCD_1IN28_HEIGHT*LCD_1IN28_WIDTH*2);</p>

<p >&nbsp; &nbsp; ///UWORD j;</p>

<p >&nbsp; &nbsp; ///for(j = 0; j &lt; LCD_1IN28_HEIGHT; j++){</p>

<p >&nbsp; &nbsp; // &nbsp; &nbsp;DEV_SPI_Write_nByte((uint8_t *)&amp;s_disbuffer_au16, LCD_1IN28_WIDTH*2);</p>

<p >&nbsp; &nbsp; //}</p>

<p >}</p>
</div>

<p >&nbsp;</p>

<p >&nbsp;</p>

<h1 ><b>参考</b></h1>

<p ><a href="https://lvgl.io/"><u>https://lvgl.io/</u></a></p>

<p >&nbsp;</p>

<p >&nbsp;</p>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>

qinyunti 发表于 2022-12-6 17:26

<p>源码</p>

<div></div>

lugl4313820 发表于 2022-12-9 22:47

<p>感谢分享。</p>

zhuangzard 发表于 2023-1-24 22:05

<p>谢谢大佬的教程!</p>

young1234566 发表于 2023-9-9 14:49

<p>mark</p>
页: [1]
查看完整版本: 【微雪RP2040双核开发板】LVGL移植