实时操作系统
本节介绍如何在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_4和70000字节的堆大小来为touchgfx应用程序提供足够大的堆。
Tip
调用以下函数时,将进入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(代码生成选项)作为外部函数。
STM32CubeMX将在生成代码时在main.c
中创建TouchGFX任务,从而启动TouchGFX主循环。
为确保SYS Timebase Source始终接收到用于某些系统函数(包括HAL_Delay()
)的计时,指定计时器的NVIC优先级必须高于(即具有较低的数值)最高中断优先级,该优先级可用于调用中断安全FreeRTOS API函数。 在FreeRTOS中可配置为configMAX_SYSCALL_INTERRUPT_PRIORITY
,默认设置为5
。 大多数情况下,用于SYS时基源的计时器为TIM6
。
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(使用静态分配)”。
Caution
- 当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设置选项卡,并将TX_TIMER_TICKS_PER_SECOND
设置为1000,以获得每毫秒一次的计时。
在X-CUBEAZRTOS-XX菜单中完成配置后,导航至TouchGFX Generator,选择ThreadX作为Real Time Operating System(实时操作系统),并定义Memory Pool Size(内存池大小
)和Memory Stack Size(内存堆栈大小)