주요 내용으로 건너뛰기

Real Time Operating System

이 섹션에서는 기능하는 TouchGFX OSAL을 갖도록 TouchGFX Generator에서 생성된 코드와 함께 작동하도록 STM32CubeMX에서 다양한 실시간 운영 체제를 구성하는 방법을 보여줍니다.

일반적으로 TouchGFX 메인 루프는 MX_TouchGFX_Process() 함수를 호출하는 것으로 시작됩니다. 이 함수에 대한 호출은 개발자가 TouchGFX를 실행하려고 하는 작업 핸들러 내부에 있어야 합니다.

No OS

TouchGFX가 운영 체제 없이 실행될 때 TouchGFX 메인 루프는 MCU가 MX_TouchGFX_Process()를 호출하여 VSYNC 신호를 풀링하는 main()의 무한 while 루프에서 실행됩니다.

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.
*
* Note This function returns immediately if there is no VSYNC signal.
*/
if (OSWrappers::isVSyncAvailable())
{
hal.backPorchExited();
}
}

FreeRTOS(CMSIS V1 & CMSIS V2)

다음 예제는 FreeRTOS(CMSIS V2)가 TouchGFX에서 작동하도록 구성하는 방법을 보여줍니다. CMSIS V1을 실행하는 경우 동일한 구성이 적용됩니다.

프로젝트에서 FreeRTOS 활성화를 수행하면 STM32CubeMX의 프로젝트 메뉴 왼쪽에 있는 "Categories" 목록의 Middleware 아래에 있는 THREADX 메뉴로 이동합니다. 밀리 초마다 한 번씩 틱이 발생하도록 TICK_RATE_HZ1000으로 설정합니다. Memory Management schemeTOTAL_HEAP_SIZE를 구성합니다. 이 예제에서는 touchgfx 애플리케이션에서 충분히 큰 힙을 갖도록 heap_4와 힙 크기 70000바이트를 사용하고 있습니다.

FreeRTOS용 TouchGFX 작업 생성

Tip
특정 애플리케이션에 대한 시행 착오를 통해 보다 최적의 TOTAL_HEAP_SIZE를 찾을 수 있습니다.

다음 함수를 호출할 때 TouchGFX 메인 루프에 들어갑니다.

void MX_TouchGFX_Process(void);

개발자는 TouchGFX 메인 루프가 실행되어야 하는 STM32CubeMX에서 자체 커스텀 테스크를 구성하거나, TouchGFX Generator에서 생성되는 TouchGFX_Task() 핸들을 사용할 수 있습니다.

Custom 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 테스크를 구성한 경우, 다음 예제는 TouchGFX를 시작하기 위해 커스텀 테스크 핸들러에서 MX_TouchGFX_Process()를 호출하는 방법을 보여줍니다.

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

TouchGFX 작업

TouchGFX 메인 루프는 TouchGFX Task에서 실행될 수 있습니다. 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 스케줄러에서 이 작업을 예약하려면 FREERTOS 메뉴의 STM32CubeMX에서 작업을 생성해야 합니다. Tasks and Queues 탭에서 "TouchGFX_Task"Entry Function이고, Code Generation OptionAs external인 테스크를 추가합니다.

FreeRTOS용 TouchGFX 작업 생성

STM32CubeMX는 코드를 생성할 때 main.c에 TouchGFX Task를 생성하여 TouchGFX 메인 루프를 시작합니다.

HAL_Delay()를 포함하는 일부 시스템 함수에서 사용하는 SYS Timebase Source가 항상 tick을 받을 수 있도록 하려면 할당된 타이머의 NVIC 우선순위가 안전한 FreeRTOS API 함수를 인터럽트하는 호출을 생성할 때 사용하는 최고 인터럽트 우선순위보다 높아야 합니다(예: 더 낮은 숫자 ). 이는 FreeRTOS에서 configMAX_SYSCALL_INTERRUPT_PRIORITY로 구성할 수 있으며 기본적으로 5로 설정됩니다. 대부분의 경우 SYS Timebase Source에 사용하는 타이머는 TIM6입니다.

sysTick 타이머의 NVIC 우선순위 설정

ThreadX

이 섹션에서는 ThreadX를 X-CUBE Software Pack이나 Native ThreadX Middleware를 사용해서 TouchGFX 프로젝트를 구성하는 방법을 보여줍니다.

Native Middleware 구성

다음 예제는 ThreadX를 네이티브 미들웨어로 사용할 수 있도록 STM32U5 MCU에 대해 ThreadX를 구성하는 방법을 보여줍니다.

프로젝트에서 Thread 네이티브 미들웨어 활성화를 수행하면 STM32CubeMX의 프로젝트 메뉴 왼쪽에 있는 "Categories" 목록의 Middleware 아래에 있는 THREADX 메뉴로 이동합니다. Mode 목록에서 Core를 활성화합니다. TX_TIMER_TICKS_PER_SECOND1000으로 설정해서 밀리 초마다 한 번씩 틱을 발생시키고, Memory Pool AllocationUse Static Allocation으로 설정합니다.

Native ThreadX Middleware 활성화 및 구성

Caution
  • STM32CubeMX는Native ThreadX Middleware를 사용할 때 Memory Pool AllocationUse Static Allocation으로 설정되었을 때만 전체 OSAL을 생성합니다.
    • Memory Pool AllocationUse Dynamic Allocation으로 설정된 경우에는 다음과 같이 하십시오.
      • 사용자는 생성된 app_azure_rtos.c 파일의 USER CODE BEGIN DYNAMIC_MEM_ALLOC 섹션에 누락된 코드를 추가해야 합니다.
      • 또한, 생성된 app_azure_rtos.c 파일의 코드 주석에 설명되어 있는 권장 사항에 따라 링커 파일을 업데이트해야 합니다.
      • STM32CubeMX 버전에 따라 Dynamic Allocation이 제대로 작동하지 않을 수 있습니다.

    STM32CubeMX는 ThreadX를 사용할 때 테스크를 할당 및 스케쥴링하는 코드를 생성할 책임이 없습니다. 이러한 이유로 TouchGFX 프레임워크를 초기화하는 MX_TouchGFX_PreOSInit() 함수에 대한 호출이 STM32CubeMX에서 생성됩니다. 그런 다음, ThreadX 커널을 초기화하고 시작하는 MX_ThreadX_Init() 함수에 대한 또 다른 호출이 생성됩니다.

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

    TouchGFX Generator는 TouchGFX 스레드를 생성하는 MX_TouchGFX_Init() 함수를 생성합니다.

    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()를 호출합니다. 이 코드는 TouchGFX Task 바이트 풀이 정의된 app_azure_rtos.c 파일에서 함수 tx_application_define()의 해당 위치에 삽입됩니다.

    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 구성

    다음 예제는 ThreadX를 X-CUBE Software Pack으로 사용할 수 있도록 STM32H7 MCU에 대해 ThreadX를 구성하는 방법을 보여줍니다.

    프로젝트에서X-CUBE Software Pack으로서 Thread 활성화를 수행하면 STM32CubeMX의 프로젝트 메뉴 왼쪽에 있는 "Categories" 목록의 Software Packs 아래에 보이는 X-CUBE-AZRTOS-XX 메뉴로 이동해서 Use Static MemPool Allocation으로 Memory Allocation을 설정합니다.

    ThreadX 구성 - X-CUBE-AZRTOS 소프트웨어 팩

    ThreadX 설정 탭을 선택하고, TX_TIMER_TICKS_PER_SECOND1000으로 설정해서 밀리 초마다 한 번씩 틱을 발생시킵니다.

    ThreadX 구성 - X-CUBE-AZRTOS 소프트웨어 팩

    X-CUBE-AZRTOS-XX 메뉴에서 구성이 완료되면, TouchGFX Generator로 이동해서 실시간 운영 체제ThreadX를 선택하고 TouchGFX 작업에 대한 Memory Pool SizeMemory Stack Size를 정의합니다.

    FreeRTOS용 TouchGFX 작업 생성

    Caution
    TouchGFX 4.21.0에서는 최신 버전의 X-CUBE-AZRTOS-XX에서 생성된 코드가 안타깝게도 ThreadX 드라이버에서 TouchGFX Task를 생성 및 초기화하지 않습니다. 따라서, 작동 중인 애플리케이션을 가지려면 개발자가 Native Middleware 구성 섹션에 나와 있는 코드 스니펫에서 수동으로 코드 추가를 수행해야 합니다.