跳转到主要内容

实时操作系统

本节介绍如何在STM32CubeMX中配置不同的Real Time Operating System(实时操作系统),与TouchGFX Generator生成的代码配合使用,实现能正常运行的TouchGFX OSAL。

通常,通过调用函数MX_TouchGFX_Process()启动TouchGFX主循环。 对该函数的调用应位于开发人员希望运行TouchGFX的任务处理程序内。

无OS

当TouchGFX在无操作系统的情况下运行时,TouchGFX主循环在main()中的无限while循环中运行,MCU通过调用MX_TouchGFX_Process()来等待VSYNC信号。

main.c
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
MX_TouchGFX_Process();
/* USER CODE BEGIN 3 */
}

MX_TouchGFX_Process()调用TouchGFX_taskEntry(),当检测到VSYNC时,TouchGFX开始处理下一帧。

TouchGFXConfiguration.cpp
void touchgfx_taskEntry()
{
/*
* Main event loop will check for VSYNC signal, and then process next frame.
*
* 注意,如果无VSYNC信号,该函数将立即返回。
*/
if (OSWrappers::isVSyncAvailable())
{
hal.backPorchExited();
}
}

FreeRTOS (CMSIS V1 & CMSIS V2)

以下示例显示了FreeRTOS (CMSIS V2)的配置方法,以便与TouchGFX配合使用。 如运行CMSIS V1,适用同样配置。

在项目中启用FreeRTOS后,导航至STM32CubeMX项目菜单左侧“Categories(类别)”列表中间件下的FreeRTOS菜单。 将TICK_RATE_HZ设置为1000,以获取每毫秒的计时 配置Memory Management(内存管理)方案TOTAL_HEAP_SIZE。 本例中,我们使用heap_470000字节的堆大小来为touchgfx应用程序提供足够大的堆。

为FreeRTOS创建TouchGFX任务

Tip
通过针对特定应用程序的试错,可以找到更为理想的TOTAL_HEAP_SIZE

调用以下函数时,将进入TouchGFX主循环。

void MX_TouchGFX_Process(void);

开发人员可以在STM32CubeMX中配置自定义任务来运行TouchGFX主循环,也可以使用TouchGFX Generator生成的TouchGFX_task()句柄。

自定义任务

函数void  touchgfx_taskEntry(void)MX_TouchGFX_Process()调用。

void MX_TouchGFX_Process(void)
{
// Calling forward to touchgfx_taskEntry in C++ domain
touchgfx_taskEntry();
}

开发人员需要在运行TouchGFX主循环的任务中调用MX_TouchGFX_Process() 。 如果开发人员在STM32CubeMX中配置了一个名为MyTask的FeeRTOS任务,那么下面的示例显示了如何在自定义任务处理程序中调用MX_TouchGFX_Process()来启动TouchGFX。

void StartMyTask(void *argument)
{
/* USER CODE BEGIN 5 */
MX_TouchGFX_Process();
/* USER CODE END 5 */
}

TouchGFX任务

TouchGFX主循环可在TouchGFX任务中运行。 TouchGFX Generator 定义了一个任务处理程序,在app_touchgfx.c中调用名为TouchGFX_Task()的函数,在此函数中调用touchgfx_taskEntry()

app_touchgfx.c
void TouchGFX_Task(void *argument)
{
// Calling forward to touchgfx_taskEntry in C++ domain
touchgfx_taskEntry();
}

要让FreeRTOS调度程序调度该任务,必须在STM32CubeMX的FREERTOS菜单中创建该任务。 在“Tasks and Queues(任务和队列)”选项卡中,添加一个任务,将“TouchGFX_Task”作为入口函数,并将一个足够大的堆栈和Code Generation Option(代码生成选项)作为外部函数

为FreeRTOS创建TouchGFX任务

STM32CubeMX将在生成代码时在main.c中创建TouchGFX任务,从而启动TouchGFX主循环。

为确保SYS Timebase Source始终接收到用于某些系统函数(包括HAL_Delay())的计时,指定计时器的NVIC优先级必须高于(即具有较低的数值)最高中断优先级,该优先级可用于调用中断安全FreeRTOS API函数。 在FreeRTOS中可配置为configMAX_SYSCALL_INTERRUPT_PRIORITY,默认设置为5。 大多数情况下,用于SYS时基源的计时器为TIM6

设置系统计时器的NVIC优先级

ThreadX

本节展示了如何使用ThreadX作为X-CUBE软件包或本机ThreadX中间件来配置TouchGFX项目。

本机中间件配置

以下示例显示如何为STM32U5 MCU配置ThreadX,该MCU将ThreadX用作本机中间件。

在项目中启用线程本机中间件后,导航至STM32CubeMX项目菜单左侧“Categories(类别)”列表中间件下的THREADX菜单。 从Mode(模式)列表启用Core。 将TX_TIMER_TICKS_PER_SECOND设置为1000以获得每毫秒一次的计时,并将“Memory Pool Allocation(内存池分配)”设置为“Use Static Allocation(使用静态分配)”

启用并配置本机ThreadX中间件

Caution
  • STM32CubeMX在使用Native ThreadX中间件时,仅当Memory Pool Allocation(内存池分配)设置为Use Static Allocation(使用静态分配)时才会生成完整的OSAL
    • Memory Pool Allocation(内存池分配)设置为使用Use Dynamic Allocation(动态分配)时:
      • 用户必须在生成的app_azure_rtos.c文件的USER CODE BEGIN DYNAMIC_MEM_ALLOC部分中添加缺失的代码。
      • 用户还需要根据生成的app_azure_rtos.c文件中代码注释所描述的建议更新链接器文件。
      • 根据STM32CubeMX版本,Dynamic Allocation(动态分配)可能无法正常工作。

    在使用ThreadX时,STM32CubeMX不负责生成分配和调度任务的代码。 为此,STM32CubeMX生成对函数MX_TouchGFX_PreOSInit()的调用,该函数将对TouchGFX 框架进行初始化。 之后,生成对函数MX_ThreadX_Init()的另一个调用,初始化并启动ThreadX内核。

    main.c
    int main(void)
    {
    ...
    /* Call PreOsInit function */
    MX_TouchGFX_PreOSInit();
    ...
    MX_ThreadX_Init();
    ...
    }

    TouchGFX Generator将生成MX_TouchGFX_Init()函数,该函数将创建TouchGFX线程。

    app_touchgfx.c
    UINT MX_TouchGFX_Init(VOID *memory_ptr)
    {
    UINT ret = TX_SUCCESS;
    CHAR *pointer = 0;

    /* Allocate the stack for TouchGFX Thread. */
    if (tx_byte_allocate((TX_BYTE_POOL*)memory_ptr, (VOID **) &pointer,
    TOUCHGFX_STACK_SIZE, TX_NO_WAIT) != TX_SUCCESS)
    {
    ret = TX_POOL_ERROR;
    }

    /* Create TouchGFX Thread */
    else if (tx_thread_create(&TouchGFXThread, (CHAR *)"TouchGFX", TouchGFX_Task, 0,
    pointer, TOUCHGFX_STACK_SIZE,
    5, 5,
    TX_NO_TIME_SLICE, TX_AUTO_START) != TX_SUCCESS)
    {
    ret = TX_THREAD_ERROR;
    }

    return ret;
    }

    然后,TouchGFX Generator将生成创建TouchGFX任务字节池的代码,并调用MX_TouchGFX_Init()。 此代码插入app_azure_rtos.c文件中函数tx_application_define()的适当位置,对TouchGFX任务字节池进行定义。

    tx_application_define()函数在初始化时由ThreadX内核调用。 TouchGFX线程将在稍后启动ThreadX内核时启动。

    app_azure_rtos.c
    #include "app_touchgfx.h"
    ...
    __ALIGN_BEGIN static UCHAR touchgfx_byte_pool_buffer[TOUCHGFX_APP_MEM_POOL_SIZE] __ALIGN_END;
    static TX_BYTE_POOL touchgfx_app_byte_pool;
    ...
    VOID tx_application_define(VOID *first_unused_memory)
    {
    ...
    if (tx_byte_pool_create(&touchgfx_app_byte_pool, "TouchGFX App memory pool", touchgfx_byte_pool_buffer, TOUCHGFX_APP_MEM_POOL_SIZE) != TX_SUCCESS)
    {
    /* USER CODE BEGIN TouchGFX_Byte_Pool_Error */

    /* USER CODE END TouchGFX_Byte_Pool_Error */
    }
    else
    {
    /* USER CODE BEGIN TouchGFX_Byte_Pool_Success */

    /* USER CODE END TouchGFX_Byte_Pool_Success */

    memory_ptr = (VOID *)&touchgfx_app_byte_pool;
    if (MX_TouchGFX_Init(memory_ptr) != TX_SUCCESS)
    {
    /* USER CODE BEGIN MX_X-CUBE-TOUCHGFX_Init_Error */

    /* USER CODE END MX_X-CUBE-TOUCHGFX_Init_Error */
    }
    /* USER CODE BEGIN MX_X-CUBE-TOUCHGFX_Init_Success */

    /* USER CODE END MX_X-CUBE-TOUCHGFX_Init_Success */
    }

    ...
    }

    X-CUBE-AZRTOS配置

    以下示例显示如何为STM32H7 MCU配置ThreadX,该MCU的ThreadX作为X-CUBE软件包提供。

    在项目中将ThreadX作为X-CUBE软件包启用后,导航至STM32CubeMX中项目菜单左侧“Categories(类别)”列表软件包下X-CUBE-AZRTOS-XX菜单,并将Memory Allocation(内存分配)设置为使用Use Static MemPool Allocation(静态内存池分配)

    ThreadX配置 - X-CUBE-AZRTOS软件包

    选择ThreadX设置选项卡,并将TX_TIMER_TICKS_PER_SECOND设置为1000,以获得每毫秒一次的计时。

    ThreadX配置 - X-CUBE-AZRTOS软件包

    X-CUBEAZRTOS-XX菜单中完成配置后,导航至TouchGFX Generator,选择ThreadX作为Real Time Operating System(实时操作系统),并定义Memory Pool Size(内存池大小)和Memory Stack Size(内存堆栈大小)

    为FreeRTOS创建TouchGFX任务

    Caution
    不幸的是,根据TouchGFX 4.21.0,最新版X-CUBE-ARTOS-XX生成的代码不会在ThreadX驱动程序中创建和初始化TouchGFX Task。 这需要开发人员从Native Middleware配置部分显示的代码片段中手动添加代码,以便应用程序正常工作。