From 67975d76a0094c5bc1c3b012c371adb0a660754c Mon Sep 17 00:00:00 2001 From: jLynx Date: Sun, 21 Apr 2024 20:44:47 +1200 Subject: [PATCH] ADS1100 (#2116) * WIP * WIP * WIP * Corrected name * WIP * WIP * WIP * WIP * Added new calc * WIP * WIP * WIP * WIP * WIP * WIP * Added debug serial lines * WIP * Fixed issue * Fixed calculation issue * Added voltage to performance DFU menu * Added padding function and added voltage to perf menu * Clean up * Refactor * Fixed linting * Hides voltage if PP does not conatin IC * WIP showing battery % * made the percentage a int * Added % to header * Removed test UI * Removed comment * Added fix for precision too large * Added fix for precision too large * Linting --- firmware/application/CMakeLists.txt | 1 + firmware/application/apps/ui_dfu_menu.cpp | 11 +- firmware/application/apps/ui_dfu_menu.hpp | 5 +- firmware/application/portapack.cpp | 3 + firmware/application/portapack.hpp | 3 + firmware/application/string_format.cpp | 25 +++++ firmware/application/string_format.hpp | 1 + firmware/common/ads1110.cpp | 130 ++++++++++++++++++++++ firmware/common/ads1110.hpp | 58 ++++++++++ 9 files changed, 235 insertions(+), 2 deletions(-) create mode 100644 firmware/common/ads1110.cpp create mode 100644 firmware/common/ads1110.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 1410d95d..4e67af38 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -176,6 +176,7 @@ set(CPPSRC ${COMMON}/ui_language.cpp ${COMMON}/utility.cpp ${COMMON}/wm8731.cpp + ${COMMON}/ads1110.cpp ${COMMON}/performance_counter.cpp app_settings.cpp audio.cpp diff --git a/firmware/application/apps/ui_dfu_menu.cpp b/firmware/application/apps/ui_dfu_menu.cpp index 63efe41f..887a33f0 100644 --- a/firmware/application/apps/ui_dfu_menu.cpp +++ b/firmware/application/apps/ui_dfu_menu.cpp @@ -41,6 +41,11 @@ DfuMenu::DfuMenu(NavigationView& nav) &text_info_line_8, &text_info_line_9, &text_info_line_10}); + + if (portapack::battery_ads1110.isDetected()) { + add_child(&voltage_label); + add_child(&text_info_line_11); + } } void DfuMenu::paint(Painter& painter) { @@ -48,6 +53,8 @@ void DfuMenu::paint(Painter& painter) { size_t m0_fragmented_free_space = 0; const auto m0_fragments = chHeapStatus(NULL, &m0_fragmented_free_space); + auto lines = (portapack::battery_ads1110.isDetected() ? 11 : 10) + 2; + text_info_line_1.set(to_string_dec_uint(chCoreStatus(), 6)); text_info_line_2.set(to_string_dec_uint(m0_fragmented_free_space, 6)); text_info_line_3.set(to_string_dec_uint(m0_fragments, 6)); @@ -58,9 +65,11 @@ void DfuMenu::paint(Painter& painter) { text_info_line_8.set(to_string_dec_uint(shared_memory.m4_performance_counter, 6)); text_info_line_9.set(to_string_dec_uint(shared_memory.m4_buffer_missed, 6)); text_info_line_10.set(to_string_dec_uint(chTimeNow() / 1000, 6)); + if (portapack::battery_ads1110.isDetected()) { + text_info_line_11.set(to_string_decimal_padding(portapack::battery_ads1110.readVoltage(), 3, 6)); + } constexpr auto margin = 5; - constexpr auto lines = 10 + 2; painter.fill_rectangle( {{6 * CHARACTER_WIDTH - margin, 3 * LINE_HEIGHT - margin}, diff --git a/firmware/application/apps/ui_dfu_menu.hpp b/firmware/application/apps/ui_dfu_menu.hpp index 9729bcb7..91e3f6ee 100644 --- a/firmware/application/apps/ui_dfu_menu.hpp +++ b/firmware/application/apps/ui_dfu_menu.hpp @@ -58,7 +58,9 @@ class DfuMenu : public View { {{6 * CHARACTER_WIDTH, 11 * LINE_HEIGHT}, "M4 stack:", Color::dark_cyan()}, {{6 * CHARACTER_WIDTH, 12 * LINE_HEIGHT}, "M4 cpu %:", Color::dark_cyan()}, {{6 * CHARACTER_WIDTH, 13 * LINE_HEIGHT}, "M4 miss:", Color::dark_cyan()}, - {{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "uptime:", Color::dark_cyan()}}; + {{6 * CHARACTER_WIDTH, 14 * LINE_HEIGHT}, "Uptime:", Color::dark_cyan()}}; + + Labels voltage_label{{{6 * CHARACTER_WIDTH, 15 * LINE_HEIGHT}, "Voltage:", Color::dark_cyan()}}; Text text_info_line_1{{15 * CHARACTER_WIDTH, 5 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; Text text_info_line_2{{15 * CHARACTER_WIDTH, 6 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; @@ -70,6 +72,7 @@ class DfuMenu : public View { Text text_info_line_8{{15 * CHARACTER_WIDTH, 12 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; Text text_info_line_9{{15 * CHARACTER_WIDTH, 13 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; Text text_info_line_10{{15 * CHARACTER_WIDTH, 14 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; + Text text_info_line_11{{15 * CHARACTER_WIDTH, 15 * LINE_HEIGHT, 6 * CHARACTER_WIDTH, 1 * LINE_HEIGHT}, ""}; }; class DfuMenu2 : public View { diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index 6010c604..806a1f33 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -52,6 +52,7 @@ using asahi_kasei::ak4951::AK4951; #include "sd_card.hpp" #include "string_format.hpp" #include "bitmap.hpp" +#include "ui_widget.hpp" namespace portapack { @@ -84,6 +85,7 @@ ClockManager clock_manager{ WM8731 audio_codec_wm8731{i2c0, 0x1a}; AK4951 audio_codec_ak4951{i2c0, 0x12}; +ads1110::ADS1110 battery_ads1110{i2c0, 0x48}; ReceiverModel receiver_model; TransmitterModel transmitter_model; @@ -585,6 +587,7 @@ init_status_t init() { chThdSleepMilliseconds(10); audio::init(portapack_audio_codec()); + battery_ads1110.init(); if (lcd_fast_setup) draw_splash_screen_icon(4, ui::bitmap_icon_speaker); diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index f9f23d6e..ba5f1147 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -33,6 +33,8 @@ #include "backlight.hpp" #include "usb_serial.hpp" +#include "ads1110.hpp" + #include "radio.hpp" #include "clock_manager.hpp" #include "temperature_logger.hpp" @@ -61,6 +63,7 @@ extern portapack::USBSerial usb_serial; extern si5351::Si5351 clock_generator; extern ClockManager clock_manager; +extern ads1110::ADS1110 battery_ads1110; extern ReceiverModel receiver_model; extern TransmitterModel transmitter_model; diff --git a/firmware/application/string_format.cpp b/firmware/application/string_format.cpp index fefbc16f..381cb75f 100644 --- a/firmware/application/string_format.cpp +++ b/firmware/application/string_format.cpp @@ -172,6 +172,31 @@ std::string to_string_decimal(float decimal, int8_t precision) { return result; } +std::string to_string_decimal_padding(float decimal, int8_t precision, const int32_t l) { + double integer_part; + double fractional_part; + + std::string result; + if (precision > 9) precision = 9; // we will convert to uin32_t, and that is the max it can hold. + + fractional_part = modf(decimal, &integer_part) * pow(10, precision); + + if (fractional_part < 0) { + fractional_part = -fractional_part; + } + + result = to_string_dec_int(integer_part) + "." + to_string_dec_uint(fractional_part, precision, '0'); + + // Add padding with spaces to meet the length requirement + if (result.length() < l) { + int padding_length = l - result.length(); + std::string padding(padding_length, ' '); + result = padding + result; + } + + return result; +} + // right-justified frequency in Hz, always 10 characters std::string to_string_freq(const uint64_t f) { std::string final_str{""}; diff --git a/firmware/application/string_format.hpp b/firmware/application/string_format.hpp index 4413afc2..cfebd70f 100644 --- a/firmware/application/string_format.hpp +++ b/firmware/application/string_format.hpp @@ -54,6 +54,7 @@ std::string to_string_bin(const uint32_t n, const uint8_t l = 0); std::string to_string_dec_uint(const uint32_t n, const int32_t l, const char fill = ' '); std::string to_string_dec_int(const int32_t n, const int32_t l, const char fill = 0); std::string to_string_decimal(float decimal, int8_t precision); +std::string to_string_decimal_padding(float decimal, int8_t precision, const int32_t l); std::string to_string_hex(uint64_t n, int32_t length); std::string to_string_hex_array(uint8_t* array, int32_t length); diff --git a/firmware/common/ads1110.cpp b/firmware/common/ads1110.cpp new file mode 100644 index 00000000..04bed5ea --- /dev/null +++ b/firmware/common/ads1110.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2024 jLynx. + * + * 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 "ads1110.hpp" +#include "utility.hpp" +#include +#include + +namespace ads1110 { + +constexpr float BATTERY_MIN_VOLTAGE = 3.0; +constexpr float BATTERY_MAX_VOLTAGE = 4.0; + +void ADS1110::init() { + if (!detected_) { + detected_ = detect(); + } + if (detected_) { + // Set the configuration register + write(0x8C); + } +} + +bool ADS1110::detect() { + uint8_t data[3]; + if (bus.receive(bus_address, data, 3)) { + // Check if the received data is valid + uint8_t configRegister = data[2]; + if ((configRegister & 0x0F) == 0x0C) { + // The configuration register value matches the expected value (0x8C) + detected_ = true; + return true; + } + } + return false; +} + +bool ADS1110::write(const uint8_t value) { + return bus.transmit(bus_address, &value, 1); +} + +float ADS1110::readVoltage() { + // Read the conversion result + uint8_t data[3]; + if (!bus.receive(bus_address, data, 3)) { + return 0.0f; // Return 0 if the read fails + } + + uint16_t raw = (static_cast(data[0]) << 8) | data[1]; + + // Calculate the voltage based on the output code + float voltage = 0.0f; + float minCode = 0; + float pga = 0.0f; + + uint8_t pga_rate = data[2] & 0x03; + switch (pga_rate) { + case 0: + pga = 1.0f; + break; + case 1: + pga = 2.0f; + break; + case 2: + pga = 4.0f; + break; + case 3: + pga = 8.0f; + break; + default: + // Handle invalid data rate + break; + } + + uint8_t data_rate = (data[2] >> 2) & 0x03; + switch (data_rate) { + case 0: // 240 + minCode = -2048.0; + break; + case 1: // 60 + minCode = -8192.0; + break; + case 2: // 30 + minCode = -16384.0; + break; + case 3: // 15 + minCode = -32768.0; + break; + default: + // Handle invalid data rate + break; + } + + // 2.048 is the reference voltage & 2.0 is to make up for the voltage divider + voltage = raw / (-1.0 * minCode) * pga * 2.048 * 2.0; + + return voltage; +} + +void ADS1110::getBatteryInfo(float& batteryPercentage, float& voltage) { + voltage = readVoltage(); + + // Calculate the remaining battery percentage + batteryPercentage = (voltage - BATTERY_MIN_VOLTAGE) / + (BATTERY_MAX_VOLTAGE - BATTERY_MIN_VOLTAGE) * 100.0; + + // Limit the values to the valid range + batteryPercentage = std::clamp(batteryPercentage, 0.0f, 100.0f); + // ToDo: if its > 4, then 100%, if < 3 then 0% +} + +} /* namespace ads1110 */ diff --git a/firmware/common/ads1110.hpp b/firmware/common/ads1110.hpp new file mode 100644 index 00000000..7b5231d2 --- /dev/null +++ b/firmware/common/ads1110.hpp @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2024 jLynx. + * + * 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. + */ + +#ifndef __ADS1110_H__ +#define __ADS1110_H__ + +#include +#include +#include + +#include "i2c_pp.hpp" + +namespace ads1110 { + +using address_t = uint8_t; +using reg_t = uint16_t; + +class ADS1110 { + public: + constexpr ADS1110(I2C& bus, const I2C::address_t bus_address) + : bus(bus), bus_address(bus_address), detected_(false) {} + + void init(); + bool isDetected() const { return detected_; } + + float readVoltage(); + void getBatteryInfo(float& batteryPercentage, float& voltage); + + private: + I2C& bus; + const I2C::address_t bus_address; + bool detected_; + + bool write(const uint8_t value); + bool detect(); +}; + +} /* namespace ads1110 */ + +#endif /* __ADS1110_H__ */ \ No newline at end of file