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

/* ************************************************************************************
*	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");





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值