跳转到主要内容

抽象层架构

如前一节所述,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.

抽象层类

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向被阻塞的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.

渲染完成

渲染完成后,TouchGFX Engine调用渲染完成钩子、OSWrappers::waitForVSync

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基元的空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);

// 然后等待下一次VSYNC。
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
  • TouchGFX Engine等待生成下一帧时,其他任务可以执行重要的工作。
  • 显示就绪

    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,则使用变量,并分配一个非零值以打破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,并返回是否检测到触摸(真或假)。

    Tip
    TouchGFX Generator将生成一个将TouchController接口函数定义为空的类。 HAL开发人员必须填入具体实现代码。

    有多种实现此函数的方法:

    1. 在sampleTouch() 中轮询:通过发送请求并轮询结果,从硬件触摸控制器(通常为I2C) 读取触摸状态。 这会影响应用程序的总体渲染时间,因为I2C往返传输通常长达1ms,在此期间,图形引擎会被阻塞。
    2. 基于中断:另一种可能是使用中断。 I2C读取命令由定时器定期启动,或作为对触摸硬件外部中断的响应而启动。 按钮控制器接口touchgfx::ButtonController可用于将硬件信号(按钮或其他) 映射到应用程序事件。 以下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接口的类实体,并将参数传递至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类中的样本方法。 如果返回真值,则键值将被传递至当前屏幕的handleKeyEvent事件处理程序。

    Further reading
    有关如何将通过ButtonController采集的值用作设计工具中的交互触发器的更多信息,请参见交互文章。

    同步帧缓冲访问

    多个执行体可能涉及对帧缓存区的访问。

    1CPU在渲染期间读取和写入像素
    2DMA2D在硬件辅助渲染期间读取和写入像素
    3LTDC在传输到并行RGB显示屏期间读取像素
    4DMA在传输到SPI显示屏期间读取像素

    TouchGFX Engine通过OSWrappers接口来同步帧缓存访问,同时希望访问帧缓存的外设(如DMA2D) 也必须执行相同操作。 常规设计是使用信号量来保证对帧缓冲的访问,但也可以使用其他同步机制。

    下表显示了OSWrappers类(OSWrappers.cpp) 中的函数列表,这些函数可由TouchGFX Generator生成或由用户手动生成。

    方法说明
    takeFrameBufferSemaphore由图形引擎调用,以获得对帧缓存的独占访问。 这将阻塞引擎,直至DMA2D完成(如果正在运行)
    tryTakeFrameBufferSemaphore确保已锁定。 该方法不会阻塞引擎,但对takeFrameBufferSemaphore的下次调用将被阻塞
    giveFrameBufferSemaphore解除帧缓存锁定
    giveFrameBufferSemaphoreFromISR从中断上下文解除帧缓存锁定
    Tip
    TouchGFX Generator可生成使用OSWrappers接口来同步的ChromART驱动程序,以及根据RTOS选择来执行该同步的函数实现。

    报告下一个可用的帧缓冲区

    无论采用哪种渲染策略,TouchGFX Engine都必须知道在每个时间片中应将像素渲染到哪个存储区。 使用单帧缓存或双帧缓存战略时,TouchGFX Engine将根据帧缓存的全宽、高度和位宽将像素数据写入存储区。 图形引擎负责双缓存设置中两个帧缓存之间的交换。

    可以将对帧缓存的访问限制为部分帧缓存。 可在HAL子类中重新实现HAL::getTFTCurrentLine()方法。 返回上面用于图形引擎绘制而保存的行号。

    使用部分帧缓存时,开发人员定义TouchGFX Engine在渲染时将使用的一个或多个存储器块。 在此处阅读更多相关信息。

    Tip
    TouchGFX Generator支持所有帧缓冲策略的配置。

    执行渲染操作

    渲染和显示图形很少是应用程序的唯一目的。 其他任务也需要使用CPU。 TouchGFX的目标之一尽可能少地占用CPU资源来绘制用户界面。 HAL类可对许多STM32微控制器(或其他硬件功能) 上的DMA2D功能进行抽象,并使其可用于图形引擎。

    将位图之类的资源渲染到帧缓冲时,TouchGFX Engine检查HAL是否具有将部分或全部位图传输到帧缓存的功能。 如果有此功能,则将绘图操作委托给HAL,而不是由CPU处理。

    引擎调用方法HAL::getBlitCaps(),以获取硬件功能描述。 HAL子类可重新实现此调用,以添加功能。

    引擎在绘制用户界面时调用HAL类上的操作(HAL::blitCopy) ,并对DMA操作排队。 如果HAL无法报告所需的功能,则图形引擎将退而使用软件方式来渲染。

    Tip
    许多STM32 MCU都具有ChromART芯片,在执行alpha像素混合时,可将数据从外部Flash存储器等移动到帧缓存。 对于许多MCU,TouchGFX Generator可生成ChromART驱动程序,该驱动程序使用ChromART芯片来增加几个“块位传输”操作功能。

    处理到显示屏的帧缓冲传输

    为将帧缓存内容传输到显示屏,TouchGFX AL经常使用“区域渲染完成”钩子。 一旦部分帧缓存渲染完成,引擎就会向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
    通读方案以获取有关如何支持各种显示接口的具体示例。