码迷,mamicode.com
首页 > 其他好文 > 详细

005触摸屏驱动程序

时间:2019-09-02 16:56:13      阅读:117      评论:0      收藏:0      [点我收藏+]

标签:文件   print   编译   ret   edit   scale   去掉   校验   平台   

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

内核会带有(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");

005触摸屏驱动程序

标签:文件   print   编译   ret   edit   scale   去掉   校验   平台   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!