灵动微电子 MM32
直播中

zhu

8年用户 1640经验值
擅长:控制/MCU
私信 关注
[原创]

基于MM32 MCU的OS移植与应用——ThreadX开篇

Azure RTOS ThreadX概述

Azure RTOS ThreadX是微软先进的工业级实时操作系统(RTOS),专为嵌入式、实时和物联网应用程序设计。Azure RTOS ThreadX提供先进的调度、通讯、同步、定时器、内存管理和中断管理等功能。此外,Azure RTOS ThreadX还具有许多高级特性,包括它的Picokernel体系结构、抢占阈值调度、事件链接、执行分析、性能度量和系统事件跟踪。结合其优越的易用性,Azure RTOS ThreadX是最苛刻的嵌入式应用程序的理想选择。截至 2017 年,Azure RTOS ThreadX 已在各种产品(包括消费类设备、医疗电子设备和工业控制设备)中部署了超过 62 亿次。
MM32 eMiniBoard开发板

Insight系列MM32 eMiniBoard开发板是灵动微电子推出的基于Cortex-M0/M3系列MCU开发评估板,集成了MM32-LINK-OB在线仿真器,支持SWD调试接口及CDC虚拟调试串口;此外结合MCU的外设,开发板还支持LED、按键、EEPROM、SPI FLASH、蜂鸣器、电位器、串口和CAN通讯等功能,为终端用户的功能验证提供了便捷的开发环境。
资料准备


1
Azure RTOS ThreadX源码
https://github.com/azure-rtos/threadx/releases
2
Azure RTOS ThreadX在线文档
https://docs.microsoft.com/en-us/azure/rtos/threadx/overview-threadx
3
eMiniBoard MB-025开发板用户手册http://www.mindmotion.com.cn/userfiles/images/XiaZaiZhongXin/ug_mm32_emb_v0.92_cn.pdf
4
MM32F0133数据手册及用户手册http://www.mindmotion.com.cn/userfiles/images/MM32F013XiLieWenDang/DS_MM32F013x_V1.00_SC.pdf
http://www.mindmotion.com.cn/userfiles/images/MM32F013XiLieWenDang/UM_MM32F013x_V1.00_SC.pdf
5
MM32F0133库函数及例程http://www.mindmotion.com.cn/userfiles/images/KuHanShuHeLiCheng/MM32F013x_Lib_Samples_V1.03.zip

移植Azure RTOS ThreadX


01
功能验证
在eMiniBoard MB-025开发板上创建最小系统工程,使用开发板的UART2和LED外设,通过UART2打印输出消息,并让LED灯间隔闪烁。

02
移植步骤
当前下载的Azure RTOS ThreadX源码版本为6.1.1_rel,解压后如下图所示:
13.png 移植需要的两个文件夹分别为ports和common,将这两个文件夹复制到刚刚创建的最小系统工程中,存放路径为:SourceUtilityThreadX,如下图所示: 12.png
eMiniBoard MB-025板载的芯片为MM32F0133C7P,是基于Cortex-M0的MCU,软件工程是基于KEIL MDK的AC5编译的,所以我们在移植的时候需要使用的是portscortex_m0ac5文件夹下的内容。
将portscortex_m0ac5example_build文件夹下的tx_initialize_low_level.s文件复制到portscortex_m0ac5src文件夹下。

打开刚刚创建的最小系统工程,点击如下图的Manage Project Items图标,弹出Manage Project Items对话框:
11.png

在Manage Project Items对话框中的Project Items选项卡中,在Groups中添加ThreadX_Common和ThreadX_Ports选中ThreadX_Common,添加SourceUtilityThreadXcommonsrc文件夹下的所有源文件
选中ThreadX_Ports,
添加SourceUtilityThreadXportscortex_m0ac5src文件夹下的所有源文件
如下图所示:

10.png 9.png 点击如下图所示的Options for Target...图标,在弹出的Options for Target对话框的C/C++选项卡中,点击Include Paths,添加Azure RTOS ThreadX头文件路径,如下图所示: 8.png 7.png 6.png
至此,Azure RTOS ThreadX的移植文件就添加OK了,我们尝试编译一下工程,编译结果提示有一个ERROR错误,如下图所示: 5.png

这是因为在我们工程中的startup_mm32f013x_keil.s和tx_initialize_low_level.s这两个文件中的定义配置重复冲突引起的。
我们想保留startup_mm32f013x_keil.s这个原厂库函数中提供的启动文件,所以我们来修改tx_initialize_low_level.s这个文件中的实现内容,具体修改如下:

IMPORT  _tx_thread_system_stack_ptr
    IMPORT  _tx_initialize_unused_memory
    IMPORT  _tx_timer_interrupt
    IMPORT  __initial_sp
    IMPORT  __Vectors


SYSTEM_CLOCK        EQU     72000000
SYSTICK_CYCLES      EQU     ((SYSTEM_CLOCK / 1000) - 1)


    AREA ||.text||, CODE, READONLY

VOID   _tx_initialize_low_level(VOID)
{
    EXPORT  _tx_initialize_low_level
    _tx_initialize_low_level

     /* Ensure that interrupts are disabled.  */

    CPSID   i                                       ; Disable interrupts

     /* Set base of available memory to end of non-initialised RAM area.  */

    LDR     r0, =_tx_initialize_unused_memory       ; Build address of unused memory pointer
    LDR     r1, =__initial_sp                       ; Build first free address
    ADDS    r1, r1, #4                              ;
    STR     r1, [r0]                                ; Setup first unused memory pointer

     /* Setup Vector Table Offset Register.  */

    LDR     r0, =0xE000ED08                         ; Build address of NVIC registers
    LDR     r1, =__Vectors                          ; Pickup address of vector table
    STR     r1, [r0]                                ; Set vector table address

     /* Enable the cycle count register.  */

    LDR     r0, =0xE0001000                         ; Build address of DWT register
    LDR     r1, [r0]                                ; Pickup the current value
    MOVS    r2, #1
    ORRS    r1, r1, r2                              ; Set the CYCCNTENA bit
    STR     r1, [r0]                                ; Enable the cycle count register

     /* Setup Vector Table Offset Register.  */

    LDR     r0, =0xE000E000                         ; Build address of NVIC registers
    LDR     r2, =0xD08                              ; Offset to vector base register
    ADD     r0, r0, r2                              ; Build vector base register
    LDR     r1, =__Vectors                          ; Pickup address of vector table
    STR     r1, [r0]                                ; Set vector table address

     /* Set system stack pointer from vector value.  */

    LDR     r0, =_tx_thread_system_stack_ptr        ; Build address of system stack pointer
    LDR     r1, =__Vectors                          ; Pickup address of vector table
    LDR     r1, [r1]                                ; Pickup reset stack pointer
    STR     r1, [r0]                                ; Save system stack pointer

     /* Configure SysTick.  */

    LDR     r0, =0xE000E000                         ; Build address of NVIC registers
    LDR     r1, =SYSTICK_CYCLES
    STR     r1, [r0, #0x14]                         ; Setup SysTick Reload Value
    MOVS    r1, #0x7                                ; Build SysTick Control Enable Value
    STR     r1, [r0, #0x10]                         ; Setup SysTick Control

     /* Configure handler priorities.  */

    LDR     r1, =0x00000000                         ; Rsrv, UsgF, BusF, MemM
    LDR     r0, =0xE000E000                         ; Build address of NVIC registers
    LDR     r2, =0xD18                              ;
    ADD     r0, r0, r2                              ;
    STR     r1, [r0]                                ; Setup System Handlers 4-7 Priority Registers

    LDR     r1, =0xFF000000                         ; SVCl, Rsrv, Rsrv, Rsrv
    LDR     r0, =0xE000E000                         ; Build address of NVIC registers
    LDR     r2, =0xD1C                              ;
    ADD     r0, r0, r2                              ;
    STR     r1, [r0]                                ; Setup System Handlers 8-11 Priority Registers
                                                    ; Note: SVC must be lowest priority, which is 0xFF

    LDR     r1, =0x40FF0000                         ; SysT, PnSV, Rsrv, DbgM
    LDR     r0, =0xE000E000                         ; Build address of NVIC registers
    LDR     r2, =0xD20                              ;
    ADD     r0, r0, r2                              ;
    STR     r1, [r0]                                ; Setup System Handlers 12-15 Priority Registers
                                                    ; Note: PnSV must be lowest priority, which is 0xFF

     /* Return to caller.  */

    BX      lr
}


    EXPORT  SysTick_Handler
    EXPORT  __tx_SysTickHandler
__tx_SysTickHandler
SysTick_Handler
  VOID TimerInterruptHandler (VOID)
  {

    PUSH    {r0, lr}
    BL      _tx_timer_interrupt
    POP     {r0, r1}
    MOV     lr, r1
    BX      lr
  }


    ALIGN
    LTORG
    END

在修改了tx_initialize_low_level.s文件后,我们再尝试编译一下工程,编译结果又提示了一个错误:SysTick_Handler multiply defined,系统的SysTick中断函数被重复定义了。
4.png
在移植了Azure RTOS ThreadX后,MCU的SysTick中断就***作系统给接管了,如果程序中还有我们自己实现的中断处理函数,则需要注释掉,不然就会提示重复定义的错误。
在注释上自己实现的SysTick_Handler函数后,我们再来编译一下工程;这个时候编译结果又提示了一个错误:Undefined symbol tx_application_define,没有定义tx_application_define,如下图所示:
3.png 我们在main.c中添加一个tx_application_define函数,再重新编译一下工程,这个时候整个工程编译没有错误。
使用MDK v5.31版本,设置-O0优化等级,经过编译后最小资源如下:
2.png 此时Azure RTOS ThreadX在MM32F0133的空工程算是移植成功了,接下来我们来添加一个最简的LED闪灯和UART串口打印输出例程,验证一下移植是否成功。
在main.c文件中实现我们上述描述的功能,如下所示:

/* Define to prevent recursive inclusion -----------------------------*/
#define __MAIN_C__

/* Includes ----------------------------------------------------------*/
#include "main.h"

/* Private typedef ---------------------------------------------------*/
/* Private define ----------------------------------------------------*/
/* Private macro -----------------------------------------------------*/

/* Private variables -------------------------------------------------*/
TX_THREAD mm32_thread1;

/* Private function prototypes ---------------------------------- -----*/
/* Private functions --------------------------------------------------*/
/**
  * @Brief  
  * @param  None
  * @retval None
  */
void mm32_thread1_entry(ULONG thread_input)
{
    while(1)
    {
        LED1_TOGGLE();
        printf("rnmm32 thread1 running...");

        tx_thread_sleep(1000);
    }
}

/**
  * @brief  
  * @param  None
  * @retval None
  */
void tx_application_define(void *first_unused_memory)
{
    /* 创建任务 */
    tx_thread_create(
        &mm32_thread1,      /* 任务控制块地址 */
        "mm32 thread1",     /* 任务名称 */
        mm32_thread1_entry, /* 启动任务函数地址 */
        0,                  /* 传递给任务的参数 */
        first_unused_memory,/* 堆栈基地址 */
        1024,               /* 堆栈空间大小 */
        1,                  /* 任务优先级 */
        1,                  /* 任务抢占阈值 */
        TX_NO_TIME_SLICE,   /* 不开启时间片 */
        TX_AUTO_START       /* 创建后立即启动 */
    );
}

/**
  * @brief  芯片内部资源初始化集合
  * @param  None
  * @retval None
  */
void MCU_Config(void)
{
    SysTick_Init();     /* SysTick系统滴答时钟初始化配置 */

    UART2_Config();     /* UART2串口初始化配置 */
}

/**
  * @brief  板载硬件资源初始化集合
  * @param  None
  * @retval None
  */
void BSP_Init(void)
{
    LED_Init();         /* LED灯初始化 */
}

/**
  * @brief  
  * @param  None
  * @retval None
  */
int main(void)
{
    MCU_Config();       /* 芯片内部资源初始化集合 */

    BSP_Init();         /* 板载硬件资源初始化集合 */

    /* 打印系统上电初始化完成后的信息 */
    printf("rnrneMiniBoard MB-025(MM32F0133C7P) %s %srnrn", __DATE__, __TIME__);

    /* 启动内核 */
    tx_kernel_enter();

    while(1)
    {
    }
}

编译程序无误后将程序下载到开发板运行,查看到LED1每间隔1秒钟闪烁一次,通过串口终端工具可以查看到程序正在运行,每间隔1秒钟打印输出一条信息。
1.png

更多回帖

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