即時作業系統
本節說明如何在STM32CubeMX設定不同的即時作業系統,以搭配使用TouchGFX Generator產生的程式碼,實現可正常運作的TouchGFX OSAL。
一般來說,TouchGFX主迴圈是以呼叫MX_TouchGFX_Process()
函數的方式啟動。 呼叫此函數的位置,應該位在開發人員執行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()
,其中TouchGFX會在偵測到VSYNC
時開始處理下一個畫面。
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時也適用相同設定。
Having enabled FreeRTOS in a project, navigate to the FREERTOS menu found under Middleware in the "Categories" list on the left side of the project menu in STM32CubeMX. 將TICK_RATE_HZ
設定為1000,也就是每毫秒一個時標。 設定Memory Management scheme
(記憶體管理配置)及TOTAL_HEAP_SIZE
。 我們在此範例中使用heap_4及70000位元組的堆積大小,讓堆積足以因應TouchGFX應用程式需求。
The required heap size depends on the application, as all software modules can allocate memory in the FreeRTOS heap.
A standard TouchGFX application generated with CubeMX allocates the following objects in the FreeRTOS heap:
Object | Size |
---|---|
Frame buffer semaphore | 80 bytes |
VSync message queue | 84 bytes |
TouchGFX task control block | 96 bytes |
TouchGFX task stack | 16,384 bytes |
Default task control block | 96 bytes |
Default task stack | 512 bytes |
Total | 17,252 bytes |
This can be reduced by lowering the stack size (see below).
The Default task is not used by TouchGFX, but is created by default by CubeMX. It can be deleted.
If your application is using the LIBJPEG middleware or software based MJPEG video with TouchGFX you need more heap space. LIBJPEG is allocating many objects in the heap during decompression. The LIBJPEG memory usage depends on the video or images decompressed (mostly resolution). For one of the videos available in the TouchGFX Designer of resolution 480*272, the memory used by LIBJPEG is 42,600 bytes.
Adding this number to the heap requirements gives:
Object | Size |
---|---|
Standard objects | 17,252 bytes |
LIBJPEG objects | 42,600 bytes |
Total | 59,852 bytes |
FreeRTOS aligns the heap array on 8-bytes in both ends (loosing a few bytes) and allocates a linked list of free blocks in the heap. So it is necessary to round up the calculated number a bit, for example to 60*1024 bytes.
Main loop
呼叫下列函數時會進入TouchGFX主迴圈。
void MX_TouchGFX_Process(void);
開發人員可在STM32CubeMX設定自己的客製工作,讓TouchGFX主迴圈在其中執行,或使用TouchGFX Generator產生的TouchGFX_Task()
控制代碼。
客製工作
The function void touchgfx_taskEntry(void)
is called by MX_TouchGFX_Process()
.
void MX_TouchGFX_Process(void)
{
// Calling forward to touchgfx_taskEntry in C++ domain
touchgfx_taskEntry();
}
開發人員必須在打算執行TouchGFX主迴圈的工作中,於工作處理常式呼叫MX_TouchGFX_Process()
。 If the developer has configured a FreeRTOS task in STM32CubeMX called MyTask
for instance, then the following example shows how MX_TouchGFX_Process()
should be called in the custom task handler to start TouchGFX.
void StartMyTask(void *argument)
{
/* USER CODE BEGIN 5 */
MX_TouchGFX_Process();
/* USER CODE END 5 */
}
TouchGFX工作
TouchGFX主迴圈可於TouchGFX Task (TouchGFX工作)中執行。 TouchGFX Generator會定義工作處理常式以呼叫名為TouchGFX_Task()
的touchgfx_taskEntry()
,位於app_touchgfx.c
內部。
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」作為Entry Function,並且要有夠大的堆疊,其中的Code Generation Option (程式碼產生選項)要設定為As external (外部)。
Here we have configured a stack size of 4,096 words (equal to 16,384 bytes). The required stack size depends on the application (mostly on the number of widgets updated at the same time). A stack size of 2048 words is enough for most applications, and many can go even further down in stack size.
Tip
STM32CubeMX將在產生程式碼時於main.c
建立TouchGFX工作,用於啟動TouchGFX主迴圈。
為了確保SYS時基源一定收到時標,以供包含HAL_Delay()
在內的系統函數使用,指派計時器的NVIC優先順序必須高於(亦即數字較小)可呼叫中斷安全FreeRTOS API函數的最高中斷優先順序。 這可在FreeRTOS中設定為configMAX_SYSCALL_INTERRUPT_PRIORITY
,並在預設設定為5
。 在大部分情況下,用於SYS時基源的計時器為TIM6
。
ThreadX
本節說明如何使用ThreadX作為X-CUBE套裝軟體或原生ThreadX中介軟體以設定TouchGFX專案。
原生中介軟體設定
以下範例顯示如何為STM32U5 MCU設定ThreadX,其中ThreadX可作為原生中介軟體使用。
在專案中啟用執行緒原生中介軟體,導覽至STM32CubeMX專案選單左側「Categories」(類別)清單中Middleware (中介軟體)之下的THREADX選單。 啟用Core
(核心):請由Mode (模式)清單啟用。 將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 (動態分配)可能無法正常運作。
STM32CubeMX並不負責產生程式碼,在使用ThreadX時分配及排程工作。 因此呼叫函數MX_TouchGFX_PreOSInit()
用於初始化TouchGFX架構時,將由STM32CubeMX產生該呼叫。 在此之後,將會產生另一次呼叫,以呼叫函數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()
。 此程式碼將插入至函數tx_application_define()
的適當位置;該函數位於app_azure_rtos.c檔案之中,TouchGFX工作位元組集區就是在此定義。
tx_application_define()
函數是由ThreadX核心在初始化時間呼叫。 ThreadX核心啟動時,TouchGFX執行緒會稍後啟動。
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,其中ThreadX可作為X-CUBE套裝軟體使用。
在專案中啟用執行緒作為X-CUBE套裝軟體,導覽至STM32CubeMX專案選單左側「Categories」(類別)清單Software Packs (套裝軟體)之下的X-CUBE-AZRTOS-XX選單,並將Memory Allocation
(記憶體分配)設定為Use Static MemPool Allocation (使用靜態記憶體集區分配)。
選擇ThreadX設定索引標籤,將TX_TIMER_TICKS_PER_SECOND
設定為1000,也就是每毫秒一個時標。
在X-CUBE-AZRTOS-XX選單完成設定後,請導覽至TouchGFX Generator選擇ThreadX
作為Real Time Operating System (即時作業系統),並為TouchGFX工作定義Memory Pool Size
(記憶體集區大小)和Memory Stack Size
(記憶體堆疊大小)。