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