第29章 电容触摸屏 |
您所在的位置:网站首页 › 触摸屏怎么画图 › 第29章 电容触摸屏 |
第29章 电容触摸屏—触摸画板
全套200集视频教程和1000页PDF教程请到秉火论坛下载:www.firebbs.cn 野火视频教程优酷观看网址:http://i.youku.com/firege
本章参考资料:《STM32F4xx 中文参考手册》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。 关于开发板配套的触摸屏参数可查阅《5.0寸触摸屏面板说明》,触摸面板配套的触摸控制芯片可查阅《电容触控芯片GT9157 Datasheet》及《gt91x编程指南》配套资料获知。对于7寸电容屏,请查阅《电容触摸芯片GT911》相关的数据手册,7寸电容屏的驱动原理与5寸电容屏的类似,仅写入触摸芯片的配置参数有细节差异。
在前面我们学习了如何使用LTDC外设控制液晶屏并用它显示各种图形及文字,利用液晶屏,STM32的系统具有了高级信息输出功能,然而我们还希望有用户友好的输入设备,触摸屏是不二之选,目前大部分电子设备都使用触摸屏配合液晶显示器组成人机交互系统。 29.1 触摸屏简介触摸屏又称触控面板,它是一种把触摸位置转化成坐标数据的输入设备,根据触摸屏的检测原理,主要分为电阻式触摸屏和电容式触摸屏。相对来说,电阻屏造价便宜,能适应较恶劣的环境,但它只支持单点触控(一次只能检测面板上的一个触摸位置),触摸时需要一定的压力,使用久了容易造成表面磨损,影响寿命;而电容屏具有支持多点触控、检测精度高的特点,电容屏通过与导电物体产生的电容效应来检测触摸动作,只能感应导电物体的触摸,湿度较大或屏幕表面有水珠时会影响电容屏的检测效果。 图 291单电阻屏、电阻液晶屏(带触摸控制芯片) 图 292单电容屏、电容液晶屏(带触摸控制芯片) 图 291和图 292分别是带电阻触摸屏及电容触摸屏的两种屏幕,从外观上并没有明显的区别,区分电阻屏与电容屏最直接的方法就是使用绝缘物体点击屏幕,因为电阻屏通过压力能正常检测触摸动作,而该绝缘物体无法影响电容屏所检测的信号,因而无法检测到触摸动作。目前电容式触摸屏被大部分应用在智能手机、平板电脑等电子设备中,而在汽车导航、工控机等设备中电阻式触摸屏仍占主流。 29.1.1 电阻式触摸屏检测原理电阻式的触摸屏结构见图 293。它主要由表面硬涂层、两个ITO层、间隔点以及玻璃底层构成,这些结构层都是透明的,整个触摸屏覆盖在液晶面板上,透过触摸屏可看到液晶面板。表面涂层起到保护作用,玻璃底层起承载的作用,而两个ITO层是触摸屏的关键结构,它们是涂有铟锡金属氧化物的导电层。两个ITO层之间使用间隔点使两层分开,当触摸屏表面受到压力时,表面弯曲使得上层ITO与下层ITO接触,在触点处连通电路。 图 293 电阻式触摸屏结构 两个ITO涂层的两端分别引出X-、X+、Y-、Y+四个电极,见图 294,这是电阻屏最常见的四线结构,通过这些电极,外部电路向这两个涂层可以施加匀强电场或检测电压。 图 294 XY的ITO层结构 当触摸屏被按下时,两个ITO层相互接触,从触点处把ITO层分为两个电阻,且由于ITO层均匀导电,两个电阻的大小与触点离两电极的距离成比例关系,利用这个特性,可通过以下过程来检测坐标,这也正是电阻触摸屏名称的由来,见图 295。 计算X坐标时,在X+电极施加驱动电压Vref,X-极接地,所以X+与X-处形成了匀强电场,而触点处的电压通过Y+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点X坐标与屏宽度之比,从而:
计算Y坐标时,在Y+电极施加驱动电压Vref,Y-极接地,所以Y+与Y-处形成了匀强电场,而触点处的电压通过X+电极采集得到,由于ITO层均匀导电,触点电压与Vref之比等于触点Y坐标与屏高度之比,从而:
图 295 触摸检测等效电路 为了方便检测触摸的坐标,一些芯片厂商制作了电阻屏专用的控制芯片,控制上述采集过程、采集电压,外部微控制器直接与触摸控制芯片通讯直接获得触点的电压或坐标。如图 291中我们生产的这款3.2寸电阻触摸屏就是采用XPT2046芯片作为触摸控制芯片,XPT2046芯片控制4线电阻触摸屏,STM32与XPT2046采用SPI通讯获取采集得的电压,然后转换成坐标。 29.1.2 电容式触摸屏检测原理与电阻式触摸屏不同,电容式触摸屏不需要通过压力使触点变形,再通过触点处电压值来检测坐标,它的基本原理和前面定时器章节中介绍的电容按键类似,都是利用充电时间检测电容大小,从而通过检测出电容值的变化来获知触摸信号。见图 296,电容屏的最上层是玻璃(不会像电阻屏那样形变),核心层部分也是由ITO材料构成的,这些导电材料在屏幕里构成了人眼看不见的静电网,静电网由多行X轴电极和多列Y轴电极构成,两个电极之间会形成电容。触摸屏工作时,X轴电极发出AC交流信号,而交流信号能穿过电容,即通过Y轴能感应出该信号,当交流电穿越时电容会有充放电过程,检测该充电时间可获知电容量。若手指触摸屏幕,会影响触摸点附近两个电极之间的耦合,从而改变两个电极之间的电容量,若检测到某电容的电容量发生了改变,即可获知该电容处有触摸动作(这就是为什么它被称为电容式触摸屏以及绝缘体触摸没有反应的原因)。 图 296 电容触摸屏基本原理 电容屏ITO层的结构见图 297,这是比较常见的形式,电极由多个菱形导体组成,生产时使用蚀刻工艺在ITO层生成这样的结构。 图 297 电容触摸屏的ITO层结构 X轴电极与Y轴电极在交叉处形成电容,即这两组电极构成了电容的两极,这样的结构覆盖了整个电容屏,每个电容单元在触摸屏中都有其特定的物理位置,即电容的位置就是它在触摸屏的XY坐标。检测触摸的坐标时,第1条X轴的电极发出激励信号,而所有Y轴的电极同时接收信号,通过检测充电时间可检测出各个Y轴与第1条X轴相交的各个互电容的大小,各个X轴依次发出激励信号,重复上述步骤,即可得到整个触摸屏二维平面的所有电容大小。当手指接近时,会导致局部电容改变,根据得到的触摸屏电容量变化的二维数据表,可以得知每个触摸点的坐标,因此电容触摸屏支持多点触控。 其实电容触摸屏可看作是多个电容按键组合而成,就像机械按键中独立按键和矩阵按键的关系一样,甚至电容触摸屏的坐标扫描方式与矩阵按键都是很相似的。 29.2 电容触摸屏控制芯片相对来说,电容屏的坐标检测比电阻屏的要复杂,因而它也有专用芯片用于检测过程,下面我们以本章重点讲述的电容屏使用的触控芯片GT9157为例进行讲解,关于它的详细说明可从《gt91x编程指南》和《电容触控芯片GT9157》文档了解。(7寸屏使用GT911触控芯片,原理类似) 29.2.1 GT9157芯片的引脚GT9157芯片的外观可以图 292中找到,其内部结构框图见图 298。 图 298 GT9157结构框图 该芯片对外引出的信号线介绍如下: 表 291 GT9157信号线说明 信号线 说明 AVDD、AVDD18、DVDD12、VDDDIO、GND 电源和地 Driving channels 激励信号输出的引脚,一共有0-25个引脚,它连接到电容屏ITO层引出的各个激励信号轴 Sensing channels 信号检测引脚,一共有0-13个引脚,它连接到电容屏ITO层引出的各个电容量检测信号轴 I2C I2C通信信号线,包含SCL与SDA,外部控制器通过它与GT9157芯片通讯,配置GT9157的工作方式或获取坐标信号 INT 中断信号,GB9157芯片通过它告诉外部控制器有新的触摸事件 /RSTB 复位引脚,用于复位GT9157芯片;在上电时还与INT引脚配合设置IIC通讯的设备地址 若您把电容触摸屏与液晶面板分离开来,在触摸面板的背面,可看到它的边框有一些电路走线,它们就是触摸屏ITO层引出的XY轴信号线,这些信号线分别引出到GT9157芯片的Driving channels及Sensing channels引脚中。也正是因为触摸屏有这些信号线的存在,所以手机厂商追求的屏幕无边框是比较难做到的。 29.2.2 上电时序与I2C设备地址GT9157触控芯片有两个备选的I2C通讯地址,这是由芯片的上电时序设定的,见图 299。上电时序有Reset引脚和INT引脚生成,若Reset引脚从低电电平转变到高电平期间,INT引脚为高电平的时候,触控芯片使用的I2C设备地址为0x28/0x29(8位写、读地址),7位地址为0x14;若Reset引脚从低电电平转变到高电平期间,INT引脚一直为低电平,则触控芯片使用的I2C设备地址为0xBA/0xBB(8位写、读地址),7位地址为0x5D。 图 299 GT9157的上电时序及I2C设备地址 29.2.3 寄存器配置上电复位后,GT9157芯片需要通过外部主控芯片加载寄存器配置,设定它的工作模式,这些配置通过I2C信号线传输到GT9157,它的配置寄存器地址都由两个字节来表示,这些寄存器的地址从0x8047-0x8100,一般来说,我们实际配置的时候会按照GT9157生产厂商给的默认配置来控制芯片,仅修改部分关键寄存器,其中部分寄存器说明见图 2910。 图 2910 部分寄存器配置说明 这些寄存器介绍如下: (1) 配置版本寄存器 0x8047配置版本寄存器,它包含有配置文件的版本号,若新写入的版本号比原版本大,或者版本号相等,但配置不一样时,才会更新配置文件到寄存器中。其中配置文件是指记录了寄存器0x8048-0x80FE控制参数的一系列数据。 为了保证每次都更新配置,我们一般把配置版本寄存器设置为"0x00",这样版本号会默认初始化为'A',这样每次我们修改其它寄存器配置的时候,都会写入到GT9157中。 (2) X、Y分辨率 0x8048-0x804B寄存器用于配置触控芯片输出的XY坐标的最大值,为了方便使用,我们把它配置得跟液晶面板的分辨率一致,这样就能使触控芯片输出的坐标一一对应到液晶面板的每一个像素点了。 (3) 触点个数 0x804C触点个数寄存器用于配置它最多可输出多少个同时按下的触点坐标,这个极限值跟触摸屏面板有关,如我们本章实验使用的触摸面板最多支持5点触控。 (4) 模式切换 0x804D模式切换寄存器中的X2Y位可以用于交换XY坐标轴;而INT触发方式位可以配置不同的触发方式,当有触摸信号时,INT引脚会根据这里的配置给出触发信号。 (5) 配置校验 0x80FF配置校验寄存器用于写入前面0x8047-0x80FE寄存器控制参数字节之和的补码,GT9157收到前面的寄存器配置时,会利用这个数据进行校验,若不匹配,就不会更新寄存器配置。 (6) 配置更新 0x8100配置更新寄存器用于控制GT9157进行更新,传输了前面的寄存器配置并校验通过后,对这个寄存器写1,GT9157会更新配置。 29.2.4 读取坐标信息 坐标寄存器上述寄存器主要是由外部主控芯片给GT9157写入配置的,而它则使用图 2911中的寄存器向主控器反馈信息。 图 2911 坐标信息寄存器 (1) 产品ID及版本 0x8140-0x8143 寄存器存储的是产品ID,上电后我们可以利用I2C读取这些寄存器的值来判断I2C是否正常通讯,这些寄存器中包含有"9157"字样; 而0x8144-0x8145则保存有固件版本号,不同版本可能不同。 (2) X/Y分辨率 0x8146-0x8149寄存器存储了控制触摸屏的分辨率,它们的值与我们前面在配置寄存器写入的XY控制参数一致。所以我们可以通过读取这两个寄存器的值来确认配置参数是否正确写入。 (3) 状态寄存器 0x814E地址的是状态寄存器,它的Buffer status位存储了坐标状态,当它为1时,表示新的坐标数据已准备好,可以读取,0表示未就绪,数据无效,外部控制器读取完坐标后,须对这个寄存器位写0 。number of touch points位表示当前有多少个触点。其余数据位我们不关心。 (4) 坐标数据 从地址0x814F-0x8156的是触摸点1的坐标数据,从0x8157-0x815E的是触摸点2的坐标数据,依次还有存储3-10触摸点坐标数据的寄存器。读取这些坐标信息时,我们通过它们的track id来区分笔迹,多次读取坐标数据时,同一个track id号里的数据属于同一个连续的笔划轨迹。 读坐标流程上电、配置完寄存器后,GT9157就会开监测触摸屏,若我们前面的配置使INT采用中断上升沿报告触摸信号的方式,整个读取坐标信息的过程如下: (1) 待机时INT引脚输出低电平; (2) 有坐标更新时,INT引脚输出上升沿; (3) INT输出上升沿后,INT 脚会保持高直到下一个周期(该周期可由配置 Refresh_Rate 决定)。外部主控器在检测到INT的信号后,先读取状态寄存器(0x814E)中的number of touch points位获当前有多少个触摸点,然后读取各个点的坐标数据,读取完后将 buffer status位写为 0。外部主控器的这些读取过程要在一周期内完成,该周期由0x8056地址的Refresh_Rate寄存器配置; (4) 上一步骤中INT输出上升沿后,若主控未在一个周期内读走坐标,下次 GT9157 即使检测到坐标更新会再输出一个 INT 脉冲但不更新坐标; (5) 若外部主控一直未读走坐标,则 GT9 会一直输出 INT 脉冲。 29.3 电容触摸屏—触摸画板实验本小节讲解如何驱动电容触摸屏,并利用触摸屏制作一个简易的触摸画板应用。 学习本小节内容时,请打开配套的"电容触摸屏—触摸画板"工程配合阅读。 29.3.1 硬件设计图 2912 液晶屏实物图 本实验使用的液晶电容屏实物见图 2719,屏幕背面的PCB电路对应图 2721、图 2725中的原理图,分别是触摸屏接口及排针接口。 我们这个触摸屏出厂时就与GT9157芯片通过柔性电路板连接在一起了,柔性电路板从GT9157芯片引出VCC、GND、SCL、SDA、RSTN及INT引脚,再通过FPC座子引出到屏幕的PCB电路板中,PCB电路板加了部分电路,如I2C的上拉电阻,然后把这些引脚引出到屏幕右侧的排针处,方便整个屏幕与外部器件相连。 图 2913 电容屏接口 以上是我们STM32F429实验板使用的5寸屏原理图,它通过屏幕上的排针接入到实验板的液晶排母接口,与STM32芯片的引脚相连,连接见图 2725。 图 2914 屏幕与实验板的引脚连接 图 2725中35-38号引脚即电容触摸屏相关的控制引脚。 以上原理图可查阅《LCD5.0-黑白原理图》及《秉火F429开发板黑白原理图》文档获知,若您使用的液晶屏或实验板不一样,请根据实际连接的引脚修改程序。 29.3.2 软件设计本工程中的GT9157芯片驱动主要是从官方提供的Linux驱动修改过来的,我们把这部分文件存储到"gt9xx.c"及"gt9xx.h"文件中,而这些驱动的底层I2C通讯接口我们存储到了"bsp_i2c_touch.c"及"bsp_i2c_touch.h"文件中,这些文件也可根据您的喜好命名,它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。在我们提供的资料《gt9xx_1.8_drivers.zip》压缩包里有官方的原Linux驱动,感兴趣的读者可以对比这些文件,了解如何移植驱动。 1. 编程要点(1) 分析官方的gt9xx驱动,了解需要提供哪些底层接口; (2) 编写底层驱动接口; (3) 利用gt9xx驱动,获取触摸坐标; (4) 编写测试程序检验驱动。 2. 代码分析 触摸屏硬件相关宏定义根据触摸屏与STM32芯片的硬件连接,我们把触摸屏硬件相关的配置都以宏的形式定义到"bsp_i2c_touch.h"文件中,见代码清单 242。 代码清单 291 触摸屏硬件配置相关的宏(bsp_i2c_touch.h文件) 1 /*设定使用的电容屏IIC设备地址*/ 2 #define GTP_ADDRESS 0xBA 3 4 #define I2CT_FLAG_TIMEOUT ((uint32_t)0x1000) 5 #define I2CT_LONG_TIMEOUT ((uint32_t)(10 * I2CT_FLAG_TIMEOUT)) 6 7 /*I2C引脚*/ 8 #define GTP_I2C I2C2 9 #define GTP_I2C_CLK RCC_APB1Periph_I2C2 10 #define GTP_I2C_CLK_INIT RCC_APB1PeriphClockCmd 11 12 #define GTP_I2C_SCL_PIN GPIO_Pin_4 13 #define GTP_I2C_SCL_GPIO_PORT GPIOH 14 #define GTP_I2C_SCL_GPIO_CLK RCC_AHB1Periph_GPIOH 15 #define GTP_I2C_SCL_SOURCE GPIO_PinSource4 16 #define GTP_I2C_SCL_AF GPIO_AF_I2C2 17 18 #define GTP_I2C_SDA_PIN GPIO_Pin_5 19 #define GTP_I2C_SDA_GPIO_PORT GPIOH 20 #define GTP_I2C_SDA_GPIO_CLK RCC_AHB1Periph_GPIOH 21 #define GTP_I2C_SDA_SOURCE GPIO_PinSource5 22 #define GTP_I2C_SDA_AF GPIO_AF_I2C2 23 24 /*复位引脚*/ 25 #define GTP_RST_GPIO_PORT GPIOD 26 #define GTP_RST_GPIO_CLK RCC_AHB1Periph_GPIOD 27 #define GTP_RST_GPIO_PIN GPIO_Pin_11 28 /*中断引脚*/ 29 #define GTP_INT_GPIO_PORT GPIOD 30 #define GTP_INT_GPIO_CLK RCC_AHB1Periph_GPIOD 31 #define GTP_INT_GPIO_PIN GPIO_Pin_13 32 #define GTP_INT_EXTI_PORTSOURCE EXTI_PortSourceGPIOD 33 #define GTP_INT_EXTI_PINSOURCE EXTI_PinSource13 34 #define GTP_INT_EXTI_LINE EXTI_Line13 35 #define GTP_INT_EXTI_IRQ EXTI15_10_IRQn 36 /*中断服务函数*/ 37 #define GTP_IRQHandler EXTI15_10_IRQHandler 以上代码根据硬件的连接,把与触摸屏通讯使用的引脚号、引脚源以及复用功能映射都以宏封装起来。在这里还定义了与GT9157芯片通讯的I2C设备地址,该地址是一个8位的写地址,它是由我们的上电时序决定的。 初始化触摸屏控制引脚利用上面的宏,编写LTDC的触摸屏控制引脚的初始化函数,见代码清单 243。 代码清单 292 触摸屏控制引脚的GPIO初始化函数(bsp_i2c_touch.c文件) 1 /** 2 * @brief 触摸屏 I/O配置 3 * @param 无 4 * @retval 无 5 */ 6 static void I2C_GPIO_Config(void) 7 { 8 GPIO_InitTypeDef GPIO_InitStructure; 9 10 /*使能I2C时钟 */ 11 GTP_I2C_CLK_INIT(GTP_I2C_CLK, ENABLE); 12 13 /*使能触摸屏使用的引脚的时钟*/ 14 RCC_AHB1PeriphClockCmd(GTP_I2C_SCL_GPIO_CLK | GTP_I2C_SDA_GPIO_CLK| 15 GTP_RST_GPIO_CLK|GTP_INT_GPIO_CLK, ENABLE); 16 17 RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); 18 19 /* 配置I2C_SCL源*/ 20GPIO_PinAFConfig(GTP_I2C_SCL_GPIO_PORT, GTP_I2C_SCL_SOURCE, GTP_I2C_SCL_AF); 21 /* 配置I2C_SDA 源*/ 22GPIO_PinAFConfig(GTP_I2C_SDA_GPIO_PORT, GTP_I2C_SDA_SOURCE, GTP_I2C_SDA_AF); 23 24 /*配置SCL引脚 */ 25 GPIO_InitStructure.GPIO_Pin = GTP_I2C_SCL_PIN; 26 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 27 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 28 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; 29 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 30 GPIO_Init(GTP_I2C_SCL_GPIO_PORT, &GPIO_InitStructure); 31 32 /*配置SDA引脚 */ 33 GPIO_InitStructure.GPIO_Pin = GTP_I2C_SDA_PIN; 34 GPIO_Init(GTP_I2C_SDA_GPIO_PORT, &GPIO_InitStructure); 35 36 /*配置RST引脚,下拉推挽输出 */ 37 GPIO_InitStructure.GPIO_Pin = GTP_RST_GPIO_PIN; 38 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 39 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 40 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 41 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; 42 GPIO_Init(GTP_RST_GPIO_PORT, &GPIO_InitStructure); 43 44 /*配置 INT引脚,下拉推挽输出,方便初始化 */ 45 GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; 46 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 47 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 48 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 49 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化 50 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); 51 } 以上函数初始化了触摸屏用到的I2C信号线,并且把RST及INT引脚也初始化成了下拉推挽输出模式,以便刚上电的时候输出上电时序,设置触摸屏的I2C设备地址。 配置I2C的模式接下来需要配置I2C的工作模式,GT9157芯片使用的是标准7位地址模式的I2C通讯,所以I2C这部分的配置跟我们在EEPROM实验中的是一样的,不了解这部分内容的请阅读EEPROM章节,见代码清单 244。 代码清单 293 配置I2C工作模式(bsp_i2c_touch.c文件) 1 2 /* STM32 I2C 快速模式 */ 3 #define I2C_Speed 400000 4 5 /* 这个地址只要与STM32外挂的I2C器件地址不一样即可 */ 6 #define I2C_OWN_ADDRESS7 0x0A 7 8 /** 9 * @brief I2C 工作模式配置 10 * @param 无 11 * @retval 无 12 */ 13 static void I2C_Mode_Config(void) 14 { 15 I2C_InitTypeDef I2C_InitStructure; 16 17 /* I2C 模式配置 */ 18 I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; 19 I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; 20 I2C_InitStructure.I2C_OwnAddress1 =I2C_OWN_ADDRESS7; 21 I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ; 22 I2C_InitStructure.I2C_AcknowledgedAddress = 23 I2C_AcknowledgedAddress_7bit; /* I2C的寻址模式 */ 24 I2C_InitStructure.I2C_ClockSpeed = I2C_Speed; /* 通信速率 */ 25 I2C_Init(GTP_I2C, &I2C_InitStructure); /* I2C1 初始化 */ 26 I2C_Cmd(GTP_I2C, ENABLE); /* 使能 I2C1 */ 27 28 I2C_AcknowledgeConfig(GTP_I2C, ENABLE); 29 } 使用上电时序设置触摸屏的I2C地址注:因硬件I2C在实际驱动时存在无法成功发送信号的情况,我们的范例程序中关于I2C的底层驱动已改成使用软件I2C,其原理类似,硬件I2C的驱动在范例程序中有保留,可使用bsp_i2c_touch.h头文件中的宏来切换。 在上面配置完成STM32的引脚后,就可以开始控制这些引脚对触摸屏进行控制了,为了使用I2C通讯,首先要根据GT9157芯片的上电时序给它设置I2C设备地址,见代码清单 294。 代码清单 294使用上电时序设置触摸屏的I2C地址(bsp_i2c_touch.c文件) 1 2 /** 3 * @brief 对GT91xx芯片进行复位 4 * @param 无 5 * @retval 无 6 */ 7 void I2C_ResetChip(void) 8 { 9 GPIO_InitTypeDef GPIO_InitStructure; 10 11 /*配置 INT引脚,下拉推挽输出,方便初始化 */ 12 GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; 13 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; 14 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 15 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; 16 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //设置为下拉,方便初始化 17 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); 18 19 /*初始化GT9157,rst为高电平,int为低电平,则gt9157的设备地址被配置为0xBA*/ 20 21 /*复位为低电平,为初始化做准备*/ 22 GPIO_ResetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN); 23 Delay(0x0FFFFF); 24 25 /*拉高一段时间,进行初始化*/ 26 GPIO_SetBits (GTP_RST_GPIO_PORT,GTP_RST_GPIO_PIN); 27 Delay(0x0FFFFF); 28 29 /*把INT引脚设置为浮空输入模式,以便接收触摸中断信号*/ 30 GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; 31 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 32 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 33 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 34 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); 35 } 这段函数中控制RST引脚由低电平改变至高电平,且期间INT一直为低电平,这样的上电时序可以控制触控芯片的I2C写地址为0xBA,读地址为0xBB,即(0xBA|0x01)。输出完上电时序后,把STM32的INT引脚模式改成浮空输入模式,使它可以接收触控芯片输出的触摸中断信号。接下来我们在I2C_GTP_IRQEnable函数中使能INT中断,见代码清单 295。 代码清单 295 使能INT中断(bsp_i2c_touch.c文件) 1 2 /** 3 * @brief 使能触摸屏中断 4 * @param 无 5 * @retval 无 6 */ 7 void I2C_GTP_IRQEnable(void) 8 { 9 EXTI_InitTypeDef EXTI_InitStructure; 10 NVIC_InitTypeDef NVIC_InitStructure; 11 GPIO_InitTypeDef GPIO_InitStructure; 12 /*配置 INT 为浮空输入 */ 13 GPIO_InitStructure.GPIO_Pin = GTP_INT_GPIO_PIN; 14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; 15 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 16 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; 17 GPIO_Init(GTP_INT_GPIO_PORT, &GPIO_InitStructure); 18 19 /* 连接 EXTI 中断源到INT 引脚 */ 20 SYSCFG_EXTILineConfig(GTP_INT_EXTI_PORTSOURCE, GTP_INT_EXTI_PINSOURCE); 21 22 /* 选择 EXTI 中断源 */ 23 EXTI_InitStructure.EXTI_Line = GTP_INT_EXTI_LINE; 24 EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt; 25 EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising; 26 EXTI_InitStructure.EXTI_LineCmd = ENABLE; 27 EXTI_Init(&EXTI_InitStructure); 28 29 /* 配置中断优先级 */ 30 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); 31 32 /*使能中断*/ 33 NVIC_InitStructure.NVIC_IRQChannel = GTP_INT_EXTI_IRQ; 34 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; 35 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; 36 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; 37 NVIC_Init(&NVIC_InitStructure); 38 } 这个INT引脚我们配置为上升沿触发,是跟后面写入到触控芯片的配置参数一致的。 初始化封装利用以上函数,我们把信号引脚及I2C设备地址初始化的过程都封装到函数I2C_Touch_Init中,见代码清单 296。 代码清单 296 封装引脚初始化及上电时序(bsp_i2c_touch.c文件) 1 2 /** 3 * @brief I2C 外设(GT91xx)初始化 4 * @param 无 5 * @retval 无 6 */ 7 void I2C_Touch_Init(void) 8 { 9 I2C_GPIO_Config(); 10 11 I2C_Mode_Config(); 12 13 I2C_ResetChip(); 14 15 I2C_GTP_IRQEnable(); 16 } I2C基本读写函数为了与上层"gt9xx.c"驱动文件中的函数对接,本实验中的I2C读写函数与EEPROM实验中的有稍微不同,见代码清单 278。 代码清单 297 I2C基本读写函数(bsp_i2c_touch.c文件) 1 2 __IO uint32_t I2CTimeout = I2CT_LONG_TIMEOUT; 3 /** 4 * @brief IIC等待超时调用本函数输出调试信息 5 * @param None. 6 * @retval 返回0xff,表示IIC读取数据失败 7 */ 8 static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode) 9 { 10 /* Block communication and all processes */ 11 GTP_ERROR("I2C 等待超时!errorCode = %d",errorCode); 12 return 0xFF; 13 } 14 /** 15 * @brief 使用IIC读取数据 16 * @param 17 * @arg ClientAddr:从设备地址 18 * @arg pBuffer:存放由从机读取的数据的缓冲区指针 19 * @arg NumByteToRead:读取的数据长度 20 * @retval 无 21 */ 22 uint32_t I2C_ReadBytes(uint8_t ClientAddr,uint8_t* pBuffer, 23 uint16_t NumByteToRead) 24 { 25 I2CTimeout = I2CT_LONG_TIMEOUT; 26 27 while (I2C_GetFlagStatus(GTP_I2C, I2C_FLAG_BUSY)) 28 { 29 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0); 30 } 31 32 /* Send STRAT condition */ 33 I2C_GenerateSTART(GTP_I2C, ENABLE); 34 35 I2CTimeout = I2CT_FLAG_TIMEOUT; 36 37 /* Test on EV5 and clear it */ 38 while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_MODE_SELECT)) 39 { 40 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1); 41 } 42 /* Send GT91xx address for read */ 43 I2C_Send7bitAddress(GTP_I2C, ClientAddr, I2C_Direction_Receiver); 44 45 I2CTimeout = I2CT_FLAG_TIMEOUT; 46 47 /* Test on EV6 and clear it */ 48 while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) 49 { 50 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2); 51 } 52 /* While there is data to be read */ 53 while (NumByteToRead) 54 { 55 if (NumByteToRead == 1) 56 { 57 /* Disable Acknowledgement */ 58 I2C_AcknowledgeConfig(GTP_I2C, DISABLE); 59 60 /* Send STOP Condition */ 61 I2C_GenerateSTOP(GTP_I2C, ENABLE); 62 } 63 I2CTimeout = I2CT_LONG_TIMEOUT; 64 while (I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED)==0) 65 { 66 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3); 67 } 68 { 69 /* Read a byte from the device */ 70 *pBuffer = I2C_ReceiveData(GTP_I2C); 71 72 /* Point to the next location where the byte read will be saved */ 73 pBuffer++; 74 75 /* Decrement the read bytes counter */ 76 NumByteToRead--; 77 } 78 } 79 /* Enable Acknowledgement to be ready for another reception */ 80 I2C_AcknowledgeConfig(GTP_I2C, ENABLE); 81 return 0; 82 } 83 84 85 86 87 /** 88 * @brief 使用IIC写入数据 89 * @param 90 * @arg ClientAddr:从设备地址 91 * @arg pBuffer:缓冲区指针 92 * @arg NumByteToWrite:写的字节数 93 * @retval 无 94 */ 95 uint32_t I2C_WriteBytes(uint8_t ClientAddr,uint8_t* pBuffer, 96 uint8_t NumByteToWrite) 97 { 98 I2CTimeout = I2CT_LONG_TIMEOUT; 99 100 while (I2C_GetFlagStatus(GTP_I2C, I2C_FLAG_BUSY)) 101 { 102 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4); 103 } 104 105 /* Send START condition */ 106 I2C_GenerateSTART(GTP_I2C, ENABLE); 107 108 I2CTimeout = I2CT_FLAG_TIMEOUT; 109 110 /* Test on EV5 and clear it */ 111 while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_MODE_SELECT)) 112 { 113 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5); 114 } 115 116 /* Send GT91xx address for write */ 117 I2C_Send7bitAddress(GTP_I2C, ClientAddr, I2C_Direction_Transmitter); 118 119 I2CTimeout = I2CT_FLAG_TIMEOUT; 120 121 /* Test on EV6 and clear it */ 122 while(!I2C_CheckEvent(GTP_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) 123 { 124 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6); 125 } 126 /* While there is data to be written */ 127 while (NumByteToWrite--) 128 { 129 /* Send the current byte */ 130 I2C_SendData(GTP_I2C, *pBuffer); 131 132 /* Point to the next byte to be written */ 133 pBuffer++; 134 135 I2CTimeout = I2CT_FLAG_TIMEOUT; 136 137 /* Test on EV8 and clear it */ 138 while (!I2C_CheckEvent(GTP_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED)) 139 { 140 if ((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7); 141 } 142 } 143 /* Send STOP condition */ 144 I2C_GenerateSTOP(GTP_I2C, ENABLE); 145 return 0; 146 } 这里的读写函数都是很纯粹的I2C通讯过程,即读函数只有读过程,不包含发送寄存器地址的过程,而写函数也是只有写过程,没有包含寄存器的地址,大家可以对比一下它们与前面EEPROM实验中的差别。这两个函数都只包含从I2C的设备地址、缓冲区指针以及数据量。 Linux的I2C驱动接口使用前面的基本读写函数,主要是为了对接原"gt9xx.c"驱动里使用的Linux I2C接口函数I2C_Transfer,实现了这个函数后,移植时就可以减少"gt9xx.c"文件的修改量。I2C_Transfer函数见代码清单 298。 代码清单 298 Linux的I2C驱动接口(gt9xx.c文件) 1 2 /* 表示读数据 */ 3 #define I2C_M_RD 0x0001 4 /* 5 * 存储I2C通讯的信息 6 * @addr:从设备的I2C设备地址 7 * @flags: 控制标志 8 * @len:读写数据的长度 9 * @buf:存储读写数据的指针 10 **/ 11 struct i2c_msg 12 { 13 uint8_t addr; /*从设备的I2C设备地址 */ 14 uint16_t flags; /*控制标志*/ 15 uint16_t len; /*读写数据的长度 */ 16 uint8_t *buf; /*存储读写数据的指针 */ 17 }; 18 19 /** 20 * @brief 使用IIC进行数据传输 21 * @param 22 * @arg i2c_msg:数据传输结构体 23 * @arg num:数据传输结构体的个数 24 * @retval 正常完成的传输结构个数,若不正常,返回0xff 25 */ 26 static int I2C_Transfer( struct i2c_msg *msgs,int num) 27 { 28 int im = 0; 29 int ret = 0; 30 //输出调试信息,可忽略 31 GTP_DEBUG_FUNC(); 32 33 for (im = 0; ret == 0 && im != num; im++) 34 { 35 //根据flag判断是读数据还是写数据 36 if ((msgs[im].flags&I2C_M_RD)) 37 { 38 //IIC读取数据 39 ret = I2C_ReadBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); 40 } 41 else 42 { 43 //IIC写入数据 44 ret = I2C_WriteBytes(msgs[im].addr, msgs[im].buf, msgs[im].len); 45 } 46 } 47 48 if (ret) 49 return ret; 50 51 return im; //正常完成的传输结构个数 52 } I2C_Transfer的主要输入参数是i2c_msg结构体的指针以及要传输多少个这样的结构体。i2c_msg结构体包含以下几个成员: (1) addr 这是从机的I2C设备地址,通讯时无论是读方向还是写方向,给这个成员赋值为写地址即可(本实验中为0xBA)。 (2) flags 这个成员存储了控制标志,它用于指示本i2c_msg结构体要求以什么方式来传输。在原Linux驱动中有很多种控制方式,在我们这个工程中,只支持读或写控制标志,flags被赋值为I2C_M_RD宏的时候表示读方向,其余值表示写方向。 (3) len 本成员存储了要读写的数据长度。 (4) buf 本成员存储了指向读写数据缓冲区的指针。 利用这个结构体,我们再来看I2C_Transfer函数做了什么工作。 (1) 输入参数中可能包含有多个要传输的i2c_msg结构体,利用for循环把这些结构体一个个地传输出去; (2) 传输的时候根据i2c_msg结构体中的flags标志,确定应该调用I2C读函数还是写函数,这些函数即前面定义的I2C基本读写函数。调用这些函数的时候,以i2c_msg结构体的成员作为参数。 I2C复合读写函数理解了I2C_Transfer函数的代码,我们发现它还是什么都没做,只是对I2C基本读写函数封装了比较特别的调用形式而已,而我们知道GT9157触控芯片都有很多不同的寄存器,如果我们仅用上面的函数,如何向特定寄存器写入参数或读取特定寄存器的内容呢?这就需要再利用I2C_Transfer函数编写具有I2C通讯复合时序的读写函数了。Linux驱动进行这样的封装是为了让它的核心层与具体设备独立开来,对于这个巨型系统,这样写代码是很有必要的,上述的I2C_Transfer函数属于Linux内部的驱动层,它对外提供接口,而像GT9157、EEPROM等使用I2C的设备,都利用这个接口编写自己具体的驱动文件,GT9157的这些I2C复合读写函数见代码清单 299。 代码清单 299 I2C复合读写函数(gt9xx.c文件) 1 //寄存器地址的长度 2 #define GTP_ADDR_LENGTH 2 3 4 /** 5 * @brief 从IIC设备中读取数据 6 * @param 7 * @arg client_addr:设备地址 8 * @arg buf[0~1]: 读取数据寄存器的起始地址 9 * @arg buf[2~len-1]: 存储读出来数据的缓冲buffer 10 * @arg len: GTP_ADDR_LENGTH + read bytes count( 11 寄存器地址长度+读取的数据字节数) 12 * @retval i2c_msgs传输结构体的个数,2为成功,其它为失败 13 */ 14 static int32_t GTP_I2C_Read(uint8_t client_addr, uint8_t *buf, 15 int32_t len) 16 { 17 struct i2c_msg msgs[2]; 18 int32_t ret=-1; 19 int32_t retries = 0; 20 21 //输出调试信息,可忽略 22 GTP_DEBUG_FUNC(); 23 /*一个读数据的过程可以分为两个传输过程: 24 * 1. IIC 写入要读取的寄存器地址 25 * 2. IIC 读取数据 26 * */ 27 28 msgs[0].flags = !I2C_M_RD; //写入 29 msgs[0].addr = client_addr; //IIC设备地址 30 msgs[0].len = GTP_ADDR_LENGTH; //寄存器地址为2字节(即写入两字节的数据) 31 msgs[0].buf = &buf[0]; //buf[0~1]存储的是要读取的寄存器地址 32 33 msgs[1].flags = I2C_M_RD; //读取 34 msgs[1].addr = client_addr; //IIC设备地址 35 msgs[1].len = len - GTP_ADDR_LENGTH; //要读取的数据长度 36 msgs[1].buf = &buf[GTP_ADDR_LENGTH]; //buf[GTP_ADDR_LENGTH]之后的缓冲区存储读出的数据 37 38 while (retries < 5) // 39 { 40 ret = I2C_Transfer( msgs, 2); //调用IIC数据传输过程函数,有2个传输过程 41 if (ret == 2)break; 42 retries++; 43 } 44 if ((retries >= 5)) 45 { 46 //发送失败,输出调试信息 47 GTP_ERROR("I2C Read Error"); 48 } 49 return ret; 50 } 51 52 /** 53 * @brief 向IIC设备写入数据 54 * @param 55 * @arg client_addr:设备地址 56 * @arg buf[0~1]: 要写入的数据寄存器的起始地址 57 * @arg buf[2~len-1]: 要写入的数据 58 * @arg len: GTP_ADDR_LENGTH + write bytes count( 59 寄存器地址长度+写入的数据字节数) 60 * @retval i2c_msgs传输结构体的个数,1为成功,其它为失败 61 */ 62 static int32_t GTP_I2C_Write(uint8_t client_addr,uint8_t *buf, 63 int32_t len) 64 { 65 struct i2c_msg msg; 66 int32_t ret = -1; 67 int32_t retries = 0; 68 69 //输出调试信息,可忽略 70 GTP_DEBUG_FUNC(); 71 /*一个写数据的过程只需要一个传输过程: 72 * 1. IIC连续写入数据寄存器地址及数据 73 * */ 74 msg.flags = !I2C_M_RD; //写入 75 msg.addr = client_addr; //从设备地址 76 msg.len = len; //长度直接等于(寄存器地址长度+写入的数据字节数) 77 msg.buf = buf; //直接连续写入缓冲区中的数据(包括了寄存器地址) 78 79 while (retries < 5) 80 { 81 ret = I2C_Transfer(&msg, 1); //调用IIC数据传输过程函数,1个传输过程 82 if (ret == 1)break; 83 retries++; 84 } 85 if ((retries >= 5)) 86 { 87 //发送失败,输出调试信息 88 GTP_ERROR("I2C Write Error"); 89 } 90 return ret; 91 } 可以看到,复合读写函数都包含有client_addr、buf及len输入参数,其中client_addr表示I2C的设备地址,buf存储了要读写的寄存器地址及数据,len表示buf的长度。在函数的内部处理中,复合读写过程被分解成两个基本的读写过程,输入参数被转化存储到i2c_msg结构体中,每个基本读写过程使用一个i2c_msg结构体来表示,见表 292和表 293。 表 292 复合读过程的步骤分解 复合读过程的步骤分解 说明 传输寄存器地址 这相当于一个I2C的基本写过程,写入一个2字节长度的寄存器地址,buf指针的前两个字节内容被解释为寄存器地址。 从寄存器读取内容 这是一个I2C的基本读过程,读取到的数据存储到buf指针的第3个地址开始的空间中。 表 293 复合写过程的步骤分解 复合写过程的步骤分解 说明 传输寄存器地址 这相当于一个I2C的基本写过程,写入一个2字节长度的寄存器地址,buf指针的前两个字节内容被解释为寄存器地址。 向寄存器写入内容 这也是一个I2C的基本写过程,写入的数据为buf指针的第3个地址开始的内容。 复合过程的分解主要是针对寄存器地址传输和实际数据传输来划分的,调用这两个复合读写过程的时候,我们需要注意buf的前两个字节为寄存器地址,且len的长度为buf的整体长度。 读取触控芯片的产品ID及版本号利用上述复合读写函数,我们就可以使用I2C控制触控芯片了,首先是最简单的读取版本函数,见代码清单 2910。 代码清单 2910读取触控芯片的产品ID及版本号(gt9xx.c文件) 1 2 /*设定使用的电容屏IIC设备地址*/ 3 #define GTP_ADDRESS 0xBA 4 //芯片版本号地址 5 #define GTP_REG_VERSION 0x8140 6 7 /******************************************************* 8 Function: 9 Read chip version. 10 Input: 11 client: i2c device 12 version: buffer to keep ic firmware version 13 Output: 14 read operation return. 15 2: succeed, otherwise: failed 16 *******************************************************/ 17 int32_t GTP_Read_Version(void) 18 { 19 int32_t ret = -1; 20 //寄存器地址 21 uint8_t buf[8] = {GTP_REG_VERSION >> 8, GTP_REG_VERSION & 0xff}; 22 //输出调试信息,可忽略 23 GTP_DEBUG_FUNC(); 24 25 ret = GTP_I2C_Read(GTP_ADDRESS, buf, sizeof(buf)); 26 if (ret < 0) 27 { 28 GTP_ERROR("GTP read version failed"); 29 return ret; 30 } 31 32 if (buf[5] == 0x00) 33 { 34 GTP_INFO("IC Version: %c%c%c_%02x%02x", 35 buf[2], buf[3], buf[4], buf[7], buf[6]); 36 } 37 else 38 { 39 GTP_INFO("IC Version: %c%c%c%c_%02x%02x", 40 buf[2], buf[3], buf[4], buf[5], buf[7], buf[6]); 41 } 42 return ret; 43 } 这个函数定义了一个8字节的buf数组,并且向它的第0和第1个元素写入产品ID寄存器的地址,然后调用复合读取函数,即可从芯片中读取这些寄存器的信息,结果使用宏GTP_INFO输出。 向触控芯片写入配置参数万事俱备,现在我们可以使用I2C向触摸芯片写入寄存器配置了,见代码清单 2911。 代码清单 2911 初始化并向触控芯片写入配置参数(gt9xx.c文件) 1 // 5寸屏GT9157驱动配置 2 uint8_t CTP_CFG_GT9157[] ={ 3 0x00,0x20,0x03,0xE0,0x01,0x05,0x3C,0x00,0x01,0x08, 4 0x28,0x0C,0x50,0x32,0x03,0x05,0x00,0x00,0x00,0x00, 5 /*...部分内容省略...*/ 6 0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 7 0xFF,0xFF,0xFF,0xFF,0x48,0x01 8 }; 9 10 // 7寸屏GT911驱动配置 11 uint8_t CTP_CFG_GT911[] = { 12 0x00,0x20,0x03,0xE0,0x01,0x05,0x3D,0x00,0x01,0x48, 13 0x28,0x0D,0x50,0x32,0x03,0x05,0x00,0x00,0x00,0x00, 14 /*...部分内容省略...*/ 15 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 16 0x00,0x00,0x00,0x00,0x11,0x01 17 }; 18 19 20 uint8_t config[GTP_CONFIG_MAX_LENGTH + GTP_ADDR_LENGTH] 21 = {GTP_REG_CONFIG_DATA >> 8, GTP_REG_CONFIG_DATA & 0xff}; 22 23 TOUCH_IC touchIC; 24 25 26 /******************************************************* 27 Function: 28 Initialize gtp. 29 Input: 30 ts: goodix private data 31 Output: 32 Executive outcomes. 33 0: succeed, otherwise: failed 34 *******************************************************/ 35 int32_t GTP_Init_Panel(void) 36 { 37 int32_t ret = -1; 38 39 int32_t i = 0; 40 uint8_t check_sum = 0; 41 int32_t retry = 0; 42 43 uint8_t* cfg_info; 44 uint8_t cfg_info_len ; 45 46 uint8_t cfg_num =0x80FE-0x8047+1 ; //需要配置的寄存器个数 47 48 GTP_DEBUG_FUNC(); 49 50 I2C_Touch_Init(); 51 52 ret = GTP_I2C_Test(); 53 if (ret < 0) 54 { 55 GTP_ERROR("I2C communication ERROR!"); 56 return ret; 57 } 58 59 //获取触摸IC的型号 60 GTP_Read_Version(); 61 62 //根据IC的型号指向不同的配置 63 if(touchIC == GT9157) 64 { 65 cfg_info = CTP_CFG_GT9157; //指向寄存器配置 66 cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT9157);//计算配置表的大小 67 } 68 else 69 { 70 cfg_info = CTP_CFG_GT911;//指向寄存器配置 71 cfg_info_len = CFG_GROUP_LEN(CTP_CFG_GT911) ;//计算配置表的大小 72 } 73 74 memset(&config[GTP_ADDR_LENGTH], 0, GTP_CONFIG_MAX_LENGTH); 75 memcpy(&config[GTP_ADDR_LENGTH], cfg_info, cfg_info_len); 76 77 //计算要写入checksum寄存器的值 78 check_sum = 0; 79 for (i = GTP_ADDR_LENGTH; i < cfg_num+GTP_ADDR_LENGTH; i++) 80 { 81 check_sum += config[i]; 82 } 83 config[ cfg_num+GTP_ADDR_LENGTH] = (~check_sum) + 1; //checksum 84 config[ cfg_num+GTP_ADDR_LENGTH+1] = 1; //refresh 配置更新标志 85 86 //写入配置信息 87 for (retry = 0; retry < 5; retry++) 88 { 89 ret = GTP_I2C_Write(GTP_ADDRESS, config , cfg_num + GTP_ADDR_LENGTH+2); 90 if (ret > 0) 91 { 92 break; 93 } 94 } 95 Delay(0xfffff); //延迟等待芯片更新 96 97 /*使能中断,这样才能检测触摸数据*/ 98 I2C_GTP_IRQEnable(); 99 100 GTP_Get_Info(); 101 102 return 0; 103 } 这段代码调用I2C_Touch_Init初始化了STM32的I2C外设,设定触控芯片的I2C设备地址,然后调用了GTP_Read_Version尝试获取触控芯片的版本号。接下来是函数的主体,它使用GTP_I2C_Write函数通过I2C把配置参数表CTP_CFG_GT9157(5寸屏)或CTP_CFG_GT911(7寸屏)写入到触控芯片的的配置寄存器中,注意传输中包含有checksum寄存器的值。写入完参数后调用I2C_GTP_IRQEnable以使能INT引脚检测中断。 INT中断服务函数经过上面的函数初始化后,触摸屏就可以开始工作了,当触摸时,INT引脚会产生触摸中断,会进入中断服务函数GTP_IRQHandler,见代码清单 2912。 代码清单 2912 触摸屏的中断服务函数(stm32f4xx_it.c文件) 1 //触摸屏中断服务函数 2 void GTP_IRQHandler(void) 3 { 4 //确保是否产生了EXTI Line中断 5 if (EXTI_GetITStatus(GTP_INT_EXTI_LINE) != RESET) 6 { 7 GTP_TouchProcess(); 8 EXTI_ClearITPendingBit(GTP_INT_EXTI_LINE); //清除中断标志位 9 } 10 } 中断服务函数只是简单地调用了GTP_TouchProcess函数,它是读取触摸坐标的主体。 读取坐标数据GTP_TouchProcess函数的内容见代码清单 2913。 代码清单 2913 GTP_TouchProcess坐标读取函数 1 2 /*状态寄存器地址*/ 3 #define GTP_READ_COOR_ADDR 0x814E 4 5 /** 6 * @brief 触屏处理函数,轮询或者在触摸中断调用 7 * @param 无 8 * @retval 无 9 */ 10 static void Goodix_TS_Work_Func(void) 11 { 12 uint8_t end_cmd[3] = {GTP_READ_COOR_ADDR >> 8, GTP_READ_COOR_ADDR & 0xFF, 0}; 13 uint8_t point_data[2 + 1 + 8 * GTP_MAX_TOUCH + 1]= {GTP_READ_COOR_ADDR >> 8, 14 GTP_READ_COOR_ADDR & 0xFF }; 15 16 uint8_t touch_num = 0; 17 uint8_t finger = 0; 18 static uint16_t pre_touch = 0; 19 static uint8_t pre_id[GTP_MAX_TOUCH] = {0}; 20 21 uint8_t client_addr=GTP_ADDRESS; 22 uint8_t* coor_data = NULL; 23 int32_t input_x = 0; 24 int32_t input_y = 0; 25 int32_t input_w = 0; 26 uint8_t id = 0; 27 28 int32_t i = 0; 29 int32_t ret = -1; 30 31 GTP_DEBUG_FUNC(); 32 33 ret = GTP_I2C_Read(client_addr, point_data, 12);//10字节寄存器加2字节地址 34 if (ret < 0) 35 { 36 GTP_ERROR("I2C transfer error. errno:%d\n ", ret); 37 return; 38 } 39 40 finger = point_data[GTP_ADDR_LENGTH];//状态寄存器数据 41 42 if (finger == 0x00) //没有数据,退出 43 { 44 return; 45 } 46 47 if ((finger & 0x80) == 0) //判断buffer status位 48 { 49 goto exit_work_func;//坐标未就绪,数据无效 50 } 51 52 touch_num = finger & 0x0f;//坐标点数 53 if (touch_num > GTP_MAX_TOUCH) 54 { 55 goto exit_work_func;//大于最大支持点数,错误退出 56 } 57 58 if (touch_num > 1)//不止一个点 59 { 60 uint8_t buf[8 * GTP_MAX_TOUCH] = {(GTP_READ_COOR_ADDR + 10) >> 8, 61 (GTP_READ_COOR_ADDR + 10) & 0xff}; 62 63 64 ret = GTP_I2C_Read(client_addr, buf, 2 + 8 * (touch_num - 1)); 65 //复制其余点数的数据到point_data memcpy(&point_data[12], &buf[2], 8 * (touch_num - 1)); 66 } 67 68 if (pre_touch>touch_num) //pre_touch>touch_num,表示有的点释放了 69 { 70 for (i = 0; i < pre_touch; i++) //一个点一个点处理 71 { 72 uint8_t j; 73 for (j=0; j= touch_num-1)//遍历当前所有id都找不到pre_id[i],表示已释放 81 { 82 GTP_Touch_Up( pre_id[i]); 83 } 84 } 85 } 86 } 87 88 if (touch_num) 89 { 90 for (i = 0; i < touch_num; i++) //一个点一个点处理 91 { 92 coor_data = &point_data[i * 8 + 3]; 93 94 id = coor_data[0] & 0x0F; //track id 95 pre_id[i] = id; 96 97 input_x = coor_data[1] | (coor_data[2] |
今日新闻 |
推荐新闻 |
CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3 |