单片机 > 单片机程序设计 > 详情

基于STM32F407的官方版UCOS III源码,增加了FPU浮点支持

发布时间:2020-08-26 发布时间:
|
基于CORTEX-M4的STM32F407最大的特色就是加入了一个FPU浮点处理器,能支持DSP运算。但是UCOS III官方的移植版本竟然不支持FPU浮点运算,如果在MDK的设置选项中把使能FPU打上的话运行的时候会把芯片带到硬fault,我用STLINK逐步跟踪,发现在使能FPU后系统在进行任务堆栈切换的时候发生错误进入硬fault,遂在GOOGLE上搜索了半天,并查看了M4的参考手册,发现使能FPU之后系统在进入中断的时候会自动将部分FPU寄存器入栈,而不支持FPU的UCOS移植版本显然没有为FPU寄存器做堆栈设置,所以在堆栈进行还原的时候导致还原点不同,进入硬fault。下面逐一分析:

1. 在UCOS III for STM32F4 中,UCOS在进行任务切换的时候并不是直接在任务中进行堆栈切换,而是使用了CORTEX-M系统架构中的一个叫做PENDSV的异常,通过悬起一个PENDSV异常,PENDSV异常其实想当于一个SVC(系统高胜),但是跟SVC的在于PENDSV的悬起特性,即是说置位一个PSNDSV异常之后如果当前还有更高优先级的异常在进行,则PENDSV会被延迟执行,当更高优先级的异常执行完毕才轮到PENDSV例程执行,CORTEX-M的尾链技术能够确保在别的中断完毕退出之后能够快速进入PENDSV执行,在PENDSV中,UCOS才进行实际的任务切换操作,PS:STM32有两个堆栈,一个是PSP(线程堆栈),一个是MSP(管理堆栈),一般来说用户程序运行在PSP的线程模式中,所有中断例程运行在MSP的特权模式中。更多关于PENDSV的内容参见《Cortex-M3权威指南》中文版(宋岩译)P121-P124

2. 使能/失能 FPU时的中断入栈区别。这个就是修改FPU支持的重点,在STM32F4中,发生中断时会将部分寄存器入栈,再进入中断全程,但是使能和失能FPU这两两种情况的入栈流程是有区别的。
a). 首先是普通情况下,不使用FPU的时候,这个时候CORTEX-M4是跟CORTEX-M3一样的入栈方式,一个异常到来的时候,系统自动将R1-R3、R12、LR、PC、xPSR入栈,这个时候UCOS官方移植版会接着将剩下的R4-R11入栈,保证当前任务的执行上下文全部保存起来,在os_cpu_a.asm的OS_CPU_PendSVHandler中有如下语句:
    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack    STM     R0, {R4-R11}

b). 在使用FPU的情况下,异常到来的时候,CORTEX-M4会将部分FPU的寄存器也一起自动入栈,包括一个未知的字,一个FPSCR寄存器,以及S15-S0,所以如果要使UCOS支持FPU,就必须配合M4的这种入栈/出栈行为相应地修改任务堆栈的结构以及任务切换时的入栈/出栈汇编代码

3. 要修改的有两个地方,一个是任务初始化时的堆栈结构设置,在os_cpu_c.c的OSTaskStkInit函数中,函数原型:
CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,
                         void          *p_arg,
                         CPU_STK       *p_stk_base,
                         CPU_STK       *p_stk_limit,
                         CPU_STK_SIZE   stk_size,
                         OS_OPT         opt)
{
    CPU_STK  *p_stk;


    (void)opt;                                              /* Prevent compiler warning                               */

    p_stk = &p_stk_base[stk_size];                          /* Load stack pointer                                     */
                                                                /* Align the stack to 8-bytes.                            */
    p_stk = (CPU_STK *)((CPU_STK)(p_stk) & 0xFFFFFFF8);
                                                            /* Registers stacked as if auto-saved on exception        */
    *--p_stk = (CPU_STK)0x01000000u;                        /* xPSR                                                   */
    *--p_stk = (CPU_STK)p_task;                             /* Entry Point                                            */
    *--p_stk = (CPU_STK)OS_TaskReturn;                      /* R14 (LR)                                               */
    *--p_stk = (CPU_STK)0x12121212u;                        /* R12                                                    */
    *--p_stk = (CPU_STK)0x03030303u;                        /* R3                                                     */
    *--p_stk = (CPU_STK)0x02020202u;                        /* R2                                                     */
    *--p_stk = (CPU_STK)p_stk_limit;                        /* R1                                                     */
    *--p_stk = (CPU_STK)p_arg;                              /* R0 : argument                                          */
                                                            /* Remaining registers saved on process stack             */
    *--p_stk = (CPU_STK)0x11111111u;                        /* R11                                                    */
    *--p_stk = (CPU_STK)0x10101010u;                        /* R10                                                    */
    *--p_stk = (CPU_STK)0x09090909u;                        /* R9                                                     */
    *--p_stk = (CPU_STK)0x08080808u;                        /* R8                                                     */
    *--p_stk = (CPU_STK)0x07070707u;                        /* R7                                                     */
    *--p_stk = (CPU_STK)0x06060606u;                        /* R6                                                     */
    *--p_stk = (CPU_STK)0x05050505u;                        /* R5                                                     */
    *--p_stk = (CPU_STK)0x04040404u;                        /* R4                                                     */

    return (p_stk);
}

需要在中间增加FPU寄存器的保存结构,修改成下面的代码,红色部分即是修改的,使用两人个宏来判断是否需要增加此结构,以便用户自由地在MDK设置中设置是否启用FPU,而不用亲自修改该部分代码,为些需要在该文件头中#include  "stm32f4xx.h"
CPU_STK  *OSTaskStkInit (OS_TASK_PTR    p_task,
                         void          *p_arg,
                         CPU_STK       *p_stk_base,
                         CPU_STK       *p_stk_limit,
                         CPU_STK_SIZE   stk_size,
                         OS_OPT         opt)
{
    CPU_STK  *p_stk;


    (void)opt;                                              /* Prevent compiler warning                               */

    p_stk = &p_stk_base[stk_size];                          /* Load stack pointer                                     */
                                                                /* Align the stack to 8-bytes.                            */
    p_stk = (CPU_STK *)((CPU_STK)(p_stk) & 0xFFFFFFF8);
                                                            /* Registers stacked as if auto-saved on exception        */
#if(__FPU_PRESENT == 1)&&(__FPU_USED == 1)
        *--p_stk = (CPU_STK)0x00000000u;                        /*unknow register                                         */
        *--p_stk = (CPU_STK)0x00000000u;                        /*FPSCR                                                   */
        *--p_stk = (CPU_STK)0x15151515u;                        /*S15                                                     */
        *--p_stk = (CPU_STK)0x14141414u;                        /*S14                                                     */
        *--p_stk = (CPU_STK)0x13131313u;                        /*S13                                                     */
        *--p_stk = (CPU_STK)0x12121212u;                        /*S12                                                     */
        *--p_stk = (CPU_STK)0x11111111u;                        /*S11                                                     */
        *--p_stk = (CPU_STK)0x10101010u;                        /*S10                                                     */
        *--p_stk = (CPU_STK)0x09090909u;                        /*S09                                                     */
        *--p_stk = (CPU_STK)0x08080808u;                        /*S08                                                     */
        *--p_stk = (CPU_STK)0x07070707u;                        /*S07                                                     */
        *--p_stk = (CPU_STK)0x06060606u;                        /*S06                                                     */
        *--p_stk = (CPU_STK)0x05050505u;                        /*S05                                                     */
        *--p_stk = (CPU_STK)0x04040404u;                        /*S04                                                     */
        *--p_stk = (CPU_STK)0x03030303u;                        /*S03                                                     */
        *--p_stk = (CPU_STK)0x02020202u;                        /*S02                                                     */
        *--p_stk = (CPU_STK)0x01010101u;                        /*S01                                                     */
        *--p_stk = (CPU_STK)0x00000000u;                        /*S00                                                     */
#endif
    *--p_stk = (CPU_STK)0x01000000u;                        /* xPSR                                                   */
    *--p_stk = (CPU_STK)p_task;                             /* Entry Point                                            */
    *--p_stk = (CPU_STK)OS_TaskReturn;                      /* R14 (LR)                                               */
    *--p_stk = (CPU_STK)0x12121212u;                        /* R12                                                    */
    *--p_stk = (CPU_STK)0x03030303u;                        /* R3                                                     */
    *--p_stk = (CPU_STK)0x02020202u;                        /* R2                                                     */
    *--p_stk = (CPU_STK)p_stk_limit;                        /* R1                                                     */
    *--p_stk = (CPU_STK)p_arg;                              /* R0 : argument                                          */
                                                            /* Remaining registers saved on process stack             */                                                        
#if(__FPU_PRESENT == 1)&&(__FPU_USED == 1)
        *--p_stk = (CPU_STK)0x31313131u;                        /*S31                                                     */
        *--p_stk = (CPU_STK)0x30303030u;                        /*S30                                                     */
        *--p_stk = (CPU_STK)0x29292929u;                        /*S29                                                     */
        *--p_stk = (CPU_STK)0x28282828u;                        /*S28                                                     */
        *--p_stk = (CPU_STK)0x27272727u;                        /*S27                                                     */
        *--p_stk = (CPU_STK)0x26262626u;                        /*S26                                                     */
        *--p_stk = (CPU_STK)0x25252525u;                        /*S25                                                     */
        *--p_stk = (CPU_STK)0x24242424u;                        /*S24                                                     */
        *--p_stk = (CPU_STK)0x23232323u;                        /*S23                                                     */
        *--p_stk = (CPU_STK)0x22222222u;                        /*S22                                                     */
        *--p_stk = (CPU_STK)0x21212121u;                        /*S21                                                     */
        *--p_stk = (CPU_STK)0x20202020u;                        /*S20                                                     */
        *--p_stk = (CPU_STK)0x19191919u;                        /*S19                                                     */
        *--p_stk = (CPU_STK)0x18181818u;                        /*S18                                                     */
        *--p_stk = (CPU_STK)0x17171717u;                        /*S17                                                     */
        *--p_stk = (CPU_STK)0x16161616u;                        /*S16                                                     */
#endif        
    *--p_stk = (CPU_STK)0x11111111u;                        /* R11                                                    */
    *--p_stk = (CPU_STK)0x10101010u;                        /* R10                                                    */
    *--p_stk = (CPU_STK)0x09090909u;                        /* R9                                                     */
    *--p_stk = (CPU_STK)0x08080808u;                        /* R8                                                     */
    *--p_stk = (CPU_STK)0x07070707u;                        /* R7                                                     */
    *--p_stk = (CPU_STK)0x06060606u;                        /* R6                                                     */
    *--p_stk = (CPU_STK)0x05050505u;                        /* R5                                                     */
    *--p_stk = (CPU_STK)0x04040404u;                        /* R4                                                     */

    return (p_stk);
}
第二个需要修改的地方是任务切换函数,上面说了UCOS使用PENDSV异常来执行任务切换动作,所以在源码中找出PENDSV异常例程的代码,在os_cpu_a.asm文件中,使用汇编编写,找到OS_CPU_PendSVHandler函数,原型如下:
OS_CPU_PendSVHandler
    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

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCurPtr                                    ; OSTCBCurPtr->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}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur   = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCurPtr                                    ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R1, =OSTCBHighRdyPtr
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    END

修改后为:
OS_CPU_PendSVHandler
    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


    ;if enable the FPU
    SUBS    R0, R0, #0X40
    VSTM    R0, {S16-S31}

    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
    STM     R0, {R4-R11}

    LDR     R1, =OSTCBCurPtr                                    ; OSTCBCurPtr->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}

    LDR     R0, =OSPrioCur                                      ; OSPrioCur   = OSPrioHighRdy;
    LDR     R1, =OSPrioHighRdy
    LDRB    R2, [R1]
    STRB    R2, [R0]

    LDR     R0, =OSTCBCurPtr                                    ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R1, =OSTCBHighRdyPtr
    LDR     R2, [R1]
    STR     R2, [R0]

    LDR     R0, [R2]                                            ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20
        
        
    ;if enable FPU
    VLDM    R0, {S16-S31}
    ADDS    R0, R0, #0X40
        
        
    MSR     PSP, R0                                             ; Load PSP with new process SP
    ORR     LR, LR, #0x04                                       ; Ensure exception return uses process stack
    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    END


这些还需要在程序一开始启动FPU,我找到cstartup.s启动文件的Reset_Handler中开头有一段被注释掉的FPU启动代码,将些注释释放出来即可


同时需要在Project->Options->Target右边的Code Generation中选择“Use FPU”.

至此,修改工作完成,测试一下跑了两个任务执行浮点运算,相互不干扰,正常运行!!

附修改后的工程源码,删除了一些不必要的代码。 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
ARM JTAG 调试原理