触摸屏驱动程序

触摸屏工作原理:

S3c2440触摸屏目标是获取X/Y坐标信息,坐标信息获取分两种:

1X/Y位置分布转换模式,先获取其中一个坐标,再获取另外一个。

2X/Y自动转换模式,两个坐标同时获取到。

工作流程

一:选择X/Y坐标获取模式,分别还是自动。

二:设置触摸屏到等待终端的状态。

三:如果中断发生启动相应数模转换。四:数模转换完成后获取坐标X/Y值,返回等待中断状态,到二之后三之前,继续循环。

流程里有两个重要中断INT_TC中断和INT_ADC中断:1INT_TC中断,当按下弹起触摸屏产生。2INT_ADC中断,当TC中断开始后,启动AD转换,通过ADC中断告诉CPU转换完成。 

程序分析:阅读顺序:模块初始化->中断->file_operation

#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/delay.h>
#include <asm/hardware.h>

/* debug macros */
#undef DEBUG
#ifdef DEBUG
#define DPRINTK( x... )	printk("s3c2410-ts: " ##x)
#else
#define DPRINTK( x... )
#endif

#define PEN_UP	        0		
#define PEN_DOWN	1
#define PEN_FLEETING	2
#define MAX_TS_BUF	8	/* how many do we want to buffer */

#define DEVICE_NAME	"s3c2410-ts"
#define TSRAW_MINOR	1

typedef struct {
	unsigned int penStatus;		/* PEN_UP, PEN_DOWN, PEN_SAMPLE */
	TS_RET buf[MAX_TS_BUF];		/* protect against overrun */
	unsigned int head, tail;	/* head and tail for queued events */
	wait_queue_head_t wq;
	spinlock_t lock;
} TS_DEV;

static TS_DEV tsdev;

#define BUF_HEAD	(tsdev.buf[tsdev.head])
#define BUF_TAIL	(tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) 	((++(x)) & ((mod) - 1))

static int tsMajor = 0;

static void (*tsEvent)(void);

#define HOOK_FOR_DRAG
#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY  (HZ/100) /* 10 ms */
static struct timer_list ts_timer;
#endif

#define wait_down_int()	{ ADCTSC = DOWN_INT | XP_PULL_UP_EN | \
				XP_AIN | XM_HIZ | YP_AIN | YM_GND | \
				XP_PST(WAIT_INT_MODE); }
#define wait_up_int()	{ ADCTSC = UP_INT | XP_PULL_UP_EN | XP_AIN | XM_HIZ | \
				YP_AIN | YM_GND | XP_PST(WAIT_INT_MODE); }
#define mode_x_axis()	{ ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
				XP_PULL_UP_DIS | XP_PST(X_AXIS_MODE); }
#define mode_x_axis_n()	{ ADCTSC = XP_EXTVLT | XM_GND | YP_AIN | YM_HIZ | \
				XP_PULL_UP_DIS | XP_PST(NOP_MODE); }
#define mode_y_axis()	{ ADCTSC = XP_AIN | XM_HIZ | YP_EXTVLT | YM_GND | \
				XP_PULL_UP_DIS | XP_PST(Y_AXIS_MODE); }
#define start_adc_x()	{ ADCCON = PRESCALE_EN | PRSCVL(49) | \
				ADC_INPUT(ADC_IN5) | ADC_START_BY_RD_EN | \
				ADC_NORMAL_MODE; \
			  ADCDAT0; }
#define start_adc_y()	{ ADCCON = PRESCALE_EN | PRSCVL(49) | \
				ADC_INPUT(ADC_IN7) | ADC_START_BY_RD_EN | \
				ADC_NORMAL_MODE; \
			  ADCDAT1; }
#define disable_ts_adc()	{ ADCCON &= ~(ADCCON_READ_START); }

static int adc_state = 0;
static int x, y;	/* touch screen coorinates */

static void tsEvent_raw(void)
{
	if (tsdev.penStatus == PEN_DOWN) { /* 保存按下时的坐标 */
		BUF_HEAD.x = x;
		BUF_HEAD.y = y;
		BUF_HEAD.pressure = PEN_DOWN;

#ifdef HOOK_FOR_DRAG 
		ts_timer.expires = jiffies + TS_TIMER_DELAY;
		add_timer(&ts_timer); /* 对长时间按下键的处理 */
#endif
	} else {
#ifdef HOOK_FOR_DRAG 
		del_timer(&ts_timer);
#endif
		
		BUF_HEAD.x = 0;
		BUF_HEAD.y = 0;
		BUF_HEAD.pressure = PEN_UP; /* 保存弹起时的坐标 */
	}

	tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
	wake_up_interruptible(&(tsdev.wq)); /* 唤醒进程 */

}

static int tsRead(TS_RET * ts_ret)
{
        spin_lock_irq(&(tsdev.lock));
	ts_ret->x = BUF_TAIL.x;
	ts_ret->y = BUF_TAIL.y;
	ts_ret->pressure = BUF_TAIL.pressure;
	tsdev.tail = INCBUF(tsdev.tail, MAX_TS_BUF);
	spin_unlock_irq(&(tsdev.lock));
        
	return sizeof(TS_RET);
}


static ssize_t s3c2410_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
	TS_RET ts_ret;

retry: 
	if (tsdev.head != tsdev.tail) { //判断有无新数据可读
		int count;
		count = tsRead(&ts_ret);
		if (count) copy_to_user(buffer, (char *)&ts_ret, count);
		return count;
	} else {
		if (filp->f_flags & O_NONBLOCK)
			return -EAGAIN;	///用户有无设计flag非阻塞
		interruptible_sleep_on(&(tsdev.wq)); //阻塞应用进程
		if (signal_pending(current))	//信号和中断都可唤醒,这里判断是否信号唤醒
  //current指向当前进程的task_struct指针
			return -ERESTARTSYS;
		goto retry;
	}

	return sizeof(TS_RET);
}

static unsigned int s3c2410_ts_poll(struct file *filp, struct poll_table_struct *wait)
{
	poll_wait(filp, &(tsdev.wq), wait); //设置等待队列,根据设备是否可读写返回掩码
	return (tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM); //仅返回读掩码
}

static inline void start_ts_adc(void)
{	//通过寄存器的设置,启动adc转换
	adc_state = 0;
	mode_x_axis();
	start_adc_x();
}

static inline void s3c2410_get_XY(void)
{
	if (adc_state == 0) { /* 转换x */
  //区分获取X还是Y坐标
		adc_state = 1;
		disable_ts_adc();
		y = (ADCDAT0 & 0x3ff); /* 获取x坐标 */
		mode_y_axis(); 
		start_adc_y(); /*启动y坐标转化,转换完后又会产生ADC中断*/
	} else if (adc_state == 1) {  /*转换y*/
		adc_state = 0;
		disable_ts_adc();
		x = (ADCDAT1 & 0x3ff); /* 获取y坐标 */
		tsdev.penStatus = PEN_DOWN; /* 改变屏状态 */
		DPRINTK("PEN DOWN: x: %08d, y: %08d\n", x, y);
		wait_up_int();   /* 等待弹起中断 */
		tsEvent();
	}
}

static void s3c2410_isr_adc(int irq, void *dev_id, struct pt_regs *reg)
{		//转换完后启动ADC中断

	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_UP)
	  s3c2410_get_XY();
#ifdef HOOK_FOR_DRAG
	else
	  s3c2410_get_XY();
#endif
	spin_unlock_irq(&(tsdev.lock));
}

/* 当按键按下时首先产生的中断,数模转换 */
static void s3c2410_isr_tc(int irq, void *dev_id, struct pt_regs *reg)
{
	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_UP) /*如果是按下中断*/
	{
	  start_ts_adc(); /* 开始AD转化 */
	} else 
	{
	  tsdev.penStatus = PEN_UP; /* 如果是弹起中断*/
	  DPRINTK("PEN UP: x: %08d, y: %08d\n", x, y);
	  wait_down_int(); /*等待按下中断*/
	  tsEvent(); /* 调用后续处理函数 */
	}
	spin_unlock_irq(&(tsdev.lock));
}

#ifdef HOOK_FOR_DRAG
static void ts_timer_handler(unsigned long data)
//按下时每隔0.1s启动超时函数,获取按下的坐标,笔移动一直获取坐标轨迹
{
	spin_lock_irq(&(tsdev.lock));
	if (tsdev.penStatus == PEN_DOWN) {
		start_ts_adc();
	}
	spin_unlock_irq(&(tsdev.lock));
}
#endif

static int s3c2410_ts_open(struct inode *inode, struct file *filp)
{	//timer 等待队列初始化
	tsdev.head = tsdev.tail = 0;
	tsdev.penStatus = PEN_UP;
#ifdef HOOK_FOR_DRAG 
	init_timer(&ts_timer);
	ts_timer.function = ts_timer_handler;  //超时函数
#endif
	tsEvent = tsEvent_raw;
	init_waitqueue_head(&(tsdev.wq));

	MOD_INC_USE_COUNT;
	return 0;
}

static int s3c2410_ts_release(struct inode *inode, struct file *filp)
{
#ifdef HOOK_FOR_DRAG
	del_timer(&ts_timer);
#endif
	MOD_DEC_USE_COUNT;
	return 0;
}

static struct file_operations s3c2410_fops = {
	owner:	THIS_MODULE,
	open:	s3c2410_ts_open,
	read:	s3c2410_ts_read,	
	release:	s3c2410_ts_release,
	poll:	s3c2410_ts_poll,
};

void tsEvent_dummy(void) {}


#ifdef CONFIG_DEVFS_FS
static devfs_handle_t devfs_ts_dir, devfs_tsraw;
#endif
static int __init s3c2410_ts_init(void)
{
	int ret;

	tsEvent = tsEvent_dummy;

	/* 注册字符设备,主设备号为0,表示自动分配 */
	ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
	if (ret < 0) {
	  printk(DEVICE_NAME " can't get major number\n");
	  return ret;
	}
	tsMajor = ret;

	/* 相应GPIO寄存器操作设置为XP, YM, YP and  YM */
	set_gpio_ctrl(GPIO_YPON); 
	set_gpio_ctrl(GPIO_YMON);
	set_gpio_ctrl(GPIO_XPON);
	set_gpio_ctrl(GPIO_XMON);

	
	/* 注册中断TC, ADC */
	ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT, 
			  DEVICE_NAME, s3c2410_isr_adc);
	if (ret) goto adc_failed;
	ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT, 
			  DEVICE_NAME, s3c2410_isr_tc);
	if (ret) goto tc_failed;

	/* 设置寄存器让触摸屏等待被按下的中断 */
	wait_down_int();

#ifdef CONFIG_DEVFS_FS
  //创建设备文件
	devfs_ts_dir = devfs_mk_dir(NULL, "touchscreen", NULL);
	devfs_tsraw = devfs_register(devfs_ts_dir, "0raw", DEVFS_FL_DEFAULT,
			tsMajor, TSRAW_MINOR, S_IFCHR | S_IRUSR | S_IWUSR,
			&s3c2410_fops, NULL);	
#endif

	ADCDLY = 0xFFFF;
	printk(DEVICE_NAME " initialized\n");

	return 0;
 tc_failed:
	free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
 adc_failed:
	return ret;
}

static void __exit s3c2410_ts_exit(void)
{
#ifdef CONFIG_DEVFS_FS	
	devfs_unregister(devfs_tsraw);
	devfs_unregister(devfs_ts_dir);
#endif	
	unregister_chrdev(tsMajor, DEVICE_NAME);

	free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
	free_irq(IRQ_TC, s3c2410_isr_tc);
}

module_init(s3c2410_ts_init);
module_exit(s3c2410_ts_exit);


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值