目录标题
- 前言
- 1、简单介绍
- 2、触摸芯片与主机的硬件连接
- 3、内部寄存器
- 3.1、控制寄存器(0X8040)
- 3.2、配置寄存器组(0X8047~0X8100)
- 3.3、状态寄存器(0x814E)
- 3.4、坐标寄存器(0x8150-0x8177)
- 4、初始化流程
- 4.1、IIC地址选择
- 4.2、更新GT911寄存器配置
- 5、触摸点坐标值读取
- 6、提示
- 7、代码部分
- 7.1、gt911.h
- 7.2、GT911.c
前言
做毕设用到了电容触摸屏,移植了几天内才整好。在网上找到的好多例程都是电容屏、电阻屏都融合到一起去了,对于新手来说,不是太好理解,所以我来分享一下我的认识、例程和学习过程中遇到的问题以及要注意的点。
1、简单介绍
GT911、GT928、GT9147都属于GT9系列非单层多点触控芯片,他们支持的触控点数不同(GT928支持10个点、GT911支持5个点)、驱动和感应通道也可能不同。可是他们的寄存器和IIC通讯时序是相同的,也就是说驱动程序是兼容的。
所以如果是使用的GT9147也可以看看这个文档,整理下思路。
2、触摸芯片与主机的硬件连接
接口 | 介绍 |
---|---|
VCC | 电源 |
GND | 地 |
SCL | 与GT911进行IIC通信的时钟线 |
SDA | 与GT911进行IIC通信的数据线 |
INT | 当发生触摸时,GT911的INT脚会输出上升/下降沿(内部寄存器可以配置),主机就可以通过外部中断处理去读取触点信息。 |
RST | 复位引脚,拉低100us以上,即可复位。正常工作时,应该保持拉高 |
3、内部寄存器
GT911上电初始化情况,是由其内部FLASH中的若干个寄存器来决定的,我们下面对其中比较重要的进行讲解。
3.1、控制寄存器(0X8040)
通过往里面写入不同的8bit数据。可以使其完成不同的任务。
如图示,一般的操作是在复位时,先往0X8040里面写2,对其进行软复位,然后判断是否需要对其配置寄存器进行更新,若需要更新,则写入新版本的配置参数(这部分配合着后面来看才能看懂),然后在往0X8040里面写0,结束软复位,后面就能正常读取触摸坐标了。
总结一下:
- 要更新配置:(一般来说,买到的屏幕商家都已经预先写进去了,不需要更新)
- 1、往0X8040写2(开始软复位)
- 2、往0X8047~0X8100写入新的配置寄存器参数
- 3、往0X8040写0(结束软复位)
- 4、后面正常读触点坐标
- 不更新配置:
- 1、往0X8040写2(开始软复位)
- 2、往0X8040写0(结束软复位)
- 3、后面正常读触点坐标
3.2、配置寄存器组(0X8047~0X8100)
如上图示,这里共有186个寄存器,用于配置GT9147的各个参数,这些参数对于不同的屏幕也都各有不同。一般买回来的里面呢都自带的有(也就是说,原来就是好用的,不需要再自己进行操作了)
如果你要是不小心把原来的覆盖掉了,就只能再重新写一次了(我在调试过程中就是这样,把其它型号的配置参数写进去了,要命的是我还没有正确的了,最后还是从我同学的一块同款屏幕上将这186个寄存器的值都读出来,又重新写入到我的屏幕的GT911中,才弄好了)
在这样的情况下,你就需要对这些寄存器的一些特性,做些更深入的了解了。
- 0x8047——这个寄存器中,保存的是当前配置参数的版本号,你新写入配置参数的版本号,必须要比它大或者在相等的情况下,其他参数有变化,否则不会生效。
- 0x80FF——这里需要写入的是从0x8047到0x80FE之间184个数据和的补码,否则那你写入的配置也不会生效。
- 0x8100——用于控制是否将配置保存在GT911的FLASH中,写0,则不保存配置,写1则保存配置。
3.3、状态寄存器(0x814E)
寄存器 | bit7 | bit6 | bit5 bit4 | bit3 bit2 bit1 bit0 |
---|---|---|---|---|
0x814E | buffer状态 | large detect | Reserved | 有效触点个数 |
我们只看此寄存器的最高位和低四位即可。
最高位表示的Buffer状态,若有数据(有触摸),则Buffer为1‘
低四位表示有效出点的个数,0~5,0表示此时没有触摸。5表示此时有5点触摸
(我们上面提到过gt911最多同时支持五个触控点数)
3.4、坐标寄存器(0x8150-0x8177)
如上图所示,我们可以将其分为5组,其分别用来表示5个触点坐标
下面以触点1的坐标数据寄存器为例做具体讲解:
寄存器 | bit7~0 | 寄存器 | bit7~0 |
---|---|---|---|
0x8150 | 触点1X坐标低8位 | 0x8151 | 触点1X坐标高8位 |
0x8152 | 触点1Y坐标低8位 | 0x8153 | 触点1Y坐标高8位 |
0x8154 | 触点1触摸尺寸低8位 | 0x8155 | 触点1触摸尺寸高8位 |
一般只用到触点的x,y坐标,所以只需要读取0X8150-0X8153的数据,组合即可得到触点坐标。其它四组分别是:0X8158、0X8160、0X8168和0X8170等开头的的16个寄存器组成,分别针对触点2~5的坐标。同样GT911也支持寄存器地址自增,我们只需要发送寄存器组的首地址,然后连续读取即可,GT9147会自动地址自增,从而提高读取速度。
4、初始化流程
4.1、IIC地址选择
GT911作为从设备,其地址有两种选择:
7bit地址 | 8bit写地址 | 8bit读地址 |
---|---|---|
0x5D | 0xBA | 0xBB |
0x14 | 0x28 | 0x29 |
其配置方法如下:
-
将地址配置为0x28/0x29
- 1、将RST引脚设置为上拉推挽输出模式,将INT引脚设置为上拉输入模式
- 2、将RST拉低,延时大于1ms
- 3、将RST拉高,延时大于5ms
- 4、将INT配置为悬浮输入态,RST维持拉高输出
-
将地址配置为0xBA/0xBB
- 1、将RST和INT引脚配置为输出模式并拉低
- 2、延时1ms
- 3、将RST拉高,延时大于5ms
- 4、将INT配置为悬浮输入态,RST维持拉高输出
注意:因为我使用的是查询扫描模式,所以没有配置INT引脚的外部中断模式,如果需要,这里也需要配置。
4.2、更新GT911寄存器配置
就像上面3.2说的,如果刚买回来的触摸屏幕,里面应该是本身就有适配此屏幕的参数配置的,这一条就可以省略直接进行下面的4.3,去读取触摸点坐标值。
注:这点尤为关键,因为你非常容易写进去错误的寄存器配置导致原来的配置被覆盖掉,你还不知道原来的配置参数是多少。
如果你真的需要更新它。
- 1、向命令寄存器(0x8040)写入2,开始软复位
- 2、把配置参数数组(那186个参数)写入寄存器(0x8047-0x8100).
- 1)、 0x8047——这个寄存器中,保存的是当前配置参数的版本号,你新写入配置参数的版本号,必须要比它大或者在相等的情况下,其他参数有变化,否则不会生效。
- 2)、0x80FF——这里需要写入的是从0x8047到0x80FE之间184个数据和的补码,否则那你写入的配置也不会生效。
- 3)、0x8100——用于控制是否将配置保存在GT911的FLASH中,写0,则不保存配置,写1则保存配置。
- 3、往向命令寄存器(0x8040)写入0,结束软复位
5、触摸点坐标值读取
当有触摸发生时,GT911会有3个提示:
- 1、INT会输出上升沿或下降沿信号。(由寄存器配置参数决定)
- 2、状态寄存器0x814E的最高位(buffer状态位)的值变为1,表示数据已准备好。
- 3、状态寄存器0x814E的低4位的值会变为触摸点个数,提示有多少个点被按下。
注意:读完坐标后,要把0x814E寄存器清为0,表示坐标点已读。否则GT911会一直输出INT信号,不继续检测触摸。
读取方法:
- 1、使用INT中断,当发生触摸时,会进入外部中断处理程序,在中断服务中,可以去读取0x8114E中的低四位获取触摸点数,然后再去读去相应寄存器,获取触点坐标,读完后将0x814E寄存器清零。
- 2、轮询读取0x814E寄存器值,若最高位为1,则通过低四位获取触摸点数,然后去读相应触摸点数的坐标值,读完后将0x814E清零。若最高位为0,则退出,等待下次监测。
6、提示
- 因为GT911的触摸检测频率小于100HZ,所以我们采用轮询读取时,读取频率设的小于100HZ即可。
7、代码部分
7.1、gt911.h
#ifndef __GT911_H
#define __GT911_H
#include "sys.h" #include "ctiic.h"
#include "Serial.h"
#include "DWT_Delay.h"
#include "string.h" #include "lcd_init.h"
#define TP_PRES_DOWN 0x80 //触屏被按下
#define TP_CATH_PRES 0x40 //有按键按下了
#define CT_MAX_TOUCH 5 //电容屏支持的点数,固定为5点//触摸屏控制器
typedef struct
{uint16_t x[CT_MAX_TOUCH]; //当前坐标uint16_t y[CT_MAX_TOUCH]; //电容屏有最多5组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用//x[4],y[4]存储第一次按下时的坐标. uint8_t sta; //笔的状态 //b7:按下1/松开0; //b6:0,没有按键按下;1,有按键按下. //b5:保留//b4~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
}_m_tp_dev;extern _m_tp_dev tp_dev; //触屏控制器在touch.c里面定义
//IO操作函数
#define GT_RST PBout(9) //GT911复位引脚
#define GT_INT PBin(8) //GT911中断引脚 //I2C读写命令
#define GT_CMD_WR 0X28 //写命令
#define GT_CMD_RD 0X29 //读命令//GT911 部分寄存器定义
#define GT_CTRL_REG 0X8040 //GT911控制寄存器
#define GT_CFGS_REG 0X8047 //GT911配置起始地址寄存器
#define GT_CHECK_REG 0X80FF //GT911校验和寄存器
#define GT_PID_REG 0X8140 //GT911产品ID寄存器#define GT_GSTID_REG 0X814E //GT911当前检测到的触摸情况
#define GT_TP1_REG 0X8150 //第一个触摸点数据地址
#define GT_TP2_REG 0X8158 //第二个触摸点数据地址
#define GT_TP3_REG 0X8160 //第三个触摸点数据地址
#define GT_TP4_REG 0X8168 //第四个触摸点数据地址
#define GT_TP5_REG 0X8170 //第五个触摸点数据地址 void GT911_Send_Cfg(uint8_t mode); /*更新GT911配置参数*/
uint8_t GT911_WR_Reg(uint16_t reg,uint8_t *buf,uint8_t len); /*向GT911写入一次数据*/
void GT911_RD_Reg(uint16_t reg,uint8_t *buf,uint8_t len); /*从GT911读出一次数据*/
uint8_t GT911_Init(void); /*初始化GT911触摸屏*/
uint8_t GT911_Scan(uint8_t mode); /*扫描触摸屏(采用查询方式)*/
#endif
7.2、GT911.c
#include "gt911.h"_m_tp_dev tp_dev; /*五个触摸点的数据寄存器起始地址*/
const uint16_t GT911_TPX_TBL[5]={GT_TP1_REG,GT_TP2_REG,GT_TP3_REG,GT_TP4_REG,GT_TP5_REG};//GT911配置参数表--适用于耀元鸿2.8寸IPS屏幕。不同尺寸屏幕配置参数会有区别
const uint8_t GT911_CFG_TBL[]=
{
0x61,0xf0,0x00,0x40,0x01,0x05,0x35,0x00,0x02,0x08,
0x1e,0x08,0x50,0x3c,0x0f,0x05,0x00,0x00,0x00,0x00,
0x50,0x00,0x00,0x18,0x1a,0x1e,0x14,0x87,0x27,0x0a,
0x4b,0x4d,0xd3,0x07,0x00,0x00,0x00,0x02,0x32,0x1c,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x32,0x00,0x00,0x2a,0x32,0x64,0x94,0xd5,0x02,0x07,0x00,0x00,0x04,
0xa5,0x35,0x00,0x91,0x3d,0x00,0x80,0x46,0x00,0x70,
0x51,0x00,0x63,0x5d,0x00,0x63,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x14,0x12,0x10,0x0e,0x0c,0x0a,0x08,0x06,
0x04,0x02,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0x00,0x02,0x04,0x06,0x08,0x0a,0x0c,0x24,0x22,0x21,0x20,0x1f,0x1e,0x1d,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,
}; /*
* 函数名称: GT911_Send_Cfg
* 功能描述: 更新GT911配置参数
* 传入参数: mode:0,参数不保存到flash
* 1:参数保存到flash
* 返回值: 0,成功;1,失败.
*/
void GT911_Send_Cfg(uint8_t mode)
{uint8_t buf[2];uint8_t i=0;buf[0]=0;buf[1]=mode; //是否写入到GT911 FLASH? 即是否掉电保存for(i=0;i<sizeof(GT911_CFG_TBL);i++)buf[0]+=GT911_CFG_TBL[i];//计算校验和buf[0]=(~buf[0])+1;GT911_WR_Reg(GT_CFGS_REG,(uint8_t*)GT911_CFG_TBL,sizeof(GT911_CFG_TBL));//发送寄存器配置GT911_WR_Reg(GT_CHECK_REG,buf,2);//写入校验和,和配置更新标记
} /*
* 函数名称: GT911_WR_Reg
* 功能描述: 向GT911写入一次数据
* 传入参数: reg:起始寄存器地址
* buf:数据缓缓存区
* len:写数据长度
* 返回值: 0,成功;1,失败.
*/
uint8_t GT911_WR_Reg(uint16_t reg,uint8_t *buf,uint8_t len)
{uint8_t i;uint8_t ret=0;CT_IIC_Start(); CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令 CT_IIC_Wait_Ack();CT_IIC_Send_Byte(reg>>8); //发送高8位地址CT_IIC_Wait_Ack(); CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址CT_IIC_Wait_Ack(); for(i=0;i<len;i++){ CT_IIC_Send_Byte(buf[i]); //发数据ret=CT_IIC_Wait_Ack();if(ret)break; }CT_IIC_Stop(); //产生一个停止条件 return ret;
}/*
* 函数名称: GT911_RD_Reg
* 功能描述: 从GT911读出一次数据
* 传入参数: reg:起始寄存器地址
* buf:数据缓缓存区
* len:读数据长度
* 返回值: 无
*/
void GT911_RD_Reg(uint16_t reg,uint8_t *buf,uint8_t len)
{uint8_t i; CT_IIC_Start(); CT_IIC_Send_Byte(GT_CMD_WR); //发送写命令 CT_IIC_Wait_Ack();CT_IIC_Send_Byte(reg>>8); //发送高8位地址CT_IIC_Wait_Ack(); CT_IIC_Send_Byte(reg&0XFF); //发送低8位地址CT_IIC_Wait_Ack(); CT_IIC_Start(); CT_IIC_Send_Byte(GT_CMD_RD); //发送读命令 CT_IIC_Wait_Ack(); for(i=0;i<len;i++){ buf[i]=CT_IIC_Read_Byte(i==(len-1)?0:1); //发数据 } CT_IIC_Stop();//产生一个停止条件
} /*
* 函数名称: GT911_Init
* 功能描述: 初始化GT911触摸屏
* 传入参数: 无
* 返回值: 0,初始化成功;1,初始化失败
*/uint8_t GT911_Init(void)
{// uint8_t zancun[190]; /*测试使用*/
// uint8_t i;
// uint8_t z;uint8_t temp[5]; GPIO_InitTypeDef GPIO_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB,C时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 ;//PB1设置为上拉输入GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//输入模式GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHzGPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;//PC13设置为推挽输出GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//输出模式GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 CT_IIC_Init(); //初始化电容屏的I2C总线 GT_RST=0; //复位DWT_DelayMS(1);GT_RST=1; //释放复位 DWT_DelayMS(5); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化 DWT_DelayMS(10); GT911_RD_Reg(GT_PID_REG,temp,4);//读取产品IDtemp[4]=0;printf("CTP ID:%s\r\n",temp); //打印IDif(strcmp((char*)temp,"911")==0)//ID==9147{temp[0]=0X02; GT911_WR_Reg(GT_CTRL_REG,temp,1);//软复位GT911GT911_RD_Reg(GT_CFGS_REG,temp,1);//读取GT_CFGS_REG寄存器/*可用来读取GT911本地0x8047-0x80FE的寄存器参数*/
/*GT911_RD_Reg(GT_CFGS_REG,(uint8_t *)zancun,186); //读取出配置寄存器值for(i=0;i<186;i++){if(i<185){z+=zancun[i];}if(i==185){printf("校验值1:%x\r\n",z);}printf("%x,",zancun[i]); //打印出配置寄存器值}
*/if(temp[0]<0X62)//默认版本比较低,需要更新flash配置{printf("Default Ver:%x\r\n",temp[0]);
// GT911_Send_Cfg(1);//更新并保存配置}DWT_DelayMS(10);temp[0]=0X00; GT911_WR_Reg(GT_CTRL_REG,temp,1);//结束复位 return 0;} return 0;
}/*
* 函数名称: GT911_Scan
* 功能描述: 扫描触摸屏(采用查询方式)
* 传入参数: mode:0,正常扫描.
* 传出参数: 无
* 返回值: 当前触屏状态.0,触屏无触摸;1,触屏有触摸
*/
uint8_t GT911_Scan(uint8_t mode)
{uint8_t buf[4]; /*触屏坐标信息存储缓冲区*/uint8_t i=0;uint8_t res=0; /*返回触摸状态*/uint8_t temp; uint8_t tempsta; /*保存当前触摸状态*/static uint8_t t=0;//控制查询间隔,从而降低CPU占用率 t++;if((t%10)==0||t<10)//空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率{GT911_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态 temp=0;GT911_WR_Reg(GT_GSTID_REG,&temp,1);//清标志 if((mode&0XF)&&((mode&0XF)<6)) {temp=0XFF<<(mode&0XF); //将点的个数转换为1的位数,匹配tp_dev.sta定义 tempsta=tp_dev.sta; //保存当前的tp_dev.sta值tp_dev.sta=(~temp)|TP_PRES_DOWN|TP_CATH_PRES; tp_dev.x[4]=tp_dev.x[0]; //保存触点0的数据tp_dev.y[4]=tp_dev.y[0];for(i=0;i<5;i++){if(tp_dev.sta&(1<<i)) //触摸有效?{GT911_RD_Reg(GT911_TPX_TBL[i],buf,4); //读取XY坐标值 tp_dev.y[i]=240-(((uint16_t)buf[1]<<8)+buf[0]);tp_dev.x[i]=((uint16_t)buf[3]<<8)+buf[2];printf("x[%d]:%d,y[%d]:%d\r\n",i,tp_dev.x[i],i,tp_dev.y[i]);} } res=1;if(tp_dev.x[0]>LCD_W||tp_dev.y[0]>LCD_H)//非法数据(坐标超出了){ if((mode&0XF)>1) //有其他点有数据,则复第二个触点的数据到第一个触点.{tp_dev.x[0]=tp_dev.x[1];tp_dev.y[0]=tp_dev.y[1];t=0; //触发一次,则会最少连续监测10次,从而提高命中率}else //非法数据,则忽略此次数据(还原原来的) {tp_dev.x[0]=tp_dev.x[4];tp_dev.y[0]=tp_dev.y[4];mode=0X80; tp_dev.sta=tempsta; //恢复tp_dev.sta}}else t=0; //触发一次,则会最少连续监测10次,从而提高命中率}}if((mode&0X8F)==0X80)//无触摸点按下{ if(tp_dev.sta&TP_PRES_DOWN) //之前是被按下的{tp_dev.sta&=~(1<<7); //标记按键松开}tp_dev.x[0]=0xffff;tp_dev.y[0]=0xffff;tp_dev.sta&=0XE0; //清除点有效标记 } if(t>240)t=10;//重新从10开始计数return res;
}