#include "st7789_lcd.h" #include #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; }