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