Linux输入子系统之触摸屏驱动2

来源:互联网 发布:mac rar解压软件下载 编辑:程序博客网 时间:2024/05/22 13:20

触摸屏使用过程
1.按下,产生中断pen_down_up_irq
2.在中断处理程序里面启动ADC,转换坐标(x,y)
3.ADC结束之后,产生ADC中断
4.在ADC中断处理函数里面上报事件(input_event),启动定时器。
5.定时器时间到(处理长按、滑动)
5.1如果触摸笔松开,进入等待按下模式
5.2否则,再次再一次测量x,y,再次启动ADC

初始化函数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);   //按下触摸屏,产生中断,pen_down_up_irq:中断处理函数    request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);       //ADC完成之后产生的中断,adc_irq:中断处理函数    /* 优化措施1:      * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断     */    s3c_ts_regs->adcdly = 0xffff;    /* 优化措施5: 使用定时器处理长按,滑动的情况     *      */    init_timer(&ts_timer);     //初始化,传入定时器地址&ts_timer    ts_timer.function = s3c_ts_timer_function;  //超时函数    add_timer(&ts_timer);   //把定时器告诉内核,当超时后,s3c_ts_timer_function被调用    enter_wait_pen_down_mode();   //设置等待触摸笔按下模式,让寄存器ADCTSC=0xd3,进入等待中断模式Waiting for Interrupt Mode    return 0;}

按键按下后,产生的中断函数
pen_down_up_irq()

static irqreturn_t pen_down_up_irq(int irq, void *dev_id){       /*如何判断按下还是松开?  ADCDAT0的bit[15],0表示按下,1表示松开         松开的时候进入等待按下模式enter_wait_pen_down_mode(),这样他就继续处理下一次。              */    if (s3c_ts_regs->adcdat0 & (1<<15))   //松开    {        //printk("pen up\n");        input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);    //上报事件,ABS_PRESSURE压力值为0        input_report_key(s3c_ts_dev, BTN_TOUCH, 0);    //BTN_TOUCH表示按键类事件,没有按下按键        input_sync(s3c_ts_dev);        enter_wait_pen_down_mode();   //触摸笔按下模式    }    else    //按下    {        //printk("pen down\n");        //enter_wait_pen_up_mode();        enter_measure_xy_mode();  //测量x,y坐标模式        start_adc();     //启动ADC    }    return IRQ_HANDLED;}

进入测量x,y坐标模式

static void enter_measure_xy_mode(void){    s3c_ts_regs->adctsc = (1<<3)|(1<<2);   //bit[3]=1 禁止上拉使能,上拉电阻要断开,bit[2]=1设置模式3自动(连续)的x/y坐标转换模式}

启动ADC

static void start_adc(void){    s3c_ts_regs->adccon |= (1<<0);  //设置启动ADCCON寄存器bit[0]=1,就启动ADC}

ADC结束,产生ADC中断

static irqreturn_t adc_irq(int irq, void *dev_id)      //ADC 完成之后,产生一个中断{    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\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);        /* 优化措施3: 多次测量求平均值 */        x[cnt] = adcdat0 & 0x3ff;      //x的坐标放在adcdat0寄存器的bit[9:0]里面        y[cnt] = adcdat1 & 0x3ff;     //y的坐标放在adcdat1寄存器的bit[9:0]里面        ++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);                //上报事件                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);//上报压力,1表示按下                input_report_key(s3c_ts_dev, BTN_TOUCH, 1);  //上报按键事件BTN_TOUCH,1表示按下                input_sync(s3c_ts_dev);   //同步事件            }            cnt = 0;            enter_wait_pen_up_mode();         //进入等待松开模式,这样才能连续操作。测量完成之后,要等待触摸笔松开            /* 启动定时器处理长按/滑动的情况,定时10毫秒 */            mod_timer(&ts_timer, jiffies + HZ/100);  //HZ=1s,1000/100=10        }        else        {            enter_measure_xy_mode();            start_adc();        }           }    return IRQ_HANDLED;}

优化措施5: 使用定时器处理长按,滑动的情况

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();    }}

完整代码

#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 {        //S3C2440触摸屏寄存器    unsigned long adccon;      //unsigned long是4字节,每个寄存器相差4字节    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;      //定义一个指针,第一个指针指向哪里?ioremap就可以了static struct timer_list ts_timer;static void enter_wait_pen_down_mode(void)     //触摸笔按下模式{    s3c_ts_regs->adctsc = 0xd3; //进入等待中断模式Waiting for Interrupt Mode,bit[8]=0,等待按下}static void enter_wait_pen_up_mode(void){    s3c_ts_regs->adctsc = 0x1d3;//进入等待中断模式Waiting for Interrupt Mode,bit[8]=1,等待松开}static void enter_measure_xy_mode(void){    s3c_ts_regs->adctsc = (1<<3)|(1<<2);   //bit[3]=1 禁止上拉使能,上拉电阻要断开,bit[2]=1设置模式3自动(连续)的x/y坐标转换模式}static void start_adc(void){    s3c_ts_regs->adccon |= (1<<0);  //设置启动ADCCON寄存器bit[0]=1,就启动ADC}static int s3c_filter_ts(int x[], int y[])         //过滤函数{#define ERR_LIMIT 10     //误差范围为10    int avr_x, avr_y;      //x,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]);  //如果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))     //如果误差大于10,认为他是错误的值,返回0        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){       /*如何判断按下还是松开?  ADCDAT0的bit[15],0表示按下,1表示松开         松开的时候进入等待按下模式enter_wait_pen_down_mode(),这样他就继续处理下一次。              */    if (s3c_ts_regs->adcdat0 & (1<<15))   //松开    {        //printk("pen up\n");        input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0);    //上报事件,ABS_PRESSURE压力值为0        input_report_key(s3c_ts_dev, BTN_TOUCH, 0);    //BTN_TOUCH表示按键类事件,没有按下按键        input_sync(s3c_ts_dev);        enter_wait_pen_down_mode();   //触摸笔按下模式    }    else    //按下    {        //printk("pen down\n");        //enter_wait_pen_up_mode();        enter_measure_xy_mode();  //测量x,y坐标模式        start_adc();     //启动ADC    }    return IRQ_HANDLED;}static irqreturn_t adc_irq(int irq, void *dev_id)      //ADC 完成之后,产生一个中断{    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\n", ++cnt, adcdat0 & 0x3ff, adcdat1 & 0x3ff);        /* 优化措施3: 多次测量求平均值 */        x[cnt] = adcdat0 & 0x3ff;      //x的坐标放在adcdat0寄存器的bit[9:0]里面        y[cnt] = adcdat1 & 0x3ff;     //y的坐标放在adcdat1寄存器的bit[9:0]里面        ++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);                //上报事件                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);//上报压力,1表示按下                input_report_key(s3c_ts_dev, BTN_TOUCH, 1);  //上报按键事件BTN_TOUCH,1表示按下                input_sync(s3c_ts_dev);   //同步事件            }            cnt = 0;            enter_wait_pen_up_mode();         //进入等待松开模式,这样才能连续操作。测量完成之后,要等待触摸笔松开            /* 启动定时器处理长按/滑动的情况,定时10毫秒 */            mod_timer(&ts_timer, jiffies + HZ/100);  //HZ=1s,1000/100=10        }        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);   //按下触摸屏,产生中断,pen_down_up_irq:中断处理函数    request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);       //ADC完成之后产生的中断,adc_irq:中断处理函数    /* 优化措施1:      * 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断     */    s3c_ts_regs->adcdly = 0xffff;    /* 优化措施5: 使用定时器处理长按,滑动的情况     *      */    init_timer(&ts_timer);     //初始化,传入定时器地址&ts_timer    ts_timer.function = s3c_ts_timer_function;  //超时函数    add_timer(&ts_timer);   //把定时器告诉内核,当超时后,s3c_ts_timer_function被调用    enter_wait_pen_down_mode();   //设置等待触摸笔按下模式,让寄存器ADCTSC=0xd3,进入等待中断模式Waiting for Interrupt 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");

命令代码
在192….
/work/drivers_and_test/11th_ts/2th下 make

/work/drivers_and_test/11th_ts/2th下 cp s3c_ts.ko /work/nfs_root/first_fs
book@book-desktop:/work/drivers_and_test/11th_ts/2thcd/work/system/linux2.6.22.6/book@bookdesktop:/work/system/linux2.6.22.6 make menuconfig
-> Device Drivers
-> Input device support
-> Generic input layer
-> Touchscreens
<> S3C2410/S3C2440 touchscreens

make uImage
使用新内核启动

book@book-desktop:/work/system/linux-2.6.22.6$ cp arch/arm/boot/uImage /work/nfs_root/uImage_nots

在com下 reboot
q
nfs 30000000 192.168.31.105:/work/nfs_root/uImage_nots
bootm 30000000

mount -t nfs -o nolock,vers=2 192.168.31.105:/work/nfs_root/first_fs /mnt
cd /mnt/
因为这里是重新编译了内核,所以不需要rmmod s3c_ts.ko
insmod s3c_ts.ko

  1. insmod s3c_ts.ko
    按下/松开触摸笔

测试2th~7th:
1. ls /dev/event*
2. insmod s3c_ts.ko
3. ls /dev/event*
4. hexdump /dev/event0
秒 微秒 type code value
0000000 29a4 0000 8625 0008 0003 0000 0172 0000
0000010 29a4 0000 8631 0008 0003 0001 027c 0000
0000020 29a4 0000 8634 0008 0003 0018 0001 0000
0000030 29a4 0000 8638 0008 0001 014a 0001 0000
0000040 29a4 0000 863c 0008 0000 0000 0000 0000
0000050 29a4 0000 c85e 0008 0003 0000 0171 0000
0000060 29a4 0000 c874 0008 0003 0001 027d 0000
0000070 29a4 0000 c87b 0008 0000 0000 0000 0000
0000080 29a4 0000 ed37 0008 0003 0018 0000 0000
0000090 29a4 0000 ed48 0008 0001 014a 0000 0000
00000a0 29a4 0000 ed4a 0008 0000 0000 0000 0000

测8th
编译:
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

安装:
cd tmp
cp * -rf /nfsroot

使用:

先安装s3c_ts.ko, lcd.ko

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

module_raw input

改为:
module_raw input

2.
export TSLIB_TSDEVICE=/dev/event1
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

ts_test

cat /proc/mymsg

replay_r, replay_w

0x00075cf7 0x00000003 0x00000018 0

0x00076617 0x00000003 0x00000018 0
0x00076617 0x00000003 0x00000018 0
0x00076617 0x00000003 0x00000018 0
0x00076617 0x00000003 0x00000018 0
0x00076617 0x00000003 0x00000018 0
0x0007661b 0x00000003 0x00000018 0
0x0007661b 0x00000003 0x00000018 0
0x0007661b 0x00000003 0x00000018 0

原创粉丝点击