ESP32_TV_Demo/main/lib/st7789_lcd.c

195 lines
5.9 KiB
C

#include "st7789_lcd.h"
#include <stddef.h>
#define LCD_ASSERT(x) \
if (!(x)) \
for (;;) { /* ABORT. */ \
}
#define LCD_ERROR_CHECK(x) \
if (x != ST7789_SUCCESS) return ST7789_FAIL
static uint8_t st7789_lcd_init_sequence[] = {
0x05, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, // Porch Setting
0x01, 0xB7, 0x35, // Gate Control
0x01, 0xBB, 0x19, // VCOM Setting // Factory 0x19 -> 0.725V
0x01, 0xC0, 0x2C, // LCM Control
0x01, 0xC2, 0x01, // VDV and VRH Command Enable
0x01, 0xC3, 0x12, // VRH Set // Factory 12H
0x01, 0xC4, 0x20, // VDV Set
0x01, 0xC6, 0x0F, // Frame Rate Control in Normal Mode
0x02, 0xD0, 0xA4, 0xA1, // Power Control 1
0x0E, 0xE0, 0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F,
0x23, // Positive Voltage Gamma Control
0x0E, 0xE1, 0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20,
0x23, // Negative Voltage Gamma Control
0x00, 0x21, // Inversion On
};
static st7789_lcd_ret_t st7789_lcd_execute_sequence(st7789_lcd_cb_t *cb, void *user_data, uint8_t *seq,
uint32_t seq_len) {
LCD_ASSERT(cb->write_command_cb != NULL);
uint32_t i = 0;
while (i < seq_len) {
LCD_ERROR_CHECK(cb->write_command_cb(user_data, &seq[i + 1], seq[i] + 1));
i += seq[i] + 2;
}
return ST7789_SUCCESS;
}
static st7789_lcd_ret_t st7789_lcd_reset(st7789_lcd_t *lcd) {
return lcd->cb.reset_cb(lcd->user_data);
}
static st7789_lcd_ret_t st7789_lcd_sleep(st7789_lcd_t *lcd, bool sleep) {
uint8_t tx_buf[1] = {0x11U};
if (sleep) {
tx_buf[0] = 0x10U;
}
return lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01);
}
static st7789_lcd_ret_t st7789_lcd_config(st7789_lcd_t *lcd) {
/* PIXFMT */
uint8_t tx_buf[2] = {0x3AU, lcd->config.pix_fmt};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x02));
/* INVERSION */
if (lcd->config.inverted) {
tx_buf[0] = 0x20U;
} else {
tx_buf[0] = 0x21U;
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01));
tx_buf[0] = 0x36;
switch (lcd->config.dir) {
case ST7789_LCD_DIR_270:
tx_buf[1] = 0xA8;
break;
case ST7789_LCD_DIR_180:
tx_buf[1] = 0xC8;
break;
case ST7789_LCD_DIR_90:
tx_buf[1] = 0x68;
break;
case ST7789_LCD_DIR_0:
default:
tx_buf[1] = 0x08;
break;
}
if (lcd->config.bgr_mode) {
tx_buf[1] &= ~(1 << 3U); /* RGB <-> BGR */
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x02));
return ST7789_SUCCESS;
}
static st7789_lcd_ret_t st7789_lcd_window(st7789_lcd_t *lcd, st7789_lcd_coord_t *coord) {
uint16_t real_x_start, real_x_end, real_y_start, real_y_end;
uint16_t x_offset, y_offset;
switch (lcd->config.dir) {
case ST7789_LCD_DIR_0:
case ST7789_LCD_DIR_90:
x_offset = 0;
y_offset = 0;
break;
case ST7789_LCD_DIR_180:
x_offset = 0;
y_offset = 80;
break;
case ST7789_LCD_DIR_270:
x_offset = 80;
y_offset = 0;
break;
default:
x_offset = 0;
y_offset = 0;
}
real_x_start = coord->x_start + x_offset;
real_x_end = coord->x_end + x_offset;
real_y_start = coord->y_start + y_offset;
real_y_end = coord->y_end + y_offset;
uint8_t tx_buf[5] = {
0x2A,
((uint8_t)(real_x_start >> 0x08U) & 0xFFU),
(real_x_start & 0xFFU),
((uint8_t)(real_x_end >> 0x08U) & 0xFFU),
(real_x_end & 0xFFU),
};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05));
tx_buf[0] = 0x2BU;
tx_buf[1] = ((uint8_t)(real_y_start >> 0x08U) & 0xFFU);
tx_buf[2] = (real_y_start & 0xFFU);
tx_buf[3] = ((uint8_t)(real_y_end >> 0x08U) & 0xFFU);
tx_buf[4] = (real_y_end & 0xFFU);
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_init(st7789_lcd_t *lcd) {
LCD_ERROR_CHECK(st7789_lcd_reset(lcd));
LCD_ERROR_CHECK(st7789_lcd_sleep(lcd, false));
LCD_ERROR_CHECK(lcd->cb.delay_cb(lcd->user_data, 120));
LCD_ERROR_CHECK(st7789_lcd_execute_sequence(&lcd->cb, lcd->user_data, st7789_lcd_init_sequence,
sizeof(st7789_lcd_init_sequence)));
LCD_ERROR_CHECK(st7789_lcd_config(lcd));
LCD_ERROR_CHECK(st7789_lcd_power(lcd, true));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_power(st7789_lcd_t *lcd, bool on) {
uint8_t tx_buf[1] = {0x28U};
if (on) {
tx_buf[0] = 0x29U;
}
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 1));
return ST7789_SUCCESS;
}
st7789_lcd_ret_t st7789_lcd_upload(st7789_lcd_t *lcd, st7789_lcd_coord_t *coord, uint8_t *data) {
LCD_ERROR_CHECK(st7789_lcd_window(lcd, coord));
uint32_t data_size = (coord->y_end - coord->y_start + 1) * (coord->x_end - coord->x_start + 1);
switch (lcd->config.pix_fmt) {
case ST7789_LCD_PIXFMT_RGB444:
data_size = data_size * 3 / 2;
break;
case ST7789_LCD_PIXFMT_RGB565:
data_size = data_size * 2;
break;
case ST7789_LCD_PIXFMT_RGB666:
case ST7789_LCD_PIXFMT_RGB888:
default:
data_size = data_size * 3;
break;
}
uint8_t tx_buf[1] = {0x2C};
LCD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x01));
LCD_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, data_size));
return ST7789_SUCCESS;
}