之前移值过ucos2到stm32f2系列的单片机,这个单片机是属于arm的m3内核的。最近在学习永磁同步电机的控制,对于这个电机的控制,有比较多的数学计算,甚至于还有浮点的运算。所以用到了stm32f4系列的单片机,这个单片机内置FPU,可以用几条指令就可以处理单精度的浮点数据,而它是属于M4内核的。因为原先移植过M3的基础,想着应该很快会搞定,没想到移植了几天的时间才搞清楚,下面就记录下M3与M4内核的ucos2的移植不同之处。其实M3与M4内核相差不大,对于我应用的来说,其实最大的不同一是M4的最大主频提高了,二是M4带浮点功能。
1、ucos2移植到M3核的重点(对于ucos2移植到M3内核详细的讲解以及ucos2的结构后续会单独写一篇博客描述)
M3核对于操作系统的支持很好,它有一个NVIC向量中断控制器,它管理着对CM3的所有中断请求。其中有几个队操作系统很重要的中断:
a、SVC(系统服务调用),用于产生系统函数的调用请求,例如操作系统通常不让用户程序直接访问硬件,而是通过提供一些系统服务函数,让用户程序使用SVC发出对系统服务函数的呼叫请求,以这种方法调用它们来间接访问硬件。因此,当用户程序想要控制特定的硬件时,它就要产生一个SVC异常,然后操作系统提供的SVC异常服务例程得到执行,它再调用相关的操作系统函数,后者完成用户程序请求的服务。这与传统的arm核比如arm7与amr9等的SWI的软件中断异常类似。
b、PendSV(可悬起的系统中断),它是可以像普通的中断一样被悬起的(不像SVC那样会上访)。OS可以利用它“缓期执行”一个异常——直到其它重要的任务完成后才执行动作。悬起PendSV 的方法是:手工往NVIC的PendSV悬起寄存器中写1。悬起后,如果优先级不够高,则将缓期等待执行。所以它常常 被用来进行任务的切换,UCOS2的任务切换就是在这个中断里面实现的。
c、SysTick定时器被捆绑在NVIC中,用于产生SysTick异常(异常号:15)。SysTick定时器能产生中断,CM3为它专门开出一个异常类型,并且在向量表中有它的一席之地。它用于操作系统的滴答时钟。在以前,操作系统还有所有使用了时基的系统,都必须一个硬件定时器来产生需要的“滴答”中断,作为整个系统的时基。滴答中断对操作系统尤其重要。例如,操作系统可以为多个任务许以不同数目的时间片,确保没有一个任务能霸占系统;或者把每个定时器周期的某个时间范围赐予特定的任务等,还有操作系统提供的各种定时功能,都与这个滴答定时器有关。
ucos2对每一个任务都会分配一个任务控制块,任务控制块的首地址存放着该任务的堆栈指针,它存放的数据的格式如在函数,可以看到它的堆栈是向下递增的,也就是说堆栈顶部的位置始终是数值大的地址。首先存放的是M3内核发生异常时自动保存的数据,可以看到任务的函数地址也被保存在内,它的值其实就是任务发生切换时PC的值,如果后续要切换回这个任务了只要做一次PendSV中断,切换PSP指针为这个任务的堆栈指针,当从PendSV中断返回时task的值就会自动的存放到PC寄存器中,这样就做到了任务切换。接着保存M3内核发生异常时没有自动保存的寄存器,剩下的堆栈内容就保存任务函数的一些变量以及函数调用等等。
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) { OS_STK *p_stk; (void)opt; /* 'opt' is not used, prevent warning */ p_stk = ptos; /* Load stack pointer */ /* Registers stacked as if auto-saved on exception */ *(--p_stk) = (OS_STK)0x01000000uL; /* xPSR */ *(--p_stk) = (OS_STK)task; /* Entry Point */ *(--p_stk) = (OS_STK)OS_TaskReturn; /* R14 (LR) */ *(--p_stk) = (OS_STK)0x12121212uL; /* R12 */ *(--p_stk) = (OS_STK)0x03030303uL; /* R3 */ *(--p_stk) = (OS_STK)0x02020202uL; /* R2 */ *(--p_stk) = (OS_STK)0x01010101uL; /* R1 */ *(--p_stk) = (OS_STK)p_arg; /* R0 : argument */ /* Remaining registers saved on process stack */ *(--p_stk) = (OS_STK)0x11111111uL; /* R11 */ *(--p_stk) = (OS_STK)0x10101010uL; /* R10 */ *(--p_stk) = (OS_STK)0x09090909uL; /* R9 */ *(--p_stk) = (OS_STK)0x08080808uL; /* R8 */ *(--p_stk) = (OS_STK)0x07070707uL; /* R7 */ *(--p_stk) = (OS_STK)0x06060606uL; /* R6 */ *(--p_stk) = (OS_STK)0x05050505uL; /* R5 */ *(--p_stk) = (OS_STK)0x04040404uL; /* R4 */ return (p_stk); }
接着看到UCOS2在M3内核下运行时的任务切换代码,可以看到任务切换函数,主要是将被打断任务的堆栈保存好,然后将需要运行的任务的堆栈出栈,任何以从进程堆栈中做出栈操作,进行任务切换
PendSV_Handler //;中断处理函数 CPSID I //; Prevent interruption during context switch ;在任务切换时禁止其它中断 MRS R0, PSP //; PSP is process stack pointer ; CBZ R0, OS_CPU_PendSVHandler_nosave //; Skip register save the first time ;检查是否是从进程中进来的,如果不是,直接跳到OS_CPU_PendSVHandler_nosave 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 保存当前任务堆栈指针到当前任务控制块 //; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} //; Save LR exc_return value LDR R0, =OSTaskSwHook //; OSTaskSwHook(); BLX R0 //; POP {R14} //;//只是为了调用OSTaskSwHook函数 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] //; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} // ; Restore r4-11 from new process stack 取出新任务的R4-R11寄存器值 ADDS R0, R0, #0x20 MSR PSP, R0 // ; Load PSP with new process SP 切换新任务指针给PSP ORR LR, LR, #0x04 // ; Ensure exception return uses process stack 从进程堆栈中作出出栈操作 CPSIE I // ; 开启中断 BX LR // ; Exception return will restore remaining context 任务切换,切换到新任务被切换时的地址 END
2、ucos2移植到M4核
UCOS2在M3与M4运行任务切换时的原理是一样的,都是利用的PendSV中断。但是保存的寄存器有所不同,因为M4内核可以支持单精度浮点操作,这样就会有了浮点相关的寄存器的保存。这就是与M3内核最大的不同之处。支持浮点运算的M4内核比M3内核多一个FPU模块,它里面有33个寄存器,其中S0-S16是异常上下文会自动保存的。
a、M4内核堆栈的初始化,与M3相比多了保存34个寄存的内容
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt) { OS_STK *p_stk; (void)opt; /* 'opt' is not used, prevent warning */ p_stk = ptos; /* Load stack pointer */ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) *(--p_stk) = (INT32U)0x00000000L; //No Name Register *(--p_stk) = (INT32U)0x00001000L; //FPSCR *(--p_stk) = (INT32U)0x00000015L; //s15 *(--p_stk) = (INT32U)0x00000014L; //s14 *(--p_stk) = (INT32U)0x00000013L; //s13 *(--p_stk) = (INT32U)0x00000012L; //s12 *(--p_stk) = (INT32U)0x00000011L; //s11 *(--p_stk) = (INT32U)0x00000010L; //s10 *(--p_stk) = (INT32U)0x00000009L; //s9 *(--p_stk) = (INT32U)0x00000008L; //s8 *(--p_stk) = (INT32U)0x00000007L; //s7 *(--p_stk) = (INT32U)0x00000006L; //s6 *(--p_stk) = (INT32U)0x00000005L; //s5 *(--p_stk) = (INT32U)0x00000004L; //s4 *(--p_stk) = (INT32U)0x00000003L; //s3 *(--p_stk) = (INT32U)0x00000002L; //s2 *(--p_stk) = (INT32U)0x00000001L; //s1 *(--p_stk) = (INT32U)0x00000000L; //s0 #endif /* Registers stacked as if auto-saved on exception */ *(--p_stk) = (OS_STK)0x01000000uL; /* xPSR */ *(--p_stk) = (OS_STK)task; /* Entry Point */ *(--p_stk) = (OS_STK)OS_TaskReturn; /* R14 (LR) */ *(--p_stk) = (OS_STK)0x12121212uL; /* R12 */ *(--p_stk) = (OS_STK)0x03030303uL; /* R3 */ *(--p_stk) = (OS_STK)0x02020202uL; /* R2 */ *(--p_stk) = (OS_STK)0x01010101uL; /* R1 */ *(--p_stk) = (OS_STK)p_arg; /* R0 : argument */ #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) *(--p_stk) = (INT32U)0x00000031L; //s31 *(--p_stk) = (INT32U)0x00000030L; //s30 *(--p_stk) = (INT32U)0x00000029L; //s29 *(--p_stk) = (INT32U)0x00000028L; //s28 *(--p_stk) = (INT32U)0x00000027L; //s27 *(--p_stk) = (INT32U)0x00000026L; //s26 *(--p_stk) = (INT32U)0x00000025L; //s25 *(--p_stk) = (INT32U)0x00000024L; //s24 *(--p_stk) = (INT32U)0x00000023L; //s23 *(--p_stk) = (INT32U)0x00000022L; //s22 *(--p_stk) = (INT32U)0x00000021L; //s21 *(--p_stk) = (INT32U)0x00000020L; //s20 *(--p_stk) = (INT32U)0x00000019L; //s19 *(--p_stk) = (INT32U)0x00000018L; //s18 *(--p_stk) = (INT32U)0x00000017L; //s17 *(--p_stk) = (INT32U)0x00000016L; //s16 #endif /* Remaining registers saved on process stack */ *(--p_stk) = (OS_STK)0x11111111uL; /* R11 */ *(--p_stk) = (OS_STK)0x10101010uL; /* R10 */ *(--p_stk) = (OS_STK)0x09090909uL; /* R9 */ *(--p_stk) = (OS_STK)0x08080808uL; /* R8 */ *(--p_stk) = (OS_STK)0x07070707uL; /* R7 */ *(--p_stk) = (OS_STK)0x06060606uL; /* R6 */ *(--p_stk) = (OS_STK)0x05050505uL; /* R5 */ *(--p_stk) = (OS_STK)0x04040404uL; /* R4 */ return (p_stk); }
a、M4内核的任务切换,与M3相比多了保存16个寄存器
PendSV_Handler //;中断处理函数 CPSID I //; Prevent interruption during context switch ;在任务切换时禁止其它中断 MRS R0, PSP //; PSP is process stack pointer ; CBZ R0, OS_CPU_PendSVHandler_nosave //; Skip register save the first time ;检查是否是从进程中进来的,如果不是,直接跳到OS_CPU_PendSVHandler_nosave //;Is the task using the FPU context? If so, push high vfp registers.支持M4内核保存S16-S31寄存器 TST R14, #0x10 IT EQ VSTMDBEQ R0!, {S16-S31} 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 保存当前任务堆栈指针到当前任务控制块 //; At this point, entire context of process has been saved OS_CPU_PendSVHandler_nosave PUSH {R14} //; Save LR exc_return value LDR R0, =OSTaskSwHook //; OSTaskSwHook(); BLX R0 //; POP {R14} //;//只是为了调用OSTaskSwHook函数 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] //; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} // ; Restore r4-11 from new process stack 取出新任务的R4-R11寄存器值 ADDS R0, R0, #0x20 ;Is the task using the FPU context? If so, push high vfp registers.支持M4内核出栈S16-S31寄存器 TST R14, #0x10 IT EQ VLDMIAEQ R0!, {S16-S31} MSR PSP, R0 // ; Load PSP with new process SP 切换新任务指针给PSP ORR LR, LR, #0x04 // ; Ensure exception return uses process stack 从进程堆栈中作出出栈操作 CPSIE I // ; 开启中断 BX LR // ; Exception return will restore remaining context 任务切换,切换到新任务被切换时的地址 END
以上就是移值UCOS2到M4核与M3核的区别