总结篇
这一小节内容是对之前学习的十节课进行总复习,综合性非常强,完成触摸屏驱动需要学会如下知识:
1、驱动开发原理
第一节
2、platform平台总线模型
第三节
3、设备树
第四节
4、gpio子系统
第五节
5、中断原理
第六节
6、工作队列
第七节
7、input子系统
第九节
8、iic驱动原理
第十节
为了清晰描述触摸屏驱动实验,对代码每一句进行详细注释:
触摸屏驱动代码
#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 = ®_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");