灵动微电子 · 2023年01月05日 · 北京市

灵动微课堂 |使用MM32F3270基于Azure RTOS定时器组的应用

 简 介 

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应用程序计时器功能。

表 1 适用系列型号

image.png

1 移植应用的准备

1.1   硬件开发板的准备

该移植过程中应用的开发板为MM32的EVB-F3270,板载MM32F3273G9P。
image.png
EVB-F3270 (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/

image.png

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 clonehttps://github.com/azure-rtos/threadx

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

下载后的仓库代码目录列表如下:
image.png
Azure RTOS ThreadX(源码)支持的开发环境

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

image.png

本次移植过程使用Azure RTOS原有的sample_threadx.c文件为例,稍作修改,演示定时器组功能。

2 ThreadX定时器组的应用

该章节介绍动态内存管理相关知识,演示程序可在MM32F3273G9P的EVB-F3270上运行。此示例在文件 main_timer_demo.c 中实现,旨在说明如何在嵌入式多线程环境中使用定时器组功能。

2.1  定时器组

2.1.1  应用程序计时器

快速响应异步外部事件是嵌入式实时应用程序中最重要的功能。但是,其中的许多应用程序还必须按预定的时间间隔执行某些活动。

借助 ThreadX 应用程序计时器,应用程序能够按特定的时间间隔执行应用程序 C 函数。应用程序计时器也可能只过期一次。这种类型的计时器称为“单次计时器”,而重复间隔计时器称为“定期计时器”。

每个应用程序计时器都是一个公用资源。ThreadX 对如何使用应用程序计时器没有任何限制。

2.1.2  计时器间隔

在 ThreadX 中,时间间隔通过定期计时器中断来测量。每个计时器中断称为计时器时钟周期。计时器时钟周期之间的实际时间由应用程序指定,但 10 毫秒是大多数实现的标准时间。定期计时器设置通常位于 tx_initialize_low_level 程序集文件中。

值得一提的是,基础硬件必须能够生成定期中断,应用程序计时器才会正常运行。在某些情况下,处理器具有内置的定期中断功能。如果处理器没有此功能,用户的主板必须包含可生成定期中断的外围设备。

即使没有定期中断源,ThreadX 仍可正常工作。但随后会禁用所有与计时器相关的处理。这包括时间切片、挂起超时和计时器服务。

2.1.3  计时器准确性

计时器过期时间根据时钟周期指定。达到每个计时器时钟周期时,指定到期值将减一。由于应用程序计时器可在计时器中断(或计时器时钟周期)之前启用,因此,实际过期时间可能会提前一个时钟周期。

如果计时器时钟周期速率为 10 毫秒,应用程序计时器可能会提前 10 毫秒过期。与 1 秒计时器相比,这对 10 毫秒计时器更重要。当然,增加计时器中断频率会减少此误差范围。

2.1.4  计时器执行

应用程序计时器按照其激活的顺序执行。例如,如果创建了三个具有相同过期值的计时器并已激活,这些计时器对应的过期函数将保证按它们激活的顺序执行。

2.1.5  创建应用程序计时器

应用程序计时器由应用程序线程在初始化期间或运行时创建。应用程序中应用程序计时器的数量没有限制。

2.1.6  运行时应用程序计时器性能信息

ThreadX 提供可选的运行时应用程序计时器性能信息。如果 ThreadX 库和应用程序是在定义 TX_TIMER_ENABLE_PERFORMANCE_INFO 的情况下生成的,ThreadX 会累积以下信息。

整个系统的总数:

激活数

停用数

重新激活(定期计时器)

expirations

过期调整数

每个应用程序计时器的总数:

激活数

停用数

重新激活(定期计时器)

expirations

过期调整数

此信息在运行时通过 tx_timer_performance_info_get 和 tx_timer_performance_system_info_get 服务提供。应用程序计时器性能信息在确定应用程序是否正常运行时非常有用。此信息对于优化应用程序也很有用。

2.1.7  应用程序计时器控制块 TX_TIMER

每个应用程序计时器的特征都可在其控制块中找到。该控制块包含诸如 32 位过期标识值等有用信息。此结构在 tx_api.h 文件中定义。

应用程序计时器控制块可以位于内存中的任意位置,但最常见的是通过在任何函数的作用域外部定义该控件块来使其成为全局结构。

2.1.8  计时器过多

默认情况下,应用程序计时器在优先级为 0 时运行的隐藏系统线程中执行,该线程的优先级通常比任何应用程序线程都高。因此,在应用程序计时器内进行处理应保持最小值。

如果可能,还应尽可能避免使用在每个时钟周期过期的计时器。这种情况可能导致应用程序的开销过大。

如前所述,应用程序计时器在隐藏的系统线程中执行。因此,请不要在应用程序计时器的过期函数内执行任何 ThreadX 服务调用时选择挂起。

2.1.9  相对时间

除了前面所述的应用程序计时器,ThreadX 还提供单个连续递增的 32 位时钟周期计数器。每次发生计时器中断时,时钟周期计数器或时间就会加一。

应用程序可以通过分别调用 tx_time_get 和 tx_time_set 来读取或设置此 32 位计数器。此时钟周期计数器的使用完全由应用程序确定。ThreadX 不在内部使用此计时器。

2.2  Azure ThreadX 定时器组的相关函数

tx_timer_create 创建应用程序计时器

UINT tx_timer_create(
    TX_TIMER *timer_ptr, 
    CHAR *name_ptr,
    VOID (*expiration_function)(ULONG),
    ULONG expiration_input, 
    ULONG initial_ticks,
    ULONG reschedule_ticks, 
    UINT auto_activate);

说明

此服务创建具有指定过期函数和定期的应用程序计时器。

参数

timer_ptr:

指向计时器控制块的指针。

name_ptr:

指向计时器名称的指针。

expiration_function:

在计时器过期时要调用的应用程序函数。

expiration_input:

在计时器过期时要传递到过期函数的输入。

initial_ticks:

指定计时器过期的初始时钟周期数。合法值的范围为 1 至 0xFFFFFFFF。

reschedule_ticks:

指定第一个计时器过期后所有计时器过期的时钟周期数。如果此参数为 0,则计时器是一次性的。否则,对于周期性计时器,合法值的范围为 1 至 0xFFFFFFFF。

备注

一次性计时器过期后,必须通过 tx_timer_change 将其重置,然后才能再次激活。

auto_activate:

确定创建期间是否自动激活计时器。如果此值为 TX_AUTO_ACTIVATE (0x01),则激活计时器。否则,如果选择了值 TX_NO_ACTIVATE (0x00),则所创建的计时器处于非活动状态。在这种情况下,随后需要调用 tx_timer_activate 服务来实际启动计时器。

返回值

TX_SUCCESS:

(0X00) 成功创建应用程序计时器。

TX_TIMER_ERROR:

(0X15) 应用程序计时器指针无效。指针为 NULL 或已创建计时器。

TX_TICK_ERROR:

(0x16) 为初始时钟周期提供的值无效(零)。

TX_ACTIVATE_ERROR:

(0x17) 选择的激活无效。

NX_CALLER_ERROR:

(0x13) 此服务的调用方无效。

示例

TX_TIMER my_timer;
UINT status;

/* Create an application timer that executes
"my_timer_function" after 100 ticks initially and then
after every 25 ticks. This timer is specified to start
immediately! */
status = tx_timer_create(&my_timer,"my_timer_name",
    my_timer_function, 0x1234, 100, 25,
    TX_AUTO_ACTIVATE);

/* If status equals TX_SUCCESS, my_timer_function will
be called 100 timer ticks later and then called every
25 timer ticks. Note that the value 0x1234 is passed to
my_timer_function every time it is called. */

另请参阅

tx_timer_activate

tx_timer_change

tx_timer_deactivate

tx_timer_delete

tx_timer_info_get

tx_timer_performance_info_get

tx_timer_performance_system_info_get

具体函数的中文说明可以参考:

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

具体函数的英文说明可以参考:

https://docs.microsoft.com/en-us/azure/rtos/threadx/threadx-smp/chapter4

2.3  定时器组的应用演示

2.3.1  工程目录的建立

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

image.png

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

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

3 ThreadX的定时器组应用

3.1  代码实现

下载调试默认会运行到main()函数,如下为全部实现的代码。

Demo演示代码

/* This is a small demo of the high-performance ThreadX kernel.  It includes examples of six
   threads of different priorities, using a message queue, semaphore, and an event flags group.  */

#include "tx_api.h"
#include "delay.h"
#include "led.h"
#include "key.h"
#include "uart.h"

#define DEMO_STACK_SIZE         1024

#define THREAD0_PRIORITY 1
#define THREAD0_PREEMPTION_THRESHOLD 1


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

TX_TIMER MyTimer;

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

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

/* Define thread prototypes.  */

void    thread_0_entry(ULONG thread_input);


volatile unsigned int bootloop;


void System_Init(void);
void AppThreadCreate(void);
void AppModuleCreate(void);


/* Define main entry point.  */

int main()
{
    System_Init();

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


/* Define what the initial system looks like.  */

void tx_application_define(void* first_unused_memory)
{
    AppThreadCreate();
    AppModuleCreate();

}

void System_Init(void)
{
    DELAY_Init();//can not use systick
    LED_Init();
    KEY_Init();
    CONSOLE_Init(115200);
    printf("!!! Start !!!\r\n");  

}

void AppThreadCreate(void)
{
    /* Create thread 0.  */
    tx_thread_create(
            &thread_0, 
            "thread 0", 
            thread_0_entry, 
            0,
            thread_0_stack, 
            DEMO_STACK_SIZE,
            THREAD0_PRIORITY, 
            THREAD0_PREEMPTION_THRESHOLD, 
            TX_NO_TIME_SLICE, 
            TX_AUTO_START);

}

void AppModuleCreate(void)
{        
    /*  Create a timer group. */
    tx_timer_create(&MyTimer,
                "My Timer",
                TimerCallback, 
                0,       /* The parameters passed */
                100,     /* Set the initial delay for timer time overflow */
                1000,    /* Set the timer run period after the initial delay */
                TX_AUTO_ACTIVATE);  /* Activate the timer */
}


/*    The callback function for the timer group. */
void TimerCallback(ULONG thread_input)
{

    LED2_TOGGLE();

}

/* Define the test threads.  */
void thread_0_entry(ULONG thread_input)
{
    /* This thread simply controls LED flashing to indicate that the system is running  */
    while(1)
    {
        /* Increment the thread counter.  */
        thread_0_counter++;
LED1_TOGGLE();
        /* Sleep for 300 ticks.  */
        tx_thread_sleep(300);

    }
}

3.2  下载与调试

运行程序,板载LED1闪烁,表示当前系统正在运行。

观察LED2间隔1s闪烁。

程序中创建定时器组,设置溢出周期为1000ms,在定时器回调函数中配置LED2引脚翻转,激活定时器。当计时周期到时,LED2引脚翻转,运行现象是LED2间隔1s闪烁,Demo演示成功。

4 小结

Azure RTOS ThreadX提供定时器组能够使应用程序按照特定的时间间隔执行,结合MM32F3270的强大性能,可以实现Azure RTOS广泛的应用场景。

作者:灵动MM32
文章来源:灵动MM32MCU

推荐阅读

更多MM32F5系列资料请关注灵动MM32 MCU专栏。如想进行MM32相关芯片技术交流,请添加极术小姐姐微信(id:aijishu20)加入微信群。
推荐阅读
关注数
6108
内容数
272
灵动MM32 MCU相关技术知识,欢迎关注~
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息