大家好,又见面了,我是你们的朋友全栈君。
一、介绍与硬件连接
GT911、GT928、GT9147都属于GT9系列非单层多点触控芯片,他们支持的触控点数不同(GT928支持10个点、GT911支持5个点)、驱动和感应通道也可能不同。可是他们的寄存器和IIC通讯时序是相同的,也就是说驱动程序是兼容的。
与主机的接口共有6PIN,分别为:VDD、GND、SCL、SDA、INT、RESET。INT、RESET不需要接上下拉电阻,可与主机直连。SCL、SDA需要接上拉电阻4.7K,毕竟400KHz的通信频率,没有上拉可能导致SCL、SDA边沿不够陡峭。
RST是复位引脚,拉低100us以上,即可复位。正常工作时,应该保持拉高。
INT是GT9xx的触摸信号输出引脚,在正常工作时,主机端要设置为悬浮输入,即不上下拉(GT9xx的驱动能力有限,如果外部上下拉,GT9xx可能驱动不了)。当有触摸发生时,INT引脚会输出上升沿或下降沿(内部寄存器可以配置),主机端可以一直读取INT脚的电平信号,也可以用端口外部中断检测。
二、初始化
1、第一步,GT9xx的IIC地址选择
GT9 系列在通信中始终作为从设备,其I2C设备地址由7 位设备地址加1 位读写控制位组成,高7 位为地址,bit 0为读写 控制位。GT9 系列有两个从设备地址可供选择:
设置方法为:
把RST、INT引脚配置为输出模式。
A、把RST、INT拉低,延时10ms,把INT拉高,延时100us,把RST拉高,就可以把IIC地址设为0x28/0x29
B、把RST、INT拉低,延时10ms,把RST拉高,就可以把IIC地址设为0xBA/0xBB
2、第二步,把INT设置为悬浮输入模式(如果要用外部中断检测INT,也要在这里配置好),RST保持拉高输出。
3、第三步,更新GT9xx寄存器配置,如果不需要更新,可以直接跳过。
1)、往控制命令寄存器(0x8040)写入2,软复位GT9xx。
2)、把寄存器配置数组写入(0x8047-0x8100),一共186个寄存器,这些寄存器的功能可以查看数据手册。需要注意的是:
A,0X8047寄存器用于指示配置文件版本号,程序写入的版本号,必须大于等于GT9xx本地保存的版本号,才可以更新配置,否则当前写入配置无效。
B,0X80FF寄存器用于存储校验和,使得0X8047-0X80FF之间所有数据之和为0,如果校验不通过,当前配置无效。
C,0X8100用于控制是否将配置保存在本地,写0,则不保存配置,写1则保存配置。
3)、往控制命令寄存器(0x8040)写入0,结束软复位。
4、完成初始化过程。
三、读取触摸点坐标值
当有触摸发生时,GT9xx会有3个提示:
1、INT会输出上升沿或下降沿信号。
2、状态寄存器0x814E的最高位(buffer状态位)的值变为1,表示数据已准备好。
3、状态寄存器0x814E的低4位的值会变为触摸点个数,提示有多少个点被按下。
读完坐标后,要把0x814E寄存器清为0,表示坐标点已读。否则GT9xx会一直输出INT信号,不继续检测触摸。切记!
因此对应的,我们有3种读取方法:
1、轮询读取0x814E寄存器的值,如果最高位(buffer状态位)的值变为1,则依照低4位来读取相应个数的坐标值,读完后把0x814E寄存器清为0。如果最高位为0,则退出,继续下一次检测。
2、使用INT中断,当有触摸发生,INT输出信号触发外部中断。在中断服务程序中读取0x814E的低4位得到触摸点数,再去读取相应个数的坐标值,读完后把0x814E寄存器清为0。
3、使用IIC的DMA,因为IIC的通信频率是400K,假如只有一个触摸点,那么把这个触摸点的坐标读出来需要:S ADR_W ACK Reg_H ACK Reg_L ACK E S ADR_R ACK DATA_1 ACK DATA_2 ACK DATA_3 ACK DATA_4 NACK E,一共76*1/400,190us,还没有加上检测的时间,因此直接读取IIC的效率是很低的。使用DMA的方式,只需要把数据放入、取出就可以了,时间可以缩短到几个us。具体方法是,开两个DMA通道,一个用于发送,一个用于接收。发送通道负责S ADR_W ACK Reg_H ACK Reg_L ACK E,接收通道负责S ADR_R ACK DATA_1 ACK DATA_2 ACK DATA_3 ACK DATA_4 ACK DATA_5 ACK DATA_6 NACK E,根据自己的使用情况确定好需要读取多少个坐标,比如只需要读取1个坐标点,那么从0x804E开始读6个字节就行了,读出来后根据0x804E的值来判断坐标是否有效,如果有效,这6个字节的后4位就是坐标值,取完坐标值,再通过DMA发送通道清除0x804E。
四、其他提示
1、GT9xx的触摸检测频率为100Hz,因此使用DMA或者轮询时,读取频率为100Hz就可以了,读再快也没有意义了。
2、检测触摸是否一直按着没放,需要做一下额外的处理。假如按下了一个点,一直不放,读取频率为100Hz,发生的情况如下:触摸按下后,0x814E的最高位会变为1,读取坐标后,把0x814E清为0,再去读取0x814E,0x814E的最高位会变为0,过一会儿读到的0x814E的最高位才会变为1。也就是说,读完坐标,清零0x814E后,要等一会儿触摸点才能准备好,即0x814E的最高位被清零后,要等一会儿才变为1,可是这段时间内,触摸一直是按下状态的。0x814E值的变化情况如下:
如果不处理的话,一直按下的状态就会变为按下放开、按下放开。。。
因此处理为:
定义按下状态变量Press_Sta,按下计数变量Press_Times。
当0x814E的最高位变为1时,Press_Sta=1表示被按下,同时Press_Times=0。
当0x814E的最高位变为0时,Press_Times 。当Press_Times>8,Press_Sta=0表示触摸放开。(8可以自己调整)
编程手册链接:https://download.csdn.net/download/qq_33822946/10753049
轮询方式的参考程序(来自正点原子)
代码语言:javascript复制#include "gt9147.h"
#include "touch.h"
#include "ctiic.h"
#include "usart.h"
#include "delay.h"
#include "string.h"
#include "lcd.h"
//IO操作函数
#define GT_RST(n) (n?HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOI,GPIO_PIN_8,GPIO_PIN_RESET))//GT9147复位引脚
#define GT_INT HAL_GPIO_ReadPin(GPIOH,GPIO_PIN_7) //GT9147中断引脚
//I2C读写命令
#define GT_CMD_WR 0X28 //写命令
#define GT_CMD_RD 0X29 //读命令
//GT9147 部分寄存器定义
#define GT_CTRL_REG 0X8040 //GT9147控制寄存器
#define GT_CFGS_REG 0X8047 //GT9147配置起始地址寄存器
#define GT_CHECK_REG 0X80FF //GT9147校验和寄存器
#define GT_PID_REG 0X8140 //GT9147产品ID寄存器
#define GT_GSTID_REG 0X814E //GT9147当前检测到的触摸情况
#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 //第五个触摸点数据地址
//GT9147配置参数表
//第一个字节为版本号(0X60),必须保证新的版本号大于等于GT9147内部
//flash原有版本号,才会更新配置.
const u8 GT9147_CFG_TBL[]=
{
0X60,0XE0,0X01,0X20,0X03,0X05,0X35,0X00,0X02,0X08,
0X1E,0X08,0X50,0X3C,0X0F,0X05,0X00,0X00,0XFF,0X67,
0X50,0X00,0X00,0X18,0X1A,0X1E,0X14,0X89,0X28,0X0A,
0X30,0X2E,0XBB,0X0A,0X03,0X00,0X00,0X02,0X33,0X1D,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X32,0X00,0X00,
0X2A,0X1C,0X5A,0X94,0XC5,0X02,0X07,0X00,0X00,0X00,
0XB5,0X1F,0X00,0X90,0X28,0X00,0X77,0X32,0X00,0X62,
0X3F,0X00,0X52,0X50,0X00,0X52,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X0F,
0X0F,0X03,0X06,0X10,0X42,0XF8,0X0F,0X14,0X00,0X00,
0X00,0X00,0X1A,0X18,0X16,0X14,0X12,0X10,0X0E,0X0C,
0X0A,0X08,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X29,0X28,0X24,0X22,0X20,0X1F,0X1E,0X1D,
0X0E,0X0C,0X0A,0X08,0X06,0X05,0X04,0X02,0X00,0XFF,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,0XFF,
0XFF,0XFF,0XFF,0XFF,
};
//发送GT9147配置参数
//mode:0,参数不保存到flash
// 1,参数保存到flash
u8 GT9147_Send_Cfg(u8 mode)
{
u8 buf[2];
u8 i=0;
buf[0]=0;
buf[1]=mode; //是否写入到GT9147 FLASH? 即是否掉电保存
for(i=0;i<sizeof(GT9147_CFG_TBL);i )buf[0] =GT9147_CFG_TBL[i];//计算校验和
buf[0]=(~buf[0]) 1;
GT9147_WR_Reg(GT_CFGS_REG,(u8*)GT9147_CFG_TBL,sizeof(GT9147_CFG_TBL));//发送寄存器配置
GT9147_WR_Reg(GT_CHECK_REG,buf,2);//写入校验和,和配置更新标记
return 0;
}
//向GT9147写入一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:写数据长度
//返回值:0,成功;1,失败.
u8 GT9147_WR_Reg(u16 reg,u8 *buf,u8 len)
{
u8 i;
u8 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;
}
//从GT9147读出一次数据
//reg:起始寄存器地址
//buf:数据缓缓存区
//len:读数据长度
void GT9147_RD_Reg(u16 reg,u8 *buf,u8 len)
{
u8 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();//产生一个停止条件
}
//初始化GT9147触摸屏
//返回值:0,初始化成功;1,初始化失败
u8 GT9147_Init(void)
{
u8 temp[5];
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOH时钟
__HAL_RCC_GPIOI_CLK_ENABLE(); //开启GPIOI时钟
//PH7
GPIO_Initure.Pin=GPIO_PIN_7; //PH7
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
//PI8
GPIO_Initure.Pin=GPIO_PIN_8; //PI8
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
HAL_GPIO_Init(GPIOI,&GPIO_Initure); //初始化
CT_IIC_Init(); //初始化电容屏的I2C总线
GT_RST(0); //复位
delay_ms(10);
GT_RST(1); //释放复位
delay_ms(10);
GPIO_Initure.Pin=GPIO_PIN_7; //PH7
GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入
GPIO_Initure.Pull=GPIO_NOPULL; //不带上下拉,浮空输入
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化
delay_ms(100);
GT9147_RD_Reg(GT_PID_REG,temp,4);//读取产品ID
temp[4]=0;
printf("CTP ID:%srn",temp); //打印ID
if(strcmp((char*)temp,"9147")==0)//ID==9147
{
temp[0]=0X02;
GT9147_WR_Reg(GT_CTRL_REG,temp,1);//软复位GT9147
GT9147_RD_Reg(GT_CFGS_REG,temp,1);//读取GT_CFGS_REG寄存器
if(temp[0]<0X60)//默认版本比较低,需要更新flash配置
{
printf("Default Ver:%drn",temp[0]);
if(lcddev.id==0X5510)GT9147_Send_Cfg(1);//更新并保存配置
}
delay_ms(10);
temp[0]=0X00;
GT9147_WR_Reg(GT_CTRL_REG,temp,1);//结束复位
return 0;
}
return 1;
}
const u16 GT9147_TPX_TBL[5]={GT_TP1_REG,GT_TP2_REG,GT_TP3_REG,GT_TP4_REG,GT_TP5_REG};
//扫描触摸屏(采用查询方式)
//mode:0,正常扫描.
//返回值:当前触屏状态.
//0,触屏无触摸;1,触屏有触摸
u8 GT9147_Scan(u8 mode)
{
u8 buf[4];
u8 i=0;
u8 res=0;
u8 temp;
u8 tempsta;
static u8 t=0;//控制查询间隔,从而降低CPU占用率
t ;
if((t)==0||t<10)//空闲时,每进入10次CTP_Scan函数才检测1次,从而节省CPU使用率
{
GT9147_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态
if(mode&0X80&&((mode&0XF)<6))
{
temp=0;
GT9147_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)) //触摸有效?
{
GT9147_RD_Reg(GT9147_TPX_TBL[i],buf,4); //读取XY坐标值
if(lcddev.id==0X5510) //4.3寸800*480 MCU屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.y[i]=((u16)buf[1]<<8) buf[0];
tp_dev.x[i]=800-(((u16)buf[3]<<8) buf[2]);
}else
{
tp_dev.x[i]=((u16)buf[1]<<8) buf[0];
tp_dev.y[i]=((u16)buf[3]<<8) buf[2];
}
}else if(lcddev.id==0X4342) //4.3寸480*272 RGB屏
{
if(tp_dev.touchtype&0X01)//横屏
{
tp_dev.x[i]=(((u16)buf[1]<<8) buf[0]);
tp_dev.y[i]=(((u16)buf[3]<<8) buf[2]);
}else
{
tp_dev.y[i]=((u16)buf[1]<<8) buf[0];
tp_dev.x[i]=272-(((u16)buf[3]<<8) buf[2]);
}
}
}
}
res=1;
if(tp_dev.x[0]>lcddev.width||tp_dev.y[0]>lcddev.height)//非法数据(坐标超出了)
{
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); //标记按键松开
}else //之前就没有被按下
{
tp_dev.x[0]=0xffff;
tp_dev.y[0]=0xffff;
tp_dev.sta&=0XE0; //清除点有效标记
}
}
if(t>240)t=10;//重新从10开始计数
return res;
}
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/148484.html原文链接:https://javaforall.cn