zoukankan      html  css  js  c++  java
  • 《驱动学习

    1.查看原理图和芯片手册

     

     

    2.驱动程序分析

      2.1 init函数和exit函数,向内核注册file_operations结构体。并且创建设备信息

    static struct class *thirddrv_class;
    static struct device    *thirddrv_class_dev;
    
    static struct file_operations sencod_drv_fops = {
        .owner   =  THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
        .open    =  third_drv_open,     
        .read     =    third_drv_read,       
        .release =  third_drv_close,       
    };
    
    
    int major;
    static int third_drv_init(void)
    {
        major = register_chrdev(0, "third_drv", &sencod_drv_fops);
    
        thirddrv_class = class_create(THIS_MODULE, "third_drv");
    
        thirddrv_class_dev = device_create(thirddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
        return 0;
    }
    
    static void third_drv_exit(void)
    {
        unregister_chrdev(major, "third_drv");
        device_unregister(thirddrv_class_dev);
        class_destroy(thirddrv_class);
        return 0;
    }
    
    
    module_init(third_drv_init);
    
    module_exit(third_drv_exit);
    
    MODULE_LICENSE("GPL");

      

      2.2 编写open函数

    struct pin_desc{
        unsigned int pin;
        unsigned int key_val;
    };
    
    /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
    /* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
    static unsigned char key_val;
    
    /*
     * K1,K2,K3,K4对应GPG0,GPG3,GPG5,GPG6
     */
    
    struct pin_desc pins_desc[4] = {
        {S3C2410_GPG(0), 0x01},
        {S3C2410_GPG(3), 0x02},
        {S3C2410_GPG(5), 0x03},
        {S3C2410_GPG(6), 0x04},
    };
    
    static int third_drv_open(struct inode *inode, struct file *file)
    {
        /* GPG0,GPG3,GPG5,GPG6为中断引脚: EINT8,EINT11,EINT13,EINT14 */
        request_irq(IRQ_EINT8,  buttons_irq, IRQ_TYPE_EDGE_BOTH, "K1", &pins_desc[0]);
        request_irq(IRQ_EINT11, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K2", &pins_desc[1]);
        request_irq(IRQ_EINT13, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K3", &pins_desc[2]);
        request_irq(IRQ_EINT14, buttons_irq, IRQ_TYPE_EDGE_BOTH, "K4", &pins_desc[3]);    
    
        return 0;
    }

      request_irq函数用于向内核申请中断。

      第一个参数:要注册中断服务函数的中断号

      第二个参数:中断服务函数

      第三个参数:触发中断的参数,比如边沿触发。这里是双边沿触发,定义在include/linux/irq.h

      第四个参数:中断程序的名字,使用cat /proc/interrupt 可以查看中断程序名字

      第五个参数:传入中断处理程序的参数,注册共享中断时不能为NULL,因为卸载时需要这个做参数,避免卸载其它中断服务函数

      2.3 close函数

    int third_drv_close(struct inode *inode, struct file *file)
    {
        free_irq(IRQ_EINT8,  &pins_desc[0]);
        free_irq(IRQ_EINT11, &pins_desc[1]);
        free_irq(IRQ_EINT13, &pins_desc[2]);
        free_irq(IRQ_EINT14, &pins_desc[3]);
        return 0;
    }

      free_irq函数:释放分配给已定中断的内存

      第一个参数:要卸载的中断号

      第二个参数:这个是要卸载的中断action下的哪个服务函数

      2.4 read函数

    static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
    
    /* 中断事件标志, 中断服务程序将它置1,third_drv_read将它清0 */
    static volatile int ev_press = 0;
    
    ssize_t third_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
    {
        if (size != 1)
            return -EINVAL;
    
        /* 如果没有按键动作, 休眠 */
        wait_event_interruptible(button_waitq, ev_press);
    
        /* 如果有按键动作, 返回键值 */
        copy_to_user(buf, &key_val, 1);
        ev_press = 0;
        
        return 1;
    }

      DECLARE_WAIT_QUEUE_HEAD是一个宏定义,来定义一个

      struct __wait_queue_head {
         spinlock_t lock;
         struct list_head task_list;
      };

      等待队列头的结构体。后面用于进程的休眠和唤醒。

      wait_event_interruptible(button_waitq, ev_press)函数:当第二个参数为0时,就会使应用程序进入休眠。

      2.5 中断服务函数

    static irqreturn_t buttons_irq(int irq, void *dev_id)
    {
        struct pin_desc * pindesc = (struct pin_desc *)dev_id;
        unsigned int pinval;
        
        pinval = s3c2410_gpio_getpin(pindesc->pin);
    
        if (pinval)
        {
            /* 松开 */
            key_val = 0x80 | pindesc->key_val;
        }
        else
        {
            /* 按下 */
            key_val = pindesc->key_val;
        }
    
        ev_press = 1;                  /* 表示中断发生了 */
        wake_up_interruptible(&button_waitq);   /* 唤醒休眠的进程 */
    
        
        return IRQ_RETVAL(IRQ_HANDLED);
    }

      如果是由IRQ_EXIT8触发中断,第一个参数irq就是IRQ_EXIT8,第二个参数就是pins_desc[0]的地址 

      当触发中断时,就会进入中断服务函数。然后将ev_press设置为1,然后唤醒应用程序。这样就又会进入read函数,然后将数据发送给应用程序,再将ev_press设置为0,这样应用程序就又休眠。

  • 相关阅读:
    为什么 "auto a = 1;" 在C语言中可以编译通过?
    谈谈duilib
    软工第一次作业
    数独_个人项目
    统计Github项目信息
    字符串中的匹配之递归
    软工第0次作业
    c++浅拷贝与深拷贝(LeetCode669)
    修改xcode初始生成代码
    树上处理的问题总结
  • 原文地址:https://www.cnblogs.com/zhuangquan/p/11623591.html
Copyright © 2011-2022 走看看