显示屏

[English]

许多应用需要把信息显示给用户,因此显示屏是很重要的一个显示设备。ESP32 系列芯片为屏幕显示应用提供了丰富的外设支持,包含的接口类型有 I2C8080SPIRGB

一块屏幕通常有两个主要部分:显示基板驱动 IC。显示基板(也称显示面板)决定了显示的尺寸、分辨率、色彩等参数;驱动 IC (也称屏幕控制器)是匹配显示基板并与基板封装在一起的,它通过一定的接口对外通信并控制显示基板进行显示。

ESP32 系列芯片的屏幕接口

大部分屏幕控制器可以支持多种显示接口。如 ILI9486 支持 MCU 接口(即 8080 接口)、SPI 接口、RGB 接口和 MIPI 接口。

ESP32 系列芯片接口支持情况如下:

SoC

I2C 接口

SPI 接口

8080 接口

RGB 接口

ESP32

supported

supported

supported

ESP32-S2

supported

supported

supported

ESP32-S3

supported

supported

supported

supported

ESP32-C3

supported

supported

注解

ESP32 系列芯片的 I2C、SPI、8080 和 RGB 接口均可以通过 GPIO 交换矩阵将信号映射到任意管脚。

SPI 接口

ESP32 系列芯片的 SPI 接口特点:

  • SPI 时钟频率最高可达 80MHz

  • 支持 QSPI 模式

8080 接口

ESP32 系列芯片的 8080 接口分别由不同的外设实现

SoC

实现 8080 接口的外设

特点

ESP32

I2S

8/16 位模式下时钟频率最高可达 20MHz

ESP32-S2

I2S

8 位模式下时钟频率最高可达 40MHz,16 位模式下 20MHz

ESP32-S3

LCD_CAM

8 位模式下时钟频率最高可达 40MHz,16 位模式下 20MHz

RGB 接口

ESP32-S3 芯片拥有一个 RGB 接口,支持并行 RGB565 模式和串行 RGB888 模式。RGB 接口与 8080 接口都是并行接口,一个重要区别在于 RGB 接口的屏幕一般不带显存,这就需要每一个刷新周期都对屏幕上的所有像素数据进行更新。由于 ESP32-S3 芯片内部没有足够的 RAM 用于显存,所以需要外接 PSRAM,并将显存置于 PSRAM 上。由于显存要求的带宽较大,所以为达到良好的显示效果 需要使用 8 线 PSRAM ,并且将 PSRAM 时钟频率至少设置为 80MHz

注解

RGB 接口的驱动在 esp-idf 的 components/esp_lcd 组件中而不在 esp-iot-solution。例程请参考 esp-idf/examples/peripherals/lcd/rgb_panel

屏幕控制器

已适配的屏幕控制器型号如下表所示:

控制器型号

最大分辨率

类型

NT35510

480 x 865

Color

ILI9806

480 x 865

Color

RM68120

480 x 865

Color

ILI9486

320 x 480

Color

ILI9488

320 x 480

Color

ILI9341

240 x 320

Color

ILI9342

240 x 320

Color

ST7789

240 x 320

Color

ST7796

320 x 480

Color

SSD1351

128 x 128

Color

SSD1306

128 x 64

Mono

SSD1307

128 x 39

Mono

SSD1322

480 x 128

Gray

SSD1963

480 x 864

Color

其中 SSD1963 可将 8080 接口转换到 RGB 接口。

驱动结构

../_images/screen_driver_structure.png

屏幕驱动结构框图

为了更加符合一个屏幕控制芯片拥有多个接口的实际情况,将屏幕驱动程序划分为 接口驱动屏幕控制器驱动 两部分。

  • 接口驱动:完成基本的命令和数据的读写

  • 屏幕控制器驱动:通过接口驱动来完成屏幕的显示

在程序设计上,一个屏幕控制器驱动可以通过调用不同的接口驱动以实现切换硬件上不同的接口。

屏幕的分类

对屏幕进行分类的讨论将有助于我们对驱动的理解,这里将按照屏幕可显示的色彩来分类,而非 OLED、LCD 等屏幕的面板材料。 一般情况下,屏幕显示的色彩决定了 BPP (Bits Per Pixel),而 BPP 的不同导致程序的处理方式有一些不同,下面将更直观地列举几种 GRAM 映射到像素点的方式:

../_images/screen_driver_RGB565.png

BPP = 16 GRAM 结构

../_images/screen_driver_mono.png

BPP = 1 GRAM 结构

../_images/screen_driver_gray.png

BPP = 4 GRAM 结构

从以上图中可以看出,映射方式大概可以分为两类:

  • BBP >= 8,通常是支持 RGB888、RGB666、RGB565 等编码的彩色屏幕。

  • BPP < 8,通常是单色的屏幕,可能是黑白的,也可能是灰阶的。

BPP < 8 时,一个字节映射到了多个像素,因此无法直接地控制单个像素。这时,驱动不支持 draw_pixel() 函数,且 set_window() 的参数也将受到限制。BPP >= 8 时,则可以轻松地访问单个像素。

注意

对于彩色屏幕,驱动仅支持 RGB565 颜色编码。

接口驱动

一个屏幕控制器通常有多种接口,在 ESP32 上通常使用 8080 并口SPII2C 这三种接口与屏幕连接,可以在调用 scr_interface_create() 创建接口驱动时选用其中一种接口。

注解

使用 scr_interface_create() 创建不同类型的接口时需要注意传入与之对应的配置参数类型,例如 8080 接口是 i2s_lcd_config_t 而 SPI 接口是 scr_interface_spi_config_t

为了方便在屏幕控制器驱动中统一使用这些接口,display/screen/screen_utility/interface_drv_def.h 中定义了所有接口,可以使用简单的参数调用接口驱动。

注解

大部分屏幕是大端模式,而 ESP32 是小端模式,因此可在使用的接口驱动中根据 swap_data 配置可选择进行大小端转换。请注意: 当使用 SPI 接口时,由于 IDF 的 SPI 驱动内部没有提供该功能,接口驱动将会对传入数据进行转换,这要求传入的数据是可写的,因此数据 必须 存放在 RAM 中。

屏幕控制器驱动

该部分将根据不同的屏幕控制器分别实现显示等功能,为了方便地移植到不同 GUI 库,对屏幕的一部分通用函数用 scr_driver_t 进行了抽象。对于一些屏幕的非通用功能,需要自行调用其特定的函数完成。

对于这些通用函数,由于屏幕控制器本身的功能不尽相同,并不是所有的屏幕都全部实现了,例如:对于 BPP < 8 的屏幕不支持 draw_pixel() 函数。调用不支持的函数将返回 ESP_ERR_NOT_SUPPORTED

显示方向

这里设置的屏幕显示方向是完全由屏幕硬件实现的,这个功能在不同的屏幕控制器上会有差异。一共有 8 种可能的显示方向,显示器可以旋转 0°、90°、180° 或 270°,也可以从顶部或底部查看,默认方向为 0° 和顶部查看。这 8 (4 × 2) 个不同的显示方向也可以表示为 3 个二进制开关的组合:X-mirroring、Y-mirroring 和 X/Y swapping。

下表将列出全部 8 种组合显示的方向。如果显示方向不正常,请查看下表中的配置开关使其正常工作。

original

0 [SCR_DIR_LRTB]

mirror_y

SCR_MIRROR_Y [SCR_DIR_LRBT]

mirror_x

SCR_MIRROR_X [SCR_DIR_RLTB]

mirror_xy

SCR_MIRROR_X| SCR_MIRROR_Y [SCR_DIR_RLBT]

swap_xy

SCR_SWAP_XY [SCR_DIR_TBLR]

swap_xy_mirror_y

SCR_SWAP_XY| SCR_MIRROR_Y [SCR_DIR_BTLR]

swap_xy_mirror_x

SCR_SWAP_XY| SCR_MIRROR_X [SCR_DIR_TBRL]

swap_xy_mirror_xy

SCR_SWAP_XY| SCR_MIRROR_X| SCR_MIRROR_Y [SCR_DIR_BTRL]

对于不同屏幕控制器,屏幕显示方向的实现并不完全相同,通常分为以下的情况:

  • 对于彩色屏幕,支持 8 个方向的旋转。

  • 对于单色屏幕,如 SSD1306 等屏幕来说,只支持 scr_dir_t 中定义的前 4 个方向,即不支持交换 XY 轴。

注解

显示方向还和使用的屏幕面板有关系,你可能会发现两种异常的情况:

  • 显示方向设置为 SCR_DIR_LRTB,屏幕却不是按照上表中对应的显示方向。这可能是因为屏幕面板上的走线对 X/Y 方向上进行了镜像,这时应该根据实际情况调整旋转以得到期望的显示方向。

  • 旋转了屏幕后,显示内容不见了。这可能是因为屏幕面板的分辨率小于屏幕控制器的分辨率,导致旋转后显示区域没有完全落在屏幕面板上,这时应考虑设置正确的显示区域偏移。

显示区域的偏移

在一些小尺寸的屏幕上,通常其可视区域分辨率小于所用屏幕控制器的分辨率。请参考以下示例图:

../_images/screen_offset.png

图中 Controller window 是屏幕控制器的显示窗口,分辨率为 240 × 320,Panel window 是屏幕面板窗口,分辨率为 135 × 240,可视的区域为屏幕面板区域。可以看出显示区域在水平方向上偏移了 52 个像素,在垂直方向上偏移了 40 个像素。

当屏幕逆时针旋转 90° 后,变成水平方向上偏移了 40 个像素,在垂直方向上偏移了 53 个像素,如下所示:

../_images/screen_offset_rotate.png

屏幕控制器驱动会帮助你自动根据屏幕的旋转来改变偏移的值,以保持正确的显示区域。你需要做的是在 scr_controller_config_t 中正确配置屏幕在 SCR_DIR_LRTB 方向时的偏移和屏幕面板尺寸。

注解

  • 显示偏移仅支持 BPP >= 8 的屏幕。

  • 当屏幕控制器是可选分辨率的时候,发现偏移不对可能是因为选择的分辨率与实际不符,此时应该修改程序,如: ILI9806 可尝试修改 ili9806.c 中的 ILI9806_RESOLUTION_VER 为实际的分辨率。

应用示例

初始化屏幕

scr_driver_t g_lcd; // A screen driver
esp_err_t ret = ESP_OK;

/** Initialize 16bit 8080 interface */
i2s_lcd_config_t i2s_lcd_cfg = {
    .data_width  = 16,
    .pin_data_num = {
        1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16
    },
    .pin_num_cs = 45,
    .pin_num_wr = 34,
    .pin_num_rs = 33,
    .clk_freq = 20000000,
    .i2s_port = I2S_NUM_0,
    .buffer_size = 32000,
    .swap_data = false,
};
scr_interface_driver_t *iface_drv;
scr_interface_create(SCREEN_IFACE_8080, &i2s_lcd_cfg, &iface_drv);

/** Find screen driver for ILI9806 */
ret = scr_find_driver(SCREEN_CONTROLLER_ILI9806, &g_lcd);
if (ESP_OK != ret) {
    ESP_LOGE(TAG, "screen find failed");
    return;
}

/** Configure screen controller */
scr_controller_config_t lcd_cfg = {
    .interface_drv = iface_drv,
    .pin_num_rst = -1,      // The reset pin is not connected
    .pin_num_bckl = -1,     // The backlight pin is not connected
    .rst_active_level = 0,
    .bckl_active_level = 1,
    .offset_hor = 0,
    .offset_ver = 0,
    .width = 480,
    .height = 854,
    .rotate = SCR_DIR_LRBT,
};

/** Initialize ILI9806 screen */
g_lcd.init(&lcd_cfg);

注解

默认情况下只打开了 ILI9341 屏幕的驱动,如果要使用其他的驱动,需要在 menuconfig -> Component config -> LCD Drivers -> Select Screen Controller 中使能对应屏幕驱动。

显示图像

/** Draw a red point at position (10, 20) */
lcd.draw_pixel(10, 20, COLOR_RED);

/** Draw a bitmap */
lcd.draw_bitmap(0, 0, width_of_pic, height_of_pic, pic_data);

获取屏幕信息

scr_info_t lcd_info;
lcd.get_info(&lcd_info);
ESP_LOGI(TAG, "Screen name:%s | width:%d | height:%d", lcd_info.name, lcd_info.width, lcd_info.height);

API 参考

Header File

Functions

esp_err_t scr_find_driver(scr_controller_t controller, scr_driver_t *out_screen)

Find a screen driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

  • ESP_ERR_NOT_FOUND Screen controller was not found.

Parameters
  • controller: Screen controller to initialize

  • out_screen: Pointer to a screen driver

Structures

struct scr_controller_config_t

configuration of screen controller

Public Members

scr_interface_driver_t *interface_drv

Interface driver for screen

int8_t pin_num_rst

Pin to hardreset LCD

int8_t pin_num_bckl

Pin for control backlight

uint8_t rst_active_level

Reset pin active level

uint8_t bckl_active_level

Backlight active level

uint16_t width

Screen width

uint16_t height

Screen height

uint16_t offset_hor

Offset of horizontal

uint16_t offset_ver

Offset of vertical

scr_dir_t rotate

Screen rotate direction

struct scr_info_t

Information of screen.

Public Members

uint16_t width

Current screen width, it may change when apply to rotate

uint16_t height

Current screen height, it may change when apply to rotate

scr_dir_t dir

Current screen direction

scr_color_type_t color_type

Color type of the screen, See scr_color_type_t struct

uint8_t bpp

Bits per pixel

const char *name

Name of the screen

struct scr_driver_t

Define a screen common function.

Public Members

esp_err_t (*init)(const scr_controller_config_t *lcd_conf)

Initialize screen.

Return

  • ESP_OK on success

  • ESP_FAIL Driver not installed

Parameters

esp_err_t (*deinit)(void)

Deinitialize screen.

Return

  • ESP_OK on success

  • ESP_FAIL Deinitialize failed

  • ESP_ERR_NOT_SUPPORTED unsupported

esp_err_t (*set_direction)(scr_dir_t dir)

Set screen direction of rotation.

Note

Not all screens support eight directions, it depends on the screen controller.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • dir: Pointer to a scr_dir_t structure. You can set the direction in two ways, for example, set it to “SCR_DIR_LRBT” or “SCR_MIRROR_Y”, They are the same, depending on which expression you want to use

esp_err_t (*set_window)(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1)

Set screen window.

Note

When the BPP of the screen controller is less than 8, the coordinate value is limited to a multiple of some number

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x0: Starting point in X direction

  • y0: Starting point in Y direction

  • x1: End point in X direction

  • y1: End point in Y direction

esp_err_t (*write_ram_data)(uint16_t color)

Write a RAM data.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • color: New color of a pixel

esp_err_t (*draw_pixel)(uint16_t x, uint16_t y, uint16_t color)

Draw one pixel in screen with color.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x: X co-ordinate of set orientation

  • y: Y co-ordinate of set orientation

  • color: New color of the pixel

esp_err_t (*draw_bitmap)(uint16_t x, uint16_t y, uint16_t w, uint16_t h, uint16_t *bitmap)

Fill the pixels on LCD screen with bitmap.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters
  • x: Starting point in X direction

  • y: Starting point in Y direction

  • w: width of image in bitmap array

  • h: height of image in bitmap array

  • bitmap: pointer to bitmap array

esp_err_t (*get_info)(scr_info_t *info)

Get screen information.

Return

  • ESP_OK on success

  • ESP_FAIL Failed

Parameters

Macros

COLOR_BLACK
COLOR_NAVY
COLOR_DARKGREEN
COLOR_DARKCYAN
COLOR_MAROON
COLOR_PURPLE
COLOR_OLIVE
COLOR_LIGHTGREY
COLOR_DARKGREY
COLOR_BLUE
COLOR_GREEN
COLOR_CYAN
COLOR_RED
COLOR_MAGENTA
COLOR_YELLOW
COLOR_WHITE
COLOR_ORANGE
COLOR_GREENYELLOW
COLOR_PINK
COLOR_SILVER
COLOR_GRAY
COLOR_LIME
COLOR_TEAL
COLOR_FUCHSIA
COLOR_ESP_BKGD

Enumerations

enum scr_dir_t

Define all screen direction.

Values:

SCR_DIR_LRTB

From left to right then from top to bottom, this consider as the original direction of the screen

SCR_DIR_LRBT

From left to right then from bottom to top

SCR_DIR_RLTB

From right to left then from top to bottom

SCR_DIR_RLBT

From right to left then from bottom to top

SCR_DIR_TBLR

From top to bottom then from left to right

SCR_DIR_BTLR

From bottom to top then from left to right

SCR_DIR_TBRL

From top to bottom then from right to left

SCR_DIR_BTRL

From bottom to top then from right to left

SCR_DIR_MAX
SCR_MIRROR_X = 0x40

Mirror X-axis

SCR_MIRROR_Y = 0x20

Mirror Y-axis

SCR_SWAP_XY = 0x80

Swap XY axis

enum scr_color_type_t

The types of colors that can be displayed on the screen.

Values:

SCR_COLOR_TYPE_MONO

The screen is monochrome

SCR_COLOR_TYPE_GRAY

The screen is gray

SCR_COLOR_TYPE_RGB565

The screen is colorful

enum scr_controller_t

All supported screen controllers.

Values:

SCREEN_CONTROLLER_ILI9341
SCREEN_CONTROLLER_ILI9342
SCREEN_CONTROLLER_ILI9806
SCREEN_CONTROLLER_ILI9486
SCREEN_CONTROLLER_ILI9488
SCREEN_CONTROLLER_NT35510
SCREEN_CONTROLLER_RM68120
SCREEN_CONTROLLER_ST7789
SCREEN_CONTROLLER_ST7796
SCREEN_CONTROLLER_SSD1351
SCREEN_CONTROLLER_SSD1963
SCREEN_CONTROLLER_SSD1306
SCREEN_CONTROLLER_SSD1307
SCREEN_CONTROLLER_SSD1322

Header File

Functions

esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_interface_driver_t **out_driver)

Create screen interface driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

  • ESP_FAIL Initialize failed

  • ESP_ERR_NO_MEM: Cannot allocate memory.

Parameters
  • type: Type of screen interface

  • config: configuration of interface driver

  • out_driver: Pointer to a screen interface driver

esp_err_t scr_interface_delete(const scr_interface_driver_t *driver)

Delete screen interface driver.

Return

  • ESP_OK on success

  • ESP_ERR_INVALID_ARG Arguments is NULL.

Parameters
  • driver: screen interface driver to delete

Structures

struct scr_interface_spi_config_t

SPI interface configuration.

Public Members

spi_bus_handle_t spi_bus

Handle of spi bus

int8_t pin_num_cs

SPI Chip Select Pin

int8_t pin_num_dc

Pin to select Data or Command for LCD

int clk_freq

SPI clock frequency

bool swap_data

Whether to swap data

struct scr_interface_i2c_config_t

I2C interface configuration.

Public Members

i2c_bus_handle_t i2c_bus

Handle of i2c bus

uint32_t clk_speed

I2C clock frequency for master mode, (no higher than 1MHz for now)

uint16_t slave_addr

I2C slave address

struct scr_interface_driver_t

Define common function for screen interface driver.

Public Members

scr_interface_type_t type

Interface bus type, see scr_interface_type_t struct

esp_err_t (*write_command)(void *handle, const uint8_t *cmd, uint32_t length)

Function to write command

esp_err_t (*write_data)(void *handle, uint16_t data)

Function to write a data

esp_err_t (*write)(void *handle, const uint8_t *data, uint32_t length)

Function to write a block data

esp_err_t (*read)(void *handle, uint8_t *data, uint32_t length)

Function to read a block data

esp_err_t (*bus_acquire)(void *handle)

Function to acquire interface bus

esp_err_t (*bus_release)(void *handle)

Function to release interface bus

Enumerations

enum scr_interface_type_t

Type of screen interface.

Values:

SCREEN_IFACE_I2C

I2C interface

SCREEN_IFACE_8080

8080 parallel interface

SCREEN_IFACE_SPI

SPI interface