【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录

一 并口屏接线

        之前整了一块串口屏,实际使用中,感觉整屏(320*240)的刷新速度还是有点偏慢,肉眼能够看到明显的刷屏动作,故而考虑改用并口屏来实现显示功能。

        首先根据显示屏的接线图进行接线:

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第1张图片【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第2张图片

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第3张图片 

        其中,真正需要关注的是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 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
}

        与此同时,清屏程序_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;jDrawMode & 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用户手册中的内容

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第4张图片

         至此,第二种方法也移植成功。

五 汇总几个遇到的坑

        (1)设置LCD自动扫描方向的接口LCD_ScanDir中,有一段这样的代码:

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第5张图片

        这边我一开始设置成regval |= 0x08,导致的结果就是颜色反向了,原因在于0x36命令是存储器访问控制,其中有一位寄存器表示RGB与BGR的顺序(若为1,则是BGR;若为0,则是RGB):

【嵌入式】MCU(HC32F460)+并口LCD液晶屏ILI9341 移植emWin记录_第6张图片

         (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,单片机,嵌入式硬件)