精华内容
下载资源
问答
  • 触摸屏设备驱动程序

    2015-03-16 18:43:44
    触摸屏设备驱动程序 由于触摸屏设备简单、价格低廉,到处应用 在消费电子商品、工业控制系统、甚至航空领域都有应用   触摸屏作为一种最新的电脑输入设备,是目前最简单、方便、自然的的一种人机交互方式,具有...

    触摸屏设备驱动程序

    由于触摸屏设备简单、价格低廉,到处应用

    在消费电子商品、工业控制系统、甚至航空领域都有应用

     

    触摸屏作为一种最新的电脑输入设备,是目前最简单、方便、自然的的一种人机交互方式,具有坚固耐用、反应速度快、节省空间、易于交流等许多优点。

    事实上,触摸屏是一个使多媒体信息系统改头换面的设备,它赋予多媒体系统以崭新的面貌,是极富有吸引力的全新多媒体交互设备

     

    从技术原理来区别触摸屏,可分为5类:

    1,矢量压力传感技术触摸屏

    2,电阻式触摸屏

    3,电容式触摸屏

    4,红外线技术触摸屏

    5,表面声波技术触摸屏

     

    矢量压力传感技术触摸屏已经退出历史舞台

    红外线技术触摸屏价格低廉,但其外框易碎,容易产生光干扰,曲面情况下失真

    电容技术触摸屏设计构思合理,但其图像失真问题很难得到解决

    电阻技术触摸屏的定位准确,但其价格颇高,且怕刮易损

    表面声波触摸屏解决了以往触摸屏的各种缺陷,清晰且不容易被破坏,适用于各种场合,缺点是:屏幕表面如果有水滴和尘土会使触摸屏变得迟钝

     

    电阻式触摸屏的屏体部分是一块与显示器表面相匹配的多层复合薄膜,由一层玻璃或有机玻璃作为底层,表面涂有一层透明的导电层,上面再盖有一层外表面硬化处理、光滑防刮的塑料层,它的内表面也涂有一层透明导电层,在两层导电层之间有很多细小(小于千分之一英寸)的透明隔离点把它们隔开绝缘。

    四线触摸屏包含两个阻性层。其中一层在屏幕的左右边缘各有一条垂直总线,另一层在屏幕的底部和顶部各有一条水平总线,见图4。为了在X轴方向进行测量,将左侧总线偏置为0V,右侧总线偏置为VREF。将顶部或底部总线连接到ADC,当顶层和底层相接触时即可作一次测量。   为了在Y轴方向进行测量,将顶部总线偏置为VREF,底部总线偏置为0V。将ADC输入端接左 侧总线或右侧总线,当顶层与底层相接触时即可对电压进行测量。图5显示了四线触摸屏在两层相接触时的简化模型。对于四线触摸屏,最理想的连接方法是将偏置 为VREF的总线接ADC的正参考输入端,并将设置为0V的总线接ADC的负参考输入端。

     

    s3c2440芯片支持触摸屏接口。这个触摸屏接口包括一个外部晶体管控制逻辑和一个模数转换器ADC,s3c2440芯片具有一个8通道的10位CMOS模数转换器(ADC)。2.5MHz下A/D转换器可达500KSPS,A/D支持片上采样和保持功能,并支持掉电模式

    s3c2440触摸屏特点:

    1,分辨率,10位

    2,微分线性误差:1.0LSB

    3,积分线性误差:2.0LSB

    4,最大转换速率:500KSPS

    5,低功耗

    6,供电电压:3.3V

    7,输入模拟电压范围:0~3.3V

    8,片上采样保持功能

    9,普通转换模式

    10,分离X/Y轴坐标转换模式

    11,自动(连续)X/Y轴坐标转换模式

    12,等待中断模式

     

    S3C2440触摸屏4种工作模式:

    1,正常转换模式

               在不使用触摸屏设备时,可以单独使用触摸屏接口中共用的模数转换器ADC。这这种模式下,可以通过设置ADCCON寄存器启动普通的A/D转韩,当转换结束时,结果被写到ADCCDAT0寄存器中

    2,等待中断模式

                当设置触摸屏接口控制器的ADCTSC寄存器为0XD3时,触摸屏就处于等待中断模式。这时触摸屏等待触摸信号的到来。当触摸信号到来时,触摸屏接口控制器将通过INT_TC线产生中断信号,表示有触摸动作发生。当中断发生,触摸屏可以转换为其他两种状态来读取触摸点的位置(x,y)。这两种模式是独立的X/Y位置转换模式和自动X/Y位置转换模式

    3,独立X/Y位置转换模式

                独立的X/Y位置转换模式由两个子模式组成,分别是X位置模式和Y位置模式。X位置模式将转换后的X坐标写到ADCDAT0寄存器的XPDATA位。转换后,触摸屏接口控制器会通过INT_ADC中断线产生中断信号,由中断处理函数来处理。Y位置模式将转换后的Y坐标写到ADCDAT1寄存器的YPDATA位。同样转换后,触摸屏接口控制器会通过INT_ADC中断线产生中断信号,由中断处理函数来处理

    4,自动X/Y位置转化模式

                 这种模式触摸屏控制器自动转换X位置和Y位置。当位置转换后,模式触摸屏接口控制器自动写转换后的X坐标写到ADCDAT0寄存器的XPDATA位;写转换后的Y坐标到ADCDAT1寄存器的YPDATA位。当转换完后,触摸屏接口控制器会通过INT_AC中断行产生中断信号

     

    寄存器:

    ADCCON:模数转换控制寄存器,用于控制AD转换、是否使用分频、设置分频系数、读取AD转换器的状态

    ADCDLY:ADC延时寄存器,用于正常模式下和等待中断模式下的延时操作

    ADCDAT0:ADC转换数据寄存器0,用于存储触摸屏的点击状态、工作模式、X坐标等

    ADCDAT1:ADC转换数据寄存器1,用于存储触摸屏的点击状态、工作模式、Y坐标等

    注意:ADCDAT0和ADCDAT1的第15位,表示X和Y方向上检测到的触摸屏是否被按下。只有当ADCDAT0和ADCDAT1寄存器的第15位,即两个寄存器的15位都等于0时,才表示触摸屏被按下,或者有触笔点击触摸屏。

    ADCTSC:ADC触摸屏控制寄存器,用于控制触摸屏的YMON,nYPON,nXPON和XMON等状态

     

     

    触摸屏设备驱动程序

    struct s3c2410ts {
        struct input_dev dev;
        long xp;
        long yp;
        int count;
        int shift;
        char phys[32];
    };

    触摸屏设备驱动程序的初始化函数、退出函数、和中断处理函数关系如图:

    1,当模块加载时,会调用初始化函数s3c2410ts_init()。在该函数中会调用probe()函数,该函数中会进一步调用request_irq()函数注册两个中断。这两个中断的处理函数分别是stylus_updown()和stylus_action()。request_irq()函数会操作内核中的一个中断描述符数组结构irq_dest。该数组结构比较复杂,主要功能就是记录中断号对应的中断处理函数

    2,当中断到来时,会到中断描述符数组中询问中断号对应的处理函数,然后执行该函数。在本例中,这两个中断的处理函数分别是stylus_updown()和stylus_action()。

    3,卸载模块时,会调用退出函数s3c2410ts_exit()。在该函数中,会调用free_irq()来释放设备所使用的中断号。free_irq()函数也会操作中断描述符数组结构irq_desc,将该设备对应的中断处理函数删除

    4,中断处理函数stylus_updown()中会调用touch_timer_fire()。这个函数也被定时器触发,触发的条件是,缓冲区中的数据不为0,也就是有触摸时间产生

     

    加载和卸载函数:

    int __init s3c2410ts_init(void)                    //加载函数
    {
        return driver_register(&s3c2410ts_driver);           //注册一个触摸屏设备驱动程序,之后内核会以s3c2410ts_driver中的name成员为依据,在系统查找已经注册的相同name的设备。如果 找到相应的设备,就调用s3c2410ts_driver中定义的探测函数probe()。driver_register()函数是Linux设备驱动 模型中最常用的函数
    }

    void __exit s3c2410ts_exit(void)                  //卸载函数
    {
        driver_unregister(&s3c2410ts_driver);           //卸载触摸屏设备驱动程序
    }

     

    下面来看看导火线:s3c2410ts_driver:

    static struct device_driver s3c2410ts_driver = {
        .name           = "s3c2410-ts",                   //名字
        .bus            = &platform_bus_type,        //总线
        .probe          = s3c2410ts_probe,             //探测函数,该函数在模块加载函数完成且设备匹配成功后执行
        .remove         = s3c2410ts_remove,          //移除函数
    };

     

    probe()对应的s3c2410ts_probe()。这个函数在触摸屏设备的初始化过程中,检查设备是否准备就绪、映射物理地址到虚拟地址、配置GPIO引脚、注册相应的中断和注册输入设备等,大多数Linux设备驱动程序中,当执行完模块加载后,就执行probe():

    static int __init s3c2410ts_probe(struct device *dev)
    {
        struct s3c2410_ts_mach_info *info;                                                    //定义了触摸屏接口相关硬件的配置信息结构体指针
        
        info = ( struct s3c2410_ts_mach_info *)dev->platform_data;               //从device的平台数据dev->platform_data中获得指向struct s3c2410_ts_match_info结构体的指针
        
        if (!info)                           //说明没有平台数据
        {
            printk(KERN_ERR "Hm... too bad : no platform data for ts\n");
            return -EINVAL;
        }
        
    #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG             //如果内核配置了调试信息选项,则打印调试信息
        printk(DEBUG_LVL "Entering s3c2410ts_init\n");
    #endif
        
        adc_clock = clk_get(NULL, "adc");                                  //获得ADC的时钟源,并赋值给adc_clock指针
        if (!adc_clock) {                       //如果没有正确的获得ADC时钟源,错误退出
            printk(KERN_ERR "failed to get adc clock source\n");
            return -ENOENT;
        }
        clk_use(adc_clock);                                                     //增加时钟源的使用计数
        clk_enable(adc_clock);                                                //启动ADC时钟源
        
    #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG                     //配置调试信息选项的话打印调试信息
        printk(DEBUG_LVL "got and enabled clock\n");
    #endif
        
        base_addr=ioremap(S3C2410_PA_ADC,0x20);                             //把一个ADC控制寄存器的物理地址内存映射到一个虚拟地址。这段被映射的长度是0x20。

                                                                                                           #define S3C2410_PA_ADC       (0x58000000)   //这是触摸屏一组寄存器的首地址

        if (base_addr == NULL) {                        //映射失败退出
            printk(KERN_ERR "Failed to remap register block\n");
            return -ENOMEM;
        }
        
        
        /* Configure GPIOs */
        s3c2410_ts_connect();                                  //配置处理器的GPIO。这个函数配置端口G的12,13,14,15引脚为XMON,nXPON,YMON,nPON。
        /* Set the prscvl*/
        if ((info->presc&0xff) > 0)                                   //设置AD转换的分频系数。如果info->presc的低8位的值大于0,那么配置ADCCON寄存器

                                                                                                位14:是否使用AD转换器的预分频功能,位6-13位共8位表示AD转换的预分频器的数值

            writel(S3C2410_ADCCON_PRSCEN | S3C2410_ADCCON_PRSCVL(info->presc&0xFF),\
                     base_addr+S3C2410_ADCCON);
        else
            writel(0,base_addr+S3C2410_ADCCON);                               //不使用预分频功能,将ADCCON寄存器各位都设置为0
        
        
        /* Initialise the adcdly registers */
        if ((info->delay&0xffff) > 0)                           //如果需要延时。写延时时间到ADCDLY寄存器
            writel(info->delay & 0xffff,  base_addr+S3C2410_ADCDLY);                    
        
        writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);                                 //写ADC触摸屏设备的ADCTSC寄存器,使触摸屏设备 处于等待中断模式                   
        
        /* Initialise input stuff */
        memset(&ts, 0, sizeof(struct s3c2410ts));                        //清零全局变量
        init_input_dev(&ts.dev);                                                //申请并初始化一个输入设备。通过这个输入设备,驱动程序才能和用户交互
        ts.dev.evbit[0] = ts.dev.evbit[0] = BIT(EV_SYN) | BIT(EV_KEY) | BIT(EV_ABS);                             //下面是初始化ts变量
        ts.dev.keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
        input_set_abs_params(&ts.dev, ABS_X, 0, 0x3FF, 0, 0);
        input_set_abs_params(&ts.dev, ABS_Y, 0, 0x3FF, 0, 0);
        input_set_abs_params(&ts.dev, ABS_PRESSURE, 0, 1, 0, 0);
        
        sprintf(ts.phys, "ts0");
        
        ts.dev.private = &ts;
        ts.dev.name = s3c2410ts_name;
        ts.dev.phys = ts.phys;
        ts.dev.id.bustype = BUS_RS232;
        ts.dev.id.vendor = 0xDEAD;
        ts.dev.id.product = 0xBEEF;
        ts.dev.id.version = S3C2410TSVERSION;
        
        ts.shift = info->oversampling_shift;
        
        /* Get irqs */
        if (request_irq(IRQ_ADC, stylus_action, SA_SAMPLE_RANDOM,
            "s3c2410_action", &ts.dev)) {                                  //申请ADC中断,并设置中断处理函数为stylus_action
            printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_ADC !\n");
            iounmap(base_addr);
            return -EIO;
        }
        if (request_irq(IRQ_TC, stylus_updown, SA_SAMPLE_RANDOM,
            "s3c2410_action", &ts.dev)) {                                  //申请触摸屏中断,并设置中断处理函数为stylus_updown
            printk(KERN_ERR "s3c2410_ts.c: Could not allocate ts IRQ_TC !\n");
            iounmap(base_addr);
            return -EIO;
        }
        
        printk(KERN_INFO "%s successfully loaded\n", s3c2410ts_name);
        
        /* All went ok, so register to the input system */
        input_register_device(&ts.dev);                            //将触摸屏设备注册到输入子系统中
        
        return 0;
    }

     

    s3c2410_ts_mach_info是s3c2410触摸屏接口相关硬件的配置信息

    struct  s3c2410_ts_mach_info

    {

                int delay;                          //延时时间

                int  presc;                        //预分频值

                int  oversampling_shift;    //输入数据的缓冲区大小

    };

     

    下面介绍触摸屏设备配置,具体的硬件知识,请参考相关资料:

    s3c2410_ts_connect()用来处理GPG端口12,13,14,15引脚,设置为与触摸屏相关的状态。这里分别设为XMON,nXPON,YMON,和nYOPN状态:

    static inline void s3c2410_ts_connect(void)
    {
        s3c2410_gpio_cfgpin(S3C2410_GPG12, S3C2410_GPG12_XMON);          //GPG的端口12引脚设置为读写触摸屏设备XM引脚的值,EINT【20】连接到触摸屏XM引脚上
        s3c2410_gpio_cfgpin(S3C2410_GPG13, S3C2410_GPG13_nXPON);         //GPG的端口13引脚设置为读写触摸屏设备nXPON引脚的值,EINT[21]连接到触摸屏的nXPON上
        s3c2410_gpio_cfgpin(S3C2410_GPG14, S3C2410_GPG14_YMON);          //GPG的端口13引脚设置为读写触摸屏设备YMON引脚的值,EINT[22]连接到触摸屏的YMON上
        s3c2410_gpio_cfgpin(S3C2410_GPG15, S3C2410_GPG15_nYPON);         //GPG的端口14引脚设置为读写触摸屏设备nYPON引脚的值,EINT[23]连接到触摸屏的nYPON上
    }

    触摸屏设备中断处理函数

    当触摸屏设备驱动的探测函数执行完之后,驱动程序处于等待状态。在等待状态中,驱动程序可以接受两个中断信号,并触发中断处理函数。这两个中断是触摸屏中断(IRQ_TC)和ADC中断(IRQ_ADC)。

    stylus_updown():

    当触摸屏被按下时,会产生触摸中断信号IRQ_TC,该函数会激发stylus_updown()函数的调用:

    static irqreturn_t stylus_updown(int irq, void *dev_id, struct pt_regs *regs)
    {
        unsigned long data0;
        unsigned long data1;
        int updown;              //用来表示触摸屏是否被按下。按下是1,否则是0
        /********************************debug************************************/
        printk(KERN_INFO "You touch the screen\n");
        /*************************************************************************/
        data0 = readl(base_addr+S3C2410_ADCDAT0);               //读取ADCDATA0寄存器的值
        data1 = readl(base_addr+S3C2410_ADCDAT1);               //读取ADCDATA1寄存器的值
        
        updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));  //判断触摸笔是否被按下
        
        /* TODO we should never get an interrupt with updown set while
        * the timer is running, but maybe we ought to verify that the
        * timer isn't running anyways. */
        
        if (updown)                      //如果被按下,执行touch_timer_fire()
            touch_timer_fire(0);
        
        return IRQ_HANDLED;
    }

    touch_timer_fire()函数:继续处理触摸中断:

    static void touch_timer_fire(unsigned long data)
    {
        unsigned long data0;
        unsigned long data1;
        int updown;
        
        data0 = readl(base_addr+S3C2410_ADCDAT0);
        data1 = readl(base_addr+S3C2410_ADCDAT1);
        
        updown = (!(data0 & S3C2410_ADCDAT0_UPDOWN)) && (!(data1 & S3C2410_ADCDAT1_UPDOWN));      //上面的和stylus_updown()函数中前面的一样
        
        if (updown) {
            if (ts.count != 0) {                                         //用来向输入子系统报告当前触摸笔的位置
                ts.xp >>= ts.shift;
                ts.yp >>= ts.shift;
                
    #ifdef CONFIG_TOUCHSCREEN_S3C2410_DEBUG
                {
                    struct timeval tv;
                    do_gettimeofday(&tv);
                    printk(DEBUG_LVL "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
                    printk(KERN_INFO "T: %06d, X: %03ld, Y: %03ld\n", (int)tv.tv_usec, ts.xp, ts.yp);
                }
    #endif
                
                input_report_abs(&ts.dev, ABS_X, ts.xp);                  
                input_report_abs(&ts.dev, ABS_Y, ts.yp);
                
                input_report_key(&ts.dev, BTN_TOUCH, 1);
                input_report_abs(&ts.dev, ABS_PRESSURE, 1);
                input_sync(&ts.dev);
            }
            
            ts.xp = 0;                     //将x,y坐标和count设置为0,表示缓冲区中没有数据,也就是没有触屏按下的事件发生
            ts.yp = 0;
            ts.count = 0;
            
            writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);                                 //将AD转换模式设置为自动转模式

    //                                                                              #define S3C2410_ADCTSC_PULL_UP_DISABLE  (1 << 3)  表示将XP上拉,另外

    //                                                                                #define AUTOPST      (S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN 

    //                                                                                                                                                      |  S3C2410_ADCTSC_AUTO_PST | S3C2410_ADCTSC_XY_PST(0)

    ADCOPST宏主要任务是将AD转换器设置为自动XY坐标转换模式。S3C2410_ADCTSC_YM_SEN表示将YMON输出为1;S3C2410_ADCTSC_YP_SEN表示将nYPON输出为1;S3C2410_ADCTSC_XP_SEN表示将nXPOM输出为1;S3C2410_ADCTSC_ATUO_PST表示将ADCTSC寄存器的第二位设置为1,即将AD转换器设置为自动XY坐标转换模式。S3C2410_ADCTSC_XY_PST(0)为0,表示AD转换器没有任何操作


            writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);               //将ADCCON寄存器的第0位设置为1.其中S3C2410_ADCCON_ENABLE_START宏表示第0位为1.这句话的代码功能是启动AD转换功能,当启动后,ADCCON第0位自动设置为0
        } else {      //表示触摸屏没有被按下的操作。此时调用input_report_key()函数向输入子系统报告触摸屏被弹起事件。input_report_key()函数的第三个参数传递0,表示按键被释放。input_report_abs()函数发送触摸屏的一个绝对坐标。它们最终表现在文件系统中,应用程序可以访问Linux的文件系统,从而得到触摸屏的信息。事实上,不使用这两个函数驱动程序仍然能够控制触摸屏设备,但是需要通过其他方法访问触摸屏设备的状态
            ts.count = 0;
            
            input_report_key(&ts.dev, BTN_TOUCH, 0);
            input_report_abs(&ts.dev, ABS_PRESSURE, 0);
            input_sync(&ts.dev);        //通知事件发送者发送一个完整的报告。这里BTN_TOUCH和ABS_PRESSURE事件不能单独发送,必须同时发送
            
            writel(WAIT4INT(0), base_addr+S3C2410_ADCTSC);       //将触摸屏重新设置为等待中断模式,等待触摸屏被按下。这些工作是通过设置触摸屏控制寄存器ADCTSC来完成的。WAIT4INT是需要写入的ADCTSC寄存器的值,定义:

    //            #define   WAIT4INT(x)       (((x) << 8) | S3C2410_ADCTSC_YM_SEN | S3C2410_ADCTSC_YP_SEN | S3C2410_ADCTSC_XP_SEN  | S3C2410_ADCTSC_XY_PST(3))

        }
    }

     

    stylus_action函数:

    触摸屏设备产生的另一个中断是ADC中断(IRQ_ADC)。触摸屏在自动X/Y位置转换模式和独立的X/Y位置转换模式时,当坐标数据转换之后会产生IRQ_ADC中断。中断产生之后会调用stylus_action()函数:

    static irqreturn_t stylus_action(int irq, void *dev_id, struct pt_regs *regs)
    {
        unsigned long data0;
        unsigned long data1;
        data0 = readl(base_addr+S3C2410_ADCDAT0);
        data1 = readl(base_addr+S3C2410_ADCDAT1);
        
        ts.xp += data0 & S3C2410_ADCDAT0_XPDATA_MASK;   //获得触摸点的X坐标,把它加到ts.xp上。ADCDAT0寄存器的[0:9]表示X坐标。该坐标在ADC中断产生前存入ADCDAT0
        ts.yp += data1 & S3C2410_ADCDAT1_YPDATA_MASK;  //获得触摸点的Y坐标,把它加到ts.yp上。ADCDAT0寄存器的[0:9]表示Y坐标。该坐标在ADC中断产生前存入ADCDAT1
        ts.count++;                 //计数器加1
        
        if (ts.count < (1<<ts.shift)) {                //如果缓冲区为满,再次激活ADC转换器
            writel(S3C2410_ADCTSC_PULL_UP_DISABLE | AUTOPST, base_addr+S3C2410_ADCTSC);
            writel(readl(base_addr+S3C2410_ADCCON) | S3C2410_ADCCON_ENABLE_START, base_addr+S3C2410_ADCCON);
        } else {
            mod_timer(&touch_timer, jiffies+1);                          //修改定时器,将其时间延后一个单位。在下一个定时器时刻将调用touch_timer定时器指定 的函数
            writel(WAIT4INT(1), base_addr+S3C2410_ADCTSC);                  //将触摸屏设置为等待中断模式
        }
        
        return IRQ_HANDLED;
    }

     

    touch_timer定时器:

    touch_timer定时器用来当缓冲区不为空时,不断触发touch_timer_fire()函数。touch_timer_fire()函数读取触摸屏的坐标信息,并传递给内核子系统。

    全局变量:

    static struct timer_list touch_timer =
    TIMER_INITIALIZER(touch_timer_fire, 0, 0);

     

    s3c2440触摸屏驱动模块的remove()函数:
    remove()函数是Linux设备驱动程序中一个非常重要的函数,这个函数实现了与probe()函数相反的功能,体现了Linux内核中,资源分配和释放的思想 。资源应该在使用时分配,在不使用时释放。这个函数释放了申请的中断、时钟、内存。

    static int s3c2410ts_remove(struct device *dev)
    {
        disable_irq(IRQ_ADC);                      //关闭中断,并释放中断    #define IRQ_ADC    S3C2410_IRQ(70)
        disable_irq(IRQ_TC);                         //#define     IRQ_TC       (0x0)
        free_irq(IRQ_TC,&ts.dev);
        free_irq(IRQ_ADC,&ts.dev);
        
        if (adc_clock) {                                  //关闭并释放ADC时钟源
            clk_disable(adc_clock);
            clk_unuse(adc_clock);
            clk_put(adc_clock);
            adc_clock = NULL;
        }
        
        input_unregister_device(&ts.dev);        //注销触摸屏输入设备
        iounmap(base_addr);                           //释放映射的虚拟内存地址
        
        return 0;
    }

    有一种落差是,你配不上自己的野心,也辜负了所受的苦难

    展开全文
  • Linux设备驱动程序设计 实验目的 学习Linux下进行驱动程序设计的原理 掌握Linux设备驱动程序开发的基本过程和设计方法 实验内容 内核驱动设计入门模块方式驱动程序5.1 内核驱动设计实验触摸屏驱动5.2 写一个简单的...
  • 本文以触摸屏控制器ADS7843为例,介绍了其内部结构、工作原理,并详细阐述了在Linux操作系统的设备文件系统机制中编写设备驱动程序的过程与方法,同时给出基于PXA255微外理器的触摸屏控制器ADS7843中断方式的驱动程序。...
  • 一般来说,WinCE触摸屏驱动设计和实现有以下几个步骤:  (1)配置和初始化触摸屏  触摸屏驱动在初始化过程会调用TouchPanelEnable函数,该函数调用的DDSI函数为DdsiTouchPanelEnable和 DdsiTouchPanelDisable...
  • 简要介绍了Linux 设备驱动程序的概念、分类、基本工作原理和关键技术, 以及嵌入式系统中常用的电阻式触摸屏的组成和 工作原理。给出了基于嵌入式Linux 的触摸屏设备驱动程序设计和实现方法。
  • 本文介绍了基于三星S3C2410X微处理器,采用SPI接口与ADS7843触摸屏控制器芯片完成触摸屏模块的设计。具体包括在嵌入式Linux操作...设计完成的触摸屏驱动程序在博创公司教学实验设备UP-NETARM2410-S平台上运行效果良好。
  • 触摸屏驱动程序设计

    2015-08-27 19:00:36
    触摸屏作为一种输入设备,是目前最简单、方便的一种人机交互方式,具有坚固耐用、反应速度快、节省空间、易于交流等优点。

    触摸屏作为一种输入设备,是目前最简单、方便的一种人机交互方式,具有坚固耐用、反应速度快、节省空间、易于交流等优点。从技术原理来分,触摸屏可以分为以下几类:
    ①电阻式触摸屏
    ②电容式触摸屏
    ③红外线技术触摸屏
    ④表面声波技术触摸屏
    ⑤矢量压力传感技术触摸屏
    常用的有电阻式触摸屏和电容式触摸屏。


    电阻触摸屏结构图为:

    这里写图片描述

    电阻触摸屏是一块4层的透明的复合薄膜屏,触摸屏由两层塑料薄膜组成,各薄膜层上均涂有一层导电金属(通常是氧化铟锡),中间的空气间隙将二者分开。如上图所示,外面两层白色的,上面为软薄膜,下面白色为玻璃基板,中间两层分别为X、Y两个透明电极层。由上图可知,电阻触摸屏在最上面的触摸屏是软的,所以也称为软屏(相对于电容触摸屏为硬屏)。

    计算X,Y触点的坐标分为两步:
    ①计算Y坐标:在Y+端施加驱动电压VDD,Y-端接地,X+作为引出端测量得到的触点电压。由于电极层均匀导电,触点电压域VDD电压之比相当于触点Y坐标与屏高度之比。
    ②计算X坐标:在X+端施加驱动电压VDD,X-端接地,Y+作为引出端测量得到的触点电压。由于电极层均匀导电,触点电压域VDD电压之比相当于触点X坐标与屏宽度之比。

    相比于电阻屏,电容屏的优点是感应灵敏,支持多点触控,更适合现代产品的要求。电容屏是利用人体的电流感应进行工作的。电容式触摸屏是一块四层复合玻璃屏,玻璃屏的内表面和夹层各涂有一层ITO,最外层是一薄层矽土玻璃保护层,夹层ITO涂层作为工作面,四个角上引出四个电极,内层ITO为屏蔽层以保证良好的工作环境。当手指触摸在金属层上时,由于人体电场,用户和触摸屏表面形成一个耦合电容,对于高频电流来说,电容是直接导体,于是手指从接触点吸走一个很小的电流。这个电流分从触摸屏的四角上的电极中流出,并且流经这四个电极的电流与手指到四角的距离成正比,控制器通过对这四个电流比例的精确计算,得出触摸点的位置。


    驱动程序的设计步骤如下:

    这里写图片描述

    其中S3C2440支持的触摸屏中断模式有两种,一种是TC中断,即按下产生中断,一种是ADC中断,即完成AD转换后产生中断,这里采用的是TC中断。代码如下:

    int xdata,ydata;
    
    #define ADCCON    (*(volatile unsigned *)0x58000000)   //ADC control
    #define ADCTSC    (*(volatile unsigned *)0x58000004)   //ADC touch screen control
    #define ADCDLY    (*(volatile unsigned *)0x58000008)   //ADC start or Interval Delay
    #define ADCDAT0   (*(volatile unsigned *)0x5800000c)   //ADC conversion data 0
    #define ADCDAT1   (*(volatile unsigned *)0x58000010)   //ADC conversion data 1
    
    /*interrupt registes*/
    #define SRCPND              (*(volatile unsigned long *)0x4A000000)
    #define INTMSK              (*(volatile unsigned long *)0x4A000008)
    #define INTOFFSET           (*(volatile unsigned long *)0x4A000014)
    #define SUBSRCPND           (*(volatile unsigned long *)0x4A000018)
    #define INTSUBMSK           (*(volatile unsigned long *)0x4A00001c)
    #define INTPND              (*(volatile unsigned long *)0x4A000010)
    
    void ts_init()
    {
        //1. 设置AD转换时钟
        ADCCON = (1<<14) | (49<<6);
    
        //2. 设置中断屏蔽位
        INTMSK = ~(1<<31);
        INTSUBMSK = ~(1<<9);
    
        //3. 进入等待中断的模式
        ADCTSC = 0xd3;
    }
    
    void tc_handler()
    {
    
        //1. 启动xy坐标自动转换 
        ADCTSC = (1<<2);
        ADCCON |= (1<<0);
    
        //2. 等待转换完成
        while (!(ADCCON & (1<<15)))
    
        //3. 获取坐标
        xdata = ADCDAT0 & 0x3ff;
        ydata = ADCDAT1 & 0x3ff;
    
        //4. 清除按下中断
        SUBSRCPND |= (1<<9);
        SRCPND = (1<<31);
        INTPND = (1<<31); 
    
    
        //5. 进入等待弹起中断
        ADCTSC = 0xd3;
        ADCTSC |= (1<<8);
    
        while(1)
        {
            if(SUBSRCPND &(1<<9))   
                break;
        }
    
        //6. 清除弹起中断
        SUBSRCPND |= (1<<9);
        SRCPND = (1<<31);
        INTPND = (1<<31); 
    
        printf("x is %d,y is %d\n",xdata,ydata);
    
        //7. 再次进入等待按下中断的状态
        ADCTSC = 0xd3;
    }

    完成以上设置后还需要在中断判断中添加中断源判断,使程序遇到触摸屏中断时能判断出该中断是触摸屏的中断,并跳转到void tc_handler()执行代码。

    展开全文
  • 实验步骤-触摸屏驱动 测试驱动程序 驱动程序位于内核源代码目录下面的drivers/char/下面, 名称:s3c2410-ts-ads7843.o 装载驱动程序和建立设备文件节点 在将触摸屏驱动加载到内核中时,可以看到系统为触摸屏设备分配的...
  • 论文在介绍了嵌入式Linux下设备驱动层次结构、运行机制、编译平台方法以及字符设备驱动程序使用流程的基础上,针对Unity805plus此新型平台下键盘、触摸屏、LCD这三种人机交互设备提出了实际的驱动设计方案。...
  • linux系统下触摸屏驱动程序,源代码,用于学习和参考
  • 视频及资料链接地址:(上传中) ...提取码:qgox  一、linux驱动的分类 ...LED、KEY、BEEP、声卡、显卡、摄像头、鼠标、键盘、触摸屏、手写板、USB、..... [root@GEC6818 /]#ls /dev -l crw-rw---- 1...

    视频及资料链接地址:(上传中)

    链接:https://pan.baidu.com/s/1avZjYyQGr2ljfPhEZjPumA 
    提取码:qgox 


    一、linux驱动的分类
    1、字符设备驱动
    1)设备:
    LED、KEY、BEEP、声卡、显卡、摄像头、鼠标、键盘、触摸屏、手写板、USB、.....
    [root@GEC6818 /]#ls /dev -l
    crw-rw----    1 root     root       29,   0 Jan  1  1970 fb0
    crw-rw----    1 root     root       14,   3 Jan  1  1970 dsp
    crw-rw----    1 root     root       14,  19 Jan  1  1970 dsp1
    crw-rw----    1 root     root      204,  64 Jan  1  1970 ttySAC0
    crw-rw----    1 root     root      204,  65 Jan  1  1970 ttySAC1

    2)特点:
    设备类型是c。应用程序和驱动程序之间交互数据的时候,数据是以字节为单位,不同的设备类型,交互的数据格式不一样的。字符设备驱动数据是实时传递的,按照固定的格式传递。字符设备是没有缓存的,字符设备是没有文件系统的。

    3)应用程序访问方法
    linux系统IO函数:open()、read()、write()、ioctl()、close()、mmap()
    触摸屏:
    struct input_event ts_ev;
    int fd=open("/dev/input/event0", O_RDONLY);
    read(fd, &ts_ev, sizeof(struct input_event));
    close(fd);

    ---------------------------------------------------------------------------------------------------------

    2、块设备驱动
    1)设备:
    大容量的存储设备。如:eMMC(nand flash:8GB)、SD卡、U盘、移动硬盘、....
    [root@GEC6818 /]#ls /dev -l
    brw-rw----    1 root     root      179,   0 Jan  1  1970 mmcblk0
    brw-rw----    1 root     root      179,  16 Jan  1  1970 mmcblk0boot1
    brw-rw----    1 root     root      179,   1 Jan  1  1970 mmcblk0p1
    brw-rw----    1 root     root      179,   8 Jan  1  1970 mmcblk0boot0
    brw-rw----    1 root     root      179,   2 Jan  1  1970 mmcblk0p2
    brw-rw----    1 root     root      179,   4 Jan  1  1970 mmcblk0p4
    brw-rw----    1 root     root      179,   3 Jan  1  1970 mmcblk0p3
    brw-rw----    1 root     root      179,   5 Jan  1  1970 mmcblk0p5
    brw-rw----    1 root     root      179,   6 Jan  1  1970 mmcblk0p6
    brw-rw----    1 root     root      179,   7 Jan  1  1970 mmcblk0p7

    brw-rw-rw-    1 root     root        8,   0 Jan  1 00:06 sda
    brw-rw-rw-    1 root     root        8,   1 Jan  1 00:06 sda1


    2)特点:
    块设备是带有缓存的,当缓存满了(刷新缓存)这些数据才会写到块设备上去。块设备是有文件系统的(根文件系统的格式是ext4)。数据是以块(block)为为单位的,1block=1024B。
    3)应用程序访问方法
    例:
        U盘中有一个文件test.txt,编写一个程序,读取test.txt文件中的内容,并将该内容通过串口2发送出去。
    如何访问U盘?
    挂载-----将块设备以某一种文件系统的格式挂载到根文件系统的某个目录上,在根据该目录访问块设备。
    有些嵌入式平台可以自动挂载U盘(做了配置文件):
    [root@GEC6818 /]#mount
    sda1 on /mnt/udisk type vfat (rw,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro)
    U盘就自动挂载到/mnt/udisk/,文件系统的类型vfat。
    [root@GEC6818 /]#ls /mnt/udisk/
    1-STM32????                5-tiny210
    2-??????                   System Volume Information
    3-GEC6818                  ???????
    4-GEC210

    访问方法:
    fd = open("/mnt/udisk/test.txt", O_RDONLY);
    read()
    close()

    手动挂载:
    [root@GEC6818 /]#mount -t vfat /dev/sda1 /data
    [root@GEC6818 /]#ls /data

    如何访问串口
    fd = open("/dev/ttySAC2", O_RDWR);
    初始化串口2
    write(fd, buf, sizeof(buf));
    close(fd);

    -----------------------------------------------------------------------------------------------------
    3、网络设备驱动
    1)设备:网卡(有线网卡、无线网卡)
    如何查看网络设备??
    [root@GEC6818 /]#ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 0E:72:C9:E8:03:96  
              inet addr:192.168.22.180  Bcast:192.168.22.255  Mask:255.255.255.0
              inet6 addr: fe80::c72:c9ff:fee8:396/64 Scope:Link
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:0 (0.0 B)  TX bytes:238 (238.0 B)
              Interrupt:80 

    ip6tnl0   Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
              NOARP  MTU:1452  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    lo        Link encap:Local Loopback  
              LOOPBACK  MTU:16436  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    sit0      Link encap:IPv6-in-IPv4  
              NOARP  MTU:1480  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    2)特点:    
        网卡----物理层
        网络驱动-----数据链路层
        
    3)应用程序访问网络设备
    socket套接字:字节流---TCP;数据报---UDP;原始套接字---开发网络协议
                  地址信息: IP地址和端口号
    TCP的服务器
    socket()/bind()/listen()/accept()/read()/recv()/write()/send()/close()
    TCP的客户端
    socket()/connect()//read()/recv()/write()/send()/close()

    ======================================================================================================
    二、linux驱动模型
    以字符设备驱动为例。

    一、linux驱动的分类
    1、字符设备驱动
    1)设备:
    LED、KEY、BEEP、声卡、显卡、摄像头、鼠标、键盘、触摸屏、手写板、USB、.....
    [root@GEC6818 /]#ls /dev -l
    crw-rw----    1 root     root       29,   0 Jan  1  1970 fb0
    crw-rw----    1 root     root       14,   3 Jan  1  1970 dsp
    crw-rw----    1 root     root       14,  19 Jan  1  1970 dsp1
    crw-rw----    1 root     root      204,  64 Jan  1  1970 ttySAC0
    crw-rw----    1 root     root      204,  65 Jan  1  1970 ttySAC1
    crw-rw----    1 root     root      204,  66 Jan  1  1970 ttySAC2
    crw-rw----    1 root     root      204,  67 Jan  1  1970 ttySAC3

    2)特点:
    设备类型是c。应用程序和驱动程序之间交互数据的时候,数据是以字节为单位,不同的设备类型,交互的数据格式不一样的。字符设备驱动数据是实时传递的,按照固定的格式传递。字符设备是没有缓存的,字符设备是没有文件系统的。

    3)应用程序访问方法
    linux系统IO函数:open()、read()、write()、ioctl()、close()、mmap()
    触摸屏:
    struct input_event ts_ev;
    int fd=open("/dev/input/event0", O_RDONLY);
    read(fd, &ts_ev, sizeof(struct input_event));
    close(fd);

    ---------------------------------------------------------------------------------------------------------

    2、块设备驱动
    1)设备:
    大容量的存储设备。如:eMMC(nand flash:8GB)、SD卡、U盘、移动硬盘、....
    [root@GEC6818 /]#ls /dev -l
    brw-rw----    1 root     root      179,   0 Jan  1  1970 mmcblk0
    brw-rw----    1 root     root      179,  16 Jan  1  1970 mmcblk0boot1
    brw-rw----    1 root     root      179,   1 Jan  1  1970 mmcblk0p1
    brw-rw----    1 root     root      179,   8 Jan  1  1970 mmcblk0boot0
    brw-rw----    1 root     root      179,   2 Jan  1  1970 mmcblk0p2
    brw-rw----    1 root     root      179,   4 Jan  1  1970 mmcblk0p4
    brw-rw----    1 root     root      179,   3 Jan  1  1970 mmcblk0p3
    brw-rw----    1 root     root      179,   5 Jan  1  1970 mmcblk0p5
    brw-rw----    1 root     root      179,   6 Jan  1  1970 mmcblk0p6
    brw-rw----    1 root     root      179,   7 Jan  1  1970 mmcblk0p7

    brw-rw-rw-    1 root     root        8,   0 Jan  1 00:06 sda
    brw-rw-rw-    1 root     root        8,   1 Jan  1 00:06 sda1


    2)特点:
    块设备是带有缓存的,当缓存满了(刷新缓存)这些数据才会写到块设备上去。块设备是有文件系统的(根文件系统的格式是ext4)。数据是以块(block)为为单位的,1block=1024B。
    3)应用程序访问方法
    例:
        U盘中有一个文件test.txt,编写一个程序,读取test.txt文件中的内容,并将该内容通过串口2发送出去。
    如何访问U盘?
    挂载-----将块设备以某一种文件系统的格式挂载到根文件系统的某个目录上,在根据该目录访问块设备。
    有些嵌入式平台可以自动挂载U盘(做了配置文件):
    [root@GEC6818 /]#mount
    sda1 on /mnt/udisk type vfat (rw,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro)
    U盘就自动挂载到/mnt/udisk/,文件系统的类型vfat。
    [root@GEC6818 /]#ls /mnt/udisk/
    1-STM32????                5-tiny210
    2-??????                   System Volume Information
    3-GEC6818                  ???????
    4-GEC210

    访问方法:
    fd = open("/mnt/udisk/test.txt", O_RDONLY);
    read()
    close()

    手动挂载:
    [root@GEC6818 /]#mount -t vfat /dev/sda1 /data
    [root@GEC6818 /]#ls /data

    如何访问串口
    fd = open("/dev/ttySAC2", O_RDWR);
    初始化串口2
    write(fd, buf, sizeof(buf));
    close(fd);

    -----------------------------------------------------------------------------------------------------
    3、网络设备驱动
    1)设备:网卡(有线网卡、无线网卡)
    如何查看网络设备??
    [root@GEC6818 /]#ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 0E:72:C9:E8:03:96  
              inet addr:192.168.22.180  Bcast:192.168.22.255  Mask:255.255.255.0
              inet6 addr: fe80::c72:c9ff:fee8:396/64 Scope:Link
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:0 (0.0 B)  TX bytes:238 (238.0 B)
              Interrupt:80 

    ip6tnl0   Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
              NOARP  MTU:1452  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    lo        Link encap:Local Loopback  
              LOOPBACK  MTU:16436  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    sit0      Link encap:IPv6-in-IPv4  
              NOARP  MTU:1480  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    2)特点:    
        网卡----物理层
        网络驱动-----数据链路层
        
    3)应用程序访问网络设备
    socket套接字:字节流---TCP;数据报---UDP;原始套接字---开发网络协议
                  地址信息: IP地址和端口号
    TCP的服务器
    socket()/bind()/listen()/accept()/read()/recv()/write()/send()/close()
    TCP的客户端
    socket()/connect()//read()/recv()/write()/send()/close()

    ======================================================================================================
    二、linux驱动模型
    以字符设备驱动为例。

        
        

    一、linux驱动的分类
    1、字符设备驱动
    1)设备:
    LED、KEY、BEEP、声卡、显卡、摄像头、鼠标、键盘、触摸屏、手写板、USB、.....
    [root@GEC6818 /]#ls /dev -l
    crw-rw----    1 root     root       29,   0 Jan  1  1970 fb0
    crw-rw----    1 root     root       14,   3 Jan  1  1970 dsp
    crw-rw----    1 root     root       14,  19 Jan  1  1970 dsp1
    crw-rw----    1 root     root      204,  64 Jan  1  1970 ttySAC0
    crw-rw----    1 root     root      204,  65 Jan  1  1970 ttySAC1
    crw-rw----    1 root     root      204,  66 Jan  1  1970 ttySAC2
    crw-rw----    1 root     root      204,  67 Jan  1  1970 ttySAC3

    2)特点:
    设备类型是c。应用程序和驱动程序之间交互数据的时候,数据是以字节为单位,不同的设备类型,交互的数据格式不一样的。字符设备驱动数据是实时传递的,按照固定的格式传递。字符设备是没有缓存的,字符设备是没有文件系统的。

    3)应用程序访问方法
    linux系统IO函数:open()、read()、write()、ioctl()、close()、mmap()
    触摸屏:
    struct input_event ts_ev;
    int fd=open("/dev/input/event0", O_RDONLY);
    read(fd, &ts_ev, sizeof(struct input_event));
    close(fd);

    ---------------------------------------------------------------------------------------------------------

    2、块设备驱动
    1)设备:
    大容量的存储设备。如:eMMC(nand flash:8GB)、SD卡、U盘、移动硬盘、....
    [root@GEC6818 /]#ls /dev -l
    brw-rw----    1 root     root      179,   0 Jan  1  1970 mmcblk0
    brw-rw----    1 root     root      179,  16 Jan  1  1970 mmcblk0boot1
    brw-rw----    1 root     root      179,   1 Jan  1  1970 mmcblk0p1
    brw-rw----    1 root     root      179,   8 Jan  1  1970 mmcblk0boot0
    brw-rw----    1 root     root      179,   2 Jan  1  1970 mmcblk0p2
    brw-rw----    1 root     root      179,   4 Jan  1  1970 mmcblk0p4
    brw-rw----    1 root     root      179,   3 Jan  1  1970 mmcblk0p3
    brw-rw----    1 root     root      179,   5 Jan  1  1970 mmcblk0p5
    brw-rw----    1 root     root      179,   6 Jan  1  1970 mmcblk0p6
    brw-rw----    1 root     root      179,   7 Jan  1  1970 mmcblk0p7

    brw-rw-rw-    1 root     root        8,   0 Jan  1 00:06 sda
    brw-rw-rw-    1 root     root        8,   1 Jan  1 00:06 sda1


    2)特点:
    块设备是带有缓存的,当缓存满了(刷新缓存)这些数据才会写到块设备上去。块设备是有文件系统的(根文件系统的格式是ext4)。数据是以块(block)为为单位的,1block=1024B。
    3)应用程序访问方法
    例:
        U盘中有一个文件test.txt,编写一个程序,读取test.txt文件中的内容,并将该内容通过串口2发送出去。
    如何访问U盘?
    挂载-----将块设备以某一种文件系统的格式挂载到根文件系统的某个目录上,在根据该目录访问块设备。
    有些嵌入式平台可以自动挂载U盘(做了配置文件):
    [root@GEC6818 /]#mount
    sda1 on /mnt/udisk type vfat (rw,relatime,fmask=0000,dmask=0000,allow_utime=0022,codepage=cp437,iocharset=iso8859-1,shortname=mixed,utf8,errors=remount-ro)
    U盘就自动挂载到/mnt/udisk/,文件系统的类型vfat。
    [root@GEC6818 /]#ls /mnt/udisk/
    1-STM32????                5-tiny210
    2-??????                   System Volume Information
    3-GEC6818                  ???????
    4-GEC210

    访问方法:
    fd = open("/mnt/udisk/test.txt", O_RDONLY);
    read()
    close()

    手动挂载:
    [root@GEC6818 /]#mount -t vfat /dev/sda1 /data
    [root@GEC6818 /]#ls /data

    如何访问串口
    fd = open("/dev/ttySAC2", O_RDWR);
    初始化串口2
    write(fd, buf, sizeof(buf));
    close(fd);

    -----------------------------------------------------------------------------------------------------
    3、网络设备驱动
    1)设备:网卡(有线网卡、无线网卡)
    如何查看网络设备??
    [root@GEC6818 /]#ifconfig -a
    eth0      Link encap:Ethernet  HWaddr 0E:72:C9:E8:03:96  
              inet addr:192.168.22.180  Bcast:192.168.22.255  Mask:255.255.255.0
              inet6 addr: fe80::c72:c9ff:fee8:396/64 Scope:Link
              UP BROADCAST MULTICAST  MTU:1500  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:3 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:1000 
              RX bytes:0 (0.0 B)  TX bytes:238 (238.0 B)
              Interrupt:80 

    ip6tnl0   Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
              NOARP  MTU:1452  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    lo        Link encap:Local Loopback  
              LOOPBACK  MTU:16436  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    sit0      Link encap:IPv6-in-IPv4  
              NOARP  MTU:1480  Metric:1
              RX packets:0 errors:0 dropped:0 overruns:0 frame:0
              TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
              collisions:0 txqueuelen:0 
              RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)

    2)特点:    
        网卡----物理层
        网络驱动-----数据链路层
        
    3)应用程序访问网络设备
    socket套接字:字节流---TCP;数据报---UDP;原始套接字---开发网络协议
                  地址信息: IP地址和端口号
    TCP的服务器
    socket()/bind()/listen()/accept()/read()/recv()/write()/send()/close()
    TCP的客户端
    socket()/connect()//read()/recv()/write()/send()/close()

    ======================================================================================================
    二、linux驱动模型
    以字符设备驱动为例。

        
        
        

     

    ====================================================================================================
    一、移植设备驱动:
    (1)总体思路:搭建编译设备驱动环境:即编译linux内核,需要注意内核版本的选择-------->建立设备驱动demo:两个文件(xx.c,Makefile);注意Makefile编写格式,驱动程序
    也有框架------->下载到嵌入式设备安装,查询检测。

    (2)详细步骤:如下。

    ======================================================================================================
    二、linux内核模块是什么?
    linux kernel module----linux内核模块

    在linux内核中,每个驱动程序都是独立的module。每设计一个驱动程序,首先需要设计一个module。module相当于驱动程序的盒子。
    module会编译成ko文件。module可以安装,可以卸载。


    =======================================================================================================

    三、编译驱动的内核源码
    1.内核源码的版本要与目标平台上运行的linux内核的版本要一致
      GEC6818 :#uname -r
      内核源码:
        gec@ubuntu:~/6818GEC/kernel$ pwd
        /home/gec/6818GEC/kernel
        gec@ubuntu:~/6818GEC/kernel$ vi Makefile
       1 VERSION = 3                                                                
       2 PATCHLEVEL = 4
       3 SUBLEVEL = 39
       4 EXTRAVERSION =
       5 NAME = Saber-toothed Squirrel

    2.内核源码要针对目标平台正确的配置过:
        gec@ubuntu:~/6818GEC/kernel$ make menuconfig

        System Type  --->
            ARM system type (SLsiAP S5P6818)  ---> 

    3.内核源码要正确的编译过
    gec@ubuntu:~/6818GEC$ ls
    buildroot  GEC6818uboot  kernel  linux  mk  out  prebuilts  prototype  tools
    gec@ubuntu:~/6818GEC$ ./mk -k
    ===============================================================================


    四。设计一个最简单的module
    查看linux内核源码,内核源码中有驱动程序设计的案例。
    sourceinsight-----内核源码工程

    例子:
    /kernel/drivers/watchdog/nxp_wdt.c

    #include <linux/module.h>
    #include <linux/kernel.h>

    static int __init gec6818_led_init(void)
    {
        printk(KERN_WARNING "gec6818 led init\n");

        return 0;
    }


    static void __exit gec6818_led_exit(void)
    {
        printk(KERN_WARNING "gec6818 led exit");
    }

    //驱动程序的入口和出口
    module_init(gec6818_led_init);
    module_exit(gec6818_led_exit);

    //module的描述
    MODULE_AUTHOR("2196481172@qq.com");
    MODULE_DESCRIPTION("GEC6818 LED Device Driver");
    MODULE_LICENSE("GPL");
    MODULE_VERSION("V1.0");

    =======================================================================================================
    五、驱动程序的特点
    1、驱动程序是运行在linux内核中的,而应用程序是运行在linux的用户空间的。
    2、每个硬件都需要有一个驱动程序,驱动程序是独立的。在一个应用程序中可以访问多个驱动程序
        视频播放器 ---->显卡 + 声卡 + 鼠标
    3、应用程序是有入口函数的,main()是入口。而应用程序是没有出口。
       驱动程序有入口、有出口。
       入口函数:module_init(gec6818_led_init);
       出口函数:module_exit(gec6818_led_exit);
       安装驱动:
            #insmod  led_drv.ko ---->自动调用入口函数module_init()---->gec6818_led_init()--->从内核申请资源、向内核注册驱动、建立驱动模型、....
       卸载驱动:
            #rmmod led_drv.ko
            ---->自动调用出口函数module_exit()---->gec6818_led_exit()--->释放申请的资源,注销驱动。

    4、编写应用程序的时候,我们可以使用库函数和系统调用函数。
    应用程序使用的头文件是哪里来的:
    stdio.h  stdlib.h string.h   ------>
    gcc : /usr/include/stdio.h
    arm-linux-gcc : /usr/local/arm/5.4.0/usr/arm-none-linux-gnueabi/sysroot/usr/include/stdio.h
        编写驱动程序的时候,使用的头文件是哪里来的?
        linux/kernel.h  /linux/module.h   ------->
    来自于linux内核源码:/kernel/linux/module.h

    5、函数区别
    应用程序:printf() 、malloc()、sleep()
    驱动程序:printk()、kmalloc()、ssleep()
            
    6、static 的作用?
    应用程序:static修饰局部变量
    驱动程序:static修饰全局变量和函数

    7、__init和__exit关键字
    __init用来修饰初始化函数,一般情况下初始化函数只运行一次,运行结束以后,就会将该函数占用的内存释放掉。

    [    0.000000] Memory: 1024MB = 1024MB total
    [    0.000000] Memory: 810820k/810820k available, 237756k reserved, 272384K highmem
    ..............................
    [    0.000000]       .init : 0xc0a52000 - 0xc0a8f100   ( 245 kB)
    [    0.000000]       .data : 0xc0a90000 - 0xc0b297d8   ( 614 kB)
    ..............................

    [    4.218000] devtmpfs: mounted
    [    4.221000] Freeing init memory: 244K


    8、程序的编译
    应用程序:
        #arm-linux-gcc -o test test.c
    驱动程序:
        使用Makefile文件,并利用内核源码的Makefile和内核源码中的头文件来编译驱动程序。

    gec@ubuntu:/mnt/hgfs/14嵌入式系统驱动/2module/demo1$ ls
    led_drv.c  Makefile

    gec@ubuntu:/mnt/hgfs/14嵌入式系统驱动/2module/demo1$ make
    ------------------------------------------------------------------------------------------
    出错信息如果是:表明没有编译内核,请按照上面 “三、编译驱动的内核源码”编译内核即可,再次make
        make[1]: Entering directory '/home/wgh/6818GEC/kernel'

       ERROR: Kernel configuration is invalid.
             include/generated/autoconf.h or include/config/auto.conf are missing.
             Run 'make oldconfig && make prepare' on kernel src to fix it.


       WARNING: Symbol version dump /home/wgh/6818GEC/kernel/Module.symvers
               is missing; modules will have no dependencies and modversions.
    -------------------------------------------------------------------------------------------

    成功编译信息输出如下:
    make ARCH=arm CROSS_COMPILE=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi- -C /home/gec/6818GEC/kernel M=/mnt/hgfs/14嵌入式系统驱动/2module/demo1 modules
    make[1]: Entering directory '/home/gec/6818GEC/kernel'
      CC [M]  /mnt/hgfs/14嵌入式系统驱动/2module/demo1/led_drv.o
      Building modules, stage 2.
      MODPOST 1 modules
      CC      /mnt/hgfs/14嵌入式系统驱动/2module/demo1/led_drv.mod.o
      LD [M]  /mnt/hgfs/14嵌入式系统驱动/2module/demo1/led_drv.ko
    make[1]: Leaving directory '/home/gec/6818GEC/kernel'

    9、驱动程序安装好以后,驱动程序不是一直运行的;而是安装到内核中的一个程序,只有应用程序去调用驱动程序,这个驱动程序才开始工作。
    =======================================================================================================
    六、驱动的调试

    1.查看ko的信息:modinfo 
    gec@ubuntu:xxx/demo1$  modinfo led_drv.ko


    2、查看ko的格式
    gec@ubuntu:xxx/demo1$ file led_drv.ko

    3、下载 led_drv.ko 到开发板上

    4、安装驱动
    [root@GEC6818 /test]#insmod led_drv.ko
    [ 3488.195000] gec6818 led init

    5、查看安装好的ko
    [root@GEC6818 /test]#lsmod
    led_drv 748 0 - Live 0xbf000000 (O)

    6、卸载驱动
    [root@GEC6818 /test]#rmmod led_drv.ko 
    [ 3621.975000] gec6818 led exit

    ===============================================================================================================
    七、编译驱动的Makefile
    obj-m += led_drv.o
    KERNELDIR:=/home/gec/6818GEC/kernel
    CROSS_COMPILE:=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-

    PWD:=$(shell pwd)

    default:
        $(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules

    clean:
        rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions
        
    1)obj-m += led_drv.o 
    ----m是module的意思,+= 追加赋值。?= 或 := 区别。led_drv.o---驱动远程序的目标文件。

    2)KERNELDIR:=/home/gec/6818GEC/kernel 
    ----->内核源码的路径

    3)CROSS_COMPILE:=/home/gec/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
    -----交叉编译器的路径

    4)PWD:=$(shell pwd)
    -----当前路径,即编译驱动程序的Makefile文件所在的路径。

    5)$(MAKE) ARCH=arm CROSS_COMPILE=$(CROSS_COMPILE) -C $(KERNELDIR) M=$(PWD) modules
    去内核源码目录下,找到内核源码的Makefile文件,并将ARCH和CROSS_COMPILE变量传递给Makefile文件。利用内核源码的Makefile文件来编译当前路径下的驱动源文件,将驱动源文件编译成一个ko。

    ============================================================================================================


    九、printk()


        
        
        
        

    展开全文
  • 专题5-触摸屏驱动程序设计 第1课-输入子系统模型解析 为什么需要输入子系统 输入子系统模型介绍 输入子系统由设备驱动层(input device driver),核心层(input core)和事件驱动层(event driver)三部份...

    专题5-触摸屏驱动程序设计

    第1课-输入子系统模型解析

     

    1. 为什么需要输入子系统
    2. 输入子系统模型介绍

     

    输入子系统由设备驱动层(input device driver),核心层(input core)和事件驱动层(event driver)三部份组成。任何一次输入事件,如鼠标移动,按

    键按下,都需要通过InputDeviceDriver->InputCore->EventDrive才能到达用户空间的应用程序。

    l  设备驱动层:

    将底层的硬件输入转化为统一事件型式,向输入核心(InputCore)汇报。

    l  v输入核心层:

    为设备驱动层提供输入设备注册与操作接口,如:input_register_device;通知事件处理层对事件进行处理;

    l  事件驱动层:

    主要作用是和用户空间交互,如提供read,open等设备方法,创建设备文件等。

    1. 输入子系统案例分析

    输入型设备驱动;输入型按键驱动

     

    附录

    事件类型:

    EV_RST         Reset                        EV_KEY 按键

    EV_REL         相对坐标                     EV_ABS 绝对坐标

    EV_MSC        其它                         EV_LED LED

    EV_SND        声音

    EV_REP         Repeat

    EV_FF          力反馈

    当事件类型为EV_KEY时,还需指明按键类型:

    BTN_ LEFT:鼠标左键                      BTN_0:数字0键

    BTN_RIGHT:鼠标右键                     BTN_1:数字1键

     

    第2课-输入子系统原理分析

    1. 子系统核心架构

     

     

    1. 输入设备注册

    输入型设备注册

    1. 事件上报

    第3课-触摸屏驱动分析

    第4课-触摸屏驱动编程

    转载于:https://www.cnblogs.com/free-1122/p/11452274.html

    展开全文
  • CE系统与其它操作系统一样,也提供了设备驱动程序。以驱动内部或者外围硬件设备。驱动程序将操作系统和设备链接起来,使得操作系统能够识别设备,并为应用程序提供相应的服务。要想真正了解驱动程序必须结合一些驱动...
  • 一、我的学习理解: ...3、触发事件时,会上报给注册好的输入型设备,对应的handler会调用event函数,这个函数会把事件的信息打包后保存于buffer 4、用户层会访问到该字符设备的fops,从而就得到了
  • 国嵌培训课件Linux驱动程序设计

    热门讨论 2011-01-07 13:57:25
    2.字符设备驱动程序设计 3.驱动调试技术 4. 并发与竞态 第二天 1.Ioctl型驱动 2.内核等待队列 3. 阻塞型驱动程序设计 4.Poll设备操作 第三天 1.Mmap设备操作 2. 硬件访问 3. 混杂设备驱动 4. LED驱动程序...
  • CE系统与其它操作系统一样,也提供了设备驱动程序。以驱动内部或者外围硬件设备。驱动程序将操作系统和设备链接起来,使得操作系统能够识别设备,并为应用程序提供相应的服务。要想真正了解驱动程序必须结合一些驱动...
  • 转:基于S3C2410的触摸屏驱动程序设计

    千次阅读 2008-04-03 19:25:00
    摘要: 本文介绍了基于三星S3C2410X微处理器,采用SPI接口与ADS7843触摸屏控制器芯片完成触摸屏模块的设计。具体包括在嵌入式Linux...设计完成的触摸屏驱动程序在博创公司教学实验设备UP-NETARM2410-S平台上运行效果
  • 尽管触摸屏正在迅速普及开来,但大多数开发...本文将介绍如何编写软件驱动程序,从而能够使用这些微处理器配置、校准触摸屏以及对触摸屏输入持续响应。最终将提供可免费下载和使用的工作代码,作为读者进一步设计的基础
  • 网卡触摸屏驱动程序

    千次阅读 2014-05-07 13:47:38
    一、Linux网络体系架构 二、网卡驱动设计 三、Dm9000网卡驱动分析 四、Linux输入子系统 五、触摸屏驱动设计
  • 触摸屏驱动程序

    千次阅读 2014-02-26 21:53:30
    触摸屏工作原理: S3c2440触摸屏目标是获取X/Y坐标信息,坐标信息获取分两种: 1:X/Y位置分布转换模式,先获取其中一个坐标,再获取另外一个。 2:X/Y自动转换模式,两个坐标同时获取到。 工作流程 一:选择X/...
  • 嵌入式Linux设备驱动程序:在运行时读取驱动程序状态 Embedded Linux device drivers: Reading driver state at runtime 在运行时了解驱动程序 一旦有了一个正在运行的Linux系统,了解哪些设备驱动程序被加载以及...
  • 深入分析Windows CE.Net设备驱动程序的框架结构,详细介绍了分层驱动程序和流接口驱动程序的开发方法。通过研究Windows CE.Net内核及部分驱动程序的源代码,设计了操作系统的键盘驱动程序、触摸屏、LCD驱动、电源...
  • 1.国嵌的 /设备模型/国嵌内核驱动深入班5-2-4(platform驱动程序设计) 中讲有 2.mini2440的驱动程序没有是platform的,只有dm9000不过这个不是他们移植的 3.看视频不懂:难道platform都要有两个文件。像国嵌举例中的...
  •  本书是linux设备驱动程序开发领域的权威著作。全书基于2.6内核,不仅透彻讲解了基本概念和技术,更深入探讨了其他书没有涵盖或浅尝辄止的许多重要主题和关键难点,如pcmcia、i2c和usb等外部总线以及视频、音频、...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,774
精华内容 4,309
关键字:

触摸屏设备驱动程序设计