先介绍一下寄存器
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");