功能性聚酯薄膜是什么,【正点原子stm32连载】第三十九章 触摸屏实验 摘自【正点原子】stm32f103... -爱游戏平台

苗坤旺离型膜

1)实验平台:正点原子stm32f103战舰开发板v4 2)平台购买地址:https://detail.tmall.com/item.htm?id=609294757420 3)全套实验源码 手册 视频下载地址: http://www.openedv.com/thread-340252-1-1.html#

第三十九章 触摸屏实验

本章,我们将介绍如何使用stm32f1来驱动触摸屏,正点原子战舰stm32f103本身并没有触摸屏控制器,但是它支持触摸屏,可以通过外接带触摸屏的lcd模块(比如正点原子 tftlcd模块),来实现触摸屏控制。在本章中,我们将向大家介绍stm32控制正点原子tftlcd模块(包括电阻触摸与电容触摸),实现触摸屏驱动,最终实现一个手写板的功能。 本章分为如下几个部分: 39.1触摸屏简介 39.2硬件设计 39.3程序设计 39.4下载验证

39.1 触摸屏简介

触摸屏是在显示屏的基础上,在屏幕或屏幕上方分布一层与屏幕大小相近的传感器形成的组合器件。触摸和显示功能由软件控制,可以独立也可以组合实现,用户可以通过侦测传感器的触点再配合相应的软件实现触摸效果。目前最常用的触摸屏有两种:电阻式触摸屏与电容式触摸屏。下面,我们来分别介绍。 39.1.1 电阻式触摸屏 正点原子2.4/2.8/3.5寸tftlcd模块自带的触摸屏都属于电阻式触摸屏,下面简单介绍下电阻式触摸屏的原理。 电阻触摸屏的主要部分是一块与显示器表面非常贴合的电阻薄膜屏,这是一种多层的复合薄膜,具体结构如下图39.1.1.1所示。

图39.1.1.1 电阻触摸屏多层结构图 表面硬涂层起保护作用,主要是一层外表面硬化处理、光滑防擦的塑料层。玻璃底层用于支撑上面的结构,主要是玻璃或者塑料平板。透明隔离点用来分离开外层ito和内层ito。ito层是触摸屏关键结构,是涂有铟锡金属氧化物的导电层。还有一个结构上图没有标出,就是pet层。pet层是聚酯薄膜,处于外层ito和表面硬涂层之间,很薄很有弹性,触摸时向下弯曲,使得ito层接触。 当手指触摸屏幕时,两个ito层在触摸点位置就有接触,电阻发生变化,在x和y两个方向上产生电信号,然后送到触摸屏控制器,具体情况如下图39.1.1.2所示。触摸屏控制器侦测到这一接触并计算出x和y方向上的ad值,简单来讲,电阻触摸屏将触摸点(x,y)的物理位置转换为代表x坐标和y坐标的电压值。单片机与触摸屏控制器进行通信获取到ad值,通过一定比例关系运算,获得x和y轴坐标值。

图39.1.1.2 电阻式触摸屏的触点坐标结构 电阻触摸屏的优点:精度高、价格便宜、抗干扰能力强、稳定性好。 电阻触摸屏的缺点:容易被划伤、透光性不太好、不支持多点触摸。 从以上介绍可知,触摸屏都需要一个ad转换器,一般来说是需要一个控制器的。正点原子 tftlcd模块选择的是四线电阻式触摸屏,这种触摸屏的控制芯片有很多,包括:ads7543、ads7846、tsc2046、xpt2046和hr2046等。这几款芯片的驱动基本上是一样的,也就是你只要写出了xpt2046的驱动,这个驱动对其他几个芯片也是有效的。而且封装也有一样的,完全pin-to-pin兼容。所以在替换起来,很方便。 正点原子tftlcd模块自带的触摸屏控制芯片为xpt2046或hr2046。这里以xpt2046作为介绍。xpt2046是一款4导线制触摸屏控制器,使用的是spi通信接口,内含12位分辨率125khz转换速率逐步逼近型a/d转换器。xpt2046支持从1.5v到5.25v的低电压i/o接口。xpt2046能通过执行两次a/d转换(一次获取x位置,一次获取y位置)查出被按的屏幕位置,除此之外,还可以测量加在触摸屏上的压力。内部自带2.5v参考电压可以作为辅助输入、温度测量和电池监测模式之用,电池监测的电压范围可以从0v到6v。xpt2046片内集成有一个温度传感器。在2.7v的典型工作状态下,关闭参考电压,功耗可小于0.75mw。 xpt2046的驱动方法也是很简单,主要看懂xpt2046通信时序图,如下图39.1.1.3所示。

图39.1.1.3 xpt2046通信时序图 依照时序图,就可以很好写出这个通信代码,上图具体过程:拉低片选,选中器件发送命令字清除busy读取16位数据(高12位数据有效即转换的ad值)拉高片选,结束操作。这里的难点就是需要搞清楚命令字该发送什么?只要搞清楚发送什么数值,就可以获取到ad值。命令字的详情如下图39.1.1.4所示:

图39.1.1.4 命令字详情图 位7,开始位,置1即可。位3,为了提供精度,mode位清0选择12位分辨率。位2,是进行工作模式选择,为了达到最佳性能,首选差分工作模式即该位清0即可。位1-0是功耗相关的,直接清0即可。而位6-4的值要取决于工作模式,在确定了差分功能模式后,通道选择位也确定了,如图39.1.1.5所示。

图39.1.1.5 差分模式输入配置图(ser/dfr=0) 从上图,就可以知道:当我们需要检测y轴位置时,a2a1a0赋值为001;检测x轴位置时,a2a1a0赋值为101。结合前面对其他位的赋值,在x,y方向与屏幕相同的情况下,命令字0xd0就是读取x坐标ad值,0x90就是读取y坐标的ad值。假如x,y方向与屏幕相反,0x90就是读取x坐标的ad值,而0xd0就是读取y坐标的ad值。 关于这个芯片其他的功能,也可以参考芯片的datasheet。 电阻式触摸屏就介绍到这里。 39.1.2 电容式触摸屏 现在几乎所有智能手机,包括平板电脑都是采用电容屏作为触摸屏,电容屏是利用人体感应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标。正点原子4.3/7寸tftlcd模块自带的触摸屏采用的是电容式触摸屏,下面简单介绍下电容式触摸屏的原理。 电容式触摸屏主要分为两种: 1、表面电容式电容触摸屏。 表面电容式触摸屏技术是利用ito(铟锡氧化物,是一种透明的导电材料)导电膜,通过电场感应方式感测屏幕表面的触摸行为进行。但是表面电容式触摸屏有一些局限性,它只能识别一个手指或者一次触摸。 2、投射式电容触摸屏。 投射电容式触摸屏是传感器利用触摸屏电极发射出静电场线。一般用于投射电容传感技术的电容类型有两种:自我电容和交互电容。 自我电容又称绝对电容,是最广为采用的一种方法,自我电容通常是指扫描电极与地构成的电容。在玻璃表面有用ito制成的横向与纵向的扫描电极,这些电极和地之间就构成一个电容的两极。当用手或触摸笔触摸的时候就会并联一个电容到电路中去,从而使在该条扫描线上的总体的电容量有所改变。在扫描的时候,控制ic依次扫描纵向和横向电极,并根据扫描前后的电容变化来确定触摸点坐标位置。笔记本电脑触摸输入板就是采用的这种方式,笔记本电脑的输入板采用xy的传感电极阵列形成一个传感格子,当手指靠近触摸输入板时,在手指和传感电极之间产生一个小量电荷。采用特定的运算法则处理来自行、列传感器的信号来确定手指的位置。 交互电容又叫做跨越电容,它是在玻璃表面的横向和纵向的ito电极的交叉处形成电容。交互电容的扫描方式就是扫描每个交叉处的电容变化,来判定触摸点的位置。当触摸的时候就会影响到相邻电极的耦合,从而改变交叉处的电容量,交互电容的扫面方法可以侦测到每个交叉点的电容值和触摸后电容变化,因而它需要的扫描时间与自我电容的扫描方式相比要长一些,需要扫描检测xy根电极。目前智能手机/平板电脑等的触摸屏,都是采用交互电容技术。 正点原子所选择的电容触摸屏,也是采用的是投射式电容屏(交互电容类型),所以后面仅以投射式电容屏作为介绍。 透射式电容触摸屏采用纵横两列电极组成感应矩阵,来感应触摸。以两个交叉的电极矩阵,即:x轴电极和y轴电极,来检测每一格感应单元的电容变化,如图39.1.2.1所示:

图39.1.2.1投射式电容屏电极矩阵示意图 示意图中的电极,实际是透明的,这里是为了方便大家理解。图中,x、y轴的透明电极电容屏的精度、分辨率与x、y轴的通道数有关,通道数越多,精度越高。以上就是电容触摸屏的基本原理,接下来看看电容触摸屏的优缺点: 电容触摸屏的优点:手感好、无需校准、支持多点触摸、透光性好。 电容触摸屏的缺点:成本高、精度不高、抗干扰能力差。 这里特别提醒大家电容触摸屏对工作环境的要求是比较高的,在潮湿、多尘、高低温环境下面,都是不适合使用电容屏的。 电容触摸屏一般都需要一个驱动ic来检测电容触摸,正点原子的电容触摸屏使用的是iic接口输出触摸数据的触摸芯片。正点原子7’tftlcd模块的电容触摸屏,采用的是15*10的驱动结构(10个感应通道,15个驱动通道),采用的是gt911/ft5206作为驱动ic。正点原子4.3’tftlcd模块采用的驱动ic是:gt9xxx(gt9147/gt917s/gt911/gt1151/gt9271),不同型号感应通道和驱动通道数量都不一样,详看数据手册,但是这些驱动ic驱动方式都类似,这里我们以gt9147为例给大家做介绍,其他的大家参考着学习即可。 gt9147与mcu通过4根线连接:sda、scl、rst和int。gt9147的iic地址,可以是0x14或者0x5d,当复位结束后的5ms内,如果int是高电平,则使用0x14作为地址,否则使用0x5d作为地址,具体的设置过程,请看:gt9147数据手册.pdf这个文档。本章我们使用0x14作为器件地址(不含最低位,换算成读写命令则是读:0x29,写:0x28),接下来,介绍一下gt9147的几个重要的寄存器。 1,控制命令寄存器(0x8040) 该寄存器可以写入不同值,实现不同的控制,我们一般使用0和2这两个值,写入2,即可软复位gt9147。在硬复位之后,一般要往该寄存器写2,实行软复位。然后,写入0,即可正常读取坐标数据(并且会结束软复位)。 2,配置寄存器组(0x8047~0x8100) 这里共186个寄存器,用于配置gt9147的各个参数,这些配置一般由厂家提供给我们(一个数组),所以我们只需要将厂家给我们的配置,写入到这些寄存器里面,即可完成gt9147的配置。由于gt9147可以保存配置信息(可写入内部flash,从而不需要每次上电都更新配置),我们有几点注意的地方提醒大家:1,0x8047寄存器用于指示配置文件版本号,程序写入的版本号,必须大于等于gt9147本地保存的版本号,才可以更新配置。2,0x80ff寄存器用于存储校验和,使得0x8047~0x80ff之间所有数据之和为0。3,0x8100用于控制是否将配置保存在本地,写0,则不保存配置,写1则保存配置。 3,产品id寄存器(0x8140~0x8143) 这里总共由4个寄存器组成,用于保存产品id,对于gt9147,这4个寄存器读出来就是:9,1,4,7四个字符(ascii码格式)。因此,我们可以通过这4个寄存器的值,来判断驱动ic的型号,以便执行不同的初始化。 4,状态寄存器(0x814e) 该寄存器各位描述如表表39.1.2.1所示: 寄存器 bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 0x814e buffer状态 大点 接近有效 按键 有效触点个数 表39.1.2.1 状态寄存器各位描述 这里,我们仅关心最高位和最低4位,最高位用于表示buffer状态,如果有数据(坐标/按键),buffer就会是1,最低4位用于表示有效触点的个数,范围是:0~5,0,表示没有触摸,5表示有5点触摸。最后,该寄存器在每次读取后,如果bit7有效,则必须写0,清除这个位,否则不会输出下一次数据!!这个要特别注意!!! 5,坐标数据寄存器(共30个) 这里共分成5组(5个点),每组6个寄存器存储数据,以触点1的坐标数据寄存器组为例,如表39.1.2.2所示: 寄存器 bit7~0 寄存器 bit7~0 0x8150 触点1 x坐标低8位 0x8151 触点1 x坐标高8位 0x8152 触点1 y坐标低8位 0x8153 触点1 y坐标高8位 0x8154 触点1触摸尺寸低8位 0x8155 触点1触摸尺寸高8位 表39.1.2.2 触点1坐标寄存器组描述 我们一般只用到触点的x,y坐标,所以只需要读取0x81500x8153的数据,组合即可得到触点坐标。其他4组分别是:0x8158、0x8160、0x8168和0x8170等开头的16个寄存器组成,分别针对触点24的坐标。同样gt9147也支持寄存器地址自增,我们只需要发送寄存器组的首地址,然后连续读取即可,gt9147会自动地址自增,从而提高读取速度。 gt9147相关寄存器的介绍就介绍到这里,更详细的资料,请参考:gt9147编程指南.pdf这个文档。 gt9147只需要经过简单的初始化就可以正常使用了,初始化流程:硬复位延时10ms结束硬复位设置iic地址延时100ms软复位更新配置(需要时)结束软复位。此时gt9147即可正常使用了。然后,我们不停的查询0x814e寄存器,判断是否有有效触点,如果有,则读取坐标数据寄存器,得到触点坐标。特别注意,如果0x814e读到的值最高位为1,就必须对该位写0,否则无法读到下一次坐标数据。 电容式触摸屏部分,就介绍到这里。 39.1.3 触摸控制原理 前面已经简单地介绍了电阻屏和电容屏的原理,并且知道了不同类型的触摸屏其实是屏幕 触摸传感器组成。那么这里就会有两组相互独立的参数:屏幕坐标和触摸坐标。要实现触摸功能,就是要把触摸点和屏幕坐标对应起来。 我们以lcd显示屏为例,我们知道屏幕的扫描方向是可以编程设定的,而触摸点,在触摸传感器安装好后,ad值的变化向方向则是固定的,我们以最常见的屏幕坐标方向:先从左到右,再从上到下扫描为例,此时,屏幕坐标和触点ad的坐标有类似的规律:从坐标原点出发,水平方向屏幕坐标增加时,ad值的x方向也增加;屏幕坐标的y方向坐标增加,ad值的y方向也增加;坐标减少时对应的关系也类似,可以用图39.1.3.1的示意图来表示这种关系:

图39.1.3.1 屏幕坐标和触摸坐标的一种对应关系 这里再来引入两个概念,物理坐标和逻辑坐标。物理坐标指触摸屏上点的实际位置,通常以液晶上点的个数来度量。逻辑坐标指这点被触摸时a/d转换后的坐标值。仍以图39.1.3.1为例,我们假定液晶最左上角为坐标轴原点a,在液晶上任取一点b(实际人手比像素点大得多,一次按下会有多个触点,此处取十字线交叉中心),b在x方向与a相距100个点,在y方向与a距离200个点,则这点的物理坐标b为(100,200)。如果我们触摸这一点时得到的x向a/d转换值为200,y向a/d转换值为400,则这点的逻辑坐标b’为(200,400)。 需要特别说明的是,正点原子的电容屏的参数已经在出厂时由厂家调好,所以无需进行校准,而且可以直接读到转换后的触点坐标;对于电阻屏,请大家理解并熟记物理坐标和逻辑坐标逻辑上的对应关系,我们后面编程需要用到。 39.2 硬件设计

例程功能 正点原子的触摸屏种类很多,并且设计了规格相对统一的接口。根据屏幕的种类不同,设置了相应的硬件id(正点原子自编id),可以通过软件判断触摸屏的种类。 本章实验功能简介:开机的时候先初始化lcd,读取lcd id,随后,根据lcd id判断是电阻触摸屏还是电容触摸屏,如果是电阻触摸屏,则先读取24c02的数据判断触摸屏是否已经校准过,如果没有校准,则执行校准程序,校准过后再进入电阻触摸屏测试程序,如果已经校准了,就直接进入电阻触摸屏测试程序。 如果是4.3寸电容触摸屏,则执行gt9xxx的初始化代码;如果是7寸电容触摸屏(仅支持新款7寸屏,使用ssd1963 ft5206方案),则执行ft5206的初始化代码,在初始化电容触摸屏完成后,进入电容触摸屏测试程序(电容触摸屏无需校准!!)。 电阻触摸屏测试程序和电容触摸屏测试程序基本一样,只是电容触摸屏支持最多5点同时触摸,电阻触摸屏只支持一点触摸,其他一模一样。测试界面的右上角会有一个清空的操作区域(rst),点击这个地方就会将输入全部清除,恢复白板状态。使用电阻触摸屏的时候,可以通过按key0来实现强制触摸屏校准,只要按下key0就会进入强制校准程序。硬件资源 1)led灯 led0 – pb5 2 ) 独立按键 key0 – pe4 3)eeprom at24c02 4)正点原子 2.8/3.5/4.3/7/10寸tftlcd模块(仅限mcu屏,16位8080并口驱动) 5)串口1(pa9/pa10连接在板载usb转串口芯片ch340上面)原理图 所有这些资源与stm32f1的连接图,在前面都已经介绍了,这里我们只针对tftlcd模块与stm32f1的连接端口再说明一下,tftlcd模块的触摸屏(电阻触摸屏)总共有5根线与stm32f1连接,连接电路图如图39.2.1所示:

图39.2.1 触摸屏与stm32f1的连接图 从图中可以看出:t_sck、t_miso、t_mosi、t_pen和t_cs分别连接在stm32f1的pb1、pb2、pf9、pf10和pf11上。 如果是电容式触摸屏,我们的接口和电阻式触摸屏一样(上图右侧接口),只是没有用到五根线了,而是四根线,分别是:t_pen(ct_int)、t_cs(ct_rst)、t_clk(ct_scl)和t_mosi(ct_sda)。其中:ct_int、ct_rst、ct_scl和ct_sda分别是gt9147/ft5206的:中断输出信号、复位信号,iic的scl和sda信号。我们用查询的方式读取gt9147/ft5206的数据,对于ft5206没有用到中断信号(ct_int),所以同stm32f1的连接,最少只需要3根线即可,不过gt9147等ic还需要用到ct_int做iic地址设定,所以需要4根线连接。 39.3 程序设计 39.3.1 hal库驱动 触摸芯片我们使用到的是iic和spi的驱动,这部分的时序分析可以参考之前iic/spi的章节,我们直接使用的是软件模拟的方式,所以只需要使用hal库的驱动的gpio操作部分。 触摸ic驱动步骤 1)初始化通信接口与其io(使能时钟、配置gpio工作模式) 触摸ic用到的gpio口,主要是pb1、pb2、pf9、pf10和pf11,因为都是用软件模拟的方式,因此在这里我们只需使能gpiob和gpiof时钟即可。参考代码如下: __hal_rcc_gpiob_clk_enable(); /* 使能gpiob时钟 / __hal_rcc_gpiof_clk_enable(); / 使能gpiof时钟 */ gpio模式设置通过调用hal_gpio_init函数实现,详见本例程源码。 2)编写通信协议基础读写函数 通过参考时序图,在iic驱动或spi驱动基础上,编写基础读写函数。读写函数均以一字节数据进行操作。 3)参考触摸ic时序图,编写触摸ic读写驱动函数 根据触摸ic的读写时序进行编写触摸ic的读写函数,详见本例程源码。 4)编写坐标获取函数(电阻触摸屏和电容触摸屏) 查阅数据手册获得命令词(电阻触摸屏)/寄存器(电容触摸屏),通过读写函数获取坐标数据,详见本例程源码。 39.3.2 程序流程图

图39.3.2.1 触摸屏实验流程图 39.3.3 程序解析 这里我们只讲解核心代码,详细的源码请大家参考光盘本实验对应源码。touch驱动源码包括如下文件:ctiic.c、ctiic.h、ft5206.c、ft5206.h、gt9xxx.c、gt9xxx.h、touch.c和touch.h。 由于正点原子的tftlcd的型号很多,触摸控制这部分驱动代码根据不同屏幕搭载的触摸芯片驱动而有不同,在我们的屏幕上使用的是lcd id来帮助软件上区分。为了解决多种驱动芯片的问题,我们设计了touch.c/touch.h这两个文件统一管理各类型的驱动。不同的驱动芯片类型可以在touch.c中集中添加,并通过touch.c中的接口统一调用,不同的触摸芯片各自编写独立的.c/.h文件,需要时被touch.c调用。电阻触摸屏相关代码也在touch.c中实现。

触摸管理驱动代码 因为需要支持的触摸驱动比较多,为了方便管理和添加新的驱动,我们用touch.c文件来统一管理这些触摸驱动,然后针对各类触摸芯片编写独立的驱动。为了方便管理触摸,我们在touch.h中定义一个用于管理触摸信息的结构体类型,具体代码如下:

/* 触摸屏控制器 */

typedef struct

{

uint8_t (*init)(void); /* 初始化触摸屏控制器 */

uint8_t (*scan)(uint8_t); /* 扫描触摸屏.0,屏幕扫描;1,物理坐标; */

void (*adjust)(void); /* 触摸屏校准 */

uint16_t x[ct_max_touch]; /* 当前坐标 */

uint16_t y[ct_max_touch]; /* 电容屏有最多10组坐标,电阻屏则用x[0],y[0]代表:此次扫

描时,触屏的坐标,用 x[9],y[9]存储第一次按下时的坐标 */

uint16_t sta; /* 笔的状态

* b15:按下1/松开0;

* b14:0,没有按键按下;1,有按键按下.

* b13~b10:保留

* b9~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)

*/

/* 5点校准触摸屏校准参数(电容屏不需要校准) */

float xfac; /* 5点校准法x方向比例因子 */

float yfac; /* 5点校准法y方向比例因子 */

short xc; /* 中心x坐标物理值(ad值) */

short yc; /* 中心y坐标物理值(ad值) */

/* 新增的参数,当触摸屏的左右上下完全颠倒时需要用到.

* b0: 0, 竖屏(适合左右为x坐标,上下为y坐标的tp)

* 1, 横屏(适合左右为y坐标,上下为x坐标的tp)

* b1~6: 保留.

* b7: 0, 电阻屏

* 1, 电容屏

*/

uint8_t touchtype;

} _m_tp_dev;

extern _m_tp_dev tp_dev; /* 触屏控制器在touch.c里面定义 */

这里我们定义了函数指针,只要把相对应的触摸芯片的函数指针赋值给它,就可以通过这个通用接口很方便调用不同芯片的函数接口。正点原子不同的触摸屏区别如下: 1、在使用4.3寸屏、10.1寸屏电容屏时,使用的是汇顶科技的gt9xxx系列触摸屏驱动ic,这是一个iic接口的驱动芯片,我们要编写gt9xxx系列芯片的初始化程序,并编写一个坐标扫描程序,这里我们先预留这两个接口分别为gt9xxx_init()和gt9xxx_scan(),在gt9xxx.c文件中再专门实现这两个驱动,标记使用的为电容屏; 2、类似地,在使用ssd1963 7寸屏、7寸800480/1024600 rgb屏时,我们的屏幕搭载的触摸驱动芯片是ft5206/gt911,ft5206触摸ic预留这两个接口分别为ft5206_init()和ft5206_scan(),在ft5206.c文件中再专门实现这两个驱动,标记使用的为电容屏;gt911也是调用gtxxx_init()和gt9xxx_scan()接口。 3、当为其它id时,默认为电阻屏,而电阻屏默认使用的是spi接口的xpt2046芯片。由于电阻屏存在线性误差,所以在使用前需要进行校准,这也是为什么在前面的结构体类型中存在关于校准参数的成员。为了避免每次都要进行校准的麻烦,所以会使用at24c02来存储校准成功后的数据。如何进行校准也会在后面进行讲解。作为电阻屏,它也有一个扫描坐标函数即tp_scan()。 (*init)(void)这个结构体函数指针,默认指向tp_init的,而在tp_init里对触摸屏进行初始化并对(*scan)(uint8_t)函数指针根据触摸芯片类型重新做了指向。在这里简单看一下touch.c的触摸屏初始化函数tp_init,其代码如下:

/**

* @brief 触摸屏初始化

* @param 无

* @retval 0,没有进行校准

* 1,进行过校准

*/

uint8_t tp_init(void)

{

gpio_inittypedef gpio_init_struct;

tp_dev.touchtype = 0; /* 默认设置(电阻屏 & 竖屏) */

tp_dev.touchtype |= lcddev.dir & 0x01; /* 根据lcd判定是横屏还是竖屏 */

if (lcddev.id == 0x5510 || lcddev.id == 0x4342 || lcddev.id == 0x4384 || lcddev.id == 0x1018)

{ /* 电容触摸屏,4.3寸/10.1寸屏 */

gt9xxx_init();

tp_dev.scan = gt9xxx_scan; /* 扫描函数指向gt9147触摸屏扫描 */

tp_dev.touchtype |= 0x80; /* 电容屏 */

return 0;

}

else if (lcddev.id == 0x1963 || lcddev.id == 0x7084 || lcddev.id == 0x7016)

{ /* ssd1963 7寸屏或者 7寸800*480/1024*600 rgb屏 */

if (!ft5206_init())

{

tp_dev.scan = ft5206_scan; /* 扫描函数指向ft5206触摸屏扫描 */

}

else

{

gt9xxx_init();

tp_dev.scan = gt9xxx_scan; /* 扫描函数指向gt9147触摸屏扫描 */

}

tp_dev.touchtype |= 0x80; /* 电容屏 */

return 0;

}

else

{

t_pen_gpio_clk_enable(); /* t_pen脚时钟使能 */

t_cs_gpio_clk_enable(); /* t_cs脚时钟使能 */

t_miso_gpio_clk_enable(); /* t_miso脚时钟使能 */

t_mosi_gpio_clk_enable(); /* t_mosi脚时钟使能 */

t_clk_gpio_clk_enable(); /* t_clk脚时钟使能 */

gpio_init_struct.pin = t_pen_gpio_pin;

gpio_init_struct.mode = gpio_mode_input; /* 输入 */

gpio_init_struct.pull = gpio_pullup; /* 上拉 */

gpio_init_struct.speed = gpio_speed_freq_high; /* 高速 */

hal_gpio_init(t_pen_gpio_port, &gpio_init_struct); /* 初始化t_pen引脚 */

gpio_init_struct.pin = t_miso_gpio_pin;

hal_gpio_init(t_miso_gpio_port, &gpio_init_struct); /* 初始化t_miso引脚* /

gpio_init_struct.pin = t_mosi_gpio_pin;

gpio_init_struct.mode = gpio_mode_output_pp; /* 推挽输出 */

gpio_init_struct.pull = gpio_pullup; /* 上拉 */

gpio_init_struct.speed = gpio_speed_freq_high; /* 高速 */

hal_gpio_init(t_mosi_gpio_port, &gpio_init_struct); /* 初始化t_mosi引脚 */

gpio_init_struct.pin = t_clk_gpio_pin;

hal_gpio_init(t_clk_gpio_port, &gpio_init_struct); /* 初始化t_clk引脚 */

gpio_init_struct.pin = t_cs_gpio_pin;

hal_gpio_init(t_cs_gpio_port, &gpio_init_struct); /* 初始化t_cs引脚 */

tp_read_xy(&tp_dev.x[0], &tp_dev.y[0]); /* 第一次读取初始化 */

at24cxx_init(); /* 初始化24cxx */

if (tp_get_adjust_data())

{

return 0; /* 已经校准 */

}

else /* 未校准? */

{

lcd_clear(white); /* 清屏 */

tp_adjust(); /* 屏幕校准 */

tp_save_adjust_data();

}

tp_get_adjust_data();

}

return 1;

}

正点原子的电容屏在出厂时已经由厂家较对好参数了,而电阻屏由于工艺和每个屏的线性有所差异,我们需要先对其进行“校准”,我们在下一点补充说明它的实现。 通过上面的触摸初始化后,我们就可以读取相关的触点信息用于显示编程了,注意到上面还有很多个函数还没实现,比如读取坐标和校准,我们在接下来的代码中将它补充完整。 2. 电阻屏触摸函数 前面我们介绍过了电阻式触摸屏的原理,由于电阻屏的驱动代码都比较类似,我们决定把电阻屏的驱动函数直接添加在touch.c/touch.h中实现。 在touch.c的初始化函数tp_init中,对使用到的spi接口io进行了初始化。接下来介绍一下获取触摸点在屏幕上坐标的算法:先获取逻辑坐标(ad值),再转换成屏幕坐标。 如何获取逻辑坐标(ad值),在前面已经分析过了,所以这里我们看一下tp_read_ad()函数接口:

/**

* @brief spi读数据

* @note 从触摸屏ic读取adc值

* @param cmd: 指令

* @retval 读取到的数据,adc值(12bit)

*/

static uint16_t tp_read_ad(uint8_t cmd)

{

uint8_t count = 0;

uint16_t num = 0;

t_clk(0); /* 先拉低时钟 */

t_mosi(0); /* 拉低数据线 */

t_cs(0); /* 选中触摸屏ic */

tp_write_byte(cmd); /* 发送命令字 */

delay_us(6); /* ads7846的转换时间最长为6us */

t_clk(0);

delay_us(1);

t_clk(1); /* 给1个时钟,清除busy */

delay_us(1);

t_clk(0);

for (count = 0; count < 16; count ) /* 读出16位数据,只有高12位有效 */

{

num <<= 1;

t_clk(0); /* 下降沿有效 */

delay_us(1);

t_clk(1);

if (t_miso)num ;

}

num >>= 4; /* 只有高12位有效. */

t_cs(1); /* 释放片选 */

return num;

}

这里我们使用的是软件模拟spi,遵照时序编写spi读函数接口。而发送命令字是通过写函数tp_write_byte来实现,详看源码。 一次读取的误差会很大,我们采用平均值滤波的方法,多次读取数据并丢弃波动最大的最大和最小值,取余下的平均值。具体可以查看tp_read_xoy函数内部实现。 /* 电阻触摸驱动芯片 数据采集 滤波用参数 */

#define tp_read_times 5 /* 读取次数 */

#define tp_lost_val 1 /* 丢弃值 */

/**

* @brief 读取一个坐标值(x或者y)

* @note 连续读取tp_read_times次数据,对这些数据升序排列,

* 然后去掉最低和最高tp_lost_val个数, 取平均值

* 设置时需满足: tp_read_times > 2*tp_lost_val 的条件

* @param cmd : 指令

* @arg 0xd0: 读取x轴坐标(@竖屏状态,横屏状态和y对调.)

* @arg 0x90: 读取y轴坐标(@竖屏状态,横屏状态和x对调.)

*

* @retval 读取到的数据(滤波后的), adc值(12bit)

*/

static uint16_t tp_read_xoy(uint8_t cmd)

{

uint16_t i, j;

uint16_t buf[tp_read_times];

uint16_t sum = 0;

uint16_t temp;

for (i = 0; i < tp_read_times; i ) /* 先读取tp_read_times次数据 */

{

buf[i] = tp_read_ad(cmd);

}

for (i = 0; i < tp_read_times - 1; i ) /* 对数据进行排序 */

{

for (j = i 1; j < tp_read_times; j )

{

if (buf[i] > buf[j]) /* 升序排列 */

{

temp = buf[i];

buf[i] = buf[j];

buf[j] = temp;

}

}

}

sum = 0;

for (i = tp_lost_val; i < tp_read_times - tp_lost_val; i )

{ /* 去掉两端的丢弃值 */

sum = buf[i]; /* 累加去掉丢弃值以后的数据. */

}

temp = sum / (tp_read_times - 2 * tp_lost_val); /* 取平均值 */

return temp;

}

有了前述代码,我们就可以通过tp_read_xoy(uint8_t cmd)接口调取需要的x或者y坐标的ad值了。这里我们加上横屏或者竖屏的处理代码,编写一个可以通过指针一次得到x和y的两个ad值的接口,代码如下:

/**

* @brief 读取x, y坐标

* @param x,y: 读取到的坐标值

* @retval 无

*/

static void tp_read_xy(uint16_t *x, uint16_t *y)

{

uint16_t xval, yval;

if (tp_dev.touchtype & 0x01) /* x,y方向与屏幕相反 */

{

xval = tp_read_xoy(0x90); /* 读取x轴坐标ad值, 并进行方向变换 */

yval = tp_read_xoy(0xd0); /* 读取y轴坐标ad值 */

}

else /* x,y方向与屏幕相同 */

{

xval = tp_read_xoy(0xd0); /* 读取x轴坐标ad值 */

yval = tp_read_xoy(0x90); /* 读取y轴坐标ad值 */

}

*x = xval;

*y = yval;

}

为了进一步保证参数的精度,我们连续读两次触摸数据并取平均值作为最后的触摸参数,并对这两次滤波值平均后再传给目标存储区,由于ad的精度为12位,故该函数读取坐标的值0~4095,tp_read_xy2的代码如下: /* 连续两次读取x,y坐标的数据误差最大允许值 */

#define tp_err_range 50 /* 误差范围 */

/**

* @brief 连续读取2次触摸ic数据, 并滤波

* @note 连续2次读取触摸屏ic,且这两次的偏差不能超过err_range,满足

* 条件,则认为读数正确,否则读数错误.该函数能大大提高准确度.

*

* @param x,y: 读取到的坐标值

* @retval 0, 失败; 1, 成功;

*/

static uint8_t tp_read_xy2(uint16_t *x, uint16_t *y)

{

uint16_t x1, y1;

uint16_t x2, y2;

tp_read_xy(&x1, &y1); /* 读取第一次数据 */

tp_read_xy(&x2, &y2); /* 读取第二次数据 */

/* 前后两次采样在 -tp_err_range内 */

if (((x2 <= x1 && x1

((y2 <= y1 && y1

{

*x = (x1 x2) / 2;

*y = (y1 y2) / 2;

return 1;

}

return 0;

}

根据以上的流程,可以得到电阻屏触摸点的比较精确的ad信息。每次触摸屏幕时会对应一组x、y的ad值,由于坐标的ad值是在x、y方向都是线性的,很容易想到要把触摸信息的ad值和屏幕坐标联系起来,这里需要编写一个坐标转换函数,前面在编写初始化接口时讲到的校准函数这时候就派上用场了。 从前面的知识我们就知道触摸屏的ad的xad、yad可以构成一个逻辑平面,lcd屏的屏幕坐标x、y也是一个逻辑平面,由于存在误差,这两个平面并不重合,校准的作用就是要将逻辑平面映射到物理平面上,即得到触点在液晶屏上的位置坐标。校准算法的中心思想也就是要建立这样一个映射函数现有的校准算法大多是基于线性校准,即首先假定物理平面和逻辑平面之间的误差是线性误差,由旋转和偏移形成。 常用的电阻式触摸屏矫正方法有两点校准法和三点校准法。本文这里介绍的是结合了不同的电阻式触摸屏矫正法的优化算法:五点校正法。其中主要的原理是使用4点矫正法的比例运算以及三点矫正法的基准点运算。五点校正法优势在于可以更加精确的计算出x和y方向的比例缩放系数,同时提供了中心基准点,对于一些线性电阻系数比较差的电阻式触摸屏有很好的校正功能。校正相关的变量主要有: ?x[5],y[5]五点定位的物理坐标(lcd坐标) ?xl[5],yl[5]五点定位的逻辑坐标(触摸ad值) ?kx,ky横纵方向伸缩系数 ?xlc,ylc中心基点逻辑坐标 ?xc,yc中心基点物理坐标(数值采用lcd显示屏的物理长宽分辨率的一半) x[5],y[5]五点定位的物理坐标是已知的,其中4点分别设置在lcd的角落,一点设置在lcd正中心,作为基准矫正点,校正关键点和距离布局如图39.3.3.1所示。

图39.3.3.1 电阻屏五点校准法的参考点设定 校正步骤如下:

通过先后点击lcd的4个角落的矫正点,获取4个角落的逻辑坐标值。计算屏幕坐标和四点间距: s1 = x[1]- x[0] s3 = x[2]- x[3] s2 = y[2]- y[1] s4 = y[3]- y[0] 一般取点可以人为的设定s1=s3和s2=s4,以方便运算。 计算逻辑坐标的四点“间距”,由于实际触点肯定会存在误差,所以触摸点会落在实际设定点的更大范围内,在图39.3.1中,设定点为五个点,但实际采样时触点有时会落在稍大的外圈范围,图中用红色的圆圈标注了,所以有必要设定一个误差范围: s1’ = xl[1]- xl[0] s3’ = xl[2]- xl[3] s2’ = yl[2]- yl[1] s4’ = yl[3]- yl[0] 由于触点的误差,对于逻辑点s1’和s3’则大概率不会相等,同样的,s2’和s4’也很难取到相等的点,那么为了简化计算,我们强制以(s1’ s3’)/2的线长作一个矩形一边,以(s2’ s4’)/2为矩形另一边,这样构建的矩形在误差范围是可以接受的,也方便计算,于是得到x和y方向的近似缩放系数: kx =(s1’ s3’) / 2 / s1 ky =(s2’ s4’) / 2 / s2点击lcd正中心,获取中心点的逻辑坐标,作为矫正的基准点。这里也同样的需要限制误差,之后可以得到一个中心点的ad值坐标(xl[4],yl[4]),这个点的ad值我们就作为我们对比的基准点,即xl[4]=xlc,yl[4]=ylc;完成以上步骤则校正完成。下次点击触摸屏的时候获取的逻辑值xl和yl,便可以按下以公式转换为物理坐标: x = (xl - xlc) / kx xc y = (yl - ylc) / ky yc 最后一步的转换公式可能不好理解,大家换个角度,如果我们求到的缩放比例是正确的,在取新的触摸的时候,这个触摸点的逻辑坐标和物理坐标的转换,必然与中心点在两方向上的缩放比例相等,用中学数学直线斜率相等的情况,变换便可得到上述公式。 通过上述得到校准参数后,在以后的使用中,我们把所有得到的物理坐标都按照这个关系式来计算,得到的就是触摸点的屏幕坐标。为了省去每次都需要校准的麻烦,我们保存这些参数到at24cxx的指定扇区地址,这样只要校准一次就可以重复使用这些参数了。 根据上面的原理,我们设计的校准函数tp_adjust如下:

/**

* @brief 触摸屏校准代码

* @note 使用五点校准法(具体原理请百度)

* 本函数得到x轴/y轴比例因子xfac/yfac及物理中心坐标值(xc,yc)等4个参数

* 我们规定: 物理坐标即ad采集到的坐标值,范围是0~4095.

* 逻辑坐标即lcd屏幕的坐标, 范围为lcd屏幕的分辨率.

*

* @param 无

* @retval 无

*/

void tp_adjust(void)

{

uint16_t pxy[5][2]; /* 物理坐标缓存值 */

uint8_t cnt = 0;

short s1, s2, s3, s4; /* 4个点的坐标差值 */

double px, py; /* x,y轴物理坐标比例,用于判定是否校准成功 */

uint16_t outtime = 0;

cnt = 0;

lcd_clear(white); /* 清屏 */

lcd_show_string(40, 40, 160, 100, 16, tp_remind_msg_tbl, red);/*显示提示信息*/

tp_draw_touch_point(20, 20, red); /* 画点1 */

tp_dev.sta = 0; /* 消除触发信号 */

while (1) /* 如果连续10秒钟没有按下,则自动退出 */

{

tp_dev.scan(1); /* 扫描物理坐标 */

if ((tp_dev.sta & 0xc000) == tp_cath_pres)

{ /* 按键按下了一次(此时按键松开了) */

outtime = 0;

tp_dev.sta &= ~tp_cath_pres; /* 标记按键已经被处理过了. */

pxy[cnt][0] = tp_dev.x[0]; /* 保存x物理坐标 */

pxy[cnt][1] = tp_dev.y[0]; /* 保存y物理坐标 */

cnt ;

switch (cnt)

{

case 1:

tp_draw_touch_point(20, 20, white); /* 清点1 */

tp_draw_touch_point(lcddev.width - 20, 20, red); /* 画点2 */

break;

case 2:

tp_draw_touch_point(lcddev.width-20, 20, white); /* 清点2 */

tp_draw_touch_point(20, lcddev.height - 20, red); /* 画点3 */

break;

case 3:

tp_draw_touch_point(20, lcddev.height-20, white); /* 清点3 */

/* 画点4 */

tp_draw_touch_point(lcddev.width- 20, lcddev.height - 20, red);

break;

case 4:

lcd_clear(white); /* 画第五个点了, 直接清屏 */

/* 画点5 */

tp_draw_touch_point(lcddev.width / 2, lcddev.height / 2, red);

break;

case 5: /* 全部5个点已经得到 */

s1=pxy[1][0]-pxy[0][0]; /*第2个点和第1个点的x轴物理坐标差值(ad值)*/

s3=pxy[3][0]-pxy[2][0]; /*第4个点和第3个点的x轴物理坐标差值(ad值)*/

s2=pxy[3][1]-pxy[1][1]; /*第4个点和第2个点的y轴物理坐标差值(ad值)*/

s4=pxy[2][1]-pxy[0][1]; /*第3个点和第1个点的y轴物理坐标差值(ad值)*/

px = (double)s1 / s3; /* x轴比例因子 */

py = (double)s2 / s4; /* y轴比例因子 */

if (px < 0)px = -px; /* 负数改正数 */

if (py < 0)py = -py; /* 负数改正数 */

if (px < 0.95 || px > 1.05 || py < 0.95 || py > 1.05 ||

abs(s1)>4095||abs(s2)>4095||abs(s3)>4095||abs(s4)>4095||

abs(s1)==0 ||abs(s2)==0||abs(s3)==0||abs(s4)==0)

{ /* 比例不合格,差值大于坐标范围或等于0,重绘校准图形 */

cnt = 0;

/* 清除点5 */

tp_draw_touch_point(lcddev.width/2, lcddev.height/2, white);

tp_draw_touch_point(20, 20, red); /* 重新画点1 */

tp_adjust_info_show(pxy, px, py); /* 显示当前信息,方便找问题 */

continue;

}

tp_dev.xfac = (float)(s1 s3) / (2 * (lcddev.width - 40));

tp_dev.yfac = (float)(s2 s4) / (2 * (lcddev.height - 40));

tp_dev.xc = pxy[4][0]; /* x轴,物理中心坐标 */

tp_dev.yc = pxy[4][1]; /* y轴,物理中心坐标 */

lcd_clear(white); /* 清屏 */

lcd_show_string(35, 110, lcddev.width, lcddev.height, 16,

"touch screen adjust ok!", blue); /* 校准完成 */

delay_ms(1000);

tp_save_adjust_data();

lcd_clear(white); /* 清屏 */

return; /* 校正完成 */

}

}

delay_ms(10);

outtime ;

if (outtime > 1000)

{

tp_get_adjust_data();

break;

}

}

}

注意该函数里面多次使用了lcddev.width和lcddev.height,用于坐标设置,故在程序调用前需要预先初始化lcd得 到lcd的一些屏幕信息,主要是为了兼容不同尺寸的lcd(比如320*240、480*320和800*480的屏都可以兼容)。

有了校准参数后,由于我们需要频繁地进行屏幕坐标和物理坐标的转换,我们为电阻屏增加一个tp_scan(uint8_t mode)用于转换,为了实际使用上更灵活,我们使这个参数支持物理坐标和屏幕坐标,设计的函数如下:

/**

* @brief 触摸按键扫描

* @param mode: 坐标模式

* @arg 0, 屏幕坐标;

* @arg 1, 物理坐标(校准等特殊场合用)

*

* @retval 0, 触屏无触摸; 1, 触屏有触摸;

*/

uint8_t tp_scan(uint8_t mode)

{

if (t_pen == 0) /* 有按键按下 */

{

if (mode) /* 读取物理坐标, 无需转换 */

{

tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0]);

}

else if (tp_read_xy2(&tp_dev.x[0], &tp_dev.y[0]))/* 读取屏幕坐标, 需要转换*/

{ /* 将x轴 物理坐标转换成逻辑坐标(即对应lcd屏幕上面的x坐标值) */

tp_dev.x[0] = (signed short)(tp_dev.x[0] - tp_dev.xc)

/ tp_dev.xfac lcddev.width / 2;

/* 将y轴 物理坐标转换成逻辑坐标(即对应lcd屏幕上面的y坐标值) */

tp_dev.y[0] = (signed short)(tp_dev.y[0] - tp_dev.yc)

/ tp_dev.yfac lcddev.height / 2;

}

if ((tp_dev.sta & tp_pres_down) == 0) /* 之前没有被按下 */

{

tp_dev.sta = tp_pres_down | tp_cath_pres; /* 按键按下 */

tp_dev.x[ct_max_touch - 1] = tp_dev.x[0]; /* 记录第一次按下时的坐标 */

tp_dev.y[ct_max_touch - 1] = tp_dev.y[0];

}

}

else

{

if (tp_dev.sta & tp_pres_down) /* 之前是被按下的 */

{

tp_dev.sta &= ~tp_pres_down; /* 标记按键松开 */

}

else /* 之前就没有被按下 */

{

tp_dev.x[ct_max_touch - 1] = 0;

tp_dev.y[ct_max_touch - 1] = 0;

tp_dev.x[0] = 0xffff;

tp_dev.y[0] = 0xffff;

}

}

return tp_dev.sta & tp_pres_down; /* 返回当前的触屏状态 */

}

要进行电阻触摸屏的触摸扫描,只要调取tp_scan()函数,就能灵活地得到触摸坐标。电阻屏的触摸就讲到这里。 3. 电容屏触摸驱动代码 电容触摸芯片使用的是iic接口。iic接口部分代码,我们可以参考 myiic.c和myiic.h的代码,为了使代码独立,我们在“touch”文件夹下也是采用软件模拟iic的方式实现ctiic.c和ctiic.h,这样io的使用更灵活,这里部分参考iic章节的知识就可以了,这里不重复介绍了。 电容触摸芯片除了iic接口相关引脚ct_scl和ct_sda,还有ct_int和ct_rst,接口图如图39.3.3.2所示。

图39.3.3.2 电容触摸芯片接口图 gt9xxx_init的实现也比较简单,实现ct_int和ct_rst引脚初始化和调用ct_iic_init函数实现对ct_sda和ct_scl初始化。由于电容触摸屏在设计时是根据屏幕进行参数设计的,参数已经保存在芯片内部。所以在初始化后,就可以参考手册推荐的iic时序从相对应的坐标数据寄存器中把对应的xy坐标数据读出来,再通过数据整理转成lcd坐标。 与电阻屏不同的是,我们是通过iic读取状态寄存器的值并非引脚电平。而gt9xxx系列是支持中断或轮询方式得到触摸状态,本实验使用的是轮询方式: 1、按照读时序,先读取寄存器0x814e,若当前buffer(buffer status为1)数据准备好,则依据有效触点个数到相对应的坐标数据地址处进行坐标数据读取。 2、若在1中发现buffer数据(buffer status为0)未准备好,则等待1ms再进行读取。 这样,gt9xxx_scan()函数的实现如下:

/* gt9xxx 10个触摸点(最多) 对应的寄存器表 */

const uint16_t gt9xxx_tpx_tbl[10] =

{

gt9xxx_tp1_reg,gt9xxx_tp2_reg,gt9xxx_tp3_reg,gt9xxx_tp4_reg,gt9xxx_tp5_reg,

gt9xxx_tp6_reg,gt9xxx_tp7_reg,gt9xxx_tp8_reg,gt9xxx_tp9_reg,gt9xxx_tp10_reg,

};

/**

* @brief 扫描触摸屏(采用查询方式)

* @param mode : 电容屏未用到次参数, 为了兼容电阻屏

* @retval 当前触屏状态

* @arg 0, 触屏无触摸;

* @arg 1, 触屏有触摸;

*/

uint8_t gt9xxx_scan(uint8_t mode)

{

uint8_t buf[4];

uint8_t i = 0;

uint8_t res = 0;

uint16_t temp;

uint16_t tempsta;

static uint8_t t = 0; /* 控制查询间隔,从而降低cpu占用率 */

t ;

if ((t % 10) == 0 || t < 10)

{ /* 空闲时,每进入10次ctp_scan函数才检测1次,从而节省cpu使用率 */

gt9xxx_rd_reg(gt9xxx_gstid_reg, &mode, 1); /* 读取触摸点的状态 */

if ((mode & 0x80) && ((mode & 0xf) <= g_gt_tnum))

{

i = 0;

gt9xxx_wr_reg(gt9xxx_gstid_reg, &i, 1); /* 清标志 */

}

if ((mode & 0xf) && ((mode & 0xf) <= g_gt_tnum))

{

/* 将点的个数转换为1的位数,匹配tp_dev.sta定义 */

temp = 0xffff << (mode & 0xf);

tempsta = tp_dev.sta; /* 保存当前的tp_dev.sta值 */

tp_dev.sta = (~temp) | tp_pres_down | tp_cath_pres;

tp_dev.x[g_gt_tnum - 1] = tp_dev.x[0]; /* 保存触点0的数据 */

tp_dev.y[g_gt_tnum - 1] = tp_dev.y[0];

for (i = 0; i < g_gt_tnum; i )

{

if (tp_dev.sta & (1 << i)) /* 触摸有效? */

{

gt9xxx_rd_reg(gt9xxx_tpx_tbl[i], buf, 4); /* 读取xy坐标值 */

if (lcddev.id == 0x5510) /* 4.3寸800*480 mcu屏 */

{

if (tp_dev.touchtype & 0x01) /* 横屏 */

{

tp_dev.y[i] = ((uint16_t)buf[1] << 8) buf[0];

tp_dev.x[i] = 800 - (((uint16_t)buf[3] << 8) buf[2]);

}

else

{

tp_dev.x[i] = ((uint16_t)buf[1] << 8) buf[0];

tp_dev.y[i] = ((uint16_t)buf[3] << 8) buf[2];

}

}

else /* 其他型号 */

{

if (tp_dev.touchtype & 0x01) /* 横屏 */

{

tp_dev.x[i] = (((uint16_t)buf[1] << 8) buf[0]);

tp_dev.y[i] = (((uint16_t)buf[3] << 8) buf[2]);

}

else

{

tp_dev.x[i]=lcddev.width-(((uint16_t)buf[3]<<8) buf[2]);

tp_dev.y[i] = ((uint16_t)buf[1] << 8) buf[0];

}

}

//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] > 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[g_gt_tnum - 1];

tp_dev.y[0] = tp_dev.y[g_gt_tnum - 1];

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 &= ~tp_pres_down; /* 标记按键松开 */

}

else /* 之前就没有被按下 */

{

tp_dev.x[0] = 0xffff;

tp_dev.y[0] = 0xffff;

tp_dev.sta &= 0xe000; /* 清除点有效标记 */

}

}

if (t > 240)t = 10; /* 重新从10开始计数 */

return res;

}

大家可以打开gt9xxx芯片对应的编程手册,对照时序,即可理解上述的实现过程,只是程序中为了匹配多种屏幕和横屏显示,添加了一些代码。 电容屏驱动ft5206.c/ft5206.h的驱动实现与gt9xxx的实现类似,大家参考本例程源码即可。 电容屏的触摸实验代码讲解到这里。 4. main函数和测试代码 在main.c里面编程如下代码:

void rtp_test(void)

{

uint8_t key;

uint8_t i = 0;

while (1)

{

key = key_scan(0);

tp_dev.scan(0);

if (tp_dev.sta & tp_pres_down) /* 触摸屏被按下 */

{

if (tp_dev.x[0] < lcddev.width && tp_dev.y[0] < lcddev.height)

{

if (tp_dev.x[0] > (lcddev.width - 24) && tp_dev.y[0] < 16)

{

load_draw_dialog(); /* 清除 */

}

else

{

tp_draw_big_point(tp_dev.x[0], tp_dev.y[0], red); /* 画点 */

}

}

}

else

{

delay_ms(10); /* 没有按键按下的时候 */

}

if (key == key0_pres) /* key0按下,则执行校准程序 */

{

lcd_clear(white); /* 清屏 */

tp_adjust(); /* 屏幕校准 */

tp_save_adjust_data();

load_draw_dialog();

}

i ;

if (i % 20 == 0)led0_toggle();

}

}

/* 10个触控点的颜色(电容触摸屏用) */

const uint16_t point_color_tbl[10] = {red, green, blue, brown, yellow, magenta, cyan, lightblue, brred, gray};

void ctp_test(void)

{

uint8_t t = 0;

uint8_t i = 0;

uint16_t lastpos[10][2]; /* 最后一次的数据 */

uint8_t maxp = 5;

if (lcddev.id == 0x1018) maxp = 10;

while (1)

{

tp_dev.scan(0);

for (t = 0; t < maxp; t )

{

if ((tp_dev.sta) & (1 << t))

{ /* 坐标在屏幕范围内 */

if (tp_dev.x[t] < lcddev.width && tp_dev.y[t] < lcddev.height)

{

if (lastpos[t][0] == 0xffff)

{

lastpos[t][0] = tp_dev.x[t];

lastpos[t][1] = tp_dev.y[t];

}

lcd_draw_bline(lastpos[t][0], lastpos[t][1], tp_dev.x[t],

tp_dev.y[t], 2, point_color_tbl[t]); /* 画线 */

lastpos[t][0] = tp_dev.x[t];

lastpos[t][1] = tp_dev.y[t];

if (tp_dev.x[t] > (lcddev.width - 24) && tp_dev.y[t] < 20)

{

load_draw_dialog();/* 清除 */

}

}

}

else

{

lastpos[t][0] = 0xffff;

}

}

delay_ms(5);

i ;

if (i % 20 == 0)led0_toggle();

}

}

int main(void)

{

hal_init(); /* 初始化hal库 */

sys_stm32_clock_init(rcc_pll_mul9); /* 设置时钟, 72mhz */

delay_init(72); /* 延时初始化 */

usart_init(115200); /* 串口初始化为115200 */

led_init(); /* 初始化led */

lcd_init(); /* 初始化lcd */

key_init(); /* 初始化按键 */

tp_dev.init(); /* 触摸屏初始化 */

lcd_show_string(30, 50, 200, 16, 16, "stm32f103", red);

lcd_show_string(30, 70, 200, 16, 16, "touch test", red);

lcd_show_string(30, 90, 200, 16, 16, "atom@alientek", red);

if (tp_dev.touchtype != 0xff)

{ /* 电阻屏才显示 */

lcd_show_string(30, 110, 200, 16, 16, "press key0 to adjust", red);

}

delay_ms(1500);

load_draw_dialog();

if (tp_dev.touchtype & 0x80)

{

ctp_test(); /* 电容屏测试 */

}

else

{

rtp_test(); /* 电阻屏测试 */

}

}

上面没有把main.c全部代码列出来,只是列出重要函数,这里简单介绍一下这三个函数。 rtp_test,该函数用于电阻触摸屏的测试,该函数代码比较简单,就是扫描按键和触摸屏,如果触摸屏有按下,则在触摸屏上面划线,如果按中“rst”区域,则执行清屏。如果按键key0按下,则执行触摸屏校准。 ctp_test,该函数用于电容触摸屏的测试,由于我们采用tp_dev.sta来标记当前按下的触摸屏点数,所以判断是否有电容触摸屏按下,也就是判断tp_dev.sta的最低5位,如果有数据,则画线,如果没数据则忽略,且5个点画线的颜色各不一样,方便区分。另外,电容触摸屏不需要校准,所以没有校准程序。 main函数,则比较简单,初始化相关外设,然后根据触摸屏类型,去选择执行ctp_test还是rtp_test。 软件部分就介绍到这里,接下来看看下载验证。 39.4 下载验证 在代码编译成功之后,我们通过下载代码到开发板上,电阻触摸屏测试如图39.4.1所示界面:

图39.4.1 电阻触摸屏测试程序运行效果 图中我们在电阻屏上画了一些内容,右上角的rst可以用来清屏,点击该区域,即可清屏重画。另外,按key0可以进入校准模式,如果发现触摸屏不准,则可以按key0,进入校准,重新校准一下,即可正常使用。 如果是电容触摸屏,测试界面如图39.4.2所示:

图39.4.2 电容触摸屏测试界面 图中,同样输入了一些内容。电容屏支持多点触摸,每个点的颜色都不一样,图中的波浪线就是三点触摸画出来的,最多可以5点触摸。按右上角的rst标志,可以清屏。电容屏无需校准,所以按key0无效。key0校准仅对电阻屏有效。

rfid标签的知识(2)–标签分类

文章目录

rfid标签的知识(2)--标签分类

前言一、根据频率分类二、根据工艺分类1. 不干胶标签2. 卡片式的标签3. 特殊标签3.1 注塑标签3.2 织唛标签:3.3 硅胶标签:3.4 pcb标签:3.5 陶瓷标签:3.6 其他标签:

三、根据供电分类四、根据材料分类五、根据应用领域分类总结

前言

每种标签都有一个属于自己的tag,或从工艺上,或从结构上,或从材料上,或从应用领域上。例如:下图 这款标签, 从频率上来看,属于超高频标签; 从工艺上来看,属于注塑标签; 从供电上来看,属于无源标签; 从材料上来看,属于abs内嵌pcb标签; 从应用领域上来看,属于固资标签等。

所以,每个标签都不“单纯”。

下面我们就根据不同的类别来看看,都有哪些标签种类。

提示:以下是本篇文章正文内容。

一、根据频率分类

上篇文章讲到过,就简单过一下。

二、根据工艺分类

不同的标签,生产的每个流程的工艺上都有不同的分类。我也没办法全部都了解清楚,只能就我了解的一些常见种类标签的部分工艺进行简单分类。

1. 不干胶标签

不干胶标签的最大区别主要在天线的制作工艺上有不同,根据需要可能会在此基础上增加天线的局部或者整体易碎的工艺,或者直接在标签上用花刀模切,以达到防转移的目的。

不干胶标签最为常见的是铝蚀刻的标签,技术成熟,成本较低。不干胶标签根据基材不同也会有很多细分,这里先不讲。

2. 卡片式的标签

卡片式的标签,一般是将铝蚀刻的inlay(hf和uhf较多),或者绕制好的线圈天线(lf和hf较多)放进制作卡片的材料之间,通过胶合或者层压固定,或者直接在一侧材料上绕制线圈再进行层压。然后对整体材料进行冲切,做出不同的形状和尺寸。

3. 特殊标签

3.1 注塑标签

注塑标签多为特种标签,常用abs1等工程塑料无外壳将内部标签包装起来,增加标签的耐高低温,耐腐蚀,防水,抗金属,延长使用寿命等方面的使用需求,便于应用更多复杂环境下。

3.2 织唛标签:

织唛标签有很多种,但都具备表面材料是织唛的特性,常用于衣物、床上用品等的盘点和溯源等。

内部有两种方式:一种两层织唛间内置inlay2式的天线,通过胶合或者缝合边缘的方式制作成品;一种是将金属丝缝在织唛上作为天线臂,一侧用热熔胶粘合另一层织唛,如下图。

前者更美观,便于印刷表面。后者更牢固,可以重复洗涤,常用于酒店织物的管理。

3.3 硅胶标签:

如其名,表面材料是硅胶的标签。标签内部多为fpc3(我理解为做了芯片保护的铜蚀刻inlay),pcb4等形式天线,具体优势是表面更舒适,亲肤。常用作于洗衣标签,腕带使用。

3.4 pcb标签:

介质材料为用于制作pcb的板材,上面蚀刻的是rfid的天线。常用于超高频抗金属标签,高频标签。因其不抗老化的缺点,不便单独用于室外环境。常常需要在表面喷漆或者在外部加外壳的方式延长使用寿命。

3.5 陶瓷标签:

介质材料为陶瓷的标签,表层天线材料常为银浆。陶瓷易碎,所以陶瓷标签常用于车辆挡风玻璃。

因其介电损耗小等特点常用于超高频抗金属方向,相同尺寸制作的标签比pcb的碱金属标签读距略远(基于相同测试环境,测试设备,相同功率和芯片灵敏度等前提)。

3.6 其他标签:

前面没有提到的其他种类的标签还有很多,例如轮胎用的橡胶/弹簧标签,动物注射的玻璃管标签等等。这些都应用于专门的领域,比较小众,不一 一展开。

三、根据供电分类

标签根据是否有额外电池供电,可简单分为无源标签,半有源或者半无源标签,有源标签三类。

无源标签:仅靠读写器发送到标签的能量激励标签工作,无外接电源,即为无源标签。 有源标签:标签工作的能量由电池提供,主动向读写器发送信号,无需读写器供能。 半有源/半无源标签:标签也连接有电池,但电池是为了给标签内部电路供电,标签工作与无源标签相差无几。

从成本上来看:有源>半有源/半无源>无源

从读距上来看:有源>半有源/半无源>无源

从使用寿命上来看:无源>半有源/半无源>有源

有源标签和无源标签使用的频率也有所不同。频率在433mhz、2.4ghz、5.8ghz时使用有源,860-960mhz使用无源。半有源在125khz低频下激活,在2.45gh频率识别和上传数据。

四、根据材料分类

根据不同的标签的类型的制作工艺的不同,使用环境不同,会使用不同的材料,这里可根据第二节里的工艺分类后段来学习,这里不再赘述。

通常一种标签都不会只有一种材料,是由几种材料结合制成。

五、根据应用领域分类

因为不同行业有着不同的行业需求和标准,所以标签的读距、标签制作工艺、标签使用寿命等因素也就决定着它们能够应用于什么领域。

仅现阶段,rfid标签的应用领域就已经非常广泛。 rfid标签的功能性和便利性渐渐为人所知,越来越多的行业引入了rfid技术。

随着这些新行业需求的提出,使得rfid标签的研发者们不得不尝试新工艺,新结构,新材料,新芯片…所以这也推进了rfid技术的更快的发展。

总结

rfid标签可以从很多方向分类,如频率、工艺、材料、供电、应用领域等,每种分类都可以将标签分为很多小分支,这些小分支他们各自独立又互相联系。

再简单的标签都可以从他们的分类上学到很多东西。

以上是鄙人对标签分类的浅薄总结,欢迎交流探讨。

图片来自网络,如有侵权,联系删除。

abs :丙烯腈(a)、丁二烯(b)、苯乙烯(s)三种单体的三元共聚物,三种单体相对含量可任意变化,制成各种树脂。 (来自百度)工程塑料的一种。 ?? inlay:是智能卡行业的专用术语,是指一种由多层pet底材含有芯片及线圈层合在一起的产品。即在印刷有天线的pet上通过导电胶水,热压好芯片之后的产品。称之为inlay。(pet:也是工程塑料的一种) ?? fpc:柔性电路板,是以聚酰亚胺或聚酯薄膜为基材制成的一种具有高度可靠性,绝佳的可挠性印刷电路板。具有配线密度高、重量轻、厚度薄、弯折性好的特点。(来自百度) ?? pcb:印制电路板,又称印刷线路板,是重要的电子部件,是电子元器件的支撑体,是电子元器件电气相互连接的载体。(来自百度) 因制作pcb的工艺中,需要将需要蚀刻的电路印刷上去,蚀刻掉多余的金属,前面提到的铝蚀刻和铜蚀刻的inlay也有同样的步骤,明明是蚀刻的天线,有时候也称为印刷天线。银浆印刷标签除外。 ??

fpc线路板也叫柔性线路板,通称“柔性线路板,行业内别名fpc,要用软性的绝缘层基材(目的是为了聚丙烯腈或聚脂薄膜)做成的印刷线路板,具备很多普通印刷线路板不具备的优势,比如它能够随意弯折、缠绕、伸缩,运用fpc可大大的变小电子设备的容积,可用电子设备向密度高的、微型化、高可靠方位发展趋势的必须,

因而,fpc在航空航天、国防、移动通信、笔记本电脑、电子计算机外接设备、pda、数字相机等行业或设备上得到了普遍的使用。

fpc电路板生产的工艺流程繁琐,从切料打孔到封装交货,正中间所必须的步骤有20多道,在此悠长的生产过程中,依据客户满意度,将使用多种多样辅助材料。

fpc的基材一般为双层或单层铜泊,这也是全部fpc的基本,fpc的电气设备特性都由它确定。别的辅助材料仅仅用于协助组装与融入使用环境。

关键有下边几类:1、fr4-材质较硬,有0.15-2.0mm的不一样薄厚,关键用在fpc电焊焊接处的背面,做为提升,便捷电焊焊接平稳靠谱;fr-4是一种阻燃性材料级别的编号,所代表什么意思是环氧树脂材料通过点燃情况一定可以自动熄火的一种材料规格型号,它并不是一种材料名字,反而是一种材料级别,因而现阶段一般线路板常用的fr-4级别材料就会有十分多的是类型,可是大部分全是以所说的四作用(tera-function)的环氧树脂胶再加上填充料(filler)及其玻纤所作出的复合型材料。

2、pi胶布-材质过软,可弯折,关键用在火红金手指地区的加厚型提升,有利于插下;pi胶布,全称聚酰亚胺胶带。pi胶布要以聚酰亚胺薄膜为基材,选用进口的有机硅材料压敏胶黏剂,具备耐高低温试验、耐腐蚀、耐水洗、电气绝缘(h级)、防电磁辐射等特性。适用于电子线路板波峰焊机锡遮掩、维护火红金手指和高端电器绝缘、电机绝缘层,及其锂电池正负极耳固定不动。

3、钢片-材质硬,作用与fr4一样,用于电焊焊接处加固,比fr4美观大方,可接地装置,强度较fr4高;钢片,材料为进口不锈钢板经热处理工艺精抛生产加工做成,具备精度高、拉幅度强、光滑度好、有延展性、不容易断裂的特性。

4、tpx阻胶纸-一款性能卓越耐热的环氧树脂抵抗pet保护膜,用于pcb线路板压合工艺流程,经专业的结构设计,可用于抵抗环氧树脂外溢后埋孔和盲埋孔的数次压层工艺流程上,有着优良的阻胶、塞孔实际效果。

5、eim电磁感应膜-贴于fpc表层,用于屏蔽信号影响;eim电磁感应膜是一种根据真空泵磁控溅射的方式,能够在不一样衬底的(pet/pc/夹层玻璃等)基材上镀屏蔽掉材料,以极低的电阻器完成emi干扰信号屏蔽掉。

6、导电胶-用于钢片与fpc的联接压合,导电性,可完成钢片接地装置作用;导电胶是一种干固或烘干后具备一定导电性能的胶黏剂。主要是由环氧树脂基材、导电性颗粒和分散化添加物、改性剂等构成。它可以将多种多样导电性材料联接在一起,使被联接材料间产生电的通道。在电子工业中,导电胶已变成一种必不可少的新材料。

7、3m胶带纸-关键作为于0.4mm及之上薄厚的fr4与fpc黏贴,及其fpc与顾客产品组装固定不动;fpc辅助材料的应用,最后要按照顾客的使用环境与功能性规定来确定。做好板,找深亚

分享
文章爱游戏平台的版权声明:除非注明,否则均为苗坤旺离型膜原创文章,转载或复制请以超链接形式并注明出处。

发表评论

快捷回复: 表情:
applausebadlaughcoffeefabulousfacepalmfecesfrownheyhainsidiouskeepfightingnoprobpigheadshockedslapsocialsweattolaughwatermelonwittywowyeahyellowdog
评论列表 (暂无评论,2人围观)

还没有评论,来说两句吧...

微信二维码
微信二维码
支付宝二维码
网站地图