zoukankan      html  css  js  c++  java
  • 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
    
    ",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
    
    ");
        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");
    

    <wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

  • 相关阅读:
    oracle omf管理 and asm omf
    oracle 9i下载地址
    rac部署过程的任务列表
    oracle connect实例
    cpio.gz文件解压缩方式
    函数remove_constants
    oracle优化日记脚本
    查看oracle隐含参数
    rac笔记
    MotionEvent的getX(),getY()与getRawX(),getRawY()区别
  • 原文地址:https://www.cnblogs.com/luosir520/p/11447310.html
Copyright © 2011-2022 走看看