rgb_lcd: restart when dma eof interrupt is delayed

This commit is contained in:
morris 2023-04-04 18:34:57 +08:00
parent 771883395b
commit 35d0835508
2 changed files with 16 additions and 0 deletions

View File

@ -105,6 +105,8 @@ struct esp_rgb_panel_t {
size_t bb_size; // If not-zero, the driver uses two bounce buffers allocated from internal memory
int bounce_pos_px; // Position in whatever source material is used for the bounce buffer, in pixels
uint8_t *bounce_buffer[RGB_LCD_PANEL_BOUNCE_BUF_NUM]; // Pointer to the bounce buffers
size_t bb_eof_count; // record the number we received the DMA EOF event, compare with `expect_eof_count` in the VSYNC_END ISR
size_t expect_eof_count; // record the number of DMA EOF event we expected to receive
gdma_channel_handle_t dma_chan; // DMA channel handle
esp_lcd_rgb_panel_vsync_cb_t on_vsync; // VSYNC event callback
esp_lcd_rgb_panel_bounce_buf_fill_cb_t on_bounce_empty; // callback used to fill a bounce buffer rather than copying from the frame buffer
@ -245,10 +247,12 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
// calculate buffer size
size_t fb_size = rgb_panel_config->timings.h_res * rgb_panel_config->timings.v_res * fb_bits_per_pixel / 8;
size_t bb_size = rgb_panel_config->bounce_buffer_size_px * fb_bits_per_pixel / 8;
size_t expect_bb_eof_count = 0;
if (bb_size) {
// we want the bounce can always end in the second buffer
ESP_GOTO_ON_FALSE(fb_size % (2 * bb_size) == 0, ESP_ERR_INVALID_ARG, err, TAG,
"fb size must be even multiple of bounce buffer size");
expect_bb_eof_count = fb_size / bb_size;
}
// calculate the number of DMA descriptors
@ -270,6 +274,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
rgb_panel->num_fbs = num_fbs;
rgb_panel->fb_size = fb_size;
rgb_panel->bb_size = bb_size;
rgb_panel->expect_eof_count = expect_bb_eof_count;
rgb_panel->panel_id = -1;
// register to platform
int panel_id = lcd_com_register_device(LCD_COM_DEVICE_TYPE_RGB, rgb_panel);
@ -915,6 +920,9 @@ static IRAM_ATTR bool lcd_rgb_panel_eof_handler(gdma_channel_handle_t dma_chan,
// Figure out which bounce buffer to write to.
// Note: what we receive is the *last* descriptor of this bounce buffer.
int bb = (desc == &panel->dma_nodes[panel->num_dma_nodes - 1]) ? 0 : 1;
portENTER_CRITICAL_ISR(&panel->spinlock);
panel->bb_eof_count++;
portEXIT_CRITICAL_ISR(&panel->spinlock);
return lcd_rgb_panel_fill_bounce_buffer(panel, panel->bounce_buffer[bb]);
}
@ -1008,6 +1016,10 @@ static IRAM_ATTR void lcd_rgb_panel_try_restart_transmission(esp_rgb_panel_t *pa
panel->flags.need_restart = false;
do_restart = true;
}
if (panel->bb_eof_count < panel->expect_eof_count) {
do_restart = true;
}
panel->bb_eof_count = 0;
portEXIT_CRITICAL_ISR(&panel->spinlock);
#endif // CONFIG_LCD_RGB_RESTART_IN_VSYNC

View File

@ -387,6 +387,10 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
It's highly recommended to turn on the "PSRAM XIP (Execute In Place)" feature in this mode by enabling the Kconfig options: :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA`, which allows the CPU to fetch instructions and readonly data from the PSRAM instead of the main flash. What's more, the external memory cache won't be disabled even if you attempt to write to the main flash through SPI1. This makes it possible to display an OTA progress bar for your application.
.. note::
This mode still has another problem which is also caused by insufficient PSRAM bandwidth. e.g. when your draw buffers are allocated from PSRAM, and their contents are copied into the internal frame buffer on CPU core 1. On CPU core 0, there's another memory copy happening in the DMA EOF ISR. In this situation, both CPUs are accessing the PSRAM by cache and sharing the bandwidth of the PSRAM. This increases the memory copy time that spent in the DMA EOF ISR significantly. The driver can't switch the bounce buffer in time, thus leading to a shift on the LCD screen. Although the driver can detect such a condition and perform a restart in the LCD's VSYNC interrupt handler, you still can see a flickering on the screen.
.. code:: c
esp_lcd_panel_handle_t panel_handle = NULL;