Linux字符设备驱动模型之点亮LED灯

发布时间:2017-4-9 10:45    发布者:FWW7
在前面的文章中,已经描述了整一个字符设备模型,那现在,可以来使用相应的模型进行操作硬件设备了。那么,从点亮一个LED灯开始。
一、设备控制操作
1.理论基础
大部分驱动程序除了需要提供读写设备的能力外,还需要具备控制设备的能力。比如设置UART波特率这样的操作。
实际上在Linux系统用户空间中,提供了ioctl系统调用函数来实现了控制设备的操作。其原型如下:
int ioctl(int fd,unsigned long cmd,...)
·fd: 要控制的设备文件描述符
·cmd: 发送给设备的控制命令
·…: 3个参数是可选的参数,存在与否是依赖于控
制命令(2 个参数 )
当应用程序在用户空间使用ioctl系统调用时,驱动程序将由如下函数来响应:
(1)Linux 2.6.36版本之前的内核
long (*ioctl) (struct inode* node ,struct file* filp, unsigned int cmd,unsigned long arg)
(2)Linux 2.6.36版本之后的内核
long (*unlocked_ioctl) (struct file *filp, unsigned int cmd, unsigned long arg);
·参数cmd: 通过应用函数ioctl传递下来的命令
此函数方法由struct file_operations操作函数集实现,如下图:
在此笔者所使用的是3.0.8版本的Linux内核。
2.控制方法实现
(1)定义命令
命令从其实质而言就是一个整数, 但为了让这个整数具备更好的可读性,我们通常会把这个整数分为几个段:类型(8位),序号,参数传送方向,参数长度
·Type(类型/幻数): 表明这是属于哪个设备的命令。
·Number(序号):用来区分同一设备的不同命令。
·Direction:参数传送的方向,可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE(向设备写入参数)。
·Size:参数长度
Linux系统提供了下面的宏来帮助定义命令:
·_IO(type,nr):不带参数的命令
·_IOR(type,nr,datatype):从设备中读参数的命令
·_IOW(type,nr,datatype):向设备写入参数的命令
示例:
#define MEM_MAGIC m//定义幻数
#define MEM_SET _IOW(MEM_MAGIC, 0, int)
(2)实现操作
Linux内核操作函数集中的unlocked_ioctl函数的实现通常是根据命令执行的一个switch语句。但是,当命令号不能匹配任何一个设备所支持的命令时,返回-EINVAL.
二、Linux内核空间操作硬件设备
1.确定硬件接口
在操作一个硬件设备前,必须要了解的是其所使用的硬件接口和硬件原理。在此笔者所使用的是S5PV210平台。
如上图可知,LED1LED2CPUGPC0_3GPC0_4这两个I/O口控制,并且GPIO口工作在输出模式时,当输出高电平时,灯亮;输出低电平时,灯灭。
如上图为GPC0口的控制寄存器GPC0CON(地址为0xe0200060),分析可知,当将[19:12]位配置为[00010001]时,GPC0_3GPC0_4口工作在输出模式下。
如上图为GPC0口的数据寄存器GPC0DAT0XE0200064),当I/O口工作在输出模式时,其每一位代表着每一个GPIO口的电平配置。所以GPC0DAT寄存器的位3代表着GPC0_3口,位4代表GPC0_4口。
在程序头文件led_driver.h中定义寄存器硬件地址:
2.命令和设备结构体定义
根据ioctl命令的定义方式,在程序头文件led_driver.h中定义命令,如下图:
如上图中定义了一个led设备相关的结构体struct led_device,成员struct class  *led_class;代表led设备类,struct device *led_device;代表了的设备,unsigned int  val;代表其可能需要的配置值。
对于LED的操作而言,实现可以单个点亮或熄灭LED,也可以全部点亮或熄灭LED。所以定义了__LED_SELECT枚举、ONOFF命令,然后通过__IO()宏进行定义命令。
3.设备驱动模块初始化
(1)定义struct led_device结构体变量struct led_device *led_dev;和硬件操作相关变量gpc0congpc0dat
并为指针led_device申请内存空间。
(2)静态申请主设备号
其中LED_MAJOR为静态定义的主设备号,在程序头文件led_driver.h中定义。led_fopsstruct file_operations操作函数集。
#define        LED_MAJOR        100
(3)自动创建设备节点文件
首先使用class_create函数创建一个设备类,然后再根据设备类由device_create函数创建一个名为led的设备节点文件,即为当在Linux内核中加载此驱动时,会自动在用户空间/dev目录下生成的设备节点文件led
当操作失败时,处理方式如下:
(4)将硬件设备操作寄存器地址映射为虚拟地址。
Linux内核中,是不允许直接操作设备的物理地址的,智能通过虚拟地址映射的方式进行操作或者配置CPU私有外设的寄存器地址,从而达到操作相关寄存器的目的。
Linux内核中提供了ioremap宏函数来实现物理地址到虚拟地址的映射。
以上操作即为LED设备驱动的初始化函数led_init的实现,源码如下:
4.设备模块卸载
设备模块的卸载主要实现将设备号释放,销毁设备节点文件、销毁设备类,释放所申请的内存空间。
5.操作函数集的实现
如上图可知,一共实现了3个操作函数。分别对应于用户空间的opencloseioctl系统调用的操作。其中led_open操作函数的实现主要进行硬件寄存器的初始化配置。
如上图即是配置GPC0CON寄存器,将GPC0_3GPC0_4口配置为输出模式,并初始化GPC0DAT寄存器,保证初始状态为灯灭。
操作函数led_close的实现不实现任何操作。为空函数。
6.led_ioctl操作函数的实现
如上图所示,led_ioctl函数通过接受从用户空间传递而来的命令cmd,来决定LED设备的亮灭。以上这种方式是通过直接操作寄存器的方式来实现。实际上在Linux内核中提供了相应的读写寄存器的函数方法来实现操作,如下图:
除了这种方法之外,要操作GOIO口,更可以使用gpio_get_valuegpio_set_value这样的函数来进行配置GPIO口,对于GPIO口的配置方式,还有更多的实现。
三、Linux用户空间操作硬件设备
应用程序的实现思路是,从外表为用户程序提供命令来进行操作LED灯,main函数从外表接收两个命令:
命令的执行状态为:./app_led LED编号 状态
·编号可取值1,2,3,分别表示LED1LED2和全部LED
·状态可取值01,分别表示灯亮和灯灭。
首先实现判断外表命令的输入情况,打开设备文件/dev/led,和将命令由字符转化为整数。
然后就是点灯功能的实现了。
四、验证现象
分别编译好内涵驱动模块和应用程序,分别得到led_driver.ko文件和app_led文件,然后将他们都拷贝到开发板根文件系统。
执行命令:insmod led_driver.ko插入内核驱动模块。最后执行应用程序./app_led进行操作LED。如下图:
至此!整个点灯实现完成。



本文地址:https://www.eechina.com/thread-360083-1-1.html     【打印本页】

本站部分文章为转载或网友发布,目的在于传递和分享信息,并不代表本网赞同其观点和对其真实性负责;文章版权归原作者及原出处所有,如涉及作品内容、版权和其它问题,我们将根据著作权人的要求,第一时间更正或删除。
FWW7 发表于 2017-4-9 10:47:04
Linux驱动这块有什么不懂的,可加QQ2232894713相互交流学习
您需要登录后才可以发表评论 登录 | 立即注册

厂商推荐

关于我们  -  服务条款  -  使用指南  -  站点地图  -  友情链接  -  联系我们
电子工程网 © 版权所有   京ICP备16069177号 | 京公网安备11010502021702
快速回复 返回顶部 返回列表