zoukankan      html  css  js  c++  java
  • 【STM32F407】第4章 RTX5操作系统移植(MDK AC5)

    论坛原始地址(持续更新):http://www.armbbs.cn/forum.php?mod=viewthread&tid=93149

    第4章   RTX5操作系统移植(MDK AC5)

    本章教程为大家讲解RTX5内核的AC5编译器移植。

    4.1 初学者重要提示

    4.2 移植RTX5内核整体说明

    4.3 了解RTX5内核模板框架设计

    4.4 第1步,安装MDK软件包版本

    4.5 第2步,准备一个工程模板

    4.6 第3步,添加RTX5源码

    4.7 第4步,配置文件bsp.c

    4.8 第5步,更新bsp_timer.c和bsp.h文件

    4.9 第6步,修改文件stm32f4xx_it.c

    4.10 第7步,添加头文件的汇总文件includes.h

    4.11 第8步,HAL库时间基准stm32f4xx_hal_timbase_tim.c

    4.12 第9步,添加BSP驱动文件bsp_dwt.c

    4.13 第10步,创建应用任务(重要,注意启动任务)

    4.14 常见移植错误总结

    4.15 实验例程

    4.16 总结

    4.1   初学者重要提示

    1.   当前RTX5可以移植到GCC,MDK和IAR三大平台,考虑到仅MDK平台下有RTX5的调试组件,我们这里仅提供了MDK的移植说明。
    2.   STM32H7使用MDK RTE环境添加RTX5,需要强制运行一次STM32CubeMX,因为H7已经没有配套RTE经典添加方式,而STM32F4是支持经典方式的,所以无需运行STM32CubeMX。

    4.2   移植RXT5内核整体说明

    移植之前,有必要对移植过程有个整体的认识:

    •   第1步,准备一个工程模板。
    •   第2步,移植RTX5。
    •   第3步,处理HAL库时间基准,MPU配置等。
    •   第4步,创建应用。

    总的来说,这4步就可以完成移植, 下面将STM32F4的移植步骤和注意事项为大家做个说明。

    4.3   了解RTX5内核模板框架设计

    移植RTX5前,我们优先了解下移植好的RTX5内核模板,方面大家后面移植:

     

    框图如下:

     

    4.4   第1步,安装指定的MDK软件包版本

    移植新版RTX5需要大家下载当前最新的MDK软件包版本(如果有最新版,推荐大家用最新版):

    •   CMSIS 软件包使用当前最新的:V5.7.0
    •   STM32H7使用当前最新的:V2.6.0
    •   STM32F4使用当前最新的:V2.6.0
    •   STM32CubeMX使用当前最新的:V6.0.x
    •   ARM_Compiler使用当前最新的:V1.6.1

    这些软件包的安装在STM32F4用户手册的第2章2.3小节有详细说明。

    http://www.armbbs.cn/forum.php?mod=viewthread&tid=93255

    •   所有这些软件包汇总下载地址:

    http://www.armbbs.cn/forum.php?mod=viewthread&tid=100086

    不管以后MDK的软件包版本如何升级,当前的软件包版本和以后的新版是可以同时安装的,也就是说可以安装多个不同版本,在这里可以选择指定版本:

     

    4.5   第2步,准备一个工程模板

    首先准备好一个简单的裸机工程模板,已经为大家做好:V5-400_Base Template,准备好的工程模板如下图所示(大家也可以制作其它任意的工程模板,不限制):

     

    4.6   第3步,添加RTX5并配置

    RTX5可以方便的通过MDK的RTE环境添加进来。对于F4版本,会添加多个F4版的HAL库文件,这些库文件我们可以使用,也可以不使用。教程配套的工程文件是不使用这些的,因为前面的工程模板里面已经添加了。所以要将这些文件全部隔离出来.

    4.6.1      添加RTX5源码

     

    点击OK按钮后, 可以看到RTX5源码已经添加到工程里面了:

     

    4.6.2      将自动添加的库文件隔离出来

    添加添加的所有文件中,startup_stm32f407xx.s和system_stm32f4xx.c要隔离出来,隔离方法也比较简单,比如隔离startup_stm32f407xx.s文库,鼠标右击此文件选择Options for file ‘startup_stm32f407xx.s’

     

    取消下面的对勾:

     

    隔离这一个文件后,另一个文件system_stm32f4xx.c也被自动隔离了,隔离后的效果如下:

     

    4.6.3      RTX5配置

    剩下就是配置RTX5,设置RTX_Config.h文件即可,移植阶段先按照如下设置配置好,后面章节会专门为大家讲解每个参数的配置含义:

     

    4.7   第4步,配置文件bsp.c

    这个bsp.c文件也比较重要,移植阶段,直接将我们移植好的模板内容复制过去即可,这里把相关的内容为大家做个说明。

    4.7.1      函数System_Init

    系统初始化,主要是系统时钟配置,需要在RTX5初始化之前调用。

    /*
    *********************************************************************************************************
    *    函 数 名: System_Init
    *    功能说明: 系统初始化,主要是系统时钟配置
    *    形    参:无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void System_Init(void)
    {
        /* 
           STM32H429 HAL 库初始化,此时系统用的还是F429自带的16MHz,HSI时钟:
           - 调用函数HAL_InitTick,初始化滴答时钟中断1ms。
           - 设置NVIV优先级分组为4。
         */
        HAL_Init();
    
        /* 
           配置系统时钟到168MHz
           - 切换使用HSE。
           - 此函数会更新全局变量SystemCoreClock,并重新配置HAL_InitTick。
        */
        SystemClock_Config();
    
        /* 
           Event Recorder:
           - 可用于代码执行时间测量,MDK5.25及其以上版本才支持,IAR不支持。
           - 默认不开启,如果要使能此选项,务必看V6开发板用户手册第8章
        */    
    #if Enable_EventRecorder == 1  
        /* 初始化EventRecorder并开启 */
        EventRecorderInitialize(EventRecordAll, 1U);
        EventRecorderStart();
    #endif
    }

    4.7.2      函数bsp_Init

    硬件外设的初始化,这个函数在RTX5的启动任务里面调用。

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_Init
    *    功能说明: 初始化所有的硬件设备。该函数配置CPU寄存器和外设的寄存器并初始化一些全局变量。只需要调用一次
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_Init(void)
    {
        bsp_InitKey();        /* 按键初始化,要放在滴答定时器之前,因为按钮检测是通过滴答定时器扫描 */
        bsp_InitTimer();      /* 初始化滴答定时器 */
        bsp_InitUart();    /* 初始化串口 */
        bsp_InitExtIO();   /* 初始化扩展IO */
        bsp_InitLed();        /* 初始化LED */    
    }

    4.7.3      函数SystemClock_Config

    这个函数主要是完成系统时钟配置。

    /*
    *********************************************************************************************************
    *    函 数 名: SystemClock_Config
    *    功能说明: 初始化系统时钟
    *                System Clock source            = PLL (HSE)
    *                SYSCLK(Hz)                     = 168000000 (CPU Clock)
    *                HCLK = SYSCLK / 1              = 168000000 (AHB1Periph)
    *                PCLK2 = HCLK / 2               = 84000000  (APB2Periph)
    *                PCLK1 = HCLK / 4               = 42000000  (APB1Periph)
    *                HSE Frequency(Hz)              = 25000000
    *               PLL_M                          = 25
    *                PLL_N                          = 336
    *                PLL_P                          = 2
    *                PLL_Q                          = 4
    *                VDD(V)                         = 3.3
    *                Flash Latency(WS)              = 5
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void SystemClock_Config(void)
    {
        RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
        RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    
        
        /* 芯片内部的LDO稳压器输出的电压范围,选用的PWR_REGULATOR_VOLTAGE_SCALE1 */
        __HAL_RCC_PWR_CLK_ENABLE();
        __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
    
        /* 使能HSE,并选择HSE作为PLL时钟源 */
        RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
        RCC_OscInitStruct.HSEState = RCC_HSE_ON;
        RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
        RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
        RCC_OscInitStruct.PLL.PLLM = 8;
        RCC_OscInitStruct.PLL.PLLN = 336;
        RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
        RCC_OscInitStruct.PLL.PLLQ = 4;
        if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);
        }
    
        /* 
           选择PLL的输出作为系统时钟
            HCLK = SYSCLK / 1  (AHB1Periph)
            PCLK2 = HCLK / 2   (APB2Periph)
            PCLK1 = HCLK / 4   (APB1Periph)
        */
        RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                      |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
        RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
        RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
        RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
        RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
    
        /* 此函数会更新SystemCoreClock,并重新配置HAL_InitTick */
        if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
        {
            Error_Handler(__FILE__, __LINE__);
        }
    
        /* 使能SYS时钟和IO补偿 */
        __HAL_RCC_SYSCFG_CLK_ENABLE() ;
    
        HAL_EnableCompensationCell();
    }

    4.7.4      函数bsp_RunPer10ms

    这个函数里面默认有个按键扫描,如果大家移植的程序里面没有按键初始化,务必要把这个按键扫描函数注释掉。

    /*
    *********************************************************************************************************
    *    函 数 名: bsp_RunPer10ms
    *    功能说明: 该函数每隔10ms被Systick中断调用1次。详见 bsp_timer.c的定时中断服务程序。一些处理时间要求
    *              不严格的任务可以放在此函数。比如:按键扫描、蜂鸣器鸣叫控制等。
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void bsp_RunPer10ms(void)
    {
        bsp_KeyScan10ms();
    }

    4.8   第5步,更新bsp_timer.c和bsp.h文件

    更新bsp_timer.c文件是因为此文件跟RTX5都要使用滴答定时器,有冲突。所以大家直接将我们工程模板里面此文件覆盖移植的这个文件即可。

    bsp.h文件里面要添加一个宏定义,因为bsp_timer.c文件里面做了些条件编译:

    #define  USE_RTX    1

    另外,bsp.h文件将大部分头文件都添加进来了,大家可以根据需要,用到那些头文件,使能那些,用不到的,可以注释掉。当然,不注释也是没问题的:

    /* 通过取消注释或者添加注释的方式控制是否包含底层驱动模块 */
    //#include "bsp_msg.h"
    //#include "bsp_user_lib.h"
    #include "bsp_timer.h"
    #include "bsp_led.h"
    #include "bsp_key.h"
    #include "bsp_dwt.h"
    
    //#include "bsp_cpu_rtc.h"
    //#include "bsp_cpu_adc.h"
    //#include "bsp_cpu_dac.h"
    #include "bsp_uart_fifo.h"
    //#include "bsp_uart_gps.h"
    //#include "bsp_uart_esp8266.h"
    //#include "bsp_uart_sim800.h"
    
    //#include "bsp_spi_bus.h"
    //#include "bsp_spi_ad9833.h"
    //#include "bsp_spi_ads1256.h"
    //#include "bsp_spi_dac8501.h"
    //#include "bsp_spi_dac8562.h"
    //#include "bsp_spi_flash.h"
    //#include "bsp_spi_tm7705.h"
    //#include "bsp_spi_vs1053b.h"
    
    //#include "bsp_fmc_sdram.h"
    //#include "bsp_fmc_nand_flash.h"
    //#include "bsp_fmc_ad7606.h"
    //#include "bsp_fmc_oled.h"
    #include "bsp_fmc_io.h"
    
    //#include "bsp_i2c_gpio.h"
    //#include "bsp_i2c_bh1750.h"
    //#include "bsp_i2c_bmp085.h"
    //#include "bsp_i2c_eeprom_24xx.h"
    //#include "bsp_i2c_hmc5883l.h"
    //#include "bsp_i2c_mpu6050.h"
    //#include "bsp_i2c_si4730.h"
    //#include "bsp_i2c_wm8978.h"
    
    //#include "bsp_tft_lcd.h"
    //#include "bsp_ts_touch.h"
    //#include "bsp_ts_ft5x06.h"
    //#include "bsp_ts_gt811.h"
    //#include "bsp_ts_gt911.h"
    //#include "bsp_ts_stmpe811.h"
    
    #include "bsp_beep.h"
    #include "bsp_tim_pwm.h"
    //#include "bsp_sdio_sd.h"
    //#include "bsp_dht11.h"
    //#include "bsp_ds18b20.h"
    //#include "bsp_ps2.h"
    //#include "bsp_ir_decode.h"
    //#include "bsp_camera.h"
    //#include "bsp_rs485_led.h"
    //#include "bsp_can.h"

    4.9   第6步,修改文件stm32f4xx_it.c

    删除此文件里面带的如下函数,RTX5要使用,冲突了。

    /**
      * @brief  This function handles SVCall exception.
      * @param  None
      * @retval None
      */
    void SVC_Handler(void)
    {
    }
    
    /**
      * @brief  This function handles PendSVC exception.
      * @param  None
      * @retval None
      */
    void PendSV_Handler(void)
    {
    }

    4.10 第7步,添加头文件的汇总文件includes.h

    在User文件夹下添加文件incudes.h,直接从本章节教程配套例子的User文件夹复制即可。此文件主要用于RTX5的各种头文件汇总。


     

    4.11 第8步,HAL库时间基准stm32f4xx_hal_timebase_tim.c

    由于RTX5和HAL库需要一个时间基准,而且默认都是用的滴答定时器,所有要有一个选用其它的时间基准。当前的处理方案是为HAL库提供一个时间基准文件stm32f4xx_hal_timbase_tim.c。此文件

    里面做了两套方案,一个是使用TIM7做时间基准,另一个是使用RTX5的API做时间基准,通过条件编译做选择。默认是采用RTX5的API做时间基准。

    /*
    *********************************************************************************************************
    *    函 数 名: HAL_Delay
    *    功能说明: 重定向毫秒延迟函数。替换HAL中的函数。因为HAL中的缺省函数依赖于Systick中断,如果在USB、SD
    *             卡中断中有延迟函数,则会锁死。也可以通过函数HAL_NVIC_SetPriority提升Systick中断
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    void HAL_Delay(uint32_t Delay)
    {
        bsp_DelayMS(Delay);
    }
    
    HAL_StatusTypeDef HAL_InitTick (uint32_t TickPriority)
    {
        return HAL_OK;
    }
    
    uint32_t HAL_GetTick (void) 
    {
        static uint32_t ticks = 0U;
        uint32_t i;
    
        if (osKernelGetState () == osKernelRunning)
        {
            return ((uint32_t)osKernelGetTickCount ());
        }
    
        /* 如果RTX5还没有运行,采用下面方式 */
        for (i = (SystemCoreClock >> 14U); i > 0U; i--) 
        {
            __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
            __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP();
        }
        
        return ++ticks;
    }

    4.12 第9步,添加BSP驱动文件bsp_dwt.c

    添加bsp_dwt.c文件和bsp_dwt.h文件主要是因为第8步中的stm32f4xx_hal_timebase_tim.c文件里面的函数bsp_DelayMS要使用,此函数是基于DWT系统时钟周期计数器实现。

     

    4.13 第10步,创建应用任务(重要,注意启动任务)

    应用程序比较简单,大家可以直接复制本章教程配置例子的main.c文件中的内容到自己工程里面测试。主要创建了如下几个任务:

    AppTaskUserIF任务   : 按键消息处理。

    AppTaskLED任务      : LED闪烁。

    AppTaskMsgPro任务   : 消息处理,暂未使用。

    AppTaskStart任务    : 启动任务,也是最高优先级任务,这里实现按键扫描。

    osRtxTimerThread任务: 定时器任务,暂未使用。

    任务栈大小和任务控制块定义如下:

    /*
    **********************************************************************************************************
                                                 变量
    **********************************************************************************************************
    */
    /* 任务的属性设置 */
    const osThreadAttr_t ThreadStart_Attr = 
    {
        /* 未使用 */
    //    .cb_mem = &worker_thread_tcb_1,
    //    .cb_size = sizeof(worker_thread_tcb_1),
    //    .stack_mem = &worker_thread_stk_1[0],
    //    .stack_size = sizeof(worker_thread_stk_1),
    //    .priority = osPriorityAboveNormal,
    //    .tz_module = 0
        
        .name = "osRtxStartThread",
        .attr_bits = osThreadDetached, 
        .priority = osPriorityHigh4,
        .stack_size = 2048,
    };
    
    const osThreadAttr_t ThreadMsgPro_Attr = 
    {
        .name = "osRtxMsgProThread",
        .attr_bits = osThreadDetached, 
        .priority = osPriorityHigh3,
        .stack_size = 1024,
    };
    
    const osThreadAttr_t ThreadLED_Attr = 
    {
        .name = "osRtxLEDThread",
        .attr_bits = osThreadDetached, 
        .priority = osPriorityHigh2,
        .stack_size = 512,
    };
    
    const osThreadAttr_t ThreadUserIF_Attr = 
    {
        .name = "osRtxThreadUserIF",
        .attr_bits = osThreadDetached, 
        .priority = osPriorityHigh1,
        .stack_size = 1024,
    };

    任务创建:

    /*
    *********************************************************************************************************
    *    函 数 名: main
    *    功能说明: 标准c程序入口。
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    int main (void) 
    {    
        /* HAL库,MPU,Cache,时钟等系统初始化 */
        System_Init();
    
        /* 内核开启前关闭HAL的时间基准 */
        HAL_SuspendTick();
        
        /* 内核初始化 */
        osKernelInitialize();                                  
    
        /* 创建启动任务 */
        ThreadIdStart = osThreadNew(AppTaskStart, NULL, &ThreadStart_Attr);  
    
        /* 开启多任务 */
        osKernelStart();
        
        while(1);
    }
    
    /*
    *********************************************************************************************************
    *    函 数 名: AppTaskCreate
    *    功能说明: 创建应用任务
    *    形    参: 无
    *    返 回 值: 无
    *********************************************************************************************************
    */
    static void AppTaskCreate (void)
    {
        ThreadIdTaskMsgPro = osThreadNew(AppTaskMsgPro, NULL, &ThreadMsgPro_Attr);  
        ThreadIdTaskLED = osThreadNew(AppTaskLED, NULL, &ThreadLED_Attr);  
        ThreadIdTaskUserIF = osThreadNew(AppTaskUserIF, NULL, &ThreadUserIF_Attr);  
    }

    这里我们重点看下启动任务,主要做了四个工作:

    •   外设初始化bsp_Init。
    •   任务创建AppTaskCreate。
    •   需要周期性处理的程序bsp_ProPer1ms,对应裸机工程调用的SysTick_ISR。这个的实现非常重要,这样之前裸机里面使用的API,就可以直接在RTX5里面直接调用。
    /*
    *********************************************************************************************************
    *    函 数 名: AppTaskStart
    *    功能说明: 启动任务,这里用作BSP驱动包处理。
    *    形    参: 无
    *    返 回 值: 无
    *   优 先 级: osPriorityHigh4  
    *********************************************************************************************************
    */
    void AppTaskStart(void *argument)
    {
        const uint16_t usFrequency = 1; /* 延迟周期 */
        uint32_t tick;
        
        /* 初始化外设 */
        HAL_ResumeTick();
        bsp_Init();
    
        /* 创建任务 */
        AppTaskCreate();
    
        /* 获取当前时间 */
        tick = osKernelGetTickCount(); 
        
        while(1)
        {
            /* 需要周期性处理的程序,对应裸机工程调用的SysTick_ISR */
            bsp_ProPer1ms();
            
            /* 相对延迟 */
            tick += usFrequency;                          
            osDelayUntil(tick);
        }
    }

    4.14 常见移植错误总结

    常见的移植错误主要有下面几种情况:

    •   编译后提示如下两种错误:

    Error: L6200E: Symbol PendSV_Handler multiply defined (by irq_cm4f.o and stm32f4xx_it.o).

    Error: L6200E: Symbol SVC_Handler multiply defined (by irq_cm4f.o and stm32f4xx_it.o).

    解决办法:这是函数重定义了,直接将stm32f4xx_it.c文件里面的PendSV_Handler和SVC_Handler删掉。

    4.15 实验例程

    本章节配套了如下几个例子供大家移植参考:

    •   V5-400_Base Template

    裸机模板,方便大家添加RTX5内核源码。

    •  V5-401_Threadx Kernel Template

    ThreadX内核模板。

    MDK进入调试状态后,选择周期更新:

     

    然后打开调试组件,注意和RTX4的调试组件位置不同:

     

    然后点击MDK的全速运行,

     

    至此,就可以动态实时查看RTX5的运行状态:

     

    4.16 总结

    本章节为大家讲解了RTX5 在MDK AC5上的移植方法,移植涉及到的知识点比较多,初学的话,建议实际动手操作一遍。

    微信公众号:armfly_com 安富莱论坛:www.armbbs.cn 安富莱淘宝:https://armfly.taobao.com
  • 相关阅读:
    urllib.request.urlretrieve()
    python2.X与python3.X爬虫常用的模块变化对应
    .net 发布程序时出现“类型ASP.global_asax同时存在于...”错误的解决办法
    批量引用iconfont字体图标到项目
    动态设置bootstrapswitch状态
    MD5加密过时方法替换
    SQL语句
    PHP中的闭包
    算法复杂度
    快速排序
  • 原文地址:https://www.cnblogs.com/armfly/p/14499843.html
Copyright © 2011-2022 走看看