kopia lustrzana https://github.com/espressif/esp-idf
lcd: support putting RGB frame buffer in PSRAM
rodzic
21067a0455
commit
dc1d14a37f
|
@ -54,6 +54,7 @@ typedef struct {
|
|||
struct {
|
||||
unsigned int disp_active_low: 1; /*!< If this flag is enabled, a low level of display control signal can turn the screen on; vice versa */
|
||||
unsigned int relax_on_idle: 1; /*!< If this flag is enabled, the host won't refresh the LCD if nothing changed in host's frame buffer (this is usefull for LCD with built-in GRAM) */
|
||||
unsigned int fb_in_psram: 1; /*!< If this flag is enabled, the frame buffer will be allocated from PSRAM preferentially */
|
||||
} flags;
|
||||
} esp_lcd_rgb_panel_config_t;
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <sys/cdefs.h>
|
||||
#include <sys/param.h>
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
@ -27,6 +28,9 @@
|
|||
#include "esp_private/gdma.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#if CONFIG_SPIRAM
|
||||
#include "spiram.h"
|
||||
#endif
|
||||
#if SOC_LCDCAM_SUPPORTED
|
||||
#include "esp_lcd_common.h"
|
||||
#include "soc/lcd_periph.h"
|
||||
|
@ -37,6 +41,9 @@ static const char *TAG = "lcd_panel.rgb";
|
|||
|
||||
typedef struct esp_rgb_panel_t esp_rgb_panel_t;
|
||||
|
||||
// This function is located in ROM (also see esp_rom/${target}/ld/${target}.rom.ld)
|
||||
extern int Cache_WriteBack_Addr(uint32_t addr, uint32_t size);
|
||||
|
||||
static esp_err_t rgb_panel_del(esp_lcd_panel_t *panel);
|
||||
static esp_err_t rgb_panel_reset(esp_lcd_panel_t *panel);
|
||||
static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel);
|
||||
|
@ -72,11 +79,12 @@ struct esp_rgb_panel_t {
|
|||
int x_gap; // Extra gap in x coordinate, it's used when calculate the flush window
|
||||
int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window
|
||||
struct {
|
||||
int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num`
|
||||
int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
|
||||
int new_frame: 1; // Whether the frame we're going to flush is a new one
|
||||
unsigned int disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num`
|
||||
unsigned int stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
|
||||
unsigned int new_frame: 1; // Whether the frame we're going to flush is a new one
|
||||
unsigned int fb_in_psram: 1; // Whether the frame buffer is in PSRAM
|
||||
} flags;
|
||||
dma_descriptor_t dma_nodes[0]; // DMA descriptor pool of size `num_dma_nodes`
|
||||
dma_descriptor_t dma_nodes[]; // DMA descriptor pool of size `num_dma_nodes`
|
||||
};
|
||||
|
||||
esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_config, esp_lcd_panel_handle_t *ret_panel)
|
||||
|
@ -96,10 +104,24 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
|||
rgb_panel = heap_caps_calloc(1, sizeof(esp_rgb_panel_t) + num_dma_nodes * sizeof(dma_descriptor_t), MALLOC_CAP_DMA);
|
||||
ESP_GOTO_ON_FALSE(rgb_panel, ESP_ERR_NO_MEM, no_mem_panel, TAG, "no mem for rgb panel");
|
||||
rgb_panel->num_dma_nodes = num_dma_nodes;
|
||||
// alloc frame buffer, currently we have to put the frame buffer in SRAM
|
||||
// alloc frame buffer
|
||||
bool alloc_from_psram = false;
|
||||
// fb_in_psram is only an option, if there's no PSRAM on board, we still alloc from SRAM
|
||||
if (rgb_panel_config->flags.fb_in_psram) {
|
||||
#if CONFIG_SPIRAM_USE_MALLOC || CONFIG_SPIRAM_USE_CAPS_ALLOC
|
||||
if (esp_spiram_is_initialized()) {
|
||||
alloc_from_psram = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (alloc_from_psram) {
|
||||
rgb_panel->fb = heap_caps_calloc(1, fb_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||
} else {
|
||||
rgb_panel->fb = heap_caps_calloc(1, fb_size, MALLOC_CAP_INTERNAL);
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(rgb_panel->fb, ESP_ERR_NO_MEM, no_mem_fb, TAG, "no mem for frame buffer");
|
||||
rgb_panel->fb_size = fb_size;
|
||||
rgb_panel->flags.fb_in_psram = alloc_from_psram;
|
||||
// semaphore indicates new frame trans done
|
||||
rgb_panel->done_sem = xSemaphoreCreateBinary();
|
||||
ESP_GOTO_ON_FALSE(rgb_panel->done_sem, ESP_ERR_NO_MEM, no_mem_sem, TAG, "create done sem failed");
|
||||
|
@ -113,7 +135,7 @@ esp_err_t esp_lcd_new_rgb_panel(const esp_lcd_rgb_panel_config_t *rgb_panel_conf
|
|||
// initialize HAL layer, so we can call LL APIs later
|
||||
lcd_hal_init(&rgb_panel->hal, panel_id);
|
||||
// install interrupt service, (LCD peripheral shares the interrupt source with Camera by different mask)
|
||||
int isr_flags = 0;
|
||||
int isr_flags = ESP_INTR_FLAG_SHARED;
|
||||
ret = esp_intr_alloc_intrstatus(lcd_periph_signals.panels[panel_id].irq_id, isr_flags,
|
||||
(uint32_t)lcd_ll_get_interrupt_status_reg(rgb_panel->hal.dev),
|
||||
LCD_LL_EVENT_VSYNC_END, lcd_default_isr_handler, rgb_panel, &rgb_panel->intr);
|
||||
|
@ -257,18 +279,22 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
|
|||
y_end = MIN(y_end, rgb_panel->timings.v_res);
|
||||
xSemaphoreTake(rgb_panel->done_sem, portMAX_DELAY); // wait for last transaction done
|
||||
// convert the frame buffer to 3D array
|
||||
int bytes_pre_pixel = rgb_panel->data_width / 8;
|
||||
int pixels_pre_line = rgb_panel->timings.h_res;
|
||||
int bytes_per_pixel = rgb_panel->data_width / 8;
|
||||
int pixels_per_line = rgb_panel->timings.h_res;
|
||||
const uint8_t *from = (const uint8_t *)color_data;
|
||||
uint8_t (*to)[pixels_pre_line][bytes_pre_pixel] = (uint8_t (*)[pixels_pre_line][bytes_pre_pixel])rgb_panel->fb;
|
||||
uint8_t (*to)[pixels_per_line][bytes_per_pixel] = (uint8_t (*)[pixels_per_line][bytes_per_pixel])rgb_panel->fb;
|
||||
// manipulate the frame buffer
|
||||
for (int j = y_start; j < y_end; j++) {
|
||||
for (int i = x_start; i < x_end; i++) {
|
||||
for (int k = 0; k < bytes_pre_pixel; k++) {
|
||||
for (int k = 0; k < bytes_per_pixel; k++) {
|
||||
to[j][i][k] = *from++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (rgb_panel->flags.fb_in_psram) {
|
||||
// CPU writes data to PSRAM through DCache, data in PSRAM might not get updated, so write back
|
||||
Cache_WriteBack_Addr((uint32_t)&to[y_start][0][0], (y_end - y_start) * rgb_panel->timings.h_res * bytes_per_pixel);
|
||||
}
|
||||
// we don't care the exact frame ID, as long as it's different from the previous one
|
||||
rgb_panel->new_frame_id++;
|
||||
if (!rgb_panel->flags.stream_mode) {
|
||||
|
@ -400,6 +426,11 @@ static esp_err_t lcd_rgb_panel_create_trans_link(esp_rgb_panel_t *panel)
|
|||
ret = gdma_new_channel(&dma_chan_config, &panel->dma_chan);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "alloc DMA channel failed");
|
||||
gdma_connect(panel->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_LCD, 0));
|
||||
gdma_transfer_ability_t ability = {
|
||||
.psram_trans_align = 64,
|
||||
.sram_trans_align = 4,
|
||||
};
|
||||
gdma_set_transfer_ability(panel->dma_chan, &ability);
|
||||
// the start of DMA should be prior to the start of LCD engine
|
||||
gdma_start(panel->dma_chan, (intptr_t)panel->dma_nodes);
|
||||
|
||||
|
|
|
@ -8,33 +8,31 @@
|
|||
|
||||
#define TEST_LCD_H_RES (480)
|
||||
#define TEST_LCD_V_RES (272)
|
||||
#define TEST_LCD_VSYNC_GPIO (19)
|
||||
#define TEST_LCD_HSYNC_GPIO (18)
|
||||
#define TEST_LCD_VSYNC_GPIO (1)
|
||||
#define TEST_LCD_HSYNC_GPIO (2)
|
||||
#define TEST_LCD_DE_GPIO (-1)
|
||||
#define TEST_LCD_PCLK_GPIO (17)
|
||||
#define TEST_LCD_DATA0_GPIO (42) // B0
|
||||
#define TEST_LCD_DATA1_GPIO (41) // B1
|
||||
#define TEST_LCD_DATA2_GPIO (40) // B2
|
||||
#define TEST_LCD_DATA3_GPIO (39) // B3
|
||||
#define TEST_LCD_DATA4_GPIO (38) // B4
|
||||
#define TEST_LCD_DATA5_GPIO (4) // G0
|
||||
#define TEST_LCD_DATA6_GPIO (5) // G1
|
||||
#define TEST_LCD_DATA7_GPIO (6) // G2
|
||||
#define TEST_LCD_DATA8_GPIO (7) // G3
|
||||
#define TEST_LCD_DATA9_GPIO (15) // G4
|
||||
#define TEST_LCD_DATA10_GPIO (16) // G5
|
||||
#define TEST_LCD_DATA11_GPIO (37) // R0
|
||||
#define TEST_LCD_DATA12_GPIO (36) // R1
|
||||
#define TEST_LCD_DATA13_GPIO (35) // R2
|
||||
#define TEST_LCD_DATA14_GPIO (34) // R3
|
||||
#define TEST_LCD_DATA15_GPIO (33) // R4
|
||||
#define TEST_LCD_PCLK_GPIO (3)
|
||||
#define TEST_LCD_DATA0_GPIO (4) // B0
|
||||
#define TEST_LCD_DATA1_GPIO (5) // B1
|
||||
#define TEST_LCD_DATA2_GPIO (6) // B2
|
||||
#define TEST_LCD_DATA3_GPIO (7) // B3
|
||||
#define TEST_LCD_DATA4_GPIO (8) // B4
|
||||
#define TEST_LCD_DATA5_GPIO (9) // G0
|
||||
#define TEST_LCD_DATA6_GPIO (10) // G1
|
||||
#define TEST_LCD_DATA7_GPIO (11) // G2
|
||||
#define TEST_LCD_DATA8_GPIO (12) // G3
|
||||
#define TEST_LCD_DATA9_GPIO (13) // G4
|
||||
#define TEST_LCD_DATA10_GPIO (14) // G5
|
||||
#define TEST_LCD_DATA11_GPIO (15) // R0
|
||||
#define TEST_LCD_DATA12_GPIO (16) // R1
|
||||
#define TEST_LCD_DATA13_GPIO (17) // R2
|
||||
#define TEST_LCD_DATA14_GPIO (18) // R3
|
||||
#define TEST_LCD_DATA15_GPIO (19) // R4
|
||||
#define TEST_LCD_DISP_EN_GPIO (-1)
|
||||
|
||||
#if SOC_LCD_RGB_SUPPORTED
|
||||
|
||||
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
|
||||
/* Not enough memory for framebuffer when running in default_2 config TODO IDF-3565 */
|
||||
|
||||
// RGB driver consumes a huge memory to save frame buffer, only test it with PSRAM enabled
|
||||
#if CONFIG_SPIRAM_USE_MALLOC
|
||||
TEST_CASE("lcd rgb lcd panel", "[lcd]")
|
||||
{
|
||||
#define TEST_IMG_SIZE (100 * 100 * sizeof(uint16_t))
|
||||
|
@ -68,7 +66,7 @@ TEST_CASE("lcd rgb lcd panel", "[lcd]")
|
|||
TEST_LCD_DATA15_GPIO,
|
||||
},
|
||||
.timings = {
|
||||
.pclk_hz = 20000000,
|
||||
.pclk_hz = 6000000,
|
||||
.h_res = TEST_LCD_H_RES,
|
||||
.v_res = TEST_LCD_V_RES,
|
||||
.hsync_back_porch = 43,
|
||||
|
@ -78,6 +76,7 @@ TEST_CASE("lcd rgb lcd panel", "[lcd]")
|
|||
.vsync_front_porch = 1,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.flags.fb_in_psram = 1,
|
||||
};
|
||||
// Test stream mode and one-off mode
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
@ -145,7 +144,7 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]")
|
|||
TEST_LCD_DATA15_GPIO,
|
||||
},
|
||||
.timings = {
|
||||
.pclk_hz = 20000000,
|
||||
.pclk_hz = 6000000,
|
||||
.h_res = TEST_LCD_H_RES,
|
||||
.v_res = TEST_LCD_V_RES,
|
||||
.hsync_back_porch = 43,
|
||||
|
@ -155,6 +154,7 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]")
|
|||
.vsync_front_porch = 1,
|
||||
.vsync_pulse_width = 1,
|
||||
},
|
||||
.flags.fb_in_psram = 1,
|
||||
.on_frame_trans_done = notify_lvgl_ready_to_flush,
|
||||
.user_data = &disp,
|
||||
};
|
||||
|
@ -165,7 +165,5 @@ TEST_CASE("lvgl gui with rgb interface", "[lcd][lvgl][ignore]")
|
|||
test_lvgl_task_loop(panel_handle, TEST_LCD_H_RES, TEST_LCD_V_RES, &disp);
|
||||
}
|
||||
#endif // CONFIG_LV_USE_USER_DATA
|
||||
|
||||
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
|
||||
|
||||
#endif // CONFIG_SPIRAM_USE_MALLOC
|
||||
#endif // SOC_LCD_RGB_SUPPORTED
|
||||
|
|
Ładowanie…
Reference in New Issue