diff --git a/CMakeLists.txt b/CMakeLists.txt index d101d2ed..f9bab661 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,6 +29,7 @@ set(app_sources "src/epdiy.c" "src/board/epd_board_v6.c" "src/board/epd_board_v7.c" "src/board/epd_board_v7_raw.c" + "src/board/epd_board_v7_103.c" # Experimental board (not ready yet) ) diff --git a/examples/dragon/main/main.c b/examples/dragon/main/main.c index f5d9d453..e00ca6a6 100644 --- a/examples/dragon/main/main.c +++ b/examples/dragon/main/main.c @@ -47,4 +47,4 @@ void app_main() { idf_loop(); }; } -#endif +#endif \ No newline at end of file diff --git a/src/board/epd_board_v6.c b/src/board/epd_board_v6.c index f544d1a8..ebbb730d 100644 --- a/src/board/epd_board_v6.c +++ b/src/board/epd_board_v6.c @@ -196,6 +196,14 @@ static void epd_board_poweron(epd_ctrl_state_t* state) { state->ep_stv = true; config_reg.wakeup = true; epd_board_set_ctrl(state, &mask); + + // Check if DISPLAY_UPSEQ_MC2 is set + const EpdDisplay_t* display = epd_get_display(); + if (display->display_type & DISPLAY_UPSEQ_MC2) { + vTaskDelay(3); + tps_set_upseq_carta1300(); + } + config_reg.pwrup = true; epd_board_set_ctrl(state, &mask); config_reg.vcom_ctrl = true; diff --git a/src/board/epd_board_v7.c b/src/board/epd_board_v7.c index 1dd4414a..23e6715f 100644 --- a/src/board/epd_board_v7.c +++ b/src/board/epd_board_v7.c @@ -212,6 +212,14 @@ static void epd_board_poweron(epd_ctrl_state_t* state) { state->ep_output_enable = true; config_reg.wakeup = true; epd_board_set_ctrl(state, &mask); + + // Check if DISPLAY_UPSEQ_MC2 is set + const EpdDisplay_t* display = epd_get_display(); + if (display->display_type & DISPLAY_UPSEQ_MC2) { + vTaskDelay(3); + tps_set_upseq_carta1300(); + printf("Setting UPSEQ for DISPLAY_UPSEQ_MC2\n"); + } config_reg.pwrup = true; epd_board_set_ctrl(state, &mask); config_reg.vcom_ctrl = true; diff --git a/src/board/epd_board_v7_103.c b/src/board/epd_board_v7_103.c new file mode 100644 index 00000000..e9c2dbe2 --- /dev/null +++ b/src/board/epd_board_v7_103.c @@ -0,0 +1,353 @@ +#include +#include "epd_board.h" +#include "epdiy.h" + +#include "../output_common/render_method.h" +#include "../output_lcd/lcd_driver.h" +#include "esp_log.h" +#include "pca9555.h" +#include "tps65185.h" + +#include +#include +#include + +// Make this compile von the ESP32 without ifdefing the whole file +#ifndef CONFIG_IDF_TARGET_ESP32S3 +#define GPIO_NUM_40 -1 +#define GPIO_NUM_41 -1 +#define GPIO_NUM_42 -1 +#define GPIO_NUM_43 -1 +#define GPIO_NUM_44 -1 +#define GPIO_NUM_45 -1 +#define GPIO_NUM_46 -1 +#define GPIO_NUM_47 -1 +#define GPIO_NUM_48 -1 +#endif + +#define CFG_SCL GPIO_NUM_40 +#define CFG_SDA GPIO_NUM_39 +#define CFG_INTR GPIO_NUM_2 // NUM_2 is >1.1 NUM_38 is 1.0 +#define EPDIY_I2C_PORT I2C_NUM_0 + +#define CFG_PIN_OE (PCA_PIN_PC10 >> 8) +#define CFG_PIN_MODE (PCA_PIN_PC11 >> 8) +#define __CFG_PIN_STV (PCA_PIN_PC12 >> 8) +#define CFG_PIN_PWRUP (PCA_PIN_PC13 >> 8) +#define CFG_PIN_VCOM_CTRL (PCA_PIN_PC14 >> 8) +#define CFG_PIN_WAKEUP (PCA_PIN_PC15 >> 8) +#define CFG_PIN_PWRGOOD (PCA_PIN_PC16 >> 8) +#define CFG_PIN_INT (PCA_PIN_PC17 >> 8) + +#define D15 GPIO_NUM_5 +#define D14 GPIO_NUM_6 +#define D13 GPIO_NUM_7 +#define D12 GPIO_NUM_15 +#define D11 GPIO_NUM_16 +#define D10 GPIO_NUM_17 +#define D9 GPIO_NUM_18 +#define D8 GPIO_NUM_8 + +#define D7 GPIO_NUM_9 +#define D6 GPIO_NUM_10 +#define D5 GPIO_NUM_11 +#define D4 GPIO_NUM_12 +#define D3 GPIO_NUM_13 +#define D2 GPIO_NUM_14 +#define D1 GPIO_NUM_21 +#define D0 GPIO_NUM_47 + +/* Control Lines */ +#define CKV GPIO_NUM_42 +#define STH GPIO_NUM_45 +#define LEH GPIO_NUM_48 +#define STV GPIO_NUM_41 + +/* Edges */ +#define CKH GPIO_NUM_4 + +typedef struct { + i2c_port_t port; + bool pwrup; + bool vcom_ctrl; + bool wakeup; + bool others[8]; +} epd_config_register_t; + +/** The VCOM voltage to use. */ +static int vcom = 1600; + +static epd_config_register_t config_reg; + +static bool interrupt_done = false; + +static void IRAM_ATTR interrupt_handler(void* arg) { + interrupt_done = true; +} + +static lcd_bus_config_t lcd_config = { + .clock = CKH, + .ckv = CKV, + .leh = LEH, + .start_pulse = STH, + .stv = STV, + .data[0] = D0, + .data[1] = D1, + .data[2] = D2, + .data[3] = D3, + .data[4] = D4, + .data[5] = D5, + .data[6] = D6, + .data[7] = D7, + .data[8] = D8, + .data[9] = D9, + .data[10] = D10, + .data[11] = D11, + .data[12] = D12, + .data[13] = D13, + .data[14] = D14, + .data[15] = D15, +}; + +static void epd_board_init(uint32_t epd_row_width) { + gpio_hold_dis(CKH); // free CKH after wakeup + + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = CFG_SDA; + conf.scl_io_num = CFG_SCL; + conf.sda_pullup_en = GPIO_PULLUP_ENABLE; + conf.scl_pullup_en = GPIO_PULLUP_ENABLE; + conf.master.clk_speed = 100000; + conf.clk_flags = 0; + i2c_param_config(EPDIY_I2C_PORT, &conf); + + i2c_driver_install(EPDIY_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0); + + config_reg.port = EPDIY_I2C_PORT; + config_reg.pwrup = false; + config_reg.vcom_ctrl = false; + config_reg.wakeup = false; + for (int i = 0; i < 8; i++) { + config_reg.others[i] = false; + } + + gpio_set_direction(CFG_INTR, GPIO_MODE_INPUT); + gpio_set_intr_type(CFG_INTR, GPIO_INTR_NEGEDGE); + + ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_EDGE)); + + ESP_ERROR_CHECK(gpio_isr_handler_add(CFG_INTR, interrupt_handler, (void*)CFG_INTR)); + + // set all epdiy lines to output except TPS interrupt + PWR good + ESP_ERROR_CHECK(pca9555_set_config(config_reg.port, CFG_PIN_PWRGOOD | CFG_PIN_INT, 1)); + + const EpdDisplay_t* display = epd_get_display(); + + LcdEpdConfig_t config = { + .pixel_clock = display->bus_speed * 1000 * 1000, + .ckv_high_time = 60, + .line_front_porch = 4, + .le_high_time = 4, + .bus_width = display->bus_width, + .bus = lcd_config, + }; + epd_lcd_init(&config, display->width, display->height); +} + +static void epd_board_deinit() { + epd_lcd_deinit(); + + ESP_ERROR_CHECK(pca9555_set_config( + config_reg.port, CFG_PIN_PWRGOOD | CFG_PIN_INT | CFG_PIN_VCOM_CTRL | CFG_PIN_PWRUP, 1 + )); + + int tries = 0; + while (!((pca9555_read_input(config_reg.port, 1) & 0xC0) == 0x80)) { + if (tries >= 50) { + ESP_LOGE("epdiy", "failed to shut down TPS65185!"); + break; + } + tries++; + vTaskDelay(1); + } + + // Not sure why we need this delay, but the TPS65185 seems to generate an interrupt after some + // time that needs to be cleared. + vTaskDelay(50); + pca9555_read_input(config_reg.port, 0); + pca9555_read_input(config_reg.port, 1); + i2c_driver_delete(EPDIY_I2C_PORT); + + gpio_uninstall_isr_service(); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t* state, const epd_ctrl_state_t* const mask) { + uint8_t value = 0x00; + if (mask->ep_output_enable || mask->ep_mode || mask->ep_stv) { + if (state->ep_output_enable) + value |= CFG_PIN_OE; + if (state->ep_mode) + value |= CFG_PIN_MODE; + // if (state->ep_stv) value |= CFG_PIN_STV; + if (config_reg.pwrup) + value |= CFG_PIN_PWRUP; + if (config_reg.vcom_ctrl) + value |= CFG_PIN_VCOM_CTRL; + if (config_reg.wakeup) + value |= CFG_PIN_WAKEUP; + + ESP_ERROR_CHECK(pca9555_set_value(config_reg.port, value, 1)); + } +} + +static void epd_board_poweron(epd_ctrl_state_t* state) { + epd_ctrl_state_t mask = { + .ep_output_enable = true, + .ep_mode = true, + .ep_stv = true, + }; + state->ep_stv = true; + state->ep_mode = false; + state->ep_output_enable = true; + config_reg.wakeup = true; + epd_board_set_ctrl(state, &mask); + + // Check if DISPLAY_UPSEQ_MC2 is set + const EpdDisplay_t* display = epd_get_display(); + if (display->display_type & DISPLAY_UPSEQ_MC2) { + // Might need a bigger delay till TPS65185 fully wakes up + vTaskDelay(3); + tps_set_upseq_carta1300(); + printf("Setting UPSEQ for DISPLAY_UPSEQ_MC2\n"); + } + config_reg.pwrup = true; + epd_board_set_ctrl(state, &mask); + config_reg.vcom_ctrl = true; + epd_board_set_ctrl(state, &mask); + + // give the IC time to powerup and set lines + vTaskDelay(1); + int i = 0; + while (!(pca9555_read_input(config_reg.port, 1) & CFG_PIN_PWRGOOD)) { + vTaskDelay(1); + i++; + if (i == 10) { + printf("Timeout waiting for PWRGOOD\n"); + break; + } + } + + ESP_ERROR_CHECK(tps_write_register(config_reg.port, TPS_REG_ENABLE, 0x3F)); + + tps_set_vcom(config_reg.port, vcom); + + state->ep_sth = true; + mask = (const epd_ctrl_state_t){ + .ep_sth = true, + }; + epd_board_set_ctrl(state, &mask); + + int tries = 0; + while (!((tps_read_register(config_reg.port, TPS_REG_PG) & 0xFA) == 0xFA)) { + if (tries >= 500) { + ESP_LOGE( + "epdiy", + "Power enable failed! PG status: %X", + tps_read_register(config_reg.port, TPS_REG_PG) + ); + return; + } + tries++; + vTaskDelay(1); + } +} + +static void epd_board_measure_vcom(epd_ctrl_state_t* state) { + epd_ctrl_state_t mask = { + .ep_output_enable = true, + .ep_mode = true, + .ep_stv = true, + }; + state->ep_stv = true; + state->ep_mode = false; + state->ep_output_enable = true; + config_reg.wakeup = true; + epd_board_set_ctrl(state, &mask); + config_reg.pwrup = true; + epd_board_set_ctrl(state, &mask); + + // give the IC time to powerup and set lines + vTaskDelay(1); + state->ep_sth = true; + mask = (const epd_ctrl_state_t){ + .ep_sth = true, + }; + epd_board_set_ctrl(state, &mask); + + while (!(pca9555_read_input(config_reg.port, 1) & CFG_PIN_PWRGOOD)) { + } + ESP_LOGI("epdiy", "Power rails enabled"); + + state->ep_sth = true; + mask = (const epd_ctrl_state_t){ + .ep_sth = true, + }; + epd_board_set_ctrl(state, &mask); + + int tries = 0; + while (!((tps_read_register(config_reg.port, TPS_REG_PG) & 0xFA) == 0xFA)) { + if (tries >= 500) { + ESP_LOGE( + "epdiy", + "Power enable failed! PG status: %X", + tps_read_register(config_reg.port, TPS_REG_PG) + ); + return; + } + tries++; + vTaskDelay(1); + } +} + +static void epd_board_poweroff(epd_ctrl_state_t* state) { + epd_ctrl_state_t mask = { + .ep_stv = true, + .ep_output_enable = true, + .ep_mode = true, + }; + config_reg.vcom_ctrl = false; + config_reg.pwrup = false; + state->ep_stv = false; + state->ep_output_enable = false; + state->ep_mode = false; + epd_board_set_ctrl(state, &mask); + vTaskDelay(1); + config_reg.wakeup = false; + epd_board_set_ctrl(state, &mask); +} + +static float epd_board_ambient_temperature() { + return 20; +} + +static void set_vcom(int value) { + vcom = value; +} + +const EpdBoardDefinition epd_board_v7_103 = { + .init = epd_board_init, + .deinit = epd_board_deinit, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .measure_vcom = epd_board_measure_vcom, + .get_temperature = epd_board_ambient_temperature, + .set_vcom = set_vcom, + + // unimplemented for now, but shares v6 implementation + .gpio_set_direction = NULL, + .gpio_read = NULL, + .gpio_write = NULL, +}; diff --git a/src/board/tps65185.c b/src/board/tps65185.c index ef5cb3da..7b8ab526 100644 --- a/src/board/tps65185.c +++ b/src/board/tps65185.c @@ -125,4 +125,9 @@ unsigned tps_vcom_kickback_rdy() { } else { return 0; } +} + +void tps_set_upseq_carta1300() { + tps_write_register(I2C_NUM_0, TPS_REG_UPSEQ0, 0xE1); + tps_write_register(I2C_NUM_0, TPS_REG_UPSEQ1, 0xAA); } \ No newline at end of file diff --git a/src/board/tps65185.h b/src/board/tps65185.h index 96ba1d35..24181500 100644 --- a/src/board/tps65185.h +++ b/src/board/tps65185.h @@ -48,6 +48,10 @@ void tps_vcom_kickback_start(); */ unsigned tps_vcom_kickback_rdy(); +/** + * Sets a special power-up voltage sequence that is specific for Carta 1300 panels + */ +void tps_set_upseq_carta1300(); /** * Read the temperature via the on-board thermistor. */ diff --git a/src/displays.c b/src/displays.c index 40fcae1b..91cc62e1 100644 --- a/src/displays.c +++ b/src/displays.c @@ -81,3 +81,11 @@ const EpdDisplay_t ED052TC4 = { .default_waveform = &epdiy_ED097TC2, .display_type = DISPLAY_TYPE_HORIZONTAL_MIRRORED, }; +// Combined flags for display_type since this panel needs an UP SEQ init +const EpdDisplay_t ED103MC2 + = { .width = 1872, + .height = 1404, + .bus_width = 16, + .bus_speed = 11, + .default_waveform = &epdiy_ED097TC2, + .display_type = DISPLAY_TYPE_HORIZONTAL_MIRRORED | DISPLAY_UPSEQ_MC2 }; \ No newline at end of file diff --git a/src/epd_board.h b/src/epd_board.h index c5b1d29f..86c69d47 100644 --- a/src/epd_board.h +++ b/src/epd_board.h @@ -126,6 +126,7 @@ extern const EpdBoardDefinition epd_board_v5; extern const EpdBoardDefinition epd_board_v6; extern const EpdBoardDefinition epd_board_v7; extern const EpdBoardDefinition epd_board_v7_raw; +extern const EpdBoardDefinition epd_board_v7_103; // Experimental board (not ready yet) /** * Helper for short, precise delays. diff --git a/src/epd_display.h b/src/epd_display.h index 84d46993..99912ea4 100644 --- a/src/epd_display.h +++ b/src/epd_display.h @@ -2,18 +2,19 @@ #include #include "epd_internals.h" - /** * Display type as "compatibility classes", * Grouping displays by workarounds needed. */ enum EpdDisplayType { - /// A generic EPD, assume default config. - DISPLAY_TYPE_GENERIC, - /// Fast display where we can get away with low hold times. - DISPLAY_TYPE_ED097TC2, + /// A generic EPD, no specific flags set. + DISPLAY_TYPE_GENERIC = 0x00, // 0000 (no flags) /// Display with horizontal mirroring. - DISPLAY_TYPE_HORIZONTAL_MIRRORED, + DISPLAY_TYPE_HORIZONTAL_MIRRORED = (1 << 0), // 0001 (bit 0) + /// Fast display where low hold times are required. + DISPLAY_TYPE_ED097TC2 = (1 << 1), // 0010 (bit 1) + /// Up voltage sequence. + DISPLAY_UPSEQ_MC2 = (1 << 2), // 0100 (bit 2) }; typedef struct { @@ -42,4 +43,5 @@ extern const EpdDisplay_t ED133UT2; extern const EpdDisplay_t ED047TC1; extern const EpdDisplay_t ED047TC2; extern const EpdDisplay_t ED078KC1; -extern const EpdDisplay_t ED052TC4; \ No newline at end of file +extern const EpdDisplay_t ED052TC4; +extern const EpdDisplay_t ED103MC2; \ No newline at end of file diff --git a/src/highlevel.c b/src/highlevel.c index 20c699b6..f8eab6a0 100644 --- a/src/highlevel.c +++ b/src/highlevel.c @@ -52,8 +52,8 @@ EpdiyHighlevelState epd_hl_init(const EpdWaveform* waveform) { memset(state.front_fb, 0xFF, fb_size); memset(state.back_fb, 0xFF, fb_size); - - state.mirror_x = epd_get_display()->display_type == DISPLAY_TYPE_HORIZONTAL_MIRRORED; + bool is_mirrored = ((epd_get_display()->display_type & DISPLAY_TYPE_HORIZONTAL_MIRRORED) != 0); + state.mirror_x = is_mirrored; already_initialized = true; return state; } diff --git a/src/output_lcd/lcd_driver.h b/src/output_lcd/lcd_driver.h index 34c453aa..ca071eb1 100644 --- a/src/output_lcd/lcd_driver.h +++ b/src/output_lcd/lcd_driver.h @@ -4,7 +4,9 @@ #include #include #include - +#ifndef __DECLARE_RCC_ATOMIC_ENV +#define __DECLARE_RCC_ATOMIC_ENV (0) +#endif /** * LCD bus configuration parameters. */