diff --git a/.github/workflows/Arduino-Lint-Check.yml b/.github/workflows/Arduino-Lint-Check.yml index de06bf0..603bcdc 100644 --- a/.github/workflows/Arduino-Lint-Check.yml +++ b/.github/workflows/Arduino-Lint-Check.yml @@ -17,7 +17,7 @@ concurrency: jobs: lint: name: Lint Check - runs-on: [self-hosted, Linux, X64] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: arduino/arduino-lint-action@v2 diff --git a/.github/workflows/arduino-esp-v2-build-check.yml b/.github/workflows/arduino-esp-v2-build-check.yml index c62dc02..ff108a2 100644 --- a/.github/workflows/arduino-esp-v2-build-check.yml +++ b/.github/workflows/arduino-esp-v2-build-check.yml @@ -12,28 +12,28 @@ on: branches: - '*' paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-esp-v2-build-check.yml' + - '.github/workflows/arduino-esp-v2-build-check.yml' pull_request: paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-esp-v2-build-check.yml' + - '.github/workflows/arduino-esp-v2-build-check.yml' workflow_dispatch: defaults: @@ -46,9 +46,9 @@ concurrency: jobs: build: - name: ${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} + name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 12 strategy: fail-fast: false @@ -84,19 +84,27 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - # Build + - name: Prepare libraries list + id: libs + run: | + { + echo "yaml<> "$GITHUB_OUTPUT" + - name: Compile examples - uses: ArminJo/arduino-test-compile@master + uses: arduino/compile-sketches@v1 with: - arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} - arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} - platform-url: ${{ matrix.platform-url }} - required-libraries: ${{ env.REQUIRED_LIBRARIES }} - extra-arduino-cli-args: ${{ matrix.cli-args }} - #build-properties: ${{ matrix.build-properties }} - sketch-names: ${{ matrix.sketch }}.ino - sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - #sketches-exclude: ${{ matrix.sketches-exclude }} + fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} + platforms: | + - name: ${{ matrix.platform }}:${{ matrix.archi }} + source-url: ${{ matrix.platform-url }} + version: ${{ matrix.platform-version }} + libraries: ${{ steps.libs.outputs.yaml }} + sketch-paths: | + - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }}/${{ matrix.sketch }} diff --git a/.github/workflows/arduino-esp-v3-build-check.yml b/.github/workflows/arduino-esp-v3-build-check.yml index 263c8ee..246aebf 100644 --- a/.github/workflows/arduino-esp-v3-build-check.yml +++ b/.github/workflows/arduino-esp-v3-build-check.yml @@ -12,28 +12,28 @@ on: branches: - '*' paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-esp-v3-build-check.yml' + - '.github/workflows/arduino-esp-v3-build-check.yml' pull_request: paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-esp-v3-build-check.yml' + - '.github/workflows/arduino-esp-v3-build-check.yml' workflow_dispatch: defaults: @@ -46,9 +46,9 @@ concurrency: jobs: build: - name: ${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} + name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 12 strategy: fail-fast: false @@ -64,33 +64,27 @@ jobs: - UnitDDS board: + - arduino_nesso_n1 - m5stack_atom - m5stack_atoms3 - m5stack_capsule -# - m5stack_cardputer + - m5stack_cardputer - m5stack_core - m5stack_core2 - m5stack_coreink - m5stack_cores3 - m5stack_dial + - m5stack_dinmeter - m5stack_fire - m5stack_nanoc6 - m5stack_paper -# - m5stack_poe_cam -# - m5stack_stamp_c3 -# - m5stack_stamp_pico - m5stack_stamp_s3 -# - m5stack_station -# - m5stack_stickc - m5stack_stickc_plus - m5stack_stickc_plus2 -# - m5stack_timer_cam -# - m5stack_tough -# - m5stack_unit_cam -# - m5stack_unit_cams3 + - m5stack_tab5 platform-version: - - 3.1.3 + - 3.3.6 platform: - esp32 @@ -101,19 +95,27 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - # Build + - name: Prepare libraries list + id: libs + run: | + { + echo "yaml<> "$GITHUB_OUTPUT" + - name: Compile examples - uses: ArminJo/arduino-test-compile@master + uses: arduino/compile-sketches@v1 with: - arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} - arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} - platform-url: ${{ matrix.platform-url }} - required-libraries: ${{ env.REQUIRED_LIBRARIES }} - extra-arduino-cli-args: ${{ matrix.cli-args }} - #build-properties: ${{ matrix.build-properties }} - sketch-names: ${{ matrix.sketch }}.ino - sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - #sketches-exclude: ${{ matrix.sketches-exclude }} + fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} + platforms: | + - name: ${{ matrix.platform }}:${{ matrix.archi }} + source-url: ${{ matrix.platform-url }} + version: ${{ matrix.platform-version }} + libraries: ${{ steps.libs.outputs.yaml }} + sketch-paths: | + - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }}/${{ matrix.sketch }} diff --git a/.github/workflows/arduino-m5-build-check.yml b/.github/workflows/arduino-m5-build-check.yml index c8586b9..92ca657 100644 --- a/.github/workflows/arduino-m5-build-check.yml +++ b/.github/workflows/arduino-m5-build-check.yml @@ -12,28 +12,28 @@ on: branches: - '*' paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-m5-build-check.yml' + - '.github/workflows/arduino-m5-build-check.yml' pull_request: paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**arduino-m5-build-check.yml' + - '.github/workflows/arduino-m5-build-check.yml' workflow_dispatch: defaults: @@ -46,9 +46,9 @@ concurrency: jobs: build: - name: ${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} + name: ${{ matrix.unit }}:${{ matrix.sketch }}:${{matrix.board}}@${{matrix.platform-version}} runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 12 strategy: fail-fast: false @@ -64,11 +64,12 @@ jobs: - UnitDDS board: + - arduino_nesso_n1 - m5stack_atom - m5stack_atoms3 - m5stack_atoms3r - m5stack_capsule -# - m5stack_cardputer + - m5stack_cardputer - m5stack_core - m5stack_core2 - m5stack_coreink @@ -76,22 +77,17 @@ jobs: - m5stack_dial - m5stack_dinmeter - m5stack_fire + - m5stack_nano_c6 - m5stack_paper -# - m5stack_poe_cam -# - m5stack_stamp_c3 -# - m5stack_stamp_pico + - m5stack_papers3 - m5stack_stamp_s3 -# - m5stack_station -# - m5stack_stickc - m5stack_stickc_plus - m5stack_stickc_plus2 -# - m5stack_timer_cam -# - m5stack_tough -# - m5stack_unit_cam -# - m5stack_unit_cams3 + - m5stack_sticks3 + - m5stack_tab5 platform-version: - - 3.2.1 + - 3.2.5 platform: - m5stack @@ -102,20 +98,27 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - # Build + - name: Prepare libraries list + id: libs + run: | + { + echo "yaml<> "$GITHUB_OUTPUT" + - name: Compile examples - uses: ArminJo/arduino-test-compile@master + uses: arduino/compile-sketches@v1 with: - arduino-board-fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} - arduino-platform: ${{ matrix.platform }}:${{ matrix.archi }}@${{ matrix.platform-version }} - platform-url: ${{ matrix.platform-url }} - required-libraries: ${{ env.REQUIRED_LIBRARIES }} - extra-arduino-cli-args: ${{ matrix.cli-args }} - #build-properties: ${{ matrix.build-properties }} - sketch-names: ${{ matrix.sketch }}.ino - sketch-names-find-start: ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }} - #sketches-exclude: ${{ matrix.sketches-exclude }} - + fqbn: ${{ matrix.platform }}:${{ matrix.archi }}:${{ matrix.board }} + platforms: | + - name: ${{ matrix.platform }}:${{ matrix.archi }} + source-url: ${{ matrix.platform-url }} + version: ${{ matrix.platform-version }} + libraries: ${{ steps.libs.outputs.yaml }} + sketch-paths: | + - ${{ env.SKETCH_NAMES_FIND_START }}/${{ matrix.unit }}/${{ matrix.sketch }} diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index e4f992a..9f3d4f7 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -11,14 +11,13 @@ on: branches: - '*' paths: - - '*' - '**.ino' - '**.cpp' - '**.hpp' - '**.h' - '**.c' - '**.inl' - - '**clang-format-check.yml' + - '.github/workflows/clang-format-check.yml' - '**.clang-format' pull_request: paths: @@ -28,32 +27,35 @@ on: - '**.h' - '**.c' - '**.inl' - - '**clang-format-check.yml' + - '.github/workflows/clang-format-check.yml' - '**.clang-format' workflow_dispatch: +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: formatting-check: name: Formatting Check - runs-on: [self-hosted, Linux, X64] + runs-on: ubuntu-latest strategy: matrix: path: - - check: 'src' # path to include - exclude: '' # path to exclude - - check: 'test' # path to include - exclude: '' # path to exclude - - check: 'examples' # path to include - exclude: '' # path to exclude + - check: 'src' + - check: 'test' + - check: 'examples' steps: - name: Checkout uses: actions/checkout@v4 - with: - ref: ${{ github.event.pull_request.head.sha }} - name: Run clang-format style check for C/C++/Protobuf programs. - uses: jidicula/clang-format-action@v4.10.2 # Using include-regex 10.x or later + uses: jidicula/clang-format-action@v4.16.0 with: clang-format-version: '13' check-path: ${{ matrix.path['check'] }} diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml index 91db787..4b1fd40 100644 --- a/.github/workflows/doxygen-gh-pages.yml +++ b/.github/workflows/doxygen-gh-pages.yml @@ -1,8 +1,10 @@ -name: Deploy Doxygen docuemnt on GitHub Pages +name: Deploy Doxygen document on GitHub Pages on: [release, workflow_dispatch] #branches: # - main # - master +permissions: + contents: write defaults: run: shell: bash diff --git a/.github/workflows/platformio-build-check.yml b/.github/workflows/platformio-build-check.yml index 245f8c0..6555341 100644 --- a/.github/workflows/platformio-build-check.yml +++ b/.github/workflows/platformio-build-check.yml @@ -8,30 +8,30 @@ on: branches: - '*' paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**/platformio-build-check.yml' - - '**/platformio.ini' + - '.github/workflows/platformio-build-check.yml' + - 'platformio.ini' pull_request: paths: - - 'src/unit/**.cpp' - - 'src/unit/**.hpp' - - 'src/unit/**.h' - - 'src/unit/**.c' + - 'src/**.cpp' + - 'src/**.hpp' + - 'src/**.h' + - 'src/**.c' - 'examples/UnitUnified/**.ino' - 'examples/UnitUnified/**.cpp' - 'examples/UnitUnified/**.hpp' - 'examples/UnitUnified/**.h' - 'examples/UnitUnified/**.c' - - '**/platformio-build-check.yml' - - '**/platformio.ini' + - '.github/workflows/platformio-build-check.yml' + - 'platformio.ini' workflow_dispatch: defaults: @@ -46,12 +46,12 @@ jobs: build: name: ${{ matrix.unit }}:${{ matrix.example }}@${{ matrix.board }}:${{ matrix.framework }}:${{ matrix.espressif32 }} runs-on: ubuntu-latest - timeout-minutes: 5 + timeout-minutes: 12 strategy: fail-fast: false max-parallel: 20 - + matrix: example: - Output @@ -66,14 +66,18 @@ jobs: - Fire - StampS3 - Dial - - AtomMatrix + - Atom - AtomS3 - AtomS3R - NanoC6 - StickCPlus - StickCPlus2 + - StickS3 - Paper - CoreInk + - Cardputer + - Tab5 + - NessoN1 framework: - Arduino @@ -84,8 +88,14 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 with: - ref: ${{ github.event.pull_request.head.sha }} + python-version: '3.10' + + - name: Install intelhex + run: pip install intelhex - name: Build examples uses: karniv00l/platformio-run-action@v1 diff --git a/.gitignore b/.gitignore index 540f918..cc4236f 100644 --- a/.gitignore +++ b/.gitignore @@ -59,3 +59,15 @@ bin/ obj/ *.code-workspace + +# Doxygen +docs/html/ + +# Temporary +tmp/ + +# Secrets +.env* +*.pem +*.key +*.cert diff --git a/README.md b/README.md index fcf3aee..3796bb1 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ M5UnitUnified is a library for unified handling of various M5 units products. - [M5Utility](https://github.com/m5stack/M5Utility) - [M5HAL](https://github.com/m5stack/M5HAL) +### Examples See also [examples/UnitUnified](examples/UnitUnified) ### Doxygen document diff --git a/docs/doxy.sh b/docs/doxy.sh index 064edd0..d4492c9 100755 --- a/docs/doxy.sh +++ b/docs/doxy.sh @@ -1,6 +1,6 @@ #!/bin/bash -# Please execute on repositry root +# Please execute on repository root ## Get version from library.properties ## Get git rev of HEAD diff --git a/examples/UnitUnified/UnitDDS/Output/main/Output.cpp b/examples/UnitUnified/UnitDDS/Output/main/Output.cpp index 4851582..2eb5bb6 100644 --- a/examples/UnitUnified/UnitDDS/Output/main/Output.cpp +++ b/examples/UnitUnified/UnitDDS/Output/main/Output.cpp @@ -10,6 +10,7 @@ #include #include #include +#include using namespace m5::unit::dds; @@ -26,8 +27,6 @@ const char* mode_str[] = { }; uint8_t mode_index{}; bool cur_bank{}; -// constexpr uint32_t FREQ_BANK_0{10000}; -// constexpr uint32_t FREQ_BANK_1{80000}; constexpr uint32_t FREQ_BANK_0{10}; constexpr uint32_t FREQ_BANK_1{80}; @@ -36,29 +35,58 @@ constexpr uint32_t FREQ_BANK_1{80}; void setup() { M5.begin(); + M5.setTouchButtonHeightByRatio(100); // The screen shall be in landscape mode if (lcd.height() > lcd.width()) { lcd.setRotation(1); } - 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.end(); - Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U); - - if (!Units.add(unit, Wire) || !Units.begin()) { + auto board = M5.getBoard(); + + // NessoN1: Arduino Wire (I2C_NUM_0) cannot be used for GROVE port. + // Wire is used by M5Unified In_I2C for internal devices (IOExpander etc.). + // Wire1 exists but is reserved for HatPort — cannot be used for GROVE. + // Reconfiguring Wire to GROVE pins breaks In_I2C, causing ESP_ERR_INVALID_STATE in M5.update(). + // Solution: Use SoftwareI2C via M5HAL (bit-banging) for the GROVE port. + // NanoC6: Wire.begin() on GROVE pins conflicts with m5::I2C_Class registered by Ex_I2C.setPort() + // on the same I2C_NUM_0, causing sporadic NACK errors. + // Solution: Use M5.Ex_I2C (m5::I2C_Class) directly instead of Arduino Wire. + bool unit_ready{}; + if (board == m5::board_t::board_ArduinoNessoN1) { + // NessoN1: GROVE is on port_b (GPIO 5/4), not port_a (which maps to Wire pins 8/10) + auto pin_num_sda = M5.getPin(m5::pin_name_t::port_b_out); + auto pin_num_scl = M5.getPin(m5::pin_name_t::port_b_in); + M5_LOGI("getPin(M5HAL): SDA:%u SCL:%u", pin_num_sda, pin_num_scl); + m5::hal::bus::I2CBusConfig i2c_cfg; + i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda); + i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl); + auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg); + M5_LOGI("Bus:%d", i2c_bus.has_value()); + unit_ready = Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) && Units.begin(); + } else if (board == m5::board_t::board_M5NanoC6) { + // NanoC6: Use M5.Ex_I2C (m5::I2C_Class, not Arduino Wire) + M5_LOGI("Using M5.Ex_I2C"); + unit_ready = Units.add(unit, M5.Ex_I2C) && Units.begin(); + } else { + 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.end(); + Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U); + unit_ready = Units.add(unit, Wire) && Units.begin(); + } + if (!unit_ready) { M5_LOGE("Failed to begin"); - lcd.clear(TFT_RED); + lcd.fillScreen(TFT_RED); while (true) { m5::utility::delay(10000); } } - M5_LOGI("M5UnitUnified has been begun"); + M5_LOGI("M5UnitUnified initialized"); M5_LOGI("%s", Units.debugInfo().c_str()); - lcd.clear(TFT_DARKGREEN); + lcd.fillScreen(TFT_DARKGREEN); unit.writeFrequencyAndPhase(true, FREQ_BANK_1, true, 180); // Set freq and phase to BANK 1 unit.writeOutput(mode_table[mode_index], cur_bank, FREQ_BANK_0, 0); // Set freq and phase to Bank 0 and use BANK 0 @@ -73,12 +101,11 @@ void setup() void loop() { M5.update(); - auto touch = M5.Touch.getDetail(); Units.update(); // Change mode // To reduce glitches on mode change, enclose it in sleep(true,false) and wakeup() - if (M5.BtnA.wasClicked() || touch.wasClicked()) { + if (M5.BtnA.wasClicked()) { M5.Speaker.tone(3000, 20); unit.sleep(true, false); @@ -96,7 +123,7 @@ void loop() } // Change using bank for freq/phase - if (M5.BtnA.wasHold() || touch.wasHold()) { + if (M5.BtnA.wasHold()) { M5.Speaker.tone(1500, 20); cur_bank = !cur_bank; unit.writeCurrent(cur_bank, cur_bank); diff --git a/library.json b/library.json index 7e2001e..fa7a7bc 100644 --- a/library.json +++ b/library.json @@ -1,20 +1,17 @@ { "name": "M5Unit-DDS", "description": "Library for M5Stack UNIT DDS using M5UnitUnified", - "keywords": [ - "M5Stack", - "DDS Unit" - ], + "keywords": "m5stack, m5unitunified, dds, unit, ad9833, waveform-generator, i2c", "authors": { "name": "M5Stack", - "url": "http://www.m5stack.com" + "url": "https://www.m5stack.com" }, "repository": { "type": "git", "url": "https://github.com/m5stack/M5Unit-DDS.git" }, "dependencies": { - "m5stack/M5UnitUnified": ">=0.2.0" + "m5stack/M5UnitUnified": ">=0.4.4" }, "version": "0.1.0", "frameworks": [ @@ -23,10 +20,7 @@ "platforms": [ "espressif32" ], - "headers": [ - "Unit_DDS.h", - "M5UnitUnifiedDDS.h" - ], + "headers": "Unit_DDS.h,M5UnitUnifiedDDS.h", "license": "MIT", "export": { "exclude": [ diff --git a/platformio.ini b/platformio.ini index 6609ee6..ce54287 100644 --- a/platformio.ini +++ b/platformio.ini @@ -10,9 +10,8 @@ lib_ldf_mode = deep test_framework = googletest test_build_src = true lib_deps=m5stack/M5Unified - m5stack/M5UnitUnified@>=0.2.0 + m5stack/M5UnitUnified@>=0.4.4 -; -------------------------------- [m5base] monitor_speed = 115200 monitor_filters = esp32_exception_decoder, time @@ -22,9 +21,7 @@ test_ignore= native/* [Core] extends = m5base -board = m5stack-grey -;m5stack-core-esp32-16M ;;6.8.0 or later -;m5stack-core-esp32 +board = m5stack-core-esp32-16M lib_deps = ${env.lib_deps} [Core2] @@ -54,18 +51,21 @@ board = m5stack-stamps3 lib_deps = ${env.lib_deps} m5stack/M5Dial -[AtomMatrix] +[Atom] +;include AtomMatrix,AtomLite,AtomU extends = m5base board = m5stack-atom lib_deps = ${env.lib_deps} [AtomS3] +;include AtomEchoS3R,AtomS3Lite,AtomS3U extends = m5base board = m5stack-atoms3 lib_deps = ${env.lib_deps} ; Using ./boards/m5stack-atoms3r.json [AtomS3R] +;include AtomEchoS3R extends = m5base board = m5stack-atoms3r lib_deps = ${env.lib_deps} @@ -74,12 +74,8 @@ lib_deps = ${env.lib_deps} [NanoC6] extends = m5base board = m5stack-nanoc6 -platform = https://github.com/platformio/platform-espressif32.git -platform_packages = - platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.7 - platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 +lib_deps = ${env.lib_deps} board_build.partitions = default.csv -lib_deps = ${env.lib_deps} [StickCPlus] extends = m5base @@ -92,6 +88,19 @@ extends = m5base board = m5stick-cplus2 lib_deps = ${env.lib_deps} +[StickS3] +extends = m5base +board = esp32-s3-devkitc-1 +board_build.arduino.partitions = default_8MB.csv +board_build.arduino.memory_type = qio_opi +build_flags = + -DESP32S3 + -DBOARD_HAS_PSRAM + -mfix-esp32-psram-cache-issue + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 +lib_deps = ${env.lib_deps} + [Paper] extends = m5base board = m5stack-fire @@ -102,8 +111,33 @@ extends = m5base board = m5stack-coreink lib_deps = ${env.lib_deps} +[Cardputer] +extends = m5base +board = esp32-s3-devkitc-1 +build_flags = + -DESP32S3 + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 +lib_deps = ${env.lib_deps} + +[Tab5] +extends = m5base +board = esp32-p4-evboard +board_build.mcu = esp32p4 +board_build.flash_mode = qio +build_flags = + -DBOARD_HAS_PSRAM + -DARDUINO_USB_CDC_ON_BOOT=1 + -DARDUINO_USB_MODE=1 +lib_deps = ${env.lib_deps} + +[NessoN1] +extends = m5base +board = arduino_nesso_n1 +lib_deps = ${env.lib_deps} + [sdl] -build_flags = -O3 -xc++ -std=c++14 -lSDL2 +build_flags = -O3 -xc++ -std=c++14 -lSDL2 -arch arm64 ; for arm mac -I"/usr/local/include/SDL2" ; for intel mac homebrew SDL2 -L"/usr/local/lib" ; for intel mac homebrew SDL2 @@ -112,45 +146,31 @@ build_flags = -O3 -xc++ -std=c++14 -lSDL2 platform = native test_filter= native/* test_ignore= embedded/* -lib_deps = ${env.lib_deps} +lib_deps = ${env.lib_deps} ; -------------------------------- ;Choose framework [arduino_latest] -;platform = espressif32 @ 6.8.1 -platform = espressif32 @ 6.10.0 -framework = arduino - -[arduino_69] -platform = espressif32@6.10.0 +platform = espressif32 @ 6.12.0 framework = arduino -[arduino_6_6_0] -platform = espressif32 @ 6.6.0 -framework = arduino - -[arduino_6_0_1] -platform = espressif32 @ 6.0.1 -framework = arduino - -[arduino_5_4_0] -platform = espressif32 @ 5.4.0 +[nanoc6_latest] +platform = espressif32 @ 6.12.0 +platform_packages = + platformio/framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.7 + platformio/framework-arduinoespressif32-libs @ https://github.com/espressif/esp32-arduino-libs.git#idf-release/v5.1 framework = arduino -[arduino_4_4_0] -platform = espressif32 @ 4.4.0 +[pioarduino_latest] +platform = https://github.com/pioarduino/platform-espressif32/releases/download/55.03.36/platform-espressif32.zip framework = arduino -;[esp-idf] -;platform = espressif32 @ 6.8.1 -;framework = espidf - ; -------------------------------- ;Choose build options [option_release] build_type=release -build_flags = ${env.build_flags} +build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=3 -DLOG_LOCAL_LEVEL=3 -DAPP_LOG_LEVEL=3 @@ -158,14 +178,14 @@ build_flags = ${env.build_flags} [option_log] build_type=release -build_flags = ${env.build_flags} +build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=5 -DLOG_LOCAL_LEVEL=5 -DAPP_LOG_LEVEL=5 [option_debug] build_type=debug -build_flags = ${env.build_flags} +build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=5 -DLOG_LOCAL_LEVEL=5 -DAPP_LOG_LEVEL=5 @@ -173,14 +193,14 @@ build_flags = ${env.build_flags} [option_map] build_type=release -build_flags = ${env.build_flags} +build_flags = ${env.build_flags} -DCORE_DEBUG_LEVEL=3 -DLOG_LOCAL_LEVEL=3 -DAPP_LOG_LEVEL=3 -DM5_LOG_LEVEL=0 -Wl,-Map,output.map -; Require at leaset C++14 after 1.13.0 +; Require at least C++14 after 1.13.0 [test_fw] lib_deps = google/googletest@1.12.1 @@ -190,86 +210,116 @@ lib_deps = google/googletest@1.12.1 ; DDS [env:test_DDS_Core] extends=Core, option_release, arduino_latest -lib_deps = ${Core.lib_deps} +lib_deps = ${Core.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_Core2] extends=Core2, option_release, arduino_latest -lib_deps = ${Core2.lib_deps} +lib_deps = ${Core2.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_CoreS3] extends=CoreS3, option_release, arduino_latest -lib_deps = ${CoreS3.lib_deps} +lib_deps = ${CoreS3.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_Fire] extends=Fire, option_release, arduino_latest -lib_deps = ${Fire.lib_deps} +lib_deps = ${Fire.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_StampS3] extends=StampS3, option_release, arduino_latest -lib_deps = ${StampS3.lib_deps} +lib_deps = ${StampS3.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_Dial] extends=Dial, option_release, arduino_latest -lib_deps = ${Dial.lib_deps} +lib_deps = ${Dial.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds -[env:test_DDS_AtomMatrix] -extends=AtomMatrix, option_release, arduino_latest -lib_deps = ${AtomMatrix.lib_deps} +[env:test_DDS_Atom] +extends=Atom, option_release, arduino_latest +lib_deps = ${Atom.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_AtomS3] extends=AtomS3, option_release, arduino_latest -lib_deps = ${AtomS3.lib_deps} +lib_deps = ${AtomS3.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_AtomS3R] extends=AtomS3R, option_release, arduino_latest -lib_deps = ${AtomS3R.lib_deps} +lib_deps = ${AtomS3R.lib_deps} ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_NanoC6] -extends=NanoC6, option_release, arduino_latest +extends=NanoC6, option_release, nanoc6_latest lib_deps = ${NanoC6.lib_deps} - ${test_fw.lib_deps} + ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_StickCPlus] extends=StickCPlus, option_release, arduino_latest -lib_deps = ${StickCPlus.lib_deps} - ${test_fw.lib_deps} +lib_deps = ${StickCPlus.lib_deps} + ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_StickCPlus2] extends=StickCPlus2, option_release, arduino_latest -lib_deps = ${StickCPlus2.lib_deps} - ${test_fw.lib_deps} +lib_deps = ${StickCPlus2.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_dds + +[env:test_DDS_StickS3] +extends=StickS3, option_release, arduino_latest +build_flags = ${StickS3.build_flags} + ${option_release.build_flags} +lib_deps = ${StickS3.lib_deps} + ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_Paper] extends=Paper, option_release, arduino_latest -lib_deps = ${Paper.lib_deps} - ${test_fw.lib_deps} +lib_deps = ${Paper.lib_deps} + ${test_fw.lib_deps} test_filter= embedded/test_dds [env:test_DDS_CoreInk] extends=CoreInk, option_release, arduino_latest -lib_deps = ${CoreInk.lib_deps} - ${test_fw.lib_deps} +lib_deps = ${CoreInk.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_dds + +[env:test_DDS_Cardputer] +extends=Cardputer, option_release, arduino_latest +build_flags = ${Cardputer.build_flags} + ${option_release.build_flags} +lib_deps = ${Cardputer.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_dds + +[env:test_DDS_Tab5] +extends=Tab5, option_release, pioarduino_latest +build_flags = ${Tab5.build_flags} + ${option_release.build_flags} +lib_deps = ${Tab5.lib_deps} + ${test_fw.lib_deps} +test_filter= embedded/test_dds + +[env:test_DDS_NessoN1] +extends=NessoN1, option_release, pioarduino_latest +lib_deps = ${NessoN1.lib_deps} + ${test_fw.lib_deps} test_filter= embedded/test_dds ; -------------------------------- @@ -292,8 +342,8 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Outp extends=StampS3, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> -[env:UnitDDS_Output_AtomMatrix_Arduino_latest] -extends=AtomMatrix, option_release, arduino_latest +[env:UnitDDS_Output_Atom_Arduino_latest] +extends=Atom, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> [env:UnitDDS_Output_AtomS3_Arduino_latest] @@ -309,7 +359,7 @@ extends=Dial, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> [env:UnitDDS_Output_NanoC6_Arduino_latest] -extends=NanoC6, option_release, arduino_latest +extends=NanoC6, option_release, nanoc6_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> [env:UnitDDS_Output_StickCPlus_Arduino_latest] @@ -320,6 +370,12 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Outp extends=StickCPlus2, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> +[env:UnitDDS_Output_StickS3_Arduino_latest] +extends=StickS3, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> +build_flags = ${StickS3.build_flags} + ${option_release.build_flags} + [env:UnitDDS_Output_Paper_Arduino_latest] extends=Paper, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> @@ -331,3 +387,19 @@ build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Outp [env:UnitDDS_Output_Fire_Arduino_latest] extends=Fire, option_release, arduino_latest build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> + +[env:UnitDDS_Output_Cardputer_Arduino_latest] +extends=Cardputer, option_release, arduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> +build_flags = ${Cardputer.build_flags} + ${option_release.build_flags} + +[env:UnitDDS_Output_Tab5_Arduino_latest] +extends=Tab5, option_release, pioarduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> +build_flags = ${Tab5.build_flags} + ${option_release.build_flags} + +[env:UnitDDS_Output_NessoN1_Arduino_latest] +extends=NessoN1, option_release, pioarduino_latest +build_src_filter = +<*> -<.git/> -<.svn/> +<../examples/UnitUnified/UnitDDS/Output> diff --git a/src/M5UnitUnifiedDDS.h b/src/M5UnitUnifiedDDS.h index cf5bddd..846b233 100644 --- a/src/M5UnitUnifiedDDS.h +++ b/src/M5UnitUnifiedDDS.h @@ -5,7 +5,7 @@ */ /*! @file M5UnitUnifiedDDS.h - @brief Main header of M5Unit-DDS using M5UnitUnfied + @brief Main header of M5Unit-DDS using M5UnitUnified */ #ifndef M5_UNIT_UNIFIED_DDS_H #define M5_UNIT_UNIFIED_DDS_H diff --git a/src/unit/unit_DDS.cpp b/src/unit/unit_DDS.cpp index 269af5c..70308d2 100644 --- a/src/unit/unit_DDS.cpp +++ b/src/unit/unit_DDS.cpp @@ -19,19 +19,21 @@ using namespace m5::unit::dds::command; namespace { constexpr char DESC[] = "ad9833"; -constexpr double MCLK{10000000.f}; +constexpr float MCLK{10000000.f}; constexpr uint32_t MINIMUM_FREQ{0}; constexpr uint32_t MAXIMUM_FREQ{1000000}; // Calculate 28-bit FTW from out[Hz] to be output uint32_t calculate_ftw(const uint32_t out_hz) { - constexpr double scale = static_cast(1ULL << 28); - uint32_t ftw = static_cast(llround(static_cast(out_hz) * scale / MCLK)); + constexpr float scale = static_cast(1ULL << 28); + uint32_t ftw = static_cast(lroundf(static_cast(out_hz) * scale / MCLK)); return ftw & 0x0FFFFFFF; } // Calculate 11-bit PHASE from phase[deg] +// Note (independent testing): The STM32 register accepts 12-bit (bit 11:0), but only +// 11-bit (2048 steps) is effective for AD9833 output. Verified on hardware 2026-04-02. uint16_t calculate_phase(const uint16_t deg) { uint16_t d = deg % 360; @@ -41,8 +43,7 @@ uint16_t calculate_phase(const uint16_t deg) inline bool is_valid_frequency(const uint32_t freq) { - // return std::isfinite(freq) && freq >= MINIMUM_FREQ && freq <= MAXIMUM_FREQ; - return std::isfinite(freq) && freq <= MAXIMUM_FREQ; + return freq <= MAXIMUM_FREQ; } } // namespace @@ -105,7 +106,7 @@ bool UnitDDS::writeMode(const Mode mode) v = (v & ~0x07) | m5::stl::to_underlying(mode); // *** From Firmware Implementation *** // Ctrl must also be re-written to reflect the mode change - // When SAWTOOH/DC mode is selected, the internal ferq is set to 0, so it is set back. + // When SAWTOOTH/DC mode is selected, the internal freq is set to 0, so it is set back. return write_register8(MODE_REG, v) && write_register8(CONTROL_REG, ctrl) && (write_freq ? (writeFrequency0(_freq[0]) && writeFrequency1(_freq[1])) : true); } @@ -153,14 +154,12 @@ bool UnitDDS::writeFrequencyAndPhase(const bool select_freq, const uint32_t freq uint32_t ftw = calculate_ftw(freq); uint16_t ph = calculate_phase(deg); uint8_t buf[6]{}; - buf[0] = ((ftw >> 24) & 0x0F) | (select_freq ? 0xC0 : 0x80); - buf[1] = (ftw >> 16) & 0xFF; - buf[2] = (ftw >> 8) & 0xFF; - buf[3] = ftw & 0xFF; - buf[4] = ((ph >> 8) & 0x07) | (select_phase ? 0xC0 : 0x80); - buf[5] = ph & 0xFF; - // M5_LIB_LOGE("%02X:%02X:%02X:%02X:%02X:%02X", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - + buf[0] = ((ftw >> 24) & 0x0F) | (select_freq ? 0xC0 : 0x80); + buf[1] = (ftw >> 16) & 0xFF; + buf[2] = (ftw >> 8) & 0xFF; + buf[3] = ftw & 0xFF; + buf[4] = ((ph >> 8) & 0x07) | (select_phase ? 0xC0 : 0x80); + buf[5] = ph & 0xFF; _freq[(int)select_freq] = 0; if (writeRegister(FREQUENCY_REG, buf, m5::stl::size(buf))) { _freq[(int)select_freq] = freq; diff --git a/src/unit/unit_DDS.hpp b/src/unit/unit_DDS.hpp index 0ae833d..7f5cd7c 100644 --- a/src/unit/unit_DDS.hpp +++ b/src/unit/unit_DDS.hpp @@ -57,6 +57,8 @@ class UnitDDS : public Component { uint16_t deg{0}; //!< Phase if start output on begin }; + //! @brief Constructor + //! @param addr I2C address explicit UnitDDS(const uint8_t addr = DEFAULT_ADDRESS) : Component(addr) { auto ccfg = component_config(); @@ -67,16 +69,18 @@ class UnitDDS : public Component { { } + //! @brief Begin unit + //! @return True if successful virtual bool begin() override; ///@name Settings for begin ///@{ - /*! @brief Gets the configration */ + /*! @brief Gets the configuration */ inline config_t config() { return _cfg; } - //! @brief Set the configration + //! @brief Set the configuration inline void config(const config_t& cfg) { _cfg = cfg; @@ -86,12 +90,12 @@ class UnitDDS : public Component { ///@name Properties ///@{ //! @brief Gets written frequency 0 (Hz) - uint16_t frequency0() const + uint32_t frequency0() const { return _freq[0]; } //! @brief Gets written frequency 1 (Hz) - uint16_t frequency1() const + uint32_t frequency1() const { return _freq[1]; } @@ -176,7 +180,7 @@ class UnitDDS : public Component { @brief Write the frequency and phase @param select_freq Frequency target bank 0 if false, bank 1 if true @param freq Frequency(Hz) 0 - 1Mhz - @param select_freq Phase target bank 0 if false, bank 1 if true + @param select_phase Phase target bank 0 if false, bank 1 if true @param deg Phase (degree) @return True if successful @warning Frequency and phase settings are ignored for Mode::Sawtooth and Mode::DC @@ -185,22 +189,22 @@ class UnitDDS : public Component { const uint16_t deg); /*! @brief Write which bank setting to use - @param select_freq Frequecny using bank 0 if false, bank 1 if true - @param select_freq Phase using bank 0 if false, bank 1 if true + @param select_freq Frequency using bank 0 if false, bank 1 if true + @param select_phase Phase using bank 0 if false, bank 1 if true @return True if successful @warning Frequency and phase settings are ignored for Mode::Sawtooth and Mode::DC */ bool writeCurrent(const bool select_freq, const bool select_phase); /*! @brief Write which bank frequency setting to use - @param select_freq Frequecny using bank 0 if false, bank 1 if true + @param select_freq Frequency using bank 0 if false, bank 1 if true @return True if successful @warning Frequency and phase settings are ignored for Mode::Sawtooth and Mode::DC */ bool writeCurrentFrequency(const bool select); /*! @brief Write which bank phase setting to use - @param select_freq Phase using bank 0 if false, bank 1 if true + @param select Phase using bank 0 if false, bank 1 if true @return True if successful @warning Frequency and phase settings are ignored for Mode::Sawtooth and Mode::DC */ @@ -222,7 +226,7 @@ class UnitDDS : public Component { /*! @brief Sleep @param mclk Sleep mclk (Keep output current value) - @param DAC Sleep DAC (Stop output) + @param DAC Sleep DAC (Stop DAC clock output) @return True if successful */ bool sleep(const bool mclk = true, const bool DAC = true); @@ -255,7 +259,7 @@ class UnitDDS : public Component { private: config_t _cfg{}; dds::Mode _mode{}; - uint16_t _freq[2]{}; + uint32_t _freq[2]{}; }; namespace dds { diff --git a/test/embedded/test_dds/dds_test.cpp b/test/embedded/test_dds/dds_test.cpp index ad86ce0..96d69e5 100644 --- a/test/embedded/test_dds/dds_test.cpp +++ b/test/embedded/test_dds/dds_test.cpp @@ -13,10 +13,9 @@ #include #include #include +#include #include -#include #include -#include #include using namespace m5::unit::googletest; @@ -25,28 +24,17 @@ using namespace m5::unit::dds; using namespace m5::unit::dds::command; using m5::unit::types::elapsed_time_t; -const ::testing::Environment* global_fixture = ::testing::AddGlobalTestEnvironment(new GlobalFixture<400000U>()); - -class TestDDS : public ComponentTestBase { +class TestDDS : public I2CComponentTestBase { protected: virtual UnitDDS* get_instance() override { auto ptr = new m5::unit::UnitDDS(); return ptr; } - virtual bool is_using_hal() const override - { - return GetParam(); - }; }; -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestDDS, ::testing::Values(false, true)); -// INSTANTIATE_TEST_SUITE_P(ParamValues, TestDDS, ::testing::Values(true)); -INSTANTIATE_TEST_SUITE_P(ParamValues, TestDDS, ::testing::Values(false)); - namespace { -auto rng = std::default_random_engine{}; constexpr uint32_t MINIMUM_FREQ{0}; constexpr uint32_t MAXIMUM_FREQ{1000000}; @@ -72,7 +60,7 @@ uint8_t read_control(UnitDDS* u) } // namespace -TEST_P(TestDDS, Basic) +TEST_F(TestDDS, Basic) { SCOPED_TRACE(ustr); @@ -81,7 +69,7 @@ TEST_P(TestDDS, Basic) EXPECT_TRUE(strcmp(desc, "ad9833") == 0) << desc; } -TEST_P(TestDDS, Mode) +TEST_F(TestDDS, Mode) { SCOPED_TRACE(ustr); @@ -93,7 +81,7 @@ TEST_P(TestDDS, Mode) } } -TEST_P(TestDDS, Settings) +TEST_F(TestDDS, Settings) { SCOPED_TRACE(ustr); @@ -167,7 +155,7 @@ TEST_P(TestDDS, Settings) } } -TEST_P(TestDDS, Output) +TEST_F(TestDDS, Output) { SCOPED_TRACE(ustr); @@ -196,7 +184,127 @@ TEST_P(TestDDS, Output) } } -TEST_P(TestDDS, Sleep) +TEST_F(TestDDS, FrequencyCache) +{ + SCOPED_TRACE(ustr); + + // Verify frequency0()/frequency1() cache after writeFrequency + for (auto&& f : valid_freq_table) { + auto s = m5::utility::formatString("freq:%u", f); + SCOPED_TRACE(s); + EXPECT_TRUE(unit->writeFrequency(false, f)); + EXPECT_EQ(unit->frequency0(), f); + EXPECT_TRUE(unit->writeFrequency(true, f)); + EXPECT_EQ(unit->frequency1(), f); + } + + // Verify cache after writeFrequencyAndPhase + EXPECT_TRUE(unit->writeFrequencyAndPhase(false, 123456, true, 90)); + EXPECT_EQ(unit->frequency0(), 123456U); + EXPECT_TRUE(unit->writeFrequencyAndPhase(true, 999999, false, 45)); + EXPECT_EQ(unit->frequency1(), 999999U); + + // Verify cache is not updated on failure + uint32_t prev = unit->frequency0(); + EXPECT_FALSE(unit->writeFrequency(false, MAXIMUM_FREQ + 1)); + EXPECT_EQ(unit->frequency0(), prev); +} + +TEST_F(TestDDS, BeginConfig) +{ + SCOPED_TRACE(ustr); + + // Verify begin() with start_output=false does not write output + auto cfg = unit->config(); + cfg.start_output = false; + unit->config(cfg); + + // Re-begin should succeed without writing output + EXPECT_TRUE(unit->begin()); + + // Verify begin() with custom config + cfg.start_output = true; + cfg.mode = Mode::Triangle; + cfg.select = true; + cfg.freq = 500000; + cfg.deg = 180; + unit->config(cfg); + EXPECT_TRUE(unit->begin()); + + Mode m{}; + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Triangle); + + // select=true so frequency is written to bank1 + EXPECT_EQ(unit->frequency1(), 500000U); +} + +TEST_F(TestDDS, ModeTransitionFreqRestore) +{ + SCOPED_TRACE(ustr); + + // Set known frequencies + constexpr uint32_t freq0 = 100000; + constexpr uint32_t freq1 = 200000; + EXPECT_TRUE(unit->writeFrequency(false, freq0)); + EXPECT_TRUE(unit->writeFrequency(true, freq1)); + + // Switch to Sawtooth (firmware resets internal freq to 0) + EXPECT_TRUE(unit->writeMode(Mode::Sawtooth)); + Mode m{}; + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Sawtooth); + + // Switch back to Sin — should restore frequencies + EXPECT_TRUE(unit->writeMode(Mode::Sin)); + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Sin); + + // Cache should still hold the original frequencies + EXPECT_EQ(unit->frequency0(), freq0); + EXPECT_EQ(unit->frequency1(), freq1); + + // Same test with DC mode + EXPECT_TRUE(unit->writeMode(Mode::DC)); + EXPECT_TRUE(unit->writeMode(Mode::Triangle)); + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Triangle); + EXPECT_EQ(unit->frequency0(), freq0); + EXPECT_EQ(unit->frequency1(), freq1); +} + +TEST_F(TestDDS, RandomFrequency) +{ + SCOPED_TRACE(ustr); + + EXPECT_TRUE(unit->writeMode(Mode::Sin)); + + for (int i = 0; i < 10; ++i) { + uint32_t f = esp_random() % (MAXIMUM_FREQ + 1); + auto s = m5::utility::formatString("random freq:%u", f); + SCOPED_TRACE(s); + EXPECT_TRUE(unit->writeFrequency(false, f)); + EXPECT_EQ(unit->frequency0(), f); + } +} + +TEST_F(TestDDS, ModeReserved) +{ + SCOPED_TRACE(ustr); + + // Mode::Reserved (value 0) — firmware behavior is undefined but should not crash + EXPECT_TRUE(unit->writeMode(Mode::Reserved)); + Mode m{}; + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Reserved); + + // Should be able to switch back to a normal mode + EXPECT_TRUE(unit->writeMode(Mode::Sin)); + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Sin); +} + +TEST_F(TestDDS, Sleep) { SCOPED_TRACE(ustr); @@ -222,3 +330,28 @@ TEST_P(TestDDS, Sleep) c = read_control(unit.get()); EXPECT_EQ(0, c & 0x1C); } + +TEST_F(TestDDS, SleepWriteSettings) +{ + SCOPED_TRACE(ustr); + + // Write initial state + EXPECT_TRUE(unit->writeMode(Mode::Sin)); + EXPECT_TRUE(unit->writeFrequency(false, 1000)); + + // Enter MCLK sleep + EXPECT_TRUE(unit->sleep(true, false)); + + // Write new frequency while sleeping — AD9833 accepts register writes during SLEEP1 + EXPECT_TRUE(unit->writeFrequency(false, 500000)); + EXPECT_EQ(unit->frequency0(), 500000U); + + // Write phase while sleeping + EXPECT_TRUE(unit->writePhase(false, 90)); + + // Wakeup and verify mode is still correct + EXPECT_TRUE(unit->wakeup()); + Mode m{}; + EXPECT_TRUE(unit->readMode(m)); + EXPECT_EQ(m, Mode::Sin); +}