建立ucgui的交叉编译工程后,就可以真正展开ucgui的移植工作了。在此之前,需要完成了LCD驱动工作,这代表你对eCos的framebuf框架有所了解。
本节主要介绍在eCos系统中如何让ucgui跑起来。ucgui设计优秀,具有良好移植性,配合eCos framebuf优秀框架,可以让你很快见到ucgui呈现的精美显示界面,let’s go…
eCos framebuf
关于eCos LCD驱动,详见:http://velep.com/archives/615.html和http://velep.com/archives/616.html
ucgui源码结构
解压ucgui源码包后,含3个文件夹:Sample、Start、Tool。如上左图所示。
- Sample:一些ucgui使用实例和模板,如GUIDemo程序;
- Start:ucgui源码;
- Tool:一些开发用户图形程序的实用工具;
Start目录结构如上右图所示。其中,Config和GUI是ucgui的程序目录,Application应该设计为存放用户的图形程序,System是windows下ucgui的仿真程序,Output存放仿真程序的编译输出,Simulation.dsw是ucgui的仿真工程。
我们主要关注Config和GUI两目录。相关文件介绍如下:
ucgui配置
ucgui的配置位于Config目录,分为GUI层配置和LCD层配置,前者配置GUI核心,对应GUIConf.h文件;后者配置LCD设备和触摸设备属性,对应LCDConf.h和GUITouchConf.h文件。
配置GUI
打开GUIConf.h文件,其配置项如下图所示,具体含义不做具体介绍(注释已经很详细了)。
配置LCD设备属性
对于LCDConf.h文件,只考虑上图中的配置项,其它内容忽略。根据LCD,定义其X、Y值、每个像素位数等。LCD_CONTROLLER的值,浏览了下代码,似乎是用于预编译用的,暂时保持为默认值1375。
LCD_SWAP_RB配置项,是本次移植时加上的,用于交换LCD的R和B两个颜色组,具体下面再详细描述。
ucgui驱动接口
LCDDriver下面的文件是ucgui与LCD驱动的接口文件,源码包中提供了3个模板,分别是:LCDWin.c、LCDNull.c、LCDDummy.c。移植时,按照模板填充相关函数。
网上很多移植教程是以LCDDummy.c模板来说明的。拷贝该文件并重命名为LCDeCos.c,然后修改该文件。这里,简要说下修改哪些地方,具体的代码请看http://52ecos.net/thread-648-1-1.html提供的源文件。
1. 修改头文件开头的编译宏开关,如下代码:
#if defined(ECOS) && !defined(LCD_SIMCONTROLLER)。
2. 包含相关头文件:
#include "LCD_Private.h" /* private modul definitions & config */ #include "GUI_Private.h" #include "GUIDebug.h" #include <pkgconf/system.h> #include <pkgconf/hal.h> #include <cyg/kernel/kapi.h> #include <cyg/infra/diag.h> #include <cyg/io/io.h> /* I/O functions */ #include <cyg/io/framebuf.h>
3. LCD framebuf设备宏定义:
#define FRAMEBUF fb0
4. 完成下面几个接口函数,分别是:
- LCD_L0_SetPixelIndex();
- LCD_L0_GetPixelIndex();
- LCD_L0_XorPixel;
- LCD_L0_DrawHLine();
- LCD_L0_DrawVLine();
- LCD_L0_FillRect();
- LCD_On();
- LCD_Off();
- LCD_L0_Init();
在ecos/packages/io/framebuf/ecos_version/include/framebuf.h文件可找到对应宏来完成上述接口函数。以LCD_L0_SetPixelIndex()函数为例进行说明。
LCD_L0_SetPixelIndex(),完成在LCD上画点操作。在eCos中对应的接口宏为:CYG_FB_WRITE_PIXEL()。开始移植时,我纠结CYG_FB_WRITE_PIXEL()第4个参数_colour_与LCD_L0_SetPixelIndex()第3个参数PixelIndex的对应关系。是不是要转换?经过分析ucgui代码发现,PixelIndex参数实际就是个RGB颜色索引值。因此,可以直接使用PixelIndex作为CYG_FB_WRITE_PIXEL()宏的第4个实参。
再说下LCD_L0_DrawHLine()接口函数。eCos中对应的接口宏为:CYG_FB_WRITE_HLINE()。它同样有个_colour_参数,但LCD_L0_DrawHLine()接口则没有相应的参数,怎么办?不急,ucgui提供了一个宏LCD_COLORINDEX,用于获取当前RGB颜色索引值。
打开LCD framebuf设备和LCD背光可以放在LCD_On()接口函数中,也可以放在LCD_L0_Init()接口函数。
至此,完成了ucgui的移植,使用其提供的demo作为测试程序,编译后下载程序到板子上运行,开始调试。
ucgui调试
只要你的LCD驱动没问题的话,初始编译后就可以看到显示界面。我的调试主要集中在LCD的颜色。
如上所述,开始移植时不明白eCos framebuf宏接口中_colour_参数的含义,实际上当初弄LCD驱动时也没明白这个参数的含义。导致LCD显示颜色不正确。
_colour_参数是个颜色值,通过查看eCos framebuf和LCD驱动代码发现,它直接写入到LCD GRAM寄存器。查看LCD驱动芯片SPFD5420A的数据手册,写入到LCD GRAM寄存器的值,是一个RGB565值,16-bit,代表65536种颜色。但注意,16-bit值到底是RBG还是BGR,可以通过设置LCD驱动芯片R0001寄存器的SS bit和R0003寄存器BGR bit来控制。
SS bit控制数据移位方向,BGR bit标示RGB反转,见SPFD5420A数据手册14和15页的说明。组合关系如下:
在LCD驱动中,SS=0,BGR=1(采用安富莱开发板的初始化配置),因此,写入GRAM的值对应的是BGR颜色值。在ucgui中,默认的是RGB颜色值,这就出现了蓝色和红色相反的问题。
#elif (FIXEDPALETTE == 565) && (LCD_SWAP_RB==0) #define COLOR2INDEX(Color) LCD_Color2Index_565(Color) #define INDEX2COLOR(Index) LCD_Index2Color_565(Index) #define GETINDEXMASK() LCD_GetIndexMask_565() #elif (FIXEDPALETTE == 565) && (LCD_SWAP_RB) #define COLOR2INDEX(Color) LCD_Color2Index_M565(Color) #define INDEX2COLOR(Index) LCD_Index2Color_M565(Index)
上面是ucgui LCD_L0_Generic.c文件一段关于把颜色转换成颜色值的代码,从中可看到,ucgui支持RGB颜色R和B调换,免去了修改LCD驱动,前提是有LCD_SWAP_RB宏的定义,于是我把该宏定义在了LCDConf.h头文件。解决了LCD颜色显示不正确的问题。
关于优化
根据ucgui提供的demo程序GUISpeed,测试出来的速度大概是每秒画80万个PIXs。这个速度,结合具体的显示画面来看,我是可以接受的,不过,看了网上其他人的测试结果,居然达到两三百个PIXs。这应该是做了很多优化的动作,看他们的介绍,严重的破坏了程序结构。
我个人认为,不必刻意去追求高速度,力求在用户体验与代码结构上找到一个平衡点即可。
再来看eCos framebuf驱动框架。其实前面已经提到,如果你追踪framebuf驱动框架代码,你可以发现,ucgui的驱动接口函数中调用的eCos framebuf接口,实际上是一系列的宏定义。也就是说,ucgui驱动接口函数中调用的实际上是直接操作LCD驱动芯片的函数,你别看eCos把它封装了一层又一层,实际上这些封装对性能没有任何副作用,而且,大大提高了程序的层次结构。