μC/OS-II+FPU移植至stm32f407方法总结

文章目录

  • 一、前言
  • 二、移植μC/OS-II步骤
    • 1.建立三个文件夹
    • 2.keil的配置
    • 3. 修改源码
      • 1)修改ucos_ii.h文件
      • 2)修改os_cpu_a.asm文件
      • 3)修改 stm32f4xx_it.c文件
      • 4)修改os_cpu_c.c
  • 四、开启FPU
  • 五、关于时钟节拍的设置

一、前言

移植μC/OS-II + FPU到stm32f407方法主要参考了正点原子和 FPU堆栈修改.文章。在不使用浮点运算的情况下,我们无需开启STM32F407的FPU;而频繁地进行浮点运算时,则建议开启硬件PFU,运算的效率将大幅提升,尤其在实时操作系统中,运算的快慢,关系到整个系统的实时性。

在多任务运行时,且在没有针对PFU进行修改ucos源码的情况下,我们调用sprintf进行浮点数格式转换,可能使系统进入HardFault_Handler函数,意味着,程序发生堆栈溢出,程序跑飞。

为此,我们需要针对正点原子提供的移植方法基础上,再进一步针对FPU进行内核的源码修改。

我们针对FPU,主要修改 os_cpu_c.c 的OSTaskStkInit,以及os_cpu_a.asm的 PendSV_Handler和PendSV_Handler_Nosave。

二、移植μC/OS-II步骤

1.建立三个文件夹

μC/OS-II源码可以在官网上下载,但需要登录。为了节省时间,可以直接使用正点原子资料包里的
μC/OS-II源码。

拿到源码后,我们在工程目录需要建立三个文件夹,用来存放μC/OS-II源码文件。

三个文件夹分别为:CONFIG、CORE、PORT。

在CONFIG文件夹中,添加includes.h 和 os_cfg.h。includes.h包含了相关的头文件,os_cfg.h用于配置和裁剪μC/OS-II。
μC/OS-II+FPU移植至stm32f407方法总结_第1张图片
在CORE文件夹中,添加4个文件。
μC/OS-II+FPU移植至stm32f407方法总结_第2张图片
在CORE文件夹中,将内核源码,服务函数源码都添加进来。

μC/OS-II+FPU移植至stm32f407方法总结_第3张图片

2.keil的配置

首先,将源文件添加到工程上。
μC/OS-II+FPU移植至stm32f407方法总结_第4张图片
然后,添加相应的头文件路径。这一步很重要,否则编译不通过。
μC/OS-II+FPU移植至stm32f407方法总结_第5张图片

3. 修改源码

首先,编译一下,你会发现很多错误。接下来,我们需要整理一下源码。

1)修改ucos_ii.h文件

在ucos_ii.h中,我们将#include 屏蔽掉,然后添加#include
μC/OS-II+FPU移植至stm32f407方法总结_第6张图片

2)修改os_cpu_a.asm文件

将OS_CPU_PendSVHandler函数名改为 PendSV_Handler。接着修改内部源码。

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}

#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SUB     R0, R0, #0x40
    VSTM    R0, {D8-D15}
#endif

    LDR     R1, =OSTCBCur        ; OSTCBCur->OSTCBStkPtr = SP;
    LDR     R1, [R1]
    STR     R0, [R1]             ; R0 is SP of process being switched out

将OS_CPU_PendSVHandler_nosave函数名修改为 PendSV_Handler_Nosave。接着修改内部源码。

PendSV_Handler_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->StkPtr;
    
;#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    VLDM    R0, {D8-D15}
    ADD     R0, R0, #0x40
;#endif

    LDM     R0, {R4-R11}         ; Restore r4-11 from new process stack
    ADDS    R0, R0, #0x20

;#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    BIC.W   LR, LR, #0x10
;#endif

    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 

修改函数名字的目的是:为了匹配stm32f407的启动文件里面中断函数名字,避免去修改启动文件。但我们需要将原来的中断服务函数屏蔽掉,不然编译发生错误。

3)修改 stm32f4xx_it.c文件

1)将void PendSV_Handler(void)函数屏蔽,不然重复定义。
2)修改SysTick_Handler函数

void SysTick_Handler(void)
{
  /* USER CODE BEGIN SysTick_IRQn 0 */

  /* USER CODE END SysTick_IRQn 0 */
  HAL_IncTick();
  /* USER CODE BEGIN SysTick_IRQn 1 */
	OSIntEnter(); 			//进入中断
	OSTimeTick(); 			//调用ucos的服务函数
	OSIntExit();			//触发任务切换中断

  /* USER CODE END SysTick_IRQn 1 */
}

提示:每次生成stm32cubemx工程时,记得将void PendSV_Handler(void)函数屏蔽。

4)修改os_cpu_c.c

1)修改OSTaskStkInit函数

OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
	int i = 0;
    OS_STK *stk;
    (void)opt;          /* 'opt' is not used, prevent warning                 */
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)

    stk       = ptos;   /* Load stack pointer                                 */

                        /* Registers stacked as if auto-saved on exception    */

    *(stk)    = (INT32U)0x00000000uL;
    *(--stk)  = (INT32U)0x00000000uL;

    for (i = 0; i < 16; ++i)
    {
        *(--stk) = (INT32U)(0x00000000uL);
    }

    *(--stk)  = (INT32U)0x01000000uL;      // xPSR
    *(--stk)  = (INT32U)task;              // Entry Point
    *(--stk)  = (INT32U)OS_TaskReturn;     // R14 (LR)
    *(--stk)  = (INT32U)0x12121212uL;      // R12
    *(--stk)  = (INT32U)0x03030303uL;      // R3
    *(--stk)  = (INT32U)0x02020202uL;      // R2
    *(--stk)  = (INT32U)0x01010101uL;      // R1
    *(--stk)  = (INT32U)p_arg;             // R0 : argument

                                           // Remaining registers saved on process stack
    *(--stk)  = (INT32U)0x11111111uL;      // R11
    *(--stk)  = (INT32U)0x10101010uL;      // R10
    *(--stk)  = (INT32U)0x09090909uL;      // R9
    *(--stk)  = (INT32U)0x08080808uL;      // R8
    *(--stk)  = (INT32U)0x07070707uL;      // R7
    *(--stk)  = (INT32U)0x06060606uL;      // R6
    *(--stk)  = (INT32U)0x05050505uL;      // R5
    *(--stk)  = (INT32U)0x04040404uL;      // R4

    for (i = 0; i < 16; ++i)
    {
        *(--stk) = (INT32U)(0x00000000uL);
    }

#else

    stk       = ptos;   /* Load stack pointer                                 */

                        /* Registers stacked as if auto-saved on exception    */
    *(stk)    = (INT32U)0x01000000uL;      // xPSR
    *(--stk)  = (INT32U)task;              // Entry Point
    *(--stk)  = (INT32U)OS_TaskReturn;     // R14 (LR)
    *(--stk)  = (INT32U)0x12121212uL;      // R12
    *(--stk)  = (INT32U)0x03030303uL;      // R3
    *(--stk)  = (INT32U)0x02020202uL;      // R2
    *(--stk)  = (INT32U)0x01010101uL;      // R1
    *(--stk)  = (INT32U)p_arg;             // R0 : argument

                                           // Remaining registers saved on process stack
    *(--stk)  = (INT32U)0x11111111uL;      // R11
    *(--stk)  = (INT32U)0x10101010uL;      // R10
    *(--stk)  = (INT32U)0x09090909uL;      // R9
    *(--stk)  = (INT32U)0x08080808uL;      // R8
    *(--stk)  = (INT32U)0x07070707uL;      // R7
    *(--stk)  = (INT32U)0x06060606uL;      // R6
    *(--stk)  = (INT32U)0x05050505uL;      // R5
    *(--stk)  = (INT32U)0x04040404uL;      // R4

#endif

    return (stk);
}

四、开启FPU

第一种是在stm32f407xx.h 添加 #define __FPU_USED 1U
第二种是 设置Option for target–>Target–>floating Point Hardware:Single Precision
还需添加宏,STM32F407xx__FPU_PRESENT=1,__TARGET_FPU_VFP,ARM_MATH_CM4,__CC_ARM,STM32F407xx 在Option for Target

五、关于时钟节拍的设置

	4.1 首先,在stm32f4xx_hal.h中,滴答定时器默认频率为1K,HAL_TICK_FREQ_DEFAULT = HAL_TICK_FREQ_1KHZ (读者可以自行查找,找到它的位置)
		所以,1ms进入一次中断 SysTick_Handler()
	
	4.2 在OSTimeDlyHMSM函数中,它是这样设置时间的:
		 ticks = ((INT32U)hours * 3600uL + (INT32U)minutes * 60uL + (INT32U)seconds) * OS_TICKS_PER_SEC
					+ OS_TICKS_PER_SEC * ((INT32U)ms + 500uL / OS_TICKS_PER_SEC) / 1000uL;
		意味着,我们要设置OS_TICKS_PER_SEC,才能实现准确的延时。
	
		OS_TICKS_PER_SEC 在os_cfg.h,它的意思是 设置1秒的ticks值。在这里,我们设置为1000,即得到1s。
		
		所以,延时的机制如下:
		OSTimeDlyHMSM用来设置一个时间---> 
		这个时间的设置与我们要设置OS_TICKS_PER_SEC有关,系统默认1ms进入一次中断,每次进入中断,都会将计数器减一。
		假如,我们延时1秒,在OSTimeDlyHMSM内部,ticks = 1000,意味着,进入滴答定时器中断1000次,才能延时1秒

你可能感兴趣的:(stm32,ucosii,stm32f407)