学习笔记 --- LINUX触摸屏原理与驱动分析

来源:互联网 发布:淘宝网店操作 编辑:程序博客网 时间:2024/05/29 15:36

电阻式触摸屏的原理:


触摸屏定位过程:


输入子系统之前已经分析了一个按键,这里写一个触摸屏,也是一个输入,可以用到输入子系统,我们只要实现device端就可以了,按照以下步骤:

1 分配

2 设置

3 注册

4 硬件设置

前面三步都是软件操作,后面就是配置硬件平台的寄存器,看代码:

#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 struct timer_list ts_timer;//等待按下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;}//进入测量xy电压模式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 int s3c_filter_ts(int x[], int y[]){#define ERR_LIMIT 10int avr_x, avr_y;int det_x, det_y;avr_x = (x[0] + x[1])/2;avr_y = (y[0] + y[1])/2;det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))return 0;avr_x = (x[1] + x[2])/2;avr_y = (y[1] + y[2])/2;det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))return 0;return 1;}//处理长按或滑动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();}}//触屏中断处理static irqreturn_t pen_down_up_irq(int irq, void *dev_id){if (s3c_ts_regs->adcdat0 & (1<<15)) //如果是松开时产生的中断{//printk("pen up\n");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  //按下时产生的中断{//printk("pen down\n");//enter_wait_pen_up_mode();enter_measure_xy_mode();//按下触屏,测量xy电压start_adc();}return IRQ_HANDLED;}//xy电压测完,产生中断static irqreturn_t adc_irq(int irq, void *dev_id){static int cnt = 0;static int x[4], y[4];int adcdat0, adcdat1;/* 优化措施2: 如果ADC完成时, 发现触摸笔已经松开, 则丢弃此次结果 */adcdat0 = s3c_ts_regs->adcdat0;adcdat1 = s3c_ts_regs->adcdat1;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();}else{// printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);/* 优化措施3: 多次测量求平均值 */x[cnt] = adcdat0 & 0x3ff;y[cnt] = adcdat1 & 0x3ff;++cnt;if (cnt == 4){/* 优化措施4: 软件过滤 */if (s3c_filter_ts(x, y)){//printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);input_report_key(s3c_ts_dev, BTN_TOUCH, 1);input_sync(s3c_ts_dev);}cnt = 0;enter_wait_pen_up_mode();/* 启动定时器处理长按/滑动的情况 */mod_timer(&ts_timer, jiffies + HZ/100);  //100ms定时}else{enter_measure_xy_mode();start_adc();}}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);/* 优化措施1:  * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断 */s3c_ts_regs->adcdly = 0xffff;/* 优化措施5: 使用定时器处理长按,滑动的情况 *  */init_timer(&ts_timer);ts_timer.function = s3c_ts_timer_function;add_timer(&ts_timer);enter_wait_pen_down_mode();  //最开始等待按下return 0;}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);}module_init(s3c_ts_init);module_exit(s3c_ts_exit);MODULE_LICENSE("GPL");


0 0