我是靠谱客的博主 呆萌舞蹈,这篇文章主要介绍(4)NUC980----GPIO驱动,现在分享给大家,希望可以做个参考。

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

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

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

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

复制代码
1
# 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,说明它们使用同一份驱动:

复制代码
1
# ls -l /dev/ttyS*

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

复制代码
1
# cat /proc/devices

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

头文件:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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函数实现
 

复制代码
1
2
3
4
5
6
7
8
9
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函数进行操作。

复制代码
1
2
3
4
5
6
7
long qgpio_ioctl(struct file *filp,unsigned int cmd,unsigned long arg) {undefined     //需要执行的代码 }

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

复制代码
1
2
3
4
5
6
7
8
9
struct file_operations qgpio_fops = {undefined     .owner = THIS_MODULE,     .open = qgpio_open,     .unlocked_ioctl = qgpio_ioctl, };

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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.许可证
 

复制代码
1
MODULE_LICENSE ("GPL");

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

ioctl对指令的解析

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;

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

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
   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;

自动注销设备号和节点
 

复制代码
1
2
3
4
5
6
7
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驱动内容请搜索靠谱客的其他文章。

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

评论列表共有 0 条评论

立即
投稿
返回
顶部