linux的s3c6410触摸屏驱动

先介绍一下寄存器



ADCCON(ADC控制寄存器):


ADCCON初始化:

1.需要把PRSCEN位置1.

2.根据PRSCVL = PCLK/freq -1 设置PRSCVL位.




ADCDLY(ADC延时寄存器):

这个寄存器主要设置DELAY(延时),在我的驱动程序里设置为了100000.



ADCTSC(ADC触摸屏控制寄存器)

这个寄存器主要注意UD_SEN,YM_SEN,YP_SEN,XP_SEN,AUTO_PST,XY_PST

XY_PST:这里需要设置为11(waiting for interrupt mode)等待中断模式

UD_SEN:初始化时设置为0,即按下中断,当按下发生中断后,在中断处理函数中设置此为为1,即释放时产生中断.而初始化时YM_SEN,YP_SEN,XP_SEN这几位一律置1.

当需要进行模数转换时,XY_PST置0,AUTO_PST置1,YM_SEN,YP_SEN,XP_SEN置1.




ADCDAT0(数据寄存器0,当模数转换完成后X的值会存放在此寄存器中的[11:0])

UPDOWN:判断处于按下还是释放状态

XPDATA:存放10位数据

XPDATA_12:存放11~12位数据.



ADCDAT1(数据寄存器1,当模数转换完成后Y的值会存放在此寄存器中的[11:0])

UPDOWN:判断处于按下还是释放状态

YPDATA:存放10位数据

YPDATA_12:存放11~12位数据.



这是一个platform总线设备驱动,首先编写设备模块

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <mach/regs-gpio.h>
#include <linux/platform_device.h>
#include <linux/irq.h>
struct platform_device *ts_dev;
struct resource ts_res[] = 
{
	[0] = 
	{
		.name = "TS_MEM",		
		.start = 0x7e00b000,		//寄存器地址
		.end = 0x7e00b000 + 0x23,	//寄存器结束地址
		.flags = IORESOURCE_MEM,
	},
	[1] =
	{
		.name = "TS_UD",
		.start = IRQ_PENDN,		//按下或释放产生中断
		.end = IRQ_PENDN,
		.flags = IORESOURCE_IRQ,
	},
	[2] =
	{
		.name = "TS_ADC",
		.start = IRQ_ADC,		//模数转换完成产生中断
		.end = IRQ_ADC,
		.flags = IORESOURCE_IRQ,
	},
};

int __init ts_dev_init()
{
	int retval;
	ts_dev = platform_device_alloc("touch_screen", -1);
	if(!ts_dev)
	{
		printk(KERN_ERR "Alloc device error.\n");
		return -EINVAL;
	}

	retval = platform_device_add_resources(ts_dev, ts_res, 3);
	if(retval)
	{
		printk(KERN_ERR "Add resources error.\n");
		platform_device_del(ts_dev);
		return retval;
	}
	retval = platform_device_add(ts_dev);
	if(retval)
	{
		printk(KERN_ERR "Add error.\n");
		return retval;
	}
}

void __exit ts_dev_exit()
{
	platform_device_del(ts_dev);
}

module_init(ts_dev_init);
module_exit(ts_dev_init);
MODULE_LICENSE("GPL");



触摸屏驱动模块

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <mach/irqs.h>
#include <asm/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/input.h>
#include <linux/errno.h>
#include <asm/irq.h>
#include <linux/virtio.h>
#include <linux/semaphore.h>

#define ADCCON (addr + 0x00)
#define ADCTSC (addr + 0x04)
#define ADCDLY (addr + 0x08)
#define ADCDAT0 (addr + 0xc)
#define ADCDAT1 (addr + 0x10)
#define ADCUPDN (addr + 0x14)
#define ADCCLRINT (addr + 0x18)
#define ADCMUX (addr + 0x1c)
#define ADCCLRWK (addr + 0x20)

#define WAIT4INT(x) (((x)<<8)|(1<<7)|(1<<6)|(1<<4)|(((3)&0x3)<<0))
#define AUTOPST ((1<<7)|(1<<6)|(1<<4)|(1<<2)|((0&0x3)<<0))

void timer_handler(unsigned long);
struct clk *clock;
struct input_dev *ts;
struct timer_list timer = TIMER_INITIALIZER(timer_handler, 0, 0);		//初始化定时器,定时器中断处理函数为timer_handler
void *addr;
struct resource *res[3];
static unsigned int count,x,y;
static struct semaphore sem;

irqreturn_t ts_updown(int irq, void *dev_id, struct pt_regs *regs)
{
	unsigned long data0,data1;
	unsigned long down;

	data0 = ioread32(ADCDAT0);
	data1 = ioread32(ADCDAT1);
	down = (!(data0&1<<15))&(!(data1&1<<15));		//检测是否为按下
	if(down)
	{
		timer_handler(1);				//若按下,执行timer_handler函数
	}
	iowrite32(0x0, ADCCLRWK);
	iowrite32(0x0, ADCCLRINT);
	return IRQ_HANDLED;	
}

irqreturn_t adc(int irq, void *dev_id, struct pt_regs *regs)	//模数转换完成中断处理函数
{
	unsigned long data0,data1;
	data0 = ioread32(ADCDAT0);
	data1 = ioread32(ADCDAT1);
	if(count < 4)						//转换小于4次
	{
		x += data0&0x3ff;
		y += data1&0x3ff;
		count++;
		iowrite32((1<<3)|AUTOPST, ADCTSC);
		iowrite32(ioread32(ADCCON)|(1<<0), ADCCON);
	}
	else if(count == 4)					//转换等于4次
	{
		mod_timer(&timer,jiffies + 1);			//下一个时钟滴答执行timer_handler函数
		iowrite32(WAIT4INT(1), ADCTSC);			//设置触摸屏中断为释放中断
	}
	iowrite32(0x0, ADCCLRINT);				//清除ADC中断.
	return IRQ_HANDLED;
}

void timer_handler(unsigned long data)
{
	if(!down_trylock(&sem))
	{
		unsigned long data0,data1,down;

		data0 = ioread32(ADCDAT0);
		data1 = ioread32(ADCDAT1);
		down = (!(data0&(1<<15))&&(!(data1&(1<<15))));
		if(down)							//判断是否为按下状态
		{
			if(count != 0)						//已转换4次
			{
				x = x >> 2;					//X,Y取平均值
				y = y >> 2;
				input_report_abs(ts, ABS_X, x);
				input_report_abs(ts, ABS_Y, y);
				input_report_key(ts, BTN_TOUCH, 1);
				input_report_abs(ts, ABS_PRESSURE, 1);
				input_sync(ts);
			}
			x = 0;
			y = 0;
			count = 0;
			iowrite32((1<<3)|AUTOPST, ADCTSC);			//设置自动X,Y转换
			iowrite32(ioread32(ADCCON)|(1<<0), ADCCON);		//开始模数转换
		}	
		else								//若为释放状态,报告
		{
			input_report_key(ts, BTN_TOUCH, 0);
			input_report_abs(ts, ABS_PRESSURE, 0);
			input_sync(ts);
			iowrite32(WAIT4INT(0), ADCTSC);				//设置为触摸屏按下产生中断
		}
		up(&sem);
	}
}	

int ts_probe(struct platform_device *dev)
{
	int retval;
	res[0] = platform_get_resource(dev, IORESOURCE_MEM, 0);					//获取设备寄存器地址资源
	if(!res[0])
	{
		printk(KERN_ERR "Get memory resource error.\n");
		return -ENOMEM;
	}
	
	res[0] = request_mem_region(res[0]->start,(res[0]->end - res[0]->start+1), "ts_mem");	//获取内存区域
	if(!res[0])
	{
		printk(KERN_ERR "Request memory region error.\n");
		return -ENOMEM;
	}
		
	addr = ioremap(res[0]->start, (res[0]->end - res[0]->start + 1));			//寄存器区域映射到获取的内存区域
	if(!addr)
	{
		printk(KERN_ERR "I/O remap error.\n");
		release_mem_region(res[0]->start, (res[0]->end - res[0]->start + 1));
		return -ENOMEM;
	}

	res[1] = platform_get_resource(dev, IORESOURCE_IRQ, 0);					//获取触摸屏中断资源

	if(!res[1])
	{
		printk(KERN_ERR "Get IRQ_TC error.\n");
		goto tc_fail;
	}

	retval = request_irq(res[1]->start, ts_updown, IRQF_SAMPLE_RANDOM, "updown", (void *)&res[1]);	//申请触摸屏中断.中断处理函数为ts_updown
	if(retval)
	{
		printk(KERN_ERR "Request IRQ_TC error.\n");
		goto tc_fail;
	}

	res[2] = platform_get_resource(dev, IORESOURCE_IRQ, 1);					//获取ADC转换完成中断
	if(!res[2])
	{
		printk(KERN_ERR "Get IRQ_ADC error.\n");
		goto adc_fail;
	}

	retval = request_irq(res[2]->start, adc, IRQF_SAMPLE_RANDOM, "adc", (void *)&res[2]);	//申请ADC转换完成中断,中断处理函数为adc
	if(retval)
	{
		printk(KERN_ERR "Request IRQ_ADC error.\n");
		goto adc_fail;
	}

	ts = input_allocate_device();
	if(!ts)
	{
		printk(KERN_ERR "Allocate input device error.\n");
		goto input_fail;
	}
	
	ts->name = "S3C TouchScreen";
	retval = input_register_device(ts);						//注册输入子系统
	if(retval)
	{
		printk(KERN_ERR "Register input device error.\n");
		goto input_fail;
	}

	clock = clk_get(&dev->dev, "adc");						//获取adc时钟
	if(IS_ERR(clock))
	{
		dev_err(dev, "failed to find watchdog clock source\n");
		goto clock_fail;	
	}
	
	sema_init(&sem, 1);
	clk_enable(clock);
	
	iowrite32((1<<14)|((49&0xff)<<6), ADCCON);					//初始化ADCCON
	iowrite32(10000&0xffff, ADCDLY);						//初始化ADCDLY
	iowrite32(WAIT4INT(0), ADCTSC);							//初始化ADCTSC
	
	ts->evbit[0] = BIT_MASK(EV_SYN)|BIT_MASK(EV_KEY)|BIT_MASK(EV_ABS);
	ts->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
	input_set_abs_params(ts, ABS_X, 0, 0x3ff, 0, 0);				//设置X的最大值为0x3ff
	input_set_abs_params(ts, ABS_Y, 0, 0x3ff, 0, 0);				//设置Y的最大值为0x3ff
	input_set_abs_params(ts, ABS_PRESSURE, 0, 1, 0, 0);				//设置按下为1
	return 0;

clock_fail:
	input_unregister_device(ts);

input_fail:
	free_irq(res[2]->start, (void *)&res[2]);

adc_fail:

	free_irq(res[1]->start, (void *)&res[1]);
tc_fail:
	iounmap(addr);
	release_mem_region(res[0]->start, (res[0]->end - res[0]->start + 1));
	return -EINVAL;


int ts_remove(struct platform_device *dev)
{
	input_unregister_device(ts);
	free_irq(res[2]->start, (void *)&res[2]);
	free_irq(res[1]->start, (void *)&res[1]);
	iounmap(addr);
	release_mem_region(res[0]->start, (res[0]->end - res[0]->start + 1));
}

struct platform_driver ts_drv = 
{
	.probe = ts_probe,
	.remove = ts_remove,
	.driver = 
	{
		.name = "touch_screen",
		.owner = THIS_MODULE,
	},
};

int __init ts_drv_init()
{
	int retval;
	retval = platform_driver_register(&ts_drv);			//注册platform驱动
	if(retval)
	{
		printk(KERN_ERR "Register driver error.\n");
		return retval;
	}
	return 0;
}

void __exit ts_drv_exit()
{
	platform_driver_unregister(&ts_drv);
}

module_init(ts_drv_init);
module_exit(ts_drv_exit);
MODULE_LICENSE("GPL");



驱动流程大致为:等待触摸屏按下中断->触摸屏按下中断->timer_handler(开始ADC转换)->设置为触摸屏释放中断->持续ADC转换->触摸屏释放中断->设置为触摸屏按下中断->等待触摸屏按下中断



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值