From f1e266ca5a0502661db30fc4e7c7da797e58079b Mon Sep 17 00:00:00 2001 From: Micke Prag Date: Mon, 30 May 2022 21:30:58 +0200 Subject: [PATCH] 160 remove board defines (#168) * Add board HAL (Hardware abstraction layer) Instead of using defines to set the board hardware definitions each board get its own file with the routines to access the hardware functions. This way also third party boards can be implemented without being required to modify the epdiy source. So far only logic from display_ops.c has been ported. Work still also exists to optimize duplicated code. Care has been taken to not break existing code bases. If a board is defined in menuconfig this will still be used. * Move content of config_reg_v4.h into epd_board_v5.c The goal is to get rid of the config_reg_v*.h files * Move content of config_reg_v4.h into epd_board_v4.c * Move content of config_reg_v6.h into epd_board_v6.c * Move content of config_reg_v2.h into epd_board_v2_v3.c * Move content of config_reg_v2.h into epd_board_lilygo_t5_47.c * Remove board specific defines from display_ops.h * Free the i2s clock pin in i2s code This should not be done for individual boards. * Deprecate v6 board specific functions as generic function. Boards specific code should be marked as such. * Move temperature readings into board code * Implement a control interface for gpios Move signals oe, mode and stv to this Implement start_frame using the ctrl interface * Move logic of end_frame() from board to display_opts * Remove uneccesary function latch_row() * Move logic for latch_row from board to display_opts * Remove warning about v6_wait_for_interrupt() is unused * Optimize set_ctrl by supplying a mask with the changed signals * Share the temperature readings between boards v2 to v5 * Deprecate epd_powerdown() and make it to a board specific function instead * Read vcom voltage from epd_board_vcom_v6() This makes vcom voltage available at runtime and not at compile time --- src/epd_board.h | 13 + src/epd_driver/CMakeLists.txt | 10 +- src/epd_driver/Kconfig | 3 + src/epd_driver/board/epd_board_common.c | 35 +++ src/epd_driver/board/epd_board_common.h | 8 + src/epd_driver/board/epd_board_lilygo_t5_47.c | 198 ++++++++++++ src/epd_driver/board/epd_board_v2_v3.c | 160 ++++++++++ src/epd_driver/board/epd_board_v4.c | 182 +++++++++++ src/epd_driver/board/epd_board_v5.c | 183 +++++++++++ src/epd_driver/board/epd_board_v6.c | 294 ++++++++++++++++++ src/epd_driver/board_specific.c | 14 +- src/epd_driver/config_reg_v2.h | 108 ------- src/epd_driver/config_reg_v4.h | 110 ------- src/epd_driver/config_reg_v6.h | 158 ---------- src/epd_driver/display_ops.c | 211 +++++-------- src/epd_driver/display_ops.h | 80 +---- src/epd_driver/epd_board.c | 10 + src/epd_driver/epd_temperature.c | 64 +--- src/epd_driver/i2s_data_bus.c | 51 +-- src/epd_driver/i2s_data_bus.h | 39 ++- src/epd_driver/include/board/epd_board_v6.h | 10 + src/epd_driver/include/epd_board.h | 37 +++ src/epd_driver/include/epd_board_specific.h | 38 ++- src/epd_driver/include/epd_driver.h | 30 +- src/epd_driver/render.c | 32 +- 25 files changed, 1350 insertions(+), 728 deletions(-) create mode 100644 src/epd_board.h create mode 100644 src/epd_driver/board/epd_board_common.c create mode 100644 src/epd_driver/board/epd_board_common.h create mode 100644 src/epd_driver/board/epd_board_lilygo_t5_47.c create mode 100644 src/epd_driver/board/epd_board_v2_v3.c create mode 100644 src/epd_driver/board/epd_board_v4.c create mode 100644 src/epd_driver/board/epd_board_v5.c create mode 100644 src/epd_driver/board/epd_board_v6.c delete mode 100644 src/epd_driver/config_reg_v2.h delete mode 100644 src/epd_driver/config_reg_v4.h delete mode 100644 src/epd_driver/config_reg_v6.h create mode 100644 src/epd_driver/epd_board.c create mode 100644 src/epd_driver/include/board/epd_board_v6.h create mode 100644 src/epd_driver/include/epd_board.h diff --git a/src/epd_board.h b/src/epd_board.h new file mode 100644 index 00000000..215f270d --- /dev/null +++ b/src/epd_board.h @@ -0,0 +1,13 @@ +#pragma once +// This file is only used in the Arduino IDE +// and just includes the IDF component header. + +#ifdef __cplusplus +extern "C" { +#endif + +#include "epd_driver/include/epd_board.h" + +#ifdef __cplusplus +} +#endif diff --git a/src/epd_driver/CMakeLists.txt b/src/epd_driver/CMakeLists.txt index 156bf458..de927b80 100644 --- a/src/epd_driver/CMakeLists.txt +++ b/src/epd_driver/CMakeLists.txt @@ -1,5 +1,6 @@ set(app_sources "epd_driver.c" + "epd_board.c" "render.c" "display_ops.c" "tps65185.c" @@ -11,7 +12,14 @@ set(app_sources "epd_driver.c" "i2s_data_bus.c" "rmt_pulse.c" "highlevel.c" - "epd_temperature.c") + "epd_temperature.c" + "board/epd_board_common.c" + "board/epd_board_lilygo_t5_47.c" + "board/epd_board_v2_v3.c" + "board/epd_board_v4.c" + "board/epd_board_v5.c" + "board/epd_board_v6.c" +) idf_component_register(SRCS ${app_sources} INCLUDE_DIRS "include" REQUIRES esp_adc_cal) diff --git a/src/epd_driver/Kconfig b/src/epd_driver/Kconfig index e6992c69..8d88d231 100644 --- a/src/epd_driver/Kconfig +++ b/src/epd_driver/Kconfig @@ -53,6 +53,9 @@ menu "E-Paper Driver" config EPD_BOARD_REVISION_V6 bool "epdiy v6" + + config EPD_BOARD_CUSTOM + bool "Custom board" endchoice config EPD_DRIVER_V6_VCOM diff --git a/src/epd_driver/board/epd_board_common.c b/src/epd_driver/board/epd_board_common.c new file mode 100644 index 00000000..e5ae52e1 --- /dev/null +++ b/src/epd_driver/board/epd_board_common.c @@ -0,0 +1,35 @@ +#include "epd_board.h" +#include "driver/adc.h" +#include "esp_adc_cal.h" +#include "esp_log.h" + +static const adc1_channel_t channel = ADC1_CHANNEL_7; +static esp_adc_cal_characteristics_t adc_chars; + +#define NUMBER_OF_SAMPLES 100 + +void epd_board_temperature_init_v2() { + esp_adc_cal_value_t val_type = esp_adc_cal_characterize( + ADC_UNIT_1, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, &adc_chars + ); + if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) { + ESP_LOGI("epd_temperature", "Characterized using Two Point Value\n"); + } else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) { + ESP_LOGI("esp_temperature", "Characterized using eFuse Vref\n"); + } else { + ESP_LOGI("esp_temperature", "Characterized using Default Vref\n"); + } + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(channel, ADC_ATTEN_DB_6); +} + +float epd_board_ambient_temperature_v2() { + uint32_t value = 0; + for (int i = 0; i < NUMBER_OF_SAMPLES; i++) { + value += adc1_get_raw(channel); + } + value /= NUMBER_OF_SAMPLES; + // voltage in mV + float voltage = esp_adc_cal_raw_to_voltage(value, &adc_chars); + return (voltage - 500.0) / 10.0; +} diff --git a/src/epd_driver/board/epd_board_common.h b/src/epd_driver/board/epd_board_common.h new file mode 100644 index 00000000..eaedbd36 --- /dev/null +++ b/src/epd_driver/board/epd_board_common.h @@ -0,0 +1,8 @@ +/** + * @file "epd_board_common.h" + * @brief Common board functions shared between boards. + */ +#pragma once + +void epd_board_temperature_init_v2(); +float epd_board_ambient_temperature_v2(); diff --git a/src/epd_driver/board/epd_board_lilygo_t5_47.c b/src/epd_driver/board/epd_board_lilygo_t5_47.c new file mode 100644 index 00000000..59a03272 --- /dev/null +++ b/src/epd_driver/board/epd_board_lilygo_t5_47.c @@ -0,0 +1,198 @@ +#include "epd_board.h" + +#include "../display_ops.h" +#include "../i2s_data_bus.h" +#include "../rmt_pulse.h" + +#define CFG_DATA GPIO_NUM_23 +#define CFG_CLK GPIO_NUM_18 +#define CFG_STR GPIO_NUM_0 +#define D7 GPIO_NUM_22 +#define D6 GPIO_NUM_21 +#define D5 GPIO_NUM_27 +#define D4 GPIO_NUM_2 +#define D3 GPIO_NUM_19 +#define D2 GPIO_NUM_4 +#define D1 GPIO_NUM_32 +#define D0 GPIO_NUM_33 + +/* Control Lines */ +#define CKV GPIO_NUM_25 +#define STH GPIO_NUM_26 + +#define V4_LATCH_ENABLE GPIO_NUM_15 + +/* Edges */ +#define CKH GPIO_NUM_5 + +typedef struct { + bool power_disable : 1; + bool pos_power_enable : 1; + bool neg_power_enable : 1; + bool ep_scan_direction : 1; +} epd_config_register_t; + +static i2s_bus_config i2s_config = { + .clock = CKH, + .start_pulse = STH, + .data_0 = D0, + .data_1 = D1, + .data_2 = D2, + .data_3 = D3, + .data_4 = D4, + .data_5 = D5, + .data_6 = D6, + .data_7 = D7, +}; + +static void IRAM_ATTR push_cfg_bit(bool bit) { + gpio_set_level(CFG_CLK, 0); + gpio_set_level(CFG_DATA, bit); + gpio_set_level(CFG_CLK, 1); +} + +static epd_config_register_t config_reg; + +static void epd_board_init(uint32_t epd_row_width) { + /* Power Control Output/Off */ + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_DATA], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_CLK], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_STR], PIN_FUNC_GPIO); + gpio_set_direction(CFG_DATA, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_CLK, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_STR, GPIO_MODE_OUTPUT); + fast_gpio_set_lo(CFG_STR); + + config_reg.power_disable = true; + config_reg.pos_power_enable = false; + config_reg.neg_power_enable = false; + config_reg.ep_scan_direction = true; + + // Setup I2S + // add an offset off dummy bytes to allow for enough timing headroom + i2s_bus_init( &i2s_config, epd_row_width + 32 ); + + rmt_pulse_init(CKV); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t *state, const epd_ctrl_state_t * const mask) { + if (state->ep_sth) { + fast_gpio_set_hi(STH); + } else { + fast_gpio_set_lo(STH); + } + + if (mask->ep_output_enable || mask->ep_mode || mask->ep_stv || mask->ep_latch_enable) { + fast_gpio_set_lo(CFG_STR); + + // push config bits in reverse order + push_cfg_bit(state->ep_output_enable); + push_cfg_bit(state->ep_mode); + push_cfg_bit(config_reg.ep_scan_direction); + push_cfg_bit(state->ep_stv); + + push_cfg_bit(config_reg.neg_power_enable); + push_cfg_bit(config_reg.pos_power_enable); + push_cfg_bit(config_reg.power_disable); + push_cfg_bit(state->ep_latch_enable); + + fast_gpio_set_hi(CFG_STR); + } +} + +static void epd_board_poweron(epd_ctrl_state_t *state) { + i2s_gpio_attach(&i2s_config); + + // This was re-purposed as power enable. + config_reg.ep_scan_direction = true; + + // POWERON + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_disable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + config_reg.neg_power_enable = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.pos_power_enable = true; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + state->ep_stv = true; + state->ep_sth = true; + mask.ep_sth = true; + epd_board_set_ctrl(state, &mask); + // END POWERON +} + +void epd_powerdown_lilygo_t5_47() { + epd_ctrl_state_t *state = epd_ctrl_state(); + + // This was re-purposed as power enable however it also disables the touch. + // this workaround may still leave power on to epd and as such may cause other + // problems such as grey screen. + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(10 * 240); + + config_reg.neg_power_enable = false; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = false; + mask.ep_stv = true; + state->ep_output_enable = false; + mask.ep_output_enable = true; + state->ep_mode = false; + mask.ep_mode = true; + config_reg.power_disable = true; + epd_board_set_ctrl(state, &mask); + + i2s_gpio_detach(&i2s_config); +} + +static void epd_board_poweroff(epd_ctrl_state_t *state) { + // This was re-purposed as power enable. + config_reg.ep_scan_direction = false; + + // POWEROFF + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(10 * 240); + + config_reg.neg_power_enable = false; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = false; + mask.ep_stv = true; + state->ep_output_enable = false; + mask.ep_output_enable = true; + state->ep_mode = false; + mask.ep_mode = true; + config_reg.power_disable = true; + epd_board_set_ctrl(state, &mask); + + i2s_gpio_detach(&i2s_config); + // END POWEROFF +} + +const EpdBoardDefinition epd_board_lilygo_t5_47 = { + .init = epd_board_init, + .deinit = NULL, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .temperature_init = NULL, + .ambient_temperature = NULL, +}; diff --git a/src/epd_driver/board/epd_board_v2_v3.c b/src/epd_driver/board/epd_board_v2_v3.c new file mode 100644 index 00000000..a2516481 --- /dev/null +++ b/src/epd_driver/board/epd_board_v2_v3.c @@ -0,0 +1,160 @@ +#include "epd_board.h" + +#include "epd_board_common.h" +#include "../display_ops.h" +#include "../i2s_data_bus.h" +#include "../rmt_pulse.h" + +#define CFG_DATA GPIO_NUM_23 +#define CFG_CLK GPIO_NUM_18 +#define CFG_STR GPIO_NUM_19 +#define D7 GPIO_NUM_22 +#define D6 GPIO_NUM_21 +#define D5 GPIO_NUM_27 +#define D4 GPIO_NUM_2 +#define D3 GPIO_NUM_0 +#define D2 GPIO_NUM_4 +#define D1 GPIO_NUM_32 +#define D0 GPIO_NUM_33 + +/* Control Lines */ +#define CKV GPIO_NUM_25 +#define STH GPIO_NUM_26 + +/* Edges */ +#define CKH GPIO_NUM_5 + +typedef struct { + bool power_disable : 1; + bool pos_power_enable : 1; + bool neg_power_enable : 1; + bool ep_scan_direction : 1; +} epd_config_register_t; + +static i2s_bus_config i2s_config = { + .clock = CKH, + .start_pulse = STH, + .data_0 = D0, + .data_1 = D1, + .data_2 = D2, + .data_3 = D3, + .data_4 = D4, + .data_5 = D5, + .data_6 = D6, + .data_7 = D7, +}; + +static void IRAM_ATTR push_cfg_bit(bool bit) { + gpio_set_level(CFG_CLK, 0); + gpio_set_level(CFG_DATA, bit); + gpio_set_level(CFG_CLK, 1); +} + +static epd_config_register_t config_reg; + +static void epd_board_init(uint32_t epd_row_width) { + /* Power Control Output/Off */ + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_DATA], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_CLK], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_STR], PIN_FUNC_GPIO); + gpio_set_direction(CFG_DATA, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_CLK, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_STR, GPIO_MODE_OUTPUT); + fast_gpio_set_lo(CFG_STR); + + config_reg.power_disable = true; + config_reg.pos_power_enable = false; + config_reg.neg_power_enable = false; + config_reg.ep_scan_direction = true; + + // Setup I2S + // add an offset off dummy bytes to allow for enough timing headroom + i2s_bus_init( &i2s_config, epd_row_width + 32 ); + + rmt_pulse_init(CKV); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t *state, const epd_ctrl_state_t * const mask) { + if (state->ep_sth) { + fast_gpio_set_hi(STH); + } else { + fast_gpio_set_lo(STH); + } + + if (mask->ep_output_enable || mask->ep_mode || mask->ep_stv || mask->ep_latch_enable) { + fast_gpio_set_lo(CFG_STR); + + // push config bits in reverse order + push_cfg_bit(state->ep_output_enable); + push_cfg_bit(state->ep_mode); + push_cfg_bit(config_reg.ep_scan_direction); + push_cfg_bit(state->ep_stv); + + push_cfg_bit(config_reg.neg_power_enable); + push_cfg_bit(config_reg.pos_power_enable); + push_cfg_bit(config_reg.power_disable); + push_cfg_bit(state->ep_latch_enable); + + fast_gpio_set_hi(CFG_STR); + } +} + +static void epd_board_poweron(epd_ctrl_state_t *state) { + // POWERON + i2s_gpio_attach(&i2s_config); + + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_disable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + config_reg.neg_power_enable = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.pos_power_enable = true; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + state->ep_stv = true; + state->ep_sth = true; + mask.ep_sth = true; + epd_board_set_ctrl(state, &mask); + // END POWERON +} + +static void epd_board_poweroff(epd_ctrl_state_t *state) { + // POWEROFF + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(10 * 240); + + config_reg.neg_power_enable = false; + config_reg.pos_power_enable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = false; + state->ep_output_enable = false; + mask.ep_output_enable = true; + state->ep_mode = false; + mask.ep_mode = true; + config_reg.power_disable = true; + epd_board_set_ctrl(state, &mask); + + i2s_gpio_detach(&i2s_config); + // END POWEROFF +} + +const EpdBoardDefinition epd_board_v2_v3 = { + .init = epd_board_init, + .deinit = NULL, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .temperature_init = epd_board_temperature_init_v2, + .ambient_temperature = epd_board_ambient_temperature_v2, +}; diff --git a/src/epd_driver/board/epd_board_v4.c b/src/epd_driver/board/epd_board_v4.c new file mode 100644 index 00000000..1670494e --- /dev/null +++ b/src/epd_driver/board/epd_board_v4.c @@ -0,0 +1,182 @@ +#include "epd_board.h" + +#include "epd_board_common.h" +#include "../display_ops.h" +#include "../i2s_data_bus.h" +#include "../rmt_pulse.h" + +#define CFG_DATA GPIO_NUM_23 +#define CFG_CLK GPIO_NUM_18 +#define CFG_STR GPIO_NUM_19 +#define D7 GPIO_NUM_22 +#define D6 GPIO_NUM_21 +#define D5 GPIO_NUM_27 +#define D4 GPIO_NUM_2 +#define D3 GPIO_NUM_0 +#define D2 GPIO_NUM_4 +#define D1 GPIO_NUM_32 +#define D0 GPIO_NUM_33 + +/* Control Lines */ +#define CKV GPIO_NUM_25 +#define STH GPIO_NUM_26 + +#define V4_LATCH_ENABLE GPIO_NUM_15 + +/* Edges */ +#define CKH GPIO_NUM_5 + +typedef struct { + bool power_disable : 1; + bool power_enable_vpos : 1; + bool power_enable_vneg : 1; + bool power_enable_gl : 1; + bool power_enable_gh : 1; +} epd_config_register_t; + +static i2s_bus_config i2s_config = { + .clock = CKH, + .start_pulse = STH, + .data_0 = D0, + .data_1 = D1, + .data_2 = D2, + .data_3 = D3, + .data_4 = D4, + .data_5 = D5, + .data_6 = D6, + .data_7 = D7, +}; + +static void IRAM_ATTR push_cfg_bit(bool bit) { + gpio_set_level(CFG_CLK, 0); + gpio_set_level(CFG_DATA, bit); + gpio_set_level(CFG_CLK, 1); +} + +static epd_config_register_t config_reg; + +static void epd_board_init(uint32_t epd_row_width) { + /* Power Control Output/Off */ + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_DATA], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_CLK], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_STR], PIN_FUNC_GPIO); + gpio_set_direction(CFG_DATA, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_CLK, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_STR, GPIO_MODE_OUTPUT); + fast_gpio_set_lo(CFG_STR); + + config_reg.power_disable = true; + config_reg.power_enable_vpos = false; + config_reg.power_enable_vneg = false; + config_reg.power_enable_gl = false; + config_reg.power_enable_gh = false; + + // use latch pin as GPIO + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[V4_LATCH_ENABLE], PIN_FUNC_GPIO); + ESP_ERROR_CHECK(gpio_set_direction(V4_LATCH_ENABLE, GPIO_MODE_OUTPUT)); + gpio_set_level(V4_LATCH_ENABLE, 0); + + // Setup I2S + // add an offset off dummy bytes to allow for enough timing headroom + i2s_bus_init( &i2s_config, epd_row_width + 32 ); + + rmt_pulse_init(CKV); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t *state, const epd_ctrl_state_t * const mask) { + if (state->ep_sth) { + fast_gpio_set_hi(STH); + } else { + fast_gpio_set_lo(STH); + } + + if (mask->ep_output_enable || mask->ep_mode || mask->ep_stv) { + fast_gpio_set_lo(CFG_STR); + + // push config bits in reverse order + push_cfg_bit(state->ep_output_enable); + push_cfg_bit(state->ep_mode); + push_cfg_bit(config_reg.power_enable_gh); + push_cfg_bit(state->ep_stv); + + push_cfg_bit(config_reg.power_enable_gl); + push_cfg_bit(config_reg.power_enable_vneg); + push_cfg_bit(config_reg.power_enable_vpos); + push_cfg_bit(config_reg.power_disable); + + fast_gpio_set_hi(CFG_STR); + fast_gpio_set_lo(CFG_STR); + } + + if (state->ep_latch_enable) { + fast_gpio_set_hi(V4_LATCH_ENABLE); + } else { + fast_gpio_set_lo(V4_LATCH_ENABLE); + } +} + +static void epd_board_poweron(epd_ctrl_state_t *state) { + // POWERON + i2s_gpio_attach(&i2s_config); + + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_disable = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + config_reg.power_enable_gl = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_vneg = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_gh = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_vpos = true; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + state->ep_stv = true; + state->ep_sth = true; + mask.ep_sth = true; + epd_board_set_ctrl(state, &mask); + // END POWERON +} + +static void epd_board_poweroff(epd_ctrl_state_t *state) { + // POWEROFF + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_enable_gh = false; + config_reg.power_enable_vpos = false; + epd_board_set_ctrl(state, &mask); + busy_delay(10 * 240); + config_reg.power_enable_gl = false; + config_reg.power_enable_vneg = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = false; + state->ep_output_enable = false; + mask.ep_output_enable = true; + state->ep_mode = false; + mask.ep_mode = true; + config_reg.power_disable = true; + epd_board_set_ctrl(state, &mask); + + i2s_gpio_detach(&i2s_config); + // END POWEROFF +} + +const EpdBoardDefinition epd_board_v4 = { + .init = epd_board_init, + .deinit = NULL, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .temperature_init = epd_board_temperature_init_v2, + .ambient_temperature = epd_board_ambient_temperature_v2, +}; diff --git a/src/epd_driver/board/epd_board_v5.c b/src/epd_driver/board/epd_board_v5.c new file mode 100644 index 00000000..4d2f12de --- /dev/null +++ b/src/epd_driver/board/epd_board_v5.c @@ -0,0 +1,183 @@ +#include "epd_board.h" + +#include "epd_board_common.h" +#include "driver/rtc_io.h" +#include "../display_ops.h" +#include "../i2s_data_bus.h" +#include "../rmt_pulse.h" + +#define CFG_DATA GPIO_NUM_33 +#define CFG_CLK GPIO_NUM_32 +#define CFG_STR GPIO_NUM_0 +#define D7 GPIO_NUM_23 +#define D6 GPIO_NUM_22 +#define D5 GPIO_NUM_21 +#define D4 GPIO_NUM_19 +#define D3 GPIO_NUM_18 +#define D2 GPIO_NUM_5 +#define D1 GPIO_NUM_4 +#define D0 GPIO_NUM_25 + +/* Control Lines */ +#define CKV GPIO_NUM_26 +#define STH GPIO_NUM_27 + +#define V4_LATCH_ENABLE GPIO_NUM_2 + +/* Edges */ +#define CKH GPIO_NUM_15 + +typedef struct { + bool power_enable : 1; + bool power_enable_vpos : 1; + bool power_enable_vneg : 1; + bool power_enable_gl : 1; + bool power_enable_gh : 1; +} epd_config_register_t; +static epd_config_register_t config_reg; + +static i2s_bus_config i2s_config = { + .clock = CKH, + .start_pulse = STH, + .data_0 = D0, + .data_1 = D1, + .data_2 = D2, + .data_3 = D3, + .data_4 = D4, + .data_5 = D5, + .data_6 = D6, + .data_7 = D7, +}; + +static void IRAM_ATTR push_cfg_bit(bool bit) { + gpio_set_level(CFG_CLK, 0); + gpio_set_level(CFG_DATA, bit); + gpio_set_level(CFG_CLK, 1); +} + +static void epd_board_init(uint32_t epd_row_width) { + /* Power Control Output/Off */ + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_DATA], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_CLK], PIN_FUNC_GPIO); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_STR], PIN_FUNC_GPIO); + gpio_set_direction(CFG_DATA, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_CLK, GPIO_MODE_OUTPUT); + gpio_set_direction(CFG_STR, GPIO_MODE_OUTPUT); + fast_gpio_set_lo(CFG_STR); + + config_reg.power_enable = false; + config_reg.power_enable_vpos = false; + config_reg.power_enable_vneg = false; + config_reg.power_enable_gl = false; + config_reg.power_enable_gh = false; + + // use latch pin as GPIO + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[V4_LATCH_ENABLE], PIN_FUNC_GPIO); + ESP_ERROR_CHECK(gpio_set_direction(V4_LATCH_ENABLE, GPIO_MODE_OUTPUT)); + gpio_set_level(V4_LATCH_ENABLE, 0); + + // Setup I2S + // add an offset off dummy bytes to allow for enough timing headroom + i2s_bus_init( &i2s_config, epd_row_width + 32 ); + + rmt_pulse_init(CKV); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t *state, const epd_ctrl_state_t * const mask) { + if (state->ep_sth) { + fast_gpio_set_hi(STH); + } else { + fast_gpio_set_lo(STH); + } + + if (mask->ep_output_enable || mask->ep_mode || mask->ep_stv) { + fast_gpio_set_lo(CFG_STR); + + // push config bits in reverse order + push_cfg_bit(state->ep_output_enable); + push_cfg_bit(state->ep_mode); + push_cfg_bit(config_reg.power_enable_gh); + push_cfg_bit(state->ep_stv); + + push_cfg_bit(config_reg.power_enable_gl); + push_cfg_bit(config_reg.power_enable_vneg); + push_cfg_bit(config_reg.power_enable_vpos); + push_cfg_bit(config_reg.power_enable); + + fast_gpio_set_hi(CFG_STR); + fast_gpio_set_lo(CFG_STR); + } + + if (state->ep_latch_enable) { + fast_gpio_set_hi(V4_LATCH_ENABLE); + } else { + fast_gpio_set_lo(V4_LATCH_ENABLE); + } +} + +static void epd_board_poweron(epd_ctrl_state_t *state) { + // POWERON + i2s_gpio_attach(&i2s_config); + + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_enable = true; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + config_reg.power_enable_gl = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_vneg = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_gh = true; + epd_board_set_ctrl(state, &mask); + busy_delay(500 * 240); + config_reg.power_enable_vpos = true; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = true; + state->ep_sth = true; + mask.ep_sth = true; + epd_board_set_ctrl(state, &mask); + // END POWERON +} + +static void epd_board_poweroff(epd_ctrl_state_t *state) { + // POWEROFF + epd_ctrl_state_t mask = { // Trigger output to shift register + .ep_stv = true, + }; + config_reg.power_enable_gh = false; + config_reg.power_enable_vpos = false; + epd_board_set_ctrl(state, &mask); + busy_delay(10 * 240); + config_reg.power_enable_gl = false; + config_reg.power_enable_vneg = false; + epd_board_set_ctrl(state, &mask); + busy_delay(100 * 240); + + state->ep_stv = false; + state->ep_output_enable = false; + mask.ep_output_enable = true; + state->ep_mode = false; + mask.ep_mode = true; + config_reg.power_enable = false; + epd_board_set_ctrl(state, &mask); + + i2s_gpio_detach(&i2s_config); + // END POWEROFF +} + +const EpdBoardDefinition epd_board_v5 = { + .init = epd_board_init, + .deinit = NULL, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .temperature_init = epd_board_temperature_init_v2, + .ambient_temperature = epd_board_ambient_temperature_v2, +}; diff --git a/src/epd_driver/board/epd_board_v6.c b/src/epd_driver/board/epd_board_v6.c new file mode 100644 index 00000000..3bed23fd --- /dev/null +++ b/src/epd_driver/board/epd_board_v6.c @@ -0,0 +1,294 @@ +#include "epd_board.h" +#include "board/epd_board_v6.h" + +#include "esp_log.h" +#include "../display_ops.h" +#include "../i2s_data_bus.h" +#include "../rmt_pulse.h" +#include "../tps65185.h" +#include "../pca9555.h" + +#include + +static int v6_wait_for_interrupt(int timeout) __attribute__((unused)); + +#define CFG_SCL GPIO_NUM_33 +#define CFG_SDA GPIO_NUM_32 +#define CFG_INTR GPIO_NUM_35 +#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 D7 GPIO_NUM_23 +#define D6 GPIO_NUM_22 +#define D5 GPIO_NUM_21 +#define D4 GPIO_NUM_19 +#define D3 GPIO_NUM_18 +#define D2 GPIO_NUM_5 +#define D1 GPIO_NUM_4 +#define D0 GPIO_NUM_25 + +/* Control Lines */ +#define CKV GPIO_NUM_26 +#define STH GPIO_NUM_27 + +#define V4_LATCH_ENABLE GPIO_NUM_2 + +/* Edges */ +#define CKH GPIO_NUM_15 + +typedef struct { + i2c_port_t port; + bool pwrup; + bool vcom_ctrl; + bool wakeup; + bool others[8]; +} epd_config_register_t; + +static i2s_bus_config i2s_config = { + .clock = CKH, + .start_pulse = STH, + .data_0 = D0, + .data_1 = D1, + .data_2 = D2, + .data_3 = D3, + .data_4 = D4, + .data_5 = D5, + .data_6 = D6, + .data_7 = D7, +}; + +static bool interrupt_done = false; + +static void IRAM_ATTR interrupt_handler(void* arg) { + interrupt_done = true; +} + +static int v6_wait_for_interrupt(int timeout) { + int tries = 0; + while (!interrupt_done && gpio_get_level(CFG_INTR) == 1) { + if (tries >= 500) { + return -1; + } + tries++; + vTaskDelay(1); + } + int ival = 0; + interrupt_done = false; + pca9555_read_input(EPDIY_I2C_PORT, 1); + ival = tps_read_register(EPDIY_I2C_PORT, TPS_REG_INT1); + ival |= tps_read_register(EPDIY_I2C_PORT, TPS_REG_INT2) << 8; + while (!gpio_get_level(CFG_INTR)) { vTaskDelay(1); } + return ival; +} + +static epd_config_register_t config_reg; + +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 = 400000; + conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; + ESP_ERROR_CHECK(i2c_param_config(EPDIY_I2C_PORT, &conf)); + + ESP_ERROR_CHECK(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)); + + // use latch pin as GPIO + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[V4_LATCH_ENABLE], PIN_FUNC_GPIO); + ESP_ERROR_CHECK(gpio_set_direction(V4_LATCH_ENABLE, GPIO_MODE_OUTPUT)); + gpio_set_level(V4_LATCH_ENABLE, 0); + + // Setup I2S + // add an offset off dummy bytes to allow for enough timing headroom + i2s_bus_init( &i2s_config, epd_row_width + 32 ); + + rmt_pulse_init(CKV); +} + +static void epd_board_deinit() { + //gpio_reset_pin(CFG_INTR); + //rtc_gpio_isolate(CFG_INTR); + + 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 >= 500) { + ESP_LOGE("epdiy", "failed to shut down TPS65185!"); + break; + } + tries++; + vTaskDelay(1); + printf("%X\n", pca9555_read_input(config_reg.port, 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(500); + pca9555_read_input(config_reg.port, 0); + pca9555_read_input(config_reg.port, 1); + ESP_LOGI("epdiy", "going to sleep."); + i2c_driver_delete(EPDIY_I2C_PORT); +} + +static void epd_board_set_ctrl(epd_ctrl_state_t *state, const epd_ctrl_state_t * const mask) { + uint8_t value = 0x00; + if (state->ep_sth) { + fast_gpio_set_hi(STH); + } else { + fast_gpio_set_lo(STH); + } + + 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)); + } + + if (state->ep_latch_enable) { + fast_gpio_set_hi(V4_LATCH_ENABLE); + } else { + fast_gpio_set_lo(V4_LATCH_ENABLE); + } +} + +static void epd_board_poweron(epd_ctrl_state_t *state) { + i2s_gpio_attach(&i2s_config); + + epd_ctrl_state_t mask = { + .ep_stv = true, + }; + state->ep_stv = true; + config_reg.wakeup = true; + epd_board_set_ctrl(state, &mask); + 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); + + while (!(pca9555_read_input(config_reg.port, 1) & CFG_PIN_PWRGOOD)) { + vTaskDelay(1); + } + + ESP_ERROR_CHECK(tps_write_register(config_reg.port, TPS_REG_ENABLE, 0x3F)); + + tps_set_vcom(config_reg.port, epd_board_vcom_v6()); + + 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); + + i2s_gpio_detach(&i2s_config); +} + +static float epd_board_ambient_temperature() { + return tps_read_thermistor(EPDIY_I2C_PORT); +} + +/** + * Set GPIO direction of the broken-out GPIO extender port. + * Each pin corresponds to a bit in `direction`. + * `1` corresponds to input, `0` corresponds to output. + */ +esp_err_t epd_gpio_set_direction_v6(uint8_t direction) { + return pca9555_set_config(EPDIY_I2C_PORT, direction, 0); +} + +/** + * Get the input level of the broken-out GPIO extender port. + */ +uint8_t epd_gpio_get_level_v6() { + return pca9555_read_input(EPDIY_I2C_PORT, 0); +} + +/** + * Get the input level of the broken-out GPIO extender port. + */ +esp_err_t epd_gpio_set_value_v6(uint8_t value) { + return pca9555_set_value(EPDIY_I2C_PORT, value, 0); +} + +uint16_t __attribute__((weak)) epd_board_vcom_v6() { +#ifdef CONFIG_EPD_DRIVER_V6_VCOM + return CONFIG_EPD_DRIVER_V6_VCOM; +#else + // Arduino IDE... + extern int epd_driver_v6_vcom; + return epd_driver_v6_vcom; +#endif +} + +const EpdBoardDefinition epd_board_v6 = { + .init = epd_board_init, + .deinit = epd_board_deinit, + .set_ctrl = epd_board_set_ctrl, + .poweron = epd_board_poweron, + .poweroff = epd_board_poweroff, + + .temperature_init = NULL, + .ambient_temperature = epd_board_ambient_temperature, +}; diff --git a/src/epd_driver/board_specific.c b/src/epd_driver/board_specific.c index 2783aa1c..d6fd25a7 100644 --- a/src/epd_driver/board_specific.c +++ b/src/epd_driver/board_specific.c @@ -1,30 +1,28 @@ #include "epd_board_specific.h" -#if defined(CONFIG_EPD_BOARD_REVISION_V6) -#include "pca9555.h" -#include "display_ops.h" - /** * Set GPIO direction of the broken-out GPIO extender port. * Each pin corresponds to a bit in `direction`. * `1` corresponds to input, `0` corresponds to output. */ esp_err_t epd_gpio_set_direction(uint8_t direction) { - return pca9555_set_config(EPDIY_I2C_PORT, direction, 0); + return epd_gpio_set_direction_v6(direction); } /** * Get the input level of the broken-out GPIO extender port. */ uint8_t epd_gpio_get_level() { - return pca9555_read_input(EPDIY_I2C_PORT, 0); + return epd_gpio_get_level_v6(); } /** * Get the input level of the broken-out GPIO extender port. */ esp_err_t epd_gpio_set_value(uint8_t value) { - return pca9555_set_value(EPDIY_I2C_PORT, value, 0); + return epd_gpio_set_value_v6(value); } -#endif +void epd_powerdown() { + epd_powerdown_lilygo_t5_47(); +} diff --git a/src/epd_driver/config_reg_v2.h b/src/epd_driver/config_reg_v2.h deleted file mode 100644 index cae25c19..00000000 --- a/src/epd_driver/config_reg_v2.h +++ /dev/null @@ -1,108 +0,0 @@ -#include "display_ops.h" -#include - -typedef struct { - bool ep_latch_enable : 1; - bool power_disable : 1; - bool pos_power_enable : 1; - bool neg_power_enable : 1; - bool ep_stv : 1; - bool ep_scan_direction : 1; - bool ep_mode : 1; - bool ep_output_enable : 1; -} epd_config_register_t; - -static void config_reg_init(epd_config_register_t *cfg) { - cfg->ep_latch_enable = false; - cfg->power_disable = true; - cfg->pos_power_enable = false; - cfg->neg_power_enable = false; - cfg->ep_stv = true; - cfg->ep_scan_direction = true; - cfg->ep_mode = false; - cfg->ep_output_enable = false; -} - -inline static void IRAM_ATTR push_cfg_bit(bool bit); -void IRAM_ATTR busy_delay(uint32_t cycles); -inline static void fast_gpio_set_hi(gpio_num_t gpio_num); -inline static void fast_gpio_set_lo(gpio_num_t gpio_num); - -static void IRAM_ATTR push_cfg(const epd_config_register_t *cfg) { - fast_gpio_set_lo(CFG_STR); - - // push config bits in reverse order - push_cfg_bit(cfg->ep_output_enable); - push_cfg_bit(cfg->ep_mode); - push_cfg_bit(cfg->ep_scan_direction); - push_cfg_bit(cfg->ep_stv); - - push_cfg_bit(cfg->neg_power_enable); - push_cfg_bit(cfg->pos_power_enable); - push_cfg_bit(cfg->power_disable); - push_cfg_bit(cfg->ep_latch_enable); - - fast_gpio_set_hi(CFG_STR); -} - -static void cfg_poweron(epd_config_register_t *cfg) { -#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) - // This was re-purposed as power enable. - cfg->ep_scan_direction = true; -#endif - // POWERON - cfg->power_disable = false; - push_cfg(cfg); - busy_delay(100 * 240); - cfg->neg_power_enable = true; - push_cfg(cfg); - busy_delay(500 * 240); - cfg->pos_power_enable = true; - push_cfg(cfg); - busy_delay(100 * 240); - cfg->ep_stv = true; - push_cfg(cfg); - fast_gpio_set_hi(STH); - // END POWERON -} - -#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) -static void cfg_powerdown(epd_config_register_t *cfg) { - // This was re-purposed as power enable however it also disables the touch. - // this workaround may still leave power on to epd and as such may cause other - // problems such as grey screen. - cfg->pos_power_enable = false; - push_cfg(cfg); - busy_delay(10 * 240); - - cfg->neg_power_enable = false; - cfg->pos_power_enable = false; - push_cfg(cfg); - busy_delay(100 * 240); - - cfg->ep_stv = false; - cfg->ep_output_enable = false; - cfg->ep_mode = false; - cfg->power_disable = true; - push_cfg(cfg); -} -#endif - -static void cfg_poweroff(epd_config_register_t *cfg) { - // POWEROFF - cfg->pos_power_enable = false; - push_cfg(cfg); - busy_delay(10 * 240); - - cfg->neg_power_enable = false; - cfg->pos_power_enable = false; - push_cfg(cfg); - busy_delay(100 * 240); - - cfg->ep_stv = false; - cfg->ep_output_enable = false; - cfg->ep_mode = false; - cfg->power_disable = true; - push_cfg(cfg); - // END POWEROFF -} diff --git a/src/epd_driver/config_reg_v4.h b/src/epd_driver/config_reg_v4.h deleted file mode 100644 index d5cb2b13..00000000 --- a/src/epd_driver/config_reg_v4.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include "display_ops.h" - -typedef struct { -#if defined(CONFIG_EPD_BOARD_REVISION_V5) - bool power_enable : 1; -#else - bool power_disable : 1; -#endif - bool power_enable_vpos : 1; - bool power_enable_vneg : 1; - bool power_enable_gl : 1; - bool ep_stv : 1; - bool power_enable_gh : 1; - bool ep_mode : 1; - bool ep_output_enable : 1; -} epd_config_register_t; - -static void config_reg_init(epd_config_register_t *cfg) { -#if defined(CONFIG_EPD_BOARD_REVISION_V5) - cfg->power_enable = false; -#else - cfg->power_disable = true; -#endif - cfg->power_enable_vpos = false; - cfg->power_enable_vneg = false; - cfg->power_enable_gl = false; - cfg->ep_stv = true; - cfg->power_enable_gh = false; - cfg->ep_mode = false; - cfg->ep_output_enable = false; -} - -inline static void IRAM_ATTR push_cfg_bit(bool bit); -void IRAM_ATTR busy_delay(uint32_t cycles); -inline static void fast_gpio_set_hi(gpio_num_t gpio_num); -inline static void fast_gpio_set_lo(gpio_num_t gpio_num); - -static void IRAM_ATTR push_cfg(const epd_config_register_t *cfg) { - fast_gpio_set_lo(CFG_STR); - - // push config bits in reverse order - push_cfg_bit(cfg->ep_output_enable); - push_cfg_bit(cfg->ep_mode); - push_cfg_bit(cfg->power_enable_gh); - push_cfg_bit(cfg->ep_stv); - - push_cfg_bit(cfg->power_enable_gl); - push_cfg_bit(cfg->power_enable_vneg); - push_cfg_bit(cfg->power_enable_vpos); -#if defined(CONFIG_EPD_BOARD_REVISION_V5) - push_cfg_bit(cfg->power_enable); -#else - push_cfg_bit(cfg->power_disable); -#endif - - fast_gpio_set_hi(CFG_STR); - fast_gpio_set_lo(CFG_STR); -} - -static void cfg_poweron(epd_config_register_t *cfg) { - // POWERON -#if defined(CONFIG_EPD_BOARD_REVISION_V5) - cfg->power_enable = true; -#else - cfg->power_disable = false; -#endif - push_cfg(cfg); - busy_delay(100 * 240); - cfg->power_enable_gl = true; - push_cfg(cfg); - busy_delay(500 * 240); - cfg->power_enable_vneg = true; - push_cfg(cfg); - busy_delay(500 * 240); - cfg->power_enable_gh = true; - push_cfg(cfg); - busy_delay(500 * 240); - cfg->power_enable_vpos = true; - push_cfg(cfg); - busy_delay(100 * 240); - cfg->ep_stv = true; - push_cfg(cfg); - fast_gpio_set_hi(STH); - // END POWERON -} - -static void cfg_poweroff(epd_config_register_t *cfg) { - // POWEROFF - cfg->power_enable_gh = false; - cfg->power_enable_vpos = false; - push_cfg(cfg); - busy_delay(10 * 240); - cfg->power_enable_gl = false; - cfg->power_enable_vneg = false; - push_cfg(cfg); - busy_delay(100 * 240); - - cfg->ep_stv = false; - cfg->ep_output_enable = false; - cfg->ep_mode = false; -#if defined(CONFIG_EPD_BOARD_REVISION_V5) - cfg->power_enable = false; -#else - cfg->power_disable = true; -#endif - push_cfg(cfg); - // END POWEROFF -} diff --git a/src/epd_driver/config_reg_v6.h b/src/epd_driver/config_reg_v6.h deleted file mode 100644 index f32a63e2..00000000 --- a/src/epd_driver/config_reg_v6.h +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once - -#include "display_ops.h" -#include "pca9555.h" -#include "tps65185.h" -#include -#include -#include - -#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) - -typedef struct { - i2c_port_t port; - bool ep_output_enable; - bool ep_mode; - bool ep_stv; - bool pwrup; - bool vcom_ctrl; - bool wakeup; - bool others[8]; -} epd_config_register_t; - -static bool interrupt_done = false; - -static void IRAM_ATTR interrupt_handler(void* arg) { - interrupt_done = true; -} - -int v6_wait_for_interrupt(int timeout) { - - int tries = 0; - while (!interrupt_done && gpio_get_level(CFG_INTR) == 1) { - if (tries >= 500) { - return -1; - } - tries++; - vTaskDelay(1); - } - int ival = 0; - interrupt_done = false; - pca9555_read_input(EPDIY_I2C_PORT, 1); - ival = tps_read_register(EPDIY_I2C_PORT, TPS_REG_INT1); - ival |= tps_read_register(EPDIY_I2C_PORT, TPS_REG_INT2) << 8; - while (!gpio_get_level(CFG_INTR)) {vTaskDelay(1); } - return ival; -} - -void config_reg_init(epd_config_register_t* reg) { - reg->ep_output_enable = false; - reg->ep_mode = false; - reg->ep_stv = false; - reg->pwrup = false; - reg->vcom_ctrl = false; - reg->wakeup = false; - for (int i=0; i<8; i++) { - 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(reg->port, CFG_PIN_PWRGOOD | CFG_PIN_INT, 1)); -} - -static void push_cfg(epd_config_register_t* reg) { - uint8_t value = 0x00; - if (reg->ep_output_enable) value |= CFG_PIN_OE; - if (reg->ep_mode) value |= CFG_PIN_MODE; - if (reg->ep_stv) value |= CFG_PIN_STV; - if (reg->pwrup) value |= CFG_PIN_PWRUP; - if (reg->vcom_ctrl) value |= CFG_PIN_VCOM_CTRL; - if (reg->wakeup) value |= CFG_PIN_WAKEUP; - - ESP_ERROR_CHECK(pca9555_set_value(reg->port, value, 1)); -} - -static void cfg_poweron(epd_config_register_t* reg) { - reg->ep_stv = true; - reg->wakeup = true; - push_cfg(reg); - reg->pwrup = true; - push_cfg(reg); - reg->vcom_ctrl = true; - push_cfg(reg); - - // give the IC time to powerup and set lines - vTaskDelay(1); - - while (!(pca9555_read_input(reg->port, 1) & CFG_PIN_PWRGOOD)) { - vTaskDelay(1); - } - - ESP_ERROR_CHECK(tps_write_register(reg->port, TPS_REG_ENABLE, 0x3F)); - -#ifdef CONFIG_EPD_DRIVER_V6_VCOM - tps_set_vcom(reg->port, CONFIG_EPD_DRIVER_V6_VCOM); -// Arduino IDE... -#else - extern int epd_driver_v6_vcom; - tps_set_vcom(reg->port, epd_driver_v6_vcom); -#endif - - gpio_set_level(STH, 1); - - int tries = 0; - while (!((tps_read_register(reg->port, TPS_REG_PG) & 0xFA) == 0xFA)) { - if (tries >= 500) { - ESP_LOGE("epdiy", "Power enable failed! PG status: %X", tps_read_register(reg->port, TPS_REG_PG)); - return; - } - tries++; - vTaskDelay(1); - } -} - -static void cfg_poweroff(epd_config_register_t* reg) { - reg->vcom_ctrl = false; - reg->pwrup = false; - reg->ep_stv = false; - reg->ep_output_enable = false; - reg->ep_mode = false; - push_cfg(reg); - vTaskDelay(1); - reg->wakeup = false; - push_cfg(reg); -} - -static void cfg_deinit(epd_config_register_t* reg) { - ESP_ERROR_CHECK(pca9555_set_config(reg->port, CFG_PIN_PWRGOOD | CFG_PIN_INT | CFG_PIN_VCOM_CTRL | CFG_PIN_PWRUP, 1)); - - int tries = 0; - while (!((pca9555_read_input(reg->port, 1) & 0xC0) == 0x80)) { - if (tries >= 500) { - ESP_LOGE("epdiy", "failed to shut down TPS65185!"); - break; - } - tries++; - vTaskDelay(1); - printf("%X\n", pca9555_read_input(reg->port, 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(500); - pca9555_read_input(reg->port, 0); - pca9555_read_input(reg->port, 1); - ESP_LOGI("epdiy", "going to sleep."); -} diff --git a/src/epd_driver/display_ops.c b/src/epd_driver/display_ops.c index e2fd3310..876e2361 100644 --- a/src/epd_driver/display_ops.c +++ b/src/epd_driver/display_ops.c @@ -3,38 +3,14 @@ #include "esp_log.h" #include "i2s_data_bus.h" #include "rmt_pulse.h" +#include "epd_board.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "xtensa/core-macros.h" -#if defined(CONFIG_EPD_BOARD_REVISION_V2_V3) || defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) -#include "config_reg_v2.h" -#else -#if defined(CONFIG_EPD_BOARD_REVISION_V4) || defined(CONFIG_EPD_BOARD_REVISION_V5) -#include "config_reg_v4.h" -#else -#if defined(CONFIG_EPD_BOARD_REVISION_V6) -#include "config_reg_v6.h" -#else -#error "unknown revision" -#endif -#endif -#endif - -static epd_config_register_t config_reg; - -/* - * Write bits directly using the registers. - * Won't work for some pins (>= 32). - */ -inline static void fast_gpio_set_hi(gpio_num_t gpio_num) { - GPIO.out_w1ts = (1 << gpio_num); -} - -inline static void fast_gpio_set_lo(gpio_num_t gpio_num) { - GPIO.out_w1tc = (1 << gpio_num); -} +static epd_ctrl_state_t ctrl_state; +static const epd_ctrl_state_t NoChangeState = {0}; void IRAM_ATTR busy_delay(uint32_t cycles) { volatile unsigned long counts = XTHAL_GET_CCOUNT() + cycles; @@ -42,68 +18,26 @@ void IRAM_ATTR busy_delay(uint32_t cycles) { }; } +void epd_hw_init(uint32_t epd_row_width) { + ctrl_state.ep_latch_enable = false; + ctrl_state.ep_output_enable = false; + ctrl_state.ep_sth = true; + ctrl_state.ep_mode = false; + ctrl_state.ep_stv = true; + epd_ctrl_state_t mask = { + .ep_latch_enable = true, + .ep_output_enable = true, + .ep_sth = true, + .ep_mode = true, + .ep_stv = true, + }; -#if !defined(CONFIG_EPD_BOARD_REVISION_V6) -inline static void IRAM_ATTR push_cfg_bit(bool bit) { - gpio_set_level(CFG_CLK, 0); - if (bit) { - gpio_set_level(CFG_DATA, 1); - } else { - gpio_set_level(CFG_DATA, 0); - } - gpio_set_level(CFG_CLK, 1); -} -#endif - -void epd_base_init(uint32_t epd_row_width) { - - -#if defined(CONFIG_EPD_BOARD_REVISION_V6) - 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 = 400000; - conf.clk_flags = I2C_SCLK_SRC_FLAG_FOR_NOMAL; - ESP_ERROR_CHECK(i2c_param_config(EPDIY_I2C_PORT, &conf)); - - ESP_ERROR_CHECK(i2c_driver_install(EPDIY_I2C_PORT, I2C_MODE_MASTER, 0, 0, 0)); - - config_reg.port = EPDIY_I2C_PORT; -#else - /* Power Control Output/Off */ - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_DATA], PIN_FUNC_GPIO); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_CLK], PIN_FUNC_GPIO); - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[CFG_STR], PIN_FUNC_GPIO); - gpio_set_direction(CFG_DATA, GPIO_MODE_OUTPUT); - gpio_set_direction(CFG_CLK, GPIO_MODE_OUTPUT); - gpio_set_direction(CFG_STR, GPIO_MODE_OUTPUT); - fast_gpio_set_lo(CFG_STR); -#endif - - config_reg_init(&config_reg); - -#if defined(CONFIG_EPD_BOARD_REVISION_V4) || defined(CONFIG_EPD_BOARD_REVISION_V5) || defined(CONFIG_EPD_BOARD_REVISION_V6) - // use latch pin as GPIO - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[V4_LATCH_ENABLE], PIN_FUNC_GPIO); - ESP_ERROR_CHECK(gpio_set_direction(V4_LATCH_ENABLE, GPIO_MODE_OUTPUT)); - gpio_set_level(V4_LATCH_ENABLE, 0); -#endif - - push_cfg(&config_reg); - - // Setup I2S - // add an offset off dummy bytes to allow for enough timing headroom - i2s_bus_init( epd_row_width + 32 ); - - rmt_pulse_init(CKV); + epd_board->init(epd_row_width); + epd_board->set_ctrl(&ctrl_state, &mask); } void epd_poweron() { - i2s_gpio_attach(); - cfg_poweron(&config_reg); + epd_board->poweron(&ctrl_state); } #if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) @@ -114,65 +48,67 @@ void epd_powerdown() { #endif void epd_poweroff() { - cfg_poweroff(&config_reg); - i2s_gpio_detach(); + epd_board->poweroff(&ctrl_state); +} + +epd_ctrl_state_t *epd_ctrl_state() { + return &ctrl_state; } -void epd_base_deinit(){ +void epd_deinit() { epd_poweroff(); i2s_deinit(); -#if defined(CONFIG_EPD_BOARD_REVISION_V6) - cfg_deinit(&config_reg); - i2c_driver_delete(EPDIY_I2C_PORT); -#else - config_reg.ep_stv = false; - config_reg.ep_mode = false; - config_reg.ep_output_enable = false; - push_cfg(&config_reg); -#endif + ctrl_state.ep_output_enable = false; + ctrl_state.ep_mode = false; + ctrl_state.ep_stv = false; + epd_ctrl_state_t mask = { + .ep_output_enable = true, + .ep_mode = true, + .ep_stv = true, + }; + epd_board->set_ctrl(&ctrl_state, &mask); + + if (epd_board->deinit) { + epd_board->deinit(); + } + + // FIXME: deinit processes } void epd_start_frame() { while (i2s_is_busy() || rmt_busy()) { }; - config_reg.ep_mode = true; - push_cfg(&config_reg); + + epd_ctrl_state_t mask = NoChangeState; + + ctrl_state.ep_mode = true; + mask.ep_mode = true; + epd_board->set_ctrl(&ctrl_state, &mask); pulse_ckv_us(1, 1, true); // This is very timing-sensitive! - config_reg.ep_stv = false; - push_cfg(&config_reg); + mask = NoChangeState; + ctrl_state.ep_stv = false; + mask.ep_stv = true; + epd_board->set_ctrl(&ctrl_state, &mask); //busy_delay(240); pulse_ckv_us(1000, 100, false); - config_reg.ep_stv = true; - push_cfg(&config_reg); + mask = NoChangeState; + ctrl_state.ep_stv = true; + mask.ep_stv = true; + epd_board->set_ctrl(&ctrl_state, &mask); //pulse_ckv_us(0, 10, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); - config_reg.ep_output_enable = true; - push_cfg(&config_reg); -} - -static inline void latch_row() { -#if defined(CONFIG_EPD_BOARD_REVISION_V2_V3) || defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) - config_reg.ep_latch_enable = true; - push_cfg(&config_reg); - - config_reg.ep_latch_enable = false; - push_cfg(&config_reg); -#else -#if defined(CONFIG_EPD_BOARD_REVISION_V4) || defined(CONFIG_EPD_BOARD_REVISION_V5) || defined(CONFIG_EPD_BOARD_REVISION_V6) - fast_gpio_set_hi(V4_LATCH_ENABLE); - fast_gpio_set_lo(V4_LATCH_ENABLE); -#else -#error "unknown revision" -#endif -#endif + mask = NoChangeState; + ctrl_state.ep_output_enable = true; + mask.ep_output_enable = true; + epd_board->set_ctrl(&ctrl_state, &mask); } void IRAM_ATTR epd_skip() { @@ -186,13 +122,20 @@ void IRAM_ATTR epd_skip() { } void IRAM_ATTR epd_output_row(uint32_t output_time_dus) { - while (i2s_is_busy() || rmt_busy()) { }; - fast_gpio_set_hi(STH); + epd_ctrl_state_t mask = NoChangeState; + ctrl_state.ep_sth = true; + ctrl_state.ep_latch_enable = true; + mask.ep_sth = true; + mask.ep_latch_enable = true; + epd_board->set_ctrl(&ctrl_state, &mask); - latch_row(); + mask = NoChangeState; + ctrl_state.ep_latch_enable = false; + mask.ep_latch_enable = true; + epd_board->set_ctrl(&ctrl_state, &mask); #if defined(CONFIG_EPD_DISPLAY_TYPE_ED097TC2) || \ defined(CONFIG_EPD_DISPLAY_TYPE_ED133UT2) @@ -206,18 +149,24 @@ void IRAM_ATTR epd_output_row(uint32_t output_time_dus) { } void epd_end_frame() { - config_reg.ep_stv = false; - push_cfg(&config_reg); + epd_ctrl_state_t mask = NoChangeState; + ctrl_state.ep_stv = false; + mask.ep_stv = true; + epd_board->set_ctrl(&ctrl_state, &mask); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); - config_reg.ep_mode = false; - push_cfg(&config_reg); + mask = NoChangeState; + ctrl_state.ep_mode = false; + mask.ep_mode = true; + epd_board->set_ctrl(&ctrl_state, &mask); pulse_ckv_us(0, 10, true); - config_reg.ep_output_enable = false; - push_cfg(&config_reg); + mask = NoChangeState; + ctrl_state.ep_output_enable = false; + mask.ep_output_enable = true; + epd_board->set_ctrl(&ctrl_state, &mask); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); pulse_ckv_us(1, 1, true); diff --git a/src/epd_driver/display_ops.h b/src/epd_driver/display_ops.h index 8170c74a..cd7c8e22 100644 --- a/src/epd_driver/display_ops.h +++ b/src/epd_driver/display_ops.h @@ -1,78 +1,28 @@ #pragma once #include "driver/gpio.h" +#include "epd_board.h" -#if defined(CONFIG_EPD_BOARD_REVISION_V5) || defined(CONFIG_EPD_BOARD_REVISION_V6) -#define D7 GPIO_NUM_23 -#define D6 GPIO_NUM_22 -#define D5 GPIO_NUM_21 -#define D4 GPIO_NUM_19 -#define D3 GPIO_NUM_18 -#define D2 GPIO_NUM_5 -#define D1 GPIO_NUM_4 -#define D0 GPIO_NUM_25 - -/* Control Lines */ -#define CKV GPIO_NUM_26 -#define STH GPIO_NUM_27 - -#define V4_LATCH_ENABLE GPIO_NUM_2 - -/* Edges */ -#define CKH GPIO_NUM_15 - -#if defined(CONFIG_EPD_BOARD_REVISION_V5) -/* Config Reggister Control */ -#define CFG_DATA GPIO_NUM_33 -#define CFG_CLK GPIO_NUM_32 -#define CFG_STR GPIO_NUM_0 -#endif -#if defined(CONFIG_EPD_BOARD_REVISION_V6) -#define CFG_SCL GPIO_NUM_33 -#define CFG_SDA GPIO_NUM_32 -#define CFG_INTR GPIO_NUM_35 - -#define EPDIY_I2C_PORT I2C_NUM_0 -#endif - -#else -#define D7 GPIO_NUM_22 -#define D6 GPIO_NUM_21 -#define D5 GPIO_NUM_27 -#define D4 GPIO_NUM_2 -#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) -#define D3 GPIO_NUM_19 -#else -#define D3 GPIO_NUM_0 -#endif -#define D2 GPIO_NUM_4 -#define D1 GPIO_NUM_32 -#define D0 GPIO_NUM_33 - -#define CFG_DATA GPIO_NUM_23 -#define CFG_CLK GPIO_NUM_18 -#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) -#define CFG_STR GPIO_NUM_0 -#else -#define CFG_STR GPIO_NUM_19 -#endif - -/* Control Lines */ -#define CKV GPIO_NUM_25 -#define STH GPIO_NUM_26 - -#define V4_LATCH_ENABLE GPIO_NUM_15 +/* + * Write bits directly using the registers. + * Won't work for some pins (>= 32). + */ +inline void fast_gpio_set_hi(gpio_num_t gpio_num) { + GPIO.out_w1ts = (1 << gpio_num); +} -/* Edges */ -#define CKH GPIO_NUM_5 +inline void fast_gpio_set_lo(gpio_num_t gpio_num) { + GPIO.out_w1tc = (1 << gpio_num); +} -#endif +void IRAM_ATTR busy_delay(uint32_t cycles); -void epd_base_init(uint32_t epd_row_width); -void epd_base_deinit(); +void epd_hw_init(uint32_t epd_row_width); void epd_poweron(); void epd_poweroff(); +epd_ctrl_state_t *epd_ctrl_state(); + /** * Start a draw cycle. */ diff --git a/src/epd_driver/epd_board.c b/src/epd_driver/epd_board.c new file mode 100644 index 00000000..8b3336b8 --- /dev/null +++ b/src/epd_driver/epd_board.c @@ -0,0 +1,10 @@ +#include "epd_board.h" + +#include "epd_driver.h" +#include + +const EpdBoardDefinition *epd_board = NULL; + +void epd_set_board(const EpdBoardDefinition *board_definition) { + epd_board = board_definition; +} diff --git a/src/epd_driver/epd_temperature.c b/src/epd_driver/epd_temperature.c index d4a1ac18..ea3d3fdf 100644 --- a/src/epd_driver/epd_temperature.c +++ b/src/epd_driver/epd_temperature.c @@ -1,65 +1,17 @@ -#include "driver/adc.h" -#include "esp_adc_cal.h" #include "esp_log.h" -#include "display_ops.h" +#include "epd_board.h" -#ifdef CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47 -void epd_temperature_init() {} -float epd_ambient_temperature() -{ - ESP_LOGW("epd_temperature", "No ambient temperature sensor - returning 21C"); - return 21.0; -} -#else -#ifndef CONFIG_EPD_BOARD_REVISION_V6 -/// Use GPIO 35 for boards v4 - v5 -static const adc1_channel_t channel = ADC1_CHANNEL_7; -static esp_adc_cal_characteristics_t adc_chars; - -#define NUMBER_OF_SAMPLES 100 - -void epd_temperature_init() -{ - esp_adc_cal_value_t val_type = esp_adc_cal_characterize( - ADC_UNIT_1, ADC_ATTEN_DB_6, ADC_WIDTH_BIT_12, 1100, &adc_chars); - if (val_type == ESP_ADC_CAL_VAL_EFUSE_TP) - { - ESP_LOGI("epd_temperature", "Characterized using Two Point Value\n"); - } - else if (val_type == ESP_ADC_CAL_VAL_EFUSE_VREF) - { - ESP_LOGI("esp_temperature", "Characterized using eFuse Vref\n"); - } - else - { - ESP_LOGI("esp_temperature", "Characterized using Default Vref\n"); +void epd_temperature_init() { + if (epd_board->temperature_init) { + epd_board->temperature_init(); } - adc1_config_width(ADC_WIDTH_BIT_12); - adc1_config_channel_atten(channel, ADC_ATTEN_DB_6); } float epd_ambient_temperature() { - uint32_t value = 0; - for (int i = 0; i < NUMBER_OF_SAMPLES; i++) - { - value += adc1_get_raw(channel); + if (!epd_board->ambient_temperature) { + ESP_LOGW("epd_temperature", "No ambient temperature sensor - returning 21C"); + return 21.0; } - value /= NUMBER_OF_SAMPLES; - // voltage in mV - float voltage = esp_adc_cal_raw_to_voltage(value, &adc_chars); - return (voltage - 500.0) / 10.0; + return epd_board->ambient_temperature(); } -#elif defined(CONFIG_EPD_BOARD_REVISION_V6) - -#include "tps65185.h" - -void epd_temperature_init() {} - -float epd_ambient_temperature() -{ - return tps_read_thermistor(EPDIY_I2C_PORT); -} - -#endif -#endif diff --git a/src/epd_driver/i2s_data_bus.c b/src/epd_driver/i2s_data_bus.c index 436064c9..fc85e447 100644 --- a/src/epd_driver/i2s_data_bus.c +++ b/src/epd_driver/i2s_data_bus.c @@ -1,6 +1,6 @@ #include "i2s_data_bus.h" -#include "display_ops.h" #include "driver/periph_ctrl.h" +#include "driver/rtc_io.h" #if ESP_IDF_VERSION < (4, 0, 0) || ARDUINO_ARCH_ESP32 #include "rom/lldesc.h" #else @@ -116,13 +116,13 @@ void IRAM_ATTR i2s_start_line_output() { dev->conf.tx_start = 1; } -void i2s_gpio_attach() { - gpio_num_t I2S_GPIO_BUS[] = {D6, D7, D4, - D5, D2, D3, - D0, D1}; +void i2s_gpio_attach(i2s_bus_config *cfg) { + gpio_num_t I2S_GPIO_BUS[] = {cfg->data_6, cfg->data_7, cfg->data_4, + cfg->data_5, cfg->data_2, cfg->data_3, + cfg->data_0, cfg->data_1}; - gpio_set_direction(STH, GPIO_MODE_OUTPUT); - gpio_set_level(STH, 1); + gpio_set_direction(cfg->start_pulse, GPIO_MODE_OUTPUT); + gpio_set_level(cfg->start_pulse, 1); // Use I2S1 with no signal offset (for some reason the offset seems to be // needed in 16-bit mode, but not in 8 bit mode. int signal_base = I2S1O_DATA_OUT0_IDX; @@ -131,29 +131,34 @@ void i2s_gpio_attach() { for (int x = 0; x < 8; x++) { gpio_setup_out(I2S_GPIO_BUS[x], signal_base + x, false); } - // Invert word select signal - gpio_setup_out(CKH, I2S1O_WS_OUT_IDX, true); + // Free CKH after wakeup + gpio_hold_dis(cfg->clock); + // Invert word select signal + gpio_setup_out(cfg->clock, I2S1O_WS_OUT_IDX, true); } -void i2s_gpio_detach() { - gpio_set_direction(D0, GPIO_MODE_INPUT); - gpio_set_direction(D1, GPIO_MODE_INPUT); - gpio_set_direction(D2, GPIO_MODE_INPUT); - gpio_set_direction(D3, GPIO_MODE_INPUT); - gpio_set_direction(D4, GPIO_MODE_INPUT); - gpio_set_direction(D5, GPIO_MODE_INPUT); - gpio_set_direction(D6, GPIO_MODE_INPUT); - gpio_set_direction(D7, GPIO_MODE_INPUT); - gpio_set_direction(STH, GPIO_MODE_INPUT); - gpio_set_direction(CKH, GPIO_MODE_INPUT); +void i2s_gpio_detach(i2s_bus_config *cfg) { + gpio_set_direction(cfg->data_0, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_1, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_2, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_3, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_4, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_5, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_6, GPIO_MODE_INPUT); + gpio_set_direction(cfg->data_7, GPIO_MODE_INPUT); + gpio_set_direction(cfg->start_pulse, GPIO_MODE_INPUT); + gpio_set_direction(cfg->clock, GPIO_MODE_INPUT); + + gpio_reset_pin(cfg->clock); + rtc_gpio_isolate(cfg->clock); } -void i2s_bus_init(uint32_t epd_row_width) { - i2s_gpio_attach(); +void i2s_bus_init(i2s_bus_config *cfg, uint32_t epd_row_width) { + i2s_gpio_attach(cfg); // store pin in global variable for use in interrupt. - start_pulse_pin = STH; + start_pulse_pin = cfg->start_pulse; periph_module_enable(PERIPH_I2S1_MODULE); diff --git a/src/epd_driver/i2s_data_bus.h b/src/epd_driver/i2s_data_bus.h index a79faeac..e0e590f8 100644 --- a/src/epd_driver/i2s_data_bus.h +++ b/src/epd_driver/i2s_data_bus.h @@ -12,42 +12,39 @@ /** * I2S bus configuration parameters. */ -// typedef struct { -// /// GPIO numbers of the parallel bus pins. -// gpio_num_t data_0; -// gpio_num_t data_1; -// gpio_num_t data_2; -// gpio_num_t data_3; -// gpio_num_t data_4; -// gpio_num_t data_5; -// gpio_num_t data_6; -// gpio_num_t data_7; +typedef struct { + // GPIO numbers of the parallel bus pins. + gpio_num_t data_0; + gpio_num_t data_1; + gpio_num_t data_2; + gpio_num_t data_3; + gpio_num_t data_4; + gpio_num_t data_5; + gpio_num_t data_6; + gpio_num_t data_7; -// /// Data clock pin. -// gpio_num_t clock; + // Data clock pin. + gpio_num_t clock; -// /// "Start Pulse", enabling data input on the slave device (active low) -// gpio_num_t start_pulse; - -// // Width of a display row in pixels. -// uint32_t epd_row_width; -// } i2s_bus_config; + // "Start Pulse", enabling data input on the slave device (active low) + gpio_num_t start_pulse; +} i2s_bus_config; /** * Initialize the I2S data bus for communication * with a 8bit parallel display interface. */ -void i2s_bus_init(uint32_t epd_row_width); +void i2s_bus_init(i2s_bus_config *cfg, uint32_t epd_row_width); /** * Attach I2S to gpio's */ -void i2s_gpio_attach(); +void i2s_gpio_attach(i2s_bus_config *cfg); /** * Detach I2S from gpio's */ -void i2s_gpio_detach(); +void i2s_gpio_detach(i2s_bus_config *cfg); /** * Get the currently writable line buffer. diff --git a/src/epd_driver/include/board/epd_board_v6.h b/src/epd_driver/include/board/epd_board_v6.h new file mode 100644 index 00000000..2d797e18 --- /dev/null +++ b/src/epd_driver/include/board/epd_board_v6.h @@ -0,0 +1,10 @@ +/** + * @file "epd_board_v6.h" + * @brief Board-specific functions for v6. + */ + +#pragma once + +#include + +uint16_t epd_board_vcom_v6(); diff --git a/src/epd_driver/include/epd_board.h b/src/epd_driver/include/epd_board.h new file mode 100644 index 00000000..d43cacf2 --- /dev/null +++ b/src/epd_driver/include/epd_board.h @@ -0,0 +1,37 @@ +/** + * @file "epd_board.h" + * @brief Board-definitions provided by epdiy. + */ + +#pragma once + +#include +#include + +typedef struct { + bool ep_latch_enable : 1; + bool ep_output_enable : 1; + bool ep_sth : 1; + bool ep_mode : 1; + bool ep_stv : 1; +} epd_ctrl_state_t; + +typedef struct { + void (*init)(uint32_t epd_row_width); + void (*deinit)(void); + void (*set_ctrl)(epd_ctrl_state_t *, const epd_ctrl_state_t * const); + void (*poweron)(epd_ctrl_state_t *); + void (*poweroff)(epd_ctrl_state_t *); + + void (*temperature_init)(void); + float (*ambient_temperature)(void); +} EpdBoardDefinition; + +extern const EpdBoardDefinition *epd_board; + +// Built in board definitions +extern const EpdBoardDefinition epd_board_lilygo_t5_47; +extern const EpdBoardDefinition epd_board_v2_v3; +extern const EpdBoardDefinition epd_board_v4; +extern const EpdBoardDefinition epd_board_v5; +extern const EpdBoardDefinition epd_board_v6; diff --git a/src/epd_driver/include/epd_board_specific.h b/src/epd_driver/include/epd_board_specific.h index 2a7d3049..35585bad 100644 --- a/src/epd_driver/include/epd_board_specific.h +++ b/src/epd_driver/include/epd_board_specific.h @@ -9,16 +9,42 @@ #include "esp_err.h" /** - * Set GPIO direction of the broken-out GPIO extender port. + * Set GPIO direction of the broken-out GPIO extender port on v6 boards. */ -esp_err_t epd_gpio_set_direction(uint8_t direction); +esp_err_t epd_gpio_set_direction_v6(uint8_t direction); +esp_err_t epd_gpio_set_direction(uint8_t direction) __attribute__ ((deprecated)); /** - * Get the input level of the broken-out GPIO extender port. + * Get the input level of the broken-out GPIO extender port on v6 boards. */ -uint8_t epd_gpio_get_level(); +uint8_t epd_gpio_get_level_v6(); +uint8_t epd_gpio_get_level() __attribute__ ((deprecated)); /** - * Get the input level of the broken-out GPIO extender port. + * Get the input level of the broken-out GPIO extender port on v6 boards. */ -esp_err_t epd_gpio_set_value(uint8_t value); +esp_err_t epd_gpio_set_value_v6(uint8_t value); +esp_err_t epd_gpio_set_value(uint8_t value) __attribute__ ((deprecated)); + +/** This is a Lilygo47 specific function + + This is a work around a hardware issue with the Lilygo47 epd_poweroff() turns off the epaper completely + however the hardware of the Lilygo47 is different than the official boards. Which means that on the Lilygo47 this + disables power to the touchscreen. + + This is a workaround to allow to disable display power but not the touch screen. + On the Lilygo the epd power flag was re-purposed as power enable + for everything. This is a hardware thing. + \warning This workaround may still leave power on to epd and as such may cause other problems such as grey screen. + Please also use epd_poweroff() and epd_deinit() when you sleep the system wake on touch will still work. + + Arduino specific code: + \code{.c} + epd_powerdown_lilygo_t5_47(); + epd_deinit(); + esp_sleep_enable_ext1_wakeup(GPIO_SEL_13, ESP_EXT1_WAKEUP_ANY_HIGH); + esp_deep_sleep_start(); + \endcode +*/ +void epd_powerdown_lilygo_t5_47(); +void epd_powerdown() __attribute__ ((deprecated)); diff --git a/src/epd_driver/include/epd_driver.h b/src/epd_driver/include/epd_driver.h index 9c7d4939..8a31f2a7 100644 --- a/src/epd_driver/include/epd_driver.h +++ b/src/epd_driver/include/epd_driver.h @@ -11,6 +11,7 @@ extern "C" { #include #include #include "epd_internals.h" +#include "epd_board.h" /** Display software rotation. * Sets the rotation for the purposes of the drawing and font functions @@ -188,6 +189,9 @@ typedef struct { /** Initialize the ePaper display */ void epd_init(enum EpdInitOptions options); +/** Set the board hardware definition. This must be called before epd_init() */ +void epd_set_board(const EpdBoardDefinition *board); + /** Get the display rotation value */ enum EpdRotation epd_get_rotation(); @@ -206,30 +210,6 @@ void epd_deinit(); /** Enable display power supply. */ void epd_poweron(); -#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) -/** This is a Lilygo47 specific function - - This is a work around a hardware issue with the Lilygo47 epd_poweroff() turns off the epaper completely - however the hardware of the Lilygo47 is different than the official boards. Which means that on the Lilygo47 this - disables power to the touchscreen. - - This is a workaround to allow to disable display power but not the touch screen. - On the Lilygo the epd power flag was re-purposed as power enable - for everything. This is a hardware thing. - \warning This workaround may still leave power on to epd and as such may cause other problems such as grey screen. - Please also use epd_poweroff() and epd_deinit() when you sleep the system wake on touch will still work. - - Arduino specific code: - \code{.c} - epd_poweroff(); - epd_deinit(); - esp_sleep_enable_ext1_wakeup(GPIO_SEL_13, ESP_EXT1_WAKEUP_ANY_HIGH); - esp_deep_sleep_start(); - \endcode -*/ -void epd_powerdown(); -#endif - /** Disable display power supply. */ void epd_poweroff(); @@ -420,7 +400,7 @@ void epd_get_text_bounds(const EpdFont *font, const char *string, * @param margin : to be pllied to the width and height * @returns EpdRect with x and y as per the original and height and width * adjusted to fit the text with the margin added as well. - */ + */ EpdRect epd_get_string_rect (const EpdFont *font, const char *string, int x, int y, int margin, const EpdFontProperties *properties ); diff --git a/src/epd_driver/render.c b/src/epd_driver/render.c index 19d82a2f..158dc515 100644 --- a/src/epd_driver/render.c +++ b/src/epd_driver/render.c @@ -3,9 +3,9 @@ #include "epd_driver.h" #include "include/epd_driver.h" #include "include/epd_internals.h" +#include "include/epd_board.h" #include "lut.h" -#include "driver/rtc_io.h" #include "esp_types.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" @@ -234,10 +234,22 @@ void epd_clear_area_cycles(EpdRect area, int cycles, int cycle_time) { void epd_init(enum EpdInitOptions options) { +#if defined(CONFIG_EPD_BOARD_REVISION_LILYGO_T5_47) + epd_set_board(&epd_board_lilygo_t5_47); +#elif defined(CONFIG_EPD_BOARD_REVISION_V2_V3) + epd_set_board(&epd_board_v2_v3); +#elif defined(CONFIG_EPD_BOARD_REVISION_V4) + epd_set_board(&epd_board_v4); +#elif defined(CONFIG_EPD_BOARD_REVISION_V5) + epd_set_board(&epd_board_v5); +#elif defined(CONFIG_EPD_BOARD_REVISION_V6) + epd_set_board(&epd_board_v6); +#else + // Either the board should be set in menuconfig or the epd_set_board() must be called before epd_init() + assert(epd_board != NULL); +#endif - gpio_hold_dis(CKH); // freeing CKH after wakeup - - epd_base_init(EPD_WIDTH); + epd_hw_init(EPD_WIDTH); epd_temperature_init(); size_t lut_size = 0; @@ -284,21 +296,9 @@ void epd_init(enum EpdInitOptions options) { queue_len = 8; } output_queue = xQueueCreate(queue_len, EPD_WIDTH); -} -void epd_deinit() { - // FIXME: deinit processes -#if defined(CONFIG_EPD_BOARD_REVISION_V5) || defined(CONFIG_EPD_BOARD_REVISION_V6) - - gpio_reset_pin(CKH); - rtc_gpio_isolate(CKH); - //gpio_reset_pin(CFG_INTR); - //rtc_gpio_isolate(CFG_INTR); -#endif - epd_base_deinit(); } - EpdRect epd_difference_image_base( const uint8_t* to, const uint8_t* from,