STM32F407+UCOSII 使用浮点数导致hardfault

目录

现象:

原因:

开启FPU的配置:

1。CMSIS-Core配置

2。MDK配置

解决方案:

1。关闭惰性压栈

2。修改UCOSII

2。1  修改函数 CPU_STK *OSTaskStkInit()

2。2   修改函数 PendSV_Handler

参考:


现象:

       STM32F407 + UCOSII v2.92 使用浮点数会进入hardfault

原因:

        ucosii 移植时不支持FPU

开启FPU的配置:

        要开启FPU需要配置两个地方:代码预编译宏(使用FPU模块)和MDK编译配置(编译生成使用FPU的相关汇编)        

1。CMSIS-Core配置

CMSIS-Core具有两个和FPU配置相关的预处理宏__FPU_PRESENT 和 __FPU_USED只有这两个宏都为1时,SystemInit()函数才会通过配置CPACR寄存器来使用FPU

2。MDK配置

MDK配置项 “Floating Point Hardware" 选择 ”Single Precision"

如果选择“Not Used"则会关闭FPU

注:如果这里配置为"Not Used"会导致__FPU_USED宏为定义为0,因为如果选择”Not Used“那么MDK将不会定义宏__TARGET_FPU_VFP,最终导致 __FPU_USED 为宏,他们的关系如下图

解决方案:

        可以选择以下两个方案的其中一种

1。关闭惰性压栈

惰性压栈是和浮点单元寄存器压栈有关的一种特性,是Cortex-M4的一个重要特性。在FPU可用且使用的情况下产生了异常,则栈帧的长度会增加。不过,为了减少中断等待时间,只会将R0-R3、R12、LR、返回地址和xPSR压栈。

关于惰性压栈详细说明可查看《ARM Cortex-M3与Cortex-M4权威指南》的8.3.6 和 13.3 两个章节。

修改startup_stm32f407xx.s文件中Reset_Handler, 增加FPU使能和关闭惰性压栈修,改如下:

修改前:

Reset_Handler    PROC
                 EXPORT  Reset_Handler             [WEAK]
        IMPORT  SystemInit
        IMPORT  __main

                 LDR     R0, =SystemInit
                 BLX     R0
                 LDR     R0, =__main
                 BX      R0
                 ENDP

修改后:

Reset_Handler   PROC    
                EXPORT  Reset_Handler                 [WEAK]
                IMPORT  __main

                IF {FPU} != "SoftVFP"
                                                ; 使用能FPU
                                                ; Enable Floating Point Support at reset for FPU												
                LDR.W   R0, =0xE000ED88         ; Load address of CPACR register
                LDR     R1, [R0]                ; Read value at CPACR
                ORR     R1,  R1, #(0xF <<20)    ; Set bits 20-23 to enable CP10 and CP11 coprocessors
                                                ; Write back the modified CPACR value
                STR     R1, [R0]                ; Wait for store to complete
                DSB
                
                                                ; 关闭惰性压栈和 关闭寄存器(S0~S15 & FPSCR)自动入栈和出栈
												; Disable automatic FP register content
                                                ; Disable lazy context switch
                LDR.W   R0, =0xE000EF34         ; Load address to FPCCR register
                LDR     R1, [R0]
                AND     R1,  R1, #(0x3FFFFFFF)  ; Clear the LSPEN and ASPEN bits
                STR     R1, [R0]
                ISB                             ; Reset pipeline now the FPU is enabled
                ENDIF

                LDR     R0, =__main
                BX      R0
                ENDP

上面修改涉及到的两个寄存器如下:

2。修改UCOSII

        修改UCOSII 源码,使其支持FPU。主要修改两个函数:一个是 CPU_STK *OSTaskStkInit(),另一个是 PendSV中断。

2。1  修改函数 CPU_STK *OSTaskStkInit()

此函数在 os_cpu_c.c 中。

修改后,如下:

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 + 1u;                                     /* Load stack pointer                                   */
                                                                /* Align the stack to 8-bytes.                          */
    p_stk      = (OS_STK *)((OS_STK)(p_stk) & 0xFFFFFFF8u);
                                                                /* 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                                                   */

    /*
     这句话最重要,这里是将 EXC_RETURN 也进行了入栈处理。
     对于 M4 内核,EXC_RETURN 的 bit4 也是有意义的:
     当 bit4 = 1 时,8 个寄存器自动入栈,还有 8 个寄存器需要手动入栈
     当 bit4 = 0 时,18个浮点寄存器 + 8 个寄存器自动入栈,还有 16 个浮点寄存器 +8 个寄存器需要手动入栈。
    */

    *--p_stk = (CPU_STK)0xFFFFFFFDUL;

#if (OS_CPU_ARM_FP_EN == DEF_ENABLED)
    if ((opt & OS_TASK_OPT_SAVE_FP) != (INT16U)0) {
        *--p_stk = (CPU_STK)0x02000000u;                        /* FPSCR                                                  */
                                                                /* Initialize S0-S31 floating point registers             */
        *--p_stk = (CPU_STK)0x41F80000u;                        /* S31                                                    */
        *--p_stk = (CPU_STK)0x41F00000u;                        /* S30                                                    */
        *--p_stk = (CPU_STK)0x41E80000u;                        /* S29                                                    */
        *--p_stk = (CPU_STK)0x41E00000u;                        /* S28                                                    */
        *--p_stk = (CPU_STK)0x41D80000u;                        /* S27                                                    */
        *--p_stk = (CPU_STK)0x41D00000u;                        /* S26                                                    */
        *--p_stk = (CPU_STK)0x41C80000u;                        /* S25                                                    */
        *--p_stk = (CPU_STK)0x41C00000u;                        /* S24                                                    */
        *--p_stk = (CPU_STK)0x41B80000u;                        /* S23                                                    */
        *--p_stk = (CPU_STK)0x41B00000u;                        /* S22                                                    */
        *--p_stk = (CPU_STK)0x41A80000u;                        /* S21                                                    */
        *--p_stk = (CPU_STK)0x41A00000u;                        /* S20                                                    */
        *--p_stk = (CPU_STK)0x41980000u;                        /* S19                                                    */
        *--p_stk = (CPU_STK)0x41900000u;                        /* S18                                                    */
        *--p_stk = (CPU_STK)0x41880000u;                        /* S17                                                    */
        *--p_stk = (CPU_STK)0x41800000u;                        /* S16                                                    */
        *--p_stk = (CPU_STK)0x41700000u;                        /* S15                                                    */
        *--p_stk = (CPU_STK)0x41600000u;                        /* S14                                                    */
        *--p_stk = (CPU_STK)0x41500000u;                        /* S13                                                    */
        *--p_stk = (CPU_STK)0x41400000u;                        /* S12                                                    */
        *--p_stk = (CPU_STK)0x41300000u;                        /* S11                                                    */
        *--p_stk = (CPU_STK)0x41200000u;                        /* S10                                                    */
        *--p_stk = (CPU_STK)0x41100000u;                        /* S9                                                     */
        *--p_stk = (CPU_STK)0x41000000u;                        /* S8                                                     */
        *--p_stk = (CPU_STK)0x40E00000u;                        /* S7                                                     */
        *--p_stk = (CPU_STK)0x40C00000u;                        /* S6                                                     */
        *--p_stk = (CPU_STK)0x40A00000u;                        /* S5                                                     */
        *--p_stk = (CPU_STK)0x40800000u;                        /* S4                                                     */
        *--p_stk = (CPU_STK)0x40400000u;                        /* S3                                                     */
        *--p_stk = (CPU_STK)0x40000000u;                        /* S2                                                     */
        *--p_stk = (CPU_STK)0x3F800000u;                        /* S1                                                     */
        *--p_stk = (CPU_STK)0x00000000u;                        /* S0                                                     */
    }
#endif

    return (p_stk);
}

2。2   修改函数 PendSV_Handler

这里需要修改两个汇编函数 PendSV_Handler 和  OS_CPU_PendSVHandler_nosave

此函数在 os_cpu_a.asm 文件中

修改如下:


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

;屏蔽原有逻辑
;    SUBS    R0, R0, #0x20                                       ; Save remaining regs r4-11 on process stack
;    STM     R0, {R4-R11}


;*************************添加新增逻辑***********************************************************************************

;入栈:通过检测 EXC_RETURN(LR)的 bit4 来看这个任务是否使用了浮点寄存器,如果使用了(bit4 == 0)需要将剩余的 16 个浮点寄存器入栈
    TST     LR, #0x10
    IT EQ
    VSTMDBEQ R0!, {S16-S31} 

;将 EXC_RETURN 入栈
    MOV     R3, LR
    STMDB   R0!,{R3-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}

    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
;    ADDS    R0, R0, #0x20
    



;*************************添加新增逻辑***********************************************************************************

; 出栈
    LDMIA   R0!,{R3-R11}
    MOV     LR, R3

;出栈:通过检测 EXC_RETURN(LR)的 bit4 来看这个任务是否使用了浮点寄存器,如果使用了(bit4 == 0)需要将剩余的 16 个浮点寄存器出栈
    TST     LR, #0x10
    IT EQ
    VLDMIAEQ R0!, {S16-S31}


;*************************添加结束***************************************************************************************

    
    MSR     PSP, R0                                             ; Load PSP with new process SP
    
;屏蔽原有逻辑    
;    ORR     LR, LR, #0xF4                                       ; Ensure exception return uses process stack

    CPSIE   I
    BX      LR                                                  ; Exception return will restore remaining context

    END

参考:

1。使用STM32F4浮点运算(FPU)功能开启+使用DSP库

2。《安富莱_STM32-V5开发板_μCOS-III教程(V1.3)》

3。《ARM Cortex-M3与Cortex-M4权威指南》

  • 19
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
STM32F407移植uCos II是将uCos II实时操作系统移植到STM32F407系列微控制器上的过程。移植uCos II可以使嵌入式系统具备多任务处理和实时性能,提高系统的可靠性和灵活性。 首先,需要在STM32F407上配置系统时钟和外设,以便与uCos II进行通信。可以使用寄存器级别的编程技术来配置时钟和外设,建立与uCos内核的通信。 其次,需要为STM32F407编写适配器代码,以实现与uCos II内核的接口。适配器代码主要包括时钟中断处理函数、任务切换函数、内存管理函数等。这些函数需要根据具体的硬件特性来编写。 然后,需要对STM32F407上的存储器进行划分,为uCos II的内核堆栈、任务堆栈和其他数据结构分配内存空间。 接下来,需要将uCos II的源代码添加到项目中,并根据需要进行配置和编译。可以根据系统的需求选择合适的uCos II配置选项,并根据实际情况配置任务、消息队列、信号量等。 最后,在主函数中初始化uCos II内核,并创建任务。可以根据系统的需要创建不同的任务,并设置其优先级和堆栈大小。 总的来说,STM32F407移植uCos II的过程包括配置系统时钟和外设、编写适配器代码、分配存储器空间、添加源代码、配置和编译、初始化内核和创建任务等步骤。通过这些步骤,可以成功地将uCos II移植到STM32F407系列微控制器上,实现多任务处理和实时性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值