一、移植准备:
首先大致看一遍《uCGUI用户手册》, 了解ucGUI的源代码。
下表显示了GUI 所有子目录的内容:
目 录
内容
Config
配置文件(根据应用配置)
GUI/AntiAlias
抗锯齿支持 *
GUI/ConvertMono
用于B/W(黑白两 {MOD})及灰度显示的 {MOD}彩转换程序
GUI/ConvertColor
用于彩 {MOD}显示的 {MOD}彩转换的程序
GUI/Core
μC/GUI 内核
GUI/Font
字体文件
GUI/LCDDriver
LCD驱动(移植部分:根据需要修改或添加)
GUI/JPEG
JPEG图像支持 *
GUI/Mendev
存储器件支持 *
GUI/Touch
触摸屏支持 *
GUI/Widget
视窗控件库 *
GUI/WM
视窗管理器 *
(带“*”标志的为可选项,不同版本带的有所不同)
说明:移植是要特别注意配置文件Config和GUI/LCDDriver。
①.GUI/LCDDriver文件夹中存放的是一些LCD驱动代码,如果你使用的LCD在这里可以找到代码,可以直接通过Config中的LCDConfig.h里边的:
#define LCD_CONTROLLER -1 //-1:表示没有选择的LCD驱动,而是使用里边的样本程序进行修改。
uCGUI中具体支持哪些LCD?可以查询《uCGUI用户手册》的第22章 LCD驱动程序。里边详细的说明了,支持些什么控制器的LCD。
②.在工程中只需加载需要的LCD驱动代码文件即可。如果设置为-1,则选择加载LCDDummy.C或LCDTemplate.C文件(不同的版本,此代码的文件名可能会不同)。
二、代码移植:
1. 必须编写好LCD的驱动函数,当然如果uCGUI中已经包含了移植需要的LCD底层驱动,就不用再编写。如果没有就需要编写好LCD的底层驱动。底层驱动需要包含如下函数:
①.LCD_Init ():LCD初始化函数。
②.LCD_SetPixel(): LCD画点函数。
③.LCD_GetPixel(): LCD读点颜 {MOD}函数。
2.编写好这三个函数后,可以直接在LCDDummy.C或LCDTemplate.C文件中添加相应的代码
①.添加画点函数:
void LCD_L0_SetPixelIndex(int x, int y, int PixelIndex){
/* Write into hardware ... Adapt to yoursystem */
{
/* ... */ 添加画点函数
}
}
②.添加获取像素点颜 {MOD}函数:
unsigned int LCD_L0_GetPixelIndex(int x, int y) {
/* Read from hardware ... Adapt to yoursystem */
{
PixelIndex = 0;
/* ... */ 添加获取像素点颜 {MOD}函数
}
return PixelIndex;
}
③.添加初始化函数:
int LCD_L0_Init(void) {
LCD_INIT_CONTROLLER(); // 把这句改成LCD_Init ();
return 0;
}
当然也可以直接在LCDConf.c文件中,把宏改成如下:
#define LCD_INIT_CONTROLLER() TFTInit();
3. 以上方法是利用样本函数,当然也可以自己编写连接代码。如果是直接编写,则需要编写如下函数:
函数
说明
初始化及显示控制组
LCD_L0_Init ()
初始化显示屏。
LCD_L0_ReInit ()
不擦除内容而重新初始化LCD。
LCD_L0_Off ()
关闭 LCD。
LCD_L0_On ()
开启 LCD。
绘制组
LCD_L0_DrawBitmap ()
通用绘制位图函数。
LCD_L0_DrawHLine ()
绘制一条水平线。
LCD_L0_DrawPixel ()
以当前前景 {MOD}绘制一个像素。
LCD_L0_DrawVLine ()
绘制一条垂直线。
LCD_L0_FillRect ()
直译一个矩形区域。
LCD_L0_SetPixelIndex()
以指定颜 {MOD}绘制一个像素。
LCD_L0_XorPixel ()
反转一个像素。
“Get”组
LCD_L0_GetPixelIndex()
返回指定像素的颜 {MOD}的索引。
“Set”组
LCD_L0_SetOrg ()
不再使用,保留给将来使用(必须在驱动器中存在)。
查询表组
LCD_L0_SetLUTEntry ()
修改LUT 的单个条目。
Misc.组(可选)
LCD_L0_ControlCache ()
锁/解锁/清除 LCD 高速缓存。
注意:红 {MOD}为必须要实现的函数,其他函数可以为空,但必须保留函数体。
4. 在工程中添加uCGUI源代码和配置代码:
每个文件中只要添加相应的C文件即可,没有使用的部分可以不添加,LCDDriver中只要添加需要的LCD驱动代码即可。如果没有相应的LCD代码,而是使用样本代码,则只需要添加样本文件;如果是自己编写的代码,则添加自己的代码即可。
5. 设置包含路径,“Include”目录,确认你的Include 路径包括有以下目录(包括的先后顺序并不重要):
? Config
? GUI/Core
? GUI/Widget(如果使用视窗控件库)
? GUI/WM(如果使用视窗管理器)
警告:你必须确认你在每个文件中只使用了一个版本的μC/GUI
设置“include paths”
添加包含路径
6. 在例程中拷贝GUI_X文件夹中的文件:
①.GUI_X.C :无系统
②.GUI_X_embOS.C :embOS系统
③.GUI_X_uCOS.C :uCOS系统
根据不同条件添加不同文件,如果是无系统下添加文件①,否则根据系统添加文件,或编写文件。特别说明如下函数:
a.int GUI_X_GetTime(void);
此函数调用系统时间如果你的系统有实时时钟之类的可以将实时时钟驱动程序放在这里面。没有也没关系只是调用系统时间相关的函数不能用而已,可能这个功能很多人也不会用。
b.void GUI_X_Delay(int Period);
此函数用于GUI的延时,如果没有跑系统,就自己手动的写个延时程序,如果跑了操作系统,就调用系统的延时函数。
在uCGUI中有很多地方都调用了以上函数,在其它的GUIDEMO_XXXX.C中也有这样一些循环,你要是调试是发现液晶屏上的显示一直停在一个画面上很久的话,应该就是以上函数的问题,需要根据需要修改。
三、应用说明:
1. 如果要根据自己的需要编写应用代码的话,需要认真阅读API函数,利用API函数必须自己的应用程序即可,如:
void main(void)
{
int i=0;
/* 要做的事:确认硬件首先初始化了! */
GUI_Init();
GUI_DispString("Hello world!");
while(1);
}
2. 如果需要调用uCGUI例程,则需要添加GUIDemo文件夹及里边的全部文件,根据需要选择实例或调用GUIDEMO_main()函数。
四、注意事项:
1. 由于某些编译器(如:ADS1.2,MDK)不会初始化全局变量,部分内核必须修改,否则程序只工作一遍就不再工作。具体需要修改的地方如下,红 {MOD}表示添加的代码:
①.修改如下函数为:(位于GUICore.c)
static void _InitContext(GUI_CONTEXT* pContext) {
……
pContext->PenSize =1;
pContext->DrawMode = GUI_DRAWMODE_NORMAL;
pContext->TextMode = GUI_TEXTMODE_NORMAL;
pContext->TextAlign = GUI_TA_LEFT|GUI_TA_TOP;
pContext->AA_HiResEnable = 0;
/* Variables in WM module */
……
}
②.修改如下函数为:(位于GUIAlloc.c)
void GUI_ALLOC_Init(void) {
......
GUI_ALLOC.NumUsedBytes = 0;
memset(&aBlock, 0,sizeof(aBlock[0])*GUI_MAXBLOCKS);
aBlock[0].Size = (1<
③.修改如下函数为:(位于WM.c)
void WM_Init(void) {
if(!_IsInited) {
......
memset(&_ClipContext,0, sizeof(WM_IVR_CONTEXT));
NextDrawWin =WM__FirstWin = WM_HWIN_NULL;
......
sInited = ;
}
}
2. 自己编写初始化变量函数,该函数应在GUI_Init()调用之前调用。
voidMyInit() {
IsInitialized = 0; // MemDev
_IsInited = 0; //Win
WM__CreateFlags = 0;
GUI_CURSOR_pfTempHide = NULL;
GUI_Context.hDevData = 0;
WM__hCapture=0;
WM__hWinFocus=0;
WM_pfPollPID = 0;
WM_pfHandlePID = 0;
GUI_pfTimerExec = 0;
_KeyMsgCnt = 0;
}
本移植实验在《LPC2148开发板V2》验证通过;软件平台是MDK。
参考 <http://blog.163.com/zhaojun_xf/blog/static/300505802010102732525712/>
其它相关移植
- 触摸屏的移植
在使用触摸屏之前必须将Config目录下GUIConf.h中的GUI_SUPPORT_TOUCH设置为1,由于项目中使用了操作系统所以同时将GUI_OS也设置为1。触摸屏触点位置的获得是通过调用GUI/core/目录中GUI_TOUCH_DriverAnalog.c文件中的 GUI_TOUCH_Exec()函数来实现的,对该函数进行修改后的伪代码如下:
void GUI_TOUCH_Exec(void)
{
读取触点在触摸屏上(x,y)点实际坐标值;
进行实际坐标值到逻辑坐标的转换;
调用GUI_TOUCH_StoreState(x,y)保存逻辑坐标值;
}
系统为了实时得到触点坐标,就要不断调用GUI_TOUCH_Exec()函数。因此需要在uc/os-ii操作系统中建立一个单独的任务对该函数进行调用,这样可以保证触摸屏任务的实时响应。实现方式如下:
voidTask_Touch(void*id){ //建立触摸屏任务
while(1){ GUI_TOUCH_Exec(); //调用此函数
OSTimeDly(1); } //延时一个时钟节拍
}
-
液晶屏的移植
LCD的移植与具体使用的LCD有关,并且相同的LCD可以有不同的显示模式,这些都影响相关配置文件的修改。本系统使用的是CASIO公司320×240象素STN伪彩 {MOD}LCD,S3C44B0X中的LCD控制器与LCD的连接方式为8位单扫描方式,显示模式为彩 {MOD}显示。
配置的参数包含在LCDConf.h文件中,修改后的参数如下:
#defineLCD_XSIZE 320 //X,Y大小
#defineLCD_YSIZE 240
#defineLCDCOLOR //定义显示模式
#defineLCD_BITSPERPIXEL 8 //每个象素点的位数
#defineLCD_SWAP_RB 1 //是否交换蓝 {MOD}分量和红 {MOD}分量
#defineLCD_FIXEDPALETTE 332 //调 {MOD}板模式,本例使用3红,3绿,2蓝
#defineLCD_MAX_LOG_COLORS(256) //最大的逻辑颜 {MOD}数
以上是对LCD各配置参数的修改,接下来将完成LCD驱动API函数。其伪代码如下:
U32BUFFER[LCD_YSIZE][LCD_XSIZE/4] //定义显存,对显存操作直接反映到LCD上
//来_自:_S_TM8_. CN
int
LCD_L0_Init(void){ //LCD初始化函数
关闭LCD;
设定S3C44B0X LCD控制寄存器;
打开LCD;
return 0;
}
void LCD_SetPixel(BUFFER, x,y,color) //画象素点函数
BUFFER[(y)][(x)/4]=((BUFFER[(y)][(x)/4]&(~(0xff000000>>((x)%4)*8)))|((c)<<((4-1-((x)%4))*8)));
另外,在uc/os-ii操作系统中也需要建立一个单独的任务对GUI_Exec()函数进行调用,以保证屏幕的及时刷新,给此屏幕刷新任务分配一个尽量低的优先级,确保核心任务的实时性。实现方式如下:
void Task_LCDfresh (void*id){ //该任务完成屏幕刷新
while(1){ GUI_Exec(); //完成屏幕刷新
GUI_X_ExecIdle(); } //空闲任务
}
-
uc/os-ii接口文件的编写
uc/os-ii下使用uC/GUI需要提供一些内核接口函数,来实现任务间同步。接口函数实现如下:
staticOS_EVENT*DispSem; //uC/GUI使用的信号量
int GUI_X_GetTime(void) //获得当前时间
{ return((int)OSTimeGet()); }
void GUI_X_Delay(intperiod) //uC/GUI中的时间延时
{ INT32U ticks;
ticks=(period * 1000) / OS_TICKS_PER_SEC;
OSTimeDly(ticks); }
void GUI_X_InitOS(void) //初始化信号量
{ DispSem =OSSemCreate(1); }
void GUI_X_Lock(void) //锁定GUI任务
{ INT8U err;
OSSemPend(DispSem, 0,&err); }
void GUI_X_Unlock(void){ //解除锁定
OSSemPost(DispSem);
}
U32 GUI_X_GetTaskId(void){ //返回当前任务的ID号
return((U32)(OSTCBCur->OSTCBPrio));
}
有了这些内核接口函数,就可以使uC/GUI运行于uc/os-ii系统上。通过任务调度来实现各个任务间的协调工作,在任务建立时注意不要超出GUI/Core/guitask.c中规定的任务最大数GUI_MAXTASK。
-
中文小字库的实现
uC/GUI带有多种常用的ASCII字体,也支持UNICODE字符显示。移植GUI目的就是使人机界面友好﹑方便操作,所以对于国内用户来说装入汉字库是必须的。由于嵌入式系统内存资源十分有限,而整个汉字库又十分庞大,装入汉字库就意味着要牺牲很多的内存空间。基于上述考虑本文提出了建立自己的小型汉字库,不但解决了汉字显示问题还节约了宝贵的内存空间。接下来重点讲述小型汉字库的创建方法及其相关程序代码。
uC/GUI的文字显示是通过查找字模的方式实现。字库中每一个字母都有其对应的字模,所有字母的字模都是由GUI_FONT和GUI_FONT_PROP这两个结构体来统一管理。从汉字库中选出所必须的汉字,组成自己的汉字库,选出的汉字其机内码可能是不连续的,这样必须要为每一个汉字建立一个GUI_FONT_PROP结构,再将它们链接成链表。此种方法比较烦琐,要为每个汉字都建立一个链表结构。本文提出了一种新的构造方式,即采取自定义的编码。自定义的编码也是两个字节,但这些编码必须是连续的,这样就将不连续的汉字机内码映射到此连续区域。此时只需要建立一个GUI_FONT_PROP结构就可以管理所有的汉字了。比如要实现“参数设置”这四个汉字,具体实现的伪代码如下:
GUI_FLASH constunsigned characFontHZ12_b2ce[24]={……………} //汉字“参”的点阵
//来_自:_S_TM8_. CN
GUI_FLASH constunsigned characFontHZ12_cafd[24]={……………} //汉字“数”的点阵
GUI_FLASH constunsigned characFontHZ12_c9e8[24]={……………} //汉字“设”的点阵
GUI_FLASH constunsigned characFontHZ12_d6c3[24]={……………} //汉字“置”的点阵
GUI_FLASH constGUI_CHARINFOGUI_FontHZ12_CharInfo[4]={ //建立自己的汉字库
>{ 12, 12, 2, (voidGUI_FLASH*)&acFontHZ12_b2ce }, //参0xa1a1
{ 12, 12, 2,(voidGUI_FLASH *)&acFontHZ12_cafd}, //数0xa1a2
{ 12, 12, 2,(voidGUI_FLASH *)&acFontHZ12_c9e8}, //设0xa1a3
{ 12, 12, 2,(voidGUI_FLASH *)&acFontHZ12_d6c3} //置0xa1a4
};
GUI_FLASH constGUI_FONT_PROPGUI_FontHZ12_Propa2= {
0xa1a1, //映射地址起始位置
0xa1fe, //映射地址结束位置
&GUI_FontHZ12_CharInfo[0], //字模代码入口位置
0
};
GUI_FLASH const GUI_FONT GUI_FontHZ12 = {
GUI_FONTTYPE_PROP_SJIS, //字体类型
12, //字体的高度
12, //字体Y轴的间距
1, //Y轴的放大倍数
1, //X轴的放大倍数
(void GUI_FLASH*)&GUI_FontHZ12_Propa2
};
完成上述代码后,再将GUIConfig.h中的GUI_DEFAULT_FONT设置为:&GUI_FontHZ12;在GUI/Core/GUI.H中定义:externconst GUI_FONT GUI_FontHZ12;至此移植的主要工作已完成,将修改后的代码加入工程中一起编译,汉字就能显示在LCD屏幕上了。
源文档 <http://stm8.cn/news/UCGUIFile/20121115/1067.html>