- 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。