目录
2。1 修改函数 CPU_STK *OSTaskStkInit()
现象:
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权威指南》