005触摸屏驱动程序

一、 触摸屏驱动程序之概念介绍(第十六课/第一节)

内核会带有(s3c2410_ts.c)触摸屏的驱动程序(平台驱动),大概浏览,然后仿造

注册一个平台驱动

若内核里面有同名的平台设备驱动程序的时候,probe函数就会被调用

分配一个input_dev结构体

设置它

注册

当有事情发生时会上报事件

1th、现在开始写触摸屏驱动代码**

先从入口函数开始

第一步:分配一个input_dev结构体

第二步:设置

设置能产生哪类事件

设置能产生该类事件里哪些事件

第三步:注册

触摸屏原理:

触摸屏使用过程

二、 触摸屏驱动程序之编写驱动(第十六课/第二节)

查看硬件原理图:

先看看内核里的触摸屏驱动程序做了哪些事情

为了省电,内核在启动时,对那些不是必须的模块都会关掉。

ADC触摸屏有四种模式

1. 正常的转换模式:

2. 分离的X/Y坐标转换模式:

3. 自动(连续)X/Y坐标转换模式:

4. 等待中断模式:

2th、在第一个基础写第二个触摸屏驱动测试(可以检测按下或松开)

定义一个时钟,并使能

映射相关寄存器

注册中断

中断函数

测试2th:

第一步:make menuconfig去掉原来的驱动程序

第二步:编译没有TS的内核

第三步:使用新的没有TS的uImage启动

第四步:编译,挂接,加载驱动

3th、 按下触摸屏打印adc的值

第一步:注册添加ADC中断

第二步:按下触摸屏后启动测量

第三步:adc转换结束后会触发中断

测试3th:

编译3th驱动程序,加载驱动

BUG来了:电压变化挺大,需要继续改进

分析:1.触摸屏巧妙的应用了"欧姆定律",当按下时就立刻产生中断,但此时的电压可能并没有稳定下来,所以测量的电压可能会不太准确;2.ADC启动和转换需要一定的时间,不可能瞬间完成,若在启动过程中松开触摸屏,这个电压值也不稳定。

4th、 改进电压不稳定

改进方法1:使用(ADCDLY)寄存器增加延时,我们设置为最大

改进方法2:(判断寄存器ADCDAT0或ADCDAT1)若在启动过程中松开触摸屏,丢弃该值

测试4th:

编译4th驱动程序,加载驱动

5th、 多次测量优化

改进方法3:多次测量求平均值

测试5th:

编译5th驱动程序,加载驱动

6th、 软件过滤

改进方法4:软件过滤思路:一共采集4个数,把前两个数求平均值后与第三个数相减求差值,若差值大于某个设定值,则说明这四个值都不可靠;若可靠,把第二个数和第三个数求平均值后与第四个数相减求差值,若差值大于某个设定值,也说明这四个值都不可靠;若也可靠则返回过滤成功,然后打印。

测试6th:

编译6th驱动程序,加载驱动

7th、 处理触摸屏滑动

改进方法5:使用定时器处理长时间按下或滑动

定义并添加一个定时器到定时器列表

在数据完整可靠的情况下修改定时器的超时时间

测试7th:

编译7th驱动程序,加载驱动

8th、 完整的触摸屏驱动程序

操作方法:把里面的"printk()"改成"上报事件"即可。

先看看内核自带的触摸屏驱动怎么做的

在自己的触摸屏驱动程序里模仿

同样在松开的时候也应该上报事件

测试8th:

编译8th驱动程序,在加载驱动前,先看看有哪些event设备结点

加载驱动,并再次查看设备结点

(hexdump /dev/event0)读取event0设备

使用tslib测试:

根据(tslib编译使用方法.TXT)文件配置

拷贝tmp到nfs文件系统的first_fs目录下,并改名为ts_dir:

r:表示递归;f:表示强制;d:表示链接仍保持原本的链接

修改(/etc/ts.conf)文件

添加环境变量

加载触摸屏驱动,加载LCD驱动

校验测试:

校验成功后生成以下校验文件

还有很多别的测试程序:

最后的触摸屏驱动程序

#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 int adccon;
    unsigned int adctsc;
    unsigned int adcdly;
    unsigned int adcdat0;
    unsigned int adcdat1;
    unsigned int adcupdn;
    };

static struct clk    *s3c_adc_clock;
static struct input_dev *s3c_ts_dev;
static volatile struct s3c_ts_regs* s3c_ts_regs;
static struct timer_list timer_ts;

static void enter_wait_down_mode(void)
{
    s3c_ts_regs->adctsc = 0xd3;
}

static void enter_wait_up_mode(void)
{
    s3c_ts_regs->adctsc = 0x1d3;
}

static void enter_measure_mode(void)
{
    s3c_ts_regs->adctsc = (1<<2)|(1<<3);
}

static void start_adc(void)
{
    s3c_ts_regs->adccon |= (1<<0);
}

static int filter_adc(int *a, int *b)
{
#define ERR_LIMIT 10
    int avr_x,avr_y;
    int del_x, del_y;

    avr_x = (a[0] + a[1])/2;
    avr_y = (b[0] + b[1])/2;

    del_x = (avr_x > a[2])? (avr_x - a[2]):(a[2] - avr_x);
    del_y = (avr_y > b[2])? (avr_y - b[2]):(b[2] - avr_y);

    if((del_x > ERR_LIMIT)||(del_y > ERR_LIMIT))
        return 0;

    avr_x = (a[1] + a[2])/2;
    avr_y = (b[1] + b[2])/2;

    del_x = (avr_x > a[3])? (avr_x - a[3]):(a[3] - avr_x);
    del_y = (avr_y > b[3])? (avr_y - b[3]):(b[3] - avr_y);

    if((del_x > ERR_LIMIT)||(del_y > ERR_LIMIT))
        return 0;

    return 1;
}

static void s3c_timer_irq_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_down_mode();
    }
    else
    {
        enter_measure_mode();
        start_adc();
    }
}

static irqreturn_t s3c_adc_irq_function(int irq, void *dev_id)
{
    /* 改进措施2:在adc还在转换时松开的值应该丢弃 */
    static int cnt = 0;
    static int adc_x[4], adc_y[4];
    /* 松开 */
    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_down_mode();
    }
    else
    {
        /* 优化措施3:多次测量求平均值 */
        adc_x[cnt] = s3c_ts_regs->adcdat0&0x3ff;
        adc_y[cnt] = s3c_ts_regs->adcdat1&0x3ff;
        cnt++;
        if(4==cnt)
        {
            if(filter_adc(adc_x, adc_y))
            {
//                printk("cnt = %d, adc_x = %d, adc_y = %d\n\r",cnt++, (adc_x[0]+adc_x[1]+adc_x[2]+adc_x[3])/4, (adc_y[0]+adc_y[1]+adc_y[2]+adc_y[3])/4);
                input_report_abs(s3c_ts_dev, ABS_X, (adc_x[0]+adc_x[1]+adc_x[2]+adc_x[3])/4);
                input_report_abs(s3c_ts_dev, ABS_Y, (adc_y[0]+adc_y[1]+adc_y[2]+adc_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_up_mode();

            /* 启动定时器 */
            mod_timer(&timer_ts, jiffies + HZ/100);    //10ms后启动定时器
        }
        else
        {
            enter_measure_mode();
            start_adc();
        }
    }
    return IRQ_HANDLED;
}

static irqreturn_t s3c_ts_irq_function(int irq, void *dev_id)
{
    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_down_mode();
    }
    else
    {
        /* 按下 */
        enter_measure_mode();
        start_adc();
    }
    return IRQ_HANDLED;
}

static int s3c_ts_init(void)
{
    /* 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使能时钟 bit[15] */
    s3c_adc_clock = clk_get(NULL, "adc");
    clk_enable(s3c_adc_clock);

    /* 4.3 配置寄存器 */
    s3c_ts_regs = ioremap(0x58000000, sizeof(struct s3c_ts_regs));

    /* ADCCON 寄存器
     * [14]   : 1-A/D converter prescaler enable
     * [13:6] : A/D converter prescaler value
     */
    s3c_ts_regs->adccon = (1<<14)|(49<<6);

    /* 4.2 注册中断 */
    request_irq(IRQ_TC,   s3c_ts_irq_function,    IRQF_SAMPLE_RANDOM,"s3c_ts", NULL);
    request_irq(IRQ_ADC, s3c_adc_irq_function, IRQF_SAMPLE_RANDOM,"s3c_adc", NULL);

    /* 优化措施1:设置     adcdly */
    s3c_ts_regs->adcdly = 0xffff;

    /* 优化措施5:使用定时器处理长按,滑动的情况 */
    init_timer(&timer_ts);
    timer_ts.function =  s3c_timer_irq_function;
    add_timer(&timer_ts);

    enter_wait_down_mode();
    printk("s3c_ts_init is ok\n\r");
    return 0;
}

static void s3c_ts_exit(void)
{
    free_irq(IRQ_ADC,NULL);
    free_irq(IRQ_TC, NULL);
    iounmap(s3c_ts_regs);
    input_unregister_device(s3c_ts_dev);
    input_free_device(s3c_ts_dev);
    del_timer(&timer_ts);

    clk_disable(s3c_adc_clock);
    clk_put(s3c_adc_clock);
    s3c_adc_clock = NULL;
}

module_init(s3c_ts_init);
module_exit(s3c_ts_exit);

MODULE_LICENSE("GPL");

原文地址:https://www.cnblogs.com/luosir520/p/11447310.html

时间: 2024-10-11 22:48:36

005触摸屏驱动程序的相关文章

TQ2440四线电阻式触摸屏驱动程序的分析

一,编写触摸屏驱动程序的准备知识之一:输入子系统  1.输入子系统简单介绍 linux系统提供了input子系统,按键.触摸屏.鼠标等输入型设备都可以利用input接口函数来实现设备驱动. 2.输入子系统的组成 输入子系统由驱动层(drivers).输入子系统核心层(input core)和事件处理层(Event Handler)三部分组成. 驱动层:将底层的硬件输入转化为统一的事件型式,向输入核心层汇报. 输入子系统核心层:为驱动层提供输入设备注册与操作接口,如input_register_d

触摸屏驱动程序

触摸屏驱动程序框架与上一片文章的输入子系统类似,只是底层驱动由按键变成了触摸屏. S3C2440的ADC相关寄存器: struct s3c_ts_regs { unsigned long adccon; unsigned long adctsc; unsigned long adcdly; unsigned long adcdat0; unsigned long adcdat1; unsigned long adcupdn; }; 1.分配input_dev结构体 struct input_de

网卡触摸屏驱动程序

一.Linux网络体系架构 二.网卡驱动设计 三.Dm9000网卡驱动分析 四.Linux输入子系统 五.触摸屏驱动设计 网卡触摸屏驱动程序

TQ2440触摸屏驱动程序的移植

按照天嵌官方的<linux移植之step by step>手册上的方法,做linux2.6.30.4触摸屏驱动程序的移植没有成功,经过一番摸索发现是2.6.30.4没有合适的ADC驱动,所以就着手自己做ADC和触摸屏驱动的移植,下面是我解决问题的详细过程: 1.找到天嵌给的linux2.6.30.4的源码,进入drivers/char 目录,把ADC驱动程序的源码抠出来,注意还有那个ADC驱动程序要调用的头文件,我的是直接把头文件加到ADC驱动程序的源码里面,下面是加入头文件修改后的ADC驱动

linux驱动之触摸屏驱动程序

触摸屏归纳为输入子系统,这里主要是针对电阻屏,其使用过程如下 :当用触摸笔按下时,产生中断.在中断处理函数处理函数中启动ADC转换x,y坐标.ADC结束,产生ADC中断,在ADC中断处理函数里上报(input_event)启动定时器,再次启动定时器(可以处理滑动.长按),松开按键.其驱动程序的写法和之前写输入子系统的写法基本上一致.写出入口函数,出口函数并加以修饰,加入相关头文件,然后开始完善各函数,在入口函数中分配input_dev结构体,设置(能产生哪类事件,能产生这类事件中的哪些事件),注

驱动06.触摸屏驱动程序

1.触摸屏的简介 触摸屏是标准的输入设备,在写驱动程序时采用的之前讲过的输入子系统那套框架.我们无需关心对设备文件的操作,只需关心对硬件寄存器的操作和上报事件即可. 触摸屏是附在LCD上的一层薄膜,并不是我们平时认识的触摸屏,它只是起到确定坐标的作用. S3C2440提供的触摸屏接口有4种处理模式,分别是:正常转换模式.单独的X/Y位置转换模式.自动X/Y位置转换模式和等待中断模式.本例子中用的是等待中断模式 2.以s3c2410_ts.c为例分析整体框架 2.1 s3c2410ts_init函

电阻触摸屏驱动程序(二)

首先来看一下,硬件操作需要干哪些事情.看一下TSYP.TSXP.TSYM.TSXM四个引脚接到了哪里? 搜索TSYP引脚 TSYM.TSYP.TSXM.TSXP分别接到了AIN4.AIN5.AIN6.AIN7上,它们不是普通的GPIO管脚. 在2440的芯片手册里搜索AIN4,发现要么用作模拟输入,要么用作触摸屏的,因此不需要 配置寄存器. 1 #include <linux/errno.h> 2 #include <linux/kernel.h> 3 #include <l

NUC970平台触摸屏驱动移植

原理概述 1.首先要区分lcd和触摸屏,lcd是一个屏幕,触摸屏是贴在lcd上的两层膜. 2.四线电阻触摸屏的原理:触摸屏就是上下两层膜,比如上层代表x轴(XM:负端,XP:正端),下层代表y轴(YM:负端,YP:正端).当读取x轴坐标时,XP接3.3v,XM接地,从YM读取按下点的电压值作为模拟输入信号,再经过AD转换后就得到了x轴坐标.同理,当读取y轴坐标时,YP接3.3v,YM接地,从XM读取按下点的电压值作为模拟输入,再经过AD转换后就得到了y轴坐标. 触摸屏驱动程序采用中断方式对触摸笔

S3C2440触摸屏驱动实例开发讲解

出处:http://www.embeddedlinux.org.cn/html/yingjianqudong/ 一.开发环境 主  机:VMWare--Fedora 9 开发板:Mini2440--64MB Nand, Kernel:2.6.30.4 编译器:arm-linux-gcc-4.3.2 二.前提知识 1.Linux输入子系统(Input Subsystem): 在Linux中,输入子系统是由输入子系统设备驱动层.输入子系统核心层(Input Core)和输入子系统事件处理层(Even