-
-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support for ESP BSP 3 / IDF 5 and double buffering #394
Comments
any sponsor is welcome ;) |
OK i sponsored! hopefully it will help :) |
Hello, I have the Adafruit Qualia ESP32-S3 for TTL RGB-666 Displays (https://www.adafruit.com/product/5800) with the Round RGB TTL TFT Display - 4" 720x720 - NV3052C (https://www.adafruit.com/product/5793) Hope this will help you. Here is my code: #include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "hal/gpio_types.h"
#include "rom/cache.h"
#include "esp_err.h"
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_rgb.h"
#include "freertos/semphr.h"
#include "esp_timer.h"
#include "MyWire.h"
#include "MyPCA9554.h"
#include "MyUtils.h"
#define PCA_TFT_BACKLIGHT 4
#define PCA_BUTTON_DOWN 6
#define PCA_BUTTON_UP 5
#define TFT_DE 2
#define TFT_VSYNC 42
#define TFT_HSYNC 41
#define TFT_PCLK 1
#define TFT_R1 11
#define TFT_R2 10
#define TFT_R3 9
#define TFT_R4 46
#define TFT_R5 3
#define TFT_G0 48
#define TFT_G1 47
#define TFT_G2 21
#define TFT_G3 14
#define TFT_G4 13
#define TFT_G5 12
#define TFT_B1 40
#define TFT_B2 39
#define TFT_B3 38
#define TFT_B4 0
#define TFT_B5 45
#define WIDTH 720
#define HEIGHT 720
#define RGB565(r, g, b) ((((r)&0xF8) << 8) | (((g)&0xFC) << 3) | ((b) >> 3))
#define SQUARE_COUNT 20
typedef struct {
int x;
int y;
int size;
int dx;
int dy;
uint16_t color;
} SQUARE;
void fill_rect(uint16_t *frame_buffer, int32_t x, int32_t y, int32_t w, int32_t h, uint16_t color) {
uint16_t *p = frame_buffer + WIDTH * y + x;
uint16_t *first_line = p;
uint32_t iw = w;
if (h--) {
while (iw--) *p++ = color;
}
p = first_line;
while (h--) {
p += WIDTH;
memcpy(p, first_line, w << 1);
}
}
int clamp(int v, int low, int high) {
if (v < low) {
v = low;
}
if (v >= high) {
v = high;
}
return v;
}
void init_square(SQUARE *p) {
p->size = 20 + (rand() % 30);
p->x = clamp(rand() % WIDTH, 0, WIDTH - p->size - 1);
p->y = clamp(rand() % HEIGHT, 0, HEIGHT - p->size - 1);
p->dx = (3 + (rand() % 6)) * ((rand() % 2) == 1 ? 1 : -1);
p->dy = (3 + (rand() % 6)) * ((rand() % 2) == 1 ? 1 : -1);
p->color = RGB565(rand() % 255, rand() % 255, rand() % 255);
}
void draw_square(uint16_t *frame_buffer, SQUARE *p) {
fill_rect(frame_buffer, p->x, p->y, p->size, p->size, p->color);
}
void update_square(SQUARE *p) {
p->x += p->dx;
if (p->x >= WIDTH - p->size - 1) {
p->x = WIDTH - p->size - 1;
p->dx = -p->dx;
} else if (p->x <= 0) {
p->x = 0;
p->dx = -p->dx;
}
p->y += p->dy;
if (p->y >= HEIGHT - p->size - 1) {
p->y = HEIGHT - p->size - 1;
p->dy = -p->dy;
} else if (p->y <= 0) {
p->y = 0;
p->dy = -p->dy;
}
}
static const uint8_t hd40015c40_init_operations[] = {
BEGIN_WRITE,
WRITE_C8_D8, 0xFF, 0x30,
WRITE_C8_D8, 0xFF, 0x52,
WRITE_C8_D8, 0xFF, 0x01,
WRITE_C8_D8, 0xE3, 0x00,
WRITE_C8_D8, 0x0A, 0x11,
WRITE_C8_D8, 0x23, 0xA0,
WRITE_C8_D8, 0x24, 0x32,
WRITE_C8_D8, 0x25, 0x12,
WRITE_C8_D8, 0x26, 0x2E,
WRITE_C8_D8, 0x27, 0x2E,
WRITE_C8_D8, 0x29, 0x02,
WRITE_C8_D8, 0x2A, 0xCF,
WRITE_C8_D8, 0x32, 0x34,
WRITE_C8_D8, 0x38, 0x9C,
WRITE_C8_D8, 0x39, 0xA7,
WRITE_C8_D8, 0x3A, 0x27,
WRITE_C8_D8, 0x3B, 0x94,
WRITE_C8_D8, 0x42, 0x6D,
WRITE_C8_D8, 0x43, 0x83,
WRITE_C8_D8, 0x81, 0x00,
WRITE_C8_D8, 0x91, 0x67,
WRITE_C8_D8, 0x92, 0x67,
WRITE_C8_D8, 0xA0, 0x52,
WRITE_C8_D8, 0xA1, 0x50,
WRITE_C8_D8, 0xA4, 0x9C,
WRITE_C8_D8, 0xA7, 0x02,
WRITE_C8_D8, 0xA8, 0x02,
WRITE_C8_D8, 0xA9, 0x02,
WRITE_C8_D8, 0xAA, 0xA8,
WRITE_C8_D8, 0xAB, 0x28,
WRITE_C8_D8, 0xAE, 0xD2,
WRITE_C8_D8, 0xAF, 0x02,
WRITE_C8_D8, 0xB0, 0xD2,
WRITE_C8_D8, 0xB2, 0x26,
WRITE_C8_D8, 0xB3, 0x26,
WRITE_C8_D8, 0xFF, 0x30,
WRITE_C8_D8, 0xFF, 0x52,
WRITE_C8_D8, 0xFF, 0x02,
WRITE_C8_D8, 0xB1, 0x0A,
WRITE_C8_D8, 0xD1, 0x0E,
WRITE_C8_D8, 0xB4, 0x2F,
WRITE_C8_D8, 0xD4, 0x2D,
WRITE_C8_D8, 0xB2, 0x0C,
WRITE_C8_D8, 0xD2, 0x0C,
WRITE_C8_D8, 0xB3, 0x30,
WRITE_C8_D8, 0xD3, 0x2A,
WRITE_C8_D8, 0xB6, 0x1E,
WRITE_C8_D8, 0xD6, 0x16,
WRITE_C8_D8, 0xB7, 0x3B,
WRITE_C8_D8, 0xD7, 0x35,
WRITE_C8_D8, 0xC1, 0x08,
WRITE_C8_D8, 0xE1, 0x08,
WRITE_C8_D8, 0xB8, 0x0D,
WRITE_C8_D8, 0xD8, 0x0D,
WRITE_C8_D8, 0xB9, 0x05,
WRITE_C8_D8, 0xD9, 0x05,
WRITE_C8_D8, 0xBD, 0x15,
WRITE_C8_D8, 0xDD, 0x15,
WRITE_C8_D8, 0xBC, 0x13,
WRITE_C8_D8, 0xDC, 0x13,
WRITE_C8_D8, 0xBB, 0x12,
WRITE_C8_D8, 0xDB, 0x10,
WRITE_C8_D8, 0xBA, 0x11,
WRITE_C8_D8, 0xDA, 0x11,
WRITE_C8_D8, 0xBE, 0x17,
WRITE_C8_D8, 0xDE, 0x17,
WRITE_C8_D8, 0xBF, 0x0F,
WRITE_C8_D8, 0xDF, 0x0F,
WRITE_C8_D8, 0xC0, 0x16,
WRITE_C8_D8, 0xE0, 0x16,
WRITE_C8_D8, 0xB5, 0x2E,
WRITE_C8_D8, 0xD5, 0x3F,
WRITE_C8_D8, 0xB0, 0x03,
WRITE_C8_D8, 0xD0, 0x02,
WRITE_C8_D8, 0xFF, 0x30,
WRITE_C8_D8, 0xFF, 0x52,
WRITE_C8_D8, 0xFF, 0x03,
WRITE_C8_D8, 0x08, 0x09,
WRITE_C8_D8, 0x09, 0x0A,
WRITE_C8_D8, 0x0A, 0x0B,
WRITE_C8_D8, 0x0B, 0x0C,
WRITE_C8_D8, 0x28, 0x22,
WRITE_C8_D8, 0x2A, 0xE9,
WRITE_C8_D8, 0x2B, 0xE9,
WRITE_C8_D8, 0x34, 0x51,
WRITE_C8_D8, 0x35, 0x01,
WRITE_C8_D8, 0x36, 0x26,
WRITE_C8_D8, 0x37, 0x13,
WRITE_C8_D8, 0x40, 0x07,
WRITE_C8_D8, 0x41, 0x08,
WRITE_C8_D8, 0x42, 0x09,
WRITE_C8_D8, 0x43, 0x0A,
WRITE_C8_D8, 0x44, 0x22,
WRITE_C8_D8, 0x45, 0xDB,
WRITE_C8_D8, 0x46, 0xdC,
WRITE_C8_D8, 0x47, 0x22,
WRITE_C8_D8, 0x48, 0xDD,
WRITE_C8_D8, 0x49, 0xDE,
WRITE_C8_D8, 0x50, 0x0B,
WRITE_C8_D8, 0x51, 0x0C,
WRITE_C8_D8, 0x52, 0x0D,
WRITE_C8_D8, 0x53, 0x0E,
WRITE_C8_D8, 0x54, 0x22,
WRITE_C8_D8, 0x55, 0xDF,
WRITE_C8_D8, 0x56, 0xE0,
WRITE_C8_D8, 0x57, 0x22,
WRITE_C8_D8, 0x58, 0xE1,
WRITE_C8_D8, 0x59, 0xE2,
WRITE_C8_D8, 0x80, 0x1E,
WRITE_C8_D8, 0x81, 0x1E,
WRITE_C8_D8, 0x82, 0x1F,
WRITE_C8_D8, 0x83, 0x1F,
WRITE_C8_D8, 0x84, 0x05,
WRITE_C8_D8, 0x85, 0x0A,
WRITE_C8_D8, 0x86, 0x0A,
WRITE_C8_D8, 0x87, 0x0C,
WRITE_C8_D8, 0x88, 0x0C,
WRITE_C8_D8, 0x89, 0x0E,
WRITE_C8_D8, 0x8A, 0x0E,
WRITE_C8_D8, 0x8B, 0x10,
WRITE_C8_D8, 0x8C, 0x10,
WRITE_C8_D8, 0x8D, 0x00,
WRITE_C8_D8, 0x8E, 0x00,
WRITE_C8_D8, 0x8F, 0x1F,
WRITE_C8_D8, 0x90, 0x1F,
WRITE_C8_D8, 0x91, 0x1E,
WRITE_C8_D8, 0x92, 0x1E,
WRITE_C8_D8, 0x93, 0x02,
WRITE_C8_D8, 0x94, 0x04,
WRITE_C8_D8, 0x96, 0x1E,
WRITE_C8_D8, 0x97, 0x1E,
WRITE_C8_D8, 0x98, 0x1F,
WRITE_C8_D8, 0x99, 0x1F,
WRITE_C8_D8, 0x9A, 0x05,
WRITE_C8_D8, 0x9B, 0x09,
WRITE_C8_D8, 0x9C, 0x09,
WRITE_C8_D8, 0x9D, 0x0B,
WRITE_C8_D8, 0x9E, 0x0B,
WRITE_C8_D8, 0x9F, 0x0D,
WRITE_C8_D8, 0xA0, 0x0D,
WRITE_C8_D8, 0xA1, 0x0F,
WRITE_C8_D8, 0xA2, 0x0F,
WRITE_C8_D8, 0xA3, 0x00,
WRITE_C8_D8, 0xA4, 0x00,
WRITE_C8_D8, 0xA5, 0x1F,
WRITE_C8_D8, 0xA6, 0x1F,
WRITE_C8_D8, 0xA7, 0x1E,
WRITE_C8_D8, 0xA8, 0x1E,
WRITE_C8_D8, 0xA9, 0x01,
WRITE_C8_D8, 0xAA, 0x03,
WRITE_C8_D8, 0xFF, 0x30,
WRITE_C8_D8, 0xFF, 0x52,
WRITE_C8_D8, 0xFF, 0x00,
WRITE_C8_D8, 0x36, 0x0A,
WRITE_COMMAND_8, 0x11, // Sleep Out
END_WRITE,
DELAY, 100,
BEGIN_WRITE,
WRITE_COMMAND_8, 0x29, // Display On
END_WRITE,
DELAY, 50
};
// Semaphore used to synchronize frame buffers swap
SemaphoreHandle_t sem_vsync_end;
SemaphoreHandle_t sem_frame_buffer_ready;
uint16_t *frame_buffers[2];
int current_frame_buffer_idx = 0;
bool on_vsync(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *data, void *user_ctx) {
BaseType_t high_task_awoken0 = pdFALSE;
xSemaphoreGiveFromISR(sem_vsync_end, &high_task_awoken0);
BaseType_t high_task_awoken1 = pdFALSE;
xSemaphoreTakeFromISR(sem_frame_buffer_ready, &high_task_awoken1);
return (high_task_awoken0 == pdTRUE) || (high_task_awoken1 == pdTRUE);
}
void wait_vsync_and_show_frame_buffer(esp_lcd_panel_handle_t panel, uint16_t *frame_buffer) {
xSemaphoreTake(sem_vsync_end, portMAX_DELAY);
// If the last esp_lcd_panel_draw_bitmap arg is a frame buffer allocated in PSRAM,
// then esp_lcd_panel_draw_bitmap does not make a copy but switches to this frame buffer
esp_lcd_panel_draw_bitmap(panel, 0, 0, WIDTH, HEIGHT, frame_buffer);
xSemaphoreGive(sem_frame_buffer_ready);
}
void create_lcd_panel(esp_lcd_panel_handle_t *panel_handle) {
esp_lcd_rgb_panel_config_t panel_config;
memset(&panel_config, 0, sizeof(esp_lcd_rgb_panel_config_t));
panel_config.data_width = 16; // RGB565 in parallel mode, thus 16bit in width
panel_config.psram_trans_align = 64;
panel_config.clk_src = LCD_CLK_SRC_PLL160M;
panel_config.disp_gpio_num = GPIO_NUM_NC;
panel_config.pclk_gpio_num = TFT_PCLK;
panel_config.vsync_gpio_num = TFT_VSYNC;
panel_config.hsync_gpio_num = TFT_HSYNC;
panel_config.de_gpio_num = TFT_DE;
panel_config.data_gpio_nums[0] = TFT_B1;
panel_config.data_gpio_nums[1] = TFT_B2;
panel_config.data_gpio_nums[2] = TFT_B3;
panel_config.data_gpio_nums[3] = TFT_B4;
panel_config.data_gpio_nums[4] = TFT_B5;
panel_config.data_gpio_nums[5] = TFT_G0;
panel_config.data_gpio_nums[6] = TFT_G1;
panel_config.data_gpio_nums[7] = TFT_G2;
panel_config.data_gpio_nums[8] = TFT_G3;
panel_config.data_gpio_nums[9] = TFT_G4;
panel_config.data_gpio_nums[10] = TFT_G5;
panel_config.data_gpio_nums[11] = TFT_R1;
panel_config.data_gpio_nums[12] = TFT_R2;
panel_config.data_gpio_nums[13] = TFT_R3;
panel_config.data_gpio_nums[14] = TFT_R4;
panel_config.data_gpio_nums[15] = TFT_R5;
panel_config.timings.pclk_hz = 16000000;
panel_config.timings.h_res = WIDTH;
panel_config.timings.v_res = HEIGHT;
panel_config.timings.hsync_back_porch = 44;
panel_config.timings.hsync_front_porch = 46;
panel_config.timings.hsync_pulse_width = 2;
panel_config.timings.vsync_back_porch = 16;
panel_config.timings.vsync_front_porch = 50;
panel_config.timings.vsync_pulse_width = 16;
panel_config.timings.flags.pclk_active_neg = 0;
panel_config.timings.flags.vsync_idle_low = 1;
// Request 2 frame buffers in PSRAM
panel_config.num_fbs = 2;
panel_config.flags.fb_in_psram = true;
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, panel_handle));
esp_lcd_rgb_panel_event_callbacks_t callbacks;
memset(&callbacks, 0, sizeof(esp_lcd_rgb_panel_event_callbacks_t));
callbacks.on_vsync = on_vsync;
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(*panel_handle, &callbacks, NULL));
ESP_ERROR_CHECK(esp_lcd_panel_reset(*panel_handle));
ESP_ERROR_CHECK(esp_lcd_panel_init(*panel_handle));
// Retrieve allocated frame buffers
ESP_ERROR_CHECK(esp_lcd_rgb_panel_get_frame_buffer(*panel_handle, 2, (void **)&frame_buffers[0], (void **)&frame_buffers[1]));
}
void app_main(void) {
printf("Hello world!\n");
sem_vsync_end = xSemaphoreCreateBinary();
assert(sem_vsync_end);
sem_frame_buffer_ready = xSemaphoreCreateBinary();
assert(sem_frame_buffer_ready);
my_wire_create();
MY_PCA9554_HANDLE handle;
my_PCA9554_create(&handle);
my_PCA9554_send_command(&handle, 0x01);
my_delay(120);
my_PCA9554_batch(&handle, hd40015c40_init_operations, sizeof(hd40015c40_init_operations));
esp_lcd_panel_handle_t panel_handle = NULL;
create_lcd_panel(&panel_handle);
my_PCA9554_pin_mode(&handle, PCA_TFT_BACKLIGHT, OUTPUT);
my_PCA9554_digital_write(&handle, PCA_TFT_BACKLIGHT, HIGH);
SQUARE squares[SQUARE_COUNT];
for (int i = 0; i < SQUARE_COUNT; i++) {
init_square(&squares[i]);
}
int fps_counter = 0;
unsigned long fps_time = my_millis();
while (true) {
uint16_t *frame_buffer;
frame_buffer = frame_buffers[current_frame_buffer_idx];
// Clear frame buffer and draw squares
fill_rect(frame_buffer, 0, 0, WIDTH, HEIGHT, RGB565(0, 0, 255));
for (int i = 0; i < SQUARE_COUNT; i++) {
draw_square(frame_buffer, &squares[i]);
}
wait_vsync_and_show_frame_buffer(panel_handle, frame_buffer);
current_frame_buffer_idx = 1 - current_frame_buffer_idx;
// Update squares positions
for (int i = 0; i < SQUARE_COUNT; i++) {
update_square(&squares[i]);
}
// Update FPS counter and print FPS
fps_counter++;
unsigned long t = my_millis();
if (t - fps_time >= 1000) {
printf("FPS: %d\n", fps_counter);
fps_counter = 0;
fps_time = t;
}
}
my_PCA9554_delete(&handle);
my_wire_delete();
} |
Just trying 3.0.0-ALPHA3, seems this version not yet enable SOC_LCD_RGB_SUPPORTED. Still a long way to go. |
I wish you could upload it to us the way we can test the code in arduino. Can you give us a little bit information how to compile it and test it on our dafruit Qualia ESP32-S3 boards? |
@moononournation I have a Makerfabs 4.0" 480x480 ESP32S3 based board that suffers badly from screen tearing when using LVGL. When the picture is still, I get about 33 FPS, but when large portions of the screen are changing, it drops down to about 4 FPS, and it tears really bad. Will the double buffer work with this screen as well (RGB565+SPI). If yes, that would be a game changer for all the Makerfabs ESP32-based touch screens. |
The problem is that this code can be compiled only using ESP IDF 5.X. The arduino environment is currently using ESP IDF 4.X. |
yes, still need to wait next dev version. |
How about PlatformIO? That's what I'm currently using. I'm using the Arduino-ESP32 framework, but as far as I know, it uses IDF 5.x. |
try it |
They have already released development versions of their next arduino-esp32 package: It should be possible to use with Arduino IDE if one install the development version: |
hi, I've get rid of Arduino IDE and installed VSCode yesterday night. I can build and upload ESP32-S3 programs to it and it runs. Only Problem is we don't have #include "MyWire.h" those files and related to them. So I could not compile and test the code. Arduino IDE is irrelevant to me to run the LCD now. If those files are in a library can you share the name of them so I could download and check them ? |
It's a very basic reimplementation of minimum required functions to use the Qualia 666 Unzip this in the lib directory of your platformio project. |
Hello, It's me again. Happy new year :D I'm sorry to bother you. I could not have time to test it. But today I've done it. with only one error /esp/esp-idf/components/freertos/app_startup.c:206: undefined reference to `app_main' I think this is a major error, because there seems no solution for it. Could you help me a little what did I do wrong ? EDIT: I've found the problem. I alwasy pressed "ESP-IDF build" button. There is another button called "PlatformIO Build". And I could build it and upload it to RGB666 board. But There are some warnings like :
|
I have this message from Serial Port of Qualia S3 RGB666 board. Did you ever get such an error ?? Would you please help me ? "lcd_rgb_panel_alloc_frame_buffers(156): no mem for frame buffer" Rebooting... my_wire_create Core 0 register dump: Backtrace: 0x420034be:0x3fc9a560 0x42003ac7:0x3fc9a580 0x4200187b:0x3fc9a5c0 0x42001997:0x3fc9a690 0x4037de10:0x3fc9a8a0 0x4037da09:0x3fc9a8d0 ELF file SHA256: 1ab96ed1b3ec3a5e |
Hello I tested your program on platformio with IDF 5.2.1 and it works. your LCD setup is spot on and the timing issue is not that but your updating the buffers too fast and its causing what looks to be vertical row. if you add vTaskDelay and play with delay it works. also found that the log command is also slowing it down to show frame buffers. its not 100% but it works. To emre2blue you do not have PSram turned on so its not seeing the Psram flash. have to configure that in menuconfig fill_rect(frame_buffer, 0, 0, WIDTH, HEIGHT, RGB565(0, 0, 255));
|
Arduino-ESP32 3.0 now supports IDF5. eagerly hope that gfx will support it soon. just sponsored, thank you :) |
hiya @moononournation - we were looking to see if this library could support the latest ESP board support package (right now its 3.0.0 alpha) since there's many changes in IDF5. also we think that it would be great to enable double-buffer support for the RGB TTL interface to avoid the tearing effect
https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-reference/peripherals/lcd.html#double-frame-buffer-in-psram
i don't have time to submit a PR...can we sponsor you to add one or both? if so please email limor@adafruit.com !
thanks :)
The text was updated successfully, but these errors were encountered: