触摸屏驱动开发_三星4412开发板触摸屏驱动 - CSDN
精华内容
参与话题
  • 触摸屏驱动编写

    2020-03-31 23:18:46
    触摸屏要使用到输入子系统 分析之前的输入子系统 输入子系统详解 用linux里面的 input.c 用两边的probe 匹配系统里的软件控制和驱动 写出框架 我们要写的就是 input_dev 开始代码编写和分析 分配一个input_dev...
    触摸屏要使用到输入子系统

    分析之前的输入子系统
    输入子系统详解
    用linux里面的 input.c 用两边的probe 匹配系统里的软件控制和驱动
    写出框架
    我们要写的就是 input_dev
    在这里插入图片描述

    开始代码编写和分析

    分配一个input_dev结构体
    设置
    注册
    硬件相关的操作

    由于触发按键 中断时候 要用到 input_event 来上报事件

    在这里插入图片描述

    1分配一个input_dev结构体

    在这里插入图片描述

    2设置

    2.1 能产生哪类事件

    产生按键类事件 产生触摸屏绝对位移事件
    在这里插入图片描述

    能产生这类事件里面的 什么事件

    在这里插入图片描述
    在这里插入图片描述
    ** 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);**
    里面的参数 0x3FF 从cup芯片手册上面找到
    在这里插入图片描述
    在这里插入图片描述
    产生的大小是10位 也就是最大 0x3FF
    input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
    压力值设置 该触摸屏 不是放下就是松开 所以只有0 和1

    3注册

    在这里插入图片描述

    4硬件相关的操作

    先来看看触摸屏使用的过程
    在这里插入图片描述

    打开cpu的原理图
    在这里插入图片描述
    在这里插入图片描述
    看了看 芯片手册
    在这里插入图片描述
    这几个引脚 就是用来做ADC的 不用专门的设置

    这个ADC转换是 8:1 转换 左边八个物理信号选择一个转换为数字信号
    在这里插入图片描述

    4.1 设置soc的时钟(CLKCON)

    在 SOC 里面 有各种各样的模块 为了省电 一般是关的 这时候 要爆我们的ADC打开
    在这里插入图片描述
    在这里插入图片描述

    4.2初始化ADC/TS寄存器

    在芯片手册里面找到寄存器
    在这里插入图片描述
    给这些寄存器定义一个结构体
    在这里插入图片描述
    设置指针指向这个结构体
    在这里插入图片描述
    ioremap 这些地址
    在这里插入图片描述
    进入内核 看工作时钟的频率
    FCLK : cpu工作时钟 HCLK : 内存工作时钟 PCKL : 外设工作时钟
    在这里插入图片描述

    a.设置ADCCON寄存器

    A/D 转换器预分频器使能设置为1
    在这里插入图片描述
    A/D 转换器预分频值设置
    芯片手册里面有个例子 之前找到了 PCLK是50MHz 所以 prescaler value =49
    在这里插入图片描述
    A/D conversion starts by enable. 先设为0 用到时候再打开

    b注册中断函数

    当按下 和松开时 会产生中断 注册中断函数 pen_down_up_irq
    在这里插入图片描述
    这时候 写出这个中断函数 由于会有 按下和松开 所以要分开判断
    在芯片手册中 读取ADCDATA0 的15位 进行判断
    在这里插入图片描述
    当松开的时候 进入等待按下模式 等待下一次被按下
    在这里插入图片描述
    在这里插入图片描述
    当按下的时候 进入测量 xy 坐标模式

    在这里插入图片描述
    这时候 写出测量xy的函数
    芯片手册中有一个自带的x,y 坐标测量模式
    在这里插入图片描述
    根据提示设置enter_measure_xy_mode()函数

    在这里插入图片描述
    在这里插入图片描述
    于此同时当被按下的时候根据原理图 上拉电阻都是断开的
    在这里插入图片描述
    在这里插入图片描述
    写出函数
    在这里插入图片描述

    进入这个模式之后 ,使用ADC转换,启动adc

    在这里插入图片描述ADC不会瞬间完成,当ADC好了之后,会产生一个ADC中断,注册一个ADC中断函数
    在这里插入图片描述
    同时写出这个ADC中断函数 ,函数里面可以找到,当前的XY的电压值
    x的电压值 在ADCDATA0里面
    在这里插入图片描述
    y的电压值 在ADCDATA1里面
    在这里插入图片描述
    先在中断函数里面 打印出ADC的电压值
    在这里插入图片描述

    在这里插入图片描述
    到这里就能显示数值了 但是不够准确 , 不能检测到滑动,现在开始优化措施

    优化措施

    让数值精确,等电压稳定才会继续

    用延时值,ADCDLY,当稳定的时候才发出中断
    在这里插入图片描述

    丢弃没有检测好的数值,多次测量取值

    ADC启动需要一定的时间,有时候触摸已经松开,才完成启动,就把这个测量失误的值丢弃,并且多次测量
    建立一个数组,x,y都保存4次,再取平均值
    没到4次的话再次启动ADC测量
    在这里插入图片描述
    在这里插入图片描述

    设置软件过滤

    当四个值中差距太大了,出现了测量错误,应该丢弃
    在这里插入图片描述

    启动定时器,记录滑动

    创建一个定时器,设置定时器功能,增加这个定时器
    在这里插入图片描述
    写下定时器的功能
    在这里插入图片描述
    在测值时候加入定时器
    在这里插入图片描述

    完整代码

    #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 <asm/io.h>
    #include <asm/irq.h>
    
    #include <asm/plat-s3c24xx/ts.h>
    
    #include <asm/arch/regs-adc.h>
    #include <asm/arch/regs-gpio.h>
    
    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))
    	{
    		/* 已经松开 */
    		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\n");
    		enter_wait_pen_down_mode();
    	}
    	else
    	{
    		//printk("pen down\n");
    		//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;
    		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);
    			}
    			cnt = 0;
    			enter_wait_pen_up_mode();
    
    			/* 启动定时器处理长按/滑动的情况 */
    			mod_timer(&ts_timer, jiffies + HZ/100);
    		}
    		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");
    	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");
    
    
    
    

    makefile

    KERN_DIR = /work/system/linux-2.6.22.6
    
    all:
    	make -C $(KERN_DIR) M=`pwd` modules 
    
    clean:
    	make -C $(KERN_DIR) M=`pwd` modules clean
    	rm -rf modules.order
    
    obj-m	+= s3c_ts.o
    
    

    烧录,配置内核,编译,测试

    重新配置内核

    进入之前的内核,去掉原来的触摸屏程序
    make menuconfig
    -> device drivers
    -> input device support
    -> generic input layer
    ->touchscreens
    去掉
    在这里插入图片描述
    make uImage 重新烧录内核
    insmod s3c_ts.ko

    这时候点击触摸屏就能 有显示

    用tslib 来测试驱动

    使用之前 要让驱动上报事件,这样app 才能收到数据 input_event
    在这里使用input_report_abs()
    把之前的打印全部改为上报

    松开的上报事件
    在这里插入图片描述
    按下的上报事件
    在这里插入图片描述

    把之前的LCD驱动4个ko文件 和下面代码的ko文件 一起放进开发版里

    在虚拟机上安装tslib

    编译:
    tar xzf tslib-1.4.tar.gz
    cd tslib
    ./autogen.sh
    mkdir tmp 这个临时目录以后放他的编译结果。
    echo "ac_cv_func_malloc_0_nonnull=yes" >arm-linux.cache
    ./configure --host=arm-linux --cache-file=arm-linux.cache --prefix=$(pwd)/tmp
    make
    make install 这个安装就是把编译的结果安装到“临时目录”tmp 中。
    

    cd tmp
    cp * -rf /nfsroot (这只是一个示意)就是说要拷贝到开发板的“根”目录下。就是把
    相关文件归到开发板的根文件系统的“bin”,“etc”,"lib"和“include”目录下。
    注意-rfd:r 递归,f 强制,d,保持链接。
    在这里插入图片描述

    先安装 s3c_ts.ko, lcd.ko
    在这里插入图片描述

    修改 /etc/ts.conf 第 1 行(去掉#号和第一个空格):

    module_raw input

    改为:
    module_raw input
    在这里插入图片描述
    可以通过环境变量来设置:先设置环境变量后,才能使用触摸屏程序。
    export TSLIB_TSDEVICE=/dev/event0 //这是指触摸屏设备。
    export TSLIB_CALIBFILE=/etc/pointercal //校验文件放在这里。
    export TSLIB_CONFFILE=/etc/ts.conf //配置文件放在这里。
    export TSLIB_PLUGINDIR=/lib/ts //插件放在这里
    export TSLIB_CONSOLEDEVICE=none
    export TSLIB_FBDEVICE=/dev/fb0 //显示屏
    ts_calibrate //是指校验,在 SHELL 中输入这个程序后:
    这时屏幕上会出现一些文字,左上角会有个“十字架”。
    开始进行测试
    在这里插入图片描述
    在shell 上面输入 ts_test 即可以使用这个程序的其他功能
    在这里插入图片描述

    展开全文
  • linux 触摸屏驱动编写

    2019-06-25 10:51:57
    所以,今天可以看看触摸屏驱动在linux上是如何进行的。 1、驱动目录 drivers/input 2、看看这个目录的Makefile如何设计 obj-(CONFIGINPUT)+=input−core.oinput−core−y:=input.oinput−compat.oin...

    早在诺基亚手机还比较流行的时候,那时候触摸屏用的还不多。但是随着触摸屏手机、即智能手机的流行,触摸屏基本成了手机的标配。所以,今天可以看看触摸屏驱动在linux上是如何进行的。

    1、驱动目录

    drivers/input
    2、看看这个目录的Makefile如何设计

    obj-(CONFIGINPUT)+=inputcore.oinputcorey:=input.oinputcompat.oinputmt.offcore.oobj(CONFIG_INPUT) += input-core.o input-core-y := input.o input-compat.o input-mt.o ff-core.o obj-(CONFIG_INPUT_TOUCHSCREEN) += touchscreen/
    3、除了input-core.o这个文件外,只需要看touchscreen目录就可以了

    config TOUCHSCREEN_S3C2410 tristate “Samsung S3C2410/generic touchscreen input driver” depends on ARCH_S3C24XX || SAMSUNG_DEV_TS depends on S3C_ADC help Say Y here if you have the s3c2410 touchscreen. If unsure, say N. To compile this driver as a module, choose M here: the module will be called s3c2410_ts.
    4、看懂了Kconfig之后,再阅读Makefile,注意S3C_ADC宏可以参考arch/arm/plat-samsung/adc.c

    obj-$(CONFIG_TOUCHSCREEN_S3C2410) += s3c2410_ts.o
    5、继续阅读s3c2410_ts.c文件

    static const struct platform_device_id s3cts_driver_ids[] = { { “s3c2410-ts”, 0 }, { “s3c2440-ts”, 0 }, { “s3c64xx-ts”, FEAT_PEN_IRQ }, { } }; MODULE_DEVICE_TABLE(platform, s3cts_driver_ids); static struct platform_driver s3c_ts_driver = { .driver = { .name = “samsung-ts”, #ifdef CONFIG_PM .pm = &s3c_ts_pmops, #endif }, .id_table = s3cts_driver_ids, .probe = s3c2410ts_probe, .remove = s3c2410ts_remove, }; module_platform_driver(s3c_ts_driver);周易起名
    6、根据probe函数,看看有什么需要注意的内容

    ts.client = s3c_adc_register(pdev, s3c24xx_ts_select, s3c24xx_ts_conversion, 1); if (IS_ERR(ts.client)) { dev_err(dev, “failed to register adc client\n”); ret = PTR_ERR(ts.client); goto err_iomap; }
    7、接着,查看是否有中断函数被注册

    ret = request_irq(ts.irq_tc, stylus_irq, 0, “s3c2410_ts_pen”, ts.input);
    8、最后

    很明显,触摸屏驱动本质上还是由TOUCHSCREEN_S3C2410和S3C_ADC两个macro一起完成的。

    展开全文
  • 嵌入式Linux ———触摸屏驱动开发

    千次阅读 2018-11-07 19:43:26
    声明:本文章是看完韦东山老师的触摸屏驱动视频所写的关于触摸屏的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇...

    声明:本文章是看完韦东山老师的触摸屏驱动视频所写的关于触摸屏的驱动,因此如果有相关内容与其他网友相同,敬请原谅。同时我还是想说本文只是总结自己的学习所得,同时也将自己所学到的知识写下来,所以如果这篇文章对你有帮助,那是我的荣幸。

    本文主要介绍韦东山老师视频中所讲的触摸屏驱动,同时还会将s3c2410_ts.c这个驱动程序讲一下。虽然老师讲的驱动是对这个驱动程序的精简和整理,但内核的程序我们还是要自己看懂比较好,这样以后自己写其他内核驱动程序时会很有帮助。

    在这里我介绍一篇不错的讲解s3c2410_ts.c的文章:linux嵌入式系统开发之触摸屏---驱动篇(下/源码分析)

    而我在自己的上一篇文章中已经翻译了s3c2440数据手册中的ADC与触摸屏这章:S3C2440-ADC触摸屏,所以有硬件方面不清楚的可以看这一章了解触摸屏寄存器的工作模式以及如何设置。

    下面我们言归正传开始讲触摸屏的驱动程序:

    触摸屏驱动程序是输入子系统中的一类,所以我们按输入子系统的方法写驱动程序,所以步骤如下:

     

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

    而上面步骤1,2,3所对应的程序为:

    /* 1. 分配一个input_dev结构体 */
    	s3c_ts_dev = input_allocate_device();
    	/* 1.1 初始化定时器 */
    	init_timer(&ts_timer);
    	ts_timer.function = s3c_ts_timer_func; //这个为定时器的处理函数
    	add_timer(&ts_timer);
    	
    	/* 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);  //绝对位移中的x方向,此处的0x3ff是因为触摸屏为10位ADC
    	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);//绝对位移中的压力方向,此处只有抬起和按下两种情况
    
    	/* 3.  注册这个结构体 */
    	input_register_device(s3c_ts_dev);
    
    

     

    现在已将input_dev结构体设置好了,接下来就是对触摸屏硬件的设置了,而在设置硬件之前要先向大家介绍一下触摸屏的使用过程:
    按下触摸屏产生中断
    在触摸屏中断处理程序中,将通过ADC采集的电压值转化为XY方向的坐标值,然后启动ADC。
    AD转化结束产生ADC中断。
    在ADC中断处理函数中上报事件,并启动定时器(用于处理长按和滑动事件)
    定时器时间到,跳到步骤2继续往下执行
    直到松开触摸屏
    下面是硬件寄存器和中断的设置,为上面触摸屏的操作做准备,程序为:

     

    /* 4.  硬件相关 */
    	/* 4.1 使能时钟(CLKCON[15])使能ADC模块 */
    	clk = clk_get(NULL,"adc");
    	clk_enable(clk);
    	
    	/* 4.2 设置S3c的ADC寄存器 */
    	s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_reg));
    
    	/*
    	*ADCCON:
    	*bit[14]    PRSCEN :A/D converter 预分频使能
    	*				1 = Enable
    	*bit[13:6]  PRSCVL :预分频系数
    	*				49:	PLCK/(PRSCVL+1) = 50MHZ/(49+1) = 1MHZ
    	*bit[0]     ENABLE_START :ADC开始转化使能
    	*				此处先设为0
    	*/
    	s3c_ts_regs->ADCCON = (1<<14) | (49<<6) | (0<<0);
    
    	/* ADCDLY
    	*bit[15:0]   DELAY :ADC转化开始延迟值
    	*					=0xffff;
    	*/
    	s3c_ts_regs->ADCDLY = 0xffff;
    	
    	/* 4.3 注册中断,按下触摸屏产生中断 */
    	request_irq(IRQ_TC,pen_down_up_irq,IRQF_SAMPLE_RANDOM,"ts_pen",NULL);
    
    	/* 4.5 ADC转化结束,产生ADC中断 */
    	request_irq(IRQ_ADC,adc_irq,IRQF_SAMPLE_RANDOM,"ts_adc",NULL);
    	
    	enter_wait_pen_down_mode();
    

     

    上面程序中的s3c_ts_regs是触摸屏寄存器的一个总的集合,并将其放入一个结构体中,代码为:

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


    上面是准备代码,下面我们将模仿触摸屏按下的过程来分析代码:
    假设我们程序已经写好,并且在开发版上运行正常,当你拿起笔按在触摸屏上的一点,因为在之前我们已经注册了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();
    		start_adc();
    	}
    
    	return IRQ_HANDLED;
    }
    

     

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

     

     

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

    而上面的程序已经开启了ADC:

     

    start_adc()

     

    ,所以我们将进入ADC的中断处理函数adc_irq,详细的代码:

     

     

    static irqreturn_t adc_irq(int irq,void *dev_id)
    {
    	static int cnt;
    	static int x[4],y[4];
    	
    	x[cnt] = ((s3c_ts_regs->ADCDAT0)&0x3ff);
    	y[cnt] = ((s3c_ts_regs->ADCDAT1)&0x3ff);
    	cnt++;
    	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{
    
    		if(cnt == 4){
    			//printk("adc_irq 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;
    			/* 4.6 在ADC中断处理函数中上报(input_event),启动定时器 */
    			mod_timer(&ts_timer,jiffies + HZ/100);
    			
    			enter_wait_pen_up_mode();
    		}else{
    			enter_measure_xy_mode();
    			start_adc();
    		}
    	}
    mod_timer(&ts_timer,jiffies + HZ/100);
    
    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,继续采集上报,直到你松开触摸屏。

    好了,上面就是触摸屏驱动的大致流程和代码分析了。

    下面我们将要按着前面所讲的方法和步骤去分析s3c2410_ts.c这个驱动文件。
    首先分析一个驱动文件,第一个要看的就是他的入口函数:

     

     

    platform_driver_register(&s3c2410ts_driver);
    


    这个入口函数中注册了一个platform_driver的结构体,而这个结构体的probe函数就是当配对成功时,首先进入的函数:
    下面我们进probe中分析:

     

     

     

     

     

    static int __init s3c2410ts_probe(struct platform_device *pdev)
    


    在s3c2410_ts.c中首先对硬件的操作:

     

     

     

     

     

    	adc_clock = clk_get(NULL, "adc");
    	if (!adc_clock) {
    		printk(KERN_ERR "failed to get adc clock source\n");
    		return -ENOENT;
    	}
    	clk_enable(adc_clock);


    这个是对CLKCON的寄存器中ADC模块的使能。

     

     

     

     

    base_addr=ioremap(S3C2410_PA_ADC,0x20);  //对s3c2410z中寄存器的remap
    	if (base_addr == NULL) {
    		printk(KERN_ERR "Failed to remap register block\n");
    		return -ENOMEM;
    	}
    
    	/* 对触摸屏寄存器的操作 */
    	s3c2410_ts_connect();
    
    	if ((info->presc&0xff) > 0)
    		iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
    			     base_addr+S3C2410_ADCCON);
    	else
    		iowrite32(0,base_addr+S3C2410_ADCCON);
    
    
    	/* 初始化寄存器 */
    	if ((info->delay&0xffff) > 0)
    		iowrite32(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);
    
    	iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);


    通过对上面的代码的分析我们可以看出,上面主要是对硬件相关的操作,如寄存器。
    而下面的代码就是将触摸屏注册到输入子系统中:

     

     

     

     

     

    /* Initialise input stuff */
    	memset(&ts, 0, sizeof(struct s3c2410ts));
    	input_dev = input_allocate_device();      //分配一个input_dev结构体
    
    	if (!input_dev) {
    		printk(KERN_ERR "Unable to allocate the input device !!\n");
    		return -ENOMEM;
    	}
    
    	ts.dev = input_dev;
    	ts.dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);   //设置产生同步事件,按键类事件和绝对位移事件
    	ts.dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);            //设置产生按键中的触摸屏按键
    	input_set_abs_params(ts.dev, ABS_X, 0, 0x3FF, 0, 0);         //绝对位移在X方向的事件
    	input_set_abs_params(ts.dev, ABS_Y, 0, 0x3FF, 0, 0);       //绝对位移在Y方向的事件
    	input_set_abs_params(ts.dev, ABS_PRESSURE, 0, 1, 0, 0);   //绝对位移在压力方向的事件
    
    
    	ts.dev->private = &ts;
    	ts.dev->name = s3c2410ts_name;
    	ts.dev->id.bustype = BUS_RS232;
    	ts.dev->id.vendor = 0xDEAD;
    	ts.dev->id.product = 0xBEEF;
    	ts.dev->id.version = S3C2410TSVERSION;
    
    	ts.shift = info->oversampling_shift;
    

     

     

    而接下来就是对中断的设置了:

     

    	if (request_irq(IRQ_ADC, stylus_action, IRQF_SAMPLE_RANDOM | SA_SHIRQ,
    		"s3c2410_action", ts.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", ts.dev)) {
    		printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
    		iounmap(base_addr);
    		return -EIO;
    	}

     

     

    当上面所有的工作做完后我们就可以注册这个设备了:

     

    /* All went ok, so register to the input system */
    	input_register_device(ts.dev);
    


    而接下来就是和上面一样对触摸屏按下和抬起过程进行分析了:
    我们同样先将整个过程写下来,让大家有个大致过程的认识:




    1)按下触摸屏,触发触摸屏中断然后进入stylus_updown,如果触摸屏状态为按下,则调用touch_timer_fire启动ADC转换;
    2)当ADC启动后,触发ADC中断即进入stylus_action,如果转换的次数小于4,则重新启动ADC进行转换,如果4次完毕后,启动1个时间滴答的定时器,
       停止ADC转换,也就是说在这个时间滴答内,ADC转换是停止的;这里为什么要在1个时间滴答到来之前停止ADC的转换呢?这是为了防止屏幕抖动。
    3)如果1个时间滴答到来则进入定时器服务程序touch_timer_fire,判断触摸屏仍然处于按下状态则上报事件和转换的数据,并重启ADC转换,重复第(2)步;
    4)如果触摸抬起了,则上报释放事件,并将触摸屏重新设置为等待中断状态。
    上面的步骤是从linux嵌入式系统开发之触摸屏---驱动篇(下/源码分析),中抄来的。
    下面就带着这个过程用代码分析:
    首先触摸屏按下,触发触摸屏中断处理函数:

     

     

     

    static irqreturn_t stylus_updown(int irq, void *dev_id)
    {
    	unsigned long data0;
    	unsigned long data1;
    	int updown;
    
    	data0 = ioread32(base_addr+S3C2410_ADCDAT0);  //读出x方向的值
    	data1 = ioread32(base_addr+S3C2410_ADCDAT1);  //读出y方向的值
    	/* 判断触摸屏是否按下 */
    	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
    
    	/* TODO we should never get an interrupt with updown set while
    	 * the timer is running, but maybe we ought to verify that the
    	 * timer isn't running anyways. */
    
    	if (updown)  //如果处于按下状态 
    		touch_timer_fire(0);      //调用touch_timer_fire函数
    
    	return IRQ_HANDLED;
    }
    


    我们假设现在还处于按下状态,所以将继续调用touch_timer_fire函数:

     

     

     

    static void touch_timer_fire(unsigned long data)
    {
      	unsigned long data0;
      	unsigned long data1;
    	int updown;
    
      	data0 = ioread32(base_addr+S3C2410_ADCDAT0); //读出x方向的值
      	data1 = ioread32(base_addr+S3C2410_ADCDAT1); //读出y方向的值
    	/* 判断触摸屏是否按下 */
     	updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));
    
     	if (updown) {   //屏幕被按下
     		if (ts.count != 0) {   //判断是否采集的次数为0
    			long tmp;  /* add by www.100ask.net */
    
    			tmp = ts.xp;
    			ts.xp = ts.yp;
    			ts.yp = tmp;
    			/* 多次采集求平均值,这里整除的数就是要采集的次数 */
     			ts.xp >>= ts.shift;
     			ts.yp >>= ts.shift;
    			/* 将采集的到的平均值上报 */
     			input_report_abs(ts.dev, ABS_X, ts.xp);
     			input_report_abs(ts.dev, ABS_Y, ts.yp);
    
     			input_report_key(ts.dev, BTN_TOUCH, 1);
     			input_report_abs(ts.dev, ABS_PRESSURE, 1);
     			input_sync(ts.dev);
     		}
    		/* 如果还没有开启ADC采集,则设置XP,yp,count为0,并开启ADC */
     		ts.xp = 0;
     		ts.yp = 0;
     		ts.count = 0;
    		/* 开启ADC */
      		iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
      		iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
     	} else { //如果松手,将count设为0,并上报事件。
     		ts.count = 0;
    
     		input_report_key(ts.dev, BTN_TOUCH, 0);
     		input_report_abs(ts.dev, ABS_PRESSURE, 0);
     		input_sync(ts.dev);
    
     		iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);
     	}
    }
    

     

     

    从上面我们知道想要得到XY的坐标值要开启ADC后在ADC中断处理函数中才能采集XY的值,所以下面分析ADC中断函数:

     

     

    static irqreturn_t stylus_action(int irq, void *dev_id)
    {
    	unsigned long data0;
    	unsigned long data1;
    	
    	data0 = ioread32(base_addr+S3C2410_ADCDAT0);
    	data1 = ioread32(base_addr+S3C2410_ADCDAT1);
    	/* 采集XY的坐标值,并多次测量,而在后面求平均值 */
    	ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;  //因为ADC为10位,所以要使用有效位
    	ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;
    	ts.count++;   //此处count就是采集的次数
    			
    	 if (ts.count < (1<<ts.shift)) {  //当没有采集到约定的值时,开启ADC继续采集
    		iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
    		iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, 			       base_addr+S3C2410_ADCCON);
    		} else {  //而当采集到约定的次数时,进入开启定时器,然后进定时器处理函数中对XY坐标进	                   //行处理
    			mod_timer(&touch_timer, jiffies+1);
    			iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);
    		}
    
    	return IRQ_HANDLED;
    }
    


    通过上面的分析我们可以看出,老师所讲的方法和内核所提供的方法,他们大致的内容是一样的,不同的地方只是具体内容在不同的位置设定。

     

     

     

    展开全文
  • WinCE触摸屏驱动开发详解)

    千次阅读 2013-08-16 23:44:30
    本文向您介绍WinCE中采用分层驱动程序结构实现的触摸屏驱动,包括对触摸屏驱动模型的讲解、触摸屏驱动程序的实现及接口实现等知识。 1.触摸屏驱动程序的模型 1.1 分层触摸屏驱动程序结构 本触摸屏驱动采用分层驱动...

    欢迎加入Wince技术讨论群QQ#326444254


    本文向您介绍WinCE中采用分层驱动程序结构实现的触摸屏驱动,包括对触摸屏驱动模型的讲解、触摸屏驱动程序的实现及接口实现等知识。

    1.触摸屏驱动程序的模型

    1.1 分层触摸屏驱动程序结构

    本触摸屏驱动采用分层驱动程序结构,其驱动模型如下图所示,这种结构将驱动程序代码区分为上层模型设备驱动层(MDD),下层是依赖平台的驱动层(PDD)。其中MDD层通常无需修改就可以直接使用,该部分提供面向GWES的DDI的接口,而MDD通过指定的DDSI函数接口调用PDD,这就是我们通常驱动要实现的部分。PDD部分和MDD部分除了DDSI函数集接口外,还要实现一些指定的变量的定义或变量初始化动作(比如,gIntrTouch和gIntrTouchChanged在PDD层定义,但主要在MDD层使用。),也就是说MDD层和PDD层之间并不一定是以严格的分层模型来实现的,有时候也要通过共享变量的方式来完成交互。

    图1 WinCE的触摸屏驱动模型

    1.2 DDI函数集(MDD层)

    TouchPanelPowerHandler(BOOL boff)

    Touch Screen的电源管理函数,boff:TRUE表示关闭电源,FALSE表示打开电源,其只是调用DdsiTouchPanelPowerHandler()函数,该函数在进入或退出poweroff状态时产生。

    TouchPanelCalibrationAPoint()

    该函数用于校准输入的触摸屏坐标,把触摸屏坐标转换为显示坐标,利用了公式Sx=A1*Tx+B1*Ty+C和Sy=A2*Tx+B2*Ty+C2。

    TouchPanelReadCalibrationPoint()

    在执行触摸屏校准程序时,用这个函数获得在当前校准点的十字形上点击的触摸屏坐标。

    TouchPanelReadCalibrationAbord()

    该函数在校准取消时被调用(在触摸屏校准程序运行过程中取消校准),仅仅设置状态位和事件后返回。

    TouchPanelDisable()

    禁用触摸屏(touch panel)设备,该函数关闭ISR,停止中断和注销事件及其他同步手段,此函数调用了DdsiTouchPanelDisable()函数。

    TouchPanelEnable(PFN_TOUCH_PANEL_CALLBACK    pfnCallback)

    PfnCallback是指向处理touch panel事件的回调函数,该函数的执行动作:

    ⑴创建事件hTouchPanelEvent和hCalibrationSampleAvailable,其中当触笔按下或抬起,或者定时器中断时会触发hTouchPanelEvent事件,而在校准状态下当有校准数据输入时会触发hCalibrationSampleAvailable事件。

    ⑵初始化临界区,初始化所需的触摸屏中断gIntrTouch和gIntrTouchChanged,并且把它们关联到事件hTouchPanelEvent中。

    ⑶创建IST TouchPanelpISR,并设定其优先级。

    TouchPanelSetCalibration()

    该函数通过运行触摸屏校准程序时的校准动作获得显示坐标(Sx,Sy)和触笔在十字形上按下的触摸坐标(Tx,Ty)用于计算校准参数A1,B1,C1和A2,B2,C2。

    TouchPanelGetDeviceCaps()

    用于查询触摸屏设备支持的具体功能,通过DDSI函数查询相应的信息,当查询屏幕坐标信息时保存屏幕信息,供后面程序计算校准参数所用。

    TouchPanelSetMode()

    用于设置触摸屏的工作模式(采用低采样率还是高采样率),当设置IST优先级时直接通过内核API来完成,而直接将其他设置交给DdsiTouchPanelSetMode()函数来处理。

    1.3  WinCE DDSI函数集(PDD层)

    DdsiTouchPanelGetDeviceCaps(INT iIndex, LPVOID lpOutput)

    查询touch panel设备的相关信息。

    IIndex:查询的索引值,其取值如下:

    TPDC_SAMPLE_RATE_ID:查询采样率信息。

    TPDC_CALIBRATION_POINT_COUNT_ID:查询用于校验的点的个数。

    TPDC_CALIBRATION_POINT_ID:查询需要校验的点的坐标。

    LpOutput:根据iIndex值分别指向相关的信息。

    DdsiTouchPanelSetMode()

    设置Touch Panel工作模式。

    iIndex:模式索引

    TPSM_SAMPLERATE_HIGH_ID:高采样率

    TPSM_SAMPLERATE_LOW_ID:低采样率

    lpInput:指向包含相关信息的内存

    DdsiTouchPanelEnable()

    该函数所执行的动作:

    ⑴为需要用到的I/O,ADC,PWM和INT寄存器分配内存空间。

    ⑵配置触摸屏控制器、中断控制器和PWM的寄存器。

    ⑶申请触摸屏中断gIntrTouch和定时器中断gIntrTouchChanged,并且对它们进行初始化,为物理中断号分配相应的系统逻辑中断号。。

    DdsiTouchPanelDisable()

    屏蔽触摸屏中断和释放为I/O,ADC,PWM和INT寄存器分配的WinCE内存空间。

    DdsiTouchPanelAttach()

    只是简单地返回1。

    DdsiTouchPanelDetach()

    只是简单地返回0。

    DdsiTouchPanelGetPoint (TOUCH_PANEL_SAMPLE_FLAGS * pTipStateFlags,

                           INT * pUncalX,

                           INT * pUncalY )

    获得Touch Panel上被按下的点的状态和坐标。

    ◆pTipStateFlags:当前触摸点的状态,比如无效点,有效点,被按下的点等。

    ◆pUnCalX:触摸点的X坐标

    ◆pUnCalY:触摸点的Y坐标

    DdsiTouchPanelPowerHandler(BOOL   bOff)

    设置touch panel的电源状态,boff:TRUE表示关闭电源,FALSE表示打开电源,

    2.触摸屏驱动程序的实现

    Windows CE5.0触摸屏驱动程序采用中断方式对触摸笔的按下状态进行检测,如果检测到触摸笔按下将产生中断并触发一个事件通知一个工作线程开始采集数据。同时,驱动将打开一个硬件定时器,只要检测到触摸笔仍然在按下状态将定时触发同一个事件通知工作线程采集数据,直到触摸笔抬起后关闭该定时器,并重新检测按下状态。驱动中采用了触摸屏中断以及定时器中断2个中断源,不仅可以监控触摸笔按下和抬起状态,而且可以检测触摸笔按下时的拖动轨迹。触摸屏驱动流程下图所示

    3.四线电阻式触摸屏的工作原理

    四线电阻式触摸屏的结构如图1,在玻璃或丙烯酸基板上覆盖有两层透平,均匀导电的ITO层,分别做为X电极和Y电极,它们之间由均匀排列的透明格点分开绝缘。其中下层的ITO与玻璃基板附着,上层的ITO附着在PET薄膜上。X电极和Y电极的正负端由“导电条”(图中黑色条形部分)分别从两端引 出,且X电极和Y电极导电条的位置相互垂直。引出端X-,X+,Y-,Y+一共四条线,这就是四线电阻式触摸屏名称的由来。当有物体接触触摸屏表面并施以 一定的压力时,上层的ITO导电层发生形变与下层ITO发生接触,该结构可以等效为相应的电路,

    计算触点的X,Y坐标分为如下两步:

    1. 计算Y坐标,在Y+电极施加驱动电压Vdrive, Y-电极接地,X+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点Y坐标与屏高度之比。

    2. 计算X坐标,在X+电极施加驱动电压Vdrive, X-电极接地,Y+做为引出端测量得到接触点的电压,由于ITO层均匀导电,触点电压与Vdrive电压之比等于触点X坐标与屏宽度之比。

    测得的电压由ADC转化为触摸点的原始坐标(数值范围由所选用的A/D转换器位数决定)后,还要根据具体使用的液晶屏实际像素进行转换,转换后通过校准直接转化为屏幕上的坐标,供GWES使用。

    4.触摸屏的接口部分

    ◆X+:连接触摸屏控制器的TSXP,。

    ◆X-:连接触摸屏控制器的TSXM。

    ◆Y+:连接触摸屏控制器的TSYP。

    ◆Y-:连接触摸屏控制器的TSYM。

    在触摸屏接口使用时,TSXM或TSYM应该接触摸屏接口的。

    5.配置控制器硬件

    5.1 ADCCON-----ADC控制寄存器

    ECFLG:ADCCON[15],AD转换结束标志,只读,0表示AD转换在过程中;1表示AD转换结束。

    PRSCEN:ADCCON[14],AD转换器预分频器使能,在此使能,故为1。

    PRSCVL:ADCCON[13:6],AD转换器预分频器值,在此为49。

    SEL_MUX:ADCCON[5:3],模拟信号输入通道选择,在此选择XP,故为7。

    STDBM:ADCCON[2],备用操作模式选择,在此选择普通操作模式,故为0。

    READ_START:ADCCON[1],通过读取来启动A/D转换,在此选择通过读取操作无效来启动A/D转换,故为0。

    ENABLE_START,ADCCON[0],通过使能该位来启动A/D转换,在此选择无操作。此位在A/D转换开始后被使能。

    5.2 ADCTSC-----ADC触摸屏控制寄存器

    UD_SEN:ADCTSC[8],在此选择检测到触笔按下就产生中断信号,故为0。

    YM_SEN:ADCTSC[7],YM开关使能,在此选择YM输出驱动有效(GND),故为1。

    YP_SEN:ADCTSC[6],YP开关使能,在此选择YP输出驱动无效(AIN5),故为1。

    XM_SEN:ADCTSC[5],XM开关使能,在此选择XM输出驱动无效(Hi-Z),故为0。

    XP_SEN:ADCTSC[4],XP开关使能,在此选择XP输出驱动无效(AIN7),故为1。

    PULL_UP:ADCTSC[3],上拉开关使能,在此选择XP上拉有效,故为0。

    AUTO_PST:ADCTSC[2],初始化时,在此选择自动连续测量X坐标和Y坐标,故为0,但如果开始转换时,应该置1。

    XY_PST:ADCTSC[1:0],手动测量X坐标和Y坐标,在此选择等待中断模式,故为3。

    注:当等待触摸屏中断时,XP_SEN位(XP输出无效)应该置为1,且PULL_UP(XP上拉使能)位应该置为0。

    5.3 ADCDLY-----ADC开始延时寄存器

    DELAY:ADCDLY[154:0],因为选择等待中断模式,此值表示,当触笔按下出现在睡眠模式时,产生一个用于推出睡眠模式的信号,有几个毫秒的时间间隔。在此此值为40000?????????(40s,太长了吧?)

    SUBINTMSK-----WinCE中子中断屏蔽寄存器

    该寄存器有11位,每位和一位中断源相关。触摸屏中断请求有效,故第十位应设为0。

    v_pINTregs->INTSUBMSK &= ~(1<<IRQ_SUB_TC);

    5.4 TCFG1-----5路多路器及DMA模式选择寄存器

    MUX3:TCFG1[15:12],为PWM计时器3选择多路输入,并初始化其值,每个定时器都有一个时钟分频器,其可以生成5钟不同的分频信号(1/2,1/4,1/8,1/16和TCLK),在此选择1/16分频。

    v_pPWMregs->TCFG1 &= ~(0xf << 12); /* Timer3's Divider Value   */

    v_pPWMregs->TCFG1 |= (3   << 12);     /* 1/16   

    TCNTB3-----PWM定时器3计数缓存寄存器,选择定时器3为时钟,比如定义10ms中断一次,提供触摸屏采样时间基准,即10ms触摸屏采样一次。在此为17×1000/100=170,在此PCLK=400MHz/6,可以得出timer3的时钟频率=PCLK/(244+1).16,可以算数触摸屏就是10ms产生一次定时中断,进行一次采样。

    6.判断触摸屏是否被触摸和中断处理

    我们采用中断等待模式,当触笔按下时,触摸屏控制器产生中断信号(INT_TC)给中断控制器,中断处理程序捕捉到这个物理中断后,调用已注册的ISR来决定如何处理这个硬件中断。ISR向内核返回对应于这个物理中断的逻辑中断号,比如我们用触笔按下触摸屏时,产生INT_TC物理中断,ISR把物理中断号映射成逻辑中断信号,然后操作系统根据物理中断号触发所关联的WinCE事件内核对象gIntrTouch,从而等待这个事件内核对象的IST(TouchPanelpISR,这个名字不是很合理应该改为TouchPanelpIST)来开始执行中断处理。

    我们在驱动中采用了两个中断源,一个是触摸屏中断(包括触笔按下或抬起中断,根据ADC触摸屏控制寄存器ADCTSC的UD_SEN位来决定,在代码中的名字是gIntrTouch),也就是说当触笔按下或抬起时会产生物理中断INT_TC,其对应的逻辑中断号为SYSINTR_TOUCH;另一个是定时器中断(在此采用timer3),只要检测到触笔仍然在按下状态,将定时(在此是10ms,在代码中的名字是gIntrTouchChanged)产生定时中断INT_TIMER3,其对应的逻辑中断号为SYSINTR_TOUCH_CHANGED。这两个中断产生时都会触发同一个事件hTouchPanelEvent,然后等待这个时间的IST(TouchPanelpISR)线程将开始执行。

    以下是产生中断的条件

    ⑴ 触笔按下时,产生触摸屏中断。

    ⑵ 触笔抬起时,也产生触摸屏中断。

    ⑶ 触笔按下产生触摸屏中断的同时,驱动打开硬件定时器timer3,只要检测到触笔仍然在按下状态,将定时(每隔10ms产生定时器中断)中断。

    判断触笔对于触摸屏的活动情况的流程如下: 

    7. 获得稳定、去抖动的位置测量数据

    在获得触摸点的原始坐标(数值范围由所选用的A/D转换器位数决定)后,还要根据具体使用的液晶屏实际像素进行转换,转换后通过校准直接转化为屏幕上的坐标,以方便图形界面的后续开发,然后通过回调函数把屏幕坐标和采样状态这些参数传递给GWES

    人机界面中对触摸屏的操作有以下3钟:

    ⑴ 触摸笔在触摸屏上的位置不变。

    ⑵ 触摸笔在触摸屏上连续滑过。

    ⑶ 触摸笔在触摸屏上有大幅度的跳跃。

    7.1 触摸屏采样程序

    当触笔按下时,就开始进行触摸屏触摸坐标的采样,同时打开定时中断,为10ms后的定时器中断做好准备。下面是采样过程:

    ⑴ 通过对ADC控制寄存器ADCCON和ADC触摸屏控制寄存ADCTSC的设置,启动自动测量X坐标和Y坐标。

    ⑵ 等待AD转换完成,通过对ADCCON的ECFLG的访问可以判断AD转换完成与否。

    ⑶ AD转换完成后,就通过对ADC转换数据寄存器ADCDAT0和ADCDAT1的读取触摸屏坐标的Y坐标和X坐标值。

    ⑷ 回到第一步,连续取得5组数据,求和并计算其平均值,如果平均值和这5组数据中最近一次数据的差值大于我们规定的阈值(这个阈值应该需要根据我们采用的LCD屏的分辨率来决定)时,认为此次采样值无效,同时对修改寄存器ADCTSC的设置,让其处于等待中断模式,为下次中断(触笔抬起中断做好准备)。

    7.2 触摸屏滤波程序

    通过函数TSP_GetXY()得到触笔按下位置的触摸屏坐标后,到把校准后的坐标通过回调函数传递给GWES,其中此回调函数是通过GWES调用TouchPanelEnable(pfnCallback)函数传递进来的,这过程有以下步骤:

    ⑴ 通过函数TSP_GetXY()得到触笔按下位置的触摸屏坐标。

    ⑵ 根据具体使用的液晶屏实际像素对触摸屏坐标进行转换,也即通过函数

    TSP_TransXY()转换TSP_GetXY()函数得到的触摸屏坐标,用到公式

    X=W*(x-x1)/(x2-x1),Y=H*(y-y1)/(y2-y1),其中(x1,y1)和(x2,y2)分别表示显示区域左上角和右上角对应的触摸屏的采样坐标,在此显示屏的分辨率为240×320,代码中(x1,y1)和(x2,y2)值为(85,105)和(965,980),这些值是分辨率240×4和320×4后确定的,相应地我们也需要对W和H做处理:W=4*W,H=4*H,(x,y)是触摸屏上任意一点的采样坐标(在这里有一个地方有疑问,右下角坐标的x值为什么是965,它不是大于240×4了吗?)。

    ⑶ 对由⑵得到的坐标值进行判断,如果x坐标值大于等于240×4,或者y坐标值大于等于320×4(也就是⑵转换前的x坐标值大于等于965,y坐标值大于等于980)时,我们认为这次采样值无效,或者如果最近两次采样值的相应的x坐标值差额或y坐标值差额分别小于等于15和40,则表示触笔在这两次采样的时间内一直在同一个位置,故忽略最近的采样值;如果如果最近两次采样值的相应的x坐标值差额或y坐标值差额分别大于15和40,则表示触笔在这两次采样的时间内触笔在滑动,此次采样有效。

    ⑷通过TouchPanelCalibrateAPoint()函数把有效的触摸屏按下点的坐标转换成相应的显示设备点坐标,用到Sx=A1*Tx+B1*Ty+C和Sy=A2*Tx+B2*Ty+C2这两个公式,其中A1,B1,C1和A2,B2,C2在执行触摸屏校准程序的时候通过TouchPanelSetCalibration()函数计算出来。

    ⑸通过⑷转换得到显示坐标,对这些值进行边界检查,如果此x坐标值大于等于。

    ⑹通过TouchPanelEnable(pfnCallback)函数传递进来的函数指针进行回调,同时传递采样状态,校准后得到的显示坐标给GWES。


    触笔按下的触摸屏坐标经过校准后,通过callbak函数采样状态和位置变化信息通过GWES发送给更高层的图形软件。

    8.校准触摸屏

    触摸屏的校准过程如下:

    ⑴ 调用TouchPanelEnable函数来启动触摸屏采样。

    ⑵ 调用TouchPanelGetDeviceCaps函数来获得校准触点的个数。

    ⑶ 去校准点,对于每一个校准点都有一下过程:

    I:调用TouchPanelGetDeviceCaps函数来得到校准点的坐标。

    II:在上述校准点处画十字,等待用户点击.

    III:用户点击十字图形后,调用TouchPanelReadCalibrationPoint函数来获得上述校准点处触摸屏对应的坐标。

    ⑷ 当取完所需用的校准点,调用TouchPanelSetCalibration函数来计算校准系数A1,B1,C1和A2,B2,C2。

    完成上述步骤后,WinCE平台下的触摸屏驱动开发就已基本完成,实际操作中还需考虑具体的硬件环境。

    展开全文
  • 触摸屏驱动之编写驱动程序

    千次阅读 2017-03-13 22:02:48
    我们打开我们的核心板原理图可以看到这四根引脚分别接在xadcAIN2,3,4,5上面 ...先来看一看内核自带的触摸屏驱动做了什么事情 ts.clock = clk_get(dev, "adc"); if (IS_ERR(ts.clock)) { dev_err
  • 触摸屏驱动开发——转载
  • 触摸屏驱动开发

    千次阅读 2012-08-20 21:59:03
    触摸屏驱动开发 1.触摸屏驱动程序的模型 1.1 分层触摸屏驱动层序结构 本触摸屏驱动采用那个分层驱动程序结构,其驱动模型如下图所示,这种结构将驱动程序代码区分为上层模型设备驱动层(MDD),下层是依赖平台...
  • 一、电容式触摸屏检测原理 基本原理是利用充电时间检测电容大小,从而通过检测出电容值的变化来获知触摸信号。电容屏的最上层是玻璃(不会像电阻屏那样形变),核心层部分也是由ITO材料构成的,这些导电材料在屏幕里...
  • 嵌入式Linux/Android驱动开发揭秘(1)触摸屏驱动开发 专题简介:自1971年,美国人SamHurst发明了世界上第一个触摸传感器以来,触摸屏技术不断革新,给了程序设计师和UI工程师无限的想象空间,它极大改善了终端...
  • win10 64bits,信捷触摸屏开发软件,下载的时候需要安装驱动程序。 用的以前的win7 64bits的驱动程序,直接双击安装,显示无法操作注册表,需要管理员模式。 使用管理员权限安装,仍然显示无法操作注册表。 ...
  • 本期课程主要讲解触摸屏,首先用一节课讲了输入类设备的特点,尤其是触摸屏的一些特征。然后用2节课讲了电阻式触摸屏的原理、SOC的电阻触摸屏控制器的工作方式与编程方法;后用2节课讲了电容式触摸屏的工作原理、...
  • 本课程是linux驱动开发的第9个课程,主要内容是linux的I2C子系统以及电容触摸屏驱动的移植。学习本课程的核心点在于I2C子系统,目标是对I2C驱动框架的彻底理解和熟练运用。本课程承袭前面platform平台总线的讲解思路...
  • 有一个工程需要利用昆仑通态工控软件(电脑版)监控两个...但是,他们提供昆仑通态触摸屏对于这两种PLC的驱动。想到前一段研究过该触摸屏与电脑的通讯问题,通过ModbusTCPIP转发功能,该触摸屏可以作为一个Modb...
  • 安卓的触摸屏驱动
  • 使用MCGS触摸屏与单片机通讯的心得

    千次阅读 2018-09-30 08:56:40
    所以如果你想做MCGS与单片机通讯,最好采用他们提供的脚本驱动开发工具V2.0,这个在网上都能找到的,这里我就不给出链接了。这款软件使用的语言是类VB语言,不完全遵循VB语言的语法规则,所以你还是要看下他的语法...
  • 触摸屏: 一、首先要在开发板上安装tslib ... 二、安装完后,加载触摸屏驱动:insmod ts.ko,然后运行./ts_calibrate 进行五点校准,之后会在/etc/下生成一个文件 pointercal,要有这个触摸屏才能用。 三、修改/etc/p
  • 触摸屏驱动程序的模型  <br />1.1 分层触摸屏驱动层序结构 <br />本触摸屏驱动采用分层驱动程序结构,其驱动模型如下图所示,这种结构将驱动程序代码区分为上层模型设备驱动层(MDD),下层是依赖平台的驱动...
  • 本产品是专为树莓派打造、却也不限于树莓派的7寸HDMI显示屏,800x480高清分辨率,带电容触摸屏。 本产品同时也是通用HDMI显示屏,用户可以把它用到其他mini PC上(需要驱动支持),甚至将其作为计算机显示器。 产品...
  • WINCE6.0+S3C6410的触摸屏驱动

    千次阅读 2011-08-13 09:00:31
    ********************************LoongEmbedded******************************** 作者:LoongEmbedded(kandi) 时间:2011.8.13 类别:WINCE 驱动开发 *******
  • linux下i2c接口的电容触摸屏驱动开发

    千次阅读 2012-06-28 14:00:19
    linux下i2c接口的电容触摸屏驱动开发 http://koffuxu.blog.51cto.com/1063541/548672 5 I2C 电容触摸屏驱动 a) I2C通信协议在这里使用100K的通信速率,其他不做介绍。linux系统下编写I2C驱动,主要有两种方式,...
1 2 3 4 5 ... 20
收藏数 14,302
精华内容 5,720
关键字:

触摸屏驱动开发