搜索
bottom↓
回复: 0

linux触摸屏驱动分析,touchscreen, struct input_dev,基于TSC2007

[复制链接]

出0入0汤圆

发表于 2011-8-5 15:57:31 | 显示全部楼层 |阅读模式
【详细说明】linux触摸屏驱动分析,touchscreen, struct input_dev,基于TSC2007

代码从2.6.32拷贝:


/*
* drivers/input/touchscreen/tsc2007.c
*
* Copyright (c) 2008 MtekVision Co., Ltd.
*  Kwangwoo Lee <kwlee@mtekvision.com>
*
* Using code from:
*  - ads7846.c
*  Copyright (c) 2005 David Brownell
*  Copyright (c) 2006 Nokia Corporation
*  - corgi_ts.c
*  Copyright (C) 2004-2005 Richard Purdie
*  - omap_ts.[hc], ads7846.h, ts_osk.c
*  Copyright (C) 2002 MontaVista Software
*  Copyright (C) 2004 Texas Instruments
*  Copyright (C) 2005 Dirk Behme
*
*  This program is free software; you can redistribute it and/or modify
*  it under the terms of the GNU General Public License version 2 as
*  published by the Free Software Foundation.
*/  
#include <linux/module.h>  
#include <linux/slab.h>  
#include <linux/input.h>  
#include <linux/interrupt.h>  
#include <linux/i2c.h>  
#include <linux/i2c/tsc2007.h>  
#define TS_POLL_DELAY           1 /* ms delay between samples */  
#define TS_POLL_PERIOD          1 /* ms delay between samples */  
#define TSC2007_MEASURE_TEMP0       (0x0 << 4)  
#define TSC2007_MEASURE_AUX     (0x2 << 4)  
#define TSC2007_MEASURE_TEMP1       (0x4 << 4)  
#define TSC2007_ACTIVATE_XN     (0x8 << 4)  
#define TSC2007_ACTIVATE_YN     (0x9 << 4)  
#define TSC2007_ACTIVATE_YP_XN      (0xa << 4)  
#define TSC2007_SETUP           (0xb << 4)  
#define TSC2007_MEASURE_X       (0xc << 4)  
#define TSC2007_MEASURE_Y       (0xd << 4)  
#define TSC2007_MEASURE_Z1      (0xe << 4)  
#define TSC2007_MEASURE_Z2      (0xf << 4)  
#define TSC2007_POWER_OFF_IRQ_EN    (0x0 << 2)  
#define TSC2007_ADC_ON_IRQ_DIS0     (0x1 << 2)  
#define TSC2007_ADC_OFF_IRQ_EN      (0x2 << 2)  
#define TSC2007_ADC_ON_IRQ_DIS1     (0x3 << 2)  
#define TSC2007_12BIT           (0x0 << 1)  
#define TSC2007_8BIT            (0x1 << 1)  
#define MAX_12BIT           ((1 << 12) - 1)  
#define ADC_ON_12BIT    (TSC2007_12BIT | TSC2007_ADC_ON_IRQ_DIS0)  
#define READ_Y      (ADC_ON_12BIT | TSC2007_MEASURE_Y)  
#define READ_Z1     (ADC_ON_12BIT | TSC2007_MEASURE_Z1)  
#define READ_Z2     (ADC_ON_12BIT | TSC2007_MEASURE_Z2)  
#define READ_X      (ADC_ON_12BIT | TSC2007_MEASURE_X)  
#define PWRDOWN     (TSC2007_12BIT | TSC2007_POWER_OFF_IRQ_EN)  
struct ts_event {  
    u16 x;  
    u16 y;  
    u16 z1, z2;  
};  
struct tsc2007 {  
    struct input_dev    *input;  
    char            phys[32];  
    struct delayed_work work;  
    struct i2c_client   *client;  
    u16         model;  
    u16         x_plate_ohms;  
    bool            pendown;  
    int         irq;  
    int         (*get_pendown_state)(void);  
    void            (*clear_penirq)(void);  
};  
static inline int tsc2007_xfer(struct tsc2007 *tsc, u8 cmd)  
{  
    s32 data;  
    u16 val;  
    data = i2c_smbus_read_word_data(tsc->client, cmd);  
    if (data < 0) {  
        dev_err(&tsc->client->dev, "i2c io error: %d\n", data);  
        return data;  
    }  
    /* The protocol and raw data format from i2c interface:
     * S Addr Wr [A] Comm [A] S Addr Rd [A] [DataLow] A [DataHigh] NA P
     * Where DataLow has [D11-D4], DataHigh has [D3-D0 << 4 | Dummy 4bit].
     */  
    val = swab16(data) >> 4;  
    dev_dbg(&tsc->client->dev, "data: 0x%x, val: 0x%x\n", data, val);  
    return val;  
}  
static void tsc2007_read_values(struct tsc2007 *tsc, struct ts_event *tc)  
{  
    /* y- still on; turn on only y+ (and ADC) */  
    tc->y = tsc2007_xfer(tsc, READ_Y);  
    /* turn y- off, x+ on, then leave in lowpower */  
    tc->x = tsc2007_xfer(tsc, READ_X);  
    /* turn y+ off, x- on; we'll use formula #1 */  
    tc->z1 = tsc2007_xfer(tsc, READ_Z1);  
    tc->z2 = tsc2007_xfer(tsc, READ_Z2);  
    /* Prepare for next touch reading - power down ADC, enable PENIRQ */  
    tsc2007_xfer(tsc, PWRDOWN);  
}  
static u32 tsc2007_calculate_pressure(struct tsc2007 *tsc, struct ts_event *tc)  
{  
    u32 rt = 0;  
    /* range filtering */  
    if (tc->x == MAX_12BIT)  
        tc->x = 0;  
    if (likely(tc->x && tc->z1)) {  
        /* compute touch pressure resistance using equation #1 */  
        rt = tc->z2 - tc->z1;  
        rt *= tc->x;  
        rt *= tsc->x_plate_ohms;  
        rt /= tc->z1;  
        rt = (rt + 2047) >> 12;  
    }  
    return rt;  
}  
static void tsc2007_send_up_event(struct tsc2007 *tsc)  
{  
    struct input_dev *input = tsc->input;  
    dev_dbg(&tsc->client->dev, "UP\n");  
    input_report_key(input, BTN_TOUCH, 0);  
    input_report_abs(input, ABS_PRESSURE, 0);  
    input_sync(input);  
}  
//中断后跑到这里来  
static void tsc2007_work(struct work_struct *work)  
{  
    struct tsc2007 *ts =  
        container_of(to_delayed_work(work), struct tsc2007, work);  
    struct ts_event tc;  
    u32 rt;  
    /*
     * NOTE: We can't rely on the pressure to determine the pen down
     * state, even though this controller has a pressure sensor.
     * The pressure value can fluctuate for quite a while after
     * lifting the pen and in some cases may not even settle at the
     * expected value.
     *
     * The only safe way to check for the pen up condition is in the
     * work function by reading the pen signal state (it's a GPIO
     * and IRQ). Unfortunately such callback is not always available,
     * in that case we have rely on the pressure anyway.
     */  
    if (ts->get_pendown_state) {  
        if (unlikely(!ts->get_pendown_state())) {  
            tsc2007_send_up_event(ts);  
            ts->pendown = false;  
            goto out;  
        }  
        dev_dbg(&ts->client->dev, "pen is still down\n");  
    }  
    tsc2007_read_values(ts, &tc);  
    rt = tsc2007_calculate_pressure(ts, &tc); //计算压力  
    if (rt > MAX_12BIT) {  
        /*
         * Sample found inconsistent by debouncing or pressure is
         * beyond the maximum. Don't report it to user space,
         * repeat at least once more the measurement.
         */  
        dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);  
        goto out;  
    }  
    if (rt) {  
        struct input_dev *input = ts->input;  
        if (!ts->pendown) {  
            dev_dbg(&ts->client->dev, "DOWN\n");  
            input_report_key(input, BTN_TOUCH, 1);  
            ts->pendown = true;  
        }  
        input_report_abs(input, ABS_X, tc.x);  
        input_report_abs(input, ABS_Y, tc.y);  
        input_report_abs(input, ABS_PRESSURE, rt);  
        input_sync(input);  
        dev_dbg(&ts->client->dev, "point(%4d,%4d), pressure (%4u)\n",  
            tc.x, tc.y, rt);  
    } else if (!ts->get_pendown_state && ts->pendown) {  
        /*
         * We don't have callback to check pendown state, so we
         * have to assume that since pressure reported is 0 the
         * pen was lifted up.
         */  
        tsc2007_send_up_event(ts);  
        ts->pendown = false;  
    }  
out:  
    if (ts->pendown)  
        schedule_delayed_work(&ts->work,  
                      msecs_to_jiffies(TS_POLL_PERIOD));  
    else  
        enable_irq(ts->irq);  
}  
static irqreturn_t tsc2007_irq(int irq, void *handle)  
{  
    struct tsc2007 *ts = handle;  
    if (!ts->get_pendown_state || likely(ts->get_pendown_state())) {  
        disable_irq_nosync(ts->irq);  
        schedule_delayed_work(&ts->work,  
                      msecs_to_jiffies(TS_POLL_DELAY));  
    }  
    if (ts->clear_penirq)  
        ts->clear_penirq();  
    return IRQ_HANDLED;  
}  
static void tsc2007_free_irq(struct tsc2007 *ts)  
{  
    free_irq(ts->irq, ts);  
    if (cancel_delayed_work_sync(&ts->work)) {  
        /*
         * Work was pending, therefore we need to enable
         * IRQ here to balance the disable_irq() done in the
         * interrupt handler.
         */  
        enable_irq(ts->irq);  
    }  
}  
static int __devinit tsc2007_probe(struct i2c_client *client,  
                   const struct i2c_device_id *id)  
{  
    struct tsc2007 *ts;  
    struct tsc2007_platform_data *pdata = pdata = client->dev.platform_data;  
    struct input_dev *input_dev;  
    int err;  
    if (!pdata) {  
        dev_err(&client->dev, "platform data is required!\n");  
        return -EINVAL;  
    }  
    if (!i2c_check_functionality(client->adapter,  
                     I2C_FUNC_SMBUS_READ_WORD_DATA))  
        return -EIO;  
    ts = kzalloc(sizeof(struct tsc2007), GFP_KERNEL);  
    input_dev = input_allocate_device();  
    if (!ts || !input_dev) {  
        err = -ENOMEM;  
        goto err_free_mem;  
    }  
    ts->client = client;  
    ts->irq = client->irq;  
    ts->input = input_dev;  
    INIT_DELAYED_WORK(&ts->work, tsc2007_work);  
    ts->model             = pdata->model;  
    ts->x_plate_ohms      = pdata->x_plate_ohms;  
    ts->get_pendown_state = pdata->get_pendown_state;  
    ts->clear_penirq      = pdata->clear_penirq;  
    snprintf(ts->phys, sizeof(ts->phys),  
         "%s/input0", dev_name(&client->dev));  
    input_dev->name = "TSC2007 Touchscreen";  
    input_dev->phys = ts->phys;  
    input_dev->id.bustype = BUS_I2C;  
    input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);  
    input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);  
    input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0);  
    input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0);  
    input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);  
    if (pdata->init_platform_hw)  
        pdata->init_platform_hw();  
    err = request_irq(ts->irq, tsc2007_irq, 0,  
            client->dev.driver->name, ts);  
    if (err < 0) {  
        dev_err(&client->dev, "irq %d busy?\n", ts->irq);  
        goto err_free_mem;  
    }  
    /* Prepare for touch readings - power down ADC and enable PENIRQ */  
    err = tsc2007_xfer(ts, PWRDOWN);  
    if (err < 0)  
        goto err_free_irq;  
    err = input_register_device(input_dev);  
    if (err)  
        goto err_free_irq;  
    i2c_set_clientdata(client, ts);  
    return 0;  
err_free_irq:  
    tsc2007_free_irq(ts);  
    if (pdata->exit_platform_hw)  
        pdata->exit_platform_hw();  
err_free_mem:  
    input_free_device(input_dev);  
    kfree(ts);  
    return err;  
}  
static int __devexit tsc2007_remove(struct i2c_client *client)  
{  
    struct tsc2007  *ts = i2c_get_clientdata(client);  
    struct tsc2007_platform_data *pdata = client->dev.platform_data;  
    tsc2007_free_irq(ts);  
    if (pdata->exit_platform_hw)  
        pdata->exit_platform_hw();  
    input_unregister_device(ts->input);  
    kfree(ts);  
    return 0;  
}  
static struct i2c_device_id tsc2007_idtable[] = {  
    { "tsc2007", 0 },  
    { }  
};  
MODULE_DEVICE_TABLE(i2c, tsc2007_idtable);  
static struct i2c_driver tsc2007_driver = {  
    .driver = {  
        .owner  = THIS_MODULE,  
        .name   = "tsc2007"  
    },  
    .id_table   = tsc2007_idtable,  
    .probe      = tsc2007_probe,  
    .remove     = __devexit_p(tsc2007_remove),  
};  
static int __init tsc2007_init(void)  
{  
    return i2c_add_driver(&tsc2007_driver);  
}  
static void __exit tsc2007_exit(void)  
{  
    i2c_del_driver(&tsc2007_driver);  
}  
module_init(tsc2007_init);  
module_exit(tsc2007_exit);  
MODULE_AUTHOR("Kwangwoo Lee <kwlee@mtekvision.com>");  
MODULE_DESCRIPTION("TSC2007 TouchScreen Driver");  
MODULE_LICENSE("GPL");  


看一遍后就大概知道流程了。

当按下触摸板后产生中断,中断里面调用延时函数进入底半部程序。

在调度函数里面,先读取坐标,计算压力,然后根据压力大小上报坐标。

若触摸板一直按下,那么就重新调用,并通过压力值来看触摸板是否释放。

简单吧?

阿莫论坛20周年了!感谢大家的支持与爱护!!

你熬了10碗粥,别人一桶水倒进去,淘走90碗,剩下10碗给你,你看似没亏,其实你那10碗已经没有之前的裹腹了,人家的一桶水换90碗,继续卖。说白了,通货膨胀就是,你的钱是挣来的,他的钱是印来的,掺和在一起,你的钱就贬值了。
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-9-28 06:56

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表