第3版emWin教程第8章 emWin6.x的带OS方式移植(STM32H7之RGB接口)

Posted Simon223

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第3版emWin教程第8章 emWin6.x的带OS方式移植(STM32H7之RGB接口)相关的知识,希望对你有一定的参考价值。

教程不断更新中:http://www.armbbs.cn/forum.php?mod=viewthread&tid=98429

第8章   emWin6.x的带OS方式移植(STM32H7之RGB接口)

本章节为大家讲解emWin6.x的裸机方式移植。提供的移植方法支持emWin的多图层配置,多缓冲配置以及各种颜色格式的实现。同时可以自适应我们生产的4.3寸,5寸和7寸的电阻屏和电容屏。

虽然本章节是以我们开发板为例进行移植的,但是教会大家如何移植到自己的板子上以及移植过程中的注意事项是本章节的重点。

目录

8.1   初学者重要提示

8.2   移植前的准备工作以及移植emWin的流程

8.3   第1步:下载emWin库并添加到工程模板

8.4   第2步:SDRAM驱动的实现

8.5   第3步:LTDC涉及到的引脚配置和时序配置

8.5.1  LTDC时序配置

8.5.2  如何验证LTDC的时序配置是否正确

8.5.3  添加涉及到的所有文件到工程

8.6   第4步:电阻屏和电容屏触摸驱动的实现

8.6.1  添加GUIX的按下,松手和移动三个事件

8.6.2  周期性调用触摸扫描函数

8.6.3  如何将触摸驱动移植到自己的板子

8.7   第5步:emWin底层接口函数和配置

8.7.1  GUIConf.c文件

8.7.2  GUIConf.h文件

8.7.3  LCDConf_Lin_Template.c文件

8.8   第6步:emWin的RTOS方式接口文件

8.9   第7步:SDRAM的MPU Cache配置

8.10 第8步:添加emWin应用进行测试

8.11 显示屏闪烁问题解决方法

8.12 避免显示屏上电瞬间高亮和撕裂感

8.13 实验例程

8.14 总结


 

 

8.1   初学者重要提示

1、  学习本章节前,务必保证已经学习了本教程的第4章,第5章和第6章,这三章是移植前的必备知识。

2、  为了方便大家移植,推荐直接添加我们的工程文件到自己的工程或者直接使用我们的工程模板,按照本章的修改说明移植即可,本章是以uCOS-III移植为例进行说明。

3、  本章节是以移植到MDK上面为例进行讲解的,如果是移植到IAR上,方法是一样的。

4、  本章教程使用的emWin库来源和注意事项:

  •   本章教程使用的emWin库来自MDK的安装目录的emWin6.x.
  •   由于STemWin的版本一直停留在V5.44版本,所以我们本章教程没有使用。如果大家使用STemWin,初始化emWin前,务必记得使能STM32的硬件CRC时钟,因为ST官方对他们的emWin库做了保护,否则emWin无法启动。

5、  由于开发板要自适应4.3寸,5寸和7寸显示屏,而且还分电阻触摸和电容触摸,所以移植过程中添加的文件稍多。虽然移植是以我们的开发板为例进行讲解的,但是重点依然是告诉大家如何移植自己的板子以及移植过程中需要注意的事项。

6、  对于本章节的移植,我们需要先从整体上把控。由于开发板已经把需要移植的文件都整理好了,用户仅需添加文件就可以使用。我们这里着重介绍如何移植到自己的板子上面,这个才是本章节的重点。

  •   显示屏的移植

emWin需要的底层接口函数已经全部集成在LCDConf_Lin_Template.c文件里面,此文件已经比较成熟了,基本没有bug。对于这个文件,用户仅需学会使用里面的几个宏配置以及提供一个显示屏背光调节函数LCD_SetBackLight即可,其它都不用做任何修改。

另外还有一个LTDC涉及到的引脚和时序配置的问题,这个是需要用户自己去实现的,配置方法已经在本章节的4.6小节进行讲解。如果这个也配置完成了,emWin的显示屏移植就完成了。

  •   触摸的移植

电容触摸的移植比较容易,因为电容触摸芯片可以自动触摸校准,所以仅需配置完触摸芯片后将触摸芯片返回的触摸坐标(电容触摸芯片返回的就是实际的坐标值)和触摸按下状态通过函数GUI_PID_StoreState存储到指针输入设备的FIFO里面即可。

电阻触摸的移植要稍麻烦些,由于电阻触摸板的线性度不是很好,如果不做触摸校准和滤波处理会有点击不准确和飞点问题。emWin本身是支持两点触摸校准的,实际测试发现效果并不是很好,容易出现飞点,特别是使用线性度比较差的电阻触摸板。针对此问题,我们专门做了一个四点触摸校准,实际效果好了很多。其中触摸滤波方法是检测到触摸后先延迟30ms,消除抖动,然后采集10组坐标值做升序排列,去掉最大的几组坐标和最小的几组坐标,对中间的几组求平均作为最终的数值(电容触摸芯片返回的是ADC数值,不是实际坐标值)。然后将最终的数值代入通过触摸校准建立的线性公式来获得实际的坐标值,此时就可以将触摸坐标和触摸按下状态通过函数GUI_PID_StoreState存储到指针输入设备的FIFO里面。

8.2   移植前的准备工作以及移植emWin的流程

移植前注意以下两个问题:

  •   本章节的IDE开发环境务必是MDK5.30及其以上版本,镜像下载地址:

http://www.armbbs.cn/forum.php?mod=viewthread&tid=96992 。。

  •   准备一个简单的uCOS-III工程(其它RTOS是一样的),越简单越好,我们就在这个简单的工程上面移植即可。

http://www.armbbs.cn/forum.php?mod=viewthread&tid=96918

emWin库的移植通过以下8步完成,下面各个小节详细讲解每一步:

8.3   第1步:下载emWin库并添加到工程模板

(提示:本小节要实现的操作,最简单的方法是复制教程配套例子里面的emWin文件夹到自己的工程目录即可)。

首先准备好一个简单的裸机工程模板,工程模板的制作就不做讲解了,这里的重点是教大家移植。准备好的工程模板如下图所示(大家也可以制作其它任意的工程模板,不限制):

http://www.armbbs.cn/forum.php?mod=viewthread&tid=96918

准备好工程模板后,就可以开始移植了。首先要做的就是将所有需要的文件放到工程模板里面。下面分四步和大家进行说明。当然,不限制必须使用下面的方法添加源码到工程,只要将需要的文件添加到工程模板即可。

 第1步:按照第2章2.3.5小节讲解的方法找到emWin软件包,下面是emWin软件包内容:

我们将其做了简单整理,推荐大家移植阶段直接使用我们本章教程配套例子的emWin文件夹,将其复制到自己的工程里面即可。

  第2步:在工程模板创建emWin文件夹

emWin文件夹里面再创建如下几个子文件夹,方便我们管理:

  •   Config文件夹用于添加配置文件和emWin底层驱动接口文件。

 

这四个文件来自emWin软件包里面的Config文件夹。

  •   DisplayDriver默认驱动,未使用。
  •   emWinTask文件用于添加用户自己的应用代码文件。

 

这四个文件是需要用户自己实现的测试代码,移植仅用到了MainTask.c和MainTask.h。这里大家可以直接复制本章配套例子在此文件夹下的这两个文件:

  •   GUI_X文件用于添加裸机或者RTOS方式的接口文件。

 

其中GUI_X_uCOS-III.c是用于RTOS的。

  •   GUILib文件用于添加emWin库文件。

 

GUI_CM0_L.lib用于M0内核芯片,GUI_CM3_L.lib用于M3内核新品,GUI_CM4_L.lib用于M4和M7内核芯片。这三个库都是小端格式。

  •   HanZi中文字库文件。

用于添加emWin汉子字库,当前移植阶段还用不到。

  •   Include文件用于添加头文件。

emWin的大部分头文件都在这个文件中,部分截图:

 

  •   JPEGConf用于STM32H7的硬JPEG解码。

这个是专门为H7设计的硬件解码接口文件(非SEGGER设计,是我们自己设计的)。

 

  第3步:将源码文件添加到MDK的工程项目中,添加后的效果如下:

添加完毕后,别忘了添加头文件的路径:

至此,我们需要的emWin文件都已经添加完毕。

8.4   第2步:SDRAM驱动的实现

(提示:本小节要实现的操作,最简单的方法是复制教程配套例子里面的SDRAM驱动到自己的工程目录进行调试修改即可)

一定要保证SDRAM大批量读写数据时是正常的,SDRAM的测试可以自己专门做一个工程测试下。对于SDRAM的驱动实现,可以看BSP驱动教程的SDRAM章节:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980

不管你使用的是镁光的,海力士的,三星的,ISSI的或者华邦的,实现方法基本都是一样的。教程配套的板子使用的是镁光32位带宽的SDRAM,如果想最大限度的发挥STM32H7驱动SDRAM的性能,强烈建议使用32位带宽的SDRAM,或者两个16位SDRAM组成32位带宽的SDRAM也是可以的。那SDRAM主要起到什么作用呢?作用有二:

  •   用作显示屏的显存

STM32H7的LTDC外接RGB接口屏是没有显存的,所以需要SDRAM用作显存。如果用户选择STM32H7 LTDC的颜色格式是32位色ARGB8888,那么所需要显存大小(单位字节)是:显示屏宽 * 显示屏高 * (32/8), 其中32/8是表示这种颜色格式的一个像素点需要4个字节来表示。又比如配置颜色格式是16位色的RGB565,那么需要的显存大小是:显示屏宽 * 显示屏高 * (16/8),其中16/8是表示这种颜色格式的一个像素点需要2个字节来表示。其它的颜色格式,依此类推。

  •   用作emWin动态内存

emWin是极其消耗动态内存的,所以用户可以将SDRAM除了用于显存以外的所有内存全部用作emWin动态内存。

============================================================

如果SDRAM的驱动测试已经没有问题了,就可以将其添加到工程里面了,开发板使用的SDRAM驱动文件是bsp_fmc_sdram.c。

添加到工程里面后要分配SDRAM的使用,教程配套开发板使用的是32MB,32位带宽的SDRAM,图层1占用4MB,图层2占用4MB,最后24MB给emWin动态内存使用。也许会有初学者会问,每个图层分配4MB是不是有些多了?实际上不多的,因为我们要让不同的颜色格式都通用,而且要满足三缓冲对显存的需求,这里分配4MB的话,教程实例使用很方便。大家实际项目中的使用可以配置成实际大小。具体的配置如下,详情见bsp_fmc_sdram.h文件:

#define EXT_SDRAM_ADDR      ((uint32_t)0xC0000000)
#define EXT_SDRAM_SIZE        (32 * 1024 * 1024)

/* LCD显存,第1页, 分配4M字节 */
#define SDRAM_LCD_BUF1         EXT_SDRAM_ADDR

/* LCD显存,第2页, 分配4M字节 */
#define SDRAM_LCD_BUF2        (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE)

#define SDRAM_LCD_SIZE        (4 * 1024 * 1024)        /* 每层4M */
#define SDRAM_LCD_LAYER        2                        /* 2层 */

/* 剩下的24M字节,提供给应用程序使用 */
#define SDRAM_APP_BUF        (EXT_SDRAM_ADDR + SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)
#define SDRAM_APP_SIZE        (EXT_SDRAM_SIZE - SDRAM_LCD_SIZE * SDRAM_LCD_LAYER)

至此,SDRAM的驱动配置也讲解完毕。

8.5   第3步:LTDC涉及到的引脚配置和时序配置

8.5.1  LTDC时序配置

(提示:本小节要实现的操作,最简单的方法是复制教程配套例子整理好的驱动文件LCDConf_Lin_Template.c到自己的工程目录,然后调试修改文件中的函数LCD_LL_Init即可,最后开启显示屏背光即可,)

用户仅需配置LTDC涉及到的引脚和时序即可,LTDC其余的配置已经在文件LCDConf_Lin_Template.c全部封好了。将引脚配置预留出来供用户配置是因为硬件设计不同,比如可能使用RGB888接口,也可能是RGB565接口,所以用户仅需把需要的引脚初始化即可。将时序配置也预留出来是因为不同厂家的裸屏,驱动时序是不同的。

由于开发板配套了4.3寸,5寸和7寸屏显示屏,所以要对这几种尺寸的显示屏做自适应。每个屏的时序配置都是不一样的,具体实现在LCDConf_Lin_Template.c文件中,即函数LCD_LL_Init。大家在给自己的显示屏移植时仅需提供这个LCD_LL_Init函数即可,引脚配置需要在这个函数里面实现。

引脚的配置还比较容易,硬件上用到哪些引脚了就把那些引脚配置下即可,关键是LTDC的时序配置。针对这个问题,本教程第4章的4.4小结有详细说明(必看)。我们这里就不再赘述了,具体代码如下:

/*
*********************************************************************************************************
*    函 数 名: LCD_LL_Init
*    功能说明: 配置LTDC
*    形    参: 无
*    返 回 值: 无
*   笔    记:
*       LCD_TFT 同步时序配置(整理自官方做的一个截图,言简意赅):
*       ----------------------------------------------------------------------------
*    
*                                                 Total Width
*                             <--------------------------------------------------->
*                       Hsync width HBP             Active Width                HFP
*                             <---><--><--------------------------------------><-->
*                         ____    ____|_______________________________________|____ 
*                             |___|   |                                       |    |
*                                     |                                       |    |
*                         __|         |                                       |    |
*            /|\\    /|\\  |            |                                       |    |
*             | VSYNC|   |            |                                       |    |
*             |Width\\|/  |__          |                                       |    |
*             |     /|\\     |         |                                       |    |
*             |  VBP |      |         |                                       |    |
*             |     \\|/_____|_________|_______________________________________|    |
*             |     /|\\     |         | / / / / / / / / / / / / / / / / / / / |    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*    Total    |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*    Heigh    |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |Active|      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |Heigh |      |         |/ / / / / / Active Display Area / / / /|    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
*             |     \\|/_____|_________|_______________________________________|    |
*             |     /|\\     |                                                      |
*             |  VFP |      |                                                      |
*            \\|/    \\|/_____|______________________________________________________|
*            
*     
*     每个LCD设备都有自己的同步时序值:
*     Horizontal Synchronization (Hsync) 
*     Horizontal Back Porch (HBP)       
*     Active Width                      
*     Horizontal Front Porch (HFP)     
*   
*     Vertical Synchronization (Vsync)  
*     Vertical Back Porch (VBP)         
*     Active Heigh                       
*     Vertical Front Porch (VFP)         
*     
*     LCD_TFT 窗口水平和垂直的起始以及结束位置 :
*     ----------------------------------------------------------------
*   
*     HorizontalStart = (Offset_X + Hsync + HBP);
*     HorizontalStop  = (Offset_X + Hsync + HBP + Window_Width - 1); 
*     VarticalStart   = (Offset_Y + Vsync + VBP);
*     VerticalStop    = (Offset_Y + Vsync + VBP + Window_Heigh - 1);
*
*********************************************************************************************************
*/
static void LCD_LL_Init(void) 
{
    /* 配置LCD相关的GPIO */
    {
        /* GPios Configuration */
        /*
        +------------------------+-----------------------+----------------------------+
        +                       LCD pins assignment                                   +
        +------------------------+-----------------------+----------------------------+
        |  LCDH7_TFT R0 <-> PI.15  |  LCDH7_TFT G0 <-> PJ.07 |  LCDH7_TFT B0 <-> PJ.12      |
        |  LCDH7_TFT R1 <-> PJ.00  |  LCDH7_TFT G1 <-> PJ.08 |  LCDH7_TFT B1 <-> PJ.13      |
        |  LCDH7_TFT R2 <-> PJ.01  |  LCDH7_TFT G2 <-> PJ.09 |  LCDH7_TFT B2 <-> PJ.14      |
        |  LCDH7_TFT R3 <-> PJ.02  |  LCDH7_TFT G3 <-> PJ.10 |  LCDH7_TFT B3 <-> PJ.15      |
        |  LCDH7_TFT R4 <-> PJ.03  |  LCDH7_TFT G4 <-> PJ.11 |  LCDH7_TFT B4 <-> PK.03      |
        |  LCDH7_TFT R5 <-> PJ.04  |  LCDH7_TFT G5 <-> PK.00 |  LCDH7_TFT B5 <-> PK.04      |
        |  LCDH7_TFT R6 <-> PJ.05  |  LCDH7_TFT G6 <-> PK.01 |  LCDH7_TFT B6 <-> PK.05      |
        |  LCDH7_TFT R7 <-> PJ.06  |  LCDH7_TFT G7 <-> PK.02 |  LCDH7_TFT B7 <-> PK.06      |
        -------------------------------------------------------------------------------
        |  LCDH7_TFT HSYNC <-> PI.12  | LCDTFT VSYNC <->  PI.13 |
        |  LCDH7_TFT CLK   <-> PI.14  | LCDH7_TFT DE   <->  PK.07 |
        -----------------------------------------------------
        */        
        GPIO_InitTypeDef GPIO_Init_Structure;

        /*##-1- Enable peripherals and GPIO Clocks #################################*/  
        /* 使能LTDC和DMA2D时钟 */
        __HAL_RCC_LTDC_CLK_ENABLE();
        __HAL_RCC_DMA2D_CLK_ENABLE();  
        
        /* 使能GPIO时钟 */
        __HAL_RCC_GPIOI_CLK_ENABLE();
        __HAL_RCC_GPIOJ_CLK_ENABLE();
        __HAL_RCC_GPIOK_CLK_ENABLE();

        /* GPIOI 配置 */
        GPIO_Init_Structure.Pin       = GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; 
        GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
        GPIO_Init_Structure.Pull      = GPIO_NOPULL;
        GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
        HAL_GPIO_Init(GPIOI, &GPIO_Init_Structure);

        /* GPIOJ 配置 */  
        GPIO_Init_Structure.Pin       = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \\
                                      GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7 | \\
                                      GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | \\
                                      GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; 
        GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
        GPIO_Init_Structure.Pull      = GPIO_NOPULL;
        GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
        HAL_GPIO_Init(GPIOJ, &GPIO_Init_Structure);  

        /* GPIOK 配置 */  
        GPIO_Init_Structure.Pin       = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3 | \\
                                      GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_6 | GPIO_PIN_7; 
        GPIO_Init_Structure.Mode      = GPIO_MODE_AF_PP;
        GPIO_Init_Structure.Pull      = GPIO_NOPULL;
        GPIO_Init_Structure.Speed     = GPIO_SPEED_FREQ_HIGH;
        GPIO_Init_Structure.Alternate = GPIO_AF14_LTDC;  
        HAL_GPIO_Init(GPIOK, &GPIO_Init_Structure);      
    }
    
    /*##-2- LTDC初始化 #############################################################*/  
    {    
        uint16_t Width, Height, HSYNC_W, HBP, HFP, VSYNC_W, VBP, VFP;
        RCC_PeriphCLKInitTypeDef  PeriphClkInitStruct = {0};

        /* 支持6种面板 */
        switch (g_LcdType)
        {
            case LCD_35_480X320:    /* 3.5寸 480 * 320 */    
                Width = 480;
                Height = 272;
                HSYNC_W = 10;
                HBP = 20;
                HFP = 20;
                VSYNC_W = 20;
                VBP = 20;
                VFP = 20;
                break;
            
            case LCD_43_480X272:        /* 4.3寸 480 * 272 */            
                Width = 480;
                Height = 272;

                HSYNC_W = 40;
                HBP = 2;
                HFP = 2;
                VSYNC_W = 9;
                VBP = 2;
                VFP = 2;
        
                /* LCD 时钟配置 */
                /* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */
                /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */
                /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */
                /* LTDC clock frequency = PLLLCDCLK = 24MHz */
                /*
                    刷新率 = 24MHz /((Width + HSYNC_W  + HBP  + HFP)*(Height + VSYNC_W +  VBP  + VFP))
                              = 24000000/((480 + 40  + 2  + 2)*(272 + 9 +  2  + 2)) 
                           = 24000000/(524*285)
                           = 160Hz    

                    当前这个配置方便用户使用PLL3Q输出的48MHz时钟供USB使用。
                */
                PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
                PeriphClkInitStruct.PLL3.PLL3M = 5;
                PeriphClkInitStruct.PLL3.PLL3N = 24;
                PeriphClkInitStruct.PLL3.PLL3P = 2;
                PeriphClkInitStruct.PLL3.PLL3Q = 5;
                PeriphClkInitStruct.PLL3.PLL3R = 10;                
                HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);                 
                break;
            
            case LCD_50_480X272:        /* 5.0寸 480 * 272 */
                Width = 480;
                Height = 272;
            
                HSYNC_W = 40;
                HBP = 2;
                HFP = 2;
                VSYNC_W = 9;
                VBP = 2;
                VFP = 2;            
                break;
            
            case LCD_50_800X480:        /* 5.0寸 800 * 480 */
            case LCD_70_800X480:        /* 7.0寸 800 * 480 */                    
                Width = 800;
                Height = 480;

                HSYNC_W = 96;    /* =10时,显示错位,20时部分屏可以的,80时全部OK */
                HBP = 10;
                HFP = 10;
                VSYNC_W = 2;
                VBP = 10;
                VFP = 10;            

            ·
                PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
                PeriphClkInitStruct.PLL3.PLL3M = 5;
                PeriphClkInitStruct.PLL3.PLL3N = 48;
                PeriphClkInitStruct.PLL3.PLL3P = 2;
                PeriphClkInitStruct.PLL3.PLL3Q = 5;
                PeriphClkInitStruct.PLL3.PLL3R = 10; 
                HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);                 
                break;
            
            case LCD_70_1024X600:        /* 7.0寸 1024 * 600 */
                /* 实测像素时钟 = 53.7M */
                Width = 1024;
                Height = 600;

                HSYNC_W = 2;    /* =10时,显示错位,20时部分屏可以的,80时全部OK */
                HBP = 157;
                HFP = 160;
                VSYNC_W = 2;
                VBP = 20;
                VFP = 12;        
            
                PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
                PeriphClkInitStruct.PLL3.PLL3M = 5;
                PeriphClkInitStruct.PLL3.PLL3N = 48;
                PeriphClkInitStruct.PLL3.PLL3P = 2;
                PeriphClkInitStruct.PLL3.PLL3Q = 5;
                PeriphClkInitStruct.PLL3.PLL3R = 10;
                HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);             
                break;
            
            default:
                Width = 800;
                Height = 480;

                HSYNC_W = 80;    /* =10时,显示错位,20时部分屏可以的,80时全部OK */
                HBP = 10;
                HFP = 10;
                VSYNC_W = 10;
                VBP = 10;
                VFP = 10;        
            
                /* LCD 时钟配置 */
                /* PLL3_VCO Input = HSE_VALUE/PLL3M = 25MHz/5 = 5MHz */
                /* PLL3_VCO Output = PLL3_VCO Input * PLL3N = 5MHz * 48 = 240MHz */
                /* PLLLCDCLK = PLL3_VCO Output/PLL3R = 240 / 10 = 24MHz */
                /* LTDC clock frequency = PLLLCDCLK = 24MHz */
                /*
                    刷新率 = 24MHz /((Width + HSYNC_W  + HBP  + HFP)*(Height + VSYNC_W +  VBP  + VFP))
                              = 24000000/((800 + 96  + 10  + 10)*(480 + 2 +  10  + 10)) 
                           = 24000000/(916*502)
                           = 52Hz

                    根据需要可以加大,100Hz刷新率完全没问题,设置PeriphClkInitStruct.PLL3.PLL3N = 100即可
                    此时的LTDC时钟是50MHz
                    刷新率 = 50MHz /((Width + HSYNC_W  + HBP  + HFP )*(Height + VSYNC_W +  VBP  +VFP  )) 
                           = 5000000/(916*502) 
                           = 108.7Hz

                    当前这个配置方便用户使用PLL3Q输出的48MHz时钟供USB使用。
                */ 
                PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LTDC;
                PeriphClkInitStruct.PLL3.PLL3M = 5;
                PeriphClkInitStruct.PLL3.PLL3N = 48;
                PeriphClkInitStruct.PLL3.PLL3P = 2;
                PeriphClkInitStruct.PLL3.PLL3Q = 5;
                PeriphClkInitStruct.PLL3.PLL3R = 10;  
                HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct);             
                break;
        }        

        g_LcdHeight = Height;
        g_LcdWidth = Width;
        
        /* 配置信号极性 */    
        hltdc.Init.HSPolarity = LTDC_HSPOLARITY_AL;    /* HSYNC 低电平有效 */
        hltdc.Init.VSPolarity = LTDC_VSPOLARITY_AL;     /* VSYNC 低电平有效 */
        hltdc.Init.DEPolarity = LTDC_DEPOLARITY_AL;     /* DE 低电平有效 */
        hltdc.Init.PCPolarity = LTDC_PCPOLARITY_IPC;

        /* 时序配置 */    
        hltdc.Init.HorizontalSync = (HSYNC_W - 1);
        hltdc.Init.VerticalSync = (VSYNC_W - 1);
        hltdc.Init.AccumulatedHBP = (HSYNC_W + HBP - 1);
        hltdc.Init.AccumulatedVBP = (VSYNC_W + VBP - 1);  
        hltdc.Init.AccumulatedActiveH = (Height + VSYNC_W + VBP - 1);
        hltdc.Init.AccumulatedActiveW = (Width + HSYNC_W + HBP - 1);
        hltdc.Init.TotalHeigh = (Height + VSYNC_W + VBP + VFP - 1);
        hltdc.Init.TotalWidth = (Width + HSYNC_W + HBP + HFP - 1); 

        /* 配置背景层颜色 */
        hltdc.Init.Backcolor.Blue = 0;
        hltdc.Init.Backcolor.Green = 0;
        hltdc.Init.Backcolor.Red = 0;

        hltdc.Instance = LTDC;

        /* 配置LTDC  */  
        if (HAL_LTDC_Init(&hltdc) != HAL_OK)
        {
            /* 初始化错误 */
            Error_Handler(__FILE__, __LINE__);
        }
    }  

    /* 使能行中断 */
    HAL_LTDC_ProgramLineEvent(&hltdc, 0);
  
    /* 使能Dither */
    //HAL_LTDC_EnableDither(&hltdc);

    /* 使能LTDC中断,并配置其优先级 */
    HAL_NVIC_SetPriority(LTDC_IRQn, 0x2, 0);
    HAL_NVIC_EnableIRQ(LTDC_IRQn);
}

8.5.2  如何验证LTDC的时序配置是否正确

下面说一个最重要的问题,配置好时序了,怎么检查自己的配置是否成功了?用户仅需在函数LCD_LL_Init里面的如下代码后面加上两个函数:

/* 配置LTDC  */  
if (HAL_LTDC_Init(&hltdc_F) != HAL_OK)
{
    /* 初始化错误 */
    Error_Handler(__FILE__, __LINE__);
}

/* 下面是添加的 */
LCD_SetBackLight(BRIGHT_DEFAULT);
while(1);

加上这两行代码后,再将背景层设置为一个合适的颜色,建议设置成红色,方便观察:

/* 配置背景层颜色 */
hltdc_F.Init.Backcolor.Blue = 0;
hltdc_F.Init.Backcolor.Green = 0;
hltdc_F.Init.Backcolor.Red = 255;

如果背景层可以正常显示红色,说明引脚和时序配置都是没有问题的。如果不成功要从以下几个方面着手检查:

  • 首先要清楚一点,当前的配置是否成功与SDRAM没有任何关系,因为背景层还用不到SDRAM,图层1和图层2才需要SDRAM做显存使用。
  • 从硬件着手检查,保证STM32H7芯片焊接没问题,TFT接口一定要牢固,防止接触不良,特别是使用FPC软排线的时候,测试阶段,软排线越短越好。有时候也可能是显示屏有问题,最好可以备两个显示屏测试。
  • 从软件配置着手检查,查看LTDC涉及到的所有引脚是否配置,引脚时钟是否使能。有时候无法显示也有可能是板子硬件设计不规范导致干扰较大造成的,此时,可以降低LTDC所涉及到GPIO的速度等级。

如果显示了,但是显示的位置不正确,可以重新调整时序参数即可。

8.5.3  添加涉及到的所有文件到工程

实际上大家自己实现的话,仅需修改函数LCD_LL_Init,自己实现此函数即可,无需其它任何配置。由于我们开发板要做不同显示屏的自适应,所以关联了好多个文件,所有关于TFT,触摸,触摸校准参数保存和字体的文件都要添加进来:

  

下面把这些新添加的文件做一个简单的介绍:

  •   GUI/Driver分组中的文件

bsp_tft_h7.c --- STM32H7的LTDC驱动文件。

bsp_tft_lcd.c --- TFT驱动和相关API函数汇总文件,比如用户的RA8875显示屏,ili9488显示屏,F429/H7所带TFT控制器驱动显示屏都可以有一个单独的文件,然后将这些显示屏相同功能的函数汇总成一个函数。这个文件就起到这个作用。emWin仅用到这个文件里面的全局变量g_LcdHeight,g_LcdWidth以及背光函数LCD_SetBackLight,其它所有函数都没有用到。

bsp_ts_touch.c --- 触摸芯片自适应驱动,根据用户使用的触摸IC选择不同的驱动。另外,电阻屏的触摸扫描,触摸校准和触摸滤波也是在这个文件里面实现。

bsp_ts_gt811.c --- 电容触摸芯片GT811的驱动以及触摸扫描。

bsp_ts_gt911.c --- 电容触摸芯片GT911的驱动以及触摸扫描。

bsp_ts _ft5x06.c --- 电容触摸芯片FT5X06的驱动以及触摸扫描。

bsp_ts_stmpe811.c --- 电阻触摸芯片STMPE811的驱动。

  •   GUI/GUI_OS分组中的文件

GUI_X.c --- emWin裸机接口文件,主要是emWin的时间基准和延迟实现。

  •   GUI/GUIConfig分组中的文件

GUIConf.c 和.h--- emWin动态内存的设置。

LCDConf_Lin_Template.c和.h --- emWin的LCD接口文件。

  •   GUI/Demo分组中的文件

MainTask.c 和.h--- emWin应用设计文件。

  •   GUI/JPEG分组中的文件

这几个文件全部用于STM32H7的硬件JPEG实现。

  •   GUI/Lib分组中的文件

emWin的库文件。

  •  Fonts分组中的文件

所有这些文件,emWin都用不上,只是因为被文件bsp_tft_lcd.c文件关联了。

asc12.c ---12点阵ASCII字符字库

asc16.c---16点阵ASCII字符字库

asc24.c ---24点阵ASCII字符字库

asc32.c---23点阵ASCII字符字库

hz12.c --- 12点阵宋体小字库

hz16.c --- 16点阵宋体小字库

hz24.c --- 24点阵宋体小字库

hz32.c --- 32点阵宋体小字库

ra8875_asc_width.c -- RA8875 ASCII字体的宽度表

  •   bsp分组中的文件

这个分组里面的三个文件,emWin都要间接用到。

bsp_tim_pwm.c --- 定时器驱动,显示屏的背光要用到PWM。

bsp_i2c_gpio.c --- I2C接口驱动,EEPROM,GT811,GT911,STMPE811和FT5X06都要用到,因为他们的接口都是I2C方式。

bsp_eeprom_24xx.c --- EEPROM驱动,用于存储电阻屏触摸校准参数。

bsp_fmc_sdram.c --- SDRAM驱动文件。

  •   HAL_Driver分组中的文件

圈出来的几个文件,emWin都要间接用到。

8.6   第4步:电阻屏和电容屏触摸驱动的实现

本小节的实现基于本教程的第5章,当前驱动对电阻触摸芯片STMPE811和电容触摸芯片FT5X06、GT911和GT811的显示屏都进行了支持。

实现比较简单,因为GUIX的触摸分按下,松手和移动三个事件,正好这几款触摸芯片的驱动也是分这三个事件,所以仅需修改下函数TOUCH_PutKey,所有显示屏触摸就都可以完美融合了。

8.6.1  添加GUIX的按下,松手和移动三个事件

文件bsp_ts_touch.c里的函数TOUCH_PutKey修改如下:

/*
*********************************************************************************************************
*    函 数 名: TOUCH_PutKey
*    功能说明: 将1个触摸点坐标值压入触摸FIFO缓冲区。电阻触摸屏形参是ADC值,电容触摸屏形参是坐标值
*    形    参: _usX, _usY 坐标值
*    返 回 值: 无
*********************************************************************************************************
*/
GUI_PID_STATE State;
void TOUCH_PutKey(uint8_t _ucEvent, uint16_t _usX, uint16_t _usY)
{
#if 1

    uint16_t xx, yy;


    if (g_tTP.Enable == 1)    /* 电阻屏。 形参是ADC值 */
    {
        xx = TOUCH_TransX(_usX, _usY);
        yy = TOUCH_TransY(_usX, _usY);
    }
    else    /* GT811,FTX06,GT911 电容触摸走此分之 */
    {
        /* 无需转换, 直接是坐标值 */
        xx = _usX;
        yy = _usY;        
    }
    
    /* 按下, 移动和松手事件 */
    switch (_ucEvent)
    {
        case TOUCH_DOWN:
            State.x = xx;
            State.y = yy;
            State.Pressed = 1;
            GUI_PID_StoreState(&State);
           break;

        case TOUCH_MOVE:
            State.x = xx;
            State.y = yy;
            State.Pressed = 1;
            GUI_PID_StoreState(&State);
           break;

        case TOUCH_RELEASE:
            State.Pressed = 0;
            GUI_PID_StoreState(&State);
            break;

        default:
            break;
    }

#else
    省略未写
#endif
}

通过函数GUI_PID_StoreState将触摸坐标轴存储到emWin中。

8.6.2  周期性调用触摸扫描函数

电阻触摸和电容触摸的扫描函数是TOUCH_Scan和TOUCH_CapScan,为了实现使用了ThreadX和裸机时的一样的调用方式,专门在启动任务里面周期性的调用函数bsp_ProPer1ms(SysTick_ISR),而SysTick_ISR里面调用了bsp_RunPer1ms,然后bsp_RunPer1ms里面调用扫描函数,即如下的调用关系:

代码如下:

/*
*********************************************************************************************************
*    函 数 名: AppTaskStart
*    功能说明: 这是一个启动任务,在多任务系统启动后,必须初始化滴答计数器。本任务主要实现按键检测。
*    形    参: p_arg 是在创建该任务时传递的形参
*    返 回 值: 无
    优 先 级: 2
*********************************************************************************************************
*/
static  void  AppTaskStart (void *p_arg)
{
    OS_ERR      err;

   (void)p_arg;
    
    HAL_ResumeTick();
    
    CPU_Init(); /* 此函数要优先调用,因为外设驱动中使用的us和ms延迟是基于此函数的 */
     bsp_Init();
    BSP_OS_TickEnable();
    
#if OS_CFG_STAT_TASK_EN > 0u
     OSStatTaskCPUUsageInit(&err);   
#endif

#ifdef CPU_CFG_INT_DIS_MEAS_EN
    CPU_IntDisMeasMaxCurReset();
#endif
    
    /* 创建任务 */
    AppTaskCreate(); 

    /* 创建任务间通信机制 */
    AppObjCreate();    
    
    while (1)
    {  
        /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */
        bsp_ProPer1ms();
        OSTimeDly(1, OS_OPT_TIME_PERIODIC, &err);
    }
}

8.6.3  如何将触摸驱动移植到自己的板子

通过前面的讲解,移植触摸驱动到自己的板子上,最简单的办法是将开发板与触摸相关的文件全部移植过来,然后在这些文件的基础上进行修改。下面分两种情况进行说明:

  •   电容屏触摸的移植比较简单,如果用户用的触摸IC跟开发板一样,直接拿来用即可,如果不一样,需要先将触摸IC的驱动实现,然后按照开发板提供的GT911,GT811或者FT5X06的触摸扫描函数,照葫芦画瓢实现一个即可。
  •   电阻屏的移植稍麻烦些,如果用户用的触摸IC跟开发板一样,直接拿来用即可,如果不一样,需要先将触摸IC的驱动实现,然后仿照bsp_ts_stmpe811.c文件提供触摸按下状态函数,X轴,Y轴的ADC数值读取函数和清除触摸中断标志函数。最后用重新实现的这几个函数替换bsp_ts_touch.c文件中的原函数即可。另外要注意一点,这种方式实现后,虽然触摸校准依然可以使用,但是开发板的触摸校准参数是保存在EEPROM中的,用户可以根据自己的实际情况选择存储介质。另外,触摸参数的保存和读取在bsp_ts_touch.c文件末尾的函数TOUCH_SaveParam和TOUCH_LoadParam实现。

如果大家不想用开发板实现的方案,想自己重新实现一个,也是没问题的,注意跟emWin关联的方式。

8.7   第5步:emWin底层接口函数和配置

emWin的底层接口函数和配置的实现在我们第1步中添加的GUIConf.c及其头文件和LCDConf_Lin_Template.c及其头文件里面。下面我们把这四个文件分别做个介绍。

8.7.1  GUIConf.c文件

GUIConf.c文件主要实现系统动态内存的配置,我们在官方代码的基础上做了下修改,添加了一个宏定义,方便用户选择使用内部 SRAM还是外部SDRAM作为emWin的动态内存:

#include "GUI.h"
#include "bsp.h"


/*********************************************************************
*
*       Defines
*
**********************************************************************
*/
//
// Define the available number of bytes available for the GUI
//
#define EX_SRAM   1/*1 used extern sram, 0 used internal sram */   //--------------(1)

#if EX_SRAM
#define GUI_NUMBYTES  (1024*1024*24) 
#else
#define GUI_NUMBYTES  (100*1024)    
#endif

/* Define the average block size */
#define GUI_BLOCKSIZE 0x80          //--------------(2)



/*********************************************************************
*
*       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) 
{
#if EX_SRAM                                    //--------------(3)
    static U32 *aMemory;
    aMemory = (U32 *)SDRAM_APP_BUF;
    
    /*  Assign memory to emWin */
    GUI_ALLOC_AssignMemory(aMemory, GUI_NUMBYTES);  
    GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);
#else                                         //--------------(4)
//    /* 32 bit aligned memory area */
//    static U32 aMemory[GUI_NUMBYTES / 4];
    static U32 *aMemory;
    aMemory = (U32 *)0x24000000;    
    /*  Assign memory to emWin */
    GUI_ALLOC_AssignMemory(aMemory, GUI_NUMBYTES);
    GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);
#endif
}

1、通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:

#define  EX_SRAM     1 表示使用外部SDRAM作为emWin动态内存,大小24MB。具体大小是由宏定义#define GUI_NUMBYTES  (1024*1024*24)来设置的。

#define  EX_SRAM     0 表示使用内部SRAM作为emWin动态内存,大小100KB。具体大小是由宏定义#define GUI_NUMBYTES  (100*1024)来设置的。

默认情况下,本教程配套的所有emWin例子都是用外部SDRAM作为emWin动态内存。

2、配置使用外部SDRAM作为emWin动态内存,其中SDRAM_APP_BUF是emWin动态内存的首地址。通过函数GUI_ALLOC_AssignMemory为emWin分配动态内存,注意第二个参数的单位是字节。

通过函数GUI_ALLOC_SetAvBlockSize配置内存块大小,官方手册推荐的大小范围是32字节到1024字节,一般情况下取0x80,即128字节即可。

3、配置使用内部SRAM作为emWin动态内存。其中定义的局部静态变量数组就是emWin动态内存。

我们这里是设置使用了AXI SRAM的100KB空间。

8.7.2  GUIConf.h文件

这个文件主要是默认的系统配置,代码如下:

#ifndef GUICONF_H
#define GUICONF_H

/*********************************************************************
*
*       Multi layer/display support
*/

#define GUI_NUM_LAYERS            2    /* Maximum number of available layers */ 
#define OS_SUPPORT
/*********************************************************************
*
*       Multi tasking support
*/
#ifdef OS_SUPPORT
 #define GUI_OS                    (1)  /* Compile with multitasking support */ 
#else
 #define GUI_OS                    (0)
#endif

/*********************************************************************
*
*       Configuration of touch support
*/
#ifndef   GUI_SUPPORT_TOUCH
  #define GUI_SUPPORT_TOUCH       (1)  /* Support touchscreen */
#endif

/*********************************************************************
*
*       Default font
*/
#define GUI_DEFAULT_FONT          &GUI_Font6x8

/*********************************************************************
*
*         Configuration of available packages
*/
#define GUI_SUPPORT_MOUSE             (1)    /* Support a mouse */
#define GUI_WINSUPPORT                (1)    /* Use window manager */
#define GUI_SUPPORT_MEMDEV            (1)    /* Memory device package available */
#define GUI_SUPPORT_DEVICES           (1)    /* Enable use of device pointers */
#define BUTTON_REACT_ON_LEVEL         (1)    /* Enable button reaction on level */
#define GUI_MEMDEV_SUPPORT_CUSTOMDRAW (1)    /* Enable use of memdev custom draw */
#define GUI_USE_ARGB                  (0)    /* Enable use of ARGB color mode */


#endif  /* Avoid multiple inclusion */

由于emWin是库文件,这些宏定义设置对库是不起作用的,只能运行时调用API配置。其中这个GUI_USE_ARGB也是不起作用(0表示使用ABGR格式,1表示使用ARGB格式),但是常用颜色值用到这个宏定义了,定义在GUI.h文件。此宏定义对下面的颜色值起作用,所以大家使用要注意:

/*********************************************************************
*
*       Standard colors
*/
#define GUI_INVALID_COLOR ((((U32)GUI_TRANS_BYTE) << 24) | 0x00ABCDEFul)  /* Invalid color (transparency + determined color) */

#if (GUI_USE_ARGB)
  #define GUI_MAKE_COLOR(ABGR)  (((((U32)ABGR) & 0xFF000000ul) ^ 0xFF000000ul) | ((((U32)ABGR) & 0x00FF0000ul) >> 16) | (((U32)ABGR) & 0x0000FF00ul) | ((((U32)ABGR) & 0x000000FFul) << 16))
  #define GUI_MAKE_TRANS(Alpha) (255 - (Alpha))
#else
  #define GUI_MAKE_COLOR(ABGR)  (ABGR)
  #define GUI_MAKE_TRANS(Alpha) (Alpha)
#endif

#if (GUI_USE_ARGB)
  #define GUI_TRANS_BYTE 0x00
#else
  #define GUI_TRANS_BYTE 0xFF
#endif

#define GUI_BLUE          GUI_MAKE_COLOR(0x00FF0000)
#define GUI_GREEN         GUI_MAKE_COLOR(0x0000FF00)
#define GUI_RED           GUI_MAKE_COLOR(0x000000FF)
#define GUI_CYAN          GUI_MAKE_COLOR(0x00FFFF00)
#define GUI_MAGENTA       GUI_MAKE_COLOR(0x00FF00FF)
#define GUI_YELLOW        GUI_MAKE_COLOR(0x0000FFFF)
#define GUI_LIGHTBLUE     GUI_MAKE_COLOR(0x00FF8080)
#define GUI_LIGHTGREEN    GUI_MAKE_COLOR(0x0080FF80)
#define GUI_LIGHTRED      GUI_MAKE_COLOR(0x008080FF)
#define GUI_LIGHTCYAN     GUI_MAKE_COLOR(0x00FFFF80)
#define GUI_LIGHTMAGENTA  GUI_MAKE_COLOR(0x00FF80FF)
#define GUI_LIGHTYELLOW   GUI_MAKE_COLOR(0x0080FFFF)
#define GUI_DARKBLUE      GUI_MAKE_COLOR(0x00800000)
#define GUI_DARKGREEN     GUI_MAKE_COLOR(0x00008000)
#define GUI_DARKRED       GUI_MAKE_COLOR(0x00000080)
#define GUI_DARKCYAN      GUI_MAKE_COLOR(0x00808000)
#define GUI_DARKMAGENTA   GUI_MAKE_COLOR(0x00800080)
#define GUI_DARKYELLOW    GUI_MAKE_COLOR(0x00008080)
#define GUI_WHITE         GUI_MAKE_COLOR(0x00FFFFFF)
#define GUI_LIGHTGRAY     GUI_MAKE_COLOR(0x00D3D3D3)
#define GUI_GRAY          GUI_MAKE_COLOR(0x00808080)
#define GUI_DARKGRAY      GUI_MAKE_COLOR(0x00404040)
#define GUI_BLACK         GUI_MAKE_COLOR(0x00000000)
#define GUI_BROWN         GUI_MAKE_COLOR(0x002A2AA5)
#define GUI_ORANGE        GUI_MAKE_COLOR(0x0000A5FF)
#define GUI_TRANSPARENT   GUI_MAKE_COLOR(0xFF000000)

#define GUI_GRAY_3F       GUI_MAKE_COLOR(0x003F3F3F)
#define GUI_GRAY_50       GUI_MAKE_COLOR(0x00505050)
#define GUI_GRAY_55       GUI_MAKE_COLOR(0x00555555)
#define GUI_GRAY_60       GUI_MAKE_COLOR(0x00606060)
#define GUI_GRAY_7C       GUI_MAKE_COLOR(0x007C7C7C)
#define GUI_GRAY_9A       GUI_MAKE_COLOR(0x009A9A9A)
#define GUI_GRAY_AA       GUI_MAKE_COLOR(0x00AAAAAA)
#define GUI_GRAY_C0       GUI_MAKE_COLOR(0x00C0C0C0)
#define GUI_GRAY_C8       GUI_MAKE_COLOR(0x00C8C8C8)
#define GUI_GRAY_D0       GUI_MAKE_COLOR(0x00D0D0D0)
#define GUI_GRAY_E7       GUI_MAKE_COLOR(0x00E7E7E7)
#define GUI_BLUE_98       GUI_MAKE_COLOR(0x00980000)

另外,LCDConf_Lin_Template.c也用到了GUI_USE_ARGB宏定义,大家使用要注意。也是说提供的驱动文件既支持ARGB格式的emWin库,也支持ABGR格式的emWin库,其中MDK提供的emWin库仅支持ABGR格式,而ST提了ARGB和ABGR两种格式库,但emWin版本较低。

8.7.3  LCDConf_Lin_Template.c文件

毫不夸张的说,这个文件是emWin移植过程中最重要的文件,主要配置了emWin的显示尺寸,显示驱动,颜色转换以及底层优化都是在这个文件实现的。当前这个驱动文件已经做得比较成熟完善了,为了完善这个驱动耗费了很多精力。emWin支持的多缓冲,多图层和STM32H7支持的常用颜色格式都可以配置。

如果用户要使用这个文件配置自己的显示屏,直接拿去使用即可,唯一要做的就是根据自己的显示屏和项目需求来设置宏定义,修改本文件中函数LCD_LL_Init里面的时序参数以及显示屏背光设置函数LCD_SetBackLight(这个函数要自己实现,教程提供的例子是在bsp_tft_lcd.c文件中实现),别的什么都不用修改。

供用户使用的选项宏定义,注释已经非常详细,默认情况下,本教程配套的emWin例子都是用的三缓冲,RGB565格式,且仅使用单图层:

/*
**********************************************************************************************************
                                    用户可以配置的选项
**********************************************************************************************************
*/
/* 0 暂时用不到这几个功能 */
#define DrawBitmapA4Enalbe    0
#define ClearCacheHookEnalbe  0
#define DrawAlpha             0
#define UsedDrawBitmap8bpp    0

/* 
  1. 显示屏的物理分辨率,驱动已经做了显示屏自适应,支持4.3寸,5寸和7寸屏
     这里填写自适应显示屏中的最大分辨率。
*/
#define XSIZE_PHYS       800
#define YSIZE_PHYS       480

/* 2. 旋转方向,暂未使用此选项,直接在工程运行时做旋转即可 */
#define ROTATION_0       0
#define ROTATION_CW      1
#define ROTATION_180     2
#define ROTATION_CCW     3

/* 
   3. STM32H7支持的颜色模式。
      (1) 如果打算使用24位色或者32位色,请选择CMS_ARGB8888,如果使用16位色,请选择CMS_RGB565,其它颜色格式不再做支持。
      (2) 如果用户选择了ARGB8888或者RGB888模式,LCD闪烁的话,优先查看是否是此贴的问题:
          http://www.armbbs.cn/forum.php?mod=viewthread&tid=16892 (适用于F429和H7系列)
          如果不是这个问题,再降低LTDC输出时钟即可,在本文件的函数LCD_ConfigLTDC里面    
*/
#define CMS_ARGB8888 1
#define CMS_RGB888   2
#define CMS_RGB565   3
#define CMS_ARGB1555 4
#define CMS_ARGB4444 5
#define CMS_L8       6
#define CMS_AL44     7
#define CMS_AL88     8

/* 4. 多缓冲 / 虚拟屏,多缓冲和虚拟屏不可同时使用,emWin不支持 */
#define NUM_BUFFERS      3 /* 定义多缓冲个数,仅可以设置1,2和3,也就是最大支持三缓冲 */
#define NUM_VSCREENS     1 /* 定义虚拟屏个数 */

/* 
   5. 重定义图层数,对于STM32H7,用户可以选择一个图层或者两个图层,不支持三图层 
      (1). 设置GUI_NUM_LAYERS = 1时,即仅使用图层1时,默认触摸值是发送给图层1的。
      (2). 设置GUI_NUM_LAYERS = 2时,即图层1和图层2都已经使能,此时图层2是顶层,
           用户需要根据自己的使用情况设置如下两个地方。
           a. 在bsp_touch.c文件中的函数TOUCH_InitHard里面设置参数State.Layer = 1,1就表示
              给图层2发送触摸值。
           b. 调用GUI_Init函数后,调用函数GUI_SelectLayer(1), 设置当前操作的是图层2。
*/
#undef  GUI_NUM_LAYERS
#define GUI_NUM_LAYERS 1

/* 
   6. 设置图层1和图层2对应的显存地址
      (1) EXT_SDRAM_ADDR 是SDRAM的首地址。
      (2) LCD_LAYER0_FRAME_BUFFER 是图层1的显存地址。
      (3) LCD_LAYER1_FRAME_BUFFER 是图层2的显存地址。
      (4) 每个图层的显存大小比较考究,这里进行下简单的说明。
          如果用户选择的颜色模式 = 32位

以上是关于第3版emWin教程第8章 emWin6.x的带OS方式移植(STM32H7之RGB接口)的主要内容,如果未能解决你的问题,请参考以下文章

第3版emWin教程第36章 emWin6.x光标

第3版emWin教程第37章 emWin6.x抗锯齿

第3版emWin教程第35章 emWin6.x的AppWizard中文实现方法

第3版emWin教程第38章 emWin6.x多任务设计

第3版emWin教程第49章 emWin6.x的AppWizard创建控件回调消息

第3版emWin教程第12章 emWin6.x文本显示