本帖最后由 zhangbin_abc 于 2015-12-27 17:11 编辑
STM32F469具有cortex-M4架构,而cortex-M4相对于cortex-M3一个显著的不同就是多了一个浮点单元FPU,其他的二者差不多都一样,所以如果你以前在STM32的cortex-M3架构的微控制器上移植过uC/OS-II操作系统,而你的系统中又不开启FPU的话,以前的程序应该可以直接在STM32F469就能跑了。但是FPU对于浮点计算具有很大的优势,如果要开启该功能单元,则需要在移植的时候进行一些文件的修改。 下面将在上一篇建立的工程模板的基础上,结合FPU详细说明uC/OS-II STM32F469上的移植。
一、软件准备
uC/OS-II的源码可以在官网上自行下载。
二、工程文件组织
在上一篇的工程模板中,将uC/OS-II的源码加入其中,组织结构如图所示。在uC/OS-II的Source文件夹中的os_dbg_r.c和ucos_ii.c文件不要添加到工程中,否则编译链接时会提示变量重复定义。
三、与移植相关的文件的修改
1、时钟节拍修改
1)初始化Systick: 在这里使用STM32的库函数进行实现,因此将os_cpu_c.c文件中的void OS_CPU_SysTickInit (INT32U cnts)函数注释掉,而采用core_cm4.h文件中的__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)函数,实现如下:
static void OS_CPU_SysTickInit(void)
{
SysTick_Config(SysCoreClock/OS_TICKS_PER_SEC);
}
这里SysCoreClock是系统的内核时钟,即180MHz,OS_TICKS_PER_SEC是每秒uC/OS-ii的时钟节拍数,在os_cfg.h文件中定义,这里设置为1000,即1ms产生一次节拍中断。
2)Systick中断服务程序的修改: 首先,在TM32F469的stm32f4xx_it.c和uC/OS-II的os_cpu_c.c文件中均定义了void SysTick_Handler (void) 函数,造成重复定义,所以这里将os_cpu_c.c文件中的相关函数注释掉,而在stm32f4xx_it.c文件中进行实现:
void SysTick_Handler(void)
{
static uint32_t i;
OSIntEnter(); /* enter the interrupt, */
OSTimeTick();
OSIntExit();
i++;
if(i==1000){
LED1_ON();
}
else if(i==2000){
LED1_OFF();
i = 0;
}
}
在这里我添加了一个LED的闪烁代码,指示系统在正常运行。
2、os_cpu_c.c文件中系统堆栈初始化函数的修改
由于在uC/OS-II任务的上下文切换时需要进入PendSV异常,这就需要对一些寄存器进行保存。
当使用FPU时,将涉及到S0~S31寄存器组。在系统进入异常处理服务时,FPSCR、S0~S15、xPSR、PC、LR、R12、R3~R0这几个寄存器是被自动入栈的。入栈顺序如下图所示。
剩余的S16~S31和R4~R11寄存器需要手动入栈。根据以上情况,对OSTaskStkInit函数修改如下: 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 + 1; /* Load stack pointer */
stk = (OS_STK *)((OS_STK)(stk)&0xFFFFFFF8u);
#if (__FPU_PRESENT==1)&&(__FPU_USED==1)
*(--stk) = (INT32U)0x00000000L; //No Name Register
*(--stk) = (INT32U)0x00001000L; //FPSCR
*(--stk) = (INT32U)0x00000015L; //s15
*(--stk) = (INT32U)0x00000014L; //s14
*(--stk) = (INT32U)0x00000013L; //s13
*(--stk) = (INT32U)0x00000012L; //s12
*(--stk) = (INT32U)0x00000011L; //s11
*(--stk) = (INT32U)0x00000010L; //s10
*(--stk) = (INT32U)0x00000009L; //s9
*(--stk) = (INT32U)0x00000008L; //s8
*(--stk) = (INT32U)0x00000007L; //s7
*(--stk) = (INT32U)0x00000006L; //s6
*(--stk) = (INT32U)0x00000005L; //s5
*(--stk) = (INT32U)0x00000004L; //s4
*(--stk) = (INT32U)0x00000003L; //s3
*(--stk) = (INT32U)0x00000002L; //s2
*(--stk) = (INT32U)0x00000001L; //s1
*(--stk) = (INT32U)0x00000000L; //s0
#endif
/* Registers stacked as if auto-saved on exception */
*(--stk) = (INT32U)0x01000000uL; /* xPSR */
*(--stk) = (INT32U)task; /* Entry Point */
*(--stk) = (INT32U)OS_TaskReturn; /* R14 (LR) */
*(--stk) = (INT32U)0x12121212uL; /* R12 */
*(--stk) = (INT32U)0x03030303uL; /* R3 */
*(--stk) = (INT32U)0x02020202uL; /* R2 */
*(--stk) = (INT32U)0x01010101uL; /* R1 */
*(--stk) = (INT32U)p_arg; /* R0 : argument */
#if (__FPU_PRESENT==1)&&(__FPU_USED==1)
*(--stk) = (INT32U)0x00000031L; //s31
*(--stk) = (INT32U)0x00000030L; //s30
*(--stk) = (INT32U)0x00000029L; //s29
*(--stk) = (INT32U)0x00000028L; //s28
*(--stk) = (INT32U)0x00000027L; //s27
*(--stk) = (INT32U)0x00000026L; //s26
*(--stk) = (INT32U)0x00000025L; //s25
*(--stk) = (INT32U)0x00000024L; //s24
*(--stk) = (INT32U)0x00000023L; //s23
*(--stk) = (INT32U)0x00000022L; //s22
*(--stk) = (INT32U)0x00000021L; //s21
*(--stk) = (INT32U)0x00000020L; //s20
*(--stk) = (INT32U)0x00000019L; //s19
*(--stk) = (INT32U)0x00000018L; //s18
*(--stk) = (INT32U)0x00000017L; //s17
*(--stk) = (INT32U)0x00000016L; //s16
#endif
/* Remaining registers saved on process stack */
*(--stk) = (INT32U)0x11111111uL; /* R11 */
*(--stk) = (INT32U)0x10101010uL; /* R10 */
*(--stk) = (INT32U)0x09090909uL; /* R9 */
*(--stk) = (INT32U)0x08080808uL; /* R8 */
*(--stk) = (INT32U)0x07070707uL; /* R7 */
*(--stk) = (INT32U)0x06060606uL; /* R6 */
*(--stk) = (INT32U)0x05050505uL; /* R5 */
*(--stk) = (INT32U)0x04040404uL; /* R4 */
*(--stk) = (INT32U)0xFFFFFFFDUL;
return (stk);
}
这里需要注意的是在程序的结尾处一定要添加 *(--stk) = (INT32U)0xFFFFFFFDUL; 这个是对EXC_RETURN特殊寄存器进行堆栈初始化。 3、os_cpu_a.asm文件修改 在该文件中主要是对PendSV异常中断服务程序进行修改。由于在STM32F469的启动汇编文件startup_stm32f469_479xx.s中也有相应函数的声明,所以在这两个文件中的文件名应该修改为相同,否则编译会出错,这里统一修改为PendSV_Handler。在PendSV_Handler函数中主要实现内容如下: PendSV_Handler
CPSID I ; Prevent interruption during context switch
MRS R0, PSP ; PSP is process stack pointer
CBZ R0, PendSV_Handler_Nosave ; Skip register save the first time
;Is the task using the FPU context? If so, push high vfp registers.
TST R14, #0x10
IT EQ
VSTMDBEQ R0!, {S16-S31}
MOV R3, R14
STMDB R0!, {R3-R11}
;SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack
;STM R0, {R4-R11}
LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP;
LDR R1, [R1]
STR R0, [R1] ; R0 is SP of process being switched out
这里需要注意第7~12行的代码,这是在Cortex-m4中开启FPU所特有的,具体分析如下:上面的意思是说进入异常服务程序后,LR寄存器(即R14寄存器)的值会被自动更新到EXC_RETURN寄存器中,该寄存器的具体位段描述如下:
其中bit4的值决定了我们要将哪些寄存器进行入栈处理,如果为0,说明使用了FPU,需要对S16~S31进行入栈,然后将R4~R11入栈,与此同时,还需将EXC_RETURN入栈,这是与堆栈初始化函数中最后一句相对应的。 而PendSV_Handler_Nosave对应寄存器出栈处理,内容如下: PendSV_Handler_Nosave
PUSH {R14} ; Save LR exc_return value
LDR R0, =OSTaskSwHook ; OSTaskSwHook();
BLX R0
POP {R14}
LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy;
LDR R1, =OSPrioHighRdy
LDRB R2, [R1]
STRB R2, [R0]
LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy;
LDR R1, =OSTCBHighRdy
LDR R2, [R1]
STR R2, [R0]
LDR R0, [R2]
LDMIA R0!, {R3-R11}
MOV R14, R3
; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
;LDM R0, {R4-R11} ; Restore r4-11 from new process stack
;ADDS R0, R0, #0x20
;Is the task using the FPU context? If so, push high vfp registers.
TST R14, #0x10
IT EQ
VLDMIAEQ R0!, {S16-S31}
四、移植测试
最后编写了一个测试程序,对uC/OS-II操作系统能否正常运行进行测试。如果需要开启FPU,还需要在系统初始化中进行开启操作,而在system_stm32f4xx.c文件中的系统上电初始化函数SystemInit中,有相应的代码如下:
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
所以必须将__FPU_PRESENT 和__FPU_USED定义为1,而__FPU_PRESENT在stm32f4xx.h中已经定义为1,所以需要在options for Target中的C/C++选项卡中添加__FPU_USED的宏定义:
同时在Target选项卡的Floating Point Hardware中选择使用单精度,即Use Single Precision。
测试部分主要代码如下:
void App_TaskCreat(void)
{
u3_printf("create multitask...\r\n");
OSTaskCreateExt(App_Task1,(void *)0,&AppTask1Stk[APP_TASK_1_STK_SIZE-1],APP_TASK_1_PRIO,
APP_TASK_1_PRIO,&AppTask1Stk[0],APP_TASK_1_STK_SIZE,(void *)0,(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));
OSTaskCreateExt(App_Task2,(void *)0,&AppTask2Stk[APP_TASK_2_STK_SIZE-1],APP_TASK_2_PRIO,
APP_TASK_2_PRIO,&AppTask2Stk[0],APP_TASK_2_STK_SIZE,(void *)0,(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));
}
static void App_Task1(void *p_arg)
{
static float float_num=0.00f;
(void)p_arg;
while(1){
float_num += 0.01f;
u3_printf("float_num:%.4f\r\n",float_num);
u3_printf("Task1 is running...\r\n");
OSTimeDlyHMSM(0,0,1,0);
}
}
static void App_Task2(void *p_arg)
{
(void)p_arg;
while(1){
u3_printf("Task2 is running...\r\n");
OSTimeDlyHMSM(0,0,1,0);
}
}
编译链接后下载运行,结果如下图所示,说明系统移植成功。
在程序中float_num += 0.01f;语句前面设置断点,观察反汇编代码,如下:
可以看到程序将要执行VLDR、VADD、VSTR等汇编指令,而这些指令都是单精度浮点指令。而左边窗口中FPU状态和控制寄存器FPSCR中的IXC位为1
从上表中可以看到,IXC位置1表示发生了浮点异常。以上实验现象均说明系统确实开启了FPU。
|