197

第第66章章嵌入式嵌入式LinuxLinux驱动程序开发驱动程序开发

嵌入式Linux的设备管理

设备驱动程序开发过程

应用实例

198

1.1.嵌入式嵌入式LinuxLinux的设备管理的设备管理

设备管理概述

设备类型

设备号

Linux驱动程序概念

驱动程序结构

用户访问插口

Linux对中断的处理

设备驱动的初始化

设备驱动初始化函数主要功能

199

1.11.1设备管理概述设备管理概述

linux将所有的设备看作具体的文件,通过

文件系统层对设备进行访问。这样可以挺好的

做到“设备无关性”。可以把Linux对设备的管

理界定成四个层次:

用户进程

文件系统层

设备驱动层

硬件层

它们之间的关系如右

图所示:

第六章

123

200

1.11.1设备管理概述设备管理概述

用户进程用户进程通常坐落内核之外,当它须要操

作设备时,可以如同访问普通文件一样,通过调用

read(),write()等文件操作系统调用来完成对设备文

件的访问和控制。

文件系统层:向用户提供一组统一的用户插口。它

坐落用户进程层下边,属于内核空间,基本功能是

执行适宜于所有设备的输入输出功能,使用户透明

的访问文件。在用户进程发出系统调用要求输入输

出操作时,由文件系统层就处理恳求的权限。

设备驱动层:用于屏蔽具体设备的细节,设备驱动

程序坐落内核中,它按照文件系统层的输入输出请

求来操作硬件上的设备控制器,完成设备的初始

化、打开释放设备以及数据在内核和设备间的传递

等操作。

第六章

123

201

1.21.2设备类型设备类型

Linux中的设备可以分为三类:

字符设备:数据的处理是以字节为单位按次序进

行的,没有数据缓冲区,也不支持随机读写。嵌

入式系统中简单的按钮、触摸屏、手写板都属于

字符设备。

块设备:是指这些在输入/输出时数据处理以块为

单位的设备,它通常都采用了缓存技术,支持数

据的随机读写。典型的块设备有硬碟、cd-rom

等。对用户来说块设备和字符设备的访问插口都

是一样的,区别仅在内核和驱动程序的软件插口

上。

网路设备:实现方式不同于以上两种设备,它面

向的上一层不是文件系统层而是网路合同层,内

核和网路设备驱动程序间的通讯,与字符设备和

块设备也完全不同。

第六章

123

202

1.31.3设备号设备号

传统的设备管理上,不仅设备类型外,linux

内核还须要一对被称作为主设备号、次设备号

的参数,就能惟一的标示设备。

主设备号:标示设备对应的驱动程序。系统中不

同的设备可以有相同的主设备号,主设备号相同

的设备使用相同的驱动程序,内核借助主设备号

将设备与相应的驱动程序对应上去。

次设备号:拿来分辨具体驱动程序的实例。从上

面可以晓得,一个主设备号可能有多个设备与之

对应,这多个设备正是在驱动程序内通过次设备

号来进一步分辨的。次设备号只能由设备驱动程

序使用,内核的其他部份仅将它作为参数传递给

驱动程序。

第六章

123

203

Linux设备管理_嵌入式linux驱动程序实战开发 pdf_嵌入式Linux驱动程序开发

1.4Linux1.4Linux驱动程序概念驱动程序概念

设备驱动程序是操作系统内核与机器硬件之

间的插口,设备驱动程序为应用程序屏蔽了硬

件的细节,从应用程序来看,硬件设备只是一

个设备文件,应用程序可以向操作普通文件一

样对硬件设备进行操作,设备驱动程序属于内

核,主要功能有:

对设备初始化和释放。

把数据从内核传送到硬件和从硬件读取数据。

传送应用程序和设备文件之间的数据

检查和处理设备出现的错误。

设备驱动程序的开发方法主要有两种:基于

内核的形式和基于模块的形式。

第六章

123

204

1.51.5驱动程序结构驱动程序结构

Linux的设备驱动程序分为三个主要部份:

手动配臵和初始化子程序:负责检测所要驱动的

硬件设备是否存在和能够正常工作。仅在初始化

时被调用一次。

服务于I/O恳求的子程序:俗称为驱动程序的上半

部份。这部份程序由系统调用执行,和调用进程

属于同一个进程,具有调用进程的运行环境,只

是由用户态弄成了核态度。

中断服务子程序:俗称为驱动程序的下半部份。

由Linux系统接收,再由系统调用中断服务子程

序。

中断可以在任何一个进程运行时形成,因此中

断应当独立于任何进程。

同一个中断可以服务于多个设备,因此应当为

中断程序提供恳求服务设备的标识参数。

第六章

123

205

1.51.5用户访问插口用户访问插口

对用户程序而言嵌入式linux驱动程序实战开发 pdf,设备驱动程序隐藏了设备

的具体细节,对各类不同设备提供了一致的接

口,I/O设备的存取可以通过一组固定的入口点

来进行,这组入口点是由每位设备的设备驱动

程序提供的。主要包括:

open入口点:打开设备打算I/O操作。

release入口点:关掉由open()函数打开的文件

read入口点:从设备上读数据。对于有缓冲区的

I/O操作,通常是从缓冲区里读数据。

write入口点:往设备上写数据,对于有缓冲区

的I/O操作,通常是把数据写入缓冲区里。

ioctl入口点:执行读、写之外的操作,主要是一

些自定义的命令

第六章

123

206

1.51.5用户访问插口用户访问插口

Lseek入口点:联通文件表针的位臵,用于随机

存取的设备。

Readdir入口点:取得下一个目录入口点,只有

与文件系统相关的设备驱动程序才使用。

Select入口点:进行选择操作。假如驱动程序没

有提供select入口,select操作将会觉得设备已然

打算好进行任何的I/O操作。

这种入口点由用户驱动程序提供,在Linux

系统中,使用file_operations结构组织入口点,

其中的成员全部是函数表针嵌入式linux驱动程序实战开发 pdf,所以实质上就是

函数跳转表,每位进程对设备的操作,就会根

据主次设备号,转换成对该结构的访问。

file_operations的结构如下:

第六章

123

207

1.51.5用户访问插口用户访问插口

structfile_operations{

int(*lseek)(structinode*,structfile*,off_t,int);

int(*read)(structinode*,structfile*,char*,int);

int(*write)(structinode*,structfile*,constchar*,int);

int(*readdir)(structinode*,structfile*,void*,filldir_t);

int(*select)(structinode*,structfile*,int,select_table

*);

int(*ioctl)(structinode*,structfile*,unsignedint,

unsignedlong);

int(*open)(structinode*,structfile*);

Linux设备管理_嵌入式linux驱动程序实战开发 pdf_嵌入式Linux驱动程序开发

void(*release)(structinode*,structfile*);

…………

};

第六章

123

208

1.6Linux1.6Linux对中断的处理对中断的处理

中断是发挥硬件尤其是cpu性能的一个重要

方面,在linux中为中断的管理提供了挺好的接

口,从应用编程角度来看编撰一个中断处理程

序主要做两件事,

依据具体应用实现中断服务子程序。

借助一系列LinuxAPI函数向内核注册该服务子

程序,而具体的中断调度处理由linux内部实现。

注册中断处理程序

向内核注册中断处理程序主要实现两个功能,一

是注册中断号,二是注册中断处理函数。

注册函数为:intrequest_irq(unsignedintirq,voi

d(*handler)(int,void*,structpt_regs*),…..)

释放函数为:voidfree_irq(unsignedintirq,…)

第六章

123

209

1.71.7设备驱动的初始化设备驱动的初始化

设备驱动程序所提供的入口点,必须向系统

进行登记,便于系统在适当的时侯调用,这个

过程称为设备驱动的初始化,在Linux系统中对

字符型设备提供了两个相关函数:

externintregister_chrdev(unsignedintmajor,

constchar*name,structfile_operations*fops);

拿来注册驱动程序,它按照提供的主设备号、设

备名及相关的入口点结构向系统注册该设备驱动

程序并完成相关的初始化工作。

externintunregister_chrdev(unsignedintmajor,

constchar*name);拿来卸载设备驱动程序。它

依据主设备号和设备名,对该设备的驱动程序进

行卸载操作。

第六章

123

210

1.81.8设备驱动初始化函数主要功能设备驱动初始化函数主要功能

对驱动程序管理的硬件进行必要的初始化:对硬

件寄存器进行设臵,如设臵并口的工作方法、并

口的数据方向等。

初始化设备驱动相关的参数:每位设备都有用于

保存设备相关参数的变量,必须初始化。

在内核中注册设备:每位驱动程序都有惟一的主

设备号,内核必须记录设备的主设备号及其驱动

程序以以便调用。在Linux文件系统的/proc下的

devices文件保存着主设备号及其设备名。

注册中断:假如设备须要IRQ支持,则要注册中

断。

其他初始化工作:例如给设备分配I/O、申请

DMA通道等。

第六章

123

211

2.2.设备驱动程序开发过程设备驱动程序开发过程

设备驱动程序开发流程

模块化驱动程序设计

字符设备驱动程序举例

212

2.12.1设备驱动程序开发流程设备驱动程序开发流程

定义主、次设备号,也可以动态获取。

实现驱动初始化和清理函数。假如驱动程序采用

模块形式,则要实现模块初始化和清理函数。

设计所要实现的文件操作linux系统安装教程,定义file_operations

结构。

实现所须要的文件调用,如read、write等

实现中断服务函数并向内核注册。

将驱动编译到内核或编译成模块并加载。

生成设备节点文件。

第六章

123

213

2.22.2模块化驱动程序设计模块化驱动程序设计

在linux系统中提供了一种模块化机制

“module”。借助这些机制,可以按照须要在不

重新编译内核的情况下,将编译好的模块动态

的插入运行中的内核,或则将内核中早已存在

嵌入式linux驱动程序实战开发 pdf_嵌入式Linux驱动程序开发_Linux设备管理

的一个模块移走。这些机制为驱动程序开发调

试提供了很大的便捷。在运行的系统中可以通

过lsmod察看内核中早已动态加载的模块。

模块的安装和从内核中卸载可以通过以下命

令实现,它们操作的对象是经过编译但没有连

接的.o文件。

insmodxxxx

rmmodxxxx

第六章

123

214

2.22.2模块化驱动程序设计模块化驱动程序设计

对应的模块化编程,源程序中必须起码提供

两个函数:

init_module():初始化模块函数,在模块载入时

调用,为之后调用模块的函数做打算。

cleanup_module():卸载。在模块卸载时调用。

一个简单的模块化程序

/*————mdemo.c————*/

#defineMODULE

#include

intinit_module(void){

printk(“nhello,world!nn”);return0;

第六章

123

215

2.22.2模块化驱动程序设计模块化驱动程序设计

voidcleanup_module(void){

printk(“nByeByenn”);

mdemo.c可以通过下边命令来编译:

gcc-c-D__KERNEL__-DMODULE

-omdemomdemo.c

在得到了mdemo后,用insmod命令把它动态

加入内核:

#insmodmdemo

#helloworld!

#rmmodmdemo

#ByeBye

第六章

123

216

2.32.3字符设备驱动程序举例字符设备驱动程序举例

教材P291

第六章

123

217

3.3.应用实例应用实例

按键驱动

Sitsang板的触摸屏驱动

218

3.13.1按键及其工作原理按键及其工作原理

鼠标在计算机应用系统中是一个很

关键的部件,它能实现向计算机输入数

据、传达命令等功能,是人工干预的主

要手段。

这些部件可分为独立式键盘和导数

按键两种

下边仅对导数按键给以讨论。

219

一、行列式(矩阵式)按键结构特征一、行列式(矩阵式)按键结构特征

矩阵式按键适用于键盘数目较多的场合,它由行线和列

线组成,键盘坐落行、列的交叉点上。

如图6.3-1所示,一个3×3的行、列结构可以构成一个

有9个键盘的按键。

同理一个4×4的行、列结构可以构成一个富含16个按

键的鼠标等等。

很显著,在键盘数目较多的场合,矩阵按键与独立式键盘

盘相比,要节约好多的I/O口。

220

导数(矩阵式)按键示意图导数(矩阵式)按键示意图

221

二、行列式按键工作原理二、行列式按键工作原理

4×4的按键结构如图6.3-2所示,图建行线通过内阻接

+5V,当按键上没有键闭合时,所有的行线和列线断掉,行线

~X

都呈高电平。

222

二、行列式按键工作原理二、行列式按键工作原理

当按键上某一个键闭合时,则该键所对应的行线与列线短

路。诸如,6号键按下闭合时,行线X

和列线Y

漏电,此时X

的电平

由Y

嵌入式Linux驱动程序开发_Linux设备管理_嵌入式linux驱动程序实战开发 pdf

的电平所决定.假如把行线接到CPU的输入口,列线接到CPU

的输出口,则在CPU的控制下,使列线Y

为低电平(0),其余三根列

线Y

、Y

、Y

都为高电平。

之后CPU通过输入口读行线的状态,倘若X

都为高电

平,则Y

这一列上没有键闭合,倘若读出的列线状态不全为高电

平,则为低电平的行线和Y

相交的键处于闭合状态;

223

假如Y

这一列上没有键闭合,接着使列线Y1为低电平,其余列线为高电

平,用同样的方式检测Y

这一列键上有无键闭合。以这种推,最后使列线Y

为低电平,其余的列线为高电平,检测Y

这一列上是否有键闭合。

224

这些逐行逐列地检测鼠标状态的过程称为对按键

的一次扫描。

CPU对键扫描要以采取程序控制的随机方

式,CPU空闲时扫描鼠标,也可以采取定时控制方法,

每隔一定时间,CPU对按键扫描一次,CPU可随时响应

按键输入恳求,对按键扫描,以辨识哪一键处于闭合状

态,并对键输入信息做出相应处理。

225

CPU对按键上闭合键键号的确定,可以按照行线和

列线的状态估算求得,也可以按照行线和列线状态查

表求得。

为了保证CPU对键的闭合作一次且仅作一次处理,

必须消除晃动,在键的稳定闭合或断掉时读键的状态,

并判断出键由闭合到释放时,再作键输入处理。

226

三、键盘导数鼠标键盘的辨识方式三、键盘导数鼠标键盘的辨识方式

导数鼠标键的辨识方式有二种:扫描法和反转法。这

里只介绍扫描法。

按照前面的剖析,很容易得出矩阵鼠标键盘的辨识方

法,此方式分两步进行:第一步,辨识按键有无键被按下;

第二步,假如有键被按下,辨识开具体的键盘。分述如下:

辨识具体键盘的方式是(也称之为扫描法):逐列臵零

电平,其余各列臵为高电乎,检测各行线电平的变化,假如

某行电平由高电平变为零电平,则可确定此行与列交叉点处

的键盘被按下。

227

鼠标的控制方法鼠标的控制方法

按键扫描只是CPU的工作内容之一。CPU在疏于各

项工作任务时,怎样兼具按键的输入,取决于鼠标的

工作方法。按键的工作方法的选定应按照实际应用系

统中CPU工作的忙、闲情况而定。其原则是既要保证

能及时响应键盘操作,又要不过多占用CPU的工作时

间。一般,鼠标工作方法有三种,即:编程扫描、定

时扫描和中断扫描。

228

一、编程扫描方法一、编程扫描方法

CPU对按键的扫描采取程序控制方法,一旦步入键

扫描状态,则反复地扫描按键,等待用户从按键上输入

命令或数据。而在执行键入命令或处理键入数据过程

中,CPU将不再响应键入要求,直至CPU返回重新扫描

鼠标为止。

229

按键扫描子程序编程要点按键扫描子程序编程要点

(1)判定按键上有无键按下——其方式为列输出全0,读行

输入状态,若状态为全1,则说明鼠标无键按下;若不全为1,

则说明按键有按下。

(2)清除键盘晃动的影响——其方式为,在判定有键按下

后,用软件延时的方式(延时10ms),再判定按键状态,假如

仍为有键按下状态,则觉得有一个确定的键按下,否则当成键盘

晃动处理。

(3)求按热键臵——用扫描的方式辨识闭合键N所在的行号

X和列号Y,并按照:以下公式估算闭合键的键号

N=X行首键号+列号Y。

(4)键闭合一次仅进行一次按钮处理——方法是等待键盘释

放以后,再进行键盘功能的处理操作。

230

二、定时扫描工作方法二、定时扫描工作方法

定时扫描工作方法是借助内部定

时器形成定时中断(比如10

嵌入式linux驱动程序实战开发 pdf_嵌入式Linux驱动程序开发_Linux设备管理

ms),CPU响应中断后对按键进行

扫描,并在有键按下时辨识出该键并

执行相应键功能程序。定时扫描工作

形式的按键硬件电路。

与编程扫描工作方法相同,软件

框图如图6.3-3所示。

231

三、中断工作方法三、中断工作方法

按键工作于编程扫描状态时,CPU要不间断地对按键进行扫

描工作,以监视按键的输入情况,直至有键按下为止。其间CPU

不能干任何其它工作,假如CPU工作量较大linux源代码分析,这些方法将不能适

应。

定时扫描进了一大步,不仅定时监视一下按键输入情况外,

其余时间可进行其它任务的处理,因而CPU效率提升了。

为了进一步提升CPU工作效率,可采用只有在鼠标有键按下

时,才执行按键扫描并执行该按钮功能程序,倘若无键按下,

CPU将不搭理按键。

232

3.23.2按键驱动按键驱动

简单起见,我们以一个按钮的实现为例来讲

述驱动的编撰。如右图所示:

平台上的一个按钮和外部中断IRQ6相连,

当按钮按下时,引脚输入低电平触发中断。下

面我们来看驱动程序的设计。

第六章

123

233

3.2.13.2.1设备初始化设备初始化

驱动程序在init_keyboard()中实现向系统注

册主次设备号,设备名,并初始化寄存器,如

下:

voidinit_dev_set(void){

ICR=0x00;//低电平触发中断

PDDIR=0x80;//设臵PD7为输入

PDSEL=0x80;//PD7作为I/O与外部联接

PDKBEN=0x00;//鼠标中断使能

………..

第六章

123

234

3.2.13.2.1设备初始化设备初始化

intinit_keyboard(void){

#definekeyboard_major50//主设备号为50

#definekeyboard_minor0//次设备号为0

intrc;

rc=register_chrdev(keyboard_major,”

keyboard”,&keyboard_fops);

if(rc

printk(“registerkeyboard-Drivererrorn”);

elseinit_dev_set();

returnrc;

};

第六章

123

235

3.2.23.2.2注册中断和中断处理程序注册中断和中断处理程序

在open函数中向内核注册中断,如下:

staticintkeyboard_open(structinode*inode,structfile

*file){

rc=request_irq(IRQ_MACHSPEC|IRQ6_IRQ_NUM,keyboar

d_interrupt,IRQ_FLG_STD,”

keyboard-IRQ”,NULL/*Userdata!!!*/);//向内核注册中断

if(rc){

printk(“keyboard-Driver:Errorwhileinstallinginterrupt

handlern”);

return-ENODEV;

};//返回值不为零,则注册失败

MOD_INC_USE_COUNT;

return0;

第六章

123

236

3.2.23.2.2注册中断和中断处理程序注册中断和中断处理程序

按键的中断处理程序如下:

staticvoidkeyboard_interrupt(intirq,void*d

ev_id,structpt_regs*regs){

ISR|=(1

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复