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!\n", 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!\n");   
        return err;
    }
    
    printk("init ok\n");    
    return 0;
    
}

static void __exit gpio_interrupt_exit(void)
{
    cdev_del(&my_cdev);
    unregister_chrdev_region(gpio_int_num, 1);
    printk("exit ok\n");   
}

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!\n");
        return -1;
    }
    printk("hello irq\n");
    return 0;
    
}

int gpio_interrupt_close(struct inode *inode,struct file *file)
{
    free_irq(my_gpio_key.eint, NULL);
    printk("good bye irq\n");
    return 0;
}

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

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

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。