1. 电阻式触摸屏的原理
触摸屏已经在现实生活中大量使用,比如银行的自动存/取款机等。触摸屏的种类有很多,比如超声波触摸屏、红外触摸屏、电容触摸屏、电阻触摸屏等。
电阻触摸屏由于造价低廉,在电气上可以直接接入用户的系统中而得到大量使用。电阻触摸屏有几种类型,比如“四线”,“五线”和“八线”。线越多,精度就越高,温度漂移也就越少,但基本的操作是一样的。它本质上是个电阻分压器,将矩形区域中触摸点(x,y)的物理位置转换为代表x坐标和y坐标的电压。
2. S3C2440触摸屏接口
S3C2440的触摸屏接口可以驱动四线电阻触摸屏,其接口结构如下:
其接四线电阻触摸屏的等效电路如下:
3. 等效原理图分析
触摸屏首先需要进入“等待中断模式”,去检测触摸屏被按下或被释放。
检测按下操作:此时只有S5 和S4 是被接通的,Y_ADC处为高电平,当触摸屏被按下时,X轴和Y轴就被接通,这样Y_ADC就会检测到高电压到低电压的转变,从而触发INC_TC中断。
检测触摸屏弹起是同样的道理:此时依然只有S5 和S4 是接通的,在按下后到释放前,Y_ADC处都为低电平,当释放后,X轴和Y轴分离,这样Y_ADC就会检测到低电平到高电平的转变,从而触发INC_TC中断。
那么怎么知道INC_TC中断是由触摸屏被按下触发的还是被释放触发的呢?这个在2440中非常简单,因为可以通过ADCDATX寄存器中UPDOWN位得到相应信息。同样可以通过设置ADCTSC寄存器的UD_SEN位来设定“等待中断模式”是去检测按下还是释放。
当触摸屏被按下后,我们就需要去得到相应的坐标信息。
检测X坐标:此时只有S1 和S3接地,这样X轴就有电压分布,因为按下的状态,X轴和Y轴是相通的,所以X_ADC就被接在触摸点处,自然就可以得到相应的分压,自然也就得到了相应的坐标(需要上层做转换)。
检测Y坐标:此时只有S2 和S4接地,这样Y轴就有电压分布,因为按下的状态,X轴和Y轴是相通的,所以Y_ADC就被接在触摸点处,自然就可以得到相应的分压。
S3C2440有“分离的X/Y轴坐标转换模式”和“自动X/Y轴坐标转换模式”两种转换模式,我们一般使用后者,这样就设置相应的寄存器后,硬件会自动完成相应的转换工作,并将结果写入ADCDATX寄存器中的前10位中(2440的ADC分辨率为10位),然后触发INC_ADC中断。
4. 源程序分析
-
#include <linux/input.h>
-
#include <linux/module.h>
-
#include <linux/init.h>
-
#include <linux/interrupt.h>
-
#include <linux/clk.h>
-
-
#include <asm/io.h>
-
#include <asm/delay.h>
-
#include <asm/arch/regs-adc.h>
-
#include <asm/irq.h>
-
#include <asm/arch/regs-irq.h>
-
-
-
//#define DEBUG_TS
-
-
#define TCMODNAME "ts_touch"
-
#define ADCMODNAME "ts_adc"
-
-
#define S3C2440_TS_ABS_X_MIN 0
-
#define S3C2440_TS_ABS_X_MAX 0x3ff
-
#define S3C2440_TS_ABS_Y_MIN 0
-
#define S3C2440_TS_ABS_Y_MAX 0x3ff
-
-
#define PEN_DOWN (0 << 8)
-
#define PEN_UP (1 << 8)
-
-
//进入等待中断模式,等待触摸屏被按下
-
#define WAIT_DOWN_INT() { \
-
writel(PEN_DOWN | \
-
S3C2410_ADCTSC_YM_SEN | \
-
S3C2410_ADCTSC_YP_SEN | \
-
S3C2410_ADCTSC_XP_SEN | \
-
S3C2410_ADCTSC_XY_PST(3), S3C2410_ADCTSC); \
-
}
-
-
//进入等待中断模式,等待触摸屏被按下
-
#define WAIT_UP_INT() { \
-
writel(PEN_UP | \
-
S3C2410_ADCTSC_YM_SEN | \
-
S3C2410_ADCTSC_YP_SEN | \
-
S3C2410_ADCTSC_XP_SEN | \
-
S3C2410_ADCTSC_XY_PST(3), S3C2410_ADCTSC); \
-
}
-
-
//使能ADC转换
-
#define ENABLE_START() { \
-
writel( \
-
readl(S3C2410_ADCCON) | \
-
S3C2410_ADCCON_ENABLE_START , \
-
S3C2410_ADCCON); \
-
}
-
-
//进入自动X/Y坐标转换模式
-
#define AUTO_PST_INT() { \
-
writel( \
-
S3C2410_ADCTSC_PULL_UP_DISABLE | \
-
S3C2410_ADCTSC_AUTO_PST | \
-
S3C2410_ADCTSC_XY_PST(0), S3C2410_ADCTSC); \
-
ENABLE_START(); \
-
}
-
-
//定义每个触摸点采样的次数
-
#define CVR_CNT 10
-
-
struct s3c24xx_ts_dev {
-
struct input_dev *dev;
-
struct clk *clk;
-
unsigned int cnt; //for adc
-
int px[CVR_CNT];
-
int py[CVR_CNT];
-
unsigned int oflag; //for ts
-
};
-
static struct s3c24xx_ts_dev *ts_devp;
-
-
//定义一个延时队列,将坐标相关工作移到中断下半部完成
-
static void do_adc_convert(struct work_struct *work);
-
static DECLARE_DELAYED_WORK(ts_work, do_adc_convert);
-
-
-
//检测是否是按下模式
-
static unsigned int get_down_status(void)
-
{
-
return !((readl(S3C2410_ADCDAT0) & S3C2410_ADCDAT0_UPDOWN) >> 15) &&
-
!((readl(S3C2410_ADCDAT1) & S3C2410_ADCDAT1_UPDOWN) >> 15); //通过读取ADCDATX的第15位,等到相关信息
-
}
-
-
//计算平均值,并将最小和最大的两个数据丢弃
-
static int get_truth_value(int *dat, unsigned int cnt)
-
{
-
int tmp_min, tmp_max, tmp_val, tmp_sum = 0;
-
unsigned int i;
-
-
tmp_min = dat[0];
-
tmp_max = dat[0];
-
-
for (i = 0; i < cnt-1; i++) {
-
if(tmp_min > dat[i])
-
tmp_min = dat[i];
-
if(tmp_max < dat[i])
-
tmp_max = dat[i];
-
tmp_sum += dat[i];
-
}
-
-
tmp_val = (tmp_sum - tmp_min - tmp_max) / (cnt - 2);
-
-
return tmp_val;
-
}
-
-
//获取ADC转换数据,进行累加,平均,之后提交输入事件。
-
static void do_adc_convert(struct work_struct *work)
-
{
-
int absx = 0, absy = 0;
-
-
#ifdef DEBUG_TS
-
printk("[do_adc_convert] start!\n");
-
#endif
-
-
if (get_down_status()) { //按下状态,读取X轴和Y轴数据。
-
absy = (readl(S3C2410_ADCDAT0) & S3C2410_ADCDAT0_XPDATA_MASK); //发现对应的X和Y轴颠倒,故修正
-
absx = (readl(S3C2410_ADCDAT1) & S3C2410_ADCDAT1_YPDATA_MASK);
-
-
ts_devp->px[ts_devp->cnt] = absx; //累加相应坐标的数据,并累加次数
-
ts_devp->py[ts_devp->cnt] = absy;
-
ts_devp->cnt++;
-
-
if (ts_devp->cnt >= CVR_CNT) { //当转换的次数达到预设的值,则调用get_truth_value()求均值
-
-
absx = get_truth_value(ts_devp->px, ts_devp->cnt);
-
absy = get_truth_value(ts_devp->py, ts_devp->cnt);
-
ts_devp->cnt = 0;
-
-
input_report_key(ts_devp->dev, BTN_TOUCH, 1); //向输入核心层提交相关数据
-
input_report_abs(ts_devp->dev, ABS_X, absx);
-
input_report_abs(ts_devp->dev, ABS_Y, absy);
-
input_sync(ts_devp->dev);
-
-
#ifdef DEBUG_TS
-
printk("absx is %3d, absy is %3d \n\n", absx, absy);
-
#endif
-
-
WAIT_UP_INT(); //进入等待释放模式
-
schedule_delayed_work(&ts_work, 10); //进行延时调用,如果是长按或是在滑动,还是会进入自动转换模式,这个比较关键,否则无法得到滑动的坐标点
-
#ifdef DEBUG_TS
-
printk("WAIT_UP_INT ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));
-
#endif
-
} else { //如果转换次数不够,则继续采样
-
AUTO_PST_INT(); //必须再次设置自动转换并使能ADC,否则S3C2410_ADCDAT0的值有问题
-
}
-
}
-
}
-
-
//初始化相应的ADC寄存器,设置分频,并进入等待按下模式
-
static int init_s3c24xx_adcreg(void)
-
{
-
unsigned long reg_tmp;
-
-
reg_tmp = S3C2410_ADCCON_PRSCEN | // prescaler enable
-
S3C2410_ADCCON_PRSCVL(19); // PCLK=50MHz, AD_CLK<=2.5MHz, AD_CLK = PCLK / (PRSCVL + 1)
-
writel(reg_tmp, S3C2410_ADCCON);
-
#ifdef DEBUG_TS
-
printk("the ADCCON is 0x%x \n", readl(S3C2410_ADCCON));
-
#endif
-
-
writel(5000, S3C2410_ADCDLY);
-
-
WAIT_DOWN_INT(); //自动等待模式(按下)
-
#ifdef DEBUG_TS
-
printk("the ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));
-
#endif
-
-
}
-
-
//发生INC_TC中断时,会进入此函数
-
static irqreturn_t ts_touch_interrupt(int irq, void *dev_id)
-
{
-
-
#ifdef DEBUG_TS
-
printk("[ts_touch_interrupt] start!\n");
-
#endif
-
-
#if 0
-
if (get_down_status() && !ts_devp->oflag) {
-
#else
-
if (get_down_status()) {
-
#endif
-
// disable_irq_nosync(irq);
-
// ts_devp->oflag = 1;
-
AUTO_PST_INT();
-
-
#ifdef DEBUG_TS
-
printk("touch down \n\n");
-
#endif
-
} else {
-
input_report_key(ts_devp->dev, BTN_TOUCH, 0);
-
input_sync(ts_devp->dev);
-
-
#ifdef DEBUG_TS
-
printk("[ts_touch_interrupt] touch up \n\n\n\n");
-
#endif
-
-
WAIT_DOWN_INT();
-
// ts_devp->oflag = 0;
-
#ifdef DEBUG_TS
-
printk("WAIT_DOWN_INT ADCTSC is 0x%x \n", readl(S3C2410_ADCTSC));
-
#endif
-
}
-
return IRQ_HANDLED;
-
}
-
-
//发生INC_ADC中断时,会进入此函数
-
static irqreturn_t ts_adc_interrupt(int irq, void *dev_id)
-
{
-
#ifdef DEBUG_TS
-
printk("[ts_adc_interrupt] start!\n");
-
#endif
-
-
schedule_delayed_work(&ts_work, 0); //立即启动ts_work队列,调用do_adc_convert()
-
-
return IRQ_HANDLED;
-
}
-
-
-
/*
-
* The functions for inserting/removing us as a module.
-
*/
-
-
static int __init s3c24xx_ts_init(void)
-
{
-
int err;
-
-
ts_devp = kzalloc(sizeof(struct s3c24xx_ts_dev), GFP_KERNEL);
-
if (!ts_devp)
-
return -ENOMEM;
-
-
ts_devp->clk = clk_get(NULL, "adc");
-
clk_enable(ts_devp->clk);
-
-
init_s3c24xx_adcreg();
-
-
ts_devp->dev = input_allocate_device();
-
if (!ts_devp->dev)
-
return -ENOMEM;
-
-
ts_devp->dev->name = "s3c24xx_touchscreen";
-
ts_devp->dev->phys = "s3c24xx/input0";
-
ts_devp->dev->id.bustype = BUS_RS232;
-
ts_devp->dev->id.vendor = 0xdead;
-
ts_devp->dev->id.product = 0xbeef;
-
ts_devp->dev->id.version = 0x0101;
-
-
ts_devp->dev->evbit[0] = BIT(EV_ABS) | BIT(EV_KEY);
-
ts_devp->dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
-
-
input_set_abs_params(ts_devp->dev, ABS_X,
-
S3C2440_TS_ABS_X_MIN, S3C2440_TS_ABS_X_MAX, 0, 0);
-
input_set_abs_params(ts_devp->dev, ABS_Y,
-
S3C2440_TS_ABS_Y_MIN, S3C2440_TS_ABS_Y_MAX, 0, 0);
-
//input_set_abs_params(s3c24xx_ts_dev, ABS_PRESSURE,
-
// 0, 1, 0, 0);
-
-
if (request_irq(IRQ_TC, ts_touch_interrupt ,
-
IRQF_DISABLED, TCMODNAME, NULL) < 0) {
-
printk(KERN_ERR "s3c24xx_ts_input.c: Can't allocate irq %d\n",
-
IRQ_TC);
-
err = -EBUSY;
-
goto fail1;
-
}
-
-
if (request_irq(IRQ_ADC, ts_adc_interrupt,
-
IRQF_DISABLED, ADCMODNAME, NULL) < 0) {
-
printk(KERN_ERR "s3c24xx_ts_input.c: Can't allocate irq %d\n",
-
IRQ_ADC);
-
err = -EBUSY;
-
goto fail2;
-
}
-
-
err = input_register_device(ts_devp->dev);
-
if (err)
-
goto fail3;
-
-
return 0;
-
-
fail3:
-
free_irq(IRQ_ADC, NULL);
-
-
fail2:
-
free_irq(IRQ_TC, NULL);
-
cancel_delayed_work(&ts_work);
-
flush_scheduled_work();
-
-
fail1:
-
input_free_device(ts_devp->dev);
-
return err;
-
}
-
-
static void __exit s3c24xx_ts_exit(void)
-
{
-
input_unregister_device(ts_devp->dev);
-
-
free_irq(IRQ_ADC, NULL);
-
free_irq(IRQ_TC, NULL);
-
-
cancel_delayed_work(&ts_work);
-
flush_scheduled_work();
-
-
input_free_device(ts_devp->dev);
-
-
kfree(ts_devp);
-
}
-
-
module_init(s3c24xx_ts_init);
-
module_exit(s3c24xx_ts_exit);
5. 触摸的方式思考
触摸的状态有几种? 两种,按下和释放?? 这应该是大部分人的第一反应。我在初期移植的时候也是这种思维,所以走了很多弯路。
其实应该分为:“短按”,“长按”,“滑动”,“释放”,这样才能更好的完成人际互动,实现例如绘图、手写等操作。
那么怎么样才能正确检测这些操作呢?最好的办法就是当触摸按下后,在没有弹起来之前就按照一定的频率去采样X/Y轴的坐标,并提交上去,这样上层应用就可以等到这些信息,然后判断是什么样的操作。
阅读(1106) | 评论(0) | 转发(0) |