驱动06.触摸屏驱动程序
1.触摸屏的简介
触摸屏是标准的输入设备,在写驱动程序时采用的之前讲过的输入子系统那套框架。我们无需关心对设备文件的操作,只需关心对硬件寄存器的操作和上报事件即可。
触摸屏是附在LCD上的一层薄膜,并不是我们平时认识的触摸屏,它只是起到确定坐标的作用。
S3C2440提供的触摸屏接口有4种处理模式,分别是:正常转换模式、单独的X/Y位置转换模式、自动X/Y位置转换模式和等待中断模式。本例子中用的是等待中断模式
2.以s3c2410_ts.c为例分析整体框架
2.1 s3c2410ts_init函数
1 static int __init s3c2410ts_init(void) 2 { 3 // init_MUTEX(&gADClock); 4 return platform_driver_register(&s3c2410ts_driver);/*注册s3c2410ts_driver*/ 5 }
1 static struct platform_driver s3c2410ts_driver = { 2 .driver = { 3 .name = "s3c2410-ts", 4 .owner = THIS_MODULE, 5 }, 6 .probe = s3c2410ts_probe, 7 .remove = s3c2410ts_remove, 8 };
如果出现与其同名的平台设备,将调用其probe函数
2.2 s3c2410ts_probe函数
static int __init s3c2410ts_probe(struct platform_device *pdev) { /*使能adc时钟*/ adc_clock = clk_get(NULL, "adc"); clk_enable(adc_clock); //映射adc的地址 base_addr=ioremap(S3C2410_PA_ADC,0x20); //配置GPIO,初始化 s3c2410_ts_connect(); //分配一个input_dev结构体 input_dev = input_allocate_device(); //设置这个结构体 ts.dev = input_dev; //事件类型:按键类、绝对位移类 ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH); //绝对位移类的各种参数:X方向、Y方向、按压类 input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0); input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0); input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0); request_irq(IRQ_ADC,stylus_action,...);//ADC中断 request_irq(IRQ_TC,stylus_updown,...);//触摸屏中断 //注册input_dev结构体 input_register_device(ts.dev); }
2.3 ADC中断处理函数stylus_action
static irqreturn_t stylus_action(int irq, void *dev_id) { unsigned long data0; unsigned long data1; // if (bADCForTS) { data0 = ioread32(base_addr+S3C2410_ADCDAT0); data1 = ioread32(base_addr+S3C2410_ADCDAT1); ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; ts.count++; // bADCForTS = 0; // up(&gADClock); if (ts.count < (1<<ts.shift)) { // if (!down_trylock(&gADClock)) { // bADCForTS = 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); // } } else { mod_timer(&touch_timer, jiffies+1); iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC); } // } return IRQ_HANDLED; }
2.4 触摸屏中断处理函数stylus_updown
1 static irqreturn_t stylus_updown(int irq, void *dev_id) 2 { 3 unsigned long data0; 4 unsigned long data1; 5 int updown; 6 7 data0 = ioread32(base_addr+S3C2410_ADCDAT0); 8 data1 = ioread32(base_addr+S3C2410_ADCDAT1); 9 10 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN)); 11 12 /* TODO we should never get an interrupt with updown set while 13 * the timer is running, but maybe we ought to verify that the 14 * timer isn't running anyways. */ 15 16 if (updown) 17 touch_timer_fire(0); 18 19 return IRQ_HANDLED; 20 }
1 static void touch_timer_fire(unsigned long data) //上报事件 2 { 3 unsigned long data0; 4 unsigned long data1; 5 int updown; 6 7 data0 = ioread32(base_addr+S3C2410_ADCDAT0); 8 data1 = ioread32(base_addr+S3C2410_ADCDAT1); 9 10 updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN)); 11 12 if (updown) { 13 if (ts.count != 0) { 14 long tmp; 15 16 tmp = ts.xp; 17 ts.xp = ts.yp; 18 ts.yp = tmp; 19 20 ts.xp >>= ts.shift; 21 ts.yp >>= ts.shift; 22 23 #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG 24 { 25 struct timeval tv; 26 do_gettimeofday(&tv); 27 printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp); 28 } 29 #endif 30 31 input_report_abs(ts.dev, ABS_X, ts.xp); 32 input_report_abs(ts.dev, ABS_Y, ts.yp); 33 34 input_report_key(ts.dev, BTN_TOUCH, 1); 35 input_report_abs(ts.dev, ABS_PRESSURE, 1); 36 input_sync(ts.dev); 37 } 38 39 ts.xp = 0; 40 ts.yp = 0; 41 ts.count = 0; 42 43 // if (!down_trylock(&gADClock)) { 44 // bADCForTS = 1; 45 iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); 46 iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON); 47 // } 48 } else { 49 ts.count = 0; 50 51 input_report_key(ts.dev, BTN_TOUCH, 0); 52 input_report_abs(ts.dev, ABS_PRESSURE, 0); 53 input_sync(ts.dev); 54 55 iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); 56 } 57 }
2.5 s3c2410ts_remove,s3c2410ts_exit函数做的工作与上述相反。
3 写代码
3.1 框架
(1)分配一个input_dev结构体
(2)设置
(3)硬件相关的操作(难点)
(4)注册
(5)进一步优化(可有可无,有是最好)
--------------------------------------------------------------编辑于2017-01-11 00:56:39
ADC使用的四个步骤:
(1)设置ADCCON寄存器,选择输入信号通道,设置A/D转换器时钟。
(2)设置ADCTSC寄存器,设置其为触摸屏使用。
(3)设置ADCCON寄存器,启动A/D转换。
(4)转换结束时,读取ADCDAT0寄存器获取数值。
1 /*参考s3c2410_ts.c*/ 2 3 #include <linux/errno.h> 4 #include <linux/kernel.h> 5 #include <linux/module.h> 6 #include <linux/slab.h> 7 #include <linux/input.h> 8 #include <linux/init.h> 9 #include <linux/serio.h> 10 #include <linux/delay.h> 11 #include <linux/platform_device.h> 12 #include <linux/clk.h> 13 #include <asm/io.h> 14 #include <asm/irq.h> 15 16 #include <asm/plat-s3c24xx/ts.h> 17 18 #include <asm/arch/regs-adc.h> 19 #include <asm/arch/regs-gpio.h> 20 21 struct s3c_ts_regs{ 22 unsigned long adccon; 23 unsigned long adctsc; 24 unsigned long adcdly; 25 unsigned long adcdat0; 26 unsigned long adcdat1; 27 unsigned long adcupdn; 28 29 }; 30 31 static struct input_dev *s3c_ts_dev; 32 static struct clk *adc_clock; 33 static volatile struct s3c_ts_regs *s3c_ts_regs; 34 static struct timer_list ts_timer; 35 36 static void wait_pen_down_mode() 37 { 38 s3c_ts_regs->adctsc = 0xd3; 39 } 40 41 static void wait_pen_up_mode() 42 { 43 s3c_ts_regs->adctsc = 0x1d3; 44 } 45 46 static void measure_xy_mode() 47 { 48 s3c_ts_regs->adctsc = (1<<3) |(1<<2); 49 } 50 51 static void start_adc() 52 { 53 s3c_ts_regs->adccon |= (1<<0); 54 } 55 56 static int s3c_filter_ts(int x[], int y[]) 57 { 58 #define ERR_LIMIT 10 59 60 int avr_x, avr_y; 61 int det_x, det_y; 62 63 avr_x = (x[0] + x[1])/2; 64 avr_y = (y[0] + y[1])/2; 65 66 det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]); 67 det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]); 68 69 if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) 70 return 0; 71 72 avr_x = (x[1] + x[2])/2; 73 avr_y = (y[1] + y[2])/2; 74 75 det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]); 76 det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]); 77 78 if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT)) 79 return 0; 80 81 return 1; 82 } 83 84 static void s3c_ts_timer_func(unsigned long data) 85 { 86 if (s3c_ts_regs->adcdat0 & (1<<15)) 87 { 88 /*pen up*/ 89 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0); 90 input_report_key(s3c_ts_dev, BTN_TOUCH, 0); 91 input_sync(s3c_ts_dev); 92 wait_pen_down_mode(); 93 } 94 else 95 { 96 /* 测量X/Y坐标 */ 97 measure_xy_mode(); 98 start_adc(); 99 } 100 } 101 102 103 static irqreturn_t tc_irq(int irq, void *dev_id) 104 { 105 if(s3c_ts_regs->adcdat0 & (1<<15)) 106 { 107 /*pen up*/ 108 109 input_report_key(s3c_ts_dev, BTN_TOUCH, 0); 110 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0); 111 input_sync(s3c_ts_dev); 112 wait_pen_down_mode(); 113 114 } 115 else 116 { 117 measure_xy_mode(); 118 start_adc(); 119 } 120 return IRQ_HANDLED; 121 } 122 123 static irqreturn_t adc_irq(int irq, void *dev_id) 124 { 125 static int cnt = 0; 126 static int x[4],y[4]; 127 int adcdat0, adcdat1; 128 129 adcdat0 = s3c_ts_regs->adcdat0; 130 adcdat1 = s3c_ts_regs->adcdat1; 131 132 /*如果发现ADC转换完成后pen up,则丢弃数据*/ 133 if(s3c_ts_regs->adcdat0 & (1<<15)) 134 { /*pen up*/ 135 cnt = 0; 136 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0); 137 input_report_key(s3c_ts_dev, BTN_TOUCH, 0); 138 input_sync(s3c_ts_dev); 139 wait_pen_down_mode(); 140 } 141 else 142 { 143 /*多次测量,取平均值*/ 144 x[cnt] = adcdat0 & 0x3ff; 145 y[cnt] = adcdat1 & 0x3ff; 146 ++cnt; 147 if (cnt == 4) 148 { 149 /*软件过滤*/ 150 if (s3c_filter_ts(x, y)) 151 { 152 //printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4); 153 input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4); 154 input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4); 155 input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1); 156 input_report_key(s3c_ts_dev, BTN_TOUCH, 1); 157 input_sync(s3c_ts_dev); 158 } 159 cnt = 0; 160 wait_pen_up_mode(); 161 /*启动定时器实现长按/滑动的情况*/ 162 mod_timer(&ts_timer, jiffies + HZ/100); 163 164 } 165 else 166 { 167 measure_xy_mode(); 168 start_adc(); 169 } 170 171 172 } 173 return IRQ_HANDLED; 174 } 175 176 177 178 static int s3c_ts_init(void) 179 { /*1.分配一个input_dev结构体*/ 180 s3c_ts_dev = input_allocate_device(); 181 if (!s3c_ts_dev) { 182 printk(KERN_ERR "Unable to allocate the input device !!\n"); 183 return -ENOMEM; 184 } 185 186 /*2 设置这个结构体*/ 187 /*2.1 设置事件类型*/ 188 set_bit(EV_KEY,s3c_ts_dev->evbit); 189 set_bit(EV_ABS,s3c_ts_dev->evbit); 190 191 /*2.2 设置该类型下的哪一个具体事件*/ 192 set_bit(BTN_TOUCH,s3c_ts_dev->keybit); 193 194 input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0); 195 input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0); 196 input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0); 197 198 /*3 注册*/ 199 input_register_device(s3c_ts_dev); 200 201 /*4 硬件相关的操作*/ 202 /*4.1 使能ADC 时钟*/ 203 adc_clock = clk_get(NULL, "adc"); 204 if (!adc_clock) { 205 printk(KERN_ERR "failed to get adc clock source\n"); 206 return -ENOENT; 207 } 208 clk_enable(adc_clock); 209 210 /*4.2 寄存器初始化*/ 211 s3c_ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs)); 212 s3c_ts_regs->adccon = (1<<14) |(49<<6); 213 214 /*4.3 申请中断*/ 215 request_irq(IRQ_TC, tc_irq, IRQF_SAMPLE_RANDOM,"tc", NULL); 216 request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM,"adc", NULL); 217 218 s3c_ts_regs->adcdly = 0xffff;/*待数值稳定后在转换*/ 219 220 init_timer(&ts_timer); 221 ts_timer.function = s3c_ts_timer_func; 222 add_timer(&ts_timer); 223 224 wait_pen_down_mode(); 225 226 return 0; 227 } 228 229 static void s3c_ts_exit(void) 230 { 231 free_irq(IRQ_TC, NULL); 232 free_irq(IRQ_ADC, NULL); 233 iounmap(s3c_ts_regs); 234 input_unregister_device(s3c_ts_dev); 235 input_free_device(s3c_ts_dev); 236 del_timer(&ts_timer); 237 238 } 239 240 module_init(s3c_ts_init); 241 module_exit(s3c_ts_exit); 242 243 244 MODULE_LICENSE("GPL");
--------------------------------------------------------------编辑于2017-01-11 18:21:04