Chinaunix首页 | 论坛 | 博客
  • 博客访问: 15273052
  • 博文数量: 2005
  • 博客积分: 11986
  • 博客等级: 上将
  • 技术积分: 22535
  • 用 户 组: 普通用户
  • 注册时间: 2007-05-17 13:56
文章分类

全部博文(2005)

文章存档

2014年(2)

2013年(2)

2012年(16)

2011年(66)

2010年(368)

2009年(743)

2008年(491)

2007年(317)

分类: C/C++

2007-08-20 21:20:48

μC/GUI-v3.90a学习笔记4--建立多任务的改进方法讨论

μC/OS-ii和μC/GUI结合,设立几个task,分别控制一个独立面板界面,将GUI_Exec()放入
μC/OS-ii系统的OS_TaskIdle()空闲进程中,这样当任何一个面板发生失效区域时,位于
OS_TaskIdle()空闲进程中的GUI_Exec()都能立即检测到,然后扫描GUI中创建的所有窗体的
WM__FirstWin单向链表,找到失效窗体hwin,并使用同步方式执行相应hwin的消息回调处理函数
完成当前GUI_Context上下文对应的各种图形属性的绘制操作,但是因为GUI_Lock()函数中,
已经将全局GUI_Context图形上下文恢复成了空闲进程idle()对应的GUI_Context内容,
所以本来是各个task进程分而治之的事情,确因"全局GUI_Context图形上下文"不能顺利恢复到
相应失效窗体hwin所在task专有的局部GUI_Context图像上下文,而不能达到"名副其实",
基本被架空,本来需要便利的地方,却没能实现便利,使得各个task不能专心于自身,
如果不进行代码修改,那么各个task自己设置的个性专属的GUI_Context图像上下文,就必须在每次
执行回调函数进行绘制之前,一个个的再手工设置回去,这显然不实际,因为控件自身的有很多
默认的回调函数,都是使用当前GUI上下文全局量GUI_Context,从这里来看μC/GUI代码本身
并没有很好的提供对多线程支持机制,最好在单线程中完成所有的工作,这样代码执行效率也高,
当然在多task中,如果一个task使用OSTimeDly()延时一段时间,让出cpu供其他task使用GUI,
那么延时到达之后,该task在调用GUI_Lock的时候就会恢复该task的对应的个性GUI_Context.
但是真正绘制时使用的绘制GUI_Context却是OS_TaskIdle()空闲进程task对应的GUI_Context,
所以在真正绘制的时候出现了不一致,引发了麻烦.
void GUI_Lock(void) {
  if (_EntranceCnt == 0) {
    GUI_X_Lock();//占用资源
    _TaskIDLock = GUI_X_GetTaskId();//当前task id号
  } else {
    if (_TaskIDLock != GUI_X_GetTaskId()) {
      GUI_X_Lock();//如果另一个task需要使用资源,那么等待在这里,
                   //直到_TaskIDLock对应的task解锁GUI_Unlock资源互斥
      _TaskIDLock = GUI_X_GetTaskId();//本task获得执行机会,因为前一个task已经解锁,
                                      //所以此时_EntranceCnt=0
    }
  }
  if (++_EntranceCnt == 1) {
    int TaskNo = _GetTaskNo();//==1时表示,新的task进程已经获得执行机会

    if (TaskNo != _CurrentTaskNo) {
      if (_CurrentTaskNo>=0) {//与先前的task,对应的进程标识不同,那么保存,先前的,恢复现在的
        _Save[_CurrentTaskNo].Context = GUI_Context;//保存先前的GUI上下文
        GUI_Context = _Save[TaskNo].Context;//将当前task对应的GUI上下文恢复到全局量中
      }
      _CurrentTaskNo = TaskNo;
    }
  }
}
但是如果我们抛开代码执行效率,或者牺牲掉一些代码执行效率,并不会影响到我们的产品性能,
那么模块化实现μC/GUI就很有意义,因为那样的话每个task都认为自己独有一个自己的μC/GUI系统,
因为他们独有一个与自己task id对应的局部GUI_Context存储单元,能够很好的恢复现场.
所以首要解决的问题是:GUI_Exec()函数中失效窗体所属task对应的GUI_Context图形上下文如何恢复.
那么很自然的一种笨方式可以采用:在WM_Obj中添加一个GUI_Context类型指针GUI_CONTEXT *pcontext,
1.
GUI_Exec()->GUI_Exec1()->
WM_LOCK();//锁住,资源互斥,防止其他进程执行其他抢占互斥资源
_DrawNext();
WM_UNLOCK();
之后_DrawNext()->_Paint()->WM__ClipAtParentBorders()如果失效区域合法,那么在
WM_SelectWindow()函数之前加入我们的改变task对应GUI上下文指针的操作,
GUI_Context = *pWin->pcontext;//添加的内容,恢复该win对应的task专有GUI上下文
2.
同样创建窗体过程如下:
控件创建时都会调用WM_CreateWindowAsChild()把自己登记到hParent上和WM__FirstWin单向链表中,
在_AddToLinList()之前加入:SetTaskGUI_Context(pWin);
它的具体实现如下:
void SetTaskGUI_Context(WM_Obj* pWin)
{int i;
  for(i = 0;i < GUI_MAXTASK;i++)
  {U32 TaskId = GUI_X_GetTaskId();
    if(_Save[i].TaskID == TaskId)
    {
      pWin->pcontext = &_Save[i].Context;
      return;
    }
  }
  pWin->pcontext = &GUI_Context;
}
3.
通过上面两个函数的添加基本可以了,以后所有的窗体创建操作都要在task进程内部来做,
同时为了能够正常运行,各个task进程执行代码的书写模式如下:
void xxxx_thread(void *pdata)
{
  Register_Task();//首先注册登记线程自己
  //-----------------------
  //好了,下面xxxx_thread()进程就可以认为自己独占μC/GUI了
  //以后遇到进程切换,GUI_Context会被保存到_Save[i].Context中,
  //同时当从其他进程切换到该xxxx_thread()进程时,GUI_Context也会从_Save[i].Context中恢复
  GUI_Context就可以认为是该xxxx_thread()进程专用的了
  for(;;)
  {
    ...μC/GUI中各种动态效果设置代码...
  }
}

:
//首先向wm/WM.h结构体添加pcontext
typedef struct {
  GUI_RECT Rect; /* outer dimensions of window */
  GUI_RECT InvalidRect; /* invalid rectangle */
  WM_CALLBACK* cb; /* ptr to notification callback */
  WM_HWIN hNextLin; /* Next window in linear list */
  WM_HWIN hParent;
  WM_HWIN hFirstChild;
  WM_HWIN hNext;
  U16 Status;     /* Some status flags */
  GUI_CONTEXT *pcontext;//添加
} WM_Obj;
//1.向GUI/Core/GUITask.c添加代码如下
//--------------------------------------------------
//gliethttp--实现多线程下GUI的绘制独立性
#include "WM.h"
extern U8 WM_IsActive;
void SetTaskGUI_Context(WM_Obj* pWin)
{int i;
  if(WM_IsActive)
  {
    for(i = 0;i < GUI_MAXTASK;i++)
    {U32 TaskId = GUI_X_GetTaskId();
      if(_Save[i].TaskID == TaskId)
      {
        pWin->pcontext = &_Save[i].Context;
        return;
      }
    }
  }
  pWin->pcontext = &GUI_Context;
}
void Register_Task(void)
{
  GUI_Lock();//注册登记进程对应的_Save[i].Context位置和相应的_Save[i].TaskID.
  GUI_Unlock();
}
//2.向wm/WM.c->WM_CreateWindowAsChild()函数中添加
SetTaskGUI_Context(pWin);//添加的内容
_AddToLinList(hWin);//原有内容

//3.向wm/WM.c->_Paint()函数中添加
GUI_Context = *pWin->pcontext;//添加的内容,恢复该win对应的task专有GUI上下文
WM_SelectWindow(hWin);//原有内容

//4.最后一步:屏蔽wm/WM.c->WM_SetDefault()函数
void WM_SetDefault(void) {
// GL_SetDefault();
// GUI_Context.WM__pUserClipRect = NULL;
}
好了,修改工作已经全部完毕,以后多任务情况下,每个任务就可以不用担心绘制时候出问题了,
这时每个task才是真正意义上独占μC/GUI了.

改动的几个文件下载:μCGUI多任务改进尝试.rar

阅读(4253) | 评论(12) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2009-12-10 09:15:20

顺便请教下博主和楼上的兄弟,UCGUI我想实现横屏->竖屏。我改了以下几个配置都不行,请博主指点下。谢谢。 #define LCD_SWAP_RB (1) #define LCD_SWAP_XY (1) #define LCD_MIRROR_X (1) #define LCD_MIRROR_Y (0) 我这边的环境是:ADS,ARM7自带TFT控制器,4.3寸彩屏,UCGUI3.90A。我把画点函数中的XY位置交换下,显示的字符是镜像了,按照手册上的介绍只要配置LCD_MIRROR_X Y 就行了,结果百思不得其解。是不是横屏竖屏只是对外扩LCD控制器才起作用?可是我在VC上模拟的时候就采用了samples中带LCD控制器来仿真,结果是连镜像的结果都没有实现出来。

chinaunix网友2009-10-29 12:45:45

好久没来师兄这里学习了,7月份毕业到现在一直在忙工作的事情,现在已经稳定了,选择了做驱动这块,呵呵,以后要多来这吸取“功力”~

chinaunix网友2009-03-03 18:23:32

不错,能够自己分析出来,理解更加深刻了,Congratulations! :)

chinaunix网友2009-02-25 13:28:22

师兄,我已经找到原因了,这些天真是麻烦你了,谢谢你的帮助。原来是ucos的任务里调用ucgui的函数时是要处理比较大的数据的,我初始化任务堆栈时给的空间太小了,才导致程序跑飞的,呵呵,还有好多要学习啊~

chinaunix网友2009-02-21 00:42:45

师兄,是不是移植ucgui的时候还要设置一些中断服务子程序才行,我看网上的好多移植步骤里都没提到