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.mindmo
tion.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,解压后如下图所示:
移植需要的两个文件夹分别为ports和common,将这两个文件夹复制到刚刚创建的最小系统工程中,存放路径为:SourceUtilityThreadX,如下图所示:
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对话框:
在Manage Project Items对话框中的Project Items选项卡中,在Groups中添加ThreadX_Common和ThreadX_Ports选中ThreadX_Common,添加SourceUtilityThreadXcommonsrc文件夹下的所有源文件
选中ThreadX_Ports,
添加SourceUtilityThreadXportscortex_m0ac5src文件夹下的所有源文件
如下图所示:
点击如下图所示的Options for Target...图标,在弹出的Options for Target对话框的C/C++选项卡中,点击Include Paths,添加Azure RTOS ThreadX头文件路径,如下图所示:
至此,Azure RTOS ThreadX的移植文件就添加OK了,我们尝试编译一下工程,编译结果提示有一个ERROR错误,如下图所示:
这是因为在我们工程中的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中断函数被重复定义了。
在移植了Azure RTOS ThreadX后,MCU的SysTick中断就***作系统给接管了,如果程序中还有我们自己实现的中断处理函数,则需要注释掉,不然就会提示重复定义的错误。
在注释上自己实现的SysTick_Handler函数后,我们再来编译一下工程;这个时候编译结果又提示了一个错误:Undefined symbol tx_application_define,没有定义tx_application_define,如下图所示:
我们在main.c中添加一个tx_application_define函数,再重新编译一下工程,这个时候整个工程编译没有错误。
使用MDK v5.31版本,设置-O0优化等级,经过编译后最小资源如下:
此时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秒钟打印输出一条信息。