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

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*);

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”。借助这些机制,可以按照须要在不
重新编译内核的情况下,将编译好的模块动态
的插入运行中的内核,或则将内核中早已存在

的一个模块移走。这些机制为驱动程序开发调
试提供了很大的便捷。在运行的系统中可以通
过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

的电平所决定.假如把行线接到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

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
