주요 내용으로 건너뛰기

Abstraction Layer Architecture

이전 섹션에서 설명했듯이 TouchGFX AL은 특정한 책임을 맡고 있습니다. Responsibilities are either implemented in the hardware part of the AL (HAL) or the part of the AL that synchronizes with TouchGFX Engine, typically through an RTOS (OSAL). The following table summarizes these responsibilities which were outlined in the previous section:

책임운영 체제 또는 하드웨어
TouchGFX Engine 메인 루프를 디스플레이 전송과 동기화운영 체제 및 하드웨어
터치 및 물리적 버튼 이벤트 보고하드웨어
프레임 버퍼 액세스 동기화운영 체제
차기 가용 프레임 버퍼 영역 보고하드웨어
렌더링 작업 수행하드웨어
디스플레이에 프레임 버퍼 전송 처리 하드웨어

아래의 각 하위 섹션에는 위의 책임들을 이행하기 위해 수행해야 하는 작업이 명시되어 있습니다. For custom hardware platforms the TouchGFX Generator, inside STM32CubeMX, can generate most of the AL and accompanying TouchGFX project. The remaining parts, that the AL developer must implement manually, are pointed out through code comments and notifications through the TouchGFX Generator. Read more about the TouchGFX Generator in the next section.

AL(Abstraction Layer) 클래스

The TouchGFX AL is accessed by the TouchGFX Engine through concrete sub-classes and through class implementation files (.cpp) where member functions of classes defined in the TouchGFX Engine are implemented. These sub-classes and class implementation files are generated by the TouchGFX Generator. The TouchGFX Generator can generate both the part of the HAL that reflects configurations from STM32CubeMX, as well as the OSAL for the used RTOS. Please read the section on TouchGFX Generator for further details. 일반적으로 HAL의 아키텍처는 아래 그림과 같습니다.

생성된 코드의 계층 구조

TouchGFX Engine 메인 루프를 디스플레이 전송과 동기화

The main idea behind this step is to block the TouchGFX Engine main loop when rendering is done, ensuring that no further frames are produced. 디스플레이가 준비되면 OSAL은 차단된 TouchGFX Engine 메인 루프를 시그널링 하여 프레임을 계속 생성합니다.

In order to fulfill this responsibility the typical way of a TouchGFX AL is to utilize the engine hook Rendering done and the interrupt Display Ready, as outlined in Responsibilities of the Abstraction Layer. The OSAL defines a function OSWrappers::signalVSync in which developers can signal the semaphore that the engine waits upon when called.

Tip
The TouchGFX Generator can create a complete OSAL for CMSIS V1, CMSIS V2, ThreadX and when not using an RTOS.

Rendering Done

Rendering done 후크인 OSWrappers::waitForVSync는 렌더링이 완료된 후에 TouchGFX Engine에서 호출됩니다.

When implementing this OSAL method, the Abstraction Layer must block the graphics engine until it is time to render the next frame. 이 블록을 구현하는 표준 메서드는 메시지 대기열에서 읽기 차단을 수행하는 것입니다. The HAL developer is free to use any method to implement the block if this is not feasible.

Tip
또한 TouchGFX Generator는 이러한 소프트웨어를 사용할 수 없는 경우에 RTOS의 기본 요소들이 아닌 스핀락(spinlock) 을 사용해 대기하도록 빈 OSAL을 생성할 수도 있습니다.

When OSWrappers::signalVSync is signaled (or the semaphore/queue used in OSWrappers::waitForVSync is signaled) TouchGFX will start rendering the next application frame. The following code based on CMSIS V2 causes the TouchGFX engine to block until an element is added to the queue by another part of the system, typically an interrupt synchronized with the display.

OSWrappers.cpp (CMSIS V2)
static osMessageQueueId_t vsync_queue = NULL; //Queue identifier is assigned elsewhere

void OSWrappers::waitForVSync()
{
uint32_t dummyGet;
// First make sure the queue is empty, by trying to remove an element with 0 timeout.
osMessageQueueGet(vsync_queue, &dummyGet, 0, 0);

// Then, wait for next VSYNC to occur.
osMessageQueueGet(vsync_queue, &dummyGet, 0, osWaitForever);
}

RTOS를 사용하지 않는 경우에는 TouchGFX Generator가 휘발성 변수를 사용해 waitForVSync에 대해 다음과 같이 함수를 구현합니다.

OSWrappers.cpp (No OS)
static volatile uint32_t vsync_sem = 0;

void OSWrappers::waitForVSync()
{
if(vsync_sem)
{
vsync_sem = 0;
// Signal TouchGFX to start rendering next frame
...
}
}
Tip
  • While TouchGFX Engine은 다른 작업들을 통해 중요한 작업을 수행할 수 있는 다음 프레임을 생성할 때까지 대기합니다.
  • Display ready

    The Display ready signal to unblock the main loop should come from an interrupt from a display controller, from the display itself or even from a hardware timer. The source of the signal is dependent on the type of display.

    OSWrappers 클래스는 이 신호에 대해 함수 OSWrappers::signalVsync를 정의합니다. The implementation of the function must unblock the main loop by satisfying the wait condition used in OSWrappers::waitForVSync.

    Continuing from the above CMSIS V2 example, the following code puts a message into the message queue vsync_queue which unblocks the TouchGFX Engine.

    OSWrappers.cpp (CMSIS V2)
    void OSWrappers::signalVSync()
    {
    osMessageQueuePut(vsync_queue, &dummy, 0, 0);
    }

    This OSWrappers::signalVSync method must be called at hardware level from an interrupt for e.g. an LTDC, an external signal from the display, or a hardware timer.

    RTOS를 사용하지 않는 경우에는 변수를 사용하고 0이 아닌 값을 할당해서 while 루프를 종료해야 합니다.

    OSWrappers.cpp (No OS)
    void OSWrappers::signalVSync()
    {
    vsync_sem = 1;
    }

    터치 및 물리적 버튼 이벤트 보고

    Before rendering a new frame, the TouchGFX Engine collects external input from the TouchController and ButtonController interfaces.

    터치 좌표

    Coordinates from the touch controller are translated into click-, drag- and gesture events by the TouchGFX Engine and passed to the application.

    The way touch coordinated from the touch controller is accessed by the TouchGFX Engine is by passing an implementation

    The following code is generated by the TouchGFX Generator:

    TouchGFXConfiguration.cpp
    static STM32TouchController tc;
    ...
    static TouchGFXHAL hal(dma, display, tc, 390, 390);

    TouchGFX Engine는 렌더링 주기 동안 입력을 수집할 때 tc 객체에서 sampleTouch() 함수를 호출합니다.

    bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)

    AL 개발자가 제공하는 함수는 읽기 터치 좌표 값을 x와 y로 할당하고 터치 감지 여부(true 또는 false)를 반환해야 합니다.

    Tip
    TouchGFX Generator는 TouchController 인터페이스 함수를 빈 함수로 정의하는 클래스를 생성합니다. Hal 개발자는 함수를 채워야 합니다.

    이러한 함수를 구현하는 방법에는 몇 가지가 있습니다.

    1. sampleTouch() 에서 폴링: 요청을 전송하고 결과에 대해 폴링을 수행함으로써 하드웨어 터치 컨트롤러(보통 I2C)에서 터치 상태를 읽어옵니다. 그래픽 엔진이 차단되는 동안 I2C 왕복에 최대 1ms의 시간이 소요되는 경우가 종종 있다는 점에서 이는 애플리케이션의 전체 렌더링 시간에 영향을 줍니다.
    2. 인터럽트 기반: 인터럽트를 사용하는 방법도 가능합니다. I2C 읽기 명령은 타이머에 의해, 또는 터치 하드웨어에서 나온 외부 인터럽트에 대한 응답으로서 주기적으로 시작됩니다. I2C 데이터가 가용 상태가 되면(또 다른 인터럽트) 이를 메시지 대기열이나 전역 변수를 통해 STM32TouchController에서 사용할 수 있습니다. STM32TouchController.cpp(TouchGFX Generator에서 생성됨)에서 나온 아래 코드는 sampleTouch가 RTOS를 이용해 시스템을 검색하는 방식을 보여줍니다.
    STM32TouchController.cpp
    bool STM32TouchController::sampleTouch(int32_t& x, int32_t& y)
    {
    if (osMessageQueueGet(mid_MsgQueue, &msg, NULL, 0U) == osOK)
    {
    x = msg.x;
    y = msg.y;
    return true;
    }
    return false;
    }

    이 파일의 위치는 TouchGFX Generator에 대한 다음 장에 나와 있습니다.

    기타 외부 이벤트

    버튼 컨트롤러 인터페이스인 touchgfx::ButtonController는 하드웨어 신호(버튼 또는 기타)를 애플리케이션에 대한 이벤트로 매핑하는 데 사용할 수 있습니다. 이러한 이벤트에 대한 반응을 TouchGFX Designer 내에 구성할 수 있습니다. 이 인터페이스의 사용 방법은 ButtonController를 갖는 것이 필수가 아니라는 점을 제외하고 위의 터치 컨트롤러와 유사합니다.

    이 인터페이스의 사용 방법은 ButtonController를 갖는 것이 필수가 아니라는 점을 제외하고 위의 터치 컨트롤러와 유사합니다. 사용을 위해 클래스의 인스턴스를 생성하여 ButtonController 인터페이스를 구현하고 HAL에 인스턴스에 대한 참조를 전달합니다.

    MyButtonController.cpp
    class MyButtonController : public touchgfx::ButtonController
    {
    bool sample(uint8_t& key)
    {
    ... //Sample IO, set key, return true/false
    }
    };
    TouchGFXConfiguration.cpp
    static MyButtonController bc;
    void touchgfx_init()
    {
    ...
    hal.initialize();
    hal.setButtonController(&bc);
    }

    ButtonController 클래스의 sample 메서드는 각 프레임에 앞서 호출됩니다. True를 반환하면 이 값이 현재 화면의 handleKeyEvent 이벤트 핸들러에 전달됩니다.

    Further reading
    ButtonController를 통해 샘플링된 값을 TouchGFX Designer의 상호 작용을 위한 트리거로 사용하는 자세한 방법은 상호 작용 문서를 참조하십시오.

    프레임 버퍼 액세스 동기화

    다수의 동작들이 프레임 버퍼 메모리를 액세스하는 데 관심을 가질 수 있습니다.

    1CPU렌더링 동안 픽셀 읽기 및 쓰기
    2DMA2D*하드웨어 지원 렌더링 동안 픽셀 읽기 및 쓰기
    3LTDC병렬 RGB 디스플레이에 전송하는 동안 픽셀 읽기
    4DMASPI 디스플레이에 전송하는 동안 픽셀 읽기

    TouchGFX Engine은 OSWrappers 인터페이스를 통해 프레임 버퍼 액세스를 동기화하며, 동일한 프레임 버퍼에 액세스하고 싶은 주변 장치(예: DMA2D)는 이와 똑같이 해야 합니다. 일반적인 설계에서는 세마포어를 사용하여 프레임 버퍼에 대한 액세스를 보호하지만, 다른 동기화 메커니즘을 사용할 수도 있습니다.

    아래 표에는 TouchGFX Generator를 통해서 생성하거나 사용자가 직접 생성할 수 있는 OSWrappers 클래스(OSWrappers.cpp)의 함수 목록이 나와 있습니다.

    메서드설명
    takeFrameBufferSemaphore프레임 버퍼에 대한 독점 액세스 권한을 얻기 위해 TouchGFX Engine에서 호출됩니다. 이 메서드는 DMA2D가 수행될 때까지(실행되는 경우) TouchGFX Engine을 차단합니다.
    tryTakeFrameBufferSemaphore잠금이 실행되었는지 확인합니다. 이 메서드는 차단을 직접 하지 않고 takeFrameBufferSemaphore에 대한 다음 호출이 호출자를 차단하는지 확인합니다.
    giveFrameBufferSemaphore프레임 버퍼 잠금을 해제합니다.
    giveFrameBufferSemaphoreFromISR인터럽트 컨텍스트에서 프레임 버퍼 잠금을 해제합니다.
    Tip
    TouchGFX Generator는 OSWrappers 인터페이스를 사용해 동기화하는 ChromART 드라이버를 비롯해, 선택한 RTOS에 따라 이러한 동기화를 수행하는 함수를 생성할 수 있습니다.

    차기 가용 프레임 버퍼 영역 보고

    렌더링 전략에 관계없이 TouchGFX Engine은 각각의 틱에서 픽셀을 렌더링해야 하는 메모리 영역을 알아야 합니다. TouchGFX Engine은 단일 또는 이중 프레임 버퍼 전략을 사용하여 프레임 버퍼의 전체 너비, 높이 및 비트 심도에 따라 메모리 영역에 픽셀 데이터를 기록합니다. 또한 이중 버퍼 설정에서 두 버퍼 간의 스왑도 관리합니다.

    프레임 버퍼에 대한 액세스 권한을 프레임 버퍼의 일부로 제한하는 것도 가능합니다. HAL::getTFTCurrentLine() 메소드는 HAL 하위 클래스에서 재구현이 가능합니다. TouchGFX Engine이 그리기를 위해 저장한 메소드 위의 라인 번호를 반환합니다.

    개발자는 부분 프레임 버퍼 전략을 사용해 TouchGFX Engine이 렌더링 시 사용하게 될 메모리의 블록을 1개 이상 정의합니다. 이에 대한 자세한 내용은 여기를 참조하십시오.

    Tip
    TouchGFX Generator는 지원되는 모든 프레임 버퍼 전략을 위한 구성을 제공할 수 있습니다.

    렌더링 작업 수행

    그래픽 렌더링 및 표시가 애플리케이션의 유일한 목적인 경우는 거의 없습니다. CPU를 사용하기 위해서는 다른 작업들도 필요합니다. TouchGFX의 목표 중 하나는 가능한 적은 CPU 주기를 사용해 사용자 인터페이스를 그리는 것입니다. HAL 클래스는 많은 STM32 마이크로컨틀로러(또는 기타 하드웨어 기능)에서 나타나는 DMA2D를 추상화하여 이를 TouchGFX Engine에서 사용할 수 있게 지원합니다.

    TouchGFX Engine은 비트맵 같은 자산을 프레임 버퍼에 렌더링할 때 HAL에 비트맵의 일부 또는 전체를 프레임 버퍼로 '블릿(blit, block transfer)' 할 수 있는 기능이 있는지 확인합니다. 블릿 기능이 있는 경우에는 그리기 작업이 CPU에서 처리되는 대신 HAL에 위임됩니다.

    TouchGFX Engine은 HAL::getBlitCaps() 메소드를 호출하여 하드웨어의 기능에 대한 설명을 가져옵니다. HAL 하위 클래스는 기능을 추가하기 위해 이를 다시 구현할 수 있습니다.

    TouchGFX Engine은 사용자 인터페이스를 그리는 동안 HAL 클래스에서 연산(예: DMA에 대한 연산을 대기열에 저장하는 HAL::blitCopy)을 사용합니다. HAL이 필요한 기능을 보고하지 않는 경우, TouchGFX Engine은 소프트웨어 렌더링 폴백(fallback)을 사용합니다.

    Tip
    많은 STM32 MCU들이 ChromART 칩을 가지고 있어서 픽셀에 대한 알파 블렌딩을 수행하는 동안 예를 들면 외부 플래시 메모리에서 프레임 버퍼로 데이터를 옮길 수 있습니다. 많은 MCU에서 TouchGFX Generator는 ChromART 칩을 사용해 몇몇 "블릿(blit)" 연산 기능을 추가하는 ChromART 드라이버를 생성할 수 있습니다.

    디스플레이에 프레임 버퍼 전송 처리

    프레임 버퍼를 디스플레이에 전달하기 위해 TouchGFX AL에서는 "Rendering of area complete" 후크가 주로 사용됩니다. TouchGFX Engine은 프레임 버퍼의 일부에 대한 렌더링이 완료되면 AL을 시그널링합니다. AL은 프레임 버퍼의 이 부분을 디스플레이에 전송하는 방법을 선택할 수 있습니다.

    전체 영역 렌더링

    코드에서 이러한 후크는 가상 함수 HAL::flushFrameBuffer(Rect& rect)입니다.

    LTDC 컨트롤러를 장착한 STM32 마이크로컨트롤러에서는 모든 렌더링 이후에 프레임 버퍼를 전송하기 위한 작업을 수행할 필요가 없습니다. LTDC가 개시된 이후에 일정한 주기로 전송이 계속 이루어지기 때문에 이 메서드를 빈 상태로 남겨둘 수 있습니다.

    SPI나 8080 같은 다른 디스플레이 유형에서는 프레임 버퍼를 수동으로 전송해야 합니다.

    이 함수를 구현하면 개발자가 GRAM에서 디스플레이에 프레임 버퍼의 해당 영역을 수동 전송하는 작업을 개시할 수 있습니다:

    void TouchGFXHAL::flushFrameBuffer(const touchgfx::Rect& r)
    {
    HAL::flushFrameBuffer(rect); //call superclass

    //start transfer if not running already!
    if (!IsTransmittingData())
    {
    const uint8_t* pixels = ...; // Calculate pixel address
    SendFrameBufferRect((uint8_t*)pixels, r.x, r.y, r.width, r.height);
    }
    else
    {
    ... // Queue rect for later or wait here
    }
    }
    Further reading
    다양한 디스플레이 인터페이스를 지원하는 방법의 구체적인 예는 시나리오를 참조하십시오.