搜索
bottom↓
回复: 9

移植uCOSII体会(一)

[复制链接]

出0入0汤圆

发表于 2009-3-27 00:03:36 | 显示全部楼层 |阅读模式
目的:基本的任务能在目标板上跑起来
经过一段时间的摸索基本完成的了既定的任务,下面有些我的体会和疑惑和大家一起分享
       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, [PC, #-0x0120]     ; Vector from VicVectAddr
                        LDR     PC, FIQ_Addr
        其中特别有意思的是 LDR     PC, [PC, #-0x0120],因为它取代了我们比较熟悉的  LDR     PC, IRQ_Addr
        其原因就是LCP23XX系统的arm7有个内部VIC,其中断发生后都由它来处理,做第二次跳转,而这个内部的VIC的寄存器
        地址是0xFFFF FF00。IQR响应地址为0x0000 0020.(0x0000 0020 - 0x0000 0120 = 0xFFFF FF00) 所以,LDR     PC, [PC, #-0x0120],
        就正好跳到了要到的地方。真是个不错的方法,当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程序,代码为
     void  OS_CPU_ExceptHndlr (CPU_DATA except_id)
   {
    CPU_FNCT_VOID  pfnct;
    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()这个用来产生节拍信号的函数没有了。别一个时钟中断处理程序取代了。下面是初始化时钟中断和中断处理函数的代码
static  void  Tmr_TickInit (void)
{
    CPU_INT32U  pclk_freq;
    CPU_INT32U  rld_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                                           */
}

void  Tmr_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)

阿莫论坛20周年了!感谢大家的支持与爱护!!

知道什么是神吗?其实神本来也是人,只不过神做了人做不到的事情 所以才成了神。 (头文字D, 杜汶泽)

出0入0汤圆

发表于 2009-3-27 07:24:01 | 显示全部楼层
1. copy代码到运行时域;
2. 初始化RW,ZI和NOINIT区域;
能过startup.s但是又在main.c之前挂掉的话,多半和你的堆和栈设置有关系。

出0入0汤圆

 楼主| 发表于 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_HEAP  0x40007000 EMPTY  0x00000100   {}
    ARM_LIB_STACK 0x40008000 EMPTY -0x00000E00   {}
}
有几个地方还不是特别理解:如
1.RW,ZI段在写C程序的时候并没有进行特别说明,是不是编译器自己确定的
2.如果我想用51C中那样写个在代码段的数组 unsigned char code aa[256]
  如何在C中实现呢,在arm的C语言中好像不支持 code 这个关键字了

出0入0汤圆

发表于 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一类的声明变量只读性,具体放在什么位置是编译器决定的。

出0入0汤圆

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

出0入0汤圆

发表于 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都开了的情况下。

出0入0汤圆

发表于 2012-1-17 18:21:44 | 显示全部楼层
我也遇到这种类似的问题啊,求解。我是at91sam9261

出0入0汤圆

发表于 2012-4-1 17:10:40 | 显示全部楼层
楼主求联系啊~~加我qq: 972659239    email:hel_tju@163.com   求指导~~

出0入0汤圆

发表于 2012-10-17 19:07:39 | 显示全部楼层
多谢 指教

出0入0汤圆

发表于 2013-4-9 15:25:37 | 显示全部楼层
樓主強悍
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片。注意:要连续压缩2次才能满足要求!!】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-7-23 09:25

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表