android 触摸屏驱动分析,Android 触摸屏驱动代码分析(ADC 类型触摸屏 CPU:s3c

Android 2.1 farsight version for s5pc100

File Name: s3c-ts.c

1           简介

1.1          本例基于s5pc100开发板,触摸屏与CPU直接使用ADC连接。下次再找个I2C的驱动分析一下(比如:tsc2007.c)。

接口如下:

43328850_1.gif

1.2          相关寄存器设置请看《S5PC100_UM_REV104.PDF》第1669页,REGISTER DESCRIPTION.

1.3          总体上触摸屏驱动程序的工作流程如下:

Step1:在Probe里注册一个Input设备,并注册TSADC Pen Down Interrupt和TSADC EOC (End of conversion) Interrupt。

Step2:触摸笔按下,响应中断,打开AD转换

Step3:响应AD转换结束中断,记录转换结果。

Step4:如果没超过最大连续AD转换次数,再次转换。

Step5:如果超过最大连续AD转换次数,则计算坐标上报事件。打开Timer。

Step6:Timer到期,检查按下状态。

Step7:如果按下打开AD转换,转换结束进入Step3

Step8:如果抬起状态,上报抬起事件。

1.4          源代码如下:.

2           初始化

static struct platform_driver s3c_ts_driver = {

.probe          = s3c_ts_probe,

.remove         = s3c_ts_remove,

.suspend        = s3c_ts_suspend,

.resume         = s3c_ts_resume,

.driver                  = {

.owner     = THIS_MODULE,

.name       = "s3c-ts",

},

};

static char banner[] __initdata = KERN_INFO "S3C Touchscreen driver, (c) 2008 Samsung Electronics\n";//一个常量信息,放在init.data文件中,内核启动结束后释放。

static int __init s3c_ts_init(void)

{

printk(banner);

return platform_driver_register(&s3c_ts_driver);//注册一个名为s3c-ts的驱动程序

}

static void __exit s3c_ts_exit(void)

{

platform_driver_unregister(&s3c_ts_driver); //注销一个名为s3c-ts的驱动程序

}

module_init(s3c_ts_init);//入口宏

module_exit(s3c_ts_exit);//出口宏

MODULE_AUTHOR("Samsung AP");

MODULE_DESCRIPTION("S3C touchscreen driver");

MODULE_LICENSE("GPL");

3           探测函数s3c_ts_probe

3.1          res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

获得资源并保存在res变量中。获得了flags= IORESOURCE_MEM,索引为0的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

注意除了ts外很多资源定义在arch\arm\plat-s5pc1xx\devs.c中

/* Touch srcreen */

static struct resource s3c_ts_resource[] = {

[0] = {

.start = S3C_PA_ADC,

.end   = S3C_PA_ADC + SZ_4K - 1,

.flags = IORESOURCE_MEM,

},

[1] = {

.start = IRQ_PENDN,

.end   = IRQ_PENDN,

.flags = IORESOURCE_IRQ,

},

[2] = {

.start = IRQ_ADC,

.end   = IRQ_ADC,

.flags = IORESOURCE_IRQ,

}

};

3.2           ts_mem = request_mem_region(res->start, size, pdev->name);

申请内存区域。申请之后,才能开始ioremap()或者ioremap_nocache()来映射,映射后的变量才能使用。

3.3          ts_base = ioremap(res->start, size);

将io映射到变量ts_base,以后访问这个变量相当于访问ts对应的io地址了。

3.4          ts_clock = clk_get(&pdev->dev, "adc");

得到adc的时钟。adc时钟定义在arch/arm/plat-s5pc1xx/clock.c中

static struct clk init_clocks[] = {

}, {

.name                = "adc",

.id              = -1,

.parent              = &clk_p,

.enable              = s5pc1xx_clk_d15_ctrl,

.ctrlbit      = S5P_CLKGATE_D15_TSADC,

}, {

3.5          s3c_ts_cfg = s3c_ts_get_platdata(&pdev->dev); //得到dev的platdata,如果为空则返回s3c_ts_default_cfg的数据。

3.6          寄存器初始化

if ((s3c_ts_cfg->presc&0xff) > 0)//cfg是否设置了proscale,

writel(S3C_ADCCON_PRSCEN | S3C_ADCCON_PRSCVL(s3c_ts_cfg->presc&0xFF),\

ts_base+S3C_ADCCON); // 如果设置了,则设置到寄存器

else

writel(0, ts_base+S3C_ADCCON);

/* Initialise registers */

if ((s3c_ts_cfg->delay&0xffff) > 0)//是否设置了延时

writel(s3c_ts_cfg->delay & 0xffff, ts_base+S3C_ADCDLY);

if (s3c_ts_cfg->resol_bit==12) {//设置AD模式,10bit 或12bit

switch(s3c_ts_cfg->s3c_adc_con) {

case ADC_TYPE_2://6410 或s5pc100

writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT, ts_base+S3C_ADCCON);

break;

case ADC_TYPE_1://2410

writel(readl(ts_base+S3C_ADCCON)|S3C_ADCCON_RESSEL_12BIT_1, ts_base+S3C_ADCCON);

break;

default:

dev_err(dev, "Touchscreen over this type of AP isn't supported !\n");

break;

}

}

3.7          writel(WAIT4INT(0), ts_base+S3C_ADCTSC); // S3C_ADCTSC 寄存器清零,此时应该不会发生TouchScreen引起的AD转换。

3.8          input_dev = input_allocate_device();

使用input子系统的一般流程为:input_allocate_device()申请一个input_dev设备——>初始化该input_dev——>input_register_device()向子系统注册该设备——>中断时input_event()向子系统报告事件

3.9          初始化input_dev(即:ts->dev)

初始化的内容各个TS差不多。

注意:BTN_TOUCH 这个事件的相应是必须的。这是Andorid特有的要求。

ts->dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);

3.10       register_early_suspend(&ts->early_suspend);

注册一个early_suspend函数,这个函数会在power management的时候用到。

3.11       ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

获得资源并保存在ts_irq变量中。获得了flags= IORESOURCE_IRQ,索引为0的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

(详见本节开头)

3.12       ret = request_irq(ts_irq->start, stylus_updown, IRQF_SAMPLE_RANDOM, "s3c_updown", ts);

注册一个中断响应函数。其中:ts_irq->start 等于IRQ_PENDN

此函数在触摸屏按下或者抬起的时候发生。

详见arch/arm/plat-s5pc1xx/ include/plat/irqs.h

#define IRQ_PENDN               S5PC1XX_IRQ_VIC2(24)

#define IRQ_TC                       IRQ_PENDN

3.13       ts_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 1);

获得资源并保存在ts_irq变量中。获得了flags= IORESOURCE_IRQ,索引为1的资源。

资源定义在如下文件中:arch\arm\plat-s3c\Dev-ts.c

(详见本节开头)

3.14       ret = request_irq(ts_irq->start, stylus_action, IRQF_SAMPLE_RANDOM, "s3c_action", ts);

注册一个中断响应函数。其中:ts_irq->start 等于IRQ_ADC

当触摸屏按下后发生滑动的时候发生。

3.15       ret = input_register_device(ts->dev);

注册一个input 设备

4           s3c_ts_remove

s3c_ts_probe的反过程。注销设备,释放资源。

5           static irqreturn_t stylus_updown(int irqno, void *param)

5.1          读取按下的状态

data0 = readl(ts_base+S3C_ADCDAT0);

data1 = readl(ts_base+S3C_ADCDAT1);

//判断data0,data1最高位是否仍为"0",为“0”表示触摸笔状态保持为down

updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));

#ifdef CONFIG_TOUCHSCREEN_S3C_DEBUG

printk(KERN_INFO "   %c\n", updown ? 'D' : 'U');

#endif

如果updown是1表示按下。

5.2          判断按下的状态

if (updown)

touch_timer_fire(0);

5.3          如果是6410或者s5pc100则,关闭中断(ADCCLRINT 和ADCCLRINTPNDNUP)

if(ts->s3c_adc_con==ADC_TYPE_2) {

__raw_writel(0x0, ts_base+S3C_ADCCLRWK);

__raw_writel(0x0, ts_base+S3C_ADCCLRINT);

}

6           static irqreturn_t stylus_action(int irqno, void *param)

unsigned long data0;

unsigned long data1;

data0 = readl(ts_base+S3C_ADCDAT0);//读取数据

data1 = readl(ts_base+S3C_ADCDAT1);

if(ts->resol_bit==12) {//根据类型设置不同的掩码,将数据保存在yp,xp变量中

#if defined(CONFIG_SMDK6410_REV10) || defined(CONFIG_TOUCHSCREEN_NEW)

ts->yp += S3C_ADCDAT0_XPDATA_MASK_12BIT - (data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT);

ts->xp += S3C_ADCDAT1_YPDATA_MASK_12BIT - (data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT);

#else

ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK_12BIT;

ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK_12BIT;

#endif

}

else {//这里是10bit

#if defined(CONFIG_SMDK6410_REV10) || defined(CONFIG_TOUCHSCREEN_NEW)

ts->yp += S3C_ADCDAT0_XPDATA_MASK - (data0 & S3C_ADCDAT0_XPDATA_MASK);

ts->xp += S3C_ADCDAT1_YPDATA_MASK - (data1 & S3C_ADCDAT1_YPDATA_MASK);

#else

ts->xp += data0 & S3C_ADCDAT0_XPDATA_MASK;

ts->yp += data1 & S3C_ADCDAT1_YPDATA_MASK;

#endif

}

ts->count++;//累计AD转化次数

if (ts->count < (1<shift)) {//如果未到达次数限制,通过设置寄存器开始AD转化

writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);

writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);

} else {

#ifdef CONFIG_FB_S3C_INNOLUX430

ts->yp = S3C_ADCDAT1_YPDATA_MASK_12BIT * ts->count - ts->yp;

#endif

mod_timer(&touch_timer, jiffies+1);//设置一个Timer

writel(WAIT4INT(1), ts_base+S3C_ADCTSC);//TS 的AD转化进入等待状态

}

if(ts->s3c_adc_con==ADC_TYPE_2) {//6410,S5pc100清理中断

__raw_writel(0x0, ts_base+S3C_ADCCLRWK);

__raw_writel(0x0, ts_base+S3C_ADCCLRINT);

}

return IRQ_HANDLED;

7           static void touch_timer_fire(unsigned long data)

7.1          校准变量

a0=205,a1=-4999,a2=64552724,a3=6326,a4=-12,a5=-37526976,a6=65536;

这是本驱动所用的变量。

Android要求对获得的xy数据进行校准,代码如下:(相关解释见下面备注)

ts->xp=(long) ((a2+(a0*x)+(a1*y))/a6);

ts->yp=(long) ((a5+(a3*x)+(a4*y))/a6);

备注:

Android的校准主要有如下两种方案:

方案一:移植TSLIB,通过TSLIB产生 pointercal 校准参数文件。

方案二:在驱动程序中校准。

校准公式解释如下:

(XL, YL是显示屏坐标,XT, YT是触摸屏坐标,)

XL = XT*A+YT*B+C

YL = XT*D+YT*E+F

由于具体计算是希望是整数运算,所以实际中保存的ABCDEF为整数,而增加一个参数Div

XL = (XT*A+YT*B+C) / Div

YL = (YT*D+YT*E+F) / Div

TSLIB把以上的7个参数 ABCDEF Div 保存在 pointercal 文件中。

不校准的数据: A=1, B=0, C=0, D=0, E=1, F=0, Div=1

A    B    C    D    E    F    Div

-411    37818    -3636780    -51325    39    47065584    65536

7.2          判断是按下或者抬起

data0 = readl(ts_base+S3C_ADCDAT0);

data1 = readl(ts_base+S3C_ADCDAT1);

updown = (!(data0 & S3C_ADCDAT0_UPDOWN)) && (!(data1 & S3C_ADCDAT1_UPDOWN));

updown==1表示按下

7.3          如果累计AD转换次数大于零,计算坐标并上报事件

x=(int) ts->xp;

y=(int) ts->yp;

ts->xp=(long) ((a2+(a0*x)+(a1*y))/a6);//计算校准

ts->yp=(long) ((a5+(a3*x)+(a4*y))/a6);

if(ts->xp!=ts->xp_old || ts->yp!=ts->yp_old)//如果和上一次的坐标不同

{

input_report_abs(ts->dev, ABS_X, ts->xp);//上报坐标

input_report_abs(ts->dev, ABS_Y, ts->yp);

input_report_abs(ts->dev, ABS_Z, 0);

input_report_key(ts->dev, BTN_TOUCH, 1);

input_sync(ts->dev);

}

ts->xp_old=ts->xp;//记录新的坐标点,供下次比对

ts->yp_old=ts->yp;

7.4          如果累计AD转换次数为零,则开启AD转换

//设置ADC开启转换

writel(S3C_ADCTSC_PULL_UP_DISABLE | AUTOPST, ts_base+S3C_ADCTSC);

writel(readl(ts_base+S3C_ADCCON) | S3C_ADCCON_ENABLE_START, ts_base+S3C_ADCCON);

7.5          如果不是按下,上报抬起事件

ts->count = 0;//计数器清空

#ifdef ANDROID_TS

input_report_abs(ts->dev, ABS_X, ts->xp_old);//上报位置

input_report_abs(ts->dev, ABS_Y, ts->yp_old);

input_report_abs(ts->dev, ABS_Z, 0);

#endif

input_report_key(ts->dev, BTN_TOUCH, 0);//上报抬起事件

#ifndef ANDROID_TS

input_report_abs(ts->dev, ABS_PRESSURE, 0);

#endif

input_sync(ts->dev);

writel(WAIT4INT(0), ts_base+S3C_ADCTSC);//清除ADC中断

备注:

1.        oversampling_shift 防止采样过度。

2.       关于中断类型的解释如下:

/*

* These flags used only by the kernel as part of the

* irq handling routines.

*

* IRQF_DISABLED - keep irqs disabled when calling the action handler

* IRQF_SAMPLE_RANDOM - irq is used to feed the random generator

* IRQF_SHARED - allow sharing the irq among several devices

* IRQF_PROBE_SHARED - set by callers when they expect sharing mismatches to occur

* IRQF_TIMER - Flag to mark this interrupt as timer interrupt

* IRQF_PERCPU - Interrupt is per cpu

* IRQF_NOBALANCING - Flag to exclude this interrupt from irq balancing

* IRQF_IRQPOLL - Interrupt is used for polling (only the interrupt that is

*                registered first in an shared interrupt is considered for

*                performance reasons)

*/

#define IRQF_DISABLED            0x00000020

#define IRQF_SAMPLE_RANDOM    0x00000040

#define IRQF_SHARED               0x00000080

#define IRQF_PROBE_SHARED         0x00000100

#define IRQF_TIMER                  0x00000200

#define IRQF_PERCPU                0x00000400

#define IRQF_NOBALANCING  0x00000800

#define IRQF_IRQPOLL               0x00001000

3.       adc type defination

File name: arch\arm\plat-s3c\include\plat\ts.h

enum s3c_adc_type {

ADC_TYPE_0,

ADC_TYPE_1, //S3C2416, S3C2450

ADC_TYPE_2, //S3C64XX, S5PC1XX

};

4.       s3c_ts_mach_info type defination

//用于设置触摸屏信息

struct s3c_ts_mach_info {

int delay; //AD转换延时

int presc; //分频

int oversampling_shift; // 采样的数据

int resol_bit; //转换精度

enum s3c_adc_type s3c_adc_con;

};

struct s3c_ts_mach_info s3c_ts_default_cfg __initdata = {

.delay =            10000,

.presc =            49,

.oversampling_shift =     2,

.resol_bit =               10

};

//采集触摸屏信息

struct s3c_ts_info {

struct input_dev *dev;

long xp; //x方向位置

long yp; //y方向位置

int count; //累加xp或yp数据的次数

int shift;

char phys[32];

int resol_bit; //转换精度

enum s3c_adc_type s3c_adc_con;

};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android系统的ADC按键驱动主要涉及到驱动层和应用层两个方面。 驱动层: Android系统的ADC按键驱动主要是由Linux内核的Input子系统来实现的。Input子系统是Linux内核中一个用于处理输入设备的子系统,主要功能是接收来自输入设备的数据,并将其传递到应用程序。 在Android系统中,Input子系统主要负责处理键盘、鼠标、触摸屏等输入设备的数据。对于ADC按键,一般都是通过GPIO口连接到处理器上,因此需要通过驱动程序来读取GPIO口的状态,以检测按键的按下和松开。 在Linux内核中,GPIO的读取是通过sysfs文件系统实现的。因此,驱动程序需要将GPIO口的状态映射到sysfs文件系统中的一个文件,以便应用程序能够读取GPIO口的状态。 应用层: 在Android系统中,应用程序一般使用Java语言编写。对于ADC按键,应用程序可以通过Android提供的KeyEvent类来处理按键事件。 当用户按下或松开ADC按键时,Linux内核会将这个事件传递给Android系统的Input子系统。Input子系统会将这个事件转换成一个KeyEvent对象,并将其发送给应用程序。 应用程序可以通过监听KeyEvent对象来处理按键事件。当用户按下或松开ADC按键时,应用程序可以根据不同的按键事件来执行不同的操作,例如打开应用程序、切换菜单、调节音量等。 综上所述,Android系统的ADC按键驱动主要是由Linux内核的Input子系统和应用层来实现的。在驱动层,需要通过驱动程序来读取GPIO口的状态,以检测按键的按下和松开;在应用层,需要通过监听KeyEvent对象来处理按键事件,以执行不同的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值