本节主要是说一下触摸屏驱动的编写. 触摸屏输入输入设备,所以我们本次通过输入子系统的方式来实现,输入子系统的框架图如下:
然后,我们看一线电路图的触摸屏部分:
可以看到触摸屏是通过I2C接口进行数据和命令的传输,接在了i2c1上面,然后,同时接了一个中断引脚,这个中断引脚主要是在初始化和有数据时使用,比如当用户按下触摸屏产生信息后,首先会产生一个中断,告诉soc有数据来了,这个我们就可以把相关驱动设置为中断响应方式,而不必用轮询的方式来做,这样可以大大提高代码质量和为CPU减负,当然,其实还有一种途径就是直接通过i2c产生中断,这种方式在STM32用的是比较平常的,这里则没有这么用,接下来,我们需要在mach-tiny4412.c里配置i2c1数组,实际上,友善之臂已经移植好了,然后,我们重点关注驱动本身的实现,通过查看友善之臂提供的文档,发现,其实相关的触摸屏驱动已经给我们写好了,我用的是X710的屏幕,此屏幕友善之臂没有资料,所以,还是按照老套路来,直接查看源代码,来猜测相关信息是什么.
通过查看tiny4412-lcds.c发现如下数组:
static struct { char *name; struct s3cfb_lcd *lcd; int ctp; } tiny4412_lcd_config[] = { { "HD700", &wxga_hd700, 1 }, { "HD701", &wxga_hd700, 1 }, { "S70", &wvga_s70, 1 }, { "S702", &wvga_s70, 1 }, { "S70D", &wvga_s70d, 1 }, { "W50", &wvga_w50, 0 }, { "W101", &wsvga_w101, 1 }, { "X710", &wsvga_x710, CTP_ITE7260 }, { "A97", &xga_a97, 0 }, { "LQ150", &xga_lq150, 1 }, { "L80", &vga_l80, 1 }, { "HD101", &wxga_hd101, 1 }, { "BP101", &wxga_bp101, 1 }, { "HDM", &hdmi_def, 0 }, /* Pls keep it at last */ };
我们用的是X710,所以,数组中的第7项的第3个成员是CTP_ITE7260,那对应的触摸屏驱动应该是和CTP_ITE7260有关的驱动了,通过查看源代码的驱动目录:\drivers\input\touchscreen在这个目录下发现了it7260_mts.c这个文件,这个就是X710对应的触摸屏的驱动程序了,我们make menuconfig,然后在Device Drivers --->Input device support --->Touchscreens --->选中里面的ITE it7260 TouchScreen driver.
之后,为了尽快看到演示效果,本此不写测试程序了,直接让触摸屏驱动打印log,直接查看log对不对就好了,所以,我们需要修改it7260_mts.c这个程序,主要是如下两个地方,如下:
it7260_ts_poscheck() @ it7260_mts.c (1) printk(KERN_DEBUG "it7260: key number %d\n", buf[1]); 改为: printk("it7260: key number %d\n", buf[1]); (2) #if 0 printk("finger %d > (%4d, %4d), event = %d\n", i, ypos[i], xpos[i], event[i]); #endif 改为: if 1 ... #endif
之后再把tiny4412-lcds.c文件里做如下修改:
unsigned int tiny4412_get_ctp(void) { +++ lcd_idx = 7; if (tiny4412_lcd_config[lcd_idx].ctp) return ctp_type; else return CTP_NONE; }
然后make -j4重新编译新的内核,然后把内核放入T卡启动它,就可以看到如下效果:
当然,如果这么简单,就不用写这篇博客了,接下来,让我们自己去实现触摸屏的驱动,因为没有资料,有一些关于寄存器的操作都是参考原来的it7260_mts.c中的内容.相关驱动程序如下(关键地方都给出了注释,所以程序就不讲了):
/** * 本例程是it7260触摸屏驱动程序 * 物理器件这边引出的是i2c接口,所以,这里是以i2c设备驱动来做, * 同时,触摸屏在Linux里定义为输入设备,所以这里有采用输入子系统 */ #include <linux/init.h> #include <linux/module.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/gpio.h> #include <linux/pm.h> #include <plat/gpio-cfg.h> #include <plat/ctouch.h> #include <asm/io.h> #define MULTI_POINT 3 // 3点触摸 #define TS_MAX_HIGHT 600 // 触摸屏的纵向分辨率 #define TS_MAX_WIDTH 1024 // 触摸屏的横向分辨率 #define CMD_BUF 0x20 // command buffer (write only) #define SYS_CMD_BUF 0x40 // systerm command buffer (write only) #define QUERY_BUF 0x80 // query buffer (read only) #define CMD_RSP_BUF 0xA0 // command response buffer (read only) #define SYS_CMD_RSP_BUF 0xC0 // systerm command response buffer (read only) #define POINT_INFO_BUF 0xE0 // point information buffer (read only) struct it7260_ts_object{ int irqno; struct i2c_client *client; struct input_dev *inputdev; struct work_struct work; }; struct it7260_ts_object *it7260_obj; int it7260_i2c_write_reg(struct i2c_client *client, unsigned char buf_index, char *buf, int count) { int ret; struct i2c_msg msg; unsigned char buf1[2]; buf1[0] = buf_index; memcpy(&buf[1], buf, count); msg.addr = client->addr; msg.flags = 0; msg.len = count + 1; msg.buf = buf1; ret = i2c_transfer(client->adapter, &msg, 1); return ret == 1 ? count : ret; } int it7260_i2c_read_reg(struct i2c_client *client, unsigned char buf_index, char *buf, int count) { int ret; struct i2c_msg msg[2]; msg[0].addr = client->addr; msg[0].flags = 0; msg[0].len = 1; msg[0].buf = &buf_index; msg[1].addr = client->addr; msg[1].flags = 1; msg[1].len = count; msg[1].buf = buf; ret = i2c_transfer(client->adapter, msg, 2); return ret == 2 ? count : ret; } static int it7260_identify_capsensor(struct i2c_client *client) { unsigned char buf[16] = {0}; unsigned char query = 0; do { it7260_i2c_read_reg(client, QUERY_BUF, &query, 1); } while (query & 0x01); /* 0x00: the command of identify cap sensor */ buf[0] = 0x00; it7260_i2c_write_reg(client, CMD_BUF, buf, 1); do { it7260_i2c_read_reg(client, QUERY_BUF, &query, 1); } while (query & 0x01); memset(&buf, 0, sizeof(buf)); it7260_i2c_read_reg(client, CMD_RSP_BUF, buf, 10); dev_info(&client->dev, "Found chip %s\n", &buf[1]); if (buf[1] != 'I' || buf[2] != 'T' || buf[3] != 'E') return -1; return 0; } void it7260_irq_work(struct work_struct *work) { // 通过work成员找到整个数据对象 struct it7260_ts_object *ts_obj = container_of(work, struct it7260_ts_object, work); unsigned char buf[14]; unsigned short xpos[MULTI_POINT] = {0}, ypos[MULTI_POINT] = {0}; unsigned char event[MULTI_POINT] = {0}; unsigned char query = 0; int touch_point = 0; int ret, i; it7260_i2c_read_reg(ts_obj->client, QUERY_BUF, &query, 1); if (!(query & 0x80)) { goto up; } memset(buf, 0, sizeof(buf)); ret = it7260_i2c_read_reg(ts_obj->client, POINT_INFO_BUF, buf, 14); if (ret != 14) { printk("failed to read point info buffer\n"); goto out; } /* touch key */ if (buf[0] == 0x41) { printk("it7260: key number %d\n", buf[1]); if (buf[1] == 0x04) input_report_key(ts_obj->inputdev, KEY_MENU, !!buf[2]); else if (buf[1] == 0x03) input_report_key(ts_obj->inputdev, KEY_HOMEPAGE, !!buf[2]); else if (buf[1] == 0x02) input_report_key(ts_obj->inputdev, KEY_BACK, !!buf[2]); else if (buf[1] == 0x01) input_report_key(ts_obj->inputdev, KEY_SEARCH, !!buf[2]); else goto out; goto sync; } /* finger 0 */ if (buf[0] & 0x01) { xpos[0] = ((buf[3] & 0x0F) << 8) | buf[2]; ypos[0] = ((buf[3] & 0xF0) << 4) | buf[4]; event[0] = buf[5] & 0x0F; } /* finger 1 */ if (buf[0] & 0x02) { xpos[1] = ((buf[7] & 0x0F) << 8) | buf[6]; ypos[1] = ((buf[7] & 0xF0) << 4) | buf[8]; event[1] = buf[9] & 0x0F; } /* finger 2 */ if (buf[0] & 0x04) { xpos[2] = ((buf[11] & 0x0F) << 8) | buf[10]; ypos[2] = ((buf[11] & 0xF0) << 4) | buf[12]; event[2] = buf[13] & 0x0F; } for (i = 0; i < MULTI_POINT; i++) { if (xpos[i] || ypos[i] || event[i]) { touch_point++; #if 1 input_report_abs(ts_obj->inputdev, ABS_X, xpos[i]); input_report_abs(ts_obj->inputdev, ABS_Y, ypos[i]); input_report_abs(ts_obj->inputdev, ABS_PRESSURE, (event[i] << 4)); input_report_key(ts_obj->inputdev, BTN_TOUCH, 1); #else input_report_abs(ts_obj->inputdev, ABS_MT_POSITION_X, xpos[i]); input_report_abs(ts_obj->inputdev, ABS_MT_POSITION_Y, ypos[i]); input_report_abs(ts_obj->inputdev, ABS_MT_PRESSURE, (event[i] << 4)); // input_report_abs(ts_obj->inputdev, ABS_MT_TOUCH_MAJOR, event[i]); input_report_abs(ts_obj->inputdev, ABS_MT_TRACKING_ID, i); input_mt_sync(ts_obj->inputdev); #endif #if 1 printk("123finger %d > (%4d, %4d), event = %d\n", i, ypos[i], xpos[i], event[i]); #endif } } up: input_mt_sync(ts_obj->inputdev); sync: input_sync(ts_obj->inputdev); out: enable_irq(ts_obj->irqno); } irqreturn_t it7260_irq_handler_t(int irqno, void *dev_id) { disable_irq_nosync(irqno); // 直接调度中断下半部 schedule_work(&it7260_obj->work); return IRQ_HANDLED; } int it7260_drv_probe(struct i2c_client *client, const struct i2c_device_id *dev_id) { int ret = -1; // 0, ret = it7260_identify_capsensor(client); if (ret) { printk("cannot identify the touch screen\n"); return -EINVAL; } // 1,申请设备对象 it7260_obj = kzalloc(sizeof(struct it7260_ts_object), GFP_KERNEL); if(NULL == it7260_obj){ printk("kzalloc failed!\n"); return -ENOMEM; } // 2,记录client it7260_obj->client = client; // dev_set_drvdata(&client->dev, it7260_obj); // 3.1,构建inputdev it7260_obj->inputdev = input_allocate_device(); if(NULL == it7260_obj->inputdev){ printk("input alloc failed!\n"); goto err1; } // 3.2,初始化inputdev it7260_obj->inputdev->name = "it7260_ts"; it7260_obj->inputdev->phys = "I2C"; it7260_obj->inputdev->uniq = "ite/it7260"; it7260_obj->inputdev->id.bustype = BUS_I2C; it7260_obj->inputdev->id.vendor = 0xFEED; it7260_obj->inputdev->id.product = 0x0008; it7260_obj->inputdev->id.version = 0x0003; // 3.3,设置inputdev能够产生哪些类型的数据 __set_bit(EV_ABS, it7260_obj->inputdev->evbit); __set_bit(EV_KEY, it7260_obj->inputdev->evbit); #if 1 // 3.4,设定能产生哪些abs数据 __set_bit(ABS_X, it7260_obj->inputdev->absbit); __set_bit(ABS_Y, it7260_obj->inputdev->absbit); __set_bit(ABS_PRESSURE, it7260_obj->inputdev->absbit); // 3.5,设置abs的最大值和最小值 input_set_abs_params(it7260_obj->inputdev, ABS_X, 0, TS_MAX_WIDTH, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_Y, 0, TS_MAX_HIGHT, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_PRESSURE, 0, 255, 0, 0); #else input_set_abs_params(it7260_obj->inputdev, ABS_MT_POSITION_X, 0, TS_MAX_WIDTH, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_MT_POSITION_Y, 0, TS_MAX_HIGHT, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_MT_TOUCH_MAJOR, 0, 16, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_MT_WIDTH_MAJOR, 0, 2, 0, 0); input_set_abs_params(it7260_obj->inputdev, ABS_MT_TRACKING_ID, 0, 3, 0, 0); #endif // 3.6,设置key的最大值和最小值 input_set_capability(it7260_obj->inputdev, EV_KEY, KEY_MENU); input_set_capability(it7260_obj->inputdev, EV_KEY, KEY_BACK); input_set_capability(it7260_obj->inputdev, EV_KEY, KEY_HOMEPAGE); input_set_capability(it7260_obj->inputdev, EV_KEY, KEY_SEARCH); // 3.7,注册inputdev ret = input_register_device(it7260_obj->inputdev); if(0 != ret){ printk("input register faield!\n"); goto err2; } // 4,初始化中断下半部 INIT_WORK(&it7260_obj->work, it7260_irq_work); /** * 5,申请中断,注册中断处理函数 * 通过查看电路图,触摸屏引出3根线,分别是i2c1 SCL, i2c1 SDA和一个中断引脚 * 其中中断引脚就是在触摸屏有数据的时候,会产生中断报告给内核,内核通过中断处理 * 函数,读取相应的绝对坐标值,相关引脚定义在mach-tiny4412.c,低电平有效 */ it7260_obj->irqno = gpio_to_irq(client->irq); ret = request_irq(it7260_obj->irqno, it7260_irq_handler_t, IRQF_TRIGGER_LOW, client->name, it7260_obj); if(ret){ printk("request irq failed!\n"); goto err3; } // tiny4412_set_ctp(CTP_ITE7260); // 设置设备使用唤醒 device_init_wakeup(&client->dev, 1); return 0; err3: free_irq(it7260_obj->irqno, NULL); err2: input_free_device(it7260_obj->inputdev); err1: kfree(it7260_obj); return ret; } int it7260_drv_remove(struct i2c_client *client) { input_unregister_device(it7260_obj->inputdev); input_free_device(it7260_obj->inputdev); free_irq(it7260_obj->irqno, NULL); kfree(it7260_obj); return 0; } int it7260_drv_suspend(struct i2c_client *client, pm_message_t mesg) { int ret = -1; u8 suspend_cmd[] = {0x04, 0x00, 0x02}; struct it7260_ts_object *ts_obj = i2c_get_clientdata(client); if(device_may_wakeup(&client->dev)){ enable_irq_wake(ts_obj->irqno); if(sizeof(suspend_cmd) == it7260_i2c_write_reg(client, CMD_BUF, suspend_cmd, 3)) ret = 0; } return ret; } int it7260_drv_resume(struct i2c_client *client) { int ret = -1; unsigned char query; struct it7260_ts_object *ts_obj = i2c_get_clientdata(client); if(device_may_wakeup(&client->dev)){ it7260_i2c_read_reg(client, QUERY_BUF, &query, 1); disable_irq_wake(ts_obj->irqno); ret = 0; } return ret; } const struct i2c_device_id ite_id_table[] = { {"IT7260", 0x7777}, {}, }; static struct i2c_driver it7260_i2c_driver = { .driver = { .name = "IT7260-ts", }, .probe = it7260_drv_probe, .remove = it7260_drv_remove, .suspend = it7260_drv_suspend, .resume = it7260_drv_resume, .id_table = ite_id_table, }; static void __exit it7260_ts_exit(void) { i2c_del_driver(&it7260_i2c_driver); } static int __init it7260_ts_init(void) { return i2c_add_driver(&it7260_i2c_driver); } module_init(it7260_ts_init); module_exit(it7260_ts_exit); MODULE_LICENSE("GPL");
然后,遇到了一些困难,我写这个驱动程序是参考it7260_mts.c写的,但是按照以往的逻辑,应该要在probe函数里面进行对触摸屏寄存器的初始化工作,但是it7260_mts.c里边没有做相关的操作,所以怀疑这一款触摸屏不需要初始化,上电即可工作,但是我原本是准备动态加载驱动程序,写好了驱动程序之后,把原本在make menuconfig里选中的ITE it7260 TouchScreen driver去掉,重新编译了新的内核文件,然后加载内核,进入文件系统之后.加载我写的驱动生成的.ko文件,按触摸屏发现没有任何反应,怀疑是我写的驱动代码有问题,所以,就原本不动地换成了it7260_mts.c的代码,生成.ko文件,加载,发现,也一样没办法用.这就很尴尬了,于是换了一个套路,把自己写的这个驱动的C文件改名字为it7260_mts.c把原来的文件改成其它名字,menuconfig选中这个程序,重新编译内核,加载新的内核,发现ok了,所以,怀疑,初始化寄存器的工作在其他地方完成,这里只管功能逻辑,但是搜索代码,暂时没有发现什么地方做了这些工作,由于一些事情比较紧急,所以,这个暂时先不追究是什么问题,反正自己写的驱动程序已经测试通过了,虽然,这样的心态是不对的,但是有些时候确实身不由己,之后会完善的.