概述
本节主要是说一下触摸屏驱动的编写. 触摸屏输入输入设备,所以我们本次通过输入子系统的方式来实现,输入子系统的框架图如下:
然后,我们看一线电路图的触摸屏部分:
可以看到触摸屏是通过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有关的驱动了,通过查看源代码的驱动目录:driversinputtouchscreen在这个目录下发现了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 %dn", buf[1]);
改为:
printk("it7260: key number %dn", buf[1]);
(2)
#if 0
printk("finger %d > (%4d, %4d), event = %dn",
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(&buf1[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 %sn", &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 buffern");
goto out;
}
/* touch key */
if (buf[0] == 0x41) {
printk("it7260: key number %dn", 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 = %dn", 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 screenn");
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了,所以,怀疑,初始化寄存器的工作在其他地方完成,这里只管功能逻辑,但是搜索代码,暂时没有发现什么地方做了这些工作,由于一些事情比较紧急,所以,这个暂时先不追究是什么问题,反正自己写的驱动程序已经测试通过了,虽然,这样的心态是不对的,但是有些时候确实身不由己,之后会完善的.
最后
以上就是等待大米为你收集整理的⑧tiny4412 Linux驱动开发之触摸屏(TouchScreen)驱动程序的全部内容,希望文章能够帮你解决⑧tiny4412 Linux驱动开发之触摸屏(TouchScreen)驱动程序所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复