Xunwei embedded linux driver development notes (11) - touch screen driver

Summary

The content of this section is a general review of the ten lessons learned before. It is very comprehensive. To complete the touch screen driver, you need to learn the following knowledge: 1. The first section
of driver development principles

2. Platform platform bus model
Section 3

3. The fourth section of the device tree

4. Section 5 of the gpio subsystem

5. Interruption Principle
Section VI

6. Section 7 of the work queue

7. Input Subsystem
Section IX

8. The tenth chapter of iic driving principle

In order to clearly describe the touch screen driving experiment, each sentence of the code is annotated in detail:

Touch screen driver code

#include<linux/init.h>
/*驱动模块*/
#include<linux/module.h>
/*IIC*/
#include <linux/i2c.h>
/*GPIO*/
#include <linux/gpio.h>
/*of函数获取gpio*/
#include <linux/of_gpio.h>
/*of函数获取中断*/
#include <linux/of_irq.h>
/*中断*/
#include <linux/interrupt.h>
/*输入子系统*/
#include <linux/input.h>
/*延时函数*/
#include <linux/delay.h>
/*工作队列*/
#include <linux/workqueue.h>
/*初始化设备节点*/
struct device_node *ft5x06_device_node;
/*定义设备编号*/
#define DEVICE_NODE 0X00 
/*屏幕设备寄存器地址*/
#define ID_G_MODE 0Xa4
/*定义中断号*/
int irq;
/*定义输入子系统设备*/
static struct input_dev *ft5x06_dev;
/*定义IIC适配器*/
static struct i2c_client *ft5x06_client;
/*声明IIC写函数*/
static void ft5x06_write_reg(u8 reg_addr,u8 data,u8 len);
/*中断入口函数*/
static void ft5x06_func(struct work_struct *work);
/*初始化工作队列*/
DECLARE_WORK(ft5x06_work,ft5x06_func);

/*定义IIC写函数*/
static void ft5x06_write_reg(u8 reg_addr,u8 data,u8 len)
{
    
    
		/*定义缓冲区大小*/
    u8 buff[256];
	/*初始化i2c_msg结构体*/
    struct i2c_msg msgs[] = {
    
    
            [0] = {
    
    
            /*主机地址*/
            .addr = ft5x06_client->addr,
            /*0为写*/
            .flags = 0,
            /*读取长度*/
            .len = len+1,
            /*缓存区的地址*/
            .buf = buff,
            }
    };
		/*存放寄存器的地址*/
    buff[0] = reg_addr;
		/*拷贝len字节的数据长度到buff[1]之后位置*/
    memcpy(&buff[1],&data,len);
	 /*iic数据包传送,参数1:适配器,参数2:数据包,参数3:数量*/
    i2c_transfer(ft5x06_client->adapter,msgs,1);  
}

/*定义IIC读函数*/
static int ft5x06_read_reg(u8 reg_addr)
{
    
    
		/*定义读出数据的返回值*/
    u8 data;
		/*封装结构体*/
    struct i2c_msg msgs[] = {
    
    
        [0] = {
    
    
        		/*主机地址*/
            .addr = ft5x06_client->addr,
            /*flags为0表示写入*/
            .flags = 0,
            /*读出字节长度*/
            .len = sizeof(reg_addr),
            /*读取函数的地址*/
            .buf = &reg_addr,
        },

        [1] = {
    
    
        		
            .addr = ft5x06_client->addr,
            .flags = 1,
            .len = sizeof(data),
            .buf = &data,
        }
    };
		/*iic数据包传送,参数1:适配器,参数2:数据包,参数3:数量为2*/
    i2c_transfer(ft5x06_client->adapter,msgs,2);
		/*返回读取到的数据*/
    return data;

}



static const struct i2c_device_id ft5x06_id_ts[] = {
    
    
    {
    
    "xxx",0},
    {
    
    }
};

static const struct of_device_id ft5x06_id[] = 
{
    
    
    {
    
    .compatible = "edt,edt-ft5306",0,},
    {
    
    .compatible = "edt,edt-ft5x06",0,},
    {
    
    .compatible = "edt,edt-ft5406",0,},
    {
    
    }

};

static void ft5x06_func(struct work_struct *work)
{
    
    
    //读取寄存器中的内容
    int TOUCH1_XH,TOUCH1_XL,x;

    int TOUCH1_YH,TOUCH1_YL,y;

    int TD_STATUS;

    //读取TOUCH1_XH寄存器的值
    TOUCH1_XH = ft5x06_read_reg(0x03);
    //读取TOUCH1_XL寄存器的值
    TOUCH1_XL = ft5x06_read_reg(0x04);

    //只想要12位,获取到X的坐标值
    x = ((TOUCH1_XH<<8)|TOUCH1_XL)&0x0fff;

     //读取TOUCH1_YH寄存器的值
    TOUCH1_YH = ft5x06_read_reg(0x05);
    //读取TOUCH1_XL寄存器的值
    TOUCH1_YL = ft5x06_read_reg(0x06);
    //只想要12位,获取到Y的坐标值
    y = ((TOUCH1_YH<<8)|TOUCH1_YL)&0x0fff;

    //读取寄存器TD_STATUS的值
    TD_STATUS = ft5x06_read_reg(0x02);
    //获取手指在屏幕上
    TD_STATUS = TD_STATUS&0xf;

    if (TD_STATUS == 0)//判断有没有手指按上,如果有的话,上报按下去的事件,如果没有的话,上报抬手事件
    {
    
    
        input_report_key(ft5x06_dev,BTN_TOUCH,0);//上报按键事件

        input_sync(ft5x06_dev);
    }
    else{
    
    
        input_report_key(ft5x06_dev,BTN_TOUCH,1);//上报按键事件

        input_report_abs(ft5x06_dev,ABS_X,x);

        input_report_abs(ft5x06_dev,ABS_Y,x);

        input_sync(ft5x06_dev);//上报完成

        

    }
    

}

static irqreturn_t ft5x06_handler(int irq,void* args)
{
    
    
    //启动中断下文
    schedule_work(&ft5x06_work);
    return IRQ_HANDLED;
}



int ft5x06_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    
    
		/*定义返回值*/
    int ret;
		/*定义中断引脚*/
    int ft5x06_irq_gpio;
		/*定义复位引脚*/
    int ft5x06_reset_gpio;
    /*打印函数*/
    printk("ft5x06_probe\n");
	 /*因为要在别的地方用到client,所以要把他复制出来*/
    ft5x06_client = client;
    /*设备树位置,根据设备树信息查找屏幕挂载的位置信息,获得触摸芯片的节点*/
    ft5x06_device_node = of_find_node_by_path("/soc/aips-bus@02100000/i2c@021a4000/edt-ft5x06@38");
		/*出错判断*/
    if (ft5x06_device_node == NULL)
    {
    
    
        printk("of_find_node_by_path is error\n");
        return -1;
    }
    printk("ft5x06_device_node is %s \n",ft5x06_device_node->name);

    /*获得中断引脚的GPIO编号——节点名称,IO名称,第几个*/
    ft5x06_irq_gpio = of_get_named_gpio(ft5x06_device_node,"irq-gpios",0);
		/*出错判断*/
    if (ft5x06_irq_gpio<0)
    {
    
    
        printk("ft5x06_irq_gpio of_get_namd_gpio is error\n");
        return -2;
    }
    /*获得复位引脚的GPIO编号——节点名称,IO名称,第几个*/
    ft5x06_reset_gpio = of_get_named_gpio(ft5x06_device_node,"reset-gpios",0);
	 /*出错判断*/
    if (ft5x06_reset_gpio<0)
    {
    
    
        printk("ft5x06_reset_gpio of_get_namd_gpio is error\n");
        return -3;
    }
    printk("ft5x06_irq_gpio is %d \n",ft5x06_irq_gpio);
    printk("ft5x06_reset_gpio is %d \n",ft5x06_reset_gpio);
    /*避免申请失败*/
    gpio_free(ft5x06_irq_gpio);
    /*申请中断引脚*/
    ret = gpio_request(ft5x06_irq_gpio,"irq_gpio");
    /*出错判断*/
    if (ret<0)
    {
    
    
        printk("gpio_request is error\n");
        return -4;
    }
     /*申请复位引脚*/
    ret = gpio_request(ft5x06_reset_gpio,"reset_gpio");
    if (ret < 0)
    {
    
    
        printk("gpio_request is error\n");
        return -5;
    }
    /*设置中断引脚为输入*/
    gpio_direction_input(ft5x06_irq_gpio);
    /*操作一下复位的引脚,设置方向为输出,然后停止复位*/
    gpio_direction_output(ft5x06_reset_gpio,0);
    msleep(5);
    /*设置输出为高电平*/
    gpio_set_value(ft5x06_reset_gpio,1);

    /*获取中断号*/
    irq = gpio_to_irq(ft5x06_irq_gpio);
    /*中断号、触发方式、中断名称、传递参数*/
    ret = request_irq(irq,ft5x06_handler,IRQ_TYPE_EDGE_FALLING|IRQF_ONESHOT,"ft5x06_irq",NULL);
	/*出错判断*/
    if (ret<0)
    {
    
    
        printk("requset_irq is error\n");
        goto error_requset_irq;
    }
    
    /*工作模式为正常模式:地址:写入数据:数据长度*/
    ft5x06_write_reg(DEVICE_NODE,0,1);
    
    /*工作模式为中断模式:地址:写入数据:数据长度*/
    ft5x06_write_reg(ID_G_MODE,1,1);
		/*输入子系统申请设备*/
    ft5x06_dev = input_allocate_device();
		/*初始化设备名称*/
    ft5x06_dev->name = "ft5x06_input_test";
    /*支持按键事件*/
    __set_bit(EV_KEY,ft5x06_dev->evbit);
    /*支持绝对坐标事件*/
    __set_bit(EV_ABS,ft5x06_dev->evbit);
    /*支持触摸按键检测*/
    __set_bit(BTN_TOUCH,ft5x06_dev->keybit);
    /*支持X坐标*/
    __set_bit(ABS_X,ft5x06_dev->absbit);
    /*支持Y坐标*/
    __set_bit(ABS_Y,ft5x06_dev->absbit);
    /*支持压力检测*/
    __set_bit(ABS_PRESSURE,ft5x06_dev->keybit);

    input_set_abs_params(ft5x06_dev,ABS_X,0,1024,0,0);
    /*设置Y坐标*/
    input_set_abs_params(ft5x06_dev,ABS_Y,0,600,0,0);
    /*设置压力数值*/
    input_set_abs_params(ft5x06_dev,ABS_PRESSURE,0,255,0,0);
    /*注册到设备中*/
    ret = input_register_device(ft5x06_dev);
	 /*出错判断*/
    if (ret<0)
    {
    
    
        printk("input_register_device is error");
        goto error_input_register;
    }
   
    return 0;

/*错误处理*/
error_requset_irq:
    free_irq(irq,NULL);  
    
/*错误处理*/
error_input_register:
		/*释放中断*/
    free_irq(irq,NULL);
    /*注销设备*/
    input_unregister_device(ft5x06_dev);
    /*是否输入子系统*/
    input_free_device(ft5x06_dev);
    return ret;
}

/*remove函数*/
int ft5x06_remove(struct i2c_client *i2c_client)
{
    
    
    return 0;
}

/*初始化i2c_driver结构体*/
static struct i2c_driver ft5x06_driver = {
    
     
			/*必须初始化的成员*/
			//int (*probe)(struct i2c_client *, const struct i2c_device_id *); 	
			//int (*remove)(struct i2c_client *); 
			//struct device_driver driver; 
			//const struct i2c_device_id *id_table;

			/*driver成员初始化*/
        .driver = {
    
    
        	/*表示当前模块下*/
            .owner = THIS_MODULE,
            /*驱动名称*/
            .name = "ft5x06_test",
            /*匹配id名称*/
            .of_match_table = ft5x06_id,
        },
		/*probe函数*/
        .probe = ft5x06_probe,
       /*remove函数*/
        .remove = ft5x06_remove,
        /*设备名称匹配*/
        .id_table = ft5x06_id_ts
         
}; 


/*驱动初始化*/
static int ft5x06_driver_init(void)
{
    
    
    /*返回值定义*/
    int ret;
		/*添加iic驱动*/
    ret = i2c_add_driver(&ft5x06_driver);
    /*出错判断*/
    if (ret < 0)
    {
    
    
        printk("i2c_add_driver is error\n");
        return ret;
    }
    printk("i2c_add_driver init\n");
    return 0;
}


/*定义驱动退出函数*/
static void ft5x06_driver_exit(void)
{
    
    
    /*删除iic设备*/
    i2c_del_driver(&ft5x06_driver);
	/*释放中断*/
    free_irq(irq,NULL);
	/*注销输入子系统*/
    input_unregister_device(ft5x06_dev);
	/*释放设备*/
    input_free_device(ft5x06_dev);
    printk("i2c_del_driver exit\n");

}
/*模块初始化*/
module_init(ft5x06_driver_init);
/*模块退出*/
module_exit(ft5x06_driver_exit);
MODULE_LICENSE("GPL");

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324135231&siteId=291194637