s3c2410触摸屏在linux下的驱动分析(在EL-ARM-830开发板上测试通过)

 

转载注明出处:http://hi.baidu.com/luv%5Fresplendent/blog/item/183c841f23e992cea78669f8.html

   

触摸屏驱动在/kernel/drivers/char/s3c2410-ts.c 文件中。(声明:此文章是在一网友基础上修改的,在此谢过)

该驱动总要有以下重要数据结构:
1.触摸屏的file_operations
static struct file_operations s3c2410_fops={
    owner: THIS_MODULE,
    open: s3c2410_ts_open,
    read: s3c2410_ts_read,
    release: s3c2410_ts_release,
#ifdef USE_ASYNC
    fasync: s3c2410_ts_fasync,//异步通知
#endif
    poll: s3c2410_ts_poll,//轮询
};
2.全局变量TS_DEV结构体,用来保存触摸屏的相关参数、等待处理的消息队列、当前采样数据、上一次采样数据等信息
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; //* 自旋锁
#ifdef USE_ASYNC
   struct fasync_struct *aq;
#endif
#ifdef CONFIG_PM
   struct pm_dev *pm_dev; //友善之臂专有的,我后面的代码删除了这段
#endif
} TS_DEV;
其中TS_RET在/kernel/include/asm-arm/linuette_ioctl.h 文件中://友善之臂专有,达盛的就自己定义吧
typedef struct {
    unsigned short pressure; //* 压力,这里可定义为笔按下,笔抬起,笔拖曳
    unsigned short x;   //* 横坐标的采样值
   unsigned short y;   //* 纵坐标的采样值
   unsigned short pad;   //* 填充位
} TS_RET;
结构体wait_queue_head_t在/kernel/include/linux/wait.h 文件中:
struct __wait_queue_head {
wq_lock_t lock;
struct list_head task_list;
#if WAITQUEUE_DEBUG
long __magic;
long __creator;
#endif
};
typedef struct __wait_queue_head wait_queue_head_t;
    TS_RET结构体中的信息就是驱动程序提供给上层应用程序使用的信息,用来存储触摸屏的返回值。上层应用程序通过读接口,从底层驱动中读取信息,并根据得到的值进行其他方面的操作。
TS_DEV结构用于记录触摸屏运行的各种状态,PenStatus包括PEN_UP、PEN_DOWN和PEN_FLEETING。buf[MAX_TS_BUF]是用来存放数据信息的事件队列,head、tail分别指向事件队列的头和尾。程序中的笔事件队列是一个环形结构,当有事件加入时,队列头加一,当有事件被取走时,队列尾加一,当头尾位置指针一致时读取笔事件的信息,进程会被安排进入睡眠。wq等待队列,包含一个锁变量和一个正在睡眠进程链表。当有好几个进程都在等待某件事时,Linux会把这些进程记录到这个等待队列。它的作用是当没有笔触事件发生时,阻塞上层的读操作,直到有笔触事件发生。lock使用自旋锁,自旋锁是基于共享变量来工作的,函数可以通过给某个变量设置一个特殊值来获得锁。而其他需要锁的函数则会循环查询锁是否可用。MAX_TS_BUF的值为16,即在没有被读取之前,系统缓冲区中最多可以存放16个笔触数据信息。
   模块初始化函数是调用s3c2410_ts_init 函数来实现的,主要完成触摸屏设备的内核模块加载、初始化系统I/O、中断注册、设备注册,为设备文件系统创建入口等标准的字符设备初始化工作。
static int __init s3c2410_ts_init(void)
   ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
   tsMajor = ret;
这里首先对字符设备进行注册,将触摸屏的file_operations 结构中的函数接口传入内核,注册成功后获得系统自动分配的主设备号。
set_gpio_ctrl(GPIO_YPON);
set_gpio_ctrl(GPIO_YMON);
set_gpio_ctrl(GPIO_XPON);
set_gpio_ctrl(GPIO_XMON);
在/kernel/include/asm-arm/arch-s3c2410/smdk.h 文件中:
#define GPIO_YPON (GPIO_MODE_nYPON | GPIO_PULLUP_DIS | GPIO_G15)
#define GPIO_YMON (GPIO_MODE_YMON | GPIO_PULLUP_EN | GPIO_G14)
#define GPIO_XPON (GPIO_MODE_nXPON | GPIO_PULLUP_DIS | GPIO_G13)
#define GPIO_XMON (GPIO_MODE_XMON | GPIO_PULLUP_EN | GPIO_G12)
GPIO 口的Port G 端口有4个管脚对应触摸屏的控制接口,分别是:
GPG15 --- nYPON   Y+ 控制信号
GPG14 --- YMON Y- 控制信号
GPG13 --- nXPON   X+ 控制信号
GPG12 --- XMON X- 控制信号
需要通过配置GPGCON 寄存器来设定该端口管脚的输出模式,对应位如下:
[31:30] [29:28] [27:26] [25:24]
GPG15 GPG14 GPG13 GPG12 ...
参考S3C2410 芯片datasheet 的I/O口章节 p276,都要设为11(二进制)。
ADCDLY = 0x5000;
配置ADCDLY 寄存器,对自动转换模式来说是设定ADC 开始转换时的延时时间,以及对X轴和Y轴转换的时间间隔,对正常模式来说仅设定对X轴和Y轴转换的时间间隔。注意该值不能为0。
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 for touch screen interrupts */
wait_down_int();

     函数request_irq 是Linux 系统中驱动程序注册中断的方法。irq 为所要申请的硬件中断号,handler 为系统所注册的中断处理子程序,irq_flags 为申请时的选项,devname 为指向设备名称的字符指针,dev_id 为申请时告诉系统的设备标识。若中断申请成功则返回0,失败则返回负值。
ret = request_irq(IRQ_ADC_DONE, s3c2410_isr_adc, SA_INTERRUPT,
         DEVICE_NAME, s3c2410_isr_adc);
调用该函数来进行A/D转换的中断注册,所要申请的硬件中断号为IRQ_ADC_DONE(62),在arch/irq s.h中定义;系统所注册的中断处理子程序为s3c2410_isr_adc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。

ret = request_irq(IRQ_TC, s3c2410_isr_tc, SA_INTERRUPT,
        DEVICE_NAME, s3c2410_isr_tc);
接着继续调用该函数来进行触摸屏触摸的中断注册,所要申请的硬件中断号为IRQ_TC(61);系统所注册的中断处理子程序为s3c2410_isr_tc 函数;申请中断选项为SA_INTERRUPT,表示中断处理程序是快速处理程序,即快速处理程序运行时,所有中断都被屏蔽;设备名称定义为DEVICE_NAME,即"s3c2410-ts";而设备标识仍然用中断处理子程序代替。
wait_down_int();
    调用该宏函数来设置触摸屏为等待中断模式【笔按下产生中断】,具体定义如下:
#define wait_down_int() { ADCTSC = DOWN_INT | XP_PULL_UP_EN | /
XP_AIN | XM_HIZ | YP_AIN | YM_GND | /
XP_PST(WAIT_INT_MODE); }
用该宏函数来设置ADC 触摸屏控制寄存器,参考S3C2410 芯片datasheet 中关于触摸屏的章节,具体设置参数如下:
DOWN_INT = 1<<8 * 0   该位保留且应该设为0 【笔按下或笔抬起中断信号控制位,设为0 表示笔按下产生中断信号】
XP_PULL_UP_EN = 1<<3 * 0   上拉开关使能,设为0 表示XP 引脚上拉使能
XP_AIN = 1<<4 * 1   选择nXPON 引脚输出值,设为1 表示nXPON 引脚输出1,则XP 引脚连接AIN[7] 引脚
XM_HIZ = 1<<5 * 0   选择XMON 引脚输出值,设为0 表示XMON 引脚输出0,则XM 引脚为高阻态
YP_AIN = 1<<6 * 1   选择nYPON 引脚输出值,设为1 表示nYPON 引脚输出1,则YP 引脚连接AIN[5] 引脚
YM_GND = 1<<7 * 1   选择YMON 引脚输出值,设为1 表示YMON 引脚输出1,则YM 引脚为接地
XP_PST(WAIT_INT_MODE); = 3   X坐标Y坐标手动测量设置,设为3 表示等待中断模式

#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
   这里调用了devfs_mk_dir 函数,在设备文件系统中创建了一个名为touchscreen 的目录,并返回一个带有目录结构的数据结构变量devfs_ts_dir。将该变量作为下一步devfs_register 函数的参数,该参数在调用设备文件系统注册清除函数devfs_unregister 时也要作为参数传入。
调用devfs_register 函数后,会在刚才创建的touchscreen 目录下再创建一个名为0raw 的设备文件节点。该函数的参数中,DEVFS_FL_DEFAULT 为该函数的标志选项,tsMajor 为注册字符设备时系统自动分配的主设备号,TSRAW_MINOR(1)为次设备号,S_IFCHR | S_IRUSR | S_IWUSR 为默认的文件模式,&s3c2410_fops 为传入内核的触摸屏file_operations 结构中的函数接口,私有数据指针为空。返回一个devfs_handle_t 数据结构的变量devfs_tsraw,这会在调用设备文件系统注册清除函数devfs_unregister 时作为参数传入。

   模块的退出函数为s3c2410_ts_exit,该函数的工作就是清除已注册的字符设备,中断以及设备文件系统。
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);
#ifdef CONFIG_PM
   pm_unregister(tsdev.pm_dev);
#endif
   free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
   free_irq(IRQ_TC, s3c2410_isr_tc);
#ifdef CONFIG_DEVFS_FS
devfs_unregister(devfs_tsraw);
devfs_unregister(devfs_ts_dir);
#endif
这里首先清除原先后一步创建设备文件节点0raw 的结构变量devfs_tsraw,然后再清除创建touchscreen 目录的结构变量devfs_ts_dir。
unregister_chrdev(tsMajor, DEVICE_NAME);
   接下来删除字符设备的注册信息。
void free_irq(unsigned int irq, void *dev_id)
函数free_irq 与函数request_irq 相对应,通常在模块被卸载时调用,负责注销一个已经申请的中断。

free_irq(IRQ_ADC_DONE, s3c2410_isr_adc);
free_irq(IRQ_TC, s3c2410_isr_tc);
最后依次注销A/D转换和定时器这两个已经申请的中断。

源代码:

/*
* s3c2410-ts.c
*
* touchScreen driver for SAMSUNG S3C2410
*
* Author: Janghoon Lyu <nandy@mizi.com>
* Date : $Date: 2002/06/04 07:11:00 $
*
* Author: cuican     <luv.y@163.com>
* Date:       2008.8.10
*
*/

#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

//#define DEBUG

#ifdef DEBUG
#define DPRINTK( x... )   printk("s3c2410_touch_driver: " ##x)
#else
#define DPRINTK( x... )
#endif

#define PEN_UP            0      
#define PEN_DOWN          1
//#define PEN_FLEETING   2

#define MAX_TS_BUF   16   /* how many do we want to buffer */

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

//#undef   IRQ_TC
//#define IRQ_TC          IRQ_EINT5   //IRQ_EINT7
/*
#ifdef IRQ_GPIO
#undef IRQ_GPIO
#endif

#define IRQ_GPIO           GPIO_F5

#ifdef GPIO_YPON
#undef GPIO_YPON
#endif

#ifdef GPIO_xPON
#undef GPIO_xPON
#endif

#ifdef GPIO_YMON
#undef GPIO_YMON
#endif

#ifdef GPIO_XMON
#undef GPIO_XMON
#endif

#define GPIO_YPON (GPIO_MODE_nYPON | GPIO_PULLUP_DIS | GPIO_G15)
#define GPIO_YMON (GPIO_MODE_YMON | GPIO_PULLUP_EN | GPIO_G14)
#define GPIO_XPON (GPIO_MODE_nXPON | GPIO_PULLUP_DIS | GPIO_G13)
#define GPIO_XMON (GPIO_MODE_XMON | GPIO_PULLUP_EN | GPIO_G12)
*/
/*
#ifdef BIT_ADC
#undef BIT_ADC
#endif
#define BIT_ADC   (0x1<<31)

#ifdef BIT_SUB_ADC
#undef BIT_SUB_ADC
#endif
#define BIT_SUB_ADC   (0x1<<10)

#ifdef BIT_SUB_TC
#undef BIT_SUB_TC
#endif
#define BIT_SUB_TC   (0x1<<9)
*/
#define BUF_HEAD   (tsdev.buf[tsdev.head])
#define BUF_TAIL   (tsdev.buf[tsdev.tail])
#define INCBUF(x,mod)    ((++(x)) & ((mod) - 1))

#if 0
#define CONVERT_X(x) (((x)-200) / 5)
#define CONVERT_Y(y) ((1800-(y)) /7 )
#else
#define CONVERT_X(x) (x)
#define CONVERT_Y(y) (y)
#endif

#define HOOK_FOR_DRAG/**************/

#ifdef HOOK_FOR_DRAG
#define TS_TIMER_DELAY (HZ/100) /* 10 ms */
#define   TS_TIMER_DELAY1   (HZ/200) /* 5 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_IN7) | ADC_START_BY_RD_EN | /
                ADC_NORMAL_MODE; /
            ADCDAT0; }
#define start_adc_y()   { ADCCON = PRESCALE_EN | PRSCVL(49) | /
                ADC_INPUT(ADC_IN5) | ADC_START_BY_RD_EN | /
                ADC_NORMAL_MODE; /
            ADCDAT1; }
#define disable_ts_adc()   { ADCCON &= ~(ADCCON_READ_START); }


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;
static int adc_state=0;



static int x, y;   /* touch screen coorinates */
static int tsMajor = 0;
static void (*tsEvent)(void);

/*
static void printRegisters(void)
{
printk ("GPFCON:%x/tGPFUP:%x/tGPFDAT:%x/n",
      GPFCON, GPFUP, GPFDAT );
printk ("GPGCON:%x/tGPGUP:%x/tGPGDAT:%x/n",
      GPGCON, GPGUP, GPGDAT );
printk ("INTMOD:%x/tINTMSK:%x/tINTPND:%x/tSRCPND:%x/n",
      INTMOD, INTMSK, INTPND, SRCPND);
printk ("EXTINT0:%x/tEINTMASK:%x/tEINTPEND:%x/n",
      EXTINT0, EINTMASK, EINTPEND );
}
*/



static void tsEvent_raw(void)
{
if (tsdev.penStatus == PEN_DOWN)
   {
        /*填充缓冲区*/
    BUF_HEAD.x = CONVERT_X(x);
    BUF_HEAD.y = CONVERT_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));
   #ifdef USE_ASYNC
        if(tsdev.aq)
            kill_fasync(&(tsdev.aq),SIGIO,POLL_IN);//异步通知
   #endif
}

/*tsRead在read函数中被调用*/
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);
}

/*read */
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;
    interruptible_sleep_on(&(tsdev.wq));//在等待队列上睡眠
    if (signal_pending(current))//恢复系统调用
      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);//添加等待队列到poll_table
return (tsdev.head == tsdev.tail) ? 0 : (POLLIN | POLLRDNORM);
}
/*start ad 转换*/
static inline void start_ts_adc(void)
{
   adc_state = 0;
   mode_x_axis();
   start_adc_x();
   DPRINTK("start_ts_adc!/n");
}


/*A/D转换*/
static inline void s3c2410_get_XY(void)
{
   int temp=0,i=16;
   //GPGUP = 0xffff;
    if(adc_state == 0)
   {
        adc_state = 1;
        disable_ts_adc();
        //ADCTSC = (0<<8)|(0<<7)|(1<<6)|(1<<5)|(0<<4)|(1<<3)|(0<<2)|(1);
        while(i--)//16次转换去平均值
        {
            //ADCCON = (1<<14)|(49<<6)|(7<<3)|(0<<2)|(0<<1)|(1);
            start_adc_x();
            while(ADCCON & 0x1);//测试转换是否开始
            udelay(5);
            while(!(ADCCON & 0x800));//测试转换是否结束
            temp += (ADCDAT0 & 0x3ff);
        }
        x = temp>>4;
        mode_y_axis();
        start_adc_y();
        DPRINTK("s3c2410_get_XY-X convert--PEN DOWN: x: %08d/n",x);
   }
   else if(adc_state==1)
   {
   adc_state = 0;
   temp = 0;
   i = 16;
   disable_ts_adc();
   while(i--)//16次转换去平均值
        {
        while(ADCCON & 0x1);//测试转换是否开始
        udelay(5);
        while(!(ADCCON & 0x800));//测试转换是否结束
        temp += (ADCDAT1 & 0x3ff);
        }
   y = temp>>4;
   tsdev.penStatus = PEN_DOWN;
   DPRINTK("s3c2410_get_XY-y convert---PEN DOWN: x:%08d, y: %08d/n",x,y);
   wait_up_int();//置等待IRQ_TC中断模式
   if(x!=0 && y!=0) //舍弃掉非正常值
        tsEvent();
   }
}


/*A/D转换中断处理函数*/
static void s3c2410_isr_adc(int irq,void *dev_id,struct pt_regs *reg)
{
   DPRINTK ("AD Interrupt occured/n");
   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)
{
DPRINTK ("/n Touch Screen Interrupt occured/n");
spin_lock_irq(&(tsdev.lock));
if (tsdev.penStatus == PEN_UP)//中断发生前 pen已经抬起,此时就是PEN_DOWN
   {
        start_ts_adc();
   }
   else
   {
        tsdev.penStatus = PEN_UP;
        DPRINTK("s3c2410_isr_tc--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)
{
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)
{
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;
DPRINTK ( "ts opened/n");
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;
DPRINTK ( "ts closed/n");
return 0;
}

static struct file_operations s3c2410_fops = {
owner:   THIS_MODULE,
open:   s3c2410_ts_open,
read:   s3c2410_ts_read,  
release:   s3c2410_ts_release,
#ifdef USE_ASYNC
fasync:   s3c2410_ts_fasync, //function fasync
#endif
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;

   ret = register_chrdev(0, DEVICE_NAME, &s3c2410_fops);
   if (ret < 0) {
      printk(DEVICE_NAME " can't get major number/n");
      return ret;
   }
   tsMajor = ret;
  
#if 0
   if(SRCPND & BIT_ADC)
        SRCPND |= BIT_ADC;
   if(INTPND & BIT_ADC)
        INTPND |= BIT_ADC;
   if(SUBSRCPND & BIT_SUB_TC)
        SUBSRCPND |= BIT_SUB_TC;
   if(SUBSRCPND & BIT_SUB_ADC)
        SUBSRCPND |= BIT_SUB_ADC;

   INTMSK &= ~(BIT_ADC);
   INTSUBMSK &= ~(BIT_SUB_ADC);
   INTSUBMSK &= ~(BIT_SUB_TC);
   /* set gpio to XP, YM, YP and YM */

   set_GPIO_mode(GPIO106_nYPON_MD);
   set_GPIO_mode(GPIO105_YMON_MD);
   set_GPIO_mode(GPIO104_nXPON_MD);
   set_GPIO_mode(GPIO103_XMON_MD);

   GPUP(GPIO106_nYPON) |= GPIO_bit(GPIO106_nYPON);
   GPUP(GPIO105_YMON) &= GPIO_bit(GPIO105_YMON);
   GPUP(GPIO104_nXPON) |= GPIO_bit(GPIO104_nXPON);
   GPUP(GPIO103_XMON) &= GPIO_bit(GPIO103_XMON);
#else
   set_gpio_ctrl(GPIO_YPON);
   set_gpio_ctrl(GPIO_YMON);
   set_gpio_ctrl(GPIO_XPON);
   set_gpio_ctrl(GPIO_XMON);
#endif
  
   ADCDLY = 0x5000;  

   /* Enable touch interrupt */
   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 for touch screen interrupts */
   wait_down_int();//置等待触电IRQ_TC中断模式

#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


  
   /*  
   ADCTSC = (0<<8)|(1<<7)|(1<<6)|(0<<5)|(1<<4)|(0<<3)|(0<<2)|(3);
   ADCCON = (1<<14)|(49<<6)|(7<<3)|(0<<2)|(1<<1)|(0);
   */
   printk(DEVICE_NAME " initialized!/n");
  
   return 0;

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


/*exit*/
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);
   printk (DEVICE_NAME"s3c2410_ts_exit /n");
}

module_init(s3c2410_ts_init);
module_exit(s3c2410_ts_exit);

MODULE_LICENSE("GPL");

加几点个人见解便于大家理解程序

1、大家可能看到了在程序中有这么一段:

    interruptible_sleep_on(&(tsdev.wq));
   if (signal_pending(current))
      return -ERESTARTSYS;

signal_pending( current )――检查当前进程是否有信号处理,返回不为0表示有信号需要处理。
-ERESTARTSYS表示信号函数处理完毕后重新执行信号函数前的某个系统调用。
也就是说,如果信号函数前有发生系统调用,在调度用户信号函数之前,内核会检查系统调用的返回值,看看是不是因为这个信号而中断了系统调用.如果返回值- ERESTARTSYS,并且当前调度的信号具备-ERESTARTSYS属性,系统就会在用户信号函数返回之后再执行该系统调用。 

2、INCBUF(tsdev.tail, MAX_TS_BUF);

#define INCBUF(x,mod)     ((++(x)) & ((mod) - 1))

这里的INCBUF(x,mod) 就等同于   (x+1)%(mod-1)

3、(触摸屏的整个中断过程) 触摸屏在一次点击中中断发生的次数为四次,以下是按照中断发生的次序排列的.(两次INT_TC和两次INT_ADC)

       1、INT_TC中断发生(也就是触摸屏按下中断),此过程中首先判断if (tsdev.penStatus == PEN_UP)//即中断发生前 pen已经抬起,此时就是PEN_DOWN,然后调用start_ts_adc()进行x坐标转换前的准备工作,由于IRQ_TC是快速中断(不可打断),IRQ_ADC在IRQ_TC中断执行完成后触发。

       2、INT_ADC中断发生,阶段一已经做好了x坐标转换的准备,这次中断还是判断if (tsdev.penStatus == PEN_UP)//中断发生前 pen已经抬起,此时就是PEN_DOWN,若成立,调用s3c2410_get_XY()开始坐标转换,在阶段一中设置了adc_state=0,故首先开始x坐标转换。

       3、INT_ADC中断再次触发(两次坐标转换嘛,这里本人搞的不是太懂,可能INT_ADC设置的是边缘触发的方式吧?觉得有问题的欢迎指出).在阶段二中已经设置了adc_state=1,故这一次将转换坐标y.在这个过程中还将penStatus 设置成了PEN_DOWN;

       4、INT_TC中断再次触发(笔抬起中断),阶段3中将penStatus设置了PEN_DOWN,现在将执行tsdev.penStatus = PEN_UP;    wait_down_int();        tsEvent(); tsEvent()函数实际调用tsEvent_raw函数,在此函数中当penStatus==PEN_DOWN时,它会完成缓冲区的填充,等待等列的唤醒和异步通知信号的释放;当penStatus==PEN_UP时,它会将缓冲区头清零,也唤醒等待队列并释放异步通知信号。

转载注明出处:http://hi.baidu.com/luv%5Fresplendent/blog/item/183c841f23e992cea78669f8.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、 实验目的 1.了解S3C2410的通用I/0接口 2.掌握I/0功能的复用并熟练的配置,进行编程实验 二、 实验内容 1.在实验箱的CPU 板上点亮LED 灯LED1、LED2,并轮流闪烁! 三、 实验设备 1.EL-ARM-860教学实验箱,PentiumII以上的PC机,仿真调试电缆。 2.PC操作系统WIN98或WIN2000或WINXP,ADS1.2集成开发环境,仿真调试驱动程序。 四、 实验原理 S3C2410 CPU共有117个多功能复用输入输出口,分为8组端口: ·4个16位的I/0端口(PORT C、PORT D、PORT E、PORT G) ·2个11位的I/0端口(PORT B和 PORT H) ·1个8位的I/0端口(PORT F) ·1个23位的I/0端口(PORT A) 这些通用的GPI/0接口,是可配置的,PORTA除功能口外,它们仅用作输出使用,剩下的 PORTB、PORTC、PORTD、PORTE、PORTF、PORTG均可作为输入输出口使用。 配置这些端口,是通过一些寄存器来实现的,这些寄存器均有各自的地址,位长 32位。 往该地址中写入相应的数据,即可实现功能及数据配置。 GPACON (Ox56000000)/ /Port A control GPADAT (Ox56000004) //Port A data GPBCON (Ox56000010)//Port B control GPBDAT (Ox56000014)/ /Port B data GPBUP(Ox56000018)//Pull-up control B GPCCON (Ox56000020)/ /Port C control GPCDAT (Ox56000024)/ /Port C data GPCUP (Ox56000028)//Pull-up control c GPDCON (Ox56000030)//Port D control GPDDAT (Ox56000034) //Port D data GPDUP (Ox56000038)//Pul1-up control D GPECON (Ox56000040)//Port E control GPEDAT (Ox56000044)//Port E data GPEUP(Ox56000048)//Pul1-up control E GPFCON (Ox56000050)//Port F control GPFDAT (Ox56000054)/ /Port F data GPFUP(Ox56000058)//Pull-up control F GPGCON (Ox56000060)/ /Port G control GPGDAT (Ox56000064)//Port G data GPGUP (Ox56000068)//Pul1-up control G GPHCON (Ox56000070)/ /Port H control GPHDAT (Ox56000074)//Port H data GPHUP (Ox56000078)//Pull-up control H 现用G口、H口举例说明。对于G口如表1、表2、表3, 表1 表2 也就是说,在地址Ox0x56000060中,给32位的每一位赋值,那么,在CPU的管脚上就定义了管脚的功能值。当G口某管脚配置成输出端口,则在 PDATG对应的地址中的对应位上,写入1,则该管脚输出为高电平,写入0,则该管脚输出为低电平。若配置为功能管脚,则该管脚变成具体的功能脚。 对于H口见表4、表5、表6、表7 表6 也就是说,在地址Ox56000070中,给32位的每一位赋值,那么,在CPU的管脚上就定义了管脚的功能值。当H口某管脚配置成输入端口,则在 GPHDAT对应的地址中的对应位上,得到1,则该管脚的输入为高电平,得到0,则该管脚的输入为低电平。当H口某管脚配置成输出端口,则在GPGDAT对应的地址中的对应位上,写入1,则该管脚输出为高电平,写入0,则该管脚输出为低电平。若配置为功能管脚,则该管脚变成具体的功能脚。其他端口配置请参见PDF文挡。 在程序中对GPI/0各寄存器的读写实现,是通过给宏赋值实现的。这些宏在2410addr.h中定义;具体如:。 #define rGPACON (* (volatile unsigned *)Ox56000000)(//Port A control #define rGPADAT (*
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值