触模屏设备驱动

来源:互联网 发布:lcd1602编程 编辑:程序博客网 时间:2024/06/05 16:10


/*=========================================================================
  工程名称: 4_touchscreen_driver  
  组成文件: touch_driver.c
  功能描述: 触模屏设备驱动,可以返回坐标或AD值
  硬件连接: 连接四线电阻式触模屏
 
=========================================================================*/


#include <linux/module.h>    /*module_init()*/
#include <linux/kernel.h> /* printk() */
#include <linux/init.h> /* __init __exit */
#include <linux/fs.h> /* file_operation */
#include <asm/uaccess.h> /* copy_to_user, copy_from_user */
#include <linux/device.h>  /*class ,class_create ,device_create 等*/
#include <linux/errno.h> /* Error number */
#include <linux/delay.h> /* mdelay ,ndelay*/
#include <asm/delay.h>     /* udelay */
#include <mach/regs-gpio.h>  /*S3C2410_GPGCON*/
#include <linux/pci.h> /*S3C24XX_VA_GPIO*/


#include <linux/irq.h> //set_irq_type ,IRQ_TYPE_EDGE_FALLING
#include <linux/interrupt.h> //request_irq , free_irq
#include <plat/regs-adc.h>   // S3C2410_ADCCON
#include <asm/io.h> //ioremap()
#include <linux/clk.h> //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_HEIGHT240 // 触摸屏的垂直分辨率
#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\n", 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("\ns3c2440_isr_tc!!!\n");
if (tsdev.penStatus == PEN_UP) 
{
 start_ts_adc();

else 
{
 tsdev.penStatus = PEN_UP;
 PRINTK("PEN UP: x: %08d, y: %08d\n", 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("\ns3c2440_ts_open!!!\n");

adc_clk = clk_get(NULL, "adc");
    if(!adc_clk)
    {
        /*错误处理*/
        PRINTK("falied to find adc clock source\n");
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\n", 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\n", 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\n");
/* Driver register */
MAJOR_NR = register_chrdev(MAJOR_NR, DRIVER_NAME, &s3c2440_fops);
if(MAJOR_NR < 0)
{
PRINTK("register char device fail!\n");
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\n", MAJOR_NR);
return 0;
}


static void __exit s3c2440_ts_exit(void)
{
/* Module exit code */
PRINTK("exit in\n");
/* 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\n");
}
return;
}


module_init(s3c2440_ts_init);
module_exit(s3c2440_ts_exit);


MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiuhai");

原创粉丝点击