gdtyy
驱动牛犊
驱动牛犊
  • 注册日期2002-08-15
  • 最后登录2012-02-16
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望16点
  • 贡献值0点
  • 好评度4点
  • 原创分0分
  • 专家分0分
阅读:2713回复:3

VxWorks在EasyARM2200和SmartARM2200上的移植

楼主#
更多 发布于:2007-07-14 07:50
**********************************************
* 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.vector), zlgarmInt, (int)&zlgarmChan );
    intEnable(INUM_TO_IVEC(devParas.vector));
    把串口中断ISR服务程序挂在相应中断号devParas.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),不提供源码,提供文档支持。
gdtyy
驱动牛犊
驱动牛犊
  • 注册日期2002-08-15
  • 最后登录2012-02-16
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望16点
  • 贡献值0点
  • 好评度4点
  • 原创分0分
  • 专家分0分
沙发#
发布于:2007-07-14 07:51
VxWorks for LPC2210应用编程实战
***********************************
* VxWorks for LPC2210应用编程实战 *
***********************************
                      ------ 《ecos增值包》选配之VxWorks应用编程实战
    2007/04/22  asdjf@163.com  www.armecos.com
    
    vxWorks_romResident.bin试用版下载 483K
    
    《ecos增值包》用户可以选配VxWorks for LPC2210,以便在EASYARM2200或SMARTARM2200上进行VxWorks应用程序开发。该选件不单独出售,不含BSP源码,需另行购买开发环境。如过您满足这些要求,那么希望下面的文章能对您开发VxWorks应用程序有所帮助。
    
    VxWorks和ecos一样,都是多任务操作系统,都用GNU工具链开发,只是API函数名略有不同,因此,我们分别用ecos中介绍过的两个demo程序对照讲解VxWorks应用编程的开发方法。
    
    与ucos51和ecos相似,第一次使用操作系统,先弄个多任务跑跑,demo1演示了多任务并发运行(注:本站也出售ucos51软件包,51工程师也可以跑OS)。3个线程任务同时打印信息,A线程每秒打印一次,B线程每3秒打印一次,C线程每6秒打印一次。对比这些程序,其实就是API函数名字不同啦,思路完全相同,这说明OS抽象了硬件和时间流,使我们更接近应用的实质。下面是demo1源码:

#define STACK_SIZE 2000

void taska(int arg);
void taskb(int arg);
void taskc(int arg);

void demo1(void)
{
    printf("Demo1 Start......\n");
    
    taskSpawn("taska", 90, 0, STACK_SIZE, taska, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    
    taskSpawn("taskb", 90, 0, STACK_SIZE, taskb, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0);
    
    taskSpawn("taskc", 90, 0, STACK_SIZE, taskc, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

void taska(int arg)
{
    for(;;)
        {
        printf("\tAAAAAA111111 is active.thread data is %d.\n", arg);
        taskDelay(100);
        }
}

void taskb(int arg)
{
    for(;;)
        {
        printf("\tBBBBBB333333 is active.thread data is %d.\n", arg);
        taskDelay(300);
        }
}

void taskc(int arg)
{
    for(;;)
        {
        printf("\tCCCCCC666666 is active.thread data is %d.\n", arg);
        taskDelay(600);
        }
}

    由上可见,VxWorks中创建任务使用taskSpawn,延时使用taskDelay。下表对比了创建任务和任务延时在ucos51、ecos、VxWorks中的名称。
    ------------------------------------------------------
    |         |      创建任务       |      任务延时      |
    ------------------------------------------------------
    | ucos51  |    OSTaskCreate     |     OSTimeDly      |
    ------------------------------------------------------
    |  ecos   |  cyg_thread_create  |  cyg_thread_delay  |
    ------------------------------------------------------
    | VxWorks |     taskSpawn       |     taskDelay      |
    ------------------------------------------------------
    
    VxWorks函数语法:
    
    任务号 = taskSpawn(名称,优先级,选项,堆栈大小,任务函数名,参数1,...参数10);
    taskDelay(延时tick数);
    
    测试程序运行结果,Demo1程序创建了3个任务A、B、C,优先级全为10,A每秒显示一次,B每3秒显示一次,C每6秒显示一次。从显示结果看,显示3个A后显示1个B,显示6个A和2个B后显示1个C,结果显然正确。
    
    (demo1运行效果图)
    
    下面的demo2例子是控制GPIO让蜂鸣器发声的应用程序,响1秒停1秒,周而复始。

#define HAL_WRITE_UINT32( _register_, _value_ ) \
        (*((volatile unsigned int *)(_register_)) = (_value_))

#define LPC2XXX_GPIO_IO0SET       0xE0028004
#define LPC2XXX_GPIO_IO0DIR       0xE0028008
#define LPC2XXX_GPIO_IO0CLR       0xE002800C

#define STACK_SIZE 2000
#define BEEPCON 0x0000080

void task(void);

void demo2(void)
{
    printf("Demo2 Start......\n");
    
    taskSpawn("task", 90, 0, STACK_SIZE, task, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

void task(void)
{
    HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0DIR,BEEPCON);
    
    for(;;)
        {
        HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0SET,BEEPCON);
        taskDelay(100);
        HAL_WRITE_UINT32(LPC2XXX_GPIO_IO0CLR,BEEPCON);
        taskDelay(100);
        }
}

    由上可见,VxWorks支持直接寄存器操作,使用平板内存模式,和ecos操作硬件I/O的方法一样。
    
    注意:应用程序使用/**/做注释,如果使用"//"注释,需要去除-ansi编译选项。
    
    下面图示说明使用方法:
    
    首先将vxWorks_romResident.bin文件烧写在板子的flash里,设置好跳线。上电后将看到控制台shell界面,
    
    打开Tornado,新建downloadable项目,
    
    (新建项目图)1、2、3
    
    
    
    
    增加demo文件并编译,会提示生成依赖文件,选OK。
    
    4、5
    
    
    
    设置Target Server(配置文件设置),使用串口通信,9600波特率,好象最高速率为38400,太高就不容易连接成功,用网络的话就比较快。注意:Core File and Symbols里的File文件要与烧写在flash里的VxWorks一致,即带调试信息的ELF文件。连接成功会显示成功信息,托盘里出现一个象靶子一样的图标。
    
    6、7、8、9
    
    
    
    
    
    下载.out文件
    
    10
    
    
    启动目标调试器,点那个象蚂蚁一样的图标。打开源文件设置断点什么的,然后点那个奔跑小人图标,就可以调试了,支持单步、全速等各种调试手段。开始时会询问运行任务函数名称和参数,如图设置即可。
    
    13、14、11
    
    
    
    
    串口0控制台运行效果
    
    12
    
    
    点击->i图标可以启动主机shell,输入i可以查看任务运行情况。刚开始有4个任务,执行任务、日志任务、shell终端、wdb调试驻留任务。当运行demo1后再输入i查询,可以看到多出了3个新任务:taska、taskb、taskc,正是我们在demo1里创建的3个任务。可以清楚看到任务优先级和延迟剩余时间及状态等信息。
    
    15、16
    
    
gdtyy
驱动牛犊
驱动牛犊
  • 注册日期2002-08-15
  • 最后登录2012-02-16
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望16点
  • 贡献值0点
  • 好评度4点
  • 原创分0分
  • 专家分0分
板凳#
发布于:2007-07-14 07:51
VxWorks之MUX网络驱动在EasyARM2200和SmartARM2200上的实现
***********************************************************
* VxWorks之MUX网络驱动在EasyARM2200和SmartARM2200上的实现 *
***********************************************************
                          ------ 浅谈《ecos增值包》辅助开发VxWorks网络驱动
    2007/05/11  asdjf@163.com  www.armecos.com
    
    又有一些网友购买《ecos增值包》了,下面进一步谈谈VxWorks下网络驱动程序的开发,目标板为EasyARM2200和SmartARM2200(网卡芯片为RTL8019AS),调试环境是redboot,依靠打印输出调试。
    
    --------------------
    | 网络驱动程序概述 |
    --------------------
    在VxWorks中,网卡驱动程序分为END(Enhanced Network Driver)和BSD两种。END驱动程序基于MUX模式,是目前在VxWorks操作系统上应用最广泛的一种网络驱动程序。在该模式下,网络驱动程序被划分为协议组件和硬件组件,它们之间没有内部交换数据,只通过MUX间接相互作用。MUX接口的作用是分解协议和硬件网络驱动程序,从而使它们几乎独立,这种独立使添加新的驱动程序和协议变得简单。例如:添加一个新的网络驱动,则所有现有基于MUX的协议均可使用新的驱动程序;同样,添加一个新的基于MUX的协议,则任何现有的网络驱动均可通过MUX接口来访问新的协议。如下图所示:
    
    协议A   协议B  ......
      |       |             |
      -----------------------
                 |
                MUX
                 |
      -----------------------
      |       |             |
    设备1   设备2  ......
    
    通过MUX接口,协议和硬件设备相互独立,任何协议都可以通过MUX统一接口访问任何硬件设备,任何硬件设备也可以通过MUX接口支持所有协议。
    
    VxWorks MUX驱动调用关系如下图所示:
    
    ------------------------       ---------------------       ---------------------
    |                      |------>| muxBind()         |       |                   |
    | stackTxShutdownRtn() |<------| muxUnbind()       |       |                   |
    |                      |       |                   |       |                   |
    |                      |       | muxDevLoad()      |------>| endLoad()         |
    |                      |       | muxDevUnload()    |------>| endUnload()       |
    |                      |       |                   |       |                   |
    | stackRcvRtn()        |<------| muxReceive()      |<------|                   |
    | stackError()         |<------| muxError()        |<------|                   |
    |                      |       |                   |       |                   |
    |                      |------>| muxSend()         |------>| endSend()         |
    | stackTxRestartRtn()  |<------| muxTxRestartRtn() |<------| endTxRestartRtn() |
    |                      |------>| muxMCastAddrGet() |------>| endMCastAddrGet() |
    |                      |------>| muxMCastAddrDel() |------>| endMCastAddrDel() |
    |                      |------>| muxMCastAddrAdd() |------>| endMCastAddrAdd() |
    |                      |------>| muxPollSend()     |------>| endPollSend()     |
    |                      |------>| muxPollReceive()  |------>| endPollReceive()  |
    |                      |------>| muxIoctl()        |------>| endIoctl()        |
    |                      |       |                   |       |                   |
    |                      |       |                   |------>| endStart()        |
    |                      |       |                   |------>| endStop()         |
    ------------------------       ---------------------       ---------------------
          协议层接口                      MUX接口                     END接口
    
    VxWorks启动时执行任务tUsrRoot来完成驱动程序的安装,它产生tNetTask任务来处理网络任务工作队列中的条目,调用muxDevLoad()来加载网络驱动程序,调用muxDevStart()来启动驱动程序。协议栈注册了4个回调函数,由MUX接口回调。
    
    基本过程就是:
        1、muxDevLoad()调用endLoad()
            endLoad()初始化任何特殊设备,填写END_OBJ和NET_FUNCS结构并返回。MUX把返回的END_OBJ添加到END_OBJ结构的链表中,此链表列出了当前系统所运行的所有网络设备的信息,此时,用户驱动程序装载完毕以备用。
        
        2、MUX调用endStart()启动网卡,注册中断服务程序并使能中断。
    
    综上,VxWorks的网络驱动只要在endLoad()中填写END_OBJ结构和实现endXXX()函数即可。当然,内存管理也是一个很重要的方面,VxWorks的内存池API实现了网络数据“零拷贝”,各协议层仅传递指针而不重复拷贝数据。endXXX()函数主要实现开始、结束、发、收、IOCTL、多播地址插入/删除/读取等。其中收发既可以基于中断也可以基于查询方式。
    
    -------------------------------------------------
    | EasyARM2200和SmartARM2200上网络驱动程序的实现 |
    -------------------------------------------------
    要在EasyARM2200和SmartARM2200上实现END类型的网卡驱动,需要在BSP目录中增加几个文件:
    BSP目录为c:\Tornado2.2\target\config\zlgarm
    增加rtl8019end.c、rtl8019end.h、configNet.h文件,并修改config.h、Makefile配置。
    
    -----------
    configNet.h
    -----------
    #define ZLGARM7_LOAD_FUNC_0    rtl8019EndLoad
    
    #define ZLGARM7_LOAD_STRING_0 "83400000:17"
    IMPORT END_OBJ * ZLGARM7_LOAD_FUNC_0 (char *, void *);
    
    END_TBL_ENTRY endDevTbl [] =
    {
    #ifdef INCLUDE_RTL8019_END
        { 0, ZLGARM7_LOAD_FUNC_0, ZLGARM7_LOAD_STRING_0, 1 , NULL, FALSE},
    #endif /* INCLUDE_RTL8019_END */
        { 0, END_TBL_END, NULL, 0, NULL, FALSE},
    };
    
    在configNet.h配置文件中,主要涉及到一个表格(END_TBL_ENTRY)的填写。这个表格的每一项对应一个网卡的加载信息。
    END_TBL_ENTRY结构中各项的含义分别为:
        { 设备编号,加载函数,初始化资源字串,缓冲类型,BSP内部指针,处理完成与否的标志 }
    END_TBL_ENTRY使用END_TBL_END结束表格填写。
    由此可见,上面的语句注册了一个网卡驱动rtl8019EndLoad。
    
    资源字串的含义是:网卡基地址:网卡中断号。
    这个字串是自行定义的,各项间用冒号分隔,只要能正确分解出各项内容即可,没有统一规定。例如这里定义资源字串的两项分别为网卡基地址和中断号。
    
    ---------
    config.h
    ---------
    #define DEFAULT_BOOT_LINE   "rtl(0,0)host:vxWorks " \
                                "h=192.168.0.6 " \
                                "e=192.168.0.2 " \
                                "g=192.168.0.1 "  \
                                "u=target "     \
                                "tn=targetname"
    
    #define INCLUDE_NETWORK
    #define INCLUDE_END
    
    #ifdef  INCLUDE_END
    #define INCLUDE_RTL8019_END   /* Include Ethernet driver */
    #endif /* INCLUDE_END */
    
    #undef  WDB_COMM_TYPE         /* default WDB agent communication path is END */
    #define WDB_COMM_TYPE         WDB_COMM_END
    
    这里设置目标机IP地址(e),使能END驱动,增加8019驱动程序,使用网络连通WDB agent。
    
    --------
    Makefile
    --------
    在MACH_EXTRA=后面添加rtl8019end.o,以便编译8019驱动。
    
    MACH_EXTRA      = rtl8019end.o
    
    ------------
    rtl8019end.h
    ------------
    这个文件里定义各种与8019有关的宏定义。
    
    typedef struct end_device
        {
        END_OBJ           end;             /* The class we inherit from. */
        ULONG             base;            /* base address */
        int               ivec;            /* rtl8019 interrupt vector */
        int               offset;          /* offset */
        long              flags;           /* Our local flags. */
        UCHAR             enetAddr[6];     /* ethernet address */
        char              packetBuf[2048]; /* packet buffer */
        ......
        }END_DEVICE;
    
    # define DP_IN(_b_, _o_, _d_)  HAL_READ_UINT8 ((_b_)->base + 2*(_o_), (_d_))
    # define DP_OUT(_b_, _o_, _d_) HAL_WRITE_UINT8((_b_)->base + 2*(_o_), (_d_))
    
    这里面的END_DEVICE是自定义结构,其中必须包含一个END_OBJ结构,其他内容根据实际情况自行定义,例如:这里面自行定义了8019网卡驱动程序用到的变量:包缓冲区、标志、MAC地址、基址、中断号等。各种驱动函数都会传递这个结构体的指针。
    DP_IN和DP_OUT定义了8019输入输出函数,其中偏移要乘2,因为A0接到了A1上。
    
    ------------
    rtl8019end.c
    ------------
    LOCAL NET_FUNCS rtl8019EndFuncTable =
        {
        rtl8019EndStart,
        rtl8019EndStop,
        rtl8019EndUnload,
        rtl8019EndIoctl,
        rtl8019EndSend,
        rtl8019EndMCastAdd,
        rtl8019EndMCastDel,
        rtl8019EndMCastGet,
        rtl8019EndPollSend,
        rtl8019EndPollRcv,
        endEtherAddressForm,  
        endEtherPacketDataGet,
        endEtherPacketAddrGet  
        };
    
    rtl8019EndFuncTable结构体中填写了8019网卡的所有操作函数,rtl8019end.c中主要实现这些函数和rtl8019EndLoad、rtl8019EndUnload、rtl8019EndParse、rtl8019EndMemInit、rtl8019EndInt、rtl8019EndConfig、rtl8019EndReset等。
    
    首先说下内存管理。VxWorks采用一种复杂灵活的内存管理机制来有效减少数据复制。这种机制能满足数据包头尾添加/删除,可变长内存分配,尽量少的数据复制(“零拷贝”)等要求。
    
    要实现这种机制,首先在rtl8019EndMemInit中设置内存池的参数,然后申请内存空间;再使用netPoolInit来形成具体的内存池。
    在网卡驱动程序的其他地方,如果需要进行内存分配,则都在这个内存池中申请。申请步骤为:
    1、调用系统函数netClusterGet()预定一块簇缓冲区;
    2、调用系统函数netClBlkGet()预定一个clBlk结构;
    3、调用系统函数netMblkGet()预定一个mBlk结构;
    4、调用系统函数netClBlkJoin()把簇添加到clBlk结构中;
    5、调用系统函数netMblkClJoin()把clBlk结构添加到mBlk结构中。
    最后将mBlk作为参数传递给处理函数。这种管理方法有很大好处,因为一个clBlk可与多个mBlk关联,这就使得在不同协议间传递数据变得容易,只须传递指针,而无须拷贝其中的数据。
    
    当网络设备产生中断时,VxWorks调用驱动程序先前注册的中断服务程序。中断服务程序应尽可能地短,减少中断阻塞时间。它不应包含耗时的处理工作。为了将一些数据包处理工作放置在任务级,中断服务程序必须调用netJobAdd()将相应的处理程序作为输入来生成一个任务级的数据处理任务。例如:netJobAdd ((FUNCPTR)rtl8019EndRcvInt, (int)pDrvCtrl, 0, 0, 0, 0);。
    
    关于8019的详细工作原理,可以通过购买本站(www.armecos.com)的“51+8019资料”获得。
    
    ---------------------------------------------
    | 《ecos增值包》辅助开发VxWorks网络驱动程序 |
    ---------------------------------------------
    为了调试方便,我们使用“驻留ROM”型编译模式,这样就需要把VxWorks烧写到flash里才能调试,不过,有了redboot,我们可以把RAM虚拟成ROM,让VxWorks在RAM里运行,这样就可以减少flash擦写次数,加快调试速度。此时,需要修改conig.h中的ROM_XXX_XXX宏定义,把地址定位到RAM上。
    使用:lo -b 0x81010000 -r -h 192.168.0.1 a.bin下载VxWorks程序,
    使用 go 0x81010000运行VxWorks程序。
    通过打印输出调试信息,有些不适合打印的地方可以采用内存打印技术(在VxWorks里往内存打印数据,然后通过redboot查看内存),如中断程序里的打印。
    
    ----------------------------------------------------------------------------
    8019复位
    ----------------------------------------------------------------------------
    
    LOCAL void    rtl8019EndReset
    (
    END_DEVICE* pDrvCtrl    /* device to be reset */
    )
    {
    unsigned int i;
    unsigned char tmp;

        if(pDrvCtrl->unit != 0)
            return;
    
        for(i = 0; i < 250; i++); /* 延时一段时间 */
        DP_IN(pDrvCtrl, 0x1F, tmp);
        DP_OUT(pDrvCtrl, 0x1F, tmp);
    }
    
    ----------------------------------------------------------------------------
    内存池初始化
    ----------------------------------------------------------------------------
    
    LOCAL STATUS rtl8019EndMemInit
    (
    END_DEVICE * pDrvCtrl    /* device to be initialized */
    )
    {
    DBG_PRINTF ("InitMem\n");

    /*
     * Set up an END netPool using netBufLib(1).
     */
    
    endMclConfig.mBlkNum = END_MBLK_NUM;
    endClDescTbl[0].clNum = END_CL_NUM;
    endMclConfig.clBlkNum = endClDescTbl[0].clNum;

    /* Calculate the total memory for all the M-Blks and CL-Blks. */
    endMclConfig.memSize = (endMclConfig.mBlkNum * (MSIZE + sizeof (long))) +
                          (endMclConfig.clBlkNum * (CL_BLK_SZ + sizeof(long)));

    if ((endMclConfig.memArea = (char *) memalign (sizeof(long),
                                                  endMclConfig.memSize))
        == NULL)
        return (ERROR);
    
    /* Calculate the memory size of all the clusters. */
    endClDescTbl[0].memSize = (endClDescTbl[0].clNum *
                               (endClDescTbl[0].clSize + 8))
        + sizeof(int);        /* +8 is for proper alignment */

    /* Allocate the memory for the clusters */
    endClDescTbl[0].memArea =
        (char *) cacheDmaMalloc (endClDescTbl[0].memSize);

    if (endClDescTbl[0].memArea == NULL)
        {
        DBG_PRINTF ("system memory unavailable\n");
        return (ERROR);
        }

    if ((pDrvCtrl->end.pNetPool = (NET_POOL_ID) malloc (sizeof(NET_POOL)))
        == NULL)
        return (ERROR);

    /* Initialize the memory pool. */
    if (netPoolInit(pDrvCtrl->end.pNetPool, &endMclConfig,
                    &endClDescTbl[0], endClDescTblNumEnt, NULL) == ERROR)
        {
        return (ERROR);
        }

    DBG_PRINTF ("InitMem OK!\n");
    return OK;
    }
    
    ----------------------------------------------------------------------------
    接收函数
    ----------------------------------------------------------------------------
    LOCAL STATUS rtl8019EndRecv
    (
    END_DEVICE         *pDrvCtrl  /* device structure */
    )
    {
    M_BLK_ID     pMblk = NULL;    
    CL_BLK_ID    pClBlk = NULL;    
    UINT32       len;
    char *       pBuf = NULL;
    
    if (pDrvCtrl->end.pNetPool == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: Illegal pNetPool on entry!\n");
        END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);
        goto cleanRXD;
        }

    if ((pMblk = mBlkGet (pDrvCtrl->end.pNetPool, M_DONTWAIT, MT_DATA))
        == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: Out of M Blocks!\n");
        END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);
        goto cleanRXD;
        }

    pBuf = netClusterGet (pDrvCtrl->end.pNetPool,
                                 pDrvCtrl->end.pNetPool->clTbl[0]);

    if (pBuf == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: Out of clusters!\n");
        pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
        muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
        goto cleanRXD;
        }

    /* 读取数据 */
    len = rtl8019PacketGet(pDrvCtrl, pBuf);

    if ((pClBlk = netClBlkGet (pDrvCtrl->end.pNetPool, M_DONTWAIT)) == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: Out of Cluster Blocks!\n");
        pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
        muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
        goto cleanRXD;
        }

    if (netClBlkJoin (pClBlk, pBuf, len, NULL, 0, 0, 0) == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: netClBlkJoin failed\n");
        pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
        muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
        goto cleanRXD;
        }
    
    if (netMblkClJoin (pMblk, pClBlk) == NULL)
        {
        DBG_PRINTF ("rtl8019EndRecv: netMblkClJoin failed\n");
        pDrvCtrl->lastError.errCode = END_ERR_NO_BUF;
        muxError(&pDrvCtrl->end, &pDrvCtrl->lastError);
        goto cleanRXD;
        }

    
    pMblk->mBlkHdr.mFlags |= M_PKTHDR;
    pMblk->mBlkHdr.mLen   = len;
    pMblk->mBlkPktHdr.len = len;
    pMblk->mBlkHdr.mData += pDrvCtrl->offset;

    END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_UCAST, +1);

    END_RCV_RTN_CALL(&pDrvCtrl->end, pMblk);

    return (OK);

cleanRXD:

    if (pClBlk != NULL)
        {
        netClBlkFree (pDrvCtrl->end.pNetPool, pClBlk);
        }

    if (pBuf != NULL)
        {
        netClFree (pDrvCtrl->end.pNetPool, pBuf);
        pBuf = NULL;
        }

    if (pMblk != NULL)
        {
        netMblkFree (pDrvCtrl->end.pNetPool, pMblk);
        }

    END_ERR_ADD (&pDrvCtrl->end, MIB2_IN_ERRS, +1);


    return (ERROR);
    }
    
    ----------------------------------------------------------------------------
    发送函数
    ----------------------------------------------------------------------------
    LOCAL STATUS rtl8019EndSend
    (
    END_DEVICE *pDrvCtrl,    /* device ptr */
    M_BLK_ID pNBuff          /* data to send */
    )
    {
    int       len;
    int       i;

    if (pDrvCtrl->resetting)
        {
        return ERROR;
        }

    END_TX_SEM_TAKE (&pDrvCtrl->end, WAIT_FOREVER);

    len = netMblkToBufCopy(pNBuff,
                           (void *)pDrvCtrl->packetBuf, NULL) ;
    len = max(len, ETHERSMALL);

    END_TX_SEM_GIVE (&pDrvCtrl->end);

    page(pDrvCtrl, 0);
    DP_OUT(pDrvCtrl, 0x09, 0x40);  /* send buffer start address */
    DP_OUT(pDrvCtrl, 0x08, 0x00);
    DP_OUT(pDrvCtrl, 0x0B, len >> 8);  /* send packet len */
    DP_OUT(pDrvCtrl, 0x0A, len & 0xFF);
    DP_OUT(pDrvCtrl, 0x00, 0x12);   /* write dma, page0 */

    for(i = 0; i < len; i++){  /* 发送数据到双口RAM */
        DP_OUT(pDrvCtrl, 0x10, pDrvCtrl->packetBuf);
        printf("%x ", pDrvCtrl->packetBuf);
    }
    printf("\n");
    
    DP_OUT(pDrvCtrl, 0x04, 0x40);  /* 发送缓冲区首址*/
    DP_OUT(pDrvCtrl, 0x06, len >> 8);  /* 发送包长度 */
    DP_OUT(pDrvCtrl, 0x05, len & 0xFF);
    DP_OUT(pDrvCtrl, 0x00, 0x3E);  /* 发送 */

    END_ERR_ADD (&pDrvCtrl->end, MIB2_OUT_UCAST, +1);

    netMblkClChainFree (pNBuff);

    return (OK);
    }
gdtyy
驱动牛犊
驱动牛犊
  • 注册日期2002-08-15
  • 最后登录2012-02-16
  • 粉丝0
  • 关注0
  • 积分4分
  • 威望16点
  • 贡献值0点
  • 好评度4点
  • 原创分0分
  • 专家分0分
地板#
发布于:2007-07-14 07:52
VxWorks for LPC2210网络应用编程实战
***************************************
* VxWorks for LPC2210网络应用编程实战 *
***************************************
                      ------ 《ecos增值包》选配之VxWorks网络应用编程实战
    2007/06/06  asdjf@163.com  www.armecos.com
    
    VxWorks对网络支持得相当好,稳定且高效,内存池的巧妙运用实现了“零内存拷贝”,netJobAdd函数的使用减少了中断延迟时间,用户只需要实现网络驱动就可以运行完整的TCP/IP协议栈。通过网络下载和调试程序比串口快很多,我们已经移植好了EasyARM2200和SmartARM2200上的VxWorks网络驱动BSP,下面通过一个例子demo3.c来讲一下VxWorks for LPC2210网络应用编程。
    
    这个例子是开发板上网页演示程序在VxWorks上的移植,通过实际测试(在SmartARM2200上),响应速度非常快(平均为0.09秒),ping测试(74字节包)的平均响应时间为3ms,ARP响应时间为3ms。源码如下:
    
#include "sockLib.h"
#include "inetLib.h"

#define STACK_SIZE 2000

void webserver_test(int arg);

unsigned char httpweb[]={
    "HTTP/1.0 200 OK\r\n"
    "Date: Mon, 24 Nov 2003 01:24:17 GMT\r\n"
    "Server: microHttp/1.0 Zlgmcu Corporation\r\n"
    "Accept-Ranges: bytes\r\n"
    "Connection: Keep-Close\r\n"
    "Content-Type: text/html\r\n"
    "\r\n"
    };

unsigned char web[]={
    "<HTML>\r\n"
    "<HEAD>\r\n"
    "<TITLE>ARM_NET演示网页(周立功单片机)</TITLE>\r\n"
    "<BODY aLink=green background=/100.bmp bgColor=#f1f1dd link=red\r\n"
    "vLink=#321afd>\r\n"
    "<H1>HELLO WELCOME TO EasyArm WEBSERVER</H1>\r\n"
    "<UL>\r\n"
    "<LI> <A HREF=\"http://www.zlgmcu.com/\">周立功单片机网站 </A>\r\n"
    "<LI> <A HREF=\"http://www.zlg.cn/\">周立功单片机内部BBS </A>\r\n"
    "<LI> <A HREF=\"http://www.zlgmcu.cn/\">周立功单片机 </A>\r\n"
     "</UL>\r\n"
    "</BODY>\r\n"
    "</HTML>\r\n"
    };

unsigned char httpgif[]={
    "HTTP/1.0 200 OK\r\n"
    "Date: Mon, 24 Nov 2003 01:24:17 GMT\r\n"
    "Server: microHttp/1.0 Zlgmcu Corporation\r\n"
    "Accept-Ranges: bytes\r\n"
    "Connection: Keep-Close\r\n"
    "Content-Type: image/bmp\r\n"
    "\r\n"
    };

unsigned char bmp[442]={
0x42,0x4d,0xb6,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x76,0x00,0x00,0x00,0x28,0x00,
0x00,0x00,0x1a,0x00,0x00,0x00,0x14,0x00,0x00,0x00,0x01,0x00,0x04,0x00,0x00,0x00,
0x00,0x00,0x40,0x01,0x00,0x00,0xc4,0x0e,0x00,0x00,0xc4,0x0e,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x80,
0x00,0x00,0x00,0x80,0x80,0x00,0x80,0x00,0x00,0x00,0x80,0x00,0x80,0x00,0x80,0x80,
0x00,0x00,0xc0,0xc0,0xc0,0x00,0x80,0x80,0x80,0x00,0x00,0x00,0xff,0x00,0x00,0xff,
0x00,0x00,0x00,0xff,0xff,0x00,0xff,0x00,0x00,0x00,0xff,0x00,0xff,0x00,0xff,0xff,
0x00,0x00,0xff,0xff,0xff,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xf6,0x66,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0x66,0x66,0x6f,0x6f,0xff,0x66,0x66,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xf6,0xff,0xff,0x6f,0xf6,0xff,0xf6,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x6f,0xff,0x6f,0xf6,0xff,0xf6,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xf6,0xff,0xf6,0xf6,0xff,0xff,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0x6f,0xf6,0xff,0x6f,0xff,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xf6,0x66,0x66,0xf6,0xff,0xf6,0x66,
0x6f,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x6f,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0x00,0x00,0x00
};

void demo3(void)
{
    printf("Demo3 Start......\n");
    
    taskSpawn("webserver_test", 90, 0, STACK_SIZE, webserver_test, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0);
}

void
webserver_test(int arg)
{
    struct sockaddr_in host,client;
    int s,sa,e_source,len, sockAddrSize;
    unsigned char buf[400];

    s = socket(AF_INET, SOCK_STREAM, 0);
    if (s < 0) {
        return;
    }

    /* Set up host address */
    sockAddrSize = sizeof(struct sockaddr_in);
    host.sin_family = AF_INET;
    host.sin_len = sizeof(host);
    host.sin_addr.s_addr = INADDR_ANY;
    host.sin_port = ntohs(80);
    
    if(bind(s, (struct sockaddr *) &host, sizeof(host)) < 0) {
        return;
    }
    
    listen(s, SOMAXCONN);
    
    while(1){
        memset(buf, 0, sizeof(buf));

        if ((sa = accept(s, (struct sockaddr *)&client, &sockAddrSize)) < 0) {
            printf("Accept ERROR!\n");
            continue;
        }
        
        printf("SERVER : HTTP request arrived from %s:%d\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        
        len = read(sa, buf, sizeof(buf));
        
        if(buf[5] == ' '){
            len = write(sa, httpweb, sizeof(httpweb)-1);
            len = write(sa, web, sizeof(web));
        }
        else if(buf[5] == '1'){
            len = write(sa, httpgif, sizeof(httpgif)-1);
            len = write(sa, bmp,sizeof(bmp));
        }

        close(sa);
    }
}

    下面图示说明使用方法:
    和前面《VxWorks for LPC2210应用编程实战》里介绍的使用方法类似,唯一不同点是Target Server要设置成wdbrpc。
    下面只介绍不同部分的设置。
    
    设置Target Server,使用网络RPC通信,目标机IP地址为192.168.0.2,超时和重试次数根据需要设置,调试时如果网络速度比较慢的话(因为加入了调试信息),使用缺省值可能导致连接失败,正常工作时网络速度很快,不存在此问题。注意:Core File and Symbols里的File文件要与烧写在flash里的VxWorks一致,即带调试信息的ELF文件(vxWorks_romResident)。连接成功会显示成功信息,托盘里出现一个象靶子一样的图标。
    
    
    
    
    其他操作与《VxWorks for LPC2210应用编程实战》里介绍的使用方法相同,不再赘述。
    
    超级终端上显示的运行信息。
    
    
    
    用IE访问192.168.0.2就可以浏览内置网页,响应速度相当快(0.09秒)。
    
    
    
    基于网络RPC的调试shell比基于串口的快多了,输入“i”后,几乎是一下子蹦出任务状态列表信息。
    
    
    
    这是ping测试的结果,通过sniffer抓包分析,ping测试(74字节包)的平均响应时间为3ms,ARP响应时间为3ms。看来VxWorks真的不赖。
    
    
游客

返回顶部