摘 要:针对传统开发模式存在的问题,在原有工程基础上对低速串行设备驱动程序进行了更改、封装,并提出了一种低速串行设备驱动层次化设计方法。
关键词:层次化设计方法;低速串行设备;设备驱动
随着通信技术的发展,低速串行设备动态扩展和多低速串行设备全双工通信技术在嵌入式领域的应用越来越广泛。利用现场可编程门阵列FPGA(Field Programmable Gates Army)完全可以将若干接口电路的功能集成到一片FPGA中,这不仅具有集成度高、体积小和功耗低等优点,而且还具有用户可编程能力,同时还可实现整个系统的功能重构以及项目过程中一些特殊低速串行设备传输方式,这是专用低速串行设备实现芯片所不具备的功能。
嵌入式系统一般采用PowerPC+FPGA的架构方式,FPGA主要完成底层接口协议处理,PowerPC的任务主要实现控制功能。FPGA与PowerPC通过内部总线(Local Bus)方式连接,FPGA实现基于总线的时序操作;采用内存映射方式,FPGA实现的多个低速串行设备的设置、操作寄存器以及FIFO的入口出口映射为PowerPC可寻址的一段内存空间,根据系统设计方法采用中断复用方式或者使用多个PowerPC中断号。
在驱动程序传统开发过程中,FPGA实现的多个低速串行设备驱动程序的设计不受驱动体系约束,只是简单地根据上层应用的需求提供接口,导致应用程序代码可移植性、可读性差,开发流程并行度不高,层次性不明确。实际调试过程中则需要应用程序人员、驱动开发人员同时对各自的程序进行修改、重新编译。
本文在原有工程基础上对FPGA驱动程序进行了更改、封装,实现了基于FPGA的多个低速串行设备在VxWorks系统的标准I/O设备驱动,并在此基础上提出了一种低速串行设备驱动层次化设计方法。
1 低速串行设备驱动程序设计
在VxWorks中,串行设备是一种特殊字符设备。与字符设备不同的是,串行设备的驱动程序并不是直接挂在I/O系统中,而是通过虚拟设备ttyDrv来使用[1]。ttyDrv与I/O系统以及真实驱动程序之间的关系如图1所示。
ttyDrv和tyLib实现了以下的功能[2]:(1)I/O系统请求(在驱动表中增加入口函数,创建设备描述符等);(2)管理I/O系统的入口函数,如tyOpen,ttyIoctl,tyRead,tyWrite等;(3)管理selectLib的调用;(4)管理数据缓冲区以及缓冲区上的线程同步互斥;(5)ttyDrv处理open和ioctl操作(ttyOpen and ttyIoctl);(6)tyLib处理read和write操作(tyRead and tyWrite)。
真实的低速串行设备驱动函数(xxDrv)通过ttyDrv安装的回调函数在I/O系统和设备之间移动数据;tyLib提供了2个回调函数(tyITx和tyIRd)。
1.1 标准驱动程序的构造
SIO_DRV_FUNCS结构体是串行设备驱动程序的一个重要的组成部分,它是驱动函数的入口,在串行设备的驱动程序中必须要有一个包含指向SIO_DRV_FUNCS指针的结构(xx_CHAN),xx_CHAN包含了xxDrv中所需要的设备特定信息,以及提供给高层函数的底层操作函数;由于系统不知道驱动函数中所使用的数据结构,VxWorks提供了一个数据结构SIO_CHAN来进行数据类型转换[3]。SIO_CHAN结构仅定义了一个指向SIO_DRV_FUNCS结构体的指针。
FPGA实现的多个低速串行设备驱动程序的结构构造实例如下:
typedef struct{
/*SIO_CHAN*MUST*be first*/
SIO_CHAN sio;/*standard SIO_CHAN element*/
UINT32* inBase; /*FIFO入口地址*/
UINT32* outBase; /*FIFO出口地址*/
UINT32* baudFreqSetBase; /*波特率设置地址*/
UINT32* IntSourceBase;/*中断源寄存器地址*/
UINT32* inNumBase;
/*FIFO有效数据长度寄存器地址*/
SEM_ID RecvSem;/*对应的信号量*/
UINT32 intLevel;
UINT32 IntSourceMask;/*中断源寄存器掩码*/
/*callbacks*/
STATUS (*getTxChar) (void*,char*);
void (*putRcvChar) (void*,char);
void * getTxArg;
void * putRcvArg;
} FPGA_CHAN;
1.2 驱动函数和回调函数的挂接
ttyDrv通过SIO_DRV_FUNCS提供的入口对xxDrv的服务进行访问,xxDrv通过回调函数来访问ttyDrv提供的服务[4]。xxDrv驱动函数在SIO_DRV_FUNCS中的挂接实例如下:
LOCAL SIO_DRV_FUNCS fpgaSioDrvFuncs=
{
fpgaIoctl, /*Support device specific ioctl cmds*/
fpgaTxStartup, /*Initiates a transmit cycle*/
fpgaCallbackInstall,
/*Installs access to higher level protocols */
fpgaPollInput,/*Poll mode input routine*/
fpgaPollOutput /*Poll mode output routine*/
};
1.3 驱动程序标准操作函数的实现
1.3.1 标准写函数
标准的I/O系统写函数write()调用tyWrite()函数——ttyDrv加载在驱动表中的写入口函数,tyWrite()函数将数据放入环形缓冲队列中并调用驱动函数fpgaTxStartup()发起一次传输过程。write函数的调用关系如图2所示。
2 低速串行设备的加载
(1)根据实际需求修改config.h中NUM_TTYS宏;
(2)参照sysSerialHwInit()函数实现FpgaSerialHwInit(),对NUM_TTYS个低速串行设备结构体FPGA_CHAN进行初始化以及驱动函数和回调函数的挂接;
(3)参照sysSerialHwInit2()函数实现FpgaSerialHwInit2(),挂接中断处理函数fpgaRcvInt();
(4)调用ttyDevCreate()函数创建tty设备,添加设备驱动表,生成环形数据缓冲区,安装回调函数tyITx()和tyIRd()。
3 低速串行设备驱动层次化设计方法
在不同的系统设计中,PowerPC和FPGA硬件连接方式不尽相同,如不同的片选信号、总线上数据线高低位的不同接法,中断管脚接法的不同,导致FPGA实现的寄存器映射地址及使用的中断号存在差异;针对不同的应用程序和应用场所,外部接口对应的设备也会发生相应的变化。以上这些都会破坏程序的独立性、设备无关性。需要对驱动程序、应用程序进行重新编译、烧写,不利于版本的管理以及外场设备的维护。这是在设备驱动设计中需要考虑的一个问题。
由此本文提出了一种层次化的低速串行设备驱动设计方法。将驱动程序的实现分为三层,硬件分离层、设备加载层和功能描述层,各自实现的功能如下:
(1)硬件分离层:通过XML文件实现低速串行驱动程序结构初始化和硬件设备实现差异的分离;
(2)设备加载层:实现低速串行设备在VxWorks系统的标准I/O设备驱动的加载;
(3)功能描述层:通过XML文件实现应用程序和低速串行设备之间的映射关系以及串行设备功能的描述。
低速串行设备驱动层次化设计方法流程如图5所示。
3.1 层次化设计驱动程序的实现
嵌入式操作系统中,通常都使用Flash作为主存介质。在Flash上建立TFFS文件系统,建立文件系统后,类似于在Windows操作系统下对硬盘操作一样,进行数据的拷贝、删除以及文件的建立等操作。XML作为配置文件的主要优点是改变参数配置时不需要改变和重新编译应用程序,只需在XML文件中更改就可以了。TinyXML是一个简单小巧、且很容易集成到其他程序中的C++XML解析器。即TinyXML解析一个XML文档并由此生成一个可读、可修改、可保存的文档对象模型(DOM)。本文的实现过程中使用TinyXML对XML文档进行解析。
低速串行设备驱动层次化设计方法建立在文件系统之上,VxWorks可以动态地加载用户程序,并在系统中加入对XML文件进行解析和存储的功能。
硬件分离层:FpgaSerialHwInit()通过读取设备对应的BSP_Config.xml文件,对多个低速串行设备结构体FPGA_CHAN中的各个寄存器地址进行初始化以及驱动函数和回调函数的挂接。
设备加载层:实现标准I/O设备驱动的加载。
功能描述层:应用程序通过读取USER_Config.xml文件,获取设备的对外连接状态及设备提供的功能,对多个低速串行设备read、write以及ioctl等操作。
通常设备驱动人员只需要进行对BSP_Config.xml和USER_Config.xml两个配置文件进行修改、更新。
3.2 XML配置文件的设计及实现
BSP_Config.xml文件格式示例如下:
const char*bspXmlCfg=
""
"
"
"
"
……
"
"
……
"
USER_Config.xml文件格式示例如下:
const char*userXmlCfg=
""
"
"
"
"
……
"
XML文件的遍历和读取方法如下:
TiXmlDocument doc(CONFIG_FILE_NAME);
bool loadOkay=doc.LoadFile();/*读取文件*/
doc.Print();/*输出打印*/
rootNode=doc.FirstChild("Settings");/*获取根节点*/
……
node=rootNode->FirstChild("System");
/*获取匹配的子节点*/
itemElement=node->ToElement();/*转换为元素*/
itemElement->Attribute("Type",&dValue);/*读取属性*/
XML文件的保存和更新方法如下:
TiXmlDocument doc;
doc.Parse(demoXmlCfg);
/*读取需要保存的XML文件框架结构*/
rootNode=doc.FirstChild("Settings");/*获取根节点*/
……
node=rootNode->FirstChild("System");
/*获取匹配的子节点*/
itemElement=node->ToElement();/*转换为元素*/
itemElement->SetDoubleAttribute("Type",Type)
;/*更新属性*/
……
doc.SaveFile(CONFIG_FILE_NAME);
/*存储更新配置文件*/
本文提出并实现了一种VxWorks低速串行设备驱动的层次化设计方法,通过驱动程序功能分层设计方式,屏蔽各层的实现细节,克服了嵌入式系统开发过程中,由于设备驱动程序的设计不受驱动体系约束带来的程序代码的可移植性、可读性差,开发流程并行度不高,层次性不明确等不足之处;在实际的工程实现中节约了开发时间,极大地提高了效率。
参考文献
[1] Vxworks kernel programmers guide[Z].Wind River Systems Inc,2006.
[2] Vxworks device driver developers guide[Z]. Wind River Systems Inc, 2006.
[3] 内核示例源代码[Z]. Wind River Systems Inc, 2006.
[4] 陈智育,等.VxWorks程序开发实践[M].北京:人民邮电出版社,2004.