From 5739cc831b775568fce04c715c70f8f5924fecea Mon Sep 17 00:00:00 2001 From: imi415 Date: Sat, 3 Sep 2022 16:48:46 +0800 Subject: [PATCH] Added ZJY 0.96" IPS LCD. --- CMakeLists.txt | 1 + include/epd-spi/panel/lcd_zjy096s0800.h | 40 ++++++ include/epd-spi/panel/lcd_zjy350c4001.h | 23 ++-- src/panel/lcd_zjy096s0800.c | 173 ++++++++++++++++++++++++ src/panel/lcd_zjy350c4001.c | 21 ++- 5 files changed, 241 insertions(+), 17 deletions(-) create mode 100644 include/epd-spi/panel/lcd_zjy096s0800.h create mode 100644 src/panel/lcd_zjy096s0800.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a3944a..e4015af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ set(EPD_SOURCES "src/panel/epd_gdew042t2.c" "src/panel/lcd_jlx256128g_920.c" "src/panel/lcd_zjy350c4001.c" + "src/panel/lcd_zjy096s0800.c" "src/panel/oled_elw1501aa.c" "src/panel/oled_zjy150s700.c" ) diff --git a/include/epd-spi/panel/lcd_zjy096s0800.h b/include/epd-spi/panel/lcd_zjy096s0800.h new file mode 100644 index 0000000..fa7c106 --- /dev/null +++ b/include/epd-spi/panel/lcd_zjy096s0800.h @@ -0,0 +1,40 @@ +#ifndef LCD_ZJY096S0800_H +#define LCD_ZJY096S0800_H + +#include "epd-spi/epd_common.h" + +typedef enum { + LCD_ZJY096S0800_DIR_0 = 0x08U, + LCD_ZJY096S0800_DIR_90 = 0x68U, + LCD_ZJY096S0800_DIR_180 = 0xC8U, + LCD_ZJY096S0800_DIR_270 = 0xA8U, +} lcd_zjy096s0800_direction_t; + +typedef enum { + LCD_ZJY096S0800_RGB444 = 3, + LCD_ZJY096S0800_RGB565 = 5, + LCD_ZJY096S0800_RGB666 = 6, + LCD_ZJY096S0800_RGB888 = 7, +} lcd_zjy096s0800_pixfmt_t; + +typedef struct { + lcd_zjy096s0800_direction_t direction; + lcd_zjy096s0800_pixfmt_t pix_fmt; + uint8_t inversion; + uint8_t bgr_mode; + uint8_t mirrored; +} lcd_zjy096s0800_config_t; + +typedef struct { + epd_cb_t cb; + void *user_data; + lcd_zjy096s0800_config_t config; +} lcd_zjy096s0800_t; + +epd_ret_t lcd_zjy096s0800_init(lcd_zjy096s0800_t *lcd); +epd_ret_t lcd_zjy096s0800_upload(lcd_zjy096s0800_t *lcd, uint8_t *data, epd_coord_t *coord); +epd_ret_t lcd_zjy096s0800_sleep(lcd_zjy096s0800_t *lcd, uint8_t sleep_mode); +epd_ret_t lcd_zjy096s0800_display(lcd_zjy096s0800_t *lcd, uint8_t display_on); +epd_ret_t lcd_zjy096s0800_config(lcd_zjy096s0800_t *lcd, lcd_zjy096s0800_config_t *config); + +#endif \ No newline at end of file diff --git a/include/epd-spi/panel/lcd_zjy350c4001.h b/include/epd-spi/panel/lcd_zjy350c4001.h index bbb0ab1..39c7c99 100644 --- a/include/epd-spi/panel/lcd_zjy350c4001.h +++ b/include/epd-spi/panel/lcd_zjy350c4001.h @@ -8,32 +8,33 @@ typedef enum { LCD_ZJY350C4001_DIR_90 = 0x68U, LCD_ZJY350C4001_DIR_180 = 0xC8U, LCD_ZJY350C4001_DIR_270 = 0xA8U, -} LCD_ZJY350C4001_direction_t; +} lcd_zjy350c4001_direction_t; typedef enum { LCD_ZJY350C4001_RGB444 = 3, LCD_ZJY350C4001_RGB565 = 5, LCD_ZJY350C4001_RGB666 = 6, LCD_ZJY350C4001_RGB888 = 7, -} LCD_ZJY350C4001_pixfmt_t; +} lcd_zjy350c4001_pixfmt_t; typedef struct { - LCD_ZJY350C4001_direction_t direction; - LCD_ZJY350C4001_pixfmt_t pix_fmt; - uint8_t inversion; - uint8_t bgr_mode; -} LCD_ZJY350C4001_config_t; + lcd_zjy350c4001_direction_t direction; + lcd_zjy350c4001_pixfmt_t pix_fmt; + uint8_t inversion; + uint8_t bgr_mode; + uint8_t mirrored; +} lcd_zjy350c4001_config_t; typedef struct { - epd_cb_t cb; - void *user_data; - LCD_ZJY350C4001_config_t config; + epd_cb_t cb; + void *user_data; + lcd_zjy350c4001_config_t config; } lcd_zjy350c4001_t; epd_ret_t lcd_zjy350c4001_init(lcd_zjy350c4001_t *lcd); epd_ret_t lcd_zjy350c4001_upload(lcd_zjy350c4001_t *lcd, uint8_t *data, epd_coord_t *coord); epd_ret_t lcd_zjy350c4001_sleep(lcd_zjy350c4001_t *lcd, uint8_t sleep_mode); epd_ret_t lcd_zjy350c4001_display(lcd_zjy350c4001_t *lcd, uint8_t display_on); -epd_ret_t lcd_zjy350c4001_config(lcd_zjy350c4001_t *lcd, LCD_ZJY350C4001_config_t *config); +epd_ret_t lcd_zjy350c4001_config(lcd_zjy350c4001_t *lcd, lcd_zjy350c4001_config_t *config); #endif \ No newline at end of file diff --git a/src/panel/lcd_zjy096s0800.c b/src/panel/lcd_zjy096s0800.c new file mode 100644 index 0000000..95d709b --- /dev/null +++ b/src/panel/lcd_zjy096s0800.c @@ -0,0 +1,173 @@ +#include "panel/lcd_zjy096s0800.h" + +#include + +#include "epd_private.h" + +static uint8_t lcd_zjy096s0800_init_seq[] = { + 0x01, 0xF0, 0xC3, // Enable command part 1 + 0x01, 0xF0, 0x96, // Enable command part 2 + 0x08, 0xE8, 0x40, 0x82, 0x07, 0x18, 0x27, 0x0A, 0xB6, 0x33, // DOCA + 0x01, 0xC5, 0x27, // VCOM control + 0x01, 0xC2, 0xA7, // Power control 3 + 0x0E, 0xE0, 0xF0, 0x01, 0x06, 0x0F, 0x12, 0x1D, 0x36, 0x54, 0x44, 0x0C, 0x18, 0x16, 0x13, 0x15, // PGC + 0x0E, 0xE1, 0xF0, 0x01, 0x05, 0x0A, 0x0B, 0x07, 0x32, 0x44, 0x44, 0x0C, 0x18, 0x17, 0x13, 0x16, // NGC + 0x01, 0xF0, 0x3C, // Disable command part 1 + 0x01, 0xF0, 0x69, // Disable command part 2 +}; + +epd_ret_t lcd_zjy096s0800_window(lcd_zjy096s0800_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->config.direction) { + case LCD_ZJY096S0800_DIR_90: + x_offset = 0; + y_offset = 0; + break; + case LCD_ZJY096S0800_DIR_180: + x_offset = 320; + y_offset = 480; + break; + case LCD_ZJY096S0800_DIR_270: + x_offset = 480; + y_offset = 320; + break; + case LCD_ZJY096S0800_DIR_0: + 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)}; + + if (lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05) != EPD_OK) { + return EPD_FAIL; + } + + tx_buf[0] = 0x2B; + 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); + + if (lcd->cb.write_command_cb(lcd->user_data, tx_buf, 0x05) != EPD_OK) { + return EPD_FAIL; + } + + return EPD_OK; +} + +epd_ret_t lcd_zjy096s0800_reset(lcd_zjy096s0800_t *lcd) { return lcd->cb.reset_cb(lcd->user_data); } + +epd_ret_t lcd_zjy096s0800_init(lcd_zjy096s0800_t *lcd) { + if (lcd_zjy096s0800_reset(lcd) != EPD_OK) return EPD_FAIL; + if (epd_common_execute_sequence(&lcd->cb, lcd->user_data, lcd_zjy096s0800_init_seq, + sizeof(lcd_zjy096s0800_init_seq)) != EPD_OK) + return EPD_FAIL; + if (lcd_zjy096s0800_config(lcd, &lcd->config) != EPD_OK) return EPD_FAIL; + if (lcd_zjy096s0800_sleep(lcd, 0) != EPD_OK) return EPD_FAIL; + if (lcd_zjy096s0800_display(lcd, 1) != EPD_OK) return EPD_FAIL; + + return EPD_OK; +} + +epd_ret_t lcd_zjy096s0800_upload(lcd_zjy096s0800_t *lcd, uint8_t *data, epd_coord_t *coord) { + uint32_t pixel_count = (coord->y_end - coord->y_start + 1) * (coord->x_end - coord->x_start + 1); + + uint32_t data_len; + + switch (lcd->config.pix_fmt) { + case LCD_ZJY096S0800_RGB444: + data_len = pixel_count * 3 / 2; + break; + case LCD_ZJY096S0800_RGB565: + data_len = pixel_count * 2; + break; + case LCD_ZJY096S0800_RGB666: + case LCD_ZJY096S0800_RGB888: + data_len = pixel_count * 3; + break; + default: + data_len = pixel_count; + break; + } + + // Set cursor + if (lcd_zjy096s0800_window(lcd, coord) != EPD_OK) { + return EPD_FAIL; + } + + uint8_t command = 0x2C; // Memory Write + if (lcd->cb.write_command_cb(lcd->user_data, &command, 0x01) != EPD_OK) { + return EPD_FAIL; + } + + // Write pixel data + if (lcd->cb.write_data_cb(lcd->user_data, data, data_len) != EPD_OK) { + return EPD_FAIL; + } + + return EPD_OK; +} + +epd_ret_t lcd_zjy096s0800_sleep(lcd_zjy096s0800_t *lcd, uint8_t sleep_mode) { + // Write SLPIN or SLPOUT command. + uint8_t command = sleep_mode ? 0x10 : 0x11; + return lcd->cb.write_command_cb(lcd->user_data, &command, 0x01); +} + +epd_ret_t lcd_zjy096s0800_display(lcd_zjy096s0800_t *lcd, uint8_t display_on) { + // write display_on command; + uint8_t command = display_on ? 0x29 : 0x28; + if (lcd->cb.write_command_cb(lcd->user_data, &command, 0x01) != EPD_OK) { + return EPD_FAIL; + } + + if ((lcd->cb.backlight_cb != NULL) && (lcd->cb.backlight_cb(lcd->user_data, display_on) != EPD_OK)) { + return EPD_FAIL; + } + + return EPD_OK; +} + +epd_ret_t lcd_zjy096s0800_config(lcd_zjy096s0800_t *lcd, lcd_zjy096s0800_config_t *config) { + lcd->config.direction = config->direction; + + // Write inversion command. + uint8_t command[2] = {config->inversion ? 0x20 : 0x21, 0x00}; + if (lcd->cb.write_command_cb(lcd->user_data, command, 0x01) != EPD_OK) { + return EPD_FAIL; + } + lcd->config.inversion = config->inversion; + + command[0] = 0x3A; + command[1] = config->pix_fmt; + if (lcd->cb.write_command_cb(lcd->user_data, command, 0x02) != EPD_OK) { + return EPD_FAIL; + } + lcd->config.pix_fmt = config->pix_fmt; + + command[0] = 0x36; + command[1] = config->direction; + + if (!config->bgr_mode) { + command[1] &= ~0x08U; + } + + if (config->mirrored) { + if (config->direction == LCD_ZJY096S0800_DIR_90 || config->direction == LCD_ZJY096S0800_DIR_270) { + command[1] ^= 0x80U; + } else { + command[1] ^= 0x40U; + } + } + + return lcd->cb.write_command_cb(lcd->user_data, command, 0x02); +} \ No newline at end of file diff --git a/src/panel/lcd_zjy350c4001.c b/src/panel/lcd_zjy350c4001.c index 47fe2bf..deac930 100644 --- a/src/panel/lcd_zjy350c4001.c +++ b/src/panel/lcd_zjy350c4001.c @@ -21,10 +21,6 @@ epd_ret_t lcd_zjy350c4001_window(lcd_zjy350c4001_t *lcd, epd_coord_t *coord) { uint16_t x_offset, y_offset; switch (lcd->config.direction) { - case LCD_ZJY350C4001_DIR_0: - x_offset = 0; - y_offset = 0; - break; case LCD_ZJY350C4001_DIR_90: x_offset = 0; y_offset = 0; @@ -37,6 +33,7 @@ epd_ret_t lcd_zjy350c4001_window(lcd_zjy350c4001_t *lcd, epd_coord_t *coord) { x_offset = 480; y_offset = 320; break; + case LCD_ZJY350C4001_DIR_0: default: x_offset = 0; y_offset = 0; @@ -71,7 +68,9 @@ epd_ret_t lcd_zjy350c4001_reset(lcd_zjy350c4001_t *lcd) { return lcd->cb.reset_c epd_ret_t lcd_zjy350c4001_init(lcd_zjy350c4001_t *lcd) { if (lcd_zjy350c4001_reset(lcd) != EPD_OK) return EPD_FAIL; - if(epd_common_execute_sequence(&lcd->cb, lcd->user_data, lcd_zjy350c4001_init_seq_zjy_480_320, sizeof(lcd_zjy350c4001_init_seq_zjy_480_320)) != EPD_OK) return EPD_FAIL; + if (epd_common_execute_sequence(&lcd->cb, lcd->user_data, lcd_zjy350c4001_init_seq_zjy_480_320, + sizeof(lcd_zjy350c4001_init_seq_zjy_480_320)) != EPD_OK) + return EPD_FAIL; if (lcd_zjy350c4001_config(lcd, &lcd->config) != EPD_OK) return EPD_FAIL; if (lcd_zjy350c4001_sleep(lcd, 0) != EPD_OK) return EPD_FAIL; if (lcd_zjy350c4001_display(lcd, 1) != EPD_OK) return EPD_FAIL; @@ -138,7 +137,7 @@ epd_ret_t lcd_zjy350c4001_display(lcd_zjy350c4001_t *lcd, uint8_t display_on) { return EPD_OK; } -epd_ret_t lcd_zjy350c4001_config(lcd_zjy350c4001_t *lcd, LCD_ZJY350C4001_config_t *config) { +epd_ret_t lcd_zjy350c4001_config(lcd_zjy350c4001_t *lcd, lcd_zjy350c4001_config_t *config) { lcd->config.direction = config->direction; // Write inversion command. @@ -157,8 +156,18 @@ epd_ret_t lcd_zjy350c4001_config(lcd_zjy350c4001_t *lcd, LCD_ZJY350C4001_config_ command[0] = 0x36; command[1] = config->direction; + if (!config->bgr_mode) { command[1] &= ~0x08U; } + + if (config->mirrored) { + if (config->direction == LCD_ZJY350C4001_DIR_90 || config->direction == LCD_ZJY350C4001_DIR_270) { + command[1] ^= 0x80U; + } else { + command[1] ^= 0x40U; + } + } + return lcd->cb.write_command_cb(lcd->user_data, command, 0x02); } \ No newline at end of file