195 lines
5.9 KiB
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;
|
|
} |