linux 内核驱动-- 4412 触摸屏驱动编写--tsc2007

接触linux驱动也有一段时间了,写了一些驱动,最近写了个触摸屏驱动,就记录下来了,就当一种回忆吧,不多说废话了,直接分析下吧。

一。本驱动的硬件是TI的TSC2007  它是IIC接口的,还有一条线是中断作用,当触摸屏被触摸的时候,这条线会下拉,所以我们4412就需要做好中断配置,当被中断时,就用iic接口去读取坐标值,上报即可。总体的思想就这样。

原理图

 

二。代码

1.i2c_client 部分

#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/wait.h>
#include <linux/i2c.h>

static struct i2c_client *ts2007_i2c_client;

static const unsigned short normal_i2c[] = { 0x90, 0x48, I2C_CLIENT_END };  //  ts2007的i2c地址按我的原理图是0x48

static int ts2007_i2c_dev_init(void){

    struct i2c_adapter *i2c_adap;
    
    struct i2c_board_info i2c_info;    

    i2c_adap = i2c_get_adapter(7);    //使用第7个I2C适配器

    memset(&i2c_info, 0, sizeof(struct i2c_board_info));

    strlcpy(i2c_info.type, "ts2007_xbf", I2C_NAME_SIZE);    /*i2c的client 和driver靠这个名字匹配,当匹配成功调用i2c_driver的prob函数*/

    ts2007_i2c_client = i2c_new_probed_device(i2c_adap, &i2c_info,normal_i2c, NULL);    

    i2c_put_adapter(i2c_adap);
        
    return 0;
}

static void ts2007_i2c_dev_exit(void){

    i2c_unregister_device(ts2007_i2c_client);    

}
module_init(ts2007_i2c_dev_init);
module_exit(ts2007_i2c_dev_exit);
MODULE_LICENSE("GPL");
2.   i2c_driver

#include <linux/module.h>
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/gpio.h>
#include <linux/delay.h>
#include <linux/ctype.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/workqueue.h>
#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/regs-gpio.h>
#include <mach/gpio-exynos4.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <linux/i2c.h>
#include <linux/input.h>

#define  IRQ_TS    IRQ_EINT(0)

  #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 ts2007_console {

    int x_val;

    int y_val;

    int z1;

    int z2;
    
    int press_val;

    char pree_down;

};



static struct delayed_work ts2007_work_tasklet;

static struct i2c_client *ts2007_i2c_client;

static  struct ts2007_console   ts2007_data;

static struct input_dev *input_dev;


static int  Get_ts_val(char cmd){

   int data;
   int val;

   data=i2c_smbus_read_word_data(ts2007_i2c_client, cmd);

   if(data<0){
        printk("tsc2007 read error \n");
        return data;
       }

   
   val=  swab16(data) >> 4;

   return val;
}

/×计算压力,具体的看手册×/
 static u32 tsc2007_calculate_pressure(void)  
{  
    u32 rt = 0;  
    /* range filtering */  
    if (ts2007_data.x_val == MAX_12BIT)  
        ts2007_data.x_val=0;  
    if (likely(ts2007_data.x_val && ts2007_data.z1)) {  
        /* compute touch pressure resistance using equation #1 */  
        rt = ts2007_data.z2- ts2007_data.z1;  
        rt *= ts2007_data.x_val;  
        rt *= 180;  
        rt /= ts2007_data.z1;  
        rt = (rt + 2047) >> 12;  
    }  
    return rt;  
}  

/×获取坐标值×/

static void Read_TS_VAL(void){

    ts2007_data.y_val=Get_ts_val(READ_Y);

    ts2007_data.x_val=Get_ts_val(READ_X);

    ts2007_data.z1=Get_ts_val(READ_Z1);

    ts2007_data.z2=Get_ts_val(READ_Z2);

    ts2007_data.press_val=tsc2007_calculate_pressure();

/×我发现我的触摸屏幕好像装反了,所以用了这下面两步×/

    ts2007_data.y_val=MAX_12BIT-ts2007_data.y_val;
    
    ts2007_data.x_val=MAX_12BIT-ts2007_data.x_val;
        
    //Get_ts_val(PWRDOWN);

}

/×多次读取求平均值×/

static void Get_abs_press(void){
    int sum_x=0;
    int sum_y=0;
    int i=0;
    for(i=0;i<6;i++){
        
        Read_TS_VAL();

        sum_x+=ts2007_data.x_val;

        sum_y+=ts2007_data.y_val;
    }

    ts2007_data.x_val=sum_x/6;
    
    ts2007_data.y_val=sum_y/6;

    Get_ts_val(PWRDOWN);

}

/×读取坐标数据并上报  队列×/
static void ts2007_work_queue(struct work_struct *work){

                
        //printk(" start conversion:  ");

       /*判断此时还在按下吗*/
        if(!gpio_get_value(EXYNOS4_GPX0(0))){

                
                ts2007_data.pree_down=2;
            
                Get_abs_press();

                if(ts2007_data.press_val> MAX_12BIT)
                    goto giveup_report;
                
                //printk("X:%d Y:%d \n",ts2007_data.x_val,ts2007_data.y_val);

                /*上报坐标数据*/

                input_report_abs(input_dev, ABS_X, ts2007_data.x_val);
                input_report_abs(input_dev, ABS_Y, ts2007_data.y_val);

                input_report_abs(input_dev, ABS_PRESSURE,ts2007_data.press_val);

                input_report_key(input_dev, BTN_TOUCH, 1);
                
                input_sync(input_dev);

                            /×继续执行队列,这样就可以用于长按×/

giveup_report:    schedule_delayed_work(&ts2007_work_tasklet,  
                      msecs_to_jiffies(TS_POLL_PERIOD));
            
                                      
        }else{        
                    
                    ts2007_data.pree_down--;

                    if(ts2007_data.pree_down ==1){

                    input_report_abs(input_dev, ABS_PRESSURE, 0);

                    input_report_key(input_dev, BTN_TOUCH, 0);

                    input_sync(input_dev);
                    
                    //printk("press up \n");
                    
                    enable_irq(IRQ_TS);                

                    return;

                    }else{

                    enable_irq(IRQ_TS);    
                    
                    }
        }    
}

/×

当发生触摸时,触摸此函数,我们先关闭此中断,当放开触摸屏时在打开,等待触摸,

此时用一个工作队列去读取数据

linux中断中断函数不能做耗时太长的动作

×/

static irqreturn_t TS_IRQ(int irq, void *dev_id){

    disable_irq_nosync(IRQ_TS);

    //printk("TS_IRQ \n");

    schedule_delayed_work(&ts2007_work_tasklet,  
                      msecs_to_jiffies(TS_POLL_DELAY));  /×让队列1ms后去执行,可以防抖动×/
    
       return IRQ_RETVAL(IRQ_HANDLED);;

}

/*当i2c_diver和i2c_client匹配时,调用这个prob函数

在这个函数里我们做这么几件事

1.注册中断,当发生触摸时,就会触摸中断,这时我们就去读取坐标

2.注册input_device  ,我们用 此 来上报数据给系统

*/
static int ts2007_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
{

        printk("find ts2007_i2c_dev \n");

        ts2007_i2c_client=client;

        if(request_irq(IRQ_TS, TS_IRQ, IRQF_TRIGGER_FALLING , "TS_IRQ", NULL))        
           return -1;

        INIT_DELAYED_WORK(&ts2007_work_tasklet, ts2007_work_queue);


        input_dev= input_allocate_device();

    
        if(input_dev==NULL)
            goto inputerr;

        /* 2. 设置 */
        /* 2.1 能产生哪类事件 */
        set_bit(EV_KEY, input_dev->evbit);
        set_bit(EV_ABS, input_dev->evbit);

        /* 2.2 能产生这类事件里的哪些事件 */
        set_bit(BTN_TOUCH, input_dev->keybit);

        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);/×tsc2007  是12位的adc,最大值是MAX_12BIT×/
        input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0);/×tsc2007  是12位的adc,最大值是MAX_12BIT×/

        input_dev->name = "TSC2007 Touchscreen";
        input_dev->id.bustype = BUS_I2C;
        
        /* 3. 注册 */
        if(input_register_device(input_dev))
            goto registererr;

        /*prepare for test*/
        Get_ts_val(PWRDOWN);
    
        return  0;
        
registererr:
        input_free_device(input_dev);
inputerr:
        free_irq(IRQ_TS, NULL);    


        return -1;
}

static int ts2007_i2c_rm(struct i2c_client *client)
{


        input_unregister_device(input_dev);

        input_free_device(input_dev);
        
        free_irq(IRQ_TS, NULL);    
        
        return 0;
}


static const struct i2c_device_id ts2007_i2c_id[] = {
    { "ts2007_xbf", 0 },/*i2c_driver和i2c_client 靠这个名字匹配*/
};

static struct i2c_driver ts2007_i2c_driver = {
    .driver = {
        .name    = "ts2007_xbf",
        .owner  = THIS_MODULE,
    },
    .probe        = ts2007_i2c_probe,
    .remove        = ts2007_i2c_rm,
    .id_table    = ts2007_i2c_id,
};

static int  ts2007_init_drv(void){

        i2c_add_driver(&ts2007_i2c_driver);  /×注册i2c_driver×/
        
        return 0;
}

static void ts2007_exit_drv(void){
    
    i2c_del_driver(&ts2007_i2c_driver);

}

module_init(ts2007_init_drv);
module_exit(ts2007_exit_drv);
MODULE_LICENSE("GPL");

三。结果tslib测试

 

 

 

 

 

 

 

 

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值