转载自:MiniGUI 开源版本专区
我用的是远峰的2440的开发板,配7”的TTF显示屏和触摸屏,分辨率为800X480。在MiniGUI下面使用触摸屏,光标出现小范围的抖动,在离开触摸屏的瞬间,采样的数值出现突变,光标跑飞了,也就是常说的野点,影响了点击的效果,经常出现点击无效的问题。
触摸屏按下一个点时,采样的数值并不是固定的,通常都只是在一个范围之内不断的变动,也就是T1时刻与T2时刻采值的数值是有变化的。在MiniGUI里的表现就是光标不断的抖动。一般需要在驱动程序里进行滤波。
滤波的方法有很多。其中在嵌入式资讯网里看到一遍文章《触摸屏在S3C2410上的应用实例》,里面介绍了滤波与消除野点的方法。但我在实际应用的平台上实施的效果并不理想。主要是触摸屏的灵敏度大大降低了(为原来的1/27)。而且去抖动的效果也不好,光标还是不断抖动。
一般来说,触摸屏要求的精度并不太高,于是,可以把一定范围内的坐标变动进行滤波,也就是当光标的变化不超过一定的范围,可以认为触摸的位置没有发生改变。
坐标滤波,考虑人机界面中对触摸屏的操作有3种:
*触摸笔在触摸屏上的位置不变;
*触摸笔在触摸屏上连续滑过;
*触摸笔在触摸屏上有大幅度的跳跃。
假设三次连续采样时刻为T1、T2、T3(T3>T2>T1),采样间隔为10ms。由于采样间隔远小于人的反应时间,所以在前两种操作模式下,如果采样点有效,将T1和T3时刻的采样值作平均。其平均值和T2时刻的采样值比较一般不会大于某个门限,否则判定此次采样点为野点。而对于第三种模式下,采样点数据会有很大的跳变。跳变过程中的数据是不稳定的,虽然记入了数据,但被判定成无效的采样点,所以需要在程序中定义一个静态数组x[2]记录相邻的两次采样数据。只有当前后数据持续稳定一段时间,才认为这时的采样点有效。程序中使用的间隔门限FILTER_LIMIT是需要经过试验来选取的。
修改后的2440驱动程序如下:
/*
* s3c2440-ts.c
*
* touchScreen driver for SAMSUNG MOBILE
*
* Author: Kwanghyun La <nala.la@samsung.com>
* Date : $Date: 2004/07/14 10:38:33 $
*
* Recension: Guanshangming <6681193@163.com>
* Date : $Date: 2006/6/23
*
* $Revision: 1.6 $
*
* Based on pt036001b-ts.c
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive
* for more details.
*
* History:
*
* 2003-09-18 : kwanghyun La <nala.la@samsung.com>
* - modify about bus clk freq & touch screen
* 2006-6-23 : Guanshangming <6681193@163.com
*- add coordinate filter
*/
#i nclude <linux/config.h>
#i nclude <linux/module.h>
#i nclude <linux/kernel.h>
#i nclude <linux/init.h>
#i nclude <linux/miscdevice.h>
#i nclude <linux/sched.h>
#i nclude <linux/delay.h>
#i nclude <linux/poll.h>
#i nclude <linux/spinlock.h>
#i nclude <asm/irq.h>
#i nclude <asm/arch/clocks.h>
#define ADC_FREQ2000000// 2MHz AD convert freq
#define FILTER_LIMIT 5
static int PreScale_n;// PCLK / (PreScale_n+1) = ADConversion freq.
typedef struct {
unsigned short pressure;
unsigned short x;
unsigned short y;
unsigned short pad;
} TS_RET;
typedef struct {
int xscale;
int xtrans;
int yscale;
int ytrans;
int xyswap;
} TS_CAL;
#i nclude <asm/hardware.h>
#ifdef CONFIG_PM
#i nclude <linux/pm.h>
#endif
#if 0
/* debug macros */
#ifdef DEBUG
#define DBGMSG(format, args...)printk("[%s : %s : L%d]\n" format, __FILE__, __FUNCTION__, __LINE__, ##args)
#else
#define DBGMSG(format, args...)printk("[%s] " format, __FUNCTION__, ##args)
#endif
#endif
#define DBGMSG(format, args...)
#define PEN_UP 0
#define PEN_DOWN1
#define PEN_FLEETING2
#define MAX_TS_BUF16/* how many do we want to buffer */
#undef USE_ASYNC//1
#define DEVICE_NAME"s3c2440-ts"
#define TSRAW_MINOR1
typedef struct {
unsigned int penStatus;/* PEN_UP, PEN_DOWN, PEN_SAMPLE */
TS_RET buf[MAX_TS_BUF];/* protect against overrun */
unsigned int head, tail;/* head and tail for queued events */
wait_queue_head_t wq;
spinlock_t lock;
#ifdef USE_ASYNC
struct fasync_struct *aq;
#endif
#ifdef CONFIG_PM
struct pm_dev *pm_dev;
#endif
} TS_DEV;
static TS_DEV tsdev;
#define BUF_HEAD(tsdev.buf[tsdev.head])
#define BUF_TAIL(tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) ((++(x)) & ((mod) - 1))
static int tsMajor = 0;
static void (*tsEvent) (void);
#define HOOK_FOR_DRAG
#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY (HZ/100)/* 10 ms */
static struct timer_list ts_timer;
#endif
#define wait_down_int(){ ADCTSC = DOWN_INT | XP_PULL_UP_EN | \
XP_AIN | XM_HIZ | YP_AIN | YM_GND | \
XP_PST(WAIT_INT_MODE); }
#define wait_up_int(){ ADCTSC = UP_INT | XP_PULL_UP_EN | XP_AIN | XM_HIZ | \
YP_AIN | YM_GND | XP_PST(WAIT_INT_MODE); }
#define mode_x_axis(){ ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
XP_PULL_UP_DIS | XP_PST(X_AXIS_MODE); }
#define mode_x_axis_n(){ ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
XP_PULL_UP_DIS | XP_PST(NOP_MODE); }
#define mode_y_axis(){ ADCTSC = XP_AIN | XM_HIZ | YP_EXTVLT | YM_GND | \
XP_PULL_UP_DIS | XP_PST(Y_AXIS_MODE); }
#define STOP_ADC(){ ADCCON &= (0xffff & (1<<2));}
#define START_ADC(){ ADCCON |= (1<<0)|(0<<2); }
/*
* 66MHz/(32+1) = 2MHz
* 1/(2MHz/5cycle) = 2.5uS
*/
#define start_adc_x(n){ ADCCON = PRESCALE_EN | PRSCVL(n) | \
ADC_INPUT(ADC_IN7) | ADC_START_BY_RD_EN | \
ADC_NORMAL_MODE; \
ADCDAT0; }
#define start_adc_y(n){ ADCCON = PRESCALE_EN | PRSCVL(n) | \
ADC_INPUT(ADC_IN5) | ADC_START_BY_RD_EN | \
ADC_NORMAL_MODE; \
ADCDAT1; }
#define disable_ts_adc(){ ADCCON &= ~(ADCCON_READ_START); }
static int adc_state = 0;
static int x, y;/* touch screen coorinates */
static int ptx[3],pty[3];
static int sampidx;
#define V16C6448AC
//#define LQ039Q2DS54
//#define PDE35
#ifndef BOOL
#define BOOL int
#define TRUE 1
#define FALSE 0
#endif
BOOL Touch_Pen_filtering(void)
{
static int his_x[2],his_y[2];
static int count=0;
BOOL retVal;
count++;
// if in effect sampling > 2 start filter
if(count>2){
int TmpX,TmpY,dx,dy;
count=2;
TmpX=(his_x[0]+x)/2;
TmpY=(his_y[0]+y)/2;
dx=(his_x[1]>TmpX)?(his_x[1]-TmpX):(TmpX-his_x[1]);
dy=(his_y[1]>TmpY)?(his_y[1]-TmpY):(TmpY-his_y[1]);
if(dx>FILTER_LIMIT || dy>FILTER_LIMIT){
x=his_x[1];
y=his_y[1];
retVal=FALSE;
count=0;
}
else{
his_x[0]=his_x[1];
his_y[0]=his_y[1];
his_x[1]=x;
his_y[1]=y;
retVal=TRUE;
}
}
else{
his_x[0]=his_x[1];
his_y[0]=his_y[1];
his_x[1]=x;
his_y[1]=y;
retVal=FALSE;
}
return retVal;
}
static BOOL tsEvent_down(void)
{
static int lastx,lasty;
ptx[0] = ptx[1];
ptx[1] = ptx[2];
ptx[2] = x;
pty[0] = pty[1];
pty[1] = pty[2];
pty[2] = y;
if((sampidx++)>2)
{
int TmpX,TmpY;
int dx,dy;
TmpX = (ptx[0]+ptx[1]+ptx[2])/3;
TmpY = (pty[0]+pty[1]+pty[2])/3;
dx = TmpX>lastx?(TmpX-lastx):(lastx-TmpX);
dy = TmpY>lasty?(TmpY-lasty):(lasty-TmpY);
if(dx>12 || dy>10) {
lastx = TmpX;
lasty = TmpY;
x = TmpX;
y = TmpY;
}
else {
x = lastx;
y = lasty;
}
return Touch_Pen_filtering();
}
return FALSE;
}
static void tsEvent_raw(void)
{
BOOL bSuccess = TRUE;
if(tsdev.penStatus == PEN_DOWN) {
bSuccess = tsEvent_down();
if(bSuccess)
{
#ifdef LQ039Q2DS54
BUF_HEAD.x = x;
BUF_HEAD.y = y;//softmcu
#endif
#ifdef V16C6448AC
BUF_HEAD.x = y;//x;
BUF_HEAD.y = x;//y;//softmcu
#endif
#ifdef PDE35
BUF_HEAD.x = y;//x;
BUF_HEAD.y = x;//y;//softmcu
#endif
BUF_HEAD.pressure = PEN_DOWN;
DBGMSG("PEN DOWN: x: %08d, y: %08d\n", x, y);
}
#ifdef HOOK_FOR_DRAG
ts_timer.expires = jiffies + TS_TIMER_DELAY;
add_timer(&ts_timer);
#endif
} else {
#ifdef HOOK_FOR_DRAG
del_timer(&ts_timer);
#endif
sampidx = 0;
BUF_HEAD.x = 0;
BUF_HEAD.y = 0;
BUF_HEAD.pressure = PEN_UP;
}
if(bSuccess)
{
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
wake_up_interruptible(&(tsdev.wq));
}
#ifdef USE_ASYNC
if(tsdev.aq)
kill_fasync(&(tsdev.aq), SIGIO, POLL_IN);
#endif
#ifdef CONFIG_PM
pm_access(tsdev.pm_dev);
#endif
}
static int tsRead(TS_RET * ts_ret)
{
spin_lock_irq(&(tsdev.lock));
ts_ret->x = BUF_TAIL.x;
ts_ret->y = BUF_TAIL.y;
ts_ret->pressure = BUF_TAIL.pressure;
tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);
spin_unlock_irq(&(tsdev.lock));
return sizeof(TS_RET);
}
static ssize_t
elfin_ts_read(struct file *filp, char *buffer, size_t count, loff_t * ppos)
{
TS_RET ts_ret;
retry:
if(tsdev.head != tsdev.tail) {
int count;
count = tsRead(&ts_ret);
if(count)
copy_to_user(buffer, (char *)&ts_ret, count);
return count;
} else {
if(filp->f_flags & O_NONBLOCK)
return -EAGAIN;
interruptible_sleep_on(&(tsdev.wq));
if(signal_pending(current))
return -ERESTARTSYS;
goto retry;
}
return sizeof(TS_RET);
}
#ifdef USE_ASYNC
static int elfin_ts_fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &(tsdev.aq));
}
#endif
static unsigned int
elfin_ts_poll(struct file *filp, struct poll_table_struct *wait)
{
poll_wait(filp, &(tsdev.wq), wait);
return (tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM);
}
static inline void start_ts_adc(void)
{
adc_state = 0;
mode_x_axis();
start_adc_x(PreScale_n);// modify for ADC freq
}
static inline void elfin_get_XY(void)
{
if(adc_state == 0) {
adc_state = 1;
disable_ts_adc();
y = (ADCDAT0 & 0x3ff);
mode_y_axis();
start_adc_y(PreScale_n);// modify for ADC freq
} else if(adc_state == 1) {
adc_state = 0;
disable_ts_adc();
x = (ADCDAT1 & 0x3ff);
tsdev.penStatus = PEN_DOWN;
//DBGMSG("PEN DOWN: x: %08d, y: %08d\n", x, y);
wait_up_int();
tsEvent();
}
}
static void elfin_isr_adc(int irq, void *dev_id, struct pt_regs *reg)
{
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_UP) {
elfin_get_XY();
}
#ifdef HOOK_FOR_DRAG
else {
elfin_get_XY();
}
#endif
spin_unlock_irq(&(tsdev.lock));
}
static void elfin_isr_tc(int irq, void *dev_id, struct pt_regs *reg)
{
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_UP) {
start_ts_adc();
} else {
tsdev.penStatus = PEN_UP;
DBGMSG("PEN UP : x: %08d, y: %08d\n", x, y);
wait_down_int();
tsEvent();
}
spin_unlock_irq(&(tsdev.lock));
}
#ifdef HOOK_FOR_DRAG
static void ts_timer_handler(unsigned long data)
{
spin_lock_irq(&(tsdev.lock));
if(tsdev.penStatus == PEN_DOWN) {
start_ts_adc();
}
spin_unlock_irq(&(tsdev.lock));
}
#endif
static int elfin_ts_open(struct inode *inode, struct file *filp)
{
tsdev.head = tsdev.tail = 0;
tsdev.penStatus = PEN_UP;
#ifdef HOOK_FOR_DRAG
init_timer(&ts_timer);
ts_timer.function = ts_timer_handler;
#endif
tsEvent = tsEvent_raw;
init_waitqueue_head(&(tsdev.wq));
MOD_INC_USE_COUNT;
return 0;
}
static int elfin_ts_release(struct inode *inode, struct file *filp)
{
#ifdef HOOK_FOR_DRAG
del_timer(&ts_timer);
#endif
MOD_DEC_USE_COUNT;
return 0;
}
static struct file_operations elfin_fops = {
owner:THIS_MODULE,
open:elfin_ts_open,
read:elfin_ts_read,
release:elfin_ts_release,
#ifdef USE_ASYNC
fasync:elfin_ts_fasync,
#endif
poll:elfin_ts_poll,
};
void tsEvent_dummy(void)
{
}
#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_ts_dir, devfs_tsraw;
#endif
#ifdef CONFIG_PM
static int
elfin_ts_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
{
switch (req) {
case PM_SUSPEND:
STOP_ADC();
break;
case PM_RESUME:
START_ADC();
break;
}
return 0;
}
#endif
static int __init elfin_ts_init(void)
{
int ret;
tsEvent = tsEvent_dummy;
ADCDLY = 20000;
//append for AD converting freq calculate
ret = elfin_get_bus_clk(GET_PCLK);
PreScale_n = (int)(ret / ADC_FREQ) - 1;
ret = register_chrdev(0, DEVICE_NAME, &elfin_fops);
if(ret < 0) {
printk(DEVICE_NAME " can't get major number\n");
return ret;
}
tsMajor = ret;
/* Enable touch interrupt */
ret = request_irq(IRQ_ADC_DONE, elfin_isr_adc, SA_INTERRUPT,
DEVICE_NAME, elfin_isr_adc);
if(ret) {
goto adc_failed;
}
ret = request_irq(IRQ_TC, elfin_isr_tc, SA_INTERRUPT,
DEVICE_NAME, elfin_isr_tc);
if(ret) {
goto tc_failed;
}
/* Wait for touch screen interrupts */
wait_down_int();
#ifdef CONFIG_DEVFS_FS
devfs_ts_dir = devfs_mk_dir(NULL, "touchscreen", NULL);
devfs_tsraw = devfs_register(devfs_ts_dir, "0raw", DEVFS_FL_DEFAULT,
tsMajor, TSRAW_MINOR,
S_IFCHR | S_IRUSR | S_IWUSR,
&elfin_fops, NULL);
#endif
#ifdef CONFIG_PM
pm_register(PM_DEBUG_DEV, PM_USER_INPUT, elfin_ts_pm_callback);
#endif
printk(DEVICE_NAME " initialized\n");
return 0;
tc_failed:
free_irq(IRQ_ADC_DONE, elfin_isr_adc);
adc_failed:
return ret;
}
static void __exit elfin_ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_tsraw);
devfs_unregister(devfs_ts_dir);
#endif
unregister_chrdev(tsMajor, DEVICE_NAME);
#ifdef CONFIG_PM
pm_unregister(tsdev.pm_dev);
#endif
free_irq(IRQ_ADC_DONE, elfin_isr_adc);
free_irq(IRQ_TC, elfin_isr_tc);
}
module_init(elfin_ts_init);
module_exit(elfin_ts_exit);