diff --git a/src/drivers/CMakeLists.txt b/src/drivers/CMakeLists.txt index 072a8b68..413e1ea0 100644 --- a/src/drivers/CMakeLists.txt +++ b/src/drivers/CMakeLists.txt @@ -1,6 +1,6 @@ idf_component_register( SRCS "touchwheel.cpp" "dac.cpp" "gpio_expander.cpp" "battery.cpp" "storage.cpp" "i2c.cpp" - "spi.cpp" "display.cpp" "display_init.cpp" "driver_cache.cpp" + "spi.cpp" "display.cpp" "display_init.cpp" "driver_cache.cpp" "samd.cpp" INCLUDE_DIRS "include" REQUIRES "esp_adc" "fatfs" "result" "lvgl" "span") target_compile_options(${COMPONENT_LIB} PRIVATE ${EXTRA_WARNINGS}) diff --git a/src/drivers/battery.cpp b/src/drivers/battery.cpp index 8d747c07..92b269de 100644 --- a/src/drivers/battery.cpp +++ b/src/drivers/battery.cpp @@ -42,6 +42,7 @@ Battery::Battery() { Battery::~Battery() { adc_cali_delete_scheme_line_fitting(adc_calibration_handle_); + ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle_)); } auto Battery::Millivolts() -> uint32_t { diff --git a/src/drivers/include/samd.hpp b/src/drivers/include/samd.hpp new file mode 100644 index 00000000..4ba5877c --- /dev/null +++ b/src/drivers/include/samd.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace drivers { + +class Samd { + public: + Samd(); + ~Samd(); + + enum class ChargeStatus { + // There is no battery plugged into the device. + kNoBattery, + // The battery is discharging, and the current voltage level is very low. + kBatteryCritical, + // The battery is discharging. + kDischarging, + // The battery is charging over a low-current USB connection + kChargingRegular, + // The battery is charging over a high-current USB connection + kChargingFast, + // The battery is full charged, and we are still plugged in. + kFullCharge, + }; + + auto ReadChargeStatus() -> std::optional; + + enum class UsbMscStatus { + // There is no compatible usb host attached. + kDetached, + // There is a compatible usb host attached, but USB MSC is not currently + // in use by the SAMD. + kAttachedIdle, + // The SAMD is currently exposing the SD card via USB MSC. + kAttachedMounted, + }; + + auto WriteAllowUsbMsc(bool is_allowed) -> void; + auto ReadUsbMscStatus() -> UsbMscStatus; + + auto ReadFlashingEnabled() -> bool; + auto WriteFlashingEnabled(bool) -> void; +}; + +} // namespace drivers diff --git a/src/drivers/samd.cpp b/src/drivers/samd.cpp new file mode 100644 index 00000000..3b275c47 --- /dev/null +++ b/src/drivers/samd.cpp @@ -0,0 +1,109 @@ +#include "samd.hpp" +#include +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "hal/i2c_types.h" +#include "i2c.hpp" + +enum Registers { + kRegisterVersion = 0, + kRegisterCharge = 1, + kRegisterUsbMsc = 2, + kRegisterFlashing = 3, +}; + +static const uint8_t kAddress = 0x45; +static const char kTag[] = "SAMD"; + +namespace drivers { + +Samd::Samd() { + // Being able to interface with the SAMD properly is critical. To ensure we + // will be able to, we begin by checking the I2C protocol version is + // compatible, and throw if it's not. + uint8_t raw_res = 0; + I2CTransaction transaction; + transaction.start() + .write_addr(kAddress, I2C_MASTER_WRITE) + .write_ack(kRegisterVersion) + .write_addr(kAddress, I2C_MASTER_READ) + .read(&raw_res, I2C_MASTER_NACK) + .stop(); + ESP_LOGI(kTag, "checking samd firmware rev"); + ESP_ERROR_CHECK(transaction.Execute()); + ESP_LOGI(kTag, "samd firmware: %u", raw_res); +} +Samd::~Samd() {} + +auto Samd::ReadChargeStatus() -> std::optional { + uint8_t raw_res; + I2CTransaction transaction; + transaction.start() + .write_addr(kAddress, I2C_MASTER_WRITE) + .write_ack(kRegisterCharge) + .write_addr(kAddress, I2C_MASTER_READ) + .read(&raw_res, I2C_MASTER_NACK) + .stop(); + ESP_LOGI(kTag, "checking charge status"); + ESP_ERROR_CHECK(transaction.Execute()); + ESP_LOGI(kTag, "raw charge status: %x", raw_res); + + uint8_t usb_state = raw_res & 0b11; + uint8_t charge_state = (raw_res >> 2) & 0b111; + switch (charge_state) { + case 0: + return ChargeStatus::kNoBattery; + case 1: + return usb_state == 1 ? ChargeStatus::kChargingRegular + : ChargeStatus::kChargingFast; + case 2: + return ChargeStatus::kFullCharge; + case 4: + return ChargeStatus::kBatteryCritical; + case 5: + return ChargeStatus::kDischarging; + case 3: + // Fall-through. + default: + return {}; + } +} + +auto Samd::WriteAllowUsbMsc(bool is_allowed) -> void { + I2CTransaction transaction; + transaction.start() + .write_addr(kAddress, I2C_MASTER_WRITE) + .write_ack(kRegisterUsbMsc, is_allowed) + .stop(); + ESP_ERROR_CHECK(transaction.Execute()); +} + +auto Samd::ReadUsbMscStatus() -> UsbMscStatus { + return UsbMscStatus::kDetached; +} + +auto Samd::ReadFlashingEnabled() -> bool { + uint8_t raw_res; + I2CTransaction transaction; + transaction.start() + .write_addr(kAddress, I2C_MASTER_WRITE) + .write_ack(kRegisterVersion) + .write_addr(kAddress, I2C_MASTER_READ) + .read(&raw_res, I2C_MASTER_NACK) + .stop(); + ESP_ERROR_CHECK(transaction.Execute()); + return raw_res; +} + +auto Samd::WriteFlashingEnabled(bool is_enabled) -> void { + I2CTransaction transaction; + transaction.start() + .write_addr(kAddress, I2C_MASTER_WRITE) + .write_ack(kRegisterFlashing, is_enabled) + .stop(); + ESP_ERROR_CHECK(transaction.Execute()); +} + +} // namespace drivers diff --git a/src/main/main.cpp b/src/main/main.cpp index 07547713..29ac2c7f 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -31,6 +31,7 @@ #include "gpio_expander.hpp" #include "i2c.hpp" #include "lvgl_task.hpp" +#include "samd.hpp" #include "spi.hpp" #include "storage.hpp" #include "touchwheel.hpp" @@ -48,6 +49,16 @@ extern "C" void app_main(void) { ESP_LOGI(TAG, "Init GPIOs"); drivers::GpioExpander* expander = drivers->AcquireGpios(); + ESP_LOGI(TAG, "Init SAMD comms"); + drivers::Samd samd; + ESP_LOGI(TAG, "It might have worked? Let's read something!"); + auto res = samd.ReadChargeStatus(); + if (res) { + ESP_LOGI(TAG, "Charge status is %d", static_cast(*res)); + } else { + ESP_LOGI(TAG, "no charge status?"); + } + ESP_LOGI(TAG, "Enable power rails for development"); expander->with( [&](auto& gpio) { gpio.set_pin(drivers::GpioExpander::AMP_EN, 1); });