From e08d953fcb0f8483f72c7b232494d421c22f2447 Mon Sep 17 00:00:00 2001 From: imi415 Date: Fri, 5 Nov 2021 02:28:27 +0800 Subject: [PATCH] Added GDEW042T2. --- CMakeLists.txt | 3 +- include/epd_common.h | 4 +- include/epd_panel_gdew042t2.h | 26 +++ include/epd_panel_gdew042t2_lut.h | 185 +++++++++++++++ ..._wfh0420cz35.h => epd_panel_wfh0420cz35.h} | 5 + include/epd_private.h | 3 + src/epd_common.c | 2 +- src/epd_panel_gdew042t2.c | 220 ++++++++++++++++++ ..._wfh0420cz35.c => epd_panel_wfh0420cz35.c} | 45 +++- 9 files changed, 486 insertions(+), 7 deletions(-) create mode 100644 include/epd_panel_gdew042t2.h create mode 100644 include/epd_panel_gdew042t2_lut.h rename include/{epd_wfh0420cz35.h => epd_panel_wfh0420cz35.h} (71%) create mode 100644 src/epd_panel_gdew042t2.c rename src/{epd_wfh0420cz35.c => epd_panel_wfh0420cz35.c} (70%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0065f6b..668462f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,8 @@ project(epd-spi) set(EPD_SOURCES "src/epd_common.c" - "src/epd_wfh0420cz35.c" + "src/epd_panel_wfh0420cz35.c" + "src/epd_panel_gdew042t2.c" ) set(EPD_INCLUDES diff --git a/include/epd_common.h b/include/epd_common.h index 44b28e8..b52aaf9 100644 --- a/include/epd_common.h +++ b/include/epd_common.h @@ -3,6 +3,7 @@ #include #include +#include typedef enum { EPD_OK = 0, EPD_FAIL } epd_ret_t; @@ -21,7 +22,4 @@ typedef struct { uint32_t y_end; } epd_coord_t; -#define EPD_ASSERT(x) if(!(x)) for(;;) { /* ABORT. */} -#define EPD_ERROR_CHECK(x) if(x != EPD_OK) return EPD_FAIL - #endif \ No newline at end of file diff --git a/include/epd_panel_gdew042t2.h b/include/epd_panel_gdew042t2.h new file mode 100644 index 0000000..7159a2f --- /dev/null +++ b/include/epd_panel_gdew042t2.h @@ -0,0 +1,26 @@ +#ifndef EPD_GDEW042T2_H +#define EPD_GDEW042T2_H + +#include "epd_common.h" + +/** + * @brief This display module has UC8176 controller IC, + * with BW partial or 4 grayscale LUT. + * + */ + +typedef enum { + EPD_GDEW042T2_MODE_GS = 0U, /* Grayscale mode */ + EPD_GDEW042T2_MODE_BW = 1U, /* B/W mode */ + EPD_GDEW042T2_MODE_BW_PART = 2U, /* B/W mode with partial flush */ +} epd_gdew042t2_mode_t; + +typedef struct { + void *user_data; + epd_gdew042t2_mode_t mode; + epd_cb_t cb; +} epd_gdew042t2_t; + +epd_ret_t epd_gdew042t2_upload(epd_gdew042t2_t *epd, epd_coord_t *coord, uint8_t *data1, uint8_t *data2); + +#endif \ No newline at end of file diff --git a/include/epd_panel_gdew042t2_lut.h b/include/epd_panel_gdew042t2_lut.h new file mode 100644 index 0000000..b2bc53b --- /dev/null +++ b/include/epd_panel_gdew042t2_lut.h @@ -0,0 +1,185 @@ +#ifndef EPD_GDEW042T2_LUT_H +#define EPD_GDEW042T2_LUT_H + +#include + +/** + * @brief Note for LUTs: + * LUTs are stored in arrays at the following order: + * VCOM WW BW WB BB WW(??), + * VCOM LUT has 44 bytes, while others has 42 bytes. + * + */ + +typedef struct { + uint8_t VCOM[44]; + uint8_t WW[42]; + uint8_t BW[42]; + uint8_t WB[42]; + uint8_t BB[42]; +} gdew042t2_lut_t; + +// LUT waveforms +static gdew042t2_lut_t gdew042t2_lut[3] = { + { + // Grayscale + .VCOM = + { + 0x00, 0x0A, 0x00, 0x00, 0x00, 0x01, // + 0x60, 0x14, 0x14, 0x00, 0x00, 0x01, // + 0x00, 0x14, 0x00, 0x00, 0x00, 0x01, // + 0x00, 0x13, 0x0A, 0x01, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WW = + { + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, // + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, // + 0x10, 0x14, 0x0A, 0x00, 0x00, 0x01, // + 0xA0, 0x13, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BW = + { + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, // + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, // + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, // + 0x99, 0x0C, 0x01, 0x03, 0x04, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WB = + { + 0x40, 0x0A, 0x00, 0x00, 0x00, 0x01, // + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, // + 0x00, 0x14, 0x0A, 0x00, 0x00, 0x01, // + 0x99, 0x0B, 0x04, 0x04, 0x01, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BB = + { + 0x80, 0x0A, 0x00, 0x00, 0x00, 0x01, // + 0x90, 0x14, 0x14, 0x00, 0x00, 0x01, // + 0x20, 0x14, 0x0A, 0x00, 0x00, 0x01, // + 0x50, 0x13, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + }, + { + // BW + .VCOM = + { + 0x00, 0x17, 0x00, 0x00, 0x00, 0x02, // + 0x00, 0x17, 0x17, 0x00, 0x00, 0x02, // + 0x00, 0x0A, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x0E, 0x0E, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WW = + { + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, // + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, // + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, // + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BW = + { + 0x40, 0x17, 0x00, 0x00, 0x00, 0x02, // + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, // + 0x40, 0x0A, 0x01, 0x00, 0x00, 0x01, // + 0xA0, 0x0E, 0x0E, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WB = + { + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, // + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, // + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, // + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BB = + { + 0x80, 0x17, 0x00, 0x00, 0x00, 0x02, // + 0x90, 0x17, 0x17, 0x00, 0x00, 0x02, // + 0x80, 0x0A, 0x01, 0x00, 0x00, 0x01, // + 0x50, 0x0E, 0x0E, 0x00, 0x00, 0x02, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + }, + { + // BW Partial + .VCOM = + { + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WW = + { + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BW = + { + 0x80, 0x19, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .WB = + { + 0x40, 0x19, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + .BB = + { + 0x00, 0x19, 0x01, 0x00, 0x00, 0x01, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // + }, + }, +}; + +#endif \ No newline at end of file diff --git a/include/epd_wfh0420cz35.h b/include/epd_panel_wfh0420cz35.h similarity index 71% rename from include/epd_wfh0420cz35.h rename to include/epd_panel_wfh0420cz35.h index 82abfca..9543aa6 100644 --- a/include/epd_wfh0420cz35.h +++ b/include/epd_panel_wfh0420cz35.h @@ -3,6 +3,11 @@ #include "epd_common.h" +/** + * @brief This display module has UC8176 controller IC, with BWR LUT stored in OTP. + * + */ + typedef struct { void *user_data; epd_cb_t cb; diff --git a/include/epd_private.h b/include/epd_private.h index 54188ca..d843ab2 100644 --- a/include/epd_private.h +++ b/include/epd_private.h @@ -3,6 +3,9 @@ #include "epd_common.h" +#define EPD_ASSERT(x) if(!(x)) for(;;) { /* ABORT. */} +#define EPD_ERROR_CHECK(x) if(x != EPD_OK) return EPD_FAIL + epd_ret_t epd_common_execute_sequence(epd_cb_t *cb, void *user_data, uint8_t *seq, uint32_t seq_len); #endif \ No newline at end of file diff --git a/src/epd_common.c b/src/epd_common.c index fcad200..b1a54a4 100644 --- a/src/epd_common.c +++ b/src/epd_common.c @@ -1,4 +1,4 @@ -#include "epd_common.h" +#include "epd_private.h" /** * @brief Execute command sequence. diff --git a/src/epd_panel_gdew042t2.c b/src/epd_panel_gdew042t2.c new file mode 100644 index 0000000..6d19b9e --- /dev/null +++ b/src/epd_panel_gdew042t2.c @@ -0,0 +1,220 @@ +#include "epd_panel_gdew042t2.h" + +#include "epd_panel_gdew042t2_lut.h" +#include "epd_private.h" + +#define DTM1 0x10 +#define DTM2 0x13 + +static uint8_t epd_gdew042t2_init_sequence_bw[] = { + 0x04, 0x01, 0x03, 0x00, 0x2B, 0x2B, // PWR setting + 0x03, 0x06, 0x17, 0x17, 0x17, // Boost +}; + +static uint8_t epd_gdew042t2_init_sequence_gs[] = { + 0x05, 0x01, 0x03, 0x00, 0x2B, 0x2B, 0x13, // PWR setting + 0x03, 0x06, 0x17, 0x17, 0x17, // Boost +}; + +static uint8_t epd_gdew042t2_init_sequence_bw_2[] = { + 0x02, 0x00, 0xBF, 0x0D, // Panel settings + 0x01, 0x30, 0x3C, // PLL + 0x04, 0x61, 0x01, 0x90, 0x01, 0x2C, // Resolution + 0x01, 0x82, 0x28, // VCOM DC + 0x01, 0x50, 0x97, // VCOM/Data Interval +}; + +static uint8_t epd_gdew042t2_init_sequence_gs_2[] = { + 0x01, 0x00, 0x3F, // Panel settings + 0x01, 0x30, 0x3C, // PLL + 0x04, 0x61, 0x01, 0x90, 0x01, 0x2C, // Resolution + 0x01, 0x82, 0x12, // VCOM DC + 0x01, 0x50, 0x97, // VCOM/Data Interval +}; + +static epd_ret_t epd_gdew042t2_write_lut(epd_gdew042t2_t *epd) { + uint8_t tx_buf[45]; + + tx_buf[0] = 0x20; // Write VCOM LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].VCOM, 44); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 45)); + + tx_buf[0] = 0x21; // Write WW LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].WW, 42); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 42)); + + tx_buf[0] = 0x22; // Write BW LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].BW, 42); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 42)); + + tx_buf[0] = 0x23; // Write WB LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].WB, 42); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 42)); + + tx_buf[0] = 0x24; // Write BB LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].BB, 42); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 42)); + + if (epd->mode == EPD_GDEW042T2_MODE_GS) { + tx_buf[0] = 0x25; // Write mysterious WW2 LUT + memcpy(&tx_buf[1], gdew042t2_lut[epd->mode].WW, 42); + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 42)); + } + + return EPD_OK; +} + +static epd_ret_t epd_gdew042t2_init(epd_gdew042t2_t *epd) { + EPD_ASSERT(epd); + EPD_ASSERT(epd->cb.reset_cb); + EPD_ASSERT(epd->cb.write_command_cb); + EPD_ASSERT(epd->cb.poll_busy_cb); + + EPD_ERROR_CHECK(epd->cb.reset_cb(epd->user_data)); + + // Pre power-on settings + if (epd->mode != EPD_GDEW042T2_MODE_GS) { + EPD_ERROR_CHECK(epd_common_execute_sequence(&epd->cb, epd->user_data, epd_gdew042t2_init_sequence_bw, + sizeof(epd_gdew042t2_init_sequence_bw))); + } else { + EPD_ERROR_CHECK(epd_common_execute_sequence(&epd->cb, epd->user_data, epd_gdew042t2_init_sequence_gs, + sizeof(epd_gdew042t2_init_sequence_gs))); + } + + // Issue power on command + uint8_t tx_buf[2] = {0x04, 0x00}; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01)); + + EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + + // Post power-on settings + if (epd->mode != EPD_GDEW042T2_MODE_GS) { + EPD_ERROR_CHECK(epd_common_execute_sequence(&epd->cb, epd->user_data, epd_gdew042t2_init_sequence_bw_2, + sizeof(epd_gdew042t2_init_sequence_bw_2))); + } else { + EPD_ERROR_CHECK(epd_common_execute_sequence(&epd->cb, epd->user_data, epd_gdew042t2_init_sequence_gs_2, + sizeof(epd_gdew042t2_init_sequence_gs_2))); + } + + return EPD_OK; +} + +/** + * @brief Enable partial mode for UC8176 IC + * and set partial mode window. + * + * @param epd pointer to epd_gdew042t2_t + * @param coord pointer to epd_coord_t + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ +static epd_ret_t epd_gdew042t2_partial(epd_gdew042t2_t *epd, epd_coord_t *coord) { + EPD_ASSERT(epd); + EPD_ASSERT(epd->cb.write_command_cb); + + // Partial In + uint8_t tx_buf[10] = {0x91, 0x00}; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 1)); + + // Partial window + tx_buf[0] = 0x90; + tx_buf[1] = (coord->x_start >> 8U) & 0x01U; + tx_buf[2] = coord->x_start & 0xF8U; // Bank number, last 3 LSBs must be zero. + tx_buf[3] = (coord->x_end >> 8U) & 0x01U; + tx_buf[4] = coord->x_end & 0xF8U; // Same as above. + tx_buf[5] = (coord->y_start >> 8U) & 0x01U; + tx_buf[6] = coord->y_start & 0xFFU; + tx_buf[7] = (coord->y_end >> 8U) & 0x01U; + tx_buf[8] = coord->y_end & 0xFFU; + tx_buf[9] = 0x01; // Only scan partial area. + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 10)); + + return EPD_OK; +} + +/** + * @brief Issue UC8176 DTM1 or DTM2 command, with payload data and stop command. + * + * @param epd pointer to epd_gdew042t2_t + * @param dtm_num DTM1 or DTM2 + * @param data payload pixel data + * @param len payload data length, calculated as bytes + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ +static epd_ret_t epd_gdew042t2_dtm(epd_gdew042t2_t *epd, uint8_t dtm_num, uint8_t *data, uint32_t len) { + EPD_ASSERT(epd); + EPD_ASSERT(epd->cb.write_command_cb); + + uint8_t tx_buf[2] = {dtm_num}; + + // Issue DTM1 or DTM2 command + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01)); + // Send data + EPD_ERROR_CHECK(epd->cb.write_data_cb(epd->user_data, data, len)); + // Issue DSP command. + tx_buf[0] = 0x11; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01)); + + return EPD_OK; +} + +/** + * @brief Issue display update command and enter deep-sleep mode. + * Deep-sleep mode can only exits with a hardware reset. + * @param epd pointer to epd_gdew042t2_t + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ +static epd_ret_t epd_gdew042t2_update_and_sleep(epd_gdew042t2_t *epd) { + EPD_ASSERT(epd); + EPD_ASSERT(epd->cb.write_command_cb); + EPD_ASSERT(epd->cb.delay_cb); + + // Issue DRF command + uint8_t tx_buf[2] = {0x12, 0x00}; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01)); + + // Delay 100ms and wait for busy + EPD_ERROR_CHECK(epd->cb.delay_cb(epd->user_data, 100)); + EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + + // Issue POF command + tx_buf[0] = 0x02; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x01)); + EPD_ERROR_CHECK(epd->cb.poll_busy_cb(epd->user_data)); + + // Issue DSLP command + tx_buf[0] = 0x07; + tx_buf[1] = 0xA5; + EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 0x02)); + + return EPD_OK; +} + +/** + * @brief Initialize display, upload frame buffer and refresh. + * Then goes to deep-sleep mode to conserve power. + * This is the only function needs to be called by the user. + * + * @param epd pointer to epd_gdew042t2_t + * @param coord pointer to epd_coord_t, x_start or x_end MUST be wrapped to 8 pixel boundaries. + * @param data1 bw data or old data array + * @param data2 bw data or new data array + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ +epd_ret_t epd_gdew042t2_upload(epd_gdew042t2_t *epd, epd_coord_t *coord, uint8_t *data1, uint8_t *data2) { + EPD_ERROR_CHECK(epd_gdew042t2_init(epd)); + EPD_ERROR_CHECK(epd_gdew042t2_write_lut(epd)); + EPD_ERROR_CHECK(epd_gdew042t2_partial(epd, coord)); + + uint8_t bank_start = coord->x_start / 8; // Wrap to bank + uint8_t bank_end = coord->x_end / 8; // Wrap to bank + uint32_t tx_len = (bank_end - bank_start + 1) * (coord->y_end - coord->y_start + 1); + + if (epd->mode != EPD_GDEW042T2_MODE_BW) { + EPD_ERROR_CHECK(epd_gdew042t2_dtm(epd, DTM1, data1, tx_len)); + } + EPD_ERROR_CHECK(epd_gdew042t2_dtm(epd, DTM2, data2, tx_len)); + + EPD_ERROR_CHECK(epd_gdew042t2_update_and_sleep(epd)); + + return EPD_OK; +} diff --git a/src/epd_wfh0420cz35.c b/src/epd_panel_wfh0420cz35.c similarity index 70% rename from src/epd_wfh0420cz35.c rename to src/epd_panel_wfh0420cz35.c index fc6bf39..7e6c0b3 100644 --- a/src/epd_wfh0420cz35.c +++ b/src/epd_panel_wfh0420cz35.c @@ -1,4 +1,4 @@ -#include "epd_wfh0420cz35.h" +#include "epd_panel_wfh0420cz35.h" #include "epd_private.h" @@ -9,6 +9,13 @@ static uint8_t epd_wfh0420_init_sequence[] = { 0x03, 0x06, 0x17, 0x17, 0x17, }; +/** + * @brief This function initialize the driver IC and + * enable internal power supplies. + * + * @param epd pointer to epd_wfh0420_t + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ static epd_ret_t epd_wfh0420_init(epd_wfh0420_t *epd) { EPD_ASSERT(epd); EPD_ASSERT(epd->cb.reset_cb); @@ -34,6 +41,14 @@ static epd_ret_t epd_wfh0420_init(epd_wfh0420_t *epd) { return EPD_OK; } +/** + * @brief Enable partial mode for UC8176 IC + * and set partial mode window. + * + * @param epd pointer to epd_wfh0420_t + * @param coord pointer to epd_coord_t + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ static epd_ret_t epd_wfh0420_partial(epd_wfh0420_t *epd, epd_coord_t *coord) { EPD_ASSERT(epd); EPD_ASSERT(epd->cb.write_command_cb); @@ -52,12 +67,21 @@ static epd_ret_t epd_wfh0420_partial(epd_wfh0420_t *epd, epd_coord_t *coord) { tx_buf[6] = coord->y_start & 0xFFU; tx_buf[7] = (coord->y_end >> 8U) & 0x01U; tx_buf[8] = coord->y_end & 0xFFU; - tx_buf[9] = 0x01; // Only scan partial area. + tx_buf[9] = 0x01; // Only scan partial area. EPD_ERROR_CHECK(epd->cb.write_command_cb(epd->user_data, tx_buf, 10)); return EPD_OK; } +/** + * @brief Issue UC8176 DTM1 or DTM2 command, with payload data and stop command. + * + * @param epd pointer to epd_wfh0420_t + * @param dtm_num DTM1 or DTM2 + * @param data payload pixel data + * @param len payload data length, calculated as bytes + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ static epd_ret_t epd_wfh0420_dtm(epd_wfh0420_t *epd, uint8_t dtm_num, uint8_t *data, uint32_t len) { EPD_ASSERT(epd); EPD_ASSERT(epd->cb.write_command_cb); @@ -75,6 +99,12 @@ static epd_ret_t epd_wfh0420_dtm(epd_wfh0420_t *epd, uint8_t dtm_num, uint8_t *d return EPD_OK; } +/** + * @brief Issue display update command and enter deep-sleep mode. + * Deep-sleep mode can only exits with a hardware reset. + * @param epd pointer to epd_wfh0420_t + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ static epd_ret_t epd_wfh0420_update_and_sleep(epd_wfh0420_t *epd) { EPD_ASSERT(epd); EPD_ASSERT(epd->cb.write_command_cb); @@ -101,6 +131,17 @@ static epd_ret_t epd_wfh0420_update_and_sleep(epd_wfh0420_t *epd) { return EPD_OK; } +/** + * @brief Initialize display, upload frame buffer and refresh. + * Then goes to deep-sleep mode to conserve power. + * This is the only function needs to be called by the user. + * + * @param epd pointer to epd_wfh0420_t + * @param coord pointer to epd_coord_t, x_start or x_end MUST be wrapped to 8 pixel boundaries. + * @param red_data red pixel data array, bit 0 for red, bit 1 for white + * @param bw_data black pixel data array, bit 0 for black, bit 1 for white + * @return epd_ret_t EPD_OK for success, EPD_FAIL for error. + */ epd_ret_t epd_wfh0420_upload(epd_wfh0420_t *epd, epd_coord_t *coord, uint8_t *red_data, uint8_t *bw_data) { EPD_ERROR_CHECK(epd_wfh0420_init(epd)); EPD_ERROR_CHECK(epd_wfh0420_partial(epd, coord));