详细资料请加QQ群索取

欢迎加入qq交流群:669495872

上一节中所实现的最小驱动模块,实现了一个驱动的加载与卸载,但是这个最小驱动除了打印字符串以外,没有任何卵用。本节将通过驱动实例——GPIO驱动,来具体讲解一个有实际作用的驱动到底是怎么写出来的。

1 .Linux设备和分类

在进行驱动编程之前,需要了解一下Linux设备的分类。Linux 系统中的设备可以分为字符设备、块设备和网络设备这 3 类。

  1. 1字符设备:

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

  1. 2块设备:

块设备是 Linux 系统中进行 I/O 操作时必须以块为单位进行访问的设备,块设备能够安装文件系统。块设备驱动会利用一块系统内存作为缓冲区,因此对块设备发出读写访问,并不一定立即产生硬件 I/O 操作。Linux 系统中常见的块设备有如硬盘、软驱等等。

  1. 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驱动可以分为以下几个组成部分:

  1. 头文件:

#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驱动相关推荐

  1. 树莓派GPIO驱动原理

    1.前言 最近认真学习了树莓派,从浅到深认真分析了wiringPi实现代码,借助树莓派学习linux收获颇丰.深入学习linux一段时间后发现它非常有魅力,一个简单的IO口输出操作尽有那么多的&quo ...

  2. gpio驱动广播Android,[RK3288][Android6.0] 调试笔记 --- 通用GPIO驱动控制LED【转】

    Platform: ROCKCHIP OS: Android 6.0 Kernel: 3.10.92 由于板子没有lcd无法得知sd卡升级是否完成,因此使用LED显示. Recovery中升级完成后控 ...

  3. 【WinCE】流设备驱动简介及GPIO驱动的实现

    流设备驱动实际上就是导出标准的流接口函数的驱动,这是文档上面的定义.在WinCE中,所有的流设备都导出流设备接口,这样WinCE中的Device Manager可以加载和管理这些流设备驱动.  流设备 ...

  4. 自己动手写TCC7901的GPIO驱动

    //===================================================================== //TITLE: //    自己动手写TCC7901的 ...

  5. WinCE流设备驱动简介及GPIO驱动的实现

    作者:ARM-WinCE 流设备驱动实际上就是导出标准的流接口函数的驱动,这是文档上面的定义.在WinCE中,所有的流设备都导出流设备接口,这样WinCE中的Device Manager可以加载和管理 ...

  6. 我的内核学习笔记10:Intel GPIO驱动源码分析

    本文对Intel e3800的GPIO驱动源码进行分析. 一.概述 1.1 内核配置 Intel e3800的GPIO在Linux内核中使用的驱动名为gpio_ich(为了行文方便,将对应的设备称为& ...

  7. pcDuino的linux移植五GPIO驱动开发

    2019独角兽企业重金招聘Python工程师标准>>> 为首的亮,灭.同时如果你GPIO4,GPIO5接个LED,也会跟着亮,灭. 开发环境: 系统:Ubuntu的 一,硬件介绍 仔 ...

  8. 树莓派linux下gpio驱动,树莓派官方自带gpio驱动bcm2708_gpio.c原理分析 linux gpio架构 gpio子系统...

    对树莓派gpio的操作有好多方法,比如mmap映射cpu内存,编写内核驱动模块等.这里推荐一篇文章外链网址已屏蔽 其实树莓派官方使用linux内核gpio驱动框架内置了一个驱动,让我们可以直接使用标准 ...

  9. NUC972触摸屏驱动移植过程分析(三)

    如果有问题,请加QQ群 891339868 进行交流 今天继续分析NUC972触摸屏驱动,今天咱们详细说一下具体的TSC2007这个芯片的驱动代码.其实这段代码并不十分复杂,主要先搞清楚以下几点: 第 ...

  10. STC8H开发(十五): GPIO驱动Ci24R1无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

最新文章

  1. 计算机基础算法棋盘覆盖,分治算法求解棋盘覆盖问题互动教学过程.doc
  2. grabcut.cpp:380: error: (-215) !bgdSamples.empty() !fgdSamples.empty() in function initGMMs
  3. 《全栈性能Jmeter》-4JMeter脚本开发
  4. 史上最快的拼接字串方法
  5. Android ndk使用
  6. Html5中新增的表单元素详解
  7. Mysql Replication 之 GTID 实战
  8. 【转】No Persistence provider for EntityManager问题
  9. so运行出错:只包含了头文件,未同时编译cpp
  10. python写字典_用python编写字典并将其写入HDF5-fi
  11. 微信小程序链接快速生成方法
  12. Windows安装宝塔后跳过绑定手机号
  13. 飞轮效应中的复利:相信时间的力量
  14. Python视频字幕识别——pytesseract
  15. plsql免oracle客户端配置,PLSQL DEVELOPER 配置免安装ORACLE客户端
  16. Day3—HTML个人简历制作及五彩导航练习
  17. 测试问卷星问卷公开的问题
  18. navicat 表中文显示? 解决
  19. 今天我教大家用js制作一个简单的计算器
  20. 电化学工作站的原理与应用

热门文章

  1. Kettle拓展——迁移和装载之Excel输出
  2. ETC系统组成部分 ETC+OBU+RUS缺一不可
  3. 一图说明如何修改VisualStudio工程的配置管理器界面宽度
  4. C与C++游戏项目练习9:接金币游戏简易版
  5. 信息收集---zoomeye
  6. sojson本地反调试原理解析
  7. 如何在技术上实现室内导航
  8. 功能测试必备:抓包工具之谷歌开发者工具介绍及使用场景
  9. ubuntu配置ohmyzsh
  10. 设计模式入门篇——EIT造型