diff --git a/.clang-format b/.clang-format index 3003f59..214adf0 100644 --- a/.clang-format +++ b/.clang-format @@ -1,8 +1,9 @@ BasedOnStyle: Google IndentWidth: 4 -AlignConsecutiveMacros: AcrossEmptyLines -AlignConsecutiveDeclarations: true -AlignConsecutiveAssignments: AcrossEmptyLinesAndComments +AlignConsecutiveMacros: Consecutive +AlignConsecutiveDeclarations: Consecutive +AlignConsecutiveAssignments: Consecutive +AllowShortFunctionsOnASingleLine: None BreakBeforeBraces: Custom BraceWrapping: AfterEnum: false diff --git a/CMakeLists.txt b/CMakeLists.txt index e4015af..1750d50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,8 +5,10 @@ project(epd-spi) set(EPD_SOURCES "src/epd_common.c" "src/driver/oled_ssd1327.c" + "src/driver/lcd_st7789.c" "src/panel/epd_wfh0420cz35.c" "src/panel/epd_gdew042t2.c" + "src/panel/lcd_hp32030d.c" "src/panel/lcd_jlx256128g_920.c" "src/panel/lcd_zjy350c4001.c" "src/panel/lcd_zjy096s0800.c" diff --git a/include/epd-spi/driver/lcd_st7789.h b/include/epd-spi/driver/lcd_st7789.h new file mode 100644 index 0000000..87df305 --- /dev/null +++ b/include/epd-spi/driver/lcd_st7789.h @@ -0,0 +1,49 @@ +#ifndef LCD_ST7789_H +#define LCD_ST7789_H + +#include "epd-spi/epd_common.h" + +typedef enum { + LCD_ST7789_DIR_0 = 0x08U, + LCD_ST7789_DIR_90 = 0x68U, + LCD_ST7789_DIR_180 = 0xC8U, + LCD_ST7789_DIR_270 = 0xA8U, +} lcd_st7789_direction_t; + +typedef enum { + LCD_ST7789_RGB444 = 3, + LCD_ST7789_RGB565 = 5, + LCD_ST7789_RGB666 = 6, + LCD_ST7789_RGB888 = 7, +} lcd_st7789_pixel_format_t; + +typedef struct { + uint8_t *init_struct; + uint32_t init_struct_length; + + uint16_t ram_offset_x; + uint16_t ram_offset_y; + + uint16_t size_x; + uint16_t size_y; + + bool inversion; + bool bgr_filter; +} st7789_panel_config_t; + +typedef struct { + void *user_data; + epd_cb_t cb; + + const st7789_panel_config_t *panel_config; + lcd_st7789_direction_t direction; + lcd_st7789_pixel_format_t pixel_format; +} lcd_st7789_t; + +epd_ret_t lcd_st7789_init(lcd_st7789_t *lcd, const st7789_panel_config_t *config); +epd_ret_t lcd_st7789_load(lcd_st7789_t *lcd, epd_coord_t *coord, const uint8_t *data); +epd_ret_t lcd_st7789_set_pixel_format(lcd_st7789_t *lcd, lcd_st7789_pixel_format_t format); +epd_ret_t lcd_st7789_set_direction(lcd_st7789_t *lcd, lcd_st7789_direction_t direction); +epd_ret_t lcd_st7789_set_inversion(lcd_st7789_t *lcd, bool invert); + +#endif // LCD_ST7789_H diff --git a/include/epd-spi/epd_common.h b/include/epd-spi/epd_common.h index 1c72baa..ac8a7f7 100644 --- a/include/epd-spi/epd_common.h +++ b/include/epd-spi/epd_common.h @@ -1,6 +1,7 @@ #ifndef EPD_COMMON_H #define EPD_COMMON_H +#include #include #include #include @@ -9,7 +10,7 @@ typedef enum { EPD_OK = 0, EPD_FAIL } epd_ret_t; typedef struct { epd_ret_t (*write_command_cb)(void *handle, uint8_t *command, uint32_t len); - epd_ret_t (*write_data_cb)(void *handle, uint8_t *data, uint32_t len); + epd_ret_t (*write_data_cb)(void *handle, const uint8_t *data, uint32_t len); epd_ret_t (*reset_cb)(void *handle); epd_ret_t (*poll_busy_cb)(void *handle); epd_ret_t (*delay_cb)(void *handle, uint32_t msec); diff --git a/include/epd-spi/panel/lcd_hp32030d.h b/include/epd-spi/panel/lcd_hp32030d.h new file mode 100644 index 0000000..4dbc268 --- /dev/null +++ b/include/epd-spi/panel/lcd_hp32030d.h @@ -0,0 +1,12 @@ +// +// Created by imi415 on 23/10/21. +// + +#ifndef LCD_HP32030D_H +#define LCD_HP32030D_H + +#include "epd-spi/driver/lcd_st7789.h" + +extern const st7789_panel_config_t lcd_hp32030d_panel_config; + +#endif // LCD_HP32030D_H diff --git a/src/driver/lcd_st7789.c b/src/driver/lcd_st7789.c new file mode 100644 index 0000000..b33751a --- /dev/null +++ b/src/driver/lcd_st7789.c @@ -0,0 +1,189 @@ +#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_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)); + + 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; +} + +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 = 240 - (lcd->panel_config->ram_offset_x + lcd->panel_config->size_x); + y_offset = 320 - (lcd->panel_config->ram_offset_y + lcd->panel_config->size_y); + break; + case LCD_ST7789_DIR_270: + x_offset = 320 - (lcd->panel_config->ram_offset_y + lcd->panel_config->size_y); + y_offset = 240 - (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; +} \ No newline at end of file diff --git a/src/panel/lcd_hp32030d.c b/src/panel/lcd_hp32030d.c new file mode 100644 index 0000000..614f24b --- /dev/null +++ b/src/panel/lcd_hp32030d.c @@ -0,0 +1,31 @@ +#include "panel/lcd_hp32030d.h" + +static uint8_t panel_init_struct[] = { + 0x05, 0xB2, 0x0C, 0x0C, 0x00, 0x33, 0x33, // PORCTRL(B2H) + 0x02, 0xB0, 0x00, 0xE0, // RAMCTRL (B0H) + 0x01, 0x36, 0x00, // MADCTL (36H) + 0x01, 0x3A, 0x05, // COLMOD (3AH) + 0x01, 0xB7, 0x56, // GCTRL (B7H) + 0x01, 0xBB, 0x14, // VCOMS (BBH) + 0x01, 0xC0, 0x2C, // LCMCTRL (C0H) + 0x01, 0xC2, 0x01, // VDVVRHEN (C2H) + 0x01, 0xC3, 0x0B, // VRHS (C3H) + 0x01, 0xC4, 0x10, // VDVS (C4H) + 0x01, 0xC6, 0x0F, // FRCTRL2 (C6H) + 0x02, 0xD0, 0xA4, 0xA1, // PWCTRL1 (D0H) + 0x01, 0xD5, 0xA1, // ?? + 0x0E, 0xE0, 0xD0, 0x08, 0x0A, 0x0D, 0x0B, 0x07, 0x21, 0x33, 0x39, 0x39, 0x16, 0x16, 0x1F, 0x3C, // PGC (E0H) + 0x0E, 0xE1, 0xD0, 0x00, 0x03, 0x01, 0x00, 0x10, 0x21, 0x32, 0x38, 0x16, 0x14, 0x14, 0x20, 0x3D, // NGC (E1H) + 0x00, 0x21, 0x00, 0x29, 0x00, 0x2C, +}; + +const st7789_panel_config_t lcd_hp32030d_panel_config = { + .init_struct = panel_init_struct, + .init_struct_length = sizeof(panel_init_struct), + .ram_offset_x = 0U, + .ram_offset_y = 0U, + .size_x = 240, + .size_y = 320, + .inversion = true, + .bgr_filter = false, +}; \ No newline at end of file