基于STM32的uCGUI移植和优化 首先在开始这个说明之前,要简要说明下具体的环境: 编译工具:MDK4.20 开发板:安富莱v2版开发板 调试器:JLink v8盗版 移植篇 相信大家有移植经验的都知道,移植确实是一件非常墨迹的事情,怎么说呢,代码都是别人的,风格也是别人的,文件结构,定义之类都是别人的,看别人的东西是种进步,但是,也是一个痛苦的过程,因为有时候资料确实很少,而且有时候还是E文的,专业名词一大堆,我们根本没有办法想象工作量是多么的巨大. 不过事情都是这样,你不懂他的时候他就像是巨山,但是一旦你理解他的时候,你才会感觉到原来他是那么的简单(从我的经验上来看,至少应该是这样的). 好吧,闲话少说,我们就来开始我们的移植之旅把. 首先,我们需要准备的东西有uCGUI3.90,这个版本是大家现在用的比较多的,效率也比较高,别人都是这么评论的,至于其他版本的,我没有接触很多,所以不能过多评论. uCGUI有三个文件夹,一个是tool,这个文件夹是用来使用一些uCgui的上位机程序,基本都是字体和模板查看之类的.在sample文件夹下面是已经别人帮你写好了很多有用的东西,像跟操作系统有关的GUI_X或者一些模板(后面我们会用到的自己定义的Demo),或者是gui配置.后面再一一详细叙说这个文件夹的功能.在Start文件夹里面,这是我们最主要的文件夹.里面就包含了uCGUI的源代码,uCGUI的作者把源代码放进vc里面进行编译了(当然,这是用标准C语言写的程序,所以我们可以放在任何C语言平台下编译而不会担心兼容性问题,这个uCGUI在这方面做的算是完美了),所以,我们可以在vc平台下写界面,然后再把代码拷进我们的下位机编译器进行编译,这样子效率就会非常高了.(像51那时候写界面就是疯狂的一次一次的烧,真是纠结..). 然后这里放的就是uCGUI的源代码了,在GUI文件夹下面. 这则是每个文件夹的功能(参考uCGUI中文手册,ucgui.com翻译). 大概看一下就可以了,这个跟我们移植的关系不大,关键点是带*的可以不包含进去(待会配置会讲到.).然后其他的都要包含进去. 接着我们要把我们的文件包含进我们已经搭建好的工程,这里说明下我们的工程要求. 一般来说,我们要画一个图形,最基本的就是从点开始,从点到线,从点到面...,所以在已经建好的工程里面你要能点亮你的屏幕,能点出最基本的点,能填充出最基本矩阵(这是uCGUI最包含的函数),反正我移植的时候涉及到的包括三个函数,LCD_Init();LCD_Draw_Point(x,y,color),LCD_Fillcircuit(x1,x2,y1,y2).这三个函数是必须的,后面也会说明如何把这三个函数进行填充. 当我们把文件复制进去的时候,再加上我们一开始已经创建好的工程的时候,文件结构差不多就是这个样子了,截图如下 user包括,main函数就是我们初始化和函数调用,绘图用的文件,另外那几个文件相信大家都明白了把,tft_lcd.c就是你在,没有移植uCGUI的情况下,纯液晶屏驱动,这里建议把液晶屏的API和最底层驱动(API就是画圆啊,画椭圆啊,清除屏幕之类的,底层驱动就是驱动液晶屏的管脚运作,fsmc初始化,时钟配置之类的),不过我这里也是集成在一起了,比较懒,大家别学. 其他文件夹我都包含进去了,在没包含进去的时候,编译是可以通过的,但是,那么多文件包含进去,有些配置还是没有设定好的.所以会有错误,蛮编译一下.没事的. 这里我们需要修改的文件只有这几个:,,,这是ucgui开放给我们的用户层的文件,在ucgui中,lcdDrive文件夹要自己加进去,GUI_X.c也是,另外三个文件都是包含了,在GUIConf.h中 1 #ifndef GUICONF_H GUITouchConf.h是有关于触摸屏配置的,这里我们就略过了. 1 #ifndef LCDCONF_H 配置层的东西我们都已经搞定了,接下来我们要修改的是uCGUI开放给我们的用户层的东西,GUI_X.c可以直接拷进去,这个是用户层和系统层的关联文件,一些demo也会用到这个文件的时间函数或者延迟函数,所以这个文件拷进去放着就可以了. 1 #include "GUI.h" 在uCGUI和底层驱动的接口文件时LCDDriver.c,大家打开文件夹可以看到这几个文件:lcdwin.c,lcdnull.c,lcdDummy.c,这三个文件你随便修改哪个都行,一开始我是直接修改lcdnull.c的,不过一开始没经验,一直不能正常调用我的uCGUI函数,所以,我的队友告诉我,要修改另外一个(lcdwin.c),这次才反应过来,理论上,修改lcdnull.c也是可以的,就是一些细节性的东西可以要多注意点.下面我讲解下怎么修改lcdwin.c这个文件. 源文件我先贴出来: #if defined(WIN32) && !defined(LCD_SIMCONTROLLER) #include <windows.h> #include "LCD.h" #include "LCD_Private.h" /* include LCDConf.h */ #include "LCDSIM.h" #include "GUI_Private.h" #include "memory.h" /********************************************************************* * * Defines * ********************************************************************** */ #if LCD_BITSPERPIXEL <= 8 #define PIXELINDEX U8 #else #define PIXELINDEX WORD #endif #ifdef WIN32 #ifndef ASSERT #define ASSERT(Val) if (!(Val)) MessageBox(NULL,"...in file "__FILE__,"Assertion failed...",MB_OK); #endif #endif #ifdef LCD_ASSERT #undef LCD_ASSERT #endif #define LCD_ASSERT(v) ASSERT(v) #ifndef LCD_DISPLAY_INDEX #define LCD_DISPLAY_INDEX 0 #endif /********************************************************************* * * Macros for internal use */ #ifdef _DEBUG static int _CheckBound(unsigned int c) { unsigned int NumColors = LCD_BITSPERPIXEL > 8 ? 0xffff : (1 << LCD_BITSPERPIXEL) - 1; if (c > NumColors) { GUI_DEBUG_ERROROUT("LCDWin::SETPIXEL: parameters out of bounds"); return 1; } return 0; } #define SETPIXEL(x, y, c) if (!_CheckBound(c)) { LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX); } #else #define SETPIXEL(x, y, c) LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX) #endif #define XORPIXEL(x, y) _XorPixel(x,y) /********************************************************************* * * Static code * ********************************************************************** */ /********************************************************************* * * _XorPixel */ static void _XorPixel(int x, int y) { unsigned int Index = LCD_L0_GetPixelIndex(x,y); LCDSIM_SetPixelIndex(x, y, LCD_NUM_COLORS-1-Index, LCD_DISPLAY_INDEX); } /********************************************************************* * * _DrawBitLine1BPP */ static void _DrawBitLine1BPP(int x, int y, U8 const*p, int Diff, int xsize, const LCD_PIXELINDEX*pTrans) { LCD_PIXELINDEX Index0 = *(pTrans+0); LCD_PIXELINDEX Index1 = *(pTrans+1); x+=Diff; switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS|LCD_DRAWMODE_XOR)) { case 0: /* Write mode */ do { LCDSIM_SetPixelIndex(x++,y, (*p & (0x80>>Diff)) ? Index1 : Index0, LCD_DISPLAY_INDEX); if (++Diff==8) { Diff=0; p++; } } while (--xsize); break; case LCD_DRAWMODE_TRANS: do { if (*p & (0x80>>Diff)) LCDSIM_SetPixelIndex(x,y, Index1, LCD_DISPLAY_INDEX); x++; if (++Diff==8) { Diff=0; p++; } } while (--xsize); break; case LCD_DRAWMODE_XOR:; do { if (*p & (0x80>>Diff)) { int Pixel = LCDSIM_GetPixelIndex(x,y, LCD_DISPLAY_INDEX); LCDSIM_SetPixelIndex(x,y, LCD_NUM_COLORS-1-Pixel, LCD_DISPLAY_INDEX); } x++; if (++Diff==8) { Diff=0; p++; } } while (--xsize); break; } } /********************************************************************* * * _DrawBitLine2BPP */ #if (LCD_MAX_LOG_COLORS > 2) static void _DrawBitLine2BPP(int x, int y, U8 const * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) { LCD_PIXELINDEX Pixels = *p; int CurrentPixel = Diff; x += Diff; switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) { case 0: if (pTrans) { do { int Shift = (3 - CurrentPixel) << 1; int Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift; LCD_PIXELINDEX PixelIndex = *(pTrans + Index); SETPIXEL(x++, y, PixelIndex); if (++CurrentPixel == 4) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } else { do { int Shift = (3 - CurrentPixel) << 1; int Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift; SETPIXEL(x++, y, Index); if (++CurrentPixel == 4) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } break; case LCD_DRAWMODE_TRANS: if (pTrans) { do { int Shift = (3 - CurrentPixel) << 1; int Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift; if (Index) { LCD_PIXELINDEX PixelIndex = *(pTrans + Index); SETPIXEL(x, y, PixelIndex); } x++; if (++CurrentPixel == 4) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } else { do { int Shift = (3 - CurrentPixel) << 1; int Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift; if (Index) { SETPIXEL(x, y, Index); } x++; if (++CurrentPixel == 4) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } break; } } #endif /********************************************************************* * * _DrawBitLine4BPP */ #if (LCD_MAX_LOG_COLORS > 4) static void _DrawBitLine4BPP(int x, int y, U8 const * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) { LCD_PIXELINDEX Pixels = *p; int CurrentPixel = Diff; x += Diff; switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) { case 0: if (pTrans) { do { int Shift = (1 - CurrentPixel) << 2; int Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift; LCD_PIXELINDEX PixelIndex = *(pTrans + Index); SETPIXEL(x++, y, PixelIndex); if (++CurrentPixel == 2) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } else { do { int Shift = (1 - CurrentPixel) << 2; int Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift; SETPIXEL(x++, y, Index); if (++CurrentPixel == 2) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } break; case LCD_DRAWMODE_TRANS: if (pTrans) { do { int Shift = (1 - CurrentPixel) << 2; int Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift; if (Index) { LCD_PIXELINDEX PixelIndex = *(pTrans + Index); SETPIXEL(x, y, PixelIndex); } x++; if (++CurrentPixel == 2) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } else { do { int Shift = (1 - CurrentPixel) << 2; int Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift; if (Index) { SETPIXEL(x, y, Index); } x++; if (++CurrentPixel == 2) { CurrentPixel = 0; Pixels = *(++p); } } while (--xsize); } break; } } #endif /********************************************************************* * * _DrawBitLine8BPP */ #if (LCD_MAX_LOG_COLORS > 16) static void _DrawBitLine8BPP(int x, int y, U8 const*p, int xsize, const LCD_PIXELINDEX*pTrans) { LCD_PIXELINDEX pixel; if ((GUI_Context.DrawMode & LCD_DRAWMODE_TRANS)==0) { if (pTrans) { for (;xsize > 0; xsize--,x++,p++) { pixel = *p; SETPIXEL(x, y, *(pTrans+pixel)); } } else { for (;xsize > 0; xsize--,x++,p++) { SETPIXEL(x, y, *p); } } } else { /* Handle transparent bitmap */ if (pTrans) { for (; xsize > 0; xsize--, x++, p++) { pixel = *p; if (pixel) { SETPIXEL(x+0, y, *(pTrans+pixel)); } } } else { for (; xsize > 0; xsize--, x++, p++) { pixel = *p; if (pixel) { SETPIXEL(x+0, y, pixel); } } } } } #endif /********************************************************************* * * _DrawBitLine16BPP */ #if (LCD_BITSPERPIXEL > 8) static void _DrawBitLine16BPP(int x, int y, U16 const*p, int xsize, const LCD_PIXELINDEX*pTrans) { LCD_PIXELINDEX pixel; if ((GUI_Context.DrawMode & LCD_DRAWMODE_TRANS)==0) { if (pTrans) { for (;xsize > 0; xsize--,x++,p++) { pixel = *p; SETPIXEL(x, y, *(pTrans+pixel)); } } else { for (;xsize > 0; xsize--,x++,p++) { SETPIXEL(x, y, *p); } } } else { /* Handle transparent bitmap */ if (pTrans) { for (; xsize > 0; xsize--, x++, p++) { pixel = *p; if (pixel) { SETPIXEL(x+0, y, *(pTrans+pixel)); } } } else { for (; xsize > 0; xsize--, x++, p++) { pixel = *p; if (pixel) { SETPIXEL(x+0, y, pixel); } } } } } #endif /********************************************************************* * * Exported code * ********************************************************************** */ /********************************************************************* * * LCD_L0_DrawPixel * * Purpose: Writes 1 pixel into the display. */ void LCD_L0_DrawPixel(int x, int y) { if (GUI_Context.DrawMode & LCD_DRAWMODE_XOR) { XORPIXEL(x, y); } else { SETPIXEL(x, y, LCD_COLORINDEX); } } /********************************************************************* * * LCD_L0_DrawHLine */ void LCD_L0_DrawHLine(int x0, int y, int x1) { if (GUI_Context.DrawMode & LCD_DRAWMODE_XOR) { for (;x0 <= x1; x0++) { XORPIXEL(x0, y); } } else { for (;x0 <= x1; x0++) { SETPIXEL(x0, y, LCD_COLORINDEX); } } } /********************************************************************* * * LCD_L0_DrawVLine */ void LCD_L0_DrawVLine(int x, int y0, int y1) { if (GUI_Context.DrawMode & LCD_DRAWMODE_XOR) { while (y0 <= y1) { XORPIXEL(x, y0); y0++; } } else { while (y0 <= y1) { SETPIXEL(x, y0, LCD_COLORINDEX); y0++; } } } /********************************************************************* * * LCD_L0_FillRect */ void LCD_L0_FillRect(int x0, int y0, int x1, int y1) { for (; y0 <= y1; y0++) { LCD_L0_DrawHLine(x0,y0, x1); } } /********************************************************************* * * LCD_L0_DrawBitmap */ void LCD_L0_DrawBitmap(int x0, int y0, int xsize, int ysize, int BitsPerPixel, int BytesPerLine, const U8* pData, int Diff, const LCD_PIXELINDEX* pTrans) { int i; /* Use DrawBitLineXBPP */ for (i=0; i<ysize; i++) { switch (BitsPerPixel) { case 1: _DrawBitLine1BPP(x0, i+y0, pData, Diff, xsize, pTrans); break; #if (LCD_MAX_LOG_COLORS > 2) case 2: _DrawBitLine2BPP(x0, i+y0, pData, Diff, xsize, pTrans); break; #endif #if (LCD_MAX_LOG_COLORS > 4) case 4: _DrawBitLine4BPP(x0, i+y0, pData, Diff, xsize, pTrans); break; #endif #if (LCD_MAX_LOG_COLORS > 16) case 8: _DrawBitLine8BPP(x0, i+y0, pData, xsize, pTrans); break; #endif #if (LCD_BITSPERPIXEL > 8) case 16: _DrawBitLine16BPP(x0, i+y0, (const U16 *)pData, xsize, pTrans); break; #endif } pData += BytesPerLine; } } /********************************************************************* * * LCD_L0_DrawBitmap * * Purpose: * Sets the original position of the virtual display. * Has no function at this point with the PC-driver. */ void LCD_L0_SetOrg(int x, int y) { GUI_USE_PARA(x); GUI_USE_PARA(y); } /********************************************************************* * * Support for verification * * Purpose: * The following routines are implemented, but have no functionility * at this point. The reason is that these functions are supposed * to supervise the hardware, which for obvious reasons can not be * done in a simulation. */ #if LCD_VERIFY int LCD_GetErrStat(void) { return 0; } void LCD_ClrErrStat(void) { } int LCD_GetErrCnt (void) { return 0; } #endif /********************************************************************* * * LCD_On * LCD_Off * * (Not supported in Simulation) */ void LCD_Off (void) {} void LCD_On (void) {} /********************************************************************* * * LCD_L0_SetLUTEntry */ void LCD_L0_SetLUTEntry(U8 Pos, LCD_COLOR color) { LCDSIM_SetLUTEntry(Pos, color, LCD_DISPLAY_INDEX); } /********************************************************************* * * LCD_L0_Init */ int LCD_L0_Init(void) { return 0; } int LCD_L0_CheckInit(void) { return 0; } /********************************************************************* * * LCD_L0_ReInit * * Purpose: * This routine is supplied for compatibility and interchangability of * "C"-sources with embedded versions of the driver. It has no real * effect in the PC-version as there is simply no need to re-initialize * the LCD since it is just simulated anyhow. */ void LCD_L0_ReInit (void) {} unsigned LCD_L0_GetPixelIndex(int x, int y) { return LCDSIM_GetPixelIndex(x,y, LCD_DISPLAY_INDEX); } /********************************************************************* * * LCD_L0_XorPixel * * Purpose: * Inverts 1 pixel of the display. */ void LCD_L0_XorPixel(int x, int y) { XORPIXEL(x, y); } /********************************************************************* * * LCD_L0_SetPixelIndex * * Purpose: * Writes 1 pixel into the display. */ void LCD_L0_SetPixelIndex(int x, int y, int ColorIndex) { SETPIXEL(x, y, ColorIndex); } #else void LCDWin_c(void); void LCDWin_c(void) { } /* avoid empty object files */ #endif /* defined(WIN32) && defined(LCD_USE_WINSIM) */ 这个文件可能有点大,500多行,都是密密麻麻的代码,不过,我们要想使我们的uCgui能够使用,要修改的函数其实也就每几句. 我先给大家讲解下这里面函数的关系: #if LCD_BITSPERPIXEL <= 8 这里我们定义的是16位的像素点(在前面那三个配置文件里面),定义为word. #define SETPIXEL(x, y, c) LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX) 这里定义的是显示点的函数,我们需要把所有LCDSIM_SetPixelIndex(x, y, c, LCD_DISPLAY_INDEX),都换成你的画点函数,比如我这里使用的是 LCD_L0_SetPixelIndex(x轴坐标,y轴坐标,该点颜色);然后呢,就是修改LCD_L0_FillRect(int x0, int y0, int x1, int y1);这个是填充矩形函数, 你可以直接把函数里面的东西清空,然后写上自己在底层硬件驱动的api,我的函数优化得体无完肤了,基本没有办法再拷贝过来,所以待会讲这函数的时候,直接跳过去. 最后最重要的要修改的就是LCD_L0_Init(void);这函数里面可以直接填充你的初始化函数,好了,剩下基本就不需要修改了,其他的修改就留给优化了. 下面我给大家详细讲解下每个函数的用途 1 /********************************************************************* 移植讲到这边差不多就结束了,因为是凭着自己的感觉写的,所以可能很多东西讲的不是很清楚,有需要我精讲的可以联系我,我会陆续修改,接下来我们开始我们的优化之旅 补充: 优化篇 这里的优化会介绍两种模式,GPIO口模拟程序,FSMC模块模式. FSMC模式只有在STM32大容量模式的主控才有的,优化起来效果确实很是最好的,因为当你根据液晶屏ic配置好fsmc的时候,液晶屏的寄存器和GRAM就会被映射到系统的4G内存空间的某一块区域,我们往指定地址写数据或读数据,FSMC就会自动帮你把数据送到液晶屏控制器上面了,所以这里面我们省去了对GPIO管脚和接口的操作(...这个确实很强大,因为你可以花更多的时间来进行运算,每个点都是通过指令来产生的,产生指令也是需要时间的,同样,模拟IO口时序也是需要时间的,把这部分时间剩下来,是非常可观的),至于一些人不理解FSMC其实也不是很打紧啦,你就把GRAM和液晶屏寄存器想象成一个完整的外置SRAM就可以了,因为他们的时序都是同一个原理的,而FSMC就是会自动帮你模拟各种IO口操作,我把它简单的理解成(你写的io口操作程序,他用硬件来实现了). 相信大家都有看到别人移植好的demo程序,里面最开始以来就是一个speedDEMO,这个是测试每秒钟可以通过计算可以画多少个点,里面包含了随即填充矩阵函数. 分数越高,代表着你刷频也就是越快,当然,跟着我的脚步走,可以快到把刷这个字去掉(不严格意义上); 在做优化的时候,我们得提前做好准备,因为优化更多代表着是结构性的破坏,可读性的疯狂下降,因为从最底层的函数开始一层一层封装上去,使得函数可以很容易的去理解调用,我们优化就是拆开这些函数调用,并把一些有关硬件层的驱动进行修改,对算法进行更新. 举个函数调用的例子,填充矩阵: 这个比较大家可看可不看,涉及到一些汇编的知识.略过对后面的理解没有问题. 1 void fill_Rect(int v, int h, int x, int y) 这是我随便写的一个函数,我们差不多都是这样子调用一个写点的函数把,通过计算可得,假设我们要绘制一个100*100的矩阵, 那么就要调用10000次 Darw_Point函数,10000次Set_LCD_reg函数,10000Set_LCD_gram()函数,而每次调用一个函数的时候,要包括如下过程:
/*********************************************************************已经最优化 与优化前的进行对比:这里的的数据全部改成对地址进行操作(BANk4_LCD_D和C都是被映射了的内存地址).模拟IO口可能会有差别 大家在进行写函数的时候,对管脚进行配置都是直接调用库函数的setbit或者resetbit来进行的,我们可以直接查询库函数,对寄存器进行操作: void GPIO_SetBits ( GPIO_TypeDef * GPIOx, 库函数大家可以字节查询源代码,最后修改后demo代码如下: #ifdef HARDWARE_PLATFORM_ALI 把对管脚置高置底进行的操作完全跟改成寄存器操作,当然,你也可以改成对寄存器指针进行操作,不过效率是一样的,因为define的效果是复制,所以,通过观察源代码: 00127 #define GPIO_Pin_0 ((uint16_t)0x0001) 所以,当程序在编译的时候,也是把地址进行简单的拷贝,所以这部分功夫是可以完全剩下来的. 接下来是关于填充矩阵的函数操作: /********************************************************************* 这里有关于两种填充方式,都是避开函数操作,被注释掉的是对窗口进行操作的,而没被注释掉的是对线条进行填充. 对线条进行操作的相信大家应该非常了解了,这里详细解释下对窗口进行操作的一些细节: 窗口:也就是可以进行填充的区域,液晶驱动里面,每个物理像素对应的坐标已经是固定的,但是窗口可以不固定,窗口就是可以进行填充的区域,你如果要在窗口外面 进行填充,是无法进行的,同样的,当你填充到窗口边缘的时候,会自动跳转到下一行进行填充,只要你设定的点正确,那么整个你设定的窗口区域都会被填充完毕,这段期间 你要做的知识单纯的填充数据,不需要进行设定点的操作,也不需要换行,这样子屏幕填充矩阵操作看起来效果就不会有刷屏的感觉了. 填充行:对行进行填充,只需要在换行的时候进行坐标切换,我用整个函数,慢了30万个点每秒把.
在优化的时候,我只是抛砖引玉的给大家介绍下怎么用什么样的思路进行优化,细节性的东西还是要大家好好去琢磨的. 初稿到这边就差不多结束了,后面会陆续补充,只要大家想知道都可以直接进行联系. |
|