#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 {
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 s3c_ts_regs *s3c_ts_regs;
static struct timer_list ts_timer;
//等到触控笔按下模式
static void enter_wait_pen_down_mode(void)
{
s3c_ts_regs->adctsc = 0xd3;
}
//等到触控笔松开模式
static void enter_wait_pen_up_mode(void)
{
s3c_ts_regs->adctsc = 0x1d3;
}
//进入X/Y方向ADC同时转换模式
static void enter_measure_xy_mode(void)
{
s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}
//启动ADC转换
static void start_adc(void)
{
s3c_ts_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 (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)
{
if (s3c_ts_regs->adcdat0 & (1<<15))
{
//printk("pen up\n");
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("pen down\n");
//enter_wait_pen_up_mode();
enter_measure_xy_mode();
start_adc();
}
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;
// 优化措施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;
y[cnt] = adcdat1 & 0x3ff;
++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);
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;
// 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);//按压力度:1表示按下、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);
request_irq(IRQ_ADC, adc_irq, IRQF_SAMPLE_RANDOM, "adc", NULL);
// 优化措施1:
// 设置ADCDLY为最大值, 这使得电压稳定后再发出IRQ_TC中断
s3c_ts_regs->adcdly = 0xffff;
//优化措施5: 使用定时器处理长按,滑动的情况
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(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");
================================================================
解析:
加载驱动以后运行s3c_ts_init函数,程序进入等待触控笔按下模式enter_wait_pen_down_mode(),当有触控笔按下时进入按下中断服务函数中运行,即pen_down_up_irq中,进入中断服务函数后立即判断触控笔是否依然按下,如果这个时候触控笔已经松开则上报事件;若此时触控笔依然按下则进入X/Y双方向同时进行ADC转换模式,并启动ADC转换。当ADC转换完成以后进入ADC中断服务程序,从adcdat0、adcdat1获取到x方向、y方向的ADC数据,再判断触控笔是否离开,如果已经离开则进行上报数据;否则保存此次ADC转换数据再判断ADC采集到的数据有没有到4次,如果累计到4次则进行软件滤波后上报事件,进入等待触控笔离开模式enter_wait_pen_up_mode,同时启动定时器开始计时处理连续按压事件;如果不够4次则再次进入X/Y双方向同时进行ADC转换模式,并启动ADC转换。当定时时间到了以后进入定时中断服务函数里,判断触控笔是否离开,如果触控笔松开则上报事件,进入等待触控笔按下模式,否则再次进入X/Y双方向同时进行ADC转换模式,并启动ADC转换。依次!
测试2th~7th:
1. make menuconfig 去掉原来的触摸屏驱动程序
-> Device Drivers
-> Input device support
-> Generic input layer
-> Touchscreens
<> S3C2410/S3C2440 touchscreens
make uImage
使用新内核启动
2. 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
lcd和触摸屏联合使用参考”tslib编译使用方法“
//暂时忽略下面三行命令
//sudo apt-get install autoconf
//sudo apt-get install automake
//sudo apt-get install libtool
编译:
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目录
安装:
cd tmp
再把tmp目录下的4个文件全都拷贝到开发板的根目录下
cp * /home/book/workspace/JZ2440_TestFile/system/first_fs -rfd
(采用网络文件系统启动时开发板的根目录,但是此时并没有拷贝到开发板的flash上面,如果要拷贝到开发板的flash上面可以不用网络文件系统启动,采用手动挂载的方式把文件系统挂载到开发板的mnt目录下,在从mnt目录下把tmp里的文件拷贝到开发板的根目录下面,这样就算真正的在开发板的flash上面了。)
使用:
先安装s3c_ts.ko, lcd.ko
1.
修改 /etc/ts.conf第1行(去掉#号和第一个空格):
# module_raw input
改为:
module_raw input
2.设置环境变量
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
使用以下两个命令进行测试:
ts_calibrate
ts_test