diff --git a/.gitignore b/.gitignore index 38bcae99..50ac7c8a 100644 --- a/.gitignore +++ b/.gitignore @@ -22,4 +22,7 @@ dependencies.lock sdkconfig.board.*.dev -.tactility/ \ No newline at end of file +.tactility/ + +.caveman.json +.ai/mcp \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 00000000..06e71def --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +Documentation/README.md \ No newline at end of file diff --git a/Devices/btt-panda-touch/Source/devices/Display.cpp b/Devices/btt-panda-touch/Source/devices/Display.cpp index 24607968..c534a556 100644 --- a/Devices/btt-panda-touch/Source/devices/Display.cpp +++ b/Devices/btt-panda-touch/Source/devices/Display.cpp @@ -3,11 +3,15 @@ #include #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 41 and interrupt pin is 40 + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Devices/cyd-2432s032c/Source/devices/Display.cpp b/Devices/cyd-2432s032c/Source/devices/Display.cpp index f771c1dd..d0bb5ddd 100644 --- a/Devices/cyd-2432s032c/Source/devices/Display.cpp +++ b/Devices/cyd-2432s032c/Source/devices/Display.cpp @@ -3,10 +3,14 @@ #include #include #include +#include +#include static std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, LCD_HORIZONTAL_RESOLUTION, LCD_VERTICAL_RESOLUTION ); diff --git a/Devices/cyd-3248s035c/Source/devices/Display.cpp b/Devices/cyd-3248s035c/Source/devices/Display.cpp index f9615824..d67a8cf6 100644 --- a/Devices/cyd-3248s035c/Source/devices/Display.cpp +++ b/Devices/cyd-3248s035c/Source/devices/Display.cpp @@ -3,10 +3,14 @@ #include #include #include +#include +#include static std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, LCD_HORIZONTAL_RESOLUTION, LCD_VERTICAL_RESOLUTION ); diff --git a/Devices/cyd-4848s040c/Source/devices/St7701Display.cpp b/Devices/cyd-4848s040c/Source/devices/St7701Display.cpp index e9c14889..154849a2 100644 --- a/Devices/cyd-4848s040c/Source/devices/St7701Display.cpp +++ b/Devices/cyd-4848s040c/Source/devices/St7701Display.cpp @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include #include @@ -221,8 +223,10 @@ lvgl_port_display_rgb_cfg_t St7701Display::getLvglPortDisplayRgbConfig(esp_lcd_p std::shared_ptr St7701Display::getTouchDevice() { if (touchDevice == nullptr) { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 480, 480 ); diff --git a/Devices/cyd-8048s043c/Source/devices/Display.cpp b/Devices/cyd-8048s043c/Source/devices/Display.cpp index 2d1e61e2..c2ecaae7 100644 --- a/Devices/cyd-8048s043c/Source/devices/Display.cpp +++ b/Devices/cyd-8048s043c/Source/devices/Display.cpp @@ -3,12 +3,16 @@ #include #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 38 and interrupt pin is 18 // or INT = NC, schematic and other info floating around is kinda conflicting... + auto* i2c = device_find_by_name("i2c_internal"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Devices/elecrow-crowpanel-advance-35/Source/devices/Display.cpp b/Devices/elecrow-crowpanel-advance-35/Source/devices/Display.cpp index 3771b46c..8c7dbc61 100644 --- a/Devices/elecrow-crowpanel-advance-35/Source/devices/Display.cpp +++ b/Devices/elecrow-crowpanel-advance-35/Source/devices/Display.cpp @@ -3,10 +3,14 @@ #include #include #include +#include +#include static std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 320, 480 ); diff --git a/Devices/elecrow-crowpanel-advance-50/Source/devices/Display.cpp b/Devices/elecrow-crowpanel-advance-50/Source/devices/Display.cpp index 3476d82d..26f9bdc7 100644 --- a/Devices/elecrow-crowpanel-advance-50/Source/devices/Display.cpp +++ b/Devices/elecrow-crowpanel-advance-50/Source/devices/Display.cpp @@ -2,12 +2,16 @@ #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 38 and interrupt pin is 18 // or INT = NC, schematic and other info floating around is kinda conflicting... + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Devices/elecrow-crowpanel-basic-50/Source/devices/Display.cpp b/Devices/elecrow-crowpanel-basic-50/Source/devices/Display.cpp index 6a9f70e7..8149bfa4 100644 --- a/Devices/elecrow-crowpanel-basic-50/Source/devices/Display.cpp +++ b/Devices/elecrow-crowpanel-basic-50/Source/devices/Display.cpp @@ -3,12 +3,16 @@ #include #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 38 and interrupt pin is 18 // or INT = NC, schematic and other info floating around is kinda conflicting... + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp b/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp index 151b05f4..08724d46 100644 --- a/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp +++ b/Devices/guition-jc1060p470ciwy/Source/devices/Display.cpp @@ -5,21 +5,22 @@ #include #include #include +#include +#include constexpr auto LCD_PIN_RESET = GPIO_NUM_0; // Match P4 EV board reset line constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_23; constexpr auto LCD_HORIZONTAL_RESOLUTION = 1024; constexpr auto LCD_VERTICAL_RESOLUTION = 600; -constexpr auto TOUCH_I2C_PORT = I2C_NUM_0; -constexpr auto TOUCH_I2C_SDA = GPIO_NUM_7; -constexpr auto TOUCH_I2C_SCL = GPIO_NUM_8; constexpr auto TOUCH_PIN_RESET = GPIO_NUM_NC; constexpr auto TOUCH_PIN_INTERRUPT = GPIO_NUM_NC; static std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c_internal"); + check(i2c); auto configuration = std::make_unique( - TOUCH_I2C_PORT, + i2c, LCD_HORIZONTAL_RESOLUTION, LCD_VERTICAL_RESOLUTION, false, // swapXY diff --git a/Devices/guition-jc8048w550c/Source/devices/Display.cpp b/Devices/guition-jc8048w550c/Source/devices/Display.cpp index 2d1e61e2..06ddbc34 100644 --- a/Devices/guition-jc8048w550c/Source/devices/Display.cpp +++ b/Devices/guition-jc8048w550c/Source/devices/Display.cpp @@ -3,12 +3,16 @@ #include #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 38 and interrupt pin is 18 // or INT = NC, schematic and other info floating around is kinda conflicting... + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Devices/lilygo-tdeck/Source/Configuration.cpp b/Devices/lilygo-tdeck/Source/Configuration.cpp index 615b7b7a..4f620ace 100644 --- a/Devices/lilygo-tdeck/Source/Configuration.cpp +++ b/Devices/lilygo-tdeck/Source/Configuration.cpp @@ -7,16 +7,20 @@ #include #include +#include +#include bool initBoot(); using namespace tt::hal; static std::vector> createDevices() { + auto* i2c_internal = device_find_by_name("i2c0"); + check(i2c_internal); return { createPower(), createDisplay(), - std::make_shared(), + std::make_shared(i2c_internal), std::make_shared(), std::make_shared(), createSdCard() diff --git a/Devices/lilygo-tdeck/Source/Init.cpp b/Devices/lilygo-tdeck/Source/Init.cpp index 6604afd3..b0635dbe 100644 --- a/Devices/lilygo-tdeck/Source/Init.cpp +++ b/Devices/lilygo-tdeck/Source/Init.cpp @@ -58,7 +58,7 @@ bool initBoot() { std::vector gps_configurations; gps_service->getGpsConfigurations(gps_configurations); if (gps_configurations.empty()) { - if (gps_service->addGpsConfiguration(tt::hal::gps::GpsConfiguration {.uartName = "uart1", .baudRate = 38400, .model = tt::hal::gps::GpsModel::UBLOX10})) { + if (gps_service->addGpsConfiguration(tt::hal::gps::GpsConfiguration {.uartName = "uart0", .baudRate = 38400, .model = tt::hal::gps::GpsModel::UBLOX10})) { LOGGER.info("Configured internal GPS"); } else { LOGGER.error("Failed to configure internal GPS"); diff --git a/Devices/lilygo-tdeck/Source/devices/Display.cpp b/Devices/lilygo-tdeck/Source/devices/Display.cpp index 92a679b1..f25e1eb2 100644 --- a/Devices/lilygo-tdeck/Source/devices/Display.cpp +++ b/Devices/lilygo-tdeck/Source/devices/Display.cpp @@ -3,10 +3,14 @@ #include #include #include +#include +#include static std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 240, 320, true, diff --git a/Devices/lilygo-tdeck/Source/devices/KeyboardBacklight.cpp b/Devices/lilygo-tdeck/Source/devices/KeyboardBacklight.cpp index 41ef5b67..42e70596 100644 --- a/Devices/lilygo-tdeck/Source/devices/KeyboardBacklight.cpp +++ b/Devices/lilygo-tdeck/Source/devices/KeyboardBacklight.cpp @@ -1,6 +1,5 @@ #include "KeyboardBacklight.h" #include // Driver -#include #include // TODO: Add Mutex and consider refactoring into a class diff --git a/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.cpp b/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.cpp index 7254acc6..ec32b025 100644 --- a/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.cpp +++ b/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.cpp @@ -1,24 +1,18 @@ #include "TdeckKeyboard.h" -#include -#include -#include -#include -#include -#include -#include -#include + #include +#include +#include +#include +#include +#include +#include using tt::hal::findFirstDevice; static const auto LOGGER = tt::Logger("TdeckKeyboard"); -constexpr auto TDECK_KEYBOARD_I2C_BUS_HANDLE = I2C_NUM_0; -constexpr auto TDECK_KEYBOARD_SLAVE_ADDRESS = 0x55; - -static bool keyboard_i2c_read(uint8_t* output) { - return tt::hal::i2c::masterRead(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, output, 1, 100 / portTICK_PERIOD_MS); -} +constexpr uint8_t TDECK_KEYBOARD_SLAVE_ADDRESS = 0x55; /** * The callback simulates press and release events, because the T-Deck @@ -37,7 +31,8 @@ static void keyboard_read_callback(lv_indev_t* indev, lv_indev_data_t* data) { data->key = 0; data->state = LV_INDEV_STATE_RELEASED; - if (keyboard_i2c_read(&read_buffer)) { + auto* keyboard = static_cast(lv_indev_get_user_data(indev)); + if (i2c_controller_read(keyboard->getI2cController(), TDECK_KEYBOARD_SLAVE_ADDRESS, &read_buffer, 1, 100 / portTICK_PERIOD_MS) == ERROR_NONE) { if (read_buffer == 0 && read_buffer != last_buffer) { if (LOGGER.isLoggingDebug()) { LOGGER.debug("Released {}", last_buffer); @@ -91,5 +86,5 @@ bool TdeckKeyboard::stopLvgl() { } bool TdeckKeyboard::isAttached() const { - return tt::hal::i2c::masterHasDeviceAtAddress(TDECK_KEYBOARD_I2C_BUS_HANDLE, TDECK_KEYBOARD_SLAVE_ADDRESS, 100); + return i2c_controller_has_device_at_address(i2cController, TDECK_KEYBOARD_SLAVE_ADDRESS, 100) == ERROR_NONE; } diff --git a/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.h b/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.h index 7204f1a0..5695ce82 100644 --- a/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.h +++ b/Devices/lilygo-tdeck/Source/devices/TdeckKeyboard.h @@ -1,17 +1,23 @@ #pragma once #include -#include + +struct Device; class TdeckKeyboard final : public tt::hal::keyboard::KeyboardDevice { + ::Device* i2cController; lv_indev_t* deviceHandle = nullptr; public: + explicit TdeckKeyboard(::Device* i2cController) : i2cController(i2cController) {} + std::string getName() const override { return "T-Deck Keyboard"; } std::string getDescription() const override { return "I2C keyboard"; } + ::Device* getI2cController() const { return i2cController; } + bool startLvgl(lv_display_t* display) override; bool stopLvgl() override; bool isAttached() const override; diff --git a/Devices/lilygo-tdeck/lilygo,tdeck.dts b/Devices/lilygo-tdeck/lilygo,tdeck.dts index ecb1f226..a60e6ef7 100644 --- a/Devices/lilygo-tdeck/lilygo,tdeck.dts +++ b/Devices/lilygo-tdeck/lilygo,tdeck.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -30,15 +31,6 @@ pin-scl = <&gpio0 8 GPIO_FLAG_NONE>; }; - i2c_external: i2c1 { - compatible = "espressif,esp32-i2c"; - status = "disabled"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 43 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 44 GPIO_FLAG_NONE>; - }; - i2s0 { compatible = "espressif,esp32-i2s"; port = ; @@ -55,7 +47,7 @@ pin-sclk = <&gpio0 40 GPIO_FLAG_NONE>; }; - uart1 { + uart0 { compatible = "espressif,esp32-uart"; port = ; pin-tx = <&gpio0 43 GPIO_FLAG_NONE>; diff --git a/Devices/lilygo-tlora-pager/Source/Configuration.cpp b/Devices/lilygo-tlora-pager/Source/Configuration.cpp index a6a5a816..363e0cc1 100644 --- a/Devices/lilygo-tlora-pager/Source/Configuration.cpp +++ b/Devices/lilygo-tlora-pager/Source/Configuration.cpp @@ -4,6 +4,7 @@ #include "devices/TpagerKeyboard.h" #include "devices/TpagerPower.h" #include +#include #include #include @@ -14,17 +15,18 @@ bool tpagerInit(); using namespace tt::hal; static DeviceVector createDevices() { - auto bq27220 = std::make_shared(I2C_NUM_0); + auto* i2c = device_find_by_name("i2c0"); + auto bq27220 = std::make_shared(i2c); auto power = std::make_shared(bq27220); - auto tca8418 = std::make_shared(I2C_NUM_0); + auto tca8418 = std::make_shared(i2c); auto keyboard = std::make_shared(tca8418); return std::vector> { tca8418, - std::make_shared(I2C_NUM_0), + std::make_shared(i2c), bq27220, - std::make_shared(I2C_NUM_0), + std::make_shared(i2c), power, createTpagerSdCard(), createDisplay(), diff --git a/Devices/lilygo-tlora-pager/Source/devices/TpagerKeyboard.cpp b/Devices/lilygo-tlora-pager/Source/devices/TpagerKeyboard.cpp index ac51b44b..902da0f2 100644 --- a/Devices/lilygo-tlora-pager/Source/devices/TpagerKeyboard.cpp +++ b/Devices/lilygo-tlora-pager/Source/devices/TpagerKeyboard.cpp @@ -1,6 +1,5 @@ #include "TpagerKeyboard.h" -#include #include #include @@ -156,7 +155,7 @@ bool TpagerKeyboard::stopLvgl() { } bool TpagerKeyboard::isAttached() const { - return tt::hal::i2c::masterHasDeviceAtAddress(keypad->getPort(), keypad->getAddress(), 100); + return i2c_controller_has_device_at_address(keypad->getController(), keypad->getAddress(), 100) == ERROR_NONE; } bool TpagerKeyboard::initBacklight(gpio_num_t pin, uint32_t frequencyHz, ledc_timer_t timer, ledc_channel_t channel) { diff --git a/Devices/lilygo-tlora-pager/Source/devices/TpagerPower.cpp b/Devices/lilygo-tlora-pager/Source/devices/TpagerPower.cpp index 35d291ad..919bff7b 100644 --- a/Devices/lilygo-tlora-pager/Source/devices/TpagerPower.cpp +++ b/Devices/lilygo-tlora-pager/Source/devices/TpagerPower.cpp @@ -5,8 +5,6 @@ static const auto LOGGER = tt::Logger("TpagerPower"); -constexpr auto TPAGER_GAUGE_I2C_BUS_HANDLE = I2C_NUM_0; - TpagerPower::~TpagerPower() {} bool TpagerPower::supportsMetric(MetricType type) const { diff --git a/Devices/m5stack-cardputer-adv/Source/Configuration.cpp b/Devices/m5stack-cardputer-adv/Source/Configuration.cpp index bd347562..bb8ca682 100644 --- a/Devices/m5stack-cardputer-adv/Source/Configuration.cpp +++ b/Devices/m5stack-cardputer-adv/Source/Configuration.cpp @@ -4,6 +4,7 @@ #include "devices/CardputerPower.h" #include +#include #include #include @@ -16,7 +17,7 @@ static bool initBoot() { } static DeviceVector createDevices() { - auto tca8418 = std::make_shared(I2C_NUM_0); + auto tca8418 = std::make_shared(device_find_by_name("i2c_internal")); return { createSdCard(), createDisplay(), diff --git a/Devices/m5stack-cardputer-adv/Source/devices/CardputerKeyboard.cpp b/Devices/m5stack-cardputer-adv/Source/devices/CardputerKeyboard.cpp index 01a1aa3b..80177eec 100644 --- a/Devices/m5stack-cardputer-adv/Source/devices/CardputerKeyboard.cpp +++ b/Devices/m5stack-cardputer-adv/Source/devices/CardputerKeyboard.cpp @@ -1,10 +1,8 @@ #include "CardputerKeyboard.h" -#include +#include constexpr auto* TAG = "CardputerKeyb"; -constexpr auto BACKLIGHT = GPIO_NUM_46; - constexpr auto KB_ROWS = 14; constexpr auto KB_COLS = 4; @@ -151,5 +149,5 @@ bool CardputerKeyboard::stopLvgl() { } bool CardputerKeyboard::isAttached() const { - return tt::hal::i2c::masterHasDeviceAtAddress(keypad->getPort(), keypad->getAddress(), 100); + return i2c_controller_has_device_at_address(keypad->getController(), keypad->getAddress(), 100) == ERROR_NONE; } diff --git a/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts b/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts index da1cb215..1a3ba58d 100644 --- a/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts +++ b/Devices/m5stack-cardputer-adv/m5stack,cardputer-adv.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -36,12 +37,14 @@ }; }; - i2c_port_a { - compatible = "espressif,esp32-i2c"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 2 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 1 GPIO_FLAG_NONE>; + port_a: grove0 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 2 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 1 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; display_spi: spi0 { @@ -68,12 +71,4 @@ pin-data-out = <&gpio0 42 GPIO_FLAG_NONE>; pin-data-in = <&gpio0 46 GPIO_FLAG_NONE>; }; - - uart_port_a: uart1 { - compatible = "espressif,esp32-uart"; - status = "disabled"; - port = ; - pin-tx = <&gpio0 1 GPIO_FLAG_NONE>; - pin-rx = <&gpio0 2 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-cardputer/m5stack,cardputer.dts b/Devices/m5stack-cardputer/m5stack,cardputer.dts index d30a7a65..bb169c2d 100644 --- a/Devices/m5stack-cardputer/m5stack,cardputer.dts +++ b/Devices/m5stack-cardputer/m5stack,cardputer.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -22,12 +23,14 @@ gpio-count = <49>; }; - i2c_port_a { - compatible = "espressif,esp32-i2c"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 2 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 1 GPIO_FLAG_NONE>; + port_a: grove0 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 2 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 1 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; display_spi: spi0 { @@ -55,12 +58,4 @@ pin-data-out = <&gpio0 42 GPIO_FLAG_NONE>; pin-data-in = <&gpio0 46 GPIO_FLAG_NONE>; }; - - uart_port_a: uart1 { - compatible = "espressif,esp32-uart"; - status = "disabled"; - port = ; - pin-tx = <&gpio0 1 GPIO_FLAG_NONE>; - pin-rx = <&gpio0 2 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-core2/Source/devices/Power.cpp b/Devices/m5stack-core2/Source/devices/Power.cpp index 8140046f..da54c9bd 100644 --- a/Devices/m5stack-core2/Source/devices/Power.cpp +++ b/Devices/m5stack-core2/Source/devices/Power.cpp @@ -1,10 +1,11 @@ #include +#include static std::shared_ptr axp192 = nullptr; std::shared_ptr createAxp192() { assert(axp192 == nullptr); - auto configuration = std::make_unique(I2C_NUM_0); + auto configuration = std::make_unique(device_find_by_name("i2c_internal")); axp192 = std::make_shared(std::move(configuration)); return axp192; } diff --git a/Devices/m5stack-core2/m5stack,core2.dts b/Devices/m5stack-core2/m5stack,core2.dts index 8092e39c..28ecb146 100644 --- a/Devices/m5stack-core2/m5stack,core2.dts +++ b/Devices/m5stack-core2/m5stack,core2.dts @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -37,12 +38,14 @@ }; }; - i2c_port_a { - compatible = "espressif,esp32-i2c"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 32 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 33 GPIO_FLAG_NONE>; + port_a: grove0 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 32 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 33 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; spi0 { @@ -63,12 +66,4 @@ pin-data-out = <&gpio0 2 GPIO_FLAG_NONE>; pin-data-in = <&gpio0 34 GPIO_FLAG_NONE>; }; - - uart_port_a: uart1 { - compatible = "espressif,esp32-uart"; - status = "disabled"; - port = ; - pin-tx = <&gpio0 33 GPIO_FLAG_NONE>; - pin-rx = <&gpio0 32 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-cores3/Source/InitBoot.cpp b/Devices/m5stack-cores3/Source/InitBoot.cpp index f188ccd9..3699455a 100644 --- a/Devices/m5stack-cores3/Source/InitBoot.cpp +++ b/Devices/m5stack-cores3/Source/InitBoot.cpp @@ -146,8 +146,9 @@ bool initPowerControl() { bool initBoot() { LOGGER.info("initBoot()"); - axp2101 = std::make_shared(I2C_NUM_0); - aw9523 = std::make_shared(I2C_NUM_0); + auto controller = device_find_by_name("i2c_internal"); + axp2101 = std::make_shared(controller); + aw9523 = std::make_shared(controller); return initPowerControl() && initGpioExpander(); } \ No newline at end of file diff --git a/Devices/m5stack-cores3/Source/devices/Display.cpp b/Devices/m5stack-cores3/Source/devices/Display.cpp index 0c03dea8..ba608739 100644 --- a/Devices/m5stack-cores3/Source/devices/Display.cpp +++ b/Devices/m5stack-cores3/Source/devices/Display.cpp @@ -4,14 +4,17 @@ #include #include #include -#include + +#include static const auto LOGGER = tt::Logger("CoreS3Display"); static void setBacklightDuty(uint8_t backlightDuty) { const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100] - under 20 is too dark // TODO: Refactor to use Axp2102 driver subproject. Reference: https://github.com/m5stack/M5Unified/blob/b8cfec7fed046242da7f7b8024a4e92004a51ff7/src/utility/AXP2101_Class.cpp#L42 - if (!tt::hal::i2c::masterWriteRegister(I2C_NUM_0, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000)) { // Sets DLD01 + auto controller = device_find_by_name("i2c_internal"); + check(controller); + if (i2c_controller_write_register(controller, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000) != ERROR_NONE) { // Sets DLD01 LOGGER.error("Failed to set display backlight voltage"); } } diff --git a/Devices/m5stack-cores3/m5stack,cores3.dts b/Devices/m5stack-cores3/m5stack,cores3.dts index 53d9e30f..1f231190 100644 --- a/Devices/m5stack-cores3/m5stack,cores3.dts +++ b/Devices/m5stack-cores3/m5stack,cores3.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -42,30 +43,34 @@ }; }; - i2c_port_a { - compatible = "espressif,esp32-i2c"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 2 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 1 GPIO_FLAG_NONE>; + port_a: grove0 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 2 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 1 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; - i2c_port_b { - compatible = "espressif,esp32-i2c"; - status = "disabled"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 9 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 8 GPIO_FLAG_NONE>; + port_b: grove1 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 9 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 8 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; - i2c_port_c { - compatible = "espressif,esp32-i2c"; - status = "disabled"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 18 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 17 GPIO_FLAG_NONE>; + port_c: grove2 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 17 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 18 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; spi0 { @@ -89,12 +94,4 @@ pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>; pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>; }; - - uart_port_a: uart1 { - compatible = "espressif,esp32-uart"; - status = "disabled"; - port = ; - pin-tx = <&gpio0 1 GPIO_FLAG_NONE>; - pin-rx = <&gpio0 2 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-papers3/Source/devices/Display.cpp b/Devices/m5stack-papers3/Source/devices/Display.cpp index 42cc2f7f..acfd5f59 100644 --- a/Devices/m5stack-papers3/Source/devices/Display.cpp +++ b/Devices/m5stack-papers3/Source/devices/Display.cpp @@ -1,10 +1,14 @@ #include "Display.h" #include #include +#include +#include std::shared_ptr createTouch() { + auto* i2c = device_find_by_name("i2c_internal"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 540, 960, true, // swapXy diff --git a/Devices/m5stack-stackchan/Source/Configuration.cpp b/Devices/m5stack-stackchan/Source/Configuration.cpp index cb80cb47..dd3d0ef1 100644 --- a/Devices/m5stack-stackchan/Source/Configuration.cpp +++ b/Devices/m5stack-stackchan/Source/Configuration.cpp @@ -231,7 +231,7 @@ bool initBoot() { } // Keep Axp2101 C++ wrapper alive for Axp2101Power (backlight + battery) - axp2101 = std::make_shared(I2C_NUM_0); + axp2101 = std::make_shared(i2c); return true; } diff --git a/Devices/m5stack-stackchan/Source/devices/Display.cpp b/Devices/m5stack-stackchan/Source/devices/Display.cpp index cca04077..7eb75630 100644 --- a/Devices/m5stack-stackchan/Source/devices/Display.cpp +++ b/Devices/m5stack-stackchan/Source/devices/Display.cpp @@ -4,13 +4,16 @@ #include #include #include -#include + +#include static const auto LOGGER = tt::Logger("StackChanDisplay"); static void setBacklightDuty(uint8_t backlightDuty) { const uint8_t voltage = 20 + ((8 * backlightDuty) / 255); // [0b00000, 0b11100] - if (!tt::hal::i2c::masterWriteRegister(I2C_NUM_0, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000)) { + auto controller = device_find_by_name("i2c_internal"); + check(controller); + if (i2c_controller_write_register(controller, AXP2101_ADDRESS, 0x99, &voltage, 1, 1000) != ERROR_NONE) { // Sets DLD01 LOGGER.error("Failed to set display backlight voltage"); } } diff --git a/Devices/m5stack-stackchan/m5stack,stackchan.dts b/Devices/m5stack-stackchan/m5stack,stackchan.dts index d42c25d4..00b4729b 100644 --- a/Devices/m5stack-stackchan/m5stack,stackchan.dts +++ b/Devices/m5stack-stackchan/m5stack,stackchan.dts @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -67,30 +68,43 @@ // TODO: BMM150 magnetometer @ 0x10 — accessible only via BMI270 aux I2C }; - i2c_port_a { - compatible = "espressif,esp32-i2c"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 2 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 1 GPIO_FLAG_NONE>; + // TODO: Servo UART (SCS9009, 1 Mbaud) — TX=GPIO6, RX=GPIO7 + uart_port_a: uart1 { + compatible = "espressif,esp32-uart"; + status = "disabled"; + port = ; + pin-tx = <&gpio0 6 GPIO_FLAG_NONE>; + pin-rx = <&gpio0 7 GPIO_FLAG_NONE>; }; - i2c_port_b { - compatible = "espressif,esp32-i2c"; - status = "disabled"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 9 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 8 GPIO_FLAG_NONE>; + port_a: grove0 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 2 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 1 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; - i2c_port_c { - compatible = "espressif,esp32-i2c"; - status = "disabled"; - port = ; - clock-frequency = <400000>; - pin-sda = <&gpio0 18 GPIO_FLAG_NONE>; - pin-scl = <&gpio0 17 GPIO_FLAG_NONE>; + port_b: grove1 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 9 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 8 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; + }; + + port_c: grove2 { + compatible = "espressif,esp32-grove"; + defaultMode = ; + pinSdaRx = <&gpio0 17 GPIO_FLAG_NONE>; + pinSclTx = <&gpio0 18 GPIO_FLAG_NONE>; + uartPort = ; + i2cPort = ; + i2cClockFrequency = <400000>; }; spi0 { @@ -111,13 +125,4 @@ pin-data-in = <&gpio0 14 GPIO_FLAG_NONE>; pin-mclk = <&gpio0 0 GPIO_FLAG_NONE>; }; - - // TODO: Servo UART (SCS9009, 1 Mbaud) — TX=GPIO6, RX=GPIO7 - uart_port_a: uart1 { - compatible = "espressif,esp32-uart"; - status = "disabled"; - port = ; - pin-tx = <&gpio0 1 GPIO_FLAG_NONE>; - pin-rx = <&gpio0 2 GPIO_FLAG_NONE>; - }; }; diff --git a/Devices/m5stack-stickc-plus/Source/devices/Power.cpp b/Devices/m5stack-stickc-plus/Source/devices/Power.cpp index 40dc1137..3efd322c 100644 --- a/Devices/m5stack-stickc-plus/Source/devices/Power.cpp +++ b/Devices/m5stack-stickc-plus/Source/devices/Power.cpp @@ -1,10 +1,11 @@ #include +#include static std::shared_ptr axp192 = nullptr; std::shared_ptr createAxp192() { assert(axp192 == nullptr); - auto configuration = std::make_unique(I2C_NUM_0); + auto configuration = std::make_unique(device_find_by_name("i2c_internal")); axp192 = std::make_shared(std::move(configuration)); return axp192; } diff --git a/Devices/m5stack-tab5/Source/Configuration.cpp b/Devices/m5stack-tab5/Source/Configuration.cpp index dd9dec53..e1f3348d 100644 --- a/Devices/m5stack-tab5/Source/Configuration.cpp +++ b/Devices/m5stack-tab5/Source/Configuration.cpp @@ -7,7 +7,6 @@ #include #include -#include using namespace tt::hal; diff --git a/Devices/m5stack-tab5/Source/devices/Display.cpp b/Devices/m5stack-tab5/Source/devices/Display.cpp index b26fe70e..c56b6307 100644 --- a/Devices/m5stack-tab5/Source/devices/Display.cpp +++ b/Devices/m5stack-tab5/Source/devices/Display.cpp @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include #include @@ -19,8 +21,10 @@ constexpr auto LCD_PIN_RESET = GPIO_NUM_NC; constexpr auto LCD_PIN_BACKLIGHT = GPIO_NUM_22; static std::shared_ptr createGt911Touch() { + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 720, 1280, false, // swapXY diff --git a/Devices/unphone/Source/InitBoot.cpp b/Devices/unphone/Source/InitBoot.cpp index ddcfe6d6..4485b13c 100644 --- a/Devices/unphone/Source/InitBoot.cpp +++ b/Devices/unphone/Source/InitBoot.cpp @@ -1,4 +1,5 @@ #include "UnPhoneFeatures.h" +#include #include #include #include @@ -160,7 +161,7 @@ static bool unPhonePowerOn() { bootStats.printInfo(); bootStats.notifyBootStart(); - bq24295 = std::make_shared(I2C_NUM_0); + bq24295 = std::make_shared(device_find_by_name("i2c_internal")); unPhoneFeatures = std::make_shared(bq24295); diff --git a/Devices/waveshare-s3-touch-lcd-43/Source/devices/Display.cpp b/Devices/waveshare-s3-touch-lcd-43/Source/devices/Display.cpp index 49aba831..30090af1 100644 --- a/Devices/waveshare-s3-touch-lcd-43/Source/devices/Display.cpp +++ b/Devices/waveshare-s3-touch-lcd-43/Source/devices/Display.cpp @@ -2,12 +2,16 @@ #include #include +#include +#include std::shared_ptr createTouch() { // Note for future changes: Reset pin is 38 and interrupt pin is 18 // or INT = NC, schematic and other info floating around is kinda conflicting... + auto* i2c = device_find_by_name("i2c0"); + check(i2c); auto configuration = std::make_unique( - I2C_NUM_0, + i2c, 800, 480 ); diff --git a/Documentation/README.md b/Documentation/README.md new file mode 100644 index 00000000..91471f99 --- /dev/null +++ b/Documentation/README.md @@ -0,0 +1,145 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +Tactility is an operating system for the ESP32 microcontroller family. It runs on 40+ supported devices (CYD boards, LilyGO, M5Stack, Elecrow, etc.) and includes a desktop simulator. Built with C++23, ESP-IDF, LVGL, and FreeRTOS. + +## Build Commands + +### Simulator (Linux/macOS, no ESP-IDF needed) + +```bash +cmake -B buildsim -G Ninja +ninja -C buildsim # build firmware + tests +./buildsim/Firmware/Tactility # run simulator +``` + +### ESP32 firmware + +```bash +python device.py # generate sdkconfig for device (e.g. lilygo-tdeck) +python device.py --dev # dev mode: force 4MB partition table +idf.py build # build firmware +idf.py flash monitor # flash and monitor +``` + +Device IDs are the folder names under `Devices/` (e.g. `lilygo-tdeck`, `m5stack-cores3`, `cyd-2432s028r`). + +### Tests + +Tests use Doctest and run on simulator (POSIX) target only: + +```bash +cmake -B buildsim -G Ninja +ninja -C buildsim build-tests +cd buildsim && ctest # run all tests +./buildsim/Tests/TactilityCore/TactilityCoreTests # run a single test suite +./buildsim/Tests/TactilityKernel/TactilityKernelTests +./buildsim/Tests/Tactility/TactilityTests +./buildsim/Tests/TactilityFreeRtos/TactilityFreeRtosTests +``` + +## Architecture + +### Layer Stack (bottom to top) + +- **TactilityKernel** — C API kernel: device/driver/module lifecycle, concurrency primitives (thread, mutex, timer, dispatcher), filesystem, logging. Header convention: `` (lowercase snake_case). +- **TactilityCore** — Former kernel subproject. Deprecated, replaced by TactilityKernel. Contains C++ utilities: Bundle (key-value data), string helpers, file I/O, crypto. Header convention: `` (UpperCamelCase). +- **TactilityFreeRtos** — Thin C++ wrappers around FreeRTOS primitives. +- **Tactility** — Main OS layer: app framework, service framework, HAL (deprecated, replaced by TactilityKernel), LVGL integration, networking and services (Wi-Fi, BLE, NTP, ESP-NOW), settings, i18n. +- **TactilityC** — C bindings (`tt_*.h`) for TactilityCore and Tactility subprojects, used by side-loaded ELF apps on ESP32. Deprecated, replaced by TactilityKernel. +- **Firmware** — Entry point (`app_main`). + +### Device/Driver/Module System (kernel layer, C API) + +The kernel uses a Linux-inspired device model: + +- **Module** (`struct Module`): loadable unit that registers drivers and hardware. Lifecycle: `module_construct` → `module_add` → `module_start`. Each device board and platform is a module. +- **Driver** (`struct Driver`): binds to devices via `compatible` strings (like devicetree). Has `start_device`/`stop_device` callbacks and an `api` pointer for type-specific operations. +- **Device** (`struct Device`): represents hardware. Lifecycle: `device_construct` → `device_add` → `device_start`. Has a parent-child tree, driver binding, and locking. +- **DeviceType** (`struct DeviceType`): enables discovering devices by category (e.g. `DISPLAY_TYPE`, `TOUCH_TYPE`, `UART_CONTROLLER_TYPE`). + +Devices are defined via **devicetree** `.dts` files in each `Devices//` folder. A custom devicetree compiler (`Buildscripts/DevicetreeCompiler/compile.py`) generates C code from these files. Each device folder also has a `devicetree.yaml` specifying dependencies and the `.dts` file. + +### App Framework + +Apps implement `tt::app::App` (or just provide callbacks). Each app has an `AppManifest` with `appId`, `appName`, `appCategory`, and a factory function `createApp`. Apps are registered at startup in `Tactility.cpp`. External apps can be loaded from SD card via `manifest.properties` files, or side-loaded as ELF binaries on ESP32. + +### Service Framework + +Services implement `tt::service::Service` with a `ServiceManifest`. Services are long-running background processes (GUI, Wi-Fi, loader, statusbar, GPS, etc.). + +### HAL Layer + +#### Deprecated HAL + +Located in Tactility folder. + +`tt::hal::Configuration` is declared per-device board (in `Devices//Source/Configuration.cpp`). It provides `initBoot` for early hardware setup and `createDevices` to instantiate HAL device wrappers (display, touch, power, keyboard, etc.). + +#### Current HAL + +Located in TactilityKernel. Based on Linux driver subsystems. + +#### Driver + +A driver generally consists of: +- Registration of driver in parent module (optional) +- YAML bindings in the `bindings/` folder +- An `#include` that is used in the `.dts` file. The include is in `[projectname]/bindings/[drivername].h` +- The driver implementation: a `.cpp` and `.h` file. The implementation is C++, but the header exposes pure C functions. + +Drivers can be stored in: +- TactilityKernel +- A subproject in Platforms/ folder +- A subproject in Devices/ folder +- A subproject in Drivers/ folder. This is a kernel module. Naming is lower case and postfixed with `-module` + +#### Kernel Modules + +Projects that are kernel modules: + +1. Declare a `struct Module` +2. Contain a `devicetree.yaml` file that declares a list of dependencies (for parsing the devicetree) and specifies the bindings folder that contains the drivers' YAML definitions. For example: +```yaml +dependencies: + - TactilityKernel +bindings: bindings +``` + +### Platform Abstraction + +- `Platforms/platform-esp32/` — ESP-IDF specific implementations +- `Platforms/platform-posix/` — POSIX simulator implementations (SDL for display) + +### Build System + +The `tactility_add_module()` CMake macro (in `Buildscripts/module.cmake`) wraps ESP-IDF's `idf_component_register` on ESP32 and standard `add_library` on POSIX, allowing the same source to build for both targets. + +`device.py` reads `Devices//device.properties` and generates the `sdkconfig` file with all necessary ESP-IDF config (target chip, flash size, SPIRAM, LVGL fonts, Bluetooth, USB, etc.). + +### LVGL + +User interfaces should scale well for everything between very large (e.g. 1280x720) and small (e.g. 135x240) displays. Vertical and horizontal layouts are supported. + +## Coding Style + +Two conventions coexist; which one to use depends on the project layer: + +- **C code** (TactilityKernel, drivers): `lower_snake_case` for files, functions, variables. `UpperCamelCase` for types. Files in `source/`, `include/`, `private/` directories. +- **C++ code** (TactilityCore, Tactility, apps, services): `UpperCamelCase` for files and types. `lowerCamelCase` for functions. Files in `Source/`, `Include/`, `Private/` directories. + +Formatting is enforced by `.clang-format` (LLVM-based, 4-space indent, no column limit). +Never throw exceptions — use return types for error handling. Use `enum class` over plain `enum`. +Don't do null checks: caller is responsible for passing valid data. +Pointers are expected to be non-null unless documented otherwise. + +## Key Conventions + +- `#ifdef ESP_PLATFORM` guards ESP32-specific code; the simulator uses POSIX equivalents. +- The `Drivers/` directory contains hardware drivers (display controllers, touch controllers, PMICs, etc.) — each is its own CMake component. +- `Modules/` contains cross-cutting modules: `hal-device-module` (device lifecycle) and `lvgl-module` (LVGL task management). +- `Data/system/` and `Data/data/` are flashed as FAT filesystem images on ESP32. +- Translations are in `Translations/` as CSV files, generated via `generate.py`. diff --git a/Drivers/AW9523/Source/Aw9523.h b/Drivers/AW9523/Source/Aw9523.h index 1b711dfb..48c7e27b 100644 --- a/Drivers/AW9523/Source/Aw9523.h +++ b/Drivers/AW9523/Source/Aw9523.h @@ -8,7 +8,7 @@ class Aw9523 : public tt::hal::i2c::I2cDevice { public: - explicit Aw9523(i2c_port_t port) : I2cDevice(port, AW9523_ADDRESS) {} + explicit Aw9523(::Device* controller) : I2cDevice(controller, AW9523_ADDRESS) {} std::string getName() const final { return "AW9523"; } std::string getDescription() const final { return "GPIO expander with LED driver and I2C interface."; } diff --git a/Drivers/AXP192/Include/Axp192.h b/Drivers/AXP192/Include/Axp192.h index c1c70b2b..cc11bb82 100644 --- a/Drivers/AXP192/Include/Axp192.h +++ b/Drivers/AXP192/Include/Axp192.h @@ -1,8 +1,8 @@ #pragma once #include +#include #include -#include #include @@ -14,7 +14,7 @@ class Axp192 final : public tt::hal::power::PowerDevice { public: struct Configuration { - i2c_port_t port; + ::Device* controller; TickType_t readTimeout = 50 / portTICK_PERIOD_MS; TickType_t writeTimeout = 50 / portTICK_PERIOD_MS; }; diff --git a/Drivers/AXP192/Source/Axp192.cpp b/Drivers/AXP192/Source/Axp192.cpp index aa3a60b6..13fe3113 100644 --- a/Drivers/AXP192/Source/Axp192.cpp +++ b/Drivers/AXP192/Source/Axp192.cpp @@ -1,10 +1,11 @@ #include "Axp192.h" +#include constexpr auto TAG = "Axp192Power"; int32_t Axp192::i2cRead(void* handle, uint8_t address, uint8_t reg, uint8_t* buffer, uint16_t size) { const auto* device = static_cast(handle); - if (tt::hal::i2c::masterReadRegister(device->configuration->port, address, reg, buffer, size, device->configuration->readTimeout)) { + if (i2c_controller_read_register(device->configuration->controller, address, reg, buffer, size, device->configuration->readTimeout) == ERROR_NONE) { return AXP192_OK; } else { return 1; @@ -13,7 +14,7 @@ int32_t Axp192::i2cRead(void* handle, uint8_t address, uint8_t reg, uint8_t* buf int32_t Axp192::i2cWrite(void* handle, uint8_t address, uint8_t reg, const uint8_t* buffer, uint16_t size) { const auto* device = static_cast(handle); - if (tt::hal::i2c::masterWriteRegister(device->configuration->port, address, reg, buffer, size, device->configuration->writeTimeout)) { + if (i2c_controller_write_register(device->configuration->controller, address, reg, buffer, size, device->configuration->writeTimeout) == ERROR_NONE) { return AXP192_OK; } else { return 1; diff --git a/Drivers/AXP2101/Source/Axp2101.cpp b/Drivers/AXP2101/Source/Axp2101.cpp index e43cf7b5..8f78252c 100644 --- a/Drivers/AXP2101/Source/Axp2101.cpp +++ b/Drivers/AXP2101/Source/Axp2101.cpp @@ -54,5 +54,5 @@ bool Axp2101::getVBusVoltage(float& out) const { } bool Axp2101::setRegisters(uint8_t* bytePairs, size_t bytePairsSize) const { - return tt::hal::i2c::masterWriteRegisterArray(port, address, bytePairs, bytePairsSize, DEFAULT_TIMEOUT); + return i2c_controller_write_register_array(controller, address, bytePairs, bytePairsSize, DEFAULT_TIMEOUT) == ERROR_NONE; } diff --git a/Drivers/AXP2101/Source/Axp2101.h b/Drivers/AXP2101/Source/Axp2101.h index 19515e91..e6f63e5b 100644 --- a/Drivers/AXP2101/Source/Axp2101.h +++ b/Drivers/AXP2101/Source/Axp2101.h @@ -19,7 +19,7 @@ public: CHARGE_STATUS_STANDBY = 0b00 }; - explicit Axp2101(i2c_port_t port) : I2cDevice(port, AXP2101_ADDRESS) {} + explicit Axp2101(::Device* controller) : I2cDevice(controller, AXP2101_ADDRESS) {} std::string getName() const override { return "AXP2101"; } std::string getDescription() const override { return "Power management with I2C interface."; } diff --git a/Drivers/BQ24295/Source/Bq24295.h b/Drivers/BQ24295/Source/Bq24295.h index fa016fed..ff69a674 100644 --- a/Drivers/BQ24295/Source/Bq24295.h +++ b/Drivers/BQ24295/Source/Bq24295.h @@ -22,7 +22,7 @@ public: Enabled160s = 0b110000 }; - explicit Bq24295(i2c_port_t port) : I2cDevice(port, BQ24295_ADDRESS) {} + explicit Bq24295(::Device* controller) : I2cDevice(controller, BQ24295_ADDRESS) {} bool getWatchDogTimer(WatchDogTimer& out) const; bool setWatchDogTimer(WatchDogTimer in) const; diff --git a/Drivers/BQ25896/Source/Bq25896.h b/Drivers/BQ25896/Source/Bq25896.h index 38fd702d..0e85a573 100644 --- a/Drivers/BQ25896/Source/Bq25896.h +++ b/Drivers/BQ25896/Source/Bq25896.h @@ -8,7 +8,7 @@ class Bq25896 final : public tt::hal::i2c::I2cDevice { public: - explicit Bq25896(i2c_port_t port) : I2cDevice(port, BQ25896_ADDRESS) { + explicit Bq25896(::Device* controller) : I2cDevice(controller, BQ25896_ADDRESS) { powerOn(); } diff --git a/Drivers/BQ27220/Source/Bq27220.h b/Drivers/BQ27220/Source/Bq27220.h index 1e5ac6d8..6f9017d2 100644 --- a/Drivers/BQ27220/Source/Bq27220.h +++ b/Drivers/BQ27220/Source/Bq27220.h @@ -89,7 +89,7 @@ public: std::string getDescription() const override { return "I2C-controlled CEDV battery fuel gauge"; } - explicit Bq27220(i2c_port_t port) : I2cDevice(port, BQ27220_ADDRESS), accessKey(0xFFFFFFFF) {} + explicit Bq27220(::Device* controller) : I2cDevice(controller, BQ27220_ADDRESS), accessKey(0xFFFFFFFF) {} bool configureCapacity(uint16_t designCapacity, uint16_t fullChargeCapacity); bool getVoltage(uint16_t &value); diff --git a/Drivers/DRV2605/Source/Drv2605.cpp b/Drivers/DRV2605/Source/Drv2605.cpp index b7e85a1a..596f1a41 100644 --- a/Drivers/DRV2605/Source/Drv2605.cpp +++ b/Drivers/DRV2605/Source/Drv2605.cpp @@ -5,7 +5,7 @@ static const auto LOGGER = tt::Logger("DRV2605"); -Drv2605::Drv2605(i2c_port_t port, bool autoPlayStartupBuzz) : I2cDevice(port, ADDRESS), autoPlayStartupBuzz(autoPlayStartupBuzz) { +Drv2605::Drv2605(::Device* controller, bool autoPlayStartupBuzz) : I2cDevice(controller, ADDRESS), autoPlayStartupBuzz(autoPlayStartupBuzz) { check(init(), "Initialize DRV2605"); if (autoPlayStartupBuzz) { diff --git a/Drivers/DRV2605/Source/Drv2605.h b/Drivers/DRV2605/Source/Drv2605.h index 9bf93aa3..11d26543 100644 --- a/Drivers/DRV2605/Source/Drv2605.h +++ b/Drivers/DRV2605/Source/Drv2605.h @@ -62,7 +62,7 @@ class Drv2605 : public tt::hal::i2c::I2cDevice { public: - explicit Drv2605(i2c_port_t port, bool autoPlayStartupBuzz = true); + explicit Drv2605(::Device* controller, bool autoPlayStartupBuzz = true); std::string getName() const final { return "DRV2605"; } std::string getDescription() const final { return "Haptic driver for ERM/LRA with waveform library & auto-resonance tracking"; } diff --git a/Drivers/GT911/Source/Gt911Touch.cpp b/Drivers/GT911/Source/Gt911Touch.cpp index 06fbf660..be67ac81 100644 --- a/Drivers/GT911/Source/Gt911Touch.cpp +++ b/Drivers/GT911/Source/Gt911Touch.cpp @@ -1,31 +1,43 @@ #include "Gt911Touch.h" #include -#include +#include #include #include +#include +#include +#include +#include +#include static const auto LOGGER = tt::Logger("GT911"); bool Gt911Touch::createIoHandle(esp_lcd_panel_io_handle_t& outHandle) { esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG(); - /** - * When the interrupt pin is low, the address is 0x5D. Otherwise it is 0x14. - * There is not reset pin, and the current driver fails when you only specify the interrupt pin. - * Because of that, we don't use the interrupt pin and we'll simply scan the bus instead: - */ - if (tt::hal::i2c::masterHasDeviceAtAddress(configuration->port, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS)) { + auto* i2c = configuration->i2cController; + + if (i2c_controller_has_device_at_address(i2c, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS, pdMS_TO_TICKS(10)) == ERROR_NONE) { io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS; - } else if (tt::hal::i2c::masterHasDeviceAtAddress(configuration->port, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP)) { + } else if (i2c_controller_has_device_at_address(i2c, ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP, pdMS_TO_TICKS(10)) == ERROR_NONE) { io_config.dev_addr = ESP_LCD_TOUCH_IO_I2C_GT911_ADDRESS_BACKUP; } else { LOGGER.error("No device found on I2C bus"); return false; } - return esp_lcd_new_panel_io_i2c(configuration->port, &io_config, &outHandle) == ESP_OK; + auto* driver = device_get_driver(i2c); + if (driver_is_compatible(driver, "espressif,esp32-i2c")) { + auto port = static_cast(i2c->config)->port; + return esp_lcd_new_panel_io_i2c_v1(port, &io_config, &outHandle) == ESP_OK; + } else if (driver_is_compatible(driver, "espressif,esp32-i2c-master")) { + auto bus = esp32_i2c_master_get_bus_handle(i2c); + return esp_lcd_new_panel_io_i2c_v2(bus, &io_config, &outHandle) == ESP_OK; + } + + LOGGER.error("Unsupported I2C driver"); + return false; } bool Gt911Touch::createTouchHandle(esp_lcd_panel_io_handle_t ioHandle, const esp_lcd_touch_config_t& configuration, esp_lcd_touch_handle_t& panelHandle) { diff --git a/Drivers/GT911/Source/Gt911Touch.h b/Drivers/GT911/Source/Gt911Touch.h index 1ded5fda..fcb8da4e 100644 --- a/Drivers/GT911/Source/Gt911Touch.h +++ b/Drivers/GT911/Source/Gt911Touch.h @@ -2,10 +2,11 @@ #include #include -#include #include +struct Device; + class Gt911Touch final : public EspLcdTouch { public: @@ -14,7 +15,7 @@ public: public: Configuration( - i2c_port_t port, + ::Device* i2cController, uint16_t xMax, uint16_t yMax, bool swapXy = false, @@ -24,7 +25,7 @@ public: gpio_num_t pinInterrupt = GPIO_NUM_NC, unsigned int pinResetLevel = 0, unsigned int pinInterruptLevel = 0 - ) : port(port), + ) : i2cController(i2cController), xMax(xMax), yMax(yMax), swapXy(swapXy), @@ -36,7 +37,7 @@ public: pinInterruptLevel(pinInterruptLevel) {} - i2c_port_t port; + ::Device* i2cController; uint16_t xMax; uint16_t yMax; bool swapXy; diff --git a/Drivers/TCA8418/Source/Tca8418.h b/Drivers/TCA8418/Source/Tca8418.h index 9ef9b6dc..a74688f5 100644 --- a/Drivers/TCA8418/Source/Tca8418.h +++ b/Drivers/TCA8418/Source/Tca8418.h @@ -45,7 +45,7 @@ public: std::string getDescription() const final { return "I2C-controlled keyboard scan IC"; } - explicit Tca8418(i2c_port_t port) : I2cDevice(port, TCA8418_ADDRESS) { + explicit Tca8418(::Device* controller) : I2cDevice(controller, TCA8418_ADDRESS) { delta_micros = 0; last_update_micros = 0; this_update_micros = 0; diff --git a/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml b/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml new file mode 100644 index 00000000..11805e03 --- /dev/null +++ b/Platforms/platform-esp32/bindings/espressif,esp32-grove.yaml @@ -0,0 +1,29 @@ +description: ESP32 Grove Port + +compatible: "espressif,esp32-grove" + +properties: + defaultMode: + type: int + required: true + description: "One of enum Esp32GroveMode" + pinSdaRx: + type: phandle-array + required: true + description: SDA (I2C) or RX (UART) pin + pinSclTx: + type: phandle-array + required: true + description: SCL (I2C) or TX (UART) pin + uartPort: + type: int + required: true + description: UART port number + i2cPort: + type: int + required: true + description: I2C port number + i2cClockFrequency: + type: int + required: true + description: I2C clock frequency diff --git a/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h b/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h new file mode 100644 index 00000000..8d71c662 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/bindings/esp32_grove.h @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +DEFINE_DEVICETREE(esp32_grove, struct Esp32GroveConfig) + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h new file mode 100644 index 00000000..01a18451 --- /dev/null +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_grove.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct Esp32GroveConfig { + enum GroveMode defaultMode; + struct GpioPinSpec pinSdaRx; + struct GpioPinSpec pinSclTx; + uart_port_t uartPort; + i2c_port_t i2cPort; + uint32_t i2cClockFrequency; +}; + +#ifdef __cplusplus +} +#endif diff --git a/Platforms/platform-esp32/include/tactility/drivers/esp32_i2c_master.h b/Platforms/platform-esp32/include/tactility/drivers/esp32_i2c_master.h index 3e302f7a..32e01a90 100644 --- a/Platforms/platform-esp32/include/tactility/drivers/esp32_i2c_master.h +++ b/Platforms/platform-esp32/include/tactility/drivers/esp32_i2c_master.h @@ -2,12 +2,14 @@ #pragma once #include -#include +#include #ifdef __cplusplus extern "C" { #endif +struct Device; + struct Esp32I2cMasterConfig { i2c_port_num_t port; uint32_t clockFrequency; @@ -16,6 +18,8 @@ struct Esp32I2cMasterConfig { struct GpioPinSpec pinScl; }; +i2c_master_bus_handle_t esp32_i2c_master_get_bus_handle(struct Device* device); + #ifdef __cplusplus } #endif diff --git a/Platforms/platform-esp32/source/drivers/esp32_grove.cpp b/Platforms/platform-esp32/source/drivers/esp32_grove.cpp new file mode 100644 index 00000000..fad1400e --- /dev/null +++ b/Platforms/platform-esp32/source/drivers/esp32_grove.cpp @@ -0,0 +1,228 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define TAG "esp32_grove" + +struct Esp32GroveInternal { + Device* child_device = nullptr; + void* child_config = nullptr; + char* child_name = nullptr; + GroveMode current_mode = GROVE_MODE_DISABLED; +}; + +#define GET_CONFIG(device) ((const struct Esp32GroveConfig*)device->config) +#define GET_DATA(device) ((struct Esp32GroveInternal*)device_get_driver_data(device)) + +extern "C" { + +static error_t stop_child(Device* device) { + auto* data = GET_DATA(device); + if (!data) return ERROR_NONE; + + if (data->child_device) { + if (data->child_device->internal) { + if (device_is_added(data->child_device)) { + if (device_is_ready(data->child_device)) { + if (device_stop(data->child_device) != ERROR_NONE) { + LOG_E(TAG, "%s: failed to stop child device", device->name); + return ERROR_RESOURCE; + } + } + if (device_remove(data->child_device) != ERROR_NONE) { + LOG_E(TAG, "%s: failed to remove child device", device->name); + return ERROR_RESOURCE; + } + } + check(device_destruct(data->child_device) == ERROR_NONE); + } + delete data->child_device; + data->child_device = nullptr; + } + + if (data->child_config) { + if (data->current_mode == GROVE_MODE_UART) { + delete static_cast(data->child_config); + } else if (data->current_mode == GROVE_MODE_I2C) { + delete static_cast(data->child_config); + } + data->child_config = nullptr; + } + + delete[] data->child_name; + data->child_name = nullptr; + + data->current_mode = GROVE_MODE_DISABLED; + return ERROR_NONE; +} + +static error_t start_child(Device* device, GroveMode mode) { + const auto* config = GET_CONFIG(device); + auto* data = GET_DATA(device); + + if (mode == GROVE_MODE_DISABLED) { + LOG_I(TAG, "%s: Grove port disabled", device->name); + data->current_mode = GROVE_MODE_DISABLED; + return ERROR_NONE; + } + + data->child_device = new(std::nothrow) Device(); + if (!data->child_device) { + return ERROR_OUT_OF_MEMORY; + } + std::memset(data->child_device, 0, sizeof(Device)); + + size_t name_len = std::strlen(device->name) + 10; + data->child_name = new(std::nothrow) char[name_len]; + if (!data->child_name) { + delete data->child_device; + data->child_device = nullptr; + return ERROR_OUT_OF_MEMORY; + } + + data->child_device->parent = device; + const char* compatible = nullptr; + + if (mode == GROVE_MODE_UART) { + // Device name + std::snprintf(data->child_name, name_len, "%s_uart", device->name); + data->child_device->name = data->child_name; + // Device config + auto* uart_cfg = new(std::nothrow) struct Esp32UartConfig(); + if (!uart_cfg) { + delete[] data->child_name; + data->child_name = nullptr; + delete data->child_device; + data->child_device = nullptr; + return ERROR_OUT_OF_MEMORY; + } + std::memset(uart_cfg, 0, sizeof(Esp32UartConfig)); + uart_cfg->port = config->uartPort; + uart_cfg->pin_tx = config->pinSclTx; + uart_cfg->pin_rx = config->pinSdaRx; + uart_cfg->pin_cts = GPIO_PIN_SPEC_NONE; + uart_cfg->pin_rts = GPIO_PIN_SPEC_NONE; + data->child_config = uart_cfg; + compatible = "espressif,esp32-uart"; + LOG_I(TAG, "%s: starting UART mode on port %d", device->name, (int)uart_cfg->port); + } else if (mode == GROVE_MODE_I2C) { + // Device name + std::snprintf(data->child_name, name_len, "%s_i2c", device->name); + data->child_device->name = data->child_name; + // Device config + auto* i2c_cfg = new (std::nothrow) struct Esp32I2cMasterConfig(); + if (!i2c_cfg) { + delete[] data->child_name; + data->child_name = nullptr; + delete data->child_device; + data->child_device = nullptr; + return ERROR_OUT_OF_MEMORY; + } + std::memset(i2c_cfg, 0, sizeof(Esp32I2cMasterConfig)); + i2c_cfg->port = static_cast(config->i2cPort); + i2c_cfg->clockFrequency = config->i2cClockFrequency; + i2c_cfg->pinSda = config->pinSdaRx; + i2c_cfg->pinScl = config->pinSclTx; + // New driver seems to require pull-up setting + i2c_cfg->pinSda.flags |= GPIO_FLAG_PULL_UP; + i2c_cfg->pinScl.flags |= GPIO_FLAG_PULL_UP; + i2c_cfg->clkSource = 0; // Default + data->child_config = i2c_cfg; + compatible = "espressif,esp32-i2c-master"; + LOG_I(TAG, "%s: starting I2C mode on port %d", device->name, (int)config->i2cPort); + } else { + LOG_E(TAG, "%s: unknown mode %d", device->name, mode); + if (data->child_name != nullptr) { + delete[] data->child_name; + data->child_name = nullptr; + } + delete data->child_device; + data->child_device = nullptr; + return ERROR_INVALID_ARGUMENT; + } + + data->child_device->config = data->child_config; + + error_t err = device_construct_add_start(data->child_device, compatible); + if (err != ERROR_NONE) { + LOG_E(TAG, "%s: failed to start child device: %d", device->name, err); + stop_child(device); + return err; + } + + data->current_mode = mode; + return ERROR_NONE; +} + +static error_t start_device(Device* device) { + const auto* config = GET_CONFIG(device); + + auto* data = new(std::nothrow) Esp32GroveInternal(); + if (!data) return ERROR_OUT_OF_MEMORY; + device_set_driver_data(device, data); + + if (start_child(device, config->defaultMode) != ERROR_NONE) { + device_set_driver_data(device, nullptr); + delete data; + return ERROR_RESOURCE; + } + + return ERROR_NONE; +} + +static error_t stop_device(Device* device) { + auto* data = GET_DATA(device); + if (!data) return ERROR_NONE; + + stop_child(device); + delete data; + device_set_driver_data(device, nullptr); + + return ERROR_NONE; +} + +static error_t esp32_grove_set_mode(Device* device, enum GroveMode mode) { + auto* data = GET_DATA(device); + if (data->current_mode == mode) return ERROR_NONE; + + error_t err = stop_child(device); + if (err != ERROR_NONE) return err; + + return start_child(device, mode); +} + +static error_t esp32_grove_get_mode(Device* device, enum GroveMode* mode) { + auto* data = GET_DATA(device); + *mode = data->current_mode; + return ERROR_NONE; +} + +static const GroveApi esp32_grove_api = { + .set_mode = esp32_grove_set_mode, + .get_mode = esp32_grove_get_mode +}; + +extern Module platform_esp32_module; + +Driver esp32_grove_driver = { + .name = "esp32_grove", + .compatible = (const char*[]) { "espressif,esp32-grove", nullptr }, + .start_device = start_device, + .stop_device = stop_device, + .api = &esp32_grove_api, + .device_type = &GROVE_TYPE, + .owner = &platform_esp32_module, + .internal = nullptr +}; + +} // extern "C" diff --git a/Platforms/platform-esp32/source/drivers/esp32_i2c_master.cpp b/Platforms/platform-esp32/source/drivers/esp32_i2c_master.cpp index 4d631845..6d51da2c 100644 --- a/Platforms/platform-esp32/source/drivers/esp32_i2c_master.cpp +++ b/Platforms/platform-esp32/source/drivers/esp32_i2c_master.cpp @@ -308,4 +308,8 @@ Driver esp32_i2c_master_driver = { .internal = nullptr }; +i2c_master_bus_handle_t esp32_i2c_master_get_bus_handle(Device* device) { + return GET_DATA(device)->bus_handle; +} + } // extern "C" diff --git a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp index 4185c392..5eee7e5d 100644 --- a/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp +++ b/Platforms/platform-esp32/source/drivers/usb/esp32_usbhost.cpp @@ -71,9 +71,7 @@ static error_t start_device(struct Device* device) { .intr_flags = ESP_INTR_FLAG_LEVEL1, .enum_filter_cb = nullptr, .fifo_settings_custom = {}, -#if CONFIG_IDF_TARGET_ESP32P4 .peripheral_map = cfg->peripheral_map, -#endif }; esp_err_t ret = usb_host_install(&host_cfg); diff --git a/Platforms/platform-esp32/source/module.cpp b/Platforms/platform-esp32/source/module.cpp index 3ac65fbf..b6a3fc67 100644 --- a/Platforms/platform-esp32/source/module.cpp +++ b/Platforms/platform-esp32/source/module.cpp @@ -20,6 +20,7 @@ extern Driver esp32_sdmmc_driver; #endif extern Driver esp32_spi_driver; extern Driver esp32_uart_driver; +extern Driver esp32_grove_driver; #if defined(CONFIG_BT_NIMBLE_ENABLED) extern Driver esp32_bluetooth_driver; extern Driver esp32_ble_serial_driver; @@ -45,6 +46,7 @@ static error_t start() { #endif check(driver_construct_add(&esp32_spi_driver) == ERROR_NONE); check(driver_construct_add(&esp32_uart_driver) == ERROR_NONE); + check(driver_construct_add(&esp32_grove_driver) == ERROR_NONE); #if defined(CONFIG_BT_NIMBLE_ENABLED) check(driver_construct_add(&esp32_bluetooth_driver) == ERROR_NONE); check(driver_construct_add(&esp32_ble_serial_driver) == ERROR_NONE); @@ -84,6 +86,7 @@ static error_t stop() { #endif check(driver_remove_destruct(&esp32_spi_driver) == ERROR_NONE); check(driver_remove_destruct(&esp32_uart_driver) == ERROR_NONE); + check(driver_remove_destruct(&esp32_grove_driver) == ERROR_NONE); return ERROR_NONE; } diff --git a/Tactility/Include/Tactility/hal/i2c/I2c.h b/Tactility/Include/Tactility/hal/i2c/I2c.h deleted file mode 100644 index f4f9c34f..00000000 --- a/Tactility/Include/Tactility/hal/i2c/I2c.h +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -#include "./I2cCompat.h" -#include "Tactility/Lock.h" - -#include - -#include -#include - -namespace tt::hal::i2c { - -constexpr TickType_t defaultTimeout = 10 / portTICK_PERIOD_MS; - -enum class Status { - Started, - Stopped, - Unknown -}; - -/** Start the bus for the specified port. */ -bool start(i2c_port_t port); - -/** Stop the bus for the specified port. */ -bool stop(i2c_port_t port); - -/** @return true if the bus is started */ -bool isStarted(i2c_port_t port); - -/** @return name or nullptr */ -const char* getName(i2c_port_t port); - -/** Read bytes in master mode. */ -bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout = defaultTimeout); - -/** Read bytes from the specified register in master mode. */ -bool masterReadRegister(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout = defaultTimeout); - -/** Write bytes in master mode. */ -bool masterWrite(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout); - -/** Write bytes to a register in master mode */ -bool masterWriteRegister(i2c_port_t port, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout); - -/** - * Write multiple values to multiple registers in master mode. - * The input is as follows: { register1, value1, register2, value2, ... } - * @return false if any of the write operations failed - */ -bool masterWriteRegisterArray(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout = defaultTimeout); - -/** Write bytes and then read the response bytes in master mode*/ -bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout = defaultTimeout); - -/** @return true when a device is detected at the specified address */ -bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout = defaultTimeout); - -/** - * The lock for the specified bus. - * This can be used when calling native I2C functionality outside of Tactility. - */ -Lock& getLock(i2c_port_t port); - -} // namespace diff --git a/Tactility/Include/Tactility/hal/i2c/I2cCompat.h b/Tactility/Include/Tactility/hal/i2c/I2cCompat.h deleted file mode 100644 index d4cd78b8..00000000 --- a/Tactility/Include/Tactility/hal/i2c/I2cCompat.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#ifdef ESP_PLATFORM - -#include -#include - -#else - -#include - -enum i2c_port_t { - I2C_NUM_0 = 0, - I2C_NUM_1, - LP_I2C_NUM_0, - I2C_NUM_MAX, -}; - -enum i2c_mode_t { - I2C_MODE_MASTER, - I2C_MODE_MAX, -}; - -struct i2c_config_t { - i2c_mode_t mode; - int sda_io_num; - int scl_io_num; - bool sda_pullup_en; - bool scl_pullup_en; - union { - struct { - uint32_t clk_speed; - } master; - }; - uint32_t clk_flags; -}; - -#endif diff --git a/Tactility/Include/Tactility/hal/i2c/I2cDevice.h b/Tactility/Include/Tactility/hal/i2c/I2cDevice.h index 0f7474fa..97ad0249 100644 --- a/Tactility/Include/Tactility/hal/i2c/I2cDevice.h +++ b/Tactility/Include/Tactility/hal/i2c/I2cDevice.h @@ -1,7 +1,7 @@ #pragma once #include -#include "I2c.h" +#include namespace tt::hal::i2c { @@ -10,12 +10,13 @@ namespace tt::hal::i2c { * It helps to read and write registers. * * All read and write calls are thread-safe. + * @deprecated Use the device API from the Kernel project */ class I2cDevice : public Device { protected: - i2c_port_t port; + ::Device* controller; uint8_t address; static constexpr TickType_t DEFAULT_TIMEOUT = 1000 / portTICK_PERIOD_MS; @@ -36,11 +37,11 @@ protected: public: - explicit I2cDevice(i2c_port_t port, uint32_t address) : port(port), address(address) {} + explicit I2cDevice(::Device* controller, uint32_t address) : controller(controller), address(address) {} Type getType() const override { return Type::I2c; } - i2c_port_t getPort() const { return port; } + ::Device* getController() const { return controller; } uint8_t getAddress() const { return address; } }; diff --git a/Tactility/Private/Tactility/app/i2cscanner/I2cHelpers.h b/Tactility/Private/Tactility/app/i2cscanner/I2cHelpers.h index a898468a..21df444a 100644 --- a/Tactility/Private/Tactility/app/i2cscanner/I2cHelpers.h +++ b/Tactility/Private/Tactility/app/i2cscanner/I2cHelpers.h @@ -3,12 +3,14 @@ #include #include +struct Device; + namespace tt::app::i2cscanner { std::string getAddressText(uint8_t address); std::string getPortNamesForDropdown(); -bool getActivePortAtIndex(int32_t index, int32_t& out); +bool getActivePortAtIndex(int32_t index, struct Device** out); } diff --git a/Tactility/Source/Tactility.cpp b/Tactility/Source/Tactility.cpp index 803d583a..adb615bd 100644 --- a/Tactility/Source/Tactility.cpp +++ b/Tactility/Source/Tactility.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -27,6 +26,7 @@ #include #include +#include #include #include #include @@ -94,6 +94,7 @@ namespace app { namespace files { extern const AppManifest manifest; } namespace fileselection { extern const AppManifest manifest; } namespace gpssettings { extern const AppManifest manifest; } + namespace grovesettings { extern const AppManifest manifest; } namespace i2cscanner { extern const AppManifest manifest; } namespace imageviewer { extern const AppManifest manifest; } namespace inputdialog { extern const AppManifest manifest; } @@ -187,7 +188,11 @@ static void registerInternalApps() { addAppManifest(app::chat::manifest); #endif - if (device_exists_of_type(&UART_CONTROLLER_TYPE)) { + if (device_exists_of_type(&GROVE_TYPE)) { + addAppManifest(app::grovesettings::manifest); + } + + if (device_exists_of_type(&UART_CONTROLLER_TYPE) || device_exists_of_type(&GROVE_TYPE)) { addAppManifest(app::addgps::manifest); addAppManifest(app::gpssettings::manifest); } diff --git a/Tactility/Source/app/grovesettings/GroveSettings.cpp b/Tactility/Source/app/grovesettings/GroveSettings.cpp new file mode 100644 index 00000000..bd92c84c --- /dev/null +++ b/Tactility/Source/app/grovesettings/GroveSettings.cpp @@ -0,0 +1,79 @@ +#include + +#include + +#include +#include +#include + +#include +#include + +namespace tt::app::grovesettings { + +class GroveSettingsApp final : public App { + + std::vector<::Device*> devices; + + void collectDevices() { + devices.clear(); + device_for_each_of_type(&GROVE_TYPE, &devices, [](auto* device, auto* context) { + auto* vec = static_cast*>(context); + vec->push_back(device); + return true; + }); + } + + static void onModeChanged(lv_event_t* e) { + auto* device = static_cast<::Device*>(lv_event_get_user_data(e)); + auto* dropdown = static_cast(lv_event_get_target(e)); + auto mode = static_cast(lv_dropdown_get_selected(dropdown)); + grove_set_mode(device, mode); + } + +public: + void onShow(AppContext& app, lv_obj_t* parent) override { + collectDevices(); + + lv_obj_set_flex_flow(parent, LV_FLEX_FLOW_COLUMN); + lv_obj_set_style_pad_row(parent, 0, LV_STATE_DEFAULT); + + lvgl::toolbar_create(parent, app); + + auto* main_wrapper = lv_obj_create(parent); + lv_obj_set_flex_flow(main_wrapper, LV_FLEX_FLOW_COLUMN); + lv_obj_set_width(main_wrapper, LV_PCT(100)); + lv_obj_set_flex_grow(main_wrapper, 1); + + for (auto* device : devices) { + auto* row = lv_obj_create(main_wrapper); + lv_obj_set_size(row, LV_PCT(100), LV_SIZE_CONTENT); + lv_obj_set_style_pad_all(row, 0, LV_STATE_DEFAULT); + lv_obj_set_style_border_width(row, 0, LV_STATE_DEFAULT); + + auto* label = lv_label_create(row); + lv_label_set_text(label, device->name); + lv_obj_align(label, LV_ALIGN_LEFT_MID, 0, 0); + + auto* dropdown = lv_dropdown_create(row); + lv_dropdown_set_options(dropdown, "Disabled\nUART\nI2C"); + lv_obj_align(dropdown, LV_ALIGN_RIGHT_MID, 0, 0); + + GroveMode current = GROVE_MODE_DISABLED; + grove_get_mode(device, ¤t); + lv_dropdown_set_selected(dropdown, static_cast(current)); + + lv_obj_add_event_cb(dropdown, onModeChanged, LV_EVENT_VALUE_CHANGED, device); + } + } +}; + +extern const AppManifest manifest = { + .appId = "GroveSettings", + .appName = "Grove", + .appIcon = LVGL_ICON_SHARED_CABLE, + .appCategory = Category::Settings, + .createApp = create +}; + +} diff --git a/Tactility/Source/app/i2cscanner/I2cHelpers.cpp b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp index 97ba2d01..0329074a 100644 --- a/Tactility/Source/app/i2cscanner/I2cHelpers.cpp +++ b/Tactility/Source/app/i2cscanner/I2cHelpers.cpp @@ -1,7 +1,8 @@ #include "Tactility/app/i2cscanner/I2cHelpers.h" #include -#include + +#include #include #include @@ -19,31 +20,36 @@ std::string getAddressText(uint8_t address) { std::string getPortNamesForDropdown() { std::vector config_names; - for (int port = 0; port < I2C_NUM_MAX; ++port) { - auto native_port = static_cast(port); - if (hal::i2c::isStarted(native_port)) { - auto* name = hal::i2c::getName(native_port); - if (name != nullptr) { - config_names.push_back(name); - } + device_for_each_of_type(&I2C_CONTROLLER_TYPE, &config_names, [](auto* device, auto* context) { + auto* names = static_cast*>(context); + if (device_is_ready(device)) { + names->push_back(device->name); } - } + return true; + }); return string::join(config_names, "\n"); } -bool getActivePortAtIndex(int32_t index, int32_t& out) { - int current_index = -1; - for (int port = 0; port < I2C_NUM_MAX; ++port) { - auto native_port = static_cast(port); - if (hal::i2c::isStarted(native_port)) { - current_index++; - if (current_index == index) { - out = port; - return true; +bool getActivePortAtIndex(int32_t index, struct Device** out) { + struct Context { + int32_t targetIndex; + int32_t currentIndex; + struct Device* foundDevice; + } context = { index, 0, nullptr }; + + device_for_each_of_type(&I2C_CONTROLLER_TYPE, &context, [](auto* device, auto* ctx) { + auto* c = static_cast(ctx); + if (device_is_ready(device)) { + if (c->currentIndex == c->targetIndex) { + c->foundDevice = device; + return false; } + c->currentIndex++; } - } - return false; + return true; + }); + *out = context.foundDevice; + return context.foundDevice != nullptr; } } diff --git a/Tactility/Source/app/i2cscanner/I2cScanner.cpp b/Tactility/Source/app/i2cscanner/I2cScanner.cpp index e728e01b..c57503dd 100644 --- a/Tactility/Source/app/i2cscanner/I2cScanner.cpp +++ b/Tactility/Source/app/i2cscanner/I2cScanner.cpp @@ -1,9 +1,8 @@ #include #include -#include #include -#include +#include #include #include #include @@ -33,7 +32,7 @@ class I2cScannerApp final : public App { std::unique_ptr scanTimer = nullptr; // State ScanState scanState = ScanStateInitial; - i2c_port_t port = I2C_NUM_0; + struct Device* portDevice = nullptr; std::vector scannedAddresses; // Widgets lv_obj_t* scanButtonLabelWidget = nullptr; @@ -47,14 +46,13 @@ class I2cScannerApp final : public App { static void onSelectBusCallback(lv_event_t* event); static void onPressScanCallback(lv_event_t* event); - static void onScanTimerCallback(); void onSelectBus(lv_event_t* event); void onPressScan(lv_event_t* event); void onScanTimer(); bool shouldStopScanTimer(); - bool getPort(i2c_port_t* outPort); + bool getPort(struct Device** outPort); bool addAddressToList(uint8_t address); bool hasScanThread(); void startScanning(); @@ -140,8 +138,10 @@ void I2cScannerApp::onShow(AppContext& app, lv_obj_t* parent) { lv_obj_add_flag(scan_list, LV_OBJ_FLAG_HIDDEN); scanListWidget = scan_list; - int32_t first_port; - if (getActivePortAtIndex(0, first_port)) { + struct Device* dummy; + if (getActivePortAtIndex(selected_bus, &dummy)) { + selectBus(selected_bus); + } else if (getActivePortAtIndex(0, &dummy)) { lv_dropdown_set_selected(port_dropdown, 0); selectBus(0); } @@ -184,9 +184,9 @@ void I2cScannerApp::onPressScanCallback(lv_event_t* event) { // endregion Callbacks -bool I2cScannerApp::getPort(i2c_port_t* outPort) { +bool I2cScannerApp::getPort(struct Device** outPort) { if (mutex.lock(100 / portTICK_PERIOD_MS)) { - *outPort = this->port; + *outPort = this->portDevice; mutex.unlock(); return true; } else { @@ -219,21 +219,21 @@ bool I2cScannerApp::shouldStopScanTimer() { void I2cScannerApp::onScanTimer() { logger.info("Scan thread started"); - i2c_port_t safe_port; + Device* safe_port; if (!getPort(&safe_port)) { logger.error("Failed to get I2C port"); onScanTimerFinished(); return; } - if (!hal::i2c::isStarted(safe_port)) { + if (!device_is_ready(safe_port)) { logger.error("I2C port not started"); onScanTimerFinished(); return; } for (uint8_t address = 0; address < 128; ++address) { - if (hal::i2c::masterHasDeviceAtAddress(safe_port, address, 10 / portTICK_PERIOD_MS)) { + if (i2c_controller_has_device_at_address(safe_port, address, 10 / portTICK_PERIOD_MS) == ERROR_NONE) { logger.info("Found device at address 0x{:02X}", address); if (!shouldStopScanTimer()) { addAddressToList(address); @@ -305,14 +305,14 @@ void I2cScannerApp::onSelectBus(lv_event_t* event) { } void I2cScannerApp::selectBus(int32_t selected) { - int32_t found_port; - if (!getActivePortAtIndex(selected, found_port)) { + struct Device* found_device; + if (!getActivePortAtIndex(selected, &found_device)) { return; } if (mutex.lock(100 / portTICK_PERIOD_MS)) { scannedAddresses.clear(); - port = static_cast(found_port); + portDevice = found_device; scanState = ScanStateInitial; mutex.unlock(); } @@ -334,16 +334,6 @@ void I2cScannerApp::onPressScan(lv_event_t* event) { updateViews(); } -static bool findDeviceName(const std::vector>& devices, i2c_port_t port, uint8_t address, std::string& outName) { - for (auto& device : devices) { - if (device->getPort() == port && device->getAddress() == address) { - outName = device->getName(); - return true; - } - } - return false; -} - void I2cScannerApp::updateViews() { if (mutex.lock(100 / portTICK_PERIOD_MS)) { if (scanState == ScanStateScanning) { @@ -358,18 +348,10 @@ void I2cScannerApp::updateViews() { if (scanState == ScanStateStopped) { lv_obj_remove_flag(scanListWidget, LV_OBJ_FLAG_HIDDEN); - auto devices = hal::findDevices(hal::Device::Type::I2c); - if (!scannedAddresses.empty()) { for (auto address: scannedAddresses) { std::string address_text = getAddressText(address); - std::string device_name; - if (findDeviceName(devices, port, address, device_name)) { - auto text = std::format("{} - {}", address_text, device_name); - lv_list_add_text(scanListWidget, text.c_str()); - } else { - lv_list_add_text(scanListWidget, address_text.c_str()); - } + lv_list_add_text(scanListWidget, address_text.c_str()); } } else { lv_list_add_text(scanListWidget, "No devices found"); diff --git a/Tactility/Source/hal/i2c/I2c.cpp b/Tactility/Source/hal/i2c/I2c.cpp deleted file mode 100644 index ae8bb62e..00000000 --- a/Tactility/Source/hal/i2c/I2c.cpp +++ /dev/null @@ -1,111 +0,0 @@ -#include - -#include -#include - -#include -#include -#include - -#ifdef ESP_PLATFORM -#include -#endif - -namespace tt::hal::i2c { - -class NoLock final : public tt::Lock { - bool lock(TickType_t timeout) const override { return true; } - void unlock() const override { /* NO-OP */ } -}; - -static NoLock NO_LOCK; - -Device* findDevice(i2c_port_t port) { -#ifdef ESP_PLATFORM - struct Params { - i2c_port_t port; - Device* device; - }; - - Params params = { - .port = port, - .device = nullptr - }; - - device_for_each_of_type(&I2C_CONTROLLER_TYPE, ¶ms, [](auto* device, auto* context) { - auto* params_ptr = (Params*)context; - auto* driver = device_get_driver(device); - if (driver == nullptr) return true; - if (!driver_is_compatible(driver, "espressif,esp32-i2c")) return true; - auto* config = static_cast(device->config); - if (config->port != params_ptr->port) return true; - // Found it, stop iterating - params_ptr->device = device; - return false; - }); - - return params.device; -#else - return nullptr; -#endif -} - -bool isStarted(i2c_port_t port) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return device_is_ready(device); -} - -const char* getName(i2c_port_t port) { - auto* device = findDevice(port); - if (device == nullptr) return nullptr; - return device->name; -} - -bool masterRead(i2c_port_t port, uint8_t address, uint8_t* data, size_t dataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_read(device, address, data, dataSize, timeout) == ERROR_NONE; -} - -bool masterReadRegister(i2c_port_t port, uint8_t address, uint8_t reg, uint8_t* data, size_t dataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_read_register(device, address, reg, data, dataSize, timeout) == ERROR_NONE; -} - -bool masterWrite(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_write(device, address, data, dataSize, timeout) == ERROR_NONE; -} - -bool masterWriteRegister(i2c_port_t port, uint8_t address, uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_write_register(device, address, reg, data, dataSize, timeout) == ERROR_NONE; -} - -bool masterWriteRegisterArray(i2c_port_t port, uint8_t address, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_write_register_array(device, address, data, dataSize, timeout) == ERROR_NONE; -} - -bool masterWriteRead(i2c_port_t port, uint8_t address, const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_write_read(device, address, writeData, writeDataSize, readData, readDataSize, timeout) == ERROR_NONE; -} - -bool masterHasDeviceAtAddress(i2c_port_t port, uint8_t address, TickType_t timeout) { - auto* device = findDevice(port); - if (device == nullptr) return false; - return i2c_controller_has_device_at_address(device, address, timeout) == ERROR_NONE; -} - -Lock& getLock(i2c_port_t port) { - return NO_LOCK; -} - -} // namespace diff --git a/Tactility/Source/hal/i2c/I2cDevice.cpp b/Tactility/Source/hal/i2c/I2cDevice.cpp index 3cc44080..a59e455f 100644 --- a/Tactility/Source/hal/i2c/I2cDevice.cpp +++ b/Tactility/Source/hal/i2c/I2cDevice.cpp @@ -5,24 +5,24 @@ namespace tt::hal::i2c { bool I2cDevice::read(uint8_t* data, size_t dataSize, TickType_t timeout) { - return masterRead(port, address, data, dataSize, timeout); + return i2c_controller_read(controller, address, data, dataSize, timeout) == ERROR_NONE; } bool I2cDevice::write(const uint8_t* data, uint16_t dataSize, TickType_t timeout) { - return masterWrite(port, address, data, dataSize, timeout); + return i2c_controller_write(controller, address, data, dataSize, timeout) == ERROR_NONE; } bool I2cDevice::writeRead(const uint8_t* writeData, size_t writeDataSize, uint8_t* readData, size_t readDataSize, TickType_t timeout) { - return masterWriteRead(port, address, writeData, writeDataSize, readData, readDataSize, timeout); + return i2c_controller_write_read(controller, address, writeData, writeDataSize, readData, readDataSize, timeout) == ERROR_NONE; } bool I2cDevice::writeRegister(uint8_t reg, const uint8_t* data, uint16_t dataSize, TickType_t timeout) { - return masterWriteRegister(port, address, reg, data, dataSize, timeout); + return i2c_controller_write_register(controller, address, reg, data, dataSize, timeout) == ERROR_NONE; } bool I2cDevice::readRegister12(uint8_t reg, float& out) const { std::uint8_t data[2] = {0}; - if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) { + if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) { out = (data[0] & 0x0F) << 8 | data[1]; return true; } else { @@ -32,7 +32,7 @@ bool I2cDevice::readRegister12(uint8_t reg, float& out) const { bool I2cDevice::readRegister14(uint8_t reg, float& out) const { std::uint8_t data[2] = {0}; - if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) { + if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) { out = (data[0] & 0x3F) << 8 | data[1]; return true; } else { @@ -42,7 +42,7 @@ bool I2cDevice::readRegister14(uint8_t reg, float& out) const { bool I2cDevice::readRegister16(uint8_t reg, uint16_t& out) const { std::uint8_t data[2] = {0}; - if (masterReadRegister(port, address, reg, data, 2, DEFAULT_TIMEOUT)) { + if (i2c_controller_read_register(controller, address, reg, data, 2, DEFAULT_TIMEOUT) == ERROR_NONE) { out = data[0] << 8 | data[1]; return true; } else { @@ -51,11 +51,11 @@ bool I2cDevice::readRegister16(uint8_t reg, uint16_t& out) const { } bool I2cDevice::readRegister8(uint8_t reg, uint8_t& result) const { - return masterWriteRead(port, address, ®, 1, &result, 1, DEFAULT_TIMEOUT); + return i2c_controller_write_read(controller, address, ®, 1, &result, 1, DEFAULT_TIMEOUT) == ERROR_NONE; } bool I2cDevice::writeRegister8(uint8_t reg, uint8_t value) const { - return masterWriteRegister(port, address, reg, &value, 1, DEFAULT_TIMEOUT); + return i2c_controller_write_register(controller, address, reg, &value, 1, DEFAULT_TIMEOUT) == ERROR_NONE; } bool I2cDevice::bitOn(uint8_t reg, uint8_t bitmask) const { diff --git a/TactilityKernel/include/tactility/drivers/grove.h b/TactilityKernel/include/tactility/drivers/grove.h new file mode 100644 index 00000000..37dfc2be --- /dev/null +++ b/TactilityKernel/include/tactility/drivers/grove.h @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: Apache-2.0 +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +/** + * @brief Grove Port modes + */ +enum GroveMode { + GROVE_MODE_DISABLED = 0, + GROVE_MODE_UART = 1, + GROVE_MODE_I2C = 2 +}; + +/** + * @brief API for Grove drivers. + * + * The grove driver is meant for external interfaces with two wires that can be used as UART, I2C or GPIO as needed. + * It can be used with the Grove connector, but also with others such as Stemma QT. + */ +struct GroveApi { + /** + * @brief Sets the Grove port mode. + * @param[in] device the Grove device + * @param[in] mode the new mode + * @return ERROR_NONE on success + */ + error_t (*set_mode)(struct Device* device, enum GroveMode mode); + + /** + * @brief Gets the current Grove port mode. + * @param[in] device the Grove device + * @param[out] mode pointer to store the current mode + * @return ERROR_NONE on success + */ + error_t (*get_mode)(struct Device* device, enum GroveMode* mode); +}; + +/** + * @brief Sets the Grove port mode using the specified device. + */ +error_t grove_set_mode(struct Device* device, enum GroveMode mode); + +/** + * @brief Gets the current Grove port mode using the specified device. + */ +error_t grove_get_mode(struct Device* device, enum GroveMode* mode); + +extern const struct DeviceType GROVE_TYPE; + +#ifdef __cplusplus +} +#endif diff --git a/TactilityKernel/source/drivers/grove.cpp b/TactilityKernel/source/drivers/grove.cpp new file mode 100644 index 00000000..37c1cb79 --- /dev/null +++ b/TactilityKernel/source/drivers/grove.cpp @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: Apache-2.0 +#include +#include + +#define GROVE_DRIVER_API(driver) ((struct GroveApi*)driver->api) + +extern "C" { + +error_t grove_set_mode(struct Device* device, enum GroveMode mode) { + const auto* driver = device_get_driver(device); + return GROVE_DRIVER_API(driver)->set_mode(device, mode); +} + +error_t grove_get_mode(struct Device* device, enum GroveMode* mode) { + const auto* driver = device_get_driver(device); + return GROVE_DRIVER_API(driver)->get_mode(device, mode); +} + +const struct DeviceType GROVE_TYPE { + .name = "grove" +}; + +}