/*=========================================================================
工程名称:
4_touchscreen_driver
组成文件:
touch_driver.c
功能描述:
触模屏设备驱动,可以返回坐标或AD值
硬件连接:
连接四线电阻式触模屏
=========================================================================*/
#include
/*module_init()*/
#include /* printk() */
#include /* __init __exit */
#include /* file_operation */
#include /* copy_to_user, copy_from_user */
#include /*class ,class_create ,device_create 等*/
#include /* Error number */
#include /* mdelay ,ndelay*/
#include /* udelay */
#include /*S3C2410_GPGCON*/
#include /*S3C24XX_VA_GPIO*/
#include //set_irq_type ,IRQ_TYPE_EDGE_FALLING
#include //request_irq , free_irq
#include // S3C2410_ADCCON
#include //ioremap()
#include //clk_get() , clk_enable()
//#define DEBUG //open debug message
#ifdef DEBUG
#define PRINTK(fmt, arg...) printk(KERN_WARNING fmt, ##arg)
#else
#define PRINTK(fmt, arg...) printk(KERN_DEBUG fmt, ##arg)
#endif
#define ADCCON (*(volatile unsigned long *)(regs_adc + S3C2410_ADCCON))
//ADC control
#define ADCDLY (*(volatile unsigned long *)(regs_adc + S3C2410_ADCDLY))
//ADC start or Interval Delay
#define ADCDAT0 (*(volatile unsigned long *)(regs_adc + S3C2410_ADCDAT0))
#define ADCDAT1 (*(volatile unsigned long *)(regs_adc + S3C2410_ADCDAT1))
#define ADCTSC (*(volatile unsigned long *)(regs_adc + S3C2410_ADCTSC))
#define TOUCH_WIDTH 320
// 触摸屏的水平分辨率
#define TOUCH_HEIGHT
240 // 触摸屏的垂直分辨率
#define TOUCH_DEFAULT_LB 0x55
// 左边缘对应的默认AD转换值(16bit)
#define TOUCH_DEFAULT_RB 0x3ac
// 右边缘对应的默认AD转换值(16bit)
#define TOUCH_DEFAULT_TB 0x89
// 上边缘对应的默认AD转换值(16bit)
#define TOUCH_DEFAULT_BB 0x384
// 下边缘对应的默认AD转换值(16bit)
#define PEN_UP 0
#define PEN_DOWN
1
#define PEN_FLEETING 2
#define MAX_TS_BUF 16
/* how many do we want to buffer */
#define ADC_X_END 0
#define ADC_Y_END 1
#define DRIVER_NAME "touch_driver"
#define TSRAW_MINOR 1
#define BUF_HEAD (tsdev.buf[tsdev.head])
#define BUF_TAIL (tsdev.buf[tsdev.tail])
#define INCBUF(x,mod) ((++(x)) & ((mod) - 1))
#define ADC_FREQ 2000000 // 2MHz AD convert freq 030918
static void __iomem *regs_adc;
static int PreScale_n = 30; // PCLK / (PreScale_n+1) = ADConversion freq.
//以struct TS_RET结构体的形式给应用层转递数据
struct TS_RET{
unsigned short pressure;
unsigned short x;
unsigned short y;
unsigned short pad;
};
typedef struct
{
unsigned int penStatus;
/* PEN_UP, PEN_DOWN, PEN_SAMPLE */
struct TS_RET buf[MAX_TS_BUF];
/* protect against overrun */
unsigned int head, tail;
/* head and tail for queued events */
} TS_DEV;
static int MAJOR_NR = 0;
static int MINOR_NR = 0;
struct class *my_class;
static TS_DEV tsdev;
static int adc_state = ADC_X_END;
static int p_x,p_y; //通过这两个全局变量来保存AD值
static void tsEvent_raw(void)
{
if (tsdev.penStatus == PEN_DOWN)
{
#if 0
//x,y从右上AD 值最小,左下最大,但坐标从左上最小开始
BUF_HEAD.x = TOUCH_WIDTH * ( TOUCH_DEFAULT_RB -p_x) / (TOUCH_DEFAULT_RB - TOUCH_DEFAULT_LB);
BUF_HEAD.y = TOUCH_HEIGHT * (p_y - TOUCH_DEFAULT_TB) / (TOUCH_DEFAULT_BB - TOUCH_DEFAULT_TB);
#else
BUF_HEAD.x = p_x;
BUF_HEAD.y = p_y;
#endif
BUF_HEAD.pressure = PEN_DOWN;
}
else
{
BUF_HEAD.x = 0;
BUF_HEAD.y = 0;
BUF_HEAD.pressure = PEN_UP;
}
tsdev.head = INCBUF(tsdev.head, MAX_TS_BUF);
}
//从消息队列读取坐标值
static int tsRead(struct TS_RET * ts_ret)
{
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);
return sizeof(struct TS_RET);
}
static ssize_t s3c2440_ts_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
{
struct TS_RET ts_ret;
int ret;
//读取不阻塞
if (tsdev.head != tsdev.tail)
{
ret = tsRead(&ts_ret);
if (ret)
ret = copy_to_user(buffer, (char *)&ts_ret, ret);
return ret;
}
else
return -1;
}
static inline void start_ts_adc(void)
{
int read_ad;
ADCTSC = (1<<6)|(1<<5)|(1<<3)|(1); //读取x模式
ADCCON = (1<<14)|(PreScale_n<<6)|(5<<3)|(1<<1)|(1);//设置频率启动AD
read_ad = ADCDAT0;
//启动AD转换
adc_state = ADC_X_END; //设置转换状态
}
//通过AD转换结束中断回调该函数,最后计算并将点击坐标放入消息队列
static inline void s3c2440_get_XY(void)
{
int read_ad;
if (adc_state == ADC_X_END)
{
ADCCON &= ~(1<<1);//禁止读取转换
p_x = (ADCDAT0 & 0x3ff);
ADCTSC = (1<<7)|(1<<4)|(1<<3)|(2);//读y模式
ADCCON = (1<<14)|(PreScale_n<<6)|(7<<3)|(1<<1)|(1);//设置频率启动AD
read_ad = ADCDAT1;
adc_state = ADC_Y_END;
}
else if (adc_state == ADC_Y_END)
{
ADCCON &= ~(1<<1);//禁止读取转换
p_y = (ADCDAT1 & 0x3ff);
tsdev.penStatus = PEN_DOWN;
PRINTK("PEN DOWN: x: %08d, y: %08d
", p_x, p_y);
ADCTSC = (1<<8)|(1<<7)|(1<<6)|(1<<4)|(3);//等待UP 中断模式
tsEvent_raw();
adc_state = ADC_X_END;
}
}
static irqreturn_t s3c2440_isr_adc(int irq, void *dev_id)
{
if (tsdev.penStatus == PEN_UP)
{
s3c2440_get_XY();
}
return 0;
}
static irqreturn_t s3c2440_isr_tc(int irq, void *dev_id)
{
PRINTK("
s3c2440_isr_tc!!!
");
if (tsdev.penStatus == PEN_UP)
{
start_ts_adc();
}
else
{
tsdev.penStatus = PEN_UP;
PRINTK("PEN UP: x: %08d, y: %08d
", p_x, p_y);
ADCTSC = (1<<4)|(1<<7)|(1<<6)|(3);//等待DOWN 中断模式
}
return 0;
}
static int s3c2440_ts_open(struct inode *inode, struct file *filp)
{
int err;
struct clk *adc_clk;
PRINTK("
s3c2440_ts_open!!!
");
adc_clk = clk_get(NULL, "adc");
if(!adc_clk)
{
/*错误处理*/
PRINTK("falied to find adc clock source
");
clk_disable(adc_clk);
clk_put(adc_clk);
return -ENOENT;
}
clk_enable(adc_clk);
regs_adc = ioremap(S3C2410_PA_ADC, 0x20);
ADCDLY = 20000; // it is got from test
ADCTSC = 0x00;
ADCTSC = (1<<4)|(1<<7)|(1<<6)|(3);//等待DOWN 中断模式
err = request_irq(IRQ_ADC,s3c2440_isr_adc,IRQF_SHARED,"ts_adc", (void *)1);
if(err)
{
PRINTK("IRQ%d error %d
", IRQ_ADC, err);
free_irq(IRQ_ADC, (void *)1);
iounmap(regs_adc);
err = -EINVAL;
}
err = request_irq(IRQ_TC,s3c2440_isr_tc,IRQF_SHARED,"ts_tc", (void *)2);
if(err)
{
PRINTK("IRQ%d error %d
", IRQ_TC, err);
free_irq(IRQ_TC, (void *)2);
iounmap(regs_adc);
err = -EINVAL;
}
tsdev.head = tsdev.tail = 0;
tsdev.penStatus = PEN_UP;
return err;
}
static int s3c2440_ts_release(struct inode *inode, struct file *filp)
{
disable_irq(IRQ_ADC);
disable_irq(IRQ_TC);
free_irq(IRQ_ADC, (void *)1);
free_irq(IRQ_TC, (void *)2);
iounmap(regs_adc); /*释放虚拟地址映射空间*/
return 0;
}
static struct file_operations s3c2440_fops = {
owner:
THIS_MODULE,
open: s3c2440_ts_open,
read: s3c2440_ts_read,
release:
s3c2440_ts_release,
};
static int __init s3c2440_ts_init(void)
{
/* Module init code */
PRINTK("TSdriver_init
");
/* Driver register */
MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &s3c2440_fops);
if(MAJOR_NR < 0)
{
PRINTK("register char device fail!
");
return MAJOR_NR;
}
my_class=class_create(THIS_MODULE,"udev_touch");
device_create(my_class,NULL, MKDEV(MAJOR_NR, MINOR_NR), NULL,DRIVER_NAME);
PRINTK("register myDriver OK! Major = %d
", MAJOR_NR);
return 0;
}
static void __exit s3c2440_ts_exit(void)
{
/* Module exit code */
PRINTK("exit in
");
/* Driver unregister */
if(MAJOR_NR > 0)
{
unregister_chrdev(MAJOR_NR, DRIVER_NAME);
device_destroy(my_class,MKDEV(MAJOR_NR, MINOR_NR));
class_destroy(my_class);
PRINTK("myModule_exit ok
");
}
return;
}
module_init(s3c2440_ts_init);
module_exit(s3c2440_ts_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiuhai");