本文介绍了TFT控制器背后的机制及其在TouchGFX的时序和性能方面的核心作用。
门廊区域
MCU上的TFT控制器负责使用帧缓冲区中找到的像素值更新显示屏。显示屏玻璃本身没有任何存储器,因此无论帧缓冲区内容是否更改,像素值都必须连续传输到显示器。因此,即使在显示静止图像时,帧缓冲区也将被传输到显示器。
显示器更新频率因显示器而异,但通常在50-60 Hz范围内。这意味着每16-20毫秒将传输帧缓冲区内容。传输通常以比更新频率短的持续时间进行突发,从而在每次更新之间留出一些空闲时间,在该空闲时间中可以通过其他方式访问保存帧缓冲区的内存(例如,在帧缓冲区中写入新的像素值) 。
下图显示了320x240像素显示屏上TFT控制器的更新周期:
门廊区域是没有数据发送的延迟。从显示屏的左上角(像素0,0)开始水平移动,每次传输一个像素一个像素,每行之前和之后都有延迟(水平门廊),第一行之前和最后一行之后都有延迟(垂直)门廊)。对于每个显示更新(帧),都会发出VSYNC信号,对于该帧中的每一行,都会发出HSYNC信号。每当控制器位于活动区域内时,包含帧缓冲区的SDRAM就会繁忙。为了优化SDRAM访问,当TouchGFX执行渲染时,将控制器信号直接连接到该信号,如下所述。
渲染通行证
当TFT控制器进入活动区域时,开始进行帧渲染。渲染过程包括以下步骤:
- 调用虚拟beginFrame函数
- 调用
Application::handleTickEvent
功能,它将打勾计时器小部件和活动视图。此步骤可能导致屏幕无效。无效表示显示屏玻璃和帧缓冲区的内容不反映屏幕的当前布局,因此需要重新绘制帧缓冲区以使显示屏玻璃在屏幕上显示正确的信息。 - 采样触摸并将触摸事件转发到应用程序。此步骤也可能导致无效。
- 如果合适,请对键和按钮进行采样,并将其转发给应用程序。此步骤也可能导致无效。
- 合并无效区域并在需要时开始实际绘图
- 等待绘图完成
- 调用虚拟endFrame函数
影格速率
如果渲染屏幕所花费的时间少于显示更新速率,则系统的实际帧速率将与显示更新速率匹配。如果什么都不需要或只画一小部分屏幕,通常会发生这种情况。但是,如果绘图花费的时间更长,则实际的帧速率将降低,因为下一次TFT控制器下次进入活动区域时将不会进行下一帧渲染,因为先前的绘图仍在进行中。下图概述了跨四个帧的典型渲染过程的时序,其中所有渲染过程花费的时间少于显示更新频率。
上面您看到的是,当TFT控制器进入标记为a)的活动区域(即,开始更新显示)时,GUI任务立即开始其帧渲染。GUI任务将采样触摸和按钮,并在活动视图和已注册计时器事件的所有小部件上调用滴答声。在此示例中,这导致屏幕区域无效。在b)处,渲染完成,并且GUI任务现在可以休眠。在c),TFT控制器开始更新显示(为下一帧输入有效区域)。这里发生了几件事:
- 由于我们已经重绘了屏幕的某些部分,因此我们交换了帧缓冲区,以便TFT控制器将使用我们刚刚绘制的帧缓冲区作为此周期中更新显示的基础。
- 此外,GUI任务再次唤醒并开始下一个帧渲染过程。在此示例图中,在此过程中无需更改屏幕,因此GUI任务将快速进入睡眠状态(d),系统空闲直到下一帧。
- 您还将注意到,在下一次显示更新(e)时不会发生帧缓冲区交换,因为没有绘制任何内容。
如果需要绘制的屏幕区域很大,则绘制操作所花费的时间很可能比显示更新频率长,如下所示:
在这里,您将注意到下一个TFT控制器更新开始时渲染过程仍在进行(a)。由于前一帧未完成,因此将忽略此事件,因此此处不会开始新的帧渲染。渲染完成后,在下一次TFT更新时将发生帧缓冲区交换(b)。在该示例中,有效帧速率(即,显示器上的像素实际改变的频率)为VSYNC / 2。如果绘图操作花费了很长时间,那么它甚至可能跨越三到四个更新。如果仅在诸如切换到其他视图之类的情况下发生,则这不是问题。但是,如果它发生在应该看起来流畅的动画中,则有效帧速率将太慢。
由于帧缓冲区交换发生在绘制完成后的下一个显示更新处,因此可能有必要调整VSYNC频率以获得最佳时序。考虑以下示例:
上面看到的是,显示开始更新(a)后,渲染立即完成。此时,由于TFT控制器正处于更新显示的中间状态,因此无法发生帧缓冲区交换。因此,交换要等到下一次更新才能进行,这意味着像素更新要等到下一次更新(b)才可见。这样产生的有效帧速率为VSYNC / 3。在这种情况下,最好通过延长垂直门廊来降低TFT更新频率。下图说明了相同的绘制操作,但VSYNC频率略低:
门廊延迟区域已稍微扩大(a)。现在,渲染将在TFT更新之前立即完成,从而允许较早交换帧缓冲区,从而产生更好的有效帧速率。
因此,在实施UI之前,很难确定系统的最佳VSYNC时序。一个好的经验法则是最初将更新频率设置为16-20ms,并研究应用程序的实际渲染时间以进行微调。
交错基于MCU和DMA的渲染
为了简单起见,以上所有示例仅显示了渲染时间,并未区分是通过DMA还是MCU进行渲染。TouchGFX将尝试尽可能多地使用DMA,以使MCU负载保持较低。根据目标微控制器和用户界面的组成,GUI任务本身也执行渲染并不罕见,例如,如果绘制文本或alpha混合图像(如果DMA引擎不支持)的话。有关硬件支持的详细信息,请参见消除操作和硬件消除功能。如果涉及软件渲染,则更详细的时序图如下:
在上图中,我们看到GUI任务被唤醒(a)并开始处理帧(采样触摸,处理计时器滴答,创建DMA操作队列)。完成后,DMA接管(b)。然后,我们可以看到在c)处执行基于MCU渲染的GUI任务,并在其余渲染过程中进行后续的交错DMA和基于MCU的绘制。交错过程的同步内置在TouchGFX中,并由帧缓冲区信号量控制,这使GUI任务和DMA可以在帧缓冲区中交替执行绘制操作。
补偿降低的帧频
每次抽奖调用一次tick方法。也就是说,每个新帧都打一个勾。在具有偶尔需要较长时间渲染的帧的情况下,滴答频率可能是不规则的。
定期滴答可用于实现动画:例如,一个图标应在约3秒内在屏幕上移动200个像素。如果LCD更新速率为60 Hz(相当于16.7 ms),则图标需要每帧移动1个像素。
如果绘图操作可以在16.7毫秒内完成,则效果很好。如果绘图操作花费的时间长于此时间,则会错过下一次LCD更新并降低帧速率。通常这不是一个大问题,因为可以修改动画,例如,将图标每帧移动2个像素。在这种情况下,动画仍将在3秒内完成。但是,在滴答速度不规则的情况下会出现问题,即滴答速度有时为16.7毫秒,有时为33毫秒,因为这会导致动画看起来不均匀,并且持续时间无法预测。
为了减轻这种情况,TouchGFX具有“降低的帧速率”补偿功能,可以缓解此问题。启用后,它将为每个错过的LCD更新进行一次额外的滴答呼叫,滴答的呼叫速率将与LCD更新速率匹配。
在上面的示例中,在长时间运行的绘制操作之后,在调用draw之前,将调用tick方法3次。现在将图标移动1个像素就足够了。
默认情况下,禁用帧率补偿。
通过在HAL中调用setFrameRateCompensation方法来启用它:
#include
HAL::getInstance()->setFrameRateCompensation(true);
如果需要将其作为应用程序的常规设置,建议将调用插入到BoardConfiguration.cpp的touchgfx_init()中。否则,可以基于每个视图启用/禁用它。
TouchGFX可以通过调用HAL中的getLCDRefreshCount方法来提供每个刻度丢失的帧数:
HAL::getInstance()->getLCDRefreshCount()
通过检查LCDRefreshCount的大小,可用于跳过tick方法中的耗时操作。这将等于自上次滴答以来已执行的绘制次数,对于FrameRateCompensation,将等于在此帧中将执行的滴答次数。
【来源】