网站首页 > 资源文章 正文
1)实验平台:正点原子开拓者FPGA 开发板
2)摘自《开拓者 Nios II开发指南》关注官方微信号公众号,获取更多资料:正点原子
3)全套实验源码+手册+视频下载地址:http://www.openedv.com/docs/index.html
第十二章MCU TFT-LCD画板实验(触摸驱动)
现在几乎所有智能手机,包括平板电脑都是采用电容屏作为触摸屏,电容屏是利用人体感
应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标。
在本章中,我们将向大家介绍 Nios II 控制 ALIENTKE LCD 电容触摸模块,实现触摸屏驱动,最
终实现一个手写板的功能。
本章包括以下几个部分:
12.1 简介
12.2 实验任务
12.3 硬件设计
12.4 软件设计
12.5 下载验证
简介
目前最常用的触摸屏有两种:电阻式触摸屏与电容式触摸屏。下面,我们来分别介绍。
1) 电阻式触摸屏
在 Iphone 面世之前,几乎清一色的都是使用电阻式触摸屏,电阻式触摸屏利用压力感应
进行触点检测控制,需要直接应力接触,通过检测电阻来定位触摸位置。
ALIENTEK 2.4/2.8/3.5 寸 LCD 模块自带的触摸屏都属于电阻式触摸屏,下面简单介绍下电阻
式触摸屏的原理。
电阻触摸屏的主要部分是一块与显示器表面非常配合的电阻薄膜屏,这是一种多层的复合
薄膜,它以一层玻璃或硬塑料平板作为基层,表面涂有一层透明氧化金属(透明的导电电阻)
导电层,上面再盖有一层外表面硬化处理、光滑防擦的塑料层、它的内表面也涂有一层涂层、
在它们之间有许多细小的(小于 1/1000 英寸)的透明隔离点把两层导电层隔开绝缘。当手指
触摸屏幕时,两层导电层在触摸点位置就有了接触,电阻发生变化,在 X 和 Y 两个方向上产生
信号,然后送触摸屏控制器。控制器侦测到这一接触并计算出(X,Y)的位置,再根据获得的
位置模拟鼠标的方式运作。这就是电阻技术触摸屏的最基本的原理。
电阻触摸屏的优点:精度高、价格便宜、抗干扰能力强、稳定性好。
电阻触摸屏的缺点:容易被划伤、透光性不太好、不支持多点触摸。
从以上介绍可知,触摸屏都需要一个 AD 转换器,一般来说是需要一个控制器的。ALIENTEK
LCD 模块选择的是四线电阻式触摸屏,这种触摸屏的控制芯片有很多,包括:ADS7843、ADS7846、
TSC2046、XPT2046 和 AK4182 等。这几款芯片的驱动基本上是一样的,也就是你只要写出了
ADS7843 的驱动,这个驱动对其他几个芯片也是有效的。而且封装也有一样的,完全 PIN TO PIN
兼容。所以在替换起来,很方便。
ALIENTEK LCD 模块自带的触摸屏控制芯片为 XPT2046。XPT2046 是一款 4 导线制触摸屏控
制器,内含 12 位分辨率 125KHz 转换速率逐步逼近型 A/D 转换器。XPT2046 支持从 1.5V 到
5.25V 的低电压 I/O 接口。XPT2046 能通过执行两次 A/D 转换查出被按的屏幕位置,除此之外,
还可以测量加在触摸屏上的压力。内部自带 2.5V 参考电压可以作为辅助输入、温度测量和电
池监测模式之用,电池监测的电压范围可以从 0V 到 6V。XPT2046 片内集成有一个温度传感器。
在 2.7V 的典型工作状态下,关闭参考电压,功耗可小于 0.75mW。XPT2046 采用微小的封装形
式:TSSOP-16,QFN-16(0.75mm 厚度)和 VFBGA-48。工作温度范围为-40℃~+85℃。
该芯片完全是兼容 ADS7843 和 ADS7846 的,关于这个芯片的详细使用,可以参考这两个芯片的 datasheet。
电阻式触摸屏就介绍到这里。
2) 电容式触摸屏
现在几乎所有智能手机,包括平板电脑都是采用电容屏作为触摸屏,电容屏是利用人体感
应进行触点检测控制,不需要直接接触或只需要轻微接触,通过检测感应电流来定位触摸坐标。
ALIENTEK 4.3/7 寸 LCD 模块自带的触摸屏采用的是电容式触摸屏,下面简单介绍下电容式
触摸屏的原理。
电容式触摸屏主要分为两种:
1、表面电容式电容触摸屏。
表面电容式触摸屏技术是利用 ITO(铟锡氧化物,一种透明的导电材料)导电膜,通过电场
感应方式感测屏幕表面的触摸行为进行。但是表面电容式触摸屏有一些局限性,它只能识别一
个手指或者一次触摸。
投射式电容触摸屏
投射电容式触摸屏是传感器利用触摸屏电极发射出静电场线。一般用于投射电容传感技术
的电容类型有两种:自我电容和交互电容。
自我电容又称绝对电容,是最广为采用的一种方法,自我电容通常是指扫描电极与地构成
的电容。在玻璃表面有用 ITO 制成的横向与纵向的扫描电极,这些电极和地之间就构成一个电
容的两极。当用手或触摸笔触摸的时候就会并联一个电容到电路中去,从而使在该条扫描线上
的总体的电容量有所改变。在扫描的时候,控制 IC 依次扫描纵向和横向电极,并根据扫描前
后的电容变化来确定触摸点坐标位置。笔记本电脑触摸输入板就是采用的这种方式,笔记本电
脑的输入板采用 X*Y 的传感电极阵列形成一个传感格子,当手指靠近触摸输入板时,在手指和
传感电极之间产生一个小量电荷。采用特定的运算法则处理来自行、列传感器的信号来确定手
指的位置。
交互电容又叫做跨越电容,它是在玻璃表面的横向和纵向的 ITO 电极的交叉处形成电容。
交互电容的扫描方式就是扫描每个交叉处的电容变化,来判定触摸点的位置。当触摸的时候就
会影响到相邻电极的耦合,从而改变交叉处的电容量,交互电容的扫面方法可以侦测到每个交
叉点的电容值和触摸后电容变化,因而它需要的扫描时间与自我电容的扫描方式相比要长一些,
需要扫描检测 X*Y 根电极。目前智能手机/平板电脑等的触摸屏,都是采用交互电容技术。
ALIENTEK 所选择的电容触摸屏,也是采用的是投射式电容屏(交互电容类型),所以后面
仅以投射式电容屏作为介绍。
透射式电容触摸屏采用纵横两列电极组成感应矩阵,来感应触摸。以两个交叉的电极矩阵,
即:X 轴电极和 Y 轴电极,来检测每一格感应单元的电容变化,如下图所示:
图 12.1.1 投射式电容屏电极矩阵示意图
示意图中的电极,实际是透明的,这里是为了方便大家理解。图中,X、Y 轴的透明电极电
容屏的精度、分辨率与 X、Y 轴的通道数有关,通道数越多,精度越高。以上就是电容触摸屏
的基本原理,接下来看看电容触摸屏的优缺点:
电容触摸屏的优点:手感好、无需校准、支持多点触摸、透光性好。
电容触摸屏的缺点:成本高、精度不高、抗干扰能力差。
这里特别提醒大家电容触摸屏对工作环境的要求是比较高的,在潮湿、多尘、高低温环境
下面,都是不适合使用电容屏的。
电容触摸屏一般都需要一个驱动 IC 来检测电容触摸,且一般是通过 IIC 接口输出触摸数据
的。ALIENTEK 7’LCD 模块的电容触摸屏,使用 FT5206/FT5426 做为驱动 IC,采用的是 15*28 的
驱动结构(15 个感应通道,28 个驱动通道)。ALIENTEK 4.3’LCD 模块则使用 GT9147 作为驱
动 IC,采用 17*10 的驱动结构(10 个感应通道,17 个驱动通道)。
这两个模块都只支持最多 5 点触摸,本例程除 CPLD 方案的 V1 版本 7 寸屏模块不支持外,
其他所有 ALIENTEK 的 LCD 模块都支持,电容触摸驱动 IC,这里只介绍 GT9147 的驱动,FT5206
和 FT5426 的驱动同 GT9147 类似,大家可以参考着学习即可。
下面我们简单介绍下 GT9147,该芯片是深圳汇顶科技研发的一颗电容触摸屏驱动 IC,支
持 100Hz 触点扫描频率,支持 5 点触摸,支持 18*10 个检测通道,适合小于 4.5 寸的电容触摸
屏使用。
GT9147 与 FPGA 连接是通过 4 根线:SDA、SCL、RST 和 INT。其中:SDA 和 SCL 是 IIC 通信
用的,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 的型号,从而判断是 GT9147 还是 FT5206,以便执行不同的初始化。
4,状态寄存器(0X814E)
该寄存器各位描述如下表所示:
图 12.1.2 寄存器定义
这里,我们仅关心最高位和最低 4 位,最高位用于表示 buffer 状态,如果有数据(坐标/
按键),buffer 就会是 1,最低 4 位用于表示有效触点的个数,范围是:0~5,0 表示没有触摸,
5 表示有 5 点触摸。最后,该寄存器在每次读取后,如果 bit7 有效,则必须写 0,清除这个位,
否则不会输出下一次数据!!这个要特别注意!!!
5,坐标数据寄存器(共 30 个)
这里共分成 5 组(5 个点),每组 6 个寄存器存储数据,以触点 1 的坐标数据寄存器组为
例,如下表所示:
图 12.1.3 触点 1 坐标寄存器组描述
我们一般只用到触点的 x,y 坐标,所以只需要读取 0X8150~0X8153 的数据,组合即可得
到触点坐标。其他 4 组分别是:0X8158、0X8160、0X8168 和 0X8170 等开头的 16 个寄存器组
成,分别针对触点 2~4 的坐标。GT9147 支持寄存器地址自增,我们只需要发送寄存器组的首
地址,然后连续读取即可,GT9147 会自动地址自增,从而提高读取速度。
GT9147 相关寄存器的介绍就介绍到这里,更详细的资料,请参考:GT9147 编程指南.pdf
这个文档。
GT9147 只需要经过简单的初始化就可以正常使用了,初始化流程:硬复位→延时 10ms→
结束硬复位→设置 IIC 地址→延时 100ms→软复位→更新配置(需要时)→结束软复位。此时
GT9147 即可正常使用了。
然后,我们不停的查询 0X814E 寄存器,判断是否有有效触点,如果有,则读取坐标数据
寄存器,得到触点坐标,特别注意,如果 0X814E 读到的值最高位为 1,就必须对该位写 0,否
则无法读到下一次坐标数据。
特别说明:FT5206 和 FT5426 的驱动代码完全一模一样,他们只是版本号读取的时候稍有
差异,读坐标数据和配置等操作动完全是一模一样的。所以,这两个电容屏驱动 IC,可以共用
一个.c 文件(ft5206.c)。电容式触摸屏部分,就介绍到这里。
实验任务
本章我们使用 Nios II 结合 MCU TFT-LCD 驱动,实现 LCD 画板功能。
硬件设计
硬件框架
本章实验的硬件框架是在 MCU TFT-LCD 显示实验框架的基础上添加触摸屏驱动所需的 4
个 PIO 信号,如下图所示:
图12.3.1 TFTLCD显示实验的硬件框架图
顶层代码如下:
1 module qsys_touch(
2 //module clock
3 input sys_clk , //系统时钟,50Mhz
4 input sys_rst_n , //系统复位,低电平有效
5
6 //SDRAM interface
7 output sdram_clk , //SDRAM 芯片时钟
8 output sdram_cke , //SDRAM 时钟有效
9 output sdram_cs_n , //SDRAM 片选
10 output sdram_ras_n, //SDRAM 行有效
11 output sdram_cas_n, //SDRAM 列有效
12 output sdram_we_n , //SDRAM 写有效
13 output [ 1:0] sdram_ba , //SDRAM Bank 地址
14 output [12:0] sdram_addr , //SDRAM 行/列地址
15 inout [15:0] sdram_data , //SDRAM 数据
16 output [ 1:0] sdram_dqm , //SDRAM 数据掩码
17
18 //EPCS FLASH interface
19 output epcs_dclk , // EPCS 时钟信号
20 output epcs_sce , // EPCS 片选信号
21 output epcs_sdo , // EPCS 数据输出信号
22 input epcs_data0 , // EPCS 数据输入信号
23
24 //MCU LCD interface
25 inout [15:0] mlcd_data , // LCD 数据信号
26 output mlcd_bl , // LCD 背光信号
27 output mlcd_cs_n , // LCD 片选信号
28 output mlcd_wr_n , // LCD 写信号
29 output mlcd_rd_n , // LCD 读信号
30 output mlcd_rs , // LCD 命令/数据信号
31 output mlcd_rst_n , // LCD 复位信号
32
33 //TOUCH interface
34 output touch_tcs , // 触摸屏复位信号
35 inout touch_int , // 触摸屏中断信号
36 output touch_scl , // 触摸屏 IIC_SCL 信号
37 inout touch_sda // 触摸屏 IIC_SDA 信号
38 );
39
40 //wire define
41 wire clk_100m; //SDRAM 控制器时钟
42 wire rst_n ; //系统复位信号
43 wire locked ; //PLL 输出稳定标志
44
45 //*****************************************************
46 //** main code
47 //*****************************************************
48
49 //待 PLL 输出稳定之后,停止系统复位
50 assign rst_n = sys_rst_n & locked;
51
52 //例化 PLL
53 pll_clk u_pll_clk(
54 .areset (~sys_rst_n),
55 .inclk0 (sys_clk ),
56 .c0 (clk_100m ),
57 .c1 (sdram_clk ),
58 .locked (locked )
59 );
60
61 //例化 Nios2 系统模块
62 nios2os u_nios2os (
63 .clk_clk (clk_100m ), // 时钟 100M
64 .reset_reset_n (rst_n ), // 复位信号
65 .sdram_addr (sdram_addr ), // SDRAM 行/列地址
66 .sdram_ba (sdram_ba ), // SDRAM Bank 地址
67 .sdram_cas_n (sdram_cas_n), // SDRAM 列有效
68 .sdram_cke (sdram_cke ), // SDRAM 时钟有效
69 .sdram_cs_n (sdram_cs_n ), // SDRAM 片选
70 .sdram_dq (sdram_data ), // SDRAM 数据
71 .sdram_dqm (sdram_dqm ), // SDRAM 数据掩码
72 .sdram_ras_n (sdram_ras_n), // SDRAM 行有效
73 .sdram_we_n (sdram_we_n ), // SDRAM 写有效
74 .epcs_dclk (epcs_dclk ), // EPCS 时钟信号
75 .epcs_sce (epcs_sce ), // EPCS 片选信号
76 .epcs_sdo (epcs_sdo ), // EPCS 数据输出信号
77 .epcs_data0 (epcs_data0 ), // EPCS 数据输入信号
78 .mlcd_data_export (mlcd_data ), // LCD 数据信号
79 .mlcd_cs_n_export (mlcd_cs_n ), // LCD 片选信号
80 .mlcd_wr_n_export (mlcd_wr_n ), // LCD 写信号
81 .mlcd_rd_n_export (mlcd_rd_n ), // LCD 读信号
82 .mlcd_rst_n_export(mlcd_rst_n ), // LCD 复位信号
83 .mlcd_rs_export (mlcd_rs ), // LCD 命令/数据信号
84 .mlcd_bl_export (mlcd_bl ), // LCD 背光信号
85 .touch_tcs_export (touch_tcs ), // 触摸屏复位信号
86 .touch_int_export (touch_int ), // 触摸屏中断信号
87 .touch_scl_export (touch_scl ), // 触摸屏 IIC_SCL 信号
88 .touch_sda_export (touch_sda ) // 触摸屏 IIC_SDA 信号
89 );
90
91 endmodule
顶层代码主要实现 PLL 和 Nios II 系统模块的例化。
接下来进行引脚分配,需要在 TFT LCD 显示实验的基础上分配触摸的引脚,如下:
图 12.3.2 引脚分配
软件设计
创建好软件工程后,我们将 hello_world.c 更改为 qsys_touch.c,并在 qsys_touch 的应用工
程下新建两个文件夹,分别命名为 drives 和 APP,然后在 drives 文件夹里面新建两个文件夹,
分别命名为 LCD 和 TOUCH,LCD 文件夹存放 TFTLCD 的驱动代码文件和相应的字体文件,TOUCH
文件夹存放触摸驱动相关的文件,APP 文件夹下存放宏定义文件和延时函数文件。工程目录结
构如下图所示:
图 12.4.1 工程目录结构
LCD 文件夹的内容在 MCU TFT-LCD 章节已具体介绍,主要用于驱动 TFT LCD 的显示,TOUCH
文件夹下的文件用于驱动正点原子不同型号的采用 IIC 协议的电容触摸屏,myiic 文件是用 C
模拟的 IIC 协议。我们先来看一下 APP 文件夹下的文件内容。
defines.h 文件是将我们常用的宏定义做一个归类,方便使用,内容如下:
1 #ifndef DEFINES_H_
2 #define DEFINES_H_
3
4 //重新定义类型
5 #define u8 unsigned char
6 #define u16 unsigned short
7 #define u32 unsigned long
8 #define u64 unsigned long long
9
10 #endif /* DEFINES_H_ */
delay.c 文件内容如下:
1 #include "delay.h"
2 #include <unistd.h>
3
4 //延时函数,单位毫秒
5 void delay_ms(u32 n)
6 {
7 usleep(n*1000);
8 }
9
10 //延时函数,单位微秒
11 void delay_us(u32 n)
12 {
13 usleep(n);
14 }
现在我们来看一下使用 C 来模拟 IIC 协议的 myiic.h 和 myiic.c 文件,myiic.h 内容如下:
1 #ifndef __MYIIC_H
2 #define __MYIIC_H
3 #include "delay.h"
4 #include "system.h"
5 #include "altera_avalon_pio_regs.h"
6
7 //IO 方向设置
8 #define SDA_IN() {IOWR_ALTERA_AVALON_PIO_DIRECTION(TOUCH_SDA_BASE,0x0);} //输入模式
9 #define SDA_OUT() {IOWR_ALTERA_AVALON_PIO_DIRECTION(TOUCH_SDA_BASE,0x1);} //输出模式
10
11 //IO 操作函数
12 #define IIC_SCL(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_SCL_BASE,x) //SCL
13 #define IIC_SDA(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_SDA_BASE,x) //写入 SDA
14 #define READ_SDA IORD_ALTERA_AVALON_PIO_DATA(TOUCH_SDA_BASE) //读取 SDA
15
16 //IIC 所有操作函数
17 void IIC_Init(void); //初始化 IIC 的 IO 口
18 void IIC_Start(void); //发送 IIC 开始信号
19 void IIC_Stop(void); //发送 IIC 停止信号
20 void IIC_Send_Byte(u8 txd); //IIC 发送一个字节
21 u8 IIC_Read_Byte(unsigned char ack); //IIC 读取一个字节
22 u8 IIC_Wait_Ack(void); //IIC 等待 ACK 信号
23 void IIC_Ack(void); //IIC 发送 ACK 信号
24 void IIC_NAck(void); //IIC 不发送 ACK 信号
25 #endif
代码的第 8~9 行,设置了双向信号 SDA 的输入、输出模式。代码的第 12~14 行宏定义了 IO
操作函数。
myiic.c 文件的内容如下:
1 #include "myiic.h"
2 #include "delay.h"
3 #include <unistd.h>
4
5 //初始化 IIC
6 void IIC_Init(void)
7 {
8 IIC_SCL(1);
9 IIC_SDA(1);
10 }
11
12 //产生 IIC 起始信号
13 void IIC_Start(void)
14 {
15 SDA_OUT(); //sda 线输出
16 IIC_SDA(1);
17 IIC_SCL(1);
18 usleep(2);
19 IIC_SDA(0); //START 信号:SCL 为高时,SDA 由高变低
20 usleep(2);
21 IIC_SCL(0); //钳住 I2C 总线,准备发送或接收数据
22 }
23
24 //产生 IIC 停止信号
25 void IIC_Stop(void)
26 {
27 SDA_OUT(); //sda 线输出
28 IIC_SCL(0);
29 IIC_SDA(0); //STOP 信号:SCL 为高时,SDA 由低变高
30 usleep(2);
31 IIC_SCL(1);
32 usleep(2);
33 IIC_SDA(1); //发送 I2C 总线结束信号
34 usleep(2);
35 }
36
37 //等待应答信号到来
38 //返回值:1,接收应答失败
39 // 0,接收应答成功
40 u8 IIC_Wait_Ack(void)
41 {
42 u8 ucErrTime=0;
43 SDA_IN(); //SDA 设置为输入
44 IIC_SDA(1);
45 usleep(2);
46 IIC_SCL(1);
47 usleep(2);
48 while(READ_SDA) {
49 ucErrTime++;
50 if(ucErrTime>250) {
51 IIC_Stop();
52 return 1;
53 }
54 }
55 IIC_SCL(0);//时钟输出 0
56 return 0;
57 }
58
59 //产生 ACK 应答
60 void IIC_Ack(void)
61 {
62 IIC_SCL(0);
63 SDA_OUT();
64 IIC_SDA(0);
65 usleep(2);
66 IIC_SCL(1);
67 usleep(2);
68 IIC_SCL(0);
69 }
70
71 //不产生 ACK 应答
72 void IIC_NAck(void)
73 {
74 IIC_SCL(0);
75 SDA_OUT();
76 IIC_SDA(1);
77 usleep(1);
78 IIC_SCL(1);
79 usleep(1);
80 IIC_SCL(0);
81 }
82
83 //IIC 发送一个字节
84 //返回从机有无应答
85 //1,有应答
86 //0,无应答
87 void IIC_Send_Byte(u8 txd)
88 {
89 u8 t;
90 SDA_OUT();
91 IIC_SCL(0);//拉低时钟开始数据传输
92 for(t=0; t<8; t++) {
93 IIC_SDA((txd&0x80)>>7);
94 txd<<=1;
95 usleep(1);
96 IIC_SCL(1);
97 usleep(1);
98 IIC_SCL(0);
99 usleep(1);
100 }
101 }
102
103 //读 1 个字节,ack=1 时,发送 ACK,ack=0,发送 nACK
104 u8 IIC_Read_Byte(unsigned char ack)
105 {
106 unsigned char i,receive=0;
107 SDA_IN();//SDA 设置为输入
108 for(i=0; i<8; i++ ) {
109 IIC_SCL(0);
110 usleep(1);
111 IIC_SCL(1);
112 receive<<=1;
113 if(READ_SDA)
114 receive++;
115 usleep(1);
116 }
117 if (!ack)
118 IIC_NAck();//发送 nACK
119 else
120 IIC_Ack(); //发送 ACK
121 return receive;
122 }
该部分为 IIC 驱动代码,实现包括 IIC 的初始化(IO 口)、IIC 开始、IIC 结束、ACK、IIC 读
写等功能,在其他函数里面,只需要调用相关的 IIC 函数就可以和外部 IIC 器件通信了,这里并
不局限于 TOUCH 的驱动,该段代码可以用在任何 IIC 设备上。对于 IIC 协议的具体介绍可以参
见《开拓者 FPGA 开发指南》的第二十八章 EEPROM 读写测试实验。
保存该部分代码,把 myiic.c 加入到 drives\TOUCH 文件夹下面,然后在 myiic.h 里面输入如
下代码:
1 #ifndef __MYIIC_H
2 #define __MYIIC_H
3 #include "delay.h"
4 #include "system.h"
5 #include "altera_avalon_pio_regs.h"
6
7 //IO 方向设置
8 #define SDA_IN() {IOWR_ALTERA_AVALON_PIO_DIRECTION(TOUCH_SDA_BASE,0x0);} //输入模式
9 #define SDA_OUT() {IOWR_ALTERA_AVALON_PIO_DIRECTION(TOUCH_SDA_BASE,0x1);} //输出模式
10
11 //IO 操作函数
12 #define IIC_SCL(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_SCL_BASE,x) //SCL
13 #define IIC_SDA(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_SDA_BASE,x) //写入 SDA
14 #define READ_SDA IORD_ALTERA_AVALON_PIO_DATA(TOUCH_SDA_BASE) //读取 SDA
15
16 //IIC 所有操作函数
17 void IIC_Init(void); //初始化 IIC 的 IO 口
18 void IIC_Start(void); //发送 IIC 开始信号
19 void IIC_Stop(void); //发送 IIC 停止信号
20 void IIC_Send_Byte(u8 txd); //IIC 发送一个字节
21 u8 IIC_Read_Byte(unsigned char ack); //IIC 读取一个字节
22 u8 IIC_Wait_Ack(void); //IIC 等待 ACK 信号
23 void IIC_Ack(void); //IIC 发送 ACK 信号
24 void IIC_NAck(void); //IIC 不发送 ACK 信号
25 #endif
接下来我们看一下电容触摸屏的驱动代码。
首先,我们看看 touch.h 里面的代码,
1 #ifndef __TOUCH_H__
2 #define __TOUCH_H__
3
4 #include "system.h"
5 #include "gt9147.h"
6 #include "gt9271.h"
7 #include "ft5206.h"
8 #include "mculcd.h"
9
10 #define TP_PRES_DOWN 0x8000 //触屏被按下
11 #define TP_CATH_PRES 0x4000 //有按键按下了
12 #define CT_MAX_TOUCH 10 //电容屏支持的点数,固定为10点
13
14 #define PEN IORD_ALTERA_AVALON_PIO_DATA(TOUCH_INT_BASE) //T_PEN
15
16 //触摸屏控制器
17 typedef struct
18 {
19 u8 (*init)(void); //初始化触摸屏控制器
20 u8 (*scan)(u8); //扫描触摸屏.0,屏幕扫描;1,物理坐标;
21 u16 x[CT_MAX_TOUCH]; //当前坐标
22 u16 y[CT_MAX_TOUCH];
23 //电容屏最多有10组坐标,电阻屏则用x[0],y[0]代表:此次扫描时,触屏的坐标,用
24 //x[9],y[9]存储第一次按下时的坐标.
25 u16 sta; //笔的状态
26 //b15:按下1/松开0;
27 //b14:0,没有按键按下;1,有按键按下.
28 //b13~b10:保留
29 //b9~b0:电容触摸屏按下的点数(0,表示未按下,1表示按下)
30 //新增的参数,当触摸屏的左右上下完全颠倒时需要用到.
31 //b0:0,竖屏(适合左右为X坐标,上下为Y坐标的TP)
32 // 1,横屏(适合左右为Y坐标,上下为X坐标的TP)
33 //b1~6:保留.
34 //b7:0,电阻屏
35 // 1,电容屏
36 u8 touchtype;
37 }_m_tp_dev;
38
39 extern _m_tp_dev tp_dev; //触屏控制器在touch.c里面定义
40
41 //电容屏 共用函数
42 u8 TP_Scan(); //扫描
43 void TP_Init(void); //初始化
44
45 #endif
上述代码,我们重点看看_m_tp_dev 结构体,改结构体用于管理和记录触摸屏相关信息。
通过结构体,在使用的时候,我们一般直接调用 tp_dev 的相关成员函数/变量,即可达到需要
的效果,这种设计简化了接口,且方便管理和维护,大家可以效仿一下。
现在,我们看一下 touch.c 里面的代码,由于代码比较多,我们就不一一介绍了,这里我
们仅介绍 TP_Init 函数,该函数根据 LCD 的 ID(即 lcddev.id)判别不同的电容屏,然后执行不
同的初始化,该函数代码如下:
46 //触摸屏初始化
47 void TP_Init(void)
48 {
49 if(lcddev.id==0X5510||lcddev.id==0X4342) { //电容触摸屏,4.3 寸屏
50 GT9147_Init();
51 tp_dev.scan=GT9147_Scan; //扫描函数指向 GT9147 触摸屏扫描
52 tp_dev.touchtype|=0X80; //电容屏
53 tp_dev.touchtype|=lcddev.dir&0X01; //横屏还是竖屏
54 }
55 //SSD1963 7 寸屏或者 7 寸 800*480/1024*600 RGB 屏
56 else if(lcddev.id==0X1963||lcddev.id==0X7084||lcddev.id==0X7016) {
57 FT5206_Init();
58 tp_dev.scan=FT5206_Scan; //扫描函数指向 GT9147 触摸屏扫描
59 tp_dev.touchtype|=0X80; //电容屏
60 tp_dev.touchtype|=lcddev.dir&0X01; //横屏还是竖屏
61 } else if(lcddev.id==0X1018) {
62 GT9271_Init();
63 tp_dev.scan=GT9271_Scan; //扫描函数指向 GT271 触摸屏扫描
64 tp_dev.touchtype|=0X80; //电容屏
65 tp_dev.touchtype|=lcddev.dir&0X01; //横屏还是竖屏
66 }
67 }
该函数比较简单,重点说一下:tp_dev.scan,这个结构体函数指针,默认是指向 TP_Scan
的,如果是电阻屏则用默认的即可,如果是电容屏,则指向新的扫描函数 GT9147_Scan 或
FT5206_Scan(根据芯片 ID 判断到底指向那个),执行电容触摸屏的扫描函数。
因为 GT9147 和 GT9271 为同一系列的触摸屏,除配置参数有差别外,其它的差别不大,
而 FT5206 的代码与 GT9147 的差别在于无需通过 int 引脚来选择不同的 i2c 器件地址,也无需
配置繁杂的寄存器,其余的和 GT9147 差别不大。所以下面我们主要针对 GT9147 的驱动进行
介绍。
首先我们看下 gt9147.h 代码,如下:
1 #ifndef __GT9147_H
2 #define __GT9147_H
3 #include "system.h"
4 #include "defines.h"
5 #include "mculcd.h"
6
7 //IO 操作函数
8 #define GT9147_RST(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_TCS_BASE,x) //GT9147 复位引脚
9 #define GT9147_INT(x) IOWR_ALTERA_AVALON_PIO_DATA(TOUCH_INT_BASE,x) //GT9147 中断引脚
10 //设置 PIO 的方向
11 #define GT9147_INT_DIR(x) IOWR_ALTERA_AVALON_PIO_DIRECTION(TOUCH_INT_BASE,x)
12
13 //I2C 读写命令
14 #define GT_CMD_WR 0X28 //写命令
15 #define GT_CMD_RD 0X29 //读命令
16
17 //GT9147 部分寄存器定义
18 #define GT_CTRL_REG 0X8040 //GT9147 控制寄存器
19 #define GT_CFGS_REG 0X8047 //GT9147 配置起始地址寄存器
20 #define GT_CHECK_REG 0X80FF //GT9147 校验和寄存器
21 #define GT_PID_REG 0X8140 //GT9147 产品 ID 寄存器
22
23 #define GT_GSTID_REG 0X814E //GT9147 当前检测到的触摸情况
24 #define GT_TP1_REG 0X8150 //第一个触摸点数据地址
25 #define GT_TP2_REG 0X8158 //第二个触摸点数据地址
26 #define GT_TP3_REG 0X8160 //第三个触摸点数据地址
27 #define GT_TP4_REG 0X8168 //第四个触摸点数据地址
28 #define GT_TP5_REG 0X8170 //第五个触摸点数据地址
29
30
31 u8 GT9147_Send_Cfg(u8 mode); //发送 GT9147 配置参数
32 u8 GT9147_WR_Reg(u16 reg,u8 *buf,u8 len); //向 GT9147 写入一次数据
33 void GT9147_RD_Reg(u16 reg,u8 *buf,u8 len); //从 GT9147 读出一次数据
34 u8 GT9147_Init(void); //初始化 GT9147 触摸屏
35 u8 GT9147_Scan(u8 mode); //扫描触摸屏(采用查询方式)
36 #endif
37
在该头文件中我们宏定义了 IO 操作函数和 GT9147 的寄存器地址。主要用于方便修改和
管理。
接下来看下 gt9147.c 里面的代码,这里我们仅介绍 GT9147_Init 和 GT9147_Scan 两个函数,
代码如下:
94 //初始化 GT9147 触摸屏
95 //返回值:0,初始化成功;1,初始化失败
96 u8 GT9147_Init(void)
97 {
98 u8 temp[5];
99 GT9147_INT_DIR(1);
100 GT9147_INT(1);
101 IIC_Init(); //初始化电容屏的 I2C 总线
102 GT9147_RST(0); //复位
103 delay_ms(10);
104 GT9147_RST(1); //释放复位
105 delay_ms(10);
106 GT9147_INT_DIR(0);
107 delay_ms(100);
108 GT9147_RD_Reg(GT_PID_REG,temp,4);//读取产品 ID
109 temp[4]=0;
110 printf("CTP ID:%s\r\n",temp); //打印 ID
111 if(strcmp((char*)temp,"9147")==0) { //ID==9147
112 temp[0]=0X02;
113 GT9147_WR_Reg(GT_CTRL_REG,temp,1);//软复位 GT9147
114 GT9147_RD_Reg(GT_CFGS_REG,temp,1);//读取 GT_CFGS_REG 寄存器
115 if(temp[0]<0X60) { //默认版本比较低,需要更新 flash 配置
116 // printf("Default Ver:%d\r\n",temp[0]);
117 if(lcddev.id==0X5510)
118 GT9147_Send_Cfg(1);//仅 4.3 寸 MCU 屏,更新并保存配置
119 }
120 delay_ms(10);
121 temp[0]=0X00;
122 GT9147_WR_Reg(GT_CTRL_REG,temp,1);//结束复位
123 return 0;
124 }
125 return 1;
126 }
以上代码,GT9147_Init 用于初始化 GT9147,该函数通过读取 0X8140~0X8143 这 4 个寄存
器,并判断是否是:“9147”
,来确定是不是 GT9147 芯片,在读取到正确的 ID 后,软复位 GT9147,
然后根据当前芯片版本号,确定是否需要更新配置,通过 GT9147_Send_Cfg 函数,发送配置信
息(一个数组),配置完后,结束软复位,即完成 GT9147 初始化。
130 //扫描触摸屏(采用查询方式)
131 //mode:0,正常扫描.
132 //返回值:当前触屏状态.
133 //0,触屏无触摸;1,触屏有触摸
134 u8 GT9147_Scan(u8 mode)
135 {
136 u8 buf[4];
137 u8 i=0;
138 u8 res=0;
139 u8 temp;
140 u8 tempsta;
141 static u8 t=0;//控制查询间隔,从而降低 CPU 占用率
142 t++;
143 if((t%10)==0||t<10) { //空闲时,每进入 10 次 CTP_Scan 函数才检测 1 次,从而节省 CPU 使用率
144 GT9147_RD_Reg(GT_GSTID_REG,&mode,1); //读取触摸点的状态
145 if((mode & 0X80) &&((mode&0XF)<6)) {
146 temp=0;
147 GT9147_WR_Reg(GT_GSTID_REG,&temp,1);//清标志
148 }
149 if((mode&0XF)&&((mode&0XF)<6)) {
150 temp=0XFF<<(mode&0XF); //将点的个数转换为 1 的位数,匹配 tp_dev.sta 定义
151 tempsta=tp_dev.sta; //保存当前的 tp_dev.sta 值
152 tp_dev.sta=(~temp)|TP_PRES_DOWN|TP_CATH_PRES;
153 tp_dev.x[4]=tp_dev.x[0]; //保存触点 0 的数据
154 tp_dev.y[4]=tp_dev.y[0];
155 for(i=0; i<5; i++) {
156 if(tp_dev.sta&(1<<i)) { //触摸有效?
157 GT9147_RD_Reg(GT9147_TPX_TBL[i],buf,4); //读取 XY 坐标值
158 if(lcddev.id==0X5510) { //4.3 寸 800*480 MCU 屏
159 if(tp_dev.touchtype & 0X01) { //横屏
160 tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
161 tp_dev.x[i]=800-(((u16)buf[3]<<8)+buf[2]);
162 } else {
163 tp_dev.x[i]=((u16)buf[1]<<8)+buf[0];
164 tp_dev.y[i]=((u16)buf[3]<<8)+buf[2];
165 }
166 } else if(lcddev.id==0X4342) { //4.3 寸 480*272 RGB 屏
167 if(tp_dev.touchtype & 0X01) { //横屏
168 tp_dev.x[i]=(((u16)buf[1]<<8)+buf[0]);
169 tp_dev.y[i]=(((u16)buf[3]<<8)+buf[2]);
170 } else {
171 tp_dev.y[i]=((u16)buf[1]<<8)+buf[0];
172 tp_dev.x[i]=272-(((u16)buf[3]<<8)+buf[2]);
173 }
174 }
175 // printf("x[%d]:%d,y[%d]:%d\r\n",i,tp_dev.x[i],i,tp_dev.y[i]);
176 }
177 }
178 res=1;
179 if(tp_dev.x[0]>lcddev.width||tp_dev.y[0]>lcddev.height) { // 坐标超出了
180 if((mode&0XF)>1) { //有其他点有数据,则复第二个触点的数据到第一个触点.
181 tp_dev.x[0]=tp_dev.x[1];
182 tp_dev.y[0]=tp_dev.y[1];
183 t=0; //触发一次,则会最少连续监测 10 次,从而提高命中率
184 } else { //非法数据,则忽略此次数据(还原原来的)
185 tp_dev.x[0]=tp_dev.x[4];
186 tp_dev.y[0]=tp_dev.y[4];
187 mode=0X80;
188 tp_dev.sta=tempsta; //恢复 tp_dev.sta
189 }
190 } else t=0; //触发一次,则会最少连续监测 10 次,从而提高命中率
191 }
192 }
193 if((mode&0X8F)==0X80) { //无触摸点按下
194 if(tp_dev.sta&TP_PRES_DOWN) { //之前是被按下的
195 tp_dev.sta &=~(1<<7); //标记按键松开
196 } else { //之前就没有被按下
197 tp_dev.x[0]=0xffff;
198 tp_dev.y[0]=0xffff;
199 tp_dev.sta&=0XE0; //清除点有效标记
200 }
201 }
202 if(t>240)
203 t=10;//重新从 10 开始计数
204 return res;
205 }
GT9147_Scan 函数用于扫描电容触摸屏是否有按键按下,由于我们不是用的中断方式来读
取 GT9147 的数据的,而是采用查询的方式,所以这里使用了一个静态变量来提高效率,当无
触摸的时候,尽量减少对 CPU 的占用,当有触摸的时候,又保证能迅速检测到。对 GT9147 数
据的读取,先读取手势 ID 寄存器(GT_GSTID_REG),判断是不是有有效数据,如果有,则读
取,否则直接忽略,继续后面的处理。
最后我们打开 qsys_touch.c,这里就不全部贴出来了,仅介绍 main 函数和电容触摸屏测试
函数。main 函数代码如下:
17 int main(void) {
18 printf("Hello from Nios II!\n");
19 MCULCD_Init(); //初始化 MCU LCD
20 tp_dev.init(); //触摸屏初始化
21 POINT_COLOR = MLCD_RED;
22
23 LCD_ShowString(30, 50, 300, 30, 24, 0, "Welcome to PIONEER FPGA");
24 LCD_ShowString(30, 80, 400, 30, 24, 0, "This is a TOUCH test application");
25 LCD_ShowString(30, 110, 200, 30, 24, 0, "ATOM@ALIENTEK");
26 LCD_ShowString(30, 140, 200, 30, 24, 0, "2018/10/10");
27
28 delay_ms(1500);
29 Load_Drow_Dialog();
30
31 if (tp_dev.touchtype & 0X80)
32 ctp_test(); //电容屏测试
33 }
main 函数比较简单,初始化相关外设,输出提示信息,然后根据触摸屏类型,去选择执
行 ctp_test 函数。ctp_test 函数代码如下:
83 //电容触摸屏测试函数
84 void ctp_test(void) {
85 u8 t = 0;
86 u8 i = 0;
87 u16 lastpos[10][2]; //最后一次的数据
88 u8 maxp = 5; //最大触摸点数
89 if (lcddev.id == 0X1018)
90 maxp = 10;
91 while(1) {
92 tp_dev.scan(0);
93 for (t = 0; t < maxp; t++) {
94 if ((tp_dev.sta) & (1 << t)) {
95 if (tp_dev.x[t] < lcddev.width && tp_dev.y[t] < lcddev.height) {
96 if (lastpos[t][0] == 0XFFFF) {
97 lastpos[t][0] = tp_dev.x[t];
98 lastpos[t][1] = tp_dev.y[t];
99 }
100 lcd_draw_bline(lastpos[t][0], lastpos[t][1], tp_dev.x[t],
101 tp_dev.y[t], 2, POINT_COLOR_TBL[t]); //画线
102 lastpos[t][0] = tp_dev.x[t];
103 lastpos[t][1] = tp_dev.y[t];
104 if (tp_dev.x[t] > (lcddev.width - 43) && tp_dev.y[t] < 24) {
105 Load_Drow_Dialog(); //清除
106 }
107 }
108 } else
109 lastpos[t][0] = 0XFFFF;
110 }
111 delay_ms(5);
112 i++;
113 }
114 }
ctp_test,该函数用于电容触摸屏的测试,由于我们采用 tp_dev.sta 来标记当前按下的触摸
屏点数,所以判断是否有电容触摸屏按下,也就是判断 tp_dev.sta 的最低 5 位,如果有数据,
则划线,如果没数据则忽略,且 5 个点划线的颜色各不一样,方便区分。另外,电容触摸屏不
需要校准,所以没有校准程序。
软件部分就介绍到这里,接下来看看下载验证。
下载验证
讲完了软件工程,接下来我们就将该实验下载至我们的开拓者开发板进行验证。
首先我们将 4.3 寸的 ATK-4.3’TFTLCD 与开发板上的 MCU TFT LCD 接口连接。再将下载器
一端连电脑,另一端与开发板上对应端口连接,最后连接电源线并打开电源开关。
我们在 Quartus II 软件中将 qsys_touch.sof 文件下载至我们的开拓者开发板,qsys_ touch.sof
下载完成后,我们还需要在 Nios II SBT for Eclipse 软件中将 qsys_ touch.elf 文件下载至我们的开
拓者开发板,qsys_ touch.elf 下载完成以后,我们的 C 程序将会执行在我们的开拓者开发板上。
手指在触摸屏上划动时显示划动的轨迹(写一个“正”字),结果如下图所示。
图 12.5.1 实验结果图
至此,我们的 TFT LCD 触摸屏实验就完成了。
猜你喜欢
- 2024-09-16 手机“满血复活”时间从数小时到几分钟 技术进步让大众告别充电焦虑
- 2024-09-16 51单片机AD模数转换,使用SPI通信方式
- 2024-09-16 正点原子STM32F4/F7水星开发板资料连载第三十五章 触摸屏实验
- 2024-09-16 正点原子开拓者NiosII资料连载第二十七章GUI综合实验
- 2024-09-16 「正点原子STM32Mini板资料连载」第二十六章 触摸屏实验
- 2024-09-16 串行交互程序设计基于STM32103VET6
- 2024-09-16 Labview实验二十:触摸屏实验(labview自动触发按键)
- 2024-09-16 探索者 STM32F407 开发板资料连载第三十三章 触摸屏实验
- 2024-09-16 基于STM32设计的数码相册(stm32电子相册毕业设计论文)
- 2024-09-16 基于STM32的 RTC实时时钟屏幕显示工程
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- 电脑显示器花屏 (79)
- 403 forbidden (65)
- linux怎么查看系统版本 (54)
- 补码运算 (63)
- 缓存服务器 (61)
- 定时重启 (59)
- plsql developer (73)
- 对话框打开时命令无法执行 (61)
- excel数据透视表 (72)
- oracle认证 (56)
- 网页不能复制 (84)
- photoshop外挂滤镜 (58)
- 网页无法复制粘贴 (55)
- vmware workstation 7 1 3 (78)
- jdk 64位下载 (65)
- phpstudy 2013 (66)
- 卡通形象生成 (55)
- psd模板免费下载 (67)
- shift (58)
- localhost打不开 (58)
- 检测代理服务器设置 (55)
- frequency (66)
- indesign教程 (55)
- 运行命令大全 (61)
- ping exe (64)
本文暂时没有评论,来添加一个吧(●'◡'●)