- 浏览: 1028285 次
文章分类
最新评论
-
yj963552657:
楼主,请问一下你用的是什么IDE?
浅谈Windows Socket程序设计(TCP和UDP) -
haohao-xuexi02:
我最喜欢那一段,家庭,孩子。。那一切都是那么顺利,值得。。
典型80后的5年工作总结 -
yunzhu:
转载的怎么可以把原文中声明原创的内容删除了,这有点不太厚道啊 ...
打破常规——大胆尝试在路由器上搭建SVN服务器 -
zhuchao_ko:
与君共勉。
典型80后的5年工作总结 -
香煎马鲛鱼:
我还在上大学,看了你的文章突然觉得生活好残忍……
典型80后的5年工作总结
转载一篇写得不错的UCOS-II移植文章
今天突然有个想法,是否在其他结构比较简单的平台上移植比较容易一点,正好同学有一个凌阳的精简板,反正今天是星期天,就当是休息了。
首先肯定是去熟悉SPCE061A的结构和IDE了。主要是存储器结构、指令系统和中断这几个部分。本来不是做这个的,没有必要深究,总体看看,知道在哪些地方查就行,所以看到很快。于是摆好uCOS系统的资料,按照移植步骤,一个个文件、函数地写好,其他没有什么,就是时间节拍比较难一点,用了不少时间写,主要是去熟悉凌阳的中断系统,了解几个寄存器的用法。按照标准移植函数步骤写下来,代码也就10来行。
在这里我想说的不是如何移植,而是编译。凌阳的IDE说实话肯定是不太完善的。因为我同学本科的时候做过,那个时候似乎听他提到过这个问题。不过我今天算是感受到了。
写好文件,编译——我的错,有一个函数写错了,编译没有通过。然后我改了。编译,???怎么回事,还是这个错误?
大体是这样的,我写了一个OSTaskSw函数(原本想写OSCtxSw的),结果,这个IDE居然还真的认出来一个OSTaskSw,我当时就晕了,我好像在内核里没有看到过这个函数嘛。我赶紧去内核查找一下,没有嘛。我把OSTaskSw函数改成OSCtxSw(OS_CPU.H里),再编译,还是有。更晕了~
这个错误是这样报的:
Error L0080: The external symbol "_memset" has not a public definition.
Error L0080: The external symbol "_OSTaskSw" has not a public definition.
memset嘛好说,这应该是我没有包含某个库文件,我只是知道这是个字符串处理函数,应该在string.h里面,但是包含了它,还是有这个错误(现在还没有解决,惭愧~),但是OSTaskSw都没有了还给我报什么?
后来我想,这个IDE是GCC的,是不是因为增量编译,链接的时候用了以前的文件?干脆把所有以前编译生成的文件删除了(用clear没有用),再编译,嘿嘿,还真的没有这个错误了。I服了HIM。确实没有用过,问题都不好找。
今天比较晚了,明天来解决另一个问题吧!我怀疑最大的可能是在工程文件的组织上有问题,应该好好梳理一下。因为我发现,编译应用程序文件是没有错的,Build的时候才出现。
心头憋得慌,一大早就跑到实验室来调试。弄了半天,把include文件夹中的memset搜索了一遍,就只有一个string.h里面有嘛。哪里还有其他的哩?难道我包含的地方不对?“SPCE061A.h”包含在includes.h中,能够使用,按说所有.c文件都包含includes.h,放在这里是没有问题的嘛。不过上天要它说不行,我也没辙啊~
没办法,上网查查吧。
一说是版本问题!我意识到,好像以前看过一个版本,在OSTask.c中好像是没有用到memset函数。那下载一个老版本的来实验一下,总不能让我去改内核吧,改出来更多问题,得不偿失。我下的是2.00的。
经过一番挣扎一般的调试(很久都没有怎么用过凌阳的IDE,很多功能不会用了,又不知道其Bug,出了问题就只好老老实实重新编译等等,确实很累),总算调通了。
经过记录下来——为了系统化,把uCOS标准移植伪码加上对比。
移植准备:
ucOS-II的移植主要涉及以下三个文件:
OS_CPU.H
OS_CPU_A.ASM
OS_CPU_C.C
移植的工作包括以下几个内容:
1.在OS_CPU.H文件中
声明10个数据类型
BOOLEAN
INT8U
INT8S
INT16U
INT16S
INT32U
INT32S
FP32
FP64
OS_STK
声明三个宏
OS_ENTER_CRITICAL()
OS_EXIT_CRITICAL()
OS_TASK_SW()
设置一个常量的值
OS_STK_GROWTH
2.编写四个汇编语言函数(OS_CPU_A.ASM)
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
3.用C语言编写六个简单的函数(OS_CPU_C.C)
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
………………
4.一般认为上面几个方面就构成了整个移植要做的工作,其实我认为还应该包含对Includes.h和OS_CFG.H的配置,因为这些决定了操作系统的功能和编译的环境。
移植工作:
1、OS_CPU.H
根据凌阳的数据结构特点,定义如下:
typedef unsigned charBOOLEAN;
typedef unsigned charINT8U;
typedef signedcharINT8S;
typedef unsigned intINT16U;
typedef signedintINT16S;
typedef unsigned longINT32U;
typedef signedlongINT32S;
typedef floatFP32;
typedef doubleFP64;
typedef unsigned intOS_STK;
#defineOS_CRITICAL_METHOD2
#ifOS_CRITICAL_METHOD == 2/*一般都使用第二种方法*/
#defineOS_ENTER_CRITICAL()__asm__("INT OFF /n/t")/*关中断*/
#defineOS_EXIT_CRITICAL()__asm__("INT IRQ /n/t" "INT FIQ /n/t")/*开中断*/
#endif
#defineOS_STK_GROWTH1/*堆栈增长方式,凌阳是从高向低增长*/
#defineOS_TASK_SW()OSCtxSw()/*任务切换函数*/
*这里需要说明的是,很多编译器由于支持软中断,所以,这里通常使用的是软中断进入管理模式,然后进行任务切换(例如在ARM7中)。但是凌阳没有软中断,只好通过函数调用的方式进行,而不是让某个中断向量指向OSCtxSw()。
到这里,OS_CPU.H的工作大体就是这么多了。
2、OS_CPU_C.C
在这个文件中,最重要的是OSTaskStkInit()函数的编写,其他Hook函数用来扩展内核功能而不去修改内核结构。
先来看看一般堆栈需要初始化成什么样子:
l保存参数(这个视情况而定,因为有些处理器的参数是通过寄存器传递的;如ARM7)
l保存任务地址,用于保存当前任务(由于凌阳的段寄存器没用,为0,保存task+1)
l处理器状态字(凌阳应该是SR吧,反正我保存的是SR)
l中断返回地址保存(这个好像不太重要,我看ARM7里面就没有保存)
l寄存器组
按照这个步骤,编写如下:
这里注意,版本不同,声明的类型也有不同,在2.52中是OS_STK。
void*OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{
OS_STK *stk;
opt= opt;
stk= (OS_STK *)ptos;
*stk-- = *((INT16U*)pdata);
//*stk-- = *((INT16U*)task);//为0,不需要保存
*stk-- = *((INT16U*)task + 1);
*stk-- = (INT16U)0x0000;//SR不能乱放东西的,因为这个是程序执行用到的
*stk-- = (INT16U)0x5555;//这些就可以随便放了为了调试方便,可以放一些有意义的数值
*stk-- = (INT16U)0x4444;
*stk-- = (INT16U)0x3333;
*stk-- = (INT16U)0x2222;
*stk-- = (INT16U)0x1111;
return ((void*)stk);
}
*说明:在标准伪码里面提到一个模拟ISR的压栈,暂时没有明白是什么意思。(就是说每次任务的调度都是一次中断)
3、OS_CPU_A.ASM
首先声明需要的外部变量和要输出的变量:
.external _OSTCBCur
.external _OSTCBHighRdy
.external _OSRunning
.external _OSPrioCur
.external _OSPrioHighRdy
.external _OSIntNesting
.external _OSTaskSwHook
.external _OSIntEnter
.external _OSIntExit
.external _OSTimeTick
.public _OSStartHighRdy
.public _OSIntCtxSw
.public _OSCtxSw
.public _OSTickISR
大概就这些吧
1)函数_OSStartHighRdy
标准函数伪码:
void OSStartHighRdy(void)
{
调用用户定义的OSTaskSwHook();
OSRunning = TRUE;
得到将要恢复运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务堆栈中恢复处理器的所有寄存器;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSStartHighRdy:
CALL_OSTaskSwHook
R1 = 1
[_OSRunning] = R1;
R1 = [_OSTCBHighRdy]
SP = [R1]//注意是所指向的内容,调这个错误用了不少时间
POPALL//此函数只做了任务切换工作的一半,并没有保存当前任务的寄存器
RETI
2)函数_OSCtxSw
标准函数伪码:
void OSCtxSw(void)
{
保存处理器寄存器;
在当前任务的任务控制块中保存当前任务的堆栈指针:
OSTCBCur——>OSTCBStkPtr = Stack Pointer;
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
得到将要运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务的任务堆栈中恢复处理器所有寄存器的值;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSCtxSw:
PUSHALL
R1 = [_OSTCBCur]
[R1] = SP
CALL _OSTaskSwHook
R1 = [_OSTCBHighRdy]
[_OSTCBCur] = R1
R2 = [_OSPrioHighRdy]
[_OSPrioCur] = R2
SP = [R1]//所指向内容
POPALL
RETI
3)函数_OSIntCtxSw
标准函数伪码:
void OSIntCtxSw(void)
{
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
得到将要运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务的任务堆栈中恢复处理器所有寄存器的值;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSIntCtxSw:
R1 = [_OSTCBCur]//在这之前要不要调整指针,还说不好,因为不调整暂时也可以运行
[R1] = SP//注意要保存当前任务的堆栈,否则不能返回——
//当然最好是放在OSTickISR中,这样可以避免在后面调整堆栈指针——
CALL _OSTaskSwHook
R1 = [_OSTCBHighRdy]
[_OSTCBCur] = R1
R2 = [_OSPrioHighRdy]
[_OSPrioCur] = R2
SP = [R1]//所指向内容
POPALL
RETI
看起来,这个函数同OSCtxSw没有太多的不同,区别在于如果ISR保存了CPU寄存器,这里不用再保存了。
4)函数_OSTickISR
标准函数伪码:
void OSTickISR(void)
{
保存处理器寄存器;
调用OSIntEnter()或者直接给OSIntNesting加1;
if(OSIntNesting == 1)
{
OSTCBCur——>OSTCBStkPtr = Stack Pointer;//在2.51之前是没有的
}
给产生中断的设备清中断;
重新使能允许中断(可选);
OSTimeTick();
OSIntExit();
恢复处理器寄存器;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
需要注意到是,要在OS_CFG.H中设置节拍数
.public _IRQ6
_IRQ6:
_OSTickISR:
PUSHALL
R1=C_IRQ6_TMB2//判断是否为IRQ_TMB2中断
TEST R1,[P_INT_Ctrl]
JNZ IRQ_TMB2//是,进入IRQ_TMB2;否,进入IRQ_TMB1
IRQ_TMB1:
R1=C_IRQ6_TMB1//清中断标志
[P_INT_Clear]=R1
R1=0x0001
[P_Watchdog_Clear]=R1//清看门狗
R1=[_OSIntNesting]//中断嵌套标志加1
R1+=1//也可call _OSIntEnter
[_OSIntNesting]=R1//
//最好在这里加入保存步骤,省去调整SP;
//当然如果这里加了,前面就不要再保存了
call _OSTimeTick
call _OSIntExit
POPALL
reti
IRQ_TMB2://中断子程序IRQ_TMB2
R1=C_IRQ6_TMB2//清中断标志
[P_INT_Clear]=R1
POPALL
reti
*说明:由于凌阳提供两个时基中断,需要判断中断控制器的值。具体参照手册。
到这里,主要的移植工作就完成了。
后记:
整个过程受制于对凌阳的不熟悉,所以中间编译的过程比较头疼,这也反映了移植工作是建立在对目标系统熟悉的基础之上。
有几个问题需要注意:
移植版本:
移植版本不同,内核所需要的库函数也不同;这个移植过程遇到了一个string.h的问题,加上都没有用,我郁闷。
OSIntCtxSw():
这是我后来在网上看到的,好几个实例中都调整了SP的值,目的是舍弃多余的数据。而在我的移植中,没有进行这个调整,也能够运行。可能区别在于对内存的合理使用上。
翻看uCOS_II第二版的304-307页,我们可以找到答案:
在以前版本中,没有在OSTickISR()中进行中断时的堆栈保存:
if(OSIntNesting == 1)
{
OSTCBCur——>OSTCBStkPtr = Stack Pointer;//在2.51之前是没有的
}
因此,在调用OSIntCtxSw()的时候,需要先做这个工作:调整堆栈指针,标准代码中给出的是:
OSIntExit();
OSIntCtxSw();
不是太明白,这个调用不成了自己调用递归了么?实验过,不行的。
然后保存当前任务的堆栈指针到当前任务控制块中。
OSTCBCur——>OSTCBStkPtr =Stack Pointer
为了避免调整指针,最好在OSTickISR中保存堆栈先——
在凌阳中,具体操作上将SP的值加7,就是抛弃这7个数据的值。为什么要说7,据说同编译器有关,这里也没有时间去详细追究,网上也没有相关的说明,只有自己调试的时候打开调试窗口观察吧。
我调试了一会儿。在任务切换处设置一个断点观测。当执行到POPALL时,SP跳到00d0处(这个地址值为空),将之前压栈的值弹出到寄存器中。从下面可以看到,弹栈前,SP指向的是00d0——栈顶;弹栈后,堆栈中的值(包括PC)弹出到寄存器中,SP指向的是00d7,所以这个值显然是7了。这也比较符合凌阳的寄存器结构。
其实最好的方法是在OSTickISR中加入中断嵌套堆栈保存的步骤,这样就不需要去根据编译器调整堆栈指针了,移植性更强。参考uCOS_II P305、P307。
就在函数中添加这样的代码:
CMP R1,1//R1中保存了OSIntNesting的值
JNZ NOT_SAVE_SP
SAVE_SP:
R1 = [_OSTCBCur]
[R1] = SP
NOT_SAVE_SP:
今天突然有个想法,是否在其他结构比较简单的平台上移植比较容易一点,正好同学有一个凌阳的精简板,反正今天是星期天,就当是休息了。
首先肯定是去熟悉SPCE061A的结构和IDE了。主要是存储器结构、指令系统和中断这几个部分。本来不是做这个的,没有必要深究,总体看看,知道在哪些地方查就行,所以看到很快。于是摆好uCOS系统的资料,按照移植步骤,一个个文件、函数地写好,其他没有什么,就是时间节拍比较难一点,用了不少时间写,主要是去熟悉凌阳的中断系统,了解几个寄存器的用法。按照标准移植函数步骤写下来,代码也就10来行。
在这里我想说的不是如何移植,而是编译。凌阳的IDE说实话肯定是不太完善的。因为我同学本科的时候做过,那个时候似乎听他提到过这个问题。不过我今天算是感受到了。
写好文件,编译——我的错,有一个函数写错了,编译没有通过。然后我改了。编译,???怎么回事,还是这个错误?
大体是这样的,我写了一个OSTaskSw函数(原本想写OSCtxSw的),结果,这个IDE居然还真的认出来一个OSTaskSw,我当时就晕了,我好像在内核里没有看到过这个函数嘛。我赶紧去内核查找一下,没有嘛。我把OSTaskSw函数改成OSCtxSw(OS_CPU.H里),再编译,还是有。更晕了~
这个错误是这样报的:
Error L0080: The external symbol "_memset" has not a public definition.
Error L0080: The external symbol "_OSTaskSw" has not a public definition.
memset嘛好说,这应该是我没有包含某个库文件,我只是知道这是个字符串处理函数,应该在string.h里面,但是包含了它,还是有这个错误(现在还没有解决,惭愧~),但是OSTaskSw都没有了还给我报什么?
后来我想,这个IDE是GCC的,是不是因为增量编译,链接的时候用了以前的文件?干脆把所有以前编译生成的文件删除了(用clear没有用),再编译,嘿嘿,还真的没有这个错误了。I服了HIM。确实没有用过,问题都不好找。
今天比较晚了,明天来解决另一个问题吧!我怀疑最大的可能是在工程文件的组织上有问题,应该好好梳理一下。因为我发现,编译应用程序文件是没有错的,Build的时候才出现。
心头憋得慌,一大早就跑到实验室来调试。弄了半天,把include文件夹中的memset搜索了一遍,就只有一个string.h里面有嘛。哪里还有其他的哩?难道我包含的地方不对?“SPCE061A.h”包含在includes.h中,能够使用,按说所有.c文件都包含includes.h,放在这里是没有问题的嘛。不过上天要它说不行,我也没辙啊~
没办法,上网查查吧。
一说是版本问题!我意识到,好像以前看过一个版本,在OSTask.c中好像是没有用到memset函数。那下载一个老版本的来实验一下,总不能让我去改内核吧,改出来更多问题,得不偿失。我下的是2.00的。
经过一番挣扎一般的调试(很久都没有怎么用过凌阳的IDE,很多功能不会用了,又不知道其Bug,出了问题就只好老老实实重新编译等等,确实很累),总算调通了。
经过记录下来——为了系统化,把uCOS标准移植伪码加上对比。
移植准备:
ucOS-II的移植主要涉及以下三个文件:
OS_CPU.H
OS_CPU_A.ASM
OS_CPU_C.C
移植的工作包括以下几个内容:
1.在OS_CPU.H文件中
声明10个数据类型
BOOLEAN
INT8U
INT8S
INT16U
INT16S
INT32U
INT32S
FP32
FP64
OS_STK
声明三个宏
OS_ENTER_CRITICAL()
OS_EXIT_CRITICAL()
OS_TASK_SW()
设置一个常量的值
OS_STK_GROWTH
2.编写四个汇编语言函数(OS_CPU_A.ASM)
OSStartHighRdy()
OSCtxSw()
OSIntCtxSw()
OSTickISR()
3.用C语言编写六个简单的函数(OS_CPU_C.C)
OSTaskStkInit()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskSwHook()
OSTaskStatHook()
OSTimeTickHook()
………………
4.一般认为上面几个方面就构成了整个移植要做的工作,其实我认为还应该包含对Includes.h和OS_CFG.H的配置,因为这些决定了操作系统的功能和编译的环境。
移植工作:
1、OS_CPU.H
根据凌阳的数据结构特点,定义如下:
typedef unsigned charBOOLEAN;
typedef unsigned charINT8U;
typedef signedcharINT8S;
typedef unsigned intINT16U;
typedef signedintINT16S;
typedef unsigned longINT32U;
typedef signedlongINT32S;
typedef floatFP32;
typedef doubleFP64;
typedef unsigned intOS_STK;
#defineOS_CRITICAL_METHOD2
#ifOS_CRITICAL_METHOD == 2/*一般都使用第二种方法*/
#defineOS_ENTER_CRITICAL()__asm__("INT OFF /n/t")/*关中断*/
#defineOS_EXIT_CRITICAL()__asm__("INT IRQ /n/t" "INT FIQ /n/t")/*开中断*/
#endif
#defineOS_STK_GROWTH1/*堆栈增长方式,凌阳是从高向低增长*/
#defineOS_TASK_SW()OSCtxSw()/*任务切换函数*/
*这里需要说明的是,很多编译器由于支持软中断,所以,这里通常使用的是软中断进入管理模式,然后进行任务切换(例如在ARM7中)。但是凌阳没有软中断,只好通过函数调用的方式进行,而不是让某个中断向量指向OSCtxSw()。
到这里,OS_CPU.H的工作大体就是这么多了。
2、OS_CPU_C.C
在这个文件中,最重要的是OSTaskStkInit()函数的编写,其他Hook函数用来扩展内核功能而不去修改内核结构。
先来看看一般堆栈需要初始化成什么样子:
l保存参数(这个视情况而定,因为有些处理器的参数是通过寄存器传递的;如ARM7)
l保存任务地址,用于保存当前任务(由于凌阳的段寄存器没用,为0,保存task+1)
l处理器状态字(凌阳应该是SR吧,反正我保存的是SR)
l中断返回地址保存(这个好像不太重要,我看ARM7里面就没有保存)
l寄存器组
按照这个步骤,编写如下:
这里注意,版本不同,声明的类型也有不同,在2.52中是OS_STK。
void*OSTaskStkInit (void (*task)(void *pd), void *pdata, void *ptos, INT16U opt)
{
OS_STK *stk;
opt= opt;
stk= (OS_STK *)ptos;
*stk-- = *((INT16U*)pdata);
//*stk-- = *((INT16U*)task);//为0,不需要保存
*stk-- = *((INT16U*)task + 1);
*stk-- = (INT16U)0x0000;//SR不能乱放东西的,因为这个是程序执行用到的
*stk-- = (INT16U)0x5555;//这些就可以随便放了为了调试方便,可以放一些有意义的数值
*stk-- = (INT16U)0x4444;
*stk-- = (INT16U)0x3333;
*stk-- = (INT16U)0x2222;
*stk-- = (INT16U)0x1111;
return ((void*)stk);
}
*说明:在标准伪码里面提到一个模拟ISR的压栈,暂时没有明白是什么意思。(就是说每次任务的调度都是一次中断)
3、OS_CPU_A.ASM
首先声明需要的外部变量和要输出的变量:
.external _OSTCBCur
.external _OSTCBHighRdy
.external _OSRunning
.external _OSPrioCur
.external _OSPrioHighRdy
.external _OSIntNesting
.external _OSTaskSwHook
.external _OSIntEnter
.external _OSIntExit
.external _OSTimeTick
.public _OSStartHighRdy
.public _OSIntCtxSw
.public _OSCtxSw
.public _OSTickISR
大概就这些吧
1)函数_OSStartHighRdy
标准函数伪码:
void OSStartHighRdy(void)
{
调用用户定义的OSTaskSwHook();
OSRunning = TRUE;
得到将要恢复运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务堆栈中恢复处理器的所有寄存器;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSStartHighRdy:
CALL_OSTaskSwHook
R1 = 1
[_OSRunning] = R1;
R1 = [_OSTCBHighRdy]
SP = [R1]//注意是所指向的内容,调这个错误用了不少时间
POPALL//此函数只做了任务切换工作的一半,并没有保存当前任务的寄存器
RETI
2)函数_OSCtxSw
标准函数伪码:
void OSCtxSw(void)
{
保存处理器寄存器;
在当前任务的任务控制块中保存当前任务的堆栈指针:
OSTCBCur——>OSTCBStkPtr = Stack Pointer;
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
得到将要运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务的任务堆栈中恢复处理器所有寄存器的值;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSCtxSw:
PUSHALL
R1 = [_OSTCBCur]
[R1] = SP
CALL _OSTaskSwHook
R1 = [_OSTCBHighRdy]
[_OSTCBCur] = R1
R2 = [_OSPrioHighRdy]
[_OSPrioCur] = R2
SP = [R1]//所指向内容
POPALL
RETI
3)函数_OSIntCtxSw
标准函数伪码:
void OSIntCtxSw(void)
{
OSTaskSwHook();
OSTCBCur = OSTCBHighRdy;
OSPrioCur = OSPrioHighRdy;
得到将要运行的任务的堆栈指针:
Stack Pointer = OSTCBHighRdy——>OSTCBStkPtr;
从新任务的任务堆栈中恢复处理器所有寄存器的值;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
_OSIntCtxSw:
R1 = [_OSTCBCur]//在这之前要不要调整指针,还说不好,因为不调整暂时也可以运行
[R1] = SP//注意要保存当前任务的堆栈,否则不能返回——
//当然最好是放在OSTickISR中,这样可以避免在后面调整堆栈指针——
CALL _OSTaskSwHook
R1 = [_OSTCBHighRdy]
[_OSTCBCur] = R1
R2 = [_OSPrioHighRdy]
[_OSPrioCur] = R2
SP = [R1]//所指向内容
POPALL
RETI
看起来,这个函数同OSCtxSw没有太多的不同,区别在于如果ISR保存了CPU寄存器,这里不用再保存了。
4)函数_OSTickISR
标准函数伪码:
void OSTickISR(void)
{
保存处理器寄存器;
调用OSIntEnter()或者直接给OSIntNesting加1;
if(OSIntNesting == 1)
{
OSTCBCur——>OSTCBStkPtr = Stack Pointer;//在2.51之前是没有的
}
给产生中断的设备清中断;
重新使能允许中断(可选);
OSTimeTick();
OSIntExit();
恢复处理器寄存器;
执行中断返回指令;
}
按这个标准函数,编写函数如下:
需要注意到是,要在OS_CFG.H中设置节拍数
.public _IRQ6
_IRQ6:
_OSTickISR:
PUSHALL
R1=C_IRQ6_TMB2//判断是否为IRQ_TMB2中断
TEST R1,[P_INT_Ctrl]
JNZ IRQ_TMB2//是,进入IRQ_TMB2;否,进入IRQ_TMB1
IRQ_TMB1:
R1=C_IRQ6_TMB1//清中断标志
[P_INT_Clear]=R1
R1=0x0001
[P_Watchdog_Clear]=R1//清看门狗
R1=[_OSIntNesting]//中断嵌套标志加1
R1+=1//也可call _OSIntEnter
[_OSIntNesting]=R1//
//最好在这里加入保存步骤,省去调整SP;
//当然如果这里加了,前面就不要再保存了
call _OSTimeTick
call _OSIntExit
POPALL
reti
IRQ_TMB2://中断子程序IRQ_TMB2
R1=C_IRQ6_TMB2//清中断标志
[P_INT_Clear]=R1
POPALL
reti
*说明:由于凌阳提供两个时基中断,需要判断中断控制器的值。具体参照手册。
到这里,主要的移植工作就完成了。
后记:
整个过程受制于对凌阳的不熟悉,所以中间编译的过程比较头疼,这也反映了移植工作是建立在对目标系统熟悉的基础之上。
有几个问题需要注意:
移植版本:
移植版本不同,内核所需要的库函数也不同;这个移植过程遇到了一个string.h的问题,加上都没有用,我郁闷。
OSIntCtxSw():
这是我后来在网上看到的,好几个实例中都调整了SP的值,目的是舍弃多余的数据。而在我的移植中,没有进行这个调整,也能够运行。可能区别在于对内存的合理使用上。
翻看uCOS_II第二版的304-307页,我们可以找到答案:
在以前版本中,没有在OSTickISR()中进行中断时的堆栈保存:
if(OSIntNesting == 1)
{
OSTCBCur——>OSTCBStkPtr = Stack Pointer;//在2.51之前是没有的
}
因此,在调用OSIntCtxSw()的时候,需要先做这个工作:调整堆栈指针,标准代码中给出的是:
OSIntExit();
OSIntCtxSw();
不是太明白,这个调用不成了自己调用递归了么?实验过,不行的。
然后保存当前任务的堆栈指针到当前任务控制块中。
OSTCBCur——>OSTCBStkPtr =Stack Pointer
为了避免调整指针,最好在OSTickISR中保存堆栈先——
在凌阳中,具体操作上将SP的值加7,就是抛弃这7个数据的值。为什么要说7,据说同编译器有关,这里也没有时间去详细追究,网上也没有相关的说明,只有自己调试的时候打开调试窗口观察吧。
我调试了一会儿。在任务切换处设置一个断点观测。当执行到POPALL时,SP跳到00d0处(这个地址值为空),将之前压栈的值弹出到寄存器中。从下面可以看到,弹栈前,SP指向的是00d0——栈顶;弹栈后,堆栈中的值(包括PC)弹出到寄存器中,SP指向的是00d7,所以这个值显然是7了。这也比较符合凌阳的寄存器结构。
其实最好的方法是在OSTickISR中加入中断嵌套堆栈保存的步骤,这样就不需要去根据编译器调整堆栈指针了,移植性更强。参考uCOS_II P305、P307。
就在函数中添加这样的代码:
CMP R1,1//R1中保存了OSIntNesting的值
JNZ NOT_SAVE_SP
SAVE_SP:
R1 = [_OSTCBCur]
[R1] = SP
NOT_SAVE_SP:
相关推荐
一篇写得不错的UCOS-II移植文章,非常不错的资料。
FM3 uCOS-II移植,所有Cortex-M3系列ARM都可以作参考移植。
uCOS-II uCOS-II中文手册 uCOS-II移植 嵌入式系统
详细介绍uCOS-II移植到STM32硬件平台上的移植过程
使用VS2015建立的uCOS-II项目,相关博文:http://blog.csdn.net/hxiaohai/article/details/50396417
uCOS-II原理 任哲源码 海量源码供你选择
官方移植uCOS-II,成功运行stm32f10xzet6,如有错误,请留言给我。
本篇文档是介绍MCU-stm32f207vct6在IAR工程建立和UCOS-II移植过程及代码烧录方式。包括软件的安装,工程的配置,ucos源码包的使用,代码的多种烧录方式等
uCOS-ii移植的流程,按照教程,轻松移植ucosii
uCOS-II内核移植到STC单片机
1.了解uCOS-II内核的主要结构。 2.掌握将uCOS-II内核移植到ARM920T处理器上的基本方法。 3. 将uCOS-II内核移植到ARM920T微处理器上。 4.编写两个简单任务,在超级终端上观察两个任务的切换。
ARM7TDMI-uCOS-II移植(修正版),修正了其中串口显示乱码的问题。
ucos-ii移植到stm32f103vc,主任务灯闪烁,还有一个按键检测任务和对应灯亮的任务。
UCOS-II的入门教程,可以看看,不错的资料
文件为移植好的stm32 ucos-ii的keil4工程文件,移植日期2012年7月20日,各种移植源码均为最新版
STM32平台移植uCOS-II详细说明 1. 建立工程所需的文件夹 建立文件夹uCOS-II-Port :工程根目录 建立文件夹uCOS-II-Port/App :存放用户应用程序相关 建立文件夹uCOS-II-Port/Bsp :存放开发板初始化驱动...
用于ucosii系统移植。同时也可以帮助需要从新配置ucos系统的朋友。
这是ARM7TDMI-uCOS-II移植成功的例子
由网上资源整理的uCOS-II移植到PC代码,相关博文:http://blog.csdn.net/hxiaohai/article/details/50396417
Micriμm uCos-II在Cortex-M3移植的详细说明