触摸屏驱动
硬件配置
硬件原理
ADC AND TOUCH SCREEN INTERFACE SPECIAL REGISTERS
程序框架
软件架构:输入子系统
优化措施
①问题:ADC转换出来的值变化太大,不稳定;
原因1:触摸屏被"触摸",触摸点电压值尚未稳定,已经被ADC转换成数字量;
改善1:设置ADC转换延时(reg:ADCDLY),把延时值设为最大;
原因2:假设在ADC转换结束之前,触摸屏已"触摸松开",那么转换出来的值将不正确;
改善2:ADC转换完成,判断触摸屏状态,如果已经"松开",则认为转换无效,丢弃已转换数据;
②为了使ADC转换出来的数据稳定可靠,可增加软件滤波算法;
驱动程序
driver.c
1 /*
2 * 参考s3c2410_ts.c
3 */
4
5 #include <linux/errno.h>
6 #include <linux/kernel.h>
7 #include <linux/module.h>
8 #include <linux/slab.h>
9 #include <linux/input.h>
10 #include <linux/init.h>
11 #include <linux/serio.h>
12 #include <linux/delay.h>
13 #include <linux/platform_device.h>
14 #include <linux/clk.h>
15 #include <asm/io.h>
16 #include <asm/irq.h>
17
18 #include <asm/plat-s3c24xx/ts.h>
19
20 #include <asm/arch/regs-adc.h>
21 #include <asm/arch/regs-gpio.h>
22
23
24 struct s3c_ts_regs{
25 unsigned long ADCCON;
26 unsigned long ADCTSC;
27 unsigned long ADCDLY;
28 unsigned long ADCDAT0;
29 unsigned long ADCDAT1;
30 unsigned long ADCUPDN;
31 };
32
33 static struct timer_list ts_timer;
34 static struct input_dev *ts_dev;
35 static struct s3c_ts_regs *ts_regs;
36
37 static void enter_wait_pen_down(void)
38 {
39 //设置电阻屏"点击"触发中断
40 ts_regs->ADCTSC = 0xd3;
41 }
42
43 static void enter_wait_pen_up(void)
44 {
45 //设置电阻屏"松开"触发中断
46 ts_regs->ADCTSC = 0x1d3;
47 }
48
49 static void enter_measure_xy_mode(void)
50 {
51 //测量"被点击"的坐标
52 ts_regs->ADCTSC &= ~((1<<3) | (1<<2));
53 ts_regs->ADCTSC |= ((1<<3) | (1<<2));
54 }
55
56 static void start_adc(void)
57 {
58 //启动ADC
59 ts_regs->ADCCON |= 1;
60 }
61
62 /*
63 * 电阻屏"点击"或"松开"中断事件处理函数
64 */
65 static irqreturn_t pen_down_up_irq(int irq, void* dev_id)
66 {
67 //电阻屏松开状态
68 if (ts_regs->ADCDAT0 & (1<<15))
69 {
70 //上报"松开"事件
71 input_report_abs(ts_dev, ABS_PRESSURE, 0);
72 input_report_key(ts_dev, BTN_TOUCH, 0);
73 input_sync(ts_dev);
74 enter_wait_pen_down();
75 }
76 //电阻屏点击状态
77 else {
78 //"被点击",测量"被点击"坐标
79 enter_measure_xy_mode();
80 start_adc();
81 }
82 return IRQ_HANDLED;
83 }
84
85 /*
86 * 软件过滤
87 */
88 static int filter_ts(int sadcdat0, int sadcdat1)
89 {
90 //滤波算法
91 return 1;
92 }
93
94 /*
95 * ADC转换中断处理函数:
96 * 条件:ADC中断处理完成,进入中断
97 * 处理:
98 * ①如果已"松开",丢弃转换结果,上报"松开时间",并进入等待"点击";
99 * ②否则,获取转换值:
100 * 连续记录转换出来的坐标值,如果不能连续获得4次坐标值,认为本次获取的坐标值是不准确的,丢弃结果;
101 * 如果成功测得4组数据,则进行软件滤波处理;通过滤波,上报坐标,否则丢弃结果;
102 */
103 static irqreturn_t adc_irq(int irq, void* dev_id)
104 {
105 static int cnt;
106 static int sadcdat0[4], sadcdat1[4];
107 int adcdat0, adcdat1;
108
109 adcdat0 = ts_regs->ADCDAT0;
110 adcdat1 = ts_regs->ADCDAT1;
111 if (ts_regs->ADCDAT0 & (1<<15))
112 {
113 cnt = 0;
114 input_report_abs(ts_dev, ABS_PRESSURE, 0);
115 input_report_key(ts_dev, BTN_TOUCH, 0);
116 input_sync(ts_dev);
117 enter_wait_pen_down();
118 }
119 else {
120 sadcdat0[cnt] = adcdat0 & 0x3ff;
121 sadcdat1[cnt] = adcdat1 & 0x3ff;
122 cnt ++;
123 if (cnt == 4)
124 {
125 if(filter_ts(sadcdat0, sadcdat1))
126 {
127 //printk("(%d, %d)\n", (sadcdat0[0]+sadcdat0[1]+sadcdat0[2]+sadcdat0[3])/4 , (sadcdat1[0]+sadcdat1[1]+sadcdat1[2]+sadcdat1[3])/4);
128 //上报事件
129 input_report_abs(ts_dev, ABS_X, (sadcdat0[0]+sadcdat0[1]+sadcdat0[2]+sadcdat0[3])/4);
130 input_report_abs(ts_dev, ABS_Y, (sadcdat1[0]+sadcdat1[1]+sadcdat1[2]+sadcdat1[3])/4);
131 input_report_abs(ts_dev, ABS_PRESSURE, 1);
132 input_report_key(ts_dev, BTN_TOUCH, 1);
133 input_sync(ts_dev);
134 }
135 cnt = 0;
136 enter_wait_pen_up();
137
138 //启动定时器,处理长按、滑动的情况
139 mod_timer(&ts_timer, jiffies + HZ/100); //定时10ms
140 }
141 else {
142 enter_measure_xy_mode();
143 start_adc();
144 }
145 }
146 return IRQ_HANDLED;
147 }
148
149 /*
150 * 定时器超时处理函数
151 */
152 static void ts_timer_fun(unsigned long data)
153 {
154 if (ts_regs->ADCDAT0 & (1<<15))
155 {
156 //已经"松开"
157 input_report_abs(ts_dev, ABS_PRESSURE, 1);
158 input_report_key(ts_dev, BTN_TOUCH, 1);
159 input_sync(ts_dev);
160 enter_wait_pen_down();
161 }
162 else {
163 //长按、滑动状态,继续测量坐标值
164 enter_measure_xy_mode();
165 start_adc();
166 }
167
168 return;
169 }
170
171 /* 1、出入口函数 */
172 static __init int sc_init(void)
173 {
174 struct clk *clk;
175
176 /* 1.1、分配一个input_dev结构体 */
177 ts_dev = input_allocate_device();
178 /******** 1.1 end ********/
179
180 /* 1.2、设置 */
181 //1.2.1 事件类设置
182 set_bit(EV_KEY, ts_dev->evbit);
183 set_bit(EV_ABS, ts_dev->evbit);
184
185 //1.2.2 具体事件的产生
186 set_bit(BTN_TOUCH, ts_dev->keybit);
187
188 input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0);
189 input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
190 input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
191 /******** 1.2 end ********/
192
193 /* 1.3 注册 */
194 input_register_device(ts_dev);
195 /******** 1.3 end ********/
196
197 /* 1.4 硬件相关的操作 */
198 //1.4.1 使能ADC外设时钟(CLKCON[15])
199 clk = clk_get(NULL, "adc");
200 clk_enable(clk);
201
202 //1.4.2 设置s3c2440的ADC/TS寄存器
203 ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));
204
205 ts_regs->ADCCON = ((1<<14) | (49<<6));//设置预分频
206
207 //模数转换延时,使电压值更稳定
208 ts_regs->ADCDLY = 0xffff;
209
210 //1.4.3 注册中断
211 request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
212 request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);
213 enter_wait_pen_down();
214
215 //1.4.4 使用定时器处理长按、滑动
216 init_timer(&ts_timer);
217 ts_timer.function = ts_timer_fun;
218 add_timer(&ts_timer);
219 /******** 1.4 end ********/
220 return 0;
221 }
222
223 static __exit void sc_exit(void)
224 {
225 free_irq(IRQ_TC, NULL);
226 free_irq(IRQ_ADC, NULL);
227 iounmap(ts_regs);
228 input_unregister_device(ts_dev);
229 input_release_device(ts_dev);
230 del_timer(&ts_timer);
231 return;
232 }
233
234 module_init(sc_init);
235 module_exit(sc_exit);
236
237 MODULE_LICENSE("GPL");
238 /******** 1 end ********/
Makefile
1 KERN_DIR = /work/system/linux-2.6.22.6
2
3 all:
4 make -C $(KERN_DIR) M=`pwd` modules
5
6 clean:
7 make -C $(KERN_DIR) M=`pwd` modules clean
8 rm -rf modules.order
9
10 obj-m += ts.o
调试
pc-linux:
cd /work/system/linux-2.6.22.6/
make menuconfig(屏蔽触摸屏驱动)
make uImage
cp arch/arm/boot/uImage /work/nfs_root/uImage_nots
board-uboot:
nfs 30000000 192.168.0.103:/work/nfs_root/uImage_nots
bootm 30000000
board-linux:
ls /dev/event*
insmod ts.ko
ls /dev/event*
hexdump /dev/event