Deki 自己的显示库。驱动大多数爱好者开发板使用的屏幕,读取它们的触摸,并完整地发送每一帧。
仓库:github.com/dekiengine/deki-display · 最新版本:0.20.0 · 需要引擎 0.20.0 或更高版本 · 依赖:deki-spi, deki-gpio, deki-i2c, deki-input, deki-rendering
使用 DekiEditor --packages-add deki-display <project> 或通过包管理器安装。引擎自身的组件和 C++ API 见 Deki 文档。
在编辑器中显示为 **Display**。
组件
| 组件 | 类别 | 说明 |
| DisplayPanelComponent | 显示屏 | 使用 Deki 自己的显示库,通过 SPI、四线 SPI 或 8 位、16 位并行总线驱动显示面板(ST7789、ILI9341、GC9A01 等),并将其交给引擎。 |
| TouchPanelComponent | 显示屏 | 使用 Deki 自己的显示库读取面板的触摸控制器(I2C 上的 GT911、CST816、FT5x06;SPI 上的 XPT2046),作为指针输入。 |
功能开关
可以从构建中去掉的部分。带有 define 的功能通过该 define 开关,其余的随其组件一起被剥离。
| 功能 | define | 默认值 |
| Display Panel | DEKI_FEATURE_DISPLAY_PANEL | 开 |
| Touch Panel | DEKI_FEATURE_DISPLAY_TOUCH | 开 |
组件参考
DisplayPanelComponent
使用 Deki 自己的显示库,通过 SPI、四线 SPI 或 8 位、16 位并行总线驱动显示面板(ST7789、ILI9341、GC9A01 等),并将其交给引擎。
检视器中的名称:显示面板组件 · 类别:显示屏 · 类型:DekiDisplay::DisplayPanelComponent
| 属性 | 类型 | 默认值 | 约束 | 说明 |
| controller | enum | "ST7789" | ST7789, RM67162, ILI9341, ST7735, ST7735S, GC9A01, ST7789P3, ST7796, ILI9488, SSD1351, CO5300 | 面板的控制器 IC |
| initSequence | string | "" | | 启动此面板模块的命令,用来替换控制器的默认命令。面板的电压、VCOM 和伽马属于模块而不是控制器,因此由开发板在这里设置。每行或每个 ';' 一条命令:先写十六进制的命令字节,再写参数字节,可选地加上 'delay <ms>'。'#' 之后为注释。留空:使用控制器的默认命令 |
| panelWidth | int32 | 240 | 1 到 1024 | 旋转前的可见宽度(像素) |
| panelHeight | int32 | 320 | 1 到 1024 | 旋转前的可见高度(像素) |
| rotation | enum | "Landscape" | Portrait, Landscape, Portrait180, Landscape270, PortraitMirror, LandscapeMirror, Portrait180Mirror, Landscape270Mirror | 面板的朝向 |
| offsetX | int32 | 0 | | 可见面板在控制器显存中的起始位置,横向(用于原点不为 0 的面板) |
| offsetY | int32 | 0 | | 可见面板在控制器显存中的起始位置,纵向 |
| invertColor | bool | false | | 反转面板颜色(大多数 IPS 面板需要开启) |
| rgbOrder | bool | false | | 面板先接收红色。关闭时设置控制器的 BGR 顺序 |
| busType | enum | "SPI" | SPI, QSPI, Parallel8, Parallel16 | 面板的接线方式:带数据/命令引脚的 SPI(大多数 LCD)、四线 SPI(RM67162 等 AMOLED 面板,需要带 IO2 和 IO3 的 SPI Bus),或 8 位、16 位并行总线(8080,由本组件自行启动) |
| spiHost | int32 | 1 | 0 到 3;busType 允许时显示 | 面板所在的 SPI 主机,与启动它的 SPI Bus 组件编号一致(在 ESP32-S3 上:1 = SPI2,2 = SPI3) |
| spiWriteHz | int32 | 40000000 | busType 允许时显示 | 面板的 SPI 时钟。80 MHz 是 ESP32-S3 的最快速度,大多数 ST7789 模块都支持 |
| parallelWriteHz | int32 | 20000000 | busType 允许时显示 | 并行总线上写选通的频率。大多数面板支持 20 MHz;连线短的可以更高 |
| dcPin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据/命令引脚(DC,也叫 RS) |
| wrPin | int32 | -1 | -1 到 48;busType 允许时显示 | 写选通引脚(WR) |
| rdPin | int32 | -1 | -1 到 48;busType 允许时显示 | 读选通引脚(RD),保持高电平:只向面板写入(-1 = 在板上固定接高电平) |
| d0Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D0 |
| d1Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D1 |
| d2Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D2 |
| d3Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D3 |
| d4Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D4 |
| d5Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D5 |
| d6Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D6 |
| d7Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D7 |
| d8Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D8 |
| d9Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D9 |
| d10Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D10 |
| d11Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D11 |
| d12Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D12 |
| d13Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D13 |
| d14Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D14 |
| d15Pin | int32 | -1 | -1 到 48;busType 允许时显示 | 数据线 D15 |
| csPin | int32 | -1 | -1 到 48 | 片选引脚(-1 = 固定接低电平) |
| rstPin | int32 | -1 | -1 到 48 | 复位引脚(-1 = 未连接) |
| tePin | int32 | -1 | -1 到 48 | 面板的撕裂效应(TE)输出(-1 = 未连接)。接上后画面不会撕裂:每一帧在面板开始刷新时开始发送,并按面板自身的行顺序发送;旋转的屏幕在发送时旋转,并按列条带绘制 |
| scanlineSync | bool | true | busType 允许时显示 | 没有接 TE 线时,改为读取面板的扫描线,让画面依然不会撕裂。无法回读的面板按绘制顺序发送,启动日志会说明 |
| spiReadHz | int32 | 10000000 | scanlineSync 允许时显示 | 回读面板时的 SPI 时钟,比写入慢(ST7789 在 10 MHz 下读取可靠) |
| readsOnMosi | bool | false | scanlineSync 允许时显示 | 面板在数据输入线上应答(SDA 双向,如 LilyGO T-Deck),而不是在 MISO 上 |
| blPin | int32 | -1 | -1 到 48 | 背光引脚(-1 = 无)。点亮时保持高电平:通过计数脉冲调光的背光驱动器(LilyGO T-Deck 的)正需要这样 |
| blInvert | bool | false | | 引脚为低电平时背光点亮 |
| memoryWidth | int32 | 0 | 0 到 1024 | 控制器的显存宽度,可能大于可见宽度(0 = 控制器的值) |
| memoryHeight | int32 | 0 | 0 到 1024 | 控制器的显存高度(0 = 控制器的值) |
| usePsram | bool | false | | 将帧缓冲放在 PSRAM 而不是内部 RAM 中:可腾出一屏大小的内部 RAM,但绘制会变慢 |
| doubleBuffer | bool | false | | 两个帧缓冲区,每帧后交换 |
| asyncPresent | bool | true | | 在绘制下一帧的同时,从芯片的另一个核心发送每一帧:一帧的耗时是绘制和发送中较长的那个,而不是两者之和。需要双核芯片和两帧的外部内存(PSRAM);没有时按绘制顺序发送,启动日志会说明 |
TouchPanelComponent
使用 Deki 自己的显示库读取面板的触摸控制器(I2C 上的 GT911、CST816、FT5x06;SPI 上的 XPT2046),作为指针输入。
检视器中的名称:触摸面板组件 · 类别:显示屏 · 类型:DekiDisplay::TouchPanelComponent
| 属性 | 类型 | 默认值 | 约束 | 说明 |
| controller | enum | "GT911" | GT911, CST816, FT5x06, XPT2046 | 触摸控制器 IC |
| i2cPort | int32 | 0 | 0 到 3;controller 允许时显示 | 它所在的 I2C 端口(必须在此组件之前由 I2C Bus 组件启动该端口) |
| i2cAddress | int32 | 0 | 0 到 127;controller 允许时显示 | 其 I2C 地址;0 表示使用控制器的常用地址(GT911:0x5D,无应答时改用 0x14;CST816:0x15;FT5x06:0x38) |
| spiHost | int32 | 1 | 0 到 3;controller 允许时显示 | 其所在的 SPI 主机,与启动它的 SPI Bus 组件编号一致(通常与显示屏相同) |
| csPin | int32 | -1 | -1 到 48;controller 允许时显示 | 其片选引脚 |
| spiHz | int32 | 1000000 | controller 允许时显示 | 触摸控制器的 SPI 时钟。XPT2046 在 1 MHz 下读取效果好,最高支持 2.5 MHz |
| intPin | int32 | -1 | -1 到 48 | 中断引脚:有触摸报告等待读取时,或按下期间(XPT2046 的 PENIRQ),控制器将其拉低(-1 = 未连接,每帧读取) |
| rstPin | int32 | -1 | -1 到 48;controller 允许时显示 | 复位引脚(-1 = 未连接) |
| xMin | int32 | 0 | | 触摸控制器报告的原始 X 最小值,沿显示屏旋转前的面板宽度方向(电容式:0;XPT2046 从约 300 开始,需要校准) |
| xMax | int32 | 239 | | 触摸控制器报告的原始 X 最大值,沿显示屏旋转前的面板宽度方向(240x320 面板横向显示时:239) |
| yMin | int32 | 0 | | 触摸控制器报告的原始 Y 最小值,沿显示屏旋转前的面板高度方向 |
| yMax | int32 | 319 | | 触摸控制器报告的原始 Y 最大值,沿显示屏旋转前的面板高度方向(240x320 面板横向显示时:319) |
| offsetRotation | enum | "None" | None, CW_90, CW_180, CW_270, Mirror, Mirror_CW_90, Mirror_CW_180, Mirror_CW_270 | 触摸控制器相对面板的旋转方向,会叠加到显示屏旋转上。控制器按面板自身方向报告坐标时选择“无” |
更新日志
更新日志为英文。
0.20.0
Changed
- minEngine 0.20.0: built against engine 0.20.0. A build of this package from before does not load and is rebuilt.
Added
- The package: DisplayPanelComponent, which starts a panel on a deki-spi bus from the boot scene and gives the engine PanelDisplay, a Deki::IDisplay presenting whole frames and changed rectangles with the UI overlay blended over them; PanelController, a MIPI DCS controller driven by a table per controller (ST7789 first, its start-up and MADCTL ported from LovyanGFX); SpiPanelIO, 4-wire SPI with pixels queued for DMA; and initSequence, a board's own start-up for its panel module, as text.
- Async present (asyncPresent): each frame is sent from the other core while the next is drawn. A whole frame is copied, swapped into the wire's byte order as it is copied, to one of two copies in external DMA memory, and sent from there by DMA in parts as long as the bus takes, with no bands in internal RAM; a strip is copied to one of two copies in internal DMA memory and swapped in place a chunk at a time. The worker holds the bus across back-to-back frames and strips, with each one's window commands queued behind the one before, so the wire does not wait between them; a copy goes back to the drawing core as soon as its last part is sent. The platform runs the worker through PanelDisplay::SetWorkerBackend (deki-esp32-integration: a FreeRTOS task pinned to the other core). On a LilyGO T-Deck Plus at 80 MHz with the frame rate uncapped: 63.2 fps in whole frames with 83.6 KB of internal RAM free, 61.0 in strips of 40 rows (LovyanGFX: 62.6 with 73.8 KB, and 60.2).
- Touch: TouchPanelComponent starts a GT911 (GT911Touch, ported from LovyanGFX's driver) on a deki-i2c bus, shared with whatever else is on it, and gives deki-input PanelTouchInput: a press, moves and a release at screen pixels, turned with the display by TouchMapper (LovyanGFX's default calibration and rotation, so a board's touch bounds carry over). Without an INT pin, a point that stops changing is released and not pressed again until it moves.
- QSPI panels: DisplayPanelComponent's busType QSPI drives an AMOLED through QspiPanelIO (each register write framed 0x02, pixels 0x32 on four lines, as LovyanGFX frames them). The RM67162 is the first: its start-up, MADCTL and offsets ported from LovyanGFX, rows widened to even, and brightness by command where an LCD has a backlight.
- The CST816S/T touch controller (TouchController::CST816). It sleeps, and does not answer on I2C, until touched: it is set up when it first answers, and a read it does not answer is no touch.
- Async present works with render buffers (usePsram, doubleBuffer) too: the worker sends a copy, so the engine never draws into what is being sent.
- Tear-free presenting: DisplayPanelComponent's tePin, the panel's tearing-effect output. A panel refreshes its own rows one after another, and a frame written while the refresh passes shows half of each frame. With TE on (0x35), each frame starts as the panel starts a refresh, and goes in the panel's own row order, which a write outruns the refresh in. A screen shown turned is turned as it is sent: the controller stays at its own orientation, and the renderer is asked for strips of columns in the order the panel refreshes them (deki-rendering's IScanOrderedDisplay). With async present the worker does the turning, a band at a time while the band before it is on the wire. Whole frames and strips both. A TE line that never pulses is said once in the log, and frames go on without waiting. On a LilyGO T-Display S3 AMOLED (its panel refreshes at 59.6 Hz): 59.5 fps in strips with no tearing, and 28.6 in whole frames, which take 25 ms to draw in PSRAM.
- More controllers, each a table ported from LovyanGFX 1.2.29: ILI9341, ST7735 (the B version), ST7735S (the R version), GC9A01, ST7789P3, ST7796, ILI9488 and SSD1351 on SPI or a parallel bus, and the CO5300 AMOLED on QSPI. A controller names its own commands (ControllerCommands): the SSD1351's window is a byte a coordinate, its column and row commands trade places when turned, and its remap takes MADCTL's place. The GC9A01 opens its rows before a turned window's columns, as LovyanGFX does. Checked against LovyanGFX's bytes in tests; none has run on a board yet.
- 18 bits a pixel: the ILI9488 takes no RGB565 over SPI, so its pixels go out as RGB666 through Rgb666PanelIO, which widens each piece into its own DMA buffers while the piece before is sent, and COLMOD is 0x66.
- Parallel panels: busType Parallel8 and Parallel16, an 8080 bus of 8 or 16 data lines with DC, WR and RD pins and its write clock (parallelWriteHz). The platform drives the bus (ParallelBus, a factory it registers; ParallelPanelIO speaks to it), and the component starts it itself. On 16 lines a pixel goes as memory holds it, so a little-endian frame needs no byte swap.
- Touch: the FT5x06 family (FT5206, FT6206, FT6336 and others) on I2C, and the XPT2046 resistive controller on SPI, both ported from LovyanGFX. The XPT2046's raw readings need calibrating in the touch bounds. A controller whose count or pressure says when the finger has gone (ITouchController::MayRepeatLastTouch false) is not released for holding still.
- Tear-free: a frame that missed its refresh's start is started for the longest write any frame has taken (PanelDisplay::LongestWriteUs), not the shorter of the last two. The frame that opens a new scene is late (the scene loads in it) and changes the whole screen, while the frames before it changed a little: started for theirs, it began too late, the refresh caught it, and the screen tore across.
- Tear-free with no TE line: scanlineSync reads the panel's scanline (Get_Scanline, 0x45) back through the panel IO (IPanelIO::Read) at its own clock (spiReadHz), on MISO or, with readsOnMosi, on the panel's data line. ScanlineClock finds two refresh starts, where the count drops from the last row to the blanking (it holds there; the low byte rolling over at row 256 does not), and keeps the clock right with each start it catches. On a LilyGO T-Deck Plus: 60.2 Hz found, every demo scene at 60 fps with frames in step with the refresh.
- A frame that takes about a refresh to go out, or up to two, is started just behind the refresh rather than at once (PanelDisplay::WritesBehind): it never catches the refresh's rows up, and the next refresh shows it whole. Before, such a frame was taken to tear wherever it started.
- A turned frame is read a screen row at a time while it is turned, a run of neighbouring pixels from each, instead of one pixel per screen row: a whole frame in PSRAM, turned on the way out, went from 47.8 fps to 60 on the T-Deck Plus.
- asyncPresent and scanlineSync are on by default: a board gets frames sent from the other core, and tear-free frames without a TE line, where it can. Where it cannot (one core, no PSRAM for the copies, a panel that cannot be read back) frames are sent as they are drawn, and the boot log says so as information, not an error. Scenes that set them keep what they set.
更早的版本:CHANGELOG.md
由包的反射数据生成(引擎 0.20.2)。属性名就是场景文件中使用的键。重命名后会保留别名(兼容性)。