diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 1d8ac12f..2532a486 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -292,6 +292,7 @@ set(CPPSRC apps/ui_btle_rx.cpp # apps/ui_coasterp.cpp apps/ui_debug.cpp + apps/ui_debug_battery.cpp apps/ui_dfu_menu.cpp apps/ui_encoders.cpp apps/ui_fileman.cpp @@ -306,7 +307,7 @@ set(CPPSRC apps/ui_looking_glass_app.cpp apps/ui_mictx.cpp apps/ui_modemsetup.cpp - apps/ui_morse.cpp + # apps/ui_morse.cpp # apps/ui_nrf_rx.cpp # apps/ui_nuoptix.cpp apps/ui_playlist.cpp diff --git a/firmware/application/apps/ui_battinfo.cpp b/firmware/application/apps/ui_battinfo.cpp index 4dcc38d5..fa875390 100644 --- a/firmware/application/apps/ui_battinfo.cpp +++ b/firmware/application/apps/ui_battinfo.cpp @@ -49,7 +49,10 @@ void BattinfoView::update_result() { text_voltage.set("UNKNOWN"); text_current.set("-"); text_charge.set("-"); + text_cycles.set("-"); + text_ttef.set("-"); text_method.set("-"); + text_warn.set(""); return; } bool uichg = false; @@ -73,11 +76,52 @@ void BattinfoView::update_result() { text_current.set(to_string_dec_int(current) + " mA"); text_charge.set(current >= 0 ? "Charging" : "Discharging"); labels_opt.hidden(false); + + text_ttef.hidden(false); } else { if (!labels_opt.hidden()) uichg = true; labels_opt.hidden(true); text_current.hidden(true); text_charge.hidden(true); + text_cycles.hidden(true); + text_ttef.hidden(true); + text_warn.set(""); + } + if ((valid_mask & battery::BatteryManagement::BATT_VALID_CYCLES) == battery::BatteryManagement::BATT_VALID_CYCLES) { + text_cycles.hidden(false); + uint16_t cycles = battery::BatteryManagement::get_cycles(); + if (cycles < 2) + text_warn.set("SoC improves after 2 cycles"); + else + text_warn.set(""); + text_cycles.set(to_string_dec_uint(cycles)); + } else { + text_cycles.hidden(true); + text_warn.set(""); + } + if ((valid_mask & battery::BatteryManagement::BATT_VALID_TTEF) == battery::BatteryManagement::BATT_VALID_TTEF) { + text_ttef.hidden(false); + float ttef = 0; + if (current <= 0) { + ttef = battery::BatteryManagement::get_tte(); + } else { + ttef = battery::BatteryManagement::get_ttf(); + } + + // Convert ttef to hours and minutes + uint8_t hours = static_cast(ttef); + uint8_t minutes = static_cast((ttef - hours) * 60 + 0.5); // +0.5 for rounding + + // Create the formatted string + std::string formatted_time; + if (hours > 0) { + formatted_time += to_string_dec_uint(hours) + "h "; + } + formatted_time += to_string_dec_uint(minutes) + "m"; + + text_ttef.set(formatted_time); + } else { + text_ttef.hidden(true); } if ((valid_mask & battery::BatteryManagement::BATT_VALID_PERCENT) == battery::BatteryManagement::BATT_VALID_PERCENT) { text_method.set("IC"); @@ -102,7 +146,10 @@ BattinfoView::BattinfoView(NavigationView& nav) &text_charge, &text_method, &button_mode, - &button_exit}); + &button_exit, + &text_cycles, + &text_warn, + &text_ttef}); button_exit.on_select = [this, &nav](Button&) { nav.pop(); diff --git a/firmware/application/apps/ui_battinfo.hpp b/firmware/application/apps/ui_battinfo.hpp index 7500bf47..83b6112c 100644 --- a/firmware/application/apps/ui_battinfo.hpp +++ b/firmware/application/apps/ui_battinfo.hpp @@ -55,12 +55,15 @@ class BattinfoView : public View { {{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground}, {{2 * 8, 3 * 16}, "Method:", Theme::getInstance()->fg_light->foreground}, - {{2 * 8, 7 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground}, }; Labels labels_opt{ {{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground}, - {{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}}; + {{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}, + {{2 * 8, 6 * 16}, "TTF/E:", Theme::getInstance()->fg_light->foreground}, + {{2 * 8, 7 * 16}, "Cycles:", Theme::getInstance()->fg_light->foreground}, + {{2 * 8, 10 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground}, + }; Text text_percent{ {13 * 8, 1 * 16, 10 * 16, 16}, @@ -77,9 +80,19 @@ class BattinfoView : public View { Text text_charge{ {13 * 8, 5 * 16, 10 * 16, 16}, "-"}; + Text text_ttef{ + {13 * 8, 6 * 16, 10 * 16, 16}, + "-"}; + Text text_cycles{ + {13 * 8, 7 * 16, 10 * 16, 16}, + "-"}; + + Text text_warn{ + {2 * 8, 8 * 16, 30 * 8, 2 * 16}, + ""}; Button button_mode{ - {2 * 8, 8 * 16 + 5, 5 * 16, 32}, + {2 * 8, 11 * 16 + 5, 5 * 16, 32}, "Volt"}; Button button_exit{ diff --git a/firmware/application/apps/ui_debug.cpp b/firmware/application/apps/ui_debug.cpp index 33112e93..54844d6f 100644 --- a/firmware/application/apps/ui_debug.cpp +++ b/firmware/application/apps/ui_debug.cpp @@ -36,6 +36,7 @@ #include "ui_font_fixed_8x16.hpp" #include "ui_painter.hpp" #include "ui_external_items_menu_loader.hpp" +#include "ui_debug_battery.hpp" #include "portapack.hpp" #include "portapack_persistent_memory.hpp" @@ -511,6 +512,11 @@ void DebugMenuView::on_populate() { {"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push(); }}, }); + if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) { + add_item( + {"Battery", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_batt_icon, [this]() { nav_.push(); }}); + } + for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) { add_item(gridItem); }; diff --git a/firmware/application/apps/ui_debug_battery.cpp b/firmware/application/apps/ui_debug_battery.cpp new file mode 100644 index 00000000..2099d02c --- /dev/null +++ b/firmware/application/apps/ui_debug_battery.cpp @@ -0,0 +1,104 @@ +#include "ui_debug_battery.hpp" +#include "string_format.hpp" + +namespace ui { + +BatteryCapacityView::RegisterEntry BatteryCapacityView::get_entry(size_t index) { + if (index < battery::max17055::MAX17055::entries_count) { + return battery::max17055::MAX17055::entries[index]; + } + return {"", 0, "", 0, false, "", false, 0, false, false, false, 0, false}; +} + +BatteryCapacityView::BatteryCapacityView(NavigationView& nav) { + for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) { + name_texts[i].set_parent_rect({0 * 8, static_cast((i + 1) * 16), 8 * 8, 16}); + addr_texts[i].set_parent_rect({9 * 8, static_cast((i + 1) * 16), 4 * 8, 16}); + hex_texts[i].set_parent_rect({14 * 8, static_cast((i + 1) * 16), 6 * 8, 16}); + value_texts[i].set_parent_rect({21 * 8, static_cast((i + 1) * 16), 10 * 8, 16}); + + add_child(&name_texts[i]); + add_child(&addr_texts[i]); + add_child(&hex_texts[i]); + add_child(&value_texts[i]); + } + + add_children({&labels, &page_text, &button_done}); + + button_done.on_select = [&nav](Button&) { nav.pop(); }; + + populate_page(0); + update_page_text(); +} + +void BatteryCapacityView::focus() { + button_done.focus(); +} + +bool BatteryCapacityView::on_encoder(const EncoderEvent delta) { + int32_t new_page = current_page + delta; + if (new_page >= 0 && new_page < ((int32_t)battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE) { + current_page = new_page; + populate_page(current_page * ENTRIES_PER_PAGE); + update_page_text(); + } + return true; +} + +void BatteryCapacityView::update_values() { + for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) { + size_t entry_index = current_page * ENTRIES_PER_PAGE + i; + if (entry_index < battery::max17055::MAX17055::entries_count) { + const auto entry = get_entry(entry_index); + uint16_t raw_value = battery::BatteryManagement::read_register(entry.address); + + hex_texts[i].set("0x" + to_string_hex(raw_value, 4)); + + float scaled_value; + if (entry.is_signed) { + int16_t signed_value = static_cast(raw_value); + scaled_value = signed_value * entry.scalar; + } else { + scaled_value = raw_value * entry.scalar; + } + + // Format the value with appropriate decimal places + std::string formatted_value; + if (entry.resolution > 0) { + formatted_value = to_string_decimal(scaled_value, std::min(entry.resolution, 3)); + } else { + formatted_value = to_string_dec_int(scaled_value); // Show up to 3 decimal places + } + + value_texts[i].set(formatted_value + " " + entry.unit); + } + } +} + +void BatteryCapacityView::populate_page(int start_index) { + for (size_t i = 0; i < ENTRIES_PER_PAGE; ++i) { + size_t entry_index = start_index + i; + if (entry_index < battery::max17055::MAX17055::entries_count) { + const auto entry = get_entry(entry_index); + name_texts[i].set(entry.name); + addr_texts[i].set("0x" + to_string_hex(entry.address, 2)); + name_texts[i].hidden(false); + addr_texts[i].hidden(false); + hex_texts[i].hidden(false); + value_texts[i].hidden(false); + } else { + name_texts[i].hidden(true); + addr_texts[i].hidden(true); + hex_texts[i].hidden(true); + value_texts[i].hidden(true); + } + } + update_values(); +} + +void BatteryCapacityView::update_page_text() { + int total_pages = (battery::max17055::MAX17055::entries_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; + page_text.set("Page " + to_string_dec_uint(current_page + 1) + "/" + to_string_dec_uint(total_pages)); +} + +} // namespace ui \ No newline at end of file diff --git a/firmware/application/apps/ui_debug_battery.hpp b/firmware/application/apps/ui_debug_battery.hpp new file mode 100644 index 00000000..3fafa305 --- /dev/null +++ b/firmware/application/apps/ui_debug_battery.hpp @@ -0,0 +1,48 @@ +#ifndef __UI_DEBUG_BATTERY_HPP__ +#define __UI_DEBUG_BATTERY_HPP__ + +#include "ui.hpp" +#include "ui_widget.hpp" +#include "ui_navigation.hpp" +#include "battery.hpp" +#include "max17055.hpp" + +namespace ui { + +class BatteryCapacityView : public View { + public: + BatteryCapacityView(NavigationView& nav); + void focus() override; + std::string title() const override { return "Battery Registers"; } + + bool on_encoder(const EncoderEvent delta) override; + + using RegisterEntry = battery::max17055::RegisterEntry; + + private: + static RegisterEntry get_entry(size_t index); + + Labels labels{ + {{0 * 8, 0 * 16}, "Reg", Theme::getInstance()->fg_yellow->foreground}, + {{9 * 8, 0 * 16}, "Addr", Theme::getInstance()->fg_yellow->foreground}, + {{14 * 8, 0 * 16}, "Hex", Theme::getInstance()->fg_yellow->foreground}, + {{21 * 8, 0 * 16}, "Value", Theme::getInstance()->fg_yellow->foreground}, + }; + std::array name_texts = {}; + std::array addr_texts = {}; + std::array hex_texts = {}; + std::array value_texts = {}; + + Text page_text{{144, 284, 80, 16}, "Page 1/1"}; + Button button_done{{16, 280, 96, 24}, "Done"}; + + void update_values(); + void populate_page(int start_index); + void update_page_text(); + int current_page = 0; + static constexpr int ENTRIES_PER_PAGE = 16; +}; + +} // namespace ui + +#endif // __UI_DEBUG_BATTERY_HPP__ \ No newline at end of file diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 3edab611..3988014e 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -969,6 +969,8 @@ SetBatteryView::SetBatteryView(NavigationView& nav) { &button_cancel, &checkbox_overridebatt}); + if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BATT_MAX17055) add_children({&button_reset, &labels2}); + button_save.on_select = [&nav, this](Button&) { pmem::set_ui_override_batt_calc(checkbox_overridebatt.value()); battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value()); @@ -976,6 +978,13 @@ SetBatteryView::SetBatteryView(NavigationView& nav) { nav.pop(); }; + button_reset.on_select = [&nav, this](Button&) { + if (battery::BatteryManagement::reset_learned()) + nav.display_modal("Reset", "Battery parameters reset"); + else + nav.display_modal("Error", "Error parameter reset"); + }; + checkbox_overridebatt.set_value(pmem::ui_override_batt_calc()); button_cancel.on_select = [&nav, this](Button&) { @@ -1017,7 +1026,7 @@ void SettingsMenuView::on_populate() { {"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push(); }}, {"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push(); }}, }); - if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push(); }}); + if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_batt_icon, [this]() { nav_.push(); }}); } } /* namespace ui */ diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 048f67e8..4a94d90c 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -906,13 +906,14 @@ class SetBatteryView : public View { Labels labels{ {{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground}, {{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}}; - + Labels labels2{ + {{1 * 8, 6 * 16}, "Reset IC's learned params.", Theme::getInstance()->fg_light->foreground}}; Button button_save{ {2 * 8, 16 * 16, 12 * 8, 32}, "Save"}; Checkbox checkbox_overridebatt{ - {2 * 8, 6 * 16}, + {2 * 8, 4 * 16}, 23, "Override"}; @@ -920,6 +921,11 @@ class SetBatteryView : public View { {16 * 8, 16 * 16, 12 * 8, 32}, "Cancel", }; + + Button button_reset{ + {2 * 8, 8 * 16, 12 * 8, 32}, + "Reset", + }; }; class SettingsMenuView : public BtnGridView { diff --git a/firmware/application/external/blespam/main.cpp b/firmware/application/external/blespam/main.cpp index 1756f388..aa8e8b2a 100644 --- a/firmware/application/external/blespam/main.cpp +++ b/firmware/application/external/blespam/main.cpp @@ -76,7 +76,7 @@ __attribute__((section(".external_app.app_blespam.application_information"), use /*.icon_color = */ ui::Color::yellow().v, /*.menu_location = */ app_location_t::TX, - /*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'B', 'T', 'T'}, + /*.m4_app_tag = portapack::spi_flash::image_tag_btle_tx */ {'P', 'B', 'T', 'T'}, /*.m4_app_offset = */ 0x00000000, // will be filled at compile time }; } diff --git a/firmware/application/external/external.cmake b/firmware/application/external/external.cmake index a5ad1754..43791dca 100644 --- a/firmware/application/external/external.cmake +++ b/firmware/application/external/external.cmake @@ -88,6 +88,11 @@ set(EXTCPPSRC #adsbtx external/adsbtx/main.cpp external/adsbtx/ui_adsb_tx.cpp + + + #morse_tx + external/morse_tx/main.cpp + external/morse_tx/ui_morse.cpp ) set(EXTAPPLIST @@ -112,4 +117,5 @@ set(EXTAPPLIST tpmsrx protoview adsbtx + morse_tx ) diff --git a/firmware/application/external/external.ld b/firmware/application/external/external.ld index 980a9ec9..7c574eb1 100644 --- a/firmware/application/external/external.ld +++ b/firmware/application/external/external.ld @@ -44,6 +44,7 @@ MEMORY ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k + ram_external_app_morse_tx(rwx) : org = 0xADC60000, len = 32k } SECTIONS @@ -176,4 +177,11 @@ SECTIONS } > ram_external_app_adsbtx + .external_app_morse_tx : ALIGN(4) SUBALIGN(4) + { + KEEP(*(.external_app.app_morse_tx.application_information)); + *(*ui*external_app*morse_tx*); + } > ram_external_app_morse_tx + + } diff --git a/firmware/application/external/morse_tx/main.cpp b/firmware/application/external/morse_tx/main.cpp new file mode 100644 index 00000000..a1d499a1 --- /dev/null +++ b/firmware/application/external/morse_tx/main.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "ui.hpp" +#include "ui_morse.hpp" +#include "ui_navigation.hpp" +#include "external_app.hpp" + +namespace ui::external_app::morse_tx { +void initialize_app(ui::NavigationView& nav) { + nav.push(); +} +} // namespace ui::external_app::morse_tx + +extern "C" { + +__attribute__((section(".external_app.app_morse_tx.application_information"), used)) application_information_t _application_information_morse_tx = { + /*.memory_location = */ (uint8_t*)0x00000000, + /*.externalAppEntry = */ ui::external_app::morse_tx::initialize_app, + /*.header_version = */ CURRENT_HEADER_VERSION, + /*.app_version = */ VERSION_MD5, + + /*.app_name = */ "Morse", + /*.bitmap_data = */ { + 0x00, + 0x00, + 0xFE, + 0x7F, + 0xFF, + 0xFF, + 0xBB, + 0xD0, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0x0B, + 0xE1, + 0xFF, + 0xFF, + 0xFF, + 0xFF, + 0xEB, + 0xD0, + 0xFF, + 0xFF, + 0xFE, + 0x7F, + 0x70, + 0x00, + 0x30, + 0x00, + 0x10, + 0x00, + 0x00, + 0x00, + }, + /*.icon_color = */ ui::Color::green().v, + /*.menu_location = */ app_location_t::TX, + + /*.m4_app_tag = portapack::spi_flash::image_tag_tones */ {'P', 'T', 'O', 'N'}, + /*.m4_app_offset = */ 0x00000000, // will be filled at compile time +}; +} diff --git a/firmware/application/apps/ui_morse.cpp b/firmware/application/external/morse_tx/ui_morse.cpp similarity index 97% rename from firmware/application/apps/ui_morse.cpp rename to firmware/application/external/morse_tx/ui_morse.cpp index 77ec6f99..99af99bd 100644 --- a/firmware/application/apps/ui_morse.cpp +++ b/firmware/application/external/morse_tx/ui_morse.cpp @@ -36,7 +36,7 @@ using namespace portapack; using namespace morse; using namespace hackrf::one; -namespace ui { +namespace ui::external_app::morse_tx { static WORKING_AREA(ookthread_wa, 256); @@ -194,7 +194,8 @@ void MorseView::set_foxhunt(size_t i) { MorseView::MorseView( NavigationView& nav) : nav_(nav) { - baseband::run_image(portapack::spi_flash::image_tag_tones); + // baseband::run_image(portapack::spi_flash::image_tag_tones); + baseband::run_prepared_image(portapack::memory::map::m4_code.base()); add_children({&labels, &checkbox_foxhunt, @@ -284,4 +285,4 @@ MorseView::MorseView( }; } -} /* namespace ui */ +} // namespace ui::external_app::morse_tx diff --git a/firmware/application/apps/ui_morse.hpp b/firmware/application/external/morse_tx/ui_morse.hpp similarity index 98% rename from firmware/application/apps/ui_morse.hpp rename to firmware/application/external/morse_tx/ui_morse.hpp index 7630b2c7..d3783af0 100644 --- a/firmware/application/apps/ui_morse.hpp +++ b/firmware/application/external/morse_tx/ui_morse.hpp @@ -39,7 +39,7 @@ using namespace morse; -namespace ui { +namespace ui::external_app::morse_tx { class MorseView : public View { public: @@ -179,6 +179,6 @@ class MorseView : public View { }}; }; -} /* namespace ui */ +} // namespace ui::external_app::morse_tx #endif /*__MORSE_TX_H__*/ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 5eaba200..b7f99cef 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -55,7 +55,7 @@ #include "ui_level.hpp" #include "ui_looking_glass_app.hpp" #include "ui_mictx.hpp" -#include "ui_morse.hpp" +// #include "ui_morse.hpp" //moved to ext // #include "ui_nrf_rx.hpp" //moved to ext // #include "ui_numbers.hpp" // #include "ui_nuoptix.hpp" @@ -90,7 +90,7 @@ #include "ais_app.hpp" #include "analog_audio_app.hpp" // #include "analog_tv_app.hpp" //moved to ext -#include "ble_comm_app.hpp" +// #include "ble_comm_app.hpp" #include "ble_rx_app.hpp" #include "ble_tx_app.hpp" #include "capture_app.hpp" @@ -185,7 +185,7 @@ const NavigationView::AppList NavigationView::appList = { {"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory()}, {"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory()}, {"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory()}, - {"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory()}, + //{"morse", "Morse", TX, ui::Color::green(), &bitmap_icon_morse, new ViewFactory()}, //moved to ext //{"nuoptixdtmf", "Nuoptix DTMF", TX, ui::Color::green(), &bitmap_icon_nuoptix, new ViewFactory()}, {"ooktx", "OOK", TX, ui::Color::yellow(), &bitmap_icon_remote, new ViewFactory()}, {"pocsagtx", "POCSAG TX", TX, ui::Color::green(), &bitmap_icon_pocsag, new ViewFactory()}, diff --git a/firmware/common/battery.cpp b/firmware/common/battery.cpp index cad684fb..3871a96e 100644 --- a/firmware/common/battery.cpp +++ b/firmware/common/battery.cpp @@ -23,16 +23,16 @@ bool BatteryManagement::calcOverride = false; void BatteryManagement::detect() { // try to detect supported modules detected_ = BATT_NONE; + if (battery_max17055.detect()) { + battery_max17055.init(); + detected_ = BATT_MAX17055; + return; + } if (battery_ads1110.detect()) { battery_ads1110.init(); detected_ = BATT_ADS1110; return; } - if (battery_max17055.detect()) { - // battery_max17055.init(); //detect will call this on each "re detect" - detected_ = BATT_MAX17055; - return; - } // add new supported module detect + init here @@ -51,6 +51,13 @@ void BatteryManagement::init(bool override) { create_thread(); } +bool BatteryManagement::reset_learned() { + if (detected_ == BATT_MAX17055) { + return battery_max17055.reset_learned(); + } + return false; +} + // set if the default percentage calculation should be overrided by voltage based one void BatteryManagement::set_calc_override(bool override) { calcOverride = override; @@ -90,6 +97,26 @@ void BatteryManagement::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPerc (void)current; } +uint16_t BatteryManagement::get_cycles() { + if (detected_ == BATT_MAX17055) { + return (uint16_t)battery_max17055.getValue("Cycles"); + } + return 0; +} + +float BatteryManagement::get_tte() { + if (detected_ == BATT_MAX17055) { + return battery_max17055.getValue("TTE"); + } + return 0; +} +float BatteryManagement::get_ttf() { + if (detected_ == BATT_MAX17055) { + return battery_max17055.getValue("TTF"); + } + return 0; +} + uint16_t BatteryManagement::read_register(const uint8_t reg) { if (detected_ == BATT_MAX17055) { return battery_max17055.read_register(reg); diff --git a/firmware/common/battery.hpp b/firmware/common/battery.hpp index 8146b52f..6601420b 100644 --- a/firmware/common/battery.hpp +++ b/firmware/common/battery.hpp @@ -44,6 +44,8 @@ class BatteryManagement { BATT_VALID_VOLTAGE = 1, BATT_VALID_CURRENT = 2, BATT_VALID_PERCENT = 4, + BATT_VALID_CYCLES = 8, + BATT_VALID_TTEF = 16, }; static void init(bool override = false); static void detect(); @@ -56,6 +58,10 @@ class BatteryManagement { static bool write_register(const uint8_t reg, const uint16_t value); static void set_calc_override(bool override); static uint8_t calc_percent_voltage(uint16_t); // calculates battery percentage from the voltage + static bool reset_learned(); // resets the ic's learned parameters + static uint16_t get_cycles(); + static float get_tte(); + static float get_ttf(); private: static void create_thread(); diff --git a/firmware/common/max17055.cpp b/firmware/common/max17055.cpp index 634ff1c9..2c09d484 100644 --- a/firmware/common/max17055.cpp +++ b/firmware/common/max17055.cpp @@ -21,6 +21,7 @@ Boston, MA 02110-1301, USA. */ #include "max17055.hpp" #include "utility.hpp" +#include "portapack_persistent_memory.hpp" #include #include #include @@ -28,81 +29,292 @@ Boston, MA 02110-1301, USA. namespace battery { namespace max17055 { +const RegisterEntry MAX17055::entries[] = { + {"Status", 0x00, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"VAlrtTh", 0x01, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"TAlrtTh", 0x02, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"SAlrtTh", 0x03, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"AtRate", 0x04, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"RepCap", 0x05, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"RepSOC", 0x06, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"Age", 0x07, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"Temp", 0x08, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false}, + {"VCell", 0x09, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, false}, + {"Current", 0x0A, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"AvgCurrent", 0x0B, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"QResidual", 0x0C, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"MixSOC", 0x0D, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"AvSOC", 0x0E, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"MixCap", 0x0F, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + + {"FullCapRep", 0x10, "capacity", 0.5, false, "mAh", true, 1, true, true, false, 0, false}, + {"TTE", 0x11, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false}, + {"QRTable00", 0x12, "model", 1, false, "", false, 0, true, true, false, 0, false}, + {"FullSocThr", 0x13, "model", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"RCell", 0x14, "resistance", 0.244140625, false, "mOhms", false, 6, true, false, false, 0, false}, + {"Reserved", 0x15, "", 0.244140625, false, "mOhms", false, 6, true, false, false, 0, true}, + {"AvgTA", 0x16, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false}, + {"Cycles", 0x17, "cycles", 0.01, false, "Cycles", false, 2, true, true, false, 0, false}, + {"DesignCap", 0x18, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"AvgVCell", 0x19, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, false}, + {"MaxMinTemp", 0x1A, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"MaxMinVolt", 0x1B, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"MaxMinCurr", 0x1C, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Config", 0x1D, "model", 1, false, "", false, 0, true, false, false, 0, false}, + {"IChgTerm", 0x1E, "model", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"AvCap", 0x1F, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + + {"TTF", 0x20, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false}, + {"DevName", 0x21, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"QRTable10", 0x22, "model", 1, false, "", false, 0, true, true, false, 0, false}, + {"FullCapNom", 0x23, "capacity", 0.5, false, "mAh", true, 1, true, true, false, 0, false}, + {"Reserved", 0x24, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true}, + {"Reserved", 0x25, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true}, + {"Reserved", 0x26, "", 0.00390625, true, "C", true, 6, true, false, false, 0, true}, + {"AIN", 0x27, "temperature", 0.0015259, false, "%", true, 6, true, false, false, 0, false}, + {"LearnCfg", 0x28, "model", 1, false, "", false, 0, true, false, false, 0, false}, + {"FilterCfg", 0x29, "model", 1, false, "", false, 0, true, false, false, 0, false}, + {"RelaxCfg", 0x2A, "model", 1, false, "", false, 0, true, false, false, 0, false}, + {"MiscCfg", 0x2B, "model", 1, false, "", false, 0, true, false, false, 0, false}, + {"TGain", 0x2C, "temperature", 1, false, "", false, 0, true, false, false, 0, false}, + {"TOff", 0x2D, "temperature", 1, false, "", false, 0, true, false, false, 0, false}, + {"CGain", 0x2E, "current", 0.09765625, true, "%", true, 8, true, false, false, 0, false}, + {"COff", 0x2F, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + + {"Reserved", 0x30, "", 0.00125, false, "V", true, 5, true, false, false, 0, true}, + {"Reserved", 0x31, "", 0.005, true, "mA", true, 3, true, false, false, 0, true}, + {"QRTable20", 0x32, "model", 1, false, "", false, 0, true, true, false, 0, false}, + {"Reserved", 0x33, "", 0.0015625, false, "hr", true, 6, true, true, false, 0, true}, + {"DieTemp", 0x34, "temperature", 0.00390625, true, "C", true, 6, true, false, false, 0, false}, + {"FullCap", 0x35, "model", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"Reserved", 0x36, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true}, + {"Reserved", 0x37, "led", 1, false, "", false, 0, true, true, false, 0, false}, + {"RComp0", 0x38, "model", 1, false, "", false, 0, true, true, false, 0, false}, + {"TempCo", 0x39, "model", 1, false, "", false, 0, true, true, false, 0, false}, + {"VEmpty", 0x3A, "model", 0.000078125, false, "", false, 1, true, false, false, 0, false}, + {"Reserved", 0x3B, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true}, + {"Reserved", 0x3C, "", 0.000976563, false, "s", true, 6, true, false, false, 0, true}, + {"FStat", 0x3D, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Timer", 0x3E, "time", 4.88E-05, false, "hr", true, 7, true, false, false, 0, false}, + {"ShdnTimer", 0x3F, "", 0.000366, false, "hr", true, 6, true, false, false, 0, false}, + + {"UserMem1", 0x40, "led", 1, false, "", false, 0, true, false, false, 0, false}, + {"Reserved", 0x41, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"QRTable30", 0x42, "model", 0.15625, false, "", false, 0, true, true, false, 0, false}, + {"RGain", 0x43, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Reserved", 0x44, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true}, + {"dQAcc", 0x45, "capacity", 16, false, "mAh", true, 0, true, true, false, 0, false}, + {"dPAcc", 0x46, "percent", 0.0625, false, "%", true, 4, true, true, false, 0, false}, + {"Reserved", 0x47, "", 0.00390625, true, "%", true, 6, true, true, false, 0, true}, + {"Reserved", 0x48, "", 0.00390625, false, "%", true, 6, true, true, false, 0, true}, + {"ConvgCfg", 0x49, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"VFRemCap", 0x4A, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"Reserved", 0x4B, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Reserved", 0x4C, "", 0.5, true, "mAh", true, 1, true, false, false, 0, true}, + {"QH", 0x4D, "capacity", 0.5, true, "mAh", true, 1, true, false, false, 0, false}, + {"Reserved", 0x4E, "", 7.63E-06, false, "mAh", true, 8, true, false, false, 0, true}, + {"Reserved", 0x4F, "", 0.5, false, "mAh", true, 1, true, false, false, 0, true}, + + {"Status2", 0xB0, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Power", 0xB1, "", 0.8, true, "", false, 1, true, false, false, 0, false}, + {"ID", 0xB2, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"AvgPower", 0xB3, "", 0.8, true, "", false, 1, true, false, false, 0, false}, + {"IAlrtTh", 0xB4, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"TTFCfg", 0xB5, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"CVMixCap", 0xB6, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"CVHalfTime", 0xB7, "time", 0.000195313, false, "hr", true, 6, true, false, false, 0, false}, + {"CGTempCo", 0xB8, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Curve", 0xB9, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"HibCfg", 0xBA, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Config2", 0xBB, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"VRipple", 0xBC, "voltage", 9.77E-06, false, "V", true, 8, true, false, false, 0, false}, + {"RippleCfg", 0xBD, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"TimerH", 0xBE, "time", 3.2, false, "hr", true, 1, true, false, false, 0, false}, + {"Reserved", 0xBF, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true}, + + {"Rsense", 0xD0, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"ScOcvLim", 0xD1, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"Reserved", 0xD2, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"SOCHold", 0xD3, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"MaxPeakPower", 0xD4, "", 0.8, true, "", false, 0, true, false, false, 0, false}, + {"SusPeakPower", 0xD5, "", 0.8, true, "", false, 0, true, false, false, 0, false}, + {"PackResistance", 0xD6, "", 0.244141063, false, "", false, 0, true, false, false, 0, false}, + {"SysResistance", 0xD7, "", 0.244141063, false, "", false, 0, true, false, false, 0, false}, + {"MinSysVoltage", 0xD8, "", 0.000078125, false, "", false, 0, true, false, false, 0, false}, + {"MPPCurrent", 0xD9, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"SPPCurrent", 0xDA, "current", 0.15625, true, "mA", true, 5, true, false, false, 0, false}, + {"ModelCfg", 0xDB, "", 1, false, "", false, 0, true, false, false, 0, false}, + {"AtQResidual", 0xDC, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + {"AtTTE", 0xDD, "time", 0.0015625, false, "hr", true, 6, true, false, false, 0, false}, + {"AtAvSOC", 0xDE, "percent", 0.00390625, false, "%", true, 6, true, false, false, 0, false}, + {"AtAvCap", 0xDF, "capacity", 0.5, false, "mAh", true, 1, true, false, false, 0, false}, + + {"Reserved", 0xE0, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE1, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE2, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE3, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE4, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE5, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE6, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE7, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE8, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xE9, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xEA, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xEB, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xEC, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xED, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xEE, "voltage", 0.000078125, false, "V", true, 9, true, false, false, 0, true}, + {"Reserved", 0xEF, "", 1, false, "", false, 0, true, false, false, 0, true}, + + {"Reserved", 0xF0, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF1, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF2, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF3, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF4, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF5, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xF6, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true}, + {"Reserved", 0xF7, "", 0.001, true, "s", true, 3, true, false, false, 0, true}, + {"Reserved", 0xF8, "", 0.003051758, true, "C", true, 6, true, false, false, 0, true}, + {"Reserved", 0xF9, "", 0.00015625, false, "V", true, 6, true, false, false, 0, true}, + {"Reserved", 0xFA, "", 0.15625, true, "mA", true, 5, true, false, false, 0, true}, + {"Reserved", 0xFB, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true}, + {"Reserved", 0xFC, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xFD, "", 1, false, "", false, 0, true, false, false, 0, true}, + {"Reserved", 0xFE, "", 0.000078125, false, "V", true, 9, true, false, false, 0, true}, + {"Reserved", 0xFF, "", 0.00390625, false, "%", true, 6, true, false, false, 0, true}, +}; + void MAX17055::init() { if (!detected_) { detected_ = detect(); } - if (detected_) { // check again if it is detected - config(); - setHibCFG(0x0000); - - // Design Capacity Register - setDesignCapacity(__MAX17055_Design_Capacity__); - - // ModelCfg Register - setModelCfg(__MAX17055_Battery_Model__); - - // IChgTerm Register - setChargeTerminationCurrent(__MAX17055_Termination_Current__); - - // VEmpty Register - setEmptyVoltage(__MAX17055_Empty_Voltage__); - - // VRecovery Register - setRecoveryVoltage(__MAX17055_Recovery_Voltage__); - - // Set Minimum Voltage - setMinVoltage(__MAX17055_Min_Voltage__); - - // Set Maximum Voltage - setMaxVoltage(__MAX17055_Max_Voltage__); - - // Set Maximum Current - setMaxCurrent(__MAX17055_Max_Current__); - - // Set Minimum SOC - setMinSOC(__MAX17055_Min_SOC__); - - // Set Maximum SOC - setMaxSOC(__MAX17055_Max_SOC__); - - // Set Minimum Temperature - setMinTemperature(__MAX17055_Min_Temperature__); - - // Set Maximum Temperature - setMaxTemperature(__MAX17055_Max_Temperature__); - - // Clear Bits - statusClear(); + if (detected_) { + if (needsInitialization()) { + // First-time or POR initialization + full_reset_and_init(); + } else { + // Subsequent boot + partialInit(); + } + partialInit(); + // statusClear(); I am not sure if this should be here or not } } -bool MAX17055::detect() { - uint8_t _MAX17055_Data[2]; +bool MAX17055::full_reset_and_init() { + if (!soft_reset()) { + return false; + } - // Get Data from IC - if (readMultipleRegister(0x00, _MAX17055_Data, 2, false)) { + if (!initialize_custom_parameters()) { + return false; + } + + if (!load_custom_parameters()) { + return false; + } + + if (!clear_por()) { + return false; + } + + return true; +} + +bool MAX17055::soft_reset() { + return write_register(0x0BB, 0x0000); +} + +bool MAX17055::initialize_custom_parameters() { + if (!write_register(0xD0, 0x03E8)) return false; // Unknown register, possibly related to battery profile + if (!write_register(0xDB, 0x0000)) return false; // ModelCfg + if (!write_register(0x05, 0x0000)) return false; // RepCap + if (!write_register(0x18, 0x1388)) return false; // DesignCap + if (!write_register(0x45, 0x009C)) return false; // dQAcc + if (!write_register(0x1E, 0x03C0)) return false; // IChgTerm + if (!write_register(0x3A, 0x9661)) return false; // VEmpty + if (!write_register(0x60, 0x0090)) return false; // Unknown register + if (!write_register(0x46, 0x0561)) return false; // dPAcc + if (!write_register(0xDB, 0x8000)) return false; // ModelCfg + + if (!write_register(0x40, 0x0001)) return false; // Set user mem to 1 + return true; +} + +bool MAX17055::load_custom_parameters() { + uint16_t hib_cfg = read_register(0xBA); + if (!write_register(0xBA, 0x0000)) return false; // Disable hibernate mode + if (!write_register(0x60, 0x0000)) return false; // Unknown register + + // Wait for the model to be loaded + for (int i = 0; i < 10; i++) { + uint16_t model_cfg = read_register(0xDB); + if ((model_cfg & 0x8000) == 0) { + break; + } + chThdSleepMilliseconds(10); + } + + if (!write_register(0xBA, hib_cfg)) return false; // Restore hibernate config + return true; +} + +bool MAX17055::clear_por() { + uint16_t status = read_register(0x00); + status &= ~(1 << 1); + return write_register(0x00, status); +} + +bool MAX17055::needsInitialization() { + uint16_t UserMem1 = read_register(0x40); + + if (UserMem1 == 0) { + return true; + } + return false; +} + +void MAX17055::partialInit() { + // Only update necessary volatile settings + setHibCFG(0x0000); // If you always want hibernation disabled + // Add any other volatile settings that need updating +} + +bool MAX17055::reset_learned() { + // this if for reset all the learned parameters by ic + // the full inis should do this + full_reset_and_init(); + return true; +} + +bool MAX17055::detect() { + // Read the DevName register (0x21) + uint16_t dev_name = read_register(0x21); + + // The DevName register should return 0x4010 for MAX17055 + if (dev_name == 0x4010) { detected_ = true; return true; } + + // If DevName doesn't match, try reading Status register as a fallback + uint16_t status = read_register(0x00); + if (status != 0xFFFF && status != 0x0000) { + detected_ = true; + return true; + } + detected_ = false; return false; } -bool bitRead(uint8_t value, uint8_t bit) { - return (value >> bit) & 0x01; -} - -void bitSet(uint16_t& value, uint8_t bit) { - value |= (1 << bit); -} - -void bitSet(uint8_t& value, uint8_t bit) { - value |= (1 << bit); -} - -void bitClear(uint16_t& value, uint8_t bit) { - value &= ~(1 << bit); +const RegisterEntry* MAX17055::findEntry(const char* name) const { + for (const auto& entry : entries) { + if (std::strcmp(entry.name, name) == 0) { + return &entry; + } + } + return nullptr; } uint16_t MAX17055::read_register(const uint8_t reg) { @@ -124,497 +336,53 @@ bool MAX17055::write_register(const uint8_t reg, const uint16_t value) { tx[2] = (value >> 8) & 0xFF; // High byte bool success = bus.transmit(bus_address, tx.data(), tx.size()); + chThdSleepMilliseconds(1); return success; } -bool MAX17055::readMultipleRegister(uint8_t reg, uint8_t* data, uint8_t length, bool endTransmission) { - if (bus.transmit(bus_address, ®, 1)) { - if (bus.receive(bus_address, data, length)) { - if (endTransmission) { - // bus.stop(); // Testing if we need this line - // Perform any necessary end transmission actions - // For example, you can use bus.endTransmission() - } - return true; - } - } - return false; -} - -bool MAX17055::writeMultipleRegister(uint8_t reg, const uint8_t* data, uint8_t length) { - uint8_t buffer[length + 1]; - buffer[0] = reg; - memcpy(buffer + 1, data, length); - - if (bus.transmit(bus_address, buffer, length + 1)) { - // Perform any necessary end transmission actions - // For example, you can use bus.endTransmission() - return true; - } - return false; -} - void MAX17055::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current) { if (detected_) { - uint16_t status = 0; // Read Status Register - readMultipleRegister(0x00, (uint8_t*)&status, 2, false); - voltage = averageVoltage(); + uint16_t status = read_register(0x00); + voltage = averageMVoltage(); if ((status == 0 && voltage == 0) || (status == 0x0002 && voltage == 3600) || (status == 0x0002 && voltage == 0)) { valid_mask = 0; return; } batteryPercentage = stateOfCharge(); current = instantCurrent(); - valid_mask = 7; // BATT_VALID_VOLTAGE + CURRENT + PERCENT + valid_mask = 31; // BATT_VALID_VOLTAGE + CURRENT + PERCENT + BATT_VALID_CYCLES + BATT_VALID_TTEF } else { // let's indicate the data is wrong. ui will handle this by display UNK values. valid_mask = 0; } } -bool MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) { - // Set Voltage Value - uint16_t _Raw_Voltage = ((uint16_t)((uint16_t)_Empty_Voltage * 100) << 7) & 0b1111111110000000; - - // Define Data Variable - uint8_t MAX17055_RAW_Data[2]; - - // Handle Data - MAX17055_RAW_Data[1] = (uint8_t)(_Raw_Voltage >> 8); - MAX17055_RAW_Data[0] = (uint8_t)(_Raw_Voltage & 0x00FF); - - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x3A, MAX17055_Current_Data, 2, true); - - // Clear Current Value - MAX17055_Current_Data[0] &= 0b01111111; - MAX17055_Current_Data[1] &= 0b00000000; - - // Define Data Variable - uint8_t MAX17055_Handle_Data[2]; - - // Handle Data - MAX17055_Handle_Data[0] = MAX17055_Current_Data[0] | MAX17055_RAW_Data[0]; - MAX17055_Handle_Data[1] = MAX17055_Current_Data[1] | MAX17055_RAW_Data[1]; - - // Set Register - bool _Result = writeMultipleRegister(0x3A, MAX17055_Handle_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) { - // Handle Value - _Recovery_Voltage = _Recovery_Voltage * 1000 / 40; - - // Set Voltage Value - uint16_t _Raw_Voltage = ((uint16_t)_Recovery_Voltage); - - // Define Data Variable - uint8_t MAX17055_RAW_Data[2]; - - // Handle Data - MAX17055_RAW_Data[1] = (uint8_t)(_Raw_Voltage >> 8); - MAX17055_RAW_Data[0] = (uint8_t)(_Raw_Voltage & 0x00FF); - - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x3A, MAX17055_Current_Data, 2, true); - - // Clear Current Value - MAX17055_Current_Data[0] &= 0b10000000; - MAX17055_Current_Data[1] &= 0b11111111; - - // Define Data Variable - uint8_t MAX17055_Handle_Data[2]; - - // Handle Data - MAX17055_Handle_Data[0] = MAX17055_Current_Data[0] | MAX17055_RAW_Data[0]; - MAX17055_Handle_Data[1] = MAX17055_Current_Data[1] | MAX17055_RAW_Data[1]; - - // Set Register - bool _Result = writeMultipleRegister(0x3A, MAX17055_Handle_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) { - // Set Voltage Value - uint8_t _Raw_Voltage = (uint8_t)(_Minimum_Voltage * 1000 / 20); - - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x01, MAX17055_Current_Data, 2, true); - - // Set Voltage Value - MAX17055_Current_Data[0] = _Raw_Voltage; - - // Set Register - bool _Result = writeMultipleRegister(0x01, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) { - // Set Voltage Value - uint8_t _Raw_Voltage = (uint8_t)(_Maximum_Voltage * 1000 / 20); - - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x01, MAX17055_Current_Data, 2, true); - - // Set Voltage Value - MAX17055_Current_Data[1] = _Raw_Voltage; - - // Set Register - bool _Result = writeMultipleRegister(0x01, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMaxCurrent(uint16_t _Maximum_Current) { - // Set Current Value - uint8_t _Raw_Current = (uint8_t)(_Maximum_Current * 1000 / 40); - - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0xB4, MAX17055_Current_Data, 2, true); - - // Set Current Value - MAX17055_Current_Data[1] = _Raw_Current; - - // Set Register - bool _Result = writeMultipleRegister(0xB4, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setChargeTerminationCurrent(uint16_t _Charge_Termination_Current) { - // Handle Raw Data - uint16_t _RAW_Data = (uint16_t)(_Charge_Termination_Current * 1000000 * __MAX17055_Resistor__ / 1.5625); - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_RAW_Data & (uint16_t)0x00FF)); - _Data[1] = ((_RAW_Data & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0x1E, _Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setDesignCapacity(const uint16_t _Capacity) { - // Set Raw - uint16_t _Raw_Cap = (uint16_t)_Capacity * 2; - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Raw_Cap & (uint16_t)0x00FF)); - _Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0x18, _Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setFullCapRep(const uint16_t _Capacity) { - // Set Raw - uint16_t _Raw_Cap = (uint16_t)_Capacity * 2; - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Raw_Cap & (uint16_t)0x00FF)); - _Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0x10, _Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setFullCapNom(const uint16_t _Capacity) { - // Set Raw - uint16_t _Raw_Cap = (uint16_t)_Capacity * 2; - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Raw_Cap & (uint16_t)0x00FF)); - _Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0x23, _Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setRepCap(const uint16_t _Capacity) { - // Set Raw - uint16_t _Raw_Cap = (uint16_t)_Capacity * 2; - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Raw_Cap & (uint16_t)0x00FF)); - _Data[1] = ((_Raw_Cap & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0x05, _Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMinSOC(uint8_t _Minimum_SOC) { - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x03, MAX17055_Current_Data, 2, true); - - // Scale Value - uint8_t _MinSOC = (_Minimum_SOC / 100) * 255; - - // Set Voltage Value - MAX17055_Current_Data[0] = _MinSOC; - - // Set Register - bool _Result = writeMultipleRegister(0x03, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMaxSOC(uint8_t _Maximum_SOC) { - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x03, MAX17055_Current_Data, 2, true); - - // Scale Value - uint8_t _MaxSOC = (_Maximum_SOC / 100) * 255; - - // Set Voltage Value - MAX17055_Current_Data[1] = _MaxSOC; - - // Set Register - bool _Result = writeMultipleRegister(0x03, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) { - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x02, MAX17055_Current_Data, 2, true); - - // Set Voltage Value - MAX17055_Current_Data[0] = _Minimum_Temperature; - - // Set Register - bool _Result = writeMultipleRegister(0x02, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) { - // Define Data Variable - uint8_t MAX17055_Current_Data[2]; - - // Read Current Register - readMultipleRegister(0x02, MAX17055_Current_Data, 2, true); - - // Set Voltage Value - MAX17055_Current_Data[1] = _Maximum_Temperature; - - // Set Register - bool _Result = writeMultipleRegister(0x02, MAX17055_Current_Data, 2); - - // End Function - return _Result; -} - -bool MAX17055::setModelCfg(const uint8_t _Model_ID) { - // Declare Variable - uint16_t _Data_Set = 0x00; - - // Set Charge Voltage - bitSet(_Data_Set, 10); - - // Set Battery Model - if (_Model_ID == 0) { - bitClear(_Data_Set, 4); - bitClear(_Data_Set, 5); - bitClear(_Data_Set, 6); - bitClear(_Data_Set, 7); +float MAX17055::getValue(const char* entityName) { + const RegisterEntry* entry = findEntry(entityName); + if (entry) { + uint16_t raw_value = read_register(entry->address); + + float scaled_value; + if (entry->is_signed) { + int16_t signed_value = static_cast(raw_value); + scaled_value = signed_value * entry->scalar; + } else { + scaled_value = raw_value * entry->scalar; + } + + return scaled_value; } - if (_Model_ID == 2) { - bitClear(_Data_Set, 4); - bitSet(_Data_Set, 5); - bitClear(_Data_Set, 6); - bitClear(_Data_Set, 7); - } - if (_Model_ID == 6) { - bitClear(_Data_Set, 4); - bitSet(_Data_Set, 5); - bitSet(_Data_Set, 6); - bitClear(_Data_Set, 7); - } - - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Data_Set & (uint16_t)0x00FF)); - _Data[1] = ((_Data_Set & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0xDB, _Data, 2); - - // End Function - return _Result; + return 0; // Return 0 if entry not found } -bool MAX17055::setHibCFG(const uint16_t _Config) { - // Declare Default Data Array - uint8_t _Data[2]; - - // Set Data Low/High Byte - _Data[0] = ((_Config & (uint16_t)0x00FF)); - _Data[1] = ((_Config & (uint16_t)0xFF00) >> 8); - - // Set Register - bool _Result = writeMultipleRegister(0xBA, _Data, 2); - - // End Function - return _Result; -} - -void MAX17055::config(void) { - // Declare Default Data Array - uint8_t _Config1[2]; - uint8_t _Config2[2]; - - // Set Default Data - _Config1[0] = 0b00000000; - _Config1[1] = 0b00000000; - _Config2[0] = 0b00011000; - _Config2[1] = 0b00000110; - - // Set Configuration bits [Config1] - if (MAX17055_Ber) bitSet(_Config1[0], 0); - if (MAX17055_Bei) bitSet(_Config1[0], 1); - if (MAX17055_Aen) bitSet(_Config1[0], 2); - if (MAX17055_FTHRM) bitSet(_Config1[0], 3); - if (MAX17055_ETHRM) bitSet(_Config1[0], 4); - if (MAX17055_COMMSH) bitSet(_Config1[0], 6); - if (MAX17055_SHDN) bitSet(_Config1[0], 7); - if (MAX17055_Tex) bitSet(_Config1[1], 0); - if (MAX17055_Ten) bitSet(_Config1[1], 1); - if (MAX17055_AINSH) bitSet(_Config1[1], 2); - if (MAX17055_IS) bitSet(_Config1[1], 3); - if (MAX17055_VS) bitSet(_Config1[1], 4); - if (MAX17055_TS) bitSet(_Config1[1], 5); - if (MAX17055_SS) bitSet(_Config1[1], 6); - if (MAX17055_TSel) bitSet(_Config1[1], 7); - - // Set Configuration bits [Config2] - if (MAX17055_CPMode) bitSet(_Config2[0], 1); - if (MAX17055_LDMDL) bitSet(_Config2[0], 5); - if (MAX17055_TAIrtEN) bitSet(_Config2[0], 6); - if (MAX17055_dSOCen) bitSet(_Config2[0], 7); - if (MAX17055_DPEn) bitSet(_Config2[1], 4); - if (MAX17055_AtRateEn) bitSet(_Config2[1], 5); - - // Config1 Setting - writeMultipleRegister(0x1D, _Config1, 2); - writeMultipleRegister(0xBB, _Config2, 2); -} - -uint16_t MAX17055::instantVoltage(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x09); - - // Calculate Measurement - uint16_t _Value = ((uint32_t)_Measurement_Raw * 1.25 / 16); - - // End Function - return _Value; -} - -uint16_t MAX17055::averageVoltage(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x19); - - // Calculate Measurement - uint16_t _Value = ((uint32_t)_Measurement_Raw * 1.25 / 16); - - // End Function - return _Value; -} - -uint16_t MAX17055::emptyVoltage(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x3A); - _Measurement_Raw = ((_Measurement_Raw & 0xFF80) >> 7); - - // Calculate Measurement - uint16_t _Value = ((uint32_t)_Measurement_Raw * 10); - - // End Function - return _Value; -} - -uint16_t MAX17055::recoveryVoltage(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x3A); - - _Measurement_Raw = (_Measurement_Raw & 0x7F); - - // Calculate Measurement - uint16_t _Value = ((uint32_t)_Measurement_Raw * 40); - - // End Function - return _Value; +uint16_t MAX17055::averageMVoltage(void) { + return static_cast(getValue("AvgVCell") * 1000.0f); // Convert to millivolts } int32_t MAX17055::instantCurrent(void) { + return getValue("Current"); + // Get Data from IC uint16_t _Measurement_Raw = read_register(0x0A); @@ -627,194 +395,230 @@ int32_t MAX17055::instantCurrent(void) { return _Value; } -int32_t MAX17055::averageCurrent(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x0B); - - // Convert to signed int16_t (two's complement) - int32_t _Signed_Raw = static_cast(_Measurement_Raw); - - int32_t _Value = (_Signed_Raw * 15625) / (__MAX17055_Resistor__ * 100) / 100000; - - // End Function - return _Value; -} - uint16_t MAX17055::stateOfCharge(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x06); // RepSOC - - // Calculate Measurement - uint8_t _Value = (_Measurement_Raw >> 8) & 0xFF; - - // End Function - return _Value; + return getValue("RepSOC"); } -uint16_t MAX17055::averageStateOfCharge(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x0E); +bool MAX17055::setEmptyVoltage(uint16_t _Empty_Voltage) { + // Calculate the new VE_Empty value (upper 9 bits) + uint16_t ve_empty = ((_Empty_Voltage * 100) / 10) & 0xFF80; - // Calculate Measurement - uint16_t _Value = ((uint32_t)_Measurement_Raw * 100 / 256); + // Read the current register value + uint16_t current_value = read_register(0x3A); - // End Function - return _Value; + // Preserve the lower 7 bits (VR_Empty) and combine with new VE_Empty + uint16_t new_value = (ve_empty & 0xFF80) | (current_value & 0x007F); + + // Write the new value back to the register + return write_register(0x3A, new_value); } -uint16_t MAX17055::instantCapacity(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x05); +bool MAX17055::setRecoveryVoltage(uint16_t _Recovery_Voltage) { + // Calculate the new VR_Empty value (lower 7 bits) + uint16_t vr_empty = (_Recovery_Voltage * 25) & 0x007F; // 40mV per bit, 25 = 1000/40 - // Calculate Data - uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__; + // Read the current register value + uint16_t current_value = read_register(0x3A); - // End Function - return _Value; + // Preserve the upper 9 bits (VE_Empty) and combine with new VR_Empty + uint16_t new_value = (current_value & 0xFF80) | vr_empty; + + // Write the new value back to the register + return write_register(0x3A, new_value); } -uint16_t MAX17055::designCapacity(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x18); +bool MAX17055::setMinVoltage(uint16_t _Minimum_Voltage) { + uint16_t current_value = read_register(0x01); - // Calculate Data - uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__; + uint16_t min_voltage_raw = (_Minimum_Voltage * 50) & 0x00FF; // 20mV per bit, 50 = 1000/20 + uint16_t new_value = (current_value & 0xFF00) | min_voltage_raw; - // End Function - return _Value; + return write_register(0x01, new_value); } -uint16_t MAX17055::fullCapacity(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x35); +bool MAX17055::setMaxVoltage(uint16_t _Maximum_Voltage) { + uint16_t current_value = read_register(0x01); - // Calculate Data - uint16_t _Value = _Measurement_Raw * 5 / 1000 / __MAX17055_Resistor__; + uint16_t max_voltage_raw = ((_Maximum_Voltage * 50) & 0x00FF) << 8; // 20mV per bit, 50 = 1000/20 + uint16_t new_value = (current_value & 0x00FF) | max_voltage_raw; - // End Function - return _Value; + return write_register(0x01, new_value); } -uint16_t MAX17055::icTemperature(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x08); +bool MAX17055::setMaxCurrent(uint16_t _Maximum_Current) { + uint16_t current_value = read_register(0xB4); - // Declare Variables - bool _Signiture = false; + uint16_t max_current_raw = ((_Maximum_Current * 25) & 0x00FF) << 8; // 40mV per bit, 25 = 1000/40 + uint16_t new_value = (current_value & 0x00FF) | max_current_raw; - // Control for Negative Value - if ((_Measurement_Raw >> 12) == 0xF) { - // Calculate Data - _Measurement_Raw = 0xFFFF - _Measurement_Raw; + return write_register(0xB4, new_value); +} - // Assign Signiture - _Signiture = true; +bool MAX17055::setChargeTerminationCurrent(uint16_t _Charge_Termination_Current) { + float lsb_mA = 1.5625 / (__MAX17055_Resistor__ * 1000); // Convert to mA + uint16_t ichgterm_value = static_cast(round(_Charge_Termination_Current / lsb_mA)); + + return write_register(0x1E, ichgterm_value); +} + +bool MAX17055::setDesignCapacity(const uint16_t _Capacity) { + uint16_t raw_cap = (uint16_t)_Capacity * 2; + return write_register(0x18, raw_cap); +} + +bool MAX17055::setFullCapRep(const uint16_t _Capacity) { + uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB + return write_register(0x10, raw_cap); +} + +bool MAX17055::setFullCapNom(const uint16_t _Capacity) { + uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB + return write_register(0x23, raw_cap); +} + +bool MAX17055::setRepCap(const uint16_t _Capacity) { + uint16_t raw_cap = _Capacity * 2; // 0.5mAh per LSB + return write_register(0x05, raw_cap); +} + +bool MAX17055::setMinSOC(uint8_t _Minimum_SOC) { + uint16_t current_value = read_register(0x03); + + uint16_t min_soc_raw = ((_Minimum_SOC * 256) / 100) & 0x00FF; + uint16_t new_value = (current_value & 0xFF00) | min_soc_raw; + + return write_register(0x03, new_value); +} + +bool MAX17055::setMaxSOC(uint8_t _Maximum_SOC) { + uint16_t current_value = read_register(0x03); + + uint16_t max_soc_raw = (((_Maximum_SOC * 256) / 100) & 0x00FF) << 8; + uint16_t new_value = (current_value & 0x00FF) | max_soc_raw; + + return write_register(0x03, new_value); +} + +bool MAX17055::setMinTemperature(uint8_t _Minimum_Temperature) { + uint16_t current_value = read_register(0x02); + + uint16_t min_temp_raw = (uint8_t)_Minimum_Temperature; + uint16_t new_value = (current_value & 0xFF00) | min_temp_raw; + + return write_register(0x02, new_value); +} + +bool MAX17055::setMaxTemperature(uint8_t _Maximum_Temperature) { + uint16_t current_value = read_register(0x02); + + uint16_t max_temp_raw = ((uint8_t)_Maximum_Temperature) << 8; + uint16_t new_value = (current_value & 0x00FF) | max_temp_raw; + + return write_register(0x02, new_value); +} + +bool MAX17055::setModelCfg(const uint8_t _Model_ID) { + uint16_t model_cfg = 0x0400; // Set Charge Voltage (bit 10) + + // Set Battery Model + switch (_Model_ID) { + case 0: // Default model + break; + case 2: // EZ model + model_cfg |= 0x0020; + break; + case 6: // Short EZ model + model_cfg |= 0x0060; + break; + default: + return false; // Invalid model ID } - // Calculate Data - uint16_t _Value = ((uint32_t)_Measurement_Raw * 100 / 256); - - // Assign Signiture - if (_Signiture) _Value = -_Value; - - // End Function - return _Value; + return write_register(0xDB, model_cfg); } -uint16_t MAX17055::timeToEmpty(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x11); - - // Calculate Data - uint16_t _Value = ((uint32_t)_Measurement_Raw * 5625 / 60 / 60 / 100); - - // End Function - return _Value; +bool MAX17055::setHibCFG(const uint16_t _Config) { + return write_register(0xBA, _Config); } -uint16_t MAX17055::timeToFull(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x20); +void MAX17055::config(void) { + uint16_t config1 = 0x0000; + uint16_t config2 = 0x0618; // Default value: 0b00011000 00000110 - // Calculate Data - uint16_t _Value = ((uint32_t)_Measurement_Raw * 5625 / 60 / 60 / 100); + // Set Configuration bits [Config1] + if (MAX17055_Ber) config1 |= 0x0001; + if (MAX17055_Bei) config1 |= 0x0002; + if (MAX17055_Aen) config1 |= 0x0004; + if (MAX17055_FTHRM) config1 |= 0x0008; + if (MAX17055_ETHRM) config1 |= 0x0010; + if (MAX17055_COMMSH) config1 |= 0x0040; + if (MAX17055_SHDN) config1 |= 0x0080; + if (MAX17055_Tex) config1 |= 0x0100; + if (MAX17055_Ten) config1 |= 0x0200; + if (MAX17055_AINSH) config1 |= 0x0400; + if (MAX17055_IS) config1 |= 0x0800; + if (MAX17055_VS) config1 |= 0x1000; + if (MAX17055_TS) config1 |= 0x2000; + if (MAX17055_SS) config1 |= 0x4000; + if (MAX17055_TSel) config1 |= 0x8000; - // End Function - return _Value; + // Set Configuration bits [Config2] + if (MAX17055_CPMode) config2 |= 0x0002; + if (MAX17055_LDMDL) config2 |= 0x0020; + if (MAX17055_TAIrtEN) config2 |= 0x0040; + if (MAX17055_dSOCen) config2 |= 0x0080; + if (MAX17055_DPEn) config2 |= 0x1000; + if (MAX17055_AtRateEn) config2 |= 0x2000; + + // Write configurations to registers + write_register(0x1D, config1); + write_register(0xBB, config2); } -uint16_t MAX17055::batteryAge(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x07); - - // End Function - return _Measurement_Raw; +bool MAX17055::statusClear() { + // Clear all bits in the Status register (0x00) + return write_register(0x00, 0x0000); } -uint16_t MAX17055::chargeCycle(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x17); - - // End Function - return _Measurement_Raw; +bool bitRead(uint8_t value, uint8_t bit) { + return (value >> bit) & 0x01; } bool MAX17055::statusControl(const uint8_t _Status) { - // Define Data Variable - uint8_t _Status_Register[2] = {0x00, 0x00}; - - // Read Status Register - readMultipleRegister(0x00, _Status_Register, 2, false); + // Read Status Register (0x00) + uint16_t status_register = read_register(0x00); // Control for Status - if (_Status == MAX17055_POR) - return bitRead(_Status_Register[0], 1); - else if (_Status == MAX17055_IMin) - return bitRead(_Status_Register[0], 2); - else if (_Status == MAX17055_IMax) - return bitRead(_Status_Register[0], 6); - else if (_Status == MAX17055_VMin) - return bitRead(_Status_Register[1], 0); - else if (_Status == MAX17055_VMax) - return bitRead(_Status_Register[1], 4); - else if (_Status == MAX17055_TMin) - return bitRead(_Status_Register[1], 1); - else if (_Status == MAX17055_TMax) - return bitRead(_Status_Register[1], 5); - else if (_Status == MAX17055_SOC_Min) - return bitRead(_Status_Register[1], 2); - else if (_Status == MAX17055_SOC_Max) - return bitRead(_Status_Register[1], 6); - else if (_Status == MAX17055_SOC_Change) - return bitRead(_Status_Register[0], 7); - else if (_Status == MAX17055_Bat_Status) - return bitRead(_Status_Register[0], 3); - else if (_Status == MAX17055_Bat_Insert) - return bitRead(_Status_Register[1], 3); - else if (_Status == MAX17055_Bat_Remove) - return bitRead(_Status_Register[1], 7); - - // End Function - return false; -} - -void MAX17055::statusClear(void) { - // Define Data Variable - const uint8_t _Status_Register[2] = {0x00, 0x00}; - - // Write Status Register - writeMultipleRegister(0x00, _Status_Register, 2); -} - -uint16_t MAX17055::chargeTerminationCurrent(void) { - // Get Data from IC - uint16_t _Measurement_Raw = read_register(0x1E); - - // Calculate Data - uint16_t Value = (((uint32_t)_Measurement_Raw * 1.5625) / __MAX17055_Resistor__); - - // End Function - return Value; + switch (_Status) { + case MAX17055_POR: + return bitRead(status_register & 0xFF, 1); + case MAX17055_IMin: + return bitRead(status_register & 0xFF, 2); + case MAX17055_IMax: + return bitRead(status_register & 0xFF, 6); + case MAX17055_SOC_Change: + return bitRead(status_register & 0xFF, 7); + case MAX17055_Bat_Status: + return bitRead(status_register & 0xFF, 3); + case MAX17055_VMin: + return bitRead(status_register >> 8, 0); + case MAX17055_VMax: + return bitRead(status_register >> 8, 4); + case MAX17055_TMin: + return bitRead(status_register >> 8, 1); + case MAX17055_TMax: + return bitRead(status_register >> 8, 5); + case MAX17055_SOC_Min: + return bitRead(status_register >> 8, 2); + case MAX17055_SOC_Max: + return bitRead(status_register >> 8, 6); + case MAX17055_Bat_Insert: + return bitRead(status_register >> 8, 3); + case MAX17055_Bat_Remove: + return bitRead(status_register >> 8, 7); + default: + return false; + } } } /* namespace max17055 */ diff --git a/firmware/common/max17055.hpp b/firmware/common/max17055.hpp index c9a96e31..8e5d6ce9 100644 --- a/firmware/common/max17055.hpp +++ b/firmware/common/max17055.hpp @@ -83,7 +83,7 @@ // Define Termination Current #ifndef __MAX17055_Termination_Current__ -#define __MAX17055_Termination_Current__ 0.1 // Termination Current +#define __MAX17055_Termination_Current__ 200 // Termination Current #endif // Define Minimum Temperature @@ -253,49 +253,63 @@ namespace max17055 { using address_t = uint8_t; +struct RegisterEntry { + const char* name; + uint8_t address; + const char* type; + float scalar; + bool is_signed; + const char* unit; + bool abbr_units; + int resolution; + bool is_user; + bool is_save_restore; + bool is_nv; + uint16_t por_data; + bool is_read_only; +}; + class MAX17055 { public: constexpr MAX17055(I2C& bus, const I2C::address_t bus_address) : bus(bus), bus_address(bus_address), detected_(false) {} + static const RegisterEntry entries[]; + static constexpr size_t entries_count = 144; + + uint16_t read_register(const uint8_t reg); + bool write_register(const uint8_t reg, const uint16_t value); + void init(); bool detect(); bool isDetected() const { return detected_; } - uint16_t readVoltage(); - uint8_t readPercentage(); void getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage, int32_t& current); + bool reset_learned(); - uint16_t instantVoltage(void); - uint16_t averageVoltage(void); - uint16_t emptyVoltage(void); - uint16_t recoveryVoltage(void); + float getValue(const char* entityName); + uint16_t averageMVoltage(void); int32_t instantCurrent(void); - int32_t averageCurrent(void); uint16_t stateOfCharge(void); - uint16_t averageStateOfCharge(void); - uint16_t instantCapacity(void); - uint16_t designCapacity(void); - uint16_t fullCapacity(void); - uint16_t icTemperature(void); - uint16_t timeToEmpty(void); - uint16_t timeToFull(void); - uint16_t batteryAge(void); - uint16_t chargeCycle(void); - bool statusControl(const uint8_t _Status); - void statusClear(void); - uint16_t chargeTerminationCurrent(void); - uint16_t read_register(const uint8_t reg); - bool write_register(const uint8_t reg, const uint16_t value); private: I2C& bus; const I2C::address_t bus_address; bool detected_ = false; - bool readRegister(uint8_t reg, uint16_t& value); - bool readMultipleRegister(uint8_t reg, uint8_t* data, uint8_t length, bool endTransmission); - bool writeMultipleRegister(uint8_t reg, const uint8_t* data, uint8_t length); + const RegisterEntry* findEntry(const char* name) const; + + bool needsInitialization(); + void partialInit(); + + bool statusControl(const uint8_t _Status); + bool statusClear(); + + bool full_reset_and_init(); + bool soft_reset(); + bool clear_por(); + bool initialize_custom_parameters(); + bool load_custom_parameters(); bool setEmptyVoltage(uint16_t _Empty_Voltage); bool setRecoveryVoltage(uint16_t _Recovery_Voltage); diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index b4546212..a96a7cfd 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -953,18 +953,15 @@ bool ui_hide_sd_card() { bool ui_hide_fake_brightness() { return data->ui_config2.hide_fake_brightness; } - bool ui_hide_numeric_battery() { return data->ui_config2.hide_numeric_battery; } bool ui_hide_battery_icon() { return data->ui_config2.hide_battery_icon; } - uint8_t ui_theme_id() { return data->ui_config2.theme_id; } - bool ui_override_batt_calc() { return data->ui_config2.override_batt_calc; }