chenguozi 发表于 2009-3-27 00:03:36

移植uCOSII体会(一)

目的:基本的任务能在目标板上跑起来
经过一段时间的摸索基本完成的了既定的任务,下面有些我的体会和疑惑和大家一起分享
       1.从uCOS官方网站上下了LPC2378的代码基本拿来就能用,只是在外设上与少许的不同(比如led灯)
         但从学习的角度出发没有这样做,而是自己独立建立工程,一步步的移植。由于uCOS的可移植性
         很好,所以工作量不大,而且邵贝贝那本书对移植也说的很清楚。
       2.首先,从启动的*.s文件入手。基本上很简单,大家要注意的是中断表的处理。
         Keil for arm 的默认启动文件是这样的
      Vectors         LDR   PC, Reset_Addr         
                        LDR   PC, Undef_Addr
                        LDR   PC, SWI_Addr
                        LDR   PC, PAbt_Addr
                        LDR   PC, DAbt_Addr
                        NOP                            ; Reserved Vector
                        ;LDR   PC, IRQ_Addr这个别注释掉了
                        LDR   PC,    ; Vector from VicVectAddr
                        LDR   PC, FIQ_Addr
      其中特别有意思的是 LDR   PC, ,因为它取代了我们比较熟悉的LDR   PC, IRQ_Addr
      其原因就是LCP23XX系统的arm7有个内部VIC,其中断发生后都由它来处理,做第二次跳转,而这个内部的VIC的寄存器
      地址是0xFFFF FF00。IQR响应地址为0x0000 0020.(0x0000 0020 - 0x0000 0120 = 0xFFFF FF00) 所以,LDR   PC, ,
      就正好跳到了要到的地方。真是个不错的方法,当uCOSII的标准做法就不是这样的了,他仍然是采用了LDR   PC, IRQ_Addr
      代码如下:
      Vectors
                ldr   pc, Reset_Addr
                ldr   pc, Undef_Addr
                ldr   pc, SWI_Addr
                ldr   pc, Prefetch_Addr
                ldr   pc, Abort_Addr
                nop
                ldr          pc, IRQ_Addr
              ldr          pc, FIQ_Addr
               Reset_Addr      dcd   ResetHndlr
                Undef_Addr      dcd   OS_CPU_ARM_ExceptUndefInstrHndlr
                SWI_Addr      dcd   OS_CPU_ARM_ExceptSwiHndlr
                Prefetch_Addr   dcd   OS_CPU_ARM_ExceptPrefetchAbortHndlr
                Abort_Addr      dcd   OS_CPU_ARM_ExceptDataAbortHndlr
                nop
                IRQ_Addr      dcd   OS_CPU_ARM_ExceptIrqHndlr
                FIQ_Addr      dcd   OS_CPU_ARM_ExceptFiqHndlr
         大家会发现他是跳到了 OS_CPU_ARM_ExceptIrqHndlr 这个地方去了,具体位置写到另一个汇编文件中,代码为
      OS_CPU_ARM_ExceptIrqHndlr
      SUB   LR, LR, #4                                          ; LR offset to return from this exception: -4.
      STMFD   SP!, {R0-R12, LR}                                 ; Push working registers.
      MOV   R3, LR                                              ; Save link register.
      MOV   R0, #OS_CPU_ARM_EXCEPT_IRQ                        ; Set exception ID to OS_CPU_ARM_EXCEPT_IRQ.
      B            OS_CPU_ARM_ExceptHndlr
      这是一个就有了一个参数“OS_CPU_ARM_EXCEPT_IRQ”,通过R0,传给了一个C程序,代码为
   voidOS_CPU_ExceptHndlr (CPU_DATA except_id)
   {
    CPU_FNCT_VOIDpfnct;
    CPU_INT32U    *sp;                                                                     
   if ((except_id == OS_CPU_ARM_EXCEPT_IRQ) || (except_id == OS_CPU_ARM_EXCEPT_FIQ)) {
      pfnct = (CPU_FNCT_VOID)VICAddress;                              /* Read the interrupt vector from the VIC         */
      if (pfnct != (CPU_FNCT_VOID)0) {                              /* Make sure we don't have a NULL pointer         */
          (*pfnct)();                                                   /* Execute the ISR for the interrupting device      */
            VICAddress = 1;                                             /* Acknowlege the VIC interrupt                     */
      }
    } else {
      sp = (CPU_INT32U *)OSTCBCur->OSTCBStkPtr;
      APP_TRACE_INFO(("\nCPU_ARM_EXCEPTION #%d trapped.\n", except_id));
      APP_TRACE_INFO(("R0: 0x%08x\n", *(sp + 0x01)));
      APP_TRACE_INFO(("R1: 0x%08x\n", *(sp + 0x02)));
      APP_TRACE_INFO(("R2: 0x%08x\n", *(sp + 0x03)));
      APP_TRACE_INFO(("R3: 0x%08x\n", *(sp + 0x04)));
      APP_TRACE_INFO(("R4: 0x%08x\n", *(sp + 0x05)));
      APP_TRACE_INFO(("R5: 0x%08x\n", *(sp + 0x06)));
      APP_TRACE_INFO(("R6: 0x%08x\n", *(sp + 0x07)));
      APP_TRACE_INFO(("R7: 0x%08x\n", *(sp + 0x08)));
      APP_TRACE_INFO(("R8: 0x%08x\n", *(sp + 0x09)));
      APP_TRACE_INFO(("R9: 0x%08x\n", *(sp + 0x0A)));
      APP_TRACE_INFO(("R10 : 0x%08x\n", *(sp + 0x0B)));
      APP_TRACE_INFO(("R11 : 0x%08x\n", *(sp + 0x0C)));
      APP_TRACE_INFO(("R12 : 0x%08x\n", *(sp + 0x0D)));
      APP_TRACE_INFO(("SP: 0x%08x\n",   sp));
      APP_TRACE_INFO(("LR: 0x%08x\n", *(sp + 0x0E)));
      APP_TRACE_INFO(("PC: 0x%08x\n", *(sp + 0x0F)));
      APP_TRACE_INFO(("CPSR: 0x%08x\n", *(sp + 0x00)));
      while (DEF_TRUE) {
            ;
      }
    }
}
   这就是uCOS对于中断的处理。
   
    3.书上说的OSTickISQ()这个用来产生节拍信号的函数没有了。别一个时钟中断处理程序取代了。下面是初始化时钟中断和中断处理函数的代码
staticvoidTmr_TickInit (void)
{
    CPU_INT32Upclk_freq;
    CPU_INT32Urld_cnts;
                                                            /* VIC timer #0 Initialization                              */
    VICIntSelect &= ~(1 << VIC_TIMER0);                         /* Configure the timer interrupt as an IRQ source         */
    VICVectAddr4=(CPU_INT32U)Tmr_TickISR_Handler;         /* Set the vector address                                 */
    VICIntEnable=(1 << VIC_TIMER0);                         /* Enable the timer interrupt source                        */

    pclk_freq   =   BSP_CPU_PclkFreq(PCLK_TIMER0);            /* Get the peripheral clock frequency                     */

    rld_cnts      =   pclk_freq / OS_TICKS_PER_SEC;             /* Calculate the # of counts necessary for the OS ticker    */

    T0TCR         =(1 << 1);                                  /* Disable and reset counter 0 and the prescale counter 0   */
    T0TCR         =   0;                                        /* Clear the reset bit                                    */
    T0PC          =   0;                                        /* Prescaler is set to no division                        */

    T0MR0         =   rld_cnts;
    T0MCR         =   3;                                        /* Interrupt on MR0 (reset TC), stop TC                     */

    T0CCR         =   0;                                        /* Capture is disabled.                                     */
    T0EMR         =   0;                                        /* No external match output.                              */
    T0TCR         =   1;                                        /* Enable timer 0                                           */
}

voidTmr_TickISR_Handler (void)
{
    T0IR = 0xFF;                                                /* Clear timer #0 interrupt                                 */

    OSTimeTick();                                             /* Call uC/OS-II's OSTimeTick()                           */
}
         
          4.在移植的过程中遇到了一个我百思不得其解的问题。就是我移植完成以后,进入不到main(),单步仿真后发现
            在进入main()前keil for arm 的编译器增加了很多代码,个人理解为:为C程序的运行建立必要的环境。可我
            移植的程序一到这里就跑飞了,不知道做什么去了。在这里郁闷了很长时间,后来经过仔细的调试,发现在我的
            程序中出现了一天软中断语句 SWI ,这就让程序自动跳到了的中断处理程序中,大家通过看上面的中断处理程序
            就能发现如果不是IRQ或者FIQ中断,就会进入到APP_TRACE_INFO(("R0: 0x%08x\n", *(sp + 0x01)));等这些,保护
            报错部分。而到了这里程序就跑飞了。直接把响应的所以APP_TRACE_INFO。。。。都删除后。问题就解决了,而且
            再也没出现SWI这样的软中断,我彻底无语了。那位高手能解释一下进入main()前keil for arm 的编译器增加了很多代码
            到底做了些什么(我的汇编水平实在有限,想看都看不下去,呵呵),还有我出现的这个怪问题到底是什么原因产生的

          5.那位高手能解释一下进入main()前keil for arm 的编译器增加了很多代码 到底做了些什么(我的汇编水平实在有限,
            想看都看不下去,呵呵),还有我出现的这个怪问题到底是什么原因产生的,谢谢!

          6.最后,做了两个独立的任务,去闪灯

这是源代码ourdev_428707.rar(文件大小:642K) (原文件名:myucos.rar)

maxwell_lee 发表于 2009-3-27 07:24:01

1. copy代码到运行时域;
2. 初始化RW,ZI和NOINIT区域;
能过startup.s但是又在main.c之前挂掉的话,多半和你的堆和栈设置有关系。

chenguozi 发表于 2009-3-27 13:18:42

RW,ZI和NOINIT区域,的设计用了专门的scat文件做的。如下:
LR_IROM1 0x00000000 0x00080000                                                                         ;; Load region
{
    ER_IROM1 0x00000000        0x00080000
    {
      vectors.o (VECT, +First)
      init.o (INIT)
      * (+RO)
    }

    RW_IRAM1 0x40000000 0x00007000
    {
      * (+RW,+ZI)
    }                                                         ;; The following declarations select the "two region model" ;                                                                ;; A default __user_initial_stackheap() will be used      ;
    ARM_LIB_HEAP0x40007000 EMPTY0x00000100   {}
    ARM_LIB_STACK 0x40008000 EMPTY -0x00000E00   {}
}
有几个地方还不是特别理解:如
1.RW,ZI段在写C程序的时候并没有进行特别说明,是不是编译器自己确定的
2.如果我想用51C中那样写个在代码段的数组 unsigned char code aa
如何在C中实现呢,在arm的C语言中好像不支持 code 这个关键字了

dr2001 发表于 2009-3-27 14:51:07

RO,RW,ZI之类的都是不同的变量类型占据的。
通常代码,Const类型的数据固定在RO,RW的初值在RO;RW是具有全局生存期,带有初值的变量,初值在RO,初始化的时候拷贝到RW;ZI是全局生存期,没有给定初值的,C运行库在Scatter没有给NOINIT的情况下,会直接区域初始化0。当然Scattar不能太复杂,必须符合ARM定义的布局规则才行。

如果在__main到main之间挂了,绝大多数是因为C RunTimeLibrary初始化错误,典型是堆容量不够,有一个__init_stackheap类似的函数要手工实现,给CTRL正确的堆空间。初始栈指针也要搞定才行。

如果出现SWI,可能是你引用了SemiHosting的东西,尤其是printf类函数在没有RetargetIO的情况下,但是印象中KeilMDK是没有SemiHosting的,需要具体分析才行。

ARMv5开始内核是哈佛的,对外总线地址空间就是统一的,不像51分了各种地址空间。因此,编译器肯定不会有存储器类型修饰符的支持。const一类的声明变量只读性,具体放在什么位置是编译器决定的。

chenguozi 发表于 2009-3-27 17:48:31

谢谢 【3楼】 dr2001 的指点,收益非浅!^_^
   1.是不是可以理解为RO,RW,ZI对应的都是变量,而不对应的代码。如果我要把代码放在速度比较快的外
       部RAM中运行是不是必须自己进行代码的搬运,然后自己设定好堆栈等运行环境,软化改变PC指针,这样
       就能够实现代码在外部RAM上的运行。
   2.我竟然忘记了const这个对常量的申明,还真是C51用多了,惯性思维害人啊

dr2001 发表于 2009-3-27 20:23:29

Ans 1
RO,RW,ZI是表征内存的读写特性,Read Only; Read & Write; Zero Initialzed。所以什么东西在什么地方保存,应该就很清楚了。而且为什么这样也是很明白的。
你说的第二个事情就是BootLoader或者二级BootLoader干的,Boot,部分初始化,搬运代码,跳转到指定地点运行。这个东西是如果有SDRAM的处理器,从NAND Flash或者Serial Flash Boot,经常使用的方案。不过这个就有编译地址和运行地址的问题,具体问题具体分析 -_-b

Ans 2
ARM下变量RO属性主要靠const了;具体定位在内存中的什么地方,那就依赖于各种编译选项和Scatter以及代码本身的移动性。尤其是DTCM/ITCM,Cache,MMU都开了的情况下。

raojialong111 发表于 2012-1-17 18:21:44

我也遇到这种类似的问题啊,求解。我是at91sam9261

二过 发表于 2012-4-1 17:10:40

楼主求联系啊~~加我qq: 972659239    email:hel_tju@163.com   求指导~~

dengshaozhong 发表于 2012-10-17 19:07:39

多谢 指教

1501697860 发表于 2013-4-9 15:25:37

樓主強悍
页: [1]
查看完整版本: 移植uCOSII体会(一)