주요 내용으로 건너뛰기

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을 실행하는 경우 동일한 구성이 적용됩니다.

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

FreeRTOS용 TouchGFX 작업 생성

필요한 heap 크기는 애플리케이션에 따라 달라집니다. 모든 소프트웨어 모듈이 FreeRTOS heap에서 메모리를 할당할 수 있기 때문입니다.

CubeMX로 생성된 표준 TouchGFX 애플리케이션은 FreeRTOS heap에 다음 객체를 할당합니다:

객체크기
Frame buffer semaphore80바이트
VSync message queue84바이트
TouchGFX task control block96바이트
TouchGFX task stack16,384바이트
기본 task control block96바이트
기본 task stack512바이트
17,252바이트

Stack 크기를 줄임으로써 이를 감소시킬 수 있습니다(아래 참조).

Default task는 TouchGFX에서 사용되지 않지만, CubeMX에 의해 기본적으로 생성됩니다. 삭제할 수 있습니다.

애플리케이션이 LIBJPEG 미들웨어나 소프트웨어 기반 MJPEG 비디오를 TouchGFX와 함께 사용하는 경우 더 많은 hea 공간이 필요합니다. LIBJPEG는 압축 해제 중에 heap에 많은 객체를 할당하고 있습니다. LIBJPEG의 메모리 사용량은 압축 해제되는 비디오 또는 이미지에 따라 달라집니다(주로 해상도에 영향을 받음). TouchGFX Designer에서 사용할 수 있는 480*272 해상도의 비디오 중 하나에 대해, LIBJPEG가 사용하는 메모리는 42,600바이트입니다.

이 숫자를 heap 요구 사항에 더하면 다음과 같습니다:

객체크기
표준 객체17,252바이트
LIBJPEG 객체42,600바이트
59,852바이트

FreeRTOS는 heap 배열의 양 끝단을 8바이트 단위로 정렬하며(이 과정에서 몇 바이트의 메모리 손실이 발생함), heap 내에 사용 가능한 블록들의 linked list를 할당합니다. 따라서 계산된 값을 약간 올림하여, 예를 들어 60*1024바이트로 설정할 필요가 있습니다.

메인 루프

다음 함수를 호출할 때 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에서 FreeRTOS 작업을 구성한 경우, 다음 예제는 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 작업 생성

여기서 stack 크기를 4,096단어(16,384바이트에 해당)로 설정했습니다. 필요한 stack 크기는 애플리케이션에 따라 달라집니다(주로 동시에 업데이트되는 위젯의 수에 따름). 대부분의 애플리케이션에는 2,048단어의 stack 크기가 충분하며, 많은 경우 stack 크기를 더 줄일 수도 있습니다.

Tip
FreeRTOS는 stack 사용량을 검사하고 오버플로를 감지하는 기능을 제공합니다.

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 구성 섹션에 나와 있는 코드 스니펫에서 수동으로 코드 추가를 수행해야 합니다.