一 emWin移植准备
完成了前一文点亮LCD屏之后,考虑到将要设计较为复杂的界面GUI,光用一些基本的绘图、显示字符接口不能满足要求,所以琢磨着再移植一套emWin,用来辅助设计GUI(由emWin的用户手册中可以看到,emWin是可以支持ILI9341的液晶屏驱动芯片的)。
emWin的移植需要做一些准备:
【1】首先,在自己的工程目录下建立GUI文件夹,以存放相关的文件,这边衍生出来的子文件夹可以起到归纳分类的作用:
【2】其次,前往官网下载或者在本地keil路径下(不同人安装路径不一样,我本机的路径在此E:\Pro Software\Keil_v5\ARM\PACK\Keil\MDK-Middleware\7.8.0\emWin\)找到emWin文件,确认版本(我这边用的是emWin V5.42)。找到移植需要的文件,并移入自己工程目录下的GUI文件夹中,具体如下:
(注:关于库文件的CM3不用太纠结,CM3与CM4的主要区别在于浮点数的处理,某些地方可以通用)
【3】在keil中添加上面的源文件,并包含上面的头文件路径:
完成了上述准备工作之后,即可正式进入移植。
二 emWin移植步骤
【1】修改GUIConf.c文件。这边只要改一下GUI_NUMBYTES 的值,这个定义的是分配内存的大小,太大可能会使得GUI_Init()卡死,修改为合适的值即可:
#define GUI_NUMBYTES ((1024)*56)
/*********************************************************************
*
* Public code
*
**********************************************************************
*/
/*********************************************************************
*
* GUI_X_Config
*
* Purpose:
* Called during the initialization process in order to set up the
* available memory for the GUI.
*/
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);
}
【2】修改GUIDRV_Template.c文件。这边需要修改画点函数_SetPixelIndex(),在其中嵌入
自己的打点函数LCD_DrawPoint(x, y),打点函数的具体实现在前一篇文章驱动点亮SPI屏中有记录:
/*********************************************************************
*
* _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, LCD_PIXELINDEX 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...
//
LCD_DrawPoint(x, y); //重要:自行添加的画点函数
}
#if (LCD_MIRROR_X == 0) && (LCD_MIRROR_Y == 0) && (LCD_SWAP_XY == 0)
#undef xPhys
#undef yPhys
#endif
#endif
}
【3】修改GUI_X_RTX.c文件。这个文件是官方已经适配好RTX操作系统的,同理还有GUI_X_uCOS、GUI_X_FreeRTOS等其他操作系统的适配,基本上拿过来就直接用了。主要作用就是用你的操作系统,建立一个时钟任务,来控制GUI的运行。
【4】修改LCDConf.c文件。这边是最重要的一步,主要目的就是将emWin和LCD显示屏建立连接。
这边先贴出来代码,下面再一一说明:
#include "GUI.h"
#include "GUIDRV_FlexColor.h"
extern void LCD_AllInit(void);
extern void LCD_ConfigureREG(void);
extern void LCD_WriteCMD(U8 Command);
extern void LCD_WriteDAT(U8 Command);
/*********************************************************************
*
* Layer configuration (to be modified)
*
**********************************************************************
*/
//
// Physical display size
// The display size should be adapted in order to match the size of
// the target display.
//
#define XSIZE_PHYS 320
#define YSIZE_PHYS 240
//
// Color conversion
// The color conversion functions should be selected according to
// the color mode of the target display. Detaileds can be found in
// the chapter "Colors" in the emWin user manual.
//
//#define COLOR_CONVERSION GUICC_8888
#define COLOR_CONVERSION GUICC_565
//
// Display driver
// GUIDRV_WIN32 is for use only within the emWin Simulation
// environment. In order to use the target display controller, the
// according emWin display driver should be configured as it is
// described in the chapter "Display Drivers" in the emWin user
// manual. Beyond that sample configuration files can be found in
// The folder "Sample\LCDConf\%DISPLAY_DRIVER%\".
//
//#define DISPLAY_DRIVER GUIDRV_WIN32
#define DISPLAY_DRIVER &GUIDRV_Template_API
/*********************************************************************
*
* Configuration checking
*
**********************************************************************
*/
#ifndef XSIZE_PHYS
#error Physical X size of display is not defined!
#endif
#ifndef YSIZE_PHYS
#error Physical Y size of display is not defined!
#endif
#ifndef COLOR_CONVERSION
#error Color conversion not defined!
#endif
#ifndef DISPLAY_DRIVER
#error No display driver defined!
#endif
/*********************************************************************
*
* Public code
*
**********************************************************************
*/
static void LcdWriteReg(U8 Data) {
LCD_WriteCMD(Data);
}
static void LcdWriteData(U8 Data) {
LCD_WriteDAT(Data);
}
static void LcdWriteDataMultiple(U8 * pData, int NumItems) {
while(NumItems--){
LCD_WriteDAT(*pData);
}
}
static void LcdReadDataMultiple(U8 * pData, int NumItems) {
return ;
}
/*********************************************************************
*
* LCD_X_Config
*
* Function description
* Called during the initialization process in order to set up the
* display driver configuration.
*/
void LCD_X_Config(void) {
// //1.这种初始化用不了颜色
// GUI_DEVICE_CreateAndLink(DISPLAY_DRIVER, COLOR_CONVERSION, 0, 0);
// if (LCD_GetSwapXY()) {
// LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
// LCD_SetVSizeEx(0, YSIZE_PHYS, XSIZE_PHYS);
// } else {
// LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS);
// LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);
// }
//2.这种初始化可以使用颜色
GUI_DEVICE * pDevice;
GUI_PORT_API PortAPI = {0};
pDevice = GUI_DEVICE_CreateAndLink(&GUIDRV_FlexColor_API, COLOR_CONVERSION, 0, 0);
if (LCD_GetSwapXY()) {
LCD_SetSizeEx (0, YSIZE_PHYS, XSIZE_PHYS);
LCD_SetVSizeEx(0, YSIZE_PHYS, XSIZE_PHYS);
} else {
LCD_SetSizeEx (0, XSIZE_PHYS, YSIZE_PHYS);
LCD_SetVSizeEx(0, XSIZE_PHYS, YSIZE_PHYS);
}
PortAPI.pfWrite8_A0 = LcdWriteReg;
PortAPI.pfWrite8_A1 = LcdWriteData;
PortAPI.pfWriteM8_A1 = LcdWriteDataMultiple;
PortAPI.pfReadM8_A1 = LcdReadDataMultiple;
GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B8);
}
/*********************************************************************
*
* LCD_X_DisplayDriver
*
* Function description
* Display driver callback function. This function is called by the
* Display driver for certain purposes. Using GUIDRV_Win32 it is not
* required to react to any command.
*/
int LCD_X_DisplayDriver(unsigned LayerIndex, unsigned Cmd, void * pData) {
GUI_USE_PARA(LayerIndex);
GUI_USE_PARA(Cmd);
GUI_USE_PARA(pData);
// LCD_AllInit();//重要:添加初始化函数
LCD_ConfigureREG();
return 0;
}
/*************************** End of file ****************************/
(1)首先是定义LCD的屏幕尺寸、颜色格式以及驱动配置(这边定义的驱动GUIDRV_Template_API来自于上面第二步中的配置,因为不是触摸屏,这边只定义了打点函数,没有定义读点函数)
(2)LCD_X_Config配置。这边提供两种连接驱动(GUI_DEVICE_CreateAndLink)的方法,一开始用方法一,连接的是上面定义好打点函数的驱动&GUIDRV_Template_API,测试emWin可以绘图,但是颜色只能显示黑白的。遂换用第二种方法,连接GUIDRV_FlexColor.h中定义的驱动&GUIDRV_FlexColor_API,写命令/数据接口全部都自己定义,具体的实现也可以参考上一篇文章驱动点亮SPI屏。
这两种方法的区别,本质在于一个是直接把打点函数传递给&GUIDRV_FlexColor_API,另一个是把写命令/数据的接口传递给&GUIDRV_FlexColor_API,让其自己在内部实现打点。感觉两种方法本质上是一样的,导致方法一颜色用不了的区别可能在于下面GUIDRV_FlexColor_SetFunc接口的使用,这边我就没有继续细究了,如果有大佬看到的话,希望能够在评论区给我解个惑(20211108追更关于这个问题的解答:参考这篇帖子方法一:直接线性访问驱动和方法二:间接访问驱动)。
(3)LCD_X_DisplayDriver配置。加入自己的LCD初始化函数。这边的LCD_ConfigureREG()同样参考上一篇文章,这边就不在赘述了:
总上,配置完尺寸、颜色、驱动、初始化接口几项内容之后,emWin就实现与LCD的联系(LCDConf.c),再结合前面的自身内存的配置(GUIConf.c),以及与操作系统的关联(GUI_X_RTX.c),emWin就移植成功了。
三 emWin效果实现
emWin移植成功之后,再结合LCD显示屏,就可以显示一些复杂的界面和内容了,这边测试仅试用一下emWin画⚪以及显示字符的功能(其中,字符显示部分,参考文章【嵌入式】emwin中插入中文字体——FontCvt的使用):
/**************************************************************************
* 函数名称: GUI_Task
* 功能描述: GUI子任务
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明:
**************************************************************************/
void GUI_Task(void)
{
//使用emwin来绘图
LCD_InitGPIO(); //初始化几个GPIO口,包括BL、DC、RES以及SPI的CS
LCD_InitSPI1(); //初始化SPI的几个口,包括SCK、MOSI以及MISO
LCD_HardwareReset(); //LCD复位
GUI_Init(); //emwin初始化
LCD_DisplayDir(1); //默认为横屏
LCD_Clear(WHITE); //清屏白色
GUI_SetBkColor(GUI_WHITE);
GUI_AA_SetFactor(4);
GUI_EnableAlpha(1);
GUI_UC_SetEncodeUTF8();
GUI_SetFont(&GUI_FontFont32);
GUI_SetColor(BLACK);
GUI_SetTextMode(GUI_TM_TRANS);
GUI_DispStringAt("Hello LCD!", 160, 120);
GUI_DispStringAt("欢迎!", 160, 60);
GUI_DrawCircle(80, 60, 40);
while(1)
{
os_dly_wait(1000);
}
}
四 汇总几个遇到的坑
(1)欢迎两个字明明加到字库中了,但是屏幕上始终显示不了
【问题解决】:查阅资料发现问题在于,执行中文显示的语句GUI_DispStringAt("欢迎!", 160, 60)所在的源文件,没有转化为UTF-8编码格式,照这下面帖子的方法即可解决:emWin—显示汉字。
(2)GUI内存配置的GUI_NUMBYTES不宜过大,一开始用的默认0x200000,程序挂死,后面改小为1024*56即可。
(3)颜色显示仍然有问题,使用GUI自带的颜色库,例如GUI_GREEN等,实际显示的颜色与设置的不一致,具体原因没搞清楚,猜测可能跟LCD支持的颜色为GUICC_565(0xFFFF),而GUI库中的颜色为GUICC_888(0xFFFFFF)有关。
五 附录
【1】【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录1----点亮LCD屏
【2】【嵌入式】emwin中插入中文字体——FontCvt的使用
【3】HC32F460资料汇总