引 言
随着嵌入式系统及集成电路技术的飞速发展,针对移动手持终端的专用芯片获得了长足发展。芯片的RAM和ROM的容量越大,在上面跑操作系统也越来越容易。Linux是当今流行的操作系统之一。由于其内核健壮、运行高效、源码开放,并且Linux是免费的操作系统,再加上其良好的可移植性等技术优势,使其已经成为嵌入式操作系统的主流。设备驱动程序是Linux操作系统中的一个重要的组成部分,现在不断升级的Linux内核中,增加最多的就是驱动程序。由于Linux是开放源代码的,给我们提供了一个绝好的机会来分析和改造设备驱动程序,使其满足自己的特殊应用,这样在嵌入式产品中,就可以为自己特有的外围设备编写一个设备驱动程序。由于IrDA技术的蓬勃发展,提供了各种信息家电设备之间的无线连接的最佳选择,红外数据传输,成本低廉、连接方便、简单易用、结构紧凑,在小型嵌入式移动设备中也得到了广泛的应用。配备有红外技术移动电话、个人数字助理、笔记本电脑都已登陆市场,因此,嵌入式设备的IrDA模块的开发有着广阔的市场前景。
本文将详细讲解基于Inte1的PXA255处理器的Sitsang开发平台红外模块的设计,其中包括Linux下的IrDA驱动程序的开发和基于MC68HC908AP64红外发射器的软硬件设计两个部分。
1 Linux下IrDA模块的设计
1.1 Linux下的设备驱动程序简介
系统调用是操作系统内核和应用程序之间的接口,驱动程序是操作系统内核和机器硬件之间的接El,也是应用层和实际硬件设备之间的软件。一个驱动程序就是一个函数和数据结构的集合,它的目的就是实现一个简单的管理设备的接口。内核用这个接口请求驱动程序控制设备的I/O操作。设备驱动程序为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个设备文件,应用程序可以像操作普通文件一样对硬件设备进行操作。Linux操作系统支持三种不同类型的设备,即字符设备、块设备和网络接口,相应地有三种类型的设备驱动程序。本文主要讨论字符型设备的驱动程序。设备驱动程序是内核的一部分,主要完成以下的功能:
①对设备初始化和释放;
②把数据从内核传送到硬件和从硬件读取数据;
③读取应用程序传送给设备文件的数据和回送应用程序请求的数据;
④检测和处理设备出现的错误。
由于应用程序是通过设备文件同硬件打交道,对设备文件的操作方式不外乎就是一些系统调用,如open、read、write、close等等。Linux操作系统是通过一组固定的接口把系统调用和驱动程序关联起来的。这组入口点是由每个设备的设备驱动程序组成了一个数据结构来向系统提供的,它提供了字符型设备驱动程序所需的操作。这是一个非常关键的数据结构:
struet file_operations{
struct modul*owner;
loft t(*llseek)(struet file*10flf_t,int);
ssize t(*read)(struct file*,char*,size_t,10ff_t);
ssize tf*write)(struct file*,const char*size_t,10ff_t*);
int f*readdir)(struct file*,void*,filldir_t);
unsigned int(*poll)(struct file*,struct poll_table_struct*);
int(*ioctl)(struct inode*.struct file*,unsigned int,unsigned long);
int(*mmap)(struet file*,struct vm_area_struct*)
int(*open)(struct inode*,stmct file*);
int(*flush)(struct nle*);
int(*release)(struct inode*,stmct file*);
这个结构的每一个成员的名字都对应着一个系统调月。应用程序利用系统调用在对设备文件进行诸如read/write操作时,系统调用通过设备文件的主设备号找到相应的设备驱动程序,然后读取这个数据结构相应的函数指针,接着把控制权交给该函数。这是Linux的设备驱动程序工作的基本原理。既然是这样,编写设备驱动程序的主要工作就是编写子函数,并填充me_operations的各个域。多数情况下,只需为上面结构中的少数方法编写服务函数,其它均设为NULL。
已经提到,应用程序是通过设备文件来与实际的硬件打交道的。每个设备文件都有其文件属性(c/b),表示是字符设备还是块设备。另外每个文件都有两个设备号:第一个是主设备号,标识驱动程序;第二个是从设备号,标识使用同一个设备驱动程序的不同的硬件设备,比如有两个软盘,就可以用从设备号来区分它们。设备文件的主设备号必须与设备驱动程序在登记时申请的主设备号一致,否则用户进程将无法访问到驱动程序。
1.2 IrDA模块驱动程序
IrDA是一种廉价、近距离、无线、低功耗、保密性强的通信技术,适合于低成本、跨平台、点对点高速数据连接,尤其是嵌入式系统;主要应用于无线数据传输,有时也用于无线网络接入和近程遥控。IrDA制定了很多红外通信协议,其中IrDA1.0协议基于异步收发器uART,最高通信速率在115 2kbps,简称sIR(Seria Infrared,串行红外协议),采用3/1 6 ENDEC编/解码机制。我们所要实现的就是基于sIR的IrDA驱动。它的发射强度与接收灵敏度因不同器件、不同应用设计而强弱不一,使用时只能以半双工方式进行红外通信。
我们的设计思想是Sitsang板只作为接收端,而基于MC68HC908AP64红外发射器作为发送端。其中file_operations结构中的ioctl()函数可以进行发送或接收的状态切换。原Sitsang板载Linux系统所带的IrDA驱动程序是作为网络部分编写的,使用过于复杂,且在处理数据收发时需要做一些自己的处理和验证规则,所以我们使用标准串口在Linux下自己编写了一个IrDA的设备驱动程序。这样在使用时,可以根据自己的需要作相应的更改,比较灵活。
在IrDA驱动程序中主要实现了s_r_read()、s_r_write()、siLopen()、sir_close()、siLioctl()及sir_handle_irq()中断处理程序六个函数。相应的接口结构如下所示:
static struct file_operations siLfops={
ioctl: sir ioctl,
read: siread,
write: sir_write,
open: slr_open,
release:sir close
1.2.1 sir_handle_irq函数
用户空间进程通过接口函数进入到内核,内核再调用驱动程序相应的I/O函数。IrDA驱动程序是字符类型的驱动程序,我们用中断的方式实现内核与设备之间的数据传输。当驱动程序在启动后设备就挂起自己,直到串口完成操作并发出一个中断请求(IRQ)。当IRQ产生时,注册的中断处理程序sir_handlejrq得以运行。在Sir_handle_irq中,程序通过相应的寄存器操作得到接收的数据,并将数据存入到一个内部缓冲中。
1.2.2 sir open和sir_cIose函数
sir_open函数的主要功能就是递增使用计数和设备初始化操作。这里把设置并初始化sitsang板上的红外设备放在了sir_open函数中,这样在每次打开IrDA设备时,红外设备都会被正确地设置,确保了红外硬件的正常工作。另外,把申请设备中断号的工作也放到了Sir_open函数中,这样IrDA设备所占用的中断号,在没有使用IrDA设备时也可以被其它设备共享。
static int sir sopen(struct inode*inode,struet *filp){
计数器加1;
申请设备中断号;
设置Sitsang板上的红外设备并初始化;
}
sir_close()函数所作的工作与sir_open()的正好相反,计数器减1,注销设备中断号。
1.2.3 sir_ioctl函数
由于所使用的红外收发器HSDL-3200只能以半双工方式进行红外通信,所以就需要命令进行接收和发送状态的转换。ioctl函数的主要功能就是对硬件设备进行控制,因此在sir—ioctl函数中实现了这一功能。
static int siLioctl(stmctiTlode*inode,structfile*mp,unsigned_int cmd,unsignedlogarg){
swltell(cmd){
case接收:
设置接收寄存器;
break:
case发送:
设置发送寄存器;
break;
default:
}
}
1.2.4 sir_read和sir_write函数
这两个函数主要完成读取应用程序传送给内核设备文件的数据和回送应用程序请求的数据,并把数据从内核传送到硬件和从硬件读取数据的通信过程。这也是在整个驱动程序中最重要的部分。
当用户调用read()函数时,内核相应地调用sir_read()函数。在Sir_read()中,通过判断硬件寄存器是否有新数据到来而决定是否从设备读取数据,然后使用内核提供的copy touser(void*to,const void*from,unsigned long count)函数将数据返回应用程序。write()函数的实现与read()函数的实现过程正好相反。在sir_write()中,通过调用copy_from user_form_user(void*to,const void*from,unsigned long count,)函数来完成把数据从用户的应用程序传送给硬件设备。
1.2.5实现模式
设备驱动程序的主体完成了,现在要把驱动程序嵌入内核。实现Llnux下IrDA设备驱动功能主要有两种形式:一是通过内核来进行加载,需要用户在./etc/rc.d/目录中定义的初始启动脚本中写入命令,当内核启动的时候,就开始加载IrDA设备驱动程序,内核启动完成之后,IrDA驱动功能也随即实现了,但是增大了内核;第二是通过模块加载的形式。比较两者,第二种形式更加灵活,在此着重对模块加载形式进行讨论。模块设计是Llnux中特有的技术,它使Linux内核功能更容易扩展。采用模块来设计Linux设备驱动程序会很轻松,并且能够形成固定的模式。任何人只要依照这个模式去设计,都能设计出优良的设备驱动程序。
先简要概述一下基于模块加载的设备驱动程序的设计步骤。首先每一个可装配的设备驱动程序都必须有init_module和cleanup module两个函数,装载和卸载设备时内核自动调用这两个函数。前者在insmod的时候执行,后者在rmmod的时候执行。通过模块加载命令insmod来把IrDA设备驱动程序插入到内核之中。在init_module中,除可以对硬件设备进行检查和初始化外,还必须调用reglster_*’函数将设备登记到系统中。本例中是通过register_chrdev来登记的,如果是块设备或网络设备则应该用reglstei_blkdev和register_netdev来登记。registeT_chrdev的主要功能是将设备名和结构flle operatioons登记到系统的设备控制块中。最后可以通过执行模块卸载命令rmmod,调用IrDA驱动程序中的cleanup_module()函数,来对IrDA驱动程序模块卸载,具体实现过程如图l所示。
2 基于MC68HC908AP64红外发射器的设计
2.1 红外发射器的硬件设计
为了可以检测Sitsang板端的IrDA设备能否正常工作,设计了一个IrDA发射器。发射器的体积为l3cm×10cm,安装灵活方便。在发射器上有一个拨位开关,可以用来设置发射不同的码值。红外收发器选用具有半双工功能的HSDL-3200。
单片机的可靠性和片上资源是选择的关键。如一片单片机的资源不足,还要另加其它芯片,就会给系统的可靠性、外型体积、造价带来很多负面的影响。MC68HC908AP64单片机对程序安全运行有较全面的保护。与其它的MCU相比,最重要的是它内部集成有UART单元及其接口,支持IrDA标准,有红外接口可以直接与红外收发体系连接。可以直接驱动HSDL_3200,片上的其它资源包含了发射器的全部需要,并且廉价低功耗。所以选用其作为发射器的主控芯片电路如图2所示。
2.2 红外发射器的软件实现流程
发射器的软件编程对产品的可靠性有很大影响。由于IrDA是异步半双工的通信方式,在某一个时刻,IrDA收发器只可能呈现一种状态。鉴于这种情况,设置IrDA收发器始终处于发射状态,而SitSang板上的IrDA收发器始终处于接收状态,这样就不用切换收发状态,保证了系统的稳定性。发射器和接收器之间的通信需要制定一套合理的通信协议来协调总通信。这里采用的是数据包通信方式。通信波特率为9600bps,通信数据是成帧发送的,每帧数据都可以设置自己的引导码和数据。其中引导码是用于同步每一帧数据;数据是IrDA发射器拨位开关的值,可以自己随意设定。当红外发射器和Sitsang板调通以后,也可以通过sir_ioctl函数来切换收发状态,达到双方通信的目的。发射器的软件流程如图3所示。
结语
本文分析了设备驱动程序在内核中的实现方法,并讲解了基于Intel的PXA255处理器的Sitsang平台开发的驱动程序设计和实现过程。其中结合Linux下一个现在使用广泛的IrDA驱动程序,详细阐述了红外模块驱动程序的设计和开发过程,最后从硬件和软件两个方面讲述了基于MC68HC908AP64红外发射器的设计和实现。虽然嵌入式Linux操作系统和传统的嵌入式操作系统相比还不够成熟完善,但是Linux本身所具有的优越性使其在移动设备的OS领域具有广阔的应用前景。