触摸屏驱动-学习笔记(韦东山)

890 阅读5分钟

声明:本文是看完韦东山老师的触摸屏驱动视频如果有相关内容与其他网友相同,敬请原谅,如果这篇文章对你有帮助,那我将十分荣幸。

触摸屏驱动开发

首先我们要知道,触摸屏驱动属于输入子系统中的一类,这里我们按照输入子系统的的方法写驱动程序,步骤为:

  1. 分配一个input_event结构体
  2. 设置该结构体用于触摸屏驱动
  3. 注册这个结构体
  4. 做硬件相关操作

 /* 1. 分配一个input_dev结构体 */
 s3c_ts_dev = input_allocate_device();

 /* 2. 设置 */
 /* 2.1 能产生哪类事件 */
 set_bit(EV_KEY, s3c_ts_dev->evbit);//按键类事件
 set_bit(EV_ABS, s3c_ts_dev->evbit);//绝对位移事件

 /* 2.2 能产生这类事件里的哪些事件 */
 set_bit(BTN_TOUCH, s3c_ts_dev->keybit);//按键类事件中的触摸屏按键,此处将触摸屏抽象为按键,笔按下时为键被按下,笔抬起时为键被抬起 

 input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0);
 input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0);//绝对位移中的y方向,此处的0x3ff是因为触摸屏为10位ADC
 input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);

接下来我们处理对于触摸屏的硬件操作:

  1. 按下触摸屏,产生中断INT_TC
  2. 在中断处理程序中,启动ADC转换X,Y坐标
  3. ADC结束,产生ADC中断
  4. 在ADC中断处理函数中,上报,启动定时器
  5. 定时时间到,继续执行第2步往下执行(处理长按,滑动)
  6. 松开

代码为:

struct s3c_ts_reg {
 unsigned long ADCCON;
 unsigned long ADCTSC;
 unsigned long ADCDLY;
 unsigned long ADCDAT0;
 unsigned long ADCDAT1;
 unsigned long ADCUPDN;
};

struct clk* clk;
/* 4. 硬件相关的操作 */
/* 4.1 使能时钟(CLKCON[15]) */
clk = clk_get(NULL, "adc");
clk_enable(clk);
/* 4.2 设置S3C2440的ADC/TS寄存器 */
 s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));

 /* bit[14]  : 1-A/D converter prescaler enable,预分频使能,为1时使能
  * bit[13:6]: A/D converter prescaler value,预分频系数
  *            49, ADCCLK=PCLK/(49+1)=50MHz/(49+1)=1MHz
  * bit[0]: A/D conversion starts by enable. 先设为0,ADC开始转换使能
  */
 s3c_ts_regs->adccon = (1<<14)|(49<<6);

  /*按下触摸屏产生中断*/
 request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
  /*ADC转换结束,产生中断*/
 request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);

 /* 优化措施1: 
  * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断
  */
 s3c_ts_regs->adcdly = 0xffff;

流程,当我们按下触摸屏时,会触发IRQ_TC中断

request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);

所以当我们按下触摸屏时,进入相应的中断函数:pen_down_up_irq,代码为:

static irqreturn_t pen_down_up_irq(int irq,void *dev_id)
{
 if(s3c_ts_regs->ADCDAT0 & (1<<15)){      //判断屏幕是否还是处于抬起状态
  //printk("pen up\n");     //处于抬起状态,上报抬起事件
  input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);
  input_report_key(s3c_ts_dev,BTN_TOUCH,0);
  input_sync(s3c_ts_dev);
  enter_wait_pen_down_mode();  //进入等待按下模式
 }else{
  //此时屏幕还处于按下状态
  /* 4.4 在中断中启动ADC,转化XY坐标 */
 // printk("pen down\n");
 // enter_wait_pen_up_mode();
  enter_measure_xy_mode();
    /*触发ADC中断*/
  start_adc();
 }
 
 return IRQ_HANDLED;
}

如上面程序所示,此时如果你抬起了你的笔,程序将上报事件,而如果你还处于按下状态,我们将继续跟着程序走: 由于你已经注册了ADC中断:

request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM,"ts_adc",NULL); 

所以,执行start_adc函数之后,会触发ADC中断

static irqreturn_t adc_irq(int irq,void *dev_id)
{
  static int cnt = 0;
 static int x[4], y[4];
 int adcdat0, adcdat1;
 /* 优化措施2: 如果ADC完成时, 发现触摸笔已经松开, 则丢弃此次结果 */
 adcdat0 = s3c_ts_regs->adcdat0;
 adcdat1 = s3c_ts_regs->adcdat1;

 if (s3c_ts_regs->adcdat0 & (1<<15))
 {
  /* 已经松开 */
  cnt = 0;
  input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);
  input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
  input_sync(s3c_ts_dev);
  enter_wait_pen_down_mode();
 }
 else
 {
  // printk("adc_irq cnt = %d, x = %d, y = %d\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);
  /* 优化措施3: 多次测量求平均值 */
  x[cnt] = adcdat0 & 0x3ff;
  y[cnt] = adcdat1 & 0x3ff;
  ++cnt;
  if (cnt == 4)
  {
   /* 优化措施4: 软件过滤 */
   if (s3c_filter_ts(x, y))
   {   
    //printk("x = %d, y = %d\n", (x[0]+x[1]+x[2]+x[3])/4, (y[0]+y[1]+y[2]+y[3])/4);
    input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
    input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
    input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1);
    input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
    input_sync(s3c_ts_dev);
   }
   cnt = 0;
   enter_wait_pen_up_mode();

   /* 启动定时器处理长按/滑动的情况 */
   mod_timer(&ts_timer, jiffies + HZ/100);
  }
  else
  {
   enter_measure_xy_mode();
   start_adc();
  }  
 }
 
 return IRQ_HANDLED;

而通过上面的程序,我们知道了触摸屏的处理过程,此时,触摸屏驱动已经基本写好了。而下面就是对特殊的处理,如当长按或者滑动时,此时我们将通过定时器来处理这样的问题,加入此时你还没有松手,触摸屏还处于按下状态。(你可能觉得怎么这么长时间了笔还没抬起啊,其实上面分析的可能麻烦,但在程序中上面的代码还是跑的很快的)。那么,我们接下来就是对定时器的分析了,因为在上文中我们启动了定时器:mod_timer(&ts_timer,jiffies + HZ/100); 当定时时间(此处我们设置定时时间为10ms)到时,进入定时器处理函数

void s3c_ts_timer_func(unsigned long data)
{
 /* 4.7 定时器时间到,在定时器处理函数中,重复4.4的步骤(用于处理长按和滑动) */
 
 if(s3c_ts_regs->ADCDAT0 & (1<<15)){   
  //printk("pen up\n");
  input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);
  input_report_key(s3c_ts_dev,BTN_TOUCH,0);
  input_sync(s3c_ts_dev);
  enter_wait_pen_down_mode();
 }else{
  
  /* 4.4 在中断中启动ADC,转化XY坐标 */
 // printk("pen down\n");
 // enter_wait_pen_up_mode();
  enter_measure_xy_mode();
  start_adc();
 }
}

在上面的程序中,可以看出定时时间到后定时器又会启动ADC然后将接着步骤2继续执行,一直这样循环,直到松开,而这样也就可以处理长按或者滑动了,当你长按或者滑动时,定时时间到后会采集你所在的位置的XY坐标,然后上报,然后继续进入定时器,继续进入ADC,继续采集上报,直到你松开触摸屏。

本文使用 mdnice 排版