s3c2440触摸屏.docx
《s3c2440触摸屏.docx》由会员分享,可在线阅读,更多相关《s3c2440触摸屏.docx(16页珍藏版)》请在冰豆网上搜索。
s3c2440触摸屏
S3C2440上触摸屏驱动实例开发讲解
(摘自:
建立触摸屏驱动程序my2440_ts.c,首先实现加载和卸载部分,在驱动加载部分,我们主要做的事情是:
启用ADC所需要的时钟、映射IO口、初始化寄存器、申请中断、初始化输入设备、将输入设备注册到输入子系统。
代码如下:
1.#include
2.#include
3.#include
4.#include
5.#include
6.#include
7.#include
8.#include
9.#include
10.
11./*用于保存从平台时钟列表中获取的ADC时钟*/
12.static struct clk *adc_clk;
13./*定义了一个用来保存经过虚拟映射后的内存地址*/
14.static void __iomem *adc_base;
15./*定义一个输入设备来表示我们的触摸屏设备*/
16.static struct input_dev *ts_dev;
17./*设备名称*/
18.#define DEVICE_NAME "my2440_TouchScreen"
19./*定义一个WAIT4INT宏,该宏将对ADC触摸屏控制寄存器进行操作
20.S3C2410_ADCTSC_YM_SEN这些宏都定义在regs-adc.h中*/
21.#define WAIT4INT(x) (((x)<<8) | S3C2410_ADCTSC_YM_SEN |\
22.S3C2410_ADCTSC_YP_SEN |S3C2410_ADCTSC_XP_SEN | S3C2410_ADCTSC_XY_PST(3))
23.
24.static int __init ts_init(void)
25.{
26. int ret;
27.
28./*从平台时钟队列中获取ADC的时钟,这里为什么要取得这个时钟,因为ADC的转换频率跟时钟有关。
29.系统的一些时钟定义在arch/arm/plat-s3c24xx/s3c2410-clock.c中*/
30. adc_clk = clk_get(NULL, "adc");
31. if(!
adc_clk)
32. {
33./*错误处理*/
34. printk(KERN_ERR "falied to find adc clock source\n");
35. return -ENOENT;
36. }
37.
38./*时钟获取后要使能后才可以使用,clk_enable定义在arch/arm/plat-s3c/clock.c中*/
39. clk_enable(adc_clk);
40.
41./*将ADC的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
42.注意:
IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,
43.S3C2410_PA_ADC是ADC控制器的基地址,定义在mach-s3c2410/include/mach/map.h
44.中,0x20是虚拟地址长度大小*/
45. adc_base = ioremap(S3C2410_PA_ADC, 0x20);
46. if(adc_base == NULL)
47. {
48./*错误处理*/
49. printk(KERN_ERR "failed to remap register block\n");
50. ret = -EINVAL;
51. goto err_noclk;
52. }
53.
54./*初始化ADC控制寄存器和ADC 触摸屏控制寄存器*/
55. adc_initialize();
56.
57./*申请ADC中断,AD转换完成后触发。
这里使用共享中断IRQF_SHARED 是因为该中断号在ADC驱动
58.中也使用了,最后一个参数1是随便给的一个值,因为如果不给值设为NULL的话,中断就申请不成功*/
59. ret = request_irq(IRQ_ADC, adc_irq, IRQF_SHARED |\
60.IRQF_SAMPLE_RANDOM, DEVICE_NAME, 1);
61. if(ret)
62. {
63. printk(KERN_ERR "IRQ%d error %d\n", IRQ_ADC, ret);
64. ret = -EINVAL;
65. goto err_nomap;
66. }
67.
68./*申请触摸屏中断,对触摸屏按下或提笔时触发*/
69. ret = request_irq(IRQ_TC, tc_irq, IRQF_SAMPLE_RANDOM, DEVICE_NAME, 1);
70. if(ret)
71. {
72. printk(KERN_ERR "IRQ%d error %d\n", IRQ_TC, ret);
73. ret = -EINVAL;
74. goto err_noirq;
75. }
76.
77./*给输入设备申请空间,input_allocate_device定义在input.h中*/
78. ts_dev = input_allocate_device();
79.
80./*下面初始化输入设备,即给输入设备结构体input_dev的成员设置值。
81.evbit字段用于描述支持的事件,这里支持同步事件、按键事件、绝对坐标事件,
82.BIT宏实际就是对1进行位操作,定义在linux/bitops.h中*/
83. ts_dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);
84.
85./*keybit字段用于描述按键的类型,在input.h中定义了很多,
86.这里用BTN_TOUCH类型来表示触摸屏的点击*/
87. ts_dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);
88.
89./*对于触摸屏来说,使用的是绝对坐标系统。
这里设置该坐标系统中X和Y坐标的最小值和最大值
90.(0-1023范围)ABS_X和ABS_Y就表示X坐标和Y坐标,ABS_PRESSURE就表示触摸屏是按下还是抬起状态*/
91. input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0);
92. input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
93. input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
94.
95./*以下是设置触摸屏输入设备的身份信息,直接在这里写死。
96. 这些信息可以在驱动挂载后在/proc/bus/input/devices中查看到*/
97. ts_dev->name = DEVICE_NAME; /*设备名称*/
98. ts_dev->id.bustype = BUS_RS232; /*总线类型*/
99. ts_dev->id.vendor = 0xDEAD; /*经销商ID号*/
100. ts_dev->id.product = 0xBEEF; /*产品ID号*/
101. ts_dev->id.version = 0x0101; /*版本ID号*/
102.
103./*好了,一些都准备就绪,现在就把 ts_dev触摸屏设备注册到输入子系统中*/
104. input_register_device(ts_dev);
105.
106. return 0;
107.
108./*下面是错误跳转处理*/
109.err_noclk:
110. clk_disable(adc_clk);
111. clk_put(adc_clk);
112.
113.err_nomap:
114. iounmap(adc_base);
115.
116.err_noirq:
117. free_irq(IRQ_ADC, 1);
118.
119. return ret;
120.}
121.
122./*初始化ADC控制寄存器和ADC触摸屏控制寄存器*/
123.static void adc_initialize(void)
124.{
125./*计算结果为(二进制):
111111111000000,再根据数据手册得知
126.此处是将AD转换预定标器值设为255、AD转换预定标器使能有效*/
127. writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF), adc_base S3C2410_ADCCON);
128.
129. /*对ADC开始延时寄存器进行设置,延时值为0xffff*/
130. writel(0xffff, adc_base S3C2410_ADCDLY);
131.
132. /*WAIT4INT宏计算结果为(二进制):
11010011,再根据数据手册得知
133. 此处是将ADC触摸屏控制寄存器设置成等待中断模式*/
134. writel(WAIT4INT(0), adc_base S3C2410_ADCTSC);
135.}
136.
137.static void __exit ts_exit(void)
138.{
139. /*屏蔽和释放中断*/
140. disable_irq(IRQ_ADC);
141. disable_irq(IRQ_TC);
142. free_irq(IRQ_ADC, 1);
143. free_irq(IRQ_TC, 1);
144.
145. /*释放虚拟地址映射空间*/
146. iounmap(adc_base);
147.
148. /*屏蔽和销毁时钟*/
149. if(adc_clk)
150. {
151. clk_disable(adc_clk);
152. clk_put(adc_clk);
153. adc_clk = NULL;
154. }
155.
156. /*将触摸屏设备从输入子系统中注销*/
157. input_unregister_device(ts_dev);
158.}
159.
160.module_init(ts_init);
161.module_exit(ts_exit);
162.
163.MODULE_LICENSE("GPL");
164.MODULE_AUTHOR("Huang Gang");
165.MODULE_DESCRIPTION("My2440 Touch Screen Driver");
3、接下来要做的是,在两个中断服务程序中实现触摸屏状态和坐标的转换。
先看代码,如下:
1./*定义一个外部的信号量ADC_LOCK,因为ADC_LOCK在ADC驱动程序中已申明
2.这样就能保证ADC资源在ADC驱动和触摸屏驱动中进行互斥访问*/
3.extern struct semaphore ADC_LOCK;
4.
5./*做为一个标签,只有对触摸屏操作后才对X和Y坐标进行转换*/
6.static int OwnADC = 0;
7.
8./*用于记录转换后的X坐标值和Y坐标值*/
9.static long xp;
10.static long yp;
11.
12./*用于计数对触摸屏压下或抬起时模拟输入转换的次数*/
13.static int count;
14.
15./*定义一个AUTOPST宏,将ADC触摸屏控制寄存器设置成自动转换模式*/
16.#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN\
17.| S3C2410_ADCTSC_XP_SEN |S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
18.
19.
20./*触摸屏中断服务程序,对触摸屏按下或提笔时触发执行*/
21.static irqreturn_t tc_irq(int irq, void *dev_id)
22.{
23. /*用于记录这一次AD转换后的值*/
24. unsigned long data0;
25. unsigned long data1;
26.
27. /*用于记录触摸屏操作状态是按下还是抬起*/
28. int updown;
29.
30. /*ADC资源可以获取,即上锁*/
31. if (down_trylock(&ADC_LOCK) == 0)
32. {
33. /*标识对触摸屏进行了操作*/
34. OwnADC = 1;
35.
36. /*读取这一次AD转换后的值,注意这次主要读的是状态*/
37. data0 = readl(adc_base S3C2410_ADCDAT0);
38. data1 = readl(adc_base S3C2410_ADCDAT1);
39.
40./*记录这一次对触摸屏是压下还是抬起,该状态保存在数据寄存器的第15位,
41.所以与上 S3C2410_ADCDAT0_UPDOWN*/
42.updown = (!
(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!
(data1 & S3C2410_ADCDAT0_UPDOWN));
43.
44. /*判断触摸屏的操作状态*/
45. if (updown)
46. {
47./*如果是按下状态,则调用touch_timer_fire函数来启动ADC转换,该函数定义后面再讲*/
48. touch_timer_fire(0);
49. }
50. else
51. {
52./*如果是抬起状态,就结束了这一次的操作,所以就释放ADC资源的占有*/
53. OwnADC = 0;
54. up(&ADC_LOCK);
55. }
56. }
57.
58. return IRQ_HANDLED;
59.}
60.
61.static void touch_timer_fire(unsigned long data)
62.{
63. /*用于记录这一次AD转换后的值*/
64. unsigned long data0;
65. unsigned long data1;
66.
67. /*用于记录触摸屏操作状态是按下还是抬起*/
68. int updown;
69.
70. /*读取这一次AD转换后的值,注意这次主要读的是状态*/
71. data0 = readl(adc_base S3C2410_ADCDAT0);
72. data1 = readl(adc_base S3C2410_ADCDAT1);
73.
74./*记录这一次对触摸屏是压下还是抬起,该状态保存在数据寄存器的第15位,
75.所以与上 S3C2410_ADCDAT0_UPDOWN*/
76.updown = (!
(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!
(data1 & S3C2410_ADCDAT0_UPDOWN));
77.
78. /*判断触摸屏的操作状态*/
79. if (updown)
80. {
81. /*如果状态是按下,并且ADC已经转换了就报告事件和数据*/
82. if (count !
= 0)
83. {
84. long tmp;
85.
86. tmp = xp;
87. xp = yp;
88. yp = tmp;
89.
90. xp >>= 2;
91. yp >>= 2;
92.
93.#ifdef CONFIG_TOUCHSCREEN_MY2440_DEBUG
94./*触摸屏调试信息,编译内核时选上此项后,点击触摸屏会在终端上打印出坐标信息*/
95.struct timeval tv;
96.do_gettimeofday(&tv);
97.printk(KERN_DEBUG "T:
%06d, X:
%03ld, Y:
%03ld\n", (int)tv.tv_usec, xp, yp);
98.#endif
99.
100./*报告X、Y的绝对坐标值*/
101.input_report_abs(ts_dev, ABS_X, xp);
102.input_report_abs(ts_dev, ABS_Y, yp);
103.
104./*报告触摸屏的状态,1表明触摸屏被按下*/
105.input_report_abs(ts_dev, ABS_PRESSURE, 1);
106.
107./*报告按键事件,键值为1(代表触摸屏对应的按键被按下)*/
108.input_report_key(ts_dev, BTN_TOUCH, 1);
109.
110./*等待接收方受到数据后回复确认,用于同步*/
111.input_sync(ts_dev);
112.}
113.
114./*如果状态是按下,并且ADC还没有开始转换就启动ADC进行转换*/
115.xp = 0;
116.yp = 0;
117.count = 0;
118./*设置触摸屏的模式为自动转换模式*/
119.writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, adc_base S3C2410_ADCTSC);
120.
121./*启动ADC转换*/
122.writel(readl(adc_base S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,
123. adc_base S3C2410_ADCCON);
124.}
125. else
126. {
127./*否则是抬起状态*/
128.count = 0;
129./*报告按键事件,键值为0(代表触摸屏对应的按键被释放)*/
130.input_report_key(ts_dev, BTN_TOUCH, 0);
131./*报告触摸屏的状态,0表明触摸屏没被按下*/
132.input_report_abs(ts_dev, ABS_PRESSURE, 0);
133./*等待接收方受到数据后回复确认,用于同步*/
134.input_sync(ts_dev);
135./*将触摸屏重新设置为等待中断状态*/
136.writel(WAIT4INT(0), adc_base S3C2410_ADCTSC);
137./*如果触摸屏抬起,就意味着这一次的操作结束,所以就释放ADC资源的占有*/
138.if (OwnADC)
139.{
140.OwnADC = 0;
141.up(&ADC_LOCK);
142.}
143.}
144.}
145.
146./*定义并初始化了一个定时器touch_timer,定时器服务程序为touch_timer_fire*/
147.static struct timer_list touch_timer = TIMER_INITIALIZER(touch_timer_fire, 0, 0);