• linux设备驱动的学习之一


    由于项目上要用到,于是乎我要学习linux设备驱动的编写,开始的时候还比较清楚,能够对简单的GPIO控制操作实现出来,但是项目上要用到的是SPI和GPIO的输入中断来读取AD的电压值,然后就陷入到了一个庞大的设备代码阅读中去了,尤其是platform device的学习,到现在都还没有理清其中的关系,虽然搜索了很多网上的文章,但庆幸的是我有一种比着框框买鸭蛋的精神,我想要比着这些源码画一个出来。以前没有在LPC1768上使用过SPI,导致对SPI是一个完全陌生的状态,不清楚他的传输方式,这也是学习中的一个问题,也是一开始我的盲目无方向感的原因,因为这里的linux SPI设备驱动和SPI协议就是两个要学习的问题。

    先来把“简单”的中断实现出来吧,其实中断处理并不简单,他是很多项目中必须要用到的东西,这里使用的S5PV210的GPH3(2)这一个GPIO来实现,查看芯片手册其对应的外部中断号为EINT26,所以在驱动中定义一个结构体来描述他如下:

    struct s5pv210_gpio_key{
        int pin;//引脚号
        int eint;//外部中断号
        int eintcfg;//外部中断使能
        int inputcfg;//输入使能
    };
    struct s5pv210_gpio_key my_gpio_key={    
        .pin = S5PV210_GPH3(2), 
        .eintcfg = 0X0f<<4,      
        .inputcfg = 0<<4,
        .eint = IRQ_EINT(26),    
    };

    然后构建一个驱动的框架代码:

    static int __init gpio_interrupt_init(void)
    {
        int err=0;
        gpio_int_num = MKDEV(MY_MAJOR,MY_MINOR);
        err = register_chrdev_region(gpio_int_num, 1, "GPH3_2_interrupt");
        if(err < 0)
        {
            printk("register error, num: %d have been used!
    ", gpio_int_num);
            return err;
        }
        cdev_init(&my_cdev , &gpio_interrupt_ops); 
        my_cdev.owner = THIS_MODULE;
        err = cdev_add(&my_cdev, gpio_int_num, 1);
        if(err < 0)
        {
            printk("add my_cdev error!
    ");   
            return err;
        }
        
        printk("init ok
    ");    
        return 0;
        
    }
    
    static void __exit gpio_interrupt_exit(void)
    {
        cdev_del(&my_cdev);
        unregister_chrdev_region(gpio_int_num, 1);
        printk("exit ok
    ");   
    }
    
    module_init(gpio_interrupt_init);
    module_exit(gpio_interrupt_exit);
    
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("galuo");

     在加载模块的函数中,register_chrdev_region注册了一个设备号,在加载了该模块后,使用mknod命令来创建设备文件节点;然后使用cdev_init来初始化字符设备结构体变量cdev,将file_operations结构体gpio_interrupt_ops加载进来,就可以在用户空间使用的时候明确调用的关系;使用cdev_add向内核注册字符I/O设备。字符模块的退出函数使用cdev_del函数来删除内核中的字符设备;使用unregister_chrdev_region来释放设备号。

    定义file_operations结构体实例gpio_interrupt_ops,在用户空间操作的时候实现调用对应的功能,其定义如下:

    struct file_operations gpio_interrupt_ops={
        .open=gpio_interrupt_open,
        .release=gpio_interrupt_close,
        
    };

    .open实现中断的请求,并且使能中断

    .release实现中断的释放

    int gpio_interrupt_open(struct inode *inode,struct file *file)
    {
        int error;
        error = request_irq(my_gpio_key.eint, //中断号
                            gpio_keys_isr,//中断处理函数
                            IRQF_TRIGGER_FALLING ,//下降沿触发
                            "interrupt_test",
                            NULL);
        if(error){
            printk("request irq failed!
    ");
            return -1;
        }
        printk("hello irq
    ");
        return 0;
        
    }
    
    int gpio_interrupt_close(struct inode *inode,struct file *file)
    {
        free_irq(my_gpio_key.eint, NULL);
        printk("good bye irq
    ");
        return 0;
    }

    在中断处理函数gpio_keys_isr中就可以实现中断的需求:

    static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
    {
        printk("this is interrupt function
    ");
        
        return IRQ_HANDLED;
    }

    这里中断处理函数的定义类型和返回值固定不变。

  • 相关阅读:
    01.网页学习阶段、整站分析、规划
    书签搬运
    如何判断两个链表相交及找到第一个相交点
    Windows平台使用git bash管理github中的工程
    二级指针的操作
    结构体的内存对齐
    大端和小端
    剑指Offer——面试题26:复杂链表的复制
    使用editcap命令将ERF格式转换为pcap格式
    如何在STL的map中使用结构体作为键值
  • 原文地址:https://www.cnblogs.com/galuo/p/4111576.html
Copyright © 2020-2023  润新知