我是靠谱客的博主 呆萌舞蹈,最近开发中收集的这篇文章主要介绍(4)NUC980----GPIO驱动,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

上一节中所实现的最小驱动模块,实现了一个驱动的加载与卸载,。本节将通过驱动实例——GPIO驱动,来具体讲解一个有实际作用的驱动到底是怎么写出来的。

最简单的驱动开始(字符设备)
字符设备是能够像字节流一样被访问的设备,当对字符设备发出读写请求,相应的 I/O 操作立即发生。Linux 系统中很多设备都是字符设备,如字符终端、串口、键盘、鼠标等。在嵌入式 Linux 开发中,接触最多的就是字符设备以及驱动。

设备节点和设备号
设备节点
设备(包括硬件设备)在 Linux 系统下,表现为设备节点,也称设备文件。设备文件是一种特殊的文件,它们存储在文件系统中(通常在/dev 目录下),但它们仅占用文件目录项而不涉及存储数据。事实上,它们仅仅记录了其所属的设备类别、主设备号和从设备号等设备相关信息。

来看两个典型的设备文件的详细信息:

# ls -l /dev/ttyS0 /dev/sda1

 以/dev/ttyS0 的信息为例,对其中几项进行说明。

当程序打开一个设备文件时,内核就可以获取对应设备的设备类型、主设备号和次设备号等信息,内核也就知道了程序需要操作使用哪个设备驱动程序。在程序随后对这个文件的操作都会调用相应的驱动程序的函数,同时把从设备号传递给驱动程序。

设备编号
设备编号由主设备号和从设备号构成。在 Linux 内核中,使用 dev_t 类型来保存设备编号。在Linux 内核中,dev_t 是一个 32 位数,高 12 位是主设备号,低 20 位是次设备号。

主设备号标识设备对应的驱动程序,告诉 Linux 内核使用哪个驱动程序驱动该设备。如果多个设备使用同一个驱动程序,则它们拥有相同的主设备号。例如/dev/ttyS0~3、/dev/ttyS8 这 5 个设备,拥有相同的主设备号 4,说明它们使用同一份驱动:

# ls -l /dev/ttyS*

主设备号由系统来维护,尽管 Linux 可以容纳大量的设备,但是在使用主设备号的时候,注意一定不要使用系统已经使用的主设备号。一般来说,231~239 这几个设备号是系统没有分配的,用户可以自行安排使用。当前运行系统占用了哪些主设备号,可通过查看/proc/devices 文件得到。例如:

# cat /proc/devices

开始编写GPIO驱动
    GPIO驱动可以分为以下几个组成部分:

头文件:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>

#include <linux/io.h>
#include <linux/errno.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
#include <mach/gpio.h>
#include <linux/clk.h>

#include <mach/map.h>
#include <mach/regs-gpio.h>
#include <mach/regs-clock.h>
#include <mach/regs-gcr.h>
#include <mach/irqs.h>


#include <linux/miscdevice.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <linux/regulator/consumer.h>
#include <linux/delay.h>

2.open函数实现
 

int qgpio_open(struct inode *inode,struct file *filp)

{undefined

    //需要执行的代码

    return nonseekable_open(inode,filp);

}

当应用使用open()函数打开驱动时,驱动内部会自动调用这个qgpio_open函数,一般情况下,open函数里面放该驱动的初始化代码,因为应用只会在程序开头调用一次open()函数。

3.ioctl函数
字符型设备驱动通讯接口函数一般有read()、write()和ioctl()。read()函数为读取函数,一般读取一个数组,参数为数组首地址和数组长度。write()函数为写入函数,一般写入一个数组,参数为数组首地址和数组长度。ioctl()可读可写,但是一次只能读或者写一个32位的数据。根据GPIO的操作特性,我们选择ioctl函数进行操作。

long qgpio_ioctl(struct file *filp,unsigned int cmd,unsigned long arg)

{undefined

    //需要执行的代码

}

4.fops定义
fops定义主要作用是将我们写的各项函数接口告知给内核驱动。比如说,驱动的open函数是qgpio_open(),可以这样表达:.open   = qgpio_open,

struct file_operations qgpio_fops = {undefined

    .owner = THIS_MODULE,

    .open = qgpio_open,

    .unlocked_ioctl = qgpio_ioctl,

};

5.init函数和exit函数
  关于模块的初始化,和模块的退出,8.2节已经介绍过了,这里就不再赘述。

static int __init qgpio_init (void)

{undefined

   //需要执行的函数

    return 0;

}
static void __exit qgpio_exit (void)

{undefined

    //需要执行的函数

}
module_init (qgpio_init);
module_exit (qgpio_exit);

6.许可证
 

MODULE_LICENSE ("GPL");

GPIO硬件操作
nuc972_gpio.c中提供了GPIO操作的函数接口,其编程方式可裸机编程大致相同,这里就不做说明,详情可以参考裸机开发手册。

ioctl对指令的解析

  unsigned short iocmd;
  unsigned short portx;
  iocmd=cmd/0xff;
  portx=cmd%0xff;
  switch(iocmd)
  {undefined
     case 1:
       set_gpio_direction(portx,arg);
     break;
     case 2:
       return get_gpio_direction(portx);
     break;
     case 3:
       set_gpio_odr(portx,arg);
     break;
     case 4:
       return get_gpio_odr(portx);
     break;
     case 5:
       return get_gpio_idr(portx);
     break;
     default:
         return -EINVAL;
  }
  return 0;

自动获得设备号和设备节点

    int result;
    devno = MKDEV(QGPIO_MAJOR, QGPIO_MINOR);
    if (QGPIO_MAJOR)
        result = register_chrdev_region(devno, 2, "qgpio");
    else
    {undefined
        result = alloc_chrdev_region(&devno, 0, 2, "qgpio");
        QGPIO_MAJOR = MAJOR(devno);
    } 
    printk("MAJOR IS %dn",QGPIO_MAJOR);
    my_class = class_create(THIS_MODULE,"qgpio_class");  //类名
    if(IS_ERR(my_class))
    {undefined
        printk("Err: failed in creating class.n");
        return -1;
    }
    device_create(my_class,NULL,devno,NULL,"qgpio");      //设备名
    if (result<0)
    {undefined
        printk (KERN_WARNING "hello: can't get major number %dn", QGPIO_MAJOR);
        return result;
    }

    cdev_init(&cdev, &qgpio_fops);
    cdev.owner = THIS_MODULE;
    cdev_add(&cdev, devno, NUMBER_OF_DEVICES);
    printk (KERN_INFO "qgpio driver Registeredn");
    return 0;

自动注销设备号和节点
 

cdev_del (&cdev);
device_destroy(my_class, devno);
//delete device node under /dev必须先删除设备,再删除class类
class_destroy(my_class);
//delete class created by us
unregister_chrdev_region (devno,NUMBER_OF_DEVICES);
printk (KERN_INFO "char driver cleaned upn");

最后

以上就是呆萌舞蹈为你收集整理的(4)NUC980----GPIO驱动的全部内容,希望文章能够帮你解决(4)NUC980----GPIO驱动所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部