资讯详情

TQ2440四线电阻式触摸屏驱动程序的分析

三,TQ2440触摸屏驱动程序源代码分析(因为我的代码注释已经详细了,所以不分析一个函数):

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define S3C2410TSVERSION 0x0101

#define WAIT4INT(x) (((x)<<8) | \

S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \

S3C2410_ADCTSC_XY_PST(3))

#define AUTOPST (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN | \

S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0))

static char *tq2440ts_name = "TQ2440 TouchScreen";

static struct input_dev *dev;

static long xp;

static long yp;

static int count;

extern struct semaphore ADC_LOCK; //定义信号量ADC_LOCK

static int OwnADC = 0;

static void __iomem *base_addr; ////用于保存映射后的虚拟地址

/*touch_timer_fire函数的工作如下:

* 读出触摸屏是按下还是抬起

* 如果触摸屏被按下,A/D转换报告事件和数据

* 如果触摸屏被按下,但是A/D转换在开始之前就开始了A/D转换

* 如果触摸屏被抬起,首先报告事件,然后重新进入等待中断模式并释放触摸屏占用ADC资源

*/

static void touch_timer_fire(unsigned long data)

{

unsigned long data0;

unsigned long data1;

int updown;

//读取A/D转换后的值

data0 = ioread32(base_addr S3C2410_ADCDAT0);

data1 = ioread32(base_addr S3C2410_ADCDAT1);

//S3C2410_ADCDAT0_UPDOWN =1000000000000000,updown1时表示触摸屏被按下,updown0时表示触摸屏未按下

updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

//如果按下触摸屏,并且A/D转换已完成,报告事件和数据

if (updown) {

if (count != 0)

{

long tmp;

tmp = xp;

xp = yp;

yp = tmp;

//由于A/D转换时对X和Y坐标采样4次,这里X和Y坐标取四次采样值的平均值(坐标除以4)

xp >>= 2;

yp >>= 2;

//报告X,Y坐标数据

input_report_abs(dev, ABS_X, xp);

input_report_abs(dev, ABS_Y, yp);

//报告按键事件,1表示按下触摸点

input_report_key(dev, BTN_TOUCH, 1);

//报告触摸屏状态

input_report_abs(dev, ABS_PRESSURE, 1);

//完成报告

input_sync(dev);

}

//如果按下触摸屏,但是A/D转换在开始之前就开始了A/D转换

xp = 0;

yp = 0;

count = 0;

////自动设置触摸屏X/Y轴坐标转换模式

iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr S3C2410_ADCTSC);

//启动A/D转换

iowrite32(ioread32(base_addr S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr S3C2410_ADCCON);

}

else //如果触摸屏被抬起

{

count = 0;

//报告按键事件,0表示触摸点被抬起

input_report_key(dev, BTN_TOUCH, 0);

//报告触摸屏状态,0表示触摸屏被抬起

input_report_abs(dev, ABS_PRESSURE, 0);

//完成报告

input_sync(dev);

///将触摸屏重新进入等待按下的中断模式 ADCTSC = 011010011

iowrite32(WAIT4INT(0), base_addr S3C2410_ADCTSC);

if (OwnADC) ///触摸屏被抬起,不应占用ADC应释放资源

{

OwnADC = 0;

up(&ADC_LOCK); ////释放触摸屏占用的触摸屏ADC资源

}

}

}

//定义并初始化定时器touch_timer

static struct timer_list touch_timer =

TIMER_INITIALIZER(touch_timer_fire, 0, 0);

/* TC中断,当触摸屏被按下或松开时,该函数的主要工作如下:

* 获得ADC资源,因为在ADC也用于驱动ADC资源

* 判断触摸屏是被按下还是被抬起,并对这两种状态做出相应的处理

*/

static irqreturn_t stylus_updown(int irq, void *dev_id)

{

unsigned long data0;

unsigned long data1;

int updown;///按下或抬起触摸屏的标志

//尝试获得信号量ADC_LOCK,若down_trylock返回0意味着信号量成功,触摸屏可以使用ADC否则不能使用资源ADC资源

if (down_trylock(&ADC_LOCK) == 0)

{

OwnADC = 1; //表示触摸屏ADC资源可用

//读取A/D转换后的值

data0 = ioread32(base_addr S3C2410_ADCDAT0);

data1 = ioread32(base_addr S3C2410_ADCDAT1);

//S3C2410_ADCDAT0_UPDOWN =1000000000000000,updown1时表示触摸屏被按下,updown0时表示触摸屏未按下

updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT0_UPDOWN));

if (updown) //如果按下触摸屏,调用touch_timer_fire函数启动A/D转换

{

touch_timer_fire(0);

}

else //如果触摸屏松开,则释放触摸屏占用的触摸屏ADC资源

{

OwnADC = 0;

up(&ADC_LOCK);//释放ADC_LOCK信号量

}

}

return IRQ_HANDLED;

}

* 负责取得X和Y的坐标值

*/

static irqreturn_t stylus_action(int irq, void *dev_id)

{

unsigned long data0;

unsigned long data1;

if (OwnADC) //标示触摸屏资源可用

{

//读取ADCDAT0寄存器(读出X轴坐标转换数据值)

data0 = ioread32(base_addr+S3C2410_ADCDAT0);

//读取ADCDAT1寄存器(读出Y轴坐标转换数据值)

data1 = ioread32(base_addr+S3C2410_ADCDAT1);

//取得X坐标的值,即读取ADCDAT0寄存器0-9位的值,S3C2410_ADCDAT0_XPDATA_MASK = 0000001111111111

xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;

//取得Y坐标的值,即读取ADCDAT1寄存器0-9位的值,S3C2410_ADCDAT1_YPDATA_MASK = 0000001111111111

yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;

//记录这一次A/D转换的次数

count++;

if (count < (1<<2)) //如果转换次数小于4次

{

//设置触摸屏为自动X/Y轴坐标转换模式

iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);

//重新启动A/D转换

iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);

}

else

{

//否则,启动1个时间滴答的定时器,这就会去执行touch_timer_fire定时器超时函数的上报事件���数据

mod_timer(&touch_timer, jiffies+1);

//停止ADC转换,防止屏幕抖动

iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC);

}

}

return IRQ_HANDLED;

}

static struct clk *adc_clock;

/*设备初始化函数,该函数主要做的工作如下:

* 获得ADC时钟以及使能ADC时钟

* 映射ADC的IO内存的地址

* 设置ADCCON寄存器使能预分频,设置ADCDLY寄存器(采样的延时值)

* 设置ADCTSC寄存器进入等待中断模式(等待触摸屏被按下)

* 设置输入设备支持的事件

* 申请ADC,TC中断

* 注册输入设备

*/

static int __init tq2440ts_init(void)

{

int ret1,ret2;

struct input_dev *input_dev; //定义输入设备

adc_clock = clk_get(NULL, "adc"); //获得外设adc的时钟

if (!adc_clock) //如果adc_clock为空

{

printk(KERN_ERR "failed to get adc clock source\n");

return -ENOENT;

}

clk_enable(adc_clock); //使能adc时钟

//映射ADC的I/O内存地址,S3C2410_PA_ADC是ADC控制器的基地址(0x58000000),0x20是虚拟地址长度大小

base_addr=ioremap(S3C2410_PA_ADC,0x20);

if (base_addr == NULL) //如果映射ADC的I/O内存地址失败

{

printk(KERN_ERR "Failed to remap register block\n");

return -ENOMEM;

}

//设置ADCCON寄存器使能预分频,预分频系数选择255,ADCCON =0111111111000000

iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),base_addr+S3C2410_ADCCON);

//ADCDLY= 1111111111111111,采样的延迟值

iowrite32(0xffff,  base_addr+S3C2410_ADCDLY);

//进入等待按下的中断模式  ADCTSC = 011010011

iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC);

//分配一个input_dev结构体

input_dev = input_allocate_device();

if (!input_dev)

{

printk(KERN_ERR "Unable to allocate the input device !!\n");

return -ENOMEM;

}

//初始化输入设备

dev = input_dev;

//支持同步事件,按键事件,绝对位移事件

dev->evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);

//set_bit(EV_SYN,dev->evbit);

//set_bit(EV_KEY,dev->evbit);

//set_bit(EV_ABS,dev->evbit);

//支持的按键类型为触摸屏点击

dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH);

//set_bit(BTN_TOUCH,dev->keybit);

//设置触摸屏的x坐标,y坐标,压力

input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0);

input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0);

input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);

//在驱动挂载后在/proc/bus/input/devices中能看到的输入设备的信息

dev->name = tq2440ts_name;

dev->id.bustype = BUS_RS232;

dev->id.vendor = 0xDEAD;

dev->id.product = 0xBEEF;

dev->id.version = S3C2410TSVERSION;

//申请ADC中断,X,Y坐标的AD转换完成时即产生此中断

ret1 =request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM, tq2440ts_name, dev);

if (ret1) //如果申请AD中断失败

{

printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");

iounmap(base_addr); //解除ADC控制器的内存地址映射

return -EIO;

}

//申请TC中断,触摸屏被按下或弹起时即产生此中断

ret2 =request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM, tq2440ts_name, dev);

if (ret2) //如果申请TC中断失败

{

printk(KERN_ERR "tq2440_ts.c: Could not allocate ts IRQ_ADC !\n");

iounmap(base_addr);//解除ADC控制器的内存地址映射

return -EIO;

}

printk(KERN_INFO "%s successfully loaded\n", tq2440ts_name);

input_register_device(dev);//注册输入设备

return 0;

}

/*设备退出函数,该函数主要做的工作如下:

* 关闭并释放ADC和TC中断

* 关闭并注销ADC时钟

* 注销输入设备

* 解除ADC控制器的内存空间的映射

*/

static void __exit tq2440ts_exit(void)

{

disable_irq(IRQ_ADC);//关闭ADC中断

disable_irq(IRQ_TC); //关闭TC中断

free_irq(IRQ_TC,dev); //释放TC中断

free_irq(IRQ_ADC,dev); //释放ADC中断

if (adc_clock)

{

clk_disable(adc_clock); //关闭时钟ADC

clk_put(adc_clock); //注销ADC时钟

adc_clock = NULL;

}

input_unregister_device(dev);//注销输入设备

iounmap(base_addr); //解除ADC控制器的内存映射

}

module_init(tq2440ts_init);

module_exit(tq2440ts_exit);

MODULE_LICENSE("GPL");

四,触摸屏驱动的移植

关于触摸屏驱动程序的移植请看我前面写的一篇文章《TQ2440触摸屏驱动程序的移植》 ,至此有关TQ2440触摸屏驱动程序的编写就告一段落了。

0b1331709591d260c1c78e86d0c51c18.png

标签: tc05电阻

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台