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

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

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

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

目录

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

4.1   初学者重要提示

4.2   移植RXT5内核整体说明

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

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

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

4.6   第3步,添加RTX5并配置

4.6.1      添加RTX5源码

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

4.6.3      RTX5配置

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

4.7.1      函数System_Init

4.7.2      函数bsp_Init

4.7.3      函数SystemClock_Config

4.7.4      函数bsp_RunPer10ms

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_timebase_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内核模板,方面大家后面移植:

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第1张图片

框图如下:

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第2张图片

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的软件包版本如何升级,当前的软件包版本和以后的新版是可以同时安装的,也就是说可以安装多个不同版本,在这里可以选择指定版本:

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第3张图片

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

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第4张图片

4.6   第3步,添加RTX5并配置

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

4.6.1      添加RTX5源码

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第5张图片

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第6张图片

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

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第7张图片

取消下面的对勾:

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第8张图片

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第9张图片

4.6.3      RTX5配置

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第10张图片

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_429.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的各种头文件汇总。

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第11张图片

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系统时钟周期计数器实现。

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第12张图片

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 实验例程

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

  •  V6-400_Base Template

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

  •   V6-401_Threadx Kernel Template

ThreadX内核模板。

 

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第13张图片

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

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第14张图片

然后点击MDK的全速运行,

【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第15张图片

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

 【STM32F429】第4章 RTX5操作系统移植(MDK AC5)_第16张图片

4.16 总结

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

 

你可能感兴趣的:(RTX5内核教程,RTOS,RTX5,CMSIS,GUIX,USB)