您的位置:首页 > 运维架构 > Linux

基于linux的mini2440触摸屏驱动分析(yz版)

2014-04-22 14:41 495 查看
驱动程序的大致框架分析:

s3c2410ts_init 进行初始化,申请中断,注册输入设备工作,s3c2410ts_exit 注销设备,注销中断,释放资源。按下触摸屏后,会触发TC中断,进入TC中断处理函数stylus_updown。stylus_updown中调用touch_timer_fire启动AD转换。转换结束会触发ADC中断,进入ADC中断处理函数stylus_action。stylus_action中,若转换次数未达4次,会再次启动AD转换,否则启动1个时间滴答的内核定时器,调用touch_timer_fire进行数据上报。接着若触摸屏仍处于被按下的状态,会再次启动AD转换,转换4次后继续进行数据上报。直至触摸屏被释放,AD转换才会停止。

#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 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 *s3c2410ts_name = "s3c2410 TouchScreen";

static struct input_dev *dev;

static long xp;

static long yp;

static int count;

extern struct semaphore ADC_LOCK;

static int OwnADC = 0;

static void __iomem *base_addr;

/*read already************************************************************************/

/* the effect of s3c2410_gpio_cfgpin is configuring a pin to a function */

static inline void s3c2410_ts_connect(void)

{

s3c2410_gpio_cfgpin(S3C2410_GPG(12), S3C2410_GPG12_XMON); //configure GPG12 to "reserved"

s3c2410_gpio_cfgpin(S3C2410_GPG(13), S3C2410_GPG13_nXPON); //configure GPG13 to "reserved"

s3c2410_gpio_cfgpin(S3C2410_GPG(14), S3C2410_GPG14_YMON); //configure GPG14 to "reserved"

s3c2410_gpio_cfgpin(S3C2410_GPG(15), S3C2410_GPG15_nYPON); //configure GPG15 to "reserved"

}

/*read already*************************************************************************/

/* ADCDAT0 is used for X position and ADCDAT1 is used for Y position */

static void touch_timer_fire(unsigned long data)

{

unsigned long data0;

unsigned long data1;

int updown;

data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0

data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1

/*get the state of stylus to "updown", "1" represents "down", "0" represents "up"*/

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

if (updown) {

if (count != 0) { // "count" is the times of conversion

long tmp;

tmp = xp;

xp = yp;

yp = tmp;

xp >>= 2; //get the average value of 4 times of conversion

yp >>= 2; //get the average value of 4 times of conversion

input_report_abs(dev, ABS_X, xp); //report absolute x axis value

input_report_abs(dev, ABS_Y, yp); //report absolute y axis value

input_report_key(dev, BTN_TOUCH, 1); //report key event

input_report_abs(dev, ABS_PRESSURE, 1); //report touch screen state

input_sync(dev); //synchronization ( means we have reported a whole touch screen event )

}

xp = 0;

yp = 0;

count = 0;

iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER

iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,

base_addr+S3C2410_ADCCON); //start AD conversion

} else {

count = 0;

input_report_key(dev, BTN_TOUCH, 0);

input_report_abs(dev, ABS_PRESSURE, 0);

input_sync(dev);

iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER to Detect Stylus Down Interrupt Signal

if (OwnADC) {

OwnADC = 0;

up(&ADC_LOCK); // "up" releases semaphore

}

}

}

/*read already************************************************************************/

static struct timer_list touch_timer =

TIMER_INITIALIZER(touch_timer_fire, 0, 0); //create kernel timer

/*read already************************************************************************/

static irqreturn_t stylus_updown(int irq, void *dev_id)

{

unsigned long data0;

unsigned long data1;

int updown;

/* "down_trylock" tries to get semaphore, if succeeds, return 0 */

if (down_trylock(&ADC_LOCK) == 0) {

OwnADC = 1;

data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0

data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1

/*get the state of stylus to "updown", "1" represents "down", "0" represents "up"*/

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

S3C2410_ADCDAT0_UPDOWN));

if (updown) { // stylus is down

touch_timer_fire(0);

} else {

OwnADC = 0;

up(&ADC_LOCK); // "up" releases semaphore

}

}

return IRQ_HANDLED;

}

/*read already*************************************************************************/

static irqreturn_t stylus_action(int irq, void *dev_id)

{

unsigned long data0;

unsigned long data1;

if (OwnADC) {

data0 = ioread32(base_addr+S3C2410_ADCDAT0); //read data from ADC conversion data register ADCDAT0

data1 = ioread32(base_addr+S3C2410_ADCDAT1); //read data from ADC conversion data register ADCDAT1

xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK; //calculate sum of 4 times of conversion

yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK; //calculate sum of 4 times of conversion

count++;

if (count < (1<<2)) {

iowrite32(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST,

base_addr+S3C2410_ADCTSC); //set ADCTSC

iowrite32(ioread32(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START,

base_addr+S3C2410_ADCCON); //start AD conversion

} else {

mod_timer(&touch_timer, jiffies+1); //activate kernel timer

iowrite32(WAIT4INT(1), base_addr+S3C2410_ADCTSC); //set ADC TOUCH SCREEN CONTROL REGISTER to Detect Stylus Up Interrupt Signal

}

}

return IRQ_HANDLED;

}

/*read already************************************************************************/

static struct clk *adc_clock;

/*read already************************************************************************/

static int __init s3c2410ts_init(void)

{

struct input_dev *input_dev;

adc_clock = clk_get(NULL, "adc"); //get adc clock

if (!adc_clock) {

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

return -ENOENT;

}

clk_enable(adc_clock); //run adc clock

base_addr=ioremap(S3C2410_PA_ADC,0x20); //map IO address space to kernel virtual address space

if (base_addr == NULL) {

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

return -ENOMEM;

}

s3c2410_ts_connect(); // Configure GPIOs

iowrite32(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(0xFF),\

base_addr+S3C2410_ADCCON); //initialize ADCCON register

iowrite32(0xffff, base_addr+S3C2410_ADCDLY); //initialize ADCDLY register

iowrite32(WAIT4INT(0), base_addr+S3C2410_ADCTSC); //initialize ADCTSC register

/* Initialize struct input_dev */

input_dev = input_allocate_device(); //allocate a input 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); //supported event type

dev->keybit[BITS_TO_LONGS(BTN_TOUCH)] = BIT(BTN_TOUCH); //supported key type

input_set_abs_params(dev, ABS_X, 0, 0x3FF, 0, 0); //set parameters of axis X of touch screen

input_set_abs_params(dev, ABS_Y, 0, 0x3FF, 0, 0); //set parameters of axis Y of touch screen

input_set_abs_params(dev, ABS_PRESSURE, 0, 1, 0, 0);//set parameters of pressure of touch screen

dev->name = s3c2410ts_name;

dev->id.bustype = BUS_RS232;

dev->id.vendor = 0xDEAD;

dev->id.product = 0xBEEF;

dev->id.version = S3C2410TSVERSION;

/* request interrupt */

if (request_irq(IRQ_ADC, stylus_action, IRQF_SHARED|IRQF_SAMPLE_RANDOM,

"s3c2410_action", dev)) {

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

iounmap(base_addr);

return -EIO;

}

if (request_irq(IRQ_TC, stylus_updown, IRQF_SAMPLE_RANDOM,

"s3c2410_action", dev)) {

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

iounmap(base_addr);

return -EIO;

}

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

/* All went ok, so register to the input system */

input_register_device(dev);

return 0;

}

/*read already************************************************************************/

static void __exit s3c2410ts_exit(void)

{

disable_irq(IRQ_ADC);

disable_irq(IRQ_TC);

free_irq(IRQ_TC,dev); //release memory allocated to irq

free_irq(IRQ_ADC,dev); //release memory allocated to irq

if (adc_clock) {

clk_disable(adc_clock);

clk_put(adc_clock); //??

adc_clock = NULL;

}

input_unregister_device(dev);

iounmap(base_addr);

}

/*read already************************************************************************/

module_init(s3c2410ts_init); //module entry

module_exit(s3c2410ts_exit); //module exit
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: