硬汉嵌入式论坛

 找回密码
 立即注册
查看: 4405|回复: 11
收起左侧

[emWin教程入门篇] 【emWin实战教程V2.0】第4章    emWin5.xx的裸机方式移植(F4

[复制链接]

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
发表于 2016-12-26 15:53:29 | 显示全部楼层 |阅读模式
完整65章+12章附件教程下载地址:
http://www.armbbs.cn/forum.php?mod=viewthread&tid=19834


                       第4章       emWin5.xx的裸机方式移植(F429 之RGB接口)


    本章节为大家讲解emWin5.xx的裸机方式移植,不建议初学者直接学习,因为本章节涉及到的知识点很多,建议对emWin的应用有一些了解后再来看,这样将事半功倍。本章节提供的移植方法支持emWin的多图层配置,多缓冲配置以及STM32F429/439支持的8种颜色格式的实现。同时可以自适应我们生产的4.3寸,5寸和7寸的电阻屏和电容屏。
    虽然本章节是以我们开发板为例进行移植的,但是教会大家如何移植到自己的板子上以及移植过程中的注意事项是本章节的重点。
4.1初学者重要提示
4.2显示屏相关的基础知识
4.3移植前的准备工作以及移植STemWin的流程
4.4第1步:下载STemWin库并添加到工程模板
4.5第2步:SDRAM驱动的实现
4.6第3步:LTDC涉及到的引脚配置和时序配置
4.7第4步:电阻屏和电容屏触摸驱动的实现
4.8第5步:STemWin底层接口和配置
4.9第6步:STemWin裸机方式的接口文件
4.10第7步:添加STemWin应用进行测试
4.11显示屏闪烁文件解决方法
4.12      总结

4.1  初学者重要提示

1、初学时不要一上来就研究emWin的底层驱动接口,比较影响初学的积极性,搞得后面越来越没有兴趣去研究了。
2、本章节是以移植到MDK上面为例进行讲解的,如果是移植到IAR上,方法是一样的。
3、初始化emWin前,务必记得使能STM32F429/439的硬件CRC时钟,因为ST官方对他们的emWin库做了保护,否则emWin无法启动。
4、学习本章节前,务必花点时间把STM32F429/439参考手册LTDC章节的开头部分读一读,如果一下子没有理解,也是没有关系的,随着本教程的学习以及经验的积累,不理解的知识点也就慢慢明白了。
5、由于开发板要自适应4.3寸,5寸和7寸显示屏,而且还分电阻触摸和电容触摸,所以移植过程中添加的文件稍多。虽然移植是以我们的开发板为例进行讲解的,但是重点依然是告诉大家如何移植自己的板子以及移植过程中需要注意的事项。
6、对于本章节的移植,我们需要先从整体上把控。由于开发板已经把需要移植的文件都整理好了,用户仅需添加文件就可以使用。我们这里着重介绍如何移植到自己的板子上面,这个才是本章节的重点。

        (1)显示屏的移植
            emWin需要的底层接口函数已经全部集成在LCDConf_Lin_Template.c文件里面,此文件已经比较成熟了,基本没有bug。对于这个文件,用户仅需学会使用里面的12个宏配置以及提供一个显示屏背光调节函数LCD_SetBackLight即可,其它都不用做任何修改。
             另外还有一个LTDC涉及到的引脚和时序配置的问题,这个是需要用户自己去实现的,配置方法已经在本章节的4.6小节进行讲解。如果这个也配置完成了,emWin的显示屏移植就完成了。
        (2)触摸的移植
            电容触摸的移植比较容易,因为电容触摸芯片可以自动触摸校准,所以仅需配置完触摸芯片后将触摸芯片返回的触摸坐标(电容触摸芯片返回的就是实际的坐标值)和触摸按下状态通过函数GUI_PID_StoreState存储到指针输入设备的FIFO里面即可。
            电阻触摸的移植要稍麻烦些,由于电阻触摸板的线性度不是很好,如果不做触摸校准和滤波处理会有点击不准确和飞点问题。emWin本身是支持两点触摸校准的,实际测试发现效果并不是很好,容易出现飞点,特别是使用线性度比较差的电阻触摸板。针对此问题,我们专门做了一个四点触摸校准,实际效果好了很多。其中触摸滤波方法是检测到触摸后先延迟30ms,消除抖动,然后采集10组坐标值做升序排列,去掉最大的几组坐标和最小的几组坐标,对中间的几组求平均作为最终的数值(电容触摸芯片返回的是ADC数值,不是实际坐标值)。然后将最终的数值代入通过触摸校准建立的线性公式来获得实际的坐标值,此时就可以将触摸坐标和触摸按下状态通过函数GUI_PID_StoreState存储到指针输入设备的FIFO里面。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 15:56:48 | 显示全部楼层
4.2   显示屏相关的基础知识



4.2.1   显示屏相关知识


    显示屏的结构有必要给大家普及下,这里我们通过如下三种类型的显示屏进行说明,基本已经涵盖我们常用的方式了。
(1)RA8875 + RGB接口裸屏
    首先RA8875是一个显示屏控制器,自带显存,它的作用就是让不支持RGB接口的MCU也可以使用RGB接口的大屏。这起到了一个桥接的作用,可以将RGB接口屏转换成8080总线接口,SPI接口或者I2C接口方式,这种情况下,甚至低速的51单片机都可以外接大屏了。另外像SSD1963也是同样的作用。
(2)ili9488类显示屏
    这种类型是把显示控制器和显示屏都集成好了,支持8080总线接口,有些还支持SPI或者I2C接口,而且显存也都集成了,不过主要是驱动一些小屏。像ili9341,ili9326,SPFD5420等也是一样的。此外还要注意,部分这种类型显示屏也是支持RGB接口的,像ST官方的STM32F429探索板外接的ili9431就是用的RGB接口。
(3)STM32F429/439 +SDRAM + RGB接口裸屏
    这个是我们本章节要讲解的,STM32F429/439是自带LCD控制器的,再配合SDRAM作为显示屏的显存,整体作用跟RA8875是一样的,可以直接外接RGB接口的屏了。

    有了这些认识后,对于裸屏还有些知识点需要了解。首先,裸屏本身不是什么控制芯片都没有,其构成也是比较复杂的,有兴趣了解的话,可以搜索关键字“TFT结构”进行学习。其次,TFT裸屏中主要的两个IC是Gate Driver IC和SourceDriver IC,这两个IC的引脚都超级多,基本都是几百个引脚。最后,不管使用的哪种裸屏,一般都有规格书,会给出时序参数,这个在配置STM32F429/F439的LTDC时要用到,如果规格书没有直接给出时序参数,则会给出使用的Driver IC型号,用户可以搜索此Driver IC的手册,在手册中会给出。
    为了让大家有个感性认识,我们来看一看TFT裸屏的实际效果,下面是SPDF5420显示屏,400*240分辨率:
4.1.png

下面是TFT裸屏,480*272分辨率:
4.2.png

下面是TFT裸屏,800*480分辨率:
4.3.png


4.2.2  电阻触摸和电容触摸相关知识

    有了TFT裸屏后还要配套电阻触摸板或者电容触摸板才可以获取触摸信息。触摸板是贴到TFT屏上面的,然后再通过电阻触摸芯片就可以获取电阻触摸板的信息,通过电容触摸芯片采集电容触摸板的信息。教程配套开发板的显示屏使用了三种触摸IC,电阻触摸IC是STMPE811,电容触摸IC是GT811和FT5X06。其中,电阻触摸和电容触摸两者的区别是初学者务必要知道的:
(1)电阻触摸芯片STMPE811其实就是ADC,返回的是ADC数值,而电容触摸芯片GT811FT5X06返回的是显示屏实际的坐标值。
(2)使用电阻触摸芯片STMPE811需要做触摸校准,而使用电容触摸芯片GT811FT5X06是自动校准的,无需手动校准。
下面是四线电阻触摸板的效果:
4.4.png

下面是电容触摸板的效果:
4.5.png

了解了这些知识,基本已经够我们本章节使用了,更多电阻触摸和电容触摸的相关知识可以看这个文档,讲解比较全面:http://www.armbbs.cn/forum.php?mod=viewthread&tid=14898
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 15:58:10 | 显示全部楼层
4.3  移植前的准备工作以及移植STemWin的流程

移植前注意以下两个问题:
1、本章节的IDE开发环境是MDK,用MDK4.7X或者MDK5.XX均可,保证支持STM32F429/439即可。
2、准备一个简单的工程,最好是跑马灯之类的,越简单越好,我们就在这个简单的工程上面移植即可。
STemWin库的移植通过以下7步完成,下面各个小节详细讲解每一步:
第1步:从ST官方下载STemWin软件包,将库文件和配置文件都添加到工程中。
第2步:SDRAM驱动的实现。
第3步:STM32F429/439的LCD控制器LTDC涉及到的引脚初始化和时序配置。
第4步:电阻屏和电容屏触摸驱动的实现。
第5步:STemWin底层接口和配置。
第6步:STemWin裸机方式的接口文件。
第7步:添加STemWin应用,测试是否正常。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:02:38 | 显示全部楼层
4.4   第1步:下载STemWin库并添加到工程模板


    (提示:本小节要实现的操作,最简单的方法是复制教程配套例子里面的emWin文件夹到自己的工程目录即可)。
    首先准备好一个简单的裸机工程模板,工程模板的制作就不做讲解了,这里的重点是教大家移植。准备好的工程模板如下图所示(大家也可以制作其它任意的工程模板,不限制):
4.6.png

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


第1步:按照第2章2.3.4小节讲解的方法下载用于STM32F4xx的STemWin软件包,软件包位于路径STM32Cube_FW_F4_V1.13.0\\Middlewares\\ST\\STemWin文件夹里面(如果软件包升级了,路径是类似的),下面是STemWin软件包内容:
4.7.png

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

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

Config文件夹用于添加配置文件和emWin底层驱动接口文件。
emWinTask文件用于添加用户自己的应用代码文件。
GUI_X文件用于添加裸机或者RTOS方式的接口文件。
GUILib文件用于添加emWin库文件。
Include文件用于添加头文件。


第3步:添加源码文件到相应的子文件夹
    (1)Config文件中添加如下四个文件:
4.10.png

     这四个文件来自STemWin软件包里面的Config文件夹。
    (2)emWinTask文件中添加如下两个文件:
4.11.png

    这两个文件是需要用户自己实现的测试代码,这里大家可以直接复制我们开发板例子在此文件夹下的这两个文件:V6-501_STemWin实验_裸机方式移植模板(含MDK和IAR)。
    (3)GUI_X文件夹中添加如下文件:
4.12.png

    这个文件来自STemWin软件包里面的OS文件夹。
    (4)GUILib文件中添加如下文件:
4.13.png

    这两个文件来自STemWin软件包里面的Lib文件夹。
        STemWin532_CM4_IAR_ot.a表示emWin版本为5.32,用于CM4内核MCU且被优化了的IAR版emWin库(字母ot是单词optimization的缩写)。
        STemWin532_CM4_Keil_ot.lib表示emWin版本为5.32,用于CM4内核MCU且被优化了的KEIL版emWin库(字母ot是单词optimization的缩写)。
(5)Include文件中添加如下文件(部分截图):
4.14.png

    将STemWin软件包inc文件夹里面的所有文件都复制到Include文件夹里。


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

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

    至此,我们需要的emWin文件都已经添加完毕。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:04:29 | 显示全部楼层
4.5    第2步:SDRAM驱动的实现


    (提示:本小节要实现的操作,最简单的方法是复制教程配套例子里面的SDRAM驱动到自己的工程目录进行调试修改即可,或者复制官方F429综合板子或者探索板子的SDRAM驱动进行调试修改也是可以的
    一定要保证SDRAM大批量读写数据时是正常的,SDRAM的测试可以自己专门做一个工程测试下。对于SDRAM的驱动实现,可以学习我们写的教程:http://www.armbbs.cn/forum.php?mod=viewthread&tid=1942 。不管你使用的是镁光的,海力士的,三星的,ISSI的或者华邦的,实现方法基本都是一样的。
    教程配套的板子使用的是镁光32位带宽的SDRAM,如果想最大限度的发挥STM32F429/439驱动SDRAM的性能,强烈建议使用32位带宽的SDRAM,或者两个16位SDRAM组成32位带宽的SDRAM也是可以的。那SDRAM主要起到什么作用呢?作用有二:
1、用作显示屏的显存
        STM32F429/439的LTDC外接RGB接口屏是没有显存的,所以需要SDRAM用作显存。如果用户选择STM32F429/439 LTDC的颜色格式是32位色ARGB8888,那么所需要显存大小(单位字节)是:显示屏宽 * 显示屏高 * (32/8), 其中32/8是表示这种颜色格式的一个像素点需要4个字节来表示。又比如配置颜色格式是16位色的RGB565,那么需要的显存大小是:显示屏宽 * 显示屏高 * (16/8),其中16/8是表示这种颜色格式的一个像素点需要2个字节来表示。其它的颜色格式,依此类推。
2、用作emWin动态内存
        emWin是极其消耗动态内存的,所以用户可以将SDRAM除了用于显存以外的所有内存全部用作emWin动态内存。
============================================================
    如果SDRAM的驱动测试已经没有问题了,就可以将其添加到工程里面了,开发板使用的SDRAM驱动文件是bsp_fmc_sdram.c。
4.17.png

添加到工程里面后要分配SDRAM的使用,教程配套开发板使用的是16MB,32位带宽的SDRAM,图层1占用4MB,图层2占用4MB,最后8MB给emWin动态内存使用。也许会有初学者会问,每个图层分配4MB是不是有些多了?实际上不多的,因为我们要让不同的颜色格式都通用,而且要满足三缓冲对显存的需求,这里分配4MB的话,教程实例使用很方便。大家实际项目中的使用可以配置成实际大小。具体的配置如下,详情见bsp_fmc_sdram.h文件:
  1. /*
  2. **********************************************************************************************************
  3.                                LCD显存使用,共使用8MB,SDRAM容量16MB
  4. **********************************************************************************************************
  5. */
  6. /* LCD显存, 图层1, 分配4M字节 */
  7. #define SDRAM_LCD_BUF1      EXT_SDRAM_ADDR
  8. /* LCD显存, 图层2, 分配4M字节 */
  9. #define SDRAM_LCD_BUF2    (EXT_SDRAM_ADDR + 4 * 1024 * 1024)
  10. /* 仅SDRAM驱动里面的测试代码调用了 */
  11. #define SDRAM_APP_SIZE    (8 * 1024 * 1024)
  12. /*
  13. **********************************************************************************************************
  14.                        emWin动态内存使用,除了显存使用的8MB,后8MB给动态内存使用
  15. **********************************************************************************************************
  16. */
  17. /* emWin动态内存首地址 */
  18. #define SDRAM_APP_BUF     (EXT_SDRAM_ADDR + 8 * 1024 * 1024)
复制代码
至此,SDRAM的驱动配置也讲解完毕。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:11:22 | 显示全部楼层
4.6  第3步:LTDC涉及到的引脚配置和时序配置



4.6.1  LTDC时序配置


    (提示:本小节要实现的操作,最简单的方法是复制教程配套例子整理好的驱动文件bsp_tft_429.c到自己的工程目录,然后调试修改文件中的函数LCD_ConfigLTDC即可,由于此文件关联了几个其它冗余文件,所以也是要添加的
    用户仅需配置LTDC涉及到的引脚和时序即可,LTDC其余的配置已经在文件LCDConf_Lin_Template.c全部封好了。将引脚配置预留出来供用户配置是因为硬件设计不同,比如可能使用RGB888接口,也可能是RGB565接口,所以用户仅需把需要的引脚初始化即可。将时序配置也预留出来是因为不同厂家的裸屏,驱动时序是不同的。
    引脚的配置还比较容易,硬件上用到哪些引脚了就把那些引脚配置下即可,关键是LTDC的时序配置。针对这个问题,专门发布了一个LTDC时序配置的帖子:http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528
帖子中是以开发板的7寸显示屏为例进行配置的,如果大家配置自己的显示屏,方法是一样的,一定要参照裸屏驱动手册中的时序参数进行配置。
    由于开发板配套了4.3寸,5寸和7寸屏显示屏,所以要对这几种尺寸的显示屏做自适应。每个屏的时序配置都是不一样的,具体实现在bsp_tft_429.c文件末尾,即函数LCD_ConfigLTDC。大家在给自己的显示屏移植时仅需提供这个LCD_ConfigLTDC函数即可,引脚配置需要在这个函数里面实现。另外,文件bsp_tft_429.c里面的其它函数都没有用到,其它的函数是供裸机(不含GUI)代码使用的,与emWin没有任何关系。下面我们再结合函数LCD_ConfigLTDC的实现,讲解下配置时要注意的一些问题,具体代码如下:
  1. //////////////////////////////////////////////////////////////////////////////////////////////////////////
  2. /*
  3. *********************************************************************************************************
  4. *                                     下面的函数被emWin所调用
  5. *********************************************************************************************************
  6. */
  7. /*
  8. *********************************************************************************************************
  9. *    函 数 名: LCD_ConfigLTDC
  10. *    功能说明: 配置LTDC
  11. *    形    参: 无
  12. *    返 回 值: 无
  13. *   笔    记:
  14. *       LCD_TFT 同步时序配置(整理自官方做的一个截图,言简意赅):
  15. *       ----------------------------------------------------------------------------
  16. *   
  17. *                                                 Total Width
  18. *                             <--------------------------------------------------->
  19. *                       Hsync width HBP             Active Width                HFP
  20. *                             <---><--><--------------------------------------><-->
  21. *                         ____    ____|_______________________________________|____
  22. *                             |___|   |                                       |    |
  23. *                                     |                                       |    |
  24. *                         __|         |                                       |    |
  25. *            /|\\    /|\\  |            |                                       |    |
  26. *             | VSYNC|   |            |                                       |    |
  27. *             |Width\\|/  |__          |                                       |    |
  28. *             |     /|\\     |         |                                       |    |
  29. *             |  VBP |      |         |                                       |    |
  30. *             |     \\|/_____|_________|_______________________________________|    |
  31. *             |     /|\\     |         | / / / / / / / / / / / / / / / / / / / |    |
  32. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  33. *    Total    |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  34. *    Heigh    |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  35. *             |Active|      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  36. *             |Heigh |      |         |/ / / / / / Active Display Area / / / /|    |
  37. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  38. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  39. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  40. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  41. *             |      |      |         |/ / / / / / / / / / / / / / / / / / / /|    |
  42. *             |     \\|/_____|_________|_______________________________________|    |
  43. *             |     /|\\     |                                                      |
  44. *             |  VFP |      |                                                      |
  45. *            \\|/    \\|/_____|______________________________________________________|
  46. *           
  47. *   
  48. *     每个LCD设备都有自己的同步时序值:
  49. *     Horizontal Synchronization (Hsync)
  50. *     Horizontal Back Porch (HBP)      
  51. *     Active Width                     
  52. *     Horizontal Front Porch (HFP)   
  53. *  
  54. *     Vertical Synchronization (Vsync)
  55. *     Vertical Back Porch (VBP)        
  56. *     Active Heigh                     
  57. *     Vertical Front Porch (VFP)        
  58. *   
  59. *     LCD_TFT 窗口水平和垂直的起始以及结束位置 :
  60. *     ----------------------------------------------------------------
  61. *  
  62. *     HorizontalStart = (Offset_X + Hsync + HBP);
  63. *     HorizontalStop  = (Offset_X + Hsync + HBP + Window_Width - 1);
  64. *     VarticalStart   = (Offset_Y + Vsync + VBP);
  65. *     VerticalStop    = (Offset_Y + Vsync + VBP + Window_Heigh - 1);
  66. *
  67. *********************************************************************************************************
  68. */
  69. __IO uint16_t Width, Height, HSYNC_W, VSYNC_W, HBP, HFP, VBP, VFP;
  70. void LCD_ConfigLTDC(void)
  71. {
  72.      LTDC_InitTypeDef       LTDC_InitStruct;
  73.      LTDC_Layer_TypeDef     LTDC_Layerx;
  74.      /* 使能LTDC */
  75.      RCC_APB2PeriphClockCmd(RCC_APB2Periph_LTDC, ENABLE); //--------------(1)
  76.      /* 使能DMA2D */
  77.      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2D, ENABLE); //--------------(2)
  78.      /* 配置LCD引脚 */
  79.      LCD429_AF_GPIOConfig();  //--------------(3)
  80.      /* 配置信号极性 */
  81.      LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL;   /* HSYNC 低电平有效 */  //--------------(4)
  82.      LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL;   /* VSYNC 低电平有效 */
  83.      LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL;   /* DE 低电平有效 */
  84.      LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IPC;
  85.      /* 背景色 */
  86.      LTDC_InitStruct.LTDC_BackgroundRedValue = 0;    //--------------(5)
  87.      LTDC_InitStruct.LTDC_BackgroundGreenValue = 0;
  88.      LTDC_InitStruct.LTDC_BackgroundBlueValue = 0;
  89.    
  90.      /*
  91.         LTDC时钟配置说明:
  92.           函数RCC_PLLSAIConfig的第一个参数是PLLSAI_N,第三个参数数PLLSAI_R。
  93.           函数RCC_LTDCCLKDivConfig的参数是RCC_PLLSAIDivR。
  94.       
  95.         下面举一个例子:PLLSAI_N = 400, PLLSAI_R = 4  RCC_PLLSAIDivR = 2:
  96.           首先,输入时钟 PLLSAI_VCO Input = HSE_VALUE / PLL_M = 8M / 8 = 1MHz
  97.             输出时钟 PLLSAI_VCO Output  = PLLSAI_VCO Input * PLLSAI_N = 1 * 400 = 400 1MHz
  98.             PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 400 / 4 = 100 1MHz
  99.           最好,LTDC 时钟 = PLLLCDCLK / RCC_PLLSAIDivR = 100 / 2 = 50 1MHz
  100.       */
  101.                   
  102.      /* 支持6种面板 */
  103.      switch (g_LcdType)   //--------------(6)
  104.      {
  105.          case LCD_35_480X320:   /* 3.5寸 480 * 320 */
  106.               RCC_PLLSAIConfig(429, 2,  4);
  107.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8);
  108.         
  109.               Width = 480;
  110.               Height = 272;
  111.               HSYNC_W = 10;
  112.               HBP = 20;
  113.               HFP = 20;
  114.               VSYNC_W = 20;
  115.               VBP = 20;
  116.               VFP = 20;
  117.               break;
  118.         
  119.          case LCD_43_480X272:/* 4.3寸 480 * 272  选择LTDC输出20MHz,所有颜色深度都可以选择这个时钟频率 */
  120.               RCC_PLLSAIConfig(280, 2,  7);
  121.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div2);
  122.               Width = 480;
  123.               Height = 272;
  124.               HSYNC_W = 40;
  125.               HBP = 2;
  126.               HFP = 2;
  127.               VSYNC_W = 9;
  128.               VBP = 2;
  129.               VFP = 2;
  130.               break;
  131.         
  132.          case LCD_50_480X272:        /* 5.0寸 480 * 272 */
  133.               RCC_PLLSAIConfig(429, 2,  4);
  134.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8);
  135.         
  136.               Width = 480;
  137.               Height = 272;
  138.         
  139.               HSYNC_W = 40;
  140.               HBP = 2;
  141.               HFP = 2;
  142.               VSYNC_W = 9;
  143.               VBP = 2;
  144.               VFP = 2;         
  145.               break;
  146.         
  147.          case LCD_50_800X480:/* 5.0寸 800 * 480,24位或者32位色选择LTDC输出15MHz,16位或者8位30MHz */
  148.               RCC_PLLSAIConfig(420, 2,  7);
  149.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4);
  150.               Width = 800;
  151.               Height = 480;
  152.               HSYNC_W = 96;
  153.               HBP = 10;
  154.               HFP = 10;
  155.               VSYNC_W = 2;
  156.               VBP = 10;
  157.               VFP = 10;         
  158.               break;
  159.         
  160.           //--------------(7)
  161.          case LCD_70_800X480:/* 7.0寸 800 * 480,24位或者32位色选择LTDC输出15MHz,16位或者8位30MHz*/
  162.               RCC_PLLSAIConfig(420, 2,  7);
  163.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div2);
  164.             
  165.               #if 0
  166.                    RCC_PLLSAIConfig(400, 2,  2);
  167.                    RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4);
  168.               #endif
  169.               Width = 800;
  170.               Height = 480;
  171.               HSYNC_W = 48;
  172.               HBP = 88;
  173.               HFP = 40;
  174.         
  175.               VSYNC_W = 3;
  176.               VBP = 32;
  177.               VFP = 13;     
  178.               break;
  179.         
  180.          case LCD_70_1024X600:       /* 7.0寸 1024 * 600 */
  181.               LTDC_InitStruct.LTDC_HSPolarity = LTDC_HSPolarity_AL;   /* HSYNC 低电平有效 */
  182.               LTDC_InitStruct.LTDC_VSPolarity = LTDC_VSPolarity_AL;   /* VSYNC 低电平有效 */
  183.               LTDC_InitStruct.LTDC_DEPolarity = LTDC_DEPolarity_AL;   /* DE 低电平有效 */
  184.               LTDC_InitStruct.LTDC_PCPolarity = LTDC_PCPolarity_IIPC;
  185.         
  186.               /* IPS 7寸 1024*600,  像素时钟频率范围 : 57 -- 65 --- 70.5MHz
  187.         
  188.                    PLLSAI_VCO Input   = HSE_VALUE / PLL_M = 8M / 4 = 2 Mhz
  189.                    PLLSAI_VCO Output  = PLLSAI_VCO Input * PLLSAI_N =   2 * 429 = 858 Mhz
  190.                    PLLLCDCLK = PLLSAI_VCO Output / PLLSAI_R = 858 / 4 = 214.5 Mhz
  191.                    LTDC clock frequency = PLLLCDCLK / RCC_PLLSAIDivR = 214.5 / 4 = 53.625 Mhz   
  192.                    (429, 2, 4); RCC_PLLSAIDivR_Div4 实测像素时钟 = 53.7M
  193.               */
  194.               RCC_PLLSAIConfig(429, 2, 6);
  195.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div4);
  196.         
  197.               Width = 1024;
  198.               Height = 600;
  199.               HSYNC_W = 2;  /* =10时,显示错位,20时部分屏可以的,80时全部OK */
  200.               HBP = 157;
  201.               HFP = 160;
  202.         
  203.               VSYNC_W = 2;
  204.               VBP = 20;
  205.               VFP = 12;         
  206.               break;
  207.         
  208.          default:
  209.               RCC_PLLSAIConfig(429, 2,  4);
  210.               RCC_LTDCCLKDivConfig(RCC_PLLSAIDivR_Div8);
  211.               Width = 800;
  212.               Height = 480;
  213.               HSYNC_W = 80; /* =10时,显示错位,20时部分屏可以的,80时全部OK */
  214.               HBP = 10;
  215.               HFP = 10;
  216.               VSYNC_W = 10;
  217.               VBP = 10;
  218.               VFP = 10;         
  219.               break;
  220.      }
  221.    
  222.      g_LcdWidth  = Width;        /* 显示屏分辨率-宽度 */  //--------------(8)
  223.      g_LcdHeight = Height;       /* 显示屏分辨率-高度 */
  224.    
  225.      /* 使能 PLLSAI */
  226.      RCC_PLLSAICmd(ENABLE);   //--------------(9)
  227.      /* 等待完成 */
  228.      while(RCC_GetFlagStatus(RCC_FLAG_PLLSAIRDY) == RESET);
  229.    
  230.      /* 配置LTDC的同步时序 */
  231.      LTDC_InitStruct.LTDC_HorizontalSync = HSYNC_W;
  232.      LTDC_InitStruct.LTDC_VerticalSync = VSYNC_W;
  233.      LTDC_InitStruct.LTDC_AccumulatedHBP = LTDC_InitStruct.LTDC_HorizontalSync + HBP;
  234.      LTDC_InitStruct.LTDC_AccumulatedVBP = LTDC_InitStruct.LTDC_VerticalSync + VBP;
  235.      LTDC_InitStruct.LTDC_AccumulatedActiveW = Width + LTDC_InitStruct.LTDC_AccumulatedHBP;
  236.      LTDC_InitStruct.LTDC_AccumulatedActiveH = Height + LTDC_InitStruct.LTDC_AccumulatedVBP;
  237.      LTDC_InitStruct.LTDC_TotalWidth = LTDC_InitStruct.LTDC_AccumulatedActiveW + HFP;
  238.      LTDC_InitStruct.LTDC_TotalHeigh = LTDC_InitStruct.LTDC_AccumulatedActiveH + VFP;
  239.      LTDC_Init(<DC_InitStruct); //--------------(10)
  240. }
复制代码

1.     LTDC时钟使能,此函数切不要忘记。
2.     DMA2D时钟使能,此函数切不要忘记。
3.     LTDC用到的引脚配置,此函数切不要忘记。LCD接口原理图如下,RGB888接口方式:
4.18.png


原理图中用到哪些引脚了,那些引脚就需要做初始化,具体初始化代码如下,初始化时别忘了初始化引脚对应的时钟:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: LCD429_AF_GPIOConfig
  4. *    功能说明: 配置GPIO用于 LTDC.
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. static void LCD429_AF_GPIOConfig(void)
  10. {
  11.      GPIO_InitTypeDef GPIO_InitStruct;
  12.      /* Enable GPIOI, GPIOJ, GPIOK AHB Clocks */
  13.      RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOI | RCC_AHB1Periph_GPIOJ | \\
  14.                   RCC_AHB1Periph_GPIOK,  ENABLE);
  15.      /* GPIOs Configuration */
  16.      /*
  17.      +------------------------+-----------------------+----------------------------+
  18.      +                       LCD pins assignment                                   +
  19.      +------------------------+-----------------------+----------------------------+
  20.      |  LCD429_TFT R0 <-> PI.15  |  LCD429_TFT G0 <-> PJ.07 |  LCD429_TFT B0 <-> PJ.12      |
  21.      |  LCD429_TFT R1 <-> PJ.00  |  LCD429_TFT G1 <-> PJ.08 |  LCD429_TFT B1 <-> PJ.13      |
  22.      |  LCD429_TFT R2 <-> PJ.01  |  LCD429_TFT G2 <-> PJ.09 |  LCD429_TFT B2 <-> PJ.14      |
  23.      |  LCD429_TFT R3 <-> PJ.02  |  LCD429_TFT G3 <-> PJ.10 |  LCD429_TFT B3 <-> PJ.15      |
  24.      |  LCD429_TFT R4 <-> PJ.03  |  LCD429_TFT G4 <-> PJ.11 |  LCD429_TFT B4 <-> PK.03      |
  25.      |  LCD429_TFT R5 <-> PJ.04  |  LCD429_TFT G5 <-> PK.00 |  LCD429_TFT B5 <-> PK.04      |
  26.      |  LCD429_TFT R6 <-> PJ.05  |  LCD429_TFT G6 <-> PK.01 |  LCD429_TFT B6 <-> PK.05      |
  27.      |  LCD429_TFT R7 <-> PJ.06  |  LCD429_TFT G7 <-> PK.02 |  LCD429_TFT B7 <-> PK.06      |
  28.      -------------------------------------------------------------------------------
  29.      |  LCD429_TFT HSYNC <-> PI.12  | LCDTFT VSYNC <->  PI.13 |
  30.      |  LCD429_TFT CLK   <-> PI.14  | LCD429_TFT DE   <->  PK.07 |
  31.      -----------------------------------------------------
  32.      */
  33.      /* GPIOI configuration */
  34.      GPIO_PinAFConfig(GPIOI, GPIO_PinSource12, GPIO_AF_LTDC);
  35.      GPIO_PinAFConfig(GPIOI, GPIO_PinSource13, GPIO_AF_LTDC);
  36.      GPIO_PinAFConfig(GPIOI, GPIO_PinSource14, GPIO_AF_LTDC);
  37.      GPIO_PinAFConfig(GPIOI, GPIO_PinSource15, GPIO_AF_LTDC);
  38.      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  39.      //GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  40.      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz;
  41.      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  42.      GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  43.      GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  44.      GPIO_Init(GPIOI, &GPIO_InitStruct);
  45.    
  46.      /* GPIOJ configuration */
  47.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource0, GPIO_AF_LTDC);
  48.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource1, GPIO_AF_LTDC);
  49.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource2, GPIO_AF_LTDC);
  50.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource3, GPIO_AF_LTDC);
  51.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource4, GPIO_AF_LTDC);
  52.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource5, GPIO_AF_LTDC);
  53.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource6, GPIO_AF_LTDC);
  54.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource7, GPIO_AF_LTDC);
  55.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource8, GPIO_AF_LTDC);
  56.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource9, GPIO_AF_LTDC);
  57.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource10, GPIO_AF_LTDC);
  58.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource11, GPIO_AF_LTDC);
  59.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource12, GPIO_AF_LTDC);
  60.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource13, GPIO_AF_LTDC);
  61.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource14, GPIO_AF_LTDC);
  62.      GPIO_PinAFConfig(GPIOJ, GPIO_PinSource15, GPIO_AF_LTDC);
  63.      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | \\
  64.                       GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7 | \\
  65.                       GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | \\
  66.                       GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
  67.      GPIO_Init(GPIOJ, &GPIO_InitStruct);
  68.      /* GPIOI configuration */
  69.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource0, GPIO_AF_LTDC);
  70.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource1, GPIO_AF_LTDC);
  71.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource2, GPIO_AF_LTDC);
  72.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource3, GPIO_AF_LTDC);
  73.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource4, GPIO_AF_LTDC);
  74.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource5, GPIO_AF_LTDC);
  75.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource6, GPIO_AF_LTDC);
  76.      GPIO_PinAFConfig(GPIOK, GPIO_PinSource7, GPIO_AF_LTDC);
  77.      GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | \\
  78.                       GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
  79.      GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
  80.      GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;
  81.      GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
  82.      GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL;
  83.      GPIO_Init(GPIOK, &GPIO_InitStruct);
  84. }
复制代码
4.     STM32F429/439的图层是由背景层,图层1和图层2组成,这里配置的是背景层的颜色值,分别配置了R,G,B三原色的数值,范围都是0-255。
5.    信号极性配置,在上面提到的帖子中已经讲解(LTDC时序配置的帖子:http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528),这里不再赘述。
6.    六种显示面板的LTDC输出时钟和时序参数配置,六种面板的识别是在bsp_touch.c文件中实现的。大家自己配置时用不到这个,仅需提供一组时序参数和输出时钟即可,除非项目中需要切换不同显示屏。
7.    这里是7寸面板的LTDC时钟输出配置和时序参数配置,配置方法在上面提到的帖子中已经讲解(LTDC时序配置的帖子:http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528),这里不再赘述,其它面板的设置方法是一样的。时序参数的几个变量Width, Height, HSYNC_W,VSYNC_W, HBP, HFP, VBP, VFP被定义成了全局变量,因为文件LCDConf_Lin_Template.c还要用到。
8.    全局变量g_LcdWidth和g_LcdHeight在文件bsp_tft_lcd.c文件定义。如果大家自己移植时用不到文件bsp_tft_lcd.c的话,需要自行定义这两个全局变量(另外,此文件里面的背光设置函数也要自行实现),因为这两个变量要被文件LCDConf_Lin_Template.c所调用。
9.    等待LTDC输出时钟配置完成。
10. 通过函数LTDC_Init完成时序配置。


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


下面说一个最重要的问题,配置好时序了,怎么检查自己的配置是否成功了?用户仅需在这个函数的末尾加上如下三行代码:
  1. /* 开背光,这个函数是在bsp_tft_lcd.c文件实现,如果大家移植自己的板子,写个类似的函数,开启背光即可 */
  2. LCD_SetBackLight(255);
  3. /* 开启LTDC */
  4. LTDC_Cmd(ENABLE);
  5. /* 将程序停止在这里 */
  6. while(1);
复制代码
加上这三行代码后,再将背景层设置为一个合适的颜色,建议设置成红色,方便观察:
  1. /* 背景色 */
  2.      LTDC_InitStruct.LTDC_BackgroundRedValue = 0xff;
  3.      LTDC_InitStruct.LTDC_BackgroundGreenValue = 0;
  4.      LTDC_InitStruct.LTDC_BackgroundBlueValue = 0;
复制代码

如果背景层可以正常显示红色,说明引脚和时序配置都是没有问题的。如果不成功要从以下几个方面着手检查:
1、首先要清楚一点,当前的设置与是否使用了SDRAM没有任何关系,因为背景层是不需要SDRAM的,图层1和图层2才需要SDRAM做显存使用。
2、从硬件着手检查,保证F429/439芯片焊接没问题,TFT接口一定要牢固,防止接触不良,特别是使用FPC软排线的时候,测试阶段,软排线越短越好。有时候也可能是显示屏有问题,最好可以备两个显示屏测试。
3、从软件配置着手检查,查看LTDC涉及到的所有引脚是否配置,引脚时钟是否使能,有时候无法显示也有可能是板子硬件设计不规范导致干扰较大造成的,此时,可以降低LTDC涉及到的引脚速度。
4、如果显示了,但是显示的位置不正确,可以重新调整时序参数即可。
    这部分知识就为大家讲解这么多,下面讲解工程中需要添加的文件。

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

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


下面把这些新添加的文件做一个简单的介绍:
1、Fonts分组中的文件
    所有这些文件,emWin都用不上,只是因为被文件bsp_tft_lcd.c文件关联了。
        asc12.c---12点阵ASCII字符字库
        asc16.c---16点阵ASCII字符字库
        hz12.c--- 12点阵宋体小字库
        hz16.c--- 16点阵宋体小字库
        hz24.c--- 24点阵宋体小字库
        hz32.c--- 32点阵宋体小字库
        ra8875_asc_width.c-- RA8875 ASCII字体的宽度表
2、LCD_Driver分组中的文件
    这个分组里面的文件,emWin都要用到。
        param.c--- 触摸校准时参数保存和参数加载功能的实现。
        bsp_tft_lcd.c--- TFT驱动和相关API函数汇总文件,比如用户的RA8875显示屏,ili9488显示屏,F429所带TFT控制器驱动
                                   显示屏都可以有一个单独的文件,然后将这些显示屏相同功能的函数汇总成一个函数。这个文件就起到
                                   这个作用。emWin仅用到这个文件里面的全局变量g_LcdHeightg_LcdWidth以及背光函数
                                   LCD_SetBackLight其它所有函数都没有用到。
        bsp_touch.c --- 触摸芯片自适应驱动,根据用户使用的触摸IC选择不同的驱动。另外,电阻屏的触摸扫描,触摸校准和触摸滤波也是在这个文件里面实现。
        bsp_gt811.c --- 电容触摸芯片GT811的驱动以及触摸扫描。
        bsp_ft5x06.c --- 电容触摸芯片FT5X06的驱动以及触摸扫描。
       TOUCH_STMPE811.c --- 电阻触摸芯片STMPE811的驱动。
3、bsp分组中的文件
    这个分组里面的三个文件,emWin都要间接用到。
        bsp_tim_pwm.c--- 定时器驱动,显示屏的背光要用到PWM。
       bsp_i2c_gpio.c--- I2C接口驱动,EEPROM,GT811,STMPE811和FT5X06都要用到,因为他们的接口都是I2C方式。
       bsp_eeprom_24xx.c ---EEPROM驱动,用于存储电阻屏触摸校准参数。
4、StdPeriph_Driver分组中的文件
    这个分组里面的两个文件,emWin都要间接用到。
        stm32f4xx_tim.c--- 显示屏的背光是用PWM驱动的,需要用到这个定时器库文件。
        stm32f4xx_ltdc.c ---LTDC相关的API函数需要用到这个库文件。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:20:58 | 显示全部楼层
4.7  第4步:电阻屏和电容屏触摸驱动的实现


    (提示:本小节要实现的操作,最简单的方法是复制教程配套例子整理好的触摸驱动文件,然后在驱动文件的基础上做修改

4.7.1  开发板电阻触摸和电容触摸的移植方法


    教程配套开发板的显示屏既有电阻屏也有电容屏,电阻屏触摸板使用的芯片是STMPE811,电容屏触摸板使用的芯片有两种,一个是FT5X06,另一个是GT811。由于开发板已经提供了这三款芯片的驱动,将其直接添加到工程中即可(这三款芯片都是I2C通信接口)。另外,特别注意一点,仅电阻屏需要触摸校准,电容屏无需校准。
4.20.png

上面红色方框中的6个文件是触摸相关的:
bsp_i2c_gpio.c --- I2C接口驱动,上面说到的三款触摸芯片都是I2C通信接口。
param.c --- 触摸校准时参数保存和参数加载功能的实现。
bsp_touch.c --- 触摸芯片自适应驱动,根据用户使用的触摸IC选择不同的驱动。另外,电阻屏的触
                              摸扫描,触摸校准也是在这个文件里面实现。
bsp_gt811.c --- 电容触摸芯片GT811的驱动以及触摸扫描的实现。
bsp_ft5x06.c --- 电容触摸芯片FT5X06的驱动以及触摸扫描的实现。
TOUCH_STMPE811.c --- 电阻触摸芯片STMPE811的驱动。
    下面我们对这三款触摸IC做个介绍。

4.7.2  电容屏触摸IC---FT5X06


    电容触摸IC都是支持多点触摸的,FT5X06支持多达10点触摸同时按下,并提供了I2C和SPI两种通信接口方式,开发板使用的是I2C通信接口。更多相关知识学习可以在这里下载FT5X06数据手册和应用手册:http://www.armbbs.cn/forum.php?mod=viewthread&tid=16461
    基于这个触摸芯片的触摸板最简单,我们开发板使用的是触摸芯片和触摸板一体的,使用时不需要配置,直接读取触摸数值即可(注意,这个芯片返回的就是实际的坐标值,比如显示屏的分辨率是800*480,那么返回的就是在这个分辨率范围内的实际坐标),读出数值后怎么跟emWin关联呢?这个是移植的关键,我们这里是通过函数GUI_PID_StoreStateFT5X06读出的实际坐标值存储到指针输入设备的FIFO中即可,默认情况下FIFO可以存储5组。具体实现代码如下(注意,我们这里并没有使用emWin支持的多点触摸功能,仅发一个触摸值给emWin,一般情况下已经够用):
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: FT5X06_OnePiontScan
  4. *    功能说明: FT5X06_OnePiontScan函数仅读取了电容触摸屏的一次触摸,在bsp_ts_ft5x06.h文件里面设置参数
  5. *             #define FT5X06_TOUCH_POINTS   1
  6. *             专门用于emWin。
  7. *    形    参: 无
  8. *    返 回 值: 无
  9. *********************************************************************************************************
  10. */
  11. extern GUI_PID_STATE State;
  12. void FT5X06_OnePiontScan(void)
  13. {
  14.      uint8_t buf[CFG_POINT_READ_BUF];
  15.      uint8_t i;
  16.      static uint8_t s_tp_down = 0;
  17.      uint16_t x, y;
  18.      if (g_tFT5X06.Enable == 0)
  19.      {
  20.          return;
  21.      }
  22.    
  23.      if (FT5X06_PenInt() == 0)
  24.      {
  25.          return;
  26.      }
  27.    
  28.      FT5X06_ReadReg(2, buf, 1);    //--------------(1)
  29.     /* 判断是否有触摸数据 */   
  30.      if ((buf[0] & 0x07) == 0)
  31.      {
  32.          if (s_tp_down == 1)
  33.          {
  34.               /* State.x和State.y的数值无需更新,State是全局变量,保存的就是最近一次的数值 */
  35.               s_tp_down = 0;
  36.               State.Pressed = 0;
  37.               GUI_PID_StoreState(&State); /* 释放 */
  38.          }
  39.          return;
  40.      }
  41.    
  42.      /* 有触摸,读取完整的数据,这里读取了一次 */
  43.      FT5X06_ReadReg(0, buf, CFG_POINT_READ_BUF);
  44.      for (i = 0; i < FT5X06_TOUCH_POINTS; i++)
  45.      {
  46.          uint8_t pointid;
  47.         
  48.          pointid = (buf[5 + 6*i]) >> 4;
  49.          if (pointid >= 0x0f)
  50.          {
  51.               break;
  52.          }
  53.          else
  54.          {
  55.         g_tFT5X06.X[i] = (int16_t)(buf[3 + 6*i] & 0x0F)<<8 | (int16_t)buf[4 + 6*i];
  56.         g_tFT5X06.Y[i] = (int16_t)(buf[5 + 6*i] & 0x0F)<<8 | (int16_t)buf[6 + 6*i];
  57.         g_tFT5X06.Event[i] = buf[0x3 + 6*i] >> 6;
  58.         g_tFT5X06.id[i] = (buf[5 + 6*i])>>4;
  59.     }
  60.     }
  61.      /* 检测按下 */
  62.      {
  63.          if ((g_tFT5X06.ChipID == 0x55)||(g_tFT5X06.ChipID == 0xa3))       /* 4.3寸 480 * 272 */
  64.          {
  65.               x = g_tFT5X06.Y[0];
  66.               y = g_tFT5X06.X[0];   
  67.             
  68.               /* 判断值域 */
  69.               if (x > 479)
  70.               {
  71.                    x = 479;
  72.               }
  73.             
  74.               if (y > 271)
  75.               {
  76.                    y = 271;
  77.               }            
  78.          }
  79.          else if (g_tFT5X06.ChipID == 0x0A)   /* 5.0寸 800 * 480 */
  80.          {
  81.               x = g_tFT5X06.X[0];
  82.               y = g_tFT5X06.Y[0];   
  83.             
  84.               /* 判断值域 */
  85.               if (x > 799)
  86.               {
  87.                    x = 799;
  88.               }            
  89.               if (y > 479)
  90.               {
  91.                    y = 479;
  92.               }            
  93.          }
  94.          else /* id == 0x06 表示7寸电容屏(FT芯片) */
  95.          {
  96.               x = g_tFT5X06.X[0];
  97.               y = g_tFT5X06.Y[0];   
  98.             
  99.               /* 判断值域 */
  100.               if (x > 799)
  101.               {
  102.                    x = 799;
  103.               }            
  104.               if (y > 479)
  105.               {
  106.                    y = 479;
  107.               }            
  108.          }
  109.      }
  110.    
  111.      if (s_tp_down == 0)  //--------------(2)
  112.      {
  113.          s_tp_down = 1;
  114.          State.x = x;
  115.          State.y = y;
  116.          State.Pressed = 1;
  117.          GUI_PID_StoreState(&State);
  118.      }
  119.      else                 //--------------(3)
  120.      {
  121.          State.x = x;
  122.          State.y = y;
  123.          State.Pressed = 1;
  124.          GUI_PID_StoreState(&State);
  125.      }
  126. #if 0
  127.      for (i = 0; i < CFG_POINT_READ_BUF; i++)
  128.      {
  129.          printf("%02X ", buf[i]);
  130.      }
  131.      printf("\\r\\n");
  132. #endif
  133. #if 0  /* 打印5个坐标点数据 */      //--------------(4)
  134.      printf("(%5d,%5d,%3d,%3d) ",  g_tFT5X06.X[0], g_tFT5X06.Y[0], g_tFT5X06.Event[0],  g_tFT5X06.id[0]);
  135.      printf("(%5d,%5d,%3d,%3d) ",  g_tFT5X06.X[1], g_tFT5X06.Y[1], g_tFT5X06.Event[1],  g_tFT5X06.id[1]);
  136.      printf("(%5d,%5d,%3d,%3d) ",  g_tFT5X06.X[2], g_tFT5X06.Y[2], g_tFT5X06.Event[2],  g_tFT5X06.id[2]);
  137.      printf("(%5d,%5d,%3d,%3d) ",  g_tFT5X06.X[3], g_tFT5X06.Y[3], g_tFT5X06.Event[3],  g_tFT5X06.id[3]);
  138.      printf("(%5d,%5d,%3d,%3d) ",  g_tFT5X06.X[4], g_tFT5X06.Y[4], g_tFT5X06.Event[4],  g_tFT5X06.id[4]);
  139.      printf("\\r\\n");
  140. #endif  
  141. }
复制代码
1.     从寄存器2读取一个数据,判断是否有触摸数据,如果没有触摸数据,而且变量标志s_tp_down = 1(此变量等于1表示之前处于触摸按下或者移动状态,如果等于0表示之前处于未被触摸状态),那么此时要给emWin发送触摸释放消息,这一步比较重要,切不可省略。如果变量标志s_tp_down= 0,直接退出即可。
2.     如果变量s_tp_down = 0表示之前处于未被触摸状态,这里设置此变量为1,并给emWin发送触摸按下消息和触摸坐标。
3.     如果变量不等于0(其实这里也就是1)表示之前处于按下状态,此时触摸处于移动状态,这里不用重复设置此变量了,但要给emWin发送触摸按下消息和触摸坐标(由于emWin没有移动状态标识,这里继续发送按下状态就可以)。
4.     这里预留的条件编译主要是方便调试阶段使用。

4.7.3  电容屏触摸IC---GT811


    电容触摸IC都是支持多点触摸的,GT811支持多达5点触摸同时按下,并提供了I2C通信接口方式,更多相关知识学习可以在这里下载GT811数据手册:http://www.armbbs.cn/forum.php?mod=viewthread&tid=18581  
    基于这个触摸芯片的触摸板配置要麻烦些,因为要配置的寄存器非常多。具体配置就不在教程里面讲解了,大家如果有兴趣可以结合GT811数据手册研究我们提供的驱动配置。配置好GT811后,使用上基本跟FT5X06是一样的(注意,芯片GT811返回的就是实际的坐标值,比如显示屏的分辨率是800*480,那么返回的就是在这个分辨率范围内的实际坐标,跟FT5X06也是一样的)。跟emWin的关联方法也是通过函数GUI_PID_StoreState将实际坐标值存储到指针输入设备的FIFO中(注意,我们这里并没有使用emWin支持的多点触摸功能,仅发一个触摸坐标给emWin,一般情况下已经够用):
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: GT811_OnePiontScan
  4. *    功能说明: 读取GT811触摸数据,这里仅读取一个触摸点。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. extern GUI_PID_STATE State;
  10. void GT811_OnePiontScan(void)
  11. {
  12.      uint8_t buf[6];
  13.      static uint8_t s_tp_down = 0;
  14.      uint16_t x, y;
  15.    
  16.      /* 读取寄存器:0x721  R  TouchpointFlag  Sensor_ID  key  tp4  tp3  tp2  tp1  tp0 */
  17.      GT811_ReadReg(GT811_READ_XY_REG, buf, 1);   //--------------(1)
  18.    
  19.      /* 判断是否按下,没有按下,直接退出 */  
  20.      if ((buf[0] & 0x01) == 0)
  21.      {
  22.          if (s_tp_down == 1)
  23.          {
  24.               /* State.x和State.y的数值无需更新,State是全局变量,保存的就是最近一次的数值 */
  25.               s_tp_down = 0;
  26.               State.Pressed = 0;
  27.               GUI_PID_StoreState(&State);
  28.          }
  29.          return;
  30.      }
  31.    
  32.      /* 读取第一个触摸点0 */
  33.      GT811_ReadReg(GT811_READ_XY_REG + 1, &buf[1], 5);
  34.    
  35.      /*
  36.      0x721  R  TouchpointFlag  Sensor_ID  key  tp4  tp3  tp2  tp1  tp0
  37.      0x722  R  Touchkeystate     0  0  0  0  key4  key3  key2  key1
  38.      0x723  R  Point0Xh  触摸点 0,X 坐标高 8 位
  39.      0x724  R  Point0Xl  触摸点 0,X 坐标低 8 位
  40.      0x725  R  Point0Yh  触摸点 0,Y 坐标高 8 位
  41.      0x726  R  Point0Yl  触摸点 0,Y 坐标低 8 位
  42.      0x727  R  Point0Pressure  触摸点 0,触摸压力
  43.      */
  44.      g_GT811.TouchpointFlag = buf[0];
  45.      g_GT811.Touchkeystate = buf[1];
  46.    
  47.      g_GT811.X0 = ((uint16_t)buf[2] << 8) + buf[3];
  48.      g_GT811.Y0 = ((uint16_t)buf[4] << 8) + buf[5];
  49.    
  50.      /* 检测按下 */
  51.      /* 坐标转换 :
  52.          电容触摸板左下角是 (0,0);  右上角是 (479,799)
  53.          需要转到LCD的像素坐标 (左上角是 (0,0), 右下角是 (799,479)
  54.      */
  55.      x = g_GT811.Y0;
  56.      y = 479 - g_GT811.X0;
  57.    
  58.      if (s_tp_down == 0)  //--------------(2)
  59.      {
  60.          s_tp_down = 1;
  61.          State.x = x;
  62.          State.y = y;
  63.          State.Pressed = 1;
  64.          GUI_PID_StoreState(&State);
  65.      }
  66.      Else               //--------------(3)
  67.      {
  68.          State.x = x;
  69.          State.y = y;
  70.          State.Pressed = 1;
  71.          GUI_PID_StoreState(&State);
  72.      }
  73. #if 0                 //--------------(4)
  74.      printf("%5d,%5d,%3d\\r\\n",  g_GT811.X0, g_GT811.Y0, g_GT811.P0);
  75. #endif  
  76. }
复制代码
1.     从寄存器GT811_READ_XY_REG中读取一个数据,判断是否有触摸数据,如果没有触摸数据,而且变量标志s_tp_down = 1(此变量等于1表示之前处于触摸按下或者移动状态,如果等于0表示之前处于未被触摸状态),那么此时要给emWin发送触摸释放消息,这一步比较重要,切不可省略。如果变量标志s_tp_down= 0,直接退出即可。
2.     如果变量s_tp_down = 0表示之前处于未被触摸状态,这里设置此变量为1,并给emWin发送触摸按下消息和触摸坐标。
3.     如果变量不等于0(其实这里也就是1)表示之前处于按下状态,此时触摸处于移动状态,这里不用重复设置此变量了,但要给emWin发送触摸按下消息和触摸坐标(由于emWin没有移动状态标识,这里继续发送按下状态就可以)。
4.     这里预留的条件编译主要是方便调试阶段使用。

4.7.4  电阻屏触摸IC---STMPE811


    电阻触摸要比电容触摸麻烦很多,因为电阻触摸要做校准,还要做滤波,否则采集回来的触摸值会抖动或者出现飞点,出现这种情况的主要原因是电阻触摸板的线性度不够好。开发板电阻屏使用的触摸芯片是STMPE811,这个芯片其实就是12位分辨率的ADC,用来采集电阻触摸板的X轴ADC值和Y轴ADC值,然后按照一定的线性关系将ADC值转换为实际的坐标值。其中这个线性关系是通过触摸校准建立起来的,每次采集的X轴和Y轴ADC就可以代入这个线性关系,从而获得实际的坐标值。
    总的来说,STMPE811的驱动不难实现,可以结合STMPE811的数据手册http://www.armbbs.cn/forum.php?mod=viewthread&tid=23306 研究开发板提供的驱动配置。配置好后仅需要提供读取的X轴,Y轴的ADC值以及触摸按下状态(判断STMPE811的中断输出引脚就可以了,如果有触摸,这个引脚输出低电平,反之,输出高电平。通过判断这个引脚就可以选择是否读取X轴,Y轴的ADC值,避免不必要的操作)。这些函数是独立在TOUCH_STMPE811.c文件实现的。而触摸值滤波,触摸扫描和触摸校准是在bsp_touch.c文件里面实现的。
     下面是判断触摸按下状态,清除触摸中断标志和X轴,Y轴的ADC值读取的API函数,这些函数被bsp_touch.c文件所调用。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: STMPE811_PenInt
  4. *    功能说明: 判断触摸按下
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. uint8_t STMPE811_PenInt(void)  //--------------(1)
  10. {
  11.      if ((PORT_TP_INT->IDR & PIN_TP_INT) == 0)
  12.      {
  13.          return 1;
  14.      }
  15.      return 0;
  16. }
  17. /*
  18. *********************************************************************************************************
  19. *    函 数 名: STMPE811_ClearInt
  20. *    功能说明: 清楚触笔中断
  21. *    形    参: 无
  22. *    返 回 值: 无
  23. *********************************************************************************************************
  24. */
  25. void STMPE811_ClearInt(void)  //--------------(2)
  26. {
  27.      STMPE811_WriteReg1(REG811_INT_STA, 0xFF);
  28. }
  29. /*
  30. *********************************************************************************************************
  31. *    函 数 名: STMPE811_ReadX
  32. *    功能说明: 读取X坐标adc
  33. *    形    参: 无
  34. *    返 回 值: X坐标值adc
  35. *********************************************************************************************************
  36. */
  37. uint16_t STMPE811_ReadX(void)  //--------------(3)
  38. {
  39.      /* 按照 XY 读取模式,连续读取3字节数据,然后分解出X,Y
  40.       |  byte0   |     byte1      |   byte2  |
  41.       | X[11:4], | X[3:0],Y[11:8] | Y[7:0]   |
  42.      */
  43.      uint8_t buf[3];
  44.    
  45. #if 0
  46.      STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
  47.    
  48.      s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
  49.      s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
  50. #else
  51.      if (STMPE811_ReadReg1(REG811_TSC_CTRL) & 0x80)
  52.      {   
  53.          STMPE811_ReadBytes(buf, REG811_TSC_DATA1, 3);
  54.         
  55.          s_AdcX = ((uint16_t)buf[0] << 4) | (buf[1] >> 4);
  56.          s_AdcY = ((uint16_t)(buf[1] & 0xF) << 8) | buf[2];
  57.         
  58.          #if 0
  59.          /* for debug */
  60.          {
  61.               static int32_t s_t1 = 0;
  62.               int32_t tt;
  63.                            
  64.               tt = bsp_GetRunTime();
  65.               if (tt - s_t1 > 1000)
  66.               {
  67.                    printf("\\r\\n");
  68.                    s_t1 = tt;
  69.               }
  70.               printf("(%7d) %5d %5d\\r\\n", tt, s_AdcX, s_AdcY);
  71.          }
  72.          #endif
  73.      }
  74.      else
  75.      {
  76.          s_AdcX = 0;
  77.          s_AdcY = 0;
  78.      }
  79. #endif
  80.    
  81.      return s_AdcX;
  82. }
  83. /*
  84. *********************************************************************************************************
  85. *    函 数 名: STMPE811_ReadX
  86. *    功能说明: 读取Y坐标adc
  87. *    形    参: 无
  88. *    返 回 值: Y坐标值adc
  89. *********************************************************************************************************
  90. */
  91. uint16_t STMPE811_ReadY(void)  //--------------(4)
  92. {
  93.      return  s_AdcY;
  94. }
复制代码
1.    通过判断STMPE811的中断输出引脚的高低电平来判断触摸板是否被按下,如果有触摸,这个引脚输出低电平,反之,输出高电平。通过判断这个引脚就可以选择是否读取X轴,Y轴的ADC值,避免不必要的操作。
2.    清除触摸中断标志,检测到触摸屏未被按下时,要做清除。
3.    读取X轴ADC数值。
4.    读取Y轴ADC数值。
    接下来再来看bsp_touch.c文件中STMPE811触摸扫描函数的实现。电阻触摸值与emWin的关联是在触摸扫描中实现的,具体方法也是调用函数GUI_PID_StoreState将触摸坐标信息存储到指针输入设备的FIFO中:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TOUCH_Scan
  4. *    功能说明: 触摸板事件检测程序。该函数被周期性调用,每ms调用1次. 见 bsp_Timer.c
  5. *    形    参:  无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void TOUCH_Scan(void)  //--------------(1)
  10. {
  11.      uint16_t usAdcX;
  12.      uint16_t usAdcY;
  13.      static uint16_t s_usXBuf[SAMPLE_COUNT];
  14.      static uint16_t s_usYBuf[SAMPLE_COUNT];
  15.      static uint8_t s_ucPos = 0;
  16.      static uint8_t s_count = 0;
  17.      static uint8_t s_down = 0;
  18.      static uint16_t s_usSaveAdcX, s_usSaveAdcY; /* 用于触笔抬起事件,保存按下和移动的最后采样值 */
  19.      static uint8_t s_ms = 0;
  20.      if (g_GT811.Enable == 1)
  21.      {
  22.          GT811_Timer1ms();  /* 电容触摸屏程序计数器 */
  23.          return;
  24.      }
  25.    
  26.      if (g_tFT5X06.Enable == 1)
  27.      {
  28.          FT5X06_Timer1ms(); /* 电容触摸屏程序计数器 */
  29.          return;
  30.      }
  31.    
  32.      if (g_tTP.Enable == 0)
  33.      {
  34.          return;
  35.      }
  36.    
  37.      if (++s_ms >= 2)           //--------------(2)
  38.      {
  39.          return;
  40.      }
  41.    
  42.      /* 2ms进入一次 */
  43.      s_ms = 0;
  44.    
  45.      /* 触笔中断发生 */
  46.      if (STMPE811_PenInt())     //--------------(3)
  47.      {
  48.          /* 获得原始的ADC值,未滤波 */
  49.          usAdcX = STMPE811_ReadX();  //--------------(4)
  50.          usAdcY = STMPE811_ReadY();
  51.          if (TOUCH_PressValid(usAdcX, usAdcY))   //--------------(5)
  52.          {
  53.               /* 按压30ms之后才开始采集数据 */
  54.               if (s_count >= DOWN_VALID / 2)     //--------------(6)
  55.               {
  56.                    s_usXBuf[s_ucPos] = usAdcX;
  57.                    s_usYBuf[s_ucPos] = usAdcY;
  58.                    /* 采集20ms数据进行滤波 */
  59.                    if (++s_ucPos >= SAMPLE_COUNT / 2) //--------------(7)
  60.                    {
  61.                        s_ucPos = 0;
  62.                         /* 对ADC采样值进行软件滤波 */
  63.                        g_tTP.usAdcNowX = TOUCH_DataFilter(s_usXBuf, SAMPLE_COUNT / 2); //--------(8)
  64.                        g_tTP.usAdcNowY = TOUCH_DataFilter(s_usYBuf, SAMPLE_COUNT / 2);
  65.                        if (s_down == 0)   //--------------(9)
  66.                        {
  67.                             s_down = 1;
  68.                            
  69.                             /* 触摸按下事件 */
  70.                             State.x = TOUCH_TransX( g_tTP.usAdcNowX, g_tTP.usAdcNowY);
  71.                             State.y = TOUCH_TransY( g_tTP.usAdcNowX, g_tTP.usAdcNowY);
  72.                             State.Pressed = 1;
  73.                             GUI_PID_StoreState(&State);
  74.                             /* 用于触笔抬起事件,保存按下和移动的最后采样值 */
  75.                             s_usSaveAdcX = g_tTP.usAdcNowX;
  76.                             s_usSaveAdcY = g_tTP.usAdcNowY;
  77.                        }
  78.                        else                //--------------(10)
  79.                        {
  80.                             if (TOUCH_MoveValid(s_usSaveAdcX, s_usSaveAdcY, g_tTP.usAdcNowX,
  81. g_tTP.usAdcNowY))
  82.                             {
  83.                                  /* 触摸移动事件 */
  84.                                  State.x = TOUCH_TransX( g_tTP.usAdcNowX, g_tTP.usAdcNowY);
  85.                                  State.y = TOUCH_TransY( g_tTP.usAdcNowX, g_tTP.usAdcNowY);
  86.                                  State.Pressed = 1;
  87.                                  GUI_PID_StoreState(&State);
  88.                                  /* 用于触笔抬起事件,保存按下和移动的最后采样值 */
  89.                                  s_usSaveAdcX = g_tTP.usAdcNowX;
  90.                                  s_usSaveAdcY = g_tTP.usAdcNowY;
  91.                             }
  92.                             else
  93.                             {
  94.                                  g_tTP.usAdcNowX = 0; /* for debug stop */
  95.                             }
  96.                        }
  97.                    }
  98.               }
  99.               else
  100.               {
  101.                    s_count++;
  102.               }
  103.          }
  104.          else   //--------------(11)
  105.          {
  106.               if (s_count > 0)
  107.               {
  108.                    if (--s_count == 0)
  109.                    {
  110.                        /* 触摸释放事件 */
  111.                        /* State.x和State.y的数值无需更新,State是全局变量,保存的就是最近一次的数值 */
  112.                        State.Pressed = 0;
  113.                        GUI_PID_StoreState(&State);
  114.                        s_count = 0;
  115.                        s_down = 0;
  116.                        g_tTP.usAdcNowX = 0;
  117.                        g_tTP.usAdcNowY = 0;
  118.                        STMPE811_ClearInt();        /* 清触笔中断标志 */
  119.                    }
  120.               }
  121.               s_ucPos = 0;
  122.          }
  123.      }
  124. }
复制代码
1.    此函数要每1ms被调用一次。
2.    设置每2ms进行一次STMPE811检测,也许不理解的初学者会问直接设置此函数每2ms调用一次不就好了?不可以的,因为这段代码前面还有GT811和FT5X06的程序计数器要执行,这两个是要每1ms执行一次的。但事实上,emWin并没有用到这两个程序计数器,这里仅仅是为了保持跟裸机工程(不含emWin的工程)的触摸检测一致。
3.    这个就是前面一直说的利用STMPE811的中断输出引脚的高低电平来判断触摸板是否被按下。
4.    读取X轴ADC数值和Y轴ADC数值。
5.    通过函数TOUCH_PressValid检测刚刚读取的X轴,Y轴数值是否在有效的范围内。
    函数TOUCH_PressValid的具体实现如下,其中全局变量g_tTP.usMaxAdc = 4095,因为电阻触摸芯片STMPE811是12位ADC,最大触摸值就是2^12 –1 = 4095。
  1. /* 有效ADC值的判断门限. 太接近ADC临界值的坐标认为无效 */
  2. #define ADC_VALID_OFFSET    2
  3. /*
  4. *********************************************************************************************************
  5. *    函 数 名: TOUCH_PressValid
  6. *    功能说明: 判断按压是否有效,根据X, Y的ADC值进行大致判断
  7. *    形    参:  无
  8. *    返 回 值: 1 表示有效; 0 表示无效
  9. *********************************************************************************************************
  10. */
  11. static uint8_t     TOUCH_PressValid(uint16_t _usX, uint16_t _usY)
  12. {
  13.      if ((_usX <= ADC_VALID_OFFSET) || (_usY <= ADC_VALID_OFFSET)
  14.          || (_usX >= g_tTP.usMaxAdc - ADC_VALID_OFFSET)
  15.          || (_usY >= g_tTP.usMaxAdc - ADC_VALID_OFFSET))
  16.      {
  17.          return 0;
  18.      }
  19.      else
  20.      {
  21.          return 1;
  22.      }
  23. }
复制代码
6.    DOWN_VALID的宏定义是
         #define DOWN_VALID             30
     由于是每2ms进行一次检测,这里就表示延迟30ms后进行触摸数据采集。延迟30ms是为了消除触摸抖动。
7.    SAMPLE_COUNT 的宏定义是
        #define SAMPLE_COUNT 20   
      由于是每2ms进行一次检测,这里就表示采集够10组数据,即20ms后进行下一步操作。
8.    对X轴和Y轴的ADC数值都进行软件滤波。软件滤波函数TOUCH_DataFilter的实现方法是对10组数值由小到大进行排序,对第3个,第4个和第5个数值求和,然后求平均,将平均值作为最终的ADC数值。
9.    变量标识s_down = 0表示触摸之前是未按下状态,在此条件里面设置s_down = 1表示触摸已经按下,并通过函数TOUCH_TransX(这个函数比较关键,是通过触摸校准函数得到的一个线性关系)将当前的X轴和Y轴ADC数值转换成实际的坐标值,然后调用函数GUI_PID_StoreState将当前的坐标信息存储到指针输入设备的FIFO里面。
10.  设置变量标识s_down = 1后会进入此条件里面,在这个条件里面通过函数TOUCH_MoveValid判断当前是否是有效的移动,如果是,就继续调用函数GUI_PID_StoreState将当前的坐标信息存储到指针输入设备的FIFO里面,如果不是,就设置全局变量g_tTP.usAdcNowX = 0。
11.  如果通过STMPE811的中断输出引脚检测到触摸未按下,然后判断变量s_count是否大于0,如果大于0的话,做减减运算,算是做了一个松手延迟,防止抖动。减到0的时候,将触摸未按下或者说触摸释放消息通过函数GUI_PID_StoreState存储到指针输入设备的FIFO里面。

4.7.5  电阻屏触摸校准


     由于不同电阻触摸板的线性度参差不齐,不能直接采用比例关系将电阻触摸芯片STMPE811的返回值通过比例关系转换成实际的坐标。比如我们操作的显示屏分辨率是800*480,电阻触摸芯片采用STMPE81112位ADC,触摸值范围0-4095),获得当前的触摸值是(1024, 2048),按照比例关系转换成坐标值就是(1024*800/40962048*800/4096),即(200400)。采用这种方法效果不好,容易出现点击不准确的问题。
      鉴于此原因,需要通过触摸校准在ADC数值和显示屏分辨率之间建立一个新的线性关系,简单的说就是由比例关系y = ax升级为y = ax + b。如果有了新的触摸ADC数值,代入这个线性关系里面就可以得到当前实际的坐标值,触摸校准的作用就在这里了。
     另外,emWin本身也是支持两点触摸校准的,后来测试发现自带的这种校准与国内厂商生产的电阻屏配套时,效果不够理想,容易出现飞点。现在修改为我们自己实现的4点触摸校准,效果好了很多,基本已经没有飞点了。具体触摸校准的实现,我们这里暂时不做讲解了,因为做的稍有些复杂,不太容易讲解清楚,后面再次升级emWin教程时,会结合我们F407板子的电阻触摸芯片TSC2046和RA8875自带的电阻触摸统一讲解。如果大家有兴趣的话,可以研究代码的实现。
     电阻触摸校准的校准方法在第66章,附件A有讲解。

4.7.6  不同触摸IC的识别方法


     开发板配套了不同触摸IC的显示屏,所以要做区分,区分方法也比较简单,因为三款芯片GT811,FT5X06和STMPE811都是I2C接口,通过I2C地址就区分开了,具体代码如下:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_DetectLcdType
  4. *    功能说明: 通过I2C触摸芯片,识别LCD模组类型。结果存放在全局变量 g_LcdType 和 g_TouchType
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_DetectLcdType(void)
  10. {
  11.      uint8_t i;
  12.    
  13.      g_TouchType = 0xFF;
  14.      g_LcdType = 0xFF;
  15.    
  16.      /* 50ms,等待GT811复位就绪,才能探测GT811芯片 ID */
  17.      for (i = 0; i < 5; i++)
  18.      {
  19.           /*
  20.               GT811电容触摸, 有 800 * 480 和 1024 * 600 两种分辨率(根据GT811的 Sensor ID识别(有3个状态)      
  21.               GT811 的从设备地址有三组可选,以方便主控调配。三组地址分别为:0xBA/0xBB、0x6E/0x6F和0x28/0x29        
  22.          */
  23.          if (i2c_CheckDevice(GT811_I2C_ADDR1) == 0)   //--------------(1)
  24.          {
  25.               g_GT811.i2c_addr = GT811_I2C_ADDR1;
  26.               g_TouchType = CT_GT811;
  27.               g_LcdType = LCD_70_800X480;     
  28.               touch_printf("检测到7.0寸电容触摸屏 800x480\\r\\n");
  29.               break;
  30.          }
  31.          if (i2c_CheckDevice(GT811_I2C_ADDR3) == 0)  //--------------(2)
  32.          {
  33.               g_GT811.i2c_addr = GT811_I2C_ADDR3;
  34.               g_TouchType = CT_GT811;
  35.               g_LcdType = LCD_70_1024X600;
  36.               touch_printf("检测到7.0寸电容触摸屏 1024x600\\r\\n");
  37.               break;
  38.          }      
  39.         
  40.          /* FT系列电容触摸触摸 : 4.3寸id = 0x55    5.0寸id = 0x0A  7.0寸id = 0x06 */
  41.          if (i2c_CheckDevice(FT5X06_I2C_ADDR) == 0) //--------------(3)
  42.          {
  43.               uint8_t id;
  44.                
  45.               bsp_DelayMS(100);                     //--------------(4)
  46.             
  47.               id = FT5X06_ReadID();                  //--------------(5)
  48.               if (id == 0x55)
  49.               {
  50.                    g_TouchType = CT_FT5X06;
  51.                    g_LcdType = LCD_43_480X272;     
  52.                    touch_printf("检测到4.3寸电容触摸屏\\r\\n");
  53.               }
  54.               else if (id == 0x0A)
  55.               {
  56.                    g_TouchType = CT_FT5X06;
  57.                    g_LcdType = LCD_50_800X480;     
  58.                    touch_printf("检测到5.0寸电容触摸屏\\r\\n");                 
  59.               }
  60.               else /* id == 0x06 表示7寸电容屏(FT芯片) */
  61.               {
  62.                    g_TouchType = CT_FT5X06;
  63.                    g_LcdType = LCD_70_800X480;     
  64.                    touch_printf("检测到7.0寸电容触摸屏\\r\\n");                     
  65.               }
  66.               break;
  67.          }
  68.           /* 电阻触摸板 */      
  69.          if (i2c_CheckDevice(STMPE811_I2C_ADDRESS) == 0) //--------------(6)
  70.          {
  71.               /*           
  72.                    0  = 4.3寸屏(480X272)
  73.                    1  = 5.0寸屏(480X272)
  74.                    2  = 5.0寸屏(800X480)
  75.                    3  = 7.0寸屏(800X480)
  76.                   4  = 7.0寸屏(1024X600)
  77.                    5  = 3.5寸屏(480X320)            
  78.               */                    
  79.               uint8_t id;           
  80.             
  81.               g_TouchType = CT_STMPE811;  /* 触摸类型 */
  82.             
  83.               STMPE811_InitHard();   /* 必须先配置才能读取ID */
  84.             
  85.               id = STMPE811_ReadIO(); /* 识别LCD硬件类型 */          //--------------(7)
  86.               touch_printf("检测到电阻触摸屏, id = %d\\r\\n", id);
  87.               switch (id)
  88.               {
  89.                    case 0:
  90.                        g_LcdType = LCD_43_480X272;
  91.                        break;
  92.                    case 1:
  93.                        g_LcdType = LCD_50_480X272;
  94.                        break;
  95.                    case 2:
  96.                        g_LcdType = LCD_50_800X480;
  97.                        break;
  98.                    case 3:
  99.                        g_LcdType = LCD_70_800X480;
  100.                        break;
  101.                    case 4:
  102.                        g_LcdType = LCD_70_1024X600;
  103.                        break;
  104.                    case 5:
  105.                        g_LcdType = LCD_35_480X320;
  106.                        break;            
  107.                   
  108.                    default:
  109.                        g_LcdType = LCD_35_480X320;
  110.                        break;
  111.               }            
  112.               break;            
  113.          }      
  114.         
  115.          bsp_DelayMS(10);   //--------------(6)
  116.      }
  117.    
  118.      if (i == 5)
  119.      {
  120.          touch_printf("未识别出显示模块\\r\\n");
  121.      }
  122. }
复制代码
1.    通过GT811的I2C地址GT811_I2C_ADDR1检测是否是7寸800*480分辨率显示屏。
2.    通过GT811的I2C地址GT811_I2C_ADDR3检测是否是7寸1024*600分辨率显示屏。
3.    通过FT5X06的I2C地址FT5X06_I2C_ADDR检查携带此触摸IC的显示屏。
4.    这个延迟函数千万不可以省略,如果这个延迟省略了,FT5X06后面的通信会有问题。加上这个延迟后才正常,估计是因为上电后,FT5X06需要等待一段时间才能进入稳定状态,比如自动的触摸校准是需要时间的。
5.    通过函数FT5X06_ReadID返回的ID号可以识别基于此芯片的4.3寸,5寸和7寸触摸屏。
6.    通过STMPE811的I2C地址STMPE811_I2C_ADDR检查携带此触摸IC的显示屏。
7.    通过函数STMPE811_ReadIO返回的硬件IO状态可以识别基于此芯片的3.5寸480*320分辨率显示屏,4.3寸480*272分辨率显示屏,5寸480*272和800*480分辨率显示屏以及7寸800*480和1024*600分辨率显示屏。
8.    这个延迟函数也千万不要省略,如果这个延迟省略了,GT811后面的通信会有问题。加上这个延迟后才正常,估计是因为上电后,GT811需要等待一段时间才能进入稳定状态,比如自动的触摸校准是需要时间的。这个跟FT5X06是一样的。

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


      三款触摸芯片都是使用查询方式做的触摸扫描,只是扫描周期不一样,具体实现是将电容触摸GT811和FT5X06都设置为10ms调用一次,而电阻触摸STMPE811设置为1ms调用一次,调用方法是将这种扫描放在周期为1ms的滴答定时器中断里面实现,下面的两个函数都被滴答定时器中断所调用,这两个函数在bsp.c文件中实现,而滴答定时器中断在bsp_timer.c文件里面:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_RunPer10ms
  4. *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
  5. *             不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
  6. *    形    参:无
  7. *    返 回 值: 无
  8. *********************************************************************************************************
  9. */
  10. void bsp_RunPer10ms(void)          //--------------(1)
  11. {
  12.      bsp_KeyScan();         /* 按键扫描 */
  13.    
  14.      /* 电容触摸屏GT811 */
  15.      if(g_GT811.Enable == 1)       //--------------(2)
  16.      {
  17.          GT811_OnePiontScan();
  18.      }
  19.      /* 电阻触摸屏FT5X06 */
  20.      if(g_tFT5X06.Enable == 1)   //--------------(3)
  21.      {
  22.          FT5X06_OnePiontScan();
  23.      }
  24. }
  25. /*
  26. *********************************************************************************************************
  27. *    函 数 名: bsp_RunPer1ms
  28. *    功能说明: 该函数每隔1ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些需要周期性处理的事务
  29. *             可以放在此函数。比如:触摸坐标扫描。
  30. *    形    参: 无
  31. *    返 回 值: 无
  32. *********************************************************************************************************
  33. */
  34. void bsp_RunPer1ms(void)    //--------------(4)
  35. {
  36.      /* 电阻触摸屏 */
  37.      if(g_tTP.Enable == 1)  //--------------(5)
  38.      {
  39.          TOUCH_Scan();
  40.      }
  41. }
复制代码
1.    此函数被滴答定时器中断10ms调用一次,滴答定时器中断周期被设置为1ms,在bsp_timer.c文件里面实现。
2.    电容屏GT811的扫描函数。变量g_GT811.Enable的使能是通过用户调用了初始化函数bsp_DetectLcdType()和TOUCH_InitHard()来实现的。其中函数bsp_DetectLcdType用来识别不同的显示屏,函数TOUCH_InitHard根据识别出的显示屏调用GT811的初始化函数GT811_InitHard,在这个函数里面设置变量g_GT811.Enable = 1。
3.    电容屏FT5X06的扫描函数。变量g_tFT5X06.Enable的使能是通过用户调用了初始化函数bsp_DetectLcdType()和TOUCH_InitHard()来实现的。其中函数bsp_DetectLcdType用来识别不同的显示屏,函数TOUCH_InitHard根据识别出的显示屏调用FT5X06的初始化函数FT5X06_InitHard,在这个函数里面设置变量g_tFT5X06.Enable = 1。
4.    此函数被滴答定时器中断1ms调用一次。
5.    电阻屏STMPE811的扫描函数。变量g_tTP.Enable的使能是通过用户调用了初始化函数bsp_DetectLcdType()和TOUCH_InitHard()来实现的。其中函数bsp_DetectLcdType用来识别不同的显示屏,函数TOUCH_InitHard根据识别出的显示屏在函数内部将g_tTP.Enable设置为1。
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: TOUCH_InitHard
  4. *    功能说明: 初始化触摸芯片。 再之前,必须先执行 bsp_DetectLcdType() 识别触摸出触摸芯片型号.
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void TOUCH_InitHard(void)
  10. {   
  11.     g_tTP.Enable = 0;
  12.    
  13.      /* 默认是发给图层1,如果是发给图层2,请修改Layer参数为1 */
  14.      State.Layer = 0;
  15.    
  16.      switch (g_TouchType)
  17.      {
  18.          case CT_GT811:              /* 电容触摸 7寸 */
  19.               GT811_InitHard();
  20.               break;
  21.         
  22.          case CT_FT5X06:             /* 电容触摸 4.3寸 */
  23.               FT5X06_InitHard();
  24.               break;
  25.         
  26.          case CT_STMPE811:      /* 电阻的 */
  27.               //STMPE811_InitHard();   < bsp_DetectLcdType() 内部已经执行初始化
  28.               g_tTP.usMaxAdc = 4095; /* 12位ADC */   
  29.         
  30.               TOUCH_LoadParam(); /* 读取校准参数 */
  31.               g_tTP.Write = g_tTP.Read = 0;
  32.               g_tTP.Enable = 1;
  33.               break;
  34.         
  35.          default:
  36.               break;
  37.      }
  38. }
复制代码
注意上面代码中置红的两个语句。
     1、State是全局的结构体变量,如果设置State.Layer = 0表示给图层1发送触摸坐标信息。如果设置State.Layer = 1表示给图层2发送触摸坐标信息。
     2、函数TOUCH_LoadParam用于从板载的EEPROM中加载触摸参数。

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


    通过前面的讲解,移植触摸驱动到自己的板子上,最简单的办法是将开发板上与触摸相关的文件全部移植过来,然后在这些文件的基础上进行修改。下面分两种情况进行说明:
    1、电容屏触摸的移植比较简单,如果用户用的触摸IC跟开发板一样,直接拿来用即可,如果不一样,需要先将触摸IC的驱动实现,然后按照开发板提供的GT811或者FT5X06的触摸扫描函数,照葫芦画瓢实现一个即可。
    2、电阻屏的移植稍麻烦些,如果用户用的触摸IC跟开发板一样,直接拿来用即可,如果不一样,需要先将触摸IC的驱动实现,然后仿照TOUCH_STMPE811.c文件提供触摸按下状态函数,X轴,Y轴的ADC数值读取函数和清除触摸中断标志函数。最后用重新实现的这几个函数替换bsp_touch.c文件中的原函数即可。另外要注意一点,这种方式实现后,虽然触摸校准依然可以使用,但是开发板的触摸校准参数保存在EEPROM中,用户可以根据自己的实际情况选择存储介质。另外,触摸参数的保存和读取在param.c文件实现。
     如果大家不想用开发板实现的方案,想自己重新实现一个,也是没问题的,注意跟emWin关联的方式,即函数GUI_PID_StoreState的使用。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:25:16 | 显示全部楼层
4.8  第5步:STemWin底层接口函数和配置


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

4.8.1  GUIConf.c文件


    GUIConf.c文件主要实现系统动态内存的配置,我们在官方代码的基础上做了下修改,添加了一个宏定义,方便用户选择使用内部 SRAM还是外部SDRAM作为emWin的动态内存:
  1. #include "GUI.h"
  2. #include "bsp.h"
  3. /*********************************************************************
  4. *
  5. *       Defines
  6. *
  7. **********************************************************************
  8. */
  9. //
  10. // Define the available number of bytes available for the GUI
  11. //
  12. #define EX_SRAM   1/*1 used extern sram, 0 used internal sram */   //--------------(1)
  13. #if EX_SRAM
  14. #define GUI_NUMBYTES  (1024*1024*8)
  15. #else
  16. #define GUI_NUMBYTES  (100*1024)   
  17. #endif
  18. /* Define the average block size */
  19. #define GUI_BLOCKSIZE 0x80          //--------------(2)
  20. /*********************************************************************
  21. *
  22. *       Public code
  23. *
  24. **********************************************************************
  25. */
  26. /*********************************************************************
  27. *
  28. *       GUI_X_Config
  29. *
  30. * Purpose:
  31. *   Called during the initialization process in order to set up the
  32. *   available memory for the GUI.
  33. */
  34. void GUI_X_Config(void)
  35. {
  36. #if EX_SRAM                                    //--------------(3)
  37.      static U32 *aMemory;
  38.      aMemory = (U32 *)SDRAM_APP_BUF;
  39.    
  40.      /*  Assign memory to emWin */
  41.      GUI_ALLOC_AssignMemory(aMemory, GUI_NUMBYTES);
  42.      GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);
  43. #else                                         //--------------(4)
  44.      /* 32 bit aligned memory area */
  45.      static U32 aMemory[GUI_NUMBYTES / 4];
  46.    
  47.      /*  Assign memory to emWin */
  48.      GUI_ALLOC_AssignMemory(aMemory, GUI_NUMBYTES);
  49.      GUI_ALLOC_SetAvBlockSize(GUI_BLOCKSIZE);
  50. #endif
  51. }
复制代码
1.     通过宏定义来配置使用内部SRAM还是外部的SDRAM做为emWin的动态内存,当配置:
     #define EX_SRAM    1 表示使用外部SDRAM作为emWin动态内存,大小8MB。具体大小是由宏定义#define GUI_NUMBYTES (1024*1024*8)来设置的。
      #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动态内存。
       static U32 aMemory[GUI_NUMBYTES / 4];  

4.8.2  GUIConf.h文件


    这个文件主要是默认的系统配置,代码如下:
  1. #ifndef GUICONF_H
  2. #define GUICONF_H
  3. /*********************************************************************
  4. *
  5. *       Multi layer/display support
  6. */
  7. #define GUI_NUM_LAYERS            2    // Maximum number of available layers   //--------------(1)
  8. /*********************************************************************
  9. *
  10. *       Multi tasking support
  11. */
  12. #define GUI_OS                    0    // Compile with multitasking support    //--------------(2)
  13. /*********************************************************************
  14. *
  15. *       Configuration of touch support
  16. */
  17. #define GUI_SUPPORT_TOUCH         1    // Support a touch screen (req. win-manager) //--------------(3)
  18. /*********************************************************************
  19. *
  20. *       Default font
  21. */
  22. #define GUI_DEFAULT_FONT          &GUI_Font6x8
  23. /*********************************************************************
  24. *
  25. *         Configuration of available packages
  26. */
  27. #define GUI_SUPPORT_MOUSE    1    // Mouse support              //--------------(4)
  28. #define GUI_WINSUPPORT       1    // Use Window Manager         //--------------(5)
  29. #define GUI_SUPPORT_MEMDEV   1    // Use Memory Devices         //--------------(6)
  30. #define GUI_SUPPORT_DEVICES  1    // Enable use of device pointers   //--------------(7)
  31. #endif  // Avoid multiple inclusion
复制代码
1.     设置最大支持的图层数为2。
2.     设置不支持OS,因为我们是裸机方式移植,这里配置为0就表示不支持,配置为1表示支持。
3.     设置支持触摸屏,配置为0表示不支持,配置为1表示支持。
4.     设置支持鼠标,配置为0表示不支持,配置为1表示支持。
5.     设置支持窗口管理器,配置为0表示不支持,配置为1表示支持。
6.     设置支持存储设备,配置为0表示不支持,配置为1表示支持。
7.     设置支持设备指针,配置为0表示不支持,配置为1表示支持。

4.8.3  LCDConf_Lin_Template.c文件


    毫不夸张的说,这个文件是STemWin移植过程中最重要的文件,主要配置了STemWin的显示尺寸,显示驱动,颜色转换以及底层优化都是在这个文件实现的。当前这个驱动文件已经做得比较成熟完善了,为了完善这个驱动耗费了很多精力(这个驱动是由官方的驱动修改而来)。STemWin支持的多缓冲,多图层和STM32F429/439支持的8种颜色格式都可以任意配置。
    如果用户要使用这个文件配置自己的显示屏,直接拿去使用即可,唯一要做的就是根据自己的显示屏和项目需求来设置宏定义以及显示屏背光设置函数LCD_SetBackLight(这个函数要自己实现,教程提供的例子是在bsp_tft_lcd.c文件中实现),别的什么都不用修改。
    供用户使用的12项宏定义,注释已经非常详细,默认情况下,本教程配套的emWin例子都是用的三缓冲,RGB565格式,且仅使用单图层:
  1. /*
  2. **********************************************************************************************************
  3.                                           用户可以配置的选项
  4. **********************************************************************************************************
  5. */
  6. /* 0. 在官方代码的基础上再做优化,官方的部分函数效率低,耗内存, 0表示优化 */
  7. #define emWin_Optimize   0
  8. /*
  9.   1. 显示屏的物理分辨率,驱动已经做了显示屏自适应,支持4.3寸,5寸和7寸屏
  10.      这里填写自适应显示屏中的最大分辨率。
  11. */
  12. #define XSIZE_PHYS       800
  13. #define YSIZE_PHYS       480
  14. /* 2. 多缓冲 / 虚拟屏,多缓冲和虚拟屏不可同时使用,emWin不支持 */
  15. #define NUM_BUFFERS      3 /* 定义多缓冲个数,仅可以设置1,2和3,也就是最大支持三缓冲 */
  16. #define NUM_VSCREENS     1 /* 定义虚拟屏个数 */
  17. /* 3. 没有图层激活时,背景色设置, 暂时未用到 */
  18. #define BK_COLOR         GUI_DARKBLUE
  19. /*
  20.    4. 重定义图层数,对于STM32F429/439,用户可以选择一个图层或者两个图层,不支持三图层
  21.       (1). 设置GUI_NUM_LAYERS = 1时,即仅使用图层1时,默认触摸值是发送给图层1的。
  22.        (2). 设置GUI_NUM_LAYERS = 2时,即图层1和图层2都已经使能,此时图层2是顶层,
  23.             用户需要根据自己的使用情况设置如下两个地方。
  24.             a. 在bsp_touch.c文件中的函数TOUCH_InitHard里面设置参数State.Layer = 1,1就表示
  25.                给图层2发送触摸值。
  26.             b. 调用GUI_Init函数后,调用函数GUI_SelectLayer(1), 设置当前操作的是图层2。
  27. */
  28. #undef  GUI_NUM_LAYERS
  29. #define GUI_NUM_LAYERS    1
  30. /*
  31.    5. 设置图层1和图层2对应的显存地址
  32.       (1) EXT_SDRAM_ADDR 是SDRAM的首地址。
  33.       (2) LCD_LAYER0_FRAME_BUFFER 是图层1的显存地址。
  34.        (3) LCD_LAYER1_FRAME_BUFFER 是图层2的显存地址。
  35.        (4) 每个图层的显存大小比较考究,这里进行下简单的说明。
  36.            如果用户选择的颜色模式 = 32位色ARGB8888,显存的大小:
  37.            XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS * NUM_BUFFERS
  38.          
  39.            颜色模式 = 24位色RGB888,显存的大小:
  40.            XSIZE_PHYS * YSIZE_PHYS * 3 * NUM_VSCREENS * NUM_BUFFERS
  41.          
  42.            颜色模式 = 16位色RGB566,ARGB1555, ARGB4444,AL88,那么显存的大小就是:
  43.            XSIZE_PHYS * YSIZE_PHYS * 2 * NUM_VSCREENS * NUM_BUFFERS
  44.            颜色模式 = 8位色L8,AL44,那么显存的大小就是:
  45.            XSIZE_PHYS * YSIZE_PHYS * 1 * NUM_VSCREENS * NUM_BUFFERS  
  46.      
  47.       这里为了方便起见,将开发板配套的16MB的SDRAM前8MB分配给LCD显存使用,后8MB用于emWin动态内存。
  48.        对于24位色,16位色,8位色,用户可以对其使能三缓冲,并且使能双图层。但是32位色也使能三缓冲和双
  49.        图层的话会超出8MB,所以用户根据自己的情况做显存和emWin动态内存的分配调整。
  50.          举一个例子,对于800*480分辨率的显示屏,使能32位色,三缓冲,那么最终一个图层需要的大小就是
  51.       800 * 480 * 4 * 3  = 4.394MB的空间,如果是双图层,已经超出8MB的分配范围。
  52.      
  53.       (5)为了方便起见,图层2的宏定义LCD_LAYER1_FRAME_BUFFER中的参数4是按照32位色设置的,如果用户的图层1
  54.          使用的是8位色,这里填数字1,如果是16位色,这里填2,如果是24位色,这里填3。
  55. */
  56. #define LCD_LAYER0_FRAME_BUFFER  EXT_SDRAM_ADDR
  57. #define LCD_LAYER1_FRAME_BUFFER  (LCD_LAYER0_FRAME_BUFFER + XSIZE_PHYS * YSIZE_PHYS * 4 * NUM_VSCREENS *
  58. NUM_BUFFERS)
  59. /*
  60.    6. STM32F429/439支持的颜色模式,所有模式都支持,用户可任意配置。
  61.       特别注意如下两个问题:
  62.        (1) 如果用户选择了ARGB8888或者RGB888模式,LCD闪烁比较厉害的话,
  63.            请降低LTDC的时钟大小,在文件bsp_tft_429.c的函数LCD_ConfigLTDC里面设置。
  64.            a. 一般800*480分辨率的显示屏,ARGB8888或者RGB888模式LTDC时钟选择10-20MHz即可。
  65.            b. 480*272分辨率的可以高些,取20MHz左右即可。
  66.        (2) 16位色或者8位色模式,LTDC的时钟频率一般可以比24位色或者32位色的高一倍。
  67. */
  68. #define _CM_ARGB8888      1
  69. #define _CM_RGB888        2
  70. #define _CM_RGB565        3
  71. #define _CM_ARGB1555      4
  72. #define _CM_ARGB4444      5
  73. #define _CM_L8            6
  74. #define _CM_AL44          7
  75. #define _CM_AL88          8
  76. /* 7. 配置图层1的颜色模式和分辨率大小 */
  77. #define COLOR_MODE_0      _CM_RGB565
  78. #define XSIZE_0           XSIZE_PHYS
  79. #define YSIZE_0           YSIZE_PHYS
  80. /* 8. 配置图层2的的颜色模式和分辨率大小 */
  81. #define COLOR_MODE_1      _CM_RGB565
  82. #define XSIZE_1           XSIZE_PHYS
  83. #define YSIZE_1           YSIZE_PHYS
  84. /* 9. 单图层情况下,根据用户选择的颜色模式可自动选择图层1的emWin的驱动和颜色模式 */
  85. #if   (COLOR_MODE_0 == _CM_ARGB8888)
  86.   #define COLOR_CONVERSION_0 GUICC_M8888I
  87.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_32
  88. #elif (COLOR_MODE_0 == _CM_RGB888)
  89.   #define COLOR_CONVERSION_0 GUICC_M888
  90.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_24
  91. #elif (COLOR_MODE_0 == _CM_RGB565)
  92.   #define COLOR_CONVERSION_0 GUICC_M565
  93.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  94. #elif (COLOR_MODE_0 == _CM_ARGB1555)
  95.   #define COLOR_CONVERSION_0 GUICC_M1555I
  96.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  97. #elif (COLOR_MODE_0 == _CM_ARGB4444)
  98.   #define COLOR_CONVERSION_0 GUICC_M4444I
  99.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  100. #elif (COLOR_MODE_0 == _CM_L8)
  101.   #define COLOR_CONVERSION_0 GUICC_8666
  102.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  103. #elif (COLOR_MODE_0 == _CM_AL44)
  104.   #define COLOR_CONVERSION_0 GUICC_1616I
  105.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_8
  106. #elif (COLOR_MODE_0 == _CM_AL88)
  107.   #define COLOR_CONVERSION_0 GUICC_88666I
  108.   #define DISPLAY_DRIVER_0   GUIDRV_LIN_16
  109. #else
  110.   #error Illegal color mode 0!
  111. #endif
  112. /* 10. 双图层情况下,根据用户选择的颜色模式可自动选择图层2的emWin的驱动和颜色模式 */
  113. #if (GUI_NUM_LAYERS > 1)
  114. #if   (COLOR_MODE_1 == _CM_ARGB8888)
  115.   #define COLOR_CONVERSION_1 GUICC_M8888I
  116.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_32
  117. #elif (COLOR_MODE_1 == _CM_RGB888)
  118.   #define COLOR_CONVERSION_1 GUICC_M888
  119.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_24
  120. #elif (COLOR_MODE_1 == _CM_RGB565)
  121.   #define COLOR_CONVERSION_1 GUICC_M565
  122.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  123. #elif (COLOR_MODE_1 == _CM_ARGB1555)
  124.   #define COLOR_CONVERSION_1 GUICC_M1555I
  125.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  126. #elif (COLOR_MODE_1 == _CM_ARGB4444)
  127.   #define COLOR_CONVERSION_1 GUICC_M4444I
  128.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  129. #elif (COLOR_MODE_1 == _CM_L8)
  130.   #define COLOR_CONVERSION_1 GUICC_8666
  131.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  132. #elif (COLOR_MODE_1 == _CM_AL44)
  133.   #define COLOR_CONVERSION_1 GUICC_1616I
  134.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_8
  135. #elif (COLOR_MODE_1 == _CM_AL88)
  136.   #define COLOR_CONVERSION_1 GUICC_88666I
  137.   #define DISPLAY_DRIVER_1   GUIDRV_LIN_16
  138. #else
  139.   #error Illegal color mode 1!
  140. #endif
  141. #else
  142. #undef XSIZE_0
  143. #undef YSIZE_0
  144. #define XSIZE_0       XSIZE_PHYS
  145. #define YSIZE_0       YSIZE_PHYS
  146. #endif
  147. /*11. 配置选项检测,防止配置错误或者某些选项没有配置 */
  148. #ifndef   XSIZE_PHYS
  149.   #error Physical X size of display is not defined!
  150. #endif
  151. #ifndef   YSIZE_PHYS
  152.   #error Physical Y size of display is not defined!
  153. #endif
  154. #ifndef   NUM_VSCREENS
  155.   #define NUM_VSCREENS 1
  156. #else
  157.   #if (NUM_VSCREENS <= 0)
  158.     #error At least one screeen needs to be defined!
  159.   #endif
  160. #endif
  161. #if (NUM_VSCREENS > 1) && (NUM_BUFFERS > 1)
  162.   #error Virtual screens and multiple buffers are not allowed!
  163. #endif
复制代码
==============
    这里就不把这个文件展开为大家讲解了,因为展开后涉及到的内容非常多,工作量稍大。但为了满足爱研究的同学,把如何学习这个文件的方法说一说,这样就能事半功倍了,要不不知道从哪里着手:
    1、这个文件里面最主要的就是函数LCD_X_Config和函数LCD_X_DisplayDriver,除了LTDC行中断函数以外,所有其它的函数都是直接或者间接的被这两个函数所调用,有兴趣的同学可以在纸上把这两个函数所调用函数的层层关系全部关联起来,然后逐个进行击破,这样就有一个比较全面的认识了。
    2、把这个文件里面各个函数的关系搞清楚之后,有必要了解下函数LCD_X_Config和函数LCD_X_DisplayDriver是如何被emWin调用的。对于这个问题,emWin官方手册的配置章节已经给出了答案,就是下面这个截图:
4.21.png

    由上面的截图可以看出,函数LCD_X_Config和函数LCD_X_DisplayDriver在用户所调用的初始化函数GUI_Init里面依次被调用。
特别补充,待此教程发布后,根据用户的反馈,如果觉得这部分知识有必要全部展开详细讲解的话,我们会在论坛专门开个帖子详细讲解或者发布emWin的升级版时加入
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:29:37 | 显示全部楼层
4.9   第6步:STemWin裸机方式的接口文件


    STemWin裸机方式的接口文件就是第1步中添加的文件GUI_X.c,用户要做的就是提供一个系统时间基准即可,代码实现如下:
  1. #include "GUI.h"
  2. #include "bsp.h"
  3. /*********************************************************************
  4. *
  5. *       Global data
  6. */
  7. volatile GUI_TIMER_TIME OS_TimeMS;    //--------------(1)
  8. /*********************************************************************
  9. *
  10. *      Timing:
  11. *                 GUI_X_GetTime()
  12. *                 GUI_X_Delay(int)
  13.   Some timing dependent routines require a GetTime
  14.   and delay function. Default time unit (tick), normally is
  15.   1 ms.
  16. */
  17. extern __IO int32_t g_iRunTime;    //--------------(2)
  18. int GUI_X_GetTime(void)            //--------------(3)
  19. {
  20.      return g_iRunTime;
  21. }
  22. void GUI_X_Delay(int ms)           //--------------(4)
  23. {
  24.      int tEnd = g_iRunTime + ms;
  25.      while ((tEnd - g_iRunTime) > 0);
  26. }
  27. /*********************************************************************
  28. *
  29. *       GUI_X_Init()
  30. *
  31. * Note:
  32. *     GUI_X_Init() is called from GUI_Init is a possibility to init
  33. *     some hardware which needs to be up and running before the GUI.
  34. *     If not required, leave this routine blank.
  35. */
  36. void GUI_X_Init(void) {}
  37. /*********************************************************************
  38. *
  39. *       GUI_X_ExecIdle
  40. *
  41. * Note:
  42. *  Called if WM is in idle state
  43. */
  44. void GUI_X_ExecIdle(void) {}
  45. /*********************************************************************
  46. *
  47. *      Logging: OS dependent
  48. Note:
  49.   Logging is used in higher debug levels only. The typical target
  50.   build does not use logging and does therefor not require any of
  51.   the logging routines below. For a release build without logging
  52.   the routines below may be eliminated to save some space.
  53.   (If the linker is not function aware and eliminates unreferenced
  54.   functions automatically)
  55. */
  56. void GUI_X_Log     (const char *s) { GUI_USE_PARA(s); }
  57. void GUI_X_Warn    (const char *s) { GUI_USE_PARA(s); }
  58. void GUI_X_ErrorOut(const char *s) { GUI_USE_PARA(s); }
复制代码
1.     这个是官方定义的时间基准变量,我们未使用,重新定义了一个,方便与我们的BSP板级支持包统一。
2.     bsp_timer.c文件里面定义的时间基准变量,在滴答定时器中断里面每1ms进行一次计数。
3.     获取系统时间基准的接口函数,直接返回变量g_iRunTime的数值即可。
4.     基于系统时间基准变量g_iRunTime实现的阻塞式延迟功能,单位ms。
----------------------
其它接口函数都没有使用到,用户仅需完成以上两个函数即可。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:31:50 | 显示全部楼层
4.10    第7步:添加STemWin应用进行测试。


    (提示:使用ST的emWin务必要在初始化前使能STM32F439/429的硬件CRC,否则emWin无法正常启动,切记切记!!!!
    为了测试移植的工程是否可用,为其做了一个简单的测试界面,显示效果如下,800*480分辨率:
4.22.png


界面能够显示出来只能说明显示驱动没问题了,还要测试触摸。这个例子已经开启了游标显示,用户可以水平或者垂直地在界面上滑动,看看游标如何移动,如果能够跟着手一起移动说明触摸也没有问题。如果不能,就要分两种情况进行分析:
1、对于电阻屏需要做触摸校准,校准方法看教程第66章附件A中的说明。另外,如果这个是大家自己设计的,就要排查代码和触摸板的硬件设计是否有问题。
2、由于电容屏是自动触摸校准的,出现这种情况的话需要排查代码和触摸板的硬件设计。

下面是编写的测试代码,配套的测试例子完整版是:V6-501_STemWin实验_裸机方式移植模板(含MDK和IAR)。
主函数初始化:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: main
  4. *    功能说明: 标准c程序入口。
  5. *    形    参: 无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. int main (void)
  10. {   
  11.      /* 初始化外设 */
  12.      bsp_Init();
  13.      /* 进入emWin主函数 */
  14.      MainTask();
  15. }
复制代码


硬件外设初始化
    硬件外设的初始化是在bsp.c文件实现:
  1. /*
  2. *********************************************************************************************************
  3. *    函 数 名: bsp_Init
  4. *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
  5. *    形    参:无
  6. *    返 回 值: 无
  7. *********************************************************************************************************
  8. */
  9. void bsp_Init(void)
  10. {
  11.      /*
  12.          由于ST固件库的启动文件已经执行了CPU系统时钟的初始化,所以不必再次重复配置系统时钟。
  13.          启动文件配置了CPU主时钟频率、内部Flash访问速度和可选的外部SRAM FSMC初始化。
  14.          系统时钟缺省配置为168MHz,如果需要更改,可以修改 system_stm32f4xx.c 文件
  15.      */
  16.      /* 使能CRC 因为使用STemWin前必须要使能 */
  17.     RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_CRC, ENABLE);
  18.    
  19.      /* 优先级分组设置为4,可配置0-15级抢占式优先级,0级子优先级,即不存在子优先级。*/
  20.      NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4);
  21.    
  22.      SystemCoreClockUpdate();    /* 根据PLL配置更新系统时钟频率变量 SystemCoreClock */
  23.      bsp_InitUart();        /* 初始化串口 */
  24.      bsp_InitKey();         /* 初始化按键变量(必须在 bsp_InitTimer() 之前调用) */
  25.    
  26.      bsp_InitExtIO();       /* FMC总线上扩展了32位输出IO, 操作LED等外设必须初始化 */
  27.      bsp_InitLed();         /* 初始LED指示灯端口 */
  28.    
  29.      bsp_InitI2C();         /* 配置I2C总线 */
  30.    
  31.      bsp_InitExtSDRAM();   /* 初始化SDRAM */
  32.      bsp_DetectLcdType();  /* 检测触摸板和LCD面板型号, 结果存在全局变量 g_TouchType, g_LcdType */
  33.    
  34.      TOUCH_InitHard();    /* 初始化配置触摸芯片 */
  35.      LCD_ConfigLTDC();     /* 初始化配置LTDC */
  36.    
  37. }
复制代码


emWin功能的具体实现(在MainTask.c文件里面):
  1. #include "MainTask.h"
  2. /*
  3. *********************************************************************************************************
  4. *                                         宏定义
  5. *********************************************************************************************************
  6. */
  7. #define ID_FRAMEWIN_0 (GUI_ID_USER + 0x00)
  8. #define ID_BUTTON_0 (GUI_ID_USER + 0x01)
  9. #define ID_SLIDER_0 (GUI_ID_USER + 0x02)
  10. /*
  11. *********************************************************************************************************
  12. *                           GUI_WIDGET_CREATE_INFO类型数组
  13. *********************************************************************************************************
  14. */
  15. static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
  16.   { FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 0, 800, 480, 0, 0x64, 0 },
  17.   { BUTTON_CreateIndirect, "Button", ID_BUTTON_0, 130, 28, 147, 35, 0, 0x0, 0 },
  18.   { SLIDER_CreateIndirect, "Slider", ID_SLIDER_0, 133, 118, 137, 25, 0, 0x0, 0 },
  19. };
  20. /*
  21. *********************************************************************************************************
  22. *    函 数 名: _cbDialog
  23. *    功能说明: 对话框回调函数        
  24. *    形    参: pMsg  回调参数
  25. *    返 回 值: 无
  26. *********************************************************************************************************
  27. */
  28. static void _cbDialog(WM_MESSAGE * pMsg)
  29. {
  30.      WM_HWIN hItem;
  31.      int     NCode;
  32.      int     Id;
  33.      switch (pMsg->MsgId)
  34.      {
  35.          case WM_INIT_DIALOG:
  36.               //
  37.               // 初始化 'Framewin'
  38.               //
  39.               hItem = pMsg->hWin;
  40.               FRAMEWIN_SetFont(hItem, GUI_FONT_32B_ASCII);
  41.               FRAMEWIN_SetTextAlign(hItem, GUI_TA_HCENTER | GUI_TA_VCENTER);
  42.               FRAMEWIN_SetText(hItem, "armfly");
  43.         
  44.               //
  45.               // 初始化 'Button'
  46.               //
  47.               hItem = WM_GetDialogItem(pMsg->hWin, ID_BUTTON_0);
  48.               BUTTON_SetFont(hItem, GUI_FONT_24B_ASCII);
  49.               BUTTON_SetText(hItem, "armfly");
  50.               break;
  51.         
  52.          case WM_NOTIFY_PARENT:
  53.               Id    = WM_GetId(pMsg->hWinSrc);
  54.               NCode = pMsg->Data.v;
  55.               switch(Id)
  56.               {
  57.                    case ID_BUTTON_0:
  58.                        switch(NCode)
  59.                        {
  60.                             case WM_NOTIFICATION_CLICKED:
  61.                                  break;
  62.                            
  63.                             case WM_NOTIFICATION_RELEASED:
  64.                                  break;
  65.                        }
  66.                        break;
  67.                   
  68.                    case ID_SLIDER_0:
  69.                        switch(NCode)
  70.                        {
  71.                             case WM_NOTIFICATION_CLICKED:
  72.                                  break;
  73.                            
  74.                             case WM_NOTIFICATION_RELEASED:
  75.                                  break;
  76.                            
  77.                             case WM_NOTIFICATION_VALUE_CHANGED:
  78.                                  break;
  79.                        }
  80.                        break;
  81.               }
  82.               break;
  83.             
  84.          default:
  85.               WM_DefaultProc(pMsg);
  86.               break;
  87.      }
  88. }
  89. /*
  90. *********************************************************************************************************
  91. *    函 数 名: CreateFramewin
  92. *    功能说明: 创建对话框      
  93. *    形    参: 无
  94. *    返 回 值: 返回对话框句柄
  95. *********************************************************************************************************
  96. */
  97. WM_HWIN CreateFramewin(void)
  98. {
  99.      WM_HWIN hWin;
  100.      hWin = GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);
  101.      return hWin;
  102. }
  103. /*
  104. *********************************************************************************************************
  105. *    函 数 名: MainTask
  106. *    功能说明: GUI主函数
  107. *    形    参: 无
  108. *    返 回 值: 无
  109. *********************************************************************************************************
  110. */
  111. void MainTask(void)
  112. {
  113.    
  114.      /* 初始化 */
  115.      GUI_Init();
  116.    
  117.      /*
  118.       关于多缓冲和窗口内存设备的设置说明
  119.         1. 使能多缓冲是调用的如下函数,用户要在LCDConf_Lin_Template.c文件中配置了多缓冲,调用此函数才有效:
  120.            WM_MULTIBUF_Enable(1);
  121.         2. 窗口使能使用内存设备是调用函数:WM_SetCreateFlags(WM_CF_MEMDEV);
  122.         3. 如果emWin的配置多缓冲和窗口内存设备都支持,二选一即可,且务必优先选择使用多缓冲,实际使用
  123.            STM32F429BIT6 + 32位SDRAM + RGB565/RGB888平台测试,多缓冲可以有效的降低窗口移动或者滑动时的撕裂
  124.            感,并有效的提高流畅性,通过使能窗口使用内存设备是做不到的。
  125.         4. 所有emWin例子默认是开启三缓冲。
  126.      */
  127.      WM_MULTIBUF_Enable(1);
  128.    
  129.      /*
  130.        触摸校准函数默认是注释掉的,电阻屏需要校准,电容屏无需校准。如果用户需要校准电阻屏的话,执行
  131.         此函数即可,会将触摸校准参数保存到EEPROM里面,以后系统上电会自动从EEPROM里面加载。
  132.      */
  133. //TOUCH_Calibration();
  134.      /* 显示游标 */
  135. GUI_CURSOR_Show();
  136.    
  137.      /* 创建对话框 */
  138.      CreateFramewin();
  139.         
  140.      while(1)
  141.      {
  142.          GUI_Delay(500);
  143.      }
  144. }
复制代码
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

740

主题

1326

回帖

3546

积分

管理员

春暖花开

Rank: 9Rank: 9Rank: 9

积分
3546
QQ
 楼主| 发表于 2016-12-26 16:32:50 | 显示全部楼层
4.11 显示屏闪烁问题解决方法


    如果大家调试状态下或者刚下载emWin的程序到STM32F429/439里面时,屏幕会闪烁,或者说抖动,这个是正常现象。详见此贴的说明:http://www.armbbs.cn/forum.php?mod=viewthread&tid=16892
    如果显示屏长时间处于抖动状态,说明LTDC的时钟配置高了,需要降低,可以先降低一半。配置方法看本章节4.6.1小节里面给的一个帖子地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=18528,里面有详细说明。

4.12 总结


    本章节为大家讲解的内容涉及到的知识较多,信息量较大,部分知识点没有弄明白是没有关系的,但是一定要按照本章节提供的移植方法,动手移植一遍。另外,最好移植一个与教程配套开发板不一样的显示屏和触摸IC,这样对于本章的认识将更加全面。
努力打造安富莱高质量微信公众号:点击扫描图片关注
回复

使用道具 举报

0

主题

2

回帖

36

积分

新手上路

积分
36
发表于 2018-6-20 10:51:54 | 显示全部楼层
HAVE A LOOK
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-4-30 07:12 , Processed in 0.322042 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表