From 824e6137faa972cd1d987403d629597d567e72a8 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Mon, 1 Jul 2024 11:27:17 -0500 Subject: [PATCH 01/14] update espp to v0.12.2 --- components/espp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/espp b/components/espp index bfe9f28..c044871 160000 --- a/components/espp +++ b/components/espp @@ -1 +1 @@ -Subproject commit bfe9f2815ca06b2df8e909dc21a2977d8c42025b +Subproject commit c044871f4063eecaee1e4362615958804af6a7f5 From f50c357ab5c605f6064327089f51bd3bc568e7ef Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Mon, 1 Jul 2024 11:28:54 -0500 Subject: [PATCH 02/14] update rom-info to have const specifiers for formatting functions --- components/rom_info/include/rom_info.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/rom_info/include/rom_info.hpp b/components/rom_info/include/rom_info.hpp index 5928bba..85b1a55 100644 --- a/components/rom_info/include/rom_info.hpp +++ b/components/rom_info/include/rom_info.hpp @@ -31,10 +31,10 @@ std::vector parse_metadata(const std::string& metadata_path); // for easy printing of Emulator using libfmt template <> struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { return ctx.begin(); } template - auto format(const Emulator& platform, FormatContext& ctx) { + auto format(const Emulator& platform, FormatContext& ctx) const { switch (platform) { case Emulator::UNKNOWN: return fmt::format_to(ctx.out(), "UNKNOWN"); @@ -67,10 +67,10 @@ struct fmt::formatter { // for easy printing of RomInfo using libfmt template <> struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + constexpr auto parse(format_parse_context& ctx) const { return ctx.begin(); } template - auto format(const RomInfo& info, FormatContext& ctx) { + auto format(const RomInfo& info, FormatContext& ctx) const { return fmt::format_to(ctx.out(), "RomInfo {{ name: {}, boxart_path: {}, rom_path: {}, platform: {} }}\n", info.name, info.boxart_path, info.rom_path, info.platform); } From 94180c02e3d7a7a20368581a9f6296781e970525 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Mon, 1 Jul 2024 15:06:19 -0500 Subject: [PATCH 03/14] update for latest filesystem changes --- components/espp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/espp b/components/espp index c044871..e3757ed 160000 --- a/components/espp +++ b/components/espp @@ -1 +1 @@ -Subproject commit c044871f4063eecaee1e4362615958804af6a7f5 +Subproject commit e3757ed643aa849071db7ffb09eb53a701b09400 From 4ed39907c53de5d938735a985d38cdbd2aae9ea2 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Mon, 1 Jul 2024 21:27:23 -0500 Subject: [PATCH 04/14] wip refactor over to box-emu component which uses esp-box component from espp --- components/box-emu/CMakeLists.txt | 5 + components/box-emu/Kconfig.projbuild | 14 + components/box-emu/idf_component.yml | 4 + components/box-emu/include/battery_info.hpp | 20 ++ components/box-emu/include/box-emu.hpp | 257 ++++++++++++++++++ components/box-emu/include/box.hpp | 69 +++++ components/box-emu/include/box_3.hpp | 68 +++++ components/box-emu/include/box_emu_hal.hpp | 114 ++++++++ components/box-emu/include/emu_v0.hpp | 29 ++ components/box-emu/include/emu_v1.hpp | 37 +++ components/box-emu/include/fs_init.hpp | 18 ++ components/box-emu/include/hal.hpp | 11 + components/box-emu/include/hal_events.hpp | 7 + components/box-emu/include/input_state.hpp | 16 ++ components/box-emu/include/lvgl_inputs.h | 16 ++ components/box-emu/include/make_color.h | 15 ++ components/box-emu/include/mmap.hpp | 14 + components/box-emu/include/usb.hpp | 16 ++ components/box-emu/include/video_setting.hpp | 29 ++ components/box-emu/src/battery.cpp | 113 ++++++++ components/box-emu/src/box-emu.cpp | 122 +++++++++ components/box-emu/src/box_emu_hal.cpp | 20 ++ components/box-emu/src/fs_init.cpp | 79 ++++++ components/box-emu/src/hal_i2c.cpp | 40 +++ components/box-emu/src/i2s_audio.cpp | 267 +++++++++++++++++++ components/box-emu/src/input.cpp | 260 ++++++++++++++++++ components/box-emu/src/mmap.cpp | 35 +++ components/box-emu/src/spi_lcd.cpp | 261 ++++++++++++++++++ components/box-emu/src/usb.cpp | 119 +++++++++ components/box-emu/src/video_setting.cpp | 11 + components/box-emu/src/video_task.cpp | 183 +++++++++++++ 31 files changed, 2269 insertions(+) create mode 100644 components/box-emu/CMakeLists.txt create mode 100644 components/box-emu/Kconfig.projbuild create mode 100644 components/box-emu/idf_component.yml create mode 100644 components/box-emu/include/battery_info.hpp create mode 100644 components/box-emu/include/box-emu.hpp create mode 100644 components/box-emu/include/box.hpp create mode 100644 components/box-emu/include/box_3.hpp create mode 100644 components/box-emu/include/box_emu_hal.hpp create mode 100644 components/box-emu/include/emu_v0.hpp create mode 100644 components/box-emu/include/emu_v1.hpp create mode 100644 components/box-emu/include/fs_init.hpp create mode 100644 components/box-emu/include/hal.hpp create mode 100644 components/box-emu/include/hal_events.hpp create mode 100644 components/box-emu/include/input_state.hpp create mode 100644 components/box-emu/include/lvgl_inputs.h create mode 100644 components/box-emu/include/make_color.h create mode 100644 components/box-emu/include/mmap.hpp create mode 100644 components/box-emu/include/usb.hpp create mode 100644 components/box-emu/include/video_setting.hpp create mode 100644 components/box-emu/src/battery.cpp create mode 100644 components/box-emu/src/box-emu.cpp create mode 100644 components/box-emu/src/box_emu_hal.cpp create mode 100644 components/box-emu/src/fs_init.cpp create mode 100644 components/box-emu/src/hal_i2c.cpp create mode 100644 components/box-emu/src/i2s_audio.cpp create mode 100644 components/box-emu/src/input.cpp create mode 100644 components/box-emu/src/mmap.cpp create mode 100644 components/box-emu/src/spi_lcd.cpp create mode 100644 components/box-emu/src/usb.cpp create mode 100644 components/box-emu/src/video_setting.cpp create mode 100644 components/box-emu/src/video_task.cpp diff --git a/components/box-emu/CMakeLists.txt b/components/box-emu/CMakeLists.txt new file mode 100644 index 0000000..308ab12 --- /dev/null +++ b/components/box-emu/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRC_DIRS "src" + REQUIRES "driver" "heap" "fatfs" "esp_lcd" "esp_psram" "hal" "usb" "esp_tinyusb" "spi_flash" "nvs_flash" "codec" "adc" "aw9523" "button" "display" "display_drivers" "mcp23x17" "input_drivers" "tt21100" "gt911" "drv2605" "event_manager" "i2c" "task" "timer" "serialization" "max1704x" "esp-box" + ) diff --git a/components/box-emu/Kconfig.projbuild b/components/box-emu/Kconfig.projbuild new file mode 100644 index 0000000..ee31925 --- /dev/null +++ b/components/box-emu/Kconfig.projbuild @@ -0,0 +1,14 @@ +menu "BOX Emulator Configuration" + + choice + prompt "Module Configuration" + default HARDWARE_BOX + help + Select which display + SoC module you're using. + config HARDWARE_BOX + bool "ESP32-S3-BOX" + config HARDWARE_BOX_3 + bool "ESP32-S3-BOX-3" + endchoice + +endmenu diff --git a/components/box-emu/idf_component.yml b/components/box-emu/idf_component.yml new file mode 100644 index 0000000..47e8a9f --- /dev/null +++ b/components/box-emu/idf_component.yml @@ -0,0 +1,4 @@ +## IDF Component Manager Manifest File +dependencies: + espressif/esp_tinyusb: "^1.4.2" + idf: "^5.1" diff --git a/components/box-emu/include/battery_info.hpp b/components/box-emu/include/battery_info.hpp new file mode 100644 index 0000000..6854d0e --- /dev/null +++ b/components/box-emu/include/battery_info.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "format.hpp" + +struct BatteryInfo { + float voltage; + float level; + float charge_rate; +}; + +// for printing battery info using libfmt +template <> +struct fmt::formatter { + constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } + + template + auto format(const BatteryInfo& info, FormatContext& ctx) { + return fmt::format_to(ctx.out(), "BatteryInfo {{ voltage: {:.1f}, level: {:.1f}, charge_rate: {:.1f} }}", info.voltage, info.level, info.charge_rate); + } +}; diff --git a/components/box-emu/include/box-emu.hpp b/components/box-emu/include/box-emu.hpp new file mode 100644 index 0000000..62c6746 --- /dev/null +++ b/components/box-emu/include/box-emu.hpp @@ -0,0 +1,257 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "esp-box.hpp" + +#include "aw9523.hpp" +#include "base_component.hpp" +#include "high_resolution_timer.hpp" +#include "keypad_input.hpp" +#include "max1704x.hpp" +#include "mcp23x17.hpp" +#include "oneshot_adc.hpp" +#include "serialization.hpp" +#include "task.hpp" +#include "timer.hpp" + +#include "battery_info.hpp" +#include "gamepad_state.hpp" +#include "video_setting.hpp" + +class BoxEmu : public espp::BaseComponent { +public: + /// The Version of the BoxEmu + enum class Version { + UNKNOWN, ///< unknown box + V0, ///< first version of the box + V1, ///< second version of the box + }; + + /// @brief Access the singleton instance of the BoxEmu class + /// @return Reference to the singleton instance of the BoxEmu class + static BoxEmu &get() { + static BoxEmu instance; + return instance; + } + + BoxEmu(const BoxEMu &) = delete; + BoxEmu &operator=(const BoxEmu &) = delete; + BoxEmu(BoxEmu &&) = delete; + BoxEmu &operator=(BoxEmu &&) = delete; + + /// Get the version of the BoxEmu that was detected + /// \return The version of the BoxEmu that was detected + /// \see Version + Version version() const; + + /// Get a reference to the internal I2C bus + /// \return A reference to the internal I2C bus + /// \note The internal I2C bus is used for the touchscreen and audio codec + I2c &internal_i2c(); + + /// Get a reference to the external I2C bus + /// \return A reference to the external I2C bus + /// \note The external I2C bus is used for the gamepad functionality + I2c &external_i2c(); + + /// Initialize the EspBox hardware + /// \return True if the initialization was successful, false otherwise + /// \note This initializes the touch, display, and sound subsystems which are + /// internal to the EspBox + /// \see EspBox + bool initialize_box(); + + ///////////////////////////////////////////////////////////////////////////// + // uSD Card + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_sdcard(); + sdmmc_card_t *sdcard() const; + + ///////////////////////////////////////////////////////////////////////////// + // Memory + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_memory(); + size_t copy_romdata_to_cart_partition(const std::string& filename); + uint8_t *mapped_romdata() const; + + ///////////////////////////////////////////////////////////////////////////// + // Gamepad + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_gamepad(); + bool update_gamepad_state(); + GamepadState gamepad_state(); + void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape); + std::shared_ptr keypad() const; + + ///////////////////////////////////////////////////////////////////////////// + // Battery + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_battery(); + std::shared_ptr battery(); + + ///////////////////////////////////////////////////////////////////////////// + // Video + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_video(); + void set_display_size(size_t width, size_t height); + void set_native_size(size_t width, size_t height, int pitch = -1); + void set_palette(const uint16_t *palette, size_t size = 256); + void push_frame(const void* frame); + VideoSetting video_setting() const; + void video_setting(VideoSetting setting); + + ///////////////////////////////////////////////////////////////////////////// + // USB + ///////////////////////////////////////////////////////////////////////////// + + bool initialize_usb(); + bool deinitialize_usb(); + bool is_usb_enabled() const; + +protected: + BoxEmu(); + void detect(); + + struct version0 { + using InputDriver = espp::mcp23x17; + static constexpr uint16_t START_PIN = (1<<0) << 0; // start pin is on port a of the MCP23x17 + static constexpr uint16_t SELECT_PIN = (1<<1) << 0; // select pin is on port a of the MCP23x17 + static constexpr uint16_t UP_PIN = (1<<0) << 8; // up pin is on port b of the MCP23x17 + static constexpr uint16_t DOWN_PIN = (1<<1) << 8; // down pin is on port b of the MCP23x17 + static constexpr uint16_t LEFT_PIN = (1<<2) << 8; // left pin is on port b of the MCP23x17 + static constexpr uint16_t RIGHT_PIN = (1<<3) << 8; // right pin is on port b of the MCP23x17 + static constexpr uint16_t A_PIN = (1<<4) << 8; // a pin is on port b of the MCP23x17 + static constexpr uint16_t B_PIN = (1<<5) << 8; // b pin is on port b of the MCP23x17 + static constexpr uint16_t X_PIN = (1<<6) << 8; // x pin is on port b of the MCP23x17 + static constexpr uint16_t Y_PIN = (1<<7) << 8; // y pin is on port b of the MCP23x17 + static constexpr uint16_t BAT_ALERT_PIN = 0; // battery alert pin doesn't exist on the MCP23x17 + static constexpr uint16_t VOL_UP_PIN = 0; // volume up pin doesn't exist on the MCP23x17 + static constexpr uint16_t VOL_DOWN_PIN = 0; // volume down pin doesn't exist on the MCP23x17 + static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN); + static constexpr uint16_t INTERRUPT_MASK = (START_PIN | SELECT_PIN); + static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN ); // pins are active low so invert them + static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; + static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; + static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; + static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; + }; + + struct version1 { + using InputDriver = espp::Aw9523; + static constexpr gpio_num_t VBAT_SENSE_PIN = GPIO_NUM_14; // battery sense pin is on GPIO 14 + static constexpr gpio_num_t AW9523_INT_PIN = GPIO_NUM_21; // interrupt pin is on GPIO 21 + static constexpr uint16_t UP_PIN = (1<<0) << 0; // up pin is on port 0 of the AW9523 + static constexpr uint16_t DOWN_PIN = (1<<1) << 0; // down pin is on port 0 of the AW9523 + static constexpr uint16_t LEFT_PIN = (1<<2) << 0; // left pin is on port 0 of the AW9523 + static constexpr uint16_t RIGHT_PIN = (1<<3) << 0; // right pin is on port 0 of the AW9523 + static constexpr uint16_t A_PIN = (1<<4) << 0; // a pin is on port 0 of the AW9523 + static constexpr uint16_t B_PIN = (1<<5) << 0; // b pin is on port 0 of the AW9523 + static constexpr uint16_t X_PIN = (1<<6) << 0; // x pin is on port 0 of the AW9523 + static constexpr uint16_t Y_PIN = (1<<7) << 0; // y pin is on port 0 of the AW9523 + static constexpr uint16_t START_PIN = (1<<0) << 8; // start pin is on port 1 of the AW9523 + static constexpr uint16_t SELECT_PIN = (1<<1) << 8; // select pin is on port 1 of the AW9523 + static constexpr uint16_t BAT_ALERT_PIN = (1<<3) << 8; // battery alert pin is on port 1 of the AW9523 + static constexpr uint16_t VOL_UP_PIN = (1<<4) << 8; // volume up pin is on port 1 of the AW9523 + static constexpr uint16_t VOL_DOWN_PIN = (1<<5) << 8; // volume down pin is on port 1 of the AW9523 + static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); + static constexpr uint16_t INTERRUPT_MASK = (BAT_ALERT_PIN); + static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); // pins are active low so invert them + static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; + static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; + static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; + static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; + + // ADC for the battery voltage, it's on ADC2_CH3, which is IO14 + static constexpr adc_unit_t BATTERY_ADC_UNIT = ADC_UNIT_2; + static constexpr adc_channel_t BATTERY_ADC_CHANNEL = ADC_CHANNEL_3; + }; + + class InputBase { + public: + virtual uint16_t get_pins(std::error_code& ec) = 0; + virtual GamepadState pins_to_gamepad_state(uint16_t pins) = 0; + virtual void handle_volume_pins(uint16_t pins) = 0; + }; + + template + class Input : public InputBase { + public: + explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} + virtual uint16_t get_pins(std::error_code& ec) override { + auto val = input_driver->get_pins(ec); + if (ec) { + return 0; + } + return val ^ T::INVERT_MASK; + } + virtual GamepadState pins_to_gamepad_state(uint16_t pins) override { + GamepadState state; + state.a = (bool)(pins & T::A_PIN); + state.b = (bool)(pins & T::B_PIN); + state.x = (bool)(pins & T::X_PIN); + state.y = (bool)(pins & T::Y_PIN); + state.start = (bool)(pins & T::START_PIN); + state.select = (bool)(pins & T::SELECT_PIN); + state.up = (bool)(pins & T::UP_PIN); + state.down = (bool)(pins & T::DOWN_PIN); + state.left = (bool)(pins & T::LEFT_PIN); + state.right = (bool)(pins & T::RIGHT_PIN); + return state; + } + virtual void handle_volume_pins(uint16_t pins) override { + // check the volume pins and send out events if they're pressed / released + bool volume_up = (bool)(pins & T::VOL_UP_PIN); + bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); + int volume_change = (volume_up * 10) + (volume_down * -10); + if (volume_change != 0) { + // change the volume + float current_volume = espp::EspBox::volume(); + float new_volume = std::clamp(current_volume + volume_change, 0, 100); + espp::EspBox::volume(new_volume); + // send out a volume change event + espp::EventManager::get().publish(volume_changed_topic, {}); + } + } + protected: + std::shared_ptr input_driver; + }; + + // external I2c (peripherals) + static constexpr auto external_i2c_port = I2C_NUM_1; + static constexpr auto external_i2c_clock_speed = 400 * 1000; + static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; + static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; + + Version version_{Version::UNKNOWN}; + + I2c external_i2c_{{.port = external_i2c_port, + .sda_io_num = external_i2c_sda, + .scl_io_num = external_i2c_scl, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE}}; + + std::recursive_mutex gamepad_state_mutex_; + GamepadState gamepad_state_; + std::shared_ptr input_; + std::shared_ptr keypad_; + std::shared_ptr input_timer_; +}; diff --git a/components/box-emu/include/box.hpp b/components/box-emu/include/box.hpp new file mode 100644 index 0000000..2950e73 --- /dev/null +++ b/components/box-emu/include/box.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include "hal/spi_types.h" +#include "driver/gpio.h" +#include "driver/i2s_std.h" +#include "driver/spi_master.h" + +#include "i2c.hpp" +#include "st7789.hpp" +#include "touchpad_input.hpp" +#include "tt21100.hpp" + +static constexpr std::string_view dev_kit = "ESP32-S3-BOX"; + +// internal i2c (touchscreen, audio codec) +static constexpr auto internal_i2c_port = I2C_NUM_0; +static constexpr auto internal_i2c_clock_speed = 400 * 1000; +static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; +static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; + +// external I2c (peripherals) +static constexpr auto external_i2c_port = I2C_NUM_1; +static constexpr auto external_i2c_clock_speed = 400 * 1000; +static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; +static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; + +// LCD +static constexpr int lcd_clock_speed = 60 * 1000 * 1000; +static constexpr auto lcd_spi_num = SPI2_HOST; +static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; +static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; +static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; +static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; +static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; +static constexpr gpio_num_t backlight_io = GPIO_NUM_45; +static constexpr size_t lcd_width = 320; +static constexpr size_t lcd_height = 240; +static constexpr bool backlight_value = true; +static constexpr bool reset_value = false; +static constexpr bool invert_colors = true; +static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; +static constexpr bool mirror_x = true; +static constexpr bool mirror_y = true; +using DisplayDriver = espp::St7789; + +// touch +static constexpr bool touch_swap_xy = false; +static constexpr bool touch_invert_x = true; +static constexpr bool touch_invert_y = false; +static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; +using TouchDriver = espp::Tt21100; + +// sound +static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; +static constexpr auto i2s_port = I2S_NUM_0; +static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; +static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; +static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_47; +static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; +static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; +static constexpr gpio_num_t mute_pin = GPIO_NUM_1; + +// uSD card +static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; +static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; +static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; +static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; +static constexpr auto sdcard_spi_num = SPI3_HOST; + diff --git a/components/box-emu/include/box_3.hpp b/components/box-emu/include/box_3.hpp new file mode 100644 index 0000000..391af74 --- /dev/null +++ b/components/box-emu/include/box_3.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "hal/spi_types.h" +#include "driver/gpio.h" +#include "driver/i2s_std.h" +#include "driver/spi_master.h" + +#include "i2c.hpp" +#include "st7789.hpp" +#include "touchpad_input.hpp" +#include "gt911.hpp" + +static constexpr std::string_view dev_kit = "ESP32-S3-BOX-3"; + +// internal i2c (touchscreen, audio codec) +static constexpr auto internal_i2c_port = I2C_NUM_0; +static constexpr auto internal_i2c_clock_speed = 400 * 1000; +static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; +static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; + +// external I2c (peripherals) +static constexpr auto external_i2c_port = I2C_NUM_1; +static constexpr auto external_i2c_clock_speed = 400 * 1000; +static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; +static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; + +// LCD +static constexpr int lcd_clock_speed = 60 * 1000 * 1000; +static constexpr auto lcd_spi_num = SPI2_HOST; +static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; +static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; +static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; +static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; +static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; +static constexpr gpio_num_t backlight_io = GPIO_NUM_47; // was 45 on ESP32-S3-BOX +static constexpr size_t lcd_width = 320; +static constexpr size_t lcd_height = 240; +static constexpr bool backlight_value = true; +static constexpr bool reset_value = true; // was false on ESP32-S3-BOX +static constexpr bool invert_colors = true; +static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; +static constexpr bool mirror_x = true; +static constexpr bool mirror_y = true; +using DisplayDriver = espp::St7789; + +// touch +static constexpr bool touch_swap_xy = false; +static constexpr bool touch_invert_x = false; +static constexpr bool touch_invert_y = false; +static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; +using TouchDriver = espp::Gt911; + +// sound +static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; +static constexpr auto i2s_port = I2S_NUM_0; +static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; +static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; +static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_45; // was 47 on ESP32-S3-BOX +static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; +static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; +static constexpr gpio_num_t mute_pin = GPIO_NUM_1; + +// uSD card +static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; +static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; +static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; +static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; +static constexpr auto sdcard_spi_num = SPI3_HOST; diff --git a/components/box-emu/include/box_emu_hal.hpp b/components/box-emu/include/box_emu_hal.hpp new file mode 100644 index 0000000..c055438 --- /dev/null +++ b/components/box-emu/include/box_emu_hal.hpp @@ -0,0 +1,114 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "aw9523.hpp" +#include "button.hpp" +#include "event_manager.hpp" +#include "display.hpp" +#include "i2c.hpp" +#include "keypad_input.hpp" +#include "logger.hpp" +#include "max1704x.hpp" +#include "mcp23x17.hpp" +#include "oneshot_adc.hpp" +#include "serialization.hpp" +#include "st7789.hpp" +#include "task.hpp" +#include "timer.hpp" +#include "high_resolution_timer.hpp" +#include "touchpad_input.hpp" + +#if CONFIG_HARDWARE_BOX +#include "box.hpp" +#elif CONFIG_HARDWARE_BOX_3 +#include "box_3.hpp" +#else +#error "Invalid module selection" +#endif + +#include "emu_v0.hpp" +#include "emu_v1.hpp" + +#include "es7210.hpp" +#include "es8311.hpp" +#include "lvgl_inputs.h" +#include "make_color.h" + +#include "battery_info.hpp" +#include "fs_init.hpp" +#include "hal_events.hpp" +#include "input_state.hpp" +#include "mmap.hpp" +#include "statistics.hpp" +#include "usb.hpp" +#include "video_setting.hpp" + +namespace hal { + // top level init function + void init(); + + // i2c / peripheral interfaces + void i2c_init(); + std::shared_ptr get_internal_i2c(); + std::shared_ptr get_external_i2c(); + + // input + void init_input(); + void get_input_state(InputState *state); + + // video + static constexpr int NUM_ROWS_IN_FRAME_BUFFER = 50; + std::shared_ptr get_display(); + uint16_t *get_vram0(); + uint16_t *get_vram1(); + uint8_t *get_frame_buffer0(); + uint8_t *get_frame_buffer1(); + void lcd_write(const uint8_t *data, size_t length, uint32_t user_data); + void lcd_write_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, const uint8_t *data); + void lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); + void lcd_init(); + void set_display_brightness(float brightness); + float get_display_brightness(); + + void init_video_task(); + void set_display_size(size_t width, size_t height); + void set_native_size(size_t width, size_t height, int pitch = -1); + void set_palette(const uint16_t* palette, size_t size = 256); + void push_frame(const void* frame); + + VideoSetting get_video_setting(); + void set_video_setting(const VideoSetting& setting); + + // audio + void audio_init(); + void set_audio_sample_rate(uint32_t sample_rate); + uint32_t get_audio_sample_rate(); + void play_audio(const uint8_t *data, uint32_t num_bytes); + bool is_muted(); + void set_muted(bool mute); + int get_audio_volume(); + void set_audio_volume(int percent); + + // battery + void battery_init(); + std::shared_ptr get_battery(); +} diff --git a/components/box-emu/include/emu_v0.hpp b/components/box-emu/include/emu_v0.hpp new file mode 100644 index 0000000..019a109 --- /dev/null +++ b/components/box-emu/include/emu_v0.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include + +#include "mcp23x17.hpp" + +class EmuV0 { +public: + static constexpr uint16_t START_PIN = (1<<0) << 0; // start pin is on port a of the MCP23x17 + static constexpr uint16_t SELECT_PIN = (1<<1) << 0; // select pin is on port a of the MCP23x17 + static constexpr uint16_t UP_PIN = (1<<0) << 8; // up pin is on port b of the MCP23x17 + static constexpr uint16_t DOWN_PIN = (1<<1) << 8; // down pin is on port b of the MCP23x17 + static constexpr uint16_t LEFT_PIN = (1<<2) << 8; // left pin is on port b of the MCP23x17 + static constexpr uint16_t RIGHT_PIN = (1<<3) << 8; // right pin is on port b of the MCP23x17 + static constexpr uint16_t A_PIN = (1<<4) << 8; // a pin is on port b of the MCP23x17 + static constexpr uint16_t B_PIN = (1<<5) << 8; // b pin is on port b of the MCP23x17 + static constexpr uint16_t X_PIN = (1<<6) << 8; // x pin is on port b of the MCP23x17 + static constexpr uint16_t Y_PIN = (1<<7) << 8; // y pin is on port b of the MCP23x17 + static constexpr uint16_t BAT_ALERT_PIN = 0; // battery alert pin doesn't exist on the MCP23x17 + static constexpr uint16_t VOL_UP_PIN = 0; // volume up pin doesn't exist on the MCP23x17 + static constexpr uint16_t VOL_DOWN_PIN = 0; // volume down pin doesn't exist on the MCP23x17 + static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN); + static constexpr uint16_t INTERRUPT_MASK = (START_PIN | SELECT_PIN); + static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN ); // pins are active low so invert them + static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; + static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; + static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; + static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; +}; diff --git a/components/box-emu/include/emu_v1.hpp b/components/box-emu/include/emu_v1.hpp new file mode 100644 index 0000000..5c614f7 --- /dev/null +++ b/components/box-emu/include/emu_v1.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "aw9523.hpp" +#include "oneshot_adc.hpp" + +class EmuV1 { +public: + using InputDriver = espp::Aw9523; + static constexpr gpio_num_t VBAT_SENSE_PIN = GPIO_NUM_14; // battery sense pin is on GPIO 14 + static constexpr gpio_num_t AW9523_INT_PIN = GPIO_NUM_21; // interrupt pin is on GPIO 21 + static constexpr uint16_t UP_PIN = (1<<0) << 0; // up pin is on port 0 of the AW9523 + static constexpr uint16_t DOWN_PIN = (1<<1) << 0; // down pin is on port 0 of the AW9523 + static constexpr uint16_t LEFT_PIN = (1<<2) << 0; // left pin is on port 0 of the AW9523 + static constexpr uint16_t RIGHT_PIN = (1<<3) << 0; // right pin is on port 0 of the AW9523 + static constexpr uint16_t A_PIN = (1<<4) << 0; // a pin is on port 0 of the AW9523 + static constexpr uint16_t B_PIN = (1<<5) << 0; // b pin is on port 0 of the AW9523 + static constexpr uint16_t X_PIN = (1<<6) << 0; // x pin is on port 0 of the AW9523 + static constexpr uint16_t Y_PIN = (1<<7) << 0; // y pin is on port 0 of the AW9523 + static constexpr uint16_t START_PIN = (1<<0) << 8; // start pin is on port 1 of the AW9523 + static constexpr uint16_t SELECT_PIN = (1<<1) << 8; // select pin is on port 1 of the AW9523 + static constexpr uint16_t BAT_ALERT_PIN = (1<<3) << 8; // battery alert pin is on port 1 of the AW9523 + static constexpr uint16_t VOL_UP_PIN = (1<<4) << 8; // volume up pin is on port 1 of the AW9523 + static constexpr uint16_t VOL_DOWN_PIN = (1<<5) << 8; // volume down pin is on port 1 of the AW9523 + static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); + static constexpr uint16_t INTERRUPT_MASK = (BAT_ALERT_PIN); + static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); // pins are active low so invert them + static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; + static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; + static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; + static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; + + // ADC for the battery voltage, it's on ADC2_CH3, which is IO14 + static constexpr adc_unit_t BATTERY_ADC_UNIT = ADC_UNIT_2; + static constexpr adc_channel_t BATTERY_ADC_CHANNEL = ADC_CHANNEL_3; +}; diff --git a/components/box-emu/include/fs_init.hpp b/components/box-emu/include/fs_init.hpp new file mode 100644 index 0000000..2613087 --- /dev/null +++ b/components/box-emu/include/fs_init.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +#define MOUNT_POINT "/sdcard" + +#include "hal.hpp" +#include "format.hpp" + +void fs_init(); +sdmmc_card_t *get_sdcard(); diff --git a/components/box-emu/include/hal.hpp b/components/box-emu/include/hal.hpp new file mode 100644 index 0000000..343dfaa --- /dev/null +++ b/components/box-emu/include/hal.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include + +#if defined(CONFIG_HARDWARE_BOX) +#include "box.hpp" +#elif defined(CONFIG_HARDWARE_BOX_3) +#include "box_3.hpp" +#else +#error "Unsupported hardware configuration specified" +#endif diff --git a/components/box-emu/include/hal_events.hpp b/components/box-emu/include/hal_events.hpp new file mode 100644 index 0000000..ee8c691 --- /dev/null +++ b/components/box-emu/include/hal_events.hpp @@ -0,0 +1,7 @@ +#pragma once + +#include "event_manager.hpp" + +static const std::string mute_button_topic = "mute"; +static const std::string battery_topic = "battery"; +static const std::string volume_changed_topic = "volume"; diff --git a/components/box-emu/include/input_state.hpp b/components/box-emu/include/input_state.hpp new file mode 100644 index 0000000..e6ec8fc --- /dev/null +++ b/components/box-emu/include/input_state.hpp @@ -0,0 +1,16 @@ +#pragma once + +struct InputState { + int a : 1; + int b : 1; + int x : 1; + int y : 1; + int select : 1; + int start : 1; + int up : 1; + int down : 1; + int left : 1; + int right : 1; + + bool operator==(const InputState& other) const = default; +}; diff --git a/components/box-emu/include/lvgl_inputs.h b/components/box-emu/include/lvgl_inputs.h new file mode 100644 index 0000000..68cacca --- /dev/null +++ b/components/box-emu/include/lvgl_inputs.h @@ -0,0 +1,16 @@ +#pragma once + +#include "lvgl.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + lv_indev_t *get_keypad_input_device(); + void touchpad_read(unsigned char *num_touch_points, unsigned short* x, unsigned short* y, unsigned char* btn_state); + +#ifdef __cplusplus +} + +#endif diff --git a/components/box-emu/include/make_color.h b/components/box-emu/include/make_color.h new file mode 100644 index 0000000..245c54e --- /dev/null +++ b/components/box-emu/include/make_color.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" +{ +#endif + +uint16_t make_color(uint8_t r, uint8_t g, uint8_t b); + +#ifdef __cplusplus +} +#endif diff --git a/components/box-emu/include/mmap.hpp b/components/box-emu/include/mmap.hpp new file mode 100644 index 0000000..924c1f2 --- /dev/null +++ b/components/box-emu/include/mmap.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include +#include "nvs_flash.h" +#include "spi_flash_mmap.h" +#include "esp_partition.h" + +#include "format.hpp" + +void init_memory(); +size_t copy_romdata_to_cart_partition(const std::string& rom_filename); +uint8_t *get_mmapped_romdata(); diff --git a/components/box-emu/include/usb.hpp b/components/box-emu/include/usb.hpp new file mode 100644 index 0000000..de33e6a --- /dev/null +++ b/components/box-emu/include/usb.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include + +#include "fs_init.hpp" + +bool usb_is_enabled(); +void usb_init(); +void usb_deinit(); diff --git a/components/box-emu/include/video_setting.hpp b/components/box-emu/include/video_setting.hpp new file mode 100644 index 0000000..187b6a0 --- /dev/null +++ b/components/box-emu/include/video_setting.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "format.hpp" + +enum class VideoSetting { ORIGINAL, FIT, FILL, MAX_UNUSED }; + +// for libfmt printing of VideoSetting +template <> +struct fmt::formatter : fmt::formatter { + template + auto format(VideoSetting c, FormatContext& ctx) const { + std::string name; + switch (c) { + case VideoSetting::ORIGINAL: + name = "ORIGINAL"; + break; + case VideoSetting::FIT: + name = "FIT"; + break; + case VideoSetting::FILL: + name = "FILL"; + break; + default: + name = "UNKNOWN"; + break; + } + return fmt::formatter::format(name, ctx); + } +}; diff --git a/components/box-emu/src/battery.cpp b/components/box-emu/src/battery.cpp new file mode 100644 index 0000000..73bb7fc --- /dev/null +++ b/components/box-emu/src/battery.cpp @@ -0,0 +1,113 @@ +#include "box_emu_hal.hpp" + +static std::shared_ptr battery_{nullptr}; +static std::shared_ptr adc_{nullptr}; +static std::shared_ptr battery_task_; +static bool battery_initialized_ = false; +static std::vector channels; + +using namespace std::chrono_literals; + +void hal::battery_init() { + if (battery_initialized_) { + return; + } + fmt::print("Initializing battery...\n"); + auto i2c = hal::get_external_i2c(); + bool can_communicate = i2c->probe_device(espp::Max1704x::DEFAULT_ADDRESS); + if (!can_communicate) { + fmt::print("Could not communicate with battery!\n"); + // go ahead and set the battery_initialized_ flag to true so we don't try to + // initialize the battery again + battery_initialized_ = true; + return; + } + + // // NOTE: we could also make an ADC for measuring battery voltage + // // make the adc channels + // channels.clear(); + // channels.push_back({ + // .unit = BATTERY_ADC_UNIT, + // .channel = BATTERY_ADC_CHANNEL, + // .attenuation = ADC_ATTEN_DB_12}); + // adc_ = std::make_shared(espp::OneshotAdc::Config{ + // .unit = BATTERY_ADC_UNIT, + // .channels = channels + // }); + + // now make the Max17048 that we'll use to get good state of charge, charge + // rate, etc. + battery_ = std::make_shared(espp::Max1704x::Config{ + .device_address = espp::Max1704x::DEFAULT_ADDRESS, + .write = std::bind(&espp::I2c::write, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .read = std::bind(&espp::I2c::read, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) + }); + // NOTE: the MAX17048 is tied to the VBAT for its power supply (as you would + // imagine), this means that we cannnot communicate with it if the battery is + // not connected. Therefore, if we are unable to communicate with the battery + // we will just return and not start the battery task. + battery_task_ = std::make_shared(espp::HighResolutionTimer::Config{ + .name = "battery", + .callback = []() { + std::error_code ec; + // get the voltage (V) + auto voltage = battery_->get_battery_voltage(ec); + if (ec) { + fmt::print("Error getting battery voltage: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + // get the state of charge (%) + auto soc = battery_->get_battery_percentage(ec); + if (ec) { + fmt::print("Error getting battery percentage: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + // get the charge rate (+/- % per hour) + auto charge_rate = battery_->get_battery_charge_rate(ec); + if (ec) { + fmt::print("Error getting battery charge rate: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + + // NOTE: we could also get voltage from the adc for the battery if we + // wanted, but the MAX17048 gives us the same voltage anyway + // auto maybe_mv = adc_->read_mv(channels[0]); + // if (maybe_mv.has_value()) { + // // convert mv -> V and from the voltage divider (R1=R2) to real + // // battery volts + // voltage = maybe_mv.value() / 1000.0f * 2.0f; + // } + + // now publish a BatteryInfo struct to the battery_topic + auto battery_info = BatteryInfo{ + .voltage = voltage, + .level = soc, + .charge_rate = charge_rate, + }; + std::vector battery_info_data; + // fmt::print("Publishing battery info: {}\n", battery_info); + auto bytes_serialized = espp::serialize(battery_info, battery_info_data); + if (bytes_serialized == 0) { + return; + } + espp::EventManager::get().publish(battery_topic, battery_info_data); + return; + }}); + uint64_t battery_period_us = 500 * 1000; // 500ms + battery_task_->periodic(battery_period_us); + battery_initialized_ = true; +} + +std::shared_ptr hal::get_battery() { + battery_init(); + return battery_; +} diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp new file mode 100644 index 0000000..22745bf --- /dev/null +++ b/components/box-emu/src/box-emu.cpp @@ -0,0 +1,122 @@ +#include "box-emu.hpp" + +void BoxEmu::detect() { + bool mcp23x17_found = external_i2c_.probe_device(espp::Mcp23x17::DEFAULT_ADDRESS); + bool aw9523_found = external_i2c_.probe_device(espp::Aw9523::DEFAULT_ADDRESS); + if (aw9523_found) { + // Version 1 + version_ = BoxEmu::Version::V1; + } else if (mcp23x17_found) { + // Version 0 + version_ = BoxEmu::Version::V0; + } else { + logger_.warn("No box detected"); + // No box detected + version_ = BoxEmu::Version::UNKNOWN; + return; + } + logger_.info("version {}", version_); +} + +extern "C" lv_indev_t *get_keypad_input_device() { + auto keypad = espp::EspBox::get().keypad(); + if (!keypad) { + fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); + return nullptr; + } + return keypad->get_input_device(); +} + +bool BoxEmu::initialize_gamepad() { + if (version_ == BoxEmu::Version::V0) { + auto raw_input = new Input( + std::make_shared(version0::InputDriver::Config{ + .port_0_direction_mask = version0::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = version0::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = version0::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = version0::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .write_then_read = std::bind(&espp::I2c::write_read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + .log_level = espp::Logger::Verbosity::WARN + }) + ); + input_.reset(raw_input); + } else if (version_ == BoxEmu::Version::V1) { + auto raw_input = new Input( + std::make_shared(version1::InputDriver::Config{ + .port_0_direction_mask = version1::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = version1::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = version1::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = version1::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .write_then_read = std::bind(&espp::I2c::write_read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + .log_level = espp::Logger::Verbosity::WARN + }) + ); + input_.reset(raw_input); + } else { + return false; + } + + // now initialize the keypad driver + keypad_ = std::make_shared(espp::KeypadInput::Config{ + .read = keypad_read, + .log_level = espp::Logger::Verbosity::WARN + }); + + // now initialize the input timer + input_timer_ = std::make_shared(espp::HighResolutionTimer::Config{ + .name = "Input timer", + .callback = []() { + espp::EspBox::get().update_touch(); + update_gamepad_state(); + }}); + uint64_t period_us = 30 * 1000; + input_timer_->periodic(period_us); + + return true; +} + +InputState BoxEmu::gamepad_state() { + std::lock_guard lock(input_mutex_); + return input_state_; +} + +bool BoxEmu::update_gamepad_state() { + if (!input_) { + return false; + } + if (!can_read_gamepad_) { + return false; + } + std::error_code ec; + auto pins = input_->get_pins(ec); + if (ec) { + logger_.error("Error reading input pins: {}", ec.message()); + can_read_gamepad_ = false; + return false; + } + + auto new_input_state = input_->pins_to_gamepad_state(pins); + bool changed = false; + { + std::lock_guard lock(input_mutex_); + changed = input_state_ != new_input_state; + input_state_ = new_input_state; + } + input_->handle_volume_pins(pins); + + return changed; +} + +void BoxEmu::keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) { + InputState state; + hal::get_input_state(&state); + *up = state.up; + *down = state.down; + *left = state.left; + *right = state.right; + + *enter = state.a || state.start; + *escape = state.b || state.select; +} diff --git a/components/box-emu/src/box_emu_hal.cpp b/components/box-emu/src/box_emu_hal.cpp new file mode 100644 index 0000000..99b1769 --- /dev/null +++ b/components/box-emu/src/box_emu_hal.cpp @@ -0,0 +1,20 @@ +#include "box_emu_hal.hpp" + +void hal::init() { + // init nvs and partition + init_memory(); + // init filesystem + fs_init(); + // init the display subsystem + hal::lcd_init(); + // initialize the i2c buses for touchpad, imu, audio codecs, gamepad, haptics, etc. + hal::i2c_init(); + // init the audio subsystem + hal::audio_init(); + // initialize the rest of the input system which is common to both v0 and v1 + hal::init_input(); + // initialize the video task for the emulators + hal::init_video_task(); + // initialize the battery system + hal::battery_init(); +} diff --git a/components/box-emu/src/fs_init.cpp b/components/box-emu/src/fs_init.cpp new file mode 100644 index 0000000..562f14a --- /dev/null +++ b/components/box-emu/src/fs_init.cpp @@ -0,0 +1,79 @@ +#include "fs_init.hpp" + +static sdmmc_card_t *sdcard = nullptr; + +static void sdcard_init() { + esp_err_t ret; + + // Options for mounting the filesystem. If format_if_mount_failed is set to + // true, SD card will be partitioned and formatted in case when mounting + // fails. + esp_vfs_fat_sdmmc_mount_config_t mount_config; + memset(&mount_config, 0, sizeof(mount_config)); + mount_config.format_if_mount_failed = false; + mount_config.max_files = 5; + mount_config.allocation_unit_size = 16 * 1024; + const char mount_point[] = "/sdcard"; + fmt::print("Initializing SD card\n"); + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. + // Please check its source code and implement error recovery when developing + // production applications. + fmt::print("Using SPI peripheral\n"); + + // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) + // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI) + // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + host.slot = sdcard_spi_num; + // host.max_freq_khz = 20 * 1000; + + spi_bus_config_t bus_cfg; + memset(&bus_cfg, 0, sizeof(bus_cfg)); + bus_cfg.mosi_io_num = sdcard_mosi; + bus_cfg.miso_io_num = sdcard_miso; + bus_cfg.sclk_io_num = sdcard_sclk; + bus_cfg.quadwp_io_num = -1; + bus_cfg.quadhd_io_num = -1; + bus_cfg.max_transfer_sz = 8192; + spi_host_device_t host_id = (spi_host_device_t)host.slot; + ret = spi_bus_initialize(host_id, &bus_cfg, SDSPI_DEFAULT_DMA); + if (ret != ESP_OK) { + fmt::print("Failed to initialize bus.\n"); + return; + } + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = sdcard_cs; + slot_config.host_id = host_id; + + fmt::print("Mounting filesystem\n"); + ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &sdcard); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + fmt::print("Failed to mount filesystem. " + "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.\n"); + } else { + fmt::print("Failed to initialize the card ({}). " + "Make sure SD card lines have pull-up resistors in place.\n", esp_err_to_name(ret)); + } + return; + } + fmt::print("Filesystem mounted\n"); + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, sdcard); +} + +void fs_init() { + if (sdcard) return; + sdcard_init(); +} + +sdmmc_card_t *get_sdcard() { + return sdcard; +} diff --git a/components/box-emu/src/hal_i2c.cpp b/components/box-emu/src/hal_i2c.cpp new file mode 100644 index 0000000..7947836 --- /dev/null +++ b/components/box-emu/src/hal_i2c.cpp @@ -0,0 +1,40 @@ +#include "box_emu_hal.hpp" + +static std::shared_ptr internal_i2c = nullptr; +static std::shared_ptr external_i2c = nullptr; + +static bool initialized = false; + +void hal::i2c_init() { + if (initialized) return; + // make the i2c on core 1 so that the i2c interrupts are handled on core 1 + espp::Task::run_on_core([]() { + internal_i2c = std::make_shared(espp::I2c::Config{ + .port = internal_i2c_port, + .sda_io_num = internal_i2c_sda, + .scl_io_num = internal_i2c_scl, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .timeout_ms = 100, + }); + external_i2c = std::make_shared(espp::I2c::Config{ + .port = external_i2c_port, + .sda_io_num = external_i2c_sda, + .scl_io_num = external_i2c_scl, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE, + .timeout_ms = 100, + }); + }, 1); + initialized = true; +} + +std::shared_ptr hal::get_internal_i2c() { + i2c_init(); + return internal_i2c; +} + +std::shared_ptr hal::get_external_i2c() { + i2c_init(); + return external_i2c; +} diff --git a/components/box-emu/src/i2s_audio.cpp b/components/box-emu/src/i2s_audio.cpp new file mode 100644 index 0000000..79190e3 --- /dev/null +++ b/components/box-emu/src/i2s_audio.cpp @@ -0,0 +1,267 @@ +#include "box_emu_hal.hpp" + +static std::atomic muted_{false}; +static std::atomic volume_{60}; + +static std::shared_ptr mute_button; +static std::unique_ptr audio_task; +static TaskHandle_t main_task_handle = NULL; +static i2s_chan_handle_t tx_handle = NULL; + +// Scratch buffers +namespace hal { +static constexpr int DEFAULT_AUDIO_RATE = 48000; +static constexpr int NUM_CHANNELS = 2; +static constexpr int NUM_BYTES_PER_CHANNEL = 2; +static constexpr int UPDATE_FREQUENCY = 60; +static constexpr int AUDIO_BUFFER_SIZE = DEFAULT_AUDIO_RATE * NUM_CHANNELS * NUM_BYTES_PER_CHANNEL / UPDATE_FREQUENCY; +} +static uint8_t tx_buffer[hal::AUDIO_BUFFER_SIZE]; +static StreamBufferHandle_t tx_audio_stream; + +static i2s_std_config_t std_cfg; + +static i2s_event_callbacks_t tx_callbacks_; +std::atomic has_sound{false}; + +static void update_volume_output() { + if (muted_) { + es8311_codec_set_voice_volume(0); + } else { + es8311_codec_set_voice_volume(volume_); + } +} + +void hal::set_muted(bool mute) { + muted_ = mute; + update_volume_output(); +} + +bool hal::is_muted() { + return muted_; +} + +void hal::set_audio_volume(int percent) { + volume_ = percent; + update_volume_output(); +} + +int hal::get_audio_volume() { + return volume_; +} + +uint32_t hal::get_audio_sample_rate() { + return std_cfg.clk_cfg.sample_rate_hz; +} + +void hal::set_audio_sample_rate(uint32_t sample_rate) { + fmt::print("Setting sample rate to {}\n", sample_rate); + // disable i2s + i2s_channel_disable(tx_handle); + // update the config + std_cfg.clk_cfg.sample_rate_hz = sample_rate; + i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg); + // clear the buffer + xStreamBufferReset(tx_audio_stream); + // re-enable i2s + i2s_channel_enable(tx_handle); +} + +static bool IRAM_ATTR audio_task_fn(std::mutex &m, std::condition_variable &cv) +{ + // Queue the next I2S out frame to write + uint16_t available = xStreamBufferBytesAvailable(tx_audio_stream); + available = std::min(available, hal::AUDIO_BUFFER_SIZE); + uint8_t* buffer = &tx_buffer[0]; + static constexpr int BUFF_SIZE = hal::AUDIO_BUFFER_SIZE; + memset(buffer, 0, BUFF_SIZE); + + if (available == 0) { + i2s_channel_write(tx_handle, buffer, BUFF_SIZE, NULL, portMAX_DELAY); + } else { + xStreamBufferReceive(tx_audio_stream, buffer, available, 0); + i2s_channel_write(tx_handle, buffer, available, NULL, portMAX_DELAY); + } + return false; // don't stop the task +} + +static bool IRAM_ATTR tx_sent_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) +{ + // notify the main task that we're done + vTaskNotifyGiveFromISR(main_task_handle, NULL); + return true; +} + +static esp_err_t i2s_driver_init(void) +{ + fmt::print("initializing i2s driver...\n"); + auto ret_val = ESP_OK; + fmt::print("Using newer I2S standard\n"); + i2s_chan_config_t chan_cfg = { \ + .id = i2s_port, + .role = I2S_ROLE_MASTER, + .dma_desc_num = 16, + .dma_frame_num = 48, + .auto_clear = true, + .intr_priority = 0, + }; + + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, nullptr)); + + std_cfg = { + .clk_cfg = { + .sample_rate_hz = hal::DEFAULT_AUDIO_RATE, + .clk_src = I2S_CLK_SRC_DEFAULT, + .ext_clk_freq_hz = 0, + .mclk_multiple = I2S_MCLK_MULTIPLE_256, + }, + .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), + .gpio_cfg = { + .mclk = i2s_mck_io, + .bclk = i2s_bck_io, + .ws = i2s_ws_io, + .dout = i2s_do_io, + .din = i2s_di_io, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false, + }, + }, + }; + std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256; + + ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); + + tx_audio_stream = xStreamBufferCreate(hal::AUDIO_BUFFER_SIZE*4, 0); + + memset(&tx_callbacks_, 0, sizeof(tx_callbacks_)); + tx_callbacks_.on_sent = tx_sent_callback; + i2s_channel_register_event_callback(tx_handle, &tx_callbacks_, NULL); + + main_task_handle = xTaskGetCurrentTaskHandle(); + + audio_task = std::make_unique(espp::Task::Config{ + .name = "audio task", + .callback = audio_task_fn, + .stack_size_bytes = 1024 * 4, + .priority = 19, + .core_id = 1, + }); + + xStreamBufferReset(tx_audio_stream); + + ESP_ERROR_CHECK(i2s_channel_enable(tx_handle)); + return ret_val; +} + +// es8311 is for audio output codec +static esp_err_t es8311_init_default(void) +{ + fmt::print("initializing es8311 codec...\n"); + esp_err_t ret_val = ESP_OK; + audio_hal_codec_config_t cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.codec_mode = AUDIO_HAL_CODEC_MODE_DECODE; + cfg.dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1; + cfg.i2s_iface.bits = AUDIO_HAL_BIT_LENGTH_16BITS; + cfg.i2s_iface.fmt = AUDIO_HAL_I2S_NORMAL; + cfg.i2s_iface.mode = AUDIO_HAL_MODE_SLAVE; + cfg.i2s_iface.samples = AUDIO_HAL_16K_SAMPLES; + + ret_val |= es8311_codec_init(&cfg); + ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); + ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); + ret_val |= es8311_codec_set_voice_volume(volume_); + ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); + + if (ESP_OK != ret_val) { + fmt::print("Failed initialize codec\n"); + } else { + fmt::print("Codec initialized\n"); + } + + return ret_val; +} + +static std::unique_ptr mute_task; +static QueueHandle_t gpio_evt_queue; + +static void gpio_isr_handler(void *arg) { + uint32_t gpio_num = (uint32_t)arg; + xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +static void init_mute_button(void) { + // register that we publish the mute button state + espp::EventManager::get().add_publisher(mute_button_topic, "i2s_audio"); + + fmt::print("Initializing mute button\n"); + mute_button = std::make_shared(espp::Button::Config{ + .name = "mute button", + .interrupt_config = + { + .gpio_num = mute_pin, + .callback = + [&](const espp::Interrupt::Event &event) { + hal::set_muted(event.active); + // simply publish that the mute button was presssed + espp::EventManager::get().publish(mute_button_topic, {}); + }, + .active_level = espp::Interrupt::ActiveLevel::LOW, + .interrupt_type = espp::Interrupt::Type::ANY_EDGE, + .pullup_enabled = true, + .pulldown_enabled = false, + }, + .task_config = + { + .name = "mute button task", + .stack_size_bytes = 4 * 1024, + .priority = 5, + }, + .log_level = espp::Logger::Verbosity::WARN, + }); + + // update the mute state (since it's a flip-flop and may have been set if we + // restarted without power loss) + hal::set_muted(mute_button->is_pressed()); + + fmt::print("Mute button initialized\n"); +} + +static bool initialized = false; +void hal::audio_init() { + if (initialized) return; + + // Config power control IO + gpio_set_direction(sound_power_pin, GPIO_MODE_OUTPUT); + gpio_set_level(sound_power_pin, 1); + + auto internal_i2c = hal::get_internal_i2c(); + + set_es8311_write(std::bind(&espp::I2c::write, internal_i2c.get(), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); + set_es8311_read(std::bind(&espp::I2c::read_at_register, internal_i2c.get(), + std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); + + i2s_driver_init(); + es8311_init_default(); + + // now initialize the mute gpio + init_mute_button(); + + audio_task->start(); + + fmt::print("Audio initialized\n"); + + initialized = true; +} + +void IRAM_ATTR hal::play_audio(const uint8_t *data, uint32_t num_bytes) { + if (has_sound) { + ulTaskNotifyTake(pdTRUE, portMAX_DELAY); + } + // don't block here + xStreamBufferSendFromISR(tx_audio_stream, data, num_bytes, NULL); + has_sound = true; +} diff --git a/components/box-emu/src/input.cpp b/components/box-emu/src/input.cpp new file mode 100644 index 0000000..9b602d5 --- /dev/null +++ b/components/box-emu/src/input.cpp @@ -0,0 +1,260 @@ + +#include "box_emu_hal.hpp" + +using namespace std::chrono_literals; + +struct TouchpadData { + uint8_t num_touch_points = 0; + uint16_t x = 0; + uint16_t y = 0; + uint8_t btn_state = 0; +}; + +class InputBase { +public: + virtual uint16_t get_pins(std::error_code& ec) = 0; + virtual InputState pins_to_gamepad_state(uint16_t pins) = 0; + virtual void handle_volume_pins(uint16_t pins) = 0; +}; + +template +class Input : public InputBase { +public: + explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} + virtual uint16_t get_pins(std::error_code& ec) override { + auto val = input_driver->get_pins(ec); + if (ec) { + return 0; + } + return val ^ T::INVERT_MASK; + } + virtual InputState pins_to_gamepad_state(uint16_t pins) override { + InputState state; + state.a = (bool)(pins & T::A_PIN); + state.b = (bool)(pins & T::B_PIN); + state.x = (bool)(pins & T::X_PIN); + state.y = (bool)(pins & T::Y_PIN); + state.start = (bool)(pins & T::START_PIN); + state.select = (bool)(pins & T::SELECT_PIN); + state.up = (bool)(pins & T::UP_PIN); + state.down = (bool)(pins & T::DOWN_PIN); + state.left = (bool)(pins & T::LEFT_PIN); + state.right = (bool)(pins & T::RIGHT_PIN); + return state; + } + virtual void handle_volume_pins(uint16_t pins) override { + // check the volume pins and send out events if they're pressed / released + bool volume_up = (bool)(pins & T::VOL_UP_PIN); + bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); + int volume_change = (volume_up * 10) + (volume_down * -10); + if (volume_change != 0) { + // change the volume + int current_volume = hal::get_audio_volume(); + int new_volume = std::clamp(current_volume + volume_change, 0, 100); + hal::set_audio_volume(new_volume); + // send out a volume change event + espp::EventManager::get().publish(volume_changed_topic, {}); + } + } +protected: + std::shared_ptr input_driver; +}; + +static std::shared_ptr input; + +static std::shared_ptr touch_driver; +static std::shared_ptr touchpad; +static std::shared_ptr keypad; +static std::shared_ptr input_timer; +static struct InputState gamepad_state; +static std::mutex gamepad_state_mutex; +static TouchpadData touchpad_data; +static std::mutex touchpad_data_mutex; + +/** + * Touch Controller configuration + */ +void touchpad_read(uint8_t* num_touch_points, uint16_t* x, uint16_t* y, uint8_t* btn_state) { + std::lock_guard lock(touchpad_data_mutex); + *num_touch_points = touchpad_data.num_touch_points; + *x = touchpad_data.x; + *y = touchpad_data.y; + *btn_state = touchpad_data.btn_state; +} + +void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) { + InputState state; + hal::get_input_state(&state); + *up = state.up; + *down = state.down; + *left = state.left; + *right = state.right; + + *enter = state.a || state.start; + *escape = state.b || state.select; +} + +void update_touchpad_input() { + // get the latest data from the device + std::error_code ec; + bool new_data = touch_driver->update(ec); + if (ec) { + fmt::print("error updating touch_driver: {}\n", ec.message()); + std::lock_guard lock(touchpad_data_mutex); + touchpad_data = {}; + return; + } + if (!new_data) { + std::lock_guard lock(touchpad_data_mutex); + touchpad_data = {}; + return; + } + // get the latest data from the touchpad + TouchpadData temp_data; + touch_driver->get_touch_point(&temp_data.num_touch_points, &temp_data.x, &temp_data.y); + temp_data.btn_state = touch_driver->get_home_button_state(); + // update the touchpad data + std::lock_guard lock(touchpad_data_mutex); + touchpad_data = temp_data; +} + +void update_gamepad_input() { + static bool can_read_input = true; + if (!input) { + return; + } + if (!can_read_input) { + return; + } + // pins are active low + // start, select = A0, A1 + std::error_code ec; + auto pins = input->get_pins(ec); + if (ec) { + fmt::print("error getting pins: {}\n", ec.message()); + can_read_input = false; + return; + } + + auto new_gamepad_state = input->pins_to_gamepad_state(pins); + { + std::lock_guard lock(gamepad_state_mutex); + gamepad_state = new_gamepad_state; + } + input->handle_volume_pins(pins); + // TODO: check the battery alert pin and if it's low, send out a battery alert event +} + +static void init_input_generic() { + auto internal_i2c = hal::get_internal_i2c(); + + fmt::print("Initializing touch driver\n"); + touch_driver = std::make_shared(TouchDriver::Config{ + .write = std::bind(&espp::I2c::write, internal_i2c.get(), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .read = std::bind(&espp::I2c::read, internal_i2c.get(), std::placeholders::_1, + std::placeholders::_2, std::placeholders::_3), + .log_level = espp::Logger::Verbosity::WARN + }); + + fmt::print("Initializing touchpad\n"); + touchpad = std::make_shared(espp::TouchpadInput::Config{ + .touchpad_read = touchpad_read, + .swap_xy = touch_swap_xy, + .invert_x = touch_invert_x, + .invert_y = touch_invert_y, + .log_level = espp::Logger::Verbosity::WARN + }); + + auto external_i2c = hal::get_external_i2c(); + + fmt::print("Initializing keypad\n"); + keypad = std::make_shared(espp::KeypadInput::Config{ + .read = keypad_read, + .log_level = espp::Logger::Verbosity::WARN + }); + + fmt::print("Initializing input task\n"); + input_timer = std::make_shared(espp::HighResolutionTimer::Config{ + .name = "Input timer", + .callback = []() { + update_touchpad_input(); + update_gamepad_input(); + }}); + uint64_t period_us = 30 * 1000; + input_timer->periodic(period_us); +} + +static void init_input_v0() { + auto external_i2c = hal::get_external_i2c(); + fmt::print("initializing input driver\n"); + using InputDriver = espp::Mcp23x17; + auto raw_input = new Input( + std::make_shared(InputDriver::Config{ + .port_0_direction_mask = EmuV0::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = EmuV0::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = EmuV0::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = EmuV0::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .read_register = std::bind(&espp::I2c::read_at_register, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .log_level = espp::Logger::Verbosity::WARN + })); + input.reset(raw_input); +} + +static void init_input_v1() { + auto external_i2c = hal::get_external_i2c(); + fmt::print("initializing input driver\n"); + using InputDriver = espp::Aw9523; + auto raw_input = new Input( + std::make_shared(InputDriver::Config{ + .port_0_direction_mask = EmuV1::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = EmuV1::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = EmuV1::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = EmuV1::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .write_then_read = std::bind(&espp::I2c::write_read, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + .log_level = espp::Logger::Verbosity::WARN + })); + input.reset(raw_input); +} + +static std::atomic initialized = false; +void hal::init_input() { + if (initialized) return; + fmt::print("Initializing input subsystem\n"); + // probe the i2c bus for the mcp23x17 (which would be v0) or the aw9523 (which + // would be v1) + auto i2c = hal::get_external_i2c(); + bool mcp23x17_found = i2c->probe_device(espp::Mcp23x17::DEFAULT_ADDRESS); + bool aw9523_found = i2c->probe_device(espp::Aw9523::DEFAULT_ADDRESS); + + if (mcp23x17_found) { + fmt::print("Found MCP23x17, initializing input VERSION 0\n"); + init_input_v0(); + } else if (aw9523_found) { + fmt::print("Found AW9523, initializing input VERSION 1\n"); + init_input_v1(); + } else { + fmt::print("ERROR: No input systems found!\n"); + } + + // now initialize the rest of the input systems which are common to both + // versions + init_input_generic(); + + initialized = true; +} + +extern "C" lv_indev_t *get_keypad_input_device() { + if (!keypad) { + fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); + return nullptr; + } + return keypad->get_input_device(); +} + +void hal::get_input_state(struct InputState* state) { + std::lock_guard lock(gamepad_state_mutex); + *state = gamepad_state; +} diff --git a/components/box-emu/src/mmap.cpp b/components/box-emu/src/mmap.cpp new file mode 100644 index 0000000..83788bc --- /dev/null +++ b/components/box-emu/src/mmap.cpp @@ -0,0 +1,35 @@ +#include "mmap.hpp" +#include +#include "esp_heap_caps.h" + +static uint8_t* romdata = nullptr; + +void init_memory() { + // allocate memory for the ROM and make sure it's on the SPIRAM + romdata = (uint8_t*)heap_caps_malloc(4*1024*1024, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + if (romdata == nullptr) { + fmt::print(fg(fmt::terminal_color::red), "ERROR: Couldn't allocate memory for ROM!\n"); + } +} + +size_t copy_romdata_to_cart_partition(const std::string& rom_filename) { + // load the file data and iteratively copy it over + std::ifstream romfile(rom_filename, std::ios::binary | std::ios::ate); //open file at end + if (!romfile.is_open()) { + fmt::print("Error: ROM file does not exist\n"); + return 0; + } + size_t filesize = romfile.tellg(); // get size from current file pointer location; + romfile.seekg(0, std::ios::beg); //reset file pointer to beginning; + romfile.read((char*)(romdata), filesize); + romfile.close(); + return filesize; +} + +extern "C" uint8_t *osd_getromdata() { + return get_mmapped_romdata(); +} + +uint8_t *get_mmapped_romdata() { + return romdata; +} diff --git a/components/box-emu/src/spi_lcd.cpp b/components/box-emu/src/spi_lcd.cpp new file mode 100644 index 0000000..f77da20 --- /dev/null +++ b/components/box-emu/src/spi_lcd.cpp @@ -0,0 +1,261 @@ +#include "box_emu_hal.hpp" + +static spi_device_handle_t spi; +static spi_device_interface_config_t devcfg; + +static constexpr size_t pixel_buffer_size = lcd_width * hal::NUM_ROWS_IN_FRAME_BUFFER; +static std::shared_ptr display; + +std::shared_ptr hal::get_display() { + return display; +} + +static constexpr size_t frame_buffer_size = (((320) * 2) * 240); +static uint8_t *frame_buffer0; +static uint8_t *frame_buffer1; + +// the user flag for the callbacks does two things: +// 1. Provides the GPIO level for the data/command pin, and +// 2. Sets some bits for other signaling (such as LVGL FLUSH) +static constexpr int FLUSH_BIT = (1 << (int)espp::display_drivers::Flags::FLUSH_BIT); +static constexpr int DC_LEVEL_BIT = (1 << (int)espp::display_drivers::Flags::DC_LEVEL_BIT); + +// This function is called (in irq context!) just before a transmission starts. +// It will set the D/C line to the value indicated in the user field +// (DC_LEVEL_BIT). +static void IRAM_ATTR lcd_spi_pre_transfer_callback(spi_transaction_t *t) +{ + uint32_t user_flags = (uint32_t)(t->user); + bool dc_level = user_flags & DC_LEVEL_BIT; + gpio_set_level(lcd_dc_io, dc_level); +} + +// This function is called (in irq context!) just after a transmission ends. It +// will indicate to lvgl that the next flush is ready to be done if the +// FLUSH_BIT is set. +static void IRAM_ATTR lcd_spi_post_transfer_callback(spi_transaction_t *t) +{ + uint16_t user_flags = (uint32_t)(t->user); + bool should_flush = user_flags & FLUSH_BIT; + if (should_flush) { + lv_disp_t * disp = _lv_refr_get_disp_refreshing(); + lv_disp_flush_ready(disp->driver); + } +} +// Transaction descriptors. Declared static so they're not allocated on the +// stack; we need this memory even when this function is finished because the +// SPI driver needs access to it even while we're already calculating the next +// line. +static const int spi_queue_size = 6; +static spi_transaction_t trans[spi_queue_size]; +static std::atomic num_queued_trans = 0; + +static void IRAM_ATTR lcd_wait_lines() { + spi_transaction_t *rtrans; + esp_err_t ret; + // fmt::print("Waiting for {} queued transactions\n", num_queued_trans); + // Wait for all transactions to be done and get back the results. + while (num_queued_trans) { + ret = spi_device_get_trans_result(spi, &rtrans, 10 / portTICK_PERIOD_MS); + if (ret != ESP_OK) { + fmt::print("Could not get trans result: {} '{}'\n", ret, esp_err_to_name(ret)); + } + num_queued_trans--; + //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. + } +} + + +void IRAM_ATTR hal::lcd_write(const uint8_t *data, size_t length, uint32_t user_data) { + if (length == 0) { + return; + } + lcd_wait_lines(); + esp_err_t ret; + memset(&trans[0], 0, sizeof(spi_transaction_t)); + trans[0].length = length * 8; + trans[0].user = (void*)user_data; + // look at the length of the data and use tx_data if it is <= 32 bits + if (length <= 4) { + // copy the data pointer to trans[0].tx_data + memcpy(trans[0].tx_data, data, length); + trans[0].flags = SPI_TRANS_USE_TXDATA; + } else { + trans[0].tx_buffer = data; + trans[0].flags = 0; + } + ret = spi_device_queue_trans(spi, &trans[0], 10 / portTICK_PERIOD_MS); + if (ret != ESP_OK) { + fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); + } else { + num_queued_trans++; + } +} + +void IRAM_ATTR hal::lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data) { + // if we haven't waited by now, wait here... + lcd_wait_lines(); + esp_err_t ret; + size_t length = (xe-xs+1)*(ye-ys+1)*2; + if (length == 0) { + fmt::print("Bad length: ({},{}) to ({},{})\n", xs, ys, xe, ye); + } + // initialize the spi transactions + for (int i=0; i<6; i++) { + memset(&trans[i], 0, sizeof(spi_transaction_t)); + if ((i&1)==0) { + //Even transfers are commands + trans[i].length = 8; + trans[i].user = (void*)0; + } else { + //Odd transfers are data + trans[i].length = 8*4; + trans[i].user = (void*)DC_LEVEL_BIT; + } + trans[i].flags = SPI_TRANS_USE_TXDATA; + } + trans[0].tx_data[0] = (uint8_t)DisplayDriver::Command::caset; + trans[1].tx_data[0] = (xs)>> 8; + trans[1].tx_data[1] = (xs)&0xff; + trans[1].tx_data[2] = (xe)>>8; + trans[1].tx_data[3] = (xe)&0xff; + trans[2].tx_data[0] = (uint8_t)DisplayDriver::Command::raset; + trans[3].tx_data[0] = (ys)>>8; + trans[3].tx_data[1] = (ys)&0xff; + trans[3].tx_data[2] = (ye)>>8; + trans[3].tx_data[3] = (ye)&0xff; + trans[4].tx_data[0] = (uint8_t)DisplayDriver::Command::ramwr; + trans[5].tx_buffer = data; + trans[5].length = length*8; + // undo SPI_TRANS_USE_TXDATA flag + trans[5].flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL; + // we need to keep the dc bit set, but also add our flags + trans[5].user = (void*)(DC_LEVEL_BIT | user_data); + //Queue all transactions. + for (int i=0; i<6; i++) { + ret = spi_device_queue_trans(spi, &trans[i], 10 / portTICK_PERIOD_MS); + if (ret != ESP_OK) { + fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); + } else { + num_queued_trans++; + } + } + //When we are here, the SPI driver is busy (in the background) getting the + //transactions sent. That happens mostly using DMA, so the CPU doesn't have + //much to do here. We're not going to wait for the transaction to finish + //because we may as well spend the time calculating the next line. When that + //is done, we can call lcd_wait_lines, which will wait for the transfers + //to be done and check their status. +} + +extern "C" uint16_t make_color(uint8_t r, uint8_t g, uint8_t b) { + return lv_color_make(r,g,b).full; +} + +uint16_t* hal::get_vram0() { + return display->vram0(); +} + +uint16_t* hal::get_vram1() { + return display->vram1(); +} + +uint8_t* hal::get_frame_buffer0() { + return frame_buffer0; +} + +uint8_t* hal::get_frame_buffer1() { + return frame_buffer1; +} + +void hal::lcd_write_frame(const uint16_t xs, const uint16_t ys, const uint16_t width, const uint16_t height, const uint8_t * data){ + if (data) { + // have data, fill the area with the color data + lv_area_t area { + .x1 = (lv_coord_t)(xs), + .y1 = (lv_coord_t)(ys), + .x2 = (lv_coord_t)(xs+width-1), + .y2 = (lv_coord_t)(ys+height-1)}; + DisplayDriver::fill(nullptr, &area, (lv_color_t*)data); + } else { + // don't have data, so clear the area (set to 0) + DisplayDriver::clear(xs, ys, width, height); + } +} + +static bool initialized = false; +void hal::lcd_init() { + if (initialized) { + return; + } + esp_err_t ret; + + spi_bus_config_t buscfg; + memset(&buscfg, 0, sizeof(buscfg)); + buscfg.mosi_io_num = lcd_mosi_io; + buscfg.miso_io_num = -1; + buscfg.sclk_io_num = lcd_sclk_io; + buscfg.quadwp_io_num = -1; + buscfg.quadhd_io_num = -1; + buscfg.max_transfer_sz = frame_buffer_size * sizeof(lv_color_t) + 100; + + memset(&devcfg, 0, sizeof(devcfg)); + devcfg.mode = 0; + // devcfg.flags = SPI_DEVICE_NO_RETURN_RESULT; + devcfg.clock_speed_hz = lcd_clock_speed; + devcfg.input_delay_ns = 0; + devcfg.spics_io_num = lcd_cs_io; + devcfg.queue_size = spi_queue_size; + devcfg.pre_cb = lcd_spi_pre_transfer_callback; + devcfg.post_cb = lcd_spi_post_transfer_callback; + + //Initialize the SPI bus + ret = spi_bus_initialize(lcd_spi_num, &buscfg, SPI_DMA_CH_AUTO); + ESP_ERROR_CHECK(ret); + //Attach the LCD to the SPI bus + ret = spi_bus_add_device(lcd_spi_num, &devcfg, &spi); + ESP_ERROR_CHECK(ret); + // initialize the controller + DisplayDriver::initialize(espp::display_drivers::Config{ + .lcd_write = hal::lcd_write, + .lcd_send_lines = hal::lcd_send_lines, + .reset_pin = lcd_reset_io, + .data_command_pin = lcd_dc_io, + .reset_value = reset_value, + .invert_colors = invert_colors, + .mirror_x = mirror_x, + .mirror_y = mirror_y + }); + // initialize the display / lvgl + using namespace std::chrono_literals; + display = std::make_shared(espp::Display::AllocatingConfig{ + .width = lcd_width, + .height = lcd_height, + .pixel_buffer_size = pixel_buffer_size, + .flush_callback = DisplayDriver::flush, + .backlight_pin = backlight_io, + .backlight_on_value = backlight_value, + .task_config = { + .name = "display task", + .priority = 10, + .core_id = 1, + }, + .update_period = 5ms, + .double_buffered = true, + .allocation_flags = MALLOC_CAP_8BIT | MALLOC_CAP_DMA, + .rotation = rotation, + .software_rotation_enabled = true, + }); + + frame_buffer0 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + frame_buffer1 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + initialized = true; +} + +void hal::set_display_brightness(float brightness) { + display->set_brightness(brightness); +} + +float hal::get_display_brightness() { + return display->get_brightness(); +} diff --git a/components/box-emu/src/usb.cpp b/components/box-emu/src/usb.cpp new file mode 100644 index 0000000..db7ba1d --- /dev/null +++ b/components/box-emu/src/usb.cpp @@ -0,0 +1,119 @@ +#include "usb.hpp" + +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) + +enum { + ITF_NUM_MSC = 0, + ITF_NUM_TOTAL +}; + +enum { + EDPT_CTRL_OUT = 0x00, + EDPT_CTRL_IN = 0x80, + + EDPT_MSC_OUT = 0x01, + EDPT_MSC_IN = 0x81, +}; + +static uint8_t const desc_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64), +}; + +static tusb_desc_device_t descriptor_config = { + .bLength = sizeof(descriptor_config), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Finger563", // 1: Manufacturer + "ESP-Box-Emu", // 2: Product + "123456", // 3: Serials + "Box-Emu uSD Card", // 4. MSC +}; + +// callback that is delivered when storage is mounted/unmounted by application. +static void storage_mount_changed_cb(tinyusb_msc_event_t *event) +{ + fmt::print("Storage mounted to application: {}\n", event->mount_changed_data.is_mounted ? "Yes" : "No"); +} + +static bool usb_enabled_ = false; +static usb_phy_handle_t jtag_phy; + +bool usb_is_enabled() { + return usb_enabled_; +} + +void usb_init() { + // get the card from the filesystem initialization + auto card = get_sdcard(); + if (!card) { + fmt::print("No SD card found, skipping USB MSC initialization\n"); + return; + } + + fmt::print("Deleting JTAG PHY\n"); + usb_del_phy(jtag_phy); + + fmt::print("USB MSC initialization\n"); + // register the callback for the storage mount changed event. + const tinyusb_msc_sdmmc_config_t config_sdmmc = { + .card = card, + .callback_mount_changed = storage_mount_changed_cb, + .mount_config = { + .max_files = 5, + } + }; + ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); + ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); + + // initialize the tinyusb stack + fmt::print("USB MSC initialization\n"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &descriptor_config, + .string_descriptor = string_desc_arr, + .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), + .external_phy = false, + .configuration_descriptor = desc_configuration, + }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + fmt::print("USB MSC initialization DONE\n"); + usb_enabled_ = true; +} + +void usb_deinit() { + if (!usb_enabled_) { + return; + } + fmt::print("USB MSC deinitialization\n"); + auto err = tinyusb_driver_uninstall(); + if (err != ESP_OK) { + fmt::print("tinyusb_driver_uninstall failed: {}\n", esp_err_to_name(err)); + } + usb_enabled_ = false; + // and reconnect the CDC port, see: + // https://github.com/espressif/idf-extra-components/pull/229 + usb_phy_config_t phy_conf = { + // NOTE: for some reason, USB_PHY_CTRL_SERIAL_JTAG is not defined in the SDK + // for the ESP32s3 + .controller = (usb_phy_controller_t)1, + }; + usb_new_phy(&phy_conf, &jtag_phy); +} diff --git a/components/box-emu/src/video_setting.cpp b/components/box-emu/src/video_setting.cpp new file mode 100644 index 0000000..d4b59a3 --- /dev/null +++ b/components/box-emu/src/video_setting.cpp @@ -0,0 +1,11 @@ +#include "box_emu_hal.hpp" + +static std::atomic video_setting_{VideoSetting::FIT}; + +VideoSetting hal::get_video_setting() { + return video_setting_; +} + +void hal::set_video_setting(const VideoSetting& setting) { + video_setting_ = setting; +} diff --git a/components/box-emu/src/video_task.cpp b/components/box-emu/src/video_task.cpp new file mode 100644 index 0000000..e0f5320 --- /dev/null +++ b/components/box-emu/src/video_task.cpp @@ -0,0 +1,183 @@ +#include "box_emu_hal.hpp" + +using namespace hal; + +static std::shared_ptr video_task_; +static QueueHandle_t video_queue_; + +static size_t display_width = lcd_width; +static size_t display_height = lcd_height; + +static size_t native_width = lcd_width; +static size_t native_height = lcd_height; +static int native_pitch = lcd_width; + +static const uint16_t* palette = nullptr; +static size_t palette_size = 256; + +static bool video_task(std::mutex &m, std::condition_variable& cv); + +void hal::init_video_task() { + static bool initialized = false; + if (initialized) { + return; + } + fmt::print("initializing video task...\n"); + video_queue_ = xQueueCreate(1, sizeof(uint16_t*)); + video_task_ = std::make_shared(espp::Task::Config{ + .name = "video task", + .callback = video_task, + .stack_size_bytes = 4*1024, + .priority = 20, + .core_id = 1 + }); + video_task_->start(); + initialized = true; +} + +void hal::set_display_size(size_t width, size_t height) { + display_width = width; + display_height = height; +} + +void hal::set_native_size(size_t width, size_t height, int pitch) { + native_width = width; + native_height = height; + native_pitch = pitch == -1 ? width : pitch; +} + +void hal::set_palette(const uint16_t* _palette, size_t size) { + palette = _palette; + palette_size = size; +} + +void hal::push_frame(const void* frame) { + if (video_queue_ == nullptr) { + fmt::print("video queue is null, make sure to call init_video_task() first\n"); + return; + } + xQueueSend(video_queue_, &frame, 10 / portTICK_PERIOD_MS); +} + +static bool has_palette() { + return palette != nullptr; +} + +static bool is_native() { + return native_width == display_width && native_height == display_height; +} + +static int get_x_offset() { + return (lcd_width-display_width)/2; +} + +static int get_y_offset() { + return (lcd_height-display_height)/2; +} + +static const uint16_t* get_palette() { + return palette; +} + +static bool video_task(std::mutex &m, std::condition_variable& cv) { + const void *_frame_ptr; + if (xQueuePeek(video_queue_, &_frame_ptr, 100 / portTICK_PERIOD_MS) != pdTRUE) { + // we couldn't get anything from the queue, return + return false; + } + if (_frame_ptr == nullptr) { + // make sure we clear the queue + xQueueReceive(video_queue_, &_frame_ptr, 10 / portTICK_PERIOD_MS); + // we got a nullptr, return + return false; + } + static constexpr int num_lines_to_write = NUM_ROWS_IN_FRAME_BUFFER; + static int vram_index = 0; // has to be static so that it persists between calls + const int x_offset = get_x_offset(); + const int y_offset = get_y_offset(); + const uint16_t* _palette = get_palette(); + if (is_native()) { + for (int y=0; y(num_lines_to_write, display_height-y); + if (has_palette()) { + const uint8_t* _frame = (const uint8_t*)_frame_ptr; + for (int i=0; i(x_scale * native_width, 0, lcd_width); + for (int y=0; y= max_y) { + break; + } + int source_y = (float)_y * inv_y_scale; + // shoudl i put this around the outer loop or is this loop a good + // balance for perfomance of the check? + if (has_palette()) { + const uint8_t* _frame = (const uint8_t*)_frame_ptr; + // write two pixels (32 bits) at a time because it's faster + for (int x=0; x Date: Tue, 2 Jul 2024 09:31:21 -0500 Subject: [PATCH 05/14] fleshing out impl --- components/box-emu/CMakeLists.txt | 31 +- components/box-emu/example/CMakeLists.txt | 22 + components/box-emu/example/README.md | 39 ++ .../box-emu/example/main/CMakeLists.txt | 3 + .../box-emu/example/main/box_emu_example.cpp | 182 +++++++ components/box-emu/example/main/click.wav | Bin 0 -> 35918 bytes components/box-emu/example/sdkconfig.defaults | 22 + components/box-emu/include/box-emu.hpp | 204 +++++--- components/box-emu/include/box.hpp | 69 --- components/box-emu/include/box_3.hpp | 68 --- components/box-emu/include/box_emu_hal.hpp | 114 ----- components/box-emu/include/emu_v0.hpp | 29 -- components/box-emu/include/emu_v1.hpp | 37 -- components/box-emu/include/fs_init.hpp | 18 - .../{input_state.hpp => gamepad_state.hpp} | 4 +- components/box-emu/include/hal.hpp | 11 - components/box-emu/include/mmap.hpp | 14 - components/box-emu/src/box-emu.cpp | 459 ++++++++++++++++-- components/box-emu/src/box_emu_hal.cpp | 20 - components/box-emu/src/fs_init.cpp | 79 --- components/box-emu/src/hal_i2c.cpp | 40 -- components/box-emu/src/i2s_audio.cpp | 267 ---------- components/box-emu/src/input.cpp | 260 ---------- components/box-emu/src/mmap.cpp | 35 -- components/box-emu/src/spi_lcd.cpp | 261 ---------- components/box-emu/src/video_setting.cpp | 11 - components/box-emu/src/video_task.cpp | 183 ------- components/events/CMakeLists.txt | 3 + .../include/events.hpp} | 2 + components/statistics/CMakeLists.txt | 4 + components/statistics/include/statistics.hpp | 17 + components/statistics/src/statistics.cpp | 59 +++ 32 files changed, 941 insertions(+), 1626 deletions(-) create mode 100644 components/box-emu/example/CMakeLists.txt create mode 100644 components/box-emu/example/README.md create mode 100644 components/box-emu/example/main/CMakeLists.txt create mode 100644 components/box-emu/example/main/box_emu_example.cpp create mode 100644 components/box-emu/example/main/click.wav create mode 100644 components/box-emu/example/sdkconfig.defaults delete mode 100644 components/box-emu/include/box.hpp delete mode 100644 components/box-emu/include/box_3.hpp delete mode 100644 components/box-emu/include/box_emu_hal.hpp delete mode 100644 components/box-emu/include/emu_v0.hpp delete mode 100644 components/box-emu/include/emu_v1.hpp delete mode 100644 components/box-emu/include/fs_init.hpp rename components/box-emu/include/{input_state.hpp => gamepad_state.hpp} (66%) delete mode 100644 components/box-emu/include/hal.hpp delete mode 100644 components/box-emu/include/mmap.hpp delete mode 100644 components/box-emu/src/box_emu_hal.cpp delete mode 100644 components/box-emu/src/fs_init.cpp delete mode 100644 components/box-emu/src/hal_i2c.cpp delete mode 100644 components/box-emu/src/i2s_audio.cpp delete mode 100644 components/box-emu/src/input.cpp delete mode 100644 components/box-emu/src/mmap.cpp delete mode 100644 components/box-emu/src/spi_lcd.cpp delete mode 100644 components/box-emu/src/video_setting.cpp delete mode 100644 components/box-emu/src/video_task.cpp create mode 100644 components/events/CMakeLists.txt rename components/{box-emu/include/hal_events.hpp => events/include/events.hpp} (91%) create mode 100644 components/statistics/CMakeLists.txt create mode 100644 components/statistics/include/statistics.hpp create mode 100644 components/statistics/src/statistics.cpp diff --git a/components/box-emu/CMakeLists.txt b/components/box-emu/CMakeLists.txt index 308ab12..fe11f99 100644 --- a/components/box-emu/CMakeLists.txt +++ b/components/box-emu/CMakeLists.txt @@ -1,5 +1,34 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" - REQUIRES "driver" "heap" "fatfs" "esp_lcd" "esp_psram" "hal" "usb" "esp_tinyusb" "spi_flash" "nvs_flash" "codec" "adc" "aw9523" "button" "display" "display_drivers" "mcp23x17" "input_drivers" "tt21100" "gt911" "drv2605" "event_manager" "i2c" "task" "timer" "serialization" "max1704x" "esp-box" + REQUIRES + "driver" + "heap" + "fatfs" + "esp_lcd" + "esp_psram" + "hal" + "usb" + "esp_tinyusb" + "spi_flash" + "nvs_flash" + "codec" + "adc" + "aw9523" + "button" + "display" + "display_drivers" + "mcp23x17" + "input_drivers" + "tt21100" + "gt911" + "drv2605" + "event_manager" + "events" + "task" + "timer" + "serialization" + "statistics" + "max1704x" + "esp-box" ) diff --git a/components/box-emu/example/CMakeLists.txt b/components/box-emu/example/CMakeLists.txt new file mode 100644 index 0000000..b909df9 --- /dev/null +++ b/components/box-emu/example/CMakeLists.txt @@ -0,0 +1,22 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +# add the component directories that we want to use +set(EXTRA_COMPONENT_DIRS + "../../../components/" + "../../../components/espp/components/" +) + +set( + COMPONENTS + "main esptool_py box-emu" + CACHE STRING + "List of components to include" + ) + +project(box_emu_example) + +set(CMAKE_CXX_STANDARD 20) diff --git a/components/box-emu/example/README.md b/components/box-emu/example/README.md new file mode 100644 index 0000000..b7245e1 --- /dev/null +++ b/components/box-emu/example/README.md @@ -0,0 +1,39 @@ +# ESP-BOX Example + +This example shows how to use the `espp::EspBox` hardware abstraction component +to automatically detect and initialize components on both the ESP32-S3-BOX and +the ESP32-S3-BOX-3. + +It initializes the touch, display, and audio subsystems. It reads the touchpad +state and each time you touch the scren it 1) uses LVGL to draw a circle where +you touch, and 2) play a click sound (wav file bundled with the firmware). If +you press the home button on the display, it will clear the circles. + +## How to use example + +### Hardware Required + +This example is designed to run on the ESP32-S3-BOX or ESP32-S3-BOX-3. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view +serial output: + +``` +idf.py -p PORT flash monitor +``` + +(Replace PORT with the name of the serial port to use.) + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +BOX3: +![CleanShot 2024-07-01 at 08 47 31](https://github.com/esp-cpp/espp/assets/213467/27cdec8e-6db0-4e3d-8fd6-91052ce2ad92) + +BOX: +![CleanShot 2024-07-01 at 09 56 23](https://github.com/esp-cpp/espp/assets/213467/2f758ff5-a82e-4620-896e-99223010f013) diff --git a/components/box-emu/example/main/CMakeLists.txt b/components/box-emu/example/main/CMakeLists.txt new file mode 100644 index 0000000..a3e606e --- /dev/null +++ b/components/box-emu/example/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRC_DIRS "." + INCLUDE_DIRS "." + EMBED_TXTFILES click.wav) diff --git a/components/box-emu/example/main/box_emu_example.cpp b/components/box-emu/example/main/box_emu_example.cpp new file mode 100644 index 0000000..2ddd2d8 --- /dev/null +++ b/components/box-emu/example/main/box_emu_example.cpp @@ -0,0 +1,182 @@ +#include +#include +#include + +#include "esp-box.hpp" + +using namespace std::chrono_literals; + +static std::vector circles; +static std::vector audio_bytes; + +static void draw_circle(int x0, int y0, int radius); +static void clear_circles(); + +static size_t load_audio(); +static void play_click(espp::EspBox &box); + +extern "C" void app_main(void) { + espp::Logger logger({.tag = "ESP BOX Example", .level = espp::Logger::Verbosity::INFO}); + logger.info("Starting example!"); + + //! [esp box example] + BoxEmu &emu = BoxEmu::get(); + emu.set_log_level(espp::Logger::Verbosity::INFO); + espp::EspBox &box = espp::EspBox::get(); + logger.info("Running on {}", box.box_type()); + logger.info("Box Emu version: {}", emu.version()); + + // initialize + if (!emu.initialize_box()) { + logger.error("Failed to initialize box!"); + return; + } + + if (!emu.initialize_sdcard()) { + logger.error("Failed to initialize SD card!"); + return; + } + + if (!emu.initialize_memory()) { + logger.error("Failed to initialize memory!"); + return; + } + + if (!emu.initialize_gamepad()) { + logger.error("Failed to initialize gamepad!"); + return; + } + + if (!emu.initialize_battery()) { + logger.error("Failed to initialize battery!"); + return; + } + + if (!emu.initialize_video()) { + logger.error("Failed to initialize video!"); + return; + } + + // set the pixel buffer to be 50 lines high + static constexpr size_t pixel_buffer_size = box.lcd_width() * 50; + // initialize the LVGL display for the esp-box + if (!box.initialize_display(pixel_buffer_size)) { + logger.error("Failed to initialize display!"); + return; + } + + // set the background color to black + lv_obj_t *bg = lv_obj_create(lv_scr_act()); + lv_obj_set_size(bg, box.lcd_width(), box.lcd_height()); + lv_obj_set_style_bg_color(bg, lv_color_make(0, 0, 0), 0); + + // add text in the center of the screen + lv_obj_t *label = lv_label_create(lv_scr_act()); + lv_label_set_text(label, "Touch the screen!\nPress the home button to clear circles."); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); + lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0); + + // start a simple thread to do the lv_task_handler every 16ms + espp::Task lv_task({.callback = [](std::mutex &m, std::condition_variable &cv) -> bool { + lv_task_handler(); + std::unique_lock lock(m); + cv.wait_for(lock, 16ms); + return false; + }, + .task_config = { + .name = "lv_task", + }}); + lv_task.start(); + + // load the audio file (wav file bundled in memory) + size_t wav_size = load_audio(); + logger.info("Loaded {} bytes of audio", wav_size); + + // unmute the audio and set the volume to 60% + box.mute(false); + box.volume(60.0f); + + // set the display brightness to be 75% + box.brightness(75.0f); + + auto previous_touchpad_data = box.touchpad_convert(box.touchpad_data()); + while (true) { + std::this_thread::sleep_for(100ms); + if (box.update_touch()) { + // NOTE: since we're directly using the touchpad data, and not using the + // TouchpadInput + LVGL, we'll need to ensure the touchpad data is + // converted into proper screen coordinates instead of simply using the + // raw values. + auto touchpad_data = box.touchpad_convert(box.touchpad_data()); + if (touchpad_data != previous_touchpad_data) { + logger.info("Touch: {}", touchpad_data); + previous_touchpad_data = touchpad_data; + // if the button is pressed, clear the circles + if (touchpad_data.btn_state) { + clear_circles(); + } + // if there is a touch point, draw a circle and play a click sound + if (touchpad_data.num_touch_points > 0) { + draw_circle(touchpad_data.x, touchpad_data.y, 10); + play_click(box); + } + } + } + } + //! [esp box example] +} + +static void draw_circle(int x0, int y0, int radius) { + lv_obj_t *my_Cir = lv_obj_create(lv_scr_act()); + lv_obj_set_scrollbar_mode(my_Cir, LV_SCROLLBAR_MODE_OFF); + lv_obj_set_size(my_Cir, 42, 42); + lv_obj_set_pos(my_Cir, x0 - 21, y0 - 21); + lv_obj_set_style_radius(my_Cir, LV_RADIUS_CIRCLE, 0); + circles.push_back(my_Cir); +} + +static void clear_circles() { + // remove the circles from lvgl + for (auto circle : circles) { + lv_obj_del(circle); + } + // clear the vector + circles.clear(); +} + +static size_t load_audio() { + // if the audio_bytes vector is already populated, return the size + if (audio_bytes.size() > 0) { + return audio_bytes.size(); + } + + // these are configured in the CMakeLists.txt file + extern const char wav_start[] asm("_binary_click_wav_start"); + extern const char wav_end[] asm("_binary_click_wav_end"); + + // -1 due to the size being 1 byte too large, I think because end is the byte + // immediately after the last byte in the memory but I'm not sure - cmm 2022-08-20 + // + // Suppression as these are linker symbols and cppcheck doesn't know how to ensure + // they are the same object + // cppcheck-suppress comparePointers + size_t wav_size = (wav_end - wav_start) - 1; + FILE *fp = fmemopen((void *)wav_start, wav_size, "rb"); + // read the file into the audio_bytes vector + audio_bytes.resize(wav_size); + fread(audio_bytes.data(), 1, wav_size, fp); + fclose(fp); + return wav_size; +} + +static void play_click(espp::EspBox &box) { + // use the box.play_audio() function to play a sound, breaking it into + // audio_buffer_size chunks + auto audio_buffer_size = box.audio_buffer_size(); + size_t offset = 0; + while (offset < audio_bytes.size()) { + size_t bytes_to_play = std::min(audio_buffer_size, audio_bytes.size() - offset); + box.play_audio(audio_bytes.data() + offset, bytes_to_play); + offset += bytes_to_play; + } +} diff --git a/components/box-emu/example/main/click.wav b/components/box-emu/example/main/click.wav new file mode 100644 index 0000000000000000000000000000000000000000..2183344a236219ea90c0b6d27b792c93542eda0f GIT binary patch literal 35918 zcmeHw3AjyV`}ebkJ)C(obI37!WgZhrlu(GGSrUl`Aw?-tNh%3NNQRPGk*P9;kjU&H z(=nZSpL6!O)_T6*eb&3awb$PJ9Nzc;eb@DU*VR7nbDrtGfA{=6&pvha_UYEOYuAq$ zd!*CDU7i~;szxGXOkg6rX9Z)4YegorME2~!Q3FR!VNB&Juj75Hc!u{mw@|#!d%r)O z?TPIiyRFK(a!V`o^4}(WpSVvx>G1mpvpYTg)l-_x+uJ(v7X$UY+io<>X>jqK%w87{ zUTS)luH%^E}N ztV!En`&xQh-KT4xt6i|C*a)%BQ{}e(t!E~F+Xp9+<(R>?hf@~w0I4qrX;IgZn(U;tNGt;-)zg;4sDy(q_EjrHTTzz zPW`rWH^+jwgGy&%vg=4uAoHuMlTPkCJ?mif;gS1i9wgI`8sb_|D)yul~-1+ob+&t zU-H|(C?4(Wc)eTB(--<(ZgVd8+-GMSp1JL8?&&6HyJz_S9(Jzr)y|m{U9<9j5FHwGnQYHE%zRhzq{1^!sRP#E(^9@IcP9E++;yS5t**a`>Mx9S@5$YjmwUa_jh`~l zWDdUc?4>%F=Ur-gZOxTYx!$ZH#e-Z~fr`GX!tdPgm|=f8zJJ{0)HhQ0RV=D-du3kf zql(W~+MT+%eC@P~pCUUEU_1cllOzp?#6OGxkz+e&X7M1}W#0E2Msx+A_6Wxo1))rN$+- zPu?5bH2%1KnX@H(Mr`2kro3Ft-8TwWx|-zOpO=zzAiF4QV%B@v7qU9$HOxI(*ueF$ zua#$=_K7l9+A8dI%(tJ7X&kdKp+&;jFrkX$R}%YiateMi#&5e!bucS5n>=dGF@FlKV;CZ@Hfp+>>9UXo0J)XKQhmf06Hf-dX!x zZZEZpy6X5Qc1g@9@pa>yB)pX1ieH@|#(Uzs#NHh@G-`D8Fxz7L4%SR;uWVG`^=|i_ zD*nQqSaeU(S=Vw`2iNI>_gxNGwW8+>1&>m^-T$I*t=3+>NA$4Xwt;f7W4NP7baZs> z*qB&nT*bJ#v4ye6qd$wO=xiG0l{?upg{o4qmdlfr-<3Oj2Yo-gTY4PDeT!c$8d(%o z{B_ZI_nP9N-se2C{WE>3Y9qzPYw|>?yBKTlV;kW->NpbhUR2BIzR@S6`bGPltD+V; z20J&|#@ky+nsg_tD6~h9#(=kDp<;i=^x>szG! z5g5WpYh0)()R*6ouGrqO^>Ea8^mjh(%y5cP^PE?l;~jp-eA@u~!%`br5;BDo+HC%} z@`4&27#0}rTj6W(eagGdv&mE5d&pDWH{1J_|BA1_@>!szHePMYKIh%VUBaXCB`L+e z*4D`3wSVOJ!qL?+(=o~Ja16EC?20r;ZX+HR>#-e-Xb3;uv_bD(yhr@BP( zYyGv=tQH$277ASYPI}aqX6tOPZ~wx+-2RAto_&@r+CEN>u~n7UOOu5P;tqa4D^N#h z9hL8u%l;+-*`MxT?)%*LyKkber~k6ASzx_?jFJ~f(k7~xd2jxU&{TL%$`{wmL*=h* zZ`-Qdo7vmiN7^%O?d;`k@7j(?kH{^=ed16i3yZauJVBkPZVJfCO8=jJ#pm(;`}w_rmje^j%SwIzv38u@&t4Ikm??cORkdZw`L^@6F80Um3HILh0k&PXj_}3b z;wb4s;i9mJH(h$yJkZ|X-+$J(->3Mt`abm6_P-YJ`!_0$l@8ikwU9s0 z*Fxv+(s^;V{DAzH?P;6aX15E_dV$Sr`%CU*n=e(D%ZrP}rKUrT?Muh2i$pQg+U%u$`{U3|Hg#hzhri3#FGX@(@(j?2H$sZ_K>_6;Y7ig|jRGSGKwR!T3?0~(Z zSQIr|+7N$O){Pu?i>Mdw`g4a|SYpIX>7u&j8P^0V){I)#76)9hV@aj_T0u1Q_wjw!utMalWL z*2$IZ)06huya}>^$j8!eO~a%D1+D;@*^J zIsYXcm*xxocmdz;@1k8Q-m2u}zYxgIUgK+;mEjh%d%54rEpo3aSm3KuY}fL)NA$Fnoe$J7;)$A3}vdzX2? zc5f=0Uf9}oG5l z(J9;G+9gj+_&mX%P(F5X{1?uzW4Qd8V;bu&l?yz}hZJ8AEY5%5yE>a1g#BVSCl*|h57&%VQ2U-z)U zPlb98_D_}Q1hU-4{Ha*F5pae${99Attly5Yg>P|$Ns4> z+p$z!;`l-;@8~1fu&Q&!Y z%2fY7%7Q>QrH(pNVLVQ~#ulqzh)-!(rB3`$`EEAIc9-z2t-Sc5?OQQd&KLiX`b)ov zhx{q-*InImUoHNnFe7T6?MU^C6<4LZt4>RKF?oobWy|L4we=o%k^AzenTL;6J>CDL zcxJ}6_1B&%8c~$y^#-02vh6#P&z9q9ORIOTdc0a%(qE}O=HB>Wu^&WNvcDiU^Br;5 za&^pq*yAjGfvxv-7B4E*S#7PRJW%>G=Dz3=u~TE>rTc9^ct2Lh7U%fZ6)o~y$nEIr zeJwVp>5YN8?xJb#J$wSsm#f+ykE#{zOnxe9W~Ec*->CR}xy=a~aXlRCY~P5p_)p3@ z?=P-(c~@^Vz1}hFi|Z2#9?DbPON$%%D+ku|w&HirwlS#*E0dRdCOARu>B`q)(g`qY`54#QqXDhg^8XEg?+ux7u)>_-pT4D<*GPE+~`PmZjE{&W@yxd zu@5?T#=KxZ9<^4q+uu-kvP!=5fd-xf-in?V@uIrZGuJo7+g0r!s4NcQ`L-N!f#Zz4 z+cCn{z!7h|)Aow=rtlQIq|8^k`{w%I^HlT3cs!mNo+#g9-}JyF^&?(UjFPw6);aPW zH=-&>9gglAH8}c^qpR~X`2%?!tIFO|8z{X3clt7XPkNs49d-}(7kMryGyR+RNi|3K zi#;kek($UEa$|Xt?RRmr+={Id-%(qzw*q#ptAD7n!#6t6+dnbTTlquj&z*dvI9@1} zXGnYP)okY-Ikp{+?`^g0mF1_US;8~yg0?|@TNxi%<-h1#-E!HbL4T)Rx_NpS&pjDprtUgz92*eu|AzCv#bOL~9!ON*x$jp^jBXX_d6qxP{_{ zeZrIC%hF)!9r=v3Uj9fbFHaKBi(YoWP?LYmd#YLLuYqmKG5=%AVgDk$D;`h-3Rl-^ zH?$Ag@4SonE&D`TCNz^rijCy);!J71FhQKc(uAS>Xa2XgPJ4zoN9HxqS_@;e*93vj z6V~#B!ei`+u$N5}PO&R&Ci{XXvG3IWd|P0$w!(i-UFct?J{u@jzg8A&esv~)gfC}q zwnxYk*NfZbyQK5B?oyIHLwwFAi?Q-K_PqEWS6N5x3h%9+)Q&4Zs|S^XN)z<~#ic%@ zcr{H)V!x?&;T~;+Fi1-gU(_m#-)nyfRrr0v&-@$q4*uS8yYMYvA-u|4im!4me#;8Z|YWUw9-cF7RXUY`SaAqxXV}jZ)pC&A-+dF z3VRL+)r4tcA5q22Dq4z@*NPKmzc5v1LY$n7u(3YvwMW8Y_WKnZI)^Z z6Xl*lK)z3?U~>xVAG<@i`OKs%)EQpc;W;Jh5EmQxO>R|BtUJn$GFt~|n8 ztKEdoTAbLAzbCe4OT|Cf3u3x(MR-EEU05g#WCg-}_(b4+#jlWKW3&e1N17%a(Dn-H z{AFPrKg_oCi~I`TiSJn7s~P-H)$Whu>tU)3jtby{y>6>ldj zX4Qqag&VBCIGRln7w}o)SnVlspel&lmAS$OrGrorH_#^abM}VTp7rM!_)s>LuNLm$ z`C_(qM4G8pky~i{q?2l@G+%8eeyt7`vec>Ub!{eZ!>4PBY@M3N>Z-d1r~0;dw<=5X z)Rt1FDoItfk>YS|u8^lKVRQM9ydOKS`B*D$EcCl9e4suq&QvepuI?-i(t1iBEmNF? zldg~_3SIG6yLVZU_9MPEWiXH8W3`nyVO5}tur1I~7^K`OyojiOqjeJg*V*sNE>>RUY`t1jcvq_`TV^c{Y+W;i^{5 zMr(UnEv-8%Qje+U)hvIZ`ntb^_PMrS`%#|G?{dDxK6KnAT(Mmi=Gq<<8_REt-|^SQ zC;TnNYo4{j7O$KA=&Qy)^mXT}{0p^VS_|z}QP3n=Rr^Rav?OVWc3e8Fb(0?A3)v}D zb$50^oy%hR0XBu-U=6uV2=FxFUa<+7nhMjU%7P{ov2#e!9j_RGfsXB+h ztcs}D=hz5s6H8@9?7C1{C>C!Q>PWW>@lrYAnD{r-gs<77Y?tq{%E+dPPVQW5o@8oY+&HE`F*+i$hgW=%W?#Gupp+HMT(OBevDz z3`@aZSDxV?3bE>8VRB%zunhVBvy#p>tBv__?IrC1JENva6V);D z47D$K-;}EIC1NTYDCDwMe5G(vxkFqKct%_taEi_GUcRV&fqUy5@5XNDBgCcJ9BHh! zOZripBsJ!mxRL)VbYR=~uk4&!O}L=cgSStzJxX8JT;0Q;)b8T{V!vqzh1a!p;yW6X z{?_c$i~JighP^L*&x(0R{N-Vh;8A}NHmk#hjcPti*B*l&oB4cJ4LQ9+Yb(B|^~XKc z5U1~(!WiBff203Z`;^UBHKr(yU|SX8ymFS!Q{Q2QS^|3=_s$lf6aP|7!?&N?c{k~4 zepp<=_X#n$V~4R1aYAm?wzHbrQkJbgkNYx(acvbp&L7~DSu!fi$p;Ak;=OPS9%0A1 z9e?d;$v3l)wOFB&))c2!U7@z-Vn?(u*)Cp}{m9nvcZA-&hFFo05=Fe&WxhkG#jmm_ z@y`ra@^swlBeXG0Kqd*=PBv7VgDU9DcJp{Pf#vYqgd#p!5ZMQU#G0dD!BSW(qYTh1vKk`plQ!WUHwI0F` z+92VacAs!JDs3NMg};CF$Lm32gK@*p5PI;@f}K|pGPN(53+I12e~J&}=d~I9D{T{> zrDfo|yufPk+H5xO%F44R@j4iaznKli-veG^o7oiTzJfJkzp)d17yFVgX7BP5Y!+__ zyRPyWwva#0`tqgB$^T;ev}0^J{?fHkdlnXP{t#YNTkwLY#;(CHCvXnAv>2foZ!Jvc zcf$jbgK;=J79n5%=B-#di)YWVvp9n`@MBozY%#3f%#o`&87s04tPdN&CNVeg=faBK z{9{&yKgIl7WtOY$=c+cDH{gBvi@Y^na;>=oR&~V9HW>HoI93^cc$7_NAF@vvJiwN~ zH`9POh~0}5_YP)f&G1&bjqTwQYUC8(%fIKR`CG8z3H-h0KJH{4xsBb!-TX0rmVdyv z^Zl@`3j2x=XY=`LSbT<^sr=@ z{RCaV1L8FJWf+^oy0Yd=4!Ci7((M_!qng zErxx=E3%(p+fnphp1^7_2kVJ{DmNDT&1L1_g}P|%f&Kt{71`Ypb~zyX5UO%M{QDX| z#GizQ{qcQr2(+DyELp|hKpf`7AAj;1h)-KYsT&)@dLw#$*vG6po6I_~5v(;n9>Ru|5zsr{m?G0sUt1I&1(mX~SEx7Q6+!57jUj-g^i3&gYqY2dX3+ zKCj5u@(yT^K>Bm+Ab*uz6&Zvnl$n93FK9JqKEqIS;AM$qiyYl0# zCSDxTe3OvOM+t3s2Vo>n6*lnE@YNu^Cnsv}vIn(a*<|esn~#%nwRWD>;~SX9C&G%J z(7X!zEadlAoEU!k@Kfo#UAp03HXu^*0O^A$57>+u>4<~sm{u65tSp&F; z7syEdD^G)za=a#MfOF+e_+bQp1LyrjWa&6$?(@jpUZ{(~%hcJSQHegh%*j9zbTKv1f4qO~Q%1 z8@EFu^1eGNbtI~58uEA+kR~94_v6J9hfK*p4QzxL=ObHZ@qK(b>gyz`EEeZrE4G9` zgj?e|d~X?o^RXu~sS3`AOYp@HxSc*mZj1)S%Q#2h=j~93^;j-XMyC0AZ=7jk*gklB z3;g*5ABc0R6;z{7@0nfKfzYuK72-Nfb4Rzt=c6n z@|}D-V(<`Kf!FO%+|T~tC)f={AqwYmJywC;$v)zpSr>jMdmN|Pr@R8F6z)OYy}FoqO0`uHY6p!Y=X! z@Je6Yzh_Wy10W}ccLCqs$j+AResIh}oyP+8U7Y6oVcTiM?+@Gx%Tap+5vKxtpLid4 zL{DCcb%5M1z!?O&%OE!abvX>*Wago*#=oJk5^Xx#1Mr=jN3)g4_76Ze4*djR%;$NC z_i0#q09mvLb-j!;cGqJ(br-OG8a3;#3#llLF+$1wIU?@q6r7J`Eloi;U`t%&G+aGa&~NMfN7* z-Ik8Cw*_L?2^n=Ci^1*H9I@?%tRH~feE=)XamFO!RL$X8{15PMuM0t2jgZDFG$wA!d+jt>fBqY58&bm76 zI&TWzR`5hCc%&(7gWIMZBsarJa2s@vgCrj^;|6?j0TDZg{vuu-H}KnyGgoB!(17k) z4NU@<1DfEzLDX}33b-plqw3JHCcKr7>Z*Y^LKXZ@g=eDhR#5Pw%Y`-PkzvR1#yJRS ze_(VNKd1OnXnqztXF-yJD8<4HmB3jOH%x8RX*zzY;wK4sR4poA8^y>i7b4>VUolQz zA97m*GDB7gxIsAd_Va7V!s}SghAl3Pd>DziHDh2&IarVeJ!?bHdN{}Hz$-N%sXSyy zBV$yY1O?!wH_16<$mx7*76}Q06aUQ7>`#+GJdB(!z7Gi(HzJJ32O{9@|A!l z!xs+FMx!EV#Q|#t=7F9rNXmwO*ATrch`|-~*P(MBZ1aMKWW*q1Dewt-rYiKTg5MP( zDY*pA2dX^ia~1x$48BZgejR-tklpZt3LNsT4cd^-He`f^Ucj6h<(3Vjs1mL1Ky|{~ z(U2Gm*|E?y8XmF3cLMS$03C}FvjTV{7u7@_&ODv#`B8l`;t_>fjlzA6(*}AkLR$gsHF)#vz{z$U zd#RvlfL0%^F0^d`8)~CE;;`!~$>!70=QMOZ2kSF&{^g<`=*`5kz@wL%u(&aR@a@=&8*SS?~T24`V1 zT6vrdl~9w_P`S6k-dcDa)IzlIPdea(n(#;(Je>^dRK)5U^gDx_JOl5!bNqKi?l8P^ z7GBE1d80xjIytMt#s)xXj(E3*cbY?T1DuU%sQ+Y~%y#qvSeTDUUxjDR!_KqVJqr!~ zMqOUO{1VQOE7;G1Ocy_o7@Ps$8PxM7oO=a$@rv+b0z6X*tvbfHm7GP1(3P^Kuq3zg z!F2<5dyW4Eq@#%98C2X=M5GXP%VB*K&a`A?O+}2VmehR#e0mK#7eMhh{B#kTWFs10 z)ToSjC!m%|1G*2=P`MQmzeI4@(JRQSBIuh7`?BGcyb|gPCAboDEBfF06%XH1&C2kt z3J$U}AKuJHo>HT+2lc>_v5pd%Qb~4~M+Q*U#-Z8KG)Q*iRHO4Q2f3C5nFY|*4J+y2 z^P}w6&%JzTl7o983shO4$i{9yYRnCvXwbl3g5yJcRGg3^q!4}-=vQc3vol>?@ZA7D&&wcR!jlKphAG=aP(=o`YW z>adf})B?z$nmq;yN1^9Qc#_U156-7}aHqjvb>O#p$dp>(p**!C{zdTQHE47Z{YAv} z3gYTQyy=WghCWrGR~q!I3O!R16&a20pNnXx(2k*RPD6+pfX^yML=CJ^dF0-aRp zIC&`J$O2!<-5G=0ap1)u!ta!~K18D!k_$_8O@cKE7&}UCJ0Ik!s6GbWB=Fe4W5xH@2j?u;WE^ION)a8;el_sNzdjJ;tJ}xgS>hQCk7@l#^8b z_#;sXzXWY;knBJsO4@an_(gzClrs{vp!=4AQUfyiB~a36?8h42{TeXoJ{F;+geGGw zm;6u!6lmfDiXJ7s_kb%cpaH!Jb6^v_+ls+UXD@jx3YZi>I(4ETm0m}Bl*!Yi4}(5>BRgn63VP8?$p(4! zZ-eX6q6jgZ8uT*NWq3<+iu^*d0;nGq8AVk*NI6Ls*x@xB@>)U@kmJ0hYA6yM zST^YC0KXHI4(Lg-qg*D>QmhTnlh^4bNYN&bQ}sL1bbSSkDQe_lLiWM~o`3()JI0SW zMV-7)^-tPS?a=El7JShqXp}(#;Olu!IZY=?F*tRt#S%+&-;!mdFaEJEcn}pokYiYj=s}eymdd5GGkELqCeV-srVWmg_l6 z8A_H>_Ry=HY^SQ^XyiSzMz1yUCS{?9eTqHRFr9=HJtyXrCDHIAdDQX0^`#mjDzPN* z-N2>qC*%{VdXh&!9GIjpWi&;G?A06TNhgP1({wKAc}LYtXD6K&ln0dmdR z5Hew_C3Qs+wxf~f9gs&EOFq&gK$S-)9NA3SLb*w`px3@0f695XT>~XG@-bmhUg#A` z`K8whl*T?a`fX@#RJE?NQQ^9tI<~GW<%8aIIdq=r$Rw4d)2@CNlFnqmeo`2op?uZP z5&g6wY8^{Qpc>IXWk#isYrJMqi0s$zA-anw_sLU~S;S4g)0+`j+M$?Hd?}~(8rE+SiVN96H9-EegO~0f z(uDM+A5)gDH(5ZM5I*@u&wX8Q!lUz%e68mp$tEQIL%UWTsV+&D9#^u3=uIB-upU=K z4%I5j)n(}?fnNV~2I&4E9}y<`gz)vdK)*K$k3L2{5*3Z9(MOl3+o#u?zD^d99fp3S zE6JfBl4aPY%hLHtM?xgYx+ePRrE5lrhGc86@94in^)qzTk#&6|*`%LRx_?MR`WTk# z8j)6J)2%cpjGnlNThCis)gwq*qBp%7DPw4t=!}z?P9yz9qFr5I`fWr*&v)G~6e(R2 zNuZCRj~NleV%jlm3AU2Px@UC?Ga{4$dei-{OCVax)ZnJaQTM0m9aBeLPhC@8V_l}9 zuc0Glj*&e&ny#}I$v`7+UGv~PA^AF2FmE^oSxXe*`s)0;wT1*8%g|TnwPulyX2>*b z)id45cEf62Ch^mcVYS}tnPON$S{NP+j+zlM>UB*GtAj0~v8kml&G4&Xk&drts*$mJ zPjU^54V+M24eyy+nED3mXdN3GhwB`SZ^$&fP1Hud2kUFB>DuZV8@!>tVYS{{XF9$a z@nBpNyLA5s>uJR@d=hSVxCXlZ23BxSJHaj3r~0~%Z>7@pwXW%?!C0k_OWz5`B+2A2 zBQ7*2-_zSc|1P6fHc2ME>7Fy*apt|H>uR2KW-Sn&9uG4XrtnzdoosV|Hn?Yg7&~1vOqaFucih*Rr%=AU+vUJXH4NO@EwzW4j4DSss zsMpV_P|jeA;BjeM=I{w#I6BZ=!@_1XG)M!K**Kirx)QFfu^+x;$kOjnV@AEf6W*7GV)dv=6^yEnP0Da|bHzk3 z=uISJB~l;Gr&F28!6Tj8M7EA1;TvnE$)R=YC!EqM#h6*Sf@k3fh86}@WC{b>+Lz@I z9ZyHIa?#9`8a$4?YET#&M(Pcka7p@#71=~E`G_Kv)}%8=CcQyz?M*obhqW)A-%3X_ zlh)LYq=oM2bCW+Zg{fyazsXycTtjwQNQP`3(U^tzI!3rACZ#zFr?ToBKGS(kJ%dLk zT4{VHMKB+YgLllaiBlQ{tw;WprKur3GH39rK^dtJri(N#UFWiP3`wRJf~l=?f_L<> z!5!Y4cvg(?nSo?-gv+5l{ZlrTi5?ll;5N|A-q?xUo47P88(ZJkIc|kgr@3{?ThqX_ zEOcb*uj^RW?{KX`(e$~AX>gQ<8oU=7FMKuFtHJ!n*pRFDrD+ws8!Dr0308c=-pH%8 z8~!nP42>iAH`g#+rd5_HhbT+`Sh<2{Rs<6zT(XrivMik|7(aAuqL>I}$ujA~sjS#m zN)tVN6pp0xS`kdDTa#nbmyJfd`loDp)?KR=R$11Wm5=tqS4@twG%;m{(i;{Sy-689 zGH4?8x@O@##!6Xo%d(30tRE}2fp7Mvq;OsXNyjjG!besVt4u-(U9rw}yt1gQ^d_aD zp}AtE4A<1UV)B_t!6OsR%11Mk*BA$LhVL2(k#>UD3_86J-qFWaDKszW# zBXnJ#o7`5e$kdTF2<0q|H`2PUU8J4JYo@;8mIiYeR-1kBt~s{i8M9D}^tp*u)=0-T zkjm=ILJ8ixbwpiSxc0$l=GgQ?Fo#v+;GOWXiAAHxc;>n}GAXUn%$b$;*5p`uZtgWJ zsyQ>Y3r8%A%G5r1q+^(T){#|KFrRfNSQ9IiF|*=Enwf|OkJ$&~n`0ftxftkC|F9kzO-A~$CMd~&)mH^ziExhbu+um(kxh_ju=_P z(A8kRvd5vErO(U8F?UVNgS}A}ZYYO|6*>x~jD#92wd}Eu9*!A_OP3UBCv@#r@Ivjn znI@6!EKN>0b~t5ZJY&_gC>+bWVnr_7M+SN%Dl4ivGx5vfE{%$KZf=W7W0hg$HmMBl zf>%Pv22G?sT#CM8YHFbA+{P?YZ{kPVjU-8zV)B`z$Ow_>!zsfh-JGv%t4S}boNyVm z5=o9>lfG;6g(8OX8fc+>27-Ybxi_dH^`VkN5hC#zYoW9>kEChnsyWvYtUSi9Atmx| zuszJN>294m*e-n>S=Oz1E|~Mb#|@Qa zSQL&Jc_oy;wE3;$2HT~NBV$JPdSoqW^?!y-+}2MxUL+aBQ#Pe(ok?kxWX`NeCMS(d zZtK`erBj-S;iJ;HOT(viE02{jT!Oh`@>#XCBA8Uf89WOfo2bUf+_j2zIbzYs{N*}CEq#b?D@Na1px>o<~Zme`qFyj zkCh`_*RrG$qRAi5YuFlDvaxC+1f%I=ou(`bk`c}qymoU8LzA2L!MKs^4cDY}OcN^- zlDTGLmX1~Wj)_zHDA?DfBk4P)*2Goz$J9KSE3$-O3hUUE5gE^*4#zciZly09TSvGx zoM1g}g@f$773smak;W#9>9Np}DJK#_=vpL16T#f2HS-fr5sGWfO@y+ziAw*N^tUd9 zIIVnT$@;gTFdi$0NkyYb=%K4tj^LS<+sYSAr;n}Fx+PW~q6q)!e78n#l|ks%k5yi{ zEOR9sN#`{wXk=1Z<yt)O(Oz#ce@ZKuVI=dB&P>yg3p(~*r#ypg+thvDx z+Ly&+-3#Wl?$9iFH*_3MUHXd7YvvTq|7V|IEE-#N{ZFt%d98En>}FXMEH@Ho=vpxS z&Bmd;rOmB4p;XpgV-`vkIydP4(>@d@bpC%5??1%~r4QB3n1|Bc%G}@$?QezGp!rXD zLurkhDC_#FTLSR*9QsIBb#yXODfUlVsS_B8zZAjO5TRy)SY #include "esp-box.hpp" +#include "event_manager.hpp" #include "aw9523.hpp" #include "base_component.hpp" +#include "events.hpp" #include "high_resolution_timer.hpp" #include "keypad_input.hpp" #include "max1704x.hpp" @@ -48,7 +50,7 @@ class BoxEmu : public espp::BaseComponent { return instance; } - BoxEmu(const BoxEMu &) = delete; + BoxEmu(const BoxEmu &) = delete; BoxEmu &operator=(const BoxEmu &) = delete; BoxEmu(BoxEmu &&) = delete; BoxEmu &operator=(BoxEmu &&) = delete; @@ -61,12 +63,12 @@ class BoxEmu : public espp::BaseComponent { /// Get a reference to the internal I2C bus /// \return A reference to the internal I2C bus /// \note The internal I2C bus is used for the touchscreen and audio codec - I2c &internal_i2c(); + espp::I2c &internal_i2c(); /// Get a reference to the external I2C bus /// \return A reference to the external I2C bus /// \note The external I2C bus is used for the gamepad functionality - I2c &external_i2c(); + espp::I2c &external_i2c(); /// Initialize the EspBox hardware /// \return True if the initialization was successful, false otherwise @@ -87,8 +89,8 @@ class BoxEmu : public espp::BaseComponent { ///////////////////////////////////////////////////////////////////////////// bool initialize_memory(); - size_t copy_romdata_to_cart_partition(const std::string& filename); - uint8_t *mapped_romdata() const; + size_t copy_file_to_romdata(const std::string& filename); + uint8_t *romdata() const; ///////////////////////////////////////////////////////////////////////////// // Gamepad @@ -105,19 +107,19 @@ class BoxEmu : public espp::BaseComponent { ///////////////////////////////////////////////////////////////////////////// bool initialize_battery(); - std::shared_ptr battery(); + std::shared_ptr battery() const; ///////////////////////////////////////////////////////////////////////////// // Video ///////////////////////////////////////////////////////////////////////////// bool initialize_video(); - void set_display_size(size_t width, size_t height); - void set_native_size(size_t width, size_t height, int pitch = -1); - void set_palette(const uint16_t *palette, size_t size = 256); + void display_size(size_t width, size_t height); + void native_size(size_t width, size_t height, int pitch = -1); + void palette(const uint16_t *palette, size_t size = 256); void push_frame(const void* frame); VideoSetting video_setting() const; - void video_setting(VideoSetting setting); + void video_setting(const VideoSetting setting); ///////////////////////////////////////////////////////////////////////////// // USB @@ -131,8 +133,67 @@ class BoxEmu : public espp::BaseComponent { BoxEmu(); void detect(); + bool has_palette() const; + bool is_native() const; + int x_offset() const; + int y_offset() const; + const uint16_t *palette() const; + bool video_task_callback(std::mutex &m, std::condition_variable &cv); + + class InputBase { + public: + virtual uint16_t get_pins(std::error_code& ec) = 0; + virtual GamepadState pins_to_gamepad_state(uint16_t pins) = 0; + virtual void handle_volume_pins(uint16_t pins) = 0; + }; + + template + class Input : public InputBase { + public: + explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} + virtual uint16_t get_pins(std::error_code& ec) override { + auto val = input_driver->get_pins(ec); + if (ec) { + return 0; + } + return val ^ T::INVERT_MASK; + } + virtual GamepadState pins_to_gamepad_state(uint16_t pins) override { + GamepadState state; + state.a = (bool)(pins & T::A_PIN); + state.b = (bool)(pins & T::B_PIN); + state.x = (bool)(pins & T::X_PIN); + state.y = (bool)(pins & T::Y_PIN); + state.start = (bool)(pins & T::START_PIN); + state.select = (bool)(pins & T::SELECT_PIN); + state.up = (bool)(pins & T::UP_PIN); + state.down = (bool)(pins & T::DOWN_PIN); + state.left = (bool)(pins & T::LEFT_PIN); + state.right = (bool)(pins & T::RIGHT_PIN); + return state; + } + virtual void handle_volume_pins(uint16_t pins) override { + // check the volume pins and send out events if they're pressed / released + bool volume_up = (bool)(pins & T::VOL_UP_PIN); + bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); + int volume_change = (volume_up * 10) + (volume_down * -10); + if (volume_change != 0) { + // change the volume + auto &box = espp::EspBox::get(); + float current_volume = box.volume(); + float new_volume = std::clamp(current_volume + volume_change, 0, 100); + box.volume(new_volume); + // send out a volume change event + espp::EventManager::get().publish(volume_changed_topic, {}); + } + } + protected: + std::shared_ptr input_driver; + }; + struct version0 { - using InputDriver = espp::mcp23x17; + using InputDriver = espp::Mcp23x17; + typedef Input InputType; static constexpr uint16_t START_PIN = (1<<0) << 0; // start pin is on port a of the MCP23x17 static constexpr uint16_t SELECT_PIN = (1<<1) << 0; // select pin is on port a of the MCP23x17 static constexpr uint16_t UP_PIN = (1<<0) << 8; // up pin is on port b of the MCP23x17 @@ -157,6 +218,7 @@ class BoxEmu : public espp::BaseComponent { struct version1 { using InputDriver = espp::Aw9523; + typedef Input InputType; static constexpr gpio_num_t VBAT_SENSE_PIN = GPIO_NUM_14; // battery sense pin is on GPIO 14 static constexpr gpio_num_t AW9523_INT_PIN = GPIO_NUM_21; // interrupt pin is on GPIO 21 static constexpr uint16_t UP_PIN = (1<<0) << 0; // up pin is on port 0 of the AW9523 @@ -185,73 +247,83 @@ class BoxEmu : public espp::BaseComponent { static constexpr adc_channel_t BATTERY_ADC_CHANNEL = ADC_CHANNEL_3; }; - class InputBase { - public: - virtual uint16_t get_pins(std::error_code& ec) = 0; - virtual GamepadState pins_to_gamepad_state(uint16_t pins) = 0; - virtual void handle_volume_pins(uint16_t pins) = 0; - }; - - template - class Input : public InputBase { - public: - explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} - virtual uint16_t get_pins(std::error_code& ec) override { - auto val = input_driver->get_pins(ec); - if (ec) { - return 0; - } - return val ^ T::INVERT_MASK; - } - virtual GamepadState pins_to_gamepad_state(uint16_t pins) override { - GamepadState state; - state.a = (bool)(pins & T::A_PIN); - state.b = (bool)(pins & T::B_PIN); - state.x = (bool)(pins & T::X_PIN); - state.y = (bool)(pins & T::Y_PIN); - state.start = (bool)(pins & T::START_PIN); - state.select = (bool)(pins & T::SELECT_PIN); - state.up = (bool)(pins & T::UP_PIN); - state.down = (bool)(pins & T::DOWN_PIN); - state.left = (bool)(pins & T::LEFT_PIN); - state.right = (bool)(pins & T::RIGHT_PIN); - return state; - } - virtual void handle_volume_pins(uint16_t pins) override { - // check the volume pins and send out events if they're pressed / released - bool volume_up = (bool)(pins & T::VOL_UP_PIN); - bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); - int volume_change = (volume_up * 10) + (volume_down * -10); - if (volume_change != 0) { - // change the volume - float current_volume = espp::EspBox::volume(); - float new_volume = std::clamp(current_volume + volume_change, 0, 100); - espp::EspBox::volume(new_volume); - // send out a volume change event - espp::EventManager::get().publish(volume_changed_topic, {}); - } - } - protected: - std::shared_ptr input_driver; - }; - // external I2c (peripherals) static constexpr auto external_i2c_port = I2C_NUM_1; static constexpr auto external_i2c_clock_speed = 400 * 1000; static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; + // uSD card + static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; + static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; + static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; + static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; + static constexpr auto sdcard_spi_num = SPI3_HOST; + + static constexpr int num_rows_in_framebuffer = 50; + Version version_{Version::UNKNOWN}; - I2c external_i2c_{{.port = external_i2c_port, - .sda_io_num = external_i2c_sda, - .scl_io_num = external_i2c_scl, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE}}; + espp::I2c external_i2c_{{.port = external_i2c_port, + .sda_io_num = external_i2c_sda, + .scl_io_num = external_i2c_scl, + .sda_pullup_en = GPIO_PULLUP_ENABLE, + .scl_pullup_en = GPIO_PULLUP_ENABLE}}; + + // sdcard + sdmmc_card_t *sdcard_{nullptr}; + // memory + uint8_t *romdata_{nullptr}; + + // gamepad + std::atomic can_read_gamepad_{true}; std::recursive_mutex gamepad_state_mutex_; GamepadState gamepad_state_; std::shared_ptr input_; std::shared_ptr keypad_; std::shared_ptr input_timer_; + + // battery + std::shared_ptr battery_{nullptr}; + std::shared_ptr adc_{nullptr}; + std::shared_ptr battery_task_; + std::vector channels; + + // video + std::atomic video_setting_{VideoSetting::FIT}; + std::shared_ptr video_task_{nullptr}; + QueueHandle_t video_queue_{nullptr}; + + size_t display_width_{espp::EspBox::lcd_width()}; + size_t display_height_{espp::EspBox::lcd_height()}; + + size_t native_width_{espp::EspBox::lcd_width()}; + size_t native_height_{espp::EspBox::lcd_height()}; + int native_pitch_{espp::EspBox::lcd_width()}; + + const uint16_t* palette_{nullptr}; + size_t palette_size_{256}; + +}; + +// for libfmt printing of the BoxEmu::Version enum +template <> +struct fmt::formatter : fmt::formatter { + template + auto format(BoxEmu::Version v, FormatContext &ctx) const { + std::string_view name; + switch (v) { + case BoxEmu::Version::UNKNOWN: + name = "UNKNOWN"; + break; + case BoxEmu::Version::V0: + name = "V0"; + break; + case BoxEmu::Version::V1: + name = "V1"; + break; + } + return fmt::formatter::format(name, ctx); + } }; diff --git a/components/box-emu/include/box.hpp b/components/box-emu/include/box.hpp deleted file mode 100644 index 2950e73..0000000 --- a/components/box-emu/include/box.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "hal/spi_types.h" -#include "driver/gpio.h" -#include "driver/i2s_std.h" -#include "driver/spi_master.h" - -#include "i2c.hpp" -#include "st7789.hpp" -#include "touchpad_input.hpp" -#include "tt21100.hpp" - -static constexpr std::string_view dev_kit = "ESP32-S3-BOX"; - -// internal i2c (touchscreen, audio codec) -static constexpr auto internal_i2c_port = I2C_NUM_0; -static constexpr auto internal_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; -static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; - -// external I2c (peripherals) -static constexpr auto external_i2c_port = I2C_NUM_1; -static constexpr auto external_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; -static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; - -// LCD -static constexpr int lcd_clock_speed = 60 * 1000 * 1000; -static constexpr auto lcd_spi_num = SPI2_HOST; -static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; -static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; -static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; -static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; -static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; -static constexpr gpio_num_t backlight_io = GPIO_NUM_45; -static constexpr size_t lcd_width = 320; -static constexpr size_t lcd_height = 240; -static constexpr bool backlight_value = true; -static constexpr bool reset_value = false; -static constexpr bool invert_colors = true; -static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; -static constexpr bool mirror_x = true; -static constexpr bool mirror_y = true; -using DisplayDriver = espp::St7789; - -// touch -static constexpr bool touch_swap_xy = false; -static constexpr bool touch_invert_x = true; -static constexpr bool touch_invert_y = false; -static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; -using TouchDriver = espp::Tt21100; - -// sound -static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; -static constexpr auto i2s_port = I2S_NUM_0; -static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; -static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; -static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_47; -static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; -static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; -static constexpr gpio_num_t mute_pin = GPIO_NUM_1; - -// uSD card -static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; -static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; -static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; -static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; -static constexpr auto sdcard_spi_num = SPI3_HOST; - diff --git a/components/box-emu/include/box_3.hpp b/components/box-emu/include/box_3.hpp deleted file mode 100644 index 391af74..0000000 --- a/components/box-emu/include/box_3.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "hal/spi_types.h" -#include "driver/gpio.h" -#include "driver/i2s_std.h" -#include "driver/spi_master.h" - -#include "i2c.hpp" -#include "st7789.hpp" -#include "touchpad_input.hpp" -#include "gt911.hpp" - -static constexpr std::string_view dev_kit = "ESP32-S3-BOX-3"; - -// internal i2c (touchscreen, audio codec) -static constexpr auto internal_i2c_port = I2C_NUM_0; -static constexpr auto internal_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; -static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; - -// external I2c (peripherals) -static constexpr auto external_i2c_port = I2C_NUM_1; -static constexpr auto external_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; -static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; - -// LCD -static constexpr int lcd_clock_speed = 60 * 1000 * 1000; -static constexpr auto lcd_spi_num = SPI2_HOST; -static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; -static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; -static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; -static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; -static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; -static constexpr gpio_num_t backlight_io = GPIO_NUM_47; // was 45 on ESP32-S3-BOX -static constexpr size_t lcd_width = 320; -static constexpr size_t lcd_height = 240; -static constexpr bool backlight_value = true; -static constexpr bool reset_value = true; // was false on ESP32-S3-BOX -static constexpr bool invert_colors = true; -static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; -static constexpr bool mirror_x = true; -static constexpr bool mirror_y = true; -using DisplayDriver = espp::St7789; - -// touch -static constexpr bool touch_swap_xy = false; -static constexpr bool touch_invert_x = false; -static constexpr bool touch_invert_y = false; -static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; -using TouchDriver = espp::Gt911; - -// sound -static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; -static constexpr auto i2s_port = I2S_NUM_0; -static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; -static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; -static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_45; // was 47 on ESP32-S3-BOX -static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; -static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; -static constexpr gpio_num_t mute_pin = GPIO_NUM_1; - -// uSD card -static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; -static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; -static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; -static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; -static constexpr auto sdcard_spi_num = SPI3_HOST; diff --git a/components/box-emu/include/box_emu_hal.hpp b/components/box-emu/include/box_emu_hal.hpp deleted file mode 100644 index c055438..0000000 --- a/components/box-emu/include/box_emu_hal.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "aw9523.hpp" -#include "button.hpp" -#include "event_manager.hpp" -#include "display.hpp" -#include "i2c.hpp" -#include "keypad_input.hpp" -#include "logger.hpp" -#include "max1704x.hpp" -#include "mcp23x17.hpp" -#include "oneshot_adc.hpp" -#include "serialization.hpp" -#include "st7789.hpp" -#include "task.hpp" -#include "timer.hpp" -#include "high_resolution_timer.hpp" -#include "touchpad_input.hpp" - -#if CONFIG_HARDWARE_BOX -#include "box.hpp" -#elif CONFIG_HARDWARE_BOX_3 -#include "box_3.hpp" -#else -#error "Invalid module selection" -#endif - -#include "emu_v0.hpp" -#include "emu_v1.hpp" - -#include "es7210.hpp" -#include "es8311.hpp" -#include "lvgl_inputs.h" -#include "make_color.h" - -#include "battery_info.hpp" -#include "fs_init.hpp" -#include "hal_events.hpp" -#include "input_state.hpp" -#include "mmap.hpp" -#include "statistics.hpp" -#include "usb.hpp" -#include "video_setting.hpp" - -namespace hal { - // top level init function - void init(); - - // i2c / peripheral interfaces - void i2c_init(); - std::shared_ptr get_internal_i2c(); - std::shared_ptr get_external_i2c(); - - // input - void init_input(); - void get_input_state(InputState *state); - - // video - static constexpr int NUM_ROWS_IN_FRAME_BUFFER = 50; - std::shared_ptr get_display(); - uint16_t *get_vram0(); - uint16_t *get_vram1(); - uint8_t *get_frame_buffer0(); - uint8_t *get_frame_buffer1(); - void lcd_write(const uint8_t *data, size_t length, uint32_t user_data); - void lcd_write_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, const uint8_t *data); - void lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); - void lcd_init(); - void set_display_brightness(float brightness); - float get_display_brightness(); - - void init_video_task(); - void set_display_size(size_t width, size_t height); - void set_native_size(size_t width, size_t height, int pitch = -1); - void set_palette(const uint16_t* palette, size_t size = 256); - void push_frame(const void* frame); - - VideoSetting get_video_setting(); - void set_video_setting(const VideoSetting& setting); - - // audio - void audio_init(); - void set_audio_sample_rate(uint32_t sample_rate); - uint32_t get_audio_sample_rate(); - void play_audio(const uint8_t *data, uint32_t num_bytes); - bool is_muted(); - void set_muted(bool mute); - int get_audio_volume(); - void set_audio_volume(int percent); - - // battery - void battery_init(); - std::shared_ptr get_battery(); -} diff --git a/components/box-emu/include/emu_v0.hpp b/components/box-emu/include/emu_v0.hpp deleted file mode 100644 index 019a109..0000000 --- a/components/box-emu/include/emu_v0.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "mcp23x17.hpp" - -class EmuV0 { -public: - static constexpr uint16_t START_PIN = (1<<0) << 0; // start pin is on port a of the MCP23x17 - static constexpr uint16_t SELECT_PIN = (1<<1) << 0; // select pin is on port a of the MCP23x17 - static constexpr uint16_t UP_PIN = (1<<0) << 8; // up pin is on port b of the MCP23x17 - static constexpr uint16_t DOWN_PIN = (1<<1) << 8; // down pin is on port b of the MCP23x17 - static constexpr uint16_t LEFT_PIN = (1<<2) << 8; // left pin is on port b of the MCP23x17 - static constexpr uint16_t RIGHT_PIN = (1<<3) << 8; // right pin is on port b of the MCP23x17 - static constexpr uint16_t A_PIN = (1<<4) << 8; // a pin is on port b of the MCP23x17 - static constexpr uint16_t B_PIN = (1<<5) << 8; // b pin is on port b of the MCP23x17 - static constexpr uint16_t X_PIN = (1<<6) << 8; // x pin is on port b of the MCP23x17 - static constexpr uint16_t Y_PIN = (1<<7) << 8; // y pin is on port b of the MCP23x17 - static constexpr uint16_t BAT_ALERT_PIN = 0; // battery alert pin doesn't exist on the MCP23x17 - static constexpr uint16_t VOL_UP_PIN = 0; // volume up pin doesn't exist on the MCP23x17 - static constexpr uint16_t VOL_DOWN_PIN = 0; // volume down pin doesn't exist on the MCP23x17 - static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN); - static constexpr uint16_t INTERRUPT_MASK = (START_PIN | SELECT_PIN); - static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN ); // pins are active low so invert them - static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; - static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; - static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; - static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; -}; diff --git a/components/box-emu/include/emu_v1.hpp b/components/box-emu/include/emu_v1.hpp deleted file mode 100644 index 5c614f7..0000000 --- a/components/box-emu/include/emu_v1.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "aw9523.hpp" -#include "oneshot_adc.hpp" - -class EmuV1 { -public: - using InputDriver = espp::Aw9523; - static constexpr gpio_num_t VBAT_SENSE_PIN = GPIO_NUM_14; // battery sense pin is on GPIO 14 - static constexpr gpio_num_t AW9523_INT_PIN = GPIO_NUM_21; // interrupt pin is on GPIO 21 - static constexpr uint16_t UP_PIN = (1<<0) << 0; // up pin is on port 0 of the AW9523 - static constexpr uint16_t DOWN_PIN = (1<<1) << 0; // down pin is on port 0 of the AW9523 - static constexpr uint16_t LEFT_PIN = (1<<2) << 0; // left pin is on port 0 of the AW9523 - static constexpr uint16_t RIGHT_PIN = (1<<3) << 0; // right pin is on port 0 of the AW9523 - static constexpr uint16_t A_PIN = (1<<4) << 0; // a pin is on port 0 of the AW9523 - static constexpr uint16_t B_PIN = (1<<5) << 0; // b pin is on port 0 of the AW9523 - static constexpr uint16_t X_PIN = (1<<6) << 0; // x pin is on port 0 of the AW9523 - static constexpr uint16_t Y_PIN = (1<<7) << 0; // y pin is on port 0 of the AW9523 - static constexpr uint16_t START_PIN = (1<<0) << 8; // start pin is on port 1 of the AW9523 - static constexpr uint16_t SELECT_PIN = (1<<1) << 8; // select pin is on port 1 of the AW9523 - static constexpr uint16_t BAT_ALERT_PIN = (1<<3) << 8; // battery alert pin is on port 1 of the AW9523 - static constexpr uint16_t VOL_UP_PIN = (1<<4) << 8; // volume up pin is on port 1 of the AW9523 - static constexpr uint16_t VOL_DOWN_PIN = (1<<5) << 8; // volume down pin is on port 1 of the AW9523 - static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); - static constexpr uint16_t INTERRUPT_MASK = (BAT_ALERT_PIN); - static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); // pins are active low so invert them - static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; - static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; - static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; - static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; - - // ADC for the battery voltage, it's on ADC2_CH3, which is IO14 - static constexpr adc_unit_t BATTERY_ADC_UNIT = ADC_UNIT_2; - static constexpr adc_channel_t BATTERY_ADC_CHANNEL = ADC_CHANNEL_3; -}; diff --git a/components/box-emu/include/fs_init.hpp b/components/box-emu/include/fs_init.hpp deleted file mode 100644 index 2613087..0000000 --- a/components/box-emu/include/fs_init.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#define MOUNT_POINT "/sdcard" - -#include "hal.hpp" -#include "format.hpp" - -void fs_init(); -sdmmc_card_t *get_sdcard(); diff --git a/components/box-emu/include/input_state.hpp b/components/box-emu/include/gamepad_state.hpp similarity index 66% rename from components/box-emu/include/input_state.hpp rename to components/box-emu/include/gamepad_state.hpp index e6ec8fc..78347d9 100644 --- a/components/box-emu/include/input_state.hpp +++ b/components/box-emu/include/gamepad_state.hpp @@ -1,6 +1,6 @@ #pragma once -struct InputState { +struct GamepadState { int a : 1; int b : 1; int x : 1; @@ -12,5 +12,5 @@ struct InputState { int left : 1; int right : 1; - bool operator==(const InputState& other) const = default; + bool operator==(const GamepadState& other) const = default; }; diff --git a/components/box-emu/include/hal.hpp b/components/box-emu/include/hal.hpp deleted file mode 100644 index 343dfaa..0000000 --- a/components/box-emu/include/hal.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#if defined(CONFIG_HARDWARE_BOX) -#include "box.hpp" -#elif defined(CONFIG_HARDWARE_BOX_3) -#include "box_3.hpp" -#else -#error "Unsupported hardware configuration specified" -#endif diff --git a/components/box-emu/include/mmap.hpp b/components/box-emu/include/mmap.hpp deleted file mode 100644 index 924c1f2..0000000 --- a/components/box-emu/include/mmap.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#include -#include "nvs_flash.h" -#include "spi_flash_mmap.h" -#include "esp_partition.h" - -#include "format.hpp" - -void init_memory(); -size_t copy_romdata_to_cart_partition(const std::string& rom_filename); -uint8_t *get_mmapped_romdata(); diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index 22745bf..e0182d2 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -18,8 +18,176 @@ void BoxEmu::detect() { logger_.info("version {}", version_); } +espp::I2c &BoxEmu::internal_i2c() { + return espp::EspBox::get().internal_i2c(); +} + +espp::I2c &BoxEmu::external_i2c() { + return external_i2c_; +} + +bool BoxEmu::initialize_box() { + auto &box = espp::EspBox::get(); + // initialize the touchpad + if (!box.initialize_touch()) { + logger_.error("Failed to initialize touchpad!"); + return false; + } + // initialize the sound + if (!box.initialize_sound()) { + logger_.error("Failed to initialize sound!"); + return false; + } + // initialize the LCD + if (!box.initialize_lcd()) { + logger_.error("Failed to initialize LCD!"); + return false; + } + static constexpr size_t pixel_buffer_size = espp::EspBox::lcd_width() * num_rows_in_framebuffer; + // initialize the LVGL display for the esp-box + if (!box.initialize_display(pixel_buffer_size)) { + logger_.error("Failed to initialize display!"); + return false; + } + + return true; +} + +///////////////////////////////////////////////////////////////////////////// +// uSD Card +///////////////////////////////////////////////////////////////////////////// + +bool BoxEmu::initialize_sdcard() { + if (sdcard_) { + logger_.error("SD card already initialized!"); + return false; + } + + esp_err_t ret; + + // Options for mounting the filesystem. If format_if_mount_failed is set to + // true, SD card will be partitioned and formatted in case when mounting + // fails. + esp_vfs_fat_sdmmc_mount_config_t mount_config; + memset(&mount_config, 0, sizeof(mount_config)); + mount_config.format_if_mount_failed = false; + mount_config.max_files = 5; + mount_config.allocation_unit_size = 16 * 1024; + const char mount_point[] = "/sdcard"; + logger_.info("Initializing SD card"); + + // Use settings defined above to initialize SD card and mount FAT filesystem. + // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. + // Please check its source code and implement error recovery when developing + // production applications. + logger_.debug("Using SPI peripheral"); + + // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) + // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI) + // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; + sdmmc_host_t host = SDSPI_HOST_DEFAULT(); + host.slot = sdcard_spi_num; + // host.max_freq_khz = 20 * 1000; + + spi_bus_config_t bus_cfg; + memset(&bus_cfg, 0, sizeof(bus_cfg)); + bus_cfg.mosi_io_num = sdcard_mosi; + bus_cfg.miso_io_num = sdcard_miso; + bus_cfg.sclk_io_num = sdcard_sclk; + bus_cfg.quadwp_io_num = -1; + bus_cfg.quadhd_io_num = -1; + bus_cfg.max_transfer_sz = 8192; + spi_host_device_t host_id = (spi_host_device_t)host.slot; + ret = spi_bus_initialize(host_id, &bus_cfg, SDSPI_DEFAULT_DMA); + if (ret != ESP_OK) { + logger_.error("Failed to initialize bus."); + return false; + } + + // This initializes the slot without card detect (CD) and write protect (WP) signals. + // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. + sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); + slot_config.gpio_cs = sdcard_cs; + slot_config.host_id = host_id; + + logger_.debug("Mounting filesystem"); + ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &sdcard_); + + if (ret != ESP_OK) { + if (ret == ESP_FAIL) { + logger_.error("Failed to mount filesystem. " + "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option."); + return false; + } else { + logger_.error("Failed to initialize the card ({}). " + "Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret)); + return false; + } + return false; + } + + logger_.info("Filesystem mounted"); + + // Card has been initialized, print its properties + sdmmc_card_print_info(stdout, sdcard_); + + return true; +} + +sdmmc_card_t *BoxEmu::sdcard() const { + return sdcard_; +} + +///////////////////////////////////////////////////////////////////////////// +// Memory +///////////////////////////////////////////////////////////////////////////// + +extern "C" uint8_t *osd_getromdata() { + auto &emu = BoxEmu::get(); + return emu.romdata(); +} + +bool BoxEmu::initialize_memory() { + if (romdata_) { + logger_.error("ROM already initialized!"); + return false; + } + + // allocate memory for the ROM and make sure it's on the SPIRAM + romdata_ = (uint8_t*)heap_caps_malloc(4*1024*1024, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); + if (romdata_ == nullptr) { + logger_.error("Couldn't allocate memory for ROM!"); + return false; + } + + return true; +} + +size_t BoxEmu::copy_file_to_romdata(const std::string& filename) { + // load the file data and iteratively copy it over + std::ifstream romfile(filename, std::ios::binary | std::ios::ate); //open file at end + if (!romfile.is_open()) { + logger_.error("ROM file does not exist"); + return 0; + } + size_t filesize = romfile.tellg(); // get size from current file pointer location; + romfile.seekg(0, std::ios::beg); //reset file pointer to beginning; + romfile.read((char*)(romdata_), filesize); + romfile.close(); + + return filesize; +} + +uint8_t *BoxEmu::romdata() const { + return romdata_; +} + +///////////////////////////////////////////////////////////////////////////// +// Gamepad +///////////////////////////////////////////////////////////////////////////// + extern "C" lv_indev_t *get_keypad_input_device() { - auto keypad = espp::EspBox::get().keypad(); + auto keypad = BoxEmu::get().keypad(); if (!keypad) { fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); return nullptr; @@ -29,30 +197,30 @@ extern "C" lv_indev_t *get_keypad_input_device() { bool BoxEmu::initialize_gamepad() { if (version_ == BoxEmu::Version::V0) { - auto raw_input = new Input( - std::make_shared(version0::InputDriver::Config{ - .port_0_direction_mask = version0::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = version0::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = version0::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = version0::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .write_then_read = std::bind(&espp::I2c::write_read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - .log_level = espp::Logger::Verbosity::WARN - }) - ); + auto raw_input = new version0::InputType( + std::make_shared(version0::InputDriver::Config{ + .port_0_direction_mask = version0::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = version0::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = version0::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = version0::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .read_register = std::bind(&espp::I2c::read_at_register, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .log_level = espp::Logger::Verbosity::WARN + }) + ); input_.reset(raw_input); } else if (version_ == BoxEmu::Version::V1) { - auto raw_input = new Input( - std::make_shared(version1::InputDriver::Config{ - .port_0_direction_mask = version1::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = version1::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = version1::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = version1::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .write_then_read = std::bind(&espp::I2c::write_read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - .log_level = espp::Logger::Verbosity::WARN - }) - ); + auto raw_input = new version1::InputType( + std::make_shared(version1::InputDriver::Config{ + .port_0_direction_mask = version1::PORT_0_DIRECTION_MASK, + .port_0_interrupt_mask = version1::PORT_0_INTERRUPT_MASK, + .port_1_direction_mask = version1::PORT_1_DIRECTION_MASK, + .port_1_interrupt_mask = version1::PORT_1_INTERRUPT_MASK, + .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .write_then_read = std::bind(&espp::I2c::write_read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), + .log_level = espp::Logger::Verbosity::WARN + }) + ); input_.reset(raw_input); } else { return false; @@ -60,14 +228,14 @@ bool BoxEmu::initialize_gamepad() { // now initialize the keypad driver keypad_ = std::make_shared(espp::KeypadInput::Config{ - .read = keypad_read, + .read = std::bind(&BoxEmu::keypad_read, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5, std::placeholders::_6), .log_level = espp::Logger::Verbosity::WARN }); // now initialize the input timer input_timer_ = std::make_shared(espp::HighResolutionTimer::Config{ .name = "Input timer", - .callback = []() { + .callback = [this]() { espp::EspBox::get().update_touch(); update_gamepad_state(); }}); @@ -77,9 +245,9 @@ bool BoxEmu::initialize_gamepad() { return true; } -InputState BoxEmu::gamepad_state() { - std::lock_guard lock(input_mutex_); - return input_state_; +GamepadState BoxEmu::gamepad_state() { + std::lock_guard lock(gamepad_state_mutex_); + return gamepad_state_; } bool BoxEmu::update_gamepad_state() { @@ -97,12 +265,12 @@ bool BoxEmu::update_gamepad_state() { return false; } - auto new_input_state = input_->pins_to_gamepad_state(pins); + auto new_gamepad_state = input_->pins_to_gamepad_state(pins); bool changed = false; { - std::lock_guard lock(input_mutex_); - changed = input_state_ != new_input_state; - input_state_ = new_input_state; + std::lock_guard lock(gamepad_state_mutex_); + changed = gamepad_state_ != new_gamepad_state; + gamepad_state_ = new_gamepad_state; } input_->handle_volume_pins(pins); @@ -110,13 +278,222 @@ bool BoxEmu::update_gamepad_state() { } void BoxEmu::keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) { - InputState state; - hal::get_input_state(&state); - *up = state.up; - *down = state.down; - *left = state.left; - *right = state.right; - - *enter = state.a || state.start; - *escape = state.b || state.select; + std::lock_guard lock(gamepad_state_mutex_); + *up = gamepad_state_.up; + *down = gamepad_state_.down; + *left = gamepad_state_.left; + *right = gamepad_state_.right; + + *enter = gamepad_state_.a || gamepad_state_.start; + *escape = gamepad_state_.b || gamepad_state_.select; +} + +std::shared_ptr BoxEmu::keypad() const { + return keypad_; +} + +///////////////////////////////////////////////////////////////////////////// +// Battery +///////////////////////////////////////////////////////////////////////////// + +bool BoxEmu::initialize_battery() { + if (battery_) { + logger_.error("Battery already initialized!"); + return false; + } + + return true; +} + +std::shared_ptr BoxEmu::battery() const { + return battery_; +} + +///////////////////////////////////////////////////////////////////////////// +// Video +///////////////////////////////////////////////////////////////////////////// + +bool BoxEmu::initialize_video() { + if (video_task_) { + logger_.error("Video task already initialized!"); + return false; + } + + logger_.info("initializing video task"); + video_queue_ = xQueueCreate(1, sizeof(uint16_t*)); + video_task_ = std::make_shared(espp::Task::Config{ + .name = "video task", + .callback = std::bind(&BoxEmu::video_task_callback, this, std::placeholders::_1, std::placeholders::_2), + .stack_size_bytes = 4*1024, + .priority = 20, + .core_id = 1 + }); + video_task_->start(); + + return true; +} + +void BoxEmu::display_size(size_t width, size_t height) { + display_width_ = width; + display_height_ = height; +} + +void BoxEmu::native_size(size_t width, size_t height, int pitch) { + native_width_ = width; + native_height_ = height; + native_pitch_ = pitch == -1 ? width : pitch; +} + +void BoxEmu::palette(const uint16_t *palette, size_t size) { + palette_ = palette; + palette_size_ = size; +} + +void BoxEmu::push_frame(const void* frame) { + if (video_queue_ == nullptr) { + logger_.error("video queue is null, make sure to call initialize_video() first!"); + return; + } + xQueueSend(video_queue_, &frame, 10 / portTICK_PERIOD_MS); +} + +VideoSetting BoxEmu::video_setting() const { + return video_setting_; +} + +void BoxEmu::video_setting(const VideoSetting setting) { + video_setting_ = setting; +} + +///////////////////////////////////////////////////////////////////////////// +// USB +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Static Video Task: +///////////////////////////////////////////////////////////////////////////// + +bool BoxEmu::has_palette() const { + return palette_ != nullptr; +} + +bool BoxEmu::is_native() const { + return native_width_ == display_width_ && native_height_ == display_height_; +} + +int BoxEmu::x_offset() const { + return (espp::EspBox::lcd_width()-display_width_)/2; +} + +int BoxEmu::y_offset() const { + return (espp::EspBox::lcd_height()-display_height_)/2; +} + +const uint16_t* BoxEmu::palette() const { + return palette_; +} + +bool BoxEmu::video_task_callback(std::mutex &m, std::condition_variable& cv) { + const void *_frame_ptr; + if (xQueuePeek(video_queue_, &_frame_ptr, 100 / portTICK_PERIOD_MS) != pdTRUE) { + // we couldn't get anything from the queue, return + return false; + } + if (_frame_ptr == nullptr) { + // make sure we clear the queue + xQueueReceive(video_queue_, &_frame_ptr, 10 / portTICK_PERIOD_MS); + // we got a nullptr, return + return false; + } + static constexpr int num_lines_to_write = num_rows_in_framebuffer; + auto &box = espp::EspBox::get(); + static int vram_index = 0; // has to be static so that it persists between calls + const int _x_offset = x_offset(); + const int _y_offset = y_offset(); + const uint16_t* _palette = palette(); + if (is_native()) { + for (int y=0; y(num_lines_to_write, display_height_-y); + if (has_palette()) { + const uint8_t* _frame = (const uint8_t*)_frame_ptr; + for (int i=0; i(x_scale * native_width_, 0, espp::EspBox::lcd_width()); + for (int y=0; y= max_y) { + break; + } + int source_y = (float)_y * inv_y_scale; + // shoudl i put this around the outer loop or is this loop a good + // balance for perfomance of the check? + if (has_palette()) { + const uint8_t* _frame = (const uint8_t*)_frame_ptr; + // write two pixels (32 bits) at a time because it's faster + for (int x=0; x internal_i2c = nullptr; -static std::shared_ptr external_i2c = nullptr; - -static bool initialized = false; - -void hal::i2c_init() { - if (initialized) return; - // make the i2c on core 1 so that the i2c interrupts are handled on core 1 - espp::Task::run_on_core([]() { - internal_i2c = std::make_shared(espp::I2c::Config{ - .port = internal_i2c_port, - .sda_io_num = internal_i2c_sda, - .scl_io_num = internal_i2c_scl, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE, - .timeout_ms = 100, - }); - external_i2c = std::make_shared(espp::I2c::Config{ - .port = external_i2c_port, - .sda_io_num = external_i2c_sda, - .scl_io_num = external_i2c_scl, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE, - .timeout_ms = 100, - }); - }, 1); - initialized = true; -} - -std::shared_ptr hal::get_internal_i2c() { - i2c_init(); - return internal_i2c; -} - -std::shared_ptr hal::get_external_i2c() { - i2c_init(); - return external_i2c; -} diff --git a/components/box-emu/src/i2s_audio.cpp b/components/box-emu/src/i2s_audio.cpp deleted file mode 100644 index 79190e3..0000000 --- a/components/box-emu/src/i2s_audio.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::atomic muted_{false}; -static std::atomic volume_{60}; - -static std::shared_ptr mute_button; -static std::unique_ptr audio_task; -static TaskHandle_t main_task_handle = NULL; -static i2s_chan_handle_t tx_handle = NULL; - -// Scratch buffers -namespace hal { -static constexpr int DEFAULT_AUDIO_RATE = 48000; -static constexpr int NUM_CHANNELS = 2; -static constexpr int NUM_BYTES_PER_CHANNEL = 2; -static constexpr int UPDATE_FREQUENCY = 60; -static constexpr int AUDIO_BUFFER_SIZE = DEFAULT_AUDIO_RATE * NUM_CHANNELS * NUM_BYTES_PER_CHANNEL / UPDATE_FREQUENCY; -} -static uint8_t tx_buffer[hal::AUDIO_BUFFER_SIZE]; -static StreamBufferHandle_t tx_audio_stream; - -static i2s_std_config_t std_cfg; - -static i2s_event_callbacks_t tx_callbacks_; -std::atomic has_sound{false}; - -static void update_volume_output() { - if (muted_) { - es8311_codec_set_voice_volume(0); - } else { - es8311_codec_set_voice_volume(volume_); - } -} - -void hal::set_muted(bool mute) { - muted_ = mute; - update_volume_output(); -} - -bool hal::is_muted() { - return muted_; -} - -void hal::set_audio_volume(int percent) { - volume_ = percent; - update_volume_output(); -} - -int hal::get_audio_volume() { - return volume_; -} - -uint32_t hal::get_audio_sample_rate() { - return std_cfg.clk_cfg.sample_rate_hz; -} - -void hal::set_audio_sample_rate(uint32_t sample_rate) { - fmt::print("Setting sample rate to {}\n", sample_rate); - // disable i2s - i2s_channel_disable(tx_handle); - // update the config - std_cfg.clk_cfg.sample_rate_hz = sample_rate; - i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg); - // clear the buffer - xStreamBufferReset(tx_audio_stream); - // re-enable i2s - i2s_channel_enable(tx_handle); -} - -static bool IRAM_ATTR audio_task_fn(std::mutex &m, std::condition_variable &cv) -{ - // Queue the next I2S out frame to write - uint16_t available = xStreamBufferBytesAvailable(tx_audio_stream); - available = std::min(available, hal::AUDIO_BUFFER_SIZE); - uint8_t* buffer = &tx_buffer[0]; - static constexpr int BUFF_SIZE = hal::AUDIO_BUFFER_SIZE; - memset(buffer, 0, BUFF_SIZE); - - if (available == 0) { - i2s_channel_write(tx_handle, buffer, BUFF_SIZE, NULL, portMAX_DELAY); - } else { - xStreamBufferReceive(tx_audio_stream, buffer, available, 0); - i2s_channel_write(tx_handle, buffer, available, NULL, portMAX_DELAY); - } - return false; // don't stop the task -} - -static bool IRAM_ATTR tx_sent_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) -{ - // notify the main task that we're done - vTaskNotifyGiveFromISR(main_task_handle, NULL); - return true; -} - -static esp_err_t i2s_driver_init(void) -{ - fmt::print("initializing i2s driver...\n"); - auto ret_val = ESP_OK; - fmt::print("Using newer I2S standard\n"); - i2s_chan_config_t chan_cfg = { \ - .id = i2s_port, - .role = I2S_ROLE_MASTER, - .dma_desc_num = 16, - .dma_frame_num = 48, - .auto_clear = true, - .intr_priority = 0, - }; - - ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, nullptr)); - - std_cfg = { - .clk_cfg = { - .sample_rate_hz = hal::DEFAULT_AUDIO_RATE, - .clk_src = I2S_CLK_SRC_DEFAULT, - .ext_clk_freq_hz = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_256, - }, - .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), - .gpio_cfg = { - .mclk = i2s_mck_io, - .bclk = i2s_bck_io, - .ws = i2s_ws_io, - .dout = i2s_do_io, - .din = i2s_di_io, - .invert_flags = { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }, - }; - std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256; - - ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); - - tx_audio_stream = xStreamBufferCreate(hal::AUDIO_BUFFER_SIZE*4, 0); - - memset(&tx_callbacks_, 0, sizeof(tx_callbacks_)); - tx_callbacks_.on_sent = tx_sent_callback; - i2s_channel_register_event_callback(tx_handle, &tx_callbacks_, NULL); - - main_task_handle = xTaskGetCurrentTaskHandle(); - - audio_task = std::make_unique(espp::Task::Config{ - .name = "audio task", - .callback = audio_task_fn, - .stack_size_bytes = 1024 * 4, - .priority = 19, - .core_id = 1, - }); - - xStreamBufferReset(tx_audio_stream); - - ESP_ERROR_CHECK(i2s_channel_enable(tx_handle)); - return ret_val; -} - -// es8311 is for audio output codec -static esp_err_t es8311_init_default(void) -{ - fmt::print("initializing es8311 codec...\n"); - esp_err_t ret_val = ESP_OK; - audio_hal_codec_config_t cfg; - memset(&cfg, 0, sizeof(cfg)); - cfg.codec_mode = AUDIO_HAL_CODEC_MODE_DECODE; - cfg.dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1; - cfg.i2s_iface.bits = AUDIO_HAL_BIT_LENGTH_16BITS; - cfg.i2s_iface.fmt = AUDIO_HAL_I2S_NORMAL; - cfg.i2s_iface.mode = AUDIO_HAL_MODE_SLAVE; - cfg.i2s_iface.samples = AUDIO_HAL_16K_SAMPLES; - - ret_val |= es8311_codec_init(&cfg); - ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); - ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); - ret_val |= es8311_codec_set_voice_volume(volume_); - ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); - - if (ESP_OK != ret_val) { - fmt::print("Failed initialize codec\n"); - } else { - fmt::print("Codec initialized\n"); - } - - return ret_val; -} - -static std::unique_ptr mute_task; -static QueueHandle_t gpio_evt_queue; - -static void gpio_isr_handler(void *arg) { - uint32_t gpio_num = (uint32_t)arg; - xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); -} - -static void init_mute_button(void) { - // register that we publish the mute button state - espp::EventManager::get().add_publisher(mute_button_topic, "i2s_audio"); - - fmt::print("Initializing mute button\n"); - mute_button = std::make_shared(espp::Button::Config{ - .name = "mute button", - .interrupt_config = - { - .gpio_num = mute_pin, - .callback = - [&](const espp::Interrupt::Event &event) { - hal::set_muted(event.active); - // simply publish that the mute button was presssed - espp::EventManager::get().publish(mute_button_topic, {}); - }, - .active_level = espp::Interrupt::ActiveLevel::LOW, - .interrupt_type = espp::Interrupt::Type::ANY_EDGE, - .pullup_enabled = true, - .pulldown_enabled = false, - }, - .task_config = - { - .name = "mute button task", - .stack_size_bytes = 4 * 1024, - .priority = 5, - }, - .log_level = espp::Logger::Verbosity::WARN, - }); - - // update the mute state (since it's a flip-flop and may have been set if we - // restarted without power loss) - hal::set_muted(mute_button->is_pressed()); - - fmt::print("Mute button initialized\n"); -} - -static bool initialized = false; -void hal::audio_init() { - if (initialized) return; - - // Config power control IO - gpio_set_direction(sound_power_pin, GPIO_MODE_OUTPUT); - gpio_set_level(sound_power_pin, 1); - - auto internal_i2c = hal::get_internal_i2c(); - - set_es8311_write(std::bind(&espp::I2c::write, internal_i2c.get(), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - set_es8311_read(std::bind(&espp::I2c::read_at_register, internal_i2c.get(), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - - i2s_driver_init(); - es8311_init_default(); - - // now initialize the mute gpio - init_mute_button(); - - audio_task->start(); - - fmt::print("Audio initialized\n"); - - initialized = true; -} - -void IRAM_ATTR hal::play_audio(const uint8_t *data, uint32_t num_bytes) { - if (has_sound) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - } - // don't block here - xStreamBufferSendFromISR(tx_audio_stream, data, num_bytes, NULL); - has_sound = true; -} diff --git a/components/box-emu/src/input.cpp b/components/box-emu/src/input.cpp deleted file mode 100644 index 9b602d5..0000000 --- a/components/box-emu/src/input.cpp +++ /dev/null @@ -1,260 +0,0 @@ - -#include "box_emu_hal.hpp" - -using namespace std::chrono_literals; - -struct TouchpadData { - uint8_t num_touch_points = 0; - uint16_t x = 0; - uint16_t y = 0; - uint8_t btn_state = 0; -}; - -class InputBase { -public: - virtual uint16_t get_pins(std::error_code& ec) = 0; - virtual InputState pins_to_gamepad_state(uint16_t pins) = 0; - virtual void handle_volume_pins(uint16_t pins) = 0; -}; - -template -class Input : public InputBase { -public: - explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} - virtual uint16_t get_pins(std::error_code& ec) override { - auto val = input_driver->get_pins(ec); - if (ec) { - return 0; - } - return val ^ T::INVERT_MASK; - } - virtual InputState pins_to_gamepad_state(uint16_t pins) override { - InputState state; - state.a = (bool)(pins & T::A_PIN); - state.b = (bool)(pins & T::B_PIN); - state.x = (bool)(pins & T::X_PIN); - state.y = (bool)(pins & T::Y_PIN); - state.start = (bool)(pins & T::START_PIN); - state.select = (bool)(pins & T::SELECT_PIN); - state.up = (bool)(pins & T::UP_PIN); - state.down = (bool)(pins & T::DOWN_PIN); - state.left = (bool)(pins & T::LEFT_PIN); - state.right = (bool)(pins & T::RIGHT_PIN); - return state; - } - virtual void handle_volume_pins(uint16_t pins) override { - // check the volume pins and send out events if they're pressed / released - bool volume_up = (bool)(pins & T::VOL_UP_PIN); - bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); - int volume_change = (volume_up * 10) + (volume_down * -10); - if (volume_change != 0) { - // change the volume - int current_volume = hal::get_audio_volume(); - int new_volume = std::clamp(current_volume + volume_change, 0, 100); - hal::set_audio_volume(new_volume); - // send out a volume change event - espp::EventManager::get().publish(volume_changed_topic, {}); - } - } -protected: - std::shared_ptr input_driver; -}; - -static std::shared_ptr input; - -static std::shared_ptr touch_driver; -static std::shared_ptr touchpad; -static std::shared_ptr keypad; -static std::shared_ptr input_timer; -static struct InputState gamepad_state; -static std::mutex gamepad_state_mutex; -static TouchpadData touchpad_data; -static std::mutex touchpad_data_mutex; - -/** - * Touch Controller configuration - */ -void touchpad_read(uint8_t* num_touch_points, uint16_t* x, uint16_t* y, uint8_t* btn_state) { - std::lock_guard lock(touchpad_data_mutex); - *num_touch_points = touchpad_data.num_touch_points; - *x = touchpad_data.x; - *y = touchpad_data.y; - *btn_state = touchpad_data.btn_state; -} - -void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) { - InputState state; - hal::get_input_state(&state); - *up = state.up; - *down = state.down; - *left = state.left; - *right = state.right; - - *enter = state.a || state.start; - *escape = state.b || state.select; -} - -void update_touchpad_input() { - // get the latest data from the device - std::error_code ec; - bool new_data = touch_driver->update(ec); - if (ec) { - fmt::print("error updating touch_driver: {}\n", ec.message()); - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = {}; - return; - } - if (!new_data) { - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = {}; - return; - } - // get the latest data from the touchpad - TouchpadData temp_data; - touch_driver->get_touch_point(&temp_data.num_touch_points, &temp_data.x, &temp_data.y); - temp_data.btn_state = touch_driver->get_home_button_state(); - // update the touchpad data - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = temp_data; -} - -void update_gamepad_input() { - static bool can_read_input = true; - if (!input) { - return; - } - if (!can_read_input) { - return; - } - // pins are active low - // start, select = A0, A1 - std::error_code ec; - auto pins = input->get_pins(ec); - if (ec) { - fmt::print("error getting pins: {}\n", ec.message()); - can_read_input = false; - return; - } - - auto new_gamepad_state = input->pins_to_gamepad_state(pins); - { - std::lock_guard lock(gamepad_state_mutex); - gamepad_state = new_gamepad_state; - } - input->handle_volume_pins(pins); - // TODO: check the battery alert pin and if it's low, send out a battery alert event -} - -static void init_input_generic() { - auto internal_i2c = hal::get_internal_i2c(); - - fmt::print("Initializing touch driver\n"); - touch_driver = std::make_shared(TouchDriver::Config{ - .write = std::bind(&espp::I2c::write, internal_i2c.get(), std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read, internal_i2c.get(), std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .log_level = espp::Logger::Verbosity::WARN - }); - - fmt::print("Initializing touchpad\n"); - touchpad = std::make_shared(espp::TouchpadInput::Config{ - .touchpad_read = touchpad_read, - .swap_xy = touch_swap_xy, - .invert_x = touch_invert_x, - .invert_y = touch_invert_y, - .log_level = espp::Logger::Verbosity::WARN - }); - - auto external_i2c = hal::get_external_i2c(); - - fmt::print("Initializing keypad\n"); - keypad = std::make_shared(espp::KeypadInput::Config{ - .read = keypad_read, - .log_level = espp::Logger::Verbosity::WARN - }); - - fmt::print("Initializing input task\n"); - input_timer = std::make_shared(espp::HighResolutionTimer::Config{ - .name = "Input timer", - .callback = []() { - update_touchpad_input(); - update_gamepad_input(); - }}); - uint64_t period_us = 30 * 1000; - input_timer->periodic(period_us); -} - -static void init_input_v0() { - auto external_i2c = hal::get_external_i2c(); - fmt::print("initializing input driver\n"); - using InputDriver = espp::Mcp23x17; - auto raw_input = new Input( - std::make_shared(InputDriver::Config{ - .port_0_direction_mask = EmuV0::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = EmuV0::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = EmuV0::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = EmuV0::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read_register = std::bind(&espp::I2c::read_at_register, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - .log_level = espp::Logger::Verbosity::WARN - })); - input.reset(raw_input); -} - -static void init_input_v1() { - auto external_i2c = hal::get_external_i2c(); - fmt::print("initializing input driver\n"); - using InputDriver = espp::Aw9523; - auto raw_input = new Input( - std::make_shared(InputDriver::Config{ - .port_0_direction_mask = EmuV1::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = EmuV1::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = EmuV1::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = EmuV1::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .write_then_read = std::bind(&espp::I2c::write_read, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - .log_level = espp::Logger::Verbosity::WARN - })); - input.reset(raw_input); -} - -static std::atomic initialized = false; -void hal::init_input() { - if (initialized) return; - fmt::print("Initializing input subsystem\n"); - // probe the i2c bus for the mcp23x17 (which would be v0) or the aw9523 (which - // would be v1) - auto i2c = hal::get_external_i2c(); - bool mcp23x17_found = i2c->probe_device(espp::Mcp23x17::DEFAULT_ADDRESS); - bool aw9523_found = i2c->probe_device(espp::Aw9523::DEFAULT_ADDRESS); - - if (mcp23x17_found) { - fmt::print("Found MCP23x17, initializing input VERSION 0\n"); - init_input_v0(); - } else if (aw9523_found) { - fmt::print("Found AW9523, initializing input VERSION 1\n"); - init_input_v1(); - } else { - fmt::print("ERROR: No input systems found!\n"); - } - - // now initialize the rest of the input systems which are common to both - // versions - init_input_generic(); - - initialized = true; -} - -extern "C" lv_indev_t *get_keypad_input_device() { - if (!keypad) { - fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); - return nullptr; - } - return keypad->get_input_device(); -} - -void hal::get_input_state(struct InputState* state) { - std::lock_guard lock(gamepad_state_mutex); - *state = gamepad_state; -} diff --git a/components/box-emu/src/mmap.cpp b/components/box-emu/src/mmap.cpp deleted file mode 100644 index 83788bc..0000000 --- a/components/box-emu/src/mmap.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "mmap.hpp" -#include -#include "esp_heap_caps.h" - -static uint8_t* romdata = nullptr; - -void init_memory() { - // allocate memory for the ROM and make sure it's on the SPIRAM - romdata = (uint8_t*)heap_caps_malloc(4*1024*1024, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (romdata == nullptr) { - fmt::print(fg(fmt::terminal_color::red), "ERROR: Couldn't allocate memory for ROM!\n"); - } -} - -size_t copy_romdata_to_cart_partition(const std::string& rom_filename) { - // load the file data and iteratively copy it over - std::ifstream romfile(rom_filename, std::ios::binary | std::ios::ate); //open file at end - if (!romfile.is_open()) { - fmt::print("Error: ROM file does not exist\n"); - return 0; - } - size_t filesize = romfile.tellg(); // get size from current file pointer location; - romfile.seekg(0, std::ios::beg); //reset file pointer to beginning; - romfile.read((char*)(romdata), filesize); - romfile.close(); - return filesize; -} - -extern "C" uint8_t *osd_getromdata() { - return get_mmapped_romdata(); -} - -uint8_t *get_mmapped_romdata() { - return romdata; -} diff --git a/components/box-emu/src/spi_lcd.cpp b/components/box-emu/src/spi_lcd.cpp deleted file mode 100644 index f77da20..0000000 --- a/components/box-emu/src/spi_lcd.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include "box_emu_hal.hpp" - -static spi_device_handle_t spi; -static spi_device_interface_config_t devcfg; - -static constexpr size_t pixel_buffer_size = lcd_width * hal::NUM_ROWS_IN_FRAME_BUFFER; -static std::shared_ptr display; - -std::shared_ptr hal::get_display() { - return display; -} - -static constexpr size_t frame_buffer_size = (((320) * 2) * 240); -static uint8_t *frame_buffer0; -static uint8_t *frame_buffer1; - -// the user flag for the callbacks does two things: -// 1. Provides the GPIO level for the data/command pin, and -// 2. Sets some bits for other signaling (such as LVGL FLUSH) -static constexpr int FLUSH_BIT = (1 << (int)espp::display_drivers::Flags::FLUSH_BIT); -static constexpr int DC_LEVEL_BIT = (1 << (int)espp::display_drivers::Flags::DC_LEVEL_BIT); - -// This function is called (in irq context!) just before a transmission starts. -// It will set the D/C line to the value indicated in the user field -// (DC_LEVEL_BIT). -static void IRAM_ATTR lcd_spi_pre_transfer_callback(spi_transaction_t *t) -{ - uint32_t user_flags = (uint32_t)(t->user); - bool dc_level = user_flags & DC_LEVEL_BIT; - gpio_set_level(lcd_dc_io, dc_level); -} - -// This function is called (in irq context!) just after a transmission ends. It -// will indicate to lvgl that the next flush is ready to be done if the -// FLUSH_BIT is set. -static void IRAM_ATTR lcd_spi_post_transfer_callback(spi_transaction_t *t) -{ - uint16_t user_flags = (uint32_t)(t->user); - bool should_flush = user_flags & FLUSH_BIT; - if (should_flush) { - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_flush_ready(disp->driver); - } -} -// Transaction descriptors. Declared static so they're not allocated on the -// stack; we need this memory even when this function is finished because the -// SPI driver needs access to it even while we're already calculating the next -// line. -static const int spi_queue_size = 6; -static spi_transaction_t trans[spi_queue_size]; -static std::atomic num_queued_trans = 0; - -static void IRAM_ATTR lcd_wait_lines() { - spi_transaction_t *rtrans; - esp_err_t ret; - // fmt::print("Waiting for {} queued transactions\n", num_queued_trans); - // Wait for all transactions to be done and get back the results. - while (num_queued_trans) { - ret = spi_device_get_trans_result(spi, &rtrans, 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Could not get trans result: {} '{}'\n", ret, esp_err_to_name(ret)); - } - num_queued_trans--; - //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. - } -} - - -void IRAM_ATTR hal::lcd_write(const uint8_t *data, size_t length, uint32_t user_data) { - if (length == 0) { - return; - } - lcd_wait_lines(); - esp_err_t ret; - memset(&trans[0], 0, sizeof(spi_transaction_t)); - trans[0].length = length * 8; - trans[0].user = (void*)user_data; - // look at the length of the data and use tx_data if it is <= 32 bits - if (length <= 4) { - // copy the data pointer to trans[0].tx_data - memcpy(trans[0].tx_data, data, length); - trans[0].flags = SPI_TRANS_USE_TXDATA; - } else { - trans[0].tx_buffer = data; - trans[0].flags = 0; - } - ret = spi_device_queue_trans(spi, &trans[0], 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); - } else { - num_queued_trans++; - } -} - -void IRAM_ATTR hal::lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data) { - // if we haven't waited by now, wait here... - lcd_wait_lines(); - esp_err_t ret; - size_t length = (xe-xs+1)*(ye-ys+1)*2; - if (length == 0) { - fmt::print("Bad length: ({},{}) to ({},{})\n", xs, ys, xe, ye); - } - // initialize the spi transactions - for (int i=0; i<6; i++) { - memset(&trans[i], 0, sizeof(spi_transaction_t)); - if ((i&1)==0) { - //Even transfers are commands - trans[i].length = 8; - trans[i].user = (void*)0; - } else { - //Odd transfers are data - trans[i].length = 8*4; - trans[i].user = (void*)DC_LEVEL_BIT; - } - trans[i].flags = SPI_TRANS_USE_TXDATA; - } - trans[0].tx_data[0] = (uint8_t)DisplayDriver::Command::caset; - trans[1].tx_data[0] = (xs)>> 8; - trans[1].tx_data[1] = (xs)&0xff; - trans[1].tx_data[2] = (xe)>>8; - trans[1].tx_data[3] = (xe)&0xff; - trans[2].tx_data[0] = (uint8_t)DisplayDriver::Command::raset; - trans[3].tx_data[0] = (ys)>>8; - trans[3].tx_data[1] = (ys)&0xff; - trans[3].tx_data[2] = (ye)>>8; - trans[3].tx_data[3] = (ye)&0xff; - trans[4].tx_data[0] = (uint8_t)DisplayDriver::Command::ramwr; - trans[5].tx_buffer = data; - trans[5].length = length*8; - // undo SPI_TRANS_USE_TXDATA flag - trans[5].flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL; - // we need to keep the dc bit set, but also add our flags - trans[5].user = (void*)(DC_LEVEL_BIT | user_data); - //Queue all transactions. - for (int i=0; i<6; i++) { - ret = spi_device_queue_trans(spi, &trans[i], 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); - } else { - num_queued_trans++; - } - } - //When we are here, the SPI driver is busy (in the background) getting the - //transactions sent. That happens mostly using DMA, so the CPU doesn't have - //much to do here. We're not going to wait for the transaction to finish - //because we may as well spend the time calculating the next line. When that - //is done, we can call lcd_wait_lines, which will wait for the transfers - //to be done and check their status. -} - -extern "C" uint16_t make_color(uint8_t r, uint8_t g, uint8_t b) { - return lv_color_make(r,g,b).full; -} - -uint16_t* hal::get_vram0() { - return display->vram0(); -} - -uint16_t* hal::get_vram1() { - return display->vram1(); -} - -uint8_t* hal::get_frame_buffer0() { - return frame_buffer0; -} - -uint8_t* hal::get_frame_buffer1() { - return frame_buffer1; -} - -void hal::lcd_write_frame(const uint16_t xs, const uint16_t ys, const uint16_t width, const uint16_t height, const uint8_t * data){ - if (data) { - // have data, fill the area with the color data - lv_area_t area { - .x1 = (lv_coord_t)(xs), - .y1 = (lv_coord_t)(ys), - .x2 = (lv_coord_t)(xs+width-1), - .y2 = (lv_coord_t)(ys+height-1)}; - DisplayDriver::fill(nullptr, &area, (lv_color_t*)data); - } else { - // don't have data, so clear the area (set to 0) - DisplayDriver::clear(xs, ys, width, height); - } -} - -static bool initialized = false; -void hal::lcd_init() { - if (initialized) { - return; - } - esp_err_t ret; - - spi_bus_config_t buscfg; - memset(&buscfg, 0, sizeof(buscfg)); - buscfg.mosi_io_num = lcd_mosi_io; - buscfg.miso_io_num = -1; - buscfg.sclk_io_num = lcd_sclk_io; - buscfg.quadwp_io_num = -1; - buscfg.quadhd_io_num = -1; - buscfg.max_transfer_sz = frame_buffer_size * sizeof(lv_color_t) + 100; - - memset(&devcfg, 0, sizeof(devcfg)); - devcfg.mode = 0; - // devcfg.flags = SPI_DEVICE_NO_RETURN_RESULT; - devcfg.clock_speed_hz = lcd_clock_speed; - devcfg.input_delay_ns = 0; - devcfg.spics_io_num = lcd_cs_io; - devcfg.queue_size = spi_queue_size; - devcfg.pre_cb = lcd_spi_pre_transfer_callback; - devcfg.post_cb = lcd_spi_post_transfer_callback; - - //Initialize the SPI bus - ret = spi_bus_initialize(lcd_spi_num, &buscfg, SPI_DMA_CH_AUTO); - ESP_ERROR_CHECK(ret); - //Attach the LCD to the SPI bus - ret = spi_bus_add_device(lcd_spi_num, &devcfg, &spi); - ESP_ERROR_CHECK(ret); - // initialize the controller - DisplayDriver::initialize(espp::display_drivers::Config{ - .lcd_write = hal::lcd_write, - .lcd_send_lines = hal::lcd_send_lines, - .reset_pin = lcd_reset_io, - .data_command_pin = lcd_dc_io, - .reset_value = reset_value, - .invert_colors = invert_colors, - .mirror_x = mirror_x, - .mirror_y = mirror_y - }); - // initialize the display / lvgl - using namespace std::chrono_literals; - display = std::make_shared(espp::Display::AllocatingConfig{ - .width = lcd_width, - .height = lcd_height, - .pixel_buffer_size = pixel_buffer_size, - .flush_callback = DisplayDriver::flush, - .backlight_pin = backlight_io, - .backlight_on_value = backlight_value, - .task_config = { - .name = "display task", - .priority = 10, - .core_id = 1, - }, - .update_period = 5ms, - .double_buffered = true, - .allocation_flags = MALLOC_CAP_8BIT | MALLOC_CAP_DMA, - .rotation = rotation, - .software_rotation_enabled = true, - }); - - frame_buffer0 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - frame_buffer1 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - initialized = true; -} - -void hal::set_display_brightness(float brightness) { - display->set_brightness(brightness); -} - -float hal::get_display_brightness() { - return display->get_brightness(); -} diff --git a/components/box-emu/src/video_setting.cpp b/components/box-emu/src/video_setting.cpp deleted file mode 100644 index d4b59a3..0000000 --- a/components/box-emu/src/video_setting.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::atomic video_setting_{VideoSetting::FIT}; - -VideoSetting hal::get_video_setting() { - return video_setting_; -} - -void hal::set_video_setting(const VideoSetting& setting) { - video_setting_ = setting; -} diff --git a/components/box-emu/src/video_task.cpp b/components/box-emu/src/video_task.cpp deleted file mode 100644 index e0f5320..0000000 --- a/components/box-emu/src/video_task.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "box_emu_hal.hpp" - -using namespace hal; - -static std::shared_ptr video_task_; -static QueueHandle_t video_queue_; - -static size_t display_width = lcd_width; -static size_t display_height = lcd_height; - -static size_t native_width = lcd_width; -static size_t native_height = lcd_height; -static int native_pitch = lcd_width; - -static const uint16_t* palette = nullptr; -static size_t palette_size = 256; - -static bool video_task(std::mutex &m, std::condition_variable& cv); - -void hal::init_video_task() { - static bool initialized = false; - if (initialized) { - return; - } - fmt::print("initializing video task...\n"); - video_queue_ = xQueueCreate(1, sizeof(uint16_t*)); - video_task_ = std::make_shared(espp::Task::Config{ - .name = "video task", - .callback = video_task, - .stack_size_bytes = 4*1024, - .priority = 20, - .core_id = 1 - }); - video_task_->start(); - initialized = true; -} - -void hal::set_display_size(size_t width, size_t height) { - display_width = width; - display_height = height; -} - -void hal::set_native_size(size_t width, size_t height, int pitch) { - native_width = width; - native_height = height; - native_pitch = pitch == -1 ? width : pitch; -} - -void hal::set_palette(const uint16_t* _palette, size_t size) { - palette = _palette; - palette_size = size; -} - -void hal::push_frame(const void* frame) { - if (video_queue_ == nullptr) { - fmt::print("video queue is null, make sure to call init_video_task() first\n"); - return; - } - xQueueSend(video_queue_, &frame, 10 / portTICK_PERIOD_MS); -} - -static bool has_palette() { - return palette != nullptr; -} - -static bool is_native() { - return native_width == display_width && native_height == display_height; -} - -static int get_x_offset() { - return (lcd_width-display_width)/2; -} - -static int get_y_offset() { - return (lcd_height-display_height)/2; -} - -static const uint16_t* get_palette() { - return palette; -} - -static bool video_task(std::mutex &m, std::condition_variable& cv) { - const void *_frame_ptr; - if (xQueuePeek(video_queue_, &_frame_ptr, 100 / portTICK_PERIOD_MS) != pdTRUE) { - // we couldn't get anything from the queue, return - return false; - } - if (_frame_ptr == nullptr) { - // make sure we clear the queue - xQueueReceive(video_queue_, &_frame_ptr, 10 / portTICK_PERIOD_MS); - // we got a nullptr, return - return false; - } - static constexpr int num_lines_to_write = NUM_ROWS_IN_FRAME_BUFFER; - static int vram_index = 0; // has to be static so that it persists between calls - const int x_offset = get_x_offset(); - const int y_offset = get_y_offset(); - const uint16_t* _palette = get_palette(); - if (is_native()) { - for (int y=0; y(num_lines_to_write, display_height-y); - if (has_palette()) { - const uint8_t* _frame = (const uint8_t*)_frame_ptr; - for (int i=0; i(x_scale * native_width, 0, lcd_width); - for (int y=0; y= max_y) { - break; - } - int source_y = (float)_y * inv_y_scale; - // shoudl i put this around the outer loop or is this loop a good - // balance for perfomance of the check? - if (has_palette()) { - const uint8_t* _frame = (const uint8_t*)_frame_ptr; - // write two pixels (32 bits) at a time because it's faster - for (int x=0; x + #include "event_manager.hpp" static const std::string mute_button_topic = "mute"; diff --git a/components/statistics/CMakeLists.txt b/components/statistics/CMakeLists.txt new file mode 100644 index 0000000..851fca2 --- /dev/null +++ b/components/statistics/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register( + INCLUDE_DIRS "include" + SRC_DIRS "src" + REQUIRES "format") diff --git a/components/statistics/include/statistics.hpp b/components/statistics/include/statistics.hpp new file mode 100644 index 0000000..077c55b --- /dev/null +++ b/components/statistics/include/statistics.hpp @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "format.hpp" + +void update_frame_time(uint64_t frame_time); +void reset_frame_time(); + +float get_fps(); +uint64_t get_frame_time(); +uint64_t get_frame_time_max(); +uint64_t get_frame_time_min(); +float get_frame_time_avg(); + +void print_statistics(); diff --git a/components/statistics/src/statistics.cpp b/components/statistics/src/statistics.cpp new file mode 100644 index 0000000..0c5703a --- /dev/null +++ b/components/statistics/src/statistics.cpp @@ -0,0 +1,59 @@ +#include "statistics.hpp" + +static uint32_t num_frames = 0; +static uint64_t frame_time = 0.0f; +static uint64_t frame_time_total = 0.0f; +static uint64_t frame_time_max = 0.0f; +static uint64_t frame_time_min = 0.0f; +static float frame_time_avg = 0.0f; + +void update_frame_time(uint64_t frame_time) +{ + num_frames++; + ::frame_time = frame_time; + frame_time_total = frame_time_total + frame_time; + frame_time_max = std::max(frame_time_max, frame_time); + frame_time_min = std::min(frame_time_min, frame_time); + frame_time_avg = float(frame_time_total) / num_frames; +} + +void reset_frame_time() +{ + num_frames = 0; + frame_time = 0; + frame_time_total = 0; + frame_time_max = 0; + frame_time_min = 1000000; // some large number + frame_time_avg = 0.0f; +} + +float get_fps() { + if (frame_time_total == 0) { + return 0.0f; + } + return num_frames / (frame_time_total / 1e6f); +} + +uint64_t get_frame_time() { + return frame_time; +} + +uint64_t get_frame_time_max() { + return frame_time_max; +} + +uint64_t get_frame_time_min() { + return frame_time_min; +} + +float get_frame_time_avg() { + return frame_time_avg; +} + +void print_statistics() { + fmt::print("Statistics:\n"); + fmt::print("-----------\n"); + fmt::print("Frames: {}\n", num_frames); + fmt::print("FPS: {:.1f}\n", get_fps()); + fmt::print("Frame Time: [min {} us, avg: {:.1f} us, max: {} us]\n", get_frame_time_min(), get_frame_time_avg(), get_frame_time_max()); +} From c675050d22498bc6c594c7853769fb9bf5485edc Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Tue, 2 Jul 2024 21:51:55 -0500 Subject: [PATCH 06/14] update for build-able example --- .github/workflows/build.yml | 13 +- components/box-emu/Kconfig.projbuild | 14 -- .../box-emu/example/main/box_emu_example.cpp | 2 +- components/box-emu/example/partitions.csv | 4 + components/box-emu/example/sdkconfig.defaults | 10 + components/box-emu/include/box-emu.hpp | 5 + components/box-emu/include/usb.hpp | 16 -- components/box-emu/src/battery.cpp | 113 ---------- components/box-emu/src/box-emu.cpp | 210 ++++++++++++++++++ components/box-emu/src/usb.cpp | 119 ---------- 10 files changed, 241 insertions(+), 265 deletions(-) delete mode 100644 components/box-emu/Kconfig.projbuild create mode 100644 components/box-emu/example/partitions.csv delete mode 100644 components/box-emu/include/usb.hpp delete mode 100644 components/box-emu/src/battery.cpp delete mode 100644 components/box-emu/src/usb.cpp diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index cb101fe..04e0f8b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -6,6 +6,15 @@ jobs: build: runs-on: ubuntu-latest + continue-on-error: true + + strategy: + matrix: + test: + - path: '.' + target: esp32s3 + - path: 'components/box-emu/example' + target: esp32s3 steps: - name: Checkout repo @@ -17,5 +26,5 @@ jobs: uses: espressif/esp-idf-ci-action@v1 with: esp_idf_version: release-v5.2 - target: esp32s3 - path: '.' + target: ${{matrix.test.target}} + path: ${{matrix.test.path}} diff --git a/components/box-emu/Kconfig.projbuild b/components/box-emu/Kconfig.projbuild deleted file mode 100644 index ee31925..0000000 --- a/components/box-emu/Kconfig.projbuild +++ /dev/null @@ -1,14 +0,0 @@ -menu "BOX Emulator Configuration" - - choice - prompt "Module Configuration" - default HARDWARE_BOX - help - Select which display + SoC module you're using. - config HARDWARE_BOX - bool "ESP32-S3-BOX" - config HARDWARE_BOX_3 - bool "ESP32-S3-BOX-3" - endchoice - -endmenu diff --git a/components/box-emu/example/main/box_emu_example.cpp b/components/box-emu/example/main/box_emu_example.cpp index 2ddd2d8..ee0c2d8 100644 --- a/components/box-emu/example/main/box_emu_example.cpp +++ b/components/box-emu/example/main/box_emu_example.cpp @@ -2,7 +2,7 @@ #include #include -#include "esp-box.hpp" +#include "box-emu.hpp" using namespace std::chrono_literals; diff --git a/components/box-emu/example/partitions.csv b/components/box-emu/example/partitions.csv new file mode 100644 index 0000000..b8a6dbe --- /dev/null +++ b/components/box-emu/example/partitions.csv @@ -0,0 +1,4 @@ +# Name, Type, SubType, Offset, Size +nvs, data, nvs, 0x9000, 0x6000 +phy_init, data, phy, 0xf000, 0x1000 +factory, app, factory, 0x10000, 6M diff --git a/components/box-emu/example/sdkconfig.defaults b/components/box-emu/example/sdkconfig.defaults index 177e6f3..84ba9a6 100644 --- a/components/box-emu/example/sdkconfig.defaults +++ b/components/box-emu/example/sdkconfig.defaults @@ -20,3 +20,13 @@ CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144 # set the functions into IRAM CONFIG_SPI_MASTER_IN_IRAM=y + +CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y +CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHMODE_QIO=y # over twice as fast as DIO + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" diff --git a/components/box-emu/include/box-emu.hpp b/components/box-emu/include/box-emu.hpp index 3a62226..c754435 100644 --- a/components/box-emu/include/box-emu.hpp +++ b/components/box-emu/include/box-emu.hpp @@ -13,6 +13,7 @@ #include #include +#include #include #include "esp-box.hpp" @@ -285,6 +286,7 @@ class BoxEmu : public espp::BaseComponent { std::shared_ptr input_timer_; // battery + std::atomic battery_comms_good_{true}; std::shared_ptr battery_{nullptr}; std::shared_ptr adc_{nullptr}; std::shared_ptr battery_task_; @@ -305,6 +307,9 @@ class BoxEmu : public espp::BaseComponent { const uint16_t* palette_{nullptr}; size_t palette_size_{256}; + // usb + std::atomic usb_enabled_{false}; + usb_phy_handle_t jtag_phy_; }; // for libfmt printing of the BoxEmu::Version enum diff --git a/components/box-emu/include/usb.hpp b/components/box-emu/include/usb.hpp deleted file mode 100644 index de33e6a..0000000 --- a/components/box-emu/include/usb.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include "fs_init.hpp" - -bool usb_is_enabled(); -void usb_init(); -void usb_deinit(); diff --git a/components/box-emu/src/battery.cpp b/components/box-emu/src/battery.cpp deleted file mode 100644 index 73bb7fc..0000000 --- a/components/box-emu/src/battery.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::shared_ptr battery_{nullptr}; -static std::shared_ptr adc_{nullptr}; -static std::shared_ptr battery_task_; -static bool battery_initialized_ = false; -static std::vector channels; - -using namespace std::chrono_literals; - -void hal::battery_init() { - if (battery_initialized_) { - return; - } - fmt::print("Initializing battery...\n"); - auto i2c = hal::get_external_i2c(); - bool can_communicate = i2c->probe_device(espp::Max1704x::DEFAULT_ADDRESS); - if (!can_communicate) { - fmt::print("Could not communicate with battery!\n"); - // go ahead and set the battery_initialized_ flag to true so we don't try to - // initialize the battery again - battery_initialized_ = true; - return; - } - - // // NOTE: we could also make an ADC for measuring battery voltage - // // make the adc channels - // channels.clear(); - // channels.push_back({ - // .unit = BATTERY_ADC_UNIT, - // .channel = BATTERY_ADC_CHANNEL, - // .attenuation = ADC_ATTEN_DB_12}); - // adc_ = std::make_shared(espp::OneshotAdc::Config{ - // .unit = BATTERY_ADC_UNIT, - // .channels = channels - // }); - - // now make the Max17048 that we'll use to get good state of charge, charge - // rate, etc. - battery_ = std::make_shared(espp::Max1704x::Config{ - .device_address = espp::Max1704x::DEFAULT_ADDRESS, - .write = std::bind(&espp::I2c::write, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) - }); - // NOTE: the MAX17048 is tied to the VBAT for its power supply (as you would - // imagine), this means that we cannnot communicate with it if the battery is - // not connected. Therefore, if we are unable to communicate with the battery - // we will just return and not start the battery task. - battery_task_ = std::make_shared(espp::HighResolutionTimer::Config{ - .name = "battery", - .callback = []() { - std::error_code ec; - // get the voltage (V) - auto voltage = battery_->get_battery_voltage(ec); - if (ec) { - fmt::print("Error getting battery voltage: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - // get the state of charge (%) - auto soc = battery_->get_battery_percentage(ec); - if (ec) { - fmt::print("Error getting battery percentage: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - // get the charge rate (+/- % per hour) - auto charge_rate = battery_->get_battery_charge_rate(ec); - if (ec) { - fmt::print("Error getting battery charge rate: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - - // NOTE: we could also get voltage from the adc for the battery if we - // wanted, but the MAX17048 gives us the same voltage anyway - // auto maybe_mv = adc_->read_mv(channels[0]); - // if (maybe_mv.has_value()) { - // // convert mv -> V and from the voltage divider (R1=R2) to real - // // battery volts - // voltage = maybe_mv.value() / 1000.0f * 2.0f; - // } - - // now publish a BatteryInfo struct to the battery_topic - auto battery_info = BatteryInfo{ - .voltage = voltage, - .level = soc, - .charge_rate = charge_rate, - }; - std::vector battery_info_data; - // fmt::print("Publishing battery info: {}\n", battery_info); - auto bytes_serialized = espp::serialize(battery_info, battery_info_data); - if (bytes_serialized == 0) { - return; - } - espp::EventManager::get().publish(battery_topic, battery_info_data); - return; - }}); - uint64_t battery_period_us = 500 * 1000; // 500ms - battery_task_->periodic(battery_period_us); - battery_initialized_ = true; -} - -std::shared_ptr hal::get_battery() { - battery_init(); - return battery_; -} diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index e0182d2..400a35d 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -1,5 +1,13 @@ #include "box-emu.hpp" +BoxEmu::BoxEmu() : espp::BaseComponent("BoxEmu") { + detect(); +} + +BoxEmu::Version BoxEmu::version() const { + return version_; +} + void BoxEmu::detect() { bool mcp23x17_found = external_i2c_.probe_device(espp::Mcp23x17::DEFAULT_ADDRESS); bool aw9523_found = external_i2c_.probe_device(espp::Aw9523::DEFAULT_ADDRESS); @@ -302,6 +310,95 @@ bool BoxEmu::initialize_battery() { return false; } + battery_comms_good_ = external_i2c_.probe_device(espp::Max1704x::DEFAULT_ADDRESS); + if (!battery_comms_good_) { + logger_.error("Could not communicate with battery!"); + return false; + } + + // now make the Max17048 that we'll use to get good state of charge, charge + // rate, etc. + battery_ = std::make_shared(espp::Max1704x::Config{ + .device_address = espp::Max1704x::DEFAULT_ADDRESS, + .write = std::bind(&espp::I2c::write, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .read = std::bind(&espp::I2c::read, &external_i2c_, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) + }); + + // // NOTE: we could also make an ADC for measuring battery voltage + // // make the adc channels + // channels.clear(); + // channels.push_back({ + // .unit = BATTERY_ADC_UNIT, + // .channel = BATTERY_ADC_CHANNEL, + // .attenuation = ADC_ATTEN_DB_12}); + // adc_ = std::make_shared(espp::OneshotAdc::Config{ + // .unit = BATTERY_ADC_UNIT, + // .channels = channels + // }); + + // NOTE: the MAX17048 is tied to the VBAT for its power supply (as you would + // imagine), this means that we cannnot communicate with it if the battery is + // not connected. Therefore, if we are unable to communicate with the battery + // we will just return and not start the battery task. + battery_task_ = std::make_shared(espp::HighResolutionTimer::Config{ + .name = "battery", + .callback = [this]() { + std::error_code ec; + // get the voltage (V) + auto voltage = battery_->get_battery_voltage(ec); + if (ec) { + fmt::print("Error getting battery voltage: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + // get the state of charge (%) + auto soc = battery_->get_battery_percentage(ec); + if (ec) { + fmt::print("Error getting battery percentage: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + // get the charge rate (+/- % per hour) + auto charge_rate = battery_->get_battery_charge_rate(ec); + if (ec) { + fmt::print("Error getting battery charge rate: {}\n", ec.message()); + fmt::print("Battery is probably not connected!\n"); + fmt::print("Stopping battery task...\n"); + battery_task_->stop(); + return; + } + + // NOTE: we could also get voltage from the adc for the battery if we + // wanted, but the MAX17048 gives us the same voltage anyway + // auto maybe_mv = adc_->read_mv(channels[0]); + // if (maybe_mv.has_value()) { + // // convert mv -> V and from the voltage divider (R1=R2) to real + // // battery volts + // voltage = maybe_mv.value() / 1000.0f * 2.0f; + // } + + // now publish a BatteryInfo struct to the battery_topic + auto battery_info = BatteryInfo{ + .voltage = voltage, + .level = soc, + .charge_rate = charge_rate, + }; + std::vector battery_info_data; + // fmt::print("Publishing battery info: {}\n", battery_info); + auto bytes_serialized = espp::serialize(battery_info, battery_info_data); + if (bytes_serialized == 0) { + return; + } + espp::EventManager::get().publish(battery_topic, battery_info_data); + return; + }}); + uint64_t battery_period_us = 500 * 1000; // 500ms + battery_task_->periodic(battery_period_us); + return true; } @@ -369,6 +466,119 @@ void BoxEmu::video_setting(const VideoSetting setting) { // USB ///////////////////////////////////////////////////////////////////////////// +#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) + +enum { + ITF_NUM_MSC = 0, + ITF_NUM_TOTAL +}; + +enum { + EDPT_CTRL_OUT = 0x00, + EDPT_CTRL_IN = 0x80, + + EDPT_MSC_OUT = 0x01, + EDPT_MSC_IN = 0x81, +}; + +static uint8_t const desc_configuration[] = { + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), + + // Interface number, string index, EP Out & EP In address, EP size + TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64), +}; + +static tusb_desc_device_t descriptor_config = { + .bLength = sizeof(descriptor_config), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = TUSB_CLASS_MISC, + .bDeviceSubClass = MISC_SUBCLASS_COMMON, + .bDeviceProtocol = MISC_PROTOCOL_IAD, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers + .idProduct = 0x4002, + .bcdDevice = 0x100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static char const *string_desc_arr[] = { + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "Finger563", // 1: Manufacturer + "ESP-Box-Emu", // 2: Product + "123456", // 3: Serials + "Box-Emu uSD Card", // 4. MSC +}; + +bool BoxEmu::is_usb_enabled() const { + return usb_enabled_; +} + +bool BoxEmu::initialize_usb() { + // get the card from the filesystem initialization + auto card = sdcard(); + if (!card) { + logger_.error("No SD card found, skipping USB MSC initialization"); + return false; + } + + logger_.debug("Deleting JTAG PHY"); + usb_del_phy(jtag_phy_); + + fmt::print("USB MSC initialization\n"); + // register the callback for the storage mount changed event. + const tinyusb_msc_sdmmc_config_t config_sdmmc = { + .card = card, + .callback_mount_changed = nullptr, // storage_mount_changed_cb, + .mount_config = { + .max_files = 5, + } + }; + ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); + // ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); + + // initialize the tinyusb stack + fmt::print("USB MSC initialization\n"); + const tinyusb_config_t tusb_cfg = { + .device_descriptor = &descriptor_config, + .string_descriptor = string_desc_arr, + .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), + .external_phy = false, + .configuration_descriptor = desc_configuration, + }; + ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); + fmt::print("USB MSC initialization DONE\n"); + usb_enabled_ = true; + + return true; +} + +bool BoxEmu::deinitialize_usb() { + if (!usb_enabled_) { + logger_.warn("USB MSC not initialized"); + return false; + } + logger_.info("USB MSC deinitialization"); + auto err = tinyusb_driver_uninstall(); + if (err != ESP_OK) { + logger_.error("tinyusb_driver_uninstall failed: {}", esp_err_to_name(err)); + return false; + } + usb_enabled_ = false; + // and reconnect the CDC port, see: + // https://github.com/espressif/idf-extra-components/pull/229 + usb_phy_config_t phy_conf = { + // NOTE: for some reason, USB_PHY_CTRL_SERIAL_JTAG is not defined in the SDK + // for the ESP32s3 + .controller = USB_PHY_CTRL_SERIAL_JTAG, // (usb_phy_controller_t)1, + }; + usb_new_phy(&phy_conf, &jtag_phy_); + return true; +} ///////////////////////////////////////////////////////////////////////////// // Static Video Task: diff --git a/components/box-emu/src/usb.cpp b/components/box-emu/src/usb.cpp deleted file mode 100644 index db7ba1d..0000000 --- a/components/box-emu/src/usb.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "usb.hpp" - -#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) - -enum { - ITF_NUM_MSC = 0, - ITF_NUM_TOTAL -}; - -enum { - EDPT_CTRL_OUT = 0x00, - EDPT_CTRL_IN = 0x80, - - EDPT_MSC_OUT = 0x01, - EDPT_MSC_IN = 0x81, -}; - -static uint8_t const desc_configuration[] = { - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - // Interface number, string index, EP Out & EP In address, EP size - TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64), -}; - -static tusb_desc_device_t descriptor_config = { - .bLength = sizeof(descriptor_config), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers - .idProduct = 0x4002, - .bcdDevice = 0x100, - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - .bNumConfigurations = 0x01 -}; - -static char const *string_desc_arr[] = { - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "Finger563", // 1: Manufacturer - "ESP-Box-Emu", // 2: Product - "123456", // 3: Serials - "Box-Emu uSD Card", // 4. MSC -}; - -// callback that is delivered when storage is mounted/unmounted by application. -static void storage_mount_changed_cb(tinyusb_msc_event_t *event) -{ - fmt::print("Storage mounted to application: {}\n", event->mount_changed_data.is_mounted ? "Yes" : "No"); -} - -static bool usb_enabled_ = false; -static usb_phy_handle_t jtag_phy; - -bool usb_is_enabled() { - return usb_enabled_; -} - -void usb_init() { - // get the card from the filesystem initialization - auto card = get_sdcard(); - if (!card) { - fmt::print("No SD card found, skipping USB MSC initialization\n"); - return; - } - - fmt::print("Deleting JTAG PHY\n"); - usb_del_phy(jtag_phy); - - fmt::print("USB MSC initialization\n"); - // register the callback for the storage mount changed event. - const tinyusb_msc_sdmmc_config_t config_sdmmc = { - .card = card, - .callback_mount_changed = storage_mount_changed_cb, - .mount_config = { - .max_files = 5, - } - }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); - ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); - - // initialize the tinyusb stack - fmt::print("USB MSC initialization\n"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = &descriptor_config, - .string_descriptor = string_desc_arr, - .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), - .external_phy = false, - .configuration_descriptor = desc_configuration, - }; - ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); - fmt::print("USB MSC initialization DONE\n"); - usb_enabled_ = true; -} - -void usb_deinit() { - if (!usb_enabled_) { - return; - } - fmt::print("USB MSC deinitialization\n"); - auto err = tinyusb_driver_uninstall(); - if (err != ESP_OK) { - fmt::print("tinyusb_driver_uninstall failed: {}\n", esp_err_to_name(err)); - } - usb_enabled_ = false; - // and reconnect the CDC port, see: - // https://github.com/espressif/idf-extra-components/pull/229 - usb_phy_config_t phy_conf = { - // NOTE: for some reason, USB_PHY_CTRL_SERIAL_JTAG is not defined in the SDK - // for the ESP32s3 - .controller = (usb_phy_controller_t)1, - }; - usb_new_phy(&phy_conf, &jtag_phy); -} From c38ec618ff7beffd0dbc29b93e99461a65488600 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 08:05:56 -0500 Subject: [PATCH 07/14] update for working example --- .../box-emu/example/main/box_emu_example.cpp | 55 ++++++++++--------- components/box-emu/example/sdkconfig.defaults | 31 +++++++++++ components/box-emu/src/box-emu.cpp | 17 +++++- 3 files changed, 75 insertions(+), 28 deletions(-) diff --git a/components/box-emu/example/main/box_emu_example.cpp b/components/box-emu/example/main/box_emu_example.cpp index ee0c2d8..aa072b9 100644 --- a/components/box-emu/example/main/box_emu_example.cpp +++ b/components/box-emu/example/main/box_emu_example.cpp @@ -57,14 +57,6 @@ extern "C" void app_main(void) { return; } - // set the pixel buffer to be 50 lines high - static constexpr size_t pixel_buffer_size = box.lcd_width() * 50; - // initialize the LVGL display for the esp-box - if (!box.initialize_display(pixel_buffer_size)) { - logger.error("Failed to initialize display!"); - return; - } - // set the background color to black lv_obj_t *bg = lv_obj_create(lv_scr_act()); lv_obj_set_size(bg, box.lcd_width(), box.lcd_height()); @@ -100,26 +92,37 @@ extern "C" void app_main(void) { box.brightness(75.0f); auto previous_touchpad_data = box.touchpad_convert(box.touchpad_data()); + // NOTE: while in the EspBox example we had to call the + // EspBox::update_touch(), we don't have to anymore since the BoxEmu has an + // internal input update task which updates both the touchscreen and the + // gamepad while (true) { std::this_thread::sleep_for(100ms); - if (box.update_touch()) { - // NOTE: since we're directly using the touchpad data, and not using the - // TouchpadInput + LVGL, we'll need to ensure the touchpad data is - // converted into proper screen coordinates instead of simply using the - // raw values. - auto touchpad_data = box.touchpad_convert(box.touchpad_data()); - if (touchpad_data != previous_touchpad_data) { - logger.info("Touch: {}", touchpad_data); - previous_touchpad_data = touchpad_data; - // if the button is pressed, clear the circles - if (touchpad_data.btn_state) { - clear_circles(); - } - // if there is a touch point, draw a circle and play a click sound - if (touchpad_data.num_touch_points > 0) { - draw_circle(touchpad_data.x, touchpad_data.y, 10); - play_click(box); - } + auto gamepad_state = emu.gamepad_state(); + if (gamepad_state.a) { + // clear the circles + clear_circles(); + } + if (gamepad_state.b) { + // play a click sound + play_click(box); + } + // NOTE: since we're directly using the touchpad data, and not using the + // TouchpadInput + LVGL, we'll need to ensure the touchpad data is + // converted into proper screen coordinates instead of simply using the + // raw values. + auto touchpad_data = box.touchpad_convert(box.touchpad_data()); + if (touchpad_data != previous_touchpad_data) { + logger.info("Touch: {}", touchpad_data); + previous_touchpad_data = touchpad_data; + // if the button is pressed, clear the circles + if (touchpad_data.btn_state) { + clear_circles(); + } + // if there is a touch point, draw a circle and play a click sound + if (touchpad_data.num_touch_points > 0) { + draw_circle(touchpad_data.x, touchpad_data.y, 10); + play_click(box); } } } diff --git a/components/box-emu/example/sdkconfig.defaults b/components/box-emu/example/sdkconfig.defaults index 84ba9a6..4f790c3 100644 --- a/components/box-emu/example/sdkconfig.defaults +++ b/components/box-emu/example/sdkconfig.defaults @@ -18,6 +18,17 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=16384 # Set esp-timer task stack size to 6KB CONFIG_ESP_TIMER_TASK_STACK_SIZE=6144 +# SPIRAM Configuration +CONFIG_SPIRAM=y +CONFIG_SPIRAM_USE_MALLOC=y +CONFIG_SPIRAM_MODE_OCT=y +CONFIG_SPIRAM_SPEED_80M=y +# anything larger than this is attempted to allocate within SPIRAM first +CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0 +# specifcally reserved internally for DMA +CONFIG_SPIRAM_MALLOC_RESERVE_INTERNAL=262144 +# CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY=y + # set the functions into IRAM CONFIG_SPI_MASTER_IN_IRAM=y @@ -30,3 +41,23 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y # over twice as fast as DIO # CONFIG_PARTITION_TABLE_CUSTOM=y CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" + +# +# LVGL configuration - # Color settings +# +# CONFIG_LV_COLOR_DEPTH_32 is not set +CONFIG_LV_COLOR_DEPTH_16=y +# CONFIG_LV_COLOR_DEPTH_8 is not set +# CONFIG_LV_COLOR_DEPTH_1 is not set +CONFIG_LV_COLOR_DEPTH=16 +CONFIG_LV_COLOR_16_SWAP=y +CONFIG_LV_COLOR_MIX_ROUND_OFS=128 +CONFIG_LV_COLOR_CHROMA_KEY_HEX=0x00FF00 + +# +# LVGL configuration - # Themes +# +CONFIG_LV_USE_THEME_DEFAULT=y +CONFIG_LV_THEME_DEFAULT_DARK=y +CONFIG_LV_THEME_DEFAULT_GROW=y +CONFIG_LV_THEME_DEFAULT_TRANSITION_TIME=80 diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index 400a35d..838c6d9 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -35,6 +35,7 @@ espp::I2c &BoxEmu::external_i2c() { } bool BoxEmu::initialize_box() { + logger_.info("Initializing EspBox"); auto &box = espp::EspBox::get(); // initialize the touchpad if (!box.initialize_touch()) { @@ -71,8 +72,9 @@ bool BoxEmu::initialize_sdcard() { return false; } - esp_err_t ret; + logger_.info("Initializing SD card"); + esp_err_t ret; // Options for mounting the filesystem. If format_if_mount_failed is set to // true, SD card will be partitioned and formatted in case when mounting // fails. @@ -82,7 +84,6 @@ bool BoxEmu::initialize_sdcard() { mount_config.max_files = 5; mount_config.allocation_unit_size = 16 * 1024; const char mount_point[] = "/sdcard"; - logger_.info("Initializing SD card"); // Use settings defined above to initialize SD card and mount FAT filesystem. // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. @@ -161,6 +162,7 @@ bool BoxEmu::initialize_memory() { return false; } + logger_.info("Initializing memory (romdata)"); // allocate memory for the ROM and make sure it's on the SPIRAM romdata_ = (uint8_t*)heap_caps_malloc(4*1024*1024, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); if (romdata_ == nullptr) { @@ -204,6 +206,7 @@ extern "C" lv_indev_t *get_keypad_input_device() { } bool BoxEmu::initialize_gamepad() { + logger_.info("Initializing gamepad"); if (version_ == BoxEmu::Version::V0) { auto raw_input = new version0::InputType( std::make_shared(version0::InputDriver::Config{ @@ -310,6 +313,8 @@ bool BoxEmu::initialize_battery() { return false; } + logger_.info("Initializing battery"); + battery_comms_good_ = external_i2c_.probe_device(espp::Max1704x::DEFAULT_ADDRESS); if (!battery_comms_good_) { logger_.error("Could not communicate with battery!"); @@ -417,6 +422,7 @@ bool BoxEmu::initialize_video() { } logger_.info("initializing video task"); + video_queue_ = xQueueCreate(1, sizeof(uint16_t*)); video_task_ = std::make_shared(espp::Task::Config{ .name = "video task", @@ -519,6 +525,13 @@ bool BoxEmu::is_usb_enabled() const { } bool BoxEmu::initialize_usb() { + if (usb_enabled_) { + logger_.error("USB MSC already initialized!"); + return false; + } + + logger_.info("USB MSC initialization"); + // get the card from the filesystem initialization auto card = sdcard(); if (!card) { From aea8e11ac6d92b2147b85e6bde38937582054763 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 08:25:58 -0500 Subject: [PATCH 08/14] flesh out example and remove warnings; improve i/o on uSd card --- .../box-emu/example/main/box_emu_example.cpp | 12 +++--- components/box-emu/src/box-emu.cpp | 37 +++++++++++-------- 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/components/box-emu/example/main/box_emu_example.cpp b/components/box-emu/example/main/box_emu_example.cpp index aa072b9..687c759 100644 --- a/components/box-emu/example/main/box_emu_example.cpp +++ b/components/box-emu/example/main/box_emu_example.cpp @@ -33,8 +33,8 @@ extern "C" void app_main(void) { } if (!emu.initialize_sdcard()) { - logger.error("Failed to initialize SD card!"); - return; + logger.warn("Failed to initialize SD card!"); + logger.warn("This may happen if the SD card is not inserted."); } if (!emu.initialize_memory()) { @@ -43,13 +43,13 @@ extern "C" void app_main(void) { } if (!emu.initialize_gamepad()) { - logger.error("Failed to initialize gamepad!"); - return; + logger.warn("Failed to initialize gamepad!"); + logger.warn("This may happen if the gamepad is not connected."); } if (!emu.initialize_battery()) { - logger.error("Failed to initialize battery!"); - return; + logger.warn("Failed to initialize battery!"); + logger.warn("This may happen if the battery is not connected."); } if (!emu.initialize_video()) { diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index 838c6d9..bef6d4d 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -308,6 +308,11 @@ std::shared_ptr BoxEmu::keypad() const { ///////////////////////////////////////////////////////////////////////////// bool BoxEmu::initialize_battery() { + if (version_ == BoxEmu::Version::V0) { + logger_.warn("Battery not supported on version 0"); + return false; + } + if (battery_) { logger_.error("Battery already initialized!"); return false; @@ -544,25 +549,29 @@ bool BoxEmu::initialize_usb() { fmt::print("USB MSC initialization\n"); // register the callback for the storage mount changed event. - const tinyusb_msc_sdmmc_config_t config_sdmmc = { + tinyusb_msc_sdmmc_config_t config_sdmmc = { .card = card, - .callback_mount_changed = nullptr, // storage_mount_changed_cb, + .callback_mount_changed = nullptr, + .callback_premount_changed = nullptr, .mount_config = { + .format_if_mount_failed = false, .max_files = 5, - } + .allocation_unit_size = 16 * 1024, // sector size is 512 bytes, this should be between sector size and (128 * sector size). Larger means higher read/write performance and higher overhead for small files. + .disk_status_check_enable = false, // true if you see issues or are unmounted properly; slows down I/O + }, }; ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); // ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); // initialize the tinyusb stack fmt::print("USB MSC initialization\n"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = &descriptor_config, - .string_descriptor = string_desc_arr, - .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), - .external_phy = false, - .configuration_descriptor = desc_configuration, - }; + tinyusb_config_t tusb_cfg; + memset(&tusb_cfg, 0, sizeof(tusb_cfg)); + tusb_cfg.device_descriptor = &descriptor_config; + tusb_cfg.string_descriptor = string_desc_arr; + tusb_cfg.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]); + tusb_cfg.external_phy = false; + tusb_cfg.configuration_descriptor = desc_configuration; ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); fmt::print("USB MSC initialization DONE\n"); usb_enabled_ = true; @@ -584,11 +593,9 @@ bool BoxEmu::deinitialize_usb() { usb_enabled_ = false; // and reconnect the CDC port, see: // https://github.com/espressif/idf-extra-components/pull/229 - usb_phy_config_t phy_conf = { - // NOTE: for some reason, USB_PHY_CTRL_SERIAL_JTAG is not defined in the SDK - // for the ESP32s3 - .controller = USB_PHY_CTRL_SERIAL_JTAG, // (usb_phy_controller_t)1, - }; + usb_phy_config_t phy_conf; + memset(&phy_conf, 0, sizeof(phy_conf)); + phy_conf.controller = USB_PHY_CTRL_SERIAL_JTAG; usb_new_phy(&phy_conf, &jtag_phy_); return true; } From e2939b01a108361dd55a3432bf8da8749e5bb159 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 08:29:56 -0500 Subject: [PATCH 09/14] update example readme --- components/box-emu/example/README.md | 32 ++++++++++++++++------------ 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/components/box-emu/example/README.md b/components/box-emu/example/README.md index b7245e1..e18ad1c 100644 --- a/components/box-emu/example/README.md +++ b/components/box-emu/example/README.md @@ -1,19 +1,20 @@ -# ESP-BOX Example +# Box-Emu Example -This example shows how to use the `espp::EspBox` hardware abstraction component -to automatically detect and initialize components on both the ESP32-S3-BOX and -the ESP32-S3-BOX-3. - -It initializes the touch, display, and audio subsystems. It reads the touchpad -state and each time you touch the scren it 1) uses LVGL to draw a circle where -you touch, and 2) play a click sound (wav file bundled with the firmware). If -you press the home button on the display, it will clear the circles. +This example shows how to use the `BoxEmu` component to initialize the hardware +for the BoxEmu, automatically detecting the version of the BoxEmu hardware and +initializing the appropriate hardware (sdcard, memory, gamepad, battery, video). +It uses the `espp::EspBox` component to similarly automatically detect the +version of the EspBox it is running on and intialize its hardware (display, +touch, audio) appropriately. ## How to use example ### Hardware Required -This example is designed to run on the ESP32-S3-BOX or ESP32-S3-BOX-3. +This example is designed to run on the ESP32-S3-BOX or ESP32-S3-BOX-3 in +conjunction with a BoxEmu V0 or BoxEmu V1 gamepad. + +Note: Only BoxEmu V1 supports ESP32-S3-BOX-3 (referred to as EspBox-3 here). ### Build and Flash @@ -32,8 +33,11 @@ See the Getting Started Guide for full steps to configure and use ESP-IDF to bui ## Example Output -BOX3: -![CleanShot 2024-07-01 at 08 47 31](https://github.com/esp-cpp/espp/assets/213467/27cdec8e-6db0-4e3d-8fd6-91052ce2ad92) +EspBox-3 BoxEmu V1: +![CleanShot 2024-07-03 at 08 07 46](https://github.com/esp-cpp/esp-box-emu/assets/213467/4bfecb69-683c-4cb5-9519-d9f731dd4b0d) + +EspBox BoxEmu V1: +![CleanShot 2024-07-03 at 08 10 53](https://github.com/esp-cpp/esp-box-emu/assets/213467/0d089e01-fb73-4f08-a7c7-a6c0e5d1533a) -BOX: -![CleanShot 2024-07-01 at 09 56 23](https://github.com/esp-cpp/espp/assets/213467/2f758ff5-a82e-4620-896e-99223010f013) +EspBox BoxEmu V0: +![CleanShot 2024-07-03 at 08 23 05](https://github.com/esp-cpp/esp-box-emu/assets/213467/38f277db-9a99-41e6-b047-adc51c299f30) From 4bd503373a38c1df8c9f785b08c60533e11cf592 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 09:12:14 -0500 Subject: [PATCH 10/14] update espp for fix to missing EspBox::display() implementation --- components/espp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/espp b/components/espp index e3757ed..855975f 160000 --- a/components/espp +++ b/components/espp @@ -1 +1 @@ -Subproject commit e3757ed643aa849071db7ffb09eb53a701b09400 +Subproject commit 855975f19e835bc34dc7aea74197168712e79457 From bb54482d0cff11243fb7ca364c1c73d1dea31316 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 09:12:37 -0500 Subject: [PATCH 11/14] WIP fleshing out rewiring of menu / gui / main / cart to use BoxEmu component instead of box-emu-hal component --- CMakeLists.txt | 2 +- components/box-emu/src/box-emu.cpp | 9 ------ components/gui/CMakeLists.txt | 2 +- components/gui/include/gui.hpp | 15 ++++----- components/gui/src/gui.cpp | 35 ++++++++++----------- components/menu/CMakeLists.txt | 2 +- components/menu/include/menu.hpp | 16 +++++----- components/menu/src/menu.cpp | 20 ++++++------ main/cart.hpp | 18 +++++------ main/gbc_cart.hpp | 6 ++-- main/genesis_cart.hpp | 6 ++-- main/main.cpp | 49 ++++++++++++++++++++++++++---- main/nes_cart.hpp | 6 ++-- main/sms_cart.hpp | 6 ++-- 14 files changed, 111 insertions(+), 81 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1699558..ceb349b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -80,7 +80,7 @@ set(EMULATOR_COMPONENTS set( COMPONENTS - "main esptool_py esp_lcd esp_psram task format display display_drivers monitor timer ${EMULATOR_COMPONENTS} box-emu-hal gui menu" + "main esptool_py esp_lcd esp_psram task format display display_drivers monitor timer ${EMULATOR_COMPONENTS} box-emu gui menu" CACHE STRING "List of components to include" ) diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index bef6d4d..6eea141 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -196,15 +196,6 @@ uint8_t *BoxEmu::romdata() const { // Gamepad ///////////////////////////////////////////////////////////////////////////// -extern "C" lv_indev_t *get_keypad_input_device() { - auto keypad = BoxEmu::get().keypad(); - if (!keypad) { - fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); - return nullptr; - } - return keypad->get_input_device(); -} - bool BoxEmu::initialize_gamepad() { logger_.info("Initializing gamepad"); if (version_ == BoxEmu::Version::V0) { diff --git a/components/gui/CMakeLists.txt b/components/gui/CMakeLists.txt index d139072..b309fd7 100644 --- a/components/gui/CMakeLists.txt +++ b/components/gui/CMakeLists.txt @@ -2,4 +2,4 @@ idf_component_register( SRC_DIRS "src" "generated" "generated/screens" "generated/components" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "generated" - REQUIRES lvgl task display logger jpeg rom_info box-emu-hal) + REQUIRES lvgl task display logger jpeg rom_info box-emu) diff --git a/components/gui/include/gui.hpp b/components/gui/include/gui.hpp index a46f2a8..f68a9e5 100644 --- a/components/gui/include/gui.hpp +++ b/components/gui/include/gui.hpp @@ -11,7 +11,7 @@ #include "task.hpp" #include "logger.hpp" -#include "box_emu_hal.hpp" +#include "box-emu.hpp" #include "rom_info.hpp" class Gui { @@ -80,7 +80,7 @@ class Gui { void set_mute(bool muted); void toggle_mute() { - set_mute(!hal::is_muted()); + set_mute(!espp::EspBox::get().is_muted()); } void set_audio_level(int new_audio_level); @@ -147,10 +147,11 @@ class Gui { void toggle_usb(); void update_shared_state() { - set_mute(hal::is_muted()); - set_audio_level(hal::get_audio_volume()); - set_brightness(hal::get_display_brightness() * 100.0f); - set_video_setting(hal::get_video_setting()); + auto &box = espp::EspBox::get(); + set_mute(box.is_muted()); + set_audio_level(box.volume()); + set_brightness(box.brightness()); + set_video_setting(BoxEmu::get().video_setting()); } VideoSetting get_video_setting(); @@ -158,7 +159,7 @@ class Gui { void on_rom_focused(lv_obj_t *new_focus); void on_mute_button_pressed(const std::vector& data) { - set_mute(hal::is_muted()); + set_mute(espp::EspBox::get().is_muted()); } void on_battery(const std::vector& data); diff --git a/components/gui/src/gui.cpp b/components/gui/src/gui.cpp index dc2ac1a..5c7cf6f 100644 --- a/components/gui/src/gui.cpp +++ b/components/gui/src/gui.cpp @@ -7,7 +7,7 @@ extern "C" { } void Gui::set_mute(bool muted) { - hal::set_muted(muted); + espp::EspBox::get().mute(muted); if (muted) { lv_obj_add_state(ui_mutebutton, LV_STATE_CHECKED); } else { @@ -18,17 +18,17 @@ void Gui::set_mute(bool muted) { void Gui::set_audio_level(int new_audio_level) { new_audio_level = std::clamp(new_audio_level, 0, 100); lv_bar_set_value(ui_volumebar, new_audio_level, LV_ANIM_ON); - hal::set_audio_volume(new_audio_level); + espp::EspBox::get().volume(new_audio_level); } void Gui::set_brightness(int new_brightness) { new_brightness = std::clamp(new_brightness, 10, 100); lv_bar_set_value(ui_brightnessbar, new_brightness, LV_ANIM_ON); - hal::set_display_brightness((float)new_brightness / 100.0f); + espp::EspBox::get().brightness((float)new_brightness); } void Gui::set_video_setting(VideoSetting setting) { - hal::set_video_setting(setting); + BoxEmu::get().video_setting(setting); lv_dropdown_set_selected(ui_videosettingdropdown, (int)setting); } @@ -169,7 +169,7 @@ void Gui::init_ui() { settings_screen_group_ = lv_group_create(); // get the KEYPAD indev - auto keypad = get_keypad_input_device(); + auto keypad = BoxEmu::get().keypad()->get_input_device(); if (keypad) lv_indev_set_group(keypad, rom_screen_group_); @@ -188,7 +188,7 @@ void Gui::init_ui() { lv_obj_set_flex_flow(ui_rompanel, LV_FLEX_FLOW_COLUMN); lv_obj_set_scroll_snap_y(ui_rompanel, LV_SCROLL_SNAP_CENTER); - lv_bar_set_value(ui_volumebar, hal::get_audio_volume(), LV_ANIM_OFF); + lv_bar_set_value(ui_volumebar, espp::EspBox::get().volume(), LV_ANIM_OFF); // rom screen navigation lv_obj_add_event_cb(ui_settingsbutton, &Gui::event_callback, LV_EVENT_PRESSED, static_cast(this)); @@ -306,12 +306,12 @@ void Gui::on_pressed(lv_event_t *e) { // volume controls bool is_volume_up_button = (target == ui_volumeupbutton); if (is_volume_up_button) { - set_audio_level(hal::get_audio_volume() + 10); + set_audio_level(espp::EspBox::get().volume() + 10); return; } bool is_volume_down_button = (target == ui_volumedownbutton); if (is_volume_down_button) { - set_audio_level(hal::get_audio_volume() - 10); + set_audio_level(espp::EspBox::get().volume() - 10); return; } bool is_mute_button = (target == ui_mutebutton); @@ -322,13 +322,13 @@ void Gui::on_pressed(lv_event_t *e) { // brightness controlsn bool is_brightness_up_button = (target == ui_brightnessupbutton); if (is_brightness_up_button) { - int brightness = hal::get_display_brightness() * 100.0f; + int brightness = espp::EspBox::get().brightness(); set_brightness(brightness + 10); return; } bool is_brightness_down_button = (target == ui_brightnessdownbutton); if (is_brightness_down_button) { - int brightness = hal::get_display_brightness() * 100.0f; + int brightness = espp::EspBox::get().brightness(); set_brightness(brightness - 10); return; } @@ -375,7 +375,7 @@ void Gui::on_pressed(lv_event_t *e) { void Gui::on_volume(const std::vector& data) { // the volume was changed, update our display of the volume - lv_bar_set_value(ui_volumebar, hal::get_audio_volume(), LV_ANIM_ON); + lv_bar_set_value(ui_volumebar, espp::EspBox::get().volume(), LV_ANIM_ON); } void Gui::on_battery(const std::vector& data) { @@ -417,14 +417,15 @@ void Gui::on_battery(const std::vector& data) { void Gui::toggle_usb() { fmt::print("Toggling USB\n"); + auto &emu = BoxEmu::get(); // toggle the usb - if (usb_is_enabled()) { - usb_deinit(); + if (emu.is_usb_enabled()) { + emu.deinitialize_usb(); } else { - usb_init(); + emu.initialize_usb(); } // update the label - if (usb_is_enabled()) { + if (emu.is_usb_enabled()) { lv_label_set_text(ui_usb_label, "Enabled"); } else { lv_label_set_text(ui_usb_label, "Disabled"); @@ -445,7 +446,7 @@ void Gui::focus_rommenu() { // focus the rom screen group logger_.debug("Focusing rom screen group"); lv_group_focus_freeze(rom_screen_group_, false); - auto keypad = get_keypad_input_device(); + auto keypad = BoxEmu::get().keypad()->get_input_device(); if (keypad) lv_indev_set_group(keypad, rom_screen_group_); } @@ -456,7 +457,7 @@ void Gui::focus_settings() { logger_.debug("Focusing settings screen group"); lv_group_focus_freeze(settings_screen_group_, false); // NOTE: we don't set editing here since we use it to manage the dropdown - auto keypad = get_keypad_input_device(); + auto keypad = BoxEmu::get().keypad()->get_input_device(); if (keypad) lv_indev_set_group(keypad, settings_screen_group_); } diff --git a/components/menu/CMakeLists.txt b/components/menu/CMakeLists.txt index 0641fa7..49418c5 100644 --- a/components/menu/CMakeLists.txt +++ b/components/menu/CMakeLists.txt @@ -2,4 +2,4 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" "generated" "generated/screens" "generated/components" PRIV_INCLUDE_DIRS "generated" - REQUIRES lvgl timer display logger jpeg box-emu-hal) + REQUIRES lvgl timer display logger jpeg box-emu statistics) diff --git a/components/menu/include/menu.hpp b/components/menu/include/menu.hpp index 79cb934..f54a462 100644 --- a/components/menu/include/menu.hpp +++ b/components/menu/include/menu.hpp @@ -9,7 +9,8 @@ #include "high_resolution_timer.hpp" #include "logger.hpp" -#include "box_emu_hal.hpp" +#include "box-emu.hpp" +#include "statistics.hpp" class Menu { public: @@ -90,7 +91,7 @@ class Menu { void set_mute(bool muted); void toggle_mute() { - set_mute(!hal::is_muted()); + set_mute(!espp::EspBox::get().is_muted()); } void set_audio_level(int new_audio_level); @@ -123,16 +124,17 @@ class Menu { void update_fps_label(float fps); void update_shared_state() { - set_mute(hal::is_muted()); - set_audio_level(hal::get_audio_volume()); - set_brightness(hal::get_display_brightness() * 100.0f); - set_video_setting(hal::get_video_setting()); + auto &box = espp::EspBox::get(); + set_mute(box.is_muted()); + set_audio_level(box.volume()); + set_brightness(box.brightness()); + set_video_setting(BoxEmu::get().video_setting()); } VideoSetting get_video_setting(); void on_mute_button_pressed(const std::vector& data) { - set_mute(hal::is_muted()); + set_mute(espp::EspBox::get().is_muted()); } void update() { diff --git a/components/menu/src/menu.cpp b/components/menu/src/menu.cpp index 518897c..44d42e3 100644 --- a/components/menu/src/menu.cpp +++ b/components/menu/src/menu.cpp @@ -15,7 +15,7 @@ void Menu::init_ui() { lv_group_set_default(group_); // get the KEYPAD indev - auto keypad = get_keypad_input_device(); + auto keypad = BoxEmu::get().keypad()->get_input_device(); if (keypad) lv_indev_set_group(keypad, group_); @@ -218,7 +218,7 @@ void Menu::update_fps_label(float fps) { } void Menu::set_mute(bool muted) { - hal::set_muted(muted); + espp::EspBox::get().mute(muted); if (muted) { lv_obj_add_state(ui_volume_mute_btn, LV_STATE_CHECKED); } else { @@ -229,17 +229,17 @@ void Menu::set_mute(bool muted) { void Menu::set_audio_level(int new_audio_level) { new_audio_level = std::clamp(new_audio_level, 0, 100); lv_bar_set_value(ui_Bar2, new_audio_level, LV_ANIM_ON); - hal::set_audio_volume(new_audio_level); + espp::EspBox::get().volume(new_audio_level); } void Menu::set_brightness(int new_brightness) { new_brightness = std::clamp(new_brightness, 10, 100); lv_bar_set_value(ui_brightness_bar, new_brightness, LV_ANIM_ON); - hal::set_display_brightness((float)new_brightness / 100.0f); + espp::EspBox::get().brightness((float)new_brightness); } void Menu::set_video_setting(VideoSetting setting) { - hal::set_video_setting(setting); + BoxEmu::get().video_setting(setting); lv_dropdown_set_selected(ui_Dropdown2, (int)setting); } @@ -307,12 +307,12 @@ void Menu::on_pressed(lv_event_t *e) { // volume controls bool is_volume_up_button = (target == ui_volume_inc_btn); if (is_volume_up_button) { - set_audio_level(hal::get_audio_volume() + 10); + set_audio_level(espp::EspBox::get().volume() + 10); return; } bool is_volume_down_button = (target == ui_volume_dec_btn); if (is_volume_down_button) { - set_audio_level(hal::get_audio_volume() - 10); + set_audio_level(espp::EspBox::get().volume() - 10); return; } bool is_mute_button = (target == ui_volume_mute_btn); @@ -323,19 +323,19 @@ void Menu::on_pressed(lv_event_t *e) { // brightness controls bool is_brightness_up_button = (target == ui_brightness_inc_btn); if (is_brightness_up_button) { - set_brightness(hal::get_display_brightness() * 100.0f + 10); + set_brightness(espp::EspBox::get().brightness() + 10); return; } bool is_brightness_down_button = (target == ui_brightness_dec_btn); if (is_brightness_down_button) { - set_brightness(hal::get_display_brightness() * 100.0f - 10); + set_brightness(espp::EspBox::get().brightness() - 10); return; } } void Menu::on_volume(const std::vector& data) { // the volume was changed, update our display of the volume - lv_bar_set_value(ui_Bar2, hal::get_audio_volume(), LV_ANIM_ON); + lv_bar_set_value(ui_Bar2, espp::EspBox::get().volume(), LV_ANIM_ON); } void Menu::on_battery(const std::vector& data) { diff --git a/main/cart.hpp b/main/cart.hpp index b939729..f4c1aa1 100644 --- a/main/cart.hpp +++ b/main/cart.hpp @@ -7,7 +7,7 @@ #include "display.hpp" #include "logger.hpp" -#include "box_emu_hal.hpp" +#include "box-emu.hpp" #include "rom_info.hpp" #include "menu.hpp" @@ -39,8 +39,8 @@ class Cart { // clear the screen espp::St7789::clear(0,0,320,240); // copy the romdata - rom_size_bytes_ = copy_romdata_to_cart_partition(get_rom_filename()); - romdata_ = get_mmapped_romdata(); + rom_size_bytes_ = BoxEmu::get().copy_file_to_romdata(get_rom_filename()); + romdata_ = BoxEmu::get().romdata(); // create the menu menu_ = std::make_unique(Menu::Config{ .display = display_, @@ -156,16 +156,14 @@ class Cart { virtual bool run() { running_ = true; // handle touchpad so we can know if the user presses the menu - uint8_t _num_touches = 0, _btn_state = false; - uint16_t _x = 0 ,_y = 0; - touchpad_read(&_num_touches, &_x, &_y, &_btn_state); + auto touch = espp::EspBox::get().touchpad_data(); + bool btn_state = touch.btn_state; // also get the gamepad input state so we can know if the user presses the // start/select buttons together to bring up the menu - InputState state; - hal::get_input_state(&state); + auto state = BoxEmu::get().gamepad_state(); // if the user presses the menu button or the start/select buttons, then // pause the game and show the menu - bool show_menu = _btn_state || (state.start && state.select); + bool show_menu = btn_state || (state.start && state.select); if (show_menu) { logger_.info("Menu pressed!"); pre_menu(); @@ -258,7 +256,7 @@ class Cart { virtual void handle_video_setting() { logger_.info("Base handling video setting..."); - switch (hal::get_video_setting()) { + switch (BoxEmu::get().video_setting()) { case VideoSetting::ORIGINAL: set_original_video_setting(); break; diff --git a/main/gbc_cart.hpp b/main/gbc_cart.hpp index 7458dbe..3630b5a 100644 --- a/main/gbc_cart.hpp +++ b/main/gbc_cart.hpp @@ -86,7 +86,7 @@ class GbcCart : public Cart { virtual void set_original_video_setting() override { #if defined(ENABLE_GBC) logger_.info("gbc::video: original"); - hal::set_display_size(GAMEBOY_WIDTH, GAMEBOY_HEIGHT); + BoxEmu::get().display_size(GAMEBOY_WIDTH, GAMEBOY_HEIGHT); #endif } @@ -108,14 +108,14 @@ class GbcCart : public Cart { logger_.info("gbc::video: fit"); float x_scale = static_cast(SCREEN_HEIGHT) / static_cast(GAMEBOY_HEIGHT); int new_width = static_cast(static_cast(GAMEBOY_WIDTH) * x_scale); - hal::set_display_size(new_width, SCREEN_HEIGHT); + BoxEmu::get().display_size(new_width, SCREEN_HEIGHT); #endif } virtual void set_fill_video_setting() override { #if defined(ENABLE_GBC) logger_.info("gbc::video: fill"); - hal::set_display_size(SCREEN_WIDTH, SCREEN_HEIGHT); + BoxEmu::get().display_size(SCREEN_WIDTH, SCREEN_HEIGHT); #endif } diff --git a/main/genesis_cart.hpp b/main/genesis_cart.hpp index e50f4aa..cded9a4 100644 --- a/main/genesis_cart.hpp +++ b/main/genesis_cart.hpp @@ -97,7 +97,7 @@ class GenesisCart : public Cart { #if defined(ENABLE_GENESIS) auto height = GENESIS_HEIGHT; auto width = GENESIS_WIDTH; - hal::set_display_size(width, height); + BoxEmu::get().display_size(width, height); #endif } @@ -120,14 +120,14 @@ class GenesisCart : public Cart { #if defined(ENABLE_GENESIS) logger_.info("genesis::video: fit"); // the genesis is already 320 px wide, don't do anything - hal::set_display_size(GENESIS_WIDTH, GENESIS_HEIGHT); + BoxEmu::get().display_size(GENESIS_WIDTH, GENESIS_HEIGHT); #endif } virtual void set_fill_video_setting() override { #if defined(ENABLE_GENESIS) logger_.info("genesis::video: fill"); - hal::set_display_size(SCREEN_WIDTH, SCREEN_HEIGHT); + BoxEmu::get().display_size(SCREEN_WIDTH, SCREEN_HEIGHT); #endif } diff --git a/main/main.cpp b/main/main.cpp index a7da375..e7117fd 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -11,11 +11,12 @@ #include "task_monitor.hpp" #include "timer.hpp" -#include "box_emu_hal.hpp" +#include "box-emu.hpp" #include "carts.hpp" #include "gui.hpp" #include "heap_utils.hpp" #include "rom_info.hpp" +#include "statistics.hpp" using namespace std::chrono_literals; @@ -23,15 +24,51 @@ extern "C" void app_main(void) { espp::Logger logger({.tag = "esp-box-emu", .level = espp::Logger::Verbosity::DEBUG}); logger.info("Bootup"); - hal::init(); + // initialize the hardware abstraction layer + BoxEmu &emu = BoxEmu::get(); + emu.set_log_level(espp::Logger::Verbosity::INFO); + espp::EspBox &box = espp::EspBox::get(); + logger.info("Running on {}", box.box_type()); + logger.info("Box Emu version: {}", emu.version()); + + // initialize + if (!emu.initialize_box()) { + logger.error("Failed to initialize box!"); + return; + } + + if (!emu.initialize_sdcard()) { + logger.warn("Failed to initialize SD card!"); + logger.warn("This may happen if the SD card is not inserted."); + } + + if (!emu.initialize_memory()) { + logger.error("Failed to initialize memory!"); + return; + } + + if (!emu.initialize_gamepad()) { + logger.warn("Failed to initialize gamepad!"); + logger.warn("This may happen if the gamepad is not connected."); + } + + if (!emu.initialize_battery()) { + logger.warn("Failed to initialize battery!"); + logger.warn("This may happen if the battery is not connected."); + } + + if (!emu.initialize_video()) { + logger.error("Failed to initialize video!"); + return; + } std::error_code ec; - auto external_i2c = hal::get_external_i2c(); + auto &external_i2c = emu.external_i2c(); espp::Drv2605 haptic_motor(espp::Drv2605::Config{ .device_address = espp::Drv2605::DEFAULT_ADDRESS, - .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read_register = std::bind(&espp::I2c::read_at_register, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), + .write = std::bind(&espp::I2c::write, &external_i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), + .read_register = std::bind(&espp::I2c::read_at_register, &external_i2c, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), .motor_type = espp::Drv2605::MotorType::LRA }); // we're using an LRA motor, so select th LRA library. @@ -49,7 +86,7 @@ extern "C" void app_main(void) { logger.info("initializing gui..."); - auto display = hal::get_display(); + auto display = box.display(); // initialize the gui Gui gui({ diff --git a/main/nes_cart.hpp b/main/nes_cart.hpp index 51f294f..299ff93 100644 --- a/main/nes_cart.hpp +++ b/main/nes_cart.hpp @@ -97,7 +97,7 @@ class NesCart : public Cart { virtual void set_original_video_setting() override { #if defined(ENABLE_NES) - hal::set_display_size(NES_WIDTH, NES_HEIGHT); + BoxEmu::get().display_size(NES_WIDTH, NES_HEIGHT); #endif } @@ -105,13 +105,13 @@ class NesCart : public Cart { #if defined(ENABLE_NES) float x_scale = static_cast(SCREEN_HEIGHT) / static_cast(NES_HEIGHT); int new_width = static_cast(static_cast(NES_WIDTH) * x_scale); - hal::set_display_size(new_width, SCREEN_HEIGHT); + BoxEmu::get().display_size(new_width, SCREEN_HEIGHT); #endif } virtual void set_fill_video_setting() override { #if defined(ENABLE_NES) - hal::set_display_size(SCREEN_WIDTH, SCREEN_HEIGHT); + BoxEmu::get().display_size(SCREEN_WIDTH, SCREEN_HEIGHT); #endif } diff --git a/main/sms_cart.hpp b/main/sms_cart.hpp index 8747f5d..49ea7d7 100644 --- a/main/sms_cart.hpp +++ b/main/sms_cart.hpp @@ -102,7 +102,7 @@ class SmsCart : public Cart { #if defined(ENABLE_SMS) auto height = info_.platform == Emulator::SEGA_MASTER_SYSTEM ? SMS_HEIGHT : GG_HEIGHT; auto width = info_.platform == Emulator::SEGA_MASTER_SYSTEM ? SMS_WIDTH : GG_WIDTH; - hal::set_display_size(width, height); + BoxEmu::get().display_size(width, height); #endif } @@ -128,14 +128,14 @@ class SmsCart : public Cart { float width = info_.platform == Emulator::SEGA_MASTER_SYSTEM ? SMS_WIDTH : GG_WIDTH; float x_scale = static_cast(SCREEN_HEIGHT) / height; int new_width = static_cast(width * x_scale); - hal::set_display_size(new_width, SCREEN_HEIGHT); + BoxEmu::get().display_size(new_width, SCREEN_HEIGHT); #endif } virtual void set_fill_video_setting() override { #if defined(ENABLE_SMS) logger_.info("sms::video: fill"); - hal::set_display_size(SCREEN_WIDTH, SCREEN_HEIGHT); + BoxEmu::get().display_size(SCREEN_WIDTH, SCREEN_HEIGHT); #endif } From 7dcce7736125f3e9337b9502e9f0da3f08e8b877 Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 10:25:53 -0500 Subject: [PATCH 12/14] refactored code to fully remove codec and box-emu-hal components to use new box-emu component --- components/box-emu-hal/CMakeLists.txt | 5 - components/box-emu-hal/Kconfig.projbuild | 14 - components/box-emu-hal/idf_component.yml | 4 - .../box-emu-hal/include/battery_info.hpp | 20 - components/box-emu-hal/include/box.hpp | 69 -- components/box-emu-hal/include/box_3.hpp | 68 -- .../box-emu-hal/include/box_emu_hal.hpp | 114 --- components/box-emu-hal/include/emu_v0.hpp | 29 - components/box-emu-hal/include/emu_v1.hpp | 37 - components/box-emu-hal/include/fs_init.hpp | 18 - components/box-emu-hal/include/hal.hpp | 11 - components/box-emu-hal/include/hal_events.hpp | 7 - .../box-emu-hal/include/input_state.hpp | 28 - components/box-emu-hal/include/lvgl_inputs.h | 16 - components/box-emu-hal/include/make_color.h | 15 - components/box-emu-hal/include/mmap.hpp | 14 - components/box-emu-hal/include/statistics.hpp | 17 - components/box-emu-hal/include/usb.hpp | 16 - .../box-emu-hal/include/video_setting.hpp | 3 - components/box-emu-hal/src/battery.cpp | 113 --- components/box-emu-hal/src/box_emu_hal.cpp | 20 - components/box-emu-hal/src/fs_init.cpp | 79 -- components/box-emu-hal/src/hal_i2c.cpp | 40 - components/box-emu-hal/src/i2s_audio.cpp | 267 ------- components/box-emu-hal/src/input.cpp | 260 ------- components/box-emu-hal/src/mmap.cpp | 35 - components/box-emu-hal/src/spi_lcd.cpp | 261 ------- components/box-emu-hal/src/statistics.cpp | 59 -- components/box-emu-hal/src/usb.cpp | 119 --- components/box-emu-hal/src/video_setting.cpp | 11 - components/box-emu-hal/src/video_task.cpp | 183 ----- components/box-emu/include/box-emu.hpp | 6 + components/box-emu/src/box-emu.cpp | 1 - components/box-emu/src/make_color.cpp | 7 + components/codec/CMakeLists.txt | 13 - components/codec/es7210/es7210.cpp | 537 -------------- components/codec/es7210/es7210.hpp | 262 ------- components/codec/es8311/es8311.cpp | 682 ------------------ components/codec/es8311/es8311.hpp | 281 -------- components/codec/es8388/es8388.c | 508 ------------- components/codec/es8388/es8388.h | 303 -------- components/codec/include/audio_hal.h | 138 ---- components/codec/include/esxxx_common.h | 190 ----- components/gbc/CMakeLists.txt | 2 +- components/gbc/src/gameboy.cpp | 24 +- components/genesis/CMakeLists.txt | 2 +- components/genesis/src/genesis.cpp | 33 +- components/gui/src/gui.cpp | 2 +- components/nes/CMakeLists.txt | 2 +- components/nes/src/nes.cpp | 16 +- components/nes/src/video_audio.cpp | 25 +- components/rom_info/CMakeLists.txt | 2 +- components/rom_info/include/rom_info.hpp | 2 +- components/rom_info/src/rom_info.cpp | 2 +- components/sms/CMakeLists.txt | 2 +- components/sms/src/sms.cpp | 26 +- main/cart.hpp | 2 +- 57 files changed, 87 insertions(+), 4935 deletions(-) delete mode 100644 components/box-emu-hal/CMakeLists.txt delete mode 100644 components/box-emu-hal/Kconfig.projbuild delete mode 100644 components/box-emu-hal/idf_component.yml delete mode 100644 components/box-emu-hal/include/battery_info.hpp delete mode 100644 components/box-emu-hal/include/box.hpp delete mode 100644 components/box-emu-hal/include/box_3.hpp delete mode 100644 components/box-emu-hal/include/box_emu_hal.hpp delete mode 100644 components/box-emu-hal/include/emu_v0.hpp delete mode 100644 components/box-emu-hal/include/emu_v1.hpp delete mode 100644 components/box-emu-hal/include/fs_init.hpp delete mode 100644 components/box-emu-hal/include/hal.hpp delete mode 100644 components/box-emu-hal/include/hal_events.hpp delete mode 100644 components/box-emu-hal/include/input_state.hpp delete mode 100644 components/box-emu-hal/include/lvgl_inputs.h delete mode 100644 components/box-emu-hal/include/make_color.h delete mode 100644 components/box-emu-hal/include/mmap.hpp delete mode 100644 components/box-emu-hal/include/statistics.hpp delete mode 100644 components/box-emu-hal/include/usb.hpp delete mode 100644 components/box-emu-hal/include/video_setting.hpp delete mode 100644 components/box-emu-hal/src/battery.cpp delete mode 100644 components/box-emu-hal/src/box_emu_hal.cpp delete mode 100644 components/box-emu-hal/src/fs_init.cpp delete mode 100644 components/box-emu-hal/src/hal_i2c.cpp delete mode 100644 components/box-emu-hal/src/i2s_audio.cpp delete mode 100644 components/box-emu-hal/src/input.cpp delete mode 100644 components/box-emu-hal/src/mmap.cpp delete mode 100644 components/box-emu-hal/src/spi_lcd.cpp delete mode 100644 components/box-emu-hal/src/statistics.cpp delete mode 100644 components/box-emu-hal/src/usb.cpp delete mode 100644 components/box-emu-hal/src/video_setting.cpp delete mode 100644 components/box-emu-hal/src/video_task.cpp create mode 100644 components/box-emu/src/make_color.cpp delete mode 100644 components/codec/CMakeLists.txt delete mode 100644 components/codec/es7210/es7210.cpp delete mode 100644 components/codec/es7210/es7210.hpp delete mode 100644 components/codec/es8311/es8311.cpp delete mode 100644 components/codec/es8311/es8311.hpp delete mode 100755 components/codec/es8388/es8388.c delete mode 100755 components/codec/es8388/es8388.h delete mode 100755 components/codec/include/audio_hal.h delete mode 100644 components/codec/include/esxxx_common.h diff --git a/components/box-emu-hal/CMakeLists.txt b/components/box-emu-hal/CMakeLists.txt deleted file mode 100644 index 10ce0b3..0000000 --- a/components/box-emu-hal/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -idf_component_register( - INCLUDE_DIRS "include" - SRC_DIRS "src" - REQUIRES "driver" "heap" "fatfs" "esp_lcd" "esp_psram" "hal" "usb" "esp_tinyusb" "spi_flash" "nvs_flash" "codec" "adc" "aw9523" "button" "display" "display_drivers" "mcp23x17" "input_drivers" "tt21100" "gt911" "drv2605" "event_manager" "i2c" "task" "timer" "serialization" "max1704x" - ) diff --git a/components/box-emu-hal/Kconfig.projbuild b/components/box-emu-hal/Kconfig.projbuild deleted file mode 100644 index ee31925..0000000 --- a/components/box-emu-hal/Kconfig.projbuild +++ /dev/null @@ -1,14 +0,0 @@ -menu "BOX Emulator Configuration" - - choice - prompt "Module Configuration" - default HARDWARE_BOX - help - Select which display + SoC module you're using. - config HARDWARE_BOX - bool "ESP32-S3-BOX" - config HARDWARE_BOX_3 - bool "ESP32-S3-BOX-3" - endchoice - -endmenu diff --git a/components/box-emu-hal/idf_component.yml b/components/box-emu-hal/idf_component.yml deleted file mode 100644 index 47e8a9f..0000000 --- a/components/box-emu-hal/idf_component.yml +++ /dev/null @@ -1,4 +0,0 @@ -## IDF Component Manager Manifest File -dependencies: - espressif/esp_tinyusb: "^1.4.2" - idf: "^5.1" diff --git a/components/box-emu-hal/include/battery_info.hpp b/components/box-emu-hal/include/battery_info.hpp deleted file mode 100644 index 6854d0e..0000000 --- a/components/box-emu-hal/include/battery_info.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "format.hpp" - -struct BatteryInfo { - float voltage; - float level; - float charge_rate; -}; - -// for printing battery info using libfmt -template <> -struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); } - - template - auto format(const BatteryInfo& info, FormatContext& ctx) { - return fmt::format_to(ctx.out(), "BatteryInfo {{ voltage: {:.1f}, level: {:.1f}, charge_rate: {:.1f} }}", info.voltage, info.level, info.charge_rate); - } -}; diff --git a/components/box-emu-hal/include/box.hpp b/components/box-emu-hal/include/box.hpp deleted file mode 100644 index 2950e73..0000000 --- a/components/box-emu-hal/include/box.hpp +++ /dev/null @@ -1,69 +0,0 @@ -#pragma once - -#include "hal/spi_types.h" -#include "driver/gpio.h" -#include "driver/i2s_std.h" -#include "driver/spi_master.h" - -#include "i2c.hpp" -#include "st7789.hpp" -#include "touchpad_input.hpp" -#include "tt21100.hpp" - -static constexpr std::string_view dev_kit = "ESP32-S3-BOX"; - -// internal i2c (touchscreen, audio codec) -static constexpr auto internal_i2c_port = I2C_NUM_0; -static constexpr auto internal_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; -static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; - -// external I2c (peripherals) -static constexpr auto external_i2c_port = I2C_NUM_1; -static constexpr auto external_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; -static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; - -// LCD -static constexpr int lcd_clock_speed = 60 * 1000 * 1000; -static constexpr auto lcd_spi_num = SPI2_HOST; -static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; -static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; -static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; -static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; -static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; -static constexpr gpio_num_t backlight_io = GPIO_NUM_45; -static constexpr size_t lcd_width = 320; -static constexpr size_t lcd_height = 240; -static constexpr bool backlight_value = true; -static constexpr bool reset_value = false; -static constexpr bool invert_colors = true; -static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; -static constexpr bool mirror_x = true; -static constexpr bool mirror_y = true; -using DisplayDriver = espp::St7789; - -// touch -static constexpr bool touch_swap_xy = false; -static constexpr bool touch_invert_x = true; -static constexpr bool touch_invert_y = false; -static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; -using TouchDriver = espp::Tt21100; - -// sound -static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; -static constexpr auto i2s_port = I2S_NUM_0; -static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; -static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; -static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_47; -static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; -static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; -static constexpr gpio_num_t mute_pin = GPIO_NUM_1; - -// uSD card -static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; -static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; -static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; -static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; -static constexpr auto sdcard_spi_num = SPI3_HOST; - diff --git a/components/box-emu-hal/include/box_3.hpp b/components/box-emu-hal/include/box_3.hpp deleted file mode 100644 index 391af74..0000000 --- a/components/box-emu-hal/include/box_3.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#pragma once - -#include "hal/spi_types.h" -#include "driver/gpio.h" -#include "driver/i2s_std.h" -#include "driver/spi_master.h" - -#include "i2c.hpp" -#include "st7789.hpp" -#include "touchpad_input.hpp" -#include "gt911.hpp" - -static constexpr std::string_view dev_kit = "ESP32-S3-BOX-3"; - -// internal i2c (touchscreen, audio codec) -static constexpr auto internal_i2c_port = I2C_NUM_0; -static constexpr auto internal_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t internal_i2c_sda = GPIO_NUM_8; -static constexpr gpio_num_t internal_i2c_scl = GPIO_NUM_18; - -// external I2c (peripherals) -static constexpr auto external_i2c_port = I2C_NUM_1; -static constexpr auto external_i2c_clock_speed = 400 * 1000; -static constexpr gpio_num_t external_i2c_sda = GPIO_NUM_41; -static constexpr gpio_num_t external_i2c_scl = GPIO_NUM_40; - -// LCD -static constexpr int lcd_clock_speed = 60 * 1000 * 1000; -static constexpr auto lcd_spi_num = SPI2_HOST; -static constexpr gpio_num_t lcd_cs_io = GPIO_NUM_5; -static constexpr gpio_num_t lcd_mosi_io = GPIO_NUM_6; -static constexpr gpio_num_t lcd_sclk_io = GPIO_NUM_7; -static constexpr gpio_num_t lcd_reset_io = GPIO_NUM_48; -static constexpr gpio_num_t lcd_dc_io = GPIO_NUM_4; -static constexpr gpio_num_t backlight_io = GPIO_NUM_47; // was 45 on ESP32-S3-BOX -static constexpr size_t lcd_width = 320; -static constexpr size_t lcd_height = 240; -static constexpr bool backlight_value = true; -static constexpr bool reset_value = true; // was false on ESP32-S3-BOX -static constexpr bool invert_colors = true; -static constexpr auto rotation = espp::Display::Rotation::LANDSCAPE; -static constexpr bool mirror_x = true; -static constexpr bool mirror_y = true; -using DisplayDriver = espp::St7789; - -// touch -static constexpr bool touch_swap_xy = false; -static constexpr bool touch_invert_x = false; -static constexpr bool touch_invert_y = false; -static constexpr gpio_num_t touch_interrupt = GPIO_NUM_3; -using TouchDriver = espp::Gt911; - -// sound -static constexpr gpio_num_t sound_power_pin = GPIO_NUM_46; -static constexpr auto i2s_port = I2S_NUM_0; -static constexpr gpio_num_t i2s_mck_io = GPIO_NUM_2; -static constexpr gpio_num_t i2s_bck_io = GPIO_NUM_17; -static constexpr gpio_num_t i2s_ws_io = GPIO_NUM_45; // was 47 on ESP32-S3-BOX -static constexpr gpio_num_t i2s_do_io = GPIO_NUM_15; -static constexpr gpio_num_t i2s_di_io = GPIO_NUM_16; -static constexpr gpio_num_t mute_pin = GPIO_NUM_1; - -// uSD card -static constexpr gpio_num_t sdcard_cs = GPIO_NUM_10; -static constexpr gpio_num_t sdcard_mosi = GPIO_NUM_11; -static constexpr gpio_num_t sdcard_miso = GPIO_NUM_13; -static constexpr gpio_num_t sdcard_sclk = GPIO_NUM_12; -static constexpr auto sdcard_spi_num = SPI3_HOST; diff --git a/components/box-emu-hal/include/box_emu_hal.hpp b/components/box-emu-hal/include/box_emu_hal.hpp deleted file mode 100644 index c055438..0000000 --- a/components/box-emu-hal/include/box_emu_hal.hpp +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "aw9523.hpp" -#include "button.hpp" -#include "event_manager.hpp" -#include "display.hpp" -#include "i2c.hpp" -#include "keypad_input.hpp" -#include "logger.hpp" -#include "max1704x.hpp" -#include "mcp23x17.hpp" -#include "oneshot_adc.hpp" -#include "serialization.hpp" -#include "st7789.hpp" -#include "task.hpp" -#include "timer.hpp" -#include "high_resolution_timer.hpp" -#include "touchpad_input.hpp" - -#if CONFIG_HARDWARE_BOX -#include "box.hpp" -#elif CONFIG_HARDWARE_BOX_3 -#include "box_3.hpp" -#else -#error "Invalid module selection" -#endif - -#include "emu_v0.hpp" -#include "emu_v1.hpp" - -#include "es7210.hpp" -#include "es8311.hpp" -#include "lvgl_inputs.h" -#include "make_color.h" - -#include "battery_info.hpp" -#include "fs_init.hpp" -#include "hal_events.hpp" -#include "input_state.hpp" -#include "mmap.hpp" -#include "statistics.hpp" -#include "usb.hpp" -#include "video_setting.hpp" - -namespace hal { - // top level init function - void init(); - - // i2c / peripheral interfaces - void i2c_init(); - std::shared_ptr get_internal_i2c(); - std::shared_ptr get_external_i2c(); - - // input - void init_input(); - void get_input_state(InputState *state); - - // video - static constexpr int NUM_ROWS_IN_FRAME_BUFFER = 50; - std::shared_ptr get_display(); - uint16_t *get_vram0(); - uint16_t *get_vram1(); - uint8_t *get_frame_buffer0(); - uint8_t *get_frame_buffer1(); - void lcd_write(const uint8_t *data, size_t length, uint32_t user_data); - void lcd_write_frame(const uint16_t x, const uint16_t y, const uint16_t width, const uint16_t height, const uint8_t *data); - void lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data); - void lcd_init(); - void set_display_brightness(float brightness); - float get_display_brightness(); - - void init_video_task(); - void set_display_size(size_t width, size_t height); - void set_native_size(size_t width, size_t height, int pitch = -1); - void set_palette(const uint16_t* palette, size_t size = 256); - void push_frame(const void* frame); - - VideoSetting get_video_setting(); - void set_video_setting(const VideoSetting& setting); - - // audio - void audio_init(); - void set_audio_sample_rate(uint32_t sample_rate); - uint32_t get_audio_sample_rate(); - void play_audio(const uint8_t *data, uint32_t num_bytes); - bool is_muted(); - void set_muted(bool mute); - int get_audio_volume(); - void set_audio_volume(int percent); - - // battery - void battery_init(); - std::shared_ptr get_battery(); -} diff --git a/components/box-emu-hal/include/emu_v0.hpp b/components/box-emu-hal/include/emu_v0.hpp deleted file mode 100644 index 019a109..0000000 --- a/components/box-emu-hal/include/emu_v0.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include "mcp23x17.hpp" - -class EmuV0 { -public: - static constexpr uint16_t START_PIN = (1<<0) << 0; // start pin is on port a of the MCP23x17 - static constexpr uint16_t SELECT_PIN = (1<<1) << 0; // select pin is on port a of the MCP23x17 - static constexpr uint16_t UP_PIN = (1<<0) << 8; // up pin is on port b of the MCP23x17 - static constexpr uint16_t DOWN_PIN = (1<<1) << 8; // down pin is on port b of the MCP23x17 - static constexpr uint16_t LEFT_PIN = (1<<2) << 8; // left pin is on port b of the MCP23x17 - static constexpr uint16_t RIGHT_PIN = (1<<3) << 8; // right pin is on port b of the MCP23x17 - static constexpr uint16_t A_PIN = (1<<4) << 8; // a pin is on port b of the MCP23x17 - static constexpr uint16_t B_PIN = (1<<5) << 8; // b pin is on port b of the MCP23x17 - static constexpr uint16_t X_PIN = (1<<6) << 8; // x pin is on port b of the MCP23x17 - static constexpr uint16_t Y_PIN = (1<<7) << 8; // y pin is on port b of the MCP23x17 - static constexpr uint16_t BAT_ALERT_PIN = 0; // battery alert pin doesn't exist on the MCP23x17 - static constexpr uint16_t VOL_UP_PIN = 0; // volume up pin doesn't exist on the MCP23x17 - static constexpr uint16_t VOL_DOWN_PIN = 0; // volume down pin doesn't exist on the MCP23x17 - static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN); - static constexpr uint16_t INTERRUPT_MASK = (START_PIN | SELECT_PIN); - static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN ); // pins are active low so invert them - static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; - static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; - static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; - static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; -}; diff --git a/components/box-emu-hal/include/emu_v1.hpp b/components/box-emu-hal/include/emu_v1.hpp deleted file mode 100644 index 5c614f7..0000000 --- a/components/box-emu-hal/include/emu_v1.hpp +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "aw9523.hpp" -#include "oneshot_adc.hpp" - -class EmuV1 { -public: - using InputDriver = espp::Aw9523; - static constexpr gpio_num_t VBAT_SENSE_PIN = GPIO_NUM_14; // battery sense pin is on GPIO 14 - static constexpr gpio_num_t AW9523_INT_PIN = GPIO_NUM_21; // interrupt pin is on GPIO 21 - static constexpr uint16_t UP_PIN = (1<<0) << 0; // up pin is on port 0 of the AW9523 - static constexpr uint16_t DOWN_PIN = (1<<1) << 0; // down pin is on port 0 of the AW9523 - static constexpr uint16_t LEFT_PIN = (1<<2) << 0; // left pin is on port 0 of the AW9523 - static constexpr uint16_t RIGHT_PIN = (1<<3) << 0; // right pin is on port 0 of the AW9523 - static constexpr uint16_t A_PIN = (1<<4) << 0; // a pin is on port 0 of the AW9523 - static constexpr uint16_t B_PIN = (1<<5) << 0; // b pin is on port 0 of the AW9523 - static constexpr uint16_t X_PIN = (1<<6) << 0; // x pin is on port 0 of the AW9523 - static constexpr uint16_t Y_PIN = (1<<7) << 0; // y pin is on port 0 of the AW9523 - static constexpr uint16_t START_PIN = (1<<0) << 8; // start pin is on port 1 of the AW9523 - static constexpr uint16_t SELECT_PIN = (1<<1) << 8; // select pin is on port 1 of the AW9523 - static constexpr uint16_t BAT_ALERT_PIN = (1<<3) << 8; // battery alert pin is on port 1 of the AW9523 - static constexpr uint16_t VOL_UP_PIN = (1<<4) << 8; // volume up pin is on port 1 of the AW9523 - static constexpr uint16_t VOL_DOWN_PIN = (1<<5) << 8; // volume down pin is on port 1 of the AW9523 - static constexpr uint16_t DIRECTION_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); - static constexpr uint16_t INTERRUPT_MASK = (BAT_ALERT_PIN); - static constexpr uint16_t INVERT_MASK = (UP_PIN | DOWN_PIN | LEFT_PIN | RIGHT_PIN | A_PIN | B_PIN | X_PIN | Y_PIN | START_PIN | SELECT_PIN | BAT_ALERT_PIN | VOL_UP_PIN | VOL_DOWN_PIN); // pins are active low so invert them - static constexpr uint8_t PORT_0_DIRECTION_MASK = DIRECTION_MASK & 0xFF; - static constexpr uint8_t PORT_1_DIRECTION_MASK = (DIRECTION_MASK >> 8) & 0xFF; - static constexpr uint8_t PORT_0_INTERRUPT_MASK = INTERRUPT_MASK & 0xFF; - static constexpr uint8_t PORT_1_INTERRUPT_MASK = (INTERRUPT_MASK >> 8) & 0xFF; - - // ADC for the battery voltage, it's on ADC2_CH3, which is IO14 - static constexpr adc_unit_t BATTERY_ADC_UNIT = ADC_UNIT_2; - static constexpr adc_channel_t BATTERY_ADC_CHANNEL = ADC_CHANNEL_3; -}; diff --git a/components/box-emu-hal/include/fs_init.hpp b/components/box-emu-hal/include/fs_init.hpp deleted file mode 100644 index 2613087..0000000 --- a/components/box-emu-hal/include/fs_init.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#define MOUNT_POINT "/sdcard" - -#include "hal.hpp" -#include "format.hpp" - -void fs_init(); -sdmmc_card_t *get_sdcard(); diff --git a/components/box-emu-hal/include/hal.hpp b/components/box-emu-hal/include/hal.hpp deleted file mode 100644 index 343dfaa..0000000 --- a/components/box-emu-hal/include/hal.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include - -#if defined(CONFIG_HARDWARE_BOX) -#include "box.hpp" -#elif defined(CONFIG_HARDWARE_BOX_3) -#include "box_3.hpp" -#else -#error "Unsupported hardware configuration specified" -#endif diff --git a/components/box-emu-hal/include/hal_events.hpp b/components/box-emu-hal/include/hal_events.hpp deleted file mode 100644 index ee8c691..0000000 --- a/components/box-emu-hal/include/hal_events.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -#include "event_manager.hpp" - -static const std::string mute_button_topic = "mute"; -static const std::string battery_topic = "battery"; -static const std::string volume_changed_topic = "volume"; diff --git a/components/box-emu-hal/include/input_state.hpp b/components/box-emu-hal/include/input_state.hpp deleted file mode 100644 index 628296c..0000000 --- a/components/box-emu-hal/include/input_state.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -struct InputState { - int a : 1; - int b : 1; - int x : 1; - int y : 1; - int select : 1; - int start : 1; - int up : 1; - int down : 1; - int left : 1; - int right : 1; -}; - -[[maybe_unused]] static bool operator==(const InputState& lhs, const InputState& rhs) { - return - lhs.a == rhs.a && - lhs.b == rhs.b && - lhs.x == rhs.x && - lhs.y == rhs.y && - lhs.select == rhs.select && - lhs.start == rhs.start && - lhs.up == rhs.up && - lhs.down == rhs.down && - lhs.left == rhs.left && - lhs.right == rhs.right; -} diff --git a/components/box-emu-hal/include/lvgl_inputs.h b/components/box-emu-hal/include/lvgl_inputs.h deleted file mode 100644 index 68cacca..0000000 --- a/components/box-emu-hal/include/lvgl_inputs.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "lvgl.h" - -#ifdef __cplusplus -extern "C" -{ -#endif - - lv_indev_t *get_keypad_input_device(); - void touchpad_read(unsigned char *num_touch_points, unsigned short* x, unsigned short* y, unsigned char* btn_state); - -#ifdef __cplusplus -} - -#endif diff --git a/components/box-emu-hal/include/make_color.h b/components/box-emu-hal/include/make_color.h deleted file mode 100644 index 245c54e..0000000 --- a/components/box-emu-hal/include/make_color.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include - -#ifdef __cplusplus -extern "C" -{ -#endif - -uint16_t make_color(uint8_t r, uint8_t g, uint8_t b); - -#ifdef __cplusplus -} -#endif diff --git a/components/box-emu-hal/include/mmap.hpp b/components/box-emu-hal/include/mmap.hpp deleted file mode 100644 index 924c1f2..0000000 --- a/components/box-emu-hal/include/mmap.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include - -#include -#include "nvs_flash.h" -#include "spi_flash_mmap.h" -#include "esp_partition.h" - -#include "format.hpp" - -void init_memory(); -size_t copy_romdata_to_cart_partition(const std::string& rom_filename); -uint8_t *get_mmapped_romdata(); diff --git a/components/box-emu-hal/include/statistics.hpp b/components/box-emu-hal/include/statistics.hpp deleted file mode 100644 index 077c55b..0000000 --- a/components/box-emu-hal/include/statistics.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -#include -#include - -#include "format.hpp" - -void update_frame_time(uint64_t frame_time); -void reset_frame_time(); - -float get_fps(); -uint64_t get_frame_time(); -uint64_t get_frame_time_max(); -uint64_t get_frame_time_min(); -float get_frame_time_avg(); - -void print_statistics(); diff --git a/components/box-emu-hal/include/usb.hpp b/components/box-emu-hal/include/usb.hpp deleted file mode 100644 index de33e6a..0000000 --- a/components/box-emu-hal/include/usb.hpp +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include "fs_init.hpp" - -bool usb_is_enabled(); -void usb_init(); -void usb_deinit(); diff --git a/components/box-emu-hal/include/video_setting.hpp b/components/box-emu-hal/include/video_setting.hpp deleted file mode 100644 index a54fc8a..0000000 --- a/components/box-emu-hal/include/video_setting.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -enum class VideoSetting { ORIGINAL, FIT, FILL, MAX_UNUSED }; diff --git a/components/box-emu-hal/src/battery.cpp b/components/box-emu-hal/src/battery.cpp deleted file mode 100644 index 73bb7fc..0000000 --- a/components/box-emu-hal/src/battery.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::shared_ptr battery_{nullptr}; -static std::shared_ptr adc_{nullptr}; -static std::shared_ptr battery_task_; -static bool battery_initialized_ = false; -static std::vector channels; - -using namespace std::chrono_literals; - -void hal::battery_init() { - if (battery_initialized_) { - return; - } - fmt::print("Initializing battery...\n"); - auto i2c = hal::get_external_i2c(); - bool can_communicate = i2c->probe_device(espp::Max1704x::DEFAULT_ADDRESS); - if (!can_communicate) { - fmt::print("Could not communicate with battery!\n"); - // go ahead and set the battery_initialized_ flag to true so we don't try to - // initialize the battery again - battery_initialized_ = true; - return; - } - - // // NOTE: we could also make an ADC for measuring battery voltage - // // make the adc channels - // channels.clear(); - // channels.push_back({ - // .unit = BATTERY_ADC_UNIT, - // .channel = BATTERY_ADC_CHANNEL, - // .attenuation = ADC_ATTEN_DB_12}); - // adc_ = std::make_shared(espp::OneshotAdc::Config{ - // .unit = BATTERY_ADC_UNIT, - // .channels = channels - // }); - - // now make the Max17048 that we'll use to get good state of charge, charge - // rate, etc. - battery_ = std::make_shared(espp::Max1704x::Config{ - .device_address = espp::Max1704x::DEFAULT_ADDRESS, - .write = std::bind(&espp::I2c::write, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read, i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3) - }); - // NOTE: the MAX17048 is tied to the VBAT for its power supply (as you would - // imagine), this means that we cannnot communicate with it if the battery is - // not connected. Therefore, if we are unable to communicate with the battery - // we will just return and not start the battery task. - battery_task_ = std::make_shared(espp::HighResolutionTimer::Config{ - .name = "battery", - .callback = []() { - std::error_code ec; - // get the voltage (V) - auto voltage = battery_->get_battery_voltage(ec); - if (ec) { - fmt::print("Error getting battery voltage: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - // get the state of charge (%) - auto soc = battery_->get_battery_percentage(ec); - if (ec) { - fmt::print("Error getting battery percentage: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - // get the charge rate (+/- % per hour) - auto charge_rate = battery_->get_battery_charge_rate(ec); - if (ec) { - fmt::print("Error getting battery charge rate: {}\n", ec.message()); - fmt::print("Battery is probably not connected!\n"); - fmt::print("Stopping battery task...\n"); - battery_task_->stop(); - return; - } - - // NOTE: we could also get voltage from the adc for the battery if we - // wanted, but the MAX17048 gives us the same voltage anyway - // auto maybe_mv = adc_->read_mv(channels[0]); - // if (maybe_mv.has_value()) { - // // convert mv -> V and from the voltage divider (R1=R2) to real - // // battery volts - // voltage = maybe_mv.value() / 1000.0f * 2.0f; - // } - - // now publish a BatteryInfo struct to the battery_topic - auto battery_info = BatteryInfo{ - .voltage = voltage, - .level = soc, - .charge_rate = charge_rate, - }; - std::vector battery_info_data; - // fmt::print("Publishing battery info: {}\n", battery_info); - auto bytes_serialized = espp::serialize(battery_info, battery_info_data); - if (bytes_serialized == 0) { - return; - } - espp::EventManager::get().publish(battery_topic, battery_info_data); - return; - }}); - uint64_t battery_period_us = 500 * 1000; // 500ms - battery_task_->periodic(battery_period_us); - battery_initialized_ = true; -} - -std::shared_ptr hal::get_battery() { - battery_init(); - return battery_; -} diff --git a/components/box-emu-hal/src/box_emu_hal.cpp b/components/box-emu-hal/src/box_emu_hal.cpp deleted file mode 100644 index 99b1769..0000000 --- a/components/box-emu-hal/src/box_emu_hal.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include "box_emu_hal.hpp" - -void hal::init() { - // init nvs and partition - init_memory(); - // init filesystem - fs_init(); - // init the display subsystem - hal::lcd_init(); - // initialize the i2c buses for touchpad, imu, audio codecs, gamepad, haptics, etc. - hal::i2c_init(); - // init the audio subsystem - hal::audio_init(); - // initialize the rest of the input system which is common to both v0 and v1 - hal::init_input(); - // initialize the video task for the emulators - hal::init_video_task(); - // initialize the battery system - hal::battery_init(); -} diff --git a/components/box-emu-hal/src/fs_init.cpp b/components/box-emu-hal/src/fs_init.cpp deleted file mode 100644 index 562f14a..0000000 --- a/components/box-emu-hal/src/fs_init.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "fs_init.hpp" - -static sdmmc_card_t *sdcard = nullptr; - -static void sdcard_init() { - esp_err_t ret; - - // Options for mounting the filesystem. If format_if_mount_failed is set to - // true, SD card will be partitioned and formatted in case when mounting - // fails. - esp_vfs_fat_sdmmc_mount_config_t mount_config; - memset(&mount_config, 0, sizeof(mount_config)); - mount_config.format_if_mount_failed = false; - mount_config.max_files = 5; - mount_config.allocation_unit_size = 16 * 1024; - const char mount_point[] = "/sdcard"; - fmt::print("Initializing SD card\n"); - - // Use settings defined above to initialize SD card and mount FAT filesystem. - // Note: esp_vfs_fat_sdmmc/sdspi_mount is all-in-one convenience functions. - // Please check its source code and implement error recovery when developing - // production applications. - fmt::print("Using SPI peripheral\n"); - - // By default, SD card frequency is initialized to SDMMC_FREQ_DEFAULT (20MHz) - // For setting a specific frequency, use host.max_freq_khz (range 400kHz - 20MHz for SDSPI) - // Example: for fixed frequency of 10MHz, use host.max_freq_khz = 10000; - sdmmc_host_t host = SDSPI_HOST_DEFAULT(); - host.slot = sdcard_spi_num; - // host.max_freq_khz = 20 * 1000; - - spi_bus_config_t bus_cfg; - memset(&bus_cfg, 0, sizeof(bus_cfg)); - bus_cfg.mosi_io_num = sdcard_mosi; - bus_cfg.miso_io_num = sdcard_miso; - bus_cfg.sclk_io_num = sdcard_sclk; - bus_cfg.quadwp_io_num = -1; - bus_cfg.quadhd_io_num = -1; - bus_cfg.max_transfer_sz = 8192; - spi_host_device_t host_id = (spi_host_device_t)host.slot; - ret = spi_bus_initialize(host_id, &bus_cfg, SDSPI_DEFAULT_DMA); - if (ret != ESP_OK) { - fmt::print("Failed to initialize bus.\n"); - return; - } - - // This initializes the slot without card detect (CD) and write protect (WP) signals. - // Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals. - sdspi_device_config_t slot_config = SDSPI_DEVICE_CONFIG_DEFAULT(); - slot_config.gpio_cs = sdcard_cs; - slot_config.host_id = host_id; - - fmt::print("Mounting filesystem\n"); - ret = esp_vfs_fat_sdspi_mount(mount_point, &host, &slot_config, &mount_config, &sdcard); - - if (ret != ESP_OK) { - if (ret == ESP_FAIL) { - fmt::print("Failed to mount filesystem. " - "If you want the card to be formatted, set the CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED menuconfig option.\n"); - } else { - fmt::print("Failed to initialize the card ({}). " - "Make sure SD card lines have pull-up resistors in place.\n", esp_err_to_name(ret)); - } - return; - } - fmt::print("Filesystem mounted\n"); - - // Card has been initialized, print its properties - sdmmc_card_print_info(stdout, sdcard); -} - -void fs_init() { - if (sdcard) return; - sdcard_init(); -} - -sdmmc_card_t *get_sdcard() { - return sdcard; -} diff --git a/components/box-emu-hal/src/hal_i2c.cpp b/components/box-emu-hal/src/hal_i2c.cpp deleted file mode 100644 index 7947836..0000000 --- a/components/box-emu-hal/src/hal_i2c.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::shared_ptr internal_i2c = nullptr; -static std::shared_ptr external_i2c = nullptr; - -static bool initialized = false; - -void hal::i2c_init() { - if (initialized) return; - // make the i2c on core 1 so that the i2c interrupts are handled on core 1 - espp::Task::run_on_core([]() { - internal_i2c = std::make_shared(espp::I2c::Config{ - .port = internal_i2c_port, - .sda_io_num = internal_i2c_sda, - .scl_io_num = internal_i2c_scl, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE, - .timeout_ms = 100, - }); - external_i2c = std::make_shared(espp::I2c::Config{ - .port = external_i2c_port, - .sda_io_num = external_i2c_sda, - .scl_io_num = external_i2c_scl, - .sda_pullup_en = GPIO_PULLUP_ENABLE, - .scl_pullup_en = GPIO_PULLUP_ENABLE, - .timeout_ms = 100, - }); - }, 1); - initialized = true; -} - -std::shared_ptr hal::get_internal_i2c() { - i2c_init(); - return internal_i2c; -} - -std::shared_ptr hal::get_external_i2c() { - i2c_init(); - return external_i2c; -} diff --git a/components/box-emu-hal/src/i2s_audio.cpp b/components/box-emu-hal/src/i2s_audio.cpp deleted file mode 100644 index 79190e3..0000000 --- a/components/box-emu-hal/src/i2s_audio.cpp +++ /dev/null @@ -1,267 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::atomic muted_{false}; -static std::atomic volume_{60}; - -static std::shared_ptr mute_button; -static std::unique_ptr audio_task; -static TaskHandle_t main_task_handle = NULL; -static i2s_chan_handle_t tx_handle = NULL; - -// Scratch buffers -namespace hal { -static constexpr int DEFAULT_AUDIO_RATE = 48000; -static constexpr int NUM_CHANNELS = 2; -static constexpr int NUM_BYTES_PER_CHANNEL = 2; -static constexpr int UPDATE_FREQUENCY = 60; -static constexpr int AUDIO_BUFFER_SIZE = DEFAULT_AUDIO_RATE * NUM_CHANNELS * NUM_BYTES_PER_CHANNEL / UPDATE_FREQUENCY; -} -static uint8_t tx_buffer[hal::AUDIO_BUFFER_SIZE]; -static StreamBufferHandle_t tx_audio_stream; - -static i2s_std_config_t std_cfg; - -static i2s_event_callbacks_t tx_callbacks_; -std::atomic has_sound{false}; - -static void update_volume_output() { - if (muted_) { - es8311_codec_set_voice_volume(0); - } else { - es8311_codec_set_voice_volume(volume_); - } -} - -void hal::set_muted(bool mute) { - muted_ = mute; - update_volume_output(); -} - -bool hal::is_muted() { - return muted_; -} - -void hal::set_audio_volume(int percent) { - volume_ = percent; - update_volume_output(); -} - -int hal::get_audio_volume() { - return volume_; -} - -uint32_t hal::get_audio_sample_rate() { - return std_cfg.clk_cfg.sample_rate_hz; -} - -void hal::set_audio_sample_rate(uint32_t sample_rate) { - fmt::print("Setting sample rate to {}\n", sample_rate); - // disable i2s - i2s_channel_disable(tx_handle); - // update the config - std_cfg.clk_cfg.sample_rate_hz = sample_rate; - i2s_channel_reconfig_std_clock(tx_handle, &std_cfg.clk_cfg); - // clear the buffer - xStreamBufferReset(tx_audio_stream); - // re-enable i2s - i2s_channel_enable(tx_handle); -} - -static bool IRAM_ATTR audio_task_fn(std::mutex &m, std::condition_variable &cv) -{ - // Queue the next I2S out frame to write - uint16_t available = xStreamBufferBytesAvailable(tx_audio_stream); - available = std::min(available, hal::AUDIO_BUFFER_SIZE); - uint8_t* buffer = &tx_buffer[0]; - static constexpr int BUFF_SIZE = hal::AUDIO_BUFFER_SIZE; - memset(buffer, 0, BUFF_SIZE); - - if (available == 0) { - i2s_channel_write(tx_handle, buffer, BUFF_SIZE, NULL, portMAX_DELAY); - } else { - xStreamBufferReceive(tx_audio_stream, buffer, available, 0); - i2s_channel_write(tx_handle, buffer, available, NULL, portMAX_DELAY); - } - return false; // don't stop the task -} - -static bool IRAM_ATTR tx_sent_callback(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx) -{ - // notify the main task that we're done - vTaskNotifyGiveFromISR(main_task_handle, NULL); - return true; -} - -static esp_err_t i2s_driver_init(void) -{ - fmt::print("initializing i2s driver...\n"); - auto ret_val = ESP_OK; - fmt::print("Using newer I2S standard\n"); - i2s_chan_config_t chan_cfg = { \ - .id = i2s_port, - .role = I2S_ROLE_MASTER, - .dma_desc_num = 16, - .dma_frame_num = 48, - .auto_clear = true, - .intr_priority = 0, - }; - - ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle, nullptr)); - - std_cfg = { - .clk_cfg = { - .sample_rate_hz = hal::DEFAULT_AUDIO_RATE, - .clk_src = I2S_CLK_SRC_DEFAULT, - .ext_clk_freq_hz = 0, - .mclk_multiple = I2S_MCLK_MULTIPLE_256, - }, - .slot_cfg = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO), - .gpio_cfg = { - .mclk = i2s_mck_io, - .bclk = i2s_bck_io, - .ws = i2s_ws_io, - .dout = i2s_do_io, - .din = i2s_di_io, - .invert_flags = { - .mclk_inv = false, - .bclk_inv = false, - .ws_inv = false, - }, - }, - }; - std_cfg.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_256; - - ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle, &std_cfg)); - - tx_audio_stream = xStreamBufferCreate(hal::AUDIO_BUFFER_SIZE*4, 0); - - memset(&tx_callbacks_, 0, sizeof(tx_callbacks_)); - tx_callbacks_.on_sent = tx_sent_callback; - i2s_channel_register_event_callback(tx_handle, &tx_callbacks_, NULL); - - main_task_handle = xTaskGetCurrentTaskHandle(); - - audio_task = std::make_unique(espp::Task::Config{ - .name = "audio task", - .callback = audio_task_fn, - .stack_size_bytes = 1024 * 4, - .priority = 19, - .core_id = 1, - }); - - xStreamBufferReset(tx_audio_stream); - - ESP_ERROR_CHECK(i2s_channel_enable(tx_handle)); - return ret_val; -} - -// es8311 is for audio output codec -static esp_err_t es8311_init_default(void) -{ - fmt::print("initializing es8311 codec...\n"); - esp_err_t ret_val = ESP_OK; - audio_hal_codec_config_t cfg; - memset(&cfg, 0, sizeof(cfg)); - cfg.codec_mode = AUDIO_HAL_CODEC_MODE_DECODE; - cfg.dac_output = AUDIO_HAL_DAC_OUTPUT_LINE1; - cfg.i2s_iface.bits = AUDIO_HAL_BIT_LENGTH_16BITS; - cfg.i2s_iface.fmt = AUDIO_HAL_I2S_NORMAL; - cfg.i2s_iface.mode = AUDIO_HAL_MODE_SLAVE; - cfg.i2s_iface.samples = AUDIO_HAL_16K_SAMPLES; - - ret_val |= es8311_codec_init(&cfg); - ret_val |= es8311_set_bits_per_sample(cfg.i2s_iface.bits); - ret_val |= es8311_config_fmt((es_i2s_fmt_t)cfg.i2s_iface.fmt); - ret_val |= es8311_codec_set_voice_volume(volume_); - ret_val |= es8311_codec_ctrl_state(cfg.codec_mode, AUDIO_HAL_CTRL_START); - - if (ESP_OK != ret_val) { - fmt::print("Failed initialize codec\n"); - } else { - fmt::print("Codec initialized\n"); - } - - return ret_val; -} - -static std::unique_ptr mute_task; -static QueueHandle_t gpio_evt_queue; - -static void gpio_isr_handler(void *arg) { - uint32_t gpio_num = (uint32_t)arg; - xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); -} - -static void init_mute_button(void) { - // register that we publish the mute button state - espp::EventManager::get().add_publisher(mute_button_topic, "i2s_audio"); - - fmt::print("Initializing mute button\n"); - mute_button = std::make_shared(espp::Button::Config{ - .name = "mute button", - .interrupt_config = - { - .gpio_num = mute_pin, - .callback = - [&](const espp::Interrupt::Event &event) { - hal::set_muted(event.active); - // simply publish that the mute button was presssed - espp::EventManager::get().publish(mute_button_topic, {}); - }, - .active_level = espp::Interrupt::ActiveLevel::LOW, - .interrupt_type = espp::Interrupt::Type::ANY_EDGE, - .pullup_enabled = true, - .pulldown_enabled = false, - }, - .task_config = - { - .name = "mute button task", - .stack_size_bytes = 4 * 1024, - .priority = 5, - }, - .log_level = espp::Logger::Verbosity::WARN, - }); - - // update the mute state (since it's a flip-flop and may have been set if we - // restarted without power loss) - hal::set_muted(mute_button->is_pressed()); - - fmt::print("Mute button initialized\n"); -} - -static bool initialized = false; -void hal::audio_init() { - if (initialized) return; - - // Config power control IO - gpio_set_direction(sound_power_pin, GPIO_MODE_OUTPUT); - gpio_set_level(sound_power_pin, 1); - - auto internal_i2c = hal::get_internal_i2c(); - - set_es8311_write(std::bind(&espp::I2c::write, internal_i2c.get(), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)); - set_es8311_read(std::bind(&espp::I2c::read_at_register, internal_i2c.get(), - std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4)); - - i2s_driver_init(); - es8311_init_default(); - - // now initialize the mute gpio - init_mute_button(); - - audio_task->start(); - - fmt::print("Audio initialized\n"); - - initialized = true; -} - -void IRAM_ATTR hal::play_audio(const uint8_t *data, uint32_t num_bytes) { - if (has_sound) { - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - } - // don't block here - xStreamBufferSendFromISR(tx_audio_stream, data, num_bytes, NULL); - has_sound = true; -} diff --git a/components/box-emu-hal/src/input.cpp b/components/box-emu-hal/src/input.cpp deleted file mode 100644 index 9b602d5..0000000 --- a/components/box-emu-hal/src/input.cpp +++ /dev/null @@ -1,260 +0,0 @@ - -#include "box_emu_hal.hpp" - -using namespace std::chrono_literals; - -struct TouchpadData { - uint8_t num_touch_points = 0; - uint16_t x = 0; - uint16_t y = 0; - uint8_t btn_state = 0; -}; - -class InputBase { -public: - virtual uint16_t get_pins(std::error_code& ec) = 0; - virtual InputState pins_to_gamepad_state(uint16_t pins) = 0; - virtual void handle_volume_pins(uint16_t pins) = 0; -}; - -template -class Input : public InputBase { -public: - explicit Input(std::shared_ptr input_driver) : input_driver(input_driver) {} - virtual uint16_t get_pins(std::error_code& ec) override { - auto val = input_driver->get_pins(ec); - if (ec) { - return 0; - } - return val ^ T::INVERT_MASK; - } - virtual InputState pins_to_gamepad_state(uint16_t pins) override { - InputState state; - state.a = (bool)(pins & T::A_PIN); - state.b = (bool)(pins & T::B_PIN); - state.x = (bool)(pins & T::X_PIN); - state.y = (bool)(pins & T::Y_PIN); - state.start = (bool)(pins & T::START_PIN); - state.select = (bool)(pins & T::SELECT_PIN); - state.up = (bool)(pins & T::UP_PIN); - state.down = (bool)(pins & T::DOWN_PIN); - state.left = (bool)(pins & T::LEFT_PIN); - state.right = (bool)(pins & T::RIGHT_PIN); - return state; - } - virtual void handle_volume_pins(uint16_t pins) override { - // check the volume pins and send out events if they're pressed / released - bool volume_up = (bool)(pins & T::VOL_UP_PIN); - bool volume_down = (bool)(pins & T::VOL_DOWN_PIN); - int volume_change = (volume_up * 10) + (volume_down * -10); - if (volume_change != 0) { - // change the volume - int current_volume = hal::get_audio_volume(); - int new_volume = std::clamp(current_volume + volume_change, 0, 100); - hal::set_audio_volume(new_volume); - // send out a volume change event - espp::EventManager::get().publish(volume_changed_topic, {}); - } - } -protected: - std::shared_ptr input_driver; -}; - -static std::shared_ptr input; - -static std::shared_ptr touch_driver; -static std::shared_ptr touchpad; -static std::shared_ptr keypad; -static std::shared_ptr input_timer; -static struct InputState gamepad_state; -static std::mutex gamepad_state_mutex; -static TouchpadData touchpad_data; -static std::mutex touchpad_data_mutex; - -/** - * Touch Controller configuration - */ -void touchpad_read(uint8_t* num_touch_points, uint16_t* x, uint16_t* y, uint8_t* btn_state) { - std::lock_guard lock(touchpad_data_mutex); - *num_touch_points = touchpad_data.num_touch_points; - *x = touchpad_data.x; - *y = touchpad_data.y; - *btn_state = touchpad_data.btn_state; -} - -void keypad_read(bool *up, bool *down, bool *left, bool *right, bool *enter, bool *escape) { - InputState state; - hal::get_input_state(&state); - *up = state.up; - *down = state.down; - *left = state.left; - *right = state.right; - - *enter = state.a || state.start; - *escape = state.b || state.select; -} - -void update_touchpad_input() { - // get the latest data from the device - std::error_code ec; - bool new_data = touch_driver->update(ec); - if (ec) { - fmt::print("error updating touch_driver: {}\n", ec.message()); - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = {}; - return; - } - if (!new_data) { - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = {}; - return; - } - // get the latest data from the touchpad - TouchpadData temp_data; - touch_driver->get_touch_point(&temp_data.num_touch_points, &temp_data.x, &temp_data.y); - temp_data.btn_state = touch_driver->get_home_button_state(); - // update the touchpad data - std::lock_guard lock(touchpad_data_mutex); - touchpad_data = temp_data; -} - -void update_gamepad_input() { - static bool can_read_input = true; - if (!input) { - return; - } - if (!can_read_input) { - return; - } - // pins are active low - // start, select = A0, A1 - std::error_code ec; - auto pins = input->get_pins(ec); - if (ec) { - fmt::print("error getting pins: {}\n", ec.message()); - can_read_input = false; - return; - } - - auto new_gamepad_state = input->pins_to_gamepad_state(pins); - { - std::lock_guard lock(gamepad_state_mutex); - gamepad_state = new_gamepad_state; - } - input->handle_volume_pins(pins); - // TODO: check the battery alert pin and if it's low, send out a battery alert event -} - -static void init_input_generic() { - auto internal_i2c = hal::get_internal_i2c(); - - fmt::print("Initializing touch driver\n"); - touch_driver = std::make_shared(TouchDriver::Config{ - .write = std::bind(&espp::I2c::write, internal_i2c.get(), std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .read = std::bind(&espp::I2c::read, internal_i2c.get(), std::placeholders::_1, - std::placeholders::_2, std::placeholders::_3), - .log_level = espp::Logger::Verbosity::WARN - }); - - fmt::print("Initializing touchpad\n"); - touchpad = std::make_shared(espp::TouchpadInput::Config{ - .touchpad_read = touchpad_read, - .swap_xy = touch_swap_xy, - .invert_x = touch_invert_x, - .invert_y = touch_invert_y, - .log_level = espp::Logger::Verbosity::WARN - }); - - auto external_i2c = hal::get_external_i2c(); - - fmt::print("Initializing keypad\n"); - keypad = std::make_shared(espp::KeypadInput::Config{ - .read = keypad_read, - .log_level = espp::Logger::Verbosity::WARN - }); - - fmt::print("Initializing input task\n"); - input_timer = std::make_shared(espp::HighResolutionTimer::Config{ - .name = "Input timer", - .callback = []() { - update_touchpad_input(); - update_gamepad_input(); - }}); - uint64_t period_us = 30 * 1000; - input_timer->periodic(period_us); -} - -static void init_input_v0() { - auto external_i2c = hal::get_external_i2c(); - fmt::print("initializing input driver\n"); - using InputDriver = espp::Mcp23x17; - auto raw_input = new Input( - std::make_shared(InputDriver::Config{ - .port_0_direction_mask = EmuV0::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = EmuV0::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = EmuV0::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = EmuV0::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .read_register = std::bind(&espp::I2c::read_at_register, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4), - .log_level = espp::Logger::Verbosity::WARN - })); - input.reset(raw_input); -} - -static void init_input_v1() { - auto external_i2c = hal::get_external_i2c(); - fmt::print("initializing input driver\n"); - using InputDriver = espp::Aw9523; - auto raw_input = new Input( - std::make_shared(InputDriver::Config{ - .port_0_direction_mask = EmuV1::PORT_0_DIRECTION_MASK, - .port_0_interrupt_mask = EmuV1::PORT_0_INTERRUPT_MASK, - .port_1_direction_mask = EmuV1::PORT_1_DIRECTION_MASK, - .port_1_interrupt_mask = EmuV1::PORT_1_INTERRUPT_MASK, - .write = std::bind(&espp::I2c::write, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3), - .write_then_read = std::bind(&espp::I2c::write_read, external_i2c.get(), std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5), - .log_level = espp::Logger::Verbosity::WARN - })); - input.reset(raw_input); -} - -static std::atomic initialized = false; -void hal::init_input() { - if (initialized) return; - fmt::print("Initializing input subsystem\n"); - // probe the i2c bus for the mcp23x17 (which would be v0) or the aw9523 (which - // would be v1) - auto i2c = hal::get_external_i2c(); - bool mcp23x17_found = i2c->probe_device(espp::Mcp23x17::DEFAULT_ADDRESS); - bool aw9523_found = i2c->probe_device(espp::Aw9523::DEFAULT_ADDRESS); - - if (mcp23x17_found) { - fmt::print("Found MCP23x17, initializing input VERSION 0\n"); - init_input_v0(); - } else if (aw9523_found) { - fmt::print("Found AW9523, initializing input VERSION 1\n"); - init_input_v1(); - } else { - fmt::print("ERROR: No input systems found!\n"); - } - - // now initialize the rest of the input systems which are common to both - // versions - init_input_generic(); - - initialized = true; -} - -extern "C" lv_indev_t *get_keypad_input_device() { - if (!keypad) { - fmt::print("cannot get keypad input device: keypad not initialized properly!\n"); - return nullptr; - } - return keypad->get_input_device(); -} - -void hal::get_input_state(struct InputState* state) { - std::lock_guard lock(gamepad_state_mutex); - *state = gamepad_state; -} diff --git a/components/box-emu-hal/src/mmap.cpp b/components/box-emu-hal/src/mmap.cpp deleted file mode 100644 index 83788bc..0000000 --- a/components/box-emu-hal/src/mmap.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "mmap.hpp" -#include -#include "esp_heap_caps.h" - -static uint8_t* romdata = nullptr; - -void init_memory() { - // allocate memory for the ROM and make sure it's on the SPIRAM - romdata = (uint8_t*)heap_caps_malloc(4*1024*1024, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - if (romdata == nullptr) { - fmt::print(fg(fmt::terminal_color::red), "ERROR: Couldn't allocate memory for ROM!\n"); - } -} - -size_t copy_romdata_to_cart_partition(const std::string& rom_filename) { - // load the file data and iteratively copy it over - std::ifstream romfile(rom_filename, std::ios::binary | std::ios::ate); //open file at end - if (!romfile.is_open()) { - fmt::print("Error: ROM file does not exist\n"); - return 0; - } - size_t filesize = romfile.tellg(); // get size from current file pointer location; - romfile.seekg(0, std::ios::beg); //reset file pointer to beginning; - romfile.read((char*)(romdata), filesize); - romfile.close(); - return filesize; -} - -extern "C" uint8_t *osd_getromdata() { - return get_mmapped_romdata(); -} - -uint8_t *get_mmapped_romdata() { - return romdata; -} diff --git a/components/box-emu-hal/src/spi_lcd.cpp b/components/box-emu-hal/src/spi_lcd.cpp deleted file mode 100644 index f77da20..0000000 --- a/components/box-emu-hal/src/spi_lcd.cpp +++ /dev/null @@ -1,261 +0,0 @@ -#include "box_emu_hal.hpp" - -static spi_device_handle_t spi; -static spi_device_interface_config_t devcfg; - -static constexpr size_t pixel_buffer_size = lcd_width * hal::NUM_ROWS_IN_FRAME_BUFFER; -static std::shared_ptr display; - -std::shared_ptr hal::get_display() { - return display; -} - -static constexpr size_t frame_buffer_size = (((320) * 2) * 240); -static uint8_t *frame_buffer0; -static uint8_t *frame_buffer1; - -// the user flag for the callbacks does two things: -// 1. Provides the GPIO level for the data/command pin, and -// 2. Sets some bits for other signaling (such as LVGL FLUSH) -static constexpr int FLUSH_BIT = (1 << (int)espp::display_drivers::Flags::FLUSH_BIT); -static constexpr int DC_LEVEL_BIT = (1 << (int)espp::display_drivers::Flags::DC_LEVEL_BIT); - -// This function is called (in irq context!) just before a transmission starts. -// It will set the D/C line to the value indicated in the user field -// (DC_LEVEL_BIT). -static void IRAM_ATTR lcd_spi_pre_transfer_callback(spi_transaction_t *t) -{ - uint32_t user_flags = (uint32_t)(t->user); - bool dc_level = user_flags & DC_LEVEL_BIT; - gpio_set_level(lcd_dc_io, dc_level); -} - -// This function is called (in irq context!) just after a transmission ends. It -// will indicate to lvgl that the next flush is ready to be done if the -// FLUSH_BIT is set. -static void IRAM_ATTR lcd_spi_post_transfer_callback(spi_transaction_t *t) -{ - uint16_t user_flags = (uint32_t)(t->user); - bool should_flush = user_flags & FLUSH_BIT; - if (should_flush) { - lv_disp_t * disp = _lv_refr_get_disp_refreshing(); - lv_disp_flush_ready(disp->driver); - } -} -// Transaction descriptors. Declared static so they're not allocated on the -// stack; we need this memory even when this function is finished because the -// SPI driver needs access to it even while we're already calculating the next -// line. -static const int spi_queue_size = 6; -static spi_transaction_t trans[spi_queue_size]; -static std::atomic num_queued_trans = 0; - -static void IRAM_ATTR lcd_wait_lines() { - spi_transaction_t *rtrans; - esp_err_t ret; - // fmt::print("Waiting for {} queued transactions\n", num_queued_trans); - // Wait for all transactions to be done and get back the results. - while (num_queued_trans) { - ret = spi_device_get_trans_result(spi, &rtrans, 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Could not get trans result: {} '{}'\n", ret, esp_err_to_name(ret)); - } - num_queued_trans--; - //We could inspect rtrans now if we received any info back. The LCD is treated as write-only, though. - } -} - - -void IRAM_ATTR hal::lcd_write(const uint8_t *data, size_t length, uint32_t user_data) { - if (length == 0) { - return; - } - lcd_wait_lines(); - esp_err_t ret; - memset(&trans[0], 0, sizeof(spi_transaction_t)); - trans[0].length = length * 8; - trans[0].user = (void*)user_data; - // look at the length of the data and use tx_data if it is <= 32 bits - if (length <= 4) { - // copy the data pointer to trans[0].tx_data - memcpy(trans[0].tx_data, data, length); - trans[0].flags = SPI_TRANS_USE_TXDATA; - } else { - trans[0].tx_buffer = data; - trans[0].flags = 0; - } - ret = spi_device_queue_trans(spi, &trans[0], 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); - } else { - num_queued_trans++; - } -} - -void IRAM_ATTR hal::lcd_send_lines(int xs, int ys, int xe, int ye, const uint8_t *data, uint32_t user_data) { - // if we haven't waited by now, wait here... - lcd_wait_lines(); - esp_err_t ret; - size_t length = (xe-xs+1)*(ye-ys+1)*2; - if (length == 0) { - fmt::print("Bad length: ({},{}) to ({},{})\n", xs, ys, xe, ye); - } - // initialize the spi transactions - for (int i=0; i<6; i++) { - memset(&trans[i], 0, sizeof(spi_transaction_t)); - if ((i&1)==0) { - //Even transfers are commands - trans[i].length = 8; - trans[i].user = (void*)0; - } else { - //Odd transfers are data - trans[i].length = 8*4; - trans[i].user = (void*)DC_LEVEL_BIT; - } - trans[i].flags = SPI_TRANS_USE_TXDATA; - } - trans[0].tx_data[0] = (uint8_t)DisplayDriver::Command::caset; - trans[1].tx_data[0] = (xs)>> 8; - trans[1].tx_data[1] = (xs)&0xff; - trans[1].tx_data[2] = (xe)>>8; - trans[1].tx_data[3] = (xe)&0xff; - trans[2].tx_data[0] = (uint8_t)DisplayDriver::Command::raset; - trans[3].tx_data[0] = (ys)>>8; - trans[3].tx_data[1] = (ys)&0xff; - trans[3].tx_data[2] = (ye)>>8; - trans[3].tx_data[3] = (ye)&0xff; - trans[4].tx_data[0] = (uint8_t)DisplayDriver::Command::ramwr; - trans[5].tx_buffer = data; - trans[5].length = length*8; - // undo SPI_TRANS_USE_TXDATA flag - trans[5].flags = SPI_TRANS_DMA_BUFFER_ALIGN_MANUAL; - // we need to keep the dc bit set, but also add our flags - trans[5].user = (void*)(DC_LEVEL_BIT | user_data); - //Queue all transactions. - for (int i=0; i<6; i++) { - ret = spi_device_queue_trans(spi, &trans[i], 10 / portTICK_PERIOD_MS); - if (ret != ESP_OK) { - fmt::print("Couldn't queue trans: {} '{}'\n", ret, esp_err_to_name(ret)); - } else { - num_queued_trans++; - } - } - //When we are here, the SPI driver is busy (in the background) getting the - //transactions sent. That happens mostly using DMA, so the CPU doesn't have - //much to do here. We're not going to wait for the transaction to finish - //because we may as well spend the time calculating the next line. When that - //is done, we can call lcd_wait_lines, which will wait for the transfers - //to be done and check their status. -} - -extern "C" uint16_t make_color(uint8_t r, uint8_t g, uint8_t b) { - return lv_color_make(r,g,b).full; -} - -uint16_t* hal::get_vram0() { - return display->vram0(); -} - -uint16_t* hal::get_vram1() { - return display->vram1(); -} - -uint8_t* hal::get_frame_buffer0() { - return frame_buffer0; -} - -uint8_t* hal::get_frame_buffer1() { - return frame_buffer1; -} - -void hal::lcd_write_frame(const uint16_t xs, const uint16_t ys, const uint16_t width, const uint16_t height, const uint8_t * data){ - if (data) { - // have data, fill the area with the color data - lv_area_t area { - .x1 = (lv_coord_t)(xs), - .y1 = (lv_coord_t)(ys), - .x2 = (lv_coord_t)(xs+width-1), - .y2 = (lv_coord_t)(ys+height-1)}; - DisplayDriver::fill(nullptr, &area, (lv_color_t*)data); - } else { - // don't have data, so clear the area (set to 0) - DisplayDriver::clear(xs, ys, width, height); - } -} - -static bool initialized = false; -void hal::lcd_init() { - if (initialized) { - return; - } - esp_err_t ret; - - spi_bus_config_t buscfg; - memset(&buscfg, 0, sizeof(buscfg)); - buscfg.mosi_io_num = lcd_mosi_io; - buscfg.miso_io_num = -1; - buscfg.sclk_io_num = lcd_sclk_io; - buscfg.quadwp_io_num = -1; - buscfg.quadhd_io_num = -1; - buscfg.max_transfer_sz = frame_buffer_size * sizeof(lv_color_t) + 100; - - memset(&devcfg, 0, sizeof(devcfg)); - devcfg.mode = 0; - // devcfg.flags = SPI_DEVICE_NO_RETURN_RESULT; - devcfg.clock_speed_hz = lcd_clock_speed; - devcfg.input_delay_ns = 0; - devcfg.spics_io_num = lcd_cs_io; - devcfg.queue_size = spi_queue_size; - devcfg.pre_cb = lcd_spi_pre_transfer_callback; - devcfg.post_cb = lcd_spi_post_transfer_callback; - - //Initialize the SPI bus - ret = spi_bus_initialize(lcd_spi_num, &buscfg, SPI_DMA_CH_AUTO); - ESP_ERROR_CHECK(ret); - //Attach the LCD to the SPI bus - ret = spi_bus_add_device(lcd_spi_num, &devcfg, &spi); - ESP_ERROR_CHECK(ret); - // initialize the controller - DisplayDriver::initialize(espp::display_drivers::Config{ - .lcd_write = hal::lcd_write, - .lcd_send_lines = hal::lcd_send_lines, - .reset_pin = lcd_reset_io, - .data_command_pin = lcd_dc_io, - .reset_value = reset_value, - .invert_colors = invert_colors, - .mirror_x = mirror_x, - .mirror_y = mirror_y - }); - // initialize the display / lvgl - using namespace std::chrono_literals; - display = std::make_shared(espp::Display::AllocatingConfig{ - .width = lcd_width, - .height = lcd_height, - .pixel_buffer_size = pixel_buffer_size, - .flush_callback = DisplayDriver::flush, - .backlight_pin = backlight_io, - .backlight_on_value = backlight_value, - .task_config = { - .name = "display task", - .priority = 10, - .core_id = 1, - }, - .update_period = 5ms, - .double_buffered = true, - .allocation_flags = MALLOC_CAP_8BIT | MALLOC_CAP_DMA, - .rotation = rotation, - .software_rotation_enabled = true, - }); - - frame_buffer0 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - frame_buffer1 = (uint8_t*)heap_caps_malloc(frame_buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); - initialized = true; -} - -void hal::set_display_brightness(float brightness) { - display->set_brightness(brightness); -} - -float hal::get_display_brightness() { - return display->get_brightness(); -} diff --git a/components/box-emu-hal/src/statistics.cpp b/components/box-emu-hal/src/statistics.cpp deleted file mode 100644 index 0c5703a..0000000 --- a/components/box-emu-hal/src/statistics.cpp +++ /dev/null @@ -1,59 +0,0 @@ -#include "statistics.hpp" - -static uint32_t num_frames = 0; -static uint64_t frame_time = 0.0f; -static uint64_t frame_time_total = 0.0f; -static uint64_t frame_time_max = 0.0f; -static uint64_t frame_time_min = 0.0f; -static float frame_time_avg = 0.0f; - -void update_frame_time(uint64_t frame_time) -{ - num_frames++; - ::frame_time = frame_time; - frame_time_total = frame_time_total + frame_time; - frame_time_max = std::max(frame_time_max, frame_time); - frame_time_min = std::min(frame_time_min, frame_time); - frame_time_avg = float(frame_time_total) / num_frames; -} - -void reset_frame_time() -{ - num_frames = 0; - frame_time = 0; - frame_time_total = 0; - frame_time_max = 0; - frame_time_min = 1000000; // some large number - frame_time_avg = 0.0f; -} - -float get_fps() { - if (frame_time_total == 0) { - return 0.0f; - } - return num_frames / (frame_time_total / 1e6f); -} - -uint64_t get_frame_time() { - return frame_time; -} - -uint64_t get_frame_time_max() { - return frame_time_max; -} - -uint64_t get_frame_time_min() { - return frame_time_min; -} - -float get_frame_time_avg() { - return frame_time_avg; -} - -void print_statistics() { - fmt::print("Statistics:\n"); - fmt::print("-----------\n"); - fmt::print("Frames: {}\n", num_frames); - fmt::print("FPS: {:.1f}\n", get_fps()); - fmt::print("Frame Time: [min {} us, avg: {:.1f} us, max: {} us]\n", get_frame_time_min(), get_frame_time_avg(), get_frame_time_max()); -} diff --git a/components/box-emu-hal/src/usb.cpp b/components/box-emu-hal/src/usb.cpp deleted file mode 100644 index db7ba1d..0000000 --- a/components/box-emu-hal/src/usb.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "usb.hpp" - -#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) - -enum { - ITF_NUM_MSC = 0, - ITF_NUM_TOTAL -}; - -enum { - EDPT_CTRL_OUT = 0x00, - EDPT_CTRL_IN = 0x80, - - EDPT_MSC_OUT = 0x01, - EDPT_MSC_IN = 0x81, -}; - -static uint8_t const desc_configuration[] = { - // Config number, interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), - - // Interface number, string index, EP Out & EP In address, EP size - TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64), -}; - -static tusb_desc_device_t descriptor_config = { - .bLength = sizeof(descriptor_config), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - .idVendor = 0x303A, // This is Espressif VID. This needs to be changed according to Users / Customers - .idProduct = 0x4002, - .bcdDevice = 0x100, - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - .bNumConfigurations = 0x01 -}; - -static char const *string_desc_arr[] = { - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "Finger563", // 1: Manufacturer - "ESP-Box-Emu", // 2: Product - "123456", // 3: Serials - "Box-Emu uSD Card", // 4. MSC -}; - -// callback that is delivered when storage is mounted/unmounted by application. -static void storage_mount_changed_cb(tinyusb_msc_event_t *event) -{ - fmt::print("Storage mounted to application: {}\n", event->mount_changed_data.is_mounted ? "Yes" : "No"); -} - -static bool usb_enabled_ = false; -static usb_phy_handle_t jtag_phy; - -bool usb_is_enabled() { - return usb_enabled_; -} - -void usb_init() { - // get the card from the filesystem initialization - auto card = get_sdcard(); - if (!card) { - fmt::print("No SD card found, skipping USB MSC initialization\n"); - return; - } - - fmt::print("Deleting JTAG PHY\n"); - usb_del_phy(jtag_phy); - - fmt::print("USB MSC initialization\n"); - // register the callback for the storage mount changed event. - const tinyusb_msc_sdmmc_config_t config_sdmmc = { - .card = card, - .callback_mount_changed = storage_mount_changed_cb, - .mount_config = { - .max_files = 5, - } - }; - ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); - ESP_ERROR_CHECK(tinyusb_msc_register_callback(TINYUSB_MSC_EVENT_MOUNT_CHANGED, storage_mount_changed_cb)); - - // initialize the tinyusb stack - fmt::print("USB MSC initialization\n"); - const tinyusb_config_t tusb_cfg = { - .device_descriptor = &descriptor_config, - .string_descriptor = string_desc_arr, - .string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), - .external_phy = false, - .configuration_descriptor = desc_configuration, - }; - ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); - fmt::print("USB MSC initialization DONE\n"); - usb_enabled_ = true; -} - -void usb_deinit() { - if (!usb_enabled_) { - return; - } - fmt::print("USB MSC deinitialization\n"); - auto err = tinyusb_driver_uninstall(); - if (err != ESP_OK) { - fmt::print("tinyusb_driver_uninstall failed: {}\n", esp_err_to_name(err)); - } - usb_enabled_ = false; - // and reconnect the CDC port, see: - // https://github.com/espressif/idf-extra-components/pull/229 - usb_phy_config_t phy_conf = { - // NOTE: for some reason, USB_PHY_CTRL_SERIAL_JTAG is not defined in the SDK - // for the ESP32s3 - .controller = (usb_phy_controller_t)1, - }; - usb_new_phy(&phy_conf, &jtag_phy); -} diff --git a/components/box-emu-hal/src/video_setting.cpp b/components/box-emu-hal/src/video_setting.cpp deleted file mode 100644 index d4b59a3..0000000 --- a/components/box-emu-hal/src/video_setting.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "box_emu_hal.hpp" - -static std::atomic video_setting_{VideoSetting::FIT}; - -VideoSetting hal::get_video_setting() { - return video_setting_; -} - -void hal::set_video_setting(const VideoSetting& setting) { - video_setting_ = setting; -} diff --git a/components/box-emu-hal/src/video_task.cpp b/components/box-emu-hal/src/video_task.cpp deleted file mode 100644 index e0f5320..0000000 --- a/components/box-emu-hal/src/video_task.cpp +++ /dev/null @@ -1,183 +0,0 @@ -#include "box_emu_hal.hpp" - -using namespace hal; - -static std::shared_ptr video_task_; -static QueueHandle_t video_queue_; - -static size_t display_width = lcd_width; -static size_t display_height = lcd_height; - -static size_t native_width = lcd_width; -static size_t native_height = lcd_height; -static int native_pitch = lcd_width; - -static const uint16_t* palette = nullptr; -static size_t palette_size = 256; - -static bool video_task(std::mutex &m, std::condition_variable& cv); - -void hal::init_video_task() { - static bool initialized = false; - if (initialized) { - return; - } - fmt::print("initializing video task...\n"); - video_queue_ = xQueueCreate(1, sizeof(uint16_t*)); - video_task_ = std::make_shared(espp::Task::Config{ - .name = "video task", - .callback = video_task, - .stack_size_bytes = 4*1024, - .priority = 20, - .core_id = 1 - }); - video_task_->start(); - initialized = true; -} - -void hal::set_display_size(size_t width, size_t height) { - display_width = width; - display_height = height; -} - -void hal::set_native_size(size_t width, size_t height, int pitch) { - native_width = width; - native_height = height; - native_pitch = pitch == -1 ? width : pitch; -} - -void hal::set_palette(const uint16_t* _palette, size_t size) { - palette = _palette; - palette_size = size; -} - -void hal::push_frame(const void* frame) { - if (video_queue_ == nullptr) { - fmt::print("video queue is null, make sure to call init_video_task() first\n"); - return; - } - xQueueSend(video_queue_, &frame, 10 / portTICK_PERIOD_MS); -} - -static bool has_palette() { - return palette != nullptr; -} - -static bool is_native() { - return native_width == display_width && native_height == display_height; -} - -static int get_x_offset() { - return (lcd_width-display_width)/2; -} - -static int get_y_offset() { - return (lcd_height-display_height)/2; -} - -static const uint16_t* get_palette() { - return palette; -} - -static bool video_task(std::mutex &m, std::condition_variable& cv) { - const void *_frame_ptr; - if (xQueuePeek(video_queue_, &_frame_ptr, 100 / portTICK_PERIOD_MS) != pdTRUE) { - // we couldn't get anything from the queue, return - return false; - } - if (_frame_ptr == nullptr) { - // make sure we clear the queue - xQueueReceive(video_queue_, &_frame_ptr, 10 / portTICK_PERIOD_MS); - // we got a nullptr, return - return false; - } - static constexpr int num_lines_to_write = NUM_ROWS_IN_FRAME_BUFFER; - static int vram_index = 0; // has to be static so that it persists between calls - const int x_offset = get_x_offset(); - const int y_offset = get_y_offset(); - const uint16_t* _palette = get_palette(); - if (is_native()) { - for (int y=0; y(num_lines_to_write, display_height-y); - if (has_palette()) { - const uint8_t* _frame = (const uint8_t*)_frame_ptr; - for (int i=0; i(x_scale * native_width, 0, lcd_width); - for (int y=0; y= max_y) { - break; - } - int source_y = (float)_y * inv_y_scale; - // shoudl i put this around the outer loop or is this loop a good - // balance for perfomance of the check? - if (has_palette()) { - const uint8_t* _frame = (const uint8_t*)_frame_ptr; - // write two pixels (32 bits) at a time because it's faster - for (int x=0; x + +extern "C" uint16_t make_color(uint8_t r, uint8_t g, uint8_t b) { + return lv_color_make(r,g,b).full; +} diff --git a/components/codec/CMakeLists.txt b/components/codec/CMakeLists.txt deleted file mode 100644 index 57e760f..0000000 --- a/components/codec/CMakeLists.txt +++ /dev/null @@ -1,13 +0,0 @@ -idf_component_register( - SRC_DIRS - "es7210" - "es8311" - "es8388" - INCLUDE_DIRS - "es7210" - "es8311" - "es8388" - "include" - REQUIRES - driver - ) diff --git a/components/codec/es7210/es7210.cpp b/components/codec/es7210/es7210.cpp deleted file mode 100644 index 478fda1..0000000 --- a/components/codec/es7210/es7210.cpp +++ /dev/null @@ -1,537 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2021 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include "esp_log.h" -#include "es7210.hpp" - -static write_fn i2c_write_ = nullptr; -static read_register_fn i2c_read_register_ = nullptr; - -void set_es7210_write(write_fn fn) { - i2c_write_ = fn; -} -void set_es7210_read(read_register_fn fn) { - i2c_read_register_ = fn; -} - -#define I2S_DSP_MODE_A 0 -#define MCLK_DIV_FRE 256 - -/* ES7210 address*/ -#define ES7210_ADDR ES7210_AD1_AD0_00 -#define ES7210_MCLK_SOURCE FROM_CLOCK_DOUBLE_PIN /* In master mode, 0 : MCLK from pad 1 : MCLK from clock doubler */ -#define FROM_PAD_PIN 0 -#define FROM_CLOCK_DOUBLE_PIN 1 - -/* - * Clock coefficient structer - */ -struct _coeff_div { - uint32_t mclk; /* mclk frequency */ - uint32_t lrck; /* lrck */ - uint8_t ss_ds; - uint8_t adc_div; /* adcclk divider */ - uint8_t dll; /* dll_bypass */ - uint8_t doubler; /* doubler enable */ - uint8_t osr; /* adc osr */ - uint8_t mclk_src; /* select mclk source */ - uint32_t lrck_h; /* The high 4 bits of lrck */ - uint32_t lrck_l; /* The low 8 bits of lrck */ -}; - -static const char *TAG = "ES7210"; -static es7210_input_mics_t mic_select = (es7210_input_mics_t)(ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4); - -/* Codec hifi mclk clock divider coefficients - * MEMBER REG - * mclk: 0x03 - * lrck: standard - * ss_ds: -- - * adc_div: 0x02 - * dll: 0x06 - * doubler: 0x02 - * osr: 0x07 - * mclk_src: 0x03 - * lrckh: 0x04 - * lrckl: 0x05 -*/ -static const struct _coeff_div coeff_div[] = { - //mclk lrck ss_ds adc_div dll doubler osr mclk_src lrckh lrckl - /* 8k */ - {12288000, 8000 , 0x00, 0x03, 0x01, 0x00, 0x20, 0x00, 0x06, 0x00}, - {16384000, 8000 , 0x00, 0x04, 0x01, 0x00, 0x20, 0x00, 0x08, 0x00}, - {19200000, 8000 , 0x00, 0x1e, 0x00, 0x01, 0x28, 0x00, 0x09, 0x60}, - {4096000, 8000 , 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, - - /* 11.025k */ - {11289600, 11025, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, - - /* 12k */ - {12288000, 12000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, - {19200000, 12000, 0x00, 0x14, 0x00, 0x01, 0x28, 0x00, 0x06, 0x40}, - - /* 16k */ - {4096000, 16000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, - {19200000, 16000, 0x00, 0x0a, 0x00, 0x00, 0x1e, 0x00, 0x04, 0x80}, - {16384000, 16000, 0x00, 0x02, 0x01, 0x00, 0x20, 0x00, 0x04, 0x00}, - {12288000, 16000, 0x00, 0x03, 0x01, 0x01, 0x20, 0x00, 0x03, 0x00}, - - /* 22.05k */ - {11289600, 22050, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, - - /* 24k */ - {12288000, 24000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, - {19200000, 24000, 0x00, 0x0a, 0x00, 0x01, 0x28, 0x00, 0x03, 0x20}, - - /* 32k */ - {12288000, 32000, 0x00, 0x03, 0x00, 0x00, 0x20, 0x00, 0x01, 0x80}, - {16384000, 32000, 0x00, 0x01, 0x01, 0x00, 0x20, 0x00, 0x02, 0x00}, - {19200000, 32000, 0x00, 0x05, 0x00, 0x00, 0x1e, 0x00, 0x02, 0x58}, - - /* 44.1k */ - {11289600, 44100, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, - - /* 48k */ - {12288000, 48000, 0x00, 0x01, 0x01, 0x01, 0x20, 0x00, 0x01, 0x00}, - {19200000, 48000, 0x00, 0x05, 0x00, 0x01, 0x28, 0x00, 0x01, 0x90}, - - /* 64k */ - {16384000, 64000, 0x01, 0x01, 0x01, 0x00, 0x20, 0x00, 0x01, 0x00}, - {19200000, 64000, 0x00, 0x05, 0x00, 0x01, 0x1e, 0x00, 0x01, 0x2c}, - - /* 88.2k */ - {11289600, 88200, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, - - /* 96k */ - {12288000, 96000, 0x01, 0x01, 0x01, 0x01, 0x20, 0x00, 0x00, 0x80}, - {19200000, 96000, 0x01, 0x05, 0x00, 0x01, 0x28, 0x00, 0x00, 0xc8}, -}; - -static esp_err_t es7210_write_reg(uint8_t reg_addr, uint8_t data) -{ - uint8_t write_buf[2] = {reg_addr, data}; - return i2c_write_(ES7210_ADDR, write_buf, sizeof(write_buf)) ? ESP_OK : ESP_FAIL; -} - -static esp_err_t es7210_update_reg_bit(uint8_t reg_addr, uint8_t update_bits, uint8_t data) -{ - uint8_t regv; - regv = es7210_read_reg(reg_addr); - regv = (regv & (~update_bits)) | (update_bits & data); - return es7210_write_reg(reg_addr, regv); -} - -static int get_coeff(uint32_t mclk, uint32_t lrck) -{ - for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { - if (coeff_div[i].lrck == lrck && coeff_div[i].mclk == mclk) - return i; - } - return -1; -} - -int8_t get_es7210_mclk_src(void) -{ - return ES7210_MCLK_SOURCE; -} - -int es7210_read_reg(uint8_t reg_addr) -{ - uint8_t data; - i2c_read_register_(ES7210_ADDR, reg_addr, &data, 1); - return (int)data; -} - -esp_err_t es7210_config_sample(audio_hal_iface_samples_t sample) -{ - uint8_t regv; - int coeff; - int sample_fre = 0; - int mclk_fre = 0; - esp_err_t ret = ESP_OK; - switch (sample) { - case AUDIO_HAL_08K_SAMPLES: - sample_fre = 8000; - break; - case AUDIO_HAL_11K_SAMPLES: - sample_fre = 11025; - break; - case AUDIO_HAL_16K_SAMPLES: - sample_fre = 16000; - break; - case AUDIO_HAL_22K_SAMPLES: - sample_fre = 22050; - break; - case AUDIO_HAL_24K_SAMPLES: - sample_fre = 24000; - break; - case AUDIO_HAL_32K_SAMPLES: - sample_fre = 32000; - break; - case AUDIO_HAL_44K_SAMPLES: - sample_fre = 44100; - break; - case AUDIO_HAL_48K_SAMPLES: - sample_fre = 48000; - break; - default: - ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre); - break; - } - mclk_fre = sample_fre * MCLK_DIV_FRE; - coeff = get_coeff(mclk_fre, sample_fre); - if (coeff < 0) { - ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre); - return ESP_FAIL; - } - /* Set clock parammeters */ - if (coeff >= 0) { - /* Set adc_div & doubler & dll */ - regv = es7210_read_reg(ES7210_MAINCLK_REG02) & 0x00; - regv |= coeff_div[coeff].adc_div; - regv |= coeff_div[coeff].doubler << 6; - regv |= coeff_div[coeff].dll << 7; - ret |= es7210_write_reg(ES7210_MAINCLK_REG02, regv); - /* Set osr */ - regv = coeff_div[coeff].osr; - ret |= es7210_write_reg(ES7210_OSR_REG07, regv); - /* Set lrck */ - regv = coeff_div[coeff].lrck_h; - ret |= es7210_write_reg(ES7210_LRCK_DIVH_REG04, regv); - regv = coeff_div[coeff].lrck_l; - ret |= es7210_write_reg(ES7210_LRCK_DIVL_REG05, regv); - } - return ret; -} - -esp_err_t es7210_mic_select(es7210_input_mics_t mic) -{ - esp_err_t ret = ESP_OK; - mic_select = mic; - if (mic_select & (ES7210_INPUT_MIC1 | ES7210_INPUT_MIC2 | ES7210_INPUT_MIC3 | ES7210_INPUT_MIC4)) { - for (int i = 0; i < 4; i++) { - ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43 + i, 0x10, 0x00); - } - ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0xff); - ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); - if (mic_select & ES7210_INPUT_MIC1) { - ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC1"); - ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); - ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); - ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x10, 0x10); - } - if (mic_select & ES7210_INPUT_MIC2) { - ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC2"); - ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x0b, 0x00); - ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B, 0x00); - ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x10, 0x10); - } - if (mic_select & ES7210_INPUT_MIC3) { - ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC3"); - ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); - ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); - ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x10, 0x10); - } - if (mic_select & ES7210_INPUT_MIC4) { - ESP_LOGI(TAG, "Enable ES7210_INPUT_MIC4"); - ret |= es7210_update_reg_bit(ES7210_CLOCK_OFF_REG01, 0x15, 0x00); - ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0x00); - ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x10, 0x10); - } - } else { - ESP_LOGE(TAG, "Microphone selection error"); - return ESP_FAIL; - } - return ret; -} - -esp_err_t es7210_adc_init(audio_hal_codec_config_t *codec_cfg) -{ - esp_err_t ret = ESP_OK; - - ret |= es7210_write_reg(ES7210_RESET_REG00, 0xff); - ret |= es7210_write_reg(ES7210_RESET_REG00, 0x41); - ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x1f); - ret |= es7210_write_reg(ES7210_TIME_CONTROL0_REG09, 0x30); /* Set chip state cycle */ - ret |= es7210_write_reg(ES7210_TIME_CONTROL1_REG0A, 0x30); /* Set power on state cycle */ - // ret |= es7210_write_reg(ES7210_ADC12_HPF2_REG23, 0x2a); /* Quick setup */ - // ret |= es7210_write_reg(ES7210_ADC12_HPF1_REG22, 0x0a); - // ret |= es7210_write_reg(ES7210_ADC34_HPF2_REG20, 0x0a); - // ret |= es7210_write_reg(ES7210_ADC34_HPF1_REG21, 0x2a); - /* Set master/slave audio interface */ - audio_hal_codec_i2s_iface_t *i2s_cfg = & (codec_cfg->i2s_iface); - switch (i2s_cfg->mode) { - case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */ - ESP_LOGI(TAG, "ES7210 in Master mode"); - // ret |= es7210_update_reg_bit(ES7210_MODE_CONFIG_REG08, 0x01, 0x01); - ret |= es7210_write_reg(ES7210_MODE_CONFIG_REG08, 0x20); - /* Select clock source for internal mclk */ - switch (get_es7210_mclk_src()) { - case FROM_PAD_PIN: - ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); - break; - case FROM_CLOCK_DOUBLE_PIN: - ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x80); - break; - default: - ret |= es7210_update_reg_bit(ES7210_MASTER_CLK_REG03, 0x80, 0x00); - break; - } - break; - case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */ - ESP_LOGI(TAG, "ES7210 in Slave mode"); - break; - default: - break; - } - ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xC3); /* Select power off analog, vdda = 3.3V, close vx20ff, VMID select 5KΩ start */ - ret |= es7210_write_reg(ES7210_MIC12_BIAS_REG41, 0x70); /* Select 2.87v */ - ret |= es7210_write_reg(ES7210_MIC34_BIAS_REG42, 0x70); /* Select 2.87v */ - ret |= es7210_write_reg(ES7210_OSR_REG07, 0x20); - ret |= es7210_write_reg(ES7210_MAINCLK_REG02, 0xc1); /* Set the frequency division coefficient and use dll except clock doubler, and need to set 0xc1 to clear the state */ - ret |= es7210_config_sample(i2s_cfg->samples); - ret |= es7210_mic_select(mic_select); - ret |= es7210_adc_set_gain_all(GAIN_0DB); - return ESP_OK; -} - -esp_err_t es7210_adc_deinit() -{ - return ESP_OK; -} - -esp_err_t es7210_config_fmt(audio_hal_iface_format_t fmt) -{ - esp_err_t ret = ESP_OK; - uint8_t adc_iface = 0; - adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); - adc_iface &= 0xfc; - switch (fmt) { - case AUDIO_HAL_I2S_NORMAL: - ESP_LOGD(TAG, "ES7210 in I2S Format"); - adc_iface |= 0x00; - break; - case AUDIO_HAL_I2S_LEFT: - case AUDIO_HAL_I2S_RIGHT: - ESP_LOGD(TAG, "ES7210 in LJ Format"); - adc_iface |= 0x01; - break; - case AUDIO_HAL_I2S_DSP: - if (I2S_DSP_MODE_A) { - ESP_LOGD(TAG, "ES7210 in DSP-A Format"); - adc_iface |= 0x03; - } else { - ESP_LOGD(TAG, "ES7210 in DSP-B Format"); - adc_iface |= 0x13; - } - break; - default: - adc_iface &= 0xfc; - break; - } - ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); - return ret; -} - -esp_err_t es7210_set_bits(audio_hal_iface_bits_t bits) -{ - esp_err_t ret = ESP_OK; - uint8_t adc_iface = 0; - adc_iface = es7210_read_reg(ES7210_SDP_INTERFACE1_REG11); - adc_iface &= 0x1f; - switch (bits) { - case AUDIO_HAL_BIT_LENGTH_16BITS: - adc_iface |= 0x60; - break; - case AUDIO_HAL_BIT_LENGTH_24BITS: - adc_iface |= 0x00; - break; - case AUDIO_HAL_BIT_LENGTH_32BITS: - adc_iface |= 0x80; - break; - default: - adc_iface |= 0x60; - break; - } - ret |= es7210_write_reg(ES7210_SDP_INTERFACE1_REG11, adc_iface); - return ret; -} - -esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) -{ - esp_err_t ret = ESP_OK; - ret |= es7210_set_bits(iface->bits); - ret |= es7210_config_fmt(iface->fmt); - ret |= es7210_config_sample(iface->samples); - return ret; -} - -esp_err_t es7210_start(uint8_t clock_reg_value) -{ - esp_err_t ret = ESP_OK; - ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, clock_reg_value); - ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x00); - // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0x40); - ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0x00); - ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0x00); - ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0x00); - ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0x00); - ret |= es7210_mic_select(mic_select); - return ret; -} - -esp_err_t es7210_stop(void) -{ - esp_err_t ret = ESP_OK; - ret |= es7210_write_reg(ES7210_MIC1_POWER_REG47, 0xff); - ret |= es7210_write_reg(ES7210_MIC2_POWER_REG48, 0xff); - ret |= es7210_write_reg(ES7210_MIC3_POWER_REG49, 0xff); - ret |= es7210_write_reg(ES7210_MIC4_POWER_REG4A, 0xff); - ret |= es7210_write_reg(ES7210_MIC12_POWER_REG4B,0xff); - ret |= es7210_write_reg(ES7210_MIC34_POWER_REG4C, 0xff); - // ret |= es7210_write_reg(ES7210_ANALOG_REG40, 0xc0); - ret |= es7210_write_reg(ES7210_CLOCK_OFF_REG01, 0x7f); - ret |= es7210_write_reg(ES7210_POWER_DOWN_REG06, 0x07); - return ret; -} - -esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) -{ - static uint8_t regv; - esp_err_t ret = ESP_OK; - // ESP_LOGW(TAG, "ES7210 only supports ADC mode"); - ret = es7210_read_reg(ES7210_CLOCK_OFF_REG01); - if ((ret != 0x7f) && (ret != 0xff)) { - regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); - } - if (ctrl_state == AUDIO_HAL_CTRL_START) { - ESP_LOGI(TAG, "The ES7210_CLOCK_OFF_REG01 value before stop is %x",regv); - ret |= es7210_start(regv); - } else { - ESP_LOGW(TAG, "The codec is about to stop"); - regv = es7210_read_reg(ES7210_CLOCK_OFF_REG01); - ret |= es7210_stop(); - } - return ESP_OK; -} - -esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain) -{ - esp_err_t ret_val = ESP_OK; - - if (gain < GAIN_0DB) { - gain = GAIN_0DB; - } - - if (gain > GAIN_37_5DB) { - gain = GAIN_37_5DB; - } - - if (mic_mask & ES7210_INPUT_MIC1) { - ret_val |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); - } - if (mic_mask & ES7210_INPUT_MIC2) { - ret_val |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); - } - if (mic_mask & ES7210_INPUT_MIC3) { - ret_val |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); - } - if (mic_mask & ES7210_INPUT_MIC4) { - ret_val |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); - } - - return ret_val; -} - -esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain) -{ - esp_err_t ret = ESP_OK; - uint32_t max_gain_vaule = 14; - if (gain < 0) { - gain = (es7210_gain_value_t)0; - } else if (gain > max_gain_vaule) { - gain = (es7210_gain_value_t)max_gain_vaule; - } - ESP_LOGD(TAG, "SET: gain:%d", gain); - if (mic_select & ES7210_INPUT_MIC1) { - ret |= es7210_update_reg_bit(ES7210_MIC1_GAIN_REG43, 0x0f, gain); - } - if (mic_select & ES7210_INPUT_MIC2) { - ret |= es7210_update_reg_bit(ES7210_MIC2_GAIN_REG44, 0x0f, gain); - } - if (mic_select & ES7210_INPUT_MIC3) { - ret |= es7210_update_reg_bit(ES7210_MIC3_GAIN_REG45, 0x0f, gain); - } - if (mic_select & ES7210_INPUT_MIC4) { - ret |= es7210_update_reg_bit(ES7210_MIC4_GAIN_REG46, 0x0f, gain); - } - return ret; -} - -esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain) -{ - int regv = 0; - uint8_t gain_value; - if (mic_mask & ES7210_INPUT_MIC1) { - regv = es7210_read_reg(ES7210_MIC1_GAIN_REG43); - } else if (mic_mask & ES7210_INPUT_MIC2) { - regv = es7210_read_reg(ES7210_MIC2_GAIN_REG44); - } else if (mic_mask & ES7210_INPUT_MIC3) { - regv = es7210_read_reg(ES7210_MIC3_GAIN_REG45); - } else if (mic_mask & ES7210_INPUT_MIC4) { - regv = es7210_read_reg(ES7210_MIC4_GAIN_REG46); - } else { - ESP_LOGE(TAG, "No MIC selected"); - return ESP_FAIL; - } - if (regv == ESP_FAIL) { - return regv; - } - gain_value = (regv & 0x0f); /* Retain the last four bits for gain */ - *gain = (es7210_gain_value_t)gain_value; - ESP_LOGI(TAG, "GET: gain_value:%d", gain_value); - return ESP_OK; -} - -esp_err_t es7210_adc_set_volume(int volume) -{ - esp_err_t ret = ESP_OK; - ESP_LOGD(TAG, "ADC can adjust gain"); - return ret; -} - -esp_err_t es7210_set_mute(uint8_t enable) -{ - ESP_LOGD(TAG, "ES7210 SetMute :%d", enable); - return ESP_OK; -} - -void es7210_read_all(void) -{ - for (int i = 0; i <= 0x4E; i++) { - uint8_t reg = es7210_read_reg(i); - printf("REG:%02x, %02x\n", reg, i); - } -} diff --git a/components/codec/es7210/es7210.hpp b/components/codec/es7210/es7210.hpp deleted file mode 100644 index f01b83c..0000000 --- a/components/codec/es7210/es7210.hpp +++ /dev/null @@ -1,262 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2021 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef _ES7210_H -#define _ES7210_H - -#include - -#include "audio_hal.h" - -typedef std::function write_fn; -typedef std::function read_register_fn; - -void set_es7210_write(write_fn fn); -void set_es7210_read(read_register_fn fn); - -#ifdef __cplusplus -extern "C" { -#endif - -#define ES7210_RESET_REG00 0x00 /* Reset control */ -#define ES7210_CLOCK_OFF_REG01 0x01 /* Used to turn off the ADC clock */ -#define ES7210_MAINCLK_REG02 0x02 /* Set ADC clock frequency division */ -#define ES7210_MASTER_CLK_REG03 0x03 /* MCLK source $ SCLK division */ -#define ES7210_LRCK_DIVH_REG04 0x04 /* lrck_divh */ -#define ES7210_LRCK_DIVL_REG05 0x05 /* lrck_divl */ -#define ES7210_POWER_DOWN_REG06 0x06 /* power down */ -#define ES7210_OSR_REG07 0x07 -#define ES7210_MODE_CONFIG_REG08 0x08 /* Set master/slave & channels */ -#define ES7210_TIME_CONTROL0_REG09 0x09 /* Set Chip intial state period*/ -#define ES7210_TIME_CONTROL1_REG0A 0x0A /* Set Power up state period */ -#define ES7210_SDP_INTERFACE1_REG11 0x11 /* Set sample & fmt */ -#define ES7210_SDP_INTERFACE2_REG12 0x12 /* Pins state */ -#define ES7210_ADC_AUTOMUTE_REG13 0x13 /* Set mute */ -#define ES7210_ADC34_MUTERANGE_REG14 0x14 /* Set mute range */ -#define ES7210_ADC34_HPF2_REG20 0x20 /* HPF */ -#define ES7210_ADC34_HPF1_REG21 0x21 -#define ES7210_ADC12_HPF1_REG22 0x22 -#define ES7210_ADC12_HPF2_REG23 0x23 -#define ES7210_ANALOG_REG40 0x40 /* ANALOG Power */ -#define ES7210_MIC12_BIAS_REG41 0x41 -#define ES7210_MIC34_BIAS_REG42 0x42 -#define ES7210_MIC1_GAIN_REG43 0x43 -#define ES7210_MIC2_GAIN_REG44 0x44 -#define ES7210_MIC3_GAIN_REG45 0x45 -#define ES7210_MIC4_GAIN_REG46 0x46 -#define ES7210_MIC1_POWER_REG47 0x47 -#define ES7210_MIC2_POWER_REG48 0x48 -#define ES7210_MIC3_POWER_REG49 0x49 -#define ES7210_MIC4_POWER_REG4A 0x4A -#define ES7210_MIC12_POWER_REG4B 0x4B /* MICBias & ADC & PGA Power */ -#define ES7210_MIC34_POWER_REG4C 0x4C - -typedef enum { - ES7210_AD1_AD0_00 = 0x40, - ES7210_AD1_AD0_01 = 0x41, - ES7210_AD1_AD0_10 = 0x42, - ES7210_AD1_AD0_11 = 0x43, -} es7210_address_t; - -typedef enum { - ES7210_INPUT_MIC1 = 0x01, - ES7210_INPUT_MIC2 = 0x02, - ES7210_INPUT_MIC3 = 0x04, - ES7210_INPUT_MIC4 = 0x08 -} es7210_input_mics_t; - -typedef enum gain_value{ - GAIN_0DB = 0, - GAIN_3DB, - GAIN_6DB, - GAIN_9DB, - GAIN_12DB, - GAIN_15DB, - GAIN_18DB, - GAIN_21DB, - GAIN_24DB, - GAIN_27DB, - GAIN_30DB, - GAIN_33DB, - GAIN_34_5DB, - GAIN_36DB, - GAIN_37_5DB, -} es7210_gain_value_t; - -/* - * @brief Initialize ES7210 ADC chip - * - * @param[in] codec_cfg: configuration of ES7210 - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es7210_adc_init(audio_hal_codec_config_t *codec_cfg); - -/** - * @brief Deinitialize ES7210 ADC chip - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es7210_adc_deinit(); - -/** - * @brief Configure ES7210 ADC mode and I2S interface - * - * @param[in] mode: codec mode - * @param[in] iface: I2S config - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es7210_adc_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); - -/** - * @brief Control ES7210 ADC chip - * - * @param[in] mode: codec mode - * @param[in] ctrl_state: start or stop progress - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es7210_adc_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); - -/** - * @brief Set gain of given mask - * - * @param[in] mic_mask Mask of MIC channel - * - * @param[in] gain: gain - * - * gain : value - * GAIN_0DB : 1 - * GAIN_3DB : 2 - * GAIN_6DB : 3 - * · - * · - * · - * GAIN_30DB : 10 - * GAIN_33DB : 11 - * GAIN_34_5DB : 12 - * GAIN_36DB : 13 - * GAIN_37_5DB : 14 - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es7210_adc_set_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t gain); - -/** - * @brief Set gain (Note: the enabled microphone sets the same gain) - * - * @param[in] gain: gain - * - * gain : value - * GAIN_0DB : 1 - * GAIN_3DB : 2 - * GAIN_6DB : 3 - * · - * · - * · - * GAIN_30DB : 10 - * GAIN_33DB : 11 - * GAIN_34_5DB : 12 - * GAIN_36DB : 13 - * GAIN_37_5DB : 14 - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es7210_adc_set_gain_all(es7210_gain_value_t gain); - -/** - * @brief Get MIC gain - * - * @param mic_mask Selected MIC - * @param gain Pointer to `es7210_gain_value_t` - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es7210_adc_get_gain(es7210_input_mics_t mic_mask, es7210_gain_value_t *gain); - -/** - * @brief Set volume - * - * @param[in] volume: volume - * - * @return - * - ESP_OK - */ -esp_err_t es7210_adc_set_volume(int volume); - -/** - * @brief Set ES7210 ADC mute status - * - * @return - * - ESP_FAIL - * - ESP_OK - */ -esp_err_t es7210_set_mute(uint8_t enable); - -/** - * @brief Select ES7210 mic - * - * @param[in] mic: mics - * - * @return - * - ESP_FAIL - * - ESP_OK - */ -esp_err_t es7210_mic_select(es7210_input_mics_t mic); - -/** - * @brief Read regs of ES7210 - * - * @param[in] reg_addr: reg_addr - * - * @return - * - ESP_FAIL - * - ESP_OK - */ -int es7210_read_reg(uint8_t reg_addr); - -/** - * @brief Read all regs of ES7210 - */ -void es7210_read_all(void); - -#ifdef __cplusplus -} -#endif - -#endif /* _ES7210_H_ */ diff --git a/components/codec/es8311/es8311.cpp b/components/codec/es8311/es8311.cpp deleted file mode 100644 index 5470d7b..0000000 --- a/components/codec/es8311/es8311.cpp +++ /dev/null @@ -1,682 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2019 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include "esp_log.h" -#include "es8311.hpp" - -static write_fn i2c_write_ = nullptr; -static read_register_fn i2c_read_register_ = nullptr; - -void set_es8311_write(write_fn fn) { - i2c_write_ = fn; -} -void set_es8311_read(read_register_fn fn) { - i2c_read_register_ = fn; -} - -#if !defined(BIT) -#define BIT(x) (1U << (x)) -#endif - -/* ES8311 address - * 0x32:CE=1;0x30:CE=0 - */ -#define ES8311_ADDR 0x18 - -/* - * to define the clock soure of MCLK - */ -#define FROM_MCLK_PIN 0 -#define FROM_SCLK_PIN 1 -#define ES8311_MCLK_SOURCE FROM_MCLK_PIN - -/* - * to define whether to reverse the clock - */ -#define INVERT_MCLK 0 // do not invert -#define INVERT_SCLK 0 - -#define IS_DMIC 0 // Is it a digital microphone - -#define MCLK_DIV_FRE 256 - -/* - * Clock coefficient structer - */ -struct _coeff_div { - uint32_t mclk; /* mclk frequency */ - uint32_t rate; /* sample rate */ - uint8_t pre_div; /* the pre divider with range from 1 to 8 */ - uint8_t pre_multi; /* the pre multiplier with x1, x2, x4 and x8 selection */ - uint8_t adc_div; /* adcclk divider */ - uint8_t dac_div; /* dacclk divider */ - uint8_t fs_mode; /* double speed or single speed, =0, ss, =1, ds */ - uint8_t lrck_h; /* adclrck divider and daclrck divider */ - uint8_t lrck_l; - uint8_t bclk_div; /* sclk divider */ - uint8_t adc_osr; /* adc osr */ - uint8_t dac_osr; /* dac osr */ -}; - -/* codec hifi mclk clock divider coefficients */ -static const struct _coeff_div coeff_div[] = { - //mclk rate pre_div mult adc_div dac_div fs_mode lrch lrcl bckdiv osr - /* 8k */ - {12288000, 8000 , 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 8000 , 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x10}, - {16384000, 8000 , 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {8192000 , 8000 , 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 8000 , 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {4096000 , 8000 , 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2048000 , 8000 , 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 8000 , 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1024000 , 8000 , 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 11.025k */ - {11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {5644800 , 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2822400 , 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1411200 , 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 12k */ - {12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 16k */ - {12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, - {16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {8192000 , 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {4096000 , 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2048000 , 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1024000 , 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 22.05k */ - {11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {5644800 , 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2822400 , 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1411200 , 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 24k */ - {12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 32k */ - {12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10}, - {16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {8192000 , 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {4096000 , 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2048000 , 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, - {1024000 , 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 44.1k */ - {11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {5644800 , 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2822400 , 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1411200 , 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 48k */ - {12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - - /* 64k */ - {12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, - {16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {8192000 , 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, - {4096000 , 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10}, - {2048000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18}, - {1024000 , 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, - - /* 88.2k */ - {11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {5644800 , 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {2822400 , 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1411200 , 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, - - /* 96k */ - {12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {6144000 , 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {3072000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10}, - {1536000 , 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10}, -}; - -static const char *TAG = "DRV8311"; - -#define ES_ASSERT(a, format, b, ...) \ - if ((a) != 0) { \ - ESP_LOGE(TAG, format, ##__VA_ARGS__); \ - return b;\ - } - -static esp_err_t es8311_write_reg(uint8_t reg_addr, uint8_t data) -{ - // return i2c_bus_write_byte(i2c_handle, reg_addr, data); - uint8_t write_buf[2] = {reg_addr, data}; - bool success = i2c_write_(ES8311_ADDR, write_buf, sizeof(write_buf)); - return success ? ESP_OK : ESP_FAIL; -} - -static int es8311_read_reg(uint8_t reg_addr) -{ - uint8_t data; - i2c_read_register_(ES8311_ADDR, reg_addr, &data, 1); - return (int)data; -} - -/* -* look for the coefficient in coeff_div[] table -*/ -static int get_coeff(uint32_t mclk, uint32_t rate) -{ - for (int i = 0; i < (sizeof(coeff_div) / sizeof(coeff_div[0])); i++) { - if (coeff_div[i].rate == rate && coeff_div[i].mclk == mclk) - return i; - } - return -1; -} - -/* -* set es8311 dac mute or not -* if mute = 0, dac un-mute -* if mute = 1, dac mute -*/ -static void es8311_mute(int mute) -{ - uint8_t regv; - regv = es8311_read_reg(ES8311_DAC_REG31) & 0x9f; - if (mute) { - es8311_write_reg(ES8311_SYSTEM_REG12, 0x02); - es8311_write_reg(ES8311_DAC_REG31, regv | 0x60); - es8311_write_reg(ES8311_DAC_REG32, 0x00); - es8311_write_reg(ES8311_DAC_REG37, 0x08); - } else { - es8311_write_reg(ES8311_DAC_REG31, regv); - es8311_write_reg(ES8311_SYSTEM_REG12, 0x00); - } -} - -/* -* set es8311 into suspend mode -*/ -static void es8311_suspend(void) -{ - ESP_LOGI(TAG, "Enter into es8311_suspend()"); - es8311_write_reg(ES8311_DAC_REG32, 0x00); - es8311_write_reg(ES8311_ADC_REG17, 0x00); - es8311_write_reg(ES8311_SYSTEM_REG0E, 0xFF); - es8311_write_reg(ES8311_SYSTEM_REG12, 0x02); - es8311_write_reg(ES8311_SYSTEM_REG14, 0x00); - es8311_write_reg(ES8311_SYSTEM_REG0D, 0xFA); - es8311_write_reg(ES8311_ADC_REG15, 0x00); - es8311_write_reg(ES8311_DAC_REG37, 0x08); - es8311_write_reg(ES8311_GP_REG45, 0x01); -} - -esp_err_t es8311_codec_init(audio_hal_codec_config_t *codec_cfg) -{ - uint8_t datmp, regv; - int coeff; - esp_err_t ret = ESP_OK; - - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, 0x30); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, 0x00); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG03, 0x10); - ret |= es8311_write_reg(ES8311_ADC_REG16, 0x24); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG04, 0x10); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG05, 0x00); - ret |= es8311_write_reg(ES8311_SYSTEM_REG0B, 0x00); - ret |= es8311_write_reg(ES8311_SYSTEM_REG0C, 0x00); - ret |= es8311_write_reg(ES8311_SYSTEM_REG10, 0x1F); - ret |= es8311_write_reg(ES8311_SYSTEM_REG11, 0x7F); - ret |= es8311_write_reg(ES8311_RESET_REG00, 0x80); - /* - * Set Codec into Master or Slave mode - */ - regv = es8311_read_reg(ES8311_RESET_REG00); - /* - * Set master/slave audio interface - */ - audio_hal_codec_i2s_iface_t *i2s_cfg = &(codec_cfg->i2s_iface); - switch (i2s_cfg->mode) { - case AUDIO_HAL_MODE_MASTER: /* MASTER MODE */ - ESP_LOGI(TAG, "ES8311 in Master mode"); - regv |= 0x40; - break; - case AUDIO_HAL_MODE_SLAVE: /* SLAVE MODE */ - ESP_LOGI(TAG, "ES8311 in Slave mode"); - regv &= 0xBF; - break; - default: - regv &= 0xBF; - } - ret |= es8311_write_reg(ES8311_RESET_REG00, regv); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, 0x3F); - /* - * Select clock source for internal mclk - */ - - int es8311_mclk_src = ES8311_MCLK_SOURCE; - switch (es8311_mclk_src) { - case FROM_MCLK_PIN: - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); - regv &= 0x7F; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); - break; - case FROM_SCLK_PIN: - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); - regv |= 0x80; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); - break; - default: - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); - regv &= 0x7F; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); - break; - } - int sample_fre = 0; - int mclk_fre = 0; - switch (i2s_cfg->samples) { - case AUDIO_HAL_08K_SAMPLES: - sample_fre = 8000; - break; - case AUDIO_HAL_11K_SAMPLES: - sample_fre = 11025; - break; - case AUDIO_HAL_16K_SAMPLES: - sample_fre = 16000; - break; - case AUDIO_HAL_22K_SAMPLES: - sample_fre = 22050; - break; - case AUDIO_HAL_24K_SAMPLES: - sample_fre = 24000; - break; - case AUDIO_HAL_32K_SAMPLES: - sample_fre = 32000; - break; - case AUDIO_HAL_44K_SAMPLES: - sample_fre = 44100; - break; - case AUDIO_HAL_48K_SAMPLES: - sample_fre = 48000; - break; - default: - ESP_LOGE(TAG, "Unable to configure sample rate %dHz", sample_fre); - break; - } - mclk_fre = sample_fre * MCLK_DIV_FRE; - coeff = get_coeff(mclk_fre, sample_fre); - if (coeff < 0) { - ESP_LOGE(TAG, "Unable to configure sample rate %dHz with %dHz MCLK", sample_fre, mclk_fre); - return ESP_FAIL; - } - /* - * Set clock parammeters - */ - if (coeff >= 0) { - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG02) & 0x07; - regv |= (coeff_div[coeff].pre_div - 1) << 5; - datmp = 0; - switch (coeff_div[coeff].pre_multi) { - case 1: - datmp = 0; - break; - case 2: - datmp = 1; - break; - case 4: - datmp = 2; - break; - case 8: - datmp = 3; - break; - default: - break; - } - - if (ES8311_MCLK_SOURCE == FROM_SCLK_PIN) { - datmp = 3; /* DIG_MCLK = LRCK * 256 = BCLK * 8 */ - } - regv |= (datmp) << 3; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG02, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG05) & 0x00; - regv |= (coeff_div[coeff].adc_div - 1) << 4; - regv |= (coeff_div[coeff].dac_div - 1) << 0; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG05, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG03) & 0x80; - regv |= coeff_div[coeff].fs_mode << 6; - regv |= coeff_div[coeff].adc_osr << 0; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG03, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG04) & 0x80; - regv |= coeff_div[coeff].dac_osr << 0; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG04, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG07) & 0xC0; - regv |= coeff_div[coeff].lrck_h << 0; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG07, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG08) & 0x00; - regv |= coeff_div[coeff].lrck_l << 0; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG08, regv); - - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06) & 0xE0; - if (coeff_div[coeff].bclk_div < 19) { - regv |= (coeff_div[coeff].bclk_div - 1) << 0; - } else { - regv |= (coeff_div[coeff].bclk_div) << 0; - } - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); - } - - /* - * mclk inverted or not - */ - if (INVERT_MCLK) { - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); - regv |= 0x40; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); - } else { - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG01); - regv &= ~(0x40); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG01, regv); - } - /* - * sclk inverted or not - */ - if (INVERT_SCLK) { - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06); - regv |= 0x20; - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); - } else { - regv = es8311_read_reg(ES8311_CLK_MANAGER_REG06); - regv &= ~(0x20); - ret |= es8311_write_reg(ES8311_CLK_MANAGER_REG06, regv); - } - - ret |= es8311_write_reg(ES8311_SYSTEM_REG13, 0x10); - ret |= es8311_write_reg(ES8311_ADC_REG1B, 0x0A); - ret |= es8311_write_reg(ES8311_ADC_REG1C, 0x6A); - - return ESP_OK; -} - -esp_err_t es8311_codec_deinit() -{ - return ESP_OK; -} - -esp_err_t es8311_config_fmt(es_i2s_fmt_t fmt) -{ - esp_err_t ret = ESP_OK; - uint8_t adc_iface = 0, dac_iface = 0; - dac_iface = es8311_read_reg(ES8311_SDPIN_REG09); - adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A); - switch (fmt) { - case AUDIO_HAL_I2S_NORMAL: - ESP_LOGD(TAG, "ES8311 in I2S Format"); - dac_iface &= 0xFC; - adc_iface &= 0xFC; - break; - case AUDIO_HAL_I2S_LEFT: - case AUDIO_HAL_I2S_RIGHT: - ESP_LOGD(TAG, "ES8311 in LJ Format"); - adc_iface &= 0xFC; - dac_iface &= 0xFC; - adc_iface |= 0x01; - dac_iface |= 0x01; - break; - case AUDIO_HAL_I2S_DSP: - ESP_LOGD(TAG, "ES8311 in DSP-B Format"); - adc_iface &= 0xDC; - dac_iface &= 0xDC; - adc_iface |= 0x23; - dac_iface |= 0x23; - break; - default: - dac_iface &= 0xFC; - adc_iface &= 0xFC; - break; - } - ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); - ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); - - return ret; -} - -esp_err_t es8311_set_bits_per_sample(audio_hal_iface_bits_t bits) -{ - esp_err_t ret = ESP_OK; - uint8_t adc_iface = 0, dac_iface = 0; - dac_iface = es8311_read_reg(ES8311_SDPIN_REG09); - adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A); - switch (bits) { - case AUDIO_HAL_BIT_LENGTH_16BITS: - dac_iface |= 0x0c; - adc_iface |= 0x0c; - break; - case AUDIO_HAL_BIT_LENGTH_24BITS: - break; - case AUDIO_HAL_BIT_LENGTH_32BITS: - dac_iface |= 0x10; - adc_iface |= 0x10; - break; - default: - dac_iface |= 0x0c; - adc_iface |= 0x0c; - break; - - } - ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); - ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); - - return ret; -} - -esp_err_t es8311_codec_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) -{ - int ret = ESP_OK; - ret |= es8311_set_bits_per_sample(iface->bits); - ret |= es8311_config_fmt((es_i2s_fmt_t)(iface->fmt)); - return ret; -} - -esp_err_t es8311_codec_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) -{ - esp_err_t ret = ESP_OK; - es_module_t es_mode = ES_MODULE_MIN; - - switch (mode) { - case AUDIO_HAL_CODEC_MODE_ENCODE: - es_mode = ES_MODULE_ADC; - break; - case AUDIO_HAL_CODEC_MODE_LINE_IN: - es_mode = ES_MODULE_LINE; - break; - case AUDIO_HAL_CODEC_MODE_DECODE: - es_mode = ES_MODULE_DAC; - break; - case AUDIO_HAL_CODEC_MODE_BOTH: - es_mode = ES_MODULE_ADC_DAC; - break; - default: - es_mode = ES_MODULE_DAC; - ESP_LOGW(TAG, "Codec mode not support, default is decode mode"); - break; - } - - if (ctrl_state == AUDIO_HAL_CTRL_START) { - ret |= es8311_start(es_mode); - } else { - ESP_LOGW(TAG, "The codec is about to stop"); - ret |= es8311_stop(es_mode); - } - - return ret; -} - -esp_err_t es8311_start(es_module_t mode) -{ - esp_err_t ret = ESP_OK; - uint8_t adc_iface = 0, dac_iface = 0; - - dac_iface = es8311_read_reg(ES8311_SDPIN_REG09) & 0xBF; - adc_iface = es8311_read_reg(ES8311_SDPOUT_REG0A) & 0xBF; - adc_iface |= BIT(6); - dac_iface |= BIT(6); - - if (mode == ES_MODULE_LINE) { - ESP_LOGE(TAG, "The codec es8311 doesn't support ES_MODULE_LINE mode"); - return ESP_FAIL; - } - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { - adc_iface &= ~(BIT(6)); - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { - dac_iface &= ~(BIT(6)); - } - - ret |= es8311_write_reg(ES8311_SDPIN_REG09, dac_iface); - ret |= es8311_write_reg(ES8311_SDPOUT_REG0A, adc_iface); - - ret |= es8311_write_reg(ES8311_ADC_REG17, 0xBF); - ret |= es8311_write_reg(ES8311_SYSTEM_REG0E, 0x02); - ret |= es8311_write_reg(ES8311_SYSTEM_REG12, 0x00); - ret |= es8311_write_reg(ES8311_SYSTEM_REG14, 0x1A); - - /* - * pdm dmic enable or disable - */ - int regv = 0; - if (IS_DMIC) { - regv = es8311_read_reg(ES8311_SYSTEM_REG14); - regv |= 0x40; - ret |= es8311_write_reg(ES8311_SYSTEM_REG14, regv); - } else { - regv = es8311_read_reg(ES8311_SYSTEM_REG14); - regv &= ~(0x40); - ret |= es8311_write_reg(ES8311_SYSTEM_REG14, regv); - } - - ret |= es8311_write_reg(ES8311_SYSTEM_REG0D, 0x01); - ret |= es8311_write_reg(ES8311_ADC_REG15, 0x40); - ret |= es8311_write_reg(ES8311_DAC_REG37, 0x48); - ret |= es8311_write_reg(ES8311_GP_REG45, 0x00); - - return ret; -} - -esp_err_t es8311_stop(es_module_t mode) -{ - esp_err_t ret = ESP_OK; - es8311_suspend(); - return ret; -} - -esp_err_t es8311_codec_set_voice_volume(int volume) -{ - esp_err_t res = ESP_OK; - if (volume < 0) { - volume = 0; - } else if (volume > 100) { - volume = 100; - } - int vol = (volume) * 2550 / 1000; - ESP_LOGD(TAG, "SET: volume:%d", vol); - es8311_write_reg(ES8311_DAC_REG32, vol); - return res; -} - -esp_err_t es8311_codec_get_voice_volume(int *volume) -{ - esp_err_t res = ESP_OK; - int regv = 0; - regv = es8311_read_reg(ES8311_DAC_REG32); - if (regv == ESP_FAIL) { - *volume = 0; - res = ESP_FAIL; - } else { - *volume = regv * 100 / 256; - } - ESP_LOGD(TAG, "GET: res:%d, volume:%d", regv, *volume); - return res; -} - -esp_err_t es8311_set_voice_mute(bool enable) -{ - ESP_LOGD(TAG, "Es8311SetVoiceMute volume:%d", enable); - es8311_mute(enable); - return ESP_OK; -} - -esp_err_t es8311_get_voice_mute(int *mute) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - res = es8311_read_reg(ES8311_DAC_REG31); - if (res != ESP_FAIL) { - reg = (res & 0x20) >> 5; - } - *mute = reg; - return res; -} - -esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db) -{ - esp_err_t res = ESP_OK; - res = es8311_write_reg(ES8311_ADC_REG16, gain_db); // MIC gain scale - return res; -} - -void es8311_read_all() -{ - for (int i = 0; i < 0x4A; i++) { - uint8_t reg = es8311_read_reg(i); - printf("REG:%02x, %02x\n", reg, i); - } -} diff --git a/components/codec/es8311/es8311.hpp b/components/codec/es8311/es8311.hpp deleted file mode 100644 index 6b8043d..0000000 --- a/components/codec/es8311/es8311.hpp +++ /dev/null @@ -1,281 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2019 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef _ES8311_H -#define _ES8311_H - -#include - -typedef std::function write_fn; -typedef std::function read_register_fn; - -void set_es8311_write(write_fn fn); -void set_es8311_read(read_register_fn fn); - -#include "audio_hal.h" -#include "esp_types.h" -#include "esxxx_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* - * ES8311_REGISTER NAME_REG_REGISTER ADDRESS - */ -#define ES8311_RESET_REG00 0x00 /*reset digital,csm,clock manager etc.*/ - -/* - * Clock Scheme Register definition - */ -#define ES8311_CLK_MANAGER_REG01 0x01 /* select clk src for mclk, enable clock for codec */ -#define ES8311_CLK_MANAGER_REG02 0x02 /* clk divider and clk multiplier */ -#define ES8311_CLK_MANAGER_REG03 0x03 /* adc fsmode and osr */ -#define ES8311_CLK_MANAGER_REG04 0x04 /* dac osr */ -#define ES8311_CLK_MANAGER_REG05 0x05 /* clk divier for adc and dac */ -#define ES8311_CLK_MANAGER_REG06 0x06 /* bclk inverter and divider */ -#define ES8311_CLK_MANAGER_REG07 0x07 /* tri-state, lrck divider */ -#define ES8311_CLK_MANAGER_REG08 0x08 /* lrck divider */ -/* - * SDP - */ -#define ES8311_SDPIN_REG09 0x09 /* dac serial digital port */ -#define ES8311_SDPOUT_REG0A 0x0A /* adc serial digital port */ -/* - * SYSTEM - */ -#define ES8311_SYSTEM_REG0B 0x0B /* system */ -#define ES8311_SYSTEM_REG0C 0x0C /* system */ -#define ES8311_SYSTEM_REG0D 0x0D /* system, power up/down */ -#define ES8311_SYSTEM_REG0E 0x0E /* system, power up/down */ -#define ES8311_SYSTEM_REG0F 0x0F /* system, low power */ -#define ES8311_SYSTEM_REG10 0x10 /* system */ -#define ES8311_SYSTEM_REG11 0x11 /* system */ -#define ES8311_SYSTEM_REG12 0x12 /* system, Enable DAC */ -#define ES8311_SYSTEM_REG13 0x13 /* system */ -#define ES8311_SYSTEM_REG14 0x14 /* system, select DMIC, select analog pga gain */ -/* - * ADC - */ -#define ES8311_ADC_REG15 0x15 /* ADC, adc ramp rate, dmic sense */ -#define ES8311_ADC_REG16 0x16 /* ADC */ -#define ES8311_ADC_REG17 0x17 /* ADC, volume */ -#define ES8311_ADC_REG18 0x18 /* ADC, alc enable and winsize */ -#define ES8311_ADC_REG19 0x19 /* ADC, alc maxlevel */ -#define ES8311_ADC_REG1A 0x1A /* ADC, alc automute */ -#define ES8311_ADC_REG1B 0x1B /* ADC, alc automute, adc hpf s1 */ -#define ES8311_ADC_REG1C 0x1C /* ADC, equalizer, hpf s2 */ -/* - * DAC - */ -#define ES8311_DAC_REG31 0x31 /* DAC, mute */ -#define ES8311_DAC_REG32 0x32 /* DAC, volume */ -#define ES8311_DAC_REG33 0x33 /* DAC, offset */ -#define ES8311_DAC_REG34 0x34 /* DAC, drc enable, drc winsize */ -#define ES8311_DAC_REG35 0x35 /* DAC, drc maxlevel, minilevel */ -#define ES8311_DAC_REG37 0x37 /* DAC, ramprate */ -/* - *GPIO - */ -#define ES8311_GPIO_REG44 0x44 /* GPIO, dac2adc for test */ -#define ES8311_GP_REG45 0x45 /* GP CONTROL */ -/* - * CHIP - */ -#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ -#define ES8311_CHD2_REGFE 0xFE /* CHIP ID2 */ -#define ES8311_CHVER_REGFF 0xFF /* VERSION */ -#define ES8311_CHD1_REGFD 0xFD /* CHIP ID1 */ - -#define ES8311_MAX_REGISTER 0xFF - -typedef enum { - ES8311_MIC_GAIN_MIN = -1, - ES8311_MIC_GAIN_0DB, - ES8311_MIC_GAIN_6DB, - ES8311_MIC_GAIN_12DB, - ES8311_MIC_GAIN_18DB, - ES8311_MIC_GAIN_24DB, - ES8311_MIC_GAIN_30DB, - ES8311_MIC_GAIN_36DB, - ES8311_MIC_GAIN_42DB, - ES8311_MIC_GAIN_MAX -} es8311_mic_gain_t; - -/* - * @brief Initialize ES8311 codec chip - * - * @param codec_cfg configuration of ES8311 - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_codec_init(audio_hal_codec_config_t *codec_cfg); - -/** - * @brief Deinitialize ES8311 codec chip - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_codec_deinit(void); - -/** - * @brief Control ES8311 codec chip - * - * @param mode codec mode - * @param ctrl_state start or stop decode or encode progress - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8311_codec_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); - -/** - * @brief Configure ES8311 codec mode and I2S interface - * - * @param mode codec mode - * @param iface I2S config - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8311_codec_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); - -/** - * @brief Configure ES8311 DAC mute or not. Basically you can use this function to mute the output or unmute - * - * @param enable enable(1) or disable(0) - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8311_set_voice_mute(bool enable); - -/** - * @brief Set voice volume - * - * @param volume: voice volume (0~100) - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_codec_set_voice_volume(int volume); - -/** - * @brief Get voice volume - * - * @param[out] *volume: voice volume (0~100) - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_codec_get_voice_volume(int *volume); - -/** - * @brief Configure ES8311 I2S format - * - * @param mod: set ADC or DAC or both - * @param cfg: ES8388 I2S format - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_config_fmt(es_i2s_fmt_t fmt); - -/** - * @brief Configure ES8311 data sample bits - * - * @param mode: set ADC or DAC or both - * @param bit_per_sample: bit number of per sample - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_set_bits_per_sample(audio_hal_iface_bits_t bits); - -/** - * @brief Start ES8311 codec chip - * - * @param mode: set ADC or DAC or both - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_start(es_module_t mode); - -/** - * @brief Stop ES8311 codec chip - * - * @param mode: set ADC or DAC or both - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8311_stop(es_module_t mode); - -/** - * @brief Get ES8311 DAC mute status - * - * @return - * - ESP_FAIL - * - ESP_OK - */ -esp_err_t es8311_get_voice_mute(int *mute); - -/** - * @brief Set ES8311 mic gain - * - * @param gain db of mic gain - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8311_set_mic_gain(es8311_mic_gain_t gain_db); - -/** - * @brief Print all ES8311 registers - * - * @return - * - void - */ -void es8311_read_all(); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/codec/es8388/es8388.c b/components/codec/es8388/es8388.c deleted file mode 100755 index faa60bb..0000000 --- a/components/codec/es8388/es8388.c +++ /dev/null @@ -1,508 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2018 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#include -#include "esp_log.h" -#include "es8388.h" -#include "driver/i2c.h" - -static const char *ES_TAG = "ES8388_DRIVER"; - -#define I2C_MASTER_NUM (I2C_NUM_0) -#define I2C_MASTER_TIMEOUT_MS (10) - -#define ES_ASSERT(a, format, b, ...) \ - if ((a) != 0) { \ - ESP_LOGE(ES_TAG, format, ##__VA_ARGS__); \ - return b;\ - } - -static esp_err_t es_write_reg(uint8_t slave_addr, uint8_t reg_add, uint8_t data) -{ - //return i2c_bus_write_byte(i2c_handle, reg_add, data); - uint8_t write_buf[2] = {reg_add, data}; - return i2c_master_write_to_device(I2C_MASTER_NUM, - slave_addr, - write_buf, - sizeof(write_buf), - I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); -} - -static esp_err_t es_read_reg(uint8_t reg_add, uint8_t *p_data) -{ - //return i2c_bus_read_byte(i2c_handle, reg_add, p_data); - return i2c_master_write_read_device(I2C_MASTER_NUM, - ES8388_ADDR, - ®_add, - 1, // size of addr - p_data, - 1, // amount of data to read - I2C_MASTER_TIMEOUT_MS / portTICK_PERIOD_MS); -} - -void es8388_read_all() -{ - for (int i = 0; i < 50; i++) { - uint8_t reg = 0; - es_read_reg(i, ®); - printf("%x: %x\n", i, reg); - } -} - -esp_err_t es8388_write_reg(uint8_t reg_add, uint8_t data) -{ - return es_write_reg(ES8388_ADDR, reg_add, data); -} - -/** - * @brief Configure ES8388 ADC and DAC volume. Basicly you can consider this as ADC and DAC gain - * - * @param mode: set ADC or DAC or all - * @param volume: -96 ~ 0 for example Es8388SetAdcDacVolume(ES_MODULE_ADC, 30, 6); means set ADC volume -30.5db - * @param dot: whether include 0.5. for example Es8388SetAdcDacVolume(ES_MODULE_ADC, 30, 4); means set ADC volume -30db - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -static int es8388_set_adc_dac_volume(int mode, int volume, int dot) -{ - int res = 0; - if ( volume < -96 || volume > 0 ) { - ESP_LOGW(ES_TAG, "Warning: volume < -96! or > 0!\n"); - if (volume < -96) - volume = -96; - else - volume = 0; - } - dot = (dot >= 5 ? 1 : 0); - volume = (-volume << 1) + dot; - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL8, volume); - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL9, volume); //ADC Right Volume=0db - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL5, volume); - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL4, volume); - } - return res; -} - - -/** - * @brief Power Management - * - * @param mod: if ES_POWER_CHIP, the whole chip including ADC and DAC is enabled - * @param enable: false to disable true to enable - * - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_start(es_module_t mode) -{ - esp_err_t res = ESP_OK; - uint8_t prev_data = 0, data = 0; - es_read_reg(ES8388_DACCONTROL21, &prev_data); - if (mode == ES_MODULE_LINE) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x09); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 by pass enable - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x50); // left DAC to left mixer enable and LIN signal to left mixer enable 0db : bupass enable - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x50); // right DAC to right mixer enable and LIN signal to right mixer enable 0db : bupass enable - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0xC0); //enable adc - } else { - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); //enable dac - } - es_read_reg(ES8388_DACCONTROL21, &data); - if (prev_data != data) { - res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0xF0); //start state machine - // res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x16); - // res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50); - res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00); //start state machine - } - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC || mode == ES_MODULE_LINE) { - res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x00); //power up adc and line in - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC || mode == ES_MODULE_LINE) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x3c); //power up dac and line out - res |= es8388_set_voice_mute(false); - ESP_LOGD(ES_TAG, "es8388_start default is mode:%d", mode); - } - - return res; -} - -/** - * @brief Power Management - * - * @param mod: if ES_POWER_CHIP, the whole chip including ADC and DAC is enabled - * @param enable: false to disable true to enable - * - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_stop(es_module_t mode) -{ - esp_err_t res = ESP_OK; - if (mode == ES_MODULE_LINE) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); //enable dac - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db - return res; - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0x00); - res |= es8388_set_voice_mute(true); //res |= Es8388SetAdcDacVolume(ES_MODULE_DAC, -96, 5); // 0db - //res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0xC0); //power down dac and line out - } - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { - //res |= Es8388SetAdcDacVolume(ES_MODULE_ADC, -96, 5); // 0db - res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xFF); //power down adc and line in - } - if (mode == ES_MODULE_ADC_DAC) { - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x9C); //disable mclk -// res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x00); -// res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x58); -// res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0xF3); //stop state machine - } - - return res; -} - - -/** - * @brief Config I2s clock in MSATER mode - * - * @param cfg.sclkDiv: generate SCLK by dividing MCLK in MSATER mode - * @param cfg.lclkDiv: generate LCLK by dividing MCLK in MSATER mode - * - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_i2s_config_clock(es_i2s_clock_t cfg) -{ - esp_err_t res = ESP_OK; - res |= es_write_reg(ES8388_ADDR, ES8388_MASTERMODE, cfg.sclk_div); - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, cfg.lclk_div); //ADCFsMode,singel SPEED,RATIO=256 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL2, cfg.lclk_div); //ADCFsMode,singel SPEED,RATIO=256 - return res; -} - -esp_err_t es8388_deinit(void) -{ - int res = 0; - res = es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0xFF); //reset and stop es8388 - // i2c_bus_delete(i2c_handle); - - return res; -} - -/** - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_init(audio_hal_codec_config_t *cfg) -{ - int res = 0; - - // bsp_i2c_add_device(&i2c_handle, ES8388_ADDR); - - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL3, 0x04); // 0x04 mute/0x00 unmute&ramp;DAC unmute and disabled digital volume control soft ramp - /* Chip Control and Power Management */ - res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0x50); - res |= es_write_reg(ES8388_ADDR, ES8388_CHIPPOWER, 0x00); //normal all and power up all - res |= es_write_reg(ES8388_ADDR, ES8388_MASTERMODE, cfg->i2s_iface.mode); //CODEC IN I2S SLAVE MODE - - /* dac */ - res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, 0xC0); //disable DAC and disable Lout/Rout/1/2 - res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL1, 0x12); //Enfr=0,Play&Record Mode,(0x17-both of mic&paly) -// res |= es_write_reg(ES8388_ADDR, ES8388_CONTROL2, 0); //LPVrefBuf=0,Pdn_ana=0 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, 0x18);//1a 0x18:16bit iis , 0x00:24 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL2, 0x02); //DACFsMode,SINGLE SPEED; DACFsRatio,256 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL16, 0x00); // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2 - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL17, 0x90); // only left DAC to left mixer enable 0db - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL20, 0x90); // only right DAC to right mixer enable 0db - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL21, 0x80); //set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL23, 0x00); //vroi=0 - res |= es8388_set_adc_dac_volume(ES_MODULE_DAC, 0, 0); // 0db - int tmp = 0; - if (AUDIO_HAL_DAC_OUTPUT_LINE2 == cfg->dac_output) { - tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_ROUT1; - } else if (AUDIO_HAL_DAC_OUTPUT_LINE1 == cfg->dac_output) { - tmp = DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT2; - } else { - tmp = DAC_OUTPUT_LOUT1 | DAC_OUTPUT_LOUT2 | DAC_OUTPUT_ROUT1 | DAC_OUTPUT_ROUT2; - } - res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, tmp); //0x3c Enable DAC and Enable Lout/Rout/1/2 - /* adc */ - res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0xFF); - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, 0xbb); // MIC Left and Right channel PGA gain - tmp = 0; - if (AUDIO_HAL_ADC_INPUT_LINE1 == cfg->adc_input) { - tmp = ADC_INPUT_LINPUT1_RINPUT1; - } else if (AUDIO_HAL_ADC_INPUT_LINE2 == cfg->adc_input) { - tmp = ADC_INPUT_LINPUT2_RINPUT2; - } else { - tmp = ADC_INPUT_DIFFERENCE; - } - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, tmp); //0x00 LINSEL & RINSEL, LIN1/RIN1 as ADC Input; DSSEL,use one DS Reg11; DSR, LINPUT1-RINPUT1 - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL3, 0x02); - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, 0x0d); // Left/Right data, Left/Right justified mode, Bits length, I2S format - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL5, 0x02); //ADCFsMode,singel SPEED,RATIO=256 - //ALC for Microphone - res |= es8388_set_adc_dac_volume(ES_MODULE_ADC, 0, 0); // 0db - res |= es_write_reg(ES8388_ADDR, ES8388_ADCPOWER, 0x09); //Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode - - ESP_LOGI(ES_TAG, "init,out:%02x, in:%02x", cfg->dac_output, cfg->adc_input); - return res; -} - -/** - * @brief Configure ES8388 I2S format - * - * @param mode: set ADC or DAC or all - * @param bitPerSample: see Es8388I2sFmt - * - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_config_fmt(es_module_t mode, es_i2s_fmt_t fmt) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { - res = es_read_reg(ES8388_ADCCONTROL4, ®); - reg = reg & 0xfc; - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, reg | fmt); - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { - res = es_read_reg(ES8388_DACCONTROL1, ®); - reg = reg & 0xf9; - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, reg | (fmt << 1)); - } - return res; -} - -/** - * @param volume: 0 ~ 100 - * - * @return - * - (-1) Error - * - (0) Success - */ -esp_err_t es8388_set_voice_volume(int volume) -{ - esp_err_t res = ESP_OK; - if (volume < 0) - volume = 0; - else if (volume > 100) - volume = 100; - volume /= 3; - res = es_write_reg(ES8388_ADDR, ES8388_DACCONTROL24, volume); - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL25, volume); - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL26, 0); - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL27, 0); - return res; -} - -/** - * - * @return - * volume - */ -esp_err_t es8388_get_voice_volume(int *volume) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - res = es_read_reg(ES8388_DACCONTROL24, ®); - if (res == ESP_FAIL) { - *volume = 0; - } else { - *volume = reg; - *volume *= 3; - if (*volume == 99) - *volume = 100; - } - return res; -} - -/** - * @brief Configure ES8388 data sample bits - * - * @param mode: set ADC or DAC or all - * @param bitPerSample: see BitsLength - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -esp_err_t es8388_set_bits_per_sample(es_module_t mode, es_bits_length_t bits_length) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - int bits = (int)bits_length; - - if (mode == ES_MODULE_ADC || mode == ES_MODULE_ADC_DAC) { - res = es_read_reg(ES8388_ADCCONTROL4, ®); - reg = reg & 0xe3; - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL4, reg | (bits << 2)); - } - if (mode == ES_MODULE_DAC || mode == ES_MODULE_ADC_DAC) { - res = es_read_reg(ES8388_DACCONTROL1, ®); - reg = reg & 0xc7; - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL1, reg | (bits << 3)); - } - return res; -} - -/** - * @brief Configure ES8388 DAC mute or not. Basically you can use this function to mute the output or unmute - * - * @param enable: enable or disable - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -esp_err_t es8388_set_voice_mute(bool enable) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - res = es_read_reg(ES8388_DACCONTROL3, ®); - reg = reg & 0xFB; - res |= es_write_reg(ES8388_ADDR, ES8388_DACCONTROL3, reg | (((int)enable) << 2)); - return res; -} - -esp_err_t es8388_get_voice_mute(void) -{ - esp_err_t res = ESP_OK; - uint8_t reg = 0; - res = es_read_reg(ES8388_DACCONTROL3, ®); - if (res == ESP_OK) { - reg = (reg & 0x04) >> 2; - } - return res == ESP_OK ? reg : res; -} - -/** - * @param gain: Config DAC Output - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -esp_err_t es8388_config_dac_output(es_dac_output_t output) -{ - esp_err_t res; - uint8_t reg = 0; - res = es_read_reg(ES8388_DACPOWER, ®); - reg = reg & 0xc3; - res |= es_write_reg(ES8388_ADDR, ES8388_DACPOWER, reg | output); - return res; -} - -/** - * @param gain: Config ADC input - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -esp_err_t es8388_config_adc_input(es_adc_input_t input) -{ - esp_err_t res; - uint8_t reg = 0; - res = es_read_reg(ES8388_ADCCONTROL2, ®); - reg = reg & 0x0f; - res |= es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL2, reg | input); - return res; -} - -/** - * @param gain: see es_mic_gain_t - * - * @return - * - (-1) Parameter error - * - (0) Success - */ -esp_err_t es8388_set_mic_gain(es_mic_gain_t gain) -{ - esp_err_t res, gain_n; - gain_n = (int)gain / 3; - gain_n = (gain_n << 4) + gain_n; - res = es_write_reg(ES8388_ADDR, ES8388_ADCCONTROL1, gain_n); //MIC PGA - return res; -} - -int es8388_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state) -{ - int res = 0; - int es_mode_t = 0; - switch (mode) { - case AUDIO_HAL_CODEC_MODE_ENCODE: - es_mode_t = ES_MODULE_ADC; - break; - case AUDIO_HAL_CODEC_MODE_LINE_IN: - es_mode_t = ES_MODULE_LINE; - break; - case AUDIO_HAL_CODEC_MODE_DECODE: - es_mode_t = ES_MODULE_DAC; - break; - case AUDIO_HAL_CODEC_MODE_BOTH: - es_mode_t = ES_MODULE_ADC_DAC; - break; - default: - es_mode_t = ES_MODULE_DAC; - ESP_LOGW(ES_TAG, "Codec mode not support, default is decode mode"); - break; - } - if (AUDIO_HAL_CTRL_STOP == ctrl_state) { - res = es8388_stop(es_mode_t); - } else { - res = es8388_start(es_mode_t); - ESP_LOGD(ES_TAG, "start default is decode mode:%d", es_mode_t); - } - return res; -} - -esp_err_t es8388_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface) -{ - esp_err_t res = ESP_OK; - int tmp = 0; - res |= es8388_config_fmt(ES_MODULE_ADC_DAC, iface->fmt); - if (iface->bits == AUDIO_HAL_BIT_LENGTH_16BITS) { - tmp = BIT_LENGTH_16BITS; - } else if (iface->bits == AUDIO_HAL_BIT_LENGTH_24BITS) { - tmp = BIT_LENGTH_24BITS; - } else { - tmp = BIT_LENGTH_32BITS; - } - res |= es8388_set_bits_per_sample(ES_MODULE_ADC_DAC, tmp); - return res; -} diff --git a/components/codec/es8388/es8388.h b/components/codec/es8388/es8388.h deleted file mode 100755 index 06d8082..0000000 --- a/components/codec/es8388/es8388.h +++ /dev/null @@ -1,303 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2018 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __ES8388_H__ -#define __ES8388_H__ - -#include "esp_types.h" -#include "esp_err.h" -#include "audio_hal.h" -#include "driver/i2c.h" -#include "esxxx_common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/* ES8388 address */ -#define ES8388_ADDR 0x20 /*!< 0x22:CE=1;0x20:CE=0*/ - -/* ES8388 register */ -#define ES8388_CONTROL1 0x00 -#define ES8388_CONTROL2 0x01 - -#define ES8388_CHIPPOWER 0x02 - -#define ES8388_ADCPOWER 0x03 -#define ES8388_DACPOWER 0x04 - -#define ES8388_CHIPLOPOW1 0x05 -#define ES8388_CHIPLOPOW2 0x06 - -#define ES8388_ANAVOLMANAG 0x07 - -#define ES8388_MASTERMODE 0x08 -/* ADC */ -#define ES8388_ADCCONTROL1 0x09 -#define ES8388_ADCCONTROL2 0x0a -#define ES8388_ADCCONTROL3 0x0b -#define ES8388_ADCCONTROL4 0x0c -#define ES8388_ADCCONTROL5 0x0d -#define ES8388_ADCCONTROL6 0x0e -#define ES8388_ADCCONTROL7 0x0f -#define ES8388_ADCCONTROL8 0x10 -#define ES8388_ADCCONTROL9 0x11 -#define ES8388_ADCCONTROL10 0x12 -#define ES8388_ADCCONTROL11 0x13 -#define ES8388_ADCCONTROL12 0x14 -#define ES8388_ADCCONTROL13 0x15 -#define ES8388_ADCCONTROL14 0x16 -/* DAC */ -#define ES8388_DACCONTROL1 0x17 -#define ES8388_DACCONTROL2 0x18 -#define ES8388_DACCONTROL3 0x19 -#define ES8388_DACCONTROL4 0x1a -#define ES8388_DACCONTROL5 0x1b -#define ES8388_DACCONTROL6 0x1c -#define ES8388_DACCONTROL7 0x1d -#define ES8388_DACCONTROL8 0x1e -#define ES8388_DACCONTROL9 0x1f -#define ES8388_DACCONTROL10 0x20 -#define ES8388_DACCONTROL11 0x21 -#define ES8388_DACCONTROL12 0x22 -#define ES8388_DACCONTROL13 0x23 -#define ES8388_DACCONTROL14 0x24 -#define ES8388_DACCONTROL15 0x25 -#define ES8388_DACCONTROL16 0x26 -#define ES8388_DACCONTROL17 0x27 -#define ES8388_DACCONTROL18 0x28 -#define ES8388_DACCONTROL19 0x29 -#define ES8388_DACCONTROL20 0x2a -#define ES8388_DACCONTROL21 0x2b -#define ES8388_DACCONTROL22 0x2c -#define ES8388_DACCONTROL23 0x2d -#define ES8388_DACCONTROL24 0x2e -#define ES8388_DACCONTROL25 0x2f -#define ES8388_DACCONTROL26 0x30 -#define ES8388_DACCONTROL27 0x31 -#define ES8388_DACCONTROL28 0x32 -#define ES8388_DACCONTROL29 0x33 -#define ES8388_DACCONTROL30 0x34 - -/** - * @brief Initialize ES8388 codec chip - * - * @param cfg configuration of ES8388 - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_init(audio_hal_codec_config_t *cfg); - -/** - * @brief Deinitialize ES8388 codec chip - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_deinit(void); - -/** - * @brief Configure ES8388 I2S format - * - * @param mod: set ADC or DAC or both - * @param cfg: ES8388 I2S format - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_config_fmt(es_module_t mod, es_i2s_fmt_t cfg); - -/** - * @brief Configure I2s clock in MSATER mode - * - * @param cfg: set bits clock and WS clock - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_i2s_config_clock(es_i2s_clock_t cfg); - -/** - * @brief Configure ES8388 data sample bits - * - * @param mode: set ADC or DAC or both - * @param bit_per_sample: bit number of per sample - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_set_bits_per_sample(es_module_t mode, es_bits_length_t bit_per_sample); - -/** - * @brief Start ES8388 codec chip - * - * @param mode: set ADC or DAC or both - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_start(es_module_t mode); - -/** - * @brief Stop ES8388 codec chip - * - * @param mode: set ADC or DAC or both - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_stop(es_module_t mode); - -/** - * @brief Set voice volume - * - * @param volume: voice volume (0~100) - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_set_voice_volume(int volume); - -/** - * @brief Get voice volume - * - * @param[out] *volume: voice volume (0~100) - * - * @return - * - ESP_OK - * - ESP_FAIL - */ -esp_err_t es8388_get_voice_volume(int *volume); - -/** - * @brief Configure ES8388 DAC mute or not. Basically you can use this function to mute the output or unmute - * - * @param enable enable(1) or disable(0) - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_set_voice_mute(bool enable); - -/** - * @brief Get ES8388 DAC mute status - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_get_voice_mute(void); - -/** - * @brief Set ES8388 mic gain - * - * @param gain db of mic gain - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_set_mic_gain(es_mic_gain_t gain); - -/** - * @brief Set ES8388 adc input mode - * - * @param input adc input mode - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_config_adc_input(es_adc_input_t input); - -/** - * @brief Set ES8388 dac output mode - * - * @param output dac output mode - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_config_dac_output(es_dac_output_t output); - -/** - * @brief Write ES8388 register - * - * @param reg_add address of register - * @param data data of register - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_write_reg(uint8_t reg_add, uint8_t data); - -/** - * @brief Print all ES8388 registers - * - * @return - * - void - */ -void es8388_read_all(); - -/** - * @brief Configure ES8388 codec mode and I2S interface - * - * @param mode codec mode - * @param iface I2S config - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_config_i2s(audio_hal_codec_mode_t mode, audio_hal_codec_i2s_iface_t *iface); - -/** - * @brief Control ES8388 codec chip - * - * @param mode codec mode - * @param ctrl_state start or stop decode or encode progress - * - * @return - * - ESP_FAIL Parameter error - * - ESP_OK Success - */ -esp_err_t es8388_ctrl_state(audio_hal_codec_mode_t mode, audio_hal_ctrl_t ctrl_state); - -#ifdef __cplusplus -} -#endif - -#endif //__ES8388_H__ diff --git a/components/codec/include/audio_hal.h b/components/codec/include/audio_hal.h deleted file mode 100755 index 16020ef..0000000 --- a/components/codec/include/audio_hal.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2018 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef _AUDIO_HAL_H_ -#define _AUDIO_HAL_H_ - -#define AUDIO_HAL_VOL_DEFAULT 60 - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Select media hal codec mode - */ -typedef enum { - AUDIO_HAL_CODEC_MODE_ENCODE = 1, /*!< select adc */ - AUDIO_HAL_CODEC_MODE_DECODE, /*!< select dac */ - AUDIO_HAL_CODEC_MODE_BOTH, /*!< select both adc and dac */ - AUDIO_HAL_CODEC_MODE_LINE_IN, /*!< set adc channel */ -} audio_hal_codec_mode_t; - -/** - * @brief Select adc channel for input mic signal - */ -typedef enum { - AUDIO_HAL_ADC_INPUT_LINE1 = 0x00, /*!< mic input to adc channel 1 */ - AUDIO_HAL_ADC_INPUT_LINE2, /*!< mic input to adc channel 2 */ - AUDIO_HAL_ADC_INPUT_ALL, /*!< mic input to both channels of adc */ - AUDIO_HAL_ADC_INPUT_DIFFERENCE, /*!< mic input to adc difference channel */ -} audio_hal_adc_input_t; - -/** - * @brief Select channel for dac output - */ -typedef enum { - AUDIO_HAL_DAC_OUTPUT_LINE1 = 0x00, /*!< dac output signal to channel 1 */ - AUDIO_HAL_DAC_OUTPUT_LINE2, /*!< dac output signal to channel 2 */ - AUDIO_HAL_DAC_OUTPUT_ALL, /*!< dac output signal to both channels */ -} audio_hal_dac_output_t; - -/** - * @brief Select operating mode i.e. start or stop for audio codec chip - */ -typedef enum { - AUDIO_HAL_CTRL_STOP = 0x00, /*!< set stop mode */ - AUDIO_HAL_CTRL_START = 0x01, /*!< set start mode */ -} audio_hal_ctrl_t; - -/** - * @brief Select I2S interface operating mode i.e. master or slave for audio codec chip - */ -typedef enum { - AUDIO_HAL_MODE_SLAVE = 0x00, /*!< set slave mode */ - AUDIO_HAL_MODE_MASTER = 0x01, /*!< set master mode */ -} audio_hal_iface_mode_t; - -/** - * @brief Select I2S interface samples per second - */ -typedef enum { - AUDIO_HAL_08K_SAMPLES, /*!< set to 8k samples per second */ - AUDIO_HAL_11K_SAMPLES, /*!< set to 11.025k samples per second */ - AUDIO_HAL_16K_SAMPLES, /*!< set to 16k samples in per second */ - AUDIO_HAL_22K_SAMPLES, /*!< set to 22.050k samples per second */ - AUDIO_HAL_24K_SAMPLES, /*!< set to 24k samples in per second */ - AUDIO_HAL_32K_SAMPLES, /*!< set to 32k samples in per second */ - AUDIO_HAL_44K_SAMPLES, /*!< set to 44.1k samples per second */ - AUDIO_HAL_48K_SAMPLES, /*!< set to 48k samples per second */ -} audio_hal_iface_samples_t; - -/** - * @brief Select I2S interface number of bits per sample - */ -typedef enum { - AUDIO_HAL_BIT_LENGTH_16BITS = 1, /*!< set 16 bits per sample */ - AUDIO_HAL_BIT_LENGTH_24BITS, /*!< set 24 bits per sample */ - AUDIO_HAL_BIT_LENGTH_32BITS, /*!< set 32 bits per sample */ -} audio_hal_iface_bits_t; - -/** - * @brief Select I2S interface format for audio codec chip - */ -typedef enum { - AUDIO_HAL_I2S_NORMAL = 0, /*!< set normal I2S format */ - AUDIO_HAL_I2S_LEFT, /*!< set all left format */ - AUDIO_HAL_I2S_RIGHT, /*!< set all right format */ - AUDIO_HAL_I2S_DSP, /*!< set dsp/pcm format */ -} audio_hal_iface_format_t; - -/** - * @brief I2s interface configuration for audio codec chip - */ -typedef struct { - audio_hal_iface_mode_t mode; /*!< audio codec chip mode */ - audio_hal_iface_format_t fmt; /*!< I2S interface format */ - audio_hal_iface_samples_t samples; /*!< I2S interface samples per second */ - audio_hal_iface_bits_t bits; /*!< i2s interface number of bits per sample */ -} audio_hal_codec_i2s_iface_t; - -/** - * @brief Configure media hal for initialization of audio codec chip - */ -typedef struct { - audio_hal_adc_input_t adc_input; /*!< set adc channel */ - audio_hal_dac_output_t dac_output; /*!< set dac channel */ - audio_hal_codec_mode_t codec_mode; /*!< select codec mode: adc, dac or both */ - audio_hal_codec_i2s_iface_t i2s_iface; /*!< set I2S interface configuration */ -} audio_hal_codec_config_t; - -#ifdef __cplusplus -} -#endif - -#endif //__AUDIO_HAL_H__ diff --git a/components/codec/include/esxxx_common.h b/components/codec/include/esxxx_common.h deleted file mode 100644 index bb65045..0000000 --- a/components/codec/include/esxxx_common.h +++ /dev/null @@ -1,190 +0,0 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2019 - * - * Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef _ESXXX_COMMON_H_ -#define _ESXXX_COMMON_H_ - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef enum { - BIT_LENGTH_MIN = -1, - BIT_LENGTH_16BITS = 0x03, - BIT_LENGTH_18BITS = 0x02, - BIT_LENGTH_20BITS = 0x01, - BIT_LENGTH_24BITS = 0x00, - BIT_LENGTH_32BITS = 0x04, - BIT_LENGTH_MAX, -} es_bits_length_t; - -typedef enum { - MCLK_DIV_MIN = -1, - MCLK_DIV_1 = 1, - MCLK_DIV_2 = 2, - MCLK_DIV_3 = 3, - MCLK_DIV_4 = 4, - MCLK_DIV_6 = 5, - MCLK_DIV_8 = 6, - MCLK_DIV_9 = 7, - MCLK_DIV_11 = 8, - MCLK_DIV_12 = 9, - MCLK_DIV_16 = 10, - MCLK_DIV_18 = 11, - MCLK_DIV_22 = 12, - MCLK_DIV_24 = 13, - MCLK_DIV_33 = 14, - MCLK_DIV_36 = 15, - MCLK_DIV_44 = 16, - MCLK_DIV_48 = 17, - MCLK_DIV_66 = 18, - MCLK_DIV_72 = 19, - MCLK_DIV_5 = 20, - MCLK_DIV_10 = 21, - MCLK_DIV_15 = 22, - MCLK_DIV_17 = 23, - MCLK_DIV_20 = 24, - MCLK_DIV_25 = 25, - MCLK_DIV_30 = 26, - MCLK_DIV_32 = 27, - MCLK_DIV_34 = 28, - MCLK_DIV_7 = 29, - MCLK_DIV_13 = 30, - MCLK_DIV_14 = 31, - MCLK_DIV_MAX, -} es_sclk_div_t; - -typedef enum { - LCLK_DIV_MIN = -1, - LCLK_DIV_128 = 0, - LCLK_DIV_192 = 1, - LCLK_DIV_256 = 2, - LCLK_DIV_384 = 3, - LCLK_DIV_512 = 4, - LCLK_DIV_576 = 5, - LCLK_DIV_768 = 6, - LCLK_DIV_1024 = 7, - LCLK_DIV_1152 = 8, - LCLK_DIV_1408 = 9, - LCLK_DIV_1536 = 10, - LCLK_DIV_2112 = 11, - LCLK_DIV_2304 = 12, - - LCLK_DIV_125 = 16, - LCLK_DIV_136 = 17, - LCLK_DIV_250 = 18, - LCLK_DIV_272 = 19, - LCLK_DIV_375 = 20, - LCLK_DIV_500 = 21, - LCLK_DIV_544 = 22, - LCLK_DIV_750 = 23, - LCLK_DIV_1000 = 24, - LCLK_DIV_1088 = 25, - LCLK_DIV_1496 = 26, - LCLK_DIV_1500 = 27, - LCLK_DIV_MAX, -} es_lclk_div_t; - -typedef enum { - D2SE_PGA_GAIN_MIN = -1, - D2SE_PGA_GAIN_DIS = 0, - D2SE_PGA_GAIN_EN = 1, - D2SE_PGA_GAIN_MAX = 2, -} es_d2se_pga_t; - -typedef enum { - ADC_INPUT_MIN = -1, - ADC_INPUT_LINPUT1_RINPUT1 = 0x00, - ADC_INPUT_MIC1 = 0x05, - ADC_INPUT_MIC2 = 0x06, - ADC_INPUT_LINPUT2_RINPUT2 = 0x50, - ADC_INPUT_DIFFERENCE = 0xf0, - ADC_INPUT_MAX, -} es_adc_input_t; - -typedef enum { - DAC_OUTPUT_MIN = -1, - DAC_OUTPUT_LOUT1 = 0x04, - DAC_OUTPUT_LOUT2 = 0x08, - DAC_OUTPUT_SPK = 0x09, - DAC_OUTPUT_ROUT1 = 0x10, - DAC_OUTPUT_ROUT2 = 0x20, - DAC_OUTPUT_ALL = 0x3c, - DAC_OUTPUT_MAX, -} es_dac_output_t; - -typedef enum { - MIC_GAIN_MIN = -1, - MIC_GAIN_0DB = 0, - MIC_GAIN_3DB = 3, - MIC_GAIN_6DB = 6, - MIC_GAIN_9DB = 9, - MIC_GAIN_12DB = 12, - MIC_GAIN_15DB = 15, - MIC_GAIN_18DB = 18, - MIC_GAIN_21DB = 21, - MIC_GAIN_24DB = 24, - MIC_GAIN_MAX, -} es_mic_gain_t; - -typedef enum { - ES_MODULE_MIN = -1, - ES_MODULE_ADC = 0x01, - ES_MODULE_DAC = 0x02, - ES_MODULE_ADC_DAC = 0x03, - ES_MODULE_LINE = 0x04, - ES_MODULE_MAX -} es_module_t; - -typedef enum { - ES_MODE_MIN = -1, - ES_MODE_SLAVE = 0x00, - ES_MODE_MASTER = 0x01, - ES_MODE_MAX, -} es_mode_t; - -typedef enum { - ES_I2S_MIN = -1, - ES_I2S_NORMAL = 0, - ES_I2S_LEFT = 1, - ES_I2S_RIGHT = 2, - ES_I2S_DSP = 3, - ES_I2S_MAX -} es_i2s_fmt_t; - -/** - * @brief Configure ES8388 clock - */ -typedef struct { - es_sclk_div_t sclk_div; /*!< bits clock divide */ - es_lclk_div_t lclk_div; /*!< WS clock divide */ -} es_i2s_clock_t; - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/gbc/CMakeLists.txt b/components/gbc/CMakeLists.txt index 38bca4e..5266204 100644 --- a/components/gbc/CMakeLists.txt +++ b/components/gbc/CMakeLists.txt @@ -2,7 +2,7 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" "gnuboy/src" PRIV_INCLUDE_DIRS "gnuboy/include" - REQUIRES box-emu-hal + REQUIRES "box-emu" "statistics" ) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-misleading-indentation -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) diff --git a/components/gbc/src/gameboy.cpp b/components/gbc/src/gameboy.cpp index cb6d988..4e31133 100644 --- a/components/gbc/src/gameboy.cpp +++ b/components/gbc/src/gameboy.cpp @@ -4,7 +4,8 @@ #include -#include "box_emu_hal.hpp" +#include "box-emu.hpp" +#include "statistics.hpp" static const size_t GAMEBOY_SCREEN_WIDTH = 160; static const size_t GAMEBOY_SCREEN_HEIGHT = 144; @@ -56,7 +57,7 @@ void run_to_vblank() { /* VBLANK BEGIN */ if ((frame % 2) == 0) { - hal::push_frame(framebuffer); + BoxEmu::get().push_frame(framebuffer); // swap buffers currentBuffer = currentBuffer ? 0 : 1; @@ -71,7 +72,7 @@ void run_to_vblank() { if (pcm.pos > 100) { auto audio_sample_count = pcm.pos; auto audio_buffer = (uint8_t*)pcm.buf; - hal::play_audio(audio_buffer, audio_sample_count * sizeof(int16_t)); + espp::EspBox::get().play_audio(audio_buffer, audio_sample_count * sizeof(int16_t)); pcm.pos = 0; } @@ -115,11 +116,11 @@ void init_gameboy(const std::string& rom_filename, uint8_t *romdata, size_t rom_ } // set native size - hal::set_native_size(GAMEBOY_SCREEN_WIDTH, GAMEBOY_SCREEN_HEIGHT); - hal::set_palette(nullptr); + BoxEmu::get().native_size(GAMEBOY_SCREEN_WIDTH, GAMEBOY_SCREEN_HEIGHT); + BoxEmu::get().palette(nullptr); - displayBuffer[0] = (uint16_t*)hal::get_frame_buffer0(); - displayBuffer[1] = (uint16_t*)hal::get_frame_buffer1(); + displayBuffer[0] = (uint16_t*)espp::EspBox::get().frame_buffer0(); + displayBuffer[1] = (uint16_t*)espp::EspBox::get().frame_buffer1(); memset(&fb, 0, sizeof(fb)); fb.w = GAMEBOY_SCREEN_WIDTH; @@ -132,7 +133,7 @@ void init_gameboy(const std::string& rom_filename, uint8_t *romdata, size_t rom_ fb.dirty = 0; framebuffer = displayBuffer[0]; - hal::set_audio_sample_rate(GAMEBOY_AUDIO_SAMPLE_RATE); + espp::EspBox::get().audio_sample_rate(GAMEBOY_AUDIO_SAMPLE_RATE); // save the audio buffer auto buf = pcm.buf; auto len = pcm.len; @@ -157,8 +158,7 @@ void init_gameboy(const std::string& rom_filename, uint8_t *romdata, size_t rom_ void run_gameboy_rom() { auto start = esp_timer_get_time(); // GET INPUT - InputState state; - hal::get_input_state(&state); + auto state = BoxEmu::get().gamepad_state(); pad_set(PAD_UP, state.up); pad_set(PAD_DOWN, state.down); pad_set(PAD_LEFT, state.left); @@ -206,7 +206,7 @@ void save_gameboy(std::string_view save_path) { } std::vector get_gameboy_video_buffer() { - const uint8_t* frame_buffer = hal::get_frame_buffer0(); + const uint8_t* frame_buffer = espp::EspBox::get().frame_buffer0(); // copy the frame buffer to a new buffer auto width = GAMEBOY_SCREEN_WIDTH; auto height = GAMEBOY_SCREEN_HEIGHT; @@ -220,5 +220,5 @@ std::vector get_gameboy_video_buffer() { void deinit_gameboy() { // now unload everything loader_unload(); - hal::set_audio_sample_rate(48000); + espp::EspBox::get().audio_sample_rate(48000); } diff --git a/components/genesis/CMakeLists.txt b/components/genesis/CMakeLists.txt index 19c5911..229d7d5 100644 --- a/components/genesis/CMakeLists.txt +++ b/components/genesis/CMakeLists.txt @@ -2,7 +2,7 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" "gwenesis/src/bus" "gwenesis/src/cpus/M68K" "gwenesis/src/cpus/Z80" "gwenesis/src/io" "gwenesis/src/savestate" "gwenesis/src/sound" "gwenesis/src/vdp" PRIV_INCLUDE_DIRS "." "gwenesis/src/bus" "gwenesis/src/cpus/M68K" "gwenesis/src/cpus/Z80" "gwenesis/src/io" "gwenesis/src/savestate" "gwenesis/src/sound" "gwenesis/src/vdp" - REQUIRES box-emu-hal + REQUIRES box-emu statistics ) # target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable -Wno-unused-value -O3) diff --git a/components/genesis/src/genesis.cpp b/components/genesis/src/genesis.cpp index 34d5955..fd1a539 100644 --- a/components/genesis/src/genesis.cpp +++ b/components/genesis/src/genesis.cpp @@ -16,7 +16,8 @@ extern "C" { #include -#include "box_emu_hal.hpp" +#include "box-emu.hpp" +#include "statistics.hpp" static constexpr int AUDIO_BUFFER_LENGTH = std::max(GWENESIS_AUDIO_BUFFER_LENGTH_NTSC, GWENESIS_AUDIO_BUFFER_LENGTH_PAL); @@ -162,11 +163,11 @@ static void init(uint8_t *romdata, size_t rom_data_size) { frame_counter = 0; muteFrameCount = 0; - hal::set_audio_sample_rate(REG1_PAL ? GWENESIS_AUDIO_FREQ_PAL/2 : GWENESIS_AUDIO_FREQ_NTSC/2); + espp::EspBox::get().audio_sample_rate(REG1_PAL ? GWENESIS_AUDIO_FREQ_PAL/2 : GWENESIS_AUDIO_FREQ_NTSC/2); frame_buffer = frame_buffer_index - ? hal::get_frame_buffer1() - : hal::get_frame_buffer0(); + ? espp::EspBox::get().frame_buffer1() + : espp::EspBox::get().frame_buffer0(); gwenesis_vdp_set_buffer(frame_buffer); initialized = true; @@ -174,16 +175,18 @@ static void init(uint8_t *romdata, size_t rom_data_size) { } void init_genesis(uint8_t *romdata, size_t rom_data_size) { - hal::set_native_size(GENESIS_SCREEN_WIDTH, GENESIS_VISIBLE_HEIGHT, GENESIS_SCREEN_WIDTH); + BoxEmu::get().native_size(GENESIS_SCREEN_WIDTH, GENESIS_VISIBLE_HEIGHT, GENESIS_SCREEN_WIDTH); init(romdata, rom_data_size); } void IRAM_ATTR run_genesis_rom() { + static auto& emu = BoxEmu::get(); + static auto& box = espp::EspBox::get(); + auto start = esp_timer_get_time(); // handle input here (see system.h and use input.pad and input.system) - static InputState previous_state = {}; - InputState state = {}; - hal::get_input_state(&state); + static GamepadState previous_state = {}; + auto state = emu.gamepad_state(); // set frameskip to be 3 if muted, 60 otherwise frameskip = 3; // hal::is_muted() ? 3 : 60; @@ -224,7 +227,7 @@ void IRAM_ATTR run_genesis_rom() { gwenesis_vdp_render_config(); - bool sound_enabled = !hal::is_muted(); + bool sound_enabled = !box.is_muted(); /* Reset the difference clocks and audio index */ system_clock = 0; @@ -311,21 +314,21 @@ void IRAM_ATTR run_genesis_rom() { // copy the palette memcpy(palette, CRAM565, PALETTE_SIZE * sizeof(uint16_t)); // set the palette - hal::set_palette(palette, PALETTE_SIZE); + emu.palette(palette, PALETTE_SIZE); // push the frame buffer to the display task - hal::push_frame(frame_buffer); + emu.push_frame(frame_buffer); // ping pong the frame buffer frame_buffer_index = !frame_buffer_index; frame_buffer = frame_buffer_index - ? hal::get_frame_buffer1() - : hal::get_frame_buffer0(); + ? box.frame_buffer1() + : box.frame_buffer0(); gwenesis_vdp_set_buffer(frame_buffer); } if (sound_enabled) { // push the audio buffer to the audio task int audio_len = REG1_PAL ? GWENESIS_AUDIO_BUFFER_LENGTH_PAL : GWENESIS_AUDIO_BUFFER_LENGTH_NTSC; - hal::play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len); + box.play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len); } // manage statistics @@ -370,5 +373,5 @@ std::vector get_genesis_video_buffer() { } void deinit_genesis() { - hal::set_audio_sample_rate(48000); + espp::EspBox::get().audio_sample_rate(48000); } diff --git a/components/gui/src/gui.cpp b/components/gui/src/gui.cpp index 5c7cf6f..ef2de6a 100644 --- a/components/gui/src/gui.cpp +++ b/components/gui/src/gui.cpp @@ -51,7 +51,7 @@ void Gui::clear_rom_list() { void Gui::update_rom_list() { // only do this if the metadata has changed since the last time we updated. We // do this by checking the last modified time of the metadata file. - std::filesystem::path metadata_path(std::string(MOUNT_POINT) + "/" + metadata_filename_); + std::filesystem::path metadata_path(std::string(BoxEmu::mount_point) + "/" + metadata_filename_); std::error_code ec; auto metadata_last_modified = std::filesystem::last_write_time(metadata_path, ec); if (ec) { diff --git a/components/nes/CMakeLists.txt b/components/nes/CMakeLists.txt index d1125cc..aebdd68 100644 --- a/components/nes/CMakeLists.txt +++ b/components/nes/CMakeLists.txt @@ -2,6 +2,6 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" "nofrendo/cpu" "nofrendo/libsnss" "nofrendo/nes" "nofrendo/sndhrdw" "nofrendo/mappers" "nofrendo" PRIV_INCLUDE_DIRS "nofrendo/cpu" "nofrendo/libsnss" "nofrendo/nes" "nofrendo/sndhrdw" "nofrendo" - REQUIRES box-emu-hal + REQUIRES "box-emu" "statistics" ) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) diff --git a/components/nes/src/nes.cpp b/components/nes/src/nes.cpp index 4f9779a..128bf77 100644 --- a/components/nes/src/nes.cpp +++ b/components/nes/src/nes.cpp @@ -10,7 +10,8 @@ static nes_t* console_nes; #include -#include "box_emu_hal.hpp" +#include "box-emu.hpp" +#include "statistics.hpp" void reset_nes() { nes_reset(SOFT_RESET); @@ -38,10 +39,10 @@ void init_nes(const std::string& rom_filename, uint8_t *romdata, size_t rom_data unlock = false; // set native size - hal::set_native_size(NES_SCREEN_WIDTH, NES_VISIBLE_HEIGHT); - hal::set_palette(get_nes_palette()); + BoxEmu::get().native_size(NES_SCREEN_WIDTH, NES_VISIBLE_HEIGHT); + BoxEmu::get().palette(get_nes_palette()); - hal::set_audio_sample_rate(44100 / 2); + espp::EspBox::get().audio_sample_rate(44100 / 2); nes_insertcart(rom_filename.c_str(), console_nes); vid_setmode(NES_SCREEN_WIDTH, NES_VISIBLE_HEIGHT); @@ -64,8 +65,7 @@ void run_nes_rom() { auto end = esp_timer_get_time(); // update unlock based on x button - InputState state; - hal::get_input_state(&state); + auto state = BoxEmu::get().gamepad_state(); static bool last_x = false; if (state.x && !last_x) { unlock = !unlock; @@ -94,7 +94,7 @@ std::vector get_nes_video_buffer() { std::vector frame(NES_SCREEN_WIDTH * NES_VISIBLE_HEIGHT * 2); // the frame data for the NES is stored in frame_buffer0 as a 8 bit index into the palette // we need to convert this to a 16 bit RGB565 value - const uint8_t *frame_buffer0 = hal::get_frame_buffer0(); + const uint8_t *frame_buffer0 = espp::EspBox::get().frame_buffer0(); const uint16_t *palette = get_nes_palette(); for (int i = 0; i < NES_SCREEN_WIDTH * NES_VISIBLE_HEIGHT; i++) { uint8_t index = frame_buffer0[i]; @@ -107,5 +107,5 @@ std::vector get_nes_video_buffer() { void deinit_nes() { nes_poweroff(); - hal::set_audio_sample_rate(48000); + espp::EspBox::get().audio_sample_rate(48000); } diff --git a/components/nes/src/video_audio.cpp b/components/nes/src/video_audio.cpp index 9ce0aea..a2d085a 100644 --- a/components/nes/src/video_audio.cpp +++ b/components/nes/src/video_audio.cpp @@ -22,8 +22,7 @@ extern "C" { #include } -// from box-emu-hal -#include "box_emu_hal.hpp" +#include "box-emu.hpp" #define DEFAULT_WIDTH 256 #define DEFAULT_HEIGHT NES_VISIBLE_HEIGHT @@ -41,12 +40,13 @@ static void (*audio_callback)(void *buffer, int length) = NULL; extern "C" void do_audio_frame() { if (audio_callback == NULL) return; + static auto& box = espp::EspBox::get(); static int num_channels = 2; - static int num_samples = hal::get_audio_sample_rate() * num_channels / NES_REFRESH_RATE; + static int num_samples = box.audio_sample_rate() * num_channels / NES_REFRESH_RATE; static int num_bytes = num_samples * sizeof(int16_t); static int16_t *audio_frame = (int16_t*)heap_caps_malloc(num_bytes, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); audio_callback(audio_frame, num_samples); - hal::play_audio((uint8_t*)audio_frame, num_bytes); + box.play_audio((uint8_t*)audio_frame, num_bytes); } extern "C" void osd_setsound(void (*playfunc)(void *buffer, int length)) @@ -62,14 +62,13 @@ static void osd_stopsound(void) static int osd_init_sound(void) { - hal::audio_init(); audio_callback = NULL; return 0; } extern "C" void osd_getsoundinfo(sndinfo_t *info) { - info->sample_rate = hal::get_audio_sample_rate(); + info->sample_rate = espp::EspBox::get().audio_sample_rate(); info->bps = 16; } @@ -142,7 +141,7 @@ static void set_palette(rgb_t *pal) printf("set palette!\n"); for (i = 0; i < 256; i++) { - c = make_color(pal[i].r, pal[i].g, pal[i].b); + c = BoxEmu::make_color(pal[i].r, pal[i].g, pal[i].b); myPalette[i]= c; } @@ -161,7 +160,7 @@ static void clear(uint8 color) static bitmap_t *lock_write(void) { // SDL_LockSurface(mySurface); - myBitmap = bmp_createhw((uint8*)hal::get_frame_buffer1(), DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_WIDTH*2); + myBitmap = bmp_createhw((uint8*)espp::EspBox::get().frame_buffer1(), DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_WIDTH*2); // make sure they don't try to delete the frame buffer lol myBitmap->hardware = true; return myBitmap; @@ -174,13 +173,15 @@ static void free_write(int num_dirties, rect_t *dirty_rects) } static void custom_blit(const bitmap_t *bmp, int num_dirties, rect_t *dirty_rects) { - uint8_t *lcdfb = hal::get_frame_buffer0(); + static auto& box = espp::EspBox::get(); + static auto& emu = BoxEmu::get(); + uint8_t *lcdfb = box.frame_buffer0(); if (bmp->line[0] != NULL) { memcpy(lcdfb, bmp->line[0], 256 * 224); void* arg = (void*)lcdfb; - hal::push_frame(arg); + emu.push_frame(arg); } } @@ -192,8 +193,8 @@ static int ConvertJoystickInput() { int result = 0; - static struct InputState state; - hal::get_input_state(&state); + static auto& emu = BoxEmu::get(); + auto state = emu.gamepad_state(); if (!state.a) result |= (1<<13); diff --git a/components/rom_info/CMakeLists.txt b/components/rom_info/CMakeLists.txt index 39c6a40..52d20fb 100644 --- a/components/rom_info/CMakeLists.txt +++ b/components/rom_info/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" - REQUIRES logger box-emu-hal) + REQUIRES logger box-emu) diff --git a/components/rom_info/include/rom_info.hpp b/components/rom_info/include/rom_info.hpp index 85b1a55..6b51557 100644 --- a/components/rom_info/include/rom_info.hpp +++ b/components/rom_info/include/rom_info.hpp @@ -4,7 +4,7 @@ #include #include -#include "fs_init.hpp" +#include "box-emu.hpp" #include "format.hpp" #include "string_utils.hpp" diff --git a/components/rom_info/src/rom_info.cpp b/components/rom_info/src/rom_info.cpp index 19ff4a9..6202346 100644 --- a/components/rom_info/src/rom_info.cpp +++ b/components/rom_info/src/rom_info.cpp @@ -1,7 +1,7 @@ #include "rom_info.hpp" std::vector parse_metadata(const std::string& metadata_path) { - const std::string fs_prefix = std::string(MOUNT_POINT) + "/"; + const std::string fs_prefix = std::string(BoxEmu::mount_point) + "/"; std::vector infos; // load metadata path std::ifstream metadata(fs_prefix + metadata_path, std::ios::in); diff --git a/components/sms/CMakeLists.txt b/components/sms/CMakeLists.txt index 34d16b4..996c745 100644 --- a/components/sms/CMakeLists.txt +++ b/components/sms/CMakeLists.txt @@ -2,7 +2,7 @@ idf_component_register( INCLUDE_DIRS "include" SRC_DIRS "src" "smsplus" "smsplus/cpu" "smsplus/sound" PRIV_INCLUDE_DIRS "." "smsplus" "smsplus/cpu" "smsplus/sound" - REQUIRES box-emu-hal + REQUIRES box-emu statistics ) # target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-char-subscripts -Wno-attributes -Wno-implicit-fallthrough -Wno-unused-function -Wno-unused-variable -Wno-discarded-qualifiers) target_compile_options(${COMPONENT_LIB} PRIVATE -Wno-unused-const-variable) diff --git a/components/sms/src/sms.cpp b/components/sms/src/sms.cpp index 5e37e69..25b7c29 100644 --- a/components/sms/src/sms.cpp +++ b/components/sms/src/sms.cpp @@ -6,7 +6,8 @@ extern "C" { #include -#include "box_emu_hal.hpp" +#include "box-emu.hpp" +#include "statistics.hpp" static constexpr size_t SMS_SCREEN_WIDTH = 256; static constexpr size_t SMS_VISIBLE_HEIGHT = 192; @@ -57,7 +58,7 @@ static void init(uint8_t *romdata, size_t rom_data_size) { bitmap.width = SMS_SCREEN_WIDTH; bitmap.height = SMS_VISIBLE_HEIGHT; bitmap.pitch = bitmap.width; - bitmap.data = hal::get_frame_buffer0(); + bitmap.data = espp::EspBox::get().frame_buffer0(); cart.sram = sms_sram; sms.wram = sms_ram; @@ -66,7 +67,7 @@ static void init(uint8_t *romdata, size_t rom_data_size) { set_option_defaults(); - option.sndrate = hal::get_audio_sample_rate(); + option.sndrate = espp::EspBox::get().audio_sample_rate(); option.overscan = 0; option.extra_gg = 0; @@ -85,7 +86,7 @@ static void init(uint8_t *romdata, size_t rom_data_size) { void init_sms(uint8_t *romdata, size_t rom_data_size) { is_gg = false; - hal::set_native_size(SMS_SCREEN_WIDTH, SMS_VISIBLE_HEIGHT, SMS_SCREEN_WIDTH); + BoxEmu::get().native_size(SMS_SCREEN_WIDTH, SMS_VISIBLE_HEIGHT, SMS_SCREEN_WIDTH); load_rom_data(romdata, rom_data_size); sms.console = CONSOLE_SMS; sms.display = DISPLAY_NTSC; @@ -98,7 +99,7 @@ void init_sms(uint8_t *romdata, size_t rom_data_size) { void init_gg(uint8_t *romdata, size_t rom_data_size) { is_gg = true; - hal::set_native_size(GG_SCREEN_WIDTH, GG_VISIBLE_HEIGHT, SMS_SCREEN_WIDTH); + BoxEmu::get().native_size(GG_SCREEN_WIDTH, GG_VISIBLE_HEIGHT, SMS_SCREEN_WIDTH); load_rom_data(romdata, rom_data_size); sms.console = CONSOLE_GG; sms.display = DISPLAY_NTSC; @@ -110,10 +111,11 @@ void init_gg(uint8_t *romdata, size_t rom_data_size) { } void run_sms_rom() { + static auto& emu = BoxEmu::get(); + static auto& box = espp::EspBox::get(); auto start = esp_timer_get_time(); // handle input here (see system.h and use input.pad and input.system) - InputState state; - hal::get_input_state(&state); + auto state = emu.gamepad_state(); // pad[0] is player 0 input.pad[0] = 0; @@ -145,15 +147,15 @@ void run_sms_rom() { palette[i] = (rgb565 >> 8) | (rgb565 << 8); } // set the palette - hal::set_palette(palette, PALETTE_SIZE); + emu.palette(palette, PALETTE_SIZE); // render the frame - hal::push_frame((uint8_t*)bitmap.data + frame_buffer_offset); + emu.push_frame((uint8_t*)bitmap.data + frame_buffer_offset); // ping pong the frame buffer frame_buffer_index = !frame_buffer_index; bitmap.data = frame_buffer_index - ? (uint8_t*)hal::get_frame_buffer1() - : (uint8_t*)hal::get_frame_buffer0(); + ? (uint8_t*)box.frame_buffer1() + : (uint8_t*)box.frame_buffer0(); } else { system_frame(1); } @@ -178,7 +180,7 @@ void run_sms_rom() { auto sms_audio_buffer_len = sms_snd.sample_count - 1; // push the audio buffer to the audio task - hal::play_audio((uint8_t*)sms_audio_buffer, sms_audio_buffer_len * 2 * 2); // 2 channels, 2 bytes per sample + box.play_audio((uint8_t*)sms_audio_buffer, sms_audio_buffer_len * 2 * 2); // 2 channels, 2 bytes per sample // update unlock based on x button static bool last_x = false; diff --git a/main/cart.hpp b/main/cart.hpp index f4c1aa1..934702d 100644 --- a/main/cart.hpp +++ b/main/cart.hpp @@ -191,7 +191,7 @@ class Cart { protected: static constexpr size_t SCREEN_WIDTH = 320; static constexpr size_t SCREEN_HEIGHT = 240; - static constexpr std::string FS_PREFIX = MOUNT_POINT; + static constexpr std::string FS_PREFIX = BoxEmu::mount_point; static constexpr std::string SAVE_DIR = "/saves/"; virtual void on_menu_action(Menu::Action action) { From 0174c2efe4fcd2309479878d20499b89021dd23e Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 21:38:17 -0500 Subject: [PATCH 13/14] replace static auto with simple get() for simplicity --- components/genesis/src/genesis.cpp | 17 +++++++---------- components/nes/src/video_audio.cpp | 14 +++++--------- components/sms/src/sms.cpp | 14 ++++++-------- 3 files changed, 18 insertions(+), 27 deletions(-) diff --git a/components/genesis/src/genesis.cpp b/components/genesis/src/genesis.cpp index fd1a539..cfcf102 100644 --- a/components/genesis/src/genesis.cpp +++ b/components/genesis/src/genesis.cpp @@ -180,13 +180,10 @@ void init_genesis(uint8_t *romdata, size_t rom_data_size) { } void IRAM_ATTR run_genesis_rom() { - static auto& emu = BoxEmu::get(); - static auto& box = espp::EspBox::get(); - auto start = esp_timer_get_time(); // handle input here (see system.h and use input.pad and input.system) static GamepadState previous_state = {}; - auto state = emu.gamepad_state(); + auto state = BoxEmu::get().gamepad_state(); // set frameskip to be 3 if muted, 60 otherwise frameskip = 3; // hal::is_muted() ? 3 : 60; @@ -227,7 +224,7 @@ void IRAM_ATTR run_genesis_rom() { gwenesis_vdp_render_config(); - bool sound_enabled = !box.is_muted(); + bool sound_enabled = !espp::EspBox::get().is_muted(); /* Reset the difference clocks and audio index */ system_clock = 0; @@ -314,21 +311,21 @@ void IRAM_ATTR run_genesis_rom() { // copy the palette memcpy(palette, CRAM565, PALETTE_SIZE * sizeof(uint16_t)); // set the palette - emu.palette(palette, PALETTE_SIZE); + BoxEmu::get().palette(palette, PALETTE_SIZE); // push the frame buffer to the display task - emu.push_frame(frame_buffer); + BoxEmu::get().push_frame(frame_buffer); // ping pong the frame buffer frame_buffer_index = !frame_buffer_index; frame_buffer = frame_buffer_index - ? box.frame_buffer1() - : box.frame_buffer0(); + ? espp::EspBox::get().frame_buffer1() + : espp::EspBox::get().frame_buffer0(); gwenesis_vdp_set_buffer(frame_buffer); } if (sound_enabled) { // push the audio buffer to the audio task int audio_len = REG1_PAL ? GWENESIS_AUDIO_BUFFER_LENGTH_PAL : GWENESIS_AUDIO_BUFFER_LENGTH_NTSC; - box.play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len); + espp::EspBox::get().play_audio((uint8_t*)gwenesis_ym2612_buffer, audio_len); } // manage statistics diff --git a/components/nes/src/video_audio.cpp b/components/nes/src/video_audio.cpp index a2d085a..26d7798 100644 --- a/components/nes/src/video_audio.cpp +++ b/components/nes/src/video_audio.cpp @@ -40,13 +40,12 @@ static void (*audio_callback)(void *buffer, int length) = NULL; extern "C" void do_audio_frame() { if (audio_callback == NULL) return; - static auto& box = espp::EspBox::get(); static int num_channels = 2; - static int num_samples = box.audio_sample_rate() * num_channels / NES_REFRESH_RATE; + static int num_samples = espp::EspBox::get().audio_sample_rate() * num_channels / NES_REFRESH_RATE; static int num_bytes = num_samples * sizeof(int16_t); static int16_t *audio_frame = (int16_t*)heap_caps_malloc(num_bytes, MALLOC_CAP_8BIT | MALLOC_CAP_SPIRAM); audio_callback(audio_frame, num_samples); - box.play_audio((uint8_t*)audio_frame, num_bytes); + espp::EspBox::get().play_audio((uint8_t*)audio_frame, num_bytes); } extern "C" void osd_setsound(void (*playfunc)(void *buffer, int length)) @@ -173,15 +172,13 @@ static void free_write(int num_dirties, rect_t *dirty_rects) } static void custom_blit(const bitmap_t *bmp, int num_dirties, rect_t *dirty_rects) { - static auto& box = espp::EspBox::get(); - static auto& emu = BoxEmu::get(); - uint8_t *lcdfb = box.frame_buffer0(); + uint8_t *lcdfb = espp::EspBox::get().frame_buffer0(); if (bmp->line[0] != NULL) { memcpy(lcdfb, bmp->line[0], 256 * 224); void* arg = (void*)lcdfb; - emu.push_frame(arg); + BoxEmu::get().push_frame(arg); } } @@ -193,8 +190,7 @@ static int ConvertJoystickInput() { int result = 0; - static auto& emu = BoxEmu::get(); - auto state = emu.gamepad_state(); + auto state = BoxEmu::get().gamepad_state(); if (!state.a) result |= (1<<13); diff --git a/components/sms/src/sms.cpp b/components/sms/src/sms.cpp index 25b7c29..038249b 100644 --- a/components/sms/src/sms.cpp +++ b/components/sms/src/sms.cpp @@ -111,11 +111,9 @@ void init_gg(uint8_t *romdata, size_t rom_data_size) { } void run_sms_rom() { - static auto& emu = BoxEmu::get(); - static auto& box = espp::EspBox::get(); auto start = esp_timer_get_time(); // handle input here (see system.h and use input.pad and input.system) - auto state = emu.gamepad_state(); + auto state = BoxEmu::get().gamepad_state(); // pad[0] is player 0 input.pad[0] = 0; @@ -147,15 +145,15 @@ void run_sms_rom() { palette[i] = (rgb565 >> 8) | (rgb565 << 8); } // set the palette - emu.palette(palette, PALETTE_SIZE); + BoxEmu::get().palette(palette, PALETTE_SIZE); // render the frame - emu.push_frame((uint8_t*)bitmap.data + frame_buffer_offset); + BoxEmu::get().push_frame((uint8_t*)bitmap.data + frame_buffer_offset); // ping pong the frame buffer frame_buffer_index = !frame_buffer_index; bitmap.data = frame_buffer_index - ? (uint8_t*)box.frame_buffer1() - : (uint8_t*)box.frame_buffer0(); + ? (uint8_t*)espp::EspBox::get().frame_buffer1() + : (uint8_t*)espp::EspBox::get().frame_buffer0(); } else { system_frame(1); } @@ -180,7 +178,7 @@ void run_sms_rom() { auto sms_audio_buffer_len = sms_snd.sample_count - 1; // push the audio buffer to the audio task - box.play_audio((uint8_t*)sms_audio_buffer, sms_audio_buffer_len * 2 * 2); // 2 channels, 2 bytes per sample + espp::EspBox::get().play_audio((uint8_t*)sms_audio_buffer, sms_audio_buffer_len * 2 * 2); // 2 channels, 2 bytes per sample // update unlock based on x button static bool last_x = false; From 24aa5da5b54f40a9e20c04cda93b3a5c1351a2bd Mon Sep 17 00:00:00 2001 From: William Emfinger Date: Wed, 3 Jul 2024 22:01:43 -0500 Subject: [PATCH 14/14] feat: add mute button and update espp accordingly --- components/box-emu/include/box-emu.hpp | 4 ++++ components/box-emu/src/box-emu.cpp | 30 ++++++++++++++++++++++++++ components/espp | 2 +- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/components/box-emu/include/box-emu.hpp b/components/box-emu/include/box-emu.hpp index 77f78b9..5c0438a 100644 --- a/components/box-emu/include/box-emu.hpp +++ b/components/box-emu/include/box-emu.hpp @@ -21,6 +21,7 @@ #include "aw9523.hpp" #include "base_component.hpp" +#include "button.hpp" #include "events.hpp" #include "high_resolution_timer.hpp" #include "keypad_input.hpp" @@ -283,6 +284,9 @@ class BoxEmu : public espp::BaseComponent { // memory uint8_t *romdata_{nullptr}; + // audio + std::shared_ptr mute_button_; + // gamepad std::atomic can_read_gamepad_{true}; std::recursive_mutex gamepad_state_mutex_; diff --git a/components/box-emu/src/box-emu.cpp b/components/box-emu/src/box-emu.cpp index 958f63e..e11f8c0 100644 --- a/components/box-emu/src/box-emu.cpp +++ b/components/box-emu/src/box-emu.cpp @@ -59,6 +59,36 @@ bool BoxEmu::initialize_box() { return false; } + // initialize the mute button to broadcast the mute state + logger_.info("Initializing mute button"); + mute_button_ = std::make_shared(espp::Button::Config{ + .name = "mute button", + .interrupt_config = + { + .gpio_num = espp::EspBox::get_mute_pin(), + .callback = + [](const espp::Interrupt::Event &event) { + espp::EspBox::get().mute(event.active); + // simply publish that the mute button was presssed + espp::EventManager::get().publish(mute_button_topic, {}); + }, + .active_level = espp::Interrupt::ActiveLevel::LOW, + .interrupt_type = espp::Interrupt::Type::ANY_EDGE, + .pullup_enabled = true, + .pulldown_enabled = false, + }, + .task_config = + { + .name = "mute button task", + .stack_size_bytes = 4 * 1024, + .priority = 5, + }, + .log_level = espp::Logger::Verbosity::WARN, + }); + + // update the mute state (since it's a flip-flop and may have been set if we + // restarted without power loss) + espp::EspBox::get().mute(mute_button_->is_pressed()); return true; } diff --git a/components/espp b/components/espp index 855975f..0fe56d9 160000 --- a/components/espp +++ b/components/espp @@ -1 +1 @@ -Subproject commit 855975f19e835bc34dc7aea74197168712e79457 +Subproject commit 0fe56d920dd94400724b70e08294b86e32b21709