触摸屏代码分析——实现滑动功能

前面提到过,我们可以通过定时器来实现滑动的功能。我们再来啰嗦一遍原理:设置定时器,在定时器处理函数中开启AD转换,可以得到坐标的实时值。先来看代码:

#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 adc_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 adc_regs *adc_regs;
static struct timer_list ts_timer;

static void enter_wait_pen_down_mode(void)
{
adc_regs->adctsc = 0xd3;
}

static void enter_wait_pen_up_mode(void)
{
adc_regs->adctsc = 0x1d3;
}

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

static void start_adc(void)
{
     adc_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 (adc_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
{
enter_measure_xy_mode();
start_adc();
}
}

static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{
if (adc_regs->adcdat0 & (1<<15))
{
printk("pen up\n");
enter_wait_pen_down_mode();
}
else
{
            enter_measure_xy_mode(); //进入自动转换模式
            start_adc(); //开启AD转换
}
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;
adcdat0 =adc_regs->adcdat0;
adcdat1 = adc_regs->adcdat1;
    
    if(adc_regs->adcdat0 & (1<<15))
    {
        input_report_abs(s3c_ts_dev, ABS_PRESSURE, 0); //如果按键松开则上报事件,0代表松开
 input_report_key(s3c_ts_dev, BTN_TOUCH, 0);
 input_sync(s3c_ts_dev);
        enter_wait_pen_down_mode();
    }
    else
        {
           x[cnt] = adcdat0 & 0x3ff;
  y[cnt] = adcdat1 & 0x3ff;
                cnt++;
                if(cnt==4)
                    {
                        if(s3c_filter_ts(x, y))
                            {
                                  input_report_abs(s3c_ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4); //上报x坐标
       input_report_abs(s3c_ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4); //上报y坐标
       input_report_abs(s3c_ts_dev, ABS_PRESSURE, 1); //上报按下事件,1表示按下
       input_report_key(s3c_ts_dev, BTN_TOUCH, 1);
       input_sync(s3c_ts_dev);
                            }
   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;
    s3c_ts_dev = input_allocate_device(); //分配input_dev 结构体
  
    set_bit(EV_KEY,s3c_ts_dev->evbit); //可以产生按键类事件
    set_bit(EV_ABS,s3c_ts_dev->evbit); //可以产生绝对位移事件
    
    set_bit(BTN_TOUCH,s3c_ts_dev->keybit); //可以产生按键类事件里的触摸屏事件
    input_set_abs_params(s3c_ts_dev, ABS_X, 0, 0x3FF, 0, 0); //可以产生绝对位移事件里的x位置事件
    input_set_abs_params(s3c_ts_dev, ABS_Y, 0, 0x3FF, 0, 0); //可以产生绝对位移事件里的y位置事件
    input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0); //可以产生绝对位移事件里的按下/松开位置事件

    input_register_device(s3c_ts_dev); //注册

    clk=clk_get(NULL, "adc"); //获取时钟
    clk_enable(clk); //使能时钟

    adc_regs=ioremap(0x58000000,sizeof(struct adc_regs)); //映射寄存器
    adc_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); //注册AD转换完成中断

    adc_regs->adcdly = 0xffff; //设置延时

     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(adc_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");

在这个函数里面我们新加了两个功能:一是加入了定时器功能,使其支持滑动。二是改打印坐标为上报事件。至此触摸屏函数完成了。我们再来总体概括一下它的工作流程,以方便程序的阅读:
按下触摸屏产生中断,进入按下中断处理函数里,在这个按下中断处理函数里我们开启了AD转换,一旦转换完成,就会发生AD转换完成中断,在这个AD转换完成中断处理函数中上报坐标值,并且要修改定时器的值。一旦定时器到时的话,就会进入定时器处理函数,在这个函数里,会重新启动AD转换,从而进入AD转换中断处理函数里,上报坐标值。这样即使不再次按下,也会不断进行AD转换,不断上报,从而得到实时的按下的坐标值,从而支持了滑动。

测试:
(1)从新编译内核:make
(2)从新编译模块:make modules,一开始没有从新编译模块,直接用之前的cfbcopyarea.ko、cfbfillrect.ko、cfbimgblt.ko是会出现段错误的
(3)将c fbcopyarea.ko、 cfbfillrect.ko、 cfbimgblt.ko拷贝到开发板可以挂载的目录里,新内核启动开发板
(4) 编译:
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目录下
(5)安装:
cd tmp
cp * -rf /nfsroot   //在开发板上将tmp目录下的所有文件都拷贝到开发板的根目录下
(6)使用:
insmod touchscreen.ko
insmod  c fbcopyarea.ko
insmod  cfbfillrect.ko
insmod  cfbimgblt.ko
insmod lcd.ko    //如果出现段错误,可以从新编译内核和模块
(7)
修改 /etc/ts.conf第1行(去掉#号和第一个空格):
# module_raw input
改为:
module_raw input
(8)
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
(9)正式的测试:
ts_calibrate
ts_test
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值