318 lines
9.9 KiB
C
318 lines
9.9 KiB
C
#include "platform.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "driver/gpio.h"
|
|
#include "driver/spi_master.h"
|
|
#include "esp_check.h"
|
|
#include "esp_heap_caps.h"
|
|
#include "esp_lcd_panel_io.h"
|
|
#include "esp_lcd_panel_ops.h"
|
|
#include "esp_lcd_panel_vendor.h"
|
|
#include "esp_log.h"
|
|
#include "sdkconfig.h"
|
|
|
|
static const char *TAG = "display_st7789";
|
|
|
|
#define ST7789_SPI_HOST SPI2_HOST
|
|
#define ST7789_COLOR_BYTES_PER_PIXEL 2
|
|
|
|
#ifndef CONFIG_TQ_SCREEN_SPI_MODE
|
|
#define CONFIG_TQ_SCREEN_SPI_MODE 0
|
|
#endif
|
|
|
|
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
|
#define CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH 1
|
|
#endif
|
|
|
|
#if CONFIG_TQ_SCREEN_BACKLIGHT_ACTIVE_HIGH
|
|
#define ST7789_BACKLIGHT_ON_LEVEL 1
|
|
#define ST7789_BACKLIGHT_OFF_LEVEL 0
|
|
#else
|
|
#define ST7789_BACKLIGHT_ON_LEVEL 0
|
|
#define ST7789_BACKLIGHT_OFF_LEVEL 1
|
|
#endif
|
|
|
|
typedef struct {
|
|
esp_lcd_panel_io_handle_t io_handle;
|
|
esp_lcd_panel_handle_t panel_handle;
|
|
bool bus_owned;
|
|
bool initialized;
|
|
} st7789_display_state_t;
|
|
|
|
static st7789_display_state_t s_display;
|
|
|
|
#if CONFIG_TQ_SCREEN_COLOR_ORDER_BGR
|
|
#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_BGR
|
|
#else
|
|
#define ST7789_COLOR_ORDER LCD_RGB_ELEMENT_ORDER_RGB
|
|
#endif
|
|
|
|
#if CONFIG_TQ_SCREEN_SWAP_XY
|
|
#define ST7789_SWAP_XY true
|
|
#else
|
|
#define ST7789_SWAP_XY false
|
|
#endif
|
|
|
|
#if CONFIG_TQ_SCREEN_MIRROR_X
|
|
#define ST7789_MIRROR_X true
|
|
#else
|
|
#define ST7789_MIRROR_X false
|
|
#endif
|
|
|
|
#if CONFIG_TQ_SCREEN_MIRROR_Y
|
|
#define ST7789_MIRROR_Y true
|
|
#else
|
|
#define ST7789_MIRROR_Y false
|
|
#endif
|
|
|
|
static uint16_t rgb565(uint8_t r, uint8_t g, uint8_t b) {
|
|
return (uint16_t)(((uint16_t)(r & 0xF8) << 8) |
|
|
((uint16_t)(g & 0xFC) << 3) |
|
|
((uint16_t)(b & 0xF8) >> 3));
|
|
}
|
|
|
|
static uint16_t test_pattern_color(int x, int y, int h_res, int v_res) {
|
|
int top = v_res / 3;
|
|
int middle = (v_res * 2) / 3;
|
|
|
|
if (y < top) {
|
|
uint8_t r = (uint8_t)((x * 255) / (h_res - 1));
|
|
uint8_t g = (uint8_t)(255 - r);
|
|
return rgb565(r, g, 48);
|
|
}
|
|
|
|
if (y < middle) {
|
|
uint8_t gray = (uint8_t)(((y - top) * 255) / ((middle - top) > 1 ? (middle - top - 1) : 1));
|
|
return rgb565(gray, gray, gray);
|
|
}
|
|
|
|
bool checker = (((x / 16) + (y / 16)) & 1) != 0;
|
|
return checker ? rgb565(250, 250, 250) : rgb565(8, 8, 8);
|
|
}
|
|
|
|
static void fill_test_pattern_chunk(uint16_t *buf,
|
|
int y_start,
|
|
int lines,
|
|
int h_res,
|
|
int v_res) {
|
|
for (int row = 0; row < lines; ++row) {
|
|
int y = y_start + row;
|
|
for (int x = 0; x < h_res; ++x) {
|
|
uint16_t color = test_pattern_color(x, y, h_res, v_res);
|
|
|
|
if (x == 0 || y == 0 || x == (h_res - 1) || y == (v_res - 1)) {
|
|
color = rgb565(255, 0, 0);
|
|
} else if (x == (h_res / 2) || y == (v_res / 2)) {
|
|
color = rgb565(255, 255, 255);
|
|
}
|
|
|
|
buf[row * h_res + x] = color;
|
|
}
|
|
}
|
|
}
|
|
|
|
void platform_display_deinit(void) {
|
|
#if CONFIG_TQ_SCREEN_ENABLE
|
|
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
|
(void)gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL);
|
|
}
|
|
|
|
if (s_display.panel_handle != NULL) {
|
|
(void)esp_lcd_panel_del(s_display.panel_handle);
|
|
s_display.panel_handle = NULL;
|
|
}
|
|
if (s_display.io_handle != NULL) {
|
|
(void)esp_lcd_panel_io_del(s_display.io_handle);
|
|
s_display.io_handle = NULL;
|
|
}
|
|
if (s_display.bus_owned) {
|
|
(void)spi_bus_free(ST7789_SPI_HOST);
|
|
s_display.bus_owned = false;
|
|
}
|
|
s_display.initialized = false;
|
|
#endif
|
|
}
|
|
|
|
esp_err_t platform_display_init(void) {
|
|
#if !CONFIG_TQ_SCREEN_ENABLE
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
#else
|
|
if (s_display.initialized) {
|
|
return ESP_OK;
|
|
}
|
|
|
|
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
|
ESP_RETURN_ON_ERROR(gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_OFF_LEVEL),
|
|
TAG,
|
|
"set backlight off failed");
|
|
}
|
|
|
|
const size_t max_transfer_size = (size_t)CONFIG_TQ_SCREEN_H_RES *
|
|
(size_t)CONFIG_TQ_SCREEN_DRAW_LINES *
|
|
ST7789_COLOR_BYTES_PER_PIXEL + 8;
|
|
|
|
spi_bus_config_t bus_cfg = {
|
|
.sclk_io_num = CONFIG_TQ_SPI_SHARED_CLK_PIN,
|
|
.mosi_io_num = CONFIG_TQ_SPI_SHARED_MOSI_PIN,
|
|
.miso_io_num = -1,
|
|
.quadwp_io_num = -1,
|
|
.quadhd_io_num = -1,
|
|
.max_transfer_sz = (int)max_transfer_size,
|
|
};
|
|
|
|
esp_err_t err = spi_bus_initialize(ST7789_SPI_HOST, &bus_cfg, SPI_DMA_CH_AUTO);
|
|
if (err == ESP_OK) {
|
|
s_display.bus_owned = true;
|
|
} else if (err == ESP_ERR_INVALID_STATE) {
|
|
s_display.bus_owned = false;
|
|
ESP_LOGW(TAG, "SPI host already initialized, reuse host=%d", ST7789_SPI_HOST);
|
|
} else {
|
|
return err;
|
|
}
|
|
|
|
esp_lcd_panel_io_spi_config_t io_cfg = {
|
|
.cs_gpio_num = CONFIG_TQ_SCREEN_CS_PIN,
|
|
.dc_gpio_num = CONFIG_TQ_SCREEN_DC_PIN,
|
|
.spi_mode = CONFIG_TQ_SCREEN_SPI_MODE,
|
|
.pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ,
|
|
.trans_queue_depth = 1,
|
|
.lcd_cmd_bits = 8,
|
|
.lcd_param_bits = 8,
|
|
};
|
|
err = esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)ST7789_SPI_HOST, &io_cfg, &s_display.io_handle);
|
|
if (err != ESP_OK) {
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
|
|
esp_lcd_panel_dev_config_t panel_cfg = {
|
|
.reset_gpio_num = CONFIG_TQ_SCREEN_RESET_PIN,
|
|
.rgb_ele_order = ST7789_COLOR_ORDER,
|
|
.bits_per_pixel = 16,
|
|
};
|
|
err = esp_lcd_new_panel_st7789(s_display.io_handle, &panel_cfg, &s_display.panel_handle);
|
|
if (err != ESP_OK) {
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
|
|
err = esp_lcd_panel_reset(s_display.panel_handle);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "panel reset failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_init(s_display.panel_handle);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "panel init failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_swap_xy(s_display.panel_handle, ST7789_SWAP_XY);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "swap xy failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_mirror(s_display.panel_handle, ST7789_MIRROR_X, ST7789_MIRROR_Y);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "mirror failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_set_gap(s_display.panel_handle, CONFIG_TQ_SCREEN_X_GAP, CONFIG_TQ_SCREEN_Y_GAP);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "set gap failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_invert_color(s_display.panel_handle, false);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "invert control failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
err = esp_lcd_panel_disp_on_off(s_display.panel_handle, true);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "display on failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
|
|
if (CONFIG_TQ_SCREEN_BACKLIGHT_PIN >= 0) {
|
|
err = gpio_set_level(CONFIG_TQ_SCREEN_BACKLIGHT_PIN, ST7789_BACKLIGHT_ON_LEVEL);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "set backlight on failed: %s", esp_err_to_name(err));
|
|
platform_display_deinit();
|
|
return err;
|
|
}
|
|
}
|
|
|
|
s_display.initialized = true;
|
|
ESP_LOGI(TAG,
|
|
"st7789 ready: spi_host=%d sclk=%d mosi=%d dc=%d cs=%d rst=%d bk=%d bk_on=%d spi_mode=%d size=%dx%d pclk=%u",
|
|
ST7789_SPI_HOST,
|
|
CONFIG_TQ_SPI_SHARED_CLK_PIN,
|
|
CONFIG_TQ_SPI_SHARED_MOSI_PIN,
|
|
CONFIG_TQ_SCREEN_DC_PIN,
|
|
CONFIG_TQ_SCREEN_CS_PIN,
|
|
CONFIG_TQ_SCREEN_RESET_PIN,
|
|
CONFIG_TQ_SCREEN_BACKLIGHT_PIN,
|
|
ST7789_BACKLIGHT_ON_LEVEL,
|
|
CONFIG_TQ_SCREEN_SPI_MODE,
|
|
CONFIG_TQ_SCREEN_H_RES,
|
|
CONFIG_TQ_SCREEN_V_RES,
|
|
(unsigned)CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ);
|
|
return ESP_OK;
|
|
#endif
|
|
}
|
|
|
|
esp_err_t platform_display_show_test_pattern(void) {
|
|
#if !CONFIG_TQ_SCREEN_ENABLE
|
|
return ESP_ERR_NOT_SUPPORTED;
|
|
#else
|
|
ESP_RETURN_ON_ERROR(platform_display_init(), TAG, "display init failed");
|
|
|
|
const int h_res = CONFIG_TQ_SCREEN_H_RES;
|
|
const int v_res = CONFIG_TQ_SCREEN_V_RES;
|
|
const int draw_lines = CONFIG_TQ_SCREEN_DRAW_LINES > 0 ? CONFIG_TQ_SCREEN_DRAW_LINES : 1;
|
|
const size_t buf_pixels = (size_t)h_res * (size_t)draw_lines;
|
|
const size_t buf_size = buf_pixels * ST7789_COLOR_BYTES_PER_PIXEL;
|
|
|
|
uint16_t *draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_DMA | MALLOC_CAP_8BIT);
|
|
if (draw_buf == NULL) {
|
|
draw_buf = (uint16_t *)heap_caps_malloc(buf_size, MALLOC_CAP_8BIT);
|
|
}
|
|
ESP_RETURN_ON_FALSE(draw_buf != NULL, ESP_ERR_NO_MEM, TAG, "alloc draw buffer failed");
|
|
|
|
ESP_LOGI(TAG, "draw test pattern start");
|
|
|
|
esp_err_t err = ESP_OK;
|
|
for (int y = 0; y < v_res && err == ESP_OK; y += draw_lines) {
|
|
int chunk_lines = draw_lines;
|
|
if (y + chunk_lines > v_res) {
|
|
chunk_lines = v_res - y;
|
|
}
|
|
|
|
fill_test_pattern_chunk(draw_buf, y, chunk_lines, h_res, v_res);
|
|
err = esp_lcd_panel_draw_bitmap(s_display.panel_handle, 0, y, h_res, y + chunk_lines, draw_buf);
|
|
if (err != ESP_OK) {
|
|
break;
|
|
}
|
|
|
|
// Wait for queued color transfer to finish before reusing draw_buf.
|
|
err = esp_lcd_panel_io_tx_param(s_display.io_handle, -1, NULL, 0);
|
|
}
|
|
|
|
heap_caps_free(draw_buf);
|
|
|
|
if (err == ESP_OK) {
|
|
ESP_LOGI(TAG, "draw test pattern done");
|
|
}
|
|
return err;
|
|
#endif
|
|
}
|