编写一个rt-smart上的应用程序体验一下!
- 1.本文目的
- 2.设计思路
- 2.1 驱动设计
- 2.2 树莓派4 上的framebuffer与touch
- 2.3 上层应用程序的设计
- 3.开发流程
- 3.1 环境搭建
- 3.2 编写lvgl_smart应用程序
- 3.3 编写驱动程序
- 4.效果体验
- 5.rt-smart使用心得
1.本文目的
在漫长的等待过程中,rt-smart开源版本发布出来了。拿到rt-smart第一手资料的,就在思考如何用rt-smart做些好玩的东西,可以充分发挥出用户态与内核态的特性。正好目前正在研究树莓派4的显示屏和触摸屏,所以就想着把lvgl最新版本移植上去跑跑,看看上手难度以及最后的运行体验效果究竟怎么样。心动不如行动,立即评估自己的时间。花了两三个小时就把思路理清楚了,然后花了三四个小时去用代码实现功能,最后效果确实还很好,不管是流畅性还是代码的设计都非常简单明了,下面来分享一下其中的过程。
2.设计思路
2.1 驱动设计
由于应用态与内核态的分离和单独编译,使得应用程序的开发更加关注上层业务逻辑,不太关注系统层面的东西。但是目前rt-smart的驱动框架编程上还是要有一些理解才能访问framebuffer以及触摸坐标点,目前由于lcd与touch驱动都在内核层。访问时可以通过rt-thread设备驱动框架进行。如果对rt-thread编程比较熟悉的,都会使用rt_device_find
、rt_device_open
、rt_device_control
去进行驱动接口的访问,而在rt-smart上,这一套编程模型还是依然如此。也就是userapps访问外设依然通过rt_device_xx
来进行设备的访问。
需要注意的frambuffer是内核态的地址,此时用户态不能直接访问的,如果要访问该怎么办?此时应用了一个共享内存页面的特性,将一块内核态的内存与用户态的内存进行共享,此时访问就可以正常进行了。
代码语言:javascript复制https://github.com/RT-Thread/rt-thread/blob/rt-smart/bsp/qemu-vexpress-a9/drivers/drv_clcd.c
具体可以看到下面的实现:
代码语言:javascript复制info->smem_len = lcd->width * lcd->height * 2;
info->smem_start = (uint32_t)lwp_map_user_phy(lwp_self(), RT_NULL, lcd->fb,info->smem_len, 1);
而对于触摸设备,向用户态传输不是地址,而是坐标,这样就不会出现这样的问题。应用程序只用关注坐标x,坐标y,以及是否被按下的状态。有了lcd与触摸屏这两个驱动,移植lvgl就没有什么问题了。
2.2 树莓派4 上的framebuffer与touch
目前接的是dsi的屏幕,也就是右边的图示所对应的屏。
该驱动和HDMI是一样的,也就是树莓派GPU的程序已经完成实现,接上HDMI或接上DSI,两者都是一样的显示效果。而不同的是DSI是带有触摸接口的。访问的方式采用mailbox进行CPU与GPU的通信。
对于LCD,首先写程序让CPU告诉GPU需要的分辨率和bpp,然后通过mailbox获取framebuffer的地址,向该地址写图形数据,GPU会自动刷新到LCD上。
对于触摸,只需从GPU获取一个地址,该地址记录这触摸的buffer,支持多点触摸的坐标信息。
这就是异构设计共享内存的特点,传递消息机制都是如此的有特色。
2.3 上层应用程序的设计
本来想按照rt-thread的rtt-lvgl的软件包来进行设计,我看了一下,对工程结构的改动较大。不适合作为我的设计初衷。而且随着lvgl版本的迭代升级,维护成本太大。我的设计是,不改变lvgl的整个仓库的目录结构,中间加个rt-smart与lvgl的兼容层。每次rt-smart更新或者lvgl更新,只要是关键移植api不变,那对于整个工程都没有影响。
这种设计才是我想要达到的目的。所以我设计了一个兼容层目录结构。
3.开发流程
3.1 环境搭建
做rt-smart的开发首先需要集成开发环境。
代码语言:javascript复制https://realthread.cowtransfer.com/s/5d7f490c09ef42
下载完成后解压可以看到如下的目录:
目录名 | 说明 |
---|---|
kernel | RT-Thread内核 (打开RT_USING_SMART将开启smart特性) |
tools | 编译rt-smart时用到的python脚本 |
userapps | 用户态开发环境和示例 |
userapps/apps | 用户态应用示例 |
userapps/linker_scripts | 不同CPU体系结构下的默认链接脚本 |
userapps/sdk | 用户态环境中需要用到的头文件,库等 |
集成开发环境中只有针对Linux环境下的交叉编译工具链,若要进行windows环境的开发工作,可以下载
代码语言:javascript复制http://117.143.63.254:9012/www/rt-smart/install_arm-linux-musleabi_for_i686-w64-mingw32.zip
下载后解压放到rt-smarttoolsgnu_gcc
目录。
检查并确认gcc路径为:rt-smarttoolsgnu_gccinstall_arm-linux-musleabi_for_i686-w64-mingw32bin
。
使用rt-thread的env工具,在``rt-smart的目录中输入
.smart-env.bat`则环境搭建完成。
3.2 编写lvgl_smart应用程序
只需要在userapps/apps
目录下新建一个名为lvgl_smart
的文件夹。
lvgl_smart
----[lv_rtt_port]
----[lvgl]
----lv_conf.h
----SConscript
上述目录结构中lv_rtt_port
是移植的关键部分对接程序,主要由自己编写。
lvgl
完全从https://github.com/lvgl/lvgl
上获取最新代码,完全不用修改。
lv_conf.h
文件为lvgl
目录中的lvgl_conf_template.h
修改而来,只需修改四个个宏定义即可。
#if 1 /*Set it to "1" to enable content*/ //需要打开
#define LV_HOR_RES_MAX (800) //根据自己的lcd进行配置
#define LV_VER_RES_MAX (480) //根据自己的lcd进行配置
/* Color depth:
* - 1: 1 byte per pixel
* - 8: RGB332
* - 16: RGB565
* - 32: ARGB8888
*/
#define LV_COLOR_DEPTH 32 //根据自己的lcd进行配置
核心还是在lv_rtt_port
中,而具体需要关注的文件就是lv_port_rtt.c
文件。
#ifndef FBDEV_NAME
#define FBDEV_NAME "hdmi"
#endif
#define TOUCH_NAME "touch"
通过rt_device_xxx
来操作设备。
lv_lcd = rt_device_find(FBDEV_NAME);
if (lv_lcd == RT_NULL)
{
rt_kprintf("can't find the lcd:%sn", FBDEV_NAME);
return;
}
rt_kprintf("nThe framebuffer device was opened successfully.n");
rt_device_open(lv_lcd, RT_DEVICE_OFLAG_RDWR);
// Get fixed screen information
rt_device_control(lv_lcd, FBIOGET_FSCREENINFO, &lcd_info);
rt_device_control(lv_lcd, FBIOGET_VSCREENINFO, &lcd_var_info);
主要获取framebuffer则可以进行操作了。
获取触摸点也是类似操作。然后对接lvgl相关的显示和触摸接口即可。
另外需要关注的文件是rtt_lvgl_test.c
文件,该文件是显示demo的接口。也就是该应用程序的入口地址。关于lvgl的examples我主要参考移植https://github.com/lvgl/lv_examples
。
整个app的工程项目代码在下面找到:
代码语言:javascript复制https://gitee.com/bigmagic/lvgl_smart
3.3 编写驱动程序
由于目前公开版本的代码还在稳定期,我未向其pr,目前工程代码还在整理之中。如果想体验,我已经将一份放置于百度网盘中。
代码语言:javascript复制链接:https://pan.baidu.com/s/1w2x0iKQpBhGsNjZEWM4VHw
提取码:si2r
本次改动主要在时钟tick、hdmi驱动、增加触摸功能,以及增加mbox的一个接口。rt-smart驱动的设计目前和之前的rt-thread通用版本改动不大。另外需要注意的是内核地址的映射问题。
4.效果体验
在env控制台输入
代码语言:javascript复制 .smart-env.bat
然后进入userapps目录,输入scons
开始编译应用程序。生成的文件位于rootbin*.elf。
然后进入kernelbspraspberry-piraspi4-32
输入scons
开始编译kernel。
放入boot文件
首先需要准备一张空白的32GB或者32GB以下的sd卡。
下载树莓派4b的boot文件,解压后提取文件夹里面的内容放在sd卡根目录。
代码语言:javascript复制链接:https://pan.baidu.com/s/1gvJInzKzPB8PNeoYrIvYCw
提取码:bnd7
该boot里面的kernel7.img
与bin*.elf
是rt-smart旧版本,自己编译生成后可进行替换。
连接串口线
按照树莓派的串口序号进行连接
连接时,可以将树莓派的GND与串口模块的GND相连,树莓派上的RX、TX与串口模块交叉相连。
注意串口波特率115200,停止位1,无奇偶校验位。
下面看一下具体的运行效果:
5.rt-smart使用心得
在进行rtos编程时,若进行带有mmu的中高端芯片编程,使用rt-smart是比较理想的,实现了应用程序与内核程序的分离,使得上层的代码的容错性更大一些,比如对于GUI编程之类偏向业务逻辑设计,真是非常的简单。但是对于做驱动和做中间层的人来说,难度可能稍微有点大,而对于未曾接触过rt-thread编程的人来说,确实需要一点时间熟悉rt-thread的编程思想。
该移植过程中,整个设计还是比较清晰的,就是在实现的细节上确实也遇到一些问题,比如帧率很低,移植后很卡,触摸屏坐标对不上等等问题,这些都是驱动设计上的问题,最后都得到了比较好的解决。我更加倾向于在rt-smart上做一些业务层面的东西,最好设备驱动尽量的简单一些,这样做出来的东西架构清晰,出错的机率很低,就算应用程序异常了也不会对内核的执行造成影响。