嵌入式GUI是嵌入式开发中经常要用到的组件,上一篇我们分享了LVGL的移植和使用,LVGL得益于其免费开源的策略,以及社区的不断开发维护,功能很完善,使用简单,资源多,现在越来越流行。提到嵌入式GUI这里免不得要提到嵌入式GUI领域曾经绝对的爆款uC/GUI,虽然它实际就是emWin,但是uC/GUI更出名,相信很多人就是通过其接触的嵌入式GUI开发的,甚是怀念当初对着3.9版本的源码以及其文档,买一本计算机图形学的书籍,从画点,到画线等算法,到各个组件开发,学习的过程。而提到uC/GUI又免不得要回忆下Micrium及其其提供的嵌入式开发的uC/XX全家桶。
相信很多嵌入式开发者对Micrium有着很深的感情,其uC/XXX全家桶提供了常用嵌入式开发组件,基本是一条龙整套的解决方案。其提供的源码,文档是学习的宝贵资料,其源码的风格和规范,文档的组织与详尽程度是我目前所遇到的最最顶流,个人对于创始人Jean J.Labrosse也是非常的倾佩,其分享精神值得敬佩,其各个组件的文档比任何一本相关的书籍都适合作为学习使用,尤其是那本著名的《μC/OS, The Real-Time Kernel》是很多人入门RTOS的圣经,配合其源码,本书是至高无上学习RTOS的绝佳宝典,uC/OS最早很多人接触可能是通过邵贝贝翻译的本书来了解的,也是该翻译版让uC/OS甚至RTOS在国内风靡。个人更倾向于直接阅读官网原版文档,当时自己就是打印了官方的文档,反复阅读,翻到每一页都掉渣,直到每一行代码都彻底搞懂,也是从此自己完全掌握了RTOS的原理,也可以毫无压力的自行实现一个RTOS,现在各种RTOS满天飞,其实万变不离其宗。到后来研究文件系统,研究tcp/ip协议栈,USB协议栈等等都是基于Micrium的源码和文档进行的。所以可以说个人嵌入式开发的功力,有一大半是Micrium提供的,相信也有很多类似经历的嵌入式开发者会深有同感。
Micrium以提供uC/OS和uC/OSII而闻名,还包括其他很多的嵌入式组件,有些是自己开发的,而有些实际并不是Micrium自己开发的,但是通过商业手段纳入了自己的商业版图,这也是商业上的一个决策,毕竟提供一整套嵌入式解决方案是很有吸引力的,我之前在某电力行业头部企业工作公司就采购了uC/FS和uC/OSIII,而采购uC/FS就是为了解决文件系统损坏的问题而推动的,最终也不负所望,uC/FS确实其可靠性经得起考验。。
自从Silicon Labs收购Micrium后(现在的网址是https://www.silabs.com/developers/micrium),相应的也对uC/XXX全家桶进行了免费开源而之前uC/OS等源码是开源但并不是免费的。现在由weston-embedded进行维护,网址:https://weston-embedded.com/micrium/overview.而其文档也可从https://micrium.atlassian.net/wiki/spaces获取(强烈推荐,其文档是相关组件开发绝佳的学习资料),代码可以从https://github.com/weston-embedded/获取。
而做过GUI开发的相信对uC/GUI也会印象深刻,流出的3.9版本的源码,以及其对应的DOC文档,也是学习GUI的绝佳资料,实际uC/GUI并不是Micrium自己开发的,而是SEGGER的emWin参考官网:https://www.segger.com/products/user-interface/emwin/,同时STM32的STemWin也是来源于emWin(其是通过lib库提供的,不提供源码,用过的肯定知道其一个防止在非STM32芯片上使用的一个小招数即必须开启CRC的RCC时钟)。而记得自已12年的第一份工作中就是使用uC/GUI设计了一个摩托车的仪表GUI,虽然只是作为演示并没有产品化,但也是实战的第一步,映像深刻。
这一篇我们就以网上能找到的比较新的版本的emWin506为例,分享其移植移植使用。
emWin是商业软件,不管是STemWin(使用STM32的芯片可以免费使用其库)还是uC/GUI实际都是不提供源码的(早期uC/GUI版本3左右是有源码的,网上能找到),都是以库形式提供。现在网上可以找到emWin5.06的源码,我们就以其来分享移植和使用。这里分享一个下载地址,仅供学习之用,如需使用可以购买商业授权。
链接:https://pan.baidu.com/s/11R3ySVSEXHwOCYc8ZvLYdQ?pwd=qgwb
提取码:qgwb
将emWin506-Src复制到自己的工程目录,添加以下文件到工程
emWin506-Src/GUI/Antialias/*.c
emWin506-Src/GUI/ConvertColor/*.c
emWin506-Src/GUI/ConvertMono/*.c
emWin506-Src/GUI/Core/*.c
emWin506-Src/GUI/Font/*.c
emWin506-Src/GUI/MemDev/*.c
emWin506-Src/GUI/Widget/*.c
emWin506-Src/GUI/WM/*.c
添加以下头文件包含路径
emWin506-Src/GUI/Core/
emWin506-Src/GUI/WM/
emWin506-Src/GUI/Widget
然后复制以下配置和接口文件到自己的工程中去,并添加头文件包含路径,以下文件分别是GUI需要实现的接口和配置,LCD需要实现的接口的配置,显示驱动接口。
GUI_X.c
LCD_X.c
GUIConf.h
LCDConf.h
GUIDRV_Template.c
GUIDRV_Template.h
现在就一般就可以编译通过了,或者可能有一两处问题,后面移植过程再修改。
即前面的GUIDRV_Template.c和GUIDRV_Template.h的实现。
可以复制GUI\DisplayDriver下的GUIDRV_Template.c/h,并添加到工程,放在GUI源码路径外,因为属于需要适配的实现,GUI的源码是无需任何修改的和其分开,当然也要添加头文件包含路径。
显示需要在GUIDRV_Template.c中实现结构体GUIDRV_Template_API,实际模板已经实现了框架,最低只需要用户实现
_SetPixelIndex和_GetPixelIndex两个函数,其他接口在优化性能时可以优化,比如_FillRect等,默认是基于前面的_SetPixelIndex和_GetPixelIndex两个接口实现。
GUI.h中宏WIN32定义了,且宏LCD_SIMCONTROLLER为0则使用GUIDRV_Win_API否则使用GUIDRV_Template_API,我们这里就是要实现GUIDRV_Template_API。
/*********************************************************************
*
* Display drivers
*/
//
// Addresses
//
extern const GUI_DEVICE_API GUIDRV_Win_API;
extern const GUI_DEVICE_API GUIDRV_Template_API;
//
// Macros to be used in configuration files
//
我这里实现的GUIDRV_Template.c如下,实际只需要修改两处,即写点和读点处,调用我们之前的st7789的接口实现
/*********************************************************************
* SEGGER Microcontroller GmbH & Co. KG *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1996 - 2010 SEGGER Microcontroller GmbH & Co. KG *
* *
* Internet: www.segger.com Support: support@segger.com *
* *
**********************************************************************
** emWin V5.06 - Graphical user interface for embedded applications **
emWin is protected by international copyright laws. Knowledge of the
source code may not be used to write a similar product. This file may
only be used in accordance with a license and should not be re-
distributed in any way. We appreciate your understanding and fairness.
----------------------------------------------------------------------
File : GUIDRV_Template.c
Purpose : Template driver, should be used as starting point
for new display drivers.
---------------------------END-OF-HEADER------------------------------
*/
#include
#include
#include "st7789_itf.h"
#include "LCD_Private.h"
#include "GUI_Private.h"
#include "LCD_SIM.h"
#include "LCD_ConfDefaults.h"
/*********************************************************************
*
* Defines
*
**********************************************************************
*/
/*********************************************************************
*
* Macros for MIRROR_, SWAP_ and LUT_
*/
#if (!defined (LCD_LUT_COM) && !defined(LCD_LUT_SEG))
#if (!LCD_MIRROR_X && !LCD_MIRROR_Y && !LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) x
#define LOG2PHYS_Y(x, y) y
#elif (!LCD_MIRROR_X && !LCD_MIRROR_Y && LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) y
#define LOG2PHYS_Y(x, y) x
#elif (!LCD_MIRROR_X && LCD_MIRROR_Y && !LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) x
#define LOG2PHYS_Y(x, y) LCD_YSIZE - 1 - (y)
#elif (!LCD_MIRROR_X && LCD_MIRROR_Y && LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) y
#define LOG2PHYS_Y(x, y) LCD_XSIZE - 1 - (x)
#elif ( LCD_MIRROR_X && !LCD_MIRROR_Y && !LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) LCD_XSIZE - 1 - (x)
#define LOG2PHYS_Y(x, y) y
#elif ( LCD_MIRROR_X && !LCD_MIRROR_Y && LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) LCD_YSIZE - 1 - (y)
#define LOG2PHYS_Y(x, y) x
#elif ( LCD_MIRROR_X && LCD_MIRROR_Y && !LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) LCD_XSIZE - 1 - (x)
#define LOG2PHYS_Y(x, y) LCD_YSIZE - 1 - (y)
#elif ( LCD_MIRROR_X && LCD_MIRROR_Y && LCD_SWAP_XY)
#define LOG2PHYS_X(x, y) LCD_YSIZE - 1 - (y)
#define LOG2PHYS_Y(x, y) LCD_XSIZE - 1 - (x)
#endif
#else
#if ( defined (LCD_LUT_COM) && !defined(LCD_LUT_SEG))
#define LOG2PHYS_X(x, y) x
#define LOG2PHYS_Y(x, y) LCD__aLine2Com0[y]
#elif (!defined (LCD_LUT_COM) && defined(LCD_LUT_SEG))
#define LOG2PHYS_X(x, y) LCD__aCol2Seg0[x]
#define LOG2PHYS_Y(x, y) y
#elif ( defined (LCD_LUT_COM) && defined(LCD_LUT_SEG))
#define LOG2PHYS_X(x, y) LCD__aCol2Seg0[x]
#define LOG2PHYS_Y(x, y) LCD__aLine2Com0[y]
#endif
#endif
/*********************************************************************
*
* Types
*
**********************************************************************
*/
typedef struct {
U32 VRAMAddr;
int xSize, ySize;
int vxSize, vySize;
int vxSizePhys;
int BitsPerPixel;
} DRIVER_CONTEXT;
/*********************************************************************
*
* Static functions
*
**********************************************************************
*/
/*********************************************************************
*
* _SetPixelIndex
*
* Purpose:
* Sets the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, int PixelIndex) {
#ifdef WIN32
LCDSIM_SetPixelIndex(x, y, PixelIndex, pDevice->LayerIndex);
#else
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
GUI_USE_PARA(PixelIndex);
{
//
// Write into hardware ... Adapt to your system
//
// TBD by customer...
//
st7789_itf_set_pixel(x,y,PixelIndex);
//printf("%d %d %x\r\n",x,y,PixelIndex);
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
#endif
}
/*********************************************************************
*
* _GetPixelIndex
*
* Purpose:
* Returns the index of the given pixel. The upper layers
* calling this routine make sure that the coordinates are in range, so
* that no check on the parameters needs to be performed.
*/
static unsigned int _GetPixelIndex(GUI_DEVICE * pDevice, int x, int y) {
unsigned int PixelIndex;
#ifdef WIN32
PixelIndex = LCDSIM_GetPixelIndex(x, y, pDevice->LayerIndex);
#else
//
// Convert logical into physical coordinates (Dep. on LCDConf.h)
//
#if (LCD_MIRROR_X == 1) || (LCD_MIRROR_Y == 1) || (LCD_SWAP_XY == 1)
int xPhys, yPhys;
xPhys = LOG2PHYS_X(x, y);
yPhys = LOG2PHYS_Y(x, y);
#else
#define xPhys x
#define yPhys y
#endif
GUI_USE_PARA(pDevice);
GUI_USE_PARA(x);
GUI_USE_PARA(y);
{
//
// Write into hardware ... Adapt to your system
//
// TBD by customer...
//
//PixelIndex = 0;
PixelIndex = st7789_itf_get_pixel(x,y);
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
#endif
return PixelIndex;
}
/*********************************************************************
*
* _XorPixel
*/
static void _XorPixel(GUI_DEVICE * pDevice, int x, int y) {
LCD_PIXELINDEX PixelIndex;
LCD_PIXELINDEX IndexMask;
PixelIndex = _GetPixelIndex(pDevice, x, y);
IndexMask = pDevice->pColorConvAPI->pfGetIndexMask();
_SetPixelIndex(pDevice, x, y, PixelIndex ^ IndexMask);
}
/*********************************************************************
*
* _DrawHLine
*/
static void _DrawHLine (GUI_DEVICE * pDevice, int x0, int y, int x1) {
LCD_PIXELINDEX ColorIndex;
if (GUI_Context.DrawMode & LCD_DRAWMODE_XOR) {
for (; x0 <= x1; x0++) {
_XorPixel(pDevice, x0, y);
}
} else {
ColorIndex = LCD__GetColorIndex();
for (; x0 <= x1; x0++) {
_SetPixelIndex(pDevice, x0, y, ColorIndex);
}
}
}
/*********************************************************************
*
* _DrawVLine, not optimized
*/
static void _DrawVLine (GUI_DEVICE * pDevice, int x, int y0, int y1) {
LCD_PIXELINDEX ColorIndex;
if (GUI_Context.DrawMode & LCD_DRAWMODE_XOR) {
for (; y0 <= y1; y0++) {
_XorPixel(pDevice, x, y0);
}
} else {
ColorIndex = LCD__GetColorIndex();
for (; y0 <= y1; y0++) {
_SetPixelIndex(pDevice, x, y0, ColorIndex);
}
}
}
/*********************************************************************
*
* _FillRect
*/
static void _FillRect(GUI_DEVICE * pDevice, int x0, int y0, int x1, int y1) {
for (; y0 <= y1; y0++) {
_DrawHLine(pDevice, x0, y0, x1);
}
}
/*********************************************************************
*
* Draw Bitmap 1 BPP
*/
static void _DrawBitLine1BPP(GUI_DEVICE * pDevice, unsigned x, unsigned y, U8 const GUI_UNI_PTR * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
LCD_PIXELINDEX IndexMask, Index0, Index1, Pixel;
Index0 = *(pTrans + 0);
Index1 = *(pTrans + 1);
x += Diff;
switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
case 0:
do {
_SetPixelIndex(pDevice, x++, y, (*p & (0x80 >> Diff)) ? Index1 : Index0);
if (++Diff == 8) {
Diff = 0;
p++;
}
} while (--xsize);
break;
case LCD_DRAWMODE_TRANS:
do {
if (*p & (0x80 >> Diff))
_SetPixelIndex(pDevice, x, y, Index1);
x++;
if (++Diff == 8) {
Diff = 0;
p++;
}
} while (--xsize);
break;
case LCD_DRAWMODE_XOR | LCD_DRAWMODE_TRANS:
case LCD_DRAWMODE_XOR:
IndexMask = pDevice->pColorConvAPI->pfGetIndexMask();
do {
if (*p & (0x80 >> Diff)) {
Pixel = _GetPixelIndex(pDevice, x, y);
_SetPixelIndex(pDevice, x, y, Pixel ^ IndexMask);
}
x++;
if (++Diff == 8) {
Diff = 0;
p++;
}
} while (--xsize);
break;
}
}
/*********************************************************************
*
* Draw Bitmap 2 BPP
*/
static void _DrawBitLine2BPP(GUI_DEVICE * pDevice, int x, int y, U8 const GUI_UNI_PTR * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
LCD_PIXELINDEX Pixels, PixelIndex;
int CurrentPixel, Shift, Index;
Pixels = *p;
CurrentPixel = Diff;
x += Diff;
switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
case 0:
if (pTrans) {
do {
Shift = (3 - CurrentPixel) << 1;
Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
PixelIndex = *(pTrans + Index);
_SetPixelIndex(pDevice, x++, y, PixelIndex);
if (++CurrentPixel == 4) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
} else {
do {
Shift = (3 - CurrentPixel) << 1;
Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
_SetPixelIndex(pDevice, x++, y, Index);
if (++CurrentPixel == 4) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
}
break;
case LCD_DRAWMODE_TRANS:
if (pTrans) {
do {
Shift = (3 - CurrentPixel) << 1;
Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
if (Index) {
PixelIndex = *(pTrans + Index);
_SetPixelIndex(pDevice, x, y, PixelIndex);
}
x++;
if (++CurrentPixel == 4) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
} else {
do {
Shift = (3 - CurrentPixel) << 1;
Index = (Pixels & (0xC0 >> (6 - Shift))) >> Shift;
if (Index) {
_SetPixelIndex(pDevice, x, y, Index);
}
x++;
if (++CurrentPixel == 4) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
}
break;
}
}
/*********************************************************************
*
* Draw Bitmap 4 BPP
*/
static void _DrawBitLine4BPP(GUI_DEVICE * pDevice, int x, int y, U8 const GUI_UNI_PTR * p, int Diff, int xsize, const LCD_PIXELINDEX * pTrans) {
LCD_PIXELINDEX Pixels, PixelIndex;
int CurrentPixel, Shift, Index;
Pixels = *p;
CurrentPixel = Diff;
x += Diff;
switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
case 0:
if (pTrans) {
do {
Shift = (1 - CurrentPixel) << 2;
Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
PixelIndex = *(pTrans + Index);
_SetPixelIndex(pDevice, x++, y, PixelIndex);
if (++CurrentPixel == 2) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
} else {
do {
Shift = (1 - CurrentPixel) << 2;
Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
_SetPixelIndex(pDevice, x++, y, Index);
if (++CurrentPixel == 2) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
}
break;
case LCD_DRAWMODE_TRANS:
if (pTrans) {
do {
Shift = (1 - CurrentPixel) << 2;
Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
if (Index) {
PixelIndex = *(pTrans + Index);
_SetPixelIndex(pDevice, x, y, PixelIndex);
}
x++;
if (++CurrentPixel == 2) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
} else {
do {
Shift = (1 - CurrentPixel) << 2;
Index = (Pixels & (0xF0 >> (4 - Shift))) >> Shift;
if (Index) {
_SetPixelIndex(pDevice, x, y, Index);
}
x++;
if (++CurrentPixel == 2) {
CurrentPixel = 0;
Pixels = *(++p);
}
} while (--xsize);
}
break;
}
}
/*********************************************************************
*
* Draw Bitmap 8 BPP
*/
static void _DrawBitLine8BPP(GUI_DEVICE * pDevice, int x, int y, U8 const GUI_UNI_PTR * p, int xsize, const LCD_PIXELINDEX * pTrans) {
LCD_PIXELINDEX Pixel;
switch (GUI_Context.DrawMode & (LCD_DRAWMODE_TRANS | LCD_DRAWMODE_XOR)) {
case 0:
if (pTrans) {
for (; xsize > 0; xsize--, x++, p++) {
Pixel = *p;
_SetPixelIndex(pDevice, x, y, *(pTrans + Pixel));
}
} else {
for (; xsize > 0; xsize--, x++, p++) {
_SetPixelIndex(pDevice, x, y, *p);
}
}
break;
case LCD_DRAWMODE_TRANS:
if (pTrans) {
for (; xsize > 0; xsize--, x++, p++) {
Pixel = *p;
if (Pixel) {
_SetPixelIndex(pDevice, x, y, *(pTrans + Pixel));
}
}
} else {
for (; xsize > 0; xsize--, x++, p++) {
Pixel = *p;
if (Pixel) {
_SetPixelIndex(pDevice, x, y, Pixel);
}
}
}
break;
}
}
/*********************************************************************
*
* Draw Bitmap 16 BPP, not optimized
*/
static void _DrawBitLine16BPP(GUI_DEVICE * pDevice, int x, int y, U16 const GUI_UNI_PTR * p, int xsize) {
for (;xsize > 0; xsize--, x++, p++) {
_SetPixelIndex(pDevice, x, y, *p);
}
}
/*********************************************************************
*
* Draw Bitmap 32 BPP, not optimized
*/
static void _DrawBitLine32BPP(GUI_DEVICE * pDevice, int x, int y, U32 const GUI_UNI_PTR * p, int xsize) {
for (;xsize > 0; xsize--, x++, p++) {
_SetPixelIndex(pDevice, x, y, *p);
}
}
/*********************************************************************
*
* _DrawBitmap
*/
static void _DrawBitmap(GUI_DEVICE * pDevice, int x0, int y0,
int xSize, int ySize,
int BitsPerPixel,
int BytesPerLine,
const U8 GUI_UNI_PTR * pData, int Diff,
const LCD_PIXELINDEX* pTrans) {
int i;
switch (BitsPerPixel) {
case 1:
for (i = 0; i < ySize; i++) {
_DrawBitLine1BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
pData += BytesPerLine;
}
break;
case 2:
for (i = 0; i < ySize; i++) {
_DrawBitLine2BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
pData += BytesPerLine;
}
break;
case 4:
for (i = 0; i < ySize; i++) {
_DrawBitLine4BPP(pDevice, x0, i + y0, pData, Diff, xSize, pTrans);
pData += BytesPerLine;
}
break;
case 8:
for (i = 0; i < ySize; i++) {
_DrawBitLine8BPP(pDevice, x0, i + y0, pData, xSize, pTrans);
pData += BytesPerLine;
}
break;
case 16:
for (i = 0; i < ySize; i++) {
_DrawBitLine16BPP(pDevice, x0, i + y0, (const U16 *)pData, xSize);
pData += BytesPerLine;
}
break;
case 32:
for (i = 0; i < ySize; i++) {
_DrawBitLine32BPP(pDevice, x0, i + y0, (const U32 *)pData, xSize);
pData += BytesPerLine;
}
break;
}
}
/*********************************************************************
*
* _InitOnce
*
* Purpose:
* Allocates a fixed block for the context of the driver
*
* Return value:
* 0 on success, 1 on error
*/
static int _InitOnce(GUI_DEVICE * pDevice) {
DRIVER_CONTEXT * pContext;
if (pDevice->u.pContext == NULL) {
pDevice->u.pContext = GUI_ALLOC_GetFixedBlock(sizeof(DRIVER_CONTEXT));
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
pContext->BitsPerPixel = LCD__GetBPP(pDevice->pColorConvAPI->pfGetIndexMask());
}
return pDevice->u.pContext ? 0 : 1;
}
/*********************************************************************
*
* _GetDevProp
*/
static I32 _GetDevProp(GUI_DEVICE * pDevice, int Index) {
DRIVER_CONTEXT * pContext;
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
switch (Index) {
case LCD_DEVCAP_XSIZE:
return pContext->xSize;
case LCD_DEVCAP_YSIZE:
return pContext->ySize;
case LCD_DEVCAP_VXSIZE:
return pContext->vxSize;
case LCD_DEVCAP_VYSIZE:
return pContext->vySize;
case LCD_DEVCAP_BITSPERPIXEL:
return pContext->BitsPerPixel;
case LCD_DEVCAP_NUMCOLORS:
return 0;
case LCD_DEVCAP_XMAG:
return 1;
case LCD_DEVCAP_YMAG:
return 1;
case LCD_DEVCAP_MIRROR_X:
return 0;
case LCD_DEVCAP_MIRROR_Y:
return 0;
case LCD_DEVCAP_SWAP_XY:
return 0;
}
return -1;
}
/*********************************************************************
*
* _GetDevData
*/
static void * _GetDevData(GUI_DEVICE * pDevice, int Index) {
GUI_USE_PARA(pDevice);
switch (Index) {
#if GUI_SUPPORT_MEMDEV
case LCD_DEVDATA_MEMDEV:
return (void *)&GUI_MEMDEV_DEVICE_32;
#endif
}
return NULL;
}
/*********************************************************************
*
* _GetRect
*/
static void _GetRect(GUI_DEVICE * pDevice, LCD_RECT * pRect) {
DRIVER_CONTEXT * pContext;
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
pRect->x0 = 0;
pRect->y0 = 0;
pRect->x1 = pContext->vxSize - 1;
pRect->y1 = pContext->vySize - 1;
}
/*********************************************************************
*
* _SetOrg
*/
static void _SetOrg(GUI_DEVICE * pDevice, int x, int y) {
LCD_X_SETORG_INFO Data = {0};
Data.xPos = x;
Data.yPos = y;
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_SETORG, (void *)&Data);
}
/*********************************************************************
*
* Static code: Functions available by _GetDevFunc()
*
**********************************************************************
*/
/*********************************************************************
*
* _SetVRAMAddr
*/
static void _SetVRAMAddr(GUI_DEVICE * pDevice, void * pVRAM) {
DRIVER_CONTEXT * pContext;
LCD_X_SETVRAMADDR_INFO Data = {0};
_InitOnce(pDevice);
if (pDevice->u.pContext) {
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
pContext->VRAMAddr = (U32)pVRAM;
Data.pVRAM = pVRAM;
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_SETVRAMADDR, (void *)&Data);
}
}
/*********************************************************************
*
* _SetVSize
*/
static void _SetVSize(GUI_DEVICE * pDevice, int xSize, int ySize) {
DRIVER_CONTEXT * pContext;
_InitOnce(pDevice);
if (pDevice->u.pContext) {
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
pContext->vxSize = xSize;
pContext->vySize = ySize;
pContext->vxSizePhys = xSize;
}
}
/*********************************************************************
*
* _SetSize
*/
static void _SetSize(GUI_DEVICE * pDevice, int xSize, int ySize) {
DRIVER_CONTEXT * pContext;
LCD_X_SETSIZE_INFO Data = {0};
_InitOnce(pDevice);
if (pDevice->u.pContext) {
pContext = (DRIVER_CONTEXT *)pDevice->u.pContext;
pContext->vxSizePhys = (pContext->vxSizePhys == 0) ? xSize : pContext->vxSizePhys;
pContext->xSize = xSize;
pContext->ySize = ySize;
Data.xSize = xSize;
Data.ySize = ySize;
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_SETSIZE, (void *)&Data);
}
}
/*********************************************************************
*
* _Init
*/
static int _Init(GUI_DEVICE * pDevice) {
int r;
r = _InitOnce(pDevice);
r |= LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_INITCONTROLLER, NULL);
return r;
}
/*********************************************************************
*
* _On
*/
static void _On (GUI_DEVICE * pDevice) {
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_ON, NULL);
}
/*********************************************************************
*
* _Off
*/
static void _Off (GUI_DEVICE * pDevice) {
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_OFF, NULL);
}
/*********************************************************************
*
* _SetLUTEntry
*/
static void _SetLUTEntry(GUI_DEVICE * pDevice, U8 Pos, LCD_COLOR Color) {
LCD_X_SETLUTENTRY_INFO Data = {0};
Data.Pos = Pos;
Data.Color = Color;
LCD_X_DisplayDriver(pDevice->LayerIndex, LCD_X_SETLUTENTRY, (void *)&Data);
}
/*********************************************************************
*
* _GetDevFunc
*/
static void (* _GetDevFunc(GUI_DEVICE ** ppDevice, int Index))(void) {
GUI_USE_PARA(ppDevice);
switch (Index) {
case LCD_DEVFUNC_SET_VRAM_ADDR:
return (void (*)(void))_SetVRAMAddr;
case LCD_DEVFUNC_SET_VSIZE:
return (void (*)(void))_SetVSize;
case LCD_DEVFUNC_SET_SIZE:
return (void (*)(void))_SetSize;
case LCD_DEVFUNC_INIT:
return (void (*)(void))_Init;
case LCD_DEVFUNC_ON:
return (void (*)(void))_On;
case LCD_DEVFUNC_OFF:
return (void (*)(void))_Off;
case LCD_DEVFUNC_SETLUTENTRY:
return (void (*)(void))_SetLUTEntry;
}
return NULL;
}
/*********************************************************************
*
* Public data
*
**********************************************************************
*/
/*********************************************************************
*
* GUI_DEVICE_API structure
*/
const GUI_DEVICE_API GUIDRV_Template_API = {
//
// Data
//
DEVICE_CLASS_DRIVER,
//
// Drawing functions
//
_DrawBitmap,
_DrawHLine,
_DrawVLine,
_FillRect,
_GetPixelIndex,
_SetPixelIndex,
_XorPixel,
//
// Set origin
//
_SetOrg,
//
// Request information
//
_GetDevFunc,
_GetDevProp,
_GetDevData,
_GetRect,
};
/*************************** End of file ****************************/
GUIDRV_Template.h
/*********************************************************************
* SEGGER Microcontroller GmbH & Co. KG *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1996 - 2010 SEGGER Microcontroller GmbH & Co. KG *
* *
* Internet: www.segger.com Support: support@segger.com *
* *
**********************************************************************
** emWin V5.06 - Graphical user interface for embedded applications **
emWin is protected by international copyright laws. Knowledge of the
source code may not be used to write a similar product. This file may
only be used in accordance with a license and should not be re-
distributed in any way. We appreciate your understanding and fairness.
----------------------------------------------------------------------
File : GUIDRV_Template.h
Purpose : Interface definition for GUIDRV_Template driver
---------------------------END-OF-HEADER------------------------------
*/
#ifndef GUIDRV_TEMPLATE_H
#define GUIDRV_TEMPLATE_H
/*********************************************************************
*
* Display drivers
*/
//
// Addresses
//
extern const GUI_DEVICE_API GUIDRV_Win_API;
extern const GUI_DEVICE_API GUIDRV_Template_API;
//
// Macros to be used in configuration files
//
#if defined(WIN32) && !defined(LCD_SIMCONTROLLER)
#define GUIDRV_TEMPLATE &GUIDRV_Win_API
#else
#define GUIDRV_TEMPLATE &GUIDRV_Template_API
#endif
#endif
/*************************** End of file ****************************/
GUI_X.c中实现打印,时间戳,OS相关等接口。
GUI_X_Config,GUI_X_Init在GUI_Init中被调用。
GUI_ALLOC_AssignMemory设置GUI内部管理的堆空间和大小。
如果没定义宏GUI_ALLOC_ALLOC则使用GUI_ALLOC_AssignMemory分配空间,GUI内部自己实现堆的申请和释放。
如果定义了GUI_ALLOC_ALLOC宏,则使用用户定义的接口,还需要定义GUI_ALLOC_FREE等接口见Core/GUI_Alloc.c。
这里为了简单我们使用GUI内部的实现,只需要GUI_ALLOC_AssignMemory分配空间。
GUI_DEVICE_CreateAndLink链接设备驱动和颜色模式,即设备驱动GUIDRV_Template_API(前面显示部分的移植),颜色为RGB565.
static uint8_t gui_memory[3*1024];
//
// Configuration
//
void GUI_X_Config(void)
{
GUI_ALLOC_AssignMemory(gui_memory,sizeof(gui_memory));
// Set display driver and color conversion
//
GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);
}
void GUI_X_Init(void)
{
}
实现为空即可
//
// ExecIdle - called if nothing else is left to do
//
void GUI_X_ExecIdle(void)
{
}
GUI_X_GetTime用于获取ms时间戳,需要实现,
GUI_X_Delay多任务时可以用于调用OS的dleay释放CPU,这里为空即可。
//
// Timing routines
//
int GUI_X_GetTime(void)
{
return get_ticks();
}
void GUI_X_Delay(int Period)
{
(void)Period;
}
我这里只用一个任务跑GUI或者裸机跑,则都实现为空即可
//
// Multitask routines - required only if multitasking is used (#define GUI_OS 1)
//
void GUI_X_Unlock(void)
{
}
void GUI_X_Lock(void)
{
}
U32 GUI_X_GetTaskId(void)
{
return 0;
}
void GUI_X_InitOS(void)
{
}
用于事件驱动,这里都实现为空
//
// Event driving (optional with multitasking)
//
void GUI_X_WaitEvent(void)
{
}
void GUI_X_WaitEventTimed(int Period)
{
(void)Period;
}
void GUI_X_SignalEvent(void)
{
}
GUIConf.h中定义宏
GUI_Debug.h中定义了几个等级
调试时可以设置为LOG_ALL,使用时可以降低等级减少打印。
打印调试信息,根据实际实现
//
// Recording (logs/warnings and errors) - required only for higher levels
//
void GUI_X_Log(const char *s)
{
printf("%s\r\n",s);
}
void GUI_X_Warn(const char *s)
{
printf("%s\r\n",s);
}
void GUI_X_ErrorOut(const char *s)
{
printf("%s\r\n",s);
}
完整的GUI_X.C如下
static uint8_t gui_memory[3*1024];
//
// Configuration
//
void GUI_X_Config(void)
{
GUI_ALLOC_AssignMemory(gui_memory,sizeof(gui_memory));
// Set display driver and color conversion
//
GUI_DEVICE_CreateAndLink(&GUIDRV_Template_API, GUICC_M565, 0, 0);
}
void GUI_X_Init(void)
{
}
//
// ExecIdle - called if nothing else is left to do
//
void GUI_X_ExecIdle(void)
{
}
//
// Timing routines
//
int GUI_X_GetTime(void)
{
return os_get_ticks();
}
void GUI_X_Delay(int Period)
{
(void)Period;
}
//
// Multitask routines - required only if multitasking is used (#define GUI_OS 1)
//
void GUI_X_Unlock(void)
{
}
void GUI_X_Lock(void)
{
}
U32 GUI_X_GetTaskId(void)
{
return 0;
}
void GUI_X_InitOS(void)
{
}
//
// Event driving (optional with multitasking)
//
void GUI_X_WaitEvent(void)
{
}
void GUI_X_WaitEventTimed(int Period)
{
(void)Period;
}
void GUI_X_SignalEvent(void)
{
}
//
// Recording (logs/warnings and errors) - required only for higher levels
//
void GUI_X_Log(const char *s)
{
printf("%s\r\n",s);
}
void GUI_X_Warn(const char *s)
{
printf("%s\r\n",s);
}
void GUI_X_ErrorOut(const char *s)
{
printf("%s\r\n",s);
}
GUIConf.h如下
/*********************************************************************
* SEGGER MICROCONTROLLER SYSTEME GmbH *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 2002 SEGGER Microcontroller Systeme GmbH *
* *
* Internet: www.segger.com Support: support@segger.com *
* *
**********************************************************************
**** emWin/GSC Grafical user interface for embedded applications ****
emWin is protected by international copyright laws. Knowledge of the
source code may not be used to write a similar product. This file may
only be used in accordance with a license and should not be re-
distributed in any way. We appreciate your understanding and fairness.
----------------------------------------------------------------------
File : GUIConf.h
Purpose : Configures emWins abilities, fonts etc.
----------------------------------------------------------------------
*/
#ifndef GUICONF_H
#define GUICONF_H
#define GUI_DEBUG_LEVEL 5
#define GUI_ALLOC_SIZE 1024
/*********************************************************************
*
* Multi layer/display support
*/
#define GUI_NUM_LAYERS 1 // Maximum number of available layers
/*********************************************************************
*
* Multi tasking support
*/
#define GUI_OS (0) // Compile with multitasking support
/*********************************************************************
*
* Configuration of touch support
*/
#define GUI_SUPPORT_TOUCH (0) // Support a touch screen (req. win-manager)
/*********************************************************************
*
* Default font
*/
#define GUI_DEFAULT_FONT &GUI_Font6x8
/*********************************************************************
*
* Configuration of available packages
*/
#define GUI_SUPPORT_MOUSE 0 /* Support a mouse */
#define GUI_WINSUPPORT 1 /* Use window manager */
#define GUI_SUPPORT_MEMDEV 0 /* Memory device package available */
// NOTE! THE HARDWARE BUILD HAS A DIFFERENT GUIConf.h!!!
// COPY CHANGES FROM HERE TO THERE
// KMC
#define BUTTON_REACT_ON_LEVEL 1
///* Define colors */
//// KMC - turn these defaults off to use bitmap buttons
// #define BUTTON_BKCOLOR0_DEFAULT GUI_BLUE
// #define BUTTON_BKCOLOR1_DEFAULT GUI_WHITE
// #define BUTTON_BKCOLOR2_DEFAULT GUI_LIGHTCYAN
#define BUTTON_TEXTCOLOR0_DEFAULT GUI_WHITE
// #define BUTTON_TEXTCOLOR1_DEFAULT GUI_BLACK
// #define BUTTON_TEXTCOLOR2_DEFAULT GUI_LIGHTGREEN
#define BUTTON_FOCUSCOLOR_DEFAULT GUI_GRAY
// #define BUTTON_FRAMECOLOR_DEFAULT GUI_GRAY
#define BUTTON_3D_MOVE_X 0
#define BUTTON_3D_MOVE_Y 0
// Define ICONVIEW selected state background color as transparent
// = max alpha blending (0xFF) on BLACK - any color would do (0xxxxxxx)
#define ICONVIEW_BKCOLOR1_DEFAULT 0xFF000000
#endif /* Avoid multiple inclusion */
即对应LCD_X.c和LCDConf.h
LCD_X.c实现以下接口,LCD_X_DisplayDriver和LCD_X_Config
这里只要是LCD_X_Config中设置屏幕大小,在GUI_Init中被调用。
LCD_X.c如下
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) {
int r;
(void) LayerIndex;
(void) pData;
switch (Cmd) {
case LCD_X_INITCONTROLLER: {
return 0;
}
default:
r = -1;
}
return r;
}
void LCD_X_Config(void)
{
//
// Display driver configuration, required for Lin-driver
//
LCD_SetSizeEx (0, 240 , 320);
LCD_SetVSizeEx(0, 240, 320);
}
LCDConf.h如下,定义屏幕大小的宏
/*********************************************************************
* SEGGER Microcontroller GmbH & Co. KG *
* Solutions for real time microcontroller applications *
**********************************************************************
* *
* (c) 1996 - 2009 SEGGER Microcontroller GmbH & Co. KG *
* *
* Internet: www.segger.com Support: support@segger.com *
* *
**********************************************************************
** emWin V5.00 - Graphical user interface for embedded applications **
emWin is protected by international copyright laws. Knowledge of the
source code may not be used to write a similar product. This file may
only be used in accordance with a license and should not be re-
distributed in any way. We appreciate your understanding and fairness.
----------------------------------------------------------------------
File : LCDConf.h
Purpose : Display driver configuration file
----------------------------------------------------------------------
*/
#ifndef LCDCONF_H
#define LCDCONF_H
#define LCD_XSIZE 240
#define LCD_YSIZE 320
#define LCD_BITSPERPIXEL 16
#define LCD_GET_XSIZE() LCD_XSIZE
#define LCD_GET_YSIZE() LCD_YSIZE
#endif /* LCDCONF_H */
/*************************** End of file ****************************/
GUI_Init();
while (1) {
GUI_Exec();
}
添加如下文件
emWin506-Src/GUI/GUIDemo/*.c
添加头文件包含路径
emWin506-Src/GUI/GUIDemo/
我们用一个任务专门用于刷屏,一个任务跑Demo
void main(vcoid)
{
st7789_itf_init();
create_task(gui_task, NULL, 7, 1024, "gui_task");
while(1)
{
uint32_t pre_time = os_get_ticks();
uint32_t cur_time = pre_time;
while (1) {
cur_time = os_get_ticks();
if(cur_time - pre_time >= 25)
{
pre_time = cur_time;
st7789_itf_sync();
}
}
}
return;
}
static void gui_task(void *arg)
{
(void)arg;
GUI_Init();
printf("gui task start\r\n");
GUIDEMO_main();
}
测试见视频,
如下图测试可换算帧率为5483980/(240x320x2)=35.7FPS,当然刷屏率要大于该值才行,我们之前的测试是42FPS。
emWin的可移植性也非常好,类似LVGL也只需要简单的实现写点(刷屏)接口,时间时间戳获取接口,如果使用OS实现一些OS相关的接口即可。uC/GUI的知名度可能更高于emWin,其源码和文档也是学习GUI的非常好的资料值得好好学习。