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

本文详细介绍了一种基于TSC2007芯片的触摸屏驱动开发过程,包括IIC通信配置、中断处理、数据读取及坐标上报机制。通过具体代码示例,展示了如何在Linux环境下实现触摸屏的驱动编写。

接触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测试

 

 

 

 

 

 

 

 

 

 

 

<think>好的,我需要帮用户解决在Linux系统中配置TSC2007驱动程序的问题。首先,我要回顾用户提供的引用内容,找出相关的配置步骤和关键点。 用户提到了几个引用,其中引用[2]、[3]、[4]、[5]都涉及Linux内核TSC2007驱动的配置。例如,引用[2]提到在Kconfig中添加驱动选项,引用[3]和[4]讨论了设备树配置和probe函数的作用,引用[5]提到了硬件连接和中断配置。 首先,配置驱动程序通常需要内核配置、设备树修改、编译和加载驱动。根据引用[2],用户需要在内核的Kconfig文件中添加对应的配置选项,这样在make menuconfig时才能看到并启用驱动。然后,根据引用[4],设备树的编写是关键,因为驱动会从设备树获取资源,比如I2C地址、中断号等。引用[5]指出硬件连接中的中断线需要正确配置,驱动中使用中断来触发坐标读取。 接下来,我需要将这些步骤组织成清晰的流程。可能需要注意内核版本的不同,比如引用[4]提到的是4.19.99内核,设备树的使用是必须的,而旧版本可能使用平台设备注册方式,如引用[3]中的情况。因此,要区分设备树配置和传统平台设备的差异。 另外,校准部分在引用[3]中提到,可能需要用户空间工具或内核配置来设置校准参数。测试部分需要确保驱动加载后,输入子系统能正确上报事件,可以用evtest或查看/dev/input目录下的设备节点。 需要验证每个步骤的正确性,比如检查.config文件是否启用了CONFIG_INPUT_TSC2007,设备树节点是否正确,中断是否申请成功,I2C通信是否正常。如果出现问题,可能需要检查硬件连接、设备树语法、内核配置选项是否开启依赖项等。 最后,生成相关问题时,要考虑用户可能遇到的后续问题,比如驱动调试方法、校准配置、不同内核版本的差异等,这样能帮助用户更全面地理解和解决问题。</think>### 如何在 Linux 系统中配置 TSC2007 驱动程序? #### 一、配置步骤概述 1. **内核配置**:启用 TSC2007 驱动支持 2. **设备树配置**:添加硬件接口定义 3. **驱动加载与验证**:编译内核或模块并测试功能 --- #### 二、详细配置方法 **1. 内核配置** - 进入内核源码目录,执行 `make menuconfig` - 导航至路径: $$ \text{Device Drivers} \rightarrow \text{Input device support} \rightarrow \text{Touchscreens} $$ - 勾选 `TSC2007 based touchscreens`(对应配置项 `CONFIG_INPUT_TSC2007`)[^2][^4]。 **2. 设备树配置** - 在设备树文件(如 `*.dts`)中添加以下节点,需包含以下关键属性: ```dts &i2c1 { // 根据实际I2C总线编号调整 tsc2007@48 { // I2C地址需与硬件一致(如0x48) compatible = "ti,tsc2007"; reg = <0x48>; interrupt-parent = <&gpio>; // 中断引脚所属GPIO控制器 interrupts = <IRQ_TYPE_EDGE_FALLING>; // 中断触发类型 pinctrl-names = "default"; pinctrl-0 = <&tsc2007_pins>; // 引脚控制配置 ti,x-plate-ohms = <660>; // X轴电阻值(需根据硬件手册调整) }; }; ``` **关键点**: - `compatible` 必须匹配驱动中定义的 `of_device_id` - 中断引脚需与硬件连接一致(如引用[5]中描述的触摸触发下拉信号) **3. 驱动加载与测试** - **编译内核/模块**: ```bash make drivers/input/touchscreen/tsc2007_core.ko ``` 或直接编译整个内核- **加载驱动**: ```bash insmod tsc2007_core.ko ``` - **验证功能**: - 使用 `evtest` 工具检测输入事件 - 查看系统日志 `dmesg | grep tsc2007` 确认驱动初始化是否成功 --- #### 三、常见问题排查 1. **驱动未加载** - 检查内核配置是否启用 `CONFIG_INPUT_TSC2007` - 确认设备树节点与驱动匹配(`compatible` 属性) 2. **中断未触发** - 测量硬件中断引脚电平是否正常 - 检查设备树中 `interrupts` 定义与硬件连接是否一致[^5] 3. **坐标数据错误** - 校准电阻参数 `ti,x-plate-ohms`(参考硬件手册调整)[^3] --- #### 四、引用说明 [^1]: 触摸屏驱动移植的通用方法可参考历史文档 [^2]: 内核配置需通过 Kconfig 添加选项 : 驱动入口函数与校准参数设置相关 : 设备树绑定与资源获取是关键步骤 [^5]: 硬件中断和I2C通信需正确配置 --- §§ 1. 如何调试 TSC2007 驱动的中断触发问题? 2. 在设备树中如何确定 I2C 地址和中断引脚编号? 3. TSC2007 触摸屏校准参数如何配置?
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值