• 10.触摸屏驱动


    1.小结

    使用输入子系统来做

    1. 申请input_dev结构体
    2. 设置input_dev的成员
    3. 注册input_dev驱动设备
    4. 硬件操作
       触摸屏硬件操作思路(电阻屏)
       4.1、按下产生中断
       4.2、在按下中断处理函数中,启用ADC转换X、Y坐标值
       4.3、启动ADC不会瞬间完成,一般来说启动ADC后就不管了。ADC结束,产生ADC中断
       4.4、在ADC中断处理函数中用input_event来上报事件。上报后启动定时器(实现滑动)
       4.5、定时器时间到,在到4.2步,在此启用ADC。过一段时间后,就会就会再次启动一次ADC中断,这样就能实现滑动
       4.6、松开

    2.关键函数

    • input_set_abs_params

      void input_set_abs_params(struct input_dev *dev, unsigned int axis, int min, int max, int fuzz, int flat)
      
    • clk_get

      struct clk *clk_get(struct device *dev, const char *con_id)
      
    • input_report_abs(...)

      static inline void input_report_abs(struct input_dev *dev, unsigned int code, int value)
      {
          input_event(dev, EV_ABS, code, value);
      }
      code: 值被存储到这里
      value:值
      

    3.代码解析

    struct s3c_ts_regs {
    	unsigned long adccon;
    	unsigned long adctsc;
    	unsigned long adcdly;
    	unsigned long adcdat0;
    	unsigned long adcdat1;
    	unsigned long adcupdn;
    };
    
    static struct input_dev *s3c_ts_dev;
    static volatile struct s3c_ts_regs *s3c_ts_regs;
    
    static struct timer_list ts_timer;
    
    static void enter_wait_pen_down_mode(void)
    {
    	s3c_ts_regs->adctsc = 0xd3;
    }
    
    static void enter_wait_pen_up_mode(void)
    {
    	s3c_ts_regs->adctsc = 0x1d3;
    }
    
    static void enter_measure_xy_mode(void)
    {
    	s3c_ts_regs->adctsc = (1<<3)|(1<<2);
    }
    
    static void start_adc(void)
    {
    	s3c_ts_regs->adccon |= (1<<0);
    }
    
    static int s3c_filter_ts(int x[], int y[])
    {
      #define ERR_LIMIT 10
    
    	int avr_x, avr_y;
    	int det_x, det_y;
    
    	avr_x = (x[0] + x[1])/2;
    	avr_y = (y[0] + y[1])/2;
    
    	det_x = (x[2] > avr_x) ? (x[2] - avr_x) : (avr_x - x[2]);
    	det_y = (y[2] > avr_y) ? (y[2] - avr_y) : (avr_y - y[2]);
    
    	if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
    		return 0;
    
    	avr_x = (x[1] + x[2])/2;
    	avr_y = (y[1] + y[2])/2;
    
    	det_x = (x[3] > avr_x) ? (x[3] - avr_x) : (avr_x - x[3]);
    	det_y = (y[3] > avr_y) ? (y[3] - avr_y) : (avr_y - y[3]);
    
    	if ((det_x > ERR_LIMIT) || (det_y > ERR_LIMIT))
    		return 0;
    	
    	return 1;
    }
    
    static void s3c_ts_timer_function(unsigned long data)
    {
    	if (s3c_ts_regs->adcdat0 & (1<<15))
    	{
    		/* 已经松开 */
    		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
    	{
    		/* 测量X/Y坐标 */
    		enter_measure_xy_mode();
    		start_adc();
    	}
    }
    
    
    static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
    {
    	if (s3c_ts_regs->adcdat0 & (1<<15))
    	{
    		//printk("pen up
    ");
    		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("pen down
    ");
    		//enter_wait_pen_up_mode();
    		enter_measure_xy_mode();
    		start_adc();
    	}
    	return IRQ_HANDLED;
    }
    
    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
    ", ++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
    ", (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);            //10ms
    		}
    		else
    		{
    			enter_measure_xy_mode();
    			start_adc();
    		}		
    	}
    	
    	return IRQ_HANDLED;
    }
    
    static int s3c_ts_init(void)
    {
    	struct clk* clk;
    	
    	/* 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);
    	input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
    
    
    	/* 3. 注册 */
    	input_register_device(s3c_ts_dev);
    
    	/* 4. 硬件相关的操作 */
    	/* 4.1 使能时钟(CLKCON[15]) */
    	clk = clk_get(NULL, "adc");            //得到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
    	 * 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
    	 */
    	s3c_ts_regs->adccon = (1<<14)|(49<<6);
    
    	request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
    	request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);
    
    	/* 优化措施1: 
    	 * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断
    	 */
    	s3c_ts_regs->adcdly = 0xffff;
    
    	/* 优化措施5: 使用定时器处理长按,滑动的情况
    	 * 
    	 */
    	init_timer(&ts_timer);
    	ts_timer.function = s3c_ts_timer_function;
    	add_timer(&ts_timer);
    
    	enter_wait_pen_down_mode();
    	
    	return 0;
    }
    
    static void s3c_ts_exit(void)
    {
    	free_irq(IRQ_TC, NULL);
    	free_irq(IRQ_ADC, NULL);
    	iounmap(s3c_ts_regs);
    	input_unregister_device(s3c_ts_dev);
    	input_free_device(s3c_ts_dev);
    	del_timer(&ts_timer);
    }
    
    module_init(s3c_ts_init);
    module_exit(s3c_ts_exit);
    
    
    MODULE_LICENSE("GPL");
  • 相关阅读:
    数论基础(维诺格拉多夫著,裘光明译) 勘误
    微观经济学现代观点(Hal R. Varian) 复习题 1.1
    微分学里的中值定理
    数论基础(维诺格拉多夫著,裘光明译) 勘误
    分数的一种分拆方法
    C++正则表达式的初步使用
    如何用消息系统避免分布式事务
    阿里云表格存储全面升级,打造一站式物联网存储新方案
    探究 Java 应用的启动速度优化
    技术干货|基于Apache Hudi 的CDC数据入湖「内附干货PPT下载渠道」
  • 原文地址:https://www.cnblogs.com/huangdengtao/p/13463523.html
Copyright © 2020-2023  润新知