即時作業系統
本節說明如何在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時也適用相同設定。
在專案中啟用FreeRTOS,導覽至STM32CubeMX專案選單左側「Categories」(類別)清單之中Middleware (中介軟體)之下的FREERTOS選單。 將TICK_RATE_HZ
設定為1000,也就是每毫秒一個時標。 設定Memory Management scheme
(記憶體管理配置)及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()
。 舉例來說,如果開發人員在名為MyTask
的STM32CubeMX之中設定了FeeRTOS工作,以下範例顯示應如何在客製工作處理常式中呼叫MX_TouchGFX_Process()
,以便啟動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 (外部)。
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
(記憶體堆疊大小)。