NUC972----GPIO驱动
详细资料请加QQ群索取
欢迎加入qq交流群:669495872
上一节中所实现的最小驱动模块,实现了一个驱动的加载与卸载,但是这个最小驱动除了打印字符串以外,没有任何卵用。本节将通过驱动实例——GPIO驱动,来具体讲解一个有实际作用的驱动到底是怎么写出来的。
1 .Linux设备和分类
在进行驱动编程之前,需要了解一下Linux设备的分类。Linux 系统中的设备可以分为字符设备、块设备和网络设备这 3 类。
1字符设备:
字符设备是能够像字节流一样被访问的设备,当对字符设备发出读写请求,相应的 I/O 操作立即发生。Linux 系统中很多设备都是字符设备,如字符终端、串口、键盘、鼠标等。在嵌入式 Linux 开发中,接触最多的就是字符设备以及驱动。
2块设备:
块设备是 Linux 系统中进行 I/O 操作时必须以块为单位进行访问的设备,块设备能够安装文件系统。块设备驱动会利用一块系统内存作为缓冲区,因此对块设备发出读写访问,并不一定立即产生硬件 I/O 操作。Linux 系统中常见的块设备有如硬盘、软驱等等。
3网络设备:
网络设备既可以是网卡这样的硬件设备,也可以是一个纯软件设备如回环设备。网络设备由 Linux 的网络子系统驱动,负责数据包的发送和接收,而不是面向流设备,因此在Linux系统文件系统中网络设备没有节点。对网络设备的访问是通过socket调用产生,而不是普通的文件操作。
我们要写的GPIO驱动就属于字符设备。
2. 设备节点和设备号
2.1设备节点
设备(包括硬件设备)在 Linux 系统下,表现为设备节点,也称设备文件。设备文件是一种特殊的文件,它们存储在文件系统中(通常在/dev 目录下),但它们仅占用文件目录项而不涉及存储数据。事实上,它们仅仅记录了其所属的设备类别、主设备号和从设备号等设备相关信息。
来看两个典型的设备文件的详细信息:
# ls -l /dev/ttyS0 /dev/sda1
以/dev/ttyS0 的信息为例,对其中几项进行说明。
/dev/ttyS0 是设备节点名称,c 表示该设备是字符设备,主设备号为 4从设备号为 64,该设备节点对应于系统的串口 0。由于网络设备没有设备节点,所以设备文件基本上就分为字符设备文件和块设备文件两类,在设备节点属性中,分别以 c 和 b 来表示,即 c表示字符设备节点文件,b 表示块设备节点文件。
当程序打开一个设备文件时,内核就可以获取对应设备的设备类型、主设备号和次设备号等信息,内核也就知道了程序需要操作使用哪个设备驱动程序。在程序随后对这个文件的操作都会调用相应的驱动程序的函数,同时把从设备号传递给驱动程序。
2.2设备编号
设备编号由主设备号和从设备号构成。在 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
从设备号也称次设备号,用于确定该设备文件所指定的设备。如果一个设备驱动可以驱动一组相似的设备,此时就需要依赖于次设备号对这些外设进行区分。
获取一个设备的设备编号,应当使用<linux/kdev_t.h>中定义的宏,而不应当对设备号的位数和表述结构做任何假设,因为这样会导致不兼容以前的内核,或者未来版本设备号结构和表述方式发生变化。
3. 开始编写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) { //需要执行的代码 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) { //需要执行的代码 } |
4.fops定义
fops定义主要作用是将我们写的各项函数接口告知给内核驱动。比如说,驱动的open函数是qgpio_open(),可以这样表达:.open = qgpio_open,
struct file_operations qgpio_fops = { .owner = THIS_MODULE, .open = qgpio_open, .unlocked_ioctl = qgpio_ioctl, }; |
5.init函数和exit函数
关于模块的初始化,和模块的退出,8.2节已经介绍过了,这里就不再赘述。
static int __init qgpio_init (void) { //需要执行的函数 return 0; } static void __exit qgpio_exit (void) { //需要执行的函数 } 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) { 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 { result = alloc_chrdev_region(&devno, 0, 2, "qgpio"); QGPIO_MAJOR = MAJOR(devno); } printk("MAJOR IS %d\n",QGPIO_MAJOR); |
my_class = class_create(THIS_MODULE,"qgpio_class"); //类名 if(IS_ERR(my_class)) { printk("Err: failed in creating class.\n"); return -1; } device_create(my_class,NULL,devno,NULL,"qgpio"); //设备名 if (result<0) { printk (KERN_WARNING "hello: can't get major number %d\n", 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 Registered\n"); 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 up\n"); |
NUC972----GPIO驱动相关推荐
- 树莓派GPIO驱动原理
1.前言 最近认真学习了树莓派,从浅到深认真分析了wiringPi实现代码,借助树莓派学习linux收获颇丰.深入学习linux一段时间后发现它非常有魅力,一个简单的IO口输出操作尽有那么多的&quo ...
- gpio驱动广播Android,[RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED【转】
Platform: ROCKCHIP OS: Android 6.0 Kernel: 3.10.92 由于板子没有lcd无法得知sd卡升级是否完成,因此使用LED显示. Recovery中升级完成后控 ...
- 【WinCE】流设备驱动简介及GPIO驱动的实现
流设备驱动实际上就是导出标准的流接口函数的驱动,这是文档上面的定义.在WinCE中,所有的流设备都导出流设备接口,这样WinCE中的Device Manager可以加载和管理这些流设备驱动. 流设备 ...
- 自己动手写TCC7901的GPIO驱动
//===================================================================== //TITLE: // 自己动手写TCC7901的 ...
- WinCE流设备驱动简介及GPIO驱动的实现
作者:ARM-WinCE 流设备驱动实际上就是导出标准的流接口函数的驱动,这是文档上面的定义.在WinCE中,所有的流设备都导出流设备接口,这样WinCE中的Device Manager可以加载和管理 ...
- 我的内核学习笔记10:Intel GPIO驱动源码分析
本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...
- pcDuino的linux移植五GPIO驱动开发
2019独角兽企业重金招聘Python工程师标准>>> 为首的亮,灭.同时如果你GPIO4,GPIO5接个LED,也会跟着亮,灭. 开发环境: 系统:Ubuntu的 一,硬件介绍 仔 ...
- 树莓派linux下gpio驱动,树莓派官方自带gpio驱动bcm2708_gpio.c原理分析 linux gpio架构 gpio子系统...
对树莓派gpio的操作有好多方法,比如mmap映射cpu内存,编写内核驱动模块等.这里推荐一篇文章外链网址已屏蔽 其实树莓派官方使用linux内核gpio驱动框架内置了一个驱动,让我们可以直接使用标准 ...
- NUC972触摸屏驱动移植过程分析(三)
如果有问题,请加QQ群 891339868 进行交流 今天继续分析NUC972触摸屏驱动,今天咱们详细说一下具体的TSC2007这个芯片的驱动代码.其实这段代码并不十分复杂,主要先搞清楚以下几点: 第 ...
- STC8H开发(十五): GPIO驱动Ci24R1无线模块
目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...
最新文章
- 计算机基础算法棋盘覆盖,分治算法求解棋盘覆盖问题互动教学过程.doc
- grabcut.cpp:380: error: (-215) !bgdSamples.empty() !fgdSamples.empty() in function initGMMs
- 《全栈性能Jmeter》-4JMeter脚本开发
- 史上最快的拼接字串方法
- Android ndk使用
- Html5中新增的表单元素详解
- Mysql Replication 之 GTID 实战
- 【转】No Persistence provider for EntityManager问题
- so运行出错:只包含了头文件,未同时编译cpp
- python写字典_用python编写字典并将其写入HDF5-fi
- 微信小程序链接快速生成方法
- Windows安装宝塔后跳过绑定手机号
- 飞轮效应中的复利:相信时间的力量
- Python视频字幕识别——pytesseract
- plsql免oracle客户端配置,PLSQL DEVELOPER 配置免安装ORACLE客户端
- Day3—HTML个人简历制作及五彩导航练习
- 测试问卷星问卷公开的问题
- navicat 表中文显示? 解决
- 今天我教大家用js制作一个简单的计算器
- 电化学工作站的原理与应用