使用MM32F3270基于Azure RTOS (ThreadX)的移植

cathy的头像
cathy 发布于:周五, 06/17/2022 - 12:01 ,关键词:

简 介

Azure RTOS ThreadX 是 Microsoft 提供的高级工业级实时操作系统 (RTOS)。它是专门为深度嵌入式实时 IoT 应用程序设计的。Azure RTOS ThreadX 提供高级计划、通信、同步、计时器、内存管理和中断管理功能。此外,Azure RTOS ThreadX 具有许多高级功能,包括 picokernel™ 体系结构、preemption-threshold™ 计划、event-chaining™、执行分析、性能指标和系统事件跟踪。Azure RTOS ThreadX 非常易于使用,适用于要求极其苛刻的嵌入式应用程序。Azure RTOS ThreadX 在各种产品(包括消费者设备、医疗电子设备和工业控制设备)上的部署次数已达数十亿次。

具体的介绍和用户指南可以参考:

https://docs.microsoft.com/zh-cn/azure/rtos/threadx/

在前文描述移植基本内核的基础上,本文描述了如何基于MM32F3270系列MCU结合Azure RTOS ThreadX实现多任务切换,引导用户理解Azure RTOS ThreadX任务切换特性。

包括底层启动文件的移植, 系统Systick的配置技巧,引导用户理解Azure RTOS ThreadX基本应用。

表 1 适用系列型号

“表

1、移植应用的准备

1.1 硬件开发板的准备

该移植过程中应用的开发板为MM32的EVBoard MB039(MM32F3273G9P)

“使用MM32F3270基于Azure

EV Board (MM32F3273G9P) 的简要参数:

  • Arm Cortex-M3 内核
  • 板载 MM32F3273G9P(LQFP144)
  • USB Host / Device、SPI、I2C
  • 4 x Key、4 x LED
  • I2S Speaker
  • TF-Card
  • Ethernet PHY

1.2 软件的准备

库函数和例程(Lib Samples)

该移植过程中应用的Firmware分别为MM32F3270 库函数和例程下载地址:

https://www.mindmotion.com.cn/products/mm32mcu/mm32f/mm32f_mainstream/mm32f3270/

“使用MM32F3270基于Azure

Azure RTOS ThreadX(源码)

ThreadX的源代码已经开放,我们可以从ThreadX公共源代码存储库获取 Azure RTOS ThreadX,网址为:https://github.com/azure-rtos/threadx/

具体的商用使用条件参考Azure的许可证说明:

https://www.microsoft.com/en-us/legal/intellectualproperty/tech-licensing/programs?msclkid=f7ab4ff3afa011ec90a79366a52034fa&activetab=pivot1:primaryr11

Microsoft publishes the Azure RTOS source code to GitHub. No license is required to install and use the software for internal development, testing, and evaluation purposes. A license is required to distribute or sell components and devices unless using Azure RTOS licensed hardware.

Azure RTOS 何时需要许可证?

Microsoft 将 Azure RTOS 源代码发布到 GitHub。安装和使用该软件进行内部开发、测试和评估无需许可证。分发或销售组件和设备需要许可证,除非使用 Azure RTOS 许可的硬件。

ThreadX 安装

可以通过将 GitHub 存储库克隆到本地计算机来安装 ThreadX。下面是用于在 PC 上创建 ThreadX 存储库的克隆的典型语法。

shell复制

git clone https://github.com/azure-rtos/threadx

或者,也可以使用 GitHub 主页上的“下载”按钮来下载存储库的副本。

下载后的仓库代码目录列表如下:

“使用MM32F3270基于Azure

Azure RTOS ThreadX(源码)支持的开发环境

ThreadX 内核提供好了各种主流硬件平台和软件平台的移植文件,以Cortex_M3为例,可以支持以下六种开发环境:

“使用MM32F3270基于Azure

本次移植过程使用上一个已经移植好的基本样例为起始点,做移植说明。

2、Threadx 的多任务的实现

该章节介绍了多任务的实现的过程和注意事项,该演示程序可在所有MM32F3273G9P的EVBoard MB039上运行。

此示例在文件 main_multi_task.c 中定义,旨在说明如何在嵌入式多线程环境中使用 ThreadX。演示包括初始化硬件外设、4个线程。

2.1 打开移植好的基本工程目录

打开目标工程文件夹“MM32F3270Project”:

“使用MM32F3270基于Azure

移除原有样例.c 文件sample_threadx.c:

“使用MM32F3270基于Azure

参考sample_threadx.c建立main_multi_task.c文件,并添加hardware目录中的led.c到工程项目中。

“使用MM32F3270基于Azure

并把HARDWARE\LED目录添加到包含文件目录中:

“使用MM32F3270基于Azure

2.2 Azure Threadx的运行过程

通过样例可知,Azure Threadx的主要执行的入口代码如下:

基本系统架构

“使用MM32F3270基于Azure

main 函数

系统完成初始化后,系统将控制权转交给用户提供的 main 函数。此时,应用程序会控制接下来执行的操作。对于大多数应用程序,main 函数只调用 tx_kernel_enter,这是 ThreadX 的入口。但是,应用程序可在进入 ThreadX 之前执行初步处理(通常用于硬件初始化)。

注意:

对 tx_kernel_enter 的调用不会返回结果,因此请勿在此后执行任何处理。

tx_kernel_enter

entry 函数协调各种内部 ThreadX 数据结构的初始化,然后调用应用程序的定义函数 tx_application_define。

当 tx_application_define 返回时,控制权将转交给线程调度循环。这标志着初始化结束。

应用程序定义函数

tx_application_define 函数定义所有初始应用程序线程、队列、信号灯、互斥、事件标志、内存池和计时器。在应用程序的正常操作过程中,还可以在线程中创建和删除系统资源。但是,所有初始应用程序资源都在此处定义。

值得一提的是,tx_application_define 函数只有一个输入参数。“第一个可用”的 RAM 地址是该函数唯一的输入参数。该地址通常用作线程堆栈、队列和内存池的初始运行时内存分配起点。

注意:

初始化完成后,只有正在执行的线程才能创建和删除系统资源(包括其他线程)。因此,在初始化期间必须至少创建一个线程。

中断

在整个初始化过程中,中断处于禁用状态。如果应用程序以某种方式启用中断,则可能出现不可预知的行为。下图显示了从系统重置到特定于应用程序的初始化的整个初始化过程。

“使用MM32F3270基于Azure

线程执行

计划和执行应用程序线程是 ThreadX 最重要的活动。线程通常定义为具有专用用途的半独立程序段。所有线程的组合处理构成了应用程序。

线程在初始化或线程执行期间通过调用 tx_thread_create 来动态创建。创建的线程处于“就绪”或“已挂起”状态。

2.3 Azure Threadx执行程序的建立

打开main_multi_task.c文件,添加#include "led.h"

保留默堆栈空间大小

“使用MM32F3270基于Azure

首先第一步,建立四个线程用于演示四个闪灯线程,以做建立线程的基本展示。

建立四个线程所需的线程优先级(thread priority),线程对象控制块 ( thread object control blocks ),线程累加计数器 ( thread counters ), 线程堆栈块大小 ( thread stacks )

#define  APP_CFG_TASK_LED1_PRIO                            1u
#define  APP_CFG_TASK_LED2_PRIO                           16u
#define  APP_CFG_TASK_LED3_PRIO                           16u
#define  APP_CFG_TASK_LED4_PRIO                            8u

/* Define the ThreadX object control blocks...  */
TX_THREAD               thread_0;
TX_THREAD               thread_1;
TX_THREAD               thread_2;
TX_THREAD               thread_3;

/* Define the thread stacks.  */
UCHAR                   thread_0_stack[DEMO_STACK_SIZE];
UCHAR                   thread_1_stack[DEMO_STACK_SIZE];
UCHAR                   thread_2_stack[DEMO_STACK_SIZE];
UCHAR                   thread_3_stack[DEMO_STACK_SIZE];

/* Define the counters used in the demo application...  */
ULONG                   thread_0_counter;
ULONG                   thread_1_counter;
ULONG                   thread_2_counter;
ULONG                   thread_3_counter;

每个演示线程均会递增自己的唯一计数器。可以检查以下计数器以核实演示的操作:

thread_0_counter

thread_1_counter

thread_2_counter

thread_3_counter

在演示执行过程中,每个计数器都应继续增加,其中,thread_0_counter 的增加速度最快, thread_1_counter 的增加速度属于第二快,thread_2_counter和thread_3_counter分列第三和第四;这主要和每次counter累加时sleep时长相关。

线程 0

函数 thread_0_entry 标记线程的入口点。Thread_0 是演示系统中要执行的第一个线程。其处理方式很简单:递增其计数器,休眠 10 个计时器时钟周期,设置GPIO推动LED1亮暗翻转。

后续的线程1,2,3分别操作不同的LED2,LED3,LED4翻转,并等待不同的时间。

Thread_0 优先级被设置为1,是系统中优先级最高的线程。其他的线程分别被设置为16,16,8等不同的优先级。Thread_0的优先级最高,当其请求的休眠过期时,将抢占演示中任何其他正在执行的线程。这个是Azure ThreadX RTOS强大的一个地方。

建立任务代码

void    tx_application_define(void* first_unused_memory)
{
    /* Create threads 0 */
    tx_thread_create(
        &thread_0,              /* 任务控制块地址     */
        "Task 0 led blink 1",   /* 任务名             */
        thread_0_entry,         /* 启动任务函数地址   */
        0,                      /* 传递给任务的参数   */
        thread_0_stack,         /* 堆栈基地址         */
        DEMO_STACK_SIZE,        /* 堆栈空间大小       */
        APP_CFG_TASK_LED1_PRIO, /* 任务优先级         */
        1,                      /* 任务抢占阀值       */
        TX_NO_TIME_SLICE,       /* 不开启时间片       */
        TX_AUTO_START           /* 创建后立即启动     */
    );

    /* Create threads 1 */
    tx_thread_create(
        &thread_1,
        "Task 1 led blink 2",
        thread_1_entry,
        1,
        thread_1_stack,
        DEMO_STACK_SIZE,
        APP_CFG_TASK_LED2_PRIO,
        16,
        TX_NO_TIME_SLICE,
        TX_AUTO_START
    );
    /* Create threads 2. */
    tx_thread_create(
        &thread_2,
        "Task 2 led blink 3",
        thread_2_entry, 2,
        thread_2_stack,
        DEMO_STACK_SIZE,
        APP_CFG_TASK_LED3_PRIO,
        16,
        TX_NO_TIME_SLICE,
        TX_AUTO_START);

    /* Create threads 3 */
    tx_thread_create(
        &thread_3,
        "Task 3 led blink 4",
        thread_3_entry,
        3,
        thread_3_stack,
        DEMO_STACK_SIZE,
        APP_CFG_TASK_LED4_PRIO,
        8,
        TX_NO_TIME_SLICE,
        TX_AUTO_START
    );

}

四个LED翻转的任务

void    thread_0_entry(ULONG thread_input)
{
    while(1) {
        /* Increment the thread counter.  */
        thread_0_counter++;
        LED1_TOGGLE();
        /* Sleep for 10 ticks.  */
        tx_thread_sleep(10);
    }
}

void    thread_1_entry(ULONG thread_input)
{
    while(1) {
        /* Increment the thread counter.  */
        thread_1_counter++;
        LED2_TOGGLE();
        tx_thread_sleep(100);
    }
}

void    thread_2_entry(ULONG thread_input)
{
    while(1) {
        /* Increment the thread counter.  */
        thread_2_counter++;
        LED3_TOGGLE();
        tx_thread_sleep(300);
    }
}

void    thread_3_entry(ULONG thread_input)
{

    while(1) {
        /* Increment the thread counter.  */
        thread_3_counter++;
        LED4_TOGGLE();
        tx_thread_sleep(600);
    }
}

最后添加main函数

main函数代码

int main()
{
    LED_Init();

    /* Enter the ThreadX kernel.  */
    tx_kernel_enter();
}

在main()函数中主要执行初步处理(通常用于硬件初始化),在此执行了LED_Init();然后进入tx_kernel_enter()实现系统的任务建立与任务调度。

3、Threadx 的运行与调试

3.1 下载与调试

下载调试默认会运行到main()函数。

“使用MM32F3270基于Azure

该demo示例,建立了4个线程,程序执行4个LED翻转,并累加Counter值。

全速运行后,可以看到相关的数值在变化:

“使用MM32F3270基于Azure

4、小结

关于使用MM32F3270基于Azure RTOS ThreadX 的最四个小任务就建立,并运行起来,后面再给大家介绍Task相关的抢占任务优先级等Azure RTOS的特色功能。

来源:灵动MM32MCU
免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 288