From 732aeb8c17c0ca57a04840a8193ad84ce8978deb Mon Sep 17 00:00:00 2001 From: GOB Date: Fri, 13 Dec 2024 17:32:50 +0900 Subject: [PATCH 01/10] Fixes typo --- src/unit/unit_MCP4725.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/unit/unit_MCP4725.hpp b/src/unit/unit_MCP4725.hpp index c854e05..9861c2a 100644 --- a/src/unit/unit_MCP4725.hpp +++ b/src/unit/unit_MCP4725.hpp @@ -193,14 +193,14 @@ class UnitMCP4725 : public Component { /*! @brief Read the DAC register @param[out] pd Power down mode - @paran[out] raw Output voltage raw value + @param[out] raw Output voltage raw value @return True if successful */ bool readDACRegister(mcp4725::PowerDown& pd, uint16_t& raw); /*! @brief Read the EEPROM settings @param[out] pd Power down mode - @paran[out] raw Output voltage raw value + @param[out] raw Output voltage raw value @return True if successful */ bool readEEPROM(mcp4725::PowerDown& pd, uint16_t& raw); From c79d9d3259e64602f7aa721d2c7c979845e6d75f Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:07:50 +0900 Subject: [PATCH 02/10] Add UnitADC(ADS1110) --- .../workflows/arduino-esp-v2-build-check.yml | 24 +- .../UnitADC/PlotToSerial/PlotToSerial.ino | 9 + .../PlotToSerial/main/PlotToSerial.cpp | 64 ++++ platformio.ini | 169 +++++++++- src/M5UnitUnifiedANADIG.hpp | 2 + src/unit/unit_ADS1110.cpp | 294 ++++++++++++++++++ src/unit/unit_ADS1110.hpp | 237 ++++++++++++++ src/unit/unit_GP8413.hpp | 4 +- src/unit/unit_MCP4725.hpp | 4 +- test/embedded/test_ads1110/ads1110_test.cpp | 239 ++++++++++++++ 10 files changed, 1034 insertions(+), 12 deletions(-) create mode 100644 examples/UnitUnified/UnitADC/PlotToSerial/PlotToSerial.ino create mode 100644 examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp create mode 100644 src/unit/unit_ADS1110.cpp create mode 100644 src/unit/unit_ADS1110.hpp create mode 100644 test/embedded/test_ads1110/ads1110_test.cpp diff --git a/.github/workflows/arduino-esp-v2-build-check.yml b/.github/workflows/arduino-esp-v2-build-check.yml index c2e9e60..db4b5bd 100644 --- a/.github/workflows/arduino-esp-v2-build-check.yml +++ b/.github/workflows/arduino-esp-v2-build-check.yml @@ -57,20 +57,22 @@ jobs: - https://espressif.github.io/arduino-esp32/package_esp32_index.json sketch: + - PlotToSerial - Output unit: + - UnitADC - UnitDAC - UnitDAC2 board: - - m5stack-atom - - m5stack-atoms3 - - m5stack-core-esp32 + #- m5stack-atom + #- m5stack-atoms3 + #- m5stack-core-esp32 - m5stack-core2 - - m5stack-coreink - - m5stack-cores3 - - m5stack-fire + #- m5stack-coreink + #- m5stack-cores3 + #- m5stack-fire platform-version: - 2.0.17 @@ -81,6 +83,14 @@ jobs: archi: - esp32 + exclude: + - sketch: Output + unit: UnitADC + - sketch: PlotToSerial + unit: UnitDAC + - sketch: PlotToSerial + unit: UnitDAC2 + steps: - name: Checkout uses: actions/checkout@v4 @@ -89,7 +99,7 @@ jobs: # Build - name: Compile examples - uses: ArminJo/arduino-test-compile@master + if: with: arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} diff --git a/examples/UnitUnified/UnitADC/PlotToSerial/PlotToSerial.ino b/examples/UnitUnified/UnitADC/PlotToSerial/PlotToSerial.ino new file mode 100644 index 0000000..0ba50f9 --- /dev/null +++ b/examples/UnitUnified/UnitADC/PlotToSerial/PlotToSerial.ino @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for UnitADC +*/ +#include "main/PlotToSerial.cpp" diff --git a/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp b/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp new file mode 100644 index 0000000..5cfa1d4 --- /dev/null +++ b/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + Example using M5UnitUnified for UnitADC +*/ +#include +#include +#include +#include + +namespace { +auto& lcd = M5.Display; +m5::unit::UnitUnified Units; +m5::unit::UnitADC unit; +} // namespace + +using namespace m5::unit::ads1110; + +void setup() +{ + M5.begin(); + + auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); + auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); + M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl); + Wire.begin(pin_num_sda, pin_num_scl, 100 * 1000U); + + if (!Units.add(unit, Wire) || !Units.begin()) { + M5_LOGE("Failed to begin"); + lcd.clear(TFT_RED); + while (true) { + m5::utility::delay(10000); + } + } + M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("%s", Units.debugInfo().c_str()); + + lcd.clear(TFT_DARKGREEN); +} + +void loop() +{ + M5.update(); + auto touch = M5.Touch.getDetail(); + + // Periodic + Units.update(); + if (unit.updated()) { + M5_LOGI("\n>Diff:%d\n>DiffMV:%f", unit.differentialValue(), unit.differentialVoltage()); + } + + // Single + if (M5.BtnA.wasClicked() || touch.wasClicked()) { + unit.stopPeriodicMeasurement(); + Data d{}; + if (unit.measureSingleshot(d)) { + M5_LOGI("Single: %d/%f", d.differentialValue(), d.differentialVoltage()); + } + unit.startPeriodicMeasurement(); + } +} diff --git a/platformio.ini b/platformio.ini index 57222f6..811aaf0 100644 --- a/platformio.ini +++ b/platformio.ini @@ -181,6 +181,92 @@ lib_deps = google/googletest@1.12.1 ; -------------------------------- ; UnitTest ; -------------------------------- +; ADC +[env:test_UnitADC_Core] +extends=Core, option_release, arduino_latest +lib_deps = ${Core.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_Core2] +extends=Core2, option_release, arduino_latest +lib_deps = ${Core2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_CoreS3] +extends=CoreS3, option_release, arduino_latest +lib_deps = ${CoreS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_Fire] +extends=Fire, option_release, arduino_latest +lib_deps = ${Fire.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_StampS3] +extends=StampS3, option_release, arduino_latest +lib_deps = ${StampS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_Dial] +extends=Dial, option_release, arduino_latest +lib_deps = ${Dial.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_AtomMatrix] +extends=AtomMatrix, option_release, arduino_latest +lib_deps = ${AtomMatrix.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_AtomS3] +extends=AtomS3, option_release, arduino_latest +lib_deps = ${AtomS3.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_AtomS3R] +extends=AtomS3R, option_release, arduino_latest +lib_deps = ${AtomS3R.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_NanoC6] +extends=NanoC6, option_release, arduino_latest +lib_deps = ${NanoC6.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_StickCPlus] +extends=StickCPlus, option_release, arduino_latest +lib_deps = ${StickCPlus.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_StickCPlus2] +extends=StickCPlus2, option_release, arduino_latest +lib_deps = ${StickCPlus2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_Paper] +extends=Paper, option_release, arduino_latest +lib_deps = ${Paper.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +[env:test_UnitADC_CoreInk] +extends=CoreInk, option_release, arduino_latest +lib_deps = ${CoreInk.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_ads1110 + +; DAC [env:test_UnitDAC_Core] extends=Core, option_release, arduino_latest lib_deps = ${Core.lib_deps} @@ -265,7 +351,7 @@ lib_deps = ${CoreInk.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_mcp4725 - +; DAC2 [env:test_UnitDAC2_Core] extends=Core, option_release, arduino_latest lib_deps = ${Core.lib_deps} @@ -354,6 +440,87 @@ test_filter= embedded/test_gp8413 ; -------------------------------- ; Examples by M5UnitUnified ; -------------------------------- +; ADC +[env:UnitADC_PlotToSerial_Core_Arduino_latest] +extends=Core, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Core_Arduino_5_4_0] +extends=Core, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Core_Arduino_4_4_0] +extends=Core, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Core2_Arduino_latest] +extends=Core2, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Core2_Arduino_5_4_0] +extends=Core2, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Core2_Arduino_4_4_0] +extends=Core2, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_CoreS3_Arduino_latest] +extends=CoreS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_StampS3_Arduino_latest] +extends=StampS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_AtomMatrix_Arduino_latest] +extends=AtomMatrix, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_AtomS3_Arduino_latest] +extends=AtomS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_AtomS3R_Arduino_latest] +extends=AtomS3R, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Dial_Arduino_latest] +extends=Dial, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_NanoC6_Arduino_latest] +extends=NanoC6, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_StickCPlus_Arduino_latest] +extends=StickCPlus, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_StickCPlus2_Arduino_latest] +extends=StickCPlus2, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Paper_Arduino_latest] +extends=Paper, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_CoreInk_Arduino_latest] +extends=CoreInk, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Fire_Arduino_latest] +extends=Fire, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Fire_Arduino_5_4_0] +extends=Fire, option_release, arduino_5_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + +[env:UnitADC_PlotToSerial_Fire_Arduino_4_4_0] +extends=Fire, option_release, arduino_4_4_0 +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitADC/PlotToSerial> + ;DAC [env:UnitDAC_Output_Core_Arduino_latest] extends=Core, option_release, arduino_latest diff --git a/src/M5UnitUnifiedANADIG.hpp b/src/M5UnitUnifiedANADIG.hpp index ac76ad9..7d21ccc 100644 --- a/src/M5UnitUnifiedANADIG.hpp +++ b/src/M5UnitUnifiedANADIG.hpp @@ -13,6 +13,7 @@ #ifndef M5_UNIT_UNIFIED_ANADIG_HPP #define M5_UNIT_UNIFIED_ANADIG_HPP +#include "unit/unit_ADS1110.hpp" #include "unit/unit_MCP4725.hpp" #include "unit/unit_GP8413.hpp" @@ -28,6 +29,7 @@ namespace m5 { */ namespace unit { +using UnitADC = m5::unit::UnitADS1110; using UnitDAC = m5::unit::UnitMCP4725; using UnitDAC2 = m5::unit::UnitGP8413; diff --git a/src/unit/unit_ADS1110.cpp b/src/unit/unit_ADS1110.cpp new file mode 100644 index 0000000..be62539 --- /dev/null +++ b/src/unit/unit_ADS1110.cpp @@ -0,0 +1,294 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file unit_ADS1110.cpp + @brief ADS1110 Unit for M5UnitUnified +*/ +#include "unit_ADS1110.hpp" +#include + +using namespace m5::utility::mmh3; +using namespace m5::unit::types; +using namespace m5::unit::ads1110; + +namespace { +constexpr uint32_t interval_table[] = { + 1000 / 250, + 1000 / 60, + 1000 / 30, + 1000 / 15, +}; + +constexpr uint8_t CONFIG_DEFAULT_VALUE{0x8C}; + +} // namespace + +struct Config { + inline Sampling rate() const + { + return static_cast((value >> 2) & 0x03); + } + inline PGA pga() const + { + return static_cast(value & 0x03); + } + inline bool continuous() const + { + return value & (1U << 4); + } + inline bool single() const + { + return !continuous(); + } + inline bool ready() const + { + return (value & 0x80) == 0; + } + + inline void rate(const Sampling rate) + { + value = (value & ~(0x03 << 2)) | (m5::stl::to_underlying(rate) << 2); + } + inline void pga(const PGA pga) + { + value = (value & ~0x03) | m5::stl::to_underlying(pga); + } + inline void continuous(bool enable) + { + value = (value & ~(1U << 4)) | ((enable ? 0 : 1) << 4); + } + inline void single(bool enable) + { + continuous(false); + } + inline void ready(const bool b) + { + value = (value & ~0x80) | (b ? 0x80 : 0x00); + } + + uint8_t value{}; +}; + +namespace m5 { +namespace unit { + +namespace ads1110 { +const float Data::gain_table[4] = {2.048f, 1.024f, 0.512f, 0.256f}; +} + +const char UnitADS1110::name[] = "UnitADS1110"; +const types::uid_t UnitADS1110::uid{"UnitADS1110"_mmh3}; +const types::uid_t UnitADS1110::attr{0}; + +bool UnitADS1110::begin() +{ + auto ssize = stored_size(); + assert(ssize && "stored_size must be greater than zero"); + if (ssize != _data->capacity()) { + _data.reset(new m5::container::CircularBuffer(ssize)); + if (!_data) { + M5_LIB_LOGE("Failed to allocate"); + return false; + } + } + + uint8_t c{}; + if (!read_config(c)) { + M5_LIB_LOGE("Can not detect ADS1110 %02X", c); + return false; + } + return _cfg.start_periodic ? startPeriodicMeasurement(_cfg.sampling_rate, _cfg.pga) : stopPeriodicMeasurement(); +} + +void UnitADS1110::update(const bool force) +{ + _updated = false; + if (inPeriodic()) { + elapsed_time_t at{m5::utility::millis()}; + if (force || !_latest || at >= _latest + _interval) { + Data d{}; + _updated = is_data_ready() && read_measurement(d.raw.data()); + if (_updated) { + d.pga = _pga; + _latest = at; + _data->push_back(d); + } + } + } +} + +bool UnitADS1110::start_periodic_measurement(const ads1110::Sampling rate, const ads1110::PGA pga) +{ + if (inPeriodic()) { + return false; + } + + Config c{}; + if (read_config(c.value)) { + c.rate(rate); + c.pga(pga); + c.continuous(true); + _periodic = write_config(c.value); + if (_periodic) { + _pga = c.pga(); + _interval = interval_table[m5::stl::to_underlying(rate)]; + _latest = 0; + } + } + return _periodic; +} + +bool UnitADS1110::start_periodic_measurement() +{ + Config c{}; + return read_config(c.value) && start_periodic_measurement(c.rate(), c.pga()); +} + +bool UnitADS1110::stop_periodic_measurement() +{ + Config c{}; + if (read_config(c.value)) { + // Periodic measurement stops is substituted by a change to single mode + c.single(true); + if (write_config(c.value)) { + _periodic = false; + return true; + } + } + return false; +} + +bool UnitADS1110::measureSingleshot(ads1110::Data& data, const ads1110::Sampling rate, const ads1110::PGA pga) +{ + if (inPeriodic()) { + M5_LIB_LOGD("Periodic measurements are running"); + return false; + } + + Config c{}; + if (read_config(c.value)) { + c.ready(true); + c.rate(rate); + c.pga(pga); + c.single(true); + if (write_config(c.value)) { + auto timeout_at = m5::utility::millis() + 100; + do { + if (is_data_ready() && read_measurement(data.raw.data())) { + data.pga = pga; + return true; + } + } while (m5::utility::millis() <= timeout_at); + } + } + return false; +} + +bool UnitADS1110::measureSingleshot(ads1110::Data& data) +{ + Config c{}; + return read_config(c.value) && measureSingleshot(data, c.rate(), c.pga()); +} + +bool UnitADS1110::generalReset() +{ + uint8_t cmd{0x06}; // reset command + // Reset does not return ACK, which is an error, but should be ignored + generalCall(&cmd, 1); + + Config c{}; + auto timeout_at = m5::utility::millis() + 100; + do { + if (read_config(c.value) && c.value == CONFIG_DEFAULT_VALUE) { + return stop_periodic_measurement(); + } + m5::utility::delay(1); + + } while (m5::utility::millis() <= timeout_at); + return false; +} + +bool UnitADS1110::readSamplingRate(ads1110::Sampling& rate) +{ + Config c{}; + if (read_config(c.value)) { + rate = c.rate(); + return true; + } + return false; +} + +bool UnitADS1110::writeSamplingRate(const ads1110::Sampling rate) +{ + if (inPeriodic()) { + M5_LIB_LOGD("Periodic measurements are running"); + return false; + } + + Config c{}; + if (read_config(c.value)) { + c.rate(rate); + return write_config(c.value); + } + return false; +} + +bool UnitADS1110::readPGA(ads1110::PGA& pga) +{ + Config c{}; + if (read_config(c.value)) { + pga = c.pga(); + return true; + } + return false; +} + +bool UnitADS1110::writePGA(const ads1110::PGA pga) +{ + if (inPeriodic()) { + M5_LIB_LOGD("Periodic measurements are running"); + return false; + } + + Config c{}; + if (read_config(c.value)) { + c.pga(pga); + return write_config(c.value); + } + return false; +} + +// +bool UnitADS1110::read_config(uint8_t& v) +{ + uint8_t rbuf[3]{}; // [0,]:data [2]:config + if ((writeWithTransaction(nullptr, 0U) == m5::hal::error::error_t::OK) && + (readWithTransaction(rbuf, 3) == m5::hal::error::error_t::OK)) { + v = rbuf[2]; + return true; + } + return false; +} + +bool UnitADS1110::write_config(const uint8_t v) +{ + return (writeWithTransaction(&v, 1) == m5::hal::error::error_t::OK); +} + +bool UnitADS1110::read_measurement(uint8_t* v) +{ + return (writeWithTransaction(nullptr, 0U) == m5::hal::error::error_t::OK) && + (readWithTransaction(v, 2) == m5::hal::error::error_t::OK); +} + +bool UnitADS1110::is_data_ready() +{ + Config c{}; + return read_config(c.value) && c.ready(); +} + +} // namespace unit +} // namespace m5 diff --git a/src/unit/unit_ADS1110.hpp b/src/unit/unit_ADS1110.hpp new file mode 100644 index 0000000..e9ee66b --- /dev/null +++ b/src/unit/unit_ADS1110.hpp @@ -0,0 +1,237 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/*! + @file unit_ADS1110.hpp + @brief ADS1110 Unit for M5UnitUnified +*/ +#ifndef M5_UNIT_ANADIG_UNIT_ADS1110_HPP +#define M5_UNIT_ANADIG_UNIT_ADS1110_HPP +#include +#include +#include +#include // NaN + +namespace m5 { +namespace unit { + +/*! + @namespace mcp4725 + @brief For ADS1110 +*/ +namespace ads1110 { +/*! + @enum Sampling + @brief Data sampling rate for periodic + */ +enum class Sampling : uint8_t { + Rate240, //!< 240 SPS + Rate60, //!< 60 SPS + Rate30, //!< 30 SPS + Rate15, //!< 15 SPS as default +}; + +/*! + @enum PGA + @brief Programmable Gain Amplifier + */ +enum class PGA : uint8_t { + Gain1, //!< 1 as default + Gain2, //!< 2 + Gain4, //!< 4 + Gain8, //!< 8 +}; + +/*! + @struct Data + @brief Measurement data group + */ +struct Data { + std::array raw{}; //!< Raw + PGA pga{}; //!< PGA + + ///! @brief Gets the differential value + inline int16_t differentialValue() const + { + return (int16_t)m5::types::big_uint16_t(raw[0], raw[1]).get(); + } + //! @brief differential value to differential voltage(mV) + inline float differentialVoltage() const + { + return (differentialValue() * gain_table[m5::stl::to_underlying(pga)]) / 32768.f * 1000.f; + } + static const float gain_table[4]; +}; + +} // namespace ads1110 + +/*! + @class UnitADS1110 + @brief 16-bit, self-calibrating, delta-sigma A/D converter +*/ +class UnitADS1110 : public Component, public PeriodicMeasurementAdapter { + M5_UNIT_COMPONENT_HPP_BUILDER(UnitADS1110, 0x48); + +public: + /*! + @struct config_t + @brief Settings for begin + */ + struct config_t { + //! Start periodic measurement on begin? + bool start_periodic{true}; + //! Data sampling rate if start on begin + ads1110::Sampling sampling_rate{ads1110::Sampling::Rate15}; + //! PGA if start on begin + ads1110::PGA pga{ads1110::PGA::Gain1}; + }; + + explicit UnitADS1110(const uint8_t addr = DEFAULT_ADDRESS) + : Component(addr), _data{new m5::container::CircularBuffer(1)} + { + auto ccfg = component_config(); + ccfg.clock = 400 * 1000U; + component_config(ccfg); + } + virtual ~UnitADS1110() + { + } + + virtual bool begin() override; + virtual void update(const bool force = false) override; + + ///@name Settings for begin + ///@{ + /*! @brief Gets the configration */ + inline config_t config() + { + return _cfg; + } + //! @brief Set the configration + inline void config(const config_t& cfg) + { + _cfg = cfg; + } + ///@} + + ///@name Measurement data by periodic + ///@{ + //! @brief Oldest measured differential value + inline int16_t differentialValue() const + { + return !empty() ? oldest().differentialValue() : 0; + } + //! @brief Oldest measured differential voltage(mV) + inline float differentialVoltage() const + { + return !empty() ? oldest().differentialVoltage() : std::numeric_limits::quiet_NaN(); + } + ///@} + + ///@name Settings + ///@{ + /*! + @brief Read the Sampling rate + @param[out] rate Sampling rate + @return True if successful + */ + bool readSamplingRate(ads1110::Sampling& rate); + /*! + @brief Write the Sampling rate + @param rate Sampling rate + @return True if successful + @warning During periodic detection runs, an error is returned + */ + bool writeSamplingRate(const ads1110::Sampling rate); + /*! + @brief Read the PGA + @param[out] pga PGA + @return True if successful + */ + bool readPGA(ads1110::PGA& pga); + /*! + @brief Write the PGA + @param pga PGA + @return True if successful + @warning During periodic detection runs, an error is returned + */ + bool writePGA(const ads1110::PGA pga); + ///@} + + ///@name Periodic measurement + ///@{ + /*! + @brief Start periodic measurement + @param rate Data sampling rate + @param pga Programmable Gain Amplifier + @return True if successful + */ + inline bool startPeriodicMeasurement(const ads1110::Sampling rate, const ads1110::PGA pga) + { + return start_periodic_measurement(rate, pga); + } + //! @brief Start periodic measurement using current settings + inline bool startPeriodicMeasurement() + { + return start_periodic_measurement(); + } + /*! + @brief Stop periodic measurement + @return True if successful + */ + inline bool stopPeriodicMeasurement() + { + return stop_periodic_measurement(); + } + ///@} + + ///@name Single shot measurement + ///@{ + /*! + @brief Measurement single shot + @param[out] data Measuerd data + @param rate Data sampling rate + @param pga Programmable Gain Amplifier + @return True if successful + @note Blocked until the end of measurement + @note Blocking time depends on rate value + @warning During periodic detection runs, an error is returned + @warning Each setting is overwritten + */ + bool measureSingleshot(ads1110::Data& data, const ads1110::Sampling rate, const ads1110::PGA pga); + //! @brief Measurement single shot using current settings + bool measureSingleshot(ads1110::Data& data); + ///@} + + /*! + @brief General reset + @details Reset using I2C general call + @return True if successful + @warning This is a reset by General command, the command is also sent to all devices with I2C connections + */ + bool generalReset(); + +protected: + bool read_config(uint8_t& v); + bool write_config(const uint8_t v); + bool read_measurement(uint8_t* v); + bool is_data_ready(); + + bool start_periodic_measurement(const ads1110::Sampling rate, const ads1110::PGA pga); + bool start_periodic_measurement(); + bool stop_periodic_measurement(); + + M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(UnitADS1110, ads1110::Data); + +private: + std::unique_ptr> _data{}; + ads1110::PGA _pga{ads1110::PGA::Gain1}; + config_t _cfg{}; +}; + +} // namespace unit +} // namespace m5 + +#endif diff --git a/src/unit/unit_GP8413.hpp b/src/unit/unit_GP8413.hpp index 5287f8a..ea9da5b 100644 --- a/src/unit/unit_GP8413.hpp +++ b/src/unit/unit_GP8413.hpp @@ -7,8 +7,8 @@ @file unit_GP8413.hpp @brief GP8413 Unit for M5UnitUnified */ -#ifndef M5_UNIT_ENV_UNIT_GP8413_HPP -#define M5_UNIT_ENV_UNIT_GP8413_HPP +#ifndef M5_UNIT_ANADIG_UNIT_GP8413_HPP +#define M5_UNIT_ANADIG_UNIT_GP8413_HPP #include namespace m5 { diff --git a/src/unit/unit_MCP4725.hpp b/src/unit/unit_MCP4725.hpp index 9861c2a..066ec2d 100644 --- a/src/unit/unit_MCP4725.hpp +++ b/src/unit/unit_MCP4725.hpp @@ -7,8 +7,8 @@ @file unit_MCP4725.hpp @brief MCP4725 Unit for M5UnitUnified */ -#ifndef M5_UNIT_ENV_UNIT_MCP4725_HPP -#define M5_UNIT_ENV_UNIT_MCP4725_HPP +#ifndef M5_UNIT_ANADIG_UNIT_MCP4725_HPP +#define M5_UNIT_ANADIG_UNIT_MCP4725_HPP #include namespace m5 { diff --git a/test/embedded/test_ads1110/ads1110_test.cpp b/test/embedded/test_ads1110/ads1110_test.cpp new file mode 100644 index 0000000..b4f18b6 --- /dev/null +++ b/test/embedded/test_ads1110/ads1110_test.cpp @@ -0,0 +1,239 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +/* + UnitTest for UnitADS1110 +*/ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace m5::unit::googletest; +using namespace m5::unit; +using namespace m5::unit::ads1110; +using m5::unit::types::elapsed_time_t; + +const ::testing::Environment* global_fixture = ::testing::AddGlobalTestEnvironment(new GlobalFixture<400000U>()); + +constexpr uint32_t STORED_SIZE{8}; + +class TestADS1110 : public ComponentTestBase { +protected: + virtual UnitADS1110* get_instance() override + { + auto ptr = new m5::unit::UnitADS1110(); + auto ccfg = ptr->component_config(); + ccfg.stored_size = STORED_SIZE; + ptr->component_config(ccfg); + return ptr; + + return ptr; + } + virtual bool is_using_hal() const override + { + return GetParam(); + }; +}; + +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestADS1110, ::testing::Values(false, true)); +// INSTANTIATE_TEST_SUITE_P(ParamValues, TestADS1110, ::testing::Values(true)); +INSTANTIATE_TEST_SUITE_P(ParamValues, TestADS1110, ::testing::Values(false)); + +namespace { +constexpr Sampling rate_table[] = { + Sampling::Rate240, + Sampling::Rate60, + Sampling::Rate30, + Sampling::Rate15, +}; +constexpr PGA pga_table[] = {PGA::Gain1, PGA::Gain2, PGA::Gain4, PGA::Gain8}; + +constexpr uint32_t interval_table[] = {1000 / 240, 1000 / 60, 1000 / 30, 1000 / 15}; + +template +elapsed_time_t test_periodic(U* unit, const uint32_t times, const uint32_t measure_duration = 0) +{ + auto tm = unit->interval(); + auto timeout_at = m5::utility::millis() + 10 * 1000; + + do { + unit->update(); + if (unit->updated()) { + break; + } + std::this_thread::yield(); + } while (!unit->updated() && m5::utility::millis() <= timeout_at); + // timeout + if (!unit->updated()) { + return 0; + } + + // + uint32_t measured{}; + auto start_at = m5::utility::millis(); + timeout_at = start_at + (times * (tm + measure_duration) * 2); + + do { + unit->update(); + measured += unit->updated() ? 1 : 0; + if (measured >= times) { + break; + } + std::this_thread::yield(); + + } while (measured < times && m5::utility::millis() <= timeout_at); + return (measured == times) ? m5::utility::millis() - start_at : 0; + // return (measured == times) ? unit->updatedMillis() - start_at : 0; +} + +} // namespace + +TEST_P(TestADS1110, Settings) +{ + SCOPED_TRACE(ustr); + + EXPECT_TRUE(unit->inPeriodic()); + + // Faild in periodic + for (auto&& r : rate_table) { + EXPECT_FALSE(unit->writeSamplingRate(r)); + } + for (auto&& p : pga_table) { + EXPECT_FALSE(unit->writePGA(p)); + } + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + for (auto&& r : rate_table) { + EXPECT_TRUE(unit->writeSamplingRate(r)); + Sampling sr{}; + EXPECT_TRUE(unit->readSamplingRate(sr)); + EXPECT_EQ(sr, r); + } + for (auto&& p : pga_table) { + EXPECT_TRUE(unit->writePGA(p)); + PGA pga{}; + EXPECT_TRUE(unit->readPGA(pga)); + EXPECT_EQ(pga, p); + } +} + +TEST_P(TestADS1110, Reset) +{ + SCOPED_TRACE(ustr); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_TRUE(unit->writeSamplingRate(Sampling::Rate60)); + EXPECT_TRUE(unit->writePGA(PGA::Gain4)); + + EXPECT_TRUE(unit->generalReset()); + + EXPECT_FALSE(unit->inPeriodic()); + Sampling sr{}; + EXPECT_TRUE(unit->readSamplingRate(sr)); + EXPECT_EQ(sr, Sampling::Rate15); + PGA pga{}; + EXPECT_TRUE(unit->readPGA(pga)); + EXPECT_EQ(pga, PGA::Gain1); + + // + EXPECT_TRUE(unit->writeSamplingRate(Sampling::Rate60)); + EXPECT_TRUE(unit->writePGA(PGA::Gain4)); + EXPECT_TRUE(unit->startPeriodicMeasurement()); + + EXPECT_TRUE(unit->generalReset()); + + EXPECT_FALSE(unit->inPeriodic()); + EXPECT_TRUE(unit->readSamplingRate(sr)); + EXPECT_EQ(sr, Sampling::Rate15); + EXPECT_TRUE(unit->readPGA(pga)); + EXPECT_EQ(pga, PGA::Gain1); +} + +TEST_P(TestADS1110, Singleshot) +{ + SCOPED_TRACE(ustr); + Data d{}; + + EXPECT_FALSE(unit->measureSingleshot(d)); + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + for (auto&& r : rate_table) { + for (auto&& p : pga_table) { + auto s = m5::utility::formatString("Rate:%u PGA:%u", r, p); + SCOPED_TRACE(s); + + uint32_t cnt{8}; + while (cnt--) { + EXPECT_TRUE(unit->measureSingleshot(d, r, p)); + EXPECT_TRUE(std::isfinite(d.differentialVoltage())); + } + } + } +} + +TEST_P(TestADS1110, Periodic) +{ + SCOPED_TRACE(ustr); + + EXPECT_TRUE(unit->inPeriodic()); + EXPECT_FALSE(unit->startPeriodicMeasurement()); + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + for (auto&& r : rate_table) { + for (auto&& p : pga_table) { + auto s = m5::utility::formatString("Rate:%u PGA:%u", r, p); + SCOPED_TRACE(s); + + EXPECT_TRUE(unit->startPeriodicMeasurement(r, p)); + EXPECT_TRUE(unit->inPeriodic()); + + auto tm = interval_table[m5::stl::to_underlying(r)]; + auto elapsed = test_periodic(unit.get(), STORED_SIZE, tm); + + EXPECT_TRUE(unit->stopPeriodicMeasurement()); + EXPECT_FALSE(unit->inPeriodic()); + + EXPECT_NE(elapsed, 0); + EXPECT_GE(elapsed, STORED_SIZE * tm); + + // M5_LOGI("TM:%u IT:%u e:%ld", tm, unit->interval(), elapsed); + // + EXPECT_EQ(unit->available(), STORED_SIZE); + EXPECT_FALSE(unit->empty()); + EXPECT_TRUE(unit->full()); + + uint32_t cnt{STORED_SIZE / 2}; + while (cnt-- && unit->available()) { + EXPECT_TRUE(std::isfinite(unit->differentialVoltage())); + EXPECT_EQ(unit->differentialValue(), unit->oldest().differentialValue()); + EXPECT_FLOAT_EQ(unit->differentialVoltage(), unit->oldest().differentialVoltage()); + EXPECT_FALSE(unit->empty()); + unit->discard(); + } + EXPECT_EQ(unit->available(), STORED_SIZE / 2); + EXPECT_FALSE(unit->empty()); + EXPECT_FALSE(unit->full()); + + unit->flush(); + EXPECT_EQ(unit->available(), 0); + EXPECT_TRUE(unit->empty()); + EXPECT_FALSE(unit->full()); + + EXPECT_FALSE(std::isfinite(unit->differentialVoltage())); + } + } +} From 377e7faf0d0c5787474abacde197ce8534cac10d Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:10:42 +0900 Subject: [PATCH 03/10] workflow test --- .github/workflows/arduino-esp-v2-build-check.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/arduino-esp-v2-build-check.yml b/.github/workflows/arduino-esp-v2-build-check.yml index db4b5bd..6fa327c 100644 --- a/.github/workflows/arduino-esp-v2-build-check.yml +++ b/.github/workflows/arduino-esp-v2-build-check.yml @@ -66,13 +66,13 @@ jobs: - UnitDAC2 board: - #- m5stack-atom - #- m5stack-atoms3 - #- m5stack-core-esp32 + - m5stack-atom + - m5stack-atoms3 + - m5stack-core-esp32 - m5stack-core2 - #- m5stack-coreink - #- m5stack-cores3 - #- m5stack-fire + - m5stack-coreink + - m5stack-cores3 + - m5stack-fire platform-version: - 2.0.17 @@ -99,7 +99,6 @@ jobs: # Build - name: Compile examples - if: with: arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} From 3d7dca1fe5b9fd3d536b883d0813e59b80fc01d2 Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:11:35 +0900 Subject: [PATCH 04/10] workflow test --- .github/workflows/arduino-esp-v2-build-check.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/arduino-esp-v2-build-check.yml b/.github/workflows/arduino-esp-v2-build-check.yml index 6fa327c..c2ed804 100644 --- a/.github/workflows/arduino-esp-v2-build-check.yml +++ b/.github/workflows/arduino-esp-v2-build-check.yml @@ -99,6 +99,7 @@ jobs: # Build - name: Compile examples + uses: ArminJo/arduino-test-compile@master with: arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} From ea5731d9183958bd03703cef3a5a3df1b646af2f Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:14:16 +0900 Subject: [PATCH 05/10] workflow test(v3) --- .github/workflows/arduino-esp-v3-build-check.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/arduino-esp-v3-build-check.yml b/.github/workflows/arduino-esp-v3-build-check.yml index cfc3b16..e064f37 100644 --- a/.github/workflows/arduino-esp-v3-build-check.yml +++ b/.github/workflows/arduino-esp-v3-build-check.yml @@ -97,6 +97,14 @@ jobs: archi: - esp32 + exclude: + - sketch: Output + unit: UnitADC + - sketch: PlotToSerial + unit: UnitDAC + - sketch: PlotToSerial + unit: UnitDAC2 + steps: - name: Checkout uses: actions/checkout@v4 From 7f8f7f727e57d95c97a4d881463a2a9eec4c68fa Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:16:40 +0900 Subject: [PATCH 06/10] Fixes exclusive settings --- .github/workflows/arduino-m5-build-check.yml | 8 ++++++++ .github/workflows/platformio-build-check.yml | 9 +++++++++ 2 files changed, 17 insertions(+) diff --git a/.github/workflows/arduino-m5-build-check.yml b/.github/workflows/arduino-m5-build-check.yml index c87cd78..99af84e 100644 --- a/.github/workflows/arduino-m5-build-check.yml +++ b/.github/workflows/arduino-m5-build-check.yml @@ -99,6 +99,14 @@ jobs: archi: - esp32 + exclude: + - sketch: Output + unit: UnitADC + - sketch: PlotToSerial + unit: UnitDAC + - sketch: PlotToSerial + unit: UnitDAC2 + steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/workflows/platformio-build-check.yml b/.github/workflows/platformio-build-check.yml index 6cabbc8..75ee65b 100644 --- a/.github/workflows/platformio-build-check.yml +++ b/.github/workflows/platformio-build-check.yml @@ -113,6 +113,15 @@ jobs: # - board: Paper # espressif32: '4_4_0' + + exclude: + - sketch: Output + unit: UnitADC + - sketch: PlotToSerial + unit: UnitDAC + - sketch: PlotToSerial + unit: UnitDAC2 + steps: - name: Checkout uses: actions/checkout@v4 From 16931a412f38a68b0594dc1864617f1583946f3c Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:23:04 +0900 Subject: [PATCH 07/10] Fixes syntax error --- .github/workflows/platformio-build-check.yml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/platformio-build-check.yml b/.github/workflows/platformio-build-check.yml index 75ee65b..76a90c1 100644 --- a/.github/workflows/platformio-build-check.yml +++ b/.github/workflows/platformio-build-check.yml @@ -54,8 +54,10 @@ jobs: matrix: example: - Output + - PlotToSerial unit: + - UnitADC - UnitDAC - UnitDAC2 @@ -113,13 +115,12 @@ jobs: # - board: Paper # espressif32: '4_4_0' - exclude: - - sketch: Output + - example: Output unit: UnitADC - - sketch: PlotToSerial + - example: PlotToSerial unit: UnitDAC - - sketch: PlotToSerial + - example: PlotToSerial unit: UnitDAC2 steps: From 0b74fb226ed362f1b1bf9ea3d53ee42e9d7018ed Mon Sep 17 00:00:00 2001 From: GOB Date: Tue, 17 Dec 2024 15:25:09 +0900 Subject: [PATCH 08/10] Fixes conditions --- .github/workflows/arduino-esp-v3-build-check.yml | 2 ++ .github/workflows/arduino-m5-build-check.yml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/.github/workflows/arduino-esp-v3-build-check.yml b/.github/workflows/arduino-esp-v3-build-check.yml index e064f37..d49cfe6 100644 --- a/.github/workflows/arduino-esp-v3-build-check.yml +++ b/.github/workflows/arduino-esp-v3-build-check.yml @@ -57,8 +57,10 @@ jobs: sketch: - Output + - PlotToSerial unit: + - UnitADC - UnitDAC - UnitDAC2 diff --git a/.github/workflows/arduino-m5-build-check.yml b/.github/workflows/arduino-m5-build-check.yml index 99af84e..a5a0d02 100644 --- a/.github/workflows/arduino-m5-build-check.yml +++ b/.github/workflows/arduino-m5-build-check.yml @@ -58,8 +58,10 @@ jobs: sketch: - Output + - PlotToSerial unit: + - UnitADC - UnitDAC - UnitDAC2 From 7c6586b8dcae09712091e385e3741f5dfdc14909 Mon Sep 17 00:00:00 2001 From: GOB Date: Mon, 6 Jan 2025 19:18:04 +0900 Subject: [PATCH 09/10] Change class name from UnitADC to UnitADC11 --- .../UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp | 2 +- src/M5UnitUnifiedANADIG.hpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp b/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp index 5cfa1d4..57f9fd3 100644 --- a/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp +++ b/examples/UnitUnified/UnitADC/PlotToSerial/main/PlotToSerial.cpp @@ -14,7 +14,7 @@ namespace { auto& lcd = M5.Display; m5::unit::UnitUnified Units; -m5::unit::UnitADC unit; +m5::unit::UnitADC11 unit; } // namespace using namespace m5::unit::ads1110; diff --git a/src/M5UnitUnifiedANADIG.hpp b/src/M5UnitUnifiedANADIG.hpp index 7d21ccc..fd69d2e 100644 --- a/src/M5UnitUnifiedANADIG.hpp +++ b/src/M5UnitUnifiedANADIG.hpp @@ -29,9 +29,9 @@ namespace m5 { */ namespace unit { -using UnitADC = m5::unit::UnitADS1110; -using UnitDAC = m5::unit::UnitMCP4725; -using UnitDAC2 = m5::unit::UnitGP8413; +using UnitADC11 = m5::unit::UnitADS1110; +using UnitDAC = m5::unit::UnitMCP4725; +using UnitDAC2 = m5::unit::UnitGP8413; } // namespace unit } // namespace m5 From 7f7a7b629b0fda6d3969a3a0c93eab50213a3f6c Mon Sep 17 00:00:00 2001 From: GOB Date: Mon, 6 Jan 2025 19:18:55 +0900 Subject: [PATCH 10/10] Update README --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index b91eb2f..f9d7af6 100644 --- a/README.md +++ b/README.md @@ -11,11 +11,16 @@ DAC, is a unit with the ability to convert digital signal to analog signal (volt ### SKU:U012-B DAC2 Unit is an I2C digital-to-analog signal conversion unit. It utilizes the GP8413 solution, which provides high precision and accuracy in its performance. This chip can linearly convert a 15-bit digital value into two independent analog voltages of 0-5V or 0-10V. The output voltage error is only 0.2%, and it achieves linearity of up to 0.01%. +### SKU:U013-V11 +ADC V1.1 Unit is an A/D conversion module that utilizes the ADS1110 chip, a 16-bit self-calibrating analog-to-digital converter. It is designed with an I2C interface, offering convenient connectivity. The module offers conversion speeds of 8, 16, 32, and 128 samples per second (SPS), providing varying levels of accuracy at 16, 15, 14, and 12 bits of resolution respectively. + + ## Related Link See also examples using conventional methods here. - [Unit DAC & Datasheet](https://docs.m5stack.com/en/unit/dac) - [Unit DAC2 & Datasheet](https://docs.m5stack.com/en/unit/Unit-DAC2) +- [Unit ADC v1.1 & Datasheet](https://docs.m5stack.com/en/unit/Unit-ADC_V1.1) ### Required Libraries: