1.電容觸摸屏簡介
- 電容屏只需要手指輕觸即可,而電阻屏是需要手指
給予一定的壓力才有反應(yīng),而且電容屏不需要校準(zhǔn)。 - 如果要做人機(jī)交互設(shè)備的開發(fā),多點(diǎn)電容觸摸屏基本是不可能繞過去的。
2. 驅(qū)動(dòng)器件
- 正點(diǎn)原子ATK-7016 這款屏幕其實(shí)是由 TFT LCD+觸摸屏組合起來的。底下是 LCD 面板,上面是觸摸面板,將兩個(gè)封裝到一起就成了帶有觸摸屏的 LCD 屏幕
- 電容觸摸屏也是需要一個(gè)驅(qū)動(dòng) IC的,驅(qū)動(dòng) IC 一般會(huì)提供一個(gè) I2C 接口給主控制器主控制器可以通過 I2C 接口來讀取驅(qū)動(dòng) IC里面的觸摸坐標(biāo)數(shù)據(jù)
- ATK-7016、ATK-7084這兩款屏幕使用的觸摸控制 IC 是 FT5426。
1.FT5426 這款驅(qū)動(dòng) IC 采用 15*28 的驅(qū)動(dòng)結(jié)構(gòu),也就是 15 個(gè)感應(yīng)通道,28 個(gè)驅(qū)動(dòng)通道,
2.最多支持5點(diǎn)電容觸摸。 - ATK-7016的電容觸摸屏部分有4個(gè) IO用于連接主控制器: SCL、 SDA、RST 和 INT。
1.SCL 和 SDA 是 I2C 引腳,
2.RST 是復(fù)位引腳,INT 是中斷引腳。
一般通過 INT 引腳來通知主控制器有觸摸點(diǎn)按下,然后在INT 中斷服務(wù)函數(shù)中讀取觸摸數(shù)據(jù)。也可以不使用中斷功能,采用輪詢的方式不斷查詢是否有觸摸點(diǎn)按下。 - 和所有的 I2C 器件一樣,F(xiàn)T5426 也是通過讀寫寄存器來完成初始化和觸摸坐標(biāo)數(shù)據(jù)讀取的主要工作就是讀寫FT5426 的寄存器
-
FT5426 的 I2C 設(shè)備地址為 0X38,F(xiàn)T5426 的寄存器有很多,本章我們只用到了其中的一部分:
- 觸摸屏與單片機(jī)接觸引腳如下:
1.觸摸屏連接著I.MX6U的I2C2,
2.INT引腳連接著I.MX6U的GPIO1_IO9,
3.RST 引腳連接著 I.MX6U 的 SNVS_TAMPER9。
裸機(jī)驅(qū)動(dòng)
- 同樣用的是和前面I2C驅(qū)動(dòng)的一樣的是,主機(jī)驅(qū)動(dòng)和設(shè)備驅(qū)動(dòng)
- 主機(jī)驅(qū)動(dòng)就是配置SOC的代碼
主機(jī)驅(qū)動(dòng)bsp_i2c.h
#ifndef _BSP_I2C_H
#define _BSP_I2C_H
#include "imx6ul.h"
/* 相關(guān)宏定義 */
#define I2C_STATUS_OK (0)
#define I2C_STATUS_BUSY (1)
#define I2C_STATUS_IDLE (2)
#define I2C_STATUS_NAK (3)
#define I2C_STATUS_ARBITRATIONLOST (4)
#define I2C_STATUS_TIMEOUT (5)
#define I2C_STATUS_ADDRNAK (6)
/*
* I2C方向枚舉類型
*/
enum i2c_direction
{
kI2C_Write = 0x0, /* 主機(jī)向從機(jī)寫數(shù)據(jù) */
kI2C_Read = 0x1, /* 主機(jī)從從機(jī)讀數(shù)據(jù) */
} ;
/*
* 主機(jī)傳輸結(jié)構(gòu)體
*/
struct i2c_transfer
{
unsigned char slaveAddress; /* 7位從機(jī)地址 */
enum i2c_direction direction; /* 傳輸方向 */
unsigned int subaddress; /* 寄存器地址 */
unsigned char subaddressSize; /* 寄存器地址長度 */
unsigned char *volatile data; /* 數(shù)據(jù)緩沖區(qū) */
volatile unsigned int dataSize; /* 數(shù)據(jù)緩沖區(qū)長度 */
};
/*
*函數(shù)聲明
*/
void i2c_init(I2C_Type *base);
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status);
unsigned char i2c_master_stop(I2C_Type *base);
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size);
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size);
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);
#endif
主機(jī)驅(qū)動(dòng)bsp_i2c.c
#include "bsp_i2c.h"
#include "bsp_delay.h"
#include "stdio.h"
/*
* @description : 初始化I2C,波特率100KHZ
* @param - base : 要初始化的IIC設(shè)置
* @return : 無
*/
void i2c_init(I2C_Type *base)
{
/* 1、配置I2C */
base->I2CR &= ~(1 << 7); /* 要訪問I2C的寄存器,首先需要先關(guān)閉I2C */
/* 設(shè)置波特率為100K
* I2C的時(shí)鐘源來源于IPG_CLK_ROOT=66Mhz
* IC2 時(shí)鐘 = PERCLK_ROOT/dividison(IFDR寄存器)
* 設(shè)置寄存器IFDR,IFDR寄存器參考IMX6UL參考手冊P1260頁,表29-3,
* 根據(jù)表29-3里面的值,挑選出一個(gè)還是的分頻數(shù),比如本例程我們
* 設(shè)置I2C的波特率為100K, 因此當(dāng)分頻值=66000000/100000=660.
* 在表29-3里面查找,沒有660這個(gè)值,但是有640,因此就用640,
* 即寄存器IFDR的IC位設(shè)置為0X15
*/
base->IFDR = 0X15 << 0;
/*
* 設(shè)置寄存器I2CR,開啟I2C
* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必須最先置1
*/
base->I2CR |= (1<<7);
}
/*
* @description : 發(fā)送重新開始信號(hào)
* @param - base : 要使用的IIC
* @param - addrss : 設(shè)備地址
* @param - direction : 方向
* @return : 0 正常 其他值 出錯(cuò)
*/
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;
/*
* 設(shè)置寄存器I2CR
* bit[4]: 1 發(fā)送
* bit[2]: 1 產(chǎn)生重新開始信號(hào)
*/
base->I2CR |= (1 << 4) | (1 << 2);
/*
* 設(shè)置寄存器I2DR
* bit[7:0] : 要發(fā)送的數(shù)據(jù),這里寫入從設(shè)備地址
* 參考資料:IMX6UL參考手冊P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
/*
* @description : 發(fā)送開始信號(hào)
* @param - base : 要使用的IIC
* @param - addrss : 設(shè)備地址
* @param - direction : 方向
* @return : 0 正常 其他值 出錯(cuò)
*/
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{
if(base->I2SR & (1 << 5)) /* I2C忙 */
return 1;
/*
* 設(shè)置寄存器I2CR
* bit[5]: 1 主模式
* bit[4]: 1 發(fā)送
*/
base->I2CR |= (1 << 5) | (1 << 4);
/*
* 設(shè)置寄存器I2DR
* bit[7:0] : 要發(fā)送的數(shù)據(jù),這里寫入從設(shè)備地址
* 參考資料:IMX6UL參考手冊P1249
*/
base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);
return 0;
}
/*
* @description : 檢查并清除錯(cuò)誤
* @param - base : 要使用的IIC
* @param - status : 狀態(tài)
* @return : 狀態(tài)結(jié)果
*/
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status)
{
/* 檢查是否發(fā)生仲裁丟失錯(cuò)誤 */
if(status & (1<<4))
{
base->I2SR &= ~(1<<4); /* 清除仲裁丟失錯(cuò)誤位 */
base->I2CR &= ~(1 << 7); /* 先關(guān)閉I2C */
base->I2CR |= (1 << 7); /* 重新打開I2C */
return I2C_STATUS_ARBITRATIONLOST;
}
else if(status & (1 << 0)) /* 沒有接收到從機(jī)的應(yīng)答信號(hào) */
{
return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */
}
return I2C_STATUS_OK;
}
/*
* @description : 停止信號(hào)
* @param - base : 要使用的IIC
* @param : 無
* @return : 狀態(tài)結(jié)果
*/
unsigned char i2c_master_stop(I2C_Type *base)
{
unsigned short timeout = 0xffff;
/*
* 清除I2CR的bit[5:3]這三位
*/
base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));
/* 等待忙結(jié)束 */
while((base->I2SR & (1 << 5)))
{
timeout--;
if(timeout == 0) /* 超時(shí)跳出 */
return I2C_STATUS_TIMEOUT;
}
return I2C_STATUS_OK;
}
/*
* @description : 發(fā)送數(shù)據(jù)
* @param - base : 要使用的IIC
* @param - buf : 要發(fā)送的數(shù)據(jù)
* @param - size : 要發(fā)送的數(shù)據(jù)大小
* @param - flags : 標(biāo)志
* @return : 無
*/
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size)
{
/* 等待傳輸完成 */
while(!(base->I2SR & (1 << 7)));
base->I2SR &= ~(1 << 1); /* 清除標(biāo)志位 */
base->I2CR |= 1 << 4; /* 發(fā)送數(shù)據(jù) */
while(size--)
{
base->I2DR = *buf++; /* 將buf中的數(shù)據(jù)寫入到I2DR寄存器 */
while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 */
base->I2SR &= ~(1 << 1); /* 清除標(biāo)志位 */
/* 檢查ACK */
if(i2c_check_and_clear_error(base, base->I2SR))
break;
}
base->I2SR &= ~(1 << 1);
i2c_master_stop(base); /* 發(fā)送停止信號(hào) */
}
/*
* @description : 讀取數(shù)據(jù)
* @param - base : 要使用的IIC
* @param - buf : 讀取到數(shù)據(jù)
* @param - size : 要讀取的數(shù)據(jù)大小
* @return : 無
*/
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size)
{
volatile uint8_t dummy = 0;
dummy++; /* 防止編譯報(bào)錯(cuò) */
/* 等待傳輸完成 */
while(!(base->I2SR & (1 << 7)));
base->I2SR &= ~(1 << 1); /* 清除中斷掛起位 */
base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收數(shù)據(jù) */
/* 如果只接收一個(gè)字節(jié)數(shù)據(jù)的話發(fā)送NACK信號(hào) */
if(size == 1)
base->I2CR |= (1 << 3);
dummy = base->I2DR; /* 假讀 */
while(size--)
{
while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 */
base->I2SR &= ~(1 << 1); /* 清除標(biāo)志位 */
if(size == 0)
{
i2c_master_stop(base); /* 發(fā)送停止信號(hào) */
}
if(size == 1)
{
base->I2CR |= (1 << 3);
}
*buf++ = base->I2DR;
}
}
/*
* @description : I2C數(shù)據(jù)傳輸,包括讀和寫
* @param - base: 要使用的IIC
* @param - xfer: 傳輸結(jié)構(gòu)體
* @return : 傳輸結(jié)果,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)); /* 清除標(biāo)志位 */
/* 等待傳輸完成 */
while(!((base->I2SR >> 7) & 0X1)){};
/* 如果是讀的話,要先發(fā)送寄存器地址,所以要先將方向改為寫 */
if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read))
{
direction = kI2C_Write;
}
ret = i2c_master_start(base, xfer->slaveAddress, direction); /* 發(fā)送開始信號(hào) */
if(ret)
{
return ret;
}
while(!(base->I2SR & (1 << 1))){}; /* 等待傳輸完成 */
ret = i2c_check_and_clear_error(base, base->I2SR); /* 檢查是否出現(xiàn)傳輸錯(cuò)誤 */
if(ret)
{
i2c_master_stop(base); /* 發(fā)送出錯(cuò),發(fā)送停止信號(hào) */
return ret;
}
/* 發(fā)送寄存器地址 */
if(xfer->subaddressSize)
{
do
{
base->I2SR &= ~(1 << 1); /* 清除標(biāo)志位 */
xfer->subaddressSize--; /* 地址長度減一 */
base->I2DR = ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器寫入子地址
while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 */
/* 檢查是否有錯(cuò)誤發(fā)生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
i2c_master_stop(base); /* 發(fā)送停止信號(hào) */
return ret;
}
} while ((xfer->subaddressSize > 0) && (ret == I2C_STATUS_OK));
if(xfer->direction == kI2C_Read) /* 讀取數(shù)據(jù) */
{
base->I2SR &= ~(1 << 1); /* 清除中斷掛起位 */
i2c_master_repeated_start(base, xfer->slaveAddress, kI2C_Read); /* 發(fā)送重復(fù)開始信號(hào)和從機(jī)地址 */
while(!(base->I2SR & (1 << 1))){};/* 等待傳輸完成 */
/* 檢查是否有錯(cuò)誤發(fā)生 */
ret = i2c_check_and_clear_error(base, base->I2SR);
if(ret)
{
ret = I2C_STATUS_ADDRNAK;
i2c_master_stop(base); /* 發(fā)送停止信號(hào) */
return ret;
}
}
}
/* 發(fā)送數(shù)據(jù) */
if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0))
{
i2c_master_write(base, xfer->data, xfer->dataSize);
}
/* 讀取數(shù)據(jù) */
if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0))
{
i2c_master_read(base, xfer->data, xfer->dataSize);
}
return 0;
}
這就是驅(qū)動(dòng)的分離和分層思想,主機(jī)驅(qū)動(dòng)具有通用性,對(duì)于我們只要篇編寫設(shè)備驅(qū)動(dòng)就行,這里主要就是傳感器FT5426的編寫
設(shè)備驅(qū)動(dòng)bsp_ft5xx5.h
此文件里面描寫都是設(shè)備的信息,和寄存器打交道的讀寫函數(shù)
#ifndef _FT5XX6_H
#define _FT5XX6_H
#include "imx6ul.h"
#include "bsp_gpio.h"
/* 宏定義 */
#define FT5426_ADDR 0X38 /* FT5426設(shè)備地址 */
#define FT5426_DEVICE_MODE 0X00 /* 模式寄存器 */
#define FT5426_IDGLIB_VERSION 0XA1 /* 固件版本寄存器 */
#define FT5426_IDG_MODE 0XA4 /* 中斷模式 */
#define FT5426_TD_STATUS 0X02 /* 觸摸狀態(tài)寄存器 */
#define FT5426_TOUCH1_XH 0X03 /* 觸摸點(diǎn)坐標(biāo)寄存器,
* 一個(gè)觸摸點(diǎn)用4個(gè)寄存器存儲(chǔ)坐標(biāo)數(shù)據(jù)*/
#define FT5426_XYCOORDREG_NUM 30 /* 觸摸點(diǎn)坐標(biāo)寄存器數(shù)量 */
#define FT5426_INIT_FINISHED 1 /* 觸摸屏初始化完成 */
#define FT5426_INIT_NOTFINISHED 0 /* 觸摸屏初始化未完成 */
#define FT5426_TOUCH_EVENT_DOWN 0x00 /* 按下 */
#define FT5426_TOUCH_EVENT_UP 0x01 /* 釋放 */
#define FT5426_TOUCH_EVENT_ON 0x02 /* 接觸 */
#define FT5426_TOUCH_EVENT_RESERVED 0x03 /* 沒有事件 */
/* 觸摸屏結(jié)構(gòu)體 */
struct ft5426_dev_struc
{
unsigned char initfalg; /* 觸摸屏初始化狀態(tài) */
unsigned char intflag; /* 標(biāo)記中斷有沒有發(fā)生 */
unsigned char point_num; /* 觸摸點(diǎn) */
unsigned short x[5]; /* X軸坐標(biāo) */
unsigned short y[5]; /* Y軸坐標(biāo) */
};
extern struct ft5426_dev_struc ft5426_dev;
/* 函數(shù)聲明 */
void ft5426_init(void);
void gpio1_io9_irqhandler(void);
unsigned char ft5426_write_byte(unsigned char addr,unsigned char reg, unsigned char data);
unsigned char ft5426_read_byte(unsigned char addr,unsigned char reg);
void ft5426_read_len(unsigned char addr,unsigned char reg,unsigned char len,unsigned char *buf);
void ft5426_read_tpnum(void);
void ft5426_read_tpcoord(void);
#endif
設(shè)備驅(qū)動(dòng)bsp_ft5xx5.c
這里用的是I2C2
#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 : 初始化觸摸屏,其實(shí)就是初始化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++ )
{ /* 避免編譯器自動(dòng)賦值 */
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關(guān)閉
*bit [15:14]: 1 默認(rèn)47K上拉
*bit [13]: 1 pull功能
*bit [12]: 1 pull/keeper使能
*bit [11]: 0 關(guān)閉開路輸出
*bit [7:6]: 10 速度100Mhz
*bit [5:3]: 110 驅(qū)動(dòng)能力為R0/6
*bit [0]: 1 高轉(zhuǎn)換率
*/
IOMUXC_SetPinConfig(IOMUXC_UART5_TX_DATA_I2C2_SCL,0x70B0);
IOMUXC_SetPinConfig(IOMUXC_UART5_RX_DATA_I2C2_SDA,0X70B0);
/* 2、初始化觸摸屏中斷IO和復(fù)位IO */
gpio_pin_config_t ctintpin_config;
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO09_GPIO1_IO09,0); /* 復(fù)用為GPIO1_IO9 */
IOMUXC_SetPinMux(IOMUXC_SNVS_SNVS_TAMPER9_GPIO5_IO09,0);/* 復(fù)用為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中對(duì)應(yīng)的中斷 */
system_register_irqhandler(GPIO1_Combined_0_15_IRQn, (system_irq_handler_t)gpio1_io9_irqhandler, NULL); /* 注冊中斷服務(wù)函數(shù) */
gpio_enableint(GPIO1, 9); /* 使能GPIO1_IO18的中斷功能 */
/* 復(fù)位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); /* 復(fù)位FT5426 */
delayms(20);
gpio_pinwrite(GPIO5, 9, 1); /* 停止復(fù)位FT5426 */
delayms(20);
ft5426_write_byte(FT5426_ADDR, FT5426_DEVICE_MODE, 0); /* 進(jìn)入正常模式 */
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; /* 標(biāo)記FT5426初始化完成 */
ft5426_dev.intflag = 0;
}
/*
* @description : GPIO1_IO9最終的中斷處理函數(shù)
* @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); /* 清除中斷標(biāo)志位 */
}
/*
* @description : 向FT5429寫入數(shù)據(jù)
* @param - addr: 設(shè)備地址
* @param - reg : 要寫入的寄存器
* @param - data: 要寫入的數(shù)據(jù)
* @return : 操作結(jié)果
*/
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結(jié)構(gòu)體 */
masterXfer.slaveAddress = addr; /* 設(shè)備地址 */
masterXfer.direction = kI2C_Write; /* 寫入數(shù)據(jù) */
masterXfer.subaddress = reg; /* 要寫入的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址長度一個(gè)字節(jié) */
masterXfer.data = &writedata; /* 要寫入的數(shù)據(jù) */
masterXfer.dataSize = 1; /* 寫入數(shù)據(jù)長度1個(gè)字節(jié) */
if(i2c_master_transfer(I2C2, &masterXfer))
status=1;
return status;
}
/*
* @description : 從FT5426讀取一個(gè)字節(jié)的數(shù)據(jù)
* @param - addr: 設(shè)備地址
* @param - reg : 要讀取的寄存器
* @return : 讀取到的數(shù)據(jù)。
*/
unsigned char ft5426_read_byte(unsigned char addr,unsigned char reg)
{
unsigned char val=0;
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr; /* 設(shè)備地址 */
masterXfer.direction = kI2C_Read; /* 讀取數(shù)據(jù) */
masterXfer.subaddress = reg; /* 要讀取的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址長度一個(gè)字節(jié) */
masterXfer.data = &val; /* 接收數(shù)據(jù)緩沖區(qū) */
masterXfer.dataSize = 1; /* 讀取數(shù)據(jù)長度1個(gè)字節(jié) */
i2c_master_transfer(I2C2, &masterXfer);
return val;
}
/*
* @description : 從FT5429讀取多個(gè)字節(jié)的數(shù)據(jù)
* @param - addr: 設(shè)備地址
* @param - reg : 要讀取的開始寄存器地址
* @param - len : 要讀取的數(shù)據(jù)長度.
* @param - buf : 讀取到的數(shù)據(jù)緩沖區(qū)
* @return : 無
*/
void ft5426_read_len(unsigned char addr,unsigned char reg,unsigned char len,unsigned char *buf)
{
struct i2c_transfer masterXfer;
masterXfer.slaveAddress = addr; /* 設(shè)備地址 */
masterXfer.direction = kI2C_Read; /* 讀取數(shù)據(jù) */
masterXfer.subaddress = reg; /* 要讀取的寄存器地址 */
masterXfer.subaddressSize = 1; /* 地址長度一個(gè)字節(jié) */
masterXfer.data = buf; /* 接收數(shù)據(jù)緩沖區(qū) */
masterXfer.dataSize = len; /* 讀取數(shù)據(jù)長度1個(gè)字節(jié) */
i2c_master_transfer(I2C2, &masterXfer);
}
/*
* @description : 讀取當(dāng)前觸摸點(diǎn)個(gè)數(shù)
* @param : 無
* @return : 無
*/
void ft5426_read_tpnum(void)
{
ft5426_dev.point_num = ft5426_read_byte(FT5426_ADDR, FT5426_TD_STATUS);
}
/*
* @description : 讀取當(dāng)前所有觸摸點(diǎn)的坐標(biāo)
* @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開始,連續(xù)讀取30個(gè)寄存器的值,這30個(gè)寄存器
* 保存著5個(gè)點(diǎn)的觸摸值,每個(gè)點(diǎn)占用6個(gè)寄存器。
*/
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];
/* 以第一個(gè)觸摸點(diǎn)為例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
* bit7:6 Event flag 0:按下 1:釋放 2:接觸 3:沒有事件
* bit5:4 保留
* bit3:0 X軸觸摸點(diǎn)的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; /* 獲取觸摸類型 */
/* 以第一個(gè)觸摸點(diǎn)為例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
* bit7:4 Touch ID 觸摸ID,表示是哪個(gè)觸摸點(diǎn)
* bit3:0 Y軸觸摸點(diǎn)的11~8位。
*/
//id = (buf[2] >> 4) & 0x0f;
if(type == FT5426_TOUCH_EVENT_DOWN || type == FT5426_TOUCH_EVENT_ON )/* 按下 */
{
}
else { /* 釋放 */
}
}
}
linux驅(qū)動(dòng)
1.介紹
- 看到這里,我們可以得出電容觸摸屏驅(qū)動(dòng)其實(shí)大框架就是 IIC設(shè)備驅(qū)動(dòng)
- 在此基礎(chǔ)上,介紹了觸摸屏的四個(gè)引腳,除去IIC的SCL和SDA,還有INT引腳和RST引腳.
- INT(中斷引腳))向linux內(nèi)核上報(bào)觸摸信息,因此需要用到linux中斷驅(qū)動(dòng)框架。坐標(biāo)的上報(bào)在中斷服務(wù)函數(shù)中完成。
- 觸摸屏的坐標(biāo)信息、屏幕按下和抬起信息都屬于 linux的 input子系統(tǒng),因此向 linux內(nèi)核上報(bào)觸摸屏坐標(biāo)信息就得使用 input子系統(tǒng)。這篇文章介紹了input子系統(tǒng)簡述
- 在input系統(tǒng)框架上我們知道(按鍵、鼠標(biāo)、鍵盤、觸摸屏等都屬于輸入設(shè)備,linux內(nèi)核為此專門做了一個(gè)叫做input子系統(tǒng)的框架來處理輸入事件。輸入設(shè)備本質(zhì)上還是字符設(shè)備,只是在此基礎(chǔ)上套上了input框架,用戶只需要負(fù)責(zé)上報(bào)輸入事件,比如按鍵值、坐標(biāo)等信息)但是對(duì)于多點(diǎn)觸摸的上報(bào)事件我們需要引入input 子系統(tǒng)下的多點(diǎn)電容觸摸協(xié)議(MT)
1.MT協(xié)議。 MT 協(xié)議被分為兩種類型, Type A和TypeB。
1)Type A:適用于觸摸點(diǎn)不能被區(qū)分或者追蹤,此類型的設(shè)備上報(bào)原始數(shù)據(jù)
2)適用于有硬件追蹤并能區(qū)分觸摸點(diǎn)的觸摸設(shè)備,此類型設(shè)備通過 slot更新某一個(gè)觸摸點(diǎn)的信息,F(xiàn)T5426就屬于此類型,一般的多點(diǎn)電容觸摸屏 IC都有此能力。
2.我們這里使用TypeB。我們用MT協(xié)議的根本目的就是為了上報(bào)事件給內(nèi)核(這里適用于多點(diǎn)的觸摸的ABS_MT事件)
/*
@ ABS_MT 事件
*/
#define ABS_MT_SLOT 0x2f /* MT slot being modified ==================上 報(bào) 觸 摸 點(diǎn) ID */
#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */
#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */
#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse
#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */
#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */
#define ABS_MT_POSITION_X 0x35 /* Center X touch position ===========================*/
#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position ============================*/
#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */
#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */
#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact ==對(duì)于 Type B 類 型 的 設(shè) 備 ==來區(qū)分觸摸點(diǎn)*/
#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */
#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */
#define ABS_MT_TOOL_X 0x3c /* Center X tool position */
#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */
/*
@ 對(duì)于Type A 類型的設(shè)備,函數(shù)來隔離不同的觸摸點(diǎn)數(shù)據(jù)信息
@ dev:用于指定具體的 input_dev設(shè)備
@ 功能: 函數(shù)會(huì)觸發(fā) SYN_MT_REPORT 事件,此事件會(huì)通知接收者獲取當(dāng)前觸摸數(shù)據(jù),并且準(zhǔn)備接收下一個(gè)觸摸點(diǎn)數(shù)據(jù)
*/
void input_mt_sync(struct input_dev *dev)
/*
@ 對(duì)于Type B 類型的設(shè)備,上報(bào)觸摸點(diǎn)信息的時(shí)候需要通過 input_mt_slot()函數(shù)區(qū)分是哪一個(gè)觸摸點(diǎn)
@ dev: input_dev設(shè)備
@ slot: 指定當(dāng)前上報(bào)的是哪個(gè)觸摸點(diǎn)信息
@ 功能: 函數(shù)會(huì)觸發(fā) ABS_MT_SLOT 事件,此事件會(huì)告訴接收者當(dāng)前正在更新的是哪個(gè)觸摸點(diǎn)(slot)的數(shù)據(jù)
*/
void input_mt_slot(struct input_dev *dev, int slot)
3.原理:Type B 設(shè)備驅(qū)動(dòng)需要給每個(gè)識(shí)別出來的觸摸點(diǎn)分配一個(gè) slot,后面使用這個(gè) slot 來上報(bào)觸摸點(diǎn)信息。(有點(diǎn)像信號(hào)和槽機(jī)制)。但是你既然多點(diǎn)觸摸,肯定每個(gè)點(diǎn)都有個(gè)先后順序,下面針對(duì)TypeB的來介紹
/*
@ Type B 觸摸點(diǎn)數(shù)據(jù)上報(bào)時(shí)序
*/
ABS_MT_SLOT 0 /*上報(bào)ABS_MT_SLOT事件,也就是觸摸點(diǎn)對(duì)應(yīng)的 SLOT。每次上報(bào)一個(gè)觸摸點(diǎn)坐標(biāo)之前要先使用input_mt_slot函數(shù)上報(bào)當(dāng)前觸摸點(diǎn)SLOT,觸摸點(diǎn)的SLOT其實(shí)就是觸摸點(diǎn)ID,需要由觸摸IC提供*/
ABS_MT_TRACKING_ID 45 /*根據(jù) Type B 的要求,每個(gè) SLOT 必須關(guān)聯(lián)一個(gè)ABS_MT_TRACKING_ID,通過修改 SLOT 關(guān)聯(lián)的 ABS_MT_TRACKING_ID 來完成對(duì)觸摸點(diǎn)的添加、替換或刪除。具體用到的函數(shù)就是input_mt_report_slot_state,如果是添加一個(gè)新的觸摸點(diǎn),那么此函數(shù)的第三個(gè)參數(shù)active要設(shè)置為true,linux內(nèi)核會(huì)自動(dòng)分配一個(gè) ABS_MT_TRACKING_ID 值,不需要用戶去指定具體的ABS_MT_TRACKING_ID值*/
ABS_MT_POSITION_X x[0] /*上報(bào)觸摸點(diǎn) 0的X 軸坐標(biāo),使用函數(shù) input_report_abs來完成*/
ABS_MT_POSITION_Y y[0] /*上報(bào)觸摸點(diǎn) 0的Y 軸坐標(biāo),使用函數(shù) input_report_abs來完成*/
ABS_MT_SLOT 1
ABS_MT_TRACKING_ID 46
ABS_MT_POSITION_X x[1]
ABS_MT_POSITION_Y y[1]
SYN_REPORT /*當(dāng)所有的觸摸點(diǎn)坐標(biāo)都上傳完畢以后就得發(fā)送SYN_REPORT事件,使用input_sync函數(shù)來完成*/
當(dāng)一個(gè)觸摸點(diǎn)移除以后,同樣需要通過 SLOT 關(guān)聯(lián)的ABS_MT_TRACKING_ID 來處理
/*
@ Type B 觸摸點(diǎn)移除時(shí)序
*/
ABS_MT_TRACKING_ID -1 /*當(dāng)一個(gè)觸摸點(diǎn)(SLOT)移除以后,需要通過 ABS_MT_TRACKING_ID 事件發(fā)送一個(gè)-1給內(nèi)核。方法很簡單,同樣使用 input_mt_report_slot_state函數(shù)來完成,只需要將此函數(shù)的第三個(gè)參數(shù)active設(shè)置為 false即可,不需要用戶手動(dòng)去設(shè)置-1。 */
SYN_REPORT /*當(dāng)所有的觸摸點(diǎn)坐標(biāo)都上傳完畢以后就得發(fā)送 SYN_REPORT事件。 */
4.總結(jié)一下, MT協(xié)議隸屬于linux的 input 子系統(tǒng),驅(qū)動(dòng)通過大量的 ABS_MT 事件向 linux 內(nèi)核上報(bào)多點(diǎn)觸摸坐標(biāo)數(shù)據(jù)。根據(jù)觸摸 IC的不同,分為Type A和Type B兩種類型,不同的類型其上報(bào)時(shí)序不同,目前使用最多的是 Type B 類型
5.linux 下的多點(diǎn)觸摸協(xié)議其實(shí)就是通過不同的事件來上報(bào)觸摸
點(diǎn)坐標(biāo)信息,這些事件都是通過 Linux 內(nèi)核提供的對(duì)應(yīng) API 函數(shù)實(shí)現(xiàn)的
/*
@ input_mt_init_slots函數(shù)用于初始化MT的輸入slots
@ dev: MT 設(shè)備對(duì)應(yīng)的 input_dev,因?yàn)镸T設(shè)備隸屬于input_dev。
@ num_slots:設(shè)備要使用的SLOT數(shù)量,也就是觸摸點(diǎn)的數(shù)量
@ flags:其他一些 flags信息,可以采用‘|’運(yùn)算來同時(shí)設(shè)置多個(gè) flags標(biāo)識(shí)
@ 返回值:0,成功;負(fù)值,失敗
*/
int input_mt_init_slots( struct input_dev *dev, unsigned int num_slots, unsigned int flags)
/*
@ 定義在文件include/linux/input/mt.h中
@ 此函數(shù)用于Type B 類型,此函數(shù)用于產(chǎn)生 ABS_MT_SLOT 事件,告訴內(nèi)核當(dāng)前上報(bào)的是哪個(gè)觸摸點(diǎn)的坐標(biāo)數(shù)據(jù)
@ dev: MT 設(shè)備對(duì)應(yīng)的 input_dev
@ slot:當(dāng)前發(fā)送的是哪個(gè) slot的坐標(biāo)信息,也就是哪個(gè)觸摸點(diǎn)
@ 返回值:無。
*/
void input_mt_slot(struct input_dev *dev, int slot)
/*
@ 定義在文件drivers/input/input-mt.c 中
@ 此函數(shù)用于 Type B 類型,用于產(chǎn)生 ABS_MT_TRACKING_ID 和 ABS_MT_TOOL_TYPE事件
@ dev: MT 設(shè)備對(duì)應(yīng)的 input_dev
@ tool_type:觸摸類型,可以選擇 MT_TOOL_FINGER(手指)MT_TOOL_PEN(筆)或MT_TOOL_PALM(手掌)
@ active: true,連續(xù)觸摸, input子系統(tǒng)內(nèi)核會(huì)自動(dòng)分配一個(gè)ABS_MT_TRACKING_ID給slot。false,觸摸點(diǎn)抬起,表示某個(gè)觸摸點(diǎn)無效了,input 子系統(tǒng)內(nèi)核會(huì)分配一個(gè)-1 給 slot,表示觸摸點(diǎn)溢出。
@ 返回值:無。
*/
void input_mt_report_slot_state( struct input_dev *dev, unsigned int tool_type,bool active)
/*
@ 定義在 文件include/linux/input.h中
@ Type A和Type B 類型都使用此函數(shù)上報(bào)觸摸點(diǎn)坐標(biāo)信息
@ dev: MT 設(shè)備對(duì)應(yīng)的 input_dev。
@ code:要上報(bào)的是什么數(shù)據(jù),可以設(shè)置為ABS_MT_POSITION_X或ABS_MT_POSITION_Y,也就是X 軸或者Y軸坐標(biāo)數(shù)據(jù)
@ value:具體的X 軸或Y軸坐標(biāo)數(shù)據(jù)值
*/
void input_report_abs( struct input_dev *dev,unsigned int code, int value)
/*
@ 定義在文件drivers/input/input-mt.c中
@ 函數(shù)會(huì)獲取到具體的觸摸點(diǎn)數(shù)量,不需要用戶給出
@ dev: MT 設(shè)備對(duì)應(yīng)的 input_dev
@ use_count:true,有效的觸摸點(diǎn)數(shù)量;false,追蹤到的觸摸點(diǎn)數(shù)量多于當(dāng)前上報(bào)的數(shù)量
@ 返回值:無
*/
void input_mt_report_pointer_emulation(struct input_dev *dev, bool use_count)
面的那么多都是整個(gè)框架中所涉及到的未知知識(shí)
多點(diǎn)電容觸摸驅(qū)動(dòng)的編寫框架用到的知識(shí)點(diǎn)和框架:
1.多點(diǎn)電容觸摸芯片的接口,一般都為I2C接口,因此驅(qū)動(dòng)主框架肯定是 I2C。
2.linux里面一般都是通過中斷來上報(bào)觸摸點(diǎn)坐標(biāo)信息,因此需要用到中斷框架
3.多點(diǎn)電容觸摸屬于 input子系統(tǒng),因此還要用到 input子系統(tǒng)框架。
4.在中斷處理程序中按照 linux的MT協(xié)議上報(bào)坐標(biāo)信息(此協(xié)議是多點(diǎn)觸摸專用)
多點(diǎn)電容觸摸驅(qū)動(dòng)編寫框架以及步驟如下:
- 1.I2C驅(qū)動(dòng)框架(中間的probe函數(shù)里面初始化觸摸 IC,中斷和input子系統(tǒng))
當(dāng)設(shè)備樹中觸摸 IC的設(shè)備節(jié)點(diǎn)和驅(qū)動(dòng)匹配以后probe函數(shù)會(huì)執(zhí)行
/* 設(shè)備樹匹配表 */
static const struct i2c_device_id xxx_ts_id[] = {
{ "xxx", 0, },
{ /* sentinel */ }
};
/* 設(shè)備樹匹配表 */
static const struct of_device_id xxx_of_match[] = {
{ .compatible = "xxx", },
{ /* sentinel */ }
};
/* i2c 驅(qū)動(dòng)結(jié)構(gòu)體 */
static struct i2c_driver ft5x06_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "edt_ft5x06",
.of_match_table = of_match_ptr(xxx_of_match),
},
.id_table = xxx_ts_id,
.probe = xxx_ts_probe,
.remove = xxx_ts_remove,
};
/*
* @description : 驅(qū)動(dòng)入口函數(shù)
* @param : 無
* @return : 無
*/
static int __init xxx_init(void)
{
int ret = 0;
ret = i2c_add_driver(&xxx_ts_driver);
return ret;
}
/*
* @description : 驅(qū)動(dòng)出口函數(shù)
* @param : 無
* @return : 無
*/
static void __exit xxx_exit(void)
{
i2c_del_driver(&ft5x06_ts_driver);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
- 2.初始化觸摸IC、中斷和 input子系統(tǒng)
static int xxx_ts_probe(struct i2c_client *client, const struct
i2c_device_id *id)
{
struct input_dev *input;
/* 1、初始化 I2C */
......
/*包括芯片的相關(guān) IO,比如復(fù)位、中斷等 IO 引腳,然后就是芯片本身的初始化,也就是配置觸摸芯片的相關(guān)寄存器*/
/* 2,申請中斷, */
devm_request_threaded_irq(&client->dev, client->irq, NULL,
xxx_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, &xxx);
......
/*因?yàn)橐话阌|摸芯片都是通過中斷來向系統(tǒng)上報(bào)觸摸點(diǎn)坐標(biāo)信息的,因此我們需要初始化中斷*/
/* 3,input 設(shè)備申請與初始化 */
input = devm_input_allocate_device(&client->dev);
input->name = client->name;
input->id.bustype = BUS_I2C;
input->dev.parent = &client->dev;
......
/*因?yàn)槎帱c(diǎn)電容觸摸屬于 input子系統(tǒng),申請到 input_dev以后還需要對(duì)其進(jìn)行初始化操作。*/
/* 4,初始化 input 和 MT */
__set_bit(EV_ABS, input->evbit);
__set_bit(BTN_TOUCH, input->keybit);
input_set_abs_params(input, ABS_X, 0, width, 0, 0);
input_set_abs_params(input, ABS_Y, 0, height, 0, 0);
input_set_abs_params(input, ABS_MT_POSITION_X,0, width, 0, 0)
input_set_abs_params(input, ABS_MT_POSITION_Y,0, height, 0, 0
input_mt_init_slots(input, MAX_SUPPORT_POINTS, 0); /*初始化多點(diǎn)電容觸摸的 slots*/
......
/* 5,注冊 input_dev */
input_register_device(input); /*系統(tǒng)注冊前面申請到的input_dev。*/
......
}
- 3.上報(bào)坐標(biāo)信息(Type B類型)
最后就是在中斷服務(wù)程序中上報(bào)讀取到的坐標(biāo)信息
static irqreturn_t xxx_handler(int irq, void *dev_id)
{
int num; /* 觸摸點(diǎn)數(shù)量 */
int x[n], y[n]; /* 保存坐標(biāo)值 */
/* 1、從觸摸芯片獲取各個(gè)觸摸點(diǎn)坐標(biāo)值 */
......
/* 2、上報(bào)每一個(gè)觸摸點(diǎn)坐標(biāo) */
for (i = 0; i < num; i++) {
input_mt_slot(input, id);
input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
input_report_abs(input, ABS_MT_POSITION_X, x[i]);
input_report_abs(input, ABS_MT_POSITION_Y, y[i]);
}
......
input_sync(input);
......
return IRQ_HANDLED;
}
2. 實(shí)例應(yīng)用
1.修改設(shè)備樹
修改IO
4個(gè) IO,一個(gè)復(fù)位 IO、一個(gè)中斷IO、I2C2的 SCL 和SDA。復(fù)位 IO 和中斷 IO是普通的GPIO,因此這兩個(gè)IO可以放到同一個(gè)節(jié)點(diǎn)下去描述,I2C2的SCL和 SDA 屬于 I2C2,因此這兩個(gè)要放到同一個(gè)節(jié)點(diǎn)下去描述
- 首先是復(fù)位 IO 和中斷 IO,imx6ull-alientek-emmc.dts 文件里面默認(rèn)有個(gè)名為“pinctrl_tsc”的節(jié)點(diǎn),如果被刪除了的話就自行創(chuàng)建,在此節(jié)點(diǎn)下添加觸摸屏的中斷引腳信息
/*
@
*/
pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO09__GPIO1_IO09 0xF080 /* TSC_INT */
>;
};
觸摸屏復(fù)位引腳使用的是 SNVS_TAMPER9,因此復(fù)位引腳信息要添加到 iomuxc_snvs 節(jié)點(diǎn)下,在 iomuxc_snvs 節(jié)點(diǎn)新建一個(gè)名為 pinctrl_tsc_reset 的子節(jié)點(diǎn)
pinctrl_tsc_reset: tsc_reset {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER9__GPIO5_IO09 0x10B0
>;
};
- 繼續(xù)添加 I2C2 的 SCL 和 SDA 這兩個(gè) IO 信息.x6ull-alientek-emmc.dts 里面默認(rèn)就已經(jīng)添加了 I2C2 的 IO 信息,這是 NXP 官方添加的,所以不需要我們?nèi)バ薷?
pinctrl_i2c2: i2c2grp {
fsl,pins = <
MX6UL_PAD_UART5_TX_DATA__I2C2_SCL 0x4001b8b0
MX6UL_PAD_UART5_RX_DATA__I2C2_SDA 0x4001b8b0
>;
};
注意:要防止引腳沖突
添加設(shè)備節(jié)點(diǎn)
需要向 I2C2 節(jié)點(diǎn)下添加一個(gè)子節(jié)點(diǎn),此子節(jié)點(diǎn)用于描述 FT5426,添加完成以后的 I2C2 節(jié)點(diǎn)
&i2c2 {
clock_frequency = <100000>;
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_i2c2>;
status = "okay";
/* zuozhongkai FT5406/FT5426 */
ft5426: ft5426@38 {
compatible = "edt,edt-ft5426";
reg = <0x38>; /*器件地址為 0x38*/
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc /*復(fù)位 IO 和中斷 IO 所使用的節(jié)點(diǎn)為 pinctrl_tsc和 pinctrl_tsc_reset*/
&pinctrl_tsc_reset >;
interrupt-parent = <&gpio1>;
interrupts = <9 0>;
reset-gpios = <&gpio5 9 GPIO_ACTIVE_LOW>;/*復(fù)位IO 對(duì)應(yīng)的GPIO為GPIO5_IO09*/
interrupt-gpios = <&gpio1 9 GPIO_ACTIVE_LOW>; /*中斷IO對(duì)應(yīng)的GPIO為 GPIO1_IO09。*/
};
};
2.編寫設(shè)備驅(qū)動(dòng)
總線驅(qū)動(dòng)官方已經(jīng)寫好過了,我們只需要寫設(shè)備驅(qū)動(dòng)。
#include <linux/module.h>
#include <linux/ratelimit.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/i2c.h>
#include <linux/uaccess.h>
#include <linux/delay.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/input/mt.h>
#include <linux/input/touchscreen.h>
#include <linux/input/edt-ft5x06.h>
#include <linux/i2c.h>
#define MAX_SUPPORT_POINTS 5 /* 5點(diǎn)觸摸 */
#define TOUCH_EVENT_DOWN 0x00 /* 按下 */
#define TOUCH_EVENT_UP 0x01 /* 抬起 */
#define TOUCH_EVENT_ON 0x02 /* 接觸 */
#define TOUCH_EVENT_RESERVED 0x03 /* 保留 */
/* FT5X06寄存器相關(guān)宏定義 */
#define FT5X06_TD_STATUS_REG 0X02 /* 狀態(tài)寄存器地址 */
#define FT5x06_DEVICE_MODE_REG 0X00 /* 模式寄存器 */
#define FT5426_IDG_MODE_REG 0XA4 /* 中斷模式 */
#define FT5X06_READLEN 29 /* 要讀取的寄存器個(gè)數(shù) */
struct ft5x06_dev {
struct device_node *nd; /* 設(shè)備節(jié)點(diǎn) */
int irq_pin,reset_pin; /* 中斷和復(fù)位IO */
int irqnum; /* 中斷號(hào) */
void *private_data; /* 私有數(shù)據(jù) */
struct input_dev *input; /* input結(jié)構(gòu)體 */
struct i2c_client *client; /* I2C客戶端 */
};
static struct ft5x06_dev ft5x06;
/*
* @description : 復(fù)位FT5X06
* @param - client : 要操作的i2c
* @param - multidev: 自定義的multitouch設(shè)備
* @return : 0,成功;其他負(fù)值,失敗
*/
static int ft5x06_ts_reset(struct i2c_client *client, struct ft5x06_dev *dev)
{
int ret = 0;
if (gpio_is_valid(dev->reset_pin)) { /* 檢查IO是否有效 */
/* 申請復(fù)位IO,并且默認(rèn)輸出低電平 */
ret = devm_gpio_request_one(&client->dev,
dev->reset_pin, GPIOF_OUT_INIT_LOW,
"edt-ft5x06 reset");
if (ret) {
return ret;
}
msleep(5);
gpio_set_value(dev->reset_pin, 1); /* 輸出高電平,停止復(fù)位 */
msleep(300);
}
return 0;
}
/*
* @description : 從FT5X06讀取多個(gè)寄存器數(shù)據(jù)
* @param - dev: ft5x06設(shè)備
* @param - reg: 要讀取的寄存器首地址
* @param - val: 讀取到的數(shù)據(jù)
* @param - len: 要讀取的數(shù)據(jù)長度
* @return : 操作結(jié)果
*/
static int ft5x06_read_regs(struct ft5x06_dev *dev, u8 reg, void *val, int len)
{
int ret;
struct i2c_msg msg[2];
struct i2c_client *client = (struct i2c_client *)dev->client;
/* msg[0]為發(fā)送要讀取的首地址 */
msg[0].addr = client->addr; /* ft5x06地址 */
msg[0].flags = 0; /* 標(biāo)記為發(fā)送數(shù)據(jù) */
msg[0].buf = ® /* 讀取的首地址 */
msg[0].len = 1; /* reg長度*/
/* msg[1]讀取數(shù)據(jù) */
msg[1].addr = client->addr; /* ft5x06地址 */
msg[1].flags = I2C_M_RD; /* 標(biāo)記為讀取數(shù)據(jù)*/
msg[1].buf = val; /* 讀取數(shù)據(jù)緩沖區(qū) */
msg[1].len = len; /* 要讀取的數(shù)據(jù)長度*/
ret = i2c_transfer(client->adapter, msg, 2);
if(ret == 2) {
ret = 0;
} else {
ret = -EREMOTEIO;
}
return ret;
}
/*
* @description : 向ft5x06多個(gè)寄存器寫入數(shù)據(jù)
* @param - dev: ft5x06設(shè)備
* @param - reg: 要寫入的寄存器首地址
* @param - val: 要寫入的數(shù)據(jù)緩沖區(qū)
* @param - len: 要寫入的數(shù)據(jù)長度
* @return : 操作結(jié)果
*/
static s32 ft5x06_write_regs(struct ft5x06_dev *dev, u8 reg, u8 *buf, u8 len)
{
u8 b[256];
struct i2c_msg msg;
struct i2c_client *client = (struct i2c_client *)dev->client;
b[0] = reg; /* 寄存器首地址 */
memcpy(&b[1],buf,len); /* 將要寫入的數(shù)據(jù)拷貝到數(shù)組b里面 */
msg.addr = client->addr; /* ft5x06地址 */
msg.flags = 0; /* 標(biāo)記為寫數(shù)據(jù) */
msg.buf = b; /* 要寫入的數(shù)據(jù)緩沖區(qū) */
msg.len = len + 1; /* 要寫入的數(shù)據(jù)長度 */
return i2c_transfer(client->adapter, &msg, 1);
}
/*
* @description : 向ft5x06指定寄存器寫入指定的值,寫一個(gè)寄存器
* @param - dev: ft5x06設(shè)備
* @param - reg: 要寫的寄存器
* @param - data: 要寫入的值
* @return : 無
*/
static void ft5x06_write_reg(struct ft5x06_dev *dev, u8 reg, u8 data)
{
u8 buf = 0;
buf = data;
ft5x06_write_regs(dev, reg, &buf, 1);
}
/*
* @description : FT5X06中斷服務(wù)函數(shù)
* @param - irq : 中斷號(hào)
* @param - dev_id : 設(shè)備結(jié)構(gòu)。
* @return : 中斷執(zhí)行結(jié)果
*/
static irqreturn_t ft5x06_handler(int irq, void *dev_id)
{
struct ft5x06_dev *multidata = dev_id;
u8 rdbuf[29];
int i, type, x, y, id;
int offset, tplen;
int ret;
bool down;
offset = 1; /* 偏移1,也就是0X02+1=0x03,從0X03開始是觸摸值 */
tplen = 6; /* 一個(gè)觸摸點(diǎn)有6個(gè)寄存器來保存觸摸值 */
memset(rdbuf, 0, sizeof(rdbuf)); /* 清除 */
/* 讀取FT5X06觸摸點(diǎn)坐標(biāo)從0X02寄存器開始,連續(xù)讀取29個(gè)寄存器 */
ret = ft5x06_read_regs(multidata, FT5X06_TD_STATUS_REG, rdbuf, FT5X06_READLEN);
if (ret) {
goto fail;
}
/* 上報(bào)每一個(gè)觸摸點(diǎn)坐標(biāo) */
for (i = 0; i < MAX_SUPPORT_POINTS; i++) {
u8 *buf = &rdbuf[i * tplen + offset];
/* 以第一個(gè)觸摸點(diǎn)為例,寄存器TOUCH1_XH(地址0X03),各位描述如下:
* bit7:6 Event flag 0:按下 1:釋放 2:接觸 3:沒有事件
* bit5:4 保留
* bit3:0 X軸觸摸點(diǎn)的11~8位。
*/
type = buf[0] >> 6; /* 獲取觸摸類型 */
if (type == TOUCH_EVENT_RESERVED)
continue;
/* 我們所使用的觸摸屏和FT5X06是反過來的 */
x = ((buf[2] << 8) | buf[3]) & 0x0fff;
y = ((buf[0] << 8) | buf[1]) & 0x0fff;
/* 以第一個(gè)觸摸點(diǎn)為例,寄存器TOUCH1_YH(地址0X05),各位描述如下:
* bit7:4 Touch ID 觸摸ID,表示是哪個(gè)觸摸點(diǎn)
* bit3:0 Y軸觸摸點(diǎn)的11~8位。
*/
id = (buf[2] >> 4) & 0x0f;
down = type != TOUCH_EVENT_UP;
input_mt_slot(multidata->input, id);
input_mt_report_slot_state(multidata->input, MT_TOOL_FINGER, down);
if (!down)
continue;
input_report_abs(multidata->input, ABS_MT_POSITION_X, x);
input_report_abs(multidata->input, ABS_MT_POSITION_Y, y);
}
input_mt_report_pointer_emulation(multidata->input, true);
input_sync(multidata->input);
fail:
return IRQ_HANDLED;
}
/*
* @description : FT5x06中斷初始化
* @param - client : 要操作的i2c
* @param - multidev: 自定義的multitouch設(shè)備
* @return : 0,成功;其他負(fù)值,失敗
*/
static int ft5x06_ts_irq(struct i2c_client *client, struct ft5x06_dev *dev)
{
int ret = 0;
/* 1,申請中斷GPIO */
if (gpio_is_valid(dev->irq_pin)) {
ret = devm_gpio_request_one(&client->dev, dev->irq_pin,
GPIOF_IN, "edt-ft5x06 irq");
if (ret) {
dev_err(&client->dev,
"Failed to request GPIO %d, error %d\n",
dev->irq_pin, ret);
return ret;
}
}
/* 2,申請中斷,client->irq就是IO中斷, */
ret = devm_request_threaded_irq(&client->dev, client->irq, NULL,
ft5x06_handler, IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
client->name, &ft5x06);
if (ret) {
dev_err(&client->dev, "Unable to request touchscreen IRQ.\n");
return ret;
}
return 0;
}
/*
* @description : i2c驅(qū)動(dòng)的probe函數(shù),當(dāng)驅(qū)動(dòng)與
* 設(shè)備匹配以后此函數(shù)就會(huì)執(zhí)行
* @param - client : i2c設(shè)備
* @param - id : i2c設(shè)備ID
* @return : 0,成功;其他負(fù)值,失敗
*/
static int ft5x06_ts_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
int ret = 0;
ft5x06.client = client;
/* 1,獲取設(shè)備樹中的中斷和復(fù)位引腳 */
ft5x06.irq_pin = of_get_named_gpio(client->dev.of_node, "interrupt-gpios", 0);
ft5x06.reset_pin = of_get_named_gpio(client->dev.of_node, "reset-gpios", 0);
/* 2,復(fù)位FT5x06 */
ret = ft5x06_ts_reset(client, &ft5x06);
if(ret < 0) {
goto fail;
}
/* 3,初始化中斷 */
ret = ft5x06_ts_irq(client, &ft5x06);
if(ret < 0) {
goto fail;
}
/* 4,初始化FT5X06 */
ft5x06_write_reg(&ft5x06, FT5x06_DEVICE_MODE_REG, 0); /* 進(jìn)入正常模式 */
ft5x06_write_reg(&ft5x06, FT5426_IDG_MODE_REG, 1); /* FT5426中斷模式 */
/* 5,input設(shè)備注冊 */
ft5x06.input = devm_input_allocate_device(&client->dev);
if (!ft5x06.input) {
ret = -ENOMEM;
goto fail;
}
ft5x06.input->name = client->name;
ft5x06.input->id.bustype = BUS_I2C;
ft5x06.input->dev.parent = &client->dev;
__set_bit(EV_KEY, ft5x06.input->evbit);
__set_bit(EV_ABS, ft5x06.input->evbit);
__set_bit(BTN_TOUCH, ft5x06.input->keybit);
input_set_abs_params(ft5x06.input, ABS_X, 0, 1024, 0, 0);
input_set_abs_params(ft5x06.input, ABS_Y, 0, 600, 0, 0);
input_set_abs_params(ft5x06.input, ABS_MT_POSITION_X,0, 1024, 0, 0);
input_set_abs_params(ft5x06.input, ABS_MT_POSITION_Y,0, 600, 0, 0);
ret = input_mt_init_slots(ft5x06.input, MAX_SUPPORT_POINTS, 0);
if (ret) {
goto fail;
}
ret = input_register_device(ft5x06.input);
if (ret)
goto fail;
return 0;
fail:
return ret;
}
/*
* @description : i2c驅(qū)動(dòng)的remove函數(shù),移除i2c驅(qū)動(dòng)的時(shí)候此函數(shù)會(huì)執(zhí)行
* @param - client : i2c設(shè)備
* @return : 0,成功;其他負(fù)值,失敗
*/
static int ft5x06_ts_remove(struct i2c_client *client)
{
/* 釋放input_dev */
input_unregister_device(ft5x06.input);
return 0;
}
/*
* 傳統(tǒng)驅(qū)動(dòng)匹配表
*/
static const struct i2c_device_id ft5x06_ts_id[] = {
{ "edt-ft5206", 0, },
{ "edt-ft5426", 0, },
{ /* sentinel */ }
};
/*
* 設(shè)備樹匹配表
*/
static const struct of_device_id ft5x06_of_match[] = {
{ .compatible = "edt,edt-ft5206", },
{ .compatible = "edt,edt-ft5426", },
{ /* sentinel */ }
};
/* i2c驅(qū)動(dòng)結(jié)構(gòu)體 */
static struct i2c_driver ft5x06_ts_driver = {
.driver = {
.owner = THIS_MODULE,
.name = "edt_ft5x06",
.of_match_table = of_match_ptr(ft5x06_of_match),
},
.id_table = ft5x06_ts_id,
.probe = ft5x06_ts_probe,
.remove = ft5x06_ts_remove,
};
/*
* @description : 驅(qū)動(dòng)入口函數(shù)
* @param : 無
* @return : 無
*/
static int __init ft5x06_init(void)
{
int ret = 0;
ret = i2c_add_driver(&ft5x06_ts_driver);
return ret;
}
/*
* @description : 驅(qū)動(dòng)出口函數(shù)
* @param : 無
* @return : 無
*/
static void __exit ft5x06_exit(void)
{
i2c_del_driver(&ft5x06_ts_driver);
}
module_init(ft5x06_init);
module_exit(ft5x06_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuozhongkai");
3. 驅(qū)動(dòng)測試
1.手動(dòng)加載加載驅(qū)動(dòng)
- 將上一小節(jié)編譯出來 ft5x06.ko 拷貝到
rootfs/lib/modules/4.1.15目錄中,啟動(dòng)開發(fā)板,進(jìn)入到目錄 lib/modules/4.1.15中
depmod /*第一次加載驅(qū)動(dòng)的時(shí)候需要運(yùn)行此命令*/
modprobe ft5x06.ko /*加載驅(qū)動(dòng)模塊*/
hexdump /dev/input/event2 /*開始進(jìn)行接觸測試*/
2.將驅(qū)動(dòng)添加到內(nèi)核文章來源:http://www.zghlxwxcb.cn/news/detail-452030.html
- 前面我們一直將觸摸驅(qū)動(dòng)編譯為模塊,每次系統(tǒng)啟動(dòng)以后在手動(dòng)加載驅(qū)動(dòng)模塊,這樣很不方便。當(dāng)我們把驅(qū)動(dòng)調(diào)試成功以后一般都會(huì)將其編譯到內(nèi)核中,這樣內(nèi)核啟動(dòng)以后就會(huì)自動(dòng)加載驅(qū)動(dòng),不需要我們再手動(dòng) modprobe 了
1、將驅(qū)動(dòng)文件放到合適的位置,在內(nèi)核源碼中找個(gè)合適的位置將 ft5x06.c放進(jìn)去,ft5x06.c是個(gè)觸摸屏驅(qū)動(dòng),因此我們需要查找一下 linux 內(nèi)核里面觸摸屏驅(qū)動(dòng)放到了哪個(gè)目錄下。linux 內(nèi)核里面將觸摸屏驅(qū)動(dòng)放到了 drivers/input/touchscreen 目錄下,因此我們要將 ft5x06.c拷貝到此目錄下。
2.修改對(duì)應(yīng)的Makefile
修改完成以后重新編譯 linux 內(nèi)核,然后用新的 zImage 啟動(dòng)開發(fā)板。文章來源地址http://www.zghlxwxcb.cn/news/detail-452030.html
/*將觸摸屏驅(qū)動(dòng)添加到 linux 內(nèi)核里面以后觸摸屏對(duì)應(yīng)的是 event1,而不是前面編譯為模塊對(duì)應(yīng)的event2,這一點(diǎn)一定要注意*/
hexdump /dev/input/event1 //查看觸摸屏原始數(shù)據(jù)上報(bào)信息==開始測試
到了這里,關(guān)于電容觸摸屏驅(qū)動(dòng)(Linux驅(qū)動(dòng)開發(fā)篇)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!