STM32
直播中

岳臻俊

7年用户 1014经验值
私信 关注
[问答]

如何去实现基于STM32F767的UCOS-III系统移植呢

如何去实现基于STM32F767的UCOS-III系统移植呢?有哪些移植步骤?

回帖(2)

严晶鲁

2021-11-26 09:38:48
(一)移植前的准备

1.HAL库基本工程模板

新建一个工程模块,其中包含LED驱动和串口驱动程序即可,用于验证UCOS-III系统能够正常工作。
2.UCOS-III源码准备

去Micrium官网下载最新的UCOSIII源码,下载地址:Micrium官网下载地址,没有注册过的用户需要注册一下,我自己注册的过程都是泪。由于我选择的是正点原子F7的开发板,所以在官网上选择合适源码时就选择F7的。具体版本选择下图中19年2月6号的版本:




下载完成之后发现,发现源码文件夹中东西很多大部分都是和网络移植相关的,目前我们只移植最新版本的UCOSIII,所以这些文件夹都不需要去看。进入源码目录下的uCOS-III文件夹,发现里面只有针对IAR的编译器的文件,难道MDK就凉凉了吗?凡是先不要慌,虽然编译器不同,但是实现的功能就是相同的,并不会影响到移植后操作系统的运行。
UCOSIII的移植

(一)源码文件的摘选

1.在准备好的工程文件夹下新建一个UCOS-III目录,用于存放和UCOSIII相关的所有代码。在UCOS-III目录下新建如下目录,用于UCOS源码的分类。




(1)将源码目录Micrium_STM32F746G-DISCO_CryptoMicriumSoftwareuC-CPU下的文件全部拷贝到我们创建的uC-CPU目录下,为了减少文件夹的个数,我把ARM-Cortex-MARMv7-M目录下和ARM-Cortex-MARMv7-MARM目录下的文件都移到了自己创建的uC-CPU目录下了。这里我要说明下一下IAR和ARM目录下的文件,除了汇编文件有些不同以外,头文件内容都是相同的,这边选择ARM目录下的汇编文件还是IAR目录下的汇编文件都行,因为我们都是要修改成MDK支持的形式的。
(2)将源码目录Micrium_STM32F746G-DISCO_CryptoMicriumSoftwareuC-LIB文件夹下的文件全部拷贝到自己创建的uC-LIB目录下,这边我直接把MicriumSoftwareuC-LIBPortsARM-Cortex-M4RealView目录下的文件和其他文件放在了一个文件夹下,具体如下图所示:




由于我们使用的是MDK编译,所以这边选择的是RealView目录下的汇编文件,具体什么原因的话,网上有很多解释,我就不复制他们的解释了。
(3)将Micrium_STM32F746G-DISCO_CryptoMicriumSoftwareuCOS-III目录下的文件全部拷贝到自己创建uCOS-III目录下。点开Ports的目录,发现最后只有一个IAR文件下,这边我们先不用关注,只需将文件都拷贝过来,具体内容如下图所示:









(4)将源码例程目录Micrium_STM32F746G-DISCO_CryptoSTSTM32F746G_DiscoCrypto下的部分文件和OS3下的部分文件拷贝到自己创建的UCOS-CONFIG文件夹下。具体文件如下图所示:




在源码例程文件中,发现很多都是网络相关的移植代码,所以没有拷贝过来。但是有一个clk_cfg.h很明显不是和网络相关的,为什么不移植过来呢?从名字上看就是和时钟相关的,难道说不需要时钟配置吗?时钟当然是需要配置的,但是裸机工程中时钟已经配置OK了,这边和时钟相关的文件也就不需要了,后面操作系统的时钟需要自己通过HAL库配置,这边就不详细说明了。
(5)将Micrium_STM32F746G-DISCO_CryptoSTBSPSTM32F746G_Disco目录下的bsp_cpu.c文件拷贝到自己创建的UCOS-BSP目录下,并且新建一个bsp_cpu.h头文件(用于包含操作系统的头文件)。其他文件都不需要拷贝,大多是和芯片外设相关的初始化。
(二)文件添加

(1)打开MDK工程,将拷贝的文件添加至工程当中,具体内容如下图所示:
























以及头文件路径添加,如下图所示:




上述文件都添加完成之后,点击编译,提示下图错误:




出现unknown opcode什么的错误,而且出现在os_cpu_a.asm文件中,那就打开这个汇编文件看一下,是什么问题,发现PUBLIC关键字都没有标蓝,那肯定是由于这个关键字不是MDK的关键字,所以讲PUBLIC修改为MDK所用的关键字EXPORT,具体EXPORT这个关键字怎么来的,我是查看了其他版本的UCOS在STM32的移植,看到用的是这个关键字,就直接抄过来了。还有错误与FPU相关,如下图所示:

#ifdef __ARMVFP__
    EXPORT  OS_CPU_FP_Reg_Push
    EXPORT  OS_CPU_FP_Reg_Pop
#endif


查看了正点原子的移植手册,发现上面说到

  Cortex-M7内核中有个Lazy Stacking的功能,如果使用FPU功能的话就需要关闭这个功能,需要修改startup_stm32f767xx.s汇编文件,在里面关闭这个功能。
具体代码如下所示:

IF {FPU} != "SoftVFP"
                                        ; 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
               
                                            ; 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


在startup_stm32f767xx.s中就如下图所示:




修改完这些,接下去就解决… error: A1163E: Unknown opcode ARMVFP , expecting opcode or Macro 这个错误。将原先的判断语句修改为如下语句:

IF {FPU} != "SoftVFP"
    EXPORT  OS_CPU_FP_Reg_Push
    EXPORT  OS_CPU_FP_Reg_Pop
        ENDIF


        IF {FPU} != "SoftVFP"
OS_CPU_FP_Reg_Push
    MRS     R1, PSP                                             ; PSP is process stack pointer
    CBZ     R1, OS_CPU_FP_nosave                                ; Skip FP register save the first time


    VSTMDB  R0!, {S16-S31}
    LDR     R1, =OSTCBCurPtr
    LDR     R2, [R1]
    STR     R0, [R2]
OS_CPU_FP_nosave
    BX      LR
        ENDIF


        IF {FPU} != "SoftVFP"
OS_CPU_FP_Reg_Pop
    VLDMIA  R0!, {S16-S31}
    LDR     R1, =OSTCBHighRdyPtr
    LDR     R2, [R1]
    STR     R0, [R2]
    BX      LR
        ENDIF


修改完成之后,再次编译,依旧存在 error: A1163E: Unknown opcode RSEG , expecting opcode or Macro 的错误,这主要是由于编译器关键字的问题,所以这里直接借鉴其他其他版本在MDK上的移植。具体修改代码如下:


    PRESERVE8
    THUMB


    AREA CODE, CODE, READONLY


再次编译,发现这个汇编文件中还有一个警告: warning: A1581W: Added 2 bytes of padding at address 0x112,这是提示地址没对齐,那就在末尾添加一个对齐指令,具体代码如下:


OS_CPU_PendSVHandler
    CPSID   I                                                   ; Cortex-M7 errata notice. See Note #5
    MOV32   R2, OS_KA_BASEPRI_Boundary                          ; Set BASEPRI priority level required for exception preemption
    LDR     R1, [R2]
    MSR     BASEPRI, R1
    DSB
    ISB
    CPSIE   I


    MRS     R0, PSP                                             ; PSP is process stack pointer
    STMFD   R0!, {R4-R11, R14}                                  ; Save remaining regs r4-11, R14 on process stack


    MOV32   R5, OSTCBCurPtr                                     ; OSTCBCurPtr->StkPtr = SP;
    LDR     R1, [R5]
    STR     R0, [R1]                                            ; R0 is SP of process being switched out


                                                                ; At this point, entire context of process has been saved
    MOV     R4, LR                                              ; Save LR exc_return value
    BL      OSTaskSwHook                                        ; Call OSTaskSwHook() for FPU Push & Pop


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


    MOV32   R1, OSTCBHighRdyPtr                                 ; OSTCBCurPtr = OSTCBHighRdyPtr;
    LDR     R2, [R1]
    STR     R2, [R5]


    ORR     LR,  R4, #0x04                                      ; Ensure exception return uses process stack
    LDR     R0,  [R2]                                           ; R0 is new process SP; SP = OSTCBHighRdyPtr->StkPtr;
    LDMFD   R0!, {R4-R11, R14}                                  ; Restore r4-11, R14 from new process stack
    MSR     PSP, R0                                             ; Load PSP with new process SP


    MOV32   R2, #0                                              ; Restore BASEPRI priority level to 0
    MSR     BASEPRI, R2
    BX      LR                                                  ; Exception return will restore remaining context


        ALIGN


    END
再次编译,os_cpu_a.asm文件内没有错误,还有两个小错误:






将os_cpu_c.c中os.h的头文件路径修改意见,将bsp_cpu.c中的bsp_clk.h头文件注释掉,再次编译。报错信息如下图:





发现报错信息都在bsp_cpu.c当中,而且都是和时钟频率相关的,打开源码例程中的bsp_clk.c文件,查看BSP_ClkFreqGet()函数,发现里面调用的函数不就是HAL库提供的吗,那我们直接将bsp_cpu.c中的时钟获取函数修改为HAL库提供的函数,不就OK了吗。修改后的代码如下所示:

#if (CPU_CFG_TS_TMR_EN == DEF_ENABLED)
void  CPU_TS_TmrInit (void)
{
    CPU_INT32U  fclk_freq;
    CPU_INT32U  reg_val;


                                                                /* ---- DWT WRITE ACCESS UNLOCK (CORTEX-M7 ONLY!!) ---- */
    reg_val = CPU_BSP_REG_DWT_LSR;                              /* Read lock status register.                           */
    if ((reg_val & CPU_BSP_BIT_DWT_LSR_SLI) != 0) {             /* Check if Software lock control mechanism exits       */
        if ((reg_val & CPU_BSP_BIT_DWT_LSR_SLK) != 0) {         /* Check if DWT access needs to be unlocked             */
            CPU_BSP_REG_DWT_LAR = CPU_BSP_DWT_LAR_KEY;          /* Unlock DWT write access.                             */
        }
    }


    fclk_freq =  HAL_RCC_GetHCLKFreq();


    CPU_BSP_REG_DEMCR  |= DEF_BIT_24;                           /* Set DEM_CR_TRCENA                                    */
    CPU_BSP_REG_DWT_CR |= DEF_BIT_00;                           /* Set DWT_CR_CYCCNTENA                                 */


    CPU_TS_TmrFreqSet((CPU_TS_TMR_FREQ)fclk_freq);
}
#endif


#if (CPU_CFG_TS_32_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS32_to_uSec (CPU_TS32  ts_cnts)
{
    CPU_INT64U  ts_us;
    CPU_INT64U  fclk_freq;




    fclk_freq =  HAL_RCC_GetHCLKFreq();
    ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);


    return (ts_us);
}
#endif




#if (CPU_CFG_TS_64_EN == DEF_ENABLED)
CPU_INT64U  CPU_TS64_to_uSec (CPU_TS64  ts_cnts)
{
    CPU_INT64U  ts_us;
    CPU_INT64U  fclk_freq;




    fclk_freq = HAL_RCC_GetHCLKFreq();
    ts_us     = ts_cnts / (fclk_freq / DEF_TIME_NBR_uS_PER_SEC);


    return (ts_us);
}
#endif


修改完之后,再次编译,发现了很多重复定义的错误,都适合__dbg_ucos-iii.c文件相关,那就直接移除掉这个文件,再编译看看结果如何。
编译之后发现只有一个未定义的错误,打开os_cpu.h头文件,发现了下面这行代码:
#define  OS_TASK_SW_SYNC()          __ISB()
明明定义了,却还是提示未定义的错误,那我搜索了一下__ISB()函数,在cmsis_armcc.h中明确定义了,难道没添加头文件引起的吗,那就添加头文件再编译一下,报错信息更多了,那很明显不是这个问题了。看到cmsis_armcc.h中有这一行代码:

#define __ISB() do {
                   __schedule_barrier();
                   __i***(0xF);
                   __schedule_barrier();
                } while (0U)


那就直接修改成


#define  OS_TASK_SW_SYNC()          __i***(0xF)


这样编译倒是没问题了。但是系统运行起来有没有问题就不知道,先这么做吧,这个头文件中还有一行代码也需要修改:


#ifdef        __ARMVFP__       
#define  OS_CPU_ARM_FP_EN              1u
#else
#define  OS_CPU_ARM_FP_EN              0u
#endif


修改为


#ifdef __TARGET_FPU_SOFTVFP
#define  OS_CPU_ARM_FP_EN              1u
#else
#define  OS_CPU_ARM_FP_EN              0u
#endif


编译后无错误。现在这系统能够正常运行起来了吗?讲实话我也不太清楚,那就测试一下吧。
举报

郭舒静

2021-11-26 09:38:51
(三)测试系统

拷贝如下代码至main.c中。

#include  
#include  
#include  
#include  
#include  
#include  


#include  


#include  
#include  
#include  
#include  
#include  
#include  


#include  
#include  "bsp_cpu.h"


//任务优先级
#define START_TASK_PRIO                2
//任务堆栈大小       
#define START_STK_SIZE                 512
//任务控制块
OS_TCB StartTaskTCB;
//任务堆栈       
CPU_STK START_TASK_STK[START_STK_SIZE];
//任务函数
void start_task(void *p_arg);


//任务优先级
#define LED0_TASK_PRIO                3
//任务堆栈大小       
#define LED0_STK_SIZE                 128
//任务控制块
OS_TCB Led0TaskTCB;
//任务堆栈       
CPU_STK LED0_TASK_STK[LED0_STK_SIZE];
void led0_task(void *p_arg);


//任务优先级
#define LED1_TASK_PRIO                4
//任务堆栈大小       
#define LED1_STK_SIZE       128
//任务控制块
OS_TCB Led1TaskTCB;
//任务堆栈       
CPU_STK LED1_TASK_STK[LED1_STK_SIZE];
//任务函数
void led1_task(void *p_arg);


//任务优先级
#define FLOAT_TASK_PRIO                5
//任务堆栈大小
#define FLOAT_STK_SIZE                256
//任务控制块
OS_TCB        FloatTaskTCB;
//任务堆栈
CPU_STK        FLOAT_TASK_STK[FLOAT_STK_SIZE];
//任务函数
void float_task(void *p_arg);


/*****************************************************************************************************************************************
* Function Name: main
* Input:         None
* Output:        None
* Returns:       None
* Description:   主函数
* Note:          None
*****************************************************************************************************************************************/
int main(void)
{
        OS_ERR err;
        CPU_SR_ALLOC();
       
        HW_Init();
        MT_Init();
       
        OSInit(&err);                            //初始化UCOSIII
        CPU_CRITICAL_ENTER();            //进入临界区
        //创建开始任务
        OSTaskCreate((OS_TCB         * )&StartTaskTCB,                //任务控制块
                                 (CPU_CHAR        * )"start task",                 //任务名字
                 (OS_TASK_PTR )start_task,                         //任务函数
                 (void                * )0,                                        //传递给任务函数的参数
                 (OS_PRIO          )START_TASK_PRIO,     //任务优先级
                 (CPU_STK   * )&START_TASK_STK[0],        //任务堆栈基地址
                 (CPU_STK_SIZE)START_STK_SIZE/10,        //任务堆栈深度限位
                 (CPU_STK_SIZE)START_STK_SIZE,                //任务堆栈大小
                 (OS_MSG_QTY  )0,                                        //任务内部消息队列能够接收的最大消息数目,为0时禁止接收消息
                 (OS_TICK          )0,                                        //当使能时间片轮转时的时间片长度,为0时为默认长度,
                 (void           * )0,                                        //用户补充的存储区
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP, //任务选项,为了保险起见,所有任务都保存浮点寄存器的值
                 (OS_ERR         * )&err);                                //存放该函数错误时的返回值
        CPU_CRITICAL_EXIT();        //退出临界区         
        OSStart(&err);      //开启UCOSIII
       
        while(1)
        {
               
               
        }
}
//开始任务函数
void start_task(void *p_arg)
{
        OS_ERR err;
        CPU_SR_ALLOC();
        p_arg = p_arg;


        CPU_Init();
#if OS_CFG_STAT_TASK_EN > 0u
   OSStatTaskCPUUsageInit(&err);          //统计任务               
#endif
       
#ifdef CPU_CFG_INT_DIS_MEAS_EN                //如果使能了测量中断关闭时间
    CPU_IntDisMeasMaxCurReset();       
#endif


#if        OS_CFG_SCHED_ROUND_ROBIN_EN  //当使用时间片轮转的时候
         //使能时间片轮转调度功能,设置默认的时间片长度s
        OSSchedRoundRobinCfg(DEF_ENABLED,10,&err);  
#endif               
       
        CPU_CRITICAL_ENTER();        //进入临界区
        //创建LED0任务
        OSTaskCreate((OS_TCB         * )&Led0TaskTCB,               
                                 (CPU_CHAR        * )"led0 task",                
                 (OS_TASK_PTR )led0_task,                        
                 (void                * )0,                                       
                 (OS_PRIO          )LED0_TASK_PRIO,     
                 (CPU_STK   * )&LED0_TASK_STK[0],       
                 (CPU_STK_SIZE)LED0_STK_SIZE/10,       
                 (CPU_STK_SIZE)LED0_STK_SIZE,               
                 (OS_MSG_QTY  )0,                                       
                 (OS_TICK          )0,                                       
                 (void           * )0,                                       
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
                 (OS_ERR         * )&err);                               
                                 
        //创建LED1任务
        OSTaskCreate((OS_TCB         * )&Led1TaskTCB,               
                                 (CPU_CHAR        * )"led1 task",                
                 (OS_TASK_PTR )led1_task,                        
                 (void                * )0,                                       
                 (OS_PRIO          )LED1_TASK_PRIO,            
                 (CPU_STK   * )&LED1_TASK_STK[0],       
                 (CPU_STK_SIZE)LED1_STK_SIZE/10,       
                 (CPU_STK_SIZE)LED1_STK_SIZE,               
                 (OS_MSG_QTY  )0,                                       
                 (OS_TICK          )0,                                       
                 (void           * )0,                               
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
                 (OS_ERR         * )&err);
                                 
        //创建浮点测试任务
        OSTaskCreate((OS_TCB         * )&FloatTaskTCB,               
                                 (CPU_CHAR        * )"float test task",                
                 (OS_TASK_PTR )float_task,                        
                 (void                * )0,                                       
                 (OS_PRIO          )FLOAT_TASK_PRIO,            
                 (CPU_STK   * )&FLOAT_TASK_STK[0],       
                 (CPU_STK_SIZE)FLOAT_STK_SIZE/10,       
                 (CPU_STK_SIZE)FLOAT_STK_SIZE,               
                 (OS_MSG_QTY  )0,                                       
                 (OS_TICK          )0,                                       
                 (void           * )0,                               
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR|OS_OPT_TASK_SAVE_FP,
                 (OS_ERR         * )&err);
                                 
        CPU_CRITICAL_EXIT();        //进入临界区                                 
        OSTaskSuspend((OS_TCB*)&StartTaskTCB,&err);                //挂起开始任务                         
}


//led0任务函数
void led0_task(void *p_arg)
{
        OS_ERR err;
        p_arg = p_arg;
        while(1)
        {
                HW_Led0_On();    //LED0打开
                OSTimeDlyHMSM(0,0,0,200,OS_OPT_TIME_HMSM_STRICT,&err); //延时200ms
                HW_Led0_Off();    //LED0关闭
                OSTimeDlyHMSM(0,0,0,500,OS_OPT_TIME_HMSM_STRICT,&err); //延时500ms
        }
}


//led1任务函数
void led1_task(void *p_arg)
{
        OS_ERR err;
        p_arg = p_arg;
       
        while(1)
        {
                HW_Led1_On();
        HW_Delay_ms(500);//延时500ms
                HW_Led1_Off();
                HW_Delay_ms(500);
                OSTimeDlyHMSM(0,0,0,100,OS_OPT_TIME_HMSM_STRICT,&err);
        }
}


//浮点测试任务
void float_task(void *p_arg)
{
        OS_ERR err;
       
        CPU_SR_ALLOC();
        static double double_num=0.00;
        while(1)
        {
                double_num+=0.01f;
                CPU_CRITICAL_ENTER();        //进入临界区
                printf("double_num的值为: %.4frn",double_num);
                CPU_CRITICAL_EXIT();                //退出临界区
                OSTimeDlyHMSM(0,0,0,20,OS_OPT_TIME_HMSM_STRICT,&err);
        }
}
编译后,烧写进开发板,查看现象:LED灯没亮;那系统肯定没有运行起来,通过Debug看看哪里出问题了,结果发现程序停在了如下地方:





那说明是PendSV中断处理出了问题,看看整个工程中哪里需要调整。os_cpu_a.asm汇编文件中定义了OS_CPU_PendSVHandler函数,这是Micrium官方移植例程中修改了PendSV中断函数的名称,所以这边需要修改回来,然后再编译报该函数的重定义错误。




这边屏蔽掉stm32f7xx_it.c中的PendSV_Handler函数定义,再次编译;没有报错,那就再下载验证一下。结果还是失败了,那就再Debug一下,看看是哪里的问题,发现程序运行之后,开始任务调度之后就挂在空闲任务上。找来找去没发现什么问题,本来都准备放弃了,后来看到操作系统的时间都是通过滴答定时器中断产生的,然后仔细看了一下代码,虽然使用了滴答定时器,但是没有使用中断,那赶紧加上试试,顺便将stm32f7xx_it.h中定义的滴答定时器中断函数屏蔽掉。编译之后没报错,那就在测试一下。终于,LED灯如愿的闪烁起来 ,串口也正常工作起来了,说明操作系统真正运行起来了。

举报

更多回帖

发帖
×
20
完善资料,
赚取积分