Android(安卓)触摸屏驱动代码分析(ADC 类型触摸屏 CPU:s3c-s5pc100)
Android 2.1 farsight version for s5pc100
File Name: s3c-ts.c
1 简介
1.1 本例基于s5pc100开发板,触摸屏与CPU直接使用ADC连接。下次再找个I2C的驱动分析一下(比如:tsc2007.c)。
接口如下:
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<<ts->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;
};
更多相关文章
- Android把View转换成Bitmap
- Android(安卓)编译系统 (一)
- android keystore文件转换格式为pk8和x509.pem
- Android(安卓)开发中uboot传给Kernel 的androidboot.xxx命令行参
- android 中bitmap与String(base64) byte[]、和drawable的互相转换
- Android中string转换成int
- Android(安卓)Bitmap和Drawable相互转换方法
- android想用adb查看数据库 被告知inaccessible or not found怎么
- Retrofit2 使用FastJson作为Converter