uisrc 发表于 2023-12-29 19:17:46

2-3-29 基于FPGA实现触摸屏实验

软件版本:VIVADO2021.1操作系统:WIN10 64bit硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!1 概述常规情况下我们会使用CPU通过I2C接口实现触控液晶触摸方案的实现,也可以上LINUX,一般LINUX下也有对于各大厂家触摸液晶屏的方案支持。但是如果我们在一些情况下不方便使用CPU或者我们压根不会使用CPU的方式或者也不会LINUX的方式,我们要实现一些简单的触摸控制,用FPGA实现是不是没有办法吗?答案当然是"NO"。因为通过对触摸液晶的硬件分析和驱动代码分析,笔者发现只要通过I2C接口实现对触摸液晶寄存器的控制和访问一样可以实现液晶屏的触控。笔者前面已经编写了非常好的I2C控制器,实现了EEPROM和RTC时钟芯片的读取,那么在本文中,我们也可以利用这个控制器实现和液晶屏触控芯片的通信。关于I2C总线的细节笔者就不再介绍了,读者如果没有掌握好,请学习我们前面相关课程。2 GT9X系列触摸芯片使用2.1 接口说明GT9 非单层多点系列(以下简称GT9 系列)与主机接口共有6 PIN,分别为:VDD、GND、SCL、SDA、INT、RESET。主控的INT 口线需具有上升沿或下降沿中断触发功能,并且当其在输入态时,主控端必需设为悬浮态, 取消内部上下拉功能;主机通过输出高、低来控制GT9 系列的RESET口为高或低。为保证可靠复位,建议RESET 脚输出低100μ s 以上。GT9 系列与主机通信采用标准I2C 通信,最高速率可以支持至400K bps。当主机采用200K 以上的通信速率时,需要特别注意I2C 口的外部上拉电阻阻值,以保证SCL、SDA 边沿足够陡峭。GT9 系列在通信中始终作为从设备,其I2C 设备地址由7 位设备地址加1 位读写控制位组成,高7 位为地址,bit 0 为读写控制位。GT9 系列有两个从设备地址可供选择,如下表:
模式7 位地址8 位写地址8 位读地址
10x5D0XBA0XBB
20x140X280X29


这里大家可能奇怪有2套地址,这个地址触摸芯片会根据硬件电路对于INT脚的配置采用那一套地址。当INT在上电期间是底电平则使用模式1;当INT在上电期间是高电平则使用模式2;2.2 寄存器写时序S:起始信号。Address_W:带写控制位的从设备地址。ACK:应答信号。Register_H、Register_L:待写入的16 位寄存器首地址。Data_1 至Data_n:数据字节1-n。E:停止信号。设定了写操作寄存器首地址后,可以只写1 字节数据,也可以一次性写入多个字节数据,GT9 系列自动将其往高地址顺序存储。2.3 寄存器读时序先通过前述写操作时序设定需要读取的寄存器首地址,重新发送起始信号进行读寻址,读取寄存器数据。Address_R:带读控制位的从设备地址。NACK:最后1 字节读完主控回NACK。设定了读操作寄存器地址后,主控可以一次读取1 字节,也可以一次性读取多个字节数据,GT91XX 自动递增寄存器地址,将后续数据顺序发送。设定完读操作寄存器地址后的停止信号(上图中的第一个E 信号)可发可不发,但是重新开始I2C 通信的起始信号必须再次发送。2.4 重要寄存器我们这里只对使用到的寄存器进行说明,没有使用到的,可以看我整理的资料里面有一个"GT9非单层多点系列编程指南文件.pdf"的文档比较详细描述了GT9X的触摸芯片如何使用。
寄存器命令描述
0X8040Command0:读坐标状态 1:差值原始值 2:软件复位3:基准更新(内部测试) 4:基准校准(内部测试) 5:关屏其余值无效
0X8047Config_ Version配置起始地址寄存器,配置文件的版本号(新下发的配置版本号大于原版本,或等于原版本号但配置内容有变化时保存,版本号版本正常范围:'A'~'Z',发送0x00则将版本号初始化为'A')
0X80FFConfig_Chksum校验和寄存器,配置信息校验(0x8047到0x80FE之字节和的补码)
0X8140Product ID产品ID寄存器
0X814E触摸状态寄存器Bit7: Buffer status,1表示坐标(或按键)已经准备好,主控可以读取;0表示未就绪,数据无效。当主控读取完坐标后,必须通过I2C将此标志(或整个字节)写为0。Bit4: HaveKey, 1表示有按键,0表示无按键(已经松键)。Bit3~0: Number of touch points, 屏上的坐标点个数
0X8150第1个触摸点XX坐标低字节
0X8151第1个触摸点XX坐标低字节
0X8152第1个触摸点YY坐标低字节
0X8153第1个触摸点YY坐标高字节
0X8158第2个触摸点XX坐标低字节
0X8159第2个触摸点XX坐标低字节
0X815A第2个触摸点YY坐标低字节
0X815B第2个触摸点YY坐标高字节
0X8160第3个触摸点XX坐标低字节
0X8161第3个触摸点XX坐标低字节
0X8162第3个触摸点YY坐标低字节
0X8163第3个触摸点YY坐标高字节


以上就是我们主要用到的寄存器,由于我们默认采用1024X600分辨率,所以0X8047,0X80FF ,0X8140寄存器也是无需考虑的。2.5 上电时序主机上电后,需要控制GT9X的AVDD、VDDIO、INT、Reset等脚位,控制时序请遵从如下时序图:1)、INT T2 时间后,主控是要输出高,还是低,取决于主机要用何I2C 从设备地址与GT9 芯片通信,若用地址0x28/0x29,则输出高;若用地址0xBA/0xBB,则输出低。2)、主机控制GT9 上电过程中,当主控将自身INT 转化为悬浮输入态后,需要延时50ms 再发送配置信息。2.6 坐标读取本文的设计方案采用轮询的方式读取触控液晶的坐标寄存器,当然也可以采用更高效的中断方式。读者可以去尝试下。以下是关于中断方式和轮询方式的方法介绍。主控可以采取轮询或INT 中断触发方式来读取坐标,采用轮询方式时可采取如下步骤读取:1、按第二节时序,先读取寄存器0x814E,若当前buffer(buffer status 为1)数据准备好,则依据手指个数读、按键状态取相应个数的坐标、按键信息。2、若在1 中发现buffer 数据(buffer status 为0)未准备好,则等待1ms 再进行读取。采用中断读取方式,触发中断后按上述轮询过程读取坐标。GT9 中断信号输出时序为(以输出上升沿为例,下降沿与此时序类同):1、 待机时INT 脚输出低。2、 有坐标更新时,输出上升沿。3、2 中输出上升沿后,INT 脚会保持高直到下一个周期(该周期可由配置Refresh_Rate 决定)。请在一个周期内将坐标读走并将buffer status(0x814E)写为0。4、2 中输出上升沿后,若主控未在一个周期内读走坐标,下次GT9 即使检测到坐标更新会再输出一个INT 脉冲但不更新坐标。5、若主控一直未读走坐标,则GT9 会一直打INT 脉冲。2.7 工作模式关于GT9芯片的工作模式,本文中使用的是Normal模式,下面是关于各种模式的介绍。GT9 工作模式分为Normal、Low Power(Green)、Sleep 三种,各种工作状态间相互转换关系如下图所示:默认情况下,GT9 工作自动切换Normal 和Low Power 工作模式,按键时及松键后的一段时间(这段时间由配置参数Low_Power_Control 设定,0~15 秒可设)工作在Normal mode,若该段时间后还处于无按键状态,则进入Low Power 工作模式(低速扫描)Normal 模式GT9 在Normal mode 时,最快的坐标刷新周期为5ms-20ms 间(依赖于配置信息的设定,配置信息可控周期步进长度为1ms)。Normal mode 下,一段时间无触摸事件发生,GT9 将自动转入Low Power mode,以降低功耗。GT9 无触摸自动进入Low Power mode 的时间可通过配置信息设置,范围为0~15s,步进为1s。Low Power(Green) mode在LowPower mode 下,GT9 扫描周期固定为40ms,若检测到有触摸动作发生,自动进入Normal mode。Sleep mode及唤醒主CPU通过I2C命令,使GT9进入Sleep mode(需要先将INT脚输出低电平)。当需要GT9退出Sleep mode时,主机可采用INT高电平唤醒或reset唤醒。若采用INT高电平唤醒,操作时序为:输出高电平到INT脚(主机打高INT脚2~5ms,然后转悬浮输入态),唤醒后GT9将进入Normal mode;当采用reset脚唤醒时,需要按前述上电初始化过程控制INT脚和reset脚。3 硬件电路分析硬件接口和子卡模块请阅读"附录 1"配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。4 系统框图
5 触摸控制FPGA实现5.1 gt9xx_config.v程序源码
/************************gt9xx_config 触控驱动控制器*********************--1.上电后通过控制IO_t_ini和O_t_rst对触控芯片的工作模式进行设置,设置器件地址为0XBA--2.状态机中,通过轮询方式,不断访问触控芯片,读取坐标,这里读取3组坐标信息*********************************************************************/
`timescale 1ns / 1ns
module gt9xx_config(input            I_clk,   //系统时钟输入input            I_rstn,//系统复位output             O_t_rst,   //触控屏中断信号,也用于上电初始化inout            IO_t_ini,   //触控屏复位信号,也用于上电初始化output reg         O_iic_req, //I2C总线读写请求output reg O_wr_data, //I2C写数据output regO_wr_cnt,//I2C写数据长度input      I_rd_data, //I2C读数据output regO_rd_cnt,//I2C读数据长度input            I_iic_busy,//I2C总线忙output   O_tp1_x,   //触控坐标1 X轴坐标output   O_tp1_y,   //触控坐标1 y轴坐标output   O_tp2_x,   //触控坐标2 X轴坐标output   O_tp2_y,   //触控坐标2 y轴坐标output   O_tp3_x,   //触控坐标3 X轴坐标output   O_tp3_y    //触控坐标3 y轴坐标);
`define GT_CTRL_REG   16'h4080    //GT9xx控制寄存器`define GT_CFGS_REG   16'h4780    //GT9xx配置起始地址寄存器    `define GT_CHECK_REG    16'hFF80    //GT9xx校验和寄存器`define GT_PID_REG      16'h4081    //GT9xx产品ID寄存器
`define GT_GSTID_REG    16'h4E81    //GT9xx当前检测到的触摸状态寄存器`define GT_TP1_REG      16'h5081    //第一个触摸点数据地址`define GT_TP2_REG      16'h5881    //第二个触摸点数据地址`define GT_TP3_REG      16'h6081    //第三个触摸点数据地址//`define GT_TP4_REG    16'h6881    //第四个触摸点数据地址//`define GT_TP5_REG    16'h7081    //第五个触摸点数据地址
//reset counter for delay time
reg         t_ini_r=0 ;reg rst_cnt = 25'd0;
assignO_t_rst = (rst_cnt | rst_cnt); // 触控芯片的复位,上电期间用于设置工作模式
assignIO_t_ini = (t_ini_r == 1'b0) ?1'b0 : 1'bz; //中断信号上电期间用于设置工作模式
//上电后复位计数器always@(posedge I_clk or negedge I_rstn) begin    if(I_rstn == 1'b0)      rst_cnt <= 0;    else if(!rst_cnt)      rst_cnt <= rst_cnt + 1'b1;    else      rst_cnt <= rst_cnt;end
//触控芯片上电复位计数器always@(posedge I_clkor negedge I_rstn) begin    if(I_rstn == 1'b0 || rst_cnt == 1'b0)      t_ini_r <= 1'b0;        elseif(rst_cnt)      t_ini_r <= 1'b1;    else      t_ini_r <= t_ini_r;end
reg TS_S; //状态机寄存器reg i;    //计数器寄存器reg touch_x; //X坐标寄存器,保存3组X坐标数据,每组16bitsreg touch_y; //X坐标寄存器,保存3组y坐标数据,每组16bits
//把坐标分离开,方便观察和使用wire touch_x1 = touch_x;wire touch_x2 = touch_x;wire touch_x3 = touch_x;wire touch_y1 = touch_y;wire touch_y2 = touch_y;wire touch_y3 = touch_y;
assign O_tp1_x = touch_x1;assign O_tp1_y = touch_y1;assign O_tp2_x = touch_x2;assign O_tp2_y = touch_y2;assign O_tp3_x = touch_x3;assign O_tp3_y = touch_y3;
reg ctstate; //坐标状态寄存器wire GT9XX_TPX_TBL = {`GT_TP3_REG,`GT_TP2_REG,`GT_TP1_REG};//触控芯片相关寄存器初始化
//vio 观察坐标vio_0 vio_dg (.clk(I_clk), .probe_in0(touch_x1), .probe_in1(touch_y1), .probe_in2(touch_x2), .probe_in3(touch_y2), .probe_in4(touch_x3), .probe_in5(touch_y3));//ila 在线逻辑分析仪观察状内部信号和状态机ila_0 ila_dg (.clk(I_clk),.probe0({TS_S,O_t_rst,O_iic_req,ctstate}));
//状态机always@(posedge I_clk) begin    if(!rst_cnt)begin //复位初始化信号和寄存器      i         <= 8'd0;      O_iic_req   <= 1'b0;      O_rd_cnt    <= 8'd0;      O_wr_data <= {24'd0,8'hba};//8'hba为芯片的器件地址      touch_x   <= 48'd0;      touch_y   <= 48'd0;      ctstate   <= 8'd0;      TS_S      <= 4'd0;        end    else begin      case(TS_S)      0:begin//设置触控芯片的软件复位            O_iic_req<= 1'b1; //发送I2C总线操作请求            O_wr_cnt   <= 8'd4; //写入4个数据            O_wr_data <= `GT_CTRL_REG; //GT9xx控制寄存器地址            O_wr_data <= 8'd2; //软件复位值            if(I_iic_busy)   //等在总线忙            TS_S    <= 4'd1; //下一个状态      end      1:begin //等待I2C总线空闲            O_iic_req<= 1'b0;            if(!I_iic_busy)begin //等待I2C总线空闲,代表I2C操作完成,软件复位完成            TS_S    <= 4'd2;//下一个状态            end      end      2:begin//重置触控屏芯片的控制寄存器         O_iic_req<= 1'b1;//发送I2C总线操作请求         O_wr_cnt<= 8'd4;//写入4个数据         O_wr_data <= `GT_CTRL_REG;//GT9xx控制寄存器地址             O_wr_data <= 8'd0; //重置控制寄存器为0         if(I_iic_busy)   //等在总线忙         TS_S    <= 4'd3; //下一个状态          end            3:begin //等待I2C总线空闲            O_iic_req<= 1'b0;            if(!I_iic_busy)//等待I2C总线空闲,代表I2C操作完成,软件复位完成            TS_S    <= 4'd4;//下一个状态      end      4:begin//读取坐标值         O_iic_req<= 1'b1;         O_wr_data <= `GT_GSTID_REG;//GT9xx触摸状态寄存器         O_wr_cnt <= 8'd3;//写入3个数据,器件地址,寄存器地址         O_rd_cnt <= 8'd1;//读出1个数据,状态寄存器         i      <= 8'd0;//i用于计数         if(I_iic_busy)   //等在总线忙         TS_S   <= 4'd5;//下一个状态      end      5:begin //等待I2C总线空闲            O_iic_req<= 1'b0;            if(!I_iic_busy)//等待I2C总线空闲,代表I2C操作完成,已经读到`GT_GSTID_REG状态寄存器            TS_S<= 4'd6;//下一个状态      end      6:begin //判断状态寄存器            if(I_rd_data&8'h80)begin //确认状态寄存器是否有触控事件发生            ctstate <= I_rd_data&8'h07;//触控状态,有多少触控点事件发生            TS_S<= 4'd7; //下一个状态            end            else            TS_S<= 4'd4; //如果没有触控事件发生,继续回到前一个状态,继续读状态寄存器      end            7:begin//重置触控状态寄存器         O_iic_req<= 1'b1;         O_wr_data <= `GT_GSTID_REG; //GT9xx触摸状态寄存器         O_wr_data <= 8'd0; //设置寄存器值为0         O_wr_cnt <= 8'd4;//I2C写入4BYTES         O_rd_cnt <= 8'd0;//I2C不需要读数据         i      <= 8'd0;//i计数器清零         if(I_iic_busy)   //等待总线忙 ,代表I2C控制器开始工作             TS_S   <= 4'd8;      end      8:begin //等待I2C总线空闲            O_iic_req<= 1'b0;            if(!I_iic_busy) //等待I2C总线空闲,代表I2C完成 `GT_GSTID_REG的重置            TS_S    <= 4'd9;      end            9:begin //读取1个坐标            O_iic_req<= 1'b1;            O_wr_data <= GT9XX_TPX_TBL; //坐标的寄存器地址            O_wr_cnt <= 8'd3; //I2C 写3BYTES            O_rd_cnt <= 8'd4; //i2c 读4BYTES            if(I_iic_busy)//等待总线忙 ,代表I2C控制器开始工作                TS_S    <= 4'd10;                end      10:begin//等待I2C总线空闲            O_iic_req<= 1'b0;            if(!I_iic_busy)//等待I2C总线空闲,代表I2C完成 GT9XX_TPX_TBL中对应的坐标寄存器读            TS_S    <= 4'd11;      end          11:begin //保存坐标值            touch_x <= I_rd_data;            touch_y <= I_rd_data;            i <= i + 1'b1;//i计数器+1                if(i<8'd2)      //判断本次是否读完3组坐标            TS_S <= 4'd9;   //如果没读完,继续读            else            TS_S <= 4'd4;   //如果读完了,回到状态4      end      default:TS_S <= 4'd0;      endcase   endend
endmodule


5.2 代码分析以上代码中关键是利用了米联客开发的I2C控制器,然后结合我们前面对GT9X系列触控芯片寄存器的以及如何使用的认识,来通过I2C总线对GT9X系列芯片配置,和读取触控的坐标值。状态0~3:是对控制寄存器配置,首先配置GT_CTRL_RE(0X8040)为2,对GT9X触控芯片软件复位,然后再配置GT_CTRL_REG为0,表示进入读坐标状态。状态4~6:进入读坐标状态后,先读取GT_GSTID_REG(0X8047)寄存器的值,这个寄存器是保持了触控状态,当触摸液晶感知到已经被触控了,相关的寄存器位会被置位。状态7~11:当我们通过4~6读取到了有触控行为发生,那么就读取相关坐标寄存器的值,我们这里只读取3组触控寄存器的值,这样就实现了3点触控6 实现三点触控图形绘制
/************************uitpg 测试数据发生器*********************--1.该方案用于演示触控坐标读取方案--2.通过输入的坐标信息,产生RED BLUE GREEN 方格,当在触摸屏上移动手指,方格会跟随移动*********************************************************************/
`timescale 1ns / 1ns //仿真时间间隔/精度
module uitpg(input            I_tpg_clk, //系统时钟//input            tpg_rstn_i,//系统复位输入input            I_tpg_vs,//场同步输入input            I_tpg_hs,//行同步输入input            I_tpg_de,//视频数据有效输入output         O_tpg_vs,//场同步输出output         O_tpg_hs,//行同步输出output         O_tpg_de,//视频数据有效输出output     O_tpg_data, //有效测试数据input    I_tp1_x,   //坐标1的X轴坐标input    I_tp1_y,   //坐标1的y轴坐标input    I_tp2_x,   //坐标2的X轴坐标input    I_tp2_y,   //坐标2的y轴坐标input    I_tp3_x,   //坐标3的X轴坐标input    I_tp3_y      //坐标3的y轴坐标);
regr_reg = 8'd0; // red 颜色寄存器regg_reg = 8'd0; // green 颜色寄存器regb_reg = 8'd0; // blue 颜色寄存器
reg tpg_vs_r = 1'b0;//对vs信号寄存reg tpg_hs_r = 1'b0;//对hs信号寄存
reg v_cnt = 12'd0; //场像素计数器reg h_cnt = 12'd0; //行像素计数器
always @(posedge I_tpg_clk)begin    tpg_vs_r <= I_tpg_vs; //对vs信号寄存一次    tpg_hs_r <= I_tpg_hs; //对hs信号寄存一次end
//h_cnt计数器模块always @(posedge I_tpg_clk)    h_cnt <= I_tpg_de ? h_cnt + 1'b1 : 12'd0; //计数行有效像素,//当de无效,重置 h_cnt=0
//v_cnt计数器模块always @(posedge I_tpg_clk)if(I_tpg_vs) //通过vs产生同步复位    v_cnt <= 12'd0; //重置v_cnt=0else    v_cnt <= ((!tpg_hs_r)&&I_tpg_hs) ? v_cnt + 1'b1 : v_cnt; //hs信号的上升沿,v_cnt计数,这种方式可以不管hs有效是高电平还是低电平的情况,v_cnt 场像素计数器,计数行数量

//以触控坐标的中心绘制,第1个触控点的方格,大小为200*200像素wire box1_x1 =I_tp1_x - 100;wire box1_x2 =I_tp1_x + 100;wire box1_y1 =I_tp1_y - 100;wire box1_y2 =I_tp1_y + 100;
//以触控坐标的中心绘制,第2个触控点的方格,大小为200*200像素wire box2_x1 =I_tp2_x - 100;wire box2_x2 =I_tp2_x + 100;wire box2_y1 =I_tp2_y - 100;wire box2_y2 =I_tp2_y + 100;
//以触控坐标的中心绘制,第3个触控点的方格,大小为200*200像素wire box3_x1 =I_tp3_x - 100;wire box3_x2 =I_tp3_x + 100;wire box3_y1 =I_tp3_y - 100;wire box3_y2 =I_tp3_y + 100;
wire box1_en=(h_cnt>box1_x1)&&(h_cnt<box1_x2)&&(v_cnt>box1_y1)&&(v_cnt<box1_y2);// 屏幕上,方格1的数据绘制使能wire box2_en=(h_cnt>box2_x1)&&(h_cnt<box2_x2)&&(v_cnt>box2_y1)&&(v_cnt<box2_y2);// 屏幕上,方格2的数据绘制使能wire box3_en=(h_cnt>box3_x1)&&(h_cnt<box3_x2)&&(v_cnt>box3_y1)&&(v_cnt<box3_y2);// 屏幕上,方格3的数据绘制使能
//显示颜色always @(posedge I_tpg_clk)begin    if(box1_en)begin      r_reg   <=8'b11111111;      //显示区域为红色      g_reg   <=0;      b_reg   <=0;        end    if(box2_en)begin      r_reg   <=0;                      g_reg   <=8'b11111111;   //显示区域为绿色      b_reg   <=0;        end    if(box3_en)begin      r_reg   <=0;                      g_reg   <=0;      b_reg   <=8'b11111111;   //显示区域为蓝色    end          if(!(box1_en||box2_en||box3_en))begin //背景为白色      r_reg   <=8'b11111111;          g_reg   <=8'b11111111;      b_reg   <=8'b11111111;          endend
assign O_tpg_data = {r_reg,g_reg,b_reg};assign O_tpg_vs = I_tpg_vs;assign O_tpg_hs = I_tpg_hs;assign O_tpg_de = I_tpg_de;
endmodule


当我们读取到了触控的坐标后,根据坐标的参数信息绘制矩形框,分别绘制红绿蓝三个矩形,这样我们就可以非常方便的测试触摸液晶屏是否可以正常工作。7 硬件接线(该教程为通用型教程,教程中仅展示一款示例开发板的连接方式,具体连接方式以所购买的开发板型号以及结合配套代码管脚约束为准。)我们使用米联客3.3V BASE子卡,接口使用的是我们液晶屏的RGB口。下图是RGB接线方式。请确保下载器和开发板已经正确连接,并且开发板已经上电。(注意JTAG端子不支持热插拔,而USB接口支持,所以在不通电的情况下接通好JTAG后,再插入USB到电脑,之后再上电,以免造成JTAG IO损坏)7寸液晶屏的接口使用的是LVDS接口8 测试结果8.1 VIO显示坐标值未来观察方便,添加了VIO(虚拟IO)观察读取到的触控坐标值8.2 触摸演示
页: [1]
查看完整版本: 2-3-29 基于FPGA实现触摸屏实验