驱动lcd
This commit is contained in:
18
components/lcd/CMakeLists.txt
Normal file
18
components/lcd/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
idf_component_register(
|
||||
SRCS
|
||||
"src/lcd_demo.c"
|
||||
"src/st7789p3.c"
|
||||
"src/lcd_decode_image.c"
|
||||
"src/lcd_pretty_effect.c"
|
||||
INCLUDE_DIRS
|
||||
"include"
|
||||
PRIV_INCLUDE_DIRS
|
||||
"internal"
|
||||
REQUIRES
|
||||
driver
|
||||
esp_driver_gpio
|
||||
esp_driver_spi
|
||||
espressif__esp_jpeg
|
||||
EMBED_FILES
|
||||
"assets/lcd_demo.jpg"
|
||||
)
|
||||
BIN
components/lcd/assets/lcd_demo.jpg
Normal file
BIN
components/lcd/assets/lcd_demo.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
15
components/lcd/include/lcd.h
Normal file
15
components/lcd/include/lcd.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
esp_err_t lcd_module_init(void);
|
||||
esp_err_t lcd_module_start_demo(void);
|
||||
void lcd_module_deinit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
10
components/lcd/internal/lcd_decode_image.h
Normal file
10
components/lcd/internal/lcd_decode_image.h
Normal file
@@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#define PLATFORM_LCD_DEMO_IMAGE_W 320
|
||||
#define PLATFORM_LCD_DEMO_IMAGE_H 240
|
||||
|
||||
esp_err_t platform_lcd_decode_image(uint16_t **pixels);
|
||||
8
components/lcd/internal/lcd_pretty_effect.h
Normal file
8
components/lcd/internal/lcd_pretty_effect.h
Normal file
@@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
void platform_lcd_pretty_effect_calc_lines(uint16_t *dest, int line, int frame, int linect);
|
||||
esp_err_t platform_lcd_pretty_effect_init(void);
|
||||
89
components/lcd/internal/st7789p3.h
Normal file
89
components/lcd/internal/st7789p3.h
Normal file
@@ -0,0 +1,89 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "driver/spi_master.h"
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct st7789p3_panel_t *st7789p3_handle_t;
|
||||
|
||||
#define ST7789P3_MADCTL_MY 0x80
|
||||
#define ST7789P3_MADCTL_MX 0x40
|
||||
#define ST7789P3_MADCTL_MV 0x20
|
||||
#define ST7789P3_MADCTL_ML 0x10
|
||||
#define ST7789P3_MADCTL_BGR 0x08
|
||||
#define ST7789P3_MADCTL_MH 0x04
|
||||
|
||||
typedef struct {
|
||||
spi_host_device_t spi_host;
|
||||
int pin_mosi;
|
||||
int pin_miso;
|
||||
int pin_sclk;
|
||||
int pin_cs;
|
||||
int pin_dc;
|
||||
int pin_rst;
|
||||
int pin_bckl;
|
||||
uint32_t pclk_hz;
|
||||
uint8_t spi_mode;
|
||||
int queue_size;
|
||||
size_t max_transfer_sz;
|
||||
uint16_t h_res;
|
||||
uint16_t v_res;
|
||||
uint8_t madctl_val;
|
||||
bool invert_color;
|
||||
int backlight_on_level;
|
||||
bool init_spi_bus;
|
||||
} st7789p3_config_t;
|
||||
|
||||
#define ST7789P3_CONFIG_DEFAULT() \
|
||||
{ \
|
||||
.spi_host = SPI2_HOST, \
|
||||
.pin_mosi = -1, \
|
||||
.pin_miso = -1, \
|
||||
.pin_sclk = -1, \
|
||||
.pin_cs = -1, \
|
||||
.pin_dc = -1, \
|
||||
.pin_rst = -1, \
|
||||
.pin_bckl = -1, \
|
||||
.pclk_hz = 10 * 1000 * 1000, \
|
||||
.spi_mode = 0, \
|
||||
.queue_size = 7, \
|
||||
.max_transfer_sz = 0, \
|
||||
.h_res = 320, \
|
||||
.v_res = 240, \
|
||||
.madctl_val = ST7789P3_MADCTL_MX | ST7789P3_MADCTL_MV, \
|
||||
.invert_color = false, \
|
||||
.backlight_on_level = 1, \
|
||||
.init_spi_bus = true, \
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_new_panel(const st7789p3_config_t *config, st7789p3_handle_t *ret_panel);
|
||||
esp_err_t st7789p3_del(st7789p3_handle_t panel);
|
||||
|
||||
esp_err_t st7789p3_reset(st7789p3_handle_t panel);
|
||||
esp_err_t st7789p3_init(st7789p3_handle_t panel);
|
||||
|
||||
esp_err_t st7789p3_set_backlight(st7789p3_handle_t panel, bool on);
|
||||
esp_err_t st7789p3_set_madctl(st7789p3_handle_t panel, uint8_t madctl_val);
|
||||
esp_err_t st7789p3_set_invert(st7789p3_handle_t panel, bool invert_color);
|
||||
|
||||
esp_err_t st7789p3_set_window(
|
||||
st7789p3_handle_t panel, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end);
|
||||
esp_err_t st7789p3_draw_bitmap(st7789p3_handle_t panel,
|
||||
uint16_t x_start,
|
||||
uint16_t y_start,
|
||||
uint16_t x_end,
|
||||
uint16_t y_end,
|
||||
const uint16_t *color_data);
|
||||
|
||||
esp_err_t st7789p3_fill_color(st7789p3_handle_t panel, uint16_t color);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
47
components/lcd/src/lcd_decode_image.c
Normal file
47
components/lcd/src/lcd_decode_image.c
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "lcd_decode_image.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_log.h"
|
||||
#include "jpeg_decoder.h"
|
||||
|
||||
static const char *TAG = "lcd_image_dec";
|
||||
|
||||
extern const uint8_t lcd_demo_jpg_start[] asm("_binary_lcd_demo_jpg_start");
|
||||
extern const uint8_t lcd_demo_jpg_end[] asm("_binary_lcd_demo_jpg_end");
|
||||
|
||||
esp_err_t platform_lcd_decode_image(uint16_t **pixels)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(pixels != NULL, ESP_ERR_INVALID_ARG, TAG, "pixels pointer is null");
|
||||
*pixels = NULL;
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
|
||||
*pixels = calloc(PLATFORM_LCD_DEMO_IMAGE_W * PLATFORM_LCD_DEMO_IMAGE_H, sizeof(uint16_t));
|
||||
ESP_GOTO_ON_FALSE(*pixels != NULL, ESP_ERR_NO_MEM, err, TAG, "alloc image buffer failed");
|
||||
|
||||
esp_jpeg_image_cfg_t jpeg_cfg = {
|
||||
.indata = (uint8_t *)lcd_demo_jpg_start,
|
||||
.indata_size = lcd_demo_jpg_end - lcd_demo_jpg_start,
|
||||
.outbuf = (uint8_t *)(*pixels),
|
||||
.outbuf_size = PLATFORM_LCD_DEMO_IMAGE_W * PLATFORM_LCD_DEMO_IMAGE_H * sizeof(uint16_t),
|
||||
.out_format = JPEG_IMAGE_FORMAT_RGB565,
|
||||
.out_scale = JPEG_IMAGE_SCALE_0,
|
||||
.flags = {
|
||||
.swap_color_bytes = 1,
|
||||
},
|
||||
};
|
||||
|
||||
esp_jpeg_image_output_t out_img = {0};
|
||||
ret = esp_jpeg_decode(&jpeg_cfg, &out_img);
|
||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "jpeg decode failed");
|
||||
|
||||
ESP_LOGI(TAG, "lcd demo image decoded: %d x %d", out_img.width, out_img.height);
|
||||
return ESP_OK;
|
||||
|
||||
err:
|
||||
free(*pixels);
|
||||
*pixels = NULL;
|
||||
return ret;
|
||||
}
|
||||
200
components/lcd/src/lcd_demo.c
Normal file
200
components/lcd/src/lcd_demo.c
Normal file
@@ -0,0 +1,200 @@
|
||||
#include "lcd.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "lcd_pretty_effect.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "st7789p3.h"
|
||||
|
||||
static const char *TAG = "lcd_demo";
|
||||
|
||||
#define ST7789_SPI_HOST SPI2_HOST
|
||||
#define LCD_DEMO_H_RES 320
|
||||
#define LCD_DEMO_V_RES 240
|
||||
#define LCD_DEMO_PARALLEL_LINES 16
|
||||
#define LCD_DEMO_TASK_STACK_SIZE (6 * 1024)
|
||||
#define LCD_DEMO_TASK_PRIORITY (tskIDLE_PRIORITY + 1)
|
||||
#define LCD_DEMO_TASK_CORE_ID 1
|
||||
|
||||
#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
|
||||
#else
|
||||
#define ST7789_BACKLIGHT_ON_LEVEL 0
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
st7789p3_handle_t panel;
|
||||
TaskHandle_t demo_task;
|
||||
bool initialized;
|
||||
} lcd_state_t;
|
||||
|
||||
static lcd_state_t s_lcd;
|
||||
|
||||
static int resolve_reset_pin(void)
|
||||
{
|
||||
if (CONFIG_TQ_SCREEN_RESET_PIN >= 0) {
|
||||
return CONFIG_TQ_SCREEN_RESET_PIN;
|
||||
}
|
||||
return CONFIG_TQ_SCREEN_BACKLIGHT_PIN;
|
||||
}
|
||||
|
||||
static void lcd_demo_task(void *arg)
|
||||
{
|
||||
st7789p3_handle_t panel = (st7789p3_handle_t)arg;
|
||||
uint16_t *lines[2] = {0};
|
||||
const size_t line_buffer_bytes = LCD_DEMO_H_RES * LCD_DEMO_PARALLEL_LINES * sizeof(uint16_t);
|
||||
int frame = 0;
|
||||
int draw_buffer_index = 0;
|
||||
|
||||
esp_err_t err = platform_lcd_pretty_effect_init();
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "pretty effect init failed: %s", esp_err_to_name(err));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
lines[i] = heap_caps_malloc(line_buffer_bytes, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
if (lines[i] == NULL) {
|
||||
lines[i] = heap_caps_malloc(line_buffer_bytes, MALLOC_CAP_8BIT);
|
||||
}
|
||||
if (lines[i] == NULL) {
|
||||
ESP_LOGE(TAG, "lcd demo line buffer alloc failed");
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ST7789P3 pretty effect demo started");
|
||||
while (true) {
|
||||
frame++;
|
||||
for (int y = 0; y < LCD_DEMO_V_RES; y += LCD_DEMO_PARALLEL_LINES) {
|
||||
int lines_to_draw = LCD_DEMO_V_RES - y;
|
||||
if (lines_to_draw > LCD_DEMO_PARALLEL_LINES) {
|
||||
lines_to_draw = LCD_DEMO_PARALLEL_LINES;
|
||||
}
|
||||
|
||||
platform_lcd_pretty_effect_calc_lines(lines[draw_buffer_index], y, frame, lines_to_draw);
|
||||
err = st7789p3_draw_bitmap(panel, 0, y, LCD_DEMO_H_RES, y + lines_to_draw, lines[draw_buffer_index]);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "lcd demo draw failed: %s", esp_err_to_name(err));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
draw_buffer_index ^= 1;
|
||||
vTaskDelay(pdMS_TO_TICKS(1));
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
if (lines[0] != NULL) {
|
||||
heap_caps_free(lines[0]);
|
||||
}
|
||||
if (lines[1] != NULL) {
|
||||
heap_caps_free(lines[1]);
|
||||
}
|
||||
s_lcd.demo_task = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void lcd_module_deinit(void)
|
||||
{
|
||||
#if CONFIG_TQ_SCREEN_ENABLE
|
||||
if (s_lcd.demo_task != NULL) {
|
||||
vTaskDelete(s_lcd.demo_task);
|
||||
s_lcd.demo_task = NULL;
|
||||
}
|
||||
if (s_lcd.panel != NULL) {
|
||||
(void)st7789p3_del(s_lcd.panel);
|
||||
s_lcd.panel = NULL;
|
||||
}
|
||||
s_lcd.initialized = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t lcd_module_init(void)
|
||||
{
|
||||
#if !CONFIG_TQ_SCREEN_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_lcd.initialized) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
st7789p3_config_t lcd_cfg = ST7789P3_CONFIG_DEFAULT();
|
||||
lcd_cfg.spi_host = ST7789_SPI_HOST;
|
||||
lcd_cfg.pin_miso = CONFIG_TQ_PRINTER_MISO_PIN;
|
||||
lcd_cfg.pin_mosi = CONFIG_TQ_SPI_SHARED_MOSI_PIN;
|
||||
lcd_cfg.pin_sclk = CONFIG_TQ_SPI_SHARED_CLK_PIN;
|
||||
lcd_cfg.pin_cs = CONFIG_TQ_SCREEN_CS_PIN;
|
||||
lcd_cfg.pin_dc = CONFIG_TQ_SCREEN_DC_PIN;
|
||||
lcd_cfg.pin_rst = resolve_reset_pin();
|
||||
lcd_cfg.pin_bckl = CONFIG_TQ_SCREEN_BACKLIGHT_PIN;
|
||||
lcd_cfg.backlight_on_level = ST7789_BACKLIGHT_ON_LEVEL;
|
||||
lcd_cfg.pclk_hz = CONFIG_TQ_SCREEN_PIXEL_CLOCK_HZ;
|
||||
lcd_cfg.spi_mode = CONFIG_TQ_SCREEN_SPI_MODE;
|
||||
lcd_cfg.h_res = LCD_DEMO_H_RES;
|
||||
lcd_cfg.v_res = LCD_DEMO_V_RES;
|
||||
lcd_cfg.madctl_val = ST7789P3_MADCTL_MX | ST7789P3_MADCTL_MV;
|
||||
lcd_cfg.invert_color = true;
|
||||
lcd_cfg.max_transfer_sz = LCD_DEMO_PARALLEL_LINES * LCD_DEMO_H_RES * sizeof(uint16_t) + 8;
|
||||
|
||||
esp_err_t err = st7789p3_new_panel(&lcd_cfg, &s_lcd.panel);
|
||||
if (err != ESP_OK) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = st7789p3_init(s_lcd.panel);
|
||||
if (err != ESP_OK) {
|
||||
lcd_module_deinit();
|
||||
return err;
|
||||
}
|
||||
|
||||
s_lcd.initialized = true;
|
||||
ESP_LOGI(TAG,
|
||||
"st7789p3 ready: spi_host=%d mosi=%d clk=%d miso=%d dc=%d cs=%d rst=%d bk=%d pclk=%u",
|
||||
ST7789_SPI_HOST,
|
||||
CONFIG_TQ_SPI_SHARED_MOSI_PIN,
|
||||
CONFIG_TQ_SPI_SHARED_CLK_PIN,
|
||||
CONFIG_TQ_PRINTER_MISO_PIN,
|
||||
CONFIG_TQ_SCREEN_DC_PIN,
|
||||
CONFIG_TQ_SCREEN_CS_PIN,
|
||||
lcd_cfg.pin_rst,
|
||||
CONFIG_TQ_SCREEN_BACKLIGHT_PIN,
|
||||
(unsigned)lcd_cfg.pclk_hz);
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t lcd_module_start_demo(void)
|
||||
{
|
||||
#if !CONFIG_TQ_SCREEN_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
ESP_RETURN_ON_ERROR(lcd_module_init(), TAG, "display init failed");
|
||||
if (s_lcd.demo_task != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
BaseType_t rc = xTaskCreatePinnedToCore(lcd_demo_task,
|
||||
"lcd_demo",
|
||||
LCD_DEMO_TASK_STACK_SIZE,
|
||||
(void *)s_lcd.panel,
|
||||
LCD_DEMO_TASK_PRIORITY,
|
||||
&s_lcd.demo_task,
|
||||
LCD_DEMO_TASK_CORE_ID);
|
||||
ESP_RETURN_ON_FALSE(rc == pdPASS, ESP_ERR_NO_MEM, TAG, "lcd demo task create failed");
|
||||
return ESP_OK;
|
||||
#endif
|
||||
}
|
||||
71
components/lcd/src/lcd_pretty_effect.c
Normal file
71
components/lcd/src/lcd_pretty_effect.c
Normal file
@@ -0,0 +1,71 @@
|
||||
#include "lcd_pretty_effect.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "lcd_decode_image.h"
|
||||
|
||||
static uint16_t *s_pixels;
|
||||
static int s_prev_frame = -1;
|
||||
static int8_t s_xofs[PLATFORM_LCD_DEMO_IMAGE_W];
|
||||
static int8_t s_yofs[PLATFORM_LCD_DEMO_IMAGE_H];
|
||||
static int8_t s_xcomp[PLATFORM_LCD_DEMO_IMAGE_W];
|
||||
static int8_t s_ycomp[PLATFORM_LCD_DEMO_IMAGE_H];
|
||||
|
||||
static inline uint16_t get_bgnd_pixel(int x, int y)
|
||||
{
|
||||
return (uint16_t)*(s_pixels + (y * PLATFORM_LCD_DEMO_IMAGE_W) + x);
|
||||
}
|
||||
|
||||
esp_err_t platform_lcd_pretty_effect_init(void)
|
||||
{
|
||||
if (s_pixels != NULL) {
|
||||
return ESP_OK;
|
||||
}
|
||||
return platform_lcd_decode_image(&s_pixels);
|
||||
}
|
||||
|
||||
void platform_lcd_pretty_effect_calc_lines(uint16_t *dest, int line, int frame, int linect)
|
||||
{
|
||||
if (dest == NULL || linect <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_pixels == NULL) {
|
||||
memset(dest, 0, (size_t)PLATFORM_LCD_DEMO_IMAGE_W * (size_t)linect * sizeof(uint16_t));
|
||||
return;
|
||||
}
|
||||
|
||||
if (frame != s_prev_frame) {
|
||||
for (int x = 0; x < PLATFORM_LCD_DEMO_IMAGE_W; x++) {
|
||||
s_xofs[x] = sin(frame * 0.15 + x * 0.06) * 4;
|
||||
s_xcomp[x] = sin(frame * 0.11 + x * 0.12) * 4;
|
||||
}
|
||||
for (int y = 0; y < PLATFORM_LCD_DEMO_IMAGE_H; y++) {
|
||||
s_yofs[y] = sin(frame * 0.1 + y * 0.05) * 4;
|
||||
s_ycomp[y] = sin(frame * 0.07 + y * 0.15) * 4;
|
||||
}
|
||||
s_prev_frame = frame;
|
||||
}
|
||||
|
||||
for (int y = line; y < line + linect; y++) {
|
||||
for (int x = 0; x < PLATFORM_LCD_DEMO_IMAGE_W; x++) {
|
||||
int sample_x = x + s_yofs[y] + s_xcomp[x];
|
||||
int sample_y = y + s_xofs[x] + s_ycomp[y];
|
||||
|
||||
if (sample_x < 0) {
|
||||
sample_x = 0;
|
||||
} else if (sample_x >= PLATFORM_LCD_DEMO_IMAGE_W) {
|
||||
sample_x = PLATFORM_LCD_DEMO_IMAGE_W - 1;
|
||||
}
|
||||
|
||||
if (sample_y < 0) {
|
||||
sample_y = 0;
|
||||
} else if (sample_y >= PLATFORM_LCD_DEMO_IMAGE_H) {
|
||||
sample_y = PLATFORM_LCD_DEMO_IMAGE_H - 1;
|
||||
}
|
||||
|
||||
*dest++ = get_bgnd_pixel(sample_x, sample_y);
|
||||
}
|
||||
}
|
||||
}
|
||||
460
components/lcd/src/st7789p3.c
Normal file
460
components/lcd/src/st7789p3.c
Normal file
@@ -0,0 +1,460 @@
|
||||
#include "st7789p3.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "driver/gpio.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
|
||||
#define ST7789P3_CMD_DELAY_FLAG 0x80
|
||||
#define ST7789P3_CMD_DATA_LEN_MASK 0x1F
|
||||
#define ST7789P3_TX_MAX_CHUNK_BYTES 1024
|
||||
|
||||
typedef struct {
|
||||
uint8_t cmd;
|
||||
uint8_t data[16];
|
||||
uint8_t data_bytes;
|
||||
} st7789p3_lcd_init_cmd_t;
|
||||
|
||||
struct st7789p3_panel_t {
|
||||
st7789p3_config_t cfg;
|
||||
spi_device_handle_t spi;
|
||||
bool bus_owned;
|
||||
};
|
||||
|
||||
static const char *TAG = "st7789p3";
|
||||
|
||||
// Sequence verified on the LCD used by docs/lcd_verification.
|
||||
DRAM_ATTR static const st7789p3_lcd_init_cmd_t s_st7789p3_init_cmds[] = {
|
||||
{0x36, {0x00}, 1},
|
||||
{0x3A, {0x55}, 1},
|
||||
{0xB2, {0x0C, 0x0C, 0x00, 0x33, 0x33}, 5},
|
||||
{0xB7, {0x45}, 1},
|
||||
{0xBB, {0x2B}, 1},
|
||||
{0xC0, {0x2C}, 1},
|
||||
{0xC2, {0x01, 0xFF}, 2},
|
||||
{0xC3, {0x11}, 1},
|
||||
{0xC4, {0x20}, 1},
|
||||
{0xC6, {0x0F}, 1},
|
||||
{0xD0, {0xA4, 0xA1}, 2},
|
||||
{0xE0, {0xD0, 0x00, 0x05, 0x0E, 0x15, 0x0D, 0x37, 0x43, 0x47, 0x09, 0x15, 0x12, 0x16, 0x19}, 14},
|
||||
{0xE1, {0xD0, 0x00, 0x05, 0x0D, 0x0C, 0x06, 0x2D, 0x44, 0x40, 0x0E, 0x1C, 0x18, 0x16, 0x19}, 14},
|
||||
{0x11, {0}, ST7789P3_CMD_DELAY_FLAG},
|
||||
{0, {0}, 0xFF},
|
||||
};
|
||||
|
||||
static esp_err_t st7789p3_validate_config(const st7789p3_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->pin_mosi < 0 || config->pin_sclk < 0 || config->pin_cs < 0 || config->pin_dc < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->h_res == 0 || config->v_res == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (config->spi_mode > 3) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_configure_gpio(st7789p3_handle_t panel)
|
||||
{
|
||||
uint64_t pin_mask = 0;
|
||||
|
||||
pin_mask |= (1ULL << panel->cfg.pin_dc);
|
||||
if (panel->cfg.pin_rst >= 0) {
|
||||
pin_mask |= (1ULL << panel->cfg.pin_rst);
|
||||
}
|
||||
if (panel->cfg.pin_bckl >= 0) {
|
||||
pin_mask |= (1ULL << panel->cfg.pin_bckl);
|
||||
}
|
||||
|
||||
gpio_config_t io_conf = {
|
||||
.pin_bit_mask = pin_mask,
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pull_up_en = GPIO_PULLUP_DISABLE,
|
||||
.pull_down_en = GPIO_PULLDOWN_DISABLE,
|
||||
.intr_type = GPIO_INTR_DISABLE,
|
||||
};
|
||||
|
||||
return gpio_config(&io_conf);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_spi_write(st7789p3_handle_t panel, int dc_level, const void *data, size_t data_size)
|
||||
{
|
||||
if (data_size == 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_set_level(panel->cfg.pin_dc, dc_level);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
spi_transaction_t t = {0};
|
||||
t.length = data_size * 8;
|
||||
t.tx_buffer = data;
|
||||
|
||||
return spi_device_transmit(panel->spi, &t);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_send_cmd(st7789p3_handle_t panel, uint8_t cmd)
|
||||
{
|
||||
return st7789p3_spi_write(panel, 0, &cmd, 1);
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_send_data(st7789p3_handle_t panel, const void *data, size_t data_size)
|
||||
{
|
||||
const uint8_t *data_ptr = (const uint8_t *)data;
|
||||
size_t remaining = data_size;
|
||||
size_t tx_chunk_size = ST7789P3_TX_MAX_CHUNK_BYTES;
|
||||
|
||||
if (panel->cfg.max_transfer_sz > 0 && panel->cfg.max_transfer_sz < tx_chunk_size) {
|
||||
tx_chunk_size = panel->cfg.max_transfer_sz;
|
||||
}
|
||||
|
||||
while (remaining > 0) {
|
||||
size_t chunk = (remaining > tx_chunk_size) ? tx_chunk_size : remaining;
|
||||
esp_err_t ret = st7789p3_spi_write(panel, 1, data_ptr, chunk);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
data_ptr += chunk;
|
||||
remaining -= chunk;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t st7789p3_run_init_cmds(st7789p3_handle_t panel)
|
||||
{
|
||||
for (size_t i = 0; s_st7789p3_init_cmds[i].data_bytes != 0xFF; i++) {
|
||||
const st7789p3_lcd_init_cmd_t *init_cmd = &s_st7789p3_init_cmds[i];
|
||||
uint8_t data_bytes = init_cmd->data_bytes & ST7789P3_CMD_DATA_LEN_MASK;
|
||||
uint8_t data[16] = {0};
|
||||
|
||||
if (data_bytes > 0) {
|
||||
memcpy(data, init_cmd->data, data_bytes);
|
||||
}
|
||||
|
||||
if (init_cmd->cmd == 0x36 && data_bytes == 1) {
|
||||
data[0] = panel->cfg.madctl_val;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, init_cmd->cmd);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_data(panel, data, data_bytes);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (init_cmd->data_bytes & ST7789P3_CMD_DELAY_FLAG) {
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_new_panel(const st7789p3_config_t *config, st7789p3_handle_t *ret_panel)
|
||||
{
|
||||
if (ret_panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
*ret_panel = NULL;
|
||||
|
||||
esp_err_t ret = st7789p3_validate_config(config);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
st7789p3_handle_t panel = calloc(1, sizeof(*panel));
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
panel->cfg = *config;
|
||||
if (panel->cfg.pclk_hz == 0) {
|
||||
panel->cfg.pclk_hz = 10 * 1000 * 1000;
|
||||
}
|
||||
if (panel->cfg.queue_size <= 0) {
|
||||
panel->cfg.queue_size = 7;
|
||||
}
|
||||
if (panel->cfg.max_transfer_sz == 0) {
|
||||
panel->cfg.max_transfer_sz = panel->cfg.h_res * 20 * sizeof(uint16_t) + 8;
|
||||
}
|
||||
|
||||
ret = st7789p3_configure_gpio(panel);
|
||||
if (ret != ESP_OK) {
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (panel->cfg.init_spi_bus) {
|
||||
spi_bus_config_t buscfg = {
|
||||
.mosi_io_num = panel->cfg.pin_mosi,
|
||||
.miso_io_num = panel->cfg.pin_miso,
|
||||
.sclk_io_num = panel->cfg.pin_sclk,
|
||||
.quadwp_io_num = -1,
|
||||
.quadhd_io_num = -1,
|
||||
.max_transfer_sz = panel->cfg.max_transfer_sz,
|
||||
};
|
||||
|
||||
ret = spi_bus_initialize(panel->cfg.spi_host, &buscfg, SPI_DMA_CH_AUTO);
|
||||
if (ret == ESP_OK) {
|
||||
panel->bus_owned = true;
|
||||
} else if (ret == ESP_ERR_INVALID_STATE) {
|
||||
ESP_LOGW(TAG, "SPI host already initialized, skip bus init");
|
||||
} else {
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
spi_device_interface_config_t devcfg = {
|
||||
.clock_speed_hz = panel->cfg.pclk_hz,
|
||||
.mode = panel->cfg.spi_mode,
|
||||
.spics_io_num = panel->cfg.pin_cs,
|
||||
.queue_size = panel->cfg.queue_size,
|
||||
};
|
||||
|
||||
ret = spi_bus_add_device(panel->cfg.spi_host, &devcfg, &panel->spi);
|
||||
if (ret != ESP_OK) {
|
||||
if (panel->bus_owned) {
|
||||
spi_bus_free(panel->cfg.spi_host);
|
||||
}
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
*ret_panel = panel;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_del(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = ESP_OK;
|
||||
esp_err_t local_ret = spi_bus_remove_device(panel->spi);
|
||||
if (local_ret != ESP_OK) {
|
||||
ret = local_ret;
|
||||
}
|
||||
|
||||
if (panel->bus_owned) {
|
||||
local_ret = spi_bus_free(panel->cfg.spi_host);
|
||||
if (ret == ESP_OK && local_ret != ESP_OK) {
|
||||
ret = local_ret;
|
||||
}
|
||||
}
|
||||
|
||||
free(panel);
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_backlight(st7789p3_handle_t panel, bool on)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (panel->cfg.pin_bckl < 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int on_level = panel->cfg.backlight_on_level ? 1 : 0;
|
||||
int off_level = on_level ? 0 : 1;
|
||||
return gpio_set_level(panel->cfg.pin_bckl, on ? on_level : off_level);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_madctl(st7789p3_handle_t panel, uint8_t madctl_val)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
panel->cfg.madctl_val = madctl_val;
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, 0x36);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
return st7789p3_send_data(panel, &madctl_val, 1);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_invert(st7789p3_handle_t panel, bool invert_color)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
panel->cfg.invert_color = invert_color;
|
||||
return st7789p3_send_cmd(panel, invert_color ? 0x21 : 0x20);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_reset(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (panel->cfg.pin_rst < 0) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t ret = gpio_set_level(panel->cfg.pin_rst, 0);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
ret = gpio_set_level(panel->cfg.pin_rst, 1);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_init(st7789p3_handle_t panel)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_backlight(panel, false);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_reset(panel);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_run_init_cmds(panel);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_set_invert(panel, panel->cfg.invert_color);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_cmd(panel, 0x29);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
|
||||
return st7789p3_set_backlight(panel, true);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_set_window(
|
||||
st7789p3_handle_t panel, uint16_t x_start, uint16_t y_start, uint16_t x_end, uint16_t y_end)
|
||||
{
|
||||
if (panel == NULL || x_start >= x_end || y_start >= y_end) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (x_end > panel->cfg.h_res || y_end > panel->cfg.v_res) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
uint8_t x_data[4] = {
|
||||
(uint8_t)(x_start >> 8),
|
||||
(uint8_t)(x_start & 0xFF),
|
||||
(uint8_t)((x_end - 1) >> 8),
|
||||
(uint8_t)((x_end - 1) & 0xFF),
|
||||
};
|
||||
uint8_t y_data[4] = {
|
||||
(uint8_t)(y_start >> 8),
|
||||
(uint8_t)(y_start & 0xFF),
|
||||
(uint8_t)((y_end - 1) >> 8),
|
||||
(uint8_t)((y_end - 1) & 0xFF),
|
||||
};
|
||||
|
||||
esp_err_t ret = st7789p3_send_cmd(panel, 0x2A);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = st7789p3_send_data(panel, x_data, sizeof(x_data));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = st7789p3_send_cmd(panel, 0x2B);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
ret = st7789p3_send_data(panel, y_data, sizeof(y_data));
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return st7789p3_send_cmd(panel, 0x2C);
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_draw_bitmap(st7789p3_handle_t panel,
|
||||
uint16_t x_start,
|
||||
uint16_t y_start,
|
||||
uint16_t x_end,
|
||||
uint16_t y_end,
|
||||
const uint16_t *color_data)
|
||||
{
|
||||
if (panel == NULL || color_data == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_window(panel, x_start, y_start, x_end, y_end);
|
||||
if (ret != ESP_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pixel_count = (size_t)(x_end - x_start) * (size_t)(y_end - y_start);
|
||||
return st7789p3_send_data(panel, color_data, pixel_count * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
esp_err_t st7789p3_fill_color(st7789p3_handle_t panel, uint16_t color)
|
||||
{
|
||||
if (panel == NULL) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
static const size_t fill_pixels = 256;
|
||||
uint16_t *fill_buffer = heap_caps_malloc(fill_pixels * sizeof(uint16_t), MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
|
||||
if (fill_buffer == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < fill_pixels; i++) {
|
||||
fill_buffer[i] = color;
|
||||
}
|
||||
|
||||
esp_err_t ret = st7789p3_set_window(panel, 0, 0, panel->cfg.h_res, panel->cfg.v_res);
|
||||
if (ret != ESP_OK) {
|
||||
free(fill_buffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t total_pixels = (size_t)panel->cfg.h_res * (size_t)panel->cfg.v_res;
|
||||
while (total_pixels > 0) {
|
||||
size_t pixels_to_send = (total_pixels > fill_pixels) ? fill_pixels : total_pixels;
|
||||
ret = st7789p3_send_data(panel, fill_buffer, pixels_to_send * sizeof(uint16_t));
|
||||
if (ret != ESP_OK) {
|
||||
free(fill_buffer);
|
||||
return ret;
|
||||
}
|
||||
total_pixels -= pixels_to_send;
|
||||
}
|
||||
|
||||
free(fill_buffer);
|
||||
return ESP_OK;
|
||||
}
|
||||
Reference in New Issue
Block a user