移植详解1和2中首要讲了移植需求用到的基础知识,本文则对详细的移植进程进行介绍。
首要从micrium网站上下载官方移植版别(编译器运用ARM/Keil的,V2.86版别,V2.85有问题)。
下载地址:http://micrium.com/page/downloads/ports/st/stm32
解压缩后得到如下文件夹和文件:
Micrium\
AppNotes
Licensing
Software
ReadMe.pdf
AppNotes包括ucosii移植阐明文件。这两个文件中咱们仅需关怀Micrium\AppNotes\AN1xxx-RTOS\AN1018-uCOS-II-Cortex-M3\AN-1018.pdf。由于这个文件对ucosii在CM3内核移植进程中需求修正的代码进行了阐明。
Licensing包括ucosii运用许可证。
Software下有好几个文件夹,在本文的移植中仅需关怀uCOS-II即可。
CPU: stm32规范外设库
EvalBoards: micrium官方评价板相关代码
uc-CPU: 依据micrium官方评价板的ucosii移植代码
uC-LCD:micrium官方评价板LCD驱动代码
uc-LIB: micrium官方的一个库代码
uCOS-II: ucosii源代码
uC-Probe: 和uC-Probe相关代码
ReadMe.pdf就不说了。
好了,官方的东西介绍完了,该咱们自己树立工程着手移植了。关于树立工程,并运用stm32规范外设库在我之前的文章《stm32规范外设库运用详解》已有介绍,这儿请咱们下载其间模板代码(http://download.csdn.net/source/3448543),本文的移植是依据这个工程的。
树立文件夹template\src\ucosii, template\src\ucosii\src, template\src\ucosii\port;
把Micrium\Software\uCOS-II\Source下的文件复制至template\src\ucosii\src;
把Micrium\Software\uCOS-II\Ports\ARM-Cortex-M3\Generic\RealView下的文件复制至
template\src\ucosii\port;
ucosii\src下的代码是ucosii中无需修正部分,ucosii\port下的代码是移植时需求修正的。为避免对源码的误改动形成移植失利,能够把ucosii\src下的代码文件设为只读。
这儿依据AN-1018.pdf和移植详解1、2中介绍的移植基础知识,对ucosii\port下的代码解说一下。
os_cpu.h
#ifdef OS_CPU_GLOBALS
#define OS_CPU_EXT
#else
#define OS_CPU_EXT extern
#endif
typedef unsigned char BOOLEAN;
typedef unsigned char INT8U;
typedef signed char INT8S;
typedef unsigned short INT16U;
typedef signed short INT16S;
typedef unsigned int INT32U;
typedef signed int INT32S;
typedef float FP32;
typedef double FP64;
就不解说了。
typedef unsigned int OS_STK;
typedef unsigned int OS_CPU_SR;
由于CM3是32位宽的,所以OS_STK(仓库的数据类型)被类型重界说为unsigned int。
由于CM3的状况存放器(xPSR)是32位宽的,因而OS_CPU_SR被类型重界说为unsigned int。OS_CPU_SR是在OS_CRITICAL_METHOD办法3中保存cpu状况存放器用的。在CM3中,移植OS_ENTER_CRITICAL(),OS_EXIT_CRITICAL()选办法3是最合适的。
#define OS_CRITICAL_METHOD 3
#if OS_CRITICAL_METHOD == 3
#define OS_ENTER_CRITICAL() {cpu_sr = OS_CPU_SR_Save();}
#define OS_EXIT_CRITICAL() {OS_CPU_SR_Restore(cpu_sr);}
#endif
详细界说宏OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),其间OS_CPU_SR_Save()和OS_CPU_SR_Restore()是用汇编代码写的,代码在os_cpu_a.asm中,届时再解说。
#define OS_STK_GROWTH 1
CM3中,栈是由高地址向低地址增加的,因而OS_STK_GROWTH界说为1。
#define OS_TASK_SW() OSCtxSw()
界说使命切换宏,OSCtxSw()是用汇编代码写的,代码在os_cpu_a.asm中,届时再解说。
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR OS_CPU_SR_Save(void);
void OS_CPU_SR_Restore(OS_CPU_SR cpu_sr);
#endif
void OSCtxSw(void);
void OSIntCtxSw(void);
void OSStartHighRdy(void);
void OS_CPU_PendSVHandler(void);
void OS_CPU_SysTickHandler(void);
void OS_CPU_SysTickInit(void);
INT32U OS_CPU_SysTickClkFreq(void);
声明几个函数,这儿要注意最终三个函数需求注释掉,为什么呢?
OS_CPU_SysTickHandler()界说在os_cpu_c.c中,是SysTick中止的中止处理函数,而stm32f10x_it.c,中现已有该中止函数的界说SysTick_Handler(),这儿也就不需求了,是不是很古怪官方移植版为什么会这样弄吧,后边我会解说的。
OS_CPU_SysTickInit()界说在os_cpu_c.c中,用于初始化SysTick定时器,它依赖于OS_CPU_SysTickClkFreq(),而此函数咱们自己会完成,所以注释掉。
OS_CPU_SysTickClkFreq()界说在BSP.C (Micrium\Software\EvalBoards)中,而本文移植中并未用到BSP.C,后边咱们会自己完成,因而能够把它注释掉。
os_cpu_c.c
ucosii移植时需求咱们写10个适当简略的C函数。
OSInitHookBegin()
OSInitHookEnd()
OSTaskCreateHook()
OSTaskDelHook()
OSTaskIdleHook()
OSTaskStatHook()
OSTaskStkInit()
OSTaskSwHook()
OSTCBInitHook()
OSTimeTickHook()
这些函数除了OSTaskStkInit(),都是一些hook函数。这些hook函数假如不使能的话,都不会用上,也都比较简略,看看就应该理解了,所以就不介绍。
下面就说一说OSTaskStkInit()。说之前仍是得先说一下使命切换,由于初始化使命仓库,是为使命切换服务的。代码在正常运行时,一行一行往下履行,怎样才干跑到另一个使命(即函数)履行呢?首要咱们能够回想一下中止进程,当中止发生时,本来函数履行的当地(程序计数器PC、处理器状况存放器及全部通用存放器,即当时代码的现场)被保存到栈里边去了,然后开端取中止向量,跑到中止函数里边履行。履行完了呢,想回到本来函数履行的当地,该怎样办呢,只要把栈中保存的本来函数履行的信息康复即可(把栈中保存的代码现场从头赋给cpu的各个存放器),全部就都回去了,如同什么事都没发生相同。这个进程咱们应该都比较了解,使命切换和这有什么关系,试想一下,假如有3个函数foo1(), foo2(),foo3()像是刚被中止,现场保存到栈里边去了,而中止回来时做点四肢(调度程序的效果),想回哪个回哪个,是不是就做了函数(使命)切换了。看到这儿应该有点理解OSTaskStkInit()的效果了吧,它被使命创立函数调用,所以要在开端时,在栈中作出该使命如同刚被中止相同的假象。(关于使命切换的原理邵教师书中的3.06节有介绍)。
那么中止后栈中是个什么景象呢,<>中9.1.1有介绍,xPSR,PC,LR,R12,R3-R0被主动保存到栈中的,R11-R4假如需求保存,只能手艺保存。因而OSTaskStkInit()的作业便是在使命自己的栈中保存cpu的全部存放器。这些值里R1-R12都没什么含义,这儿用相应的数字代号(如R1用0x01010101)首要是便利调试。
其他几个:
xPSR = 0x01000000L,xPSR T位(第24位)置1,不然第一次履行使命时Fault,
PC必定得指向使命进口,
R14 = 0xFFFFFFFEL,最低4位为E,是一个不合法值,首要意图是不让运用R14,即使命是不能回来的。
R0用于传递使命函数的参数,因而等于p_arg。
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
OS_STK *stk;
(void)opt; /* opt is not used, prevent warning */
stk = ptos; /* Load stack pointer */
/* Registers stacked as if auto-saved on exception */
*(stk) = (INT32U)0x01000000L; /* xPSR */
*(–stk) = (INT32U)task; /* Entry Point */
/* R14 (LR) (init value will cause fault if ever used)*/
*(–stk) = (INT32U)0xFFFFFFFEL;
*(–stk) = (INT32U)0x12121212L; /* R12 */
*(–stk) = (INT32U)0x03030303L; /* R3 */
*(–stk) = (INT32U)0x02020202L; /* R2 */
*(–stk) = (INT32U)0x01010101L; /* R1 */
*(–stk) = (INT32U)p_arg; /* R0 : argument */
/* Remaining registers saved on process stack */
*(–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 */
return (stk);
}
把OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()注释掉。
#define OS_CPU_CM3_NVIC_ST_CTRL (*((volatile INT32U *)0xE000E010))
#define OS_CPU_CM3_NVIC_ST_RELOAD (*((volatile INT32U *)0xE000E014))
#define OS_CPU_CM3_NVIC_ST_CURRENT (*((volatile INT32U *)0xE000E018))
#define OS_CPU_CM3_NVIC_ST_CAL (*((volatile INT32U *)0xE000E01C))
#define OS_CPU_CM3_NVIC_ST_CTRL_COUNT 0x00010000
#define OS_CPU_CM3_NVIC_ST_CTRL_CLK_SRC 0x00000004
#define OS_CPU_CM3_NVIC_ST_CTRL_INTEN 0x00000002
#define OS_CPU_CM3_NVIC_ST_CTRL_ENABLE 0x00000001
把上面这些宏界说也注释掉,由于它们都用于OS_CPU_SysTickHandler(), OS_CPU_SysTickInit()。
os_cpu_a.asm
这个文件包括着有必要用汇编写的代码。
EXTERN OSRunning ; External references
EXTERN OSPrioCur
EXTERN OSPrioHighRdy
EXTERN OSTCBCur
EXTERN OSTCBHighRdy
EXTERN OSIntNesting
EXTERN OSIntExit
EXTERN OSTaskSwHook
声明这些变量是在其他文件界说的,本文件只做引证(有几个如同并未引证,不过没有关系)。
EXPORT OS_CPU_SR_Save ; Functions declared in this file
EXPORT OS_CPU_SR_Restore
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT OS_CPU_PendSVHandler
声明这些函数是在本文件中界说的。
NVIC_INT_CTRL EQU 0xE000ED04 ;中止操控及状况存放器ICSR的地址
NVIC_SYSPRI14 EQU 0xE000ED22 ;PendSV优先级存放器的地址
NVIC_PENDSV_PRI EQU 0xFF ;PendSV中止的优先级为255(最低)
NVIC_PENDSVSET EQU 0x10000000 ;位28为1
界说几个常量,相似C语言中的#define预处理指令。
OS_CPU_SR_Save
MRS R0, PRIMASK ;读取PRIMASK到R0中,R0为回来值
CPSID I ;PRIMASK=1,关中止(NMI和硬fault能够呼应)
BX LR ;回来
OS_CPU_SR_Restore
MSR PRIMASK, R0 ;读取R0到PRIMASK中,R0为参数
BX LR ;回来
OSStartHighRdy()由OSStart()调用,用来发动最高优先级使命,当然使命有必要在OSStart()前已被创立。
OSStartHighRdy
;设置PendSV中止的优先级 #1
LDR R0, =NVIC_SYSPRI14 ;R0 = NVIC_SYSPRI14
LDR R1, =NVIC_PENDSV_PRI ;R1 = NVIC_PENDSV_PRI
STRB R1, [R0] ;*(uint8_t *)NVIC_SYSPRI14 = NVIC_PENDSV_PRI
;设置PSP为0 #2
MOVS R0, #0 ;R0 = 0
MSR PSP, R0 ;PSP = R0
;设置OSRunning为TRUE
LDR R0, =OSRunning ;R0 = OSRunning
MOVS R1, #1 ;R1 = 1
STRB R1, [R0] ;OSRunning = 1
;触发PendSV中止 #3
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
CPSIE I ;开中止
OSStartHang ;死循环,应该不会到这儿
B OSStartHang
#1.PendSV中止的优先级应该为最低优先级,原因在<>的7.6节已有阐明。
#2.PSP设置为0,是告知详细的使命切换程序(OS_CPU_PendSVHandler()),这是第一次使命切换。做过切换后PSP就不会为0了,后边会看到。
#3.往中止操控及状况存放器ICSR(0xE000ED04)第28位写1即可发生PendSV中止。这个<>8.4.5 其它反常的装备存放器有阐明。
当一个使命抛弃cpu的运用权,就会调用OS_TASK_SW()宏,而OS_TASK_SW()便是OSCtxSw()。OSCtxSw()应该做使命切换。但是在CM3中,全部使命切换都被放到PendSV的中止处理函数中去做了,因而OSCtxSw()只需简略的触发PendSV中止即可。OS_TASK_SW()是由OS_Sched()调用。
void OS_Sched (void)
{
#if OS_CRITICAL_METHOD == 3
OS_CPU_SR cpu_sr = 0;
#endif
OS_ENTER_CRITICAL();
if (OSIntNesting == 0) {
if (OSLockNesting == 0) {
OS_SchedNew();
if (OSPrioHighRdy != OSPrioCur) {
OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];
#if OS_TASK_PROFILE_EN > 0
OSTCBHighRdy->OSTCBCtxSwCtr++;
#endif
OSCtxSwCtr++;
OS_TASK_SW(); /* 触发PendSV中止 */
}
}
}
/* 一旦开中止,PendSV中止函数会履行(当然要等更高优先级中止处理完) */
OS_EXIT_CRITICAL();
}
OSCtxSw
;触发PendSV中止
LDR R0, =NVIC_INT_CTRL ;R0 = NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET ;R1 = NVIC_PENDSVSET
STR R1, [R0] ;*(uint32_t *)NVIC_INT_CTRL = NVIC_PENDSVSET
BX LR ;回来
当一个中止处理函数退出时,OSIntExit()会被调用来决议是否有优先级更高的使命需求履行。假如有OSIntExit()对调用OSIntCtxSw()做使命切换。
OSIntCtxSw
;触发PendSV中止
LDR R0, =NVIC_INT_CTRL
LDR R1, =NVIC_PENDSVSET
STR R1, [R0]
BX LR
看到这儿有些同学或许古怪怎样OSCtxSw()和OSIntCtxSw()彻底相同,事实上,这两个函数的含义是不相同的,OSCtxSw()做的是使命之间的切换,如使命A由于等候某个资源或是做延时切换到使命B,而OSIntCtxSw()则是中止退出时,由中止状况切换到另一个使命。由中止切换到使命时,CPU存放器入栈的作业现已做完了,所以无需做第2次了(参阅邵教师书的3.10节)。这儿只不过由于CM3的特别机制导致了在这两个函数中只要做触发PendSV中止即可,详细切换由PendSV中止来处理。
前面现已说过真实的使命切换是在PendSV中止处理函数里做的,由于CM3在中止时会有一半的存放器主动保存到使命仓库里,所以在PendSV中止处理函数中只需保存R4-R11并调理仓库指针即可。
PendSV中止处理函数伪代码如下:
OS_CPU_PendSVHandler()
{
if (PSP != NULL) {
Save R4-R11 onto task stack;
OSTCBCur->OSTCBStkPtr = SP;
}
OSTaskSwHook();
OSPrioCur = OSPrioHighRdy;
OSTCBCur = OSTCBHighRdy;
PSP = OSTCBHighRdy->OSTCBStkPtr;
Restore R4-R11 from new task stack;
Return from exception;
}
OS_CPU_PendSVHandler ;xPSR, PC, LR, R12, R0-R3已主动保存
CPSID I ;使命切换期间需求关中止
MRS R0, PSP ;R0 = PSP
;假如PSP == 0,跳到OS_CPU_PendSVHandler_nosave履行 #1
CBZ R0, OS_CPU_PendSVHandler_nosave
;保存R4-R11到使命仓库
SUBS R0, R0, #0x20 ;R0 -= 0x20
STM R0, {R4-R11} ;保存R4-R11到使命仓库
;OSTCBCur->OSTCBStkPtr = SP;
LDR R1, =OSTCBCur ;R1 = &OSTCBCur
LDR R1, [R1] ;R1 = *R1 (R1 = OSTCBCur)
STR R0, [R1] ;*R1 = R0 (*OSTCBCur = SP) #2
OS_CPU_PendSVHandler_nosave
;调用OSTaskSwHook()
PUSH {R14} ;保存R14,由于后边要调用函数
LDR R0, =OSTaskSwHook ;R0 = &OSTaskSwHook
BLX R0 ;调用OSTaskSwHook()
POP {R14} ;康复R14
;OSPrioCur = OSPrioHighRdy;
LDR R0, =OSPrioCur ;R0 = &OSPrioCur
LDR R1, =OSPrioHighRdy ;R1 = &OSPrioHighRdy
LDRB R2, [R1] ;R2 = *R1 (R2 = OSPrioHighRdy)
STRB R2, [R0] ;*R0 = R2 (OSPrioCur = OSPrioHighRdy)
;OSTCBCur = OSTCBHighRdy;
LDR R0, =OSTCBCur ;R0 = &OSTCBCur
LDR R1, =OSTCBHighRdy ;R1 = &OSTCBHighRdy
LDR R2, [R1] ;R2 = *R1 (R2 = OSTCBHighRdy)
STR R2, [R0] ;*R0 = R2 (OSTCBCur = OSTCBHighRdy)
LDR R0, [R2] ;R0 = *R2 (R0 = OSTCBHighRdy), 此刻R0是新使命的SP
;SP = OSTCBHighRdy->OSTCBStkPtr #3
LDM R0, {R4-R11} ;从使命仓库SP康复R4-R11
ADDS R0, R0, #0x20 ;R0 += 0x20
MSR PSP, R0 ;PSP = R0,用新使命的SP加载PSP
ORR LR, LR, #0x04 ;保证LR位2为1,回来后运用进程仓库 #4
CPSIE I ;开中止
BX LR ;中止回来
END
#1 假如PSP == 0,阐明OSStartHighRdy()发动后第一次做使命切换,而使命刚创立时R4-R11现已保存在仓库中了,所以不需求再保存一次了。
#2 OSTCBStkPtr是使命操控块结构体的第一个变量,所以*OSTCBCur = SP(不是很科学)便是OSTCBCur->OSTCBStkPtr = SP;
#3 和#2相似。
#4 由于在中止处理函数中运用的是MSP,所以在回来使命后有必要运用PSP,所以LR位2有必要为1。
os_dbg.c
用于体系调试,能够不论。
需求修正的代码就介绍到这儿,假如还有不理解之处,就再看看AN-1018.pdf,邵教师的书和<>。