**********************************************
* VxWorks在EasyARM2200和SmartARM2200上的移植 *
**********************************************
------ 浅谈《ecos增值包》辅助开发VxWorks BSP
2007/04/07 asdjf@163.com www.armecos.com
随着《ecos增值包》用户群的增大,我们决定进一步增加对《ecos增值包》使用者的支持力度,如果能自行解决版权问题,那么希望下面的文章能对用户您开发VxWorks BSP有帮助。
目前,EasyARM2200和SmartARM2200开发板已经(增值)支持uCos、uCLinux、ecos、uITRON(ecos兼容层)、VxWorks等操作系统(Linux和WinCE需要带MMU的芯片);三款TCP/IP完整协议栈(openBSD、FreeBSD、lwip)、若干BootLoader(redboot、u-boot、bootrom、vivi、blob等)。有些用户用它快速开发交互式web server,有些用来作为辅助开发工具、有些用做快速原型验证......其实,《ecos增值包》是一个开发平台,它不仅限于ARM7,也适用于ARM15、ARM16等未来芯片,因为ecos支持对PowerPC、MIPS、X86、SH等不同体系架构的抽象,所以,您针对ecos平台开发的软件将很少/不用改动就可以平滑移植到新的体系架构上,保护您的投资利益。ecos平台的抽象机制(HAL层、设备文件、C++等)确保您将注意力集中在事物的核心本质上,而不必被细节干扰。例如:您可以针对ecos抽象出的万能中断模型编程,而不必关心具体CPU的中断体系;只对串口设备文件操作而不管UART寄存器的细节等。即使您没有很丰富的经验,也能轻松做项目,因为ecos平台将辅助您解决稳定性、效率、功耗、成本等棘手问题。ecos提供了FS、GUI、TCP/IP等全套功能部件,完全满足您对数据存储、显示、网络互联、控制等方面的需求,是完整的一揽子解决方案。
从ecos的观点看VxWorks BSP的开发,主要是平台和变种抽象层的移植,即我们要针对ARM7变种LPC2210和其承载平台EasyARM2200、SmartARM2200进行移植。ecos和VxWorks简直太象了(仅函数名不同):都使用组件概念(不知是谁抄谁的),都用GNU编译器编译(或diab),都实现差不多的功能。这为使用《ecos增值包》开发VxWorks BSP提供了便利。
-----------------------
| VxWorks BSP移植规划 |
-----------------------
1、实现硬件初始化
2、实现LPC2210中断体系到VxWorks中断体系的映射
3、实现心跳时钟、辅助时钟、时标
4、实现基于查询/中断的串口驱动
------------------------------------------------MileStone
5、实现NOR/NAND Flash驱动
6、实现多种字符设备、块设备、MUX网络设备驱动程序
7、实现TSFS/dosFS/TrueFFS文件系统、MiniGUI等
......
------------
| 移植难点 |
------------
1、工具使用:Makefile编写、反汇编、调试器使用、Tornado使用等
2、对LPC2210工作原理、硬件体系的理解
3、对BSP目录结构、工作流程、内存分布、ROM映像类型的理解等
4、正确的调试分析方法、完备的调试环境、有效的技术支持
---------------------------------------
| 《ecos增值包》针对BSP开发的解决方案 |
---------------------------------------
《ecos增值包》本身就提供GNU开发环境,提供step by step的指导,省去了自己搭建环境的苦恼,可以快速上手。文档中有专门章节讲解Makefile使用,中断体系、串口原理等等,这对于攻克难点有莫大的帮助。进行嵌入式开发不能光看书,任何书本都不能面面俱到,而且写出的书,信息量至少损失20%,理解起来又会损失20%,真正做还要再损失至少20%,所以,比较好的方法是靠“熏”。不断在一个已经搭建好的稳定平台上反复做实验,举一反三,天长日久,就会不知不觉熏陶出来。很多用户不懂得调试,遇到问题就问别人,其实首先应该问计算机,掌握调试方法就相当于掌握了打渔的方法,学会了点金术,发现问题,debug it,trace it,解决问题,It's hacker's solution。
-----------------------
| VxWorks BSP目录结构 |
-----------------------
BSP(板级支持包)就是指VxWorks的驱动程序。当然,其他一些操作系统也有自己的BSP驱动,还有些系统的驱动程序不叫BSP,总之,这里的BSP就是指驱动程序啦。
安装VxWorks和其BSP开发环境后,会出现c:\Tornado2.2目录,在target子目录下的内容就是BSP的相关内容。
------config BSP配置文件
| |
| |-----all 通用配置文件
| |_____BspName 板级支持包
|
target |---H 头文件
|---Lib 库文件
|---Man 说明文件
|---Src VxWorks特殊的源代码
|___Unsupport 工具及驱动
我们实际关心的只有target\config\all和target\config\BspName两个目录。target\config\BspName里面是我们要移植的真正的BSP程序。
-------------------
| VxWorks工作流程 |
-------------------
VxWorks分为bootrom和内核映像两部分,分别由不同文件完成功能。
-------------------------------------------
bootrom执行流程:
-------------------------------------------
romInit
(target\config\zlgarm\romInit.s)
|
romStart
(target\config\all\bootInit.c)
|
usrInit
(target\config\all\bootConfig.c)
|
bootCmdLoop
|
-----------------
| |
autoBoot |_bootLoad
|_bootLoad |_go
|_go
-------------------------------------------
VxWorks内核映像启动流程图:
-------------------------------------------
sysInit
(target\config\zlgarm\sysALib.s) 硬件相关
| ----------------
usrInit 通用启动过程
(target\config\zlgarm\sysALib.s)
|
-----------------
| | |
usrKernelInit | sysHwInit
| (target\config\zlgarm\sysLib.c)
kernelInit
|
Hooks-->usrRoot
|
|--------
|
sysClkConnect
|
|---------
|
sysHwInit2
bootrom首先执行硬件初始化romInit,它是用汇编写的,主要考虑到有些操作不能用C语言完成,例如开关中断,特权堆栈初始化等。然后使用C语言写的程序romStart拷贝搬移ROM映像,清RAM,解压代码(如果需要的话),这部分用C的好处是编程方便。usrInit是用来执行最小内核初始化的,然后进入命令循环,分两种情况,如果7秒钟(可设置)内无按键就自动执行预先烧好的程序,如果没有找到程序,就不断重复7秒等待过程。如果有按键按下,就进入命令行界面接收用户命令。
VxWorks内核被加载后首先执行硬件相关初始化,这部分初始化工作与bootrom中的相同。为什么要重复执行相同的硬件初始化呢?主要是考虑到VxWorks可能被其他bootloader加载,为了保证工作环境一致,所以这里再次重复相同的硬件初始化过程。接下来配置内核数据、初始化内核、启动usrRoot任务、连接启动时钟心跳,OK,现在系统就活了。虽然VxWorks一般不开源,但提供很多钩子(Hooks)函数,可以选择适当位置插入用户代码,完成特殊需求。
-------------------
| VxWorks内存布局 |
-------------------
内存布局是VxWorks移植中需要非常准确理解的内容,它关系到config.h的配置和Makefile的编写,如果处理不好或者概念搞错了,那就全乱了。
-------------- 0x00100000 = LOCAL_MEM_SIZE = sysMemTop()
| |
| RAM |
| 0 filled |
| |
|------------| = (romInit+ROM_COPY_SIZE) or binArrayStart
| ROM image |
|----------- | 0x00090000 = RAM_HIGH_ADRS
| STACK_SAVE |
|------------|
| | 0x00080000 = 0.5 Megabytes
| |
| |
| 0 filled |
| |
| | 0x00001000 = RAM_ADRS & RAM_LOW_ADRS
| |
| | exc vectors, bp anchor, exc msg, bootline
| |
| |
-------------- 0x00000000 = LOCAL_MEM_LOCAL_ADRS
--------------
| ROM |
| | 0xff8xxxxx = binArrayStart
| |
| | 0xff800008 = ROM_TEXT_ADRS
-------------- 0xff800000 = ROM_BASE_ADRS
上图是一个内存布局实例(1M字节RAM空间),地址值是自己编的,主要看宏定义。
ROM_BASE_ADRS是指ROM的起始地址,例如:0x80000000。
ROM_TEXT_ADRS是代码段的起始地址,有些CPU不能从0开始执行,所以需要一个偏移量,不同CPU的要求不同,一般和ROM_BASE_ADRS值相同。
LOCAL_MEM_LOCAL_ADRS是指可用RAM的起址,如:0x81000000。
RAM_LOW_ADRS是内核在内存中的加载位置。
RAM_HIGH_ADRS是需要拷贝到内存的ROM映像和堆栈的分界线,向下是堆栈,向上是ROM映像的起址。
未用的部分可以选择清零(用“0”填充)。
大体上就是这么一个分布,但是限于篇幅,还有很多细节没有谈到。比如:堆栈保护、解压缩代码空间、不同ROM映像类型的内存分布差异等。这些看代码可以详细了解,还是那句话,亲自调试就能了解全部细节,写出来的内容总会有信息量的损失,从调试中你能得到最准确全面的信息。
------------------------
| VxWorks的ROM映像类型 |
------------------------
VxWorks有三种ROM映像类型:
1、ROM_RESIDENT-----驻留ROM型。在ROM中运行,只有数据段放在RAM里。
2、ROM_COPY---------复制ROM型。把主要映像从ROM中拷贝到RAM里并跳到其RAM入口点执行。
3、ROM_COMPRESS-----解压ROM型。把主要映像从ROM中解压到RAM里并跳到其RAM入口点执行。
各种ROM映像类型的内存布局图如下:
ROM
--------------
| |
|------------|
| data |
|------------| 0xff8xxxxx = ROM_DATA_ADRS
| text |
| | 0xff800008 = ROM_TEXT_ADRS
-------------- 0xff800000 = ROM_BASE_ADRS
RAM
-------------- 0x00100000 = LOCAL_MEM_LOCAL_ADRS + LOCAL_MEM_SIZE
| |
| |
|------------| = RAM_DATA_ADRS + data segment size
| |
|data segment|
| |
|------------| 0x00001000 = RAM_DATA_ADRS
| initial sp |
|------------| = RAM_DATA_ADRS - STACK_SAVE
| |
| |
-------------- 0x00000000 = LOCAL_MEM_LOCAL_ADRS
上图是驻留ROM型映像的内存分布,可见,只有数据段拷贝到了RAM内存里,当然堆栈也在RAM内存中。
ROM
--------------
| |
|------------| 0xff8xxxxx = binArrayEnd
| subImage |
|------------| 0xff8xxxxx = binArrayStart
| data |
|------------| 0xff8xxxxx = ROM_DATA_ADRS
| text |
| | 0xff800008 = ROM_TEXT_ADRS
-------------- 0xff800000 = ROM_BASE_ADRS
RAM
-------------- 0x00100000 = LOCAL_MEM_LOCAL_ADRS + LOCAL_MEM_SIZE
| |
| |
|------------|
| temp data |
|------------| 0x00090000 = RAM_DATA_ADRS
| initial sp |
|------------| = RAM_DATA_ADRS - STACK_SAVE
| |
| |
|------------|
| |
| subimage |
| |
|------------| 0x00001000 = RAM_DST_ADRS (for non-resident images)
| |
| |
-------------- 0x00000000 = LOCAL_MEM_LOCAL_ADRS
上图是复制ROM型和解压ROM型映像的内存分布,他们比驻留ROM型稍微有点复杂。复制ROM型和解压ROM型映像的主要差别的是subimage是否压缩,一般压缩率能超过55%,允许在ROM中烧写更大的系统,唯一的代价是增加了几秒钟的启动延迟,而复制ROM型映像没有解压过程,所以启动速度更快。subimage是由make产生的中间映像,并被make插在ROM映像文件中。代码段、初始化数据段、subimage在ROM中的排列顺序就如上图所示。
复制ROM型映像直接把binArrayStart处的subimage拷贝到RAM_DST_ADRS,没有上面的temp data部分。
对于解压ROM型映像,首先拷贝压缩的代码数据段到RAM_DATA_ADRS位置(即temp data部分),然后运行解压缩例程把解压后的subimage放置在RAM_DST_ADRS。
RAM_DST_ADRS和RAM_DATA_ADRS宏均来自定义于make的链接地址,缺省值分别为Makefile文件中的RAM_LOW_ADRS和RAM_HIGH_ADRS。关于如何改变链接地址的信息,参见“target/h/make/rules.bsp”文件。
---------------
| BSP移植详述 |
---------------
罗嗦了那么多,终于开始正式移植了。还是前面那句话,写出来的东西总是挂一漏万,无论我怎么组织语言,总有说不完的话,数不清的注意事项,虽然心里很明白,但如果每一点都指出来,那会让读者更晕,云里雾里的,没有实际做过,很难理解我说的细节,有些妙处真的是只可意会不可言传,我也不知道怎么表达出来,并非留一手。我希望帮你“熏”一下,能帮多少算多少,最重要的是你要亲自实践,光看游泳书是学不会游泳的。
==========
硬件初始化
==========
硬件初始化直接抄板子自带的汇编初始化源码,改进一下执行效率即可。
======
中断
======
中断需要提供:初始化、返回向量号、中断使能、中断禁止函数。
中断初始化真是妙不可言,因为LPC2210提供VIC映射,所以一步就可以得到向量号,不必查询,所以初始化做得好,中断响应效率可以很高。中断使能只要对LPC2XXX_VIC_INT_ENABLE寄存器对应位置1即可;中断禁止时对LPC2XXX_VIC_INT_ENABLE_CLR寄存器对应位置1。
在调试BSP时,运行后总是没有动静,通过内存打印技术,发现程序死在了excVecInit()函数处。见名知意,这个函数肯定和中断向量初始化有关,但它到底是如何工作的呢?虽然有源码,但那个是for X86的,对于ARM体系相关的函数部分没有指导意义啊,怎么办呢?此时,需要祭出战无不胜,攻无不克,见神杀神,见鬼杀鬼的利器---反汇编调试。
《ecos增值包》提供了GNU开发环境,使用“arm-elf-objdump -d vxWorks_romResident > 1.txt”就可以得到VxWorks的汇编列表文件1.txt,里面有地址和汇编信息,可用于和AXD汇编对照调试。
8101a644 <excVecInit>:
8101a644: e92d4800 stmdb sp!, {fp, lr}
8101a648: e24dd008 sub sp, sp, #8 ; 0x8
8101a64c: eb002d4e bl 81025b8c <armInitExceptionModes>
8101a650: e59fb4d4 ldr fp, [pc, #1236] ; 8101ab2c <$d> fp=81301524 excEnterTbl
8101a654: e3a01005 mov r1, #5 ; 0x5
8101a658: e59f04d0 ldr r0, [pc, #1232] ; 8101ab30 <$d+0x4> r0 = e59ff0f4
8101a65c: e24bb008 sub fp, fp, #8 ; 0x8
8101a660: e59b3008 ldr r3, [fp, #8]
8101a664: e2511001 subs r1, r1, #1 ; 0x1
8101a668: e5830000 str r0, [r3]
8101a66c: e59bc008 ldr ip, [fp, #8]
8101a670: e59b300c ldr r3, [fp, #12]
8101a674: e28bb008 add fp, fp, #8 ; 0x8
8101a678: e58c30fc str r3, [ip, #252]
8101a67c: 1afffff7 bne 8101a660 <excVecInit+0x1c> 8101a660
8101a680: e3a0c000 mov ip, #0 ; 0x0
8101a684: e59f34a8 ldr r3, [pc, #1192] ; 8101ab34 <$d+0x8> r3 = e7fddefe
8101a688: e58c3000 str r3, [ip]
8101a68c: e59fc4a4 ldr ip, [pc, #1188] ; 8101ab38 <$d+0xc> ip = 813087C8
8101a690: e59cb01c ldr fp, [ip, #28] fp = [813087E4]
8101a694: e35b0000 cmp fp, #0 ; 0x0
8101a698: 0a000003 beq 8101a6ac <excVecInit+0x68> 8101a6ac
8101a69c: e3a00000 mov r0, #0 ; 0x0
8101a6a0: e3a0101c mov r1, #28 ; 0x1c
8101a6a4: e1a0e00f mov lr, pc
8101a6a8: e1a0f00b mov pc, fp
8101a6ac: e59f3488 ldr r3, [pc, #1160] ; 8101ab3c <$d+0x10> r3 = 8101a6dc
8101a6b0: e59fc488 ldr ip, [pc, #1160] ; 8101ab40 <$d+0x14> ip = 813102a4 _func_armIrqHandler
8101a6b4: e58c3000 str r3, [ip] _func_armIrqHandler = 8101a6dc excIntHandle
8101a6b8: e3a00000 mov r0, #0 ; 0x0
8101a6bc: e28dd008 add sp, sp, #8 ; 0x8
8101a6c0: e8bd8800 ldmia sp!, {fp, pc}
8101b494 <intVecBaseSet>:
8101b494: e1a0f00e mov pc, lr
通过反复分析,intVecBaseSet在ARM体系上屁用也没有,是空的,根本不能指望通过它改变中断向量基址VEC_BASE_ADRS。这段汇编的大概意思是:在0地址开始处填写中断向量表跳转语句,在100H处写跳转地址,没有中断服务子程序的入口填写0xE59FF9F4(未定义指令,用于引发异常)。怪不得死机,LPC2210重映射到0地址的RAM空间只有64字节,向100H只读地址写数据会引发异常(44B0向ROM里写数据不会引发异常,顶多写不进去就是了,看来LPC2210在地址空间防护上做了一些工作,能识别出向只读空间里写数据的错误。虽然是好事,但给我们移植BSP带来了困难,怎么办呢?)。VxWorks考虑得真是周到,这部分是用源码提供的,那就咔嚓了excVecInit(),换成自己的myExcVecInit(),齐活。
VxWorks提供的中断处理函数不能动,因为要使用VxWorks的中断体系,由它来调用我们提供的处理函数,这样,就把LPC2210的中断体系映射到了VxWorks上。
既然可以替换成自己的代码,那就不用仿照VxWorks向量表原来的构造了,把它推翻,换个和LPC2210匹配更好的结构。我用LPC2210内部IRAM保存向量表和ISR服务程序入口地址,因为内部IRAM快,还可节省一些外部XRAM空间,然后把它映射到0地址即可(真是绝配啊!)。汇编源程序如下:(target\config\zlgarm\sysALib.s)
.globl FUNC(myExcVecInit) /* own code for armInitExceptionModes()---zk */
.extern FUNC(excEnterUndef)
.extern FUNC(excEnterSwi)
.extern FUNC(excEnterPrefetchAbort)
.extern FUNC(excEnterDataAbort)
.extern FUNC(intEnt)
.extern FUNC(armInitExceptionModes)
.extern FUNC(_func_armIrqHandler)
.extern FUNC(excIntHandle)
_ARM_FUNCTION(myExcVecInit)
stmfd sp!, {r0-r10,lr}
bl FUNC(armInitExceptionModes)
copy_vector:
adr r0, real_vectors
add r2, r0, #64
ldr r1, =0x40000000 /*前面的初始化程序已经把此RAM的前64字节重映射到了0地址*/
/*add r1, r1, #0x08*/
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble vector_copy_loop
/*反汇编的程序在此处判断了一个内存中的变量,但我没找到该变量名,没判断变量是否为0就直接赋值了。*/
/*看效果没有任何不良影响。*/
ldr r0, L$__func_armIrqHandler
ldr r1, L$_excIntHandle
str r1, [r0]
nop
ldmfd sp!, {r0-r10,pc}
/*************************************************/
/* interrupt vectors */
/*************************************************/
real_vectors:
ldr pc,.reset //0x00
ldr pc,.undefined_instruction //0x04
ldr pc,.software_interrupt //0x08
ldr pc,.prefetch_abort //0x0C
ldr pc,.data_abort //0x10
ldr pc,.not_used //0x14
ldr pc,.irq //0x18
ldr pc,.fiq //0x1C
/*************************************************/
.reset: .word 0xE59FF9F4
.undefined_instruction: .word FUNC(excEnterUndef)
.software_interrupt: .word FUNC(excEnterSwi)
.prefetch_abort: .word FUNC(excEnterPrefetchAbort)
.data_abort: .word FUNC(excEnterDataAbort)
.not_used: .word 0xE59FF9F4 /* not use */
.irq: .word FUNC(intEnt)
.fiq: .word 0xE59FF9F4 /* fiq */
注意噢:子程序要压栈保存所有改变的寄存器(除非明确需要改变,如传参数值,才不需要保存恢复),不然不要怪我没有提醒你ARM编译器会优化程序,使用寄存器传值,如果你的子程序内部改变了寄存器值又没有恢复原先的值,那么插入你自编的函数,会发生很多奇妙的事哦!
stmfd sp!, {r0-r10,lr}
ldmfd sp!, {r0-r10,pc}
============================
调试器、内存打印、点灯的异同
============================
这三者都是很好的调试方法,尤其在调试BSP类程序时很有用,此时,串口还没有工作,但又想看到信息以便进行分析,怎么办?凉拌。
点灯比较简单,看看灯的亮灭和组合就知道程序现在跑到哪了,适合问题定位。
内存打印可以表达更多信息,但需要bootloader支持,通过命令行查看内存。
调试器功能最强,有些支持源码调试,相当方便。打印语句调试是让被调程序牵着鼻子走,调试器是牵着被调程序鼻子走(可以任意修改走向),想牵谁的鼻子自己决定。
《ecos增值包》提供的调试环境适合粗调,尽管需要被被调程序牵着鼻子走,配合AXD调试(不适合粗调,容易陷在细节中,最好先粗调定位问题,再对问题点用调试器细调),还是很快的。
========
时钟驱动
========
时钟驱动需要实现:系统时钟和辅助时钟中断、连接、禁止、使能、读、写,时标中断、连接、禁止、使能、读周期、读频率、读tick值、锁中断读tick值。
需要注意的是,每次时钟中断ISR处理时都别忘了清时钟中断,不然,CPU一直陷入中断,就不能做别的事情了。具体代码可以参照其他BSP模板(在target\config\目录下就是各种ARM板子的BSP移植源码)并结合LPC2210开发板的时钟驱动范例自行写出。
========
串口驱动
========
VxWorks的串口驱动比较特别,与其他驱动不同。
VxWorks串口驱动同时支持基于查询和中断的驱动,可以在运行时通过IOCTL配置运行模式和属性。
查询方式很简单,这里主要说下基于中断的驱动。
LPC2210的UART收发中断共用同一个中断号,我们在中断服务程序里先收后发,通过状态寄存器判断是收中断还是发中断亦或是超时、错误中断。
(void) intConnect(INUM_TO_IVEC(devParas[i].vector), zlgarmInt, (int)&zlgarmChan[i] );
intEnable(INUM_TO_IVEC(devParas[i].vector));
把串口中断ISR服务程序挂在相应中断号devParas[i].vector上并使能中断。这样每次UART中断都会调用zlgarmInt函数。
void zlgarmInt(ZLGARM_CHAN *pChan)
{
zlgarmIntRcv(pChan);
zlgarmIntTx(pChan);
}
在zlgarmInt里先调用收函数,再调用发函数,实现收发操作共用同一个中断号的目的。
LOCAL int zlgarmTxStartup
(
SIO_CHAN * pSioChan /* channel to start */
)
{
ZLGARM_CHAN * pChan = (ZLGARM_CHAN *)pSioChan;
unsigned int stat;
unsigned char * base = (unsigned char *)pChan->regs;
zlgarmIntTx(pChan);
HAL_READ_UINT8(base+LPC2XXX_UART_IER, stat);
stat = stat | LPC2XXX_UART_IER_TXE;
HAL_WRITE_UINT8(base+LPC2XXX_UART_IER, stat);
return (OK);
}
每次发送前,VxWorks先调用zlgarmTxStartup激活中断,使后续发送自动化。
void zlgarmIntTx
(
ZLGARM_CHAN * pChan /* channel generating the interrupt */
)
{
unsigned int stat;
char outChar;
unsigned char * base = (unsigned char *)pChan->regs;
HAL_READ_UINT8(base + LPC2XXX_UART_LSR, stat);
if((stat & LPC2XXX_UART_STAT_TXE) == 0)
return;
if ((*pChan->getTxChar) (pChan->getTxArg, &outChar) != ERROR)
HAL_WRITE_UINT8(base + LPC2XXX_UART_THR, outChar);
else
{
HAL_READ_UINT8(base+LPC2XXX_UART_IER, stat);
stat = stat & (~LPC2XXX_UART_IER_TXE);
HAL_WRITE_UINT8(base+LPC2XXX_UART_IER, stat);
}
}
在发送时先判断是否正在发送,如果发送FIFO不为空,那么,肯定正在发送数据,程序退出,等到发送结束,发送中断会自动调用这个程序再次发送的。如果发送FIFO为空,说明已发送完毕,此时上层回调函数getTxChar从VxWorks管理的循环队列中取出一个字节数据继续发送,只要字符在发送状态,发送中断就会消失,一旦发送完毕会再次触发。如果循环队列中已无字符,一定要记住关闭发送中断使能,不然,由于发送FIFO为空,发送中断总是有效,CPU会反复陷入发送中断。
void zlgarmIntRcv
(
ZLGARM_CHAN * pChan /* channel generating the interrupt */
)
{
unsigned int stat;
unsigned int c;
unsigned char * base = (unsigned char *)pChan->regs;
HAL_READ_UINT8(base + LPC2XXX_UART_LSR, stat);
if ((stat & LPC2XXX_UART_STAT_RDR) != 0)
{
HAL_READ_UINT8(base+LPC2XXX_UART_RBR, c);
(*pChan->putRcvChar) (pChan->putRcvArg, c);
}
}
接收中断比较简单,只要判断接收状态即可,调用上层回调函数putRcvChar把收到的字符存入VxWorks管理的循环队列。
总体感觉VxWorks串口架构设计得非常合理和灵活,获益良多!妙不可言!
=========
flash驱动
=========
略
===========
MUX网络驱动
===========
略
=====================
Makefile和onfig.h配置
=====================
见《VxWorks内存布局》节
========
编译方法
========
---------------
bootrom编译方法
---------------
拷贝c:\Tornado2.2\host\x86-win32\bin\torVars.bat到c:\Tornado2.2\target\config\zlgarm目录并改名为zlgarmmake.bat。增加语句,最终内容如下:
rem Command line build environments
set WIND_HOST_TYPE=x86-win32
set WIND_BASE=C:\Tornado2.2
set PATH=%WIND_BASE%\host\%WIND_HOST_TYPE%\bin;%PATH%
rem Diab Toolchain additions
set DIABLIB=%WIND_BASE%\host\diab
set PATH=%DIABLIB%\WIN32\bin;%PATH%
make clean
make bootrom_res.bin
以后只要双击该文件就可以编译驻留ROM型bootrom映像。
---------------
vxworks编译方法
---------------
打开Tornado编译器,选择build标签,右键设定default_romResident为激活编译类型。以后只要点击编译图标即可。
注意:
每次修改Makefile和config.h后都要重新生成项目,因为Tornado使用的是上一次配置的项目,本次修改配置无效。
注释使用“/* */”,不要使用“//”。
========
使用方法
========
把bootrom或vxworks程序烧写到flash里,启动后就可以看到logo启动界面。enjoy it! ^_^
有了BSP,就可以专心开发VxWorks应用程序了,只要在生成项目时指定BSP即可。
《ecos增值包》用户(需提供识别码)对VxWorks BSP在EasyARM2200和SmartARM2200上移植有任何疑问均可来信询问(asdjf@163.com),不提供源码,提供文档支持。