驱动程序之_1_字符设备_12_触摸屏驱动
触摸屏在内核中有现成的input子系统框架,input_handler层位于ts_dev.c,只需要编写input_dev层与之融合即可,编写方法与lcd驱动程序类似
一、在入口函数
1、分配input_dev结构体
s3c_ts_dev = input_allocate_device();
2、设置input_dev结构体
3、注册input_dev结构体
4、硬件初始化
①、使能时钟
struct clk *adc_clk;
adc_clk = clk_get(0, "adc");
clk_enable(adc_clk);
②、IO映射,触摸屏、ADC控制器配置
③、初始化定时器
④、申请中断
二、在出口函数
1、硬件相关
①、注销中断
②、删除定时器
③、取消IO映射
④、关闭时钟
2、注销input_dev结构体
3、销毁input_dev结构体
三、编写功能函数
1、触摸屏中断服务程序
①、判断按下还是松开
②、按下时,开启定时器;松开时上报事件
2、ADC中断服务程序
①、判断是否仍按下
②、已松开,上报事件
③、仍按下,取出ADC转换数值处理
④、按下的情况下,若满足条件(例程中条件是已算出多次数据的平均值,并且每次数据相差不是特别大),上报事件
3、定时器中断服务程序
①、判断是否仍按下
②、已松开,上报事件
③、仍按下,开启一次ADC转换
4、其他子程序
测试方法:
一、将例程中上报事件的相关代码换成串口打印输出
加载驱动后,按下屏幕即可从串口看到输出信息
二、使用例程驱动,配合lcd驱动和tslib库测试
1、安装tslib库,并移动到开发板上
①、解压
tar xzf tslib-1.4.tar.gz
②、进入tslib目录,配置
cd tslib
./autogen.sh
③、编译、安装,其中tmp是临时目录(tslib临时安装到该目录,nfsroot是开发板根目录)
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
cd tmp
cp * -rf /nfsroot
2、配置tslib库
vi /etc/ts.conf
修改配置文件
# module_raw input
为
module_raw input
配置,其中
event0 是加载触摸屏驱动时增加的event设备(可能不是0,根据实际情况修改)
fb0 是加载lcd驱动时增加的fb设备(可能不是0,根据实际情况修改)
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
3、按照上篇中的方法,重新编译内核(反选触摸屏驱动),用新内核启动
4、加载lcd驱动、触摸屏驱动
5、使用tslib库测试
①、校准
ts_calibrate
触摸屏上依次按下准心,完成屏幕校准
②、测试
ts_test
点击drag,可以拖动十字准心移动
点击draw,可以在屏幕上画图写字
同时串口会输出相关信息
附上程序代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/fb.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/wait.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <linux/input.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/div64.h>
#include <linux/clk.h>
#include <asm/mach/map.h>
#include <asm/arch/regs-lcd.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/fb.h>
#define MEASURE_TIMES 4
struct s3c_ts_regs{
volatile unsigned long adccon;
volatile unsigned long adctsc;
volatile unsigned long adcdly;
volatile unsigned long adcdat0;
volatile unsigned long adcdat1;
volatile unsigned long adcupdn;
};
static struct input_dev *s3c_ts_dev;
static volatile struct s3c_ts_regs *s3c_ts_regs;
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;
}
static void enter_measure_mode(void)
{
s3c_ts_regs->adctsc = (1<<3) | (1<<2);
}
static void adc_start(void)
{
s3c_ts_regs->adccon |= 1;
}
static irqreturn_t pen_down_up_irq(int irq, void *dev_id)
{
if(s3c_ts_regs->adcdat0 & (1<<15))
{
enter_wait_pen_down_mode();
input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);
input_report_key(s3c_ts_dev,BTN_TOUCH,0);
input_sync(s3c_ts_dev);
}
else
{
mod_timer(&s3c_ts_dev->timer,jiffies + HZ/100);
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static int filter_xy(int x[], int y[], int num)
{
#define ERR_MAX 10
int tmp_x, tmp_y;
int del_x, del_y;
int i;
for(i = 0;i < num-1;i+=2)
{
tmp_x = x[i] + x[i+1];
tmp_x /= 2;
tmp_y = y[i] + y[i+1];
tmp_y /= 2;
if(i == (num-2))
{
del_x = tmp_x > x[0] ? tmp_x-x[0] : x[0]-tmp_x;
del_y = tmp_y > y[0] ? tmp_y-y[0] : y[0]-tmp_y;
}else
{
del_x = tmp_x > x[i+2] ? tmp_x-x[i+2] : x[i+2]-tmp_x;
del_y = tmp_y > y[i+2] ? tmp_y-y[i+2] : y[i+2]-tmp_y;
}
if((del_x > ERR_MAX) || (del_y > ERR_MAX))
return -1;
}
return 0;
}
static irqreturn_t ts_adc_irq(int irq ,void *dev_id)
{
static int adcdat0 ,adcdat1;
static int x[MEASURE_TIMES], y[MEASURE_TIMES];
int x_avg = 0, y_avg = 0;
static int i = 0;
adcdat0 = s3c_ts_regs->adcdat0;
adcdat1 = s3c_ts_regs->adcdat1;
if(s3c_ts_regs->adcdat0 & (1<<15))
{
i = 0;
enter_wait_pen_down_mode();
input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);
input_report_key(s3c_ts_dev,BTN_TOUCH,0);
input_sync(s3c_ts_dev);
}
else
{
x[i] = adcdat0 & 0x3ff;
y[i] = adcdat1 & 0x3ff;
i++;
if(i == MEASURE_TIMES)
{
if(!filter_xy(x,y,MEASURE_TIMES))
{
for(i = 0;i < MEASURE_TIMES;i++)
{
x_avg += x[i];
y_avg += y[i];
}
x_avg /= MEASURE_TIMES;
y_avg /= MEASURE_TIMES;
// printk("average : x=%d,y=%d\r\n",x_avg,y_avg);
input_report_abs(s3c_ts_dev,ABS_X,x_avg);
// input_event(s3c_ts_dev,EV_ABS,ABS_X,x_avg);
input_report_abs(s3c_ts_dev,ABS_Y,y_avg);
// input_event(s3c_ts_dev,EV_ABS,ABS_Y,y_avg);
input_report_abs(s3c_ts_dev,ABS_PRESSURE,1);
// input_event(s3c_ts_dev,EV_ABS,ABS_PRESSURE,1);
input_report_key(s3c_ts_dev,BTN_TOUCH,1);
// input_event(s3c_ts_dev,EV_KEY,BTN_TOUCH,1);
input_sync(s3c_ts_dev);
}
i = 0;
enter_wait_pen_up_mode();
mod_timer(&s3c_ts_dev->timer,jiffies + HZ/100);
}
else
{
enter_measure_mode();
adc_start();
}
}
return IRQ_RETVAL(IRQ_HANDLED);
}
static void s3c_ts_timer_func(unsigned long data)
{
if(s3c_ts_regs->adcdat0 & (1<<15))
{
enter_wait_pen_down_mode();
input_report_abs(s3c_ts_dev,ABS_PRESSURE,0);
input_report_key(s3c_ts_dev,BTN_TOUCH,0);
input_sync(s3c_ts_dev);
}
else
{
enter_measure_mode();
adc_start();
}
}
static int s3c_ts_init(void)
{
struct clk *adc_clk;
int error;
s3c_ts_dev = input_allocate_device();
strcmp(s3c_ts_dev->name,"s3c_ts");
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);
input_set_abs_params(s3c_ts_dev, ABS_X,0, 0x3ff, 0, 0);
input_set_abs_params(s3c_ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
input_register_device(s3c_ts_dev);
adc_clk = clk_get(0, "adc");
clk_enable(adc_clk);
s3c_ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs));
s3c_ts_regs->adccon = (1<<14) | (49<<6);
s3c_ts_regs->adcdly = 0xffff;
init_timer(&s3c_ts_dev->timer);
s3c_ts_dev->timer.function = s3c_ts_timer_func;
add_timer(&s3c_ts_dev->timer);
error = request_irq(IRQ_TC, pen_down_up_irq, IRQF_SAMPLE_RANDOM, "s3c_ts_pen_irq" ,0);
error = request_irq(IRQ_ADC, ts_adc_irq, IRQF_SAMPLE_RANDOM, "s3c_ts_adc_irq" ,0);
enter_wait_pen_down_mode();
return 0;
}
static void s3c_ts_exit(void)
{
struct clk *adc_clk;
free_irq(IRQ_TC, 0);
del_timer(&s3c_ts_dev->timer);
iounmap(s3c_ts_regs);
clk_get("adc");
clk_disable();
input_unregister_device(s3c_ts_dev);
input_free_device(s3c_ts_dev);
}
module_init(s3c_ts_init);
module_exit(s3c_ts_exit);
MODULE_LICENSE("GPL");