Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
LittlevGL_LVGL
N9H30开发板RTThread框架下的LVGL例程代码分析——LVGL初始化
发布于 2022-06-19 19:34:31 浏览:1489
订阅该版
上篇文章,在N9H30开发板上跑了一下RTThread下的LVGL例程。接下来慢慢分析一下例程代码。熟悉一下整个框架,从例程为突破口逐步去学习LVGL。 对于LVGL的底层适配,RTThread官方已经给搭建好了。先看最开始对LVGL的初始化,代码在“packages/LVGL-v8.2.0/env_support/rt-thread/lv_rt_thread_port.c”内。`lv_port_init()`函数已经加到了自动运行里面,无需用户调用。如下: ```c static int lv_port_init(void) { #if LV_USE_LOG lv_log_register_print_cb(lv_rt_log); #endif lv_init(); //初始化LVGL基础部件 lv_port_disp_init(); //初始化显示器设备接口 lv_port_indev_init(); //初始化输入设备接口(触摸屏) return 0; } INIT_COMPONENT_EXPORT(lv_port_init); ``` 其中lv_init()代码如下,主要初始化了一些链表。每种链表的功能后面用到了再慢慢分析。这里绕了半天的是对这些链表的声明。 ```c void lv_init(void) { /*Do nothing if already initialized*/ if(lv_initialized) { LV_LOG_WARN("lv_init: already inited"); return; } LV_LOG_INFO("begin"); /*Initialize the misc modules*/ lv_mem_init(); //RTThread提供了内存管理,所以这里没有做任何操作 _lv_timer_core_init(); //初始化LVGL相关的软件定时器,初始化定时器链表,开启定时器 _lv_fs_init(); //初始化LVGL实现的顶层文件系统(加入了文件缓存),初始化文件系统链表 _lv_anim_core_init(); //初始化动画相关代码,初始化动画链表,创建并关闭动画定时器 _lv_group_init(); //group初始化,初始化group的链表 lv_draw_init(); //绘图初始化,这里什么都没做 #if LV_USE_GPU_STM32_DMA2D /*Initialize DMA2D GPU*/ lv_draw_stm32_dma2d_init(); #endif #if LV_USE_GPU_NXP_PXP && LV_USE_GPU_NXP_PXP_AUTO_INIT if(lv_gpu_nxp_pxp_init(&pxp_default_cfg) != LV_RES_OK) { LV_LOG_ERROR("PXP init error. STOP.\n"); for(; ;) ; } #endif _lv_obj_style_init(); //初始化style链表 _lv_ll_init(&LV_GC_ROOT(_lv_disp_ll), sizeof(lv_disp_t)); //初始化disp链表 _lv_ll_init(&LV_GC_ROOT(_lv_indev_ll), sizeof(lv_indev_t)); //初始化indev链表 /*Initialize the screen refresh system*/ _lv_refr_init(); _lv_img_decoder_init(); #if LV_IMG_CACHE_DEF_SIZE lv_img_cache_set_size(LV_IMG_CACHE_DEF_SIZE); #endif /*Test if the IDE has UTF-8 encoding*/ char * txt = "脕"; uint8_t * txt_u8 = (uint8_t *)txt; if(txt_u8[0] != 0xc3 || txt_u8[1] != 0x81 || txt_u8[2] != 0x00) { LV_LOG_WARN("The strings have no UTF-8 encoding. Non-ASCII characters won't be displayed."); } uint32_t endianess_test = 0x11223344; uint8_t * endianess_test_p = (uint8_t *) &endianess_test; bool big_endian = endianess_test_p[0] == 0x11 ? true : false; if(big_endian) { LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 1, "It's a big endian system but LV_BIG_ENDIAN_SYSTEM is not enabled in lv_conf.h"); } else { LV_ASSERT_MSG(LV_BIG_ENDIAN_SYSTEM == 0, "It's a little endian system but LV_BIG_ENDIAN_SYSTEM is enabled in lv_conf.h"); } #if LV_USE_ASSERT_MEM_INTEGRITY LV_LOG_WARN("Memory integrity checks are enabled via LV_USE_ASSERT_MEM_INTEGRITY which makes LVGL much slower"); #endif #if LV_USE_ASSERT_OBJ LV_LOG_WARN("Object sanity checks are enabled via LV_USE_ASSERT_OBJ which makes LVGL much slower"); #endif #if LV_USE_ASSERT_STYLE LV_LOG_WARN("Style sanity checks are enabled that uses more RAM"); #endif #if LV_LOG_LEVEL == LV_LOG_LEVEL_TRACE LV_LOG_WARN("Log level is set to 'Trace' which makes LVGL much slower"); #endif lv_extra_init(); lv_initialized = true; LV_LOG_TRACE("finished"); } ``` 下面以_lv_timer_core_init()函数为例,分析一下对于链表_lv_timer_ll的声明和初始化,其他基本类似。 ```C void _lv_timer_core_init(void) { _lv_ll_init(&LV_GC_ROOT(_lv_timer_ll), sizeof(lv_timer_t)); /*Initially enable the lv_timer handling*/ lv_timer_enable(true); } ``` 其中LV_GC_ROOT的定义为`#define LV_GC_ROOT(x) x`,所以`_lv_ll_init`函数的调用展开为`_lv_ll_init(&_lv_timer_ll, sizeof(lv_timer_t));` `_lv_ll_init`函数的代码如下,很好理解,清空了链表,初始化了节点大小。这里最主要的是不知道传入的`_lv_timer_ll`链表头在哪定义的。比较绕,看了半天才看明白。 ```c void _lv_ll_init(lv_ll_t * ll_p, uint32_t node_size) { ll_p->head = NULL; ll_p->tail = NULL; #ifdef LV_ARCH_64 /*Round the size up to 8*/ node_size = (node_size + 7) & (~0x7); #else /*Round the size up to 4*/ node_size = (node_size + 3) & (~0x3); #endif ll_p->n_size = node_size; } ``` 右键`_lv_timer_ll`变量,跳转声明,会调到下面代码。此代码在“lv_gc.c”内。LV_ROOTS追踪到底,就是对这些链表变量的声明。 ```C #if(!defined(LV_ENABLE_GC)) || LV_ENABLE_GC == 0 LV_ROOTS #endif /*LV_ENABLE_GC*/ ``` 如下所示,是相关的宏定义。 ```c #define LV_DISPATCH(f, t, n) f(t, n) #define LV_ITERATE_ROOTS(f) \ LV_DISPATCH(f, lv_ll_t, _lv_timer_ll) /*Linked list to store the lv_timers*/ \ LV_DISPATCH(f, lv_ll_t, _lv_disp_ll) /*Linked list of display device*/ \ LV_DISPATCH(f, lv_ll_t, _lv_indev_ll) /*Linked list of input device*/ \ LV_DISPATCH(f, lv_ll_t, _lv_fsdrv_ll) \ LV_DISPATCH(f, lv_ll_t, _lv_anim_ll) \ LV_DISPATCH(f, lv_ll_t, _lv_group_ll) \ LV_DISPATCH(f, lv_ll_t, _lv_img_decoder_ll) \ LV_DISPATCH(f, lv_ll_t, _lv_obj_style_trans_ll) \ LV_DISPATCH(f, lv_layout_dsc_t *, _lv_layout_list) \ LV_DISPATCH_COND(f, _lv_img_cache_entry_t*, _lv_img_cache_array, LV_IMG_CACHE_DEF, 1) \ LV_DISPATCH_COND(f, _lv_img_cache_entry_t, _lv_img_cache_single, LV_IMG_CACHE_DEF, 0) \ LV_DISPATCH(f, lv_timer_t*, _lv_timer_act) \ LV_DISPATCH(f, lv_mem_buf_arr_t , lv_mem_buf) \ LV_DISPATCH_COND(f, _lv_draw_mask_radius_circle_dsc_arr_t , _lv_circle_cache, LV_DRAW_COMPLEX, 1) \ LV_DISPATCH_COND(f, _lv_draw_mask_saved_arr_t , _lv_draw_mask_list, LV_DRAW_COMPLEX, 1) \ LV_DISPATCH(f, void * , _lv_theme_default_styles) \ LV_DISPATCH(f, void * , _lv_theme_basic_styles) \ LV_DISPATCH_COND(f, uint8_t *, _lv_font_decompr_buf, LV_USE_FONT_COMPRESSED, 1) \ LV_DISPATCH(f, uint8_t * , _lv_grad_cache_mem) #define LV_DEFINE_ROOT(root_type, root_name) root_type root_name; #define LV_ROOTS LV_ITERATE_ROOTS(LV_DEFINE_ROOT) ``` 上面宏定义乍看比较绕,看明白了其实也很简单,展开后就是如下的变量声明.个人没太想明白这样写除了让第一眼见到此代码的人有点难懂外,有哪些好处😜。 ```C lv_ll_t _lv_timer_ll; lv_ll_t _lv_disp_ll; lv_ll_t _lv_indev_ll; lv_ll_t _lv_fsdrv_ll; lv_ll_t _lv_anim_ll; lv_ll_t _lv_group_ll; ... ``` `lv_port_disp_init()`函数体如下。主要就是在RTThread的LCD驱动上层,实现了一层display设备。实现了disp设备的初始化和注册。 ```c void lv_port_disp_init(void) { rt_err_t result; void *buf1 = RT_NULL; void *buf2 = RT_NULL; uint32_t u32FBSize; lcd_device = rt_device_find("lcd"); if (lcd_device == 0) { LOG_E("error!"); return; } /* get framebuffer address */ result = rt_device_control(lcd_device, RTGRAPHIC_CTRL_GET_INFO, &info); if (result != RT_EOK) { LOG_E("error!"); /* get device information failed */ return; } /* Disable backlight at startup. */ rt_device_control(lcd_device, RTGRAPHIC_CTRL_POWEROFF, RT_NULL); RT_ASSERT(info.bits_per_pixel == 8 || info.bits_per_pixel == 16 || info.bits_per_pixel == 24 || info.bits_per_pixel == 32); lv_disp_drv_init(&disp_drv); /*Basic initialization*/ /*Set the resolution of the display*/ disp_drv.hor_res = info.width; disp_drv.ver_res = info.height; u32FBSize = info.height * info.width * (info.bits_per_pixel / 8); #if (LV_USE_ANTI_TEARING==1) disp_drv.full_refresh = 1; #endif if (disp_drv.full_refresh) { #if (LV_USE_GPU_N9H30_GE2D==1) buf1 = (void *)info.framebuffer; // Use Non-cacheable VRAM #else buf1 = (void *)((uint32_t)info.framebuffer & ~BIT31); // Use Cacheable VRAM #endif buf2 = (void *)((uint32_t)buf1 + u32FBSize); buf3_next = (void *)((uint32_t)buf2 + u32FBSize); rt_kprintf("LVGL: Use triple screen-sized buffers(full_refresh) - buf1@%08x, buf2@%08x, buf3_next@%08x\n", buf1, buf2, buf3_next); disp_drv.flush_cb = nu_flush_full_refresh; } else { buf1 = (void *)(((uint32_t)info.framebuffer) + u32FBSize); buf2 = (void *)((uint32_t)buf1 + u32FBSize); rt_kprintf("LVGL: Use two screen-sized buffers - buf1@%08x, buf2@%08x\n", buf1, buf2); rt_device_control(lcd_device, RTGRAPHIC_CTRL_PAN_DISPLAY, info.framebuffer); disp_drv.flush_cb = nu_flush; } /*Initialize `disp_buf` with the buffer(s).*/ lv_disp_draw_buf_init(&disp_buf, buf1, buf2, info.width * info.height); result = rt_device_open(lcd_device, 0); if (result != RT_EOK) { LOG_E("error!"); return; } /*Set a display buffer*/ disp_drv.draw_buf = &disp_buf; #if LV_VERSION_EQUAL(8, 1, 0) /*Fill a memory with a color (GPU only)*/ disp_drv.gpu_fill_cb = nu_fill_cb; #endif #if (LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)) disp_drv.draw_ctx_init = lv_draw_n9h30_ge2d_ctx_init; disp_drv.draw_ctx_deinit = lv_draw_n9h30_ge2d_ctx_init; disp_drv.draw_ctx_size = sizeof(lv_draw_n9h30_ge2d_ctx_t); #endif /*Called after every refresh cycle to tell the rendering and flushing time + the number of flushed pixels*/ //disp_drv.monitor_cb = nu_perf_monitor; /*Finally register the driver*/ lv_disp_drv_register(&disp_drv); } ``` 这里顺便说一下RTThread系统的LCD驱动代码在“libraries/n9h30/rtt_port/drv_vpost.c”内。初始化代码如下,主要初始化和注册了LCD设备。另外,framebuffer的空间也是在这里开辟的。开辟了3倍或者说3块显存。应该是用于加速刷新率或者分层显示用的。具体后面边学边分析。这里暂不深究。此代码也被加入到了自动调用里,无需用户调用。 ```C int rt_hw_vpost_init(void) { int i = -1; rt_err_t ret; VPOST_T *psVpostLcmInst = vpostLCMGetInstance(VPOST_USING_LCD_IDX); RT_ASSERT(psVpostLcmInst != RT_NULL); if ((psVpostLcmInst->u32DevWidth * psVpostLcmInst->u32DevHeight) > (480 * 272)) { /* LCD clock is selected from UPLL and divide to 20MHz */ outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE18); /* LCD clock is selected from UPLL and divide to 30MHz */ //outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0x918); } else { /* LCD clock is selected from UPLL and divide to 10MHz */ outpw(REG_CLK_DIVCTL1, (inpw(REG_CLK_DIVCTL1) & ~0xff1f) | 0xE19); } /* Initial LCM */ vpostLCMInit(VPOST_USING_LCD_IDX); /* Set scale to 1:1 */ vpostVAScalingCtrl(1, 0, 1, 0, VA_SCALE_INTERPOLATION); for (i = eVpost_LCD; i < eVpost_Cnt; i++) { nu_vpost_t psVpost = &nu_fbdev[i]; rt_memset((void *)&psVpost->info, 0, sizeof(struct rt_device_graphic_info)); /* Register VPOST information */ psVpost->info.bits_per_pixel = BSP_LCD_BPP; psVpost->info.pixel_format = (BSP_LCD_BPP == 32) ? RTGRAPHIC_PIXEL_FORMAT_ARGB888 : RTGRAPHIC_PIXEL_FORMAT_RGB565; psVpost->info.pitch = psVpostLcmInst->u32DevWidth * (BSP_LCD_BPP / 8); psVpost->info.width = psVpostLcmInst->u32DevWidth; psVpost->info.height = psVpostLcmInst->u32DevHeight; /* Get pointer of video frame buffer */ /* Set display color depth */ /* Note: before get pointer of frame buffer, must set display color depth first */ if (psVpost->layer == eVpost_LCD) { #if (BSP_LCD_BPP==32) vpostSetVASrc(VA_SRC_RGB888); #else vpostSetVASrc(VA_SRC_RGB565); #endif psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiFrameBuffer(DEF_VPOST_BUFFER_NUMBER); } #if defined(BSP_USING_VPOST_OSD) else if (psVpost->layer == eVpost_OSD) { vpostOSDSetWindow(0, 0, psVpost->info.width, psVpost->info.height); #if (BSP_LCD_BPP==32) vpostSetOSDSrc(OSD_SRC_RGB888); #else vpostSetOSDSrc(OSD_SRC_RGB565); #endif psVpost->info.framebuffer = (rt_uint8_t *)vpostGetMultiOSDBuffer(DEF_VPOST_BUFFER_NUMBER); } #endif if (psVpost->info.framebuffer == NULL) { rt_kprintf("Fail to get VRAM buffer.\n"); RT_ASSERT(0); } else { uint32_t u32FBSize = psVpost->info.pitch * psVpostLcmInst->u32DevHeight; psVpost->info.smem_len = u32FBSize * DEF_VPOST_BUFFER_NUMBER; rt_memset(psVpost->info.framebuffer, 0, u32FBSize); } /* Register member functions of lcd device */ psVpost->dev.type = RT_Device_Class_Graphic; psVpost->dev.init = vpost_layer_init; psVpost->dev.open = vpost_layer_open; psVpost->dev.close = vpost_layer_close; psVpost->dev.control = vpost_layer_control; /* Register graphic device driver */ ret = rt_device_register(&psVpost->dev, psVpost->name, RT_DEVICE_FLAG_RDWR); RT_ASSERT(ret == RT_EOK); if (psVpost->layer == eVpost_LCD) { rt_hw_interrupt_install(psVpost->irqn, nu_vpost_isr, psVpost, psVpost->name); rt_hw_interrupt_umask(psVpost->irqn); } rt_kprintf("%s's fbmem at 0x%08x.\n", psVpost->name, psVpost->info.framebuffer); } /* For saving memory bandwidth. */ vpostLCMDeinit(); return (int)ret; } INIT_DEVICE_EXPORT(rt_hw_vpost_init); ``` 用到的LCD初始化结构体如下: ```C static VPOST_T DEF_FW070TFT = { 800, /*!< Panel width */ 480, /*!< Panel height */ 0, /*!< MPU command line low indicator */ 0, /*!< MPU command width */ 0, /*!< MPU bus width */ VPOSTB_DATA16or18, /*!< Display bus width */ 0, /*!< MPU mode */ VPOSTB_COLORTYPE_16M, /*!< Display colors */ VPOSTB_DEVICE_SYNC_HIGHCOLOR, /*!< Type of display panel */ 0x020d0420, /*!< CRTCSIZE register value */ 0x01e00320, /*!< CRTCDEND register value */ 0x033e0339, /*!< CRTCHR register value */ 0x040c03f8, /*!< CRTCHSYNC register value */ 0x020001f6 /*!< CRTCVR register value */ }; ``` `lv_port_indev_init()`初始化并注册了触摸屏的输入设备,链接了触摸屏的input_read回调接口。 ```C void lv_port_indev_init(void) { static lv_indev_drv_t indev_drv; /* Basic initialization */ lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_POINTER; indev_drv.read_cb = input_read; /* Register the driver in LVGL and save the created input device object */ lv_indev_drv_register(&indev_drv); } ```
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
吉利咕噜2022
国防科大-军品研发
文章
18
回答
3
被采纳
2
关注TA
发私信
相关文章
1
LittlevGL + DMA2D 显示图案扭曲
2
LittleVGL2RTT软件包还有在维护吗,测试遇到一些问题求解
3
使用littlevgl2rtt软件包实例运行不成功,emwin正常
4
关于littlevgl2rtt软件包刷频慢的解决方案?
5
移植了littlevGUI之后,用动态 线程去跑例程会卡死
6
lvgl的字体、图片文件如何升级?
7
qemu-vexpress-a9bsp下的littvgl工程可以实现触屏操作吗?
8
LVGL控件刷新死机问题
9
在lvgl上设置一个时间显示的label,一段时间后所有控件消失。
10
littlevgl2rtt和littlevgl的pc模拟器源码不兼容吗?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部