打印
[STM32F4]

【STM32F469I试用】3. uC/OS-II移植及FPU开启

[复制链接]
3428|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 zhangbin_abc 于 2015-12-27 17:11 编辑

     STM32F469具有cortex-M4架构,而cortex-M4相对于cortex-M3一个显著的不同就是多了一个浮点单元FPU,其他的二者差不多都一样,所以如果你以前在STM32的cortex-M3架构的微控制器上移植过uC/OS-II操作系统,而你的系统中又不开启FPU的话,以前的程序应该可以直接在STM32F469就能跑了。但是FPU对于浮点计算具有很大的优势,如果要开启该功能单元,则需要在移植的时候进行一些文件的修改。             下面将在上一篇建立的工程模板的基础上,结合FPU详细说明uC/OS-II STM32F469上的移植。

一、软件准备
uC/OS-II的源码可以在官网上自行下载。

二、工程文件组织
在上一篇的工程模板中,将uC/OS-II的源码加入其中,组织结构如图所示。在uC/OS-II的Source文件夹中的os_dbg_r.c和ucos_ii.c文件不要添加到工程中,否则编译链接时会提示变量重复定义。

三、与移植相关的文件的修改
1、时钟节拍修改
1)初始化Systick: 在这里使用STM32的库函数进行实现,因此将os_cpu_c.c文件中的void  OS_CPU_SysTickInit (INT32U  cnts)函数注释掉,而采用core_cm4.h文件中的__STATIC_INLINE uint32_t SysTick_Config(uint32_t ticks)函数,实现如下:
static void OS_CPU_SysTickInit(void)
{
             SysTick_Config(SysCoreClock/OS_TICKS_PER_SEC);
}

这里SysCoreClock是系统的内核时钟,即180MHz,OS_TICKS_PER_SEC是每秒uC/OS-ii的时钟节拍数,在os_cfg.h文件中定义,这里设置为1000,即1ms产生一次节拍中断。
2)Systick中断服务程序的修改: 首先,在TM32F469的stm32f4xx_it.c和uC/OS-II的os_cpu_c.c文件中均定义了void SysTick_Handler (void) 函数,造成重复定义,所以这里将os_cpu_c.c文件中的相关函数注释掉,而在stm32f4xx_it.c文件中进行实现:
void SysTick_Handler(void)
{
                static uint32_t i;
          OSIntEnter();                /* enter the interrupt, */
                OSTimeTick();
                OSIntExit();
        
                i++;
                if(i==1000){
                        LED1_ON();
                }
                else if(i==2000){
                        LED1_OFF();
                        i = 0;
                }
}
在这里我添加了一个LED的闪烁代码,指示系统在正常运行。
2、os_cpu_c.c文件中系统堆栈初始化函数的修改
      由于在uC/OS-II任务的上下文切换时需要进入PendSV异常,这就需要对一些寄存器进行保存。
      当使用FPU时,将涉及到S0~S31寄存器组。在系统进入异常处理服务时,FPSCR、S0~S15、xPSR、PC、LR、R12、R3~R0这几个寄存器是被自动入栈的。入栈顺序如下图所示。



剩余的S16~S31和R4~R11寄存器需要手动入栈。根据以上情况,对OSTaskStkInit函数修改如下:
OS_STK *OSTaskStkInit (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT16U opt)
{
    OS_STK *stk;


    (void)opt;                                   /* 'opt' is not used, prevent warning                 */
    stk       = ptos + 1;                            /* Load stack pointer                                 */
                stk                                =        (OS_STK *)((OS_STK)(stk)&0xFFFFFFF8u);
        
#if (__FPU_PRESENT==1)&&(__FPU_USED==1)        
        *(--stk) = (INT32U)0x00000000L; //No Name Register  
        *(--stk) = (INT32U)0x00001000L; //FPSCR
        *(--stk) = (INT32U)0x00000015L; //s15
        *(--stk) = (INT32U)0x00000014L; //s14
        *(--stk) = (INT32U)0x00000013L; //s13
        *(--stk) = (INT32U)0x00000012L; //s12
        *(--stk) = (INT32U)0x00000011L; //s11
        *(--stk) = (INT32U)0x00000010L; //s10
        *(--stk) = (INT32U)0x00000009L; //s9
        *(--stk) = (INT32U)0x00000008L; //s8
        *(--stk) = (INT32U)0x00000007L; //s7
        *(--stk) = (INT32U)0x00000006L; //s6
        *(--stk) = (INT32U)0x00000005L; //s5
        *(--stk) = (INT32U)0x00000004L; //s4
        *(--stk) = (INT32U)0x00000003L; //s3
        *(--stk) = (INT32U)0x00000002L; //s2
        *(--stk) = (INT32U)0x00000001L; //s1
        *(--stk) = (INT32U)0x00000000L; //s0
#endif     

                                            /* 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                                      */

#if (__FPU_PRESENT==1)&&(__FPU_USED==1)        
        *(--stk) = (INT32U)0x00000031L; //s31
        *(--stk) = (INT32U)0x00000030L; //s30
        *(--stk) = (INT32U)0x00000029L; //s29
        *(--stk) = (INT32U)0x00000028L; //s28
        *(--stk) = (INT32U)0x00000027L; //s27
        *(--stk) = (INT32U)0x00000026L; //s26        
        *(--stk) = (INT32U)0x00000025L; //s25
        *(--stk) = (INT32U)0x00000024L; //s24
        *(--stk) = (INT32U)0x00000023L; //s23
        *(--stk) = (INT32U)0x00000022L; //s22
        *(--stk) = (INT32U)0x00000021L; //s21
        *(--stk) = (INT32U)0x00000020L; //s20
        *(--stk) = (INT32U)0x00000019L; //s19
        *(--stk) = (INT32U)0x00000018L; //s18
        *(--stk) = (INT32U)0x00000017L; //s17
        *(--stk) = (INT32U)0x00000016L; //s16
#endif

                                                 /* 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                                                 */

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

    return (stk);
}
这里需要注意的是在程序的结尾处一定要添加  *(--stk)   =   (INT32U)0xFFFFFFFDUL; 这个是对EXC_RETURN特殊寄存器进行堆栈初始化。
3、os_cpu_a.asm文件修改
      在该文件中主要是对PendSV异常中断服务程序进行修改。由于在STM32F469的启动汇编文件startup_stm32f469_479xx.s中也有相应函数的声明,所以在这两个文件中的文件名应该修改为相同,否则编译会出错,这里统一修改为PendSV_Handler。在PendSV_Handler函数中主要实现内容如下:
PendSV_Handler
    CPSID   I                                                   ; Prevent interruption during context switch
    MRS     R0, PSP                                             ; PSP is process stack pointer
    CBZ     R0, PendSV_Handler_Nosave                                    ; Skip register save the first time
        
        ;Is the task using the FPU context? If so, push high vfp registers.
        TST         R14, #0x10
        IT                 EQ
        VSTMDBEQ R0!, {S16-S31}
        
        MOV                R3, R14
        STMDB        R0!, {R3-R11}
        
    ;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
这里需要注意第7~12行的代码,这是在Cortex-m4中开启FPU所特有的,具体分析如下:
上面的意思是说进入异常服务程序后,LR寄存器(即R14寄存器)的值会被自动更新到EXC_RETURN寄存器中,该寄存器的具体位段描述如下:

其中bit4的值决定了我们要将哪些寄存器进行入栈处理,如果为0,说明使用了FPU,需要对S16~S31进行入栈,然后将R4~R11入栈,与此同时,还需将EXC_RETURN入栈,这是与堆栈初始化函数中最后一句相对应的。
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]
        
        LDMIA        R0!, {R3-R11}
        MOV                R14, R3
                                           ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr;
    ;LDM     R0, {R4-R11}                                        ; Restore r4-11 from new process stack
    ;ADDS    R0, R0, #0x20

        ;Is the task using the FPU context? If so, push high vfp registers.
        TST         R14, #0x10
        IT                 EQ
        VLDMIAEQ R0!, {S16-S31}
四、移植测试
       最后编写了一个测试程序,对uC/OS-II操作系统能否正常运行进行测试。如果需要开启FPU,还需要在系统初始化中进行开启操作,而在system_stm32f4xx.c文件中的系统上电初始化函数SystemInit中,有相应的代码如下:
 /* FPU settings ------------------------------------------------------------*/
  #if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
    SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2));  /* set CP10 and CP11 Full Access */
  #endif
所以必须将__FPU_PRESENT__FPU_USED定义为1,而__FPU_PRESENT在stm32f4xx.h中已经定义为1,所以需要在options for Target中的C/C++选项卡中添加__FPU_USED的宏定义:

同时在Target选项卡的Floating Point Hardware中选择使用单精度,即Use Single Precision。

测试部分主要代码如下:
void App_TaskCreat(void)
{
                u3_printf("create multitask...\r\n");
        
          OSTaskCreateExt(App_Task1,(void *)0,&AppTask1Stk[APP_TASK_1_STK_SIZE-1],APP_TASK_1_PRIO,
                          APP_TASK_1_PRIO,&AppTask1Stk[0],APP_TASK_1_STK_SIZE,(void *)0,(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));
               
                OSTaskCreateExt(App_Task2,(void *)0,&AppTask2Stk[APP_TASK_2_STK_SIZE-1],APP_TASK_2_PRIO,
                          APP_TASK_2_PRIO,&AppTask2Stk[0],APP_TASK_2_STK_SIZE,(void *)0,(OS_TASK_OPT_STK_CLR | OS_TASK_OPT_STK_CHK));
}

static void App_Task1(void *p_arg)
{
                static float float_num=0.00f;
                (void)p_arg;
               
                while(1){
                                float_num += 0.01f;
                          u3_printf("float_num:%.4f\r\n",float_num);
                                u3_printf("Task1 is running...\r\n");
                                OSTimeDlyHMSM(0,0,1,0);
                }
}

static void App_Task2(void *p_arg)
{
                (void)p_arg;
                while(1){
                                u3_printf("Task2 is running...\r\n");
                                OSTimeDlyHMSM(0,0,1,0);
                }
}
编译链接后下载运行,结果如下图所示,说明系统移植成功。

在程序中float_num += 0.01f;语句前面设置断点,观察反汇编代码,如下:

可以看到程序将要执行VLDR、VADD、VSTR等汇编指令,而这些指令都是单精度浮点指令。而左边窗口中FPU状态和控制寄存器FPSCR中的IXC位为1

从上表中可以看到,IXC位置1表示发生了浮点异常。以上实验现象均说明系统确实开启了FPU。





沙发
zhangbin_abc|  楼主 | 2015-12-27 17:14 | 只看该作者
板凳
绝影| | 2016-8-23 00:26 | 只看该作者
楼主 您好 能共享一下工程文件吗,我移植遇到点问题,参考一下。

使用特权

评论回复
地板
desertsailor| | 2017-8-18 13:59 | 只看该作者
在保存FPSCR之前为何要增加一个No Name Register  ?

使用特权

评论回复
5
pjzmj2012| | 2020-3-5 16:43 | 只看该作者
本帖最后由 pjzmj2012 于 2020-3-5 16:46 编辑

0.01后面为什么要加个 f ??
float类型的字面常量,后面需要加上f或者F来表示是一个单精度浮点数。只所以要这样写,是因为默认的浮点数常量都是double类型。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

8

主题

84

帖子

1

粉丝