移植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) 1. copy代码到运行时域;
2. 初始化RW,ZI和NOINIT区域;
能过startup.s但是又在main.c之前挂掉的话,多半和你的堆和栈设置有关系。 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 这个关键字了 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一类的声明变量只读性,具体放在什么位置是编译器决定的。 谢谢 【3楼】 dr2001 的指点,收益非浅!^_^
1.是不是可以理解为RO,RW,ZI对应的都是变量,而不对应的代码。如果我要把代码放在速度比较快的外
部RAM中运行是不是必须自己进行代码的搬运,然后自己设定好堆栈等运行环境,软化改变PC指针,这样
就能够实现代码在外部RAM上的运行。
2.我竟然忘记了const这个对常量的申明,还真是C51用多了,惯性思维害人啊 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都开了的情况下。 我也遇到这种类似的问题啊,求解。我是at91sam9261 楼主求联系啊~~加我qq: 972659239 email:hel_tju@163.com 求指导~~ 多谢 指教 樓主強悍
页:
[1]