一、GSC3280触摸屏驱动简介
ADC与触摸屏控制器通过SPI接口挂在GSC3280的SPI0总线上,支持4线电阻式触摸屏或当ADC输入使用。
控制器可以接四线电阻式触摸屏,最大支持4000像素的屏,市面上绝大部分四线电阻式触摸屏都能支持。所接触摸屏的输出阻抗不要大于2K欧姆,否则会影响测量精度。当控制器四根引脚(XP、YP、XN、YN)接四线电阻式触摸屏时,掉电及屏没有被按下时,控制器的中断信号INT_PEN保持为低电平。当触摸屏被按下时,控制器会置起INT_PEN信号来通知CPU,然后在中断服务程序里,CPU可以通过SPI0接口发出触摸屏相关命令进行操作。接触摸屏时,屏被按下时就有中断,当CPU往SPI_DIN 线上发测量命令时,控制器自动撤掉中断,无需CPU清除。CPU取到有效数据(即在SPI_DOUT线上返回状态0,此时为有效数据)时,如果此时触摸屏还是被按下的(可能是上次按下没来得及撤掉,也可能是再次被按下的),INT_PEN会再次被置起。
二、GSC3280四线触摸屏驱动
2.1、平台设备注册
分析一个驱动,一般都是从模块初始化函数开始,程序如下:
-
#if defined(CONFIG_TOUCHSCREEN_GSC3280)
-
static struct resource gsc3280_ts_resources[] = {
-
{
-
.name = "adc-ts-irq",
-
.start = EXT_GSC3280_ADC_IRQ,
-
.end = EXT_GSC3280_ADC_IRQ,
-
.flags = IORESOURCE_IRQ,
-
},
-
-
};
-
static struct platform_device gsc3280_ts_device = {
-
.name = "adc-ts-irq",
-
.id = -1,
-
.resource = gsc3280_ts_resources,
-
.num_resources = ARRAY_SIZE(gsc3280_ts_resources),
-
};
-
#endif
-
static struct platform_driver gsc3280_ts_driver = {
-
.driver = {
-
.name = "adc-ts-irq",
-
.owner = THIS_MODULE,
-
},
-
.probe = gsc3280_ts_probe,
-
.remove = __devexit_p(gsc3280_ts_remove),
-
};
-
static int __init gsc3280_ts_init(void)
-
{
-
int ret = 0;
-
-
ret = platform_driver_register(&gsc3280_ts_driver);
-
if (ret)
-
printk(KERN_ERR "!!!!!!gsc ts init register error!!!!!!\n");
-
return ret;
-
}
-
static void __exit gsc3280_ts_exit(void)
-
{
-
platform_driver_unregister(&gsc3280_ts_driver);
-
}
-
subsys_initcall(gsc3280_ts_init);
-
module_exit(gsc3280_ts_exit);
说明:
1) 此处模块初始化使用的是subsys_initcall(),优先级高于module_init(),为什么使用subsys_initcall(),将在第三篇中讲述。
2) 将GSC3280的触摸屏驱动当作平台设备,根据(一)中的特性,此处的触摸屏和SPI0挂接在一起,所以此处涉及的资源只有中断,没有寄存器地址。
2.2、探测函数gsc3280_ts_probe()
程序如下:
-
static int __devinit gsc3280_ts_probe(struct platform_device *pdev)
-
{
-
int ret = 0;
-
struct input_dev *input = NULL;
-
struct gsc3280_ts_s *gsc = NULL;
-
-
DBG("############\n");
-
printk(KERN_INFO "gsc3280 touch screen probe start.\n");
-
gsc = kzalloc(sizeof(struct gsc3280_ts_s), GFP_KERNEL);
-
input = input_allocate_device();
-
if (!gsc || !input) {
-
ret = -ENOMEM;
-
goto err_free_mem;
-
}
-
gsc->irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-
if (gsc->irq == NULL) {
-
DBG("!!!!no irq resource specified!\n");
-
ret = -ENOENT;
-
goto err_free_mem;
-
}
-
ret = request_threaded_irq(gsc->irq->start, NULL, gsc3280_ts_irq, IRQF_ONESHOT, "adcirq", gsc);
-
if (ret) {
-
goto err_free_mem;
-
}
-
writel(0x01, (volatile unsigned int *)0xbc04a0ac); /* enable ts */
-
-
spin_lock_init(&gsc->slock);
-
gsc->dev = &pdev->dev;
-
gsc->x = 0;
-
gsc->y = 0;
-
gsc->z = 0;
-
gsc->con_cnt = 0;
-
snprintf(gsc->phys, sizeof(gsc->phys), "%s/input0", dev_name(gsc->dev));
-
input->name = "h3600_ts";
-
input->phys = gsc->phys;
-
input->dev.parent = gsc->dev;
-
input->id.vendor = 0x00;
-
input->id.version = 0x00;
-
input->id.product = 0x03;
-
input->id.bustype = BUS_HOST;
-
-
input->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
-
input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
-
input_set_abs_params(input, ABS_X, 0, 0x3FF, 0, 0);
-
input_set_abs_params(input, ABS_Y, 0, 0x3FF, 0, 0);
-
gsc->input = input;
-
input_set_abs_params(gsc->input, ABS_X, GSC_X_MIN, GSC_X_MAX, GSC_X_FUZZ, 0);
-
input_set_abs_params(gsc->input, ABS_Y, GSC_Y_MIN, GSC_Y_MAX, GSC_Y_FUZZ, 0);
-
input_set_abs_params(gsc->input, ABS_PRESSURE, GSC_PRESSURE_MIN,
-
GSC_PRESSURE_MAX, 0, 0);
-
ret = input_register_device(gsc->input);
-
if (ret) {
-
DBG("!!!!input register device error!\n");
-
goto err_free_irq;
-
}
-
INIT_LIST_HEAD(&gsc->device_entry);
-
strlcpy(gsc->name, GSC3280_TS_NAME, GSC3280_TS_NAME_SIZE);
-
-
mutex_lock(&gsc3280_ts_list_lock);
-
list_add(&gsc->device_entry, &gsc3280_ts_list);
-
mutex_unlock(&gsc3280_ts_list_lock);
-
-
platform_set_drvdata(pdev, gsc);
-
printk(KERN_INFO "gsc3280 touch screen probe success.\n");
-
DBG("############\n");
-
return 0;
-
-
err_free_irq:
-
free_irq(gsc->irq->start, gsc);
-
err_free_mem:
-
input_free_device(input);
-
kfree(gsc);
-
printk(KERN_INFO "!!!!gsc3280 touch screen probe error!\n");
-
return ret;
-
}
-
static int __devexit gsc3280_ts_remove(struct platform_device *pdev)
-
{
-
struct gsc3280_ts_s *gsc = platform_get_drvdata(pdev);
-
-
free_irq(gsc->irq->start, gsc);
-
input_unregister_device(gsc->input);
-
kfree(gsc);
-
platform_set_drvdata(pdev, NULL);
-
printk(KERN_INFO "gsc3280 touch screen remove\n");
-
return 0;
-
}
说明:
1) 首先申请结构体内存gsc3280_ts_s和input设备内存。
2) 申请中断资源和注册中断函数。
3) 初始化成员变量。
4) 注册input_dev。
5) 移除函数主要就是释放探测函数中申请的资源。
2.3、中断函数gsc3280_ts_irq()
程序如下:
-
static int test_ts_state(void)
-
{
-
u32 state = *((volatile unsigned int *)(ICTL_RAW_STATUS_REG)) ;
-
-
if (state & ICTL_SPI0_BITS) {
-
return TS_PRESS_DOWN;
-
} else
-
return TS_PRESS_UP;
-
}
-
static irqreturn_t gsc3280_ts_irq(int irq, void *dev_id)
-
{
-
u8 flg = 0;
-
u32 x = 0, y = 0, z = 0;
-
struct gsc3280_ts *ts = dev_id;
-
-
begin:
-
x = adc_cmd(ADC_CMD_MEASUREX);
-
y = adc_cmd(ADC_CMD_MEASUREY);
-
z = adc_cmd(ADC_CMD_MEASUREZ);
-
x = ((x -0x83) * 800) / (0xf5d - 0x83);
-
y = ((0xeea - y) * 480) / (0xeea - 0xcc);
-
-
if ((z < 700) && (z > 10) && (x > 0) && (x < 800) && (y > 0) && (y < 480)) {
-
if (flg == 0) {
-
flg = 1;
-
gsc3280_report_event(ts, x, y, 0);
-
}
-
gsc3280_report_event(ts, x, y, z);
-
msleep(10);
-
}
-
if (test_ts_state() == TS_PRESS_UP)
-
goto Up;
-
else
-
goto begin;
-
Up:
-
if (flg == 0)
-
return IRQ_HANDLED;
-
if ((x == 0) || (y == 0))
-
return IRQ_HANDLED;
-
gsc3280_report_event(ts, x, y, 0);
-
return IRQ_HANDLED;
-
}
说明:
1) 进入中断后,调用adc_cmd()函数测量触摸点位置和按键力度信息。
2) 根据公式计算绝对坐标,具体公式原理可以查看input子系统(二)--GSC3280一线触摸屏驱动。
3) 判断测量数据是否正确,如果正确,报告相应位置。
4) flg是报告标志,即是否向input子系统报告了位置信息,该变量还用来做是否是第一次触摸判断,如果该变量为0,表示第一次触摸,如果为1,表示不是第一次触摸。
5) mdelay(10);的作用是除抖。
6) 除抖后判断触摸是否按下,如果按下,还需要判断是否是第一次触摸,如果是第一次触摸,而且按键是抬起状态,则表示此次是抖动,直接退出。如果不是第一次触摸,此时触摸是抬起状态,则直接进入抬起流程。
7) 调用gsc_report_data(gsc);报告信息。
8) 再次判断触摸状态,如果是按下,进入开始流程。如果是抬起,进入抬起流程。
9) 抬起流程中,判断是否报告了触摸按下信息,如果报告了,就报告触摸抬起信息。
2.4、触摸点信息测量函数
程序如下:
-
void spi0_write(unsigned short v)
-
{
-
int cnt=0;
-
DBG("enter adc_write()\n");
-
do {
-
while (SPI0_STATUS_REG & 0x10)
-
if (cnt++ > 10000)
-
break;
-
} while(0);
-
cnt = 0;
-
/*spi0 fifo can write, transmit fifo empty */
-
while (SPI0_STATUS_REG & SPI_RX_FULL)
-
if (cnt++ > 1000000)
-
break;
-
SPI0_DATA_REG = v;
-
DBG("leave adc_write()\n");
-
}
-
unsigned short spi0_read(void)
-
{
-
int cnt= 0;
-
DBG("enter adc_read()\n");
-
do {
-
while (SPI0_STATUS_REG & 0x10)
-
if (cnt++ > 10000)
-
break;
-
} while(0);
-
cnt = 0;
-
/*spi0 fifo receive not empty*/
-
while (!(SPI0_STATUS_REG & SPI_RX_N_EMPTY))
-
if (cnt++>10000000)
-
break;
-
DBG("leave adc_read()\n");
-
return (unsigned short)(SPI0_DATA_REG);
-
}
-
unsigned short adc_cmd(unsigned short cmd)
-
{
-
unsigned short res = 0;
-
DBG("enter adc_cmd\n");
-
spi0_write( cmd );
-
while (1) {
-
res = spi0_read();
-
if ((res == 0xF000) || (res == (0x8000 | (cmd >> 12)))) {
-
spi0_write(0xF000);
-
}
-
else if ( res < 0x1000 ) { //data
-
char buf[20] ;
-
sprintf(buf, "adc_cmd %0x result %0x\n", cmd, res);
-
DBG(buf);
-
return res;
-
}
-
else{
-
spi0_write( cmd );
-
}
-
}
-
DBG("leave adc_cmd\n");
-
return res;
-
}
说明:
1) adc_cmd()函数的形参为具体的命令。
2) adc_cmd()函数首先通过SPI0发送命令,然后根据协议读取数据。
2.5、信息报告函数
-
static void gsc3280_report_event(struct gsc3280_ts *ts, u32 x, u32 y, u32 z)
-
{
-
#ifdef CONFIG_GSC3280_POS_PRINT
-
printk(KERN_INFO "x = %d\n", x);
-
printk(KERN_INFO "y = %d\n", y);
-
printk(KERN_INFO "z = %d\n", z);
-
#endif
-
-
input_report_abs(ts->input, ABS_PRESSURE, z);
-
input_report_abs(ts->input, ABS_X, x);
-
input_report_abs(ts->input, ABS_Y, y);
-
if (z > 0)
-
input_report_key(ts->input, BTN_TOUCH, 1);
-
else
-
input_report_key(ts->input, BTN_TOUCH, 0);
-
input_sync(ts->input);
-
}
说明:
1) 根据宏标志判断是否打印测量信息,用于测试。
2) 向input核心报告位置信息。
三、注册input_handler
handler中的成员主要作为回调函数,具体如下:
3.1、模块注册函数
程序如下:
-
static struct input_handler tsdev_handler = {
-
.event = tsdev_event,
-
.connect = tsdev_connect,
-
.disconnect = tsdev_disconnect,
-
.fops = &tsdev_fops,
-
.minor = TSDEV_MINOR_BASE,
-
.name = "tsdev",
-
.id_table = tsdev_ids,
-
};
-
static int __init tsdev_init(void)
-
{
-
input_register_handler(&tsdev_handler);
-
return 0;
-
}
-
static void __exit tsdev_exit(void)
-
{
-
input_unregister_handler(&tsdev_handler);
-
}
-
module_init(tsdev_init);
-
module_exit(tsdev_exit);
说明:
1) 调用input_register_handler()注册tsdev_handler。input_register_handler()函数将在第三篇文章中讲述。
2) 模块退出函数就是注销tsdev_handler。
3.2、连接和释放连接函数
程序如下:
-
static int tsdev_connect(struct input_handler *handler, struct input_dev *dev,
-
const struct input_device_id *id)
-
{
-
int minor = 0, ret = 0;
-
struct ts_dev *ts = NULL;
-
for (minor = 0; ((minor < (TSDEV_MINOR_MAX >> 1)) && tsdev_table[minor]); minor++) {
-
;
-
}
-
if (minor >= (TSDEV_MINOR_MAX >> 1)) {
-
DBG("!!!!You have way too many touchscreens!\n");
-
return -EBUSY;
-
}
-
if (!(ts = kzalloc(sizeof(struct ts_dev), GFP_KERNEL))) {
-
DBG("!!!!kmalloc error!\n");
-
return -ENOMEM;
-
}
-
INIT_LIST_HEAD(&ts->list);
-
init_waitqueue_head(&ts->wait);
-
wake_up_interruptible(&ts_wait_queue);
-
dev_set_name(&ts->dev, "ts%d", minor);
-
ts->exist = 1;
-
ts->minor = minor;
-
ts->handle.dev = input_get_device(dev);
-
ts->handle.name = dev_name(&ts->dev);;
-
ts->handle.handler = handler;
-
ts->handle.private = ts;
-
ts->dev.class = &input_class;
-
if (dev) {
-
ts->dev.parent = &dev->dev;
-
}
-
ts->dev.devt = MKDEV(INPUT_MAJOR, TSDEV_MINOR_BASE + minor);
-
ts->dev.release = tsdev_free;
-
device_initialize(&ts->dev);
-
ret = input_register_handle(&ts->handle);
-
if (ret) {
-
DBG("!!!!register handler tsdev error!\n");
-
return -EFAULT;
-
}
-
tsdev_table[minor] = ts;
-
ret = device_add(&ts->dev);
-
if (ret) {
-
DBG("!!!!add tsdev class error!\n");
-
return -EFAULT;
-
}
-
return 0;
-
}
-
static void tsdev_disconnect(struct input_handle *handle)
-
{
-
struct ts_dev *ts = handle->private;
-
-
ts->exist = 0;
-
device_del(&ts->dev);
-
if (ts->minor != (TSDEV_MINOR_MAX >>1))
-
input_unregister_handle(&ts->handle);
-
put_device(&ts->dev);
-
}
1) 此函数作为handler的回调函数,具体调用地点将在第三篇文章中讲述。
2) 在数组指针tsdev_table中找到还没有使用的数组索引编号。
2) 申请结构体内存,然后对其成员进行初始化。
3) 设置设备名称,此名称就是显示在“/dev”目录下的名称。
4) 计算设备号。
5) 调用input_register_handle()函数注册handle。
6) 将结构体指针放入指针数组tsdev_table中。
7) 释放连接函数主要是注销连接函数中注册的handle。
3.3、事件函数
程序如下:
-
static void tsdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
-
{
-
unsigned long flags;
-
struct timeval time;
-
struct tsdev_list *list;
-
struct ts_dev *ts = handle->private;
-
-
switch (type) {
-
case EV_ABS:
-
switch (code) {
-
case ABS_X:
-
ts->x = value;
-
break;
-
case ABS_Y:
-
ts->y = value;
-
break;
-
case ABS_PRESSURE:
-
ts->pressure = value;
-
break;
-
}
-
break;
-
case EV_REL:
-
switch (code) {
-
case REL_X:
-
ts->x += value;
-
if (ts->x < 0)
-
ts->x = 0;
-
else if (ts->x > gXres)
-
ts->x = gXres;
-
break;
-
case REL_Y:
-
ts->y += value;
-
if (ts->y < 0)
-
ts->y = 0;
-
else if (ts->y > gYres)
-
ts->y = gYres;
-
break;
-
}
-
break;
-
case EV_KEY:
-
if (code == BTN_TOUCH || code == BTN_MOUSE) {
-
switch (value) {
-
case 0:
-
ts->pressure = 0;
-
break;
-
case 1:
-
if (!ts->pressure)
-
ts->pressure = 1;
-
break;
-
}
-
}
-
break;
-
}
-
if (type != EV_SYN)
-
return;
-
list_for_each_entry(list, &ts->list, node) {
-
if (list) {
-
spin_lock_irqsave(&list->lock, flags);
-
do_gettimeofday(&time);
-
list->event[list->head].pressure = ts->pressure;
-
list->event[list->head].x = ts->x;
-
list->event[list->head].y = ts->y;
-
list->head = (list->head + 1) & (TSDEV_BUFFER_SIZE - 1);
-
kill_fasync(&list->fasync, SIGIO, POLL_IN);
-
spin_unlock_irqrestore(&list->lock, flags);
-
}
-
}
-
wake_up_interruptible(&ts_wait_queue);
-
}
说明:
1) 事件函数也是一个回调函数,具体调用地点将在第三篇文章中介绍。
2) 首先是一个switch语句,根据不同情况,进入不同的分支。各个分支主要是根据情况的不同,赋值触摸信息。
3) 判断是否是同步事件,如果不是,直接退出。
4) 如果是同步事件,将触摸信息赋值给list结构体成员,list结构体成员值将会被传递到应用层,接下来会讲述。
5) 唤醒等待队列,标志有数据可读。
3.4、函数操作集tsdev_fops
操作集具体内容如下:
-
struct file_operations tsdev_fops = {
-
.owner = THIS_MODULE,
-
.open = tsdev_open,
-
.release = tsdev_release,
-
.read = tsdev_read,
-
.poll = tsdev_poll,
-
.fasync = tsdev_fasync,
-
};
1、open和release函数
程序如下:
-
static int tsdev_open(struct inode *inode, struct file *file)
-
{
-
struct tsdev_list *list;
-
int ret = 0, i = iminor(inode) - TSDEV_MINOR_BASE;
-
-
if ((i >= TSDEV_MINOR_MAX) || (!tsdev_table[i & TSDEV_MINOR_MASK])) {
-
DBG("!!!!tsdev minor error!\n");
-
return -ENODEV;
-
}
-
list = kzalloc(sizeof(struct tsdev_list), GFP_KERNEL);
-
if (!list) {
-
DBG("!!!!kzalloc error!\n");
-
return -ENOMEM;
-
}
-
list->raw = (i >= (TSDEV_MINOR_MAX >> 1)) ? 1 : 0;
-
i &= TSDEV_MINOR_MASK;
-
list->tsdev = tsdev_table[i];
-
list_add_tail(&list->node, &list->tsdev->list);
-
list->num++;
-
list->head = list->tail = 0;
-
spin_lock_init(&list->lock);
-
file->private_data = list;
-
if (!list->tsdev->open++)
-
if (list->tsdev->exist)
-
ret = input_open_device(&list->tsdev->handle);
-
return ret;
-
}
-
static int tsdev_release(struct inode *inode, struct file *file)
-
{
-
struct tsdev_list *list = file->private_data;
-
tsdev_fasync(-1, file, 0);
-
list_del(&list->node);
-
if (!--list->tsdev->open) {
-
if (list->tsdev->exist)
-
input_close_device(&list->tsdev->handle);
-
else
-
tsdev_free(&(list->tsdev->dev));
-
}
-
kfree(list);
-
return 0;
-
}
说明:
1) 首先计算设备节点的次设备号,此即接下来申请的结构体内存地址存放索引号。
2) 申请list结构体内存,对其成员变量初始化。
3) 此处list->head和list->tail两个成员变量比较重要,通过这两个变量来标识有多少个数据可读。
4) 释放函数主要就是释放open函数中申请到的资源。
2、读函数
应用层操作“/dev/ts0”设备节点读数据时,就是调用此函数,程序如下:
-
static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos)
-
{
-
int ret = 0;
-
unsigned long flags = 0;
-
struct tsdev_list *list = file->private_data;
-
-
if ((list->head == list->tail) && (file->f_flags & O_NONBLOCK) )
-
return -EAGAIN;
-
ret = wait_event_interruptible(ts_wait_queue, (list->head != list->tail));
-
if (ret)
-
return ret;
-
spin_lock_irqsave(&list->lock, flags);
-
while ((list->head != list->tail) && (ret + sizeof (struct ts_event) <= count)) {
-
if (copy_to_user (buffer + ret, list->event + list->tail, sizeof (struct ts_event))) {
-
spin_unlock_irqrestore(&list->lock, flags);
-
return ret;
-
}
-
list->tail = (list->tail + 1) & (TSDEV_BUFFER_SIZE - 1);
-
ret += sizeof (struct ts_event);
-
}
-
spin_unlock_irqrestore(&list->lock, flags);
-
return ret;
-
}
说明:
1) 首先判断是否有数据,此时就是通过list->head和list->tail来判断的。
2) 如果没有数据,则表达式(list->head != list->tail)不满足,睡眠等待。
3) 如果有数据,将数据拷贝到应用层,在操作数据过程中,使用自旋锁保护,函数返回值为应用层接收到的数据长度。
3、poll函数
当应用层使用select或者poll函数时,调用的就是底层驱动的poll函数,此处poll函数的程序如下:
-
static unsigned int tsdev_poll(struct file *file, poll_table * wait)
-
{
-
unsigned long flags = 0;
-
struct tsdev_list *list = file->private_data;
-
-
poll_wait(file, &ts_wait_queue, wait);
-
spin_lock_irqsave(&list->lock, flags);
-
if (list->head != list->tail) {
-
spin_unlock_irqrestore(&list->lock, flags);
-
return POLLIN | POLLRDNORM;
-
}
-
spin_unlock_irqrestore(&list->lock, flags);
-
return 0;
-
}
说明:
1) 首先调用poll_wait()函数等待。
2) 当有数据时,唤醒等待队列,从上面发现,唤醒函数在3.3事件函数tsdev_event()中。
3) 如果有数据,则返回值为可读,否则返回0。
4、fasync函数
程序如下:
-
static int tsdev_fasync(int fd, struct file *file, int on)
-
{
-
int ret = 0;
-
struct tsdev_list *list = file->private_data;
-
-
ret = fasync_helper(fd, file, on, &list->fasync);
-
return ret < 0 ? ret : 0;
-
}
说明:
1) fasync函数供上层调用。
2) 在3.3中调用了kill_fasync(&list->fasync, SIGIO, POLL_IN); 函数,其表示向应用层发送数据,应用层进程中哪个函数调用fasync_helper()函数就向它发送数据,也就是说应用层哪个进程调用tsdev_fasync()函数就向它发送数据。
阅读(4874) | 评论(1) | 转发(4) |