|
rcbench
Servo and ESC test bench: panel, IOmcu and shared C
|
The EK79007 panel on the ESP32-P4's DSI host, written from the EK79007 datasheet and ESP-IDF's esp_lcd headers. More...
#include "display.h"#include <inttypes.h>#include <stdatomic.h>#include <string.h>#include "board.h"#include "board_pins.h"#include "driver/gpio.h"#include "driver/ppa.h"#include "esp_check.h"#include "esp_heap_caps.h"#include "esp_lcd_mipi_dsi.h"#include "esp_lcd_panel_io.h"#include "esp_lcd_panel_ops.h"#include "esp_ldo_regulator.h"#include "esp_log.h"#include "esp_timer.h"#include "freertos/FreeRTOS.h"#include "freertos/semphr.h"#include "freertos/task.h"#include "gfx_turn.h"Data Structures | |
| struct | ek_reg_t |
| One register write: a command and its one parameter byte. More... | |
| struct | display_state_t |
| The driver's state; all zero while the display is down. More... | |
Macros | |
| #define | DISPLAY_NUM_FBS 2 |
| Two frame buffers, always: the DPI panel scans one while the CPU draws the other, and display_flip() hands the finished one over. | |
| #define | FLIP_WAIT_MS 200 |
| The longest display_flip() waits for a frame-done event. | |
| #define | FB_BYTES ((size_t)BOARD_LCD_H_RES * BOARD_LCD_V_RES * sizeof(gfx_color_t)) |
| One frame buffer's bytes: 1024 x 600 x 2, 1,228,800. | |
| #define | LFB_ALIGN 128 |
| The logical buffers' alignment: the PSRAM's L2 cache line, 128 bytes. | |
| #define | RESET_LOW_MS 10 |
| The panel's reset: low for 10ms (the EK79007 needs 1ms), then 20ms before the first command (the datasheet states no minimum). | |
| #define | RESET_WAIT_MS 20 |
| Wait after the reset, ms: no minimum is stated in the datasheet. | |
| #define | SLEEP_OUT_MS 120 |
| After sleep out (0x11): the EK79007 takes the next command after 5ms and shows a picture about 7 frames later. 120ms covers both. | |
| #define | EK_CMD_SLEEP_OUT 0x11 |
| DCS (Display Command Set) commands, EK79007 register list. | |
| #define | EK_CMD_ADDRESS_MODE 0x36 |
| #define | EK_CMD_PANEL_CTRL2 0xB2 |
Functions | |
| static bool | on_frame_done (esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) |
| Frame-done callback: a frame buffer's scan ended. ISR (interrupt service routine) context. | |
| static bool | on_vsync (esp_lcd_panel_handle_t panel, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx) |
| VSYNC callback: count it and wake display_wait_vsync(). ISR context. | |
| static void | rebind_canvas (void) |
| Point the canvas at the UI's area of the back buffer, or of its logical buffer while turned, and start the frame's damage record. | |
| static bool | on_ppa_done (ppa_client_handle_t client, ppa_event_data_t *edata, void *user_ctx) |
| PPA transaction done: wake ppa_wait(). ISR (interrupt service routine) context, from the 2D-DMA's interrupt, which is not IRAM-safe in this build (CONFIG_DMA2D_ISR_IRAM_SAFE off) and so never runs while the flash cache is off. It touches the semaphore alone. | |
| static bool | ppa_submit (void *engine, const gfx_turn_job_t *job) |
| Start one job on the PPA: SRM (scale, rotate, mirror) at 180°, RGB565, non-blocking, its user_data the job's. | |
| static bool | ppa_wait (void *engine, uint32_t budget_us) |
Wait for on_ppa_done(), at most budget_us, in whole milliseconds up and at least one tick (CONFIG_FREERTOS_HZ 1000). | |
| static gfx_turn_copy_t | ppa_copy (void *ctx, const gfx_color_t *src, gfx_color_t *dst, gfx_rect_t r, uint32_t budget_us) |
| One block of the logical buffer into the frame buffer, turned by 180°, through the PPA (gfx_turn_engine_copy()). | |
| static uint32_t | turn_now_us (void *ctx) |
| The time for gfx_turn_flush(), us. | |
| static void | turn_up (void) |
| Screen rotation's buffers and engine: two logical buffers in PSRAM and a PPA client for 180° copies. A failure leaves the panel NORMAL (gfx_turn_fail()) and the display working. | |
| static void | turn_down (void) |
| Free what turn_up() got. A client the driver will not let go, because a transaction is still pending, keeps its semaphore, so a late on_ppa_done() never gives a deleted one. | |
| static esp_err_t | panel_reset (void) |
| Pulse the panel's reset line, RESET_LOW_MS low, then RESET_WAIT_MS. | |
| static esp_err_t | panel_commands (void) |
| Send the register table, then sleep out. | |
| static esp_err_t | bus_up (void) |
| Take the PHY supply, then create the DSI bus, the command interface and the DPI panel with its frame buffers. | |
| static void | bus_down (void) |
| Free whatever bus_up() and start() got, in reverse, after a failure or at deinit, and zero the state. | |
| static esp_err_t | start (const display_config_t *c) |
| Bring the display up: semaphores, bus, callbacks, frame buffers, panel reset and commands, video stream. | |
| esp_err_t | display_init (const display_config_t *cfg) |
| Bring up the DSI PHY supply, the bus, the panel and the video stream, and the board's I2C bus and backlight if not already up. | |
| esp_err_t | display_deinit (void) |
| Stop the video stream and release the panel, the bus and the PHY supply. | |
| gfx_canvas_t * | display_canvas (void) |
| The canvas the application draws into. The returned pointer is stable for the lifetime of the driver; display_flip() re-points it at the new back buffer in place. | |
| esp_err_t | display_wait_vsync (uint32_t timeout_ms) |
| Block until the next VSYNC (vertical sync) event. | |
| static void | account_frame (void) |
| Count a flip and update the rolling rate every 500ms. | |
| static esp_err_t | wait_frame_done (void) |
| Wait for the next frame-done event, at most FLIP_WAIT_MS. | |
| esp_err_t | display_flip (void) |
| Present the back buffer and block until the buffer it replaces is no longer scanned out, at most 200ms. | |
| int | display_width (void) |
| The canvas's width: the UI's area. | |
| int | display_height (void) |
| The canvas's height: the UI's area. | |
| int | display_back_index (void) |
| Which frame buffer display_canvas() points at. | |
| uint32_t | display_last_wait_us (void) |
| How long the last display_flip() spent blocked. | |
| uint32_t | display_frame_count (void) |
| Frames presented since display_init(). | |
| float | display_fps (void) |
| Rolling frames per second, measured over the last 500ms. | |
| uint32_t | display_vsync_count (void) |
| VSYNC events since display_init(): the panel's own refresh, whatever the flips do. | |
| void | display_set_turned (bool turned) |
| Screen rotation: 180°, or NORMAL, from the next display_flip() on. May be called from any task, also before display_init(). | |
| bool | display_turned (void) |
| Whether the picture on the glass is turned by 180°: the flag the touch map turns by (gfx_turn_shown()). | |
| bool | display_take_repaint (void) |
| Whether the UI must repaint everything before it draws the next frame: the canvas changed between a logical buffer and a frame buffer. Clears it. Call it from the task that flips. | |
| bool | display_take_turn_alert (void) |
| Whether the alert that Screen rotation 180° fell back to NORMAL is due. Clears it. Call it from the task that flips. | |
| uint32_t | display_last_turn_us (void) |
| The time the last display_flip() spent copying turned blocks. | |
| esp_err_t | display_backlight (bool on) |
| Backlight at 100% or 0%. | |
Variables | |
| static const char * | TAG = "display" |
| static const ek_reg_t | k_init [] |
| The initialisation table. | |
| static display_state_t | s_disp |
| The one display. | |
| static atomic_bool | s_want |
| What Screen rotation asks for (display_set_turned()). Outside s_disp: it is set before display_init() and from any task; the flip reads it. | |
| static const gfx_turn_engine_t | k_ppa_engine |
| The PPA as a gfx_turn engine; its done callback gets s_disp. | |
| static const gfx_turn_ops_t | k_turn_ops |
| The PPA as gfx_turn_flush()'s copy engine. | |
The EK79007 panel on the ESP32-P4's DSI host, written from the EK79007 datasheet and ESP-IDF's esp_lcd headers.
Bring-up order: the PHY supply, the bus, the command interface, the DPI panel (frame buffers allocated and black, video not started), the panel's reset, the initialisation commands, then the video stream.
Screen rotation 180° turns the picture in the flush (gfx_turn.h): the UI draws into a logical buffer, and display_flip() copies what it drew into the frame buffer, turned, through the PPA (pixel-processing accelerator) before it presents that buffer. The EK79007's scan direction (0x36) cannot do it on this board: its PINCTL strap holds the scan direction and the register is ignored, at start-up as while the video runs.
SPDX-License-Identifier: MIT
| #define EK_CMD_ADDRESS_MODE 0x36 |
EK79007 address mode command, DCS 0x36
| #define EK_CMD_PANEL_CTRL2 0xB2 |
bit 4: 2 lanes; bit 6: normally black
|
static |
Take the PHY supply, then create the DSI bus, the command interface and the DPI panel with its frame buffers.
| int display_back_index | ( | void | ) |
Which frame buffer display_canvas() points at.
The buffers alternate, so static content has to be drawn into both. A per-buffer validity flag keyed on this index keeps unchanging chrome out of the per-frame path, which on a panel fed from PSRAM decides whether the refresh rate is met.
| esp_err_t display_backlight | ( | bool | on | ) |
Backlight at 100% or 0%.
| on | true for 100%, false for 0% (dark). |
| gfx_canvas_t * display_canvas | ( | void | ) |
The canvas the application draws into. The returned pointer is stable for the lifetime of the driver; display_flip() re-points it at the new back buffer in place.
| esp_err_t display_deinit | ( | void | ) |
Stop the video stream and release the panel, the bus and the PHY supply.
| esp_err_t display_flip | ( | void | ) |
Present the back buffer and block until the buffer it replaces is no longer scanned out, at most 200ms.
| float display_fps | ( | void | ) |
Rolling frames per second, measured over the last 500ms.
| uint32_t display_frame_count | ( | void | ) |
Frames presented since display_init().
| int display_height | ( | void | ) |
The canvas's height: the UI's area.
| esp_err_t display_init | ( | const display_config_t * | cfg | ) |
Bring up the DSI PHY supply, the bus, the panel and the video stream, and the board's I2C bus and backlight if not already up.
| cfg | The set-up; NULL takes DISPLAY_CONFIG_DEFAULT(). |
| uint32_t display_last_turn_us | ( | void | ) |
The time the last display_flip() spent copying turned blocks.
| uint32_t display_last_wait_us | ( | void | ) |
How long the last display_flip() spent blocked.
| void display_set_turned | ( | bool | turned | ) |
Screen rotation: 180°, or NORMAL, from the next display_flip() on. May be called from any task, also before display_init().
| turned | true for 180° |
| bool display_take_repaint | ( | void | ) |
Whether the UI must repaint everything before it draws the next frame: the canvas changed between a logical buffer and a frame buffer. Clears it. Call it from the task that flips.
| bool display_take_turn_alert | ( | void | ) |
Whether the alert that Screen rotation 180° fell back to NORMAL is due. Clears it. Call it from the task that flips.
| bool display_turned | ( | void | ) |
Whether the picture on the glass is turned by 180°: the flag the touch map turns by (gfx_turn_shown()).
| uint32_t display_vsync_count | ( | void | ) |
VSYNC events since display_init(): the panel's own refresh, whatever the flips do.
| esp_err_t display_wait_vsync | ( | uint32_t | timeout_ms | ) |
Block until the next VSYNC (vertical sync) event.
| timeout_ms | The longest wait in milliseconds; 0 waits forever. |
| int display_width | ( | void | ) |
The canvas's width: the UI's area.
|
static |
Frame-done callback: a frame buffer's scan ended. ISR (interrupt service routine) context.
| panel | Unused. |
| edata | Unused. |
| user_ctx | The display_state_t. |
|
static |
PPA transaction done: wake ppa_wait(). ISR (interrupt service routine) context, from the 2D-DMA's interrupt, which is not IRAM-safe in this build (CONFIG_DMA2D_ISR_IRAM_SAFE off) and so never runs while the flash cache is off. It touches the semaphore alone.
The driver passes each transaction's user_data, which ppa_submit() sets from the job (gfx_turn_engine_copy() fills it with &s_disp). A NULL context, or a semaphore already deleted, is counted (gfx_turn_stray_dones()) or skipped; the wait then times out and the flush falls back to NORMAL.
| client | Unused. |
| edata | Unused. |
| user_ctx | The display_state_t. |
|
static |
VSYNC callback: count it and wake display_wait_vsync(). ISR context.
| panel | Unused. |
| edata | Unused. |
| user_ctx | The display_state_t. |
|
static |
Send the register table, then sleep out.
All in LP (low-power) mode on virtual channel 0, before the video stream starts.
|
static |
Pulse the panel's reset line, RESET_LOW_MS low, then RESET_WAIT_MS.
|
static |
One block of the logical buffer into the frame buffer, turned by 180°, through the PPA (gfx_turn_engine_copy()).
| ctx | Unused: k_ppa_engine holds the state. |
| src | The logical buffer. |
| dst | The frame buffer of the same index. |
| r | The block, in logical coordinates. |
| budget_us | The longest wait, us. |
|
static |
Start one job on the PPA: SRM (scale, rotate, mirror) at 180°, RGB565, non-blocking, its user_data the job's.
| engine | The display_state_t. |
| job | The block (gfx_turn_engine_copy()). |
|
static |
Wait for on_ppa_done(), at most budget_us, in whole milliseconds up and at least one tick (CONFIG_FREERTOS_HZ 1000).
| engine | The display_state_t. |
| budget_us | The longest wait, us. |
|
static |
Bring the display up: semaphores, bus, callbacks, frame buffers, panel reset and commands, video stream.
| c | The set-up. |
|
static |
|
static |
Wait for the next frame-done event, at most FLIP_WAIT_MS.
|
static |
The initialisation table.
Every register takes one parameter byte and takes effect at the next valid frame. The lane count goes first: the chip resets to 4 lanes. 0x80 to 0x86 are the 14 gamma fields, two per register; the values are the ones Espressif's EK79007 driver writes (Apache-2.0), against 0x88 in every register after reset.
|
static |
The PPA as a gfx_turn engine; its done callback gets s_disp.
|
static |
The PPA as gfx_turn_flush()'s copy engine.