Skip to main content

3. Display with framebuffer in internal RAM

动机#

在这一步中,通过将像素数据从内部RAM传输到显示器,我们可以看到显示器上会显示内容。 此步骤可确保我们能将数据传输到显示器,并且可以不断更新显示器内容。

除了将图像数据传输到显示器以外,我们还必须确保可连续将新数据发送到显示器,且不会在显示器上出现错误。 我们还将测量传输速度,因为这会影响显示器的帧率。

正如我们从上一节中学到的那样,我们将在内部RAM中放置一个帧缓冲,该RAM可读写。 我们将重复更新该帧缓冲,并将其传输到显示器。

回顾此 帧缓冲 是由此 公式计算的:

宽 x 高 x bpp

例如,分辨率为480 x 272的16位普通显示器将占用480x272x16/8字节 即61120字节的缓存。

即使显示器尺寸过大导致所需整帧缓存无法存储在内部RAM中,也不应跳过此步骤。 而应将显示控制器配置为仅更新显示器的一部分。 这样一来,我们可以调整帧缓冲所需的RAM量,并使其适合内部RAM大小。

显示接口的类型对传输帧缓冲所需的设置和代码有很大影响。 在本节中,我们将首先讨论连接至LTDC的显示器。 如果您使用诸如SPI显示器之类的显示器,代码会有很大区别,但任务和目标相同。

目标#

本节的目标是将帧缓冲内容传输到显示器。 You should also verify that you can modify the framebuffer content and resend the framebuffer continuously.

验证#

以下是本节的验证点:

验证点基本原理
Framebuffer is shownDisplay controller or SPI is configured and running
Updated framebuffer is shownWe know how to repeatedly transmit the framebuffer
颜色正确The GPIOs are correct (LTDC) and the data format of the display matches our framebuffer
Framerate is correctThe pixel clock and porches are configured to get the required framerate

先决条件#

以下是此步骤的先决条件:

  • 有关显示器的信息,通常为数据手册
  • 有关MCU和显示器之间的连接的信息。

执行#

根据显示器类型,所需设置会有所不同。 但对于所有类型的显示器,我们都需要内部RAM中有一个帧缓冲。 一种分配该存储器的简单方法是只声明一个大小合适的全局数组:

main.c
uint16_t framebuffer[480*272];  //16 bpp framebuffer

如果内部RAM不足以容纳该数组,请声明一个对应于较小分辨率的数组,比方480x200。

将帧缓冲传输到显示器的方法取决于显示器类型。 我们现在来了解一下这点。

并行RGB显示器#

我们首先将讨论连接至MCU的LTDC控制器的并行RGB显示器。

这类显示器的配置任务如下:

  • 配置与显示器有关的GPIO连接
  • 配置LTDC控制器
  • 配置LTDC像素时钟
  • 设置帧缓冲地址
  • 检查帧率

作为说明性示例,我们将使用STM32F746Discovery评估套件。 此板自带480*272显示器。

显示相关GPIO#

该显示器以24 BPP模式运行,所以我们为LTDC和显示器之间的连接配置24个GPIO。 This is most easily done in STM32CubeMX under Multimedia -> LTDC -> GPIO Settings:

配置显示相关GPIO

除了用于像素传输的24个GPIO(如LTDC_B0) 以外,我们还配置了4个显示控制信号:

Signal功能
LTDC_CLK像素时钟。 当对24个线中的像素进行采样时,向显示器发出信号
LTDC_DE数据使能。 它被激活时进行像素传输
LTDC_HSYNC水平同步。 允许显示器找到像素行起点
LTDC_VSYNC垂直同步。 允许显示器找到帧起点

检查您的硬件设计并进行相应的配置。

LTDC 配置#

The LTDC configuration is found in STM32CubeMX under Multimedia -> LTDC -> Parameter Settings:

配置LTDC参数

有效宽度和高度与显示器的分辨率对应。 有关同步脉冲宽度和沿宽度,请检查显示数据手册。 另请注意信号极性。 灰色的值根据其他值计算而来。 这些值被写入LTDC寄存器(也可在代码中找到) 。

现在请转到多媒体-> LTDC-> 层设置”下的LTDC层配置:

Configuring LTDC Layer Parameters

对于该测试,在TouchGFX中,我们通常仅使用一层。 第0层的分辨率应与帧缓冲大小匹配。 以后需要设置帧缓冲地址,这里先不理它。

如果您声明了小于显示器分辨率的帧缓冲数组,则调整图层大小,以便与帧缓冲尺寸匹配。 LTDC将传输帧缓冲中没有的显示器像素背景颜色。 建议将背景颜色设置为可识别的颜色,如红色(蓝色).

时钟配置#

时钟配置也很重要。 必须使能所有GPIO和LTDC的时钟。 像素时钟必须在显示器可接受的范围内。

时钟配置

LTDC取决于3个时钟: HCLK, PCLK2, and LCD_CLK.

设置帧缓冲地址#

In STM32CubeMX we configured the framebuffer address of layer 0 to 0xC0000000. 我们需要将其更改为内部RAM中的数组地址。 This is easily done by using one of the STM32Cube Firmware HAL functions:

main.c
  /* USER CODE BEGIN 2 */  HAL_LTDC_SetAddress(&hltdc, framebuffer, LTDC_LAYER_1);  /* USER CODE END 2 */

Layers are numbered 1, 2, in the HAL functions, but 0, 1 in STM32CubeMX. The LTDC is otherwise fully configured by the code generated by STM32CubeMX in the function MX_LTDC_Init(void).

LTDC控制器将帧缓冲重复发送至显示器。 显示的图像取决于帧缓冲中的值。 尝试帧缓冲中的不同值或模式。 例如,使用memset将帧缓冲清除为0xFF,以显示白屏。

Note
在某些显示器上,必须开启背光才能使图形帧可见。

检查帧率#

LTDC控制器为每个帧触发一个中断。 该中断可被应用程序所利用。

您应使用调试器来检查是否触发了该中断。

这些中断间的时间是所有像素传输和同步沿的耗时总和。 您可以通过调节同步沿来调节帧率。 同步沿参数是LTDC配置的一部分。 通常通过增强场前沿来降低帧率。

一种测量帧率的简单方法为使用中断处理程序中的HAL_GetTick():

stm32f7xx_it.c
volatile int last = 0;volatile int diff = 0;void LTDC_IRQHandler(void){  /* USER CODE BEGIN LTDC_IRQn 0 */  int now = HAL_GetTick();  diff = last - now;  last = now;  /* USER CODE END LTDC_IRQn 0 */  HAL_LTDC_IRQHandler(&hltdc);  ...

请记住,在每秒60帧的情况下,各个帧之间应该有1000 ms / 60 = 16 ms。

SPI 显示器#

现在我们将讨论与SPI总线连接的显示器。

这类显示器的配置任务如下:

  • 配置SPI外设和GPIO
  • 检查时钟
  • 编写或找到必要的驱动程序代码

SPI 配置#

Start in STM32CubeMX and enable the SPI. 这些图像来自STM32G081项目:

SPI 配置

检查使用的SPI格式的显示器数据手册(数据大小和位顺序) 。 请记住,16位字以小端字节序存储在帧缓冲中。 检查是否可以将显示器配置为此格式。 如果不可以,则必须在传输期间进行数据转换。
另请注意时钟极性和时钟相位。 这些参数已在显示器数据手册中指定。

SPI时钟(比特率) 由FCLK的分频器控制。 最小分频器为2。 如果MCU正在运行(如64MHz) ,则最大SPI比特率为32 MBit/s。

在GPIO选项卡上,您可以检查SPI外设的GPIO配置:

SPI GPIO配置

在右侧的引脚排列视图中选择GPIO:

SPI GPIO选择

现在剩下的任务为配置显示器并在SPI通道上传输帧缓冲。 STM32CubeMX cannot generate this code for you, as it depends heavily on the display.
For many displays it is necessary to send a sequence of commands and follow a specific power up sequence. 之后,通常需要设置颜色模式,然后将显示器打开。 所有这些均必须在供应商提供的数据手册或示例中指定。

The STM32Cube Firmware contains examples using SPI communication. The STM32Cube HAL contains various helper functions. 用于发送数据的基本功能:

stm32g0xx_hal_spi.h
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout);

我们建议使用这些库函数,直至通信稳定运行。 然后,可通过编写专用函数来提高性能。

在编写SPI显示驱动程序的过程中,SPI示波器或SPI USB记录器可能非常有用。

Note
从SPI上的低频开始,以避免噪声问题。

检查显示器颜色#

此时,您可以将帧缓存内容传输到显示器,建议您彻底检查显示器颜色。

该想法就是将颜色写入帧缓存,并通过视觉来检查显示器。 以下为一些示例:

测试Description
红色在帧缓存中设置红色。 显示屏也必须为红色。
绿色在帧缓存中设置绿色。 显示屏也必须为绿色。
蓝色在帧缓存中设置蓝色。 显示屏也必须为蓝色。
深色如果设置为深色(如0x8000)(50%红色),则必须在显示屏上显示黑色。
更改颜色每秒更改一次帧缓存,然后查看显示屏是否更新。

要将颜色放入RGB565帧缓冲中,可使用以下方案:

uint8_t r    = 0xff, g = 0x00, b = 0x00;             // Solid reduint16_t col = ((r>>3)<<11) | ((g>>2)<<5) | (b>>3);  // Convert colors to RGB565// put colors into the framebufferfor(int i = 0; i < W*H; i++) {  framebuffer[i] = col;}

对于24BPP显示器,最好使用字节指针来表示代码(颜色以BGR顺序存储):

uint8_t* framebuffer[480*272*3];  //24 bit framebuffer...uint8_t *fb = framebuffer;while(fb < (uint8_t*)(framebuffer + (480*272*3))) {  *fb++ = 0x00; // Write blue color and increment pointer by one byte  *fb++ = 0x00; // Write green color  *fb++ = 0xFF; // Write red color}

显示彩色帧缓存