驱动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
转载于:https://www.cnblogs.com/Lwd-linux/p/6272019.html
驱动06.触摸屏驱动程序相关推荐
- linux驱动之触摸屏驱动程序
触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 :当用触摸笔按下时,产生中断.在中断处理函数处理函数中启动ADC转换x,y坐标.ADC结束,产生ADC中断,在ADC中断处理函数里上报(i ...
- Linux下触摸屏驱动程序分析
[摘要: 本文以 linux 3.5--Exynos4412仄台,剖析 触摸屏 驱动焦点内容.Linux下触摸屏驱动(以ft5x06_ts为例)须要懂得以下学问: 1. I2C协定 2. Exynos ...
- 嵌入式Linux系统之I.MX6触摸屏驱动程序TSC2007.C的分析、移植与校准
学习交流加 个人qq: 1126137994 个人微信: liu1126137994 学习交流资源分享qq群: 962535112 今天来记录一下I.MX6开发板移植触摸屏驱动程序的过程分析.在移植驱 ...
- Windows CE操作系统的触摸屏驱动程序模型
欢迎加入Wince技术讨论群QQ#326444254 本文介绍了Windows CE操作系统的触摸屏驱动程序模型,详细阐述嵌入式系统中电阻式触摸屏的Windows CE驱动程序的设计和实现方法. 正文 ...
- 1080驱动此图形驱动程序_如何更新图形驱动程序以获得最佳游戏性能
1080驱动此图形驱动程序 A graphics driver is the software that allow your operating system and programs to use ...
- 佳能gm4080无线服务器安装,佳能Canon PIXMA GM4080打印机驱动(佳能打印机驱动程序)V1.01 正式版...
佳能Canon PIXMA GM4080打印机驱动(佳能打印机驱动程序)是一款很好用的针对佳能Canon PIXMA GM4080打印机推出的专业驱动程序.这款佳能Canon PIXMA GM4080 ...
- 摄像头驱动_摄像头驱动程序必需的11个ioctl及摄像头数据的获取过程
摄像头驱动_摄像头驱动程序必需的11个ioctl及摄像头数据的获取过程 根据虚拟驱动vivi的使用过程彻底分析摄像头驱动 // 1~2都是在v4l2_open里调用 1. open 2. ioctl( ...
- Linux驱动编程(驱动程序基石)(上)
一.休眠与唤醒 要休眠的线程,放在 wq 队列里,中断处理函数从 wq 队列里把它取出来唤醒.所以,我们要做这几件事: ① 初始化 wq 队列 ② 在驱动的 read 函数中,调用 wait_even ...
- linux rs232触摸屏驱动程序,Linux下的触摸屏驱动
一.触摸屏理论概述 对于触摸屏驱动,我们主要需要掌握触摸屏驱动代码和应用层测试代码.下面讲的是基于Mini2440的触摸屏驱动,现在的驱动我们都将设备和驱动分离,挂在平台设备总线上,让设备和驱动去匹配 ...
最新文章
- 51nod 1381 硬币游戏 概率
- 跟着百度学PHP[14]-PDO-优化驱动
- 目标函数、损失函数、代价函数
- 真格量化——GFTD策略
- 离谱!诺奖得主被曝40多篇论文造假!
- (计算机组成原理)第二章数据的表示和运算-第二节9:本节习题
- 课程 2B: 制作一款交互性应用
- Asp.net高效导出excel篇之Aspose导出excel
- 差速驱动机器人轮间距校准实验
- t检验(t test)
- 01 - Python 调用outlook发送邮件
- sql语句查询A表有而B表没有的数据
- 冷藏车的热计算机应用,基于CFD的冷链运输车辆车厢微环境动态模拟研究-计算机应用技术专业论文.docx...
- The e200z4 MMU 学习笔记
- GOOGLE HACKS巧妙使用网络搜索的技巧和工具(第二版)已经出版(上)--IT man
- 还 是 你 太 狠 心
- 三个可替代“迅雷”的下载软件,速度超快!
- ccd摄像机基础知识
- 直播视频网站源码,SharedPreference简便写法
- 【矩阵论】04——线性空间——子空间