二、实现功能:获取点击触摸屏的坐标,并在移动的过程中也能动态的显示出触点坐标。同时通过一些测试程序来校准触摸屏等(测试程序非自己撰写)。
三、实验原理:
fs2410采用电阻触摸屏,用ADC的通道5、7分别获取触点的x、y绝对坐标值。
当点击触摸屏时,会产生TS中断,在TS的中断处理程序中需要开启AD转换,获取坐标值。AD转换需要一定的时间,为了节省CPU的资源,转换完成后采用中断的方式,通知CPU AD转换已完成。
为了能在移动过程中动态的显示出坐标,需要不断的进行AD转换,本例中采用定时器机制,在定时器超时后若还处于点击状态,则继续进行AD转换,获取坐标值,若已松开,则不转换。如此循环,就能获取动态坐标值了,这样就可以在屏幕上划线了。
四、实验现象:
1、加载模块后,点击触摸屏,可以显示出坐标值;
2、运行测试程序ts_callibrate,LCD屏幕会出现十字光标,分表点击LCD屏幕四个角和中间就可以讲触摸屏和LCD屏进行校准;
3、运行测试程序ts_print_raw,会显示出坐标原始数据,现象与1类似;
4、运行测试程序ts_test,然后点击屏幕上的draw,就可以在屏幕上划线了。
五、实验总结:
由于AD转换存在一定的误差,所有在驱动代码中需要通过软件的方法对转换结果进行优化处理,如:
1、点击后延时一会,让电压值稳定后再进行AD转换;
2、在进行AD转换时,若这时触摸笔已松开,则丢弃此次AD转换结果;
3、多次测量,再去平均值;
4、过过一些软件过滤方法。
六、示例代码:
/*驱动代码s3c_ts.c*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
struct s3c_adc_regs {
unsigned long adccon;
unsigned long adctsc;
unsigned long adcdly;
unsigned long adcdat0;
unsigned long adcdat1;
};
static struct input_dev *s3c_ts;
static struct s3c_adc_regs *s3c_adc_regs;
static volatile unsigned long *clkcon;
static volatile unsigned long *gpgcon;
static struct timer_list s3c_ts_timer;
static void enter_wait_for_pen_down_mode(void)
{
s3c_adc_regs->adctsc = (1<<7) | (1<<6) | (0<<5) | (1<<4) | (0<<3) | (3);
}
static void enter_wait_for_pen_up_mode(void)
{
s3c_adc_regs->adctsc = (1<<8) | (1<<7) | (1<<6) | (0<<5) | (1<<4) | (0<<3) | (3);
}
static void enter_auto_xy_mode(void)
{
s3c_adc_regs->adctsc = (1<<7) | (1<<6) | (1<<4) | (1<<3) | (1<<2);
}
static void start_adc(void)
{
s3c_adc_regs->adccon |= 1;
}
static void s3c_ts_timer_function(unsigned long data)
{
if (s3c_adc_regs->adcdat0 & (1<<15))
{
input_report_key(s3c_ts, BTN_TOUCH, 0);
input_report_abs(s3c_ts, ABS_PRESSURE, 0);
input_sync(s3c_ts);
enter_wait_for_pen_down_mode();
return;
}
else
{
enter_auto_xy_mode();
start_adc();
}
}
static irqreturn_t s3c_ts_pen_down_up_isr(int irq, void *dev_id)
{
if (s3c_adc_regs->adcdat0 & (1<<15))
{
/* 松开 */
input_report_key(s3c_ts, BTN_TOUCH, 0);
input_report_abs(s3c_ts, ABS_PRESSURE, 0);
input_sync(s3c_ts);
enter_wait_for_pen_down_mode();
}
else
{
enter_auto_xy_mode();
start_adc();
}
return IRQ_HANDLED;
}
static int s3c_ts_filtering(int *px, int *py)
{
int avr_x, avr_y;
int dx, dy;
#define FILTER_LIMIT 10
avr_x = (px[0] + px[1]) / 2;
avr_y = (py[0] + py[1]) / 2;
dx = (px[2] > avr_x) ? (px[2] - avr_x) : (avr_x - px[2]);
dy = (py[2] > avr_y) ? (py[2] - avr_y) : (avr_y - py[2]);
if ((dx > FILTER_LIMIT) || (dy > FILTER_LIMIT))
return 0;
avr_x = (px[1] + px[2]) / 2;
avr_y = (py[1] + py[2]) / 2;
dx = (px[3] > avr_x) ? (px[3] - avr_x) : (avr_x - px[3]);
dy = (py[3] > avr_y) ? (py[3] - avr_y) : (avr_y - py[3]);
if ((dx > FILTER_LIMIT) || (dy > FILTER_LIMIT))
return 0;
return 1;
}
static irqreturn_t s3c_ts_adc_isr(int irq, void *dev_id)
{
static int x[4] = {0, 0, 0, 0}, y[4] = {0, 0, 0, 0}, cnt = 0;
int data0, data1;
data0 = s3c_adc_regs->adcdat0;
data1 = s3c_adc_regs->adcdat1;
/* 优化措施2: 如果这个时候触摸笔已经松开,此次结果丢弃 */
if (data0 & (1<<15))
{
enter_wait_for_pen_down_mode();
cnt = 0;
x[0] = x[1] = x[2] = x[3] = 0;
y[0] = y[1] = y[2] = y[3] = 0;
input_report_key(s3c_ts, BTN_TOUCH, 0);
input_report_abs(s3c_ts, ABS_PRESSURE, 0);
input_sync(s3c_ts);
return IRQ_HANDLED;
}
else
{
/* 优化措施3: 多次测量求平均值 */
x[cnt] = data0 & 0x3ff;
y[cnt] = data1 & 0x3ff;
cnt++;
if (cnt == 4)
{
/* 优化措施4: 软件过滤 */
if (s3c_ts_filtering(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, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
input_report_abs(s3c_ts, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
input_report_key(s3c_ts, BTN_TOUCH, 1);
input_report_abs(s3c_ts, ABS_PRESSURE, 1);
input_sync(s3c_ts);
}
enter_wait_for_pen_up_mode();
cnt = 0;
x[0] = x[1] = x[2] = x[3] = 0;
y[0] = y[1] = y[2] = y[3] = 0;
/* 启动定时器 */
mod_timer(&s3c_ts_timer, jiffies + HZ/100);
}
else
{
enter_auto_xy_mode();
start_adc();
}
}
return IRQ_HANDLED;
}
static int s3c_ts_init(void)
{
s3c_ts = input_allocate_device();
__set_bit(EV_KEY, s3c_ts->evbit);
__set_bit(EV_ABS, s3c_ts->evbit);
__set_bit(BTN_TOUCH, s3c_ts->keybit);
input_set_abs_params(s3c_ts, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(s3c_ts, ABS_PRESSURE, 0, 1, 0, 0);
input_register_device(s3c_ts);
clkcon = ioremap(0x4C00000C, 4);
*clkcon |= (1<<15);
iounmap(clkcon);
gpgcon = ioremap(0x56000060, 4);
*gpgcon |= 0xff000000;
iounmap(gpgcon);
s3c_adc_regs = ioremap(0x58000000, sizeof(struct s3c_adc_regs));
request_irq(IRQ_TC, s3c_ts_pen_down_up_isr, IRQF_SAMPLE_RANDOM, "ts_pen", NULL);
request_irq(IRQ_ADC, s3c_ts_adc_isr, IRQF_SAMPLE_RANDOM, "ts_adc", NULL);
s3c_adc_regs->adccon = (1<<14) | (49 << 6);
/* 优化措施1: 按下触摸屏后稍等一会再发出中断,为了让电压稳定 */
s3c_adc_regs->adcdly = 0xffff;
init_timer(&s3c_ts_timer);
s3c_ts_timer.function = s3c_ts_timer_function;
add_timer(&s3c_ts_timer);
/* 进入"Waiting for Interrupt Mode" */
enter_wait_for_pen_down_mode();
return 0;
}
static void s3c_ts_exit(void)
{
free_irq(IRQ_TC, NULL);
free_irq(IRQ_ADC, NULL);
iounmap(s3c_adc_regs);
input_unregister_device(s3c_ts);
input_free_device(s3c_ts);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");