diff --git a/firmware/main/CMakeLists.txt b/firmware/main/CMakeLists.txt index 15543fd..d287dee 100644 --- a/firmware/main/CMakeLists.txt +++ b/firmware/main/CMakeLists.txt @@ -1,10 +1,8 @@ set(BOARD_SRCS) set(BOARD_REQUIRES) if(CONFIG_TP_BOARD_43C) - # CH422G vendored (C port layer only) — avoids C++ compile errors in managed component - list(APPEND BOARD_SRCS "display_43c.c" "ui_43c.c" - "esp_io_expander.c" "esp_io_expander_ch422g.c") - # legacy i2c (i2c_driver_install) used by vendored CH422G port lives in `driver` (already required) + list(APPEND BOARD_SRCS "display_43c.c" "ui_43c.c") + # legacy i2c (i2c_driver_install) for the IO expander lives in `driver` (already required) else() list(APPEND BOARD_SRCS "display_147b.c" "ui_147b.c") list(APPEND BOARD_REQUIRES esp_driver_spi) diff --git a/firmware/main/board_43c.h b/firmware/main/board_43c.h index e336bdf..e3ec526 100644 --- a/firmware/main/board_43c.h +++ b/firmware/main/board_43c.h @@ -1,6 +1,5 @@ #pragma once -// ESP32-S3-Touch-LCD-4.3C — RGB565 800x480 + CH422G(I2C) backlight -#include "esp_io_expander.h" // IO_EXPANDER_PIN_NUM_2 used by CH422G_BL_PIN below +// ESP32-S3-Touch-LCD-4.3C — RGB565 800x480 + I2C IO-expander backlight (EXIO2) #define I2C_SDA 8 #define I2C_SCL 9 #define I2C_HZ 400000 @@ -24,6 +23,3 @@ #define BOARD_H_RES LCD_H_RES #define BOARD_V_RES LCD_V_RES - -// CH422G EXIO2 = backlight (high=on) -#define CH422G_BL_PIN IO_EXPANDER_PIN_NUM_2 diff --git a/firmware/main/display_43c.c b/firmware/main/display_43c.c index 91b4d76..cf8cd27 100644 --- a/firmware/main/display_43c.c +++ b/firmware/main/display_43c.c @@ -1,26 +1,45 @@ /* * display_43c.c — ESP32-S3-Touch-LCD-4.3C display driver * - * API corrections vs plan: - * - Uses old I2C driver (i2c_param_config/i2c_driver_install) because - * espressif/esp32_io_expander v1.x expects i2c_port_t, not i2c_master_bus_handle_t. - * - esp_lcd_rgb_panel_config_t has no bits_per_pixel in IDF v6.0.1; - * uses in_color_format/out_color_format = LCD_COLOR_FMT_RGB565 instead. - * - Component is espressif/esp32_io_expander (not esp_io_expander_ch422g). + * IO expander note: + * This board's "CH422G" footprint is actually a custom MCU-based IO expander + * (schematic U10 exposes SWDIO + PWM + ADC pins — a real CH422G has none of + * these). It speaks a simple register-pointer protocol at I2C 0x24: + * reg 0x02 = direction mask (1=output per bit), reg 0x03 = output latch byte. + * The Espressif esp_io_expander_ch422g driver uses the real-CH422G multi-address + * protocol (0x23/0x38/...) and NACKs on this board -> abort. So we talk to the + * expander directly (verified working per Waveshare's reference io_extension). + * + * Other IDF v6.0.1 notes: + * - esp_lcd_rgb_panel_config_t has no bits_per_pixel; use in/out_color_format. */ #include "display.h" #include "board.h" #include "driver/i2c.h" -#include "esp_io_expander_ch422g.h" #include "esp_lcd_panel_rgb.h" #include "esp_lcd_panel_ops.h" #include "esp_lvgl_port.h" -static esp_io_expander_handle_t g_exp = NULL; +#define IOEXP_ADDR 0x24 // I2C slave of the IO expander +#define IOEXP_REG_MODE 0x02 // 8-bit direction mask, 1 = output +#define IOEXP_REG_OUTPUT 0x03 // 8-bit output latch (write-only -> keep a shadow) +#define IOEXP_BL_BIT 2 // EXIO2 = LCD backlight (high = on) + +// Shadow of the output latch. 0xF7 is Waveshare's proven default (PA/bit3 low, +// rest high); we additionally clear the backlight bit so the screen stays dark +// until the first frame is rendered. +static uint8_t s_ioexp_out = 0xF7 & ~(1 << IOEXP_BL_BIT); + +static esp_err_t ioexp_write(uint8_t reg, uint8_t val) +{ + uint8_t buf[2] = { reg, val }; + return i2c_master_write_to_device(I2C_NUM_0, IOEXP_ADDR, buf, sizeof buf, + pdMS_TO_TICKS(100)); +} lv_display_t *board_display_init(void) { - // 1. I2C init (legacy driver, required by esp32_io_expander v1.x) + // 1. I2C (legacy driver) i2c_config_t i2c_cfg = { .mode = I2C_MODE_MASTER, .sda_io_num = I2C_SDA, @@ -32,13 +51,11 @@ lv_display_t *board_display_init(void) ESP_ERROR_CHECK(i2c_param_config(I2C_NUM_0, &i2c_cfg)); ESP_ERROR_CHECK(i2c_driver_install(I2C_NUM_0, I2C_MODE_MASTER, 0, 0, 0)); - // 2. CH422G IO expander: EXIO2 = backlight, keep off initially - ESP_ERROR_CHECK(esp_io_expander_new_i2c_ch422g( - I2C_NUM_0, ESP_IO_EXPANDER_I2C_CH422G_ADDRESS, &g_exp)); - ESP_ERROR_CHECK(esp_io_expander_set_dir(g_exp, CH422G_BL_PIN, IO_EXPANDER_OUTPUT)); - ESP_ERROR_CHECK(esp_io_expander_set_level(g_exp, CH422G_BL_PIN, 0)); + // 2. IO expander: all pins output, backlight off (until first frame) + ESP_ERROR_CHECK(ioexp_write(IOEXP_REG_MODE, 0xFF)); + ESP_ERROR_CHECK(ioexp_write(IOEXP_REG_OUTPUT, s_ioexp_out)); - // 3. RGB panel (IDF v6.0.1: use in_color_format/out_color_format, no bits_per_pixel) + // 3. RGB panel (IDF v6.0.1: in/out_color_format, no bits_per_pixel) esp_lcd_rgb_panel_config_t rgb = { .clk_src = LCD_CLK_SRC_DEFAULT, .timings = { @@ -81,7 +98,11 @@ lv_display_t *board_display_init(void) .hres = LCD_H_RES, .vres = LCD_V_RES, .color_format = LV_COLOR_FORMAT_RGB565, - .flags = { .buff_spiram = true }, + // full_refresh: redraw the whole screen every frame so BOTH RGB + // framebuffers stay fully painted. Without it, a mostly-static UI only + // paints one buffer (no dirty regions) and the panel flashes white when + // it swaps to the never-drawn second framebuffer. + .flags = { .full_refresh = true }, }; const lvgl_port_display_rgb_cfg_t rgbc = { .flags = { .bb_mode = true, .avoid_tearing = true }, @@ -91,7 +112,6 @@ lv_display_t *board_display_init(void) void board_display_backlight_on(void) { - if (g_exp) { - esp_io_expander_set_level(g_exp, CH422G_BL_PIN, 1); - } + s_ioexp_out |= (1 << IOEXP_BL_BIT); + ioexp_write(IOEXP_REG_OUTPUT, s_ioexp_out); } diff --git a/firmware/main/esp_expander_utils.h b/firmware/main/esp_expander_utils.h deleted file mode 100644 index 4a67632..0000000 --- a/firmware/main/esp_expander_utils.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Local stub for esp_expander_utils.h — replaces the managed component's - * version to avoid pulling in esp-lib-utils C++ headers that fail to compile - * under IDF v6.0.1 strict C++ mode. The C port files (esp_io_expander*.c) - * only use standard IDF macros (ESP_RETURN_ON_ERROR, ESP_LOGI, etc.) - * and do not need any macros from esp_lib_utils. - */ -#pragma once diff --git a/firmware/main/esp_io_expander.c b/firmware/main/esp_io_expander.c deleted file mode 100644 index f442168..0000000 --- a/firmware/main/esp_io_expander.c +++ /dev/null @@ -1,229 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include - -#include "esp_bit_defs.h" -#include "esp_check.h" -#include "esp_log.h" - -#include "esp_io_expander.h" - -#include "esp_expander_utils.h" - -#define VALID_IO_COUNT(handle) ((handle)->config.io_count <= IO_COUNT_MAX ? (handle)->config.io_count : IO_COUNT_MAX) - -/** - * @brief Register type - */ -typedef enum { - REG_INPUT = 0, - REG_OUTPUT, - REG_DIRECTION, -} reg_type_t; - -static const char *TAG = "io_expander"; - -static esp_err_t write_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t value); -static esp_err_t read_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t *value); - -esp_err_t esp_io_expander_set_dir(esp_io_expander_handle_t handle, uint32_t pin_num_mask, esp_io_expander_dir_t direction) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - if (pin_num_mask >= BIT64(VALID_IO_COUNT(handle))) { - ESP_LOGW(TAG, "Pin num mask out of range, bit higher than %d won't work", VALID_IO_COUNT(handle) - 1); - } - - bool is_output = (direction == IO_EXPANDER_OUTPUT) ? true : false; - uint32_t dir_reg, temp; - ESP_RETURN_ON_ERROR(read_reg(handle, REG_DIRECTION, &dir_reg), TAG, "Read direction reg failed"); - temp = dir_reg; - if ((is_output && !handle->config.flags.dir_out_bit_zero) || (!is_output && handle->config.flags.dir_out_bit_zero)) { - /* 1. Output && Set 1 to output */ - /* 2. Input && Set 1 to input */ - dir_reg |= pin_num_mask; - } else { - /* 3. Output && Set 0 to output */ - /* 4. Input && Set 0 to input */ - dir_reg &= ~pin_num_mask; - } - /* Write to reg only when different */ - if (dir_reg != temp) { - ESP_RETURN_ON_ERROR(write_reg(handle, REG_DIRECTION, dir_reg), TAG, "Write direction reg failed"); - } - - return ESP_OK; -} - -esp_err_t esp_io_expander_set_level(esp_io_expander_handle_t handle, uint32_t pin_num_mask, uint8_t level) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - if (pin_num_mask >= BIT64(VALID_IO_COUNT(handle))) { - ESP_LOGW(TAG, "Pin num mask out of range, bit higher than %d won't work", VALID_IO_COUNT(handle) - 1); - } - - uint32_t dir_reg, dir_bit; - ESP_RETURN_ON_ERROR(read_reg(handle, REG_DIRECTION, &dir_reg), TAG, "Read direction reg failed"); - - uint8_t io_count = VALID_IO_COUNT(handle); - /* Check every target pin's direction, must be in output mode */ - for (int i = 0; i < io_count; i++) { - if (pin_num_mask & BIT(i)) { - dir_bit = dir_reg & BIT(i); - /* Check whether it is in input mode */ - if ((dir_bit && handle->config.flags.dir_out_bit_zero) || (!dir_bit && !handle->config.flags.dir_out_bit_zero)) { - /* 1. 1 && Set 1 to input */ - /* 2. 0 && Set 0 to input */ - ESP_LOGE(TAG, "Pin[%d] can't set level in input mode", i); - return ESP_ERR_INVALID_STATE; - } - } - } - - uint32_t output_reg, temp; - /* Read the current output level */ - ESP_RETURN_ON_ERROR(read_reg(handle, REG_OUTPUT, &output_reg), TAG, "Read Output reg failed"); - temp = output_reg; - /* Set expected output level */ - if ((level && !handle->config.flags.output_high_bit_zero) || (!level && handle->config.flags.output_high_bit_zero)) { - /* 1. High level && Set 1 to output high */ - /* 2. Low level && Set 1 to output low */ - output_reg |= pin_num_mask; - } else { - /* 3. High level && Set 0 to output high */ - /* 4. Low level && Set 0 to output low */ - output_reg &= ~pin_num_mask; - } - /* Write to reg only when different */ - if (output_reg != temp) { - ESP_RETURN_ON_ERROR(write_reg(handle, REG_OUTPUT, output_reg), TAG, "Write Output reg failed"); - } - - return ESP_OK; -} - -esp_err_t esp_io_expander_get_level(esp_io_expander_handle_t handle, uint32_t pin_num_mask, uint32_t *level_mask) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - ESP_RETURN_ON_FALSE(level_mask, ESP_ERR_INVALID_ARG, TAG, "Invalid level"); - if (pin_num_mask >= BIT64(VALID_IO_COUNT(handle))) { - ESP_LOGW(TAG, "Pin num mask out of range, bit higher than %d won't work", VALID_IO_COUNT(handle) - 1); - } - - uint32_t input_reg; - ESP_RETURN_ON_ERROR(read_reg(handle, REG_INPUT, &input_reg), TAG, "Read input reg failed"); - if (!handle->config.flags.input_high_bit_zero) { - /* Get 1 when input high level */ - *level_mask = input_reg & pin_num_mask; - } else { - /* Get 0 when input high level */ - *level_mask = ~input_reg & pin_num_mask; - } - - return ESP_OK; -} - -esp_err_t esp_io_expander_print_state(esp_io_expander_handle_t handle) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - - uint8_t io_count = VALID_IO_COUNT(handle); - uint32_t input_reg, output_reg, dir_reg; - ESP_RETURN_ON_ERROR(read_reg(handle, REG_INPUT, &input_reg), TAG, "Read input reg failed"); - ESP_RETURN_ON_ERROR(read_reg(handle, REG_OUTPUT, &output_reg), TAG, "Read output reg failed"); - ESP_RETURN_ON_ERROR(read_reg(handle, REG_DIRECTION, &dir_reg), TAG, "Read direction reg failed"); - /* Get 1 if high level */ - if (handle->config.flags.input_high_bit_zero) { - input_reg ^= 0xffffffff; - } - /* Get 1 if high level */ - if (handle->config.flags.output_high_bit_zero) { - output_reg ^= 0xffffffff; - } - /* Get 1 if output */ - if (handle->config.flags.dir_out_bit_zero) { - dir_reg ^= 0xffffffff; - } - - for (int i = 0; i < io_count; i++) { - ESP_LOGI(TAG, "Index[%d] | Dir[%s] | In[%d] | Out[%d]", i, (dir_reg & BIT(i)) ? "Out" : "In", - (input_reg & BIT(i)) ? 1 : 0, (output_reg & BIT(i)) ? 1 : 0); - } - - return ESP_OK; -} - -esp_err_t esp_io_expander_reset(esp_io_expander_handle_t handle) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - ESP_RETURN_ON_FALSE(handle->reset, ESP_ERR_NOT_SUPPORTED, TAG, "reset isn't implemented"); - - return handle->reset(handle); -} - -esp_err_t esp_io_expander_del(esp_io_expander_handle_t handle) -{ - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - ESP_RETURN_ON_FALSE(handle->del, ESP_ERR_NOT_SUPPORTED, TAG, "del isn't implemented"); - - return handle->del(handle); -} - -/** - * @brief Write the value to a specific register - * - * @param handle: IO Expander handle - * @param reg: Specific type of register - * @param value: Expected register's value - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -static esp_err_t write_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t value) -{ - switch (reg) { - case REG_OUTPUT: - ESP_RETURN_ON_FALSE(handle->write_output_reg, ESP_ERR_NOT_SUPPORTED, TAG, "write_output_reg isn't implemented"); - return handle->write_output_reg(handle, value); - case REG_DIRECTION: - ESP_RETURN_ON_FALSE(handle->write_direction_reg, ESP_ERR_NOT_SUPPORTED, TAG, "write_direction_reg isn't implemented"); - return handle->write_direction_reg(handle, value); - default: - return ESP_ERR_NOT_SUPPORTED; - } - - return ESP_OK; -} - -/** - * @brief Read the value from a specific register - * - * @param handle: IO Expander handle - * @param reg: Specific type of register - * @param value: Actual register's value - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -static esp_err_t read_reg(esp_io_expander_handle_t handle, reg_type_t reg, uint32_t *value) -{ - ESP_RETURN_ON_FALSE(value, ESP_ERR_INVALID_ARG, TAG, "Invalid value"); - - switch (reg) { - case REG_INPUT: - ESP_RETURN_ON_FALSE(handle->read_input_reg, ESP_ERR_NOT_SUPPORTED, TAG, "read_input_reg isn't implemented"); - return handle->read_input_reg(handle, value); - case REG_OUTPUT: - ESP_RETURN_ON_FALSE(handle->read_output_reg, ESP_ERR_NOT_SUPPORTED, TAG, "read_output_reg isn't implemented"); - return handle->read_output_reg(handle, value); - case REG_DIRECTION: - ESP_RETURN_ON_FALSE(handle->read_direction_reg, ESP_ERR_NOT_SUPPORTED, TAG, "read_direction_reg isn't implemented"); - return handle->read_direction_reg(handle, value); - default: - return ESP_ERR_NOT_SUPPORTED; - } - - return ESP_OK; -} diff --git a/firmware/main/esp_io_expander.h b/firmware/main/esp_io_expander.h deleted file mode 100644 index a01f066..0000000 --- a/firmware/main/esp_io_expander.h +++ /dev/null @@ -1,266 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -/** - * @file - * @brief ESP IO expander - */ - -#pragma once - -#include -#include - -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define IO_COUNT_MAX (sizeof(uint32_t) * 8) - -/** - * @brief IO Expander Device Type - */ -typedef struct esp_io_expander_s esp_io_expander_t; -typedef esp_io_expander_t *esp_io_expander_handle_t; - -/** - * @brief IO Expander Pin Num - */ -typedef enum { - IO_EXPANDER_PIN_NUM_0 = (1ULL << 0), - IO_EXPANDER_PIN_NUM_1 = (1ULL << 1), - IO_EXPANDER_PIN_NUM_2 = (1ULL << 2), - IO_EXPANDER_PIN_NUM_3 = (1ULL << 3), - IO_EXPANDER_PIN_NUM_4 = (1ULL << 4), - IO_EXPANDER_PIN_NUM_5 = (1ULL << 5), - IO_EXPANDER_PIN_NUM_6 = (1ULL << 6), - IO_EXPANDER_PIN_NUM_7 = (1ULL << 7), - IO_EXPANDER_PIN_NUM_8 = (1ULL << 8), - IO_EXPANDER_PIN_NUM_9 = (1ULL << 9), - IO_EXPANDER_PIN_NUM_10 = (1ULL << 10), - IO_EXPANDER_PIN_NUM_11 = (1ULL << 11), - IO_EXPANDER_PIN_NUM_12 = (1ULL << 12), - IO_EXPANDER_PIN_NUM_13 = (1ULL << 13), - IO_EXPANDER_PIN_NUM_14 = (1ULL << 14), - IO_EXPANDER_PIN_NUM_15 = (1ULL << 15), - IO_EXPANDER_PIN_NUM_16 = (1ULL << 16), - IO_EXPANDER_PIN_NUM_17 = (1ULL << 17), - IO_EXPANDER_PIN_NUM_18 = (1ULL << 18), - IO_EXPANDER_PIN_NUM_19 = (1ULL << 19), - IO_EXPANDER_PIN_NUM_20 = (1ULL << 20), - IO_EXPANDER_PIN_NUM_21 = (1ULL << 21), - IO_EXPANDER_PIN_NUM_22 = (1ULL << 22), - IO_EXPANDER_PIN_NUM_23 = (1ULL << 23), - IO_EXPANDER_PIN_NUM_24 = (1ULL << 24), - IO_EXPANDER_PIN_NUM_25 = (1ULL << 25), - IO_EXPANDER_PIN_NUM_26 = (1ULL << 26), - IO_EXPANDER_PIN_NUM_27 = (1ULL << 27), - IO_EXPANDER_PIN_NUM_28 = (1ULL << 28), - IO_EXPANDER_PIN_NUM_29 = (1ULL << 29), - IO_EXPANDER_PIN_NUM_30 = (1ULL << 30), - IO_EXPANDER_PIN_NUM_31 = (1ULL << 31), -} esp_io_expander_pin_num_t; - -/** - * @brief IO Expander Pin direction - */ -typedef enum { - IO_EXPANDER_INPUT, /*!< Input direction */ - IO_EXPANDER_OUTPUT, /*!< Output direction */ -} esp_io_expander_dir_t; - -/** - * @brief IO Expander Configuration Type - */ -typedef struct { - uint8_t io_count; /*!< Count of device's IO, must be less or equal than `IO_COUNT_MAX` */ - struct { - uint8_t dir_out_bit_zero : 1; /*!< If the direction of IO is output, the corresponding bit of the direction register is 0 */ - uint8_t input_high_bit_zero : 1; /*!< If the input level of IO is high, the corresponding bit of the input register is 0 */ - uint8_t output_high_bit_zero : 1; /*!< If the output level of IO is high, the corresponding bit of the output register is 0 */ - } flags; - /* Don't support with interrupt mode yet, will be added soon */ -} esp_io_expander_config_t; - -struct esp_io_expander_s { - - /** - * @brief Read value from input register (mandatory) - * - * @note The value represents the input level from IO - * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. - * - * @param handle: IO Expander handle - * @param value: Register's value - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*read_input_reg)(esp_io_expander_handle_t handle, uint32_t *value); - - /** - * @brief Write value to output register (mandatory) - * - * @note The value represents the output level to IO - * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. - * - * @param handle: IO Expander handle - * @param value: Register's value - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*write_output_reg)(esp_io_expander_handle_t handle, uint32_t value); - - /** - * @brief Read value from output register (mandatory) - * - * @note The value represents the expected output level to IO - * @note This function can be implemented by reading the physical output register, or simply by reading a variable that record the output value (more faster) - * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. - * - * @param handle: IO Expander handle - * @param value: Register's value - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*read_output_reg)(esp_io_expander_handle_t handle, uint32_t *value); - - /** - * @brief Write value to direction register (mandatory) - * - * @note The value represents the direction of IO - * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. - * - * @param handle: IO Expander handle - * @param value: Register's value - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*write_direction_reg)(esp_io_expander_handle_t handle, uint32_t value); - - /** - * @brief Read value from directioin register (mandatory) - * - * @note The value represents the expected direction of IO - * @note This function can be implemented by reading the physical direction register, or simply by reading a variable that record the direction value (more faster) - * @note If there are multiple input registers in the device, their values should be spliced together in order to form the `value`. - * - * @param handle: IO Expander handle - * @param value: Register's value - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*read_direction_reg)(esp_io_expander_handle_t handle, uint32_t *value); - - /** - * @brief Reset the device to its initial state (mandatory) - * - * @note This function will reset all device's registers - * - * @param handle: IO Expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*reset)(esp_io_expander_handle_t handle); - - /** - * @brief Delete device (mandatory) - * - * @param handle: IO Expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ - esp_err_t (*del)(esp_io_expander_handle_t handle); - - /** - * @brief Configuration structure - */ - esp_io_expander_config_t config; -}; - -/** - * @brief Set the direction of a set of target IOs - * - * @param handle: IO Exapnder handle - * @param pin_num_mask: Bitwise OR of allowed pin num with type of `esp_io_expander_pin_num_t` - * @param direction: IO direction (only support input or output now) - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_set_dir(esp_io_expander_handle_t handle, uint32_t pin_num_mask, esp_io_expander_dir_t direction); - -/** - * @brief Set the output level of a set of target IOs - * - * @note All target IOs must be in output mode first, otherwise this function will return the error `ESP_ERR_INVALID_STATE` - * - * @param handle: IO Exapnder handle - * @param pin_num_mask: Bitwise OR of allowed pin num with type of `esp_io_expander_pin_num_t` - * @param level: 0 - Low level, 1 - High level - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_set_level(esp_io_expander_handle_t handle, uint32_t pin_num_mask, uint8_t level); - -/** - * @brief Get the input level of a set of target IOs - * - * @note This function can be called whenever target IOs are in input mode or output mode - * - * @param handle: IO Exapnder handle - * @param pin_num_mask: Bitwise OR of allowed pin num with type of `esp_io_expander_pin_num_t` - * @param level_mask: Bitwise OR of levels. For each bit, 0 - Low level, 1 - High level - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_get_level(esp_io_expander_handle_t handle, uint32_t pin_num_mask, uint32_t *level_mask); - -/** - * @brief Print the current status of each IO of the device, including direction, input level and output level - * - * @param handle: IO Exapnder handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_print_state(esp_io_expander_handle_t handle); - -/** - * @brief Reset the device to its initial status - * - * @note This function will reset all device's registers - * - * @param handle: IO Expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_reset(esp_io_expander_handle_t handle); - -/** - * @brief Delete device - * - * @param handle: IO Expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_del(esp_io_expander_handle_t handle); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/main/esp_io_expander_ch422g.c b/firmware/main/esp_io_expander_ch422g.c deleted file mode 100644 index f16fd02..0000000 --- a/firmware/main/esp_io_expander_ch422g.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#include -#include -#include - -#include "driver/i2c.h" -#include "esp_bit_defs.h" -#include "esp_check.h" -#include "esp_log.h" - -#include "esp_io_expander.h" -#include "esp_io_expander_ch422g.h" - -#include "esp_expander_utils.h" - -/* Timeout of each I2C communication */ -#define I2C_TIMEOUT_MS (10) - -#define IO_COUNT (12) - -/* Register address */ -#define CH422G_REG_WR_SET (0x48 >> 1) -#define CH422G_REG_WR_OC (0x46 >> 1) -#define CH422G_REG_WR_IO (0x70 >> 1) -#define CH422G_REG_RD_IO (0x4D >> 1) - -/* Default register value when reset */ -// *INDENT-OFF* -#define REG_WR_SET_DEFAULT_VAL (0x01UL) // Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | - // | --- | --- | --- | --- | ------- | ------- | -------- | ------- | - // Value: | / | / | / | / | [SLEEP] | [OD_EN] | [A_SCAN] | [IO_OE] | - // | --- | --- | --- | --- | ------- | ------- | -------- | ------- | - // Default: | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | - -// *INDENT-OFF* -#define REG_WR_OC_DEFAULT_VAL (0x0FU) -#define REG_WR_IO_DEFAULT_VAL (0xFFU) -#define REG_OUT_DEFAULT_VAL ((REG_WR_OC_DEFAULT_VAL << 8) | REG_WR_IO_DEFAULT_VAL) -#define REG_DIR_DEFAULT_VAL (0xFFFU) - -#define REG_WR_SET_BIT_IO_OE (1U << 0) -#define REG_WR_SET_BIT_OD_EN (1U << 2) -#define REG_WR_SET_BIT_SLEEP (1U << 3) - -/** - * @brief Device Structure Type - */ -typedef struct { - esp_io_expander_t base; - i2c_port_t i2c_num; - uint32_t i2c_address; - struct { - uint8_t wr_set; - uint8_t wr_oc; - uint8_t wr_io; - } regs; -} esp_io_expander_ch422g_t; - -static const char *TAG = "ch422g"; - -static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value); -static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value); -static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value); -static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value); -static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value); -static esp_err_t reset(esp_io_expander_t *handle); -static esp_err_t del(esp_io_expander_t *handle); - -esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle) -{ - ESP_LOGI(TAG, "version: %d.%d.%d", ESP_IO_EXPANDER_CH422G_VER_MAJOR, ESP_IO_EXPANDER_CH422G_VER_MINOR, - ESP_IO_EXPANDER_CH422G_VER_PATCH); - ESP_RETURN_ON_FALSE(i2c_num < I2C_NUM_MAX, ESP_ERR_INVALID_ARG, TAG, "Invalid i2c num"); - ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "Invalid handle"); - - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)calloc(1, sizeof(esp_io_expander_ch422g_t)); - ESP_RETURN_ON_FALSE(ch422g, ESP_ERR_NO_MEM, TAG, "Malloc failed"); - - ch422g->base.config.io_count = IO_COUNT; - ch422g->i2c_num = i2c_num; - ch422g->i2c_address = i2c_address; - ch422g->regs.wr_set = REG_WR_SET_DEFAULT_VAL; - ch422g->regs.wr_oc = REG_WR_OC_DEFAULT_VAL; - ch422g->regs.wr_io = REG_WR_IO_DEFAULT_VAL; - ch422g->base.read_input_reg = read_input_reg; - ch422g->base.write_output_reg = write_output_reg; - ch422g->base.read_output_reg = read_output_reg; - ch422g->base.write_direction_reg = write_direction_reg; - ch422g->base.read_direction_reg = read_direction_reg; - ch422g->base.del = del; - ch422g->base.reset = reset; - - esp_err_t ret = ESP_OK; - /* Reset configuration and register status */ - ESP_GOTO_ON_ERROR(reset(&ch422g->base), err, TAG, "Reset failed"); - - *handle = &ch422g->base; - return ESP_OK; -err: - free(ch422g); - return ret; -} - -esp_err_t esp_io_expander_ch422g_set_oc_open_drain(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set | REG_WR_SET_BIT_OD_EN); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -esp_err_t esp_io_expander_ch422g_set_oc_push_pull(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set & ~REG_WR_SET_BIT_OD_EN); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set & ~REG_WR_SET_BIT_IO_OE); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - // Delay 1ms to wait for the IO expander to switch to input mode - vTaskDelay(pdMS_TO_TICKS(2)); - - return ESP_OK; -} - -esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set | REG_WR_SET_BIT_IO_OE); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set | REG_WR_SET_BIT_SLEEP); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = (uint8_t)(ch422g->regs.wr_set & ~REG_WR_SET_BIT_SLEEP); - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device( - ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS) - ), TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -static esp_err_t read_input_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t temp = 0; - - ESP_RETURN_ON_ERROR( - i2c_master_read_from_device(ch422g->i2c_num, CH422G_REG_RD_IO, &temp, 1, pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Read RD-IO reg failed" - ); - *value = temp; - - return ESP_OK; -} - -static esp_err_t write_output_reg(esp_io_expander_handle_t handle, uint32_t value) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - - uint8_t wr_oc_data = (value & 0xF00) >> 8; - uint8_t wr_io_data = value & 0xFF; - - // WR-OC - if (wr_oc_data) { - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_OC, &wr_oc_data, sizeof(wr_oc_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write WR-OC reg failed" - ); - ch422g->regs.wr_oc = wr_oc_data; - } - - // WR-IO - if (wr_io_data) { - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_IO, &wr_io_data, sizeof(wr_io_data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write WR-IO reg failed" - ); - ch422g->regs.wr_io = wr_io_data; - } - - return ESP_OK; -} - -static esp_err_t read_output_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - - *value = ch422g->regs.wr_io | (((uint32_t)ch422g->regs.wr_oc) << 8); - - return ESP_OK; -} - -static esp_err_t write_direction_reg(esp_io_expander_handle_t handle, uint32_t value) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - uint8_t data = ch422g->regs.wr_set; - - value &= 0xFF; - if (value != 0) { - data |= REG_WR_SET_BIT_IO_OE; - } else { - data &= ~REG_WR_SET_BIT_IO_OE; - } - - // WR-SET - ESP_RETURN_ON_ERROR( - i2c_master_write_to_device(ch422g->i2c_num, CH422G_REG_WR_SET, &data, sizeof(data), pdMS_TO_TICKS(I2C_TIMEOUT_MS)), - TAG, "Write WR_SET reg failed" - ); - ch422g->regs.wr_set = data; - - return ESP_OK; -} - -#define DIR_OUT_VALUE (0xFFF) -#define DIR_IN_VALUE (0xF00) - -static esp_err_t read_direction_reg(esp_io_expander_handle_t handle, uint32_t *value) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - - *value = (ch422g->regs.wr_set & REG_WR_SET_BIT_IO_OE) ? DIR_OUT_VALUE : DIR_IN_VALUE; - - return ESP_OK; -} - -static esp_err_t reset(esp_io_expander_t *handle) -{ - ESP_RETURN_ON_ERROR(write_direction_reg(handle, REG_DIR_DEFAULT_VAL), TAG, "Write direction reg (WR_SET) failed"); - ESP_RETURN_ON_ERROR(write_output_reg(handle, REG_OUT_DEFAULT_VAL), TAG, "Write output reg (WR_OC & WR_IO) failed"); - - return ESP_OK; -} - -static esp_err_t del(esp_io_expander_t *handle) -{ - esp_io_expander_ch422g_t *ch422g = (esp_io_expander_ch422g_t *)__containerof(handle, esp_io_expander_ch422g_t, base); - - free(ch422g); - return ESP_OK; -} diff --git a/firmware/main/esp_io_expander_ch422g.h b/firmware/main/esp_io_expander_ch422g.h deleted file mode 100644 index a312edf..0000000 --- a/firmware/main/esp_io_expander_ch422g.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Apache-2.0 - */ - -#pragma once - -#include - -#include "driver/i2c.h" -#include "esp_err.h" - -#include "esp_io_expander.h" - -#ifdef __cplusplus -extern "C" { -#endif - -#define ESP_IO_EXPANDER_CH422G_VER_MAJOR (0) -#define ESP_IO_EXPANDER_CH422G_VER_MINOR (1) -#define ESP_IO_EXPANDER_CH422G_VER_PATCH (0) - -/** - * @brief Create a new ch422g IO expander driver - * - * @note The I2C communication should be initialized before use this function - * - * @param i2c_num: I2C port num - * @param i2c_address: I2C address of chip - * @param handle: IO expander handle - * - * @return - * - ESP_OK: Success, otherwise returns ESP_ERR_xxx - */ -esp_err_t esp_io_expander_new_i2c_ch422g(i2c_port_t i2c_num, uint32_t i2c_address, esp_io_expander_handle_t *handle); - -/** - * @brief I2C address of the ch422g. Just to keep the same with other IO expanders, but it is ignored. - */ -#define ESP_IO_EXPANDER_I2C_CH422G_ADDRESS (0x24) - -esp_err_t esp_io_expander_ch422g_set_oc_open_drain(esp_io_expander_handle_t handle); - -esp_err_t esp_io_expander_ch422g_set_oc_push_pull(esp_io_expander_handle_t handle); - -esp_err_t esp_io_expander_ch422g_set_all_input(esp_io_expander_handle_t handle); - -esp_err_t esp_io_expander_ch422g_set_all_output(esp_io_expander_handle_t handle); - -esp_err_t esp_io_expander_ch422g_enter_sleep(esp_io_expander_handle_t handle); - -esp_err_t esp_io_expander_ch422g_exit_sleep(esp_io_expander_handle_t handle); - -#ifdef __cplusplus -} -#endif diff --git a/firmware/main/idf_component.yml b/firmware/main/idf_component.yml index e483b2e..95cfb3d 100644 --- a/firmware/main/idf_component.yml +++ b/firmware/main/idf_component.yml @@ -3,5 +3,3 @@ dependencies: espressif/esp_lvgl_port: "^2" espressif/esp_tinyusb: "^1" espressif/cjson: "^1.7.18" - # esp32_io_expander vendored into main/ (C port only) to avoid C++ compilation - # errors in the managed component under IDF v6.0.1 strict mode. diff --git a/firmware/sdkconfig.43c.defaults b/firmware/sdkconfig.43c.defaults index c0dfb01..752a95d 100644 --- a/firmware/sdkconfig.43c.defaults +++ b/firmware/sdkconfig.43c.defaults @@ -6,6 +6,8 @@ CONFIG_SPIRAM_RODATA=y CONFIG_SPIRAM_SPEED_80M=y # RGB ISR placed in IRAM for real-time timing CONFIG_LCD_RGB_ISR_IRAM_SAFE=y +# IO expander uses the legacy i2c driver on purpose; silence the deprecation notice +CONFIG_I2C_SUPPRESS_DEPRECATE_WARN=y # 大屏需要更大的内置字体 CONFIG_LV_FONT_MONTSERRAT_28=y CONFIG_LV_FONT_MONTSERRAT_48=y