触摸屏驱动分析

本文详细解析了触摸屏驱动的工作流程,从入口函数开始,介绍了如何分配及设置input_dev结构体,实现触摸事件与绝对坐标事件的注册。文章还深入探讨了硬件操作过程,包括时钟配置、寄存器设置、中断注册与处理,以及通过定时器优化长按和滑动体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

触摸屏驱动分析

一.入口函数

static int s3c_ts_init(void)

1.分配一个input_dev结构体

static struct input_dev s3c_ts_dev;

分配该结构体:s3c_ts_dev=input_allocate_device();

 

struct input_dev {

void *private;

const char *name;

const char *phys;

const char *uniq;

struct input_id id;

unsigned long evbit[NBITS(EV_MAX)];//设置哪一类事件

unsigned long keybit[NBITS(KEY_MAX)];//按键类事件中的哪一件事

unsigned long relbit[NBITS(REL_MAX)];

unsigned long absbit[NBITS(ABS_MAX)];

unsigned long mscbit[NBITS(MSC_MAX)];

unsigned long ledbit[NBITS(LED_MAX)];

unsigned long sndbit[NBITS(SND_MAX)];

unsigned long ffbit[NBITS(FF_MAX)];

unsigned long swbit[NBITS(SW_MAX)];

unsigned int keycodemax;

unsigned int keycodesize;

void *keycode;

int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);

int (*getkeycode)(struct input_dev *dev, int scancode, int *keycode);

struct ff_device *ff;

unsigned int repeat_key;

struct timer_list timer;

int state;

int sync;

int abs[ABS_MAX + 1];

int rep[REP_MAX + 1];

unsigned long key[NBITS(KEY_MAX)];

unsigned long led[NBITS(LED_MAX)];

unsigned long snd[NBITS(SND_MAX)];

unsigned long sw[NBITS(SW_MAX)];

int absmax[ABS_MAX + 1];

int absmin[ABS_MAX + 1];

int absfuzz[ABS_MAX + 1];

int absflat[ABS_MAX + 1];

int (*open)(struct input_dev *dev);

void (*close)(struct input_dev *dev);

int (*flush)(struct input_dev *dev, struct file *file);

int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);

struct input_handle *grab;

struct mutex mutex;/* serializes open and close operations */

unsigned int users;

struct class_device cdev;

union {/* temporarily so while we switching to struct device */

struct device *parent;

} dev;

struct list_headh_list;

struct list_headnode;

};

2.设置input_dev结构体

(1)设置能产生哪类事件

按键类事件,绝对位移事件

set_bit(EV_KEY,s3c_ts_dev);//按键类事件

set_bit(EV_ABS,s3c_ts_dev);//绝对位移事件

(2)设置能产生一类事件中具体哪个事件

按键类事件:触摸屏事件

绝对位移事件:x方向,y方向,绝对位移方向

set_bit(BTN_TOUCH,s3c_ts_dev->keybit);//按键类事件中的触摸屏事件

input_set_abs_params(s3c_ts_dev,ABS_X,0,0x3ff,0,0);//绝对位移事件中x方向位移事件

参数1input_dev

参数2:坐标方向(xy,压力方向)

参数3:坐标最小值:0

参数4:坐标最大值:0x3ff(分辨率为10位,最大值刚好为0x3ff)

参数5,6:填0

Input_set_abs_params(s3c_ts_dev,ABS_Y,0,0X3FF,0,0);//Y方向位移事件

Iniput_set_abs_params(s3c_ts_dev,ABS_PRESSURE,0,1,0,0);//压力方向位移事件

3.注册input_dev结构体

input_register_device(s3c_ts_dev);

 

4.硬件操作

(1)使能ADCPCLK时钟(clkcon[15])

struct clk *clk;

clk_get(NULL,adc);

clk_enable(clk);

(2)设置触摸屏相关的寄存器

① 在设置前需要先映射为虚拟地址

struct s3c_ts_regs {

unsigned long adccon;

unsigned long adctsc;

unsigned long adcdly;

unsigned long adcdat0;

unsigned long adcdat1;

unsigned long adcupdn;

};//将所有寄存器写成一个结构体,只需要对结构体映射即可

ioremap(0x58000000,sizeof(struct s3c_ts_regs));//建立映射

② 设置ADC相关的寄存器(基本上只需设置adcconadctscadcdly三个)

a.ADCCON

 

bit[14]:1—使能预分频器

bit[13-6]:49—预分频值设置为49

其余各位设置为0

s3c_ts_regs->adccon=(1<<14)|(49<<6);

③ 注册中断

因为触摸屏工作的基本原理是当笔尖按下或提起时会产生中断,所以需要注册一个IRQ_TC,当笔尖按下时,会启动ADC转换;当ADC转换完成后,会触发ADC中断,进行事件上报等处理,因此还需要注册一个ADC中断(IRQ_ADC)

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)

参数1:中断名

参数2:中断处理函数

参数3irqflags

参数4:设备名

参数5dev_id  

④等待笔尖按下触发中断(IRQ_TC

enter_wait_pen_down_mode();//需要设置ADCTSC寄存器的值为0xd3,然后才能等待笔尖按下中断

void enter_wait_pen_down_mode(void)

{

s3c_ts_regs->adctsc=0xd3;

}

 

 

补充一个优化:因为如果电压还未稳定就触发了IRQ_TC中断,则此时的测量值是不可靠的,因此需要延时一段时间等电压稳定后再进行测量,即设置ADCDLY为最大值(0xffff)

 

⑤当笔尖按下时会触发中断(IRQ_TC,并触发中断处理函数pen_down_up_irq

在中断处理函数中首先判断触发原因是笔尖按下,还是笔尖抬起(因为这两者的中断处理函数是同一个);通过ADCDAT0的第15位来判断,0代表笔尖落下,1代表笔尖抬起

如果是笔尖抬起所触发的中断,则需要上报事件:

input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);

input_report_abs(s3c_ts_dev,BTN_TOUCH,0);

input_sync(s3c_ts_dev);//上报完需要添加该语句

上报完事件后(即已经处理完)再次等待笔尖落下

如果是笔尖落下所触发的中断,则进入测量x,y坐标的模式(enter_measure_xy_mode()),该模式主要设置ADCx,y测量方式,并启动ADC转换(start_adc

static void enter_measure_xy_mode(void)

{

s3c_ts_regs->adctsc = (1<<3)|(1<<2);//设置为x,y自动转换模式

}

static void start_adc(void)

{

s3c_ts_regs->adccon |= (1<<0);//启动ADC转换

}

⑥当ADC转换完成会触发IRQ_ADC中断,并执行中断处理函数adc_irq

由于在ADC转换完成前可能触摸笔就已经松开,此时的测量值就没有意义,需要舍弃, 因此在adc_irq中首先判断触摸笔是否已经松开,如果已经松开,则上报事件并等待笔 尖再次落下

static irqreturn_t adc_irq(int irq, void *dev_id)

{

if (s3c_ts_regs->adcdat0 & (1<<15))

{

/* 已经松开 */

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();

}

}

如果笔尖尚未抬起就读取x,y的测量值,并进行一些求平均值等优化操作以提高精度,

再将处理好的结果结果进行上报,最后继续等待笔尖抬起

这里的软件优化措施包括:求平均值,软件滤波(static int s3c_filter_ts(int x[], int y[])) 等

还需要处理长按或者滑动等情况,使用定时器在处理,基本原理是:使用定时器延时10ms后,再次判断笔尖是否抬起,如果抬起了(不是长按或者滑动),则上报事件;如果还没抬起(长按或者滑动,再次测量x,y的值)

——要使用定时器需要有如下步骤:

static struct timer_list ts_timer;

init_timer(&ts_timer);//初始化定时器

ts_timer.function = s3c_ts_timer_function;//定时器时间到执行此函数

add_timer(&ts_timer);//添加一个定时器

上述步骤就在入口函数中设置

mod_timer(&ts_timer, jiffies + HZ/100);//启动定时器10ms,在adc_irq中处理数据时启动

定时时间到,执行s3c_ts_timer_function

static void s3c_ts_timer_function(unsigned long data)

{

if (s3c_ts_regs->adcdat0 & (1<<15))

{

/* 已经松开 */

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坐标 */

enter_measure_xy_mode();

start_adc();

}

}

2.出口函数

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);//关闭定时器

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值