uC/OS II移植到ARM,其中,OSTickISR()函数的汇编代码

 我来答
百度网友a88246e
2011-02-25 · TA获得超过4295个赞
知道小有建树答主
回答量:2221
采纳率:0%
帮助的人:1364万
展开全部
控制器上运行。为了方便移植,大部分的µC/OS-Ⅱ代码是用C语言写的;但仍需要用C和汇编语言写一些与处理器相关的代码,这是因为µC/OS-Ⅱ在读写处理器寄存器时只能通过汇编语言来实现。由于µC/OS-Ⅱ在设计时就已经充分考虑了可移植性,所以µC/OS-Ⅱ的移植相对来说是比较容易的。[5,6]
要使µC/OS-Ⅱ正常运行,处理器必须满足以下要求:
(1) 处理器的C编译器能产生可重入代码。
(2) 用C语言就可以打开和关闭中断。
(3) 处理器支持中断,并且能产生定时中断(通常在10至100Hz之间)。
(4) 处理器支持能够容纳一定量数据(可能是几千字节)的硬件堆栈。
(5) 处理器有将堆栈指针和其它CPU寄存器读出和存储的指令
图2-1说明了µC/OS-Ⅱ的结构以及它与硬件的关系。从图中可以看到整个系统的架构。最底层是硬件层,该层主要涉及到CPU处理器的架设,以及它与外部各功能模块的连接,对于CPU处理器的初始化也是构架嵌入式系统的重要内容,特别是对定时器的设置,将是构建操作系统的基础,它决定整个系统的性能。对于软件部分,最底层是与处理器相关的程序代码,该段代码直接对CPU处理器进行初始化,这部分代码就是移植操作系统的主要内容,也是最难以理解的部分。这段代码绝大部分程序是用汇编语言编写的,因为在程序运行的时候,这部分代码的调用次数最频繁。在向上的代码就与处理器没有任何的关系,其中一部分包括操作系统的配置文件,像OS_CORE.c,OS_FLAG.c等文件。这部分代码是用来编写一些基本的底层函数,这些函数将作为以后应用部分的基本函数库进行调用,这部分函数构成了操作系统的基本构架,不同的操作系统所对应的系统的设计思想不同,主要体现在这些函数的设计中。除了系统的基本函数外,还有应用部分的基本配置文件。该文件声明的是与具体的应用配置有关的一些设置文件。比如,各任务的一些基本参数,所使用的信号量的声明,以及液晶的参数配置等。不同的应用程序对应的该文件参数配置也不同。有了底层的基本配置文件,就可以编写具体的应用程序了,最上层就是应用程序,针对不同的应用需求,编写不同的应用程序。
μCOS-II不使用C语言中的short、int、long等数据类型的定义,因为它们与处理器类型有关,隐含着不可移植性。代之以移植性强的整数数据类型,这样,既直观又可移植,不过这就成了必须移植的代码。根据ADS编译器的特性,这些代码如程序清单图2-2所示。
与所有的实时内核一样,µC/OS-Ⅱ需要先禁止中断再访问代码的临界段,并且在访问完毕后重新允许中断。这就使得µC/OS-Ⅱ能够保护临界段代码免受多任务或中断服务例程(ISRs)的破坏。为了隐藏编译器厂商提供的具体实现方法,µC/OS-Ⅱ定义了两个宏来禁止和允许中断:OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。
μCOS-II使用结构常量OS_STK_GROWTH中指定堆栈的生长方式:置OS_STK_GROWTH为0表示堆栈从下往上长。置OS_STK_GROWTH为1表示堆栈从上往下长。虽然ARM处理器核对于两种方式均支持,但ADS的C语言编译器仅支持一种方式,即从上往下长,并且必须是满递减堆栈,所以OS_STK_GROWTH的值为1。
µC/OS-Ⅱ的移植实例要求用户编写四个简单的汇编语言函数: OSStartHighRdy();OSCtxSw();OSIntCtxSw();OSTickISR()。如果用户的编译器支持插入汇编语言代码的话,用户就可以将所有与处理器相关的代码放到OS_CPU_C.C文件中,而不必再拥有一些分散的汇编语言文件。
使就绪状态的任务开始运行的函数叫做OSStart(),如下示意函数所示。在用户调用OSStart()之前,用户必须至少已经建立了一个任务。OSStartHighRdy()假设OSTCBHighRdy指向的是优先级最高的任务的任务控制块。为了简单一点,堆栈指针总是储存在任务控制块(即它的OS_TCB)的开头。换句话说,也就是要想恢复的任务堆栈指针总是储存在OS_TCB的0偏址内存单元中。
如果当前任务调用µC/OS-Ⅱ提供的系统服务,并使得更高优先级任务处于就绪状态,µC/OS-Ⅱ就会借助上面提到的向量地址找到OSCtxSw()。在系统服务调用的最后,µC/OS-Ⅱ会调用OSSched(),并由此来推断当前任务不再是要运行的最重要的任务了。软中断 (或陷阱) 指令会强制一些处理器寄存器(比如返回地址和处理器状态字)到当前任务的堆栈中,并使处理器执行OSCtxSw()。这些代码必须写在汇编语言中,因为用户不能直接从C中访问CPU寄存器。注意在OSCtxSw()和用户定义的函数OSTaskSwHook()的执行过程中,中断是禁止的。

OSIntExit()通过调用OSIntCtxSw()来从ISR中执行切换功能。因为OSIntCtxSw()是在ISR中被调用的,所以可以断定所有的处理器寄存器都被正确地保存到了被中断的任务的堆栈之中。实际上除了需要的东西外,堆栈结构中还有其它的一些东西。OSIntCtxSw()必须要清理堆栈,这样被中断的任务的堆栈结构内容才能满足人们的需要。
要想了解OSIntCtxSw(),大家可以看看µC/OS-Ⅱ调用该函数的过程。假定中断不能嵌套(即ISR不会被中断),中断是允许的,并且处理器正在执行任务级的代码。当中断来临的时候,处理器会结束当前的指令,识别中断并且初始化中断处理过程,包括将处理器的状态寄存器和返回被中断的任务的地址保存到堆栈中。至于究竟哪些寄存器保存到了堆栈上,以及保存的顺序是怎样的,并不重要。
接着,CPU会调用正确的ISR。µC/OS-Ⅱ要求ISR在开始时要保存剩下的处理器寄存器。一旦寄存器保存好了,µC/OS-Ⅱ就要求或者调用OSIntEnter(),或者将变量OSIntNesting加1。在这个时候,被中断任务的堆栈中只包含了被中断任务的寄存器内容。现在,ISR可以执行中断服务了。并且如果ISR发消息给任务(通过调用OSMboxPost()或OSQPost()),恢复任务(通过调用OSTaskResume()),或者调用OSTimeTick()或OSTimeDlyResume()的话,有可能使更高优先级的任务处于就绪状态。
假设有一个更高优先级的任务处于就绪状态。µC/OS-Ⅱ要求用户的ISR在完成中断服务的时候调用OSIntExit()。OSIntExit()会告诉µC/OS-Ⅱ到了返回任务级代码的时间了。调用OSIntExit()会导致调用者的返回地址被保存到被中断的任务的堆栈中。
OSIntExit()刚开始时会禁止中断,因为它需要执行临界段的代码。根据OS_ENTER_CRITICAL()的不同执行过程,处理器的状态寄存器会被保存到被中断的任务的堆栈中。OSIntExit()注意到由于有更高优先级的任务处于就绪状态,被中断的任务已经不再是要继续执行的任务了。在这种情况下,指针OSTCBHighRdy会被指向新任务的OS_TCB,并且OSIntExit()会调用OSIntCtxSw()来执行任务切换。调用OSIntCtxSw()也同样使返回地址被保存到被中断的任务的堆栈中。
用户切换任务的时候,用户只想将某些项保留在堆栈中,并忽略其它项。这是通过调整堆栈指针(加一个数在堆栈指针上)来完成的。加在堆栈指针上的数必须是明确的,而这个数主要依赖于移植的目标处理器(地址空间可能是16,32或64位),所用的编译器,编译器选项,内存模式等等。另外,处理器状态字可能是8,16,32甚至64位宽,并且OSIntExit()可能会分配局部变量。有些处理器允许用户直接增加常量到堆栈指针中,而有些则不允许。在后一种情况下,可以通过简单的执行一定数量的pop(出栈)指令来实现相同的功能。一旦堆栈指针完成调整,新的堆栈指针会被保存到被切换出去的任务的OS_TCB中。
这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。如果用户的编译器支持插入汇编语言代码的话,用户就可以将OSIntCtxSw()代码放到OS_CPU_C.C文件中,而不放到OS_CPU_A.ASM文件中。正如用户所看到的那样,除了第一行以外,OSIntCtxSw()的代码与OSCtxSw()是一样的。这样在移植实例中,用户可以通过“跳转”到OSCtxSw()中来减少OSIntCtxSw()代码量。
µC/OS-Ⅱ要求用户提供一个时钟资源来实现时间的延时和期满功能。时钟节拍应该每秒钟发生10-100次。为了完成该任务,可以使用硬件时钟,也可以从交流电中获得50/60Hz的时钟频率。
这些代码必须写在汇编语言中,因为用户不能直接从C语言中访问CPU寄存器。如果用户的处理器可以通过单条指令来增加OSIntNesting,那么用户就没必要调用OSIntEnter()了。增加OSIntNesting要比通过函数调用和返回快得多。OSIntEnter()只增加OSIntNesting,并且作为临界段代码中受到保护。
µC/OS-Ⅱ的移植实例要求用户编写六个简单的C函数:OSTaskStkInit(); OSTaskCreateHook();OSTaskDelHook();OSTaskSwHook();OSTaskStatHook(); OSTimeTickHook()。唯一必要的函数是OSTaskStkInit(),其它五个函数必须得声明但没必要包含代码。
OSTaskCreate()和OSTaskCreateExt()通过调用OSTaskStkInt()来初始化任务的堆栈结构,因此,堆栈看起来就像刚发生过中断并将所有的寄存器保存到堆栈中的情形一样。显示了OSTaskStkInt()放到正被建立的任务堆栈中的东西。注意,在这里我假定了堆栈是从上往下长的。下面的讨论同样适用于从下往上长的堆栈。
在用户建立任务的时候,用户会传递任务的地址,pdata指针,任务的堆栈栈顶和任务的优先级给OSTaskCreate()和OSTaskCreateExt()。虽然OSTaskCreateExt()还要求有其它的参数,但这些参数在讨论OSTaskStkInt()的时候是无关紧要的。为了正确初始化堆栈结构,OSTaskStkInt()只要求刚才提到的前三个参数和一个附加的选项,这个选项只能在OSTaskCreateExt()中得到。
该函数主要是对相关的几个寄存器进行初始化工作,初始化的寄存器对应于
最美是家
2012-07-24 · 超过23用户采纳过TA的回答
知道答主
回答量:52
采纳率:0%
帮助的人:44.2万
展开全部
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
INT32U task_addr;

opt = opt; /* 'opt' is not used, prevent warning */
stk = ptos; /* Load stack pointer */
task_addr = (INT32U)task & ~1; /* Mask off lower bit in case task is thumb mode */
*(stk) = (INT32U)task_addr; /* Entry Point */
*(--stk) = (INT32U)0x14141414L; /* R14 (LR) */
*(--stk) = (INT32U)0x12121212L; /* R12 */
*(--stk) = (INT32U)0x11111111L; /* R11 */
*(--stk) = (INT32U)0x10101010L; /* R10 */
*(--stk) = (INT32U)0x09090909L; /* R9 */
*(--stk) = (INT32U)0x08080808L; /* R8 */
*(--stk) = (INT32U)0x07070707L; /* R7 */
*(--stk) = (INT32U)0x06060606L; /* R6 */
*(--stk) = (INT32U)0x05050505L; /* R5 */
*(--stk) = (INT32U)0x04040404L; /* R4 */
*(--stk) = (INT32U)0x03030303L; /* R3 */
*(--stk) = (INT32U)0x02020202L; /* R2 */
*(--stk) = (INT32U)0x01010101L; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
if ((INT32U)task & 0x01) { /* See if task runs in Thumb or ARM mode */
*(--stk) = (INT32U)ARM_SVC_MODE_THUMB; /* CPSR (Enable both IRQ and FIQ interrupts, THUMB-mode) */
} else {
*(--stk) = (INT32U)ARM_SVC_MODE_ARM; /* CPSR (Enable both IRQ and FIQ interrupts, ARM-mode) */
}
return (stk);
}

我也刚开始学,这是从一个移植成功的例子中摘下来的 ,堆栈的增长方向是向上增长的!
本回答被网友采纳
已赞过 已踩过<
你对这个回答的评价是?
评论 收起
推荐律师服务: 若未解决您的问题,请您详细描述您的问题,通过百度律临进行免费专业咨询

为你推荐:

下载百度知道APP,抢鲜体验
使用百度知道APP,立即抢鲜体验。你的手机镜头里或许有别人想知道的答案。
扫描二维码下载
×

类别

我们会通过消息、邮箱等方式尽快将举报结果通知您。

说明

0/200

提交
取消

辅 助

模 式