在linux内核文件中,自带一个s3c2410_ts.c文件,实现了触摸屏的功能。现在参考该文件重新写一个驱动文件。
在init函数中,
1.硬件相关配置:
使能ADC时钟
clk = clk_get(NULL, "adc");
clk_enable(clk);
其中clk_get函数在/arm/plat-s3cxx/s3c2410-clock.c中定义
static struct clk init_clocks_disable[] = {
{
.name = "nand",
.id = -1,
.parent = &clk_h,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_NAND,
}, {
.name = "sdi",
.id = -1,
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_SDI,
}, {
.name = "adc",
.id = -1,
.parent = &clk_p,
.enable = s3c2410_clkcon_enable,
.ctrlbit = S3C2410_CLKCON_ADC,
.......
进一步追踪,可以看到函数:
int s3c2410_clkcon_enable(struct clk *clk, int enable)
{
unsigned int clocks = clk->ctrlbit;
unsigned long clkcon;
clkcon = __raw_readl(S3C2410_CLKCON);
if (enable)
clkcon |= clocks;
else
clkcon &= ~clocks;
/* ensure none of the special function bits set */
clkcon &= ~(S3C2410_CLKCON_IDLE|S3C2410_CLKCON_POWER);
__raw_writel(clkcon, S3C2410_CLKCON);
return 0;
}
整个函数无非就是配置2440的CLKCON寄存器。在该配置中配置ADC(&Touch Screen) [15]这一位。
之后分配一个input_dev结构,这一系列过程可以参考前面的输入子系统,类似。
然后注册一个TC和ADC中断。
最后就是在中断中进行数据处理了。
代码如下:
#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 <linux/gpio.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <plat/regs-adc.h>
#include <mach/regs-gpio.h>
#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \
S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))
static struct input_dev *ts_dev;
static struct timer_list ts_timer;
static struct clk *adc_clock;
struct s3c_ts_regs {
unsigned long adccon;
unsigned long adctsc;
unsigned long adcdly;
unsigned long adcdat0;
unsigned long adcdat1;
unsigned long adcupdn;
};
static volatile struct s3c_ts_regs *ts_regs;
static void ts_wait_down_mode(void)
{
ts_regs->adctsc = 0xd3;
}
static void ts_wait_up_mode(void)
{
ts_regs->adctsc = 0x1d3;
}
static void ts_measure_xy_mode(void)
{
ts_regs->adctsc =(1<<2)|(1<<3); //S3C2410_ADCTSC_PULL_UP_DISABLE|/*(1<<3)*/AUTOPST;
}
static void ts_start_ad(void)
{
ts_regs->adccon |= 1;
}
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 irqreturn_t ts_adc_handler(int irq, void *dev_id)
{
int adcdat0,adcdat1;
static int cnt=0;
static int x[4],y[4];
adcdat0 = ts_regs->adcdat0;
adcdat1 = ts_regs->adcdat1;
/* if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃
{
printk("ts_adc_handler TS_up \n");
cnt = 0;
input_report_abs(ts_dev, ABS_PRESSURE, 0);
input_report_key(ts_dev, BTN_TOUCH, 0);
input_sync(ts_dev);
ts_wait_down_mode();
}
else*/
{
//printk("TS_down \n");
x[cnt] = adcdat0& 0x3ff;
y[cnt] = adcdat1& 0x3ff;
cnt++;
if(cnt >= 4)
{
//printk("TS_down \n");
if(s3c_filter_ts(x,y))
{
//printk("adcdat0=%d,adcdat1=%d\n",(x[0]+x[1]+x[2]+x[3])>>2,\
// (y[0]+y[1]+y[2]+y[3])>>2 );
input_report_abs(ts_dev, ABS_X, (x[0]+x[1]+x[2]+x[3])/4);
input_report_abs(ts_dev, ABS_Y, (y[0]+y[1]+y[2]+y[3])/4);
input_report_abs(ts_dev, ABS_PRESSURE, 1);
input_report_key(ts_dev, BTN_TOUCH, 1);
input_sync(ts_dev);
}
cnt=0;
ts_wait_up_mode();
/* 启动定时器处理长按/滑动的情况 */
mod_timer(&ts_timer, jiffies + HZ/100);
}
else
{
ts_measure_xy_mode();
ts_start_ad();
}
}
return IRQ_HANDLED;
}
static irqreturn_t ts_tc_handler(int irq, void *dev_id)
{
if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15))
{
//printk("ts_tc_handler TS_up \n");
input_report_abs(ts_dev, ABS_PRESSURE, 0);
input_report_key(ts_dev, BTN_TOUCH, 0);
input_sync(ts_dev);
ts_wait_down_mode();
}
else
{
//down mode
//printk("TS_down \n");
ts_measure_xy_mode();
ts_start_ad();
}
return IRQ_HANDLED;
}
static void ts_timer_handler(unsigned long dat)
{
if(ts_regs->adcdat0 & (1<<15)||ts_regs->adcdat1 & (1<<15))
{
/* 已经松开 */
input_report_abs(ts_dev, ABS_PRESSURE, 0);
input_report_key(ts_dev, BTN_TOUCH, 0);
input_sync(ts_dev);
ts_wait_down_mode();
}
else
{
//down mode
//printk("TS_down \n");
ts_measure_xy_mode();
ts_start_ad();
}
return;
}
static int mini2440_ts_init(void)
{
int error;
/*1.分配一个input_dev结构*/
ts_dev = input_allocate_device();
if (!ts_dev)
{
printk("input_allocate failed!\n");
error = -ENOMEM;
return error;
}
/*2.设置 */
/* 2.1 设置能产生哪类事件 */
set_bit(EV_SYN, ts_dev->evbit);
set_bit(EV_ABS, ts_dev->evbit);
set_bit(EV_KEY, ts_dev->evbit);
/*2.2 设置能产生这类操作的哪些事件 */
set_bit(BTN_TOUCH, ts_dev->keybit);
input_set_abs_params(ts_dev, ABS_X, 0, 0x3FF, 0, 0);
input_set_abs_params(ts_dev, ABS_Y, 0, 0x3FF, 0, 0);
input_set_abs_params(ts_dev, ABS_PRESSURE, 0, 1, 0, 0);
error = input_register_device(ts_dev);
if (error)
{
printk("input_register_device failed!\n");
error = -ENOMEM;
return error;
}
/*3.硬件相关配置*/
adc_clock = clk_get(NULL, "adc");
if (!adc_clock) {
printk(KERN_ERR "failed to get adc clock source\n");
return -ENOENT;
}
clk_enable(adc_clock);
ts_regs = ioremap(0x58000000,sizeof(struct s3c_ts_regs));
ts_regs->adccon = (1<<14)|(0xff<<6);
ts_regs->adcdly = 0xffff;
ts_wait_down_mode();
/*3.1初始化定时器*/
init_timer(&ts_timer);
ts_timer.function = ts_timer_handler;
add_timer(&ts_timer);
/*3.2中断配置*/
error = request_irq(IRQ_ADC, ts_adc_handler, IRQF_SAMPLE_RANDOM,
"ts_adc", NULL);
if (error)
{
printk("request_irq failed!\n");
error = -ENOMEM;
return error;
}
error = request_irq(IRQ_TC, ts_tc_handler, IRQF_SAMPLE_RANDOM,
"mini2440_ts", NULL);
if (error)
{
printk("IRQ_TC_irq failed!\n");
error = -ENOMEM;
return error;
}
//ts_wait_down_mode();
return 0;
}
static void mini2440_ts_exit(void)
{
free_irq(IRQ_ADC,NULL);
free_irq(IRQ_TC,NULL);
del_timer(&ts_timer);
//input_unregister_device(ts_regs);
input_unregister_device(ts_dev);
input_free_device(ts_dev);
}
/* 这两行指定驱动程序的初始化函数和卸载函数 */
module_init(mini2440_ts_init);
module_exit(mini2440_ts_exit);
/* 描述驱动程序的一些信息 */
MODULE_LICENSE("GPL");
整个代码基本参考韦东山视频来写,在测试过程中发现在ADC采集过程中,已经up,数据丢弃,这样有些问题,不知mini2440的触摸屏什么问题,屏幕上方一小块区域内,在点击过程中就UP了,只好把那段滤波代码注释掉
static irqreturn_t ts_adc_handler(int irq, void *dev_id)
{
int adcdat0,adcdat1;
static int cnt=0;
static int x[4],y[4];
adcdat0 = ts_regs->adcdat0;
adcdat1 = ts_regs->adcdat1;
/* if(adcdat0 & (1<<15)) //如果在采集过程中,已经up,数据丢弃
{
printk("ts_adc_handler TS_up \n");
cnt = 0;
input_report_abs(ts_dev, ABS_PRESSURE, 0);
input_report_key(ts_dev, BTN_TOUCH, 0);
input_sync(ts_dev);
ts_wait_down_mode();
}
else*/
最后经测试整个区域,触摸都可用