背景知识
关于电容屏的物理原理我们就不去研究了,毕竟我们不是开发电容屏的,而是电容屏的使用者,我们只需要关注如何使用电容屏,如何得到其多点触摸坐标值即可。
我们以触摸控制IC FT5426为例,FT5426 这款驱动 IC 采用 15*28 的驱动结构,也就是 15 个感应通道, 28 个驱动通道,最多支持 5 点电容触摸。笔者所用的电容触摸屏部分有 4 个 IO 用于连接主控制器: SCL、 SDA、RST 和 INT, SCL 和 SDA 是 I2C 引脚, RST 是复位引脚, INT 是中断引脚。一般通过 INT 引脚来通知主控制器有触摸点按下,然后在 INT 中断服务函数中读取触摸数据。也可以不使用中断功能,采用轮询的方式不断查询是否有触摸点按下,本实验我们使用中断方式来获取触摸数据。
跟所有的 I2C 器件一样, FT5426 也是通过读写寄存器来完成初始化和触摸坐标数据读取的。FT5426部分寄存器及其功能如下表所示:
程序编写
ft5426.c:
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;
/* 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 { /* 释放 */
}
}
}
ft5426.c中共有7个函数,ft5426_init初始化ft5426所使用的接口引脚、复位引脚、中断引脚,而后使能了ft5426所使用的中断,注册中断处理函数,最后初始化I2C2和ft5426; gpio1_io9_irqhandler是引脚中断处理函数,在函数中读取触摸点位置信息;ft5426_write_byte 和ft5426_read_byte用于向ft5426寄存器读写指定的值;ft5426_read_len是从ft5426指定寄存器读取数个连续的寄存器;ft5426_read_tpnum,此函数用于获取 FT5426 当前有几个触摸点有效,也就是触摸点个数;ft5426_read_tpcoord,此函数就是读取 FT5426 各个触摸点坐标值。
main.c:
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_ft5xx6.h"
#include "bsp_gt9147.h"
#include "stdio.h"
/*
* @description : 使能I.MX6U的硬件NEON和FPU
* @param : 无
* @return : 无
*/
void imx6ul_hardfpu_enable(void)
{
uint32_t cpacr;
uint32_t fpexc;
/* 使能NEON和FPU */
cpacr = __get_CPACR();
cpacr = (cpacr & ~(CPACR_ASEDIS_Msk | CPACR_D32DIS_Msk))
| (3UL << CPACR_cp10_Pos) | (3UL << CPACR_cp11_Pos);
__set_CPACR(cpacr);
fpexc = __get_FPEXC();
fpexc |= 0x40000000UL;
__set_FPEXC(fpexc);
}
/*
* @description : main函数
* @param : 无
* @return : 无
*/
int main(void)
{
unsigned char i = 0;
unsigned char state = OFF;
imx6ul_hardfpu_enable(); /* 使能I.MX6U的硬件浮点 */
int_init(); /* 初始化中断(一定要最先调用!) */
imx6u_clkinit(); /* 初始化系统时钟 */
delay_init(); /* 初始化延时 */
clk_enable(); /* 使能所有的时钟 */
led_init(); /* 初始化led */
beep_init(); /* 初始化beep */
uart_init(); /* 初始化串口,波特率115200 */
lcd_init(); /* 初始化LCD */
/* 初始化触摸屏 */
if(tftlcd_dev.id == ATK7016 || tftlcd_dev.id == ATK7084) {
ft5426_init();
} else if(tftlcd_dev.id == ATK4384|| tftlcd_dev.id == ATK4342) {
gt9147_init();
}
tftlcd_dev.forecolor = LCD_RED;
lcd_show_string(50, 10, 400, 24, 24, (char*)"TOUCH SCREEN TEST");
lcd_show_string(50, 40, 200, 16, 16, (char*)"CSDN ");
lcd_show_string(50, 60, 200, 16, 16, (char*)"jiajia 2020");
lcd_show_string(50, 80, 200, 16, 16, (char*)"2021/8/2");
lcd_show_string(50, 110, 400, 16, 16, (char*)"TP Num :");
lcd_show_string(50, 130, 200, 16, 16, (char*)"Point0 X:");
lcd_show_string(50, 150, 200, 16, 16, (char*)"Point0 Y:");
lcd_show_string(50, 170, 200, 16, 16, (char*)"Point1 X:");
lcd_show_string(50, 190, 200, 16, 16, (char*)"Point1 Y:");
lcd_show_string(50, 210, 200, 16, 16, (char*)"Point2 X:");
lcd_show_string(50, 230, 200, 16, 16, (char*)"Point2 Y:");
lcd_show_string(50, 250, 200, 16, 16, (char*)"Point3 X:");
lcd_show_string(50, 270, 200, 16, 16, (char*)"Point3 Y:");
lcd_show_string(50, 290, 200, 16, 16, (char*)"Point4 X:");
lcd_show_string(50, 310, 200, 16, 16, (char*)"Point4 Y:");
tftlcd_dev.forecolor = LCD_BLUE;
while(1)
{
if(tftlcd_dev.id == ATK7016 || tftlcd_dev.id == ATK7084) {
lcd_shownum(50 + 72, 110, ft5426_dev.point_num , 1, 16);
lcd_shownum(50 + 72, 130, ft5426_dev.x[0], 5, 16);
lcd_shownum(50 + 72, 150, ft5426_dev.y[0], 5, 16);
lcd_shownum(50 + 72, 170, ft5426_dev.x[1], 5, 16);
lcd_shownum(50 + 72, 190, ft5426_dev.y[1], 5, 16);
lcd_shownum(50 + 72, 210, ft5426_dev.x[2], 5, 16);
lcd_shownum(50 + 72, 230, ft5426_dev.y[2], 5, 16);
lcd_shownum(50 + 72, 250, ft5426_dev.x[3], 5, 16);
lcd_shownum(50 + 72, 270, ft5426_dev.y[3], 5, 16);
lcd_shownum(50 + 72, 290, ft5426_dev.x[4], 5, 16);
lcd_shownum(50 + 72, 310, ft5426_dev.y[4], 5, 16);
} else if(tftlcd_dev.id == ATK4384|| tftlcd_dev.id == ATK4342) {
lcd_shownum(50 + 72, 110, gt9147_dev.point_num , 1, 16);
lcd_shownum(50 + 72, 130, gt9147_dev.x[0], 5, 16);
lcd_shownum(50 + 72, 150, gt9147_dev.y[0], 5, 16);
lcd_shownum(50 + 72, 170, gt9147_dev.x[1], 5, 16);
lcd_shownum(50 + 72, 190, gt9147_dev.y[1], 5, 16);
lcd_shownum(50 + 72, 210, gt9147_dev.x[2], 5, 16);
lcd_shownum(50 + 72, 230, gt9147_dev.y[2], 5, 16);
lcd_shownum(50 + 72, 250, gt9147_dev.x[3], 5, 16);
lcd_shownum(50 + 72, 270, gt9147_dev.y[3], 5, 16);
lcd_shownum(50 + 72, 290, gt9147_dev.x[4], 5, 16);
lcd_shownum(50 + 72, 310, gt9147_dev.y[4], 5, 16);
}
delayms(10);
i++;
if(i == 50)
{
i = 0;
state = !state;
led_switch(LED0,state);
}
}
return 0;
}
主函数中要实现的功能为判断屏幕同时触摸点的个数,并将触摸点坐标读取出来后显示在RGBLCD上。
编译下载
下载验证,结果与预想的一致,实物中分别为不触摸和三点触摸情况下的显示结果: