移值UCOS2到M4核与M3核的区别

Posted andyfly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移值UCOS2到M4核与M3核的区别相关的知识,希望对你有一定的参考价值。

之前移值过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核的区别

 

以上是关于移值UCOS2到M4核与M3核的区别的主要内容,如果未能解决你的问题,请参考以下文章

m4i系列作为M3i的替代品有哪些优势?

根目录下有文件m1.txt,m2.txt,m3.txt,m4.txt,用shell编程,自动创建m1,m2,m3,m4目录,并拷文件到各自目录

在IBM3850M3以及3650M4上安装SERVER08R2怎么加载阵列卡驱动

单片机行业经常提到的M0 M1 M2 M3 M4 M7指的是啥

LinkedHashMap和HashMap的区别以及使用方法

如何获得可靠的 Cortex M4 短延迟