STM32H743-ARM例程33-TOUCH
目录
- 实验平台
- 触摸屏
- 电阻触摸屏原理
- 电容触摸屏原理
- 触摸屏IC控制
- 电容触摸芯片GT911
- GT911的I2C通讯
- GT911功能描述
- 电阻触摸芯片NS2009
- NS2009的I2C通讯
- STM32CubeMX生成工程
- 实验代码
- 实验现象
实验平台
硬件:银杏科技GT7000双核心开发板-ARM-STM32H743XIH6,银杏科技iToolXE仿真器,7寸液晶显示模块
软件:最新版本STM32CubeH7固件库,STM32CubeMX v6.10.0,开发板环境MDK v5.35
触摸屏
触摸屏,也称为“触控屏”,是一种可接收触控等输入信号的感应式液晶显示装置。当用户用手指或其他物体触摸屏幕上的特定位置时,系统会根据预先编程的程式驱动各种连接装置,从而实现人机交互。它是最直观、最自然的人机交互方式之一。
根据触摸屏的检测原理,主要分为电阻式触摸屏和电容式触摸屏。 相对来说,电阻屏造价便宜,能适应较恶劣的环境,但它只支持单点触控(一次只能检测面板上的一个触摸位置),触摸时需要一定的压力, 使用久了容易造成表面磨损,影响寿命;而电容屏具有支持多点触控、检测精度高的特点,电容屏通过与导电物体产生的电容效应来检测触摸动作, 只能感应导电物体的触摸,湿度较大或屏幕表面有水珠时会影响电容屏的检测效果。
触摸屏实际上是在液晶屏上面贴了一层大小相等的薄膜,这个薄膜能够感知触碰,根据薄膜反馈的触摸位置,我们就能知道用户触碰在屏幕的什么位地方了。

电阻触摸屏原理
电阻式触摸屏其实就是一种传感器,虽然已经用的不多了,但是还是有过很多的LCD模块采用电阻式触摸屏,这种屏幕可以用四线、五线、七线或八线来产生屏幕偏置电压,同时读回触摸点的电压。
薄膜+玻璃(需要尖锐硬物点击)
(1)要点是薄、透明。前面板硬度稍弱,可以被硬物按下弯曲,后面板硬度很高,不会弯曲。
(2)前面板和后面板在平时没有挨住,在外力按下之下,前面板发生(局部)形变,在这一点上前后面板会挨住。 如下图所示

它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。

当手指触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 X 和 Y 两个方向上产生信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出( X Y )的位置,再根据获得的位置模拟鼠标的方式运作。
电阻式触摸屏都需要一个AD 转换器, 所以一般来说驱动屏幕需要一个控制器芯片。
例如下图所示: 我们先在X+ 和 X-之间加上一个电压,当有人按下触摸屏之后就会在相应的位置形成一个触点,那么此时我们去测量Y+与GND(或者是Y-与GND)之间的电压,那么其实得到的电压值就是发生触点处的电压值,因为电阻是均匀分布的,所以可以算出该点在x方向上的位置;同理测量Y轴也是一样的道理。

电容触摸屏原理
与电阻式触摸屏不同,电容式触摸屏不需要通过压力使触点变形,再通过触点处电压值来检测坐标,它的基本原理和前面定时器章节中介绍的电容按键类似, 都是利用充电时间检测电容大小,从而通过检测出电容值的变化来获知触摸信号。 见图 电容触摸屏基本原理 ,电容屏的最上层是玻璃(不会像电阻屏那样形变), 核心层部分也是由ITO材料构成的,这些导电材料在屏幕里构成了人眼看不见的静电网,静电网由多行X轴电极和多列Y轴电极构成,两个电极之间会形成电容。 触摸屏工作时,X轴电极发出AC交流信号,而交流信号能穿过电容,即通过Y轴能感应出该信号,当交流电穿越时电容会有充放电过程,检测该充电时间可获知电容量。 若手指触摸屏幕,会影响触摸点附近两个电极之间的耦合,从而改变两个电极之间的电容量,若检测到某电容的电容量发生了改变, 即可获知该电容处有触摸动作(这就是为什么它被称为电容式触摸屏以及绝缘体触摸没有反应的原因)。

电容屏ITO层的结构见下图,这是比较常见的形式, 电极由多个菱形导体组成,生产时使用蚀刻工艺在ITO层生成这样的结构。

X轴电极与Y轴电极在交叉处形成电容,即这两组电极构成了电容的两极,这样的结构覆盖了整个电容屏,每个电容单元在触摸屏中都有其特定的物理位置, 即电容的位置就是它在触摸屏的XY坐标。检测触摸的坐标时,第1条X轴的电极发出激励信号,而所有Y轴的电极同时接收信号, 通过检测充电时间可检测出各个Y轴与第1条X轴相交的各个互电容的大小,各个X轴依次发出激励信号,重复上述步骤,即可得到整个触摸屏二维平面的所有电容大小。 当手指接近时,会导致局部电容改变,根据得到的触摸屏电容量变化的二维数据表,可以得知每个触摸点的坐标,因此电容触摸屏支持多点触控。
其实电容触摸屏可看作是多个电容按键组合而成,就像机械按键中独立按键和矩阵按键的关系一样,甚至电容触摸屏的坐标扫描方式与矩阵按键都是很相似的。
触摸屏IC控制
有了TFT裸屏后还要配套电阻触摸板或者电容触摸板才可以获取触摸信息。触摸板是贴到TFT屏上面的,然后再通过电阻触摸芯片就可以获取电阻触摸板的信息,通过电容触摸芯片采集电容触摸板的信息。目前市面上常用的电阻触摸IC是STMPE811,电容触摸IC是GT811、GT911和FT5X06。其中,电阻触摸和电容触摸两者的区别如下:
电阻触摸芯片STMPE811其实就是ADC,返回的是ADC数值,而电容触摸芯片GT811、GT911和FT5X06返回的是显示屏实际的坐标值。
使用电阻触摸芯片STMPE811需要做触摸校准,而使用电容触摸芯片GT811、GT911和FT5X06是自动校准的,无需手动校准。
电容触摸芯片GT911
在本实验中,选用电容触摸芯片GT911来实现电容触摸功能。
电容触摸相比电阻触摸简单,因为电容触摸不需要做触摸校准,而且用的是触摸板和触摸芯片一体的,也不需要做寄存器初始化配置,上电后直接读取参数即可。
GT911、GT928、GT9147都属于GT9系列非单层多点触控芯片,他们支持的触控点数不同(GT928支持10个点、GT911支持5个点)、驱动和感应通道也可能不同。可是他们的寄存器和IIC通讯时序是相同的,也就是说驱动程序是兼容的。
GT911是专为7”~8”设计的新一代5点电容触控方案,拥有26个驱动通道和14个感应通道,以满足更高的touch精度要求。GT911可同时识别5个触摸点位的实时准确位置,移动轨迹及触摸面积。并可根据主控需要,读取相应点数的触摸信息。
与主机的接口共有6PIN,分别为:VDD、GND、SCL、SDA、INT、RESET。
- INT、RESET不需要接上下拉电阻,可与主机直连。
- SCL、SDA需要接上拉电阻4.7K,毕竟400KHz的通信频率,没有上拉可能导致SCL、SDA边沿不够陡峭。
- RST是复位引脚,拉低100us以上,即可复位。正常工作时,应该保持拉高。
- INT是GT9xx的触摸信号输出引脚,在正常工作时,主机端要设置为悬浮输入,即不上下拉(GT9xx的驱动能力有限,如果外部上下拉,GT9xx可能驱动不了)。当有触摸发生时,INT引脚会输出上升沿或下降沿(内部寄存器可以配置),主机端可以一直读取INT脚的电平信号,也可以用端口外部中断检测。
GT911的I2C通讯
GT911提供标准的I2C通讯接口,由SCL和SDA与主CPU进行通讯。在系统中GT911始终作为从设备,所有通讯都是由主CPU发起,建议通讯速度为400Kbps或以下。其支持的I2C硬件电路支持时序如下:
GT911的I2C从设备地址有两组,分别为0xBA/0xBB和0x28/0x29。主控在上电初始化时控制Reset和INT口状态进行设定,设定方法及时序图如下:
上电时序图:


- 数据传输(以设备地址为0xBA/0xBB为例)
通讯总是由主 CPU 发起,有效的起始信号为:在 SCL 保持为“1”时,SDA 上发生由“1”到“0”的跳变。地址信息或数据流均在起始信号之后传输。
所有连接在I2C总线上的从设备,都要检测总线上起始信号之后所发送的8位地址信息,
并做出正确反应。在收到与自己相匹配的地址信息时,GT911在第9个时钟周期,将SDA改为输出口,并置“0”,作为应答信号。若收到不与自己匹配的地址信息,即非0XBA或0XBB,GT911将保持闲置状态。
SDA口上的数据按9个时钟周期串行发送9位数据:8位有效数据+1位接收方发送的应答信号ACK或非应答信号NACK。数据传输在SCL为“1”时有效。
当通讯完成时,由主CPU发送停止信号。停止信号是当SCL为“1”时,SDA状态由“0”到“1”的跳变。 - 对GT911写操作(以设备地址为0xBA/0xBB为例)

上图为主CPU对GT911进行的写操作流程图。首先主CPU产生一个起始信号,然后发送地址信息及读写位信息“0”表示写操作:0XBA。在收到应答后,主CPU发送寄存器的16位地址,随后是8位要写入到寄存器的数据内容GT911寄存器的地址指针会在写操作后自动加1,所以当主CPU需要对连续地址的寄存器进行写操作时,可以在一次写操作中连续写入。写操作完成,主CPU发送停止信号结束当前写操作。
- 对GT911读操作(以设备地址为0xBA/0xBB为例)

上图为主CPU对GT911进行的读操作流程图。首先主CPU产生一个起始信号,然后发送设备地址信息及读写位信息“0”表示写操作:0XBA。在收到应答后,主CPU发送首寄存器的16位地址信息,设置要读取的寄存器地址。在收到应答后,主CPU重新发送一次起始信号,发送读操作:0XBB。收到应答后,主CPU开始读取数据。
GT911同样支持连续的读操作,默认为连续读取数据。主CPU在每收到一个Byte数
据后需发送一个应答信号表示成功接收。在接收到所需的最后一个Byte数据后,主CPU
发送“非应答信号NACK”,然后再发送停止信号结束通讯。
GT911功能描述

- 工作模式
- Normal Mode
GT911在Normal Mode时,最快的坐标刷新周期为7ms-10ms间(依赖于配置信息的设定,配置信息可控周期步进长度为1ms)。
Normal mode状态下,一段时间无触摸事件发生,GT911将自动转入Green mode,以降低功耗。GT911无触摸自动进入Green mode的时间可通过配置信息设置,范围为0~15s,步进为1s。 - Green Mode
在Green mode下,GT911扫描周期约为40ms,若检测到有触摸动作发生,自动进入Normal mode。 - Sleep Mode
主CPU通过I2C命令,使GT911进入Sleep mode(需要先将INT脚输出低电平)。当需要GT911退出Sleep mode时,主机输出一个高电平到INT脚(主机打高INT脚2~5ms),唤醒后GT911将进入Normal mode。
- 中断触发方式
当有触摸时,GT911 每个扫描周期均会通过 INT 脚发出脉冲信号,通知主 CPU 读取坐标信息。主CPU可以通过相关的寄存器位“INT”来设置触发方式。设为“0”表示上升沿触发,即在有用户操作时,GT911会在INT口输出上升沿跳变,通知 CPU;设为“1”表示下降沿触发,即在有用户操作时,GT911会在INT口输出下降沿跳变
电阻触摸芯片NS2009
在本实验中,7.0寸屏幕选用电阻触摸芯片NS2009来实现电阻触摸功能
NS2009是一款带I2C接口的4线制电阻式触摸屏控制电路,内含12位分辨率A/D转换器。NS2009能通过执行两次A/D转换查出被按的屏幕位置, 除此之外,还可以测量加在触摸屏上的压力。在2.7V的典型工作状态下,功耗可小于0.75mW。
引脚功能描述:

| 名称 | 说明 |
|---|---|
| VDD | 电源输入端 |
| GND | 接地 |
| SCL | I2C时钟接口 |
| SDA | I2C数据接口 |
| PENIRQ | 笔接触中断引脚 |
| A0 | I2C地址输入0 |
| XP | XP位置输入端 |
| YP | YP位置输入端 |
| XN | XN位置输入端 |
| YN | YN位置输入端 |
NS2009的I2C通讯
NS2009 数据接口是 I2C 串行接口,满足 I2C 的接口协议,可实现标准模式(100K)、快速模式(400K)或高速模式(3.4M),对 NS2009 的控制分为写、读两种命令格式,写命令用于输入地址和命令字节,让 NS2009 工作在指定的配置和模式下,读命令用于输出 NS2009 的 ADC 转换数据,以便获取相关的测量信息。
1. 写命令时序:

写命令的第一字节为地址字节:

最低位 R/W(bit0),为 0 表示写命令,1 表示读命令
A1(Bit2)和 A0(Bit1)为硬件地址控制位,A1(Bit2)默认是0。A0(Bit1)这1 位必须要和 MSOP-10 封装芯片的第 8 脚电平一致,才能选中对应的 NS2009;
最高 5 位为软件地址位,必须输入固定码“10010”,如图 6 所示。
在第一字节全部被接收后,NS2009 会在第 9 个时钟周期,发出应答信号ACK(0 电平),表示数据已接收。
写命令的第二字节为命令字节:

C3、C2、C1、C0 —— 决定 NS2009 的输入信号配置以及相应的测量功能。PD0 —— 用于控制节能模式和笔中断信号,如下图所示:

M —— 模式选择位,用于设置 ADC 的分辨率。MODE=0,ADC 是12 位模式;MODE=1,ADC是8位模式。 X 位(bir3 和 bit0)为预留位,一般设为 0 在第二字节全部被接收后,NS2009 会在第 18 个时钟周期,发出应答信号ACK(0 电平),表示数据已接收。
2. 读命令时序:

读命令分 3 个字节,第一字节为地址字节,和写命令类似,仅仅是 bit0 为高电平;接下来的2 字节是NS2009输出的 12bit 数据(如果是 8bits 模式,只有 1 字节数据),多余的 4bits 补零。
在 NS2009 接收到第一字节的地址数据后,会在第 9 个时钟周期发出应答信号ACK(0 电平),然后开始输出第一字节的数据,在主机接收到第一字节数据后应发出应答信号 master ACK(0 电平),NS2009 接收到maskerACK后开始发出第二字节的数据,在主机接收到第二字节数据后,不用应答,此时SDA 被上拉至高电平,也就是上图所示的 masker Not ACK 信号。
3. 笔中断输出
在 PD0=0 时,YN 驱动打开,触摸屏的 Y-面板被连到 GND。PENIRQ 输出通过两个开关和XP输入连在一起。在待机状态下,当屏幕上有触摸动作时,XP 输入通过触摸屏下拉到地,PENIRQ 输出低电平,没有触摸动作时,XP 与 GND 断开,PENIRQ 输出高电平。在测量 X、Y 和 Z 坐标的过程中,PENIRQ 输出为低电平;

PD0=1 时,笔中断功能被禁止,不能监测触摸屏上触摸动作。如果要重新使能笔中断功能,需把带有PD0=0的控制字写入 NS2009,如果在最后写入的控制字中包含了 PD0=0,笔中断输出将在写命令完成后使能。
为避免误触发,建议处理器在发控制字给 NS2009 时,屏蔽掉 PENIRQ 的中断。
STM32CubeMX生成工程
我们参考前面章节STM32H743-结合CubeMX新建HAL库MDK工程,打开CubeMX软件,重复步骤不再展示。我们来看配置LTDC部分配置参考ARM例程-LCD
实验代码
1.主函数
int main(void)
{MPU_Config();SCB_EnableICache();SCB_EnableDCache();HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_LTDC_Init();MX_FMC_Init();MX_SDMMC1_SD_Init();MX_USART6_UART_Init();BSP_SDRAM_Init(); //SDRAM初始化uart6.initialize(115200);uart6.printf("\x0c");uart6.printf("\033[1;32;40m");uart6.printf("Hello,I am GT7000!\r\n");gt911.initialize();//帧缓冲区地址映射二维数组for(int i = 0;i < LCD_HEIGHT;i ++)address_sdram[i] = LCD_SDRAM_ADDRESS + (i * LCD_WIDTH) * 2;LCD_ON; //背光开demo(); //运行弹球demowhile (1){}
}
2.gt911相关操作函数
//-----------------Include files-------------------------//
#include "gt911.h"
#include "stm32h7xx.h"
#include "string.h"
#include "i2c_touch.h"
//---------------- Function Prototype ------------------//
static int initialize(void);
static int scan(unsigned char mode);
unsigned char GT911_Send_Cfg(unsigned char mode);
unsigned char GT911_WR_Reg(unsigned short int reg,unsigned char *buf,unsigned char len);
void GT911_RD_Reg(unsigned short int reg,unsigned char *buf,unsigned char len);//-----------------Variable-----------------------------//
GT911_T gt911 = {.initialize = initialize,.scan = scan,.x[0]=800/2,.y[0]=480/2,.x[1]=800/2,.y[1]=480/2,.x[2]=800/2,.y[2]=480/2,.x[3]=800/2,.y[3]=480/2,.x[4]=800/2,.y[4]=480/2,
};
//GT911配置参数表
const unsigned char GT911_CFG_TBL[]=
{ 0x68,0x00,0x04,0x58,0x02,0x05,0x0D,0x00,0x01,0x0A,0x28,0x0F,0x50,0x32,0x03,0x05,0x00,0x00,0x00,0x00,0x00,0x00,0x08,0x17,0x19,0x1C,0x14,0x8C,0x2E,0x0E,0x2A,0x28,0xB5,0x06,0x00,0x00,0x00,0x31,0x02,0x1D,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x78,0x94,0xC5,0x02,0x07,0x00,0x00,0x04,0x9E,0x20,0x00,0x8D,0x25,0x00,0x7F,0x2A,0x00,0x73,0x30,0x00,0x37,0x38,0x00,0x67,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,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10,0x12,0x14,0x16,0x18,0x1A,0x1C,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x06,0x08,0x0A,0x0C,0x0F,0x10,0x12,0x13,0x14,0x16,0x18,0x1C,0x1D,0x1E,0x1F,0x20,0x21,0x22,0x24,0x26,0x28,0x29,0x2A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};
const unsigned short int GT911_TPX_TBL[5]={GT_TP1_REG,GT_TP2_REG,GT_TP3_REG,GT_TP4_REG,GT_TP5_REG};
//-----------------Function-----------------------------//
//发送参数函数
unsigned char GT911_Send_Cfg(unsigned char mode)
{unsigned char buf[2];unsigned char i=0;buf[0]=0;buf[1]=mode;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,(unsigned char*)GT911_CFG_TBL,sizeof(GT911_CFG_TBL));GT911_WR_Reg(GT_CHECK_REG,buf,2);return 0;
}
//GT911写i2c函数
unsigned char GT911_WR_Reg(unsigned short int reg,unsigned char *buf,unsigned char len)
{unsigned char i;unsigned char ret=0;i2c_touch.IIC_Start();i2c_touch.IIC_Send_Byte(GT_CMD_WR);i2c_touch.IIC_Wait_Ack(); i2c_touch.IIC_Send_Byte(reg>>8);i2c_touch.IIC_Wait_Ack();i2c_touch.IIC_Send_Byte(reg&0XFF);i2c_touch.IIC_Wait_Ack(); for(i=0;i<len;i++){ i2c_touch.IIC_Send_Byte(buf[i]);ret=i2c_touch.IIC_Wait_Ack();if(ret)break; }i2c_touch.IIC_Stop(); return ret;
}
//GT911读i2c函数
void GT911_RD_Reg(unsigned short int reg,unsigned char *buf,unsigned char len)
{unsigned char i; i2c_touch.IIC_Start(); i2c_touch.IIC_Send_Byte(GT_CMD_WR);i2c_touch.IIC_Wait_Ack();i2c_touch.IIC_Send_Byte(reg>>8);i2c_touch.IIC_Wait_Ack(); i2c_touch.IIC_Send_Byte(reg&0XFF);i2c_touch.IIC_Wait_Ack(); i2c_touch.IIC_Start(); i2c_touch.IIC_Send_Byte(GT_CMD_RD);i2c_touch.IIC_Wait_Ack();for(i=0;i<len;i++){ buf[i]=i2c_touch.IIC_Read_Byte(i==(len-1)?0:1);} i2c_touch.IIC_Stop(); //产生一个停止条件
}
//GT911初始化函数
int initialize(void)
{unsigned char temp[4] = {0};unsigned char buffer[200];GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOB_CLK_ENABLE(); //使能B时钟//INT初始化设置GPIO_Initure.Pin=GPIO_PIN_3;GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;//快速HAL_GPIO_Init(GPIOB,&GPIO_Initure);//RST初始化设置GPIO_Initure.Pin=GPIO_PIN_2;GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH;//快速HAL_GPIO_Init(GPIOB,&GPIO_Initure);i2c_touch.IIC_Init();//初始化电容屏的I2C总线 RST_OFF; //触摸芯片复位HAL_Delay(100);RST_ON; //触摸芯片释放复位 HAL_Delay(100);//INT初始化设置GPIO_Initure.Pin=GPIO_PIN_3;GPIO_Initure.Mode=GPIO_MODE_INPUT; //输入GPIO_Initure.Pull=GPIO_PULLUP; //上拉GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //快速HAL_GPIO_Init(GPIOB,&GPIO_Initure);HAL_Delay(100);GT911_RD_Reg(GT_PID_REG,temp,4);//读取产品IDif(strcmp((char*)temp,"911")==0)//ID==911{temp[0]=0X02;GT911_WR_Reg(GT_CTRL_REG,temp,1);GT911_RD_Reg(GT_CFGS_REG,buffer,190);if(temp[0] != 0X68){GT911_Send_Cfg(1);//更新并保存配置 }HAL_Delay(100);temp[0]=0X00;GT911_WR_Reg(GT_CTRL_REG,temp,1);//结束复位 return 0;}return 1;
}
//扫描
int scan(unsigned char mode)
{unsigned char buf[4];unsigned char i=0;unsigned char temp;GT911_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态 if(mode&0X80&&((mode&0XF)<6)){temp=0;GT911_WR_Reg(GT_GSTID_REG,&temp,1);//清标志 }if((mode&0XF)&&((mode&0XF)<6)){temp=0XFF<<(mode&0XF); //将点的个数转换为1的位数,匹配tp_dev.sta定义 gt911.sta=(~temp);for(i=0;i<5;i++){if(gt911.sta&(1<<i)){GT911_RD_Reg(GT911_TPX_TBL[i],buf,4); //读取XY坐标值gt911.x[i]=(((unsigned short int)buf[1]<<8)+buf[0]);gt911.y[i]=(((unsigned short int)buf[3]<<8)+buf[2]);if(gt911.x[i] > 994) gt911.x[i] = 994;if(gt911.x[i] < 30) gt911.x[i] = 30;if(gt911.y[i] > 570) gt911.y[i] = 570;if(gt911.y[i] < 30) gt911.y[i] = 30;}} }else{gt911.x[0]=480;gt911.y[0]=272;gt911.x[1]=480;gt911.y[1]=272;gt911.x[2]=480;gt911.y[2]=272;gt911.x[3]=480;gt911.y[3]=272;gt911.x[4]=480;gt911.y[4]=272; }return 0;
}
3.测试demo函数
void demo(void)
{int bg = WHITE; //背景色int colo[7] = {RED, BLUE, YELLOW, GREEN, 0x7BEF,0x0000,0x03E0}; //颜色列表int x=35,y=35; //圆心开始坐标int oldx,oldy,co=0,i,j;int r=30; //圆半径clear_screen(bg); //设置屏幕背景颜色draw_circle(x, y, r, RED, 1); //画圆while(1){gt911.scan(1);uart6.printf("\x0c");uart6.printf("Touch x %5d y %5d\r\n", gt911.x[0], gt911.y[0]);oldx = x;oldy = y;x = gt911.x[0];y = gt911.y[0];draw_circle(x, y, r, colo[co], 1); //画圆for(j = oldx-r-2;j < oldx+r+2;j ++){ for(i = oldy-r-2;i < oldy+r+2;i ++){if(j<=0 || j>=LCD_WIDTH || i<=0 || i>=LCD_HEIGHT)continue;if( (int)(x-j)*(x-j) + (int)(y-i)*(y-i) > (int)r*r ){*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = bg;}}}HAL_Delay(10); //延时}
}
4.画点函数
int set_pixel(int x, int y, int color)
{if(x<0 || x>LCD_WIDTH || y<0 || y>LCD_HEIGHT)return 0;*(volatile unsigned short int *) (address_sdram[y] + (x << 1)) = color;return 0;
}
5.清屏函数
void clear_screen(int color)
{int i,j;for(j = 0;j < LCD_WIDTH;j ++){for(i = 0;i < LCD_HEIGHT;i ++){*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;}}
}
6.画圆函数
int draw_circle(int x, int y, int r, int color, int fill)
{int i,j;if(x<0 || x>LCD_WIDTH || y<0 || y>LCD_HEIGHT)return 0;for(j = x-r;j < x+r;j ++){for(i = y-r;i < y+r;i ++){if(fill == 1){if( (int)(x-j)*(x-j) + (int)(y-i)*(y-i) <= (int)r*r ){*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;}}else{if( (x-i)*(x-i) + (y-j)*(y-j) >= (r-2)*(r) && (x-i)*(x-i) + (y-j)*(y-j) <= (r+1)*(r) ){*(volatile unsigned short int *) (address_sdram[i] + (j << 1)) = color;}}}}return 0;
}
实验现象
屏幕中间小红点会跟随手指在屏幕上移动。
