引言
本文在实现喷码机板载系统一体化的目标下,研究了喷码机软件系统开发的操作系统板级支持包(Board Support Package,BSP)[1]。
1 BSP结构概述
板级支持包BSP是介于硬件和操作系统之间的一层软件系统,其作用就是抽象操作系统和主板硬件之间的交互接口。基于具体开发板开发BSP,必须对它的结构有一定的了解。一般情况下,WinCE5.0的BSP包含引导程序(BootLoader)、OEM适配层(OEM Adaptation Layer,OAL)、驱动程序、配置文件四部分。
2 开发板的硬件组成
BSP不仅与特定的操作系统有关,而且与不同的开发平台也是一一对应的。喷码机系统的硬件平台主要由图1所示的几大部分组成。
图1 开发板硬件组成
以喷码机控制系统的CPU为核心,外围设备主要包括两片HY57V561620F(L)T(P)芯片、一片K9F2G08U0A和EN29LV160AB芯片,使开发板拥有了64 MB的SDRAM、256 MB的NAND FLASH以及2 MB的 NOR FLASH。网卡采用DM9000,提供互联网连接等功能,同时包含4.3寸的触摸屏、XJ128喷头以及丰富的接口资源。
3 BSP开发
从零开始研发BSP成本较高、耗时长,所以现实中最常采用的方法是根据需要对现有的相近BSP源码进行移植。本文就是以相近开发板的BSP为基础,根据喷码机硬件平台的外围配置,对BSP的源码做出对应的修改,使之能够有效支持硬件系统。主要工作介绍如下。
3.1 BootLoader的实现
BootLoader是嵌入式软件系统最底层的一组代码,是依赖硬件存在的。微软公司提供了一套WinCE系统BootLoader的基本框架,由Blcommon库、Eboot库、OEM代码、EDBG驱动以及存储管理组成[1]。移植时前二者无需改动,后面三部分则需要根据开发板的实际配置进行修改。在实现过程中应尽量使用微软提供的支持库,这样在保证了规范性、高效性的同时还节省了实现时间。
BootLoader需要实现的代码主要分为两部分:由汇编语言编写的启动代码以及由C语言编写的主代码。汇编代码startup.s是BootLoader的入口函数,CPU启动后将立即运行,完成对CPU的初始化、地址映射、及BootLoader代码的搬运[2]。以下是平台的部分代码实现:
ResetHandler
movr0, #0
mcrp15, 0, r0, c8, c7, 0; flush both TLB
mcrp15, 0, r0, c7, c5, 0; invalidate instruction cache
mcrp15, 0, r0, c7, c6, 0; invalidate data cache
ldr r0, = WTCON; disable watch dog
ldr r1, = 0x0
str r1, [r0]
; set INTMSK, INTSUBMSK, disable all interrupts
ldr r0, = INTMSK
ldr r1, = 0xffffffff; disable all interrupts
str r1, [r0]
ldr r0, = INTSUBMSK
ldr r1, = 0x7fff;disable all sub interrupt
str r1, [r0]
……
;set INTMOD, Configure MPLL, UPLL
……
;Copy boot loader to memory
……
汇编程序执行完以后,就会跳转到主程序main函数中,调用BLCOMMON库blcommon.c中定义的BootLoaderMain函数。由它控制着接下来的整个代码执行流程,是引导加载程序的主控函数。
① 调用KerneRrelocate()函数(blcommon.c中实现):将BootLoader中的全局变量重新定位到RAM中。
② 调用OEMDebugInit()函数(main.c中实现):初始化调试端口,一般情况下就是异步串行通信口UART,调用OEMInitDebugSerial()函数实现。在喷码机平台中选择串口UART0作为调试端口,查看处理器的芯片手册,要把通用I/O口的GPH2和GPH3用作功能复用口,设置它们为UART0的发送数据引脚TXD与接收数据引脚RXD,代码如下:
pIOPortReg->GPHCON &= ~((3 << 4) | (3 << 6));
pIOPortReg->GPHCON |= ((2 << 4) | (2 << 6));
同时,初始化它的传输速率、每帧传输数据位数、有无奇偶校验和停止位等,主要是对UART0的各控制寄存器进行设置,如下:
UFCON0 = 0x0;//设置串口FIFO控制寄存器,禁用FIFO
UMCON0 = 0x0;//禁用
ULCON0 = 0x3;//选择每帧数据位数为8,停止位数为1,无奇偶校验
UCON0= 0x245;//选择串口波特率时钟,发送模式,接收模式
UBRDIV0=( (int)(PCLK/(16*115200) -1 );//设置串口波特率
③ 调用OEMPlatformInit()函数(main.c中实现):调用InitDisplay(),InitUSB(),Isr_Init()等函数完成平台初始化工作。
④ 调用OEMPreDownload()函数(main.c中实现):当平台的USB下载不可用时,调用此函数完成以太网下载前其他的一些准备工作。获得IP地址,初始化TFTP传输协议。
⑤ 调用DownloadImage()函数(在blcommon.c中实现):下载操作系统映像到SDRAM中,完成后进行TOC签名的检查。
⑥ 调用OEMLaunch()函数(在main.c中实现):启动操作系统映像。
3.2 OAL移植
OAL(OEM Adaptation Layer)从WinCE5.0以后,引入了OAL的新概念:产品质量级OAL,即PQOAL。它使OAL的目录结构标准化、代码分布模块化,降低了移植的难度[3]。WinCE5.0中OAL的代码主要分成4部分:板级代码、SoC芯片级代码、体系结构级代码和硬件无关级代码。
喷码机平台相较于学习板主要是在外围设备方面做了变动,因此这里完成OAL移植的主要工作就是修改板级OAL代码,位于WINCE500\\PLATFORM\\ HARDWAER PLATFORM NAME\\SRC\\ KERNEL\\OAL。在最后的编译过程中,OAL是被编译进操作系统内核的,因此OAL的启动流程实际也就是操作系统内核的启动流程。图2是操作系统的初始化启动流程。
图2 WinCE5.0内核启动顺序
其中的绝大部分函数由微软提供,并不需要修改,只有部分涉及到具体硬件的地方需要实现。
① startup函数。这是系统启动时调用的第一个函数,主要完成CPU和硬件的初始化等工作。本系统的OAL由BootLoader引导,很多硬件设备已经在那里完成初始化,所以此处startup的主要工作就是完成其余部分初始化然后跳转到OAL的主控函数KernelStart()处开始执行。部分代码如下:
LEAF_ENTRY StartUp
addr0, pc, #g_oalAddressTable (.+ 8)
blKernelStart
ENTRY_END
② 串口调试函数OEMInitDebugSerial()。其由ARMInit()函数调用,主要完成初始化串口的工作,与BootLoader分享相同的代码。
③ OEMInit()函数。OEMInit()函数也是由ARMInit()调用,主要完成硬件平台的初始化,包括cache globals、中断、系统时钟、KITL等,几乎完成了所有的硬件初始化工作[4]。如初始化I/O函数ConfigureGPIO()的部分代码如下:
s2440IOP->GPBDAT=0x60;
s2440IOP->GPBUP=0x7FF;
s2440IOP->GPBCON=0x2A96A8;
……
s2440IOP->GPGCON=0x16A4F3B4;
s2440IOP->GPGUP=0x9BDC;
……
具体操作就是根据喷码机平台对端口的要求,查S3C2440A芯片手册的I/O部分,根据规则设置相关寄存器来初始化它们的实际功用。这些所有的初始化工作都在WINCE500\\ PLATFORM\\HARDWAER PLATFORM NAME\\SRC\\ KERNEL\\OAL\\ init.c中实现。
④ 中断初始化函数OALIntrInit()。由OEMInit()函数调用,负责初始化外围硬件的中断控制器。首先调用OALIntrMapInit()初始化物理中断Irq和逻辑中断SysIntr的映射表,然后清除外部中断和内部中断,调用BSPIntrInit()对BSP中的GPIO中断进行初始化工作,这里移植时不作修改。
⑤ 中断处理函数OEMInterruptHandler()。OAL中对中断的处理主要是实现ISR部分。当发生硬件中断时,该函数就会被调用完成ISR部分的中断处理:读取系统的中断标记位、确定中断源、屏蔽中断并返回相应的系统中断号。然后触发相应的事件,由具体驱动程序的IST完成真正的中断处理。代码存放路径为WINCE500\\PLATFORM\\ HARDWAER PLATFORM NAME\\SRC\\ COMMON\\INTR\\intr.c,针对开发板实际设置的中断修改此代码。同时涉及到中断处理的函数OALIntrRequestIrqs()、OALIntrEnableIrqs()、OALIntrDisableIrqs() 和OALIntrDoneIrqs()都在intr.c中实现,它们相应地会调用BSPIntrRequestIrqs()、BSPIntrEnableIrq()、BSPIntrDisableIrq()、BSPIntrDoneIrq()来实现同一CPU的不同开发板对中断所做的一些修改。
⑥ 内核初始化函数KernelInit()。完成初始化系统API函数调用表、系统堆、内存池、内核进程和进程调度等工作,由微软提供。
⑦ FirstSchedule()。这实际上不是一个函数,而是armtrap.s文件中的一个标签,使第一个处于就绪态的线程执行[4]。
OAL作为内核与目标硬件之间的接口,主要对硬件的4种部分加以抽象:RTC、Timers、Caches和调试端口,针对实际情况改变了哪里的硬件就修改对应的代码。具体到喷码机硬件开发板,移植的重点是硬件初始化部分和中断处理部分。
3.3 驱动程序的实现
如果增加或删改了目标板的硬件设备,那么移植BSP时,就必须修改设备驱动程序。WinCE开发平台提供了多种类型的设备驱动程序,它们的源代码由两部分组成:与硬件平台无关的部分位于WINCE500\\PUBLIC\\COMMON\\OAK\\DRIVERS目录下,与硬件平台有关的部分位于WINCE500\\PLATFORM\\下相应BSP目录的DRIVERS[5]。
我们不需要修改与硬件平台无关的公共部分,只需要修改与硬件密切相关的源代码。具体到喷码机平台,主要就是XJ128喷头底层驱动的PMJ_Init(初始化设备)、PMJ_Deinit(卸载设备)、PMJ_Open(打开设备)、PMJ_Close(关闭设备)、PMJ_Write(写数据到设备)、PMJ_IOControl(设备的I/O控制)等12个流接口驱动函数。
3.4 移植配置文件
PB5使用两种配置文件来生成操作系统运行时的镜像,一种是源代码配置文件,另一种是映像配置文件[6]。配置文件的移植主要集中在Dirs、source、BIB以及REG文件。
根据实验结果,导入移植好的BSP编译出的系统镜像内存太大,烧入喷码机平台以后系统开机十分缓慢。因此需要在BINFS基础上实现Multibin技术来将内核分块,解决开机缓慢的问题。其具体实现主要在配置文件部分:修改config.bib的MEMORY部分如表1所列。
表1 config.bib文件MEMORY部分配置
将内核分为XIPKERNEL和NK两块,开机必备的内容存放在XIPKERNEL中,其他部分存放在NK中按需调用。经过实践,XIPKERNEL应包含如下的几个模块(添加在MODULES段):
MODULES
nk.exe $(_FLATRELEASEDIR)\\kern.exe XIPKERNEL SH
coredll.dll $(_FLATRELEASEDIR)\\coredll.dll XIPKERNEL SH
filesys.exe $(_FLATRELEASEDIR)\\filesys.exe XIPKERNEL SH
fsdmgr.dll $(_FLATRELEASEDIR)\\fsdmgr.dll XIPKERNEL SH
mspart.dll $(_FLATRELEASEDIR)\\mspart.dll XIPKERNEL SH
binfs.dll $(_FLATRELEASEDIR)\\binfs.dll XIPKERNEL SH
ceddk.dll $(_FLATRELEASEDIR)\\ceddk.dll XIPKERNEL SH
regenum.dll $(_FLATRELEASEDIR)\\regenum.dll XIPKERNEL SH
busenum.dll $(_FLATRELEASEDIR)\\busenum.dll XIPKERNEL SH
pm.dll $(_FLATRELEASEDIR)\\pm.dll XIPKERNEL SH
smflash.dll $(_FLATRELEASEDIR)\\smflash.dll XIPKERNEL SH
fatfsd.dll $(_FLATRELEASEDIR)\\fatfsd.dll XIPKERNEL SH
diskcache.dll $(_FLATRELEASEDIR)\\diskcache.dll XIPKERNEL SH
fatutil.dll $(_FLATRELEASEDIR)\\fatutil.dll XIPKERNEL SH
除此之外还要在FILES段添加下面两个模块:
FILES
boot.hv $(_FLATRELEASEDIR)\\boot.hv XIPKERNEL SH
wince.nls $(_FLATRELEASEDIR)\\wince.nls XIPKERNEL SH
配合修改platform.bib、common.bin文件完善系统各功能模块的镜像归属问题,同时在platform.reg中添加BINFS文件的支持。所有工作完成后,导入新的BSP,成功编译出的系统镜像如图3所示。
图3 编译生成的系统镜像
最终将XIP.BIN烧写到喷码机平台,系统启动时只需将2 MB的XIPKERNEL复制到RAM中,而不是原来将近30 MB的NK。
这样不仅将开机时间降低至10 s以内,同时增加了29 MB的可用RAM,使系统拥有了运行大型应用软件的能力。
结语
喷码机产品的应用领域越来越广,针对不同的需要设计开发板,在定制系统时就必须有配套的BSP。移植在满足应用要求的同时可以大大节省开发时间,减少产品的研发成本。
本文讨论了在喷码机硬件平台上开发WinCE5.0操作系统BSP的具体实现工作,对同一处理器下其他硬件平台的嵌入式开发具有一定的参考价值。