在读者学习本章以及后续章节之前,最好拥有ADC和触摸屏裸机基础,可以参考:ADC和触摸屏编程。
和按键驱动类似,触摸屏也是通过电平来体现按下或松开。因此如果想要写出通用的触摸屏驱动,需要使用输入子系统完成。
考虑到我是用的并不是之前的TINY4412,在此给出下文所分析的文件:
https://files.cnblogs.com/files/Lioker/15_ts.zip
一、电容触摸屏检测原理
此段来源于百度百科,读者可查看:电容式触摸屏。
电容式触摸屏技术是利用人体的电流感应进行工作的。
电容式触摸屏是一块四层复合玻璃屏,玻璃屏的内表面和夹层各涂有一层ITO(导电玻璃),最外层是一薄层矽土玻璃保护层,夹层ITO涂层作为工作面,四个角上引出四个电极,内层ITO为屏蔽层以保证良好的工作环境。
当用户触摸电容屏时,由于人体电场,用户手指和工作面形成一个耦合电容,因为工作面上接有高频信号,于是手指吸收走一个很小的电流,这个电流分别从屏的四个角上的电极中流出,且理论上流经四个电极的电流与手指头到四角的距离成比例,控制器通过对四个电流比例的精密计算得出位置。可以达到99%的精确度,具备小于3ms的响应速度。
二、电容触摸屏控制芯片驱动分析
我使用的开发板iTOP4412的触摸屏IC为ft5406,对应的驱动文件是drivers/input/touchscreen/ft5x06_ts.c。下面来分析此文件。
首先从init()函数分析:
1 static int __init ft5x0x_ts_init(void) 2 { 3 int ret; 4 int type; 5 6 type = get_lcd_type(); 7 8 /* 设置GPIO引脚 */ 9 ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN"); 10 ... 11 gpio_direction_output(EXYNOS4_GPL0(2), 1); 12 13 s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT); 14 gpio_free(EXYNOS4_GPL0(2)); 15 16 mdelay(5); 17 18 printk("==%s: reset==\n", __FUNCTION__); 19 ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3"); 20 ... 21 gpio_direction_output(EXYNOS4_GPX0(3), 0); 22 mdelay(200); 23 /* 给触摸芯片复位 */ 24 gpio_direction_output(EXYNOS4_GPX0(3), 1); 25 26 s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT); 27 gpio_free(EXYNOS4_GPX0(3)); 28 msleep(300); 29 ... 30 /* 注册I2C驱动 */ 31 return i2c_add_driver(&ft5x0x_ts_driver); 32 }
此触摸屏使用的是i2c驱动,我们来看看此驱动结构体定义了什么。
1 static struct i2c_driver ft5x0x_ts_driver = { 2 .probe = ft5x0x_ts_probe, 3 .remove = __devexit_p(ft5x0x_ts_remove), 4 .id_table = ft5x0x_ts_id, 5 .driver = { 6 .name = FT5X0X_NAME, // #define FT5X0X_NAME "ft5x0x_ts" 7 .owner = THIS_MODULE, 8 }, 9 };
接下来,我们来查看probe()函数:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 static int ft5x0x_ts_probe(struct i2c_client *client, const struct i2c_device_id *id) 2 { 3 struct ft5x0x_i2c_platform_data *pdata; 4 struct ft5x0x_ts_data *ts; 5 struct input_dev *input_dev; 6 unsigned char val; 7 int err = -EINVAL; 8 /* 判断是否为i2c设备 */ 9 if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 10 ... 11 } 12 /* 分配空间,此结构体用于存储坐标, 压力值, 事件 */ 13 ts = kzalloc(sizeof(*ts), GFP_KERNEL); 14 ... 15 /* 获取dev的平台数据 */ 16 pdata = client->dev.platform_data; 17 18 ts->screen_max_x = pdata->screen_max_x; 19 ts->screen_max_y = pdata->screen_max_y; 20 ts->pressure_max = pdata->pressure_max; 21 /* 设置引脚为中断模式 */ 22 ts->gpio_irq = pdata->gpio_irq; 23 if (ts->gpio_irq != -EINVAL) { 24 client->irq = gpio_to_irq(ts->gpio_irq); 25 } else { 26 goto exit_no_pdata; 27 } 28 if (pdata->irq_cfg) { 29 s3c_gpio_cfgpin(ts->gpio_irq, pdata->irq_cfg); 30 s3c_gpio_setpull(ts->gpio_irq, S3C_GPIO_PULL_NONE); 31 } 32 33 ts->gpio_wakeup = pdata->gpio_wakeup; 34 ts->gpio_reset = pdata->gpio_reset; 35 /* 创建工作队列 */ 36 INIT_WORK(&ts->work, ft5x0x_ts_pen_irq_work); 37 this_client = client; 38 i2c_set_clientdata(client, ts); 39 40 ts->queue = create_singlethread_workqueue(dev_name(&client->dev)); 41 ... 42 /* 分配设置注册输入子系统 */ 43 input_dev = input_allocate_device(); 44 ts->input_dev = input_dev; 45 46 set_bit(EV_SYN, input_dev->evbit); /* 同步事件 */ 47 set_bit(EV_ABS, input_dev->evbit); /* 绝对位移事件,存储坐标 */ 48 set_bit(EV_KEY, input_dev->evbit); /* 按键事件 */ 49 50 #ifdef CONFIG_FT5X0X_MULTITOUCH 51 set_bit(ABS_MT_TRACKING_ID, input_dev->absbit); 52 set_bit(ABS_MT_TOUCH_MAJOR, input_dev->absbit); 53 set_bit(ABS_MT_WIDTH_MAJOR, input_dev->absbit); 54 set_bit(ABS_MT_POSITION_X, input_dev->absbit); 55 set_bit(ABS_MT_POSITION_Y, input_dev->absbit); 56 57 input_set_abs_params(input_dev, ABS_MT_POSITION_X, 0, ts->screen_max_x, 0, 0); 58 input_set_abs_params(input_dev, ABS_MT_POSITION_Y, 0, ts->screen_max_y, 0, 0); 59 input_set_abs_params(input_dev, ABS_MT_TOUCH_MAJOR, 0, ts->pressure_max, 0, 0); 60 input_set_abs_params(input_dev, ABS_MT_WIDTH_MAJOR, 0, 200, 0, 0); 61 input_set_abs_params(input_dev, ABS_MT_TRACKING_ID, 0, FT5X0X_PT_MAX, 0, 0); 62 #else 63 set_bit(ABS_X, input_dev->absbit); /* 绝对位移事件中的x坐标 */ 64 set_bit(ABS_Y, input_dev->absbit); /* 绝对位移事件中的y坐标 */ 65 set_bit(ABS_PRESSURE, input_dev->absbit); /* 绝对位移事件中的压力值 */ 66 set_bit(BTN_TOUCH, input_dev->keybit); /* 按键事件中的触摸屏事件 */ 67 /* 设置x, y, 压力值的范围和初始值 */ 68 input_set_abs_params(input_dev, ABS_X, 0, ts->screen_max_x, 0, 0); 69 input_set_abs_params(input_dev, ABS_Y, 0, ts->screen_max_y, 0, 0); 70 input_set_abs_params(input_dev, ABS_PRESSURE, 0, ts->pressure_max, 0 , 0); 71 #endif 72 73 input_dev->name = FT5X0X_NAME; 74 input_dev->id.bustype = BUS_I2C; 75 input_dev->id.vendor = 0x12FA; 76 input_dev->id.product = 0x2143; 77 input_dev->id.version = 0x0100; 78 79 err = input_register_device(input_dev); 80 ... 81 /* 读取芯片硬件信息 */ 82 msleep(3); 83 err = ft5x0x_read_fw_ver(&val); 84 85 err = request_irq(client->irq, ft5x0x_ts_interrupt, IRQ_TYPE_EDGE_FALLING, "ft5x0x_ts", ts); 86 disable_irq(client->irq); 87 dev_info(&client->dev, "Firmware version 0x%02x\n", val); 88 /* 支持休眠功能 */ 89 #ifdef CONFIG_HAS_EARLYSUSPEND 90 ts->early_suspend.level = EARLY_SUSPEND_LEVEL_BLANK_SCREEN;//EARLY_SUSPEND_LEVEL_DISABLE_FB + 1; 91 ts->early_suspend.suspend = ft5x0x_ts_suspend; 92 ts->early_suspend.resume = ft5x0x_ts_resume; 93 register_early_suspend(&ts->early_suspend); 94 #endif 95 96 enable_irq(client->irq); 97 98 dev_info(&client->dev, "FocalTech ft5x0x TouchScreen initialized\n"); 99 100 return err; 101 }
probe()函数所做的事情主要有:
1. 判断是否为i2c设备
2. 分配并设置struct ft5x0x_ts_data,此结构体中存储有最大y坐标,最大压力值,工作队列等。
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 struct ft5x0x_ts_data { 2 struct input_dev *input_dev; 3 struct ft5x0x_event event; 4 5 uint32_t gpio_irq; 6 uint32_t gpio_wakeup; 7 uint32_t gpio_reset; 8 9 int screen_max_x; 10 int screen_max_y; 11 int pressure_max; 12 13 struct work_struct work; 14 struct workqueue_struct *queue; 15 16 #ifdef CONFIG_HAS_EARLYSUSPEND 17 struct early_suspend early_suspend; 18 #endif 19 };
3. 设置中断
4. 初始化工作队列(工作队列可以休眠)
5. 分配、设置并注册input_device
当有触摸屏事件发生时,会跳转到probe()函数中注册的中断处理函数ft5x0x_ts_interrupt()中执行,由于在probe()函数中设置了工作队列,因此中断函数会跳转至工作队列函数ft5x0x_ts_pen_irq_work():
1 static irqreturn_t ft5x0x_ts_interrupt(int irq, void *dev_id) { 2 struct ft5x0x_ts_data *ts = dev_id; 3 4 disable_irq_nosync(this_client->irq); 5 6 if (!work_pending(&ts->work)) { 7 queue_work(ts->queue, &ts->work); 8 } 9 10 return IRQ_HANDLED; 11 }
由于i2c读取函数底层可能拥有休眠操作,但是中断中并不允许休眠,因此需要设置工作队列。
在工作队列函数中,首先完成读取x,y和压力值等参数的工作,之后完成参数的上报工作。
1 static int ft5x0x_read_data(struct ft5x0x_ts_data *ts) { 2 struct ft5x0x_event *event = &ts->event; 3 u8 buf[64] = { 0 }; 4 int ret; 5 ... 6 ret = ft5x0x_i2c_rxdata(buf, 7); 7 ... 8 memset(event, 0, sizeof(struct ft5x0x_event)); 9 event->touch_point = buf[2] & 0x0F; 10 11 if (!event->touch_point) { 12 ft5x0x_ts_release(ts); 13 return 1; 14 } 15 /* 判断有几个点按下,也就是支持多指触控 */ 16 #ifdef CONFIG_FT5X0X_MULTITOUCH 17 switch (event->touch_point) { 18 case 10: 19 event->x[9] = (s16)(buf[57] & 0x0F)<<8 | (s16)buf[58]; 20 event->y[9] = (s16)(buf[59] & 0x0F)<<8 | (s16)buf[60]; 21 case 9: 22 event->x[8] = (s16)(buf[51] & 0x0F)<<8 | (s16)buf[52]; 23 event->y[8] = (s16)(buf[53] & 0x0F)<<8 | (s16)buf[54]; 24 case 8: 25 event->x[7] = (s16)(buf[45] & 0x0F)<<8 | (s16)buf[46]; 26 event->y[7] = (s16)(buf[47] & 0x0F)<<8 | (s16)buf[48]; 27 case 7: 28 event->x[6] = (s16)(buf[39] & 0x0F)<<8 | (s16)buf[40]; 29 event->y[6] = (s16)(buf[41] & 0x0F)<<8 | (s16)buf[42]; 30 case 6: 31 event->x[5] = (s16)(buf[33] & 0x0F)<<8 | (s16)buf[34]; 32 event->y[5] = (s16)(buf[35] & 0x0F)<<8 | (s16)buf[36]; 33 case 5: 34 event->x[4] = (s16)(buf[0x1b] & 0x0F)<<8 | (s16)buf[0x1c]; 35 event->y[4] = (s16)(buf[0x1d] & 0x0F)<<8 | (s16)buf[0x1e]; 36 case 4: 37 event->x[3] = (s16)(buf[0x15] & 0x0F)<<8 | (s16)buf[0x16]; 38 event->y[3] = (s16)(buf[0x17] & 0x0F)<<8 | (s16)buf[0x18]; 39 //printk("x:%d, y:%d\n", event->x[3], event->y[3]); 40 case 3: 41 event->x[2] = (s16)(buf[0x0f] & 0x0F)<<8 | (s16)buf[0x10]; 42 event->y[2] = (s16)(buf[0x11] & 0x0F)<<8 | (s16)buf[0x12]; 43 //printk("x:%d, y:%d\n", event->x[2], event->y[2]); 44 case 2: 45 event->x[1] = (s16)(buf[0x09] & 0x0F)<<8 | (s16)buf[0x0a]; 46 event->y[1] = (s16)(buf[0x0b] & 0x0F)<<8 | (s16)buf[0x0c]; 47 //printk("x:%d, y:%d\n", event->x[1], event->y[1]); 48 case 1: 49 event->x[0] = (s16)(buf[0x03] & 0x0F)<<8 | (s16)buf[0x04]; 50 event->y[0] = (s16)(buf[0x05] & 0x0F)<<8 | (s16)buf[0x06]; 51 //printk("x:%d, y:%d\n", event->x[0], event->y[0]); 52 break; 53 default: 54 printk("%s: invalid touch data, %d\n", __func__, event->touch_point); 55 return -1; 56 } 57 #else 58 if (event->touch_point == 1) { 59 event->x[0] = (s16)(buf[0x03] & 0x0F)<<8 | (s16)buf[0x04]; 60 event->y[0] = (s16)(buf[0x05] & 0x0F)<<8 | (s16)buf[0x06]; 61 } 62 #endif 63 64 event->pressure = 200; 65 66 return 0; 67 } 68 69 static void ft5x0x_ts_report(struct ft5x0x_ts_data *ts) { 70 struct ft5x0x_event *event = &ts->event; 71 int x, y; 72 int i = 0; 73 /* 上报事件 */ 74 if (event->touch_point == 1) { 75 if (swap_xy) { 76 x = event->y[i]; 77 y = event->x[i]; 78 } else { 79 x = event->x[i]; 80 y = event->y[i]; 81 } 82 83 if (scal_xy) { 84 x = (x * ts->screen_max_x) / TOUCH_MAX_X; 85 y = (y * ts->screen_max_y) / TOUCH_MAX_Y; 86 } 87 88 input_report_abs(ts->input_dev, ABS_X, x); 89 input_report_abs(ts->input_dev, ABS_Y, y); 90 input_report_abs(ts->input_dev, ABS_PRESSURE, event->pressure); 91 } 92 93 input_report_key(ts->input_dev, BTN_TOUCH, 1); 94 95 input_sync(ts->input_dev); 96 } 97 98 static void ft5x0x_ts_pen_irq_work(struct work_struct *work) { 99 struct ft5x0x_ts_data *ts = container_of(work, struct ft5x0x_ts_data, work); 100 101 if (!ft5x0x_read_data(ts)) { 102 ft5x0x_ts_report(ts); 103 } 104 105 enable_irq(this_client->irq); 106 }
下面我们来总结一下整体过程:
1. 设置GPIO引脚
2. 注册i2c设备驱动
3. 分配、设置和注册struct input_dev
4. 设置、注册触摸屏中断,注册触摸屏中断函数底半部函数
5. 中断函数中调用工作队列
6. 工作队列中读取数据后上报数据
下一章我们根据分析的文件来自己实现触摸屏驱动。
三、自己实现触摸屏驱动
触摸屏驱动源代码:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 #include2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 #include 9 #include 10 #include 11 #include 12 #include 13 #include 14 #include 15 16 #include 17 #include 18 19 #include 20 #include 21 22 #include 23 24 static struct work_struct wq; 25 26 struct ft5x0x_event { 27 int touch_point; 28 u16 x[2]; 29 u16 y[2]; 30 u16 pressure; 31 }; 32 33 int TOUCH_MAX_X = 1024; 34 int TOUCH_MAX_Y = 768; 35 36 static struct input_dev *ftinput; 37 38 static struct i2c_client *ft5x0x_client; 39 40 static int swap_xy; 41 static int touch_size; 42 43 static void ft5x0x_release(void) 44 { 45 input_report_abs(ftinput, ABS_PRESSURE, 0); 46 input_report_key(ftinput, BTN_TOUCH, 0); 47 input_sync(ftinput); 48 } 49 50 static void ft5x0x_report(struct ft5x0x_event *event) 51 { 52 int x, y; 53 int i = 0; 54 55 if (swap_xy) { 56 x = event->y[i]; 57 y = event->x[i]; 58 } 59 else { 60 x = event->x[i]; 61 y = event->y[i]; 62 } 63 64 input_report_abs(ftinput, ABS_X, x); 65 input_report_abs(ftinput, ABS_Y, y); 66 input_report_abs(ftinput, ABS_PRESSURE, event->pressure); 67 68 input_report_key(ftinput, BTN_TOUCH, 1); 69 70 input_sync(ftinput); 71 } 72 73 74 static int master_rx(char *rxdata, int length) { 75 int ret; 76 struct i2c_msg msgs[] = { 77 { 78 .addr = ft5x0x_client->addr, 79 .flags = 0, 80 .len = 1, 81 .buf = rxdata, 82 }, 83 { 84 .addr = ft5x0x_client->addr, 85 .flags = I2C_M_RD, 86 .len = length, 87 .buf = rxdata, 88 }, 89 }; 90 91 ret = i2c_transfer(ft5x0x_client->adapter, msgs, 2); 92 if (ret < 0) 93 printk(KERN_ERR "i2c_transfer error\n"); 94 95 return ret; 96 } 97 98 static int ft5x0x_read_data(struct ft5x0x_event *event) 99 { 100 int ret; 101 u8 buf[64] = { 0 }; 102 103 ret = master_rx(buf, 7); 104 105 memset(event, 0, sizeof(struct ft5x0x_event)); 106 107 event->touch_point = buf[2] & 0x0F; 108 if (!event->touch_point) { 109 ft5x0x_release(); 110 return 1; 111 } 112 113 event->x[0] = (s16)(buf[0x03] & 0x0F)<<8 | (s16)buf[0x04]; 114 event->y[0] = (s16)(buf[0x05] & 0x0F)<<8 | (s16)buf[0x06]; 115 116 event->pressure = 200; 117 118 return 0; 119 } 120 121 static void i2c_wq_irq(unsigned long arg) 122 { 123 struct ft5x0x_event event; 124 125 if (!ft5x0x_read_data(&event)) { 126 ft5x0x_report(&event); 127 } 128 129 printk("(%d, %d)\n", event.x[0], event.y[0]); 130 } 131 132 static irqreturn_t ft5x0x_irq(int irq, void *arg) 133 { 134 schedule_work(&wq); 135 return IRQ_HANDLED; 136 } 137 138 static int read_device_info(u8 addr, u8 *val) 139 { 140 int ret; 141 u8 buf = { 0 }; 142 struct i2c_msg msgs[2]; 143 144 msgs[0].addr = ft5x0x_client->addr; 145 msgs[0].buf = &buf; 146 msgs[0].flags = 0; 147 msgs[0].len = 1; 148 149 msgs[1].addr = ft5x0x_client->addr; 150 msgs[1].buf = &buf; 151 msgs[1].flags = 1; 152 msgs[1].len = 1; 153 154 buf = addr; 155 ret = i2c_transfer(ft5x0x_client->adapter, msgs, 2); 156 if (ret < 0) 157 printk(KERN_ERR "i2c_transfer addr 0x%02x error!", buf); 158 else 159 *val = buf; 160 161 return ret; 162 } 163 164 static int device_info(unsigned char *val) 165 { 166 int ret; 167 168 ret = read_device_info(0xA6, val); 169 170 printk("device_info: %d\n",*val); 171 172 return ret; 173 } 174 175 static int ft5x0x_probe(struct i2c_client *client, const struct i2c_device_id *id) 176 { 177 int ret; 178 ft5x0x_client = client; 179 unsigned char val = 0; 180 181 /* 设置中断引脚 */ 182 client->irq = gpio_to_irq(EXYNOS4_GPX0(4)); 183 s3c_gpio_cfgpin(client->irq, S3C_GPIO_SFN(0xf)); 184 s3c_gpio_setpull(client->irq, S3C_GPIO_PULL_NONE); 185 186 /* 分配设置input_device */ 187 ftinput = input_allocate_device(); 188 189 set_bit(EV_ABS, ftinput->evbit); 190 set_bit(EV_KEY, ftinput->evbit); 191 set_bit(EV_SYN, ftinput->evbit); 192 193 set_bit(ABS_X, ftinput->absbit); 194 set_bit(ABS_Y, ftinput->absbit); 195 set_bit(ABS_PRESSURE, ftinput->absbit); 196 set_bit(BTN_TOUCH, ftinput->keybit); 197 198 input_set_abs_params(ftinput, ABS_X, 0, 768, 0, 0); 199 input_set_abs_params(ftinput, ABS_Y, 0, 1024, 0, 0); 200 input_set_abs_params(ftinput, ABS_PRESSURE, 0, 255, 0 , 0); 201 202 ftinput->name = "ft5x0x"; 203 ftinput->id.bustype = BUS_I2C; 204 205 ret = input_register_device(ftinput); 206 if (ret) { 207 printk(KERN_ERR "input_register_device error\n"); 208 input_free_device(ftinput); 209 } 210 211 ret = request_irq(client->irq, ft5x0x_irq, IRQ_TYPE_EDGE_FALLING, "ft5x0x", NULL); 212 if (ret < 0) { 213 printk(KERN_ERR "request_irq %d error\n", client->irq); 214 goto exit_irq_request_failed; 215 } 216 217 /* 注册中断底半部 */ 218 INIT_WORK(&wq, i2c_wq_irq); 219 220 device_info(&val); 221 222 return 0; 223 224 exit_irq_request_failed: 225 input_unregister_device(ftinput); 226 227 return ret; 228 } 229 230 static int ft5x0x_remove(struct i2c_client *client) 231 { 232 /* 镜像注销 */ 233 input_unregister_device(ftinput); 234 cancel_work_sync(&wq); 235 client->irq = gpio_to_irq(EXYNOS4_GPX0(4)); 236 free_irq(client->irq, NULL); 237 238 return 0; 239 } 240 241 static const struct i2c_device_id ft5x0x_table[] = { 242 { "ft5x0x", 0 }, 243 { }, 244 }; 245 246 static struct i2c_driver ft5x0x_driver = { 247 .driver = { 248 .name = "ft5x0x", 249 .owner = THIS_MODULE, 250 }, 251 .probe = ft5x0x_probe, 252 .remove = __devexit_p(ft5x0x_remove), 253 .id_table = ft5x0x_table, 254 }; 255 256 static int ft5x0x_init(void) 257 { 258 int ret; 259 ret = gpio_request(EXYNOS4_GPL0(2), "TP1_EN"); 260 if (ret) 261 printk(KERN_ERR "failed to request TP1_EN for I2C control\n"); 262 263 gpio_direction_output(EXYNOS4_GPL0(2), 1); 264 265 s3c_gpio_cfgpin(EXYNOS4_GPL0(2), S3C_GPIO_OUTPUT); 266 gpio_free(EXYNOS4_GPL0(2)); 267 268 mdelay(5); 269 270 ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3"); 271 if (ret) { 272 gpio_free(EXYNOS4_GPX0(3)); 273 ret = gpio_request(EXYNOS4_GPX0(3), "GPX0_3"); 274 if (ret) { 275 printk(KERN_ERR "failed to request GPX0_3 \n"); 276 } 277 } 278 gpio_direction_output(EXYNOS4_GPX0(3), 0); 279 mdelay(200); 280 281 gpio_direction_output(EXYNOS4_GPX0(3), 1); 282 283 s3c_gpio_cfgpin(EXYNOS4_GPX0(3), S3C_GPIO_OUTPUT); 284 gpio_free(EXYNOS4_GPX0(3)); 285 msleep(300); 286 287 touch_size = 1; 288 swap_xy = 1; 289 290 return i2c_add_driver(&ft5x0x_driver); 291 } 292 293 static void ft5x0x_exit(void) 294 { 295 i2c_del_driver(&ft5x0x_driver); 296 } 297 298 module_init(ft5x0x_init); 299 module_exit(ft5x0x_exit); 300 301 MODULE_LICENSE("GPL");
Makefile:
![](http://img.e-com-net.com/image/info8/b8d97b5613f94ed2ba791cad57d0b2ed.gif)
![](http://img.e-com-net.com/image/info8/2f88dd3f1cd145f59c0e47b51acdbd4b.gif)
1 KERN_DIR = /work/itop4412/tools/linux-3.5 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
由于触摸屏驱动已经被编译到了内核,我们要先取消它:
$ make menuconfig
Device Drivers --->
Input device support --->
Touchscreens --->
去掉FT5X0X based touchscreens
我并没有编写i2c_client层,而是在mach-itop4412.c中进行更改:
在第2078行代码的基础上,加入#else内容:
#if defined(CONFIG_TOUCHSCREEN_FT5X0X) { I2C_BOARD_INFO("ft5x0x_ts", 0x70>>1), .irq = IRQ_EINT(4), .platform_data = &ft5x0x_pdata, }, /* 加入的代码 */ #else { I2C_BOARD_INFO("ft5x0x", 0x70 >> 1), }, #endif
在重新编译烧写内核后,insmod自己的触摸屏驱动,点击屏幕可发现如下现象:
下一章 十六、USB驱动