JZ2440开发板学习------中级(二十四:下)

自己写驱动程序之触摸屏

终于到了触摸屏咯!如果开始呢?

先把能运行的程序附上去:

#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 <plat/ts.h>
#include <plat/regs-adc.h>


#include <mach/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;
}


static void enter_measure_xy_mode(void)
{
s3c_ts_regs->adctsc = (1<<3)|(1<<2);
}


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);




/* 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");

Makefile:

KERN_DIR = /home/dianyuxiao/linux-3.4.2
all:
make -C $(KERN_DIR) M=`pwd` modules 


clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order


obj-m += s3c_ts.o

测试结果如下:

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





换种方法测试如下:

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


安装:
cd tmp
cp * -rf /nfsroot    //  /nfsroot可根据实际情况来定












1.执行./autogen.sh


提示错误一:


./autogen.sh: 4: autoreconf: not found


解决方法:sudo apt-get install autoconf automake libtool


2.执行./configure --host=arm-linux --prefix=/usr/local/tslib


提示错误二:


ts_test.o(.text+0x218): In function `main':


: undefined reference to `rpl_malloc'


fbutils.o(.text+0x234): In function `open_framebuffer':


: undefined reference to `rpl_malloc'


collect2: ld returned 1 exit status


make[2]: *** [ts_test] Error 1


make[2]: Leaving directory `/home/gfpeak/Desktop/tslib/tests'


make[1]: *** [all-recursive] Error 1


make[1]: Leaving directory `/home/gfpeak/Desktop/tslib'


make: *** [all] Error 2


解决方法是在编译的时候 ./configure 后面的参数没有设置对,正确设置如下:


./configure --prefix=/usr/local/tslib/ --host=arm-linux ac_cv_func_malloc_0_nonnull=yes

























其他方法的使用:


先安装s3c_ts.ko, lcd.ko     // lcd.ko是之前编译好的LCD驱动,如果后面编译s3c_ts时改过配置,直接装载之前编译好的lcd.ko可能会出现段错误,重新编译一下lcd驱动就可以了。


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

再看看手册,毕竟是硬件手册很重要:















还是先搜索相似代码吧,在搜索相似代码之前,先把Input事件驱动程序大框写好:

/* 1. 分配一个input_dev结构体 */

/* 2. 设置 */
/* 2.1 能产生哪类事件 */

/* 2.2 能产生这类操作里的哪些事件*/

/* 3. 注册 */

/* 4. 硬件相关的操作 */

好啦!开始触摸屏之旅吧!搜索:在SI库栏中搜索:S3C2410,之后就会看到S3C2410_ts.c文件,打开:                                                                    

/******************************************************************************************************************************************************

S3C2410_ts.c

******************************************************************************************************************************************************/

struct s3c2410ts {
struct s3c_adc_client *client;
struct device *dev;
struct input_dev *input;
struct clk *clock;
void __iomem *io;
unsigned long xp;
unsigned long yp;
int irq_tc;
int count;
int shift;
int features;
};
static struct s3c2410ts ts;


static int __devinit s3c2410ts_probe(struct platform_device *pdev)
{
......

 ......
struct input_dev *input_dev;
......

 ts.clock = clk_get(dev, "adc");
clk_enable(ts.clock);
......
input_dev = input_allocate_device();
......
ts.input = input_dev;
ts.input->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
ts.input->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
input_set_abs_params(ts.input, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts.input, ABS_Y, 0, 0x3FF, 0, 0);
        ......
ret = request_irq(ts.irq_tc, stylus_irq, 0, "s3c2410_ts_pen", ts.input);

 ......
return 0;
}

static inline bool get_down(unsigned long data0, unsigned long data1)
{
/* returns true if both data values show stylus down */
return (!(data0 & S3C2410_ADCDAT0_UPDOWN) &&
!(data1 & S3C2410_ADCDAT0_UPDOWN));
}


static void touch_timer_fire(unsigned long data)
{
unsigned long data0;
unsigned long data1;
bool down;


data0 = readl(ts.io + S3C2410_ADCDAT0);
data1 = readl(ts.io + S3C2410_ADCDAT1);


down = get_down(data0, data1);


if (down) {
if (ts.count == (1 << ts.shift)) {
......
input_report_abs(ts.input, ABS_X, ts.xp);
input_report_abs(ts.input, ABS_Y, ts.yp);
input_report_key(ts.input, BTN_TOUCH, 1);
input_sync(ts.input);


ts.xp = 0;
ts.yp = 0;
ts.count = 0;
}


s3c_adc_start(ts.client, 0, 1 << ts.shift);
} else {
ts.xp = 0;
ts.yp = 0;
ts.count = 0;


input_report_key(ts.input, BTN_TOUCH, 0);
input_sync(ts.input);

}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值