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