本文主要是介绍【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
一 并口屏接线
之前整了一块串口屏,实际使用中,感觉整屏(320*240)的刷新速度还是有点偏慢,肉眼能够看到明显的刷屏动作,故而考虑改用并口屏来实现显示功能。
首先根据显示屏的接线图进行接线:
其中,真正需要关注的是CS、RS、WR、RD、RESET以及DB0-15数据线。
二 并口屏驱动初始化
接线完成之后,就需要在程序中对这几个接口进行初始化。
这边区别于SPI的串口屏,只要把所有的引脚作为GPIO口输入输出即可。
【1】首先选定几个管脚,做一些定义(注意!!!:这边的DATAOUT(x)宏是采用了位带操作,直接对PortE的PODRE寄存器进行操作,这样就相当于在PortE接满0-15管脚的情况下,输出对应的16位数据):
#define LCD_CS_HIGH() (PORT_SetBits(PortC, Pin00))
#define LCD_CS_LOW() (PORT_ResetBits(PortC, Pin00))
#define LCD_RS_HIGH() (PORT_SetBits(PortC, Pin01))
#define LCD_RS_LOW() (PORT_ResetBits(PortC, Pin01))
#define LCD_WR_HIGH() (PORT_SetBits(PortC, Pin02))
#define LCD_WR_LOW() (PORT_ResetBits(PortC, Pin02))
#define LCD_RD_HIGH() (PORT_SetBits(PortC, Pin03))
#define LCD_RD_LOW() (PORT_ResetBits(PortC, Pin03))
#define LCD_RES_HIGH() (PORT_SetBits(PortA, Pin07))
#define LCD_RES_LOW() (PORT_ResetBits(PortA, Pin07))#define DATAOUT(x) M4_PORT->PODRE=(x)
【2】其次是GPIO口初始化:
/**************************************************************************
* 函数名称: LCD_InitGPIO
* 功能描述: LCD初始化GPIO引脚
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明: 初始化LCD用到的GPIO口,包括CS\RS\WR\RD\RES\DB0-15
**************************************************************************/
void LCD_InitGPIO(void)
{stc_port_init_t stcPortInit;/* configure structure initialization */MEM_ZERO_STRUCT(stcPortInit);stcPortInit.enPinMode = Pin_Mode_Out;/* CS & RS & WR & RD & RES*/PORT_Init(PortC, Pin00, &stcPortInit);PORT_Init(PortC, Pin01, &stcPortInit);PORT_Init(PortC, Pin02, &stcPortInit);PORT_Init(PortC, Pin03, &stcPortInit);PORT_Init(PortA, Pin07, &stcPortInit);LCD_CS_HIGH();LCD_RS_HIGH();LCD_WR_HIGH();LCD_RD_HIGH();LCD_RES_HIGH();/*DB0-15*/PORT_Init(PortE, Pin00, &stcPortInit);PORT_Init(PortE, Pin01, &stcPortInit);PORT_Init(PortE, Pin02, &stcPortInit);PORT_Init(PortE, Pin03, &stcPortInit);PORT_Init(PortE, Pin04, &stcPortInit);PORT_Init(PortE, Pin05, &stcPortInit);PORT_Init(PortE, Pin06, &stcPortInit);PORT_Init(PortE, Pin07, &stcPortInit);PORT_Init(PortE, Pin08, &stcPortInit);PORT_Init(PortE, Pin09, &stcPortInit);PORT_Init(PortE, Pin10, &stcPortInit);PORT_Init(PortE, Pin11, &stcPortInit);PORT_Init(PortE, Pin12, &stcPortInit);PORT_Init(PortE, Pin13, &stcPortInit);PORT_Init(PortE, Pin14, &stcPortInit);PORT_Init(PortE, Pin15, &stcPortInit);
}
【3】封装LCD写命令、写数据、写GRAM接口(注意!!!:这边的入参与串口屏有区别,串口屏入参为一个字节数据,而这边是U16即两字节数据,这会影响到颜色的表现):
/**************************************************************************
* 函数名称: LCD_WriteCMD
* 功能描述: LCD写命令
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明:
**************************************************************************/
void LCD_WriteCMD(U16 Command)
{LCD_RS_LOW();LCD_CS_LOW();DATAOUT(Command);LCD_WR_LOW();LCD_WR_HIGH();LCD_CS_HIGH();
}/**************************************************************************
* 函数名称: LCD_WriteDAT
* 功能描述: LCD写数据
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明:
**************************************************************************/
void LCD_WriteDAT(U16 Data)
{LCD_RS_HIGH();LCD_CS_LOW();DATAOUT(Data);LCD_WR_LOW();LCD_WR_HIGH();LCD_CS_HIGH();
}/**************************************************************************
* 函数名称: LCD_WriteRAM
* 功能描述: LCD写GRAM,写颜色值
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明:
**************************************************************************/
void LCD_WriteRAM(uint16_t Data)
{LCD_RS_HIGH();LCD_CS_LOW();DATAOUT(Data);LCD_WR_LOW();LCD_WR_HIGH();LCD_CS_HIGH();
}
【4】有了底层驱动之后,因为串口屏和并口屏都是用的ILI9341驱动,所以其他的功能接口,包括寄存器的配置,参考【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录1----点亮LCD屏即可。
三 并口屏点亮
有了上面的接口作为准备,就可以驱动并口屏显示一些简单的界面了。这边的程序也与串口屏类似,这边不再赘述。
四 移植emwin
完成了点亮LCD屏之后,考虑到将要设计较为复杂的界面GUI,光用一些基本的绘图、显示字符接口不能满足要求,所以琢磨着再移植一套emWin,用来辅助设计GUI(由emWin的用户手册中可以看到,emWin是可以支持ILI9341的液晶屏驱动芯片的)。
移植准备与串口屏类似,参考【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录2----移植emWin,这边也不再赘述。
需要重点提一下与串口屏emwin移植的区别,这也是本文的重点:
这边涉及到两种移植方法,一种是直接线性访问驱动、第二种是间接访问驱动。关于这两种移植方法的区别可以参考方法一:直接线性访问驱动、方法一:间接访问驱动。
【方法一】:直接线性访问驱动
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);}
}
修改GUIDRV_Template.c文件。这边需要修改画点函数_SetPixelIndex(),在其中嵌入
自己的打点函数LCD_DrawPoint(x, y):
static void _SetPixelIndex(GUI_DEVICE * pDevice, int x, int y, LCD_PIXELINDEX PixelIndex) {#ifdef WIN32LCDSIM_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#endifGUI_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
}
与此同时,清屏程序_FillRect也需要改一下,有利于提高清屏速度(注意!!!:之前的串口屏程序没有这个操作也能用,但可能会影响到清屏速度):
/**************************************************************************
* 函数名称: LCD_Fill
* 功能描述: 填充色块函数
* 输入参数:
* 输出参数:
* 返 回 值:
* 其它说明:
**************************************************************************/
void LCD_Fill(U16 sx,U16 sy,U16 ex,U16 ey,U16 color)
{ U16 i,j;U16 xlen=0;xlen=ex-sx+1; for(i=sy;i<=ey;i++){LCD_SetCursor(sx,i); //设置光标位置 LCD_WriteRAM_Prepare(); //开始写入GRAM for(j=0;j<xlen;j++)LCD_WriteRAM(color); //设置光标位置 }
} /*********************************************************************
*
* _FillRect
*/
static void _FillRect(GUI_DEVICE * pDevice, int x0, int y0, int x1, int y1) {LCD_PIXELINDEX PixelIndex;int x;PixelIndex = LCD__GetColorIndex();if (GUI_pContext->DrawMode & LCD_DRAWMODE_XOR) {for (; y0 <= y1; y0++) {for (x = x0; x <= x1; x++) {_XorPixel(pDevice, x, y0);}}} else {
// for (; y0 <= y1; y0++) {
// for (x = x0; x <= x1; x++) {
// _SetPixelIndex(pDevice, x, y0, PixelIndex);
// }
// }LCD_Fill(x0,y0,x1,y1,LCD_COLORINDEX);}
}
其他部分的改动延用串口屏的移植即可。这样直接线性访问(LIN方式)移植成功。
【方法二】:间接访问驱动
void LCD_X_Config(void) {//方法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;PortAPI.pfWrite16_A0 = LcdWriteReg;PortAPI.pfWrite16_A1 = LcdWriteData;PortAPI.pfWriteM16_A1 = LcdWriteDataMultiple;PortAPI.pfReadM16_A1 = LcdReadDataMultiple;GUIDRV_FlexColor_SetFunc(pDevice, &PortAPI, GUIDRV_FLEXCOLOR_F66709, GUIDRV_FLEXCOLOR_M16C0B16);
}
这边的读写命令接口仍然使用8位访问的话,对于并口屏会有问题,所以修改接口为16位访问的方式:
static void LcdWriteReg(U16 Data) {LCD_WriteCMD(Data);
}static void LcdWriteData(U16 Data) {LCD_WriteDAT(Data);
}static void LcdWriteDataMultiple(U16 * pData, int NumItems) {while(NumItems--){LCD_WriteDAT(*pData);}
}static void LcdReadDataMultiple(U16 * pData, int NumItems) {return ;
}
后面的GUIDRV_FlexColor_SetFunc接口内容,由于还是用的驱动芯片ILI9341,所以也无需改动这边的代码。(注意!!!:GUIDRV_FLEXCOLOR_F66709 和 GUIDRV_FLEXCOLOR_M16C0B16 ,前一个参数是选择控制器的,后一个参数是选择位宽等模式的。首先你要看一下你所用的LCD控制器是否在受支持的范围内,如果不支持那就GG了。我的显示器是ILI9341所以我选择了66709这种方式。具体可以参考emWin用户手册中的内容)
至此,第二种方法也移植成功。
五 汇总几个遇到的坑
(1)设置LCD自动扫描方向的接口LCD_ScanDir中,有一段这样的代码:
这边我一开始设置成regval |= 0x08,导致的结果就是颜色反向了,原因在于0x36命令是存储器访问控制,其中有一位寄存器表示RGB与BGR的顺序(若为1,则是BGR;若为0,则是RGB):
(2)emWin中定义驱动器颜色COLOR_CONVERSION为GUICC_565或者GUICC_M565是有区别的,同样一个是RGB,一个是BGR。
(3)一开始使用方法二间接访问驱动,不管怎么操作,预期是GUI_SetBkColor(GUI_WHITE),然而出来的屏幕背景都是红色。后来查明原因在于LCDConf.c中,还是使用的之前串口屏的8位读写访问,可能预期发送的是白色(0xFFFF)命令,而实际只发出去了一半,即红色(0xFF00)命令。后来将这边的8位访问修改为16位访问,问题得到解决。
六 附录
【1】【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录1----点亮LCD屏
【2】【嵌入式】MCU(HC32F460)+SPI接口LCD液晶屏ILI9341 移植emWin记录2----移植emWin
【3】HC32F460相关文档资料汇总
【4】emWin_V5.42中文手册
【5】ILI9341驱动器相关文档资料汇总
这篇关于【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!