mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/rgb_lcd_rotation' into 'master'
rgb_lcd: support mirror and swap axis See merge request espressif/esp-idf!19059
This commit is contained in:
commit
20e253e663
@ -31,6 +31,7 @@
|
||||
#include "hal/gpio_hal.h"
|
||||
#include "esp_private/gdma.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_bit_defs.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
#if CONFIG_SPIRAM
|
||||
#include "esp_psram.h"
|
||||
@ -48,6 +49,16 @@
|
||||
#define LCD_RGB_INTR_ALLOC_FLAGS ESP_INTR_FLAG_INTRDISABLED
|
||||
#endif
|
||||
|
||||
#define RGB_PANEL_SWAP_XY 0
|
||||
#define RGB_PANEL_MIRROR_Y 1
|
||||
#define RGB_PANEL_MIRROR_X 2
|
||||
|
||||
typedef enum {
|
||||
ROTATE_MASK_SWAP_XY = BIT(RGB_PANEL_SWAP_XY),
|
||||
ROTATE_MASK_MIRROR_Y = BIT(RGB_PANEL_MIRROR_Y),
|
||||
ROTATE_MASK_MIRROR_X = BIT(RGB_PANEL_MIRROR_X),
|
||||
} panel_rotate_mask_t;
|
||||
|
||||
static const char *TAG = "lcd_panel.rgb";
|
||||
|
||||
typedef struct esp_rgb_panel_t esp_rgb_panel_t;
|
||||
@ -96,6 +107,7 @@ struct esp_rgb_panel_t {
|
||||
int y_gap; // Extra gap in y coordinate, it's used when calculate the flush window
|
||||
portMUX_TYPE spinlock; // to protect panel specific resource from concurrent access (e.g. between task and ISR)
|
||||
int lcd_clk_flags; // LCD clock calculation flags
|
||||
int rotate_mask; // panel rotate_mask mask, Or'ed of `panel_rotate_mask_t`
|
||||
struct {
|
||||
uint32_t disp_en_level: 1; // The level which can turn on the screen by `disp_gpio_num`
|
||||
uint32_t stream_mode: 1; // If set, the LCD transfers data continuously, otherwise, it stops refreshing the LCD when transaction done
|
||||
@ -436,6 +448,21 @@ static esp_err_t rgb_panel_init(esp_lcd_panel_t *panel)
|
||||
return ret;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void copy_pixel_16bpp(uint8_t *to, const uint8_t *from)
|
||||
{
|
||||
*to++ = *from++;
|
||||
*to++ = *from++;
|
||||
}
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline void copy_pixel_24bpp(uint8_t *to, const uint8_t *from)
|
||||
{
|
||||
*to++ = *from++;
|
||||
*to++ = *from++;
|
||||
*to++ = *from++;
|
||||
}
|
||||
|
||||
static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int y_start, int x_end, int y_end, const void *color_data)
|
||||
{
|
||||
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||
@ -459,33 +486,231 @@ static esp_err_t rgb_panel_draw_bitmap(esp_lcd_panel_t *panel, int x_start, int
|
||||
x_end += rgb_panel->x_gap;
|
||||
y_end += rgb_panel->y_gap;
|
||||
// round the boundary
|
||||
x_start = MIN(x_start, rgb_panel->timings.h_res);
|
||||
x_end = MIN(x_end, rgb_panel->timings.h_res);
|
||||
y_start = MIN(y_start, rgb_panel->timings.v_res);
|
||||
y_end = MIN(y_end, rgb_panel->timings.v_res);
|
||||
int h_res = rgb_panel->timings.h_res;
|
||||
int v_res = rgb_panel->timings.v_res;
|
||||
if (rgb_panel->rotate_mask & ROTATE_MASK_SWAP_XY) {
|
||||
x_start = MIN(x_start, v_res);
|
||||
x_end = MIN(x_end, v_res);
|
||||
y_start = MIN(y_start, h_res);
|
||||
y_end = MIN(y_end, h_res);
|
||||
} else {
|
||||
x_start = MIN(x_start, h_res);
|
||||
x_end = MIN(x_end, h_res);
|
||||
y_start = MIN(y_start, v_res);
|
||||
y_end = MIN(y_end, v_res);
|
||||
}
|
||||
|
||||
int bytes_per_pixel = rgb_panel->bits_per_pixel / 8;
|
||||
int pixels_per_line = rgb_panel->timings.h_res;
|
||||
uint32_t bytes_per_line = bytes_per_pixel * pixels_per_line;
|
||||
uint8_t *fb = rgb_panel->fbs[rgb_panel->cur_fb_index];
|
||||
uint32_t bytes_to_flush = v_res * h_res * bytes_per_pixel;
|
||||
uint8_t *flush_ptr = fb;
|
||||
|
||||
if (do_copy) {
|
||||
// copy the UI draw buffer into internal frame buffer
|
||||
const uint8_t *from = (const uint8_t *)color_data;
|
||||
uint32_t copy_bytes_per_line = (x_end - x_start) * bytes_per_pixel;
|
||||
uint8_t *to = fb + (y_start * pixels_per_line + x_start) * bytes_per_pixel;
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
memcpy(to, from, copy_bytes_per_line);
|
||||
to += bytes_per_line;
|
||||
from += copy_bytes_per_line;
|
||||
size_t offset = y_start * copy_bytes_per_line + x_start * bytes_per_pixel;
|
||||
uint8_t *to = fb;
|
||||
if (2 == bytes_per_pixel) {
|
||||
switch (rgb_panel->rotate_mask) {
|
||||
case 0: {
|
||||
uint8_t *to = fb + (y_start * h_res + x_start) * bytes_per_pixel;
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
memcpy(to, from, copy_bytes_per_line);
|
||||
to += bytes_per_line;
|
||||
from += copy_bytes_per_line;
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + y_start * bytes_per_line;
|
||||
}
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_X:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
uint32_t index = (y * h_res + (h_res - 1 - x_start)) * bytes_per_pixel;
|
||||
for (size_t x = x_start; x < x_end; x++) {
|
||||
copy_pixel_16bpp(to + index, from);
|
||||
index -= bytes_per_pixel;
|
||||
from += bytes_per_pixel;
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + y_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_Y: {
|
||||
uint8_t *to = fb + ((v_res - 1 - y_start) * h_res + x_start) * bytes_per_pixel;
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
memcpy(to, from, copy_bytes_per_line);
|
||||
to -= bytes_per_line;
|
||||
from += copy_bytes_per_line;
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - y_end) * bytes_per_line;
|
||||
}
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
uint32_t index = ((v_res - 1 - y) * h_res + (h_res - 1 - x_start)) * bytes_per_pixel;
|
||||
for (size_t x = x_start; x < x_end; x++) {
|
||||
copy_pixel_16bpp(to + index, from);
|
||||
index -= bytes_per_pixel;
|
||||
from += bytes_per_pixel;
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - y_end) * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = (x * h_res + y) * bytes_per_pixel;
|
||||
copy_pixel_16bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + x_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = (x * h_res + h_res - 1 - y) * bytes_per_pixel;
|
||||
copy_pixel_16bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + x_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = ((v_res - 1 - x) * h_res + y) * bytes_per_pixel;
|
||||
copy_pixel_16bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - x_end) * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = ((v_res - 1 - x) * h_res + h_res - 1 - y) * bytes_per_pixel;
|
||||
copy_pixel_16bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - x_end) * bytes_per_line;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (3 == bytes_per_pixel) {
|
||||
switch (rgb_panel->rotate_mask) {
|
||||
case 0: {
|
||||
uint8_t *to = fb + (y_start * h_res + x_start) * bytes_per_pixel;
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
memcpy(to, from, copy_bytes_per_line);
|
||||
to += bytes_per_line;
|
||||
from += copy_bytes_per_line;
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + y_start * bytes_per_line;
|
||||
}
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_X:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
uint32_t index = (y * h_res + (h_res - 1 - x_start)) * bytes_per_pixel;
|
||||
for (size_t x = x_start; x < x_end; x++) {
|
||||
copy_pixel_24bpp(to + index, from);
|
||||
index -= bytes_per_pixel;
|
||||
from += bytes_per_pixel;
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + y_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_Y: {
|
||||
uint8_t *to = fb + ((v_res - 1 - y_start) * h_res + x_start) * bytes_per_pixel;
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
memcpy(to, from, copy_bytes_per_line);
|
||||
to -= bytes_per_line;
|
||||
from += copy_bytes_per_line;
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - y_end) * bytes_per_line;
|
||||
}
|
||||
break;
|
||||
case ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
uint32_t index = ((v_res - 1 - y) * h_res + (h_res - 1 - x_start)) * bytes_per_pixel;
|
||||
for (size_t x = x_start; x < x_end; x++) {
|
||||
copy_pixel_24bpp(to + index, from);
|
||||
index -= bytes_per_pixel;
|
||||
from += bytes_per_pixel;
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - y_end) * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = (x * h_res + y) * bytes_per_pixel;
|
||||
copy_pixel_24bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + x_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = (x * h_res + h_res - 1 - y) * bytes_per_pixel;
|
||||
copy_pixel_24bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + x_start * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = ((v_res - 1 - x) * h_res + y) * bytes_per_pixel;
|
||||
copy_pixel_24bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - x_end) * bytes_per_line;
|
||||
break;
|
||||
case ROTATE_MASK_SWAP_XY | ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y:
|
||||
for (int y = y_start; y < y_end; y++) {
|
||||
for (int x = x_start; x < x_end; x++) {
|
||||
uint32_t j = y * copy_bytes_per_line + x * bytes_per_pixel - offset;
|
||||
uint32_t i = ((v_res - 1 - x) * h_res + h_res - 1 - y) * bytes_per_pixel;
|
||||
copy_pixel_24bpp(to + i, from + j);
|
||||
}
|
||||
}
|
||||
bytes_to_flush = (x_end - x_start) * bytes_per_line;
|
||||
flush_ptr = fb + (v_res - x_end) * bytes_per_line;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (rgb_panel->flags.fb_in_psram && !rgb_panel->bb_size) {
|
||||
// CPU writes data to PSRAM through DCache, data in PSRAM might not get updated, so write back
|
||||
// Note that if we use a bounce buffer, the data gets read by the CPU as well so no need to write back
|
||||
uint32_t bytes_to_flush = (y_end - y_start) * bytes_per_line;
|
||||
Cache_WriteBack_Addr((uint32_t)(fb + y_start * bytes_per_line), bytes_to_flush);
|
||||
Cache_WriteBack_Addr((uint32_t)(flush_ptr), bytes_to_flush);
|
||||
}
|
||||
|
||||
if (!rgb_panel->bb_size) {
|
||||
@ -513,12 +738,18 @@ static esp_err_t rgb_panel_invert_color(esp_lcd_panel_t *panel, bool invert_colo
|
||||
|
||||
static esp_err_t rgb_panel_mirror(esp_lcd_panel_t *panel, bool mirror_x, bool mirror_y)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||
rgb_panel->rotate_mask &= ~(ROTATE_MASK_MIRROR_X | ROTATE_MASK_MIRROR_Y);
|
||||
rgb_panel->rotate_mask |= (mirror_x << RGB_PANEL_MIRROR_X | mirror_y << RGB_PANEL_MIRROR_Y);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rgb_panel_swap_xy(esp_lcd_panel_t *panel, bool swap_axes)
|
||||
{
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
esp_rgb_panel_t *rgb_panel = __containerof(panel, esp_rgb_panel_t, base);
|
||||
rgb_panel->rotate_mask &= ~(ROTATE_MASK_SWAP_XY);
|
||||
rgb_panel->rotate_mask |= swap_axes << RGB_PANEL_SWAP_XY;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t rgb_panel_set_gap(esp_lcd_panel_t *panel, int x_gap, int y_gap)
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "esp_lcd_panel_rgb.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_random.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_attr.h"
|
||||
#include "spi_flash_mmap.h"
|
||||
#include "test_rgb_board.h"
|
||||
@ -212,6 +213,36 @@ TEST_CASE("lcd_rgb_panel_update_pclk", "[lcd]")
|
||||
free(img);
|
||||
}
|
||||
|
||||
TEST_CASE("lcd_rgb_panel_rotate", "[lcd]")
|
||||
{
|
||||
const int w = 200;
|
||||
const int h = 100;
|
||||
uint64_t t = 0;
|
||||
uint8_t *img = malloc(w * h * sizeof(uint16_t));
|
||||
TEST_ASSERT_NOT_NULL(img);
|
||||
uint8_t color_byte = esp_random() & 0xFF;
|
||||
memset(img, color_byte, w * h * sizeof(uint16_t));
|
||||
|
||||
printf("initialize RGB panel with stream mode\r\n");
|
||||
esp_lcd_panel_handle_t panel_handle = test_rgb_panel_initialization(16, 16, 0, false, NULL, NULL);
|
||||
|
||||
printf("Update the rotation of panel\r\n");
|
||||
for (size_t i = 0; i < 8; i++) {
|
||||
esp_lcd_panel_swap_xy(panel_handle, i & 4);
|
||||
esp_lcd_panel_mirror(panel_handle, i & 2, i & 1);
|
||||
printf("Panel Rotation=%d\r\n", i);
|
||||
t = esp_timer_get_time();
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, 0, 0, w, h, img);
|
||||
t = esp_timer_get_time() - t;
|
||||
printf("@resolution %dx%d time per frame=%.2fMS\r\n", w, h, (float)t / 1000.0f);
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
|
||||
printf("delete RGB panel\r\n");
|
||||
TEST_ESP_OK(esp_lcd_panel_del(panel_handle));
|
||||
free(img);
|
||||
}
|
||||
|
||||
#if CONFIG_LCD_RGB_ISR_IRAM_SAFE
|
||||
TEST_LCD_CALLBACK_ATTR static bool test_rgb_panel_count_in_callback(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user