OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 4602|回复: 1

[XILINX] 《领航者ZYNQ之FPGA开发指南 V2.0》第二十章 LCD触摸屏实验

[复制链接]

1118

主题

1129

帖子

2

精华

超级版主

Rank: 8Rank: 8

积分
4671
金钱
4671
注册时间
2019-5-8
在线时间
1224 小时
发表于 2021-11-16 11:38:17 | 显示全部楼层 |阅读模式
本帖最后由 正点原子运营 于 2021-11-16 11:40 编辑

1)实验平台:正点原子领航者V2FPGA开发板
2)  章节摘自【正点原子】《领航者ZYNQ之FPGA开发指南 V2.0》
3)购买链接:https://detail.tmall.com/item.htm?id=609032204975
4)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/boards/fpga/zdyz_linhanz(V2).html
5)正点原子官方B站:https://space.bilibili.com/394620890
6)正点原子FPGA技术交流QQ群:712557122 QQ群.png

原子哥.jpg

微信公众号.png


第二十章 LCD触摸屏实验


触摸屏(Touch Panel)又称为触控屏、触控面板,是一种可接收触头等输入讯号的感应式液晶显示装置,当接触屏幕时,屏幕上的触觉反馈系统可根据预先编程的程式驱动各种连结装置,可用以取代机械式的按钮面板,并借由液晶显示画面制造出生动的影音效果。本节LCD触摸实验将实现手指触碰LCD屏幕,对应触摸点的坐标就会显示在LCD屏幕上的功能。
本章包括以下几个部分:
2020.1简介
20.2实验任务
20.3硬件设计
20.4程序设计
20.5下载验证


20.1简介
目前最常用的触摸屏有两种:电阻式触摸屏与电容式触摸屏。下面,我们来分别介绍这两种或触摸屏。
1)电阻式触摸屏
在Iphone面世之前,几乎清一色的都是使用电阻式触摸屏,电阻式触摸屏利用压力感应进行触点检测控制,需要直接应力接触,通过检测电阻来定位触摸位置。
正点原子2.4/2.8/3.5寸LCD模块自带的触摸屏都属于电阻式触摸屏,下面简单介绍下电阻式触摸屏的原理。
电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、在它们之间有许多细小的(小于1/1000英寸)的透明隔离点把两层导电层隔开绝缘。当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在X和Y两个方向上产生信号,然后送达触摸屏控制器。控制器侦测到这一接触并计算出(X,Y)的位置,再根据获得的位置模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。
电阻触摸屏的优点:精度高、价格便宜、抗干扰能力强、稳定性好。
电阻触摸屏的缺点:容易被划伤、透光性不太好、不支持多点触摸。
从以上介绍可知,触摸屏都需要一个AD转换器,一般来说是需要一个控制器的。正点原子LCD模块选择的是四线电阻式触摸屏,这种触摸屏的控制芯片有很多,包括:ADS7843、ADS7846、TSC2046、XPT2046和AK4182等。这几款芯片的驱动基本上是一样的,也就是你只要写出了ADS7843的驱动,这个驱动对其他几个芯片也是有效的,而且封装也有一样的,完全PIN TO PIN兼容。所以在替换起来,很方便。
正点原子LCD模块自带的触摸屏控制芯片为XPT2046。XPT2046是一款4导线制触摸屏控制器,内含12位分辨率125KHz转换速率逐步逼近型A/D转换器。XPT2046支持从1.5V到5.25V的低电压I/O接口。XPT2046能通过执行两次A/D转换查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带2.5V参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0V到6V。XPT2046片内集成有一个温度传感器。在2.7V的典型工作状态下,关闭参考电压,功耗可小于0.75mW。XPT2046采用微小的封装形式:TSSOP-16,QFN-16(0.75mm厚度)和VFBGA-48。工作温度范围为-40℃~+85℃。
该芯片完全是兼容ADS7843和ADS7846的,关于这个芯片的详细使用,可以参考这两个芯片的datasheet。
电阻式触摸屏就介绍到这里。
2)电容式触摸屏
现在几乎所有智能手机,包括平板电脑都是采用电容屏作为触摸屏,电容屏是利用人体感应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标。
正点原子4.3/7/10.1寸LCD模块自带的触摸屏采用的是电容式触摸屏,下面简单介绍下电容式触摸屏的原理。
电容式触摸屏主要分为两种:
1、表面电容式电容触摸屏。
表面电容式触摸屏技术是利用ITO(铟锡氧化物,一种透明的导电材料)导电膜,通过电场感应方式感测屏幕表面的触摸行为进行。但是表面电容式触摸屏有一些局限性,它只能识别一个手指或者一次触摸。
2、投射式电容触摸屏。
投射电容式触摸屏是传感器利用触摸屏电极发射出静电场线。一般用于投射电容传感技术的电容类型有两种:自我电容和交互电容。
自我电容又称绝对电容,是最广为采用的一种方法,自我电容通常是指扫描电极与地构成的电容。在
玻璃表面有用ITO制成的横向与纵向的扫描电极,这些电极和地之间就构成一个电容的两极。当用手或触摸笔触摸的时候就会并联一个电容到电路中去,从而使在该条扫描线上的总体的电容量有所改变。在扫描的时候,控制IC依次扫描纵向和横向电极,并根据扫描前后的电容变化来确定触摸点坐标位置。笔记本电脑触摸输入板就是采用的这种方式,笔记本电脑的输入板采用X*Y的传感电极阵列形成一个传感格子,当手指靠近触摸输入板时,在手指和传感电极之间产生一个小量电荷。采用特定的运算法则处理来自行、列传感器的信号,以此确定手指的位置。
交互电容又叫做跨越电容,它是在玻璃表面的横向和纵向的ITO电极的交叉处形成电容。交互电容的扫描方式就是扫描每个交叉处的电容变化,来判定触摸点的位置。当触摸的时候就会影响到相邻电极的耦合,从而改变交叉处的电容量,交互电容的扫面方法可以侦测到每个交叉点的电容值和触摸后电容变化,因而它需要的扫描时间与自我电容的扫描方式相比要长一些,需要扫描检测X*Y根电极。目前智能手机/平板电脑等的触摸屏,都是采用交互电容技术。
正点原子所选择的电容触摸屏,采用的是投射式电容屏(交互电容类型),所以后面仅以投射式电容屏作为介绍。
透射式电容触摸屏采用纵横两列电极组成感应矩阵来感应触摸。以两个交叉的电极矩阵(X轴电极和Y轴电极)来检测每一格感应单元的电容变化,如下图所示:
第二十章 LCD触摸屏实验2434.png
图 7.5.13.1 投射式电容屏电极矩阵示意图
示意图中的电极,实际是透明的,这里是为了方便大家理解故填充了颜色。图中,X、Y轴的透明电极电容屏的精度、分辨率与X、Y轴的通道数有关,通道数越多,精度越高。以上就是电容触摸屏的基本原理,接下来看看电容触摸屏的优缺点:
电容触摸屏的优点:手感好、无需校准、支持多点触摸、透光性好。
电容触摸屏的缺点:成本高、精度不高、抗干扰能力差。
这里特别提醒大家电容触摸屏对工作环境的要求是比较高的,在潮湿、多尘、高低温环境下面,都是不适合使用电容屏的。
电容触摸屏一般都需要一个驱动IC来检测电容触摸,且一般是通过IIC接口输出触摸数据的。正点原子7’LCD模块的电容触摸屏,使用FT5206/FT5426做为驱动IC,该IC采用的是15*28的驱动结构(15个感应通道,28个驱动通道)。正点原子4.3’LCD模块则使用GT9147作为驱动IC,该IC采用17*10的驱动结构(10个感应通道,17个驱动通道)。
这两种不同尺寸的屏幕都只支持最多5点触摸,本例程除CPLD方案的V1版本7寸屏模块不支持外,其他所有正点原子的RGB LCD模块都支持,电容触摸驱动IC,这里只介绍GT9147的驱动,FT5206和FT5426的驱动同GT9147类似,大家可以参考着学习即可。
下面我们简单介绍下GT9147,该芯片是深圳汇顶科技研发的一颗电容触摸屏驱动IC,支持100Hz触点扫描频率,支持5点触摸,支持18*10个检测通道,适合小于4.5寸的电容触摸屏使用。
GT9147与FPGA连接是通过4根线:SDA、SCL、RST和INT。其中:SDA和SCL是IIC通信用的,RST是复位脚(低电平有效),INT是中断输出信号。
GT9147的IIC地址,可以是0X14或者0X5D,当复位结束后的5ms内,如果INT是高电平,则使用0X14作为地址,否则使用0X5D作为地址,具体的设置过程请参考GT9147数据手册.pdf这个文档。本章我们使用0X14作为器件地址(不含最低位,换算成读写命令则是读:0X29,写:0X28)。接下来,介绍一下GT9147的几个重要的寄存器。
1,控制命令寄存器(0X8040)
该寄存器可以写入不同值,实现不同的控制,我们一般使用0和2这两个值,写入2,即可软复位GT9147,在硬复位之后,一般要往该寄存器写2,实行软复位。然后,写入0,即可正常读取坐标数据(并且会结束软复位)。
2,配置寄存器组(0X8047~0X8100)
这里共186个寄存器,用于配置GT9147的各个参数,这些配置一般由厂家提供给我们(一个数组),所以我们只需要将厂家给我们的配置,写入到这些寄存器里面,即可完成GT9147的配置。由于GT9147可以保存配置信息(可写入内部FLASH,从而不需要每次上电都更新配置),这里有几点注意的地方提醒大家:1,0X8047寄存器用于指示配置文件版本号,程序写入的版本号,必须大于等于GT9147本地保存的版本号,才可以更新配置。2,0X80FF寄存器用于存储校验和,使得0X8047~0X80FF之间所有数据之和为0。3,0X8100用于控制是否将配置保存在本地,写0,则不保存配置,写1则保存配置。
3,产品ID寄存器(0X8140~0X8143)
这里总共由 4 个寄存器组成,用于保存产品 ID,对于GT9147,这4个寄存器读出来就是:9,1,4,7四个字符(ASCII码格式)。因此,我们可以通过这4个寄存器的值,来判断驱动IC的型号,从而判断是GT9147还是FT5206,以便执行不同的初始化。
4,状态寄存器(0X814E)
该寄存器各位描述如下表所示:
表 20.1.1 寄存器定义
寄存器        bit7        bit6        bit5        bit4        bit3        bit2        bit1        bit0
0X814E        buffer状态        大点        接近有效        按键        有效触点个数
这里,我们仅关心最高位和最低4位,最高位用于表示buffer状态,如果有数据(坐标/按键),buffer就会是1,最低4位用于表示有效触点的个数,范围是:0~5,0表示没有触摸,5表示有5点触摸。最后,该寄存器在每次读取后,如果bit7有效,则必须写0,清除这个位,否则不会输出下一次数据!!这个要特别注意!!!
5,坐标数据寄存器(共 30 个)
这里共分成5组(5个点),每组6个寄存器存储数据,以触点1的坐标数据寄存器组为例,如下表所示:
表 7.5.13.2 触点 1 坐标寄存器组描述
寄存器        bit7~0        寄存器        bit7~0
0X8150        触点1 x坐标低8位        0X8150        触点1 x坐标低8位
0X8150        触点1 x坐标低8位        0X8150        触点1 x坐标低8位
0X8150        触点1触摸尺寸低8位        0X8150        触点1触摸尺寸高8位
我们一般只用到触点的x,y坐标,所以只需要读取0X8150~0X8153的数据,组合即可得到触点坐标。其他4组分别是:0X8158、0X8160、0X8168和0X8170等开头的16个寄存器组成,分别针对触点2~4的坐标。GT9147支持寄存器地址自增,我们只需要发送寄存器组的首地址,然后连续读取即可,GT9147会自动地址自增,从而提高读取速度。
GT9147相关寄存器的介绍就介绍到这里,更详细的资料,请参考:GT9147编程指南.pdf这个文档。
GT9147只需要经过简单的初始化就可以正常使用了,初始化流程:硬复位→延时10ms→结束硬复位→设置IIC地址→延时100ms→软复位→更新配置(需要时)→结束软复位。此时GT9147即可正常使用了。
然后,我们不停的查询0X814E寄存器,判断是否有有效触点,如果有,则读取坐标数据寄存器,得到触点坐标,特别注意,如果0X814E读到的值最高位为1,就必须对该位写0,否则无法读到下一次坐标数据。
特别说明:FT5206和FT5426的驱动代码完全一模一样,他们只是版本号读取的时候稍有差异,读坐标数据和配置等操作动完全是一模一样的。所以,这两个电容屏驱动IC,可以共用一个驱动程序。电容式触摸屏部分,就介绍到这里。
20.2实验任务
本节的实验任务是使用领航者开发板驱动LCD显示屏,用手触摸显示屏,在屏幕上显示触摸点的坐标。
20.3硬件设计
领航者板载的LCD接口原理图如图 7.5.13.1所示。
第二十章 LCD触摸屏实验5244.png
图 7.5.13.1 LCD接口原理图
上图中的关于LCD显示部分的引脚就不再介绍了,这里我们主要看下CT_RST、IIC2_SDA、IIC2_SCL、CT_INT四个引脚,这四个引脚分别连接到了GT9147的RST、SDA、SCL和INT四根引脚,我们在代码中通过控制这四个引脚来初始化GT9147芯片或者和GT9147进行数据交互。
本实验中,各端口信号的管脚分配(由于引脚比较多,这里只给出了GT9147的控制引脚,详细引脚请参考例程提供的XDC文件)如下表所示:
表 20.3.1 触摸显示实验管脚分配
信号名        方向        管脚        端口说明        电平标准
touch_scl        output        R19        IIC通信引脚        LVCMOS33
touch_sda        inout        P20        IIC通信引脚        LVCMOS33
touch_int        inout        U19        中断引脚        LVCMOS33
touch_rst        output        M19        WM8960的数据线        LVCMOS33
对应的约束语句(GT9147的引脚约束语句)如下所示:
  1. set_property -dict {PACKAGE_PIN R19 IOSTANDARD LVCMOS33} [get_ports touch_scl]
  2. set_property -dict {PACKAGE_PIN P20 IOSTANDARD LVCMOS33} [get_ports touch_sda]
  3. set_property -dict {PACKAGE_PIN U19 IOSTANDARD LVCMOS33} [get_ports touch_int]
  4. set_property -dict {PACKAGE_PIN M19 IOSTANDARD LVCMOS33} [get_ports touch_rst]
复制代码


20.4程序设计
根据实验任务我们画出了如下的程序框图:
第二十章 LCD触摸屏实验6132.png
图 7.5.13.1 LCD触摸实验程序框图
从上图的程序框架中可以看出本次实验的软件工程主要分成两个大模块:一个是GT9147配置模块(本节实验文档是以GT9147为例的,其他触摸芯片的配置和GT9147基本雷同),这个模块当中包含了IIC通信协议模块,触摸控制模块和信号切换模块,其主要作用就是配置触摸芯片,并和触摸芯片进行数据交互;另一个模块就是LCD显示模块了,这个模块大家应该很熟悉了,在前面LCD显示相关的实验中已经和大家讲解过了,在此就不再重复赘述了。下面我们来一起分析一下本节实验的代码。
本次LCD触摸实验的代码可以分成三个大模块:顶层例化模块(LCD_Touch_top)、触摸配置顶层模块(top_touch)、LCD屏幕显示顶层模块(lcd_rgb_char)。
其中顶层例化模块(LCD_Touch_top)模块代码如下:
  1. 1  module LCD_Touch_top(
  2. 2  
  3. 3     //时钟和复位接口
  4. 4      input                clk_50m,     //晶振时钟
  5. 5      input                rst_n,       //按键复位
  6. 6     //SDRAM 接口
  7. 7      inout                touch_sda,
  8. 8      output               touch_scl,
  9. 9      inout                touch_int,
  10. 10     output               touch_rst,   
  11. 11      //RGB LCD接口
  12. 12     output               lcd_de,      //LCD 数据使能信号
  13. 13     output               lcd_hs,      //LCD 行同步信号
  14. 14     output               lcd_vs,      //LCD 场同步信号
  15. 15     output               lcd_bl,      //LCD 背光控制信号
  16. 16     output               lcd_rst_n,
  17. 17     output               lcd_clk,     //LCD 像素时钟
  18. 18     inout        [23:0]  lcd_rgb      //LCD RGB888颜色数据
  19. 19 );
  20. 20
  21. 21 //wire define
  22. 22 wire        clk_100m        ;
  23. 23 wire        clk_50m_pll     ;
  24. 24 wire        locked          ;
  25. 25 wire        sys_rst_n       ;
  26. 26 wire        touch_valid     ;
  27. 27 wire [15:0] lcd_id          ;
  28. 28 wire [31:0] tp1_xy          ;   
  29. 29 wire [31:0] data            ;
  30. 30 wire        tft_sda_i       ;
  31. 31 wire        tft_sda_o       ;
  32. 32 wire        tft_sda_t       ;
  33. 33 //*****************************************************
  34. 34 //**                    main code
  35. 35 //*****************************************************
  36. 36
  37. 37 assign sys_rst_n = rst_n & locked;
  38. 38 assign data = {tp1_xy[31:16],tp1_xy[15:0]};
  39. 39 assign lcd_rst_n = 1'b1;
  40. 40 assign touch_sda = tft_sda_t  ?  tft_sda_o  :  1'bz;
  41. 41 assign tft_sda_i = touch_sda;
  42. 42
  43. 43 //例化锁相环模块
  44. 44  clk_wiz_0 instance_name
  45. 45    (
  46. 46     // Clock out ports
  47. 47     .clk_out1(clk_50m_pll), // output clk_out1
  48. 48     .clk_out2(clk_100m),    // output clk_out2
  49. 49     // Status and control signals
  50. 50     .reset(!rst_n),         // input reset
  51. 51     .locked(locked),        // output locked
  52. 52    // Clock in ports
  53. 53     .clk_in1(clk_50m));     // input clk_in1
  54. 54
  55. 55 //触摸驱动
  56. 56 top_touch u_top_touch(
  57. 57     .sys_clk                            (clk_100m),
  58. 58     .sys_rst_n                          (sys_rst_n),      
  59. 59     .lcd_init_done                      (1),      
  60. 60     .lcd_id                             (lcd_id),               //LCD ID
  61. 61     
  62. 62    .tft_sda_i                           (tft_sda_i),
  63. 63    .tft_sda_o                           (tft_sda_o),
  64. 64    .tft_sda_t                           (tft_sda_t),
  65. 65    .tft_scl                             (touch_scl),
  66. 66    .tft_tcs                             (touch_rst),
  67. 67    .tp1_xy                              (tp1_xy),
  68. 68    .touch_valid                         (touch_valid),              
  69. 69    .tp_num                              ()
  70. 70     );
  71. 71
  72. 72 //例化LCD显示模块
  73. 73 lcd_rgb_char  u_lcd_rgb_char
  74. 74 (
  75. 75    .sys_clk                             (clk_50m_pll),
  76. 76    .sys_rst_n                           (sys_rst_n),
  77. 77    .data                                (data),
  78. 78    //RGB LCD接口
  79. 79    .lcd_id                              (lcd_id),
  80. 80    .lcd_hs                              (lcd_hs),       //LCD 行同步信号
  81. 81    .lcd_vs                              (lcd_vs),       //LCD 场同步信号
  82. 82    .lcd_de                              (lcd_de),       //LCD 数据输入使能
  83. 83    .lcd_rgb                             (lcd_rgb),      //LCD RGB888颜色数据
  84. 84    .lcd_bl                              (lcd_bl),       //LCD 背光控制信号
  85. 85    .lcd_clk                             (lcd_clk)       //LCD 采样时钟
  86. 86 );  
  87. 87
  88. 88 endmodule
复制代码


顶层代码比较简单主要就是例化锁相环(clk_wiz_0)模块、触摸配置顶层模块(top_touch)和LCD屏幕显示顶层模块(lcd_rgb_char),需要注意的一点就是代码第40和41行做了一个双向的判断,因为IIC协议不仅仅是主机(FPGA)对从机(GT9147)写数据,还要接收从机反馈的数据,因此touch_sda信号什么时候作为输入什么时候作为输出必须给出一个判断条件(tft_sda_t)来判断,这个判断条件是由IIC驱动模块(i2c_dri_m)发出的;还有一个GT9147自身中断控制信号(touch_int)也是双向信号,它是在触摸控制模块做的处理。
接下来我们继续分析顶层模块(LCD_Touch_top)所例化的子模块代码,首先看一下锁相环(clk_wiz_0)模块,它主要是用来生成一路50M时钟(clk_50m_pll)和一路100M(clk_100m)时钟。可能有人会疑惑系统时钟不就是50M时钟,为什么还要重新生成一次50M时钟?其实将系统时钟过一遍锁相环是常用的一种消除时钟抖动、扭斜的方法,并且锁相环还输出了一个locked信号,这个信号拉高代表时钟稳定,此时再去执行逻辑语句会使整个工程的时序更加稳定。
看完锁相环(clk_wiz_0)模块后我们继续分析触摸配置顶层模块(top_touch),顾名思义配置顶层模块是整个触摸操作的顶层模块,它同样例化了2个子模块,分别是寄存器配置模块(touch_gt_cfg)和触摸控制模块(touch_ctrl),下面给出触摸配置顶层模块(top_touch)的代码:
  1. 1   module top_touch(
  2. 2       //module clock
  3. 3       input                  sys_clk,          // 系统时钟信号
  4. 4       input                  sys_rst_n,        // 复位信号(低有效)
  5. 5   
  6. 6       //tft interface
  7. 7      // inout                  tft_sda,
  8. 8       input                  tft_sda_i,
  9. 9       output                 tft_sda_o,
  10. 10      output                 tft_sda_t,
  11. 11      output                 tft_scl,
  12. 12      inout                  tft_int,
  13. 13      output   reg           tft_tcs,
  14. 14  
  15. 15      //touch lcd interface   
  16. 16      output   reg           touch_valid,      // 连续触摸标志
  17. 17      output        [ 2:0]    tp_num,
  18. 18      
  19. 19      input                  lcd_init_done,
  20. 20      input         [15:0]   lcd_id,
  21. 21      output  reg   [31:0]   tp1_xy
  22. 22  );
  23. 23  
  24. 24  //parameter define
  25. 25  parameter   WIDTH = 5'd8;
  26. 26  //reg define
  27. 27  reg                       bigger_en ;        //电容屏使能信号
  28. 28  //wire define
  29. 29  wire                      sda_out   ;
  30. 30  wire                      sda_dir   ;
  31. 31  wire                      ack;
  32. 32  wire                      i2c_exec  ;
  33. 33  wire                      i2c_rh_wl ;
  34. 34  wire    [15:0]            i2c_addr  ;
  35. 35  wire    [ 7:0]            i2c_data_w;
  36. 36  wire    [WIDTH-1'b1:0]    reg_num   ;
  37. 37  wire    [ 7:0]            i2c_data_r;
  38. 38  wire                      i2c_done  ;
  39. 39  wire                      once_done ;
  40. 40  wire                      bit_ctrl  ;
  41. 41  wire                      clk       ;
  42. 42  wire                      cfg_done  ;
  43. 43  wire                      cfg_switch;
  44. 44  wire                      gf_cs     ;
  45. 45  wire                      gf_done   ;
  46. 46  wire                      gf_valid  ;
  47. 47  wire    [31:0]            gf_xy     ;
  48. 48  
  49. 49  //*****************************************************
  50. 50  //**                    main code
  51. 51  //*****************************************************
  52. 52  
  53. 53  assign tft_sda_o=sda_out;
  54. 54  assign tft_sda_t=sda_dir;
  55. 55  
  56. 56  always @(*) begin
  57. 57      if(!lcd_init_done) begin     
  58. 58          bigger_en = 1'b0;
  59. 59      end   
  60. 60          else begin           
  61. 61              bigger_en   = 1'b1;
  62. 62              tft_tcs     = gf_cs;         
  63. 63              touch_valid = gf_valid;
  64. 64              tp1_xy      = gf_xy;
  65. 65          end
  66. 66      end
  67. 67  
  68. 68  touch_gt_cfg #(.WIDTH(4'd8)) u_touch_gt_cfg(
  69. 69      //module clock
  70. 70      .clk                (sys_clk   ),         // 时钟信号
  71. 71      .rst_n              (sys_rst_n ),         // 复位信号
  72. 72      //port interface
  73. 73      .scl                (tft_scl    ),          // 时钟线scl
  74. 74      .sda_in             (tft_sda_i ),          // 数据线sda
  75. 75      .sda_out            (sda_out),
  76. 76      .sda_dir            (sda_dir),  
  77. 77      //I2C interface
  78. 78      .ack                (ack       ),
  79. 79      .i2c_exec           (i2c_exec  ),          // i2c触发控制
  80. 80      .i2c_rh_wl          (i2c_rh_wl ),          // i2c读写控制
  81. 81      .i2c_addr           (i2c_addr  ),          // i2c操作地址
  82. 82      .i2c_data_w         (i2c_data_w),          // i2c写入的数据
  83. 83      .reg_num            (reg_num   ),
  84. 84      .i2c_data_r         (i2c_data_r),          // i2c读出的数据
  85. 85      .i2c_done           (i2c_done  ),          // i2c操作结束标志
  86. 86      .once_done          (once_done ),          // 一次读写操作完成
  87. 87      .bit_ctrl           (bit_ctrl  ),
  88. 88      .clk_i2c            (clk       ),          // I2C操作时钟
  89. 89      .cfg_done           (cfg_done  ),          // 寄存器配置完成标志
  90. 90      //user interfacd
  91. 91      .cfg_switch         (cfg_switch),
  92. 92      .lcd_id             (lcd_id    )           //LCD ID
  93. 93  );
  94. 94  
  95. 95  touch_ctrl
  96. 96      #(.WIDTH(4'd8))                           // 一次读写寄存器的个数的位宽
  97. 97  u_touch_ctrl(
  98. 98      //module clock
  99. 99      .sys_clk            (sys_clk  ),
  100. 100     .clk                (clk      ),           // 时钟信号
  101. 101     .rst_n              (sys_rst_n),           // 复位信号(低有效)
  102. 102     .cfg_done           (cfg_done ),            // 配置完成标志
  103. 103     .tft_tcs            (gf_cs    ),
  104. 104     .tft_int            (tft_int  ),
  105. 105
  106. 106     //I2C interface
  107. 107     .ack                (ack       ),
  108. 108     .i2c_exec           (i2c_exec  ),          // i2c触发控制
  109. 109     .i2c_rh_wl          (i2c_rh_wl ),          // i2c读写控制
  110. 110     .i2c_addr           (i2c_addr  ),          // i2c操作地址
  111. 111     .i2c_data_w         (i2c_data_w),          // i2c写入的数据
  112. 112     .i2c_data_r         (i2c_data_r),          // i2c读出的数据
  113. 113     .once_done          (once_done ),          // 一次读写操作完成
  114. 114     .i2c_done           (i2c_done  ),          // i2c操作结束标志
  115. 115     .bit_ctrl           (bit_ctrl  ),
  116. 116     .reg_num            (reg_num   ),          // 一次读写寄存器的个数
  117. 117
  118. 118     //touch lcd interface   
  119. 119     .touch_valid        (gf_valid),
  120. 120     .tp_num             (tp_num),
  121. 121     .tp1_xy             (gf_xy),
  122. 122     .tp2_xy             (),
  123. 123     .tp3_xy             (),
  124. 124     .tp4_xy             (),
  125. 125     .tp5_xy             (),
  126. 126
  127. 127     //user interface
  128. 128     .cfg_switch         (cfg_switch),
  129. 129     .lcd_id             (lcd_id    ),      //LCD ID
  130. 130     .bigger_en          (bigger_en )
  131. 131 );
  132. 132
  133. 133 endmodule
复制代码


触摸配置顶层模块(top_touch)的代码很容易理解,它就是单纯的作为一个顶层模块去例化寄存器配置模块(touch_gt_cfg)和触摸控制模块(touch_ctrl),下面我们来重点分析这两个子模块。
首先给出触摸控制模块(touch_ctrl)的代码(由于这个模块的代码太长我们一段一段的分析):
  1. 87  assign tft_int = int_dir ? int_out : 1'bz;
  2. 88  
  3. 89  always @(*) begin
  4. 90      if(lcd_id[15:8] == 8'h70 ) begin    // 7寸屏的FT系列触摸芯片
  5. 91              bit_ctrl     = 1'b0 ;   
  6. 92              CTRL_REG     = 8'h00;        // 控制寄存器地址
  7. 93              GTCH_REG     = 8'h02;        // 检测到的当前触摸情况
  8. 94              TP1_REG      = 8'h03;        // 第一个触摸点数据地址
  9. 95              TP2_REG      = 8'h09;        // 第二个触摸点数据地址
  10. 96              TP3_REG      = 8'h0f;        // 第三个触摸点数据地址
  11. 97              TP4_REG      = 8'h15;        // 第四个触摸点数据地址
  12. 98              TP5_REG      = 8'h1b;        // 第五个触摸点数据地址
  13. 99      end
  14. 100     else begin
  15. 101             bit_ctrl     = 1'b1    ;
  16. 102             CTRL_REG     = 16'h8040;     // 控制寄存器地址
  17. 103             GTCH_REG     = 16'h814e;     // 检测到的当前触摸情况
  18. 104             TP1_REG      = 16'h8150;     // 第一个触摸点数据地址
  19. 105             TP2_REG      = 16'h8158;     // 第二个触摸点数据地址
  20. 106             TP3_REG      = 16'h8160;     // 第三个触摸点数据地址
  21. 107             TP4_REG      = 16'h8168;     // 第四个触摸点数据地址
  22. 108             TP5_REG      = 16'h8170;     // 第五个触摸点数据地址
  23. 109     end
  24. 110 end
  25. 111
  26. 112 //计时控制
  27. 113 always @(posedge clk or negedge rst_n) begin
  28. 114     if(!rst_n) begin
  29. 115        cnt_1us_cnt <= 20'd0;
  30. 116     end
  31. 117     else if(cnt_1us_en)
  32. 118       cnt_1us_cnt <= cnt_1us_cnt + 1'b1;
  33. 119     else
  34. 120       cnt_1us_cnt <= 'd0;
  35. 121 end
  36. 122
  37. 123 //状态跳转
  38. 124 always [url=home.php?mod=space&uid=95564]@[/url] (posedge clk or negedge rst_n) begin
  39. 125     if(!rst_n)
  40. 126         cur_state <= init;
  41. 127     else
  42. 128         cur_state <= next_state;
  43. 129 end
  44. 130
  45. 131 //组合逻辑状态判断转换条件
  46. 132 always @( * ) begin
  47. 133     case(cur_state)
  48. 134         init: begin
  49. 135             if(st_done)
  50. 136                      if(lcd_id == 16'h4384 || lcd_id == 16'h4342)
  51. 137                          next_state = chk_touch;
  52. 138                      else
  53. 139                         next_state = cfg_state;  
  54. 140             else
  55. 141                 next_state = init;
  56. 142         end
  57. 143
  58. 144         cfg_state: begin
  59. 145             if(st_done) begin
  60. 146                 next_state = chk_touch;
  61. 147             end
  62. 148             else
  63. 149                 next_state = cfg_state;
  64. 150         end
  65. 151         chk_touch: begin
  66. 152             if(st_done)
  67. 153                 next_state = change_addr;
  68. 154             else
  69. 155                 next_state = chk_touch;
  70. 156         end
  71. 157         change_addr: begin
  72. 158             if(st_done)
  73. 159                 next_state = getpos_xy;
  74. 160             else
  75. 161                 next_state = change_addr;
  76. 162         end
  77. 163         getpos_xy: begin
  78. 164             if(st_done)
  79. 165                 next_state = id_handle;
  80. 166             else
  81. 167                 next_state = getpos_xy;
  82. 168         end
  83. 169         id_handle: begin
  84. 170             if(st_done)
  85. 171                 next_state = tp_xy;
  86. 172             else
  87. 173                 next_state = id_handle;
  88. 174         end
  89. 175         tp_xy: begin
  90. 176             if(st_done) begin
  91. 177                 if(tp_num_t == tp_num)
  92. 178                     next_state = chk_touch;
  93. 179                 else
  94. 180                     next_state = change_addr;
  95. 181             end
  96. 182             else
  97. 183                 next_state = tp_xy;
  98. 184         end
  99. 185         default: next_state = init;
  100. 186     endcase
  101. 187 end
复制代码


因为这个模块的代码特别长,所以乍一看可能觉得特别难,但是你只要把这个模块的内容理顺了你就会发现这个模块的代码并不是很难,它是由一个LCD显示屏ID判断模块、一个计数器和一个经典三段状态机组成。代码的第89~110行就是LCD显示屏ID判断模块,根据不同的屏幕ID来判断触摸芯片是七寸屏的FT系列触摸芯片还是4.3寸屏的GT系列触摸芯片(10.1寸屏也是GT系列),不同系列触摸芯片的配置寄存器地址不同(具体寄存器地址可以查找对应芯片的数据手册),同一系列芯片的寄存器地址相同(这里仅仅是指触摸数据的存放地址相同,其它功能性寄存器地址即使是同一系列的也存在差异,具体的要看对应芯片数据手册)。代码的第113~121行是一个计数器,通过计数器使能信号(cnt_1us_en)来控制这个计数器的持续累加还是清零。
接下来的代码就是经典的三段状态机了,其中代码第124~12行是状态机第一段,主要就是时序逻辑下让状态机发生状态跳转,而代码第132~188行则是组合逻辑下判断状态机发生改变的执行条件,整个第二段(三段状态机的第二段)的状态变化条件都有st_done信号,这个信号是状态机每个状态内逻辑执行完成的标志,由状态机第三段控制。我们可以看到整个状态机共有七个状态分别是init(初始化状态)、cfg_state(配置寄存器状态)、chk_touch(触摸检测状态)、change_addr(触摸数据寄存器地址改变状态)、getpos_xy(读取触摸点坐标状态)、id_handle(坐标数据处理状态)和tp_xy(输出触摸点坐标状态)。首先进入初始化状态,在初始化状态会检查外接的显示屏ID,如果是4.3寸屏幕就跳过寄存器配置状态(因为出厂的时候厂家已经将触摸芯片的寄存器配置好了不需要我们再去重复配置,如果大家一定想自己配置寄存器,也可以不跳过配置状态进行寄存器配置,但是前提是一定要将整个工程完全看懂并且熟悉了芯片手册,否则一旦寄存器配置错误LCD显示屏将无法正常工作,即使断电重启也不行,因为配置信息会存储在FLASH中。所以这里提醒读者修改寄存器一定要慎重)进入触摸检测状态,如果是10寸屏或者7寸屏就会进入寄存器配置状态,等待寄存器配置完成再进入触摸检测状态;进入触摸检测状态后等待触摸检测状态的逻辑执行完(st_done拉高)然后进入触摸数据寄存器地址改变状态,同理等待逻辑执行完成后进入下一状态,直到进入最后一个状态(输出触摸点坐标状态),然后开始检测触摸点的个数(除了10.1寸屏幕支持最大10点触摸之外其他屏幕都是支持最大5点触摸),如果触摸点个数没有达到最大个数,则进入触摸数据寄存器地址改变状态,改变寄存器读取地址读出下一个点的坐标;如果达到了最大触摸点个数(注意这里的最大触摸点个数是可以设置的不一定就是10.1寸屏的最大10点触摸或者其他尺寸的最大5点触摸,小于10或者小于5都是可以的)则进入触摸检测状态进行下一轮的触摸点坐标检测,最终状态机会在chk_touch(触摸检测状态)、change_addr(触摸数据寄存器地址改变状态)、getpos_xy(读取触摸点坐标状态)、id_handle(坐标数据处理状态)和tp_xy(输出触摸点坐标状态)之间循环执行,以达到将连续触摸的点都输出出来的功能。
清楚了整个状态机的运作机制后我们再来看看每个状态都执行了哪些逻辑,也就是第三段状态机的内容,第三段状态机代码如下:
  1. 189 always @ (posedge clk or negedge rst_n) begin
  2. 190         if(!rst_n) begin
  3. 191             touch_valid  <=  1'b0;
  4. 192             flow_cnt     <=  4'b0;
  5. 193             st_done      <=  1'b0;
  6. 194             cnt_1us_en   <=  1'b0;
  7. 195             i2c_exec     <=  1'b0;
  8. 196             tft_tcs      <=  1'b0;
  9. 197             int_dir      <=  1'b0;
  10. 198             int_out      <=  1'b0;
  11. 199             i2c_addr     <= 16'b0;
  12. 200             i2c_data_w   <=  8'd0;
  13. 201             i2c_rh_wl    <=  1'd0;
  14. 202             cfg_switch   <=  1'd0;
  15. 203             tp_num       <=  3'b0;
  16. 204             tp_num_t     <=  3'd0;
  17. 205             reg_addr     <= 16'd0;
  18. 206             tp_x         <= 16'd0;
  19. 207             tp_y         <= 16'd0;
  20. 208             tp1_xy       <= 32'd0;
  21. 209             tp2_xy       <= 32'd0;
  22. 210             tp3_xy       <= 32'd0;
  23. 211             tp4_xy       <= 32'd0;
  24. 212             tp5_xy       <= 32'd0;
  25. 213         end
  26. 214         else begin
  27. 215             i2c_exec <= 1'b0;
  28. 216             st_done <= 1'b0;
  29. 217             case(next_state)
  30. 218                 init: begin
  31. 219                     case(flow_cnt)
  32. 220                         'd0: begin
  33. 221                             if(bigger_en) begin
  34. 222                                 flow_cnt <= flow_cnt + 1'b1;
  35. 223                                 int_dir <= 1'b1;
  36. 224                                 int_out <= 1'b1;                                
  37. 225                             end   
  38. 226                         end
  39. 227                         'd1: begin
  40. 228                             cnt_1us_en <= 1'b1;
  41. 229                             if(cnt_1us_cnt <= 20000) begin  // 延时20ms
  42. 230                                 tft_tcs <= 1'b0;
  43. 231                             end
  44. 232                             else begin
  45. 233                                 tft_tcs <= 1'b1;
  46. 234                                 cnt_1us_en <= 1'b0;
  47. 235                                 flow_cnt <= flow_cnt + 1'b1;
  48. 236                             end
  49. 237                         end
  50. 238                         'd2 : begin
  51. 239                             cnt_1us_en <= 1'b1;
  52. 240                             if(cnt_1us_cnt == 10000) begin//延时10ms设置IIC地址
  53. 241                                 int_dir <= 1'b0;
  54. 242                             end   
  55. 243                             else if(cnt_1us_cnt == 100000) begin  //延时100ms
  56. 244                                 cnt_1us_en <= 1'b0;
  57. 245                                 st_done <= 1'b1;
  58. 246                                 int_dir <= 1'b0;
  59. 247                                 flow_cnt <= 'd0;
  60. 248                             end
  61. 249                         end
  62. 250                         default : ;
  63. 251                     endcase
  64. 252                 end
  65. 253
  66. 254                 cfg_state: begin
  67. 255                     st_done <= 1'b0;
  68. 256                     case(flow_cnt)
  69. 257                         'd0 : begin
  70. 258                             i2c_exec <= 1'b1;
  71. 259                             i2c_addr <= CTRL_REG;
  72. 260                             if(lcd_id[15:8] == 8'h70)
  73. 261                                 i2c_data_w<= 8'h00;
  74. 262                             else
  75. 263                                 i2c_data_w<= 8'h02;
  76. 264                             reg_num   <=  'd1;
  77. 265                             i2c_rh_wl <= 1'b0;
  78. 266                             flow_cnt  <= flow_cnt + 1'b1;
  79. 267                         end
  80. 268                         'd1: begin
  81. 269                             if(i2c_done) begin
  82. 270                                 if(ack)
  83. 271                                     flow_cnt <= flow_cnt - 1'b1;
  84. 272                                 else
  85. 273                                     flow_cnt <= flow_cnt + 1'b1;
  86. 274                             end
  87. 275                         end
  88. 276                         'd2 : begin
  89. 277                             i2c_exec <= 1'b1;
  90. 278                             if(lcd_id[15:8] == 8'h70 )
  91. 279                                 i2c_addr <= FT_ID_G_MODE;
  92. 280                             else
  93. 281                                 i2c_addr <= CTRL_REG;                           
  94. 282                             i2c_data_w <= 8'h0;
  95. 283                             i2c_rh_wl <= 1'b0;
  96. 284                             reg_num   <=  'd1;
  97. 285                             flow_cnt <= flow_cnt + 1'b1;
  98. 286                         end
  99. 287                         'd3 : begin
  100. 288                             if(i2c_done) begin
  101. 289                                 if(ack)
  102. 290                                     flow_cnt <= flow_cnt - 1'b1;
  103. 291                                 else
  104. 292                                     flow_cnt <= flow_cnt + 1'b1;
  105. 293                             end
  106. 294                         end
  107. 295                         'd4 : begin
  108. 296                             if(lcd_id[15:8] == 8'h70 )
  109. 297                                 flow_cnt <= 'd7;
  110. 298                             else begin
  111. 299                                 flow_cnt <= flow_cnt + 1'b1;
  112. 300                                 cfg_switch <= 1'b1;
  113. 301                             end
  114. 302                         end                        
  115. 303                         'd5: begin
  116. 304                             if(cfg_done) begin
  117. 305                                 cfg_switch <= 1'b0;
  118. 306                                 flow_cnt <= flow_cnt + 1'b1;
  119. 307                             end
  120. 308                         end
  121. 309                         'd6 : begin
  122. 310                             if(i2c_done) begin
  123. 311                                 st_done <= 1'b1;
  124. 312                                 flow_cnt <= 'd0;
  125. 313                             end
  126. 314                         end
  127. 315                         'd7: begin      //设置触摸有效值
  128. 316                             i2c_exec <= 1'b1;
  129. 317                             i2c_addr <= FT_ID_G_THGROUP;
  130. 318                             i2c_data_w<= 8'd22;
  131. 319                             reg_num   <= 'd1;
  132. 320                             i2c_rh_wl <= 1'b0;
  133. 321                             flow_cnt  <= flow_cnt + 1'b1;
  134. 322                         end
  135. 323                         'd8: begin
  136. 324                             if(i2c_done) begin
  137. 325                                 if(!ack)
  138. 326                                     flow_cnt <= flow_cnt + 1'b1;
  139. 327                                 else
  140. 328                                     flow_cnt <= flow_cnt - 1'b1;
  141. 329                              end
  142. 330                         end
  143. 331                         'd9: begin          //激活周期,不能小于12,最大14
  144. 332                             i2c_exec <= 1'b1;
  145. 333                             i2c_addr <= FT_ID_G_PERIODACTIVE;
  146. 334                             i2c_data_w<= 8'd12;
  147. 335                             reg_num   <= 'd1;
  148. 336                             i2c_rh_wl <= 1'b0;
  149. 337                             flow_cnt  <= flow_cnt + 1'b1;
  150. 338                         end
  151. 339                         'd10: begin
  152. 340                             if(i2c_done) begin
  153. 341                                 flow_cnt <=  'b0;
  154. 342                                 st_done  <= 1'b1;
  155. 343                             end
  156. 344                         end
  157. 345                         default : ;
  158. 346                     endcase
  159. 347                 end
  160. 348                 chk_touch: begin    // 检测触摸状态
  161. 349                 st_done  <= 1'b0;
  162. 350                     case(flow_cnt)
  163. 351                         'd0: begin
  164. 352                             tp_num_t <= 3'd0;
  165. 353                             tp_num   <= 3'd0;
  166. 354                             if(lcd_id[15:8] == 8'h70 )
  167. 355                                 flow_cnt <= flow_cnt + 1'b1;                        
  168. 356                             else if(lcd_id[15:8] == 8'h10)begin
  169. 357                                 cnt_1us_en <= 1'b1;
  170. 358                                 if(cnt_1us_cnt == 50000)
  171. 359                                     flow_cnt <= flow_cnt + 1'b1;
  172. 360                             end
  173. 361                             else begin
  174. 362                                 cnt_1us_en <= 1'b1;
  175. 363                                 if(cnt_1us_cnt == 17000)      
  176. 364                  //模块驱动时钟周期为625ns(频率为I2C时钟的4倍,400Kbps*4)
  177. 365                                     flow_cnt <= flow_cnt + 1'b1;
  178. 366                  //此处每隔625ns*17000采样一次,即LCD每隔10.625ms更新一次坐标
  179. 367                             end
  180. 368                         end
  181. 369                         'd1: begin //'d2
  182. 370                         if(lcd_id==16'h7016||lcd_id==16'h7084)begin
  183. 371                         cnt_1us_en <= 1'b1;
  184. 372                             if(cnt_1us_cnt == 2000) begin
  185. 373                                 cnt_1us_en <= 1'b0;
  186. 374                                 i2c_exec <= 1'b1;
  187. 375                                 i2c_addr <= GTCH_REG;
  188. 376                                 i2c_rh_wl<= 1'b1;
  189. 377                                 reg_num  <=  'd1;
  190. 378                                 flow_cnt <= flow_cnt + 1'b1;   
  191. 379                             end
  192. 380                         end
  193. 381                         else begin                 
  194. 382                        
  195. 383                             cnt_1us_en <= 1'b0;
  196. 384                             i2c_exec <= 1'b1;
  197. 385                             i2c_addr <= GTCH_REG;
  198. 386                             i2c_rh_wl<= 1'b1;
  199. 387                             reg_num  <=  'd1;
  200. 388                             flow_cnt <= flow_cnt + 1'b1;
  201. 389                         end
  202. 390                         end
  203. 391                        
  204. 392                         'd2: begin
  205. 393                             if(i2c_done) begin
  206. 394                                 if(ack)
  207. 395                                     flow_cnt <= flow_cnt - 1'b1;
  208. 396                                 else
  209. 397                                     flow_cnt <= flow_cnt + 1'b1;
  210. 398                              end
  211. 399                         end
  212. 400                         'd3: begin
  213. 401                             if(lcd_id[15:8] == 8'h70 )
  214. 402                                 flow_cnt <= flow_cnt + 1'b1;
  215. 403                             else
  216. 404                                 flow_cnt <= flow_cnt + 2'd2;
  217. 405                         end
  218. 406                         'd4: begin
  219. 407                             if(i2c_data_r != 8'hff && i2c_data_r[3:0] != 4'd0
  220. 408                                 && i2c_data_r[3:0] < 4'd6) begin
  221. 409                                 flow_cnt <= 'd0;
  222. 410                                 st_done  <= 1'b1;
  223. 411                                 touch_valid<= 1'b1;
  224. 412                                 tp_num <= i2c_data_r[2:0];
  225. 413                             end
  226. 414                             else begin
  227. 415                                 touch_valid<= 1'b0;
  228. 416                                 flow_cnt <= 'd1;
  229. 417                             end
  230. 418                         end
  231. 419                         'd5: begin
  232. 420                             if(i2c_data_r[7]== 1'b1 && i2c_data_r[3:0] != 4'd0)
  233. 421                             begin
  234. 422                                 flow_cnt <= flow_cnt + 1'b1;
  235. 423                                 touch_valid<= 1'b1;           //i2c_data_r[4];
  236. 424                                 tp_num   <= i2c_data_r[2:0];
  237. 425                             end
  238. 426                             else begin
  239. 427                                 touch_valid<= 1'b0;           //i2c_data_r[4];
  240. 428                                 flow_cnt <= flow_cnt + 1'b1;
  241. 429                             end
  242. 430                         end
  243. 431                         'd6: begin
  244. 432                             i2c_exec <= 1'b1;
  245. 433                             i2c_addr <= GTCH_REG;
  246. 434                             i2c_rh_wl<= 1'b0;
  247. 435                             i2c_data_w<= 8'd0;
  248. 436                             reg_num  <=  'd1;
  249. 437                             flow_cnt <= flow_cnt + 1'b1;   
  250. 438                         end
  251. 439                         'd7: begin
  252. 440                             if(i2c_done) begin
  253. 441                                 if(ack)
  254. 442                                     flow_cnt <= flow_cnt - 1'b1;
  255. 443                                 else if(tp_num) begin
  256. 444                                     st_done <= 1'b1;
  257. 445                                     flow_cnt <= 'd0;
  258. 446                                 end
  259. 447                                 else
  260. 448                                     flow_cnt <= 'd0;
  261. 449                             end
  262. 450                         end
  263. 451                         default : ;
  264. 452                     endcase
  265. 453                 end // case: touch_statue
  266. 454                 change_addr: begin
  267. 455                 st_done  <= 1'b0;
  268. 456                     if(tp_num_t < tp_num) begin
  269. 457                         case(tp_num_t)
  270. 458                             'd0 : begin
  271. 459                                 reg_addr <= TP1_REG;
  272. 460                                 st_done <= 1'b1;
  273. 461                             end
  274. 462                             'd1 : begin
  275. 463                                 reg_addr <= TP2_REG;
  276. 464                                 st_done <= 1'b1;
  277. 465                             end
  278. 466                             'd2 : begin
  279. 467                                 reg_addr <= TP3_REG;
  280. 468                                 st_done <= 1'b1;
  281. 469                             end
  282. 470                             'd3 : begin
  283. 471                                 reg_addr <= TP4_REG;
  284. 472                                 st_done <= 1'b1;
  285. 473                             end
  286. 474                             'd4 : begin
  287. 475                                 reg_addr <= TP5_REG;
  288. 476                                 st_done <= 1'b1;
  289. 477                             end
  290. 478                             default : ;
  291. 479                         endcase
  292. 480                     end
  293. 481                     else begin
  294. 482                         st_done <= 1'b1;
  295. 483                     end
  296. 484                 end
  297. 485                 getpos_xy: begin
  298. 486                 st_done  <= 1'b0;
  299. 487                     case(flow_cnt)
  300. 488                         'd0 : begin
  301. 489                             i2c_exec <= 1'b1;
  302. 490                             i2c_rh_wl<= 1'b1;
  303. 491                             i2c_addr <= reg_addr;
  304. 492                             reg_num  <= 'd4;
  305. 493                             flow_cnt <= flow_cnt + 1'b1;
  306. 494                         end
  307. 495                         'd1 : begin
  308. 496                             if(ack)
  309. 497                                 flow_cnt <= flow_cnt - 1'b1;
  310. 498                             if(once_done) begin
  311. 499                                 tp_x[7:0] <= i2c_data_r;
  312. 500                                 flow_cnt  <= flow_cnt + 1'b1;
  313. 501                             end
  314. 502                         end
  315. 503                         'd2 : begin
  316. 504                             if(once_done) begin
  317. 505                                 tp_x[15:8] <= i2c_data_r;
  318. 506                                 flow_cnt  <= flow_cnt + 1'b1;
  319. 507                             end
  320. 508                         end
  321. 509                         'd3 : begin
  322. 510                             if(once_done) begin
  323. 511                                 tp_y[7:0] <= i2c_data_r;
  324. 512                                 flow_cnt  <= flow_cnt + 1'b1;
  325. 513                              end
  326. 514                         end
  327. 515                         'd4 : begin
  328. 516                             if(i2c_done) begin
  329. 517                                 tp_y[15:8] <= i2c_data_r;
  330. 518                                 st_done  <= 1'b1;
  331. 519                                 flow_cnt <= 'd0;
  332. 520                             end
  333. 521                         end
  334. 522                         default : ;
  335. 523                     endcase
  336. 524                 end
  337. 525                 id_handle: begin
  338. 526                 st_done  <= 1'b0;
  339. 527                     case(lcd_id)
  340. 528                         16'h4342: begin     // 4.3' RGB  
  341. 529                             tp_x <= tp_x;  
  342. 530                             tp_y <= tp_y;
  343. 531                             st_done  <= 1'b1;
  344. 532                         end
  345. 533                         16'h7084,16'h1963: begin
  346. 534                             tp_x <= {4'd0,tp_y[3:0],tp_y[15:8]};
  347. 535                             tp_y <= {4'd0,tp_x[3:0],tp_x[15:8]};
  348. 536                             st_done  <= 1'b1;        
  349. 537                         end
  350. 538                         16'h7016: begin
  351. 539                             tp_x <={4'd0,tp_y[3:0],tp_y[15:8]};
  352. 540                             tp_y <={4'd0,tp_x[3:0],tp_x[15:8]};
  353. 541                             st_done  <= 1'b1;        
  354. 542                         end
  355. 543                         16'h1018: begin
  356. 544                             tp_x <= tp_x;
  357. 545                             tp_y <= tp_y;                        
  358. 546                             st_done  <= 1'b1;
  359. 547                         end
  360. 548                          16'h4384: begin
  361. 549                          tp_x <=tp_x;
  362. 550                          tp_y <=tp_y;
  363. 551                          st_done  <= 1'b1;                       
  364. 552                          end
  365. 553                          
  366. 554                         default: st_done  <= 1'b1;
  367. 555                      endcase
  368. 556                 end
  369. 557                 tp_xy: begin
  370. 558                 st_done  <= 1'b0;
  371. 559                     case(tp_num_t)                  
  372. 560                         'd0: begin
  373. 561                             tp1_xy <= {tp_x,tp_y};
  374. 562                             st_done <= 1'b1;
  375. 563                             tp_num_t <= tp_num_t + 1'b1;
  376. 564                         end
  377. 565                         'd1 : begin
  378. 566                             tp2_xy <= {tp_x,tp_y};
  379. 567                             st_done <= 1'b1;
  380. 568                             tp_num_t <= tp_num_t + 1'b1;
  381. 569                         end
  382. 570                         'd2: begin
  383. 571                             tp3_xy <= {tp_x,tp_y};
  384. 572                             st_done <= 1'b1;
  385. 573                             tp_num_t <= tp_num_t + 1'b1;
  386. 574                         end
  387. 575                         'd3: begin
  388. 576                             tp4_xy <= {tp_x,tp_y};
  389. 577                             st_done <= 1'b1;
  390. 578                             tp_num_t <= tp_num_t + 1'b1;
  391. 579                         end
  392. 580                         'd4: begin
  393. 581                             tp5_xy <= {tp_x,tp_y};
  394. 582                             st_done <= 1'b1;
  395. 583                             tp_num_t <= tp_num_t + 1'b1;
  396. 584                         end
  397. 585                         default : ;
  398. 586                     endcase
  399. 587                 end
  400. 588                 default : ;
  401. 589             endcase
  402. 590         end
  403. 591 end
  404. 592
  405. 593 endmodule
复制代码


第三段状态机就比较长了,不过没关系我们一个状态一个状态的分析。首先是初始化状态,此状态执行了一个case语句,它包含了flow_cnt等于0、1、2三个小状态,当flow_cnt等于0的时候要检测bigger_en信号(bigger_en信号就是LCD初始化完成信号,这个接口其实是一个预留接口在顶层中直接赋常值1了),当其为高电平时int_dir(触摸芯片tft_int中断信号的方向判定)和int_out(触摸芯片中断信号的值)输出值为1,接着进入flow_cnt等于1状态进行延迟(延迟主要通过上文提到的计数器来完成),然后对应输出tft_tcs(触摸芯片的复位信号)的值,之后进入flow_cnt等于2状态再次改变int_dir的值(改变触摸芯片tft_int中断信号的方向),同时清空flow_cnt的值(flow_cnt这个寄存器是重复使用的,每个状态逻辑执行完后要对它清零)并拉高st_done信号(让三段状态机的第二段进入下一个状态),至于为什么要延迟并给中断信号和复位信号进行不同的赋值主要是根据芯片手册的时序来的,具体上电时序可以查看芯片数据手册。
初始化状态结束后是寄存器配置状态,进入这个状态同样是执行一个case语句,然后判断外接屏幕是7寸的还是10寸的(因为4.3寸屏幕会跳过这个状态,所以这里只判断是7寸还是10寸),不同尺寸的屏幕配置的寄存器地址与对应写入的值是不相同的。这里尤其要注意flow_cnt等于4的时候如果是10寸屏就会激活cfg_switch控制信号,它一旦被激活就会传递到另外一个信号切换子模块(signal_switch)然后再传递到寄存器配置子模块(i2c_reg_cfg),之后会激活整个寄存器组重新配置,总共会配置188个寄存器,其他尺寸的屏幕其实都没有真正意义上的重新配置寄存器,仅仅只是配置了控制寄存器,并读取存放触摸数据的寄存器的值,只有10寸屏是真正意义上的配置完整寄存器组。如果你想对其他尺寸的屏幕进行整个寄存器组的重配置这里也可以修改一下,比如识别到其他尺寸屏幕ID时也可以仿照10寸屏的写法激活cfg_switch信号,在寄存器配置子模块(i2c_reg_cfg)中我们是存放了三种尺寸屏幕的完整寄存器组的,只要修改了这里的状态机就可以激活所有屏幕的完整寄存器配置(这里提醒读者慎重修改,防止寄存器组配置错误导致屏幕无法正常工作)。
接下来进入触摸检测状态(chk_touch),同样的要检测外接LCD屏的ID,判断是什么尺寸的,不同尺寸的屏幕对应检测的时序不同,主要体现在延迟时间的长短上,然后根据不同尺寸的ID,访问触摸芯片的触摸控制寄存器(GTCH_REG),根据触摸芯片给出的反馈(ack)和应答(i2c_data_r读出的数据)判断触摸是否有效(touch_valid),最大触摸点数是多少(tp_num)。之后进入触摸数据存放寄存器地址改变状态,根据上一状态得到的最大触摸点数改变相应的地址。有了地址之后就可以进入读取触摸数据坐标状态(getpos_xy),在这一状态主要就是通过IIC协议把对应地址里的数据读取出来就行。再之后进入数据处理状态(id_handle),在此状态中根据不同屏幕的尺寸把对应数值做一下处理,主要就是作数据拼接(7寸屏的数据读取出来需要拼接)或者改变x轴y轴的方向(例如ID为4384的屏幕分辨率为800*480,它的x轴坐标本身是从左往右坐标递增,现在假如将第531行代码改写成“tp_x <=799-tp_x;”那么x轴坐标就变成从左往右递减了)。数据处理完后就会进入最后一个状态数据输出状态(tp_xy),在这个状态坐标会被输出,并且每输出一个坐标tp_num_t(已读出的数据个数)计数器就会加一,用来配合触摸数据存放寄存器地址改变状态(change_addr)使用。
到这里整个触摸控制子模块就给大家讲解完了,下面我们继续分析另一个寄存器配置子模块(touch_gt_cfg),其代码如下:
  1. 1   module touch_gt_cfg #(parameter WIDTH = 4'd8)
  2. 2   (
  3. 3       //system clock
  4. 4       input                   clk       ,          // 时钟信号
  5. 5       input                   rst_n     ,          // 复位信号
  6. 6   
  7. 7       //port interface
  8. 8       output                  scl       ,          // 时钟线scl
  9. 9       input                   sda_in    ,          // 数据线sda
  10. 10      output                  sda_out   ,
  11. 11      output                  sda_dir   ,
  12. 12  
  13. 13      //I2C interface
  14. 14      input                   bit_ctrl  ,
  15. 15      input                   i2c_exec  ,          // i2c触发控制
  16. 16      input                   i2c_rh_wl ,          // i2c读写控制
  17. 17      input    [15:0]         i2c_addr  ,          // i2c操作地址
  18. 18      input    [ 7:0]         i2c_data_w,          // i2c写入的数据
  19. 19      input    [WIDTH-1'b1:0] reg_num   ,
  20. 20      input                   cfg_switch,
  21. 21      output   [ 7:0]         i2c_data_r,          // i2c读出的数据
  22. 22      output                  i2c_done  ,          // i2c操作结束标志
  23. 23      output                  once_done ,          // 一次读写操作完成
  24. 24      output                  clk_i2c   ,          // I2C操作时钟
  25. 25      output                  ack       ,
  26. 26      //user interface
  27. 27      output                  cfg_done  ,          // 寄存器配置完成标志
  28. 28      input    [15:0]         lcd_id   
  29. 29  );
  30. 30  
  31. 31  //parameter define
  32. 32  //localparam    SLAVE_ADDR =  7'h5d       ; // 器件地址(SLAVE_ADDR)
  33. 33  //localparam    BIT_CTRL   =  1'b1        ; // 字地址位控制参数(16b/8b)
  34. 34  localparam    CLK_FREQ   = 27'd100_000_000;
  35. 35  //i2c_dri模块的驱动时钟频率(CLK_FREQ)
  36. 36  localparam    I2C_FREQ   = 19'd400_000    ; // I2C的SCL时钟频率
  37. 37  
  38. 38  //wire define
  39. 39  wire                  cfg_i2c_exec  ;                // i2c触发控制
  40. 40  wire                  cfg_i2c_rh_wl ;                // i2c读写控制
  41. 41  wire   [15:0]         cfg_i2c_addr  ;                // i2c操作地址
  42. 42  wire   [ 7:0]         cfg_i2c_data  ;                // i2c写入的数据
  43. 43  wire                  cfg_once_done ;                // i2c操作结束标志
  44. 44  wire   [WIDTH-1'b1:0] cfg_reg_num   ;                // i2c读出的数据
  45. 45  wire                  m_i2c_exec    ;
  46. 46  wire                  m_i2c_rh_wl   ;
  47. 47  wire   [15:0]         m_i2c_addr    ;
  48. 48  wire   [ 7:0]         m_i2c_data_w  ;
  49. 49  wire   [ 7:0]         m_i2c_data_r  ;
  50. 50  wire   [WIDTH-1'b1:0] m_reg_num     ;
  51. 51  wire                  m_once_done   ;
  52. 52  reg    [6:0]          slave_addr    ;
  53. 53   
  54. 54  //*****************************************************
  55. 55  //**                    main code
  56. 56  //*****************************************************
  57. 57  
  58. 58  always @(*) begin
  59. 59      if(lcd_id[15:8] == 8'h70 || lcd_id[15:8]== 8'h19)
  60. 60          slave_addr = 7'h38;
  61. 61      else
  62. 62          slave_addr = 7'h14;
  63. 63  end      
  64. 64  
  65. 65  //信号转换
  66. 66  signal_switch #(.WIDTH(WIDTH)
  67. 67  ) u1_signal_switch(
  68. 68      //module1
  69. 69      .m1_0        (i2c_exec      ),
  70. 70      .m1_1        (i2c_rh_wl     ),
  71. 71      .m1_2        (i2c_addr      ),
  72. 72      .m1_3        (i2c_data_w    ),
  73. 73      .m1_4        (i2c_data_r    ),
  74. 74      .m1_5        (reg_num       ),
  75. 75      .m1_6        (once_done     ),
  76. 76      //module2
  77. 77      .m2_0        (cfg_i2c_exec  ),
  78. 78      .m2_1        (cfg_i2c_rh_wl ),
  79. 79      .m2_2        (cfg_i2c_addr  ),
  80. 80      .m2_3        (cfg_i2c_data  ),
  81. 81      .m2_4        ( ),
  82. 82      .m2_5        (cfg_reg_num   ),
  83. 83      .m2_6        (cfg_once_done ),
  84. 84      //module3
  85. 85      .m3_0        (m_i2c_exec    ),   // i2c触发控制
  86. 86      .m3_1        (m_i2c_rh_wl   ),   // i2c读写控制
  87. 87      .m3_2        (m_i2c_addr    ),   // i2c寄存器地址
  88. 88      .m3_3        (m_i2c_data_w  ),   // i2c写入的数据
  89. 89      .m3_4        (m_i2c_data_r  ),   // i2c读出的数据
  90. 90      .m3_5        (m_reg_num     ),   // 一次读写寄存器的个数
  91. 91      .m3_6        (m_once_done   ),   // 一次读写操作完成
  92. 92      //ctrl signal
  93. 93      .ctrl_switch (cfg_switch    )    // 切换信号
  94. 94  );
  95. 95  
  96. 96  //例化i2c_dri_m
  97. 97  i2c_dri_m #(
  98. 98  //  .SLAVE_ADDR  (SLAVE_ADDR  ), //slave address从机地址,放此处方便参数传递
  99. 99      .CLK_FREQ    (CLK_FREQ    ), //i2c_dri模块的驱动时钟频率(CLK_FREQ)
  100. 100     .I2C_FREQ    (I2C_FREQ    ), //I2C的SCL时钟频率
  101. 101     .WIDTH       (WIDTH       )
  102. 102 ) u_i2c_dri(
  103. 103     //global clock
  104. 104     .clk         (clk         ),    // i2c_dri模块的驱动时钟(CLK_FREQ)
  105. 105     .rst_n       (rst_n       ),    // 复位信号
  106. 106     //i2c interface
  107. 107     .slave_addr  (slave_addr  ),
  108. 108     .i2c_exec    (m_i2c_exec  ),    // I2C触发执行信号
  109. 109     .bit_ctrl    (bit_ctrl    ),    // 器件地址位控制(16b/8b)
  110. 110     .i2c_rh_wl   (m_i2c_rh_wl ),    // I2C读写控制信号
  111. 111     .i2c_addr    (m_i2c_addr  ),    // I2C寄存器地址
  112. 112     .i2c_data_w  (m_i2c_data_w),    // I2C要写的数据
  113. 113     .i2c_data_r  (m_i2c_data_r),    // I2C读出的数据
  114. 114     .i2c_done    (i2c_done    ),    // I2C操作完成
  115. 115     .once_done   (m_once_done ),    // 一次读写操作完成
  116. 116     .scl         (scl         ),    // I2C的SCL时钟信号
  117. 117     .sda_in      (sda_in      ),    // I2C的SDA信号
  118. 118     .sda_out     (sda_out     ),
  119. 119     .sda_dir     (sda_dir     ),  
  120. 120     .ack         (ack         ),
  121. 121     //user interface
  122. 122     .reg_num     (m_reg_num   ),    // 一次读写寄存器的个数
  123. 123     .dri_clk     (clk_i2c     ),     // I2C操作时钟
  124. 124     .lcd_id      (lcd_id      )
  125. 125 );
  126. 126
  127. 127 //例化i2c_reg_cfg模块
  128. 128 i2c_reg_cfg  u_i2c_reg_cfg(
  129. 129     //clock & reset
  130. 130     .clk         (clk_i2c      ),   // i2c_reg_cfg驱动时钟(一般取1MHz)
  131. 131     .rst_n       (rst_n        ),   // 复位信号
  132. 132     //i2c interface
  133. 133     .i2c_exec    (cfg_i2c_exec ),   // I2C触发执行信号
  134. 134     .i2c_rh_wl   (cfg_i2c_rh_wl),   // I2C读写控制信号
  135. 135     .i2c_addr    (cfg_i2c_addr ),   // 寄存器地址
  136. 136     .i2c_data    (cfg_i2c_data ),   // 寄存器数据
  137. 137     .once_done   (cfg_once_done),   // 一次读写操作完成
  138. 138     .cfg_done    (cfg_done     ),   // 配置完成
  139. 139     //user interface
  140. 140     .reg_num     (cfg_reg_num  ),   // 一次读写寄存器的个数
  141. 141     .cfg_switch  (cfg_switch   ),   // 切换信号
  142. 142     .lcd_id      (lcd_id       )   
  143. 143 );
  144. 144
  145. 145 endmodule
复制代码


寄存器配置子模块(touch_gt_cfg)同样例化了三个子模块分别是信号切换模块(signal_switch)、IIC驱动模块(i2c_dri_m)和寄存器组控制模块(i2c_reg_cfg)。
要想理解整个寄存器配置子模块(touch_gt_cfg)是如何工作的就要先理清它所例化的三个子模块之间的关系。首先IIC驱动模块(i2c_dri_m)的功能很好理解,它就是执行IIC协议通信,给出触发信号(i2c_exec)、读写切换信号(i2c_rh_wl)、读写地址(i2c_addr)、写数据(i2c_data_w)它就能和从器件进行数据交互。关键就在这里,这四个控制线由谁发出呢?在上文已经说过了touch_ctrl模块的状态机会执行配置触摸芯片控制寄存器,会访问触摸芯片的触摸检测寄存器和触摸数据存放寄存器,它也是通过IIC协议去进行数据交互的那么它也就必然也会给出相应的i2c_exec、i2c_rh_wl、i2c_addr和i2c_data_w信号。同样的在上文touch_ctrl模块中我也说了对于10寸屏是需要配置整个寄存器组的,而寄存器组是存放在寄存器组控制模块(i2c_reg_cfg)中的,那么i2c_reg_cfg模块必然也要给出i2c_exec、i2c_rh_wl、i2c_addr和i2c_data_w信号,这就会造成冲突。因此我们引入了信号切换模块(signal_switch),寄存器组控制模块(i2c_reg_cfg)和touch_ctrl模块给出的IIC驱动信号都先接入信号切换模块(signal_switch),然后信号切换模块(signal_switch)会判断cfg_switch信号是否被激活,如果激活说明需要对触摸芯片的整个寄存器组进行重配置,那么寄存器组控制模块(i2c_reg_cfg)给出的IIC驱动信号有效,反之touch_ctrl模块给出的IIC驱动信号有效,这样就完美的解决了IIC驱动信号冲突的问题。
当我们了解了信号切换模块(signal_switch)、IIC驱动模块(i2c_dri_m)和寄存器组控制模块(i2c_reg_cfg)三者之间的关系我们再来分析它的代码,先看信号切换模块(signal_switch)的代码:
  1. 1  module signal_switch #(parameter   WIDTH  = 4'd8 //一次读写寄存器的个数的位宽
  2. 2  )(
  3. 3      //module1
  4. 4      input                           m1_0,
  5. 5      input                           m1_1,
  6. 6      input         [15:0]            m1_2,
  7. 7      input         [ 7:0]            m1_3,
  8. 8      output   reg  [ 7:0]            m1_4,
  9. 9      input         [WIDTH-1'b1:0]    m1_5,
  10. 10     output   reg                    m1_6,
  11. 11         
  12. 12     //module2
  13. 13     input                           m2_0,
  14. 14     input                           m2_1,
  15. 15     input         [15:0]            m2_2,
  16. 16     input         [ 7:0]            m2_3,
  17. 17     output   reg  [ 7:0]            m2_4,
  18. 18     input         [WIDTH-1'b1:0]    m2_5,
  19. 19     output   reg                    m2_6,
  20. 20         
  21. 21     //module2
  22. 22     output   reg                    m3_0,
  23. 23     output   reg                    m3_1,
  24. 24     output   reg  [15:0]            m3_2,
  25. 25     output   reg  [ 7:0]            m3_3,
  26. 26     input         [ 7:0]            m3_4,
  27. 27     output   reg  [WIDTH-1'b1:0]    m3_5,
  28. 28     input                           m3_6,
  29. 29     
  30. 30     //ctrl signal
  31. 31     input                       ctrl_switch         // 切换信号
  32. 32     
  33. 33 );
  34. 34
  35. 35 //*****************************************************
  36. 36 //**                    main code
  37. 37 //*****************************************************
  38. 38
  39. 39 //信号转换
  40. 40 always @(*) begin
  41. 41     if(ctrl_switch) begin
  42. 42         m3_0 = m2_0;
  43. 43         m3_1 = m2_1;
  44. 44         m3_2 = m2_2;
  45. 45         m3_3 = m2_3;
  46. 46         m2_4 = m3_4;
  47. 47         m3_5 = m2_5;
  48. 48         m2_6 = m3_6;
  49. 49     end
  50. 50     else begin
  51. 51         m3_0 = m1_0;
  52. 52         m3_1 = m1_1;
  53. 53         m3_2 = m1_2;
  54. 54         m3_3 = m1_3;
  55. 55         m1_4 = m3_4;
  56. 56         m3_5 = m1_5;
  57. 57         m1_6 = m3_6;
  58. 58     end
  59. 59 end
  60. 60
  61. 61 endmodule
复制代码


信号切换模块(signal_switch)的代码比较简单就不讲解了,下面我们直接来看寄存器组控制模块(i2c_reg_cfg)的代码:
  1. 1   module i2c_reg_cfg #(parameter WIDTH = 4'd8
  2. 2   )(
  3. 3       input                        clk       ,  //i2c_reg_cfg驱动时钟(一般取1MHz)
  4. 4       input                        rst_n     ,  // 复位信号
  5. 5       input                        once_done ,  // I2C一次操作完成反馈信号
  6. 6   
  7. 7       output  reg                  i2c_exec  ,  // I2C触发执行信号
  8. 8       output  reg                  i2c_rh_wl ,  // I2C读写控制信号
  9. 9       output  reg  [15:0]          i2c_addr  ,  // 寄存器地址
  10. 10      output  reg  [ 7:0]          i2c_data  ,  // 寄存器数据
  11. 11      output  reg                  cfg_done  ,  // WM8978配置完成
  12. 12  
  13. 13      //user interface
  14. 14      input                        cfg_switch,    // 配置切换
  15. 15      input        [15:0]          lcd_id,
  16. 16      output  reg  [WIDTH-1'b1:0]  reg_num
  17. 17  );
  18. 18  
  19. 19  //parameter define
  20. 20  localparam    MODE       = 8'h1  ;            
  21. 21  // 0X8100用于控制是否将配置保存在本地,写 0,则不保存配置,写 1 则保存配置。
  22. 22  //localparam    REG_NUM_4  = 8'd186;           // 总共需要配置的寄存器个数
  23. 23  //GT9147 部分寄存器定义
  24. 24  localparam    GT_CTRL_REG  = 16'h8040;         // GT系列控制寄存器
  25. 25  localparam    GT_CFGS_REG  = 16'h8047;         // GT系列配置起始地址寄存器
  26. 26  localparam    GT_CHECK_REG = 16'h80FF;         // GT系列校验和寄存器
  27. 27  
  28. 28  //reg define
  29. 29  reg    [2:0]  start_init_cnt;                  // 初始化时间计数
  30. 30  reg    [7:0]  init_reg_cnt  ;                  // 寄存器配置个数计数器
  31. 31  reg    [7:0]  sum_t1;                          // 计算校验和
  32. 32  reg    [7:0]  REG_NUM;                         // 总共需要配置的寄存器个数
  33. 33  
  34. 34  //wire define
  35. 35  wire          rd_en ;
  36. 36  wire   [7:0]  sum_t2;                          // 计算校验和
  37. 37  reg    [9:0]  address;
  38. 38  wire   [7:0]  q;
  39. 39  
  40. 40  //*****************************************************
  41. 41  //**                    main code
  42. 42  //*****************************************************
  43. 43  
  44. 44  //计算校验和
  45. 45  assign sum_t2 = init_reg_cnt == REG_NUM - 'd3 ? (~sum_t1 + 1'd1) : sum_t2;
  46. 46  assign rd_en = init_reg_cnt <= REG_NUM - 'd3 ? 1'b1: 1'b0;
  47. 47  
  48. 48  always @(*) begin
  49. 49      if(lcd_id ==16'h4342)
  50. 50          address =init_reg_cnt;
  51. 51      else if(lcd_id ==16'h1018)
  52. 52          address =init_reg_cnt+ 8'd184;
  53. 53      else if(lcd_id == 16'h4384)
  54. 54          address =init_reg_cnt+ 9'd370;
  55. 55  end
  56. 56  
  57. 57  always @(*) begin
  58. 58      if(lcd_id[15:12] == 4'h1)  // 10.1'
  59. 59          REG_NUM = 8'd188;
  60. 60      else
  61. 61          REG_NUM = 8'd186;   // 4.3'
  62. 62  end
  63. 63  
  64. 64  //I2C开始操作控制
  65. 65  always @(posedge clk or negedge rst_n) begin
  66. 66      if(!rst_n) begin
  67. 67          start_init_cnt <= 3'b0;
  68. 68      end
  69. 69      else if(cfg_switch) begin
  70. 70          if(start_init_cnt < 3'h2)
  71. 71              start_init_cnt <= start_init_cnt + 1'b1;
  72. 72      end
  73. 73  end
  74. 74  
  75. 75  // 触发I2C操作控制
  76. 76  always @(posedge clk or negedge rst_n) begin
  77. 77      if(!rst_n)
  78. 78          i2c_exec <= 1'b0;
  79. 79      else if(cfg_switch) begin
  80. 80          if(start_init_cnt == 9'h1)
  81. 81              i2c_exec <= 1'b1;        
  82. 82          else
  83. 83              i2c_exec <= 1'b0;
  84. 84      end
  85. 85  end
  86. 86  
  87. 87  //配置寄存器个数计数
  88. 88  always @(posedge clk or negedge rst_n) begin
  89. 89      if(!rst_n) begin
  90. 90          init_reg_cnt <= 8'd0;
  91. 91      end
  92. 92      else if(cfg_switch & once_done)
  93. 93          init_reg_cnt <= init_reg_cnt + 1'b1;
  94. 94  end
  95. 95  
  96. 96  //寄存器配置完成信号
  97. 97  always @(posedge clk or negedge rst_n) begin
  98. 98      if(!rst_n)
  99. 99          cfg_done <= 1'b0;
  100. 100     else if(init_reg_cnt == REG_NUM)
  101. 101         cfg_done <= 1'b1;
  102. 102 end
  103. 103
  104. 104 //计算校验和
  105. 105 always @(posedge clk) begin
  106. 106     if(once_done & (init_reg_cnt <= REG_NUM - 'd3))
  107. 107         sum_t1 = sum_t1 + i2c_data;
  108. 108     else
  109. 109         sum_t1 = sum_t1;
  110. 110 end
  111. 111
  112. 112 always @(posedge clk) begin
  113. 113     if(cfg_switch) begin
  114. 114         i2c_rh_wl<= 1'b0;
  115. 115         i2c_addr <= GT_CFGS_REG;
  116. 116         reg_num  <= REG_NUM;
  117. 117         if(lcd_id[15:12] == 4'h1) begin  // 16'h1018 10.1'
  118. 118             case(init_reg_cnt)
  119. 119                 8'd186: i2c_data <= sum_t2;
  120. 120                 8'd187: i2c_data <= MODE ;
  121. 121                 default: i2c_data <= q;
  122. 122             endcase
  123. 123         end
  124. 124         else begin
  125. 125             case(init_reg_cnt)
  126. 126                 8'd184: i2c_data <= sum_t2;
  127. 127                 8'd185: i2c_data <= MODE ;
  128. 128                 default: i2c_data <= q;
  129. 129             endcase
  130. 130         end
  131. 131      end
  132. 132 end
  133. 133
  134. 134 gt_cfg gt_cfg_u (
  135. 135   .clka(clk),       // input wire clka
  136. 136   .ena(rd_en),      // input wire ena
  137. 137   .addra(address),  // input wire [8 : 0] addra
  138. 138   .douta(q)         // output wire [7 : 0] douta
  139. 139 );
  140. 140
  141. 141 endmodule
复制代码


这个模块的代码其实也很简单,就是当cfg_switch信号激活后生成对应的IIC控制信号,需要注意的是整个寄存器组不是直接存放在代码中而是放在了ROM中。代码的第134~139行例化了一个ROM IP核,这个ROM会加载一个coe文件(gt_cfg1.coe文件放在工程目录下的doc文件夹中),coe文件包含了三种尺寸屏幕的寄存器组数值。因此代码的第48~55行就根据LCD屏的不同ID选择ROM读取数据地址的起始位置,不同的起始位置对应不同屏幕的寄存器组。代码第57~62行是根据屏幕ID选择具体配置寄存器的个数。代码第105~132行是计算校验和,这个仅仅是10寸屏需要执行校验,其他两种屏幕不需要。
最后我们再来看看IIC驱动模块的代码,因为IIC驱动模块在前面的例程中已经讲解过了,这里就不再详细讲解了,主要就是看一下修改的地方,本节实验的IIC驱动模块新增了连续读写的功能,代码如下(因为代码比较长,这里只给出局部代码,如果对整个IIC协议不了解的请参考前面eeprom例程):
  1. 77  assign  clk_divide =(lcd_id==16'h7016||lcd_id==16'h7084)?250:25;
  2. 78  assign  reg_done = reg_cnt == reg_num ? 1'b1 : 1'b0;
  3. 79  
  4. 80  //生成I2C的SCL的四倍频率的驱动时钟用于驱动i2c的操作
  5. 81  always @(posedge clk or negedge rst_n) begin
  6. 82      if(rst_n == 1'b0) begin
  7. 83          dri_clk <=  1'b1;
  8. 84          clk_cnt <= 10'd0;
  9. 85      end
  10. 86      else if(clk_cnt == clk_divide - 1'd1) begin
  11. 87          clk_cnt <= 10'd0;
  12. 88          dri_clk <= ~dri_clk;
  13. 89      end
  14. 90      else
  15. 91          clk_cnt <= clk_cnt + 1'b1;
  16. 92  end
  17. 93  
  18. 94  //寄存器个数计数
  19. 95  always @(posedge dri_clk or negedge rst_n) begin
  20. 96      if(!rst_n)
  21. 97          reg_cnt <= 'd0;
  22. 98      else if(once_done)
  23. 99          reg_cnt <= reg_cnt + 1'd1;
  24. 100     else if(i2c_done)
  25. 101         reg_cnt <= 'd0;   
  26. 102 end
  27. 103
  28. 104 //(三段式状态机)同步时序描述状态转移
  29. 105 always @(posedge dri_clk or negedge rst_n) begin
  30. 106     if(rst_n == 1'b0)
  31. 107         cur_state <= st_idle;
  32. 108     else
  33. 109         cur_state <= next_state;
  34. 110 end
  35. 111
  36. 112 //组合逻辑判断状态转移条件
  37. 113 always @( * ) begin
  38. 114 //    next_state = st_idle;
  39. 115     case(cur_state)
  40. 116         st_idle: begin                            // 空闲状态
  41. 117            if(i2c_exec) begin
  42. 118                next_state = st_sladdr;
  43. 119            end
  44. 120            else
  45. 121                next_state = st_idle;
  46. 122         end
  47. 123         st_sladdr: begin
  48. 124             if(st_done) begin
  49. 125                 if(!ack) begin
  50. 126                     if(bit_ctrl)                // 判断是16位还是8位字地址
  51. 127                         next_state = st_addr16;
  52. 128                     else
  53. 129                         next_state = st_addr8 ;
  54. 130                 end
  55. 131                 else
  56. 132                     next_state = st_stop;   
  57. 133             end
  58. 134             else
  59. 135                 next_state = st_sladdr;
  60. 136         end
  61. 137         st_addr16: begin                          // 写16位字地址
  62. 138             if(st_done) begin
  63. 139                 if(!ack)
  64. 140                     next_state = st_addr8;
  65. 141                 else
  66. 142                     next_state = st_stop;   
  67. 143             end
  68. 144             else
  69. 145                 next_state = st_addr16;
  70. 146         end
  71. 147         st_addr8: begin                           // 8位字地址
  72. 148             if(st_done) begin
  73. 149                 if(!ack) begin
  74. 150                     if(wr_flag==1'b0)            // 读写判断
  75. 151                         next_state = st_data_wr;
  76. 152                     else
  77. 153                         next_state = st_addr_rd;
  78. 154                 end
  79. 155                 else
  80. 156                     next_state = st_stop;   
  81. 157             end
  82. 158             else
  83. 159                 next_state = st_addr8;
  84. 160         end
  85. 161         st_data_wr: begin                        // 写数据(8 bit)
  86. 162             if(st_done) begin
  87. 163                 if(reg_done)
  88. 164                     next_state = st_stop;
  89. 165                 else
  90. 166                     next_state = st_data_wr;
  91. 167             end
  92. 168             else
  93. 169                 next_state = st_data_wr;
  94. 170         end
  95. 171         st_addr_rd: begin                      // 写地址以进行读数据
  96. 172             if(st_done) begin
  97. 173                 if(!ack)
  98. 174                     next_state = st_data_rd;
  99. 175                 else
  100. 176                     next_state = st_stop;
  101. 177             end
  102. 178             else
  103. 179                 next_state = st_addr_rd;
  104. 180         end
  105. 181         st_data_rd: begin                      // 读取数据(8 bit)
  106. 182             if(st_done) begin
  107. 183                 if(reg_done)
  108. 184                     next_state = st_stop;
  109. 185                 else
  110. 186                     next_state = st_data_rd;
  111. 187             end
  112. 188             else
  113. 189                 next_state = st_data_rd;
  114. 190         end
  115. 191         st_stop: begin                         // 结束I2C操作
  116. 192             if(st_done)
  117. 193                 next_state = st_idle;
  118. 194             else
  119. 195                 next_state = st_stop ;
  120. 196         end
  121. 197         default: next_state= st_idle;
  122. 198     endcase
  123. 199 end
复制代码


ID为7016跟7084的屏幕使用的触摸芯片是CST340,其它屏幕使用的触摸芯片是GT9147,这两款芯片的驱动时钟不一样,第77行代码的主要作用是兼容两款触摸芯片。代码的第78行定义了reg_done信号,它就是连续写完成的标志,当“reg_cnt == reg_num”的时候reg_done才会拉高。reg_num是需要配置寄存器的个数由touch_ctrl模块或者i2c_reg_cfg模块提供,代码第95~102行是寄存器个数计数器,每完成一个寄存器写入reg_cnt就会加一,直到i2c_done信号拉高为止。i2c_done信号是当IIC状态机进入最后一个状态(st_stop)时才会拉高,因此当i2c_done拉高代表连续读写多个寄存器完成。代码第105~199行是状态机第二段(IIC通信三段状态机的第二段),我们可以看到代码第161~170行以及代码第181~190行分别是IIC通信的写数据和读数据状态,这两个状态都判断了reg_done信号是否拉高,如果reg_done信号未拉高代表连续读写寄存器还没完成,那么读写状态循环执行,直到把所有寄存器全部写完或者读完,reg_done信号才会拉低,状态机进入st_stop状态,然后拉高i2c_done信号,告诉其它模块一次连续读写完成。
到这里整个触摸部分就全部给大家分析完了,接下来再带领大家一起看一下LCD显示部分代码,LCD显示部分代码在前面的例程中也都讲解过了,本节实验仅仅分析一下字模显示模块的代码,至于LCD驱动部分的代码如果大家有不熟悉的可以参考前面LCD彩条显示实验。
字模显示模块(lcd_display)代码如下(因为本模块的字模比较长,直接粘贴代码不好看,所以这里以截图的方式一段一段给大家讲解):
第二十章 LCD触摸屏实验59500.png
图 7.5.13.2字模显示模块(lcd_display)代码
上图展示的是数字0~9和字母“X”、字母“Y”的字模,它是使用“PCtoLCD2002完美版”(软件存放路径:[正点原子]领航者ZYNQ开发板光盘资料\领航者ZYNQ开发板资料盘(A盘)\6_软件资料\1_软件)软件生成的,生成步骤如下图所示:
第二十章 LCD触摸屏实验59700.png
图 7.5.13.3字模生成步骤
首先在资料盘A盘找到“PCtoLCD2002完美版”软件并打开,然后按照上图步骤操作。第一步点击“选项”按钮打开字模属性设置,然后按照上图序号2、3、4、5、6、7、8步骤设置字模参数并点击确定,之后在上图序号9的位置输入我们想要生成的字符(本节实验输入0123456789XY),再到序号10的位置设置字符的宽度和高度(本节实验选择32*32,但是在实际显示英文字符的时候宽度会减半也就是16*32),最后点击“生成字模”按钮,在上图序号11的位置就生成了我们需要的字模数据组,拷贝到代码中即可使用。
接下来我们再来看看怎么把touch_ctrl模块读取到的坐标显示到LCD屏幕上去,首先我们要把坐标点数据接入字模显示模块(lcd_display),然后将X轴Y轴数据转换成能显示的个位、十位、百位、千位(只有X轴坐标会到达千位,因为最大分辨率是1280*800),与上文的字模数组对应起来,代码如下:
36  assign  data6 = data[31:16] / 10'd1000 % 4'd10 ;  // X轴坐标千位数         
37  assign  data5 = data[31:16] / 7'd100 % 4'd10   ;  // X轴坐标百位数
38  assign  data4 = data[31:16] / 4'd10 % 4'd10    ;  // X轴坐标十位数
39  assign  data3 = data[31:16] % 4'd10            ;  // X轴坐标个位数
40  assign  data2 = data[15:0]  / 7'd100 % 4'd10   ;  // Y轴坐标百位数
41  assign  data1 = data[15:0]  / 4'd10 % 4'd10    ;  // Y轴坐标十位数
42  assign  data0 = data[15:0]  % 4'd10            ;  // Y轴坐标个位数
这里的data就是touch_ctrl模块读到的数据点坐标,data的高16位是X轴坐标,低16位是Y轴坐标,将坐标数据拆分成个位、十位、百位、千位后分别存到寄存器data0~ data6中,最后就是将实际坐标数据与字模数组联系起来,代码如下:
第二十章 LCD触摸屏实验60753.png
图 7.5.13.4显示区域赋值
上图中的代码就是将具体要显示的坐标数据和字模数组结合到一起,达到显示坐标数据的功能(上图只截了一部分,完整版请参考例程源代码),那么它的原理是什么呢?我们来分析一下代码,首先代码的第101~107行是显示第一个字符,也就是X轴坐标的最高位,其中代码第101和102行是用来确定第一个字符显示的位置,pixel_xpos与pixel_ypos是LCD屏幕行列扫描信号,CHAR_POS_X与CHAR_POS_Y是显示区域的起始坐标,CHAR_WIDTH和CHAR_HEIGHT是整个显示区域的总宽度和高度(X轴最大显示四位数,Y轴最大显示三位数,再加上“X”和“Y”两个字母,整个显示区域需要显示九个英文字符所以总宽度为16*9等于144个像素点宽度),第一个字符的显示位置就是从起始点开始算起,X轴方向大于起始点小于起始点加16,Y轴方向就是大于起始点,小于起始点加32;依次类推第二个字符的位置就是X轴方向大于起始点加16小于起始点加32,Y轴方向还是大于起始点,小于起始点加32。这样就将九个字符的位置确定下来了,那么每个位置具体显示什么数字呢?看代码第103行,我们要显示第一个数字也就是X轴坐标的最高位,它被存放在寄存器data6中,把它带入字模数组即char [data6], data6的值是几就定位到字模对应的数字为几,例如data6的值为“1”就定位到数组char [1],对应的数字就是“1”的字模。找到这个字模后就要把它显示到LCD屏幕上去,还拿data6的值为“1”举例,“1”的字模其实就是16*32等于512个像素点,每个像素点都有自己的值,要么“1”,要么“0”。我们需要做的就是把第一个字符显示区域的512个像素点代入字模的512个像素点中去,如果字模的像素点值为“1”那么对应的字符显示区域像素点赋值颜色数值为黑色,反之给白色数值。举个例子现在要显示第一个字符的第一个像素点,对应LCD屏幕上的像素点就是起始点,对应字模数组的值就是char [data6] [511],同理第二个像素点数据对应的就是char [data6] [510],依次类推最后一个像素点数据就是char [data6] [0]。因此代码第103行执行的功能就是把pixel_xpos与pixel_ypos对应的LCD实际显示像素点转换成对应的字模数组char中的元素,顺便提一句Y轴的值每增减1则像素点增减16,所以代码中要“*16”。
清楚了第一个字符的显示原理后,其他字符的显示原理是一模一样的就不再赘述了,到此整个LCD触摸实验代码就分析完了,接下来就可以生成bit流文件下板验证了。
20.5下载验证
首先将下载器与领航者底板上的JTAG接口连接,下载器另外一端与电脑连接,然后将LCD屏连接到开发板上,最后连接电源线并打开电源开关。
然后将本次实验生成的bit流文件下载到开发板中,此时可以看到LCD屏幕点亮并显示“0000X000Y”,我们用手触摸LCD屏,触摸点的数据就会显示出来了。
实验结果如下图所示:
第二十章 LCD触摸屏实验62076.png
图 7.5.13.1 触摸显示

正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

4

帖子

0

精华

新手上路

积分
25
金钱
25
注册时间
2021-6-8
在线时间
4 小时
发表于 2021-11-20 12:29:38 | 显示全部楼层
上面的touch_ctrl的代码不全呀,而且源码在资料里也找不到
回复 支持 反对

使用道具 举报

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

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-23 18:47

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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