ucGUI移植详细设计及总结
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
ucGUI移植详细设计及总结
序
本文档阐述了将ucGUI移植到IM12上的过程。
ucGUI版本为3.9,移植到IM12上,触摸屏及按键能够正常使用。
在ucGUI源码包的基础上,添加了一些接口函数以适应IM12,在使用时应该根据情况使用这些接口,这些新增加的函数的接口将在后面章节中详细讲述。
此外,适应IM12的ucGUI在Wind River Workbench 3.0环境下被编译成两个静态库文件libNoWindow.a和libWindow.a,编译程序时应该连接这两个库。
文档篇章安排如下:
第一章,ucGUI源码包简介。
主要介绍了所使用的ucGUI图形库中各文件夹的内容及功能,并对IM12中与ucGUI移植相关的部分,包括触摸屏、LCD、按键板等进
行了简单的介绍。
第二章,图形库移植。
阐述如何对ucGUI进行配置、编译,以在IM12的LCD上显示图形,此部分还未实现触摸屏及按键功能,只是纯粹的显示而已
第三章,触摸屏移植。
第四章,按键移植。
第五章,带触摸屏及按键功能的ucGUI应用程序模板。
第一章ucGUI源码包简介
ucGUI要移植到im12上,实际上就是根据im12的情况修改ucGUI中的一些配置项,或增加、删减一些程序以适应im12,同时要保持ucGUI的特性。
要做好移植工作,需对ucGUI 及IM12相关部分有足够的了解。
1.1ucGUI简介
移植所采用的ucGUI版本为3.9,主要包含的文件夹如图1所示
图1 ucGUI源码结构图
各文件夹的主要内容如下:
Config ----------- 配置文件
GUI ----------- 源代码
GUI_X ---------- 操作系统接口函数定义文件
GUI 源代码文件:
1)AntiAlias: 抗锯齿显示效果支持。
2)ConvertColor: 彩色显示的色彩转换支持。
3)ConvertMono: (b/w)和灰度显示的色彩转换支持。
4)Core: 核心文件,提供了GUI基本的功能。
5)Font: 字库。
6)JPEG: 图片操作函数。
7)LCDDriver: LCD驱动支持。
8)MemDev: 内存设备支持。
主要功能是防止在项目重叠时触摸屏的闪烁。
9)Widget: 窗体控件库。
10)WM: 窗口管理库。
注意:JPEG、MemDev、Widget、WM是可裁剪项,若要支持Widget(窗体控件),需要WM(窗口管理器)的支持;使用控件时,需要将相应的头文件包含进去,比如我们需要使用按钮BUTTON,那么我们需要先包含BUTTON.h头文件,否则控件即使支持也不可用。
本次
移植保留了JPEG、MemDev、Widget、WM这几项。
1.2IM12移植相关部分简介
移植中要设计到IM12的处理器、LCD、触摸屏、按键板几个部分。
IM12的主处理器为三星公司的S3C2410,采用的LCD像素为800x600,触屏屏芯片则使用了EPH1610,与串口2通信,按键板上包括一个飞梭、八个按键,其中一个按键实现系统开机或关机。
第二章图形库移植
图形库的移植的的工作是修改Config下的两个个配置文件:GUIConf.h、LCDConf.h。
同时需要修改GUI\LCDDriver目录下的LCD驱动文件LCD2410.c和GUI_X文件夹下的操作系统接口定义文件GUI_X.c。
2.1修改GUIConf.h
GUIConf.h文件的主要内容如下:
#define GUI_OS (1) /* 多任务支持*/
#define GUI_SUPPORT_TOUCH (1) /* 触摸屏支持*/
#define GUI_SUPPORT_UNICODE (1) /* UNICODE支持*/
#define GUI_DEFAULT_FONT &GUI_Font6x8 /* 默认字体*/
#define GUI_ALLOC_SIZE (10*1024*1024) /* 动态内存大小*/
/*********************************************************************
* Configuration of available packages
*/
#define GUI_WINSUPPORT 1 /* 视窗管理支持*/
#define GUI_SUPPORT_MEMDEV 1 /* 内存设备支持*/
#define GUI_SUPPORT_AA 1 /* 抗锯齿支持*/
在上面的配置中,值1表示支持,0表示不支持。
ucGUI程序要运行在多任务操作系统VxWorks中,所以需要选择支持多任务。
同时,需要支持触摸屏、Unicode编码。
ucGUI默认字体选择为GUI_Font6x8,该种字体在GUI.h中有声明。
GUI_ALLOC_SIZE表示可用于可分配的动态内存的大小。
IM12屏幕分辨率为800x600,用于存储屏幕数据的最小值为(800+7)/8*600*16 = 960,000,在其它地方也会耗费一些内存,所以为GUI_ALLOC_SIZE指定值为10*1024*1024。
2.2 修改LCDConfig.h
LCDConfig.h文件的主要内容如下:
#define LCD_XSIZE (800) /* LCD的X轴分辨率*/
#define LCD_YSIZE (600) /* LCD的Y轴分辨率*/
#define LCD_BITSPERPIXEL (16) /* 每个像素的颜色位数*/
#define LCD_CONTROLLER 2410 /* LCD控制器的名称*/
#define LCD_SW AP_RB_0 1 /* 是否红蓝交换*/
注意:LCD_SW AP_RB会影响到颜色的正确性,倘若发现颜色是反向的,那么不如改变LCD_SWAP_RB的值。
上面所说的LCD控制器名字并不代表LCD实际名字。
LCD的驱动文件LCD2410.c中,在文件包含声明之后,有一行代码“#if (LCD_CONTROLLER == 2410)”,此语句表示,如
果LCD_CONTROLLER的值为2410则编译下面的程序。
所以,在LCD2410.c和LCDConf.h 中的LCD_CONTROLLER中的值一致,就可以使用该驱动文件了。
值得注意的是,应该保证在可选择的LCD驱动文件不止一个的时候,各驱动文件的有效LCD_CONTROLLER值都是不同的。
2.4 修改LCD2410.c
在GUI/LCDDriver文件夹下存放着对应于LCD驱动文件LCD2410.c,在这个文件中声明了几个外部函数,需要在该文件之外进行定义。
这几个需要在外部进行定义的函数是:void LCD_init(void);
void LCD_PutPixel(int x,int y,unsigned short color);
unsigned short LCD_GetPixel(int x,int y);
void LCD_Backlight(int arg);
void LCD_TurnON(void);
void LCD_TurnOFF(void);
这几个函数中,LCD_PutPixel()、LCD_GetPixel()及LCD_Backlight()都在LCD的底层驱动中进行了定义,并且在LCD_RawDrv.h中进行了声明。
把剩下的LCD_init()、LCD_TurnON()、LCD_TurnOFF()集中在自定义的文件LCD_PrimFunction.c中,文件内容如下:
/* LCD相关硬件初始化*/
void LCD_init(void)
{
LCD_HwInit();
}
/* 打开LCD */
void LCD_TurnON(void)
{
}
/* 关闭LCD */
void LCD_TurnOFF(void)
{
}
LCD_init()调用了函数LCD_HwInit()进行初始化,而LCD_TurnOn()仅仅在LCD2410.c 中的void LCD_On(void )中被调用一次,LCD_On()函数原型如下:
void LCD_On(void)
{
extern void LCD_TurnON(void);
LCD_TurnON();
LCD_Backlight(1);
}
LCD_On()要实现的功能是打开LCD,有些LCD需要利用LCD_TurnON()做一些设置,
然后再调用LCD_BackLight(1)才算是把LCD打开,而IM12的LCD只需掉用LCD_Backlight(1)就可以了,所以定义LCD_TurnON()为空。
2.5改写操作系统相关接口函数:
主要填写GUI_X文件夹下的GUI_X.c文件,该文件下是一些操作系统相关的函数的封装或转换。
在各函数内填写适合于VxWorks的内容。
2.6 出错处理
上述配置情况下,在Wind River Workben开发环境中使用ARMARCH5diab编译器将ucGUI的所有文件一同编译,会出现如下的错误提示。
C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\make1036371.sh: dld: can't execute: (87) 参数不正确。
错误提示的大概内容是“dld命令不能执行,因为该命令的参数不正确。
”实际上是由于ucGUI中的文件数目过多,尤其是GUI下面的Widget文件夹,该文件夹的文件数目达245个。
这么多的文件,以至于在最终把.o文件链接成.out文件的工作不能完成。
为了解决这个问题,对ucGUI进行分割,将GUI文件夹中的Widget提取出来,归为另外一个工程guiWindow, 而剩下的文件归为工程guiNoWindow。
Widget调用guiNoWindow工程生成的静态库文件libNoWindow.a生成另外一个静态库文件libWindow.a,应用程序在使用时应该调用这两个静态库。
将Widget划分出来的依据是,ucGUI总共有七百多个.C和.H 文件,而Widget文件夹竟然有245个.C和.H文件,占的比例是比较大的,可见Widget 是导致链接不成功的主要原因;而且,JPEG、MemDev、Widget、WM是可裁剪项,除Widget 外,其余3个文件夹与其他的不可裁剪的函数间存在相互调用的关系,而Widget文件夹里的文件处于最高层位置,总是调用其它文件夹里的函数。
所以综合考虑,把Widget分离开来比较合适。
由于Widget中的一些文件会调用其他文件夹的文件,如BUTTON.h文件,在头文件中包含了了”..\WM\WM.h”文件,将Widget与其他文件夹分离后,编译会因找不到指定路径的WM.h文件而报错。
为了解决这种情况,将各个文件夹中的.h抽取出来放在includes 中,在Wind River WorkBench中指定到这来查找头文件,并将各个文件中相关的文件路径进行更改,更改后变为#include“fileName”。
最后,编译通过。
第三章触摸屏移植
IM12的触摸屏采用了EPH1610芯片进行控制,移植时主要修改两个文件,分别是Config 目录下的触摸屏配置文件GUITouchConf.h及GUI_X目录下的触摸屏驱动文件GUI_X_Touch.c。
3.1 修改GUITouchConf.h
GUITouchConf.h中主要内容如下:
#define GUI_TOUCH_AD_LEFT 0x07 /* 触摸屏左侧AD测量值*/
#define GUI_TOUCH_AD_RIGHT 0xf8 /* 触摸屏右侧AD测量值*/
#define GUI_TOUCH_AD_TOP 0x05 /* 触摸屏上方AD测量值*/
#define GUI_TOUCH_AD_BUTTOM 0xf6 /* 触摸屏底部AD测量值*/
#define GUI_TOUCH_SW AP_XY 0 /* X、Y方向翻转*/
#define GUI_TOUCH_MIRROR_X 0 /* X方向镜像*/
#define GUI_TOUCH_MIRROR_Y 0 /* X方向镜像*/
上面所说的触摸屏左侧、右侧、上方、底部并不是指触摸屏本身,而是相对于与其连接的LCD而言,GUI_TOUCH_AD_LEFT指LCD坐标值X坐标值为0时的AD测量值。
上述的值除了出现在触摸屏配置文件GUITouchConf.h外,还出现在ucGUI的触摸屏驱动文件GUI_TOUCH_DriverAnalog.c中,在其他位置不再使用这几值。
这几个宏定义的作用是确定在指定的物理坐标系中X轴及Y轴的最小及最大值。
在GUI_TOUCH_DriverAnalog.c中指定了这几个数的默认值。
GUI_TOUCH_SWAP_XY、GUI_TOUCH_MIRROR_X、GUI_TOUCH_MIRROR_Y 的值为0或1,因此在一开始就可以根据需要指定这三个值。
而GUI_TOUCH_AD_LEFT~BUTTOM这几个值是存在变动的,如果这几个值配置得不准确,会导致触摸不准的状况,虽说可以用校准的方法改变,但却要每次都要进行校准确实是比较麻烦。
解决这个问题的方法是,先确定一个大概的值,让触摸屏运行起来,然后再使用校准程序获取GUI_TOU_AD_LEFT~BUTTOM的值,再在GUITouchConf.h中修改这几个值,并重新编译ucGUI。
3.2 修改GUI_X_TOUCH.c
修改了GUITouchConf.h后,接着修改触摸屏底层接口定义文件GUI_X_TOUCH.c,该修改后,文件内容如下
/* 激活X方向的测量*/
void GUI_TOUCH_X_ActivateX(void)
{
}
/* 激活Y方向的测量*/
void GUI_TOUCH_X_ActivateY(void)
{
}
/* 读取X方向AD转换值*/
int GUI_TOUCH_X_MeasureX(void)
{
return Touch_MeasurementX(); /* 对应触摸驱动中的读取x值函数*/
}
/* 读取Y方向AD转换值*/
int GUI_TOUCH_X_MeasureY(void)
{
return Touch_MeasurementY(); /* 对应触摸驱动中的读取y值函数*/
}
由于在给定的触摸屏驱动中,并不存在Touch_MeasurementX()、Touch_MeasurementY()这两个。
所以引用原来的触摸屏驱动文件,并将文件名及其中的文件函数名进行更改,以避免冲突,然后定义这两个文件。
Touch_MeasurementY()和Touch_MeasurementX()是通过读取驱动文件中的全局变量ePH_phyY和ePH_phyX得到的,在使用时需要启动一个任务,不断获取AD值并更改这两个数据。
第四章按键移植
IM12的按键板通过串口与处理器进行通信。
IM12按键板上有一个飞梭和八个按钮,其中一个按钮是系统开关按钮。
ucGUI中并没有提供按键板驱动,但提供了键盘驱动,可以利用这个键盘驱动而启用按键功能。
键盘驱动程序层操作键盘信息函数。
当具体的键或按键键组合已经按下或被释放时,程序会通知视窗管理器。
驱动层有两主要函数:void GUI_StoreKeyMsg(int Key, int Pressed)及void GUI_SendKeyMsg(int Key, int Pressed)。
GUI_StoreKeyMsg()函数能够存储一个键的状态信息,而GUI_SendKeyMsg()能向一个指定的按键发送状态信息。
GUI_StoreKeyMsg()函数的实现其实就是根据输入参数Key, Pressed 来更改它的定义文件GUI_OnKey.c中的_KeyMsgCnt, _KeyMsg, _Key三个数据。
而该文件中的其他函数GUI_GetKey(), GUI_StoreKey(), GUI_SendKeyMsg(),GUI_PollKeyMsg()则调用这些数据进行操作。
这里比较重要的函数是GUI_PollKeyMsg(),该函数实现检测键盘信息并将检测到的信息发送给视窗管理器让其进行相应的处理。
要启用按键功能,有两个要点,一是不断获取按键状态并保存。
二是不断获取按键状态并进行处理。
获取按键状态的工作在上层应用层进行,实际上就是要不断调用GUI_PollKeyMsg()函数。
所以移植按键的工作实质上就是不断获取按键状态并保存,这里实际上是不断地读取串口数据里的数据,然后经转换,用GUI_StoreKeyMsg()保存。
4.1 按键值转换
ucGUI预定义了一些虚拟键盘代码,如下表所示
视窗管理器根据这些预定义的虚拟键盘值对键盘动作产生作用,如GUI_KEY_UP键使下拉框的选择项上移。
按键板产生的值与这些值并不对应,例如按下飞梭键参数的按键值为8,而GUI_KEY_ENTER预定义的值为13,所以若要使飞梭按下动作与键盘Enter键按下动作产生相同的效果,则要将从串口读取的数据进行转换,转换为键盘Enter键的值
GUI_KEY_ENTER。
按键板中,飞梭起到方向键的作用,它可产生三个动作,分别是顺时针旋转、逆时针旋转、按下。
按下动作,类似于普通的按键动作,这里把这个键转化为GUI_KEY_ENTER。
在IM12中,飞梭的旋转动作可代表产生上下、左右方向键及制表和反向制表的动作,可见飞梭的旋转动作效果必须是可切换的。
因此在按键板的主程序文件Key.c中指定一个按键模式全局变量KeyMode,按键模式由void SetKeyMode(int keyMode)进行设定。
飞梭动作值被读取后,根据当前模式,决定转换后的值,各种模式飞梭动作值经转换后的值含义如下表所示:
GUI_KEY_SHIFT_TAB是自定义的反向制表键,相当于shift+tab组合键。
ucGUI源码包中并不存在这个键的定义,也不存在退格制表的功能。
但TAB键还是发挥着制表的作用,因此可以模仿TAB键的制表功能的实现机理来实现GUI_KEY_SHIFT_TAB键的反向制表功能。
4.2反向制表功能的实现
1、反向制表功能实现的第一步当然是定义GUI_KEY_SHIFT_TAB的值,指定的值不能造成与其他键值的冲突,这里将这个值设定为0x7e。
2、查看TAB键制表功能的实现机理。
要实现此功能,必然会先判断按下的按键为TAB 键,然后再实现制表。
搜索ucGUI中GUI_KEY_TAB的使用情况,发现除定义文件外,一共出现在四个地方,出现的位置及使用情况如下:
判断按下的键为TAB键,然后再将焦点设置在下个子窗口。
函数WM_SetFocusOnNextChild()在WM.h中,查看此文件发现以下的情况:
函数SetFocusOnNextChild()将焦点设置在下一个子窗口,而WM_SetFocusOnPrevChild()函数则将焦点设置在前一个子窗口。
观察WM_SetFocusOnPrevChild()的使用情况,发现除了定义及声明外,它并没有被ucGUI中的其它地方调用。
综合考虑,可在FRAMEWIN.c及WINDOW.c模仿TAB制表的实现,调用SetFocusOnPrevChild()实现反向制表。
在FRAMEWIN.c的FRAMEWIN__cbClient()中按键值选项中,紧跟GUI_KEY_TAB项添加以下内容:
同时在WINDOW.c中的_cb()中,紧跟GUI_KEY_TAB项添加以下内容:
第五章带触摸屏及按键功能的ucGUI应用程序模板
拥有触摸屏及按键功能的ucGUI应用程序模板如下所示:
#include "Key.h"
#include "KeyValue.h"
#include "vxWorks.h"
#include "taskLib.h"
#include "semLib.h"
#include "TouchDriver.h"
#include "LCDConf.h"
#define Touch_PRIORITY 100
#define KEY_PRIORITY 101
#define GUI_PRIORITY 102
SEM_ID lcdSemId; /* lcd信号量*/
/*
------------------------------------------------------------------------------------------------------- code
-------------------------------------------------------------------------------------------------------- */
/*
* ******************************************************************* * 函数名:Task_GUI()
* 函数描述:不断地执行gui任务
* *******************************************************************/ void Task_GUI(void)
{
while(1)
{
/*
---------------------------------------------------------------------------------------------
code
---------------------------------------------------------------------------------------------
*/
GUI_ExecDialogBox(_aDialogCreate,GUI_COUNTOF(_aDialogCreate),
&_cbDialog, 0, 0, 0);
GUI_Delay(1000);
}
}
/*
* ******************************************************************* * 函数名:Task_Touch()
* 函数描述:不断地检测触摸屏状态,并保存
* *******************************************************************/ void Task_Touch(void)
{
while(1)
{
GUI_TOUCH_Exec();
taskDelay(sysClkRateGet()/100); /* 每秒检测100次*/
}
}
/********************************************************************* * MainTask
********************************************************************** */
void jh(void) {
lcdSemId = semBCreate(SEM_Q_PRIORITY, SEM_FULL);
TouchInit();
GUI_Init();
/*
---------------------------------------------------------------------------------------------
code
---------------------------------------------------------------------------------------------
*/
taskSpawn("tKey", KEY_PRIORITY, 0 , 2000, (FUNCPTR)TaskReadKey,
0,0,0,0,0,0,0,0,0,0);
taskSpawn("tTouch", Touch_PRIORITY, 0, 2000, (FUNCPTR)Task_Touch,
0,0,0,0,0,0,0,0,0,0);
taskSpawn("tGui", GUI_PRIORITY, 0, 2000, (FUNCPTR)Task_GUI,
0,0,0,0,0,0,0,0,0,0);
}
在主程序中需要生产三个任务tKey, tTouch, tGui。
tKey任务用于读取按键值并保存,tTouch任务实现对触摸屏状态的检测,tGui将不断的更新界面。
总结
本次移植ucGUI的工作费了不少时间,但学到了不少知识及技巧。
移植工作开头并不顺利,按照参考资料一步步做,资料大多都说在不加触摸屏及按键支持的移植是比较简单的,配置几个选项就可以了,我在WorkBench 3.0中照着步骤做,但总是在将.o文件链接成.out 文件的时候出现错误,费了一番周折,才发现出错的原因是文件数目过多,于是不得不使用将ucGUI分成两部分编译,并生成静态库的办法。
在触摸屏移植的时候,由于不了解ucGUI 的触摸屏消息传递机制,所以写了个看似正确的测试程序,但却由于达不到ucGUI中消息传递的条件,所以触摸屏消息不能传递到上层。
犯这个错误的原因是对ucGUI不够熟悉,写的测试程序流于表面。
意识到这个问题后,于是先完成屏幕布局菜单的设计,将ucGUI 熟悉,然后再移植按键。
屏幕布局菜单的设计主要参考了用户手册并借鉴了例子程序,进展得比较顺利。
在这个过程中,阅读了不少ucGUI源码,对ucGUI有了进一步的了解。
移植按键的时候,借鉴了移植触摸屏的方法,并把它当成键盘,移植过程也比较顺利。
在整个移植过程中,阅读了不少ucGUI的源码内容,ucGUI具有良好的代码风格,很值得借鉴。
同时在这个过程中也学会了使用比较法分析问题。