首页 > 代码库 > 触摸屏驱动三部曲之输入子系统
触摸屏驱动三部曲之输入子系统
一.触摸屏系统框架
1.框架代码(具体细节处理,见下节代码)
#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 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 irqreturn_t pen_down_up_irq(int irq, void *dev_id) { if (s3c_ts_regs->adcdat0 & (1<<15)) { printk("pen up\n"); 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; printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, s3c_ts_regs->adcdat0 & 0x3ff, s3c_ts_regs->adcdat1 & 0x3ff); enter_wait_pen_up_mode(); return IRQ_HANDLED; } static int s3c_ts_init(void) { struct clk* clk; /* 1. 分配一个input_dev结构体 */ s3c_ts_dev = input_allocate_device(); /* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, s3c_ts_dev->evbit); set_bit(EV_ABS, s3c_ts_dev->evbit); /* 2.2 能产生这类事件里的哪些事件 */ 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. 硬件相关的操作 */ /* 4.1 使能时钟(CLKCON[15]) */ clk = clk_get(NULL, "adc"); clk_enable(clk); /* 4.2 设置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. 先设为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); enter_wait_pen_down_mode(); return 0; } static void s3c_ts_exit(void) { free_irq(IRQ_TC, NULL); iounmap(s3c_ts_regs); input_unregister_device(s3c_ts_dev); input_free_device(s3c_ts_dev); } module_init(s3c_ts_init); module_exit(s3c_ts_exit); MODULE_LICENSE("GPL");
2.框架分析
我们首先想一下我们从手碰触摸屏到松开LCD上面有显示都发生了什么
a.手按下产生中断(INT_TC)
b.进入中断处理程序,进行A/D转换
c.在A/D转换结束后产生中断
d.在中断处理函数中上报(input_event)然后就可以输出了。
二.输入子系统
这里我们不必纠结与各种寄存器的设置,下节我会详细的讲的,我们只关心输入子系统的框架。
写好一个符合输入子系统的程序分为四步:
a. 分配一个input_dev结构体s3c_ts_dev = input_allocate_device();b. 设置
/* 2. 设置 */ /* 2.1 能产生哪类事件 */ set_bit(EV_KEY, s3c_ts_dev->evbit); set_bit(EV_ABS, s3c_ts_dev->evbit); /* 2.2 能产生这类事件里的哪些事件 */ 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);c. 注册
input_register_device(s3c_ts_dev);
d. 硬件相关的代码,比如在中断服务程序里上报事件
/* 4. 硬件相关的操作 */ /* 4.1 使能时钟(CLKCON[15]) */ clk = clk_get(NULL, "adc"); clk_enable(clk); /* 4.2 设置S3C2440的ADC/TS寄存器 */ s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs)); s3c_ts_regs->adccon = (1<<14)|(49<<6); request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL); enter_wait_pen_down_mode();
如果进行了上面这个四个步骤,那么你的驱动就可以加到输入子系统里面了。
1.input输入子系统整体框图
2.系统的实现
我们注册了以后都发生了什么事?
a.注册input_dev或input_handler时,会两两比较左边的input_dev和右边的input_handler,根据input_handler的id_table判断这个input_handler能否支持这个input_dev,如果能支持,则调用input_handler的connect函数建立"连接"。
b.分配一个input_handle结构体(这东西就是连接input_dev和input_handler的功臣), input_handle.dev = input_dev; 指向左边的input_dev。input_handle.handler = input_handler; 指向右边的input_handler。然后分别注册。
c 做到这里还差最后一步,就是上报事件(本节的代码没有,可参考下节的代码),为什么要上报事件?
这个很容易理解,你移动下鼠标鼠标得要内核知道吧。当硬件上产生数据的时候,input_event上报交给event进行事件的处理。(event是属于handler阵营的)
参考:韦东山视频第二期
ielife的博客 http://blog.csdn.net/ielife/article/details/7814108