您的位置:首页 > 运维架构 > Linux

linux kernel 2.6.35.7 input device use simulation spi interface ads7846 touch screen driver

2011-11-29 16:56 369 查看
/* ************************************************************************************
*	ads7846 touch screen	ads.c
*	Light	< lightwu@hotmail.com >
*	20011-11-28
*
***************************************************************************************/

#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/init.h>
#include <linux/serio.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <mach/hardware.h>
#include <mach/map.h>
#include <mach/regs-clock.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-cfg.h>
#include <mach/gpio.h>

#define ADS7846_CNV_NBR  8  //ADC连续转换的次数

struct ads7846_ts_info {

struct input_dev *dev;

unsigned int xp; //x方向位置
unsigned int yp; //y方向位置
unsigned int count; //adc转换次数
unsigned int cnv_nbr;
unsigned int x_buf[ADS7846_CNV_NBR];  //ad转换buf
unsigned int y_buf[ADS7846_CNV_NBR];

};

static struct ads7846_ts_info	*ts;
static struct input_dev *OFN_dev;
int absX = 0;
int absY = 0;

#define ADS7846_GPIO_MISO	6		//gpb6
#define ADS7846_GPIO_MOSI	7		//gpb7
#define ADS7846_GPIO_CLK	4		//gpb4
#define ADS7846_GPIO_CS		5		//gpb5

// ADS7846 Control Byte bit defines
#define ADS7846_CMD_START	0x0080
#define ADS7846_ADDR_BIT	4
#define ADS7846_ADDR_MASK 	(0x7<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_X  	(0x5<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Y  	(0x1<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Z1 	(0x3<<ADS7846_ADDR_BIT)
#define ADS7846_MEASURE_Z2 	(0x4<<ADS7846_ADDR_BIT)
#define ADS7846_8BITS     	(1<<3)
#define ADS7846_12BITS    	0
#define ADS7846_SER       	(1<<2)
#define ADS7846_DFR       	0
#define ADS7846_PWR_BIT   	0
#define ADS7846_PD      	0
#define ADS7846_ADC_ON  	(0x1<<ADS7846_PWR_BIT)
#define ADS7846_REF_ON  	(0x2<<ADS7846_PWR_BIT)
#define ADS7846_REF_ADC_ON 	(0x3<<ADS7846_PWR_BIT)

#define MEASURE_8BIT_X\
(unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_X | ADS7846_8BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_8BIT_Y\
(unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_Y | ADS7846_8BITS | ADS7846_DFR | ADS7846_PD)

#define MEASURE_12BIT_X \
(unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_X | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Y \
(unsigned short)(ADS7846_CMD_START | ADS7846_MEASURE_Y | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Z1 \
(unsigned char)(ADS7846_MEASURE_Z1 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)
#define MEASURE_12BIT_Z2 \
(unsigned char)(ADS7846_MEASURE_Z2 | ADS7846_12BITS | ADS7846_DFR | ADS7846_PD)

static inline void set_miso_as_up(void)//gpc0
{
s3c_gpio_setpull(S5PV210_GPB(6), S3C_GPIO_PULL_UP);
}

static inline void set_miso_as_input(void)//gpc0
{
gpio_direction_input(S5PV210_GPB(6));
}

static inline void set_cs_mosi_clk_as_output(void)//gpc1 2 3
{
gpio_direction_output(S5PV210_GPB(7),1);	//MOSI
gpio_direction_output(S5PV210_GPB(4),1);	//clk
gpio_direction_output(S5PV210_GPB(5),1);	//cs
}

static inline void set_cs_mosi_clk_as_up(void)//gpc1 2 3
{
s3c_gpio_setpull(S5PV210_GPB(4), S3C_GPIO_PULL_UP);
s3c_gpio_setpull(S5PV210_GPB(5), S3C_GPIO_PULL_UP);
s3c_gpio_setpull(S5PV210_GPB(7), S3C_GPIO_PULL_UP);
}

static inline void set_gpcx_value(int pinx ,int v)
{

gpio_set_value(S5PV210_GPB(pinx),v);
}

static inline int get_gpcx_value(int pinx)
{
return( gpio_get_value(S5PV210_GPB(pinx)) );
}

//读12bit
static unsigned int ads7846_Read_Data(void)
{
unsigned int i,temp=0x00;
for(i=0;i<12;i++)
{
temp <<=1;
set_gpcx_value(ADS7846_GPIO_CLK, 1); udelay(10);
set_gpcx_value(ADS7846_GPIO_CLK, 0); udelay(10);
if(get_gpcx_value(ADS7846_GPIO_MISO) != 0)temp++;
}
return (temp);
}
//写8bit
static void ads7846_Write_Data(unsigned int n)
{
unsigned char i;
set_gpcx_value(ADS7846_GPIO_CLK, 0);
for(i=0;i<8;i++)
{
if((n&0x80)==0x80)
set_gpcx_value(ADS7846_GPIO_MOSI, 1);
else
set_gpcx_value(ADS7846_GPIO_MOSI, 0);

n <<= 1;
set_gpcx_value(ADS7846_GPIO_CLK, 1);  udelay(10);
set_gpcx_value(ADS7846_GPIO_CLK, 0); udelay(10);
}
}

//ADS7846转换==//保存在ts->buf 当中//ADS7846_CNV_NBR是值为8的宏,含义是AD转换次数。
static void ads7846_conver_start(void)
{
int i;
unsigned int cmd[2];
unsigned int data[2];

set_gpcx_value(ADS7846_GPIO_CS, 0);
//开读
cmd[0] = MEASURE_12BIT_X;//宏,本程序内定义
cmd[1] = MEASURE_12BIT_Y;//宏,本程序内定义

/* CS# Low */
set_gpcx_value(ADS7846_GPIO_CS, 0);

//连续转换
for(ts->count=0; ts->count<ts->cnv_nbr;)
{
//分别读出x y坐标==
for(i=0; i<2; i++){
ads7846_Write_Data(cmd[i]);
udelay(40);
data[i] = ads7846_Read_Data();
}

//保存转换结果
ts->x_buf[ts->count]= data[0];
ts->y_buf[ts->count]= data[1];
ts->count++;
}
/* CS# High */
set_gpcx_value(ADS7846_GPIO_CS, 1);

}
//-----------------------------------------------------------------------------
//触摸屏数据滤波算法
//触摸屏AD连续转换N次后,按升序排列,再取中间几位值求平均
#define  TS_AD_NBR_PJ		4      	 //取中间4位求平均值
#define  TS_AD_NBR_MAX_CZ	10       //最大与最小的差值
static inline bool touch_ad_data_filter(unsigned int *buf0, unsigned int *buf1)
{
unsigned int i,j,k,temp,temp1,nbr=(ADS7846_CNV_NBR);
//将转换结果升序排列
//buf0
for (j= 0; j< nbr; j++)
{
for (i = 0; i < nbr; i++)
{
if(buf0[i]>buf0[i+1])//升序排列
{
temp=buf0[i+1];
buf0[i+1]=buf0[i];
buf0[i]=temp;
}
}
}

//buf1
for (j= 0; j< nbr; j++)
{
for (i = 0; i < nbr; i++)
{
if(buf1[i]>buf1[i+1])//升序排列
{
temp=buf1[i+1];
buf1[i+1]=buf1[i];
buf1[i]=temp;
}
}
}
//取中间值求平均==
k=((nbr-TS_AD_NBR_PJ)>>1);
temp = 0;temp1 = 0;
//检查值是否有效==
if((buf0[k+TS_AD_NBR_PJ-1]-buf0[k]>TS_AD_NBR_MAX_CZ)||(buf1[k+TS_AD_NBR_PJ-1]-buf1[k]>TS_AD_NBR_MAX_CZ)) //无效值
{

return 0;
}
//--
//将中间指定位数累加
for(i=0;i<TS_AD_NBR_PJ;i++)
{
temp += buf0[k+i];
temp1 += buf1[k+i];
}
//求平均值,将结果保存在最低位
buf0[0]=temp/TS_AD_NBR_PJ;
buf1[0] = temp1/TS_AD_NBR_PJ;
//--
return 1;
}

static inline bool get_down(void)	//获取中断口电平状态
{
return (gpio_get_value (S5PV210_GPH0(7)));  	// 中断管脚电平状态
}
/*===========================================================================================
touch_timer_get_value这个函数的调用:

1、  触摸笔开始点击的时候, 在中断函数touch_down里面被调用,不是直接调用,而是设置定时器超时后调用
这样是为了去抖动

2、  touch_timer_get_value被调用一次后,如果pendown信号有效,则touch_timer_get_value会被持续调用
也是通过定时器实现的

touch_timer_get_value这个函数的功能:
启动7846转换,直到连续转换8次后,再滤波处理,获得有效值,并向上报告触摸屏事件

============================================================================================*/
static void touch_timer_get_value(unsigned long data);

static DEFINE_TIMER(touch_timer, touch_timer_get_value, 0, 0);

static void touch_timer_get_value(unsigned long data) {

int pendown;

//printk("touch_timer_get_value\n");
pendown = get_down();

if(!pendown)	//	检测按下状态
{

//关中断===
disable_irq(IRQ_EINT7);
//启动ADS7846转换==
ads7846_conver_start();
//开中断==
enable_irq(IRQ_EINT7);

if(touch_ad_data_filter(ts->x_buf,ts->y_buf)) // 滤波函数处理
{

ts->xp = ts->x_buf[0];
ts->yp = ts->y_buf[0];

printk("ts->xp = %d,ts->yp = %d\n",ts->xp,ts->yp);

//--------------------------------------------------------------------
absX = ts->xp ;
absY = ts->yp ;

input_report_abs(OFN_dev, ABS_X, absX);		//X轴坐标
input_report_abs(OFN_dev, ABS_Y, absY);		//Y轴坐标
input_report_key(OFN_dev, BTN_TOUCH, 1);	//按下 按键
input_report_abs(OFN_dev, ABS_PRESSURE, 1);	//按下 压力
input_sync(OFN_dev);	//事件同步
input_report_key(OFN_dev, BTN_TOUCH, 0);	//抬起 按键值
input_report_abs(OFN_dev, ABS_PRESSURE, 0);	//抬起 压力值
input_sync(OFN_dev);
//--------------------------------------------------------------------

}

mod_timer(&touch_timer, jiffies + 2);   //重新设置系统定时器,超时后,又会调用touch_timer_get_value
//jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ次。
//因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms
//这里系统配置提HZ是100

}
else
{

/*	ads7846_conver_start();
if(touch_ad_data_filter(ts->x_buf,ts->y_buf)) // 滤波函数处理
{

ts->xp = ts->x_buf[0];
ts->yp = ts->y_buf[0];

printk("ts->xp = %d,ts->yp = %d\n",ts->xp,ts->yp);

//--------------------------------------------------------------------
absX = ts->xp ;
absY = ts->yp ;

input_report_abs(OFN_dev, ABS_X, absX);		//X轴坐标
input_report_abs(OFN_dev, ABS_Y, absY);		//Y轴坐标

input_report_key(OFN_dev, BTN_TOUCH, 0);	//抬起 按键值
input_report_abs(OFN_dev, ABS_PRESSURE, 0);	//抬起 压力值
input_sync(OFN_dev);
//--------------------------------------------------------------------

}
*/
}

}

static irqreturn_t touch_down(int irqno, void *param)			//中断处理函数
{
printk("Touch down -- Enter Interrupt 0 !\n");

//稍后调用touch_timer_get_value,去抖动
mod_timer(&touch_timer, jiffies + 15);  //等ADS7846转换完成了再读
//同时还可以防抖动,如果定时器没有超时的这段时间里,
//发生了抬起和按下中断,则定时器的值会被重设,不会超时
//内核配置时HZ值设为100,即1个jiffy等于10ms,
//touch_timer_get_value(1);  //直接调用会有抖动

return IRQ_RETVAL(IRQ_HANDLED);
}

//-------------------------------------------
static int __init ads7846_ts_init(void)
{
printk( KERN_INFO "Enter mini210 ads7846 Touchscreen driver\n" );

int ret = 0;

printk("ads7846_ts_probe start!\n");
//给ads7846_ts_info指针分配内存==
ts = kzalloc(sizeof(struct ads7846_ts_info), GFP_KERNEL);
ts->cnv_nbr = ADS7846_CNV_NBR;
ts->xp = 0;
ts->yp = 0;
ts->count = 0;
//申请中断==
s3c_gpio_setpull(S5PV210_GPH0(7), S3C_GPIO_PULL_UP);
gpio_direction_input(S5PV210_GPH0(7));
s3c_gpio_cfgpin(S5PV210_GPH0(7), S3C_GPIO_SFN(0xf0000000));	//设置为外部中断 eint0 ,参考GP0CON【】
set_irq_type(IRQ_EINT7, IRQ_TYPE_EDGE_FALLING);
//-------------------------------------------------------------------------
OFN_dev = input_allocate_device();				// 申请一个输入设备
if (!OFN_dev) return -ENOMEM;
OFN_dev->name = "TouchScreen Pipe";				// 触摸屏设备属性信息
OFN_dev->phys = "input(ts)";
OFN_dev->id.bustype = BUS_RS232;
OFN_dev->id.vendor  = 0xDEAD;
OFN_dev->id.product = 0xBEEF;
OFN_dev->id.version = 0x0101;

input_set_abs_params(OFN_dev, ABS_X, 0, 4000, 0, 0);			//
input_set_abs_params(OFN_dev, ABS_Y, 0, 4000, 0, 0);			//
input_set_abs_params(OFN_dev, ABS_PRESSURE, 0, 1, 0, 0);		// 压力

OFN_dev->evbit[0] = BIT_MASK(EV_SYN) | BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
OFN_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
//---------------------------------------------------------------------------
ret = request_irq(IRQ_EINT7, touch_down,IRQ_TYPE_EDGE_BOTH,
"ads7864_touch", ts);
if (ret != 0) {
printk("ads7846_ts.c: Could not allocate ts IRQ_EINT !\n");
ret = -EIO;
goto fail;
}
//--------------------------------------------------------------------------------
ret = input_register_device(OFN_dev);
if(ret)
{
free_irq(IRQ_EINT7, OFN_dev);
input_free_device(OFN_dev);
return -1;
}
//--------------------------------------------------------------------------------
//初始化GPIO==
set_miso_as_up();
set_miso_as_input();
set_cs_mosi_clk_as_up();
set_cs_mosi_clk_as_output();
set_gpcx_value(ADS7846_GPIO_MOSI ,1);
set_gpcx_value(ADS7846_GPIO_CS ,1);
set_gpcx_value(ADS7846_GPIO_CLK ,1);
//--
printk("ads7846_ts_probe end!\n");

return 0;

fail:
disable_irq(IRQ_EINT7);
free_irq(IRQ_EINT7, ts);

return ret;
}

static void __exit ads7846_ts_exit(void)
{
printk("Exit mini210 ads7846 Touchscreen driver\n");
printk(KERN_INFO "ads7846_ts_remove() of TS called !\n");

disable_irq(IRQ_EINT7);
free_irq(IRQ_EINT7, ts);
}

module_init(ads7846_ts_init);
module_exit(ads7846_ts_exit);

MODULE_AUTHOR("LIGHT");
MODULE_LICENSE("GPL");





                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: