emwin移植

emwin是续开源uc/GUI 3.98之后的不开源版本,所以它们的API函数几乎相同,只是emwin作了很多改进,增加了很多新功能,并且已经有很多免费的版本支持于ST、NXP等大公司的芯片上,它的移植接口也作了很大的改进,如果有了以前开源版本的移植,移植emwin不在话下,本文主要讲述移植到NXP的LPC1788上。

1、不开源的emwin其实很简单,我们可以从NXP的官方得到,加载到MDK的源文件目录如下:

嵌入式GUI之--【3】移植emWin <wbr>5.22

        从上图可以看到,不开源的emwin实在比起以前的uc/GUI简洁了很多,提供lib的库,也使编译速度快了很多。另外的hzk12.c是中文字库,下面会讲述如何在emwin上增加中文字库。

 

2、按顺序,先移植GUIConf.c文件,主要有以下代码:

#define GUI_NUMBYTES  (1024 * 64)

#define GUI_BLOCKSIZE (500)

U32 static _aMemory[GUI_NUMBYTES / 4] __attribute__ ((section ("GUI_RAM"), zero_init));

        前面两个宏定义是定义GUI缓存的大小,可以根据需要自行定义,另外定义的_aMenory数组是很有意思的,把这个数组的空间命名为“GUI_RAM”,是为了方便在分散加载里面,把GUI的缓存放在SDRAM或者其它地方。

        另外,GUIConf.c文件里面还要实现一个函数void GUI_X_Config(void),如下:

//旧版本的emWin

void GUI_X_Config(void) {

  GUI_ALLOC_AssignMemory(_aMemory, GUI_NUMBYTES);

  GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);

  GUI_SetDefaultFont(GUI_FONT_6X8);  

  GUITASK_SetMaxTask(GUI_MAX_TASK);

}

在 emWin V5.40的版本里面,变成下面

#define GUI_NUMBYTES  (1024*64)//这里我给emWin分配64K的内存给他使用


void GUI_X_Config(void) {
  //
  // 32 bit aligned memory area
  //
  static U32 aMemory[GUI_NUMBYTES / 4];
  //
  // Assign memory to emWin
  //
  GUI_ALLOC_AssignMemory(aMemory, GUI_NUMBYTES);
  //
  // Set default font
  //
  GUI_SetDefaultFont(GUI_FONT_6X8);
}

        这个函数会在初始化是调用,完成内存分配,默认字体,多任务的配置(无操作系统是可以不需要)。

 

3、完成GUIConf.c文件后,接下来是GUIConf.h文件,该文件主要完成一些配置宏的定义:

#define GUI_NUM_LAYERS            2    // Maximum number of available layers

#define GUI_OS                   (1)   // Compile with multitasking support

#define GUI_MAX_TASK             (1)   // Max. number of tasks that may call emWin

#define GUI_SUPPORT_TOUCH        (1)  // Support a touch screen (req. win-manager)

#define GUI_DEFAULT_FONT         &GUI_Font6x8

#define GUI_SUPPORT_MOUSE        1    // Support a mouse 

#define GUI_WINSUPPORT           1    // Use window manager 

#define GUI_SUPPORT_MEMDEV       0    // Memory device package available 

#define GUI_SUPPORT_DEVICES      1    // Enable use of device pointers 

        根据需要进行配置就行了,每个宏都有注释。

 

4、上面两个文件都没什么,接下来的LCDConf.c才是大块头,该文件内容稍多些,这里拿出重点说说。

#define XSIZE_PHYS 240

#define YSIZE_PHYS 240

#define COLOR_CONVERSION  GUICC_222

#define PIXEL_WIDTH  4

int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void *pData)
{
    int r;

    switch(Cmd)
    {
        case LCD_X_INITCONTROLLER:
        {
            //
            // Called during the initialization process in order to set up the
            // display controller and put it into operation. If the display
            // controller is not initialized by any external routine this needs
            // to be adapted by the customer...
            //
            // ...
            BspLcdInit();//初始化LCD控制器
            return 0;
        }

        case LCD_X_SETVRAMADDR:
        {
            //
            // Required for setting the address of the video RAM for drivers
            // with memory mapped video RAM which is passed in the 'pVRAM' element of p
            //
            LCD_X_SETVRAMADDR_INFO *p;
            p = (LCD_X_SETVRAMADDR_INFO *)pData;
            //...
            p->pVRAM = VRAM_ADDR;//这里映射显存地址
            return 0;
        }

        case LCD_X_SETORG:
        {
            //
            // Required for setting the display origin which is passed in the 'xPos' and 'yPos' element of p
            //
            LCD_X_SETORG_INFO *p;
            p = (LCD_X_SETORG_INFO *)pData;
            //...
            p = p; // To avoid warning
            return 0;
        }

        case LCD_X_SETSIZE:
        {
            //
            // Required for setting the layer position which is passed in the 'xPos' and 'yPos' element of pData
            //
            LCD_X_SETSIZE_INFO *p;
            p = (LCD_X_SETSIZE_INFO *)pData;
            _axSize[LayerIndex] = p->xSize;
            _aySize[LayerIndex] = p->ySize;
            break;
        }

        case LCD_X_SHOWBUFFER:
        {
            uint32_t Addr;

            //
            // Required if multiple buffers are used. The 'Index' element of p contains the buffer index.
            //
            LCD_X_SHOWBUFFER_INFO *p;
            p = (LCD_X_SHOWBUFFER_INFO *)pData;
            //...
            Addr = ((uint32_t)(VRAM_ADDR) + p->Index * XSIZE_PHYS * YSIZE_PHYS);

            BspLcdUpdate((uint8_t *)Addr);

            GUI_MULTIBUF_ConfirmEx(0, p->Index);

            return 0;
        }

        case LCD_X_SETLUTENTRY:
        {
            //
            // Required for setting a lookup table entry which is passed in the 'Pos' and 'Color' element of p
            //
            LCD_X_SETLUTENTRY_INFO *p;
            p = (LCD_X_SETLUTENTRY_INFO *)pData;
            //...
            p = p; // To avoid warning
            
            return 0;
        }

        case LCD_X_ON:
        {
            //
            // Required if the display controller should support switching on and off
            //
            return 0;
        }

        case LCD_X_OFF:
        {
            //
            // Required if the display controller should support switching on and off
            //
            // ...
            return 0;
        }

        default:
            r = -1;
    }

    return r;
}

 

        上面的宏很好里面,LCD的像素,显示颜色的模式及像素宽度,我这里用的是6位色的,把它定义成8位的。

        接下来是一个关键,如果你在裸机的情况下实现过LCD显示就很好办了,主要有以下两部分:

        1)、初始化,在LCD_X_DisplayDriver函数的case LCD_X_INITCONTROLLER:里增加初始化代码:

        是一个函数,还是两个函数,还是其它函数名字,你自己喜欢。

        2)、像素操作函数,在LCD_X_DisplayDriver函数的case LCD_X_SETORG:里增加像素操作函数:

        __SetDisplayOrigin(p->xPos, p->yPos);

        一样的道理,函数名字自己喜欢,但函数的两个函数是必需这样的。

 

5、好了,其实经过上面几个步骤,你的GUI基本可以显示了,好像很简单的样子。是的,接下来加入操作系统相关函数,因为在操作系统的多任务同时操作GUI函数时,必然要考虑保护机制,其实也很简单,官方都有给出代码,稍作修改即可,重要的是要理解其用意。我这里用的是FreeRTOS,如果你在某个地方找到一个GUI_X_OS.c的文件,恭喜你,这个文件基本可以用的了,下面讲讲这个文件的原理。

        1)、加入GUI的时间相关函数

int  GUI_X_GetTime (void) 

{

    return ((int) osKernelSysTick());

}

 

void  GUI_X_Delay (int ms) 

{

    osDelay( ms );

}

        这很好理解,只接调用FreeRTOS里面的时间相关函数就行了。        2)、GUI支持多任务的核心都是围绕下面的3个变量,在uCOS-II里面很基本的一个东西:信号量、邮箱,如果这个不懂,好好玩通了FreeRTOS再来搞。 

static osMutexId osMutex;
static osSemaphoreId osSemaphore;

 

void  GUI_X_InitOS (void)

    /* Create Mutex lock */
  osMutexDef(MUTEX);
 
  /* Create the Mutex used by the two threads */
  osMutex = osMutexCreate(osMutex(MUTEX));
 
  /* Create Semaphore lock */
  osSemaphoreDef(SEM);
 
  /* Create the Semaphore used by the two threads */
  osSemaphore= osSemaphoreCreate(osSemaphore(SEM), 1);

}

        GUI_X_InitOS函数主要先建立osMutex,osSemaphore。

        3)、建立互斥相关函数

void  GUI_X_Lock (void)

    osMutexWait(osMutex , osWaitForever) ;

}

void  GUI_X_Unlock (void)

    osMutexRelease(osMutex);

}

 

U32  GUI_X_GetTaskId (void) 

   return ((U32) osThreadGetId());

}

        GUI_X_Lock、GUI_X_Unlock两个函数通过信号量,来达到一个互斥的作用,该信号量是当作2值量来用的,就相当于一个厕所的锁有两个状态,要么开、要么关,一个人进了厕所后就关上,出来时再打开,来达到一个厕所里面同时只有一个人的保护作用(除非你喜欢同时两个人一起上大急,呵呵)。GUI_X_GetTaskId函数是用于获取当前任务的ID。这三个函数,GUI里面怎么调用你不需要理会,你告诉GUI,它自己会调用的(emwin不开源,你知道也没用)。

        3)、等待事件发生,与任务上锁的原理其实都相同,只是官方给的代码里面通过邮箱的方式来实现,原理是一样的

void GUI_X_WaitEvent (void)

{

   osSemaphoreWait(osSemaphore , osWaitForever) ;

}

 

void GUI_X_SignalEvent (void) 

{

   osMutexRelease(osSemaphore);

}

 

6、好了,多任务也有了,现在就放心地在FreeRTOS里面自由调用吧。但,好像事情还没结束,我们用的是触摸屏,不管是电容屏、电阻屏都好,道理都是一样的,在早期开源的版本中,我们可以直接在开源代码的GUI_PID.C源文件里的void GUI_PID_StoreState(const GUI_PID_STATE *pState)函数里面增加,但在emwin里面,该方法已行不通,可以用以下的办法:

        在FreeRTOS里面本来会用一个单独的任务用于刷新界面,这里就把触摸事件也通过这个任务里面更新,代码如下:

void TaskGUI(void *pvData)

{

    GUI_PID_STATE State;

 

    while(1) {

        if (!(LPC_GPIO2->PIN & 1 << 10)) {                      // 有按键按下

            if (__checkUpdateTouch()) {

                State.Pressed = 1;

                State.x = measureX;

                State.y = measureY;

                GUI_PID_StoreState(&State);

                GUI_TOUCH_Exec();                             // 刷新坐标

            }

        } else {

            GUI_PID_GetState(&State);

            State.Pressed = 0;

            GUI_PID_StoreState(&State);

        }

        WM_Paint(0);

        GUI_Delay(1);

    }

        其中的  

__checkUpdateTouch()

  函数是自己写的获取当前触摸屏的坐标值函数,更新后的值在measureX、measureY这两个全局变量里,当然不用全局变量,通过函数返回等各种方法都是可以的,道理都是一样:当我们改变不了源代码时,调用API接口把当前的触摸事件及坐标更新给GUI内核处理。

 

7、经过上面几步操作,emwin的移植其实已经结束了,这里面要增加一点:emwin默认没有中文字库,需要我们自行增加字库存。

        字库文件就是hzk12.c了,为什么把它方在GUI内核同个目录里面,因为它应属于内核里面的一部分,该源文件里面把中文的字库都封装成GUI_FLASH const GUI_FONT GUI_FontHZ12,然后在GUI.h头文件里的大约第1724行左右,增加以下宏:

extern const GUI_FONT GUI_FontHZ12;

#define GUI_FONT_HZ12           &GUI_FontHZ12

        这样在你的emwin里面就多了一个名称为GUI_FONT_HZ12的中文字体了,hzk12.c在网上应该也可以找到,有需要的也可以联系我。

 

    经过上边的移植,emwin就可以用了,其它的函数调用就看官方的文档操作即可,这篇文章是学习网上的资料,同时结合自己开发项目使用了FreeRTOS操作系统进行修改的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值