基于linux的mini2440触摸屏驱动分析(yz版)

来源:互联网 发布:java线程池使用场景 编辑:程序博客网 时间:2024/04/30 13:15

驱动程序的大致框架分析:

s3c2410ts_init 进行初始化,申请中断,注册输入设备工作,s3c2410ts_exit 注销设备,注销中断,释放资源。按下触摸屏后,会触发TC中断,进入TC中断处理函数stylus_updown。stylus_updown中调用touch_timer_fire启动AD转换。转换结束会触发ADC中断,进入ADC中断处理函数stylus_action。stylus_action中,若转换次数未达4次,会再次启动AD转换,否则启动1个时间滴答的内核定时器,调用touch_timer_fire进行数据上报。接着若触摸屏仍处于被按下的状态,会再次启动AD转换,转换4次后继续进行数据上报。直至触摸屏被释放,AD转换才会停止。


#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/gpio.h> 
#include <asm/io.h> 
#include <asm/irq.h> 
#include <plat/regs-adc.h> 
#include <mach/regs-gpio.h>


#define S3C2410TSVERSION 0x0101


#define WAIT4INT(x)  (((x)<<8) | \ 
       S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
       S3C2410_ADCTSC_XY_PST(3)) 


#define AUTOPST      (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | 
S3C2410_ADCTSC_XP_SEN | \ 
       S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0)) 


static char *s3c2410ts_name = "s3c2410 TouchScreen"; 
 
static struct input_dev *dev; 
static long xp; 
static long yp; 
static int count; 
 
extern struct semaphore ADC_LOCK; 
static int OwnADC = 0; 
 
 
static void __iomem *base_addr; 


/*read already************************************************************************/
 
/* the effect of s3c2410_gpio_cfgpin is configuring a pin to a function */
static inline void s3c2410_ts_connect(void) 

 s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); //configure GPG12 to "reserved"
 s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); //configure GPG13 to "reserved"
 s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); //configure GPG14 to "reserved"
 s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); //configure GPG15 to "reserved"
}


/*read already*************************************************************************/


/* ADCDAT0 is used for X position and ADCDAT1 is used for Y position */
static void touch_timer_fire(unsigned long data) 

   unsigned long data0; 
   unsigned long data1; 
 int updown; 
 
   data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0
   data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1
 
  /*get the state of stylus to "updown", "1" represents "down", "0" represents "up"*/
  updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
 
  if (updown) { 
   if (count != 0) { // "count" is the times of conversion
   long tmp; 
                                                                                              
   tmp = xp; 
   xp = yp; 
   yp = tmp; 
                                                                                              
                        xp >>= 2; //get the average value of 4 times of conversion
                        yp >>= 2; //get the average value of 4 times of conversion
 
    input_report_abs(dev, ABS_X, xp); //report absolute x axis value
    input_report_abs(dev, ABS_Y, yp); //report absolute y axis value
 
    input_report_key(dev, BTN_TOUCH, 1); //report key event
    input_report_abs(dev, ABS_PRESSURE, 1); //report touch screen state
    input_sync(dev); //synchronization ( means we have reported a whole touch screen event )
   } 
 
   xp = 0; 
   yp = 0; 
   count = 0; 
 
   iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER
   iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, 
base_addr+S3C2410_ADCCON); //start AD conversion
  } else { 
   count = 0; 


   input_report_key(dev, BTN_TOUCH, 0); 
   input_report_abs(dev, ABS_PRESSURE, 0); 
   input_sync(dev); 
 
   iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER to Detect Stylus Down Interrupt Signal
  if (OwnADC) { 
   OwnADC = 0; 
   up(&ADC_LOCK); // "up" releases semaphore
  } 
  } 



/*read already************************************************************************/
 
static struct timer_list touch_timer = 
  TIMER_INITIALIZER(touch_timer_fire, 0, 0); //create kernel timer


/*read already************************************************************************/


static irqreturn_t stylus_updown(int irq, void *dev_id) 

 unsigned long data0; 
 unsigned long data1; 
 int updown; 


 /* "down_trylock" tries to get semaphore, if succeeds, return 0 */
 if (down_trylock(&ADC_LOCK) == 0) { 
  OwnADC = 1; 
  data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0
  data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1


 /*get the state of stylus to "updown", "1" represents "down", "0" represents "up"*/
  updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & 
S3C2410_ADCDAT0_UPDOWN)); 
 
  if (updown) { // stylus is down
   touch_timer_fire(0); 
  } else { 
   OwnADC = 0; 
   up(&ADC_LOCK); // "up" releases semaphore
  } 
 } 
 
 return IRQ_HANDLED; 

 
/*read already*************************************************************************/ 
static irqreturn_t stylus_action(int irq, void *dev_id) 

 unsigned long data0; 
 unsigned long data1; 
 
 if (OwnADC) { 
  data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0
  data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1
 
  xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; //calculate sum of 4 times of conversion
  yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; //calculate sum of 4 times of conversion
  count++; 
 
     if (count < (1<<2)) { 
   iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, 
base_addr+S3C2410_ADCTSC); //set ADCTSC
   iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, 
base_addr+S3C2410_ADCCON); //start AD conversion 
  } else { 
   mod_timer(&touch_timer, jiffies+1); //activate kernel timer
   iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER to Detect Stylus Up Interrupt Signal
  } 
 } 
 
 return IRQ_HANDLED; 



/*read already************************************************************************/
 
static struct clk *adc_clock; 


/*read already************************************************************************/


static int __init s3c2410ts_init(void) 

 struct input_dev *input_dev; 
 
 adc_clock = clk_get(NULL, "adc"); //get adc clock
 if (!adc_clock) { 
  printk(KERN_ERR "failed to get adc clock source\n"); 


  return -ENOENT; 
 } 
 clk_enable(adc_clock); //run adc clock
 
 base_addr=ioremap(S3C2410_PA_ADC,0x20); //map IO address space to kernel virtual address space
 if (base_addr == NULL) { 
  printk(KERN_ERR "Failed to remap register block\n"); 
  return -ENOMEM; 
 } 
 
 s3c2410_ts_connect(); // Configure GPIOs 
 
 iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),\ 
       base_addr+S3C2410_ADCCON); //initialize ADCCON register
 iowrite32(0xffff,  base_addr+S3C2410_ADCDLY); //initialize ADCDLY register
 iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //initialize ADCTSC register
 
 /* Initialize struct input_dev */ 
 input_dev = input_allocate_device(); //allocate a input device
 
 if (!input_dev) { 
  printk(KERN_ERR "Unable to allocate the input device !!\n"); 
  return -ENOMEM; 
 } 
 
 dev = input_dev; 
 dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS); //supported event type
 dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH); //supported key type
 input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0); //set parameters of axis X of touch screen
 input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0); //set parameters of axis Y of touch screen
 input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);//set parameters of pressure of touch screen 
 
 dev->name = s3c2410ts_name; 
 dev->id.bustype = BUS_RS232; 
 dev->id.vendor = 0xDEAD; 
 dev->id.product = 0xBEEF; 
 dev->id.version = S3C2410TSVERSION; 


/* request interrupt */ 
 if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, 
  "s3c2410_action", dev)) { 
  printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n"); 
  iounmap(base_addr); 
  return -EIO; 
 } 
 if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, 
   "s3c2410_action", dev)) { 
  printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n"); 
  iounmap(base_addr); 
  return -EIO; 
 } 
 
 printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name); 
 
 /* All went ok, so register to the input system */ 
 input_register_device(dev); 
 
 return 0; 



/*read already************************************************************************/


static void __exit s3c2410ts_exit(void) 

 disable_irq(IRQ_ADC); 
 disable_irq(IRQ_TC); 
 free_irq(IRQ_TC,dev); //release memory allocated to irq
 free_irq(IRQ_ADC,dev); //release memory allocated to irq
 
 if (adc_clock) { 
  clk_disable(adc_clock); 
  clk_put(adc_clock); //??
  adc_clock = NULL; 
 } 
 
 input_unregister_device(dev); 
 iounmap(base_addr); 



/*read already************************************************************************/
 
module_init(s3c2410ts_init); //module entry
module_exit(s3c2410ts_exit); //module exit
0 0
原创粉丝点击