cat /proc/bus/input/devices
I: Bus=0013 Vendor=dead Product=beef Version=0101
N: Name="SUN TouchScreen" //和驱动中定义名字是相同的
P: Phys=
S: Sysfs=/devices/virtual/input/input0
U: Uniq=
H: Handlers=event0
B: EV=b
B: KEY=0
B: ABS=1000003
ls -l /sys/class/input
lrwxrwxrwx 1 root root 0 Jan 1 1970 event0 -> ../../devices/virtual/input/input0/event0
linux/input.h中有定义, struct input_event {
struct timeval time; //按键时间
__u16 type; //类型
__u16 code; //
__s32 value;//
};
type:
EV_KEY,键盘
EV_REL,相对坐标
EV_ABS,绝对坐标
code:
在input.h中有定义:
#define ABS_X 0x00
#define ABS_Y 0x01
value:
根据不同的事件或者说是驱动的不同,代表不同的含义,在触摸屏代表的就是按下的X,Y坐标
输入子系统是由驱动层,输入子系统核心层、事件处理层组成
Input子系统的设备主设备号是固定的
一个输入事件:
user driver ->input core ->Event handler ->userspace
input设备时用input_dev来描述的,设备驱动完成的工作是向系统报告输入的事件(通过input_event结构体描述)
输入子系统设备驱动层实现原理:
输入设备不在关心设备文件的操作,而是关心对各个硬件寄存器的操作和提交的输入事件
驱动的设备按下面的步骤实现:
1. 在驱动加载模块设置input子系统支持哪些事件。
2. 将input设备注册到input系统中
3. 在input设备发生输入操作时,提交所发生的事件及对应的键值/坐标等状态。
事件类型:
EV_SYN
EV_KEY
EV_ABS
常见的提交事件
input_report_key(dev,code,value)
input_report_abs()
在提交输入设备的事件后必须用下列方法使事件同步,让它告知input系统,设备驱动已经发出了一个完整的报告
input_sync(dev)
驱动实现的机制:
首先是通过变量OWNADC保证TS中断先发生
发生后在TS中断中判断是否按下,如果按下就需要启动ADC转化,跳转到touch_timer_fire(),在ADC转换中获得坐标数据,否则释放ADC资源
touch_timer_fire()函数的执行过程:
在这个函数中是肯定要启动中ADC采样以完成坐标采集功能,让触摸屏控制器退出等待中断等待模式,进入自动转化模式,但是由于简单的算法处理需要多次采样取平均值,这一部分需要在ADC中断服务子程序中进行处理。
touch_timer_fire()函数还要完成一个功能就是报告触摸屏抬起事件,设置触摸屏控制器处于等待按下中断等待模式。
其实touch_timer_fire()有两条执行路径:
1. 就是在TS中断中
2. 就是系统启动定时函数,在ADC完成指定4次的采样后,定时1个时钟滴答就去执行这个函数。
touch_timer_fire()完成的功能:
启动ADC,设置连续模式。在按下状态,报告事件和坐标。在抬起状态下,释放ADC资源
定时器所起的作用就是消抖
在ADC中断服务子程序中完成的功能是:
在TC控制器的自动转化模式下,采集四次值(有没有必要在设置连续转化模式?调试时进行测试),为了消除屏幕抖动,在内核中使用了定时器,定时执行touch_timer_fire函数,接着设置TC控制器进入等待释放中断等待模式。此时要是有释放中断产生,也是在touch_time_fire() 函数的else语句中进行处理的,在处理中将TS控制器再设置为等待按下中断等待模式。即要完成这样的设置:在按下中设置等待释放中断,在释放时设置等待按下中断模式。
调试分析:
press!
report to inputcore
user running!
ABS_X:558,ABS_Y: 0
user running!
ABS_X:558,ABS_Y:483
user running!
ABS_X:558,ABS_Y:483
user running!
按照上面的调试信息,触摸屏向上值报告了一次就退出了,问题可能出现在ADC只启动了一次。
注意在按驱动模块调试信息可以在Kconfig中设置,学会用depends关联上调试信息的模块,
调试时注意细节,就是在编译是要在相应的文件夹中原来的编译程序删了重新编译,否则没有更新。在调试触摸屏时也可以作为模块加载的 ,在编译的将文件放在相应的文件夹中,更改makefile和config文件,调用指令make SUBDRI=drivers/input/ modules进行编译。
- /***************************************************
- Touch panel driver
- ***************************************************/
- #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 <plat/regs-adc.h>
- #include <mach/regs-gpio.h>
- #undef DEBUG
- //#define DEBUG
- #ifdef DEBUG
- #define dprintk(x...) {printk(x);}
- #else
- #define dprintk(x...) (void)(0)
- #endif
- static void __iomem *base_addr;
- static struct clk *adc_clock;
- static char *ts_name="SUN ToucnScreen";
- #define S3C2410TSVERSION 0x0101
- static struct input_dev *dev;//定义输入设备
- extern struct semaphore ADC_LOCK;
- static int OwnADC = 0;
- static long xp;
- static long yp;
- static int count;//记录按键按下的次数、
- #define WAIT4INT(x) (((x)<<8) | \
- S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
- S3C2410_ADCTSC_XY_PST(3))
- #define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
- S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
- /*策略是在按下中报告事件的,而不是在ADC完成采集后报告,可以在调试的时候尝试一下*/
- static void touch_timer_fire(unsigned long data)
- {
-
- unsigned long data0;
- unsigned long data1;
- int updown;
- data0 = ioread32(base_addr+S3C2410_ADCDAT0);
- data1 = ioread32(base_addr+S3C2410_ADCDAT1);
- updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
- if(updown)
- {
- //如果是按下状态并且ADC已经完成了转化就报告事件和数据
- if(count!=0)
- {
- long tmp;
- tmp = xp;
- xp = yp;
- yp = tmp;
- xp >>= 2;
- yp >>= 2;
- #ifdef CONFIG_TOUCHSCREEN_TQ2440_DEBUG
- printk("xp:%d,yp:%d",xp,yp);
- #endif
- input_report_abs(dev, ABS_X, xp);
- input_report_abs(dev, ABS_Y, yp);
- input_report_key(dev, BTN_TOUCH, 1);
- input_report_abs(dev, ABS_PRESSURE, 1);
- input_sync(dev);
-
- }
- //调试时问题就出在这里,count>0时那么就不会执行else中的语句,这就导致ADC没有被是能工作,count永远无法清零,导致程序直执行一次
- //else
- // {
- //这种情况就是按下,但是AD转化没有完成,设置TSC为自动转化模式,启动ADC
-
- xp = 0;
- yp = 0;
- count = 0;
- iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
- iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
- // }
- }
- else
- {
- //这种情况就是抬起状态
- //发送抬起事件状态
- count = 0;
- input_report_key(dev, BTN_TOUCH, 0);
- input_report_abs(dev, ABS_PRESSURE, 0);
- input_sync(dev);
- //设置等待中断模式
- //ADTSC的UD_SEN位为0表示等待触摸屏被按下中断
- iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
- //按键抬起表示事件的完成,所以释然信号量
- if (OwnADC)
- {
- OwnADC = 0;
- up(&ADC_LOCK);
- }
-
- }
- }
- /***************************************************
- 触摸屏中断
- 按照正常的程序是判断是否是按下,则启动ADC转化,设置
- 进入连续转换模式,如果是松开状态则是设置进入等待中断
- 模式
- ***************************************************/
- static irqreturn_t stylus_updown(int irq, void *dev_id)
- {
- unsigned long data0;
- unsigned long data1;
- int updown;
- if(down_trylock(&ADC_LOCK)==0)//表示获得信号量
- {
- OwnADC = 1;
- //为了判断触摸屏的状态读取两个数据寄存器中的值
- data0 = ioread32(base_addr+S3C2410_ADCDAT0);
- data1 = ioread32(base_addr+S3C2410_ADCDAT1);
- updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
- if(updown)
- {
-
- touch_timer_fire(0);//这个函数肯定是用来启动ADC的
- }
- else
- {
- OwnADC = 0;
- up(&ADC_LOCK);
- }
-
- }
-
-
- return IRQ_HANDLED;
-
- }
- static struct timer_list touch_timer =
- TIMER_INITIALIZER(touch_timer_fire, 0, 0);
- /***************************************************
- ADC中断
- 在ADC中进行四次坐标采样,取平均值,
- 比较稳定,
- ***************************************************/
- static irqreturn_t stylus_action(int irq, void *dev_id)
- {
- unsigned long data0;
- unsigned long data1;
- if (OwnADC)
- {
- data0 = ioread32(base_addr+S3C2410_ADCDAT0);
- data1 = ioread32(base_addr+S3C2410_ADCDAT1);
- xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;
- yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
- count++;
- if (count < (1<<2))
- {
- //如果连续采样次数小于4,就从新启动ADC,启动连续转化,这时就退出等待中断模式,进而没有
- //TS中断产生,OwnADC这个变量就会始终为1
- iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
- iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
- }
- //这里是有一个策略,就是延迟,防止屏幕抖动,造成的误判,延迟时间时1个jiffies大概是10ms的样子
- //当ADC转化次数大于4就会停止在一个时间滴答内是停止ADC工作的
- else
- {
-
- mod_timer(&touch_timer, jiffies+1);
-
- //设置等待中断模式
- //ADTSC的UD_SEN位为0表示等待触摸屏释放中断
- iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
- }
- }
- return IRQ_HANDLED;
- }
- /***************************************************
- 设备初始化
- ***************************************************/
- static int __init ts_init(void)
- {
- struct input_dev *input_dev;
- adc_clock = clk_get(NULL, "adc");
- if (!adc_clock)
- {
- printk(KERN_ERR "failed to get adc clock source\n");
- return -ENOENT;
- }
- clk_enable(adc_clock);
- printk("success to get adc clock source\n");
- //ioremap va to kernel
- base_addr=ioremap(S3C2410_PA_ADC,0x20);
- if (base_addr == NULL)
- {
- printk(KERN_ERR "Failed to remap register block\n");
- return -ENOMEM;
- }
- //ADCCON设置 设置预分频使能 分频值为255
- iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);
- //iowrite32((1<<14)|(0xff)<<6),base_addr);
- iowrite32(0xffff, base_addr+S3C2410_ADCDLY);
- /*初始化设置ADCTSC为等待中断状态*/
- //iowrite32(0xD3, base_addr+S3C2410_ADCTSC);
- iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
- /*申请中断*/
- //request_irq(IRQ_ADC,stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM,ts_name,dev);
- //request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, ts_name, dev);
-
- /*input设备*/
- input_dev=input_allocate_device();
-
-
- if (!input_dev)
- {
- printk(KERN_ERR "Unable to allocate the input device !!\n");
- return -ENOMEM;
- }
- /*确定分配成功后才开始进行赋值操作*/
- dev = input_dev;
- dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
- dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
- input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);
- input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);
- /*这些信息在驱动挂载后会出现在/proc/bus/input/device*/
- dev->name = ts_name;
- dev->id.bustype = BUS_RS232;
- dev->id.vendor = 0xDEAD;
- dev->id.product = 0xBEEF;
- dev->id.version = S3C2410TSVERSION;
- //先有设备再有中断申请,否则中断申请函数中有个参数没有办法确定,导致中断申请失败
- if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, ts_name, dev))
- {
- printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
- iounmap(base_addr);
- return -EIO;
- }
- if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, ts_name, dev))
- {
- printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");
- iounmap(base_addr);
- return -EIO;
- }
- /*将触摸屏设备注册到输入子系统中*/
- input_register_device(dev);
- printk( "%s successfully loaded\n", ts_name);
- return 0;
-
- }
- /***************************************************
- 设备卸载
- ***************************************************/
- static void __exit ts_exit(void)
- {
- disable_irq(IRQ_ADC);
- disable_irq(IRQ_TC);
- free_irq(IRQ_TC,dev);
- free_irq(IRQ_ADC,dev);
- if (adc_clock)
- {
- clk_disable(adc_clock);
- clk_put(adc_clock);
- adc_clock = NULL;
- }
- input_unregister_device(dev);
- iounmap(base_addr);
- }
- module_init(ts_init);
- module_exit(ts_exit);