基于ucGUI的触摸屏消息流转机制的研究
2013-08-14王宜结
王宜结
(淮南师范学院 电气信息工程学院,安徽 淮南 232038)
1 前言
触摸屏按原理分,可分为电阻式、电容式、电感式、红外式、超音波等几种,常见的是前两种。电阻屏不能实现多点触控,而且反应比较迟钝,耗电,好处就是任何物体均可以触摸,容易实现手写功能,在诺基亚手机中应用广泛。电容屏优点是反应快、可以多点触控,但是必须是导电物体接触才有反应,比如手指、金属笔等。苹果、三星还有HTC的智能手机现在大都采用电容屏幕。其他几种在日常数码产品中很少见,主要应用于军事及医疗行业。触摸屏给我们带来的好处是操作便利,可以随心所欲地在上面涂鸦,替代按键操作等。但也有明显的缺点,如缺乏真实操作手感、易误操作等。
uC/GUI是Micrium公司研发的通用的嵌入式图形用户界面软件。它提供独立于处理器和LCD控制器之外的有效的图形用户接口,可应用于单一或多个任务环境,支持任何尺寸的物理显示或模拟显示,适用于任何控制器驱动的任何LCD(单色,灰度,或彩色);通过配置宏,可支持任何接口;可配置显示尺寸;支持尺寸比实际显示大的虚拟显示;支持8位/16位/32位CPU及可允许用于支持ANSI C的任何编译器;可在LCD的任何一点上显示字符和绘制位图;对于显示尺寸和速度提供优化进程,编译时间依赖于采用的优化进程。
下面就以目前最常用的价格最低廉的四线电阻屏为例,说明其在ucGUI中如何实现消息传递的。
2 触摸屏消息处理流程
为了能实时感知触摸屏的变化,需要在主程序中创建一个专门用于处理触摸屏的任务 (级别较高),如:
void Task_Touch(void*id){//触摸屏任务While(1){
GUI_TOUCH_Exec(void){……}//执行触摸屏消息处理任务
OSTimeDlyHMSM(0,0,0,10);//主动退出任务,进入就绪状态(让CPU执行其他任务)}
触摸屏消息流转过程如下:点击触摸屏(持续时间大于操作系统的ticks,至少10ms以上)→uc/OS的任务调度器将任务切换到触摸屏处理任务“Task_Touch()” →执行函数:GUI_TOUCH_Exec()→读取x坐标值,并为读取y物理坐标值做好准备→退出触摸屏处理任务→处理其他任务→再度切换到触摸屏处理任务→执行函数:“GUI_TOUCH_Exec()” →读取 y 物理坐标,并为下次读取x坐标做准备。如果读取的x、y坐标有效,则转换成逻辑坐标值,并存储该值(即:_Store-Unstable(x,y);)。退出该函数并退出该任务。
下面是应用程序任务的执行过程:
执行对话框消息循环while(1){…}→GUI_Exec()→GUI_Exec1()→ WM_Exec()→ WM_Exec1()→WM_pfHandlePID()→WM_HandlePID()(注:某处有 WM_pfHandlePID=WM_HandlePID;//函数指针)→void WM__SendTouchMessage(WM_HWIN hWin,WM_MESSAGE*pMsg)→void WM__Send-PIDMessage(hWin,pMsg)→void WM__SendMessageIfEnabled(iWin,&Msg)→void WM__SendMessage(hWin,pMsg)(向对话框发送消息)→(*pWin-〉cb)(pMsg)(_FRAMEWIN_Callback)→ _OnTouch()(用这个函数处理触摸屏消息)→ (*cb)(pMsg)(cb-Dialog) →(*_cb)(pMsg) (_MESSAGEBOX_cbCallback)
下面分析各函数的主要功能:
GUI_Exec():这个函数主要就是调用GUI_Exec1(),而 GUI_Exec1()函数的功能是:首先,如果函数指针“GUI_pfTimerExec”不为空,则执行该函数,若执行成功,则返回1,否则,往下执行WM_Exec(),若成功执行,则返回1.类似地,WM_Exec()函数的作用就是调用WM_Exec1()执行,WM_Exec1() 函数的功能是:(1) 若 void WM_tfPollPID(void)型的函数指针不为空,则执行该函数 WM_pfPollPID();(2) 若 int WM_tfHandlePID(void)型函数指针WM_pfHandlePID不为空,则执行该函数,若执行成功,则本函数执行完毕并返回1。由于WM_Exec1()函数对整个触屏消息流转过程的理解十分关键,因此,下面用框图来描述该函数的完整功能(如图1)。
WM_HandlePID()函数功能是根据各种实际情况发送相应的PID消息,由于该函数实现过程很复杂,在此不再展开分析。WM_HandlePID()中调用的发送消息的函数如下:
void WM__SendMessage(WM_HWIN hWin,WM_MESSAGE*pMsg){
static int_EntranceCnt;//定义一静态变量,用以统计正在处理消息的回调函数个数
WM_Obj*pWin;
if (_EntranceCnt〈GUI_MAX_MESSAGE_NESTING){
pWin=WM_HANDLE2PTR(hWin);//窗口句柄转换成指针形式
pMsg-〉hWin=hWin;
if(pWin-〉cb!=NULL){
_EntranceCnt++;
(*pWin-〉cb)(pMsg);//这是函数调用的形式,函数是由函数指针给出的,pMsg为参数。
_EntranceCnt--;
}else{
WM_DefaultProc(pMsg);
}
}
#if GUI_DEBUG_LEVEL 〉= GUI_DEBUG_LEVEL_CHECK_PARA
else{
GUI_DEBUG_ERROROUT ("Max.message nesting exceeded,Message skipped.");
}
#endif
}
在发送消息的函数主要功能是:定义了一个静态整型变量_EntranceCnt,当它的值小于最大消息嵌套数时,则获取发送消息的窗口句柄,若该窗口的回调函数不为空则,该变量的值加一,并调用消息窗口回调函数处理该消息,处理结束后,该变量的值减一。若窗口回调函数为空,则调用默认的消息处理函数。由此可见,发送消息的函数中包括了对消息处理的函数指针,因此发送消息也就包含了对该消息的处理方式。
ucGUI对消息的处理是来一个处理一个,这种处理是同步的。它不用队列,而是采用两个变量,一个变量记载当前消息,一个变量记载先前消息,当前消息处理结束后,便用当前消息更新先前的消息。由于不采用队列,因此节省了大量的存储空间。因为当使用鼠标类设备时,当鼠标移动时会产生大量的消息,如果采用消息队列,则需要大量的存储空间来存储这些消息。由于嵌入式系统的存储空间有限,因此ucGUI没有使用消息队列,但这也会造成消息丢失。由于没有使用队列,因此每个消息的处理时间不能过长,否则会影响消息处理的灵敏度。
ucGUI在处理消息循环时存在一个消息多层挂起的问题,由于消息是同步处理的,没有存储消息的消息队列,因此,当在消息处理过程中再次调用WM_Exec1()而进入到新的消息LOOP当中,那么旧的消息循环就被永远挂起来了。这在打开多个对话框时会发生这种情况,应该避免。下面是ucGUI应用程序的大体结构:
void MainTask(void)//主任务:创建对话框
{
GUI_Init();//GUI初始化
WM_SetDesktopColor(GUI_BLUE);//设置桌面颜色为蓝色
WM_SetCreateFlags(WM_CF_MEMDEV);//使用存储器设备(防止屏幕闪烁)
GUI_ExecDialogBox (_aDialogCreate,GUI_COUNTOF(_aDialogCreate),&_cbCallback,0,0,0);//执行对话框
}
GUI_ExecDialogBox()这个函数的作用是创建对话框并进入窗体消息循环:
其代码如下(UCGUI3.24版,最新版已经进行了修改):
intGUI_ExecDialogBox (const GUI_WIDGET_CREATE_INFO*paWidget,
int NumWidgets, WM_CALLBACK* cb,WM_HWIN hParent,
int x0,int y0)
{
_cb=cb;
GUI_CreateDialogBox(paWidget,NumWidgets,_cbDialog,hParent,x0,y0);
while(_cb){//消息循环
if(!GUI_Exec())
GUI_X_ExecIdle();
}
return_r;
}
_cb是在Dialog.c中的定义(static WM_CALLBACK*_cb;)的一个全局变量,当_cb为非空时,就进行消息循环。由代码可见,当创建对话框时,会调用_cb这个消息处理函数,假设,又要创建新的对话框,那么_cb又会指向新的对话框的消息处理函数,这样原来的那个消息处理函数就再也进不去了,这点在编程时要特别注意。另外,由代码可见,_cb在GUI_Exec()函数中一定使用了。
3 结束语
若要在ucGUI中使用触摸屏,则必须将GUI_SUPPORT_TOUCH (Config目录下 GUIConf.h中定义的宏)设置为 1。若同时使用了操作系统ucOS,则要同时将GUI_OS也设置为 1。当用户触摸液晶屏时,系统通过调用GUI_TOUCH_Driver-Analog.c文件中的 GUI_TOUCH_Exec()获取当前位置的物理坐标值并将其转换成逻辑坐标值保存起来。为了能实时获取触点坐标需要单独建立一个处理触屏的任务。并将其优先级设置成较高级别。ucGUI使用消息循环时要避免当打开多个对话框时引起的消息挂起问题。当深刻理解了ucGUI的消息处理机制后,用它来创建基于图形用户界面的嵌入式应用程序是非常不错的选择。
[1]谭浩强.C语言程序设计(第二版)[M].北京:清华大学出版社,2001
[2]Labrosse J J.uCOS-II MicroC/OS-II the real time kernel second edtion[M].邵贝贝等译.北京:北京航空航天大学出版社,2001