一 简介
在了解 RGBLCD 成像原理后 继续学习触摸屏使用
ALIENTEK 的三款 RGB LCD 屏幕都支持多点电容触摸,本章就以 ATK7016 这款 RGB LCD 屏幕为例讲解一下如 何驱动电容触摸屏,并获取对应的触摸坐标值。
1.1 触摸屏发展史
1993年,电阻式的触摸屏被用到了苹果掌上电脑Apple Newton;后来乔布斯又用iPad重新定义了平板电脑的概念和接下来iPhone手机的面世,让以触摸技术结合各种应用软件的智能产品开始逐渐商用化 在此过程中 电容触摸屏也应运而出,电容式触摸屏 普及时间是2007年以后,各种智能手机和平板电脑都使用了触控面板,家喻户晓
和电阻触摸屏相比,电容触摸屏最大的优点是支持多点触摸 ( 后面的电阻屏也支持多点触摸,但是为时已晚 ) ,电容屏只需要手指轻触即可,而电阻屏是需要手指给予一定的压力才有反应,而且电容屏不需要校准。
https://blog.youkuaiyun.com/TyearLin/article/details/131524815ALIENTEK 的三款 RGB LCD 屏幕都是支持 5 点电容触摸屏的
1.2 触摸屏 主要分类
触摸屏根据所用的介质以及工作原理,可分为电阻式、电容感应式、红外线式和表面声波式四种
- 电阻式触摸屏:这种触摸屏利用压力感应进行控制,以电阻薄膜屏为基础,是一种多层复合薄膜,由一层玻璃或硬塑料填以纤维或凝胶等材料构成。 精度高,反应灵敏,对环境的要求比较低,而且没有辐射,屏幕厚度比较薄,一般为5mm左右,价格相对低廉。由于主要利用压力感应,因此容易产生漂移,而且表面损伤后,修复较难,需要用整个更换。
- 电容式触摸屏:这种触摸屏利用人体的电流感应进行工作,主要由触摸屏传感器和触摸屏控制器组成。传感器一般采用四线或五线电阻式,控制器通过电容式触摸屏左上角和右下角电极中必须有一个加以控制。 支持多点触摸、耐用度高、防尘、防水、耐磨、反应灵敏、对环境的要求比较低、无辐射。 精度不高、如果屏幕较小,触摸困难,因而漂移现象比较严重、成本偏高。
- 红外线式触摸屏:这种触摸屏利用手指释放的红外线进行定位,工作原理是在触摸屏的前面设置一个条形音箱,音箱的透光率为零,然后利用音箱接收这些红外线并反射到屏幕的左右角顶端,再由触摸屏反射到音箱。 性能稳定、无漂移、无须力度触摸 但色彩还原能力光照情况下较差
- 表面声波式触摸屏:这种触摸屏利用表面声波技术进行定位。表面声波是一种超声波,采用先进的表面声波处理技术进行定位。具有 反应灵敏、漂移小、可以持续进行高精度高质量的定位,并且屏幕具有防暴保护作用,抗恶劣环境能力较高等特性,
根据屏幕材质又可分为 LCD、TFT、IPS、OLED、AMOLED屏
二 结构原理
电容式触摸屏的主要组成结构是盖板、触摸传感器和触摸控制板组成
2.1 ATK-7016
2.2 FT5426
电容触摸屏也是需要一个驱动 IC 的,驱动 IC 一般会提供一个 I2C 接口给主控制器,主控制器可以通过 I2C 接口来读取驱动 IC 里面的触摸坐标数据。ATK-7016、ATK-7084 这两款屏幕使用的触摸控制 IC 是 FT5426,ATK- 4342 使用的驱动 IC 是 GT9147。这三个电容屏触摸 IC 都是 I2C 接口的,使用方法基本一样。

三 例程
多点触摸实验原理
LCD 实验的基础上 增加FT5426触摸驱动 通过中断方式 读取触摸相关寄存器 值 并显示
裸机驱动低级api相对来讲 直接操作寄存器 较为复杂 ,即使在 bsp的基础上。
而linux驱动开发 通过访问设备树节点 驱动实现基本io操作 更加规范
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_ft5xx6.c
作者 : 左忠凯
版本 : V1.0
描述 : 触摸屏驱动文件,触摸芯片为FT5xx6,
包括FT5426和FT5406。
其他 : 无
论坛 : www.wtmembed.com
日志 : 初版V1.0 2019/1/21 左忠凯创建
***************************************************************/
#include "bsp_ft5xx6.h"
#include "bsp_i2c.h"
#include "bsp_int.h"
#include "bsp_delay.h"
#include "stdio.h"
struct ft5426_dev_struc ft5426_dev;
/*
* @description : 初始化触摸屏,其实就是初始化FT5426
* @param : 无
* @return : 无
*/
void ft5426_init(void)
{
unsigned char reg_value[2];
ft5426_dev.initfalg = FT5426_INIT_NOTFINISHED;
int i;
for( i = 0; i < 5; i++ )
{ /* 避免编译器自动赋值 */
ft5426_dev.x[i] = 0;
ft5426_dev.y[i] = 0;
}
ft5426_dev.point_num = 0;
/* 1、初始化IIC2 IO
* I2C2_SCL -> UART5_TXD
* I2C2_SDA -> UART5_RXD
*/
IOMUXC_SetPinMux(IOMUXC_UART5_TX_DATA_I2C2_SCL,1);
IOMUXC_SetPinMux(IOMUXC_UART5_RX_DATA_I2C2_SDA,1);
/* 配置I2C2 IO属性
*bit 16:0 HYS关闭
*bit [15:14]: 1 默认47K上拉
*bit [13]: 1 pull功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 关闭开路输出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 驱动能力为R0/6
*bit [0]: 1 高转换率
*/
IOMUXC_SetPinConfig(IOMUXC_UART5_TX_DATA_I2C2_SCL,0x70B0);
IOMUXC_SetPinConfig(IOMUXC_UART5_RX_DATA_I2C2_SDA,0X70B0);
/* 2、初始化触摸屏中断IO和复位IO */
gpio_pin_config_t ctintpin_config;
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO09_GPIO1_IO09,0); /* 复用为GPIO1_IO9 */
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09,0);/* 复用为GPIO5_IO9 */
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO09_GPIO1_IO09,0xF080);
IOMUXC_SetPinConfig(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09,0X10B0);
/* 中断IO初始化 */
ctintpin_config.direction = kGPIO_DigitalInput;
ctintpin_config.interruptMode = kGPIO_IntRisingOrFallingEdge;
gpio_init(GPIO1, 9, &ctintpin_config);
GIC_EnableIRQ(GPIO1_Combined_0_15_IRQn); /* 使能GIC中对应的中断 */
system_register_irqhandler(GPIO1_Combined_0_15_IRQn, (system_irq_handler_t)gpio1_io9_irqhandler, NULL); /* 注册中断服务函数 */
gpio_enableint(GPIO1, 9); /* 使能GPIO1_IO18的中断功能 */
/* 复位IO初始化 */
ctintpin_config.direction=kGPIO_DigitalOutput;
ctintpin_config.interruptMode=kGPIO_NoIntmode;
ctintpin_config.outputLogic=1;
gpio_init(GPIO5, 9, &ctintpin_config);
/* 3、初始化I2C */
i2c_init(I2C2);
/* 4、初始化FT5426 */
gpio_pinwrite(GPIO5, 9, 0); /* 复位FT5426 */
delayms(20);
gpio_pinwrite(GPIO5, 9, 1); /* 停止复位FT5426 */
delayms(20);
ft5426_write_byte(FT5426_ADDR, FT5426_DEVICE_MODE, 0); /* 进入正常模式 */
ft5426_write_byte(FT5426_ADDR, FT5426_IDG_MODE, 1); /* FT5426中断模式 */
ft5426_read_len(FT5426_ADDR, FT5426_IDGLIB_VERSION, 2, reg_value);
printf("Touch Frimware Version:%#X\r\n", ((unsigned short)reg_value[0] << 8) + reg_value[1]);
ft5426_dev.initfalg = FT5426_INIT_FINISHED; /* 标记FT5426初始化完成 */
ft5426_dev.intflag = 0;
}
/*
* @description : GPIO1_IO9最终的中断处理函数
* @param : 无
* @return : 无
*/
void gpio1_io9_irqhandler(void)
{
if(ft5426_dev.initfalg == FT5426_INIT_FINISHED)
{
//ft5426_dev.intflag = 1;
ft5426_read_tpcoord();
}
gpio_clearintflags(GPIO1, 9); /* 清除中断标志位 */
}
/*
* @description : 向FT5429写入数据
* @param - addr: 设备地址
* @param - reg : 要写入的寄存器
* @param - data: 要写入的数据
* @return : 操作结果
*/
unsigned char ft5426_write_byte(unsigned char addr,unsigned char reg, unsigned char data)
{
unsigned char status=0;
unsigned char writedata=data;
struct i2c_transfer masterXfer;
/* 配置I2C xfer结构体 */
masterXfer.slaveAddress = addr; /* 设备地址 */
masterXfer.direction = kI2C_Write; /* 写入数据 */
masterXfer.subaddress = reg; /* 要写入的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址长度一个字节 */
masterXfer.data = &writedata; /* 要写入的数据 */
masterXfer.dataSize = 1; /* 写入数据长度1个字节 */
if(i2c_master_transfer(I2C2, &masterXfer))
status=1;
return status;
}
/*
* @description : 从FT5426读取一个字节的数据
* @param - addr: 设备地址
* @param - reg : 要读取的寄存器
* @return : 读取到的数据。
*/
unsigned char ft5426_read_byte(unsigned char addr,unsigned char reg)
{
unsigned char val=0;
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr; /* 设备地址 */
masterXfer.direction = kI2C_Read; /* 读取数据 */
masterXfer.subaddress = reg; /* 要读取的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址长度一个字节 */
masterXfer.data = &val; /* 接收数据缓冲区 */
masterXfer.dataSize = 1; /* 读取数据长度1个字节 */
i2c_master_transfer(I2C2, &masterXfer);
return val;
}
/*
* @description : 从FT5429读取多个字节的数据
* @param - addr: 设备地址
* @param - reg : 要读取的开始寄存器地址
* @param - len : 要读取的数据长度.
* @param - buf : 读取到的数据缓冲区
* @return : 无
*/
void ft5426_read_len(unsigned char addr,unsigned char reg,unsigned char len,unsigned char *buf)
{
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr; /* 设备地址 */
masterXfer.direction = kI2C_Read; /* 读取数据 */
masterXfer.subaddress = reg; /* 要读取的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址长度一个字节 */
masterXfer.data = buf; /* 接收数据缓冲区 */
masterXfer.dataSize = len; /* 读取数据长度1个字节 */
i2c_master_transfer(I2C2, &masterXfer);
}
/*
* @description : 读取当前触摸点个数
* @param : 无
* @return : 无
*/
void ft5426_read_tpnum(void)
{
ft5426_dev.point_num = ft5426_read_byte(FT5426_ADDR, FT5426_TD_STATUS);
}
/*
* @description : 读取当前所有触摸点的坐标
* @param : 无
* @return : 无
*/
void ft5426_read_tpcoord(void)
{
unsigned char i = 0;
unsigned char type = 0;
//unsigned char id = 0;
unsigned char pointbuf[FT5426_XYCOORDREG_NUM];
ft5426_dev.point_num = ft5426_read_byte(FT5426_ADDR, FT5426_TD_STATUS);
/*
* 从寄存器FT5426_TOUCH1_XH开始,连续读取30个寄存器的值,这30个寄存器
* 保存着5个点的触摸值,每个点占用6个寄存器。
*/
ft5426_read_len(FT5426_ADDR, FT5426_TOUCH1_XH, FT5426_XYCOORDREG_NUM, pointbuf);
for(i = 0; i < ft5426_dev.point_num ; i++)
{
unsigned char *buf = &pointbuf[i * 6];
/* 以第一个触摸点为例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
* bit7:6 Event flag 0:按下 1:释放 2:接触 3:没有事件
* bit5:4 保留
* bit3:0 X轴触摸点的11~8位。
*/
ft5426_dev.x[i] = ((buf[2] << 8) | buf[3]) & 0x0fff;
ft5426_dev.y[i] = ((buf[0] << 8) | buf[1]) & 0x0fff;
type = buf[0] >> 6; /* 获取触摸类型 */
/* 以第一个触摸点为例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
* bit7:4 Touch ID 触摸ID,表示是哪个触摸点
* bit3:0 Y轴触摸点的11~8位。
*/
//id = (buf[2] >> 4) & 0x0f;
if(type == FT5426_TOUCH_EVENT_DOWN || type == FT5426_TOUCH_EVENT_ON )/* 按下 */
{
} else { /* 释放 */
}
}
}
i2c
/***************************************************************
Copyright © zuozhongkai Co., Ltd. 1998-2019. All rights reserved.
文件名 : bsp_i2c.c
作者 : 左忠凯
版本 : V1.0
描述 : IIC驱动文件。
其他 : 无
论坛 : www.wtmembed.com
日志 : 初版V1.0 2019/1/15 左忠凯创建
***************************************************************/
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "stdio.h"
/*
* @description : 初始化I2C,波特率100KHZ
* @param - base : 要初始化的IIC设置
* @return : 无
*/
void i2c_init(I2C_Type *base)
{
/* 1、配置I2C */
base->I2CR &= ~(1 << 7); /* 要访问I2C的寄存器,首先需要先关闭I2C */
/* 设置波特率为100K
* I2C的时钟源来源于IPG_CLK_ROOT=66Mhz
* IC2 时钟 = PERCLK_ROOT/dividison(IFDR寄存器)
* 设置寄存器IFDR,IFDR寄存器参考IMX6UL参考手册P1260页,表29-3,
* 根据表29-3里面的值,挑选出一个还是的分频数,比如本例程我们
* 设置I2C的波特率为100K, 因此当分频值=66000000/100000=660.
* 在表29-3里面查找,没有660这个值,但是有640,因此就用640,
* 即寄存器IFDR的IC位设置为0X15
*/
base->IFDR = 0X15 << 0;
/*
* 设置寄存器I2CR,开启I2C
* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必须最先置1
*/
base->I2CR |= (1<<7);
}
/*
* @description : 发送重新开始信号
* @param - base : 要使用的IIC
* @param - addrss : 设备地址
* @param - direction : 方向
* @return : 0 正常 其他值 出错
*/
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{
/* I2C忙并且工作在从模式,跳出 */
if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0))
return 1;
/*
* 设置寄存器I2CR
* bit[4]: 1 发送
* bit[2]: 1 产生重新开始信号
*/
base->I2CR |= (1 << 4) | (1 << 2);
/*
* 设置寄存器I2DR
* bit[7:0] : 要发送的数据,这里写入从设备地址
* 参考资料:IMX6UL参考手册P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
/*
* @description : 发送开始信号
* @param - base : 要使用的IIC
* @param - addrss : 设备地址
* @param - direction : 方向
* @return : 0 正常 其他值 出错
*/
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{
if(base->I2SR & (1 << 5)) /* I2C忙 */
return 1;
/*
* 设置寄存器I2CR
* bit[5]: 1 主模式
* bit[4]: 1 发送
*/
base->I2CR |= (1 << 5) | (1 << 4);
/*
* 设置寄存器I2DR
* bit[7:0] : 要发送的数据,这里写入从设备地址
* 参考资料:IMX6UL参考手册P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
/*
* @description : 检查并清除错误
* @param - base : 要使用的IIC
* @param - status : 状态
* @return : 状态结果
*/
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status)
{
/* 检查是否发生仲裁丢失错误 */
if(status & (1<<4))
{
base->I2SR &= ~(1<<4); /* 清除仲裁丢失错误位 */
base->I2CR &= ~(1 << 7); /* 先关闭I2C */
base->I2CR |= (1 << 7); /* 重新打开I2C */
return I2C_STATUS_ARBITRATIONLOST;
}
else if(status & (1 << 0)) /* 没有接收到从机的应答信号 */
{
return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
}
return I2C_STATUS_OK;
}
/*
* @description : 停止信号
* @param - base : 要使用的IIC
* @param : 无
* @return : 状态结果
*/
unsigned char i2c_master_stop(I2C_Type *base)
{
unsigned short timeout = 0xffff;
/*
* 清除I2CR的bit[5:3]这三位
*/
base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
/* 等待忙结束 */
while((base->I2SR & (1 << 5)))
{
timeout--;
if(timeout == 0) /* 超时跳出 */
return I2C_STATUS_TIMEOUT;
}
return I2C_STATUS_OK;
}
/*
* @description : 发送数据
* @param - base : 要使用的IIC
* @param - buf : 要发送的数据
* @param - size : 要发送的数据大小
* @param - flags : 标志
* @return : 无
*/
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size)
{
/* 等待传输完成 */
while(!(base->I2SR & (1 << 7)));
base->I2SR &= ~(1 << 1); /* 清除标志位 */
base->I2CR |= 1 << 4; /* 发送数据 */
while(size--)
{
base->I2DR = *buf++; /* 将buf中的数据写入到I2DR寄存器 */
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除标志位 */
/* 检查ACK */
if(i2c_check_and_clear_error(base, base->I2SR))
break;
}
base->I2SR &= ~(1 << 1);
i2c_master_stop(base); /* 发送停止信号 */
}
/*
* @description : 读取数据
* @param - base : 要使用的IIC
* @param - buf : 读取到数据
* @param - size : 要读取的数据大小
* @return : 无
*/
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size)
{
volatile uint8_t dummy = 0;
dummy++; /* 防止编译报错 */
/* 等待传输完成 */
while(!(base->I2SR & (1 << 7)));
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收数据 */
/* 如果只接收一个字节数据的话发送NACK信号 */
if(size == 1)
base->I2CR |= (1 << 3);
dummy = base->I2DR; /* 假读 */
while(size--)
{
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
base->I2SR &= ~(1 << 1); /* 清除标志位 */
if(size == 0)
{
i2c_master_stop(base); /* 发送停止信号 */
}
if(size == 1)
{
base->I2CR |= (1 << 3);
}
*buf++ = base->I2DR;
}
}
/*
* @description : I2C数据传输,包括读和写
* @param - base: 要使用的IIC
* @param - xfer: 传输结构体
* @return : 传输结果,0 成功,其他值 失败;
*/
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer)
{
unsigned char ret = 0;
enum i2c_direction direction = xfer->direction;
base->I2SR &= ~((1 << 1) | (1 << 4)); /* 清除标志位 */
/* 等待传输完成 */
while(!((base->I2SR >> 7) & 0X1)){};
/* 如果是读的话,要先发送寄存器地址,所以要先将方向改为写 */
if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read))
{
direction = kI2C_Write;
}
ret = i2c_master_start(base, xfer->slaveAddress, direction); /* 发送开始信号 */
if(ret)
{
return ret;
}
while(!(base->I2SR & (1 << 1))){}; /* 等待传输完成 */
ret = i2c_check_and_clear_error(base, base->I2SR); /* 检查是否出现传输错误 */
if(ret)
{
i2c_master_stop(base); /* 发送出错,发送停止信号 */
return ret;
}
/* 发送寄存器地址 */
if(xfer->subaddressSize)
{
do
{
base->I2SR &= ~(1 << 1); /* 清除标志位 */
xfer->subaddressSize--; /* 地址长度减一 */
base->I2DR = ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器写入子地址
while(!(base->I2SR & (1 << 1))); /* 等待传输完成 */
/* 检查是否有错误发生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
i2c_master_stop(base); /* 发送停止信号 */
return ret;
}
} while ((xfer->subaddressSize > 0) && (ret == I2C_STATUS_OK));
if(xfer->direction == kI2C_Read) /* 读取数据 */
{
base->I2SR &= ~(1 << 1); /* 清除中断挂起位 */
i2c_master_repeated_start(base, xfer->slaveAddress, kI2C_Read); /* 发送重复开始信号和从机地址 */
while(!(base->I2SR & (1 << 1))){};/* 等待传输完成 */
/* 检查是否有错误发生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
ret = I2C_STATUS_ADDRNAK;
i2c_master_stop(base); /* 发送停止信号 */
return ret;
}
}
}
/* 发送数据 */
if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
{
i2c_master_write(base, xfer->data, xfer->dataSize);
}
/* 读取数据 */
if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
{
i2c_master_read(base, xfer->data, xfer->dataSize);
}
return 0;
}