#include "driver/lcd_st7789.h" #include "epd_private.h" #define ST7789_CMD_SWRESET (0x01U) #define ST7789_CMD_SLPIN (0x10U) #define ST7789_CMD_SLPOUT (0x11U) #define ST7789_CMD_INVOFF (0x20U) #define ST7789_CMD_INVON (0x21U) #define ST7789_CMD_DISPOFF (0x28U) #define ST7789_CMD_DISPON (0x29U) #define ST7789_CMD_CASET (0x2AU) #define ST7789_CMD_RASET (0x2BU) #define ST7789_CMD_RAMWR (0x2CU) #define ST7789_CMD_MADCTL (0x36U) #define ST7789_CMD_COLMOD (0x3AU) static epd_ret_t lcd_st7789_reset(lcd_st7789_t *lcd); static epd_ret_t lcd_st7789_sleep(lcd_st7789_t *lcd, bool sleep_mode); static epd_ret_t lcd_st7789_panel_config(lcd_st7789_t *lcd, const st7789_panel_config_t *config); static epd_ret_t lcd_st7789_set_window(lcd_st7789_t *lcd, epd_coord_t *coord); epd_ret_t lcd_st7789_init(lcd_st7789_t *lcd, const st7789_panel_config_t *config) { EPD_ERROR_CHECK(lcd_st7789_reset(lcd)); EPD_ERROR_CHECK(lcd->cb.delay_cb(lcd->user_data, 5)); EPD_ERROR_CHECK(lcd_st7789_sleep(lcd, false)); EPD_ERROR_CHECK(lcd->cb.delay_cb(lcd->user_data, 100)); EPD_ERROR_CHECK(lcd_st7789_panel_config(lcd, config)); EPD_ERROR_CHECK(lcd_st7789_set_pixel_format(lcd, LCD_ST7789_RGB565)); EPD_ERROR_CHECK(lcd_st7789_set_direction(lcd, LCD_ST7789_DIR_0)); EPD_ERROR_CHECK(lcd_st7789_set_inversion(lcd, false)); if (lcd->cb.backlight_cb) { EPD_ERROR_CHECK(lcd->cb.backlight_cb(lcd, 1)); } return EPD_OK; } epd_ret_t lcd_st7789_load(lcd_st7789_t *lcd, epd_coord_t *coord, const uint8_t *data) { uint32_t pixel_count = (coord->y_end - coord->y_start + 1) * (coord->x_end - coord->x_start + 1); uint32_t data_len = 0; switch (lcd->pixel_format) { case LCD_ST7789_RGB444: data_len = pixel_count * 3 / 2; break; case LCD_ST7789_RGB565: data_len = pixel_count * 2; break; case LCD_ST7789_RGB666: case LCD_ST7789_RGB888: data_len = pixel_count * 3; break; default: data_len = pixel_count; break; } // Set cursor EPD_ERROR_CHECK(lcd_st7789_set_window(lcd, coord)); uint8_t command = ST7789_CMD_RAMWR; // Memory Write EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, &command, 0x01)); // Write pixel data EPD_ERROR_CHECK(lcd->cb.write_data_cb(lcd->user_data, data, data_len)); return EPD_OK; } epd_ret_t lcd_st7789_set_pixel_format(lcd_st7789_t *lcd, lcd_st7789_pixel_format_t format) { lcd->pixel_format = format; uint8_t command[2] = {ST7789_CMD_COLMOD, format}; EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, command, 0x02)); return EPD_OK; } epd_ret_t lcd_st7789_set_direction(lcd_st7789_t *lcd, lcd_st7789_direction_t direction) { lcd->direction = direction; uint8_t command[2] = {ST7789_CMD_MADCTL, direction}; if (!lcd->panel_config->bgr_filter) { command[1] &= ~0x08U; } EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, command, 0x02)); return EPD_OK; } epd_ret_t lcd_st7789_set_inversion(lcd_st7789_t *lcd, bool invert) { uint8_t command[1]; if (lcd->panel_config->inversion) { command[0] = invert ? ST7789_CMD_INVOFF : ST7789_CMD_INVON; } else { command[0] = invert ? ST7789_CMD_INVON : ST7789_CMD_INVOFF; } EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, command, 0x01)); return EPD_OK; } epd_ret_t lcd_st7789_enable_display(lcd_st7789_t *lcd, bool on) { uint8_t command[1]; if (on) { command[0] = ST7789_CMD_DISPON; } else { command[0] = ST7789_CMD_DISPOFF; } EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, command, 0x01)); return EPD_OK; } static epd_ret_t lcd_st7789_reset(lcd_st7789_t *lcd) { uint8_t cmd_buf[1] = {ST7789_CMD_SWRESET}; /* If hardware reset is implemented... */ if (lcd->cb.reset_cb) { return lcd->cb.reset_cb(lcd->user_data); } else { return lcd->cb.write_command_cb(lcd->user_data, cmd_buf, 1U); } } static epd_ret_t lcd_st7789_sleep(lcd_st7789_t *lcd, bool sleep_mode) { uint8_t cmd_buf[1] = {ST7789_CMD_SLPOUT}; if (sleep_mode) { cmd_buf[0] = ST7789_CMD_SLPIN; } return lcd->cb.write_command_cb(lcd->user_data, cmd_buf, 1U); } static epd_ret_t lcd_st7789_panel_config(lcd_st7789_t *lcd, const st7789_panel_config_t *config) { uint32_t idx = 0U; while (idx < config->init_struct_length) { uint8_t *cmd_ptr = &config->init_struct[idx + 1]; uint8_t cmd_len = config->init_struct[idx] + 1; EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, cmd_ptr, cmd_len)); idx += config->init_struct[idx] + 2; } lcd->panel_config = config; return EPD_OK; } static epd_ret_t lcd_st7789_set_window(lcd_st7789_t *lcd, epd_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->direction) { case LCD_ST7789_DIR_0: x_offset = lcd->panel_config->ram_offset_x; y_offset = lcd->panel_config->ram_offset_y; break; case LCD_ST7789_DIR_90: x_offset = lcd->panel_config->ram_offset_y; y_offset = lcd->panel_config->ram_offset_x; break; case LCD_ST7789_DIR_180: x_offset = lcd->panel_config->ram_size_x - (lcd->panel_config->ram_offset_x + lcd->panel_config->size_x); y_offset = lcd->panel_config->ram_size_y - (lcd->panel_config->ram_offset_y + lcd->panel_config->size_y); break; case LCD_ST7789_DIR_270: x_offset = lcd->panel_config->ram_size_y - (lcd->panel_config->ram_offset_y + lcd->panel_config->size_y); y_offset = lcd->panel_config->ram_size_x - (lcd->panel_config->ram_offset_x + lcd->panel_config->size_x); 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] = { ST7789_CMD_CASET, ((uint8_t)(real_x_start >> 0x08U) & 0xFFU), (real_x_start & 0xFFU), ((uint8_t)(real_x_end >> 0x08U) & 0xFFU), (real_x_end & 0xFFU), }; EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05)); tx_buf[0] = ST7789_CMD_RASET; 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); EPD_ERROR_CHECK(lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05)); return EPD_OK; }