在事件處理層的函數(shù)都是通過input_register_handler()函數(shù)注冊到input_hander_list鏈表中,搜索input_register_handler注冊函數(shù),就可以看到事件處理層里的函數(shù):

右邊的驅(qū)動事件處理,內(nèi)核是已經(jīng)寫好了的,所以觸摸屏只需要寫具體的驅(qū)動設(shè)備,然后內(nèi)核會與觸摸屏驅(qū)動tsdev.c自動連接。
結(jié)構(gòu)體成員如下
struct input_dev {
void *private;
const char *name; //設(shè)備名字
const char *phys; //文件路徑,比如 input/buttons
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)]; //表示支持哪類事件,常用有以下幾種事件(可以多選)
//EV_SYN 同步事件,當(dāng)使用input_event()函數(shù)后,就要使用這個上報個同步事件
//EV_KEY 鍵盤事件
//EV_REL (relative)相對坐標(biāo)事件,比如鼠標(biāo)
//EV_ABS (absolute)絕對坐標(biāo)事件,比如搖桿、觸摸屏感應(yīng)
//EV_MSC 其他事件,功能
//EV_LED LED燈事件
//EV_SND (sound)聲音事件
//EV_REP 重復(fù)鍵盤按鍵事件
//(內(nèi)部會定義一個定時器,若有鍵盤按鍵事件一直按下/松開,就重復(fù)定時,時間一到就上報事件)
//EV_FF 受力事件
//EV_PWR 電源事件
//EV_FF_STATUS 受力狀態(tài)事件
unsigned long keybit[NBITS(KEY_MAX)]; //存放支持的鍵盤按鍵值
//鍵盤變量定義在:include/linux/input.h, 比如: KEY_L(按鍵L)、BTN_TOUCH(觸摸屏的按鍵)
unsigned long relbit[NBITS(REL_MAX)]; //存放支持的相對坐標(biāo)值
unsigned long absbit[NBITS(ABS_MAX)]; //存放支持的絕對坐標(biāo)值,存放下面4個absxxx[]
unsigned long mscbit[NBITS(MSC_MAX)]; //存放支持的其它事件,也就是功能
unsigned long ledbit[NBITS(LED_MAX)]; //存放支持的各種狀態(tài)LED
unsigned long sndbit[NBITS(SND_MAX)]; //存放支持的各種聲音
unsigned long ffbit[NBITS(FF_MAX)]; //存放支持的受力設(shè)備
unsigned long swbit[NBITS(SW_MAX)]; //存放支持的開關(guān)功能
... ...
/*以下4個數(shù)組都會保存在上面成員absbit[]里,數(shù)組號為:ABS_xx ,位于
include/linux/input.h */
/*比如數(shù)組0,標(biāo)志就是ABS_X,以下4個的absXXX[0]就是表示絕對位移X方向的最大值、最小值... */
/*對于觸摸屏常用的標(biāo)志有:
ABS_X(X坐標(biāo)方向), ABS_Y(Y坐標(biāo)方向), ABS_PRESSURE(壓力方向,比如繪圖,越用力線就越粗)* /
int absmax[ABS_MAX + 1]; //絕對坐標(biāo)的最大值
int absmin[ABS_MAX + 1]; //絕對坐標(biāo)的最小值
int absfuzz[ABS_MAX + 1]; //絕對坐標(biāo)的干擾值,默認(rèn)為0,
int absflat[ABS_MAX + 1]; //絕對坐標(biāo)的平焊位置,默認(rèn)為0
... ...
需要用到的函數(shù)
struct input_dev *input_allocate_device(void); //向內(nèi)存中分配input_dev結(jié)構(gòu)體
input_free_device(struct input_dev *dev); //釋放內(nèi)存中的input_dev結(jié)構(gòu)體
input_register_device(struct input_dev *dev); //注冊一個input_dev,若有對應(yīng)的驅(qū)動事件,
則在/sys/class/input下創(chuàng)建這個類設(shè)備
input_unregister_device(struct input_dev *dev); //卸載/sys/class/input目錄下的
input_dev這個類設(shè)備
set_bit(nr,p); //設(shè)置某個結(jié)構(gòu)體成員p里面的某位等于nr,支持這個功能
/* 比如:
set_bit(EV_KEY,buttons_dev->evbit); //設(shè)置input_dev結(jié)構(gòu)體buttons_dev->evbit支持EV_KEY
set_bit(KEY_S,buttons_dev->keybit); //設(shè)置input_dev結(jié)構(gòu)體buttons_dev->keybit支持按鍵”S”
*/
input_set_abs_params(struct input_dev *dev, int axis, int min, int max, int fuzz, int flat);
//設(shè)置絕對位移的支持參數(shù)
//dev: 需要設(shè)置的input_dev結(jié)構(gòu)體
//axis : 需要設(shè)置的數(shù)組號,常用的有: ABS_X(X坐標(biāo)方向), ABS_Y(Y坐標(biāo)方向), ABS_PRESSURE(壓力方向)//min: axis方向的最小值, max:axis方向的最大值, fuzz: axis方向的干擾值, flat:axis方向的平焊位置
input_report_abs(struct input_dev *dev, unsigned int code, int value);
//上報EV_ABS事件
//該函數(shù)實際就是調(diào)用的input_event(dev, EV_ABS, code, value);
//*dev :要上報哪個input_dev驅(qū)動設(shè)備的事件
// code: EV_ABS事件里支持的哪個方向,比如X坐標(biāo)方向則填入: ABS_X
//value:對應(yīng)的方向的值,比如X坐標(biāo)126
input_report_key(struct input_dev *dev, unsigned int code, int value);
//上報EV_KEY事件
input_sync(struct input_dev *dev); //同步事件通知,通知系統(tǒng)有事件上報
struct clk *clk_get(struct device *dev, const char *id);
//獲得*id模塊的時鐘,返回一個clk結(jié)構(gòu)體
//*dev:填0即可, *id:模塊名字, 比如"adc","i2c"等,名字定義在clock.c中
clk_enable(struct clk *clk);
//開啟clk_get()到的模塊時鐘,就是使能CLKCON寄存器的某個模塊的位
電阻式觸摸屏介紹

引腳說明:
YM: (Y Minus)觸摸屏的Y坐標(biāo)的負(fù)線,也可以用Y -表示
YP : (Y Power)觸摸屏的Y坐標(biāo)的正線, 也可以用Y+表示
XM: (Y Minus)觸摸屏的X坐標(biāo)的負(fù)線, 也可以用X-表示
XP : (Y Power)觸摸屏的X坐標(biāo)的正線, 也可以用X+表示
觸摸屏包含了兩個阻性層,如下圖所示:

當(dāng)沒有觸摸按下時,x層和Y層是分離的,此時就測不到電壓。
測X坐標(biāo)方向時
如下圖, 把XP接3.3V , XM接0V, YP和YM懸空,我們以按壓X坐標(biāo)的中間位置, X層和Y層便閉合了,此時YP就會輸出當(dāng)前X坐標(biāo)值的1.66V給CPU。

測Y坐標(biāo)方向時:
如下圖, 把YP接3.3V , YM接0V, XP和XM懸空,我們以按壓X坐標(biāo)的中間位置, X層和Y層便閉合了,此時XP就會輸出當(dāng)前X坐標(biāo)值的1.66V給CPU 。

ADC模型數(shù)據(jù)轉(zhuǎn)換器
,2440的ADC分辨率為10位(0~0X3FFF)

如下圖,若工作在普通ADC模式,則通過寄存器ADCCON->SEL_MUX來選擇轉(zhuǎn)換哪個引腳的模擬信號。當(dāng)設(shè)置為ADC等待中斷模式時,測到有屏幕筆尖觸摸,就會產(chǎn)生INT_TC中斷,其中ADC的工作頻率最大為2.5MHZ,需要設(shè)置寄存器ADCCON->PRSCVL更改分頻系數(shù)。

獲取筆尖觸摸屏按下/松開使用的是ADC等待中斷模式:
當(dāng)筆尖落下時觸摸屏控制器產(chǎn)生中斷(INT_TC)信號。需要設(shè)置寄存器ADCTSC=0xd3/0x1d3
設(shè)置寄存器ADCTSC=0x0d3/0x1d3 (X 1101 0011)時(如下圖):

獲取XY坐標(biāo)時使用的是自動 X/Y 方向轉(zhuǎn)換模式
當(dāng)ADC轉(zhuǎn)換成功, X 坐標(biāo)值到 ADCDAT0 和 Y 坐標(biāo)值到ADCDAT1 后,就會產(chǎn)生INT_ADC中斷
自動獲取XY坐標(biāo)時(如下圖):

設(shè)置寄存器ADCTSC=0X0C (關(guān)閉XP上拉、啟動自動XY方向轉(zhuǎn)換)
設(shè)置寄存器ADCCON的位[0]=1(開啟一次ADC轉(zhuǎn)換,當(dāng)ADC轉(zhuǎn)換成功該位清0)
代碼編寫
在init入口函數(shù)中:
1.分配一個input_dev結(jié)構(gòu)體
2.設(shè)置input_dev的成員
- 設(shè)置input_dev->evbit支持按鍵事件,絕對位移事件(觸摸屏:通過按鍵BTN_TOUCH獲取按下/松開,通過絕對位移獲取坐標(biāo))
- 設(shè)置input_dev-> keybit支持BTN_TOUCH觸摸屏筆尖按下。
- 設(shè)置input_dev-> absbit 支持ABS_X、ABS_Y、 ABS_PRESSURE
input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0); // 0x3FF:最大值為10位ADC,
input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0); //壓力最多就是1
3.注冊input_dev驅(qū)動設(shè)備到內(nèi)核中
4.設(shè)置觸摸屏相關(guān)的硬件
- 開啟ADC時鐘,使用clk_get ()和clk_enable()函數(shù)。
- ioremap獲取寄存器地址,設(shè)置寄存器ADCCON =(1<<14)|(49<<6),分頻
- 設(shè)置寄存器ADCDLY=0xffff,ADC啟動延時時間設(shè)為最大值,使觸摸按壓更加穩(wěn)定
- 開啟IRQ_TC筆尖中斷、開啟IRQ_ADC中斷獲取XY坐標(biāo)
- 初始化定時器,增加觸摸滑動功能
- 最后設(shè)置寄存器ADCTSC=0x0d3,開啟IRQ_TC中斷
5.在出口函數(shù)中:
- 注銷內(nèi)核里的input_dev
- 釋放中斷、刪除定時器、iounmap注銷地址
- 釋放input_dev
6.在IRQ_TC中斷函數(shù)中:
- 若判斷筆尖為松開,設(shè)置寄存器ADCTSC =0XD3(按下中斷)
- 若判斷筆尖按下,設(shè)置為XY自動轉(zhuǎn)換模式,啟動一次ADC轉(zhuǎn)換,ADC轉(zhuǎn)換成功,會進(jìn)入ADC中斷
7.在IRQ_ADC中斷函數(shù)中:
- 獲取ADCDAT0的位[9:0],來算出XY方向坐標(biāo)值
- 測量n次值保存在數(shù)組中,然后再次設(shè)置為XY自動轉(zhuǎn)換模式,啟動ADC
- 采集完畢,使用快速排序?qū)次值排序后,以最小值為基準(zhǔn),如有誤差非常大的數(shù),則舍棄,如果沒有則打印數(shù)組的中間值,實現(xiàn)中值濾波。
- 打印數(shù)據(jù)后,必須設(shè)置寄存器ADCTSC =0X1D3(松開中斷IRQ_TC)
- 設(shè)置定時器10ms超時時間
- 在定時器超時函數(shù)中:
- 判斷ADCDAT0的bit15位,若還在按下再次啟動ADC轉(zhuǎn)換(實現(xiàn)觸摸滑動功能)
- 若松開,設(shè)置寄存器ADCTSC =0XD3(按下中斷)
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/plat-s3c24xx/ts.h>
#include <asm/arch/regs-adc.h>
#include <asm/arch/regs-gpio.h>
struct s3c_ts_regs {
unsigned long adccon;
unsigned long adctsc;
unsigned long adcdly;
unsigned long adcdat0;
unsigned long adcdat1;
unsigned long adcupdn;
};
static struct input_dev *s3c_ts_dev;
static volatile struct s3c_ts_regs *s3c_ts_regs;
static struct timer_list ts_timer;
static void enter_wait_pen_down_mode(void)
{
s3c_ts_regs->adctsc = 0xd3;
}
static void enter_wait_pen_up_mode(void)
{
s3c_ts_regs->adctsc = 0x1d3;
}
static void enter_measure_xy_mode(void)
{
s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}
static void start_adc(void)
{
s3c_ts_regs->adccon |= (1<<0);
}
static int s3c_filter_ts(int x[], int y[])
{
#define ERR_LIMIT 10
int avr_x, avr_y;
int det_x, det_y;
avr_x = (x[0] + x[1])/2;
avr_y = (y[0] + y[1])/2;
det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);
det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);
if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
return 0;
avr_x = (x[1] + x[2])/2;
avr_y = (y[1] + y[2])/2;
det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);
det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);
if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
return 0;
return 1;
}
static void s3c_ts_timer_function(unsigned long data)
{
if (s3c_ts_regs->adcdat0 & (1<<15))
{
/* 已經(jīng)松開 */
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_pen_down_mode();
}
else
{
/* 測量X/Y坐標(biāo) */
enter_measure_xy_mode();
start_adc();
}
}
static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{
if (s3c_ts_regs->adcdat0 & (1<<15))
{
//printk("pen up\n");
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_pen_down_mode();
}
else
{
//printk("pen down\n");
//enter_wait_pen_up_mode();
enter_measure_xy_mode();
start_adc();
}
return IRQ_HANDLED;
}
static irqreturn_t adc_irq(int irq, void *dev_id)
{
static int cnt = 0;
static int x[4], y[4];
int adcdat0, adcdat1;
/* 優(yōu)化措施2: 如果ADC完成時, 發(fā)現(xiàn)觸摸筆已經(jīng)松開, 則丟棄此次結(jié)果 */
adcdat0 = s3c_ts_regs->adcdat0;
adcdat1 = s3c_ts_regs->adcdat1;
if (s3c_ts_regs->adcdat0 & (1<<15))
{
/* 已經(jīng)松開 */
cnt = 0;
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
input_sync(s3c_ts_dev);
enter_wait_pen_down_mode();
}
else
{
// printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);
/* 優(yōu)化措施3: 多次測量求平均值 */
x[cnt] = adcdat0 & 0x3ff;
y[cnt] = adcdat1 & 0x3ff;
++cnt;
if (cnt == 4)
{
/* 優(yōu)化措施4: 軟件過濾 */
if (s3c_filter_ts(x, y))
{
//printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);
input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
input_sync(s3c_ts_dev);
}
cnt = 0;
enter_wait_pen_up_mode();
/* 啟動定時器處理長按/滑動的情況 */
mod_timer(&ts_timer, jiffies + HZ/100);
}
else
{
enter_measure_xy_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
static int s3c_ts_init(void)
{
struct clk* clk;
/* 1. 分配一個input_dev結(jié)構(gòu)體 */
s3c_ts_dev = input_allocate_device();
/* 2. 設(shè)置 */
/* 2.1 能產(chǎn)生哪類事件 */
set_bit(EV_KEY, s3c_ts_dev->evbit);
set_bit(EV_ABS, s3c_ts_dev->evbit);
/* 2.2 能產(chǎn)生這類事件里的哪些事件 */
set_bit(BTN_TOUCH, s3c_ts_dev->keybit);
input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
/* 3. 注冊 */
input_register_device(s3c_ts_dev);
/* 4. 硬件相關(guān)的操作 */
/* 4.1 使能時鐘(CLKCON[15]) */
clk = clk_get(NULL, "adc");
clk_enable(clk);
/* 4.2 設(shè)置S3C2440的ADC/TS寄存器 */
s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
/* bit[14] : 1-A/D converter prescaler enable
* bit[13:6]: A/D converter prescaler value,
* 49, ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz
* bit[0]: A/D conversion starts by enable. 先設(shè)為0
*/
s3c_ts_regs->adccon = (1<<14)|(49<<6);
request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);
/* 優(yōu)化措施1:
* 設(shè)置ADCDLY為最大值, 這使得電壓穩(wěn)定后再發(fā)出IRQ_TC中斷
*/
s3c_ts_regs->adcdly = 0xffff;
/* 優(yōu)化措施5: 使用定時器處理長按,滑動的情況
*
*/
init_timer(&ts_timer);
ts_timer.function = s3c_ts_timer_function;
add_timer(&ts_timer);
enter_wait_pen_down_mode();
return 0;
}
static void s3c_ts_exit(void)
{
free_irq(IRQ_TC, NULL);
free_irq(IRQ_ADC, NULL);
iounmap(s3c_ts_regs);
input_unregister_device(s3c_ts_dev);
input_free_device(s3c_ts_dev);
del_timer(&ts_timer);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");