diff --git a/.aiderignore b/.aiderignore new file mode 100644 index 0000000000..b80c459446 --- /dev/null +++ b/.aiderignore @@ -0,0 +1,21 @@ +# Build artifacts +buildroot/ +*.o +*.a +*.so +*.dylib +*.dll +*.exe + +# Web assets +*.min.js +*.min.css + +# Generated files +__pycache__/ +*.pyc +.DS_Store + +# IDE files +.vscode/ +.idea/ diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 54627d462b..77479946b5 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -38,7 +38,7 @@ "platformio.platformio-ide", "marlinfirmware.auto-build", "editorconfig.editorconfig" - ], + ] // Use 'forwardPorts' to make a list of ports inside the container available locally. // "forwardPorts": [], diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 1fe68966fb..a22a1bb82e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -7,7 +7,7 @@ contact_links: url: https://www.facebook.com/groups/1049718498464482 about: Please ask and answer questions here. - name: 🕹 Marlin on Discord - url: https://discord.gg/n5NJ59y + url: https://discord.com/servers/marlin-firmware-461605380783472640 about: Join the Discord server for support and discussion. - name: 🔗 Marlin Discussion Forum url: https://reprap.org/forum/list.php?415 diff --git a/.github/contributing.md b/.github/contributing.md index c9b31998e9..9455977528 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -43,7 +43,7 @@ We have a Message Board and a Facebook group where our knowledgable user communi If chat is more your speed, you can join the MarlinFirmware Discord server: -* Use the link https://discord.gg/n5NJ59y to join up as a General User. +* Use the link https://discord.com/servers/marlin-firmware-461605380783472640 to join up as a General User. * Even though our Discord is pretty active, it may take a while for community members to respond — please be patient! * Use the `#general` channel for general questions or discussion about Marlin. * Other channels exist for certain topics or are limited to Patrons. Check the channel list. diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml index c69e6c4fad..3cbf110aa0 100644 --- a/.github/workflows/auto-label.yml +++ b/.github/workflows/auto-label.yml @@ -14,7 +14,7 @@ jobs: autolabel: name: Auto Label if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - name: Auto Label for [BUG] uses: actions/github-script@v7 diff --git a/.github/workflows/bump-date.yml b/.github/workflows/bump-date.yml index 2d3fd1f33d..cb27ef199e 100644 --- a/.github/workflows/bump-date.yml +++ b/.github/workflows/bump-date.yml @@ -14,7 +14,7 @@ jobs: name: Bump Distribution Date if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml index 2b15067f51..a28b7fdd62 100644 --- a/.github/workflows/check-pr.yml +++ b/.github/workflows/check-pr.yml @@ -18,7 +18,7 @@ jobs: name: PR Bad Target if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: superbrothers/close-pull-request@v3 diff --git a/.github/workflows/test-builds.yml b/.github/workflows/ci-build-tests.yml similarity index 75% rename from .github/workflows/test-builds.yml rename to .github/workflows/ci-build-tests.yml index a3cf32739c..5d53536a27 100644 --- a/.github/workflows/test-builds.yml +++ b/.github/workflows/ci-build-tests.yml @@ -1,9 +1,9 @@ # -# test-builds.yml +# ci-build-tests.yml # Do test builds to catch compile errors # -name: CI +name: CI - Build Tests on: pull_request: @@ -14,30 +14,43 @@ on: - config/** - data/** - docs/** + - test/** + - Marlin/tests/** - '**/*.md' push: branches: - bugfix-2.1.x - 2.1.x + - release-* paths-ignore: - config/** - data/** - docs/** + - test/** + - Marlin/tests/** - '**/*.md' jobs: test_builds: - name: Run All Tests + name: Build Test if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 + + env: + CONFIG_BRANCH: ${{ github.base_ref || github.ref_name }} strategy: + fail-fast: true matrix: test-platform: + # RP2040 + - SKR_Pico + # Native - linux_native + - simulator_linux_release # AVR - mega2560 @@ -45,7 +58,7 @@ jobs: - at90usb1286_dfu # AVR Extended - - FYSETC_F6 + - mega2560ext - melzi_optiboot - rambo - sanguino1284p @@ -100,11 +113,11 @@ jobs: # STM32F4 - ARMED - - BIGTREE_BTT002 - - BIGTREE_GTR_V1_0 - - BIGTREE_SKR_PRO + - BTT_BTT002 + - BTT_GTR_V1_0 + - BTT_SKR_PRO - FLYF407ZG - - FYSETC_S6 + - STM32F446VE_fysetc - LERDGEK - LERDGEX - mks_robin_pro2 @@ -142,6 +155,12 @@ jobs: # HC32 - HC32F460C_aquila_101 + # GD32F3 + - GD32F303RE_creality_mfl + + # GD32F1 + - GD32F103RC_aquila_mfl + # LPC176x - Lengthy tests - LPC1768 - LPC1769 @@ -155,15 +174,20 @@ jobs: uses: actions/cache@v4 with: path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + key: ${{ runner.os }}-pip-build-v1 restore-keys: | - ${{ runner.os }}-pip- + ${{ runner.os }}-pip-build- - name: Cache PlatformIO uses: actions/cache@v4 with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + path: | + ~/.platformio + .pio/build + .pio/libdeps + key: ${{ runner.os }}-pio-build-v1 + restore-keys: | + ${{ runner.os }}-pio-build- - name: Select Python 3.9 uses: actions/setup-python@v5 @@ -177,6 +201,14 @@ jobs: pio upgrade --dev pio pkg update --global + - name: Install Simulator dependencies + run: | + sudo apt-get update + sudo apt-get install build-essential + sudo apt-get install libsdl2-dev + sudo apt-get install libsdl2-net-dev + sudo apt-get install libglm-dev + - name: Run ${{ matrix.test-platform }} Tests run: | make tests-single-ci TEST_TARGET=${{ matrix.test-platform }} diff --git a/.github/workflows/ci-unit-tests.yml b/.github/workflows/ci-unit-tests.yml new file mode 100644 index 0000000000..30af812dff --- /dev/null +++ b/.github/workflows/ci-unit-tests.yml @@ -0,0 +1,78 @@ +# +# ci-unit-tests.yml +# Build and execute unit tests to catch functional issues in code +# + +name: CI - Unit Tests + +on: + pull_request: + branches: + - bugfix-2.1.x + # Cannot be enabled on 2.1.x until it contains the unit test framework + #- 2.1.x + paths-ignore: + - config/** + - data/** + - docs/** + - '**/*.md' + push: + branches: + - bugfix-2.1.x + # Cannot be enabled on 2.1.x until it contains the unit test framework + #- 2.1.x + paths-ignore: + - config/** + - data/** + - docs/** + - '**/*.md' + +jobs: + # This runs all unit tests as a single job. While it should be possible to break this up into + # multiple jobs, they currently run quickly and finish long before the compilation tests. + run_unit_tests: + name: Unit Test + # These tests will only be able to run on the bugfix-2.1.x branch, until the next release + # pulls them into additional branches. + if: github.repository == 'MarlinFirmware/Marlin' + + runs-on: ubuntu-22.04 + + steps: + - name: Check out the PR + uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-unit-v1 + restore-keys: | + ${{ runner.os }}-pip-unit- + + - name: Cache PlatformIO + uses: actions/cache@v4 + with: + path: | + ~/.platformio + .pio/build + .pio/libdeps + key: ${{ runner.os }}-pio-tests-v1 + restore-keys: | + ${{ runner.os }}-pio-tests- + + - name: Select Python 3.9 + uses: actions/setup-python@v5 + with: + python-version: '3.9' + architecture: 'x64' + + - name: Install PlatformIO + run: | + pip install -U platformio + pio upgrade --dev + pio pkg update --global + + - name: Run All Unit Tests + run: | + make unit-test-all-local diff --git a/.github/workflows/ci-validate-boards.yml b/.github/workflows/ci-validate-boards.yml new file mode 100644 index 0000000000..aa6d284f06 --- /dev/null +++ b/.github/workflows/ci-validate-boards.yml @@ -0,0 +1,48 @@ +# +# ci-validate-boards.yml +# Validate boards.h to make sure it's all set up correctly +# + +name: CI - Validate boards.h + +# We can do the on: section as two items, one for pull requests and one for pushes... +on: + pull_request: + branches: + - bugfix-2.1.x + paths: + - "Marlin/src/core/boards.h" + push: + branches: + - bugfix-2.1.x + paths: + - "Marlin/src/core/boards.h" + +jobs: + validate_pins_files: + name: Validate boards.h + if: github.repository == 'MarlinFirmware/Marlin' + + runs-on: ubuntu-22.04 + + steps: + - name: Check out the PR + uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-validation-v1 + restore-keys: | + ${{ runner.os }}-pip-validation- + + - name: Select Python 3.9 + uses: actions/setup-python@v5 + with: + python-version: "3.9" + architecture: "x64" + + - name: Validate core/boards.h + run: | + make validate-boards -j diff --git a/.github/workflows/ci-validate-lines.yml b/.github/workflows/ci-validate-lines.yml new file mode 100644 index 0000000000..367db12e6c --- /dev/null +++ b/.github/workflows/ci-validate-lines.yml @@ -0,0 +1,40 @@ +# +# ci-validate-lines.yml +# Validate that all text files are unchanged by linesformat.py +# + +name: CI - Validate Source Files + +on: + pull_request: + branches: + - bugfix-2.1.x + - 2.1.x + push: + branches: + - bugfix-2.1.x + - 2.1.x + +jobs: + validate_source_files: + name: Validate Source Files + if: github.repository == 'MarlinFirmware/Marlin' + + runs-on: ubuntu-22.04 + + steps: + - name: Check out the PR + uses: actions/checkout@v4 + + - name: Cache node_modules + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-npm-lines-v1 + restore-keys: | + ${{ runner.os }}-npm-lines- + + - name: Validate text file formatting + run: | + npm install --save-dev prettier + make validate-lines -j diff --git a/.github/workflows/ci-validate-pins.yml b/.github/workflows/ci-validate-pins.yml new file mode 100644 index 0000000000..2086fea37a --- /dev/null +++ b/.github/workflows/ci-validate-pins.yml @@ -0,0 +1,51 @@ +# +# ci-validate-pins.yml +# Validate that all of the pins files are unchanged by pinsformat.py +# + +name: CI - Validate Pins Files + +on: + pull_request: + branches: + - bugfix-2.1.x + # Cannot be enabled on 2.1.x until it contains the unit test framework + #- 2.1.x + paths: + - "Marlin/src/pins/*/**" + push: + branches: + - bugfix-2.1.x + # Cannot be enabled on 2.1.x until it contains the unit test framework + #- 2.1.x + paths: + - "Marlin/src/pins/*/**" + +jobs: + validate_pins_files: + name: Validate Pins Files + if: github.repository == 'MarlinFirmware/Marlin' + + runs-on: ubuntu-22.04 + + steps: + - name: Check out the PR + uses: actions/checkout@v4 + + - name: Cache pip + uses: actions/cache@v4 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-validation-v1 + restore-keys: | + ${{ runner.os }}-pip-validation- + + - name: Select Python 3.9 + uses: actions/setup-python@v5 + with: + python-version: "3.9" + architecture: "x64" + + - name: Validate all pins files + run: | + make validate-pins -j diff --git a/.github/workflows/clean-closed.yml b/.github/workflows/clean-closed.yml index 3f370a4362..ad0ee8ea20 100644 --- a/.github/workflows/clean-closed.yml +++ b/.github/workflows/clean-closed.yml @@ -13,7 +13,7 @@ on: jobs: remove_label: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 strategy: matrix: diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml index 55e19de566..4bb488c56f 100644 --- a/.github/workflows/close-stale.yml +++ b/.github/workflows/close-stale.yml @@ -14,7 +14,7 @@ jobs: name: Close Stale Issues if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/stale@v9 diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml index 6392f244b5..1435a3ef44 100644 --- a/.github/workflows/lock-closed.yml +++ b/.github/workflows/lock-closed.yml @@ -14,7 +14,7 @@ jobs: name: Lock Closed Issues if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: dessant/lock-threads@v5 diff --git a/.github/workflows/unlock-reopened.yml b/.github/workflows/unlock-reopened.yml index 614ef3fab2..f88e07cdc3 100644 --- a/.github/workflows/unlock-reopened.yml +++ b/.github/workflows/unlock-reopened.yml @@ -14,7 +14,7 @@ jobs: name: Unlock Reopened if: github.repository == 'MarlinFirmware/Marlin' - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: OSDKDev/unlock-issues@v1.1 diff --git a/.gitignore b/.gitignore old mode 100755 new mode 100644 index c7d47c607f..87d7ef47d3 --- a/.gitignore +++ b/.gitignore @@ -31,6 +31,11 @@ out-language/ *.gen *.sublime-workspace +# npm +node_modules/ +package.json +package-lock.json + # OS applet/ .DS_Store @@ -125,6 +130,7 @@ vc-fileutils.settings # Visual Studio Code .vscode/* !.vscode/extensions.json +*.code-workspace # Simulation files imgui.ini @@ -168,3 +174,6 @@ __pycache__ tags *.logs *.bak +.aider* +!.aiderignore +.env diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..62761b99e9 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,10 @@ +# Prettier Ignore file +*.min.js +web-ui/ +buildroot/share/PlatformIO/boards +buildroot/share/PlatformIO/variants +*.sublime-project +*.sublime-syntax +.github +.vscode +launch.json diff --git a/.zed/settings.json b/.zed/settings.json new file mode 100644 index 0000000000..70bb14920c --- /dev/null +++ b/.zed/settings.json @@ -0,0 +1,16 @@ +/** + * Marlin-specific settings for Zed + * + * For a full list of overridable settings, and general information on folder-specific settings, + * see the documentation: https://zed.dev/docs/configuring-zed#settings-files + */ +{ + "languages": { + "C": { + "enable_language_server": false + }, + "C++": { + "enable_language_server": false + } + } +} diff --git a/Makefile b/Makefile index bc26173aaf..68c522e5b6 100644 --- a/Makefile +++ b/Makefile @@ -2,17 +2,50 @@ SCRIPTS_DIR := buildroot/share/scripts CONTAINER_RT_BIN := docker CONTAINER_RT_OPTS := --rm -v $(PWD):/code -v platformio-cache:/root/.platformio CONTAINER_IMAGE := marlin-dev +UNIT_TEST_CONFIG ?= default + +# Find a Python 3 interpreter +ifeq ($(OS),Windows_NT) + # Windows: use `where` – fall back through the three common names + PYTHON := $(shell which python 2>nul || which python3 2>nul || which py 2>nul) + # Windows: Use cmd tools to find pins files + PINS_RAW := $(shell cmd //c "dir /s /b Marlin\src\pins\*.h 2>nul | findstr /r ".*Marlin\\\\src\\\\pins\\\\.*\\\\pins_.*\.h"") + PINS := $(subst \,/,$(PINS_RAW)) +else + # POSIX: use `command -v` – prefer python3 over python + PYTHON := $(shell command -v python3 2>/dev/null || command -v python 2>/dev/null) + # Unix/Linux: Use find command + PINS := $(shell find Marlin/src/pins -mindepth 2 -name 'pins_*.h') +endif + +# Check that the found interpreter is Python 3 +# Error if there's no Python 3 available +ifneq ($(strip $(PYTHON)),) + PYTHON_VERSION := $(shell $(PYTHON) -c "import sys; print(sys.version_info[0])" 2>/dev/null) + ifneq ($(PYTHON_VERSION),3) + $(error $(PYTHON) is not Python 3 – install a Python‑3.x interpreter or adjust your PATH) + endif +else + $(error No Python executable found – install Python 3.x and make sure it is in your PATH) +endif help: @echo "Tasks for local development:" - @echo "make marlin : Build marlin for the configured board" - @echo "make format-pins : Reformat all pins files" + @echo "make marlin : Build Marlin for the configured board" + @echo "make format-pins -j : Reformat all pins files (-j for parallel execution)" + @echo "make validate-lines -j : Validate line endings, fails on trailing whitespace, etc." + @echo "make validate-pins -j : Validate all pins files, fails if any require reformatting" + @echo "make validate-boards -j : Validate boards.h and pins.h for standards compliance" @echo "make tests-single-ci : Run a single test from inside the CI" @echo "make tests-single-local : Run a single test locally" @echo "make tests-single-local-docker : Run a single test locally, using docker" @echo "make tests-all-local : Run all tests locally" @echo "make tests-all-local-docker : Run all tests locally, using docker" - @echo "make setup-local-docker : Build the local docker image" + @echo "make unit-test-single-local : Run unit tests for a single config locally" + @echo "make unit-test-single-local-docker : Run unit tests for a single config locally, using docker" + @echo "make unit-test-all-local : Run all code tests locally" + @echo "make unit-test-all-local-docker : Run all code tests locally, using docker" + @echo "make setup-local-docker : Setup local docker" @echo "" @echo "Options for testing:" @echo " TEST_TARGET Set when running tests-single-*, to select the" @@ -22,6 +55,9 @@ help: @echo " run on GitHub CI" @echo " ONLY_TEST Limit tests to only those that contain this, or" @echo " the index of the test (1-based)" + @echo " UNIT_TEST_CONFIG Set the name of the config from the test folder, without" + @echo " the leading number. Default is 'default'". Used with the + @echo " unit-test-single-* tasks" @echo " VERBOSE_PLATFORMIO If you want the full PIO output, set any value" @echo " GIT_RESET_HARD Used by CI: reset all local changes. WARNING:" @echo " THIS WILL UNDO ANY CHANGES YOU'VE MADE!" @@ -30,6 +66,9 @@ marlin: ./buildroot/bin/mftest -a .PHONY: marlin +clean: + rm -rf .pio/build* + tests-single-ci: export GIT_RESET_HARD=true $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET) PLATFORMIO_BUILD_FLAGS=-DGITHUB_ACTION @@ -43,23 +82,90 @@ tests-single-local: tests-single-local-docker: @if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET= or use make tests-all-local-docker" ; return 1; fi @if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi - $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)" + $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)" tests-all-local: + @$(PYTHON) -c "import yaml" 2>/dev/null || (echo 'pyyaml module is not installed. Install it with "$(PYTHON) -m pip install pyyaml"' && exit 1) export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \ && export VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) \ - && for TEST_TARGET in $$($(SCRIPTS_DIR)/get_test_targets.py) ; do echo "Running tests for $$TEST_TARGET" ; run_tests . $$TEST_TARGET ; done + && for TEST_TARGET in $$($(PYTHON) $(SCRIPTS_DIR)/get_test_targets.py) ; do \ + if [ "$$TEST_TARGET" = "linux_native" ] && [ "$$(uname)" = "Darwin" ]; then \ + echo "Skipping tests for $$TEST_TARGET on macOS" ; \ + continue ; \ + fi ; \ + echo "Running tests for $$TEST_TARGET" ; \ + run_tests . $$TEST_TARGET || exit 1 ; \ + sleep 5; \ + done tests-all-local-docker: @if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi - $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) $(MAKE) tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) + $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) + +unit-test-single-local: + platformio run -t marlin_$(UNIT_TEST_CONFIG) -e linux_native_test + +unit-test-single-local-docker: + @if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi + $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make unit-test-single-local UNIT_TEST_CONFIG=$(UNIT_TEST_CONFIG) + +unit-test-all-local: + platformio run -t test-marlin -e linux_native_test + +unit-test-all-local-docker: + @if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi + $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make unit-test-all-local + +USERNAME := $(shell whoami) +USER_ID := $(shell id -u) +GROUP_ID := $(shell id -g) + +.PHONY: setup-local-docker setup-local-docker-old setup-local-docker: - $(CONTAINER_RT_BIN) build -t $(CONTAINER_IMAGE) -f docker/Dockerfile . + @echo "Building marlin-dev Docker image..." + $(CONTAINER_RT_BIN) build -t $(CONTAINER_IMAGE) \ + --build-arg USERNAME=$(USERNAME) \ + --build-arg USER_ID=$(USER_ID) \ + --build-arg GROUP_ID=$(GROUP_ID) \ + -f docker/Dockerfile . + @echo + @echo "To run all tests in Docker:" + @echo " make tests-all-local-docker" + @echo "To run a single test in Docker:" + @echo " make tests-single-local-docker TEST_TARGET=mega2560" -PINS := $(shell find Marlin/src/pins -mindepth 2 -name '*.h') +setup-local-docker-old: + $(CONTAINER_RT_BIN) buildx build -t $(CONTAINER_IMAGE) -f docker/Dockerfile . + +.PHONY: $(PINS) format-pins validate-pins $(PINS): %: - @echo "Formatting $@" && node $(SCRIPTS_DIR)/pinsformat.js $@ + @echo "Formatting pins $@" + @$(PYTHON) $(SCRIPTS_DIR)/pinsformat.py $< $@ format-pins: $(PINS) + @echo "Processed $(words $(PINS)) pins files" + +validate-pins: format-pins + @echo "Validating pins files" + @git diff --exit-code || (git status && echo "\nError: Pins files are not formatted correctly. Run \"make format-pins\" to fix.\n" && exit 1) + +.PHONY: format-lines validate-lines + +format-lines: + @echo "Formatting all sources" + @$(PYTHON) $(SCRIPTS_DIR)/linesformat.py buildroot + @$(PYTHON) $(SCRIPTS_DIR)/linesformat.py Marlin + +validate-lines: + @echo "Validating text formatting" + @npx prettier --check . --editorconfig --object-wrap preserve + +BOARDS_FILE := Marlin/src/core/boards.h + +.PHONY: validate-boards + +validate-boards: + @echo "Validating boards.h file" + @$(PYTHON) $(SCRIPTS_DIR)/validate_boards.py $(BOARDS_FILE) || (echo "\nError: boards.h file is not valid. Please check and correct it.\n" && exit 1) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 72ec272b4d..e51d16d565 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -50,7 +50,7 @@ * * Calibration Guides: https://reprap.org/wiki/Calibration * https://reprap.org/wiki/Triffid_Hunter%27s_Calibration_Guide - * https://web.archive.org/web/20220907014303/https://sites.google.com/site/repraplogphase/calibration-of-your-reprap + * https://web.archive.org/web/20220907014303/sites.google.com/site/repraplogphase/calibration-of-your-reprap * https://youtu.be/wAL9d7FgInk * https://teachingtechyt.github.io/calibration.html * @@ -61,7 +61,7 @@ // @section info // Author info of this build printed to the host during boot and M115 -#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Who made the changes. +#define STRING_CONFIG_H_AUTHOR "(none, default config)" // Original author or contributor. //#define CUSTOM_VERSION_FILE Version.h // Path from the root directory (no quotes) // @section machine @@ -71,13 +71,15 @@ #define MOTHERBOARD BOARD_RAMPS_14_EFB #endif +// @section serial + /** * Select the serial port on the board to use for communication with the host. * This allows the connection of wireless adapters (for instance) to non-default port pins. * Serial port -1 is the USB emulated serial port, if available. * Note: The first serial port (-1 or 0) will always be used by the Arduino bootloader. * - * :[-1, 0, 1, 2, 3, 4, 5, 6, 7] + * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] */ #define SERIAL_PORT 0 @@ -99,24 +101,35 @@ /** * Select a secondary serial port on the board to use for communication with the host. * Currently Ethernet (-2) is only supported on Teensy 4.1 boards. - * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7] + * :[-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] */ //#define SERIAL_PORT_2 -1 //#define BAUDRATE_2 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE /** * Select a third serial port on the board to use for communication with the host. - * Currently only supported for AVR, DUE, LPC1768/9 and STM32/STM32F1 - * :[-1, 0, 1, 2, 3, 4, 5, 6, 7] + * Currently supported for AVR, DUE, SAMD51, LPC1768/9, STM32/STM32F1/HC32, and Teensy 4.x + * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] */ //#define SERIAL_PORT_3 1 //#define BAUDRATE_3 250000 // :[2400, 9600, 19200, 38400, 57600, 115200, 250000, 500000, 1000000] Enable to override BAUDRATE +/** + * Select a serial port to communicate with RS485 protocol + * :[-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + */ +//#define RS485_SERIAL_PORT 1 +#ifdef RS485_SERIAL_PORT + //#define M485_PROTOCOL 1 // Check your host for protocol compatibility + //#define RS485_BUS_BUFFER_SIZE 128 +#endif + // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH // Name displayed in the LCD "Ready" message and Info menu //#define CUSTOM_MACHINE_NAME "3D Printer" +//#define CONFIGURABLE_MACHINE_NAME // Add G-code M550 to set/report the machine name // Printer's unique ID, used by some programs to differentiate between machines. // Choose your own or use a service like https://www.uuidgenerator.net/version4 @@ -135,9 +148,9 @@ * Options: A4988, A5984, DRV8825, LV8729, TB6560, TB6600, TMC2100, * TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE, * TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE, - * TMC2660, TMC2660_STANDALONE, TMC5130, TMC5130_STANDALONE, - * TMC5160, TMC5160_STANDALONE - * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE'] + * TMC2240, TMC2660, TMC2660_STANDALONE, + * TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE + * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC2240', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE'] */ #define X_DRIVER_TYPE A4988 #define Y_DRIVER_TYPE A4988 @@ -247,6 +260,7 @@ #define SWITCHING_NOZZLE_SERVO_ANGLES { 0, 90 } // A pair of angles for { E0, E1 }. // For Dual Servo use two pairs: { { lower, raise }, { lower, raise } } #define SWITCHING_NOZZLE_SERVO_DWELL 2500 // Dwell time to wait for servo to make physical move + #define SWITCHING_NOZZLE_LIFT_TO_PROBE // Lift toolheads out of the way while probing #endif // Switch nozzles by bumping the toolhead. Requires EVENT_GCODE_TOOLCHANGE_#. @@ -375,14 +389,15 @@ * PRUSA_MMU1 : Průša MMU1 (The "multiplexer" version) * PRUSA_MMU2 : Průša MMU2 * PRUSA_MMU2S : Průša MMU2S (Requires MK3S extruder with motion sensor, EXTRUDERS = 5) + * PRUSA_MMU3 : Průša MMU3 (Requires MK3S extruder with motion sensor and MMU firmware version 3.x.x, EXTRUDERS = 5) * EXTENDABLE_EMU_MMU2 : MMU with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware) * EXTENDABLE_EMU_MMU2S : MMUS with configurable number of filaments (ERCF, SMuFF or similar with Průša MMU2 compatible firmware) * * Requires NOZZLE_PARK_FEATURE to park print head in case MMU unit fails. * See additional options in Configuration_adv.h. - * :["PRUSA_MMU1", "PRUSA_MMU2", "PRUSA_MMU2S", "EXTENDABLE_EMU_MMU2", "EXTENDABLE_EMU_MMU2S"] + * :["PRUSA_MMU1", "PRUSA_MMU2", "PRUSA_MMU2S", "PRUSA_MMU3", "EXTENDABLE_EMU_MMU2", "EXTENDABLE_EMU_MMU2S"] */ -//#define MMU_MODEL PRUSA_MMU2 +//#define MMU_MODEL PRUSA_MMU3 // @section psu control @@ -573,7 +588,7 @@ #define DUMMY_THERMISTOR_998_VALUE 25 #define DUMMY_THERMISTOR_999_VALUE 100 -// Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1 +// Resistor values when using MAX31865 sensors (-5) on TEMP_SENSOR_0 / 1 / 2 / BED #if TEMP_SENSOR_IS_MAX_TC(0) #define MAX31865_SENSOR_OHMS_0 100 // (Ω) Typically 100 or 1000 (PT100 or PT1000) #define MAX31865_CALIBRATION_OHMS_0 430 // (Ω) Typically 430 for Adafruit PT100; 4300 for Adafruit PT1000 @@ -586,6 +601,10 @@ #define MAX31865_SENSOR_OHMS_2 100 #define MAX31865_CALIBRATION_OHMS_2 430 #endif +#if TEMP_SENSOR_IS_MAX_TC(BED) + #define MAX31865_SENSOR_OHMS_BED 100 + #define MAX31865_CALIBRATION_OHMS_BED 430 +#endif #if HAS_E_TEMP_SENSOR #define TEMP_RESIDENCY_TIME 10 // (seconds) Time to wait for hotend to "settle" in M109 @@ -672,13 +691,15 @@ * MPCTEMP : Predictive Model temperature control. (~1.8K without auto-tune) */ #define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning -//#define MPCTEMP // ** EXPERIMENTAL ** See https://marlinfw.org/docs/features/model_predictive_control.html +//#define MPCTEMP // See https://marlinfw.org/docs/features/model_predictive_control.html #define PID_MAX 255 // Limit hotend current while PID is active (see PID_FUNCTIONAL_RANGE below); 255=full current #define PID_K1 0.95 // Smoothing factor within any PID loop #if ENABLED(PIDTEMP) - //#define PID_DEBUG // Print PID debug data to the serial port. Use 'M303 D' to toggle activation. + //#define MIN_POWER 0 // Min power to improve PID stability (0..PID_MAX). + // Get the power from the temperature report ('M105' => @:nnn) and try P*2-20 to P*2-10. + //#define PID_DEBUG // Print PID debug data to the serial port. Use 'M303 D' to enable/disable. //#define PID_PARAMS_PER_HOTEND // Use separate PID parameters for each extruder (useful for mismatched extruders) // Set/get with G-code: M301 E[extruder number, 0-2] @@ -703,15 +724,23 @@ * Use a physical model of the hotend to control temperature. When configured correctly this gives * better responsiveness and stability than PID and removes the need for PID_EXTRUSION_SCALING * and PID_FAN_SCALING. Enable MPC_AUTOTUNE and use M306 T to autotune the model. - * @section mpctemp + * @section mpc temp */ #if ENABLED(MPCTEMP) #define MPC_AUTOTUNE // Include a method to do MPC auto-tuning (~6.3K bytes of flash) + #if ENABLED(MPC_AUTOTUNE) + //#define MPC_AUTOTUNE_DEBUG // Enable MPC debug logging (~870 bytes of flash) + #endif //#define MPC_EDIT_MENU // Add MPC editing to the "Advanced Settings" menu. (~1.3K bytes of flash) //#define MPC_AUTOTUNE_MENU // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash) #define MPC_MAX 255 // (0..255) Current to nozzle while MPC is active. - #define MPC_HEATER_POWER { 40.0f } // (W) Heat cartridge powers. + #define MPC_HEATER_POWER { 40.0f } // (W) Nominal heat cartridge powers. + //#define MPC_PTC // Hotend power changes with temperature (e.g., PTC heat cartridges). + #if ENABLED(MPC_PTC) + #define MPC_HEATER_ALPHA { 0.0028f } // Temperature coefficient of resistance of the heat cartridges. + #define MPC_HEATER_REFTEMP { 20 } // (°C) Reference temperature for MPC_HEATER_POWER and MPC_HEATER_ALPHA. + #endif #define MPC_INCLUDE_FAN // Model the fan speed? @@ -743,6 +772,7 @@ #define MPC_TUNING_POS { X_CENTER, Y_CENTER, 1.0f } // (mm) M306 Autotuning position, ideally bed center at first layer height. #define MPC_TUNING_END_Z 10.0f // (mm) M306 Autotuning final Z position. + //#define EVENT_GCODE_AFTER_MPC_TUNE "M84" // G-code to execute after MPC tune finished and Z raised. #endif //=========================================================================== @@ -774,13 +804,14 @@ //#define PIDTEMPBED #if ENABLED(PIDTEMPBED) - //#define MIN_BED_POWER 0 - //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port. + //#define MIN_BED_POWER 0 // Min power to improve PID stability (0..MAX_BED_POWER). + // Get the power from the temperature report ('M105' => B@:nnn) and try P*2-20 to P*2-10. + //#define PID_BED_DEBUG // Print Bed PID debug data to the serial port. Use 'M303 D' to enable/disable. // 120V 250W silicone heater into 4mm borosilicate (MendelMax 1.5+) // from FOPDT model - kp=.39 Tp=405 Tdead=66, Tc set to 79.2, aggressive factor of .15 (vs .1, 1, 10) - #define DEFAULT_bedKp 10.00 - #define DEFAULT_bedKi .023 + #define DEFAULT_bedKp 10.00 + #define DEFAULT_bedKi 0.023 #define DEFAULT_bedKd 305.4 // FIND YOUR OWN: "M303 E-1 C8 S90" to run autotune on the bed at 90 degreesC for 8 cycles. @@ -788,6 +819,40 @@ //#define BED_LIMIT_SWITCHING // Keep the bed temperature within BED_HYSTERESIS of the target #endif +/** + * Peltier Bed - Heating and Cooling + * + * A Peltier device transfers heat from one side to the other in proportion to the amount of + * current flowing through the device and the direction of current flow. So the same device + * can both heat and cool. + * + * When "cooling" in addition to rejecting the heat transferred from the hot side to the cold + * side, the dissipated power (voltage * current) must also be rejected. Be sure to set up a + * fan that can be powered in sync with the Peltier unit. + * + * This feature is only set up to run in bang-bang mode because Peltiers don't handle PWM + * well without filter circuitry. + * + * Since existing 3D printers are made to handle relatively high current for the heated bed, + * we can use the heated bed power pins to control the Peltier power using the same G-codes + * as the heated bed (M140, M190, etc.). + * + * A second GPIO pin is required to control current direction. + * Two configurations are possible: Relay and H-Bridge + * + * (At this time only relay is supported. H-bridge requires 4 MOS switches configured in H-Bridge.) + * + * Power is handled by the bang-bang control loop: 0 or 255. + * Cooling applications are more common than heating, so the pin states are commonly: + * LOW = Heating = Relay Energized + * HIGH = Cooling = Relay in "Normal" state + */ +//#define PELTIER_BED +#if ENABLED(PELTIER_BED) + #define PELTIER_DIR_PIN -1 // Relay control pin for Peltier + #define PELTIER_DIR_HEAT_STATE LOW // The relay pin state that causes the Peltier to heat +#endif + // Add 'M190 R T' for more gradual M190 R bed cooling. //#define BED_ANNEALING_GCODE @@ -822,24 +887,26 @@ #define MAX_CHAMBER_POWER 255 // limits duty cycle to chamber heater; 255=full current #if ENABLED(PIDTEMPCHAMBER) - #define MIN_CHAMBER_POWER 0 - //#define PID_CHAMBER_DEBUG // Print Chamber PID debug data to the serial port. + //#define MIN_CHAMBER_POWER 0 // Min power to improve PID stability. (0..MAX_CHAMBER_POWER) + // Get the power from the temperature report ('M105' => C@:nnn) and try P*2-20 to P*2-10. + //#define PID_CHAMBER_DEBUG // Print Chamber PID debug data to the serial port. Use 'M303 D' to enable/disable. // Lasko "MyHeat Personal Heater" (200w) modified with a Fotek SSR-10DA to control only the heating element // and placed inside the small Creality printer enclosure tent. - // - #define DEFAULT_chamberKp 37.04 - #define DEFAULT_chamberKi 1.40 + #define DEFAULT_chamberKp 37.04 + #define DEFAULT_chamberKi 1.40 #define DEFAULT_chamberKd 655.17 // M309 P37.04 I1.04 D655.17 // FIND YOUR OWN: "M303 E-2 C8 S50" to run autotune on the chamber at 50 degreesC for 8 cycles. #endif // PIDTEMPCHAMBER +// @section pid temp + #if ANY(PIDTEMP, PIDTEMPBED, PIDTEMPCHAMBER) //#define PID_OPENLOOP // Puts PID in open loop. M104/M140 sets the output power from 0 to PID_MAX //#define SLOW_PWM_HEATERS // PWM with very low frequency (roughly 0.125Hz=8s) and minimum state time of approximately 1s useful for heaters driven by a relay - #define PID_FUNCTIONAL_RANGE 10 // If the temperature difference between the target temperature and the actual temperature + #define PID_FUNCTIONAL_RANGE 20 // If the temperature difference between the target temperature and the actual temperature // is more than PID_FUNCTIONAL_RANGE then the PID will be shut off and the heater will be set to min/max. //#define PID_EDIT_MENU // Add PID editing to the "Advanced Settings" menu. (~700 bytes of flash) @@ -875,7 +942,7 @@ * protect against a broken or disconnected thermistor wire. * * The issue: If a thermistor falls out, it will report the much lower - * temperature of the air in the room, and the the firmware will keep + * temperature of the air in the room, and the firmware will keep * the heater on. * * If you get "Thermal Runaway" or "Heating failed" errors the @@ -891,7 +958,7 @@ //============================= Mechanical Settings ========================= //=========================================================================== -// @section machine +// @section kinematics // Enable one of the options below for CoreXY, CoreXZ, or CoreYZ kinematics, // either in the usual order or reversed @@ -915,6 +982,15 @@ // Enable for a belt style printer with endless "Z" motion //#define BELTPRINTER +// Articulated robot (arm). Joints are directly mapped to axes with no kinematics. +//#define ARTICULATED_ROBOT_ARM + +// For a hot wire cutter with parallel horizontal axes (X, I) where the heights of the two wire +// ends are controlled by parallel axes (Y, J). Joints are directly mapped to axes (no kinematics). +//#define FOAMCUTTER_XYUV + +// @section polargraph + // Enable for Polargraph Kinematics //#define POLARGRAPH #if ENABLED(POLARGRAPH) @@ -958,9 +1034,6 @@ // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers). #define PRINTABLE_RADIUS 140.0 // (mm) - // Maximum reachable area - #define DELTA_MAX_RADIUS 140.0 // (mm) - // Center-to-center distance of the holes in the diagonal push rods. #define DELTA_DIAGONAL_ROD 250.0 // (mm) @@ -1101,15 +1174,6 @@ #define FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly #endif -// @section machine - -// Articulated robot (arm). Joints are directly mapped to axes with no kinematics. -//#define ARTICULATED_ROBOT_ARM - -// For a hot wire cutter with parallel horizontal axes (X, I) where the heights of the two wire -// ends are controlled by parallel axes (Y, J). Joints are directly mapped to axes (no kinematics). -//#define FOAMCUTTER_XYUV - //=========================================================================== //============================== Endstop Settings =========================== //=========================================================================== @@ -1331,6 +1395,11 @@ * See https://github.com/synthetos/TinyG/wiki/Jerk-Controlled-Motion-Explained */ //#define S_CURVE_ACCELERATION +#if ENABLED(S_CURVE_ACCELERATION) + // Define to use 4th instead of 6th order motion curve + //#define S_CURVE_FACTOR 0.25 // Initial and final acceleration factor, ideally 0.1 to 0.4. + // Shouldn't generally require tuning. +#endif //=========================================================================== //============================= Z Probe Options ============================= @@ -1454,7 +1523,7 @@ * A lightweight, solenoid-driven probe. * For information about this sensor https://github.com/bigtreetech/MicroProbe * - * Also requires: PROBE_ENABLE_DISABLE + * Also requires PROBE_ENABLE_DISABLE */ //#define BIQU_MICROPROBE_V1 // Triggers HIGH //#define BIQU_MICROPROBE_V2 // Triggers LOW @@ -1482,6 +1551,20 @@ #define PROBE_DEPLOY_FEEDRATE (133*60) // (mm/min) Probe deploy speed #define PROBE_STOW_FEEDRATE (133*60) // (mm/min) Probe stow speed + /** + * Magnetically Mounted Probe with a Servo mechanism + * Probe Deploy and Stow both follow the same basic sequence: + * - Rotate the SERVO to its Deployed angle + * - Perform XYZ moves to deploy or stow the PROBE + * - Rotate the SERVO to its Stowed angle + */ + //#define MAG_MOUNTED_PROBE_SERVO_NR 0 // Servo Number for this probe + #ifdef MAG_MOUNTED_PROBE_SERVO_NR + #define MAG_MOUNTED_PROBE_SERVO_ANGLES { 90, 0 } // Servo Angles for Deployed, Stowed + #define MAG_MOUNTED_PRE_DEPLOY { PROBE_DEPLOY_FEEDRATE, { 15, 160, 30 } } // Safe position for servo activation + #define MAG_MOUNTED_PRE_STOW { PROBE_DEPLOY_FEEDRATE, { 15, 160, 30 } } // Safe position for servo deactivation + #endif + #define MAG_MOUNTED_DEPLOY_1 { PROBE_DEPLOY_FEEDRATE, { 245, 114, 30 } } // Move to side Dock & Attach probe #define MAG_MOUNTED_DEPLOY_2 { PROBE_DEPLOY_FEEDRATE, { 210, 114, 30 } } // Move probe off dock #define MAG_MOUNTED_DEPLOY_3 { PROBE_DEPLOY_FEEDRATE, { 0, 0, 0 } } // Extra move if needed @@ -1589,18 +1672,21 @@ //#define PROBE_TOOLCHANGE_NO_MOVE // Suppress motion on probe tool-change #endif +//#define PROBE_WAKEUP_TIME_MS 30 // (ms) Time for the probe to wake up + // Most probes should stay away from the edges of the bed, but // with NOZZLE_AS_PROBE this can be negative for a wider probing area. #define PROBING_MARGIN 10 -// X and Y axis travel speed (mm/min) between probes -#define XY_PROBE_FEEDRATE (133*60) +// X and Y axis travel speed between probes. +// Leave undefined to use the average of the current XY homing feedrate. +#define XY_PROBE_FEEDRATE (133*60) // (mm/min) -// Feedrate (mm/min) for the first approach when double-probing (MULTIPLE_PROBING == 2) -#define Z_PROBE_FEEDRATE_FAST (4*60) +// Feedrate for the first approach when double-probing (MULTIPLE_PROBING == 2) +#define Z_PROBE_FEEDRATE_FAST (4*60) // (mm/min) -// Feedrate (mm/min) for the "accurate" probe of each point -#define Z_PROBE_FEEDRATE_SLOW (Z_PROBE_FEEDRATE_FAST / 2) +// Feedrate for the "accurate" probe of each point +#define Z_PROBE_FEEDRATE_SLOW (Z_PROBE_FEEDRATE_FAST / 2) // (mm/min) /** * Probe Activation Switch @@ -1624,6 +1710,7 @@ #define PROBE_TARE_DELAY 200 // (ms) Delay after tare before #define PROBE_TARE_STATE HIGH // State to write pin for tare //#define PROBE_TARE_PIN PA5 // Override default pin + //#define PROBE_TARE_MENU // Display a menu item to tare the probe #if ENABLED(PROBE_ACTIVATION_SWITCH) //#define PROBE_TARE_ONLY_WHILE_INACTIVE // Fail to tare/probe if PROBE_ACTIVATION_SWITCH is active #endif @@ -1661,8 +1748,8 @@ * probe Z Offset set with NOZZLE_TO_PROBE_OFFSET, M851, or the LCD. * Only integer values >= 1 are valid here. * - * Example: `M851 Z-5` with a CLEARANCE of 4 => 9mm from bed to nozzle. - * But: `M851 Z+1` with a CLEARANCE of 2 => 2mm from bed to nozzle. + * Example: 'M851 Z-5' with a CLEARANCE of 4 => 9mm from bed to nozzle. + * But: 'M851 Z+1' with a CLEARANCE of 2 => 2mm from bed to nozzle. */ #define Z_CLEARANCE_DEPLOY_PROBE 10 // (mm) Z Clearance for Deploy/Stow #define Z_CLEARANCE_BETWEEN_PROBES 5 // (mm) Z Clearance between probe points @@ -1713,18 +1800,20 @@ #define PROBING_BED_TEMP 50 #endif +// @section stepper drivers + // For Inverting Stepper Enable Pins (Active Low) use 0, Non Inverting (Active High) use 1 -// :{ 0:'Low', 1:'High' } -#define X_ENABLE_ON 0 -#define Y_ENABLE_ON 0 -#define Z_ENABLE_ON 0 -#define E_ENABLE_ON 0 // For all extruders -//#define I_ENABLE_ON 0 -//#define J_ENABLE_ON 0 -//#define K_ENABLE_ON 0 -//#define U_ENABLE_ON 0 -//#define V_ENABLE_ON 0 -//#define W_ENABLE_ON 0 +// :['LOW', 'HIGH'] +#define X_ENABLE_ON LOW +#define Y_ENABLE_ON LOW +#define Z_ENABLE_ON LOW +#define E_ENABLE_ON LOW // For all extruders +//#define I_ENABLE_ON LOW +//#define J_ENABLE_ON LOW +//#define K_ENABLE_ON LOW +//#define U_ENABLE_ON LOW +//#define V_ENABLE_ON LOW +//#define W_ENABLE_ON LOW // Disable axis steppers immediately when they're not being stepped. // WARNING: When motors turn off there is a chance of losing position accuracy! @@ -1886,6 +1975,8 @@ #endif /** + * @section filament runout sensors + * * Filament Runout Sensors * Mechanical or opto endstops are used to check for the presence of filament. * @@ -1959,8 +2050,11 @@ //#define FILAMENT_MOTION_SENSOR #if ENABLED(FILAMENT_MOTION_SENSOR) - //#define FILAMENT_SWITCH_AND_MOTION + //#define FILAMENT_SWITCH_AND_MOTION // Define separate pins below to sense motion #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + + #define FILAMENT_MOTION_DISTANCE_MM 3.0 // (mm) Missing distance required to trigger runout + #define NUM_MOTION_SENSORS 1 // Number of sensors, up to one per extruder. Define a FIL_MOTION#_PIN for each. //#define FIL_MOTION1_PIN -1 @@ -1996,10 +2090,10 @@ //#define FIL_MOTION8_STATE LOW //#define FIL_MOTION8_PULLUP //#define FIL_MOTION8_PULLDOWN - #endif - #endif - #endif -#endif + #endif // FILAMENT_SWITCH_AND_MOTION + #endif // FILAMENT_MOTION_SENSOR + #endif // FILAMENT_RUNOUT_DISTANCE_MM +#endif // FILAMENT_RUNOUT_SENSOR //=========================================================================== //=============================== Bed Leveling ============================== @@ -2045,6 +2139,12 @@ //#define AUTO_BED_LEVELING_UBL //#define MESH_BED_LEVELING +/** + * Commands to execute at the start of G29 probing, + * after switching to the PROBING_TOOL. + */ +//#define EVENT_GCODE_BEFORE_G29 "M300 P440 S200" + /** * Commands to execute at the end of G29 probing. * Useful to retract or move the Z probe out of the way. @@ -2193,7 +2293,7 @@ //=========================================================================== #define MESH_INSET 10 // Set Mesh bounds as an inset region of the bed - #define GRID_MAX_POINTS_X 3 // Don't use more than 7 points per axis, implementation limited. + #define GRID_MAX_POINTS_X 3 #define GRID_MAX_POINTS_Y GRID_MAX_POINTS_X //#define MESH_G28_REST_ORIGIN // After homing all axes ('G28' or 'G28 XYZ') rest Z at Z_MIN_POS @@ -2218,7 +2318,7 @@ #if ENABLED(LCD_BED_TRAMMING) #define BED_TRAMMING_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets #define BED_TRAMMING_HEIGHT 0.0 // (mm) Z height of nozzle at tramming points - #define BED_TRAMMING_Z_HOP 4.0 // (mm) Z height of nozzle between tramming points + #define BED_TRAMMING_Z_HOP 4.0 // (mm) Z raise between tramming points //#define BED_TRAMMING_INCLUDE_CENTER // Move to the center after the last corner //#define BED_TRAMMING_USE_PROBE #if ENABLED(BED_TRAMMING_USE_PROBE) @@ -2282,6 +2382,9 @@ // Homing speeds (linear=mm/min, rotational=°/min) #define HOMING_FEEDRATE_MM_M { (50*60), (50*60), (4*60) } +// Edit homing feedrates with M210 and MarlinUI menu items +//#define EDITABLE_HOMING_FEEDRATE + // Validate that endstops are triggered on homing moves #define VALIDATE_HOMING_ENDSTOPS @@ -2408,9 +2511,9 @@ #define PREHEAT_2_TEMP_CHAMBER 35 #define PREHEAT_2_FAN_SPEED 0 // Value from 0 to 255 -// @section motion - /** + * @section nozzle park + * * Nozzle Park * * Park the nozzle at the given XYZ position on idle or G27. @@ -2433,6 +2536,8 @@ #endif /** + * @section nozzle clean + * * Clean Nozzle Feature * * Adds the G12 command to perform a nozzle cleaning process. @@ -2593,9 +2698,24 @@ //#include "Configuration_Secure.h" // External file with PASSWORD_DEFAULT_VALUE #endif -//============================================================================= -//============================= LCD and SD support ============================ -//============================================================================= +// @section media + +/** + * SD CARD + * + * SD Card support is disabled by default. If your controller has an SD slot, + * you must uncomment the following option or it won't work. + */ +//#define SDSUPPORT + +/** + * SD CARD: ENABLE CRC + * + * Use CRC checks and retries on the SD communication. + */ +#if ENABLED(SDSUPPORT) + //#define SD_CHECK_AND_RETRY +#endif // @section interface @@ -2642,21 +2762,6 @@ */ #define LCD_INFO_SCREEN_STYLE 0 -/** - * SD CARD - * - * SD Card support is disabled by default. If your controller has an SD slot, - * you must uncomment the following option or it won't work. - */ -//#define SDSUPPORT - -/** - * SD CARD: ENABLE CRC - * - * Use CRC checks and retries on the SD communication. - */ -//#define SD_CHECK_AND_RETRY - /** * LCD Menu Items * @@ -2785,7 +2890,7 @@ // // Original RADDS LCD Display+Encoder+SDCardReader -// https://web.archive.org/web/20200719145306/http://doku.radds.org/dokumentation/lcd-display/ +// https://web.archive.org/web/20200719145306/doku.radds.org/dokumentation/lcd-display/ // //#define RADDS_DISPLAY @@ -2824,13 +2929,15 @@ // //#define MAKEBOARD_MINI_2_LINE_DISPLAY_1602 -// -// ANET and Tronxy 20x4 Controller -// -//#define ZONESTAR_LCD // Requires ADC_KEYPAD_PIN to be assigned to an analog pin. - // This LCD is known to be susceptible to electrical interference - // which scrambles the display. Pressing any button clears it up. - // This is a LCD2004 display with 5 analog buttons. +/** + * ANET and Tronxy 20x4 Controller + * LCD2004 display with 5 analog buttons. + * + * NOTE: Requires ADC_KEYPAD_PIN to be assigned to an analog pin. + * This LCD is known to be susceptible to electrical interference which + * scrambles the display. Press any button to clear it up. + */ +//#define ZONESTAR_LCD // // Generic 16x2, 16x4, 20x2, or 20x4 character-based LCD. @@ -2851,7 +2958,7 @@ // // Elefu RA Board Control Panel -// https://web.archive.org/web/20140823033947/http://www.elefu.com/index.php?route=product/product&product_id=53 +// https://web.archive.org/web/20140823033947/www.elefu.com/index.php?route=product/product&product_id=53 // //#define RA_CONTROL_PANEL @@ -2983,7 +3090,7 @@ // // Cartesio UI -// https://web.archive.org/web/20180605050442/http://mauk.cc/webshop/cartesio-shop/electronics/user-interface +// https://web.archive.org/web/20180605050442/mauk.cc/webshop/cartesio-shop/electronics/user-interface // //#define CARTESIO_UI @@ -3018,7 +3125,7 @@ // // FYSETC variant of the MINI12864 graphic controller with SD support -// https://wiki.fysetc.com/Mini12864_Panel/ +// https://wiki.fysetc.com/docs/Mini12864Panel // //#define FYSETC_MINI_12864_X_X // Type C/D/E/F. No tunable RGB Backlight by default //#define FYSETC_MINI_12864_1_2 // Type C/D/E/F. Simple RGB Backlight (always on) @@ -3107,14 +3214,14 @@ // // Tiny, but very sharp OLED display // -//#define MKS_12864OLED // Uses the SH1106 controller (default) +//#define MKS_12864OLED // Uses the SH1106 controller //#define MKS_12864OLED_SSD1306 // Uses the SSD1306 controller // // Zonestar OLED 128×64 Full Graphics Controller // //#define ZONESTAR_12864LCD // Graphical (DOGM) with ST7920 controller -//#define ZONESTAR_12864OLED // 1.3" OLED with SH1106 controller (default) +//#define ZONESTAR_12864OLED // 1.3" OLED with SH1106 controller //#define ZONESTAR_12864OLED_SSD1306 // 0.96" OLED with SSD1306 controller // @@ -3192,7 +3299,7 @@ #endif // -// Touch-screen LCD for Malyan M200/M300 printers +// LCD for Malyan M200/M300 printers // //#define MALYAN_LCD @@ -3220,6 +3327,11 @@ // //#define ANYCUBIC_LCD_VYPER +// +// Sovol SV-06 Resistive Touch Screen +// +//#define SOVOL_SV06_RTS + // // 320x240 Nextion 2.8" serial TFT Resistive Touch Screen NX3224T028 // @@ -3358,20 +3470,22 @@ #if ENABLED(TFT_COLOR_UI) /** - * TFT Font for Color_UI. Choose one of the following: + * TFT Font for Color UI. Choose one of the following: * * NOTOSANS - Default font with anti-aliasing. Supports Latin Extended and non-Latin characters. * UNIFONT - Lightweight font, no anti-aliasing. Supports Latin Extended and non-Latin characters. * HELVETICA - Lightweight font, no anti-aliasing. Supports Basic Latin (0x0020-0x007F) and Latin-1 Supplement (0x0080-0x00FF) characters only. + * :['NOTOSANS', 'UNIFONT', 'HELVETICA'] */ #define TFT_FONT NOTOSANS /** - * TFT Theme for Color_UI. Choose one of the following or add a new one to 'Marlin/src/lcd/tft/themes' directory + * TFT Theme for Color UI. Choose one of the following or add a new one to 'Marlin/src/lcd/tft/themes' directory * * BLUE_MARLIN - Default theme with 'midnight blue' background * BLACK_MARLIN - Theme with 'black' background * ANET_BLACK - Theme used for Anet ET4/5 + * :['BLUE_MARLIN', 'BLACK_MARLIN', 'ANET_BLACK'] */ #define TFT_THEME BLACK_MARLIN @@ -3391,6 +3505,8 @@ * TFT_ROTATE_180, TFT_ROTATE_180_MIRROR_X, TFT_ROTATE_180_MIRROR_Y, * TFT_ROTATE_270, TFT_ROTATE_270_MIRROR_X, TFT_ROTATE_270_MIRROR_Y, * TFT_MIRROR_X, TFT_MIRROR_Y, TFT_NO_ROTATION + * + * :{ 'TFT_NO_ROTATION':'None', 'TFT_ROTATE_90':'90°', 'TFT_ROTATE_90_MIRROR_X':'90° (Mirror X)', 'TFT_ROTATE_90_MIRROR_Y':'90° (Mirror Y)', 'TFT_ROTATE_180':'180°', 'TFT_ROTATE_180_MIRROR_X':'180° (Mirror X)', 'TFT_ROTATE_180_MIRROR_Y':'180° (Mirror Y)', 'TFT_ROTATE_270':'270°', 'TFT_ROTATE_270_MIRROR_X':'270° (Mirror X)', 'TFT_ROTATE_270_MIRROR_Y':'270° (Mirror Y)', 'TFT_MIRROR_X':'Mirror X', 'TFT_MIRROR_Y':'Mirror Y' } */ //#define TFT_ROTATION TFT_NO_ROTATION @@ -3407,6 +3523,11 @@ //#define DWIN_MARLINUI_PORTRAIT // MarlinUI (portrait orientation) //#define DWIN_MARLINUI_LANDSCAPE // MarlinUI (landscape orientation) +#if ENABLED(DWIN_CREALITY_LCD) + //#define USE_STRING_HEADINGS // Use string headings for Creality UI instead of images + //#define USE_STRING_TITLES // Use string titles for Creality UI instead of images +#endif + // // Touch Screen Settings // @@ -3415,7 +3536,9 @@ #define BUTTON_DELAY_EDIT 50 // (ms) Button repeat delay for edit screens #define BUTTON_DELAY_MENU 250 // (ms) Button repeat delay for menus - //#define DISABLE_ENCODER // Disable the click encoder, if any + #if ANY(TFT_CLASSIC_UI, TFT_COLOR_UI) + //#define NO_BACK_MENU_ITEM // Don't display a top menu item to go back to the parent menu + #endif #define TOUCH_SCREEN_CALIBRATION @@ -3439,7 +3562,9 @@ // https://reprapworld.com/products/electronics/ramps/keypad_v1_0_fully_assembled/ // //#define REPRAPWORLD_KEYPAD -//#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0 // (mm) Distance to move per key-press +#if ENABLED(REPRAPWORLD_KEYPAD) + //#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0 // (mm) Distance to move per key-press +#endif // // EasyThreeD ET-4000+ with button input and status LED @@ -3456,22 +3581,26 @@ // :[1,2,3,4,5,6,7,8] //#define NUM_M106_FANS 1 -// Use software PWM to drive the fan, as for the heaters. This uses a very low frequency -// which is not as annoying as with the hardware PWM. On the other hand, if this frequency -// is too low, you should also increment SOFT_PWM_SCALE. +/** + * Use software PWM to drive the fan, as for the heaters. This uses a very low frequency + * which is not as annoying as with the hardware PWM. On the other hand, if this frequency + * is too low, you should also increment SOFT_PWM_SCALE. + */ //#define FAN_SOFT_PWM -// Incrementing this by 1 will double the software PWM frequency, -// affecting heaters, and the fan if FAN_SOFT_PWM is enabled. -// However, control resolution will be halved for each increment; -// at zero value, there are 128 effective control positions. -// :[0,1,2,3,4,5,6,7] +/** + * Incrementing this by 1 will double the software PWM frequency, affecting heaters, and + * the fan if FAN_SOFT_PWM is enabled. However, control resolution will be halved for each + * increment; at zero value, there are 128 effective control positions. + * :[0,1,2,3,4,5,6,7] + */ #define SOFT_PWM_SCALE 0 -// If SOFT_PWM_SCALE is set to a value higher than 0, dithering can -// be used to mitigate the associated resolution loss. If enabled, -// some of the PWM cycles are stretched so on average the desired -// duty cycle is attained. +/** + * If SOFT_PWM_SCALE is set to a value higher than 0, dithering can be used to mitigate the + * associated resolution loss. If enabled, some of the PWM cycles are stretched so on average + * the desired duty cycle is attained. + */ //#define SOFT_PWM_DITHER // @section extras @@ -3481,9 +3610,11 @@ // @section lights -// Temperature status LEDs that display the hotend and bed temperature. -// If all hotends, bed temperature, and target temperature are under 54C -// then the BLUE led is on. Otherwise the RED led is on. (1C hysteresis) +/** + * Temperature status LEDs that display the hotend and bed temperature. + * If all hotends, bed temperature, and target temperature are under 54C + * the BLUE led is on. Otherwise the RED led is on. (1C hysteresis) + */ //#define TEMP_STAT_LEDS // Support for BlinkM/CyzRgb diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 434aa83e56..ae4acaa56b 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -47,8 +47,9 @@ * 2 = config.ini - File format for PlatformIO preprocessing. * 3 = schema.json - The entire configuration schema. (13 = pattern groups) * 4 = schema.yml - The entire configuration schema. + * 5 = Config.h - Minimal configuration by popular demand. */ -//#define CONFIG_EXPORT 2 // :[1:'JSON', 2:'config.ini', 3:'schema.json', 4:'schema.yml'] +//#define CONFIG_EXPORT 105 // :[1:'JSON', 2:'config.ini', 3:'schema.json', 4:'schema.yml', 5:'Config.h'] //=========================================================================== //============================= Thermal Settings ============================ @@ -173,9 +174,10 @@ * Thermocouple Options — for MAX6675 (-2), MAX31855 (-3), and MAX31865 (-5). */ //#define TEMP_SENSOR_FORCE_HW_SPI // Ignore SCK/MOSI/MISO pins; use CS and the default SPI bus. -//#define MAX31865_SENSOR_WIRES_0 2 // (2-4) Number of wires for the probe connected to a MAX31865 board. -//#define MAX31865_SENSOR_WIRES_1 2 -//#define MAX31865_SENSOR_WIRES_2 2 +//#define MAX31865_SENSOR_WIRES_0 2 // (2-4) Number of wires for the probe connected to a MAX31865 board. +//#define MAX31865_SENSOR_WIRES_1 2 +//#define MAX31865_SENSOR_WIRES_2 2 +//#define MAX31865_SENSOR_WIRES_BED 2 //#define MAX31865_50HZ_FILTER // Use a 50Hz filter instead of the default 60Hz. //#define MAX31865_USE_READ_ERROR_DETECTION // Treat value spikes (20°C delta in under 1s) as read errors. @@ -187,6 +189,7 @@ //#define MAX31865_WIRE_OHMS_0 0.95f // For 2-wire, set the wire resistances for more accurate readings. //#define MAX31865_WIRE_OHMS_1 0.0f //#define MAX31865_WIRE_OHMS_2 0.0f +//#define MAX31865_WIRE_OHMS_BED 0.0f /** * Hephestos 2 24V heated bed upgrade kit. @@ -202,7 +205,7 @@ // #if DISABLED(PIDTEMPBED) #define BED_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control - #if ENABLED(BED_LIMIT_SWITCHING) + #if ANY(BED_LIMIT_SWITCHING, PELTIER_BED) #define BED_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > BED_HYSTERESIS #endif #endif @@ -210,18 +213,19 @@ // // Heated Chamber options // -#if DISABLED(PIDTEMPCHAMBER) - #define CHAMBER_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control - #if ENABLED(CHAMBER_LIMIT_SWITCHING) - #define CHAMBER_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS - #endif -#endif #if TEMP_SENSOR_CHAMBER //#define HEATER_CHAMBER_PIN P2_04 // Required heater on/off pin (example: SKR 1.4 Turbo HE1 plug) //#define HEATER_CHAMBER_INVERTING false //#define FAN1_PIN -1 // Remove the fan signal on pin P2_04 (example: SKR 1.4 Turbo HE1 plug) + #if DISABLED(PIDTEMPCHAMBER) + #define CHAMBER_CHECK_INTERVAL 5000 // (ms) Interval between checks in bang-bang control + #if ENABLED(CHAMBER_LIMIT_SWITCHING) + #define CHAMBER_HYSTERESIS 2 // (°C) Only set the relevant heater state when ABS(T-target) > CHAMBER_HYSTERESIS + #endif + #endif + //#define CHAMBER_FAN // Enable a fan on the chamber #if ENABLED(CHAMBER_FAN) //#define CHAMBER_FAN_INDEX 2 // Index of a fan to repurpose as the chamber fan. (Default: first unused fan) @@ -293,7 +297,7 @@ * protect against a broken or disconnected thermistor wire. * * The issue: If a thermistor falls out, it will report the much lower - * temperature of the air in the room, and the the firmware will keep + * temperature of the air in the room, and the firmware will keep * the heater on. * * The solution: Once the temperature reaches the target, start observing. @@ -303,9 +307,9 @@ * If you get false positives for "Thermal Runaway", increase * THERMAL_PROTECTION_HYSTERESIS and/or THERMAL_PROTECTION_PERIOD */ -#if ENABLED(THERMAL_PROTECTION_HOTENDS) - #define THERMAL_PROTECTION_PERIOD 40 // (seconds) - #define THERMAL_PROTECTION_HYSTERESIS 4 // (°C) +#if ALL(HAS_HOTEND, THERMAL_PROTECTION_HOTENDS) + #define THERMAL_PROTECTION_PERIOD 40 // (seconds) + #define THERMAL_PROTECTION_HYSTERESIS 4 // (°C) //#define ADAPTIVE_FAN_SLOWING // Slow down the part-cooling fan if the temperature drops #if ENABLED(ADAPTIVE_FAN_SLOWING) @@ -334,7 +338,7 @@ /** * Thermal Protection parameters for the bed are just as above for hotends. */ -#if ENABLED(THERMAL_PROTECTION_BED) +#if TEMP_SENSOR_BED && ENABLED(THERMAL_PROTECTION_BED) #define THERMAL_PROTECTION_BED_PERIOD 20 // (seconds) #define THERMAL_PROTECTION_BED_HYSTERESIS 2 // (°C) @@ -348,7 +352,7 @@ /** * Thermal Protection parameters for the heated chamber. */ -#if ENABLED(THERMAL_PROTECTION_CHAMBER) +#if TEMP_SENSOR_CHAMBER && ENABLED(THERMAL_PROTECTION_CHAMBER) #define THERMAL_PROTECTION_CHAMBER_PERIOD 20 // (seconds) #define THERMAL_PROTECTION_CHAMBER_HYSTERESIS 2 // (°C) @@ -362,7 +366,7 @@ /** * Thermal Protection parameters for the laser cooler. */ -#if ENABLED(THERMAL_PROTECTION_COOLER) +#if TEMP_SENSOR_COOLER && ENABLED(THERMAL_PROTECTION_COOLER) #define THERMAL_PROTECTION_COOLER_PERIOD 10 // (seconds) #define THERMAL_PROTECTION_COOLER_HYSTERESIS 3 // (°C) @@ -590,6 +594,8 @@ // Use TEMP_SENSOR_SOC as a trigger for enabling the controller fan //#define CONTROLLER_FAN_MIN_SOC_TEMP 40 // (°C) Turn on the fan if the SoC reaches this temperature + #define CONTROLLER_FAN_BED_HEATING // Turn on the fan when heating the bed + //#define CONTROLLER_FAN_EDITABLE // Enable M710 configurable settings #if ENABLED(CONTROLLER_FAN_EDITABLE) #define CONTROLLER_FAN_MENU // Enable the Controller Fan submenu @@ -604,6 +610,8 @@ */ //#define FAN_KICKSTART_TIME 100 // (ms) //#define FAN_KICKSTART_POWER 180 // 64-255 +//#define FAN_KICKSTART_LINEAR // Set kickstart time linearly based on the speed, e.g., for 20% (51) it will be FAN_KICKSTART_TIME * 0.2. + // Useful for quick speed up to low speed. Kickstart power must be set to 255. // Some coolers may require a non-zero "off" state. //#define FAN_OFF_PWM 1 @@ -770,7 +778,7 @@ // @section endstops -// If you want endstops to stay on (by default) even when not homing +// If you want endstops to stay on (by default) even when not homing, // enable this option. Override at any time with M120, M121. //#define ENDSTOPS_ALWAYS_ON_DEFAULT @@ -1014,7 +1022,7 @@ #endif // BLTOUCH -// @section calibration +// @section calibrate /** * Z Steppers Auto-Alignment @@ -1073,15 +1081,32 @@ #define G34_MAX_GRADE 5 // (%) Maximum incline that G34 will handle #define Z_STEPPER_ALIGN_ITERATIONS 5 // Number of iterations to apply during alignment #define Z_STEPPER_ALIGN_ACC 0.02 // Stop iterating early if the accuracy is better than this + #define RESTORE_LEVELING_AFTER_G34 // Restore leveling after G34 is done? + // After G34, re-home Z (G28 Z) or just calculate it from the last probe heights? // Re-homing might be more precise in reproducing the actual 'G28 Z' homing height, especially on an uneven bed. #define HOME_AFTER_G34 -#endif -// -// Add the G35 command to read bed corners to help adjust screws. Requires a bed probe. -// + /** + * Commands to execute at the start of G34 probing, + * after switching to the PROBING_TOOL. + */ + //#define EVENT_GCODE_BEFORE_G34 "M300 P440 S200" + + /** + * Commands to execute at the end of G34 probing. + * Useful to retract or move the Z probe out of the way. + */ + //#define EVENT_GCODE_AFTER_G34 "G1 Z10 F12000\nG1 X15 Y330\nG1 Z0.5\nG1 Z10" + +#endif // Z_STEPPER_AUTO_ALIGN + +/** + * Assisted Tramming + * + * Add the G35 command to measure bed corners and help adjust screws. Requires a bed probe. + */ //#define ASSISTED_TRAMMING #if ENABLED(ASSISTED_TRAMMING) @@ -1102,107 +1127,109 @@ //#define ASSISTED_TRAMMING_WAIT_POSITION { X_CENTER, Y_CENTER, 30 } // Move the nozzle out of the way for adjustment /** - * Screw thread: - * M3: 30 = Clockwise, 31 = Counter-Clockwise - * M4: 40 = Clockwise, 41 = Counter-Clockwise - * M5: 50 = Clockwise, 51 = Counter-Clockwise + * Screw Thread. Use one of the following defines: + * + * M3_CW = M3 Clockwise, M3_CCW = M3 Counter-Clockwise + * M4_CW = M4 Clockwise, M4_CCW = M4 Counter-Clockwise + * M5_CW = M5 Clockwise, M5_CCW = M5 Counter-Clockwise + * + * :{'M3_CW':'M3 Clockwise','M3_CCW':'M3 Counter-Clockwise','M4_CW':'M4 Clockwise','M4_CCW':'M4 Counter-Clockwise','M5_CW':'M5 Clockwise','M5_CCW':'M5 Counter-Clockwise'} */ - #define TRAMMING_SCREW_THREAD 30 + #define TRAMMING_SCREW_THREAD M3_CW #endif // @section motion control /** - * Fixed-time-based Motion Control -- EXPERIMENTAL - * Enable/disable and set parameters with G-code M493. + * Fixed-time-based Motion Control -- BETA FEATURE + * Enable/disable and set parameters with G-code M493 and M494. + * See ft_types.h for named values used by FTM options. */ //#define FT_MOTION #if ENABLED(FT_MOTION) - #define FTM_DEFAULT_MODE ftMotionMode_DISABLED // Default mode of fixed time control. (Enums in ft_types.h) - #define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (Enums in ft_types.h) - #define FTM_SHAPING_DEFAULT_X_FREQ 37.0f // (Hz) Default peak frequency used by input shapers - #define FTM_SHAPING_DEFAULT_Y_FREQ 37.0f // (Hz) Default peak frequency used by input shapers - #define FTM_LINEAR_ADV_DEFAULT_ENA false // Default linear advance enable (true) or disable (false) - #define FTM_LINEAR_ADV_DEFAULT_K 0.0f // Default linear advance gain - #define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis - #define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis + //#define FTM_IS_DEFAULT_MOTION // Use FT Motion as the factory default? + //#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 and M494 parameters + //#define FTM_HOME_AND_PROBE // Use FT Motion for homing / probing. Disable if FT Motion breaks these functions. - #define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis - #define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis + #define FTM_DEFAULT_DYNFREQ_MODE dynFreqMode_DISABLED // Default mode of dynamic frequency calculation. (DISABLED, Z_BASED, MASS_BASED) - //#define FT_MOTION_MENU // Provide a MarlinUI menu to set M493 parameters + #define FTM_DEFAULT_SHAPER_X ftMotionShaper_NONE // Default shaper mode on X axis (NONE, ZV, ZVD, ZVDD, ZVDDD, EI, 2HEI, 3HEI, MZV) + #define FTM_SHAPING_DEFAULT_FREQ_X 37.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_X 0.1f // Zeta used by input shapers for X axis + #define FTM_SHAPING_V_TOL_X 0.05f // Vibration tolerance used by EI input shapers for X axis + + #define FTM_DEFAULT_SHAPER_Y ftMotionShaper_NONE // Default shaper mode on Y axis + #define FTM_SHAPING_DEFAULT_FREQ_Y 37.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_Y 0.1f // Zeta used by input shapers for Y axis + #define FTM_SHAPING_V_TOL_Y 0.05f // Vibration tolerance used by EI input shapers for Y axis + + //#define FTM_SHAPER_Z // Include Z shaping support + #define FTM_DEFAULT_SHAPER_Z ftMotionShaper_NONE // Default shaper mode on Z axis + #define FTM_SHAPING_DEFAULT_FREQ_Z 21.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_Z 0.03f // Zeta used by input shapers for Z axis + #define FTM_SHAPING_V_TOL_Z 0.05f // Vibration tolerance used by EI input shapers for Z axis + + //#define FTM_SHAPER_E // Include E shaping support + // Required to synchronize extruder with XYZ (better quality) + #define FTM_DEFAULT_SHAPER_E ftMotionShaper_NONE // Default shaper mode on Extruder axis + #define FTM_SHAPING_DEFAULT_FREQ_E 21.0f // (Hz) Default peak frequency used by input shapers + #define FTM_SHAPING_ZETA_E 0.03f // Zeta used by input shapers for E axis + #define FTM_SHAPING_V_TOL_E 0.05f // Vibration tolerance used by EI input shapers for E axis + + //#define FTM_RESONANCE_TEST // Sine sweep motion for resonance study + + //#define FTM_SMOOTHING // Smoothing can reduce artifacts and make steppers quieter + // on sharp corners, but too much will round corners. + #if ENABLED(FTM_SMOOTHING) + #define FTM_MAX_SMOOTHING_TIME 0.10f // (s) Maximum smoothing time. Higher values consume more RAM. + // Increase smoothing time to reduce jerky motion, ghosting and noises. + #define FTM_SMOOTHING_TIME_X 0.00f // (s) Smoothing time for X axis. Zero means disabled. + #define FTM_SMOOTHING_TIME_Y 0.00f // (s) Smoothing time for Y axis + #define FTM_SMOOTHING_TIME_Z 0.00f // (s) Smoothing time for Z axis + #define FTM_SMOOTHING_TIME_E 0.02f // (s) Smoothing time for E axis. Prevents noise/skipping from LA by + // smoothing acceleration peaks, which may also smooth curved surfaces. + #endif + + #define FTM_TRAJECTORY_TYPE TRAPEZOIDAL // Block acceleration profile (TRAPEZOIDAL, POLY5, POLY6) + // TRAPEZOIDAL: Continuous Velocity. Max acceleration is respected. + // POLY5: Like POLY6 with 1.5x but uses less CPU. + // POLY6: Continuous Acceleration (aka S_CURVE). + // POLY trajectories not only reduce resonances without rounding corners, but also + // reduce extruder strain due to linear advance. + + #define FTM_POLY6_ACCELERATION_OVERSHOOT 1.875f // Max acceleration overshoot factor for POLY6 (1.25 to 1.875) /** * Advanced configuration */ - #define FTM_UNIFIED_BWS // DON'T DISABLE unless you use Ulendo FBS (not implemented) - #if ENABLED(FTM_UNIFIED_BWS) - #define FTM_BW_SIZE 100 // Unified Window and Batch size with a ratio of 2 - #else - #define FTM_WINDOW_SIZE 200 // Custom Window size for trajectory generation needed by Ulendo FBS - #define FTM_BATCH_SIZE 100 // Custom Batch size for trajectory generation needed by Ulendo FBS - #endif + #define FTM_BUFFER_SIZE 128 // Window size for trajectory generation, must be a power of 2 (e.g 64, 128, 256, ...) + // The total buffered time in seconds is (FTM_BUFFER_SIZE/FTM_FS) + #define FTM_FS 1000 // (Hz) Frequency for trajectory generation. + #define FTM_STEPPER_FS 2'000'000 // (Hz) Time resolution of stepper I/O update. Shouldn't affect CPU much (slower board testing needed) + #define FTM_MIN_SHAPE_FREQ 20 // (Hz) Minimum shaping frequency, lower consumes more RAM - #define FTM_FS 1000 // (Hz) Frequency for trajectory generation. (Reciprocal of FTM_TS) - #define FTM_TS 0.001f // (s) Time step for trajectory generation. (Reciprocal of FTM_FS) - - // These values may be configured to adjust the duration of loop(). - #define FTM_STEPS_PER_LOOP 60 // Number of stepper commands to generate each loop() - #define FTM_POINTS_PER_LOOP 100 // Number of trajectory points to generate each loop() - - #if DISABLED(COREXY) - #define FTM_STEPPER_FS 20000 // (Hz) Frequency for stepper I/O update - - // Use this to adjust the time required to consume the command buffer. - // Try increasing this value if stepper motion is choppy. - #define FTM_STEPPERCMD_BUFF_SIZE 3000 // Size of the stepper command buffers - // (FTM_STEPS_PER_LOOP * FTM_POINTS_PER_LOOP) is a good start - // If you run out of memory, fall back to 3000 and increase progressively - #else - // CoreXY motion needs a larger buffer size. These values are based on our testing. - #define FTM_STEPPER_FS 30000 - #define FTM_STEPPERCMD_BUFF_SIZE 6000 - #endif - - #define FTM_STEPS_PER_UNIT_TIME (FTM_STEPPER_FS / FTM_FS) // Interpolated stepper commands per unit time - #define FTM_CTS_COMPARE_VAL (FTM_STEPS_PER_UNIT_TIME / 2) // Comparison value used in interpolation algorithm - #define FTM_MIN_TICKS ((STEPPER_TIMER_RATE) / (FTM_STEPPER_FS)) // Minimum stepper ticks between steps - - #define FTM_MIN_SHAPE_FREQ 10 // Minimum shaping frequency - #define FTM_RATIO (FTM_FS / FTM_MIN_SHAPE_FREQ) // Factor for use in FTM_ZMAX. DON'T CHANGE. - #define FTM_ZMAX (FTM_RATIO * 2) // Maximum delays for shaping functions (even numbers only!) - // Calculate as: - // ZV : FTM_RATIO / 2 - // ZVD, MZV : FTM_RATIO - // 2HEI : FTM_RATIO * 3 / 2 - // 3HEI : FTM_RATIO * 2 -#endif +#endif // FT_MOTION /** - * Input Shaping -- EXPERIMENTAL + * Input Shaping * * Zero Vibration (ZV) Input Shaping for X and/or Y movements. * * This option uses a lot of SRAM for the step buffer. The buffer size is - * calculated automatically from SHAPING_FREQ_[XY], DEFAULT_AXIS_STEPS_PER_UNIT, + * calculated automatically from SHAPING_FREQ_[XYZ], DEFAULT_AXIS_STEPS_PER_UNIT, * DEFAULT_MAX_FEEDRATE and ADAPTIVE_STEP_SMOOTHING. The default calculation can * be overridden by setting SHAPING_MIN_FREQ and/or SHAPING_MAX_FEEDRATE. * The higher the frequency and the lower the feedrate, the smaller the buffer. * If the buffer is too small at runtime, input shaping will have reduced * effectiveness during high speed movements. * - * Tune with M593 D F: - * - * D Set the zeta/damping factor. If axes (X, Y, etc.) are not specified, set for all axes. - * F Set the frequency. If axes (X, Y, etc.) are not specified, set for all axes. - * T[map] Input Shaping type, 0:ZV, 1:EI, 2:2H EI (not implemented yet) - * X<1> Set the given parameters only for the X axis. - * Y<1> Set the given parameters only for the Y axis. + * Tune with M593 D F */ //#define INPUT_SHAPING_X //#define INPUT_SHAPING_Y -#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y) +//#define INPUT_SHAPING_Z +#if ANY(INPUT_SHAPING_X, INPUT_SHAPING_Y, INPUT_SHAPING_Z) #if ENABLED(INPUT_SHAPING_X) #define SHAPING_FREQ_X 40.0 // (Hz) The default dominant resonant frequency on the X axis. #define SHAPING_ZETA_X 0.15 // Damping ratio of the X axis (range: 0.0 = no damping to 1.0 = critical damping). @@ -1211,6 +1238,10 @@ #define SHAPING_FREQ_Y 40.0 // (Hz) The default dominant resonant frequency on the Y axis. #define SHAPING_ZETA_Y 0.15 // Damping ratio of the Y axis (range: 0.0 = no damping to 1.0 = critical damping). #endif + #if ENABLED(INPUT_SHAPING_Z) + #define SHAPING_FREQ_Z 40.0 // (Hz) The default dominant resonant frequency on the Z axis. + #define SHAPING_ZETA_Z 0.15 // Damping ratio of the Z axis (range: 0.0 = no damping to 1.0 = critical damping). + #endif //#define SHAPING_MIN_FREQ 20.0 // (Hz) By default the minimum of the shaping frequencies. Override to affect SRAM usage. //#define SHAPING_MAX_STEPRATE 10000 // By default the maximum total step rate of the shaped axes. Override to affect SRAM usage. //#define SHAPING_MENU // Add a menu to the LCD to set shaping parameters. @@ -1253,8 +1284,8 @@ #define DISABLE_IDLE_E // Shut down all idle extruders // Default Minimum Feedrates for printing and travel moves -#define DEFAULT_MINIMUMFEEDRATE 0.0 // (mm/s. °/s for rotational-only moves) Minimum feedrate. Set with M205 S. -#define DEFAULT_MINTRAVELFEEDRATE 0.0 // (mm/s. °/s for rotational-only moves) Minimum travel feedrate. Set with M205 T. +#define DEFAULT_MINIMUMFEEDRATE 0.0 // (mm/s) Minimum feedrate. Set with M205 S. +#define DEFAULT_MINTRAVELFEEDRATE 0.0 // (mm/s) Minimum travel feedrate. Set with M205 T. // Minimum time that a segment needs to take as the buffer gets emptied #define DEFAULT_MINSEGMENTTIME 20000 // (µs) Set with M205 B. @@ -1332,22 +1363,20 @@ //#define CALIBRATION_SCRIPT_PRE "M117 Starting Auto-Calibration\nT0\nG28\nG12\nM117 Calibrating..." //#define CALIBRATION_SCRIPT_POST "M500\nM117 Calibration data saved" - #define CALIBRATION_MEASUREMENT_RESOLUTION 0.01 // mm - - #define CALIBRATION_FEEDRATE_SLOW 60 // mm/min - #define CALIBRATION_FEEDRATE_FAST 1200 // mm/min - #define CALIBRATION_FEEDRATE_TRAVEL 3000 // mm/min + #define CALIBRATION_FEEDRATE_SLOW 60 // (mm/min) + #define CALIBRATION_FEEDRATE_FAST 1200 // (mm/min) + #define CALIBRATION_FEEDRATE_TRAVEL 3000 // (mm/min) // The following parameters refer to the conical section of the nozzle tip. - #define CALIBRATION_NOZZLE_TIP_HEIGHT 1.0 // mm - #define CALIBRATION_NOZZLE_OUTER_DIAMETER 2.0 // mm + #define CALIBRATION_NOZZLE_TIP_HEIGHT 1.0 // (mm) + #define CALIBRATION_NOZZLE_OUTER_DIAMETER 2.0 // (mm) // Uncomment to enable reporting (required for "G425 V", but consumes flash). //#define CALIBRATION_REPORTING // The true location and dimension the cube/bolt/washer on the bed. - #define CALIBRATION_OBJECT_CENTER { 264.0, -22.0, -2.0 } // mm - #define CALIBRATION_OBJECT_DIMENSIONS { 10.0, 10.0, 10.0 } // mm + #define CALIBRATION_OBJECT_CENTER { 264.0, -22.0, -2.0 } // (mm) + #define CALIBRATION_OBJECT_DIMENSIONS { 10.0, 10.0, 10.0 } // (mm) // Comment out any sides which are unreachable by the probe. For best // auto-calibration results, all sides must be reachable. @@ -1386,7 +1415,7 @@ * Multi-stepping sends steps in bursts to reduce MCU usage for high step-rates. * This allows higher feedrates than the MCU could otherwise support. */ -#define MULTISTEPPING_LIMIT 16 //: [1, 2, 4, 8, 16, 32, 64, 128] +#define MULTISTEPPING_LIMIT 16 // :[1, 2, 4, 8, 16, 32, 64, 128] /** * Adaptive Step Smoothing increases the resolution of multi-axis moves, particularly at step frequencies @@ -1411,24 +1440,24 @@ #define MICROSTEP_MODES { 16, 16, 16, 16, 16, 16 } // [1,2,4,8,16] /** - * @section stepper motor current + * @section stepper motor current * - * Some boards have a means of setting the stepper motor current via firmware. + * Some boards have a means of setting the stepper motor current via firmware. * - * The power on motor currents are set by: - * PWM_MOTOR_CURRENT - used by MINIRAMBO & ULTIMAIN_2 - * known compatible chips: A4982 - * DIGIPOT_MOTOR_CURRENT - used by BQ_ZUM_MEGA_3D, RAMBO & SCOOVO_X9H - * known compatible chips: AD5206 - * DAC_MOTOR_CURRENT_DEFAULT - used by PRINTRBOARD_REVF & RIGIDBOARD_V2 - * known compatible chips: MCP4728 - * DIGIPOT_I2C_MOTOR_CURRENTS - used by 5DPRINT, AZTEEG_X3_PRO, AZTEEG_X5_MINI_WIFI, MIGHTYBOARD_REVE - * known compatible chips: MCP4451, MCP4018 + * The power on motor currents are set by: + * PWM_MOTOR_CURRENT - used by MINIRAMBO & ULTIMAIN_2 + * known compatible chips: A4982 + * DIGIPOT_MOTOR_CURRENT - used by BQ_ZUM_MEGA_3D, RAMBO & SCOOVO_X9H + * known compatible chips: AD5206 + * DAC_MOTOR_CURRENT_DEFAULT - used by PRINTRBOARD_REVF & RIGIDBOARD_V2 + * known compatible chips: MCP4728 + * DIGIPOT_I2C_MOTOR_CURRENTS - used by 5DPRINT, AZTEEG_X3_PRO, AZTEEG_X5_MINI_WIFI, MIGHTYBOARD_REVE + * known compatible chips: MCP4451, MCP4018 * - * Motor currents can also be set by M907 - M910 and by the LCD. - * M907 - applies to all. - * M908 - BQ_ZUM_MEGA_3D, RAMBO, PRINTRBOARD_REVF, RIGIDBOARD_V2 & SCOOVO_X9H - * M909, M910 & LCD - only PRINTRBOARD_REVF & RIGIDBOARD_V2 + * Motor currents can also be set by M907 - M910 and by the LCD. + * M907 - applies to all. + * M908 - BQ_ZUM_MEGA_3D, RAMBO, PRINTRBOARD_REVF, RIGIDBOARD_V2 & SCOOVO_X9H + * M909, M910 & LCD - only PRINTRBOARD_REVF & RIGIDBOARD_V2 */ //#define PWM_MOTOR_CURRENT { 1300, 1300, 1250 } // Values in milliamps //#define DIGIPOT_MOTOR_CURRENT { 135,135,135,135,135 } // Values 0-255 (RAMBO 135 = ~0.75A, 185 = ~1A) @@ -1492,8 +1521,17 @@ #define FEEDRATE_CHANGE_BEEP_FREQUENCY 440 #endif +/** + * Probe Offset Wizard + * Add a Probe Z Offset calibration option to the LCD menu. + * Use this helper to get a perfect 'M851 Z' probe offset. + * When launched this powerful wizard: + * - Measures the bed height at the configured position with the probe. + * - Moves the nozzle to the same position for a "paper" measurement. + * - The difference is used to set the probe Z offset. + */ #if HAS_BED_PROBE && ANY(HAS_MARLINUI_MENU, HAS_TFT_LVGL_UI) - //#define PROBE_OFFSET_WIZARD // Add a Probe Z Offset calibration option to the LCD menu + //#define PROBE_OFFSET_WIZARD #if ENABLED(PROBE_OFFSET_WIZARD) /** * Enable to init the Probe Z-Offset when starting the Wizard. @@ -1510,6 +1548,10 @@ #if HAS_MARLINUI_MENU #if HAS_BED_PROBE + + // Show Deploy / Stow Probe options in the Motion menu. + #define PROBE_DEPLOY_STOW_MENU + // Add calibration in the Probe Offsets menu to compensate for X-axis twist. //#define X_AXIS_TWIST_COMPENSATION #if ENABLED(X_AXIS_TWIST_COMPENSATION) @@ -1524,14 +1566,13 @@ #define XATC_Z_OFFSETS { 0, 0, 0 } // Z offsets for X axis sample points #endif - // Show Deploy / Stow Probe options in the Motion menu. - #define PROBE_DEPLOY_STOW_MENU #endif // Include a page of printer information in the LCD Main Menu //#define LCD_INFO_MENU #if ENABLED(LCD_INFO_MENU) //#define LCD_PRINTER_INFO_IS_BOOTSCREEN // Show bootscreen(s) instead of Printer Info pages + //#define BUILD_INFO_MENU_ITEM // Add a menu item to display the build date and time #endif /** @@ -1556,6 +1597,9 @@ // BACK menu items keep the highlight at the top //#define TURBO_BACK_MENU_ITEM + // BACK menu items show "Back" instead of the previous menu name + //#define GENERIC_BACK_MENU_ITEM + // Insert a menu for preheating at the top level to allow for quick access //#define PREHEAT_SHORTCUT_MENU_ITEM @@ -1584,7 +1628,7 @@ #if HAS_MARLINUI_U8GLIB //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of flash. #endif - #if ANY(HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE) + #if ANY(HAS_MARLINUI_U8GLIB, TOUCH_UI_FTDI_EVE, HAS_MARLINUI_HD44780) //#define SHOW_CUSTOM_BOOTSCREEN // Show the bitmap in Marlin/_Bootscreen.h on startup. #endif #endif @@ -1596,6 +1640,10 @@ //#define SOUND_MENU_ITEM // Add a mute option to the LCD menu #define SOUND_ON_DEFAULT // Buzzer/speaker default enabled state + #if ENABLED(U8GLIB_SSD1309) + //#define LCD_DOUBLE_BUFFER // Optimize display updates. Costs ~1K of SRAM. + #endif + #if HAS_WIRED_LCD //#define DOUBLE_LCD_FRAMERATE // Not recommended for slow boards. #endif @@ -1646,11 +1694,12 @@ #endif // HAS_DISPLAY -#if HAS_FEEDRATE_EDIT +// Some displays offer Feedrate / Flow editing. +#if ANY(HAS_MARLINUI_MENU, DWIN_CREALITY_LCD, DWIN_LCD_PROUI, MALYAN_LCD, TOUCH_SCREEN, ULTIPANEL_FEEDMULTIPLY) #define SPEED_EDIT_MIN 10 // (%) Feedrate percentage edit range minimum #define SPEED_EDIT_MAX 999 // (%) Feedrate percentage edit range maximum #endif -#if HAS_FLOW_EDIT +#if ANY(HAS_MARLINUI_MENU, DWIN_CREALITY_LCD, DWIN_LCD_PROUI, MALYAN_LCD, TOUCH_SCREEN) #define FLOW_EDIT_MIN 10 // (%) Flow percentage edit range minimum #define FLOW_EDIT_MAX 999 // (%) Flow percentage edit range maximum #endif @@ -1809,6 +1858,7 @@ #define SDSORT_DYNAMIC_RAM false // Use dynamic allocation (within SD menus). Least expensive option. Set SDSORT_LIMIT before use! #define SDSORT_CACHE_VFATS 2 // Maximum number of 13-byte VFAT entries to use for sorting. // Note: Only affects SCROLL_LONG_FILENAMES with SDSORT_CACHE_NAMES but not SDSORT_DYNAMIC_RAM. + #define SDSORT_QUICK true // Use Quick Sort as a sorting algorithm. Otherwise use Bubble Sort. #endif // Allow international symbols in long filenames. To display correctly, the @@ -1847,7 +1897,7 @@ * * SCLK, MOSI, MISO --> SCLK, MOSI, MISO * INT --> SD_DETECT_PIN [1] - * SS --> SDSS + * SS --> SD_SS_PIN * * [1] On AVR an interrupt-capable pin is best for UHS3 compatibility. */ @@ -1874,7 +1924,7 @@ //#define USE_OTG_USB_HOST #if DISABLED(USE_OTG_USB_HOST) - #define USB_CS_PIN SDSS + #define USB_CS_PIN SD_SS_PIN #define USB_INTR_PIN SD_DETECT_PIN #endif #endif @@ -1911,6 +1961,9 @@ //#define CUSTOM_FIRMWARE_UPLOAD #endif + // "Over-the-air" Firmware Update with M936 - Required to set EEPROM flag + //#define OTA_FIRMWARE_UPDATE + /** * Set this option to one of the following (or the board's defaults apply): * @@ -1933,8 +1986,8 @@ #if ENABLED(MULTI_VOLUME) #define VOLUME_SD_ONBOARD #define VOLUME_USB_FLASH_DRIVE - #define DEFAULT_VOLUME SV_SD_ONBOARD - #define DEFAULT_SHARED_VOLUME SV_USB_FLASH_DRIVE + #define DEFAULT_VOLUME SD_ONBOARD // :[ 'SD_ONBOARD', 'USB_FLASH_DRIVE' ] + #define DEFAULT_SHARED_VOLUME USB_FLASH_DRIVE // :[ 'SD_ONBOARD', 'USB_FLASH_DRIVE' ] #endif #endif // HAS_MEDIA @@ -1985,7 +2038,7 @@ #if IS_U8GLIB_ST7920 // Enable this option and reduce the value to optimize screen updates. // The normal delay is 10µs. Use the lowest value that still gives a reliable display. - //#define DOGM_SPI_DELAY_US 5 + //#define DOGM_SPI_DELAY_US 5 // (µs) Delay after each SPI transfer //#define LIGHTWEIGHT_UI #if ENABLED(LIGHTWEIGHT_UI) @@ -2015,17 +2068,17 @@ //#define STATUS_HEAT_PERCENT // Show heating in a progress bar //#define STATUS_HEAT_POWER // Show heater output power as a vertical bar - // Frivolous Game Options - //#define MARLIN_BRICKOUT - //#define MARLIN_INVADERS - //#define MARLIN_SNAKE - //#define GAMES_EASTER_EGG // Add extra blank lines above the "Games" sub-menu - #endif // HAS_MARLINUI_U8GLIB #if HAS_MARLINUI_U8GLIB || IS_DWIN_MARLINUI #define MENU_HOLLOW_FRAME // Enable to save many cycles by drawing a hollow frame on Menu Screens //#define OVERLAY_GFX_REVERSE // Swap the CW/CCW indicators in the graphics overlay + + // Frivolous Game Options + //#define MARLIN_BRICKOUT + //#define MARLIN_INVADERS + //#define MARLIN_SNAKE + //#define GAMES_EASTER_EGG // Add extra blank lines above the "Games" sub-menu #endif // @@ -2204,7 +2257,7 @@ // Developer menu (accessed by touching "About Printer" copyright text) //#define TOUCH_UI_DEVELOPER_MENU -#endif +#endif // TOUCH_UI_FTDI_EVE // // Classic UI Options @@ -2238,9 +2291,11 @@ // ADC Button Debounce // #if HAS_ADC_BUTTONS - #define ADC_BUTTON_DEBOUNCE_DELAY 16 // Increase if buttons bounce or repeat too fast + #define ADC_BUTTON_DEBOUNCE_DELAY 16 // (count) Increase if buttons bounce or repeat too fast #endif +//#define FAST_BUTTON_POLLING // Poll buttons at ~1kHz on 8-bit AVR. Set to 'false' for slow polling on 32-bit. + // @section safety /** @@ -2279,7 +2334,7 @@ //#define DOUBLECLICK_FOR_Z_BABYSTEPPING // Double-click on the Status Screen for Z Babystepping. #if ENABLED(DOUBLECLICK_FOR_Z_BABYSTEPPING) - #define DOUBLECLICK_MAX_INTERVAL 1250 // Maximum interval between clicks, in milliseconds. + #define DOUBLECLICK_MAX_INTERVAL 1250 // (ms) Maximum interval between clicks. // Note: Extra time may be added to mitigate controller latency. //#define MOVE_Z_WHEN_IDLE // Jump to the move Z menu on double-click when printer is idle. #if ENABLED(MOVE_Z_WHEN_IDLE) @@ -2314,15 +2369,37 @@ * See https://marlinfw.org/docs/features/lin_advance.html for full instructions. */ //#define LIN_ADVANCE -#if ENABLED(LIN_ADVANCE) + +#if ANY(LIN_ADVANCE, FT_MOTION) #if ENABLED(DISTINCT_E_FACTORS) - #define ADVANCE_K { 0.22 } // (mm) Compression length per 1mm/s extruder speed, per extruder + #define ADVANCE_K { 0.22 } // (mm) Compression length per 1mm/s extruder speed, per extruder. Override with 'M900 T K'. #else - #define ADVANCE_K 0.22 // (mm) Compression length applying to all extruders + #define ADVANCE_K 0.22 // (mm) Compression length for all extruders. Override with 'M900 K'. #endif - //#define ADVANCE_K_EXTRA // Add a second linear advance constant, configurable with M900 L. + //#define ADVANCE_K_EXTRA // Add a second linear advance constant, configurable with 'M900 L'. +#endif + +#if ENABLED(LIN_ADVANCE) //#define LA_DEBUG // Print debug information to serial during operation. Disable for production use. //#define EXPERIMENTAL_I2S_LA // Allow I2S_STEPPER_STREAM to be used with LA. Performance degrades as the LA step rate reaches ~20kHz. + + //#define SMOOTH_LIN_ADVANCE // Remove limits on acceleration by gradual increase of nozzle pressure + #if ENABLED(SMOOTH_LIN_ADVANCE) + /** + * ADVANCE_TAU is also the time ahead that the smoother needs to look + * into the planner, so the planner needs to have enough blocks loaded. + * For k=0.04 at 10k acceleration and an "Orbiter 2" extruder it can be as low as 0.0075. + * Adjust by lowering the value until you observe the extruder skipping, then raise slightly. + * Higher k and higher XY acceleration may require larger ADVANCE_TAU to avoid skipping steps. + */ + #if ENABLED(DISTINCT_E_FACTORS) + #define ADVANCE_TAU { 0.02 } // (s) Smoothing time to reduce extruder acceleration, per extruder + #else + #define ADVANCE_TAU 0.02 // (s) Smoothing time to reduce extruder acceleration + #endif + #define SMOOTH_LIN_ADV_HZ 1000 // (Hz) How often to update extruder speed + #define INPUT_SHAPING_E_SYNC // Synchronize the extruder-shaped XY axes (to increase precision) + #endif #endif /** @@ -2330,8 +2407,12 @@ * * Control extrusion rate based on instantaneous extruder velocity. Can be used to correct for * underextrusion at high extruder speeds that are otherwise well-behaved (i.e., not skipping). + * For better results also enable ADAPTIVE_STEP_SMOOTHING. */ //#define NONLINEAR_EXTRUSION +#if ENABLED(NONLINEAR_EXTRUSION) + //#define NONLINEAR_EXTRUSION_DEFAULT_ON // Enable if NLE should be ON by default +#endif // @section leveling @@ -2557,27 +2638,28 @@ //#define MINIMUM_STEPPER_PRE_DIR_DELAY 650 /** - * Minimum stepper driver pulse width (in µs) - * 0 : Smallest possible width the MCU can produce, compatible with TMC2xxx drivers - * 0 : Minimum 500ns for LV8729, adjusted in stepper.h - * 1 : Minimum for A4988 and A5984 stepper drivers - * 2 : Minimum for DRV8825 stepper drivers - * 3 : Minimum for TB6600 stepper drivers - * 30 : Minimum for TB6560 stepper drivers + * Minimum stepper driver pulse width (in ns) + * If undefined, these defaults (from Conditionals-4-adv.h) apply: + * 100 : Minimum for TMC2xxx stepper drivers + * 500 : Minimum for LV8729 + * 1000 : Minimum for A4988 and A5984 stepper drivers + * 2000 : Minimum for DRV8825 stepper drivers + * 3000 : Minimum for TB6600 stepper drivers + * 30000 : Minimum for TB6560 stepper drivers * * Override the default value based on the driver type set in Configuration.h. */ -//#define MINIMUM_STEPPER_PULSE 2 +//#define MINIMUM_STEPPER_PULSE_NS 2000 /** * Maximum stepping rate (in Hz) the stepper driver allows - * If undefined, defaults to 1MHz / (2 * MINIMUM_STEPPER_PULSE) + * If undefined, these defaults (from Conditionals-4-adv.h) apply: * 5000000 : Maximum for TMC2xxx stepper drivers * 1000000 : Maximum for LV8729 stepper driver - * 500000 : Maximum for A4988 stepper driver - * 250000 : Maximum for DRV8825 stepper driver - * 150000 : Maximum for TB6600 stepper driver - * 15000 : Maximum for TB6560 stepper driver + * 500000 : Maximum for A4988 stepper driver + * 250000 : Maximum for DRV8825 stepper driver + * 150000 : Maximum for TB6600 stepper driver + * 15000 : Maximum for TB6560 stepper driver * * Override the default value based on the driver type set in Configuration.h. */ @@ -2609,19 +2691,23 @@ #define MAX_CMD_SIZE 96 #define BUFSIZE 4 -// Transmission to Host Buffer Size -// To save 386 bytes of flash (and TX_BUFFER_SIZE+3 bytes of RAM) set to 0. -// To buffer a simple "ok" you need 4 bytes. -// For ADVANCED_OK (M105) you need 32 bytes. -// For debug-echo: 128 bytes for the optimal speed. -// Other output doesn't need to be that speedy. -// :[0, 2, 4, 8, 16, 32, 64, 128, 256] +/** + * Host Transmit Buffer Size + * - Costs 386 bytes of flash and TX_BUFFER_SIZE+3 bytes of SRAM (if not 0). + * - 4 bytes required to buffer a simple "ok". + * - 32 bytes for ADVANCED_OK (M105). + * - 128 bytes for the optimal speed of 'debug-echo:' + * - Other output doesn't need to be that speedy. + * :[0, 2, 4, 8, 16, 32, 64, 128, 256] + */ #define TX_BUFFER_SIZE 0 -// Host Receive Buffer Size -// Without XON/XOFF flow control (see SERIAL_XON_XOFF below) 32 bytes should be enough. -// To use flow control, set this buffer size to at least 1024 bytes. -// :[0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048] +/** + * Host Receive Buffer Size + * Without XON/XOFF flow control (see SERIAL_XON_XOFF below) 32 bytes should be enough. + * To use flow control, set this buffer size to at least 1024 bytes. + * :[0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048] + */ //#define RX_BUFFER_SIZE 1024 #if RX_BUFFER_SIZE >= 1024 @@ -2698,14 +2784,14 @@ * This feature is EXPERIMENTAL so use with caution and test thoroughly. * Enable this option to receive data on the serial ports via the onboard DMA * controller for more stable and reliable high-speed serial communication. - * Only some STM32 MCUs are currently supported. + * Support is currently limited to some STM32 MCUs and all HC32 MCUs. * Note: This has no effect on emulated USB serial ports. */ //#define SERIAL_DMA /** * Set the number of proportional font spaces required to fill up a typical character space. - * This can help to better align the output of commands like `G29 O` Mesh Output. + * This can help to better align the output of commands like 'G29 O' Mesh Output. * * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0. * Otherwise, adjust according to your client and font. @@ -2940,7 +3026,7 @@ /** * Trinamic Smart Drivers * - * To use TMC2130, TMC2160, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode: + * To use TMC2130, TMC2160, TMC2240, TMC2660, TMC5130, TMC5160 stepper drivers in SPI mode: * - Connect your SPI pins to the Hardware SPI interface on the board. * Some boards have simple jumper connections! See your board's documentation. * - Define the required Stepper CS pins in your `pins_MYBOARD.h` file. @@ -2962,15 +3048,25 @@ #define HOLD_MULTIPLIER 0.5 // Scales down the holding current from run current + //#define EDITABLE_HOMING_CURRENT // Add a G-code and menu to modify the Homing Current + /** * Interpolate microsteps to 256 * Override for each driver with _INTERPOLATE settings below */ #define INTERPOLATE true + #if HAS_DRIVER(TMC2240) + #define TMC2240_RREF 12000 // (Ω) 12000 .. 60000. (FLY TMC2240 = 12300) + // Max Current. Lower for more internal resolution. Raise to run cooler. + #define TMC2240_CURRENT_RANGE 1 // :{ 0:'RMS=690mA PEAK=1A', 1:'RMS=1410mA PEAK=2A', 2:'RMS=2120mA PEAK=3A', 3:'RMS=2110mA PEAK=3A' } + // Slope Control: Lower is more silent. Higher runs cooler. + #define TMC2240_SLOPE_CONTROL 0 // :{ 0:'100V/µs', 1:'200V/µs', 2:'400V/µs', 3:'800V/µs' } + #endif + #if AXIS_IS_TMC_CONFIG(X) #define X_CURRENT 800 // (mA) RMS current. Multiply by 1.414 for peak current. - #define X_CURRENT_HOME X_CURRENT // (mA) RMS current for sensorless homing + #define X_CURRENT_HOME X_CURRENT // (mA) RMS current for homing. (Typically lower than *_CURRENT.) #define X_MICROSTEPS 16 // 0..256 #define X_RSENSE 0.11 #define X_CHAIN_POS -1 // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ... @@ -3180,10 +3276,17 @@ //#define E7_HOLD_MULTIPLIER 0.5 #endif + /** + * Use the homing current for all probing. (e.g., Current may be reduced to the + * point where a collision makes the motor skip instead of damaging the bed, + * though this is unlikely to save delicate probes from being damaged. + */ + //#define PROBING_USE_CURRENT_HOME + // @section tmc/spi /** - * Override default SPI pins for TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160 drivers here. + * Override default SPI pins for TMC2130, TMC2160, TMC2240, TMC2660, TMC5130 and TMC5160 drivers here. * The default pins can be found in your board's pins file. */ //#define X_CS_PIN -1 @@ -3210,7 +3313,7 @@ //#define E7_CS_PIN -1 /** - * Software option for SPI driven drivers (TMC2130, TMC2160, TMC2660, TMC5130 and TMC5160). + * Software option for SPI driven drivers (TMC2130, TMC2160, TMC2240, TMC2660, TMC5130 and TMC5160). * The default SW SPI pins are defined the respective pins files, * but you can override or define them here. */ @@ -3269,7 +3372,7 @@ // @section tmc/stealthchop /** - * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only + * TMC2130, TMC2160, TMC2208, TMC2209, TMC2240, TMC5130 and TMC5160 only * Use Trinamic's ultra quiet stepping mode. * When disabled, Marlin will use spreadCycle stepping mode. */ @@ -3348,7 +3451,7 @@ // @section tmc/hybrid /** - * TMC2130, TMC2160, TMC2208, TMC2209, TMC5130 and TMC5160 only + * TMC2130, TMC2160, TMC2208, TMC2209, TMC2240, TMC5130 and TMC5160 only * The driver will switch to spreadCycle when stepper speed is over HYBRID_THRESHOLD. * This mode allows for faster movements at the expense of higher noise levels. * STEALTHCHOP_(XY|Z|E) must be enabled to use HYBRID_THRESHOLD. @@ -3382,20 +3485,20 @@ /** * Use StallGuard to home / probe X, Y, Z. * - * TMC2130, TMC2160, TMC2209, TMC2660, TMC5130, and TMC5160 only + * TMC2130, TMC2160, TMC2209, TMC2240, TMC2660, TMC5130, and TMC5160 only * Connect the stepper driver's DIAG1 pin to the X/Y endstop pin. * X, Y, and Z homing will always be done in spreadCycle mode. * * X/Y/Z_STALL_SENSITIVITY is the default stall threshold. * Use M914 X Y Z to set the stall threshold at runtime: * - * Sensitivity TMC2209 Others - * HIGHEST 255 -64 (Too sensitive => False positive) - * LOWEST 0 63 (Too insensitive => No trigger) + * Sensitivity TMC2209 Others + * HIGHEST 255 -64 (Too sensitive => False positive) + * LOWEST 0 63 (Too insensitive => No trigger) * * It is recommended to set HOMING_BUMP_MM to { 0, 0, 0 }. * - * SPI_ENDSTOPS *** TMC2130/TMC5160 Only *** + * SPI_ENDSTOPS *** TMC2130, TMC2240, and TMC5160 Only *** * Poll the driver through SPI to determine load when homing. * Removes the need for a wire from DIAG1 to an endstop pin. * @@ -3423,8 +3526,9 @@ //#define U_STALL_SENSITIVITY 8 //#define V_STALL_SENSITIVITY 8 //#define W_STALL_SENSITIVITY 8 - //#define SPI_ENDSTOPS // TMC2130/TMC5160 only + //#define SPI_ENDSTOPS // TMC2130, TMC2240, and TMC5160 //#define IMPROVE_HOMING_RELIABILITY + //#define SENSORLESS_STALLGUARD_DELAY 0 // (ms) Delay to allow drivers to settle #endif // @section tmc/config @@ -3444,7 +3548,7 @@ /** * Step on both rising and falling edge signals (as with a square wave). */ - //#define EDGE_STEPPING + #define EDGE_STEPPING /** * Enable M122 debugging command for TMC stepper drivers. @@ -3526,7 +3630,7 @@ //#define PHOTOGRAPH_PIN 23 // Canon Hack Development Kit - // https://web.archive.org/web/20200920094805/https://captain-slow.dk/2014/03/09/3d-printing-timelapses/ + // https://web.archive.org/web/20200920094805/captain-slow.dk/2014/03/09/3d-printing-timelapses/ //#define CHDK_PIN 4 // Optional second move with delay to trigger the camera shutter @@ -3640,6 +3744,8 @@ #define SPEED_POWER_MIN 5000 // (RPM) #define SPEED_POWER_MAX 30000 // (RPM) SuperPID router controller 0 - 30,000 RPM #define SPEED_POWER_STARTUP 25000 // (RPM) M3/M4 speed/power default (with no arguments) + + //#define DEFAULT_ACCELERATION_SPINDLE 1000 // (°/s/s) Default spindle acceleration (speed change with time) #endif #else @@ -3881,7 +3987,7 @@ /** * Extra options for the M114 "Current Position" report */ -//#define M114_DETAIL // Use 'M114` for details to check planner calculations +//#define M114_DETAIL // Use 'M114 D' for details to check planner calculations //#define M114_REALTIME // Real current position based on forward kinematics //#define M114_LEGACY // M114 used to synchronize on every call. Enable if needed. @@ -3910,7 +4016,7 @@ #endif /** - * M115 - Report capabilites. Disable to save ~1150 bytes of flash. + * M115 - Report capabilities. Disable to save ~1150 bytes of flash. * Some hosts (and serial TFT displays) rely on this feature. */ #define CAPABILITIES_REPORT @@ -3928,7 +4034,6 @@ * Spend 28 bytes of SRAM to optimize the G-code parser */ #define FASTER_GCODE_PARSER - #if ENABLED(FASTER_GCODE_PARSER) //#define GCODE_QUOTED_STRINGS // Support for quoted string parameters #endif @@ -4166,7 +4271,7 @@ //#define I2CPE_ENC_1_TICKS_REV (16 * 200) // Only needed for rotary encoders; number of stepper // steps per full revolution (motor steps/rev * microstepping) //#define I2CPE_ENC_1_INVERT // Invert the direction of axis travel. - #define I2CPE_ENC_1_EC_METHOD I2CPE_ECM_MICROSTEP // Type of error error correction. + #define I2CPE_ENC_1_EC_METHOD I2CPE_ECM_MICROSTEP // Type of error correction. #define I2CPE_ENC_1_EC_THRESH 0.10 // Threshold size for error (in mm) above which the // printer will attempt to correct the error; errors // smaller than this are ignored to minimize effects of @@ -4261,7 +4366,8 @@ /** * Instant freeze / unfreeze functionality - * Potentially useful for emergency stop that allows being resumed. + * Potentially useful for rapid stop that allows being resumed. Halts stepper movement. + * Note this does NOT pause spindles, lasers, fans, heaters or any other auxiliary device. * @section interface */ //#define FREEZE_FEATURE @@ -4342,7 +4448,7 @@ * Extras for an ESP32-based motherboard with WIFISUPPORT * These options don't apply to add-on WiFi modules based on ESP32 WiFi101. */ -#if ENABLED(WIFISUPPORT) +#if ANY(WIFISUPPORT, ESP3D_WIFISUPPORT) //#define WEBSUPPORT // Start a webserver (which may include auto-discovery) using SPIFFS //#define OTASUPPORT // Support over-the-air firmware updates //#define WIFI_CUSTOM_COMMAND // Accept feature config commands (e.g., WiFi ESP3D) from the host @@ -4378,58 +4484,81 @@ //#define E_MUX0_PIN 40 // Always Required //#define E_MUX1_PIN 42 // Needed for 3 to 8 inputs //#define E_MUX2_PIN 44 // Needed for 5 to 8 inputs -#elif HAS_PRUSA_MMU2 - // Serial port used for communication with MMU2. - #define MMU2_SERIAL_PORT 2 - // Use hardware reset for MMU if a pin is defined for it - //#define MMU2_RST_PIN 23 +#elif HAS_PRUSA_MMU2 || HAS_PRUSA_MMU3 + // Common settings for MMU2/MMU2S/MMU3 + // Serial port used for communication with MMU2/MMU2S/MMU3. + #define MMU_SERIAL_PORT 2 + #define MMU_BAUD 115200 - // Enable if the MMU2 has 12V stepper motors (MMU2 Firmware 1.0.2 and up) - //#define MMU2_MODE_12V + //#define MMU_RST_PIN 23 // Define this pin to use Hardware Reset for MMU2/MMU2S/MMU3 - // G-code to execute when MMU2 F.I.N.D.A. probe detects filament runout - #define MMU2_FILAMENT_RUNOUT_SCRIPT "M600" + //#define MMU_MENUS // Add an LCD menu for MMU2/MMU2S/MMU3 - // Add an LCD menu for MMU2 - //#define MMU2_MENUS + //#define MMU_DEBUG // Write debug info to serial output - // Settings for filament load / unload from the LCD menu. - // This is for Průša MK3-style extruders. Customize for your hardware. - #define MMU2_FILAMENTCHANGE_EJECT_FEED 80.0 - #define MMU2_LOAD_TO_NOZZLE_SEQUENCE \ - { 7.2, 1145 }, \ - { 14.4, 871 }, \ - { 36.0, 1393 }, \ - { 14.4, 871 }, \ - { 50.0, 198 } + // Options pertaining to MMU2 and MMU2S + #if HAS_PRUSA_MMU2 + // Enable if the MMU2 has 12V stepper motors (MMU2 Firmware 1.0.2 and up) + //#define MMU2_MODE_12V - #define MMU2_RAMMING_SEQUENCE \ - { 1.0, 1000 }, \ - { 1.0, 1500 }, \ - { 2.0, 2000 }, \ - { 1.5, 3000 }, \ - { 2.5, 4000 }, \ - { -15.0, 5000 }, \ - { -14.0, 1200 }, \ - { -6.0, 600 }, \ - { 10.0, 700 }, \ - { -10.0, 400 }, \ - { -50.0, 2000 } + // Settings for filament load / unload from the LCD menu. + // This is for Průša MK3-style extruders. Customize for your hardware. + #define MMU2_FILAMENTCHANGE_EJECT_FEED 80.0 + + // G-code to execute when MMU2 F.I.N.D.A. probe detects filament runout + #define MMU2_FILAMENT_RUNOUT_SCRIPT "M600" + + // MMU2 sequences use mm/min. Not compatible with MMU3, which use mm/sec. + #define MMU2_LOAD_TO_NOZZLE_SEQUENCE \ + { 4.4, 871 }, \ + { 10.0, 1393 }, \ + { 4.4, 871 }, \ + { 10.0, 198 } + + #define MMU2_RAMMING_SEQUENCE \ + { 1.0, 1000 }, \ + { 1.0, 1500 }, \ + { 2.0, 2000 }, \ + { 1.5, 3000 }, \ + { 2.5, 4000 }, \ + { -15.0, 5000 }, \ + { -14.0, 1200 }, \ + { -6.0, 600 }, \ + { 10.0, 700 }, \ + { -10.0, 400 }, \ + { -50.0, 2000 } + + #endif // HAS_PRUSA_MMU2 /** - * Using a sensor like the MMU2S - * This mode requires a MK3S extruder with a sensor at the extruder idler, like the MMU2S. + * Options pertaining to MMU2S devices + * Requires the MK3S extruder with a sensor at the extruder idler, like the MMU2S. * See https://help.prusa3d.com/guide/3b-mk3s-mk2-5s-extruder-upgrade_41560#42048, step 11 */ #if HAS_PRUSA_MMU2S #define MMU2_C0_RETRY 5 // Number of retries (total time = timeout*retries) + /** + * This is called after the filament runout sensor is triggered to check if + * the filament has been loaded properly by moving the filament back and + * forth to see if the filament runout sensor is going to get triggered + * again, which should not occur if the filament is properly loaded. + * + * Thus, the MMU2_CAN_LOAD_SEQUENCE should contain some forward and + * backward moves. The forward moves should be greater than the backward + * moves. + * + * This is useless if your filament runout sensor is way behind the gears. + * In that case use {0, MMU2_CAN_LOAD_FEEDRATE} + * + * Adjust MMU2_CAN_LOAD_SEQUENCE according to your setup. + */ #define MMU2_CAN_LOAD_FEEDRATE 800 // (mm/min) #define MMU2_CAN_LOAD_SEQUENCE \ - { 0.1, MMU2_CAN_LOAD_FEEDRATE }, \ - { 60.0, MMU2_CAN_LOAD_FEEDRATE }, \ - { -52.0, MMU2_CAN_LOAD_FEEDRATE } + { 5.0, MMU2_CAN_LOAD_FEEDRATE }, \ + { 15.0, MMU2_CAN_LOAD_FEEDRATE }, \ + { -10.0, MMU2_CAN_LOAD_FEEDRATE } #define MMU2_CAN_LOAD_RETRACT 6.0 // (mm) Keep under the distance between Load Sequence values #define MMU2_CAN_LOAD_DEVIATION 0.8 // (mm) Acceptable deviation @@ -4440,10 +4569,90 @@ // Continue unloading if sensor detects filament after the initial unload move //#define MMU_IR_UNLOAD_MOVE - #else + + #elif HAS_PRUSA_MMU3 + + // MMU3 settings + + #define MMU3_HAS_CUTTER // Enable cutter related functionality + + #define MMU3_MAX_RETRIES 3 // Number of retries (total time = timeout*retries) + + // As discussed with our PrusaSlicer profile specialist + // - ToolChange shall not try to push filament into the very tip of the nozzle + // to have some space for additional G-code to tune the extruded filament length + // in the profile + // Beware - this value is used to initialize the MMU logic layer - it will be sent to the MMU upon line up (written into its 8bit register 0x0b) + // However - in the G-code we can get a request to set the extra load distance at runtime to something else (M708 A0xb Xsomething). + // The printer intercepts such a call and sets its extra load distance to match the new value as well. + #define MMU3_FILAMENT_SENSOR_E_POSITION 0 // (mm) + #define _MMU3_LOAD_DISTANCE_PAST_GEARS 5 // (mm) + #define MMU3_TOOL_CHANGE_LOAD_LENGTH (MMU3_FILAMENT_SENSOR_E_POSITION + _MMU3_LOAD_DISTANCE_PAST_GEARS) // (mm) + + #define MMU3_LOAD_TO_NOZZLE_FEED_RATE 20.0 // (mm/s) + + #define MMU3_VERIFY_LOAD_TO_NOZZLE_FEED_RATE 50.0 // (mm/s) + #define _MMU3_VERIFY_LOAD_TO_NOZZLE_TWEAK -5.0 // (mm) Amount to adjust the length for verifying load-to-nozzle + + // The first thing the MMU does is initialize its axis. + // Meanwhile the E-motor will unload 20mm of filament in about 1 second. + #define MMU3_RETRY_UNLOAD_TO_FINDA_LENGTH 80.0 // (mm) + #define MMU3_RETRY_UNLOAD_TO_FINDA_FEED_RATE 80.0 // (mm/s) + + // After loading a new filament, the printer will extrude this length of filament + // then retract to the original position. This is used to check if the filament sensor + // reading flickers or filament is jammed. + #define _MMU_EXTRUDER_PTFE_LENGTH 42.3 // (mm) + #define _MMU_EXTRUDER_HEATBREAK_LENGTH 17.7 // (mm) + #define MMU3_CHECK_FILAMENT_PRESENCE_EXTRUSION_LENGTH (MMU3_FILAMENT_SENSOR_E_POSITION + _MMU_EXTRUDER_PTFE_LENGTH + _MMU_EXTRUDER_HEATBREAK_LENGTH + _MMU3_VERIFY_LOAD_TO_NOZZLE_TWEAK) // (mm) /** - * MMU1 Extruder Sensor + * SpoolJoin Consumes All Filament -- EXPERIMENTAL + * + * SpoolJoin normally triggers when FINDA sensor untriggers while printing. + * This is the default behaviour and it doesn't consume all the filament + * before triggering a filament change. This leaves some filament in the + * current slot and before switching to the next slot it is unloaded. + * + * Enabling this option will trigger the filament change when both FINDA + * and Filament Runout Sensor triggers during the print and it allows the + * filament in the current slot to be completely consumed before doing the + * filament change. But this can cause problems as a little bit of filament + * will be left between the extruder gears (thinking that the filament + * sensor is triggered through the gears) and the end of the PTFE tube and + * can cause filament load issues. + */ + //#define MMU3_SPOOL_JOIN_CONSUMES_ALL_FILAMENT + + // MMU3 sequences use mm/sec. Not compatible with MMU2 which use mm/min. + #define MMU3_LOAD_TO_NOZZLE_SEQUENCE \ + { _MMU_EXTRUDER_PTFE_LENGTH, MMM_TO_MMS(810) }, /* (13.5 mm/s) Fast load ahead of heatbreak */ \ + { _MMU_EXTRUDER_HEATBREAK_LENGTH, MMM_TO_MMS(198) } /* ( 3.3 mm/s) Slow load after heatbreak */ + + #define MMU3_RAMMING_SEQUENCE \ + { 0.2816, MMM_TO_MMS(1339.0) }, \ + { 0.3051, MMM_TO_MMS(1451.0) }, \ + { 0.3453, MMM_TO_MMS(1642.0) }, \ + { 0.3990, MMM_TO_MMS(1897.0) }, \ + { 0.4761, MMM_TO_MMS(2264.0) }, \ + { 0.5767, MMM_TO_MMS(2742.0) }, \ + { 0.5691, MMM_TO_MMS(3220.0) }, \ + { 0.1081, MMM_TO_MMS(3220.0) }, \ + { 0.7644, MMM_TO_MMS(3635.0) }, \ + { 0.8248, MMM_TO_MMS(3921.0) }, \ + { 0.8483, MMM_TO_MMS(4033.0) }, \ + { -15.0, MMM_TO_MMS(6000.0) }, \ + { -24.5, MMM_TO_MMS(1200.0) }, \ + { -7.0, MMM_TO_MMS( 600.0) }, \ + { -3.5, MMM_TO_MMS( 360.0) }, \ + { 20.0, MMM_TO_MMS( 454.0) }, \ + { -20.0, MMM_TO_MMS( 303.0) }, \ + { -35.0, MMM_TO_MMS(2000.0) } + + #else // MMU2 (not MMU2S) + + /** + * MMU2 Extruder Sensor * * Support for a Průša (or other) IR Sensor to detect filament near the extruder * and make loading more reliable. Suitable for an extruder equipped with a filament @@ -4453,16 +4662,14 @@ * move up to the gears. If no filament is detected, the MMU2 can make some more attempts. * If all attempts fail, a filament runout will be triggered. */ - //#define MMU_EXTRUDER_SENSOR - #if ENABLED(MMU_EXTRUDER_SENSOR) - #define MMU_LOADING_ATTEMPTS_NR 5 // max. number of attempts to load filament if first load fail + //#define MMU2_EXTRUDER_SENSOR + #if ENABLED(MMU2_EXTRUDER_SENSOR) + #define MMU2_LOADING_ATTEMPTS_NR 5 // Number of times to try loading filament before failure #endif #endif - //#define MMU2_DEBUG // Write debug info to serial output - -#endif // HAS_PRUSA_MMU2 +#endif // HAS_PRUSA_MMU2 || HAS_PRUSA_MMU3 /** * Advanced Print Counter settings @@ -4496,6 +4703,11 @@ // //#define PINS_DEBUGGING +// +// M265 - I2C Scanner +// +//#define I2C_SCANNER + // Enable Tests that will run at startup and produce a report //#define MARLIN_TEST_BUILD diff --git a/Marlin/Makefile b/Marlin/Makefile index e5ba9cb341..aed2506ac8 100644 --- a/Marlin/Makefile +++ b/Marlin/Makefile @@ -127,9 +127,9 @@ NEOPIXEL ?= 0 # on GCC versions: # https://www.avrfreaks.net/comment/1789106#comment-1789106 -CC_MAJ:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC__ | cut -f3 -d\ ) -CC_MIN:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_MINOR__ | cut -f3 -d\ ) -CC_PATCHLEVEL:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_PATCHLEVEL__ | cut -f3 -d\ ) +CC_MAJ:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC__ | cut -f3 -d' ' ) +CC_MIN:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_MINOR__ | cut -f3 -d' ' ) +CC_PATCHLEVEL:=$(shell $(CC) -dM -E - < /dev/null | grep __GNUC_PATCHLEVEL__ | cut -f3 -d' ' ) CC_VER:=$(shell echo $$(( $(CC_MAJ) * 10000 + $(CC_MIN) * 100 + $(CC_PATCHLEVEL) ))) ifeq ($(shell test $(CC_VER) -lt 40901 && echo 1),1) $(warning This GCC version $(CC_VER) is likely broken. Enabling relocation workaround.) @@ -187,6 +187,17 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1033) # RAMPS Plus 3DYMY (Power outputs: Spindle, Controller Fan) else ifeq ($(HARDWARE_MOTHERBOARD),1034) +# RAMPS 1.6+ (Power outputs: Hotend, Fan, Bed) +else ifeq ($(HARDWARE_MOTHERBOARD),1040) +# RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Bed) +else ifeq ($(HARDWARE_MOTHERBOARD),1041) +# RAMPS 1.6+ (Power outputs: Hotend, Fan0, Fan1) +else ifeq ($(HARDWARE_MOTHERBOARD),1042) +# RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Fan) +else ifeq ($(HARDWARE_MOTHERBOARD),1043) +# RAMPS 1.6+ (Power outputs: Spindle, Controller Fan) +else ifeq ($(HARDWARE_MOTHERBOARD),1044) + # # RAMPS Derivatives - ATmega1280, ATmega2560 # @@ -221,79 +232,79 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1112) else ifeq ($(HARDWARE_MOTHERBOARD),1113) # BigTreeTech or BIQU KFB2.0 else ifeq ($(HARDWARE_MOTHERBOARD),1114) -# zrib V2.0 (Chinese RAMPS replica) +# Zonestar zrib V2.0 (Chinese RAMPS replica) else ifeq ($(HARDWARE_MOTHERBOARD),1115) -# zrib V5.2 (Chinese RAMPS replica) +# Zonestar zrib V5.2 (Chinese RAMPS replica) else ifeq ($(HARDWARE_MOTHERBOARD),1116) -# Felix 2.0+ Electronics Board (RAMPS like) +# Zonestar zrib V5.3 (Chinese RAMPS replica) else ifeq ($(HARDWARE_MOTHERBOARD),1117) -# Invent-A-Part RigidBoard +# Felix 2.0+ Electronics Board (RAMPS like) else ifeq ($(HARDWARE_MOTHERBOARD),1118) -# Invent-A-Part RigidBoard V2 +# Invent-A-Part RigidBoard else ifeq ($(HARDWARE_MOTHERBOARD),1119) -# Sainsmart 2-in-1 board +# Invent-A-Part RigidBoard V2 else ifeq ($(HARDWARE_MOTHERBOARD),1120) -# Ultimaker +# Sainsmart 2-in-1 board else ifeq ($(HARDWARE_MOTHERBOARD),1121) -# Ultimaker (Older electronics. Pre 1.5.4. This is rare) +# Ultimaker else ifeq ($(HARDWARE_MOTHERBOARD),1122) +# Ultimaker (Older electronics. Pre 1.5.4. This is rare) +else ifeq ($(HARDWARE_MOTHERBOARD),1123) MCU ?= atmega1280 PROG_MCU ?= m1280 # Azteeg X3 -else ifeq ($(HARDWARE_MOTHERBOARD),1123) -# Azteeg X3 Pro else ifeq ($(HARDWARE_MOTHERBOARD),1124) -# Ultimainboard 2.x (Uses TEMP_SENSOR 20) +# Azteeg X3 Pro else ifeq ($(HARDWARE_MOTHERBOARD),1125) -# Rumba +# Ultimainboard 2.x (Uses TEMP_SENSOR 20) else ifeq ($(HARDWARE_MOTHERBOARD),1126) -# Raise3D N series Rumba derivative +# Rumba else ifeq ($(HARDWARE_MOTHERBOARD),1127) -# Rapide Lite 200 (v1, low-cost RUMBA clone with drv) +# Raise3D N series Rumba derivative else ifeq ($(HARDWARE_MOTHERBOARD),1128) -# Formbot T-Rex 2 Plus +# Rapide Lite 200 (v1, low-cost RUMBA clone with drv) else ifeq ($(HARDWARE_MOTHERBOARD),1129) -# Formbot T-Rex 3 +# Formbot T-Rex 2 Plus else ifeq ($(HARDWARE_MOTHERBOARD),1130) -# Formbot Raptor +# Formbot T-Rex 3 else ifeq ($(HARDWARE_MOTHERBOARD),1131) -# Formbot Raptor 2 +# Formbot Raptor else ifeq ($(HARDWARE_MOTHERBOARD),1132) -# bq ZUM Mega 3D +# Formbot Raptor 2 else ifeq ($(HARDWARE_MOTHERBOARD),1133) -# MakeBoard Mini v2.1.2 by MicroMake +# bq ZUM Mega 3D else ifeq ($(HARDWARE_MOTHERBOARD),1134) -# TriGorilla Anycubic version 1.3-based on RAMPS EFB +# MakeBoard Mini v2.1.2 by MicroMake else ifeq ($(HARDWARE_MOTHERBOARD),1135) -# ... Ver 1.4 +# TriGorilla Anycubic version 1.3-based on RAMPS EFB else ifeq ($(HARDWARE_MOTHERBOARD),1136) -# ... Rev 1.1 (new servo pin order) +# ... Ver 1.4 else ifeq ($(HARDWARE_MOTHERBOARD),1137) -# Creality: Ender-4, CR-8 +# ... Rev 1.1 (new servo pin order) else ifeq ($(HARDWARE_MOTHERBOARD),1138) -# Creality: CR10S, CR20, CR-X +# Creality: Ender-4, CR-8 else ifeq ($(HARDWARE_MOTHERBOARD),1139) -# Dagoma F5 +# Creality: CR10S, CR20, CR-X else ifeq ($(HARDWARE_MOTHERBOARD),1140) -# FYSETC F6 1.3 +# Creality CR-10 V2, CR-10 V3 else ifeq ($(HARDWARE_MOTHERBOARD),1141) -# FYSETC F6 1.4 +# Dagoma F5 else ifeq ($(HARDWARE_MOTHERBOARD),1142) -# Wanhao Duplicator i3 Plus +# Dagoma D6 (as found in the Dagoma DiscoUltimate V2 TMC) else ifeq ($(HARDWARE_MOTHERBOARD),1143) -# VORON Design +# FYSETC F6 1.3 else ifeq ($(HARDWARE_MOTHERBOARD),1144) -# Tronxy TRONXY-V3-1.0 +# FYSETC F6 1.4 else ifeq ($(HARDWARE_MOTHERBOARD),1145) -# Z-Bolt X Series +# Wanhao Duplicator i3 Plus else ifeq ($(HARDWARE_MOTHERBOARD),1146) -# TT OSCAR +# VORON Design else ifeq ($(HARDWARE_MOTHERBOARD),1147) -# Overlord/Overlord Pro +# Tronxy TRONXY-V3-1.0 else ifeq ($(HARDWARE_MOTHERBOARD),1148) -# ADIMLab Gantry v1 +# Z-Bolt X Series else ifeq ($(HARDWARE_MOTHERBOARD),1149) -# ADIMLab Gantry v2 +# TT OSCAR else ifeq ($(HARDWARE_MOTHERBOARD),1150) # BIQU Tango V1 else ifeq ($(HARDWARE_MOTHERBOARD),1151) @@ -307,7 +318,7 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1154) else ifeq ($(HARDWARE_MOTHERBOARD),1155) # Tenlog D3 Hero IDEX printer else ifeq ($(HARDWARE_MOTHERBOARD),1156) -# Tenlog D3,5,6 Pro IDEX printers +# Tenlog D3, D5, D6 IDEX Printer else ifeq ($(HARDWARE_MOTHERBOARD),1157) # Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Fan, Bed) else ifeq ($(HARDWARE_MOTHERBOARD),1158) @@ -319,10 +330,18 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1160) else ifeq ($(HARDWARE_MOTHERBOARD),1161) # Longer LKx PRO / Alfawise Uxx Pro (PRO version) else ifeq ($(HARDWARE_MOTHERBOARD),1162) -# Zonestar zrib V5.3 (Chinese RAMPS replica) -else ifeq ($(HARDWARE_MOTHERBOARD),1163) # Pxmalion Core I3 +else ifeq ($(HARDWARE_MOTHERBOARD),1163) +# Panowin Cutlass (as found in the Panowin F1) else ifeq ($(HARDWARE_MOTHERBOARD),1164) +# Kodama Bardo V1.x (as found in the Kodama Trinus) +else ifeq ($(HARDWARE_MOTHERBOARD),1165) +# XTLW MFF V1.0 +else ifeq ($(HARDWARE_MOTHERBOARD),1166) +# XTLW MFF V2.0 +else ifeq ($(HARDWARE_MOTHERBOARD),1167) +# E3D Rumba BigBox +else ifeq ($(HARDWARE_MOTHERBOARD),1168) # # RAMBo and derivatives @@ -340,7 +359,7 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1203) else ifeq ($(HARDWARE_MOTHERBOARD),1204) # abee Scoovo X9H else ifeq ($(HARDWARE_MOTHERBOARD),1205) -# Rambo ThinkerV2 +# ThinkerV2 else ifeq ($(HARDWARE_MOTHERBOARD),1206) # @@ -383,30 +402,42 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1315) else ifeq ($(HARDWARE_MOTHERBOARD),1316) # Geeetech GT2560 Rev B for A10(M/T/D) else ifeq ($(HARDWARE_MOTHERBOARD),1317) -# Geeetech GT2560 Rev B for A10(M/T/D) -else ifeq ($(HARDWARE_MOTHERBOARD),1318) # Geeetech GT2560 Rev B for Mecreator2 +else ifeq ($(HARDWARE_MOTHERBOARD),1318) +# Geeetech GT2560 Rev B for A20(M/T/D) else ifeq ($(HARDWARE_MOTHERBOARD),1319) -# Geeetech GT2560 Rev B for A20(M/T/D) +# Geeetech GT2560 Rev B for A10(M/T/D) else ifeq ($(HARDWARE_MOTHERBOARD),1320) -# Einstart retrofit -else ifeq ($(HARDWARE_MOTHERBOARD),1321) -# Wanhao 0ne+ i3 Mini -else ifeq ($(HARDWARE_MOTHERBOARD),1322) -# Leapfrog Xeed 2015 -else ifeq ($(HARDWARE_MOTHERBOARD),1323) -# PICA Shield (original version) -else ifeq ($(HARDWARE_MOTHERBOARD),1324) -# PICA Shield (rev C or later) -else ifeq ($(HARDWARE_MOTHERBOARD),1325) -# Intamsys 4.0 (Funmat HT) -else ifeq ($(HARDWARE_MOTHERBOARD),1326) -# Malyan M180 Mainboard Version 2 (no display function, direct G-code only) -else ifeq ($(HARDWARE_MOTHERBOARD),1327) # Geeetech GT2560 Rev B for A20(M/T/D) +else ifeq ($(HARDWARE_MOTHERBOARD),1321) +# Geeetech GT2560 V4.1B for A10(M/T/D) +else ifeq ($(HARDWARE_MOTHERBOARD),1322) +# Einstart retrofit +else ifeq ($(HARDWARE_MOTHERBOARD),1323) +# Wanhao 0ne+ i3 Mini +else ifeq ($(HARDWARE_MOTHERBOARD),1324) +# Wanhao D9 MK2 +else ifeq ($(HARDWARE_MOTHERBOARD),1325) +# Overlord/Overlord Pro +else ifeq ($(HARDWARE_MOTHERBOARD),1326) +# ADIMLab Gantry v1 +else ifeq ($(HARDWARE_MOTHERBOARD),1327) +# ADIMLab Gantry v2 else ifeq ($(HARDWARE_MOTHERBOARD),1328) -# Mega controller & Protoneer CNC Shield V3.00 +# Leapfrog Xeed 2015 else ifeq ($(HARDWARE_MOTHERBOARD),1329) +# PICA Shield (original version) +else ifeq ($(HARDWARE_MOTHERBOARD),1330) +# PICA Shield (rev C or later) +else ifeq ($(HARDWARE_MOTHERBOARD),1331) +# Intamsys 4.0 (Funmat HT) +else ifeq ($(HARDWARE_MOTHERBOARD),1332) +# Malyan M180 Mainboard Version 2 +else ifeq ($(HARDWARE_MOTHERBOARD),1333) +# Mega controller & Protoneer CNC Shield V3.00 +else ifeq ($(HARDWARE_MOTHERBOARD),1334) +# WEEDO 62A board (TINA2, Monoprice Cadet, etc.) +else ifeq ($(HARDWARE_MOTHERBOARD),1335) # # ATmega1281, ATmega2561 @@ -440,7 +471,7 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1502) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega644p PROG_MCU ?= m644p -# Melzi V2.0 +# Melzi V2 else ifeq ($(HARDWARE_MOTHERBOARD),1503) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p @@ -450,41 +481,46 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1504) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# Melzi Creality3D board (for CR-10 etc) +# Melzi Creality3D (for CR-10 etc) else ifeq ($(HARDWARE_MOTHERBOARD),1505) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# Melzi Malyan M150 board +# Melzi Creality3D (for Ender-2) else ifeq ($(HARDWARE_MOTHERBOARD),1506) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# Tronxy X5S +# Melzi Malyan M150 else ifeq ($(HARDWARE_MOTHERBOARD),1507) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# STB V1.1 +# Tronxy X5S else ifeq ($(HARDWARE_MOTHERBOARD),1508) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# Azteeg X1 +# STB V1.1 else ifeq ($(HARDWARE_MOTHERBOARD),1509) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# Anet 1.0 (Melzi clone) +# Azteeg X1 else ifeq ($(HARDWARE_MOTHERBOARD),1510) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p -# ZoneStar ZMIB V2 +# Anet 1.0 (Melzi clone) else ifeq ($(HARDWARE_MOTHERBOARD),1511) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p +# ZoneStar ZMIB V2 +else ifeq ($(HARDWARE_MOTHERBOARD),1512) + HARDWARE_VARIANT ?= Sanguino + MCU ?= atmega1284p + PROG_MCU ?= m1284p # # Other ATmega644P, ATmega644, ATmega1284P @@ -595,6 +631,10 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1707) MCU ?= at90usb1286 PROG_MCU ?= usb1286 +# +# SAM3X8E ARM Cortex-M3 +# + # UltiMachine Archim1 (with DRV8825 drivers) else ifeq ($(HARDWARE_MOTHERBOARD),3023) HARDWARE_VARIANT ?= archim @@ -767,10 +807,10 @@ endif ifeq ($(TMC), 1) LIB_CXXSRC += TMCStepper.cpp COOLCONF.cpp DRV_STATUS.cpp IHOLD_IRUN.cpp \ - CHOPCONF.cpp GCONF.cpp PWMCONF.cpp DRV_CONF.cpp DRVCONF.cpp DRVCTRL.cpp \ - DRVSTATUS.cpp ENCMODE.cpp RAMP_STAT.cpp SGCSCONF.cpp SHORT_CONF.cpp \ - SMARTEN.cpp SW_MODE.cpp SW_SPI.cpp TMC2130Stepper.cpp TMC2208Stepper.cpp \ - TMC2209Stepper.cpp TMC2660Stepper.cpp TMC5130Stepper.cpp TMC5160Stepper.cpp + CHOPCONF.cpp GCONF.cpp PWMCONF.cpp DRV_CONF.cpp DRVCONF.cpp DRVCTRL.cpp DRVSTATUS.cpp \ + GLOBAL_SCALER.cpp SLAVECONF.cpp IOIN.cpp ENCMODE.cpp RAMP_STAT.cpp SGCSCONF.cpp \ + SHORT_CONF.cpp SMARTEN.cpp SW_MODE.cpp SW_SPI.cpp TMC2130Stepper.cpp TMC2208Stepper.cpp \ + TMC2209Stepper.cpp TMC2240Stepper.cpp TMC2660Stepper.cpp TMC5130Stepper.cpp TMC5160Stepper.cpp endif ifeq ($(RELOC_WORKAROUND), 1) @@ -837,8 +877,8 @@ else ifeq ($(HARDWARE_VARIANT), archim) endif # Add all the source directories as include directories too -CINCS = ${addprefix -I ,${VPATH}} -CXXINCS = ${addprefix -I ,${VPATH}} +CINCS = ${addprefix -I, ${VPATH}} +CXXINCS = ${addprefix -I, ${VPATH}} # Silence warnings for library code (won't work for .h files, unfortunately) LIBWARN = -w -Wno-packed-bitfield-compat @@ -995,7 +1035,7 @@ extcoff: $(TARGET).elf $(NM) -n $< > $@ # Link: create ELF output file from library. - +LDFLAGS+= -Wl,-V $(BUILD_DIR)/$(TARGET).elf: $(OBJ) Configuration.h $(Pecho) " CXX $@" $P $(CXX) $(LD_PREFIX) $(ALL_CXXFLAGS) -o $@ -L. $(OBJ) $(LDFLAGS) $(LD_SUFFIX) diff --git a/Marlin/Version.h b/Marlin/Version.h index ba8adf315a..edf9a292d9 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -41,7 +41,14 @@ * here we define this default string as the date where the latest release * version was tagged. */ -//#define STRING_DISTRIBUTION_DATE "2024-04-07" +//#define STRING_DISTRIBUTION_DATE "2025-11-08" + +/** + * The protocol for communication to the host. Protocol indicates communication + * standards such as the use of ASCII, "echo:" and "error:" line prefixes, etc. + * (Other behaviors are given by the firmware version and capabilities report.) + */ +//#define PROTOCOL_VERSION "1.0" /** * Defines a generic printer name to be output to the LCD after booting Marlin. @@ -68,8 +75,8 @@ //#define WEBSITE_URL "marlinfw.org" /** - * Set the vendor info the serial USB interface, if changable - * Currently only supported by DUE platform + * Set the vendor info the serial USB interface, if changeable. + * Currently only supported by DUE platform. */ //#define USB_DEVICE_VENDOR_ID 0x0000 //#define USB_DEVICE_PRODUCT_ID 0x0000 diff --git a/Marlin/config.ini b/Marlin/config.ini index fed2a5c68c..fd2b81062a 100644 --- a/Marlin/config.ini +++ b/Marlin/config.ini @@ -62,6 +62,11 @@ motherboard = BOARD_RAMPS_14_EFB serial_port = 0 baudrate = 250000 +string_config_h_author = "(default from config.ini)" + +capabilities_report = on +extended_capabilities_report = on + use_watchdog = on thermal_protection_hotends = on thermal_protection_hysteresis = 4 @@ -77,18 +82,25 @@ temp_sensor_0 = 1 temp_hysteresis = 3 heater_0_mintemp = 5 heater_0_maxtemp = 275 -preheat_1_temp_hotend = 180 -bang_max = 255 pidtemp = on pid_k1 = 0.95 pid_max = 255 -pid_functional_range = 10 +pid_functional_range = 20 default_kp = 22.20 default_ki = 1.08 default_kd = 114.00 +temp_sensor_bed = 1 +bed_check_interval = 5000 +bed_mintemp = 5 +bed_maxtemp = 150 + +thermal_protection_bed = on +thermal_protection_bed_hysteresis = 2 +thermal_protection_bed_period = 20 + x_driver_type = A4988 y_driver_type = A4988 z_driver_type = A4988 @@ -121,10 +133,10 @@ default_max_acceleration = { 3000, 3000, 100, 10000 } homing_feedrate_mm_m = { (50*60), (50*60), (4*60) } homing_bump_divisor = { 2, 2, 4 } -x_enable_on = 0 -y_enable_on = 0 -z_enable_on = 0 -e_enable_on = 0 +x_enable_on = LOW +y_enable_on = LOW +z_enable_on = LOW +e_enable_on = LOW invert_x_dir = false invert_y_dir = true @@ -136,11 +148,6 @@ step_state_x = HIGH step_state_y = HIGH step_state_z = HIGH -disable_x = off -disable_y = off -disable_z = off -disable_e = off - proportional_font_ratio = 1.0 default_nominal_filament_dia = 1.75 @@ -153,30 +160,33 @@ default_retract_acceleration = 3000 default_minimumfeedrate = 0.0 default_mintravelfeedrate = 0.0 -minimum_planner_speed = 0.05 min_steps_per_segment = 6 default_minsegmenttime = 20000 [config:basic] +hotend_overshoot = 15 bed_overshoot = 10 +max_bed_power = 255 + busy_while_heating = on -default_ejerk = 5.0 +host_keepalive_feature = on default_keepalive_interval = 2 -default_leveling_fade_height = 0.0 -disable_other_extruders = on -display_charset_hd44780 = JAPANESE +printjob_timer_autostart = on + +jd_handle_small_segments = on +validate_homing_endstops = on +editable_steps_per_unit = on + eeprom_boot_silent = on eeprom_chitchat = on + endstoppullups = on -extrude_maxlength = 200 + +prevent_cold_extrusion = on extrude_mintemp = 170 -host_keepalive_feature = on -hotend_overshoot = 15 -jd_handle_small_segments = on -lcd_info_screen_style = 0 -lcd_language = en -max_bed_power = 255 -mesh_inset = 0 +prevent_lengthy_extrude = on +extrude_maxlength = 200 + min_software_endstops = on max_software_endstops = on min_software_endstop_x = on @@ -185,63 +195,60 @@ min_software_endstop_z = on max_software_endstop_x = on max_software_endstop_y = on max_software_endstop_z = on -preheat_1_fan_speed = 0 + preheat_1_label = "PLA" +preheat_1_temp_hotend = 180 preheat_1_temp_bed = 70 -prevent_cold_extrusion = on -prevent_lengthy_extrude = on -printjob_timer_autostart = on -probing_margin = 10 -show_bootscreen = on -soft_pwm_scale = 0 -string_config_h_author = "(none, default config)" +preheat_1_fan_speed = 0 + +preheat_2_label = "ABS" +preheat_2_temp_hotend = 240 +preheat_2_temp_bed = 110 +preheat_2_fan_speed = 0 + temp_bed_hysteresis = 3 temp_bed_residency_time = 10 temp_bed_window = 1 temp_residency_time = 10 temp_window = 1 -validate_homing_endstops = on -xy_probe_feedrate = (133*60) -z_clearance_between_probes = 5 -z_clearance_deploy_probe = 10 -z_clearance_multi_probe = 5 [config:advanced] arc_support = on +min_arc_segment_mm = 0.1 +max_arc_segment_mm = 1.0 +min_circle_segments = 72 +n_arc_correction = 25 + auto_report_temperatures = on + autotemp = on +autotemp_min = 210 +autotemp_max = 250 +autotemp_factor = 0.1f autotemp_oldweight = 0.98 -bed_check_interval = 5000 + default_stepper_timeout_sec = 120 -default_volumetric_extruder_limit = 0.00 disable_idle_x = on disable_idle_y = on disable_idle_z = on disable_idle_e = on + e0_auto_fan_pin = -1 -encoder_100x_steps_per_sec = 80 -encoder_10x_steps_per_sec = 30 -encoder_rate_multiplier = on -extended_capabilities_report = on -extruder_auto_fan_speed = 255 -extruder_auto_fan_temperature = 50 -fanmux0_pin = -1 -fanmux1_pin = -1 -fanmux2_pin = -1 + faster_gcode_parser = on +debug_flags_gcode = on + homing_bump_mm = { 5, 5, 2 } -max_arc_segment_mm = 1.0 -min_arc_segment_mm = 0.1 -min_circle_segments = 72 -n_arc_correction = 25 -serial_overrun_protection = on + slowdown = on slowdown_divisor = 2 -temp_sensor_bed = 0 -thermal_protection_bed_hysteresis = 2 -thermocouple_max_errors = 15 +multistepping_limit = 16 + +serial_overrun_protection = on tx_buffer_size = 0 + +watch_temp_increase = 2 +watch_temp_period = 40 + watch_bed_temp_increase = 2 watch_bed_temp_period = 60 -watch_temp_increase = 2 -watch_temp_period = 20 diff --git a/Marlin/src/HAL/AVR/HAL.h b/Marlin/src/HAL/AVR/HAL.h index 3c883b645c..e7a82a3b83 100644 --- a/Marlin/src/HAL/AVR/HAL.h +++ b/Marlin/src/HAL/AVR/HAL.h @@ -129,11 +129,11 @@ typedef Servo hal_servo_t; #endif #endif -#ifdef MMU2_SERIAL_PORT - #if !WITHIN(MMU2_SERIAL_PORT, 0, 3) - #error "MMU2_SERIAL_PORT must be from 0 to 3" +#ifdef MMU_SERIAL_PORT + #if !WITHIN(MMU_SERIAL_PORT, 0, 3) + #error "MMU_SERIAL_PORT must be from 0 to 3" #endif - #define MMU2_SERIAL mmuSerial + #define MMU_SERIAL mmuSerial #endif #ifdef LCD_SERIAL_PORT @@ -141,7 +141,7 @@ typedef Servo hal_servo_t; #error "LCD_SERIAL_PORT must be from 0 to 3." #endif #define LCD_SERIAL lcdSerial - #if HAS_DGUS_LCD + #if ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.get_tx_buffer_free() #endif #endif @@ -159,7 +159,7 @@ typedef Servo hal_servo_t; #define GET_PIN_MAP_INDEX(pin) pin #define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) -#define HAL_SENSITIVE_PINS 0, 1, +#define HAL_SENSITIVE_PINS 0, 1 #ifdef __AVR_AT90USB1286__ #define JTAG_DISABLE() do{ MCUCR = 0x80; MCUCR = 0x80; }while(0) @@ -204,7 +204,7 @@ public: static void isr_on() { sei(); } static void isr_off() { cli(); } - static void delay_ms(const int ms) { _delay_ms(ms); } + static void delay_ms(const int ms) { delay(ms); } // Tasks, called from idle() static void idletask() {} diff --git a/Marlin/src/HAL/AVR/MarlinSerial.cpp b/Marlin/src/HAL/AVR/MarlinSerial.cpp index 986462437c..d070731418 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.cpp +++ b/Marlin/src/HAL/AVR/MarlinSerial.cpp @@ -601,20 +601,20 @@ MSerialT1 customizedSerial1(MSerialT1::HasEmergencyParser); #endif // SERIAL_PORT_3 -#ifdef MMU2_SERIAL_PORT +#ifdef MMU_SERIAL_PORT - ISR(SERIAL_REGNAME(USART, MMU2_SERIAL_PORT, _RX_vect)) { - MarlinSerial>::store_rxd_char(); + ISR(SERIAL_REGNAME(USART, MMU_SERIAL_PORT, _RX_vect)) { + MarlinSerial>::store_rxd_char(); } - ISR(SERIAL_REGNAME(USART, MMU2_SERIAL_PORT, _UDRE_vect)) { - MarlinSerial>::_tx_udr_empty_irq(); + ISR(SERIAL_REGNAME(USART, MMU_SERIAL_PORT, _UDRE_vect)) { + MarlinSerial>::_tx_udr_empty_irq(); } - template class MarlinSerial< MMU2SerialCfg >; + template class MarlinSerial< MMU2SerialCfg >; MSerialMMU2 mmuSerial(MSerialMMU2::HasEmergencyParser); -#endif // MMU2_SERIAL_PORT +#endif // MMU_SERIAL_PORT #ifdef LCD_SERIAL_PORT @@ -629,7 +629,7 @@ MSerialT1 customizedSerial1(MSerialT1::HasEmergencyParser); template class MarlinSerial< LCDSerialCfg >; MSerialLCD lcdSerial(MSerialLCD::HasEmergencyParser); - #if HAS_DGUS_LCD + #if ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) template typename MarlinSerial::ring_buffer_pos_t MarlinSerial::get_tx_buffer_free() { const ring_buffer_pos_t t = tx_buffer.tail, // next byte to send. diff --git a/Marlin/src/HAL/AVR/MarlinSerial.h b/Marlin/src/HAL/AVR/MarlinSerial.h index a40730e0a8..9ecf3bde22 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.h +++ b/Marlin/src/HAL/AVR/MarlinSerial.h @@ -205,7 +205,7 @@ static ring_buffer_pos_t available(); static void write(const uint8_t c); static void flushTX(); - #if HAS_DGUS_LCD + #if ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) static ring_buffer_pos_t get_tx_buffer_free(); #endif @@ -246,7 +246,7 @@ #endif // !USBCON -#ifdef MMU2_SERIAL_PORT +#ifdef MMU_SERIAL_PORT template struct MMU2SerialCfg { static constexpr int PORT = serial; @@ -260,7 +260,7 @@ static constexpr bool RX_OVERRUNS = false; }; - typedef Serial1Class< MarlinSerial< MMU2SerialCfg > > MSerialMMU2; + typedef Serial1Class< MarlinSerial< MMU2SerialCfg > > MSerialMMU2; extern MSerialMMU2 mmuSerial; #endif diff --git a/Marlin/src/HAL/AVR/endstop_interrupts.h b/Marlin/src/HAL/AVR/endstop_interrupts.h index 91cb336e24..a6813ff277 100644 --- a/Marlin/src/HAL/AVR/endstop_interrupts.h +++ b/Marlin/src/HAL/AVR/endstop_interrupts.h @@ -345,6 +345,14 @@ void setup_endstop_interrupts() { pciSetup(Z_MIN_PROBE_PIN); #endif #endif + #if USE_CALIBRATION + #if (digitalPinToInterrupt(CALIBRATION_PIN) != NOT_AN_INTERRUPT) + _ATTACH(CALIBRATION_PIN); + #else + static_assert(digitalPinHasPCICR(CALIBRATION_PIN), "CALIBRATION_PIN is not interrupt-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue."); + pciSetup(CALIBRATION_PIN); + #endif + #endif // If we arrive here without raising an assertion, each pin has either an EXT-interrupt or a PCI. } diff --git a/Marlin/src/HAL/AVR/fast_pwm.cpp b/Marlin/src/HAL/AVR/fast_pwm.cpp index 6da68e6245..936f9e5688 100644 --- a/Marlin/src/HAL/AVR/fast_pwm.cpp +++ b/Marlin/src/HAL/AVR/fast_pwm.cpp @@ -150,7 +150,7 @@ void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { else { if (p == 32 || p == 128) continue; // Skip TIMER2 specific prescalers when not TIMER2 const uint16_t rft = (F_CPU) / (p * f_desired); - DEBUG_ECHOLNPGM("(Not Timer 2) F_CPU=" STRINGIFY(F_CPU), " prescaler=", p, " f_desired=", f_desired); + DEBUG_ECHOLNPGM("(Not Timer 2) F_CPU=", STRINGIFY(F_CPU), " prescaler=", p, " f_desired=", f_desired); res_fast_temp = rft - 1; res_pc_temp = rft / 2; } diff --git a/Marlin/src/HAL/AVR/fastio.cpp b/Marlin/src/HAL/AVR/fastio.cpp index 5c6ef18915..98fd636ebf 100644 --- a/Marlin/src/HAL/AVR/fastio.cpp +++ b/Marlin/src/HAL/AVR/fastio.cpp @@ -241,7 +241,7 @@ uint8_t extDigitalRead(const int8_t pin) { * * DC values -1.0 to 1.0. Negative duty cycle inverts the pulse. */ -uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb, const float dcc) { +uint16_t set_pwm_frequency_hz(const float hz, const float dca, const float dcb, const float dcc) { float count = 0; if (hz > 0 && (dca || dcb || dcc)) { count = float(F_CPU) / hz; // 1x prescaler, TOP for 16MHz base freq. @@ -254,7 +254,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb else { prescaler = 1; SET_CS(5, PRESCALER_1); } count /= float(prescaler); - const float pwm_top = round(count); // Get the rounded count + const float pwm_top = roundf(count); // Get the rounded count ICR5 = (uint16_t)pwm_top - 1; // Subtract 1 for TOP OCR5A = pwm_top * ABS(dca); // Update and scale DCs @@ -280,7 +280,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb SET_CS(5, PRESCALER_64); // 16MHz / 64 = 250kHz OCR5A = OCR5B = OCR5C = 0; } - return round(count); + return roundf(count); } #endif diff --git a/Marlin/src/HAL/AVR/fastio/fastio_1280.h b/Marlin/src/HAL/AVR/fastio/fastio_1280.h index 633774dda9..57d6181d2e 100644 --- a/Marlin/src/HAL/AVR/fastio/fastio_1280.h +++ b/Marlin/src/HAL/AVR/fastio/fastio_1280.h @@ -28,9 +28,6 @@ * Port : E0 E1 E4 E5 G5 E3 H3 H4 H5 H6 B4 B5 B6 B7 J1 J0 H1 H0 D3 D2 D1 D0 A0 A1 A2 A3 A4 A5 A6 A7 C7 C6 C5 C4 C3 C2 C1 C0 D7 G2 G1 G0 L7 L6 L5 L4 L3 L2 L1 L0 B3 B2 B1 B0 F0 F1 F2 F3 F4 F5 F6 F7 K0 K1 K2 K3 K4 K5 K6 K7 | E2 E6 E7 xx xx H2 H7 G3 G4 xx xx xx xx xx D4 D5 D6 xx xx J2 J3 J4 J5 J6 J7 xx xx xx xx xx * Logical Pin : 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | 78 79 80 xx xx 84 85 71 70 xx xx xx xx xx 81 82 83 xx xx 72 73 75 76 77 74 xx xx xx xx xx * Analog Input : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - * - * Arduino Pin Layout video: https://youtu.be/rIqeVCX09FA - * AVR alternate pin function overview video: https://youtu.be/1yd8wuI5Plg */ #include "../fastio.h" diff --git a/Marlin/src/HAL/AVR/fastio/fastio_1281.h b/Marlin/src/HAL/AVR/fastio/fastio_1281.h index 6067248978..fdff219ec3 100644 --- a/Marlin/src/HAL/AVR/fastio/fastio_1281.h +++ b/Marlin/src/HAL/AVR/fastio/fastio_1281.h @@ -26,9 +26,6 @@ * * Logical Pin: 38 39 40 41 42 43 44 45 16 10 11 12 06 07 08 09 30 31 32 33 34 35 36 37 17 18 19 20 21 22 23 24 00 01 13 05 02 03 14 15 46 47 48 49 50 51 52 53 25 26 27 28 29 04 * Port: A0 A1 A2 A3 A4 A5 A6 A7 B0 B1 B2 B3 B4 B5 B6 B7 C0 C1 C2 C3 C4 C5 C6 C7 D0 D1 D2 D3 D4 D5 D6 D7 E0 E1 E2 E3 E4 E5 E6 E7 F0 F1 F2 F3 F4 F5 F6 F7 G0 G1 G2 G3 G4 G5 - * - * Arduino Pin Layout video: https://youtu.be/rIqeVCX09FA - * AVR alternate pin function overview video: https://youtu.be/1yd8wuI5Plg */ #include "../fastio.h" diff --git a/Marlin/src/HAL/AVR/fastio/fastio_168.h b/Marlin/src/HAL/AVR/fastio/fastio_168.h index cc55979740..36dc552385 100644 --- a/Marlin/src/HAL/AVR/fastio/fastio_168.h +++ b/Marlin/src/HAL/AVR/fastio/fastio_168.h @@ -26,9 +26,6 @@ * * Logical Pin: 08 09 10 11 12 13 14 15 16 17 18 19 20 21 00 01 02 03 04 05 06 07 * Port: B0 B1 B2 B3 B4 B5 C0 C1 C2 C3 C4 C5 C6 C7 D0 D1 D2 D3 D4 D5 D6 D7 - * - * Arduino Pin Layout video: https://youtu.be/rIqeVCX09FA - * AVR alternate pin function overview video: https://youtu.be/1yd8wuI5Plg */ #include "../fastio.h" diff --git a/Marlin/src/HAL/AVR/fastio/fastio_644.h b/Marlin/src/HAL/AVR/fastio/fastio_644.h index 94b322a819..e20a3d345e 100644 --- a/Marlin/src/HAL/AVR/fastio/fastio_644.h +++ b/Marlin/src/HAL/AVR/fastio/fastio_644.h @@ -26,9 +26,6 @@ * * Logical Pin: 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 * Port: B0 B1 B2 B3 B4 B5 B6 B7 D0 D1 D2 D3 D4 D5 D6 D7 C0 C1 C2 C3 C4 C5 C6 C7 A7 A6 A5 A4 A3 A2 A1 A0 - * - * Arduino Pin Layout video: https://youtu.be/rIqeVCX09FA - * AVR alternate pin function overview video: https://youtu.be/1yd8wuI5Plg */ /** ATMega644 diff --git a/Marlin/src/HAL/AVR/fastio/fastio_AT90USB.h b/Marlin/src/HAL/AVR/fastio/fastio_AT90USB.h index 51c5e09658..a9af519ff3 100644 --- a/Marlin/src/HAL/AVR/fastio/fastio_AT90USB.h +++ b/Marlin/src/HAL/AVR/fastio/fastio_AT90USB.h @@ -26,10 +26,7 @@ * * Logical Pin: 28 29 30 31 32 33 34 35 20 21 22 23 24 25 26 27 10 11 12 13 14 15 16 17 00 01 02 03 04 05 06 07 08 09(46*47)36 37 18 19 38 39 40 41 42 43 44 45 * Port: A0 A1 A2 A3 A4 A5 A6 A7 B0 B1 B2 B3 B4 B5 B6 B7 C0 C1 C2 C3 C4 C5 C6 C7 D0 D1 D2 D3 D4 D5 D6 D7 E0 E1 E2 E3 E4 E5 E6 E7 F0 F1 F2 F3 F4 F5 F6 F7 - * The logical pins 46 and 47 are not supported by Teensyduino, but are supported below as E2 and E3 - * - * Arduino Pin Layout video: https://youtu.be/rIqeVCX09FA - * AVR alternate pin function overview video: https://youtu.be/1yd8wuI5Plg + * Logical pins 46-47 aren't supported by Teensyduino, but are supported below as E2 and E3 */ #include "../fastio.h" @@ -366,8 +363,11 @@ #define AIO7_PWM 0 #define AIO7_DDR DDRF -//-- Begin not supported by Teensyduino -//-- don't use Arduino functions on these pins pinMode/digitalWrite/etc +//-- 46-47 are not supported by Teensyduino +//-- Don't use Arduino functions (pinMode, digitalWrite, etc.) on these pins +#define PIN_E2 46 +#define PIN_E3 47 + #define DIO46_PIN PINE2 #define DIO46_RPORT PINE #define DIO46_WPORT PORTE @@ -380,10 +380,7 @@ #define DIO47_PWM 0 #define DIO47_DDR DDRE -#define TEENSY_E2 46 -#define TEENSY_E3 47 - -//-- end not supported by Teensyduino +//-- #undef PA0 #define PA0_PIN PINA0 diff --git a/Marlin/src/HAL/AVR/inc/SanityCheck.h b/Marlin/src/HAL/AVR/inc/SanityCheck.h index 85ee683685..08fe21d4f8 100644 --- a/Marlin/src/HAL/AVR/inc/SanityCheck.h +++ b/Marlin/src/HAL/AVR/inc/SanityCheck.h @@ -95,7 +95,7 @@ /** * The Trinamic library includes SoftwareSerial.h, leading to a compile error. */ -#if ALL(HAS_TRINAMIC_CONFIG, ENDSTOP_INTERRUPTS_FEATURE) +#if ALL(HAS_TMC_SW_SERIAL, ENDSTOP_INTERRUPTS_FEATURE) #error "TMCStepper includes SoftwareSerial.h which is incompatible with ENDSTOP_INTERRUPTS_FEATURE. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." #endif diff --git a/Marlin/src/HAL/AVR/pinsDebug.h b/Marlin/src/HAL/AVR/pinsDebug.h index 15db63b4d7..c833964a29 100644 --- a/Marlin/src/HAL/AVR/pinsDebug.h +++ b/Marlin/src/HAL/AVR/pinsDebug.h @@ -22,7 +22,23 @@ #pragma once /** - * PWM print routines for Atmel 8 bit AVR CPUs + * Pins Debugging for Atmel 8 bit AVR CPUs + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #include "../../inc/MarlinConfig.h" @@ -39,44 +55,44 @@ #include "pinsDebug_Teensyduino.h" // Can't use the "digitalPinToPort" function from the Teensyduino type IDEs // portModeRegister takes a different argument - #define digitalPinToTimer_DEBUG(p) digitalPinToTimer(p) - #define digitalPinToBitMask_DEBUG(p) digitalPinToBitMask(p) - #define digitalPinToPort_DEBUG(p) digitalPinToPort(p) - #define GET_PINMODE(pin) (*portModeRegister(pin) & digitalPinToBitMask_DEBUG(pin)) + #define digitalPinToTimer_DEBUG(P) digitalPinToTimer(P) + #define digitalPinToBitMask_DEBUG(P) digitalPinToBitMask(P) + #define digitalPinToPort_DEBUG(P) digitalPinToPort(P) + #define getValidPinMode(P) (*portModeRegister(P) & digitalPinToBitMask_DEBUG(P)) #elif AVR_ATmega2560_FAMILY_PLUS_70 // So we can access/display all the pins on boards using more than 70 #include "pinsDebug_plus_70.h" - #define digitalPinToTimer_DEBUG(p) digitalPinToTimer_plus_70(p) - #define digitalPinToBitMask_DEBUG(p) digitalPinToBitMask_plus_70(p) - #define digitalPinToPort_DEBUG(p) digitalPinToPort_plus_70(p) - bool GET_PINMODE(int8_t pin) {return *portModeRegister(digitalPinToPort_DEBUG(pin)) & digitalPinToBitMask_DEBUG(pin); } + #define digitalPinToTimer_DEBUG(P) digitalPinToTimer_plus_70(P) + #define digitalPinToBitMask_DEBUG(P) digitalPinToBitMask_plus_70(P) + #define digitalPinToPort_DEBUG(P) digitalPinToPort_plus_70(P) + bool getValidPinMode(pin_t pin) {return *portModeRegister(digitalPinToPort_DEBUG(pin)) & digitalPinToBitMask_DEBUG(pin); } #else - #define digitalPinToTimer_DEBUG(p) digitalPinToTimer(p) - #define digitalPinToBitMask_DEBUG(p) digitalPinToBitMask(p) - #define digitalPinToPort_DEBUG(p) digitalPinToPort(p) - bool GET_PINMODE(int8_t pin) {return *portModeRegister(digitalPinToPort_DEBUG(pin)) & digitalPinToBitMask_DEBUG(pin); } - #define GET_ARRAY_PIN(p) pgm_read_byte(&pin_array[p].pin) + #define digitalPinToTimer_DEBUG(P) digitalPinToTimer(P) + #define digitalPinToBitMask_DEBUG(P) digitalPinToBitMask(P) + #define digitalPinToPort_DEBUG(P) digitalPinToPort(P) + bool getValidPinMode(pin_t pin) {return *portModeRegister(digitalPinToPort_DEBUG(pin)) & digitalPinToBitMask_DEBUG(pin); } + #define getPinByIndex(x) pgm_read_byte(&pin_array[x].pin) #endif -#define VALID_PIN(pin) (pin >= 0 && pin < NUM_DIGITAL_PINS ? 1 : 0) +#define isValidPin(P) (P >= 0 && P < NUMBER_PINS_TOTAL) #if AVR_ATmega1284_FAMILY - #define IS_ANALOG(P) WITHIN(P, analogInputToDigitalPin(7), analogInputToDigitalPin(0)) - #define DIGITAL_PIN_TO_ANALOG_PIN(P) int(IS_ANALOG(P) ? (P) - analogInputToDigitalPin(7) : -1) + #define isAnalogPin(P) WITHIN(P, analogInputToDigitalPin(7), analogInputToDigitalPin(0)) + #define digitalPinToAnalogIndex(P) int(isAnalogPin(P) ? (P) - analogInputToDigitalPin(7) : -1) #else #define _ANALOG1(P) WITHIN(P, analogInputToDigitalPin(0), analogInputToDigitalPin(7)) #define _ANALOG2(P) WITHIN(P, analogInputToDigitalPin(8), analogInputToDigitalPin(15)) - #define IS_ANALOG(P) (_ANALOG1(P) || _ANALOG2(P)) - #define DIGITAL_PIN_TO_ANALOG_PIN(P) int(_ANALOG1(P) ? (P) - analogInputToDigitalPin(0) : _ANALOG2(P) ? (P) - analogInputToDigitalPin(8) + 8 : -1) + #define isAnalogPin(P) (_ANALOG1(P) || _ANALOG2(P)) + #define digitalPinToAnalogIndex(P) int(_ANALOG1(P) ? (P) - analogInputToDigitalPin(0) : _ANALOG2(P) ? (P) - analogInputToDigitalPin(8) + 8 : -1) #endif -#define GET_ARRAY_PIN(p) pgm_read_byte(&pin_array[p].pin) +#define getPinByIndex(x) pgm_read_byte(&pin_array[x].pin) #define MULTI_NAME_PAD 26 // space needed to be pretty if not first name assigned to a pin -void PRINT_ARRAY_NAME(uint8_t x) { - PGM_P const name_mem_pointer = (PGM_P)pgm_read_ptr(&pin_array[x].name); +void printPinNameByIndex(const uint8_t index) { + PGM_P const name_mem_pointer = (PGM_P)pgm_read_ptr(&pin_array[index].name); for (uint8_t y = 0; y < MAX_NAME_LENGTH; ++y) { char temp_char = pgm_read_byte(name_mem_pointer + y); if (temp_char != 0) @@ -88,7 +104,7 @@ void PRINT_ARRAY_NAME(uint8_t x) { } } -#define GET_ARRAY_IS_DIGITAL(x) pgm_read_byte(&pin_array[x].is_digital) +#define getPinIsDigitalByIndex(x) pgm_read_byte(&pin_array[x].is_digital) #if defined(__AVR_ATmega1284P__) // 1284 IDE extensions set this to the number of #undef NUM_DIGITAL_PINS // digital only pins while all other CPUs have it @@ -109,7 +125,7 @@ void PRINT_ARRAY_NAME(uint8_t x) { * Print a pin's PWM status. * Return true if it's currently a PWM pin. */ -bool pwm_status(uint8_t pin) { +bool pwm_status(const uint8_t pin) { char buffer[20]; // for the sprintf statements switch (digitalPinToTimer_DEBUG(pin)) { @@ -276,7 +292,7 @@ void timer_prefix(uint8_t T, char L, uint8_t N) { // T - timer L - pwm N - if (TEST(*TMSK, TOIE)) err_prob_interrupt(); } -void pwm_details(uint8_t pin) { +void printPinPWM(const uint8_t pin) { switch (digitalPinToTimer_DEBUG(pin)) { #if ABTEST(0) @@ -347,7 +363,7 @@ void pwm_details(uint8_t pin) { #else UNUSED(print_is_also_tied); #endif -} // pwm_details +} // printPinPWM #ifndef digitalRead_mod // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs int digitalRead_mod(const pin_t pin) { // same as digitalRead except the PWM stop section has been removed @@ -356,21 +372,21 @@ void pwm_details(uint8_t pin) { } #endif -void print_port(const pin_t pin) { // print port number +void printPinPort(const pin_t pin) { // print port number #ifdef digitalPinToPort_DEBUG uint8_t x; SERIAL_ECHOPGM(" Port: "); #if AVR_AT90USB1286_FAMILY - x = (pin == 46 || pin == 47) ? 'E' : digitalPinToPort_DEBUG(pin) + 64; + x = (pin == PIN_E2 || pin == PIN_E3) ? 'E' : 'A' + digitalPinToPort_DEBUG(pin) - 1; #else - x = digitalPinToPort_DEBUG(pin) + 64; + x = 'A' + digitalPinToPort_DEBUG(pin) - 1; #endif SERIAL_CHAR(x); #if AVR_AT90USB1286_FAMILY - if (pin == 46) + if (pin == PIN_E2) x = '2'; - else if (pin == 47) + else if (pin == PIN_E3) x = '3'; else { uint8_t temp = digitalPinToBitMask_DEBUG(pin); @@ -386,7 +402,7 @@ void print_port(const pin_t pin) { // print port number #endif } -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3d "), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) #undef ABTEST diff --git a/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h b/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h index c812d4fb11..463a77ec1d 100644 --- a/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h +++ b/Marlin/src/HAL/AVR/pinsDebug_Teensyduino.h @@ -102,7 +102,7 @@ const uint8_t PROGMEM digital_pin_to_port_PGM[] = { // digitalPinToBitMask(pin) is OK -#define digitalRead_mod(p) extDigitalRead(p) // Teensyduino's version of digitalRead doesn't +#define digitalRead_mod(P) extDigitalRead(P) // Teensyduino's version of digitalRead doesn't // disable the PWMs so we can use it as is // portModeRegister(pin) is OK diff --git a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h index fa479cfe8f..6565acd523 100644 --- a/Marlin/src/HAL/AVR/pinsDebug_plus_70.h +++ b/Marlin/src/HAL/AVR/pinsDebug_plus_70.h @@ -48,92 +48,92 @@ const uint8_t PROGMEM digital_pin_to_port_PGM_plus_70[] = { // PORTLIST // ------------------------ - PE , // PE 0 ** 0 ** USART0_RX - PE , // PE 1 ** 1 ** USART0_TX - PE , // PE 4 ** 2 ** PWM2 - PE , // PE 5 ** 3 ** PWM3 - PG , // PG 5 ** 4 ** PWM4 - PE , // PE 3 ** 5 ** PWM5 - PH , // PH 3 ** 6 ** PWM6 - PH , // PH 4 ** 7 ** PWM7 - PH , // PH 5 ** 8 ** PWM8 - PH , // PH 6 ** 9 ** PWM9 - PB , // PB 4 ** 10 ** PWM10 - PB , // PB 5 ** 11 ** PWM11 - PB , // PB 6 ** 12 ** PWM12 - PB , // PB 7 ** 13 ** PWM13 - PJ , // PJ 1 ** 14 ** USART3_TX - PJ , // PJ 0 ** 15 ** USART3_RX - PH , // PH 1 ** 16 ** USART2_TX - PH , // PH 0 ** 17 ** USART2_RX - PD , // PD 3 ** 18 ** USART1_TX - PD , // PD 2 ** 19 ** USART1_RX - PD , // PD 1 ** 20 ** I2C_SDA - PD , // PD 0 ** 21 ** I2C_SCL - PA , // PA 0 ** 22 ** D22 - PA , // PA 1 ** 23 ** D23 - PA , // PA 2 ** 24 ** D24 - PA , // PA 3 ** 25 ** D25 - PA , // PA 4 ** 26 ** D26 - PA , // PA 5 ** 27 ** D27 - PA , // PA 6 ** 28 ** D28 - PA , // PA 7 ** 29 ** D29 - PC , // PC 7 ** 30 ** D30 - PC , // PC 6 ** 31 ** D31 - PC , // PC 5 ** 32 ** D32 - PC , // PC 4 ** 33 ** D33 - PC , // PC 3 ** 34 ** D34 - PC , // PC 2 ** 35 ** D35 - PC , // PC 1 ** 36 ** D36 - PC , // PC 0 ** 37 ** D37 - PD , // PD 7 ** 38 ** D38 - PG , // PG 2 ** 39 ** D39 - PG , // PG 1 ** 40 ** D40 - PG , // PG 0 ** 41 ** D41 - PL , // PL 7 ** 42 ** D42 - PL , // PL 6 ** 43 ** D43 - PL , // PL 5 ** 44 ** D44 - PL , // PL 4 ** 45 ** D45 - PL , // PL 3 ** 46 ** D46 - PL , // PL 2 ** 47 ** D47 - PL , // PL 1 ** 48 ** D48 - PL , // PL 0 ** 49 ** D49 - PB , // PB 3 ** 50 ** SPI_MISO - PB , // PB 2 ** 51 ** SPI_MOSI - PB , // PB 1 ** 52 ** SPI_SCK - PB , // PB 0 ** 53 ** SPI_SS - PF , // PF 0 ** 54 ** A0 - PF , // PF 1 ** 55 ** A1 - PF , // PF 2 ** 56 ** A2 - PF , // PF 3 ** 57 ** A3 - PF , // PF 4 ** 58 ** A4 - PF , // PF 5 ** 59 ** A5 - PF , // PF 6 ** 60 ** A6 - PF , // PF 7 ** 61 ** A7 - PK , // PK 0 ** 62 ** A8 - PK , // PK 1 ** 63 ** A9 - PK , // PK 2 ** 64 ** A10 - PK , // PK 3 ** 65 ** A11 - PK , // PK 4 ** 66 ** A12 - PK , // PK 5 ** 67 ** A13 - PK , // PK 6 ** 68 ** A14 - PK , // PK 7 ** 69 ** A15 - PG , // PG 4 ** 70 ** - PG , // PG 3 ** 71 ** - PJ , // PJ 2 ** 72 ** - PJ , // PJ 3 ** 73 ** - PJ , // PJ 7 ** 74 ** - PJ , // PJ 4 ** 75 ** - PJ , // PJ 5 ** 76 ** - PJ , // PJ 6 ** 77 ** - PE , // PE 2 ** 78 ** - PE , // PE 6 ** 79 ** - PE , // PE 7 ** 80 ** - PD , // PD 4 ** 81 ** - PD , // PD 5 ** 82 ** - PD , // PD 6 ** 83 ** - PH , // PH 2 ** 84 ** - PH , // PH 7 ** 85 ** + PE, // PE 0 ** 0 ** USART0_RX + PE, // PE 1 ** 1 ** USART0_TX + PE, // PE 4 ** 2 ** PWM2 + PE, // PE 5 ** 3 ** PWM3 + PG, // PG 5 ** 4 ** PWM4 + PE, // PE 3 ** 5 ** PWM5 + PH, // PH 3 ** 6 ** PWM6 + PH, // PH 4 ** 7 ** PWM7 + PH, // PH 5 ** 8 ** PWM8 + PH, // PH 6 ** 9 ** PWM9 + PB, // PB 4 ** 10 ** PWM10 + PB, // PB 5 ** 11 ** PWM11 + PB, // PB 6 ** 12 ** PWM12 + PB, // PB 7 ** 13 ** PWM13 + PJ, // PJ 1 ** 14 ** USART3_TX + PJ, // PJ 0 ** 15 ** USART3_RX + PH, // PH 1 ** 16 ** USART2_TX + PH, // PH 0 ** 17 ** USART2_RX + PD, // PD 3 ** 18 ** USART1_TX + PD, // PD 2 ** 19 ** USART1_RX + PD, // PD 1 ** 20 ** I2C_SDA + PD, // PD 0 ** 21 ** I2C_SCL + PA, // PA 0 ** 22 ** D22 + PA, // PA 1 ** 23 ** D23 + PA, // PA 2 ** 24 ** D24 + PA, // PA 3 ** 25 ** D25 + PA, // PA 4 ** 26 ** D26 + PA, // PA 5 ** 27 ** D27 + PA, // PA 6 ** 28 ** D28 + PA, // PA 7 ** 29 ** D29 + PC, // PC 7 ** 30 ** D30 + PC, // PC 6 ** 31 ** D31 + PC, // PC 5 ** 32 ** D32 + PC, // PC 4 ** 33 ** D33 + PC, // PC 3 ** 34 ** D34 + PC, // PC 2 ** 35 ** D35 + PC, // PC 1 ** 36 ** D36 + PC, // PC 0 ** 37 ** D37 + PD, // PD 7 ** 38 ** D38 + PG, // PG 2 ** 39 ** D39 + PG, // PG 1 ** 40 ** D40 + PG, // PG 0 ** 41 ** D41 + PL, // PL 7 ** 42 ** D42 + PL, // PL 6 ** 43 ** D43 + PL, // PL 5 ** 44 ** D44 + PL, // PL 4 ** 45 ** D45 + PL, // PL 3 ** 46 ** D46 + PL, // PL 2 ** 47 ** D47 + PL, // PL 1 ** 48 ** D48 + PL, // PL 0 ** 49 ** D49 + PB, // PB 3 ** 50 ** SPI_MISO + PB, // PB 2 ** 51 ** SPI_MOSI + PB, // PB 1 ** 52 ** SPI_SCK + PB, // PB 0 ** 53 ** SPI_SS + PF, // PF 0 ** 54 ** A0 + PF, // PF 1 ** 55 ** A1 + PF, // PF 2 ** 56 ** A2 + PF, // PF 3 ** 57 ** A3 + PF, // PF 4 ** 58 ** A4 + PF, // PF 5 ** 59 ** A5 + PF, // PF 6 ** 60 ** A6 + PF, // PF 7 ** 61 ** A7 + PK, // PK 0 ** 62 ** A8 + PK, // PK 1 ** 63 ** A9 + PK, // PK 2 ** 64 ** A10 + PK, // PK 3 ** 65 ** A11 + PK, // PK 4 ** 66 ** A12 + PK, // PK 5 ** 67 ** A13 + PK, // PK 6 ** 68 ** A14 + PK, // PK 7 ** 69 ** A15 + PG, // PG 4 ** 70 ** + PG, // PG 3 ** 71 ** + PJ, // PJ 2 ** 72 ** + PJ, // PJ 3 ** 73 ** + PJ, // PJ 7 ** 74 ** + PJ, // PJ 4 ** 75 ** + PJ, // PJ 5 ** 76 ** + PJ, // PJ 6 ** 77 ** + PE, // PE 2 ** 78 ** + PE, // PE 6 ** 79 ** + PE, // PE 7 ** 80 ** + PD, // PD 4 ** 81 ** + PD, // PD 5 ** 82 ** + PD, // PD 6 ** 83 ** + PH, // PH 2 ** 84 ** + PH, // PH 7 ** 85 ** }; #define digitalPinToPort_plus_70(P) ( pgm_read_byte( digital_pin_to_port_PGM_plus_70 + (P) ) ) @@ -141,92 +141,92 @@ const uint8_t PROGMEM digital_pin_to_port_PGM_plus_70[] = { const uint8_t PROGMEM digital_pin_to_bit_mask_PGM_plus_70[] = { // PIN IN PORT // ------------------------ - _BV( 0 ) , // PE 0 ** 0 ** USART0_RX - _BV( 1 ) , // PE 1 ** 1 ** USART0_TX - _BV( 4 ) , // PE 4 ** 2 ** PWM2 - _BV( 5 ) , // PE 5 ** 3 ** PWM3 - _BV( 5 ) , // PG 5 ** 4 ** PWM4 - _BV( 3 ) , // PE 3 ** 5 ** PWM5 - _BV( 3 ) , // PH 3 ** 6 ** PWM6 - _BV( 4 ) , // PH 4 ** 7 ** PWM7 - _BV( 5 ) , // PH 5 ** 8 ** PWM8 - _BV( 6 ) , // PH 6 ** 9 ** PWM9 - _BV( 4 ) , // PB 4 ** 10 ** PWM10 - _BV( 5 ) , // PB 5 ** 11 ** PWM11 - _BV( 6 ) , // PB 6 ** 12 ** PWM12 - _BV( 7 ) , // PB 7 ** 13 ** PWM13 - _BV( 1 ) , // PJ 1 ** 14 ** USART3_TX - _BV( 0 ) , // PJ 0 ** 15 ** USART3_RX - _BV( 1 ) , // PH 1 ** 16 ** USART2_TX - _BV( 0 ) , // PH 0 ** 17 ** USART2_RX - _BV( 3 ) , // PD 3 ** 18 ** USART1_TX - _BV( 2 ) , // PD 2 ** 19 ** USART1_RX - _BV( 1 ) , // PD 1 ** 20 ** I2C_SDA - _BV( 0 ) , // PD 0 ** 21 ** I2C_SCL - _BV( 0 ) , // PA 0 ** 22 ** D22 - _BV( 1 ) , // PA 1 ** 23 ** D23 - _BV( 2 ) , // PA 2 ** 24 ** D24 - _BV( 3 ) , // PA 3 ** 25 ** D25 - _BV( 4 ) , // PA 4 ** 26 ** D26 - _BV( 5 ) , // PA 5 ** 27 ** D27 - _BV( 6 ) , // PA 6 ** 28 ** D28 - _BV( 7 ) , // PA 7 ** 29 ** D29 - _BV( 7 ) , // PC 7 ** 30 ** D30 - _BV( 6 ) , // PC 6 ** 31 ** D31 - _BV( 5 ) , // PC 5 ** 32 ** D32 - _BV( 4 ) , // PC 4 ** 33 ** D33 - _BV( 3 ) , // PC 3 ** 34 ** D34 - _BV( 2 ) , // PC 2 ** 35 ** D35 - _BV( 1 ) , // PC 1 ** 36 ** D36 - _BV( 0 ) , // PC 0 ** 37 ** D37 - _BV( 7 ) , // PD 7 ** 38 ** D38 - _BV( 2 ) , // PG 2 ** 39 ** D39 - _BV( 1 ) , // PG 1 ** 40 ** D40 - _BV( 0 ) , // PG 0 ** 41 ** D41 - _BV( 7 ) , // PL 7 ** 42 ** D42 - _BV( 6 ) , // PL 6 ** 43 ** D43 - _BV( 5 ) , // PL 5 ** 44 ** D44 - _BV( 4 ) , // PL 4 ** 45 ** D45 - _BV( 3 ) , // PL 3 ** 46 ** D46 - _BV( 2 ) , // PL 2 ** 47 ** D47 - _BV( 1 ) , // PL 1 ** 48 ** D48 - _BV( 0 ) , // PL 0 ** 49 ** D49 - _BV( 3 ) , // PB 3 ** 50 ** SPI_MISO - _BV( 2 ) , // PB 2 ** 51 ** SPI_MOSI - _BV( 1 ) , // PB 1 ** 52 ** SPI_SCK - _BV( 0 ) , // PB 0 ** 53 ** SPI_SS - _BV( 0 ) , // PF 0 ** 54 ** A0 - _BV( 1 ) , // PF 1 ** 55 ** A1 - _BV( 2 ) , // PF 2 ** 56 ** A2 - _BV( 3 ) , // PF 3 ** 57 ** A3 - _BV( 4 ) , // PF 4 ** 58 ** A4 - _BV( 5 ) , // PF 5 ** 59 ** A5 - _BV( 6 ) , // PF 6 ** 60 ** A6 - _BV( 7 ) , // PF 7 ** 61 ** A7 - _BV( 0 ) , // PK 0 ** 62 ** A8 - _BV( 1 ) , // PK 1 ** 63 ** A9 - _BV( 2 ) , // PK 2 ** 64 ** A10 - _BV( 3 ) , // PK 3 ** 65 ** A11 - _BV( 4 ) , // PK 4 ** 66 ** A12 - _BV( 5 ) , // PK 5 ** 67 ** A13 - _BV( 6 ) , // PK 6 ** 68 ** A14 - _BV( 7 ) , // PK 7 ** 69 ** A15 - _BV( 4 ) , // PG 4 ** 70 ** - _BV( 3 ) , // PG 3 ** 71 ** - _BV( 2 ) , // PJ 2 ** 72 ** - _BV( 3 ) , // PJ 3 ** 73 ** - _BV( 7 ) , // PJ 7 ** 74 ** - _BV( 4 ) , // PJ 4 ** 75 ** - _BV( 5 ) , // PJ 5 ** 76 ** - _BV( 6 ) , // PJ 6 ** 77 ** - _BV( 2 ) , // PE 2 ** 78 ** - _BV( 6 ) , // PE 6 ** 79 ** - _BV( 7 ) , // PE 7 ** 80 ** - _BV( 4 ) , // PD 4 ** 81 ** - _BV( 5 ) , // PD 5 ** 82 ** - _BV( 6 ) , // PD 6 ** 83 ** - _BV( 2 ) , // PH 2 ** 84 ** - _BV( 7 ) , // PH 7 ** 85 ** + _BV( 0 ), // PE 0 ** 0 ** USART0_RX + _BV( 1 ), // PE 1 ** 1 ** USART0_TX + _BV( 4 ), // PE 4 ** 2 ** PWM2 + _BV( 5 ), // PE 5 ** 3 ** PWM3 + _BV( 5 ), // PG 5 ** 4 ** PWM4 + _BV( 3 ), // PE 3 ** 5 ** PWM5 + _BV( 3 ), // PH 3 ** 6 ** PWM6 + _BV( 4 ), // PH 4 ** 7 ** PWM7 + _BV( 5 ), // PH 5 ** 8 ** PWM8 + _BV( 6 ), // PH 6 ** 9 ** PWM9 + _BV( 4 ), // PB 4 ** 10 ** PWM10 + _BV( 5 ), // PB 5 ** 11 ** PWM11 + _BV( 6 ), // PB 6 ** 12 ** PWM12 + _BV( 7 ), // PB 7 ** 13 ** PWM13 + _BV( 1 ), // PJ 1 ** 14 ** USART3_TX + _BV( 0 ), // PJ 0 ** 15 ** USART3_RX + _BV( 1 ), // PH 1 ** 16 ** USART2_TX + _BV( 0 ), // PH 0 ** 17 ** USART2_RX + _BV( 3 ), // PD 3 ** 18 ** USART1_TX + _BV( 2 ), // PD 2 ** 19 ** USART1_RX + _BV( 1 ), // PD 1 ** 20 ** I2C_SDA + _BV( 0 ), // PD 0 ** 21 ** I2C_SCL + _BV( 0 ), // PA 0 ** 22 ** D22 + _BV( 1 ), // PA 1 ** 23 ** D23 + _BV( 2 ), // PA 2 ** 24 ** D24 + _BV( 3 ), // PA 3 ** 25 ** D25 + _BV( 4 ), // PA 4 ** 26 ** D26 + _BV( 5 ), // PA 5 ** 27 ** D27 + _BV( 6 ), // PA 6 ** 28 ** D28 + _BV( 7 ), // PA 7 ** 29 ** D29 + _BV( 7 ), // PC 7 ** 30 ** D30 + _BV( 6 ), // PC 6 ** 31 ** D31 + _BV( 5 ), // PC 5 ** 32 ** D32 + _BV( 4 ), // PC 4 ** 33 ** D33 + _BV( 3 ), // PC 3 ** 34 ** D34 + _BV( 2 ), // PC 2 ** 35 ** D35 + _BV( 1 ), // PC 1 ** 36 ** D36 + _BV( 0 ), // PC 0 ** 37 ** D37 + _BV( 7 ), // PD 7 ** 38 ** D38 + _BV( 2 ), // PG 2 ** 39 ** D39 + _BV( 1 ), // PG 1 ** 40 ** D40 + _BV( 0 ), // PG 0 ** 41 ** D41 + _BV( 7 ), // PL 7 ** 42 ** D42 + _BV( 6 ), // PL 6 ** 43 ** D43 + _BV( 5 ), // PL 5 ** 44 ** D44 + _BV( 4 ), // PL 4 ** 45 ** D45 + _BV( 3 ), // PL 3 ** 46 ** D46 + _BV( 2 ), // PL 2 ** 47 ** D47 + _BV( 1 ), // PL 1 ** 48 ** D48 + _BV( 0 ), // PL 0 ** 49 ** D49 + _BV( 3 ), // PB 3 ** 50 ** SPI_MISO + _BV( 2 ), // PB 2 ** 51 ** SPI_MOSI + _BV( 1 ), // PB 1 ** 52 ** SPI_SCK + _BV( 0 ), // PB 0 ** 53 ** SPI_SS + _BV( 0 ), // PF 0 ** 54 ** A0 + _BV( 1 ), // PF 1 ** 55 ** A1 + _BV( 2 ), // PF 2 ** 56 ** A2 + _BV( 3 ), // PF 3 ** 57 ** A3 + _BV( 4 ), // PF 4 ** 58 ** A4 + _BV( 5 ), // PF 5 ** 59 ** A5 + _BV( 6 ), // PF 6 ** 60 ** A6 + _BV( 7 ), // PF 7 ** 61 ** A7 + _BV( 0 ), // PK 0 ** 62 ** A8 + _BV( 1 ), // PK 1 ** 63 ** A9 + _BV( 2 ), // PK 2 ** 64 ** A10 + _BV( 3 ), // PK 3 ** 65 ** A11 + _BV( 4 ), // PK 4 ** 66 ** A12 + _BV( 5 ), // PK 5 ** 67 ** A13 + _BV( 6 ), // PK 6 ** 68 ** A14 + _BV( 7 ), // PK 7 ** 69 ** A15 + _BV( 4 ), // PG 4 ** 70 ** + _BV( 3 ), // PG 3 ** 71 ** + _BV( 2 ), // PJ 2 ** 72 ** + _BV( 3 ), // PJ 3 ** 73 ** + _BV( 7 ), // PJ 7 ** 74 ** + _BV( 4 ), // PJ 4 ** 75 ** + _BV( 5 ), // PJ 5 ** 76 ** + _BV( 6 ), // PJ 6 ** 77 ** + _BV( 2 ), // PE 2 ** 78 ** + _BV( 6 ), // PE 6 ** 79 ** + _BV( 7 ), // PE 7 ** 80 ** + _BV( 4 ), // PD 4 ** 81 ** + _BV( 5 ), // PD 5 ** 82 ** + _BV( 6 ), // PD 6 ** 83 ** + _BV( 2 ), // PH 2 ** 84 ** + _BV( 7 ), // PH 7 ** 85 ** }; #define digitalPinToBitMask_plus_70(P) ( pgm_read_byte( digital_pin_to_bit_mask_PGM_plus_70 + (P) ) ) @@ -234,86 +234,86 @@ const uint8_t PROGMEM digital_pin_to_bit_mask_PGM_plus_70[] = { const uint8_t PROGMEM digital_pin_to_timer_PGM_plus_70[] = { // TIMERS // ------------------------ - NOT_ON_TIMER , // PE 0 ** 0 ** USART0_RX - NOT_ON_TIMER , // PE 1 ** 1 ** USART0_TX - TIMER3B , // PE 4 ** 2 ** PWM2 - TIMER3C , // PE 5 ** 3 ** PWM3 - TIMER0B , // PG 5 ** 4 ** PWM4 - TIMER3A , // PE 3 ** 5 ** PWM5 - TIMER4A , // PH 3 ** 6 ** PWM6 - TIMER4B , // PH 4 ** 7 ** PWM7 - TIMER4C , // PH 5 ** 8 ** PWM8 - TIMER2B , // PH 6 ** 9 ** PWM9 - TIMER2A , // PB 4 ** 10 ** PWM10 - TIMER1A , // PB 5 ** 11 ** PWM11 - TIMER1B , // PB 6 ** 12 ** PWM12 - TIMER0A , // PB 7 ** 13 ** PWM13 - NOT_ON_TIMER , // PJ 1 ** 14 ** USART3_TX - NOT_ON_TIMER , // PJ 0 ** 15 ** USART3_RX - NOT_ON_TIMER , // PH 1 ** 16 ** USART2_TX - NOT_ON_TIMER , // PH 0 ** 17 ** USART2_RX - NOT_ON_TIMER , // PD 3 ** 18 ** USART1_TX - NOT_ON_TIMER , // PD 2 ** 19 ** USART1_RX - NOT_ON_TIMER , // PD 1 ** 20 ** I2C_SDA - NOT_ON_TIMER , // PD 0 ** 21 ** I2C_SCL - NOT_ON_TIMER , // PA 0 ** 22 ** D22 - NOT_ON_TIMER , // PA 1 ** 23 ** D23 - NOT_ON_TIMER , // PA 2 ** 24 ** D24 - NOT_ON_TIMER , // PA 3 ** 25 ** D25 - NOT_ON_TIMER , // PA 4 ** 26 ** D26 - NOT_ON_TIMER , // PA 5 ** 27 ** D27 - NOT_ON_TIMER , // PA 6 ** 28 ** D28 - NOT_ON_TIMER , // PA 7 ** 29 ** D29 - NOT_ON_TIMER , // PC 7 ** 30 ** D30 - NOT_ON_TIMER , // PC 6 ** 31 ** D31 - NOT_ON_TIMER , // PC 5 ** 32 ** D32 - NOT_ON_TIMER , // PC 4 ** 33 ** D33 - NOT_ON_TIMER , // PC 3 ** 34 ** D34 - NOT_ON_TIMER , // PC 2 ** 35 ** D35 - NOT_ON_TIMER , // PC 1 ** 36 ** D36 - NOT_ON_TIMER , // PC 0 ** 37 ** D37 - NOT_ON_TIMER , // PD 7 ** 38 ** D38 - NOT_ON_TIMER , // PG 2 ** 39 ** D39 - NOT_ON_TIMER , // PG 1 ** 40 ** D40 - NOT_ON_TIMER , // PG 0 ** 41 ** D41 - NOT_ON_TIMER , // PL 7 ** 42 ** D42 - NOT_ON_TIMER , // PL 6 ** 43 ** D43 - TIMER5C , // PL 5 ** 44 ** D44 - TIMER5B , // PL 4 ** 45 ** D45 - TIMER5A , // PL 3 ** 46 ** D46 - NOT_ON_TIMER , // PL 2 ** 47 ** D47 - NOT_ON_TIMER , // PL 1 ** 48 ** D48 - NOT_ON_TIMER , // PL 0 ** 49 ** D49 - NOT_ON_TIMER , // PB 3 ** 50 ** SPI_MISO - NOT_ON_TIMER , // PB 2 ** 51 ** SPI_MOSI - NOT_ON_TIMER , // PB 1 ** 52 ** SPI_SCK - NOT_ON_TIMER , // PB 0 ** 53 ** SPI_SS - NOT_ON_TIMER , // PF 0 ** 54 ** A0 - NOT_ON_TIMER , // PF 1 ** 55 ** A1 - NOT_ON_TIMER , // PF 2 ** 56 ** A2 - NOT_ON_TIMER , // PF 3 ** 57 ** A3 - NOT_ON_TIMER , // PF 4 ** 58 ** A4 - NOT_ON_TIMER , // PF 5 ** 59 ** A5 - NOT_ON_TIMER , // PF 6 ** 60 ** A6 - NOT_ON_TIMER , // PF 7 ** 61 ** A7 - NOT_ON_TIMER , // PK 0 ** 62 ** A8 - NOT_ON_TIMER , // PK 1 ** 63 ** A9 - NOT_ON_TIMER , // PK 2 ** 64 ** A10 - NOT_ON_TIMER , // PK 3 ** 65 ** A11 - NOT_ON_TIMER , // PK 4 ** 66 ** A12 - NOT_ON_TIMER , // PK 5 ** 67 ** A13 - NOT_ON_TIMER , // PK 6 ** 68 ** A14 - NOT_ON_TIMER , // PK 7 ** 69 ** A15 - NOT_ON_TIMER , // PG 4 ** 70 ** - NOT_ON_TIMER , // PG 3 ** 71 ** - NOT_ON_TIMER , // PJ 2 ** 72 ** - NOT_ON_TIMER , // PJ 3 ** 73 ** - NOT_ON_TIMER , // PJ 7 ** 74 ** - NOT_ON_TIMER , // PJ 4 ** 75 ** - NOT_ON_TIMER , // PJ 5 ** 76 ** - NOT_ON_TIMER , // PJ 6 ** 77 ** - NOT_ON_TIMER , // PE 2 ** 78 ** - NOT_ON_TIMER , // PE 6 ** 79 ** + NOT_ON_TIMER, // PE 0 ** 0 ** USART0_RX + NOT_ON_TIMER, // PE 1 ** 1 ** USART0_TX + TIMER3B, // PE 4 ** 2 ** PWM2 + TIMER3C, // PE 5 ** 3 ** PWM3 + TIMER0B, // PG 5 ** 4 ** PWM4 + TIMER3A, // PE 3 ** 5 ** PWM5 + TIMER4A, // PH 3 ** 6 ** PWM6 + TIMER4B, // PH 4 ** 7 ** PWM7 + TIMER4C, // PH 5 ** 8 ** PWM8 + TIMER2B, // PH 6 ** 9 ** PWM9 + TIMER2A, // PB 4 ** 10 ** PWM10 + TIMER1A, // PB 5 ** 11 ** PWM11 + TIMER1B, // PB 6 ** 12 ** PWM12 + TIMER0A, // PB 7 ** 13 ** PWM13 + NOT_ON_TIMER, // PJ 1 ** 14 ** USART3_TX + NOT_ON_TIMER, // PJ 0 ** 15 ** USART3_RX + NOT_ON_TIMER, // PH 1 ** 16 ** USART2_TX + NOT_ON_TIMER, // PH 0 ** 17 ** USART2_RX + NOT_ON_TIMER, // PD 3 ** 18 ** USART1_TX + NOT_ON_TIMER, // PD 2 ** 19 ** USART1_RX + NOT_ON_TIMER, // PD 1 ** 20 ** I2C_SDA + NOT_ON_TIMER, // PD 0 ** 21 ** I2C_SCL + NOT_ON_TIMER, // PA 0 ** 22 ** D22 + NOT_ON_TIMER, // PA 1 ** 23 ** D23 + NOT_ON_TIMER, // PA 2 ** 24 ** D24 + NOT_ON_TIMER, // PA 3 ** 25 ** D25 + NOT_ON_TIMER, // PA 4 ** 26 ** D26 + NOT_ON_TIMER, // PA 5 ** 27 ** D27 + NOT_ON_TIMER, // PA 6 ** 28 ** D28 + NOT_ON_TIMER, // PA 7 ** 29 ** D29 + NOT_ON_TIMER, // PC 7 ** 30 ** D30 + NOT_ON_TIMER, // PC 6 ** 31 ** D31 + NOT_ON_TIMER, // PC 5 ** 32 ** D32 + NOT_ON_TIMER, // PC 4 ** 33 ** D33 + NOT_ON_TIMER, // PC 3 ** 34 ** D34 + NOT_ON_TIMER, // PC 2 ** 35 ** D35 + NOT_ON_TIMER, // PC 1 ** 36 ** D36 + NOT_ON_TIMER, // PC 0 ** 37 ** D37 + NOT_ON_TIMER, // PD 7 ** 38 ** D38 + NOT_ON_TIMER, // PG 2 ** 39 ** D39 + NOT_ON_TIMER, // PG 1 ** 40 ** D40 + NOT_ON_TIMER, // PG 0 ** 41 ** D41 + NOT_ON_TIMER, // PL 7 ** 42 ** D42 + NOT_ON_TIMER, // PL 6 ** 43 ** D43 + TIMER5C, // PL 5 ** 44 ** D44 + TIMER5B, // PL 4 ** 45 ** D45 + TIMER5A, // PL 3 ** 46 ** D46 + NOT_ON_TIMER, // PL 2 ** 47 ** D47 + NOT_ON_TIMER, // PL 1 ** 48 ** D48 + NOT_ON_TIMER, // PL 0 ** 49 ** D49 + NOT_ON_TIMER, // PB 3 ** 50 ** SPI_MISO + NOT_ON_TIMER, // PB 2 ** 51 ** SPI_MOSI + NOT_ON_TIMER, // PB 1 ** 52 ** SPI_SCK + NOT_ON_TIMER, // PB 0 ** 53 ** SPI_SS + NOT_ON_TIMER, // PF 0 ** 54 ** A0 + NOT_ON_TIMER, // PF 1 ** 55 ** A1 + NOT_ON_TIMER, // PF 2 ** 56 ** A2 + NOT_ON_TIMER, // PF 3 ** 57 ** A3 + NOT_ON_TIMER, // PF 4 ** 58 ** A4 + NOT_ON_TIMER, // PF 5 ** 59 ** A5 + NOT_ON_TIMER, // PF 6 ** 60 ** A6 + NOT_ON_TIMER, // PF 7 ** 61 ** A7 + NOT_ON_TIMER, // PK 0 ** 62 ** A8 + NOT_ON_TIMER, // PK 1 ** 63 ** A9 + NOT_ON_TIMER, // PK 2 ** 64 ** A10 + NOT_ON_TIMER, // PK 3 ** 65 ** A11 + NOT_ON_TIMER, // PK 4 ** 66 ** A12 + NOT_ON_TIMER, // PK 5 ** 67 ** A13 + NOT_ON_TIMER, // PK 6 ** 68 ** A14 + NOT_ON_TIMER, // PK 7 ** 69 ** A15 + NOT_ON_TIMER, // PG 4 ** 70 ** + NOT_ON_TIMER, // PG 3 ** 71 ** + NOT_ON_TIMER, // PJ 2 ** 72 ** + NOT_ON_TIMER, // PJ 3 ** 73 ** + NOT_ON_TIMER, // PJ 7 ** 74 ** + NOT_ON_TIMER, // PJ 4 ** 75 ** + NOT_ON_TIMER, // PJ 5 ** 76 ** + NOT_ON_TIMER, // PJ 6 ** 77 ** + NOT_ON_TIMER, // PE 2 ** 78 ** + NOT_ON_TIMER, // PE 6 ** 79 ** }; #define digitalPinToTimer_plus_70(P) ( pgm_read_byte( digital_pin_to_timer_PGM_plus_70 + (P) ) ) diff --git a/Marlin/src/HAL/AVR/registers.h b/Marlin/src/HAL/AVR/registers.h index 64c0955c3e..0eac4888cd 100644 --- a/Marlin/src/HAL/AVR/registers.h +++ b/Marlin/src/HAL/AVR/registers.h @@ -93,15 +93,15 @@ namespace AVRHelpers { typedef T type; }; template - struct voltype { + struct voltype { typedef uint8_t type; }; template - struct voltype { + struct voltype { typedef uint16_t type; }; template - struct voltype { + struct voltype { typedef uint32_t type; }; @@ -2007,7 +2007,7 @@ inline void _ATmega_resetperipherals() { #if defined(__AVR_TRM01__) || defined(__AVR_TRM02__) || defined(__AVR_TRM03__) || defined(__AVR_TRM05__) _EEAR._EEAR = 0; - dwrite(_EEDR, (uint8_t)0u); + dwrite(_EEDR, (uint8_t)0U); #endif #if defined(__AVR_TRM01__) || defined(__AVR_TRM02__) || defined(__AVR_TRM03__) || defined(__AVR_TRM04__) || defined(__AVR_TRM05__) diff --git a/Marlin/src/HAL/AVR/spi_pins.h b/Marlin/src/HAL/AVR/spi_pins.h index 831972938a..934b6e3b14 100644 --- a/Marlin/src/HAL/AVR/spi_pins.h +++ b/Marlin/src/HAL/AVR/spi_pins.h @@ -23,43 +23,41 @@ /** * Define SPI Pins: SCK, MISO, MOSI, SS + * Platform pins have parentheses, e.g., "(53)", so we cannot use them. */ #if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328__) || defined(__AVR_ATmega328P__) - #define AVR_SCK_PIN 13 - #define AVR_MISO_PIN 12 - #define AVR_MOSI_PIN 11 - #define AVR_SS_PIN 10 + #define _PIN_SPI_SCK 13 + #define _PIN_SPI_MISO 12 + #define _PIN_SPI_MOSI 11 + #define _PIN_SPI_SS 10 #elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__) - #define AVR_SCK_PIN 7 - #define AVR_MISO_PIN 6 - #define AVR_MOSI_PIN 5 - #define AVR_SS_PIN 4 + #define _PIN_SPI_SCK 7 + #define _PIN_SPI_MISO 6 + #define _PIN_SPI_MOSI 5 + #define _PIN_SPI_SS 4 #elif defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) - #define AVR_SCK_PIN 52 - #define AVR_MISO_PIN 50 - #define AVR_MOSI_PIN 51 - #define AVR_SS_PIN 53 + #define _PIN_SPI_SCK 52 + #define _PIN_SPI_MISO 50 + #define _PIN_SPI_MOSI 51 + #define _PIN_SPI_SS 53 #elif defined(__AVR_AT90USB1287__) || defined(__AVR_AT90USB1286__) || defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB647__) - #define AVR_SCK_PIN 21 - #define AVR_MISO_PIN 23 - #define AVR_MOSI_PIN 22 - #define AVR_SS_PIN 20 + #define _PIN_SPI_SCK 21 + #define _PIN_SPI_MISO 23 + #define _PIN_SPI_MOSI 22 + #define _PIN_SPI_SS 20 #elif defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) - #define AVR_SCK_PIN 10 - #define AVR_MISO_PIN 12 - #define AVR_MOSI_PIN 11 - #define AVR_SS_PIN 16 + #define _PIN_SPI_SCK 10 + #define _PIN_SPI_MISO 12 + #define _PIN_SPI_MOSI 11 + #define _PIN_SPI_SS 16 #endif #ifndef SD_SCK_PIN - #define SD_SCK_PIN AVR_SCK_PIN + #define SD_SCK_PIN _PIN_SPI_SCK #endif #ifndef SD_MISO_PIN - #define SD_MISO_PIN AVR_MISO_PIN + #define SD_MISO_PIN _PIN_SPI_MISO #endif #ifndef SD_MOSI_PIN - #define SD_MOSI_PIN AVR_MOSI_PIN -#endif -#ifndef SD_SS_PIN - #define SD_SS_PIN AVR_SS_PIN + #define SD_MOSI_PIN _PIN_SPI_MOSI #endif diff --git a/Marlin/src/HAL/AVR/timers.h b/Marlin/src/HAL/AVR/timers.h index 94b17f3102..0e1d7f2ba3 100644 --- a/Marlin/src/HAL/AVR/timers.h +++ b/Marlin/src/HAL/AVR/timers.h @@ -28,7 +28,7 @@ // ------------------------ typedef uint16_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFU // ------------------------ // Defines diff --git a/Marlin/src/HAL/AVR/u8g_com_HAL_AVR_sw_spi.cpp b/Marlin/src/HAL/AVR/u8g/u8g_com_HAL_AVR_sw_spi.cpp similarity index 92% rename from Marlin/src/HAL/AVR/u8g_com_HAL_AVR_sw_spi.cpp rename to Marlin/src/HAL/AVR/u8g/u8g_com_HAL_AVR_sw_spi.cpp index 131174b06c..f4fa1eb428 100644 --- a/Marlin/src/HAL/AVR/u8g_com_HAL_AVR_sw_spi.cpp +++ b/Marlin/src/HAL/AVR/u8g/u8g_com_HAL_AVR_sw_spi.cpp @@ -55,12 +55,12 @@ #if defined(ARDUINO) && !defined(ARDUINO_ARCH_STM32) && !defined(ARDUINO_ARCH_SAM) -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if HAS_MARLINUI_U8GLIB -#include "../shared/Marduino.h" -#include "../shared/Delay.h" +#include "../../shared/Marduino.h" +#include "../../shared/Delay.h" #include @@ -120,7 +120,7 @@ void u8g_spiSend_sw_AVR_mode_3(uint8_t val) { U8G_ATOMIC_END(); } -#if ENABLED(FYSETC_MINI_12864) +#if U8G_SPI_USE_MODE_3 #define SPISEND_SW_AVR u8g_spiSend_sw_AVR_mode_3 #else #define SPISEND_SW_AVR u8g_spiSend_sw_AVR_mode_0 @@ -143,9 +143,9 @@ uint8_t u8g_com_HAL_AVR_sw_sp_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void break; case U8G_COM_MSG_CHIP_SELECT: - #if ENABLED(FYSETC_MINI_12864) // LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active + #if U8G_SPI_USE_MODE_3 // LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active u8g_com_arduino_digital_write(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active u8g_com_arduino_digital_write(u8g, U8G_PI_CS, LOW); } diff --git a/Marlin/src/HAL/DUE/HAL.cpp b/Marlin/src/HAL/DUE/HAL.cpp index 763091cb00..9b3cf1a516 100644 --- a/Marlin/src/HAL/DUE/HAL.cpp +++ b/Marlin/src/HAL/DUE/HAL.cpp @@ -48,7 +48,7 @@ uint16_t MarlinHAL::adc_result; void MarlinHAL::init() { #if HAS_MEDIA - OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up + OUT_WRITE(SD_SS_PIN, HIGH); // Try to set SDSS inactive before any other SPI users start up #endif usb_task_init(); // Initialize the USB stack TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler @@ -102,6 +102,10 @@ void watchdogSetup() { #if ENABLED(USE_WATCHDOG) + #ifndef WATCHDOG_PIO_RESET + #define WATCHDOG_PIO_RESET + #endif + // 4 seconds timeout uint32_t timeout = TERN(WATCHDOG_DURATION_8S, 8000, 4000); @@ -115,15 +119,16 @@ void watchdogSetup() { timeout = 0xFFF; // We want to enable the watchdog with the specified timeout - uint32_t value = - WDT_MR_WDV(timeout) | // With the specified timeout - WDT_MR_WDD(timeout) | // and no invalid write window - #if !(SAMV70 || SAMV71 || SAME70 || SAMS70) - WDT_MR_WDRPROC | // WDT fault resets processor only - We want - // to keep PIO controller state - #endif - WDT_MR_WDDBGHLT | // WDT stops in debug state. - WDT_MR_WDIDLEHLT; // WDT stops in idle state. + uint32_t value = (0 + | WDT_MR_WDV(timeout) // With the specified timeout + | WDT_MR_WDD(timeout) // and no invalid write window + #if NONE(WATCHDOG_PIO_RESET, SAMV70, SAMV71, SAME70, SAMS70) + | WDT_MR_WDRPROC // WDT fault resets processor only with this flag. + // Omit to also reset the PIO controller. + #endif + | WDT_MR_WDDBGHLT // WDT stops in debug state. + | WDT_MR_WDIDLEHLT // WDT stops in idle state. + ); #if ENABLED(WATCHDOG_RESET_MANUAL) // We enable the watchdog timer, but only for the interrupt. diff --git a/Marlin/src/HAL/DUE/HAL.h b/Marlin/src/HAL/DUE/HAL.h index 49a8be3fe7..54a977c2d8 100644 --- a/Marlin/src/HAL/DUE/HAL.h +++ b/Marlin/src/HAL/DUE/HAL.h @@ -35,67 +35,9 @@ #include -#include "../../core/serial_hook.h" - -// ------------------------ -// Serial ports -// ------------------------ - -typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; -typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; -typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; -typedef ForwardSerial1Class< decltype(Serial3) > DefaultSerial4; -extern DefaultSerial1 MSerial0; -extern DefaultSerial2 MSerial1; -extern DefaultSerial3 MSerial2; -extern DefaultSerial4 MSerial3; - -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if SERIAL_PORT == -1 || ENABLED(EMERGENCY_PARSER) - #define MYSERIAL1 customizedSerial1 -#elif WITHIN(SERIAL_PORT, 0, 3) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #error "The required SERIAL_PORT must be from 0 to 3, or -1 for USB Serial." -#endif - -#ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 || ENABLED(EMERGENCY_PARSER) - #define MYSERIAL2 customizedSerial2 - #elif WITHIN(SERIAL_PORT_2, 0, 3) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #error "SERIAL_PORT_2 must be from 0 to 3, or -1 for USB Serial." - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 || ENABLED(EMERGENCY_PARSER) - #define MYSERIAL3 customizedSerial3 - #elif WITHIN(SERIAL_PORT_3, 0, 3) - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #else - #error "SERIAL_PORT_3 must be from 0 to 3, or -1 for USB Serial." - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if WITHIN(MMU2_SERIAL_PORT, 0, 3) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #else - #error "MMU2_SERIAL_PORT must be from 0 to 3." - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if WITHIN(LCD_SERIAL_PORT, 0, 3) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #error "LCD_SERIAL_PORT must be from 0 to 3." - #endif -#endif +// +// Serial Ports +// #include "MarlinSerial.h" #include "MarlinSerialUSB.h" @@ -127,7 +69,7 @@ typedef Servo hal_servo_t; #define HAL_ADC_RESOLUTION 10 #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) + #define analogInputToDigitalPin(p) pin_t((p < 12U) ? (p) + 54U : -1) #endif // diff --git a/Marlin/src/HAL/DUE/HAL_SPI.cpp b/Marlin/src/HAL/DUE/HAL_SPI.cpp index c2fabb0d49..e44549357e 100644 --- a/Marlin/src/HAL/DUE/HAL_SPI.cpp +++ b/Marlin/src/HAL/DUE/HAL_SPI.cpp @@ -208,8 +208,8 @@ A("str %[sck_mask],[%[sck_port],#0x4]") /* CODR */ A("bfi %[bin],%[work],#0,#1") /* Store read bit as the bit 0 */ - : [bin]"+r"(bin), - [work]"+r"(work) + : [bin]"+r"( bin ), + [work]"+r"( work ) : [bitband_miso_port]"r"( BITBAND_MISO_PORT ), [sck_mask]"r"( SCK_MASK ), [sck_port]"r"( SCK_PORT_PLUS30 ) @@ -350,7 +350,7 @@ static void spiRxBlock0(uint8_t *ptr, uint32_t todo) { uint32_t bin = 0; uint32_t work = 0; - uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS( ((uint32_t)PORT(SD_MISO_PIN))+0x3C, PIN_SHIFT(SD_MISO_PIN)); /* PDSR of port in bitband area */ + uint32_t BITBAND_MISO_PORT = BITBAND_ADDRESS(((uint32_t)PORT(SD_MISO_PIN))+0x3C, PIN_SHIFT(SD_MISO_PIN)); /* PDSR of port in bitband area */ uint32_t SCK_PORT_PLUS30 = ((uint32_t) PORT(SD_SCK_PIN)) + 0x30; /* SODR of port */ uint32_t SCK_MASK = PIN_MASK(SD_SCK_PIN); @@ -412,10 +412,10 @@ A("strb.w %[bin], [%[ptr]], #1") /* Store read value into buffer, increment buffer pointer */ A("bne.n loop%=") /* Repeat until done */ - : [ptr]"+r"(ptr), - [todo]"+r"(todo), - [bin]"+r"(bin), - [work]"+r"(work) + : [ptr]"+r"( ptr ), + [todo]"+r"( todo ), + [bin]"+r"( bin ), + [work]"+r"( work ) : [bitband_miso_port]"r"( BITBAND_MISO_PORT ), [sck_mask]"r"( SCK_MASK ), [sck_port]"r"( SCK_PORT_PLUS30 ) @@ -600,9 +600,8 @@ OUT_WRITE(SPI_EEPROM1_CS_PIN, HIGH); OUT_WRITE(SPI_EEPROM2_CS_PIN, HIGH); OUT_WRITE(SPI_FLASH_CS_PIN, HIGH); - WRITE(SD_SS_PIN, HIGH); - - OUT_WRITE(SDSS, LOW); + OUT_WRITE(SD_SS_PIN, HIGH); + WRITE(SD_SS_PIN, LOW); PIO_Configure( g_APinDescription[SPI_PIN].pPort, @@ -767,7 +766,7 @@ // Disable PIO on A26 and A27 REG_PIOA_PDR = 0x0C000000; - OUT_WRITE(SDSS, HIGH); + OUT_WRITE(SD_SS_PIN, HIGH); // Reset SPI0 (from sam lib) SPI0->SPI_CR = SPI_CR_SPIDIS; diff --git a/Marlin/src/HAL/DUE/MarlinSerial.h b/Marlin/src/HAL/DUE/MarlinSerial.h index b80ae21823..cee0857d2b 100644 --- a/Marlin/src/HAL/DUE/MarlinSerial.h +++ b/Marlin/src/HAL/DUE/MarlinSerial.h @@ -33,6 +33,21 @@ #include "../../core/types.h" #include "../../core/serial_hook.h" +typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; +typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; +typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; +typedef ForwardSerial1Class< decltype(Serial3) > DefaultSerial4; +extern DefaultSerial1 MSerial0; +extern DefaultSerial2 MSerial1; +extern DefaultSerial3 MSerial2; +extern DefaultSerial4 MSerial3; + +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#define EP_SERIAL_PORT(N) customizedSerial##N +#define USB_SERIAL_PORT(N) customizedSerial##N +#include "../shared/serial_ports.h" + // Define constants and variables for buffering incoming serial data. We're // using a ring buffer (I think), in which rx_buffer_head is the index of the // location to which to write the next incoming character and rx_buffer_tail diff --git a/Marlin/src/HAL/DUE/Servo.cpp b/Marlin/src/HAL/DUE/Servo.cpp index 2dab88238d..2f72d66b9b 100644 --- a/Marlin/src/HAL/DUE/Servo.cpp +++ b/Marlin/src/HAL/DUE/Servo.cpp @@ -50,7 +50,7 @@ static Flags<_Nbr_16timers> DisablePending; // ISR should disable the timer at the next timer reset // ------------------------ -/// Interrupt handler for the TC0 channel 1. +// Interrupt handler for the TC0 channel 1. // ------------------------ void Servo_Handler(const timer16_Sequence_t, Tc*, const uint8_t); diff --git a/Marlin/src/HAL/DUE/eeprom_flash.cpp b/Marlin/src/HAL/DUE/eeprom/eeprom_flash.cpp similarity index 99% rename from Marlin/src/HAL/DUE/eeprom_flash.cpp rename to Marlin/src/HAL/DUE/eeprom/eeprom_flash.cpp index a5c7ab836d..b33d15e106 100644 --- a/Marlin/src/HAL/DUE/eeprom_flash.cpp +++ b/Marlin/src/HAL/DUE/eeprom/eeprom_flash.cpp @@ -21,7 +21,7 @@ */ #ifdef ARDUINO_ARCH_SAM -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) @@ -132,7 +132,7 @@ static uint8_t buffer[256] = {0}, // The RAM buffer to accumulate writes curGroup = 0xFF; // Current FLASH group #define DEBUG_OUT ENABLED(EE_EMU_DEBUG) -#include "../../core/debug_out.h" +#include "../../../core/debug_out.h" static void ee_Dump(const int page, const void *data) { @@ -291,7 +291,7 @@ static bool ee_PageWrite(uint16_t page, const void *data) { uint32_t *p1 = (uint32_t*)addrflash; uint32_t *p2 = (uint32_t*)data; int count = 0; - for (i =0; i> 2; i++) { + for (i = 0; i < PageSize >> 2; i++) { if (p1[i] != p2[i]) { uint32_t delta = p1[i] ^ p2[i]; while (delta) { @@ -953,7 +953,7 @@ static void ee_Init() { /* PersistentStore -----------------------------------------------------------*/ -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #define MARLIN_EEPROM_SIZE 0x1000 // 4KB diff --git a/Marlin/src/HAL/DUE/eeprom_wired.cpp b/Marlin/src/HAL/DUE/eeprom/eeprom_wired.cpp similarity index 95% rename from Marlin/src/HAL/DUE/eeprom_wired.cpp rename to Marlin/src/HAL/DUE/eeprom/eeprom_wired.cpp index 84338ccb4b..cf9233816c 100644 --- a/Marlin/src/HAL/DUE/eeprom_wired.cpp +++ b/Marlin/src/HAL/DUE/eeprom/eeprom_wired.cpp @@ -21,7 +21,7 @@ */ #ifdef ARDUINO_ARCH_SAM -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -30,8 +30,8 @@ * with simple implementations supplied by Marlin. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM." diff --git a/Marlin/src/HAL/DUE/endstop_interrupts.h b/Marlin/src/HAL/DUE/endstop_interrupts.h index 954eb625a5..798ca4f0cb 100644 --- a/Marlin/src/HAL/DUE/endstop_interrupts.h +++ b/Marlin/src/HAL/DUE/endstop_interrupts.h @@ -64,6 +64,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp index 800915ff69..745e4205bc 100644 --- a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp +++ b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp @@ -40,11 +40,12 @@ * Some jitter in the Vref signal is OK so the interrupt priority is left at its default value. */ -#include "../../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfigPre.h" #if MB(PRINTRBOARD_G2) #include "G2_PWM.h" +#include "../../../module/stepper.h" #if PIN_EXISTS(MOTOR_CURRENT_PWM_X) #define G2_PWM_X 1 @@ -56,16 +57,12 @@ #else #define G2_PWM_Y 0 #endif -#if PIN_EXISTS(MOTOR_CURRENT_PWM_Z) +#if HAS_MOTOR_CURRENT_PWM_Z #define G2_PWM_Z 1 #else #define G2_PWM_Z 0 #endif -#if PIN_EXISTS(MOTOR_CURRENT_PWM_E) - #define G2_PWM_E 1 -#else - #define G2_PWM_E 0 -#endif +#define G2_PWM_E HAS_MOTOR_CURRENT_PWM_E #define G2_MASK_X(V) (G2_PWM_X * (V)) #define G2_MASK_Y(V) (G2_PWM_Y * (V)) #define G2_MASK_Z(V) (G2_PWM_Z * (V)) @@ -80,17 +77,22 @@ PWM_map ISR_table[NUM_PWMS] = PWM_MAP_INIT; void Stepper::digipot_init() { - #if PIN_EXISTS(MOTOR_CURRENT_PWM_X) - OUT_WRITE(MOTOR_CURRENT_PWM_X_PIN, 0); // init pins + #if G2_PWM_X + OUT_WRITE(MOTOR_CURRENT_PWM_X_PIN, LOW); // init pins #endif - #if PIN_EXISTS(MOTOR_CURRENT_PWM_Y) - OUT_WRITE(MOTOR_CURRENT_PWM_Y_PIN, 0); + #if G2_PWM_Y + OUT_WRITE(MOTOR_CURRENT_PWM_Y_PIN, LOW); #endif #if G2_PWM_Z - OUT_WRITE(MOTOR_CURRENT_PWM_Z_PIN, 0); + OUT_WRITE(MOTOR_CURRENT_PWM_Z_PIN, LOW); #endif #if G2_PWM_E - OUT_WRITE(MOTOR_CURRENT_PWM_E_PIN, 0); + #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) + OUT_WRITE(MOTOR_CURRENT_PWM_E_PIN, LOW); + #endif + #if PIN_EXISTS(MOTOR_CURRENT_PWM_E0) + OUT_WRITE(MOTOR_CURRENT_PWM_E0_PIN, LOW); + #endif #endif #define WPKEY (0x50574D << 8) // “PWM” in ASCII diff --git a/Marlin/src/HAL/DUE/fastio/G2_PWM.h b/Marlin/src/HAL/DUE/fastio/G2_PWM.h index 054eb2cf80..7fb2aaf7a8 100644 --- a/Marlin/src/HAL/DUE/fastio/G2_PWM.h +++ b/Marlin/src/HAL/DUE/fastio/G2_PWM.h @@ -26,10 +26,7 @@ * PR #7500. It is hardwired for the PRINTRBOARD_G2 Motor Current needs. */ -#include "../../../inc/MarlinConfigPre.h" -#include "../../../module/stepper.h" -//C:\Users\bobku\Documents\GitHub\Marlin-Bob-2\Marlin\src\module\stepper.h -//C:\Users\bobku\Documents\GitHub\Marlin-Bob-2\Marlin\src\HAL\HAL_DUE\G2_PWM.h +#include #define PWM_PERIOD_US 100 // base repetition rate in micro seconds diff --git a/Marlin/src/HAL/DUE/inc/SanityCheck.h b/Marlin/src/HAL/DUE/inc/SanityCheck.h index 1eaa2fbd8c..599e2aa464 100644 --- a/Marlin/src/HAL/DUE/inc/SanityCheck.h +++ b/Marlin/src/HAL/DUE/inc/SanityCheck.h @@ -68,16 +68,15 @@ * Usually the hardware SPI pins are only available to the LCD. This makes the DUE hard SPI used at the same time * as the TMC2130 soft SPI the most common setup. */ -#define _IS_HW_SPI(P) (defined(TMC_SPI_##P) && (TMC_SPI_##P == SD_MOSI_PIN || TMC_SPI_##P == SD_MISO_PIN || TMC_SPI_##P == SD_SCK_PIN)) - #if HAS_MEDIA && HAS_DRIVER(TMC2130) - #if ENABLED(TMC_USE_SW_SPI) - #if DISABLED(SOFTWARE_SPI) && (_IS_HW_SPI(MOSI) || _IS_HW_SPI(MISO) || _IS_HW_SPI(SCK)) - #error "DUE hardware SPI is required but is incompatible with TMC2130 software SPI. Either disable TMC_USE_SW_SPI or use separate pins for the two SPIs." - #endif - #elif ENABLED(SOFTWARE_SPI) + #define _IS_HW_SPI(P) (defined(TMC_SPI_##P) && (TMC_SPI_##P == SD_MOSI_PIN || TMC_SPI_##P == SD_MISO_PIN || TMC_SPI_##P == SD_SCK_PIN)) + #if DISABLED(SOFTWARE_SPI) && ENABLED(TMC_USE_SW_SPI) && (_IS_HW_SPI(MOSI) || _IS_HW_SPI(MISO) || _IS_HW_SPI(SCK)) + #error "DUE hardware SPI is required but is incompatible with TMC2130 software SPI. Either disable TMC_USE_SW_SPI or use separate pins for the two SPIs." + #endif + #if ENABLED(SOFTWARE_SPI) && DISABLED(TMC_USE_SW_SPI) #error "DUE software SPI is required but is incompatible with TMC2130 hardware SPI. Enable TMC_USE_SW_SPI to fix." #endif + #undef _IS_HW_SPI #endif #if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_FREQUENCY diff --git a/Marlin/src/HAL/DUE/pinsDebug.h b/Marlin/src/HAL/DUE/pinsDebug.h index 1544853553..e9e364dcec 100644 --- a/Marlin/src/HAL/DUE/pinsDebug.h +++ b/Marlin/src/HAL/DUE/pinsDebug.h @@ -19,13 +19,26 @@ * along with this program. If not, see . * */ +#pragma once /** - * Support routines for Due - */ - -/** - * Translation of routines & variables used by pinsDebug.h + * Pins Debugging for DUE + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #include "../shared/Marduino.h" @@ -63,20 +76,20 @@ #define NUMBER_PINS_TOTAL PINS_COUNT -#define digitalRead_mod(p) extDigitalRead(p) // AVR digitalRead disabled PWM before it read the pin -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%02d"), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital -#define VALID_PIN(pin) (pin >= 0 && pin < int8_t(NUMBER_PINS_TOTAL)) -#define DIGITAL_PIN_TO_ANALOG_PIN(p) int(p - analogInputToDigitalPin(0)) -#define IS_ANALOG(P) WITHIN(P, char(analogInputToDigitalPin(0)), char(analogInputToDigitalPin(NUM_ANALOG_INPUTS - 1))) -#define pwm_status(pin) (((g_pinStatus[pin] & 0xF) == PIN_STATUS_PWM) && \ - ((g_APinDescription[pin].ulPinAttribute & PIN_ATTR_PWM) == PIN_ATTR_PWM)) +#define digitalRead_mod(P) extDigitalRead(P) // AVR digitalRead disabled PWM before it read the pin +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%02d"), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define getPinByIndex(x) pin_array[x].pin +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define digitalPinToAnalogIndex(P) int(P - analogInputToDigitalPin(0)) +#define isAnalogPin(P) WITHIN(P, pin_t(analogInputToDigitalPin(0)), pin_t(analogInputToDigitalPin(NUM_ANALOG_INPUTS - 1))) +#define pwm_status(P) (((g_pinStatus[P] & 0xF) == PIN_STATUS_PWM) && \ + ((g_APinDescription[P].ulPinAttribute & PIN_ATTR_PWM) == PIN_ATTR_PWM)) #define MULTI_NAME_PAD 14 // space needed to be pretty if not first name assigned to a pin -bool GET_PINMODE(int8_t pin) { // 1: output, 0: input +bool getValidPinMode(const pin_t pin) { // 1: output, 0: input volatile Pio* port = g_APinDescription[pin].pPort; uint32_t mask = g_APinDescription[pin].ulPin; uint8_t pin_status = g_pinStatus[pin] & 0xF; @@ -85,14 +98,14 @@ bool GET_PINMODE(int8_t pin) { // 1: output, 0: input || pwm_status(pin)); } -void pwm_details(int32_t pin) { +void printPinPWM(const int32_t pin) { if (pwm_status(pin)) { uint32_t chan = g_APinDescription[pin].ulPWMChannel; SERIAL_ECHOPGM("PWM = ", PWM_INTERFACE->PWM_CH_NUM[chan].PWM_CDTY); } } -void print_port(const pin_t) {} +void printPinPort(const pin_t) {} /** * DUE Board pin | PORT | Label diff --git a/Marlin/src/HAL/DUE/spi_pins.h b/Marlin/src/HAL/DUE/spi_pins.h index 41ac7a8b58..e289c8ced2 100644 --- a/Marlin/src/HAL/DUE/spi_pins.h +++ b/Marlin/src/HAL/DUE/spi_pins.h @@ -24,41 +24,38 @@ /** * Define SPI Pins: SCK, MISO, MOSI, SS * - * Available chip select pins for HW SPI are 4 10 52 77 + * Available chip select pins for HW SPI are 4 10 52 77 87 */ -#if SDSS == 4 || SDSS == 10 || SDSS == 52 || SDSS == 77 || SDSS == 87 - #if SDSS == 4 - #define SPI_PIN 87 - #define SPI_CHAN 1 - #elif SDSS == 10 - #define SPI_PIN 77 - #define SPI_CHAN 0 - #elif SDSS == 52 - #define SPI_PIN 86 - #define SPI_CHAN 2 - #elif SDSS == 77 - #define SPI_PIN 77 - #define SPI_CHAN 0 - #else - #define SPI_PIN 87 - #define SPI_CHAN 1 - #endif - #define SD_SCK_PIN 76 - #define SD_MISO_PIN 74 - #define SD_MOSI_PIN 75 -#else - // defaults - #define SOFTWARE_SPI - #ifndef SD_SCK_PIN - #define SD_SCK_PIN 52 - #endif - #ifndef SD_MISO_PIN - #define SD_MISO_PIN 50 - #endif - #ifndef SD_MOSI_PIN - #define SD_MOSI_PIN 51 - #endif +#if SD_SS_PIN == 4 || SD_SS_PIN == 10 || SD_SS_PIN == 52 || SD_SS_PIN == 77 || SD_SS_PIN == 87 + #define SD_SCK_PIN 76 + #define SD_MISO_PIN 74 + #define SD_MOSI_PIN 75 #endif -/* A.28, A.29, B.21, C.26, C.29 */ -#define SD_SS_PIN SDSS +#if SD_SS_PIN == 4 + #define SPI_PIN 87 + #define SPI_CHAN 1 +#elif SD_SS_PIN == 10 + #define SPI_PIN 77 + #define SPI_CHAN 0 +#elif SD_SS_PIN == 52 + #define SPI_PIN 86 + #define SPI_CHAN 2 +#elif SD_SS_PIN == 77 + #define SPI_PIN 77 + #define SPI_CHAN 0 +#elif SD_SS_PIN == 87 + #define SPI_PIN 87 + #define SPI_CHAN 1 +#else + #define SOFTWARE_SPI + #ifndef SD_SCK_PIN + #define SD_SCK_PIN 52 + #endif + #ifndef SD_MISO_PIN + #define SD_MISO_PIN 50 + #endif + #ifndef SD_MOSI_PIN + #define SD_MOSI_PIN 51 + #endif +#endif diff --git a/Marlin/src/HAL/DUE/timers.h b/Marlin/src/HAL/DUE/timers.h index db5d83a06f..9316552ff5 100644 --- a/Marlin/src/HAL/DUE/timers.h +++ b/Marlin/src/HAL/DUE/timers.h @@ -34,7 +34,7 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define HAL_TIMER_PRESCALER 2 #define HAL_TIMER_RATE ((F_CPU) / (HAL_TIMER_PRESCALER)) // frequency of timers peripherals diff --git a/Marlin/src/HAL/DUE/u8g/u8g_com_HAL_DUE_sw_spi.cpp b/Marlin/src/HAL/DUE/u8g/u8g_com_HAL_DUE_sw_spi.cpp index 68e3e74a45..a8f4dd2b03 100644 --- a/Marlin/src/HAL/DUE/u8g/u8g_com_HAL_DUE_sw_spi.cpp +++ b/Marlin/src/HAL/DUE/u8g/u8g_com_HAL_DUE_sw_spi.cpp @@ -66,7 +66,7 @@ #include -#if ENABLED(FYSETC_MINI_12864) +#if U8G_SPI_USE_MODE_3 #define SPISEND_SW_DUE u8g_spiSend_sw_DUE_mode_3 #else #define SPISEND_SW_DUE u8g_spiSend_sw_DUE_mode_0 @@ -96,15 +96,15 @@ uint8_t u8g_com_HAL_DUE_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void break; case U8G_COM_MSG_CHIP_SELECT: - #if ENABLED(FYSETC_MINI_12864) // LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active - u8g_SetPILevel_DUE(u8g, U8G_PI_SCK, 1); //set SCK to mode 3 idle state before CS goes active + #if U8G_SPI_USE_MODE_3 // LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + u8g_SetPILevel_DUE(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active u8g_SetPILevel_DUE(u8g, U8G_PI_CS, LOW); } else { u8g_SetPILevel_DUE(u8g, U8G_PI_CS, HIGH); - u8g_SetPILevel_DUE(u8g, U8G_PI_SCK, 0); //set SCK to mode 0 idle state after CS goes inactive + u8g_SetPILevel_DUE(u8g, U8G_PI_SCK, 0); // Set SCK to mode 0 idle state after CS goes inactive } #else u8g_SetPILevel_DUE(u8g, U8G_PI_CS, !arg_val); diff --git a/Marlin/src/HAL/DUE/upload_extra_script.py b/Marlin/src/HAL/DUE/upload_extra_script.py index ca12b3b54f..729b1d67de 100644 --- a/Marlin/src/HAL/DUE/upload_extra_script.py +++ b/Marlin/src/HAL/DUE/upload_extra_script.py @@ -11,7 +11,7 @@ if pioutil.is_pio_build(): if current_OS == 'Windows': - Import("env") + env = pioutil.env # Use bossac.exe on Windows env.Replace( diff --git a/Marlin/src/HAL/DUE/usb/ctrl_access.c b/Marlin/src/HAL/DUE/usb/ctrl_access.c index b766ed1273..ec0e4adc4b 100644 --- a/Marlin/src/HAL/DUE/usb/ctrl_access.c +++ b/Marlin/src/HAL/DUE/usb/ctrl_access.c @@ -63,8 +63,8 @@ #include "compiler.h" #include "preprocessor.h" #ifdef FREERTOS_USED -#include "FreeRTOS.h" -#include "semphr.h" +#include +#include #endif #include "ctrl_access.h" diff --git a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp index 65a926ff36..74cfd9b39b 100644 --- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp +++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp @@ -18,30 +18,29 @@ extern "C" { void sd_mmc_spi_mem_init() { } -Ctrl_status sd_mmc_spi_test_unit_ready() { - #ifdef DISABLE_DUE_SD_MMC - return CTRL_NO_PRESENT; - #endif - if (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted()) - return CTRL_NO_PRESENT; - return CTRL_GOOD; -} - -// NOTE: This function is defined as returning the address of the last block -// in the card, which is cardSize() - 1 -Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector) { - if (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted()) - return CTRL_NO_PRESENT; - *nb_sector = card.diskIODriver()->cardSize() - 1; - return CTRL_GOOD; +inline bool media_ready() { + return card.isMounted() && card.isInserted() && !card.isFileOpen() && !card.isStillPrinting(); } bool sd_mmc_spi_unload(bool) { return true; } bool sd_mmc_spi_wr_protect() { return false; } -bool sd_mmc_spi_removal() { - return (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted()); +bool sd_mmc_spi_removal() { return !media_ready(); } + +Ctrl_status sd_mmc_spi_test_unit_ready() { + #if ENABLED(DISABLE_DUE_SD_MMC) + return CTRL_NO_PRESENT; + #endif + return sd_mmc_spi_removal() ? CTRL_NO_PRESENT : CTRL_GOOD; +} + +// NOTE: This function is defined as returning the address of the last block +// in the card, which is cardSize() - 1 +Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector) { + if (sd_mmc_spi_removal()) return CTRL_NO_PRESENT; + *nb_sector = card.diskIODriver()->cardSize() - 1; + return CTRL_GOOD; } #if ACCESS_USB == true @@ -58,11 +57,11 @@ uint8_t sector_buf[SD_MMC_BLOCK_SIZE]; // #define DEBUG_MMC Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector) { - #ifdef DISABLE_DUE_SD_MMC + #if ENABLED(DISABLE_DUE_SD_MMC) return CTRL_NO_PRESENT; #endif - if (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted()) - return CTRL_NO_PRESENT; + + if (sd_mmc_spi_removal()) return CTRL_NO_PRESENT; #ifdef DEBUG_MMC { @@ -98,11 +97,11 @@ Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector) { } Ctrl_status sd_mmc_spi_usb_write_10(uint32_t addr, uint16_t nb_sector) { - #ifdef DISABLE_DUE_SD_MMC + #if ENABLED(DISABLE_DUE_SD_MMC) return CTRL_NO_PRESENT; #endif - if (!IS_SD_INSERTED() || IS_SD_PRINTING() || IS_SD_FILE_OPEN() || !card.isMounted()) - return CTRL_NO_PRESENT; + + if (sd_mmc_spi_removal()) return CTRL_NO_PRESENT; #ifdef DEBUG_MMC { diff --git a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h index c0d3c925e8..464d106e52 100644 --- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h +++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.h @@ -74,7 +74,7 @@ //! //! @brief This function initializes the hw/sw resources required to drive the SD_MMC_SPI. //!/ -extern void sd_mmc_spi_mem_init(void); +void sd_mmc_spi_mem_init(); //! //! @brief This function tests the state of the SD_MMC memory and sends it to the Host. @@ -87,7 +87,7 @@ extern void sd_mmc_spi_mem_init(void); //! Media not present -> CTRL_NO_PRESENT //! Media has changed -> CTRL_BUSY //!/ -extern Ctrl_status sd_mmc_spi_test_unit_ready(void); +Ctrl_status sd_mmc_spi_test_unit_ready(); //! //! @brief This function gives the address of the last valid sector. @@ -98,7 +98,7 @@ extern Ctrl_status sd_mmc_spi_test_unit_ready(void); //! Media ready -> CTRL_GOOD //! Media not present -> CTRL_NO_PRESENT //!/ -extern Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector); +Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector); /*! \brief Unload/Load the SD/MMC card selected * @@ -109,7 +109,7 @@ extern Ctrl_status sd_mmc_spi_read_capacity(uint32_t *nb_sector); * * \return \c true if unload/load done success. */ -extern bool sd_mmc_spi_unload(bool unload); +bool sd_mmc_spi_unload(bool unload); //! //! @brief This function returns the write protected status of the memory. @@ -120,14 +120,14 @@ extern bool sd_mmc_spi_unload(bool unload); //! //! @return false -> the memory is not write-protected (always) //!/ -extern bool sd_mmc_spi_wr_protect(void); +bool sd_mmc_spi_wr_protect(); //! //! @brief This function tells if the memory has been removed or not. //! //! @return false -> The memory isn't removed //! -extern bool sd_mmc_spi_removal(void); +bool sd_mmc_spi_removal(); //---- ACCESS DATA FUNCTIONS ---- @@ -147,7 +147,7 @@ extern bool sd_mmc_spi_removal(void); //! It is ready -> CTRL_GOOD //! A error occur -> CTRL_FAIL //! -extern Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector); +Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector); //! This function initializes the SD/MMC memory for a write operation //! @@ -161,7 +161,7 @@ extern Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector); //! It is ready -> CTRL_GOOD //! An error occurs -> CTRL_FAIL //! -extern Ctrl_status sd_mmc_spi_usb_write_10(uint32_t addr, uint16_t nb_sector); +Ctrl_status sd_mmc_spi_usb_write_10(uint32_t addr, uint16_t nb_sector); #endif // #if ACCESS_USB == true diff --git a/Marlin/src/HAL/DUE/usb/udc.h b/Marlin/src/HAL/DUE/usb/udc.h index aba08d956e..e8c0e7fbea 100644 --- a/Marlin/src/HAL/DUE/usb/udc.h +++ b/Marlin/src/HAL/DUE/usb/udc.h @@ -229,7 +229,7 @@ usb_iface_desc_t UDC_DESC_STORAGE *udc_get_interface_desc(void); * - USB Device Controller (UDC) provides USB chapter 9 compliance * - USB Device Interface (UDI) provides USB Class compliance * - USB Device Driver (UDD) provides USB Driver for each Atmel MCU - + * * Many USB Device applications can be implemented on Atmel MCU. * Atmel provides many application notes for different applications: * - AVR4900, provides general information about Device Stack diff --git a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c index 01dda7e7fe..5635f2ba0c 100644 --- a/Marlin/src/HAL/DUE/usb/uotghs_device_due.c +++ b/Marlin/src/HAL/DUE/usb/uotghs_device_due.c @@ -523,7 +523,7 @@ static bool udd_ep_interrupt(void); * \internal * \brief Function called by UOTGHS interrupt to manage USB Device interrupts * - * USB Device interrupt events are splited in three parts: + * USB Device interrupt events are split in three parts: * - USB line events (SOF, reset, suspend, resume, wakeup) * - control endpoint events (setup reception, end of data transfer, underflow, overflow, stall) * - bulk/interrupt/isochronous endpoints events (end of data transfer) @@ -1567,7 +1567,7 @@ static void udd_ctrl_out_received(void) udd_ctrl_payload_buf_cnt))) { // End of reception because it is a short packet // Before send ZLP, call intermediate callback - // in case of data receiv generate a stall + // in case of data receive generate a stall udd_g_ctrlreq.payload_size = udd_ctrl_payload_buf_cnt; if (NULL != udd_g_ctrlreq.over_under_run) { if (!udd_g_ctrlreq.over_under_run()) { @@ -1808,7 +1808,7 @@ static void udd_ep_trans_done(udd_ep_id_t ep) } if (ptr_job->buf_cnt != ptr_job->buf_size) { - // Need to send or receiv other data + // Need to send or receive other data next_trans = ptr_job->buf_size - ptr_job->buf_cnt; if (UDD_ENDPOINT_MAX_TRANS < next_trans) { diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp index 4890972b01..c48aee0e21 100644 --- a/Marlin/src/HAL/ESP32/HAL.cpp +++ b/Marlin/src/HAL/ESP32/HAL.cpp @@ -34,13 +34,13 @@ #if ENABLED(WIFISUPPORT) #include - #include "wifi.h" + #include "wifi/wifi.h" #if ENABLED(OTASUPPORT) - #include "ota.h" + #include "wifi/ota.h" #endif #if ENABLED(WEBSUPPORT) - #include "spiffs.h" - #include "web.h" + #include "wifi/spiffs.h" + #include "wifi/web.h" #endif #endif @@ -175,8 +175,6 @@ uint8_t MarlinHAL::get_reset_source() { return rtc_get_reset_reason(1); } void MarlinHAL::reboot() { ESP.restart(); } -void _delay_ms(const int ms) { delay(ms); } - // return free memory between end of heap (or end bss) and whatever is current int MarlinHAL::freeMemory() { return ESP.getFreeHeap(); } @@ -209,16 +207,17 @@ int MarlinHAL::freeMemory() { return ESP.getFreeHeap(); } // ADC // ------------------------ -#define ADC1_CHANNEL(pin) ADC1_GPIO ## pin ## _CHANNEL - +// https://docs.espressif.com/projects/esp-idf/en/release-v4.4/esp32/api-reference/peripherals/adc.html adc1_channel_t get_channel(int pin) { switch (pin) { - case 39: return ADC1_CHANNEL(39); - case 36: return ADC1_CHANNEL(36); - case 35: return ADC1_CHANNEL(35); - case 34: return ADC1_CHANNEL(34); - case 33: return ADC1_CHANNEL(33); - case 32: return ADC1_CHANNEL(32); + case 39: return ADC1_CHANNEL_3; + case 36: return ADC1_CHANNEL_0; + case 35: return ADC1_CHANNEL_7; + case 34: return ADC1_CHANNEL_6; + case 33: return ADC1_CHANNEL_5; + case 32: return ADC1_CHANNEL_4; + case 37: return ADC1_CHANNEL_1; + case 38: return ADC1_CHANNEL_2; } return ADC1_CHANNEL_MAX; } @@ -243,12 +242,13 @@ void MarlinHAL::adc_init() { TERN_(HAS_TEMP_ADC_5, adc1_set_attenuation(get_channel(TEMP_5_PIN), ADC_ATTEN_11db)); TERN_(HAS_TEMP_ADC_6, adc2_set_attenuation(get_channel(TEMP_6_PIN), ADC_ATTEN_11db)); TERN_(HAS_TEMP_ADC_7, adc3_set_attenuation(get_channel(TEMP_7_PIN), ADC_ATTEN_11db)); - TERN_(HAS_HEATED_BED, adc1_set_attenuation(get_channel(TEMP_BED_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_CHAMBER, adc1_set_attenuation(get_channel(TEMP_CHAMBER_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_PROBE, adc1_set_attenuation(get_channel(TEMP_PROBE_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_COOLER, adc1_set_attenuation(get_channel(TEMP_COOLER_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_BOARD, adc1_set_attenuation(get_channel(TEMP_BOARD_PIN), ADC_ATTEN_11db)); - TERN_(FILAMENT_WIDTH_SENSOR, adc1_set_attenuation(get_channel(FILWIDTH_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_BED, adc1_set_attenuation(get_channel(TEMP_BED_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_CHAMBER, adc1_set_attenuation(get_channel(TEMP_CHAMBER_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_PROBE, adc1_set_attenuation(get_channel(TEMP_PROBE_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_COOLER, adc1_set_attenuation(get_channel(TEMP_COOLER_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_BOARD, adc1_set_attenuation(get_channel(TEMP_BOARD_PIN), ADC_ATTEN_11db)); + TERN_(HAS_FILWIDTH_ADC, adc1_set_attenuation(get_channel(FILWIDTH_PIN), ADC_ATTEN_11db)); + TERN_(HAS_FILWIDTH2_ADC, adc1_set_attenuation(get_channel(FILWIDTH2_PIN), ADC_ATTEN_11db)); // Note that adc2 is shared with the WiFi module, which has higher priority, so the conversion may fail. // That's why we're not setting it up here. diff --git a/Marlin/src/HAL/ESP32/HAL.h b/Marlin/src/HAL/ESP32/HAL.h index 0acb3676a2..36b8ea53fc 100644 --- a/Marlin/src/HAL/ESP32/HAL.h +++ b/Marlin/src/HAL/ESP32/HAL.h @@ -37,11 +37,11 @@ #include "i2s.h" #if ENABLED(WIFISUPPORT) - #include "WebSocketSerial.h" + #include "wifi/WebSocketSerial.h" #endif #if ENABLED(ESP3D_WIFISUPPORT) - #include "esp3dlib.h" + #include #endif #include "FlushableHardwareSerial.h" @@ -64,10 +64,10 @@ #define CRITICAL_SECTION_END() portEXIT_CRITICAL(&hal.spinlock) #define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment -#define PWM_FREQUENCY 1000u // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency() -#define PWM_RESOLUTION 10u // Default PWM bit resolution -#define CHANNEL_MAX_NUM 15u // max PWM channel # to allocate (7 to only use low speed, 15 to use low & high) -#define MAX_PWM_IOPIN 33u // hardware pwm pins < 34 +#define PWM_FREQUENCY 1000U // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency() +#define PWM_RESOLUTION 10U // Default PWM bit resolution +#define CHANNEL_MAX_NUM 15U // max PWM channel # to allocate (7 to only use low speed, 15 to use low & high) +#define MAX_PWM_IOPIN 33U // hardware pwm pins < 34 #ifndef MAX_EXPANDER_BITS #define MAX_EXPANDER_BITS 32 // I2S expander bit width (max 32) #endif @@ -165,8 +165,6 @@ int freeMemory(); #pragma GCC diagnostic pop -void _delay_ms(const int ms); - // ------------------------ // MarlinHAL Class // ------------------------ @@ -194,7 +192,7 @@ public: static void isr_on() { if (spinlock.owner != portMUX_FREE_VAL) portEXIT_CRITICAL(&spinlock); } static void isr_off() { portENTER_CRITICAL(&spinlock); } - static void delay_ms(const int ms) { _delay_ms(ms); } + static void delay_ms(const int ms) { delay(ms); } // Tasks, called from idle() static void idletask(); diff --git a/Marlin/src/HAL/ESP32/Servo.cpp b/Marlin/src/HAL/ESP32/Servo.cpp index ca3950d07f..3a2c11832d 100644 --- a/Marlin/src/HAL/ESP32/Servo.cpp +++ b/Marlin/src/HAL/ESP32/Servo.cpp @@ -35,7 +35,7 @@ Servo::Servo() {} int8_t Servo::attach(const int inPin) { if (inPin > 0) pin = inPin; - channel = get_pwm_channel(pin, 50u, 16u); + channel = get_pwm_channel(pin, 50U, 16U); return channel; // -1 if no PWM avail. } diff --git a/Marlin/src/HAL/ESP32/endstop_interrupts.h b/Marlin/src/HAL/ESP32/endstop_interrupts.h index 1377345992..8b88f1f41f 100644 --- a/Marlin/src/HAL/ESP32/endstop_interrupts.h +++ b/Marlin/src/HAL/ESP32/endstop_interrupts.h @@ -59,6 +59,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/ESP32/fastio.h b/Marlin/src/HAL/ESP32/fastio.h index c8e3f7e343..a85423d768 100644 --- a/Marlin/src/HAL/ESP32/fastio.h +++ b/Marlin/src/HAL/ESP32/fastio.h @@ -37,6 +37,10 @@ // Set pin as output #define _SET_OUTPUT(IO) pinMode(IO, OUTPUT) +// TODO: Store set modes in an array and use those to get the mode +#define _IS_OUTPUT(IO) true +#define _IS_INPUT(IO) true + // Set pin as input with pullup mode #define _PULLUP(IO, v) pinMode(IO, v ? INPUT_PULLUP : INPUT) @@ -70,6 +74,9 @@ // Set pin as output and init #define OUT_WRITE(IO,V) do{ _SET_OUTPUT(IO); WRITE(IO,V); }while(0) +#define IS_OUTPUT(IO) _IS_OUTPUT(IO) +#define IS_INPUT(IO) _IS_INPUT(IO) + // digitalRead/Write wrappers #define extDigitalRead(IO) digitalRead(IO) #define extDigitalWrite(IO,V) digitalWrite(IO,V) diff --git a/Marlin/src/HAL/ESP32/i2s.cpp b/Marlin/src/HAL/ESP32/i2s.cpp index 5404c3e9e2..6aeeb0e3dc 100644 --- a/Marlin/src/HAL/ESP32/i2s.cpp +++ b/Marlin/src/HAL/ESP32/i2s.cpp @@ -145,14 +145,14 @@ static void IRAM_ATTR i2s_intr_handler_default(void *arg) { void stepperTask(void *parameter) { uint32_t nextMainISR = 0; #if ENABLED(LIN_ADVANCE) - uint32_t nextAdvanceISR = Stepper::LA_ADV_NEVER; + uint32_t nextAdvanceISR = stepper.LA_ADV_NEVER; #endif for (;;) { xQueueReceive(dma.queue, &dma.current, portMAX_DELAY); dma.rw_pos = 0; - const bool using_ftMotion = TERN0(FT_MOTION, ftMotion.cfg.mode); + const bool using_ftMotion = TERN0(FT_MOTION, ftMotion.cfg.active); while (dma.rw_pos < DMA_SAMPLE_COUNT) { @@ -167,13 +167,13 @@ void stepperTask(void *parameter) { if (!using_ftMotion) { if (!nextMainISR) { - Stepper::pulse_phase_isr(); - nextMainISR = Stepper::block_phase_isr(); + stepper.pulse_phase_isr(); + nextMainISR = stepper.block_phase_isr(); } #if ENABLED(LIN_ADVANCE) else if (!nextAdvanceISR) { - Stepper::advance_isr(); - nextAdvanceISR = Stepper::la_interval; + stepper.advance_isr(); + nextAdvanceISR = stepper.la_interval; } #endif else @@ -182,10 +182,10 @@ void stepperTask(void *parameter) { nextMainISR--; #if ENABLED(LIN_ADVANCE) - if (nextAdvanceISR == Stepper::LA_ADV_NEVER) - nextAdvanceISR = Stepper::la_interval; + if (nextAdvanceISR == stepper.LA_ADV_NEVER) + nextAdvanceISR = stepper.la_interval; - if (nextAdvanceISR && nextAdvanceISR != Stepper::LA_ADV_NEVER) + if (nextAdvanceISR && nextAdvanceISR != stepper.LA_ADV_NEVER) nextAdvanceISR--; #endif } diff --git a/Marlin/src/HAL/ESP32/pinsDebug.h b/Marlin/src/HAL/ESP32/pinsDebug.h new file mode 100644 index 0000000000..42304b2a0b --- /dev/null +++ b/Marlin/src/HAL/ESP32/pinsDebug.h @@ -0,0 +1,71 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#error "PINS_DEBUGGING is not yet supported for ESP32!" + +/** + * Pins Debugging for ESP32 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) + */ + +#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS +#define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin + +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%02d"), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define getPinByIndex(x) pin_array[x].pin +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define digitalPinToAnalogIndex(P) int(P - analogInputToDigitalPin(0)) +#define isAnalogPin(P) WITHIN(P, pin_t(analogInputToDigitalPin(0)), pin_t(analogInputToDigitalPin(NUM_ANALOG_INPUTS - 1))) +bool pwm_status(const pin_t) { return false; } + +void printPinPort(const pin_t) {} + +static bool getValidPinMode(const pin_t pin) { + return isValidPin(pin) && !IS_INPUT(pin); +} + +void printPinPWM(const int32_t pin) { + if (pwm_status(pin)) { + //uint32_t chan = g_APinDescription[pin].ulPWMChannel TODO when fast pwm is operative; + //SERIAL_ECHOPGM("PWM = ", duty); + } +} diff --git a/Marlin/src/HAL/ESP32/spi_pins.h b/Marlin/src/HAL/ESP32/spi_pins.h index 58881f0ea7..b50ea725ab 100644 --- a/Marlin/src/HAL/ESP32/spi_pins.h +++ b/Marlin/src/HAL/ESP32/spi_pins.h @@ -21,7 +21,16 @@ */ #pragma once -#define SD_SS_PIN SDSS -#define SD_SCK_PIN 18 -#define SD_MISO_PIN 19 -#define SD_MOSI_PIN 23 +#define PIN_SPI_SCK 18 +#define PIN_SPI_MISO 19 +#define PIN_SPI_MOSI 23 + +#ifndef SD_SCK_PIN + #define SD_SCK_PIN PIN_SPI_SCK +#endif +#ifndef SD_MISO_PIN + #define SD_MISO_PIN PIN_SPI_MISO +#endif +#ifndef SD_MOSI_PIN + #define SD_MOSI_PIN PIN_SPI_MOSI +#endif diff --git a/Marlin/src/HAL/ESP32/timers.cpp b/Marlin/src/HAL/ESP32/timers.cpp index a6893150b6..a2996a860f 100644 --- a/Marlin/src/HAL/ESP32/timers.cpp +++ b/Marlin/src/HAL/ESP32/timers.cpp @@ -78,8 +78,8 @@ void IRAM_ATTR timer_isr(void *para) { /** * Enable and initialize the timer - * @param timer_num timer number to initialize - * @param frequency frequency of the timer + * @param timer_num timer number to initialize + * @param frequency frequency of the timer */ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { const tTimerConfig timer = timer_config[timer_num]; @@ -90,7 +90,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { config.counter_en = TIMER_PAUSE; config.alarm_en = TIMER_ALARM_EN; config.intr_type = TIMER_INTR_LEVEL; - config.auto_reload = true; + config.auto_reload = TIMER_AUTORELOAD_EN; // Select and initialize the timer timer_init(timer.group, timer.idx, &config); diff --git a/Marlin/src/HAL/ESP32/timers.h b/Marlin/src/HAL/ESP32/timers.h index aa4e1551f0..03c343af98 100644 --- a/Marlin/src/HAL/ESP32/timers.h +++ b/Marlin/src/HAL/ESP32/timers.h @@ -30,7 +30,7 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint64_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFFFFFFFFFULL +#define HAL_TIMER_TYPE_MAX 0xFFFF'FFFF'FFFF'FFFFULL #ifndef MF_TIMER_STEP #define MF_TIMER_STEP 0 // Timer Index for Stepper @@ -52,13 +52,12 @@ typedef uint64_t hal_timer_t; #if ENABLED(I2S_STEPPER_STREAM) #define STEPPER_TIMER_PRESCALE 1 - #define STEPPER_TIMER_RATE 250000 // 250khz, 4µs pulses of i2s word clock - #define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs // wrong would be 0.25 + #define STEPPER_TIMER_RATE 250'000 // 250khz, 4µs pulses of i2s word clock #else #define STEPPER_TIMER_PRESCALE 40 #define STEPPER_TIMER_RATE ((HAL_TIMER_RATE) / (STEPPER_TIMER_PRESCALE)) // frequency of stepper timer, 2MHz - #define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs #endif +#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1'000'000) // stepper timer ticks per µs #define STEP_TIMER_MIN_INTERVAL 8 // minimum time in µs between stepper interrupts diff --git a/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp index a4c1980adc..42903da9d3 100644 --- a/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp +++ b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp @@ -35,7 +35,7 @@ #if HAS_MEDIA #include "../../sd/cardreader.h" #if ENABLED(ESP3D_WIFISUPPORT) - #include "sd_ESP32.h" + #include #endif #endif diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp b/Marlin/src/HAL/ESP32/wifi/WebSocketSerial.cpp similarity index 99% rename from Marlin/src/HAL/ESP32/WebSocketSerial.cpp rename to Marlin/src/HAL/ESP32/wifi/WebSocketSerial.cpp index eb5b9d6039..9c8933289d 100644 --- a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp +++ b/Marlin/src/HAL/ESP32/wifi/WebSocketSerial.cpp @@ -21,7 +21,7 @@ */ #ifdef ARDUINO_ARCH_ESP32 -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if ENABLED(WIFISUPPORT) diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.h b/Marlin/src/HAL/ESP32/wifi/WebSocketSerial.h similarity index 96% rename from Marlin/src/HAL/ESP32/WebSocketSerial.h rename to Marlin/src/HAL/ESP32/wifi/WebSocketSerial.h index 6b3e419d10..3d2fdf1e6a 100644 --- a/Marlin/src/HAL/ESP32/WebSocketSerial.h +++ b/Marlin/src/HAL/ESP32/wifi/WebSocketSerial.h @@ -21,8 +21,8 @@ */ #pragma once -#include "../../inc/MarlinConfig.h" -#include "../../core/serial_hook.h" +#include "../../../inc/MarlinConfig.h" +#include "../../../core/serial_hook.h" #include diff --git a/Marlin/src/HAL/ESP32/ota.cpp b/Marlin/src/HAL/ESP32/wifi/ota.cpp similarity index 96% rename from Marlin/src/HAL/ESP32/ota.cpp rename to Marlin/src/HAL/ESP32/wifi/ota.cpp index 01f5924871..03508840a5 100644 --- a/Marlin/src/HAL/ESP32/ota.cpp +++ b/Marlin/src/HAL/ESP32/wifi/ota.cpp @@ -22,11 +22,15 @@ #ifdef ARDUINO_ARCH_ESP32 -#include "../../inc/MarlinConfigPre.h" +#include + +#undef ENABLED +#undef DISABLED + +#include "../../../inc/MarlinConfigPre.h" #if ALL(WIFISUPPORT, OTASUPPORT) -#include #include #include #include diff --git a/Marlin/src/HAL/ESP32/ota.h b/Marlin/src/HAL/ESP32/wifi/ota.h similarity index 100% rename from Marlin/src/HAL/ESP32/ota.h rename to Marlin/src/HAL/ESP32/wifi/ota.h diff --git a/Marlin/src/HAL/ESP32/spiffs.cpp b/Marlin/src/HAL/ESP32/wifi/spiffs.cpp similarity index 94% rename from Marlin/src/HAL/ESP32/spiffs.cpp rename to Marlin/src/HAL/ESP32/wifi/spiffs.cpp index 043ad7849a..ba891acda9 100644 --- a/Marlin/src/HAL/ESP32/spiffs.cpp +++ b/Marlin/src/HAL/ESP32/wifi/spiffs.cpp @@ -21,11 +21,11 @@ */ #ifdef ARDUINO_ARCH_ESP32 -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if ALL(WIFISUPPORT, WEBSUPPORT) -#include "../../core/serial.h" +#include "../../../core/serial.h" #include #include diff --git a/Marlin/src/HAL/ESP32/spiffs.h b/Marlin/src/HAL/ESP32/wifi/spiffs.h similarity index 100% rename from Marlin/src/HAL/ESP32/spiffs.h rename to Marlin/src/HAL/ESP32/wifi/spiffs.h diff --git a/Marlin/src/HAL/ESP32/web.cpp b/Marlin/src/HAL/ESP32/wifi/web.cpp similarity index 94% rename from Marlin/src/HAL/ESP32/web.cpp rename to Marlin/src/HAL/ESP32/wifi/web.cpp index 63a101595f..f08dfd87c0 100644 --- a/Marlin/src/HAL/ESP32/web.cpp +++ b/Marlin/src/HAL/ESP32/wifi/web.cpp @@ -21,11 +21,11 @@ */ #ifdef ARDUINO_ARCH_ESP32 -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if ALL(WIFISUPPORT, WEBSUPPORT) -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #undef DISABLED // esp32-hal-gpio.h #include diff --git a/Marlin/src/HAL/ESP32/web.h b/Marlin/src/HAL/ESP32/wifi/web.h similarity index 100% rename from Marlin/src/HAL/ESP32/web.h rename to Marlin/src/HAL/ESP32/wifi/web.h diff --git a/Marlin/src/HAL/ESP32/wifi.cpp b/Marlin/src/HAL/ESP32/wifi/wifi.cpp similarity index 96% rename from Marlin/src/HAL/ESP32/wifi.cpp rename to Marlin/src/HAL/ESP32/wifi/wifi.cpp index 060f3bdb48..1f31ca1bb9 100644 --- a/Marlin/src/HAL/ESP32/wifi.cpp +++ b/Marlin/src/HAL/ESP32/wifi/wifi.cpp @@ -21,11 +21,12 @@ */ #ifdef ARDUINO_ARCH_ESP32 -#include "../../core/serial.h" -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if ENABLED(WIFISUPPORT) +#include "../../../core/serial.h" + #include #include #include diff --git a/Marlin/src/HAL/ESP32/wifi.h b/Marlin/src/HAL/ESP32/wifi/wifi.h similarity index 100% rename from Marlin/src/HAL/ESP32/wifi.h rename to Marlin/src/HAL/ESP32/wifi/wifi.h diff --git a/Marlin/src/HAL/GD32_MFL/HAL.cpp b/Marlin/src/HAL/GD32_MFL/HAL.cpp new file mode 100644 index 0000000000..9ba20784f0 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/HAL.cpp @@ -0,0 +1,123 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfig.h" +#include "../shared/Delay.h" + +uint16_t MarlinHAL::adc_result; + +#if ENABLED(POSTMORTEM_DEBUGGING) + extern void install_min_serial(); +#endif + +#if ENABLED(MARLIN_DEV_MODE) + // Dump the clock frequencies of the system, AHB, APB1, APB2, and F_CPU. + static inline void HAL_clock_frequencies_dump() { + auto& rcuInstance = rcu::RCU::get_instance(); + uint32_t freq = rcuInstance.get_clock_frequency(rcu::Clock_Frequency::CK_SYS); + SERIAL_ECHOPGM("\nSYSTEM_CLOCK=", freq); + freq = rcuInstance.get_clock_frequency(rcu::Clock_Frequency::CK_AHB); + SERIAL_ECHOPGM("\nABH_CLOCK=", freq); + freq = rcuInstance.get_clock_frequency(rcu::Clock_Frequency::CK_APB1); + SERIAL_ECHOPGM("\nAPB1_CLOCK=", freq); + freq = rcuInstance.get_clock_frequency(rcu::Clock_Frequency::CK_APB2); + SERIAL_ECHOPGM("\nAPB2_CLOCK=", freq, + "\nF_CPU=", F_CPU); + // Done + SERIAL_ECHOPGM("\n--\n"); + } +#endif // MARLIN_DEV_MODE + +// Initializes the Marlin HAL +void MarlinHAL::init() { + // Ensure F_CPU is a constant expression. + // If the compiler breaks here, it means that delay code that should compute at compile time will not work. + // So better safe than sorry here. + constexpr unsigned int cpuFreq = F_CPU; + UNUSED(cpuFreq); + + #if PIN_EXISTS(LED) + OUT_WRITE(LED_PIN, LOW); + #endif + + SetTimerInterruptPriorities(); + + // Print clock frequencies to host serial + TERN_(MARLIN_DEV_MODE, HAL_clock_frequencies_dump()); + + // Register min serial + TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); +} + +// Returns the reset source based on the flags set in the RCU module +uint8_t MarlinHAL::get_reset_source() { + return + (RCU_I.get_flag(rcu::Status_Flags::FLAG_FWDGTRST)) ? RST_WATCHDOG : + (RCU_I.get_flag(rcu::Status_Flags::FLAG_SWRST)) ? RST_SOFTWARE : + (RCU_I.get_flag(rcu::Status_Flags::FLAG_EPRST)) ? RST_EXTERNAL : + (RCU_I.get_flag(rcu::Status_Flags::FLAG_PORRST)) ? RST_POWER_ON : + (RCU_I.get_flag(rcu::Status_Flags::FLAG_LPRST)) ? RST_BROWN_OUT : + 0; +} + +// Returns the amount of free memory available in bytes +int MarlinHAL::freeMemory() { + volatile char top; + return &top - reinterpret_cast(_sbrk(0)); +} + +// Watchdog Timer +#if ENABLED(USE_WATCHDOG) + #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout + + #include + + FWatchdogTimer& watchdogTimer = FWatchdogTimer::get_instance(); + + // Initializes the watchdog timer + void MarlinHAL::watchdog_init() { + IF_DISABLED(DISABLE_WATCHDOG_INIT, watchdogTimer.begin(WDT_TIMEOUT_US)); + } + + // Refreshes the watchdog timer to prevent system reset + void MarlinHAL::watchdog_refresh() { + watchdogTimer.reload(); + #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) + TOGGLE(LED_PIN); // Heartbeat indicator + #endif + } +#endif + +extern "C" { + extern unsigned int _ebss; // End of bss section +} + +// Resets the system to initiate a firmware flash. +WEAK void flashFirmware(const int16_t) { + hal.reboot(); +} + +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/HAL.h b/Marlin/src/HAL/GD32_MFL/HAL.h new file mode 100644 index 0000000000..56e52b53b5 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/HAL.h @@ -0,0 +1,160 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#define CPU_32_BIT + +#include "../../core/macros.h" +#include "../shared/Marduino.h" +#include "../shared/math_32bit.h" +#include "../shared/HAL_SPI.h" + +#include "temp_soc.h" +#include "fastio.h" +#include "Servo.h" + +#include "../../inc/MarlinConfigPre.h" + +#include +#include +#include + +// Default graphical display delays +#define CPU_ST7920_DELAY_1 300 +#define CPU_ST7920_DELAY_2 40 +#define CPU_ST7920_DELAY_3 340 + +// Serial Ports +#include "MarlinSerial.h" + +// Interrupts +#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() + +#define cli() __disable_irq() +#define sei() __enable_irq() + +// Alias of __bss_end__ +#define __bss_end __bss_end__ + +// Types +typedef double isr_float_t; // FPU ops are used for single-precision, so use double for ISRs. +typedef uint8_t pin_t; // Parity with mfl platform + +// Servo +class libServo; +typedef libServo hal_servo_t; +#define PAUSE_SERVO_OUTPUT() libServo::pause_all_servos() +#define RESUME_SERVO_OUTPUT() libServo::resume_all_servos() + +// Debugging +#define JTAG_DISABLE() AFIO_I.set_remap(gpio::Pin_Remap_Select::SWJ_DP_ONLY_REMAP) +#define JTAGSWD_DISABLE() AFIO_I.set_remap(gpio::Pin_Remap_Select::SWJ_ALL_DISABLED_REMAP) +#define JTAGSWD_RESET() AFIO_I.set_remap(gpio::Pin_Remap_Select::FULL_SWJ_REMAP) + +// ADC +#ifdef ADC_RESOLUTION + #define HAL_ADC_RESOLUTION ADC_RESOLUTION +#else + #define HAL_ADC_RESOLUTION 12 +#endif + +#define HAL_ADC_VREF_MV 3300 + +// Disable Marlin's software oversampling. +// The MFL framework uses 16x hardware oversampling by default +#ifdef GD32F303RE + #define HAL_ADC_FILTERED +#endif + +#define GET_PIN_MAP_PIN(index) index +#define GET_PIN_MAP_INDEX(pin) pin +#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + +#ifndef PLATFORM_M997_SUPPORT + #define PLATFORM_M997_SUPPORT +#endif + +void flashFirmware(const int16_t); + +#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment + +extern "C" char* _sbrk(int incr); +extern "C" char* dtostrf(double val, signed char width, unsigned char prec, char* sout); + +// MarlinHAL Class +class MarlinHAL { +public: + // Before setup() + MarlinHAL() = default; + + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); + + static void init(); // called early in setup() + static void init_board() {} // called less early in setup() + static void reboot() { NVIC_SystemReset(); } // restart the firmware from 0x0 + + // Interrupts + static bool isr_state() { return !__get_PRIMASK(); } + static void isr_on() { sei(); } + static void isr_off() { cli(); } + static void delay_ms(const int ms) { delay(ms); } + + // Tasks called from idle() + static void idletask() {} + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source() { RCU_I.clear_all_reset_flags(); } + + // Free SRAM + static int freeMemory(); + + // ADC methods + static uint16_t adc_result; + + // Called by Temperature::init once at startup + static void adc_init() { analogReadResolution(HAL_ADC_RESOLUTION); } + + // Called by Temperature::init for each sensor at startup + static void adc_enable(const pin_t pin) { pinMode(pin, INPUT); } + + // Called from Temperature::isr to start ADC sampling on the given pin + static void adc_start(const pin_t pin) { adc_result = static_cast(analogRead(pin)); } + + // Check if ADC is ready for reading + static bool adc_ready() { return true; } + + // Current value of the ADC register + static uint16_t adc_value() { return adc_result; } + + // Set the PWM duty cycle for the pin to the given value. + // Optionally invert the duty cycle [default = false] + // Optionally change the maximum size of the provided value to enable finer PWM duty control [default = 255] + static void set_pwm_duty(const pin_t pin, const uint16_t value, const uint16_t scale = 255U, const bool invert = false); + + // Set the frequency of the timer for the given pin. + // All Timer PWM pins run at the same frequency. + static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired); +}; diff --git a/Marlin/src/HAL/GD32_MFL/MarlinSPI.h b/Marlin/src/HAL/GD32_MFL/MarlinSPI.h new file mode 100644 index 0000000000..d0731f9e84 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/MarlinSPI.h @@ -0,0 +1,26 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include + +using MarlinSPI = SPIClass; diff --git a/Marlin/src/HAL/GD32_MFL/MarlinSerial.cpp b/Marlin/src/HAL/GD32_MFL/MarlinSerial.cpp new file mode 100644 index 0000000000..95ea8bea25 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/MarlinSerial.cpp @@ -0,0 +1,97 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfig.h" +#include "MarlinSerial.h" + +#if ENABLED(EMERGENCY_PARSER) + #include "../../feature/e_parser.h" +#endif + +using namespace arduino; + +auto MarlinSerial::get_instance(usart::USART_Base Base, pin_size_t rxPin, pin_size_t txPin) -> MarlinSerial& { + auto& serial = UsartSerial::get_instance(Base, rxPin, txPin); + return *reinterpret_cast(&serial); +} + +#if USING_HW_SERIAL0 + MSerialT MSerial0(true, MarlinSerial::get_instance(usart::USART_Base::USART0_BASE, NO_PIN, NO_PIN)); +#endif +#if USING_HW_SERIAL1 + MSerialT MSerial1(true, MarlinSerial::get_instance(usart::USART_Base::USART1_BASE, NO_PIN, NO_PIN)); +#endif +#if USING_HW_SERIAL2 + MSerialT MSerial2(true, MarlinSerial::get_instance(usart::USART_Base::USART2_BASE, NO_PIN, NO_PIN)); +#endif +#if USING_HW_SERIAL3 + MSerialT MSerial3(true, MarlinSerial::get_instance(usart::USART_Base::UART3_BASE, NO_PIN, NO_PIN)); +#endif +#if USING_HW_SERIAL4 + MSerialT MSerial4(true, MarlinSerial::get_instance(usart::USART_Base::UART4_BASE, NO_PIN, NO_PIN)); +#endif + +#if ENABLED(EMERGENCY_PARSER) + // This callback needs to access the specific MarlinSerial instance + // We'll use a static pointer to track the current instance + static MarlinSerial* current_serial_instance = nullptr; + + static void emergency_callback() { + if (!current_serial_instance) return; + const auto last_data = current_serial_instance->get_last_data(); + emergency_parser.update(current_serial_instance->emergency_state, last_data); + } + + void MarlinSerial::register_emergency_callback(void (*callback)()) { + usart_.register_interrupt_callback(usart::Interrupt_Type::INTR_RBNEIE, callback); + } +#endif + +void MarlinSerial::begin(unsigned long baudrate, uint16_t config) { + UsartSerial::begin(baudrate, config, ENABLED(SERIAL_DMA)); + #if ENABLED(EMERGENCY_PARSER) && DISABLED(SERIAL_DMA) + current_serial_instance = this; + register_emergency_callback(emergency_callback); + #endif +} + +void MarlinSerial::updateRxDmaBuffer() { + #if ENABLED(EMERGENCY_PARSER) + // Get the number of bytes available in the receive buffer + const size_t available_bytes = usart_.available_for_read(true); + + // Process only the available data + for (size_t i = 0; i < available_bytes; ++i) { + uint8_t data; + if (usart_.read_rx_buffer(data)) + emergency_parser.update(emergency_state, data); + } + #endif + // Call the base class implementation to handle any additional updates + UsartSerial::updateRxDmaBuffer(); +} + +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/MarlinSerial.h b/Marlin/src/HAL/GD32_MFL/MarlinSerial.h new file mode 100644 index 0000000000..6b33ce0e61 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/MarlinSerial.h @@ -0,0 +1,75 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(EMERGENCY_PARSER) + #include "../../feature/e_parser.h" +#endif + +#include + +#include "../../core/serial_hook.h" + +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 4 + +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() +#endif + +using namespace arduino; + +struct MarlinSerial : public UsartSerial { + static auto get_instance(usart::USART_Base Base, pin_size_t rxPin = NO_PIN, pin_size_t txPin = NO_PIN) -> MarlinSerial&; + + void begin(unsigned long baudrate, uint16_t config); + inline void begin(unsigned long baudrate) { begin(baudrate, SERIAL_8N1); } + void updateRxDmaBuffer(); + + #if DISABLED(SERIAL_DMA) + FORCE_INLINE static uint8_t buffer_overruns() { return 0; } + #endif + + #if ENABLED(EMERGENCY_PARSER) + EmergencyParser::State emergency_state; + + // Accessor method to get the last received byte + auto get_last_data() -> uint8_t { return usart_.get_last_data(); } + + // Register the emergency callback + void register_emergency_callback(void (*callback)()); + #endif + +protected: + using UsartSerial::UsartSerial; +}; + +typedef Serial1Class MSerialT; +extern MSerialT MSerial0; +extern MSerialT MSerial1; +extern MSerialT MSerial2; +extern MSerialT MSerial3; +extern MSerialT MSerial4; diff --git a/Marlin/src/HAL/GD32_MFL/MinSerial.cpp b/Marlin/src/HAL/GD32_MFL/MinSerial.cpp new file mode 100644 index 0000000000..2cc79c8853 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/MinSerial.cpp @@ -0,0 +1,163 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(POSTMORTEM_DEBUGGING) +#include "../shared/MinSerial.h" + +// Base addresses for USART peripherals +static constexpr uintptr_t USART_base[] = { + 0x40013800, // USART0 + 0x40004400, // USART1 + 0x40004800, // USART2 + 0x40004C00, // UART3 + 0x40005000 // UART4 +}; + +// Register offsets +static constexpr uint32_t STAT0_OFFSET = 0x00U; +static constexpr uint32_t DATA_OFFSET = 0x04U; +static constexpr uint32_t BAUD_OFFSET = 0x08U; +static constexpr uint32_t CTL0_OFFSET = 0x0CU; +static constexpr uint32_t CTL1_OFFSET = 0x14U; + +// Bit positions +static constexpr uint32_t TBE_BIT = 7; +static constexpr uint32_t TEN_BIT = 3; +static constexpr uint32_t UEN_BIT = 13; + +// NVIC interrupt numbers for USART +static constexpr int nvicUART[] = { 37, 38, 39, 52, 53 }; + +// RCU PCLK values for USART +static constexpr rcu::RCU_PCLK clockRegs[] = { + rcu::RCU_PCLK::PCLK_USART0, + rcu::RCU_PCLK::PCLK_USART1, + rcu::RCU_PCLK::PCLK_USART2, + rcu::RCU_PCLK::PCLK_UART3, + rcu::RCU_PCLK::PCLK_UART4 +}; + +// Memory barrier instructions +#define isb() __asm__ __volatile__ ("isb" : : : "memory") +#define dsb() __asm__ __volatile__ ("dsb" : : : "memory") +#define sw_barrier() __asm__ volatile("" : : : "memory") + +// Direct register access macros +#define USART_REG(offset) (*(volatile uint32_t*)(USART_base[SERIAL_PORT] + (offset))) +#define USART_STAT0 USART_REG(STAT0_OFFSET) +#define USART_DATA USART_REG(DATA_OFFSET) +#define USART_BAUD USART_REG(BAUD_OFFSET) +#define USART_CTL0 USART_REG(CTL0_OFFSET) +#define USART_CTL1 USART_REG(CTL1_OFFSET) + +// Bit manipulation macros +#define READ_BIT(reg, bit) (((reg) >> (bit)) & 1U) +#define SET_BIT(reg, bit) ((reg) |= (1U << (bit))) +#define CLEAR_BIT(reg, bit) ((reg) &= ~(1U << (bit))) + +// Initializes the MinSerial interface. +// This function sets up the USART interface for serial communication. +// If the selected serial port is not a hardware port, it disables the severe error reporting feature. +static void MinSerialBegin() { + #if !WITHIN(SERIAL_PORT, 0, 4) + #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error." + #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port." + #else + int nvicIndex = nvicUART[SERIAL_PORT]; + + // NVIC base address for interrupt disable + struct NVICMin { + volatile uint32_t ISER[32]; + volatile uint32_t ICER[32]; + }; + NVICMin *nvicBase = (NVICMin*)0xE000E100; + + SBI32(nvicBase->ICER[nvicIndex >> 5], nvicIndex & 0x1F); + + // We require memory barriers to properly disable interrupts + // (https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the) + dsb(); + isb(); + + // Get the RCU PCLK for this USART + rcu::RCU_PCLK pclk = clockRegs[SERIAL_PORT]; + + // Disable then enable usart peripheral clocks + rcu::RCU_DEVICE.set_pclk_enable(pclk, false); + rcu::RCU_DEVICE.set_pclk_enable(pclk, true); + + // Save current baudrate + uint32_t baudrate = USART_BAUD; + + // Reset USART control registers + USART_CTL0 = 0; + USART_CTL1 = 0; // 1 stop bit + + // Restore baudrate + USART_BAUD = baudrate; + + // Enable transmitter and USART (8 bits, no parity, 1 stop bit) + SET_BIT(USART_CTL0, TEN_BIT); + SET_BIT(USART_CTL0, UEN_BIT); + #endif +} + +// Writes a single character to the serial port. +static void MinSerialWrite(char c) { + #if WITHIN(SERIAL_PORT, 0, 4) + // Wait until transmit buffer is empty + while (!READ_BIT(USART_STAT0, TBE_BIT)) { + hal.watchdog_refresh(); + sw_barrier(); + } + // Write character to data register + USART_DATA = c; + #endif +} + +// Installs the minimum serial interface. +// Sets the HAL_min_serial_init and HAL_min_serial_out function pointers to MinSerialBegin and MinSerialWrite respectively. +void install_min_serial() { + HAL_min_serial_init = &MinSerialBegin; + HAL_min_serial_out = &MinSerialWrite; +} + +extern "C" { + // A low-level assembly-based jump handler. + // Unconditionally branches to the CommonHandler_ASM function. + __attribute__((naked, aligned(4))) void JumpHandler_ASM() { + __asm__ __volatile__ ("b CommonHandler_ASM\n"); + } + void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) HardFault_Handler(); + void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) BusFault_Handler(); + void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) UsageFault_Handler(); + void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) MemManage_Handler(); + void __attribute__((naked, alias("JumpHandler_ASM"), nothrow)) NMI_Handler(); +} + +#endif // POSTMORTEM_DEBUGGING +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/README.md b/Marlin/src/HAL/GD32_MFL/README.md new file mode 100644 index 0000000000..61800eda1c --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/README.md @@ -0,0 +1,9 @@ +# Generic GD32 HAL based on the MFL Arduino Core + +This HAL is eventually intended to act as the generic HAL for all GD32 chips using the MFL library. + +Currently it supports: + +- GD32F303RET6 + +Targeting the official [MFL Arduino Core](https://github.com/bnmguy/ArduinoCore_MFL). diff --git a/Marlin/src/HAL/GD32_MFL/Servo.cpp b/Marlin/src/HAL/GD32_MFL/Servo.cpp new file mode 100644 index 0000000000..6cbcbc049b --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/Servo.cpp @@ -0,0 +1,125 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfig.h" + +#if HAS_SERVOS + +#include "Servo.h" + +static uint_fast8_t servoCount = 0; +static libServo* servos[NUM_SERVOS] = {0}; +constexpr millis_t servoDelay[] = SERVO_DELAY; +static_assert(COUNT(servoDelay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long."); + +// Initialize to the default timer priority. This will be overridden by a call from timers.cpp. +// This allows all timer interrupt priorities to be managed from a single location in the HAL. +static uint32_t servo_interrupt_priority = NVIC_EncodePriority(NVIC_GetPriorityGrouping(), 12, 0); + +// This must be called after the MFL Servo class has initialized the timer. +// To be safe this is currently called after every call to attach(). +static void fixServoTimerInterruptPriority() { + auto& servoTimerIdx = GeneralTimer::get_instance(static_cast(TIMER_SERVO)); + NVIC_SetPriority(servoTimerIdx.getTimerUpIRQ(), servo_interrupt_priority); +} + +// Default constructor for libServo class. +// Initializes the servo delay, pause state, and pause value. +// Registers the servo instance in the servos array. +libServo::libServo() : + delay(servoDelay[servoCount]), + was_attached_before_pause(false), + value_before_pause(0) +{ + servos[servoCount++] = this; +} + +// Attaches a servo to a specified pin. +int8_t libServo::attach(const int pin) { + if (servoCount >= MAX_SERVOS) return -1; + if (pin > 0) servoPin = pin; + auto result = mflServo.attach(servoPin); + fixServoTimerInterruptPriority(); + return result; +} + +// Attaches a servo to a specified pin with minimum and maximum pulse widths. +int8_t libServo::attach(const int pin, const int min, const int max) { + if (servoCount >= MAX_SERVOS) return -1; + if (pin > 0) servoPin = pin; + auto result = mflServo.attach(servoPin, min, max); + fixServoTimerInterruptPriority(); + return result; +} + +// Moves the servo to a specified position. +void libServo::move(const int value) { + if (attach(0) >= 0) { + mflServo.write(value); + safe_delay(delay); + TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach()); + } +} + +// Pause the servo by detaching it and storing its current state. +void libServo::pause() { + was_attached_before_pause = mflServo.attached(); + if (was_attached_before_pause) { + value_before_pause = mflServo.read(); + mflServo.detach(); + } +} + +// Resume a previously paused servo. +// If the servo was attached before the pause, this function re-attaches +// the servo and moves it to the position it was in before the pause. +void libServo::resume() { + if (was_attached_before_pause) { + attach(); + move(value_before_pause); + } +} + +// Pause all servos by stopping their timers. +void libServo::pause_all_servos() { + for (auto& servo : servos) + if (servo) servo->pause(); +} + +// Resume all paused servos by starting their timers. +void libServo::resume_all_servos() { + for (auto& servo : servos) + if (servo) servo->resume(); +} + +// Set the interrupt priority for the servo. +// @param preemptPriority The preempt priority level. +// @param subPriority The sub priority level. +void libServo::setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority) { + servo_interrupt_priority = NVIC_EncodePriority(NVIC_GetPriorityGrouping(), preemptPriority, subPriority); +} + +#endif // HAS_SERVOS +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/Servo.h b/Marlin/src/HAL/GD32_MFL/Servo.h new file mode 100644 index 0000000000..80cff46ff8 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/Servo.h @@ -0,0 +1,56 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include + +#include "../../core/millis_t.h" + +// Inherit and expand on the official library +class libServo { +public: + libServo(); + + int8_t attach(const int pin = 0); // pin == 0 uses value from previous call + int8_t attach(const int pin, const int min, const int max); + void detach() { mflServo.detach(); } + + int read() { return mflServo.read(); } + void move(const int value); + + void pause(); + void resume(); + + static void pause_all_servos(); + static void resume_all_servos(); + + static void setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority); + +private: + Servo mflServo; + + int servoPin = 0; + millis_t delay = 0; + + bool was_attached_before_pause; + int value_before_pause; +}; diff --git a/Marlin/src/HAL/GD32_MFL/dogm/u8g_com_mfl_swspi.cpp b/Marlin/src/HAL/GD32_MFL/dogm/u8g_com_mfl_swspi.cpp new file mode 100644 index 0000000000..b36cbfe44d --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/dogm/u8g_com_mfl_swspi.cpp @@ -0,0 +1,129 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm / Ryan Power + * + * 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 3 of the License, 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. If not, see . + * + */ + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" + +#if ALL(HAS_MARLINUI_U8GLIB, FORCE_SOFT_SPI) + +#include +#include "../../shared/HAL_SPI.h" + +#define nop asm volatile ("\tnop\n") + +static inline uint8_t swSpiTransfer_mode_0(uint8_t b) { + for (uint8_t i = 0; i < 8; ++i) { + const uint8_t state = (b & 0x80) ? HIGH : LOW; + WRITE(DOGLCD_SCK, HIGH); + WRITE(DOGLCD_MOSI, state); + b <<= 1; + WRITE(DOGLCD_SCK, LOW); + } + return b; +} + +static inline uint8_t swSpiTransfer_mode_3(uint8_t b) { + for (uint8_t i = 0; i < 8; ++i) { + const uint8_t state = (b & 0x80) ? HIGH : LOW; + WRITE(DOGLCD_SCK, LOW); + WRITE(DOGLCD_MOSI, state); + b <<= 1; + WRITE(DOGLCD_SCK, HIGH); + } + return b; +} + +static void u8g_sw_spi_shift_out(uint8_t val) { + #if U8G_SPI_USE_MODE_3 + swSpiTransfer_mode_3(val); + #else + swSpiTransfer_mode_0(val); + #endif +} + +static void swSpiInit() { + #if PIN_EXISTS(LCD_RESET) + SET_OUTPUT(LCD_RESET_PIN); + #endif + SET_OUTPUT(DOGLCD_A0); + OUT_WRITE(DOGLCD_SCK, LOW); + OUT_WRITE(DOGLCD_MOSI, LOW); + OUT_WRITE(DOGLCD_CS, HIGH); +} + +uint8_t u8g_com_HAL_MFL_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { + switch (msg) { + case U8G_COM_MSG_INIT: + swSpiInit(); + break; + case U8G_COM_MSG_STOP: + break; + case U8G_COM_MSG_RESET: + #if PIN_EXISTS(LCD_RESET) + WRITE(LCD_RESET_PIN, arg_val); + #endif + break; + case U8G_COM_MSG_CHIP_SELECT: + #if U8G_SPI_USE_MODE_3 // This LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active + WRITE(DOGLCD_CS, LOW); + nop; // Hold SCK high for a few ns + nop; + } + else { + WRITE(DOGLCD_CS, HIGH); + WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive + } + #else + WRITE(DOGLCD_CS, !arg_val); + #endif + break; + case U8G_COM_MSG_WRITE_BYTE: + u8g_sw_spi_shift_out(arg_val); + break; + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t* ptr = (uint8_t*)arg_ptr; + while (arg_val > 0) { + u8g_sw_spi_shift_out(*ptr++); + arg_val--; + } + } break; + case U8G_COM_MSG_WRITE_SEQ_P: { + uint8_t* ptr = (uint8_t*)arg_ptr; + while (arg_val > 0) { + u8g_sw_spi_shift_out(u8g_pgm_read(ptr)); + ptr++; + arg_val--; + } + } break; + case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1) + WRITE(DOGLCD_A0, arg_val); + break; + } + return 1; +} + +#endif // HAS_MARLINUI_U8GLIB && FORCE_SOFT_SPI +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_bl24cxx.cpp b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_bl24cxx.cpp new file mode 100644 index 0000000000..1053c80a41 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_bl24cxx.cpp @@ -0,0 +1,93 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * PersistentStore for Arduino-style EEPROM interface + * with simple implementations supplied by Marlin. + */ + +#include "../../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(IIC_BL24CXX_EEPROM) + +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" + +#ifndef MARLIN_EEPROM_SIZE + #error "MARLIN_EEPROM_SIZE is required for IIC_BL24CXX_EEPROM." +#endif + +size_t PersistentStore::capacity() { + return MARLIN_EEPROM_SIZE - eeprom_exclude_size; +} + +bool PersistentStore::access_start() { + eeprom_init(); + return true; +} + +bool PersistentStore::access_finish() { + return true; +} + +bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) { + uint16_t written = 0; + while (size--) { + uint8_t v = *value; + uint8_t * const p = (uint8_t * const)REAL_EEPROM_ADDR(pos); + // EPROM has only ~100,000 write cycles, + // so only write bytes that have changed! + if (v != eeprom_read_byte(p)) { + eeprom_write_byte(p, v); + if (++written & 0x7F) delay(2); else safe_delay(2); + if (eeprom_read_byte(p) != v) { + SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE); + return true; + } + } + + crc16(crc, &v, 1); + pos++; + value++; + } + + return false; +} + +bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) { + do { + const uint8_t c = eeprom_read_byte((uint8_t*)REAL_EEPROM_ADDR(pos)); + if (writing) *value = c; + crc16(crc, &c, 1); + pos++; + value++; + } while (--size); + + return false; +} + +#endif // IIC_BL24CXX_EEPROM +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_if_iic.cpp b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_if_iic.cpp new file mode 100644 index 0000000000..765c997e1f --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_if_iic.cpp @@ -0,0 +1,54 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * Platform-independent Arduino functions for I2C EEPROM. + * Enable USE_SHARED_EEPROM if not supplied by the framework. + */ + +#include "../../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(IIC_BL24CXX_EEPROM) + +#include "../../../libs/BL24CXX.h" +#include "../../shared/eeprom_if.h" + +void eeprom_init() { + BL24CXX::init(); +} + +void eeprom_write_byte(uint8_t *pos, uint8_t value) { + const unsigned eeprom_address = (unsigned)pos; + BL24CXX::writeOneByte(eeprom_address, value); +} + +uint8_t eeprom_read_byte(uint8_t *pos) { + const unsigned eeprom_address = (unsigned)pos; + return BL24CXX::readOneByte(eeprom_address); +} + +#endif // IIC_BL24CXX_EEPROM +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_wired.cpp b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_wired.cpp new file mode 100644 index 0000000000..8860e53a62 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/eeprom/eeprom_wired.cpp @@ -0,0 +1,96 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" + +#if USE_WIRED_EEPROM + +/** + * PersistentStore for Arduino-style EEPROM interface + * with simple implementations supplied by Marlin. + */ + +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" + +#ifndef MARLIN_EEPROM_SIZE + #define MARLIN_EEPROM_SIZE size_t(E2END + 1) +#endif + +size_t PersistentStore::capacity() { + return MARLIN_EEPROM_SIZE - eeprom_exclude_size; +} + +bool PersistentStore::access_start() { + eeprom_init(); + return true; +} + +bool PersistentStore::access_finish() { + return true; +} + +bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) { + uint16_t written = 0; + while (size--) { + uint8_t v = *value; + uint8_t * const p = (uint8_t * const)REAL_EEPROM_ADDR(pos); + // EEPROM has only ~100,000 write cycles, + // so only write bytes that have changed! + if (v != eeprom_read_byte(p)) { + eeprom_write_byte(p, v); + // Avoid triggering watchdog during long EEPROM writes + if (++written & 0x7F) + delay(2); + else + safe_delay(2); + if (eeprom_read_byte(p) != v) { + SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE); + return true; + } + } + crc16(crc, &v, 1); + pos++; + value++; + } + + return false; +} + +bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) { + do { + const uint8_t c = eeprom_read_byte((uint8_t*)REAL_EEPROM_ADDR(pos)); + if (writing) + *value = c; + crc16(crc, &c, 1); + pos++; + value++; + } while (--size); + + return false; +} + +#endif // USE_WIRED_EEPROM +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/endstop_interrupts.h b/Marlin/src/HAL/GD32_MFL/endstop_interrupts.h new file mode 100644 index 0000000000..175dec3959 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/endstop_interrupts.h @@ -0,0 +1,61 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../../module/endstops.h" + +// One ISR for all EXT-Interrupts +void endstop_ISR() { endstops.update(); } + +void setup_endstop_interrupts() { + #define _ATTACH(P) attachInterrupt(P, endstop_ISR, CHANGE) + TERN_(USE_X_MAX, _ATTACH(X_MAX_PIN)); + TERN_(USE_X_MIN, _ATTACH(X_MIN_PIN)); + TERN_(USE_Y_MAX, _ATTACH(Y_MAX_PIN)); + TERN_(USE_Y_MIN, _ATTACH(Y_MIN_PIN)); + TERN_(USE_Z_MAX, _ATTACH(Z_MAX_PIN)); + TERN_(USE_Z_MIN, _ATTACH(Z_MIN_PIN)); + TERN_(USE_X2_MAX, _ATTACH(X2_MAX_PIN)); + TERN_(USE_X2_MIN, _ATTACH(X2_MIN_PIN)); + TERN_(USE_Y2_MAX, _ATTACH(Y2_MAX_PIN)); + TERN_(USE_Y2_MIN, _ATTACH(Y2_MIN_PIN)); + TERN_(USE_Z2_MAX, _ATTACH(Z2_MAX_PIN)); + TERN_(USE_Z2_MIN, _ATTACH(Z2_MIN_PIN)); + TERN_(USE_Z3_MAX, _ATTACH(Z3_MAX_PIN)); + TERN_(USE_Z3_MIN, _ATTACH(Z3_MIN_PIN)); + TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); + TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); + TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); + TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); + TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); + TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); + TERN_(USE_J_MIN, _ATTACH(J_MIN_PIN)); + TERN_(USE_K_MAX, _ATTACH(K_MAX_PIN)); + TERN_(USE_K_MIN, _ATTACH(K_MIN_PIN)); + TERN_(USE_U_MAX, _ATTACH(U_MAX_PIN)); + TERN_(USE_U_MIN, _ATTACH(U_MIN_PIN)); + TERN_(USE_V_MAX, _ATTACH(V_MAX_PIN)); + TERN_(USE_V_MIN, _ATTACH(V_MIN_PIN)); + TERN_(USE_W_MAX, _ATTACH(W_MAX_PIN)); + TERN_(USE_W_MIN, _ATTACH(W_MIN_PIN)); +} diff --git a/Marlin/src/HAL/GD32_MFL/fast_pwm.cpp b/Marlin/src/HAL/GD32_MFL/fast_pwm.cpp new file mode 100644 index 0000000000..9fba673efc --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/fast_pwm.cpp @@ -0,0 +1,97 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfig.h" + +#include +#include +#include "timers.h" + +static uint16_t timer_frequency[TIMER_COUNT]; + +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t value, const uint16_t scale, const bool invert) { + // Calculate duty cycle based on inversion flag + const uint16_t duty = invert ? scale - value : value; + + // Check if the pin supports PWM + if (PWM_PIN(pin)) { + // Get the timer peripheral base associated with the pin + const auto timer_base = getPinOpsPeripheralBase(TIMER_PinOps, static_cast(pin)); + + // Initialize the timer instance + auto& TimerInstance = GeneralTimer::get_instance(timer_base); + + // Get channel and previous channel mode + const auto channel = getPackedPinChannel(getPackedPinOps(TIMER_PinOps, static_cast(pin))); + const InputOutputMode previous = TimerInstance.getChannelMode(channel); + + if (timer_frequency[static_cast(timer_base)] == 0) { + set_pwm_frequency(pin, PWM_FREQUENCY); + } + + // Set the PWM duty cycle + TimerInstance.setCaptureCompare(channel, duty, CCFormat::B8); + + // Configure pin as PWM output + pinOpsPinout(TIMER_PinOps, static_cast(pin)); + + // Set channel mode if not already set and start timer + if (previous != InputOutputMode::PWM0) { + TimerInstance.setChannelMode(channel, InputOutputMode::PWM0, static_cast(pin)); + TimerInstance.start(); + } + } else { + pinMode(pin, OUTPUT); + digitalWrite(pin, duty < scale / 2 ? LOW : HIGH); + } +} + +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + // Check if the pin supports PWM + if (!PWM_PIN(pin)) return; + + // Get the timer peripheral base associated with the pin + const auto timer_base = getPinOpsPeripheralBase(TIMER_PinOps, static_cast(pin)); + + // Guard against modifying protected timers + #ifdef STEP_TIMER + if (timer_base == static_cast(STEP_TIMER)) return; + #endif + #ifdef TEMP_TIMER + if (timer_base == static_cast(TEMP_TIMER)) return; + #endif + #if defined(PULSE_TIMER) && MF_TIMER_PULSE != MF_TIMER_STEP + if (timer_base == static_cast(PULSE_TIMER)) return; + #endif + + // Initialize the timer instance + auto& TimerInstance = GeneralTimer::get_instance(timer_base); + + TimerInstance.setRolloverValue(f_desired, TimerFormat::HERTZ); + timer_frequency[timer_base_to_index(timer_base)] = f_desired; +} + +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/fastio.h b/Marlin/src/HAL/GD32_MFL/fastio.h new file mode 100644 index 0000000000..11020f3e52 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/fastio.h @@ -0,0 +1,86 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// Fast I/O interfaces for GD32 + +#include +#include +#include +#include + +template +FORCE_INLINE static void fast_write_pin_wrapper(pin_size_t IO, T V) { + const PortPinPair& pp = port_pin_map[IO]; + gpio::fast_write_pin(pp.port, pp.pin, static_cast(V)); +} + +FORCE_INLINE static auto fast_read_pin_wrapper(pin_size_t IO) -> bool { + const PortPinPair& pp = port_pin_map[IO]; + return gpio::fast_read_pin(pp.port, pp.pin); +} + +FORCE_INLINE static void fast_toggle_pin_wrapper(pin_size_t IO) { + const PortPinPair& pp = port_pin_map[IO]; + gpio::fast_toggle_pin(pp.port, pp.pin); +} + +// ------------------------ +// Defines +// ------------------------ + +#ifndef PWM + #define PWM OUTPUT +#endif + +#define _WRITE(IO, V) fast_write_pin_wrapper(IO, V) +#define _READ(IO) fast_read_pin_wrapper(IO) +#define _TOGGLE(IO) fast_toggle_pin_wrapper(IO) + +#define _GET_MODE(IO) +#define _SET_MODE(IO, M) pinMode((IO), (M)) +#define _SET_OUTPUT(IO) pinMode((IO), OUTPUT) +#define _SET_OUTPUT_OD(IO) pinMode((IO), OUTPUT_OPEN_DRAIN) + +#define WRITE(IO, V) _WRITE((IO), (V)) +#define READ(IO) _READ(IO) +#define TOGGLE(IO) _TOGGLE(IO) + +#define OUT_WRITE(IO, V) do { _SET_OUTPUT(IO); WRITE((IO), (V)); } while (0) +#define OUT_WRITE_OD(IO, V) do { _SET_OUTPUT_OD(IO); WRITE((IO), (V)); } while (0) + +#define SET_INPUT(IO) _SET_MODE((IO), INPUT) +#define SET_INPUT_PULLUP(IO) _SET_MODE((IO), INPUT_PULLUP) +#define SET_INPUT_PULLDOWN(IO) _SET_MODE((IO), INPUT_PULLDOWN) +#define SET_OUTPUT(IO) OUT_WRITE((IO), LOW) +#define SET_OUTPUT_OD(IO) OUT_WRITE_OD((IO), LOW) +#define SET_PWM(IO) _SET_MODE((IO), PWM) + +#define IS_INPUT(IO) +#define IS_OUTPUT(IO) + +#define PWM_PIN(P) isPinInPinOps(TIMER_PinOps, P) +#define NO_COMPILE_TIME_PWM + +// Wrappers for digitalRead and digitalWrite +#define extDigitalRead(IO) digitalRead(IO) +#define extDigitalWrite(IO, V) digitalWrite((IO), (V)) diff --git a/Marlin/src/HAL/GD32_MFL/inc/Conditionals_LCD.h b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_LCD.h new file mode 100644 index 0000000000..379ecfa7f0 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_LCD.h @@ -0,0 +1,26 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#if ALL(HAS_MARLINUI_U8GLIB, FORCE_SOFT_SPI) + #define U8G_SW_SPI_MFL 1 +#endif diff --git a/Marlin/src/HAL/GD32_MFL/inc/Conditionals_adv.h b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_adv.h new file mode 100644 index 0000000000..6df84f6f92 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_adv.h @@ -0,0 +1,26 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#if ALL(HAS_MEDIA, USBD_USE_CDC_MSC) + #define HAS_SD_HOST_DRIVE 1 +#endif diff --git a/Marlin/src/HAL/GD32_MFL/inc/Conditionals_post.h b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_post.h new file mode 100644 index 0000000000..a3db122682 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_post.h @@ -0,0 +1,29 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// If no real or emulated EEPROM selected, fall back to SD emulation +#if USE_FALLBACK_EEPROM + #define SDCARD_EEPROM_EMULATION +#elif ANY(I2C_EEPROM, SPI_EEPROM) + #define USE_SHARED_EEPROM 1 +#endif diff --git a/Marlin/src/HAL/GD32_MFL/inc/Conditionals_type.h b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_type.h new file mode 100644 index 0000000000..92cc457df5 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/inc/Conditionals_type.h @@ -0,0 +1,22 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once diff --git a/Marlin/src/HAL/GD32_MFL/inc/SanityCheck.h b/Marlin/src/HAL/GD32_MFL/inc/SanityCheck.h new file mode 100644 index 0000000000..366765dcd5 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/inc/SanityCheck.h @@ -0,0 +1,97 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// Test MFL GD32 specific configuration values for errors at compile-time. +#if ENABLED(SDCARD_EEPROM_EMULATION) && !HAS_MEDIA + #undef SDCARD_EEPROM_EMULATION // avoid additional error noise + #if USE_FALLBACK_EEPROM + #warning "EEPROM type not specified. Fallback is SDCARD_EEPROM_EMULATION." + #endif + #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation." +#endif + +#if ENABLED(FLASH_EEPROM_LEVELING) + #error "FLASH_EEPROM_LEVELING is not supported on GD32." +#endif + +#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) + #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on GD32." +#elif ENABLED(SERIAL_STATS_DROPPED_RX) + #error "SERIAL_STATS_DROPPED_RX is not supported on GD32." +#endif + +#if TEMP_SENSOR_SOC && defined(ATEMP) && TEMP_SOC_PIN != ATEMP + #error "TEMP_SENSOR_SOC requires 'TEMP_SOC_PIN ATEMP' on GD32" +#endif + +// Check for common serial pin conflicts +#define _CHECK_SERIAL_PIN(N) (( \ + BTN_EN1 == N || BTN_EN2 == N || DOGLCD_CS == N || HEATER_BED_PIN == N || FAN0_PIN == N || \ + SDIO_D2_PIN == N || SDIO_D3_PIN == N || SDIO_CK_PIN == N || SDIO_CMD_PIN == N || \ + Y_STEP_PIN == N || Y_ENABLE_PIN == N || E0_ENABLE_PIN == N || POWER_LOSS_PIN == N \ + )) + +#define CHECK_SERIAL_PIN(T, N) defined(UART##N##_##T##_PIN) && _CHECK_SERIAL_PIN(UART##N##_##T##_PIN) + +#if SERIAL_IN_USE(0) + #if CHECK_SERIAL_PIN(TX, 0) + #error "Serial Port 0 TX IO pins conflict with another pin on the board." + #endif + #if CHECK_SERIAL_PIN(RX, 0) + #error "Serial Port 0 RX IO pins conflict with another pin on the board." + #endif +#endif +#if SERIAL_IN_USE(1) + #if CHECK_SERIAL_PIN(TX, 1) + #error "Serial Port 1 TX IO pins conflict with another pin on the board." + #endif + #if CHECK_SERIAL_PIN(RX, 1) + #error "Serial Port 1 RX IO pins conflict with another pin on the board." + #endif +#endif +#if SERIAL_IN_USE(2) + #if CHECK_SERIAL_PIN(TX, 2) + #error "Serial Port 2 TX IO pins conflict with another pin on the board." + #endif + #if CHECK_SERIAL_PIN(RX, 2) + #error "Serial Port 2 RX IO pins conflict with another pin on the board." + #endif +#endif +#if SERIAL_IN_USE(3) + #if CHECK_SERIAL_PIN(TX, 3) + #error "Serial Port 3 TX IO pins conflict with another pin on the board." + #endif + #if CHECK_SERIAL_PIN(RX, 3) + #error "Serial Port 3 RX IO pins conflict with another pin on the board." + #endif +#endif +#if SERIAL_IN_USE(4) + #if CHECK_SERIAL_PIN(TX, 4) + #error "Serial Port 4 TX IO pins conflict with another pin on the board." + #endif + #if CHECK_SERIAL_PIN(RX, 4) + #error "Serial Port 4 RX IO pins conflict with another pin on the board." + #endif +#endif +#undef CHECK_SERIAL_PIN +#undef _CHECK_SERIAL_PIN diff --git a/Marlin/src/HAL/GD32_MFL/pinsDebug.h b/Marlin/src/HAL/GD32_MFL/pinsDebug.h new file mode 100644 index 0000000000..be7e4ce1ba --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/pinsDebug.h @@ -0,0 +1,104 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * Pins Debugging for GD32 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) + */ + +#include "../../inc/MarlinConfig.h" +#include +#include +#include + +#ifndef TOTAL_PIN_COUNT + #error "Expected TOTAL_PIN_COUNT not found." +#endif + +#define NUM_DIGITAL_PINS TOTAL_PIN_COUNT +#define NUMBER_PINS_TOTAL TOTAL_PIN_COUNT + +#define getPinByIndex(x) pin_t(pin_array[x].pin) +#define isValidPin(P) WITHIN(P, 0, (NUM_DIGITAL_PINS - 1)) +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNumber(P) do { sprintf_P(buffer, PSTR("%3hd "), pin_t(P)); SERIAL_ECHO(buffer); } while (0) +#define printPinAnalog(P) do { sprintf_P(buffer, PSTR(" (A%2d) "), pin_t(getAdcChannelFromPin(P))); SERIAL_ECHO(buffer); } while (0) +#define printPinNameByIndex(x) do { sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); } while (0) + +#define MULTI_NAME_PAD 21 // Space needed to be pretty if not first name assigned to a pin + +#ifndef M43_NEVER_TOUCH + #define M43_NEVER_TOUCH(x) WITHIN(x, 9, 10) // SERIAL pins: PA9(TX) PA10(RX) +#endif + +bool isAnalogPin(const pin_t pin) { + if (!isValidPin(pin)) return false; + + if (getAdcChannel(pin) != adc::ADC_Channel::INVALID) { + const PortPinPair& pp = port_pin_map[pin]; + auto& instance = gpio::GPIO::get_instance(pp.port).value(); + return instance.get_pin_mode(pp.pin) == gpio::Pin_Mode::ANALOG && !M43_NEVER_TOUCH(pin); + } + + return false; +} + +bool getValidPinMode(const pin_t pin) { + if (!isValidPin(pin)) return false; + + const PortPinPair& pp = port_pin_map[pin]; + auto& instance = gpio::GPIO::get_instance(pp.port).value(); + gpio::Pin_Mode mode = instance.get_pin_mode(pp.pin); + + return mode != gpio::Pin_Mode::ANALOG && mode != gpio::Pin_Mode::INPUT_FLOATING && + mode != gpio::Pin_Mode::INPUT_PULLUP && mode != gpio::Pin_Mode::INPUT_PULLDOWN; +} + +bool getPinIsDigitalByIndex(const int16_t index) { + const pin_t pin = getPinByIndex(index); + return (!isAnalogPin(pin)); +} + +int8_t digitalPinToAnalogIndex(const pin_t pin) { + if (!isValidPin(pin) || !isAnalogPin(pin)) return -1; + return pin; // Analog and digital pin indexes are shared +} + +bool pwm_status(const pin_t pin) { return false; } +void printPinPWM(const pin_t pin) { /* TODO */ } +void printPinPort(const pin_t pin) { /* TODO */ } diff --git a/Marlin/src/HAL/GD32_MFL/sd/SDCard.cpp b/Marlin/src/HAL/GD32_MFL/sd/SDCard.cpp new file mode 100644 index 0000000000..2e7ba4dfd9 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/sd/SDCard.cpp @@ -0,0 +1,1022 @@ +// +// MFL gd32f30x SDCARD using DMA through SDIO in C++ +// +// Copyright (C) 2025 B. Mourit +// +// This software is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// This software 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with this software. +// If not, see . +// + +#include "../../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" +#include "../../shared/Delay.h" + +#include "SDCard.h" +#include +#include + +namespace sdio { + +auto CardDMA::get_instance() -> CardDMA& { + static CardDMA instance; + return instance; +} + +CardDMA::CardDMA() : + sdcard_csd_{0U, 0U, 0U, 0U}, + sdcard_cid_{0U, 0U, 0U, 0U}, + sdcard_scr_{0U, 0U}, + desired_clock_(Default_Desired_Clock), + total_bytes_(0U), + sdio_(SDIO::get_instance()), + config_(sdio_.get_config()), + dmaBase_(dma::DMA_Base::DMA1_BASE), + dmaChannel_(dma::DMA_Channel::CHANNEL3), + dma_(dma::DMA::get_instance(dmaBase_, dmaChannel_).value()), + sdcard_rca_(0U), + transfer_error_(SDIO_Error_Type::OK), + interface_version_(Interface_Version::UNKNOWN), + card_type_(Card_Type::UNKNOWN), + current_state_(Operational_State::READY), + transfer_end_(false), + multiblock_(false), + is_rx_(false) +{ +} + +// Initialize card and put in standby state +auto CardDMA::init() -> SDIO_Error_Type { + // Reset SDIO peripheral + sdio_.reset(); + sync_domains(); + + // Initialize SDIO peripheral + // If no SDIO_Config structure is provided the default is used. + // The default provides the parameters for initialization + // using a very low clock speed (typically <= 400KHz). + sdio_.init(); + sync_domains(); + + SDIO_Error_Type result = begin_startup_procedure(); + if (result != SDIO_Error_Type::OK) { + return result; + } + + return card_init(); +} + +// Startup command procedure according to SDIO specification +auto CardDMA::begin_startup_procedure() -> SDIO_Error_Type { + sdio_.set_power_mode(Power_Control::POWER_ON); + sdio_.set_clock_enable(true); + sync_domains(); + + // CMD0 (GO_IDLE_STATE) + if (send_command_and_check(Command_Index::CMD0, 0, Command_Response::RSP_NONE, Wait_Type::WT_NONE, [this]() { + return this->get_command_sent_result(); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD0_FAILED; + } + + // CMD8 + if (send_command_and_check(Command_Index::CMD8, Check_Pattern, Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this]() { + return this->get_r7_result(); + }) != SDIO_Error_Type::OK) { + // V1.0 card + // CMD0 (GO_IDLE_STATE) + interface_version_ = Interface_Version::INTERFACE_V1_1; + if (send_command_and_check(Command_Index::CMD0, 0, Command_Response::RSP_NONE, Wait_Type::WT_NONE, [this]() { + return this->get_command_sent_result(); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD0_FAILED; + } + } else { + // V2.0 card + // CMD55 + interface_version_ = Interface_Version::INTERFACE_V2_0; + if (send_command_and_check(Command_Index::CMD55, 0, Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + } + + if (send_command_and_check(Command_Index::CMD55, 0, Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + + return validate_voltage(); +} + +// Voltage validation +auto CardDMA::validate_voltage() -> SDIO_Error_Type { + uint32_t response = 0U; + uint32_t count = 0U; + bool valid_voltage = false; + + while (count < Max_Voltage_Checks && valid_voltage == false) { + if (send_command_and_check(Command_Index::CMD55, 0, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + + if (send_command_and_check(Command_Index::ACMD41, Voltage_Window | SDCARD_HCS | Switch_1_8V_Capacity, + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::INVALID, index = false, crc = true]() { + return check_sdio_status(cmd, index, crc); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::ACMD41_FAILED; + } + response = sdio_.get_response(Response_Type::RESPONSE0); + valid_voltage = ((response >> 31U) == 1U); + count++; + } + + if (count >= Max_Voltage_Checks) { + return SDIO_Error_Type::INVALID_VOLTAGE; + } + + card_type_ = (response & SDCARD_HCS) ? Card_Type::SDCARD_HIGH_CAPACITY : Card_Type::SDCARD_STANDARD_CAPACITY; + + #if ENABLED(MARLIN_DEV_MODE) + if (card_type_ == Card_Type::SDCARD_HIGH_CAPACITY) { + SERIAL_ECHOPGM("\n SDHC!"); + } else { + SERIAL_ECHOPGM("\n SDSC!"); + } + #endif + + return SDIO_Error_Type::OK; +} + +// Shutdown +void CardDMA::begin_shutdown_procedure() { + sdio_.set_power_mode(Power_Control::POWER_OFF); +} + +// Initialize card +auto CardDMA::card_init() -> SDIO_Error_Type { + if (sdio_.get_power_mode() == static_cast(Power_Control::POWER_OFF)) { + return SDIO_Error_Type::INVALID_OPERATION; + } + + // Skip CID/RCA for IO cards + if (card_type_ != Card_Type::SD_IO_CARD) { + if (send_command_and_check(Command_Index::CMD2, 0U, Command_Response::RSP_LONG, Wait_Type::WT_NONE, + [this, cmd = Command_Index::INVALID, index = false, crc = true]() { + return check_sdio_status(cmd, index, crc); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD2_FAILED; + } + // Store CID + store_cid(); + + // Get RCA + uint16_t r6_rca; + if (send_command_and_check(Command_Index::CMD3, 0U, Command_Response::RSP_SHORT, Wait_Type::WT_NONE, + [this, cmd = Command_Index::CMD3, rca = &r6_rca]() { + return get_r6_result(cmd, rca); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD3_FAILED; + } + // Store RCA + sdcard_rca_ = r6_rca; + if (send_command_and_check(Command_Index::CMD9, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_LONG, Wait_Type::WT_NONE, + [this, cmd = Command_Index::INVALID, index = false, crc = true]() { + return check_sdio_status(cmd, index, crc); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD9_FAILED; + } + // Store CSD + store_csd(); + } + + Card_Info card_info; + SDIO_Error_Type result = get_card_specific_data(&card_info); + if (result != SDIO_Error_Type::OK) { + return result; + } + + if (select_deselect() != SDIO_Error_Type::OK) { + return SDIO_Error_Type::SELECT_DESELECT_FAILED; + } + + return SDIO_Error_Type::OK; +} + +auto CardDMA::store_cid() -> SDIO_Error_Type { + // Store the CID register values + sdcard_cid_[0] = sdio_.get_response(Response_Type::RESPONSE0); + sdcard_cid_[1] = sdio_.get_response(Response_Type::RESPONSE1); + sdcard_cid_[2] = sdio_.get_response(Response_Type::RESPONSE2); + sdcard_cid_[3] = sdio_.get_response(Response_Type::RESPONSE3); + + return SDIO_Error_Type::OK; +} + +auto CardDMA::store_csd() -> SDIO_Error_Type { + // Store the CSD register values + sdcard_csd_[0] = sdio_.get_response(Response_Type::RESPONSE0); + sdcard_csd_[1] = sdio_.get_response(Response_Type::RESPONSE1); + sdcard_csd_[2] = sdio_.get_response(Response_Type::RESPONSE2); + sdcard_csd_[3] = sdio_.get_response(Response_Type::RESPONSE3); + + return SDIO_Error_Type::OK; +} + +auto CardDMA::set_hardware_bus_width(Bus_Width width) -> SDIO_Error_Type { + if (card_type_ == Card_Type::SD_MMC || width == Bus_Width::WIDTH_8BIT) { + return SDIO_Error_Type::UNSUPPORTED_FUNCTION; + } + + // Retrieve SCR + SDIO_Error_Type result = get_scr(sdcard_rca_, sdcard_scr_); + if (result != SDIO_Error_Type::OK) { + return result; + } + + // Check and set bus width + // This function is only used to set a higher width than the default 1bit + // so no 1bit configuration logic is required. + if (width == Bus_Width::WIDTH_4BIT) { + // Send CMD55 (APP_CMD) + if (send_command_and_check(Command_Index::CMD55, static_cast(sdcard_rca_ << RCA_Shift), Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + + // Send ACMD6 (SET_BUS_WIDTH) + if (send_command_and_check(Command_Index::ACMD6, 2U, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::ACMD6]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::ACMD6_FAILED; + } + + #if ENABLED(MARLIN_DEV_MODE) + SERIAL_ECHOPGM("\n wide bus set!"); + #endif + sdio_.set_bus_width(Bus_Width::WIDTH_4BIT); + + return SDIO_Error_Type::OK; + } + + return SDIO_Error_Type::UNSUPPORTED_FUNCTION; +} + +auto CardDMA::read(uint8_t* buf, uint32_t address, uint32_t count) -> SDIO_Error_Type { + if (current_state_ == Operational_State::READY) { + transfer_error_ = SDIO_Error_Type::OK; + current_state_ = Operational_State::BUSY; + is_rx_ = true; + multiblock_ = (count > 1); + + sdio_.clear_data_state_machine(Transfer_Direction::CARD_TO_SDIO); + + // Enable the interrupts + sdio_.set_interrupt_enable(Interrupt_Type::DTCRCERRIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::DTTMOUTIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::RXOREIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::DTENDIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::STBITEIE, true); + + total_bytes_ = BLOCK_SIZE * count; + + // Set DMA transfer parameters + set_dma_parameters(buf, (total_bytes_ / 4U), false); + sdio_.set_dma_enable(true); + + if (card_type_ != Card_Type::SDCARD_HIGH_CAPACITY) { + address *= 512U; + } + + // CMD16 set card block size + if (send_command_and_check(Command_Index::CMD16, BLOCK_SIZE, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD16]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + sdio_.clear_multiple_interrupt_flags(clear_common_flags); + current_state_ = Operational_State::READY; + return SDIO_Error_Type::CMD16_FAILED; + } + + Block_Size block_size = get_data_block_size_index(BLOCK_SIZE); + + sdio_.set_data_state_machine_and_send(Data_Timeout, total_bytes_, block_size, + Transfer_Mode::BLOCK, Transfer_Direction::CARD_TO_SDIO, true); + + // CMD17/CMD18 (READ_SINGLE_BLOCK/READ_MULTIPLE_BLOCKS) send read command + Command_Index read_cmd = (count > 1U) ? Command_Index::CMD18 : Command_Index::CMD17; + if (send_command_and_check(read_cmd, address, + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = read_cmd]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + sdio_.clear_multiple_interrupt_flags(clear_common_flags); + current_state_ = Operational_State::READY; + return (count > 1U) ? SDIO_Error_Type::CMD18_FAILED : SDIO_Error_Type::CMD17_FAILED; + } + return SDIO_Error_Type::OK; + } else { + return SDIO_Error_Type::BUSY; + } +} + +auto CardDMA::write(uint8_t* buf, uint32_t address, uint32_t count) -> SDIO_Error_Type { + // Enable the interrupts + sdio_.set_interrupt_enable(Interrupt_Type::DTCRCERRIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::DTTMOUTIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::STBITEIE, true); + sdio_.set_interrupt_enable(Interrupt_Type::TXUREIE, true); + + if (card_type_ != Card_Type::SDCARD_HIGH_CAPACITY) { + address *= 512U; + } + + // CMD16 + if (send_command_and_check(Command_Index::CMD16, BLOCK_SIZE, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD16]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + sdio_.clear_multiple_interrupt_flags(clear_common_flags); + return SDIO_Error_Type::CMD16_FAILED; + } + + // CMD25/CMD24 (WRITE_MULTIPLE_BLOCK/WRITE_BLOCK) send write command + Command_Index write_cmd = (count > 1U) ? Command_Index::CMD25 : Command_Index::CMD24; + if (send_command_and_check(write_cmd, address, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = write_cmd]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + sdio_.clear_multiple_interrupt_flags(clear_common_flags); + return (count > 1U) ? SDIO_Error_Type::CMD25_FAILED : SDIO_Error_Type::CMD24_FAILED; + } + + total_bytes_ = BLOCK_SIZE * count; + // Start DMA transfer + set_dma_parameters(buf, (total_bytes_ / 4U), true); + sdio_.set_dma_enable(true); + + sdio_.clear_data_state_machine(Transfer_Direction::SDIO_TO_CARD); + Block_Size block_size = get_data_block_size_index(total_bytes_); + + sdio_.set_data_state_machine_and_send(Data_Timeout, total_bytes_, block_size, + Transfer_Mode::BLOCK, Transfer_Direction::SDIO_TO_CARD, true); + + while (dma_.get_flag(dma::Status_Flags::FLAG_FTFIF) || dma_.get_flag(dma::Status_Flags::FLAG_ERRIF)) { + // Wait for the IRQ handler to clear + } + + return SDIO_Error_Type::OK; +} + +auto CardDMA::erase(uint32_t address_start, uint32_t address_end) -> SDIO_Error_Type { + SDIO_Error_Type result = SDIO_Error_Type::OK; + + // Card command classes CSD + uint8_t temp_byte = static_cast((sdcard_csd_[1] & (0xFFU << 24U)) >> 24U); + uint16_t classes = static_cast(temp_byte << 4U); + temp_byte = static_cast((sdcard_csd_[1] & (0xFFU << 16U)) >> 16U); + classes |= static_cast((temp_byte & 0xF0U) >> 4U); + + if ((classes & (1U << static_cast(Card_Command_Class::ERASE))) == Clear) { + return SDIO_Error_Type::UNSUPPORTED_FUNCTION; + } + + uint32_t delay_time = 120000U / sdio_.get_clock_divider(); + + if (sdio_.get_response(Response_Type::RESPONSE0) & Card_Locked) { + return SDIO_Error_Type::LOCK_UNLOCK_FAILED; + } + + // Size is fixed at 512 bytes for SDHC + if (card_type_ != Card_Type::SDCARD_HIGH_CAPACITY) { + address_start *= 512U; + address_end *= 512U; + } + + if ((card_type_ == Card_Type::SDCARD_STANDARD_CAPACITY) || (card_type_ == Card_Type::SDCARD_HIGH_CAPACITY)) { + // CMD32 (ERASE_WR_BLK_START) + if (send_command_and_check(Command_Index::CMD32, address_start, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD32]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD32_FAILED; + } + // CMD33 (ERASE_WR_BLK_END) + if (send_command_and_check(Command_Index::CMD33, address_end, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD33]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD33_FAILED; + } + } + + // CMD38 (ERASE) + if (send_command_and_check(Command_Index::CMD38, 0U, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD38]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD38_FAILED; + } + + // Loop until the counter reaches the calculated time + for (uint32_t count = 0U; count < delay_time; count++) {} + + Card_State card_state; + result = get_card_state(&card_state); + while ((result == SDIO_Error_Type::OK) && ((card_state == Card_State::PROGRAMMING) || (card_state == Card_State::RECEIVE_DATA))) { + result = get_card_state(&card_state); + } + + return result; +} + +void CardDMA::handle_interrupts() { + transfer_error_ = SDIO_Error_Type::OK; + + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_DTEND)) { + sdio_.clear_interrupt_flag(Clear_Flags::FLAG_DTENDC); + // Disable all interrupts + disable_all_interrupts(); + sdio_.set_data_state_machine_enable(false); + + if (multiblock_ && !is_rx_) { + transfer_error_ = stop_transfer(); + if (transfer_error_ != SDIO_Error_Type::OK) { + return; + } + } + + if (!is_rx_) { + sdio_.set_dma_enable(false); + current_state_ = Operational_State::READY; + } + } else if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_DTCRCERR) || + sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_DTTMOUT) || + sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_STBITE) || + sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_TXURE) || + sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_RXORE)) { + + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_DTCRCERR)) { + transfer_error_ = SDIO_Error_Type::DATA_CRC_ERROR; + } + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_DTTMOUT)) { + transfer_error_ = SDIO_Error_Type::DATA_TIMEOUT; + } + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_STBITE)) { + transfer_error_ = SDIO_Error_Type::START_BIT_ERROR; + } + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_TXURE)) { + transfer_error_ = SDIO_Error_Type::TX_FIFO_UNDERRUN; + } + if (sdio_.get_interrupt_flag(Interrupt_Flags::FLAG_INTR_RXORE)) { + transfer_error_ = SDIO_Error_Type::RX_FIFO_OVERRUN; + } + sdio_.clear_multiple_interrupt_flags(clear_data_flags); + sdio_.clear_interrupt_flag(Clear_Flags::FLAG_STBITEC); + disable_all_interrupts(); + + dma_.set_transfer_abandon(); + } +} + +auto CardDMA::select_deselect() -> SDIO_Error_Type { + // CMD7 (SELECT/DESELECT_CARD) + if (send_command_and_check(Command_Index::CMD7, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD7]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD7_FAILED; + } + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_card_interface_status(uint32_t* status) -> SDIO_Error_Type { + if (status == nullptr) return SDIO_Error_Type::INVALID_PARAMETER; + + // CMD13 (SEND_STATUS) + if (send_command_and_check(Command_Index::CMD13, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD13]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD13_FAILED; + } + + *status = sdio_.get_response(Response_Type::RESPONSE0); + + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_sdcard_status(uint32_t* status) -> SDIO_Error_Type { + uint32_t count = 0U; + + // CMD16 (SET_BLOCKLEN) + if (send_command_and_check(Command_Index::CMD16, 64U, + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD16]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD16_FAILED; + } + + // CMD55 (APP_CMD) + if (send_command_and_check(Command_Index::CMD55, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + + sdio_.set_data_state_machine_and_send(Data_Timeout, 64U, Block_Size::BYTES_64, Transfer_Mode::BLOCK, Transfer_Direction::CARD_TO_SDIO, true); + + // ACMD13 (SD_STATUS) + if (send_command_and_check(Command_Index::ACMD13, 0U, + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::ACMD13]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::ACMD13_FAILED; + } + + while (!sdio_.check_scr_flags()) { + if (sdio_.get_flag(Status_Flags::FLAG_RFH)) { + for (count = 0U; count < FIFO_Half_Words; count++) { + *(status + count) = sdio_.read_fifo_word(); + } + status += FIFO_Half_Words; + } + + //SDIO_Error_Type result = SDIO_Error_Type::OK; + if (sdio_.get_flag(Status_Flags::FLAG_DTCRCERR)) { + sdio_.clear_flag(Clear_Flags::FLAG_DTCRCERRC); + return SDIO_Error_Type::DATA_CRC_ERROR; + } else if (sdio_.get_flag(Status_Flags::FLAG_DTTMOUT)) { + sdio_.clear_flag(Clear_Flags::FLAG_DTTMOUTC); + return SDIO_Error_Type::DATA_TIMEOUT; + } else if (sdio_.get_flag(Status_Flags::FLAG_RXORE)) { + sdio_.clear_flag(Clear_Flags::FLAG_RXOREC); + return SDIO_Error_Type::RX_FIFO_OVERRUN; + } else if (sdio_.get_flag(Status_Flags::FLAG_STBITE)) { + sdio_.clear_flag(Clear_Flags::FLAG_STBITEC); + return SDIO_Error_Type::START_BIT_ERROR; + } + while (sdio_.get_flag(Status_Flags::FLAG_RXDTVAL)) { + *status = sdio_.read_fifo_word(); + ++status; + } + + sdio_.clear_multiple_interrupt_flags(clear_data_flags); + status -= 16U; + for (count = 0U; count < 16U; count++) { + status[count] = ((status[count] & 0xFFU) << 24U) | + ((status[count] & (0xFFU << 8U)) << 8U) | + ((status[count] & (0xFFU << 16U)) >> 8U) | + ((status[count] & (0xFFU << 24U)) >> 24U); + } + } + + return SDIO_Error_Type::OK; +} + +void CardDMA::check_dma_complete() { + while ((dma_.get_flag(dma::Status_Flags::FLAG_FTFIF)) || (dma_.get_flag(dma::Status_Flags::FLAG_ERRIF))) { + // Wait for the IRQ handler to clear + } +} + +auto CardDMA::stop_transfer() -> SDIO_Error_Type { + // CMD12 (STOP_TRANSMISSION) + if (send_command_and_check(Command_Index::CMD12, 0, Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD12]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD12_FAILED; + } + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_transfer_state() -> Transfer_State { + Transfer_State transfer_state = Transfer_State::IDLE; + if (sdio_.get_flag(Status_Flags::FLAG_TXRUN) | sdio_.get_flag(Status_Flags::FLAG_RXRUN)) { + transfer_state = Transfer_State::BUSY; + } + + return transfer_state; +} + +[[nodiscard]] auto CardDMA::get_card_capacity() const -> uint32_t { + auto extract_bits = [](uint32_t value, uint8_t start_bit, uint8_t length) -> uint32_t { + return (value >> start_bit) & ((1U << length) - 1U); + }; + + uint32_t capacity = 0U; + uint32_t device_size = 0U; + + if (card_type_ == Card_Type::SDCARD_STANDARD_CAPACITY) { + // Extract fields from CSD data using bit manipulation + uint8_t device_size_high = static_cast(extract_bits(sdcard_csd_[1], 8U, 2U)); // Bits [73:72] + uint8_t device_size_mid = static_cast(extract_bits(sdcard_csd_[1], 0U, 8U)); // Bits [71:64] + uint8_t device_size_low = static_cast(extract_bits(sdcard_csd_[2], 24U, 2U)); // Bits [63:62] + + device_size = static_cast((device_size_high) << 10U) | + static_cast((device_size_mid) << 2U) | + static_cast((device_size_low)); + + uint8_t device_size_multiplier_high = static_cast(extract_bits(sdcard_csd_[2], 16U, 2U)); // Bits [49:48] + uint8_t device_size_multiplier_low = static_cast(extract_bits(sdcard_csd_[2], 8U, 1U)); // Bit [47] + uint8_t device_size_multiplier = static_cast((device_size_multiplier_high << 1U) | device_size_multiplier_low); + uint8_t read_block_length = static_cast(extract_bits(sdcard_csd_[1], 16U, 4U)); // Bits [83:80] + + // Capacity = (device_size + 1) * MULT * BLOCK_LEN + uint32_t mult = static_cast(1U << (device_size_multiplier + 2U)); // MULT = 2 ^ (C_SIZE_MULT + 2) + uint32_t block_length = static_cast(1U << read_block_length); // BLOCK_LEN = 2 ^ READ_BL_LEN + + capacity = (device_size + 1U) * mult * block_length; + capacity /= KILOBYTE; // Convert capacity to kilobytes + + } else if (card_type_ == Card_Type::SDCARD_HIGH_CAPACITY) { + // High-capacity card calculation + uint8_t device_size_high = static_cast(extract_bits(sdcard_csd_[1], 0U, 6U)); // Bits [69:64] + uint8_t device_size_mid = static_cast(extract_bits(sdcard_csd_[2], 24U, 8U)); // Bits [55:48] + uint8_t device_size_low = static_cast(extract_bits(sdcard_csd_[2], 16U, 8U)); // Bits [47:40] + + device_size = static_cast((device_size_high) << 16U) | + static_cast((device_size_mid) << 8U) | + static_cast(device_size_low); + + // Capacity in kilobytes + capacity = (device_size + 1U) * BLOCK_SIZE; + } + + return capacity; +} + +auto CardDMA::get_card_specific_data(Card_Info* info) -> SDIO_Error_Type { + if (info == nullptr) return SDIO_Error_Type::INVALID_PARAMETER; + + // Store basic card information + info->type = card_type_; + info->relative_address = sdcard_rca_; + + // Helper function to convert 32-bit registers to byte array + auto convert_registers_to_bytes = [](const uint32_t* registers, uint8_t* bytes, size_t reg_count) { + for (size_t i = 0U; i < reg_count; ++i) { + for (size_t j = 0U; j < 4U; ++j) { + bytes[i * 4U + j] = (registers[i] >> (24U - j * 8U)) & 0xFFU; + } + } + }; + + // Process CID data + uint8_t cid_bytes[16]; + convert_registers_to_bytes(sdcard_cid_, cid_bytes, 4); + + info->cid = { + .manufacture_id = cid_bytes[0], + .oem_id = static_cast( + (cid_bytes[1] << 8U) | + cid_bytes[2] + ), + .name0 = static_cast( + (cid_bytes[3] << 24U) | + (cid_bytes[4] << 16U) | + (cid_bytes[5] << 8U) | + cid_bytes[6] + ), + .name1 = cid_bytes[7], + .revision = cid_bytes[8], + .serial_number = static_cast( + (cid_bytes[9] << 24U) | + (cid_bytes[10] << 16U) | + (cid_bytes[11] << 8U) | + cid_bytes[12] + ), + .manufacture_date = static_cast( + ((cid_bytes[13] & 0x0FU) << 8U) | + cid_bytes[14] + ), + .checksum = static_cast(((cid_bytes[15] & 0xFEU) >> 1U)) + }; + + // Process CSD data + uint8_t csd_bytes[16]; + convert_registers_to_bytes(sdcard_csd_, csd_bytes, 4U); + + // Fill common CSD fields + info->csd = { + .transfer_speed = csd_bytes[3], + .card_command_class = static_cast((csd_bytes[4] << 4U) | ((csd_bytes[5] & 0xF0U) >> 4U)), + }; + + // Process card-type specific CSD fields and calculate capacity + if (card_type_ == Card_Type::SDCARD_STANDARD_CAPACITY) { + process_sdsc_specific_csd(info, csd_bytes); + } else if (card_type_ == Card_Type::SDCARD_HIGH_CAPACITY) { + process_sdhc_specific_csd(info, csd_bytes); + } + + // Fill remaining common CSD fields + process_common_csd_tail(info, csd_bytes); + + return SDIO_Error_Type::OK; +} + +constexpr auto CardDMA::get_data_block_size_index(uint16_t size) -> Block_Size { + if (size < 1 || size > 16384) return Block_Size::BYTES_1; + + // Check if size is a power of two + if ((size & (size - 1)) != 0) return Block_Size::BYTES_1; + + // Count trailing zeros to find the index + uint16_t index = 0; + while ((size >>= 1) != 0) ++index; + + return static_cast(index); +} + +auto CardDMA::get_card_state(Card_State* card_state) -> SDIO_Error_Type { + // CMD13 (SEND_STATUS) + if (send_command_and_check(Command_Index::CMD13, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD13]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD13_FAILED; + } + + uint32_t response = sdio_.get_response(Response_Type::RESPONSE0); + *card_state = static_cast((response >> 9U) & CardStateMask); + if ((response & All_R1_Error_Bits) == 0U) { + return SDIO_Error_Type::OK; + } + + if (response & All_R1_Error_Bits) { + for (const auto& entry : errorTableR1) { + if (TEST(response, entry.bit_position)) { + return entry.errorType; + } + } + return SDIO_Error_Type::ERROR; + } + + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_command_sent_result() -> SDIO_Error_Type { + constexpr uint32_t MAX_TIMEOUT = 0x00FFFFFFU; + uint32_t timeout = MAX_TIMEOUT; + + // Wait for command sent flag or timeout + while (!sdio_.get_flag(Status_Flags::FLAG_CMDSEND) && timeout) { + --timeout; + } + + // Check if timeout occurred + if (timeout == 0U) { + return SDIO_Error_Type::RESPONSE_TIMEOUT; + } + + // Clear command flags and return success + sdio_.clear_multiple_interrupt_flags(clear_command_flags); + return SDIO_Error_Type::OK; +} + +auto CardDMA::check_sdio_status(Command_Index index, bool check_index, bool ignore_crc) -> SDIO_Error_Type { + // Wait until one of the relevant flags is set + if (!sdio_.wait_cmd_flags()) { + return SDIO_Error_Type::RESPONSE_TIMEOUT; + } + + // Check the cmd received bit first, since noise can sometimes + // cause the timeout bit to get erroneously set + // If cmd was received we can safely ignore other checks + if (sdio_.get_flag(Status_Flags::FLAG_CMDRECV)) { + // If cmd was received, check the index + // Responses that dont do an index check will send an invalid cmd index + if (check_index && (index != Command_Index::INVALID)) { + uint8_t received_idx = sdio_.get_command_index(); + if (received_idx != static_cast(index)) { + sdio_.clear_multiple_interrupt_flags(clear_command_flags); + return SDIO_Error_Type::ILLEGAL_COMMAND; + } + } + + // Command received successfully + sdio_.clear_multiple_interrupt_flags(clear_command_flags); + return SDIO_Error_Type::OK; + } + + // Check for timeout + if (sdio_.get_flag(Status_Flags::FLAG_CMDTMOUT)) { + sdio_.clear_flag(Clear_Flags::FLAG_CMDTMOUTC); + return SDIO_Error_Type::RESPONSE_TIMEOUT; + } + + // Check for CRC error if not ignored + if (!ignore_crc && sdio_.get_flag(Status_Flags::FLAG_CCRCERR)) { + sdio_.clear_flag(Clear_Flags::FLAG_CCRCERRC); + return SDIO_Error_Type::COMMAND_CRC_ERROR; + } + + // Final index check (redundant with the first check, but keeping for safety) + // This code path should rarely be taken due to the earlier checks + if (check_index && (index != Command_Index::INVALID)) { + uint8_t received_idx = sdio_.get_command_index(); + if (received_idx != static_cast(index)) { + sdio_.clear_multiple_interrupt_flags(clear_command_flags); + return SDIO_Error_Type::ILLEGAL_COMMAND; + } + } + + // Clear all flags and return success + sdio_.clear_multiple_interrupt_flags(clear_command_flags); + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_r1_result(Command_Index index) -> SDIO_Error_Type { + SDIO_Error_Type result = check_sdio_status(index, true, false); + if (result != SDIO_Error_Type::OK) return result; + + // Get the R1 response and check for errors + uint32_t response = sdio_.get_response(Response_Type::RESPONSE0); + + if (response & All_R1_Error_Bits) { + for (const auto& entry : errorTableR1) { + if (TEST(response, entry.bit_position)) { + return entry.errorType; + } + } + return SDIO_Error_Type::ERROR; + } + + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_r6_result(Command_Index index, uint16_t* rca) -> SDIO_Error_Type { + SDIO_Error_Type result = check_sdio_status(index, true, false); + if (result != SDIO_Error_Type::OK) return result; + + uint32_t response = sdio_.get_response(Response_Type::RESPONSE0); + + if (response & R6_Error_Bits) { + for (const auto& entry : errorTableR6) { + if (TEST(response, entry.bit_position)) { + return entry.errorType; + } + } + return SDIO_Error_Type::ERROR; + } + *rca = static_cast(response >> 16U); + + return SDIO_Error_Type::OK; +} + +auto CardDMA::get_scr(uint16_t rca, uint32_t* scr) -> SDIO_Error_Type { + uint32_t temp_scr[2] = {0U, 0U}; + uint32_t index_scr = 0U; + + // CMD16 (SET_BLOCKLEN) + if (send_command_and_check(Command_Index::CMD16, 8U, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD16]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD16_FAILED; + } + + // CMD55 (APP_CMD) + if (send_command_and_check(Command_Index::CMD55, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD55]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD55_FAILED; + } + + // Set data parameters + sdio_.set_data_state_machine_and_send(Data_Timeout, 8U, Block_Size::BYTES_8, + Transfer_Mode::BLOCK, Transfer_Direction::CARD_TO_SDIO, true); + + // ACMD51 (SEND_SCR) + if (send_command_and_check(Command_Index::ACMD51, 0U, Command_Response::RSP_SHORT, + Wait_Type::WT_NONE, [this, cmd = Command_Index::ACMD51]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::ACMD51_FAILED; + } + + // Store SCR + while (!sdio_.check_scr_flags()) { + if (sdio_.get_flag(Status_Flags::FLAG_RXDTVAL)) { + temp_scr[index_scr++] = sdio_.read_fifo_word(); + } + } + + // Check for errors + if (sdio_.get_flag(Status_Flags::FLAG_DTTMOUT)) { + sdio_.clear_flag(Clear_Flags::FLAG_DTTMOUTC); + return SDIO_Error_Type::DATA_TIMEOUT; + } + else if (sdio_.get_flag(Status_Flags::FLAG_DTCRCERR)) { + sdio_.clear_flag(Clear_Flags::FLAG_DTCRCERRC); + return SDIO_Error_Type::DATA_CRC_ERROR; + } + else if (sdio_.get_flag(Status_Flags::FLAG_RXORE)) { + sdio_.clear_flag(Clear_Flags::FLAG_RXOREC); + return SDIO_Error_Type::RX_FIFO_OVERRUN; + } + + sdio_.clear_multiple_interrupt_flags(clear_data_flags); + + constexpr uint32_t BYTE0_MASK = 0xFFU; + constexpr uint32_t BYTE1_MASK = 0xFF00U; + constexpr uint32_t BYTE2_MASK = 0xFF0000U; + constexpr uint32_t BYTE3_MASK = 0xFF000000U; + + // Byte-swap the SCR values (convert from big-endian to little-endian) + scr[0] = ((temp_scr[1] & BYTE0_MASK) << 24) | + ((temp_scr[1] & BYTE1_MASK) << 8) | + ((temp_scr[1] & BYTE2_MASK) >> 8) | + ((temp_scr[1] & BYTE3_MASK) >> 24); + + scr[1] = ((temp_scr[0] & BYTE0_MASK) << 24) | + ((temp_scr[0] & BYTE1_MASK) << 8) | + ((temp_scr[0] & BYTE2_MASK) >> 8) | + ((temp_scr[0] & BYTE3_MASK) >> 24); + + return SDIO_Error_Type::OK; +} + +// DMA for rx/tx is always DMA1 channel 3 +void CardDMA::set_dma_parameters(uint8_t* buf, uint32_t count, bool is_write) { + constexpr uint32_t flag_bits = (1U << static_cast(dma::INTF_Bits::GIF3)); + dma_.clear_flags(flag_bits); + + // Disable and reset DMA + dma_.set_channel_enable(false); + dma_.clear_channel(); + + dma_.init(dma::DMA_Config{ + .count = count, + .memory_address = static_cast(reinterpret_cast(buf)), + .peripheral_address = static_cast(reinterpret_cast(sdio_.reg_address(SDIO_Regs::FIFO))), + .peripheral_bit_width = dma::Bit_Width::WIDTH_32BIT, + .memory_bit_width = dma::Bit_Width::WIDTH_32BIT, + .peripheral_increase = dma::Increase_Mode::INCREASE_DISABLE, + .memory_increase = dma::Increase_Mode::INCREASE_ENABLE, + .channel_priority = dma::Channel_Priority::MEDIUM_PRIORITY, + .direction = is_write ? dma::Transfer_Direction::M2P : dma::Transfer_Direction::P2M + }); + + dma_.set_memory_to_memory_enable(false); + dma_.set_circulation_mode_enable(false); + + // Enable DMA interrupts for transfer complete and error + dma_.set_interrupt_enable(dma::Interrupt_Type::INTR_FTFIE, true); + dma_.set_interrupt_enable(dma::Interrupt_Type::INTR_ERRIE, true); + + // Start the DMA channel + dma_.set_channel_enable(true); +} + +auto CardDMA::wait_for_card_ready() -> SDIO_Error_Type { + constexpr uint32_t MAX_TIMEOUT = 0x00FFFFFFU; + uint32_t timeout = MAX_TIMEOUT; + uint32_t response = sdio_.get_response(Response_Type::RESPONSE0); + + // Poll until card is ready for data or timeout occurs + while (((response & static_cast(R1_Status::READY_FOR_DATA)) == 0U) && timeout) { + --timeout; + + // CMD13 (SEND_STATUS) + if (send_command_and_check(Command_Index::CMD13, static_cast(sdcard_rca_ << RCA_Shift), + Command_Response::RSP_SHORT, Wait_Type::WT_NONE, [this, cmd = Command_Index::CMD13]() { + return get_r1_result(cmd); + }) != SDIO_Error_Type::OK) { + return SDIO_Error_Type::CMD13_FAILED; + } + + // Get updated response + response = sdio_.get_response(Response_Type::RESPONSE0); + } + + // Return error if timeout occurred, otherwise success + return timeout ? SDIO_Error_Type::OK : SDIO_Error_Type::ERROR; +} + +} // namespace sdio + +sdio::CardDMA& CardDMA_I = sdio::CardDMA::get_instance(); + +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/sd/SDCard.h b/Marlin/src/HAL/GD32_MFL/sd/SDCard.h new file mode 100644 index 0000000000..de28c40809 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/sd/SDCard.h @@ -0,0 +1,222 @@ +// +// MFL gd32f30x SDCARD using DMA through SDIO in C++ +// +// Copyright (C) 2025 B. Mourit +// +// This software is free software: you can redistribute it and/or modify it under the terms of the +// GNU Lesser General Public License as published by the Free Software Foundation, +// either version 3 of the License, or (at your option) any later version. +// +// This software 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 Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public License along with this software. +// If not, see . +// +#pragma once + +#include "../../../inc/MarlinConfig.h" + +#include + +namespace sdio { + +class DMA; + +class CardDMA { +public: + static auto get_instance() -> CardDMA&; + + // Initialization + auto init() -> SDIO_Error_Type; + auto card_init() -> SDIO_Error_Type; + + // Startup and shutdown procedures + auto begin_startup_procedure() -> SDIO_Error_Type; + void begin_shutdown_procedure(); + + // Configuration + auto set_hardware_bus_width(Bus_Width width) -> SDIO_Error_Type; + auto send_bus_width_command(uint32_t width_value) -> SDIO_Error_Type; + + // Main read/write/erase functions + auto read(uint8_t* buf, uint32_t address, uint32_t count) -> SDIO_Error_Type; + auto write(uint8_t* buf, uint32_t address, uint32_t count) -> SDIO_Error_Type; + auto erase(uint32_t address_start, uint32_t address_end) -> SDIO_Error_Type; + + // Card select + auto select_deselect() -> SDIO_Error_Type; + + // Status and state + auto get_card_interface_status(uint32_t* status) -> SDIO_Error_Type; + auto get_sdcard_status(uint32_t* status) -> SDIO_Error_Type; + auto get_transfer_state() -> Transfer_State; + auto get_card_state(Card_State* card_state) -> SDIO_Error_Type; + auto check_sdio_status(Command_Index index = Command_Index::INVALID, bool check_index = false, bool ignore_crc = false) -> SDIO_Error_Type; + + // DMA + void set_dma_parameters(uint8_t* buf, uint32_t count, bool is_write); + void check_dma_complete(); + + // Stop transfer + auto stop_transfer() -> SDIO_Error_Type; + + // Card information + auto get_card_specific_data(Card_Info* info) -> SDIO_Error_Type; + constexpr auto get_data_block_size_index(uint16_t size) -> Block_Size; + [[nodiscard]] auto get_card_capacity() const -> uint32_t; + + // SDIO configuration + void sdio_configure(const SDIO_Config config) { sdio_.init(config); } + + // Interrupt handler + void handle_interrupts(); + + // Variable stored parameters + auto get_scr(uint16_t rca, uint32_t* scr) -> SDIO_Error_Type; + auto store_cid() -> SDIO_Error_Type; + auto store_csd() -> SDIO_Error_Type; + + // Inlined accessor methods + auto get_config() -> SDIO_Config& { return config_; } + auto get_dma_instance() -> dma::DMA& { return dma_; } + void set_data_end_interrupt() { sdio_.set_interrupt_enable(Interrupt_Type::DTENDIE, true); } + void set_sdio_dma_enable(bool enable) { sdio_.set_dma_enable(enable); } + auto get_is_sdio_rx() -> bool { return is_rx_; } + void clear_sdio_data_flags() { sdio_.clear_multiple_interrupt_flags(clear_data_flags); } + void clear_sdio_cmd_flags() { sdio_.clear_multiple_interrupt_flags(clear_command_flags); } + void clear_sdio_common_flags() { sdio_.clear_multiple_interrupt_flags(clear_common_flags); } + auto get_state() -> Operational_State { return current_state_; } + void set_state(Operational_State state) { current_state_ = state; } + void set_transfer_error(SDIO_Error_Type error) { transfer_error_ = error; } + void set_transfer_end(bool end) { transfer_end_ = end; }; + + auto set_desired_clock(uint32_t desired_clock, bool wide_bus, bool low_power) -> SDIO_Error_Type { + sdio_.init(SDIO_Config{ + .desired_clock = desired_clock, + .enable_bypass = false, + .enable_powersave = low_power, + .enable_hwclock = false, + .clock_edge = Clock_Edge::RISING_EDGE, + .width = wide_bus ? Bus_Width::WIDTH_4BIT : Bus_Width::WIDTH_1BIT + }); + + sync_domains(); + desired_clock_ = desired_clock; + + return SDIO_Error_Type::OK; + } + +private: + CardDMA(); + + // Prevent copying or assigning + CardDMA(const CardDMA&) = delete; + auto operator=(const CardDMA&) -> CardDMA& = delete; + + // Helper function + auto wait_for_card_ready() -> SDIO_Error_Type; + + // Member variables + alignas(4) uint32_t sdcard_csd_[4]; + alignas(4) uint32_t sdcard_cid_[4]; + alignas(4) uint32_t sdcard_scr_[2]; + uint32_t desired_clock_; + uint32_t total_bytes_; + SDIO& sdio_; + SDIO_Config& config_; + const dma::DMA_Base dmaBase_; + const dma::DMA_Channel dmaChannel_; + dma::DMA& dma_; + uint16_t sdcard_rca_; + SDIO_Error_Type transfer_error_; + Interface_Version interface_version_; + Card_Type card_type_; + Operational_State current_state_; + bool transfer_end_; + bool multiblock_; + bool is_rx_; + + // Private helper methods + auto validate_voltage() -> SDIO_Error_Type; + auto get_command_sent_result() -> SDIO_Error_Type; + auto get_r1_result(Command_Index index) -> SDIO_Error_Type; + auto get_r6_result(Command_Index index, uint16_t* rca) -> SDIO_Error_Type; + auto get_r7_result() -> SDIO_Error_Type { return check_sdio_status(Command_Index::INVALID, false, false); }; + void sync_domains() { delayMicroseconds(8); } + + auto validate_transfer_params(uint32_t* buf, uint16_t size) -> bool { + if (buf == nullptr) return false; + // Size must be > 0, <= 2048 and power of 2 + return size > 0U && size <= 2048U && !(size & (size - 1U)); + } + + void process_sdsc_specific_csd(Card_Info* info, const uint8_t* csd_bytes) { + const uint32_t device_size = ((csd_bytes[6] & 0x3U) << 10) | + (csd_bytes[7] << 2) | + ((csd_bytes[8] >> 6) & 0x3U); + + const uint8_t device_size_multiplier = ((csd_bytes[9] & 0x3U) << 1) | + ((csd_bytes[10] >> 7) & 0x1U); + + // Store calculated values + info->csd.device_size = device_size; + info->csd.device_size_multiplier = device_size_multiplier; + + // Calculate block size and capacity + info->block_size = 1U << info->csd.read_block_length; + info->capacity = (device_size + 1U) * + (1U << (device_size_multiplier + 2U)) * + info->block_size; + } + + void process_sdhc_specific_csd(Card_Info* info, const uint8_t* csd_bytes) { + info->csd.device_size = static_cast((csd_bytes[7] & 0x3FU) << 16) | + static_cast((csd_bytes[8]) << 8) | + static_cast(csd_bytes[9]); + + // Set block size and calculate capacity + info->block_size = BLOCK_SIZE; + info->capacity = static_cast((info->csd.device_size + 1U) * + BLOCK_SIZE * KILOBYTE); + } + + void process_common_csd_tail(Card_Info* info, const uint8_t* csd_bytes) { + // Calculate sector_size + info->csd.sector_size = static_cast(((csd_bytes[9] & 0x3FU) << 1) | + (csd_bytes[10] & 0x80U) >> 7); + + // Calculate speed_factor and write_block_length + info->csd.speed_factor = static_cast((csd_bytes[11] & 0x1CU) >> 2); + info->csd.write_block_length = static_cast(((csd_bytes[11] & 0x3U) << 2) | + ((csd_bytes[12] & 0xC0U) >> 6)); + + // Calculate checksum + info->csd.checksum = static_cast((csd_bytes[15] & 0xFEU) >> 1); + } + + void disable_all_interrupts() { + sdio_.set_interrupt_enable(Interrupt_Type::DTCRCERRIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::DTTMOUTIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::DTENDIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::STBITEIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::TFHIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::RFHIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::TXUREIE, false); + sdio_.set_interrupt_enable(Interrupt_Type::RXOREIE, false); + } + + template + auto send_command_and_check(Command_Index command, uint32_t argument, + Command_Response response, Wait_Type type, CheckFunc check_result) -> SDIO_Error_Type { + sdio_.set_command_state_machine(command, argument, response, type); + sync_domains(); + sdio_.set_command_state_machine_enable(true); + return check_result(); + } +}; + +} // namespace sdio + +extern sdio::CardDMA& CardDMA_I; diff --git a/Marlin/src/HAL/GD32_MFL/sd/sdio.cpp b/Marlin/src/HAL/GD32_MFL/sd/sdio.cpp new file mode 100644 index 0000000000..a474c1977e --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/sd/sdio.cpp @@ -0,0 +1,231 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(ONBOARD_SDIO) + +#include +#include +#include "SDCard.h" +#include "sdio.h" + +using namespace sdio; + +#define TARGET_CLOCK 6000000U +#define BLOCK_SIZE 512U +#define CARD_TIMEOUT 500 // ms +#define READ_RETRIES 3U + +inline constexpr uint32_t TARGET_SDIO_CLOCK = TARGET_CLOCK; +inline constexpr uint32_t SDIO_BLOCK_SIZE = BLOCK_SIZE; +inline constexpr uint32_t SD_TIMEOUT = CARD_TIMEOUT; +inline constexpr uint8_t SDIO_READ_RETRIES = READ_RETRIES; + +Card_State cardState = Card_State::READY; + +auto SDIO_SetBusWidth(Bus_Width width) -> bool { + return (CardDMA_I.set_hardware_bus_width(width) == SDIO_Error_Type::OK); +} + +void mfl_sdio_init() { + pinOpsPinout(SD_CMD_PinOps, static_cast(SDIO_CMD_PIN)); + pinOpsPinout(SD_CK_PinOps, static_cast(SDIO_CK_PIN)); + pinOpsPinout(SD_DATA0_PinOps, static_cast(SDIO_D0_PIN)); + pinOpsPinout(SD_DATA1_PinOps, static_cast(SDIO_D1_PIN)); + pinOpsPinout(SD_DATA2_PinOps, static_cast(SDIO_D2_PIN)); + pinOpsPinout(SD_DATA3_PinOps, static_cast(SDIO_D3_PIN)); + + NVIC_EnableIRQ(DMA1_Channel3_4_IRQn); + NVIC_EnableIRQ(SDIO_IRQn); +} + +bool SDIO_Init() { + SDIO_Error_Type result = SDIO_Error_Type::OK; + uint8_t retryCount = SDIO_READ_RETRIES; + + mfl_sdio_init(); + + uint8_t retries = retryCount; + for (;;) { + hal.watchdog_refresh(); + result = CardDMA_I.init(); + if (result == SDIO_Error_Type::OK) break; + if (!--retries) return false; + } + + CardDMA_I.set_desired_clock(TARGET_SDIO_CLOCK, false, false); + + retries = retryCount; + for (;;) { + hal.watchdog_refresh(); + if (SDIO_SetBusWidth(Bus_Width::WIDTH_4BIT)) break; + if (!--retries) break; + } + + CardDMA_I.set_desired_clock(TARGET_SDIO_CLOCK, true, true); + + // Fallback + if (!retries) { + mfl_sdio_init(); + retries = retryCount; + for (;;) { + hal.watchdog_refresh(); + result = CardDMA_I.init(); + if (result == SDIO_Error_Type::OK) break; + if (!--retries) return false; + } + CardDMA_I.set_desired_clock(TARGET_SDIO_CLOCK, false, true); + } + + return true; +} + +static bool SDIO_ReadWriteBlock_DMA(uint32_t block, const uint8_t* src, uint8_t* dst) { + hal.watchdog_refresh(); + SDIO_Error_Type result = SDIO_Error_Type::OK; + + // Write + if (src) { + result = CardDMA_I.write(reinterpret_cast(const_cast(src)), block, 1); + } + // Read + else { + result = CardDMA_I.read(dst, block, 1); + } + + if (result != SDIO_Error_Type::OK) { + return false; + } + + millis_t timeout = millis() + SD_TIMEOUT; + while (CardDMA_I.get_state() != sdio::Operational_State::READY) { + if (ELAPSED(millis(), timeout)) { + return false; + } + } + + CardDMA_I.check_dma_complete(); + + timeout = millis() + SD_TIMEOUT; + do { + result = CardDMA_I.get_card_state(&cardState); + if (ELAPSED(millis(), timeout)) { + return false; + } + } while (result == SDIO_Error_Type::OK && cardState != sdio::Card_State::TRANSFER); + + return true; +} + +bool SDIO_ReadBlock(uint32_t block, uint8_t* dst) { + // Check if the address is aligned to 4 bytes + if (reinterpret_cast(dst) & 0x03) { + return false; + } + + uint8_t retries = SDIO_READ_RETRIES; + while (retries--) { + if (SDIO_ReadWriteBlock_DMA(block, nullptr, dst)) { + return true; + } + } + + return false; +} + +bool SDIO_WriteBlock(uint32_t block, const uint8_t* src) { + // Check if the address is aligned to 4 bytes + if (reinterpret_cast(src) & 0x03) { + return false; + } + + uint8_t retries = SDIO_READ_RETRIES; + while (retries--) { + if (SDIO_ReadWriteBlock_DMA(block, src, nullptr)) { + return true; + delay(10); + } + } + + return false; +} + +bool SDIO_IsReady() { + return (CardDMA_I.get_state() == sdio::Operational_State::READY); +} + +uint32_t SDIO_GetCardSize() { + return CardDMA_I.get_card_capacity(); +} + +// DMA interrupt handler +void DMA1_IRQHandler() { + auto& dma_instance = CardDMA_I.get_dma_instance(); + bool is_receive = CardDMA_I.get_is_sdio_rx(); + + // Check for Transfer Complete Interrupt + if (dma_instance.get_interrupt_flag(dma::Interrupt_Flags::INTR_FLAG_FTFIF)) { + dma_instance.set_interrupt_enable(dma::Interrupt_Type::INTR_FTFIE, false); + dma_instance.set_interrupt_enable(dma::Interrupt_Type::INTR_ERRIE, false); + dma_instance.clear_interrupt_flag(dma::Interrupt_Flags::INTR_FLAG_FTFIF); + if (is_receive) { + CardDMA_I.set_sdio_dma_enable(false); + CardDMA_I.clear_sdio_data_flags(); + CardDMA_I.set_state(sdio::Operational_State::READY); + } else { + CardDMA_I.set_data_end_interrupt(); + } + // Signal that transfer is complete + CardDMA_I.set_transfer_end(true); + } + + else if (dma_instance.get_interrupt_flag(dma::Interrupt_Flags::INTR_FLAG_ERRIF)) { + dma_instance.set_interrupt_enable(dma::Interrupt_Type::INTR_HTFIE, false); + dma_instance.set_interrupt_enable(dma::Interrupt_Type::INTR_ERRIE, false); + dma_instance.set_interrupt_enable(dma::Interrupt_Type::INTR_FTFIE, false); + // Clear all flags + dma_instance.clear_interrupt_flag(dma::Interrupt_Flags::INTR_FLAG_GIF); + // Signal that an error occurred + CardDMA_I.set_transfer_error(SDIO_Error_Type::ERROR); + CardDMA_I.set_state(sdio::Operational_State::READY); + } +} + +extern "C" { + + void SDIO_IRQHandler(void) { + CardDMA_I.handle_interrupts(); + } + + void DMA1_Channel3_4_IRQHandler(void) { + DMA1_IRQHandler(); + } + +} // extern "C" + +#endif // ONBOARD_SDIO +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/sd/sdio.h b/Marlin/src/HAL/GD32_MFL/sd/sdio.h new file mode 100644 index 0000000000..a39e8c7a66 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/sd/sdio.h @@ -0,0 +1,36 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include +#include + +#define SDIO_D0_PIN PC8 +#define SDIO_D1_PIN PC9 +#define SDIO_D2_PIN PC10 +#define SDIO_D3_PIN PC11 +#define SDIO_CK_PIN PC12 +#define SDIO_CMD_PIN PD2 + +void sdio_mfl_init(); +auto SDIO_SetBusWidth(sdio::Bus_Width width) -> bool; +void DMA1_IRQHandler(dma::DMA_Channel channel); diff --git a/Marlin/src/HAL/GD32_MFL/spi_pins.h b/Marlin/src/HAL/GD32_MFL/spi_pins.h new file mode 100644 index 0000000000..c8a5836838 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/spi_pins.h @@ -0,0 +1,32 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + */ +#pragma once + +// Define SPI Pins: SCK, MISO, MOSI +#ifndef SD_SCK_PIN + #define SD_SCK_PIN PIN_SPI_SCK +#endif +#ifndef SD_MISO_PIN + #define SD_MISO_PIN PIN_SPI_MISO +#endif +#ifndef SD_MOSI_PIN + #define SD_MOSI_PIN PIN_SPI_MOSI +#endif diff --git a/Marlin/src/HAL/GD32_MFL/temp_soc.h b/Marlin/src/HAL/GD32_MFL/temp_soc.h new file mode 100644 index 0000000000..bd78fba5b9 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/temp_soc.h @@ -0,0 +1,29 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#define TS_TYPICAL_V 1.405 +#define TS_TYPICAL_TEMP 25 +#define TS_TYPICAL_SLOPE 4.5 + +// TODO: Implement voltage scaling (calibrated Vrefint) and ADC resolution scaling (when applicable) +#define TEMP_SOC_SENSOR(RAW) ((TS_TYPICAL_V - (RAW) / float(OVERSAMPLENR) / float(HAL_ADC_RANGE) * (float(ADC_VREF_MV) * 0.001f)) / ((TS_TYPICAL_SLOPE) * 0.001f) + TS_TYPICAL_TEMP) diff --git a/Marlin/src/HAL/GD32_MFL/timers.cpp b/Marlin/src/HAL/GD32_MFL/timers.cpp new file mode 100644 index 0000000000..632499742c --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/timers.cpp @@ -0,0 +1,240 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef ARDUINO_ARCH_MFL + +#include "../../inc/MarlinConfig.h" +#include "timers.h" + +// ------------------------ +// Local defines +// ------------------------ + +#define SWSERIAL_TIMER_IRQ_PRIORITY_DEFAULT 1 // Requires tight bit timing to communicate reliably with TMC drivers +#define SERVO_TIMER_IRQ_PRIORITY_DEFAULT 1 // Requires tight PWM timing to control a BLTouch reliably +#define STEP_TIMER_IRQ_PRIORITY_DEFAULT 2 +#define TEMP_TIMER_IRQ_PRIORITY_DEFAULT 14 // Low priority avoids interference with other hardware and timers + +#ifndef TIMER_IRQ_PRIORITY + #define TIMER_IRQ_PRIORITY 12 +#endif + +#ifndef STEP_TIMER_IRQ_PRIORITY + #define STEP_TIMER_IRQ_PRIORITY STEP_TIMER_IRQ_PRIORITY_DEFAULT +#endif + +#ifndef TEMP_TIMER_IRQ_PRIORITY + #define TEMP_TIMER_IRQ_PRIORITY TEMP_TIMER_IRQ_PRIORITY_DEFAULT +#endif + +#if HAS_TMC_SW_SERIAL + #include + #ifndef SWSERIAL_TIMER_IRQ_PRIORITY + #define SWSERIAL_TIMER_IRQ_PRIORITY SWSERIAL_TIMER_IRQ_PRIORITY_DEFAULT + #endif +#endif + +#if HAS_SERVOS + #include "Servo.h" + #ifndef SERVO_TIMER_IRQ_PRIORITY + #define SERVO_TIMER_IRQ_PRIORITY SERVO_TIMER_IRQ_PRIORITY_DEFAULT + #endif +#endif + +#if ENABLED(SPEAKER) + // The MFL framework default timer priority is 12. The TEMP timer must have lower priority + // than this due to the long running temperature ISR, and STEP timer should higher priority. + #if !(TIMER_IRQ_PRIORITY > STEP_TIMER_IRQ_PRIORITY && TIMER_IRQ_PRIORITY < TEMP_TIMER_IRQ_PRIORITY) + #error "Default timer interrupt priority is unspecified or set to a value which may degrade performance." + #endif +#endif + +#ifndef HAL_TIMER_RATE + #define HAL_TIMER_RATE GetStepperTimerClkFreq() +#endif + +#ifndef STEP_TIMER + #define STEP_TIMER MF_TIMER_STEP +#endif +#ifndef TEMP_TIMER + #define TEMP_TIMER MF_TIMER_TEMP +#endif + +GeneralTimer& Step_Timer = GeneralTimer::get_instance(static_cast(STEP_TIMER)); +GeneralTimer& Temp_Timer = GeneralTimer::get_instance(static_cast(TEMP_TIMER)); + +bool is_step_timer_initialized = false; +bool is_temp_timer_initialized = false; + +// ------------------------ +// Public functions +// ------------------------ + +// Retrieves the clock frequency of the stepper timer +uint32_t GetStepperTimerClkFreq() { + // Cache result + static uint32_t clkFreq = Step_Timer.getTimerClockFrequency(); + return clkFreq; +} + +/** + * @brief Starts a hardware timer + * + * If the timer is not already initialized, this function will initialize it with the given frequency. + * The timer is started immediately after initialization + * + * @param timer The timer base index to start + * @param frequency The frequency at which the timer should run + * @return None + */ +void HAL_timer_start(const uint8_t timer_number, const uint32_t frequency) { + if (HAL_timer_initialized(timer_number) || (timer_number != MF_TIMER_STEP && timer_number != MF_TIMER_TEMP)) + return; + + const bool is_step = (timer_number == MF_TIMER_STEP); + const uint8_t priority = is_step ? + static_cast(STEP_TIMER_IRQ_PRIORITY) : + static_cast(TEMP_TIMER_IRQ_PRIORITY); + + // Get the reference of the timer instance + GeneralTimer& timer = is_step ? Step_Timer : Temp_Timer; + + if (is_step) { + timer.setPrescaler(STEPPER_TIMER_PRESCALE); + timer.setRolloverValue(_MIN(static_cast(HAL_TIMER_TYPE_MAX), + (HAL_TIMER_RATE) / (STEPPER_TIMER_PRESCALE)), + TimerFormat::TICK); + is_step_timer_initialized = true; + } + else { + timer.setRolloverValue(frequency, TimerFormat::HERTZ); + is_temp_timer_initialized = true; + } + + timer.setAutoReloadEnable(false); + timer.setInterruptPriority(priority, 0U); + HAL_timer_enable_interrupt(timer_number); + timer.start(); +} + +/** + * @brief Enables the interrupt for the specified timer + * + * @param handle The timer handle for which to enable the interrupt + * @return None + */ +void HAL_timer_enable_interrupt(const uint8_t timer_number) { + if (!HAL_timer_initialized(timer_number)) return; + + GeneralTimer& timer = (timer_number == MF_TIMER_STEP) ? Step_Timer : Temp_Timer; + + if (timer_number == MF_TIMER_STEP && !timer.hasInterrupt()) + timer.attachInterrupt(Step_Handler); + else if (timer_number == MF_TIMER_TEMP && !timer.hasInterrupt()) + timer.attachInterrupt(Temp_Handler); +} + +/** + * @brief Disables the interrupt for the specified timer + * + * @param handle The timer handle for which to disable the interrupt + * @return None + */ +void HAL_timer_disable_interrupt(const uint8_t timer_number) { + if (!HAL_timer_initialized(timer_number)) return; + + GeneralTimer& timer = (timer_number == MF_TIMER_STEP) ? Step_Timer : Temp_Timer; + if (timer_number == MF_TIMER_STEP || timer_number == MF_TIMER_TEMP) + timer.detachInterrupt(); +} + +/** + * @brief Checks if the interrupt is enabled for the specified timer + * + * @param handle The timer handle to check + * @return True if the interrupt is enabled, false otherwise + */ +bool HAL_timer_interrupt_enabled(const uint8_t timer_number) { + if (!HAL_timer_initialized(timer_number)) return false; + + GeneralTimer& timer = (timer_number == MF_TIMER_STEP) ? Step_Timer : Temp_Timer; + return (timer_number == MF_TIMER_STEP || timer_number == MF_TIMER_TEMP) + ? timer.hasInterrupt() + : false; +} + +// Sets the interrupt priorities for timers used by TMC SW serial and servos. +void SetTimerInterruptPriorities() { + TERN_(HAS_TMC_SW_SERIAL, SoftwareSerial::setInterruptPriority(SWSERIAL_TIMER_IRQ_PRIORITY, 0)); + TERN_(HAS_SERVOS, libServo::setInterruptPriority(SERVO_TIMER_IRQ_PRIORITY, 0)); +} + +// ------------------------ +// Detect timer conflicts +// ------------------------ + +TERN_(HAS_TMC_SW_SERIAL, static constexpr timer::TIMER_Base timer_serial[] = {static_cast(TIMER_SERIAL)}); +TERN_(SPEAKER, static constexpr timer::TIMER_Base timer_tone[] = {static_cast(TIMER_TONE)}); +TERN_(HAS_SERVOS, static constexpr timer::TIMER_Base timer_servo[] = {static_cast(TIMER_SERVO)}); + +enum TimerPurpose { + PURPOSE_SERIAL, + PURPOSE_TONE, + PURPOSE_SERVO, + PURPOSE_STEP, + PURPOSE_TEMP +}; + +// List of timers to check for conflicts +// Includes the timer purpose to ease debugging when evaluating at build-time +// This cannot yet account for timers used for PWM output, such as for fans +static constexpr struct { TimerPurpose p; int t; } timers_in_use[] = { + #if HAS_TMC_SW_SERIAL + { PURPOSE_SERIAL, timer_base_to_index(timer_serial[0]) }, // Set in variant.h + #endif + #if ENABLED(SPEAKER) + { PURPOSE_TONE, timer_base_to_index(timer_tone[0]) }, // Set in variant.h + #endif + #if HAS_SERVOS + { PURPOSE_SERVO, timer_base_to_index(timer_servo[0]) }, // Set in variant.h + #endif + { PURPOSE_STEP, MF_TIMER_STEP }, + { PURPOSE_TEMP, MF_TIMER_TEMP }, +}; + +// Verifies if there are any timer conflicts in the timers_in_use array +static constexpr bool verify_no_timer_conflicts() { + for (uint8_t i = 0; i < COUNT(timers_in_use); i++) + for (uint8_t j = i + 1; j < COUNT(timers_in_use); j++) + if (timers_in_use[i].t == timers_in_use[j].t) + return false; + + return true; +} + +// If this assertion fails at compile time, review the timers_in_use array. +// If default_envs is defined properly in platformio.ini, VSCode can evaluate the array +// when hovering over it, making it easy to identify the conflicting timers +static_assert(verify_no_timer_conflicts(), "One or more timer conflict detected. Examine \"timers_in_use\" to help identify conflict."); + +#endif // ARDUINO_ARCH_MFL diff --git a/Marlin/src/HAL/GD32_MFL/timers.h b/Marlin/src/HAL/GD32_MFL/timers.h new file mode 100644 index 0000000000..a5d36d9eca --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/timers.h @@ -0,0 +1,145 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../../inc/MarlinConfig.h" + +#include + +// ------------------------ +// Defines +// ------------------------ + +// Timer configuration constants +#define STEPPER_TIMER_RATE 2000000 +#define TEMP_TIMER_FREQUENCY 1000 + +// Timer instance definitions +#define MF_TIMER_STEP 3 +#define MF_TIMER_TEMP 1 +#define MF_TIMER_PULSE MF_TIMER_STEP + +#define hal_timer_t uint32_t +#define HAL_TIMER_TYPE_MAX UINT16_MAX + +extern uint32_t GetStepperTimerClkFreq(); + +// Timer prescaler calculations +#define STEPPER_TIMER_PRESCALE (GetStepperTimerClkFreq() / STEPPER_TIMER_RATE) // Prescaler = 30 +#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE +#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // Stepper timer ticks per µs +#define PULSE_TIMER_RATE STEPPER_TIMER_RATE +#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US + +// Timer interrupt priorities +#define STEP_TIMER_IRQ_PRIORITY 2 +#define TEMP_TIMER_IRQ_PRIORITY 14 + +#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP) +#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_STEP) +#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP) +#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP) +#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP) + +extern void Step_Handler(); +extern void Temp_Handler(); + +#ifndef HAL_STEP_TIMER_ISR + #define HAL_STEP_TIMER_ISR() void Step_Handler() +#endif +#ifndef HAL_TEMP_TIMER_ISR + #define HAL_TEMP_TIMER_ISR() void Temp_Handler() +#endif + +extern GeneralTimer& Step_Timer; +extern GeneralTimer& Temp_Timer; + +extern bool is_step_timer_initialized; +extern bool is_temp_timer_initialized; + +// Build-time mapping between timer base and index. Used in timers.cpp and fast_pwm.cpp +static inline constexpr struct {timer::TIMER_Base base; uint8_t timer_number;} base_to_index[] = { + { timer::TIMER_Base::TIMER0_BASE, 0 }, + { timer::TIMER_Base::TIMER1_BASE, 1 }, + { timer::TIMER_Base::TIMER2_BASE, 2 }, + { timer::TIMER_Base::TIMER3_BASE, 3 }, + { timer::TIMER_Base::TIMER4_BASE, 4 }, + { timer::TIMER_Base::TIMER5_BASE, 5 }, + { timer::TIMER_Base::TIMER6_BASE, 6 }, + { timer::TIMER_Base::TIMER7_BASE, 7 } +}; + +// Converts a timer base to an integer timer index. +constexpr auto timer_base_to_index(timer::TIMER_Base base) -> int { + for (const auto& timer : base_to_index) { + if (timer.base == base) { + return static_cast(timer.timer_number); + } + } + return -1; +} + +// ------------------------ +// Public functions +// ------------------------ + +void HAL_timer_start(const uint8_t timer, const uint32_t frequency); +void HAL_timer_enable_interrupt(const uint8_t timer); +void HAL_timer_disable_interrupt(const uint8_t timer); +bool HAL_timer_interrupt_enabled(const uint8_t timer); + +// Configure timer priorities for peripherals such as Software Serial or Servos. +// Exposed here to allow all timer priority information to reside in timers.cpp +void SetTimerInterruptPriorities(); + +// FORCE_INLINE because these are used in performance-critical situations +FORCE_INLINE bool HAL_timer_initialized(const uint8_t timer_number) { + return (timer_number == MF_TIMER_STEP) ? is_step_timer_initialized : + (timer_number == MF_TIMER_TEMP) ? is_temp_timer_initialized : + false; +} + +FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_number) { + if (!HAL_timer_initialized(timer_number)) return 0U; + + GeneralTimer& timer = (timer_number == MF_TIMER_STEP) ? Step_Timer : Temp_Timer; + + return (timer_number == MF_TIMER_STEP || timer_number == MF_TIMER_TEMP) + ? timer.getCounter(TimerFormat::TICK) + : 0U; +} + +FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_number, const hal_timer_t value) { + if (!HAL_timer_initialized(timer_number)) return; + + const auto new_value = static_cast(value + 1U); + GeneralTimer& timer = (timer_number == MF_TIMER_STEP) ? Step_Timer : Temp_Timer; + + if (timer_number == MF_TIMER_STEP || timer_number == MF_TIMER_TEMP) { + timer.setRolloverValue(new_value, TimerFormat::TICK); + if (value < static_cast(timer.getCounter(TimerFormat::TICK))) + timer.refresh(); + } +} + +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/GD32_MFL/u8g/LCD_defines.h b/Marlin/src/HAL/GD32_MFL/u8g/LCD_defines.h new file mode 100644 index 0000000000..720d958779 --- /dev/null +++ b/Marlin/src/HAL/GD32_MFL/u8g/LCD_defines.h @@ -0,0 +1,26 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// MFL LCD-specific defines +uint8_t u8g_com_HAL_MFL_sw_spi_fn(u8g_t* u8g, uint8_t msg, uint8_t arg_val, void* arg_ptr); // u8g_com_mfl_swspi.cpp +#define U8G_COM_HAL_SW_SPI_FN u8g_com_HAL_MFL_sw_spi_fn diff --git a/Marlin/src/HAL/HAL.h b/Marlin/src/HAL/HAL.h index f3e16cfdf1..a211dfe259 100644 --- a/Marlin/src/HAL/HAL.h +++ b/Marlin/src/HAL/HAL.h @@ -27,7 +27,7 @@ #define GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) #endif -#include HAL_PATH(..,HAL.h) +#include HAL_PATH(.., HAL.h) extern MarlinHAL hal; #define HAL_ADC_RANGE _BV(HAL_ADC_RESOLUTION) diff --git a/Marlin/src/HAL/HC32/HAL.h b/Marlin/src/HAL/HC32/HAL.h index dd02183dd0..f751dea091 100644 --- a/Marlin/src/HAL/HC32/HAL.h +++ b/Marlin/src/HAL/HC32/HAL.h @@ -30,69 +30,18 @@ #include "../../inc/MarlinConfig.h" -#include "../../core/macros.h" #include "../shared/Marduino.h" #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" #include "timers.h" -#include "MarlinSerial.h" - -#include // // Serial Ports // -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) -#define NUM_UARTS 4 -#if SERIAL_PORT == -1 - #error "USB Serial is not supported on HC32F460" -#elif WITHIN(SERIAL_PORT, 1, NUM_UARTS) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #define MYSERIAL1 MSERIAL(1) // Dummy port - static_assert(false, "SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ".") -#endif - -#ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #error "USB Serial is not supported on HC32F460" - #elif WITHIN(SERIAL_PORT_2, 1, NUM_UARTS) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #define MYSERIAL2 MSERIAL(1) // Dummy port - static_assert(false, "SERIAL_PORT_2 must be from 1 to " STRINGIFY(NUM_UARTS) ".") - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 - #error "USB Serial is not supported on HC32F460" - #elif WITHIN(SERIAL_PORT_3, 1, NUM_UARTS) - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #else - #define MYSERIAL3 MSERIAL(1) // Dummy port - static_assert(false, "SERIAL_PORT_3 must be from 1 to " STRINGIFY(NUM_UARTS) ".") - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if LCD_SERIAL_PORT == -1 - #error "USB Serial is not supported on HC32F460" - #elif WITHIN(LCD_SERIAL_PORT, 1, NUM_UARTS) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #define LCD_SERIAL MSERIAL(1) // Dummy port - static_assert(false, "LCD_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ".") - #endif - - #if HAS_DGUS_LCD - #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() - #endif -#endif +#include "MarlinSerial.h" // // Emergency Parser @@ -114,22 +63,19 @@ // Misc. Functions // #ifndef analogInputToDigitalPin -#define analogInputToDigitalPin(p) (p) + #define analogInputToDigitalPin(p) pin_t(p) #endif -#define CRITICAL_SECTION_START \ - uint32_t primask = __get_PRIMASK(); \ - (void)__iCliRetVal() +#define CRITICAL_SECTION_START() \ + const bool irqon = !__get_PRIMASK(); \ + __disable_irq(); \ + __DSB(); +#define CRITICAL_SECTION_END() \ + __DSB(); \ + if (irqon) __enable_irq(); -#define CRITICAL_SECTION_END \ - if (!primask) \ - (void)__iSeiRetVal() - -// Disable interrupts -#define cli() noInterrupts() - -// Enable interrupts -#define sei() interrupts() +#define cli() __disable_irq() +#define sei() __enable_irq() // bss_end alias #define __bss_end __bss_end__ @@ -142,12 +88,31 @@ // ADC // #define HAL_ADC_VREF_MV 3300 -#define HAL_ADC_RESOLUTION 10 +#define HAL_ADC_RESOLUTION 12 #define GET_PIN_MAP_PIN(index) index #define GET_PIN_MAP_INDEX(pin) pin #define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) +// +// Debug port disable +// JTMS / SWDIO = PA13 +// JTCK / SWCLK = PA14 +// JTDI = PA15 +// JTDO = PB3 +// NJTRST = PB4 +// +#define DBG_SWCLK _BV(0) +#define DBG_SWDIO _BV(1) +#define DBG_TDO _BV(2) +#define DBG_TDI _BV(3) +#define DBG_TRST _BV(4) +#define DBG_ALL (DBG_SWCLK | DBG_SWDIO | DBG_TDO | DBG_TDI | DBG_TRST) + +#define JTAGSWD_RESET() PORT_DebugPortSetting(DBG_ALL, Enable); +#define JTAG_DISABLE() PORT_DebugPortSetting(DBG_TDO | DBG_TDI | DBG_TRST, Disable); +#define JTAGSWD_DISABLE() PORT_DebugPortSetting(DBG_ALL, Disable); + // // MarlinHAL implementation // diff --git a/Marlin/src/HAL/HC32/MarlinHAL.cpp b/Marlin/src/HAL/HC32/MarlinHAL.cpp index 1ab374fbf1..f1d85f1694 100644 --- a/Marlin/src/HAL/HC32/MarlinHAL.cpp +++ b/Marlin/src/HAL/HC32/MarlinHAL.cpp @@ -123,6 +123,11 @@ void MarlinHAL::init() { // Register min serial TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); + + // warn if low memory after init + if (freeMemory() < 1024) { + SERIAL_WARN_MSG("HAL: low memory after init!\n"); + } } void MarlinHAL::init_board() {} @@ -147,7 +152,31 @@ void MarlinHAL::delay_ms(const int ms) { delay(ms); } -void MarlinHAL::idletask() {} +void MarlinHAL::idletask() { + #if ENABLED(MARLIN_DEV_MODE) + // check & print serial RX errors + MSerialT *serials[] = { &MSerial1, &MSerial2 }; + for (int serial = 0; serial < 2; serial++) { + usart_receive_error_t err = serials[serial]->getReceiveError(); + if (err != usart_receive_error_t::None) { + // "Warning: MSerial[n] RX [Framing|Parity|Overrun] Error" + SERIAL_WARN_START(); + SERIAL_ECHOPGM(" MSerial"); + SERIAL_ECHO(serial + 1); + SERIAL_ECHOPGM(" RX "); + switch(err) { + case usart_receive_error_t::FramingError: SERIAL_ECHOPGM("Framing"); break; + case usart_receive_error_t::ParityError: SERIAL_ECHOPGM("Parity"); break; + case usart_receive_error_t::OverrunError: SERIAL_ECHOPGM("Overrun"); break; + case usart_receive_error_t::RxDataDropped: SERIAL_ECHOPGM("DataDropped"); break; + default: break; + } + SERIAL_ECHOPGM(" Error"); + SERIAL_EOL(); + } + } + #endif +} uint8_t MarlinHAL::get_reset_source() { // Query reset cause from RMU @@ -203,7 +232,9 @@ int MarlinHAL::freeMemory() { return &top - _sbrk(0); } -void MarlinHAL::adc_init() {} +void MarlinHAL::adc_init() { + analogReadResolution(HAL_ADC_RESOLUTION); +} void MarlinHAL::adc_enable(const pin_t pin) { #if TEMP_SENSOR_SOC diff --git a/Marlin/src/HAL/HC32/MarlinSerial.cpp b/Marlin/src/HAL/HC32/MarlinSerial.cpp index eb203f79d3..11d4abfab9 100644 --- a/Marlin/src/HAL/HC32/MarlinSerial.cpp +++ b/Marlin/src/HAL/HC32/MarlinSerial.cpp @@ -46,14 +46,34 @@ constexpr bool serial_handles_emergency(int port) { // // Define serial ports // -#define DEFINE_HWSERIAL_MARLIN(name, n) \ + +// serial port where RX and TX use IRQs +#define DEFINE_IRQ_SERIAL_MARLIN(name, n) \ MSerialT name(serial_handles_emergency(n), \ &USART##n##_config, \ BOARD_USART##n##_TX_PIN, \ BOARD_USART##n##_RX_PIN); -DEFINE_HWSERIAL_MARLIN(MSerial1, 1); -DEFINE_HWSERIAL_MARLIN(MSerial2, 2); +// serial port where RX uses DMA and TX uses IRQs +// all serial ports use DMA1 +// since there are 4 USARTs and 4 DMA channels, we can use the USART number as the DMA channel +#define DEFINE_DMA_SERIAL_MARLIN(name, n) \ + MSerialT name(serial_handles_emergency(n), \ + &USART##n##_config, \ + BOARD_USART##n##_TX_PIN, \ + BOARD_USART##n##_RX_PIN, \ + M4_DMA1, \ + ((en_dma_channel_t)(n - 1))); // map USART1 to DMA channel 0, USART2 to DMA channel 1, etc. + +#define DEFINE_SERIAL_MARLIN(name, n) TERN(SERIAL_DMA, DEFINE_DMA_SERIAL_MARLIN(name, n), DEFINE_IRQ_SERIAL_MARLIN(name, n)) + +DEFINE_SERIAL_MARLIN(MSerial1, 1); +DEFINE_SERIAL_MARLIN(MSerial2, 2); + +// TODO: remove this warning when SERIAL_DMA has been tested some more +#if ENABLED(SERIAL_DMA) + #warning "SERIAL_DMA may be unstable on HC32F460." +#endif // // Serial port assertions diff --git a/Marlin/src/HAL/HC32/MarlinSerial.h b/Marlin/src/HAL/HC32/MarlinSerial.h index 08eeef4395..1a97805a51 100644 --- a/Marlin/src/HAL/HC32/MarlinSerial.h +++ b/Marlin/src/HAL/HC32/MarlinSerial.h @@ -24,18 +24,51 @@ #include "../../core/serial_hook.h" #include +#define SERIAL_INDEX_MIN 1 +#define SERIAL_INDEX_MAX 4 +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() +#endif + // Optionally set uart IRQ priority to reduce overflow errors -// #define UART_IRQ_PRIO 1 +//#define UART_RX_IRQ_PRIO 1 +//#define UART_TX_IRQ_PRIO 1 +//#define UART_RX_DMA_IRQ_PRIO 1 struct MarlinSerial : public Usart { - MarlinSerial(struct usart_config_t *usart_device, gpio_pin_t tx_pin, gpio_pin_t rx_pin) : Usart(usart_device, tx_pin, rx_pin) {} + MarlinSerial( + struct usart_config_t *usart_device, + gpio_pin_t tx_pin, + gpio_pin_t rx_pin + #if ENABLED(SERIAL_DMA) + , M4_DMA_TypeDef *dma_unit = nullptr, + en_dma_channel_t rx_dma_channel = DmaCh0 + #endif + ) : Usart(usart_device, tx_pin, rx_pin) { + #if ENABLED(SERIAL_DMA) + if (dma_unit != nullptr) { + enableRxDma(dma_unit, rx_dma_channel); + } + #endif + } - #ifdef UART_IRQ_PRIO + #if defined(UART_RX_IRQ_PRIO) || defined(UART_TX_IRQ_PRIO) || defined(UART_RX_DMA_IRQ_PRIO) void setPriority() { - NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_IRQ_PRIO); - NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_IRQ_PRIO); - NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_IRQ_PRIO); - NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_IRQ_PRIO); + #if defined(UART_RX_IRQ_PRIO) + NVIC_SetPriority(c_dev()->interrupts.rx_data_available.interrupt_number, UART_RX_IRQ_PRIO); + NVIC_SetPriority(c_dev()->interrupts.rx_error.interrupt_number, UART_RX_IRQ_PRIO); + #endif + + #if defined(UART_TX_IRQ_PRIO) + NVIC_SetPriority(c_dev()->interrupts.tx_buffer_empty.interrupt_number, UART_TX_IRQ_PRIO); + NVIC_SetPriority(c_dev()->interrupts.tx_complete.interrupt_number, UART_TX_IRQ_PRIO); + #endif + + #if defined(UART_RX_DMA_IRQ_PRIO) && ENABLED(SERIAL_DMA) + NVIC_SetPriority(c_dev()->dma.rx.rx_data_available_dma_btc.interrupt_number, UART_RX_DMA_IRQ_PRIO); + #endif } void begin(uint32_t baud) { @@ -47,7 +80,12 @@ struct MarlinSerial : public Usart { Usart::begin(baud, config); setPriority(); } - #endif + + void begin(uint32_t baud, const stc_usart_uart_init_t *config, const bool rxNoiseFilter = true) { + Usart::begin(baud, config, rxNoiseFilter); + setPriority(); + } + #endif // UART_RX_IRQ_PRIO || UART_TX_IRQ_PRIO || UART_RX_DMA_IRQ_PRIO }; typedef Serial1Class MSerialT; diff --git a/Marlin/src/HAL/HC32/README.md b/Marlin/src/HAL/HC32/README.md index c9ae8a9a20..ed11c40035 100644 --- a/Marlin/src/HAL/HC32/README.md +++ b/Marlin/src/HAL/HC32/README.md @@ -11,7 +11,7 @@ The HC32F460 HAL is designed to be generic enough for any HC32F460-based board. - Examine the board's main processor. (Refer the naming key in `hc32.ini`.) - Extend the `HC32F460C_common` base env for 256K, or `HC32F460E_common` for 512K. 3. Determine your board's application start address (see [below](#finding-the-application-start-address)) -4. Set `board_build.ld_args.flash_start` to the app start address once you've found it. If your board doesn't use a bootloader, you may be able to use the "ICSP" header or DFU. This document will be updated once we have more information about flashing without a bootloader. +4. Set `board_upload.offset_address` to the app start address once you've found it. If your board doesn't use a bootloader, you may be able to use the "ICSP" header or DFU. This document will be updated once we have more information about flashing without a bootloader. ### Finding the application start address diff --git a/Marlin/src/HAL/HC32/Servo.h b/Marlin/src/HAL/HC32/Servo.h index db2f60d190..0a9715494c 100644 --- a/Marlin/src/HAL/HC32/Servo.h +++ b/Marlin/src/HAL/HC32/Servo.h @@ -37,9 +37,9 @@ public: MarlinServo(); /** - * @brief attach the pin to the servo, set pin mode, return channel number - * @param pin pin to attach to - * @return channel number, -1 if failed + * @brief attach the pin to the servo, set pin mode, return channel number + * @param pin pin to attach to + * @return channel number, -1 if failed */ int8_t attach(const pin_t apin); diff --git a/Marlin/src/HAL/HC32/app_config.h b/Marlin/src/HAL/HC32/app_config.h new file mode 100644 index 0000000000..b971903bba --- /dev/null +++ b/Marlin/src/HAL/HC32/app_config.h @@ -0,0 +1,72 @@ +/** + * app_config.h is included by the hc32f460 arduino build script for every source file. + * it is used to configure the arduino core (and ddl) automatically according + * to the settings in Configuration.h and Configuration_adv.h. + */ +#pragma once +#ifndef _HC32_APP_CONFIG_H_ +#define _HC32_APP_CONFIG_H_ + +#include "../../inc/MarlinConfigPre.h" +#include "sysclock.h" + +// +// dev mode +// +#if ENABLED(MARLIN_DEV_MODE) + #define __DEBUG 1 + #define __CORE_DEBUG 1 +#endif + +// +// Fault Handlers and Panic +// + +#if ENABLED(POSTMORTEM_DEBUGGING) + // disable arduino core fault handler, as we define our own + #define CORE_DISABLE_FAULT_HANDLER 1 +#endif + +// force-enable panic handler so that we can use our custom one (in MinSerial) +#define PANIC_ENABLE 1 + +// use short filenames in ddl debug and core panic output +#define __DEBUG_SHORT_FILENAMES 1 +#define __PANIC_SHORT_FILENAMES 1 + +// omit panic messages in core panic output +#define __OMIT_PANIC_MESSAGE 1 + +// +// Usart +// + +// disable serial globals (Serial1, Serial2, ...), as we define our own +#define DISABLE_SERIAL_GLOBALS 1 + +// increase the size of the Usart buffers (both RX and TX) +// NOTE: +// the heap usage will increase by (SERIAL_BUFFER_SIZE - 64) * "number of serial ports used" +// if running out of heap, the system may become unstable +//#define SERIAL_BUFFER_SIZE 256 + +// enable support for Usart Clock Divider / Oversampling auto config +#define USART_AUTO_CLKDIV_OS_CONFIG 1 + +// enable USART_RX_DMA_SUPPORT core option when SERIAL_DMA is enabled +#if ENABLED(SERIAL_DMA) + #define USART_RX_DMA_SUPPORT 1 +#endif + +// +// Misc. +// + +// redirect printf to host serial +#define REDIRECT_PRINTF_TO_SERIAL 1 + +// F_CPU is F_HCLK, as that's the main CPU core's clock. +// see 'sysclock.h' for more information. +#define F_CPU F_HCLK + +#endif // _HC32_APP_CONFIG_H_ diff --git a/Marlin/src/HAL/HC32/docs/HC32F460 series Datasheet Rev1.3.pdf b/Marlin/src/HAL/HC32/docs/HC32F460 series Datasheet Rev1.3.pdf new file mode 100644 index 0000000000..a8943c819e Binary files /dev/null and b/Marlin/src/HAL/HC32/docs/HC32F460 series Datasheet Rev1.3.pdf differ diff --git a/Marlin/src/HAL/HC32/eeprom_bl24cxx.cpp b/Marlin/src/HAL/HC32/eeprom/eeprom_bl24cxx.cpp similarity index 95% rename from Marlin/src/HAL/HC32/eeprom_bl24cxx.cpp rename to Marlin/src/HAL/HC32/eeprom/eeprom_bl24cxx.cpp index 59da99b3f5..a53a7c4062 100644 --- a/Marlin/src/HAL/HC32/eeprom_bl24cxx.cpp +++ b/Marlin/src/HAL/HC32/eeprom/eeprom_bl24cxx.cpp @@ -26,12 +26,12 @@ */ #ifdef ARDUINO_ARCH_HC32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../shared/eeprom_api.h" -#include "../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for IIC_BL24CXX_EEPROM." diff --git a/Marlin/src/HAL/HC32/eeprom_if_iic.cpp b/Marlin/src/HAL/HC32/eeprom/eeprom_if_iic.cpp similarity index 89% rename from Marlin/src/HAL/HC32/eeprom_if_iic.cpp rename to Marlin/src/HAL/HC32/eeprom/eeprom_if_iic.cpp index 02b1d3fd54..0a161f23f2 100644 --- a/Marlin/src/HAL/HC32/eeprom_if_iic.cpp +++ b/Marlin/src/HAL/HC32/eeprom/eeprom_if_iic.cpp @@ -26,12 +26,12 @@ */ #ifdef ARDUINO_ARCH_HC32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../../libs/BL24CXX.h" -#include "../shared/eeprom_if.h" +#include "../../../libs/BL24CXX.h" +#include "../../shared/eeprom_if.h" void eeprom_init() { BL24CXX::init(); @@ -39,7 +39,7 @@ void eeprom_init() { void eeprom_write_byte(uint8_t *pos, unsigned char value) { const unsigned eeprom_address = (unsigned)pos; - return BL24CXX::writeOneByte(eeprom_address, value); + BL24CXX::writeOneByte(eeprom_address, value); } uint8_t eeprom_read_byte(uint8_t *pos) { diff --git a/Marlin/src/HAL/HC32/eeprom_sdcard.cpp b/Marlin/src/HAL/HC32/eeprom/eeprom_sdcard.cpp similarity index 93% rename from Marlin/src/HAL/HC32/eeprom_sdcard.cpp rename to Marlin/src/HAL/HC32/eeprom/eeprom_sdcard.cpp index 601e86dd30..759ea5722d 100644 --- a/Marlin/src/HAL/HC32/eeprom_sdcard.cpp +++ b/Marlin/src/HAL/HC32/eeprom/eeprom_sdcard.cpp @@ -25,12 +25,12 @@ */ #ifdef ARDUINO_ARCH_HC32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(SDCARD_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" -#include "../../sd/cardreader.h" +#include "../../shared/eeprom_api.h" +#include "../../../sd/cardreader.h" #define EEPROM_FILENAME "eeprom.dat" @@ -53,7 +53,7 @@ bool PersistentStore::access_start() { int bytes_read = file.read(HAL_eeprom_data, MARLIN_EEPROM_SIZE); if (bytes_read < 0) return false; - for (; bytes_read < MARLIN_EEPROM_SIZE; bytes_read++) + for (; bytes_read < long(MARLIN_EEPROM_SIZE); bytes_read++) HAL_eeprom_data[bytes_read] = 0xFF; file.close(); diff --git a/Marlin/src/HAL/HC32/eeprom_wired.cpp b/Marlin/src/HAL/HC32/eeprom/eeprom_wired.cpp similarity index 92% rename from Marlin/src/HAL/HC32/eeprom_wired.cpp rename to Marlin/src/HAL/HC32/eeprom/eeprom_wired.cpp index d9be65b4c0..997180bd98 100644 --- a/Marlin/src/HAL/HC32/eeprom_wired.cpp +++ b/Marlin/src/HAL/HC32/eeprom/eeprom_wired.cpp @@ -2,6 +2,9 @@ * Marlin 3D Printer Firmware * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * * 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 3 of the License, or @@ -18,7 +21,7 @@ */ #ifdef ARDUINO_ARCH_HC32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -29,8 +32,8 @@ * with simple implementations supplied by Marlin. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM." diff --git a/Marlin/src/HAL/HC32/endstop_interrupts.cpp b/Marlin/src/HAL/HC32/endstop_interrupts.cpp index 99987ec76f..06c4d0e186 100644 --- a/Marlin/src/HAL/HC32/endstop_interrupts.cpp +++ b/Marlin/src/HAL/HC32/endstop_interrupts.cpp @@ -101,12 +101,14 @@ void setup_endstop_interrupts() { SETUP(Z_MIN_PROBE); + SETUP(CALIBRATION); + #undef SETUP } // Ensure 1 - 10 IRQs are registered // Disable some endstops if you encounter this error -#define ENDSTOPS_INTERRUPTS_COUNT COUNT_ENABLED(USE_X_MAX, USE_X_MIN, USE_X2_MAX, USE_X2_MIN, USE_Y_MAX, USE_Y_MIN, USE_Y2_MAX, USE_Y2_MIN, USE_Z_MAX, USE_Z_MIN, USE_Z2_MAX, USE_Z2_MIN, USE_Z3_MAX, USE_Z3_MIN, USE_Z4_MAX, USE_Z4_MIN, USE_Z_MIN_PROBE) +#define ENDSTOPS_INTERRUPTS_COUNT COUNT_ENABLED(USE_X_MAX, USE_X_MIN, USE_X2_MAX, USE_X2_MIN, USE_Y_MAX, USE_Y_MIN, USE_Y2_MAX, USE_Y2_MIN, USE_Z_MAX, USE_Z_MIN, USE_Z2_MAX, USE_Z2_MIN, USE_Z3_MAX, USE_Z3_MIN, USE_Z4_MAX, USE_Z4_MIN, USE_Z_MIN_PROBE, USE_CALIBRATION) #if ENDSTOPS_INTERRUPTS_COUNT > 10 #error "Too many endstop interrupts! HC32F460 only supports 10 endstop interrupts." #elif ENDSTOPS_INTERRUPTS_COUNT == 0 diff --git a/Marlin/src/HAL/HC32/inc/SanityCheck.h b/Marlin/src/HAL/HC32/inc/SanityCheck.h index ef8d9a9975..e38bce91e5 100644 --- a/Marlin/src/HAL/HC32/inc/SanityCheck.h +++ b/Marlin/src/HAL/HC32/inc/SanityCheck.h @@ -20,6 +20,20 @@ * */ #pragma once +#include + +#if !defined(ARDUINO_CORE_VERSION_INT) || !defined(GET_VERSION_INT) + // version macros were introduced in arduino core version 1.1.0 + // below that version, we polyfill them + #define GET_VERSION_INT(major, minor, patch) ((major * 100000) + (minor * 1000) + patch) + #define ARDUINO_CORE_VERSION_INT GET_VERSION_INT(1, 0, 0) +#endif + +#if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0) + // because we use app_config.h introduced in arduino core version 1.1.0, the + // HAL is not compatible with older versions + #error "The HC32 HAL is not compatible with Arduino Core versions < 1.1.0. Consider updating the Arduino Core." +#endif #ifndef BOARD_XTAL_FREQUENCY #error "BOARD_XTAL_FREQUENCY is required for HC32F460." @@ -74,3 +88,20 @@ #error "HC32 HAL uses a custom panic handler. Do not define PANIC_USARTx_TX_PIN." #endif #endif + +#if ENABLED(SERIAL_DMA) + #if !defined(USART_RX_DMA_SUPPORT) + #error "SERIAL_DMA requires USART_RX_DMA_SUPPORT to be enabled in the arduino core." + #endif + + // Before arduino core version 1.2.0, USART_RX_DMA_SUPPORT did not implement + // core_hook_usart_rx_irq, which is required for the emergency parser. + // With 1.2.0, this was fixed (see https://github.com/shadow578/framework-arduino-hc32f46x/pull/25). + #if ENABLED(EMERGENCY_PARSER) && ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 2, 0) + #error "EMERGENCY_PARSER is not supported with SERIAL_DMA. Please disable either SERIAL_DMA or EMERGENCY_PARSER." + #endif + + #if ARDUINO_CORE_VERSION_INT < GET_VERSION_INT(1, 1, 0) + #error "SERIAL_DMA is not supported with arduino core version < 1.1.0." + #endif +#endif diff --git a/Marlin/src/HAL/HC32/pinsDebug.h b/Marlin/src/HAL/HC32/pinsDebug.h index 1bee6ceb3e..9f8e23ce44 100644 --- a/Marlin/src/HAL/HC32/pinsDebug.h +++ b/Marlin/src/HAL/HC32/pinsDebug.h @@ -2,6 +2,9 @@ * Marlin 3D Printer Firmware * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * * 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 3 of the License, or @@ -18,41 +21,47 @@ */ #pragma once +/** + * Pins Debugging for HC32 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) + */ + #include "../../inc/MarlinConfig.h" #include "fastio.h" #include -// -// Translation of routines & variables used by pinsDebug.h -// #ifndef BOARD_NR_GPIO_PINS #error "Expected BOARD_NR_GPIO_PINS not found." #endif #define NUM_DIGITAL_PINS BOARD_NR_GPIO_PINS #define NUMBER_PINS_TOTAL BOARD_NR_GPIO_PINS -#define VALID_PIN(pin) IS_GPIO_PIN(pin) +#define isValidPin(P) IS_GPIO_PIN(P) // Note: pin_array is defined in `Marlin/src/pins/pinsDebug.h`, and since this file is included // after it, it is available in this file as well. -#define GET_ARRAY_PIN(p) pin_t(pin_array[p].pin) -#define digitalRead_mod(p) extDigitalRead(p) -#define PRINT_PIN(p) \ - do { \ - sprintf_P(buffer, PSTR("%3hd "), int16_t(p)); \ - SERIAL_ECHO(buffer); \ - } while (0) -#define PRINT_PIN_ANALOG(p) \ - do { \ - sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); \ - SERIAL_ECHO(buffer); \ - } while (0) -#define PRINT_PORT(p) print_port(p) -#define PRINT_ARRAY_NAME(x) \ - do { \ - sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); \ - SERIAL_ECHO(buffer); \ - } while (0) +#define getPinByIndex(x) pin_t(pin_array[x].pin) +#define digitalRead_mod(P) extDigitalRead(P) + +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3hd "), int16_t(P)); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) + +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 21 // Space needed to be pretty if not first name assigned to a pin @@ -71,14 +80,14 @@ #define M43_NEVER_TOUCH(Q) (IS_HOST_USART_PIN(Q) || IS_OSC_PIN(Q)) #endif -static pin_t DIGITAL_PIN_TO_ANALOG_PIN(pin_t pin) { - if (!VALID_PIN(pin)) return -1; +int8_t digitalPinToAnalogIndex(const pin_t pin) { + if (!isValidPin(pin)) return -1; const int8_t adc_channel = int8_t(PIN_MAP[pin].adc_info.channel); return pin_t(adc_channel); } -static bool IS_ANALOG(pin_t pin) { - if (!VALID_PIN(pin)) return false; +bool isAnalogPin(pin_t pin) { + if (!isValidPin(pin)) return false; if (PIN_MAP[pin].adc_info.channel != ADC_PIN_INVALID) return _GET_MODE(pin) == INPUT_ANALOG && !M43_NEVER_TOUCH(pin); @@ -86,13 +95,13 @@ static bool IS_ANALOG(pin_t pin) { return false; } -static bool GET_PINMODE(const pin_t pin) { - return VALID_PIN(pin) && !IS_INPUT(pin); +bool getValidPinMode(const pin_t pin) { + return isValidPin(pin) && !IS_INPUT(pin); } -static bool GET_ARRAY_IS_DIGITAL(const int16_t array_pin) { - const pin_t pin = GET_ARRAY_PIN(array_pin); - return (!IS_ANALOG(pin)); +bool getPinIsDigitalByIndex(const int16_t index) { + const pin_t pin = getPinByIndex(index); + return (!isAnalogPin(pin)); } /** @@ -117,7 +126,7 @@ bool pwm_status(const pin_t pin) { return timera_is_unit_initialized(unit) && timera_is_channel_active(unit, channel) && getPinMode(pin) == OUTPUT_PWM; } -void pwm_details(const pin_t pin) { +void printPinPWM(const pin_t pin) { // Get timer assignment for pin timera_config_t *unit; en_timera_channel_t channel; @@ -161,7 +170,7 @@ void pwm_details(const pin_t pin) { } } -void print_port(pin_t pin) { +void printPinPort(pin_t pin) { const char port = 'A' + char(pin >> 4); // Pin div 16 const int16_t gbit = PIN_MAP[pin].bit_pos; char buffer[8]; diff --git a/Marlin/src/HAL/HC32/sdio.cpp b/Marlin/src/HAL/HC32/sdio.cpp index 4360d715ff..3c4038f92d 100644 --- a/Marlin/src/HAL/HC32/sdio.cpp +++ b/Marlin/src/HAL/HC32/sdio.cpp @@ -54,7 +54,7 @@ fn \ } -stc_sd_handle_t *handle; +stc_sd_handle_t *handle = nullptr; bool SDIO_Init() { // Configure SDIO pins @@ -66,36 +66,45 @@ bool SDIO_Init() { GPIO_SetFunc(BOARD_SDIO_CMD, Func_Sdio); GPIO_SetFunc(BOARD_SDIO_DET, Func_Sdio); + // If a handle is already initialized, free it before creating a new one + // otherwise, we will leak memory, which will eventually crash the system + if (handle != nullptr) { + delete handle->pstcDmaInitCfg; + delete handle->pstcCardInitCfg; + delete handle; + handle = nullptr; + } + // Create DMA configuration stc_sdcard_dma_init_t *dmaConf = new stc_sdcard_dma_init_t; dmaConf->DMAx = SDIO_DMA_PERIPHERAL; dmaConf->enDmaCh = SDIO_DMA_CHANNEL; + // Create card configuration + // This should be a fairly safe configuration for most cards + stc_sdcard_init_t *cardConf = new stc_sdcard_init_t; + cardConf->enBusWidth = SdiocBusWidth4Bit; + cardConf->enClkFreq = SdiocClk400K; + cardConf->enSpeedMode = SdiocNormalSpeedMode; + cardConf->pstcInitCfg = nullptr; + // Create handle in DMA mode handle = new stc_sd_handle_t; handle->SDIOCx = SDIO_PERIPHERAL; handle->enDevMode = SdCardDmaMode; handle->pstcDmaInitCfg = dmaConf; - - // Create card configuration - // This should be a fairly safe configuration for most cards - stc_sdcard_init_t cardConf = { - .enBusWidth = SdiocBusWidth4Bit, - .enClkFreq = SdiocClk400K, - .enSpeedMode = SdiocNormalSpeedMode, - //.pstcInitCfg = NULL, - }; + //handle->pstcCardInitCfg = cardConf; // assigned in SDCARD_Init // Initialize sd card - en_result_t rc = SDCARD_Init(handle, &cardConf); + en_result_t rc = SDCARD_Init(handle, cardConf); if (rc != Ok) printf("SDIO_Init() error (rc=%u)\n", rc); return rc == Ok; } bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) { - CORE_ASSERT(handle != NULL, "SDIO not initialized"); - CORE_ASSERT(dst != NULL, "SDIO_ReadBlock dst is NULL"); + CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false); + CORE_ASSERT(dst != nullptr, "SDIO_ReadBlock dst is NULL", return false); WITH_RETRY(SDIO_READ_RETRIES, { en_result_t rc = SDCARD_ReadBlocks(handle, block, 1, dst, SDIO_READ_TIMEOUT); @@ -107,8 +116,8 @@ bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) { } bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) { - CORE_ASSERT(handle != NULL, "SDIO not initialized"); - CORE_ASSERT(src != NULL, "SDIO_WriteBlock src is NULL"); + CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false); + CORE_ASSERT(src != nullptr, "SDIO_WriteBlock src is NULL", return false); WITH_RETRY(SDIO_WRITE_RETRIES, { en_result_t rc = SDCARD_WriteBlocks(handle, block, 1, (uint8_t *)src, SDIO_WRITE_TIMEOUT); @@ -120,12 +129,12 @@ bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) { } bool SDIO_IsReady() { - CORE_ASSERT(handle != NULL, "SDIO not initialized"); + CORE_ASSERT(handle != nullptr, "SDIO not initialized", return false); return bool(handle->stcCardStatus.READY_FOR_DATA); } uint32_t SDIO_GetCardSize() { - CORE_ASSERT(handle != NULL, "SDIO not initialized"); + CORE_ASSERT(handle != nullptr, "SDIO not initialized", return 0); // Multiply number of blocks with block size to get size in bytes const uint64_t cardSizeBytes = uint64_t(handle->stcSdCardInfo.u32LogBlockNbr) * uint64_t(handle->stcSdCardInfo.u32LogBlockSize); diff --git a/Marlin/src/HAL/HC32/spi_pins.h b/Marlin/src/HAL/HC32/spi_pins.h index 8a8e054b9b..5f1a94e920 100644 --- a/Marlin/src/HAL/HC32/spi_pins.h +++ b/Marlin/src/HAL/HC32/spi_pins.h @@ -2,6 +2,9 @@ * Marlin 3D Printer Firmware * Copyright (c) 2023 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * * 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 3 of the License, or diff --git a/Marlin/src/HAL/HC32/sysclock.cpp b/Marlin/src/HAL/HC32/sysclock.cpp index d205d725cc..493515b0e8 100644 --- a/Marlin/src/HAL/HC32/sysclock.cpp +++ b/Marlin/src/HAL/HC32/sysclock.cpp @@ -26,64 +26,144 @@ #ifdef ARDUINO_ARCH_HC32 -// Get BOARD_XTAL_FREQUENCY from configuration / pins #include "../../inc/MarlinConfig.h" +#include "sysclock.h" #include #include +/*** + * @brief Automatically calculate M, N, P values for the MPLL to reach a target frequency. + * @param input_frequency The input frequency. + * @param target_frequency The target frequency. + * @return The MPLL configuration structure. Q and R are not set. + * + * @note + * Simplified MPLL block diagram, with intermediary clocks (1) = VCO_in, (2) = VCO_out: + * + * INPUT -> [/ M] -(1)-> [* N] -(2)-|-> [/ P] -> MPLL-P + */ +constexpr stc_clk_mpll_cfg_t get_mpll_config(double input_frequency, double target_frequency) { + // PLL input clock divider: M in [1, 24] + for (uint32_t M = 1; M <= 24; M++) { + double f_vco_in = input_frequency / M; + + // 1 <= VCO_in <= 25 MHz + if (f_vco_in < 1e6 || f_vco_in > 25e6) continue; + + // VCO multiplier: N in [20, 480] + for (uint32_t N = 20; N <= 480; N++) { + double f_vco_out = f_vco_in * N; + + // 240 <= VCO_out <= 480 MHz + if (f_vco_out < 240e6 || f_vco_out > 480e6) continue; + + // Output "P" divider: P in [2, 16] + for (uint32_t P = 2; P <= 16; P++) { + double f_calculated_out = f_vco_out / P; + if (f_calculated_out == target_frequency) { + // Found a match, return it + return { + .PllpDiv = P, + .PllqDiv = P, // Don't care for Q and R + .PllrDiv = P, // " + .plln = N, + .pllmDiv = M + }; + } + } + } + } + + // If no valid M, N, P found, return invalid config + return { 0, 0, 0, 0, 0 }; +} + +/** + * @brief Get the division factor required to get the target frequency from the input frequency. + * @tparam input_freq The input frequency. + * @tparam target_freq The target frequency. + * @return The division factor. + */ +template +constexpr en_clk_sysclk_div_factor_t get_division_factor() { + // Calculate the divider to get the target frequency + constexpr float fdivider = static_cast(input_freq) / static_cast(target_freq); + constexpr int divider = static_cast(fdivider); + + // divider must be an integer + static_assert(fdivider == divider, "Target frequency not achievable, divider must be an integer"); + + // divider must be between 1 and 64 (enum range), and must be a power of 2 + static_assert(divider >= 1 && divider <= 64, "Invalid divider, out of range"); + static_assert((divider & (divider - 1)) == 0, "Invalid divider, not a power of 2"); + + // return the divider + switch (divider) { + case 1: return ClkSysclkDiv1; + case 2: return ClkSysclkDiv2; + case 4: return ClkSysclkDiv4; + case 8: return ClkSysclkDiv8; + case 16: return ClkSysclkDiv16; + case 32: return ClkSysclkDiv32; + case 64: return ClkSysclkDiv64; + } +} + +/** + * @brief Validate the runtime clocks match the expected values. + */ +void validate_system_clocks() { + #define CLOCK_ASSERT(expected, actual) \ + if (expected != actual) { \ + SERIAL_ECHOPGM( \ + "Clock Mismatch for " #expected ": " \ + "expected ", expected, \ + ", got ", actual \ + ); \ + CORE_ASSERT_FAIL("Clock Mismatch: " #expected); \ + } + + update_system_clock_frequencies(); + + CLOCK_ASSERT(F_SYSTEM_CLOCK, SYSTEM_CLOCK_FREQUENCIES.system); + CLOCK_ASSERT(F_HCLK, SYSTEM_CLOCK_FREQUENCIES.hclk); + CLOCK_ASSERT(F_EXCLK, SYSTEM_CLOCK_FREQUENCIES.exclk); + CLOCK_ASSERT(F_PCLK0, SYSTEM_CLOCK_FREQUENCIES.pclk0); + CLOCK_ASSERT(F_PCLK1, SYSTEM_CLOCK_FREQUENCIES.pclk1); + CLOCK_ASSERT(F_PCLK2, SYSTEM_CLOCK_FREQUENCIES.pclk2); + CLOCK_ASSERT(F_PCLK3, SYSTEM_CLOCK_FREQUENCIES.pclk3); + CLOCK_ASSERT(F_PCLK4, SYSTEM_CLOCK_FREQUENCIES.pclk4); +} + +/** + * @brief Configure HC32 system clocks. + * + * This function is called by the Arduino core early in the startup process, before setup() is called. + * It is used to configure the system clocks to the desired state. + * + * See https://github.com/MarlinFirmware/Marlin/pull/27099 for more information. + */ void core_hook_sysclock_init() { // Set wait cycles, as we are about to switch to 200 MHz HCLK sysclock_configure_flash_wait_cycles(); sysclock_configure_sram_wait_cycles(); - // Configure MPLLp to 200 MHz output, with different settings depending on XTAL availability - #if BOARD_XTAL_FREQUENCY == 8000000 // 8 MHz XTAL - // - M = 1 => 8 MHz / 1 = 8 MHz - // - N = 50 => 8 MHz * 50 = 400 MHz - // - P = 2 => 400 MHz / 2 = 200 MHz (sysclk) - // - Q,R = 4 => 400 MHz / 4 = 100 MHz (dont care) - stc_clk_mpll_cfg_t pllConf = { - .PllpDiv = 2u, // P - .PllqDiv = 4u, // Q - .PllrDiv = 4u, // R - .plln = 50u, // N - .pllmDiv = 1u, // M - }; - sysclock_configure_xtal(); - sysclock_configure_mpll(ClkPllSrcXTAL, &pllConf); + // Select MPLL input frequency based on clock availability + #if BOARD_XTAL_FREQUENCY == 8000000 || BOARD_XTAL_FREQUENCY == 16000000 // 8 MHz or 16 MHz XTAL + constexpr uint32_t mpll_input_clock = BOARD_XTAL_FREQUENCY; - #elif BOARD_XTAL_FREQUENCY == 16000000 // 16 MHz XTAL - // - M = 1 => 16 MHz / 1 = 16 MHz - // - N = 50 => 16 MHz * 25 = 400 MHz - // - P = 2 => 400 MHz / 2 = 200 MHz (sysclk) - // - Q,R = 4 => 400 MHz / 4 = 100 MHz (dont care) - stc_clk_mpll_cfg_t pllConf = { - .PllpDiv = 2u, // P - .PllqDiv = 4u, // Q - .PllrDiv = 4u, // R - .plln = 50u, // N - .pllmDiv = 1u, // M - }; sysclock_configure_xtal(); - sysclock_configure_mpll(ClkPllSrcXTAL, &pllConf); - #warning "HC32F460 with 16 MHz XTAL has not been tested." + #if BOARD_XTAL_FREQUENCY == 16000000 + #warning "HC32F460 with 16 MHz XTAL has not been tested." + #endif #else // HRC (16 MHz) - // - M = 1 => 16 MHz / 1 = 16 MHz - // - N = 25 => 16 MHz * 25 = 400 MHz - // - P = 2 => 400 MHz / 2 = 200 MHz (sysclk) - // - Q,R = 4 => 400 MHz / 4 = 100 MHz (dont care) - stc_clk_mpll_cfg_t pllConf = { - .PllpDiv = 2u, // P - .PllqDiv = 4u, // Q - .PllrDiv = 4u, // R - .plln = 25u, // N - .pllmDiv = 1u, // M - }; + + constexpr uint32_t mpll_input_clock = 16000000; + sysclock_configure_hrc(); - sysclock_configure_mpll(ClkPllSrcHRC, &pllConf); // HRC could have been configured by ICG to 20 MHz // TODO: handle gracefully if HRC is not 16 MHz @@ -91,34 +171,72 @@ void core_hook_sysclock_init() { panic("HRC is not 16 MHz"); } - #ifdef BOARD_XTAL_FREQUENCY + #if defined(BOARD_XTAL_FREQUENCY) #warning "No valid XTAL frequency defined, falling back to HRC." #endif + #endif - // Setup clock divisors for sysclk = 200 MHz: - // Note: PCLK1 is used for step+temp timers, and need to be kept at 50 MHz (until there is a better solution) - stc_clk_sysclk_cfg_t sysClkConf = { - .enHclkDiv = ClkSysclkDiv1, // HCLK = 200 MHz (CPU) - .enExclkDiv = ClkSysclkDiv2, // EXCLK = 100 MHz (SDIO) - .enPclk0Div = ClkSysclkDiv1, // PCLK0 = 200 MHz (Timer6 (not used)) - .enPclk1Div = ClkSysclkDiv4, // PCLK1 = 50 MHz (USART, SPI, I2S, Timer0 (step+temp), TimerA (Servo)) - .enPclk2Div = ClkSysclkDiv4, // PCLK2 = 50 MHz (ADC) - .enPclk3Div = ClkSysclkDiv4, // PCLK3 = 50 MHz (I2C, WDT) - .enPclk4Div = ClkSysclkDiv2, // PCLK4 = 100 MHz (ADC ctl) + // Automagically calculate MPLL configuration + constexpr stc_clk_mpll_cfg_t pllConf = get_mpll_config(mpll_input_clock, F_SYSTEM_CLOCK); + static_assert(pllConf.pllmDiv != 0 && pllConf.plln != 0 && pllConf.PllpDiv != 0, "MPLL auto-configuration failed"); + sysclock_configure_mpll(ClkPllSrcXTAL, &pllConf); + + // Setup clock divisors + constexpr stc_clk_sysclk_cfg_t sysClkConf = { + .enHclkDiv = get_division_factor(), + .enExclkDiv = get_division_factor(), + .enPclk0Div = get_division_factor(), + .enPclk1Div = get_division_factor(), + .enPclk2Div = get_division_factor(), + .enPclk3Div = get_division_factor(), + .enPclk4Div = get_division_factor(), }; sysclock_set_clock_dividers(&sysClkConf); - // Set power mode - #define POWER_MODE_SYSTEM_CLOCK 200000000 // 200 MHz - power_mode_update_pre(POWER_MODE_SYSTEM_CLOCK); + // Set power mode, before switch + power_mode_update_pre(F_SYSTEM_CLOCK); - // Switch to MPLL as sysclk source + // Switch to MPLL-P as system clock source CLK_SetSysClkSource(CLKSysSrcMPLL); - // Set power mode - power_mode_update_post(POWER_MODE_SYSTEM_CLOCK); - #undef POWER_MODE_SYSTEM_CLOCK + // Set power mode, after switch + power_mode_update_post(F_SYSTEM_CLOCK); + + // Verify clocks match expected values (at runtime) + #if ENABLED(MARLIN_DEV_MODE) || ENABLED(ALWAYS_VALIDATE_CLOCKS) + validate_system_clocks(); + #endif + + // Verify clock configuration (at compile time) + #if ARDUINO_CORE_VERSION_INT >= GET_VERSION_INT(1, 2, 0) + assert_mpll_config_valid< + mpll_input_clock, + pllConf.pllmDiv, + pllConf.plln, + pllConf.PllpDiv, + pllConf.PllqDiv, + pllConf.PllrDiv + >(); + + assert_system_clocks_valid< + F_SYSTEM_CLOCK, + sysClkConf.enHclkDiv, + sysClkConf.enPclk0Div, + sysClkConf.enPclk1Div, + sysClkConf.enPclk2Div, + sysClkConf.enPclk3Div, + sysClkConf.enPclk4Div, + sysClkConf.enExclkDiv + >(); + + static_assert(get_mpll_output_clock( + mpll_input_clock, + pllConf.pllmDiv, + pllConf.plln, + pllConf.PllpDiv + ) == F_SYSTEM_CLOCK, "actual MPLL output clock does not match F_SYSTEM_CLOCK"); + #endif } #endif // ARDUINO_ARCH_HC32 diff --git a/Marlin/src/HAL/HC32/sysclock.h b/Marlin/src/HAL/HC32/sysclock.h new file mode 100644 index 0000000000..783d5677e9 --- /dev/null +++ b/Marlin/src/HAL/HC32/sysclock.h @@ -0,0 +1,65 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * HC32F460 system clock configuration. + * + * With the HC32 HAL, the various peripheral clocks (including the CPU clock) are derived + * from the main PLL (MPLL-P) output (referred to at F_SYSTEM_CLOCK). + * + * F_SYSTEM_CLOCK is the target frequency of the main PLL, and the PLL is automatically configured + * to achieve this frequency. + * + * The peripheral clocks are the result of integer division of F_SYSTEM_CLOCK. + * Their target frequencies are defined here, and the required division factors are calculated automatically. + * Note that the division factor must be a power of 2 between 1 and 64. + * If the target frequency is not achievable, a compile-time error will be generated. + * + * Additionally, there are interdependencies between the peripheral clocks, which are described in + * Section 4.4 "Working Clock Specifications" of the HC32F460 Reference Manual. + * With Arduino core >= 1.2.0, these interdependencies are checked at compile time. + * On earlier versions, you are on your own. + * + * For all clock frequencies, they can be checked at runtime by enabling the 'ALWAYS_VALIDATE_CLOCKS' define. + * In MARLIN_DEV_MODE, they will also be printed to the serial console by 'MarlinHAL::HAL_clock_frequencies_dump'. + * + * See https://github.com/MarlinFirmware/Marlin/pull/27099 for more information. + */ + +// Target peripheral clock frequencies, must be integer divisors of F_SYSTEM_CLOCK. +// Changing the frequency here will automagically update everything else. +#define F_HCLK 200000000UL // 200 MHz; CPU +#define F_EXCLK (F_HCLK / 2) // 100 MHz; SDIO +#define F_PCLK0 (F_HCLK / 2) // 100 MHz; Timer6 (unused) +#define F_PCLK1 (F_HCLK / 4) // 50 MHz; USART, SPI, Timer0 (step + temp), TimerA (Servo) +#define F_PCLK2 (F_HCLK / 8) // 25 MHz; ADC Sampling +#define F_PCLK3 (F_HCLK / 8) // 25 MHz; I2C, WDT +#define F_PCLK4 (F_HCLK / 2) // 100 MHz; ADC Control + +// MPLL-P clock target frequency. This must be >= the highest peripheral clock frequency. +// PLL config is automatically calculated based on this value. +#define F_SYSTEM_CLOCK F_HCLK + +// The Peripheral clocks are only checked at runtime if this is enabled OR MARLIN_DEV_MODE is enabled. +// Compile time checks are always performed with Arduino core version >= 1.2.0. +#define ALWAYS_VALIDATE_CLOCKS 1 diff --git a/Marlin/src/HAL/HC32/timers.h b/Marlin/src/HAL/HC32/timers.h index 17d8967982..d03a5f9630 100644 --- a/Marlin/src/HAL/HC32/timers.h +++ b/Marlin/src/HAL/HC32/timers.h @@ -20,13 +20,14 @@ #pragma once #include #include +#include "sysclock.h" // // Timer Types // typedef Timer0 *timer_channel_t; typedef uint16_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFU // // Timer instances @@ -38,44 +39,46 @@ extern Timer0 step_timer; // Timer Configurations // -// TODO: some calculations (step irq min_step_rate) require the timer rate to be known at compile time -// this is not possible with the HC32F460, as the timer rate depends on PCLK1 -// as a workaround, PCLK1 = 50MHz is assumed (check with clock dump in MarlinHAL::init()) -#define HAL_TIMER_RATE 50000000 // 50MHz -// #define HAL_TIMER_RATE TIMER0_BASE_FREQUENCY - -// TODO: CYCLES_PER_MICROSECOND seems to be used by Marlin to calculate the number of cycles per microsecond in the timer ISRs -// by default, it uses F_CPU, but since that is not known at compile time for HC32, we overwrite it here -#undef CYCLES_PER_MICROSECOND -#define CYCLES_PER_MICROSECOND (HAL_TIMER_RATE / 1000000UL) +/** + * HAL_TIMER_RATE must be known at compile time since it's used to calculate + * STEPPER_TIMER_RATE, which is used in 'constexpr' calculations. + * On the HC32F460 the timer rate depends on PCLK1, which is derived from the + * system clock configured at runtime. + * Thus we use the 'F_PCLK1' constant defined in 'sysclock.h'. + * + * See https://github.com/MarlinFirmware/Marlin/pull/27099 for more information. + * + * NOTE: If the 'constexpr' requirement is ever lifted, TIMER0_BASE_FREQUENCY could + * be used instead. Tho this would probably not make any noticeable difference. + */ +#define HAL_TIMER_RATE F_PCLK1 // Temperature timer -#define TEMP_TIMER_NUM (&temp_timer) -#define TEMP_TIMER_PRIORITY DDL_IRQ_PRIORITY_02 -#define TEMP_TIMER_PRESCALE 16ul -#define TEMP_TIMER_RATE 1000 // 1kHz -#define TEMP_TIMER_FREQUENCY TEMP_TIMER_RATE // Alias for Marlin +#define TEMP_TIMER_NUM (&temp_timer) +#define TEMP_TIMER_PRIORITY DDL_IRQ_PRIORITY_02 +#define TEMP_TIMER_PRESCALE 16UL // 12.5MHz +#define TEMP_TIMER_RATE 1000 // 1kHz +#define TEMP_TIMER_FREQUENCY TEMP_TIMER_RATE // 1kHz also // Stepper timer -#define STEP_TIMER_NUM (&step_timer) -#define STEP_TIMER_PRIORITY DDL_IRQ_PRIORITY_01 -#define STEPPER_TIMER_PRESCALE 16ul +#define STEP_TIMER_NUM (&step_timer) +#define STEP_TIMER_PRIORITY DDL_IRQ_PRIORITY_00 // Top priority, nothing else uses it +#define STEPPER_TIMER_PRESCALE 16UL // 12.5MHz -// TODO: STEPPER_TIMER_RATE seems to work fine like this, but requires further testing... -#define STEPPER_TIMER_RATE (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) // 50MHz / 16 = 3.125MHz -#define STEPPER_TIMER_TICKS_PER_US (STEPPER_TIMER_RATE / 1000000) +#define STEPPER_TIMER_RATE (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) // 50MHz / 16 = 3.125MHz +#define STEPPER_TIMER_TICKS_PER_US (STEPPER_TIMER_RATE / 1000000UL) // Integer 3 // Pulse timer (== stepper timer) -#define PULSE_TIMER_NUM STEP_TIMER_NUM -#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE -#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US +#define PULSE_TIMER_NUM STEP_TIMER_NUM +#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE +#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US // // Channel aliases // -#define MF_TIMER_TEMP TEMP_TIMER_NUM -#define MF_TIMER_STEP STEP_TIMER_NUM -#define MF_TIMER_PULSE PULSE_TIMER_NUM +#define MF_TIMER_TEMP TEMP_TIMER_NUM +#define MF_TIMER_STEP STEP_TIMER_NUM +#define MF_TIMER_PULSE PULSE_TIMER_NUM // // HAL functions diff --git a/Marlin/src/HAL/HC32/u8g/u8g_com_HAL_HC32_sw_spi.cpp b/Marlin/src/HAL/HC32/u8g/u8g_com_HAL_HC32_sw_spi.cpp index 9f36096194..97f4b85eaf 100644 --- a/Marlin/src/HAL/HC32/u8g/u8g_com_HAL_HC32_sw_spi.cpp +++ b/Marlin/src/HAL/HC32/u8g/u8g_com_HAL_HC32_sw_spi.cpp @@ -82,7 +82,7 @@ static inline uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, c } static void u8g_sw_spi_shift_out(uint8_t val) { - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) + #if U8G_SPI_USE_MODE_3 swSpiTransfer_mode_3(val, SPI_speed); #else swSpiTransfer_mode_0(val, SPI_speed); @@ -116,15 +116,15 @@ uint8_t u8g_com_HAL_HC32_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, voi break; case U8G_COM_MSG_CHIP_SELECT: - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) // This LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active - WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active + #if U8G_SPI_USE_MODE_3 // This LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active WRITE(DOGLCD_CS, LOW); } else { WRITE(DOGLCD_CS, HIGH); - WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive + WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive } #else WRITE(DOGLCD_CS, !arg_val); diff --git a/Marlin/src/HAL/LINUX/HAL.cpp b/Marlin/src/HAL/LINUX/HAL.cpp index a90819b6b0..8a9028c8cc 100644 --- a/Marlin/src/HAL/LINUX/HAL.cpp +++ b/Marlin/src/HAL/LINUX/HAL.cpp @@ -52,7 +52,7 @@ uint8_t MarlinHAL::active_ch = 0; uint16_t MarlinHAL::adc_value() { const pin_t pin = analogInputToDigitalPin(active_ch); - if (!VALID_PIN(pin)) return 0; + if (!isValidPin(pin)) return 0; return uint16_t((Gpio::get(pin) >> 2) & 0x3FF); // return 10bit value as Marlin expects } diff --git a/Marlin/src/HAL/LINUX/HAL.h b/Marlin/src/HAL/LINUX/HAL.h index bb5fb73e05..68e0e1062c 100644 --- a/Marlin/src/HAL/LINUX/HAL.h +++ b/Marlin/src/HAL/LINUX/HAL.h @@ -124,7 +124,7 @@ public: static void isr_on() {} static void isr_off() {} - static void delay_ms(const int ms) { _delay_ms(ms); } + static void delay_ms(const int ms) { delay(ms); } // Tasks, called from idle() static void idletask() {} diff --git a/Marlin/src/HAL/LINUX/arduino.cpp b/Marlin/src/HAL/LINUX/arduino.cpp index 075b4ccde2..0a09a11091 100644 --- a/Marlin/src/HAL/LINUX/arduino.cpp +++ b/Marlin/src/HAL/LINUX/arduino.cpp @@ -31,10 +31,8 @@ void cli() { } // Disable void sei() { } // Enable // Time functions -void _delay_ms(const int ms) { delay(ms); } - -uint32_t millis() { - return (uint32_t)Clock::millis(); +unsigned long millis() { + return (unsigned long)Clock::millis(); } // This is required for some Arduino libraries we are using @@ -49,28 +47,28 @@ extern "C" void delay(const int msec) { // IO functions // As defined by Arduino INPUT(0x0), OUTPUT(0x1), INPUT_PULLUP(0x2) void pinMode(const pin_t pin, const uint8_t mode) { - if (!VALID_PIN(pin)) return; + if (!isValidPin(pin)) return; Gpio::setMode(pin, mode); } void digitalWrite(pin_t pin, uint8_t pin_status) { - if (!VALID_PIN(pin)) return; + if (!isValidPin(pin)) return; Gpio::set(pin, pin_status); } bool digitalRead(pin_t pin) { - if (!VALID_PIN(pin)) return false; + if (!isValidPin(pin)) return false; return Gpio::get(pin); } void analogWrite(pin_t pin, int pwm_value) { // 1 - 254: pwm_value, 0: LOW, 255: HIGH - if (!VALID_PIN(pin)) return; + if (!isValidPin(pin)) return; Gpio::set(pin, pwm_value); } uint16_t analogRead(pin_t adc_pin) { - if (!VALID_PIN(DIGITAL_PIN_TO_ANALOG_PIN(adc_pin))) return 0; - return Gpio::get(DIGITAL_PIN_TO_ANALOG_PIN(adc_pin)); + if (!isValidPin(digitalPinToAnalogIndex(adc_pin))) return 0; + return Gpio::get(digitalPinToAnalogIndex(adc_pin)); } char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s) { diff --git a/Marlin/src/HAL/LINUX/eeprom.cpp b/Marlin/src/HAL/LINUX/eeprom.cpp index 2b9b37e66d..f665600d5e 100644 --- a/Marlin/src/HAL/LINUX/eeprom.cpp +++ b/Marlin/src/HAL/LINUX/eeprom.cpp @@ -45,7 +45,7 @@ bool PersistentStore::access_start() { fseek(eeprom_file, 0L, SEEK_END); std::size_t file_size = ftell(eeprom_file); - if (file_size < MARLIN_EEPROM_SIZE) { + if (file_size < long(MARLIN_EEPROM_SIZE)) { memset(buffer + file_size, eeprom_erase_value, MARLIN_EEPROM_SIZE - file_size); } else { diff --git a/Marlin/src/HAL/LINUX/hardware/Timer.cpp b/Marlin/src/HAL/LINUX/hardware/Timer.cpp index 9f0d6a8f3a..013690a404 100644 --- a/Marlin/src/HAL/LINUX/hardware/Timer.cpp +++ b/Marlin/src/HAL/LINUX/hardware/Timer.cpp @@ -37,7 +37,10 @@ Timer::Timer() { } Timer::~Timer() { - timer_delete(timerid); + if (timerid != 0) { + timer_delete(timerid); + timerid = 0; + } } void Timer::init(uint32_t sig_id, uint32_t sim_freq, callback_fn* fn) { diff --git a/Marlin/src/HAL/LINUX/include/Arduino.h b/Marlin/src/HAL/LINUX/include/Arduino.h index f05aaed880..87148a40b2 100644 --- a/Marlin/src/HAL/LINUX/include/Arduino.h +++ b/Marlin/src/HAL/LINUX/include/Arduino.h @@ -74,11 +74,10 @@ extern "C" { // Time functions extern "C" void delay(const int ms); -void _delay_ms(const int ms); void delayMicroseconds(unsigned long); -uint32_t millis(); +unsigned long millis(); -//IO functions +// IO functions void pinMode(const pin_t, const uint8_t); void digitalWrite(pin_t, uint8_t); bool digitalRead(pin_t); diff --git a/Marlin/src/HAL/LINUX/include/pinmapping.h b/Marlin/src/HAL/LINUX/include/pinmapping.h index cfac5e3b48..9147ef82de 100644 --- a/Marlin/src/HAL/LINUX/include/pinmapping.h +++ b/Marlin/src/HAL/LINUX/include/pinmapping.h @@ -37,29 +37,29 @@ constexpr uint8_t NUM_ANALOG_INPUTS = 16; constexpr uint8_t analog_offset = NUM_DIGITAL_PINS - NUM_ANALOG_INPUTS; // Get the digital pin for an analog index -constexpr pin_t analogInputToDigitalPin(const int8_t p) { - return (WITHIN(p, 0, NUM_ANALOG_INPUTS) ? analog_offset + p : P_NC); +constexpr pin_t analogInputToDigitalPin(const int8_t a) { + return (WITHIN(a, 0, NUM_ANALOG_INPUTS - 1) ? analog_offset + a : P_NC); } // Get the analog index for a digital pin -constexpr int8_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t p) { - return (WITHIN(p, analog_offset, NUM_DIGITAL_PINS) ? p - analog_offset : P_NC); +constexpr int8_t digitalPinToAnalogIndex(const pin_t pin) { + return (WITHIN(pin, analog_offset, NUM_DIGITAL_PINS - 1) ? pin - analog_offset : P_NC); } // Return the index of a pin number constexpr int16_t GET_PIN_MAP_INDEX(const pin_t pin) { return pin; } // Test whether the pin is valid -constexpr bool VALID_PIN(const pin_t p) { return WITHIN(p, 0, NUM_DIGITAL_PINS); } +constexpr bool isValidPin(const pin_t pin) { return WITHIN(pin, 0, NUM_DIGITAL_PINS - 1); } // Test whether the pin is PWM -constexpr bool PWM_PIN(const pin_t p) { return false; } +constexpr bool PWM_PIN(const pin_t) { return false; } // Test whether the pin is interruptible -constexpr bool INTERRUPT_PIN(const pin_t p) { return false; } +constexpr bool INTERRUPT_PIN(const pin_t) { return false; } // Get the pin number at the given index -constexpr pin_t GET_PIN_MAP_PIN(const int16_t ind) { return ind; } +constexpr pin_t GET_PIN_MAP_PIN(const int16_t index) { return pin_t(index); } // Parse a G-code word into a pin index int16_t PARSED_PIN_INDEX(const char code, const int16_t dval); diff --git a/Marlin/src/HAL/LINUX/main.cpp b/Marlin/src/HAL/LINUX/main.cpp index f2af2ff33f..27a066d619 100644 --- a/Marlin/src/HAL/LINUX/main.cpp +++ b/Marlin/src/HAL/LINUX/main.cpp @@ -21,6 +21,7 @@ */ #ifdef __PLAT_LINUX__ +#ifndef UNIT_TEST //#define GPIO_LOGGING // Full GPIO and Positional Logging @@ -135,4 +136,5 @@ int main() { read_serial.join(); } +#endif // UNIT_TEST #endif // __PLAT_LINUX__ diff --git a/Marlin/src/HAL/LINUX/pinsDebug.h b/Marlin/src/HAL/LINUX/pinsDebug.h index e4ee27e8dd..aacb626105 100644 --- a/Marlin/src/HAL/LINUX/pinsDebug.h +++ b/Marlin/src/HAL/LINUX/pinsDebug.h @@ -19,30 +19,50 @@ * along with this program. If not, see . * */ +#pragma once /** - * Support routines for X86_64 - */ - -/** - * Translation of routines & variables used by pinsDebug.h + * Pins Debugging for Linux Native + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS -#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P) >= 0 ? 1 : 0) -#define digitalRead_mod(p) digitalRead(p) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin +#define getPinByIndex(x) pin_array[x].pin + +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) + // active ADC function/mode/code values for PINSEL registers -constexpr int8_t ADC_pin_mode(pin_t pin) { return -1; } +constexpr int8_t ADC_pin_mode(const pin_t) { return -1; } -int8_t get_pin_mode(const pin_t pin) { return VALID_PIN(pin) ? 0 : -1; } +// The pin and index are the same on this platform +bool getPinIsDigitalByIndex(const pin_t pin) { + return (!isAnalogPin(pin) || get_pin_mode(pin) != ADC_pin_mode(pin)); +} -bool GET_PINMODE(const pin_t pin) { +#define isAnalogPin(P) (digitalPinToAnalogIndex(P) >= 0) + +#define digitalRead_mod(P) digitalRead(P) + +int8_t get_pin_mode(const pin_t pin) { return isValidPin(pin) ? 0 : -1; } + +bool getValidPinMode(const pin_t pin) { const int8_t pin_mode = get_pin_mode(pin); if (pin_mode == -1 || pin_mode == ADC_pin_mode(pin)) // Invalid pin or active analog pin return false; @@ -50,11 +70,11 @@ bool GET_PINMODE(const pin_t pin) { return (Gpio::getMode(pin) != 0); // Input/output state } -bool GET_ARRAY_IS_DIGITAL(const pin_t pin) { - return (!IS_ANALOG(pin) || get_pin_mode(pin) != ADC_pin_mode(pin)); -} - -void pwm_details(const pin_t pin) {} +void printPinPWM(const pin_t) {} bool pwm_status(const pin_t) { return false; } -void print_port(const pin_t) {} +void printPinPort(const pin_t) {} + +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3d "), P); SERIAL_ECHO(buffer); }while(0) + +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) diff --git a/Marlin/src/HAL/LINUX/spi_pins.h b/Marlin/src/HAL/LINUX/spi_pins.h index 607b964281..221145e4b3 100644 --- a/Marlin/src/HAL/LINUX/spi_pins.h +++ b/Marlin/src/HAL/LINUX/spi_pins.h @@ -28,12 +28,6 @@ // spiBeginTransaction. #endif -// Onboard SD -//#define SD_SCK_PIN P0_07 -//#define SD_MISO_PIN P0_08 -//#define SD_MOSI_PIN P0_09 -//#define SD_SS_PIN P0_06 - // External SD #ifndef SD_SCK_PIN #define SD_SCK_PIN 50 @@ -44,9 +38,3 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN 52 #endif -#ifndef SD_SS_PIN - #define SD_SS_PIN 53 -#endif -#ifndef SDSS - #define SDSS SD_SS_PIN -#endif diff --git a/Marlin/src/HAL/LINUX/timers.h b/Marlin/src/HAL/LINUX/timers.h index 2b29edce0b..d75519ed3e 100644 --- a/Marlin/src/HAL/LINUX/timers.h +++ b/Marlin/src/HAL/LINUX/timers.h @@ -34,7 +34,7 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define HAL_TIMER_RATE ((SystemCoreClock) / 4) // frequency of timers peripherals diff --git a/Marlin/src/HAL/LPC1768/HAL.cpp b/Marlin/src/HAL/LPC1768/HAL.cpp index db9881cdd4..4b0e255f37 100644 --- a/Marlin/src/HAL/LPC1768/HAL.cpp +++ b/Marlin/src/HAL/LPC1768/HAL.cpp @@ -35,8 +35,6 @@ #include #include -DefaultSerial1 USBSerial(false, UsbSerial); - uint32_t MarlinHAL::adc_result = 0; pin_t MarlinHAL::adc_pin = 0; @@ -175,13 +173,8 @@ void MarlinHAL::init() { // HAL idle task void MarlinHAL::idletask() { #if HAS_SHARED_MEDIA - // If Marlin is using the SD card we need to lock it to prevent access from - // a PC via USB. - // Other HALs use IS_SD_PRINTING() and IS_SD_FILE_OPEN() to check for access but - // this will not reliably detect delete operations. To be safe we will lock - // the disk if Marlin has it mounted. Unfortunately there is currently no way - // to unmount the disk from the LCD menu. - // if (IS_SD_PRINTING() || IS_SD_FILE_OPEN()) + // When Marlin is using the SD Card it must be locked to prevent PC access via USB. + // For maximum safety we lock the disk if Marlin has it mounted for any reason. if (card.isMounted()) MSC_Aquire_Lock(); else diff --git a/Marlin/src/HAL/LPC1768/HAL.h b/Marlin/src/HAL/LPC1768/HAL.h index ab28e06eb8..9a68cdf748 100644 --- a/Marlin/src/HAL/LPC1768/HAL.h +++ b/Marlin/src/HAL/LPC1768/HAL.h @@ -38,72 +38,15 @@ extern "C" volatile uint32_t _millis; #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "MarlinSerial.h" #include #include -#include -// ------------------------ -// Serial ports -// ------------------------ +// +// Serial Ports +// -typedef ForwardSerial1Class< decltype(UsbSerial) > DefaultSerial1; -extern DefaultSerial1 USBSerial; - -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if SERIAL_PORT == -1 - #define MYSERIAL1 USBSerial -#elif WITHIN(SERIAL_PORT, 0, 3) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #error "SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." -#endif - -#ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #define MYSERIAL2 USBSerial - #elif WITHIN(SERIAL_PORT_2, 0, 3) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #error "SERIAL_PORT_2 must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 - #define MYSERIAL3 USBSerial - #elif WITHIN(SERIAL_PORT_3, 0, 3) - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #else - #error "SERIAL_PORT_3 must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL USBSerial - #elif WITHIN(MMU2_SERIAL_PORT, 0, 3) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #else - #error "MMU2_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if LCD_SERIAL_PORT == -1 - #define LCD_SERIAL USBSerial - #elif WITHIN(LCD_SERIAL_PORT, 0, 3) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #error "LCD_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif - #if HAS_DGUS_LCD - #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.available() - #endif -#endif +#include "MarlinSerial.h" // // Interrupts @@ -137,12 +80,12 @@ extern DefaultSerial1 USBSerial; // // Test whether the pin is valid -constexpr bool VALID_PIN(const pin_t pin) { +constexpr bool isValidPin(const pin_t pin) { return LPC176x::pin_is_valid(pin); } // Get the analog index for a digital pin -constexpr int8_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t pin) { +constexpr int8_t digitalPinToAnalogIndex(const pin_t pin) { return (LPC176x::pin_is_valid(pin) && LPC176x::pin_has_adc(pin)) ? pin : -1; } @@ -159,7 +102,7 @@ constexpr pin_t GET_PIN_MAP_PIN(const int16_t index) { // Parse a G-code word into a pin index int16_t PARSED_PIN_INDEX(const char code, const int16_t dval); // P0.6 thru P0.9 are for the onboard SD card -#define HAL_SENSITIVE_PINS P0_06, P0_07, P0_08, P0_09, +#define HAL_SENSITIVE_PINS P0_06, P0_07, P0_08, P0_09 // ------------------------ // Defines diff --git a/Marlin/src/HAL/LPC1768/MarlinSerial.cpp b/Marlin/src/HAL/LPC1768/MarlinSerial.cpp index f2aecf54a0..291eba68a7 100644 --- a/Marlin/src/HAL/LPC1768/MarlinSerial.cpp +++ b/Marlin/src/HAL/LPC1768/MarlinSerial.cpp @@ -25,6 +25,8 @@ #include "../../inc/MarlinConfig.h" +DefaultSerial1 USBSerial(false, UsbSerial); + #if USING_HW_SERIAL0 MarlinSerial _MSerial0(LPC_UART0); MSerialT MSerial0(true, _MSerial0); diff --git a/Marlin/src/HAL/LPC1768/MarlinSerial.h b/Marlin/src/HAL/LPC1768/MarlinSerial.h index 2fadd8209b..04110e4aff 100644 --- a/Marlin/src/HAL/LPC1768/MarlinSerial.h +++ b/Marlin/src/HAL/LPC1768/MarlinSerial.h @@ -21,6 +21,7 @@ */ #pragma once +#include #include #include @@ -30,6 +31,18 @@ #endif #include "../../core/serial_hook.h" +typedef ForwardSerial1Class< decltype(UsbSerial) > DefaultSerial1; +extern DefaultSerial1 USBSerial; + +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#define USB_SERIAL_PORT(...) USBSerial +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.available() +#endif + class MarlinSerial : public HardwareSerial { public: MarlinSerial(LPC_UART_TypeDef *UARTx) : HardwareSerial(UARTx) { } diff --git a/Marlin/src/HAL/LPC1768/eeprom_flash.cpp b/Marlin/src/HAL/LPC1768/eeprom/eeprom_flash.cpp similarity index 94% rename from Marlin/src/HAL/LPC1768/eeprom_flash.cpp rename to Marlin/src/HAL/LPC1768/eeprom/eeprom_flash.cpp index 9f873d5774..e24b0fdbc0 100644 --- a/Marlin/src/HAL/LPC1768/eeprom_flash.cpp +++ b/Marlin/src/HAL/LPC1768/eeprom/eeprom_flash.cpp @@ -36,11 +36,11 @@ * 16Kb I/O buffers (intended to hold DMA USB and Ethernet data, but currently * unused). */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" extern "C" { #include @@ -74,7 +74,7 @@ bool PersistentStore::access_start() { if (status == CMD_SUCCESS) { // sector is blank so nothing stored yet - for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = EEPROM_ERASE; + for (int i = 0; i < long(MARLIN_EEPROM_SIZE); i++) ram_eeprom[i] = EEPROM_ERASE; current_slot = EEPROM_SLOTS; } else { @@ -82,7 +82,7 @@ bool PersistentStore::access_start() { current_slot = first_nblank_loc / (MARLIN_EEPROM_SIZE); uint8_t *eeprom_data = SLOT_ADDRESS(EEPROM_SECTOR, current_slot); // load current settings - for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = eeprom_data[i]; + for (int i = 0; i < long(MARLIN_EEPROM_SIZE); i++) ram_eeprom[i] = eeprom_data[i]; } eeprom_dirty = false; diff --git a/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp b/Marlin/src/HAL/LPC1768/eeprom/eeprom_sdcard.cpp similarity index 98% rename from Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp rename to Marlin/src/HAL/LPC1768/eeprom/eeprom_sdcard.cpp index 30ecb01a09..5bf2d353b6 100644 --- a/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp +++ b/Marlin/src/HAL/LPC1768/eeprom/eeprom_sdcard.cpp @@ -26,13 +26,13 @@ #ifdef TARGET_LPC1768 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(SDCARD_EEPROM_EMULATION) //#define DEBUG_SD_EEPROM_EMULATION -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #include #include @@ -52,7 +52,6 @@ bool eeprom_file_open = false; size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE - eeprom_exclude_size; } bool PersistentStore::access_start() { - const char eeprom_erase_value = 0xFF; MSC_Aquire_Lock(); if (f_mount(&fat_fs, "", 1)) { MSC_Release_Lock(); @@ -65,6 +64,7 @@ bool PersistentStore::access_start() { UINT bytes_written; FSIZE_t file_size = f_size(&eeprom_file); f_lseek(&eeprom_file, file_size); + const char eeprom_erase_value = 0xFF; while (file_size < capacity() && res == FR_OK) { res = f_write(&eeprom_file, &eeprom_erase_value, 1, &bytes_written); file_size++; diff --git a/Marlin/src/HAL/LPC1768/eeprom_wired.cpp b/Marlin/src/HAL/LPC1768/eeprom/eeprom_wired.cpp similarity index 95% rename from Marlin/src/HAL/LPC1768/eeprom_wired.cpp rename to Marlin/src/HAL/LPC1768/eeprom/eeprom_wired.cpp index 3230e29afc..6a5d90bd02 100644 --- a/Marlin/src/HAL/LPC1768/eeprom_wired.cpp +++ b/Marlin/src/HAL/LPC1768/eeprom/eeprom_wired.cpp @@ -21,7 +21,7 @@ */ #ifdef TARGET_LPC1768 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -30,8 +30,8 @@ * with implementations supplied by the framework. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #define MARLIN_EEPROM_SIZE 0x8000 // 32K diff --git a/Marlin/src/HAL/LPC1768/endstop_interrupts.h b/Marlin/src/HAL/LPC1768/endstop_interrupts.h index 7a67b958b4..2c75fe6986 100644 --- a/Marlin/src/HAL/LPC1768/endstop_interrupts.h +++ b/Marlin/src/HAL/LPC1768/endstop_interrupts.h @@ -146,6 +146,12 @@ void setup_endstop_interrupts() { #endif _ATTACH(Z_MIN_PROBE_PIN); #endif + #if USE_CALIBRATION + #if !LPC1768_PIN_INTERRUPT_M(CALIBRATION_PIN) + #error "CALIBRATION_PIN is not INTERRUPT-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." + #endif + _ATTACH(CALIBRATION_PIN); + #endif #if USE_I_MAX #if !LPC1768_PIN_INTERRUPT_M(I_MAX_PIN) #error "I_MAX_PIN is not INTERRUPT-capable. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." diff --git a/Marlin/src/HAL/LPC1768/fast_pwm.cpp b/Marlin/src/HAL/LPC1768/fast_pwm.cpp index 6d2b1a9002..7cb212f5f9 100644 --- a/Marlin/src/HAL/LPC1768/fast_pwm.cpp +++ b/Marlin/src/HAL/LPC1768/fast_pwm.cpp @@ -26,8 +26,10 @@ void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { if (!LPC176x::pin_is_valid(pin)) return; - if (LPC176x::pwm_attach_pin(pin)) - LPC176x::pwm_write_ratio(pin, invert ? 1.0f - (float)v / v_size : (float)v / v_size); // map 1-254 onto PWM range + if (LPC176x::pwm_attach_pin(pin)) { + const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, LPC176x::pwm_get_period(pin)); + LPC176x::pwm_write(pin, duty); + } } void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { diff --git a/Marlin/src/HAL/LPC1768/fastio.h b/Marlin/src/HAL/LPC1768/fastio.h index c553ffb182..4858334080 100644 --- a/Marlin/src/HAL/LPC1768/fastio.h +++ b/Marlin/src/HAL/LPC1768/fastio.h @@ -66,7 +66,7 @@ #define _WRITE(IO,V) WRITE_PIN(IO,V) /// toggle a pin -#define _TOGGLE(IO) _WRITE(IO, !READ(IO)) +#define _TOGGLE(IO) LPC176x::gpio_toggle(IO) /// set pin as input #define _SET_INPUT(IO) SET_DIR_INPUT(IO) diff --git a/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h b/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h index 0b03cb2aea..a1b4dd5099 100644 --- a/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h +++ b/Marlin/src/HAL/LPC1768/inc/Conditionals_post.h @@ -29,6 +29,6 @@ // LPC1768 boards seem to lose steps when saving to EEPROM during print (issue #20785) // TODO: Which other boards are incompatible? -#if defined(MCU_LPC1768) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 +#if ALL(MCU_LPC1768, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 #define PRINTCOUNTER_SYNC #endif diff --git a/Marlin/src/HAL/LPC1768/inc/SanityCheck.h b/Marlin/src/HAL/LPC1768/inc/SanityCheck.h index 2782f225b0..5a5617f4ac 100644 --- a/Marlin/src/HAL/LPC1768/inc/SanityCheck.h +++ b/Marlin/src/HAL/LPC1768/inc/SanityCheck.h @@ -121,7 +121,7 @@ static_assert(DISABLED(BAUD_RATE_GCODE), "BAUD_RATE_GCODE is not yet supported o #if IS_TX1(BTN_EN2) || IS_RX1(BTN_EN1) #error "Serial port pins (1) conflict with Encoder Buttons!" #elif ANY_TX(1, SD_SCK_PIN, LCD_PINS_D4, DOGLCD_SCK, LCD_RESET_PIN, LCD_PINS_RS, SHIFT_CLK_PIN) \ - || ANY_RX(1, LCD_SDSS, LCD_PINS_RS, SD_MISO_PIN, DOGLCD_A0, SD_SS_PIN, LCD_SDSS, DOGLCD_CS, LCD_RESET_PIN, LCD_BACKLIGHT_PIN) + || ANY_RX(1, LCD_SDSS_PIN, LCD_PINS_RS, SD_MISO_PIN, DOGLCD_A0, SD_SS_PIN, DOGLCD_CS, LCD_RESET_PIN, LCD_BACKLIGHT_PIN) #error "Serial port pins (1) conflict with LCD pins!" #endif #endif @@ -211,8 +211,8 @@ static_assert(DISABLED(BAUD_RATE_GCODE), "BAUD_RATE_GCODE is not yet supported o #error "SCL0 overlaps with Encoder Button!" #elif IS_SCL0(SD_SS_PIN) #error "SCL0 overlaps with SD_SS_PIN!" - #elif IS_SCL0(LCD_SDSS) - #error "SCL0 overlaps with LCD_SDSS!" + #elif IS_SCL0(LCD_SDSS_PIN) + #error "SCL0 overlaps with LCD_SDSS_PIN!" #endif #undef PIN_IS_SDA0 #undef IS_SCL0 diff --git a/Marlin/src/HAL/LPC1768/pinsDebug.h b/Marlin/src/HAL/LPC1768/pinsDebug.h index 975511be9a..5baeadd0dd 100644 --- a/Marlin/src/HAL/LPC1768/pinsDebug.h +++ b/Marlin/src/HAL/LPC1768/pinsDebug.h @@ -19,22 +19,35 @@ * along with this program. If not, see . * */ +#pragma once /** - * Support routines for LPC1768 - */ - -/** - * Translation of routines & variables used by pinsDebug.h + * Pins Debugging for LPC1768/9 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS -#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P) >= 0 ? 1 : 0) -#define digitalRead_mod(p) extDigitalRead(p) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("P%d_%02d"), LPC176x::pin_port(p), LPC176x::pin_bit(p)); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR("_A%d "), LPC176x::pin_get_adc_channel(pin)); SERIAL_ECHO(buffer); }while(0) +#define isAnalogPin(P) (digitalPinToAnalogIndex(P) >= 0) +#define digitalRead_mod(P) extDigitalRead(P) +#define getPinByIndex(x) pin_array[x].pin +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("P%d_%02d"), LPC176x::pin_port(P), LPC176x::pin_bit(P)); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR("_A%d "), LPC176x::pin_get_adc_channel(P)); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 17 // space needed to be pretty if not first name assigned to a pin // pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities @@ -42,15 +55,15 @@ #define M43_NEVER_TOUCH(Q) ((Q) == P0_29 || (Q) == P0_30 || (Q) == P2_09) // USB pins #endif -bool GET_PINMODE(const pin_t pin) { +bool getValidPinMode(const pin_t pin) { if (!LPC176x::pin_is_valid(pin) || LPC176x::pin_adc_enabled(pin)) // Invalid pin or active analog pin return false; return LPC176x::gpio_direction(pin); } -#define GET_ARRAY_IS_DIGITAL(x) ((bool) pin_array[x].is_digital) +#define getPinIsDigitalByIndex(x) ((bool) pin_array[x].is_digital) -void print_port(const pin_t) {} -void pwm_details(const pin_t) {} +void printPinPort(const pin_t) {} +void printPinPWM(const pin_t) {} bool pwm_status(const pin_t) { return false; } diff --git a/Marlin/src/HAL/LPC1768/spi_pins.h b/Marlin/src/HAL/LPC1768/spi_pins.h index 295d6462fd..036bf0e023 100644 --- a/Marlin/src/HAL/LPC1768/spi_pins.h +++ b/Marlin/src/HAL/LPC1768/spi_pins.h @@ -28,12 +28,12 @@ // spiBeginTransaction. #endif -/** onboard SD card */ -//#define SD_SCK_PIN P0_07 -//#define SD_MISO_PIN P0_08 -//#define SD_MOSI_PIN P0_09 -//#define SD_SS_PIN P0_06 -/** external */ +// Onboard SD +//#define SD_SCK_PIN P0_07 +//#define SD_MISO_PIN P0_08 +//#define SD_MOSI_PIN P0_09 + +// External SD #ifndef SD_SCK_PIN #define SD_SCK_PIN P0_15 #endif @@ -43,10 +43,3 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN P0_18 #endif -#ifndef SD_SS_PIN - #define SD_SS_PIN P1_23 -#endif -#if !defined(SDSS) || SDSS == P_NC // gets defaulted in pins.h - #undef SDSS - #define SDSS SD_SS_PIN -#endif diff --git a/Marlin/src/HAL/LPC1768/timers.h b/Marlin/src/HAL/LPC1768/timers.h index bae01703ed..8c0a39158e 100644 --- a/Marlin/src/HAL/LPC1768/timers.h +++ b/Marlin/src/HAL/LPC1768/timers.h @@ -57,7 +57,7 @@ #define _HAL_TIMER_ISR(T) __HAL_TIMER_ISR(T) typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define HAL_TIMER_RATE ((F_CPU) / 4) // frequency of timers peripherals diff --git a/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp b/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp index e714c3c16d..09603de972 100644 --- a/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp +++ b/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp @@ -28,7 +28,7 @@ #include "../include/i2c_util.h" #include "../../../core/millis_t.h" -extern int millis(); +uint32_t millis(); #ifdef __cplusplus extern "C" { diff --git a/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.c b/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.c index 466fc80203..51ad7e095b 100644 --- a/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.c +++ b/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.c @@ -27,7 +27,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ #ifdef TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.h b/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.h index d60d93dadd..45e0610fb1 100644 --- a/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.h +++ b/Marlin/src/HAL/LPC1768/u8g/LCD_pin_routines.h @@ -28,7 +28,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ void u8g_SetPinOutput(uint8_t internal_pin_number); diff --git a/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_sw_spi.cpp b/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_sw_spi.cpp index 4edd0d3886..77e5870de7 100644 --- a/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_sw_spi.cpp +++ b/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_sw_spi.cpp @@ -132,7 +132,7 @@ uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, const pin_t sck static uint8_t SPI_speed = 0; static void u8g_sw_spi_shift_out(uint8_t dataPin, uint8_t clockPin, uint8_t val) { - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) + #if U8G_SPI_USE_MODE_3 swSpiTransfer_mode_3(val, SPI_speed, clockPin, -1, dataPin); #else swSpiTransfer_mode_0(val, SPI_speed, clockPin, -1, dataPin); @@ -160,15 +160,15 @@ uint8_t u8g_com_HAL_LPC1768_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, break; case U8G_COM_MSG_CHIP_SELECT: - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) // LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active - u8g_SetPILevel(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active + #if U8G_SPI_USE_MODE_3 // LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + u8g_SetPILevel(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active u8g_SetPILevel(u8g, U8G_PI_CS, LOW); } else { u8g_SetPILevel(u8g, U8G_PI_CS, HIGH); - u8g_SetPILevel(u8g, U8G_PI_SCK, 0); // Set SCK to mode 0 idle state after CS goes inactive + u8g_SetPILevel(u8g, U8G_PI_SCK, 0); // Set SCK to mode 0 idle state after CS goes inactive } #else u8g_SetPILevel(u8g, U8G_PI_CS, !arg_val); diff --git a/Marlin/src/HAL/LPC1768/upload_extra_script.py b/Marlin/src/HAL/LPC1768/upload_extra_script.py index efd46fdd63..f9be140592 100755 --- a/Marlin/src/HAL/LPC1768/upload_extra_script.py +++ b/Marlin/src/HAL/LPC1768/upload_extra_script.py @@ -13,9 +13,9 @@ if pioutil.is_pio_build(): target_drive = "REARM" import platform - current_OS = platform.system() - Import("env") + + env = pioutil.env def print_error(e): print('\nUnable to find destination disk (%s)\n' \ @@ -37,7 +37,7 @@ if pioutil.is_pio_build(): # # platformio.ini will accept this for a Windows upload port designation: 'upload_port = L:' # Windows - doesn't care about the disk's name, only cares about the drive letter - import subprocess,string + import subprocess, string from ctypes import windll from pathlib import PureWindowsPath @@ -54,18 +54,25 @@ if pioutil.is_pio_build(): final_drive_name = drive + ':' # print ('disc check: {}'.format(final_drive_name)) try: - volume_info = str(subprocess.check_output('cmd /C dir ' + final_drive_name, stderr=subprocess.STDOUT)) + volume_info = str(subprocess.check_output('cmd /C vol ' + final_drive_name, stderr=subprocess.STDOUT)) except Exception as e: print ('error:{}'.format(e)) continue else: - if target_drive in volume_info and not target_file_found: # set upload if not found target file yet - target_drive_found = True + if target_drive in volume_info: # set upload upload_disk = PureWindowsPath(final_drive_name) - if target_filename in volume_info: - if not target_file_found: + target_drive_found = True + break + try: + dir_info = str(subprocess.check_output('cmd /C dir ' + final_drive_name, stderr=subprocess.STDOUT)) + except Exception as e: + print ('error:{}'.format(e)) + continue + else: + if target_filename in dir_info: upload_disk = PureWindowsPath(final_drive_name) - target_file_found = True + target_file_found = True + break elif current_OS == 'Linux': # diff --git a/Marlin/src/HAL/NATIVE_SIM/HAL.h b/Marlin/src/HAL/NATIVE_SIM/HAL.h index f57e065a8e..661c502f22 100644 --- a/Marlin/src/HAL/NATIVE_SIM/HAL.h +++ b/Marlin/src/HAL/NATIVE_SIM/HAL.h @@ -52,7 +52,9 @@ uint8_t _getc(); // ------------------------ #define CPU_32_BIT -#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +class Servo; +typedef Servo hal_servo_t; #define F_CPU 100000000 #define SystemCoreClock F_CPU @@ -71,37 +73,10 @@ extern MSerialT serial_stream_2; extern MSerialT serial_stream_3; #define _MSERIAL(X) serial_stream_##X -#define MSERIAL(X) _MSERIAL(X) -#if WITHIN(SERIAL_PORT, 0, 3) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #error "SERIAL_PORT must be from 0 to 3. Please update your configuration." -#endif - -#ifdef SERIAL_PORT_2 - #if WITHIN(SERIAL_PORT_2, 0, 3) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #error "SERIAL_PORT_2 must be from 0 to 3. Please update your configuration." - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if WITHIN(MMU2_SERIAL_PORT, 0, 3) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #else - #error "MMU2_SERIAL_PORT must be from 0 to 3. Please update your configuration." - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if WITHIN(LCD_SERIAL_PORT, 0, 3) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #error "LCD_SERIAL_PORT must be from 0 to 3. Please update your configuration." - #endif -#endif +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#include "../shared/serial_ports.h" // ------------------------ // Interrupts @@ -220,7 +195,7 @@ public: static void isr_on() {} static void isr_off() {} - static void delay_ms(const int ms) { _delay_ms(ms); } + static void delay_ms(const int ms) { delay(ms); } // Tasks, called from idle() static void idletask(); @@ -259,8 +234,10 @@ public: * No option to invert the duty cycle [default = false] * No option to change the scale of the provided value to enable finer PWM duty control [default = 255] */ - static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { - analogWrite(pin, v); + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false) { + auto value = map(v, 0, v_size, 0, UINT16_MAX); + value = invert ? UINT16_MAX - value : value; + analogWrite(pin, value); } static void set_pwm_frequency(const pin_t, int) {} diff --git a/Marlin/src/HAL/NATIVE_SIM/Servo.cpp b/Marlin/src/HAL/NATIVE_SIM/Servo.cpp new file mode 100644 index 0000000000..a15eda1bf6 --- /dev/null +++ b/Marlin/src/HAL/NATIVE_SIM/Servo.cpp @@ -0,0 +1,104 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_NATIVE_SIM__ + +#include "../../inc/MarlinConfig.h" + +#if HAS_SERVOS + +#include "Servo.h" + +//#define DEBUG_SERVOS +#define DEBUG_OUT ENABLED(DEBUG_SERVOS) +#include "../../../core/debug_out.h" + +uint8_t ServoCount = 0; // the total number of attached servos + +Servo::Servo() { + // Constructor stub + DEBUG_ECHOLNPGM("Debug Servo: constructor"); + this->servoIndex = ServoCount++; // assign a servo index to this instance +} + +uint8_t Servo::attach(int pin) { + // Attach stub + DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " servo index ", this->servoIndex); + return attach(pin, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); +} + +uint8_t Servo::attach(int pin, int min, int max) { + // Attach with min and max stub + DEBUG_ECHOLNPGM("Debug Servo: attach to pin ", pin, " with min ", min, " and max ", max); + if (pin > 0) servo_pin = pin; + return this->servoIndex; +} + +void Servo::detach() { + // Detach stub + DEBUG_ECHOLNPGM("Debug Servo: detach"); +} + +// If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds +void Servo::write(int value) { + if (value < MIN_PULSE_WIDTH) { // treat values less than 544 as angles in degrees (valid values in microseconds are handled as microseconds) + value = map(constrain(value, 0, 180), 0, 180, SERVO_MIN_US(min), SERVO_MAX_US(max)); + } + writeMicroseconds(value); + DEBUG_ECHOLNPGM("Debug Servo: write ", value); +} + +void Servo::writeMicroseconds(int value) { + // Simulate the servo movement + this->value = value; + hal.set_pwm_duty(pin_t(this->servo_pin), (float(value) / 20000) * UINT16_MAX, UINT16_MAX); + DEBUG_ECHOLNPGM("Debug Servo: write microseconds ", value); +} + +int Servo::read() { + // Read stub + DEBUG_ECHOLNPGM("Debug Servo: read ", this->value); + return this->value; +} + +int Servo::readMicroseconds() { + // Read microseconds stub + DEBUG_ECHOLNPGM("Debug Servo: read microseconds"); + return 0; +} + +bool Servo::attached() { + // Attached stub + DEBUG_ECHOLNPGM("Debug Servo: attached"); + return false; +} + +int Servo::move(const unsigned char cmd) { + // Move stub + DEBUG_ECHOLNPGM("Debug Servo: move ", cmd); + write(cmd); + return 0; +} + +#endif // HAS_SERVOS +#endif // __PLAT_NATIVE_SIM__ diff --git a/Marlin/src/HAL/NATIVE_SIM/Servo.h b/Marlin/src/HAL/NATIVE_SIM/Servo.h new file mode 100644 index 0000000000..428871142c --- /dev/null +++ b/Marlin/src/HAL/NATIVE_SIM/Servo.h @@ -0,0 +1,48 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo +#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo +#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached +#define SERVO_MIN_US(v) (MIN_PULSE_WIDTH - (v) * 4) // minimum value in uS for this servo +#define SERVO_MAX_US(v) (MAX_PULSE_WIDTH - (v) * 4) // maximum value in uS for this servo + +class Servo { +public: + Servo(); + uint8_t attach(int pin); // Attach the given pin to the next free channel, set pinMode, return channel number or INVALID_SERVO if failure + uint8_t attach(int pin, int min, int max); // As above but also set min and max values for writes. + void detach(); + void write(int value); // If value is < 200 it's treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds + int read(); // Return current pulse width as an angle between 0 and 180 degrees + int readMicroseconds(); // Return current pulse width in microseconds for this servo + bool attached(); // Return true if this servo is attached, otherwise false + int move (const unsigned char cmd); +private: + uint8_t servoIndex; // Index into the channel data for this servo + int8_t min; // Minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH + int value; // Pulse width in microseconds for this servo + int servo_pin = 0; // pin number for this servo +}; diff --git a/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h b/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h new file mode 100644 index 0000000000..e74c312274 --- /dev/null +++ b/Marlin/src/HAL/NATIVE_SIM/endstop_interrupts.h @@ -0,0 +1,30 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#error "ENDSTOP_INTERRUPTS_FEATURE is not supported in this simulation environment." + +void setup_endstop_interrupts() { + // This function is a stub for setting up endstop interrupts. + // Since this is a simulation environment, actual hardware interrupts + // are not applicable. Add any necessary simulation-specific logic here. +} diff --git a/Marlin/src/HAL/NATIVE_SIM/fastio.h b/Marlin/src/HAL/NATIVE_SIM/fastio.h index f501afdbaa..e0b7af09f9 100644 --- a/Marlin/src/HAL/NATIVE_SIM/fastio.h +++ b/Marlin/src/HAL/NATIVE_SIM/fastio.h @@ -28,6 +28,8 @@ #include "../shared/Marduino.h" #include +#define NO_COMPILE_TIME_PWM + #define SET_DIR_INPUT(IO) Gpio::setDir(IO, 1) #define SET_DIR_OUTPUT(IO) Gpio::setDir(IO, 0) diff --git a/Marlin/src/HAL/NATIVE_SIM/pinsDebug.cpp b/Marlin/src/HAL/NATIVE_SIM/pinsDebug.cpp index c4d56c6c21..1354c79b31 100644 --- a/Marlin/src/HAL/NATIVE_SIM/pinsDebug.cpp +++ b/Marlin/src/HAL/NATIVE_SIM/pinsDebug.cpp @@ -25,11 +25,11 @@ #include "../../inc/MarlinConfig.h" #include "pinsDebug.h" -int8_t ADC_pin_mode(pin_t pin) { return -1; } +int8_t ADC_pin_mode(const pin_t) { return -1; } -int8_t get_pin_mode(const pin_t pin) { return VALID_PIN(pin) ? 0 : -1; } +int8_t get_pin_mode(const pin_t pin) { return isValidPin(pin) ? 0 : -1; } -bool GET_PINMODE(const pin_t pin) { +bool getValidPinMode(const pin_t pin) { const int8_t pin_mode = get_pin_mode(pin); if (pin_mode == -1 || pin_mode == ADC_pin_mode(pin)) // Invalid pin or active analog pin return false; @@ -37,12 +37,13 @@ bool GET_PINMODE(const pin_t pin) { return (Gpio::getMode(pin) != 0); // Input/output state } -bool GET_ARRAY_IS_DIGITAL(const pin_t pin) { - return !IS_ANALOG(pin) || get_pin_mode(pin) != ADC_pin_mode(pin); +// The pin and index are the same on this platform +bool getPinIsDigitalByIndex(const pin_t pin) { + return !isAnalogPin(pin) || get_pin_mode(pin) != ADC_pin_mode(pin); } -void print_port(const pin_t) {} -void pwm_details(const pin_t) {} +void printPinPort(const pin_t) {} +void printPinPWM(const pin_t) {} bool pwm_status(const pin_t) { return false; } #endif diff --git a/Marlin/src/HAL/NATIVE_SIM/pinsDebug.h b/Marlin/src/HAL/NATIVE_SIM/pinsDebug.h index 3321d1484d..752802d438 100644 --- a/Marlin/src/HAL/NATIVE_SIM/pinsDebug.h +++ b/Marlin/src/HAL/NATIVE_SIM/pinsDebug.h @@ -19,30 +19,43 @@ * along with this program. If not, see . * */ - -/** - * Support routines for X86_64 - */ #pragma once /** - * Translation of routines & variables used by pinsDebug.h + * Pins Debugging for x86_64 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS -#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P) >= 0 ? 1 : 0) -#define digitalRead_mod(p) digitalRead(p) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define isAnalogPin(P) (digitalPinToAnalogIndex(P) >= 0) +#define digitalRead_mod(P) digitalRead(P) +#define getPinByIndex(x) pin_array[x].pin +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3d "), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin // Active ADC function/mode/code values for PINSEL registers -int8_t ADC_pin_mode(pin_t pin); -int8_t get_pin_mode(const pin_t pin); -bool GET_PINMODE(const pin_t pin); -bool GET_ARRAY_IS_DIGITAL(const pin_t pin); -void print_port(const pin_t); -void pwm_details(const pin_t); +int8_t ADC_pin_mode(const pin_t); +int8_t get_pin_mode(const pin_t); +bool getValidPinMode(const pin_t); +bool getPinIsDigitalByIndex(const pin_t); +void printPinPort(const pin_t); +void printPinPWM(const pin_t); bool pwm_status(const pin_t); diff --git a/Marlin/src/HAL/NATIVE_SIM/servo_private.h b/Marlin/src/HAL/NATIVE_SIM/servo_private.h deleted file mode 100644 index e0eb30ab28..0000000000 --- a/Marlin/src/HAL/NATIVE_SIM/servo_private.h +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] - * - * Based on Sprinter and grbl. - * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm - * - * 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 3 of the License, 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. If not, see . - * - */ -#pragma once - -/** - * servo.h - Interrupt driven Servo library for Arduino using 16 bit timers- Version 2 - * Copyright (c) 2009 Michael Margolis. All right reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/** - * Based on "servo.h - Interrupt driven Servo library for Arduino using 16 bit timers - - * Version 2 Copyright (c) 2009 Michael Margolis. All right reserved. - * - * The only modification was to update/delete macros to match the LPC176x. - * - */ - -#include - -// Macros -//values in microseconds -#define MIN_PULSE_WIDTH 544 // the shortest pulse sent to a servo -#define MAX_PULSE_WIDTH 2400 // the longest pulse sent to a servo -#define DEFAULT_PULSE_WIDTH 1500 // default pulse width when servo is attached -#define REFRESH_INTERVAL 20000 // minimum time to refresh servos in microseconds - -#define MAX_SERVOS 4 - -#define INVALID_SERVO 255 // flag indicating an invalid servo index - -// Types - -typedef struct { - uint8_t nbr : 8 ; // a pin number from 0 to 254 (255 signals invalid pin) - uint8_t isActive : 1 ; // true if this channel is enabled, pin not pulsed if false -} ServoPin_t; - -typedef struct { - ServoPin_t Pin; - unsigned int pulse_width; // pulse width in microseconds -} ServoInfo_t; - -// Global variables - -extern uint8_t ServoCount; -extern ServoInfo_t servo_info[MAX_SERVOS]; diff --git a/Marlin/src/HAL/NATIVE_SIM/spi_pins.h b/Marlin/src/HAL/NATIVE_SIM/spi_pins.h index d9911bf56c..0e83e13692 100644 --- a/Marlin/src/HAL/NATIVE_SIM/spi_pins.h +++ b/Marlin/src/HAL/NATIVE_SIM/spi_pins.h @@ -32,7 +32,6 @@ //#define SD_SCK_PIN P0_07 //#define SD_MISO_PIN P0_08 //#define SD_MOSI_PIN P0_09 -//#define SD_SS_PIN P0_06 // External SD #ifndef SD_SCK_PIN @@ -44,9 +43,3 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN 52 #endif -#ifndef SD_SS_PIN - #define SD_SS_PIN 53 -#endif -#ifndef SDSS - #define SDSS SD_SS_PIN -#endif diff --git a/Marlin/src/HAL/NATIVE_SIM/timers.h b/Marlin/src/HAL/NATIVE_SIM/timers.h index d46e8e7b94..43aecf427b 100644 --- a/Marlin/src/HAL/NATIVE_SIM/timers.h +++ b/Marlin/src/HAL/NATIVE_SIM/timers.h @@ -34,7 +34,7 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint64_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFF'FFFF'FFFF'FFFFULL #define HAL_TIMER_RATE ((SystemCoreClock) / 4) // frequency of timers peripherals @@ -52,11 +52,11 @@ typedef uint64_t hal_timer_t; #endif #define SYSTICK_TIMER_FREQUENCY 1000 -#define TEMP_TIMER_RATE 1000000 -#define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency +#define TEMP_TIMER_RATE 1'000'000 +#define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency #define STEPPER_TIMER_RATE HAL_TIMER_RATE // frequency of stepper timer (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) -#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs +#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1'000'000) // stepper timer ticks per µs #define STEPPER_TIMER_PRESCALE (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US) #define PULSE_TIMER_RATE STEPPER_TIMER_RATE // frequency of pulse timer diff --git a/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp b/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp index 3566528079..9cb33694a3 100644 --- a/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp +++ b/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp @@ -27,7 +27,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ #ifdef __PLAT_NATIVE_SIM__ diff --git a/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.h b/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.h index 39af4d7e68..ab73635d28 100644 --- a/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.h +++ b/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.h @@ -28,7 +28,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ #ifdef __cplusplus diff --git a/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_st7920_sw_spi.cpp b/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_st7920_sw_spi.cpp index 46f2798afa..b07bc1644d 100644 --- a/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_st7920_sw_spi.cpp +++ b/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_st7920_sw_spi.cpp @@ -71,13 +71,13 @@ static uint8_t SPI_speed = 0; static uint8_t swSpiTransfer(uint8_t b, const uint8_t spi_speed, const pin_t sck_pin, const pin_t miso_pin, const pin_t mosi_pin) { for (uint8_t i = 0; i < 8; i++) { - WRITE_PIN(mosi_pin, !!(b & 0x80)); + WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, LOW, HIGH)); DELAY_CYCLES(SPI_SPEED); - WRITE_PIN(sck_pin, HIGH); + WRITE_PIN(mosi_pin, !!(b & 0x80)); DELAY_CYCLES(SPI_SPEED); b <<= 1; if (miso_pin >= 0 && READ_PIN(miso_pin)) b |= 1; - WRITE_PIN(sck_pin, LOW); + WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, HIGH, LOW)); DELAY_CYCLES(SPI_SPEED); } return b; @@ -85,7 +85,7 @@ static uint8_t swSpiTransfer(uint8_t b, const uint8_t spi_speed, const pin_t sck static uint8_t swSpiInit(const uint8_t spiRate, const pin_t sck_pin, const pin_t mosi_pin) { WRITE_PIN(mosi_pin, HIGH); - WRITE_PIN(sck_pin, LOW); + WRITE_PIN(sck_pin, TERN(U8G_SPI_USE_MODE_3, HIGH, LOW)); return spiRate; } @@ -93,11 +93,11 @@ static void u8g_com_st7920_write_byte_sw_spi(uint8_t rs, uint8_t val) { static uint8_t rs_last_state = 255; if (rs != rs_last_state) { // Transfer Data (FA) or Command (F8) - swSpiTransfer(rs ? 0x0FA : 0x0F8, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL); + swSpiTransfer(rs ? 0xFA : 0xF8, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL); rs_last_state = rs; DELAY_US(40); // Give the controller time to process the data: 20 is bad, 30 is OK, 40 is safe } - swSpiTransfer(val & 0x0F0, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL); + swSpiTransfer(val & 0xF0, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL); swSpiTransfer(val << 4, SPI_speed, SCK_pin_ST7920_HAL, -1, MOSI_pin_ST7920_HAL_HAL); } @@ -169,5 +169,32 @@ uint8_t u8g_com_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void } #endif +#if ENABLED(LIGHTWEIGHT_UI) + + #define ST7920_CS() { WRITE(LCD_PINS_RS, HIGH); } + #define ST7920_NCS() { WRITE(LCD_PINS_RS, LOW); } + #define ST7920_SET_CMD() { ST7920_SWSPI_SND_8BIT(0xF8); } + #define ST7920_SET_DAT() { ST7920_SWSPI_SND_8BIT(0xFA); } + #define ST7920_WRITE_BYTE(a) { ST7920_SWSPI_SND_8BIT((uint8_t)((a)&0xF0u)); ST7920_SWSPI_SND_8BIT((uint8_t)((a)<<4U)); } + + #define ST7920_DAT(V) !!((V) & 0x80) + + #define ST7920_SND_BIT(...) do{ \ + WRITE(LCD_PINS_D4, LOW); \ + WRITE(LCD_PINS_EN, ST7920_DAT(val)); \ + WRITE(LCD_PINS_D4, HIGH); \ + val <<= 1; }while(0); + + void ST7920_SWSPI_SND_8BIT(uint8_t val) { + REPEAT(8, ST7920_SND_BIT); + } + + void ST7920_cs() { ST7920_CS(); } + void ST7920_ncs() { ST7920_NCS(); } + void ST7920_set_cmd() { ST7920_SET_CMD(); } + void ST7920_set_dat() { ST7920_SET_DAT(); } + void ST7920_write_byte(const uint8_t val) { ST7920_WRITE_BYTE(val); } +#endif // LIGHTWEIGHT_UI + #endif // IS_U8GLIB_ST7920 #endif // __PLAT_NATIVE_SIM__ diff --git a/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_sw_spi.cpp b/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_sw_spi.cpp index 9184e2f618..fd11e5d767 100644 --- a/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_sw_spi.cpp +++ b/Marlin/src/HAL/NATIVE_SIM/u8g/u8g_com_sw_spi.cpp @@ -127,11 +127,11 @@ uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, const pin_t sck static uint8_t SPI_speed = 0; static uint8_t swSpiInit(const uint8_t spi_speed, const uint8_t clk_pin, const uint8_t mosi_pin) { - return spi_speed; + return spi_speed; } static void u8g_sw_spi_shift_out(uint8_t dataPin, uint8_t clockPin, uint8_t val) { - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) + #if U8G_SPI_USE_MODE_3 swSpiTransfer_mode_3(val, SPI_speed, clockPin, -1, dataPin); #else swSpiTransfer_mode_0(val, SPI_speed, clockPin, -1, dataPin); @@ -159,15 +159,15 @@ uint8_t u8g_com_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_pt break; case U8G_COM_MSG_CHIP_SELECT: - #if ANY(FYSETC_MINI_12864, MKS_MINI_12864) // LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active - u8g_SetPILevel(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active + #if U8G_SPI_USE_MODE_3 // LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + u8g_SetPILevel(u8g, U8G_PI_SCK, 1); // Set SCK to mode 3 idle state before CS goes active u8g_SetPILevel(u8g, U8G_PI_CS, LOW); } else { u8g_SetPILevel(u8g, U8G_PI_CS, HIGH); - u8g_SetPILevel(u8g, U8G_PI_SCK, 0); // Set SCK to mode 0 idle state after CS goes inactive + u8g_SetPILevel(u8g, U8G_PI_SCK, 0); // Set SCK to mode 0 idle state after CS goes inactive } #else u8g_SetPILevel(u8g, U8G_PI_CS, !arg_val); diff --git a/Marlin/src/HAL/RP2040/HAL.cpp b/Marlin/src/HAL/RP2040/HAL.cpp new file mode 100644 index 0000000000..e8d0eea7cd --- /dev/null +++ b/Marlin/src/HAL/RP2040/HAL.cpp @@ -0,0 +1,185 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "HAL.h" +//#include "usb_serial.h" + +#include "../../inc/MarlinConfig.h" +#include "../shared/Delay.h" + +extern "C" { + #include "pico/bootrom.h" + #include "hardware/watchdog.h" +} + +#if HAS_SD_HOST_DRIVE + #include "msc_sd.h" + #include "usbd_cdc_if.h" +#endif + +// ------------------------ +// Public Variables +// ------------------------ + +volatile uint16_t adc_result; + +// ------------------------ +// Public functions +// ------------------------ + +TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); + +// HAL initialization task +void MarlinHAL::init() { + // Ensure F_CPU is a constant expression. + // If the compiler breaks here, it means that delay code that should compute at compile time will not work. + // So better safe than sorry here. + constexpr unsigned int cpuFreq = F_CPU; + UNUSED(cpuFreq); + + #if HAS_MEDIA && DISABLED(ONBOARD_SDIO) && PIN_EXISTS(SD_SS) + OUT_WRITE(SD_SS_PIN, HIGH); // Try to set SD_SS_PIN inactive before any other SPI users start up + #endif + + #if PIN_EXISTS(LED) + OUT_WRITE(LED_PIN, LOW); + #endif + + #if ENABLED(SRAM_EEPROM_EMULATION) + // __HAL_RCC_PWR_CLK_ENABLE(); + // HAL_PWR_EnableBkUpAccess(); // Enable access to backup SRAM + // __HAL_RCC_BKPSRAM_CLK_ENABLE(); + // LL_PWR_EnableBkUpRegulator(); // Enable backup regulator + // while (!LL_PWR_IsActiveFlag_BRR()); // Wait until backup regulator is initialized + #endif + + HAL_timer_init(); + + #if ALL(EMERGENCY_PARSER, USBD_USE_CDC) + USB_Hook_init(); + #endif + + TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler + + TERN_(HAS_SD_HOST_DRIVE, MSC_SD_init()); // Enable USB SD card access + + #if PIN_EXISTS(USB_CONNECT) + OUT_WRITE(USB_CONNECT_PIN, !USB_CONNECT_INVERTING); // USB clear connection + delay_ms(1000); // Give OS time to notice + WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING); + #endif +} + +uint8_t MarlinHAL::get_reset_source() { + return watchdog_enable_caused_reboot() ? RST_WATCHDOG : 0; +} + +void MarlinHAL::reboot() { watchdog_reboot(0, 0, 1); } + +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout + + extern "C" { + #include "hardware/watchdog.h" + } + + void MarlinHAL::watchdog_init() { + #if DISABLED(DISABLE_WATCHDOG_INIT) + static_assert(WDT_TIMEOUT_US > 1000, "WDT Timeout is too small, aborting"); + watchdog_enable(WDT_TIMEOUT_US/1000, true); + #endif + } + + void MarlinHAL::watchdog_refresh() { + watchdog_update(); + #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) + TOGGLE(LED_PIN); // heartbeat indicator + #endif + } + +#endif + +// ------------------------ +// ADC +// ------------------------ + +volatile bool MarlinHAL::adc_has_result = false; + +void MarlinHAL::adc_init() { + analogReadResolution(HAL_ADC_RESOLUTION); + ::adc_init(); + adc_fifo_setup(true, false, 1, false, false); + irq_set_exclusive_handler(ADC_IRQ_FIFO, adc_exclusive_handler); + irq_set_enabled(ADC_IRQ_FIFO, true); + adc_irq_set_enabled(true); +} + +void MarlinHAL::adc_enable(const pin_t pin) { + if (pin >= A0 && pin <= A3) + adc_gpio_init(pin); + else if (pin == HAL_ADC_MCU_TEMP_DUMMY_PIN) + adc_set_temp_sensor_enabled(true); +} + +void MarlinHAL::adc_start(const pin_t pin) { + adc_has_result = false; + // Select an ADC input. 0...3 are GPIOs 26...29 respectively. + adc_select_input(pin == HAL_ADC_MCU_TEMP_DUMMY_PIN ? 4 : pin - A0); + adc_run(true); +} + +void MarlinHAL::adc_exclusive_handler() { + adc_run(false); // Disable since we only want one result + irq_clear(ADC_IRQ_FIFO); // Clear the IRQ + + if (adc_fifo_get_level() >= 1) { + adc_result = adc_fifo_get(); // Pop the result + adc_fifo_drain(); + adc_has_result = true; // Signal the end of the conversion + } +} + +uint16_t MarlinHAL::adc_value() { return adc_result; } + +// Reset the system to initiate a firmware flash +void flashFirmware(const int16_t) { hal.reboot(); } + +extern "C" { + void * _sbrk(int incr); + extern unsigned int __bss_end__; // end of bss section +} + +// Return free memory between end of heap (or end bss) and whatever is current +int freeMemory() { + int free_memory, heap_end = (int)_sbrk(0); + return (int)&free_memory - (heap_end ?: (int)&__bss_end__); +} + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/HAL.h b/Marlin/src/HAL/RP2040/HAL.h new file mode 100644 index 0000000000..11472f72f5 --- /dev/null +++ b/Marlin/src/HAL/RP2040/HAL.h @@ -0,0 +1,193 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#define CPU_32_BIT + +#ifndef F_CPU + #define F_CPU (XOSC_MHZ * 1000000UL) +#endif + +#include "arduino_extras.h" +#include "../../core/macros.h" +#include "../shared/math_32bit.h" +#include "../shared/HAL_SPI.h" +#include "fastio.h" +#include "watchdog.h" + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_SD_HOST_DRIVE + #include "msc_sd.h" +#endif + +// +// Serial Ports +// + +#include "MarlinSerial.h" + +// ------------------------ +// Defines +// ------------------------ + +/** + * TODO: review this to return 1 for pins that are not analog input + */ +#ifndef analogInputToDigitalPin + #define analogInputToDigitalPin(p) (p) +#endif + +#define CRITICAL_SECTION_START() uint32_t primask = __get_PRIMASK(); __disable_irq() +#define CRITICAL_SECTION_END() if (!primask) __enable_irq() +#define cli() __disable_irq() +#define sei() __enable_irq() + +// ------------------------ +// Types +// ------------------------ + +template struct IFPIN { typedef R type; }; +template struct IFPIN { typedef L type; }; +typedef IFPIN::type pin_t; + +class libServo; +typedef libServo hal_servo_t; +#define PAUSE_SERVO_OUTPUT() libServo::pause_all_servos() +#define RESUME_SERVO_OUTPUT() libServo::resume_all_servos() + +// ------------------------ +// ADC +// ------------------------ + +#define HAL_ADC_VREF 3.3 +#ifdef ADC_RESOLUTION + #define HAL_ADC_RESOLUTION ADC_RESOLUTION +#else + #define HAL_ADC_RESOLUTION 12 +#endif +// ADC index 4 is the MCU temperature +#define HAL_ADC_MCU_TEMP_DUMMY_PIN 127 + +// +// Pin Mapping for M42, M43, M226 +// +#define GET_PIN_MAP_PIN(index) index +#define GET_PIN_MAP_INDEX(pin) pin +#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + +#ifndef PLATFORM_M997_SUPPORT + #define PLATFORM_M997_SUPPORT +#endif +void flashFirmware(const int16_t); + +// Maple Compatibility +typedef void (*systickCallback_t)(void); +void systick_attach_callback(systickCallback_t cb); +void HAL_SYSTICK_Callback(); + +extern volatile uint32_t systick_uptime_millis; + +#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment +#define PWM_FREQUENCY 1000 // Default PWM frequency when set_pwm_duty() is called without set_pwm_frequency() + +// ------------------------ +// Class Utilities +// ------------------------ + +int freeMemory(); + +// ------------------------ +// MarlinHAL Class +// ------------------------ + +class MarlinHAL { +public: + + // Earliest possible init, before setup() + MarlinHAL() {} + + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); + + static void init(); // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 + + // Interrupts + static bool isr_state() { return !__get_PRIMASK(); } + static void isr_on() { __enable_irq(); } + static void isr_off() { __disable_irq(); } + + static void delay_ms(const int ms) { delay(ms); } + + // Tasks, called from idle() + static void idletask() { TERN_(HAS_SD_HOST_DRIVE, tuh_task()); } + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + // Called by Temperature::init once at startup + static void adc_init(); + + // Called by Temperature::init for each sensor at startup + static void adc_enable(const pin_t pin); + + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static void adc_start(const pin_t pin); + + // This ADC runs a periodic task + static void adc_exclusive_handler(); + + // Is the ADC ready for reading? + static volatile bool adc_has_result; + static bool adc_ready() { return adc_has_result; } + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * Set the PWM duty cycle for the pin to the given value. + * Optionally invert the duty cycle [default = false] + * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255] + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); + + /** + * Set the frequency of the timer for the given pin as close as + * possible to the provided desired frequency. Internally calculate + * the required waveform generation mode, prescaler, and resolution + * values and set timer registers accordingly. + * NOTE that the frequency is applied to all pins on the timer (Ex OC3A, OC3B and OC3B) + * NOTE that there are limitations, particularly if using TIMER2. (see Configuration_adv.h -> FAST_PWM_FAN Settings) + */ + static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired); +}; diff --git a/Marlin/src/HAL/RP2040/HAL_MinSerial.cpp b/Marlin/src/HAL/RP2040/HAL_MinSerial.cpp new file mode 100644 index 0000000000..d829edff24 --- /dev/null +++ b/Marlin/src/HAL/RP2040/HAL_MinSerial.cpp @@ -0,0 +1,68 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(POSTMORTEM_DEBUGGING) + +#include "../shared/HAL_MinSerial.h" + +static void TXBegin() { + #if !WITHIN(SERIAL_PORT, -1, 2) + #warning "Using POSTMORTEM_DEBUGGING requires a physical U(S)ART hardware in case of severe error." + #warning "Disabling the severe error reporting feature currently because the used serial port is not a HW port." + #else + #if SERIAL_PORT == -1 + USBSerial.begin(BAUDRATE); + #elif SERIAL_PORT == 0 + USBSerial.begin(BAUDRATE); + #elif SERIAL_PORT == 1 + Serial1.begin(BAUDRATE); + #endif + #endif +} + +static void TX(char b){ + #if SERIAL_PORT == -1 + USBSerial + #elif SERIAL_PORT == 0 + USBSerial + #elif SERIAL_PORT == 1 + Serial1 + #endif + .write(b); +} + +// A SW memory barrier, to ensure GCC does not overoptimize loops +#define sw_barrier() __asm__ volatile("": : :"memory"); + + +void install_min_serial() { + HAL_min_serial_init = &TXBegin; + HAL_min_serial_out = &TX; +} + +#endif // POSTMORTEM_DEBUGGING +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/HAL_SPI.cpp b/Marlin/src/HAL/RP2040/HAL_SPI.cpp new file mode 100644 index 0000000000..c88b6d1e5b --- /dev/null +++ b/Marlin/src/HAL/RP2040/HAL_SPI.cpp @@ -0,0 +1,228 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfig.h" + +#include + +// ------------------------ +// Public Variables +// ------------------------ + +static SPISettings spiConfig; + +// ------------------------ +// Public functions +// ------------------------ + +#if ENABLED(SOFTWARE_SPI) + + // ------------------------ + // Software SPI + // ------------------------ + + #include "../shared/Delay.h" + + void spiBegin(void) { + OUT_WRITE(SD_SS_PIN, HIGH); + OUT_WRITE(SD_SCK_PIN, HIGH); + SET_INPUT(SD_MISO_PIN); + OUT_WRITE(SD_MOSI_PIN, HIGH); + } + + // Use function with compile-time value so we can actually reach the desired frequency + // Need to adjust this a little bit: on a 72MHz clock, we have 14ns/clock + // and we'll use ~3 cycles to jump to the method and going back, so it'll take ~40ns from the given clock here + #define CALLING_COST_NS (3U * 1000000000U) / (F_CPU) + void (*delaySPIFunc)(); + void delaySPI_125() { DELAY_NS(125 - CALLING_COST_NS); } + void delaySPI_250() { DELAY_NS(250 - CALLING_COST_NS); } + void delaySPI_500() { DELAY_NS(500 - CALLING_COST_NS); } + void delaySPI_1000() { DELAY_NS(1000 - CALLING_COST_NS); } + void delaySPI_2000() { DELAY_NS(2000 - CALLING_COST_NS); } + void delaySPI_4000() { DELAY_NS(4000 - CALLING_COST_NS); } + + void spiInit(uint8_t spiRate) { + // Use datarates Marlin uses + switch (spiRate) { + case SPI_FULL_SPEED: delaySPIFunc = &delaySPI_125; break; // desired: 8,000,000 actual: ~1.1M + case SPI_HALF_SPEED: delaySPIFunc = &delaySPI_125; break; // desired: 4,000,000 actual: ~1.1M + case SPI_QUARTER_SPEED:delaySPIFunc = &delaySPI_250; break; // desired: 2,000,000 actual: ~890K + case SPI_EIGHTH_SPEED: delaySPIFunc = &delaySPI_500; break; // desired: 1,000,000 actual: ~590K + case SPI_SPEED_5: delaySPIFunc = &delaySPI_1000; break; // desired: 500,000 actual: ~360K + case SPI_SPEED_6: delaySPIFunc = &delaySPI_2000; break; // desired: 250,000 actual: ~210K + default: delaySPIFunc = &delaySPI_4000; break; // desired: 125,000 actual: ~123K + } + SPI.begin(); + } + + // Begin SPI transaction, set clock, bit order, data mode + void spiBeginTransaction(uint32_t spiClock, uint8_t bitOrder, uint8_t dataMode) { /* do nothing */ } + + uint8_t HAL_SPI_RP2040_SpiTransfer_Mode_3(uint8_t b) { // using Mode 3 + for (uint8_t bits = 8; bits--;) { + WRITE(SD_SCK_PIN, LOW); + WRITE(SD_MOSI_PIN, b & 0x80); + + delaySPIFunc(); + WRITE(SD_SCK_PIN, HIGH); + delaySPIFunc(); + + b <<= 1; // little setup time + b |= (READ(SD_MISO_PIN) != 0); + } + DELAY_NS(125); + return b; + } + + // Soft SPI receive byte + uint8_t spiRec() { + hal.isr_off(); // No interrupts during byte receive + const uint8_t data = HAL_SPI_RP2040_SpiTransfer_Mode_3(0xFF); + hal.isr_on(); // Enable interrupts + return data; + } + + // Soft SPI read data + void spiRead(uint8_t *buf, uint16_t nbyte) { + for (uint16_t i = 0; i < nbyte; i++) + buf[i] = spiRec(); + } + + // Soft SPI send byte + void spiSend(uint8_t data) { + hal.isr_off(); // No interrupts during byte send + HAL_SPI_RP2040_SpiTransfer_Mode_3(data); // Don't care what is received + hal.isr_on(); // Enable interrupts + } + + // Soft SPI send block + void spiSendBlock(uint8_t token, const uint8_t *buf) { + spiSend(token); + for (uint16_t i = 0; i < 512; i++) + spiSend(buf[i]); + } + +#else + + // ------------------------ + // Hardware SPI + // ------------------------ + + /** + * VGPV SPI speed start and PCLK2/2, by default 108/2 = 54Mhz + */ + + /** + * @brief Begin SPI port setup + * + * @return Nothing + * + * @details Only configures SS pin since stm32duino creates and initialize the SPI object + */ + void spiBegin() { + #if PIN_EXISTS(SD_SS) + OUT_WRITE(SD_SS_PIN, HIGH); + #endif + } + + // Configure SPI for specified SPI speed + void spiInit(uint8_t spiRate) { + // Use datarates Marlin uses + uint32_t clock; + switch (spiRate) { + case SPI_FULL_SPEED: clock = 20000000; break; // 13.9mhz=20000000 6.75mhz=10000000 3.38mhz=5000000 .833mhz=1000000 + case SPI_HALF_SPEED: clock = 5000000; break; + case SPI_QUARTER_SPEED: clock = 2500000; break; + case SPI_EIGHTH_SPEED: clock = 1250000; break; + case SPI_SPEED_5: clock = 625000; break; + case SPI_SPEED_6: clock = 300000; break; + default: + clock = 4000000; // Default from the SPI library + } + spiConfig = SPISettings(clock, MSBFIRST, SPI_MODE0); + + //SPI.setMISO(SD_MISO_PIN); //todo: implement? bad interface + //SPI.setMOSI(SD_MOSI_PIN); + //SPI.setSCLK(SD_SCK_PIN); + + SPI.begin(); + } + + /** + * @brief Receives a single byte from the SPI port. + * + * @return Byte received + * + * @details + */ + uint8_t spiRec() { + uint8_t returnByte = SPI.transfer(0xFF); + return returnByte; + } + + /** + * @brief Receive a number of bytes from the SPI port to a buffer + * + * @param buf Pointer to starting address of buffer to write to. + * @param nbyte Number of bytes to receive. + * @return Nothing + * + * @details Uses DMA + */ + void spiRead(uint8_t *buf, uint16_t nbyte) { + if (nbyte == 0) return; + memset(buf, 0xFF, nbyte); + SPI.transfer(buf, nbyte); + } + + /** + * @brief Send a single byte on SPI port + * + * @param b Byte to send + * + * @details + */ + void spiSend(uint8_t b) { + SPI.transfer(b); + } + + /** + * @brief Write token and then write from 512 byte buffer to SPI (for SD card) + * + * @param buf Pointer with buffer start address + * @return Nothing + * + * @details Use DMA + */ + void spiSendBlock(uint8_t token, const uint8_t *buf) { + //uint8_t rxBuf[512]; + //SPI.transfer(token); + SPI.transfer((uint8_t*)buf, 512); //implement? bad interface + } + +#endif // SOFTWARE_SPI + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/MarlinSerial.cpp b/Marlin/src/HAL/RP2040/MarlinSerial.cpp new file mode 100644 index 0000000000..dd01edd830 --- /dev/null +++ b/Marlin/src/HAL/RP2040/MarlinSerial.cpp @@ -0,0 +1,39 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfig.h" +#include "MarlinSerial.h" + +#if ENABLED(EMERGENCY_PARSER) + #include "../../feature/e_parser.h" +#endif + +#define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) +#define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) +#if WITHIN(SERIAL_PORT, 0, 3) + IMPLEMENT_SERIAL(SERIAL_PORT); +#endif + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/MarlinSerial.h b/Marlin/src/HAL/RP2040/MarlinSerial.h new file mode 100644 index 0000000000..b0db3167fa --- /dev/null +++ b/Marlin/src/HAL/RP2040/MarlinSerial.h @@ -0,0 +1,50 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(EMERGENCY_PARSER) + #include "../../feature/e_parser.h" +#endif + +#include "../../core/serial_hook.h" + +typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; +extern DefaultSerial1 MSerial0; +typedef ForwardSerial1Class USBSerialType; +extern USBSerialType USBSerial; + +#define Serial0 Serial +#define _DECLARE_SERIAL(X) \ + typedef ForwardSerial1Class DefaultSerial##X; \ + extern DefaultSerial##X MSerial##X +#define DECLARE_SERIAL(X) _DECLARE_SERIAL(X) + +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 6 +#define USB_SERIAL_PORT(...) MSerial0 +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() +#endif diff --git a/Marlin/src/HAL/RP2040/README.md b/Marlin/src/HAL/RP2040/README.md new file mode 100644 index 0000000000..4f9f70b8c9 --- /dev/null +++ b/Marlin/src/HAL/RP2040/README.md @@ -0,0 +1 @@ +# RP2040 Hardware Interface diff --git a/Marlin/src/HAL/RP2040/Servo.cpp b/Marlin/src/HAL/RP2040/Servo.cpp new file mode 100644 index 0000000000..2b1b2a1744 --- /dev/null +++ b/Marlin/src/HAL/RP2040/Servo.cpp @@ -0,0 +1,93 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfig.h" + +#if HAS_SERVOS + +#include "Servo.h" + +static uint_fast8_t servoCount = 0; +static libServo *servos[NUM_SERVOS] = {0}; +constexpr millis_t servoDelay[] = SERVO_DELAY; +static_assert(COUNT(servoDelay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long."); + +libServo::libServo() +: delay(servoDelay[servoCount]), + was_attached_before_pause(false), + value_before_pause(0) +{ + servos[servoCount++] = this; +} + +int8_t libServo::attach(const int pin) { + if (servoCount >= MAX_SERVOS) return -1; + if (pin > 0) servo_pin = pin; + auto result = pico_servo.attach(servo_pin); + return result; +} + +int8_t libServo::attach(const int pin, const int min, const int max) { + if (servoCount >= MAX_SERVOS) return -1; + if (pin > 0) servo_pin = pin; + auto result = pico_servo.attach(servo_pin, min, max); + return result; +} + +void libServo::move(const int value) { + if (attach(0) >= 0) { + pico_servo.write(value); + safe_delay(delay); + TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach()); + } +} + +void libServo::pause() { + was_attached_before_pause = pico_servo.attached(); + if (was_attached_before_pause) { + value_before_pause = pico_servo.read(); + pico_servo.detach(); + } +} + +void libServo::resume() { + if (was_attached_before_pause) { + attach(); + move(value_before_pause); + } +} + +void libServo::pause_all_servos() { + for (auto& servo : servos) + if (servo) servo->pause(); +} + +void libServo::resume_all_servos() { + for (auto& servo : servos) + if (servo) servo->resume(); +} + +#endif // HAS_SERVOS +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/Servo.h b/Marlin/src/HAL/RP2040/Servo.h new file mode 100644 index 0000000000..031610fd1d --- /dev/null +++ b/Marlin/src/HAL/RP2040/Servo.h @@ -0,0 +1,77 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include + +#if 1 + +#include "../../core/millis_t.h" + +// Inherit and expand on the official library +class libServo { + public: + libServo(); + int8_t attach(const int pin = 0); // pin == 0 uses value from previous call + int8_t attach(const int pin, const int min, const int max); + void detach() { pico_servo.detach(); } + int read() { return pico_servo.read(); } + void move(const int value); + + void pause(); + void resume(); + + static void pause_all_servos(); + static void resume_all_servos(); + static void setInterruptPriority(uint32_t preemptPriority, uint32_t subPriority); + + private: + Servo pico_servo; + + int servo_pin = 0; + millis_t delay = 0; + + bool was_attached_before_pause; + int value_before_pause; +}; + +#else + +class libServo: public Servo { + public: + void move(const int value) { + constexpr uint16_t servo_delay[] = SERVO_DELAY; + static_assert(COUNT(servo_delay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM_SERVOS long."); + + if (attach(servo_info[servoIndex].Pin.nbr) >= 0) { // try to reattach + write(value); + safe_delay(servo_delay[servoIndex]); // delay to allow servo to reach position + TERN_(DEACTIVATE_SERVOS_AFTER_MOVE, detach()); + } + + } +}; + +class libServo; +typedef libServo hal_servo_t; + +#endif diff --git a/Marlin/src/HAL/RP2040/arduino_extras.cpp b/Marlin/src/HAL/RP2040/arduino_extras.cpp new file mode 100644 index 0000000000..cdc0a0abf2 --- /dev/null +++ b/Marlin/src/HAL/RP2040/arduino_extras.cpp @@ -0,0 +1,33 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#ifdef __PLAT_RP2040__ + +#include + +char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s) { + char format_string[20]; + snprintf(format_string, 20, "%%%d.%df", __width, __prec); + sprintf(__s, format_string, __val); + return __s; +} + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/arduino_extras.h b/Marlin/src/HAL/RP2040/arduino_extras.h new file mode 100644 index 0000000000..9794140212 --- /dev/null +++ b/Marlin/src/HAL/RP2040/arduino_extras.h @@ -0,0 +1,29 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// #include +// #include +// #include +// #include + +char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s); diff --git a/Marlin/src/HAL/RP2040/docs/rp2040-datasheet.pdf b/Marlin/src/HAL/RP2040/docs/rp2040-datasheet.pdf new file mode 100644 index 0000000000..4c5c9db2e3 Binary files /dev/null and b/Marlin/src/HAL/RP2040/docs/rp2040-datasheet.pdf differ diff --git a/Marlin/src/HAL/RP2040/eeprom/eeprom_flash.cpp b/Marlin/src/HAL/RP2040/eeprom/eeprom_flash.cpp new file mode 100644 index 0000000000..89e882d77b --- /dev/null +++ b/Marlin/src/HAL/RP2040/eeprom/eeprom_flash.cpp @@ -0,0 +1,88 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(FLASH_EEPROM_EMULATION) + +#include "../../shared/eeprom_api.h" + +// NOTE: The Bigtreetech SKR Pico has an onboard W25Q16 flash module + +// Use EEPROM.h for compatibility, for now. +#include + +static bool eeprom_data_written = false; + +#ifndef MARLIN_EEPROM_SIZE + #define MARLIN_EEPROM_SIZE size_t(E2END + 1) +#endif +size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; } + +bool PersistentStore::access_start() { + EEPROM.begin(); // Avoid EEPROM.h warning (do nothing) + eeprom_buffer_fill(); + return true; +} + +bool PersistentStore::access_finish() { + if (eeprom_data_written) { + TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT()); + hal.isr_off(); + eeprom_buffer_flush(); + hal.isr_on(); + TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT()); + eeprom_data_written = false; + } + return true; +} + +bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) { + while (size--) { + uint8_t v = *value; + if (v != eeprom_buffered_read_byte(pos)) { + eeprom_buffered_write_byte(pos, v); + eeprom_data_written = true; + } + crc16(crc, &v, 1); + pos++; + value++; + } + return false; +} + +bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) { + do { + const uint8_t c = eeprom_buffered_read_byte(pos); + if (writing) *value = c; + crc16(crc, &c, 1); + pos++; + value++; + } while (--size); + return false; +} + +#endif // FLASH_EEPROM_EMULATION +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/eeprom/eeprom_wired.cpp b/Marlin/src/HAL/RP2040/eeprom/eeprom_wired.cpp new file mode 100644 index 0000000000..7a5ca86c4c --- /dev/null +++ b/Marlin/src/HAL/RP2040/eeprom/eeprom_wired.cpp @@ -0,0 +1,79 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../../inc/MarlinConfig.h" + +#if USE_WIRED_EEPROM + +/** + * PersistentStore for Arduino-style EEPROM interface + * with simple implementations supplied by Marlin. + */ + +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" + +#ifndef MARLIN_EEPROM_SIZE + #define MARLIN_EEPROM_SIZE size_t(E2END + 1) +#endif +size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; } + +bool PersistentStore::access_start() { eeprom_init(); return true; } +bool PersistentStore::access_finish() { return true; } + +bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, uint16_t *crc) { + uint16_t written = 0; + while (size--) { + uint8_t v = *value; + uint8_t * const p = (uint8_t * const)pos; + if (v != eeprom_read_byte(p)) { // EEPROM has only ~100,000 write cycles, so only write bytes that have changed! + eeprom_write_byte(p, v); + if (++written & 0x7F) delay(2); else safe_delay(2); // Avoid triggering watchdog during long EEPROM writes + if (eeprom_read_byte(p) != v) { + SERIAL_ECHO_MSG(STR_ERR_EEPROM_WRITE); + return true; + } + } + crc16(crc, &v, 1); + pos++; + value++; + } + return false; +} + +bool PersistentStore::read_data(int &pos, uint8_t *value, size_t size, uint16_t *crc, const bool writing/*=true*/) { + do { + // Read from either external EEPROM, program flash or Backup SRAM + const uint8_t c = eeprom_read_byte((uint8_t*)pos); + if (writing) *value = c; + crc16(crc, &c, 1); + pos++; + value++; + } while (--size); + return false; +} + +#endif // USE_WIRED_EEPROM +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/endstop_interrupts.h b/Marlin/src/HAL/RP2040/endstop_interrupts.h new file mode 100644 index 0000000000..af538406b8 --- /dev/null +++ b/Marlin/src/HAL/RP2040/endstop_interrupts.h @@ -0,0 +1,60 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../../module/endstops.h" + +// One ISR for all EXT-Interrupts +void endstop_ISR() { endstops.update(); } + +void setup_endstop_interrupts() { + #define _ATTACH(P) attachInterrupt(digitalPinToInterrupt(P), endstop_ISR, CHANGE) + TERN_(USE_X_MAX, _ATTACH(X_MAX_PIN)); + TERN_(USE_X_MIN, _ATTACH(X_MIN_PIN)); + TERN_(USE_Y_MAX, _ATTACH(Y_MAX_PIN)); + TERN_(USE_Y_MIN, _ATTACH(Y_MIN_PIN)); + TERN_(USE_Z_MAX, _ATTACH(Z_MAX_PIN)); + TERN_(HAS_Z_MIN_PIN, _ATTACH(Z_MIN_PIN)); + TERN_(USE_X2_MAX, _ATTACH(X2_MAX_PIN)); + TERN_(USE_X2_MIN, _ATTACH(X2_MIN_PIN)); + TERN_(USE_Y2_MAX, _ATTACH(Y2_MAX_PIN)); + TERN_(USE_Y2_MIN, _ATTACH(Y2_MIN_PIN)); + TERN_(USE_Z2_MAX, _ATTACH(Z2_MAX_PIN)); + TERN_(USE_Z2_MIN, _ATTACH(Z2_MIN_PIN)); + TERN_(USE_Z3_MAX, _ATTACH(Z3_MAX_PIN)); + TERN_(USE_Z3_MIN, _ATTACH(Z3_MIN_PIN)); + TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); + TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); + TERN_(USE_Z_MIN_PROBE_PIN, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); + TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); + TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); + TERN_(USE_J_MIN, _ATTACH(J_MIN_PIN)); + TERN_(USE_K_MAX, _ATTACH(K_MAX_PIN)); + TERN_(USE_K_MIN, _ATTACH(K_MIN_PIN)); + TERN_(USE_U_MAX, _ATTACH(U_MAX_PIN)); + TERN_(USE_U_MIN, _ATTACH(U_MIN_PIN)); + TERN_(USE_V_MAX, _ATTACH(V_MAX_PIN)); + TERN_(USE_V_MIN, _ATTACH(V_MIN_PIN)); + TERN_(USE_W_MAX, _ATTACH(W_MAX_PIN)); + TERN_(USE_W_MIN, _ATTACH(W_MIN_PIN)); +} diff --git a/Marlin/src/HAL/RP2040/fast_pwm.cpp b/Marlin/src/HAL/RP2040/fast_pwm.cpp new file mode 100644 index 0000000000..1349a1d59e --- /dev/null +++ b/Marlin/src/HAL/RP2040/fast_pwm.cpp @@ -0,0 +1,43 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfigPre.h" + +#include "HAL.h" +#include "pinDefinitions.h" + +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { + analogWrite(pin, v); +} + +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + mbed::PwmOut* pwm = digitalPinToPwm(pin); + if (pwm != NULL) delete pwm; + pwm = new mbed::PwmOut(digitalPinToPinName(pin)); + digitalPinToPwm(pin) = pwm; + pwm->period_ms(1000 / f_desired); +} + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/fastio.cpp b/Marlin/src/HAL/RP2040/fastio.cpp new file mode 100644 index 0000000000..fa77106cef --- /dev/null +++ b/Marlin/src/HAL/RP2040/fastio.cpp @@ -0,0 +1,32 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfig.h" + +void FastIO_init() { + +} + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/fastio.h b/Marlin/src/HAL/RP2040/fastio.h new file mode 100644 index 0000000000..e84d2e7778 --- /dev/null +++ b/Marlin/src/HAL/RP2040/fastio.h @@ -0,0 +1,87 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * Fast I/O interfaces for RP2040 + * These use GPIO register access for fast port manipulation. + */ + +// ------------------------ +// Public Variables +// ------------------------ + + +// ------------------------ +// Public functions +// ------------------------ + +void FastIO_init(); // Must be called before using fast io macros +#define FASTIO_INIT() FastIO_init() + +// ------------------------ +// Defines +// ------------------------ + +#define _BV32(b) (1UL << (b)) + +#ifndef PWM + #define PWM OUTPUT +#endif + +#define _WRITE(IO, V) digitalWrite((IO), (V)) + +#define _READ(IO) digitalRead(IO) +#define _TOGGLE(IO) digitalWrite(IO, !digitalRead(IO)) + +#define _GET_MODE(IO) +#define _SET_MODE(IO,M) pinMode(IO, M) +#define _SET_OUTPUT(IO) pinMode(IO, OUTPUT) //!< Output Push Pull Mode & GPIO_NOPULL +#define _SET_OUTPUT_OD(IO) pinMode(IO, OUTPUT_OPEN_DRAIN) + +#define WRITE(IO,V) _WRITE(IO,V) +#define READ(IO) _READ(IO) +#define TOGGLE(IO) _TOGGLE(IO) + +#define OUT_WRITE(IO,V) do{ _SET_OUTPUT(IO); WRITE(IO,V); }while(0) +#define OUT_WRITE_OD(IO,V) do{ _SET_OUTPUT_OD(IO); WRITE(IO,V); }while(0) + +#define SET_INPUT(IO) _SET_MODE(IO, INPUT) //!< Input Floating Mode +#define SET_INPUT_PULLUP(IO) _SET_MODE(IO, INPUT_PULLUP) //!< Input with Pull-up activation +#define SET_INPUT_PULLDOWN(IO) _SET_MODE(IO, INPUT_PULLDOWN) //!< Input with Pull-down activation +#define SET_OUTPUT(IO) OUT_WRITE(IO, LOW) +#define SET_PWM(IO) _SET_MODE(IO, PWM) + +#define IS_INPUT(IO) +#define IS_OUTPUT(IO) + +#define PWM_PIN(P) true //digitalPinHasPWM(P) +#define NO_COMPILE_TIME_PWM + +// digitalRead/Write wrappers +#define extDigitalRead(IO) digitalRead(IO) +#define extDigitalWrite(IO,V) digitalWrite(IO,V) + +#undef I2C_SDA +#define I2C_SDA_PIN PIN_WIRE_SDA +#undef I2C_SCL +#define I2C_SCL_PIN PIN_WIRE_SCL diff --git a/Marlin/src/HAL/RP2040/inc/Conditionals_LCD.h b/Marlin/src/HAL/RP2040/inc/Conditionals_LCD.h new file mode 100644 index 0000000000..82f95a1035 --- /dev/null +++ b/Marlin/src/HAL/RP2040/inc/Conditionals_LCD.h @@ -0,0 +1,22 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once diff --git a/Marlin/src/HAL/RP2040/inc/Conditionals_adv.h b/Marlin/src/HAL/RP2040/inc/Conditionals_adv.h new file mode 100644 index 0000000000..b96b3baa64 --- /dev/null +++ b/Marlin/src/HAL/RP2040/inc/Conditionals_adv.h @@ -0,0 +1,35 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#if HAS_MEDIA && DISABLED(NO_SD_HOST_DRIVE) + #define HAS_SD_HOST_DRIVE 1 +#endif + +// Fix F_CPU not being a compile-time constant in RP2040 framework +#ifdef BOARD_F_CPU + #undef F_CPU + #define F_CPU BOARD_F_CPU +#endif + +// The Sensitive Pins array is not optimizable +#define RUNTIME_ONLY_ANALOG_TO_DIGITAL diff --git a/Marlin/src/HAL/RP2040/inc/Conditionals_post.h b/Marlin/src/HAL/RP2040/inc/Conditionals_post.h new file mode 100644 index 0000000000..ef7853b987 --- /dev/null +++ b/Marlin/src/HAL/RP2040/inc/Conditionals_post.h @@ -0,0 +1,29 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +// If no real or emulated EEPROM selected, fall back to SD emulation +#if USE_FALLBACK_EEPROM + #define SDCARD_EEPROM_EMULATION +#elif ANY(I2C_EEPROM, SPI_EEPROM) + #define USE_SHARED_EEPROM 1 +#endif diff --git a/Marlin/src/HAL/RP2040/inc/Conditionals_type.h b/Marlin/src/HAL/RP2040/inc/Conditionals_type.h new file mode 100644 index 0000000000..82f95a1035 --- /dev/null +++ b/Marlin/src/HAL/RP2040/inc/Conditionals_type.h @@ -0,0 +1,22 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once diff --git a/Marlin/src/HAL/RP2040/inc/SanityCheck.h b/Marlin/src/HAL/RP2040/inc/SanityCheck.h new file mode 100644 index 0000000000..e4b1d7ee8d --- /dev/null +++ b/Marlin/src/HAL/RP2040/inc/SanityCheck.h @@ -0,0 +1,60 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * Test RP2040-specific configuration values for errors at compile-time. + */ +//#if ENABLED(SPINDLE_LASER_PWM) && !(SPINDLE_LASER_PWM_PIN == 4 || SPINDLE_LASER_PWM_PIN == 6 || SPINDLE_LASER_PWM_PIN == 11) +// #error "SPINDLE_LASER_PWM_PIN must use SERVO0, SERVO1 or SERVO3 connector" +//#endif + +#if ENABLED(SDCARD_EEPROM_EMULATION) && !HAS_MEDIA + #undef SDCARD_EEPROM_EMULATION // Avoid additional error noise + #if USE_FALLBACK_EEPROM + #warning "EEPROM type not specified. Fallback is SDCARD_EEPROM_EMULATION." + #endif + #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation." +#endif + +#if ENABLED(SRAM_EEPROM_EMULATION) + #error "SRAM_EEPROM_EMULATION is not supported for RP2040." +#endif + +#if ALL(PRINTCOUNTER, FLASH_EEPROM_EMULATION) + #warning "FLASH_EEPROM_EMULATION may cause long delays when writing and should not be used while printing." + #error "Disable PRINTCOUNTER or choose another EEPROM emulation." +#endif + +#if ENABLED(FLASH_EEPROM_LEVELING) + #error "FLASH_EEPROM_LEVELING is not supported for RP2040." +#endif + +#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) + #error "SERIAL_STATS_MAX_RX_QUEUED is not supported on RP2040." +#elif ENABLED(SERIAL_STATS_DROPPED_RX) + #error "SERIAL_STATS_DROPPED_RX is not supported on RP2040." +#endif + +#if ANY(TFT_COLOR_UI, TFT_LVGL_UI, TFT_CLASSIC_UI) + #error "TFT_COLOR_UI, TFT_LVGL_UI and TFT_CLASSIC_UI are not supported for RP2040." +#endif diff --git a/Marlin/src/HAL/RP2040/msc_sd.cpp b/Marlin/src/HAL/RP2040/msc_sd.cpp new file mode 100644 index 0000000000..b0de2241e5 --- /dev/null +++ b/Marlin/src/HAL/RP2040/msc_sd.cpp @@ -0,0 +1,104 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_SD_HOST_DRIVE + +#include "../../sd/cardreader.h" + +#include // TinyUSB device stack + +#define BLOCK_SIZE 512 +#define SD_MULTIBLOCK_RETRY_CNT 1 + +DiskIODriver* diskIODriver() { + #if HAS_MULTI_VOLUME + #if SHARED_VOLUME_IS(SD_ONBOARD) + return &card.media_driver_sdcard; + #elif SHARED_VOLUME_IS(USB_FLASH_DRIVE) + return &card.media_driver_usbFlash; + #endif + #else + return card.diskIODriver(); + #endif +} + +/** Callbacks used by TinyUSB MSC **/ + +extern "C" { + +bool tud_msc_ready_cb(uint8_t lun) { + return diskIODriver()->isReady(); +} + +int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, void* buffer, uint32_t bufsize) { + const uint32_t blocks = bufsize / BLOCK_SIZE; + for (uint16_t rcount = SD_MULTIBLOCK_RETRY_CNT; rcount--; ) { + if (diskIODriver()->readBlocks(lba, (uint8_t*)buffer, blocks)) + return bufsize; // Success + } + return -1; // Failure after retries +} + +int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint8_t const* buffer, uint32_t bufsize) { + const uint32_t blocks = bufsize / BLOCK_SIZE; + for (uint16_t rcount = SD_MULTIBLOCK_RETRY_CNT; rcount--; ) { + if (diskIODriver()->writeBlocks(lba, buffer, blocks)) + return bufsize; // Success + } + return -1; // Failure after retries +} + +void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) { + memcpy(vendor_id, "MARLIN ", 8); + memcpy(product_id, "Product ", 16); + memcpy(product_rev, "0.01", 4); +} + +void tud_msc_capacity_cb(uint8_t lun, uint32_t* block_count, uint16_t* block_size) { + *block_count = diskIODriver()->cardSize(); + *block_size = BLOCK_SIZE; +} + +void tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) { + if (load_eject) { + if (start) { + // Handle media load + } else { + // Handle media eject + } + } +} + +} // extern "C" + +void MSC_SD_init() { + tusb_init(); + // Add USB reinitialization logic if needed +} + +#endif // HAS_SD_HOST_DRIVE +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/msc_sd.h b/Marlin/src/HAL/RP2040/msc_sd.h new file mode 100644 index 0000000000..1c13f5578d --- /dev/null +++ b/Marlin/src/HAL/RP2040/msc_sd.h @@ -0,0 +1,24 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +void MSC_SD_init(); diff --git a/Marlin/src/HAL/RP2040/pinsDebug.h b/Marlin/src/HAL/RP2040/pinsDebug.h new file mode 100644 index 0000000000..f3842c4aff --- /dev/null +++ b/Marlin/src/HAL/RP2040/pinsDebug.h @@ -0,0 +1,146 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include +#include "HAL.h" + +#ifndef NUM_DIGITAL_PINS + #error "Expected NUM_DIGITAL_PINS not found" +#endif + +/** + * Life gets complicated if you want an easy to use 'M43 I' output (in port/pin order) + * because the variants in this platform do not always define all the I/O port/pins + * that a CPU has. + * + * VARIABLES: + * Ard_num - Arduino pin number - defined by the platform. It is used by digitalRead and + * digitalWrite commands and by M42. + * - does not contain port/pin info + * - is not in port/pin order + * - typically a variant will only assign Ard_num to port/pins that are actually used + * Index - M43 counter - only used to get Ard_num + * x - a parameter/argument used to search the pin_array to try to find a signal name + * associated with a Ard_num + * Port_pin - port number and pin number for use with CPU registers and printing reports + * + * Since M43 uses digitalRead and digitalWrite commands, only the Port_pins with an Ard_num + * are accessed and/or displayed. + * + * Three arrays are used. + * + * digitalPin[] is provided by the platform. It consists of the Port_pin numbers in + * Arduino pin number order. + * + * pin_array is a structure generated by the pins/pinsDebug.h header file. It is generated by + * the preprocessor. Only the signals associated with enabled options are in this table. + * It contains: + * - name of the signal + * - the Ard_num assigned by the pins_YOUR_BOARD.h file using the platform defines. + * EXAMPLE: "#define KILL_PIN PB1" results in Ard_num of 57. 57 is then used as the + * argument to digitalPinToPinName(IO) to get the Port_pin number + * - if it is a digital or analog signal. PWMs are considered digital here. + * + * pin_xref is a structure generated by this header file. It is generated by the + * preprocessor. It is in port/pin order. It contains just the port/pin numbers defined by the + * platform for this variant. + * - Ard_num + * - printable version of Port_pin + * + * Routines with an "x" as a parameter/argument are used to search the pin_array to try to + * find a signal name associated with a port/pin. + * + * NOTE - the Arduino pin number is what is used by the M42 command, NOT the port/pin for that + * signal. The Arduino pin number is listed by the M43 I command. + */ + +#define NUM_ANALOG_FIRST A0 + +#define MODE_PIN_INPUT 0 // Input mode (reset state) +#define MODE_PIN_OUTPUT 1 // General purpose output mode +#define MODE_PIN_ALT 2 // Alternate function mode +#define MODE_PIN_ANALOG 3 // Analog mode + +#define PIN_NUM(P) (P & 0x000F) +#define PIN_NUM_ALPHA_LEFT(P) (((P & 0x000F) < 10) ? ('0' + (P & 0x000F)) : '1') +#define PIN_NUM_ALPHA_RIGHT(P) (((P & 0x000F) > 9) ? ('0' + (P & 0x000F) - 10) : 0 ) +#define PORT_NUM(P) ((P >> 4) & 0x0007) +#define PORT_ALPHA(P) ('A' + (P >> 4)) + +/** + * Translation of routines & variables used by pinsDebug.h + */ +#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS +#define VALID_PIN(ANUM) (pin_t(ANUM) >= 0 && pin_t(ANUM) < NUMBER_PINS_TOTAL) +#define digitalRead_mod(Ard_num) extDigitalRead(Ard_num) // must use Arduino pin numbers when doing reads +#define PRINT_PIN(Q) +#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) +#define DIGITAL_PIN_TO_ANALOG_PIN(ANUM) -1 // will report analog pin number in the print port routine + +// x is a variable used to search pin_array +#define GET_ARRAY_IS_DIGITAL(x) ((bool) pin_array[x].is_digital) +#define GET_ARRAY_PIN(x) ((pin_t) pin_array[x].pin) +#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define MULTI_NAME_PAD 33 // space needed to be pretty if not first name assigned to a pin + +uint8_t get_pin_mode(const pin_t Ard_num) { + + uint dir = gpio_get_dir( Ard_num); + + if (dir) return MODE_PIN_OUTPUT; + else return MODE_PIN_INPUT; + +} + +bool getValidPinMode(const pin_t Ard_num) { + const uint8_t pin_mode = get_pin_mode(Ard_num); + return pin_mode == MODE_PIN_OUTPUT || pin_mode == MODE_PIN_ALT; // assume all alt definitions are PWM +} + +int8_t digital_pin_to_analog_pin(pin_t Ard_num) { + Ard_num -= NUM_ANALOG_FIRST; + return (Ard_num >= 0 && Ard_num < NUM_ANALOG_INPUTS) ? Ard_num : -1; +} + +bool isAnalogPin(const pin_t Ard_num) { + return digital_pin_to_analog_pin(Ard_num) != -1; +} + +bool is_digital(const pin_t x) { + const uint8_t pin_mode = get_pin_mode(x); + return pin_mode == MODE_PIN_INPUT || pin_mode == MODE_PIN_OUTPUT; +} + +void printPinPort(const pin_t Ard_num) { + SERIAL_ECHOPGM("Pin: "); + SERIAL_ECHO(Ard_num); +} + +bool pwm_status(const pin_t Ard_num) { + return get_pin_mode(Ard_num) == MODE_PIN_ALT; +} + +void printPinPWM(const pin_t Ard_num) { + if (PWM_PIN(Ard_num)) { + } +} diff --git a/Marlin/src/HAL/RP2040/spi_pins.h b/Marlin/src/HAL/RP2040/spi_pins.h new file mode 100644 index 0000000000..e6ee840b55 --- /dev/null +++ b/Marlin/src/HAL/RP2040/spi_pins.h @@ -0,0 +1,38 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * Define SPI Pins: SCK, MISO, MOSI, SS + */ +#ifndef SD_SCK_PIN + #define SD_SCK_PIN PIN_SPI_SCK +#endif +#ifndef SD_MISO_PIN + #define SD_MISO_PIN PIN_SPI_MISO +#endif +#ifndef SD_MOSI_PIN + #define SD_MOSI_PIN PIN_SPI_MOSI +#endif +#ifndef SD_SS_PIN + #define SD_SS_PIN PIN_SPI_SS +#endif diff --git a/Marlin/src/HAL/RP2040/timers.cpp b/Marlin/src/HAL/RP2040/timers.cpp new file mode 100644 index 0000000000..88d5af2983 --- /dev/null +++ b/Marlin/src/HAL/RP2040/timers.cpp @@ -0,0 +1,126 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../platforms.h" + +#ifdef __PLAT_RP2040__ + +#include "../../inc/MarlinConfig.h" + +alarm_pool_t* HAL_timer_pool_0; +alarm_pool_t* HAL_timer_pool_1; +alarm_pool_t* HAL_timer_pool_2; +alarm_pool_t* HAL_timer_pool_3; + +struct repeating_timer HAL_timer_0; +struct repeating_timer HAL_timer_1; +struct repeating_timer HAL_timer_2; +struct repeating_timer HAL_timer_3; + +volatile bool HAL_timer_irq_en[4] = { false, false, false, false }; + +void HAL_timer_init() { + //reserve all the available alarm pools to use as "pseudo" hardware timers + //HAL_timer_pool_0 = alarm_pool_create(0,2); + HAL_timer_pool_1 = alarm_pool_create(1, 6); + HAL_timer_pool_0 = HAL_timer_pool_1; + HAL_timer_pool_2 = alarm_pool_create(2, 6); + HAL_timer_pool_3 = HAL_timer_pool_2; + //HAL_timer_pool_3 = alarm_pool_create(3, 6); + + irq_set_priority(TIMER_IRQ_0, 0xC0); + irq_set_priority(TIMER_IRQ_1, 0x80); + irq_set_priority(TIMER_IRQ_2, 0x40); + irq_set_priority(TIMER_IRQ_3, 0x00); + + //alarm_pool_init_default(); +} + +void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { + const int64_t freq = (int64_t)frequency, + us = (1000000ll / freq) * -1ll; + bool result; + switch (timer_num) { + case 0: + result = alarm_pool_add_repeating_timer_us(HAL_timer_pool_0, us, HAL_timer_repeating_0_callback, NULL, &HAL_timer_0); + break; + case 1: + result = alarm_pool_add_repeating_timer_us(HAL_timer_pool_1, us, HAL_timer_repeating_1_callback, NULL, &HAL_timer_1); + break; + case 2: + result = alarm_pool_add_repeating_timer_us(HAL_timer_pool_2, us, HAL_timer_repeating_2_callback, NULL, &HAL_timer_2); + break; + case 3: + result = alarm_pool_add_repeating_timer_us(HAL_timer_pool_3, us, HAL_timer_repeating_3_callback, NULL, &HAL_timer_3); + break; + } + UNUSED(result); +} + +void HAL_timer_stop(const uint8_t timer_num) { + switch (timer_num) { + case 0: cancel_repeating_timer(&HAL_timer_0); break; + case 1: cancel_repeating_timer(&HAL_timer_1); break; + case 2: cancel_repeating_timer(&HAL_timer_2); break; + case 3: cancel_repeating_timer(&HAL_timer_3); break; + } +} + +int64_t HAL_timer_alarm_pool_0_callback(long int, void*) { + if (HAL_timer_irq_en[0]) HAL_timer_0_callback(); + return 0; +} +int64_t HAL_timer_alarm_pool_1_callback(long int, void*) { + if (HAL_timer_irq_en[1]) HAL_timer_1_callback(); + return 0; +} +int64_t HAL_timer_alarm_pool_2_callback(long int, void*) { + if (HAL_timer_irq_en[2]) HAL_timer_2_callback(); + return 0; +} +int64_t HAL_timer_alarm_pool_3_callback(long int, void*) { + if (HAL_timer_irq_en[3]) HAL_timer_3_callback(); + return 0; +} + +bool HAL_timer_repeating_0_callback(repeating_timer* timer) { + if (HAL_timer_irq_en[0]) HAL_timer_0_callback(); + return true; +} +bool HAL_timer_repeating_1_callback(repeating_timer* timer) { + if (HAL_timer_irq_en[1]) HAL_timer_1_callback(); + return true; +} +bool HAL_timer_repeating_2_callback(repeating_timer* timer) { + if (HAL_timer_irq_en[2]) HAL_timer_2_callback(); + return true; +} +bool HAL_timer_repeating_3_callback(repeating_timer* timer) { + if (HAL_timer_irq_en[3]) HAL_timer_3_callback(); + return true; +} + +void __attribute__((weak)) HAL_timer_0_callback() {} +void __attribute__((weak)) HAL_timer_1_callback() {} +void __attribute__((weak)) HAL_timer_2_callback() {} +void __attribute__((weak)) HAL_timer_3_callback() {} + +#endif // __PLAT_RP2040__ diff --git a/Marlin/src/HAL/RP2040/timers.h b/Marlin/src/HAL/RP2040/timers.h new file mode 100644 index 0000000000..4d11804361 --- /dev/null +++ b/Marlin/src/HAL/RP2040/timers.h @@ -0,0 +1,177 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include + +#include "../../core/macros.h" + +#ifdef PICO_TIME_DEFAULT_ALARM_POOL_DISABLED + #undef PICO_TIME_DEFAULT_ALARM_POOL_DISABLED + #define PICO_TIME_DEFAULT_ALARM_POOL_DISABLED 0 +#else + #define PICO_TIME_DEFAULT_ALARM_POOL_DISABLED 0 +#endif + +// ------------------------ +// Defines +// ------------------------ + +//#define _HAL_TIMER(T) _CAT(LPC_TIM, T) +//#define _HAL_TIMER_IRQ(T) TIMER##T##_IRQn +//#define __HAL_TIMER_ISR(T) extern "C" alarm_callback_t HAL_timer_alarm_pool_##T##_callback() +#define __HAL_TIMER_ISR(T) extern void HAL_timer_##T##_callback() +#define _HAL_TIMER_ISR(T) __HAL_TIMER_ISR(T) + +typedef uint64_t hal_timer_t; +#define HAL_TIMER_TYPE_MAX 0xFFFF'FFFF'FFFF'FFFFULL + +#define HAL_TIMER_RATE (1'000'000ULL) // fixed value as we use a microsecond timesource +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper +#endif +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP +#endif +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature +#endif +#ifndef MF_TIMER_PWM + #define MF_TIMER_PWM 3 // Timer Index for PWM +#endif + +#define TEMP_TIMER_RATE HAL_TIMER_RATE +#define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency + +#define STEPPER_TIMER_RATE HAL_TIMER_RATE / 10 // 100khz roughly +#define STEPPER_TIMER_TICKS_PER_US (0.1) // fixed value as we use a microsecond timesource +#define STEPPER_TIMER_PRESCALE (10) + +#define PULSE_TIMER_RATE STEPPER_TIMER_RATE // frequency of pulse timer +#define PULSE_TIMER_PRESCALE STEPPER_TIMER_PRESCALE +#define PULSE_TIMER_TICKS_PER_US STEPPER_TIMER_TICKS_PER_US + +#define ENABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_STEP) +#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_STEP) +#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP) + +#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP) +#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP) + +#ifndef HAL_STEP_TIMER_ISR + #define HAL_STEP_TIMER_ISR() _HAL_TIMER_ISR(MF_TIMER_STEP) +#endif +#ifndef HAL_TEMP_TIMER_ISR + #define HAL_TEMP_TIMER_ISR() _HAL_TIMER_ISR(MF_TIMER_TEMP) +#endif + +// Timer references by index +//#define STEP_TIMER_PTR _HAL_TIMER(MF_TIMER_STEP) +//#define TEMP_TIMER_PTR _HAL_TIMER(MF_TIMER_TEMP) + +extern alarm_pool_t* HAL_timer_pool_0; +extern alarm_pool_t* HAL_timer_pool_1; +extern alarm_pool_t* HAL_timer_pool_2; +extern alarm_pool_t* HAL_timer_pool_3; + +extern struct repeating_timer HAL_timer_0; + +void HAL_timer_0_callback(); +void HAL_timer_1_callback(); +void HAL_timer_2_callback(); +void HAL_timer_3_callback(); + +int64_t HAL_timer_alarm_pool_0_callback(long int, void*); +int64_t HAL_timer_alarm_pool_1_callback(long int, void*); +int64_t HAL_timer_alarm_pool_2_callback(long int, void*); +int64_t HAL_timer_alarm_pool_3_callback(long int, void*); + +bool HAL_timer_repeating_0_callback(repeating_timer* timer); +bool HAL_timer_repeating_1_callback(repeating_timer* timer); +bool HAL_timer_repeating_2_callback(repeating_timer* timer); +bool HAL_timer_repeating_3_callback(repeating_timer* timer); + +extern volatile bool HAL_timer_irq_en[4]; + +// ------------------------ +// Public functions +// ------------------------ + +void HAL_timer_init(); +void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); +void HAL_timer_stop(const uint8_t timer_num); + +FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, hal_timer_t compare) { + + if (timer_num == MF_TIMER_STEP){ + if (compare == HAL_TIMER_TYPE_MAX){ + HAL_timer_stop(timer_num); + return; + } + } + + compare = compare *10; //Dirty fix, figure out a proper way + + switch (timer_num) { + case 0: + alarm_pool_add_alarm_in_us(HAL_timer_pool_0, compare, HAL_timer_alarm_pool_0_callback, 0, false); + break; + + case 1: + alarm_pool_add_alarm_in_us(HAL_timer_pool_1, compare, HAL_timer_alarm_pool_1_callback, 0, false); + break; + + case 2: + alarm_pool_add_alarm_in_us(HAL_timer_pool_2, compare, HAL_timer_alarm_pool_2_callback, 0, false); + break; + + case 3: + alarm_pool_add_alarm_in_us(HAL_timer_pool_3, compare, HAL_timer_alarm_pool_3_callback, 0, false); + break; + } +} + +FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { + return 0; +} + +FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { + if (timer_num == MF_TIMER_STEP) return 0ull; + return time_us_64(); +} + + +FORCE_INLINE static void HAL_timer_enable_interrupt(const uint8_t timer_num) { + HAL_timer_irq_en[timer_num] = 1; +} + +FORCE_INLINE static void HAL_timer_disable_interrupt(const uint8_t timer_num) { + HAL_timer_irq_en[timer_num] = 0; +} + +FORCE_INLINE static bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { + return HAL_timer_irq_en[timer_num]; //lucky coincidence that timer_num and rp2040 irq num matches +} + +FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) { + return; +} + +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/SAMD21/HAL.cpp b/Marlin/src/HAL/SAMD21/HAL.cpp index 3656d97190..0a95366cc4 100644 --- a/Marlin/src/HAL/SAMD21/HAL.cpp +++ b/Marlin/src/HAL/SAMD21/HAL.cpp @@ -107,7 +107,7 @@ void MarlinHAL::init() { #if HAS_SD_DETECT && SD_CONNECTION_IS(ONBOARD) SET_INPUT_PULLUP(SD_DETECT_PIN); #endif - OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up + OUT_WRITE(SD_SS_PIN, HIGH); // Try to set SDSS inactive before any other SPI users start up #endif } diff --git a/Marlin/src/HAL/SAMD21/HAL.h b/Marlin/src/HAL/SAMD21/HAL.h index 95f391fa86..e95f0e6f70 100644 --- a/Marlin/src/HAL/SAMD21/HAL.h +++ b/Marlin/src/HAL/SAMD21/HAL.h @@ -36,11 +36,11 @@ // ------------------------ // Serial ports // ------------------------ + #include "../../core/serial_hook.h" typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1; extern DefaultSerial1 MSerialUSB; -// Serial ports typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; @@ -51,43 +51,10 @@ extern DefaultSerial3 MSerial1; #define _MSERIAL(X) __MSERIAL(X) #define MSERIAL(X) _MSERIAL(INCREMENT(X)) -#if WITHIN(SERIAL_PORT, 0, 1) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#elif SERIAL_PORT == -1 - #define MYSERIAL1 MSerialUSB -#else - #error "SERIAL_PORT must be -1 (Native USB only)." -#endif - -#ifdef SERIAL_PORT_2 - #if WITHIN(SERIAL_PORT_2, 0, 1) - #define MYSERIAL2 MSERIAL(SERIAL_PORT) - #elif SERIAL_PORT_2 == -1 - #define MYSERIAL2 MSerialUSB - #else - #error "SERIAL_PORT_2 must be -1 (Native USB only)." - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if WITHIN(MMU2_SERIAL_PORT, 0, 1) - #define MMU2_SERIAL MSERIAL(SERIAL_PORT) - #elif MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL MSerialUSB - #else - #error "MMU2_SERIAL_PORT must be -1 (Native USB only)." - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if WITHIN(LCD_SERIAL_PORT, 0, 1) - #define LCD_SERIAL MSERIAL(SERIAL_PORT) - #elif LCD_SERIAL_PORT == -1 - #define LCD_SERIAL MSerialUSB - #else - #error "LCD_SERIAL_PORT must be -1 (Native USB only)." - #endif -#endif +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 1 +#define USB_SERIAL_PORT(...) MSerialUSB +#include "../shared/serial_ports.h" typedef int8_t pin_t; diff --git a/Marlin/src/HAL/SAMD21/HAL_SPI.cpp b/Marlin/src/HAL/SAMD21/HAL_SPI.cpp index e01f540cf8..d41f868cdb 100644 --- a/Marlin/src/HAL/SAMD21/HAL_SPI.cpp +++ b/Marlin/src/HAL/SAMD21/HAL_SPI.cpp @@ -108,7 +108,6 @@ SPI.beginTransaction(spiConfig); SPI.transfer(buf, nbyte); SPI.endTransaction(); - } /** diff --git a/Marlin/src/HAL/SAMD21/QSPIFlash.cpp b/Marlin/src/HAL/SAMD21/eeprom/QSPIFlash.cpp similarity index 98% rename from Marlin/src/HAL/SAMD21/QSPIFlash.cpp rename to Marlin/src/HAL/SAMD21/eeprom/QSPIFlash.cpp index fa54c62071..2a93226a6f 100644 --- a/Marlin/src/HAL/SAMD21/QSPIFlash.cpp +++ b/Marlin/src/HAL/SAMD21/eeprom/QSPIFlash.cpp @@ -24,7 +24,7 @@ * SAMD21 HAL developed by Bart Meijer (brupje) * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician) */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(QSPI_EEPROM) diff --git a/Marlin/src/HAL/SAMD21/QSPIFlash.h b/Marlin/src/HAL/SAMD21/eeprom/QSPIFlash.h similarity index 100% rename from Marlin/src/HAL/SAMD21/QSPIFlash.h rename to Marlin/src/HAL/SAMD21/eeprom/QSPIFlash.h diff --git a/Marlin/src/HAL/SAMD21/eeprom_flash.cpp b/Marlin/src/HAL/SAMD21/eeprom/eeprom_flash.cpp similarity index 97% rename from Marlin/src/HAL/SAMD21/eeprom_flash.cpp rename to Marlin/src/HAL/SAMD21/eeprom/eeprom_flash.cpp index 66329bff19..0b5323cda4 100644 --- a/Marlin/src/HAL/SAMD21/eeprom_flash.cpp +++ b/Marlin/src/HAL/SAMD21/eeprom/eeprom_flash.cpp @@ -26,7 +26,7 @@ */ #ifdef __SAMD21__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) @@ -35,7 +35,7 @@ /* reserve flash memory */ static const uint8_t flashdata[TOTAL_FLASH_SIZE] __attribute__((__aligned__(256))) { }; \ -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE - eeprom_exclude_size; } @@ -83,7 +83,7 @@ bool PersistentStore::access_start() { NVMCTRL->CTRLA.reg = NVMCTRL_CTRLA_CMDEX_KEY | NVMCTRL_CTRLA_CMD_PBC; while (NVMCTRL->INTFLAG.bit.READY == 0) { } - PAGE_SIZE = pow(2,3 + NVMCTRL->PARAM.bit.PSZ); + PAGE_SIZE = POW(2, 3 + NVMCTRL->PARAM.bit.PSZ); ROW_SIZE= PAGE_SIZE * 4; /*NVMCTRL->SEECFG.reg = NVMCTRL_SEECFG_WMODE_BUFFERED; // Buffered mode and segment reallocation active if (NVMCTRL->SEESTAT.bit.RLOCK) diff --git a/Marlin/src/HAL/SAMD21/eeprom_qspi.cpp b/Marlin/src/HAL/SAMD21/eeprom/eeprom_qspi.cpp similarity index 96% rename from Marlin/src/HAL/SAMD21/eeprom_qspi.cpp rename to Marlin/src/HAL/SAMD21/eeprom/eeprom_qspi.cpp index 12977a178c..8bd1bd3539 100644 --- a/Marlin/src/HAL/SAMD21/eeprom_qspi.cpp +++ b/Marlin/src/HAL/SAMD21/eeprom/eeprom_qspi.cpp @@ -26,13 +26,13 @@ */ #ifdef __SAMD21__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(QSPI_EEPROM) #error "QSPI_EEPROM emulation Not implemented on SAMD21" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #include "QSPIFlash.h" diff --git a/Marlin/src/HAL/SAMD21/eeprom_wired.cpp b/Marlin/src/HAL/SAMD21/eeprom/eeprom_wired.cpp similarity index 95% rename from Marlin/src/HAL/SAMD21/eeprom_wired.cpp rename to Marlin/src/HAL/SAMD21/eeprom/eeprom_wired.cpp index da0eb1b0c8..82c701ebb1 100644 --- a/Marlin/src/HAL/SAMD21/eeprom_wired.cpp +++ b/Marlin/src/HAL/SAMD21/eeprom/eeprom_wired.cpp @@ -26,7 +26,7 @@ */ #ifdef __SAMD21__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -36,8 +36,8 @@ * with simple implementations supplied by Marlin. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM." diff --git a/Marlin/src/HAL/SAMD21/endstop_interrupts.h b/Marlin/src/HAL/SAMD21/endstop_interrupts.h index 37fdb7504b..4ef075f8f9 100644 --- a/Marlin/src/HAL/SAMD21/endstop_interrupts.h +++ b/Marlin/src/HAL/SAMD21/endstop_interrupts.h @@ -83,6 +83,7 @@ #define MATCH_Z4_MAX_EILINE(P) TERN0(USE_Z4_MAX, DEFER4(MATCH_EILINE)(P, Z4_MAX_PIN)) #define MATCH_Z4_MIN_EILINE(P) TERN0(USE_Z4_MIN, DEFER4(MATCH_EILINE)(P, Z4_MIN_PIN)) #define MATCH_Z_MIN_PROBE_EILINE(P) TERN0(USE_Z_MIN_PROBE, DEFER4(MATCH_EILINE)(P, Z_MIN_PROBE_PIN)) +#define MATCH_CALIBRATION_EILINE(P) TERN0(USE_CALIBRATION, DEFER4(MATCH_EILINE)(P, CALIBRATION_PIN)) #define AVAILABLE_EILINE(P) ( PIN_TO_EILINE(P) != -1 \ && !MATCH_X_MAX_EILINE(P) && !MATCH_X_MIN_EILINE(P) \ @@ -99,7 +100,8 @@ && !MATCH_Z2_MAX_EILINE(P) && !MATCH_Z2_MIN_EILINE(P) \ && !MATCH_Z3_MAX_EILINE(P) && !MATCH_Z3_MIN_EILINE(P) \ && !MATCH_Z4_MAX_EILINE(P) && !MATCH_Z4_MIN_EILINE(P) \ - && !MATCH_Z_MIN_PROBE_EILINE(P) ) + && !MATCH_Z_MIN_PROBE_EILINE(P) \ + && !MATCH_CALIBRATION_EILINE(P) ) // One ISR for all EXT-Interrupts void endstop_ISR() { endstops.update(); } @@ -208,6 +210,12 @@ void setup_endstop_interrupts() { #endif _ATTACH(Z_MIN_PROBE_PIN); #endif + #if USE_CALIBRATION + #if !AVAILABLE_EILINE(CALIBRATION_PIN) + #error "CALIBRATION_PIN has no EXTINT line available. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." + #endif + _ATTACH(CALIBRATION_PIN); + #endif #if USE_I_MAX #if !AVAILABLE_EILINE(I_MAX_PIN) #error "I_MAX_PIN has no EXTINT line available. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." diff --git a/Marlin/src/HAL/SAMD21/fastio.h b/Marlin/src/HAL/SAMD21/fastio.h index 471e8b62ab..32cc557528 100644 --- a/Marlin/src/HAL/SAMD21/fastio.h +++ b/Marlin/src/HAL/SAMD21/fastio.h @@ -152,7 +152,7 @@ : ((P) == 14) ? ADC_INPUTCTRL_MUXPOS_PIN14 \ : ADC_INPUTCTRL_MUXPOS_PIN15) -#define digitalPinToAnalogInput(P) (WITHIN(P, 67, 74) ? (P) - 67 : WITHIN(P, 54, 61) ? 8 + (P) - 54 : WITHIN(P, 12, 13) ? 16 + (P) - 12 : P == 9 ? 18 : -1) +#define digitalPinToAnalogIndex(P) (WITHIN(P, 67, 74) ? (P) - 67 : WITHIN(P, 54, 61) ? 8 + (P) - 54 : WITHIN(P, 12, 13) ? 16 + (P) - 12 : P == 9 ? 18 : -1) /** * pins diff --git a/Marlin/src/HAL/SAMD21/pinsDebug.h b/Marlin/src/HAL/SAMD21/pinsDebug.h index f94315cdf5..387516aa79 100644 --- a/Marlin/src/HAL/SAMD21/pinsDebug.h +++ b/Marlin/src/HAL/SAMD21/pinsDebug.h @@ -22,42 +22,57 @@ #pragma once /** - * SAMD21 HAL developed by Bart Meijer (brupje) - * Based on SAMD51 HAL by Giuliano Zaro (AKA GMagician) + * Pins Debugging for SAMD21 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL PINS_COUNT -#define digitalRead_mod(p) extDigitalRead(p) -#define PRINT_PORT(p) do{ SERIAL_ECHOPGM(" Port: "); sprintf_P(buffer, PSTR("%c%02ld"), 'A' + g_APinDescription[p].ulPort, g_APinDescription[p].ulPin); SERIAL_ECHO(buffer); }while (0) -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital -#define VALID_PIN(pin) (pin >= 0 && pin < (int8_t)NUMBER_PINS_TOTAL) -#define DIGITAL_PIN_TO_ANALOG_PIN(p) digitalPinToAnalogInput(p) -#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P)!=-1) -#define pwm_status(pin) digitalPinHasPWM(pin) +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3d "), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define getPinByIndex(x) pin_array[x].pin +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define isAnalogPin(P) (digitalPinToAnalogIndex(P) != -1) +#define pwm_status(P) digitalPinHasPWM(P) #define MULTI_NAME_PAD 27 // space needed to be pretty if not first name assigned to a pin // pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities // uses pin index #define M43_NEVER_TOUCH(Q) ((Q) >= 75) -bool GET_PINMODE(int8_t pin) { // 1: output, 0: input +bool getValidPinMode(const int8_t pin) { // 1: output, 0: input const EPortType samdport = g_APinDescription[pin].ulPort; const uint32_t samdpin = g_APinDescription[pin].ulPin; return PORT->Group[samdport].DIR.reg & MASK(samdpin) || (PORT->Group[samdport].PINCFG[samdpin].reg & (PORT_PINCFG_INEN | PORT_PINCFG_PULLEN)) == PORT_PINCFG_PULLEN; } -void pwm_details(int32_t pin) { +void printPinPWM(const int32_t pin) { if (pwm_status(pin)) { //uint32_t chan = g_APinDescription[pin].ulPWMChannel TODO when fast pwm is operative; //SERIAL_ECHOPGM("PWM = ", duty); } } +void printPinPort(const pin_t) {} + /** * SAMD21 Board pin| PORT | Label * ----------------+--------+------- diff --git a/Marlin/src/HAL/SAMD21/spi_pins.h b/Marlin/src/HAL/SAMD21/spi_pins.h index 8c25b84dc1..1186c3de48 100644 --- a/Marlin/src/HAL/SAMD21/spi_pins.h +++ b/Marlin/src/HAL/SAMD21/spi_pins.h @@ -45,10 +45,3 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN 37 #endif -#ifndef SDSS - #define SDSS 18 -#endif - -#ifndef SD_SS_PIN - #define SD_SS_PIN SDSS -#endif diff --git a/Marlin/src/HAL/SAMD21/timers.cpp b/Marlin/src/HAL/SAMD21/timers.cpp index b5f1d4f7bd..4ec6e5d867 100644 --- a/Marlin/src/HAL/SAMD21/timers.cpp +++ b/Marlin/src/HAL/SAMD21/timers.cpp @@ -176,7 +176,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { tc->COUNT32.CTRLA.reg |= TC_CTRLA_WAVEGEN_MFRQ; //set prescaler //the clock normally counts at the GCLK_TC frequency, but we can set it to divide that frequency to slow it down - //you can use different prescaler divisons here like TC_CTRLA_PRESCALER_DIV1 to get a different range + //you can use different prescaler divisions here like TC_CTRLA_PRESCALER_DIV1 to get a different range tc->COUNT32.CTRLA.reg |= TC_CTRLA_PRESCALER_DIV1 | TC_CTRLA_ENABLE; //it will divide GCLK_TC frequency by 1024 //set the compare-capture register. //The counter will count up to this value (it's a 16bit counter so we use uint16_t) diff --git a/Marlin/src/HAL/SAMD21/timers.h b/Marlin/src/HAL/SAMD21/timers.h index 303ccbdc50..a4faabb8e8 100644 --- a/Marlin/src/HAL/SAMD21/timers.h +++ b/Marlin/src/HAL/SAMD21/timers.h @@ -33,7 +33,7 @@ // -------------------------------------------------------------------------- typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define HAL_TIMER_RATE F_CPU // frequency of timers peripherals diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c index f9f77825f6..b2079fef9b 100644 --- a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c +++ b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.c @@ -32,7 +32,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ #ifdef __SAMD21__ diff --git a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h index 92626552b0..a4bf2800fe 100644 --- a/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h +++ b/Marlin/src/HAL/SAMD21/u8g/LCD_pin_routines.h @@ -33,7 +33,7 @@ * * Couldn't just call exact copies because the overhead killed the LCD update speed * With an intermediate level the softspi was running in the 10-20kHz range which - * resulted in using about about 25% of the CPU's time. + * resulted in using about 25% of the CPU's time. */ void u8g_SetPinOutput(uint8_t internal_pin_number); diff --git a/Marlin/src/HAL/SAMD51/HAL.cpp b/Marlin/src/HAL/SAMD51/HAL.cpp index a3c871ce51..aea908707b 100644 --- a/Marlin/src/HAL/SAMD51/HAL.cpp +++ b/Marlin/src/HAL/SAMD51/HAL.cpp @@ -61,7 +61,8 @@ #define GET_COOLER_ADC() TERN(HAS_TEMP_ADC_COOLER, PIN_TO_ADC(TEMP_COOLER_PIN), -1) #define GET_BOARD_ADC() TERN(HAS_TEMP_ADC_BOARD, PIN_TO_ADC(TEMP_BOARD_PIN), -1) #define GET_SOC_ADC() TERN(HAS_TEMP_ADC_BOARD, PIN_TO_ADC(TEMP_BOARD_PIN), -1) -#define GET_FILAMENT_WIDTH_ADC() TERN(FILAMENT_WIDTH_SENSOR, PIN_TO_ADC(FILWIDTH_PIN), -1) +#define GET_FILAMENT_WIDTH_ADC() TERN(HAS_FILWIDTH_ADC, PIN_TO_ADC(FILWIDTH_PIN), -1) +#define GET_FILAMENT2_WIDTH_ADC() TERN(HAS_FILWIDTH2_ADC, PIN_TO_ADC(FILWIDTH2_PIN), -1) #define GET_BUTTONS_ADC() TERN(HAS_ADC_BUTTONS, PIN_TO_ADC(ADC_KEYPAD_PIN), -1) #define GET_JOY_ADC_X() TERN(HAS_JOY_ADC_X, PIN_TO_ADC(JOY_X_PIN), -1) #define GET_JOY_ADC_Y() TERN(HAS_JOY_ADC_Y, PIN_TO_ADC(JOY_Y_PIN), -1) @@ -77,7 +78,7 @@ || GET_PROBE_ADC() == n \ || GET_COOLER_ADC() == n \ || GET_BOARD_ADC() == n || GET_SOC_ADC() == n \ - || GET_FILAMENT_WIDTH_ADC() == n \ + || GET_FILAMENT_WIDTH_ADC() == n || GET_FILAMENT2_WIDTH_ADC() == n \ || GET_BUTTONS_ADC() == n \ || GET_JOY_ADC_X() == n || GET_JOY_ADC_Y() == n || GET_JOY_ADC_Z() == n \ || GET_POWERMON_ADC_CURRENT() == n || GET_POWERMON_ADC_VOLTS() == n \ @@ -146,6 +147,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 0 FILWIDTH, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 0 + FILWIDTH2, + #endif #if GET_BUTTONS_ADC() == 0 ADC_KEY, #endif @@ -212,6 +216,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 1 FILWIDTH, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 1 + FILWIDTH2, + #endif #if GET_BUTTONS_ADC() == 1 ADC_KEY, #endif @@ -334,6 +341,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 0 FILWIDTH_PIN, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 0 + FILWIDTH2_PIN, + #endif #if GET_BUTTONS_ADC() == 0 ADC_KEYPAD_PIN, #endif @@ -400,6 +410,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 1 FILWIDTH_PIN, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 1 + FILWIDTH2_PIN, + #endif #if GET_BUTTONS_ADC() == 1 ADC_KEYPAD_PIN, #endif @@ -471,6 +484,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 0 { PIN_TO_INPUTCTRL(FILWIDTH_PIN) }, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 0 + { PIN_TO_INPUTCTRL(FILWIDTH2_PIN) }, + #endif #if GET_BUTTONS_ADC() == 0 { PIN_TO_INPUTCTRL(ADC_KEYPAD_PIN) }, #endif @@ -543,6 +559,9 @@ enum ADCIndex { #if GET_FILAMENT_WIDTH_ADC() == 1 { PIN_TO_INPUTCTRL(FILWIDTH_PIN) }, #endif + #if GET_FILAMENT2_WIDTH_ADC() == 1 + { PIN_TO_INPUTCTRL(FILWIDTH2_PIN) }, + #endif #if GET_BUTTONS_ADC() == 1 { PIN_TO_INPUTCTRL(ADC_KEYPAD_PIN) }, #endif @@ -669,7 +688,7 @@ void MarlinHAL::init() { #if HAS_SD_DETECT && SD_CONNECTION_IS(ONBOARD) SET_INPUT_PULLUP(SD_DETECT_PIN); #endif - OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up + OUT_WRITE(SD_SS_PIN, HIGH); // Try to set SDSS inactive before any other SPI users start up #endif } diff --git a/Marlin/src/HAL/SAMD51/HAL.h b/Marlin/src/HAL/SAMD51/HAL.h index c96401fd97..51fed64e35 100644 --- a/Marlin/src/HAL/SAMD51/HAL.h +++ b/Marlin/src/HAL/SAMD51/HAL.h @@ -34,62 +34,7 @@ #ifdef ADAFRUIT_GRAND_CENTRAL_M4 #include "MarlinSerial_AGCM4.h" - - // Serial ports - typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; - typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; - typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; - typedef ForwardSerial1Class< decltype(Serial3) > DefaultSerial4; - typedef ForwardSerial1Class< decltype(Serial4) > DefaultSerial5; - extern DefaultSerial1 MSerial0; - extern DefaultSerial2 MSerial1; - extern DefaultSerial3 MSerial2; - extern DefaultSerial4 MSerial3; - extern DefaultSerial5 MSerial4; - - #define __MSERIAL(X) MSerial##X - #define _MSERIAL(X) __MSERIAL(X) - #define MSERIAL(X) _MSERIAL(INCREMENT(X)) - - #if SERIAL_PORT == -1 - #define MYSERIAL1 MSerial0 - #elif WITHIN(SERIAL_PORT, 0, 3) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) - #else - #error "SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif - - #ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #define MYSERIAL2 MSerial0 - #elif WITHIN(SERIAL_PORT_2, 0, 3) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #error "SERIAL_PORT_2 must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif - #endif - - #ifdef MMU2_SERIAL_PORT - #if MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL MSerial0 - #elif WITHIN(MMU2_SERIAL_PORT, 0, 3) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #else - #error "MMU2_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif - #endif - - #ifdef LCD_SERIAL_PORT - #if LCD_SERIAL_PORT == -1 - #define LCD_SERIAL MSerial0 - #elif WITHIN(LCD_SERIAL_PORT, 0, 3) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #error "LCD_SERIAL_PORT must be from 0 to 3. You can also use -1 if the board supports Native USB." - #endif - #endif - -#endif // ADAFRUIT_GRAND_CENTRAL_M4 +#endif typedef int8_t pin_t; diff --git a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h index 1044d9fcd0..7827c1f958 100644 --- a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h +++ b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h @@ -32,3 +32,24 @@ typedef Serial1Class UartT; extern UartT Serial2; extern UartT Serial3; extern UartT Serial4; + +typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; +typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; +typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; +typedef ForwardSerial1Class< decltype(Serial3) > DefaultSerial4; +typedef ForwardSerial1Class< decltype(Serial4) > DefaultSerial5; + +extern DefaultSerial1 MSerial0; +extern DefaultSerial2 MSerial1; +extern DefaultSerial3 MSerial2; +extern DefaultSerial4 MSerial3; +extern DefaultSerial5 MSerial4; + +#define __MSERIAL(X) MSerial##X +#define _MSERIAL(X) __MSERIAL(X) +#define MSERIAL(X) _MSERIAL(INCREMENT(X)) + +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#define USB_SERIAL_PORT(...) MSerial0 +#include "../shared/serial_ports.h" diff --git a/Marlin/src/HAL/SAMD51/QSPIFlash.cpp b/Marlin/src/HAL/SAMD51/eeprom/QSPIFlash.cpp similarity index 98% rename from Marlin/src/HAL/SAMD51/QSPIFlash.cpp rename to Marlin/src/HAL/SAMD51/eeprom/QSPIFlash.cpp index fc21a1ad8c..191da1f30c 100644 --- a/Marlin/src/HAL/SAMD51/QSPIFlash.cpp +++ b/Marlin/src/HAL/SAMD51/eeprom/QSPIFlash.cpp @@ -20,7 +20,7 @@ * */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(QSPI_EEPROM) diff --git a/Marlin/src/HAL/SAMD51/QSPIFlash.h b/Marlin/src/HAL/SAMD51/eeprom/QSPIFlash.h similarity index 100% rename from Marlin/src/HAL/SAMD51/QSPIFlash.h rename to Marlin/src/HAL/SAMD51/eeprom/QSPIFlash.h diff --git a/Marlin/src/HAL/SAMD51/eeprom_flash.cpp b/Marlin/src/HAL/SAMD51/eeprom/eeprom_flash.cpp similarity index 97% rename from Marlin/src/HAL/SAMD51/eeprom_flash.cpp rename to Marlin/src/HAL/SAMD51/eeprom/eeprom_flash.cpp index 7d5518956c..2387a0f99e 100644 --- a/Marlin/src/HAL/SAMD51/eeprom_flash.cpp +++ b/Marlin/src/HAL/SAMD51/eeprom/eeprom_flash.cpp @@ -25,11 +25,11 @@ */ #ifdef __SAMD51__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #define NVMCTRL_CMD(c) do{ \ SYNC(!NVMCTRL->STATUS.bit.READY); \ diff --git a/Marlin/src/HAL/SAMD51/eeprom_qspi.cpp b/Marlin/src/HAL/SAMD51/eeprom/eeprom_qspi.cpp similarity index 96% rename from Marlin/src/HAL/SAMD51/eeprom_qspi.cpp rename to Marlin/src/HAL/SAMD51/eeprom/eeprom_qspi.cpp index a39e4c4fa3..e829c28e26 100644 --- a/Marlin/src/HAL/SAMD51/eeprom_qspi.cpp +++ b/Marlin/src/HAL/SAMD51/eeprom/eeprom_qspi.cpp @@ -25,11 +25,11 @@ */ #ifdef __SAMD51__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(QSPI_EEPROM) -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #include "QSPIFlash.h" diff --git a/Marlin/src/HAL/SAMD51/eeprom_wired.cpp b/Marlin/src/HAL/SAMD51/eeprom/eeprom_wired.cpp similarity index 95% rename from Marlin/src/HAL/SAMD51/eeprom_wired.cpp rename to Marlin/src/HAL/SAMD51/eeprom/eeprom_wired.cpp index 00a739a587..fc1eb09a0c 100644 --- a/Marlin/src/HAL/SAMD51/eeprom_wired.cpp +++ b/Marlin/src/HAL/SAMD51/eeprom/eeprom_wired.cpp @@ -25,7 +25,7 @@ */ #ifdef __SAMD51__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -34,8 +34,8 @@ * with simple implementations supplied by Marlin. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM." diff --git a/Marlin/src/HAL/SAMD51/endstop_interrupts.h b/Marlin/src/HAL/SAMD51/endstop_interrupts.h index 84b6c46da9..34c238ba5c 100644 --- a/Marlin/src/HAL/SAMD51/endstop_interrupts.h +++ b/Marlin/src/HAL/SAMD51/endstop_interrupts.h @@ -82,6 +82,7 @@ #define MATCH_Z4_MAX_EILINE(P) TERN0(USE_Z4_MAX, DEFER4(MATCH_EILINE)(P, Z4_MAX_PIN)) #define MATCH_Z4_MIN_EILINE(P) TERN0(USE_Z4_MIN, DEFER4(MATCH_EILINE)(P, Z4_MIN_PIN)) #define MATCH_Z_MIN_PROBE_EILINE(P) TERN0(USE_Z_MIN_PROBE, DEFER4(MATCH_EILINE)(P, Z_MIN_PROBE_PIN)) +#define MATCH_CALIBRATION_EILINE(P) TERN0(USE_CALIBRATION, DEFER4(MATCH_EILINE)(P, CALIBRATION_PIN)) #define AVAILABLE_EILINE(P) ( PIN_TO_EILINE(P) != -1 \ && !MATCH_X_MAX_EILINE(P) && !MATCH_X_MIN_EILINE(P) \ @@ -98,7 +99,8 @@ && !MATCH_Z2_MAX_EILINE(P) && !MATCH_Z2_MIN_EILINE(P) \ && !MATCH_Z3_MAX_EILINE(P) && !MATCH_Z3_MIN_EILINE(P) \ && !MATCH_Z4_MAX_EILINE(P) && !MATCH_Z4_MIN_EILINE(P) \ - && !MATCH_Z_MIN_PROBE_EILINE(P) ) + && !MATCH_Z_MIN_PROBE_EILINE(P) \ + && !MATCH_CALIBRATION_EILINE(P) ) // One ISR for all EXT-Interrupts void endstop_ISR() { endstops.update(); } @@ -183,6 +185,12 @@ void setup_endstop_interrupts() { #endif _ATTACH(Z_MIN_PROBE_PIN); #endif + #if USE_CALIBRATION + #if !AVAILABLE_EILINE(CALIBRATION_PIN) + #error "CALIBRATION_PIN has no EXTINT line available. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." + #endif + _ATTACH(CALIBRATION_PIN); + #endif #if USE_I_MAX #if !AVAILABLE_EILINE(I_MAX_PIN) #error "I_MAX_PIN has no EXTINT line available. Disable ENDSTOP_INTERRUPTS_FEATURE to continue." diff --git a/Marlin/src/HAL/SAMD51/fastio.h b/Marlin/src/HAL/SAMD51/fastio.h index 3d43bdb24d..1a67b6ce20 100644 --- a/Marlin/src/HAL/SAMD51/fastio.h +++ b/Marlin/src/HAL/SAMD51/fastio.h @@ -174,7 +174,7 @@ : (P == 17) ? PIN_TO_SAMD_PIN(13) \ : PIN_TO_SAMD_PIN(9)) - #define digitalPinToAnalogInput(P) (WITHIN(P, 67, 74) ? (P) - 67 : WITHIN(P, 54, 61) ? 8 + (P) - 54 : WITHIN(P, 12, 13) ? 16 + (P) - 12 : P == 9 ? 18 : -1) + #define digitalPinToAnalogIndex(P) (WITHIN(P, 67, 74) ? (P) - 67 : WITHIN(P, 54, 61) ? 8 + (P) - 54 : WITHIN(P, 12, 13) ? 16 + (P) - 12 : P == 9 ? 18 : -1) /** * pins diff --git a/Marlin/src/HAL/SAMD51/pinsDebug.h b/Marlin/src/HAL/SAMD51/pinsDebug.h index 94f91c77bc..1518c615c9 100644 --- a/Marlin/src/HAL/SAMD51/pinsDebug.h +++ b/Marlin/src/HAL/SAMD51/pinsDebug.h @@ -22,41 +22,57 @@ #pragma once /** - * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician) + * Pins Debugging for SAMD51 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL PINS_COUNT -#define digitalRead_mod(p) extDigitalRead(p) -#define PRINT_PORT(p) do{ SERIAL_ECHOPGM(" Port: "); sprintf_P(buffer, PSTR("%c%02ld"), 'A' + g_APinDescription[p].ulPort, g_APinDescription[p].ulPin); SERIAL_ECHO(buffer); }while (0) -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3d "), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital -#define VALID_PIN(pin) (pin >= 0 && pin < int8_t(NUMBER_PINS_TOTAL)) -#define DIGITAL_PIN_TO_ANALOG_PIN(p) digitalPinToAnalogInput(p) -#define IS_ANALOG(P) (DIGITAL_PIN_TO_ANALOG_PIN(P)!=-1) -#define pwm_status(pin) digitalPinHasPWM(pin) +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3d "), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define getPinByIndex(x) pin_array[x].pin +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define isAnalogPin(P) (digitalPinToAnalogIndex(P) != -1) +#define pwm_status(P) digitalPinHasPWM(P) #define MULTI_NAME_PAD 27 // space needed to be pretty if not first name assigned to a pin // pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities // uses pin index #define M43_NEVER_TOUCH(Q) ((Q) >= 75) -bool GET_PINMODE(int8_t pin) { // 1: output, 0: input +bool getValidPinMode(const int8_t pin) { // 1: output, 0: input const EPortType samdport = g_APinDescription[pin].ulPort; const uint32_t samdpin = g_APinDescription[pin].ulPin; return PORT->Group[samdport].DIR.reg & MASK(samdpin) || (PORT->Group[samdport].PINCFG[samdpin].reg & (PORT_PINCFG_INEN | PORT_PINCFG_PULLEN)) == PORT_PINCFG_PULLEN; } -void pwm_details(int32_t pin) { +void printPinPWM(const int32_t pin) { if (pwm_status(pin)) { //uint32_t chan = g_APinDescription[pin].ulPWMChannel TODO when fast pwm is operative; //SERIAL_ECHOPGM("PWM = ", duty); } } +void printPinPort(const pin_t) {} + /** * AGCM4 Board pin | PORT | Label * ----------------+--------+------- diff --git a/Marlin/src/HAL/SAMD51/spi_pins.h b/Marlin/src/HAL/SAMD51/spi_pins.h index 2c7cbeb994..a2d5b72ec4 100644 --- a/Marlin/src/HAL/SAMD51/spi_pins.h +++ b/Marlin/src/HAL/SAMD51/spi_pins.h @@ -46,14 +46,9 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN 51 #endif - #ifndef SDSS - #define SDSS 53 - #endif #else #error "Unsupported board!" #endif - -#define SD_SS_PIN SDSS diff --git a/Marlin/src/HAL/SAMD51/timers.h b/Marlin/src/HAL/SAMD51/timers.h index 86c3241892..59817453aa 100644 --- a/Marlin/src/HAL/SAMD51/timers.h +++ b/Marlin/src/HAL/SAMD51/timers.h @@ -32,7 +32,7 @@ // -------------------------------------------------------------------------- typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define HAL_TIMER_RATE F_CPU // frequency of timers peripherals diff --git a/Marlin/src/HAL/STM32/HAL.cpp b/Marlin/src/HAL/STM32/HAL.cpp index 610bd0b243..b2ae10d0f1 100644 --- a/Marlin/src/HAL/STM32/HAL.cpp +++ b/Marlin/src/HAL/STM32/HAL.cpp @@ -43,8 +43,8 @@ #endif #if HAS_SD_HOST_DRIVE - #include "msc_sd.h" - #include "usbd_cdc_if.h" + #include "sd/msc_sd.h" + #include #endif // ------------------------ @@ -66,11 +66,11 @@ void MarlinHAL::init() { // Ensure F_CPU is a constant expression. // If the compiler breaks here, it means that delay code that should compute at compile time will not work. // So better safe than sorry here. - constexpr int cpuFreq = F_CPU; + constexpr unsigned int cpuFreq = F_CPU; UNUSED(cpuFreq); - #if HAS_MEDIA && DISABLED(ONBOARD_SDIO) && (defined(SDSS) && SDSS != -1) - OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up + #if HAS_MEDIA && DISABLED(ONBOARD_SDIO) && PIN_EXISTS(SD_SS) + OUT_WRITE(SD_SS_PIN, HIGH); // Try to set SDSS inactive before any other SPI users start up #endif #if PIN_EXISTS(LED) @@ -87,7 +87,7 @@ void MarlinHAL::init() { SetTimerInterruptPriorities(); - #if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC) + #if ENABLED(EMERGENCY_PARSER) && ANY(USBD_USE_CDC, USBD_USE_CDC_MSC) USB_Hook_init(); #endif @@ -97,7 +97,7 @@ void MarlinHAL::init() { #if PIN_EXISTS(USB_CONNECT) OUT_WRITE(USB_CONNECT_PIN, !USB_CONNECT_INVERTING); // USB clear connection - delay(1000); // Give OS time to notice + delay_ms(1000); // Give OS time to notice WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING); #endif } diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h index 276f031685..a8ef51bcfc 100644 --- a/Marlin/src/HAL/STM32/HAL.h +++ b/Marlin/src/HAL/STM32/HAL.h @@ -23,18 +23,13 @@ #define CPU_32_BIT -#include "../../core/macros.h" -#include "../shared/Marduino.h" +#include "../../inc/MarlinConfigPre.h" + #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "temp_soc.h" #include "fastio.h" #include "Servo.h" -#include "MarlinSerial.h" - -#include "../../inc/MarlinConfigPre.h" - -#include // // Default graphical display delays @@ -43,85 +38,17 @@ #define CPU_ST7920_DELAY_2 40 #define CPU_ST7920_DELAY_3 340 -// ------------------------ -// Serial ports -// ------------------------ -#ifdef USBCON - #include - #include "../../core/serial_hook.h" - typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1; - extern DefaultSerial1 MSerialUSB; -#endif +// +// Serial Ports +// -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if WITHIN(SERIAL_PORT, 1, 9) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#elif !defined(USBCON) - #error "SERIAL_PORT must be from 1 to 9." -#elif SERIAL_PORT == -1 - #define MYSERIAL1 MSerialUSB -#else - #error "SERIAL_PORT must be from 1 to 9, or -1 for Native USB." -#endif - -#ifdef SERIAL_PORT_2 - #if WITHIN(SERIAL_PORT_2, 1, 9) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #elif !defined(USBCON) - #error "SERIAL_PORT_2 must be from 1 to 9." - #elif SERIAL_PORT_2 == -1 - #define MYSERIAL2 MSerialUSB - #else - #error "SERIAL_PORT_2 must be from 1 to 9, or -1 for Native USB." - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if WITHIN(SERIAL_PORT_3, 1, 9) - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #elif !defined(USBCON) - #error "SERIAL_PORT_3 must be from 1 to 9." - #elif SERIAL_PORT_3 == -1 - #define MYSERIAL3 MSerialUSB - #else - #error "SERIAL_PORT_3 must be from 1 to 9, or -1 for Native USB." - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if WITHIN(MMU2_SERIAL_PORT, 1, 9) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #elif !defined(USBCON) - #error "MMU2_SERIAL_PORT must be from 1 to 9." - #elif MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL MSerialUSB - #else - #error "MMU2_SERIAL_PORT must be from 1 to 9, or -1 for Native USB." - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if WITHIN(LCD_SERIAL_PORT, 1, 9) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #elif !defined(USBCON) - #error "LCD_SERIAL_PORT must be from 1 to 9." - #elif LCD_SERIAL_PORT == -1 - #define LCD_SERIAL MSerialUSB - #else - #error "LCD_SERIAL_PORT must be from 1 to 9, or -1 for Native USB." - #endif - #if HAS_DGUS_LCD - #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() - #endif -#endif +#include "MarlinSerial.h" /** * TODO: review this to return 1 for pins that are not analog input */ #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) (p) + #define analogInputToDigitalPin(p) pin_t(p) #endif // diff --git a/Marlin/src/HAL/STM32/HAL_SPI.cpp b/Marlin/src/HAL/STM32/HAL_SPI.cpp index cc035ecffa..b220ae6a78 100644 --- a/Marlin/src/HAL/STM32/HAL_SPI.cpp +++ b/Marlin/src/HAL/STM32/HAL_SPI.cpp @@ -37,7 +37,7 @@ static SPISettings spiConfig; // Public functions // ------------------------ -#if ENABLED(SOFTWARE_SPI) +#if ANY(SOFTWARE_SPI, FORCE_SOFT_SPI) // ------------------------ // Software SPI diff --git a/Marlin/src/HAL/STM32/HardwareSerial.cpp b/Marlin/src/HAL/STM32/HardwareSerial.cpp index 2a389447b7..58360cc31e 100644 --- a/Marlin/src/HAL/STM32/HardwareSerial.cpp +++ b/Marlin/src/HAL/STM32/HardwareSerial.cpp @@ -37,7 +37,17 @@ #include "HardwareSerial.h" #include "uart.h" -// USART/UART PIN MAPPING FOR STM32F0/F1/F2/F4/F7 +// Prevent selection of LPUART1 on STM32H7xx +#if defined(STM32H7xx) && (PIN_SERIAL1_TX == PA_9) + #undef PIN_SERIAL1_TX + #define PIN_SERIAL1_TX PA_9_ALT1 +#endif +#if defined(STM32H7xx) && (PIN_SERIAL1_RX == PA_10) + #undef PIN_SERIAL1_RX + #define PIN_SERIAL1_RX PA_10_ALT1 +#endif + +// USART/UART pin mapping for STM32F0/F1/F2/F4/F7/H7 #ifndef PIN_SERIAL1_TX #define PIN_SERIAL1_TX PA9 #endif @@ -75,46 +85,6 @@ #define PIN_SERIAL6_RX PC7 #endif -// TODO: Get from include file - -#if ANY(STM32F2xx, STM32F4xx, STM32F7xx) - - #define RCC_AHB1Periph_DMA1 ((uint32_t)0x00200000) - #define RCC_AHB1Periph_DMA2 ((uint32_t)0x00400000) - - void RCC_AHB1PeriphClockCmd(uint32_t RCC_AHB1Periph, FunctionalState NewState) { - // Check the parameters - assert_param(IS_RCC_AHB1_CLOCK_PERIPH(RCC_AHB1Periph)); - - assert_param(IS_FUNCTIONAL_STATE(NewState)); - if (NewState != DISABLE) - RCC->AHB1ENR |= RCC_AHB1Periph; - else - RCC->AHB1ENR &= ~RCC_AHB1Periph; - } - -#endif - -#if ANY(STM32F0xx, STM32F1xx) - - #define RCC_AHBPeriph_DMA1 ((uint32_t)0x00000001) - #define RCC_AHBPeriph_DMA2 ((uint32_t)0x00000002) - - void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) { - /* Check the parameters */ - assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph)); - assert_param(IS_FUNCTIONAL_STATE(NewState)); - - if (NewState != DISABLE) - RCC->AHBENR |= RCC_AHBPeriph; - else - RCC->AHBENR &= ~RCC_AHBPeriph; - } - -#endif - -// END OF TODO------------------------------------------------------ - // SerialEvent functions are weak, so when the user doesn't define them, // the linker just sets their address to 0 (which is checked below). #ifdef USING_HW_SERIAL1 @@ -161,11 +131,12 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { setRx(PIN_SERIAL1_RX); setTx(PIN_SERIAL1_TX); _uart_index = 0; - #ifdef DMA2_Stream2 - RX_DMA = { USART1, RCC_AHB1Periph_DMA2, 4, DMA2_Stream2 }; + + #ifdef DMA2_Stream2 // F2 / F4 / F7 / H7 + RX_DMA = { USART1, 2, DMA2_Stream2 }; // USART, DMA controller no., DMA stream #endif - #ifdef DMA1_Channel5 - RX_DMA = { USART1, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel5 }; + #ifdef DMA1_Channel5 // F0 / F1 + RX_DMA = { USART1, 1, DMA1_Channel5 }; // USART, DMA controller no., DMA channel #endif } else if (peripheral == USART2) { @@ -173,10 +144,10 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { setTx(PIN_SERIAL2_TX); _uart_index = 1; #ifdef DMA1_Stream5 - RX_DMA = { USART2, RCC_AHB1Periph_DMA1, 4, DMA1_Stream5 }; + RX_DMA = { USART2, 1, DMA1_Stream5 }; #endif #ifdef DMA1_Channel6 - RX_DMA = { USART2, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel6 }; + RX_DMA = { USART2, 1, DMA1_Channel6 }; #endif } else if (peripheral == USART3) { @@ -184,17 +155,17 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { setTx(PIN_SERIAL3_TX); _uart_index = 2; #ifdef DMA1_Stream1 - RX_DMA = { USART3, RCC_AHB1Periph_DMA1, 4, DMA1_Stream1 }; + RX_DMA = { USART3, 1, DMA1_Stream1 }; #endif #ifdef DMA1_Channel3 // F0 has no support for UART3, requires system remapping - RX_DMA = { USART3, RCC_AHBPeriph_DMA1, DMA1, DMA1_Channel3 }; + RX_DMA = { USART3, 1, DMA1_Channel3 }; #endif } #ifdef USART4 // Only F2 / F4 / F7 else if (peripheral == USART4) { #ifdef DMA1_Stream2 - RX_DMA = { USART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 }; + RX_DMA = { USART4, 1, DMA1_Stream2 }; #endif setRx(PIN_SERIAL4_RX); setTx(PIN_SERIAL4_TX); @@ -205,10 +176,10 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { #ifdef UART4 else if (peripheral == UART4) { #ifdef DMA1_Stream2 - RX_DMA = { UART4, RCC_AHB1Periph_DMA1, 4, DMA1_Stream2 }; + RX_DMA = { UART4, 1, DMA1_Stream2 }; #endif #ifdef DMA2_Channel3 // STM32F0xx has only 3 UARTs - RX_DMA = { UART4, RCC_AHBPeriph_DMA2, DMA2, DMA2_Channel3 }; + RX_DMA = { UART4, 2, DMA2_Channel3 }; #endif setRx(PIN_SERIAL4_RX); setTx(PIN_SERIAL4_TX); @@ -216,10 +187,10 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { } #endif - #ifdef UART5 // Only F2 / F4 / F7 + #ifdef UART5 // Only F2 / F4 / F7 / H7 else if (peripheral == UART5) { #ifdef DMA1_Stream0 - RX_DMA = { UART5, RCC_AHB1Periph_DMA1, 4, DMA1_Stream0 }; + RX_DMA = { UART5, 1, DMA1_Stream0 }; #endif setRx(PIN_SERIAL5_RX); setTx(PIN_SERIAL5_TX); @@ -227,10 +198,10 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { } #endif - #ifdef USART6 // Only F2 / F4 / F7 + #ifdef USART6 // Only F2 / F4 / F7 / H7 else if (peripheral == USART6) { #ifdef DMA2_Stream1 - RX_DMA = { USART6, RCC_AHB1Periph_DMA2, 4, DMA2_Stream1 }; + RX_DMA = { USART6, 2, DMA2_Stream1 }; #endif setRx(PIN_SERIAL6_RX); setTx(PIN_SERIAL6_TX); @@ -238,7 +209,7 @@ HAL_HardwareSerial::HAL_HardwareSerial(void *peripheral) { } #endif - else { // else get the pins of the first peripheral occurence in PinMap + else { // else get the pins of the first peripheral occurrence in PinMap _serial.pin_rx = pinmap_pin(peripheral, PinMap_UART_RX); _serial.pin_tx = pinmap_pin(peripheral, PinMap_UART_TX); } @@ -271,14 +242,34 @@ void HAL_HardwareSerial::init(PinName _rx, PinName _tx) { * @param obj : pointer to serial_t structure * @retval last character received */ -int HAL_HardwareSerial::_tx_complete_irq(serial_t *obj) { - // If interrupts are enabled, there must be more data in the output buffer. Send the next byte - obj->tx_tail = (obj->tx_tail + 1) % TX_BUFFER_SIZE; - if (obj->tx_head == obj->tx_tail) return -1; +#if DISABLED(STM32H7xx) - return 0; -} + int HAL_HardwareSerial::_tx_complete_irq(serial_t *obj) { + // If interrupts are enabled, there must be more data in the output buffer. Send the next byte + obj->tx_tail = (obj->tx_tail + 1) % TX_BUFFER_SIZE; + if (obj->tx_head == obj->tx_tail) + return -1; + + return 0; + } + +#else // STM32H7xx, has different uart_attach_tx_callback + + int HAL_HardwareSerial::_tx_complete_irq(serial_t *obj) { + // If interrupts are enabled, there must be more data in the output buffer. Send the next byte + obj->tx_tail = (obj->tx_tail + obj->tx_size) % TX_BUFFER_SIZE; + + if (obj->tx_head != obj->tx_tail) { + size_t remaining_data = (TX_BUFFER_SIZE + obj->tx_head - obj->tx_tail) % TX_BUFFER_SIZE; + obj->tx_size = min(remaining_data, (size_t)(TX_BUFFER_SIZE - obj->tx_tail)); + uart_attach_tx_callback(obj, _tx_complete_irq, obj->tx_size); + return -1; + } + return 0; + } + +#endif // Public Methods ////////////////////////////////////////////////////////////// @@ -340,7 +331,7 @@ void HAL_HardwareSerial::update_rx_head() { } #endif - #if ANY(STM32F2xx, STM32F4xx, STM32F7xx) + #if ANY(STM32F2xx, STM32F4xx, STM32F7xx, STM32H7xx) _serial.rx_head = RX_BUFFER_SIZE - RX_DMA.dma_streamRX->NDTR; #endif @@ -352,18 +343,22 @@ void HAL_HardwareSerial::update_rx_head() { int HAL_HardwareSerial::available() { update_rx_head(); + return ((unsigned int)(RX_BUFFER_SIZE + _serial.rx_head - _serial.rx_tail)) % RX_BUFFER_SIZE; } int HAL_HardwareSerial::peek() { update_rx_head(); - if (_serial.rx_head == _serial.rx_tail) return -1; + if (_serial.rx_head == _serial.rx_tail) + return -1; + return _serial.rx_buff[_serial.rx_tail]; } int HAL_HardwareSerial::read() { update_rx_head(); - if (_serial.rx_head == _serial.rx_tail) return -1; // No chars if the head isn't ahead of the tail + if (_serial.rx_head == _serial.rx_tail) + return -1; // No chars if the head isn't ahead of the tail unsigned char c = _serial.rx_buff[_serial.rx_tail]; _serial.rx_tail = (rx_buffer_index_t)(_serial.rx_tail + 1) % RX_BUFFER_SIZE; @@ -380,8 +375,18 @@ size_t HAL_HardwareSerial::write(uint8_t c) { // Interrupt based wri _serial.tx_buff[_serial.tx_head] = c; _serial.tx_head = i; - if (!serial_tx_active(&_serial)) - uart_attach_tx_callback(&_serial, _tx_complete_irq); // Write next byte, launch interrupt + #ifdef STM32H7xx // Support STM32H7xx with different uart_attach_tx_callback + if ((!serial_tx_active(&_serial)) && (_serial.tx_head != _serial.tx_tail)) { + size_t remaining_data = (TX_BUFFER_SIZE + _serial.tx_head -_serial.tx_tail) % TX_BUFFER_SIZE; + _serial.tx_size = min(remaining_data, (size_t)(TX_BUFFER_SIZE - _serial.tx_tail)); + uart_attach_tx_callback(&_serial, _tx_complete_irq, _serial.tx_size); + + return -1; + } + #else + if (!serial_tx_active(&_serial)) + uart_attach_tx_callback(&_serial, _tx_complete_irq); // Write next byte, launch interrupt + #endif return 1; } @@ -390,57 +395,132 @@ void HAL_HardwareSerial::flush() { while ((_serial.tx_head != _serial.tx_tail)) { /* nada */ } // nop, the interrupt handler will free up space for us } -#if ANY(STM32F2xx, STM32F4xx, STM32F7xx) +#if ANY(STM32F2xx, STM32F4xx, STM32F7xx, STM32H7xx) void HAL_HardwareSerial::Serial_DMA_Read_Enable() { - RCC_AHB1PeriphClockCmd(RX_DMA.dma_rcc, ENABLE); // Enable DMA clock - #ifdef STM32F7xx - RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->RDR); // RX peripheral receive address (usart) F7 + if (RX_DMA.DMA_ID == 1) + __HAL_RCC_DMA1_CLK_ENABLE(); // Enable DMA1 clock + else + __HAL_RCC_DMA2_CLK_ENABLE(); // Enable DMA2 clock + + // Reset DMA, wait if needed to complete the running process + RX_DMA.dma_streamRX->CR = 0; // DMA stream clear/disable + while (RX_DMA.dma_streamRX->CR & DMA_SxCR_EN) { /* just wait for DMA to complete */ } + + // UART clear/disable + RX_DMA.uart->CR1 = 0; + + // Configure DMA + #if ANY(STM32F7xx, STM32H7xx) // F7 and H7 use RDR (Receive Data Register) + RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->RDR); // DMA stream Peripheral Address Register = USART Data Register #else - RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->DR); // RX peripheral address (usart) F2 / F4 + RX_DMA.dma_streamRX->PAR = (uint32_t)(&RX_DMA.uart->DR); // DMA stream Peripheral Address Register = USART Data Register #endif - RX_DMA.dma_streamRX->M0AR = (uint32_t)_serial.rx_buff; // RX destination address (memory) - RX_DMA.dma_streamRX->NDTR = RX_BUFFER_SIZE; // RX buffer size - RX_DMA.dma_streamRX->CR = (RX_DMA.dma_channel << 25); // RX channel selection, set to 0 all the other CR bits + RX_DMA.dma_streamRX->M0AR = (uint32_t)_serial.rx_buff; // DMA stream Memory 0 Adress Register = RX buffer address + RX_DMA.dma_streamRX->NDTR = RX_BUFFER_SIZE; // DMA stream Number of Data Transfer Register - RX_DMA.dma_streamRX->CR |= (3 << 16); // RX priority level: Very High + #if DISABLED(STM32H7xx) // Select channel via CR register - //RX_DMA.dma_streamRX->CR &= ~(3 << 13); // RX memory data size: 8 bit - //RX_DMA.dma_streamRX->CR &= ~(3 << 11); // RX peripheral data size: 8 bit - RX_DMA.dma_streamRX->CR |= (1 << 10); // RX memory increment mode - //RX_DMA.dma_streamRX->CR &= ~(1 << 9); // RX peripheral no increment mode - RX_DMA.dma_streamRX->CR |= (1 << 8); // RX circular mode enabled - //RX_DMA.dma_streamRX->CR &= ~(1 << 6); // RX data transfer direction: Peripheral-to-memory - RX_DMA.uart->CR3 |= (1 << 6); // Enable DMA receiver (DMAR) - RX_DMA.dma_streamRX->CR |= (1 << 0); // RX enable DMA + RX_DMA.dma_streamRX->CR = 4 << DMA_SxCR_CHSEL_Pos; // DMA stream Channel Selection, always use channel 4 + + #else // STM32H7xx, select channel with DMAMUX1, channel DMA1 is channel DMAMUX, channel DMA2 is channel DMAMUX + 8 + + if (RX_DMA.uart == USART1) DMAMUX1_Channel10->CCR |= DMA_REQUEST_USART1_RX; // DMA2, Stream 2 + if (RX_DMA.uart == USART2) DMAMUX1_Channel5->CCR |= DMA_REQUEST_USART2_RX; // DMA1, Stream 5 + if (RX_DMA.uart == USART3) DMAMUX1_Channel1->CCR |= DMA_REQUEST_USART3_RX; // DMA1, Stream 1 + #ifdef UART4 + if (RX_DMA.uart == UART4) DMAMUX1_Channel2->CCR |= DMA_REQUEST_UART4_RX; // DMA1, Stream 2 + #endif + #ifdef USART4 + if (RX_DMA.uart == USART4) DMAMUX1_Channel2->CCR |= DMA_REQUEST_USART4_RX; // DMA1, Stream 2 + #endif + #ifdef UART5 + if (RX_DMA.uart == UART5) DMAMUX1_Channel0->CCR |= DMA_REQUEST_UART5_RX; // DMA1, Stream 0 + #endif + #ifdef USART6 + if (RX_DMA.uart == USART6) DMAMUX1_Channel9->CCR |= DMA_REQUEST_USART6_RX; // DMA2, Stream 1 + #endif + + #endif // !STM32H7xx + + // Configure DMA + //RX_DMA.dma_streamRX->CR |= DMA_MBURST_SINGLE; // DMA stream Memory Burst transfer: single transfer = 0b00 + //RX_DMA.dma_streamRX->CR |= DMA_PBURST_SINGLE; // DMA stream Peripheral Burst transfer: single transfer = 0b00 + + #if ENABLED(STM32H7xx) + RX_DMA.dma_streamRX->CR |= DMA_SxCR_TRBUFF; // DMA stream Transfer handle bufferable (required for UART/USART) + #endif + + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_CT; // DMA stream Current Target (only in double buffer mode) + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_DBM; // DMA stream Double Buffer Mode + //RX_DMA.dma_streamRX->CR |= DMA_PRIORITY_LOW; // DMA stream Priority Level Low = 0b00 + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_PINCOS; // DMA stream Peripheral Increment Offset Size + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_MSIZE; // DMA stream Memory data Size: 8 bit = 0b00 + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_PSIZE; // DMA stream Peripheral data Size: 8 bit = 0b00 + RX_DMA.dma_streamRX->CR |= DMA_SxCR_MINC; // DMA stream Memory Increment enable + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_PINC; // DMA stream Peripheral increment + RX_DMA.dma_streamRX->CR |= DMA_SxCR_CIRC; // DMA stream Circular mode enable + //RX_DMA.dma_streamRX->CR |= DMA_PERIPH_TO_MEMORY; // DMA stream transfer Direction: Peripheral-to-memory = b00 + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_PFCTRL; // DMA stream Peripheral Flow Controller: DMA = 0 + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_TCIE; // DMA stream Transfer Complete Interrupt + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_HTIE; // DMA stream Half Transfer Interrupt + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_TEIE; // DMA stream Transfer Error Interrupt + //RX_DMA.dma_streamRX->CR &= ~DMA_SxCR_DMEIE; // DMA stream Direct Mode Error Interrupt + RX_DMA.dma_streamRX->CR |= DMA_SxCR_EN; // DMA stream Enable + + // Configure UART/USART + RX_DMA.uart->CR3 |= USART_CR3_DMAR; // UART DMA Receiver + RX_DMA.uart->CR1 |= USART_CR1_TE; // UART Transmitter Enable + RX_DMA.uart->CR1 |= USART_CR1_RE; // UART Receiver Enable + RX_DMA.uart->CR1 |= USART_CR1_UE; // UART Enable } -#endif // STM32F2xx || STM32F4xx || STM32F7xx +#endif // STM32F2xx || STM32F4xx || STM32F7xx || STM32H7xx #if ANY(STM32F0xx, STM32F1xx) void HAL_HardwareSerial::Serial_DMA_Read_Enable() { - RCC_AHBPeriphClockCmd(RX_DMA.dma_rcc, ENABLE); // enable DMA clock - RX_DMA.dma_channelRX->CPAR = (uint32_t)(&RX_DMA.uart->DR); // RX peripheral address (usart) - RX_DMA.dma_channelRX->CMAR = (uint32_t)_serial.rx_buff; // RX destination address (memory) - RX_DMA.dma_channelRX->CNDTR = RX_BUFFER_SIZE; // RX buffer size + if (RX_DMA.DMA_ID == 1) + __HAL_RCC_DMA1_CLK_ENABLE(); // enable DMA1 clock + else + __HAL_RCC_DMA2_CLK_ENABLE(); // enable DMA2 clock - RX_DMA.dma_channelRX->CCR = 0; // RX channel selection, set to 0 all the other CR bits + RX_DMA.dma_channelRX->CCR &= ~USART_CR1_UE; // DMA stream clear/disable + while (RX_DMA.dma_channelRX->CCR & DMA_CCR_EN) { /* just wait for DMA to complete */ } - RX_DMA.dma_channelRX->CCR |= (3<<12); // RX priority level: Very High + // Clear/disable UART and DMA + RX_DMA.uart->CR1 = 0; // UART clear CR1, disable DMA - //RX_DMA.dma_channelRX->CCR &= ~(1<<10); // RX memory data size: 8 bit - //RX_DMA.dma_channelRX->CCR &= ~(1<<8); // RX peripheral data size: 8 bit - RX_DMA.dma_channelRX->CCR |= (1<<7); // RX memory increment mode - //RX_DMA.dma_channelRX->CCR &= ~(1<<6); // RX peripheral no increment mode - RX_DMA.dma_channelRX->CCR |= (1<<5); // RX circular mode enabled - //RX_DMA.dma_channelRX->CCR &= ~(1<<4); // RX data transfer direction: Peripheral-to-memory + // Configure DMA - RX_DMA.uart->CR3 |= (1<<6); // enable DMA receiver (DMAR) - RX_DMA.dma_channelRX->CCR |= (1<<0); // RX enable DMA + #ifdef STM32F0xx + RX_DMA.dma_channelRX->CPAR = (uint32_t)(&RX_DMA.uart->RDR); // DMA channel Peripheral Address Register = USART Data Register + #else + RX_DMA.dma_channelRX->CPAR = (uint32_t)(&RX_DMA.uart->DR); // DMA channel Peripheral Address Register = USART Data Register + #endif + + RX_DMA.dma_channelRX->CMAR = (uint32_t)_serial.rx_buff; // DMA channel Memory Address Register + RX_DMA.dma_channelRX->CNDTR = RX_BUFFER_SIZE; // DMA channel Number of Data Transfer Register + //RX_DMA.dma_channelRX->CCR |= (0b00 << DMA_CCR_PL_Pos); // DMA channel Priority Level: Low = 0b00 + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_MSIZE; // DMA channel Data Size: 8 bit = 0 + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_PSIZE; // DMA channel Peripheral data size: 8 bit = 0 + RX_DMA.dma_channelRX->CCR |= DMA_CCR_MINC; // DMA channel Memory Increment enable + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_PINC; // DMA channel Peripheral Increment disable + RX_DMA.dma_channelRX->CCR |= DMA_CCR_CIRC; // DMA channel Circular mode enable + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_DIR; // DMA channel Data Transfer direction: 0=Read peripheral, 1=Read memory + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_TEIE; // DMA channel Transfer Error Interrupt + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_HTIE; // DMA channel Half Transfer Interrupt + //RX_DMA.dma_channelRX->CCR &= ~DMA_CCR_TCIE; // DMA channel Transfer Complete Interrupt + RX_DMA.dma_channelRX->CCR |= DMA_CCR_EN; // DMA channel enable + + // Configure UART/USART + RX_DMA.uart->CR3 |= USART_CR3_DMAR; // UART DMA Receiver enabled + RX_DMA.uart->CR1 |= USART_CR1_TE; // UART Transmitter Enable + RX_DMA.uart->CR1 |= USART_CR1_RE; // UART Receiver Enable + RX_DMA.uart->CR1 |= USART_CR1_UE; // UART Enable } #endif // STM32F0xx || STM32F1xx diff --git a/Marlin/src/HAL/STM32/HardwareSerial.h b/Marlin/src/HAL/STM32/HardwareSerial.h index cc564322b4..cf97637278 100644 --- a/Marlin/src/HAL/STM32/HardwareSerial.h +++ b/Marlin/src/HAL/STM32/HardwareSerial.h @@ -38,12 +38,10 @@ typedef struct { USART_TypeDef * uart; - uint32_t dma_rcc; + uint32_t DMA_ID; // DMA1=1; DM2=2; BDMA=3 #if ANY(STM32F0xx, STM32F1xx) // F0 / F1 - DMA_TypeDef * dma_controller; DMA_Channel_TypeDef * dma_channelRX; - #else // F2 / F4 / F7 - uint32_t dma_channel; + #else // F2 / F4 / F7 / H7 DMA_Stream_TypeDef * dma_streamRX; #endif } DMA_CFG; diff --git a/Marlin/src/HAL/STM32/MarlinSerial.cpp b/Marlin/src/HAL/STM32/MarlinSerial.cpp index c4bc629949..862678373f 100644 --- a/Marlin/src/HAL/STM32/MarlinSerial.cpp +++ b/Marlin/src/HAL/STM32/MarlinSerial.cpp @@ -37,6 +37,9 @@ #ifndef USART5 #define USART5 UART5 #endif +#ifndef USART6 + #define USART6 UART6 +#endif #ifndef USART7 #define USART7 UART7 #endif diff --git a/Marlin/src/HAL/STM32/MarlinSerial.h b/Marlin/src/HAL/STM32/MarlinSerial.h index 4ab1e4e75a..73ab77d8d4 100644 --- a/Marlin/src/HAL/STM32/MarlinSerial.h +++ b/Marlin/src/HAL/STM32/MarlinSerial.h @@ -33,6 +33,21 @@ #include "../../core/serial_hook.h" +#ifdef USBCON + #include + typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1; + extern DefaultSerial1 MSerialUSB; + #define USB_SERIAL_PORT(...) MSerialUSB +#endif + +#define SERIAL_INDEX_MIN 1 +#define SERIAL_INDEX_MAX 9 +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() +#endif + #if ENABLED(SERIAL_DMA) struct MarlinSerial : public HAL_HardwareSerial { @@ -53,6 +68,7 @@ inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); } void _rx_complete_irq(serial_t *obj); + FORCE_INLINE static uint8_t buffer_overruns() { return 0; } // Not implemented. Void to avoid platform-dependent code. protected: usart_rx_callback_t _rx_callback; diff --git a/Marlin/src/HAL/STM32/README.md b/Marlin/src/HAL/STM32/README.md index 7680df6654..cf8aa50d50 100644 --- a/Marlin/src/HAL/STM32/README.md +++ b/Marlin/src/HAL/STM32/README.md @@ -3,9 +3,10 @@ This HAL is intended to act as the generic STM32 HAL for all STM32 chips (The whole F, H and L family). Currently it supports: - * STM32F0xx - * STM32F1xx - * STM32F4xx - * STM32F7xx + +- STM32F0xx +- STM32F1xx +- STM32F4xx +- STM32F7xx Targeting the official [Arduino STM32 Core](https://github.com/stm32duino/Arduino_Core_STM32). diff --git a/Marlin/src/HAL/STM32/Servo.cpp b/Marlin/src/HAL/STM32/Servo.cpp index 4f026ffc6d..eb535b1eb1 100644 --- a/Marlin/src/HAL/STM32/Servo.cpp +++ b/Marlin/src/HAL/STM32/Servo.cpp @@ -39,8 +39,8 @@ static_assert(COUNT(servoDelay) == NUM_SERVOS, "SERVO_DELAY must be an array NUM static uint32_t servo_interrupt_priority = NVIC_EncodePriority(NVIC_GetPriorityGrouping(), TIM_IRQ_PRIO, TIM_IRQ_SUBPRIO); // This must be called after the STM32 Servo class has initialized the timer. -// It may only be needed after the first call to attach(), but it is possible -// that is is necessary after every detach() call. To be safe this is currently +// It may only be needed after the first call to attach(), but it's possible +// that this is needed after every detach() call. To be safe this is currently // called after every call to attach(). static void fixServoTimerInterruptPriority() { NVIC_SetPriority(getTimerUpIrq(TIMER_SERVO), servo_interrupt_priority); diff --git a/Marlin/src/HAL/STM32/dogm/u8g_com_stm32duino_swspi.cpp b/Marlin/src/HAL/STM32/dogm/u8g_com_stm32duino_swspi.cpp new file mode 100644 index 0000000000..68c6430538 --- /dev/null +++ b/Marlin/src/HAL/STM32/dogm/u8g_com_stm32duino_swspi.cpp @@ -0,0 +1,136 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm / Ryan Power + * + * 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 3 of the License, 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. If not, see . + * + */ + +#ifdef HAL_STM32 + +#include "../../../inc/MarlinConfig.h" + +#if ALL(HAS_MARLINUI_U8GLIB, FORCE_SOFT_SPI) + +#include +#include "../../shared/HAL_SPI.h" + +#define nop asm volatile ("\tnop\n") + +static inline uint8_t swSpiTransfer_mode_0(uint8_t b) { + for (uint8_t i = 0; i < 8; ++i) { + const uint8_t state = (b & 0x80) ? HIGH : LOW; + WRITE(DOGLCD_SCK, HIGH); + WRITE(DOGLCD_MOSI, state); + b <<= 1; + WRITE(DOGLCD_SCK, LOW); + } + return b; +} + +static inline uint8_t swSpiTransfer_mode_3(uint8_t b) { + for (uint8_t i = 0; i < 8; ++i) { + const uint8_t state = (b & 0x80) ? HIGH : LOW; + WRITE(DOGLCD_SCK, LOW); + WRITE(DOGLCD_MOSI, state); + b <<= 1; + WRITE(DOGLCD_SCK, HIGH); + } + return b; +} + +static void u8g_sw_spi_shift_out(uint8_t val) { + #if U8G_SPI_USE_MODE_3 + swSpiTransfer_mode_3(val); + #else + swSpiTransfer_mode_0(val); + #endif +} + +static void swSpiInit() { + #if PIN_EXISTS(LCD_RESET) + SET_OUTPUT(LCD_RESET_PIN); + #endif + SET_OUTPUT(DOGLCD_A0); + OUT_WRITE(DOGLCD_SCK, LOW); + OUT_WRITE(DOGLCD_MOSI, LOW); + OUT_WRITE(DOGLCD_CS, HIGH); +} + +uint8_t u8g_com_HAL_STM32_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { + switch (msg) { + case U8G_COM_MSG_INIT: + swSpiInit(); + break; + + case U8G_COM_MSG_STOP: + break; + + case U8G_COM_MSG_RESET: + #if PIN_EXISTS(LCD_RESET) + WRITE(LCD_RESET_PIN, arg_val); + #endif + break; + + case U8G_COM_MSG_CHIP_SELECT: + #if U8G_SPI_USE_MODE_3 // This LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active + WRITE(DOGLCD_CS, LOW); + nop; // hold SCK high for a few ns + nop; + } + else { + WRITE(DOGLCD_CS, HIGH); + WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive + } + #else + WRITE(DOGLCD_CS, !arg_val); + #endif + break; + + case U8G_COM_MSG_WRITE_BYTE: + u8g_sw_spi_shift_out(arg_val); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t *ptr = (uint8_t *)arg_ptr; + while (arg_val > 0) { + u8g_sw_spi_shift_out(*ptr++); + arg_val--; + } + } break; + + case U8G_COM_MSG_WRITE_SEQ_P: { + uint8_t *ptr = (uint8_t *)arg_ptr; + while (arg_val > 0) { + u8g_sw_spi_shift_out(u8g_pgm_read(ptr)); + ptr++; + arg_val--; + } + } break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + WRITE(DOGLCD_A0, arg_val); + break; + } + return 1; +} + +#endif // HAS_MARLINUI_U8GLIB && FORCE_SOFT_SPI +#endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_bl24cxx.cpp similarity index 94% rename from Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_bl24cxx.cpp index 3e0bb58dad..8240f15d3a 100644 --- a/Marlin/src/HAL/STM32/eeprom_bl24cxx.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_bl24cxx.cpp @@ -20,7 +20,7 @@ * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 @@ -29,12 +29,12 @@ * with simple implementations supplied by Marlin. */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" // // PersistentStore diff --git a/Marlin/src/HAL/STM32/eeprom_flash.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_flash.cpp similarity index 79% rename from Marlin/src/HAL/STM32/eeprom_flash.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_flash.cpp index 37963ad501..5a9062e956 100644 --- a/Marlin/src/HAL/STM32/eeprom_flash.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_flash.cpp @@ -19,15 +19,15 @@ * along with this program. If not, see . * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" // Better: "utility/stm32_eeprom.h", but only after updating stm32duino to 2.0.0 // Use EEPROM.h for compatibility, for now. @@ -50,10 +50,10 @@ #if ENABLED(FLASH_EEPROM_LEVELING) - #include "stm32_def.h" + #include #define DEBUG_OUT ENABLED(EEPROM_CHITCHAT) - #include "../../core/debug_out.h" + #include "../../../core/debug_out.h" #ifndef MARLIN_EEPROM_SIZE #define MARLIN_EEPROM_SIZE 0x1000 // 4KB @@ -63,7 +63,7 @@ #define FLASH_SECTOR (FLASH_SECTOR_TOTAL - 1) #endif #ifndef FLASH_UNIT_SIZE - #define FLASH_UNIT_SIZE 0x20000 // 128kB + #define FLASH_UNIT_SIZE 0x20000 // 128K #endif #ifndef FLASH_ADDRESS_START @@ -74,13 +74,13 @@ #define EEPROM_SLOTS ((FLASH_UNIT_SIZE) / (MARLIN_EEPROM_SIZE)) #define SLOT_ADDRESS(slot) (FLASH_ADDRESS_START + (slot * (MARLIN_EEPROM_SIZE))) - #define UNLOCK_FLASH() if (!flash_unlocked) { \ - HAL_FLASH_Unlock(); \ - __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | \ - FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); \ - flash_unlocked = true; \ - } - #define LOCK_FLASH() if (flash_unlocked) { HAL_FLASH_Lock(); flash_unlocked = false; } + #ifdef STM32H7xx + #define FLASHWORD_SIZE 32U // STM32H7xx a FLASHWORD is 32 bytes (256 bits) + #define FLASH_FLAGS_TO_CLEAR (FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGSERR) + #else + #define FLASHWORD_SIZE 4U // STM32F4xx a FLASHWORD is 4 bytes sizeof(uint32_t) + #define FLASH_FLAGS_TO_CLEAR (FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR) + #endif #define EMPTY_UINT32 ((uint32_t)-1) #define EMPTY_UINT8 ((uint8_t)-1) @@ -88,7 +88,7 @@ static uint8_t ram_eeprom[MARLIN_EEPROM_SIZE] __attribute__((aligned(4))) = {0}; static int current_slot = -1; - static_assert(0 == MARLIN_EEPROM_SIZE % 4, "MARLIN_EEPROM_SIZE must be a multiple of 4"); // Ensure copying as uint32_t is safe + static_assert(0 == MARLIN_EEPROM_SIZE % FLASHWORD_SIZE, "MARLIN_EEPROM_SIZE must be a multiple of the FLASHWORD size"); // Ensure copying as uint32_t is safe static_assert(0 == FLASH_UNIT_SIZE % MARLIN_EEPROM_SIZE, "MARLIN_EEPROM_SIZE must divide evenly into your FLASH_UNIT_SIZE"); static_assert(FLASH_UNIT_SIZE >= MARLIN_EEPROM_SIZE, "FLASH_UNIT_SIZE must be greater than or equal to your MARLIN_EEPROM_SIZE"); static_assert(IS_FLASH_SECTOR(FLASH_SECTOR), "FLASH_SECTOR is invalid"); @@ -125,13 +125,13 @@ bool PersistentStore::access_start() { } if (current_slot == -1) { // We didn't find anything, so we'll just initialize to empty - for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = EMPTY_UINT8; + for (int i = 0; i < long(MARLIN_EEPROM_SIZE); i++) ram_eeprom[i] = EMPTY_UINT8; current_slot = EEPROM_SLOTS; } else { // load current settings uint8_t *eeprom_data = (uint8_t *)SLOT_ADDRESS(current_slot); - for (int i = 0; i < MARLIN_EEPROM_SIZE; i++) ram_eeprom[i] = eeprom_data[i]; + for (int i = 0; i < long(MARLIN_EEPROM_SIZE); i++) ram_eeprom[i] = eeprom_data[i]; DEBUG_ECHOLNPGM("EEPROM loaded from slot ", current_slot, "."); } eeprom_data_written = false; @@ -147,11 +147,15 @@ bool PersistentStore::access_start() { bool PersistentStore::access_finish() { if (eeprom_data_written) { + #ifdef STM32F4xx // MCU may come up with flash error bits which prevent some flash operations. // Clear flags prior to flash operations to prevent errors. __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR); #endif + #ifdef STM32H7xx + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGSERR); + #endif #if ENABLED(FLASH_EEPROM_LEVELING) @@ -170,7 +174,12 @@ bool PersistentStore::access_finish() { EraseInitStruct.NbSectors = 1; current_slot = EEPROM_SLOTS - 1; - UNLOCK_FLASH(); + + if (!flash_unlocked) { + HAL_FLASH_Unlock(); + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAGS_TO_CLEAR); + flash_unlocked = true; + } TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT()); hal.isr_off(); @@ -181,26 +190,37 @@ bool PersistentStore::access_finish() { DEBUG_ECHOLNPGM("HAL_FLASHEx_Erase=", status); DEBUG_ECHOLNPGM("GetError=", HAL_FLASH_GetError()); DEBUG_ECHOLNPGM("SectorError=", SectorError); - LOCK_FLASH(); + if (flash_unlocked) { + HAL_FLASH_Lock(); + flash_unlocked = false; + } return false; } } - UNLOCK_FLASH(); + if (!flash_unlocked) { + HAL_FLASH_Unlock(); + __HAL_FLASH_CLEAR_FLAG(FLASH_FLAGS_TO_CLEAR); + flash_unlocked = true; + } uint32_t offset = 0, address = SLOT_ADDRESS(current_slot), - address_end = address + MARLIN_EEPROM_SIZE, - data = 0; + address_end = address + MARLIN_EEPROM_SIZE; bool success = true; while (address < address_end) { - memcpy(&data, ram_eeprom + offset, sizeof(data)); - status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data); + #ifdef STM32H7xx + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, address, uint32_t(ram_eeprom + offset)); + #else + //memcpy(&data, ram_eeprom + offset, sizeof(data)); // IRON, IMPROVED + //status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, data); + status = HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address, *(uint32_t*)(ram_eeprom + offset)); // IRON, OPTIMIZED + #endif if (status == HAL_OK) { - address += sizeof(uint32_t); - offset += sizeof(uint32_t); + address += FLASHWORD_SIZE; + offset += FLASHWORD_SIZE; } else { DEBUG_ECHOLNPGM("HAL_FLASH_Program=", status); @@ -211,7 +231,10 @@ bool PersistentStore::access_finish() { } } - LOCK_FLASH(); + if (flash_unlocked) { + HAL_FLASH_Lock(); + flash_unlocked = false; + } if (success) { eeprom_data_written = false; diff --git a/Marlin/src/HAL/STM32/eeprom_if_iic.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_if_iic.cpp similarity index 88% rename from Marlin/src/HAL/STM32/eeprom_if_iic.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_if_iic.cpp index ad8712c0c0..2733c8f283 100644 --- a/Marlin/src/HAL/STM32/eeprom_if_iic.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_if_iic.cpp @@ -20,7 +20,7 @@ * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 @@ -29,12 +29,12 @@ * Enable USE_SHARED_EEPROM if not supplied by the framework. */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../../libs/BL24CXX.h" -#include "../shared/eeprom_if.h" +#include "../../../libs/BL24CXX.h" +#include "../../shared/eeprom_if.h" void eeprom_init() { BL24CXX::init(); } @@ -44,7 +44,7 @@ void eeprom_init() { BL24CXX::init(); } void eeprom_write_byte(uint8_t *pos, uint8_t value) { const unsigned eeprom_address = (unsigned)pos; - return BL24CXX::writeOneByte(eeprom_address, value); + BL24CXX::writeOneByte(eeprom_address, value); } uint8_t eeprom_read_byte(uint8_t *pos) { diff --git a/Marlin/src/HAL/STM32/eeprom_sdcard.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_sdcard.cpp similarity index 92% rename from Marlin/src/HAL/STM32/eeprom_sdcard.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_sdcard.cpp index 071d0bac00..64da3745d1 100644 --- a/Marlin/src/HAL/STM32/eeprom_sdcard.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_sdcard.cpp @@ -20,7 +20,7 @@ * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 @@ -28,12 +28,12 @@ * Implementation of EEPROM settings in SD Card */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(SDCARD_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" -#include "../../sd/cardreader.h" +#include "../../shared/eeprom_api.h" +#include "../../../sd/cardreader.h" #define EEPROM_FILENAME "eeprom.dat" @@ -54,7 +54,7 @@ bool PersistentStore::access_start() { int bytes_read = file.read(HAL_eeprom_data, MARLIN_EEPROM_SIZE); if (bytes_read < 0) return false; - for (; bytes_read < MARLIN_EEPROM_SIZE; bytes_read++) + for (; bytes_read < long(MARLIN_EEPROM_SIZE); bytes_read++) HAL_eeprom_data[bytes_read] = 0xFF; file.close(); return true; diff --git a/Marlin/src/HAL/STM32/eeprom_sram.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_sram.cpp similarity index 93% rename from Marlin/src/HAL/STM32/eeprom_sram.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_sram.cpp index 58a67f1759..a9d62ec29c 100644 --- a/Marlin/src/HAL/STM32/eeprom_sram.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_sram.cpp @@ -19,16 +19,16 @@ * along with this program. If not, see . * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(SRAM_EEPROM_EMULATION) -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #define MARLIN_EEPROM_SIZE 0x1000 // 4KB diff --git a/Marlin/src/HAL/STM32/eeprom_wired.cpp b/Marlin/src/HAL/STM32/eeprom/eeprom_wired.cpp similarity index 94% rename from Marlin/src/HAL/STM32/eeprom_wired.cpp rename to Marlin/src/HAL/STM32/eeprom/eeprom_wired.cpp index 5440030bd4..fa45e6c40d 100644 --- a/Marlin/src/HAL/STM32/eeprom_wired.cpp +++ b/Marlin/src/HAL/STM32/eeprom/eeprom_wired.cpp @@ -19,11 +19,11 @@ * along with this program. If not, see . * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM @@ -32,8 +32,8 @@ * with simple implementations supplied by Marlin. */ -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #define MARLIN_EEPROM_SIZE size_t(E2END + 1) diff --git a/Marlin/src/HAL/STM32/endstop_interrupts.h b/Marlin/src/HAL/STM32/endstop_interrupts.h index ab86bf29c2..e17b8a4c8e 100644 --- a/Marlin/src/HAL/STM32/endstop_interrupts.h +++ b/Marlin/src/HAL/STM32/endstop_interrupts.h @@ -45,6 +45,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/STM32/fastio.h b/Marlin/src/HAL/STM32/fastio.h index af2941c49c..a92bbc492e 100644 --- a/Marlin/src/HAL/STM32/fastio.h +++ b/Marlin/src/HAL/STM32/fastio.h @@ -77,6 +77,7 @@ void FastIO_init(); // Must be called before using fast io macros #define SET_INPUT_PULLUP(IO) _SET_MODE(IO, INPUT_PULLUP) //!< Input with Pull-up activation #define SET_INPUT_PULLDOWN(IO) _SET_MODE(IO, INPUT_PULLDOWN) //!< Input with Pull-down activation #define SET_OUTPUT(IO) OUT_WRITE(IO, LOW) +#define SET_OUTPUT_OD(IO) OUT_WRITE_OD(IO, LOW) #define SET_PWM(IO) _SET_MODE(IO, PWM) #define IS_INPUT(IO) diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_adv.h b/Marlin/src/HAL/STM32/inc/Conditionals_adv.h index 032716a294..f345b925bb 100644 --- a/Marlin/src/HAL/STM32/inc/Conditionals_adv.h +++ b/Marlin/src/HAL/STM32/inc/Conditionals_adv.h @@ -30,6 +30,3 @@ #undef F_CPU #define F_CPU BOARD_F_CPU #endif - -// The Sensitive Pins array is not optimizable -#define RUNTIME_ONLY_ANALOG_TO_DIGITAL diff --git a/Marlin/src/HAL/STM32/inc/Conditionals_post.h b/Marlin/src/HAL/STM32/inc/Conditionals_post.h index 6c97a635b3..8d72e720c1 100644 --- a/Marlin/src/HAL/STM32/inc/Conditionals_post.h +++ b/Marlin/src/HAL/STM32/inc/Conditionals_post.h @@ -29,6 +29,6 @@ #endif // Some STM32F4 boards may lose steps when saving to EEPROM during print (PR #17946) -#if defined(STM32F4xx) && ENABLED(FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 +#if ALL(STM32F4xx, FLASH_EEPROM_EMULATION) && PRINTCOUNTER_SAVE_INTERVAL > 0 #define PRINTCOUNTER_SYNC #endif diff --git a/Marlin/src/HAL/STM32/inc/SanityCheck.h b/Marlin/src/HAL/STM32/inc/SanityCheck.h index e35b4e59cf..616f96eba0 100644 --- a/Marlin/src/HAL/STM32/inc/SanityCheck.h +++ b/Marlin/src/HAL/STM32/inc/SanityCheck.h @@ -36,8 +36,8 @@ #error "SDCARD_EEPROM_EMULATION requires SDSUPPORT. Enable SDSUPPORT or choose another EEPROM emulation." #endif -#if !defined(STM32F4xx) && ENABLED(FLASH_EEPROM_LEVELING) - #error "FLASH_EEPROM_LEVELING is currently only supported on STM32F4 hardware." +#if NONE(STM32F4xx, STM32H7xx) && ENABLED(FLASH_EEPROM_LEVELING) + #error "FLASH_EEPROM_LEVELING is currently only supported on STM32F4/H7 hardware." // IRON #endif #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) @@ -58,7 +58,7 @@ * Check for common serial pin conflicts */ #define _CHECK_SERIAL_PIN(N) (( \ - BTN_EN1 == N || BTN_EN2 == N ||DOGLCD_CS == N || HEATER_BED_PIN == N || FAN0_PIN == N || \ + BTN_EN1 == N || BTN_EN2 == N || DOGLCD_CS == N || HEATER_BED_PIN == N || FAN0_PIN == N || \ SDIO_D2_PIN == N || SDIO_D3_PIN == N || SDIO_CK_PIN == N || SDIO_CMD_PIN == N || \ Y_STEP_PIN == N || Y_ENABLE_PIN == N || E0_ENABLE_PIN == N || POWER_LOSS_PIN == N \ )) diff --git a/Marlin/src/HAL/STM32/pinsDebug.h b/Marlin/src/HAL/STM32/pinsDebug.h index 13990a69f5..b14c9c721c 100644 --- a/Marlin/src/HAL/STM32/pinsDebug.h +++ b/Marlin/src/HAL/STM32/pinsDebug.h @@ -21,6 +21,26 @@ */ #pragma once +/** + * Pins Debugging for STM32 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) + */ + #include #ifndef NUM_DIGITAL_PINS @@ -34,11 +54,11 @@ * that a CPU has. * * VARIABLES: - * Ard_num - Arduino pin number - defined by the platform. It is used by digitalRead and - * digitalWrite commands and by M42. - * - does not contain port/pin info - * - is not in port/pin order - * - typically a variant will only assign Ard_num to port/pins that are actually used + * A - Arduino pin number - defined by the platform. It is used by digitalRead and + * digitalWrite commands and by M42. + * - does not contain port/pin info + * - is not in port/pin order + * - typically a variant will only assign Ard_num to port/pins that are actually used * Index - M43 counter - only used to get Ard_num * x - a parameter/argument used to search the pin_array to try to find a signal name * associated with a Ard_num @@ -98,15 +118,11 @@ const XrefInfo pin_xref[] PROGMEM = { #define MODE_PIN_ALT 2 // Alternate function mode #define MODE_PIN_ANALOG 3 // Analog mode -#define PIN_NUM(P) (P & 0x000F) -#define PIN_NUM_ALPHA_LEFT(P) (((P & 0x000F) < 10) ? ('0' + (P & 0x000F)) : '1') -#define PIN_NUM_ALPHA_RIGHT(P) (((P & 0x000F) > 9) ? ('0' + (P & 0x000F) - 10) : 0 ) -#define PORT_NUM(P) ((P >> 4) & 0x0007) -#define PORT_ALPHA(P) ('A' + (P >> 4)) - -/** - * Translation of routines & variables used by pinsDebug.h - */ +#define PIN_NUM(P) ((P) & 0x000F) +#define PIN_NUM_ALPHA_LEFT(P) ((((P) & 0x000F) < 10) ? ('0' + ((P) & 0x000F)) : '1') +#define PIN_NUM_ALPHA_RIGHT(P) ((((P) & 0x000F) > 9) ? ('0' + ((P) & 0x000F) - 10) : 0 ) +#define PORT_NUM(P) (((P) >> 4) & 0x0007) +#define PORT_ALPHA(P) ('A' + ((P) >> 4)) #if NUM_ANALOG_FIRST >= NUM_DIGITAL_PINS #define HAS_HIGH_ANALOG_PINS 1 @@ -115,42 +131,42 @@ const XrefInfo pin_xref[] PROGMEM = { #define NUM_ANALOG_LAST ((NUM_ANALOG_FIRST) + (NUM_ANALOG_INPUTS) - 1) #endif #define NUMBER_PINS_TOTAL ((NUM_DIGITAL_PINS) + TERN0(HAS_HIGH_ANALOG_PINS, NUM_ANALOG_INPUTS)) -#define VALID_PIN(P) (WITHIN(P, 0, (NUM_DIGITAL_PINS) - 1) || TERN0(HAS_HIGH_ANALOG_PINS, WITHIN(P, NUM_ANALOG_FIRST, NUM_ANALOG_LAST))) -#define digitalRead_mod(Ard_num) extDigitalRead(Ard_num) // must use Arduino pin numbers when doing reads -#define PRINT_PIN(Q) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define DIGITAL_PIN_TO_ANALOG_PIN(ANUM) -1 // will report analog pin number in the print port routine +#define isValidPin(P) (WITHIN(P, 0, (NUM_DIGITAL_PINS) - 1) || TERN0(HAS_HIGH_ANALOG_PINS, WITHIN(P, NUM_ANALOG_FIRST, NUM_ANALOG_LAST))) +#define digitalRead_mod(A) extDigitalRead(A) // must use Arduino pin numbers when doing reads +#define printPinNumber(Q) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define digitalPinToAnalogIndex(P) -1 // will report analog pin number in the print port routine // x is a variable used to search pin_array -#define GET_ARRAY_IS_DIGITAL(x) ((bool) pin_array[x].is_digital) -#define GET_ARRAY_PIN(x) ((pin_t) pin_array[x].pin) -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define getPinIsDigitalByIndex(x) ((bool) pin_array[x].is_digital) +#define getPinByIndex(x) ((pin_t) pin_array[x].pin) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 33 // space needed to be pretty if not first name assigned to a pin // // Pin Mapping for M43 // -#define GET_PIN_MAP_PIN_M43(Index) pin_xref[Index].Ard_num +#define GET_PIN_MAP_PIN_M43(x) pin_xref[x].Ard_num #ifndef M43_NEVER_TOUCH - #define _M43_NEVER_TOUCH(Index) (Index >= 9 && Index <= 12) // SERIAL/USB pins: PA9(TX) PA10(RX) PA11(USB_DM) PA12(USB_DP) - #ifdef KILL_PIN - #define M43_NEVER_TOUCH(Index) m43_never_touch(Index) + #define _M43_NEVER_TOUCH(x) WITHIN(x, 9, 12) // SERIAL/USB pins: PA9(TX) PA10(RX) PA11(USB_DM) PA12(USB_DP) + #if PIN_EXISTS(KILL) + #define M43_NEVER_TOUCH(x) m43_never_touch(x) - bool m43_never_touch(const pin_t Index) { + bool m43_never_touch(const pin_t index) { static pin_t M43_kill_index = -1; if (M43_kill_index < 0) for (M43_kill_index = 0; M43_kill_index < NUMBER_PINS_TOTAL; M43_kill_index++) if (KILL_PIN == GET_PIN_MAP_PIN_M43(M43_kill_index)) break; - return _M43_NEVER_TOUCH(Index) || Index == M43_kill_index; // KILL_PIN and SERIAL/USB + return _M43_NEVER_TOUCH(index) || index == M43_kill_index; // KILL_PIN and SERIAL/USB } #else - #define M43_NEVER_TOUCH(Index) _M43_NEVER_TOUCH(Index) + #define M43_NEVER_TOUCH(index) _M43_NEVER_TOUCH(index) #endif #endif -uint8_t get_pin_mode(const pin_t Ard_num) { - const PinName dp = digitalPinToPinName(Ard_num); +uint8_t get_pin_mode(const pin_t pin) { + const PinName dp = digitalPinToPinName(pin); uint32_t ll_pin = STM_LL_GPIO_PIN(dp); GPIO_TypeDef *port = get_GPIO_Port(STM_PORT(dp)); uint32_t mode = LL_GPIO_GetPinMode(port, ll_pin); @@ -164,41 +180,41 @@ uint8_t get_pin_mode(const pin_t Ard_num) { } } -bool GET_PINMODE(const pin_t Ard_num) { - const uint8_t pin_mode = get_pin_mode(Ard_num); +bool getValidPinMode(const pin_t pin) { + const uint8_t pin_mode = get_pin_mode(pin); return pin_mode == MODE_PIN_OUTPUT || pin_mode == MODE_PIN_ALT; // assume all alt definitions are PWM } -int8_t digital_pin_to_analog_pin(const pin_t Ard_num) { - if (WITHIN(Ard_num, NUM_ANALOG_FIRST, NUM_ANALOG_LAST)) - return Ard_num - NUM_ANALOG_FIRST; +int8_t digital_pin_to_analog_pin(const pin_t pin) { + if (WITHIN(pin, NUM_ANALOG_FIRST, NUM_ANALOG_LAST)) + return pin - NUM_ANALOG_FIRST; - const uint32_t ind = digitalPinToAnalogInput(Ard_num); + const int8_t ind = digitalPinToAnalogIndex(pin); return (ind < NUM_ANALOG_INPUTS) ? ind : -1; } -bool IS_ANALOG(const pin_t Ard_num) { - return get_pin_mode(Ard_num) == MODE_PIN_ANALOG; +bool isAnalogPin(const pin_t pin) { + return get_pin_mode(pin) == MODE_PIN_ANALOG; } -bool is_digital(const pin_t Ard_num) { - const uint8_t pin_mode = get_pin_mode(pin_array[Ard_num].pin); +bool is_digital(const pin_t pin) { + const uint8_t pin_mode = get_pin_mode(pin_array[pin].pin); return pin_mode == MODE_PIN_INPUT || pin_mode == MODE_PIN_OUTPUT; } -void print_port(const pin_t Ard_num) { +void printPinPort(const pin_t pin) { char buffer[16]; - pin_t Index; - for (Index = 0; Index < NUMBER_PINS_TOTAL; Index++) - if (Ard_num == GET_PIN_MAP_PIN_M43(Index)) break; + pin_t index; + for (index = 0; index < NUMBER_PINS_TOTAL; index++) + if (pin == GET_PIN_MAP_PIN_M43(index)) break; - const char * ppa = pin_xref[Index].Port_pin_alpha; + const char * ppa = pin_xref[index].Port_pin_alpha; sprintf_P(buffer, PSTR("%s"), ppa); SERIAL_ECHO(buffer); if (ppa[3] == '\0') SERIAL_CHAR(' '); // print analog pin number - const int8_t Port_pin = digital_pin_to_analog_pin(Ard_num); + const int8_t Port_pin = digital_pin_to_analog_pin(pin); if (Port_pin >= 0) { sprintf_P(buffer, PSTR(" (A%d) "), Port_pin); SERIAL_ECHO(buffer); @@ -208,8 +224,8 @@ void print_port(const pin_t Ard_num) { SERIAL_ECHO_SP(7); // Print number to be used with M42 - int calc_p = Ard_num; - if (Ard_num > NUM_DIGITAL_PINS) { + int calc_p = pin; + if (pin > NUM_DIGITAL_PINS) { calc_p -= NUM_ANALOG_FIRST; if (calc_p > 7) calc_p += 8; } @@ -222,15 +238,15 @@ void print_port(const pin_t Ard_num) { } } -bool pwm_status(const pin_t Ard_num) { - return get_pin_mode(Ard_num) == MODE_PIN_ALT; +bool pwm_status(const pin_t pin) { + return get_pin_mode(pin) == MODE_PIN_ALT; } -void pwm_details(const pin_t Ard_num) { +void printPinPWM(const pin_t pin) { #ifndef STM32F1xx - if (pwm_status(Ard_num)) { + if (pwm_status(pin)) { uint32_t alt_all = 0; - const PinName dp = digitalPinToPinName(Ard_num); + const PinName dp = digitalPinToPinName(pin); pin_t pin_number = uint8_t(PIN_NUM(dp)); const bool over_7 = pin_number >= 8; const uint8_t ind = over_7 ? 1 : 0; @@ -285,4 +301,4 @@ void pwm_details(const pin_t Ard_num) { #else // TODO: F1 doesn't support changing pins function, so we need to check the function of the PIN and if it's enabled #endif -} // pwm_details +} // printPinPWM diff --git a/Marlin/src/HAL/STM32/msc_sd.cpp b/Marlin/src/HAL/STM32/sd/msc_sd.cpp similarity index 95% rename from Marlin/src/HAL/STM32/msc_sd.cpp rename to Marlin/src/HAL/STM32/sd/msc_sd.cpp index 5c8bee9c62..9bb65aab4a 100644 --- a/Marlin/src/HAL/STM32/msc_sd.cpp +++ b/Marlin/src/HAL/STM32/sd/msc_sd.cpp @@ -20,20 +20,19 @@ * along with this program. If not, see . * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if HAS_SD_HOST_DRIVE -#include "../shared/Marduino.h" +#include "../../../sd/cardreader.h" + #include "msc_sd.h" -#include "usbd_core.h" - -#include "../../sd/cardreader.h" +#include #include #include @@ -49,7 +48,8 @@ class Sd2CardUSBMscHandler : public USBMscHandler { public: DiskIODriver* diskIODriver() { - #if ENABLED(MULTI_VOLUME) + // TODO: Explore a variable shared volume, or auto share the un-mounted volume(s) + #if HAS_MULTI_VOLUME #if SHARED_VOLUME_IS(SD_ONBOARD) return &card.media_driver_sdcard; #elif SHARED_VOLUME_IS(USB_FLASH_DRIVE) diff --git a/Marlin/src/HAL/STM32/msc_sd.h b/Marlin/src/HAL/STM32/sd/msc_sd.h similarity index 100% rename from Marlin/src/HAL/STM32/msc_sd.h rename to Marlin/src/HAL/STM32/sd/msc_sd.h diff --git a/Marlin/src/HAL/STM32/usb_host.cpp b/Marlin/src/HAL/STM32/sd/usb_host.cpp similarity index 96% rename from Marlin/src/HAL/STM32/usb_host.cpp rename to Marlin/src/HAL/STM32/sd/usb_host.cpp index afafe1d4f3..f411771c8a 100644 --- a/Marlin/src/HAL/STM32/usb_host.cpp +++ b/Marlin/src/HAL/STM32/sd/usb_host.cpp @@ -20,18 +20,17 @@ * */ -#include "../platforms.h" +#include "../../platforms.h" #ifdef HAL_STM32 -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ALL(USE_OTG_USB_HOST, USBHOST) #include "usb_host.h" -#include "../shared/Marduino.h" -#include "usbh_core.h" -#include "usbh_msc.h" +#include +#include USBH_HandleTypeDef hUsbHost; USBHost usb; diff --git a/Marlin/src/HAL/STM32/usb_host.h b/Marlin/src/HAL/STM32/sd/usb_host.h similarity index 100% rename from Marlin/src/HAL/STM32/usb_host.h rename to Marlin/src/HAL/STM32/sd/usb_host.h diff --git a/Marlin/src/HAL/STM32/sdio.cpp b/Marlin/src/HAL/STM32/sdio.cpp index de388b8e14..89df16628f 100644 --- a/Marlin/src/HAL/STM32/sdio.cpp +++ b/Marlin/src/HAL/STM32/sdio.cpp @@ -238,7 +238,7 @@ void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { hdma_sdio.Init.MemInc = DMA_MINC_ENABLE; hdma_sdio.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; hdma_sdio.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; - hdma_sdio.Init.Priority = DMA_PRIORITY_LOW; + hdma_sdio.Init.Priority = DMA_PRIORITY_MEDIUM; __HAL_LINKDMA(&hsd, hdmarx, hdma_sdio); __HAL_LINKDMA(&hsd, hdmatx, hdma_sdio); diff --git a/Marlin/src/HAL/STM32/spi_pins.h b/Marlin/src/HAL/STM32/spi_pins.h index 7f341a8c25..da6fa0d160 100644 --- a/Marlin/src/HAL/STM32/spi_pins.h +++ b/Marlin/src/HAL/STM32/spi_pins.h @@ -33,6 +33,3 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN PIN_SPI_MOSI #endif -#ifndef SD_SS_PIN - #define SD_SS_PIN PIN_SPI_SS -#endif diff --git a/Marlin/src/HAL/STM32/temp_soc.h b/Marlin/src/HAL/STM32/temp_soc.h index 05fad695c3..cc165dd5e4 100644 --- a/Marlin/src/HAL/STM32/temp_soc.h +++ b/Marlin/src/HAL/STM32/temp_soc.h @@ -341,6 +341,6 @@ #elif defined(TS_TYPICAL_V) && defined(TS_TYPICAL_SLOPE) && defined(TS_TYPICAL_TEMP) - #define TEMP_SOC_SENSOR(RAW) ((TS_TYPICAL_V - (RAW) / float(OVERSAMPLENR) / float(HAL_ADC_RANGE) * (float(ADC_VREF_MV) / 1000.0f)) / ((TS_TYPICAL_SLOPE) / 1000) + TS_TYPICAL_TEMP) + #define TEMP_SOC_SENSOR(RAW) ((TS_TYPICAL_V - (RAW) / float(OVERSAMPLENR) / float(HAL_ADC_RANGE) * (float(ADC_VREF_MV) / 1000.0f)) / ((TS_TYPICAL_SLOPE) / 1000.0f) + TS_TYPICAL_TEMP) #endif diff --git a/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp index 4dffe8b4fc..70caef6778 100644 --- a/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp +++ b/Marlin/src/HAL/STM32/tft/tft_fsmc.cpp @@ -132,6 +132,9 @@ void TFT_FSMC::init() { DMAtx.Init.Priority = DMA_PRIORITY_HIGH; LCD = (LCD_CONTROLLER_TypeDef *)controllerAddress; + + DMAtx.Init.PeriphInc = DMA_PINC_DISABLE; + HAL_DMA_Init(&DMAtx); } uint32_t TFT_FSMC::getID() { @@ -179,14 +182,19 @@ void TFT_FSMC::abort() { } void TFT_FSMC::transmitDMA(uint32_t memoryIncrease, uint16_t *data, uint16_t count) { - DMAtx.Init.PeriphInc = memoryIncrease; - HAL_DMA_Init(&DMAtx); + if (!__IS_DMA_CONFIGURED(&DMAtx) || DMAtx.Init.PeriphInc != memoryIncrease) { + DMAtx.Init.PeriphInc = memoryIncrease; + HAL_DMA_Init(&DMAtx); + } HAL_DMA_Start(&DMAtx, (uint32_t)data, (uint32_t)&(LCD->RAM), count); + TERN_(TFT_SHARED_IO, while (isBusy())); } void TFT_FSMC::transmit(uint32_t memoryIncrease, uint16_t *data, uint16_t count) { - DMAtx.Init.PeriphInc = memoryIncrease; - HAL_DMA_Init(&DMAtx); + if (!__IS_DMA_CONFIGURED(&DMAtx) || DMAtx.Init.PeriphInc != memoryIncrease) { + DMAtx.Init.PeriphInc = memoryIncrease; + HAL_DMA_Init(&DMAtx); + } dataTransferBegin(); HAL_DMA_Start(&DMAtx, (uint32_t)data, (uint32_t)&(LCD->RAM), count); HAL_DMA_PollForTransfer(&DMAtx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY); diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.cpp b/Marlin/src/HAL/STM32/tft/tft_spi.cpp index cda2eb28e3..71ad14534f 100644 --- a/Marlin/src/HAL/STM32/tft/tft_spi.cpp +++ b/Marlin/src/HAL/STM32/tft/tft_spi.cpp @@ -337,7 +337,7 @@ void TFT_SPI::transmitDMA(uint32_t memoryIncrease, uint16_t *data, uint16_t coun SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); // Enable Tx DMA Request #endif - TERN_(TFT_SHARED_IO, while (isBusy()) { /* nada */ }); + TERN_(TFT_SHARED_IO, while (isBusy())); } void TFT_SPI::transmit(uint32_t memoryIncrease, uint16_t *data, uint16_t count) { diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.h b/Marlin/src/HAL/STM32/tft/xpt2046.h index 685c9441ae..f3d3e53291 100644 --- a/Marlin/src/HAL/STM32/tft/xpt2046.h +++ b/Marlin/src/HAL/STM32/tft/xpt2046.h @@ -49,7 +49,11 @@ #define TOUCH_INT_PIN -1 #endif -#define XPT2046_DFR_MODE 0x00 +#if PIN_EXISTS(TOUCH_INT) + #define XPT2046_DFR_MODE 0x00 +#else + #define XPT2046_DFR_MODE 0x01 +#endif #define XPT2046_SER_MODE 0x04 #define XPT2046_CONTROL 0x80 diff --git a/Marlin/src/HAL/STM32/u8g/LCD_defines.h b/Marlin/src/HAL/STM32/u8g/LCD_defines.h index 35f1674d69..96f73002a5 100644 --- a/Marlin/src/HAL/STM32/u8g/LCD_defines.h +++ b/Marlin/src/HAL/STM32/u8g/LCD_defines.h @@ -25,8 +25,11 @@ * STM32 LCD-specific defines */ -uint8_t u8g_com_std_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL -uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL +uint8_t u8g_com_HAL_STM32_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // u8g_com_stm32duino_swspi.cpp +#define U8G_COM_HAL_SW_SPI_FN u8g_com_HAL_STM32_sw_spi_fn -#define U8G_COM_HAL_SW_SPI_FN u8g_com_std_sw_spi_fn +uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL #define U8G_COM_HAL_HW_SPI_FN u8g_com_stm32duino_hw_spi_fn + +uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // u8g_com_stm32duino_ssd_i2c.cpp +#define U8G_COM_SSD_I2C_HAL u8g_com_stm32duino_ssd_i2c_fn diff --git a/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp b/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp new file mode 100644 index 0000000000..72abe1a656 --- /dev/null +++ b/Marlin/src/HAL/STM32/u8g/u8g_com_stm32duino_ssd_i2c.cpp @@ -0,0 +1,194 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * 2-Wire I2C COM Driver + * + * Handles both Hardware and Software I2C so any pins can be used as SDA and SLC. + * Wire library is used for Hardware I2C. + * SlowSoftWire is used for Software I2C. + * + * Wire / SoftWire library selection can be done automatically at runtime. + * + * SDA and SLC pins must be named DOGLCD_SDA_PIN, DOGLCD_SCL_PIN to distinguish + * from other I2C devices (e.g., EEPROM) that use I2C_SDA_PIN, I2C_SLC_PIN. + */ +#ifdef ARDUINO_ARCH_STM32 + +#include "../../../inc/MarlinConfig.h" + +#if HAS_U8GLIB_I2C_OLED + +#include + +#if ENABLED(U8G_USES_HW_I2C) + #include + #ifndef MASTER_ADDRESS + #define MASTER_ADDRESS 0x01 + #endif +#endif + +#if ENABLED(U8G_USES_SW_I2C) + #include + #include +#endif + +/** + * BUFFER_LENGTH is defined in libraries\Wire\utility\WireBase.h + * Default value is 32 + * Increase this value to 144 to send U8G_COM_MSG_WRITE_SEQ in single block + */ +#ifndef BUFFER_LENGTH + #define BUFFER_LENGTH 32 +#endif +#if BUFFER_LENGTH > 144 + #error "BUFFER_LENGTH should not be greater than 144." +#endif +#define I2C_MAX_LENGTH (BUFFER_LENGTH - 1) + +uint8_t u8g_com_stm32duino_ssd_i2c_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { + // Hardware I2C flag + #ifdef COMPILE_TIME_I2C_IS_HARDWARE + constexpr bool isHardI2C = ENABLED(COMPILE_TIME_I2C_IS_HARDWARE); + #else + static bool isHardI2C = false; + static bool i2c_initialized = false; // Flag to only run init/linking code once + if (!i2c_initialized) { // Init runtime linkages + i2c_initialized = true; // Only do this once + I2C_TypeDef *i2cInstance1 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SDA_PIN), PinMap_I2C_SDA); + I2C_TypeDef *i2cInstance2 = (I2C_TypeDef *)pinmap_peripheral(digitalPinToPinName(DOGLCD_SCL_PIN), PinMap_I2C_SCL); + isHardI2C = (i2cInstance1 && (i2cInstance1 == i2cInstance2)); // Found hardware I2C controller + } + #endif + + static uint8_t msgInitCount = 0; // Ignore all messages until 2nd U8G_COM_MSG_INIT + if (msgInitCount) { + if (msg == U8G_COM_MSG_INIT) msgInitCount--; + if (msgInitCount) return -1; + } + + static uint8_t control; + if (isHardI2C) { // Found hardware I2C controller + #if ENABLED(U8G_USES_HW_I2C) + static TwoWire wire2; // A TwoWire object for use below + switch (msg) { + case U8G_COM_MSG_INIT: + wire2.setClock(400000); + wire2.setSCL(DOGLCD_SCL_PIN); + wire2.setSDA(DOGLCD_SDA_PIN); + wire2.begin(MASTER_ADDRESS, 0); // Start as master + break; + + case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1) + control = arg_val ? 0x40 : 0x00; + break; + + case U8G_COM_MSG_WRITE_BYTE: + wire2.beginTransmission(0x3C); + wire2.write(control); + wire2.write(arg_val); + wire2.endTransmission(); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t* dataptr = (uint8_t*)arg_ptr; + #ifdef I2C_MAX_LENGTH + while (arg_val > 0) { + wire2.beginTransmission(0x3C); + wire2.write(control); + if (arg_val <= I2C_MAX_LENGTH) { + wire2.write(dataptr, arg_val); + arg_val = 0; + } + else { + wire2.write(dataptr, I2C_MAX_LENGTH); + arg_val -= I2C_MAX_LENGTH; + dataptr += I2C_MAX_LENGTH; + } + wire2.endTransmission(); + } + #else + wire2.beginTransmission(0x3C); + wire2.write(control); + wire2.write(dataptr, arg_val); + wire2.endTransmission(); + #endif // I2C_MAX_LENGTH + break; + } + } + #endif // U8G_USES_HW_I2C + } + else { // Software I2C + #if ENABLED(U8G_USES_SW_I2C) + static SlowSoftWire sWire = SlowSoftWire(DOGLCD_SDA_PIN, DOGLCD_SCL_PIN); + + switch (msg) { + case U8G_COM_MSG_INIT: + sWire.setClock(400000); + sWire.begin(); // Start as master + break; + + case U8G_COM_MSG_ADDRESS: // Define cmd (arg_val = 0) or data mode (arg_val = 1) + control = arg_val ? 0x40 : 0x00; + break; + + case U8G_COM_MSG_WRITE_BYTE: + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + sWire.write((uint8_t)arg_val); + sWire.endTransmission(); + break; + + case U8G_COM_MSG_WRITE_SEQ: { + uint8_t* dataptr = (uint8_t*)arg_ptr; + #ifdef I2C_MAX_LENGTH + while (arg_val > 0) { + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + if (arg_val <= I2C_MAX_LENGTH) { + sWire.write((const uint8_t *)dataptr, (size_t)arg_val); + arg_val = 0; + } + else { + sWire.write((const uint8_t *)dataptr, I2C_MAX_LENGTH); + arg_val -= I2C_MAX_LENGTH; + dataptr += I2C_MAX_LENGTH; + } + sWire.endTransmission(); + } + #else + sWire.beginTransmission((uint8_t)0x3C); + sWire.write((uint8_t)control); + sWire.write((const uint8_t *)dataptr, (size_t)arg_val); + sWire.endTransmission(); + #endif // I2C_MAX_LENGTH + break; + } + } + #endif // U8G_USES_SW_I2C + } + + return 1; +} + +#endif // HAS_U8GLIB_I2C_OLED +#endif // ARDUINO_ARCH_STM32 diff --git a/Marlin/src/HAL/STM32/usb_serial.cpp b/Marlin/src/HAL/STM32/usb_serial.cpp index 0b2372f3a7..1ca8b19976 100644 --- a/Marlin/src/HAL/STM32/usb_serial.cpp +++ b/Marlin/src/HAL/STM32/usb_serial.cpp @@ -26,7 +26,7 @@ #include "../../inc/MarlinConfigPre.h" -#if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC) +#if ENABLED(EMERGENCY_PARSER) && ANY(USBD_USE_CDC, USBD_USE_CDC_MSC) #include "usb_serial.h" #include "../../feature/e_parser.h" @@ -56,5 +56,5 @@ void USB_Hook_init() { USBD_CDC_fops.Receive = USBD_CDC_Receive_hook; } -#endif // EMERGENCY_PARSER && USBD_USE_CDC +#endif // EMERGENCY_PARSER && (USBD_USE_CDC || USBD_USE_CDC_MSC) #endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32F1/HAL.cpp b/Marlin/src/HAL/STM32F1/HAL.cpp index e6cbb9fc06..fc1680cbf4 100644 --- a/Marlin/src/HAL/STM32F1/HAL.cpp +++ b/Marlin/src/HAL/STM32F1/HAL.cpp @@ -65,7 +65,8 @@ uint16_t adc_results[ADC_COUNT]; emergency_parser.update(MSerial0.emergency_state, buf[i + total - len]); } #endif -#endif + +#endif // SERIAL_USB && !HAS_SD_HOST_DRIVE // ------------------------ // Watchdog Timer @@ -130,30 +131,31 @@ uint16_t MarlinHAL::adc_result; #include -// Init the AD in continuous capture mode +// Init the ADC in continuous capture mode void MarlinHAL::adc_init() { static const uint8_t adc_pins[] = { - OPTITEM(HAS_TEMP_ADC_0, TEMP_0_PIN) - OPTITEM(HAS_TEMP_ADC_1, TEMP_1_PIN) - OPTITEM(HAS_TEMP_ADC_2, TEMP_2_PIN) - OPTITEM(HAS_TEMP_ADC_3, TEMP_3_PIN) - OPTITEM(HAS_TEMP_ADC_4, TEMP_4_PIN) - OPTITEM(HAS_TEMP_ADC_5, TEMP_5_PIN) - OPTITEM(HAS_TEMP_ADC_6, TEMP_6_PIN) - OPTITEM(HAS_TEMP_ADC_7, TEMP_7_PIN) - OPTITEM(HAS_HEATED_BED, TEMP_BED_PIN) - OPTITEM(HAS_TEMP_CHAMBER, TEMP_CHAMBER_PIN) - OPTITEM(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN) - OPTITEM(HAS_TEMP_COOLER, TEMP_COOLER_PIN) - OPTITEM(HAS_TEMP_BOARD, TEMP_BOARD_PIN) - OPTITEM(HAS_TEMP_SOC, TEMP_SOC_PIN) - OPTITEM(FILAMENT_WIDTH_SENSOR, FILWIDTH_PIN) - OPTITEM(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN) - OPTITEM(HAS_JOY_ADC_X, JOY_X_PIN) - OPTITEM(HAS_JOY_ADC_Y, JOY_Y_PIN) - OPTITEM(HAS_JOY_ADC_Z, JOY_Z_PIN) - OPTITEM(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN) - OPTITEM(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN) + OPTITEM(HAS_TEMP_ADC_0, TEMP_0_PIN ) + OPTITEM(HAS_TEMP_ADC_1, TEMP_1_PIN ) + OPTITEM(HAS_TEMP_ADC_2, TEMP_2_PIN ) + OPTITEM(HAS_TEMP_ADC_3, TEMP_3_PIN ) + OPTITEM(HAS_TEMP_ADC_4, TEMP_4_PIN ) + OPTITEM(HAS_TEMP_ADC_5, TEMP_5_PIN ) + OPTITEM(HAS_TEMP_ADC_6, TEMP_6_PIN ) + OPTITEM(HAS_TEMP_ADC_7, TEMP_7_PIN ) + OPTITEM(HAS_TEMP_ADC_BED, TEMP_BED_PIN ) + OPTITEM(HAS_TEMP_ADC_CHAMBER, TEMP_CHAMBER_PIN ) + OPTITEM(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN ) + OPTITEM(HAS_TEMP_ADC_COOLER, TEMP_COOLER_PIN ) + OPTITEM(HAS_TEMP_ADC_BOARD, TEMP_BOARD_PIN ) + OPTITEM(HAS_TEMP_ADC_SOC, TEMP_SOC_PIN ) + OPTITEM(HAS_FILWIDTH_ADC, FILWIDTH_PIN ) + OPTITEM(HAS_FILWIDTH2_ADC, FILWIDTH2_PIN ) + OPTITEM(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN ) + OPTITEM(HAS_JOY_ADC_X, JOY_X_PIN ) + OPTITEM(HAS_JOY_ADC_Y, JOY_Y_PIN ) + OPTITEM(HAS_JOY_ADC_Z, JOY_Z_PIN ) + OPTITEM(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN) + OPTITEM(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN) }; static STM32ADC adc(ADC1); // Configure the ADC @@ -174,27 +176,28 @@ void MarlinHAL::adc_start(const pin_t pin) { ADCIndex pin_index; switch (pin) { default: return; - _TCASE(HAS_TEMP_ADC_0, TEMP_0_PIN, TEMP_0) - _TCASE(HAS_TEMP_ADC_1, TEMP_1_PIN, TEMP_1) - _TCASE(HAS_TEMP_ADC_2, TEMP_2_PIN, TEMP_2) - _TCASE(HAS_TEMP_ADC_3, TEMP_3_PIN, TEMP_3) - _TCASE(HAS_TEMP_ADC_4, TEMP_4_PIN, TEMP_4) - _TCASE(HAS_TEMP_ADC_5, TEMP_5_PIN, TEMP_5) - _TCASE(HAS_TEMP_ADC_6, TEMP_6_PIN, TEMP_6) - _TCASE(HAS_TEMP_ADC_7, TEMP_7_PIN, TEMP_7) - _TCASE(HAS_HEATED_BED, TEMP_BED_PIN, TEMP_BED) - _TCASE(HAS_TEMP_CHAMBER, TEMP_CHAMBER_PIN, TEMP_CHAMBER) - _TCASE(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN, TEMP_PROBE) - _TCASE(HAS_TEMP_COOLER, TEMP_COOLER_PIN, TEMP_COOLER) - _TCASE(HAS_TEMP_BOARD, TEMP_BOARD_PIN, TEMP_BOARD) - _TCASE(HAS_TEMP_SOC, TEMP_SOC_PIN, TEMP_SOC) - _TCASE(HAS_JOY_ADC_X, JOY_X_PIN, JOY_X) - _TCASE(HAS_JOY_ADC_Y, JOY_Y_PIN, JOY_Y) - _TCASE(HAS_JOY_ADC_Z, JOY_Z_PIN, JOY_Z) - _TCASE(FILAMENT_WIDTH_SENSOR, FILWIDTH_PIN, FILWIDTH) - _TCASE(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN, ADC_KEY) - _TCASE(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN, POWERMON_CURRENT) - _TCASE(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN, POWERMON_VOLTAGE) + _TCASE(HAS_TEMP_ADC_0, TEMP_0_PIN, TEMP_0 ) + _TCASE(HAS_TEMP_ADC_1, TEMP_1_PIN, TEMP_1 ) + _TCASE(HAS_TEMP_ADC_2, TEMP_2_PIN, TEMP_2 ) + _TCASE(HAS_TEMP_ADC_3, TEMP_3_PIN, TEMP_3 ) + _TCASE(HAS_TEMP_ADC_4, TEMP_4_PIN, TEMP_4 ) + _TCASE(HAS_TEMP_ADC_5, TEMP_5_PIN, TEMP_5 ) + _TCASE(HAS_TEMP_ADC_6, TEMP_6_PIN, TEMP_6 ) + _TCASE(HAS_TEMP_ADC_7, TEMP_7_PIN, TEMP_7 ) + _TCASE(HAS_TEMP_ADC_BED, TEMP_BED_PIN, TEMP_BED ) + _TCASE(HAS_TEMP_ADC_CHAMBER, TEMP_CHAMBER_PIN, TEMP_CHAMBER ) + _TCASE(HAS_TEMP_ADC_PROBE, TEMP_PROBE_PIN, TEMP_PROBE ) + _TCASE(HAS_TEMP_ADC_COOLER, TEMP_COOLER_PIN, TEMP_COOLER ) + _TCASE(HAS_TEMP_ADC_BOARD, TEMP_BOARD_PIN, TEMP_BOARD ) + _TCASE(HAS_TEMP_ADC_SOC, TEMP_SOC_PIN, TEMP_SOC ) + _TCASE(HAS_FILWIDTH_ADC, FILWIDTH_PIN, FILWIDTH ) + _TCASE(HAS_FILWIDTH2_ADC, FILWIDTH2_PIN, FILWIDTH2 ) + _TCASE(HAS_ADC_BUTTONS, ADC_KEYPAD_PIN, ADC_KEY ) + _TCASE(HAS_JOY_ADC_X, JOY_X_PIN, JOY_X ) + _TCASE(HAS_JOY_ADC_Y, JOY_Y_PIN, JOY_Y ) + _TCASE(HAS_JOY_ADC_Z, JOY_Z_PIN, JOY_Z ) + _TCASE(POWER_MONITOR_CURRENT, POWER_MONITOR_CURRENT_PIN, POWERMON_CURRENT) + _TCASE(POWER_MONITOR_VOLTAGE, POWER_MONITOR_VOLTAGE_PIN, POWERMON_VOLTAGE) } adc_result = (adc_results[(int)pin_index] & 0xFFF) >> (12 - HAL_ADC_RESOLUTION); // shift out unused bits } @@ -252,7 +255,7 @@ void MarlinHAL::init() { #endif #if PIN_EXISTS(USB_CONNECT) OUT_WRITE(USB_CONNECT_PIN, !USB_CONNECT_INVERTING); // USB clear connection - delay(1000); // Give OS time to notice + delay_ms(1000); // Give OS time to notice WRITE(USB_CONNECT_PIN, USB_CONNECT_INVERTING); #endif TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the minimal serial handler @@ -264,7 +267,7 @@ void MarlinHAL::idletask() { /** * When Marlin is using the SD card it should be locked to prevent it being * accessed from a PC over USB. - * Other HALs use (IS_SD_PRINTING() || IS_SD_FILE_OPEN()) to check for access + * Other HALs use (card.isStillPrinting() || card.isFileOpen()) to check for access * but this won't reliably detect other file operations. To be safe we just lock * the drive whenever Marlin has it mounted. LCDs should include an Unmount * command so drives can be released as needed. diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h index 41c91e200f..68773bdf27 100644 --- a/Marlin/src/HAL/STM32F1/HAL.h +++ b/Marlin/src/HAL/STM32F1/HAL.h @@ -40,11 +40,9 @@ #include "../../inc/MarlinConfigPre.h" #if HAS_SD_HOST_DRIVE - #include "msc_sd.h" + #include "sd/msc_sd.h" #endif -#include "MarlinSerial.h" - // ------------------------ // Defines // ------------------------ @@ -64,90 +62,17 @@ #endif #endif -// ------------------------ -// Serial ports -// ------------------------ +// +// Serial Ports +// -#ifdef SERIAL_USB - typedef ForwardSerial1Class< USBSerial > DefaultSerial1; - extern DefaultSerial1 MSerial0; - #if HAS_SD_HOST_DRIVE - #define UsbSerial MarlinCompositeSerial - #else - #define UsbSerial MSerial0 - #endif -#endif - -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if ANY(STM32_HIGH_DENSITY, STM32_XL_DENSITY) - #define NUM_UARTS 5 -#else - #define NUM_UARTS 3 -#endif - -#if SERIAL_PORT == -1 - #define MYSERIAL1 UsbSerial -#elif WITHIN(SERIAL_PORT, 1, NUM_UARTS) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #define MYSERIAL1 MSERIAL(1) // dummy port - static_assert(false, "SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") -#endif - -#ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #define MYSERIAL2 UsbSerial - #elif WITHIN(SERIAL_PORT_2, 1, NUM_UARTS) - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #define MYSERIAL2 MSERIAL(1) // dummy port - static_assert(false, "SERIAL_PORT_2 must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 - #define MYSERIAL3 UsbSerial - #elif WITHIN(SERIAL_PORT_3, 1, NUM_UARTS) - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #else - #define MYSERIAL3 MSERIAL(1) // dummy port - static_assert(false, "SERIAL_PORT_3 must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") - #endif -#endif - -#ifdef MMU2_SERIAL_PORT - #if MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL UsbSerial - #elif WITHIN(MMU2_SERIAL_PORT, 1, NUM_UARTS) - #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) - #else - #define MMU2_SERIAL MSERIAL(1) // dummy port - static_assert(false, "MMU2_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") - #endif -#endif - -#ifdef LCD_SERIAL_PORT - #if LCD_SERIAL_PORT == -1 - #define LCD_SERIAL UsbSerial - #elif WITHIN(LCD_SERIAL_PORT, 1, NUM_UARTS) - #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) - #else - #define LCD_SERIAL MSERIAL(1) // dummy port - static_assert(false, "LCD_SERIAL_PORT must be from 1 to " STRINGIFY(NUM_UARTS) ". You can also use -1 if the board supports Native USB.") - #endif - #if HAS_DGUS_LCD - #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() - #endif -#endif +#include "MarlinSerial.h" /** * TODO: review this to return 1 for pins that are not analog input */ #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) (p) + #define analogInputToDigitalPin(p) pin_t(p) #endif #ifndef digitalPinHasPWM diff --git a/Marlin/src/HAL/STM32F1/HAL_N32.cpp b/Marlin/src/HAL/STM32F1/HAL_N32.cpp index 971a344f21..e45cbbbfff 100644 --- a/Marlin/src/HAL/STM32F1/HAL_N32.cpp +++ b/Marlin/src/HAL/STM32F1/HAL_N32.cpp @@ -612,7 +612,7 @@ void ADC_DMA_init() { * n32g452 - end ==============================================================================*/ -#define NS_PINRT(V...) do{ SERIAL_ECHO_START(); SERIAL_ECHOLNPAIR(V); }while(0) +#define NS_PINRT(V...) do{ SERIAL_ECHO_START(); SERIAL_ECHOLNPGM(V); }while(0) // Init the AD in continuous capture mode void MarlinHAL::adc_init() { @@ -622,7 +622,7 @@ void MarlinHAL::adc_init() { // GPIO settings reg_temp = ADC_RCC_APB2PCLKEN; - reg_temp |= 0x0f; // Make PORT mouth clock + reg_temp |= 0x0F; // Make PORT mouth clock ADC_RCC_APB2PCLKEN = reg_temp; //reg_temp = NS_GPIOC_PL_CFG; diff --git a/Marlin/src/HAL/STM32F1/HAL_N32.h b/Marlin/src/HAL/STM32F1/HAL_N32.h index 7162e2b971..719368f6f1 100644 --- a/Marlin/src/HAL/STM32F1/HAL_N32.h +++ b/Marlin/src/HAL/STM32F1/HAL_N32.h @@ -239,8 +239,8 @@ typedef struct { #define ADC_WORKMODE_SLOW_INTERL ((uint32_t)0x00080000) #define ADC_WORKMODE_ALTER_TRIG ((uint32_t)0x00090000) -#define ADC_EXT_TRIGCONV_T1_CC3 ((uint32_t)0x00040000) //!< For ADC1, ADC2 , ADC3 and ADC4 -#define ADC_EXT_TRIGCONV_NONE ((uint32_t)0x000E0000) //!< For ADC1, ADC2 , ADC3 and ADC4 +#define ADC_EXT_TRIGCONV_T1_CC3 ((uint32_t)0x00040000) //!< For ADC1, ADC2, ADC3, and ADC4 +#define ADC_EXT_TRIGCONV_NONE ((uint32_t)0x000E0000) //!< For ADC1, ADC2, ADC3, and ADC4 #define ADC_DAT_ALIGN_R ((uint32_t)0x00000000) #define ADC_DAT_ALIGN_L ((uint32_t)0x00000800) @@ -603,9 +603,9 @@ typedef struct { #define DMA_CHCFG7_PINC ((uint16_t)0x0040) //!< Peripheral increment mode #define DMA_CHCFG7_MINC ((uint16_t)0x0080) //!< Memory increment mode -#define DMA_CHCFG7_PSIZE , ((uint16_t)0x0300) //!< PSIZE[1:0] bits (Peripheral size) -#define DMA_CHCFG7_PSIZE_0 ((uint16_t)0x0100) //!< Bit 0 -#define DMA_CHCFG7_PSIZE_1 ((uint16_t)0x0200) //!< Bit 1 +#define DMA_CHCFG7_PSIZE ((uint16_t)0x0300) //!< PSIZE[1:0] bits (Peripheral size) +#define DMA_CHCFG7_PSIZE_0 ((uint16_t)0x0100) //!< Bit 0 +#define DMA_CHCFG7_PSIZE_1 ((uint16_t)0x0200) //!< Bit 1 #define DMA_CHCFG7_MSIZE ((uint16_t)0x0C00) //!< MSIZE[1:0] bits (Memory size) #define DMA_CHCFG7_MSIZE_0 ((uint16_t)0x0400) //!< Bit 0 @@ -627,9 +627,9 @@ typedef struct { #define DMA_CHCFG8_PINC ((uint16_t)0x0040) //!< Peripheral increment mode #define DMA_CHCFG8_MINC ((uint16_t)0x0080) //!< Memory increment mode -#define DMA_CHCFG8_PSIZE , ((uint16_t)0x0300) //!< PSIZE[1:0] bits (Peripheral size) -#define DMA_CHCFG8_PSIZE_0 ((uint16_t)0x0100) //!< Bit 0 -#define DMA_CHCFG8_PSIZE_1 ((uint16_t)0x0200) //!< Bit 1 +#define DMA_CHCFG8_PSIZE ((uint16_t)0x0300) //!< PSIZE[1:0] bits (Peripheral size) +#define DMA_CHCFG8_PSIZE_0 ((uint16_t)0x0100) //!< Bit 0 +#define DMA_CHCFG8_PSIZE_1 ((uint16_t)0x0200) //!< Bit 1 #define DMA_CHCFG8_MSIZE ((uint16_t)0x0C00) //!< MSIZE[1:0] bits (Memory size) #define DMA_CHCFG8_MSIZE_0 ((uint16_t)0x0400) //!< Bit 0 @@ -800,7 +800,7 @@ void ADC_StartCalibration(ADC_Module* NS_ADCx); void ADC_EnableDMA(ADC_Module* NS_ADCx, uint32_t Cmd); /**================================================================ - * Configure ADC interrupt enable enable + * Configure ADC interrupt enable ================================================================*/ void ADC_ConfigInt(ADC_Module* NS_ADCx, uint16_t ADC_IT, uint32_t Cmd); diff --git a/Marlin/src/HAL/STM32F1/MarlinSerial.h b/Marlin/src/HAL/STM32F1/MarlinSerial.h index 53bcd48476..c2dc5bd571 100644 --- a/Marlin/src/HAL/STM32F1/MarlinSerial.h +++ b/Marlin/src/HAL/STM32F1/MarlinSerial.h @@ -28,6 +28,29 @@ #include "../../inc/MarlinConfigPre.h" #include "../../core/serial_hook.h" +#ifdef SERIAL_USB + typedef ForwardSerial1Class< USBSerial > DefaultSerial1; + extern DefaultSerial1 MSerial0; + #if HAS_SD_HOST_DRIVE + #define UsbSerial MarlinCompositeSerial + #else + #define UsbSerial MSerial0 + #endif +#endif + +#define SERIAL_INDEX_MIN 1 +#if ANY(STM32_HIGH_DENSITY, STM32_XL_DENSITY) + #define SERIAL_INDEX_MAX 5 +#else + #define SERIAL_INDEX_MAX 3 +#endif +#define USB_SERIAL_PORT(...) UsbSerial +#include "../shared/serial_ports.h" + +#if defined(LCD_SERIAL_PORT) && ANY(HAS_DGUS_LCD, EXTENSIBLE_UI) + #define LCD_SERIAL_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() +#endif + // Increase priority of serial interrupts, to reduce overflow errors #define UART_IRQ_PRIO 1 diff --git a/Marlin/src/HAL/STM32F1/MinSerial.cpp b/Marlin/src/HAL/STM32F1/MinSerial.cpp index 8fb9133254..0d9a611d7e 100644 --- a/Marlin/src/HAL/STM32F1/MinSerial.cpp +++ b/Marlin/src/HAL/STM32F1/MinSerial.cpp @@ -92,7 +92,7 @@ void install_min_serial() { HAL_min_serial_out = &TX; } -#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them +#if NONE(DYNAMIC_VECTORTABLE, STM32F0xx) // Cortex M0 can't branch to a symbol that's too far, so we have a specific hack for them extern "C" { __attribute__((naked)) void JumpHandler_ASM() { __asm__ __volatile__ ( diff --git a/Marlin/src/HAL/STM32F1/README.md b/Marlin/src/HAL/STM32F1/README.md index b5bd5141fb..e289f35433 100644 --- a/Marlin/src/HAL/STM32F1/README.md +++ b/Marlin/src/HAL/STM32F1/README.md @@ -5,6 +5,7 @@ This HAL is for STM32F103 boards used with [Arduino STM32](https://github.com/ro Currently has been tested in Malyan M200 (103CBT6), SKRmini (103RCT6), Chitu 3d (103ZET6), and various 103VET6 boards. ### Main developers: + - Victorpv - xC000005 - thisiskeithb diff --git a/Marlin/src/HAL/STM32F1/SPI.h b/Marlin/src/HAL/STM32F1/SPI.h index 27bf684388..72063df699 100644 --- a/Marlin/src/HAL/STM32F1/SPI.h +++ b/Marlin/src/HAL/STM32F1/SPI.h @@ -33,12 +33,14 @@ #include #include +#include "../../core/macros.h" // for PIN_EXISTS + // Number of SPI ports -#ifdef BOARD_SPI3_SCK_PIN +#if PIN_EXISTS(BOARD_SPI3_SCK) #define BOARD_NR_SPI 3 -#elif defined(BOARD_SPI2_SCK_PIN) +#elif PIN_EXISTS(BOARD_SPI2_SCK) #define BOARD_NR_SPI 2 -#elif defined(BOARD_SPI1_SCK_PIN) +#elif PIN_EXISTS(BOARD_SPI1_SCK) #define BOARD_NR_SPI 1 #endif diff --git a/Marlin/src/HAL/STM32F1/adc.h b/Marlin/src/HAL/STM32F1/adc.h index 25f4a7ce16..a2f5652de0 100644 --- a/Marlin/src/HAL/STM32F1/adc.h +++ b/Marlin/src/HAL/STM32F1/adc.h @@ -44,7 +44,8 @@ enum ADCIndex : uint8_t { OPTITEM(HAS_TEMP_ADC_COOLER, TEMP_COOLER ) OPTITEM(HAS_TEMP_ADC_BOARD, TEMP_BOARD ) OPTITEM(HAS_TEMP_ADC_SOC, TEMP_SOC ) - OPTITEM(FILAMENT_WIDTH_SENSOR, FILWIDTH ) + OPTITEM(HAS_FILWIDTH_ADC, FILWIDTH ) + OPTITEM(HAS_FILWIDTH2_ADC, FILWIDTH2 ) OPTITEM(HAS_ADC_BUTTONS, ADC_KEY ) OPTITEM(HAS_JOY_ADC_X, JOY_X ) OPTITEM(HAS_JOY_ADC_Y, JOY_Y ) diff --git a/Marlin/src/HAL/STM32F1/build_flags.py b/Marlin/src/HAL/STM32F1/build_flags.py deleted file mode 100755 index 970ca8b767..0000000000 --- a/Marlin/src/HAL/STM32F1/build_flags.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import print_function -import sys - -#dynamic build flags for generic compile options -if __name__ == "__main__": - args = " ".join([ "-std=gnu++14", - "-Os", - "-mcpu=cortex-m3", - "-mthumb", - - "-fsigned-char", - "-fno-move-loop-invariants", - "-fno-strict-aliasing", - "-fsingle-precision-constant", - - "--specs=nano.specs", - "--specs=nosys.specs", - - "-IMarlin/src/HAL/STM32F1", - - "-MMD", - "-MP", - "-DTARGET_STM32F1" - ]) - - for i in range(1, len(sys.argv)): - args += " " + sys.argv[i] - - print(args) - -# extra script for linker options -else: - import pioutil - if pioutil.is_pio_build(): - from SCons.Script import DefaultEnvironment - env = DefaultEnvironment() - env.Append( - ARFLAGS=["rcs"], - - ASFLAGS=["-x", "assembler-with-cpp"], - - CXXFLAGS=[ - "-fabi-version=0", - "-fno-use-cxa-atexit", - "-fno-threadsafe-statics" - ], - LINKFLAGS=[ - "-Os", - "-mcpu=cortex-m3", - "-ffreestanding", - "-mthumb", - "--specs=nano.specs", - "--specs=nosys.specs", - "-u_printf_float", - ], - ) diff --git a/Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp b/Marlin/src/HAL/STM32F1/eeprom/eeprom_bl24cxx.cpp similarity index 95% rename from Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp rename to Marlin/src/HAL/STM32F1/eeprom/eeprom_bl24cxx.cpp index 1252e77b0b..9e96a741bf 100644 --- a/Marlin/src/HAL/STM32F1/eeprom_bl24cxx.cpp +++ b/Marlin/src/HAL/STM32F1/eeprom/eeprom_bl24cxx.cpp @@ -26,12 +26,12 @@ * with simple implementations supplied by Marlin. */ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" // // PersistentStore diff --git a/Marlin/src/HAL/STM32F1/eeprom_flash.cpp b/Marlin/src/HAL/STM32F1/eeprom/eeprom_flash.cpp similarity index 85% rename from Marlin/src/HAL/STM32F1/eeprom_flash.cpp rename to Marlin/src/HAL/STM32F1/eeprom/eeprom_flash.cpp index afdfefd5f0..2292c02203 100644 --- a/Marlin/src/HAL/STM32F1/eeprom_flash.cpp +++ b/Marlin/src/HAL/STM32F1/eeprom/eeprom_flash.cpp @@ -28,18 +28,18 @@ #ifdef __STM32F1__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(FLASH_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_api.h" #include #include // Store settings in the last two pages #ifndef MARLIN_EEPROM_SIZE - #define MARLIN_EEPROM_SIZE ((EEPROM_PAGE_SIZE) * 2) + #define MARLIN_EEPROM_SIZE ((EEPROM_PAGE_SIZE) * 2UL) #endif size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE - eeprom_exclude_size; } @@ -47,14 +47,14 @@ static uint8_t ram_eeprom[MARLIN_EEPROM_SIZE] __attribute__((aligned(4))) = {0}; static bool eeprom_dirty = false; bool PersistentStore::access_start() { - const uint32_t *source = reinterpret_cast(EEPROM_PAGE0_BASE); - uint32_t *destination = reinterpret_cast(ram_eeprom); + const uint32_t *src = reinterpret_cast(EEPROM_PAGE0_BASE); + uint32_t *dst = reinterpret_cast(ram_eeprom); static_assert(0 == (MARLIN_EEPROM_SIZE) % 4, "MARLIN_EEPROM_SIZE is corrupted. (Must be a multiple of 4.)"); // Ensure copying as uint32_t is safe constexpr size_t eeprom_size_u32 = (MARLIN_EEPROM_SIZE) / 4; - for (size_t i = 0; i < eeprom_size_u32; ++i, ++destination, ++source) - *destination = *source; + for (size_t i = 0; i < eeprom_size_u32; ++i, ++dst, ++src) + *dst = *src; eeprom_dirty = false; return true; @@ -80,9 +80,9 @@ bool PersistentStore::access_finish() { status = FLASH_ErasePage(EEPROM_PAGE1_BASE); if (status != FLASH_COMPLETE) ACCESS_FINISHED(true); - const uint16_t *source = reinterpret_cast(ram_eeprom); - for (size_t i = 0; i < MARLIN_EEPROM_SIZE; i += 2, ++source) { - if (FLASH_ProgramHalfWord(EEPROM_PAGE0_BASE + i, *source) != FLASH_COMPLETE) + const uint16_t *src = reinterpret_cast(ram_eeprom); + for (size_t i = 0; i < long(MARLIN_EEPROM_SIZE); i += 2, ++src) { + if (FLASH_ProgramHalfWord(EEPROM_PAGE0_BASE + i, *src) != FLASH_COMPLETE) ACCESS_FINISHED(false); } diff --git a/Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp b/Marlin/src/HAL/STM32F1/eeprom/eeprom_if_iic.cpp similarity index 90% rename from Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp rename to Marlin/src/HAL/STM32F1/eeprom/eeprom_if_iic.cpp index 78b7af0b04..e7e7fc1db1 100644 --- a/Marlin/src/HAL/STM32F1/eeprom_if_iic.cpp +++ b/Marlin/src/HAL/STM32F1/eeprom/eeprom_if_iic.cpp @@ -27,12 +27,12 @@ #ifdef __STM32F1__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(IIC_BL24CXX_EEPROM) -#include "../../libs/BL24CXX.h" -#include "../shared/eeprom_if.h" +#include "../../../libs/BL24CXX.h" +#include "../../shared/eeprom_if.h" void eeprom_init() { BL24CXX::init(); } @@ -42,7 +42,7 @@ void eeprom_init() { BL24CXX::init(); } void eeprom_write_byte(uint8_t *pos, uint8_t value) { const unsigned eeprom_address = (unsigned)pos; - return BL24CXX::writeOneByte(eeprom_address, value); + BL24CXX::writeOneByte(eeprom_address, value); } uint8_t eeprom_read_byte(uint8_t *pos) { diff --git a/Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp b/Marlin/src/HAL/STM32F1/eeprom/eeprom_sdcard.cpp similarity index 93% rename from Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp rename to Marlin/src/HAL/STM32F1/eeprom/eeprom_sdcard.cpp index 6b72193422..e41c6e0aa0 100644 --- a/Marlin/src/HAL/STM32F1/eeprom_sdcard.cpp +++ b/Marlin/src/HAL/STM32F1/eeprom/eeprom_sdcard.cpp @@ -27,12 +27,12 @@ #ifdef __STM32F1__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if ENABLED(SDCARD_EEPROM_EMULATION) -#include "../shared/eeprom_api.h" -#include "../../sd/cardreader.h" +#include "../../shared/eeprom_api.h" +#include "../../../sd/cardreader.h" #define EEPROM_FILENAME "eeprom.dat" @@ -53,7 +53,7 @@ bool PersistentStore::access_start() { int bytes_read = file.read(HAL_eeprom_data, MARLIN_EEPROM_SIZE); if (bytes_read < 0) return false; - for (; bytes_read < MARLIN_EEPROM_SIZE; bytes_read++) + for (; bytes_read < long(MARLIN_EEPROM_SIZE); bytes_read++) HAL_eeprom_data[bytes_read] = 0xFF; file.close(); return true; diff --git a/Marlin/src/HAL/STM32F1/eeprom_wired.cpp b/Marlin/src/HAL/STM32F1/eeprom/eeprom_wired.cpp similarity index 95% rename from Marlin/src/HAL/STM32F1/eeprom_wired.cpp rename to Marlin/src/HAL/STM32F1/eeprom/eeprom_wired.cpp index bfb7718094..a1464ab983 100644 --- a/Marlin/src/HAL/STM32F1/eeprom_wired.cpp +++ b/Marlin/src/HAL/STM32F1/eeprom/eeprom_wired.cpp @@ -26,12 +26,12 @@ #ifdef __STM32F1__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if USE_WIRED_EEPROM -#include "../shared/eeprom_if.h" -#include "../shared/eeprom_api.h" +#include "../../shared/eeprom_if.h" +#include "../../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE #error "MARLIN_EEPROM_SIZE is required for I2C / SPI EEPROM." diff --git a/Marlin/src/HAL/STM32F1/endstop_interrupts.h b/Marlin/src/HAL/STM32F1/endstop_interrupts.h index d11b3bf505..6724bf3456 100644 --- a/Marlin/src/HAL/STM32F1/endstop_interrupts.h +++ b/Marlin/src/HAL/STM32F1/endstop_interrupts.h @@ -70,6 +70,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/STM32F1/fastio.h b/Marlin/src/HAL/STM32F1/fastio.h index 5b3ebaa52c..8bb986b466 100644 --- a/Marlin/src/HAL/STM32F1/fastio.h +++ b/Marlin/src/HAL/STM32F1/fastio.h @@ -44,6 +44,7 @@ #define SET_INPUT_PULLUP(IO) _SET_MODE(IO, GPIO_INPUT_PU) #define SET_INPUT_PULLDOWN(IO) _SET_MODE(IO, GPIO_INPUT_PD) #define SET_OUTPUT(IO) OUT_WRITE(IO, LOW) +#define SET_OUTPUT_OD(IO) OUT_WRITE_OD(IO, LOW) #define SET_PWM(IO) pinMode(IO, PWM) // do{ gpio_set_mode(PIN_MAP[pin].gpio_device, PIN_MAP[pin].gpio_bit, GPIO_AF_OUTPUT_PP); timer_set_mode(PIN_MAP[pin].timer_device, PIN_MAP[pin].timer_channel, TIMER_PWM); }while(0) #define SET_PWM_OD(IO) pinMode(IO, PWM_OPEN_DRAIN) diff --git a/Marlin/src/HAL/STM32F1/pinsDebug.h b/Marlin/src/HAL/STM32F1/pinsDebug.h index 6f8e48f455..9191614587 100644 --- a/Marlin/src/HAL/STM32F1/pinsDebug.h +++ b/Marlin/src/HAL/STM32F1/pinsDebug.h @@ -22,11 +22,23 @@ #pragma once /** - * Support routines for MAPLE_STM32F1 - */ - -/** - * Translation of routines & variables used by pinsDebug.h + * Pins Debugging for Maple STM32F1 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #ifndef BOARD_NR_GPIO_PINS // Only in MAPLE_STM32F1 @@ -39,12 +51,12 @@ extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS]; #define NUM_DIGITAL_PINS BOARD_NR_GPIO_PINS #define NUMBER_PINS_TOTAL BOARD_NR_GPIO_PINS -#define VALID_PIN(pin) (pin >= 0 && pin < BOARD_NR_GPIO_PINS) -#define GET_ARRAY_PIN(p) pin_t(pin_array[p].pin) -#define digitalRead_mod(p) extDigitalRead(p) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%3hd "), int16_t(p)); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define isValidPin(P) (P >= 0 && P < BOARD_NR_GPIO_PINS) +#define getPinByIndex(x) pin_t(pin_array[x].pin) +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%3hd "), int16_t(P)); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 21 // space needed to be pretty if not first name assigned to a pin // pins that will cause hang/reset/disconnect in M43 Toggle and Watch utilities @@ -52,10 +64,10 @@ extern const stm32_pin_info PIN_MAP[BOARD_NR_GPIO_PINS]; #define M43_NEVER_TOUCH(Q) (Q >= 9 && Q <= 12) // SERIAL/USB pins PA9(TX) PA10(RX) #endif -int8_t get_pin_mode(const pin_t pin) { return VALID_PIN(pin) ? _GET_MODE(pin) : -1; } +int8_t get_pin_mode(const pin_t pin) { return isValidPin(pin) ? _GET_MODE(pin) : -1; } -pin_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t pin) { - if (!VALID_PIN(pin)) return -1; +int8_t digitalPinToAnalogIndex(const pin_t pin) { + if (!isValidPin(pin)) return -1; pin_t adc_channel = pin_t(PIN_MAP[pin].adc_channel); #ifdef NUM_ANALOG_INPUTS if (adc_channel >= NUM_ANALOG_INPUTS) adc_channel = (pin_t)ADCx; @@ -63,8 +75,8 @@ pin_t DIGITAL_PIN_TO_ANALOG_PIN(const pin_t pin) { return adc_channel; } -bool IS_ANALOG(const pin_t pin) { - if (!VALID_PIN(pin)) return false; +bool isAnalogPin(const pin_t pin) { + if (!isValidPin(pin)) return false; if (PIN_MAP[pin].adc_channel != ADCx) { #ifdef NUM_ANALOG_INPUTS if (PIN_MAP[pin].adc_channel >= NUM_ANALOG_INPUTS) return false; @@ -74,13 +86,13 @@ bool IS_ANALOG(const pin_t pin) { return false; } -bool GET_PINMODE(const pin_t pin) { - return VALID_PIN(pin) && !IS_INPUT(pin); +bool getValidPinMode(const pin_t pin) { + return isValidPin(pin) && !IS_INPUT(pin); } -bool GET_ARRAY_IS_DIGITAL(const int16_t array_pin) { - const pin_t pin = GET_ARRAY_PIN(array_pin); - return (!IS_ANALOG(pin) +bool getPinIsDigitalByIndex(const int16_t index) { + const pin_t pin = getPinByIndex(index); + return (!isAnalogPin(pin) #ifdef NUM_ANALOG_INPUTS || PIN_MAP[pin].adc_channel >= NUM_ANALOG_INPUTS #endif @@ -89,7 +101,7 @@ bool GET_ARRAY_IS_DIGITAL(const int16_t array_pin) { #include "../../inc/MarlinConfig.h" // Allow pins/pins.h to set density -void pwm_details(const pin_t pin) { +void printPinPWM(const pin_t pin) { if (PWM_PIN(pin)) { timer_dev * const tdev = PIN_MAP[pin].timer_device; const uint8_t channel = PIN_MAP[pin].timer_channel; @@ -111,7 +123,7 @@ void pwm_details(const pin_t pin) { bool pwm_status(const pin_t pin) { return PWM_PIN(pin); } -void print_port(const pin_t pin) { +void printPinPort(const pin_t pin) { const char port = 'A' + char(pin >> 4); // pin div 16 const int16_t gbit = PIN_MAP[pin].gpio_bit; char buffer[8]; diff --git a/Marlin/src/HAL/STM32F1/msc_sd.cpp b/Marlin/src/HAL/STM32F1/sd/msc_sd.cpp similarity index 96% rename from Marlin/src/HAL/STM32F1/msc_sd.cpp rename to Marlin/src/HAL/STM32F1/sd/msc_sd.cpp index 067b46eb8b..5f1ba30c8a 100644 --- a/Marlin/src/HAL/STM32F1/msc_sd.cpp +++ b/Marlin/src/HAL/STM32F1/sd/msc_sd.cpp @@ -22,20 +22,21 @@ */ #ifdef __STM32F1__ -#include "../../inc/MarlinConfigPre.h" +#include "../../../inc/MarlinConfigPre.h" #if HAS_SD_HOST_DRIVE #include "msc_sd.h" -#include "SPI.h" -#include "usb_reg_map.h" +#include "../SPI.h" + +#include #define PRODUCT_ID 0x29 USBMassStorage MarlinMSC; Serial1Class MarlinCompositeSerial(true); -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if SD_CONNECTION_IS(ONBOARD) diff --git a/Marlin/src/HAL/STM32F1/msc_sd.h b/Marlin/src/HAL/STM32F1/sd/msc_sd.h similarity index 93% rename from Marlin/src/HAL/STM32F1/msc_sd.h rename to Marlin/src/HAL/STM32F1/sd/msc_sd.h index 371211efc6..871d98eaf2 100644 --- a/Marlin/src/HAL/STM32F1/msc_sd.h +++ b/Marlin/src/HAL/STM32F1/sd/msc_sd.h @@ -24,8 +24,8 @@ #include -#include "../../inc/MarlinConfigPre.h" -#include "../../core/serial_hook.h" +#include "../../../inc/MarlinConfigPre.h" +#include "../../../core/serial_hook.h" extern USBMassStorage MarlinMSC; extern Serial1Class MarlinCompositeSerial; diff --git a/Marlin/src/HAL/STM32F1/onboard_sd.cpp b/Marlin/src/HAL/STM32F1/sd/onboard_sd.cpp similarity index 99% rename from Marlin/src/HAL/STM32F1/onboard_sd.cpp rename to Marlin/src/HAL/STM32F1/sd/onboard_sd.cpp index a3d8dcb2d5..ace1204b75 100644 --- a/Marlin/src/HAL/STM32F1/onboard_sd.cpp +++ b/Marlin/src/HAL/STM32F1/sd/onboard_sd.cpp @@ -13,13 +13,13 @@ #ifdef __STM32F1__ -#include "../../inc/MarlinConfig.h" +#include "../../../inc/MarlinConfig.h" #if SD_CONNECTION_IS(ONBOARD) #include "onboard_sd.h" -#include "SPI.h" -#include "fastio.h" +#include "../SPI.h" +#include "../fastio.h" #ifndef ONBOARD_SPI_DEVICE #define ONBOARD_SPI_DEVICE SPI_DEVICE diff --git a/Marlin/src/HAL/STM32F1/onboard_sd.h b/Marlin/src/HAL/STM32F1/sd/onboard_sd.h similarity index 100% rename from Marlin/src/HAL/STM32F1/onboard_sd.h rename to Marlin/src/HAL/STM32F1/sd/onboard_sd.h diff --git a/Marlin/src/HAL/STM32F1/sdio.cpp b/Marlin/src/HAL/STM32F1/sd/sdio.cpp similarity index 98% rename from Marlin/src/HAL/STM32F1/sdio.cpp rename to Marlin/src/HAL/STM32F1/sd/sdio.cpp index 23f984eff3..d657fd3b0e 100644 --- a/Marlin/src/HAL/STM32F1/sdio.cpp +++ b/Marlin/src/HAL/STM32F1/sd/sdio.cpp @@ -19,11 +19,11 @@ * along with this program. If not, see . * */ -#ifdef ARDUINO_ARCH_STM32F1 +#ifdef __STM32F1__ #include -#include "../../inc/MarlinConfig.h" // Allow pins/pins.h to set density +#include "../../../inc/MarlinConfig.h" // Allow pins/pins.h to set density #if ANY(STM32_HIGH_DENSITY, STM32_XL_DENSITY) @@ -145,7 +145,7 @@ bool SDIO_ReadBlock(uint32_t blockAddress, uint8_t *data) { return false; } -uint32_t millis(); +unsigned long millis(); bool SDIO_WriteBlock(uint32_t blockAddress, const uint8_t *data) { if (SDIO_GetCardState() != SDIO_CARD_TRANSFER) return false; @@ -219,7 +219,7 @@ bool SDIO_CmdAppSetBusWidth(uint32_t rsa, uint32_t argument) { bool SDIO_CmdAppOperCommand(uint32_t sdType) { if (!SDIO_CmdAppCommand(0)) return false; - SDIO_SendCommand(ACMD41_SD_APP_OP_COND , SDMMC_VOLTAGE_WINDOW_SD | sdType); + SDIO_SendCommand(ACMD41_SD_APP_OP_COND, SDMMC_VOLTAGE_WINDOW_SD | sdType); return SDIO_GetCmdResp3(); } @@ -308,4 +308,4 @@ bool SDIO_GetCmdResp7() { } #endif // STM32_HIGH_DENSITY || STM32_XL_DENSITY -#endif // ARDUINO_ARCH_STM32F1 +#endif // __STM32F1__ diff --git a/Marlin/src/HAL/STM32F1/sdio.h b/Marlin/src/HAL/STM32F1/sd/sdio.h similarity index 98% rename from Marlin/src/HAL/STM32F1/sdio.h rename to Marlin/src/HAL/STM32F1/sd/sdio.h index 08c884666d..ffcd354915 100644 --- a/Marlin/src/HAL/STM32F1/sdio.h +++ b/Marlin/src/HAL/STM32F1/sd/sdio.h @@ -21,7 +21,7 @@ */ #pragma once -#include "../../inc/MarlinConfig.h" // Allow pins/pins.h to override SDIO clock / retries +#include "../../../inc/MarlinConfig.h" // Allow pins/pins.h to override SDIO clock / retries #include #include diff --git a/Marlin/src/HAL/STM32F1/spi_pins.h b/Marlin/src/HAL/STM32F1/spi_pins.h index 3d3c8f8d2f..d9c5e1f8c6 100644 --- a/Marlin/src/HAL/STM32F1/spi_pins.h +++ b/Marlin/src/HAL/STM32F1/spi_pins.h @@ -46,11 +46,6 @@ #ifndef SD_MOSI_PIN #define SD_MOSI_PIN PA7 #endif -#ifndef SD_SS_PIN - #define SD_SS_PIN PA4 -#endif -#undef SDSS -#define SDSS SD_SS_PIN #ifndef SPI_DEVICE #define SPI_DEVICE 1 diff --git a/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp index 51f70b9365..73b0e7efb9 100644 --- a/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp +++ b/Marlin/src/HAL/STM32F1/tft/tft_fsmc.cpp @@ -86,7 +86,6 @@ __attribute__((always_inline)) __STATIC_INLINE void __DSB() { #define FSMC_ADDRESS_SETUP_TIME 15 // AddressSetupTime #define FSMC_DATA_SETUP_TIME 15 // DataSetupTime -static uint8_t fsmcInit = 0; void TFT_FSMC::init() { uint8_t cs = FSMC_CS_PIN, rs = FSMC_RS_PIN; uint32_t controllerAddress; @@ -99,8 +98,9 @@ void TFT_FSMC::init() { struct fsmc_nor_psram_reg_map* fsmcPsramRegion; + static bool fsmcInit = false; if (fsmcInit) return; - fsmcInit = 1; + fsmcInit = true; switch (cs) { case FSMC_CS_NE1: controllerAddress = (uint32_t)FSMC_NOR_PSRAM_REGION1; fsmcPsramRegion = FSMC_NOR_PSRAM1_BASE; break; diff --git a/Marlin/src/HAL/STM32F1/timers.h b/Marlin/src/HAL/STM32F1/timers.h index 89a609c2c3..456278db2f 100644 --- a/Marlin/src/HAL/STM32F1/timers.h +++ b/Marlin/src/HAL/STM32F1/timers.h @@ -40,7 +40,7 @@ */ typedef uint16_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFU #define HAL_TIMER_RATE uint32_t(F_CPU) // frequency of timers peripherals diff --git a/Marlin/src/HAL/STM32F1/u8g/LCD_defines.h b/Marlin/src/HAL/STM32F1/u8g/LCD_defines.h index 4bd461cb39..9d0137ae6a 100644 --- a/Marlin/src/HAL/STM32F1/u8g/LCD_defines.h +++ b/Marlin/src/HAL/STM32F1/u8g/LCD_defines.h @@ -25,7 +25,7 @@ * STM32F1 (Maple) LCD-specific defines */ -uint8_t u8g_com_HAL_STM32F1_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); +uint8_t u8g_com_HAL_STM32F1_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // u8g_com_stm32duino_swspi.cpp uint8_t u8g_com_stm32duino_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr); // See U8glib-HAL #define U8G_COM_HAL_SW_SPI_FN u8g_com_HAL_STM32F1_sw_spi_fn diff --git a/Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp b/Marlin/src/HAL/STM32F1/u8g/u8g_com_stm32duino_swspi.cpp similarity index 90% rename from Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp rename to Marlin/src/HAL/STM32F1/u8g/u8g_com_stm32duino_swspi.cpp index fc421897c4..28302edaf5 100644 --- a/Marlin/src/HAL/STM32F1/dogm/u8g_com_stm32duino_swspi.cpp +++ b/Marlin/src/HAL/STM32F1/u8g/u8g_com_stm32duino_swspi.cpp @@ -89,7 +89,7 @@ static inline uint8_t swSpiTransfer_mode_3(uint8_t b, const uint8_t spi_speed, c } static void u8g_sw_spi_shift_out(uint8_t val) { - #if ENABLED(FYSETC_MINI_12864) + #if U8G_SPI_USE_MODE_3 swSpiTransfer_mode_3(val, SPI_speed); #else swSpiTransfer_mode_0(val, SPI_speed); @@ -123,15 +123,15 @@ uint8_t u8g_com_HAL_STM32F1_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, break; case U8G_COM_MSG_CHIP_SELECT: - #if ENABLED(FYSETC_MINI_12864) // This LCD SPI is running mode 3 while SD card is running mode 0 - if (arg_val) { // SCK idle state needs to be set to the proper idle state before - // the next chip select goes active - WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active + #if U8G_SPI_USE_MODE_3 // This LCD SPI is running mode 3 while SD card is running mode 0 + if (arg_val) { // SCK idle state needs to be set to the proper idle state before + // the next chip select goes active + WRITE(DOGLCD_SCK, HIGH); // Set SCK to mode 3 idle state before CS goes active WRITE(DOGLCD_CS, LOW); } else { WRITE(DOGLCD_CS, HIGH); - WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive + WRITE(DOGLCD_SCK, LOW); // Set SCK to mode 0 idle state after CS goes inactive } #else WRITE(DOGLCD_CS, !arg_val); diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.cpp b/Marlin/src/HAL/TEENSY31_32/HAL.cpp index 2892368967..f54025991d 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.cpp +++ b/Marlin/src/HAL/TEENSY31_32/HAL.cpp @@ -37,10 +37,20 @@ #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) -#if WITHIN(SERIAL_PORT, 0, 3) +#if WITHIN(SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) IMPLEMENT_SERIAL(SERIAL_PORT); -#else - #error "SERIAL_PORT must be from 0 to 3." +#endif +#if defined(SERIAL_PORT_2) && WITHIN(SERIAL_PORT_2, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_2); +#endif +#if defined(SERIAL_PORT_3) && WITHIN(SERIAL_PORT_3, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_3); +#endif +#if defined(MMU_SERIAL_PORT) && WITHIN(MMU_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(MMU_SERIAL_PORT); +#endif +#if defined(LCD_SERIAL_PORT) && WITHIN(LCD_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(LCD_SERIAL_PORT); #endif USBSerialType USBSerial(false, SerialUSB); diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.h b/Marlin/src/HAL/TEENSY31_32/HAL.h index 16594c16a8..03610c65f8 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.h +++ b/Marlin/src/HAL/TEENSY31_32/HAL.h @@ -64,17 +64,10 @@ typedef ForwardSerial1Class USBSerialType; extern USBSerialType USBSerial; -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if SERIAL_PORT == -1 - #define MYSERIAL1 USBSerial -#elif WITHIN(SERIAL_PORT, 0, 3) - DECLARE_SERIAL(SERIAL_PORT); - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #error "The required SERIAL_PORT must be from 0 to 3, or -1 for Native USB." -#endif +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#define USB_SERIAL_PORT(...) USBSerial +#include "../shared/serial_ports.h" // ------------------------ // Types @@ -98,7 +91,7 @@ uint32_t __get_PRIMASK(void); // CMSIS // ------------------------ #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) + #define analogInputToDigitalPin(p) pin_t((p < 12U) ? (p) + 54U : -1) #endif #define HAL_ADC_VREF_MV 3300 diff --git a/Marlin/src/HAL/TEENSY31_32/endstop_interrupts.h b/Marlin/src/HAL/TEENSY31_32/endstop_interrupts.h index 954eb625a5..798ca4f0cb 100644 --- a/Marlin/src/HAL/TEENSY31_32/endstop_interrupts.h +++ b/Marlin/src/HAL/TEENSY31_32/endstop_interrupts.h @@ -64,6 +64,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/TEENSY31_32/fastio.h b/Marlin/src/HAL/TEENSY31_32/fastio.h index 622799ec8c..b582c7bfec 100644 --- a/Marlin/src/HAL/TEENSY31_32/fastio.h +++ b/Marlin/src/HAL/TEENSY31_32/fastio.h @@ -53,17 +53,17 @@ #define _SET_INPUT(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1); \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 0; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 0; \ }while(0) #define _SET_OUTPUT(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1)|PORT_PCR_SRE|PORT_PCR_DSE; \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 1; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 1; \ }while(0) #define _SET_INPUT_PULLUP(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 0; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 0; \ }while(0) #define _IS_INPUT(P) ((CORE_PIN ## P ## _DDRREG & CORE_PIN ## P ## _BITMASK) == 0) diff --git a/Marlin/src/HAL/TEENSY31_32/pinsDebug.h b/Marlin/src/HAL/TEENSY31_32/pinsDebug.h index d4a91ce801..741909bf0a 100644 --- a/Marlin/src/HAL/TEENSY31_32/pinsDebug.h +++ b/Marlin/src/HAL/TEENSY31_32/pinsDebug.h @@ -1 +1,71 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2019 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + #error "PINS_DEBUGGING is not yet supported for Teensy 3.1 / 3.2!" + +/** + * Pins Debugging for ESP32 + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) + */ + +#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS +#define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin + +#define digitalRead_mod(P) extDigitalRead(P) +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%02d"), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) +#define getPinByIndex(x) pin_array[x].pin +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define digitalPinToAnalogIndex(P) int(P - analogInputToDigitalPin(0)) +#define isAnalogPin(P) WITHIN(P, pin_t(analogInputToDigitalPin(0)), pin_t(analogInputToDigitalPin(NUM_ANALOG_INPUTS - 1))) +bool pwm_status(const pin_t) { return false; } + +void printPinPort(const pin_t) {} + +static bool getValidPinMode(const pin_t pin) { + return isValidPin(pin) /* && !IS_INPUT(pin) */ ; +} + +void printPinPWM(const int32_t pin) { + if (pwm_status(pin)) { + //uint32_t chan = g_APinDescription[pin].ulPWMChannel TODO when fast pwm is operative; + //SERIAL_ECHOPGM("PWM = ", duty); + } +} diff --git a/Marlin/src/HAL/TEENSY31_32/spi_pins.h b/Marlin/src/HAL/TEENSY31_32/spi_pins.h index 6d0d05f85a..dbd0bb9174 100644 --- a/Marlin/src/HAL/TEENSY31_32/spi_pins.h +++ b/Marlin/src/HAL/TEENSY31_32/spi_pins.h @@ -24,4 +24,3 @@ #define SD_SCK_PIN 13 #define SD_MISO_PIN 12 #define SD_MOSI_PIN 11 -#define SD_SS_PIN 20 // SDSS // A.28, A.29, B.21, C.26, C.29 diff --git a/Marlin/src/HAL/TEENSY31_32/timers.h b/Marlin/src/HAL/TEENSY31_32/timers.h index 9fcbb6f232..aeb8f2388a 100644 --- a/Marlin/src/HAL/TEENSY31_32/timers.h +++ b/Marlin/src/HAL/TEENSY31_32/timers.h @@ -34,14 +34,14 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define FTM0_TIMER_PRESCALE 8 #define FTM1_TIMER_PRESCALE 4 #define FTM0_TIMER_PRESCALE_BITS 0b011 #define FTM1_TIMER_PRESCALE_BITS 0b010 -#define FTM0_TIMER_RATE (F_BUS / (FTM0_TIMER_PRESCALE)) // 60MHz / 8 = 7500kHz +#define FTM0_TIMER_RATE (F_BUS / (FTM0_TIMER_PRESCALE)) // 60MHz / 8 = 7.5MHz #define FTM1_TIMER_RATE (F_BUS / (FTM1_TIMER_PRESCALE)) // 60MHz / 4 = 15MHz #define HAL_TIMER_RATE (FTM0_TIMER_RATE) diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.cpp b/Marlin/src/HAL/TEENSY35_36/HAL.cpp index bc02ac1c45..f41582be30 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.cpp +++ b/Marlin/src/HAL/TEENSY35_36/HAL.cpp @@ -37,10 +37,21 @@ #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) -#if WITHIN(SERIAL_PORT, 0, 3) +#if WITHIN(SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) IMPLEMENT_SERIAL(SERIAL_PORT); #endif - +#if defined(SERIAL_PORT_2) && WITHIN(SERIAL_PORT_2, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_2); +#endif +#if defined(SERIAL_PORT_3) && WITHIN(SERIAL_PORT_3, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_3); +#endif +#if defined(MMU_SERIAL_PORT) && WITHIN(MMU_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(MMU_SERIAL_PORT); +#endif +#if defined(LCD_SERIAL_PORT) && WITHIN(LCD_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(LCD_SERIAL_PORT); +#endif USBSerialType USBSerial(false, SerialUSB); // ------------------------ diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.h b/Marlin/src/HAL/TEENSY35_36/HAL.h index 6921334003..0f229000d4 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.h +++ b/Marlin/src/HAL/TEENSY35_36/HAL.h @@ -70,17 +70,10 @@ typedef ForwardSerial1Class USBSerialType; extern USBSerialType USBSerial; -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if SERIAL_PORT == -1 - #define MYSERIAL1 USBSerial -#elif WITHIN(SERIAL_PORT, 0, 3) - #define MYSERIAL1 MSERIAL(SERIAL_PORT) - DECLARE_SERIAL(SERIAL_PORT); -#else - #error "SERIAL_PORT must be from 0 to 3, or -1 for Native USB." -#endif +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 3 +#define USB_SERIAL_PORT(...) USBSerial +#include "../shared/serial_ports.h" // ------------------------ // Types @@ -103,7 +96,7 @@ typedef int8_t pin_t; // ------------------------ #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) + #define analogInputToDigitalPin(p) pin_t((p < 12U) ? (p) + 54U : -1) #endif #define HAL_ADC_VREF_MV 3300 diff --git a/Marlin/src/HAL/TEENSY35_36/endstop_interrupts.h b/Marlin/src/HAL/TEENSY35_36/endstop_interrupts.h index bc8e177fb7..a938041b5a 100644 --- a/Marlin/src/HAL/TEENSY35_36/endstop_interrupts.h +++ b/Marlin/src/HAL/TEENSY35_36/endstop_interrupts.h @@ -63,6 +63,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/TEENSY35_36/fastio.h b/Marlin/src/HAL/TEENSY35_36/fastio.h index 622799ec8c..b582c7bfec 100644 --- a/Marlin/src/HAL/TEENSY35_36/fastio.h +++ b/Marlin/src/HAL/TEENSY35_36/fastio.h @@ -53,17 +53,17 @@ #define _SET_INPUT(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1); \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 0; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 0; \ }while(0) #define _SET_OUTPUT(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1)|PORT_PCR_SRE|PORT_PCR_DSE; \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 1; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 1; \ }while(0) #define _SET_INPUT_PULLUP(P) do{ \ CORE_PIN ## P ## _CONFIG = PORT_PCR_MUX(1) | PORT_PCR_PE | PORT_PCR_PS; \ - GPIO_BITBAND(CORE_PIN ## P ## _DDRREG , CORE_PIN ## P ## _BIT) = 0; \ + GPIO_BITBAND(CORE_PIN ## P ## _DDRREG, CORE_PIN ## P ## _BIT) = 0; \ }while(0) #define _IS_INPUT(P) ((CORE_PIN ## P ## _DDRREG & CORE_PIN ## P ## _BITMASK) == 0) diff --git a/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h b/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h index 843905a851..8f2651d3ed 100644 --- a/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h +++ b/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h @@ -48,3 +48,7 @@ #if USING_PULLDOWNS #error "PULLDOWN pin mode is not available for Teensy 3.5/3.6." #endif + +#if ENABLED(PINS_DEBUGGING) + #error "PINS_DEBUGGING is not yet supported for Teensy 3.5/3.6. Needs is_output(pin), etc." +#endif diff --git a/Marlin/src/HAL/TEENSY35_36/pinsDebug.h b/Marlin/src/HAL/TEENSY35_36/pinsDebug.h index 8526febf10..f0f61b8bbe 100644 --- a/Marlin/src/HAL/TEENSY35_36/pinsDebug.h +++ b/Marlin/src/HAL/TEENSY35_36/pinsDebug.h @@ -22,7 +22,23 @@ #pragma once /** - * HAL Pins Debugging for Teensy 3.5 (MK64FX512) and Teensy 3.6 (MK66FX1M0) + * Pins Debugging for Teensy 3.5 (MK64FX512) and Teensy 3.6 (MK66FX1M0) + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS @@ -53,9 +69,18 @@ #define TPM1_CH1_PIN 17 #endif -#define IS_ANALOG(P) ((P) >= analogInputToDigitalPin(0) && (P) <= analogInputToDigitalPin(9)) || ((P) >= analogInputToDigitalPin(12) && (P) <= analogInputToDigitalPin(20)) +#define getPinByIndex(x) pin_array[x].pin +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define digitalPinToAnalogIndex(P) int(P - analogInputToDigitalPin(0)) +#define getValidPinMode(P) (isValidPin(P) && IS_OUTPUT(P)) +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%02d"), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) -void print_analog_pin(char buffer[], int8_t pin) { +#define isAnalogPin(P) ((P) >= analogInputToDigitalPin(0) && (P) <= analogInputToDigitalPin(9)) || ((P) >= analogInputToDigitalPin(12) && (P) <= analogInputToDigitalPin(20)) + +void printAnalogPin(char buffer[], int8_t pin) { if (pin <= 23) sprintf_P(buffer, PSTR("(A%2d) "), int(pin - 14)); else if (pin <= 39) sprintf_P(buffer, PSTR("(A%2d) "), int(pin - 19)); } @@ -77,7 +102,7 @@ void analog_pin_state(char buffer[], int8_t pin) { * Print a pin's PWM status. * Return true if it's currently a PWM pin. */ -bool pwm_status(int8_t pin) { +bool pwm_status(const int8_t pin) { char buffer[20]; // for the sprintf statements switch (pin) { FTM_CASE(0,0); @@ -108,4 +133,6 @@ bool pwm_status(int8_t pin) { SERIAL_ECHOPGM(" "); } -void pwm_details(uint8_t pin) { /* TODO */ } +void printPinPWM(const pin_t) { /* TODO */ } + +void printPinPort(const pin_t) {} diff --git a/Marlin/src/HAL/TEENSY35_36/spi_pins.h b/Marlin/src/HAL/TEENSY35_36/spi_pins.h index cfffdc9325..d898a923f5 100644 --- a/Marlin/src/HAL/TEENSY35_36/spi_pins.h +++ b/Marlin/src/HAL/TEENSY35_36/spi_pins.h @@ -28,4 +28,3 @@ #define SD_SCK_PIN 13 #define SD_MISO_PIN 12 #define SD_MOSI_PIN 11 -#define SD_SS_PIN 20 // SDSS // A.28, A.29, B.21, C.26, C.29 diff --git a/Marlin/src/HAL/TEENSY35_36/timers.h b/Marlin/src/HAL/TEENSY35_36/timers.h index 3536b62265..3a836ba44e 100644 --- a/Marlin/src/HAL/TEENSY35_36/timers.h +++ b/Marlin/src/HAL/TEENSY35_36/timers.h @@ -34,7 +34,7 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFF +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFFUL #define FTM0_TIMER_PRESCALE 8 #define FTM1_TIMER_PRESCALE 4 diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.cpp b/Marlin/src/HAL/TEENSY40_41/HAL.cpp index 4dd5c3678d..3a1f06095b 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.cpp +++ b/Marlin/src/HAL/TEENSY40_41/HAL.cpp @@ -39,18 +39,20 @@ #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) -#if WITHIN(SERIAL_PORT, 0, 8) +#if WITHIN(SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) IMPLEMENT_SERIAL(SERIAL_PORT); #endif -#ifdef SERIAL_PORT_2 - #if WITHIN(SERIAL_PORT_2, 0, 8) - IMPLEMENT_SERIAL(SERIAL_PORT_2); - #endif +#if defined(SERIAL_PORT_2) && WITHIN(SERIAL_PORT_2, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_2); #endif -#ifdef SERIAL_PORT_3 - #if WITHIN(SERIAL_PORT_3, 0, 8) - IMPLEMENT_SERIAL(SERIAL_PORT_3); - #endif +#if defined(SERIAL_PORT_3) && WITHIN(SERIAL_PORT_3, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(SERIAL_PORT_3); +#endif +#if defined(MMU_SERIAL_PORT) && WITHIN(MMU_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(MMU_SERIAL_PORT); +#endif +#if defined(LCD_SERIAL_PORT) && WITHIN(LCD_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + IMPLEMENT_SERIAL(LCD_SERIAL_PORT); #endif USBSerialType USBSerial(false, SerialUSB); @@ -96,7 +98,7 @@ void MarlinHAL::clear_reset_source() { #define WDT_TIMEOUT TERN(WATCHDOG_DURATION_8S, 8, 4) // 4 or 8 second timeout - constexpr uint8_t timeoutval = (WDT_TIMEOUT - 0.5f) / 0.5f; + constexpr uint8_t timeoutval = (WDT_TIMEOUT - 0.5f) * 2.0f; void MarlinHAL::watchdog_init() { CCM_CCGR3 |= CCM_CCGR3_WDOG1(3); // enable WDOG1 clocks @@ -202,18 +204,13 @@ uint16_t MarlinHAL::adc_value() { // Free Memory Accessor // ------------------------ -#define __bss_end _ebss - extern "C" { - extern char __bss_end; - extern char __heap_start; - extern void* __brkval; + // Reference for Teensy 4.x: https://forum.pjrc.com/index.php?threads/how-to-display-free-ram.33443/#post-275013 + extern unsigned long _heap_end; + extern char *__brkval; - // Doesn't work on Teensy 4.x uint32_t freeMemory() { - uint32_t free_memory; - free_memory = ((uint32_t)&free_memory) - (((uint32_t)__brkval) ?: ((uint32_t)&__bss_end)); - return free_memory; + return (char *)&_heap_end - __brkval; } } diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.h b/Marlin/src/HAL/TEENSY40_41/HAL.h index fa5971a681..f884db6684 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.h +++ b/Marlin/src/HAL/TEENSY40_41/HAL.h @@ -61,6 +61,8 @@ #undef PSTR #define PSTR(str) ({static const char *data = (str); &data[0];}) +#define HAL_CAN_SET_PWM_FREQ + // ------------------------ // Serial ports // ------------------------ @@ -76,41 +78,11 @@ typedef ForwardSerial1Class USBSerialType; extern USBSerialType USBSerial; -#define _MSERIAL(X) MSerial##X -#define MSERIAL(X) _MSERIAL(X) - -#if SERIAL_PORT == -1 - #define MYSERIAL1 USBSerial -#elif WITHIN(SERIAL_PORT, 0, 8) - DECLARE_SERIAL(SERIAL_PORT); - #define MYSERIAL1 MSERIAL(SERIAL_PORT) -#else - #error "The required SERIAL_PORT must be from 0 to 8, or -1 for Native USB." -#endif - -#ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #define MYSERIAL2 USBSerial - #elif SERIAL_PORT_2 == -2 - #define MYSERIAL2 ethernet.telnetClient - #elif WITHIN(SERIAL_PORT_2, 0, 8) - DECLARE_SERIAL(SERIAL_PORT_2); - #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) - #else - #error "SERIAL_PORT_2 must be from 0 to 8, or -1 for Native USB, or -2 for Ethernet." - #endif -#endif - -#ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 - #define MYSERIAL3 USBSerial - #elif WITHIN(SERIAL_PORT_3, 0, 8) - DECLARE_SERIAL(SERIAL_PORT_3); - #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) - #else - #error "SERIAL_PORT_3 must be from 0 to 8, or -1 for Native USB." - #endif -#endif +#define SERIAL_INDEX_MIN 0 +#define SERIAL_INDEX_MAX 8 +#define USB_SERIAL_PORT(...) USBSerial +#define ETH_SERIAL_PORT(...) ethernet.telnetClient +#include "../shared/serial_ports.h" // ------------------------ // Types @@ -133,7 +105,7 @@ typedef int8_t pin_t; // ------------------------ #ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) + #define analogInputToDigitalPin(p) pin_t((p < 12U) ? (p) + 54U : -1) #endif #define HAL_ADC_VREF_MV 3300 @@ -221,11 +193,15 @@ public: /** * Set the PWM duty cycle for the pin to the given value. - * No option to invert the duty cycle [default = false] - * No option to change the scale of the provided value to enable finer PWM duty control [default = 255] + * Optionally invert the duty cycle [default = false] + * Optionally change the scale of the provided value to enable finer PWM duty control [default = 255] */ - static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { - analogWrite(pin, v); - } + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); + /** + * Set the PWM output frequency. This may affect multiple pins, though + * Teensy 4.x provides many timers affecting only a single pin. + * See: https://www.pjrc.com/teensy/td_pulse.html + */ + static void set_pwm_frequency(const pin_t pin, const uint16_t f_desired); }; diff --git a/Marlin/src/HAL/TEENSY40_41/endstop_interrupts.h b/Marlin/src/HAL/TEENSY40_41/endstop_interrupts.h index 722912c890..f59a9409c8 100644 --- a/Marlin/src/HAL/TEENSY40_41/endstop_interrupts.h +++ b/Marlin/src/HAL/TEENSY40_41/endstop_interrupts.h @@ -63,6 +63,7 @@ void setup_endstop_interrupts() { TERN_(USE_Z4_MAX, _ATTACH(Z4_MAX_PIN)); TERN_(USE_Z4_MIN, _ATTACH(Z4_MIN_PIN)); TERN_(USE_Z_MIN_PROBE, _ATTACH(Z_MIN_PROBE_PIN)); + TERN_(USE_CALIBRATION, _ATTACH(CALIBRATION_PIN)); TERN_(USE_I_MAX, _ATTACH(I_MAX_PIN)); TERN_(USE_I_MIN, _ATTACH(I_MIN_PIN)); TERN_(USE_J_MAX, _ATTACH(J_MAX_PIN)); diff --git a/Marlin/src/HAL/TEENSY40_41/fast_pwm.cpp b/Marlin/src/HAL/TEENSY40_41/fast_pwm.cpp new file mode 100644 index 0000000000..21531d115d --- /dev/null +++ b/Marlin/src/HAL/TEENSY40_41/fast_pwm.cpp @@ -0,0 +1,54 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#ifdef __IMXRT1062__ + +#include "../../inc/MarlinConfig.h" + +#include "HAL.h" + +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size /*=255*/, const bool invert) { + + uint32_t bits = 1; + uint16_t value = _MIN(v, v_size); + + if (invert) value = v_size - value; + + // Choose scale as smallest power of 2 which holds v_size. + uint32_t scale = 1; + while (scale < v_size) { + bits++; + scale *= 2; + } + + uint32_t scaled_val = scale * value / v_size; + + uint32_t prior = analogWriteResolution(bits); + analogWrite(pin, scaled_val); + analogWriteResolution(prior); +} + +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + analogWriteFrequency(pin, f_desired); +} + +#endif // __IMXRT1062__ diff --git a/Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h b/Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h index 731658b4a3..7b1c466f23 100644 --- a/Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h +++ b/Marlin/src/HAL/TEENSY40_41/inc/SanityCheck.h @@ -33,10 +33,6 @@ #error "EMERGENCY_PARSER is not yet implemented for Teensy 4.0/4.1. Disable EMERGENCY_PARSER to continue." #endif -#if ENABLED(FAST_PWM_FAN) || SPINDLE_LASER_FREQUENCY - #error "Features requiring Hardware PWM (FAST_PWM_FAN, SPINDLE_LASER_FREQUENCY) are not yet supported for Teensy 4.0/4.1." -#endif - #if HAS_TMC_SW_SERIAL #error "TMC220x Software Serial is not supported for Teensy 4.0/4.1." #endif @@ -44,3 +40,19 @@ #if ENABLED(POSTMORTEM_DEBUGGING) #error "POSTMORTEM_DEBUGGING is not yet supported for Teensy 4.0/4.1." #endif + +#if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) || ENABLED(SERIAL_STATS_DROPPED_RX) || ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS) || ENABLED(SERIAL_STATS_RX_BUFFER_OVERRUNS) + #error "SERIAL_STATS_* features not supported on Teensy 4.0/4.1." +#endif + +#if ENABLED(BAUD_RATE_GCODE) && SERIAL_PORT_2 == -2 + #error "BAUD_RATE_GCODE cannot be enabled when using the Ethernet serial port." +#endif + +#if ENABLED(SPINDLE_LASER_USE_PWM) && !ENABLED(SILENCE_TEENSY_SPINDLE_LASER_WARNING) + #warning "SPINDLE_LASER_USE_PWM is untested on Teensy 4.0/4.1, see https://www.pjrc.com/teensy/td_pulse.html for details on frequencies, and resolution. #define SILENCE_TEENSY_SPINDLE_LASER_WARNING to silence warning." +#endif + +#if ENABLED(FAST_PWM_FAN) && FAST_PWM_FAN_FREQUENCY == 1000U + #warning "FAST_PWM_FAN_FREQUENCY has been left as default, see https://www.pjrc.com/teensy/td_pulse.html and consider raising it to reduce noise." +#endif diff --git a/Marlin/src/HAL/TEENSY40_41/pinsDebug.h b/Marlin/src/HAL/TEENSY40_41/pinsDebug.h index 54f3cb5885..3307b3f71f 100644 --- a/Marlin/src/HAL/TEENSY40_41/pinsDebug.h +++ b/Marlin/src/HAL/TEENSY40_41/pinsDebug.h @@ -22,23 +22,39 @@ #pragma once /** - * HAL Pins Debugging for Teensy 4.0 (IMXRT1062DVL6A) / 4.1 (IMXRT1062DVJ6A) + * Pins Debugging for Teensy 4.0 (IMXRT1062DVL6A) / 4.1 (IMXRT1062DVJ6A) + * + * - NUMBER_PINS_TOTAL + * - MULTI_NAME_PAD + * - getPinByIndex(index) + * - printPinNameByIndex(index) + * - getPinIsDigitalByIndex(index) + * - digitalPinToAnalogIndex(pin) + * - getValidPinMode(pin) + * - isValidPin(pin) + * - isAnalogPin(pin) + * - digitalRead_mod(pin) + * - pwm_status(pin) + * - printPinPWM(pin) + * - printPinPort(pin) + * - printPinNumber(pin) + * - printPinAnalog(pin) */ #warning "PINS_DEBUGGING is not fully supported for Teensy 4.0 / 4.1 so 'M43' may cause hangs." #define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS -#define digitalRead_mod(p) extDigitalRead(p) // AVR digitalRead disabled PWM before it read the pin -#define PRINT_ARRAY_NAME(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN(p) do{ sprintf_P(buffer, PSTR("%02d"), p); SERIAL_ECHO(buffer); }while(0) -#define PRINT_PIN_ANALOG(p) do{ sprintf_P(buffer, PSTR(" (A%2d) "), DIGITAL_PIN_TO_ANALOG_PIN(pin)); SERIAL_ECHO(buffer); }while(0) -#define GET_ARRAY_PIN(p) pin_array[p].pin -#define GET_ARRAY_IS_DIGITAL(p) pin_array[p].is_digital -#define VALID_PIN(pin) (pin >= 0 && pin < int8_t(NUMBER_PINS_TOTAL)) -#define DIGITAL_PIN_TO_ANALOG_PIN(p) int(p - analogInputToDigitalPin(0)) -#define IS_ANALOG(P) ((P) >= analogInputToDigitalPin(0) && (P) <= analogInputToDigitalPin(13)) || ((P) >= analogInputToDigitalPin(14) && (P) <= analogInputToDigitalPin(17)) -#define GET_PINMODE(PIN) (VALID_PIN(pin) && IS_OUTPUT(pin)) +#define getPinByIndex(x) pin_array[x].pin +#define printPinNameByIndex(x) do{ sprintf_P(buffer, PSTR("%-" STRINGIFY(MAX_NAME_LENGTH) "s"), pin_array[x].name); SERIAL_ECHO(buffer); }while(0) +#define getPinIsDigitalByIndex(x) pin_array[x].is_digital +#define digitalPinToAnalogIndex(P) int(P - analogInputToDigitalPin(0)) +#define getValidPinMode(P) (isValidPin(P) && IS_OUTPUT(P)) +#define isValidPin(P) (P >= 0 && P < pin_t(NUMBER_PINS_TOTAL)) +#define isAnalogPin(P) (pin_t(P) >= analogInputToDigitalPin(0) && pin_t(P) <= analogInputToDigitalPin(13)) || (pin_t(P) >= analogInputToDigitalPin(14) && pin_t(P) <= analogInputToDigitalPin(17)) +#define digitalRead_mod(P) extDigitalRead(P) // AVR digitalRead disabled PWM before it read the pin +#define printPinNumber(P) do{ sprintf_P(buffer, PSTR("%02d"), P); SERIAL_ECHO(buffer); }while(0) +#define printPinAnalog(P) do{ sprintf_P(buffer, PSTR(" (A%2d) "), digitalPinToAnalogIndex(P)); SERIAL_ECHO(buffer); }while(0) #define MULTI_NAME_PAD 16 // space needed to be pretty if not first name assigned to a pin struct pwm_pin_info_struct { @@ -118,7 +134,7 @@ const struct pwm_pin_info_struct pwm_pin_info[] = { #endif }; -void print_analog_pin(char buffer[], const pin_t pin) { +void printAnalogPin(char buffer[], const pin_t pin) { if (pin <= 23) sprintf_P(buffer, PSTR("(A%2d) "), int(pin - 14)); else if (pin <= 41) sprintf_P(buffer, PSTR("(A%2d) "), int(pin - 24)); } @@ -149,6 +165,6 @@ bool pwm_status(const pin_t pin) { return (*(portConfigRegister(pin)) == info->muxval); } -void pwm_details(const pin_t) { /* TODO */ } +void printPinPWM(const pin_t) { /* TODO */ } -void print_port(const pin_t) {} +void printPinPort(const pin_t) {} diff --git a/Marlin/src/HAL/TEENSY40_41/spi_pins.h b/Marlin/src/HAL/TEENSY40_41/spi_pins.h index ba4a2c700a..fc1c2a7fed 100644 --- a/Marlin/src/HAL/TEENSY40_41/spi_pins.h +++ b/Marlin/src/HAL/TEENSY40_41/spi_pins.h @@ -28,4 +28,3 @@ #define SD_SCK_PIN 13 #define SD_MISO_PIN 12 #define SD_MOSI_PIN 11 -#define SD_SS_PIN 20 // SDSS // A.28, A.29, B.21, C.26, C.29 diff --git a/Marlin/src/HAL/TEENSY40_41/timers.h b/Marlin/src/HAL/TEENSY40_41/timers.h index 3c7cda0b4e..277a7f318d 100644 --- a/Marlin/src/HAL/TEENSY40_41/timers.h +++ b/Marlin/src/HAL/TEENSY40_41/timers.h @@ -34,15 +34,15 @@ #define FORCE_INLINE __attribute__((always_inline)) inline typedef uint32_t hal_timer_t; -#define HAL_TIMER_TYPE_MAX 0xFFFFFFFE +#define HAL_TIMER_TYPE_MAX 0xFFFFFFFEUL -#define GPT_TIMER_RATE F_BUS_ACTUAL // 150MHz +#define GPT_TIMER_RATE (F_CPU / 4) // 150MHz (Can't use F_BUS_ACTUAL because it's extern volatile) #define GPT1_TIMER_PRESCALE 2 #define GPT2_TIMER_PRESCALE 10 -#define GPT1_TIMER_RATE (GPT_TIMER_RATE / GPT1_TIMER_PRESCALE) // 75MHz -#define GPT2_TIMER_RATE (GPT_TIMER_RATE / GPT2_TIMER_PRESCALE) // 15MHz +#define GPT1_TIMER_RATE (GPT_TIMER_RATE / GPT1_TIMER_PRESCALE) // 150MHz / 2 = 75MHz +#define GPT2_TIMER_RATE (GPT_TIMER_RATE / GPT2_TIMER_PRESCALE) // 150MHz / 10 = 15MHz #ifndef MF_TIMER_STEP #define MF_TIMER_STEP 0 // Timer Index for Stepper @@ -57,7 +57,8 @@ typedef uint32_t hal_timer_t; #define TEMP_TIMER_RATE 1000000 #define TEMP_TIMER_FREQUENCY 1000 -#define STEPPER_TIMER_RATE GPT1_TIMER_RATE +#define HAL_TIMER_RATE GPT1_TIMER_RATE +#define STEPPER_TIMER_RATE HAL_TIMER_RATE #define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) #define STEPPER_TIMER_PRESCALE ((GPT_TIMER_RATE / 1000000) / STEPPER_TIMER_TICKS_PER_US) diff --git a/Marlin/src/HAL/platforms.h b/Marlin/src/HAL/platforms.h index 9eaf7cea98..e7eea44308 100644 --- a/Marlin/src/HAL/platforms.h +++ b/Marlin/src/HAL/platforms.h @@ -37,6 +37,8 @@ #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/LPC1768/NAME) #elif defined(ARDUINO_ARCH_HC32) #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/HC32/NAME) +#elif defined(ARDUINO_ARCH_MFL) + #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/GD32_MFL/NAME) #elif defined(__STM32F1__) || defined(TARGET_STM32F1) #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/STM32F1/NAME) #elif defined(ARDUINO_ARCH_STM32) @@ -54,6 +56,8 @@ #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/SAMD51/NAME) #elif defined(__SAMD21__) #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/SAMD21/NAME) +#elif defined(__PLAT_RP2040__) + #define HAL_PATH(PATH, NAME) XSTR(PATH/HAL/RP2040/NAME) #else #error "Unsupported Platform!" #endif diff --git a/Marlin/src/HAL/shared/Delay.h b/Marlin/src/HAL/shared/Delay.h index a6795a78ea..eeaf4c59fc 100644 --- a/Marlin/src/HAL/shared/Delay.h +++ b/Marlin/src/HAL/shared/Delay.h @@ -100,7 +100,7 @@ void calibrate_delay_loop(); // For delay in microseconds, no smart delay selection is required, directly call the delay function // Teensy compiler is too old and does not accept smart delay compile-time / run-time selection correctly - #define DELAY_US(x) DelayCycleFnc((x) * ((F_CPU) / 1000000UL)) + #define DELAY_US(x) DelayCycleFnc((unsigned long)(x) * ((F_CPU) / 1000000UL)) #elif defined(__AVR__) FORCE_INLINE static void __delay_up_to_3c(uint8_t cycles) { @@ -164,7 +164,7 @@ void calibrate_delay_loop(); } // Delay in microseconds - #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL)) + #define DELAY_US(x) DELAY_CYCLES((unsigned long)(x) * ((F_CPU) / 1000000UL)) #define DELAY_CYCLES_VAR DELAY_CYCLES @@ -173,7 +173,10 @@ void calibrate_delay_loop(); // DELAY_CYCLES specified inside platform // Delay in microseconds - #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL)) + #define DELAY_US(x) DELAY_CYCLES((unsigned long)(x) * ((F_CPU) / 1000000UL)) + + #define DELAY_CYCLES_VAR DELAY_CYCLES + #else #error "Unsupported MCU architecture" diff --git a/Marlin/src/HAL/shared/backtrace/unwarm.h b/Marlin/src/HAL/shared/backtrace/unwarm.h index 72ea0b0627..8e432ebe77 100644 --- a/Marlin/src/HAL/shared/backtrace/unwarm.h +++ b/Marlin/src/HAL/shared/backtrace/unwarm.h @@ -15,7 +15,7 @@ #include "unwinder.h" -/** The maximum number of instructions to interpet in a function. +/** The maximum number of instructions to interpret in a function. * Unwinding will be unconditionally stopped and UNWIND_EXHAUSTED returned * if more than this number of instructions are interpreted in a single * function without unwinding a stack frame. This prevents infinite loops diff --git a/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp b/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp index decf74e6e9..e6064f65da 100644 --- a/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp +++ b/Marlin/src/HAL/shared/backtrace/unwarm_arm.cpp @@ -414,7 +414,7 @@ UnwResult UnwStartArm(UnwState * const state) { /* S indicates that banked registers (untracked) are used, unless * this is a load including the PC when the S-bit indicates that - * that CPSR is loaded from SPSR (also untracked, but ignored). + * CPSR is loaded from SPSR (also untracked, but ignored). */ if (S && (!L || (regList & (0x01 << 15)) == 0)) { UnwPrintd1("\nError:S-bit set requiring banked registers\n"); @@ -431,7 +431,7 @@ UnwResult UnwStartArm(UnwState * const state) { /* Check if ascending or descending. * Registers are loaded/stored in order of address. - * i.e. r0 is at the lowest address, r15 at the highest. + * i.e., r0 is at the lowest address, r15 at the highest. */ r = U ? 0 : 15; do { diff --git a/Marlin/src/HAL/shared/eeprom_if_spi.cpp b/Marlin/src/HAL/shared/eeprom_if_spi.cpp index 72c35addcb..6aa23e823a 100644 --- a/Marlin/src/HAL/shared/eeprom_if_spi.cpp +++ b/Marlin/src/HAL/shared/eeprom_if_spi.cpp @@ -81,4 +81,4 @@ void eeprom_write_byte(uint8_t *pos, uint8_t value) { } #endif // USE_SHARED_EEPROM -#endif // I2C_EEPROM +#endif // SPI_EEPROM diff --git a/Marlin/src/HAL/shared/math_32bit.h b/Marlin/src/HAL/shared/math_32bit.h index 1fb233e3e8..daf82e0650 100644 --- a/Marlin/src/HAL/shared/math_32bit.h +++ b/Marlin/src/HAL/shared/math_32bit.h @@ -22,6 +22,7 @@ #pragma once #include "../../core/macros.h" +#include /** * Math helper functions for 32 bit CPUs diff --git a/Marlin/src/HAL/shared/progmem.h b/Marlin/src/HAL/shared/progmem.h index 4cd7663df9..b3bd5c32fd 100644 --- a/Marlin/src/HAL/shared/progmem.h +++ b/Marlin/src/HAL/shared/progmem.h @@ -39,7 +39,7 @@ #endif #ifndef F class __FlashStringHelper; -#define F(str) (reinterpret_cast(PSTR(str))) +#define F(string_literal) (reinterpret_cast(PSTR(string_literal))) #endif #ifndef _SFR_BYTE #define _SFR_BYTE(n) (n) diff --git a/Marlin/src/HAL/shared/serial_ports.h b/Marlin/src/HAL/shared/serial_ports.h new file mode 100644 index 0000000000..2daeea34e3 --- /dev/null +++ b/Marlin/src/HAL/shared/serial_ports.h @@ -0,0 +1,178 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * serial_ports.h - Define Marlin serial port macros and optionally declare ports + * + * This header defines one or more of the serial ports... + * - MYSERIAL1/2/3 ..... for host devices + * - MMU_SERIAL ........ for Multi-Material color changers + * - LCD_SERIAL ........ for serial LCDs + * - RS485_SERIAL ...... for CAN bus devices + * + * Before including this header define the following macros: + * - SERIAL_INDEX_MIN, SERIAL_INDEX_MAX to provide the valid range of serial port indexes. + * - _MSERIAL(X) and MSERIAL(X) to provide the instance name of Serial Port X. (Default: MSerial##X) + * - EP_SERIAL_PORT(X) to provide the instance name of Emergency Parser serial port X (if it is special). + * - USB_SERIAL_PORT(X) to provide the instance name of the USB serial port (if any). + * - ETH_SERIAL_PORT(X) to provide the instance name of the Ethernet serial port (if any). + * - DECLARE_SERIAL(X) to declare standard a serial port with the given index. + */ + +#ifndef _MSERIAL + #define _MSERIAL(X) MSerial##X +#endif +#ifndef MSERIAL + #define MSERIAL(X) _MSERIAL(X) +#endif + +#define INDEX_RANGE_MSG " must be from " STRINGIFY(SERIAL_INDEX_MIN) " to " STRINGIFY(SERIAL_INDEX_MAX) +#if defined(USB_SERIAL_PORT) && defined(ETH_SERIAL_PORT) + #define MORE_INDEXES_MSG ", -1 for Native USB, or -2 for Ethernet" +#elif defined(USB_SERIAL_PORT) + #define MORE_INDEXES_MSG ", or -1 for Native USB" +#elif defined(ETH_SERIAL_PORT) + #define MORE_INDEXES_MSG ", or -2 for Ethernet" +#else + #define MORE_INDEXES_MSG +#endif + +// +// SERIAL_PORT => MYSERIAL1 +// + +#if defined(EP_SERIAL_PORT) && ENABLED(EMERGENCY_PARSER) + #define MYSERIAL1 EP_SERIAL_PORT(1) +#elif WITHIN(SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define MYSERIAL1 MSERIAL(SERIAL_PORT) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(SERIAL_PORT); + #endif +#elif SERIAL_PORT == -1 && defined(USB_SERIAL_PORT) + #define MYSERIAL1 USB_SERIAL_PORT(1) +#elif SERIAL_PORT == -2 && defined(ETH_SERIAL_PORT) + #define MYSERIAL1 ETH_SERIAL_PORT(1) +#endif +#ifndef MYSERIAL1 + static_assert(false, "SERIAL_PORT" INDEX_RANGE_MSG MORE_INDEXES_MSG "."); + #define MYSERIAL1 _MSERIAL(1) // Dummy port +#endif + +// +// SERIAL_PORT_2 => MYSERIAL2 +// + +#ifdef SERIAL_PORT_2 + #if defined(EP_SERIAL_PORT) && ENABLED(EMERGENCY_PARSER) + #define MYSERIAL2 EP_SERIAL_PORT(2) + #elif WITHIN(SERIAL_PORT_2, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(SERIAL_PORT_2); + #endif + #elif SERIAL_PORT_2 == -1 && defined(USB_SERIAL_PORT) + #define MYSERIAL2 USB_SERIAL_PORT(2) + #elif SERIAL_PORT_2 == -2 && defined(ETH_SERIAL_PORT) + #define MYSERIAL2 ETH_SERIAL_PORT(2) + #endif + #ifndef MYSERIAL2 + static_assert(false, "SERIAL_PORT_2" INDEX_RANGE_MSG MORE_INDEXES_MSG "."); + #define MYSERIAL2 _MSERIAL(1) // Dummy port + #endif +#endif + +// +// SERIAL_PORT_3 => MYSERIAL3 +// + +#ifdef SERIAL_PORT_3 + #if defined(EP_SERIAL_PORT) && ENABLED(EMERGENCY_PARSER) + #define MYSERIAL3 EP_SERIAL_PORT(3) + #elif WITHIN(SERIAL_PORT_3, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(SERIAL_PORT_3); + #endif + #elif SERIAL_PORT_3 == -1 && defined(USB_SERIAL_PORT) + #define MYSERIAL3 USB_SERIAL_PORT(3) + #elif SERIAL_PORT_3 == -2 && defined(ETH_SERIAL_PORT) + #define MYSERIAL3 ETH_SERIAL_PORT(3) + #endif + #ifndef MYSERIAL3 + static_assert(false, "SERIAL_PORT_3" INDEX_RANGE_MSG MORE_INDEXES_MSG "."); + #define MYSERIAL3 _MSERIAL(1) // Dummy port + #endif +#endif + +// +// MMU_SERIAL_PORT => MMU_SERIAL +// + +#ifdef MMU_SERIAL_PORT + #if WITHIN(MMU_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define MMU_SERIAL MSERIAL(MMU_SERIAL_PORT) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(MMU_SERIAL_PORT); + #endif + #else + static_assert(false, "MMU_SERIAL_PORT" INDEX_RANGE_MSG "."); + #define MMU_SERIAL _MSERIAL(1) // Dummy port + #endif +#endif + +// +// LCD_SERIAL_PORT => LCD_SERIAL +// + +#ifdef LCD_SERIAL_PORT + #if WITHIN(LCD_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(LCD_SERIAL_PORT); + #endif + #else + static_assert(false, "LCD_SERIAL_PORT" INDEX_RANGE_MSG "."); + #define LCD_SERIAL _MSERIAL(1) // Dummy port + #endif +#endif + +// +// RS485_SERIAL_PORT => RS485_SERIAL +// + +#ifdef RS485_SERIAL_PORT + #if WITHIN(RS485_SERIAL_PORT, SERIAL_INDEX_MIN, SERIAL_INDEX_MAX) + #define RS485_SERIAL MSERIAL(RS485_SERIAL_PORT) + #ifdef DECLARE_SERIAL + DECLARE_SERIAL(RS485_SERIAL_PORT); + #endif + #else + static_assert(false, "RS485_SERIAL_PORT" INDEX_RANGE_MSG "."); + #define RS485_SERIAL _MSERIAL(1) // Dummy port + #endif +#endif + +#undef DECLARE_SERIAL +#undef SERIAL_INDEX_MIN +#undef SERIAL_INDEX_MAX +#undef INDEX_RANGE_MSG diff --git a/Marlin/src/HAL/shared/servo.h b/Marlin/src/HAL/shared/servo.h index 786c1e6a72..85e645146c 100644 --- a/Marlin/src/HAL/shared/servo.h +++ b/Marlin/src/HAL/shared/servo.h @@ -59,7 +59,7 @@ * write() - Sets the servo angle in degrees. (invalid angle that is valid as pulse in microseconds is treated as microseconds) * writeMicroseconds() - Sets the servo pulse width in microseconds * read() - Gets the last written servo pulse width as an angle between 0 and 180. - * readMicroseconds() - Gets the last written servo pulse width in microseconds. (was read_us() in first release) + * readMicroseconds() - Gets the last written servo pulse width in microseconds. * attached() - Returns true if there is a servo attached. * detach() - Stops an attached servos from pulsing its i/o pin. * move(angle) - Sequence of attach(0), write(angle), @@ -76,19 +76,25 @@ #include "../LPC1768/Servo.h" #elif defined(ARDUINO_ARCH_HC32) #include "../HC32/Servo.h" +#elif defined(ARDUINO_ARCH_MFL) + #include "../GD32_MFL/Servo.h" #elif defined(__STM32F1__) || defined(TARGET_STM32F1) #include "../STM32F1/Servo.h" #elif defined(ARDUINO_ARCH_STM32) #include "../STM32/Servo.h" #elif defined(ARDUINO_ARCH_ESP32) #include "../ESP32/Servo.h" +#elif defined(__PLAT_RP2040__) + #include "../RP2040/Servo.h" +#elif defined(__PLAT_NATIVE_SIM__) + #include "../NATIVE_SIM/Servo.h" #else #include - #if defined(__AVR__) || defined(ARDUINO_ARCH_SAM) || defined(__SAMD51__) || defined(__SAMD21__) + #if defined(__AVR__) || defined(ARDUINO_ARCH_SAM) || defined(__SAMD51__) || defined(__SAMD21__) || defined(__PLAT_RP2040__) // we're good to go #else - #error "This library only supports boards with an AVR, SAM3X, SAMD21 or SAMD51 processor." + #error "This library only supports boards with an AVR, SAM3X, SAMD21, SAMD51, or RP2040 processor." #endif #define Servo_VERSION 2 // software version of this library @@ -96,22 +102,22 @@ class Servo { public: Servo(); - int8_t attach(const int pin); // attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail) - int8_t attach(const int pin, const int min, const int max); // as above but also sets min and max values for writes. + int8_t attach(const int pin); // Attach the given pin to the next free channel, set pinMode, return channel number (-1 on fail) + int8_t attach(const int pin, const int min, const int max); // As above but also set min and max values for writes. void detach(); - void write(int value); // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds - void writeMicroseconds(int value); // write pulse width in microseconds - void move(const int value); // attach the servo, then move to value - // if value is < 200 it is treated as an angle, otherwise as pulse width in microseconds - // if DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach - int read(); // returns current pulse width as an angle between 0 and 180 degrees - int readMicroseconds(); // returns current pulse width in microseconds for this servo (was read_us() in first release) - bool attached(); // return true if this servo is attached, otherwise false + void write(int value); // If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds + void writeMicroseconds(int value); // Write pulse width in microseconds + void move(const int value); // Attach the servo, then move to value + // If value is < 200 it is treated as an angle, otherwise as pulse width in microseconds + // If DEACTIVATE_SERVOS_AFTER_MOVE wait SERVO_DELAY, then detach + int read(); // Return current pulse width as an angle between 0 and 180 degrees + int readMicroseconds(); // Return current pulse width in microseconds for this servo + bool attached(); // Return true if this servo is attached, otherwise false private: - uint8_t servoIndex; // index into the channel data for this servo - int8_t min; // minimum is this value times 4 added to MIN_PULSE_WIDTH - int8_t max; // maximum is this value times 4 added to MAX_PULSE_WIDTH + uint8_t servoIndex; // Index into the channel data for this servo + int8_t min; // Minimum is this value times 4 added to MIN_PULSE_WIDTH + int8_t max; // Maximum is this value times 4 added to MAX_PULSE_WIDTH }; #endif diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 272e50340a..175fd0c315 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -79,6 +79,8 @@ #include "lcd/e3v2/creality/dwin.h" #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI) #include "lcd/e3v2/jyersui/dwin.h" + #elif ENABLED(SOVOL_SV06_RTS) + #include "lcd/sovol_rts/sovol_rts.h" #endif #endif @@ -150,8 +152,8 @@ #include "feature/encoder_i2c.h" #endif -#if (HAS_TRINAMIC_CONFIG || HAS_TMC_SPI) && DISABLED(PSU_DEFAULT_OFF) - #include "feature/tmc_util.h" +#if HAS_TRINAMIC_CONFIG + #include "module/stepper/trinamic.h" #endif #if HAS_CUTTER @@ -229,12 +231,14 @@ #include "feature/controllerfan.h" #endif -#if HAS_PRUSA_MMU1 - #include "feature/mmu/mmu.h" -#endif - -#if HAS_PRUSA_MMU2 +#if HAS_PRUSA_MMU3 + #include "feature/mmu3/mmu3.h" + #include "feature/mmu3/mmu3_reporting.h" + #include "feature/mmu3/SpoolJoin.h" +#elif HAS_PRUSA_MMU2 #include "feature/mmu/mmu2.h" +#elif HAS_PRUSA_MMU1 + #include "feature/mmu/mmu.h" #endif #if ENABLED(PASSWORD_FEATURE) @@ -261,16 +265,28 @@ #include "tests/marlin_tests.h" #endif +#if HAS_RS485_SERIAL + #include "feature/rs485.h" +#endif + +#if !HAS_MEDIA + CardReader card; // Stub instance with "no media" methods +#endif + PGMSTR(M112_KILL_STR, "M112 Shutdown"); -MarlinState marlin_state = MF_INITIALIZING; +#if ENABLED(CONFIGURABLE_MACHINE_NAME) + MString<64> machine_name; +#endif + +MarlinState marlin_state = MarlinState::MF_INITIALIZING; // For M109 and M190, this flag may be cleared (by M108) to exit the wait loop -bool wait_for_heatup = true; +bool wait_for_heatup = false; // For M0/M1, this flag may be cleared (by M108) to exit the wait-for-user loop #if HAS_RESUME_CONTINUE - bool wait_for_user; // = false; + bool wait_for_user; // = false void wait_for_user_response(millis_t ms/*=0*/, const bool no_sleep/*=false*/) { UNUSED(no_sleep); @@ -307,37 +323,27 @@ bool wait_for_heatup = true; #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wnarrowing" - -#ifndef RUNTIME_ONLY_ANALOG_TO_DIGITAL - template - constexpr pin_t OnlyPins<_SP_END, D...>::table[sizeof...(D)]; -#endif +#pragma GCC diagnostic ignored "-Wsign-compare" bool pin_is_protected(const pin_t pin) { - #ifdef RUNTIME_ONLY_ANALOG_TO_DIGITAL - static const pin_t sensitive_pins[] PROGMEM = { SENSITIVE_PINS }; - const size_t pincount = COUNT(sensitive_pins); - #else - static constexpr size_t pincount = OnlyPins::size; - static const pin_t (&sensitive_pins)[pincount] PROGMEM = OnlyPins::table; - #endif - for (uint8_t i = 0; i < pincount; ++i) { - const pin_t * const pptr = &sensitive_pins[i]; - if (pin == (sizeof(pin_t) == 2 ? (pin_t)pgm_read_word(pptr) : (pin_t)pgm_read_byte(pptr))) return true; - } + #define pgm_read_pin(P) (sizeof(pin_t) == 2 ? (pin_t)pgm_read_word(P) : (pin_t)pgm_read_byte(P)) + for (uint8_t i = 0; i < COUNT(sensitive_dio); ++i) + if (pin == pgm_read_pin(&sensitive_dio[i])) return true; + for (uint8_t i = 0; i < COUNT(sensitive_aio); ++i) + if (pin == analogInputToDigitalPin(pgm_read_pin(&sensitive_aio[i]))) return true; return false; } #pragma GCC diagnostic pop bool printer_busy() { - return planner.movesplanned() || printingIsActive(); + return planner.has_blocks_queued() || printingIsActive(); } /** * A Print Job exists when the timer is running or SD is printing */ -bool printJobOngoing() { return print_job_timer.isRunning() || IS_SD_PRINTING(); } +bool printJobOngoing() { return print_job_timer.isRunning() || card.isStillPrinting(); } /** * Printing is active when a job is underway but not paused @@ -348,7 +354,7 @@ bool printingIsActive() { return !did_pause_print && printJobOngoing(); } * Printing is paused according to SD or host indicators */ bool printingIsPaused() { - return did_pause_print || print_job_timer.isPaused() || IS_SD_PAUSED(); + return did_pause_print || print_job_timer.isPaused() || card.isPaused(); } void startOrResumeJob() { @@ -357,6 +363,7 @@ void startOrResumeJob() { TERN_(CANCEL_OBJECTS, cancelable.reset()); TERN_(LCD_SHOW_E_TOTAL, e_move_accumulator = 0); TERN_(SET_REMAINING_TIME, ui.reset_remaining_time()); + TERN_(HAS_PRUSA_MMU3, MMU3::operation_statistics.reset_per_print_stats()); } print_job_timer.start(); } @@ -388,8 +395,8 @@ void startOrResumeJob() { } inline void finishSDPrinting() { - if (queue.enqueue_one(F("M1001"))) { // Keep trying until it gets queued - marlin_state = MF_RUNNING; // Signal to stop trying + if (queue.enqueue_one(F("M1001"))) { // Keep trying until it gets queued + marlin_state = MarlinState::MF_RUNNING; // Signal to stop trying TERN_(PASSWORD_AFTER_SD_PRINT_END, password.lock_machine()); TERN_(DGUS_LCD_UI_MKS, screen.sdPrintingFinished()); } @@ -425,8 +432,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { if (gcode.stepper_max_timed_out(ms)) { SERIAL_ERROR_START(); - SERIAL_ECHOPGM(STR_KILL_PRE); - SERIAL_ECHOLNPGM(STR_KILL_INACTIVE_TIME, parser.command_ptr); + SERIAL_ECHOLN(F(STR_KILL_PRE), F(STR_KILL_INACTIVE_TIME), parser.command_ptr); kill(); } @@ -474,11 +480,16 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { #if HAS_KILL - // Check if the kill button was pressed and wait just in case it was an accidental - // key kill key press + // Check if the kill button was pressed and wait to ensure the signal is not noise + // typically caused by poor insulation and grounding on LCD cables. + // Lower numbers here will increase response time and therefore safety rating. + // It is recommended to set this as low as possible without false triggers. // ------------------------------------------------------------------------------- + #ifndef KILL_DELAY + #define KILL_DELAY 250 + #endif + static int killCount = 0; // make the inactivity button a bit less responsive - const int KILL_DELAY = 750; if (kill_state()) killCount++; else if (killCount > 0) @@ -489,8 +500,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { // ---------------------------------------------------------------- if (killCount >= KILL_DELAY) { SERIAL_ERROR_START(); - SERIAL_ECHOPGM(STR_KILL_PRE); - SERIAL_ECHOLNPGM(STR_KILL_BUTTON); + SERIAL_ECHOLN(F(STR_KILL_PRE), F(STR_KILL_BUTTON)); kill(); } #endif @@ -503,7 +513,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { // Handle a standalone HOME button constexpr millis_t HOME_DEBOUNCE_DELAY = 1000UL; static millis_t next_home_key_ms; // = 0 - if (!IS_SD_PRINTING() && !READ(HOME_PIN)) { // HOME_PIN goes LOW when pressed + if (!card.isStillPrinting() && !READ(HOME_PIN)) { // HOME_PIN goes LOW when pressed if (ELAPSED(ms, next_home_key_ms)) { next_home_key_ms = ms + HOME_DEBOUNCE_DELAY; LCD_MESSAGE(MSG_AUTO_HOME); @@ -670,14 +680,14 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { TERN_(HOTEND_IDLE_TIMEOUT, hotend_idle.check()); #if ANY(PSU_CONTROL, AUTO_POWER_CONTROL) && PIN_EXISTS(PS_ON_EDM) - if ( ELAPSED(ms, powerManager.last_state_change_ms + PS_EDM_RESPONSE) + if ( ELAPSED(ms, powerManager.last_state_change_ms, PS_EDM_RESPONSE) && (READ(PS_ON_PIN) != READ(PS_ON_EDM_PIN) || TERN0(PSU_OFF_REDUNDANT, extDigitalRead(PS_ON1_PIN) != extDigitalRead(PS_ON1_EDM_PIN))) ) kill(GET_TEXT_F(MSG_POWER_EDM_FAULT)); #endif #if ENABLED(EXTRUDER_RUNOUT_PREVENT) if (thermalManager.degHotend(active_extruder) > (EXTRUDER_RUNOUT_MINTEMP) - && ELAPSED(ms, gcode.previous_move_ms + SEC_TO_MS(EXTRUDER_RUNOUT_SECONDS)) + && ELAPSED(ms, gcode.previous_move_ms, SEC_TO_MS(EXTRUDER_RUNOUT_SECONDS)) && !planner.has_blocks_queued() ) { const int8_t e_stepper = TERN(HAS_SWITCHING_EXTRUDER, active_extruder >> 1, active_extruder); @@ -728,7 +738,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { WRITE(FET_SAFETY_PIN, FET_SAFETY_INVERTED); } #endif -} +} // manage_inactivity() #if ALL(EP_BABYSTEPPING, EMERGENCY_PARSER) #include "feature/babystep.h" @@ -779,14 +789,14 @@ void idle(const bool no_stepper_sleep/*=false*/) { TERN_(MAX7219_DEBUG, max7219.idle_tasks()); // Return if setup() isn't completed - if (marlin_state == MF_INITIALIZING) goto IDLE_DONE; + if (marlin_state == MarlinState::MF_INITIALIZING) goto IDLE_DONE; // TODO: Still causing errors TERN_(TOOL_SENSOR, (void)check_tool_sensor_stats(active_extruder, true)); // Handle filament runout sensors #if HAS_FILAMENT_SENSOR - if (TERN1(HAS_PRUSA_MMU2, !mmu2.enabled())) + if (TERN1(HAS_PRUSA_MMU2, !mmu2.enabled()) && TERN1(HAS_PRUSA_MMU3, !mmu3.enabled())) runout.run(); #endif @@ -798,7 +808,7 @@ void idle(const bool no_stepper_sleep/*=false*/) { // Handle Power-Loss Recovery #if ENABLED(POWER_LOSS_RECOVERY) && PIN_EXISTS(POWER_LOSS) - if (IS_SD_PRINTING()) recovery.outage(); + if (card.isStillPrinting()) recovery.outage(); #endif // Run StallGuard endstop checks @@ -810,9 +820,6 @@ void idle(const bool no_stepper_sleep/*=false*/) { // Handle SD Card insert / remove TERN_(HAS_MEDIA, card.manage_media()); - // Handle USB Flash Drive insert / remove - TERN_(USB_FLASH_DRIVE_SUPPORT, card.diskIODriver()->idle()); - // Announce Host Keepalive state (if any) TERN_(HOST_KEEPALIVE_FEATURE, gcode.host_keepalive()); @@ -823,7 +830,11 @@ void idle(const bool no_stepper_sleep/*=false*/) { TERN_(HAS_BEEPER, buzzer.tick()); // Handle UI input / draw events - ui.update(); + #if ENABLED(SOVOL_SV06_RTS) + RTS_Update(); + #else + ui.update(); + #endif // Run i2c Position Encoders #if ENABLED(I2C_POSITION_ENCODERS) @@ -851,7 +862,11 @@ void idle(const bool no_stepper_sleep/*=false*/) { #endif // Update the Průša MMU2 - TERN_(HAS_PRUSA_MMU2, mmu2.mmu_loop()); + #if HAS_PRUSA_MMU3 + mmu3.mmu_loop(); + #elif HAS_PRUSA_MMU2 + mmu2.mmu_loop(); + #endif // Handle Joystick jogging TERN_(POLL_JOG, joystick.inject_jog_moves()); @@ -874,7 +889,7 @@ void idle(const bool no_stepper_sleep/*=false*/) { TERN_(MARLIN_DEV_MODE, idle_depth--); return; -} +} // idle() /** * Kill all activity and lock the machine. @@ -965,9 +980,9 @@ void stop() { SERIAL_ERROR_MSG(STR_ERR_STOPPED); LCD_MESSAGE(MSG_STOPPED); safe_delay(350); // allow enough time for messages to get out before stopping - marlin_state = MF_STOPPED; + marlin_state = MarlinState::MF_STOPPED; } -} +} // stop() inline void tmc_standby_setup() { #if PIN_EXISTS(X_STDBY) @@ -1036,7 +1051,7 @@ inline void tmc_standby_setup() { #if PIN_EXISTS(E7_STDBY) SET_INPUT_PULLDOWN(E7_STDBY_PIN); #endif -} +} // tmc_standby_setup() /** * Marlin Firmware entry-point. Abandon Hope All Ye Who Enter Here. @@ -1156,6 +1171,12 @@ void setup() { millis_t serial_connect_timeout = millis() + 1000UL; while (!MYSERIAL1.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } + #if ENABLED(SOVOL_SV06_RTS) + LCD_SERIAL.begin(BAUDRATE); + serial_connect_timeout = millis() + 1000UL; + while (!LCD_SERIAL.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } + #endif + #if HAS_MULTI_SERIAL && !HAS_ETHERNET #ifndef BAUDRATE_2 #define BAUDRATE_2 BAUDRATE @@ -1240,7 +1261,7 @@ void setup() { SETUP_RUN(runout.setup()); #endif - #if HAS_TMC220x + #if HAS_TMC_UART SETUP_RUN(tmc_serial_begin()); #endif @@ -1279,7 +1300,7 @@ void setup() { // Identify myself as Marlin x.x.x SERIAL_ECHOLNPGM("Marlin " SHORT_BUILD_VERSION); - #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR) + #ifdef STRING_DISTRIBUTION_DATE SERIAL_ECHO_MSG( " Last Updated: " STRING_DISTRIBUTION_DATE " | Author: " STRING_CONFIG_H_AUTHOR @@ -1313,8 +1334,11 @@ void setup() { // UI must be initialized before EEPROM // (because EEPROM code calls the UI). - - SETUP_RUN(ui.init()); + #if ENABLED(SOVOL_SV06_RTS) + SETUP_RUN(RTS_Update()); + #else + SETUP_RUN(ui.init()); + #endif #if PIN_EXISTS(SAFE_POWER) #if HAS_DRIVER_SAFE_POWER_PROTECT @@ -1325,16 +1349,28 @@ void setup() { #endif #endif - #if ALL(HAS_MEDIA, SDCARD_EEPROM_EMULATION) - SETUP_RUN(card.mount()); // Mount media with settings before first_load + #if HAS_MEDIA + SETUP_RUN(card.init()); // Prepare for media usage + #if ANY(SDCARD_EEPROM_EMULATION, POWER_LOSS_RECOVERY) + SETUP_RUN(card.mount()); // Mount media with settings before first_load + #endif + #endif + + // Prepare some LCDs to display early + #if HAS_EARLY_LCD_SETTINGS + SETUP_RUN(settings.load_lcd_state()); + #endif + + #if ALL(HAS_WIRED_LCD, SHOW_BOOTSCREEN) + SETUP_RUN(ui.show_bootscreen()); + const millis_t bootscreen_ms = millis(); #endif SETUP_RUN(settings.first_load()); // Load data from EEPROM if available (or use defaults) // This also updates variables in the planner, elsewhere - #if ALL(HAS_WIRED_LCD, SHOW_BOOTSCREEN) - SETUP_RUN(ui.show_bootscreen()); - const millis_t bootscreen_ms = millis(); + #if ENABLED(CONFIGURABLE_MACHINE_NAME) + SETUP_RUN(ui.reset_status(false)); // machine_name Initialized by settings.load() #endif #if ENABLED(PROBE_TARE) @@ -1582,7 +1618,11 @@ void setup() { SETUP_RUN(stepper_driver_backward_report()); #endif - #if HAS_PRUSA_MMU2 + #if HAS_PRUSA_MMU3 + if (mmu3.mmu_hw_enabled) SETUP_RUN(mmu3.start()); + SETUP_RUN(mmu3.status()); + SETUP_RUN(spooljoin.initStatus()); + #elif HAS_PRUSA_MMU2 SETUP_RUN(mmu2.init()); #endif @@ -1594,6 +1634,8 @@ void setup() { #if ENABLED(DWIN_CREALITY_LCD) SETUP_RUN(dwinInitScreen()); + #elif ENABLED(SOVOL_SV06_RTS) + SETUP_RUN(rts.init()); #endif #if HAS_SERVICE_INTERVALS && DISABLED(DWIN_CREALITY_LCD) @@ -1643,11 +1685,15 @@ void setup() { SETUP_RUN(bdl.init(I2C_BD_SDA_PIN, I2C_BD_SCL_PIN, I2C_BD_DELAY)); #endif + #if HAS_RS485_SERIAL + SETUP_RUN(rs485_init()); + #endif + #if ENABLED(FT_MOTION) SETUP_RUN(ftMotion.init()); #endif - marlin_state = MF_RUNNING; + marlin_state = MarlinState::MF_RUNNING; #ifdef STARTUP_TUNE // Play a short startup tune before continuing. @@ -1658,7 +1704,7 @@ void setup() { SETUP_LOG("setup() completed."); TERN_(MARLIN_TEST_BUILD, runStartupTests()); -} +} // setup() /** * The main Marlin program loop @@ -1679,7 +1725,7 @@ void loop() { #if HAS_MEDIA if (card.flag.abort_sd_printing) abortSDPrinting(); - if (marlin_state == MF_SD_COMPLETE) finishSDPrinting(); + if (marlin_state == MarlinState::MF_SD_COMPLETE) finishSDPrinting(); #endif queue.advance(); diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h index e9c63bb31f..ecab0e3630 100644 --- a/Marlin/src/MarlinCore.h +++ b/Marlin/src/MarlinCore.h @@ -41,8 +41,12 @@ inline void idle_no_sleep() { idle(true); } void kill(FSTR_P const lcd_error=nullptr, FSTR_P const lcd_component=nullptr, const bool steppers_off=false); void minkill(const bool steppers_off=false); +#if ENABLED(CONFIGURABLE_MACHINE_NAME) + extern MString<64> machine_name; +#endif + // Global State of the firmware -enum MarlinState : uint8_t { +enum class MarlinState : uint8_t { MF_INITIALIZING = 0, MF_STOPPED, MF_KILLED, @@ -53,8 +57,8 @@ enum MarlinState : uint8_t { }; extern MarlinState marlin_state; -inline bool IsRunning() { return marlin_state >= MF_RUNNING; } -inline bool IsStopped() { return marlin_state == MF_STOPPED; } +inline bool IsRunning() { return marlin_state >= MarlinState::MF_RUNNING; } +inline bool IsStopped() { return marlin_state == MarlinState::MF_STOPPED; } bool printingIsActive(); bool printJobOngoing(); diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h index 1dfcabdefb..f22c9fd1f1 100644 --- a/Marlin/src/core/boards.h +++ b/Marlin/src/core/boards.h @@ -28,6 +28,7 @@ #include "macros.h" +#define BOARD_ERROR -2 #define BOARD_UNKNOWN -1 // @@ -54,11 +55,11 @@ #define BOARD_RAMPS_PLUS_EEF 1033 // RAMPS Plus 3DYMY (Power outputs: Hotend0, Hotend1, Fan) #define BOARD_RAMPS_PLUS_SF 1034 // RAMPS Plus 3DYMY (Power outputs: Spindle, Controller Fan) -#define BOARD_RAMPS_BTT_16_PLUS_EFB 1035 // RAMPS 1.6+ (Power outputs: Hotend, Fan, Bed) -#define BOARD_RAMPS_BTT_16_PLUS_EEB 1036 // RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Bed) -#define BOARD_RAMPS_BTT_16_PLUS_EFF 1037 // RAMPS 1.6+ (Power outputs: Hotend, Fan0, Fan1) -#define BOARD_RAMPS_BTT_16_PLUS_EEF 1038 // RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Fan) -#define BOARD_RAMPS_BTT_16_PLUS_SF 1039 // RAMPS 1.6+ (Power outputs: Spindle, Controller Fan) +#define BOARD_RAMPS_BTT_16_PLUS_EFB 1040 // RAMPS 1.6+ (Power outputs: Hotend, Fan, Bed) +#define BOARD_RAMPS_BTT_16_PLUS_EEB 1041 // RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Bed) +#define BOARD_RAMPS_BTT_16_PLUS_EFF 1042 // RAMPS 1.6+ (Power outputs: Hotend, Fan0, Fan1) +#define BOARD_RAMPS_BTT_16_PLUS_EEF 1043 // RAMPS 1.6+ (Power outputs: Hotend0, Hotend1, Fan) +#define BOARD_RAMPS_BTT_16_PLUS_SF 1044 // RAMPS 1.6+ (Power outputs: Spindle, Controller Fan) // // RAMPS Derivatives - ATmega1280, ATmega2560 @@ -79,42 +80,42 @@ #define BOARD_MKS_GEN_13 1112 // MKS GEN v1.3 or 1.4 #define BOARD_MKS_GEN_L 1113 // MKS GEN L #define BOARD_KFB_2 1114 // BigTreeTech or BIQU KFB2.0 -#define BOARD_ZRIB_V20 1115 // zrib V2.0 (Chinese RAMPS replica) -#define BOARD_ZRIB_V52 1116 // zrib V5.2 (Chinese RAMPS replica) -#define BOARD_FELIX2 1117 // Felix 2.0+ Electronics Board (RAMPS like) -#define BOARD_RIGIDBOARD 1118 // Invent-A-Part RigidBoard -#define BOARD_RIGIDBOARD_V2 1119 // Invent-A-Part RigidBoard V2 -#define BOARD_SAINSMART_2IN1 1120 // Sainsmart 2-in-1 board -#define BOARD_ULTIMAKER 1121 // Ultimaker -#define BOARD_ULTIMAKER_OLD 1122 // Ultimaker (Older electronics. Pre 1.5.4. This is rare) -#define BOARD_AZTEEG_X3 1123 // Azteeg X3 -#define BOARD_AZTEEG_X3_PRO 1124 // Azteeg X3 Pro -#define BOARD_ULTIMAIN_2 1125 // Ultimainboard 2.x (Uses TEMP_SENSOR 20) -#define BOARD_RUMBA 1126 // Rumba -#define BOARD_RUMBA_RAISE3D 1127 // Raise3D N series Rumba derivative -#define BOARD_RL200 1128 // Rapide Lite 200 (v1, low-cost RUMBA clone with drv) -#define BOARD_FORMBOT_TREX2PLUS 1129 // Formbot T-Rex 2 Plus -#define BOARD_FORMBOT_TREX3 1130 // Formbot T-Rex 3 -#define BOARD_FORMBOT_RAPTOR 1131 // Formbot Raptor -#define BOARD_FORMBOT_RAPTOR2 1132 // Formbot Raptor 2 -#define BOARD_BQ_ZUM_MEGA_3D 1133 // bq ZUM Mega 3D -#define BOARD_MAKEBOARD_MINI 1134 // MakeBoard Mini v2.1.2 by MicroMake -#define BOARD_TRIGORILLA_13 1135 // TriGorilla Anycubic version 1.3-based on RAMPS EFB -#define BOARD_TRIGORILLA_14 1136 // ... Ver 1.4 -#define BOARD_TRIGORILLA_14_11 1137 // ... Rev 1.1 (new servo pin order) -#define BOARD_RAMPS_ENDER_4 1138 // Creality: Ender-4, CR-8 -#define BOARD_RAMPS_CREALITY 1139 // Creality: CR10S, CR20, CR-X -#define BOARD_DAGOMA_F5 1140 // Dagoma F5 -#define BOARD_FYSETC_F6_13 1141 // FYSETC F6 1.3 -#define BOARD_FYSETC_F6_14 1142 // FYSETC F6 1.4 -#define BOARD_DUPLICATOR_I3_PLUS 1143 // Wanhao Duplicator i3 Plus -#define BOARD_VORON 1144 // VORON Design -#define BOARD_TRONXY_V3_1_0 1145 // Tronxy TRONXY-V3-1.0 -#define BOARD_Z_BOLT_X_SERIES 1146 // Z-Bolt X Series -#define BOARD_TT_OSCAR 1147 // TT OSCAR -#define BOARD_OVERLORD 1148 // Overlord/Overlord Pro -#define BOARD_HJC2560C_REV1 1149 // ADIMLab Gantry v1 -#define BOARD_HJC2560C_REV2 1150 // ADIMLab Gantry v2 +#define BOARD_ZRIB_V20 1115 // Zonestar zrib V2.0 (Chinese RAMPS replica) +#define BOARD_ZRIB_V52 1116 // Zonestar zrib V5.2 (Chinese RAMPS replica) +#define BOARD_ZRIB_V53 1117 // Zonestar zrib V5.3 (Chinese RAMPS replica) +#define BOARD_FELIX2 1118 // Felix 2.0+ Electronics Board (RAMPS like) +#define BOARD_RIGIDBOARD 1119 // Invent-A-Part RigidBoard +#define BOARD_RIGIDBOARD_V2 1120 // Invent-A-Part RigidBoard V2 +#define BOARD_SAINSMART_2IN1 1121 // Sainsmart 2-in-1 board +#define BOARD_ULTIMAKER 1122 // Ultimaker +#define BOARD_ULTIMAKER_OLD 1123 // Ultimaker (Older electronics. Pre 1.5.4. This is rare) +#define BOARD_AZTEEG_X3 1124 // Azteeg X3 +#define BOARD_AZTEEG_X3_PRO 1125 // Azteeg X3 Pro +#define BOARD_ULTIMAIN_2 1126 // Ultimainboard 2.x (Uses TEMP_SENSOR 20) +#define BOARD_RUMBA 1127 // Rumba +#define BOARD_RUMBA_RAISE3D 1128 // Raise3D N series Rumba derivative +#define BOARD_RL200 1129 // Rapide Lite 200 (v1, low-cost RUMBA clone with drv) +#define BOARD_FORMBOT_TREX2PLUS 1130 // Formbot T-Rex 2 Plus +#define BOARD_FORMBOT_TREX3 1131 // Formbot T-Rex 3 +#define BOARD_FORMBOT_RAPTOR 1132 // Formbot Raptor +#define BOARD_FORMBOT_RAPTOR2 1133 // Formbot Raptor 2 +#define BOARD_BQ_ZUM_MEGA_3D 1134 // bq ZUM Mega 3D +#define BOARD_MAKEBOARD_MINI 1135 // MakeBoard Mini v2.1.2 by MicroMake +#define BOARD_TRIGORILLA_13 1136 // TriGorilla Anycubic version 1.3-based on RAMPS EFB +#define BOARD_TRIGORILLA_14 1137 // ... Ver 1.4 +#define BOARD_TRIGORILLA_14_11 1138 // ... Rev 1.1 (new servo pin order) +#define BOARD_RAMPS_ENDER_4 1139 // Creality: Ender-4, CR-8 +#define BOARD_RAMPS_CREALITY 1140 // Creality: CR10S, CR20, CR-X +#define BOARD_CREALITY_V252 1141 // Creality CR-10 V2, CR-10 V3 +#define BOARD_DAGOMA_F5 1142 // Dagoma F5 +#define BOARD_DAGOMA_D6 1143 // Dagoma D6 (as found in the Dagoma DiscoUltimate V2 TMC) +#define BOARD_FYSETC_F6_13 1144 // FYSETC F6 1.3 +#define BOARD_FYSETC_F6_14 1145 // FYSETC F6 1.4 +#define BOARD_DUPLICATOR_I3_PLUS 1146 // Wanhao Duplicator i3 Plus +#define BOARD_VORON 1147 // VORON Design +#define BOARD_TRONXY_V3_1_0 1148 // Tronxy TRONXY-V3-1.0 +#define BOARD_Z_BOLT_X_SERIES 1149 // Z-Bolt X Series +#define BOARD_TT_OSCAR 1150 // TT OSCAR #define BOARD_TANGO 1151 // BIQU Tango V1 #define BOARD_MKS_GEN_L_V2 1152 // MKS GEN L V2 #define BOARD_MKS_GEN_L_V21 1153 // MKS GEN L V2.1 @@ -127,10 +128,12 @@ #define BOARD_RAMPS_S_12_EFFB 1160 // Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed) #define BOARD_LONGER3D_LK1_PRO 1161 // Longer LK1 PRO / Alfawise U20 Pro (PRO version) #define BOARD_LONGER3D_LKx_PRO 1162 // Longer LKx PRO / Alfawise Uxx Pro (PRO version) -#define BOARD_ZRIB_V53 1163 // Zonestar zrib V5.3 (Chinese RAMPS replica) -#define BOARD_PXMALION_CORE_I3 1164 // Pxmalion Core I3 -#define BOARD_PANOWIN_CUTLASS 1165 // Panowin Cutlass (as found in the Panowin F1) -#define BOARD_KODAMA_BARDO 1166 // Kodama Bardo V1.x (as found in the Kodama Trinus) +#define BOARD_PXMALION_CORE_I3 1163 // Pxmalion Core I3 +#define BOARD_PANOWIN_CUTLASS 1164 // Panowin Cutlass (as found in the Panowin F1) +#define BOARD_KODAMA_BARDO 1165 // Kodama Bardo V1.x (as found in the Kodama Trinus) +#define BOARD_XTLW_MFF_V1 1166 // XTLW MFF V1.0 +#define BOARD_XTLW_MFF_V2 1167 // XTLW MFF V2.0 +#define BOARD_RUMBA_E3D 1168 // E3D Rumba BigBox // // RAMBo and derivatives @@ -166,20 +169,24 @@ #define BOARD_GT2560_REV_A_PLUS 1315 // Geeetech GT2560 Rev A+ (with auto level probe) #define BOARD_GT2560_REV_B 1316 // Geeetech GT2560 Rev B #define BOARD_GT2560_V3 1317 // Geeetech GT2560 Rev B for A10(M/T/D) -#define BOARD_GT2560_V4 1318 // Geeetech GT2560 Rev B for A10(M/T/D) -#define BOARD_GT2560_V3_MC2 1319 // Geeetech GT2560 Rev B for Mecreator2 -#define BOARD_GT2560_V3_A20 1320 // Geeetech GT2560 Rev B for A20(M/T/D) -#define BOARD_EINSTART_S 1321 // Einstart retrofit -#define BOARD_WANHAO_ONEPLUS 1322 // Wanhao 0ne+ i3 Mini -#define BOARD_LEAPFROG_XEED2015 1323 // Leapfrog Xeed 2015 -#define BOARD_PICA_REVB 1324 // PICA Shield (original version) -#define BOARD_PICA 1325 // PICA Shield (rev C or later) -#define BOARD_INTAMSYS40 1326 // Intamsys 4.0 (Funmat HT) -#define BOARD_MALYAN_M180 1327 // Malyan M180 Mainboard Version 2 (no display function, direct G-code only) -#define BOARD_GT2560_V4_A20 1328 // Geeetech GT2560 Rev B for A20(M/T/D) -#define BOARD_PROTONEER_CNC_SHIELD_V3 1329 // Mega controller & Protoneer CNC Shield V3.00 -#define BOARD_WEEDO_62A 1330 // WEEDO 62A board (TINA2, Monoprice Cadet, etc.) -#define BOARD_GT2560_V41B 1331 // Geeetech GT2560 V4.1B for A10(M/T/D) +#define BOARD_GT2560_V3_MC2 1318 // Geeetech GT2560 Rev B for Mecreator2 +#define BOARD_GT2560_V3_A20 1319 // Geeetech GT2560 Rev B for A20(M/T/D) +#define BOARD_GT2560_V4 1320 // Geeetech GT2560 Rev B for A10(M/T/D) +#define BOARD_GT2560_V4_A20 1321 // Geeetech GT2560 Rev B for A20(M/T/D) +#define BOARD_GT2560_V41B 1322 // Geeetech GT2560 V4.1B for A10(M/T/D) +#define BOARD_EINSTART_S 1323 // Einstart retrofit +#define BOARD_WANHAO_ONEPLUS 1324 // Wanhao 0ne+ i3 Mini +#define BOARD_WANHAO_D9 1325 // Wanhao D9 MK2 +#define BOARD_OVERLORD 1326 // Overlord/Overlord Pro +#define BOARD_HJC2560C_REV1 1327 // ADIMLab Gantry v1 +#define BOARD_HJC2560C_REV2 1328 // ADIMLab Gantry v2 +#define BOARD_LEAPFROG_XEED2015 1329 // Leapfrog Xeed 2015 +#define BOARD_PICA_REVB 1330 // PICA Shield (original version) +#define BOARD_PICA 1331 // PICA Shield (rev C or later) +#define BOARD_INTAMSYS40 1332 // Intamsys 4.0 (Funmat HT) +#define BOARD_MALYAN_M180 1333 // Malyan M180 Mainboard Version 2 (no display function, direct G-code only) +#define BOARD_PROTONEER_CNC_SHIELD_V3 1334 // Mega controller & Protoneer CNC Shield V3.00 +#define BOARD_WEEDO_62A 1335 // WEEDO 62A board (TINA2, Monoprice Cadet, etc.) // // ATmega1281, ATmega2561 @@ -272,6 +279,7 @@ #define BOARD_MKS_SGEN_L_V2 2509 // MKS SGEN_L V2 #define BOARD_BTT_SKR_E3_TURBO 2510 // BigTreeTech SKR E3 Turbo #define BOARD_FLY_CDY 2511 // FLYmaker FLY CDY +#define BOARD_XTLW_CLIMBER_8TH_LPC 2512 // XTLW Climber 8 // // SAM3X8E ARM Cortex-M3 @@ -279,7 +287,7 @@ #define BOARD_DUE3DOM 3000 // DUE3DOM for Arduino DUE #define BOARD_DUE3DOM_MINI 3001 // DUE3DOM MINI for Arduino DUE -#define BOARD_RADDS 3002 // RADDS +#define BOARD_RADDS 3002 // RADDS v1.5/v1.6 #define BOARD_RAMPS_FD_V1 3003 // RAMPS-FD v1 #define BOARD_RAMPS_FD_V2 3004 // RAMPS-FD v2 #define BOARD_RAMPS_SMART_EFB 3005 // RAMPS-SMART (Power outputs: Hotend, Fan, Bed) @@ -314,7 +322,7 @@ #define BOARD_ADSK 3101 // Arduino DUE Shield Kit (ADSK) // -// STM32 ARM Cortex-M0+ +// STM32 ARM Cortex-M0/+ // #define BOARD_BTT_EBB42_V1_1 4000 // BigTreeTech EBB42 V1.1 (STM32G0B1CB) @@ -324,84 +332,93 @@ #define BOARD_BTT_MANTA_M5P_V1_0 4004 // BigTreeTech Manta M5P V1.0 (STM32G0B1RE) #define BOARD_BTT_MANTA_M8P_V1_0 4005 // BigTreeTech Manta M8P V1.0 (STM32G0B1VE) #define BOARD_BTT_MANTA_M8P_V1_1 4006 // BigTreeTech Manta M8P V1.1 (STM32G0B1VE) +#define BOARD_BTT_SKRAT_V1_0 4007 // BigTreeTech SKRat V1.0 (STM32G0B1VE) + +// +// STM32 ARM Cortex-M0 +// + +#define BOARD_MALYAN_M200_V2 4100 // STM32F070CB controller +#define BOARD_MALYAN_M300 4101 // STM32F070-based delta +#define BOARD_FLY_D5 4102 // FLY_D5 (STM32F072RB) +#define BOARD_FLY_DP5 4103 // FLY_DP5 (STM32F072RB) +#define BOARD_FLY_D7 4104 // FLY_D7 (STM32F072RB) // // STM32 ARM Cortex-M3 // -#define BOARD_MALYAN_M200_V2 5000 // STM32F070CB controller -#define BOARD_MALYAN_M300 5001 // STM32F070-based delta -#define BOARD_STM32F103RE 5002 // STM32F103RE Libmaple-based STM32F1 controller -#define BOARD_MALYAN_M200 5003 // STM32C8 Libmaple-based STM32F1 controller -#define BOARD_STM3R_MINI 5004 // STM32F103RE Libmaple-based STM32F1 controller -#define BOARD_GTM32_PRO_VB 5005 // STM32F103VE controller -#define BOARD_GTM32_MINI 5006 // STM32F103VE controller -#define BOARD_GTM32_MINI_A30 5007 // STM32F103VE controller -#define BOARD_GTM32_REV_B 5008 // STM32F103VE controller -#define BOARD_MORPHEUS 5009 // STM32F103C8 / STM32F103CB Libmaple-based STM32F1 controller -#define BOARD_CHITU3D 5010 // Chitu3D (STM32F103RE) -#define BOARD_MKS_ROBIN 5011 // MKS Robin (STM32F103ZE) -#define BOARD_MKS_ROBIN_MINI 5012 // MKS Robin Mini (STM32F103VE) -#define BOARD_MKS_ROBIN_NANO 5013 // MKS Robin Nano (STM32F103VE) -#define BOARD_MKS_ROBIN_NANO_V2 5014 // MKS Robin Nano V2 (STM32F103VE) -#define BOARD_MKS_ROBIN_LITE 5015 // MKS Robin Lite/Lite2 (STM32F103RC) -#define BOARD_MKS_ROBIN_LITE3 5016 // MKS Robin Lite3 (STM32F103RC) -#define BOARD_MKS_ROBIN_PRO 5017 // MKS Robin Pro (STM32F103ZE) -#define BOARD_MKS_ROBIN_E3 5018 // MKS Robin E3 (STM32F103RC) -#define BOARD_MKS_ROBIN_E3_V1_1 5019 // MKS Robin E3 V1.1 (STM32F103RC) -#define BOARD_MKS_ROBIN_E3D 5020 // MKS Robin E3D (STM32F103RC) -#define BOARD_MKS_ROBIN_E3D_V1_1 5021 // MKS Robin E3D V1.1 (STM32F103RC) -#define BOARD_MKS_ROBIN_E3P 5022 // MKS Robin E3P (STM32F103VE) -#define BOARD_BTT_SKR_MINI_V1_1 5023 // BigTreeTech SKR Mini v1.1 (STM32F103RC) -#define BOARD_BTT_SKR_MINI_E3_V1_0 5024 // BigTreeTech SKR Mini E3 (STM32F103RC) -#define BOARD_BTT_SKR_MINI_E3_V1_2 5025 // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC) -#define BOARD_BTT_SKR_MINI_E3_V2_0 5026 // BigTreeTech SKR Mini E3 V2.0 (STM32F103RC / STM32F103RE) -#define BOARD_BTT_SKR_MINI_MZ_V1_0 5027 // BigTreeTech SKR Mini MZ V1.0 (STM32F103RC) -#define BOARD_BTT_SKR_E3_DIP 5028 // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE) -#define BOARD_BTT_SKR_CR6 5029 // BigTreeTech SKR CR6 v1.0 (STM32F103RE) -#define BOARD_JGAURORA_A5S_A1 5030 // JGAurora A5S A1 (STM32F103ZE) -#define BOARD_FYSETC_AIO_II 5031 // FYSETC AIO_II (STM32F103RC) -#define BOARD_FYSETC_CHEETAH 5032 // FYSETC Cheetah (STM32F103RC) -#define BOARD_FYSETC_CHEETAH_V12 5033 // FYSETC Cheetah V1.2 (STM32F103RC) -#define BOARD_LONGER3D_LK 5034 // Longer3D LK1/2 - Alfawise U20/U20+/U30 (STM32F103VE) -#define BOARD_CCROBOT_MEEB_3DP 5035 // ccrobot-online.com MEEB_3DP (STM32F103RC) -#define BOARD_CHITU3D_V5 5036 // Chitu3D TronXY X5SA V5 Board (STM32F103ZE) -#define BOARD_CHITU3D_V6 5037 // Chitu3D TronXY X5SA V6 Board (STM32F103ZE) -#define BOARD_CHITU3D_V9 5038 // Chitu3D TronXY X5SA V9 Board (STM32F103ZE) -#define BOARD_CREALITY_V4 5039 // Creality v4.x (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V422 5040 // Creality v4.2.2 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V423 5041 // Creality v4.2.3 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V425 5042 // Creality v4.2.5 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V427 5043 // Creality v4.2.7 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V4210 5044 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30 -#define BOARD_CREALITY_V431 5045 // Creality v4.3.1 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V431_A 5046 // Creality v4.3.1a (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V431_B 5047 // Creality v4.3.1b (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V431_C 5048 // Creality v4.3.1c (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V431_D 5049 // Creality v4.3.1d (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V452 5050 // Creality v4.5.2 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V453 5051 // Creality v4.5.3 (STM32F103RC / STM32F103RE) -#define BOARD_CREALITY_V521 5052 // Creality v5.2.1 (STM32F103VE) as found in the SV04 -#define BOARD_CREALITY_V24S1 5053 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) v101 as found in the Ender-7 -#define BOARD_CREALITY_V24S1_301 5054 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) v301 as found in the Ender-3 S1 -#define BOARD_CREALITY_V25S1 5055 // Creality v2.5.S1 (STM32F103RE) as found in the CR-10 Smart Pro -#define BOARD_TRIGORILLA_PRO 5056 // Trigorilla Pro (STM32F103ZE) -#define BOARD_FLY_MINI 5057 // FLYmaker FLY MINI (STM32F103RC) -#define BOARD_FLSUN_HISPEED 5058 // FLSUN HiSpeedV1 (STM32F103VE) -#define BOARD_BEAST 5059 // STM32F103RE Libmaple-based controller -#define BOARD_MINGDA_MPX_ARM_MINI 5060 // STM32F103ZE Mingda MD-16 -#define BOARD_GTM32_PRO_VD 5061 // STM32F103VE controller -#define BOARD_ZONESTAR_ZM3E2 5062 // Zonestar ZM3E2 (STM32F103RC) -#define BOARD_ZONESTAR_ZM3E4 5063 // Zonestar ZM3E4 V1 (STM32F103VC) -#define BOARD_ZONESTAR_ZM3E4V2 5064 // Zonestar ZM3E4 V2 (STM32F103VC) -#define BOARD_ERYONE_ERY32_MINI 5065 // Eryone Ery32 mini (STM32F103VE) -#define BOARD_PANDA_PI_V29 5066 // Panda Pi V2.9 - Standalone (STM32F103RC) -#define BOARD_SOVOL_V131 5067 // Sovol V1.3.1 (GD32F103RE) -#define BOARD_TRIGORILLA_V006 5068 // Trigorilla V0.0.6 (GD32F103RE) -#define BOARD_KEDI_CONTROLLER_V1_2 5069 // EDUTRONICS Kedi Controller V1.2 (STM32F103RC) -#define BOARD_MD_D301 5070 // Mingda D2 DZ301 V1.0 (STM32F103ZE) -#define BOARD_VOXELAB_AQUILA 5071 // Voxelab Aquila V1.0.0/V1.0.1 (GD32F103RC / N32G455RE / STM32F103RE) -#define BOARD_SPRINGER_CONTROLLER 5072 // ORCA 3D SPRINGER Modular Controller (STM32F103VC) +#define BOARD_STM32F103RE 5000 // STM32F103RE Libmaple-based STM32F1 controller +#define BOARD_MALYAN_M200 5001 // STM32C8 Libmaple-based STM32F1 controller +#define BOARD_STM3R_MINI 5002 // STM32F103RE Libmaple-based STM32F1 controller +#define BOARD_GTM32_PRO_VB 5003 // STM32F103VE controller +#define BOARD_GTM32_PRO_VD 5004 // STM32F103VE controller +#define BOARD_GTM32_MINI 5005 // STM32F103VE controller +#define BOARD_GTM32_MINI_A30 5006 // STM32F103VE controller +#define BOARD_GTM32_REV_B 5007 // STM32F103VE controller +#define BOARD_MORPHEUS 5008 // STM32F103C8 / STM32F103CB Libmaple-based STM32F1 controller +#define BOARD_CHITU3D 5009 // Chitu3D (STM32F103RE) +#define BOARD_MKS_ROBIN 5010 // MKS Robin (STM32F103ZE) +#define BOARD_MKS_ROBIN_MINI 5011 // MKS Robin Mini (STM32F103VE) +#define BOARD_MKS_ROBIN_NANO 5012 // MKS Robin Nano (STM32F103VE) +#define BOARD_MKS_ROBIN_NANO_V2 5013 // MKS Robin Nano V2 (STM32F103VE) +#define BOARD_MKS_ROBIN_LITE 5014 // MKS Robin Lite/Lite2 (STM32F103RC) +#define BOARD_MKS_ROBIN_LITE3 5015 // MKS Robin Lite3 (STM32F103RC) +#define BOARD_MKS_ROBIN_PRO 5016 // MKS Robin Pro (STM32F103ZE) +#define BOARD_MKS_ROBIN_E3 5017 // MKS Robin E3 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3_V1_1 5018 // MKS Robin E3 V1.1 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3D 5019 // MKS Robin E3D (STM32F103RC) +#define BOARD_MKS_ROBIN_E3D_V1_1 5020 // MKS Robin E3D V1.1 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3P 5021 // MKS Robin E3P (STM32F103VE) +#define BOARD_BTT_SKR_MINI_V1_1 5022 // BigTreeTech SKR Mini v1.1 (STM32F103RC) +#define BOARD_BTT_SKR_MINI_E3_V1_0 5023 // BigTreeTech SKR Mini E3 (STM32F103RC) +#define BOARD_BTT_SKR_MINI_E3_V1_2 5024 // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC) +#define BOARD_BTT_SKR_MINI_E3_V2_0 5025 // BigTreeTech SKR Mini E3 V2.0 (STM32F103RC / STM32F103RE) +#define BOARD_BTT_SKR_MINI_MZ_V1_0 5026 // BigTreeTech SKR Mini MZ V1.0 (STM32F103RC) +#define BOARD_BTT_SKR_E3_DIP 5027 // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE) +#define BOARD_BTT_SKR_CR6 5028 // BigTreeTech SKR CR6 v1.0 (STM32F103RE) +#define BOARD_JGAURORA_A5S_A1 5029 // JGAurora A5S A1 (STM32F103ZE) +#define BOARD_FYSETC_AIO_II 5030 // FYSETC AIO_II (STM32F103RC) +#define BOARD_FYSETC_CHEETAH 5031 // FYSETC Cheetah (STM32F103RC) +#define BOARD_FYSETC_CHEETAH_V12 5032 // FYSETC Cheetah V1.2 (STM32F103RC) +#define BOARD_LONGER3D_LK 5033 // Longer3D LK1/2 - Alfawise U20/U20+/U30 (STM32F103VE) +#define BOARD_CCROBOT_MEEB_3DP 5034 // ccrobot-online.com MEEB_3DP (STM32F103RC) +#define BOARD_CHITU3D_V5 5035 // Chitu3D TronXY X5SA V5 Board (STM32F103ZE) +#define BOARD_CHITU3D_V6 5036 // Chitu3D TronXY X5SA V6 Board (STM32F103ZE) +#define BOARD_CHITU3D_V9 5037 // Chitu3D TronXY X5SA V9 Board (STM32F103ZE) +#define BOARD_CREALITY_V4 5038 // Creality v4.x (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V422 5039 // Creality v4.2.2 (STM32F103RC / STM32F103RE) ... GD32 Variant Below! +#define BOARD_CREALITY_V423 5040 // Creality v4.2.3 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V425 5041 // Creality v4.2.5 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V427 5042 // Creality v4.2.7 (STM32F103RC / STM32F103RE) ... GD32 Variant Below! +#define BOARD_CREALITY_V4210 5043 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30 +#define BOARD_CREALITY_V431 5044 // Creality v4.3.1 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_A 5045 // Creality v4.3.1a (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_B 5046 // Creality v4.3.1b (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_C 5047 // Creality v4.3.1c (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_D 5048 // Creality v4.3.1d (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V452 5049 // Creality v4.5.2 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V453 5050 // Creality v4.5.3 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V521 5051 // Creality v5.2.1 (STM32F103VE) as found in the SV04 +#define BOARD_CREALITY_V24S1 5052 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) CR-FDM-v2.4.S1_v101 as found in the Ender-7 +#define BOARD_CREALITY_V24S1_301 5053 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) CR-FDM-v24S1_301 as found in the Ender-3 S1 +#define BOARD_CREALITY_V25S1 5054 // Creality v2.5.S1 (STM32F103RE) CR-FDM-v2.5.S1_100 as found in the CR-10 Smart Pro +#define BOARD_TRIGORILLA_PRO 5055 // Trigorilla Pro (STM32F103ZE) +#define BOARD_FLY_MINI 5056 // FLYmaker FLY MINI (STM32F103RC) +#define BOARD_FLSUN_HISPEED 5057 // FLSUN HiSpeedV1 (STM32F103VE) +#define BOARD_BEAST 5058 // STM32F103RE Libmaple-based controller +#define BOARD_MINGDA_MPX_ARM_MINI 5059 // STM32F103ZE Mingda MD-16 +#define BOARD_ZONESTAR_ZM3E2 5060 // Zonestar ZM3E2 (STM32F103RC) +#define BOARD_ZONESTAR_ZM3E4 5061 // Zonestar ZM3E4 V1 (STM32F103VC) +#define BOARD_ZONESTAR_ZM3E4V2 5062 // Zonestar ZM3E4 V2 (STM32F103VC) +#define BOARD_ERYONE_ERY32_MINI 5063 // Eryone Ery32 mini (STM32F103VE) +#define BOARD_PANDA_PI_V29 5064 // Panda Pi V2.9 - Standalone (STM32F103RC) +#define BOARD_SOVOL_V131 5065 // Sovol V1.3.1 (GD32F103RE) +#define BOARD_TRIGORILLA_V006 5066 // Trigorilla V0.0.6 (GD32F103RE) +#define BOARD_KEDI_CONTROLLER_V1_2 5067 // EDUTRONICS Kedi Controller V1.2 (STM32F103RC) +#define BOARD_MD_D301 5068 // Mingda D2 DZ301 V1.0 (STM32F103ZE) +#define BOARD_VOXELAB_AQUILA 5069 // Voxelab Aquila V1.0.0/V1.0.1 (GD32F103RC / N32G455RE / STM32F103RE) +#define BOARD_SPRINGER_CONTROLLER 5070 // ORCA 3D SPRINGER Modular Controller (STM32F103VC) // // ARM Cortex-M4F @@ -414,76 +431,96 @@ // STM32 ARM Cortex-M4F // -#define BOARD_ARMED 5200 // Arm'ed STM32F4-based controller -#define BOARD_RUMBA32_V1_0 5201 // RUMBA32 STM32F446VE based controller from Aus3D -#define BOARD_RUMBA32_V1_1 5202 // RUMBA32 STM32F446VE based controller from Aus3D -#define BOARD_RUMBA32_MKS 5203 // RUMBA32 STM32F446VE based controller from Makerbase -#define BOARD_RUMBA32_BTT 5204 // RUMBA32 STM32F446VE based controller from BIGTREETECH -#define BOARD_BLACK_STM32F407VE 5205 // BLACK_STM32F407VE -#define BOARD_BLACK_STM32F407ZE 5206 // BLACK_STM32F407ZE -#define BOARD_BTT_SKR_MINI_E3_V3_0_1 5207 // BigTreeTech SKR Mini E3 V3.0.1 (STM32F401RC) -#define BOARD_BTT_SKR_PRO_V1_1 5208 // BigTreeTech SKR Pro v1.1 (STM32F407ZG) -#define BOARD_BTT_SKR_PRO_V1_2 5209 // BigTreeTech SKR Pro v1.2 (STM32F407ZG) -#define BOARD_BTT_BTT002_V1_0 5210 // BigTreeTech BTT002 v1.0 (STM32F407VG) -#define BOARD_BTT_E3_RRF 5211 // BigTreeTech E3 RRF (STM32F407VG) -#define BOARD_BTT_SKR_V2_0_REV_A 5212 // BigTreeTech SKR v2.0 Rev A (STM32F407VG) -#define BOARD_BTT_SKR_V2_0_REV_B 5213 // BigTreeTech SKR v2.0 Rev B (STM32F407VG/STM32F429VG) -#define BOARD_BTT_GTR_V1_0 5214 // BigTreeTech GTR v1.0 (STM32F407IGT) -#define BOARD_BTT_OCTOPUS_V1_0 5215 // BigTreeTech Octopus v1.0 (STM32F446ZE) -#define BOARD_BTT_OCTOPUS_V1_1 5216 // BigTreeTech Octopus v1.1 (STM32F446ZE) -#define BOARD_BTT_OCTOPUS_PRO_V1_0 5217 // BigTreeTech Octopus Pro v1.0 (STM32F446ZE / STM32F429ZG) -#define BOARD_LERDGE_K 5218 // Lerdge K (STM32F407ZG) -#define BOARD_LERDGE_S 5219 // Lerdge S (STM32F407VE) -#define BOARD_LERDGE_X 5220 // Lerdge X (STM32F407VE) -#define BOARD_FYSETC_S6 5221 // FYSETC S6 (STM32F446VE) -#define BOARD_FYSETC_S6_V2_0 5222 // FYSETC S6 v2.0 (STM32F446VE) -#define BOARD_FYSETC_SPIDER 5223 // FYSETC Spider (STM32F446VE) -#define BOARD_FLYF407ZG 5224 // FLYmaker FLYF407ZG (STM32F407ZG) -#define BOARD_MKS_ROBIN2 5225 // MKS Robin2 V1.0 (STM32F407ZE) -#define BOARD_MKS_ROBIN_PRO_V2 5226 // MKS Robin Pro V2 (STM32F407VE) -#define BOARD_MKS_ROBIN_NANO_V3 5227 // MKS Robin Nano V3 (STM32F407VG) -#define BOARD_MKS_ROBIN_NANO_V3_1 5228 // MKS Robin Nano V3.1 (STM32F407VE) -#define BOARD_MKS_MONSTER8_V1 5229 // MKS Monster8 V1 (STM32F407VE) -#define BOARD_MKS_MONSTER8_V2 5230 // MKS Monster8 V2 (STM32F407VE) -#define BOARD_ANET_ET4 5231 // ANET ET4 V1.x (STM32F407VG) -#define BOARD_ANET_ET4P 5232 // ANET ET4P V1.x (STM32F407VG) -#define BOARD_FYSETC_CHEETAH_V20 5233 // FYSETC Cheetah V2.0 (STM32F401RC) -#define BOARD_TH3D_EZBOARD_V2 5234 // TH3D EZBoard v2.0 (STM32F405RG) -#define BOARD_OPULO_LUMEN_REV3 5235 // Opulo Lumen PnP Controller REV3 (STM32F407VE / STM32F407VG) -#define BOARD_MKS_ROBIN_NANO_V1_3_F4 5236 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE) -#define BOARD_MKS_EAGLE 5237 // MKS Eagle (STM32F407VE) -#define BOARD_ARTILLERY_RUBY 5238 // Artillery Ruby (STM32F401RC) -#define BOARD_FYSETC_SPIDER_V2_2 5239 // FYSETC Spider V2.2 (STM32F446VE) -#define BOARD_CREALITY_V24S1_301F4 5240 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4 -#define BOARD_CREALITY_CR4NTXXC10 5241 // Creality E3 Free-runs Silent Motherboard (STM32F401RET6) -#define BOARD_OPULO_LUMEN_REV4 5242 // Opulo Lumen PnP Controller REV4 (STM32F407VE / STM32F407VG) -#define BOARD_FYSETC_SPIDER_KING407 5243 // FYSETC Spider King407 (STM32F407ZG) -#define BOARD_MKS_SKIPR_V1 5244 // MKS SKIPR v1.0 all-in-one board (STM32F407VE) -#define BOARD_TRONXY_CXY_446_V10 5245 // TRONXY CXY-446-V10-220413/CXY-V6-191121 (STM32F446ZE) -#define BOARD_CREALITY_F401RE 5246 // Creality CR4NS200141C13 (STM32F401RE) as found in the Ender-5 S1 -#define BOARD_BLACKPILL_CUSTOM 5247 // Custom board based on STM32F401CDU6. -#define BOARD_I3DBEEZ9_V1 5248 // I3DBEEZ9 V1 (STM32F407ZG) -#define BOARD_MELLOW_FLY_E3_V2 5249 // Mellow Fly E3 V2 (STM32F407VG) -#define BOARD_FYSETC_CHEETAH_V30 5250 // FYSETC Cheetah V3.0 (STM32F446RC) -#define BOARD_BLACKBEEZMINI_V1 5251 // BlackBeezMini V1 (STM32F401CCU6) +#define BOARD_ARMED 5200 // Arm'ed STM32F4-based controller +#define BOARD_RUMBA32_V1_0 5201 // RUMBA32 STM32F446VE based controller from Aus3D +#define BOARD_RUMBA32_V1_1 5202 // RUMBA32 STM32F446VE based controller from Aus3D +#define BOARD_RUMBA32_MKS 5203 // RUMBA32 STM32F446VE based controller from Makerbase +#define BOARD_RUMBA32_BTT 5204 // RUMBA32 STM32F446VE based controller from BIGTREETECH +#define BOARD_BLACK_STM32F407VE 5205 // Black STM32F407VE development board +#define BOARD_BLACK_STM32F407ZE 5206 // Black STM32F407ZE development board +#define BOARD_BTT_SKR_MINI_E3_V3_0_1 5207 // BigTreeTech SKR Mini E3 V3.0.1 (STM32F401RC) +#define BOARD_BTT_SKR_PRO_V1_1 5208 // BigTreeTech SKR Pro v1.1 (STM32F407ZG) +#define BOARD_BTT_SKR_PRO_V1_2 5209 // BigTreeTech SKR Pro v1.2 (STM32F407ZG) +#define BOARD_BTT_BTT002_V1_0 5210 // BigTreeTech BTT002 v1.0 (STM32F407VG) +#define BOARD_BTT_E3_RRF 5211 // BigTreeTech E3 RRF (STM32F407VG) +#define BOARD_BTT_SKR_V2_0_REV_A 5212 // BigTreeTech SKR v2.0 Rev A (STM32F407VG) +#define BOARD_BTT_SKR_V2_0_REV_B 5213 // BigTreeTech SKR v2.0 Rev B (STM32F407VG/STM32F429VG) +#define BOARD_BTT_GTR_V1_0 5214 // BigTreeTech GTR v1.0 (STM32F407IGT) +#define BOARD_BTT_OCTOPUS_V1_0 5215 // BigTreeTech Octopus v1.0 (STM32F446ZE) +#define BOARD_BTT_OCTOPUS_V1_1 5216 // BigTreeTech Octopus v1.1 (STM32F446ZE) +#define BOARD_BTT_OCTOPUS_PRO_V1_0 5217 // BigTreeTech Octopus Pro v1.0 (STM32F446ZE / STM32F429ZG) +#define BOARD_LERDGE_K 5218 // Lerdge K (STM32F407ZG) +#define BOARD_LERDGE_S 5219 // Lerdge S (STM32F407VE) +#define BOARD_LERDGE_X 5220 // Lerdge X (STM32F407VE) +#define BOARD_FYSETC_S6 5221 // FYSETC S6 (STM32F446VE) +#define BOARD_FYSETC_S6_V2_0 5222 // FYSETC S6 v2.0 (STM32F446VE) +#define BOARD_FYSETC_SPIDER 5223 // FYSETC Spider (STM32F446VE) +#define BOARD_FYSETC_SPIDER_V2_2 5224 // FYSETC Spider V2.2 (STM32F446VE) +#define BOARD_FLYF407ZG 5225 // FLYmaker FLYF407ZG (STM32F407ZG) +#define BOARD_MKS_ROBIN2 5226 // MKS Robin2 V1.0 (STM32F407ZE) +#define BOARD_MKS_ROBIN_PRO_V2 5227 // MKS Robin Pro V2 (STM32F407VE) +#define BOARD_MKS_ROBIN_NANO_V3 5228 // MKS Robin Nano V3 (STM32F407VG) +#define BOARD_MKS_ROBIN_NANO_V3_1 5229 // MKS Robin Nano V3.1 (STM32F407VE) +#define BOARD_MKS_MONSTER8_V1 5230 // MKS Monster8 V1 (STM32F407VE) +#define BOARD_MKS_MONSTER8_V2 5231 // MKS Monster8 V2 (STM32F407VE) +#define BOARD_ANET_ET4 5232 // ANET ET4 V1.x (STM32F407VG) +#define BOARD_ANET_ET4P 5233 // ANET ET4P V1.x (STM32F407VG) +#define BOARD_FYSETC_CHEETAH_V20 5234 // FYSETC Cheetah V2.0 (STM32F401RC) +#define BOARD_FYSETC_CHEETAH_V30 5235 // FYSETC Cheetah V3.0 (STM32F446RC) +#define BOARD_TH3D_EZBOARD_V2 5236 // TH3D EZBoard v2.0 (STM32F405RG) +#define BOARD_OPULO_LUMEN_REV3 5237 // Opulo Lumen PnP Controller REV3 (STM32F407VE / STM32F407VG) +#define BOARD_OPULO_LUMEN_REV4 5238 // Opulo Lumen PnP Controller REV4 (STM32F407VE / STM32F407VG) +#define BOARD_MKS_ROBIN_NANO_V1_3_F4 5239 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE) +#define BOARD_MKS_EAGLE 5240 // MKS Eagle (STM32F407VE) +#define BOARD_ARTILLERY_RUBY 5241 // Artillery Ruby (STM32F401RC) +#define BOARD_CREALITY_V24S1_301F4 5242 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4 +#define BOARD_CREALITY_CR4NTXXC10 5243 // Creality E3 Free-runs Silent Motherboard (STM32F401RET6) +#define BOARD_FYSETC_SPIDER_KING_V1_F407 5244 // FYSETC Spider King v1 (STM32F407ZG) +#define BOARD_FYSETC_SPIDER_KING_V1_1_F407 5245 // FYSETC Spider King v1.1 (STM32F407ZG) +#define BOARD_MKS_SKIPR_V1 5246 // MKS SKIPR v1.0 all-in-one board (STM32F407VE) +#define BOARD_TRONXY_CXY_446_V10 5247 // TRONXY CXY-446-V10-220413/CXY-V6-191121 (STM32F446ZE) +#define BOARD_CREALITY_F401RE 5248 // Creality CR4NS200141C13 (STM32F401RE) as found in the Ender-5 S1 +#define BOARD_BLACKPILL_CUSTOM 5249 // Custom board based on STM32F401CDU6. +#define BOARD_I3DBEEZ9_V1 5250 // I3DBEEZ9 V1 (STM32F407ZG) +#define BOARD_MELLOW_FLY_E3_V2 5251 // Mellow Fly E3 V2 (STM32F407VG) +#define BOARD_BLACKBEEZMINI_V1 5252 // BlackBeezMini V1 (STM32F401CCU6) +#define BOARD_XTLW_CLIMBER_8TH 5253 // XTLW Climber-8th (STM32F407VGT6) +#define BOARD_FLY_RRF_E3_V1 5254 // Fly RRF E3 V1.0 (STM32F407VG) +#define BOARD_FLY_SUPER8 5255 // Fly SUPER8 (STM32F407ZGT6) +#define BOARD_FLY_D8 5256 // FLY D8 (STM32F407VG) +#define BOARD_FLY_CDY_V3 5257 // FLY CDY V3 (STM32F407VGT6) +#define BOARD_ZNP_ROBIN_NANO 5258 // Elegoo Neptune 2 v1.2 board +#define BOARD_ZNP_ROBIN_NANO_V1_3 5259 // Elegoo Neptune 2 v1.3 board +#define BOARD_MKS_NEPTUNE_X 5260 // Elegoo Neptune X +#define BOARD_MKS_NEPTUNE_3 5261 // Elegoo Neptune 3 + +// +// Other ARM Cortex-M4 +// +#define BOARD_CREALITY_CR4NS 5300 // Creality CR4NS200320C13 (GD32F303RET6) as found in the Ender-3 V3 SE // // ARM Cortex-M7 // -#define BOARD_REMRAM_V1 6000 // RemRam v1 -#define BOARD_TEENSY41 6001 // Teensy 4.1 -#define BOARD_T41U5XBB 6002 // T41U5XBB Teensy 4.1 breakout board -#define BOARD_NUCLEO_F767ZI 6003 // ST NUCLEO-F767ZI Dev Board -#define BOARD_BTT_SKR_SE_BX_V2 6004 // BigTreeTech SKR SE BX V2.0 (STM32H743II) -#define BOARD_BTT_SKR_SE_BX_V3 6005 // BigTreeTech SKR SE BX V3.0 (STM32H743II) -#define BOARD_BTT_SKR_V3_0 6006 // BigTreeTech SKR V3.0 (STM32H743VI / STM32H723VG) -#define BOARD_BTT_SKR_V3_0_EZ 6007 // BigTreeTech SKR V3.0 EZ (STM32H743VI / STM32H723VG) -#define BOARD_BTT_OCTOPUS_MAX_EZ_V1_0 6008 // BigTreeTech Octopus Max EZ V1.0 (STM32H723ZE) -#define BOARD_BTT_OCTOPUS_PRO_V1_0_1 6009 // BigTreeTech Octopus Pro v1.0.1 (STM32H723ZE) -#define BOARD_BTT_OCTOPUS_PRO_V1_1 6010 // BigTreeTech Octopus Pro v1.1 (STM32H723ZE) -#define BOARD_BTT_MANTA_M8P_V2_0 6011 // BigTreeTech Manta M8P V2.0 (STM32H723ZE) -#define BOARD_BTT_KRAKEN_V1_0 6012 // BigTreeTech Kraken v1.0 (STM32H723ZG) +#define BOARD_REMRAM_V1 6000 // RemRam v1 +#define BOARD_NUCLEO_F767ZI 6001 // ST NUCLEO-F767ZI Dev Board +#define BOARD_BTT_SKR_SE_BX_V2 6002 // BigTreeTech SKR SE BX V2.0 (STM32H743II) +#define BOARD_BTT_SKR_SE_BX_V3 6003 // BigTreeTech SKR SE BX V3.0 (STM32H743II) +#define BOARD_BTT_SKR_V3_0 6004 // BigTreeTech SKR V3.0 (STM32H743VI / STM32H723VG) +#define BOARD_BTT_SKR_V3_0_EZ 6005 // BigTreeTech SKR V3.0 EZ (STM32H743VI / STM32H723VG) +#define BOARD_BTT_OCTOPUS_MAX_EZ_V1_0 6006 // BigTreeTech Octopus Max EZ V1.0 (STM32H723ZE) +#define BOARD_BTT_OCTOPUS_PRO_V1_0_1 6007 // BigTreeTech Octopus Pro v1.0.1 (STM32H723ZE) +#define BOARD_BTT_OCTOPUS_PRO_V1_1 6008 // BigTreeTech Octopus Pro v1.1 (STM32H723ZE) +#define BOARD_BTT_MANTA_M8P_V2_0 6009 // BigTreeTech Manta M8P V2.0 (STM32H723ZE) +#define BOARD_BTT_KRAKEN_V1_0 6010 // BigTreeTech Kraken v1.0 (STM32H723ZG) +#define BOARD_TEENSY40 6011 // Teensy 4.0 +#define BOARD_TEENSY41 6012 // Teensy 4.1 +#define BOARD_T41U5XBB 6013 // T41U5XBB Teensy 4.1 breakout board +#define BOARD_FLY_D8_PRO 6014 // FLY_D8_PRO (STM32H723VG) +#define BOARD_FLY_SUPER8_PRO 6015 // FLY SUPER8 PRO (STM32H723ZG) +#define BOARD_FYSETC_SPIDER_KING_V1_H723 6016 // FYSETC Spider King v1 (STM32H723ZG) +#define BOARD_FYSETC_SPIDER_KING_V1_1_H723 6017 // FYSETC Spider King v1.1 (STM32H723ZG) // // Espressif ESP32 WiFi @@ -520,9 +557,29 @@ // HC32 ARM Cortex-M4 // -#define BOARD_AQUILA_V101 7200 // Voxelab Aquila V1.0.0/V1.0.1/V1.0.2/V1.0.3 as found in the Voxelab Aquila X2 and C2 +#define BOARD_AQUILA_V101 7200 // Voxelab Aquila V1.0.0/1/2/3 (e.g., Aquila X2, C2). ... GD32 Variant Below! #define BOARD_CREALITY_ENDER2P_V24S4 7201 // Creality Ender 2 Pro v2.4.S4_170 (HC32f460kcta) +// +// GD32 ARM Cortex-M3 +// + +#define BOARD_AQUILA_V101_GD32_MFL 7300 // Voxelab Aquila V1.0.1 MFL (GD32F103RC) ... STM32/HC32 Variant Above! + +// +// GD32 ARM Cortex-M4 +// + +#define BOARD_CREALITY_V422_GD32_MFL 7400 // Creality V4.2.2 MFL (GD32F303RE) ... STM32 Variant Above! +#define BOARD_CREALITY_V427_GD32_MFL 7401 // Creality V4.2.7 MFL (GD32F303RE) ... STM32 Variant Above! + +// +// Raspberry Pi +// + +#define BOARD_RP2040 6200 // Generic RP2040 Test board +#define BOARD_BTT_SKR_PICO 6201 // BigTreeTech SKR Pico 1.x + // // Custom board // @@ -533,7 +590,7 @@ // Simulations // -#define BOARD_SIMULATED 9999 +#define BOARD_SIMULATED 9999 // Simulated 3D Printer with LCD / TFT for development #define _MB_1(B) (defined(BOARD_##B) && MOTHERBOARD==BOARD_##B) #define MB(V...) DO(MB,||,V) diff --git a/Marlin/src/core/bug_on.h b/Marlin/src/core/bug_on.h index 7f1243ed40..8ff565ff73 100644 --- a/Marlin/src/core/bug_on.h +++ b/Marlin/src/core/bug_on.h @@ -27,12 +27,12 @@ // Useful macro for stopping the CPU on an unexpected condition // This is used like SERIAL_ECHOPGM, that is: a key-value call of the local variables you want // to dump to the serial port before stopping the CPU. - // \/ Don't replace by SERIAL_ECHOPGM since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building - #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLNPGM(": "); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0) + // \/ Don't use SERIAL_ECHOPGM with ONLY_FILENAME. It can't be a PGM string, + #define BUG_ON(V...) do { SERIAL_ECHOLN(ONLY_FILENAME, __LINE__, F(": ")); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0) #elif ENABLED(MARLIN_DEV_MODE) // Don't stop the CPU here, but at least dump the bug on the serial port - // \/ Don't replace by SERIAL_ECHOPGM since ONLY_FILENAME cannot be transformed to a PGM string on Arduino and it breaks building - #define BUG_ON(V...) do { SERIAL_ECHO(ONLY_FILENAME); SERIAL_ECHO(__LINE__); SERIAL_ECHOLNPGM(": BUG!"); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); } while(0) + // \/ Don't use SERIAL_ECHOPGM with ONLY_FILENAME. It can't be a PGM string, + #define BUG_ON(V...) do { SERIAL_ECHOLN(ONLY_FILENAME, __LINE__, F(": BUG!")); SERIAL_ECHOLNPGM(V); SERIAL_FLUSHTX(); } while(0) #else // Release mode, let's ignore the bug #define BUG_ON(V...) NOOP diff --git a/Marlin/src/core/drivers.h b/Marlin/src/core/drivers.h index e28fc1bba7..80980380a5 100644 --- a/Marlin/src/core/drivers.h +++ b/Marlin/src/core/drivers.h @@ -41,6 +41,7 @@ #define _TMC2208_STANDALONE 0x2208B #define _TMC2209 0x2209A #define _TMC2209_STANDALONE 0x2209B +#define _TMC2240 0x2240A #define _TMC2660 0x2660A #define _TMC2660_STANDALONE 0x2660B #define _TMC5130 0x5130A @@ -96,31 +97,43 @@ // Does not match standalone configurations #if ( HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC2160) \ || HAS_DRIVER(TMC2208) || HAS_DRIVER(TMC2209) \ - || HAS_DRIVER(TMC2660) \ + || HAS_DRIVER(TMC2240) || HAS_DRIVER(TMC2660) \ || HAS_DRIVER(TMC5130) || HAS_DRIVER(TMC5160) ) #define HAS_TRINAMIC_CONFIG 1 #endif #define HAS_TRINAMIC HAS_TRINAMIC_CONFIG -#if ( HAS_DRIVER(TMC2130_STANDALONE) || HAS_DRIVER(TMC2160_STANDALONE) \ +#if ( HAS_DRIVER(TMC2100) \ + || HAS_DRIVER(TMC2130_STANDALONE) || HAS_DRIVER(TMC2160_STANDALONE) \ || HAS_DRIVER(TMC2208_STANDALONE) || HAS_DRIVER(TMC2209_STANDALONE) \ - || HAS_DRIVER(TMC2660_STANDALONE) || HAS_DRIVER(TMC5130_STANDALONE) \ - || HAS_DRIVER(TMC5160_STANDALONE) ) + || HAS_DRIVER(TMC2660_STANDALONE) \ + || HAS_DRIVER(TMC5130_STANDALONE) || HAS_DRIVER(TMC5160_STANDALONE) ) #define HAS_TRINAMIC_STANDALONE 1 #endif #if HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC2160) || HAS_DRIVER(TMC5130) || HAS_DRIVER(TMC5160) #define HAS_TMCX1X0 1 #endif - +#if HAS_TMCX1X0 || HAS_DRIVER(TMC2240) + #define HAS_TMCX1X0_OR_2240 1 +#endif #if HAS_DRIVER(TMC2208) || HAS_DRIVER(TMC2209) #define HAS_TMC220x 1 #endif +//#if HAS_TMC_220x || HAS_DRIVER(TMC2240) +// #define HAS_TMC22xx 1 +//#endif +//#if HAS_TMCX1X0 || HAS_TMC220x +// #define HAS_TMC_CS_ACTUAL 1 +//#endif +//#if HAS_TMCX1X0 || HAS_DRIVER(TMC2209) +// #define HAS_TMC_SG_RESULT 1 +//#endif #define AXIS_IS_TMC(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \ || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) \ - || AXIS_DRIVER_TYPE(A,TMC2660) \ + || AXIS_DRIVER_TYPE(A,TMC2240) || AXIS_DRIVER_TYPE(A,TMC2660) \ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) ) #define AXIS_IS_TMC_CONFIG AXIS_IS_TMC @@ -128,8 +141,8 @@ // Test for a driver that uses SPI - this allows checking whether a _CS_ pin // is considered sensitive #define AXIS_HAS_SPI(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \ - || AXIS_DRIVER_TYPE(A,TMC2660) || AXIS_DRIVER_TYPE(A,TMC5130) \ - || AXIS_DRIVER_TYPE(A,TMC5160) ) + || AXIS_DRIVER_TYPE(A,TMC2240) || AXIS_DRIVER_TYPE(A,TMC2660) \ + || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) ) #define AXIS_HAS_UART(A) ( AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) ) @@ -139,19 +152,21 @@ #define AXIS_HAS_SW_SERIAL(A) ( AXIS_HAS_UART(A) && !defined(A##_HARDWARE_SERIAL) ) #define AXIS_HAS_STALLGUARD(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \ - || AXIS_DRIVER_TYPE(A,TMC2209) \ + || AXIS_DRIVER_TYPE(A,TMC2209) || AXIS_DRIVER_TYPE(A,TMC2240) \ || AXIS_DRIVER_TYPE(A,TMC2660) \ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) ) #define AXIS_HAS_STEALTHCHOP(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \ || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) \ + || AXIS_DRIVER_TYPE(A,TMC2240) \ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) ) #define AXIS_HAS_SG_RESULT(A) ( AXIS_DRIVER_TYPE(A,TMC2130) || AXIS_DRIVER_TYPE(A,TMC2160) \ - || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) ) + || AXIS_DRIVER_TYPE(A,TMC2208) || AXIS_DRIVER_TYPE(A,TMC2209) \ + || AXIS_DRIVER_TYPE(A,TMC2240) ) #define AXIS_HAS_COOLSTEP(A) ( AXIS_DRIVER_TYPE(A,TMC2130) \ - || AXIS_DRIVER_TYPE(A,TMC2209) \ + || AXIS_DRIVER_TYPE(A,TMC2209) || AXIS_DRIVER_TYPE(A,TMC2240) \ || AXIS_DRIVER_TYPE(A,TMC5130) || AXIS_DRIVER_TYPE(A,TMC5160) ) #define _OR_EAH(N,T) || AXIS_HAS_##T(E##N) @@ -187,3 +202,17 @@ || HAS_DRIVER(TMC5130_STANDALONE) || HAS_DRIVER(TMC5160_STANDALONE) #define HAS_DIAG_PINS 1 #endif + +// Hybrid Threshold ranges +#define THRS_TMC2100 65535 +#define THRS_TMC2130 65535 +#define THRS_TMC2160 255 +#define THRS_TMC2208 255 +#define THRS_TMC2209 255 +#define THRS_TMC2240 255 +#define THRS_TMC2660 65535 +#define THRS_TMC5130 65535 +#define THRS_TMC5160 65535 + +#define _DRIVER_THRS(V) CAT(THRS_, V) +#define STEPPER_MAX_THRS(S) _DRIVER_THRS(S##_DRIVER_TYPE) diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 82ed6cc549..e0a31db9ab 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -88,6 +88,7 @@ #undef MACHINE_NAME #define MACHINE_NAME DEFAULT_MACHINE_NAME #endif +#define MACHINE_NAME_SUBST TERN(CONFIGURABLE_MACHINE_NAME, "$", MACHINE_NAME) #define MARLIN_WEBSITE_URL "marlinfw.org" @@ -135,6 +136,7 @@ #define STR_BUSY_PAUSED_FOR_USER "busy: paused for user" #define STR_BUSY_PAUSED_FOR_INPUT "busy: paused for input" #define STR_Z_MOVE_COMP "Z_move_comp" +#define STR_LINE_NO "Line: " #define STR_RESEND "Resend: " #define STR_UNKNOWN_COMMAND "Unknown command: \"" #define STR_ACTIVE_EXTRUDER "Active Extruder: " @@ -161,8 +163,8 @@ #define STR_SOFT_MIN " Min: " #define STR_SOFT_MAX " Max: " -#define STR_SAVED_POS "Position saved" -#define STR_RESTORING_POS "Restoring position" +#define STR_SAVED_POSITION "Saved position #" +#define STR_RESTORING_POSITION "Restoring position #" #define STR_INVALID_POS_SLOT "Invalid slot. Total: " #define STR_DONE "Done." @@ -190,6 +192,7 @@ #define STR_ERR_HOTEND_TOO_COLD "Hotend too cold" #define STR_ERR_EEPROM_WRITE "Error writing to EEPROM!" #define STR_ERR_EEPROM_CORRUPT "EEPROM Corrupt" +#define STR_EEPROM_INITIALIZED "EEPROM Initialized" #define STR_FILAMENT_CHANGE_HEAT_LCD "Press button to heat nozzle" #define STR_FILAMENT_CHANGE_INSERT_LCD "Insert filament and press button" @@ -289,9 +292,11 @@ #define STR_MAX_ACCELERATION "Max Acceleration (units/s2)" #define STR_MAX_FEEDRATES "Max feedrates (units/s)" #define STR_ACCELERATION_P_R_T "Acceleration (units/s2) (P R T)" +#define STR_HOMING_FEEDRATE "Homing Feedrate" #define STR_TOOL_CHANGING "Tool-changing" #define STR_HOTEND_OFFSETS "Hotend offsets" #define STR_SERVO_ANGLES "Servo Angles" +#define STR_AUTOTEMP "Auto Temp Control" #define STR_HOTEND_PID "Hotend PID" #define STR_BED_PID "Bed PID" #define STR_CHAMBER_PID "Chamber PID" @@ -308,8 +313,9 @@ #define STR_FILAMENT_RUNOUT_SENSOR "Filament runout sensor" #define STR_DRIVER_STEPPING_MODE "Driver stepping mode" #define STR_STEPPER_DRIVER_CURRENT "Stepper driver current" +#define STR_HOMING_CURRENT "Homing Current (mA)" #define STR_HYBRID_THRESHOLD "Hybrid Threshold" -#define STR_STALLGUARD_THRESHOLD "StallGuard threshold" +#define STR_STALLGUARD_THRESHOLD "StallGuard Threshold" #define STR_HOME_OFFSET "Home offset" #define STR_SOFT_ENDSTOPS "Soft endstops" #define STR_MATERIAL_HEATUP "Material heatup parameters" @@ -321,6 +327,53 @@ #define STR_TEMPERATURE_UNITS "Temperature Units" #define STR_USER_THERMISTORS "User thermistors" #define STR_DELAYED_POWEROFF "Delayed poweroff" +#define STR_STORED_MACROS "Stored macros" + +// +// General axis names +// +#if HAS_X_AXIS + #define AXIS1_NAME 'X' +#endif +#if HAS_Y_AXIS + #define AXIS2_NAME 'Y' +#endif +#if HAS_Z_AXIS + #define AXIS3_NAME 'Z' +#endif +#define STR_X "X" +#define STR_Y "Y" +#define STR_Z "Z" +#define STR_E "E" +#if IS_KINEMATIC + #define STR_A "A" + #define STR_B "B" + #define STR_C "C" +#else + #define STR_A STR_X + #define STR_B STR_Y + #define STR_C STR_Z +#endif +#define STR_X2 STR_A "2" +#define STR_Y2 STR_B "2" +#define STR_Z2 STR_C "2" +#define STR_Z3 STR_C "3" +#define STR_Z4 STR_C "4" +#if CORE_IS_XY || CORE_IS_XZ + #define STEPPER_A_NAME 'A' +#else + #define STEPPER_A_NAME 'X' +#endif +#if CORE_IS_XY || CORE_IS_YZ + #define STEPPER_B_NAME 'B' +#else + #define STEPPER_B_NAME 'Y' +#endif +#if CORE_IS_XZ || CORE_IS_YZ + #define STEPPER_C_NAME 'C' +#else + #define STEPPER_C_NAME 'Z' +#endif // // Endstop Names used by Endstops::report_states @@ -353,26 +406,7 @@ #define STR_Z_PROBE "z_probe" #define STR_PROBE_EN "probe_en" #define STR_FILAMENT "filament" - -// General axis names -#define STR_X "X" -#define STR_Y "Y" -#define STR_Z "Z" -#define STR_E "E" -#if IS_KINEMATIC - #define STR_A "A" - #define STR_B "B" - #define STR_C "C" -#else - #define STR_A "X" - #define STR_B "Y" - #define STR_C "Z" -#endif -#define STR_X2 "X2" -#define STR_Y2 "Y2" -#define STR_Z2 "Z2" -#define STR_Z3 "Z3" -#define STR_Z4 "Z4" +#define STR_CALIBRATION "calibration" // Extra Axis and Endstop Names #if HAS_I_AXIS diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 784002c769..d66450f758 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -55,19 +55,17 @@ #define CYCLES_PER_MICROSECOND (F_CPU / 1000000UL) // 16 or 20 on AVR #endif -// Nanoseconds per cycle -#define NANOSECONDS_PER_CYCLE (1000000000.0 / F_CPU) - // Macros to make a string from a macro #define STRINGIFY_(M) #M #define STRINGIFY(M) STRINGIFY_(M) +#define CHARIFY(M) STRINGIFY(M)[0] #define A(CODE) " " CODE "\n\t" #define L(CODE) CODE ":\n\t" // Macros for bit masks #undef _BV -#define _BV(n) (1<<(n)) +#define _BV(b) (1 << (b)) #define TEST(n,b) (!!((n)&_BV(b))) #define SET_BIT_TO(N,B,TF) do{ if (TF) SBI(N,B); else CBI(N,B); }while(0) #ifndef SBI @@ -83,18 +81,22 @@ #define CBI32(n,b) (n &= ~_BV32(b)) #define TBI32(N,B) (N ^= _BV32(B)) +// Macros for common maths operations #define cu(x) ({__typeof__(x) _x = (x); (_x)*(_x)*(_x);}) #define RADIANS(d) ((d)*float(M_PI)/180.0f) #define DEGREES(r) ((r)*180.0f/float(M_PI)) #define HYPOT2(x,y) (sq(x)+sq(y)) #define NORMSQ(x,y,z) (sq(x)+sq(y)+sq(z)) -#define CIRCLE_AREA(R) (float(M_PI) * sq(float(R))) +#define FLOAT_SQ(I) sq(float(I)) +#define CIRCLE_AREA(R) (float(M_PI) * FLOAT_SQ(R)) #define CIRCLE_CIRC(R) (2 * float(M_PI) * float(R)) #define SIGN(a) ({__typeof__(a) _a = (a); (_a>0)-(_a<0);}) #define IS_POWER_OF_2(x) ((x) && !((x) & ((x) - 1))) +#define FLIP(X) (X = !(X)) + // Macros to constrain values #ifdef __cplusplus @@ -196,8 +198,8 @@ #define ENABLED(V...) DO(ENA,&&,V) #define DISABLED(V...) DO(DIS,&&,V) #define ANY(V...) !DISABLED(V) -#define ALL ENABLED -#define NONE DISABLED +#define ALL(V...) ENABLED(V) +#define NONE(V...) DISABLED(V) #define COUNT_ENABLED(V...) DO(ENA,+,V) #define MANY(V...) (COUNT_ENABLED(V) > 1) @@ -205,19 +207,23 @@ #define TERN(O,A,B) _TERN(_ENA_1(O),B,A) // OPTION ? 'A' : 'B' #define TERN0(O,A) _TERN(_ENA_1(O),0,A) // OPTION ? 'A' : '0' #define TERN1(O,A) _TERN(_ENA_1(O),1,A) // OPTION ? 'A' : '1' -#define TERN_(O,A) _TERN(_ENA_1(O),,A) // OPTION ? 'A' : '' #define _TERN(E,V...) __TERN(_CAT(T_,E),V) // Prepend 'T_' to get 'T_0' or 'T_1' #define __TERN(T,V...) ___TERN(_CAT(_NO,T),V) // Prepend '_NO' to get '_NOT_0' or '_NOT_1' #define ___TERN(P,V...) THIRD(P,V) // If first argument has a comma, A. Else B. #define IF_DISABLED(O,A) TERN(O,,A) +// "Ternary" that emits or omits the given content +#define EMIT(V...) V +#define OMIT(...) +#define TERN_(O,A) _TERN(_ENA_1(O),OMIT,EMIT)(A) // OPTION ? 'A' : '' + // Macros to conditionally emit array items and function arguments #define _OPTITEM(A...) A, -#define OPTITEM(O,A...) TERN_(O,DEFER4(_OPTITEM)(A)) +#define OPTITEM(O,A...) TERN_(O,DEFER(_OPTITEM)(A)) #define _OPTARG(A...) , A -#define OPTARG(O,A...) TERN_(O,DEFER4(_OPTARG)(A)) +#define OPTARG(O,A...) TERN_(O,DEFER(_OPTARG)(A)) #define _OPTCODE(A) A; -#define OPTCODE(O,A) TERN_(O,DEFER4(_OPTCODE)(A)) +#define OPTCODE(O,A) TERN_(O,DEFER(_OPTCODE)(A)) // Macros to avoid operations that aren't always optimized away (e.g., 'f + 0.0' and 'f * 1.0'). // Compiler flags -fno-signed-zeros -ffinite-math-only also cover 'f * 1.0', 'f - f', etc. @@ -302,6 +308,12 @@ #define GANG_N_1(N,K) _GANG_N(N,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K) // Expansion of some list items +#define LIST_32(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,FF +#define LIST_31(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,EE +#define LIST_30(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,DD +#define LIST_29(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,CC +#define LIST_28(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,BB,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,TU,V,W,X,Y,Z,AA,BB +#define LIST_27(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,AA #define LIST_26(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z #define LIST_25(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y #define LIST_24(A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,...) A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X @@ -542,6 +554,9 @@ #endif +// Limit an index to an array size +#define ALIM(I,ARR) _MIN(I, (signed)COUNT(ARR) - 1) + // Macros for adding #define INC_0 1 #define INC_1 2 @@ -564,6 +579,17 @@ #define INC_18 19 #define INC_19 20 #define INC_20 21 +#define INC_21 22 +#define INC_22 23 +#define INC_23 24 +#define INC_24 25 +#define INC_25 26 +#define INC_26 27 +#define INC_27 28 +#define INC_28 29 +#define INC_29 30 +#define INC_30 31 +#define INC_31 32 #define INCREMENT_(n) INC_##n #define INCREMENT(n) INCREMENT_(n) @@ -599,6 +625,23 @@ #define DEC_13 12 #define DEC_14 13 #define DEC_15 14 +#define DEC_16 15 +#define DEC_17 16 +#define DEC_18 17 +#define DEC_19 18 +#define DEC_20 19 +#define DEC_21 20 +#define DEC_22 21 +#define DEC_23 22 +#define DEC_24 23 +#define DEC_25 24 +#define DEC_26 25 +#define DEC_27 26 +#define DEC_28 27 +#define DEC_29 28 +#define DEC_30 29 +#define DEC_31 30 +#define DEC_32 31 #define DECREMENT_(n) DEC_##n #define DECREMENT(n) DECREMENT_(n) @@ -629,7 +672,7 @@ #define DEFER4(M) M EMPTY EMPTY EMPTY EMPTY()()()() // Force define expansion -#define EVAL EVAL16 +#define EVAL(V...) EVAL16(V) #define EVAL4096(V...) EVAL2048(EVAL2048(V)) #define EVAL2048(V...) EVAL1024(EVAL1024(V)) #define EVAL1024(V...) EVAL512(EVAL512(V)) @@ -653,11 +696,8 @@ #define IF_ELSE(TF) _IF_ELSE(_BOOL(TF)) #define _IF_ELSE(TF) _CAT(_IF_, TF) -#define _IF_1(V...) V _IF_1_ELSE -#define _IF_0(...) _IF_0_ELSE - -#define _IF_1_ELSE(...) -#define _IF_0_ELSE(V...) V +#define _IF_1(V...) V OMIT +#define _IF_0(...) EMIT #define HAS_ARGS(V...) _BOOL(FIRST(_END_OF_ARGUMENTS_ V)()) #define _END_OF_ARGUMENTS_() 0 diff --git a/Marlin/src/core/millis_t.h b/Marlin/src/core/millis_t.h index e7032a2e55..1d3cc853b3 100644 --- a/Marlin/src/core/millis_t.h +++ b/Marlin/src/core/millis_t.h @@ -30,5 +30,7 @@ typedef uint32_t millis_t; #define MS_TO_SEC(N) millis_t((N)/1000UL) #define MS_TO_SEC_PRECISE(N) (float(N)/1000.0f) -#define PENDING(NOW,SOON) ((int32_t)(NOW-(SOON))<0) -#define ELAPSED(NOW,SOON) (!PENDING(NOW,SOON)) +constexpr bool _PENDING(const millis_t now, const millis_t when) { return int32_t(when - now) > 0; } +constexpr bool _PENDING(const millis_t now, const millis_t start, const millis_t interval) { return (now - start) < interval; } +#define PENDING(V...) _PENDING(V) +#define ELAPSED(V...) !_PENDING(V) diff --git a/Marlin/src/core/mstring.h b/Marlin/src/core/mstring.h index 31e8c8c6e6..e3fb78c50e 100644 --- a/Marlin/src/core/mstring.h +++ b/Marlin/src/core/mstring.h @@ -143,13 +143,13 @@ public: // Set with format string and arguments, like printf template - MString& setf_P(PGM_P const fmt, Args... more) { SNPRINTF_P(str, SIZE, fmt, more...); debug(F("setf_P")); return *this; } + MString& setf_P(PGM_P const pfmt, Args... more) { SNPRINTF_P(str, SIZE, pfmt, more...); debug(F("setf_P")); return *this; } template - MString& setf(const char *fmt, Args... more) { SNPRINTF(str, SIZE, fmt, more...); debug(F("setf")); return *this; } + MString& setf(const char *fmt, Args... more) { SNPRINTF(str, SIZE, fmt, more...); debug(F("setf")); return *this; } template - MString& setf(FSTR_P const fmt, Args... more) { return setf_P(FTOP(fmt), more...); } + MString& setf(FSTR_P const ffmt, Args... more) { return setf_P(FTOP(ffmt), more...); } // Chainable String appenders MString& append() { debug(F("nil")); return *this; } // for macros that might emit no output @@ -206,9 +206,9 @@ public: MString& append(const spaces_t &s) { return append(repchr_t(' ', s.count)); } template - MString& appendf_P(PGM_P const fmt, Args... more) { + MString& appendf_P(PGM_P const pfmt, Args... more) { int sz = length(); - if (sz < SIZE) SNPRINTF_P(str + sz, SIZE - sz, fmt, more...); + if (sz < SIZE) SNPRINTF_P(str + sz, SIZE - sz, pfmt, more...); debug(F("appendf_P")); return *this; } @@ -298,6 +298,9 @@ public: MString& clear() { return set(); } MString& eol() { return append('\n'); } MString& trunc(const int &i) { if (i <= SIZE) str[i] = '\0'; debug(F("trunc")); return *this; } + MString& ltrim() { char *s = str; while (*s == ' ') ++s; if (s != str) strcpy(str, s); return *this; } + MString& rtrim() { int s = length(); while (s && str[s - 1] == ' ') --s; str[s] = '\0'; return *this; } + MString& trim() { return rtrim().ltrim(); } // Truncate on a Unicode boundary MString& utrunc(const int &n=SIZE) { diff --git a/Marlin/src/core/multi_language.h b/Marlin/src/core/multi_language.h index 2c0eb7aa72..81a1754d5e 100644 --- a/Marlin/src/core/multi_language.h +++ b/Marlin/src/core/multi_language.h @@ -91,6 +91,7 @@ typedef const char Language_Str[]; #define LANG_CHARSIZE GET_TEXT(CHARSIZE) #define USE_WIDE_GLYPH (LANG_CHARSIZE > 2) +// The final "\0" is added invisibly by the compiler #define MSG_1_LINE(A) A "\0" "\0" #define MSG_2_LINE(A,B) A "\0" B "\0" #define MSG_3_LINE(A,B,C) A "\0" B "\0" C diff --git a/Marlin/src/core/serial.cpp b/Marlin/src/core/serial.cpp index d6c4b55459..852cfc77f6 100644 --- a/Marlin/src/core/serial.cpp +++ b/Marlin/src/core/serial.cpp @@ -27,6 +27,8 @@ #include "../feature/ethernet.h" #endif +#include // dtostrf + // Echo commands to the terminal by default in dev mode uint8_t marlin_debug_flags = TERN(MARLIN_DEV_MODE, MARLIN_DEBUG_ECHO, MARLIN_DEBUG_NONE); @@ -75,8 +77,8 @@ template <> void SERIAL_ECHO(const p_float_t pf) { SERIAL_IMPL.print(pf.value, p template <> void SERIAL_ECHO(const w_float_t wf) { char f1[20]; SERIAL_IMPL.print(dtostrf(wf.value, wf.width, wf.prec, f1)); } // Specializations for F-string -template <> void SERIAL_ECHO(const FSTR_P fstr) { SERIAL_ECHO_P(FTOP(fstr)); } -template <> void SERIAL_ECHOLN(const FSTR_P fstr) { SERIAL_ECHOLN_P(FTOP(fstr)); } +template <> void SERIAL_ECHO(FSTR_P const fstr) { SERIAL_ECHO_P(FTOP(fstr)); } +template <> void SERIAL_ECHOLN(FSTR_P const fstr) { SERIAL_ECHOLN_P(FTOP(fstr)); } void SERIAL_CHAR(char a) { SERIAL_IMPL.write(a); } void SERIAL_EOL() { SERIAL_CHAR('\n'); } @@ -97,7 +99,7 @@ void SERIAL_WARN_START() { SERIAL_ECHO(F("Warning:")); } void SERIAL_ECHO_SP(uint8_t count) { count *= (PROPORTIONAL_FONT_RATIO); while (count--) SERIAL_CHAR(' '); } -void serial_offset(const_float_t v, const uint8_t sp/*=0*/) { +void serial_offset(const float v, const uint8_t sp/*=0*/) { if (v == 0 && sp == 1) SERIAL_CHAR(' '); else if (v > 0 || (v == 0 && sp == 2)) @@ -112,10 +114,6 @@ void serial_ternary(FSTR_P const pre, const bool onoff, FSTR_P const on, FSTR_P if (post) SERIAL_ECHO(post); } -void serialprint_onoff(const bool onoff) { SERIAL_ECHO(onoff ? F(STR_ON) : F(STR_OFF)); } -void serialprintln_onoff(const bool onoff) { serialprint_onoff(onoff); SERIAL_EOL(); } -void serialprint_truefalse(const bool tf) { SERIAL_ECHO(tf ? F("true") : F("false")); } - void print_bin(uint16_t val) { for (uint8_t i = 16; i--;) { SERIAL_CHAR('0' + TEST(val, i)); @@ -123,21 +121,23 @@ void print_bin(uint16_t val) { } } -void _print_xyz(NUM_AXIS_ARGS_(const_float_t) FSTR_P const prefix) { +void _print_xyz(NUM_AXIS_ARGS_(const float) FSTR_P const prefix) { if (prefix) SERIAL_ECHO(prefix); #if NUM_AXES - SERIAL_ECHOPGM_P( - LIST_N(DOUBLE(NUM_AXES), SP_X_STR, x, SP_Y_STR, y, SP_Z_STR, z, SP_I_STR, i, SP_J_STR, j, SP_K_STR, k, SP_U_STR, u, SP_V_STR, v, SP_W_STR, w) - ); + SERIAL_ECHOPGM_P(NUM_AXIS_PAIRED_LIST( + SP_X_STR, x, SP_Y_STR, y, SP_Z_STR, z, + SP_I_STR, i, SP_J_STR, j, SP_K_STR, k, + SP_U_STR, u, SP_V_STR, v, SP_W_STR, w + )); #endif } -void print_xyz(NUM_AXIS_ARGS_(const_float_t) FSTR_P const prefix/*=nullptr*/, FSTR_P const suffix/*=nullptr*/) { +void print_xyz(NUM_AXIS_ARGS_(const float) FSTR_P const prefix/*=nullptr*/, FSTR_P const suffix/*=nullptr*/) { _print_xyz(NUM_AXIS_LIST_(x, y, z, i, j, k, u, v, w) prefix); if (suffix) SERIAL_ECHO(suffix); else SERIAL_EOL(); } -void print_xyze(LOGICAL_AXIS_ARGS_(const_float_t) FSTR_P const prefix/*=nullptr*/, FSTR_P const suffix/*=nullptr*/) { +void print_xyze(LOGICAL_AXIS_ARGS_(const float) FSTR_P const prefix/*=nullptr*/, FSTR_P const suffix/*=nullptr*/) { _print_xyz(NUM_AXIS_LIST_(x, y, z, i, j, k, u, v, w) prefix); #if HAS_EXTRUDERS SERIAL_ECHOPGM_P(SP_E_STR, e); diff --git a/Marlin/src/core/serial.h b/Marlin/src/core/serial.h index f9b73e6d26..6c73d72a22 100644 --- a/Marlin/src/core/serial.h +++ b/Marlin/src/core/serial.h @@ -171,8 +171,8 @@ template<> void SERIAL_ECHO(const p_float_t pf); template<> void SERIAL_ECHO(const w_float_t wf); // Specializations for F-string -template<> void SERIAL_ECHO(const FSTR_P fstr); -template<> void SERIAL_ECHOLN(const FSTR_P fstr); +template<> void SERIAL_ECHO(FSTR_P const fstr); +template<> void SERIAL_ECHOLN(FSTR_P const fstr); // Print any number of items with arbitrary types (except loose PROGMEM strings) template @@ -185,7 +185,7 @@ void SERIAL_ECHOLN(T arg1, Args ... args) { SERIAL_ECHO(arg1); SERIAL_ECHO(args // all the odd loose string elements as PROGMEM strings. // -// Print up to 20 pairs of values. Odd elements must be literal strings. +// Print pairs of values. Odd elements must be literal strings. #define __SEP_N(N,V...) _SEP_##N(V) #define _SEP_N(N,V...) __SEP_N(N,V) #define _SEP_N_REF() _SEP_N @@ -194,7 +194,7 @@ void SERIAL_ECHOLN(T arg1, Args ... args) { SERIAL_ECHO(arg1); SERIAL_ECHO(args #define _SEP_3(s,v,V...) _SEP_2(s,v); DEFER2(_SEP_N_REF)()(TWO_ARGS(V),V); #define SERIAL_ECHOPGM(V...) do{ EVAL(_SEP_N(TWO_ARGS(V),V)); }while(0) -// Print up to 20 pairs of values followed by newline. Odd elements must be literal strings. +// Print pairs of values followed by newline. Odd elements must be literal strings. #define __SELP_N(N,V...) _SELP_##N(V) #define _SELP_N(N,V...) __SELP_N(N,V) #define _SELP_N_REF() _SELP_N @@ -203,7 +203,7 @@ void SERIAL_ECHOLN(T arg1, Args ... args) { SERIAL_ECHO(arg1); SERIAL_ECHO(args #define _SELP_3(s,v,V...) _SEP_2(s,v); DEFER2(_SELP_N_REF)()(TWO_ARGS(V),V); #define SERIAL_ECHOLNPGM(V...) do{ EVAL(_SELP_N(TWO_ARGS(V),V)); }while(0) -// Print up to 20 pairs of values. Odd elements must be PSTR pointers. +// Print pairs of values. Odd elements must be PSTR pointers. #define __SEP_N_P(N,V...) _SEP_##N##_P(V) #define _SEP_N_P(N,V...) __SEP_N_P(N,V) #define _SEP_N_P_REF() _SEP_N_P @@ -212,7 +212,7 @@ void SERIAL_ECHOLN(T arg1, Args ... args) { SERIAL_ECHO(arg1); SERIAL_ECHO(args #define _SEP_3_P(p,v,V...) _SEP_2_P(p,v); DEFER2(_SEP_N_P_REF)()(TWO_ARGS(V),V); #define SERIAL_ECHOPGM_P(V...) do{ EVAL(_SEP_N_P(TWO_ARGS(V),V)); }while(0) -// Print up to 20 pairs of values followed by newline. Odd elements must be PSTR pointers. +// Print pairs of values followed by newline. Odd elements must be PSTR pointers. #define __SELP_N_P(N,V...) _SELP_##N##_P(V) #define _SELP_N_P(N,V...) __SELP_N_P(N,V) #define _SELP_N_P_REF() _SELP_N_P @@ -233,21 +233,21 @@ void serial_ternary(FSTR_P const pre, const bool onoff, FSTR_P const on, FSTR_P // Print up to 255 spaces void SERIAL_ECHO_SP(uint8_t count); -void serialprint_onoff(const bool onoff); -void serialprintln_onoff(const bool onoff); -void serialprint_truefalse(const bool tf); -void serial_offset(const_float_t v, const uint8_t sp=0); // For v==0 draw space (sp==1) or plus (sp==2) +inline FSTR_P const ON_OFF(const bool onoff) { return onoff ? F("ON") : F("OFF"); } +inline FSTR_P const TRUE_FALSE(const bool tf) { return tf ? F("true") : F("false"); } + +void serial_offset(const float v, const uint8_t sp=0); // For v==0 draw space (sp==1) or plus (sp==2) void print_bin(const uint16_t val); -void print_xyz(NUM_AXIS_ARGS_(const_float_t) FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr); +void print_xyz(NUM_AXIS_ARGS_(const float) FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr); inline void print_xyz(const xyz_pos_t &xyz, FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr) { print_xyz(NUM_AXIS_ELEM_(xyz) prefix, suffix); } -void print_xyze(LOGICAL_AXIS_ARGS_(const_float_t) FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr); +void print_xyze(LOGICAL_AXIS_ARGS_(const float) FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr); inline void print_xyze(const xyze_pos_t &xyze, FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr) { - print_xyze(LOGICAL_AXIS_ELEM_(xyze) prefix, suffix); + print_xyze(LOGICAL_AXIS_ELEM_LC_(xyze) prefix, suffix); } #define SERIAL_POS(SUFFIX,VAR) do { print_xyz(VAR, F(" " STRINGIFY(VAR) "="), F(" : " SUFFIX "\n")); }while(0) @@ -271,13 +271,13 @@ public: SString& set() { super::set(); return *this; } template - SString& setf_P(PGM_P const fmt, Args... more) { super::setf_P(fmt, more...); return *this; } + SString& setf_P(PGM_P const pfmt, Args... more) { super::setf_P(pfmt, more...); return *this; } template - SString& setf(const char *fmt, Args... more) { super::setf(fmt, more...); return *this; } + SString& setf(const char *fmt, Args... more) { super::setf(fmt, more...); return *this; } template - SString& setf(FSTR_P const fmt, Args... more) { super::setf(fmt, more...); return *this; } + SString& setf(FSTR_P const ffmt, Args... more) { super::setf(ffmt, more...); return *this; } template SString& set(const T &v) { super::set(v); return *this; } diff --git a/Marlin/src/core/serial_base.h b/Marlin/src/core/serial_base.h index a2f49417b7..07bed55b35 100644 --- a/Marlin/src/core/serial_base.h +++ b/Marlin/src/core/serial_base.h @@ -220,7 +220,7 @@ struct SerialBase { // On non 2-complement CPU, there would be no possible representation for 2147483648. write('-'); } - printNumber_unsigned((uint_fixed_print_t)n , base); + printNumber_unsigned((uint_fixed_print_t)n, base); } // Print a decimal number @@ -228,7 +228,7 @@ struct SerialBase { // Handle negative numbers if (number < 0.0) { write('-'); - number = -number; + number *= -1; } // Round correctly so that print(1.999, 2) prints as "2.00" diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index 44805e130a..486f78fde8 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -42,33 +42,47 @@ template struct IF { typedef L type; }; #define NUM_AXIS_LIST_1(V) LIST_N_1(NUM_AXES, V) #define NUM_AXIS_ARRAY(V...) { NUM_AXIS_LIST(V) } #define NUM_AXIS_ARRAY_1(V) { NUM_AXIS_LIST_1(V) } -#define NUM_AXIS_ARGS(T) NUM_AXIS_LIST(T x, T y, T z, T i, T j, T k, T u, T v, T w) -#define NUM_AXIS_ELEM(O) NUM_AXIS_LIST(O.x, O.y, O.z, O.i, O.j, O.k, O.u, O.v, O.w) -#define NUM_AXIS_DECL(T,V) NUM_AXIS_LIST(T x=V, T y=V, T z=V, T i=V, T j=V, T k=V, T u=V, T v=V, T w=V) +#define NUM_AXIS_ARGS(T) NUM_AXIS_LIST(T X, T Y, T Z, T I, T J, T K, T U, T V, T W) +#define NUM_AXIS_ARGS_LC(T) NUM_AXIS_LIST(T x, T y, T z, T i, T j, T k, T u, T v, T w) +#define NUM_AXIS_ELEM(O) NUM_AXIS_LIST(O.X, O.Y, O.Z, O.I, O.J, O.K, O.U, O.V, O.W) +#define NUM_AXIS_ELEM_LC(O) NUM_AXIS_LIST(O.x, O.y, O.z, O.i, O.j, O.k, O.u, O.v, O.w) +#define NUM_AXIS_DECL(T,V) NUM_AXIS_LIST(T X=V, T Y=V, T Z=V, T I=V, T J=V, T K=V, T U=V, T V=V, T W=V) +#define NUM_AXIS_DECL_LC(T,V) NUM_AXIS_LIST(T x=V, T y=V, T z=V, T i=V, T j=V, T k=V, T u=V, T v=V, T w=V) #define MAIN_AXIS_NAMES NUM_AXIS_LIST(X, Y, Z, I, J, K, U, V, W) +#define MAIN_AXIS_NAMES_LC NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w) #define STR_AXES_MAIN NUM_AXIS_GANG("X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W) -#define LOGICAL_AXIS_GANG(E,V...) NUM_AXIS_GANG(V) GANG_ITEM_E(E) -#define LOGICAL_AXIS_CODE(E,V...) NUM_AXIS_CODE(V) CODE_ITEM_E(E) -#define LOGICAL_AXIS_LIST(E,V...) NUM_AXIS_LIST(V) LIST_ITEM_E(E) +#define LOGICAL_AXIS_GANG(N,V...) NUM_AXIS_GANG(V) GANG_ITEM_E(N) +#define LOGICAL_AXIS_CODE(N,V...) NUM_AXIS_CODE(V) CODE_ITEM_E(N) +#define LOGICAL_AXIS_LIST(N,V...) NUM_AXIS_LIST(V) LIST_ITEM_E(N) #define LOGICAL_AXIS_LIST_1(V) NUM_AXIS_LIST_1(V) LIST_ITEM_E(V) -#define LOGICAL_AXIS_ARRAY(E,V...) { LOGICAL_AXIS_LIST(E,V) } +#define LOGICAL_AXIS_ARRAY(N,V...) { LOGICAL_AXIS_LIST(N,V) } #define LOGICAL_AXIS_ARRAY_1(V) { LOGICAL_AXIS_LIST_1(V) } -#define LOGICAL_AXIS_ARGS(T) LOGICAL_AXIS_LIST(T e, T x, T y, T z, T i, T j, T k, T u, T v, T w) -#define LOGICAL_AXIS_ELEM(O) LOGICAL_AXIS_LIST(O.e, O.x, O.y, O.z, O.i, O.j, O.k, O.u, O.v, O.w) -#define LOGICAL_AXIS_DECL(T,V) LOGICAL_AXIS_LIST(T e=V, T x=V, T y=V, T z=V, T i=V, T j=V, T k=V, T u=V, T v=V, T w=V) +#define LOGICAL_AXIS_ARGS(T) LOGICAL_AXIS_LIST(T E, T X, T Y, T Z, T I, T J, T K, T U, T V, T W) +#define LOGICAL_AXIS_ARGS_LC(T) LOGICAL_AXIS_LIST(T e, T x, T y, T z, T i, T j, T k, T u, T v, T w) +#define LOGICAL_AXIS_ELEM(O) LOGICAL_AXIS_LIST(O.E, O.X, O.Y, O.Z, O.I, O.J, O.K, O.U, O.V, O.W) +#define LOGICAL_AXIS_ELEM_LC(O) LOGICAL_AXIS_LIST(O.e, O.x, O.y, O.z, O.i, O.j, O.k, O.u, O.v, O.w) +#define LOGICAL_AXIS_DECL(T,V) LOGICAL_AXIS_LIST(T E=V, T X=V, T Y=V, T Z=V, T I=V, T J=V, T K=V, T U=V, T V=V, T W=V) +#define LOGICAL_AXIS_DECL_LC(T,V) LOGICAL_AXIS_LIST(T e=V, T x=V, T y=V, T z=V, T i=V, T j=V, T k=V, T u=V, T v=V, T w=V) #define LOGICAL_AXIS_NAMES LOGICAL_AXIS_LIST(E, X, Y, Z, I, J, K, U, V, W) +#define LOGICAL_AXIS_NAMES_LC LOGICAL_AXIS_LIST(e, x, y, z, i, j, k, u, v, w) #define LOGICAL_AXIS_MAP(F) MAP(F, LOGICAL_AXIS_NAMES) +#define LOGICAL_AXIS_MAP_LC(F) MAP(F, LOGICAL_AXIS_NAMES_LC) #define STR_AXES_LOGICAL LOGICAL_AXIS_GANG("E", "X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W) +#define NUM_AXIS_PAIRED_LIST(V...) LIST_N(DOUBLE(NUM_AXES), V) +#define LOGICAL_AXIS_PAIRED_LIST(EA,EB,V...) NUM_AXIS_PAIRED_LIST(V) LIST_ITEM_E(EA) LIST_ITEM_E(EB) + #if NUM_AXES #define NUM_AXES_SEP , #define MAIN_AXIS_MAP(F) MAP(F, MAIN_AXIS_NAMES) - #define OPTARGS_NUM(T) , NUM_AXIS_ARGS(T) - #define OPTARGS_LOGICAL(T) , LOGICAL_AXIS_ARGS(T) + #define MAIN_AXIS_MAP_LC(F) MAP(F, MAIN_AXIS_NAMES_LC) + #define OPTARGS_NUM(T) , NUM_AXIS_ARGS_LC(T) + #define OPTARGS_LOGICAL(T) , LOGICAL_AXIS_ARGS_LC(T) #else #define NUM_AXES_SEP #define MAIN_AXIS_MAP(F) + #define MAIN_AXIS_MAP_LC(F) #define OPTARGS_NUM(T) #define OPTARGS_LOGICAL(T) #endif @@ -76,9 +90,10 @@ template struct IF { typedef L type; }; #define NUM_AXIS_GANG_(V...) NUM_AXIS_GANG(V) NUM_AXES_SEP #define NUM_AXIS_LIST_(V...) NUM_AXIS_LIST(V) NUM_AXES_SEP #define NUM_AXIS_LIST_1_(V...) NUM_AXIS_LIST_1(V) NUM_AXES_SEP -#define NUM_AXIS_ARGS_(T) NUM_AXIS_ARGS(T) NUM_AXES_SEP -#define NUM_AXIS_ELEM_(T) NUM_AXIS_ELEM(T) NUM_AXES_SEP +#define NUM_AXIS_ARGS_(T) NUM_AXIS_ARGS_LC(T) NUM_AXES_SEP +#define NUM_AXIS_ELEM_(T) NUM_AXIS_ELEM_LC(T) NUM_AXES_SEP #define MAIN_AXIS_NAMES_ MAIN_AXIS_NAMES NUM_AXES_SEP +#define MAIN_AXIS_NAMES_LC_ MAIN_AXIS_NAMES_LC NUM_AXES_SEP #if LOGICAL_AXES #define LOGICAL_AXES_SEP , @@ -89,14 +104,26 @@ template struct IF { typedef L type; }; #define LOGICAL_AXIS_GANG_(V...) LOGICAL_AXIS_GANG(V) LOGICAL_AXES_SEP #define LOGICAL_AXIS_LIST_(V...) LOGICAL_AXIS_LIST(V) LOGICAL_AXES_SEP #define LOGICAL_AXIS_LIST_1_(V...) LOGICAL_AXIS_LIST_1(V) LOGICAL_AXES_SEP -#define LOGICAL_AXIS_ARGS_(T) LOGICAL_AXIS_ARGS(T) LOGICAL_AXES_SEP +#define LOGICAL_AXIS_ARGS_(T) LOGICAL_AXIS_ARGS_LC(T) LOGICAL_AXES_SEP #define LOGICAL_AXIS_ELEM_(T) LOGICAL_AXIS_ELEM(T) LOGICAL_AXES_SEP +#define LOGICAL_AXIS_ELEM_LC_(T) LOGICAL_AXIS_ELEM_LC(T) LOGICAL_AXES_SEP #define LOGICAL_AXIS_NAMES_ LOGICAL_AXIS_NAMES LOGICAL_AXES_SEP +#define LOGICAL_AXIS_NAMES_LC_ LOGICAL_AXIS_NAMES_LC LOGICAL_AXES_SEP #define SECONDARY_AXIS_GANG(V...) GANG_N(SECONDARY_AXES, V) #define SECONDARY_AXIS_CODE(V...) CODE_N(SECONDARY_AXES, V) #define SECONDARY_AXIS_LIST(V...) LIST_N(SECONDARY_AXES, V) -#define SECONDARY_AXIS_ARGS(T) SECONDARY_AXIS_LIST(T i, T j, T k, T u, T v, T w) +#if SECONDARY_AXES + #define SECONDARY_AXIS_NAMES SECONDARY_AXIS_LIST(I, J, K, U, V, W) + #define SECONDARY_AXIS_NAMES_LC SECONDARY_AXIS_LIST(i, j, k, u, v, w) + #define SECONDARY_AXIS_ARGS(T) SECONDARY_AXIS_LIST(T I, T J, T K, T U, T V, T W) + #define SECONDARY_AXIS_ARGS_LC(T) SECONDARY_AXIS_LIST(T i, T j, T k, T u, T v, T w) + #define SECONDARY_AXIS_MAP(F) MAP(F, SECONDARY_AXIS_NAMES) + #define SECONDARY_AXIS_MAP_LC(F) MAP(F, SECONDARY_AXIS_NAMES_LC) +#else + #define SECONDARY_AXIS_MAP(F) + #define SECONDARY_AXIS_MAP_LC(F) +#endif // Just the XY or XYZ elements #if HAS_Z_AXIS @@ -140,11 +167,26 @@ template struct IF { typedef L type; }; #define GANG_ITEM_E(N) #endif +// Emitters for code that only cares about XYZE and not IJKUVW +#define CARTES_COUNT TERN(HAS_EXTRUDERS, INCREMENT(XYZ_COUNT), XYZ_COUNT) +#define CARTES_LIST(x,y,z,e) XYZ_LIST(x,y,z) LIST_ITEM_E(e) +#define CARTES_PAIRED_LIST(V...) LIST_N(DOUBLE(CARTES_COUNT), V) +#define CARTES_ARRAY(x,y,z,e) { CARTES_LIST(x,y,z,e) } +#define CARTES_CODE(x,y,z,e) XYZ_CODE(x,y,z) CODE_ITEM_E(e) +#define CARTES_GANG(x,y,z,e) XYZ_GANG(x,y,z) GANG_ITEM_E(e) +#define CARTES_AXIS_NAMES CARTES_LIST(X,Y,Z,E) +#define CARTES_MAP(F) MAP(F, CARTES_AXIS_NAMES) +#if CARTES_COUNT + #define CARTES_COMMA , +#else + #define CARTES_COMMA +#endif + #define AXIS_COLLISION(L) (AXIS4_NAME == L || AXIS5_NAME == L || AXIS6_NAME == L || AXIS7_NAME == L || AXIS8_NAME == L || AXIS9_NAME == L) // Helpers #define _RECIP(N) ((N) ? 1.0f / static_cast(N) : 0.0f) -#define _ABS(N) ((N) < 0 ? -(N) : (N)) +#define _ABS(N) ((N) < decltype(N)(0) ? -(N) : (N)) #define _LS(N) T(uint32_t(N) << p) #define _RS(N) T(uint32_t(N) >> p) #define _LSE(N) N = T(uint32_t(N) << p) @@ -152,36 +194,110 @@ template struct IF { typedef L type; }; #define FI FORCE_INLINE // Define types based on largest bit width stored value required -#define bits_t(W) typename IF<((W)> 16), uint32_t, typename IF<((W)> 8), uint16_t, uint8_t>::type>::type +#define bits_t(W) typename IF<((W)> 32), uint64_t, typename IF<((W)> 16), uint32_t, typename IF<((W)>8), uint16_t, uint8_t>::type>::type>::type #define uvalue_t(V) typename IF<((V)>65535), uint32_t, typename IF<((V)>255), uint16_t, uint8_t>::type>::type #define value_t(V) typename IF<((V)>32767), int32_t, typename IF<((V)>127), int16_t, int8_t>::type>::type -// General Flags for some number of states +class BitProxy; + +// Define a template for a bit field of N bits, using the smallest type that can hold N bits +template 64)> +struct Flags; + +// Flag bits for <= 64 states template -struct Flags { - typedef uvalue_t(N) flagbits_t; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1; } N8; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1; } N16; - typedef struct { bool b0:1, b1:1, b2:1, b3:1, b4:1, b5:1, b6:1, b7:1, b8:1, b9:1, b10:1, b11:1, b12:1, b13:1, b14:1, b15:1, - b16:1, b17:1, b18:1, b19:1, b20:1, b21:1, b22:1, b23:1, b24:1, b25:1, b26:1, b27:1, b28:1, b29:1, b30:1, b31:1; } N32; - union { - flagbits_t b; - typename IF<(N>16), N32, typename IF<(N>8), N16, N8>::type>::type flag; +struct Flags { + typedef bits_t(N) flagbits_t; + flagbits_t b; + + class BitProxy { + public: + BitProxy(flagbits_t& data, int bit) : data_(data), bit_(bit) {} + + BitProxy& operator=(const bool value) { + if (value) + data_ |= (flagbits_t(1) << bit_); + else + data_ &= ~(flagbits_t(1) << bit_); + return *this; + } + + operator bool() const { return bool(data_ & (flagbits_t(1) << bit_)); } + + private: + flagbits_t& data_; + uint8_t bit_; }; + FI void reset() { b = 0; } FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } - FI void set(const int n) { b |= (flagbits_t)_BV(n); } - FI void clear(const int n) { b &= ~(flagbits_t)_BV(n); } - FI bool test(const int n) const { return TEST(b, n); } - FI bool operator[](const int n) { return test(n); } + FI void set(const int n) { b |= (flagbits_t(1) << n); } + FI void clear(const int n) { b &= ~(flagbits_t(1) << n); } + FI bool test(const int n) const { return bool(b & (flagbits_t(1) << n)); } + FI BitProxy operator[](const int n) { return BitProxy(b, n); } FI bool operator[](const int n) const { return test(n); } FI int size() const { return sizeof(b); } - FI operator bool() const { return b; } + FI operator bool() const { return b != 0; } + + FI Flags& operator|=(Flags &p) const { b |= p.b; return *this; } + FI Flags& operator&=(Flags &p) const { b &= p.b; return *this; } + FI Flags& operator^=(Flags &p) const { b ^= p.b; return *this; } + + FI Flags& operator|=(const flagbits_t &p) { b |= flagbits_t(p); return *this; } + FI Flags& operator&=(const flagbits_t &p) { b &= flagbits_t(p); return *this; } + FI Flags& operator^=(const flagbits_t &p) { b ^= flagbits_t(p); return *this; } + + FI Flags operator|(Flags &p) const { return Flags(b | p.b); } + FI Flags operator&(Flags &p) const { return Flags(b & p.b); } + FI Flags operator^(Flags &p) const { return Flags(b ^ p.b); } + FI Flags operator~() const { return Flags(~b); } + + FI flagbits_t operator|(const flagbits_t &p) const { return b | flagbits_t(p); } + FI flagbits_t operator&(const flagbits_t &p) const { return b & flagbits_t(p); } + FI flagbits_t operator^(const flagbits_t &p) const { return b ^ flagbits_t(p); } + +}; + +// Flag bits for more than 64 states +template +struct Flags { + uint8_t bitmask[(N+7)>>3]; + // Proxy class for handling bit assignment + class BitProxy { + public: + BitProxy(uint8_t data[], int n) : data_(data[n >> 3]), bit_(n & 7) {} + + // Assignment operator + BitProxy& operator=(const bool value) { + if (value) + data_ |= _BV(bit_); + else + data_ &= ~_BV(bit_); + return *this; + } + + // Conversion operator to bool + operator bool() const { return TEST(data_, bit_); } + + private: + uint8_t& data_; + uint8_t bit_; + }; + + FI void reset() { for (uint8_t b = 0; b < sizeof(bitmask); ++b) bitmask[b] = 0; } + FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } + FI void set(const int n) { bitmask[n >> 3] |= _BV(n & 7); } + FI void clear(const int n) { bitmask[n >> 3] &= ~_BV(n & 7); } + FI bool test(const int n) const { return TEST(bitmask[n >> 3], n & 7); } + FI BitProxy operator[](const int n) { return BitProxy(bitmask, n); } + FI bool operator[](const int n) const { return test(n); } + FI int size() const { return sizeof(bitmask); } + FI operator bool() const { for (uint8_t b : bitmask) if (b) return true; return false; } }; // Specialization for a single bool flag template<> -struct Flags<1> { +struct Flags<1, false> { bool b; FI void reset() { b = false; } FI void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } @@ -211,7 +327,7 @@ typedef struct { FI bool operator[](const int n) { return flags[n]; } FI bool operator[](const int n) const { return flags[n]; } FI int size() const { return sizeof(flags); } - FI operator bool() const { return flags; } + FI operator bool() const { return (bool)flags; } } AxisFlags; // @@ -219,7 +335,7 @@ typedef struct { // // - X_AXIS, Y_AXIS, and Z_AXIS should be used for axes in Cartesian space // - A_AXIS, B_AXIS, and C_AXIS should be used for Steppers, corresponding to XYZ on Cartesians -// - X_HEAD, Y_HEAD, and Z_HEAD should be used for Steppers on Core kinematics +// - X_HEAD, Y_HEAD, and Z_HEAD should be used for axes on Core kinematics // enum AxisEnum : uint8_t { @@ -236,7 +352,7 @@ enum AxisEnum : uint8_t { #endif // Distinct axes, including all E and Core - NUM_AXIS_ENUMS, + NUM_AXIS_HEADS, // Most of the time we refer only to the single E_AXIS #if HAS_EXTRUDERS @@ -268,29 +384,18 @@ enum AxisEnum : uint8_t { #define LOOP_DISTINCT_E(VAR) for (uint8_t VAR = 0; VAR < DISTINCT_E; ++VAR) // -// feedRate_t is just a humble float +// feedRate_t is just a humble float that can represent mm/s or mm/min // typedef float feedRate_t; // // celsius_t is the native unit of temperature. Signed to handle a disconnected thermistor value (-14). -// For more resolition (e.g., for a chocolate printer) this may later be changed to Celsius x 100 +// For more resolution (e.g., for a chocolate printer) this may later be changed to Celsius x 100 // typedef uint16_t raw_adc_t; typedef int16_t celsius_t; typedef float celsius_float_t; -// -// On AVR pointers are only 2 bytes so use 'const float &' for 'const float' -// -#ifdef __AVR__ - typedef const float & const_float_t; -#else - typedef const float const_float_t; -#endif -typedef const_float_t const_feedRate_t; -typedef const_float_t const_celsius_float_t; - // Type large enough to count leveling grid points typedef IF 255)), uint16_t, uint8_t>::type grid_count_t; @@ -299,7 +404,7 @@ typedef IF 255)), uint16_t, uint8_t>::ty #define MMS_TO_MMM(MM_S) (static_cast(MM_S) * 60.0f) // Packaged character for C macro and other usage -typedef struct SerialChar { char c; SerialChar(char n) : c(n) { } } serial_char_t; +typedef struct SerialChar { char c; SerialChar(const char n) : c(n) { } } serial_char_t; #define C(c) serial_char_t(c) // Packaged types: float with precision and/or width; a repeated space/character @@ -405,7 +510,7 @@ typedef ab_float_t ab_pos_t; typedef abc_float_t abc_pos_t; typedef abce_float_t abce_pos_t; -// External conversion methods +// External conversion methods (motion.h) void toLogical(xy_pos_t &raw); void toLogical(xyz_pos_t &raw); void toLogical(xyze_pos_t &raw); @@ -421,7 +526,9 @@ template struct XYval { union { struct { T x, y; }; + struct { T X, Y; }; struct { T a, b; }; + struct { T A, B; }; T pos[2]; }; @@ -458,13 +565,18 @@ struct XYval { FI constexpr T large() const { return _MAX(x, y); } // Explicit copy and copies with conversion - FI constexpr XYval copy() const { return *this; } - FI constexpr XYval ABS() const { return { T(_ABS(x)), T(_ABS(y)) }; } - FI constexpr XYval asInt() const { return { int16_t(x), int16_t(y) }; } - FI constexpr XYval asLong() const { return { int32_t(x), int32_t(y) }; } - FI constexpr XYval ROUNDL() const { return { int32_t(LROUND(x)), int32_t(LROUND(y)) }; } - FI constexpr XYval asFloat() const { return { static_cast(x), static_cast(y) }; } - FI constexpr XYval reciprocal() const { return { _RECIP(x), _RECIP(y) }; } + FI constexpr XYval copy() const { return *this; } + FI constexpr XYval ABS() const { return { T(_ABS(x)), T(_ABS(y)) }; } + FI constexpr XYval ROUNDL() const { return { int32_t(LROUND(x)), int32_t(LROUND(y)) }; } + FI constexpr XYval reciprocal() const { return { _RECIP(x), _RECIP(y) }; } + + // Conversion to other types + FI constexpr XYval asInt16() const { return { int16_t(x), int16_t(y) }; } + FI constexpr XYval asInt32() const { return { int32_t(x), int32_t(y) }; } + FI constexpr XYval asUInt32() const { return { uint32_t(x), uint32_t(y) }; } + FI constexpr XYval asInt64() const { return { int64_t(x), int64_t(y) }; } + FI constexpr XYval asUInt64() const { return { uint64_t(x), uint64_t(y) }; } + FI constexpr XYval asFloat() const { return { static_cast(x), static_cast(y) }; } // Marlin workspace shifting is done with G92 and M206 FI XYval asLogical() const { XYval o = asFloat(); toLogical(o); return o; } @@ -484,11 +596,11 @@ struct XYval { FI XYval& operator= (const XYZEval &rs) { set(XY_LIST(rs.x, rs.y)); return *this; } // Override other operators to get intuitive behaviors - #define XY_OP(OP) { x TERN_(HAS_X_AXIS, OP rs.x), y TERN_(HAS_Y_AXIS, OP rs.y) } - FI constexpr XYval operator+ (const XYval &rs) const { return { x + rs.x, y + rs.y }; } - FI constexpr XYval operator- (const XYval &rs) const { return { x - rs.x, y - rs.y }; } - FI constexpr XYval operator* (const XYval &rs) const { return { x * rs.x, y * rs.y }; } - FI constexpr XYval operator/ (const XYval &rs) const { return { x / rs.x, y / rs.y }; } + #define XY_OP(OP) { T(x TERN_(HAS_X_AXIS, OP rs.x)), T(y TERN_(HAS_Y_AXIS, OP rs.y)) } + FI constexpr XYval operator+ (const XYval &rs) const { return { T(x + rs.x), T(y + rs.y) }; } + FI constexpr XYval operator- (const XYval &rs) const { return { T(x - rs.x), T(y - rs.y) }; } + FI constexpr XYval operator* (const XYval &rs) const { return { T(x * rs.x), T(y * rs.y) }; } + FI constexpr XYval operator/ (const XYval &rs) const { return { T(x / rs.x), T(y / rs.y) }; } FI constexpr XYval operator+ (const XYZval &rs) const { return { XY_OP(+) }; } FI constexpr XYval operator- (const XYZval &rs) const { return { XY_OP(-) }; } FI constexpr XYval operator* (const XYZval &rs) const { return { XY_OP(*) }; } @@ -536,6 +648,26 @@ struct XYval { FI bool operator!=(const XYval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } + + // Exact comparison to a single value + FI bool operator==(const T &p) const { return x == p && y == p; } + FI bool operator!=(const T &p) const { return !operator==(p); } + + FI bool operator< (const XYval &rs) const { return x < rs.x && y < rs.y; } + FI bool operator<=(const XYval &rs) const { return x <= rs.x && y <= rs.y; } + FI bool operator> (const XYval &rs) const { return x > rs.x && y > rs.y; } + FI bool operator>=(const XYval &rs) const { return x >= rs.x && y >= rs.y; } + + FI bool operator< (const XYZval &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); } + FI bool operator<=(const XYZval &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); } + FI bool operator> (const XYZval &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); } + FI bool operator>=(const XYZval &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); } + + FI bool operator< (const XYZEval &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); } + FI bool operator<=(const XYZEval &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); } + FI bool operator> (const XYZEval &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); } + FI bool operator>=(const XYZEval &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); } + }; // @@ -547,7 +679,9 @@ struct XYZval { union { #if NUM_AXES struct { NUM_AXIS_CODE(T x, T y, T z, T i, T j, T k, T u, T v, T w); }; + struct { NUM_AXIS_CODE(T X, T Y, T Z, T I, T J, T K, T U, T V, T W); }; struct { NUM_AXIS_CODE(T a, T b, T c, T _i, T _j, T _k, T _u, T _v, T _w); }; + struct { NUM_AXIS_CODE(T A, T B, T C, T II, T JJ, T KK, T UU, T VV, T WW); }; #endif T pos[NUM_AXES]; }; @@ -556,19 +690,19 @@ struct XYZval { FI void reset() { NUM_AXIS_CODE(x = 0, y = 0, z = 0, i = 0, j = 0, k = 0, u = 0, v = 0, w = 0); } // Setters taking struct types and arrays - FI void set(const XYval pxy) { XY_CODE(x = pxy.x, y = pxy.y); } - FI void set(const XYval pxy, const T pz) { XYZ_CODE(x = pxy.x, y = pxy.y, z = pz); } + FI void set(const XYval &pxy) { XY_CODE(x = pxy.x, y = pxy.y); } + FI void set(const XYval &pxy, const T pz) { XYZ_CODE(x = pxy.x, y = pxy.y, z = pz); } FI void set(const T (&arr)[NUM_AXES]) { NUM_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } #if LOGICAL_AXES > NUM_AXES FI void set(const T (&arr)[LOGICAL_AXES]) { NUM_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } - FI void set(LOGICAL_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } + FI void set(LOGICAL_AXIS_ARGS_LC(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } #if DISTINCT_AXES > LOGICAL_AXES FI void set(const T (&arr)[DISTINCT_AXES]) { NUM_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } #endif #endif // Setter for all individual args - FI void set(NUM_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } + FI void set(NUM_AXIS_ARGS_LC(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } // Setters with fewer elements leave the rest untouched #if HAS_Y_AXIS @@ -603,19 +737,24 @@ struct XYZval { // If any element is true then it's true FI constexpr operator bool() const { return 0 NUM_AXIS_GANG(|| x, || y, || z, || i, || j, || k, || u, || v, || w); } // Smallest element - FI constexpr T small() const { return TERN(HAS_X_AXIS, _MIN(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w)), 0); } + FI constexpr T small() const { return TERN0(HAS_X_AXIS, _MIN(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w))); } // Largest element - FI constexpr T large() const { return TERN(HAS_X_AXIS, _MAX(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w)), 0); } + FI constexpr T large() const { return TERN0(HAS_X_AXIS, _MAX(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w))); } // Explicit copy and copies with conversion FI constexpr XYZval copy() const { XYZval o = *this; return o; } FI constexpr XYZval ABS() const { return NUM_AXIS_ARRAY(T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(i)), T(_ABS(j)), T(_ABS(k)), T(_ABS(u)), T(_ABS(v)), T(_ABS(w))); } - FI constexpr XYZval asInt() const { return NUM_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k), int16_t(u), int16_t(v), int16_t(w)); } - FI constexpr XYZval asLong() const { return NUM_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k), int32_t(u), int32_t(v), int32_t(w)); } FI constexpr XYZval ROUNDL() const { return NUM_AXIS_ARRAY(int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)), int32_t(LROUND(i)), int32_t(LROUND(j)), int32_t(LROUND(k)), int32_t(LROUND(u)), int32_t(LROUND(v)), int32_t(LROUND(w))); } - FI constexpr XYZval asFloat() const { return NUM_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } FI constexpr XYZval reciprocal() const { return NUM_AXIS_ARRAY(_RECIP(x), _RECIP(y), _RECIP(z), _RECIP(i), _RECIP(j), _RECIP(k), _RECIP(u), _RECIP(v), _RECIP(w)); } + // Conversion to other types + FI constexpr XYZval asInt16() const { return NUM_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k), int16_t(u), int16_t(v), int16_t(w)); } + FI constexpr XYZval asInt32() const { return NUM_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k), int32_t(u), int32_t(v), int32_t(w)); } + FI constexpr XYZval asUInt32() const { return NUM_AXIS_ARRAY(uint32_t(x), uint32_t(y), uint32_t(z), uint32_t(i), uint32_t(j), uint32_t(k), uint32_t(u), uint32_t(v), uint32_t(w)); } + FI constexpr XYZval asInt64() const { return NUM_AXIS_ARRAY(int64_t(x), int64_t(y), int64_t(z), int64_t(i), int64_t(j), int64_t(k), int64_t(u), int64_t(v), int64_t(w)); } + FI constexpr XYZval asUInt64() const { return NUM_AXIS_ARRAY(uint64_t(x), uint64_t(y), uint64_t(z), uint64_t(i), uint64_t(j), uint64_t(k), uint64_t(u), uint64_t(v), uint64_t(w)); } + FI constexpr XYZval asFloat() const { return NUM_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } + // Marlin workspace shifting is done with G92 and M206 FI XYZval asLogical() const { XYZval o = asFloat(); toLogical(o); return o; } FI XYZval asNative() const { XYZval o = asFloat(); toNative(o); return o; } @@ -634,24 +773,24 @@ struct XYZval { // Assignment operator overrides do the expected thing FI XYZval& operator= (const T v) { set(ARRAY_N_1(NUM_AXES, v)); return *this; } FI XYZval& operator= (const XYval &rs) { set(rs.x, rs.y); return *this; } - FI XYZval& operator= (const XYZEval &rs) { set(NUM_AXIS_ELEM(rs)); return *this; } + FI XYZval& operator= (const XYZEval &rs) { set(NUM_AXIS_ELEM_LC(rs)); return *this; } // Override other operators to get intuitive behaviors - FI constexpr XYZval operator+ (const XYval &rs) const { return NUM_AXIS_ARRAY(x + rs.x, y + rs.y, z, i, j, k, u, v, w ); } - FI constexpr XYZval operator- (const XYval &rs) const { return NUM_AXIS_ARRAY(x - rs.x, y - rs.y, z, i, j, k, u, v, w ); } - FI constexpr XYZval operator* (const XYval &rs) const { return NUM_AXIS_ARRAY(x * rs.x, y * rs.y, z, i, j, k, u, v, w ); } - FI constexpr XYZval operator/ (const XYval &rs) const { return NUM_AXIS_ARRAY(x / rs.x, y / rs.y, z, i, j, k, u, v, w ); } - FI constexpr XYZval operator+ (const XYZval &rs) const { return NUM_AXIS_ARRAY(x + rs.x, y + rs.y, z + rs.z, i + rs.i, j + rs.j, k + rs.k, u + rs.u, v + rs.v, w + rs.w ); } - FI constexpr XYZval operator- (const XYZval &rs) const { return NUM_AXIS_ARRAY(x - rs.x, y - rs.y, z - rs.z, i - rs.i, j - rs.j, k - rs.k, u - rs.u, v - rs.v, w - rs.w ); } - FI constexpr XYZval operator* (const XYZval &rs) const { return NUM_AXIS_ARRAY(x * rs.x, y * rs.y, z * rs.z, i * rs.i, j * rs.j, k * rs.k, u * rs.u, v * rs.v, w * rs.w ); } - FI constexpr XYZval operator/ (const XYZval &rs) const { return NUM_AXIS_ARRAY(x / rs.x, y / rs.y, z / rs.z, i / rs.i, j / rs.j, k / rs.k, u / rs.u, v / rs.v, w / rs.w ); } - FI constexpr XYZval operator+ (const XYZEval &rs) const { return NUM_AXIS_ARRAY(x + rs.x, y + rs.y, z + rs.z, i + rs.i, j + rs.j, k + rs.k, u + rs.u, v + rs.v, w + rs.w ); } - FI constexpr XYZval operator- (const XYZEval &rs) const { return NUM_AXIS_ARRAY(x - rs.x, y - rs.y, z - rs.z, i - rs.i, j - rs.j, k - rs.k, u - rs.u, v - rs.v, w - rs.w ); } - FI constexpr XYZval operator* (const XYZEval &rs) const { return NUM_AXIS_ARRAY(x * rs.x, y * rs.y, z * rs.z, i * rs.i, j * rs.j, k * rs.k, u * rs.u, v * rs.v, w * rs.w ); } - FI constexpr XYZval operator/ (const XYZEval &rs) const { return NUM_AXIS_ARRAY(x / rs.x, y / rs.y, z / rs.z, i / rs.i, j / rs.j, k / rs.k, u / rs.u, v / rs.v, w / rs.w ); } - FI constexpr XYZval operator* (const float &p) const { return NUM_AXIS_ARRAY((T)(x * p), (T)(y * p), (T)(z * p), (T)(i * p), (T)(j * p), (T)(k * p), (T)(u * p), (T)(v * p), (T)(w * p)); } + FI constexpr XYZval operator+ (const XYval &rs) const { return NUM_AXIS_ARRAY(T(x + rs.x), T(y + rs.y), z, i, j, k, u, v, w ); } + FI constexpr XYZval operator- (const XYval &rs) const { return NUM_AXIS_ARRAY(T(x - rs.x), T(y - rs.y), z, i, j, k, u, v, w ); } + FI constexpr XYZval operator* (const XYval &rs) const { return NUM_AXIS_ARRAY(T(x * rs.x), T(y * rs.y), z, i, j, k, u, v, w ); } + FI constexpr XYZval operator/ (const XYval &rs) const { return NUM_AXIS_ARRAY(T(x / rs.x), T(y / rs.y), z, i, j, k, u, v, w ); } + FI constexpr XYZval operator+ (const XYZval &rs) const { return NUM_AXIS_ARRAY(T(x + rs.x), T(y + rs.y), T(z + rs.z), T(i + rs.i), T(j + rs.j), T(k + rs.k), T(u + rs.u), T(v + rs.v), T(w + rs.w) ); } + FI constexpr XYZval operator- (const XYZval &rs) const { return NUM_AXIS_ARRAY(T(x - rs.x), T(y - rs.y), T(z - rs.z), T(i - rs.i), T(j - rs.j), T(k - rs.k), T(u - rs.u), T(v - rs.v), T(w - rs.w) ); } + FI constexpr XYZval operator* (const XYZval &rs) const { return NUM_AXIS_ARRAY(T(x * rs.x), T(y * rs.y), T(z * rs.z), T(i * rs.i), T(j * rs.j), T(k * rs.k), T(u * rs.u), T(v * rs.v), T(w * rs.w) ); } + FI constexpr XYZval operator/ (const XYZval &rs) const { return NUM_AXIS_ARRAY(T(x / rs.x), T(y / rs.y), T(z / rs.z), T(i / rs.i), T(j / rs.j), T(k / rs.k), T(u / rs.u), T(v / rs.v), T(w / rs.w) ); } + FI constexpr XYZval operator+ (const XYZEval &rs) const { return NUM_AXIS_ARRAY(T(x + rs.x), T(y + rs.y), T(z + rs.z), T(i + rs.i), T(j + rs.j), T(k + rs.k), T(u + rs.u), T(v + rs.v), T(w + rs.w) ); } + FI constexpr XYZval operator- (const XYZEval &rs) const { return NUM_AXIS_ARRAY(T(x - rs.x), T(y - rs.y), T(z - rs.z), T(i - rs.i), T(j - rs.j), T(k - rs.k), T(u - rs.u), T(v - rs.v), T(w - rs.w) ); } + FI constexpr XYZval operator* (const XYZEval &rs) const { return NUM_AXIS_ARRAY(T(x * rs.x), T(y * rs.y), T(z * rs.z), T(i * rs.i), T(j * rs.j), T(k * rs.k), T(u * rs.u), T(v * rs.v), T(w * rs.w) ); } + FI constexpr XYZval operator/ (const XYZEval &rs) const { return NUM_AXIS_ARRAY(T(x / rs.x), T(y / rs.y), T(z / rs.z), T(i / rs.i), T(j / rs.j), T(k / rs.k), T(u / rs.u), T(v / rs.v), T(w / rs.w) ); } + FI constexpr XYZval operator* (const float &p) const { return NUM_AXIS_ARRAY(T(x * p), T(y * p), T(z * p), T(i * p), T(j * p), T(k * p), T(u * p), T(v * p), T(w * p)); } FI constexpr XYZval operator* (const int &p) const { return NUM_AXIS_ARRAY(x * p, y * p, z * p, i * p, j * p, k * p, u * p, v * p, w * p); } - FI constexpr XYZval operator/ (const float &p) const { return NUM_AXIS_ARRAY((T)(x / p), (T)(y / p), (T)(z / p), (T)(i / p), (T)(j / p), (T)(k / p), (T)(u / p), (T)(v / p), (T)(w / p)); } + FI constexpr XYZval operator/ (const float &p) const { return NUM_AXIS_ARRAY(T(x / p), T(y / p), T(z / p), T(i / p), T(j / p), T(k / p), T(u / p), T(v / p), T(w / p)); } FI constexpr XYZval operator/ (const int &p) const { return NUM_AXIS_ARRAY(x / p, y / p, z / p, i / p, j / p, k / p, u / p, v / p, w / p); } FI constexpr XYZval operator>>(const int &p) const { return NUM_AXIS_ARRAY(_RS(x), _RS(y), _RS(z), _RS(i), _RS(j), _RS(k), _RS(u), _RS(v), _RS(w)); } FI constexpr XYZval operator<<(const int &p) const { return NUM_AXIS_ARRAY(_LS(x), _LS(y), _LS(z), _LS(i), _LS(j), _LS(k), _LS(u), _LS(v), _LS(w)); } @@ -659,7 +798,7 @@ struct XYZval { // Absolute difference between two objects FI constexpr XYZval diff(const XYZEval &rs) const { return NUM_AXIS_ARRAY(T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } - FI constexpr XYZval diff(const XYZval &rs) const { return NUM_AXIS_ARRAY(T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } + FI constexpr XYZval diff(const XYZval &rs) const { return NUM_AXIS_ARRAY(T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } FI constexpr XYZval diff(const XYval &rs) const { return NUM_AXIS_ARRAY(T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), z, i, j, k, u, v, w ); } // Modifier operators @@ -681,8 +820,23 @@ struct XYZval { FI XYZval& operator<<=(const int &p) { NUM_AXIS_CODE(_LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } + FI bool operator==(const XYZEval &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } + + // Exact comparison to a single value + FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); } + FI bool operator!=(const T &p) const { return !operator==(p); } + + FI bool operator< (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } + FI bool operator<=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } + FI bool operator> (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } + FI bool operator>=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + + FI bool operator< (const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } + FI bool operator<=(const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } + FI bool operator> (const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } + FI bool operator>=(const XYZEval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + }; // @@ -693,30 +847,32 @@ struct XYZval { template struct XYZEval { union { + struct { T LOGICAL_AXIS_ARGS_LC(); }; struct { T LOGICAL_AXIS_ARGS(); }; struct { T LOGICAL_AXIS_LIST(_e, a, b, c, _i, _j, _k, _u, _v, _w); }; + struct { T LOGICAL_AXIS_LIST(EE, A, B, C, II, JJ, KK, UU, VV, WW); }; T pos[LOGICAL_AXES]; }; // Reset all to 0 FI void reset() { LOGICAL_AXIS_GANG(e =, x =, y =, z =, i =, j =, k =, u =, v =, w =) 0; } // Setters taking struct types and arrays - FI void set(const XYval pxy) { XY_CODE(x = pxy.x, y = pxy.y); } - FI void set(const XYval pxy, const T pz) { XYZ_CODE(x = pxy.x, y = pxy.y, z = pz); } - FI void set(const XYZval pxyz) { set(NUM_AXIS_ELEM(pxyz)); } - FI void set(const T (&arr)[NUM_AXES]) { NUM_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } + FI void set(const XYval &pxy) { XY_CODE(x = pxy.x, y = pxy.y); } + FI void set(const XYval &pxy, const T pz) { XYZ_CODE(x = pxy.x, y = pxy.y, z = pz); } + FI void set(const XYZval &pxyz) { set(NUM_AXIS_ELEM_LC(pxyz)); } + FI void set(const T (&arr)[NUM_AXES]) { NUM_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } #if LOGICAL_AXES > NUM_AXES - FI void set(const T (&arr)[LOGICAL_AXES]) { LOGICAL_AXIS_CODE(e = arr[LOGICAL_AXES-1], x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } - FI void set(const XYval pxy, const T pz, const T pe) { set(pxy, pz); e = pe; } - FI void set(const XYZval pxyz, const T pe) { set(pxyz); e = pe; } - FI void set(LOGICAL_AXIS_ARGS(const T)) { LOGICAL_AXIS_CODE(_e = e, a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } + FI void set(const T (&arr)[LOGICAL_AXES]) { LOGICAL_AXIS_CODE(e = arr[LOGICAL_AXES-1], x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } + FI void set(const XYval &pxy, const T pz, const T pe) { set(pxy, pz); e = pe; } + FI void set(const XYZval &pxyz, const T pe) { set(pxyz); e = pe; } + FI void set(LOGICAL_AXIS_ARGS_LC(const T)) { LOGICAL_AXIS_CODE(_e = e, a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } #if DISTINCT_AXES > LOGICAL_AXES - FI void set(const T (&arr)[DISTINCT_AXES]) { LOGICAL_AXIS_CODE(e = arr[LOGICAL_AXES-1], x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } + FI void set(const T (&arr)[DISTINCT_AXES], const uint8_t eindex) { LOGICAL_AXIS_CODE(e = arr[LOGICAL_AXES-1 + eindex], x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5], u = arr[6], v = arr[7], w = arr[8]); } #endif #endif // Setter for all individual args - FI void set(NUM_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } + FI void set(NUM_AXIS_ARGS_LC(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k, _u = u, _v = v, _w = w); } // Setters with fewer elements leave the rest untouched #if HAS_Y_AXIS @@ -756,13 +912,18 @@ struct XYZEval { FI constexpr T large() const { return _MAX(LOGICAL_AXIS_LIST(e, x, y, z, i, j, k, u, v, w)); } // Explicit copy and copies with conversion - FI constexpr XYZEval copy() const { XYZEval v = *this; return v; } - FI constexpr XYZEval ABS() const { return LOGICAL_AXIS_ARRAY(T(_ABS(e)), T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(i)), T(_ABS(j)), T(_ABS(k)), T(_ABS(u)), T(_ABS(v)), T(_ABS(w))); } - FI constexpr XYZEval asInt() const { return LOGICAL_AXIS_ARRAY(int16_t(e), int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k), int16_t(u), int16_t(v), int16_t(w)); } - FI constexpr XYZEval asLong() const { return LOGICAL_AXIS_ARRAY(int32_t(e), int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k), int32_t(u), int32_t(v), int32_t(w)); } - FI constexpr XYZEval ROUNDL() const { return LOGICAL_AXIS_ARRAY(int32_t(LROUND(e)), int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)), int32_t(LROUND(i)), int32_t(LROUND(j)), int32_t(LROUND(k)), int32_t(LROUND(u)), int32_t(LROUND(v)), int32_t(LROUND(w))); } - FI constexpr XYZEval asFloat() const { return LOGICAL_AXIS_ARRAY(static_cast(e), static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } - FI constexpr XYZEval reciprocal() const { return LOGICAL_AXIS_ARRAY(_RECIP(e), _RECIP(x), _RECIP(y), _RECIP(z), _RECIP(i), _RECIP(j), _RECIP(k), _RECIP(u), _RECIP(v), _RECIP(w)); } + FI constexpr XYZEval copy() const { XYZEval v = *this; return v; } + FI constexpr XYZEval ABS() const { return LOGICAL_AXIS_ARRAY(T(_ABS(e)), T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(i)), T(_ABS(j)), T(_ABS(k)), T(_ABS(u)), T(_ABS(v)), T(_ABS(w))); } + FI constexpr XYZEval ROUNDL() const { return LOGICAL_AXIS_ARRAY(int32_t(LROUND(e)), int32_t(LROUND(x)), int32_t(LROUND(y)), int32_t(LROUND(z)), int32_t(LROUND(i)), int32_t(LROUND(j)), int32_t(LROUND(k)), int32_t(LROUND(u)), int32_t(LROUND(v)), int32_t(LROUND(w))); } + FI constexpr XYZEval reciprocal() const { return LOGICAL_AXIS_ARRAY(_RECIP(e), _RECIP(x), _RECIP(y), _RECIP(z), _RECIP(i), _RECIP(j), _RECIP(k), _RECIP(u), _RECIP(v), _RECIP(w)); } + + // Conversion to other types + FI constexpr XYZEval asInt16() const { return LOGICAL_AXIS_ARRAY(int16_t(e), int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k), int16_t(u), int16_t(v), int16_t(w)); } + FI constexpr XYZEval asInt32() const { return LOGICAL_AXIS_ARRAY(int32_t(e), int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k), int32_t(u), int32_t(v), int32_t(w)); } + FI constexpr XYZEval asUInt32() const { return LOGICAL_AXIS_ARRAY(uint32_t(e), uint32_t(x), uint32_t(y), uint32_t(z), uint32_t(i), uint32_t(j), uint32_t(k), uint32_t(u), uint32_t(v), uint32_t(w)); } + FI constexpr XYZEval asInt64() const { return LOGICAL_AXIS_ARRAY(int64_t(e), int64_t(x), int64_t(y), int64_t(z), int64_t(i), int64_t(j), int64_t(k), int64_t(u), int64_t(v), int64_t(w)); } + FI constexpr XYZEval asUInt64() const { return LOGICAL_AXIS_ARRAY(uint64_t(e), uint64_t(x), uint64_t(y), uint64_t(z), uint64_t(i), uint64_t(j), uint64_t(k), uint64_t(u), uint64_t(v), uint64_t(w)); } + FI constexpr XYZEval asFloat() const { return LOGICAL_AXIS_ARRAY(static_cast(e), static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k), static_cast(u), static_cast(v), static_cast(w)); } // Marlin workspace shifting is done with G92 and M206 FI XYZEval asLogical() const { XYZEval o = asFloat(); toLogical(o); return o; } @@ -781,24 +942,27 @@ struct XYZEval { // Assignment operator overrides do the expected thing FI XYZEval& operator= (const T v) { set(LOGICAL_AXIS_LIST_1(v)); return *this; } FI XYZEval& operator= (const XYval &rs) { set(rs.x, rs.y); return *this; } - FI XYZEval& operator= (const XYZval &rs) { set(NUM_AXIS_ELEM(rs)); return *this; } + FI XYZEval& operator= (const XYZval &rs) { set(NUM_AXIS_ELEM_LC(rs)); return *this; } // Override other operators to get intuitive behaviors - FI constexpr XYZEval operator+ (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, x + rs.x, y + rs.y, z, i, j, k, u, v, w); } - FI constexpr XYZEval operator- (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, x - rs.x, y - rs.y, z, i, j, k, u, v, w); } - FI constexpr XYZEval operator* (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, x * rs.x, y * rs.y, z, i, j, k, u, v, w); } - FI constexpr XYZEval operator/ (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, x / rs.x, y / rs.y, z, i, j, k, u, v, w); } - FI constexpr XYZEval operator+ (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, x + rs.x, y + rs.y, z + rs.z, i + rs.i, j + rs.j, k + rs.k, u + rs.u, v + rs.v, w + rs.w); } - FI constexpr XYZEval operator- (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, x - rs.x, y - rs.y, z - rs.z, i - rs.i, j - rs.j, k - rs.k, u - rs.u, v - rs.v, w - rs.w); } - FI constexpr XYZEval operator* (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, x * rs.x, y * rs.y, z * rs.z, i * rs.i, j * rs.j, k * rs.k, u * rs.u, v * rs.v, w * rs.w); } - FI constexpr XYZEval operator/ (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, x / rs.x, y / rs.y, z / rs.z, i / rs.i, j / rs.j, k / rs.k, u / rs.u, v / rs.v, w / rs.w); } - FI constexpr XYZEval operator+ (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(e + rs.e, x + rs.x, y + rs.y, z + rs.z, i + rs.i, j + rs.j, k + rs.k, u + rs.u, v + rs.v, w + rs.w); } - FI constexpr XYZEval operator- (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(e - rs.e, x - rs.x, y - rs.y, z - rs.z, i - rs.i, j - rs.j, k - rs.k, u - rs.u, v - rs.v, w - rs.w); } - FI constexpr XYZEval operator* (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(e * rs.e, x * rs.x, y * rs.y, z * rs.z, i * rs.i, j * rs.j, k * rs.k, u * rs.u, v * rs.v, w * rs.w); } - FI constexpr XYZEval operator/ (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(e / rs.e, x / rs.x, y / rs.y, z / rs.z, i / rs.i, j / rs.j, k / rs.k, u / rs.u, v / rs.v, w / rs.w); } - FI constexpr XYZEval operator* (const float &p) const { return LOGICAL_AXIS_ARRAY((T)(e * p), (T)(x * p), (T)(y * p), (T)(z * p), (T)(i * p), (T)(j * p), (T)(k * p), (T)(u * p), (T)(v * p), (T)(w * p)); } + FI constexpr XYZEval operator+ (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x + rs.x), T(y + rs.y), z, i, j, k, u, v, w); } + FI constexpr XYZEval operator- (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x - rs.x), T(y - rs.y), z, i, j, k, u, v, w); } + FI constexpr XYZEval operator* (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x * rs.x), T(y * rs.y), z, i, j, k, u, v, w); } + FI constexpr XYZEval operator/ (const XYval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x / rs.x), T(y / rs.y), z, i, j, k, u, v, w); } + FI constexpr XYZEval operator+ (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x + rs.x), T(y + rs.y), T(z + rs.z), T(i + rs.i), T(j + rs.j), T(k + rs.k), T(u + rs.u), T(v + rs.v), T(w + rs.w)); } + FI constexpr XYZEval operator- (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x - rs.x), T(y - rs.y), T(z - rs.z), T(i - rs.i), T(j - rs.j), T(k - rs.k), T(u - rs.u), T(v - rs.v), T(w - rs.w)); } + FI constexpr XYZEval operator* (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x * rs.x), T(y * rs.y), T(z * rs.z), T(i * rs.i), T(j * rs.j), T(k * rs.k), T(u * rs.u), T(v * rs.v), T(w * rs.w)); } + FI constexpr XYZEval operator/ (const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(e, T(x / rs.x), T(y / rs.y), T(z / rs.z), T(i / rs.i), T(j / rs.j), T(k / rs.k), T(u / rs.u), T(v / rs.v), T(w / rs.w)); } + FI constexpr XYZEval operator+ (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(T(e + rs.e), T(x + rs.x), T(y + rs.y), T(z + rs.z), T(i + rs.i), T(j + rs.j), T(k + rs.k), T(u + rs.u), T(v + rs.v), T(w + rs.w)); } + FI constexpr XYZEval operator- (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(T(e - rs.e), T(x - rs.x), T(y - rs.y), T(z - rs.z), T(i - rs.i), T(j - rs.j), T(k - rs.k), T(u - rs.u), T(v - rs.v), T(w - rs.w)); } + FI constexpr XYZEval operator* (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(T(e * rs.e), T(x * rs.x), T(y * rs.y), T(z * rs.z), T(i * rs.i), T(j * rs.j), T(k * rs.k), T(u * rs.u), T(v * rs.v), T(w * rs.w)); } + FI constexpr XYZEval operator/ (const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(T(e / rs.e), T(x / rs.x), T(y / rs.y), T(z / rs.z), T(i / rs.i), T(j / rs.j), T(k / rs.k), T(u / rs.u), T(v / rs.v), T(w / rs.w)); } + FI constexpr XYZEval operator+ (const uint32_t &p) const { return LOGICAL_AXIS_ARRAY(T(e + p), T(x + p), T(y + p), T(z + p), T(i + p), T(j + p), T(k + p), T(u + p), T(v + p), T(w + p)); } + FI constexpr XYZEval operator* (const float &p) const { return LOGICAL_AXIS_ARRAY(T(e * p), T(x * p), T(y * p), T(z * p), T(i * p), T(j * p), T(k * p), T(u * p), T(v * p), T(w * p)); } + FI constexpr XYZEval operator* (const uint32_t &p) const { return LOGICAL_AXIS_ARRAY(T(e * p), T(x * p), T(y * p), T(z * p), T(i * p), T(j * p), T(k * p), T(u * p), T(v * p), T(w * p)); } + FI constexpr XYZEval operator& (const int64_t &p) const { return LOGICAL_AXIS_ARRAY(T(e & p), T(x & p), T(y & p), T(z & p), T(i & p), T(j & p), T(k & p), T(u & p), T(v & p), T(w & p)); } FI constexpr XYZEval operator* (const int &p) const { return LOGICAL_AXIS_ARRAY(e * p, x * p, y * p, z * p, i * p, j * p, k * p, u * p, v * p, w * p); } - FI constexpr XYZEval operator/ (const float &p) const { return LOGICAL_AXIS_ARRAY((T)(e / p), (T)(x / p), (T)(y / p), (T)(z / p), (T)(i / p), (T)(j / p), (T)(k / p), (T)(u / p), (T)(v / p), (T)(w / p)); } + FI constexpr XYZEval operator/ (const float &p) const { return LOGICAL_AXIS_ARRAY(T(e / p), T(x / p), T(y / p), T(z / p), T(i / p), T(j / p), T(k / p), T(u / p), T(v / p), T(w / p)); } FI constexpr XYZEval operator/ (const int &p) const { return LOGICAL_AXIS_ARRAY(e / p, x / p, y / p, z / p, i / p, j / p, k / p, u / p, v / p, w / p); } FI constexpr XYZEval operator>>(const int &p) const { return LOGICAL_AXIS_ARRAY(_RS(e), _RS(x), _RS(y), _RS(z), _RS(i), _RS(j), _RS(k), _RS(u), _RS(v), _RS(w)); } FI constexpr XYZEval operator<<(const int &p) const { return LOGICAL_AXIS_ARRAY(_LS(e), _LS(x), _LS(y), _LS(z), _LS(i), _LS(j), _LS(k), _LS(u), _LS(v), _LS(w)); } @@ -806,8 +970,8 @@ struct XYZEval { // Absolute difference between two objects FI constexpr XYZEval diff(const XYZEval &rs) const { return LOGICAL_AXIS_ARRAY(T(_ABS(e - rs.e)), T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } - FI constexpr XYZEval diff(const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(0 , T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } - FI constexpr XYZEval diff(const XYval &rs) const { return LOGICAL_AXIS_ARRAY(0 , T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), z, i, j, k, u, v, w ); } + FI constexpr XYZEval diff(const XYZval &rs) const { return LOGICAL_AXIS_ARRAY(0, T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), T(_ABS(z - rs.z)), T(_ABS(i - rs.i)), T(_ABS(j - rs.j)), T(_ABS(k - rs.k)), T(_ABS(u - rs.u)), T(_ABS(v - rs.v)), T(_ABS(w - rs.w)) ); } + FI constexpr XYZEval diff(const XYval &rs) const { return LOGICAL_AXIS_ARRAY(0, T(_ABS(x - rs.x)), T(_ABS(y - rs.y)), z, i, j, k, u, v, w ); } // Modifier operators FI XYZEval& operator+=(const XYval &rs) { XY_CODE(x += rs.x, y += rs.y); return *this; } @@ -827,27 +991,47 @@ struct XYZEval { FI XYZEval& operator<<=(const int &p) { LOGICAL_AXIS_CODE(_LSE(e), _LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } - FI bool operator==(const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e == rs.e, && x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } + FI bool operator==(const XYZval &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } + FI bool operator==(const XYZEval &rs) const { return ANY(HAS_X_AXIS, HAS_EXTRUDERS) LOGICAL_AXIS_GANG(&& e == rs.e, && x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); } FI bool operator!=(const XYZval &rs) const { return !operator==(rs); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } + + // Exact comparison to a single value + FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) LOGICAL_AXIS_GANG(&& e == p, && x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); } + FI bool operator!=(const T &p) const { return !operator==(p); } + + FI bool operator< (const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e < rs.e, && x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } + FI bool operator<=(const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e <= rs.e, && x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } + FI bool operator> (const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e > rs.e, && x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } + FI bool operator>=(const XYZEval &rs) const { return true LOGICAL_AXIS_GANG(&& e >= rs.e, && x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + + FI bool operator< (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); } + FI bool operator<=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); } + FI bool operator> (const XYZval &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); } + FI bool operator>=(const XYZval &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); } + }; #include // for memset +// +// Axis indexed arrays of type T (x[SIZE], y[SIZE], etc.) +// template struct XYZarray { typedef T el[SIZE]; union { el data[LOGICAL_AXES]; struct { NUM_AXIS_CODE(T x, T y, T z, T i, T j, T k, T u, T v, T w); }; + struct { NUM_AXIS_CODE(T X, T Y, T Z, T I, T J, T K, T U, T V, T W); }; struct { NUM_AXIS_CODE(T a, T b, T c, T _i, T _j, T _k, T _u, T _v, T _w); }; + struct { NUM_AXIS_CODE(T A, T B, T C, T II, T JJ, T KK, T UU, T VV, T WW); }; }; FI void reset() { ZERO(data); } - FI void set(const int n, const XYval p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y,,,,,,,); } - FI void set(const int n, const XYZval p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } - FI void set(const int n, const XYZEval p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } + FI void set(const int n, const XYval &p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y,,,,,,,); } + FI void set(const int n, const XYZval &p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } + FI void set(const int n, const XYZEval &p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } // Setter for all individual args FI void set(const int n OPTARGS_NUM(const T)) { NUM_AXIS_CODE(a[n] = x, b[n] = y, c[n] = z, _i[n] = i, _j[n] = j, _k[n] = k, _u[n] = u, _v[n] = v, _w[n] = w); } @@ -887,18 +1071,20 @@ struct XYZEarray { union { el data[LOGICAL_AXES]; struct { el LOGICAL_AXIS_ARGS(); }; + struct { el LOGICAL_AXIS_ARGS_LC(); }; + struct { el LOGICAL_AXIS_LIST(EE, A, B, C, II, JJ, KK, UU, VV, WW); }; struct { el LOGICAL_AXIS_LIST(_e, a, b, c, _i, _j, _k, _u, _v, _w); }; }; FI void reset() { ZERO(data); } - FI void set(const int n, const XYval p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y,,,,,,,); } - FI void set(const int n, const XYZval p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } - FI void set(const int n, const XYZEval p) { LOGICAL_AXIS_CODE(e[n]=p.e, x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } + FI void set(const int n, const XYval &p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y,,,,,,,); } + FI void set(const int n, const XYZval &p) { NUM_AXIS_CODE(x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } + FI void set(const int n, const XYZEval &p) { LOGICAL_AXIS_CODE(e[n]=p.e, x[n]=p.x, y[n]=p.y, z[n]=p.z, i[n]=p.i, j[n]=p.j, k[n]=p.k, u[n]=p.u, v[n]=p.v, w[n]=p.w ); } // Setter for all individual args FI void set(const int n OPTARGS_NUM(const T)) { NUM_AXIS_CODE(a[n] = x, b[n] = y, c[n] = z, _i[n] = i, _j[n] = j, _k[n] = k, _u[n] = u, _v[n] = v, _w[n] = w); } #if LOGICAL_AXES > NUM_AXES - FI void set(const int n, LOGICAL_AXIS_ARGS(const T)) { LOGICAL_AXIS_CODE(_e[n] = e, a[n] = x, b[n] = y, c[n] = z, _i[n] = i, _j[n] = j, _k[n] = k, _u[n] = u, _v[n] = v, _w[n] = w); } + FI void set(const int n, LOGICAL_AXIS_ARGS_LC(const T)) { LOGICAL_AXIS_CODE(_e[n] = e, a[n] = x, b[n] = y, c[n] = z, _i[n] = i, _j[n] = j, _k[n] = k, _u[n] = u, _v[n] = v, _w[n] = w); } #endif // Setters with fewer elements leave the rest untouched @@ -930,11 +1116,12 @@ struct XYZEarray { FI XYZEval operator[](const int n) const { return XYZval(LOGICAL_AXIS_ARRAY(e[n], x[n], y[n], z[n], i[n], j[n], k[n], u[n], v[n], w[n])); } }; -class AxisBits; - +// +// Axes mapped to bits in a mask of minimum size, bits_t(NUM_AXIS_HEADS) +// class AxisBits { public: - typedef bits_t(NUM_AXIS_ENUMS) el; + typedef bits_t(NUM_AXIS_HEADS) el; union { el bits; // Axes x, y, z ... e0, e1, e2 ... hx, hy, hz @@ -987,6 +1174,25 @@ public: }; }; + class BitProxy { + public: + BitProxy(el& data, int bit) : data_(data), bit_(bit) {} + + BitProxy& operator=(const bool value) { + if (value) + data_ |= (el(1) << bit_); + else + data_ &= ~(el(1) << bit_); + return *this; + } + + operator bool() const { return bool(data_ & (el(1) << bit_)); } + + private: + el& data_; + uint8_t bit_; + }; + AxisBits() { reset(); } // Constructor, setter, and operator= for bit mask @@ -995,7 +1201,7 @@ public: FI AxisBits& operator=(const el p) { set(p); return *this; } FI void reset() { set(0); } - FI void fill() { set(_BV(NUM_AXIS_ENUMS) - 1); } + FI void fill() { set(_BV(NUM_AXIS_HEADS) - 1); } #define MSET(pE,pX,pY,pZ,pI,pJ,pK,pU,pV,pW) LOGICAL_AXIS_CODE(e=pE, x=pX, y=pY, z=pZ, i=pI, j=pJ, k=pK, u=pU, v=pV, w=pW) @@ -1084,9 +1290,12 @@ public: FI bool toggle(const AxisEnum n) { TBI(bits, n); return TEST(bits, n); } FI void bset(const AxisEnum n) { SBI(bits, n); } FI void bclr(const AxisEnum n) { CBI(bits, n); } + FI void bset(const AxisEnum n, const bool b) { if (b) bset(n); else bclr(n); } // Accessor via an AxisEnum (or any integer) [index] - FI bool operator[](const int n) const { return TEST(bits, n); } + FI BitProxy operator[](const int n) { return BitProxy(bits, n); } + FI BitProxy operator[](const AxisEnum n) { return BitProxy(bits, n); } + FI bool operator[](const int n) const { return TEST(bits, n); } FI bool operator[](const AxisEnum n) const { return TEST(bits, n); } FI AxisBits& operator|=(const el &p) { bits |= el(p); return *this; } @@ -1107,6 +1316,7 @@ public: FI AxisBits operator|(const AxisBits &p) const { return AxisBits(bits | p.bits); } FI AxisBits operator&(const AxisBits &p) const { return AxisBits(bits & p.bits); } FI AxisBits operator^(const AxisBits &p) const { return AxisBits(bits ^ p.bits); } + FI AxisBits operator~() const { return AxisBits(~bits); } FI operator bool() const { return !!bits; } FI operator uint16_t() const { return uint16_t(bits & 0xFFFF); } @@ -1121,3 +1331,14 @@ public: #undef _LSE #undef _RSE #undef FI + +// Axis names for G-code parsing, reports, etc. +constexpr xyze_char_t axis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z', AXIS4_NAME, AXIS5_NAME, AXIS6_NAME, AXIS7_NAME, AXIS8_NAME, AXIS9_NAME); +#if NUM_AXES <= XYZ && !HAS_EXTRUDERS + #define AXIS_CHAR(A) ((char)('X' + A)) + #define IAXIS_CHAR AXIS_CHAR +#else + constexpr xyze_char_t iaxis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z', 'I', 'J', 'K', 'U', 'V', 'W'); + #define AXIS_CHAR(A) axis_codes[A] + #define IAXIS_CHAR(A) iaxis_codes[A] +#endif diff --git a/Marlin/src/core/utility.cpp b/Marlin/src/core/utility.cpp index b555d9f2a6..c8b903ecc2 100644 --- a/Marlin/src/core/utility.cpp +++ b/Marlin/src/core/utility.cpp @@ -60,29 +60,38 @@ void safe_delay(millis_t ms) { #include "../feature/bedlevel/bedlevel.h" void log_machine_info() { - SERIAL_ECHOLNPGM("Machine Type: " - TERN_(DELTA, "Delta") - TERN_(IS_SCARA, "SCARA") - TERN_(IS_CORE, "Core") - TERN_(MARKFORGED_XY, "MarkForgedXY") - TERN_(MARKFORGED_YX, "MarkForgedYX") - TERN_(IS_CARTESIAN, "Cartesian") + SERIAL_ECHOLNPGM("Machine Type:" + TERN_(DELTA, " Delta") + TERN_(IS_SCARA, " SCARA") + TERN_(AXEL_TPARA, " TPARA") + TERN_(IS_CORE, " Core") + TERN_(BELTPRINTER, " Belt Printer") + TERN_(MARKFORGED_XY, " MarkForgedXY") + TERN_(MARKFORGED_YX, " MarkForgedYX") + TERN_(POLAR, " Polar") + TERN_(POLARGRAPH, " Polargraph") + TERN_(ARTICULATED_ROBOT_ARM, " Robot Arm") + TERN_(FOAMCUTTER_XYUV, " Foam Cutter") + TERN_(IS_CARTESIAN, " Cartesian") ); SERIAL_ECHOLNPGM("Probe: " - TERN_(PROBE_MANUALLY, "PROBE_MANUALLY") - TERN_(NOZZLE_AS_PROBE, "NOZZLE_AS_PROBE") - TERN_(FIX_MOUNTED_PROBE, "FIX_MOUNTED_PROBE") - TERN_(HAS_Z_SERVO_PROBE, TERN(BLTOUCH, "BLTOUCH", "SERVO PROBE")) - TERN_(BD_SENSOR, "BD_SENSOR") - TERN_(TOUCH_MI_PROBE, "TOUCH_MI_PROBE") - TERN_(Z_PROBE_SLED, "Z_PROBE_SLED") - TERN_(Z_PROBE_ALLEN_KEY, "Z_PROBE_ALLEN_KEY") - TERN_(SOLENOID_PROBE, "SOLENOID_PROBE") - TERN_(MAGLEV4, "MAGLEV4") - TERN_(BIQU_MICROPROBE_V1, "BIQU_MICROPROBE_V1") - TERN_(BIQU_MICROPROBE_V2, "BIQU_MICROPROBE_V2") - IF_DISABLED(PROBE_SELECTED, "NONE") + TERN_(PROBE_MANUALLY, "PROBE_MANUALLY") + TERN_(NOZZLE_AS_PROBE, "NOZZLE_AS_PROBE") + TERN_(FIX_MOUNTED_PROBE, "FIX_MOUNTED_PROBE") + TERN_(HAS_Z_SERVO_PROBE, TERN(BLTOUCH, "BLTOUCH", "SERVO PROBE")) + TERN_(BD_SENSOR, "BD_SENSOR") + TERN_(TOUCH_MI_PROBE, "TOUCH_MI_PROBE") + TERN_(Z_PROBE_ALLEN_KEY, "Z_PROBE_ALLEN_KEY") + TERN_(Z_PROBE_SLED, "Z_PROBE_SLED") + TERN_(RACK_AND_PINION_PROBE, "RACK_AND_PINION_PROBE") + TERN_(SOLENOID_PROBE, "SOLENOID_PROBE") + TERN_(SENSORLESS_PROBING, "SENSORLESS_PROBING") + TERN_(MAGLEV4, "MAGLEV4") + TERN_(MAG_MOUNTED_PROBE, "MAG_MOUNTED_PROBE") + TERN_(BIQU_MICROPROBE_V1, "BIQU_MICROPROBE_V1") + TERN_(BIQU_MICROPROBE_V2, "BIQU_MICROPROBE_V2") + IF_DISABLED(PROBE_SELECTED, "NONE") ); #if HAS_BED_PROBE @@ -134,7 +143,7 @@ void safe_delay(millis_t ms) { SERIAL_ECHOPGM("ABL Adjustment"); LOOP_NUM_AXES(a) { SERIAL_ECHOPGM_P((PGM_P)pgm_read_ptr(&SP_AXIS_STR[a])); - serial_offset(planner.get_axis_position_mm(AxisEnum(a)) - current_position[a]); + serial_offset(planner.get_axis_position_mm((AxisEnum)a) - current_position[a]); } #else #if ENABLED(AUTO_BED_LEVELING_UBL) @@ -145,10 +154,8 @@ void safe_delay(millis_t ms) { const float rz = bedlevel.get_z_correction(current_position); SERIAL_ECHO(ftostr43sign(rz, '+')); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - if (planner.z_fade_height) { - SERIAL_ECHOPGM(" (", ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position.z), '+')); - SERIAL_CHAR(')'); - } + if (planner.z_fade_height) + SERIAL_ECHO(F(" ("), ftostr43sign(rz * planner.fade_scaling_factor_for_z(current_position.z), '+'), C(')')); #endif #endif } @@ -167,10 +174,7 @@ void safe_delay(millis_t ms) { SERIAL_ECHOPGM("MBL Adjustment Z", ftostr43sign(z_offset + z_correction, '+')); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) if (planner.z_fade_height) { - SERIAL_ECHOPGM(" (", ftostr43sign( - z_offset + z_correction * planner.fade_scaling_factor_for_z(current_position.z), '+' - )); - SERIAL_CHAR(')'); + SERIAL_ECHO(F(" ("), ftostr43sign(z_offset + z_correction * planner.fade_scaling_factor_for_z(current_position.z), '+'), C(')')); } #endif } diff --git a/Marlin/src/core/utility.h b/Marlin/src/core/utility.h index c3324443ba..77e8bac016 100644 --- a/Marlin/src/core/utility.h +++ b/Marlin/src/core/utility.h @@ -82,17 +82,6 @@ public: // in the range 0-100 while avoiding rounding artifacts constexpr uint8_t ui8_to_percent(const uint8_t i) { return (int(i) * 100 + 127) / 255; } -// Axis names for G-code parsing, reports, etc. -const xyze_char_t axis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z', AXIS4_NAME, AXIS5_NAME, AXIS6_NAME, AXIS7_NAME, AXIS8_NAME, AXIS9_NAME); -#if NUM_AXES <= XYZ && !HAS_EXTRUDERS - #define AXIS_CHAR(A) ((char)('X' + A)) - #define IAXIS_CHAR AXIS_CHAR -#else - const xyze_char_t iaxis_codes LOGICAL_AXIS_ARRAY('E', 'X', 'Y', 'Z', 'I', 'J', 'K', 'U', 'V', 'W'); - #define AXIS_CHAR(A) axis_codes[A] - #define IAXIS_CHAR(A) iaxis_codes[A] -#endif - #if ENABLED(MARLIN_DEV_MODE) enum MarlinError : uint8_t { ERR_NONE, diff --git a/Marlin/src/feature/babystep.cpp b/Marlin/src/feature/babystep.cpp index c6717268a6..6a4929e60f 100644 --- a/Marlin/src/feature/babystep.cpp +++ b/Marlin/src/feature/babystep.cpp @@ -26,7 +26,7 @@ #include "babystep.h" #include "../MarlinCore.h" -#include "../module/motion.h" // for axes_should_home(), BABYSTEP_ALLOWED +#include "../module/motion.h" // for axis_should_home(), BABYSTEP_ALLOWED #include "../module/planner.h" // for axis_steps_per_mm[] #include "../module/stepper.h" @@ -49,18 +49,18 @@ int16_t Babystep::accum; void Babystep::step_axis(const AxisEnum axis) { const int16_t curTodo = steps[BS_AXIS_IND(axis)]; // get rid of volatile for performance if (curTodo) { - stepper.do_babystep((AxisEnum)axis, curTodo > 0); + stepper.do_babystep(axis, curTodo > 0); if (curTodo > 0) steps[BS_AXIS_IND(axis)]--; else steps[BS_AXIS_IND(axis)]++; } } -void Babystep::add_mm(const AxisEnum axis, const_float_t mm) { +void Babystep::add_mm(const AxisEnum axis, const float mm) { add_steps(axis, mm * planner.settings.axis_steps_per_mm[axis]); } #if ENABLED(BD_SENSOR) - void Babystep::set_mm(const AxisEnum axis, const_float_t mm) { - //if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axes_should_home(_BV(axis))) return; + void Babystep::set_mm(const AxisEnum axis, const float mm) { + //if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axis_should_home(axis)) return; const int16_t distance = mm * planner.settings.axis_steps_per_mm[axis]; accum = distance; // Count up babysteps for the UI steps[BS_AXIS_IND(axis)] = distance; @@ -70,8 +70,12 @@ void Babystep::add_mm(const AxisEnum axis, const_float_t mm) { } #endif +bool Babystep::can_babystep(const AxisEnum axis) { + return (ENABLED(BABYSTEP_WITHOUT_HOMING) || !axis_should_home(axis)); +} + void Babystep::add_steps(const AxisEnum axis, const int16_t distance) { - if (DISABLED(BABYSTEP_WITHOUT_HOMING) && axes_should_home(_BV(axis))) return; + if (!can_babystep(axis)) return; accum += distance; // Count up babysteps for the UI steps[BS_AXIS_IND(axis)] += distance; diff --git a/Marlin/src/feature/babystep.h b/Marlin/src/feature/babystep.h index 38a19a3b14..da330672b2 100644 --- a/Marlin/src/feature/babystep.h +++ b/Marlin/src/feature/babystep.h @@ -59,8 +59,9 @@ public: } #endif + static bool can_babystep(const AxisEnum axis); static void add_steps(const AxisEnum axis, const int16_t distance); - static void add_mm(const AxisEnum axis, const_float_t mm); + static void add_mm(const AxisEnum axis, const float mm); #if ENABLED(EP_BABYSTEPPING) // Step Z for M293 / M294 @@ -78,7 +79,7 @@ public: #endif // EP_BABYSTEPPING #if ENABLED(BD_SENSOR) - static void set_mm(const AxisEnum axis, const_float_t mm); + static void set_mm(const AxisEnum axis, const float mm); #endif static bool has_steps() { diff --git a/Marlin/src/feature/backlash.cpp b/Marlin/src/feature/backlash.cpp index 07fa7725a0..3b9d78cb2e 100644 --- a/Marlin/src/feature/backlash.cpp +++ b/Marlin/src/feature/backlash.cpp @@ -171,13 +171,14 @@ int32_t Backlash::get_applied_steps(const AxisEnum axis) { const int32_t residual_error_axis = residual_error[axis]; - // At startup it is assumed the last move was forward. - // So the applied steps will always be negative. + // At startup, when no steps are applied, it is assumed the last move was backwards. + // So the applied steps will always be zero (when moving backwards) or a positive + // number (when moving forwards). - if (forward) return -residual_error_axis; + if (!forward) return -residual_error_axis; const float f_corr = float(correction) / all_on; - const int32_t full_error_axis = -f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; + const int32_t full_error_axis = f_corr * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; return full_error_axis - residual_error_axis; } diff --git a/Marlin/src/feature/backlash.h b/Marlin/src/feature/backlash.h index 593e51b9d0..b4790cb161 100644 --- a/Marlin/src/feature/backlash.h +++ b/Marlin/src/feature/backlash.h @@ -81,10 +81,10 @@ public: static void set_correction(const float v) { set_correction_uint8(_MAX(0, _MIN(1.0, v)) * all_on + 0.5f); } static float get_correction() { return float(get_correction_uint8()) / all_on; } static void set_distance_mm(const AxisEnum axis, const float v); - static float get_distance_mm(const AxisEnum axis) {return distance_mm[axis];} + static float get_distance_mm(const AxisEnum axis) { return distance_mm[axis]; } #ifdef BACKLASH_SMOOTHING_MM static void set_smoothing_mm(const float v); - static float get_smoothing_mm() {return smoothing_mm;} + static float get_smoothing_mm() { return smoothing_mm; } #endif #endif diff --git a/Marlin/src/feature/bedlevel/abl/bbl.cpp b/Marlin/src/feature/bedlevel/abl/bbl.cpp index 14c4bd24bc..918b06d2b4 100644 --- a/Marlin/src/feature/bedlevel/abl/bbl.cpp +++ b/Marlin/src/feature/bedlevel/abl/bbl.cpp @@ -229,7 +229,7 @@ void LevelingBilinear::print_leveling_grid(const bed_mesh_t* _z_values/*=nullptr ) * 0.5f; } - float LevelingBilinear::virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) { + float LevelingBilinear::virt_2cmr(const uint8_t x, const uint8_t y, const float tx, const float ty) { float row[4], column[4]; for (uint8_t i = 0; i < 4; ++i) { for (uint8_t j = 0; j < 4; ++j) { @@ -369,7 +369,7 @@ float LevelingBilinear::get_z_correction(const xy_pos_t &raw) { * Prepare a bilinear-leveled linear move on Cartesian, * splitting the move where it crosses grid borders. */ - void LevelingBilinear::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) { + void LevelingBilinear::line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits, uint16_t y_splits) { // Get current and destination cells for this line xy_int_t c1 { CELL_INDEX(x, current_position.x), CELL_INDEX(y, current_position.y) }, c2 { CELL_INDEX(x, destination.x), CELL_INDEX(y, destination.y) }; diff --git a/Marlin/src/feature/bedlevel/abl/bbl.h b/Marlin/src/feature/bedlevel/abl/bbl.h index ca2e96593f..fb890333dc 100644 --- a/Marlin/src/feature/bedlevel/abl/bbl.h +++ b/Marlin/src/feature/bedlevel/abl/bbl.h @@ -45,7 +45,7 @@ private: static float virt_coord(const uint8_t x, const uint8_t y); static float virt_cmr(const float p[4], const uint8_t i, const float t); - static float virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty); + static float virt_2cmr(const uint8_t x, const uint8_t y, const float tx, const float ty); static void subdivide_mesh(); #endif @@ -63,7 +63,7 @@ public: static constexpr float get_z_offset() { return 0.0f; } #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) - static void line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF); + static void line_to_destination(const feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF); #endif }; diff --git a/Marlin/src/feature/bedlevel/bdl/bdl.cpp b/Marlin/src/feature/bedlevel/bdl/bdl.cpp index d469bb0c06..ea4bcc0607 100644 --- a/Marlin/src/feature/bedlevel/bdl/bdl.cpp +++ b/Marlin/src/feature/bedlevel/bdl/bdl.cpp @@ -46,10 +46,16 @@ BDS_Leveling bdl; #define DEBUG_OUT ENABLED(DEBUG_OUT_BD) #include "../../../core/debug_out.h" -// M102 S-5 Read raw Calibrate data -// M102 S-6 Start Calibrate -// M102 S4 Set the adjustable Z height value (e.g., 'M102 S4' means it will do adjusting while the Z height <= 0.4mm , disable with 'M102 S0'.) -// M102 S-1 Read sensor information +/** + * M102 S<#> : Set adjustable Z height in 0.1mm units (10ths of a mm) + * (e.g., 'M102 S4' enables adjusting for Z <= 0.4mm) + * M102 S0 : Disable adjustable Z height + * + * M102 S-1 : Read BDsensor version + * M102 S-2 : Read BDsensor distance value + * M102 S-5 : Read raw Calibration data + * M102 S-6 : Start Calibration + */ #define MAX_BD_HEIGHT 4.0f #define CMD_READ_VERSION 1016 @@ -95,7 +101,7 @@ bool BDS_Leveling::check(const uint16_t data, const bool raw_data/*=false*/, con } float BDS_Leveling::interpret(const uint16_t data) { - return (data & 0x3FF) / 100.0f; + return (data & 0x3FF) * 0.01f; } float BDS_Leveling::read() { diff --git a/Marlin/src/feature/bedlevel/bedlevel.cpp b/Marlin/src/feature/bedlevel/bedlevel.cpp index a76c6cdd26..e479e4c70a 100644 --- a/Marlin/src/feature/bedlevel/bedlevel.cpp +++ b/Marlin/src/feature/bedlevel/bedlevel.cpp @@ -77,7 +77,7 @@ void set_bed_leveling_enabled(const bool enable/*=true*/) { // Get the corrected leveled / unleveled position planner.apply_modifiers(current_position, true); // Physical position with all modifiers - planner.leveling_active ^= true; // Toggle leveling between apply and unapply + FLIP(planner.leveling_active); // Toggle leveling between apply and unapply planner.unapply_modifiers(current_position, true); // Logical position with modifiers removed sync_plan_position(); @@ -91,7 +91,7 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved( #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - void set_z_fade_height(const_float_t zfh, const bool do_report/*=true*/) { + void set_z_fade_height(const float zfh, const bool do_report/*=true*/) { if (planner.z_fade_height == zfh) return; diff --git a/Marlin/src/feature/bedlevel/bedlevel.h b/Marlin/src/feature/bedlevel/bedlevel.h index ccb9543e72..5bfa2b7faf 100644 --- a/Marlin/src/feature/bedlevel/bedlevel.h +++ b/Marlin/src/feature/bedlevel/bedlevel.h @@ -38,7 +38,7 @@ void set_bed_leveling_enabled(const bool enable=true); void reset_bed_level(); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - void set_z_fade_height(const_float_t zfh, const bool do_report=true); + void set_z_fade_height(const float zfh, const bool do_report=true); #endif #if ANY(MESH_BED_LEVELING, PROBE_MANUALLY) diff --git a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp index 14216ac424..155d34c4df 100644 --- a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp +++ b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp @@ -61,7 +61,7 @@ * Prepare a mesh-leveled linear move in a Cartesian setup, * splitting the move where it crosses mesh borders. */ - void mesh_bed_leveling::line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) { + void mesh_bed_leveling::line_to_destination(const feedRate_t scaled_fr_mm_s, uint8_t x_splits, uint8_t y_splits) { // Get current and destination cells for this line xy_uint8_t scel = cell_indexes(current_position), ecel = cell_indexes(destination); NOMORE(scel.x, GRID_MAX_CELLS_X - 1); diff --git a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h index cb4f36cd59..43dabd3adb 100644 --- a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h +++ b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h @@ -55,7 +55,7 @@ public: static bool mesh_is_valid() { return has_mesh(); } - static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; } + static void set_z(const int8_t px, const int8_t py, const float z) { z_values[px][py] = z; } static void zigzag(const int8_t index, int8_t &px, int8_t &py) { px = index % (GRID_MAX_POINTS_X); @@ -63,7 +63,7 @@ public: if (py & 1) px = (GRID_MAX_POINTS_X) - 1 - px; // Zig zag } - static void set_zigzag_z(const int8_t index, const_float_t z) { + static void set_zigzag_z(const int8_t index, const float z) { int8_t px, py; zigzag(index, px, py); set_z(px, py, z); @@ -72,33 +72,33 @@ public: static float get_mesh_x(const uint8_t i) { return index_to_xpos[i]; } static float get_mesh_y(const uint8_t i) { return index_to_ypos[i]; } - static uint8_t cell_index_x(const_float_t x) { + static uint8_t cell_index_x(const float x) { int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST); return constrain(cx, 0, GRID_MAX_CELLS_X - 1); } - static uint8_t cell_index_y(const_float_t y) { + static uint8_t cell_index_y(const float y) { int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); return constrain(cy, 0, GRID_MAX_CELLS_Y - 1); } - static xy_uint8_t cell_indexes(const_float_t x, const_float_t y) { + static xy_uint8_t cell_indexes(const float x, const float y) { return { cell_index_x(x), cell_index_y(y) }; } static xy_uint8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } - static int8_t probe_index_x(const_float_t x) { + static int8_t probe_index_x(const float x) { int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST); return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1; } - static int8_t probe_index_y(const_float_t y) { + static int8_t probe_index_y(const float y) { int8_t py = (y - (MESH_MIN_Y) + 0.5f * (MESH_Y_DIST)) * RECIPROCAL(MESH_Y_DIST); return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1; } - static xy_int8_t probe_indexes(const_float_t x, const_float_t y) { + static xy_int8_t probe_indexes(const float x, const float y) { return { probe_index_x(x), probe_index_y(y) }; } static xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.y); } - static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) { + static float calc_z0(const float a0, const float a1, const float z1, const float a2, const float z2) { const float delta_z = (z2 - z1) / (a2 - a1), delta_a = a0 - a1; return z1 + delta_a * delta_z; @@ -118,7 +118,7 @@ public: } #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) - static void line_to_destination(const_feedRate_t scaled_fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF); + static void line_to_destination(const feedRate_t scaled_fr_mm_s, uint8_t x_splits=0xFF, uint8_t y_splits=0xFF); #endif }; diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.cpp b/Marlin/src/feature/bedlevel/ubl/ubl.cpp index 0228bd247e..e1f2ed4c16 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl.cpp @@ -102,7 +102,7 @@ void unified_bed_leveling::invalidate() { set_all_mesh_points_to_value(NAN); } -void unified_bed_leveling::set_all_mesh_points_to_value(const_float_t value) { +void unified_bed_leveling::set_all_mesh_points_to_value(const float value) { GRID_LOOP(x, y) { z_values[x][y] = value; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, value)); @@ -115,7 +115,7 @@ void unified_bed_leveling::set_all_mesh_points_to_value(const_float_t value) { constexpr int16_t Z_STEPS_NAN = INT16_MAX; void unified_bed_leveling::set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values) { - auto z_to_store = [](const_float_t z) { + auto z_to_store = [](const float z) { if (isnan(z)) return Z_STEPS_NAN; const int32_t z_scaled = TRUNC(z * mesh_store_scaling); if (z_scaled == Z_STEPS_NAN || !WITHIN(z_scaled, INT16_MIN, INT16_MAX)) diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.h b/Marlin/src/feature/bedlevel/ubl/ubl.h index b08cb812f8..f6e9ba0cd9 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.h +++ b/Marlin/src/feature/bedlevel/ubl/ubl.h @@ -67,15 +67,15 @@ private: static G29_parameters_t param; #if IS_NEWPANEL - static void move_z_with_encoder(const_float_t multiplier); + static void move_z_with_encoder(const float multiplier); static float measure_point_with_encoder(); static float measure_business_card_thickness(); - static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , const bool) __O0; + static void manually_probe_remaining_mesh(const xy_pos_t&, const float, const float, const bool) __O0; static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) __O0; #endif static bool G29_parse_parameters() __O0; - static void shift_mesh_height(); + static void shift_mesh_height(const float zoffs); static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) __O0; static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map); static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir); @@ -101,13 +101,13 @@ public: static mesh_index_pair find_furthest_invalid_mesh_point() __O0; static void reset(); static void invalidate(); - static void set_all_mesh_points_to_value(const_float_t value); - static void adjust_mesh_to_mean(const bool cflag, const_float_t value); + static void set_all_mesh_points_to_value(const float value); + static void adjust_mesh_to_mean(const bool cflag, const float value); static bool sanity_check(); static void smart_fill_mesh(); static void G29() __O0; // O0 for no optimization - static void smart_fill_wlsf(const_float_t ) __O2; // O2 gives smaller code than Os on A2560 + static void smart_fill_wlsf(const float ) __O2; // O2 gives smaller code than Os on A2560 static int8_t storage_slot; @@ -130,42 +130,42 @@ public: unified_bed_leveling(); - FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; } + FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const float z) { z_values[px][py] = z; } - static int8_t cell_index_x_raw(const_float_t x) { + static int8_t cell_index_x_raw(const float x) { return FLOOR((x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST)); } - static int8_t cell_index_y_raw(const_float_t y) { + static int8_t cell_index_y_raw(const float y) { return FLOOR((y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST)); } - static bool cell_index_x_valid(const_float_t x) { + static bool cell_index_x_valid(const float x) { return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1); } - static bool cell_index_y_valid(const_float_t y) { + static bool cell_index_y_valid(const float y) { return WITHIN(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1); } - static uint8_t cell_index_x(const_float_t x) { + static uint8_t cell_index_x(const float x) { return constrain(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1); } - static uint8_t cell_index_y(const_float_t y) { + static uint8_t cell_index_y(const float y) { return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1); } - static xy_uint8_t cell_indexes(const_float_t x, const_float_t y) { + static xy_uint8_t cell_indexes(const float x, const float y) { return { cell_index_x(x), cell_index_y(y) }; } static xy_uint8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } - static int8_t closest_x_index(const_float_t x) { + static int8_t closest_x_index(const float x) { const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST); return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1; } - static int8_t closest_y_index(const_float_t y) { + static int8_t closest_y_index(const float y) { const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST); return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1; } @@ -188,7 +188,7 @@ public: * It is fairly expensive with its 4 floating point additions and 2 floating point * multiplications. */ - FORCE_INLINE static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) { + FORCE_INLINE static float calc_z0(const float a0, const float a1, const float z1, const float a2, const float z2) { return z1 + (z2 - z1) * (a0 - a1) / (a2 - a1); } @@ -202,7 +202,7 @@ public: * z_correction_for_x_on_horizontal_mesh_line is an optimization for * the case where the printer is making a vertical line that only crosses horizontal mesh lines. */ - static float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) { + static float z_correction_for_x_on_horizontal_mesh_line(const float rx0, const int x1_i, const int yi) { if (!WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(yi, 0, (GRID_MAX_POINTS_Y) - 1)) { if (DEBUGGING(LEVELING)) { @@ -225,7 +225,7 @@ public: // // See comments above for z_correction_for_x_on_horizontal_mesh_line // - static float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) { + static float z_correction_for_y_on_vertical_mesh_line(const float ry0, const int xi, const int y1_i) { if (!WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(y1_i, 0, (GRID_MAX_POINTS_Y) - 1)) { if (DEBUGGING(LEVELING)) { @@ -251,7 +251,7 @@ public: * Z-Height at both ends. Then it does a linear interpolation of these heights based * on the Y position within the cell. */ - static float get_z_correction(const_float_t rx0, const_float_t ry0) { + static float get_z_correction(const float rx0, const float ry0) { const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped /** @@ -295,9 +295,9 @@ public: } #if UBL_SEGMENTED - static bool line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s); + static bool line_to_destination_segmented(const feedRate_t scaled_fr_mm_s); #else - static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e); + static void line_to_destination_cartesian(const feedRate_t scaled_fr_mm_s, const uint8_t e); #endif static bool mesh_is_valid() { diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp index fcf408b34e..24a1638731 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp @@ -48,6 +48,10 @@ #include "../hilbert_curve.h" #endif +#if ENABLED(FT_MOTION) + #include "../../../module/ft_motion.h" +#endif + #include #define UBL_G29_P31 @@ -297,12 +301,21 @@ G29_parameters_t unified_bed_leveling::param; void unified_bed_leveling::G29() { - bool probe_deployed = false; + #ifdef EVENT_GCODE_AFTER_G29 + bool probe_deployed = false; + #define SET_PROBE_DEPLOYED(N) probe_deployed = N + #else + #define SET_PROBE_DEPLOYED(N) + #endif + if (G29_parse_parameters()) return; // Abort on parameter error const uint8_t p_val = parser.byteval('P'); const bool may_move = p_val == 1 || p_val == 2 || p_val == 4 || parser.seen_test('J'); + // Potentially disable Fixed-Time Motion for probing + TERN_(FT_MOTION, FTM_DISABLE_IN_SCOPE()); + // Check for commands that require the printer to be homed if (may_move) { planner.synchronize(); @@ -316,6 +329,11 @@ void unified_bed_leveling::G29() { #endif probe.use_probing_tool(); + #ifdef EVENT_GCODE_BEFORE_G29 + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Before G29 G-code: ", EVENT_GCODE_BEFORE_G29); + gcode.process_subcommands_now(F(EVENT_GCODE_BEFORE_G29)); + #endif + // Position bed horizontally and Z probe vertically. #if HAS_SAFE_BED_LEVELING xyze_pos_t safe_position = current_position; @@ -379,7 +397,7 @@ void unified_bed_leveling::G29() { if (parser.seen('Q')) { const int16_t test_pattern = parser.has_value() ? parser.value_int() : -99; if (!WITHIN(test_pattern, TERN0(UBL_DEVEL_DEBUGGING, -1), 2)) { - SERIAL_ECHOLNPGM("?Invalid (Q) test pattern. (" TERN(UBL_DEVEL_DEBUGGING, "-1", "0") " to 2)\n"); + SERIAL_ECHOLN(F("?Invalid "), F("(Q) test pattern. (" TERN(UBL_DEVEL_DEBUGGING, "-1", "0") " to 2)\n")); return; } SERIAL_ECHOLNPGM("Applying test pattern.\n"); @@ -430,7 +448,7 @@ void unified_bed_leveling::G29() { do_blocking_move_to_xy(0.5f * ((MESH_MIN_X) + (MESH_MAX_X)), 0.5f * ((MESH_MIN_Y) + (MESH_MAX_Y))); #endif report_current_position(); - probe_deployed = true; + SET_PROBE_DEPLOYED(true); } #endif // HAS_BED_PROBE @@ -465,7 +483,7 @@ void unified_bed_leveling::G29() { probe_entire_mesh(param.XY_pos, parser.seen_test('T'), parser.seen_test('E'), parser.seen_test('U')); report_current_position(); - probe_deployed = true; + SET_PROBE_DEPLOYED(true); } break; #endif // HAS_BED_PROBE @@ -503,7 +521,7 @@ void unified_bed_leveling::G29() { SERIAL_ECHOLNPGM("?Error in Business Card measurement."); return; } - probe_deployed = true; + SET_PROBE_DEPLOYED(true); } if (!position_is_reachable(param.XY_pos)) { @@ -593,7 +611,7 @@ void unified_bed_leveling::G29() { case 5: adjust_mesh_to_mean(param.C_seen, param.C_constant); break; - case 6: shift_mesh_height(); break; + case 6: shift_mesh_height(param.C_constant); break; } } @@ -630,7 +648,7 @@ void unified_bed_leveling::G29() { } if (!WITHIN(param.KLS_storage_slot, 0, a - 1)) { - SERIAL_ECHOLNPGM("?Invalid storage slot.\n?Use 0 to ", a - 1); + SERIAL_ECHOLN(F("?Invalid "), F("storage slot.\n?Use 0 to "), a - 1); return; } @@ -658,7 +676,7 @@ void unified_bed_leveling::G29() { } if (!WITHIN(param.KLS_storage_slot, 0, a - 1)) { - SERIAL_ECHOLNPGM("?Invalid storage slot.\n?Use 0 to ", a - 1); + SERIAL_ECHOLN(F("?Invalid "), F("storage slot.\n?Use 0 to "), a - 1); goto LEAVE; } @@ -681,13 +699,11 @@ void unified_bed_leveling::G29() { #endif #ifdef EVENT_GCODE_AFTER_G29 - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Probe End Script: ", EVENT_GCODE_AFTER_G29); + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("After G29 G-code: ", EVENT_GCODE_AFTER_G29); if (probe_deployed) { planner.synchronize(); gcode.process_subcommands_now(F(EVENT_GCODE_AFTER_G29)); } - #else - UNUSED(probe_deployed); #endif probe.use_probing_tool(false); @@ -699,7 +715,7 @@ void unified_bed_leveling::G29() { * G29 P5 C : Adjust Mesh To Mean (and subtract the given offset). * Find the mean average and shift the mesh to center on that value. */ -void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const_float_t offset) { +void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const float offset) { float sum = 0; uint8_t n = 0; GRID_LOOP(x, y) @@ -735,10 +751,10 @@ void unified_bed_leveling::adjust_mesh_to_mean(const bool cflag, const_float_t o /** * G29 P6 C : Shift Mesh Height by a uniform constant. */ -void unified_bed_leveling::shift_mesh_height() { +void unified_bed_leveling::shift_mesh_height(const float zoffs) { GRID_LOOP(x, y) if (!isnan(z_values[x][y])) { - z_values[x][y] += param.C_constant; + z_values[x][y] += zoffs; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y])); } } @@ -853,7 +869,7 @@ void set_message_with_feedback(FSTR_P const fstr) { return false; } - void unified_bed_leveling::move_z_with_encoder(const_float_t multiplier) { + void unified_bed_leveling::move_z_with_encoder(const float multiplier) { ui.wait_for_release(); while (!ui.button_pressed()) { idle(); @@ -936,7 +952,7 @@ void set_message_with_feedback(FSTR_P const fstr) { * Move to INVALID points and * NOTE: Blocks the G-code queue and captures Marlin UI during use. */ - void unified_bed_leveling::manually_probe_remaining_mesh(const xy_pos_t &pos, const_float_t z_clearance, const_float_t thick, const bool do_ubl_mesh_map) { + void unified_bed_leveling::manually_probe_remaining_mesh(const xy_pos_t &pos, const float z_clearance, const float thick, const bool do_ubl_mesh_map) { ui.capture(); TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart()); @@ -1166,7 +1182,7 @@ bool unified_bed_leveling::G29_parse_parameters() { #if HAS_BED_PROBE param.J_grid_size = parser.value_byte(); if (param.J_grid_size && !WITHIN(param.J_grid_size, 2, 9)) { - SERIAL_ECHOLNPGM("?Invalid grid size (J) specified (2-9).\n"); + SERIAL_ECHOLN(F("?Invalid "), F("grid size (J) specified (2-9).\n")); err_flag = true; } #else @@ -1590,7 +1606,7 @@ void unified_bed_leveling::smart_fill_mesh() { } if (abort_flag) break; - zig_zag ^= true; + FLIP(zig_zag); } } probe.stow(); @@ -1644,10 +1660,10 @@ void unified_bed_leveling::smart_fill_mesh() { */ #if ENABLED(VALIDATE_MESH_TILT) auto d_from = []{ DEBUG_ECHOPGM("D from "); }; - auto normed = [&](const xy_pos_t &pos, const_float_t zadd) { + auto normed = [&](const xy_pos_t &pos, const float zadd) { return normal.x * pos.x + normal.y * pos.y + zadd; }; - auto debug_pt = [](const int num, const xy_pos_t &pos, const_float_t zadd) { + auto debug_pt = [](const int num, const xy_pos_t &pos, const float zadd) { d_from(); DEBUG_ECHOLN(F("Point "), num, C(':'), p_float_t(normed(pos, zadd), 6), F(" Z error = "), p_float_t(zadd - get_z_correction(pos), 6)); }; @@ -1668,7 +1684,7 @@ void unified_bed_leveling::smart_fill_mesh() { #endif // HAS_BED_PROBE #if ENABLED(UBL_G29_P31) - void unified_bed_leveling::smart_fill_wlsf(const_float_t weight_factor) { + void unified_bed_leveling::smart_fill_wlsf(const float weight_factor) { // For each undefined mesh point, compute a distance-weighted least squares fit // from all the originally populated mesh points, weighted toward the point @@ -1778,14 +1794,14 @@ void unified_bed_leveling::smart_fill_mesh() { SERIAL_ECHOLNPGM("ubl_state_at_invocation :", ubl_state_at_invocation, "\nubl_state_recursion_chk :", ubl_state_recursion_chk); serial_delay(50); - SERIAL_ECHOLNPGM("Meshes go from ", hex_address((void*)settings.meshes_start_index()), " to ", hex_address((void*)settings.meshes_end_index())); + SERIAL_ECHOLNPGM("Meshes go from ", _hex_word(settings.meshes_start_index()), " to ", _hex_word(settings.meshes_end_index())); serial_delay(50); SERIAL_ECHOLNPGM("sizeof(unified_bed_leveling) : ", sizeof(unified_bed_leveling)); SERIAL_ECHOLNPGM("z_value[][] size: ", sizeof(z_values)); serial_delay(25); - SERIAL_ECHOLNPGM("EEPROM free for UBL: ", hex_address((void*)(settings.meshes_end_index() - settings.meshes_start_index()))); + SERIAL_ECHOLNPGM("EEPROM free for UBL: ", _hex_word(settings.meshes_end_index() - settings.meshes_start_index())); serial_delay(50); SERIAL_ECHOLNPGM("EEPROM can hold ", settings.calc_num_meshes(), " meshes.\n"); @@ -1835,7 +1851,7 @@ void unified_bed_leveling::smart_fill_mesh() { } if (!parser.has_value() || !WITHIN(parser.value_int(), 0, a - 1)) { - SERIAL_ECHOLNPGM("?Invalid storage slot.\n?Use 0 to ", a - 1); + SERIAL_ECHOLN(F("?Invalid "), F("storage slot.\n?Use 0 to "), a - 1); return; } diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp index 053a68b77d..be9fb7b947 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp @@ -47,7 +47,7 @@ // corners of cells. To fix the issue, simply check if the start/end of the line // is very close to a cell boundary in advance and don't split the line there. - void unified_bed_leveling::line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t extruder) { + void unified_bed_leveling::line_to_destination_cartesian(const feedRate_t scaled_fr_mm_s, const uint8_t extruder) { /** * Much of the nozzle movement will be within the same cell. So we will do as little computation * as possible to determine if this is the case. If this move is within the same cell, we will @@ -351,7 +351,7 @@ * Returns true if did NOT move, false if moved (requires current_position update). */ - bool __O2 unified_bed_leveling::line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s) { + bool __O2 unified_bed_leveling::line_to_destination_segmented(const feedRate_t scaled_fr_mm_s) { if (!position_is_reachable(destination)) // fail if moving outside reachable boundary return true; // did not move, so current_position still accurate diff --git a/Marlin/src/feature/binary_stream.h b/Marlin/src/feature/binary_stream.h index a9cd12b85e..304fdae300 100644 --- a/Marlin/src/feature/binary_stream.h +++ b/Marlin/src/feature/binary_stream.h @@ -134,21 +134,21 @@ private: public: static void idle() { - // If a transfer is interrupted and a file is left open, abort it after TIMEOUT ms + // If a transfer is interrupted and a file is left open, abort it after 'idle_period' ms const millis_t ms = millis(); if (transfer_active && ELAPSED(ms, idle_timeout)) { - idle_timeout = ms + IDLE_PERIOD; + idle_timeout = ms + idle_period; if (ELAPSED(ms, transfer_timeout)) transfer_abort(); } } static void process(uint8_t packet_type, char *buffer, const uint16_t length) { - transfer_timeout = millis() + TIMEOUT; + transfer_timeout = millis() + timeout; switch (static_cast(packet_type)) { case FileTransfer::QUERY: - SERIAL_ECHOPGM("PFT:version:", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH); + SERIAL_ECHO(F("PFT:version:"), version_major, C('.'), version_minor, C('.'), version_patch); #if ENABLED(BINARY_STREAM_COMPRESSION) - SERIAL_ECHOLNPGM(":compression:heatshrink,", HEATSHRINK_STATIC_WINDOW_BITS, ",", HEATSHRINK_STATIC_LOOKAHEAD_BITS); + SERIAL_ECHOLN(F(":compression:heatshrink,"), HEATSHRINK_STATIC_WINDOW_BITS, C(','), HEATSHRINK_STATIC_LOOKAHEAD_BITS); #else SERIAL_ECHOLNPGM(":compression:none"); #endif @@ -194,7 +194,7 @@ public: } } - static const uint16_t VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0, TIMEOUT = 10000, IDLE_PERIOD = 1000; + static const uint16_t version_major = 0, version_minor = 1, version_patch = 0, timeout = 10000, idle_period = 1000; }; class BinaryStream { @@ -209,7 +209,7 @@ public: struct Packet { // 10 byte protocol overhead, ascii with checksum and line number has a minimum of 7 increasing with line union Header { - static constexpr uint16_t HEADER_TOKEN = 0xB5AD; + static constexpr uint16_t header_token = 0xB5AD; struct [[gnu::packed]] { uint16_t token; // packet start token uint8_t sync; // stream sync, resend id and packet loss detection @@ -245,7 +245,7 @@ public: bytes_received = 0; checksum = 0; header_checksum = 0; - timeout = millis() + PACKET_MAX_WAIT; + timeout = millis() + packet_max_wait; buffer = nullptr; } } packet{}; @@ -272,14 +272,14 @@ public: } if (!bs_serial_data_available(card.transfer_port_index)) return false; data = bs_read_serial(card.transfer_port_index); - packet.timeout = millis() + PACKET_MAX_WAIT; + packet.timeout = millis() + packet_max_wait; return true; } template void receive(char (&buffer)[buffer_size]) { uint8_t data = 0; - millis_t transfer_window = millis() + RX_TIMESLICE; + millis_t transfer_window = millis() + rx_timeslice; #if HAS_MEDIA PORT_REDIRECT(SERIAL_PORTMASK(card.transfer_port_index)); @@ -299,7 +299,7 @@ public: case StreamState::PACKET_WAIT: if (!stream_read(data)) { idle(); return; } // no active packet so don't wait packet.header.data[1] = data; - if (packet.header.token == packet.header.HEADER_TOKEN) { + if (packet.header.token == packet.header.header_token) { packet.bytes_received = 2; stream_state = StreamState::PACKET_HEADER; } @@ -322,7 +322,7 @@ public: if (packet.header.checksum == packet.header_checksum) { // The SYNC control packet is a special case in that it doesn't require the stream sync to be correct if (static_cast(packet.header.protocol()) == Protocol::CONTROL && static_cast(packet.header.type()) == ProtocolControl::SYNC) { - SERIAL_ECHOLNPGM("ss", sync, ",", buffer_size, ",", VERSION_MAJOR, ".", VERSION_MINOR, ".", VERSION_PATCH); + SERIAL_ECHOLN(F("ss"), sync, C(','), buffer_size, C(','), version_major, C('.'), version_minor, C('.'), version_patch); stream_state = StreamState::PACKET_RESET; break; } @@ -398,7 +398,7 @@ public: stream_state = StreamState::PACKET_RESET; break; case StreamState::PACKET_RESEND: - if (packet_retries < MAX_RETRIES || MAX_RETRIES == 0) { + if (packet_retries < max_retries || max_retries == 0) { packet_retries++; stream_state = StreamState::PACKET_RESET; SERIAL_ECHO_MSG("Resend request ", packet_retries); @@ -446,7 +446,7 @@ public: SDFileTransferProtocol::idle(); } - static const uint16_t PACKET_MAX_WAIT = 500, RX_TIMESLICE = 20, MAX_RETRIES = 0, VERSION_MAJOR = 0, VERSION_MINOR = 1, VERSION_PATCH = 0; + static const uint16_t packet_max_wait = 500, rx_timeslice = 20, max_retries = 0, version_major = 0, version_minor = 1, version_patch = 0; uint8_t packet_retries, sync; uint16_t buffer_next_index; uint32_t bytes_received; diff --git a/Marlin/src/feature/bltouch.cpp b/Marlin/src/feature/bltouch.cpp index 02f76c1392..5b498cd474 100644 --- a/Marlin/src/feature/bltouch.cpp +++ b/Marlin/src/feature/bltouch.cpp @@ -48,7 +48,7 @@ bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) { // The previous write should've already delayed to detect the alarm. if (cmd != current) { servo[Z_PROBE_SERVO_NR].move(cmd); - safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay + safe_delay(_MAX(ms, uint32_t(BLTOUCH_DELAY))); // BLTOUCH_DELAY is also the *minimum* delay } return triggered(); } @@ -72,13 +72,9 @@ void BLTouch::init(const bool set_voltage/*=false*/) { #else #ifdef DEBUG_OUT - if (DEBUGGING(LEVELING)) { - PGMSTR(mode0, "OD"); - PGMSTR(mode1, "5V"); - DEBUG_ECHOPGM("BLTouch Mode: "); - DEBUG_ECHOPGM_P(bltouch.od_5v_mode ? mode1 : mode0); - DEBUG_ECHOLNPGM(" (Default " TERN(BLTOUCH_SET_5V_MODE, "5V", "OD") ")"); - } + if (DEBUGGING(LEVELING)) + DEBUG_ECHOLN( F("BLTouch Mode: "), bltouch.od_5v_mode ? F("5V") : F("OD"), + F(" (Default " TERN(BLTOUCH_SET_5V_MODE, "5V", "OD") ")")); #endif const bool should_set = od_5v_mode != ENABLED(BLTOUCH_SET_5V_MODE); diff --git a/Marlin/src/feature/bltouch.h b/Marlin/src/feature/bltouch.h index 0f9f2e68ba..1371ec9f46 100644 --- a/Marlin/src/feature/bltouch.h +++ b/Marlin/src/feature/bltouch.h @@ -26,6 +26,7 @@ // BLTouch commands are sent as servo angles typedef unsigned char BLTCommand; +#define DEPLOY_ALARM true #define STOW_ALARM true #define BLTOUCH_DEPLOY 10 #define BLTOUCH_STOW 90 @@ -104,7 +105,7 @@ public: static bool triggered(); private: - static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY); } + static bool _deploy_query_alarm() { return command(BLTOUCH_DEPLOY, BLTOUCH_DEPLOY_DELAY) == DEPLOY_ALARM; } static bool _stow_query_alarm() { return command(BLTOUCH_STOW, BLTOUCH_STOW_DELAY) == STOW_ALARM; } static void clear(); diff --git a/Marlin/src/feature/cancel_object.cpp b/Marlin/src/feature/cancel_object.cpp index 0040f6ed9d..c17c9988e4 100644 --- a/Marlin/src/feature/cancel_object.cpp +++ b/Marlin/src/feature/cancel_object.cpp @@ -30,23 +30,20 @@ CancelObject cancelable; -int8_t CancelObject::object_count, // = 0 - CancelObject::active_object = -1; -uint32_t CancelObject::canceled; // = 0x0000 -bool CancelObject::skipping; // = false +cancel_state_t CancelObject::state; void CancelObject::set_active_object(const int8_t obj) { - active_object = obj; + state.active_object = obj; if (WITHIN(obj, 0, 31)) { - if (obj >= object_count) object_count = obj + 1; - skipping = TEST(canceled, obj); + if (obj >= state.object_count) state.object_count = obj + 1; + state.skipping = TEST(state.canceled, obj); } else - skipping = false; + state.skipping = false; #if ALL(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING) - if (active_object >= 0) - ui.set_status(MString<30>(GET_TEXT_F(MSG_PRINTING_OBJECT), ' ', active_object)); + if (state.active_object >= 0) + ui.set_status(MString<30>(GET_TEXT_F(MSG_PRINTING_OBJECT), ' ', state.active_object)); else ui.reset_status(); #endif @@ -54,29 +51,29 @@ void CancelObject::set_active_object(const int8_t obj) { void CancelObject::cancel_object(const int8_t obj) { if (WITHIN(obj, 0, 31)) { - SBI(canceled, obj); - if (obj == active_object) skipping = true; + SBI(state.canceled, obj); + if (obj == state.active_object) state.skipping = true; } } void CancelObject::uncancel_object(const int8_t obj) { if (WITHIN(obj, 0, 31)) { - CBI(canceled, obj); - if (obj == active_object) skipping = false; + CBI(state.canceled, obj); + if (obj == state.active_object) state.skipping = false; } } void CancelObject::report() { - if (active_object >= 0) - SERIAL_ECHO_MSG("Active Object: ", active_object); + if (state.active_object >= 0) + SERIAL_ECHO_MSG("Active Object: ", state.active_object); - if (canceled) { - SERIAL_ECHO_START(); - SERIAL_ECHOPGM("Canceled:"); - for (int i = 0; i < object_count; i++) - if (TEST(canceled, i)) { SERIAL_CHAR(' '); SERIAL_ECHO(i); } - SERIAL_EOL(); - } + if (state.canceled == 0x0000) return; + + SERIAL_ECHO_START(); + SERIAL_ECHOPGM("Canceled:"); + for (int i = 0; i < state.object_count; i++) + if (TEST(state.canceled, i)) SERIAL_ECHO(C(' '), i); + SERIAL_EOL(); } #endif // CANCEL_OBJECTS diff --git a/Marlin/src/feature/cancel_object.h b/Marlin/src/feature/cancel_object.h index 62548a3719..7f04612d13 100644 --- a/Marlin/src/feature/cancel_object.h +++ b/Marlin/src/feature/cancel_object.h @@ -23,19 +23,23 @@ #include +typedef struct CancelState { + bool skipping = false; + int8_t object_count = 0, active_object = 0; + uint32_t canceled = 0x0000; +} cancel_state_t; + class CancelObject { public: - static bool skipping; - static int8_t object_count, active_object; - static uint32_t canceled; - static void set_active_object(const int8_t obj); + static cancel_state_t state; + static void set_active_object(const int8_t obj=state.active_object); static void cancel_object(const int8_t obj); static void uncancel_object(const int8_t obj); static void report(); - static bool is_canceled(const int8_t obj) { return TEST(canceled, obj); } + static bool is_canceled(const int8_t obj) { return TEST(state.canceled, obj); } static void clear_active_object() { set_active_object(-1); } - static void cancel_active_object() { cancel_object(active_object); } - static void reset() { canceled = 0x0000; object_count = 0; clear_active_object(); } + static void cancel_active_object() { cancel_object(state.active_object); } + static void reset() { state.canceled = 0x0000; state.object_count = 0; clear_active_object(); } }; extern CancelObject cancelable; diff --git a/Marlin/src/feature/caselight.cpp b/Marlin/src/feature/caselight.cpp index eb580a6d62..95221111b2 100644 --- a/Marlin/src/feature/caselight.cpp +++ b/Marlin/src/feature/caselight.cpp @@ -40,7 +40,7 @@ bool CaseLight::on = CASE_LIGHT_DEFAULT_ON; #if CASE_LIGHT_IS_COLOR_LED constexpr uint8_t init_case_light[] = CASE_LIGHT_DEFAULT_COLOR; - LEDColor CaseLight::color = { init_case_light[0], init_case_light[1], init_case_light[2] OPTARG(HAS_WHITE_LED, init_case_light[3]) }; + LED1Color_t CaseLight::color = { init_case_light[0], init_case_light[1], init_case_light[2] OPTARG(HAS_WHITE_LED, init_case_light[3]) }; #endif void CaseLight::update(const bool sflag) { @@ -67,13 +67,13 @@ void CaseLight::update(const bool sflag) { #if ENABLED(CASE_LIGHT_USE_NEOPIXEL) if (on) // Use current color of (NeoPixel) leds and new brightness level - leds.set_color(LEDColor(leds.color.r, leds.color.g, leds.color.b OPTARG(HAS_WHITE_LED, leds.color.w) OPTARG(NEOPIXEL_LED, n10ct))); + leds.set_color(LED1Color_t(leds.color.r, leds.color.g, leds.color.b OPTARG(HAS_WHITE_LED, leds.color.w) OPTARG(NEOPIXEL_LED, n10ct))); else // Switch off leds leds.set_off(); #else // Use CaseLight color (CASE_LIGHT_DEFAULT_COLOR) and new brightness level - leds.set_color(LEDColor(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w) OPTARG(NEOPIXEL_LED, n10ct))); + leds.set_color(LED1Color_t(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w) OPTARG(NEOPIXEL_LED, n10ct))); #endif #else // !CASE_LIGHT_IS_COLOR_LED diff --git a/Marlin/src/feature/caselight.h b/Marlin/src/feature/caselight.h index d88b3d67bf..28466cecaa 100644 --- a/Marlin/src/feature/caselight.h +++ b/Marlin/src/feature/caselight.h @@ -24,7 +24,7 @@ #include "../inc/MarlinConfig.h" #if CASE_LIGHT_IS_COLOR_LED - #include "leds/leds.h" // for LEDColor + #include "leds/leds.h" // for LED1Color_t #endif class CaseLight { @@ -50,7 +50,7 @@ public: #if ENABLED(CASE_LIGHT_IS_COLOR_LED) private: - static LEDColor color; + static LED1Color_t color; #endif }; diff --git a/Marlin/src/feature/controllerfan.cpp b/Marlin/src/feature/controllerfan.cpp index acbae459e7..243f0606ce 100644 --- a/Marlin/src/feature/controllerfan.cpp +++ b/Marlin/src/feature/controllerfan.cpp @@ -44,9 +44,30 @@ uint8_t ControllerFan::speed; void ControllerFan::setup() { SET_OUTPUT(CONTROLLER_FAN_PIN); - #ifdef CONTROLLER_FAN2_PIN + #if PIN_EXISTS(CONTROLLER_FAN2) SET_OUTPUT(CONTROLLER_FAN2_PIN); #endif + #if PIN_EXISTS(CONTROLLER_FAN3) + SET_OUTPUT(CONTROLLER_FAN3_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN4) + SET_OUTPUT(CONTROLLER_FAN4_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN5) + SET_OUTPUT(CONTROLLER_FAN5_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN6) + SET_OUTPUT(CONTROLLER_FAN6_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN7) + SET_OUTPUT(CONTROLLER_FAN7_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN8) + SET_OUTPUT(CONTROLLER_FAN8_PIN); + #endif + #if PIN_EXISTS(CONTROLLER_FAN9) + SET_OUTPUT(CONTROLLER_FAN9_PIN); + #endif init(); } @@ -70,7 +91,9 @@ void ControllerFan::update() { */ const ena_mask_t axis_mask = TERN(CONTROLLER_FAN_USE_Z_ONLY, _BV(Z_AXIS), (ena_mask_t)~TERN0(CONTROLLER_FAN_IGNORE_Z, _BV(Z_AXIS))); if ( (stepper.axis_enabled.bits & axis_mask) - || TERN0(HAS_HEATED_BED, thermalManager.temp_bed.soft_pwm_amount > 0) + #if ALL(HAS_HEATED_BED, CONTROLLER_FAN_BED_HEATING) + || thermalManager.temp_bed.soft_pwm_amount > 0 + #endif #ifdef CONTROLLER_FAN_MIN_BOARD_TEMP || thermalManager.wholeDegBoard() >= CONTROLLER_FAN_MIN_BOARD_TEMP #endif @@ -85,7 +108,7 @@ void ControllerFan::update() { * - If System is on idle and idle fan speed settings is activated. */ set_fan_speed( - settings.auto_mode && lastComponentOn && PENDING(ms, lastComponentOn + SEC_TO_MS(settings.duration)) + settings.auto_mode && lastComponentOn && PENDING(ms, lastComponentOn, SEC_TO_MS(settings.duration)) ? settings.active_speed : settings.idle_speed ); @@ -105,19 +128,38 @@ void ControllerFan::update() { fan_kick_end = 0; #endif + #define SET_CONTROLLER_FAN(N) do { \ + if (PWM_PIN(CONTROLLER_FAN##N##_PIN)) hal.set_pwm_duty(pin_t(CONTROLLER_FAN##N##_PIN), speed); \ + else WRITE(CONTROLLER_FAN##N##_PIN, speed > 0);\ + } while (0) + #if ENABLED(FAN_SOFT_PWM) soft_pwm_speed = speed; #else - if (PWM_PIN(CONTROLLER_FAN_PIN)) - hal.set_pwm_duty(pin_t(CONTROLLER_FAN_PIN), speed); - else - WRITE(CONTROLLER_FAN_PIN, speed > 0); - - #ifdef CONTROLLER_FAN2_PIN - if (PWM_PIN(CONTROLLER_FAN2_PIN)) - hal.set_pwm_duty(pin_t(CONTROLLER_FAN2_PIN), speed); - else - WRITE(CONTROLLER_FAN2_PIN, speed > 0); + SET_CONTROLLER_FAN(); + #if PIN_EXISTS(CONTROLLER_FAN2) + SET_CONTROLLER_FAN(2); + #endif + #if PIN_EXISTS(CONTROLLER_FAN3) + SET_CONTROLLER_FAN(3); + #endif + #if PIN_EXISTS(CONTROLLER_FAN4) + SET_CONTROLLER_FAN(4); + #endif + #if PIN_EXISTS(CONTROLLER_FAN5) + SET_CONTROLLER_FAN(5); + #endif + #if PIN_EXISTS(CONTROLLER_FAN6) + SET_CONTROLLER_FAN(6); + #endif + #if PIN_EXISTS(CONTROLLER_FAN7) + SET_CONTROLLER_FAN(7); + #endif + #if PIN_EXISTS(CONTROLLER_FAN8) + SET_CONTROLLER_FAN(8); + #endif + #if PIN_EXISTS(CONTROLLER_FAN9) + SET_CONTROLLER_FAN(9); #endif #endif } diff --git a/Marlin/src/feature/cooler.h b/Marlin/src/feature/cooler.h index 9891514e23..ef3590c593 100644 --- a/Marlin/src/feature/cooler.h +++ b/Marlin/src/feature/cooler.h @@ -40,7 +40,7 @@ public: static bool enabled; static void enable() { enabled = true; } static void disable() { enabled = false; } - static void toggle() { enabled = !enabled; } + static void toggle() { FLIP(enabled); } static uint8_t mode; // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling static void set_mode(const uint8_t m) { mode = m; } @@ -96,7 +96,7 @@ public: #if ENABLED(FLOWMETER_SAFETY) static bool flowfault; // Flag that the cooler is in a fault state static bool flowsafety_enabled; // Flag to disable the cutter if flow rate is too low - static void flowsafety_toggle() { flowsafety_enabled = !flowsafety_enabled; } + static void flowsafety_toggle() { FLIP(flowsafety_enabled); } static bool check_flow_too_low() { const bool too_low = flowsafety_enabled && flowrate < (FLOWMETER_MIN_LITERS_PER_MINUTE); flowfault = too_low; diff --git a/Marlin/src/feature/dac/stepper_dac.cpp b/Marlin/src/feature/dac/stepper_dac.cpp index f5664bc598..65423d3189 100644 --- a/Marlin/src/feature/dac/stepper_dac.cpp +++ b/Marlin/src/feature/dac/stepper_dac.cpp @@ -68,7 +68,7 @@ void StepperDAC::set_current_value(const uint8_t channel, uint16_t val) { } void StepperDAC::set_current_percent(const uint8_t channel, float val) { - set_current_value(channel, _MIN(val, 100.0f) * (DAC_STEPPER_MAX) / 100.0f); + set_current_value(channel, _MIN(val, 100.0f) * (DAC_STEPPER_MAX) * 0.01f); } static float dac_perc(int8_t n) { return mcp4728.getDrvPct(dac_order[n]); } @@ -87,7 +87,7 @@ void StepperDAC::print_values() { LOOP_LOGICAL_AXES(a) { SERIAL_CHAR(' ', IAXIS_CHAR(a), ':'); SERIAL_ECHO(dac_perc(a)); - SERIAL_ECHOPGM_P(PSTR(" ("), dac_amps(AxisEnum(a)), PSTR(")")); + SERIAL_ECHOPGM_P(PSTR(" ("), dac_amps((AxisEnum)a), PSTR(")")); } #if HAS_EXTRUDERS SERIAL_ECHOLNPGM_P(SP_E_LBL, dac_perc(E_AXIS), PSTR(" ("), dac_amps(E_AXIS), PSTR(")")); diff --git a/Marlin/src/feature/e_parser.cpp b/Marlin/src/feature/e_parser.cpp index da193ed30a..9e974f2e83 100644 --- a/Marlin/src/feature/e_parser.cpp +++ b/Marlin/src/feature/e_parser.cpp @@ -33,6 +33,9 @@ // Static data members bool EmergencyParser::killed_by_M112, // = false EmergencyParser::quickstop_by_M410, + #if ENABLED(FTM_RESONANCE_TEST) + EmergencyParser::rt_stop_by_M496, // = false + #endif #if HAS_MEDIA EmergencyParser::sd_abort_by_M524, #endif @@ -61,9 +64,13 @@ extern bool wait_for_user, wait_for_heatup; #endif void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { + auto uppercase = [](char c) { + return TERN0(GCODE_CASE_INSENSITIVE, WITHIN(c, 'a', 'z')) ? c + 'A' - 'a' : c; + }; + switch (state) { case EP_RESET: - switch (c) { + switch (uppercase(c)) { case ' ': case '\n': case '\r': break; case 'N': state = EP_N; break; case 'M': state = EP_M; break; @@ -81,7 +88,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { break; case EP_N: - switch (c) { + switch (uppercase(c)) { case '0' ... '9': case '-': case ' ': break; case 'M': state = EP_M; break; @@ -143,29 +150,30 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M10: state = (c == '8') ? EP_M108 : EP_IGNORE; break; case EP_M11: state = (c == '2') ? EP_M112 : EP_IGNORE; break; - case EP_M4: state = (c == '1') ? EP_M41 : EP_IGNORE; break; + case EP_M4: + switch (c) { + case '1' :state = EP_M41; break; + #if ENABLED(FT_MOTION_RESONANCE_TEST) + case '9': state = EP_M49; break; + #endif + default: state = EP_IGNORE; + } + break; + case EP_M41: state = (c == '0') ? EP_M410 : EP_IGNORE; break; + #if ENABLED(FTM_RESONANCE_TEST) + case EP_M49: state = (c == '6') ? EP_M496 : EP_IGNORE; break; + #endif + #if HAS_MEDIA case EP_M5: state = (c == '2') ? EP_M52 : EP_IGNORE; break; case EP_M52: state = (c == '4') ? EP_M524 : EP_IGNORE; break; #endif #if ENABLED(EP_BABYSTEPPING) - case EP_M2: - switch (c) { - case '9': state = EP_M29; break; - default: state = EP_IGNORE; - } - break; - - case EP_M29: - switch (c) { - case '3': state = EP_M293; break; - case '4': state = EP_M294; break; - default: state = EP_IGNORE; - } - break; + case EP_M2: state = (c == '9') ? EP_M29 : EP_IGNORE; break; + case EP_M29: state = (c == '3') ? EP_M293 : (c == '4') ? EP_M294 : EP_IGNORE; break; #endif #if ENABLED(HOST_PROMPT_SUPPORT) @@ -174,7 +182,7 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M87: state = (c == '6') ? EP_M876 : EP_IGNORE; break; case EP_M876: - switch (c) { + switch (uppercase(c)) { case ' ': break; case 'S': state = EP_M876S; break; default: state = EP_IGNORE; break; @@ -203,6 +211,9 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) { case EP_M108: wait_for_user = wait_for_heatup = false; break; case EP_M112: killed_by_M112 = true; break; case EP_M410: quickstop_by_M410 = true; break; + #if ENABLED(FTM_RESONANCE_TEST) + case EP_M496: rt_stop_by_M496 = true; break; + #endif #if ENABLED(EP_BABYSTEPPING) case EP_M293: babystep.ep_babysteps++; break; case EP_M294: babystep.ep_babysteps--; break; diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index 8dacb0581c..17e85a331d 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -43,6 +43,9 @@ public: #if HAS_MEDIA EP_M5, EP_M52, EP_M524, #endif + #if ENABLED(FTM_RESONANCE_TEST) + EP_M49, EP_M496, + #endif #if ENABLED(EP_BABYSTEPPING) EP_M2, EP_M29, EP_M293, EP_M294, #endif @@ -64,6 +67,10 @@ public: static bool killed_by_M112; static bool quickstop_by_M410; + #if ENABLED(FTM_RESONANCE_TEST) + static bool rt_stop_by_M496; + #endif + #if HAS_MEDIA static bool sd_abort_by_M524; #endif diff --git a/Marlin/src/feature/easythreed_ui.cpp b/Marlin/src/feature/easythreed_ui.cpp index 39248bfc7d..bfa51096f5 100644 --- a/Marlin/src/feature/easythreed_ui.cpp +++ b/Marlin/src/feature/easythreed_ui.cpp @@ -78,9 +78,9 @@ void EasythreedUI::blinkLED() { prev_blink_interval_ms = blink_interval_ms; blink_start_ms = ms; } - if (PENDING(ms, blink_start_ms + blink_interval_ms)) + if (PENDING(ms, blink_start_ms, blink_interval_ms)) WRITE(EASYTHREED_LED_PIN, LOW); - else if (PENDING(ms, blink_start_ms + 2 * blink_interval_ms)) + else if (PENDING(ms, blink_start_ms, 2 * blink_interval_ms)) WRITE(EASYTHREED_LED_PIN, HIGH); else blink_start_ms = ms; @@ -107,7 +107,7 @@ void EasythreedUI::loadButton() { break; case FS_PRESS: - if (ELAPSED(millis(), filament_time + BTN_DEBOUNCE_MS)) { // After a short debounce delay... + if (ELAPSED(millis(), filament_time, BTN_DEBOUNCE_MS)) { // After a short debounce delay... if (!READ(BTN_RETRACT) || !READ(BTN_FEED)) { // ...if switch still toggled... thermalManager.setTargetHotend(EXTRUDE_MINTEMP + 10, 0); // Start heating up blink_interval_ms = LED_BLINK_7; // Set the LED to blink fast @@ -131,7 +131,7 @@ void EasythreedUI::loadButton() { break; case FS_PROCEED: { - // Feed or Retract just once. Hard abort all moves and return to idle on swicth release. + // Feed or Retract just once. Hard abort all moves and return to idle on switch release. static bool flag = false; if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop) flag = false; // Restore flag to false @@ -175,21 +175,21 @@ void EasythreedUI::printButton() { break; case KS_PRESS: - if (ELAPSED(ms, key_time + BTN_DEBOUNCE_MS)) // Wait for debounce interval to expire + if (ELAPSED(ms, key_time, BTN_DEBOUNCE_MS)) // Wait for debounce interval to expire key_status = READ(BTN_PRINT) ? KS_IDLE : KS_PROCEED; // Proceed if still pressed break; case KS_PROCEED: if (!READ(BTN_PRINT)) break; // Wait for the button to be released key_status = KS_IDLE; // Ready for the next press - if (PENDING(ms, key_time + 1200 - BTN_DEBOUNCE_MS)) { // Register a press < 1.2 seconds + if (PENDING(ms, key_time, 1200 - BTN_DEBOUNCE_MS)) { // Register a press < 1.2 seconds switch (print_key_flag) { case PF_START: { // The "Print" button starts an SD card print if (printingIsActive()) break; // Already printing? (find another line that checks for 'is planner doing anything else right now?') blink_interval_ms = LED_BLINK_2; // Blink the indicator LED at 1 second intervals print_key_flag = PF_PAUSE; // The "Print" button now pauses the print card.mount(); // Force SD card to mount - now! - if (!card.isMounted) { // Failed to mount? + if (!card.isMounted()) { // Failed to mount? blink_interval_ms = LED_OFF; // Turn off LED print_key_flag = PF_START; return; // Bail out diff --git a/Marlin/src/feature/encoder_i2c.cpp b/Marlin/src/feature/encoder_i2c.cpp index 135178456b..5a47600792 100644 --- a/Marlin/src/feature/encoder_i2c.cpp +++ b/Marlin/src/feature/encoder_i2c.cpp @@ -36,7 +36,7 @@ #include "../module/stepper.h" #include "../gcode/parser.h" -#include "../feature/babystep.h" +#include "babystep.h" #include @@ -168,8 +168,7 @@ void I2CPositionEncoder::update() { float sumP = 0; for (uint8_t i = 0; i < I2CPE_ERR_PRST_ARRAY_SIZE; ++i) sumP += errPrst[i]; const int32_t errorP = int32_t(sumP * RECIPROCAL(I2CPE_ERR_PRST_ARRAY_SIZE)); - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" : CORRECT ERR ", errorP * planner.mm_per_step[encoderAxis], "mm"); + SERIAL_ECHOLN(C(AXIS_CHAR(encoderAxis)), F(" : CORRECT ERR "), errorP * planner.mm_per_step[encoderAxis], F("mm")); babystep.add_steps(encoderAxis, -LROUND(errorP)); errPrstIdx = 0; } @@ -188,8 +187,7 @@ void I2CPositionEncoder::update() { if (ABS(error) > I2CPE_ERR_CNT_THRESH * planner.settings.axis_steps_per_mm[encoderAxis]) { const millis_t ms = millis(); if (ELAPSED(ms, nextErrorCountTime)) { - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" : LARGE ERR ", error, "; diffSum=", diffSum); + SERIAL_ECHOLN(C(AXIS_CHAR(encoderAxis)), F(" : LARGE ERR "), error, F("; diffSum="), diffSum); errorCount++; nextErrorCountTime = ms + I2CPE_ERR_CNT_DEBOUNCE_MS; } @@ -208,8 +206,7 @@ void I2CPositionEncoder::set_homed() { homed = trusted = true; #ifdef I2CPE_DEBUG - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" axis encoder homed, offset of ", zeroOffset, " ticks."); + SERIAL_ECHO(C(AXIS_CHAR(encoderAxis)), F(" axis encoder homed, offset of "), zeroOffset, F(" ticks.\n")); #endif } } @@ -219,8 +216,7 @@ void I2CPositionEncoder::set_unhomed() { homed = trusted = false; #ifdef I2CPE_DEBUG - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" axis encoder unhomed."); + SERIAL_ECHO(C(AXIS_CHAR(encoderAxis)), F(" axis encoder unhomed.\n")); #endif } @@ -247,10 +243,8 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) { diff = actual - target, error = ABS(diff) > 10000 ? 0 : diff; // Huge error is a bad reading - if (report) { - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" axis target=", target, "mm; actual=", actual, "mm; err=", error, "mm"); - } + if (report) + SERIAL_ECHO(C(AXIS_CHAR(encoderAxis)), F(" axis target="), target, F("mm; actual="), actual, F("mm; err="), error, F("mm\n")); return error; } @@ -282,10 +276,8 @@ int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) { errorPrev = error; - if (report) { - SERIAL_CHAR(AXIS_CHAR(encoderAxis)); - SERIAL_ECHOLNPGM(" axis target=", target, "; actual=", encoderCountInStepperTicksScaled, "; err=", error); - } + if (report) + SERIAL_ECHOLN(C(AXIS_CHAR(encoderAxis)), F(" axis target="), target, F("; actual="), encoderCountInStepperTicksScaled, F("; err="), error); if (suppressOutput) { if (report) SERIAL_ECHOLNPGM("!Discontinuity. Suppressing error."); @@ -647,23 +639,22 @@ void I2CPositionEncodersMgr::init() { void I2CPositionEncodersMgr::report_position(const int8_t idx, const bool units, const bool noOffset) { CHECK_IDX(); - if (units) + if (units) { SERIAL_ECHOLN(noOffset ? encoders[idx].mm_from_count(encoders[idx].get_raw_count()) : encoders[idx].get_position_mm()); - else { - if (noOffset) { - const int32_t raw_count = encoders[idx].get_raw_count(); - SERIAL_CHAR(AXIS_CHAR(encoders[idx).get_axis()], ' '); - - for (uint8_t j = 31; j > 0; j--) - SERIAL_ECHO((bool)(0x00000001 & (raw_count >> j))); - - SERIAL_ECHO((bool)(0x00000001 & raw_count)); - SERIAL_CHAR(' '); - SERIAL_ECHOLN(raw_count); - } - else - SERIAL_ECHOLN(encoders[idx].get_position()); + return; } + + if (noOffset) { + const int32_t raw_count = encoders[idx].get_raw_count(); + SERIAL_CHAR(AXIS_CHAR(encoders[idx].get_axis()), ' '); + + for (uint8_t j = 31; j >= 0; j--) + SERIAL_ECHO(TEST32(raw_count, j)); + + SERIAL_ECHOLN(C(' '), raw_count); + } + else + SERIAL_ECHOLN(encoders[idx].get_position()); } void I2CPositionEncodersMgr::change_module_address(const uint8_t oldaddr, const uint8_t newaddr) { @@ -707,7 +698,7 @@ void I2CPositionEncodersMgr::change_module_address(const uint8_t oldaddr, const // and enable it (it will likely have failed initialization on power-up, before the address change). const int8_t idx = idx_from_addr(newaddr); if (idx >= 0 && !encoders[idx].get_active()) { - SERIAL_CHAR(AXIS_CHAR(encoders[idx).get_axis()]); + SERIAL_CHAR(AXIS_CHAR(encoders[idx].get_axis())); SERIAL_ECHOLNPGM(" axis encoder was not detected on printer startup. Trying again."); encoders[idx].set_active(encoders[idx].passes_test(true)); } @@ -810,7 +801,7 @@ void I2CPositionEncodersMgr::M860() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen_test(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) report_position(idx, hasU, hasO); } } @@ -837,7 +828,7 @@ void I2CPositionEncodersMgr::M861() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) report_status(idx); } } @@ -865,7 +856,7 @@ void I2CPositionEncodersMgr::M862() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) test_axis(idx); } } @@ -896,7 +887,7 @@ void I2CPositionEncodersMgr::M863() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) calibrate_steps_mm(idx, iterations); } } @@ -972,7 +963,7 @@ void I2CPositionEncodersMgr::M865() { if (!I2CPE_addr) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) report_module_firmware(encoders[idx].get_address()); } } @@ -1003,12 +994,12 @@ void I2CPositionEncodersMgr::M866() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) { if (hasR) - reset_error_count(idx, AxisEnum(i)); + reset_error_count(idx, (AxisEnum)i); else - report_error_count(idx, AxisEnum(i)); + report_error_count(idx, (AxisEnum)i); } } } @@ -1041,10 +1032,10 @@ void I2CPositionEncodersMgr::M867() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) { const bool ena = onoff == -1 ? !encoders[I2CPE_idx].get_ec_enabled() : !!onoff; - enable_ec(idx, ena, AxisEnum(i)); + enable_ec(idx, ena, (AxisEnum)i); } } } @@ -1077,7 +1068,7 @@ void I2CPositionEncodersMgr::M868() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) { if (newThreshold != -9999) set_ec_threshold(idx, newThreshold, encoders[idx].get_axis()); @@ -1111,7 +1102,7 @@ void I2CPositionEncodersMgr::M869() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { - const uint8_t idx = idx_from_axis(AxisEnum(i)); + const uint8_t idx = idx_from_axis((AxisEnum)i); if ((int8_t)idx >= 0) report_error(idx); } } diff --git a/Marlin/src/feature/encoder_i2c.h b/Marlin/src/feature/encoder_i2c.h index 861a8e52d4..e8485a6b75 100644 --- a/Marlin/src/feature/encoder_i2c.h +++ b/Marlin/src/feature/encoder_i2c.h @@ -188,7 +188,7 @@ class I2CPositionEncoder { FORCE_INLINE void set_ec_method(const byte method) { ecMethod = method; } FORCE_INLINE float get_ec_threshold() { return ecThreshold; } - FORCE_INLINE void set_ec_threshold(const_float_t newThreshold) { ecThreshold = newThreshold; } + FORCE_INLINE void set_ec_threshold(const float newThreshold) { ecThreshold = newThreshold; } FORCE_INLINE int get_encoder_ticks_mm() { switch (type) { diff --git a/Marlin/src/feature/ethernet.cpp b/Marlin/src/feature/ethernet.cpp index c5bfa932cb..cdf176f832 100644 --- a/Marlin/src/feature/ethernet.cpp +++ b/Marlin/src/feature/ethernet.cpp @@ -141,7 +141,7 @@ void MarlinEthernet::check() { case CONNECTING: telnetClient.println("Marlin " SHORT_BUILD_VERSION); - #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR) + #ifdef STRING_DISTRIBUTION_DATE telnetClient.println( " Last Updated: " STRING_DISTRIBUTION_DATE " | Author: " STRING_CONFIG_H_AUTHOR @@ -172,4 +172,46 @@ void MarlinEthernet::check() { } } +void say_ethernet() { SERIAL_ECHOPGM(" Ethernet "); } + +void MarlinEthernet::ETH0_report(const bool forReplay/*=true*/) { + say_ethernet(); + SERIAL_ECHO_TERNARY(ethernet.hardware_enabled, "port ", "en", "dis", "abled.\n"); + if (ethernet.hardware_enabled) { + say_ethernet(); + SERIAL_ECHO_TERNARY(ethernet.have_telnet_client, "client ", "en", "dis", "abled.\n"); + } + else + SERIAL_ECHOLNPGM("Send 'M552 S1' to enable."); +} + +void MarlinEthernet::MAC_report(const bool forReplay/*=true*/) { + if (!forReplay) SERIAL_ECHO_START(); + SERIAL_ECHOPGM("MAC: "); + if (ethernet.hardware_enabled) { + uint8_t mac[6]; + Ethernet.MACAddress(mac); + for (uint8_t i = 0; i < 6; ++i) { + if (mac[i] < 0x10) SERIAL_CHAR('0'); + SERIAL_PRINT(mac[i], PrintBase::Hex); + if (i < 5) SERIAL_CHAR(':'); + } + } + else + SERIAL_ECHOPGM("Disabled"); + SERIAL_EOL(); +} + +// Display current values when the link is active, +// otherwise show the stored values +void MarlinEthernet::ip_report(const uint16_t cmd, FSTR_P const post, const IPAddress &ipo, const bool forReplay/*=true*/) { + if (!forReplay) SERIAL_ECHO_START(); + SERIAL_ECHO(F(" M"), cmd, C(' ')); + for (uint8_t i = 0; i < 4; ++i) { + SERIAL_ECHO(ipo[i]); + if (i < 3) SERIAL_CHAR('.'); + } + SERIAL_ECHOLN(F(" ; "), post); +} + #endif // HAS_ETHERNET diff --git a/Marlin/src/feature/ethernet.h b/Marlin/src/feature/ethernet.h index 70a58efce7..7aa364fc3c 100644 --- a/Marlin/src/feature/ethernet.h +++ b/Marlin/src/feature/ethernet.h @@ -25,6 +25,8 @@ #include #endif +#include "../HAL/shared/Marduino.h" + // Teensy 4.1 uses internal MAC Address class MarlinEthernet { @@ -34,6 +36,10 @@ class MarlinEthernet { static EthernetClient telnetClient; static void init(); static void check(); + + static void ETH0_report(const bool forReplay=true); + static void MAC_report(const bool forReplay=true); + static void ip_report(const uint16_t cmd, FSTR_P const post, const IPAddress &ipo, const bool forReplay=true); }; extern MarlinEthernet ethernet; diff --git a/Marlin/src/feature/fancheck.h b/Marlin/src/feature/fancheck.h index b13a34fb19..3c295b3020 100644 --- a/Marlin/src/feature/fancheck.h +++ b/Marlin/src/feature/fancheck.h @@ -67,7 +67,7 @@ class FanCheck { static void compute_speed(uint16_t elapsedTime); static void print_fan_states(); #if HAS_PWMFANCHECK - static void toggle_measuring() { measuring = !measuring; } + static void toggle_measuring() { FLIP(measuring); } static bool is_measuring() { return measuring; } #endif diff --git a/Marlin/src/feature/filwidth.cpp b/Marlin/src/feature/filwidth.cpp index 3befd7752a..1142423cf7 100644 --- a/Marlin/src/feature/filwidth.cpp +++ b/Marlin/src/feature/filwidth.cpp @@ -28,7 +28,7 @@ FilamentWidthSensor filwidth; -bool FilamentWidthSensor::enabled; // = false; // (M405-M406) Filament Width Sensor ON/OFF. +bool FilamentWidthSensor::enabled; // = false // (M405-M406) Filament Width Sensor ON/OFF. uint32_t FilamentWidthSensor::accum; // = 0 // ADC accumulator uint16_t FilamentWidthSensor::raw; // = 0 // Measured filament diameter - one extruder only float FilamentWidthSensor::nominal_mm = DEFAULT_NOMINAL_FILAMENT_DIA, // (M104) Nominal filament width diff --git a/Marlin/src/feature/filwidth.h b/Marlin/src/feature/filwidth.h index ab50fe0af3..a16240936a 100644 --- a/Marlin/src/feature/filwidth.h +++ b/Marlin/src/feature/filwidth.h @@ -67,7 +67,7 @@ public: } // Convert raw measurement to mm - static float raw_to_mm(const uint16_t v) { return v * (float(ADC_VREF_MV) / 1000.0f) * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); } + static float raw_to_mm(const uint16_t v) { return v * (float(ADC_VREF_MV) * 0.001f) * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); } static float raw_to_mm() { return raw_to_mm(raw); } // A scaled reading is ready @@ -78,7 +78,7 @@ public: static void update_measured_mm() { measured_mm = raw_to_mm(); } // Update ring buffer used to delay filament measurements - static void advance_e(const_float_t e_move) { + static void advance_e(const float e_move) { // Increment counters with the E distance e_count += e_move; diff --git a/Marlin/src/feature/fwretract.cpp b/Marlin/src/feature/fwretract.cpp index aee775bfff..5ea20401ea 100644 --- a/Marlin/src/feature/fwretract.cpp +++ b/Marlin/src/feature/fwretract.cpp @@ -137,7 +137,7 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) // Retract by moving from a faux E position back to the current E position current_retract[active_extruder] = base_retract; prepare_internal_move_to_destination( // set current from destination - settings.retract_feedrate_mm_s * TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS)) + MUL_TERN(RETRACT_SYNC_MIXING, settings.retract_feedrate_mm_s, MIXING_STEPPERS) ); // Is a Z hop set, and has the hop not yet been done? @@ -164,10 +164,8 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) current_retract[active_extruder] = 0; // Recover E, set_current_to_destination - prepare_internal_move_to_destination( - (swapping ? settings.swap_retract_recover_feedrate_mm_s : settings.retract_recover_feedrate_mm_s) - * TERN1(RETRACT_SYNC_MIXING, (MIXING_STEPPERS)) - ); + const feedRate_t fr_mm_s = swapping ? settings.swap_retract_recover_feedrate_mm_s : settings.retract_recover_feedrate_mm_s; + prepare_internal_move_to_destination(MUL_TERN(RETRACT_SYNC_MIXING, fr_mm_s, MIXING_STEPPERS)); } TERN_(RETRACT_SYNC_MIXING, mixer.T(old_mixing_tool)); // Restore original mixing tool @@ -245,6 +243,7 @@ void FWRetract::M208_report() { " M208 S", LINEAR_UNIT(settings.retract_recover_extra) , " W", LINEAR_UNIT(settings.swap_retract_recover_extra) , " F", LINEAR_UNIT(MMS_TO_MMM(settings.retract_recover_feedrate_mm_s)) + , " R", LINEAR_UNIT(MMS_TO_MMM(settings.swap_retract_recover_feedrate_mm_s)) ); } diff --git a/Marlin/src/feature/hotend_idle.cpp b/Marlin/src/feature/hotend_idle.cpp index 050898e3b5..eb16781fdd 100644 --- a/Marlin/src/feature/hotend_idle.cpp +++ b/Marlin/src/feature/hotend_idle.cpp @@ -40,7 +40,7 @@ HotendIdleProtection hotend_idle; millis_t HotendIdleProtection::next_protect_ms = 0; -hotend_idle_settings_t HotendIdleProtection::cfg; // Initialized by settings.load() +hotend_idle_settings_t HotendIdleProtection::cfg; // Initialized by settings.load void HotendIdleProtection::check_hotends(const millis_t &ms) { const bool busy = (TERN0(HAS_RESUME_CONTINUE, wait_for_user) || planner.has_blocks_queued()); diff --git a/Marlin/src/feature/joystick.cpp b/Marlin/src/feature/joystick.cpp index acab5d7437..29addfcf1e 100644 --- a/Marlin/src/feature/joystick.cpp +++ b/Marlin/src/feature/joystick.cpp @@ -123,7 +123,7 @@ Joystick joystick; void Joystick::inject_jog_moves() { // Recursion barrier - static bool injecting_now; // = false; + static bool injecting_now; // = false if (injecting_now) return; #if ENABLED(NO_MOTION_BEFORE_HOMING) diff --git a/Marlin/src/feature/leds/blinkm.cpp b/Marlin/src/feature/leds/blinkm.cpp index 868eb4b3d9..b040c8e76f 100644 --- a/Marlin/src/feature/leds/blinkm.cpp +++ b/Marlin/src/feature/leds/blinkm.cpp @@ -32,7 +32,7 @@ #include "leds.h" #include -void blinkm_set_led_color(const LEDColor &color) { +void blinkm_set_led_color(const LED1Color_t &color) { Wire.begin(); Wire.beginTransmission(I2C_ADDRESS(0x09)); Wire.write('o'); //to disable ongoing script, only needs to be used once diff --git a/Marlin/src/feature/leds/blinkm.h b/Marlin/src/feature/leds/blinkm.h index 29a9e78412..d3c528acbf 100644 --- a/Marlin/src/feature/leds/blinkm.h +++ b/Marlin/src/feature/leds/blinkm.h @@ -25,7 +25,6 @@ * blinkm.h - Control a BlinkM over i2c */ -struct LEDColor; -typedef LEDColor LEDColor; +struct LED1Color_t; -void blinkm_set_led_color(const LEDColor &color); +void blinkm_set_led_color(const LED1Color_t &color); diff --git a/Marlin/src/feature/leds/leds.cpp b/Marlin/src/feature/leds/leds.cpp index ac7f181516..55a10fcbbd 100644 --- a/Marlin/src/feature/leds/leds.cpp +++ b/Marlin/src/feature/leds/leds.cpp @@ -31,19 +31,19 @@ #include "leds.h" #if ANY(CASE_LIGHT_USE_RGB_LED, CASE_LIGHT_USE_NEOPIXEL) - #include "../../feature/caselight.h" + #include "../caselight.h" #endif #if ENABLED(LED_COLOR_PRESETS) - const LEDColor LEDLights::defaultLEDColor = LEDColor( + const LED1Color_t LEDLights::defaultLEDColor { LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE OPTARG(HAS_WHITE_LED, LED_USER_PRESET_WHITE) OPTARG(NEOPIXEL_LED, LED_USER_PRESET_BRIGHTNESS) - ); + }; #endif #if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED) - LEDColor LEDLights::color; + LED1Color_t LEDLights::color; bool LEDLights::lights_on; #endif @@ -83,7 +83,7 @@ void LEDLights::setup() { if (i == 1 && PWM_PIN(RGB_LED_G_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_G_PIN), led_pwm); else WRITE(RGB_LED_G_PIN, b < 100 ? HIGH : LOW); if (i == 2 && PWM_PIN(RGB_LED_B_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_B_PIN), led_pwm); else WRITE(RGB_LED_B_PIN, b < 100 ? HIGH : LOW); #if ENABLED(RGBW_LED) - if (i == 3){ + if (i == 3) { if (PWM_PIN(RGB_LED_W_PIN)) hal.set_pwm_duty(pin_t(RGB_LED_W_PIN), led_pwm); else WRITE(RGB_LED_W_PIN, b < 100 ? HIGH : LOW); delay(RGB_STARTUP_TEST_INNER_MS);//More slowing for ending @@ -101,7 +101,7 @@ void LEDLights::setup() { constexpr int8_t led_pin_count = TERN(HAS_WHITE_LED, 4, 3); // Startup animation - LEDColor curColor = LEDColorOff(); + LED1Color_t curColor = LEDColorOff(); PCA9632_set_led_color(curColor); // blackout delay(200); @@ -156,15 +156,15 @@ void LEDLights::setup() { TERN_(LED_USER_PRESET_STARTUP, set_default()); } -void LEDLights::set_color(const LEDColor &incol +void LEDLights::set_color(const LED1Color_t &incol OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence/*=false*/) ) { #if ENABLED(NEOPIXEL_LED) const uint32_t neocolor = LEDColorWhite() == incol - ? neo.Color(NEO_WHITE) - : neo.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED, incol.w)); + ? neo.White() + : neo.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_NEOPIXEL_1, incol.w)); #if ENABLED(NEOPIXEL_IS_SEQUENTIAL) static uint16_t nextLed = 0; @@ -239,7 +239,7 @@ void LEDLights::set_color(const LEDColor &incol void LEDLights::toggle() { if (lights_on) set_off(); else update(); } #endif -#if LED_POWEROFF_TIMEOUT > 0 +#if HAS_LED_POWEROFF_TIMEOUT millis_t LEDLights::led_off_time; // = 0 @@ -258,7 +258,7 @@ void LEDLights::set_color(const LEDColor &incol #if ENABLED(NEOPIXEL2_SEPARATE) #if ENABLED(NEO2_COLOR_PRESETS) - const LEDColor LEDLights2::defaultLEDColor = LEDColor( + const LED2Color_t LEDLights2::defaultLEDColor2 = LED2Color_t( NEO2_USER_PRESET_RED, NEO2_USER_PRESET_GREEN, NEO2_USER_PRESET_BLUE OPTARG(HAS_WHITE_LED2, NEO2_USER_PRESET_WHITE) OPTARG(NEOPIXEL_LED, NEO2_USER_PRESET_BRIGHTNESS) @@ -266,7 +266,7 @@ void LEDLights::set_color(const LEDColor &incol #endif #if ENABLED(LED_CONTROL_MENU) - LEDColor LEDLights2::color; + LED2Color_t LEDLights2::color; bool LEDLights2::lights_on; #endif @@ -277,10 +277,10 @@ void LEDLights::set_color(const LEDColor &incol TERN_(NEO2_USER_PRESET_STARTUP, set_default()); } - void LEDLights2::set_color(const LEDColor &incol) { - const uint32_t neocolor = LEDColorWhite() == incol - ? neo2.Color(NEO2_WHITE) - : neo2.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_LED2, incol.w)); + void LEDLights2::set_color(const LED2Color_t &incol) { + const uint32_t neocolor = LEDColorWhite2() == incol + ? neo2.White() + : neo2.Color(incol.r, incol.g, incol.b OPTARG(HAS_WHITE_NEOPIXEL_2, incol.w)); neo2.set_brightness(incol.i); neo2.set_color(neocolor); diff --git a/Marlin/src/feature/leds/leds.h b/Marlin/src/feature/leds/leds.h index 7a31ca685d..3ba3a31cdb 100644 --- a/Marlin/src/feature/leds/leds.h +++ b/Marlin/src/feature/leds/leds.h @@ -29,11 +29,6 @@ #include -// A white component can be passed -#if ANY(RGBW_LED, PCA9632_RGBW) - #define HAS_WHITE_LED 1 -#endif - #if ENABLED(NEOPIXEL_LED) #define _NEOPIXEL_INCLUDE_ #include "neopixel.h" @@ -52,75 +47,142 @@ #include "pca9632.h" #endif +#if ANY(RGBW_LED, PCA9632_RGBW, HAS_WHITE_NEOPIXEL_1) + #define HAS_WHITE_LED 1 +#endif +#if HAS_WHITE_NEOPIXEL_2 + #define HAS_WHITE_LED2 1 +#endif + /** * LEDcolor type for use with leds.set_color */ -typedef struct LEDColor { - uint8_t r, g, b - OPTARG(HAS_WHITE_LED, w) - OPTARG(NEOPIXEL_LED, i) - ; +struct LED1Color_t { + // Basic RGB color components + uint8_t r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i); + // Default constructor - white color + LED1Color_t() : r(255), g(255), b(255) OPTARG(HAS_WHITE_LED, w(255)) OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)){} - LEDColor() : r(255), g(255), b(255) - OPTARG(HAS_WHITE_LED, w(255)) - OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)) - {} + // Copy constructor + LED1Color_t(const LED1Color_t&) = default; - LEDColor(const LEDColor&) = default; - - LEDColor(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)) + // Constructor with individual components + LED1Color_t(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)) : r(r), g(g), b(b) OPTARG(HAS_WHITE_LED, w(w)) OPTARG(NEOPIXEL_LED, i(i)) {} - LEDColor(const uint8_t (&rgbw)[4]) : r(rgbw[0]), g(rgbw[1]), b(rgbw[2]) - OPTARG(HAS_WHITE_LED, w(rgbw[3])) - OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)) - {} + // Constructor from array + LED1Color_t(const uint8_t (&rgbw)[4]) : r(rgbw[0]), g(rgbw[1]), b(rgbw[2]) + OPTARG(HAS_WHITE_LED, w(rgbw[3])) OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)){} - LEDColor& operator=(const uint8_t (&rgbw)[4]) { + // Array assignment operator + LED1Color_t& operator=(const uint8_t (&rgbw)[4]) { r = rgbw[0]; g = rgbw[1]; b = rgbw[2]; TERN_(HAS_WHITE_LED, w = rgbw[3]); return *this; } - bool operator==(const LEDColor &right) { - if (this == &right) return true; - return 0 == memcmp(this, &right, sizeof(LEDColor)); + // Comparison operators + bool operator==(const LED1Color_t &right) { + return (this == &right) || (0 == memcmp(this, &right, sizeof(LED1Color_t))); } - bool operator!=(const LEDColor &right) { return !operator==(right); } + bool operator!=(const LED1Color_t &right) { + return !operator==(right); + } + // Check if LED is effectively off bool is_off() const { return 3 > r + g + b + TERN0(HAS_WHITE_LED, w); } -} LEDColor; +}; + + +struct LED2Color_t { + // Basic RGB color components + uint8_t r, g, b OPTARG(HAS_WHITE_LED2, w) OPTARG(NEOPIXEL_LED, i); + // Default constructor - white color + LED2Color_t() : r(255), g(255), b(255) OPTARG(HAS_WHITE_LED2, w(255)) OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)){} + + // Copy constructor + LED2Color_t(const LED2Color_t&) = default; + + // Constructor with individual components + LED2Color_t(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS)) + : r(r), g(g), b(b) OPTARG(HAS_WHITE_LED2, w(w)) OPTARG(NEOPIXEL_LED, i(i)) {} + + // Constructor from array + LED2Color_t(const uint8_t (&rgbw)[4]) : r(rgbw[0]), g(rgbw[1]), b(rgbw[2]) + OPTARG(HAS_WHITE_LED2, w(rgbw[3])) OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)){} + + // Array assignment operator + LED2Color_t& operator=(const uint8_t (&rgbw)[4]) { + r = rgbw[0]; g = rgbw[1]; b = rgbw[2]; + TERN_(HAS_WHITE_LED2, w = rgbw[3]); + return *this; + } + + // Comparison operators + bool operator==(const LED2Color_t &right) { + return (this == &right) || (0 == memcmp(this, &right, sizeof(LED1Color_t))); + } + + bool operator!=(const LED2Color_t &right) { + return !operator==(right); + } + + // Check if LED is effectively off + bool is_off() const { + return 3 > r + g + b + TERN0(HAS_WHITE_LED2, w); + } +}; /** * Color presets */ -#define LEDColorOff() LEDColor( 0, 0, 0) -#define LEDColorRed() LEDColor(255, 0, 0) +#define LEDColorOff() LED1Color_t( 0, 0, 0) +#define LEDColorRed() LED1Color_t(255, 0, 0) #if ENABLED(LED_COLORS_REDUCE_GREEN) - #define LEDColorOrange() LEDColor(255, 25, 0) - #define LEDColorYellow() LEDColor(255, 75, 0) + #define LEDColorOrange() LED1Color_t(255, 25, 0) + #define LEDColorYellow() LED1Color_t(255, 75, 0) #else - #define LEDColorOrange() LEDColor(255, 80, 0) - #define LEDColorYellow() LEDColor(255, 255, 0) + #define LEDColorOrange() LED1Color_t(255, 80, 0) + #define LEDColorYellow() LED1Color_t(255, 255, 0) #endif -#define LEDColorGreen() LEDColor( 0, 255, 0) -#define LEDColorBlue() LEDColor( 0, 0, 255) -#define LEDColorIndigo() LEDColor( 0, 255, 255) -#define LEDColorViolet() LEDColor(255, 0, 255) +#define LEDColorGreen() LED1Color_t( 0, 255, 0) +#define LEDColorBlue() LED1Color_t( 0, 0, 255) +#define LEDColorIndigo() LED1Color_t( 0, 255, 255) +#define LEDColorViolet() LED1Color_t(255, 0, 255) #if HAS_WHITE_LED && DISABLED(RGB_LED) - #define LEDColorWhite() LEDColor( 0, 0, 0, 255) + #define LEDColorWhite() LED1Color_t( 0, 0, 0, 255) #else - #define LEDColorWhite() LEDColor(255, 255, 255) + #define LEDColorWhite() LED1Color_t(255, 255, 255) #endif +#define LEDColorOff2() LED2Color_t( 0, 0, 0) +#define LEDColorRed2() LED2Color_t(255, 0, 0) +#if ENABLED(LED_COLORS_REDUCE_GREEN) + #define LEDColorOrange2() LED2Color_t(255, 25, 0) + #define LEDColorYellow2() LED2Color_t(255, 75, 0) +#else + #define LEDColorOrange2() LED2Color_t(255, 80, 0) + #define LEDColorYellow2() LED2Color_t(255, 255, 0) +#endif +#define LEDColorGreen2() LED2Color_t( 0, 255, 0) +#define LEDColorBlue2() LED2Color_t( 0, 0, 255) +#define LEDColorIndigo2() LED2Color_t( 0, 255, 255) +#define LEDColorViolet2() LED2Color_t(255, 0, 255) +#if HAS_WHITE_LED2 && DISABLED(RGB_LED) + #define LEDColorWhite2() LED2Color_t( 0, 0, 0, 255) +#else + #define LEDColorWhite2() LED2Color_t(255, 255, 255) +#endif + + class LEDLights { public: #if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED) - static LEDColor color; // last non-off color + static LED1Color_t color; // last non-off color static bool lights_on; // the last set color was "on" #else static constexpr bool lights_on = true; @@ -130,7 +192,7 @@ public: static void setup(); // init() - static void set_color(const LEDColor &color + static void set_color(const LED1Color_t &color OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false) ); @@ -139,7 +201,7 @@ public: OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS) OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false) ) { - set_color(LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence)); + set_color(LED1Color_t(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence)); } static void set_off() { set_color(LEDColorOff()); } @@ -147,7 +209,7 @@ public: static void set_white() { set_color(LEDColorWhite()); } #if ENABLED(LED_COLOR_PRESETS) - static const LEDColor defaultLEDColor; + static const LED1Color_t defaultLEDColor; static void set_default() { set_color(defaultLEDColor); } static void set_red() { set_color(LEDColorRed()); } static void set_orange() { set_color(LEDColorOrange()); } @@ -158,17 +220,17 @@ public: #endif #if ENABLED(PRINTER_EVENT_LEDS) - static LEDColor get_color() { return lights_on ? color : LEDColorOff(); } + static LED1Color_t get_color() { return lights_on ? color : LEDColorOff(); } #endif #if ENABLED(LED_CONTROL_MENU) static void toggle(); // swap "off" with color #endif - #if ANY(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED) || LED_POWEROFF_TIMEOUT > 0 + #if ANY(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED, HAS_LED_POWEROFF_TIMEOUT) static void update() { set_color(color); } #endif - #if LED_POWEROFF_TIMEOUT > 0 + #if HAS_LED_POWEROFF_TIMEOUT private: static millis_t led_off_time; public: @@ -190,35 +252,35 @@ extern LEDLights leds; static void setup(); // init() - static void set_color(const LEDColor &color); + static void set_color(const LED2Color_t &color); static void set_color(uint8_t r, uint8_t g, uint8_t b - OPTARG(HAS_WHITE_LED, uint8_t w=0) + OPTARG(HAS_WHITE_LED2, uint8_t w=0) OPTARG(NEOPIXEL_LED, uint8_t i=NEOPIXEL_BRIGHTNESS) ) { - set_color(LEDColor(r, g, b - OPTARG(HAS_WHITE_LED, w) + set_color(LED2Color_t(r, g, b + OPTARG(HAS_WHITE_LED2, w) OPTARG(NEOPIXEL_LED, i) )); } - static void set_off() { set_color(LEDColorOff()); } - static void set_green() { set_color(LEDColorGreen()); } - static void set_white() { set_color(LEDColorWhite()); } + static void set_off() { set_color(LEDColorOff2()); } + static void set_green() { set_color(LEDColorGreen2()); } + static void set_white() { set_color(LEDColorWhite2()); } #if ENABLED(NEO2_COLOR_PRESETS) - static const LEDColor defaultLEDColor; - static void set_default() { set_color(defaultLEDColor); } - static void set_red() { set_color(LEDColorRed()); } - static void set_orange() { set_color(LEDColorOrange()); } - static void set_yellow() { set_color(LEDColorYellow()); } - static void set_blue() { set_color(LEDColorBlue()); } - static void set_indigo() { set_color(LEDColorIndigo()); } - static void set_violet() { set_color(LEDColorViolet()); } + static const LED2Color_t defaultLEDColor2; + static void set_default() { set_color(defaultLEDColor2); } + static void set_red() { set_color(LEDColorRed2()); } + static void set_orange() { set_color(LEDColorOrange2()); } + static void set_yellow() { set_color(LEDColorYellow2()); } + static void set_blue() { set_color(LEDColorBlue2()); } + static void set_indigo() { set_color(LEDColorIndigo2()); } + static void set_violet() { set_color(LEDColorViolet2()); } #endif #if ENABLED(NEOPIXEL2_SEPARATE) - static LEDColor color; // last non-off color + static LED2Color_t color; // last non-off color static bool lights_on; // the last set color was "on" static void toggle(); // swap "off" with color static void update() { set_color(color); } diff --git a/Marlin/src/feature/leds/neopixel.cpp b/Marlin/src/feature/leds/neopixel.cpp index 1b0772c2f9..8165c7715c 100644 --- a/Marlin/src/feature/leds/neopixel.cpp +++ b/Marlin/src/feature/leds/neopixel.cpp @@ -103,7 +103,7 @@ void Marlin_NeoPixel::init() { safe_delay(500); set_color_startup(adaneo1.Color(0, 0, 255, 0)); // blue safe_delay(500); - #if HAS_WHITE_LED + #if HAS_WHITE_NEOPIXEL_1 set_color_startup(adaneo1.Color(0, 0, 0, 255)); // white safe_delay(500); #endif @@ -158,7 +158,7 @@ void Marlin_NeoPixel::init() { safe_delay(500); set_color_startup(adaneo.Color(0, 0, 255, 0)); // blue safe_delay(500); - #if HAS_WHITE_LED2 + #if HAS_WHITE_NEOPIXEL_2 set_color_startup(adaneo.Color(0, 0, 0, 255)); // white safe_delay(500); #endif diff --git a/Marlin/src/feature/leds/neopixel.h b/Marlin/src/feature/leds/neopixel.h index 6cc8b6157e..f420a25b83 100644 --- a/Marlin/src/feature/leds/neopixel.h +++ b/Marlin/src/feature/leds/neopixel.h @@ -42,19 +42,17 @@ // Defines // ------------------------ -#define _NEO_IS_RGB(N) (N == NEO_RGB || N == NEO_RBG || N == NEO_GRB || N == NEO_GBR || N == NEO_BRG || N == NEO_BGR) +#define _NEO_IS_RGBW(N) ((N) & 0x30) != (((N) >> 2) & 0x30) -#if !_NEO_IS_RGB(NEOPIXEL_TYPE) - #define HAS_WHITE_LED 1 +#if _NEO_IS_RGBW(NEOPIXEL_TYPE) + #define HAS_WHITE_NEOPIXEL_1 1 #endif -#if HAS_WHITE_LED - #define NEO_WHITE 0, 0, 0, 255 -#else - #define NEO_WHITE 255, 255, 255 -#endif - -#if defined(NEOPIXEL2_TYPE) && NEOPIXEL2_TYPE != NEOPIXEL_TYPE && DISABLED(NEOPIXEL2_SEPARATE) +#if ENABLED(NEOPIXEL2_SEPARATE) + #if _NEO_IS_RGBW(NEOPIXEL2_TYPE) + #define HAS_WHITE_NEOPIXEL_2 1 + #endif +#elif defined(NEOPIXEL2_TYPE) && NEOPIXEL2_TYPE != NEOPIXEL_TYPE #define MULTIPLE_NEOPIXEL_TYPES 1 #endif @@ -62,6 +60,8 @@ #define CONJOINED_NEOPIXEL 1 #endif +#undef _NEO_IS_RGBW + // ------------------------ // Types // ------------------------ @@ -130,7 +130,7 @@ public: } // Accessors - static uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); } + static uint16_t pixels() { return MUL_TERN(NEOPIXEL2_INSERIES, adaneo1.numPixels(), 2); } static uint32_t pixel_color(const uint16_t n) { #if ENABLED(NEOPIXEL2_INSERIES) @@ -141,8 +141,17 @@ public: static uint8_t brightness() { return adaneo1.getBrightness(); } - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w)) { - return adaneo1.Color(r, g, b OPTARG(HAS_WHITE_LED, w)); + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_NEOPIXEL_1, uint8_t w=0)) { + return adaneo1.Color(r, g, b OPTARG(HAS_WHITE_NEOPIXEL_1, w)); + } + static uint32_t White() { + return Color( + #if HAS_WHITE_NEOPIXEL_1 + 0, 0, 0, 255 + #else + 255, 255, 255 + #endif + ); } }; @@ -151,15 +160,6 @@ extern Marlin_NeoPixel neo; // Neo pixel channel 2 #if ENABLED(NEOPIXEL2_SEPARATE) - #if _NEO_IS_RGB(NEOPIXEL2_TYPE) - #define NEOPIXEL2_IS_RGB 1 - #define NEO2_WHITE 255, 255, 255 - #else - #define NEOPIXEL2_IS_RGBW 1 - #define HAS_WHITE_LED2 1 // A white component can be passed for NEOPIXEL2 - #define NEO2_WHITE 0, 0, 0, 255 - #endif - class Marlin_NeoPixel2 { private: static Adafruit_NeoPixel adaneo; @@ -184,13 +184,20 @@ extern Marlin_NeoPixel neo; static uint16_t pixels() { return adaneo.numPixels();} static uint32_t pixel_color(const uint16_t n) { return adaneo.getPixelColor(n); } static uint8_t brightness() { return adaneo.getBrightness(); } - static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w)) { - return adaneo.Color(r, g, b OPTARG(HAS_WHITE_LED2, w)); + static uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_NEOPIXEL_2, uint8_t w=0)) { + return adaneo.Color(r, g, b OPTARG(HAS_WHITE_NEOPIXEL_2, w)); + } + static uint32_t White() { + return Color( + #if HAS_WHITE_NEOPIXEL_2 + 0, 0, 0, 255 + #else + 255, 255, 255 + #endif + ); } }; extern Marlin_NeoPixel2 neo2; #endif // NEOPIXEL2_SEPARATE - -#undef _NEO_IS_RGB diff --git a/Marlin/src/feature/leds/pca9632.cpp b/Marlin/src/feature/leds/pca9632.cpp index 07c379a815..d8fba380a4 100644 --- a/Marlin/src/feature/leds/pca9632.cpp +++ b/Marlin/src/feature/leds/pca9632.cpp @@ -68,7 +68,7 @@ #ifndef PCA9632_BLU #define PCA9632_BLU 0x04 #endif -#if HAS_WHITE_LED && !defined(PCA9632_WHT) +#if ENABLED(PCA9632_RGBW) && !defined(PCA9632_WHT) #define PCA9632_WHT 0x06 #endif @@ -124,7 +124,7 @@ static void PCA9632_WriteAllRegisters(const byte addr, const byte regadd, const } #endif -void PCA9632_set_led_color(const LEDColor &color) { +void PCA9632_set_led_color(const LED1Color_t &color) { Wire.begin(); if (!PCA_init) { PCA_init = 1; @@ -135,10 +135,7 @@ void PCA9632_set_led_color(const LEDColor &color) { const byte LEDOUT = (color.r ? LED_PWM << PCA9632_RED : 0) | (color.g ? LED_PWM << PCA9632_GRN : 0) | (color.b ? LED_PWM << PCA9632_BLU : 0) - #if ENABLED(PCA9632_RGBW) - | (color.w ? LED_PWM << PCA9632_WHT : 0) - #endif - ; + | (TERN0(PCA9632_RGBW, color.w ? LED_PWM << PCA9632_WHT : 0)); PCA9632_WriteAllRegisters(PCA9632_ADDRESS,PCA9632_PWM0, color.r, color.g, color.b OPTARG(PCA9632_RGBW, color.w) @@ -148,7 +145,7 @@ void PCA9632_set_led_color(const LEDColor &color) { #if ENABLED(PCA9632_BUZZER) - void PCA9632_buzz(const long, const uint16_t=0) { + void PCA9632_buzz(const long, const uint16_t/*=0*/) { uint8_t data[] = PCA9632_BUZZER_DATA; Wire.beginTransmission(I2C_ADDRESS(PCA9632_ADDRESS)); Wire.write(data, sizeof(data)); diff --git a/Marlin/src/feature/leds/pca9632.h b/Marlin/src/feature/leds/pca9632.h index adef0200af..442166c245 100644 --- a/Marlin/src/feature/leds/pca9632.h +++ b/Marlin/src/feature/leds/pca9632.h @@ -26,10 +26,9 @@ * Written by Robert Mendon Feb 2017. */ -struct LEDColor; -typedef LEDColor LEDColor; +struct LED1Color_t; -void PCA9632_set_led_color(const LEDColor &color); +void PCA9632_set_led_color(const LED1Color_t &color); #if ENABLED(PCA9632_BUZZER) #include diff --git a/Marlin/src/feature/leds/printer_event_leds.cpp b/Marlin/src/feature/leds/printer_event_leds.cpp index e6407a6320..3cbce3da8c 100644 --- a/Marlin/src/feature/leds/printer_event_leds.cpp +++ b/Marlin/src/feature/leds/printer_event_leds.cpp @@ -47,7 +47,7 @@ PrinterEventLEDs printerEventLEDs; inline void pel_set_rgb(const uint8_t r, const uint8_t g, const uint8_t b OPTARG(HAS_WHITE_LED, const uint8_t w=0)) { leds.set_color( - LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, neo.brightness())) + LED1Color_t(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, neo.brightness())) OPTARG(NEOPIXEL_IS_SEQUENTIAL, true) ); } diff --git a/Marlin/src/feature/leds/printer_event_leds.h b/Marlin/src/feature/leds/printer_event_leds.h index 856826b969..5fb08a547e 100644 --- a/Marlin/src/feature/leds/printer_event_leds.h +++ b/Marlin/src/feature/leds/printer_event_leds.h @@ -40,23 +40,23 @@ private: public: #if HAS_TEMP_HOTEND - static LEDColor onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); } + static LED1Color_t onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); } static void onHotendHeating(const celsius_t start, const celsius_t current, const celsius_t target); #endif #if HAS_HEATED_BED - static LEDColor onBedHeatingStart() { old_intensity = 127; return leds.get_color(); } + static LED1Color_t onBedHeatingStart() { old_intensity = 127; return leds.get_color(); } static void onBedHeating(const celsius_t start, const celsius_t current, const celsius_t target); #endif #if HAS_HEATED_CHAMBER - static LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); } + static LED1Color_t onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); } static void onChamberHeating(const celsius_t start, const celsius_t current, const celsius_t target); #endif #if HAS_TEMP_HOTEND || HAS_HEATED_BED || HAS_HEATED_CHAMBER static void onHeatingDone() { leds.set_white(); } - static void onPIDTuningDone(LEDColor c) { leds.set_color(c); } + static void onPIDTuningDone(LED1Color_t c) { leds.set_color(c); } #endif #if HAS_MEDIA diff --git a/Marlin/src/feature/max7219.cpp b/Marlin/src/feature/max7219.cpp index 64960f1dfe..81f588008d 100644 --- a/Marlin/src/feature/max7219.cpp +++ b/Marlin/src/feature/max7219.cpp @@ -283,7 +283,7 @@ void Max7219::set(const uint8_t line, const uint8_t bits) { } // Draw a float with a decimal point and optional digits - void Max7219::print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) { + void Max7219::print(const uint8_t start, const float value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false) { if (pre_size) print(start, value, pre_size, leadzero, !!post_size); if (post_size) { const int16_t after = ABS(value) * (10 ^ post_size); @@ -480,32 +480,30 @@ void Max7219::register_setup() { #if MAX7219_INIT_TEST uint8_t test_mode = 0; - millis_t next_patt_ms; bool patt_on; #if MAX7219_INIT_TEST == 2 #define MAX7219_LEDS (MAX7219_X_LEDS * MAX7219_Y_LEDS) - constexpr millis_t pattern_delay = 4; - - int8_t spiralx, spiraly, spiral_dir; + xy_int8_t spiral; + int8_t spiral_dir; uvalue_t(MAX7219_LEDS) spiral_count; - void Max7219::test_pattern() { - constexpr int8_t way[][2] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; - led_set(spiralx, spiraly, patt_on); - const int8_t x = spiralx + way[spiral_dir][0], y = spiraly + way[spiral_dir][1]; - if (!WITHIN(x, 0, MAX7219_X_LEDS - 1) || !WITHIN(y, 0, MAX7219_Y_LEDS - 1) || BIT_7219(x, y) == patt_on) + void Max7219::run_test_pattern() { + constexpr xy_int8_t way[] = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; + led_set(spiral.x, spiral.y, patt_on); + const xy_int8_t xy = spiral + way[spiral_dir]; + if (!WITHIN(xy.x, 0, MAX7219_X_LEDS - 1) || !WITHIN(xy.y, 0, MAX7219_Y_LEDS - 1) || BIT_7219(xy.x, xy.y) == patt_on) spiral_dir = (spiral_dir + 1) & 0x3; - spiralx += way[spiral_dir][0]; - spiraly += way[spiral_dir][1]; + spiral += way[spiral_dir]; if (!spiral_count--) { if (!patt_on) test_mode = 0; else { spiral_count = MAX7219_LEDS; - spiralx = spiraly = spiral_dir = 0; + spiral.reset(); + spiral_dir = 0; patt_on = false; } } @@ -516,7 +514,11 @@ void Max7219::register_setup() { constexpr millis_t pattern_delay = 20; int8_t sweep_count, sweepx, sweep_dir; - void Max7219::test_pattern() { + void Max7219::run_test_pattern() { + static millis_t next_pattern_ms = 0; + const millis_t ms = millis(); + if (PENDING(ms, next_pattern_ms)) return; + next_pattern_ms = ms + pattern_delay; set_column(sweepx, patt_on ? 0xFFFFFFFF : 0x00000000); sweepx += sweep_dir; if (!WITHIN(sweepx, 0, MAX7219_X_LEDS - 1)) { @@ -526,27 +528,21 @@ void Max7219::register_setup() { } else sweepx -= MAX7219_X_LEDS * sweep_dir; - patt_on ^= true; - next_patt_ms += 100; + FLIP(patt_on); + next_pattern_ms += 100; if (++test_mode > 4) test_mode = 0; } } #endif - void Max7219::run_test_pattern() { - const millis_t ms = millis(); - if (PENDING(ms, next_patt_ms)) return; - next_patt_ms = ms + pattern_delay; - test_pattern(); - } - void Max7219::start_test_pattern() { clear(); test_mode = 1; patt_on = true; #if MAX7219_INIT_TEST == 2 - spiralx = spiraly = spiral_dir = 0; + spiral.reset(); + spiral_dir = 0; spiral_count = MAX7219_LEDS; #else sweep_dir = 1; @@ -744,7 +740,6 @@ void Max7219::idle_tasks() { #endif #ifdef MAX7219_DEBUG_PLANNER_QUEUE - static int16_t last_depth = 0; const int16_t current_depth = BLOCK_MOD(head - tail + (BLOCK_BUFFER_SIZE)) & 0xF; if (current_depth != last_depth) { quantity16(MAX7219_DEBUG_PLANNER_QUEUE, last_depth, current_depth, &row_change_mask); @@ -762,7 +757,7 @@ void Max7219::idle_tasks() { #ifdef MAX7219_DEBUG_MULTISTEPPING static uint8_t last_multistepping = 0; - const uint8_t multistepping = Stepper::steps_per_isr; + const uint8_t multistepping = stepper.steps_per_isr; if (multistepping != last_multistepping) { static uint8_t log2_old = 0; uint8_t log2_new = 0; @@ -775,7 +770,7 @@ void Max7219::idle_tasks() { #ifdef MAX7219_DEBUG_SLOWDOWN static uint8_t last_slowdown_count = 0; - const uint8_t slowdown_count = Planner::slowdown_count; + const uint8_t slowdown_count = planner.slowdown_count; if (slowdown_count != last_slowdown_count) { mark16(MAX7219_DEBUG_SLOWDOWN, last_slowdown_count, slowdown_count, &row_change_mask); last_slowdown_count = slowdown_count; diff --git a/Marlin/src/feature/max7219.h b/Marlin/src/feature/max7219.h index a6b110fdf4..799524dc5f 100644 --- a/Marlin/src/feature/max7219.h +++ b/Marlin/src/feature/max7219.h @@ -110,7 +110,7 @@ if (mode == ACCUMULATE_TOTAL) return; // update time_fraction every hundred milliseconds - if (instance_count == 0 && ELAPSED(now, last_calc_time + 100000)) { + if (instance_count == 0 && now - last_calc_time > 100000) { time_fraction = total_time * 128 / (now - last_calc_time); last_calc_time = now; total_time = 0; @@ -166,7 +166,7 @@ public: // Draw an integer with optional leading zeros and optional decimal point void print(const uint8_t start, int16_t value, uint8_t size, const bool leadzero=false, bool dec=false); // Draw a float with a decimal point and optional digits - void print(const uint8_t start, const_float_t value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false); + void print(const uint8_t start, const float value, const uint8_t pre_size, const uint8_t post_size, const bool leadzero=false); #endif // Set a single LED by XY coordinate diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index fe3dabe8da..3b762d4ded 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -169,9 +169,11 @@ void MeatPack::handle_command(const MeatPack_Command c) { void MeatPack::report_state() { // NOTE: if any configuration vars are added below, the outgoing sync text for host plugin // should not contain the "PV' substring, as this is used to indicate protocol version - SERIAL_ECHOPGM("[MP] " MeatPack_ProtocolVersion " "); - serialprint_onoff(TEST(state, MPConfig_Bit_Active)); - SERIAL_ECHO(TEST(state, MPConfig_Bit_NoSpaces) ? F(" NSP\n") : F(" ESP\n")); + SERIAL_ECHO( + F("[MP] " MeatPack_ProtocolVersion " "), + ON_OFF(TEST(state, MPConfig_Bit_Active)), + TEST(state, MPConfig_Bit_NoSpaces) ? F(" NSP\n") : F(" ESP\n") + ); } /** diff --git a/Marlin/src/feature/mixing.cpp b/Marlin/src/feature/mixing.cpp index 4021393f18..bc98bf3b26 100644 --- a/Marlin/src/feature/mixing.cpp +++ b/Marlin/src/feature/mixing.cpp @@ -28,10 +28,6 @@ Mixer mixer; -#ifdef MIXER_NORMALIZER_DEBUG - #include "../core/serial.h" -#endif - // Used up to Planner level uint_fast8_t Mixer::selected_vtool = 0; float Mixer::collector[MIXING_STEPPERS]; // mix proportion. 0.0 = off, otherwise <= COLOR_A_MASK. @@ -170,7 +166,7 @@ void Mixer::refresh_collector(const float proportion/*=1.0*/, const uint8_t t/*= float Mixer::prev_z; // = 0 - void Mixer::update_gradient_for_z(const_float_t z) { + void Mixer::update_gradient_for_z(const float z) { if (z == prev_z) return; prev_z = z; diff --git a/Marlin/src/feature/mixing.h b/Marlin/src/feature/mixing.h index c0f45e364a..bd7ffb560e 100644 --- a/Marlin/src/feature/mixing.h +++ b/Marlin/src/feature/mixing.h @@ -174,9 +174,9 @@ class Mixer { static float prev_z; // Update the current mix from the gradient for a given Z - static void update_gradient_for_z(const_float_t z); + static void update_gradient_for_z(const float z); static void update_gradient_for_planner_z(); - static void gradient_control(const_float_t z) { + static void gradient_control(const float z) { if (gradient.enabled) { if (z >= gradient.end_z) T(gradient.end_vtool); diff --git a/Marlin/src/feature/mmu/mmu2-serial-protocol.md b/Marlin/src/feature/mmu/mmu2-serial-protocol.md index 088d41b446..474fcd488b 100644 --- a/Marlin/src/feature/mmu/mmu2-serial-protocol.md +++ b/Marlin/src/feature/mmu/mmu2-serial-protocol.md @@ -1,5 +1,4 @@ -Startup sequence -================ +# Startup sequence When initialized, MMU sends @@ -8,9 +7,9 @@ When initialized, MMU sends We follow with - MMU <= 'S1\n' -- MMU => 'ok*Firmware version*\n' +- MMU => 'ok<_Firmware version_>\n' - MMU <= 'S2\n' -- MMU => 'ok*Build number*\n' +- MMU => 'ok<_Build number_>\n' #if (12V_mode) @@ -20,26 +19,25 @@ We follow with #endif - MMU <= 'P0\n' -- MMU => '*FINDA status*\n' +- MMU => '<_FINDA status_>\n' Now we are sure MMU is available and ready. If there was a timeout or other communication problem somewhere, printer will be killed. -- *Firmware version* is an integer value, but we don't care about it -- *Build number* is an integer value and has to be >=126, or =>132 if 12V mode is enabled -- *FINDA status* is 1 if the filament is loaded to the extruder, 0 otherwise +- <_Firmware version_> is an integer value, but we don't care about it. +- <_Build number_> is an integer value and has to be >=126, or =>132 if 12V mode is enabled. +- <_FINDA status_> is 1 if the filament is loaded to the extruder, 0 otherwise. -*Build number* is checked against the required value, if it does not match, printer is halted. +<_Build number_> is checked against the required value, if it does not match, printer is halted. -Toolchange -========== +# Toolchange -- MMU <= 'T*Filament index*\n' +- MMU <= 'T<_Filament index_>\n' MMU sends - MMU => 'ok\n' -as soon as the filament is fed down to the extruder. We follow with +as soon as the filament is fed down to the extruder. We follow with: - MMU <= 'C0\n' @@ -51,34 +49,30 @@ When done, the MMU sends We don't wait for a response here but immediately continue with the next G-code which should be one or more extruder moves to feed the filament into the hotend. -FINDA status -============ +# FINDA status - MMU <= 'P0\n' -- MMU => '*FINDA status*\n' +- MMU => '<_FINDA status_>\n' -*FINDA status* is 1 if the is filament loaded to the extruder, 0 otherwise. This could be used as filament runout sensor if probed regularly. +_FINDA status_ is 1 if the is filament loaded to the extruder, 0 otherwise. This could be used as filament runout sensor if probed regularly. -Load filament -============= +# Load filament -- MMU <= 'L*Filament index*\n' +- MMU <= 'L<_Filament index_>\n' -MMU will feed filament down to the extruder, when done +MMU will feed filament down to the extruder, when done: - MMU => 'ok\n' -Unload filament -============= +# Unload filament - MMU <= 'U0\n' -MMU will retract current filament from the extruder, when done +MMU will retract current filament from the extruder, when done: - MMU => 'ok\n' -Eject filament -============== +# Eject filament -- MMU <= 'E*Filament index*\n' +- MMU <= 'E<_Filament index_>\n' - MMU => 'ok\n' diff --git a/Marlin/src/feature/mmu/mmu2.cpp b/Marlin/src/feature/mmu/mmu2.cpp index 5ef56c7eac..b976150ed9 100644 --- a/Marlin/src/feature/mmu/mmu2.cpp +++ b/Marlin/src/feature/mmu/mmu2.cpp @@ -24,6 +24,10 @@ #if HAS_PRUSA_MMU2 +/** + * mmu2.cpp - Support for Průša MMU2 and MMU2S + */ + #include "mmu2.h" #include "../../lcd/menu/menu_mmu2.h" @@ -39,14 +43,14 @@ MMU2 mmu2; #include "../../MarlinCore.h" #if ENABLED(HOST_PROMPT_SUPPORT) - #include "../../feature/host_actions.h" + #include "../host_actions.h" #endif #if ENABLED(EXTENSIBLE_UI) #include "../../lcd/extui/ui_api.h" #endif -#define DEBUG_OUT ENABLED(MMU2_DEBUG) +#define DEBUG_OUT ENABLED(MMU_DEBUG) #include "../../core/debug_out.h" #define MMU_TODELAY 100 @@ -57,7 +61,7 @@ MMU2 mmu2; #define MMU2_SEND(S) tx_str(F(S "\n")) #define MMU2_RECV(S) rx_str(F(S "\n")) -#if ENABLED(MMU_EXTRUDER_SENSOR) +#if ENABLED(MMU2_EXTRUDER_SENSOR) uint8_t mmu_idl_sens = 0; static bool mmu_loading_flag = false; #endif @@ -92,7 +96,7 @@ struct E_Step { feedRate_t feedRate; //!< feed rate in mm/s }; -inline void unscaled_mmu2_e_move(const float &dist, const feedRate_t fr_mm_s, const bool sync=true) { +inline void unscaled_mmu2_e_move(const float dist, const feedRate_t fr_mm_s, const bool sync=true) { current_position.e += dist / planner.e_factor[active_extruder]; line_to_current_position(fr_mm_s); if (sync) planner.synchronize(); @@ -106,12 +110,12 @@ void MMU2::init() { set_runout_valid(false); - #if PIN_EXISTS(MMU2_RST) - WRITE(MMU2_RST_PIN, HIGH); - SET_OUTPUT(MMU2_RST_PIN); + #if PIN_EXISTS(MMU_RST) + WRITE(MMU_RST_PIN, HIGH); + SET_OUTPUT(MMU_RST_PIN); #endif - MMU2_SERIAL.begin(MMU_BAUD); + MMU_SERIAL.begin(MMU_BAUD); extruder = MMU2_NO_TOOL; safe_delay(10); @@ -123,10 +127,10 @@ void MMU2::init() { void MMU2::reset() { DEBUG_ECHOLNPGM("MMU <= reset"); - #if PIN_EXISTS(MMU2_RST) - WRITE(MMU2_RST_PIN, LOW); + #if PIN_EXISTS(MMU_RST) + WRITE(MMU_RST_PIN, LOW); safe_delay(20); - WRITE(MMU2_RST_PIN, HIGH); + WRITE(MMU_RST_PIN, HIGH); #else MMU2_SEND("X0"); // Send soft reset #endif @@ -134,7 +138,7 @@ void MMU2::reset() { int8_t MMU2::get_current_tool() { return extruder == MMU2_NO_TOOL ? -1 : extruder; } -#if ANY(HAS_PRUSA_MMU2S, MMU_EXTRUDER_SENSOR) +#if ANY(HAS_PRUSA_MMU2S, MMU2_EXTRUDER_SENSOR) #define FILAMENT_PRESENT() (READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE) #else #define FILAMENT_PRESENT() true @@ -160,7 +164,7 @@ void MMU2::mmu_loop() { MMU2_SEND("S1"); // Read Version state = -2; } - else if (ELAPSED(millis(), prev_request + 30000)) { // 30sec after reset disable MMU + else if (ELAPSED(millis(), prev_request, 30000)) { // 30sec after reset disable MMU SERIAL_ECHOLNPGM("MMU not responding - DISABLED"); state = 0; } @@ -226,7 +230,7 @@ void MMU2::mmu_loop() { const int filament = cmd - MMU_CMD_T0; DEBUG_ECHOLNPGM("MMU <= T", filament); tx_printf(F("T%d\n"), filament); - TERN_(MMU_EXTRUDER_SENSOR, mmu_idl_sens = 1); // enable idler sensor, if any + TERN_(MMU2_EXTRUDER_SENSOR, mmu_idl_sens = 1); // enable idler sensor, if any state = 3; // wait for response } else if (WITHIN(cmd, MMU_CMD_L0, MMU_CMD_L0 + EXTRUDERS - 1)) { @@ -272,7 +276,7 @@ void MMU2::mmu_loop() { last_cmd = cmd; cmd = MMU_CMD_NONE; } - else if (ELAPSED(millis(), prev_P0_request + 300)) { + else if (ELAPSED(millis(), prev_P0_request, 300)) { MMU2_SEND("P0"); // Read FINDA state = 2; // wait for response } @@ -292,14 +296,14 @@ void MMU2::mmu_loop() { if (cmd == MMU_CMD_NONE) ready = true; state = 1; } - else if (ELAPSED(millis(), prev_request + MMU_P0_TIMEOUT)) // Resend request after timeout (3s) + else if (ELAPSED(millis(), prev_request, MMU_P0_TIMEOUT)) // Resend request after timeout (3s) state = 1; TERN_(HAS_PRUSA_MMU2S, check_filament()); break; case 3: // response to mmu commands - #if ENABLED(MMU_EXTRUDER_SENSOR) + #if ENABLED(MMU2_EXTRUDER_SENSOR) if (mmu_idl_sens) { if (FILAMENT_PRESENT() && mmu_loading_flag) { DEBUG_ECHOLNPGM("MMU <= 'A'"); @@ -331,7 +335,7 @@ void MMU2::mmu_loop() { last_cmd = MMU_CMD_NONE; } } - else if (ELAPSED(millis(), prev_request + MMU_CMD_TIMEOUT)) { + else if (ELAPSED(millis(), prev_request, MMU_CMD_TIMEOUT)) { // resend request after timeout if (last_cmd) { DEBUG_ECHOLNPGM("MMU retry"); @@ -361,8 +365,8 @@ bool MMU2::rx_str(FSTR_P fstr) { uint8_t i = strlen(rx_buffer); - while (MMU2_SERIAL.available()) { - rx_buffer[i++] = MMU2_SERIAL.read(); + while (MMU_SERIAL.available()) { + rx_buffer[i++] = MMU_SERIAL.read(); if (i == sizeof(rx_buffer) - 1) { DEBUG_ECHOLNPGM("rx buffer overrun"); @@ -393,7 +397,7 @@ bool MMU2::rx_str(FSTR_P fstr) { void MMU2::tx_str(FSTR_P fstr) { clear_rx_buffer(); PGM_P pstr = FTOP(fstr); - while (const char c = pgm_read_byte(pstr)) { MMU2_SERIAL.write(c); pstr++; } + while (const char c = pgm_read_byte(pstr)) { MMU_SERIAL.write(c); pstr++; } prev_request = millis(); } @@ -403,7 +407,7 @@ void MMU2::tx_str(FSTR_P fstr) { void MMU2::tx_printf(FSTR_P format, int argument = -1) { clear_rx_buffer(); const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument); - for (uint8_t i = 0; i < len; ++i) MMU2_SERIAL.write(tx_buffer[i]); + for (uint8_t i = 0; i < len; ++i) MMU_SERIAL.write(tx_buffer[i]); prev_request = millis(); } @@ -413,7 +417,7 @@ void MMU2::tx_printf(FSTR_P format, int argument = -1) { void MMU2::tx_printf(FSTR_P format, int argument1, int argument2) { clear_rx_buffer(); const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument1, argument2); - for (uint8_t i = 0; i < len; ++i) MMU2_SERIAL.write(tx_buffer[i]); + for (uint8_t i = 0; i < len; ++i) MMU_SERIAL.write(tx_buffer[i]); prev_request = millis(); } @@ -421,7 +425,7 @@ void MMU2::tx_printf(FSTR_P format, int argument1, int argument2) { * Empty the rx buffer */ void MMU2::clear_rx_buffer() { - while (MMU2_SERIAL.available()) MMU2_SERIAL.read(); + while (MMU_SERIAL.available()) MMU_SERIAL.read(); rx_buffer[0] = '\0'; } @@ -526,7 +530,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } switch (*special) { case '?': { - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) const uint8_t index = mmu2_choose_filament(); while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_to_nozzle(index); @@ -536,7 +540,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } } break; case 'x': { - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) planner.synchronize(); const uint8_t index = mmu2_choose_filament(); stepper.disable_extruder(); @@ -563,7 +567,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } set_runout_valid(true); } -#elif ENABLED(MMU_EXTRUDER_SENSOR) +#elif ENABLED(MMU2_EXTRUDER_SENSOR) /** * Handle tool change @@ -614,7 +618,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } switch (*special) { case '?': { DEBUG_ECHOLNPGM("case ?\n"); - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) uint8_t index = mmu2_choose_filament(); while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_to_nozzle(index); @@ -625,7 +629,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } case 'x': { DEBUG_ECHOLNPGM("case x\n"); - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) planner.synchronize(); uint8_t index = mmu2_choose_filament(); stepper.disable_extruder(); @@ -656,7 +660,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } void MMU2::mmu_continue_loading() { // Try to load the filament a limited number of times bool fil_present = 0; - for (uint8_t i = 0; i < MMU_LOADING_ATTEMPTS_NR; i++) { + for (uint8_t i = 0; i < MMU2_LOADING_ATTEMPTS_NR; i++) { DEBUG_ECHOLNPGM("Load attempt #", i + 1); // Done as soon as filament is present @@ -688,7 +692,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } mmu_idl_sens = 0; } -#else // !HAS_PRUSA_MMU2S && !MMU_EXTRUDER_SENSOR +#else // !HAS_PRUSA_MMU2S && !MMU2_EXTRUDER_SENSOR /** * Handle tool change @@ -729,7 +733,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } switch (*special) { case '?': { DEBUG_ECHOLNPGM("case ?\n"); - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) uint8_t index = mmu2_choose_filament(); while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_to_nozzle(index); @@ -740,7 +744,7 @@ inline void beep_bad_cmd() { BUZZ(400, 40); } case 'x': { DEBUG_ECHOLNPGM("case x\n"); - #if ENABLED(MMU2_MENUS) + #if ENABLED(MMU_MENUS) planner.synchronize(); uint8_t index = mmu2_choose_filament(); stepper.disable_extruder(); @@ -891,8 +895,8 @@ void MMU2::filament_runout() { } bool MMU2::can_load() { - static const E_Step can_load_sequence[] PROGMEM = { MMU2_CAN_LOAD_SEQUENCE }, - can_load_increment_sequence[] PROGMEM = { MMU2_CAN_LOAD_INCREMENT_SEQUENCE }; + static constexpr E_Step can_load_sequence[] PROGMEM = { MMU2_CAN_LOAD_SEQUENCE }, + can_load_increment_sequence[] PROGMEM = { MMU2_CAN_LOAD_INCREMENT_SEQUENCE }; execute_extruder_sequence(can_load_sequence, COUNT(can_load_sequence)); @@ -1041,7 +1045,8 @@ void MMU2::load_to_nozzle_sequence() { execute_extruder_sequence(sequence, COUNT(sequence)); } -void MMU2::execute_extruder_sequence(const E_Step * sequence, int steps) { +void MMU2::execute_extruder_sequence(const E_Step * const sequence, const uint8_t steps) { + planner.synchronize(); const E_Step *step = sequence; diff --git a/Marlin/src/feature/mmu/mmu2.h b/Marlin/src/feature/mmu/mmu2.h index 2c9fd3308d..117b3a6046 100644 --- a/Marlin/src/feature/mmu/mmu2.h +++ b/Marlin/src/feature/mmu/mmu2.h @@ -21,6 +21,10 @@ */ #pragma once +/** + * mmu2.h - Support for Průša MMU2 and MMU2S + */ + #include "../../inc/MarlinConfig.h" #if HAS_FILAMENT_SENSOR @@ -70,7 +74,7 @@ private: static bool get_response(); static void manage_response(const bool move_axes, const bool turn_off_nozzle); - static void execute_extruder_sequence(const E_Step * sequence, int steps); + static void execute_extruder_sequence(const E_Step * const sequence, const uint8_t steps); static void ramming_sequence(); static void load_to_nozzle_sequence(); @@ -85,7 +89,7 @@ private: FORCE_INLINE static bool load_to_gears() { return true; } #endif - #if ENABLED(MMU_EXTRUDER_SENSOR) + #if ENABLED(MMU2_EXTRUDER_SENSOR) #define MMU_LOAD_FEEDRATE 19.02f // (mm/s) static void mmu_continue_loading(); #endif diff --git a/Marlin/src/feature/mmu3/SpoolJoin.cpp b/Marlin/src/feature/mmu3/SpoolJoin.cpp new file mode 100644 index 0000000000..f27d2bc7e9 --- /dev/null +++ b/Marlin/src/feature/mmu3/SpoolJoin.cpp @@ -0,0 +1,73 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * SpoolJoin.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "SpoolJoin.h" +#include "../../module/settings.h" +#include "../../core/language.h" + +SpoolJoin spooljoin; + +bool SpoolJoin::enabled; // Initialized by settings.load +int SpoolJoin::epprom_addr; // Initialized by settings.load +uint8_t SpoolJoin::currentMMUSlot; + +SpoolJoin::SpoolJoin() { setSlot(0); } + +void SpoolJoin::initStatus() { + // Useful information to see during bootup + SERIAL_ECHOLN(F("SpoolJoin is "), enabled ? F("On") : F("Off")); +} + +void SpoolJoin::toggle() { + // Toggle enabled value. + FLIP(enabled); + + // Following Prusa's implementation let's save the value to the EEPROM + // TODO: Move to settings.cpp + #if ENABLED(EEPROM_SETTINGS) + persistentStore.access_start(); + persistentStore.write_data(epprom_addr, enabled); + persistentStore.access_finish(); + settings.save(); + #endif +} + +bool SpoolJoin::isEnabled() { return enabled; } + +void SpoolJoin::setSlot(const uint8_t slot) { currentMMUSlot = slot; } + +uint8_t SpoolJoin::nextSlot() { + SERIAL_ECHOPGM("SpoolJoin: ", currentMMUSlot); + if (++currentMMUSlot >= 4) currentMMUSlot = 0; + SERIAL_ECHOLNPGM(" -> ", currentMMUSlot); + return currentMMUSlot; +} + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/SpoolJoin.h b/Marlin/src/feature/mmu3/SpoolJoin.h new file mode 100644 index 0000000000..b205d26ef5 --- /dev/null +++ b/Marlin/src/feature/mmu3/SpoolJoin.h @@ -0,0 +1,72 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * SpoolJoin.h + */ + +#include "../../MarlinCore.h" + +#include + +// See documentation here: https://help.prusa3d.com/article/spooljoin-mmu2s_134252 + +class SpoolJoin { +public: + SpoolJoin(); + + enum class EEPROM : uint8_t { + Unknown, //!< SpoolJoin is unknown while printer is booting up + Enabled, //!< SpoolJoin is enabled in EEPROM + Disabled, //!< SpoolJoin is disabled in EEPROM + Empty = 0xFF //!< EEPROM has not been set before and all bits are 1 (0xFF) - either a new printer or user erased the memory + }; + + // @brief Contrary to Prusa's implementation we store the enabled status in a variable + static int epprom_addr; + static bool enabled; + + // @brief Called when EEPROM is ready to be read + static void initStatus(); + + // @brief Toggle SpoolJoin + static void toggle(); + + // @brief Check if SpoolJoin is enabled + // @return true if enabled, false if disabled + static bool isEnabled(); + + // @brief Update the saved MMU slot number so SpoolJoin can determine the next slot to use + // @param slot number of the slot to set + static void setSlot(const uint8_t slot); + + // @brief Fetch the next slot number (0 to 4). + // When filament slot 4 is depleted, the next slot should be 0. + // @return the next slot (0 to 4) + static uint8_t nextSlot(); + +private: + static uint8_t currentMMUSlot; //!< Currently used slot (0 to 4) +}; + +extern SpoolJoin spooljoin; diff --git a/Marlin/src/feature/mmu3/mmu3-serial-protocol.md b/Marlin/src/feature/mmu3/mmu3-serial-protocol.md new file mode 100644 index 0000000000..0d0f7ac6e0 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3-serial-protocol.md @@ -0,0 +1,172 @@ +# MMU3 Messages + +Starting with the version 2.0.19 of the MMU firmware, requests and responses have a trailing section that contains the CRC8 of the original message. The general structure is as follows: + +``` +Requests (what Marlin requests): +MMU3:>{RequestMsgCode}{Value}*{CRC8}\n + +Responses (what MMU responds with): +MMU3:<{RequestMsgCode}{Value} {ResponseMsgParamCode}{paramValue}*{CRC8}\n +``` + +An example of that would be: + +``` +MMU3:>S0*c6\n +MMU3:S1*ad\n +MMU3:S2*10\n +MMU3:S0*c6\n +MMU3:>S0*c6\n +MMU3:>S0*c6\n +... +``` + +Once communication is established the MMU responds with: + +``` +MMU3:S1*ad\n +MMU3:S2*10\n +MMU3:M1*{CRC8}; +MMU3:<---nothing--- +``` + +``` +MMU3:>P0 +MMU3:T{Filament index}*{CRC8}\n +MMU3:T0*{CRC8}\n + +MMU3:>Q0*{CRC8}\n +MMU3: FeedingToFinda + +MMU3:>Q0*{CRC8}\n +MMU3: FeedingToNozzle +``` + +As soon as the filament is fed down to the extruder we follow with: + +``` +MMU3:>C0*{CRC8}\n +``` + +The MMU will feed a few more millimeters of filament for the extruder gears to grab. When done, the MMU sends: + +``` +MMU3:>Q0*{CRC8}\n +MMU3: FinishingMoves +``` + +After the `T0*P9` response we immediately continue with the next G-code which should be one or more extruder moves to feed the filament into the hotend. + +## FINDA status + +``` +MMU3:>P0*{CRC8}\n +``` + +If the filament is loaded to the extruder, FINDA status is 1 and the MMU responds with: + +``` +MMU3:L{Filament index}*{CRC8}\n +MMU3:Q0*{CRC8}\n +``` + +The MMU will respond with status messages: + +``` +MMU3:Q0*{CRC8}\n +MMU3: 'ok\n'` + +## Eject filament + +- `MMU <= 'E*Filament index*\n'` +- `MMU => 'ok\n'` diff --git a/Marlin/src/feature/mmu3/mmu3.cpp b/Marlin/src/feature/mmu3/mmu3.cpp new file mode 100644 index 0000000000..f2c7be3f6a --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3.cpp @@ -0,0 +1,1185 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3.h" +#include "mmu3_error_converter.h" +#include "mmu3_fsensor.h" +#include "mmu3_log.h" +#include "mmu3_marlin.h" +#include "mmu3_marlin_macros.h" +#include "mmu3_power.h" +#include "mmu3_progress_converter.h" +#include "mmu3_reporting.h" + +#include "strlen_cx.h" +#include "SpoolJoin.h" + +#include "../../inc/MarlinConfig.h" + +#include "../../lcd/marlinui.h" +#include "../../module/planner.h" +#include "../../module/motion.h" +#include "../../gcode/parser.h" +#include "../../gcode/queue.h" +#include "../runout.h" +#if HAS_LEVELING + #include "../bedlevel/bedlevel.h" +#endif +#include "../pause.h" +#include "../../libs/stopwatch.h" + +// As of FW 3.12 we only support building the FW with only one extruder, all the multi-extruder infrastructure will be removed. +// Saves at least 800B of code size +//#ifdef __AVR__ +//static_assert(EXTRUDERS == 1); +//#endif + +#define MMU2_NO_TOOL 99 + +MMU3::MMU3 mmu3; + +namespace MMU3 { + + template + void waitForHotendTargetTemp(uint16_t delay, F f) { + while (((thermal_degTargetHotend() - thermal_degHotend()) > 5)) { + f(); + safe_delay_keep_alive(delay); + } + } + + void WaitForHotendTargetTempBeep() { + waitForHotendTargetTemp(3000, []{}); + //MakeSound(Prompt); + } + + uint8_t MMU3::cutter_mode; // Initialized by settings.load + int MMU3::cutter_mode_addr; // Initialized by settings.load + uint8_t MMU3::stealth_mode; // Initialized by settings.load + int MMU3::stealth_mode_addr; // Initialized by settings.load + // TODO: Currently, by logic, the value stored in the EEPROM for is ignored and + // mmu_hw_enabled is always overwritten by the MMU State. Thus restarting + // printer will always set the MMU as senabled. + bool MMU3::mmu_hw_enabled; // Initialized by settings.load + int MMU3::mmu_hw_enabled_addr; // Initialized by settings.load + + MMU3::MMU3() + : logic(MMU3_TOOL_CHANGE_LOAD_LENGTH, MMU3_LOAD_TO_NOZZLE_FEED_RATE) + , extruder(MMU2_NO_TOOL) + , tool_change_extruder(MMU2_NO_TOOL) + , resume_position() + , resume_hotend_temp(0) + , logicStepLastStatus(StepStatus::Finished) + , _state(xState::Stopped) + , mmu_print_saved(SavedState::None) + , loadFilamentStarted(false) + , unloadFilamentStarted(false) + , toolchange_counter(0) + , _tmcFailures(0) { } + + void MMU3::status() { + // Useful information to see during bootup and change state + SERIAL_ECHOLN(F("MMU is "), mmu_hw_enabled ? GET_TEXT_F(MSG_ON) : GET_TEXT_F(MSG_OFF)); + } + + void MMU3::start() { + mmu_hw_enabled = true; + + #if ENABLED(EEPROM_SETTINGS) + // Save mmu_hw_enabled to EEPROM + // TODO: Move to settings.cpp (for now) + persistentStore.access_start(); + persistentStore.write_data(mmu_hw_enabled_addr, mmu_hw_enabled); + persistentStore.access_finish(); + settings.save(); + #endif + + MMU_SERIAL.begin(MMU_BAUD); + + powerOn(); + MMU_SERIAL.flush(); // Make sure the UART buffer is clear before starting communication + + setCurrentTool(MMU2_NO_TOOL); + _state = xState::Connecting; + + // Start communication + logic.start(); + logic.ResetRetryAttempts(); + logic.ResetCommunicationTimeoutAttempts(); + } + + void MMU3::stop() { + stopKeepPowered(); + powerOff(); + } + + void MMU3::stopKeepPowered() { + mmu_hw_enabled = false; + + #if ENABLED(EEPROM_SETTINGS) + // Save mmu_hw_enabled to EEPROM + persistentStore.access_start(); + persistentStore.write_data(mmu_hw_enabled_addr, mmu_hw_enabled); + persistentStore.access_finish(); + settings.save(); + #endif + + _state = xState::Stopped; + logic.stop(); + MMU_SERIAL.end(); + } + + void MMU3::tune() { + switch (lastErrorCode) { + case ErrorCode::HOMING_SELECTOR_FAILED: + case ErrorCode::HOMING_IDLER_FAILED: { + // Prompt a menu for different values + tuneIdlerStallguardThreshold(); + break; + } + default: break; + } + } + + void MMU3::reset(ResetForm level) { + switch (level) { + case Software: resetX0(); break; + case ResetPin: triggerResetPin(); break; + case CutThePower: powerCycle(); break; + case EraseEEPROM: resetX42(); break; + default: break; + } + } + + void MMU3::resetX0() { logic.ResetMMU(); } // Send soft reset + void MMU3::resetX42() { logic.ResetMMU(42); } + + void MMU3::triggerResetPin() { power_reset(); } + + void MMU3::powerCycle() { + // cut the power to the MMU and after a while restore it + // Sadly, MK3/S/+ cannot do this + stop(); + safe_delay_keep_alive(1000); + start(); + } + + void MMU3::powerOff() { power_off(); } + void MMU3::powerOn() { power_on(); } + + bool MMU3::readRegister(uint8_t address) { + if (!waitForMMUReady()) return false; + + do { + logic.readRegister(address); // we may signal the accepted/rejected status of the response as return value of this function + } while (!manage_response(false, false)); + + // Update cached value + lastReadRegisterValue = logic.rsp.paramValue; + return true; + } + + bool __attribute__((noinline)) MMU3::writeRegister(uint8_t address, uint16_t data) { + if (!waitForMMUReady()) return false; + + // special cases - intercept requests of registers which influence the printer's behaviour too + perform the change even on the printer's side + switch (address) { + case (uint8_t)Register::Extra_Load_Distance: logic.PlanExtraLoadDistance(data); break; + case (uint8_t)Register::Pulley_Slow_Feedrate: logic.PlanPulleySlowFeedRate(data); break; + default: break; // Don't intercept any other register writes + } + + do { + logic.writeRegister(address, data); // we may signal the accepted/rejected status of the response as return value of this function + } while (!manage_response(false, false)); + + return true; + } + + void MMU3::mmu_loop() { + // We only leave this method if the current command was successfully + // completed - that's the Marlin's way of blocking operation + // Atomic compare_exchange would have been the most appropriate solution + // here, but this gets called only in Marlin's task, so thread safety + // should be kept + static bool avoidRecursion = false; + if (avoidRecursion) return; + avoidRecursion = true; + + mmu_loop_inner(true); + + avoidRecursion = false; + } + + void __attribute__((noinline)) MMU3::mmu_loop_inner(bool reportErrors) { + logicStepLastStatus = logicStep(reportErrors); // it looks like the mmu_loop doesn't need to be a blocking call + CheckErrorScreenUserInput(); + } + + /** + * Check if there are extruder moves planned ahead. + * + * TODO: This should go to the planner, but for now keep it here! + */ + bool MMU3::e_active() { + unsigned char e_active = 0; + block_t *block; + if (planner.block_buffer_tail != planner.block_buffer_head) { + uint8_t block_index = planner.block_buffer_tail; + while (block_index != planner.block_buffer_head) { + block = &planner.block_buffer[block_index]; + if (block->steps.e != 0) e_active++; + block_index = (block_index + 1) & (BLOCK_BUFFER_SIZE - 1); + } + } + return (e_active > 0); + } + + /** + * Trigger an M600 or the SpoolJoin feature if the FINDA cannot detect any + * filament during the print. + * + * In case of SpoolJoin feature is triggered, Marlin's implementation is a + * little different than Prusa's, as we are completely consuming the filament + * before switching to the next slot. There will be a little bit of filament + * left when the new filament is extruded SpoolJoin is not intended to be used with + * multi color/material prints so this should be fine. + */ + void MMU3::checkFINDARunout() { + if (!findaDetectsFilament() + //&& printJobOngoing() + && parser.codenum != 600 + && TERN1(HAS_LEVELING, planner.leveling_active) + && xy_are_trusted() + && e_active() + #if ENABLED(MMU3_SPOOL_JOIN_CONSUMES_ALL_FILAMENT) + && runout.enabled // to prevent M600 to be triggered during M600 AUTO + && !FILAMENT_PRESENT() // so the filament is totally consumed + #endif + ) { + SERIAL_ECHOLN_P("FINDA filament runout!"); + if (spooljoin.isEnabled() && get_current_tool() != (uint8_t)FILAMENT_UNKNOWN) { // Can't auto if F=? + #if ENABLED(MMU3_SPOOL_JOIN_CONSUMES_ALL_FILAMENT) + // set the current tool to FILAMENT_UNKNOWN so that we don't try to unload it + extruder = MMU2_NO_TOOL; + // disable the filament runout sensor (this is going to be re-enabled after the filament is loaded) + runout.reset(); + runout.filament_ran_out = false; // trying to disable the purge more / continue message + runout.enabled = false; + #endif + queue.enqueue_now(F("M600A")); // Save print and run M600 A (automatic) command + } + else { + marlin_stop_and_save_print_to_ram(); + resume_print(); + queue.enqueue_now(F("M600")); // Save print and run M600 command + } + } + } + + struct ReportingRAII { + CommandInProgress cip; + explicit inline __attribute__((always_inline)) ReportingRAII(CommandInProgress cip) + : cip(cip) { + BeginReport(cip, ProgressCode::EngagingIdler); + } + inline __attribute__((always_inline)) ~ReportingRAII() { + EndReport(cip, ProgressCode::OK); + } + }; + + bool MMU3::waitForMMUReady() { + switch (state()) { + case xState::Stopped: return false; + case xState::Connecting: + // Should we wait until the MMU reconnects? + // Fire up a fsm_dlg and show "MMU not responding"? + default: return true; + } + } + + bool MMU3::retryIfPossible(const ErrorCode ec) { + if (logic.RetryAttempts()) { + SetButtonResponse(ButtonOperations::Retry); + // check, that Retry is actually allowed on that operation + if (ButtonAvailable(ec) != Buttons::NoButton) { + logic.SetInAutoRetry(true); + SERIAL_ECHOLN_P("RetryButtonPressed"); + // We don't decrement until the button is acknowledged by the MMU. + // --retryAttempts; // "used" one retry attempt + return true; + } + } + logic.SetInAutoRetry(false); + return false; + } + + bool MMU3::verifyFilamentEnteredPTFE() { + planner_synchronize(); + + if (WhereIsFilament() != FilamentState::AT_FSENSOR) + return false; + + // MMU has finished its load, push the filament further by some defined constant length + // If the filament sensor reads 0 at any moment, then report FAILURE + const float tryload_length = MMU3_CHECK_FILAMENT_PRESENCE_EXTRUSION_LENGTH - logic.ExtraLoadDistance(); + TryLoadUnloadReporter tlur(tryload_length); + + /** + * The position is a triangle wave. + * Current position is not zero, it is an offset + * + * Keep in mind that the relationship between machine position + * and pixel index is not linear. The area around the amplitude + * needs to be taken care of carefully. The current implementation + * handles each move separately so there is no need to watch for the change + * in the slope's sign or check the last machine position. + * y(x) + * ▲ + * │ ^◄────────── tryload_length + current_position + * machine │ / \ + * position │ / \◄────────── stepper_position_mm + current_position + * (mm) │ / \ + * │ / \ + * │/ \◄───────current_position + * └──────────────► x + * 0 19 + * pixel # + */ + + bool filament_inserted = true; // Expect success + // Pixel index will go from 0 to 10, then back from 10 to 0. + // A change in this value indicates a new pixel should be drawn on the display. + for (uint8_t move = 0; move < 2; move++) { + extruder_move(move == 0 ? tryload_length : -tryload_length, MMU3_VERIFY_LOAD_TO_NOZZLE_FEED_RATE); + while (planner_any_moves()) { + filament_inserted = filament_inserted && (WhereIsFilament() == FilamentState::AT_FSENSOR); + tlur.Progress(filament_inserted); + safe_delay_keep_alive(0); + } + } + Disable_E0(); + if (!filament_inserted) IncrementLoadFails(); + tlur.DumpToSerial(); + return filament_inserted; + } + + bool MMU3::toolChangeCommonOnce(uint8_t slot) { + static_assert(MMU3_MAX_RETRIES > 1); // Need >1 retries to do the cut in the last attempt + uint8_t retries = 0; + for (;;) { + for (;;) { + Disable_E0(); // It may seem counterintuitive to disable the E-motor, but it gets enabled in the planner whenever the E-motor is to move + tool_change_extruder = slot; + logic.ToolChange(slot); // Let the MMU pull the filament out and push a new one in + + if (manage_response(true, true)) break; + + // Otherwise: failed to perform the command - unload first and then let it run again + IncrementMMUFails(); + + // Just in case we stood in an error screen for too long and the hotend got cold + resumeHotendTemp(); + // If the extruder has been parked, it will get unparked once the ToolChange command finishes OK + // - so no resumeUnpark() at this spot + + unloadInner(); + // If we run out of retries, we must do something ... maybe raise an error screen and allow the user to do something. + // But honestly - if the MMU restarts during every toolchange something else is seriously broken + // and stopping a print is probably our best option. + } + if (verifyFilamentEnteredPTFE()) return true; // success + + // Prepare a retry attempt + unloadInner(); + if (retries == (MMU3_MAX_RETRIES) - 1 && cutter_enabled()) { + cutFilamentInner(slot); // try cutting filament tip at the last attempt + retries = 0; // reset retries every MMU3_MAX_RETRIES + } + + ++retries; + } + return false; // Couldn't accomplish the task + } + + void MMU3::toolChangeCommon(uint8_t slot) { + while (!toolChangeCommonOnce(slot)) { // While not successfully fed into extruder's PTFE tube... + // Failed autoretry, report an error by forcing a "printer" error into the MMU infrastructure - it is a hack to leverage existing code + // @@TODO theoretically logic layer may not need to be spoiled with the printer error - maybe just the manage_response needs it... + logic.SetPrinterError(ErrorCode::LOAD_TO_EXTRUDER_FAILED); + // We only have to wait for the user to fix the issue and press "Retry". + // Please see checkUserInput() for details how we "leave" manage_response. + // If manage_response returns false at this spot (MMU operation interrupted aka MMU reset) + // we can safely continue because the MMU is not doing an operation now. + static_cast(manage_response(true, true)); // yes, I'd like to silence [[nodiscard]] warning at this spot by casting to void + } + + setCurrentTool(slot); // filament change is finished + spooljoin.setSlot(slot); + + ++toolchange_counter; + + // Also increment the total number of tool changes + operation_statistics.increment_tool_change_counter(); + } + + bool MMU3::tool_change(uint8_t slot) { + if (!waitForMMUReady()) return false; + + if (slot != extruder) { + if ( + //findaDetectsFilament() + //!card.isStillPrinting() && !usb_timer.running() + !marlin_printingIsActive() + ) { + // If Tcodes are used manually through the serial + // we need to unload manually as well -- but only if FINDA detects filament + unload(); + } + + ReportingRAII rep(CommandInProgress::ToolChange); + FSensorBlockRunout blockRunout; + planner_synchronize(); + toolChangeCommon(slot); + } + return true; + } + + /** + * Handle special T?/Tx/Tc commands + * + * - T? Gcode to extrude shouldn't have to follow, load to extruder wheels is done automatically + * - Tx Same as T?, except nozzle doesn't have to be preheated. Tc must be placed after extruder nozzle is preheated to finish filament load. + * - Tc Load to nozzle after filament was prepared by Tx and extruder nozzle is already heated. + */ + bool MMU3::tool_change(char code, uint8_t slot) { + if (!waitForMMUReady()) return false; + + FSensorBlockRunout blockRunout; + + switch (code) { + case '?': { + waitForHotendTargetTemp(100, []{}); + load_to_nozzle(slot); + } + break; + + case 'x': { + thermal_setExtrudeMintemp(0); // Allow cold extrusion since Tx only loads to the gears not nozzle + tool_change(slot); + thermal_setExtrudeMintemp(EXTRUDE_MINTEMP); + } + break; + + case 'c': { + waitForHotendTargetTemp(100, []{}); + execute_load_to_nozzle_sequence(); + } + break; + } + + return true; + } + + void MMU3::get_statistics() { + logic.Statistics(); + } + + uint8_t __attribute__((noinline)) MMU3::get_current_tool() const { + return extruder == MMU2_NO_TOOL ? (uint8_t)FILAMENT_UNKNOWN : extruder; + } + + uint8_t MMU3::get_tool_change_tool() const { + return tool_change_extruder == MMU2_NO_TOOL ? (uint8_t)FILAMENT_UNKNOWN : tool_change_extruder; + } + + void MMU3::setCurrentTool(uint8_t ex) { + extruder = ex; + MMU2_ECHO_MSGRPGM(PSTR("MMU2tool=")); + SERIAL_ECHOLN((int)ex); + } + + bool MMU3::set_filament_type(uint8_t /*slot*/, uint8_t /*type*/) { + if (!waitForMMUReady()) return false; + + // @@TODO - this is not supported in the new MMU yet + // slot = slot; // @@TODO + // type = type; // @@TODO + // cmd_arg = filamentType; + // command(MMU_CMD_F0 + index); + + if (!manage_response(false, false)) { + // @@TODO failed to perform the command - retry + // Comment: how is it possible for a filament type set to fail? manage_response(true, true) + } + + return true; + } + + void MMU3::unloadInner() { + FSensorBlockRunout blockRunout; + filament_ramming(); + + // we assume the printer managed to relieve filament tip from the gears, + // so repeating that part in case of an MMU restart is not necessary + for (;;) { + Disable_E0(); + logic.UnloadFilament(); + if (manage_response(false, true)) break; + IncrementMMUFails(); + } + //MakeSound(Confirm); + + // no active tool + setCurrentTool(MMU2_NO_TOOL); + tool_change_extruder = MMU2_NO_TOOL; + } + + bool MMU3::unload() { + if (!waitForMMUReady()) return false; + + WaitForHotendTargetTempBeep(); + + // Scope for ReportingRAII + { + ReportingRAII rep(CommandInProgress::UnloadFilament); + unloadInner(); + } + + ScreenUpdateEnable(); + return true; + } + + void MMU3::cutFilamentInner(uint8_t slot) { + for (;;) { + Disable_E0(); + logic.CutFilament(slot); + if (manage_response(false, true)) break; + IncrementMMUFails(); + } + } + + bool MMU3::cut_filament(uint8_t slot, bool enableFullScreenMsg /*= true*/) { + if (!waitForMMUReady()) return false; + + if (enableFullScreenMsg) fullScreenMsgCut(slot); + + // Scope for ReportingRAII + { + if (findaDetectsFilament()) unload(); + + ReportingRAII rep(CommandInProgress::CutFilament); + cutFilamentInner(slot); + setCurrentTool(MMU2_NO_TOOL); + tool_change_extruder = MMU2_NO_TOOL; + //MakeSound(SoundType::Confirm); + } + ScreenUpdateEnable(); + return true; + } + + bool MMU3::loading_test(uint8_t slot) { + fullScreenMsgTest(slot); + tool_change(slot); + planner_synchronize(); + unload(); + ScreenUpdateEnable(); + return true; + } + + bool MMU3::load_to_feeder(uint8_t slot) { + if (!waitForMMUReady()) return false; + + fullScreenMsgLoad(slot); + + // Scope for ReportingRAII + { + ReportingRAII rep(CommandInProgress::LoadFilament); + for (;;) { + Disable_E0(); + logic.LoadFilament(slot); + if (manage_response(false, false)) break; + IncrementMMUFails(); + } + //MakeSound(SoundType::Confirm); + } + ScreenUpdateEnable(); + return true; + } + + bool MMU3::load_to_nozzle(uint8_t slot) { + if (!waitForMMUReady()) return false; + + WaitForHotendTargetTempBeep(); + + fullScreenMsgLoad(slot); + + // Scope for ReportingRAII + { + // Used for MMU-menu operation "Load to Nozzle" + ReportingRAII rep(CommandInProgress::ToolChange); + FSensorBlockRunout blockRunout; + + // Filament already loaded? Free it and shape its tip properly. + if (extruder != MMU2_NO_TOOL) filament_ramming(); + + toolChangeCommon(slot); + + // Finish loading to the nozzle with finely tuned steps. + execute_load_to_nozzle_sequence(); + //MakeSound(Confirm); + } + ScreenUpdateEnable(); + return true; + } + + bool MMU3::eject_filament(uint8_t slot, bool enableFullScreenMsg /* = true */) { + if (!waitForMMUReady()) return false; + + if (enableFullScreenMsg) fullScreenMsgEject(slot); + + // Scope for ReportingRAII + { + if (findaDetectsFilament()) + unload(); + + ReportingRAII rep(CommandInProgress::EjectFilament); + for (;;) { + Disable_E0(); + logic.EjectFilament(slot); + if (manage_response(false, true)) + break; + IncrementMMUFails(); + } + setCurrentTool(MMU2_NO_TOOL); + tool_change_extruder = MMU2_NO_TOOL; + //MakeSound(Confirm); + } + ScreenUpdateEnable(); + return true; + } + + void MMU3::button(uint8_t index) { + LogEchoEvent(F("button")); + logic.button(index); + } + + void MMU3::home(uint8_t mode) { + logic.home(mode); + } + + void MMU3::saveHotendTemp(bool turn_off_nozzle) { + if (mmu_print_saved & SavedState::Cooldown) return; + + if (turn_off_nozzle && !(mmu_print_saved & SavedState::CooldownPending)) { + Disable_E0(); + resume_hotend_temp = thermal_degTargetHotend(); + mmu_print_saved |= SavedState::CooldownPending; + LogEchoEvent(F("Heater cooldown pending")); + } + } + + void MMU3::saveAndPark(bool move_axes) { + if (mmu_print_saved == SavedState::None) { // First occurrence. Save current position, park print head, disable nozzle heater. + LogEchoEvent(F("Saving and parking")); + Disable_E0(); + planner_synchronize(); + + // In case a power panic happens while waiting for the user + // take a partial back up of print state into RAM (current position, etc.) + marlin_refresh_print_state_in_ram(); + + if (move_axes) { + mmu_print_saved |= SavedState::ParkExtruder; + resume_position = planner_current_position(); // save current pos + + // Do not lift Z, as it will double lift if there is another error + // right after the current one is solved. + + // Move XY aside + if (xy_are_trusted()) nozzle_park(); + } + } + } + + void MMU3::resumeHotendTemp() { + if ((mmu_print_saved & SavedState::CooldownPending)) { + // Clear the "pending" flag if we haven't cooled yet. + mmu_print_saved &= ~(SavedState::CooldownPending); + LogEchoEvent(F("Cooldown flag cleared")); + } + if ((mmu_print_saved & SavedState::Cooldown) && resume_hotend_temp) { + LogEchoEvent(F("Resuming Temp")); + // @@TODO MMU2_ECHO_MSGRPGM(PSTR("Restoring hotend temperature ")); + SERIAL_ECHOLN(resume_hotend_temp); + mmu_print_saved &= ~(SavedState::Cooldown); + thermal_setTargetHotend(resume_hotend_temp); + fullScreenMsgRestoringTemperature(); + // @todo better report the event and let the GUI do its work somewhere else + ReportErrorHookSensorLineRender(); + waitForHotendTargetTemp(100, [] { + marlin_manage_inactivity(true); + mmu3.mmu_loop_inner(false); + ReportErrorHookDynamicRender(); + }); + ScreenUpdateEnable(); // temporary hack to stop this locking the printer... + LogEchoEvent(F("Hotend temperature reached")); + ScreenClear(); + } + } + + void MMU3::resumeUnpark() { + if (mmu_print_saved & SavedState::ParkExtruder) { + LogEchoEvent(F("Resuming XYZ")); + + // Move XY to starting position, then Z + motion_blocking_move_xy(resume_position.x, resume_position.y, feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); + + // Move Z_AXIS to saved position + motion_blocking_move_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); + + // From this point forward, power panic should not use + // the partial backup in RAM since the extruder is no + // longer in parking position + marlin_clear_print_state_in_ram(); + + mmu_print_saved &= ~(SavedState::ParkExtruder); + } + } + + void MMU3::checkUserInput() { + auto btn = ButtonPressed(lastErrorCode); + + // Was a button pressed on the MMU itself instead of the LCD? + if (btn == Buttons::NoButton && lastButton != Buttons::NoButton) { + btn = lastButton; + lastButton = Buttons::NoButton; // Clear it. + } + + if (mmuLastErrorSource() == MMU3::ErrorSourcePrinter && btn != Buttons::NoButton) { + // When the printer has raised an error screen, and a button was selected + // the error screen should always be dismissed. + clearPrinterError(); + // A horrible hack - clear the explicit printer error allowing manage_response to recover on MMU's Finished state + // Moreover - if the MMU is currently doing something (like the LoadFilament - see comment above) + // we'll actually wait for it automagically in manage_response and after it finishes correctly, + // we'll issue another command (like toolchange) + } + + switch (btn) { + case Buttons::Left: + case Buttons::Middle: + case Buttons::Right: + SERIAL_ECHOPGM("checkUserInput-btnLMR "); + SERIAL_ECHOLN((int)buttons_to_uint8t(btn)); + resumeHotendTemp(); // Recover the hotend temp before we attempt to do anything else... + + if (mmuLastErrorSource() == MMU3::ErrorSourceMMU) + // Do not send a button to the MMU unless the MMU is in error state + button(buttons_to_uint8t(btn)); + + // A quick hack: for specific error codes move the E-motor every time. + // Not sure if we can rely on the fsensor. + // Just plan the move, let the MMU take over when it is ready + switch (lastErrorCode) { + case ErrorCode::FSENSOR_DIDNT_SWITCH_OFF: + case ErrorCode::FSENSOR_TOO_EARLY: helpUnloadToFinda(); break; + default: break; + } + break; + case Buttons::TuneMMU: + tune(); + break; + case Buttons::Load: + case Buttons::Eject: + // High level operation + setPrinterButtonOperation(btn); + break; + case Buttons::ResetMMU: + reset(ResetPin); // Cannot do power cycle on the MK3 + // ... but mmu2_power.cpp knows this and triggers a soft-reset instead. + break; + case Buttons::DisableMMU: + stop(); + //DisableMMUInSettings(); // stop() already does this... + status(); + break; + case Buttons::StopPrint: + // @@TODO Unsure if we should handle this high level operation at this spot + break; + default: break; + } + } + + /** + * Originally, this was used to wait for response and deal with timeout if necessary. + * The new protocol implementation enables much nicer and intense reporting, so this method will boil down + * just to verify the result of an issued command (which was basically the original idea) + * + * It is closely related to mmu_loop() (which corresponds to our ProtocolLogic::Step()), which does NOT perform any blocking wait for a command to finish. + * But - in case of an error, the command is not yet finished, but we must react accordingly - move the printhead elsewhere, stop heating, eat a cat or so. + * That's what's being done here... + */ + bool MMU3::manage_response(const bool move_axes, const bool turn_off_nozzle) { + mmu_print_saved = SavedState::None; + + MARLIN_KEEPALIVE_STATE_IN_PROCESS; + + Stopwatch nozzle_timer; + + for (;;) { + // in our new implementation, we know the exact state of the MMU at any moment, we do not have to wait for a timeout + // So in this case we should decide if the operation is: + // - still running -> wait normally in idle() + // - failed -> then do the safety moves on the printer like before + // - finished ok -> proceed with reading other commands + safe_delay_keep_alive(0); // calls logicStep() and remembers its return status + + if (mmu_print_saved & SavedState::CooldownPending) { + if (!nozzle_timer.isRunning()) { + nozzle_timer.start(); + LogEchoEvent(F("Cooling Timeout started")); + } + else if (nozzle_timer.duration() > (PAUSE_PARK_NOZZLE_TIMEOUT * 1000UL)) { // mins->msec. + mmu_print_saved &= ~(SavedState::CooldownPending); + mmu_print_saved |= SavedState::Cooldown; + thermal_setTargetHotend(0); + LogEchoEvent(F("Heater cooldown")); + } + } + else if (nozzle_timer.isRunning()) { + nozzle_timer.stop(); + LogEchoEvent(F("Cooling timer stopped")); + } + + switch (logicStepLastStatus) { + case Finished: + // command/operation completed, let Marlin continue its work + // the E may have some more moves to finish - wait for them + resumeHotendTemp(); + resumeUnpark(); // We can now travel back to the tower or wherever we were when we saved. + if (!TuneMenuEntered()) { + // If the error screen is sleeping (running 'Tune' menu) + // then don't reset retry attempts because we this will trigger + // an automatic retry attempt when 'Tune' button is selected. We want the + // error screen to appear once more so the user can hit 'Retry' button manually. + logic.ResetRetryAttempts(); // Reset the retry counter. + } + planner_synchronize(); + return true; + case Interrupted: + // now what :D ... big bad ... ramming, unload, retry the whole command originally issued + return false; + case VersionMismatch: // this basically means the MMU will be disabled until reconnected + checkUserInput(); + return true; + case PrinterError: + saveAndPark(move_axes); + saveHotendTemp(turn_off_nozzle); + checkUserInput(); + // if button pressed "Done", return true, otherwise stay within manage_response + // Please see checkUserInput() for details how we "leave" manage_response + break; + case CommandError: + case CommunicationTimeout: + case ProtocolError: + case ButtonPushed: + if (!logic.InAutoRetry()) { + // Don't proceed to the park/save if we are doing an autoretry. + saveAndPark(move_axes); + saveHotendTemp(turn_off_nozzle); + checkUserInput(); + } + break; + case CommunicationRecovered: // @@TODO communication recovered and maybe an error recovered as well + // Maybe the logic layer can detect the change of state a respond with one "Recovered" to be handled here + resumeHotendTemp(); + resumeUnpark(); + break; + case Processing: // Wait for the MMU to respond + default: break; + } + } + } + + StepStatus MMU3::logicStep(bool reportErrors) { + // Process any buttons before proceeding with another MMU Query + checkUserInput(); + + const StepStatus ss = logic.Step(); + switch (ss) { + + case Finished: + // At this point it is safe to trigger a runout and not interrupt the MMU protocol + checkFINDARunout(); + break; + + case Processing: + onMMUProgressMsg(logic.Progress()); + break; + + case ButtonPushed: + lastButton = logic.button(); + LogEchoEvent(F("MMU button pushed")); + checkUserInput(); // Process the button immediately + break; + + case Interrupted: + // can be silently handed over to a higher layer, no processing necessary at this spot + break; + + default: + if (reportErrors) { + switch (ss) { + + case CommandError: + reportError(logic.Error(), ErrorSourceMMU); + break; + + case CommunicationTimeout: + _state = xState::Connecting; + reportError(ErrorCode::MMU_NOT_RESPONDING, ErrorSourcePrinter); + break; + + case ProtocolError: + _state = xState::Connecting; + reportError(ErrorCode::PROTOCOL_ERROR, ErrorSourcePrinter); + break; + + case VersionMismatch: + stopKeepPowered(); + reportError(ErrorCode::VERSION_MISMATCH, ErrorSourcePrinter); + break; + + case PrinterError: + reportError(logic.PrinterError(), ErrorSourcePrinter); + break; + + default: + break; + } + } + } + + if (logic.Running()) _state = xState::Active; + + return ss; + } + + void MMU3::filament_ramming() { + execute_extruder_sequence(ramming_sequence, sizeof(ramming_sequence) / sizeof(E_Step)); + } + + void MMU3::execute_extruder_sequence(const E_Step *sequence, uint8_t steps) { + planner_synchronize(); + + const E_Step *step = sequence; + for (uint8_t i = steps; i > 0; --i) { + extruder_move(pgm_read_float(&(step->extrude)), pgm_read_float(&(step->feedRate))); + step++; + } + planner_synchronize(); // it looks like it's better to sync the moves at the end - smoother move (if the sequence is not too long). + + Disable_E0(); + } + + void MMU3::execute_load_to_nozzle_sequence() { + planner_synchronize(); + // Compensate for configurable Extra Loading Distance + planner_set_current_position_E(planner_get_current_position_E() - (logic.ExtraLoadDistance() - MMU3_FILAMENT_SENSOR_E_POSITION)); + execute_extruder_sequence(load_to_nozzle_sequence, sizeof(load_to_nozzle_sequence) / sizeof(load_to_nozzle_sequence[0])); + } + + void MMU3::reportError(ErrorCode ec, ErrorSource res) { + // Due to a potential lossy error reporting layers linked to this hook + // we'd better report everything to make sure especially the error states + // do not get lost. + // - The good news here is the fact, that the MMU reports the errors repeatedly until resolved. + // - The bad news is, that MMU not responding may repeatedly occur on printers not having the MMU at all. + // + // Not sure how to properly handle this situation, options: + // - skip reporting "MMU not responding" (at least for now) + // - report only changes of states (we can miss an error message) + // - maybe some combination of MMUAvailable + UseMMU flags and decide based on their state + // Right now the filtering of MMU_NOT_RESPONDING is done in ReportErrorHook() as it is not a problem if mmu2.cpp + + // Depending on the Progress code, we may want to do some action when an error occurs + switch (logic.Progress()) { + case ProgressCode::UnloadingToFinda: + unloadFilamentStarted = false; + planner_abort_queued_moves(); // Abort excess E-moves to be safe + break; + case ProgressCode::FeedingToFSensor: + // FSENSOR error during load. Make sure E-motor stops moving. + loadFilamentStarted = false; + planner_abort_queued_moves(); // Abort excess E-moves to be safe + break; + default: break; + } + + if (ec != lastErrorCode) { // deduplicate: only report changes in error codes into the log + lastErrorCode = ec; + lastErrorSource = res; + LogErrorEvent(PrusaErrorTitle(PrusaErrorCodeIndex(ec))); + + if (ec != ErrorCode::OK && ec != ErrorCode::FILAMENT_EJECTED && ec != ErrorCode::FILAMENT_CHANGE) { + IncrementMMUFails(); + + // Check if it is a "power" failure. TMC-related errors are considered power failures. + static constexpr uint16_t tmcMask = + ( (uint16_t)ErrorCode::TMC_IOIN_MISMATCH + | (uint16_t)ErrorCode::TMC_RESET + | (uint16_t)ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP + | (uint16_t)ErrorCode::TMC_SHORT_TO_GROUND + | (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_WARN + | (uint16_t)ErrorCode::TMC_OVER_TEMPERATURE_ERROR + | (uint16_t)ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION ) & 0x7FFFU; // skip the top bit + + static_assert(tmcMask == 0x7E00); // Just make sure we fail compilation if any of the TMC error codes change + + if ((uint16_t)ec & tmcMask) { // @@TODO can be optimized to uint8_t operation + // TMC-related errors are from 0x8200 higher + incrementTMCFailures(); + } + } + } + + if (!retryIfPossible(ec)) + // If retry attempts are all used up + // or if 'Retry' operation is not available + // raise the MMU error screen and wait for user input + ReportErrorHook((CommandInProgress)logic.CommandInProgress(), ec, uint8_t(lastErrorSource)); + } + + void MMU3::reportProgress(ProgressCode pc) { + ReportProgressHook((CommandInProgress)logic.CommandInProgress(), pc); + LogEchoEvent(ProgressCodeToText(pc)); + } + + void MMU3::onMMUProgressMsg(ProgressCode pc) { + if (pc != lastProgressCode) + onMMUProgressMsgChanged(pc); + else + onMMUProgressMsgSame(pc); + } + + void MMU3::onMMUProgressMsgChanged(ProgressCode pc) { + reportProgress(pc); + lastProgressCode = pc; + switch (pc) { + case ProgressCode::UnloadingToFinda: + if ( (CommandInProgress)logic.CommandInProgress() == CommandInProgress::UnloadFilament + || ((CommandInProgress)logic.CommandInProgress() == CommandInProgress::ToolChange) + ) { + // If MK3S sent U0 command, ramming sequence takes care of releasing the filament. + // If Toolchange is done while printing, PrusaSlicer takes care of releasing the filament + // If printing is not in progress, ToolChange will issue a U0 command. + break; + } + else { + // We're likely recovering from an MMU error + planner_synchronize(); + unloadFilamentStarted = true; + helpUnloadToFinda(); + } + break; + case ProgressCode::FeedingToFSensor: + // prepare for the movement of the E-motor + planner_synchronize(); + loadFilamentStarted = true; + break; + default: break; // do nothing yet + } + } + + void __attribute__((noinline)) MMU3::helpUnloadToFinda() { + extruder_move(-MMU3_RETRY_UNLOAD_TO_FINDA_LENGTH, MMU3_RETRY_UNLOAD_TO_FINDA_FEED_RATE); + } + + void MMU3::onMMUProgressMsgSame(ProgressCode pc) { + const uint8_t pulley_slow_feedrate = logic.PulleySlowFeedRate(); + const float extrude_distance = _MIN(_MAX(EXTRUDE_MAXLENGTH - 1, 1), pulley_slow_feedrate); + + switch (pc) { + case ProgressCode::UnloadingToFinda: + if (unloadFilamentStarted && !planner_any_moves()) { // Only plan a move if there is no move ongoing + switch (WhereIsFilament()) { + case FilamentState::AT_FSENSOR: + case FilamentState::IN_NOZZLE: + case FilamentState::UNAVAILABLE: // actually Unavailable makes sense as well to start the E-move to release the filament from the gears + helpUnloadToFinda(); + break; + default: + unloadFilamentStarted = false; + } + } + break; + + case ProgressCode::FeedingToFSensor: + if (loadFilamentStarted) { + switch (WhereIsFilament()) { + case FilamentState::AT_FSENSOR: + // fsensor triggered, finish FeedingToExtruder state + loadFilamentStarted = false; + + // Abort any excess E-move from the planner queue + planner_abort_queued_moves(); + + // After the MMU knows the FSENSOR is triggered it will: + // 1. Push the filament by additional 30mm (see fsensorToNozzle) + // 2. Disengage the idler and push another 2mm. + extruder_move(logic.ExtraLoadDistance() + 2, logic.PulleySlowFeedRate()); + break; + case FilamentState::NOT_PRESENT: + // fsensor not triggered, continue moving extruder + // + // Instead of doing a very long extrude as in PrusaFirmware, + // Marlin's own MMU2s code has a better approach to this by spinning + // the extruder indefinitely... + // + // this ensures that while the MMU is pushing the filament, + // the extruder will keep rotating, preventing the filament to hit + // the extruder gears... + while (planner.movesplanned() < 3) { + extruder_move(extrude_distance, pulley_slow_feedrate, false); + } + break; + default: break; // Abort here? + } + } + break; + + default: break; // do nothing yet + } + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3.h b/Marlin/src/feature/mmu3/mmu3.h new file mode 100644 index 0000000000..968f76a912 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3.h @@ -0,0 +1,419 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2.h + */ + +#include "mmu3_state.h" +#include "mmu3_marlin.h" + +#include "mmu3_protocol_logic.h" + +#include "../../MarlinCore.h" + + #ifdef __AVR__ + typedef float feedRate_t; + #else + //#include + #endif + + struct E_Step { + float extrude; //!< extrude distance in mm + float feedRate; //!< feed rate in mm/s + }; + + static constexpr E_Step ramming_sequence[] PROGMEM = { MMU3_RAMMING_SEQUENCE }; + static constexpr E_Step load_to_nozzle_sequence[] PROGMEM = { MMU3_LOAD_TO_NOZZLE_SEQUENCE }; + + namespace MMU3 { + + // general MMU setup for MK3 + enum : uint8_t { + FILAMENT_UNKNOWN = 0xFFU + }; + + struct Version { + uint8_t major, minor, build; + }; + + // Top-level interface between Logic and Marlin. + // Intentionally named MMU3 to be (almost) a drop-in replacement for the previous implementation. + // Most of the public methods share the original naming convention as well. + class MMU3 { + public: + MMU3(); + + // Powers ON the MMU, then initializes the UART and protocol logic + void start(); + + // Stops the protocol logic, closes the UART, powers OFF the MMU + void stop(); + + // Serial output of MMU state + void status(); + + xState state() const { return _state; } + + bool enabled() const { mmu_hw_enabled = state() == xState::Active; return mmu_hw_enabled; } + + // Different levels of resetting the MMU + enum ResetForm : uint8_t { + Software = 0, //!< sends a X0 command into the MMU, the MMU will watchdog-reset itself + ResetPin = 1, //!< trigger the reset pin of the MMU + CutThePower = 2, //!< power off and power on (that includes +5V and +24V power lines) + EraseEEPROM = 42, //!< erase MMU EEPROM and then perform a software reset + }; + + // Saved print state on error. + enum SavedState : uint8_t { + None = 0, // No state saved. + ParkExtruder = 1, // The extruder was parked. + Cooldown = 2, // The extruder was allowed to cool. + CooldownPending = 4, + }; + + // Source of operation error + enum ErrorSource : uint8_t { + ErrorSourcePrinter = 0, + ErrorSourceMMU = 1, + ErrorSourceNone = 0xFF, + }; + + // Tune value in MMU registers as a way to recover from errors + // e.g. Idler Stallguard threshold + void tune(); + + // Perform a reset of the MMU + // @param level physical form of the reset + void reset(ResetForm level); + + // Power off the MMU (cut the power) + void powerOff(); + + // Power on the MMU + void powerOn(); + + // Read from a MMU register (See gcode M707) + // @param address Address of register in hexadecimal + // @return true upon success + bool readRegister(uint8_t address); + + // Write from a MMU register (See gcode M708) + // @param address Address of register in hexadecimal + // @param data Data to write to register + // @return true upon success + bool writeRegister(uint8_t address, uint16_t data); + + // The main loop of MMU processing. + // Doesn't loop (block) inside, performs just one step of logic state machines. + // Also, internally it prevents recursive entries. + void mmu_loop(); + + // The main MMU command - select a different slot + // @param slot of the slot to be selected + // @return false if the operation cannot be performed (Stopped) + bool tool_change(uint8_t slot); + + // Handling of special Tx, Tc, T? commands + bool tool_change(char code, uint8_t slot); + + // Unload of filament in collaboration with the MMU. + // That includes rotating the printer's extruder in order to release filament. + // @return false if the operation cannot be performed (Stopped or cold extruder) + bool unload(); + + // Load (insert) filament just into the MMU (not into printer's nozzle) + // @return false if the operation cannot be performed (Stopped) + bool load_to_feeder(uint8_t slot); + + // Load (push) filament from the MMU into the printer's nozzle + // @return false if the operation cannot be performed (Stopped or cold extruder) + bool load_to_nozzle(uint8_t slot); + + // Move MMU's selector aside and push the selected filament forward. + // Usable for improving filament's tip or pulling the remaining piece of filament out completely. + bool eject_filament(uint8_t slot, bool enableFullScreenMsg=true); + + // Issue a Cut command into the MMU + // Requires unloaded filament from the printer (obviously) + // @return false if the operation cannot be performed (Stopped) + bool cut_filament(uint8_t slot, bool enableFullScreenMsg=true); + + // Issue a planned request for statistics data from MMU + void get_statistics(); + + // Issue a Try-Load command + // It behaves very similarly like a ToolChange, but it doesn't load the filament + // all the way down to the nozzle. The sole purpose of this operation + // is to check, that the filament will be ready for printing. + // @param slot index of slot to be tested + // @return true + bool loading_test(uint8_t slot); + + // @return the active filament slot index (0-4) or 0xff in case of no active tool + uint8_t get_current_tool() const; + + // @return The filament slot index (0 to 4) that will be loaded next, 0xff in case of no active tool change + uint8_t get_tool_change_tool() const; + + bool set_filament_type(uint8_t slot, uint8_t type); + + // Issue a "button" click into the MMU - to be used from Error screens of the MMU + // to select one of the 3 possible options to resolve the issue + void button(uint8_t index); + + // Issue an explicit "homing" command into the MMU + void home(uint8_t mode); + + // @return current state of FINDA (true=filament present, false=filament not present) + bool findaDetectsFilament() const { return logic.findaPressed(); } + + uint16_t totalFailStatistics() const { return logic.FailStatistics(); } + + // @return Current error code + ErrorCode mmuCurrentErrorCode() const { return logic.Error(); } + + // @return Command in progress + uint8_t getCommandInProgress() const { return logic.CommandInProgress(); } + + // @return Last error source + ErrorSource mmuLastErrorSource() const { return lastErrorSource; } + + // @return Last error code + ErrorCode getLastErrorCode() const { return lastErrorCode; } + + // @return the version of the connected MMU FW. + // In the future we'll return the truly detected FW version + Version getMMUFWVersion() const { + if (state() == xState::Active) { + return { logic.mmuFwVersionMajor(), logic.mmuFwVersionMinor(), logic.mmuFwVersionRevision() }; + } + else { + return { 0, 0, 0 }; + } + } + + // Method to read-only mmu_print_saved + bool MMU_PRINT_SAVED() const { return mmu_print_saved != SavedState::None; } + + // Automagically "press" a Retry button if we have any retry attempts left + // @param ec ErrorCode enum value + // @return true if auto-retry is ongoing, false when retry is unavailable or retry attempts are all used up + bool retryIfPossible(const ErrorCode ec); + + // @return count for toolchange in current print + uint16_t toolChangeCounter() const { return toolchange_counter; } + + // Set toolchange counter to zero + void resetToolChangeCounter() { toolchange_counter = 0; } + + uint16_t tmcFailures() const { return _tmcFailures; } + void incrementTMCFailures() { ++_tmcFailures; } + void resetTMCFailures() { _tmcFailures = 0; } + + // Retrieve cached value parsed from readRegister() + // or using M707 + uint16_t getLastReadRegisterValue() const { + return lastReadRegisterValue; + } + void invokeErrorScreen(const ErrorCode ec) { + if (logic.CommandInProgress()) return; // MMU must not be busy + if (lastErrorCode == ec) return; // The error code is not a duplicate + if (mmuCurrentErrorCode() == ErrorCode::OK) { // The protocol must not be in error state + reportError(ec, ErrorSource::ErrorSourcePrinter); + } + } + + void clearPrinterError() { + logic.clearPrinterError(); + lastErrorCode = ErrorCode::OK; + lastErrorSource = ErrorSource::ErrorSourceNone; + } + + // @brief Queue a button operation which the printer can act upon + // @param btn Button operation + void setPrinterButtonOperation(Buttons btn) { + printerButtonOperation = btn; + } + + // @brief Get the printer button operation + // @return currently set printer button operation, it can be NoButton if nothing is queued + Buttons getPrinterButtonOperation() { + return printerButtonOperation; + } + + void clearPrinterButtonOperation() { + printerButtonOperation = Buttons::NoButton; + } + + static uint8_t cutter_mode; // mode 0:disabled | 1:enabled | 2:always (EXPERIMENTAL) + static int cutter_mode_addr; // EEPROM addr for cutter enabled setting + static uint8_t stealth_mode; // stealth mode + static int stealth_mode_addr; // EEPROM addr for stealth_mode setting + static bool mmu_hw_enabled; // MMU hardware can be Enabled/Disabled + // with the M709 S0 or M709 S1 commands + // and the last state is stored in the + // EEPROM + + static int mmu_hw_enabled_addr; // EEPROM addr for mmu_hw_enabled + + bool e_active(); + + #ifndef UNITTEST + private: + #endif + + // Perform software self-reset of the MMU (sends an X0 command) + void resetX0(); + + // Perform software self-reset of the MMU + erase its EEPROM (sends X2a command) + void resetX42(); + + // Trigger reset pin of the MMU + void triggerResetPin(); + + // Perform power cycle of the MMU (cold boot) + // Please note this is a blocking operation (sleeps for some time inside while doing the power cycle) + void powerCycle(); + + // Stop the communication, but keep the MMU powered on (for scenarios with incorrect FW version) + void stopKeepPowered(); + + // Along with the mmu_loop method, this loops until a response from the MMU is received and acts upon. + // In case of an error, it parks the print head and turns off nozzle heating + // @return false if the command could not have been completed (MMU interrupted) + [[nodiscard]] bool manage_response(const bool move_axes, const bool turn_off_nozzle); + + // The inner private implementation of mmu_loop() + // which is NOT (!!!) recursion-guarded. Use caution - but we do need it during waiting for hotend resume to keep comms alive! + // @param reportErrors true if Errors should raise MMU Error screen, false otherwise + void mmu_loop_inner(bool reportErrors); + + // Performs one step of the protocol logic state machine + // and reports progress and errors if needed to attached ExtUIs. + // Updates the global state of MMU (Active/Connecting/Stopped) at runtime, see @ref State + // @param reportErrors true if Errors should raise MMU Error screen, false otherwise + StepStatus logicStep(bool reportErrors); + + void filament_ramming(); + void execute_extruder_sequence(const E_Step *sequence, uint8_t steps); + void execute_load_to_nozzle_sequence(); + + // Reports an error into attached ExtUIs + // @param ec error code, see ErrorCode + // @param res reporter error source, is either Printer (0) or MMU (1) + void reportError(ErrorCode ec, ErrorSource res); + + // Reports progress of operations into attached ExtUIs + // @param pc progress code, see ProgressCode + void reportProgress(ProgressCode pc); + + // Responds to a change of MMU's progress + // - plans additional steps, e.g. starts the E-motor after fsensor trigger + // The function is quite complex, because it needs to handle asynchronnous + // progress and error reports coming from the MMU without an explicit command + // - typically after MMU's start or after some HW issue on the MMU. + // It must ensure, that calls to @ref reportProgress and/or @ref reportError are + // only executed after @ref BeginReport has been called first. + void onMMUProgressMsg(ProgressCode pc); + // Progress code changed - act accordingly + void onMMUProgressMsgChanged(ProgressCode pc); + // Repeated calls when progress code remains the same + void onMMUProgressMsgSame(ProgressCode pc); + + // @brief Save hotend temperature and set flag to cooldown hotend after 60 minutes + // @param turn_off_nozzle if true, the hotend temperature will be set to 0degC after 60 minutes + void saveHotendTemp(bool turn_off_nozzle); + + // Save print and park the print head + void saveAndPark(bool move_axes); + + // Resume hotend temperature, if it was cooled. Safe to call if we aren't saved. + void resumeHotendTemp(); + + // Resume position, if the extruder was parked. Safe to all if state was not saved. + void resumeUnpark(); + + // Check for any button/user input coming from the printer's UI + void checkUserInput(); + + // @brief Check whether to trigger a FINDA runout. If triggered this function will call M600 AUTO + // if SpoolJoin is enabled, otherwise M600 is called without AUTO which will prompt the user + // for the next filament slot to use + void checkFINDARunout(); + + // Entry check of all external commands. + // It can wait until the MMU becomes ready. + // Optionally, it can also emit/display an error screen and the user can decide what to do next. + // @return false if the MMU is not ready to perform the command (for whatever reason) + bool waitForMMUReady(); + + // After MMU completes a tool-change command + // the printer will push the filament by a constant distance. If the Fsensor untriggers + // at any moment the test fails. Else the test passes, and the E-motor retracts the + // filament back to its original position. + // @return false if test fails, true otherwise + bool verifyFilamentEnteredPTFE(); + + // Common processing of pushing filament into the extruder - shared by tool_change, load_to_nozzle and probably others + void toolChangeCommon(uint8_t slot); + bool toolChangeCommonOnce(uint8_t slot); + + void helpUnloadToFinda(); + void unloadInner(); + void cutFilamentInner(uint8_t slot); + + void setCurrentTool(uint8_t ex); + + ProtocolLogic logic; //!< implementation of the protocol logic layer + uint8_t extruder; //!< currently active slot in the MMU ... somewhat... not sure where to get it from yet + uint8_t tool_change_extruder; //!< only used for UI purposes + + xyz_pos_t resume_position; + int16_t resume_hotend_temp; + + ProgressCode lastProgressCode = ProgressCode::OK; + ErrorCode lastErrorCode = ErrorCode::MMU_NOT_RESPONDING; + ErrorSource lastErrorSource = ErrorSource::ErrorSourceNone; + Buttons lastButton = Buttons::NoButton; + uint16_t lastReadRegisterValue = 0; + Buttons printerButtonOperation = Buttons::NoButton; + + StepStatus logicStepLastStatus; + + enum xState _state; + + uint8_t mmu_print_saved; + bool loadFilamentStarted; + bool unloadFilamentStarted; + + uint16_t toolchange_counter; + uint16_t _tmcFailures; + }; + + } // MMU3 + +// following Marlin's way of doing stuff - one and only instance of MMU implementation in the code base +// + avoiding buggy singletons on the AVR platform +extern MMU3::MMU3 mmu3; diff --git a/Marlin/src/feature/mmu3/mmu3_crc.cpp b/Marlin/src/feature/mmu3/mmu3_crc.cpp new file mode 100644 index 0000000000..2e752c4873 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_crc.cpp @@ -0,0 +1,53 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_crc.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3_crc.h" + +#ifdef __AVR__ + #include +#endif + +namespace modules { + +namespace crc { + +uint8_t CRC8::CCITT_update(uint8_t crc, uint8_t b) { + #ifdef __AVR__ + return _crc8_ccitt_update(crc, b); + #else + return CCITT_updateCX(crc, b); + #endif +} + +} // namespace crc + +} // namespace modules + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_crc.h b/Marlin/src/feature/mmu3/mmu3_crc.h new file mode 100644 index 0000000000..f7221b38f5 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_crc.h @@ -0,0 +1,73 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_crc.h + */ + +#include + +namespace modules { + +// prevent silly indenting of the whole file + +// Contains all the necessary functions for computation of CRC +namespace crc { + +class CRC8 { +public: + // Compute/update CRC8 CCIIT from 8bits. + // Details: https://www.nongnu.org/avr-libc/user-manual/group__util__crc.html + static uint8_t CCITT_update(uint8_t crc, uint8_t b); + + static constexpr uint8_t CCITT_updateCX(uint8_t crc, uint8_t b) { + uint8_t data = crc ^ b; + for (uint8_t i = 0; i < 8; i++) { + if ((data & 0x80U) != 0) { + data <<= 1U; + data ^= 0x07U; + } + else { + data <<= 1U; + } + } + return data; + } + + // Compute/update CRC8 CCIIT from 16bits (convenience wrapper) + static constexpr uint8_t CCITT_updateW(uint8_t crc, uint16_t w) { + union U { + uint8_t b[2]; + uint16_t w; + explicit constexpr inline U(uint16_t w) + : w(w) {} + } + u(w); + return CCITT_updateCX(CCITT_updateCX(crc, u.b[0]), u.b[1]); + } +}; + +} // namespace crc + + +} // namespace modules diff --git a/Marlin/src/feature/mmu3/mmu3_error_converter.cpp b/Marlin/src/feature/mmu3/mmu3_error_converter.cpp new file mode 100644 index 0000000000..7ed53c0229 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_error_converter.cpp @@ -0,0 +1,376 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_error_converter.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "../../core/language.h" +#include "mmu3_error_converter.h" +#include "mmu_hw/error_codes.h" +#include "mmu_hw/errors_list.h" + +namespace MMU3 { + + static ButtonOperations buttonSelectedOperation = ButtonOperations::NoOperation; + + // we don't have a constexpr find_if in C++17/STL yet + template + constexpr InputIt find_if_cx(InputIt first, InputIt last, UnaryPredicate p) { + for (; first != last; ++first) { + if (p(*first)) return first; + } + return last; + } + + // Making a constexpr FindError should instruct the compiler to optimize the + // PrusaErrorCodeIndex in such a way that no searching will ever be done at + // runtime. A call to FindError then compiles to a single instruction even on + // the AVR. + // static constexpr uint8_t FindErrorIndex(uint16_t pec) { + static uint8_t FindErrorIndex(uint16_t pec) { + constexpr uint16_t errorCodesSize = sizeof(errorCodes) / sizeof(errorCodes[0]); + constexpr const auto *errorCodesEnd = errorCodes + errorCodesSize; + const auto *i = find_if_cx(errorCodes, errorCodesEnd, [pec](uint16_t ed) { + return ed == pec; + }); + return (i != errorCodesEnd) ? (i - errorCodes) : (errorCodesSize - 1); + } + + // check that the searching algorithm works + // static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER) == 0); + // static_assert( FindErrorIndex(ERR_MECHANICAL_FINDA_FILAMENT_STUCK) == 1); + // static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER) == 2); + // static_assert( FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK) == 3); + + constexpr ErrorCode operator&(ErrorCode a, ErrorCode b) { + return (ErrorCode)((uint16_t)a & (uint16_t)b); + } + + constexpr bool ContainsBit(ErrorCode ec, ErrorCode mask) { + return (uint16_t)ec & (uint16_t)mask; + } + + uint8_t PrusaErrorCodeIndex(const ErrorCode ec) { + switch (ec) { + case ErrorCode::FINDA_DIDNT_SWITCH_ON: + return FindErrorIndex(ERR_MECHANICAL_FINDA_DIDNT_TRIGGER); + case ErrorCode::FINDA_DIDNT_SWITCH_OFF: + return FindErrorIndex(ERR_MECHANICAL_FINDA_FILAMENT_STUCK); + case ErrorCode::FSENSOR_DIDNT_SWITCH_ON: + return FindErrorIndex(ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER); + case ErrorCode::FSENSOR_DIDNT_SWITCH_OFF: + return FindErrorIndex(ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK); + case ErrorCode::FSENSOR_TOO_EARLY: + return FindErrorIndex(ERR_MECHANICAL_FSENSOR_TOO_EARLY); + case ErrorCode::FINDA_FLICKERS: + return FindErrorIndex(ERR_MECHANICAL_INSPECT_FINDA); + case ErrorCode::LOAD_TO_EXTRUDER_FAILED: + return FindErrorIndex(ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED); + case ErrorCode::FILAMENT_EJECTED: + return FindErrorIndex(ERR_SYSTEM_FILAMENT_EJECTED); + case ErrorCode::FILAMENT_CHANGE: + return FindErrorIndex(ERR_SYSTEM_FILAMENT_CHANGE); + + case ErrorCode::STALLED_PULLEY: + case ErrorCode::MOVE_PULLEY_FAILED: + return FindErrorIndex(ERR_MECHANICAL_PULLEY_CANNOT_MOVE); + + case ErrorCode::HOMING_SELECTOR_FAILED: + return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_HOME); + case ErrorCode::MOVE_SELECTOR_FAILED: + return FindErrorIndex(ERR_MECHANICAL_SELECTOR_CANNOT_MOVE); + + case ErrorCode::HOMING_IDLER_FAILED: + return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_HOME); + case ErrorCode::MOVE_IDLER_FAILED: + return FindErrorIndex(ERR_MECHANICAL_IDLER_CANNOT_MOVE); + + case ErrorCode::MMU_NOT_RESPONDING: + return FindErrorIndex(ERR_CONNECT_MMU_NOT_RESPONDING); + case ErrorCode::PROTOCOL_ERROR: + return FindErrorIndex(ERR_CONNECT_COMMUNICATION_ERROR); + case ErrorCode::FILAMENT_ALREADY_LOADED: + return FindErrorIndex(ERR_SYSTEM_FILAMENT_ALREADY_LOADED); + case ErrorCode::INVALID_TOOL: + return FindErrorIndex(ERR_SYSTEM_INVALID_TOOL); + case ErrorCode::QUEUE_FULL: + return FindErrorIndex(ERR_SYSTEM_QUEUE_FULL); + case ErrorCode::VERSION_MISMATCH: + return FindErrorIndex(ERR_SYSTEM_FW_UPDATE_NEEDED); + case ErrorCode::INTERNAL: + return FindErrorIndex(ERR_SYSTEM_FW_RUNTIME_ERROR); + case ErrorCode::FINDA_VS_EEPROM_DISREPANCY: + return FindErrorIndex(ERR_SYSTEM_UNLOAD_MANUALLY); + case ErrorCode::MCU_UNDERVOLTAGE_VCC: + return FindErrorIndex(ERR_ELECTRICAL_MMU_MCU_ERROR); + default: break; + } + + // Electrical issues which can be detected somehow. + // Need to be placed before TMC-related errors in order to process couples of error bits between single ones + // and to keep the code size down. + if (ContainsBit(ec, ErrorCode::TMC_PULLEY_BIT)) { + if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) + return FindErrorIndex(ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED); + } + else if (ContainsBit(ec, ErrorCode::TMC_SELECTOR_BIT)) { + if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) + return FindErrorIndex(ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED); + } + else if (ContainsBit(ec, ErrorCode::TMC_IDLER_BIT)) { + if ((ec & ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) == ErrorCode::MMU_SOLDERING_NEEDS_ATTENTION) + return FindErrorIndex(ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED); + } + + // TMC-related errors - multiple of these can occur at once + // - in such a case we report the first which gets found/converted into Prusa-Error-Codes (usually the fact, that one TMC has an issue is serious enough) + // By carefully ordering the checks here we can prioritize the errors being reported to the user. + if (ContainsBit(ec, ErrorCode::TMC_PULLEY_BIT)) { + if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_RESET)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET); + if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN)) + return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR)) + return FindErrorIndex(ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR); + } + else if (ContainsBit(ec, ErrorCode::TMC_SELECTOR_BIT)) { + if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_RESET)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET); + if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN)) + return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR)) + return FindErrorIndex(ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR); + } + else if (ContainsBit(ec, ErrorCode::TMC_IDLER_BIT)) { + if (ContainsBit(ec, ErrorCode::TMC_IOIN_MISMATCH)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_RESET)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET); + if (ContainsBit(ec, ErrorCode::TMC_UNDERVOLTAGE_ON_CHARGE_PUMP)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR); + if (ContainsBit(ec, ErrorCode::TMC_SHORT_TO_GROUND)) + return FindErrorIndex(ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_WARN)) + return FindErrorIndex(ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT); + if (ContainsBit(ec, ErrorCode::TMC_OVER_TEMPERATURE_ERROR)) + return FindErrorIndex(ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR); + } + + // if nothing got caught, return a generic runtime error + return FindErrorIndex(ERR_OTHER_UNKNOWN_ERROR); + } + + uint16_t PrusaErrorCode(const uint8_t i) { return pgm_read_word(errorCodes + i); } + + FSTR_P const PrusaErrorTitle(const uint8_t i) { return (FSTR_P const)pgm_read_ptr(errorTitles + i); } + FSTR_P const PrusaErrorDesc(const uint8_t i) { return (FSTR_P const)pgm_read_ptr(errorDescs + i); } + + uint8_t PrusaErrorButtons(const uint8_t i) { return pgm_read_byte(errorButtons + i); } + + FSTR_P const PrusaErrorButtonTitle(const uint8_t bi) { + // -1 represents the hidden NoOperation button which is not drawn in any way + return (FSTR_P const)pgm_read_ptr(btnOperation + bi - 1); + } + + Buttons ButtonPressed(const ErrorCode ec) { + if (buttonSelectedOperation == ButtonOperations::NoOperation || buttonSelectedOperation == ButtonOperations::MoreInfo) + return Buttons::NoButton; // no button + + const auto result = ButtonAvailable(ec); + buttonSelectedOperation = ButtonOperations::NoOperation; // Reset operation + + return result; + } + + Buttons ButtonAvailable(const ErrorCode ec) { + uint8_t ei = PrusaErrorCodeIndex(ec); + + // The list of responses which occur in mmu error dialogs + // Return button index or perform some action on the MK3 by itself (like Reset MMU) + // Based on Prusa-Error-Codes errors_list.h + // So far hardcoded, but should be generated in the future + switch (PrusaErrorCode(ei)) { + case ERR_MECHANICAL_FINDA_DIDNT_TRIGGER: + case ERR_MECHANICAL_FINDA_FILAMENT_STUCK: + case ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER: + case ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK: + case ERR_MECHANICAL_FSENSOR_TOO_EARLY: + case ERR_MECHANICAL_INSPECT_FINDA: + case ERR_MECHANICAL_SELECTOR_CANNOT_MOVE: + case ERR_MECHANICAL_IDLER_CANNOT_MOVE: + case ERR_MECHANICAL_PULLEY_CANNOT_MOVE: + case ERR_SYSTEM_UNLOAD_MANUALLY: + switch (buttonSelectedOperation) { + // may be allow move selector right and left in the future + case ButtonOperations::Retry: // "Repeat action" + return Buttons::Middle; + default: + break; + } + break; + case ERR_MECHANICAL_SELECTOR_CANNOT_HOME: + case ERR_MECHANICAL_IDLER_CANNOT_HOME: + switch (buttonSelectedOperation) { + // may be allow move selector right and left in the future + case ButtonOperations::Tune: // Tune Stallguard threshold + return Buttons::TuneMMU; + case ButtonOperations::Retry: // "Repeat action" + return Buttons::Middle; + default: + break; + } + break; + case ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED: + case ERR_SYSTEM_FILAMENT_EJECTED: + switch (buttonSelectedOperation) { + case ButtonOperations::Continue: // User solved the serious mechanical problem by hand - there is no other way around + return Buttons::Middle; + default: + break; + } + break; + case ERR_SYSTEM_FILAMENT_CHANGE: + switch (buttonSelectedOperation) { + case ButtonOperations::Load: + return Buttons::Load; + case ButtonOperations::Eject: + return Buttons::Eject; + default: + break; + } + break; + case ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT: + case ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT: + case ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT: + switch (buttonSelectedOperation) { + case ButtonOperations::Continue: // "Continue" + return Buttons::Left; + case ButtonOperations::ResetMMU: // "Reset MMU" + return Buttons::ResetMMU; + default: + break; + } + break; + + case ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR: + case ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR: + case ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR: + + case ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR: + case ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR: + case ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR: + + case ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET: + case ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET: + case ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET: + + case ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR: + case ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR: + case ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR: + + case ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED: + case ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED: + case ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED: + + case ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED: + case ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED: + case ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED: + + case ERR_SYSTEM_QUEUE_FULL: + case ERR_SYSTEM_FW_RUNTIME_ERROR: + case ERR_ELECTRICAL_MMU_MCU_ERROR: + switch (buttonSelectedOperation) { + case ButtonOperations::ResetMMU: // "Reset MMU" + return Buttons::ResetMMU; + default: + break; + } + break; + case ERR_CONNECT_MMU_NOT_RESPONDING: + case ERR_CONNECT_COMMUNICATION_ERROR: + case ERR_SYSTEM_FW_UPDATE_NEEDED: + switch (buttonSelectedOperation) { + case ButtonOperations::DisableMMU: // "Disable" + return Buttons::DisableMMU; + case ButtonOperations::ResetMMU: // "ResetMMU" + return Buttons::ResetMMU; + default: + break; + } + break; + case ERR_SYSTEM_FILAMENT_ALREADY_LOADED: + switch (buttonSelectedOperation) { + case ButtonOperations::Unload: // "Unload" + return Buttons::Left; + case ButtonOperations::Continue: // "Proceed/Continue" + return Buttons::Right; + default: + break; + } + break; + + case ERR_SYSTEM_INVALID_TOOL: + switch (buttonSelectedOperation) { + case ButtonOperations::StopPrint: // "Stop print" + return Buttons::StopPrint; + case ButtonOperations::ResetMMU: // "Reset MMU" + return Buttons::ResetMMU; + default: + break; + } + break; + + default: + break; + } + + return Buttons::NoButton; + } + + void SetButtonResponse(ButtonOperations rsp) { + buttonSelectedOperation = rsp; + } + + ButtonOperations GetButtonResponse() { + return buttonSelectedOperation; + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_error_converter.h b/Marlin/src/feature/mmu3/mmu3_error_converter.h new file mode 100644 index 0000000000..93a4d5e455 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_error_converter.h @@ -0,0 +1,73 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_error_converter.h + */ + +#include +#include +#include "mmu_hw/buttons.h" +#include "mmu_hw/error_codes.h" + + namespace MMU3 { + + // Translates MMU3::ErrorCode into an index of Prusa-Error-Codes + // Basically this is the way to obtain an index into all other functions in this API + uint8_t PrusaErrorCodeIndex(const ErrorCode ec); + + // @return pointer to a PROGMEM string representing the Title of the Prusa-Error-Codes error + // @param i index of the error - obtained by calling ErrorCodeIndex + FSTR_P const PrusaErrorTitle(const uint8_t i); + + // @return pointer to a PROGMEM string representing the multi-page Description of the Prusa-Error-Codes error + // @param i index of the error - obtained by calling ErrorCodeIndex + FSTR_P const PrusaErrorDesc(const uint8_t i); + + // @return the actual numerical value of the Prusa-Error-Codes error + // @param i index of the error - obtained by calling ErrorCodeIndex + uint16_t PrusaErrorCode(const uint8_t i); + + // @return Btns pair of buttons for a particular Prusa-Error-Codes error + // @param i index of the error - obtained by calling ErrorCodeIndex + uint8_t PrusaErrorButtons(const uint8_t i); + + // @return pointer to a PROGMEM string representing the Title of a button + // @param i index of the error - obtained by calling PrusaErrorButtons + extracting low or high nibble from the Btns pair + FSTR_P const PrusaErrorButtonTitle(const uint8_t bi); + + // Sets the selected button for later pick-up by the MMU state machine. + // Used to save the GUI selection/decoupling + void SetButtonResponse(const ButtonOperations rsp); + ButtonOperations GetButtonResponse(); + + // @return button index/code based on currently processed error/screen + // Clears the "pressed" button upon exit + Buttons ButtonPressed(const ErrorCode ec); + + // @return button index/code based on currently processed error/screen + // Used as a subfunction of ButtonPressed. + // Does not clear the "pressed" button upon exit + Buttons ButtonAvailable(const ErrorCode ec); + + } // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_fsensor.cpp b/Marlin/src/feature/mmu3/mmu3_fsensor.cpp new file mode 100644 index 0000000000..32d2fac3f6 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_fsensor.cpp @@ -0,0 +1,65 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_fsensor.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "../runout.h" +#include "mmu3_fsensor.h" + +namespace MMU3 { + + #if HAS_FILAMENT_SENSOR + + FSensorBlockRunout::FSensorBlockRunout() { + runout.enabled = false; // Suppress filament runouts while loading filament. + //fsensor.setAutoLoadEnabled(false); //suppress filament autoloads while loading filament. + } + + FSensorBlockRunout::~FSensorBlockRunout() { + //fsensor.settings_init(); // restore filament runout state. + runout.reset(); + runout.enabled = true; + //SERIAL_ECHOLNPGM("FSUnBlockRunout"); + } + + #else + + FSensorBlockRunout::FSensorBlockRunout() { } + FSensorBlockRunout::~FSensorBlockRunout() { } + + #endif + + + FilamentState WhereIsFilament() { + //return fsensor.getFilamentPresent() ? FilamentState::AT_FSENSOR : FilamentState::NOT_PRESENT; + return FILAMENT_PRESENT() ? FilamentState::AT_FSENSOR : FilamentState::NOT_PRESENT; + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_fsensor.h b/Marlin/src/feature/mmu3/mmu3_fsensor.h new file mode 100644 index 0000000000..0ef4864e9f --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_fsensor.h @@ -0,0 +1,55 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_fsensor.h + */ + +#include "../../core/macros.h" +#include + +#define FILAMENT_PRESENT() (READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE) + +namespace MMU3 { + + // Can be used to block printer's filament sensor handling - to avoid erroneous injecting of M600 + // while doing a toolchange with the MMU + // In case of "no filament sensor" these methods default to an empty implementation + class FSensorBlockRunout { + public: + FSensorBlockRunout(); + ~FSensorBlockRunout(); + }; + + // Possible states of filament from the perspective of presence in various parts of the printer + // Beware, the numeric codes are important and sent into the MMU + enum class FilamentState : uint_fast8_t { + NOT_PRESENT = 0, //!< Filament sensor doesn't see the filament + AT_FSENSOR = 1, //!< Filament detected by the filament sensor, but the nozzle has not detected the filament yet + IN_NOZZLE = 2, //!< Filament detected by the filament sensor and also loaded in the nozzle + UNAVAILABLE = 3 //!< Sensor not available (likely not connected due broken cable) + }; + + FilamentState WhereIsFilament(); + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_log.cpp b/Marlin/src/feature/mmu3/mmu3_log.cpp new file mode 100644 index 0000000000..af2c92ec50 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_log.cpp @@ -0,0 +1,47 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_log.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3_log.h" + +namespace MMU3 { + + void LogEchoEvent_P(PGM_P const pstr) { + SERIAL_ECHO_START(); // @@TODO Decide MMU errors on serial line + SERIAL_MMU2(); + SERIAL_ECHOLN_P(pstr); + } + + void LogErrorEvent_P(PGM_P const pstr) { + LogEchoEvent_P(pstr); + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_log.h b/Marlin/src/feature/mmu3/mmu3_log.h new file mode 100644 index 0000000000..e4dbffeee2 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_log.h @@ -0,0 +1,82 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_log.h + */ + +#include "../../inc/MarlinConfig.h" + +namespace MMU3 { + + // Report the msg into the general logging subsystem (through Marlin's SERIAL_ECHO stuff) + // @param msg pointer to a string in PROGMEM + // On the AVR platform this variant reads the input string from PROGMEM. + // On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away) + void LogErrorEvent_P(PGM_P const pstr); + inline void LogErrorEvent(FSTR_P const fstr) { LogErrorEvent_P(FTOP(fstr)); } + + // Report the msg into the general logging subsystem (through Marlin's SERIAL_ECHO stuff) + // @param msg pointer to a string in PROGMEM + // On the AVR platform this variant reads the input string from PROGMEM. + // On the ARM platform it calls LogErrorEvent directly (silently expecting the compiler to optimize it away) + void LogEchoEvent_P(PGM_P const pstr); + inline void LogEchoEvent(FSTR_P const fstr) { LogEchoEvent_P(FTOP(fstr)); } + +} // MMU3 + +#ifndef UNITTEST + + #define SERIAL_MMU2() { SERIAL_ECHO(F("MMU3:")); } + + #define MMU2_ECHO_MSGLN(S) do { \ + SERIAL_ECHO_START(); \ + SERIAL_MMU2(); \ + SERIAL_ECHOLN(S); \ + }while(0) + #define MMU2_ERROR_MSGLN(S) MMU2_ECHO_MSGLN(S) //! @todo Decide MMU errors on serial line + #define MMU2_ECHO_MSGRPGM(S) do { \ + SERIAL_ECHO_START(); \ + SERIAL_MMU2(); \ + SERIAL_ECHO_P(S); \ + }while(0) + #define MMU2_ERROR_MSGRPGM(S) MMU2_ECHO_MSGRPGM(S) //! @todo Decide MMU errors on serial line + #define MMU2_ECHO_MSG(S) do { \ + SERIAL_ECHO_START(); \ + SERIAL_MMU2(); \ + SERIAL_ECHO(S); \ + }while(0) + #define MMU2_ERROR_MSG(S) MMU2_ECHO_MSG(S) //! @todo Decide MMU errors on serial line + +#else // UNITTEST + + #include "stubs/stub_interfaces.h" + #define MMU2_ECHO_MSGLN(S) marlinLogSim.AppendLine(S) + #define MMU2_ERROR_MSGLN(S) marlinLogSim.AppendLine(S) + #define MMU2_ECHO_MSGRPGM(S) /* marlinLogSim.AppendLine(S) */ + #define MMU2_ERROR_MSGRPGM(S) /* marlinLogSim.AppendLine(S) */ + #define SERIAL_ECHOLNPGM(S) /* marlinLogSim.AppendLine(S) */ + #define SERIAL_ECHOPGM(S) /* */ + #define SERIAL_ECHOLN(S) /* marlinLogSim.AppendLine(S) */ + +#endif // UNITTEST diff --git a/Marlin/src/feature/mmu3/mmu3_marlin.h b/Marlin/src/feature/mmu3/mmu3_marlin.h new file mode 100644 index 0000000000..499af0f285 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_marlin.h @@ -0,0 +1,74 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_marlin.h + */ + +#include "../../inc/MarlinConfig.h" + +namespace MMU3 { + + // This interface separates Marlin1/Marlin2 from the MMU top logic layer. + // - Unify implementation among MK3 and Buddy FW + // - Enable unit testing of MMU top layer + + void extruder_move(const float distance, const float feedRate_mm_s, const bool sync=true); + void extruder_schedule_turning(const float feedRate_mm_s); + + float move_raise_z(const float delta); + + void planner_abort_queued_moves(); + void planner_synchronize(); + bool planner_any_moves(); + float stepper_get_machine_position_E_mm(); + float planner_get_current_position_E(); + void planner_set_current_position_E(float e); + xyz_pos_t planner_current_position(); + + void motion_blocking_move_xy(float rx, float ry, float feedRate_mm_s); + void motion_blocking_move_z(float z, float feedRate_mm_s); + + void nozzle_park(); + + bool marlin_printingIsActive(); + void marlin_manage_heater(); + void marlin_manage_inactivity(bool b); + void marlin_idle(bool b); + void marlin_refresh_print_state_in_ram(); + void marlin_clear_print_state_in_ram(); + void marlin_stop_and_save_print_to_ram(); + + int16_t thermal_degTargetHotend(); + int16_t thermal_degHotend(); + void thermal_setExtrudeMintemp(int16_t t); + void thermal_setTargetHotend(int16_t t); + + void safe_delay_keep_alive(uint16_t t); + + void Enable_E0(); + void Disable_E0(); + + bool xy_are_trusted(); + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_marlin1.cpp b/Marlin/src/feature/mmu3/mmu3_marlin1.cpp new file mode 100644 index 0000000000..2c4effe106 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_marlin1.cpp @@ -0,0 +1,173 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_marlin1.cpp + * MK3 / Marlin1 implementation of support routines for the MMU3 + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "../../MarlinCore.h" +#include "../../module/stepper.h" +#include "../../module/planner.h" +#include "../../module/temperature.h" + +#include "../pause.h" +#include "../../libs/nozzle.h" +#include "mmu3_marlin.h" + +namespace MMU3 { + + static void planner_line_to_current_position(float feedRate_mm_s) { + line_to_current_position(feedRate_mm_s); + } + + static void planner_line_to_current_position_sync(float feedRate_mm_s) { + planner_line_to_current_position(feedRate_mm_s); + planner_synchronize(); + } + + void extruder_move(const float delta, const float feedRate_mm_s, const bool sync/*=true*/) { + current_position.e += delta / planner.e_factor[active_extruder]; + planner_line_to_current_position(feedRate_mm_s); + if (sync) planner.synchronize(); + } + + float move_raise_z(const float delta) { + //return raise_z(delta); + xyze_pos_t current_position_before = current_position; + do_z_clearance_by(delta); + return (current_position - current_position_before).z; + } + + void planner_abort_queued_moves() { + //planner_abort_hard(); + quickstop_stepper(); + + // Unblock the planner. This should be safe in the + // toolchange context. Currently we are mainly aborting + // excess E-moves after detecting filament during toolchange. + // If a MMU error is reported, the planner must be unblocked + // as well so the extruder can be parked safely. + //planner_aborted = false; + // eoyilmaz: we don't need this part, the print is not aborted + } + + void planner_synchronize() { + planner.synchronize(); + } + + bool planner_any_moves() { + return planner.has_blocks_queued(); + } + + float planner_get_machine_position_E_mm() { + return current_position.e; + } + + float stepper_get_machine_position_E_mm() { + return planner.get_axis_position_mm(E_AXIS); + } + + float planner_get_current_position_E() { + return current_position.e; + } + + void planner_set_current_position_E(float e) { + current_position.e = e; + } + + xyz_pos_t planner_current_position() { + return xyz_pos_t(current_position); + } + + void motion_blocking_move_xy(float rx, float ry, float feedRate_mm_s) { + current_position.set(rx, ry); + planner_line_to_current_position_sync(feedRate_mm_s); + } + + void motion_blocking_move_z(float z, float feedRate_mm_s) { + current_position.z = z; + planner_line_to_current_position_sync(feedRate_mm_s); + } + + void nozzle_park() { + #if ANY(NOZZLE_CLEAN_FEATURE, NOZZLE_PARK_FEATURE) + #if ALL(ADVANCED_PAUSE_FEATURE) + xyz_pos_t park_point = NOZZLE_PARK_POINT; + nozzle.park(0, park_point); + #endif + #endif + } + + bool marlin_printingIsActive() { return printingIsActive(); } + + void marlin_manage_heater() { thermalManager.task(); } + + void marlin_manage_inactivity(const bool b) { idle(b); } + + void marlin_idle(bool b) { + thermalManager.task(); + idle(b); + } + + void marlin_refresh_print_state_in_ram() { + // refresh_print_state_in_ram(); + // TODO: I don't see a comparable implementation in Marlin. + } + + void marlin_clear_print_state_in_ram() { + // clear_print_state_in_ram(); + // TODO: I don't see a comparable implementation in Marlin. + } + + void marlin_stop_and_save_print_to_ram() { + // stop_and_save_print_to_ram(0,0); + #if ENABLED(ADVANCED_PAUSE_FEATURE) + constexpr xyz_pos_t park_point = NOZZLE_PARK_POINT; + pause_print(0, park_point); + #endif + } + + int16_t thermal_degTargetHotend() { return thermalManager.degTargetHotend(0); } + int16_t thermal_degHotend() { return thermalManager.degHotend(0); } + void thermal_setExtrudeMintemp(int16_t t) { thermalManager.extrude_min_temp = t; } + void thermal_setTargetHotend(int16_t t) { thermalManager.setTargetHotend(t, 0); } + + void safe_delay_keep_alive(uint16_t t) { + idle(true); + safe_delay(t); + } + + void Enable_E0() { stepper.enable_extruder(TERN_(HAS_EXTRUDERS, 0)); } + void Disable_E0() { stepper.disable_extruder(TERN_(HAS_EXTRUDERS, 0)); } + + bool xy_are_trusted() { + return axis_is_trusted(X_AXIS) && axis_is_trusted(Y_AXIS); + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_marlin_macros.h b/Marlin/src/feature/mmu3/mmu3_marlin_macros.h new file mode 100644 index 0000000000..b277ce9805 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_marlin_macros.h @@ -0,0 +1,49 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_marlin_macros.h + */ + +// This file will not be the same on Marlin1 and Marlin2. +// Its purpose is to unify different macros in either of Marlin incarnations. + +#ifdef __AVR__ + #include "../../MarlinCore.h" + // brings _O and _T macros into MMU + #include "../../core/language.h" + #include "../../gcode/gcode.h" + // we don't have these in Marlin 2.x so just define them here again + #define _O(x) x + #define _T(x) x + #define MARLIN_KEEPALIVE_STATE_IN_PROCESS KEEPALIVE_STATE(IN_PROCESS) +#elif defined(UNITTEST) + #define _O(x) x + #define _T(x) x + #define MARLIN_KEEPALIVE_STATE_IN_PROCESS /*KEEPALIVE_STATE(IN_PROCESS) TODO*/ +#else + #include "../../gcode/gcode.h" + #define _O(x) x + #define _T(x) x + #define MARLIN_KEEPALIVE_STATE_IN_PROCESS KEEPALIVE_STATE(IN_PROCESS) +#endif diff --git a/Marlin/src/feature/mmu3/mmu3_power.cpp b/Marlin/src/feature/mmu3/mmu3_power.cpp new file mode 100644 index 0000000000..da905c6e29 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_power.cpp @@ -0,0 +1,65 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_power.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3.h" +#include "mmu3_power.h" + +#include "../../MarlinCore.h" + +#include "../../core/macros.h" +#include "../../core/boards.h" +#include "../../pins/pins.h" + +namespace MMU3 { + +// On MK3 we cannot do actual power cycle on HW. Instead trigger a hardware reset. +void power_on() { + #if PIN_EXISTS(MMU_RST) + OUT_WRITE(MMU_RST_PIN, HIGH); + #endif + power_reset(); +} + +void power_off() {} + +void power_reset() { + #if PIN_EXISTS(MMU_RST) // HW - pulse reset pin + WRITE(MMU_RST_PIN, LOW); + safe_delay(100); + WRITE(MMU_RST_PIN, HIGH); + #else + mmu3.reset(MMU3::Software); // TODO: Needs redesign. This power implementation shouldn't know anything about the MMU itself + #endif + // otherwise HW reset is not available +} + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/inc/Conditionals_type.h b/Marlin/src/feature/mmu3/mmu3_power.h similarity index 79% rename from Marlin/src/inc/Conditionals_type.h rename to Marlin/src/feature/mmu3/mmu3_power.h index 1f9cfcae16..4f6b94f01e 100644 --- a/Marlin/src/inc/Conditionals_type.h +++ b/Marlin/src/feature/mmu3/mmu3_power.h @@ -22,11 +22,15 @@ #pragma once /** - * Conditionals_type.h - * Internal defines that depend on Configurations and Pins but are not user-editable. - * Define conditionals in this file if they depend on core/types.h. + * mmu2_power.h */ -#ifdef GITHUB_ACTIONS - // Extras for CI testing -#endif +namespace MMU3 { + +void power_on(); + +void power_off(); + +void power_reset(); + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_progress_converter.cpp b/Marlin/src/feature/mmu3/mmu3_progress_converter.cpp new file mode 100644 index 0000000000..8933f2d5fd --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_progress_converter.cpp @@ -0,0 +1,79 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_progress_converter.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "../../core/language.h" +#include "mmu3_progress_converter.h" +#include "mmu_hw/progress_codes.h" +#include "mmu_hw/errors_list.h" + +namespace MMU3 { + + FSTR_P const progressTexts[] PROGMEM = { + GET_TEXT_F(MSG_PROGRESS_OK), // TODO: Generic messages for Marlin + GET_TEXT_F(MSG_PROGRESS_ENGAGE_IDLER), // reused below + GET_TEXT_F(MSG_PROGRESS_DISENGAGE_IDLER), // reused below + GET_TEXT_F(MSG_PROGRESS_UNLOAD_FINDA), + GET_TEXT_F(MSG_PROGRESS_UNLOAD_PULLEY), + GET_TEXT_F(MSG_PROGRESS_FEED_FINDA), + GET_TEXT_F(MSG_PROGRESS_FEED_EXTRUDER), + GET_TEXT_F(MSG_PROGRESS_FEED_NOZZLE), + GET_TEXT_F(MSG_PROGRESS_AVOID_GRIND), + GET_TEXT_F(MSG_FINISHING_MOVEMENTS), + GET_TEXT_F(MSG_PROGRESS_DISENGAGE_IDLER), // err disengaging idler is the same text + GET_TEXT_F(MSG_PROGRESS_ENGAGE_IDLER), // engage dtto. + GET_TEXT_F(MSG_PROGRESS_WAIT_USER), + GET_TEXT_F(MSG_PROGRESS_ERR_INTERNAL), + GET_TEXT_F(MSG_PROGRESS_ERR_HELP_FIL), + GET_TEXT_F(MSG_PROGRESS_ERR_TMC), + GET_TEXT_F(MSG_UNLOADING_FILAMENT), + GET_TEXT_F(MSG_LOADING_FILAMENT), + GET_TEXT_F(MSG_PROGRESS_SELECT_SLOT), + GET_TEXT_F(MSG_PROGRESS_PREPARE_BLADE), + GET_TEXT_F(MSG_PROGRESS_PUSH_FILAMENT), + GET_TEXT_F(MSG_PROGRESS_PERFORM_CUT), + GET_TEXT_F(MSG_PROGRESSPSTRETURN_SELECTOR), + GET_TEXT_F(MSG_PROGRESS_PARK_SELECTOR), + GET_TEXT_F(MSG_PROGRESS_EJECT_FILAMENT), + GET_TEXT_F(MSG_PROGRESSPSTRETRACT_FINDA), + GET_TEXT_F(MSG_PROGRESS_HOMING), + GET_TEXT_F(MSG_PROGRESS_MOVING_SELECTOR), + GET_TEXT_F(MSG_PROGRESS_FEED_FSENSOR) + }; + + FSTR_P const ProgressCodeToText(const ProgressCode pc) { + // @@TODO ?? a better fallback option? + return (int(pc) < COUNT(progressTexts)) + ? static_cast(pgm_read_ptr(&progressTexts[(uint16_t)pc])) + : static_cast(pgm_read_ptr(&progressTexts[0])); + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_progress_converter.h b/Marlin/src/feature/mmu3/mmu3_progress_converter.h new file mode 100644 index 0000000000..e7b1d86821 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_progress_converter.h @@ -0,0 +1,36 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_progress_converter.h + */ + +#include "mmu_hw/progress_codes.h" + +#include "../../HAL/shared/Marduino.h" + +namespace MMU3 { + +FSTR_P const ProgressCodeToText(const ProgressCode pc); + +} diff --git a/Marlin/src/feature/mmu3/mmu3_protocol.cpp b/Marlin/src/feature/mmu3/mmu3_protocol.cpp new file mode 100644 index 0000000000..739599f142 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_protocol.cpp @@ -0,0 +1,418 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_protocol.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3_protocol.h" + +// protocol definition +// command: Q0 +// meaning: query operation status +// Query/command: query +// Expected reply from the MMU: +// any of the running operation statuses: OID: [T|L|U|E|C|W|K][0-4] +// P[0-9] : command being processed i.e. operation running, may contain a state number +// E[0-9][0-9] : error 1-9 while doing a tool change +// F[0-9] : operation finished - will be repeated to "Q" messages until a new command is issued + +namespace modules { +namespace protocol { + + // decoding automaton + // states: input -> transition into state + // Code QTLMUXPSBEWK -> msgcode + // \n ->start + // * ->error + // error \n ->start + // * ->error + // msgcode 0-9 ->msgvalue + // * ->error + // msgvalue 0-9 ->msgvalue + // \n ->start successfully accepted command + + DecodeStatus Protocol::DecodeRequest(uint8_t c) { + switch (rqState) { + case RequestStates::Code: + switch (c) { + case 'Q': + case 'T': + case 'L': + case 'M': + case 'U': + case 'X': + case 'P': + case 'S': + case 'B': + case 'E': + case 'W': // write is gonna be a special one + case 'K': + case 'F': + case 'f': + case 'H': + case 'R': + requestMsg.code = (RequestMsgCodes)c; + requestMsg.value = 0; + requestMsg.value2 = 0; + requestMsg.crc8 = 0; + rqState = (c == 'W') ? RequestStates::Address : RequestStates::Value; // prepare special automaton path for Write commands + return DecodeStatus::NeedMoreData; + default: + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::Value: + if (IsHexDigit(c)) { + requestMsg.value <<= 4U; + requestMsg.value |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (IsCRCSeparator(c)) { + rqState = RequestStates::CRC; + return DecodeStatus::NeedMoreData; + } + else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::Address: + if (IsHexDigit(c)) { + requestMsg.value <<= 4U; + requestMsg.value |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (c == ' ') { // end of address, value coming + rqState = RequestStates::WriteValue; + return DecodeStatus::NeedMoreData; + } + else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::WriteValue: + if (IsHexDigit(c)) { + requestMsg.value2 <<= 4U; + requestMsg.value2 |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (IsCRCSeparator(c)) { + rqState = RequestStates::CRC; + return DecodeStatus::NeedMoreData; + } + else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + case RequestStates::CRC: + if (IsHexDigit(c)) { + requestMsg.crc8 <<= 4U; + requestMsg.crc8 |= Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (IsNewLine(c)) { + // check CRC at this spot + if (requestMsg.crc8 != requestMsg.ComputeCRC8()) { + // CRC mismatch + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + else { + rqState = RequestStates::Code; + return DecodeStatus::MessageCompleted; + } + } + else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + default: // case error: + if (IsNewLine(c)) { + rqState = RequestStates::Code; + return DecodeStatus::MessageCompleted; + } + else { + requestMsg.code = RequestMsgCodes::unknown; + rqState = RequestStates::Error; + return DecodeStatus::Error; + } + } + } + + uint8_t Protocol::EncodeRequest(const RequestMsg &msg, uint8_t *txbuff) { + txbuff[0] = (uint8_t)msg.code; + uint8_t i = 1 + UInt8ToHex(msg.value, txbuff + 1); + + i += AppendCRC(msg.getCRC(), txbuff + i); + + txbuff[i] = '\n'; + ++i; + return i; + static_assert(7 <= MaxRequestSize(), "Request message length exceeded the maximum size, increase the magic constant in MaxRequestSize()"); + } + + uint8_t Protocol::EncodeWriteRequest(uint8_t address, uint16_t value, uint8_t *txbuff) { + const RequestMsg msg(RequestMsgCodes::Write, address, value); + uint8_t i = BeginEncodeRequest(msg, txbuff); + // dump the value + i += UInt16ToHex(value, txbuff + i); + + i += AppendCRC(msg.getCRC(), txbuff + i); + + txbuff[i] = '\n'; + ++i; + return i; + } + + DecodeStatus Protocol::DecodeResponse(uint8_t c) { + switch (rspState) { + case ResponseStates::RequestCode: + switch (c) { + case 'Q': + case 'T': + case 'L': + case 'M': + case 'U': + case 'X': + case 'P': + case 'S': + case 'B': + case 'E': + case 'W': + case 'K': + case 'F': + case 'f': + case 'H': + case 'R': + responseMsg.request.code = (RequestMsgCodes)c; + responseMsg.request.value = 0; + responseMsg.request.value2 = 0; + responseMsg.request.crc8 = 0; + rspState = ResponseStates::RequestValue; + return DecodeStatus::NeedMoreData; + case 0x0a: + case 0x0d: + // skip leading whitespace if any (makes integration with other SW easier/tolerant) + return DecodeStatus::NeedMoreData; + default: + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + case ResponseStates::RequestValue: + if (IsHexDigit(c)) { + responseMsg.request.value <<= 4U; + responseMsg.request.value += Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (c == ' ') { + rspState = ResponseStates::ParamCode; + return DecodeStatus::NeedMoreData; + } + else { + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + case ResponseStates::ParamCode: + switch (c) { + case 'P': + case 'E': + case 'F': + case 'A': + case 'R': + case 'B': + rspState = ResponseStates::ParamValue; + responseMsg.paramCode = (ResponseMsgParamCodes)c; + responseMsg.paramValue = 0; + return DecodeStatus::NeedMoreData; + default: + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + case ResponseStates::ParamValue: + if (IsHexDigit(c)) { + responseMsg.paramValue <<= 4U; + responseMsg.paramValue += Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (IsCRCSeparator(c)) { + rspState = ResponseStates::CRC; + return DecodeStatus::NeedMoreData; + } + else { + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + case ResponseStates::CRC: + if (IsHexDigit(c)) { + responseMsg.request.crc8 <<= 4U; + responseMsg.request.crc8 += Char2Nibble(c); + return DecodeStatus::NeedMoreData; + } + else if (IsNewLine(c)) { + // check CRC at this spot + if (responseMsg.request.crc8 != responseMsg.ComputeCRC8()) { + // CRC mismatch + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + else { + rspState = ResponseStates::RequestCode; + return DecodeStatus::MessageCompleted; + } + } + else { + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + rspState = ResponseStates::Error; + return DecodeStatus::Error; + } + default: // case error: + if (IsNewLine(c)) { + rspState = ResponseStates::RequestCode; + return DecodeStatus::MessageCompleted; + } + else { + responseMsg.paramCode = ResponseMsgParamCodes::unknown; + return DecodeStatus::Error; + } + } + } + + uint8_t Protocol::EncodeResponseCmdAR(const RequestMsg &msg, ResponseMsgParamCodes ar, uint8_t *txbuff) { + // BEWARE: + // ResponseMsg rsp(RequestMsg(msg.code, msg.value), ar, 0); + // ... is NOT the same as: + // ResponseMsg rsp(msg, ar, 0); + // ... because of the usually unused parameter value2 (which only comes non-zero in write requests). + // It took me a few hours to find out why the CRC from the MMU never matched all the other sides (unit tests and the MK3S) + // It is because this was the only place where the original request kept its value2 non-zero. + // In the response, we must make sure value2 is actually zero unless being sent along with it (which is not right now) + const ResponseMsg rsp(RequestMsg(msg.code, msg.value), ar, 0); // this needs some cleanup @@TODO - check assembly how bad is it + uint8_t i = BeginEncodeRequest(rsp.request, txbuff); + txbuff[i] = (uint8_t)ar; + ++i; + i += AppendCRC(rsp.getCRC(), txbuff + i); + txbuff[i] = '\n'; + ++i; + return i; + } + + uint8_t Protocol::EncodeResponseReadFINDA(const RequestMsg &msg, uint8_t findaValue, uint8_t *txbuff) { + return EncodeResponseRead(msg, true, findaValue, txbuff); + } + + uint8_t Protocol::EncodeResponseQueryOperation(const RequestMsg &msg, ResponseCommandStatus rcs, uint8_t *txbuff) { + const ResponseMsg rsp(msg, rcs.code, rcs.value); + uint8_t i = BeginEncodeRequest(msg, txbuff); + txbuff[i] = (uint8_t)rsp.paramCode; + ++i; + i += UInt16ToHex(rsp.paramValue, txbuff + i); + i += AppendCRC(rsp.getCRC(), txbuff + i); + txbuff[i] = '\n'; + return i + 1; + } + + uint8_t Protocol::EncodeResponseRead(const RequestMsg &msg, bool accepted, uint16_t value2, uint8_t *txbuff) { + const ResponseMsg rsp(msg, + accepted ? ResponseMsgParamCodes::Accepted : ResponseMsgParamCodes::Rejected, + accepted ? value2 : 0 // be careful about this value for CRC computation - rejected status doesn't have any meaningful value which could be reconstructed from the textual form of the message + ); + uint8_t i = BeginEncodeRequest(msg, txbuff); + txbuff[i] = (uint8_t)rsp.paramCode; + ++i; + if (accepted) + // dump the value + i += UInt16ToHex(value2, txbuff + i); + i += AppendCRC(rsp.getCRC(), txbuff + i); + txbuff[i] = '\n'; + return i + 1; + } + + uint8_t Protocol::UInt8ToHex(uint8_t value, uint8_t *dst) { + if (value == 0) { + *dst = '0'; + return 1; + } + + uint8_t v = value >> 4U; + uint8_t charsOut = 1; + if (v != 0) { // skip the first '0' if any + *dst = Nibble2Char(v); + ++dst; + charsOut = 2; + } + v = value & 0xfU; + *dst = Nibble2Char(v); + return charsOut; + } + + uint8_t Protocol::UInt16ToHex(uint16_t value, uint8_t *dst) { + constexpr uint16_t topNibbleMask = 0xf000; + if (value == 0) { + *dst = '0'; + return 1; + } + // skip initial zeros + uint8_t charsOut = 4; + while ((value & topNibbleMask) == 0) { + value <<= 4U; + --charsOut; + } + for (uint8_t i = 0; i < charsOut; ++i) { + uint8_t n = (value & topNibbleMask) >> (8U + 4U); + value <<= 4U; + *dst = Nibble2Char(n); + ++dst; + } + return charsOut; + } + + uint8_t Protocol::BeginEncodeRequest(const RequestMsg &msg, uint8_t *dst) { + dst[0] = (uint8_t)msg.code; + + uint8_t i = 1 + UInt8ToHex(msg.value, dst + 1); + + dst[i] = ' '; + return i + 1; + } + + uint8_t Protocol::AppendCRC(uint8_t crc, uint8_t *dst) { + dst[0] = '*'; // reprap-style separator of CRC + return 1 + UInt8ToHex(crc, dst + 1); + } + +} // namespace protocol +} // namespace modules + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_protocol.h b/Marlin/src/feature/mmu3/mmu3_protocol.h new file mode 100644 index 0000000000..f9553b58c8 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_protocol.h @@ -0,0 +1,318 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_protocol.h + */ + +#include "../../MarlinCore.h" + +#include +#include "mmu3_crc.h" + +// prevent ARM HAL macros from breaking our code +#undef CRC + +namespace modules { + +// @brief The MMU communication protocol implementation and related stuff. +// +// See description of the new protocol in the MMU 2021 doc +namespace protocol { + + // Definition of request message codes + enum class RequestMsgCodes : uint8_t { + unknown = 0, + Query = 'Q', + Tool = 'T', + Load = 'L', + Mode = 'M', + Unload = 'U', + Reset = 'X', + Finda = 'P', + Version = 'S', + Button = 'B', + Eject = 'E', + Write = 'W', + Cut = 'K', + FilamentType = 'F', + FilamentSensor = 'f', + Home = 'H', + Read = 'R' + }; + + // Definition of response message parameter codes + enum class ResponseMsgParamCodes : uint8_t { + unknown = 0, + Processing = 'P', + Error = 'E', + Finished = 'F', + Accepted = 'A', + Rejected = 'R', + Button = 'B'// the MMU registered a button press and is sending it to the printer for processing + }; + + // A request message - requests are being sent by the printer into the MMU. + struct RequestMsg { + RequestMsgCodes code; //!< code of the request message + uint8_t value; //!< value of the request message or address of variable to read/write + uint16_t value2; //!< in case or write messages - value to be written into the register + + // CRC8 check - please note we abuse this byte for CRC of ResponseMsgs as well. + // The crc8 byte itself is not added into the CRC computation (obviously ;) ) + // Beware - adding any members of this data structure may need changing the way CRC is being computed! + uint8_t crc8; + + constexpr uint8_t ComputeCRC8() const { + uint8_t crc = 0; + crc = modules::crc::CRC8::CCITT_updateCX(0, (uint8_t)code); + crc = modules::crc::CRC8::CCITT_updateCX(crc, value); + crc = modules::crc::CRC8::CCITT_updateW(crc, value2); + return crc; + } + + // @param code of the request message + // @param value of the request message + inline constexpr RequestMsg(RequestMsgCodes code, uint8_t value) + : code(code) + , value(value) + , value2(0) + , crc8(ComputeCRC8()) { + } + + // Intended for write requests + // @param code of the request message ('W') + // @param address of the register + // @param value to write into the register + inline constexpr RequestMsg(RequestMsgCodes code, uint8_t address, uint16_t value) + : code(code) + , value(address) + , value2(value) + , crc8(ComputeCRC8()) {} + + constexpr uint8_t getCRC() const { return crc8; } + }; + + // A response message - responses are being sent from the MMU into the printer as a response to a request message. + struct ResponseMsg { + RequestMsg request; //!< response is always preceded by the request message + ResponseMsgParamCodes paramCode; //!< code of the parameter + uint16_t paramValue; //!< value of the parameter + + constexpr uint8_t ComputeCRC8() const { + uint8_t crc = request.ComputeCRC8(); + crc = modules::crc::CRC8::CCITT_updateCX(crc, (uint8_t)paramCode); + crc = modules::crc::CRC8::CCITT_updateW(crc, paramValue); + return crc; + } + + // @param request the source request message this response is a reply to + // @param paramCode code of the parameter + // @param paramValue value of the parameter + inline constexpr ResponseMsg(RequestMsg request, ResponseMsgParamCodes paramCode, uint16_t paramValue) + : request(request) + , paramCode(paramCode) + , paramValue(paramValue) { + this->request.crc8 = ComputeCRC8(); + } + + constexpr uint8_t getCRC() const { return request.crc8; } + }; + + // Combined commandStatus and its value into one data structure (optimization purposes) + struct ResponseCommandStatus { + ResponseMsgParamCodes code; + uint16_t value; + inline constexpr ResponseCommandStatus(ResponseMsgParamCodes code, uint16_t value) + : code(code) + , value(value) {} + }; + + // Message decoding return values + enum class DecodeStatus : uint_fast8_t { + MessageCompleted, //!< message completed and successfully lexed + NeedMoreData, //!< message incomplete yet, waiting for another byte to come + Error, //!< input character broke message decoding + }; + + // Protocol class is responsible for creating/decoding messages in Rx/Tx buffer + // + // Beware - in the decoding more, it is meant to be a stateful instance which works through public methods + // processing one input byte per call. + class Protocol { + public: + Protocol() + : rqState(RequestStates::Code) + , requestMsg(RequestMsgCodes::unknown, 0) + , rspState(ResponseStates::RequestCode) + , responseMsg(RequestMsg(RequestMsgCodes::unknown, 0), ResponseMsgParamCodes::unknown, 0) {} + + // Takes the input byte c and steps one step through the state machine + // @return state of the message being decoded + DecodeStatus DecodeRequest(uint8_t c); + + // Decodes response message in rxbuff + // @return decoded response message structure + DecodeStatus DecodeResponse(uint8_t c); + + // Encodes request message msg into txbuff memory + // It is expected the txbuff is large enough to fit the message + // @return number of bytes written into txbuff + static uint8_t EncodeRequest(const RequestMsg &msg, uint8_t *txbuff); + + // Encodes Write request message msg into txbuff memory + // It is expected the txbuff is large enough to fit the message + // @return number of bytes written into txbuff + static uint8_t EncodeWriteRequest(uint8_t address, uint16_t value, uint8_t *txbuff); + + // @return the maximum byte length necessary to encode a request message + // Beneficial in case of pre-allocating a buffer for encoding a RequestMsg. + static constexpr uint8_t MaxRequestSize() { return 13; } + + // @return the maximum byte length necessary to encode a response message + // Beneficial in case of pre-allocating a buffer for encoding a ResponseMsg. + static constexpr uint8_t MaxResponseSize() { return 14; } + + // Encode generic response Command Accepted or Rejected + // @param msg source request message for this response + // @param ar code of response parameter + // @param txbuff where to format the message + // @return number of bytes written into txbuff + static uint8_t EncodeResponseCmdAR(const RequestMsg &msg, ResponseMsgParamCodes ar, uint8_t *txbuff); + + // Encode response to Read FINDA query + // @param msg source request message for this response + // @param findaValue 1/0 (on/off) status of FINDA + // @param txbuff where to format the message + // @return number of bytes written into txbuff + static uint8_t EncodeResponseReadFINDA(const RequestMsg &msg, uint8_t findaValue, uint8_t *txbuff); + + // Encode response to Version query + // @param msg source request message for this response + // @param value version number (0-255) + // @param txbuff where to format the message + // @return number of bytes written into txbuff + static uint8_t EncodeResponseVersion(const RequestMsg &msg, uint16_t value, uint8_t *txbuff); + + // Encode response to Query operation status + // @param msg source request message for this response + // @param code status of operation (Processing, Error, Finished) + // @param value related to status of operation(e.g. error code or progress) + // @param txbuff where to format the message + // @return number of bytes written into txbuff + static uint8_t EncodeResponseQueryOperation(const RequestMsg &msg, ResponseCommandStatus rcs, uint8_t *txbuff); + + // Encode response to Read query + // @param msg source request message for this response + // @param accepted true if the read query was accepted + // @param value2 variable value + // @param txbuff where to format the message + // @return number of bytes written into txbuff + static uint8_t EncodeResponseRead(const RequestMsg &msg, bool accepted, uint16_t value2, uint8_t *txbuff); + + // @return the most recently lexed request message + inline const RequestMsg GetRequestMsg() const { return requestMsg; } + + // @return the most recently lexed response message + inline const ResponseMsg GetResponseMsg() const { return responseMsg; } + + // resets the internal request decoding state (typically after an error) + void ResetRequestDecoder() { + rqState = RequestStates::Code; + } + + // resets the internal response decoding state (typically after an error) + void ResetResponseDecoder() { + rspState = ResponseStates::RequestCode; + } + + #ifndef UNITTEST + private: + #endif + + enum class RequestStates : uint8_t { + Code, //!< starting state - expects message code + Value, //!< expecting code value + Address, //!< expecting address for Write command + WriteValue, //!< value to be written (Write command) + CRC, //!< CRC + Error //!< automaton in error state + }; + + RequestStates rqState; + RequestMsg requestMsg; + + enum class ResponseStates : uint8_t { + RequestCode, //!< starting state - expects message code + RequestValue, //!< expecting code value + ParamCode, //!< expecting param code + ParamValue, //!< expecting param value + CRC, //!< expecting CRC value + Error //!< automaton in error state + }; + + ResponseStates rspState; + ResponseMsg responseMsg; + + static constexpr bool IsNewLine(uint8_t c) { + return c == '\n' || c == '\r'; + } + static constexpr bool IsDigit(uint8_t c) { + return c >= '0' && c <= '9'; + } + static constexpr bool IsCRCSeparator(uint8_t c) { + return c == '*'; + } + static constexpr bool IsHexDigit(uint8_t c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'); + } + static constexpr uint8_t Char2Nibble(uint8_t c) { + switch (c) { + case '0' ... '9': return c - '0'; + case 'a' ... 'f': return c - 'a' + 10; + default: return 0; + } + } + + static constexpr uint8_t Nibble2Char(uint8_t n) { + switch (n) { + case 0x0 ... 0x9: return n + '0'; + case 0xA ... 0xF: return n - 10 + 'a'; + default: return 0; + } + } + + // @return number of characters written + static uint8_t UInt8ToHex(uint8_t value, uint8_t *dst); + + // @return number of characters written + static uint8_t UInt16ToHex(uint16_t value, uint8_t *dst); + + static uint8_t BeginEncodeRequest(const RequestMsg &msg, uint8_t *dst); + + static uint8_t AppendCRC(uint8_t crc, uint8_t *dst); + }; + +} // namespace protocol +} // namespace modules + diff --git a/Marlin/src/feature/mmu3/mmu3_protocol_logic.cpp b/Marlin/src/feature/mmu3/mmu3_protocol_logic.cpp new file mode 100644 index 0000000000..4323925b6b --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_protocol_logic.cpp @@ -0,0 +1,899 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_protocol_logic.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3_protocol_logic.h" +#include "mmu3_log.h" +#include "mmu3_fsensor.h" + + #ifdef __AVR__ + // on MK3/S/+ we shuffle the timers a bit, thus "_millis" may not equal "millis" + // #include "system_timer.h" + #define _millis millis + #else + // irrelevant on Buddy FW, just keep "_millis" as "millis" + // #include + #define _millis millis + #ifdef UNITTEST + #define strncmp_P strncmp + #else + #include "../../core/serial.h" + #endif + #endif + + #include + #include "mmu3_supported_version.h" + +namespace MMU3 { + + static constexpr uint8_t supportedMmuFWVersion[] PROGMEM = { mmuVersionMajor, mmuVersionMinor, mmuVersionPatch }; + + const Register ProtocolLogic::regs8Addrs[ProtocolLogic::regs8Count] PROGMEM = { + Register::FINDA_State, // FINDA state + Register::Set_Get_Selector_Slot, // Selector slot + Register::Set_Get_Idler_Slot, // Idler slot + }; + + const Register ProtocolLogic::regs16Addrs[ProtocolLogic::regs16Count] PROGMEM = { + Register::MMU_Errors, // MMU errors - aka statistics + Register::Get_Pulley_Position, // Pulley position [mm] + }; + + const Register ProtocolLogic::initRegs8Addrs[ProtocolLogic::initRegs8Count] PROGMEM = { + Register::Extra_Load_Distance, // Extra load distance [mm] + Register::Pulley_Slow_Feedrate, // Pulley slow feedrate [mm/s] + }; + + void ProtocolLogic::CheckAndReportAsyncEvents() { + // even when waiting for a query period, we need to report a change in filament sensor's state + // - it is vital for a precise synchronization of moves of the printer and the MMU + uint8_t fs = (uint8_t)WhereIsFilament(); + if (fs != lastFSensor) + SendAndUpdateFilamentSensor(); + } + + void ProtocolLogic::SendQuery() { + SendMsg(RequestMsg(RequestMsgCodes::Query, 0)); + scopeState = ScopeState::QuerySent; + } + + void ProtocolLogic::StartReading8bitRegisters() { + regIndex = 0; + SendReadRegister(pgm_read_byte(regs8Addrs + regIndex), ScopeState::Reading8bitRegisters); + } + + void ProtocolLogic::ProcessRead8bitRegister() { + regs8[regIndex] = rsp.paramValue; + ++regIndex; + if (regIndex >= regs8Count) + // proceed with reading 16bit registers + StartReading16bitRegisters(); + else + SendReadRegister(pgm_read_byte(regs8Addrs + regIndex), ScopeState::Reading8bitRegisters); + } + + void ProtocolLogic::StartReading16bitRegisters() { + regIndex = 0; + SendReadRegister(pgm_read_byte(regs16Addrs + regIndex), ScopeState::Reading16bitRegisters); + } + + ProtocolLogic::ScopeState __attribute__((noinline)) ProtocolLogic::ProcessRead16bitRegister(ProtocolLogic::ScopeState stateAtEnd) { + regs16[regIndex] = rsp.paramValue; + ++regIndex; + if (regIndex >= regs16Count) + return stateAtEnd; + else + SendReadRegister(pgm_read_byte(regs16Addrs + regIndex), ScopeState::Reading16bitRegisters); + return ScopeState::Reading16bitRegisters; + } + + void ProtocolLogic::StartWritingInitRegisters() { + regIndex = 0; + SendWriteRegister(pgm_read_byte(initRegs8Addrs + regIndex), initRegs8[regIndex], ScopeState::WritingInitRegisters); + } + + bool __attribute__((noinline)) ProtocolLogic::ProcessWritingInitRegister() { + ++regIndex; + if (regIndex >= initRegs8Count) + return true; + else + SendWriteRegister(pgm_read_byte(initRegs8Addrs + regIndex), initRegs8[regIndex], ScopeState::WritingInitRegisters); + return false; + } + + void ProtocolLogic::SendAndUpdateFilamentSensor() { + SendMsg(RequestMsg(RequestMsgCodes::FilamentSensor, lastFSensor = (uint8_t)WhereIsFilament())); + scopeState = ScopeState::FilamentSensorStateSent; + } + + void ProtocolLogic::SendButton(uint8_t btn) { + SendMsg(RequestMsg(RequestMsgCodes::Button, btn)); + scopeState = ScopeState::ButtonSent; + } + + void ProtocolLogic::SendVersion(uint8_t stage) { + SendMsg(RequestMsg(RequestMsgCodes::Version, stage)); + scopeState = (ScopeState)((uint_fast8_t)ScopeState::S0Sent + stage); + } + + void ProtocolLogic::SendReadRegister(uint8_t index, ScopeState nextState) { + SendMsg(RequestMsg(RequestMsgCodes::Read, index)); + scopeState = nextState; + } + + void ProtocolLogic::SendWriteRegister(uint8_t index, uint16_t value, ScopeState nextState) { + SendWriteMsg(RequestMsg(RequestMsgCodes::Write, index, value)); + scopeState = nextState; + } + + // searches for "ok\n" in the incoming serial data (that's the usual response of the old MMU FW) + struct OldMMUFWDetector { + uint8_t ok; + inline constexpr OldMMUFWDetector() + : ok(0) {} + + enum class State : uint8_t { + MatchingPart, + SomethingElse, + Matched + }; + + // @return true when "ok\n" gets detected + State Detect(uint8_t c) { + // consume old MMU FW's data if any -> avoid confusion of protocol decoder + if (ok == 0 && c == 'o') { + ++ok; + return State::MatchingPart; + } + else if (ok == 1 && c == 'k') { + ++ok; + return State::Matched; + } + return State::SomethingElse; + } + }; + + StepStatus ProtocolLogic::ExpectingMessage() { + int bytesConsumed = 0; + int c = -1; + + OldMMUFWDetector oldMMUh4x0r; // old MMU FW hacker ;) + + // try to consume as many rx bytes as possible (until a message has been completed) + while ((c = MMU_SERIAL.read()) >= 0) { + ++bytesConsumed; + RecordReceivedByte(c); + switch (protocol.DecodeResponse(c)) { + case DecodeStatus::MessageCompleted: + rsp = protocol.GetResponseMsg(); + LogResponse(); + // @@TODO reset direction of communication + RecordUARTActivity(); // something has happened on the UART, update the timeout record + return MessageReady; + case DecodeStatus::NeedMoreData: + break; + case DecodeStatus::Error: { + // consume old MMU FW's data if any -> avoid confusion of protocol decoder + auto old = oldMMUh4x0r.Detect(c); + if (old == OldMMUFWDetector::State::Matched) + // Old MMU FW 1.0.6 detected. Firmwares are incompatible. + return VersionMismatch; + else if (old == OldMMUFWDetector::State::MatchingPart) + break; + } + // [[fallthrough]]; // otherwise + // fall through + default: + RecordUARTActivity(); // something has happened on the UART, update the timeout record + return ProtocolError; + } + } + if (bytesConsumed != 0) { + RecordUARTActivity(); // something has happened on the UART, update the timeout record + return Processing; // consumed some bytes, but message still not ready + } + else if (Elapsed(linkLayerTimeout) && currentScope != Scope::Stopped) { + return CommunicationTimeout; + } + return Processing; + } + + void ProtocolLogic::SendMsg(RequestMsg rq) { + #if defined(__AVR__) || defined(TARGET_LPC1768) + // Buddy FW cannot use stack-allocated txbuff - DMA doesn't work with CCMRAM + // No restrictions on MK3/S/+ though + uint8_t txbuff[Protocol::MaxRequestSize()]; + #endif + uint8_t len = Protocol::EncodeRequest(rq, txbuff); + #if defined(__AVR__) || defined(TARGET_LPC1768) + // TODO: I'm not sure if this is the correct approach with AVR + for ( uint8_t i = 0; i < len; i++) { + MMU_SERIAL.write(txbuff[i]); + } + #else + MMU_SERIAL.write(txbuff, len); + #endif + LogRequestMsg(txbuff, len); + RecordUARTActivity(); + } + + void ProtocolLogic::SendWriteMsg(RequestMsg rq) { + #if defined(__AVR__) || defined(TARGET_LPC1768) + // Buddy FW cannot use stack-allocated txbuff - DMA doesn't work with CCMRAM + // No restrictions on MK3/S/+ though + uint8_t txbuff[Protocol::MaxRequestSize()]; + #endif + uint8_t len = Protocol::EncodeWriteRequest(rq.value, rq.value2, txbuff); + + #if defined(__AVR__) || defined(TARGET_LPC1768) + // TODO: I'm not sure if this is the correct approach with AVR + for ( uint8_t i = 0; i < len; i++) { + MMU_SERIAL.write(txbuff[i]); + } + #else + MMU_SERIAL.write(txbuff, len); + #endif + LogRequestMsg(txbuff, len); + RecordUARTActivity(); + } + + void ProtocolLogic::StartSeqRestart() { + retries = maxRetries; + SendVersion(0); + } + + void ProtocolLogic::DelayedRestartRestart() { + scopeState = ScopeState::RecoveringProtocolError; + } + + void ProtocolLogic::CommandRestart() { + scopeState = ScopeState::CommandSent; + SendMsg(rq); + } + + void ProtocolLogic::IdleRestart() { + scopeState = ScopeState::Ready; + } + + StepStatus ProtocolLogic::ProcessVersionResponse(uint8_t stage) { + if (rsp.request.code != RequestMsgCodes::Version || rsp.request.value != stage) { + // got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0? + SendVersion(stage); + } + else { + mmuFwVersion[stage] = rsp.paramValue; + if (mmuFwVersion[stage] != pgm_read_byte(&supportedMmuFWVersion[stage])) { + if (--retries == 0) return VersionMismatch; + SendVersion(stage); + } + else { + ResetCommunicationTimeoutAttempts(); // got a meaningful response from the MMU, stop data layer timeout tracking + SendVersion(stage + 1); + } + } + return Processing; + } + + StepStatus ProtocolLogic::ScopeStep() { + if (!ExpectsResponse()) { + // we are waiting for something + switch (currentScope) { + case Scope::DelayedRestart: + return DelayedRestartWait(); + case Scope::Idle: + return IdleWait(); + case Scope::Command: + return CommandWait(); + case Scope::Stopped: + return StoppedStep(); + default: + break; + } + } + else { + // we are expecting a message + auto expmsg = ExpectingMessage(); + if (expmsg != MessageReady) + return expmsg; + + // process message + switch (currentScope) { + case Scope::StartSeq: + return StartSeqStep(); // ~270B + case Scope::Idle: + return IdleStep(); // ~300B + case Scope::Command: + return CommandStep(); // ~430B + case Scope::Stopped: + return StoppedStep(); + default: + break; + } + } + return Finished; + } + + StepStatus ProtocolLogic::StartSeqStep() { + // solve initial handshake + switch (scopeState) { + case ScopeState::S0Sent: // received response to S0 - major + case ScopeState::S1Sent: // received response to S1 - minor + case ScopeState::S2Sent: // received response to S2 - patch + return ProcessVersionResponse((uint8_t)scopeState - (uint8_t)ScopeState::S0Sent); + case ScopeState::S3Sent: // received response to S3 - revision + if (rsp.request.code != RequestMsgCodes::Version || rsp.request.value != 3) { + // got a response to something else - protocol corruption probably, repeat the query OR restart the comm by issuing S0? + SendVersion(3); + } + else { + mmuFwVersionBuild = rsp.paramValue; // just register the build number + // Start General Interrogation after line up - initial parametrization is started + StartWritingInitRegisters(); + } + return Processing; + case ScopeState::WritingInitRegisters: + if (ProcessWritingInitRegister()) + SendAndUpdateFilamentSensor(); + return Processing; + case ScopeState::FilamentSensorStateSent: + SwitchFromStartToIdle(); + return Processing; // Returning Finished is not a good idea in case of a fast error recovery + // - it tells the printer, that the command which experienced a protocol error and recovered successfully actually terminated. + // In such a case we must return "Processing" in order to keep the MMU state machine running and prevent the printer from executing next G-codes. + default: + return VersionMismatch; + } + } + + StepStatus ProtocolLogic::DelayedRestartWait() { + if (Elapsed(heartBeatPeriod)) { // this basically means, that we are waiting until there is some traffic on + while (MMU_SERIAL.read() != -1); // clear the input buffer + // switch to StartSeq + start(); + } + return Processing; + } + + StepStatus ProtocolLogic::CommandWait() { + if (Elapsed(heartBeatPeriod)) + SendQuery(); + else + // even when waiting for a query period, we need to report a change in filament sensor's state + // - it is vital for a precise synchronization of moves of the printer and the MMU + CheckAndReportAsyncEvents(); + return Processing; + } + + StepStatus ProtocolLogic::ProcessCommandQueryResponse() { + switch (rsp.paramCode) { + case ResponseMsgParamCodes::Processing: + progressCode = static_cast(rsp.paramValue); + errorCode = ErrorCode::OK; + SendAndUpdateFilamentSensor(); // keep on reporting the state of fsensor regularly + return Processing; + case ResponseMsgParamCodes::Error: + // in case of an error the progress code remains as it has been before + progressCode = ProgressCode::ERRWaitingForUser; + errorCode = static_cast(rsp.paramValue); + // keep on reporting the state of fsensor regularly even in command error state + // - the MMU checks FINDA and fsensor even while recovering from errors + SendAndUpdateFilamentSensor(); + return CommandError; + case ResponseMsgParamCodes::Button: + // The user pushed a button on the MMU. Save it, do what we need to do + // to prepare, then pass it back to the MMU so it can work its magic. + buttonCode = static_cast(rsp.paramValue); + SendAndUpdateFilamentSensor(); + return ButtonPushed; + case ResponseMsgParamCodes::Finished: + // We must check whether the "finished" is actually related to the command issued into the MMU + // It can also be an X0 F which means MMU just successfully restarted. + if (ReqMsg().code == rsp.request.code && ReqMsg().value == rsp.request.value) { + progressCode = ProgressCode::OK; + errorCode = ErrorCode::OK; + scopeState = ScopeState::Ready; + rq = RequestMsg(RequestMsgCodes::unknown, 0); // clear the successfully finished request + return Finished; + } + else { + // got response to some other command - the originally issued command was interrupted! + return Interrupted; + } + default: + return ProtocolError; + } + } + + StepStatus ProtocolLogic::CommandStep() { + switch (scopeState) { + case ScopeState::CommandSent: { + switch (rsp.paramCode) { // the response should be either accepted or rejected + case ResponseMsgParamCodes::Accepted: + progressCode = ProgressCode::OK; + errorCode = ErrorCode::RUNNING; + scopeState = ScopeState::Wait; + break; + case ResponseMsgParamCodes::Rejected: + // rejected - should normally not happen, but report the error up + progressCode = ProgressCode::OK; + errorCode = ErrorCode::PROTOCOL_ERROR; + return CommandRejected; + default: + return ProtocolError; + } + } + break; + case ScopeState::QuerySent: + return ProcessCommandQueryResponse(); + case ScopeState::FilamentSensorStateSent: + StartReading8bitRegisters(); + return Processing; + case ScopeState::Reading8bitRegisters: + ProcessRead8bitRegister(); + return Processing; + case ScopeState::Reading16bitRegisters: + scopeState = ProcessRead16bitRegister(ScopeState::Wait); + return Processing; + case ScopeState::ButtonSent: + if (rsp.paramCode == ResponseMsgParamCodes::Accepted) + // Button was accepted, decrement the retry. + DecrementRetryAttempts(); + SendAndUpdateFilamentSensor(); + break; + default: + return ProtocolError; + } + return Processing; + } + + StepStatus ProtocolLogic::IdleWait() { + if (scopeState == ScopeState::Ready) { // check timeout + if (Elapsed(heartBeatPeriod)) { + SendQuery(); + return Processing; + } + } + return Finished; + } + + StepStatus ProtocolLogic::IdleStep() { + switch (scopeState) { + case ScopeState::QuerySent: // check UART + // If we are accidentally in Idle and we receive something like "T0 P1" - that means the communication dropped out while a command was in progress. + // That causes no issues here, we just need to switch to Command processing and continue there from now on. + // The usual response in this case should be some command and "F" - finished - that confirms we are in an Idle state even on the MMU side. + switch (rsp.request.code) { + case RequestMsgCodes::Cut: + case RequestMsgCodes::Eject: + case RequestMsgCodes::Load: + case RequestMsgCodes::Mode: + case RequestMsgCodes::Tool: + case RequestMsgCodes::Unload: + if (rsp.paramCode != ResponseMsgParamCodes::Finished) + return SwitchFromIdleToCommand(); + break; + case RequestMsgCodes::Reset: + // this one is kind of special + // we do not transfer to any "running" command (i.e. we stay in Idle), + // but in case there is an error reported we must make sure it gets propagated + switch (rsp.paramCode) { + case ResponseMsgParamCodes::Button: + // The user pushed a button on the MMU. Save it, do what we need to do + // to prepare, then pass it back to the MMU so it can work its magic. + buttonCode = static_cast(rsp.paramValue); + StartReading8bitRegisters(); + return ButtonPushed; + case ResponseMsgParamCodes::Finished: + if (ReqMsg().code != RequestMsgCodes::unknown) { + // got reset while doing some other command - the originally issued command was interrupted! + // this must be solved by the upper layer, protocol logic doesn't have all the context (like unload before trying again) + IdleRestart(); + return Interrupted; + } + // [[fallthrough]]; + // fall through + case ResponseMsgParamCodes::Processing: + // @@TODO we may actually use this branch to report progress of manual operation on the MMU + // The MMU sends e.g. X0 P27 after its restart when the user presses an MMU button to move the Selector + progressCode = static_cast(rsp.paramValue); + errorCode = ErrorCode::OK; + break; + default: + progressCode = ProgressCode::ERRWaitingForUser; + errorCode = static_cast(rsp.paramValue); + StartReading8bitRegisters(); // continue Idle state without restarting the communication + return CommandError; + } + break; + default: + return ProtocolError; + } + StartReading8bitRegisters(); + return Processing; + case ScopeState::Reading8bitRegisters: + ProcessRead8bitRegister(); + return Processing; + case ScopeState::Reading16bitRegisters: + scopeState = ProcessRead16bitRegister(ScopeState::Ready); + return scopeState == ScopeState::Ready ? Finished : Processing; + case ScopeState::ButtonSent: + if (rsp.paramCode == ResponseMsgParamCodes::Accepted) + // Button was accepted, decrement the retry. + DecrementRetryAttempts(); + StartReading8bitRegisters(); + return Processing; + case ScopeState::ReadRegisterSent: + if (rsp.paramCode == ResponseMsgParamCodes::Accepted) { + // @@TODO just dump the value onto the serial + } + return Finished; + case ScopeState::WriteRegisterSent: + if (rsp.paramCode == ResponseMsgParamCodes::Accepted) { + // @@TODO do something? Retry if not accepted? + } + return Finished; + default: + return ProtocolError; + } + + // The "return Finished" in this state machine requires a bit of explanation: + // The Idle state either did nothing (still waiting for the heartbeat timeout) + // or just successfully received the answer to Q0, whatever that was. + // In both cases, it is ready to hand over work to a command or something else, + // therefore we are returning Finished (also to exit mmu_loop() and unblock Marlin's loop!). + // If there is no work, we'll end up in the Idle state again + // and we'll send the heartbeat message after the specified timeout. + return Finished; + } + + ProtocolLogic::ProtocolLogic(uint8_t extraLoadDistance, uint8_t pulleySlowFeedrate) + : explicitPrinterError(ErrorCode::OK) + , currentScope(Scope::Stopped) + , scopeState(ScopeState::Ready) + , plannedRq(RequestMsgCodes::unknown, 0) + , lastUARTActivityMs(0) + , dataTO() + , rsp(RequestMsg(RequestMsgCodes::unknown, 0), ResponseMsgParamCodes::unknown, 0) + , state(State::Stopped) + , lrb(0) + , errorCode(ErrorCode::OK) + , progressCode(ProgressCode::OK) + , buttonCode(Buttons::NoButton) + , lastFSensor((uint8_t)WhereIsFilament()) + , regIndex(0) + , retryAttempts(MMU3_MAX_RETRIES) + , inAutoRetry(false) { + // @@TODO currently, I don't see a way of writing the initialization better :( + // I'd like to write something like: initRegs8 { extraLoadDistance, pulleySlowFeedrate } + // avr-gcc seems to like such a syntax, ARM gcc doesn't + initRegs8[0] = extraLoadDistance; + initRegs8[1] = pulleySlowFeedrate; + } + + void ProtocolLogic::start() { + state = State::InitSequence; + currentScope = Scope::StartSeq; + protocol.ResetResponseDecoder(); // important - finished delayed restart relies on this + StartSeqRestart(); + } + + void ProtocolLogic::stop() { + state = State::Stopped; + currentScope = Scope::Stopped; + } + + void ProtocolLogic::ToolChange(uint8_t slot) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Tool, slot)); + } + + void ProtocolLogic::Statistics() { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Version, 3)); + } + + void ProtocolLogic::UnloadFilament() { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Unload, 0)); + } + + void ProtocolLogic::LoadFilament(uint8_t slot) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Load, slot)); + } + + void ProtocolLogic::EjectFilament(uint8_t slot) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Eject, slot)); + } + + void ProtocolLogic::CutFilament(uint8_t slot) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Cut, slot)); + } + + void ProtocolLogic::ResetMMU(uint8_t mode /* = 0 */) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Reset, mode)); + } + + void ProtocolLogic::button(uint8_t index) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Button, index)); + } + + void ProtocolLogic::home(uint8_t mode) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Home, mode)); + } + + void ProtocolLogic::readRegister(uint8_t address) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Read, address)); + } + + void ProtocolLogic::writeRegister(uint8_t address, uint16_t data) { + PlanGenericRequest(RequestMsg(RequestMsgCodes::Write, address, data)); + } + + void ProtocolLogic::PlanGenericRequest(RequestMsg rq) { + plannedRq = rq; + if (!ExpectsResponse()) + ActivatePlannedRequest(); + // otherwise wait for an empty window to activate the request + } + + bool ProtocolLogic::ActivatePlannedRequest() { + switch (plannedRq.code) { + case RequestMsgCodes::Button: + // only issue the button to the MMU and do not restart the state machines + SendButton(plannedRq.value); + plannedRq = RequestMsg(RequestMsgCodes::unknown, 0); + return true; + case RequestMsgCodes::Read: + SendReadRegister(plannedRq.value, ScopeState::ReadRegisterSent); + plannedRq = RequestMsg(RequestMsgCodes::unknown, 0); + return true; + case RequestMsgCodes::Write: + SendWriteRegister(plannedRq.value, plannedRq.value2, ScopeState::WriteRegisterSent); + plannedRq = RequestMsg(RequestMsgCodes::unknown, 0); + return true; + case RequestMsgCodes::unknown: + return false; + default: // commands + currentScope = Scope::Command; + SetRequestMsg(plannedRq); + plannedRq = RequestMsg(RequestMsgCodes::unknown, 0); + CommandRestart(); + return true; + } + } + + StepStatus ProtocolLogic::SwitchFromIdleToCommand() { + currentScope = Scope::Command; + SetRequestMsg(rsp.request); + // we are recovering from a communication drop out, the command is already running + // and we have just received a response to a Q0 message about a command progress + return ProcessCommandQueryResponse(); + } + + void ProtocolLogic::SwitchToIdle() { + state = State::Running; + currentScope = Scope::Idle; + IdleRestart(); + } + + void ProtocolLogic::SwitchFromStartToIdle() { + state = State::Running; + currentScope = Scope::Idle; + IdleRestart(); + SendQuery(); // force sending Q0 immediately + } + + bool ProtocolLogic::Elapsed(uint32_t timeout) const { + return ELAPSED(_millis(), lastUARTActivityMs + timeout); + } + + void ProtocolLogic::RecordUARTActivity() { + lastUARTActivityMs = _millis(); + } + + void ProtocolLogic::RecordReceivedByte(uint8_t c) { + lastReceivedBytes[lrb] = c; + lrb = (lrb + 1) % COUNT(lastReceivedBytes); + } + + constexpr char NibbleToChar(uint8_t c) { + switch (c) { + case 0x0 ... 0x9: return c + '0'; + case 0xA ... 0xF: return (c - 10) + 'a'; + default: return 0; + } + } + + void ProtocolLogic::FormatLastReceivedBytes(char *dst) { + for (uint8_t i = 0; i < COUNT(lastReceivedBytes); ++i) { + uint8_t b = lastReceivedBytes[(COUNT(lastReceivedBytes) - 1 + (lrb - i)) % COUNT(lastReceivedBytes)]; + dst[i * 3] = NibbleToChar(b >> 4); + dst[i * 3 + 1] = NibbleToChar(b & 0xf); + dst[i * 3 + 2] = ' '; + } + dst[(COUNT(lastReceivedBytes) - 1) * 3 + 2] = 0; // terminate properly + } + + void ProtocolLogic::FormatLastResponseMsgAndClearLRB(char *dst) { + *dst++ = '<'; + for (uint8_t i = 0; i < lrb; ++i) { + uint8_t b = lastReceivedBytes[i]; + // Check for printable character, including space + if (b < 32 || b > 127) + b = '.'; + *dst++ = b; + } + *dst = 0; // terminate properly + lrb = 0; // reset the input buffer index in case of a clean message + } + + void ProtocolLogic::LogRequestMsg(const uint8_t *txbuff, uint8_t size) { + constexpr uint_fast8_t rqs = modules::protocol::Protocol::MaxRequestSize() + 1; + char tmp[rqs] = ">"; + static char lastMsg[rqs] = ""; + for (uint8_t i = 0; i < size; ++i) { + uint8_t b = txbuff[i]; + // Check for printable character, including space + if (b < 32 || b > 127) + b = '.'; + tmp[i + 1] = b; + } + tmp[size + 1] = 0; + if (!strncmp_P(tmp, PSTR(">S0*c6."), rqs) && !strncmp(lastMsg, tmp, rqs)) { + // @@TODO we skip the repeated request msgs for now + // to avoid spoiling the whole log just with ">S0" messages + // especially when the MMU is not connected. + // We'll lose the ability to see if the printer is actually + // trying to find the MMU, but since it has been reliable in the past + // we can live without it for now. + } + else { + MMU2_ECHO_MSGLN(tmp); + } + strncpy(lastMsg, tmp, rqs); + } + + void ProtocolLogic::LogError(const char *reason_P) { + char _lrb[COUNT(lastReceivedBytes) * 3]; + FormatLastReceivedBytes(_lrb); + + MMU2_ERROR_MSGRPGM(reason_P); + SERIAL_ECHOPGM(", last bytes: "); + SERIAL_ECHOLN(_lrb); + } + + void ProtocolLogic::LogResponse() { + char _lrb[COUNT(lastReceivedBytes)]; + FormatLastResponseMsgAndClearLRB(_lrb); + MMU2_ECHO_MSGLN(_lrb); + } + + StepStatus ProtocolLogic::SuppressShortDropOuts(const char *msg_P, StepStatus ss) { + if (dataTO.Record(ss)) { + LogError(msg_P); + ResetCommunicationTimeoutAttempts(); // prepare for another run of consecutive retries before firing an error + return dataTO.InitialCause(); + } + else { + return Processing; // suppress short drop outs of communication + } + } + + StepStatus ProtocolLogic::HandleCommunicationTimeout() { + MMU_SERIAL.flush(); // clear the output buffer + protocol.ResetResponseDecoder(); + start(); + return SuppressShortDropOuts(PSTR("Communication timeout"), CommunicationTimeout); + } + + StepStatus ProtocolLogic::HandleProtocolError() { + MMU_SERIAL.flush(); // clear the output buffer + state = State::InitSequence; + currentScope = Scope::DelayedRestart; + DelayedRestartRestart(); + return SuppressShortDropOuts(PSTR("Protocol Error"), ProtocolError); + } + + StepStatus ProtocolLogic::Step() { + if (!ExpectsResponse()) // if not waiting for a response, activate a planned request immediately + ActivatePlannedRequest(); + auto currentStatus = ScopeStep(); + switch (currentStatus) { + case Processing: + // we are ok, the state machine continues correctly + break; + case Finished: { + // We are ok, switching to Idle if there is no potential next request planned. + // But the trouble is we must report a finished command if the previous command has just been finished + // i.e. only try to find some planned command if we just finished the Idle cycle + if (!ActivatePlannedRequest()) { // if nothing is planned, switch to Idle + SwitchToIdle(); + } + else if (ExpectsResponse()) { + // if the previous cycle was Idle and now we have planned a new command -> avoid returning Finished + currentStatus = Processing; + } + } + break; + case CommandRejected: + // we have to repeat it - that's the only thing we can do + // no change in state + // @@TODO wait until Q0 returns command in progress finished, then we can send this one + LogError(PSTR("Command rejected")); + CommandRestart(); + break; + case CommandError: + LogError(PSTR("Command Error")); + // we should probably transfer into the Idle state and await further instructions from the upper layer + // Idle state may solve the problem of keeping up the heart beat running + break; + case VersionMismatch: + LogError(PSTR("Version mismatch")); + break; + case ProtocolError: + currentStatus = HandleProtocolError(); + break; + case CommunicationTimeout: + currentStatus = HandleCommunicationTimeout(); + break; + default: + break; + } + // special handling of explicit printer errors + return IsPrinterError() ? StepStatus::PrinterError : currentStatus; + } + + uint8_t ProtocolLogic::CommandInProgress() const { + if (currentScope != Scope::Command) return 0; + return (uint8_t)ReqMsg().code; + } + + void ProtocolLogic::DecrementRetryAttempts() { + if (inAutoRetry && retryAttempts) { + SERIAL_ECHOLNPGM("DecrementRetryAttempts"); + retryAttempts--; + } + } + + void ProtocolLogic::ResetRetryAttempts() { + SERIAL_ECHOLNPGM("ResetRetryAttempts"); + retryAttempts = MMU3_MAX_RETRIES; + } + + void __attribute__((noinline)) ProtocolLogic::ResetCommunicationTimeoutAttempts() { + SERIAL_ECHOLNPGM("RSTCommTimeout"); + dataTO.reset(); + } + + bool DropOutFilter::Record(StepStatus ss) { + if (occurrences == maxOccurrences) cause = ss; + --occurrences; + return occurrences == 0; + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_protocol_logic.h b/Marlin/src/feature/mmu3/mmu3_protocol_logic.h new file mode 100644 index 0000000000..30f9c5d0d5 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_protocol_logic.h @@ -0,0 +1,380 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_protocol_logic.h + */ + +#include "../../MarlinCore.h" + +#include + +#ifdef __AVR__ + #include + #include "mmu_hw/error_codes.h" + #include "mmu_hw/progress_codes.h" + #include "mmu_hw/buttons.h" + #include "mmu_hw/registers.h" + #include "mmu3_protocol.h" +#else // !__AVR__ + + #include "mmu_hw/error_codes.h" + #include "mmu_hw/progress_codes.h" + + // Prevent ARM HAL macros from breaking our code + #undef CRC + #include "mmu3_protocol.h" + #include "mmu_hw/buttons.h" + #include "registers.h" + +#endif // !__AVR__ + +// New MMU3 protocol logic +namespace MMU3 { + + using namespace modules::protocol; + + class ProtocolLogic; + + // ProtocolLogic stepping statuses + enum StepStatus : uint_fast8_t { + Processing = 0, + MessageReady, //!< A message has been successfully decoded from the received bytes + Finished, //!< Scope finished successfully + Interrupted, //!< Received "Finished" message related to a different command than originally issued (most likely the MMU restarted while doing something) + CommunicationTimeout, //!< The MMU failed to respond to a request within a specified time frame + ProtocolError, //!< Bytes read from the MMU didn't form a valid response + CommandRejected, //!< The MMU rejected the command due to some other command in progress, may be the user is operating the MMU locally (button commands) + CommandError, //!< The command in progress stopped due to unrecoverable error, user interaction required + VersionMismatch, //!< The MMU reports its firmware version incompatible with our implementation + PrinterError, //!< Printer's explicit error - MMU is fine, but the printer was unable to complete the requested operation + CommunicationRecovered, + ButtonPushed //!< The MMU reported the user pushed one of its three buttons. + }; + + /*inline*/ constexpr uint32_t linkLayerTimeout = 2000; //!< Default link layer communication timeout + /*inline*/ constexpr uint32_t dataLayerTimeout = linkLayerTimeout * 3; //!< Data layer communication timeout + /*inline*/ constexpr uint32_t heartBeatPeriod = linkLayerTimeout / 2; //!< Period of heart beat messages (Q0) + + static_assert(heartBeatPeriod < linkLayerTimeout && linkLayerTimeout < dataLayerTimeout, "Incorrect ordering of timeouts"); + + //!< Filter of short consecutive drop outs which are recovered instantly + class DropOutFilter { + public: + static constexpr uint8_t maxOccurrences = 10; // ideally set this to >8 seconds -> 12x heartBeatPeriod + static_assert(maxOccurrences > 1, "we should really silently ignore at least 1 comm drop out if recovered immediately afterwards"); + DropOutFilter() = default; + + // @return true if the error should be reported to higher levels (max. number of consecutive occurrences reached) + bool Record(StepStatus ss); + + // @return the initial cause which started this drop out event + inline StepStatus InitialCause() const { return cause; } + + // Rearms the object for further processing - basically call this once the MMU responds with something meaningful (e.g. S0 A2) + inline void reset() { occurrences = maxOccurrences; } + + private: + StepStatus cause; + uint8_t occurrences = maxOccurrences; + }; + + // Logic layer of the MMU vs. printer communication protocol + class ProtocolLogic { + public: + ProtocolLogic(uint8_t extraLoadDistance, uint8_t pulleySlowFeedrate); + + // Start/Enable communication with the MMU + void start(); + + // Stop/Disable communication with the MMU + void stop(); + + // Issue commands to the MMU + void ToolChange(uint8_t slot); + void Statistics(); + void UnloadFilament(); + void LoadFilament(uint8_t slot); + void EjectFilament(uint8_t slot); + void CutFilament(uint8_t slot); + void ResetMMU(uint8_t mode=0); + void button(uint8_t index); + void home(uint8_t mode); + void readRegister(uint8_t address); + void writeRegister(uint8_t address, uint16_t data); + + // Set the extra load distance to be reported to the MMU. + // Beware - this call doesn't send anything to the MMU. + // The MMU gets the newly set value either by a communication restart or via an explicit writeRegister call + inline void PlanExtraLoadDistance(uint8_t eld_mm) { initRegs8[0] = eld_mm; } + // @return the currently preset extra load distance + inline uint8_t ExtraLoadDistance() const { return initRegs8[0]; } + + // Sets the Pulley slow feed rate to be reported to the MMU. + // Beware - this call doesn't send anything to the MMU. + // The MMU gets the newly set value either by a communication restart or via an explicit writeRegister call + inline void PlanPulleySlowFeedRate(uint8_t psfr) { + initRegs8[1] = psfr; + } + // @return the currently preset Pulley slow feed rate + inline uint8_t PulleySlowFeedRate() const { + return initRegs8[1]; // even though MMU register 0x14 is 16bit, reasonable speeds are way below 255mm/s - saving space ;) + } + + // Step the state machine + StepStatus Step(); + + // @return the current/latest error code as reported by the MMU + ErrorCode Error() const { return errorCode; } + + // @return the current/latest process code as reported by the MMU + ProgressCode Progress() const { return progressCode; } + + // @return the current/latest button code as reported by the MMU + Buttons button() const { return buttonCode; } + + uint8_t CommandInProgress() const; + + inline bool Running() const { return state == State::Running; } + + inline bool findaPressed() const { return regs8[0]; } + + inline uint16_t FailStatistics() const { return regs16[0]; } + + inline uint8_t mmuFwVersionMajor() const { return mmuFwVersion[0]; } + inline uint8_t mmuFwVersionMinor() const { return mmuFwVersion[1]; } + inline uint8_t mmuFwVersionRevision() const { return mmuFwVersion[2]; } + + // Current number of retry attempts left + constexpr uint8_t RetryAttempts() const { return retryAttempts; } + + // Decrement the retry attempts, if in a retry. + // Called by the MMU protocol when a sent button is acknowledged. + void DecrementRetryAttempts(); + + // Reset the retryAttempts back to the default value + void ResetRetryAttempts(); + + void ResetCommunicationTimeoutAttempts(); + + constexpr bool InAutoRetry() const { return inAutoRetry; } + inline void SetInAutoRetry(const bool iar) { inAutoRetry = iar; } + + inline void SetPrinterError(const ErrorCode ec) { explicitPrinterError = ec; } + inline void clearPrinterError() { explicitPrinterError = ErrorCode::OK; } + inline bool IsPrinterError() const { return explicitPrinterError != ErrorCode::OK; } + inline ErrorCode PrinterError() const { return explicitPrinterError; } + + #ifndef UNITTEST + private: + #endif + + StepStatus ExpectingMessage(); + void SendMsg(RequestMsg rq); + void SendWriteMsg(RequestMsg rq); + void SwitchToIdle(); + StepStatus SuppressShortDropOuts(const char *msg_P, StepStatus ss); + StepStatus HandleCommunicationTimeout(); + StepStatus HandleProtocolError(); + bool Elapsed(uint32_t timeout) const; + void RecordUARTActivity(); + void RecordReceivedByte(uint8_t c); + void FormatLastReceivedBytes(char *dst); + void FormatLastResponseMsgAndClearLRB(char *dst); + void LogRequestMsg(const uint8_t *txbuff, uint8_t size); + void LogError(const char *reason_P); + void LogResponse(); + StepStatus SwitchFromIdleToCommand(); + void SwitchFromStartToIdle(); + + ErrorCode explicitPrinterError; + + enum class State : uint_fast8_t { + Stopped, //!< stopped for whatever reason + InitSequence, //!< initial sequence running + Running //!< normal operation - Idle + Command processing + }; + + enum class Scope : uint_fast8_t { + Stopped, + StartSeq, + DelayedRestart, + Idle, + Command + }; + Scope currentScope; + + // basic scope members + // @return true if the state machine is waiting for a response from the MMU + bool ExpectsResponse() const { return ((uint8_t)scopeState & (uint8_t)ScopeState::NotExpectsResponse) == 0; } + + // Common internal states of the derived sub-automata + // General rule of thumb: *Sent states are waiting for a response from the MMU + enum class ScopeState : uint_fast8_t { + S0Sent, // beware - due to optimization reasons these SxSent must be kept one after another + S1Sent, + S2Sent, + S3Sent, + QuerySent, + CommandSent, + FilamentSensorStateSent, + Reading8bitRegisters, + Reading16bitRegisters, + WritingInitRegisters, + ButtonSent, + ReadRegisterSent, // standalone requests for reading registers - from higher layers + WriteRegisterSent, + + // States which do not expect a message - MSb set + NotExpectsResponse = 0x80, + Wait = NotExpectsResponse + 1, + Ready = NotExpectsResponse + 2, + RecoveringProtocolError = NotExpectsResponse + 3, + }; + + ScopeState scopeState; //!< internal state of the sub-automaton + + // @return the status of processing of the FINDA query response + // @param finishedRV returned value in case the message was successfully received and processed + // @param nextState is a state where the state machine should transfer to after the message was successfully received and processed + // StepStatus ProcessFINDAReqSent(StepStatus finishedRV, State nextState); + + // @return the status of processing of the statistics query response + // @param finishedRV returned value in case the message was successfully received and processed + // @param nextState is a state where the state machine should transfer to after the message was successfully received and processed + // StepStatus ProcessStatisticsReqSent(StepStatus finishedRV, State nextState); + + // Called repeatedly while waiting for a query (Q0) period. + // All event checks to report immediately from the printer to the MMU should be done in this method. + // So far, the only such a case is the filament sensor, but there can be more like this in the future. + void CheckAndReportAsyncEvents(); + void SendQuery(); + void StartReading8bitRegisters(); + void ProcessRead8bitRegister(); + void StartReading16bitRegisters(); + ScopeState ProcessRead16bitRegister(ProtocolLogic::ScopeState stateAtEnd); + void StartWritingInitRegisters(); + // @return true when all registers have been written into the MMU + bool ProcessWritingInitRegister(); + void SendAndUpdateFilamentSensor(); + void SendButton(uint8_t btn); + void SendVersion(uint8_t stage); + void SendReadRegister(uint8_t index, ScopeState nextState); + void SendWriteRegister(uint8_t index, uint16_t value, ScopeState nextState); + + StepStatus ProcessVersionResponse(uint8_t stage); + + // Top level split - calls the appropriate step based on current scope + StepStatus ScopeStep(); + + static constexpr uint8_t maxRetries = 6; + uint8_t retries; + + void StartSeqRestart(); + void DelayedRestartRestart(); + void IdleRestart(); + void CommandRestart(); + + StepStatus StartSeqStep(); + StepStatus DelayedRestartWait(); + StepStatus IdleStep(); + StepStatus IdleWait(); + StepStatus CommandStep(); + StepStatus CommandWait(); + StepStatus StoppedStep() { return Processing; } + + StepStatus ProcessCommandQueryResponse(); + + inline void SetRequestMsg(const RequestMsg msg) { rq = msg; } + inline const RequestMsg &ReqMsg() const { return rq; } + RequestMsg rq = RequestMsg(RequestMsgCodes::unknown, 0); + + // Records the next planned state, "unknown" msg code if no command is planned. + // This is not intended to be a queue of commands to process, protocol_logic must not queue commands. + // It exists solely to prevent breaking the Request-Response protocol handshake - + // - during tests it turned out, that the commands from Marlin are coming in such an asynchronnous way, that + // we could accidentally send T2 immediately after Q0 without waiting for reception of response to Q0. + // + // Beware, if Marlin manages to call PlanGenericCommand multiple times before a response comes, + // these variables will get overwritten by the last call. + // However, that should not happen under normal circumstances as Marlin should wait for the Command to finish, + // which includes all responses (and error recovery if any). + RequestMsg plannedRq; + + // Plan a command to be processed once the immediate response to a sent request arrives + void PlanGenericRequest(RequestMsg rq); + // Activate the planned state once the immediate response to a sent request arrived + bool ActivatePlannedRequest(); + + uint32_t lastUARTActivityMs; //!< timestamp - last ms when something occurred on the UART + DropOutFilter dataTO; //!< Filter of short consecutive drop outs which are recovered instantly + + ResponseMsg rsp; //!< decoded response message from the MMU protocol + + State state; //!< internal state of ProtocolLogic + + Protocol protocol; //!< protocol codec + + uint8_t lrb, lastReceivedBytes[16]; //!< keep the last few bytes of incoming communication for diagnostic purposes + + ErrorCode errorCode; //!< last received error code from the MMU + ProgressCode progressCode; //!< last received progress code from the MMU + Buttons buttonCode; //!< Last received button from the MMU. + + uint8_t lastFSensor; //!< last state of filament sensor + + #ifndef __AVR__ + uint8_t txbuff[Protocol::MaxRequestSize()]; //!< In Buddy FW - a static transmit buffer needs to exist as DMA cannot be used from CCMRAM. + //!< On MK3/S/+ the transmit buffer is allocated on the stack without restrictions + #endif + + // 8bit registers + static constexpr uint8_t regs8Count = 3; + static_assert(regs8Count > 0); // code is not ready for empty lists of registers + static const Register regs8Addrs[regs8Count] PROGMEM; + uint8_t regs8[regs8Count] = { 0, 0, 0 }; + + // 16bit registers + static constexpr uint8_t regs16Count = 2; + static_assert(regs16Count > 0); // code is not ready for empty lists of registers + static const Register regs16Addrs[regs16Count] PROGMEM; + uint16_t regs16[regs16Count] = { 0, 0 }; + + // 8bit init values to be sent to the MMU after line up + static constexpr uint8_t initRegs8Count = 2; + static_assert(initRegs8Count > 0); // code is not ready for empty lists of registers + static const Register initRegs8Addrs[initRegs8Count] PROGMEM; + uint8_t initRegs8[initRegs8Count]; + + uint8_t regIndex; + + uint8_t mmuFwVersion[3] = { 0, 0, 0 }; + uint16_t mmuFwVersionBuild; + + uint8_t retryAttempts; + bool inAutoRetry; + + friend class MMU3; + }; + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_reporting.cpp b/Marlin/src/feature/mmu3/mmu3_reporting.cpp new file mode 100644 index 0000000000..426aa4d825 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_reporting.cpp @@ -0,0 +1,704 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * mmu2_reporting.cpp + */ + +#include "../../inc/MarlinConfig.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3.h" +#include "mmu3_log.h" +#include "mmu3_fsensor.h" +#include "mmu3_reporting.h" +#include "mmu3_error_converter.h" +#include "mmu3_marlin_macros.h" +#include "mmu3_progress_converter.h" +#include "mmu_hw/buttons.h" +#include "mmu_hw/error_codes.h" +#include "mmu_hw/errors_list.h" +#include "ultralcd.h" +#include "sound.h" + +#include "../../core/language.h" +#include "../../gcode/gcode.h" +#include "../host_actions.h" +#include "../../lcd/marlinui.h" +#include "../../lcd/menu/menu.h" +#include "../../lcd/menu/menu_item.h" +#include "../../module/temperature.h" + +namespace MMU3 { + + OperationStatistics operation_statistics; + + uint16_t OperationStatistics::fail_total_num; // total failures + uint8_t OperationStatistics::fail_num; // fails during print + uint16_t OperationStatistics::load_fail_total_num; // total load failures + uint8_t OperationStatistics::load_fail_num; // load failures during print + uint16_t OperationStatistics::tool_change_counter; // number of tool changes per print + uint32_t OperationStatistics::tool_change_total_counter; // number of total tool changes + int OperationStatistics::fail_total_num_addr; // total failures EEPROM addr + int OperationStatistics::fail_num_addr; // fails during print EEPROM addr + int OperationStatistics::load_fail_total_num_addr; // total load failures EEPROM addr + int OperationStatistics::load_fail_num_addr; // load failures during print EEPROM addr + int OperationStatistics::tool_change_counter_addr; // number of total tool changes EEPROM addr + int OperationStatistics::tool_change_total_counter_addr; // number of total tool changes EEPROM addr + + /** + * Increment both the total load fails and Per print job load fails. + */ + void OperationStatistics::increment_load_fails() { + load_fail_num += 1; + load_fail_total_num += 1; + + #if ENABLED(EEPROM_SETTINGS) + // save load_fail_num to eeprom + persistentStore.access_start(); + persistentStore.write_data(load_fail_num_addr, load_fail_num); + + // save load_fail_total_num to eeprom + persistentStore.write_data(load_fail_total_num_addr, load_fail_total_num); + persistentStore.access_finish(); + settings.save(); + #endif + } + + /** + * Increment both the total fails and the per print job fails. + */ + void OperationStatistics::increment_mmu_fails() { + fail_num += 1; + fail_total_num += 1; + + #if ENABLED(EEPROM_SETTINGS) + // save fail_num to eeprom + persistentStore.access_start(); + persistentStore.write_data(fail_num_addr, fail_num); + // save fail_total_num to eeprom + persistentStore.write_data(fail_total_num_addr, fail_total_num); + persistentStore.access_finish(); + settings.save(); + #endif + } + + /** + * Increment tool change counter + */ + void OperationStatistics::increment_tool_change_counter() { + tool_change_counter += 1; + tool_change_total_counter += 1; + + #if ENABLED(EEPROM_SETTINGS) + // save tool_change_total_counter to eeprom + persistentStore.access_start(); + persistentStore.write_data(tool_change_total_counter_addr, tool_change_total_counter); + persistentStore.access_finish(); + settings.save(); + #endif + } + + + /** + * Reset only per print operation statistics and update EEPROM. + * + * @return true if everything went okay, false otherwise. + */ + bool OperationStatistics::reset_per_print_stats() { + // Update data + load_fail_num = 0; + fail_num = 0; + tool_change_counter = 0; + + #if ENABLED(EEPROM_SETTINGS) + // Update EEPROM + persistentStore.access_start(); + persistentStore.write_data(load_fail_num_addr, load_fail_num); + persistentStore.write_data(fail_num_addr, fail_num); + persistentStore.write_data(tool_change_counter_addr, tool_change_counter); + persistentStore.access_finish(); + return settings.save(); + #else + return true; + #endif + } + + + /** + * Reset fail statistics and update EEPROM. + * + * This will keep the tool change counter change counters and delete anything + * else. + * + * @return true if everything went okay, false otherwise. + */ + bool OperationStatistics::reset_fail_stats() { + // Update data + load_fail_num = 0; + load_fail_total_num = 0; + fail_num = 0; + fail_total_num = 0; + + #if ENABLED(EEPROM_SETTINGS) + // Update EEPROM + persistentStore.access_start(); + persistentStore.write_data(load_fail_num_addr, load_fail_num); + persistentStore.write_data(load_fail_total_num_addr, load_fail_total_num); + persistentStore.write_data(fail_num_addr, fail_num); + persistentStore.write_data(fail_total_num_addr, fail_total_num); + persistentStore.access_finish(); + return settings.save(); + #else + return true; + #endif + } + + + /** + * Reset all operation statistics and update EEPROM. + * + * @return true if everything went okay, false otherwise. + */ + bool OperationStatistics::reset_stats() { + // Update data + load_fail_num = 0; + load_fail_total_num = 0; + fail_num = 0; + fail_total_num = 0; + tool_change_counter = 0; + tool_change_total_counter = 0; + + #if ENABLED(EEPROM_SETTINGS) + // Update EEPROM + persistentStore.access_start(); + persistentStore.write_data(load_fail_num_addr, load_fail_num); + persistentStore.write_data(load_fail_total_num_addr, load_fail_total_num); + persistentStore.write_data(fail_num_addr, fail_num); + persistentStore.write_data(fail_total_num_addr, fail_total_num); + persistentStore.write_data(tool_change_counter_addr, tool_change_counter); + persistentStore.write_data(tool_change_total_counter_addr, tool_change_total_counter); + persistentStore.access_finish(); + return settings.save(); + #else + return true; + #endif + } + + + void BeginReport(CommandInProgress /*cip*/, ProgressCode ec) { + // custom_message_type = CustomMsg::MMUProgress; + ui.set_status(ProgressCodeToText(ec)); + } + + void EndReport(CommandInProgress /*cip*/, ProgressCode /*ec*/) { + // clear the status msg line - let the printed filename get visible again + if (!printJobOngoing()) ui.reset_status(); + //custom_message_type = CustomMsg::Status; + } + + /** + * @brief Renders any characters that will be updated live on the MMU error screen. + *Currently, this is FINDA and Filament Sensor status and Extruder temperature. + */ + extern void ReportErrorHookDynamicRender(void) { + #if HAS_WIRED_LCD + // beware - this optimization abuses the fact, that findaDetectsFilament returns 0 or 1 and '0' is followed by '1' in the ASCII table + lcd_put_int(3, LCD_HEIGHT - 1, mmu3.findaDetectsFilament() + '0'); + lcd_put_int(8, LCD_HEIGHT - 1, FILAMENT_PRESENT() + '0'); + + // print active/changing filament slot + lcd_moveto(10, LCD_HEIGHT - 1); + lcdui_print_extruder(); + + // Print active extruder temperature + lcd_put_int(16, LCD_HEIGHT - 1, (int)(thermalManager.degHotend(0) + 0.5)); + #endif + } + + static bool drawing_more_info_screen = false; + static bool msg_next_is_consumed = false; + static FSTR_P msg_next = nullptr; + + /** + * Display more info about the error. If the error message doesn't fit into + * the screen, clicking the LCD button will go to the next screen to display + * the rest of the message, until no messages left to display and a final + * click will return to the previous screen. + * + * This gets the message data from the "editable.uint8" which is set in the + * action item. + */ + void show_more_info_screen() { + #if HAS_WIRED_LCD + if (drawing_more_info_screen) return; + drawing_more_info_screen = true; + FSTR_P fmsg = PrusaErrorDesc(editable.uint8); + if (ui.use_click()) { + if (msg_next_is_consumed) { + msg_next_is_consumed = false; + drawing_more_info_screen = false; + msg_next = nullptr; + // Prevent this function being triggered again... + SetButtonResponse(ButtonOperations::NoOperation); + return ui.go_back(); + } + fmsg = msg_next; + } + else if (msg_next_is_consumed) { + fmsg = msg_next; + } + + FSTR_P const msg_next_int = lcd_display_message_fullscreen(fmsg); + msg_next_is_consumed = strlen_P(FTOP(msg_next_int)) == 0; + if (!msg_next_is_consumed) msg_next = msg_next_int; + // Set the button response to MoreInfo so we keep coming back to this screen until all messages are consumed + SetButtonResponse(ButtonOperations::MoreInfo); + #else + // no lcd, no error display... just break the loop... + msg_next_is_consumed = false; + msg_next = nullptr; + SetButtonResponse(ButtonOperations::NoOperation); + #endif // HAS_WIRED_LCD + drawing_more_info_screen = false; + } + + /** + * @brief Renders any characters that are static on the MMU error screen i.e. they don't change. + * @param[in] ei Error code index + */ + static void ReportErrorHookStaticRender(uint8_t ei) { + #if HAS_WIRED_LCD + //! Show an error screen + //! When an MMU error occurs, the LCD content will look like this: + //! |01234567890123456789| + //! |MMU FW update needed| <- title/header of the error: max 20 characters + //! |prusa.io/04504 | <- URL max 20 characters + //! |FI:1 FS:1 5>3 t201°| <- status line, t is thermometer symbol + //! |>Retry >Done >W| <- buttons + bool two_choices = false; + + // Read and determine what operations should be shown on the menu + const uint8_t button_operation = PrusaErrorButtons(ei), + button_op_right = BUTTON_OP_RIGHT(button_operation), + button_op_middle = BUTTON_OP_MIDDLE(button_operation); + + // Check if the menu should have three or two choices + if (button_op_right == (uint8_t)ButtonOperations::NoOperation) { + // Two operations not specified, the error menu should only show two choices + two_choices = true; + } + + START_MENU(); + #ifndef __AVR__ + // TODO: I couldn't make this work on AVR + STATIC_ITEM_F(PrusaErrorTitle(ei), SS_DEFAULT | SS_INVERT); + + // Write the help page and error code + MString url(""); + url.appendf("prusa.io/04%hu", PrusaErrorCode(ei)); + STATIC_ITEM_F(nullptr, SS_DEFAULT, url.buffer()); + + //ReportErrorHookSensorLineRender(); + + editable.uint8 = button_op_middle; + ACTION_ITEM_F( + PrusaErrorButtonTitle(button_op_middle), + []{ SetButtonResponse((ButtonOperations)editable.uint8); } + ); + + if (!two_choices) { + editable.uint8 = button_op_right; + ACTION_ITEM_F( + PrusaErrorButtonTitle(button_op_right), + []{ SetButtonResponse((ButtonOperations)editable.uint8); } + ); + } + + // Add a More Info option + editable.uint8 = ei; + ACTION_ITEM_F( + GET_TEXT_F(MSG_BTN_MORE), + []{ + // only when the menu item is used push the current screen back + ui.push_current_screen(); + msg_next_is_consumed = false; + msg_next = nullptr; + SetButtonResponse(ButtonOperations::MoreInfo); + } + ); + + #endif // !__AVR__ + + // Render the choices + //if (two_choices) { + // lcd_show_choices_prompt_P( + // LCD_LEFT_BUTTON_CHOICE, + // PrusaErrorButtonTitle(button_op_middle), + // GET_TEXT(MSG_BTN_MORE), + // 18, nullptr + // ); + //} + //else { + // lcd_show_choices_prompt_P(LCD_MIDDLE_BUTTON_CHOICE, + // PrusaErrorButtonTitle(button_op_middle), + // PrusaErrorButtonTitle(button_op_right), + // 9, GET_TEXT(MSG_BTN_MORE) + // ); + //} + + END_MENU(); + //ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); + #endif // HAS_WIRED_LCD + } + + void ReportErrorHookSensorLineRender() { + #if HAS_WIRED_LCD + // Render static characters in third line + lcd_put_u8str( + 0, + LCD_HEIGHT - 1, + F("FI: FS: > " LCD_STR_THERMOMETER " " LCD_STR_DEGREE) + ); + #endif + } + + /** + * @brief Monitors the LCD button selection without blocking MMU communication + * @param[in] ei Error code index + * @return 0 if there is no knob click -- + * 1 if user clicked 'More' and firmware should render + * the error screen when ReportErrorHook is called next -- + * 2 if the user selects an operation and we would like + * to exit the error screen. The MMU will raise the menu + * again if the error is not solved. + */ + static uint8_t ReportErrorHookMonitor(uint8_t ei) { + uint8_t ret = 0; + if (GetButtonResponse() == ButtonOperations::MoreInfo) { + SetButtonResponse(ButtonOperations::NoOperation); + ret = 1; + } + else if (GetButtonResponse() != ButtonOperations::NoOperation) { + ret = 2; + } + // Next MMU error screen should reset the choice selection + // reset_button_selection = 1; + return ret; + } + + enum class ReportErrorHookStates : uint8_t { + RENDER_ERROR_SCREEN = 0, + MONITOR_SELECTION = 1, + DISMISS_ERROR_SCREEN = 2, + }; + + enum ReportErrorHookStates ReportErrorHookState = ReportErrorHookStates::RENDER_ERROR_SCREEN; + + // Helper variable to monitor knob in MMU error screen in blocking functions e.g. manage_response + static bool is_mmu_error_monitor_active; + + // Helper variable to stop rendering the error screen when the firmware is rendering complementary + // UI to resolve the error screen, for example tuning Idler Stallguard Threshold + // Set to false to allow the error screen to render again. + static bool putErrorScreenToSleep; + + void CheckErrorScreenUserInput() { + if (is_mmu_error_monitor_active) { + // Call this every iteration to keep the knob rotation responsive + // This includes when mmu_loop is called within manage_response + ReportErrorHook((CommandInProgress)mmu3.getCommandInProgress(), mmu3.getLastErrorCode(), mmu3.mmuLastErrorSource()); + } + } + + bool TuneMenuEntered() { + return putErrorScreenToSleep; + } + + void ReportErrorHook(CommandInProgress /*cip*/, ErrorCode ec, uint8_t /*es*/) { + if (putErrorScreenToSleep) return; + + if (mmu3.mmuCurrentErrorCode() == ErrorCode::OK && mmu3.mmuLastErrorSource() == MMU3::ErrorSourceMMU) { + // If the error code suddenly changes to OK, that means + // a button was pushed on the MMU and the LCD should + // dismiss the error screen until MMU raises a new error + ReportErrorHookState = ReportErrorHookStates::DISMISS_ERROR_SCREEN; + drawing_more_info_screen = false; + msg_next_is_consumed = true; + } + + const uint8_t ei = PrusaErrorCodeIndex((ErrorCode)ec); + + // This should be the equivalent of the switch..case above... + if ((uint8_t)ReportErrorHookState == (uint8_t)ReportErrorHookStates::RENDER_ERROR_SCREEN) { + KEEPALIVE_STATE(PAUSED_FOR_USER); + #if HAS_WIRED_LCD + drawing_more_info_screen = false; + msg_next_is_consumed = false; + msg_next = nullptr; + editable.uint8 = ei; + ui.defer_status_screen(); + ui.goto_screen([]{ ReportErrorHookStaticRender(editable.uint8); }); + #endif + ReportErrorHookState = ReportErrorHookStates::MONITOR_SELECTION; + } + + if ((uint8_t)ReportErrorHookState == (uint8_t)ReportErrorHookStates::MONITOR_SELECTION) { + is_mmu_error_monitor_active = true; + // ReportErrorHookDynamicRender(); // Render dynamic characters + sound_wait_for_user(); + uint8_t result = ReportErrorHookMonitor(ei); + if (result == 0) { + // No choice selected, return to loop() + } + else if (result == 1) { + // More Info button selected, change state + editable.uint8 = ei; + //ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); + ui.goto_screen(show_more_info_screen); + ReportErrorHookState = ReportErrorHookStates::MONITOR_SELECTION; + } + else if (result == 2) { + // Exit error screen and enable lcd updates + TERN_(HAS_WIRED_LCD, ui.return_to_status()); + sound_wait_for_user_reset(); + // Reset the state in case a new error is reported + is_mmu_error_monitor_active = false; + KEEPALIVE_STATE(IN_HANDLER); + ReportErrorHookState = ReportErrorHookStates::RENDER_ERROR_SCREEN; + } + return; // Always return to loop() to let MMU trigger a call to ReportErrorHook again + } + else if ((uint8_t)ReportErrorHookState == (uint8_t)ReportErrorHookStates::DISMISS_ERROR_SCREEN) { + TERN_(HAS_WIRED_LCD, ui.return_to_status()); + sound_wait_for_user_reset(); + // Reset the state in case a new error is reported + is_mmu_error_monitor_active = false; + KEEPALIVE_STATE(IN_HANDLER); + ReportErrorHookState = ReportErrorHookStates::RENDER_ERROR_SCREEN; + } + } + + void ReportProgressHook(CommandInProgress cip, ProgressCode ec) { + if (cip != CommandInProgress::NoCommand) { + // custom_message_type = CustomMsg::MMUProgress; + ui.set_status(ProgressCodeToText(ec)); + } + } + + TryLoadUnloadReporter::TryLoadUnloadReporter(float delta_mm) + : dpixel0(0), dpixel1(0), lcd_cursor_col(0) + , pixel_per_mm(0.5F * float(LCD_WIDTH) / (delta_mm) + ) { + //lcd_clearstatus(); + ui.reset_status(); + } + + TryLoadUnloadReporter::~TryLoadUnloadReporter() { + #if HAS_WIRED_LCD + // Delay the next status message just so + // the user can see the results clearly + ui.set_status_no_expire(ui.status_message); + #endif + } + + void TryLoadUnloadReporter::Render(uint8_t col, bool sensorState) { + #if HAS_WIRED_LCD + // Set the cursor position each time in case some other + // part of the firmware changes the cursor position + lcd_insert_char_into_status(col, sensorState ? LCD_STR_SOLID_BLOCK[0] : '-'); + if (ui.lcdDrawUpdate == LCDViewAction::LCDVIEW_NONE) + ui.draw_status_message(false); + #endif + } + + void TryLoadUnloadReporter::Progress(bool sensorState) { + // Always round up, you can only have 'whole' pixels. (floor is also an option) + dpixel1 = ceil((stepper_get_machine_position_E_mm() - planner_get_current_position_E()) * pixel_per_mm); + if (dpixel1 - dpixel0) { + dpixel0 = dpixel1; + if (lcd_cursor_col > (LCD_WIDTH - 1)) lcd_cursor_col = LCD_WIDTH - 1; + Render(lcd_cursor_col++, sensorState); + } + } + + void TryLoadUnloadReporter::DumpToSerial() { + char buf[LCD_WIDTH + 1]; + TERN_(HAS_WIRED_LCD, ui.status_message.copyto(buf)); + for (uint8_t i = 0; i < sizeof(buf); i++) { + // 0xFF is -1 when converting from unsigned to signed char + // If the number is negative, that means filament is present + buf[i] = (buf[i] < 0) ? '1' : '0'; + } + buf[LCD_WIDTH] = 0; + MMU2_ECHO_MSGLN(buf); + } + + void IncrementLoadFails() { + operation_statistics.increment_load_fails(); + } + + void IncrementMMUFails() { + operation_statistics.increment_mmu_fails(); + } + + bool cutter_enabled() { + return mmu3.cutter_mode > 0; + } + + void MakeSound(SoundType s) { + Sound_MakeSound((eSOUND_TYPE)s); + } + + static void fullScreenMsg(FSTR_P const fstr, uint8_t slot) { + #if HAS_WIRED_LCD + ui.clear_lcd(); + #ifndef __AVR__ + SETCURSOR(0, 1); + lcd_put_u8str(fstr); + lcd_put_lchar(' '); + lcd_put_int(slot + 1); + #else + UNUSED(fstr); + #endif + ui.refresh(LCDVIEW_CALL_REDRAW_NEXT); + ui.screen_changed = true; + #endif + } + + void fullScreenMsgCut(uint8_t slot) { fullScreenMsg(GET_TEXT_F(MSG_CUT_FILAMENT), slot); } + void fullScreenMsgEject(uint8_t slot) { fullScreenMsg(GET_TEXT_F(MSG_EJECT_FROM_MMU), slot); } + void fullScreenMsgTest(uint8_t slot) { fullScreenMsg(GET_TEXT_F(MSG_TESTING_FILAMENT), slot); } + void fullScreenMsgLoad(uint8_t slot) { fullScreenMsg(GET_TEXT_F(MSG_LOADING_FILAMENT), slot); } + + void fullScreenMsgRestoringTemperature() { + #if HAS_WIRED_LCD + lcd_display_message_fullscreen(F("MMU Retry: Restoring temperature...")); + #endif + } + + void ScreenUpdateEnable() { + TERN_(HAS_WIRED_LCD, ui.refresh(LCDVIEW_CALL_REDRAW_NEXT)); + } + + void ScreenClear() { ui.clear_lcd(); } + + struct TuneItem { + uint8_t address; + uint8_t minValue; + uint8_t maxValue; + } + __attribute__((packed)); + + static const TuneItem TuneItems[] PROGMEM = { + { (uint8_t)Register::Selector_sg_thrs_R, 1, 4 }, + { (uint8_t)Register::Idler_sg_thrs_R, 2, 10 }, + }; + + static_assert(COUNT(TuneItems) == 2); + + typedef struct { + // Variables used when editing values. + const char* editLabel; + uint8_t editValueBits; // 8 or 16 + void* editValuePtr; + int16_t currentValue; + int16_t minEditValue; + int16_t maxEditValue; + int16_t minJumpValue; + } menu_data_edit_t; + + struct _menu_tune_data_t { + menu_data_edit_t reserved; // 13 bytes reserved for number editing functions + int8_t status; // 1 byte + uint8_t currentValue; // 1 byte + TuneItem item; // 3 bytes + }; + + //static_assert(sizeof(_menu_tune_data_t) == 18); + //static_assert(sizeof(menu_data)>= sizeof(_menu_tune_data_t), "_menu_tune_data_t doesn't fit into menu_data"); + + void tuneIdlerStallguardThresholdMenu() { + // const uint8_t menu_data[32] = "Set Stallguard Threshold"; + // //static constexpr _menu_tune_data_t * const _md = (_menu_tune_data_t*)&(menu_data[0]); + // static constexpr _menu_tune_data_t * const _md = (_menu_tune_data_t*)&(menu_data[0]); + + // // Do not timeout the screen, otherwise there will be FW crash (menu recursion) + // //lcd_timeoutToStatus.stop(); + //if (_md->status == 0) { + // _md->status = 1; // Menu entered for the first time + + // // Fetch the TuneItem from PROGMEM + // const uint8_t offset = (mmu3.mmuCurrentErrorCode() == ErrorCode::HOMING_IDLER_FAILED) ? 1 : 0; + // memcpy_P(&(_md->item), &TuneItems[offset], sizeof(TuneItem)); + + // // Fetch the value which is currently in MMU EEPROM + // mmu3.readRegister(_md->item.address); + // _md->currentValue = mmu3.getLastReadRegisterValue(); + //} + + // //MENU_BEGIN(); + // //ON_MENU_LEAVE( + // // mmu3.writeRegister(_md->item.address, (uint16_t)_md->currentValue); + // // putErrorScreenToSleep = false; + // // lcd_return_to_status(); + // // return; + // //); + // //MENU_ITEM_BACK(MSG_DONE); + // //MENU_ITEM_EDIT_int3_P( + // // _i("Sensitivity"), ////MSG_MMU_SENSITIVITY c=18 + // // &_md->currentValue, + // // _md->item.minValue, + // // _md->item.maxValue + // //); + // //MENU_END(); + + //START_MENU(); + //BACK_ITEM(MSG_BACK); + //EDIT_ITEM( + // int8, + // MSG_MMU_SENSITIVITY, + // &_md->currentValue, + // _md->item.minValue, + // _md->item.maxValue, + // []{ + // write_register_and_return_to_status_menu(_md->item.address, _md->currentValue); + // } + // ); + //END_MENU(); + } + + void write_register_and_return_to_status_menu(uint8_t address, uint8_t value) { + mmu3.writeRegister(address, (uint16_t)value); + putErrorScreenToSleep = false; + ui.return_to_status(); + } + + void tuneIdlerStallguardThreshold() { + putErrorScreenToSleep = true; + //menu_submenu(tuneIdlerStallguardThresholdMenu); + } + +} // MMU3 + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_reporting.h b/Marlin/src/feature/mmu3/mmu3_reporting.h new file mode 100644 index 0000000000..d3e8db9c5e --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_reporting.h @@ -0,0 +1,168 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_reporting.h + */ + +#include "../../MarlinCore.h" + +#include +#include "mmu_hw/error_codes.h" +#include "mmu_hw/progress_codes.h" + +namespace MMU3 { + + enum CommandInProgress : uint8_t { + NoCommand = 0, + CutFilament = 'K', + EjectFilament = 'E', + Homing = 'H', + LoadFilament = 'L', + Reset = 'X', + ToolChange = 'T', + UnloadFilament = 'U', + }; + + /** + * Data class for MMU operation statistics. + * + * This is used to load/save data from/to EEPROM. + * The data is initialized by the settings.load() method. + */ + class OperationStatistics { + public: + void increment_load_fails(); + void increment_mmu_fails(); + void increment_tool_change_counter(); + bool reset_per_print_stats(); // Reset only the per print stats. + bool reset_fail_stats(); // Reset only fail stats and keep tool change counters + bool reset_stats(); // Reset MMU stats and update EEPROM + + static uint16_t fail_total_num; // total failures + static uint8_t fail_num; // fails during print + static uint16_t load_fail_total_num; // total load failures + static uint8_t load_fail_num; // load failures during print + static uint16_t tool_change_counter; // number of tool changes during print + static uint32_t tool_change_total_counter; // number of total tool changes + static int fail_total_num_addr; // total failures EEPROM addr + static int fail_num_addr; // fails during print EEPROM addr + static int load_fail_total_num_addr; // total load failures EEPROM addr + static int load_fail_num_addr; // load failures during print EEPROM addr + static int tool_change_counter_addr; // number of tool changes EEPROM addr + static int tool_change_total_counter_addr; // number of total tool changes EEPROM addr + }; + + extern OperationStatistics operation_statistics; + + // Called at the begin of every MMU operation + void BeginReport(CommandInProgress cip, ProgressCode ec); + + // Called at the end of every MMU operation + void EndReport(CommandInProgress cip, ProgressCode ec); + + // Checks for error screen user input, if the error screen is open + void CheckErrorScreenUserInput(); + + // Return true if the error screen is sleeping in the background + // Error screen sleeps when the firmware is rendering complementary + // UI to resolve the error screen, for example tuning Idler Stallguard Threshold + bool TuneMenuEntered(); + + // @brief Called when the MMU or MK3S sends operation error (even repeatedly). + // Render MMU error screen on the LCD. This must be non-blocking + // and allow the MMU and printer to communicate with each other. + // @param[in] ec error code + // @param[in] es error source + void ReportErrorHook(CommandInProgress cip, ErrorCode ec, uint8_t es); + + // Called when the MMU sends operation progress update + void ReportProgressHook(CommandInProgress cip, ProgressCode ec); + + struct TryLoadUnloadReporter { + TryLoadUnloadReporter(float delta_mm); + ~TryLoadUnloadReporter(); + void Progress(bool sensorState); + void DumpToSerial(); + + private: + // @brief Add one block to the progress bar + // @param col pixel position on the LCD status line, should range from 0 to (LCD_WIDTH - 1) + // @param sensorState if true, filament is not present, else filament is present. This controls which character to render + void Render(uint8_t col, bool sensorState); + + uint8_t dpixel0, dpixel1; + uint8_t lcd_cursor_col; + // The total length is twice delta_mm. Divide that length by number of pixels + // available to get length per pixel. + // Note: Below is the reciprocal of (2 * delta_mm) / LCD_WIDTH [mm/pixel] + float pixel_per_mm; + }; + + // Remders the sensor status line. Also used by the "resume temperature" screen. + void ReportErrorHookDynamicRender(); + + // Renders the static part of the sensor state line. Also used by "resuming temperature screen" + void ReportErrorHookSensorLineRender(); + + // @return true if the MMU is communicating and available. Can change at runtime. + //bool MMUAvailable(); + + // Global Enable/Disable use MMU (to be stored in EEPROM) + //bool UseMMU(); + + // Disable MMU in EEPROM + //void DisableMMUInSettings(); + + // Increments EEPROM cell - number of failed loads into the nozzle + // Note: technically, this is not an MMU error but an error of the printer. + void IncrementLoadFails(); + + // Increments EEPROM cell - number of MMU errors + void IncrementMMUFails(); + + // @return true when Cutter is enabled in the menus + bool cutter_enabled(); + + // Beware: enum values intentionally chosen to match the 8bit FW to save code size + enum SoundType { + Prompt = 2, + Confirm = 3 + }; + + void MakeSound(SoundType s); + + void fullScreenMsgCut(uint8_t slot); + void fullScreenMsgEject(uint8_t slot); + void fullScreenMsgTest(uint8_t slot); + void fullScreenMsgLoad(uint8_t slot); + void fullScreenMsgRestoringTemperature(); + + void ScreenUpdateEnable(); + void ScreenClear(); + + void tuneIdlerStallguardThreshold(); + + void write_register_and_return_to_status_menu(uint8_t address, uint8_t value); + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_state.h b/Marlin/src/feature/mmu3/mmu3_state.h new file mode 100644 index 0000000000..036d3ae255 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_state.h @@ -0,0 +1,48 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_state.h + */ + +#include + +namespace MMU3 { + /** + * @brief status of mmu + * + * States of a printer with the MMU: + * - Active + * - Connecting + * - Stopped + * + * When the printer's FW starts, the MMU mode is either Stopped or NotResponding (based on user's preference). + * When the MMU successfully establishes communication, the state changes to Active. + */ + enum class xState : uint_fast8_t { + Active, //!< MMU has been detected, connected, communicates and is ready to be worked with. + Connecting, //!< MMU is connected but it doesn't communicate (yet). The user wants the MMU, but it is not ready to be worked with. + Stopped //!< The user doesn't want the printer to work with the MMU. The MMU itself is not powered and does not work at all. + }; + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu3_supported_version.h b/Marlin/src/feature/mmu3/mmu3_supported_version.h new file mode 100644 index 0000000000..fe21c79d5a --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu3_supported_version.h @@ -0,0 +1,32 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * mmu2_supported_version.h + */ + +#include + +#define mmuVersionMajor 3 +#define mmuVersionMinor 0 +#define mmuVersionPatch 2 diff --git a/Marlin/src/feature/mmu3/mmu_hw/buttons.h b/Marlin/src/feature/mmu3/mmu_hw/buttons.h new file mode 100644 index 0000000000..2b35d6339c --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/buttons.h @@ -0,0 +1,73 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * buttons.h + */ + +#include + +// Helper macros to parse the operations from Btns() +#define BUTTON_OP_RIGHT(X) ((X & 0xF0) >> 4) +#define BUTTON_OP_MIDDLE(X) (X & 0x0F) + +namespace MMU3 { + +// Will be mapped onto dialog button responses in the FW +// Those responses have their unique+translated texts as well +enum class ButtonOperations : uint8_t { + NoOperation = 0, + Retry = 1, + Continue = 2, + ResetMMU = 3, + Unload = 4, + Load = 5, + Eject = 6, + Tune = 7, + StopPrint = 8, + DisableMMU = 9, + MoreInfo = 10, +}; + +// Button codes + extended actions performed on the printer's side +enum class Buttons : uint_least8_t { + Right = 0, + Middle, + Left, + + // Performed on the printer's side + ResetMMU, + Load, + Eject, + StopPrint, + DisableMMU, + TuneMMU, // Printer changes MMU register value + + NoButton = 0xFF // should be kept last +}; + +constexpr uint_least8_t buttons_to_uint8t(Buttons b) { + return static_cast(b); +} + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu_hw/check-pce.sh b/Marlin/src/feature/mmu3/mmu_hw/check-pce.sh new file mode 100755 index 0000000000..197bc6dcc5 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/check-pce.sh @@ -0,0 +1,55 @@ +#!/bin/bash + +# download Prusa Error Codes for MMU +#wget https://raw.githubusercontent.com/3d-gussner/Prusa-Error-Codes/master/04_MMU/error-codes.yaml --output-document=error-codes.yaml +wget https://raw.githubusercontent.com/prusa3d/Prusa-Error-Codes/master/04_MMU/error-codes.yaml --output-document=error-codes.yaml + +oifs="$IFS" ## save original IFS +IFS=$'\n' ## set IFS to break on newline +codes=($(cat error-codes.yaml |grep "code:" |cut -d '"' -f2)) +titles=($(cat error-codes.yaml |grep 'title:' |cut -d '"' -f2)) +texts=($(cat error-codes.yaml |grep "text:" |cut -d '"' -f2)) +actions=($(cat error-codes.yaml |grep "action:" |cut -d ':' -f2)) +ids=($(cat error-codes.yaml |grep "id:" |cut -d '"' -f2)) +IFS="$oifs" ## restore original IFS + +filename=errors_list.h + +clear +for ((i = 0; i < ${#codes[@]}; i++)) do + code=${codes[i]} + id=$(cat $filename |grep "${code#04*}" | cut -d "=" -f1 | cut -d "_" -f3- |cut -d " " -f1) + title=$(cat $filename |grep "${id}" |grep --max-count=1 "MSG_TITLE" |cut -d '"' -f2) + text=$(cat $filename |grep "${id}" |grep --max-count=1 "MSG_DESC" |cut -d '"' -f2) + action1=$(cat $filename |grep "),//$id"| cut -d "," -f1) + action2=$(cat $filename |grep "),//$id"| cut -d "," -f2) + action1=$(echo $action1 | cut -d ":" -f2- |cut -d ":" -f2) + action2=$(echo $action2 | cut -d ":" -f2- |cut -d ":" -f2 |cut -d ")" -f1) + if [ "$action2" == "NoOperation" ]; then + action=" [$action1]" + else + action=" [$action1,$action2]" + fi + echo -n "code: $code |" + if [ "$id" != "${ids[i]}" ]; then + echo -n "$(tput setaf 1) $id $(tput sgr0) # $(tput setaf 2)${ids[i]}$(tput sgr0)|" + else + echo -n " $id |" + fi + if [ "$title" != "${titles[i]}" ]; then + echo -n "$(tput setaf 1) $title $(tput sgr0) # $(tput setaf 2)${titles[i]}$(tput sgr0)|" + else + echo -n " $title |" + fi + if [ "$text" != "${texts[i]}" ]; then + echo -n "$(tput setaf 1) $text $(tput sgr0) # $(tput setaf 2)${texts[i]}$(tput sgr0)|" + else + echo -n " $text |" + fi + if [ "$action" != "${actions[i]}" ]; then + echo -n "$(tput setaf 1) $action $(tput sgr0) # $(tput setaf 2)${actions[i]}$(tput sgr0)|" + else + echo -n " $action |" + fi + echo +done diff --git a/Marlin/src/feature/mmu3/mmu_hw/error_codes.h b/Marlin/src/feature/mmu3/mmu_hw/error_codes.h new file mode 100644 index 0000000000..b0bc36e206 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/error_codes.h @@ -0,0 +1,151 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * error_codes.h + */ + +#include + +/** + * A complete set of error codes which may be a result of a high-level command/operation. + * This header file should be included in the printer's firmware as well as a reference, + * therefore the error codes have been extracted to one place. + * + * Please note the errors are intentionally coded as "negative" values (highest bit set), + * because they are a complement to reporting the state of the high-level state machines - + * positive values are considered as normal progress, negative values are errors. + * + * Please note, that multiple TMC errors can occur at once, thus they are defined as a bitmask of the higher byte. + * Also, as there are 3 TMC drivers on the board, each error is added a bit for the corresponding TMC - + * TMC_PULLEY_BIT, TMC_SELECTOR_BIT, TMC_IDLER_BIT, + * The resulting error is a bitwise OR over 3 TMC drivers and their status, which should cover most of the situations correctly. + */ +enum class ErrorCode : uint_fast16_t { + RUNNING = 0x0000, //!< the operation is still running - keep this value as ZERO as it is used for initialization of error codes as well + OK = 0x0001, //!< the operation finished OK + + // TMC bit masks + TMC_PULLEY_BIT = 0x0040, //!< +64 TMC Pulley bit + TMC_SELECTOR_BIT = 0x0080, //!< +128 TMC Pulley bit + TMC_IDLER_BIT = 0x0100, //!< +256 TMC Pulley bit + + // Unload Filament related error codes + FINDA_DIDNT_SWITCH_ON = 0x8001, //!< E32769 FINDA didn't switch on while loading filament - either there is something blocking the metal ball or a cable is broken/disconnected + FINDA_DIDNT_SWITCH_OFF = 0x8002, //!< E32770 FINDA didn't switch off while unloading filament + + FSENSOR_DIDNT_SWITCH_ON = 0x8003, //!< E32771 Filament sensor didn't switch on while performing LoadFilament + FSENSOR_DIDNT_SWITCH_OFF = 0x8004, //!< E32772 Filament sensor didn't switch off while performing UnloadFilament + + FILAMENT_ALREADY_LOADED = 0x8005, //!< E32773 cannot perform operation LoadFilament or move the selector as the filament is already loaded + + INVALID_TOOL = 0x8006, //!< E32774 tool/slot index out of range (typically issuing T5 into an MMU with just 5 slots - valid range 0-4) + + HOMING_FAILED = 0x8007, //!< generic homing failed error - always reported with the corresponding axis bit set (Idler or Selector) as follows: + HOMING_SELECTOR_FAILED = HOMING_FAILED | TMC_SELECTOR_BIT, //!< E32903 the Selector was unable to home properly - that means something is blocking its movement + HOMING_IDLER_FAILED = HOMING_FAILED | TMC_IDLER_BIT, //!< E33031 the Idler was unable to home properly - that means something is blocking its movement + STALLED_PULLEY = HOMING_FAILED | TMC_PULLEY_BIT, //!< E32839 for the Pulley "homing" means just StallGuard detected during Pulley's operation (Pulley doesn't home) + + FINDA_VS_EEPROM_DISREPANCY = 0x8008, //!< E32776 FINDA is pressed but we have no such record in EEPROM - this can only happen at the start of the MMU and can be resolved by issuing an Unload command + + FSENSOR_TOO_EARLY = 0x8009, //!< E32777 FSensor triggered while doing FastFeedToBondtech - that means either: + //!< - the PTFE is too short + //!< - a piece of filament was left inside - pushed in front of the loaded filament causing the fsensor trigger too early + //!< - fsensor is faulty producing bogus triggers + + FINDA_FLICKERS = 0x800A, //!< FINDA flickers - seems to be badly calibrated and happens to be pressed at spots where it used to be not pressed before. + //!< The user is obliged to inspect FINDA and tune its switching + + MOVE_FAILED = 0x800B, //!< generic move failed error - always reported with the corresponding axis bit set (Idler or Selector) as follows: + MOVE_SELECTOR_FAILED = MOVE_FAILED | TMC_SELECTOR_BIT, //!< E32905 the Selector was unable to move to desired position properly - that means something is blocking its movement, e.g. a piece of filament got out of pulley body + MOVE_IDLER_FAILED = MOVE_FAILED | TMC_IDLER_BIT, //!< E33033 the Idler was unable to move - unused at the time of creation, but added for completeness + MOVE_PULLEY_FAILED = MOVE_FAILED | TMC_PULLEY_BIT, //!< E32841 the Pulley was unable to move - unused at the time of creation, but added for completeness + + FILAMENT_EJECTED = 0x800C, //!< Filament was ejected, waiting for user input - technically, this is not an error + + MCU_UNDERVOLTAGE_VCC = 0x800D, //!< MCU VCC rail undervoltage. + + FILAMENT_CHANGE = 0x8029, //!< E32809 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle + LOAD_TO_EXTRUDER_FAILED = 0x802A, //!< E32810 internal error of the printer - try-load-unload sequence detected missing filament -> failed load into the nozzle + QUEUE_FULL = 0x802B, //!< E32811 internal logic error - attempt to move with a full queue + VERSION_MISMATCH = 0x802C, //!< E32812 internal error of the printer - incompatible version of the MMU FW + PROTOCOL_ERROR = 0x802D, //!< E32813 internal error of the printer - communication with the MMU got garbled - protocol decoder couldn't decode the incoming messages + MMU_NOT_RESPONDING = 0x802E, //!< E32814 internal error of the printer - communication with the MMU is not working + INTERNAL = 0x802F, //!< E32815 internal runtime error (software) + + // TMC driver init error - TMC dead or bad communication + // - E33344 Pulley TMC driver + // - E33408 Selector TMC driver + // - E33536 Idler TMC driver + // - E33728 All 3 TMC driver + TMC_IOIN_MISMATCH = 0x8200, + + // TMC driver reset - recoverable, we just need to rehome the axis + // Idler: can be rehomed any time + // Selector: if there is a filament, remove it and rehome, if there is no filament, just rehome + // Pulley: do nothing - for the loading sequence - just restart and move slowly, for the unload sequence just restart + // - E33856 Pulley TMC driver + // - E33920 Selector TMC driver + // - E34048 Idler TMC driver + // - E34240 All 3 TMC driver + TMC_RESET = 0x8400, + + // not enough current for the TMC, NOT RECOVERABLE + // - E34880 Pulley TMC driver + // - E34944 Selector TMC driver + // - E35072 Idler TMC driver + // - E35264 All 3 TMC driver + TMC_UNDERVOLTAGE_ON_CHARGE_PUMP = 0x8800, + + // TMC driver serious error - short to ground on coil A or coil B - dangerous to recover + // - E36928 Pulley TMC driver + // - E36992 Selector TMC driver + // - E37120 Idler TMC driver + // - E37312 All 3 TMC driver + TMC_SHORT_TO_GROUND = 0x9000, + + // TMC driver over temperature warning - can be recovered by restarting the driver. + // If this error happens, we should probably go into the error state as soon as the current command is finished. + // The driver technically still works at this point. + // - E41024 Pulley TMC driver + // - E41088 Selector TMC driver + // - E41216 Idler TMC driver + // - E41408 All 3 TMC driver + TMC_OVER_TEMPERATURE_WARN = 0xA000, + + // TMC driver over temperature error - we really shouldn't ever reach this error. + // It can still be recovered if the driver cools down below 120C. + // The driver needs to be disabled and enabled again for operation to resume after this error is cleared. + // - E49216 Pulley TMC driver + // - E49280 Selector TMC driver + // - E49408 Idler TMC driver + // - E49600 All 3 TMC driver + TMC_OVER_TEMPERATURE_ERROR = 0xC000, + + // TMC driver - IO pins are unreliable. While in theory it's recoverable, in practice it most likely + // means your hardware is borked (we can't command the drivers reliably via STEP/EN/DIR due to electrical + // issues or hardware fault. Possible "fixable" cause is undervoltage on the 5v logic line. + // Unfixable possible cause: bad or cracked solder joints on the PCB, failed shift register, failed driver. + MMU_SOLDERING_NEEDS_ATTENTION = 0xC200, + +}; diff --git a/Marlin/src/feature/mmu3/mmu_hw/errors_list.h b/Marlin/src/feature/mmu3/mmu_hw/errors_list.h new file mode 100644 index 0000000000..7fb93b0771 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/errors_list.h @@ -0,0 +1,352 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * errors_list.h + */ + +/** + * Extracted from Prusa-Error-Codes repo + * Subject to automation and optimization + * BEWARE - This file should only be included by mmu2_error_converter.cpp! + */ +#include "inttypes.h" +#include "../../../core/language.h" +#include "../../../lcd/marlinui.h" +#ifdef __AVR__ + #include +#endif +#include "buttons.h" +#include "../strlen_cx.h" +#include "../ultralcd.h" + +namespace MMU3 { + +static constexpr uint8_t ERR_MMU_CODE = 4; + +typedef enum : uint16_t { + ERR_UNDEF = 0, + + ERR_MECHANICAL = 100, + ERR_MECHANICAL_FINDA_DIDNT_TRIGGER = 101, + ERR_MECHANICAL_FINDA_FILAMENT_STUCK = 102, + ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER = 103, + ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK = 104, + + ERR_MECHANICAL_PULLEY_CANNOT_MOVE = 105, + ERR_MECHANICAL_FSENSOR_TOO_EARLY = 106, + ERR_MECHANICAL_INSPECT_FINDA = 107, + ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED = 108, + ERR_MECHANICAL_SELECTOR_CANNOT_HOME = 115, + ERR_MECHANICAL_SELECTOR_CANNOT_MOVE = 116, + ERR_MECHANICAL_IDLER_CANNOT_HOME = 125, + ERR_MECHANICAL_IDLER_CANNOT_MOVE = 126, + + ERR_TEMPERATURE = 200, + ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT = 201, + ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT = 211, + ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT = 221, + + ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR = 202, + ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR = 212, + ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR = 222, + + + ERR_ELECTRICAL = 300, + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR = 301, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR = 311, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR = 321, + + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET = 302, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET = 312, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET = 322, + + ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR = 303, + ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR = 313, + ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR = 323, + + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED = 304, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED = 314, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED = 324, + + ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED = 305, + ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED = 315, + ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED = 325, + + ERR_ELECTRICAL_MMU_MCU_ERROR = 306, + + ERR_CONNECT = 400, + ERR_CONNECT_MMU_NOT_RESPONDING = 401, + ERR_CONNECT_COMMUNICATION_ERROR = 402, + + + ERR_SYSTEM = 500, + ERR_SYSTEM_FILAMENT_ALREADY_LOADED = 501, + ERR_SYSTEM_INVALID_TOOL = 502, + ERR_SYSTEM_QUEUE_FULL = 503, + ERR_SYSTEM_FW_UPDATE_NEEDED = 504, + ERR_SYSTEM_FW_RUNTIME_ERROR = 505, + ERR_SYSTEM_UNLOAD_MANUALLY = 506, + ERR_SYSTEM_FILAMENT_EJECTED = 507, + ERR_SYSTEM_FILAMENT_CHANGE = 508, + + ERR_OTHER_UNKNOWN_ERROR = 900 +} err_num_t; + +// Avr gcc has serious trouble understanding static data structures in PROGMEM +// and inadvertently falls back to copying the whole structure into RAM (which is obviously unwanted). +// But since this file ought to be generated in the future from yaml prescription, +// it really makes no difference if there are "nice" data structures or plain arrays. +static const constexpr err_num_t errorCodes[] PROGMEM = { + ERR_MECHANICAL_FINDA_DIDNT_TRIGGER, + ERR_MECHANICAL_FINDA_FILAMENT_STUCK, + ERR_MECHANICAL_FSENSOR_DIDNT_TRIGGER, + ERR_MECHANICAL_FSENSOR_FILAMENT_STUCK, + ERR_MECHANICAL_PULLEY_CANNOT_MOVE, + ERR_MECHANICAL_FSENSOR_TOO_EARLY, + ERR_MECHANICAL_INSPECT_FINDA, + ERR_MECHANICAL_LOAD_TO_EXTRUDER_FAILED, + ERR_MECHANICAL_SELECTOR_CANNOT_HOME, + ERR_MECHANICAL_SELECTOR_CANNOT_MOVE, + ERR_MECHANICAL_IDLER_CANNOT_HOME, + ERR_MECHANICAL_IDLER_CANNOT_MOVE, + ERR_TEMPERATURE_WARNING_TMC_PULLEY_TOO_HOT, + ERR_TEMPERATURE_WARNING_TMC_SELECTOR_TOO_HOT, + ERR_TEMPERATURE_WARNING_TMC_IDLER_TOO_HOT, + ERR_TEMPERATURE_TMC_PULLEY_OVERHEAT_ERROR, + ERR_TEMPERATURE_TMC_SELECTOR_OVERHEAT_ERROR, + ERR_TEMPERATURE_TMC_IDLER_OVERHEAT_ERROR, + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_ERROR, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_ERROR, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_ERROR, + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_RESET, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_RESET, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_RESET, + ERR_ELECTRICAL_TMC_PULLEY_UNDERVOLTAGE_ERROR, + ERR_ELECTRICAL_TMC_SELECTOR_UNDERVOLTAGE_ERROR, + ERR_ELECTRICAL_TMC_IDLER_UNDERVOLTAGE_ERROR, + ERR_ELECTRICAL_TMC_PULLEY_DRIVER_SHORTED, + ERR_ELECTRICAL_TMC_SELECTOR_DRIVER_SHORTED, + ERR_ELECTRICAL_TMC_IDLER_DRIVER_SHORTED, + ERR_ELECTRICAL_MMU_PULLEY_SELFTEST_FAILED, + ERR_ELECTRICAL_MMU_SELECTOR_SELFTEST_FAILED, + ERR_ELECTRICAL_MMU_IDLER_SELFTEST_FAILED, + ERR_ELECTRICAL_MMU_MCU_ERROR, + ERR_CONNECT_MMU_NOT_RESPONDING, + ERR_CONNECT_COMMUNICATION_ERROR, + ERR_SYSTEM_FILAMENT_ALREADY_LOADED, + ERR_SYSTEM_INVALID_TOOL, + ERR_SYSTEM_QUEUE_FULL, + ERR_SYSTEM_FW_UPDATE_NEEDED, + ERR_SYSTEM_FW_RUNTIME_ERROR, + ERR_SYSTEM_UNLOAD_MANUALLY, + ERR_SYSTEM_FILAMENT_EJECTED, + ERR_SYSTEM_FILAMENT_CHANGE, + ERR_OTHER_UNKNOWN_ERROR +}; + +FSTR_P const errorTitles[] PROGMEM = { + GET_TEXT_F(MSG_TITLE_FINDA_DIDNT_TRIGGER), + GET_TEXT_F(MSG_TITLE_FINDA_FILAMENT_STUCK), + GET_TEXT_F(MSG_TITLE_FSENSOR_DIDNT_TRIGGER), + GET_TEXT_F(MSG_TITLE_FSENSOR_FILAMENT_STUCK), + GET_TEXT_F(MSG_TITLE_PULLEY_CANNOT_MOVE), + GET_TEXT_F(MSG_TITLE_FSENSOR_TOO_EARLY), + GET_TEXT_F(MSG_TITLE_INSPECT_FINDA), + GET_TEXT_F(MSG_TITLE_LOAD_TO_EXTRUDER_FAILED), + GET_TEXT_F(MSG_TITLE_SELECTOR_CANNOT_HOME), + GET_TEXT_F(MSG_TITLE_SELECTOR_CANNOT_MOVE), + GET_TEXT_F(MSG_TITLE_IDLER_CANNOT_HOME), + GET_TEXT_F(MSG_TITLE_IDLER_CANNOT_MOVE), + GET_TEXT_F(MSG_TITLE_TMC_WARNING_TMC_TOO_HOT), + GET_TEXT_F(MSG_TITLE_TMC_WARNING_TMC_TOO_HOT), + GET_TEXT_F(MSG_TITLE_TMC_WARNING_TMC_TOO_HOT), + GET_TEXT_F(MSG_TITLE_TMC_OVERHEAT_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_OVERHEAT_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_OVERHEAT_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_RESET), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_RESET), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_RESET), + GET_TEXT_F(MSG_TITLE_TMC_UNDERVOLTAGE_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_UNDERVOLTAGE_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_UNDERVOLTAGE_ERROR), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_SHORTED), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_SHORTED), + GET_TEXT_F(MSG_TITLE_TMC_DRIVER_SHORTED), + GET_TEXT_F(MSG_TITLE_SELFTEST_FAILED), + GET_TEXT_F(MSG_TITLE_SELFTEST_FAILED), + GET_TEXT_F(MSG_TITLE_SELFTEST_FAILED), + GET_TEXT_F(MSG_TITLE_MMU_MCU_ERROR), + GET_TEXT_F(MSG_TITLE_MMU_NOT_RESPONDING), + GET_TEXT_F(MSG_TITLE_COMMUNICATION_ERROR), + GET_TEXT_F(MSG_TITLE_FILAMENT_ALREADY_LOADED), + GET_TEXT_F(MSG_TITLE_INVALID_TOOL), + GET_TEXT_F(MSG_TITLE_QUEUE_FULL), + GET_TEXT_F(MSG_TITLE_FW_UPDATE_NEEDED), + GET_TEXT_F(MSG_TITLE_FW_RUNTIME_ERROR), + GET_TEXT_F(MSG_TITLE_UNLOAD_MANUALLY), + GET_TEXT_F(MSG_TITLE_FILAMENT_EJECTED), + GET_TEXT_F(MSG_TITLE_FILAMENT_CHANGE), + GET_TEXT_F(MSG_TITLE_UNKNOWN_ERROR) +}; + +// @@TODO Looking at the texts, they can be composed of several parts and/or parameterized (could save a lot of space) ) +// Moreover, some of them have been disabled in favour of saving some more code size. + +FSTR_P const errorDescs[] PROGMEM = { + GET_TEXT_F(MSG_DESC_FINDA_DIDNT_TRIGGER), + GET_TEXT_F(MSG_DESC_FINDA_FILAMENT_STUCK), + GET_TEXT_F(MSG_DESC_FSENSOR_DIDNT_TRIGGER), + GET_TEXT_F(MSG_DESC_FSENSOR_FILAMENT_STUCK), + GET_TEXT_F(MSG_DESC_PULLEY_CANNOT_MOVE), + GET_TEXT_F(MSG_DESC_FSENSOR_TOO_EARLY), + GET_TEXT_F(MSG_DESC_INSPECT_FINDA), + GET_TEXT_F(MSG_DESC_LOAD_TO_EXTRUDER_FAILED), + GET_TEXT_F(MSG_DESC_SELECTOR_CANNOT_HOME), + GET_TEXT_F(MSG_DESC_CANNOT_MOVE), + GET_TEXT_F(MSG_DESC_IDLER_CANNOT_HOME), + GET_TEXT_F(MSG_DESC_CANNOT_MOVE), + GET_TEXT_F(MSG_DESC_TMC), // WARNING_TMC_PULLEY_TOO_HOT + GET_TEXT_F(MSG_DESC_TMC), // WARNING_TMC_SELECTOR_TOO_HOT + GET_TEXT_F(MSG_DESC_TMC), // WARNING_TMC_IDLER_TOO_HOT + GET_TEXT_F(MSG_DESC_TMC), // TMC_PULLEY_OVERHEAT_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_SELECTOR_OVERHEAT_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_IDLER_OVERHEAT_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_PULLEY_DRIVER_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_SELECTOR_DRIVER_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_IDLER_DRIVER_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_PULLEY_DRIVER_RESET + GET_TEXT_F(MSG_DESC_TMC), // TMC_SELECTOR_DRIVER_RESET + GET_TEXT_F(MSG_DESC_TMC), // TMC_IDLER_DRIVER_RESET + GET_TEXT_F(MSG_DESC_TMC), // TMC_PULLEY_UNDERVOLTAGE_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_SELECTOR_UNDERVOLTAGE_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_IDLER_UNDERVOLTAGE_ERROR + GET_TEXT_F(MSG_DESC_TMC), // TMC_PULLEY_DRIVER_SHORTED + GET_TEXT_F(MSG_DESC_TMC), // TMC_SELECTOR_DRIVER_SHORTED + GET_TEXT_F(MSG_DESC_TMC), // TMC_IDLER_DRIVER_SHORTED + GET_TEXT_F(MSG_DESC_TMC), // MMU_PULLEY_SELFTEST_FAILED + GET_TEXT_F(MSG_DESC_TMC), // MMU_SELECTOR_SELFTEST_FAILED + GET_TEXT_F(MSG_DESC_TMC), // MMU_IDLER_SELFTEST_FAILED + GET_TEXT_F(MSG_DESC_TMC), // MSG_DESC_MMU_MCU_ERROR + GET_TEXT_F(MSG_DESC_MMU_NOT_RESPONDING), + GET_TEXT_F(MSG_DESC_COMMUNICATION_ERROR), + GET_TEXT_F(MSG_DESC_FILAMENT_ALREADY_LOADED), + GET_TEXT_F(MSG_DESC_INVALID_TOOL), + GET_TEXT_F(MSG_DESC_QUEUE_FULL), + GET_TEXT_F(MSG_DESC_FW_UPDATE_NEEDED), + GET_TEXT_F(MSG_DESC_FW_RUNTIME_ERROR), + GET_TEXT_F(MSG_DESC_UNLOAD_MANUALLY), + GET_TEXT_F(MSG_DESC_FILAMENT_EJECTED), + GET_TEXT_F(MSG_DESC_FILAMENT_CHANGE), + GET_TEXT_F(MSG_DESC_UNKNOWN_ERROR) +}; + +// We have max 3 buttons/operations to select from. +// One of them is "More" to show the explanation text normally hidden in the next screens. +// It is displayed with W (Double down arrow, special character from CGRAM) +// 01234567890123456789 +// >bttxt >bttxt >W +// Therefore at least some of the buttons, which can occur on the screen together, can only be 8-chars long max @@TODO. +// Beware - we only have space for 2 buttons on the LCD while the MMU has 3 buttons +// -> the left button on the MMU is not used/rendered on the LCD (it is also almost unused on the MMU side) + +// Used to parse the buttons from Btns(). +FSTR_P const btnOperation[] PROGMEM = { + GET_TEXT_F(MSG_BTN_RETRY), + GET_TEXT_F(MSG_DONE), + GET_TEXT_F(MSG_BTN_RESET_MMU), + GET_TEXT_F(MSG_BTN_UNLOAD), + GET_TEXT_F(MSG_BTN_LOAD), + GET_TEXT_F(MSG_BTN_EJECT), + GET_TEXT_F(MSG_TUNE), + GET_TEXT_F(MSG_BTN_STOP), + GET_TEXT_F(MSG_BTN_DISABLE_MMU) +}; + +// We have 8 different operations/buttons at this time, so we need at least 4 bits to encode each. +// Since one of the buttons is always "More", we can skip that one. +// Therefore we need just 1 byte to describe the necessary buttons for each screen. +uint8_t constexpr Btns(ButtonOperations bMiddle, ButtonOperations bRight) { + return ((uint8_t)bRight) << 4 | ((uint8_t)bMiddle); +} + +static const uint8_t errorButtons[] PROGMEM = { + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // FINDA_DIDNT_TRIGGER + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // FINDA_FILAMENT_STUCK + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // FSENSOR_DIDNT_TRIGGER + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // FSENSOR_FILAMENT_STUCK + + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // PULLEY_CANNOT_MOVE + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // FSENSOR_TOO_EARLY + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // INSPECT_FINDA + Btns(ButtonOperations::Continue, ButtonOperations::NoOperation), // LOAD_TO_EXTRUDER_FAILED + Btns(ButtonOperations::Retry, ButtonOperations::Tune), // SELECTOR_CANNOT_HOME + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // SELECTOR_CANNOT_MOVE + Btns(ButtonOperations::Retry, ButtonOperations::Tune), // IDLER_CANNOT_HOME + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // IDLER_CANNOT_MOVE + + Btns(ButtonOperations::Continue, ButtonOperations::ResetMMU), // WARNING_TMC_PULLEY_TOO_HOT + Btns(ButtonOperations::Continue, ButtonOperations::ResetMMU), // WARNING_TMC_SELECTOR_TOO_HOT + Btns(ButtonOperations::Continue, ButtonOperations::ResetMMU), // WARNING_TMC_IDLER_TOO_HOT + + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_PULLEY_OVERHEAT_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_SELECTOR_OVERHEAT_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_IDLER_OVERHEAT_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_PULLEY_DRIVER_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_SELECTOR_DRIVER_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_IDLER_DRIVER_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_PULLEY_DRIVER_RESET + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_SELECTOR_DRIVER_RESET + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_IDLER_DRIVER_RESET + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_PULLEY_UNDERVOLTAGE_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_SELECTOR_UNDERVOLTAGE_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_IDLER_UNDERVOLTAGE_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_PULLEY_DRIVER_SHORTED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_SELECTOR_DRIVER_SHORTED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // TMC_IDLER_DRIVER_SHORTED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // MMU_PULLEY_SELFTEST_FAILED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // MMU_SELECTOR_SELFTEST_FAILED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // MMU_IDLER_SELFTEST_FAILED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // MMU_MCU_ERROR + Btns(ButtonOperations::ResetMMU, ButtonOperations::DisableMMU), // MMU_NOT_RESPONDING + Btns(ButtonOperations::ResetMMU, ButtonOperations::DisableMMU), // COMMUNICATION_ERROR + + Btns(ButtonOperations::Unload, ButtonOperations::Continue), // FILAMENT_ALREADY_LOADED + Btns(ButtonOperations::StopPrint, ButtonOperations::ResetMMU), // INVALID_TOOL + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // QUEUE_FULL + Btns(ButtonOperations::ResetMMU, ButtonOperations::DisableMMU), // FW_UPDATE_NEEDED + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // FW_RUNTIME_ERROR + Btns(ButtonOperations::Retry, ButtonOperations::NoOperation), // UNLOAD_MANUALLY + Btns(ButtonOperations::Continue, ButtonOperations::NoOperation), // FILAMENT_EJECTED + Btns(ButtonOperations::Eject, ButtonOperations::Load), // FILAMENT_CHANGE + Btns(ButtonOperations::ResetMMU, ButtonOperations::NoOperation), // UNKOWN_ERROR +}; + +static_assert(COUNT(errorCodes) == COUNT(errorDescs)); +static_assert(COUNT(errorCodes) == COUNT(errorTitles)); +static_assert(COUNT(errorCodes) == COUNT(errorButtons)); + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/mmu_hw/progress_codes.h b/Marlin/src/feature/mmu3/mmu_hw/progress_codes.h new file mode 100644 index 0000000000..16e63a8564 --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/progress_codes.h @@ -0,0 +1,72 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * progress_codes.h + */ + +#include + +/** + * A complete set of progress codes which may be reported while running a high-level command/operation. + * This header file should be included in the printer's firmware as well as a reference, so the progress + * codes are extracted to one place. + */ +enum class ProgressCode : uint_fast8_t { + OK = 0, //!< finished ok + + EngagingIdler, // P1 + DisengagingIdler, // P2 + UnloadingToFinda, // P3 + UnloadingToPulley, // P4 + FeedingToFinda, // P5 + FeedingToExtruder, // P6 + FeedingToNozzle, // P7 + AvoidingGrind, // P8 + FinishingMoves, // P9 + + ERRDisengagingIdler, // P10 + ERREngagingIdler, // P11 + ERRWaitingForUser, // P12 + ERRInternal, // P13 + ERRHelpingFilament, // P14 + ERRTMCFailed, // P15 + + UnloadingFilament, // P16 + LoadingFilament, // P17 + SelectingFilamentSlot, // P18 + PreparingBlade, // P19 + PushingFilament, // P20 + PerformingCut, // P21 + ReturningSelector, // P22 + ParkingSelector, // P23 + EjectingFilament, // P24 + RetractingFromFinda, // P25 + + Homing, // P26 + MovingSelector, // P27 + + FeedingToFSensor, // P28 + + Empty = 0xFF // dummy empty state +}; diff --git a/Marlin/src/feature/mmu3/mmu_hw/registers.h b/Marlin/src/feature/mmu3/mmu_hw/registers.h new file mode 100644 index 0000000000..3e423c479d --- /dev/null +++ b/Marlin/src/feature/mmu3/mmu_hw/registers.h @@ -0,0 +1,70 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * registers.h + */ + +#include + +namespace MMU3 { + +// Register map for MMU +enum class Register : uint8_t { + Project_Major = 0x00, + Project_Minor = 0x01, + Project_Revision = 0x02, + Project_Build_Number = 0x03, + MMU_Errors = 0x04, + Current_Progress_Code = 0x05, + Current_Error_Code = 0x06, + Filament_State = 0x07, + FINDA_State = 0x08, + FSensor_State = 0x09, + Motor_Mode = 0x0A, + Extra_Load_Distance = 0x0B, + FSensor_Unload_Check_Distance = 0xC, + Pulley_Unload_Feedrate = 0x0D, + Pulley_Acceleration = 0x0E, + Selector_Acceleration = 0x0F, + Idler_Acceleration = 0x10, + Pulley_Load_Feedrate = 0x11, + Selector_Nominal_Feedrate = 0x12, + Idler_Nominal_Feedrate = 0x13, + Pulley_Slow_Feedrate = 0x14, + Selector_Homing_Feedrate = 0x15, + Idler_Homing_Feedrate = 0x16, + Pulley_sg_thrs_R = 0x17, + Selector_sg_thrs_R = 0x18, + Idler_sg_thrs_R = 0x19, + Get_Pulley_Position = 0x1A, + Set_Get_Selector_Slot = 0x1B, + Set_Get_Idler_Slot = 0x1C, + Set_Get_Selector_Cut_iRun = 0x1D, + Set_Get_Pulley_iRun = 0x1E, + Set_Get_Selector_iRun = 0x1F, + Set_Get_Idler_iRun = 0x20, + Reserved = 0x21, +}; + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/registers.h b/Marlin/src/feature/mmu3/registers.h new file mode 100644 index 0000000000..3e423c479d --- /dev/null +++ b/Marlin/src/feature/mmu3/registers.h @@ -0,0 +1,70 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * registers.h + */ + +#include + +namespace MMU3 { + +// Register map for MMU +enum class Register : uint8_t { + Project_Major = 0x00, + Project_Minor = 0x01, + Project_Revision = 0x02, + Project_Build_Number = 0x03, + MMU_Errors = 0x04, + Current_Progress_Code = 0x05, + Current_Error_Code = 0x06, + Filament_State = 0x07, + FINDA_State = 0x08, + FSensor_State = 0x09, + Motor_Mode = 0x0A, + Extra_Load_Distance = 0x0B, + FSensor_Unload_Check_Distance = 0xC, + Pulley_Unload_Feedrate = 0x0D, + Pulley_Acceleration = 0x0E, + Selector_Acceleration = 0x0F, + Idler_Acceleration = 0x10, + Pulley_Load_Feedrate = 0x11, + Selector_Nominal_Feedrate = 0x12, + Idler_Nominal_Feedrate = 0x13, + Pulley_Slow_Feedrate = 0x14, + Selector_Homing_Feedrate = 0x15, + Idler_Homing_Feedrate = 0x16, + Pulley_sg_thrs_R = 0x17, + Selector_sg_thrs_R = 0x18, + Idler_sg_thrs_R = 0x19, + Get_Pulley_Position = 0x1A, + Set_Get_Selector_Slot = 0x1B, + Set_Get_Idler_Slot = 0x1C, + Set_Get_Selector_Cut_iRun = 0x1D, + Set_Get_Pulley_iRun = 0x1E, + Set_Get_Selector_iRun = 0x1F, + Set_Get_Idler_iRun = 0x20, + Reserved = 0x21, +}; + +} // MMU3 diff --git a/Marlin/src/feature/mmu3/sound.cpp b/Marlin/src/feature/mmu3/sound.cpp new file mode 100644 index 0000000000..63edd2dd5e --- /dev/null +++ b/Marlin/src/feature/mmu3/sound.cpp @@ -0,0 +1,203 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * sound.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + + //#include "backlight.h" + #include "../../libs/buzzer.h" + #include "sound.h" + + // eSOUND_MODE eSoundMode=e_SOUND_MODE_LOUD; + // doesn't matter if Sound_Init is called (i.e. the value is in the EEPROM) + // !?! eSOUND_MODE eSoundMode; in ultraldc.cpp :: cd_settings_menu() it appears as a local variable like this + eSOUND_MODE eSoundMode; // =e_SOUND_MODE_DEFAULT; + + + static void Sound_SaveMode(void); + static void Sound_DoSound_Echo(void); + static void Sound_DoSound_Prompt(void); + static void Sound_DoSound_Alert(bool bOnce); + static void Sound_DoSound_Encoder_Move(void); + static void Sound_DoSound_Blind_Alert(void); + + void Sound_Init(void) { + // SET_OUTPUT(BEEPER); + // eSoundMode = static_cast(eeprom_init_default_byte((uint8_t*)EEPROM_SOUND_MODE, e_SOUND_MODE_DEFAULT)); + } + + void Sound_SaveMode(void) { + // eeprom_update_byte((uint8_t*)EEPROM_SOUND_MODE,(uint8_t)eSoundMode); + } + + void Sound_CycleState(void) { + switch (eSoundMode) { + case e_SOUND_MODE_LOUD: eSoundMode = e_SOUND_MODE_ONCE; break; + case e_SOUND_MODE_ONCE: eSoundMode = e_SOUND_MODE_SILENT; break; + case e_SOUND_MODE_SILENT: eSoundMode = e_SOUND_MODE_BLIND; break; + case e_SOUND_MODE_BLIND: eSoundMode = e_SOUND_MODE_LOUD; break; + default: eSoundMode = e_SOUND_MODE_LOUD; + } + Sound_SaveMode(); + } + + // if critical is true then silent and once mode is ignored + void __attribute__((noinline)) Sound_MakeCustom(uint16_t ms, uint16_t tone_, bool critical) { + if (critical || eSoundMode != e_SOUND_MODE_SILENT) + //if (!tone_) { + // WRITE(BEEPER, HIGH); + // _delay(ms); + // WRITE(BEEPER, LOW); + //} + //else { + // _tone(BEEPER, tone_); + // _delay(ms); + // _noTone(BEEPER); + //} + BUZZ(ms, tone_); + } + + void Sound_MakeSound(eSOUND_TYPE eSoundType) { + switch (eSoundMode) { + case e_SOUND_MODE_LOUD: + if (eSoundType == e_SOUND_TYPE_ButtonEcho) + Sound_DoSound_Echo(); + if (eSoundType == e_SOUND_TYPE_StandardPrompt) + Sound_DoSound_Prompt(); + if (eSoundType == e_SOUND_TYPE_StandardAlert) + Sound_DoSound_Alert(false); + break; + case e_SOUND_MODE_ONCE: + if (eSoundType == e_SOUND_TYPE_ButtonEcho) + Sound_DoSound_Echo(); + if (eSoundType == e_SOUND_TYPE_StandardPrompt) + Sound_DoSound_Prompt(); + if (eSoundType == e_SOUND_TYPE_StandardAlert) + Sound_DoSound_Alert(true); + break; + case e_SOUND_MODE_SILENT: + if (eSoundType == e_SOUND_TYPE_StandardAlert) + Sound_DoSound_Alert(true); + break; + case e_SOUND_MODE_BLIND: + if (eSoundType == e_SOUND_TYPE_ButtonEcho) + Sound_DoSound_Echo(); + if (eSoundType == e_SOUND_TYPE_StandardPrompt) + Sound_DoSound_Prompt(); + if (eSoundType == e_SOUND_TYPE_StandardAlert) + Sound_DoSound_Alert(false); + if (eSoundType == e_SOUND_TYPE_EncoderMove) + Sound_DoSound_Encoder_Move(); + if (eSoundType == e_SOUND_TYPE_BlindAlert) + Sound_DoSound_Blind_Alert(); + break; + default: + break; + } + } + + static void Sound_DoSound_Blind_Alert(void) { + // backlight_wake(1); + uint8_t nI; + for (nI = 0; nI < 20; nI++) { + BUZZ(94, 404); + BUZZ(94, 0); + } + } + + static void Sound_DoSound_Encoder_Move(void) { + uint8_t nI; + for (nI = 0; nI < 5; nI++) { + BUZZ(75, 404); + BUZZ(75, 0); + } + } + + static void Sound_DoSound_Echo(void) { + uint8_t nI; + for (nI = 0; nI < 10; nI++) { + BUZZ(100, 404); + BUZZ(100, 0); + } + } + + static void Sound_DoSound_Prompt(void) { + // backlight_wake(2); + BUZZ(500, 404); + } + + static void Sound_DoSound_Alert(bool bOnce) { + uint8_t nI, nMax; + nMax = bOnce ? 1 : 3; + for (nI = 0; nI < nMax; nI++) { + BUZZ(200, 404); + BUZZ(500, 0); + } + } + + static int16_t constexpr CONTINOUS_BEEP_PERIOD = 2000; // in ms + // static ShortTimer beep_timer; // Timer to keep track of continous beeping + static bool bFirst; // true if the first beep has occurred, e_SOUND_MODE_ONCE + + // @brief Handles sound when waiting for user input + // the function must be non-blocking. It is up to the caller + // to call this function repeatedly. + // Make sure to call sound_wait_for_user_reset() when the user has clicked the knob + // Loud - should continuously beep + // Silent - should be silent + // Once - should beep once + // Assist/Blind - as loud with beep and click on knob rotation and press + void sound_wait_for_user() { + #if BEEPER > 0 + if (eSoundMode == e_SOUND_MODE_SILENT) return; + + // Handle case where only one beep is needed + if (eSoundMode == e_SOUND_MODE_ONCE) { + if (bFirst) return; + Sound_MakeCustom(80, 0, false); + bFirst = true; + } + + // Handle case where there should be continous beeps + if (beep_timer.expired_cont(CONTINOUS_BEEP_PERIOD)) { + beep_timer.start(); + if (eSoundMode == e_SOUND_MODE_LOUD) + Sound_MakeCustom(80, 0, false); + else + // Assist (lower volume sound) + Sound_MakeSound(e_SOUND_TYPE_ButtonEcho); + } + #endif // BEEPER > 0 + } + + // @brief Resets the global state of sound_wait_for_user() + void sound_wait_for_user_reset() { + // beep_timer.stop(); + bFirst = false; + } + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/sound.h b/Marlin/src/feature/mmu3/sound.h new file mode 100644 index 0000000000..f72e6ecafe --- /dev/null +++ b/Marlin/src/feature/mmu3/sound.h @@ -0,0 +1,69 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * sound.h + */ + +#include + +#define e_SOUND_MODE_NULL 0xFF +typedef enum : uint8_t { + e_SOUND_MODE_LOUD, + e_SOUND_MODE_ONCE, + e_SOUND_MODE_SILENT, + e_SOUND_MODE_BLIND +} eSOUND_MODE; + +#define e_SOUND_MODE_DEFAULT e_SOUND_MODE_LOUD + +typedef enum : uint8_t { + e_SOUND_TYPE_ButtonEcho, + e_SOUND_TYPE_EncoderEcho, + e_SOUND_TYPE_StandardPrompt, + e_SOUND_TYPE_StandardConfirm, + e_SOUND_TYPE_StandardWarning, + e_SOUND_TYPE_StandardAlert, + e_SOUND_TYPE_EncoderMove, + e_SOUND_TYPE_BlindAlert +} eSOUND_TYPE; + +typedef enum : uint8_t { + e_SOUND_CLASS_Echo, + e_SOUND_CLASS_Prompt, + e_SOUND_CLASS_Confirm, + e_SOUND_CLASS_Warning, + e_SOUND_CLASS_Alert +} eSOUND_CLASS; + +extern eSOUND_MODE eSoundMode; + +extern void Sound_Init(void); +extern void Sound_CycleState(void); +extern void Sound_MakeSound(eSOUND_TYPE eSoundType); +extern void Sound_MakeCustom(uint16_t ms, uint16_t tone_, bool critical); +void sound_wait_for_user(); +void sound_wait_for_user_reset(); + +//static void Sound_DoSound_Echo(void); +//static void Sound_DoSound_Prompt(void); diff --git a/Marlin/src/feature/mmu3/strlen_cx.h b/Marlin/src/feature/mmu3/strlen_cx.h new file mode 100644 index 0000000000..6ac2a84b42 --- /dev/null +++ b/Marlin/src/feature/mmu3/strlen_cx.h @@ -0,0 +1,30 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * strlen_cx.h + */ + +constexpr inline int strlen_constexpr(const char *str) { + return *str ? 1 + strlen_constexpr(str + 1) : 0; +} diff --git a/Marlin/src/feature/mmu3/ultralcd.cpp b/Marlin/src/feature/mmu3/ultralcd.cpp new file mode 100644 index 0000000000..96e019740f --- /dev/null +++ b/Marlin/src/feature/mmu3/ultralcd.cpp @@ -0,0 +1,217 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +/** + * ultralcd.cpp + */ + +#include "../../inc/MarlinConfigPre.h" + +#if HAS_PRUSA_MMU3 + +#include "mmu3.h" +#include "mmu3_marlin_macros.h" +#include "mmu_hw/errors_list.h" +#include "ultralcd.h" + +#include "../../lcd/menu/menu_item.h" +#include "../../gcode/gcode.h" +#include "../../lcd/marlinui.h" + + //! @brief Show a two-choice prompt on the last line of the LCD + //! @param selected Show first choice as selected if true, the second otherwise + //! @param first_choice text caption of first possible choice + //! @param second_choice text caption of second possible choice + //! @param second_col column on LCD where second choice is rendered. + //! @param third_choice text caption of third, optional, choice. + void lcd_show_choices_prompt_P(uint8_t selected, const char *first_choice, const char *second_choice, uint8_t second_col, const char *third_choice) { + lcd_put_lchar(0, 3, selected == LCD_LEFT_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(first_choice); + lcd_put_lchar(second_col, 3, selected == LCD_MIDDLE_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(second_choice); + if (third_choice) { + lcd_put_lchar(18, 3, selected == LCD_RIGHT_BUTTON_CHOICE ? '>' : ' '); + lcd_put_u8str(third_choice); + } + } + + void lcd_space(uint8_t n) { + while (n--) lcd_put_lchar(' '); + } + + // Print extruder status (5 chars total) + // Scenario 1: "F?" + // There is no filament loaded and no tool change is in progress + // Scenario 2: "F[nr.]" + // [nr.] ranges from 1 to 5. + // Shows which filament is loaded. No tool change is in progress + // Scenario 3: "?>[nr.]" + // [nr.] ranges from 1 to 5. + // There is no filament currently loaded, but [nr.] is currently being loaded via tool change + // Scenario 4: "[nr.]>?" + // [nr.] ranges from 1 to 5. + // This scenario indicates a bug in the firmware if ? is on the right side + // Scenario 5: "[nr1.]>[nr2.]" + // [nr1.] ranges from 1 to 5. + // [nr2.] ranges from 1 to 5. + // Filament [nr1.] was loaded, but [nr2.] is currently being loaded via tool change + // Scenario 6: "?>?" + // This scenario should not be possible and indicates a bug in the firmware + uint8_t lcdui_print_extruder(void) { + uint8_t chars = 1; + lcd_space(1); + if (mmu3.get_current_tool() == mmu3.get_tool_change_tool()) { + lcd_put_lchar('F'); + lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); + chars += 2; + } + else { + lcd_put_lchar(mmu3.get_current_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_current_tool() + '1'); + lcd_put_lchar('>'); + lcd_put_lchar(mmu3.get_tool_change_tool() == (uint8_t)MMU3::FILAMENT_UNKNOWN ? '?' : mmu3.get_tool_change_tool() + '1'); + chars += 3; + } + return chars; + } + + bool pgm_is_whitespace(const char *c_addr) { + const char c = pgm_read_byte(c_addr); + return c == ' ' || c == '\t' || c == '\r' || c == '\n'; + } + + bool pgm_is_interpunction(const char *c_addr) { + const char c = pgm_read_byte(c_addr); + return c == '.' || c == ',' || c == ':' || c == ';' || c == '?' || c == '!' || c == '/'; + } + + /** + * @brief show full screen message + * + * This function is non-blocking + * @param msg message to be displayed from PROGMEM + * @return rest of the text (to be displayed on next page) + */ + static FSTR_P const lcd_display_message_fullscreen_nonBlocking(FSTR_P const fmsg) { + PGM_P msg = FTOP(fmsg); + PGM_P msgend = msg; + //bool multi_screen = false; + for (uint8_t row = 0; row < LCD_HEIGHT; ++row) { + if (pgm_read_byte(msgend) == 0) break; + SETCURSOR(0, row); + + // Previous row ended with a complete word, so the first character in the + // next row is a whitespace. We can skip the whitespace on a new line. + if (pgm_is_whitespace(msg) && ++msg == nullptr) break; // End of the message. + + uint8_t linelen = (strlen_P(msg) > LCD_WIDTH) ? LCD_WIDTH : strlen_P(msg); + PGM_P const msgend2 = msg + linelen; + msgend = msgend2; + if (row == 3 && linelen == LCD_WIDTH) { + // Last line of the display, full line should be displayed. + // Find out, whether this message will be split into multiple screens. + //multi_screen = pgm_read_byte(msgend) != 0; + // We do not need this... + //if (multi_screen) msgend = (msgend2 -= 2); + } + if (pgm_read_byte(msgend) != 0 && !pgm_is_whitespace(msgend) && !pgm_is_interpunction(msgend)) { + // Splitting a word. Find the start of the current word. + while (msgend > msg && !pgm_is_whitespace(msgend - 1)) --msgend; + if (msgend == msg) msgend = msgend2; // Found a single long word, which cannot be split. Just cut it. + } + for (; msg < msgend; ++msg) { + const char c = char(pgm_read_byte(msg)); + if (c == '\n') { + // Abort early if '\n' is encountered. + // This character is used to force the following words to be printed on the next line. + break; + } + lcd_put_lchar(c); + } + } + // We do not need this part... + //if (multi_screen) { + // // Display the double down arrow. + // lcd_put_lchar(LCD_WIDTH - 2, LCD_HEIGHT - 2, LCD_STR_ARROW_2_DOWN[0]); + //} + //return multi_screen ? msgend : nullptr; + return FPSTR(msgend); + } + + FSTR_P const lcd_display_message_fullscreen(FSTR_P const fmsg) { + // Disable update of the screen by the usual lcd_update(0) routine. + #if HAS_WIRED_LCD + //ui.lcdDrawUpdate = LCDViewAction::LCDVIEW_NONE; + ui.clear_lcd(); + return lcd_display_message_fullscreen_nonBlocking(fmsg); + #else + return fmsg + #endif + } + + /** + * @brief show full screen message and wait + * + * This function is blocking. + * @param msg message to be displayed from PROGMEM + */ + void lcd_show_fullscreen_message_and_wait(FSTR_P const fmsg) { + LcdUpdateDisabler lcdUpdateDisabler; + FSTR_P fmsg_next = lcd_display_message_fullscreen(fmsg); + const bool multi_screen = fmsg_next != nullptr; + ui.use_click(); + KEEPALIVE_STATE(PAUSED_FOR_USER); + // Until confirmed by a button click. + for (;;) { + if (fmsg_next == nullptr) { + // Display the confirm char. + //lcd_put_lchar(LCD_WIDTH - 2, LCD_HEIGHT - 2, LCD_STR_CONFIRM[0]); + } + // Wait for 5 seconds before displaying the next text. + for (uint8_t i = 0; i < 100; ++i) { + idle(true); + safe_delay(50); + if (ui.use_click()) { + if (fmsg_next == nullptr) { + KEEPALIVE_STATE(IN_HANDLER); + return ui.go_back(); + } + if (!multi_screen) break; + if (fmsg_next == nullptr) fmsg_next = fmsg; + fmsg_next = lcd_display_message_fullscreen(fmsg_next); + } + } + //if (multi_screen) { + // if (fmsg_next == nullptr) fmsg_next = fmsg; + // fmsg_next = lcd_display_message_fullscreen(fmsg_next); + //} + } + } + + void lcd_insert_char_into_status(uint8_t position, const char message) { + if (position >= LCD_WIDTH) return; + //int size = ui.status_message.length(); + char *str = ui.status_message.buffer(); + str[position] = message; + ui.refresh(LCDVIEW_REDRAW_NOW); // force redraw + } + +#endif // HAS_PRUSA_MMU3 diff --git a/Marlin/src/feature/mmu3/ultralcd.h b/Marlin/src/feature/mmu3/ultralcd.h new file mode 100644 index 0000000000..2f17b61d0a --- /dev/null +++ b/Marlin/src/feature/mmu3/ultralcd.h @@ -0,0 +1,72 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +/** + * ultralcd.h + */ + +#include "../../MarlinCore.h" +#include "../../lcd/marlinui.h" + +#define LCD_LEFT_BUTTON_CHOICE 0 +#define LCD_MIDDLE_BUTTON_CHOICE 1 +#define LCD_RIGHT_BUTTON_CHOICE 2 + +#define LCD_STR_ARROW_2_DOWN "\x88" +#define LCD_STR_CONFIRM "\x89" +#define LCD_STR_SOLID_BLOCK "\xFF" // from the default character set + +/** + * @brief Helper class to temporarily disable LCD updates + * + * When constructed (on stack), original state state of lcd_update_enabled is stored + * and LCD updates are disabled. + * When destroyed (gone out of scope), original state of LCD update is restored. + * It has zero overhead compared to storing bool saved = lcd_update_enabled + * and calling lcd_update_enable(false) and lcd_update_enable(saved). + */ +class LcdUpdateDisabler { + public: + LcdUpdateDisabler() : m_updateEnabled(ui.lcdDrawUpdate) { + TERN_(HAS_WIRED_LCD, ui.lcdDrawUpdate = LCDViewAction::LCDVIEW_NONE); + } + ~LcdUpdateDisabler() { + #if HAS_WIRED_LCD + ui.lcdDrawUpdate = m_updateEnabled; + ui.clear_lcd(); + ui.update(); + #endif + } + + private: + LCDViewAction m_updateEnabled; +}; + +bool pgm_is_whitespace(const char *c_addr); +bool pgm_is_interpunction(const char *c_addr); +FSTR_P const lcd_display_message_fullscreen(FSTR_P const pmsg); +void lcd_show_choices_prompt_P(uint8_t selected, const char *first_choice, const char *second_choice, uint8_t second_col, const char *third_choice=nullptr); +void lcd_show_fullscreen_message_and_wait(FSTR_P const fmsg); +uint8_t lcdui_print_extruder(void); +void lcd_space(uint8_t n); +void lcd_insert_char_into_status(uint8_t position, const char message); diff --git a/Marlin/src/feature/password/password.cpp b/Marlin/src/feature/password/password.cpp index 1d376cc586..d36b3edd09 100644 --- a/Marlin/src/feature/password/password.cpp +++ b/Marlin/src/feature/password/password.cpp @@ -36,7 +36,7 @@ uint32_t Password::value, Password::value_entry; // // Authenticate user with password. -// Called from Setup, after SD Prinitng Stops/Aborts, and M510 +// Called from Setup, after SD Printing Stops/Aborts, and M510 // void Password::lock_machine() { is_locked = true; diff --git a/Marlin/src/feature/password/password.h b/Marlin/src/feature/password/password.h index 208765b212..0f8bc28bd8 100644 --- a/Marlin/src/feature/password/password.h +++ b/Marlin/src/feature/password/password.h @@ -37,6 +37,8 @@ public: static void access_menu_password(); static void authentication_done(); static void media_gatekeeper(); + static void media_gatekeeper_sd(); + static void media_gatekeeper_usb(); private: static void authenticate_user(const screenFunc_t, const screenFunc_t); diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp index 74a4f236c0..e48a0f8ec4 100644 --- a/Marlin/src/feature/pause.cpp +++ b/Marlin/src/feature/pause.cpp @@ -62,6 +62,8 @@ #if ENABLED(EXTENSIBLE_UI) #include "../lcd/extui/ui_api.h" +#elif ENABLED(SOVOL_SV06_RTS) + #include "../lcd/sovol_rts/sovol_rts.h" #endif #include "../lcd/marlinui.h" @@ -150,6 +152,11 @@ static bool ensure_safe_temperature(const bool wait=true, const PauseMode mode=P ui.pause_show_message(PAUSE_MESSAGE_HEATING, mode); + #if ENABLED(SOVOL_SV06_RTS) + rts.gotoPage(ID_Cold_L, ID_Cold_D); + rts.updateTempE0(); + #endif + if (wait) return thermalManager.wait_for_hotend(active_extruder); // Allow interruption by Emergency Parser M108 @@ -181,10 +188,8 @@ static bool ensure_safe_temperature(const bool wait=true, const PauseMode mode=P * * Returns 'true' if load was completed, 'false' for abort */ -bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=0*/, const int8_t max_beep_count/*=0*/, - const bool show_lcd/*=false*/, const bool pause_for_user/*=false*/, - const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/ - DXC_ARGS +bool load_filament(const float slow_load_length/*=0*/, const float fast_load_length/*=0*/, const float purge_length/*=0*/, const int8_t max_beep_count/*=0*/, + const bool show_lcd/*=false*/, const bool pause_for_user/*=false*/, const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/ DXC_ARGS ) { DEBUG_SECTION(lf, "load_filament", true); DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " showlcd:", show_lcd, " pauseforuser:", pause_for_user, " pausemode:", mode DXC_SAY); @@ -212,14 +217,14 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load while (wait_for_user) { impatient_beep(max_beep_count); - #if ALL(FILAMENT_CHANGE_RESUME_ON_INSERT, FILAMENT_RUNOUT_SENSOR) + #if ALL(HAS_FILAMENT_SENSOR, FILAMENT_CHANGE_RESUME_ON_INSERT) #if MULTI_FILAMENT_SENSOR - #define _CASE_INSERTED(N) case N-1: if (READ(FIL_RUNOUT##N##_PIN) != FIL_RUNOUT##N##_STATE) wait_for_user = false; break; + #define _CASE_INSERTED(N) case N-1: if (!FILAMENT_IS_OUT(N)) wait_for_user = false; break; switch (active_extruder) { REPEAT_1(NUM_RUNOUT_SENSORS, _CASE_INSERTED) } #else - if (READ(FIL_RUNOUT_PIN) != FIL_RUNOUT_STATE) wait_for_user = false; + if (!FILAMENT_IS_OUT()) wait_for_user = false; #endif #endif idle_no_sleep(); @@ -277,6 +282,11 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load // "Wait for filament purge" if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE); + #if ENABLED(SOVOL_SV06_RTS) + rts.updateTempE0(); + rts.gotoPage(ID_Purge_L, ID_Purge_D); + #endif + // Extrude filament to get into hotend unscaled_e_move(purge_length, ADVANCED_PAUSE_PURGE_FEEDRATE); } @@ -292,6 +302,7 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load ui.pause_show_message(PAUSE_MESSAGE_OPTION); // MarlinUI and MKS UI also set PAUSE_RESPONSE_WAIT_FOR #else pause_menu_response = PAUSE_RESPONSE_WAIT_FOR; + TERN_(SOVOL_SV06_RTS, rts.gotoPage(ID_PurgeMore_L, ID_PurgeMore_D)); #endif while (pause_menu_response == PAUSE_RESPONSE_WAIT_FOR) idle_no_sleep(); } @@ -331,10 +342,10 @@ inline void disable_active_extruder() { * * Returns 'true' if unload was completed, 'false' for abort */ -bool unload_filament(const_float_t unload_length, const bool show_lcd/*=false*/, +bool unload_filament(const float unload_length, const bool show_lcd/*=false*/, const PauseMode mode/*=PAUSE_MODE_PAUSE_PRINT*/ #if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER) - , const_float_t mix_multiplier/*=1.0*/ + , const float mix_multiplier/*=1.0*/ #endif ) { DEBUG_SECTION(uf, "unload_filament", true); @@ -355,6 +366,11 @@ bool unload_filament(const_float_t unload_length, const bool show_lcd/*=false*/, if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_UNLOAD, mode); + #if ENABLED(SOVOL_SV06_RTS) + rts.updateTempE0(); + rts.gotoPage(ID_Unload_L, ID_Unload_D); + #endif + // Retract filament unscaled_e_move(-(FILAMENT_UNLOAD_PURGE_RETRACT) * mix_multiplier, (PAUSE_PARK_RETRACT_FEEDRATE) * mix_multiplier); @@ -400,7 +416,7 @@ bool unload_filament(const_float_t unload_length, const bool show_lcd/*=false*/, */ uint8_t did_pause_print = 0; -bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool show_lcd/*=false*/, const_float_t unload_length/*=0*/ DXC_ARGS) { +bool pause_print(const float retract, const xyz_pos_t &park_point, const bool show_lcd/*=false*/, const float unload_length/*=0*/ DXC_ARGS) { DEBUG_SECTION(pp, "pause_print", true); DEBUG_ECHOLNPGM("... park.x:", park_point.x, " y:", park_point.y, " z:", park_point.z, " unloadlen:", unload_length, " showlcd:", show_lcd DXC_SAY); @@ -421,7 +437,7 @@ bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool // Pause the print job and timer #if HAS_MEDIA - const bool was_sd_printing = IS_SD_PRINTING(); + const bool was_sd_printing = card.isStillPrinting(); if (was_sd_printing) { card.pauseSDPrint(); ++did_pause_print; // Indicate SD pause also @@ -503,6 +519,11 @@ void show_continue_prompt(const bool is_reload) { DEBUG_ECHOLNPGM("... is_reload:", is_reload); ui.pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING); + #if ENABLED(SOVOL_SV06_RTS) + rts.updateTempE0(); + rts.gotoPage(ID_Insert_L, ID_Insert_D); + rts.sendData(Beep, SoundAddr); + #endif SERIAL_ECHO_START(); SERIAL_ECHO(is_reload ? F(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : F(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n")); } @@ -544,6 +565,10 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep // re-heat the nozzle, re-show the continue prompt, restart idle timers, start over if (nozzle_timed_out) { ui.pause_show_message(PAUSE_MESSAGE_HEAT); + #if ENABLED(SOVOL_SV06_RTS) + rts.updateTempE0(); + rts.gotoPage(ID_HeatNozzle_L, ID_HeatNozzle_D); + #endif SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT)); TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_HEATER_TIMEOUT), GET_TEXT_F(MSG_REHEAT))); @@ -611,9 +636,27 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep * - Send host action for resume, if configured * - Resume the current SD print job, if any */ -void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_length/*=0*/, const_float_t purge_length/*=ADVANCED_PAUSE_PURGE_LENGTH*/, const int8_t max_beep_count/*=0*/, const celsius_t targetTemp/*=0*/ DXC_ARGS) { +void resume_print( + const float slow_load_length/*=0*/, + const float fast_load_length/*=0*/, + const float purge_length/*=ADVANCED_PAUSE_PURGE_LENGTH*/, + const int8_t max_beep_count/*=0*/, + const celsius_t targetTemp/*=0*/, + const bool show_lcd/*=true*/, + const bool pause_for_user/*=false*/ + DXC_ARGS +) { DEBUG_SECTION(rp, "resume_print", true); - DEBUG_ECHOLNPGM("... slowlen:", slow_load_length, " fastlen:", fast_load_length, " purgelen:", purge_length, " maxbeep:", max_beep_count, " targetTemp:", targetTemp DXC_SAY); + DEBUG_ECHOLNPGM( + "... slowlen:", slow_load_length + , " fastlen:", fast_load_length + , " purgelen:", purge_length + , " maxbeep:", max_beep_count + , " targetTemp:", targetTemp + , " show_lcd:", show_lcd + , " pause_for_user:", pause_for_user + DXC_SAY + ); /* SERIAL_ECHOLNPGM( @@ -627,7 +670,7 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ if (!did_pause_print) return; // Re-enable the heaters if they timed out - bool nozzle_timed_out = false; + bool nozzle_timed_out = pause_for_user; HOTEND_LOOP() { nozzle_timed_out |= thermalManager.heater_idle[e].timed_out; thermalManager.reset_hotend_idle_timer(e); @@ -637,7 +680,7 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ thermalManager.setTargetHotend(targetTemp, active_extruder); // Load the new filament - load_filament(slow_load_length, fast_load_length, purge_length, max_beep_count, true, nozzle_timed_out, PAUSE_MODE_SAME DXC_PASS); + load_filament(slow_load_length, fast_load_length, purge_length, max_beep_count, show_lcd, nozzle_timed_out, PAUSE_MODE_SAME DXC_PASS); if (targetTemp > 0) { thermalManager.setTargetHotend(targetTemp, active_extruder); @@ -691,6 +734,12 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ planner.set_e_position_mm((destination.e = current_position.e = resume_position.e)); ui.pause_show_message(PAUSE_MESSAGE_STATUS); + #if ENABLED(SOVOL_SV06_RTS) + if (pause_flag) + rts.gotoPage(ID_PrintResume_L, ID_PrintResume_D); + else + rts.refreshTime(); + #endif #ifdef ACTION_ON_RESUMED hostui.resumed(); diff --git a/Marlin/src/feature/pause.h b/Marlin/src/feature/pause.h index b86f86b633..4fbb108180 100644 --- a/Marlin/src/feature/pause.h +++ b/Marlin/src/feature/pause.h @@ -57,9 +57,7 @@ enum PauseMessage : char { }; #if M600_PURGE_MORE_RESUMABLE - /** - * Input methods can Purge More, Resume, or request input - */ + // Input methods can Purge More, Resume, or request input enum PauseMenuResponse : char { PAUSE_RESPONSE_WAIT_FOR, PAUSE_RESPONSE_EXTRUDE_MORE, @@ -93,10 +91,10 @@ extern uint8_t did_pause_print; // Pause the print. If unload_length is set, do a Filament Unload bool pause_print( - const_float_t retract, // (mm) Retraction length + const float retract, // (mm) Retraction length const xyz_pos_t &park_point, // Parking XY Position and Z Raise const bool show_lcd=false, // Set LCD status messages? - const_float_t unload_length=0 // (mm) Filament Change Unload Length - 0 to skip + const float unload_length=0 // (mm) Filament Change Unload Length - 0 to skip DXC_PARAMS // Dual-X-Carriage extruder index ); @@ -107,18 +105,20 @@ void wait_for_confirmation( ); void resume_print( - const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move - const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move - const_float_t extrude_length=ADVANCED_PAUSE_PURGE_LENGTH, // (mm) Purge length + const float slow_load_length=0, // (mm) Slow Load Length for finishing move + const float fast_load_length=0, // (mm) Fast Load Length for initial move + const float purge_length=ADVANCED_PAUSE_PURGE_LENGTH, // (mm) Purge length const int8_t max_beep_count=0, // Beep alert for attention - const celsius_t targetTemp=0 // (°C) A target temperature for the hotend + const celsius_t targetTemp=0, // (°C) A target temperature for the hotend + const bool show_lcd=true, // Set LCD status messages? + const bool pause_for_user=false // Pause for user before returning? DXC_PARAMS // Dual-X-Carriage extruder index ); bool load_filament( - const_float_t slow_load_length=0, // (mm) Slow Load Length for finishing move - const_float_t fast_load_length=0, // (mm) Fast Load Length for initial move - const_float_t extrude_length=0, // (mm) Purge length + const float slow_load_length=0, // (mm) Slow Load Length for finishing move + const float fast_load_length=0, // (mm) Fast Load Length for initial move + const float purge_length=0, // (mm) Purge length const int8_t max_beep_count=0, // Beep alert for attention const bool show_lcd=false, // Set LCD status messages? const bool pause_for_user=false, // Pause for user before returning? @@ -127,11 +127,11 @@ bool load_filament( ); bool unload_filament( - const_float_t unload_length, // (mm) Filament Unload Length - 0 to skip + const float unload_length, // (mm) Filament Unload Length - 0 to skip const bool show_lcd=false, // Set LCD status messages? const PauseMode mode=PAUSE_MODE_PAUSE_PRINT // Pause Mode to apply #if ALL(FILAMENT_UNLOAD_ALL_EXTRUDERS, MIXING_EXTRUDER) - , const_float_t mix_multiplier=1.0f // Extrusion multiplier (for a Mixing Extruder) + , const float mix_multiplier=1.0f // Extrusion multiplier (for a Mixing Extruder) #endif ); diff --git a/Marlin/src/feature/power.cpp b/Marlin/src/feature/power.cpp index 20eb63a6f1..a28d95d967 100644 --- a/Marlin/src/feature/power.cpp +++ b/Marlin/src/feature/power.cpp @@ -201,7 +201,7 @@ void Power::power_off() { /** * Check all conditions that would signal power needing to be on. * - * @returns bool if power is needed + * @return bool if power is needed */ bool Power::is_power_needed() { @@ -261,7 +261,7 @@ void Power::power_off() { nextPowerCheck = now + 2500UL; if (is_power_needed()) power_on(); - else if (!lastPowerOn || (POWER_TIMEOUT > 0 && ELAPSED(now, lastPowerOn + SEC_TO_MS(POWER_TIMEOUT)))) + else if (!lastPowerOn || (POWER_TIMEOUT > 0 && ELAPSED(now, lastPowerOn, SEC_TO_MS(POWER_TIMEOUT)))) power_off(); } } diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp index 1867aba6ed..385ca6b72d 100644 --- a/Marlin/src/feature/powerloss.cpp +++ b/Marlin/src/feature/powerloss.cpp @@ -28,17 +28,18 @@ #if ENABLED(POWER_LOSS_RECOVERY) +#include "../inc/MarlinConfig.h" + #include "powerloss.h" -#include "../core/macros.h" #if ENABLED(EXTENSIBLE_UI) #include "../lcd/extui/ui_api.h" #endif -bool PrintJobRecovery::enabled; // Initialized by settings.load() +bool PrintJobRecovery::enabled; // Initialized by settings.load #if HAS_PLR_BED_THRESHOLD - celsius_t PrintJobRecovery::bed_temp_threshold; // Initialized by settings.load() + celsius_t PrintJobRecovery::bed_temp_threshold; // Initialized by settings.load #endif MediaFile PrintJobRecovery::file; @@ -60,12 +61,15 @@ uint32_t PrintJobRecovery::cmd_sdpos, // = 0 #include "../module/planner.h" #include "../module/printcounter.h" #include "../module/temperature.h" -#include "../core/serial.h" #if HOMING_Z_WITH_PROBE #include "../module/probe.h" #endif +#if ENABLED(SOVOL_SV06_RTS) + #include "../lcd/sovol_rts/sovol_rts.h" +#endif + #if ENABLED(FWRETRACT) #include "fwretract.h" #endif @@ -95,7 +99,7 @@ PrintJobRecovery recovery; /** * Clear the recovery info */ -void PrintJobRecovery::init() { memset(&info, 0, sizeof(info)); } +void PrintJobRecovery::init() { info = {}; } /** * Enable or disable then call changed() @@ -113,7 +117,7 @@ void PrintJobRecovery::enable(const bool onoff) { void PrintJobRecovery::changed() { if (!enabled) purge(); - else if (IS_SD_PRINTING()) + else if (card.isStillPrinting()) save(true); TERN_(EXTENSIBLE_UI, ExtUI::onSetPowerLoss(enabled)); } @@ -170,7 +174,7 @@ void PrintJobRecovery::prepare() { */ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POWER_LOSS_ZRAISE*/, const bool raised/*=false*/) { - // We don't check IS_SD_PRINTING here so a save may occur during a pause + // We don't check isStillPrinting here so a save may occur during a pause #if SAVE_INFO_INTERVAL_MS > 0 static millis_t next_save_ms; // = 0 @@ -198,25 +202,29 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW // Set Head and Foot to matching non-zero values if (!++info.valid_head) ++info.valid_head; // non-zero in sequence - //if (!IS_SD_PRINTING()) info.valid_head = 0; + //if (!card.isStillPrinting()) info.valid_head = 0; info.valid_foot = info.valid_head; // Machine state // info.sdpos and info.current_position are pre-filled from the Stepper ISR info.feedrate = uint16_t(MMS_TO_MMM(feedrate_mm_s)); + info.feedrate_percentage = feedrate_percentage; + COPY(info.flow_percentage, planner.flow_percentage); + info.zraise = zraise; info.flag.raised = raised; // Was Z raised before power-off? + TERN_(CANCEL_OBJECTS, info.cancel_state = cancelable.state); TERN_(GCODE_REPEAT_MARKERS, info.stored_repeat = repeat); TERN_(HAS_HOME_OFFSET, info.home_offset = home_offset); TERN_(HAS_WORKSPACE_OFFSET, info.workspace_offset = workspace_offset); E_TERN_(info.active_extruder = active_extruder); - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION info.flag.volumetric_enabled = parser.volumetric_enabled; #if HAS_MULTI_EXTRUDER - EXTRUDER_LOOP() info.filament_size[e] = planner.filament_size[e]; + COPY(info.filament_size, planner.filament_size); #else if (parser.volumetric_enabled) info.filament_size[0] = planner.filament_size[active_extruder]; #endif @@ -262,14 +270,17 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW #if ENABLED(BACKUP_POWER_SUPPLY) - void PrintJobRecovery::retract_and_lift(const_float_t zraise) { + void PrintJobRecovery::retract_and_lift(const float zraise) { #if POWER_LOSS_RETRACT_LEN || POWER_LOSS_ZRAISE gcode.set_relative_mode(true); // Use relative coordinates #if POWER_LOSS_RETRACT_LEN // Retract filament now - gcode.process_subcommands_now(F("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN))); + const uint16_t old_flow = planner.flow_percentage[active_extruder]; + planner.set_flow(active_extruder, 100); + gcode.process_subcommands_now(F("G1F3000E-" STRINGIFY(POWER_LOSS_RETRACT_LEN))); + planner.set_flow(active_extruder, old_flow); #endif #if POWER_LOSS_ZRAISE @@ -315,7 +326,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW // Save the current position, distance that Z was (or should be) raised, // and a flag whether the raise was already done here. - if (IS_SD_PRINTING()) save(true, zraise, ENABLED(BACKUP_POWER_SUPPLY)); + if (card.isStillPrinting()) save(true, zraise, ENABLED(BACKUP_POWER_SUPPLY)); // Tell the LCD about the outage, even though it is about to die TERN_(EXTENSIBLE_UI, ExtUI::onPowerLoss()); @@ -409,8 +420,11 @@ void PrintJobRecovery::resume() { #endif // Interpret the saved Z according to flags - const float z_print = resume_pos.z, - z_raised = z_print + info.zraise; + const float z_print = resume_pos.z; + + #if ANY(Z_HOME_TO_MAX, POWER_LOSS_RECOVER_ZHOME) || DISABLED(BELTPRINTER) + const float z_raised = z_print + info.zraise; + #endif // // Home the axes that can safely be homed, and @@ -471,7 +485,7 @@ void PrintJobRecovery::resume() { #if !HOMING_Z_DOWN // The physical Z was adjusted at power-off so undo the M420S1 correction to Z with G92.9. - PROCESS_SUBCOMMANDS_NOW(TS(F("G92.9Z"), p_float_t(z_now, 1))); + PROCESS_SUBCOMMANDS_NOW(TS(F("G92.9Z"), p_float_t(z_now, 3))); #endif #endif @@ -482,7 +496,7 @@ void PrintJobRecovery::resume() { #endif // Recover volumetric extrusion state - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION #if HAS_MULTI_EXTRUDER EXTRUDER_LOOP() PROCESS_SUBCOMMANDS_NOW(TS(F("M200T"), e, F("D"), p_float_t(info.filament_size[e], 3))); @@ -518,7 +532,7 @@ void PrintJobRecovery::resume() { } #endif - // Restore retract and hop state from an active `G10` command + // Restore retract and hop state from an active 'G10' command #if ENABLED(FWRETRACT) EXTRUDER_LOOP() { if (info.retract[e] != 0.0) { @@ -555,12 +569,21 @@ void PrintJobRecovery::resume() { // Move back down to the saved Z for printing PROCESS_SUBCOMMANDS_NOW(TS(F("G1F600Z"), p_float_t(z_print, 3))); - // Restore the feedrate + // Restore the feedrate and percentage PROCESS_SUBCOMMANDS_NOW(TS(F("G1F"), info.feedrate)); + feedrate_percentage = info.feedrate_percentage; + + // Flowrate percentage + EXTRUDER_LOOP() planner.set_flow(e, info.flow_percentage[e]); // Restore E position with G92.9 PROCESS_SUBCOMMANDS_NOW(TS(F("G92.9E"), p_float_t(resume_pos.e, 3))); + #if ENABLED(CANCEL_OBJECTS) + cancelable.state = info.cancel_state; + cancelable.set_active_object(); // Sets the status message + #endif + TERN_(GCODE_REPEAT_MARKERS, repeat = info.stored_repeat); TERN_(HAS_HOME_OFFSET, home_offset = info.home_offset); TERN_(HAS_WORKSPACE_OFFSET, workspace_offset = info.workspace_offset); @@ -574,6 +597,11 @@ void PrintJobRecovery::resume() { // Resume the SD file from the last position PROCESS_SUBCOMMANDS_NOW(MString(F("M23 "), info.sd_filename)); PROCESS_SUBCOMMANDS_NOW(TS(F("M24S"), resume_sdpos, 'T', info.print_job_elapsed)); + + #if ENABLED(SOVOL_SV06_RTS) + if (rts.print_state) rts.refreshTime(); + rts.start_print_flag = false; + #endif } #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) @@ -589,10 +617,19 @@ void PrintJobRecovery::resume() { } DEBUG_EOL(); - DEBUG_ECHOLNPGM("feedrate: ", info.feedrate); + DEBUG_ECHOLN(F("feedrate: "), info.feedrate, F(" x "), info.feedrate_percentage, '%'); + EXTRUDER_LOOP() DEBUG_ECHOLN('E', e + 1, F(" flow %: "), info.flow_percentage[e]); DEBUG_ECHOLNPGM("zraise: ", info.zraise, " ", info.flag.raised ? "(before)" : ""); + #if ENABLED(CANCEL_OBJECTS) + const cancel_state_t cs = info.cancel_state; + DEBUG_ECHOPGM("Canceled:"); + for (int i = 0; i < cs.object_count; i++) + if (TEST(cs.canceled, i)) { DEBUG_CHAR(' '); DEBUG_ECHO(i); } + DEBUG_EOL(); + #endif + #if ENABLED(GCODE_REPEAT_MARKERS) const uint8_t ind = info.stored_repeat.count(); DEBUG_ECHOLNPGM("repeat markers: ", ind); @@ -622,7 +659,7 @@ void PrintJobRecovery::resume() { DEBUG_ECHOLNPGM("active_extruder: ", info.active_extruder); #endif - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION DEBUG_ECHOPGM("filament_size:"); EXTRUDER_LOOP() DEBUG_ECHOLNPGM(" ", info.filament_size[e]); DEBUG_EOL(); @@ -688,7 +725,7 @@ void PrintJobRecovery::resume() { DEBUG_ECHOLNPGM("flag.dryrun: ", AS_DIGIT(info.flag.dryrun)); DEBUG_ECHOLNPGM("flag.allow_cold_extrusion: ", AS_DIGIT(info.flag.allow_cold_extrusion)); - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION DEBUG_ECHOLNPGM("flag.volumetric_enabled: ", AS_DIGIT(info.flag.volumetric_enabled)); #endif } diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h index 7de8450c91..eceb862779 100644 --- a/Marlin/src/feature/powerloss.h +++ b/Marlin/src/feature/powerloss.h @@ -30,12 +30,16 @@ #include "../inc/MarlinConfig.h" +#if ENABLED(CANCEL_OBJECTS) + #include "cancel_object.h" +#endif + #if ENABLED(GCODE_REPEAT_MARKERS) - #include "../feature/repeat.h" + #include "repeat.h" #endif #if ENABLED(MIXING_EXTRUDER) - #include "../feature/mixing.h" + #include "mixing.h" #endif #if !defined(POWER_LOSS_STATE) && PIN_EXISTS(POWER_LOSS) @@ -59,9 +63,16 @@ typedef struct { // Machine state xyze_pos_t current_position; uint16_t feedrate; + int16_t feedrate_percentage; + uint16_t flow_percentage[EXTRUDERS]; float zraise; + // Canceled objects + #if ENABLED(CANCEL_OBJECTS) + cancel_state_t cancel_state; + #endif + // Repeat information #if ENABLED(GCODE_REPEAT_MARKERS) Repeat stored_repeat; @@ -77,7 +88,7 @@ typedef struct { uint8_t active_extruder; #endif - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION float filament_size[EXTRUDERS]; #endif @@ -129,7 +140,7 @@ typedef struct { #if HAS_LEVELING bool leveling:1; // M420 S #endif - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION bool volumetric_enabled:1; // M200 S D #endif } flag; @@ -226,7 +237,7 @@ class PrintJobRecovery { static void write(); #if ENABLED(BACKUP_POWER_SUPPLY) - static void retract_and_lift(const_float_t zraise); + static void retract_and_lift(const float zraise); #endif #if PIN_EXISTS(POWER_LOSS) || ENABLED(DEBUG_POWER_LOSS_RECOVERY) diff --git a/Marlin/src/feature/probe_temp_comp.cpp b/Marlin/src/feature/probe_temp_comp.cpp index f640a9fd2f..7dfa28f4b9 100644 --- a/Marlin/src/feature/probe_temp_comp.cpp +++ b/Marlin/src/feature/probe_temp_comp.cpp @@ -104,12 +104,12 @@ void ProbeTempComp::print_offsets() { #endif } -void ProbeTempComp::prepare_new_calibration(const_float_t init_meas_z) { +void ProbeTempComp::prepare_new_calibration(const float init_meas_z) { calib_idx = 0; init_measurement = init_meas_z; } -void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z) { +void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const float meas_z) { if (calib_idx >= cali_info[tsi].measurements) return; sensor_z_offsets[tsi][calib_idx++] = static_cast((meas_z - init_measurement) * 1000.0f); } @@ -186,7 +186,7 @@ void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius }; // Interpolate Z based on a temperature being within a given range - auto linear_interp = [](const_float_t x, xy_float_t p1, xy_float_t p2) { + auto linear_interp = [](const float x, xy_float_t p1, xy_float_t p2) { // zoffs1 + zoffset_per_toffset * toffset return p1.y + (p2.y - p1.y) / (p2.x - p1.x) * (x - p1.x); }; @@ -212,7 +212,7 @@ void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius } // convert offset to mm and apply it - meas_z -= offset / 1000.0f; + meas_z -= offset * 0.001f; } bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d) { diff --git a/Marlin/src/feature/probe_temp_comp.h b/Marlin/src/feature/probe_temp_comp.h index 42348db684..e6509a20cf 100644 --- a/Marlin/src/feature/probe_temp_comp.h +++ b/Marlin/src/feature/probe_temp_comp.h @@ -84,8 +84,8 @@ class ProbeTempComp { } static bool set_offset(const TempSensorID tsi, const uint8_t idx, const int16_t offset); static void print_offsets(); - static void prepare_new_calibration(const_float_t init_meas_z); - static void push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z); + static void prepare_new_calibration(const float init_meas_z); + static void push_back_new_measurement(const TempSensorID tsi, const float meas_z); static bool finish_calibration(const TempSensorID tsi); static void set_enabled(const bool ena) { enabled = ena; } diff --git a/Marlin/src/feature/rs485.cpp b/Marlin/src/feature/rs485.cpp new file mode 100644 index 0000000000..8009185450 --- /dev/null +++ b/Marlin/src/feature/rs485.cpp @@ -0,0 +1,39 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../inc/MarlinConfig.h" + +#if HAS_RS485_SERIAL + +#include "rs485.h" + +HardwareSerialBusIO rs485BusIO(&RS485_SERIAL); +RS485Bus rs485Bus(rs485BusIO, RS485_RX_ENABLE_PIN, RS485_TX_ENABLE_PIN); + +PhotonProtocol rs485Protocol; + +Packetizer rs485Packetizer(rs485Bus, rs485Protocol); + +uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE]; + +void rs485_init() { RS485_SERIAL.begin(57600); } + +#endif // HAS_RS485_SERIAL diff --git a/Marlin/src/feature/rs485.h b/Marlin/src/feature/rs485.h new file mode 100644 index 0000000000..3327626a3c --- /dev/null +++ b/Marlin/src/feature/rs485.h @@ -0,0 +1,40 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#pragma once + +#include "../inc/MarlinConfigPre.h" + +#include +#include + +#include +#include + +#define RS485_SEND_BUFFER_SIZE 32 + +extern HardwareSerialBusIO rs485BusIO; +extern RS485Bus rs485Bus; +extern PhotonProtocol rs485Protocol; +extern Packetizer rs485Packetizer; +extern uint8_t rs485Buffer[RS485_SEND_BUFFER_SIZE]; + +void rs485_init(); diff --git a/Marlin/src/feature/runout.cpp b/Marlin/src/feature/runout.cpp index 2bcb47e99a..f0b94d0de8 100644 --- a/Marlin/src/feature/runout.cpp +++ b/Marlin/src/feature/runout.cpp @@ -33,7 +33,7 @@ FilamentMonitor runout; bool FilamentMonitorBase::enabled = true, - FilamentMonitorBase::filament_ran_out; // = false + FilamentMonitorBase::filament_ran_out; // = false #if ENABLED(HOST_ACTION_COMMANDS) bool FilamentMonitorBase::host_handling; // = false @@ -51,6 +51,10 @@ bool FilamentMonitorBase::enabled = true, #if ENABLED(FILAMENT_MOTION_SENSOR) uint8_t FilamentSensorEncoder::motion_detected; #endif + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + bool RunoutResponseDelayed::ignore_motion = false; + float RunoutResponseDelayed::motion_distance_mm = FILAMENT_MOTION_DISTANCE_MM; + #endif #else int8_t RunoutResponseDebounced::runout_count[NUM_RUNOUT_SENSORS]; // = 0 #endif @@ -59,7 +63,7 @@ bool FilamentMonitorBase::enabled = true, // Filament Runout event handler // #include "../MarlinCore.h" -#include "../feature/pause.h" +#include "pause.h" #include "../gcode/queue.h" #if ENABLED(HOST_ACTION_COMMANDS) @@ -72,6 +76,8 @@ bool FilamentMonitorBase::enabled = true, void event_filament_runout(const uint8_t extruder) { + runout.init_for_restart(false); // Reset and disable + if (did_pause_print) return; // Action already in progress. Purge triggered repeated runout. #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index c95c39f273..1cd90b0bb2 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -30,7 +30,7 @@ #include "../module/planner.h" #include "../module/stepper.h" // for block_t #include "../gcode/queue.h" -#include "../feature/pause.h" // for did_pause_print +#include "pause.h" // for did_pause_print #include "../MarlinCore.h" // for printingIsActive() #include "../inc/MarlinConfig.h" @@ -46,11 +46,15 @@ #if ENABLED(FILAMENT_MOTION_SENSOR) #define HAS_FILAMENT_MOTION 1 -#endif -#if DISABLED(FILAMENT_MOTION_SENSOR) || ENABLED(FILAMENT_SWITCH_AND_MOTION) + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + #define HAS_FILAMENT_SWITCH 1 + #endif +#else #define HAS_FILAMENT_SWITCH 1 #endif +#define FILAMENT_IS_OUT(N...) (READ(FIL_RUNOUT##N##_PIN) == FIL_RUNOUT##N##_STATE) + typedef Flags< #if NUM_MOTION_SENSORS > NUM_RUNOUT_SENSORS NUM_MOTION_SENSORS @@ -118,15 +122,18 @@ class TFilamentMonitor : public FilamentMonitorBase { static void filament_motion_present(const uint8_t extruder) { response.filament_motion_present(extruder); } + static float& motion_distance() { return response.motion_distance_mm; } + static void set_motion_distance(const float mm) { response.motion_distance_mm = mm; } #endif #if HAS_FILAMENT_RUNOUT_DISTANCE static float& runout_distance() { return response.runout_distance_mm; } - static void set_runout_distance(const_float_t mm) { response.runout_distance_mm = mm; } + static void set_runout_distance(const float mm) { response.runout_distance_mm = mm; } #endif // Handle a block completion. RunoutResponseDelayed uses this to // add up the length of filament moved while the filament is out. + // Called from ISR context! static void block_completed(const block_t * const b) { if (enabled) { response.block_completed(b); @@ -136,39 +143,44 @@ class TFilamentMonitor : public FilamentMonitorBase { // Give the response a chance to update its counter. static void run() { - if (enabled && !filament_ran_out && should_monitor_runout()) { - TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, cli()); // Prevent RunoutResponseDelayed::block_completed from accumulating here - response.run(); - sensor.run(); - const runout_flags_t runout_flags = response.has_run_out(); - TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, sei()); - #if MULTI_FILAMENT_SENSOR - #if ENABLED(WATCH_ALL_RUNOUT_SENSORS) - const bool ran_out = bool(runout_flags); // any sensor triggers - uint8_t extruder = 0; - if (ran_out) while (!runout_flags.test(extruder)) extruder++; - #else - const bool ran_out = runout_flags[active_extruder]; // suppress non active extruders - uint8_t extruder = active_extruder; - #endif + if (!enabled || filament_ran_out || !should_monitor_runout()) return; + TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, cli()); // Prevent RunoutResponseDelayed::block_completed from accumulating here + response.run(); + sensor.run(); + const runout_flags_t runout_flags = response.has_run_out(); + TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, sei()); + #if MULTI_FILAMENT_SENSOR + #if ENABLED(WATCH_ALL_RUNOUT_SENSORS) + const bool ran_out = bool(runout_flags); // any sensor triggers + uint8_t extruder = 0; + if (ran_out) while (!runout_flags.test(extruder)) extruder++; #else - const bool ran_out = bool(runout_flags); + const bool ran_out = runout_flags[active_extruder]; // suppress non active extruders uint8_t extruder = active_extruder; #endif + #else + const bool ran_out = bool(runout_flags); + uint8_t extruder = active_extruder; + #endif - if (ran_out) { - #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) - SERIAL_ECHOPGM("Runout Sensors: "); - for (uint8_t i = 0; i < 8; ++i) SERIAL_CHAR('0' + char(runout_flags[i])); - SERIAL_ECHOLNPGM(" -> ", extruder, " RUN OUT"); - #endif + if (!ran_out) return; - filament_ran_out = true; - event_filament_runout(extruder); - planner.synchronize(); - } - } + #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) + SERIAL_ECHOPGM("Runout Sensors: "); + for (uint8_t i = 0; i < 8; ++i) SERIAL_CHAR('0' + char(runout_flags[i])); + SERIAL_ECHOLNPGM(" -> ", extruder, " RUN OUT"); + #endif + + filament_ran_out = true; + event_filament_runout(extruder); + planner.synchronize(); } + + // Reset after a filament runout or upon resuming a job + static void init_for_restart(const bool onoff=true) { + response.init_for_restart(onoff); + } + }; /*************************** FILAMENT PRESENCE SENSORS ***************************/ @@ -265,6 +277,7 @@ class FilamentSensorBase { } public: + // Called from ISR context to indicate a block was completed static void block_completed(const block_t * const b) { // If the sensor wheel has moved since the last call to // this method reset the runout counter for the extruder. @@ -301,6 +314,7 @@ class FilamentSensorBase { } public: + // Called from ISR context to indicate a block was completed static void block_completed(const block_t * const) {} static void run() { @@ -330,6 +344,7 @@ class FilamentSensorBase { TERN_(HAS_FILAMENT_SWITCH, static FilamentSensorSwitch switch_sensor); public: + // Called from ISR context to indicate a block was completed static void block_completed(const block_t * const b) { TERN_(HAS_FILAMENT_MOTION, encoder_sensor.block_completed(b)); TERN_(HAS_FILAMENT_SWITCH, switch_sensor.block_completed(b)); @@ -360,40 +375,67 @@ class FilamentSensorBase { class RunoutResponseDelayed { private: static countdown_t mm_countdown; + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + static bool ignore_motion; // Flag to ignore the encoder + #endif public: static float runout_distance_mm; + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + static float motion_distance_mm; + #endif + + static void set_ignore_motion(const bool ignore=true) { + UNUSED(ignore); + TERN_(FILAMENT_SWITCH_AND_MOTION, ignore_motion = ignore); + } + static void reset() { for (uint8_t i = 0; i < NUM_RUNOUT_SENSORS; ++i) filament_present(i); #if ENABLED(FILAMENT_SWITCH_AND_MOTION) for (uint8_t i = 0; i < NUM_MOTION_SENSORS; ++i) filament_motion_present(i); #endif + set_ignore_motion(false); } static void run() { #if ENABLED(FILAMENT_RUNOUT_SENSOR_DEBUG) static millis_t t = 0; const millis_t ms = millis(); - if (ELAPSED(ms, t)) { - t = millis() + 1000UL; - for (uint8_t i = 0; i < NUM_RUNOUT_SENSORS; ++i) - SERIAL_ECHO(i ? F(", ") : F("Runout remaining mm: "), mm_countdown.runout[i]); - #if ENABLED(FILAMENT_SWITCH_AND_MOTION) - for (uint8_t i = 0; i < NUM_MOTION_SENSORS; ++i) - SERIAL_ECHO(i ? F(", ") : F("Motion remaining mm: "), mm_countdown.motion[i]); - #endif - SERIAL_EOL(); - } + if (PENDING(ms, t)) return; + t = ms + 1000UL; + for (uint8_t i = 0; i < NUM_RUNOUT_SENSORS; ++i) + SERIAL_ECHO(i ? F(", ") : F("Runout remaining mm: "), mm_countdown.runout[i]); + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + for (uint8_t i = 0; i < NUM_MOTION_SENSORS; ++i) + SERIAL_ECHO(i ? F(", ") : F("Motion remaining mm: "), mm_countdown.motion[i]); + #endif + SERIAL_EOL(); #endif } + // Get runout status for all presence sensors and motion sensors static runout_flags_t has_run_out() { runout_flags_t runout_flags{0}; - for (uint8_t i = 0; i < NUM_RUNOUT_SENSORS; ++i) if (mm_countdown.runout[i] < 0) runout_flags.set(i); + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) - for (uint8_t i = 0; i < NUM_MOTION_SENSORS; ++i) if (mm_countdown.motion[i] < 0) runout_flags.set(i); + // Runout based on filament motion + if (!ignore_motion) { + for (uint8_t i = 0; i < NUM_MOTION_SENSORS; ++i) { + if (mm_countdown.motion[i] < 0) { + runout_flags.set(i); + mm_countdown.runout[i] = -1; // For a filament jam don't wait for runout_distance_mm! + } + } + } #endif + + // Runout based on filament presence + for (uint8_t i = 0; i < NUM_RUNOUT_SENSORS; ++i) + if (mm_countdown.runout[i] < 0) + runout_flags.set(i); + return runout_flags; } @@ -417,8 +459,8 @@ class FilamentSensorBase { #if ENABLED(FILAMENT_SWITCH_AND_MOTION) static void filament_motion_present(const uint8_t extruder) { // Same logic as filament_present - if (mm_countdown.motion[extruder] < runout_distance_mm || did_pause_print) { - mm_countdown.motion[extruder] = runout_distance_mm; + if (mm_countdown.motion[extruder] < motion_distance_mm || did_pause_print) { + mm_countdown.motion[extruder] = motion_distance_mm; mm_countdown.motion_reset.clear(extruder); } else @@ -426,29 +468,42 @@ class FilamentSensorBase { } #endif + // Called from ISR context to indicate a block was completed static void block_completed(const block_t * const b) { - const int32_t esteps = b->steps.e; - if (!esteps) return; - // No calculation unless paused or printing if (!should_monitor_runout()) return; + // Only extrusion moves are examined + const int32_t esteps = b->steps.e; + if (!esteps) return; + // No need to ignore retract/unretract movement since they complement each other const uint8_t e = b->extruder; const float mm = (b->direction_bits.e ? esteps : -esteps) * planner.mm_per_step[E_AXIS_N(e)]; + // Apply E distance to runout countdown, reset if flagged if (e < NUM_RUNOUT_SENSORS) { mm_countdown.runout[e] -= mm; if (mm_countdown.runout_reset[e]) filament_present(e); // Reset pending. Try to reset. } #if ENABLED(FILAMENT_SWITCH_AND_MOTION) - if (e < NUM_MOTION_SENSORS) { + // Apply E distance to motion countdown, reset if flagged + if (!ignore_motion && e < NUM_MOTION_SENSORS) { mm_countdown.motion[e] -= mm; if (mm_countdown.motion_reset[e]) filament_motion_present(e); // Reset pending. Try to reset. } #endif } + + // Reset after a filament runout or upon resuming a job + static void init_for_restart(const bool onoff=true) { + UNUSED(onoff); + #if ENABLED(FILAMENT_SWITCH_AND_MOTION) + reset(); // also calls set_ignore_motion(false) + set_ignore_motion(!onoff); + #endif + } }; #else // !HAS_FILAMENT_RUNOUT_DISTANCE @@ -476,11 +531,15 @@ class FilamentSensorBase { return runout_flags; } + // Called from ISR context to indicate a block was completed static void block_completed(const block_t * const) { } static void filament_present(const uint8_t extruder) { runout_count[extruder] = runout_threshold; } + + // Reset after a filament runout or upon resuming a job + static void init_for_restart(const bool=true) { reset(); } }; #endif // !HAS_FILAMENT_RUNOUT_DISTANCE diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp index c0635c7220..5f0ea7dd7b 100644 --- a/Marlin/src/feature/spindle_laser.cpp +++ b/Marlin/src/feature/spindle_laser.cpp @@ -35,7 +35,7 @@ #endif #if ENABLED(I2C_AMMETER) - #include "../feature/ammeter.h" + #include "ammeter.h" #endif SpindleLaser cutter; @@ -43,6 +43,10 @@ bool SpindleLaser::enable_state; // Virtual uint8_t SpindleLaser::power, // Actual power output 0-255 ocr or "0 = off" > 0 = "on" SpindleLaser::last_power_applied; // = 0 // Basic power state tracking +#if HAS_SPINDLE_ACCELERATION + uint32_t SpindleLaser::acceleration_spindle_deg_per_s2; // (°/s/s) Spindle acceleration. Initialized by settings.load +#endif + #if ENABLED(LASER_FEATURE) cutter_test_pulse_t SpindleLaser::testPulse = 50; // (ms) Test fire pulse default duration uint8_t SpindleLaser::last_block_power; // = 0 // Track power changes for dynamic inline power @@ -100,7 +104,22 @@ void SpindleLaser::init() { #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); #endif - hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + #if HAS_SPINDLE_ACCELERATION + const int16_t diff = ocr - last_power_applied; + const uint8_t abs_diff = ABS(diff); + uint8_t current_ocr = last_power_applied; + // Duration between ocr increments. SPEED_POWER_MAX is in RPM. + const millis_t duration = (float(SPEED_POWER_MAX) * (60000.f / 2550.f) / float(acceleration_spindle_deg_per_s2)) * abs_diff; + millis_t next_ocr_change = millis() + duration; + while (current_ocr != ocr) { + while (PENDING(millis(), next_ocr_change)) idle(); + current_ocr += diff > 0 ? 1 : -1; + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), current_ocr ^ SPINDLE_LASER_PWM_OFF); + next_ocr_change += duration; + } + #else + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + #endif } void SpindleLaser::set_ocr(const uint8_t ocr) { @@ -111,10 +130,10 @@ void SpindleLaser::init() { } void SpindleLaser::ocr_off() { + _set_ocr(0); #if PIN_EXISTS(SPINDLE_LASER_ENA) WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Cutter OFF #endif - _set_ocr(0); } #endif // SPINDLE_LASER_USE_PWM @@ -127,9 +146,8 @@ void SpindleLaser::init() { */ void SpindleLaser::apply_power(const uint8_t opwr) { if (enabled() || opwr == 0) { // 0 check allows us to disable where no ENA pin exists - // Test and set the last power used to improve performance + // Test the last power used to improve performance if (opwr == last_power_applied) return; - last_power_applied = opwr; // Handle PWM driven or just simple on/off #if ENABLED(SPINDLE_LASER_USE_PWM) if (CUTTER_UNIT_IS(RPM) && unitPower == 0) @@ -146,6 +164,7 @@ void SpindleLaser::apply_power(const uint8_t opwr) { WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); isReadyForUI = true; #endif + last_power_applied = opwr; } else { #if PIN_EXISTS(SPINDLE_LASER_ENA) diff --git a/Marlin/src/feature/spindle_laser.h b/Marlin/src/feature/spindle_laser.h index 681df2f081..8305c1fec4 100644 --- a/Marlin/src/feature/spindle_laser.h +++ b/Marlin/src/feature/spindle_laser.h @@ -35,8 +35,12 @@ // Inline laser power #include "../module/planner.h" +#define RPM_TO_PWM(X) ((X) * 255 / (SPEED_POWER_MAX)) +#define PWM_TO_RPM(X) ((X) * (SPEED_POWER_MAX) / 255) #define PCT_TO_PWM(X) ((X) * 255 / 100) +#define PWM_TO_PCT(X) ((X) * 100 / 255) #define PCT_TO_SERVO(X) ((X) * 180 / 100) +#define CUTTER_PWM_TO_SPWR(X) (CUTTER_UNIT_IS(PERCENT) ? PWM_TO_PCT(X) : (CUTTER_UNIT_IS(RPM) ? PWM_TO_RPM(X) : X)) // Laser/Cutter operation mode enum CutterMode : int8_t { @@ -50,13 +54,13 @@ class SpindleLaser { public: static CutterMode cutter_mode; - static constexpr uint8_t pct_to_ocr(const_float_t pct) { return uint8_t(PCT_TO_PWM(pct)); } + static constexpr uint8_t pct_to_ocr(const float pct) { return uint8_t(PCT_TO_PWM(pct)); } // cpower = configured values (e.g., SPEED_POWER_MAX) // Convert configured power range to a percentage static constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0); static constexpr uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) { - return cpwr ? round(100.0f * (cpwr - power_floor) / (SPEED_POWER_MAX - power_floor)) : 0; + return cpwr ? LROUND(100.0f * (cpwr - power_floor) / (SPEED_POWER_MAX - power_floor)) : 0; } // Convert config defines from RPM to %, angle or PWM when in Spindle mode @@ -105,11 +109,14 @@ public: static uint8_t power, last_power_applied; // Basic power state tracking - static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K + static cutter_frequency_t frequency; // (Hz) Laser/Spindle PWM frequency (2000..50000) - static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage or RPM - unitPower; // Power as displayed status in PWM, Percentage or RPM + static cutter_power_t menuPower, // Power as set via LCD menu in PWM, Percentage, or RPM + unitPower; // Power as displayed status in PWM, Percentage, or RPM + #if HAS_SPINDLE_ACCELERATION + static uint32_t acceleration_spindle_deg_per_s2; // (°/s/s) Spindle acceleration + #endif static void init(); #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY @@ -157,7 +164,7 @@ public: */ static cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit=_CUTTER_POWER(CUTTER_POWER_UNIT)) { static constexpr float - min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, round(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)), + min_pct = TERN(CUTTER_POWER_RELATIVE, 0, TERN(SPINDLE_FEATURE, roundf(100.0f * (SPEED_POWER_MIN) / (SPEED_POWER_MAX)), SPEED_POWER_MIN)), max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX); if (pwr <= 0) return 0; cutter_power_t upwr; diff --git a/Marlin/src/feature/tmc_util.cpp b/Marlin/src/feature/tmc_util.cpp index 421f9b0c22..b1fc69f88a 100644 --- a/Marlin/src/feature/tmc_util.cpp +++ b/Marlin/src/feature/tmc_util.cpp @@ -24,6 +24,12 @@ #if HAS_TRINAMIC_CONFIG +/** + * feature/tmc_util.cpp - Functions for debugging Trinamic stepper drivers. + * The main entry point is `tmc_report_all` which is called by M122 to collect + * and report diagnostic information about each enabled TMC driver. + */ + #include "tmc_util.h" #include "../MarlinCore.h" @@ -32,6 +38,10 @@ #include "../libs/duration_t.h" #include "../gcode/gcode.h" +#if ENABLED(SOVOL_SV06_RTS) + #include "../lcd/sovol_rts/sovol_rts.h" +#endif + #if ENABLED(TMC_DEBUG) #include "../libs/hex_print.h" #if ENABLED(MONITOR_DRIVER_STATUS) @@ -39,6 +49,10 @@ #endif #endif +#if ENABLED(EDITABLE_HOMING_CURRENT) + homing_current_t homing_current_mA; +#endif + /** * Check for over temperature or short to ground error flags. * Report and log warning of overtemperature condition. @@ -64,7 +78,7 @@ #endif ; #if ENABLED(TMC_DEBUG) - #if HAS_TMCX1X0 || HAS_TMC220x + #if HAS_TMCX1X0_OR_2240 || HAS_TMC220x uint8_t cs_actual; #endif #if HAS_STALLGUARD @@ -134,6 +148,67 @@ #endif // HAS_TMCX1X0 + #if HAS_DRIVER(TMC2240) + + #if ENABLED(TMC_DEBUG) + static uint32_t get_pwm_scale(TMC2240Stepper &st) { return st.PWM_SCALE(); } + #endif + + static TMC_driver_data get_driver_data(TMC2240Stepper &st) { + constexpr uint8_t OT_bp = 25, OTPW_bp = 26; + constexpr uint32_t S2G_bm = 0x18000000; + #if ENABLED(TMC_DEBUG) + constexpr uint16_t SG_RESULT_bm = 0x3FF; // 0:9 + constexpr uint8_t STEALTH_bp = 14; + constexpr uint32_t CS_ACTUAL_bm = 0x1F0000; // 16:20 + constexpr uint8_t STALL_GUARD_bp = 24; + constexpr uint8_t STST_bp = 31; + #endif + TMC_driver_data data; + const auto ds = data.drv_status = st.DRV_STATUS(); + #ifdef __AVR__ + + // 8-bit optimization saves up to 70 bytes of PROGMEM per axis + uint8_t spart; + #if ENABLED(TMC_DEBUG) + data.sg_result = ds & SG_RESULT_bm; + spart = ds >> 8; + data.is_stealth = TEST(spart, STEALTH_bp - 8); + spart = ds >> 16; + data.cs_actual = spart & (CS_ACTUAL_bm >> 16); + #endif + spart = ds >> 24; + data.is_ot = TEST(spart, OT_bp - 24); + data.is_otpw = TEST(spart, OTPW_bp - 24); + data.is_s2g = !!(spart & (S2G_bm >> 24)); + #if ENABLED(TMC_DEBUG) + data.is_stall = TEST(spart, STALL_GUARD_bp - 24); + data.is_standstill = TEST(spart, STST_bp - 24); + data.sg_result_reasonable = !data.is_standstill; // sg_result has no reasonable meaning while standstill + #endif + + #else // !__AVR__ + + data.is_ot = TEST(ds, OT_bp); + data.is_otpw = TEST(ds, OTPW_bp); + data.is_s2g = !!(ds & S2G_bm); + #if ENABLED(TMC_DEBUG) + constexpr uint8_t CS_ACTUAL_sb = 16; + data.sg_result = ds & SG_RESULT_bm; + data.is_stealth = TEST(ds, STEALTH_bp); + data.cs_actual = (ds & CS_ACTUAL_bm) >> CS_ACTUAL_sb; + data.is_stall = TEST(ds, STALL_GUARD_bp); + data.is_standstill = TEST(ds, STST_bp); + data.sg_result_reasonable = !data.is_standstill; // sg_result has no reasonable meaning while standstill + #endif + + #endif // !__AVR__ + + return data; + } + + #endif // TMC2240 + #if HAS_TMC220x #if ENABLED(TMC_DEBUG) @@ -207,6 +282,7 @@ if (data.is_ot) SERIAL_ECHOLNPGM("overtemperature"); if (data.is_s2g) SERIAL_ECHOLNPGM("coil short circuit"); TERN_(TMC_DEBUG, tmc_report_all()); + TERN_(SOVOL_SV06_RTS, rts.gotoPage(ID_DriverError_L, ID_DriverError_D)); kill(F("Driver error")); } #endif @@ -222,13 +298,14 @@ SString<50>(F(" driver overtemperature warning! ("), st.getMilliamps(), F("mA)")).echoln(); } - template - void report_polled_driver_data(TMC &st, const TMC_driver_data &data) { - const uint32_t pwm_scale = get_pwm_scale(st); - st.printLabel(); - SString<60> report(':', pwm_scale); - #if ENABLED(TMC_DEBUG) - #if HAS_TMCX1X0 || HAS_TMC220x + #if ENABLED(TMC_DEBUG) + + template + void report_polled_driver_data(TMC &st, const TMC_driver_data &data) { + const uint32_t pwm_scale = get_pwm_scale(st); + st.printLabel(); + SString<60> report(':', pwm_scale); + #if HAS_TMCX1X0_OR_2240 || HAS_TMC220x report.append('/', data.cs_actual); #endif #if HAS_STALLGUARD @@ -238,22 +315,21 @@ else report += '-'; #endif - #endif - report += '|'; - if (st.error_count) report += 'E'; // Error - if (data.is_ot) report += 'O'; // Over-temperature - if (data.is_otpw) report += 'W'; // over-temperature pre-Warning - #if ENABLED(TMC_DEBUG) + report += '|'; + if (st.error_count) report += 'E'; // Error + if (data.is_ot) report += 'O'; // Over-temperature + if (data.is_otpw) report += 'W'; // over-temperature pre-Warning if (data.is_stall) report += 'G'; // stallGuard if (data.is_stealth) report += 'T'; // stealthChop if (data.is_standstill) report += 'I'; // standstIll - #endif - if (st.flag_otpw) report += 'F'; // otpw Flag - report += '|'; - if (st.otpw_count > 0) report += st.otpw_count; - report += '\t'; - report.echo(); - } + if (st.flag_otpw) report += 'F'; // otpw Flag + report += '|'; + if (st.otpw_count > 0) report += st.otpw_count; + report += '\t'; + report.echo(); + } + + #endif // TMC_DEBUG #if CURRENT_STEP_DOWN > 0 @@ -313,9 +389,9 @@ else if (st.otpw_count > 0) st.otpw_count = 0; } - #if ENABLED(TMC_DEBUG) - if (need_debug_reporting) report_polled_driver_data(st, data); - #endif + if (need_debug_reporting) { + TERN_(TMC_DEBUG, report_polled_driver_data(st, data)); + } return should_step_down; } @@ -339,127 +415,70 @@ if (need_update_error_counters || need_debug_reporting) { - #if AXIS_IS_TMC(X) || AXIS_IS_TMC(X2) - { - bool result = false; - #if AXIS_IS_TMC(X) - if (monitor_tmc_driver(stepperX, need_update_error_counters, need_debug_reporting)) result = true; - #endif - #if AXIS_IS_TMC(X2) - if (monitor_tmc_driver(stepperX2, need_update_error_counters, need_debug_reporting)) result = true; - #endif - if (result) { - #if AXIS_IS_TMC(X) - step_current_down(stepperX); - #endif - #if AXIS_IS_TMC(X2) - step_current_down(stepperX2); - #endif + #if X_IS_TRINAMIC || X2_IS_TRINAMIC + if ( TERN0(X_IS_TRINAMIC, monitor_tmc_driver(stepperX, need_update_error_counters, need_debug_reporting)) + || TERN0(X2_IS_TRINAMIC, monitor_tmc_driver(stepperX2, need_update_error_counters, need_debug_reporting)) + ) { + TERN_(X_IS_TRINAMIC, step_current_down(stepperX)); + TERN_(X2_IS_TRINAMIC, step_current_down(stepperX2)); } - } #endif - #if AXIS_IS_TMC(Y) || AXIS_IS_TMC(Y2) - { - bool result = false; - #if AXIS_IS_TMC(Y) - if (monitor_tmc_driver(stepperY, need_update_error_counters, need_debug_reporting)) result = true; - #endif - #if AXIS_IS_TMC(Y2) - if (monitor_tmc_driver(stepperY2, need_update_error_counters, need_debug_reporting)) result = true; - #endif - if (result) { - #if AXIS_IS_TMC(Y) - step_current_down(stepperY); - #endif - #if AXIS_IS_TMC(Y2) - step_current_down(stepperY2); - #endif + #if Y_IS_TRINAMIC || Y2_IS_TRINAMIC + if ( TERN0(Y_IS_TRINAMIC, monitor_tmc_driver(stepperY, need_update_error_counters, need_debug_reporting)) + || TERN0(Y2_IS_TRINAMIC, monitor_tmc_driver(stepperY2, need_update_error_counters, need_debug_reporting)) + ) { + TERN_(Y_IS_TRINAMIC, step_current_down(stepperY)); + TERN_(Y2_IS_TRINAMIC, step_current_down(stepperY2)); } - } #endif - #if AXIS_IS_TMC(Z) || AXIS_IS_TMC(Z2) || AXIS_IS_TMC(Z3) || AXIS_IS_TMC(Z4) - { - bool result = false; - #if AXIS_IS_TMC(Z) - if (monitor_tmc_driver(stepperZ, need_update_error_counters, need_debug_reporting)) result = true; - #endif - #if AXIS_IS_TMC(Z2) - if (monitor_tmc_driver(stepperZ2, need_update_error_counters, need_debug_reporting)) result = true; - #endif - #if AXIS_IS_TMC(Z3) - if (monitor_tmc_driver(stepperZ3, need_update_error_counters, need_debug_reporting)) result = true; - #endif - #if AXIS_IS_TMC(Z4) - if (monitor_tmc_driver(stepperZ4, need_update_error_counters, need_debug_reporting)) result = true; - #endif - if (result) { - #if AXIS_IS_TMC(Z) - step_current_down(stepperZ); - #endif - #if AXIS_IS_TMC(Z2) - step_current_down(stepperZ2); - #endif - #if AXIS_IS_TMC(Z3) - step_current_down(stepperZ3); - #endif - #if AXIS_IS_TMC(Z4) - step_current_down(stepperZ4); - #endif + #if ANY(Z_IS_TRINAMIC, Z2_IS_TRINAMIC, Z3_IS_TRINAMIC, Z4_IS_TRINAMIC) + if ( TERN0(Z_IS_TRINAMIC, monitor_tmc_driver(stepperZ, need_update_error_counters, need_debug_reporting)) + || TERN0(Z2_IS_TRINAMIC, monitor_tmc_driver(stepperZ2, need_update_error_counters, need_debug_reporting)) + || TERN0(Z3_IS_TRINAMIC, monitor_tmc_driver(stepperZ3, need_update_error_counters, need_debug_reporting)) + || TERN0(Z4_IS_TRINAMIC, monitor_tmc_driver(stepperZ4, need_update_error_counters, need_debug_reporting)) + ) { + TERN_(Z_IS_TRINAMIC, step_current_down(stepperZ)); + TERN_(Z2_IS_TRINAMIC, step_current_down(stepperZ2)); + TERN_(Z3_IS_TRINAMIC, step_current_down(stepperZ3)); + TERN_(Z4_IS_TRINAMIC, step_current_down(stepperZ4)); } - } #endif - #if AXIS_IS_TMC(I) + #if I_IS_TRINAMIC if (monitor_tmc_driver(stepperI, need_update_error_counters, need_debug_reporting)) step_current_down(stepperI); #endif - #if AXIS_IS_TMC(J) + #if J_IS_TRINAMIC if (monitor_tmc_driver(stepperJ, need_update_error_counters, need_debug_reporting)) step_current_down(stepperJ); #endif - #if AXIS_IS_TMC(K) + #if K_IS_TRINAMIC if (monitor_tmc_driver(stepperK, need_update_error_counters, need_debug_reporting)) step_current_down(stepperK); #endif - #if AXIS_IS_TMC(U) + #if U_IS_TRINAMIC if (monitor_tmc_driver(stepperU, need_update_error_counters, need_debug_reporting)) step_current_down(stepperU); #endif - #if AXIS_IS_TMC(V) + #if V_IS_TRINAMIC if (monitor_tmc_driver(stepperV, need_update_error_counters, need_debug_reporting)) step_current_down(stepperV); #endif - #if AXIS_IS_TMC(W) + #if W_IS_TRINAMIC if (monitor_tmc_driver(stepperW, need_update_error_counters, need_debug_reporting)) step_current_down(stepperW); #endif - #if AXIS_IS_TMC(E0) - (void)monitor_tmc_driver(stepperE0, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E1) - (void)monitor_tmc_driver(stepperE1, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E2) - (void)monitor_tmc_driver(stepperE2, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E3) - (void)monitor_tmc_driver(stepperE3, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E4) - (void)monitor_tmc_driver(stepperE4, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E5) - (void)monitor_tmc_driver(stepperE5, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E6) - (void)monitor_tmc_driver(stepperE6, need_update_error_counters, need_debug_reporting); - #endif - #if AXIS_IS_TMC(E7) - (void)monitor_tmc_driver(stepperE7, need_update_error_counters, need_debug_reporting); - #endif + TERN_(E0_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE0, need_update_error_counters, need_debug_reporting)); + TERN_(E1_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE1, need_update_error_counters, need_debug_reporting)); + TERN_(E2_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE2, need_update_error_counters, need_debug_reporting)); + TERN_(E3_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE3, need_update_error_counters, need_debug_reporting)); + TERN_(E4_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE4, need_update_error_counters, need_debug_reporting)); + TERN_(E5_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE5, need_update_error_counters, need_debug_reporting)); + TERN_(E6_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE6, need_update_error_counters, need_debug_reporting)); + TERN_(E7_IS_TRINAMIC, (void)monitor_tmc_driver(stepperE7, need_update_error_counters, need_debug_reporting)); if (TERN0(TMC_DEBUG, need_debug_reporting)) SERIAL_EOL(); } @@ -505,7 +524,7 @@ TMC_TSTEP, TMC_TPWMTHRS, TMC_TPWMTHRS_MMS, - TMC_OTPW, + TMC_DEBUG_OTPW, TMC_OTPW_TRIGGERED, TMC_TOFF, TMC_TBL, @@ -513,7 +532,12 @@ TMC_HSTRT, TMC_SGT, TMC_MSCNT, - TMC_INTERPOLATE + TMC_INTERPOLATE, + TMC_VAIN, + TMC_VSUPPLY, + TMC_TEMP, + TMC_OVERTEMP, + TMC_OVERVOLT_THD }; enum TMC_drv_status_enum : char { TMC_DRV_CODES, @@ -557,24 +581,49 @@ TMC_GET_DRVCTRL, TMC_GET_DRVSTATUS, TMC_GET_SGCSCONF, - TMC_GET_SMARTEN + TMC_GET_SMARTEN, + TMC_GET_SG4_THRS, + TMC_GET_SG4_RESULT }; template static void print_vsense(TMC &st) { SERIAL_ECHO(st.vsense() ? F("1=.18") : F("0=.325")); } + #if HAS_DRIVER(TMC2160) + template + void print_vsense(TMCMarlin &) { } + #endif + #if HAS_DRIVER(TMC5160) + template + void print_vsense(TMCMarlin &) { } + #endif + #if HAS_DRIVER(TMC2240) + template + void print_vsense(TMCMarlin &) { } + #endif + + template + void print_cs_actual(TMC &st) { SERIAL_ECHO(st.cs_actual(), F("/31")); } + #if HAS_DRIVER(TMC2240) + template + void print_cs_actual(TMCMarlin &) { } + #endif + + static void print_true_or_false(const bool tf) { SERIAL_ECHO(TRUE_FALSE(tf)); } #if HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC5130) + // Additional tmc_status fields for 2130/5130 and related drivers static void _tmc_status(TMC2130Stepper &st, const TMC_debug_enum i) { switch (i) { case TMC_PWM_SCALE: SERIAL_ECHO(st.PWM_SCALE()); break; case TMC_SGT: SERIAL_ECHO(st.sgt()); break; - case TMC_STEALTHCHOP: serialprint_truefalse(st.en_pwm_mode()); break; - case TMC_INTERPOLATE: serialprint_truefalse(st.intpol()); break; + case TMC_STEALTHCHOP: print_true_or_false(st.en_pwm_mode()); break; + case TMC_INTERPOLATE: print_true_or_false(st.intpol()); break; default: break; } } #endif #if HAS_TMCX1X0 + // Additional tmc_parse_drv_status fields for 2130 and related drivers static void _tmc_parse_drv_status(TMC2130Stepper &st, const TMC_drv_status_enum i) { switch (i) { case TMC_STALLGUARD: if (st.stallguard()) SERIAL_CHAR('*'); break; @@ -587,57 +636,56 @@ #endif #if HAS_DRIVER(TMC2160) || HAS_DRIVER(TMC5160) - template - void print_vsense(TMCMarlin &) { } - - template - void print_vsense(TMCMarlin &) { } - + // Additional tmc_status fields for 2160/5160 and related drivers static void _tmc_status(TMC2160Stepper &st, const TMC_debug_enum i) { switch (i) { case TMC_PWM_SCALE: SERIAL_ECHO(st.PWM_SCALE()); break; case TMC_SGT: SERIAL_ECHO(st.sgt()); break; - case TMC_STEALTHCHOP: serialprint_truefalse(st.en_pwm_mode()); break; - case TMC_GLOBAL_SCALER: - { - const uint16_t value = st.GLOBAL_SCALER(); - SERIAL_ECHO(value ?: 256); - SERIAL_ECHOPGM("/256"); - } - break; - case TMC_INTERPOLATE: serialprint_truefalse(st.intpol()); break; + case TMC_STEALTHCHOP: print_true_or_false(st.en_pwm_mode()); break; + case TMC_GLOBAL_SCALER: { + const uint16_t value = st.GLOBAL_SCALER(); + SERIAL_ECHO(value ?: 256); + SERIAL_ECHOPGM("/256"); + } break; + case TMC_INTERPOLATE: print_true_or_false(st.intpol()); break; default: break; } } - #endif + #endif // TMC2160 || TMC5160 #if HAS_TMC220x + + // Additional tmc_status fields for 2208/2224/2209 drivers static void _tmc_status(TMC2208Stepper &st, const TMC_debug_enum i) { switch (i) { + // PWM_SCALE case TMC_PWM_SCALE_SUM: SERIAL_ECHO(st.pwm_scale_sum()); break; case TMC_PWM_SCALE_AUTO: SERIAL_ECHO(st.pwm_scale_auto()); break; + // PWM_AUTO case TMC_PWM_OFS_AUTO: SERIAL_ECHO(st.pwm_ofs_auto()); break; case TMC_PWM_GRAD_AUTO: SERIAL_ECHO(st.pwm_grad_auto()); break; - case TMC_STEALTHCHOP: serialprint_truefalse(st.stealth()); break; - case TMC_INTERPOLATE: serialprint_truefalse(st.intpol()); break; + // CHOPCONF + case TMC_STEALTHCHOP: print_true_or_false(st.stealth()); break; + case TMC_INTERPOLATE: print_true_or_false(st.intpol()); break; default: break; } } #if HAS_DRIVER(TMC2209) + // Additional tmc_status fields for 2209 drivers template static void _tmc_status(TMCMarlin &st, const TMC_debug_enum i) { switch (i) { case TMC_SGT: SERIAL_ECHO(st.SGTHRS()); break; case TMC_UART_ADDR: SERIAL_ECHO(st.get_address()); break; default: - TMC2208Stepper *parent = &st; - _tmc_status(*parent, i); + _tmc_status(static_cast(st), i); break; } } #endif + // Additional tmc_parse_drv_status fields for 2208/2224/2209 drivers static void _tmc_parse_drv_status(TMC2208Stepper &st, const TMC_drv_status_enum i) { switch (i) { case TMC_T157: if (st.t157()) SERIAL_CHAR('*'); break; @@ -652,52 +700,99 @@ } #if HAS_DRIVER(TMC2209) + // Additional tmc_parse_drv_status fields for 2209 drivers static void _tmc_parse_drv_status(TMC2209Stepper &st, const TMC_drv_status_enum i) { switch (i) { case TMC_SG_RESULT: SERIAL_ECHO(st.SG_RESULT()); break; - default: _tmc_parse_drv_status(static_cast(st), i); break; + default: + _tmc_parse_drv_status(static_cast(st), i); + break; } } #endif - #endif + + #endif // HAS_TMC220x + + #if HAS_DRIVER(TMC2240) + + // Additional tmc_parse_drv_status fields for 2240 drivers + static void _tmc_parse_drv_status(TMC2240Stepper &st, const TMC_drv_status_enum i) { + switch (i) { + case TMC_S2VSA: if (st.s2vsa()) SERIAL_CHAR('*'); break; + case TMC_S2VSB: if (st.s2vsb()) SERIAL_CHAR('*'); break; + case TMC_STEALTHCHOP: print_true_or_false(st.stealth()); break; + case TMC_FSACTIVE: if (st.fsactive()) SERIAL_CHAR('*'); break; + case TMC_DRV_CS_ACTUAL: if (st.CS_ACTUAL()) SERIAL_CHAR('*'); break; + case TMC_STALLGUARD: if (st.stallguard()) SERIAL_CHAR('*'); break; + case TMC_OT: if (st.ot()) SERIAL_CHAR('*'); break; + case TMC_SG_RESULT: SERIAL_ECHO(st.SG_RESULT()); break; + default: break; // other... + } + } + + // Additional tmc_status fields for 2240 drivers + static void _tmc_status(TMC2240Stepper &st, const TMC_debug_enum i) { + switch (i) { + // PWM_SCALE + case TMC_PWM_SCALE_SUM: SERIAL_ECHO(st.pwm_scale_sum()); break; + case TMC_PWM_SCALE_AUTO: SERIAL_ECHO(st.pwm_scale_auto()); break; + // PWM_AUTO + case TMC_PWM_OFS_AUTO: SERIAL_ECHO(st.pwm_ofs_auto()); break; + case TMC_PWM_GRAD_AUTO: SERIAL_ECHO(st.pwm_grad_auto()); break; + // CHOPCONF + case TMC_STEALTHCHOP: print_true_or_false(st.stealth()); break; + case TMC_INTERPOLATE: print_true_or_false(st.intpol()); break; + case TMC_VAIN: SERIAL_ECHO(st.get_ain_voltage()); break; + case TMC_VSUPPLY: SERIAL_ECHO(st.get_vsupply_voltage()); break; + case TMC_TEMP: SERIAL_ECHO(st.get_chip_temperature()); break; + case TMC_OVERTEMP: SERIAL_ECHO(st.get_overtemp_prewarn_celsius()); break; + case TMC_OVERVOLT_THD: SERIAL_ECHO(st.get_overvoltage_threshold_voltage()); break; + default: break; + } + } + + #endif // TMC2240 #if HAS_DRIVER(TMC2660) static void _tmc_parse_drv_status(TMC2660Stepper, const TMC_drv_status_enum) { } static void _tmc_status(TMC2660Stepper &st, const TMC_debug_enum i) { switch (i) { - case TMC_INTERPOLATE: serialprint_truefalse(st.intpol()); break; + case TMC_INTERPOLATE: print_true_or_false(st.intpol()); break; default: break; } } #endif + template + void print_tstep(TMC &st) { + const uint32_t tstep_value = st.TSTEP(); + if (tstep_value != 0xFFFFF) + SERIAL_ECHO(tstep_value); + else + SERIAL_ECHOPGM("max"); + } + void print_tstep(TMC2660Stepper &st) { } + + template + void print_blank_time(TMC &st) { SERIAL_ECHO(st.blank_time()); } + template + void print_blank_time(TMCMarlin &) { } + template static void tmc_status(TMC &st, const TMC_debug_enum i) { SERIAL_CHAR('\t'); switch (i) { case TMC_CODES: st.printLabel(); break; - case TMC_ENABLED: serialprint_truefalse(st.isEnabled()); break; + case TMC_ENABLED: print_true_or_false(st.isEnabled()); break; case TMC_CURRENT: SERIAL_ECHO(st.getMilliamps()); break; case TMC_RMS_CURRENT: SERIAL_ECHO(st.rms_current()); break; case TMC_MAX_CURRENT: SERIAL_ECHO(p_float_t(st.rms_current() * 1.41, 0)); break; - case TMC_IRUN: - SERIAL_ECHO(st.irun()); - SERIAL_ECHOPGM("/31"); - break; - case TMC_IHOLD: - SERIAL_ECHO(st.ihold()); - SERIAL_ECHOPGM("/31"); - break; - case TMC_CS_ACTUAL: - SERIAL_ECHO(st.cs_actual()); - SERIAL_ECHOPGM("/31"); - break; + case TMC_IRUN: SERIAL_ECHO(st.irun()); SERIAL_ECHOPGM("/31"); break; + case TMC_IHOLD: SERIAL_ECHO(st.ihold()); SERIAL_ECHOPGM("/31"); break; + case TMC_CS_ACTUAL: print_cs_actual(st); break; case TMC_VSENSE: print_vsense(st); break; case TMC_MICROSTEPS: SERIAL_ECHO(st.microsteps()); break; - case TMC_TSTEP: { - const uint32_t tstep_value = st.TSTEP(); - if (tstep_value != 0xFFFFF) SERIAL_ECHO(tstep_value); else SERIAL_ECHOPGM("max"); - } break; + case TMC_TSTEP: print_tstep(st); break; #if ENABLED(HYBRID_THRESHOLD) case TMC_TPWMTHRS: SERIAL_ECHO(uint32_t(st.TPWMTHRS())); break; case TMC_TPWMTHRS_MMS: { @@ -705,12 +800,12 @@ if (tpwmthrs_val) SERIAL_ECHO(tpwmthrs_val); else SERIAL_CHAR('-'); } break; #endif - case TMC_OTPW: serialprint_truefalse(st.otpw()); break; + case TMC_DEBUG_OTPW: print_true_or_false(st.otpw()); break; #if ENABLED(MONITOR_DRIVER_STATUS) - case TMC_OTPW_TRIGGERED: serialprint_truefalse(st.getOTPW()); break; + case TMC_OTPW_TRIGGERED: print_true_or_false(st.getOTPW()); break; #endif case TMC_TOFF: SERIAL_ECHO(st.toff()); break; - case TMC_TBL: SERIAL_ECHO(st.blank_time()); break; + case TMC_TBL: print_blank_time(st); break; case TMC_HEND: SERIAL_ECHO(st.hysteresis_end()); break; case TMC_HSTRT: SERIAL_ECHO(st.hysteresis_start()); break; case TMC_MSCNT: SERIAL_ECHO(st.get_microstep_counter()); break; @@ -724,132 +819,82 @@ SERIAL_CHAR('\t'); switch (i) { case TMC_CODES: st.printLabel(); break; - case TMC_ENABLED: serialprint_truefalse(st.isEnabled()); break; + case TMC_ENABLED: print_true_or_false(st.isEnabled()); break; case TMC_CURRENT: SERIAL_ECHO(st.getMilliamps()); break; case TMC_RMS_CURRENT: SERIAL_ECHO(st.rms_current()); break; case TMC_MAX_CURRENT: SERIAL_ECHO(p_float_t(st.rms_current() * 1.41, 0)); break; - case TMC_IRUN: - SERIAL_ECHO(st.cs()); - SERIAL_ECHOPGM("/31"); - break; + case TMC_IRUN: SERIAL_ECHO(st.cs()); SERIAL_ECHOPGM("/31"); break; case TMC_VSENSE: SERIAL_ECHO(st.vsense() ? F("1=.165") : F("0=.310")); break; case TMC_MICROSTEPS: SERIAL_ECHO(st.microsteps()); break; - //case TMC_OTPW: serialprint_truefalse(st.otpw()); break; - //case TMC_OTPW_TRIGGERED: serialprint_truefalse(st.getOTPW()); break; + //case TMC_DEBUG_OTPW: print_true_or_false(st.otpw()); break; + //case TMC_OTPW_TRIGGERED: print_true_or_false(st.getOTPW()); break; case TMC_SGT: SERIAL_ECHO(st.sgt()); break; case TMC_TOFF: SERIAL_ECHO(st.toff()); break; - case TMC_TBL: SERIAL_ECHO(st.blank_time()); break; + case TMC_TBL: print_blank_time(st); break; case TMC_HEND: SERIAL_ECHO(st.hysteresis_end()); break; case TMC_HSTRT: SERIAL_ECHO(st.hysteresis_start()); break; - default: break; + default: _tmc_status(st, i); break; } } - #endif + #endif // TMC2660 template static void tmc_parse_drv_status(TMC &st, const TMC_drv_status_enum i) { SERIAL_CHAR('\t'); switch (i) { - case TMC_DRV_CODES: st.printLabel(); break; - case TMC_STST: if (!st.stst()) SERIAL_CHAR('*'); break; - case TMC_OLB: if (st.olb()) SERIAL_CHAR('*'); break; - case TMC_OLA: if (st.ola()) SERIAL_CHAR('*'); break; - case TMC_S2GB: if (st.s2gb()) SERIAL_CHAR('*'); break; - case TMC_S2GA: if (st.s2ga()) SERIAL_CHAR('*'); break; - case TMC_DRV_OTPW: if (st.otpw()) SERIAL_CHAR('*'); break; - case TMC_OT: if (st.ot()) SERIAL_CHAR('*'); break; + case TMC_DRV_CODES: st.printLabel(); break; + case TMC_STST: if (!st.stst()) SERIAL_CHAR('*'); break; + case TMC_OLB: if (st.olb()) SERIAL_CHAR('*'); break; + case TMC_OLA: if (st.ola()) SERIAL_CHAR('*'); break; + case TMC_S2GB: if (st.s2gb()) SERIAL_CHAR('*'); break; + case TMC_S2GA: if (st.s2ga()) SERIAL_CHAR('*'); break; + case TMC_DRV_OTPW: if (st.otpw()) SERIAL_CHAR('*'); break; + case TMC_OT: if (st.ot()) SERIAL_CHAR('*'); break; case TMC_DRV_STATUS_HEX: { const uint32_t drv_status = st.DRV_STATUS(); - SERIAL_CHAR('\t'); - st.printLabel(); - SERIAL_CHAR('\t'); - print_hex_long(drv_status, ':', true); + SERIAL_CHAR('\t'); st.printLabel(); + SERIAL_CHAR('\t'); print_hex_long(drv_status, ':', true); if (drv_status == 0xFFFFFFFF || drv_status == 0) SERIAL_ECHOPGM("\t Bad response!"); SERIAL_EOL(); - break; - } + } break; default: _tmc_parse_drv_status(st, i); break; } } static void tmc_debug_loop(const TMC_debug_enum n OPTARGS_LOGICAL(const bool)) { if (TERN0(HAS_X_AXIS, x)) { - #if AXIS_IS_TMC(X) - tmc_status(stepperX, n); - #endif - #if AXIS_IS_TMC(X2) - tmc_status(stepperX2, n); - #endif + TERN_(X_IS_TRINAMIC, tmc_status(stepperX, n)); + TERN_(X2_IS_TRINAMIC, tmc_status(stepperX2, n)); } if (TERN0(HAS_Y_AXIS, y)) { - #if AXIS_IS_TMC(Y) - tmc_status(stepperY, n); - #endif - #if AXIS_IS_TMC(Y2) - tmc_status(stepperY2, n); - #endif + TERN_(Y_IS_TRINAMIC, tmc_status(stepperY, n)); + TERN_(Y2_IS_TRINAMIC, tmc_status(stepperY2, n)); } if (TERN0(HAS_Z_AXIS, z)) { - #if AXIS_IS_TMC(Z) - tmc_status(stepperZ, n); - #endif - #if AXIS_IS_TMC(Z2) - tmc_status(stepperZ2, n); - #endif - #if AXIS_IS_TMC(Z3) - tmc_status(stepperZ3, n); - #endif - #if AXIS_IS_TMC(Z4) - tmc_status(stepperZ4, n); - #endif + TERN_(Z_IS_TRINAMIC, tmc_status(stepperZ, n)); + TERN_(Z2_IS_TRINAMIC, tmc_status(stepperZ2, n)); + TERN_(Z3_IS_TRINAMIC, tmc_status(stepperZ3, n)); + TERN_(Z4_IS_TRINAMIC, tmc_status(stepperZ4, n)); } - #if AXIS_IS_TMC(I) - if (i) tmc_status(stepperI, n); - #endif - #if AXIS_IS_TMC(J) - if (j) tmc_status(stepperJ, n); - #endif - #if AXIS_IS_TMC(K) - if (k) tmc_status(stepperK, n); - #endif - #if AXIS_IS_TMC(U) - if (u) tmc_status(stepperU, n); - #endif - #if AXIS_IS_TMC(V) - if (v) tmc_status(stepperV, n); - #endif - #if AXIS_IS_TMC(W) - if (w) tmc_status(stepperW, n); - #endif + TERN_(I_IS_TRINAMIC, if (i) tmc_status(stepperI, n)); + TERN_(J_IS_TRINAMIC, if (j) tmc_status(stepperJ, n)); + TERN_(K_IS_TRINAMIC, if (k) tmc_status(stepperK, n)); + TERN_(U_IS_TRINAMIC, if (u) tmc_status(stepperU, n)); + TERN_(V_IS_TRINAMIC, if (v) tmc_status(stepperV, n)); + TERN_(W_IS_TRINAMIC, if (w) tmc_status(stepperW, n)); if (TERN0(HAS_EXTRUDERS, e)) { - #if AXIS_IS_TMC(E0) - tmc_status(stepperE0, n); - #endif - #if AXIS_IS_TMC(E1) - tmc_status(stepperE1, n); - #endif - #if AXIS_IS_TMC(E2) - tmc_status(stepperE2, n); - #endif - #if AXIS_IS_TMC(E3) - tmc_status(stepperE3, n); - #endif - #if AXIS_IS_TMC(E4) - tmc_status(stepperE4, n); - #endif - #if AXIS_IS_TMC(E5) - tmc_status(stepperE5, n); - #endif - #if AXIS_IS_TMC(E6) - tmc_status(stepperE6, n); - #endif - #if AXIS_IS_TMC(E7) - tmc_status(stepperE7, n); - #endif + TERN_(E0_IS_TRINAMIC, tmc_status(stepperE0, n)); + TERN_(E1_IS_TRINAMIC, tmc_status(stepperE1, n)); + TERN_(E2_IS_TRINAMIC, tmc_status(stepperE2, n)); + TERN_(E3_IS_TRINAMIC, tmc_status(stepperE3, n)); + TERN_(E4_IS_TRINAMIC, tmc_status(stepperE4, n)); + TERN_(E5_IS_TRINAMIC, tmc_status(stepperE5, n)); + TERN_(E6_IS_TRINAMIC, tmc_status(stepperE6, n)); + TERN_(E7_IS_TRINAMIC, tmc_status(stepperE7, n)); } SERIAL_EOL(); @@ -857,82 +902,38 @@ static void drv_status_loop(const TMC_drv_status_enum n OPTARGS_LOGICAL(const bool)) { if (TERN0(HAS_X_AXIS, x)) { - #if AXIS_IS_TMC(X) - tmc_parse_drv_status(stepperX, n); - #endif - #if AXIS_IS_TMC(X2) - tmc_parse_drv_status(stepperX2, n); - #endif + TERN_(X_IS_TRINAMIC, tmc_parse_drv_status(stepperX, n)); + TERN_(X2_IS_TRINAMIC, tmc_parse_drv_status(stepperX2, n)); } if (TERN0(HAS_Y_AXIS, y)) { - #if AXIS_IS_TMC(Y) - tmc_parse_drv_status(stepperY, n); - #endif - #if AXIS_IS_TMC(Y2) - tmc_parse_drv_status(stepperY2, n); - #endif + TERN_(Y_IS_TRINAMIC, tmc_parse_drv_status(stepperY, n)); + TERN_(Y2_IS_TRINAMIC, tmc_parse_drv_status(stepperY2, n)); } if (TERN0(HAS_Z_AXIS, z)) { - #if AXIS_IS_TMC(Z) - tmc_parse_drv_status(stepperZ, n); - #endif - #if AXIS_IS_TMC(Z2) - tmc_parse_drv_status(stepperZ2, n); - #endif - #if AXIS_IS_TMC(Z3) - tmc_parse_drv_status(stepperZ3, n); - #endif - #if AXIS_IS_TMC(Z4) - tmc_parse_drv_status(stepperZ4, n); - #endif + TERN_(Z_IS_TRINAMIC, tmc_parse_drv_status(stepperZ, n)); + TERN_(Z2_IS_TRINAMIC, tmc_parse_drv_status(stepperZ2, n)); + TERN_(Z3_IS_TRINAMIC, tmc_parse_drv_status(stepperZ3, n)); + TERN_(Z4_IS_TRINAMIC, tmc_parse_drv_status(stepperZ4, n)); } - #if AXIS_IS_TMC(I) - if (i) tmc_parse_drv_status(stepperI, n); - #endif - #if AXIS_IS_TMC(J) - if (j) tmc_parse_drv_status(stepperJ, n); - #endif - #if AXIS_IS_TMC(K) - if (k) tmc_parse_drv_status(stepperK, n); - #endif - #if AXIS_IS_TMC(U) - if (u) tmc_parse_drv_status(stepperU, n); - #endif - #if AXIS_IS_TMC(V) - if (v) tmc_parse_drv_status(stepperV, n); - #endif - #if AXIS_IS_TMC(W) - if (w) tmc_parse_drv_status(stepperW, n); - #endif + TERN_(I_IS_TRINAMIC, if (i) tmc_parse_drv_status(stepperI, n)); + TERN_(J_IS_TRINAMIC, if (j) tmc_parse_drv_status(stepperJ, n)); + TERN_(K_IS_TRINAMIC, if (k) tmc_parse_drv_status(stepperK, n)); + TERN_(U_IS_TRINAMIC, if (u) tmc_parse_drv_status(stepperU, n)); + TERN_(V_IS_TRINAMIC, if (v) tmc_parse_drv_status(stepperV, n)); + TERN_(W_IS_TRINAMIC, if (w) tmc_parse_drv_status(stepperW, n)); if (TERN0(HAS_EXTRUDERS, e)) { - #if AXIS_IS_TMC(E0) - tmc_parse_drv_status(stepperE0, n); - #endif - #if AXIS_IS_TMC(E1) - tmc_parse_drv_status(stepperE1, n); - #endif - #if AXIS_IS_TMC(E2) - tmc_parse_drv_status(stepperE2, n); - #endif - #if AXIS_IS_TMC(E3) - tmc_parse_drv_status(stepperE3, n); - #endif - #if AXIS_IS_TMC(E4) - tmc_parse_drv_status(stepperE4, n); - #endif - #if AXIS_IS_TMC(E5) - tmc_parse_drv_status(stepperE5, n); - #endif - #if AXIS_IS_TMC(E6) - tmc_parse_drv_status(stepperE6, n); - #endif - #if AXIS_IS_TMC(E7) - tmc_parse_drv_status(stepperE7, n); - #endif + TERN_(E0_IS_TRINAMIC, tmc_parse_drv_status(stepperE0, n)); + TERN_(E1_IS_TRINAMIC, tmc_parse_drv_status(stepperE1, n)); + TERN_(E2_IS_TRINAMIC, tmc_parse_drv_status(stepperE2, n)); + TERN_(E3_IS_TRINAMIC, tmc_parse_drv_status(stepperE3, n)); + TERN_(E4_IS_TRINAMIC, tmc_parse_drv_status(stepperE4, n)); + TERN_(E5_IS_TRINAMIC, tmc_parse_drv_status(stepperE5, n)); + TERN_(E6_IS_TRINAMIC, tmc_parse_drv_status(stepperE6, n)); + TERN_(E7_IS_TRINAMIC, tmc_parse_drv_status(stepperE7, n)); } SERIAL_EOL(); @@ -942,7 +943,7 @@ * M122 report functions */ - void tmc_report_all(LOGICAL_AXIS_ARGS(const bool)) { + void tmc_report_all(LOGICAL_AXIS_ARGS_LC(const bool)) { #define TMC_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); tmc_debug_loop(ITEM OPTARGS_LOGICAL()); }while(0) #define DRV_REPORT(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); drv_status_loop(ITEM OPTARGS_LOGICAL()); }while(0) @@ -970,16 +971,16 @@ TMC_REPORT("tstep\t", TMC_TSTEP); TMC_REPORT("PWM thresh.", TMC_TPWMTHRS); TMC_REPORT("[mm/s]\t", TMC_TPWMTHRS_MMS); - TMC_REPORT("OT prewarn", TMC_OTPW); + TMC_REPORT("OT prewarn", TMC_DEBUG_OTPW); #if ENABLED(MONITOR_DRIVER_STATUS) - TMC_REPORT("triggered\n OTP\t", TMC_OTPW_TRIGGERED); + TMC_REPORT("OTPW trig.\t", TMC_OTPW_TRIGGERED); #endif #if HAS_TMC220x - TMC_REPORT("pwm scale sum", TMC_PWM_SCALE_SUM); - TMC_REPORT("pwm scale auto", TMC_PWM_SCALE_AUTO); - TMC_REPORT("pwm offset auto", TMC_PWM_OFS_AUTO); - TMC_REPORT("pwm grad auto", TMC_PWM_GRAD_AUTO); + TMC_REPORT("pwm scale sum", TMC_PWM_SCALE_SUM); + TMC_REPORT("pwm scale auto", TMC_PWM_SCALE_AUTO); + TMC_REPORT("pwm offset auto", TMC_PWM_OFS_AUTO); + TMC_REPORT("pwm grad auto", TMC_PWM_GRAD_AUTO); #endif TMC_REPORT("off time", TMC_TOFF); @@ -988,11 +989,12 @@ TMC_REPORT(" -start\t", TMC_HSTRT); TMC_REPORT("Stallguard thrs", TMC_SGT); TMC_REPORT("uStep count", TMC_MSCNT); + DRV_REPORT("DRVSTATUS", TMC_DRV_CODES); - #if HAS_TMCX1X0 || HAS_TMC220x + #if HAS_TMCX1X0_OR_2240 || HAS_TMC220x DRV_REPORT("sg_result", TMC_SG_RESULT); #endif - #if HAS_TMCX1X0 + #if HAS_TMCX1X0_OR_2240 DRV_REPORT("stallguard", TMC_STALLGUARD); DRV_REPORT("fsactive", TMC_FSACTIVE); #endif @@ -1008,30 +1010,40 @@ DRV_REPORT("150C\t", TMC_T150); DRV_REPORT("143C\t", TMC_T143); DRV_REPORT("120C\t", TMC_T120); + #endif + #if HAS_TMC220x || HAS_DRIVER(TMC2240) DRV_REPORT("s2vsa\t", TMC_S2VSA); DRV_REPORT("s2vsb\t", TMC_S2VSB); #endif - DRV_REPORT("Driver registers:\n",TMC_DRV_STATUS_HEX); + DRV_REPORT("Driver registers:\n", TMC_DRV_STATUS_HEX); + #if HAS_DRIVER(TMC2240) + TMC_REPORT("Analog in (v)", TMC_VAIN); + TMC_REPORT("Supply (v)", TMC_VSUPPLY); + TMC_REPORT("Temp (°C)", TMC_TEMP); + TMC_REPORT("OT pre warn (°C)", TMC_OVERTEMP); + TMC_REPORT("OV threshold (v)", TMC_OVERVOLT_THD); + #endif SERIAL_EOL(); } #define PRINT_TMC_REGISTER(REG_CASE) case TMC_GET_##REG_CASE: print_hex_long(st.REG_CASE(), ':'); break - #if HAS_TMCX1X0 - static void tmc_get_ic_registers(TMC2130Stepper &st, const TMC_get_registers_enum i) { - switch (i) { - PRINT_TMC_REGISTER(TCOOLTHRS); - PRINT_TMC_REGISTER(THIGH); - PRINT_TMC_REGISTER(COOLCONF); - default: SERIAL_CHAR('\t'); break; - } - } - #endif - #if HAS_TMC220x - static void tmc_get_ic_registers(TMC2208Stepper, const TMC_get_registers_enum) { SERIAL_CHAR('\t'); } - #endif - #if HAS_TRINAMIC_CONFIG + + template + static void tmc_get_ic_registers(TMC &, const TMC_get_registers_enum) { SERIAL_CHAR('\t'); } + + #if HAS_TMCX1X0 + static void tmc_get_ic_registers(TMC2130Stepper &st, const TMC_get_registers_enum i) { + switch (i) { + PRINT_TMC_REGISTER(TCOOLTHRS); + PRINT_TMC_REGISTER(THIGH); + PRINT_TMC_REGISTER(COOLCONF); + default: SERIAL_CHAR('\t'); break; + } + } + #endif + template static void tmc_get_registers(TMC &st, const TMC_get_registers_enum i) { switch (i) { @@ -1051,7 +1063,9 @@ } SERIAL_CHAR('\t'); } - #endif + + #endif // HAS_TRINAMIC_CONFIG + #if HAS_DRIVER(TMC2660) template static void tmc_get_registers(TMCMarlin &st, const TMC_get_registers_enum i) { @@ -1071,88 +1085,44 @@ static void tmc_get_registers(TMC_get_registers_enum n OPTARGS_LOGICAL(const bool)) { if (TERN0(HAS_X_AXIS, x)) { - #if AXIS_IS_TMC(X) - tmc_get_registers(stepperX, n); - #endif - #if AXIS_IS_TMC(X2) - tmc_get_registers(stepperX2, n); - #endif + TERN_(X_IS_TRINAMIC, tmc_get_registers(stepperX, n)); + TERN_(X2_IS_TRINAMIC, tmc_get_registers(stepperX2, n)); } if (TERN0(HAS_Y_AXIS, y)) { - #if AXIS_IS_TMC(Y) - tmc_get_registers(stepperY, n); - #endif - #if AXIS_IS_TMC(Y2) - tmc_get_registers(stepperY2, n); - #endif + TERN_(Y_IS_TRINAMIC, tmc_get_registers(stepperY, n)); + TERN_(Y2_IS_TRINAMIC, tmc_get_registers(stepperY2, n)); } if (TERN0(HAS_Z_AXIS, z)) { - #if AXIS_IS_TMC(Z) - tmc_get_registers(stepperZ, n); - #endif - #if AXIS_IS_TMC(Z2) - tmc_get_registers(stepperZ2, n); - #endif - #if AXIS_IS_TMC(Z3) - tmc_get_registers(stepperZ3, n); - #endif - #if AXIS_IS_TMC(Z4) - tmc_get_registers(stepperZ4, n); - #endif + TERN_(Z_IS_TRINAMIC, tmc_get_registers(stepperZ, n)); + TERN_(Z2_IS_TRINAMIC, tmc_get_registers(stepperZ2, n)); + TERN_(Z3_IS_TRINAMIC, tmc_get_registers(stepperZ3, n)); + TERN_(Z4_IS_TRINAMIC, tmc_get_registers(stepperZ4, n)); } - #if AXIS_IS_TMC(I) - if (i) tmc_get_registers(stepperI, n); - #endif - #if AXIS_IS_TMC(J) - if (j) tmc_get_registers(stepperJ, n); - #endif - #if AXIS_IS_TMC(K) - if (k) tmc_get_registers(stepperK, n); - #endif - #if AXIS_IS_TMC(U) - if (u) tmc_get_registers(stepperU, n); - #endif - #if AXIS_IS_TMC(V) - if (v) tmc_get_registers(stepperV, n); - #endif - #if AXIS_IS_TMC(W) - if (w) tmc_get_registers(stepperW, n); - #endif + TERN_(I_IS_TRINAMIC, if (i) tmc_get_registers(stepperI, n)); + TERN_(J_IS_TRINAMIC, if (j) tmc_get_registers(stepperJ, n)); + TERN_(K_IS_TRINAMIC, if (k) tmc_get_registers(stepperK, n)); + TERN_(U_IS_TRINAMIC, if (u) tmc_get_registers(stepperU, n)); + TERN_(V_IS_TRINAMIC, if (v) tmc_get_registers(stepperV, n)); + TERN_(W_IS_TRINAMIC, if (w) tmc_get_registers(stepperW, n)); if (TERN0(HAS_EXTRUDERS, e)) { - #if AXIS_IS_TMC(E0) - tmc_get_registers(stepperE0, n); - #endif - #if AXIS_IS_TMC(E1) - tmc_get_registers(stepperE1, n); - #endif - #if AXIS_IS_TMC(E2) - tmc_get_registers(stepperE2, n); - #endif - #if AXIS_IS_TMC(E3) - tmc_get_registers(stepperE3, n); - #endif - #if AXIS_IS_TMC(E4) - tmc_get_registers(stepperE4, n); - #endif - #if AXIS_IS_TMC(E5) - tmc_get_registers(stepperE5, n); - #endif - #if AXIS_IS_TMC(E6) - tmc_get_registers(stepperE6, n); - #endif - #if AXIS_IS_TMC(E7) - tmc_get_registers(stepperE7, n); - #endif + TERN_(E0_IS_TRINAMIC, tmc_get_registers(stepperE0, n)); + TERN_(E1_IS_TRINAMIC, tmc_get_registers(stepperE1, n)); + TERN_(E2_IS_TRINAMIC, tmc_get_registers(stepperE2, n)); + TERN_(E3_IS_TRINAMIC, tmc_get_registers(stepperE3, n)); + TERN_(E4_IS_TRINAMIC, tmc_get_registers(stepperE4, n)); + TERN_(E5_IS_TRINAMIC, tmc_get_registers(stepperE5, n)); + TERN_(E6_IS_TRINAMIC, tmc_get_registers(stepperE6, n)); + TERN_(E7_IS_TRINAMIC, tmc_get_registers(stepperE7, n)); } SERIAL_EOL(); } - void tmc_get_registers(LOGICAL_AXIS_ARGS(bool)) { + void tmc_get_registers(LOGICAL_AXIS_ARGS_LC(bool)) { #define _TMC_GET_REG(LABEL, ITEM) do{ SERIAL_ECHOPGM(LABEL); tmc_get_registers(ITEM OPTARGS_LOGICAL()); }while(0) #define TMC_GET_REG(NAME, TABS) _TMC_GET_REG(STRINGIFY(NAME) TABS, TMC_GET_##NAME) _TMC_GET_REG("\t", TMC_AXIS_CODES); @@ -1203,11 +1173,29 @@ st.TCOOLTHRS(0); } + bool tmc_enable_stallguard(TMC2240Stepper &st) { + const bool stealthchop_was_enabled = st.en_pwm_mode(); + + // TODO: Use StallGuard4 when stealthChop is enabled + // and leave stealthChop state unchanged. + + st.TCOOLTHRS(0xFFFFF); + st.en_pwm_mode(false); + st.diag0_stall(true); + + return stealthchop_was_enabled; + } + void tmc_disable_stallguard(TMC2240Stepper &st, const bool restore_stealth) { + st.TCOOLTHRS(0); + st.en_pwm_mode(restore_stealth); + st.diag0_stall(false); + } + bool tmc_enable_stallguard(TMC2660Stepper) { // TODO return false; } - void tmc_disable_stallguard(TMC2660Stepper, const bool) {}; + void tmc_disable_stallguard(TMC2660Stepper, const bool) { } #endif // USE_SENSORLESS @@ -1232,161 +1220,45 @@ static bool test_connection(TMC &st) { return test_result; } -void test_tmc_connection(LOGICAL_AXIS_ARGS(const bool)) { +void test_tmc_connection(LOGICAL_AXIS_ARGS_LC(const bool)) { uint8_t axis_connection = 0; if (TERN0(HAS_X_AXIS, x)) { - #if AXIS_IS_TMC(X) - axis_connection += test_connection(stepperX); - #endif - #if AXIS_IS_TMC(X2) - axis_connection += test_connection(stepperX2); - #endif + TERN_(X_IS_TRINAMIC, axis_connection += test_connection(stepperX)); + TERN_(X2_IS_TRINAMIC, axis_connection += test_connection(stepperX2)); } if (TERN0(HAS_Y_AXIS, y)) { - #if AXIS_IS_TMC(Y) - axis_connection += test_connection(stepperY); - #endif - #if AXIS_IS_TMC(Y2) - axis_connection += test_connection(stepperY2); - #endif + TERN_(Y_IS_TRINAMIC, axis_connection += test_connection(stepperY)); + TERN_(Y2_IS_TRINAMIC, axis_connection += test_connection(stepperY2)); } if (TERN0(HAS_Z_AXIS, z)) { - #if AXIS_IS_TMC(Z) - axis_connection += test_connection(stepperZ); - #endif - #if AXIS_IS_TMC(Z2) - axis_connection += test_connection(stepperZ2); - #endif - #if AXIS_IS_TMC(Z3) - axis_connection += test_connection(stepperZ3); - #endif - #if AXIS_IS_TMC(Z4) - axis_connection += test_connection(stepperZ4); - #endif + TERN_(Z_IS_TRINAMIC, axis_connection += test_connection(stepperZ)); + TERN_(Z2_IS_TRINAMIC, axis_connection += test_connection(stepperZ2)); + TERN_(Z3_IS_TRINAMIC, axis_connection += test_connection(stepperZ3)); + TERN_(Z4_IS_TRINAMIC, axis_connection += test_connection(stepperZ4)); } - #if AXIS_IS_TMC(I) - if (i) axis_connection += test_connection(stepperI); - #endif - #if AXIS_IS_TMC(J) - if (j) axis_connection += test_connection(stepperJ); - #endif - #if AXIS_IS_TMC(K) - if (k) axis_connection += test_connection(stepperK); - #endif - #if AXIS_IS_TMC(U) - if (u) axis_connection += test_connection(stepperU); - #endif - #if AXIS_IS_TMC(V) - if (v) axis_connection += test_connection(stepperV); - #endif - #if AXIS_IS_TMC(W) - if (w) axis_connection += test_connection(stepperW); - #endif + TERN_(I_IS_TRINAMIC, if (i) axis_connection += test_connection(stepperI)); + TERN_(J_IS_TRINAMIC, if (j) axis_connection += test_connection(stepperJ)); + TERN_(K_IS_TRINAMIC, if (k) axis_connection += test_connection(stepperK)); + TERN_(U_IS_TRINAMIC, if (u) axis_connection += test_connection(stepperU)); + TERN_(V_IS_TRINAMIC, if (v) axis_connection += test_connection(stepperV)); + TERN_(W_IS_TRINAMIC, if (w) axis_connection += test_connection(stepperW)); if (TERN0(HAS_EXTRUDERS, e)) { - #if AXIS_IS_TMC(E0) - axis_connection += test_connection(stepperE0); - #endif - #if AXIS_IS_TMC(E1) - axis_connection += test_connection(stepperE1); - #endif - #if AXIS_IS_TMC(E2) - axis_connection += test_connection(stepperE2); - #endif - #if AXIS_IS_TMC(E3) - axis_connection += test_connection(stepperE3); - #endif - #if AXIS_IS_TMC(E4) - axis_connection += test_connection(stepperE4); - #endif - #if AXIS_IS_TMC(E5) - axis_connection += test_connection(stepperE5); - #endif - #if AXIS_IS_TMC(E6) - axis_connection += test_connection(stepperE6); - #endif - #if AXIS_IS_TMC(E7) - axis_connection += test_connection(stepperE7); - #endif + TERN_(E0_IS_TRINAMIC, axis_connection += test_connection(stepperE0)); + TERN_(E1_IS_TRINAMIC, axis_connection += test_connection(stepperE1)); + TERN_(E2_IS_TRINAMIC, axis_connection += test_connection(stepperE2)); + TERN_(E3_IS_TRINAMIC, axis_connection += test_connection(stepperE3)); + TERN_(E4_IS_TRINAMIC, axis_connection += test_connection(stepperE4)); + TERN_(E5_IS_TRINAMIC, axis_connection += test_connection(stepperE5)); + TERN_(E6_IS_TRINAMIC, axis_connection += test_connection(stepperE6)); + TERN_(E7_IS_TRINAMIC, axis_connection += test_connection(stepperE7)); } if (axis_connection) LCD_MESSAGE(MSG_ERROR_TMC); } #endif // HAS_TRINAMIC_CONFIG - -#if HAS_TMC_SPI - #define SET_CS_PIN(st) OUT_WRITE(st##_CS_PIN, HIGH) - void tmc_init_cs_pins() { - #if AXIS_HAS_SPI(X) - SET_CS_PIN(X); - #endif - #if AXIS_HAS_SPI(Y) - SET_CS_PIN(Y); - #endif - #if AXIS_HAS_SPI(Z) - SET_CS_PIN(Z); - #endif - #if AXIS_HAS_SPI(X2) - SET_CS_PIN(X2); - #endif - #if AXIS_HAS_SPI(Y2) - SET_CS_PIN(Y2); - #endif - #if AXIS_HAS_SPI(Z2) - SET_CS_PIN(Z2); - #endif - #if AXIS_HAS_SPI(Z3) - SET_CS_PIN(Z3); - #endif - #if AXIS_HAS_SPI(Z4) - SET_CS_PIN(Z4); - #endif - #if AXIS_HAS_SPI(I) - SET_CS_PIN(I); - #endif - #if AXIS_HAS_SPI(J) - SET_CS_PIN(J); - #endif - #if AXIS_HAS_SPI(K) - SET_CS_PIN(K); - #endif - #if AXIS_HAS_SPI(U) - SET_CS_PIN(U); - #endif - #if AXIS_HAS_SPI(V) - SET_CS_PIN(V); - #endif - #if AXIS_HAS_SPI(W) - SET_CS_PIN(W); - #endif - #if AXIS_HAS_SPI(E0) - SET_CS_PIN(E0); - #endif - #if AXIS_HAS_SPI(E1) - SET_CS_PIN(E1); - #endif - #if AXIS_HAS_SPI(E2) - SET_CS_PIN(E2); - #endif - #if AXIS_HAS_SPI(E3) - SET_CS_PIN(E3); - #endif - #if AXIS_HAS_SPI(E4) - SET_CS_PIN(E4); - #endif - #if AXIS_HAS_SPI(E5) - SET_CS_PIN(E5); - #endif - #if AXIS_HAS_SPI(E6) - SET_CS_PIN(E6); - #endif - #if AXIS_HAS_SPI(E7) - SET_CS_PIN(E7); - #endif - } -#endif // HAS_TMC_SPI diff --git a/Marlin/src/feature/tmc_util.h b/Marlin/src/feature/tmc_util.h index 7ed070c9b8..556035f08c 100644 --- a/Marlin/src/feature/tmc_util.h +++ b/Marlin/src/feature/tmc_util.h @@ -29,7 +29,7 @@ #include #include "../module/planner.h" -#define CHOPPER_DEFAULT_12V { 3, -1, 1 } +#define CHOPPER_DEFAULT_12V { 3, -1, 1 } // { toff, hend, hstrt } #define CHOPPER_DEFAULT_19V { 4, 1, 1 } #define CHOPPER_DEFAULT_24V { 4, 2, 1 } #define CHOPPER_DEFAULT_36V { 5, 2, 4 } @@ -77,8 +77,8 @@ class TMCStorage { struct { OPTCODE(HAS_STEALTHCHOP, bool stealthChop_enabled = false) - OPTCODE(HYBRID_THRESHOLD, uint8_t hybrid_thrs = 0) - OPTCODE(USE_SENSORLESS, int16_t homing_thrs = 0) + OPTCODE(HYBRID_THRESHOLD, uint16_t hybrid_thrs = 0) + OPTCODE(USE_SENSORLESS, int16_t homing_thrs = 0) } stored; }; @@ -95,7 +95,7 @@ class TMCMarlin : public TMC, public TMCStorage { TMC(CS, RS, pinMOSI, pinMISO, pinSCK) {} TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t axis_chain_index) : - TMC(CS, RS, pinMOSI, pinMISO, pinSCK, axis_chain_index) + TMC(CS, RS, pinMOSI, pinMISO, pinSCK, axis_chain_index) {} uint16_t rms_current() { return TMC::rms_current(); } void rms_current(uint16_t mA) { @@ -124,7 +124,7 @@ class TMCMarlin : public TMC, public TMCStorage { #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { - return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); + return _tmc_thrs(this->microsteps(), TMC::TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); } void set_pwm_thrs(const uint32_t thrs) { TMC::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID])); @@ -197,7 +197,7 @@ class TMCMarlin : public TMC220 #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { - return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); + return _tmc_thrs(this->microsteps(), TMC2208Stepper::TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); } void set_pwm_thrs(const uint32_t thrs) { TMC2208Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID])); @@ -249,13 +249,14 @@ class TMCMarlin : public TMC220 #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { - return _tmc_thrs(this->microsteps(), this->TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); + return _tmc_thrs(this->microsteps(), TMC2209Stepper::TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); } void set_pwm_thrs(const uint32_t thrs) { TMC2209Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID])); TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs); } #endif + #if USE_SENSORLESS int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); } void homing_threshold(int16_t sgt_val) { @@ -278,6 +279,74 @@ class TMCMarlin : public TMC220 sgt_max = 255; }; +template +class TMCMarlin : public TMC2240Stepper, public TMCStorage { + public: + TMCMarlin(const uint16_t cs_pin, const uint8_t axis_chain_index) : + TMC2240Stepper(cs_pin, axis_chain_index) + {} + TMCMarlin(const uint16_t CS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t axis_chain_index) : + TMC2240Stepper(CS, pinMOSI, pinMISO, pinSCK, axis_chain_index ) + {} + + //uint8_t get_address() { return slave_address; } + uint16_t get_microstep_counter() { return microsteps(); } + + uint16_t rms_current() { return TMC2240Stepper::rms_current(); } + void rms_current(const uint16_t mA) { + this->val_mA = mA; + TMC2240Stepper::rms_current(mA); + } + void rms_current(const uint16_t mA, const float mult) { + this->val_mA = mA; + TMC2240Stepper::rms_current(mA, mult); + } + + #if HAS_STEALTHCHOP + bool get_stealthChop() { return this->en_pwm_mode(); } + bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } + void refresh_stepping_mode() { this->en_pwm_mode(this->stored.stealthChop_enabled); } + void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); } + bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); } + #endif + + void set_chopper_times(const chopper_timing_t &ct) { + TMC2240Stepper::toff(ct.toff); + TMC2240Stepper::hysteresis_end(ct.hend); + TMC2240Stepper::hysteresis_start(ct.hstrt); + } + + #if ENABLED(HYBRID_THRESHOLD) + uint32_t get_pwm_thrs() { + return _tmc_thrs(this->microsteps(), TMC2240Stepper::TPWMTHRS(), planner.settings.axis_steps_per_mm[AXIS_ID]); + } + void set_pwm_thrs(const uint32_t thrs) { + TMC2240Stepper::TPWMTHRS(_tmc_thrs(this->microsteps(), thrs, planner.settings.axis_steps_per_mm[AXIS_ID])); + TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs); + } + #endif + + #if USE_SENSORLESS + int16_t homing_threshold() { return TMC2240Stepper::sgt(); } + void homing_threshold(int16_t sgt_val) { + sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max); + TMC2240Stepper::sgt(sgt_val); + TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val); + } + #endif + + void refresh_stepper_current() { rms_current(this->val_mA); } + #if ENABLED(HYBRID_THRESHOLD) + void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } + #endif + #if USE_SENSORLESS + void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } + #endif + + static constexpr int8_t sgt_min = -64, + sgt_max = 63; +}; + template class TMCMarlin : public TMC2660Stepper, public TMCStorage { public: @@ -320,14 +389,14 @@ class TMCMarlin : public TMC266 }; void monitor_tmc_drivers(); -void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); +void test_tmc_connection(LOGICAL_AXIS_DECL_LC(const bool, true)); #if ENABLED(TMC_DEBUG) #if ENABLED(MONITOR_DRIVER_STATUS) void tmc_set_report_interval(const uint16_t update_interval); #endif - void tmc_report_all(LOGICAL_AXIS_DECL(const bool, true)); - void tmc_get_registers(LOGICAL_AXIS_ARGS(const bool)); + void tmc_report_all(LOGICAL_AXIS_DECL_LC(const bool, true)); + void tmc_get_registers(LOGICAL_AXIS_ARGS_LC(const bool)); #endif /** @@ -353,6 +422,9 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); bool tmc_enable_stallguard(TMC2209Stepper &st); void tmc_disable_stallguard(TMC2209Stepper &st, const bool restore_stealth); + bool tmc_enable_stallguard(TMC2240Stepper &st); + void tmc_disable_stallguard(TMC2240Stepper &st, const bool restore_stealth); + bool tmc_enable_stallguard(TMC2660Stepper); void tmc_disable_stallguard(TMC2660Stepper, const bool); @@ -362,7 +434,7 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); bool TMCMarlin::test_stall_status() { this->switchCSpin(LOW); - // read stallGuard flag from TMC library, will handle HW and SW SPI + // Read stallGuard flag from TMC library, will handle HW and SW SPI TMC2130_n::DRV_STATUS_t drv_status{0}; drv_status.sr = this->DRV_STATUS(); @@ -375,8 +447,30 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); #endif // USE_SENSORLESS -#endif // HAS_TRINAMIC_CONFIG +#if HAS_HOMING_CURRENT -#if HAS_TMC_SPI - void tmc_init_cs_pins(); -#endif + // Axes that have a distinct homing current + struct homing_current_t { + OPTCODE(X_HAS_HOME_CURRENT, uint16_t X) + OPTCODE(Y_HAS_HOME_CURRENT, uint16_t Y) + OPTCODE(Z_HAS_HOME_CURRENT, uint16_t Z) + OPTCODE(X2_HAS_HOME_CURRENT, uint16_t X2) + OPTCODE(Y2_HAS_HOME_CURRENT, uint16_t Y2) + OPTCODE(Z2_HAS_HOME_CURRENT, uint16_t Z2) + OPTCODE(Z3_HAS_HOME_CURRENT, uint16_t Z3) + OPTCODE(Z4_HAS_HOME_CURRENT, uint16_t Z4) + OPTCODE(I_HAS_HOME_CURRENT, uint16_t I) + OPTCODE(J_HAS_HOME_CURRENT, uint16_t J) + OPTCODE(K_HAS_HOME_CURRENT, uint16_t K) + OPTCODE(U_HAS_HOME_CURRENT, uint16_t U) + OPTCODE(V_HAS_HOME_CURRENT, uint16_t V) + OPTCODE(W_HAS_HOME_CURRENT, uint16_t W) + }; + + #if ENABLED(EDITABLE_HOMING_CURRENT) + extern homing_current_t homing_current_mA; + #endif + +#endif // HAS_HOMING_CURRENT + +#endif // HAS_TRINAMIC_CONFIG diff --git a/Marlin/src/feature/tramming.h b/Marlin/src/feature/tramming.h index c8f20f0010..de4c6c020c 100644 --- a/Marlin/src/feature/tramming.h +++ b/Marlin/src/feature/tramming.h @@ -24,9 +24,16 @@ #include "../inc/MarlinConfig.h" #include "../module/probe.h" -#if !WITHIN(TRAMMING_SCREW_THREAD, 30, 51) || TRAMMING_SCREW_THREAD % 10 > 1 - #error "TRAMMING_SCREW_THREAD must be equal to 30, 31, 40, 41, 50, or 51." -#endif +enum TrammingThread : uint8_t { + M3_CW = 30, M3_CCW = 31, + M4_CW = 40, M4_CCW = 41, + M5_CW = 50, M5_CCW = 51 +}; + +static_assert( + TRAMMING_SCREW_THREAD < 60 && TRAMMING_SCREW_THREAD % 10 < 2, + "TRAMMING_SCREW_THREAD must be M3_CW, M3_CCW, M4_CW, M4_CCW, M5_CW, or M5_CCW." +); constexpr xy_pos_t tramming_points[] = TRAMMING_POINT_XY; diff --git a/Marlin/src/feature/twibus.h b/Marlin/src/feature/twibus.h index de23abbed5..b438d10a33 100644 --- a/Marlin/src/feature/twibus.h +++ b/Marlin/src/feature/twibus.h @@ -64,7 +64,7 @@ class TWIBus { private: /** * @brief Number of bytes on buffer - * @description Number of bytes in the buffer waiting to be flushed to the bus + * @details Number of bytes in the buffer waiting to be flushed to the bus */ uint8_t buffer_s = 0; @@ -77,7 +77,7 @@ class TWIBus { public: /** * @brief Target device address - * @description The target device address. Persists until changed. + * @details The target device address. Persists until changed. */ uint8_t addr = 0; diff --git a/Marlin/src/feature/x_twist.cpp b/Marlin/src/feature/x_twist.cpp index b8f7e52ab6..6b155c49ab 100644 --- a/Marlin/src/feature/x_twist.cpp +++ b/Marlin/src/feature/x_twist.cpp @@ -30,7 +30,7 @@ XATC xatc; bool XATC::enabled; float XATC::spacing, XATC::start; -xatc_array_t XATC::z_offset; // Initialized by settings.load() +xatc_array_t XATC::z_offset; // Initialized by settings.load void XATC::reset() { constexpr float xzo[] = XATC_Z_OFFSETS; @@ -53,7 +53,7 @@ void XATC::print_points() { SERIAL_EOL(); } -float lerp(const_float_t t, const_float_t a, const_float_t b) { return a + t * (b - a); } +float lerp(const float t, const float a, const float b) { return a + t * (b - a); } float XATC::compensation(const xy_pos_t &raw) { if (!enabled) return 0; diff --git a/Marlin/src/gcode/bedlevel/G26.cpp b/Marlin/src/gcode/bedlevel/G26.cpp index ab940c65ac..ba4647e6f5 100644 --- a/Marlin/src/gcode/bedlevel/G26.cpp +++ b/Marlin/src/gcode/bedlevel/G26.cpp @@ -58,10 +58,10 @@ * * L # Layer Layer height. (Height of nozzle above bed) If not specified .20mm will be used. * - * O # Ooooze How much your nozzle will Ooooze filament while getting in position to print. This - * is over kill, but using this parameter will let you get the very first 'circle' perfect - * so you have a trophy to peel off of the bed and hang up to show how perfectly you have your - * Mesh calibrated. If not specified, a filament length of .3mm is assumed. + * O # Ooze How much your nozzle will Ooooze filament while getting in position to print. If not + * specified, a filament length of .3mm is assumed. This might be overkill, but this + * parameter ensures the very first 'circle' is perfect (providing an ideal trophy to hang + * up to show off your perfectly calibrated Mesh). * * P # Prime Prime the nozzle with specified length of filament. If this parameter is not * given, no prime action will take place. If the parameter specifies an amount, that much @@ -102,7 +102,7 @@ #define G26_OK false #define G26_ERR true -#include "../../gcode/gcode.h" +#include "../gcode.h" #include "../../feature/bedlevel/bedlevel.h" #include "../../MarlinCore.h" @@ -132,11 +132,11 @@ #endif #ifndef G26_XY_FEEDRATE - #define G26_XY_FEEDRATE (PLANNER_XY_FEEDRATE() / 3.0) + #define G26_XY_FEEDRATE (PLANNER_XY_FEEDRATE_MM_S / 3.0) #endif #ifndef G26_XY_FEEDRATE_TRAVEL - #define G26_XY_FEEDRATE_TRAVEL (PLANNER_XY_FEEDRATE() / 1.5) + #define G26_XY_FEEDRATE_TRAVEL (PLANNER_XY_FEEDRATE_MM_S / 1.5) #endif #if CROSSHAIRS_SIZE >= INTERSECTION_CIRCLE_RADIUS @@ -170,7 +170,7 @@ float g26_random_deviation = 0.0; #endif -void move_to(const_float_t rx, const_float_t ry, const_float_t z, const_float_t e_delta) { +void move_to(const float rx, const float ry, const float z, const float e_delta) { static float last_z = -999.99; const xy_pos_t dest = { rx, ry }; @@ -196,7 +196,7 @@ void move_to(const_float_t rx, const_float_t ry, const_float_t z, const_float_t prepare_internal_move_to_destination(fr_mm_s); } -void move_to(const xyz_pos_t &where, const_float_t de) { move_to(where.x, where.y, where.z, de); } +void move_to(const xyz_pos_t &where, const float de) { move_to(where.x, where.y, where.z, de); } typedef struct { float extrusion_multiplier = EXTRUSION_MULTIPLIER, @@ -268,8 +268,10 @@ typedef struct { // If the end point of the line is closer to the nozzle, flip the direction, // moving from the end to the start. On very small lines the optimization isn't worth it. - if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length)) - return print_line_from_here_to_there(e, s); + if (dist_end < dist_start && (INTERSECTION_CIRCLE_RADIUS) < ABS(line_length)) { + print_line_from_here_to_there(e, s); + return; + } // Decide whether to retract & lift if (dist_start > 2.0) retract_lift_move(s); @@ -661,7 +663,7 @@ void GcodeSuite::G26() { do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES); - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION bool volumetric_was_enabled = parser.volumetric_enabled; parser.volumetric_enabled = false; planner.calculate_volumetric_multipliers(); @@ -783,7 +785,7 @@ void GcodeSuite::G26() { g26.recover_filament(destination); - { REMEMBER(fr, feedrate_mm_s, PLANNER_XY_FEEDRATE() * 0.1f); + { REMEMBER(fr, feedrate_mm_s, PLANNER_XY_FEEDRATE_MM_S * 0.1f); plan_arc(endpoint, arc_offset, false, 0); // Draw a counter-clockwise arc destination = current_position; } @@ -854,7 +856,7 @@ void GcodeSuite::G26() { destination.z = Z_CLEARANCE_BETWEEN_PROBES; move_to(destination, 0); // Raise the nozzle - #if DISABLED(NO_VOLUMETRICS) + #if HAS_VOLUMETRIC_EXTRUSION parser.volumetric_enabled = volumetric_was_enabled; planner.calculate_volumetric_multipliers(); #endif diff --git a/Marlin/src/gcode/bedlevel/G35.cpp b/Marlin/src/gcode/bedlevel/G35.cpp index c1a329fb8a..b3c56b49b3 100644 --- a/Marlin/src/gcode/bedlevel/G35.cpp +++ b/Marlin/src/gcode/bedlevel/G35.cpp @@ -53,7 +53,7 @@ * 41 - Counter-Clockwise M4 * 50 - Clockwise M5 * 51 - Counter-Clockwise M5 - **/ + */ void GcodeSuite::G35() { DEBUG_SECTION(log_G35, "G35", DEBUGGING(LEVELING)); diff --git a/Marlin/src/gcode/bedlevel/G42.cpp b/Marlin/src/gcode/bedlevel/G42.cpp index cb5ed97406..44f5ceada8 100644 --- a/Marlin/src/gcode/bedlevel/G42.cpp +++ b/Marlin/src/gcode/bedlevel/G42.cpp @@ -27,47 +27,56 @@ #include "../gcode.h" #include "../../MarlinCore.h" // for IsRunning() #include "../../module/motion.h" -#include "../../module/probe.h" // for probe.offset #include "../../feature/bedlevel/bedlevel.h" +#if HAS_PROBE_XY_OFFSET + #include "../../module/probe.h" // for probe.offset +#endif + /** * G42: Move X & Y axes to mesh coordinates (I & J) + * + * Parameters: + * F : Feedrate in mm/min + * I : X axis point index + * J : Y axis point index + * P : Flag to put the probe at the given point */ void GcodeSuite::G42() { - if (MOTION_CONDITIONS) { - const bool hasI = parser.seenval('I'); - const int8_t ix = hasI ? parser.value_int() : 0; - const bool hasJ = parser.seenval('J'); - const int8_t iy = hasJ ? parser.value_int() : 0; + if (!MOTION_CONDITIONS) return; - if ((hasI && !WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) || (hasJ && !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))) { - SERIAL_ECHOLNPGM(STR_ERR_MESH_XY); - return; - } + const bool hasI = parser.seenval('I'); + const int8_t ix = hasI ? parser.value_int() : 0; + const bool hasJ = parser.seenval('J'); + const int8_t iy = hasJ ? parser.value_int() : 0; - // Move to current_position, as modified by I, J, P parameters - destination = current_position; - - if (hasI) destination.x = bedlevel.get_mesh_x(ix); - if (hasJ) destination.y = bedlevel.get_mesh_y(iy); - - #if HAS_PROBE_XY_OFFSET - if (parser.boolval('P')) { - if (hasI) destination.x -= probe.offset_xy.x; - if (hasJ) destination.y -= probe.offset_xy.y; - } - #endif - - const feedRate_t fval = parser.linearval('F'), - fr_mm_s = MMM_TO_MMS(fval > 0 ? fval : 0.0f); - - // SCARA kinematic has "safe" XY raw moves - #if IS_SCARA - prepare_internal_fast_move_to_destination(fr_mm_s); - #else - prepare_internal_move_to_destination(fr_mm_s); - #endif + if ((hasI && !WITHIN(ix, 0, GRID_MAX_POINTS_X - 1)) || (hasJ && !WITHIN(iy, 0, GRID_MAX_POINTS_Y - 1))) { + SERIAL_ECHOLNPGM(STR_ERR_MESH_XY); + return; } + + // Move to current_position, as modified by I, J, P parameters + destination = current_position; + + if (hasI) destination.x = bedlevel.get_mesh_x(ix); + if (hasJ) destination.y = bedlevel.get_mesh_y(iy); + + #if HAS_PROBE_XY_OFFSET + if (parser.seen_test('P')) { + if (hasI) destination.x -= probe.offset_xy.x; + if (hasJ) destination.y -= probe.offset_xy.y; + } + #endif + + const feedRate_t fval = parser.linearval('F'), + fr_mm_s = MMM_TO_MMS(fval > 0 ? fval : 0.0f); + + // SCARA kinematic has "safe" XY raw moves + #if IS_SCARA + prepare_internal_fast_move_to_destination(fr_mm_s); + #else + prepare_internal_move_to_destination(fr_mm_s); + #endif } #endif // HAS_MESH diff --git a/Marlin/src/gcode/bedlevel/M420.cpp b/Marlin/src/gcode/bedlevel/M420.cpp index c495da3018..05fa98e459 100644 --- a/Marlin/src/gcode/bedlevel/M420.cpp +++ b/Marlin/src/gcode/bedlevel/M420.cpp @@ -27,7 +27,10 @@ #include "../gcode.h" #include "../../feature/bedlevel/bedlevel.h" #include "../../module/planner.h" -#include "../../module/probe.h" + +#if ENABLED(MARLIN_DEV_MODE) + #include "../../module/probe.h" +#endif #if ENABLED(EEPROM_SETTINGS) #include "../../module/settings.h" @@ -42,14 +45,14 @@ /** * M420: Enable/Disable Bed Leveling and/or set the Z fade height. * - * S[bool] Turns leveling on or off - * Z[height] Sets the Z fade height (0 or none to disable) - * V[bool] Verbose - Print the leveling grid + * S Turns leveling on or off + * Z Sets the Z fade height (0 or none to disable) + * V Verbose - Print the leveling grid * * With AUTO_BED_LEVELING_UBL only: * - * L[index] Load UBL mesh from index (0 is default) - * T[map] 0:Human-readable 1:CSV 2:"LCD" 4:Compact + * L Load UBL mesh from index (0 is default) + * T 0:Human-readable 1:CSV 2:"LCD" 4:Compact * * With mesh-based leveling only: * @@ -225,9 +228,7 @@ void GcodeSuite::M420() { if (to_enable && !planner.leveling_active) SERIAL_ERROR_MSG(STR_ERR_M420_FAILED); - SERIAL_ECHO_START(); - SERIAL_ECHOPGM("Bed Leveling "); - serialprintln_onoff(planner.leveling_active); + SERIAL_ECHO_MSG("Bed Leveling ", ON_OFF(planner.leveling_active)); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) SERIAL_ECHO_START(); @@ -249,14 +250,13 @@ void GcodeSuite::M420_report(const bool forReplay/*=true*/) { report_heading_etc(forReplay, F( TERN(MESH_BED_LEVELING, "Mesh Bed Leveling", TERN(AUTO_BED_LEVELING_UBL, "Unified Bed Leveling", "Auto Bed Leveling")) )); - SERIAL_ECHO( + SERIAL_ECHOLN( F(" M420 S"), planner.leveling_active #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) , FPSTR(SP_Z_STR), LINEAR_UNIT(planner.z_fade_height) #endif - , F(" ; Leveling ") + , F(" ; Leveling "), ON_OFF(planner.leveling_active) ); - serialprintln_onoff(planner.leveling_active); } #endif // HAS_LEVELING diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index 449c25fefd..c306274aec 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -33,6 +33,7 @@ #include "../../../module/motion.h" #include "../../../module/planner.h" #include "../../../module/probe.h" +#include "../../../module/temperature.h" #include "../../queue.h" #if ENABLED(AUTO_BED_LEVELING_LINEAR) @@ -51,11 +52,17 @@ #include "../../../lcd/extui/ui_api.h" #elif ENABLED(DWIN_CREALITY_LCD) #include "../../../lcd/e3v2/creality/dwin.h" +#elif ENABLED(SOVOL_SV06_RTS) + #include "../../../lcd/sovol_rts/sovol_rts.h" #endif #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) #include "../../../core/debug_out.h" +#if DISABLED(PROBE_MANUALLY) && ENABLED(FT_MOTION) + #include "../../../module/ft_motion.h" +#endif + #if ABL_USES_GRID #if ENABLED(PROBE_Y_FIRST) #define PR_OUTER_VAR abl.meshCount.x @@ -70,14 +77,21 @@ #endif #endif +/** + * @brief Do some things before returning from G29. + * @param retry : true if the G29 can and should be retried. false if the failure is too serious. + * @param did : true if the leveling procedure completed successfully. + */ static void pre_g29_return(const bool retry, const bool did) { if (!retry) { TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE, false)); } - if (did) { - TERN_(DWIN_CREALITY_LCD, dwinLevelingDone()); - TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); - } + #if DISABLED(G29_RETRY_AND_RECOVER) + if (!retry || did) { + TERN_(DWIN_CREALITY_LCD, dwinLevelingDone()); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); + } + #endif } #define G29_RETURN(retry, did) do{ \ @@ -142,81 +156,72 @@ public: #endif /** - * G29: Detailed Z probe, probes the bed at 3 or more points. - * Will fail if the printer has not been homed with G28. + * G29: Bed Leveling * - * Enhanced G29 Auto Bed Leveling Probe Routine + * Enhanced G29 Auto Bed Leveling Probe Routine. + * Probes the bed at 3 or more points. + * Will fail if the printer has not been homed with G28. * - * O Auto-level only if needed + * Parameters: + * O Auto-level only if needed (Optional) * - * D Dry-Run mode. Just evaluate the bed Topology - Don't apply - * or alter the bed level data. Useful to check the topology - * after a first run of G29. + * D Dry-Run mode. Just evaluate the bed Topology - + * Don't apply or alter the bed level data. + * Useful to check the topology after a first run of G29. * - * J Jettison current bed leveling data + * J Jettison current bed leveling data * - * V Set the verbose level (0-4). Example: "G29 V3" + * V<0-4> Set the verbose level (0-4) + * Example: G29 V3 * - * Parameters With LINEAR leveling only: + * With AUTO_BED_LEVELING_LINEAR: + * P Set the size of the grid that will be probed (P x P points) + * Example: G29 P4 * - * P Set the size of the grid that will be probed (P x P points). - * Example: "G29 P4" + * X Set the X size of the grid that will be probed (X x Y points) + * Example: G29 X7 Y5 * - * X Set the X size of the grid that will be probed (X x Y points). - * Example: "G29 X7 Y5" + * Y Set the Y size of the grid that will be probed (X x Y points) * - * Y Set the Y size of the grid that will be probed (X x Y points). + * T Generate a Bed Topology Report + * Example: G29 P5 T - for a detailed report. + * This is useful for manual bed leveling and finding flaws in the bed + * (to assist with part placement). + * Not supported by non-linear delta printer bed leveling. * - * T Generate a Bed Topology Report. Example: "G29 P5 T" for a detailed report. - * This is useful for manual bed leveling and finding flaws in the bed (to - * assist with part placement). - * Not supported by non-linear delta printer bed leveling. + * With AUTO_BED_LEVELING_LINEAR and AUTO_BED_LEVELING_BILINEAR: + * S Set the XY travel speed between probe points (in units/min) + * H Set bounds to a centered square H x H units in size + * -or- + * F Set the Front limit of the probing grid + * B Set the Back limit of the probing grid + * L Set the Left limit of the probing grid + * R Set the Right limit of the probing grid * - * Parameters With LINEAR and BILINEAR leveling only: + * With AUTO_BED_LEVELING_BILINEAR: + * Z Supply additional Z offset to all probe points. + * W Write a mesh point. (If G29 is idle.) + * I Index for mesh point + * J Index for mesh point + * X For mesh point, overrides I + * Y For mesh point, overrides J + * Z For mesh point. If omitted, uses current position's raw Z * - * S Set the XY travel speed between probe points (in units/min) + * With DEBUG_LEVELING_FEATURE: + * C Make a totally fake grid with no actual probing. + * For use in testing when no probing is possible. * - * H Set bounds to a centered square H x H units in size + * With PROBE_MANUALLY: + * To do manual probing simply repeat G29 until the procedure is complete. + * The first G29 accepts parameters. 'G29 Q' for status, 'G29 A' to abort. * - * -or- + * Q Query leveling and G29 state + * A Abort current leveling procedure * - * F Set the Front limit of the probing grid - * B Set the Back limit of the probing grid - * L Set the Left limit of the probing grid - * R Set the Right limit of the probing grid - * - * Parameters with DEBUG_LEVELING_FEATURE only: - * - * C Make a totally fake grid with no actual probing. - * For use in testing when no probing is possible. - * - * Parameters with BILINEAR leveling only: - * - * Z Supply an additional Z probe offset - * - * Extra parameters with PROBE_MANUALLY: - * - * To do manual probing simply repeat G29 until the procedure is complete. - * The first G29 accepts parameters. 'G29 Q' for status, 'G29 A' to abort. - * - * Q Query leveling and G29 state - * - * A Abort current leveling procedure - * - * Extra parameters with BILINEAR only: - * - * W Write a mesh point. (If G29 is idle.) - * I X index for mesh point - * J Y index for mesh point - * X X for mesh point, overrides I - * Y Y for mesh point, overrides J - * Z Z for mesh point. Otherwise, raw current Z. - * - * Without PROBE_MANUALLY: - * - * E By default G29 will engage the Z probe, test the bed, then disengage. - * Include "E" to engage/disengage the Z probe for each sample. - * There's no extra effect if you have a fixed Z probe. + * Without PROBE_MANUALLY: + * E By default G29 will engage the Z probe, test the bed, then disengage + * Include "E" to engage/disengage the Z probe for each sample. + * There's no extra effect if you have a fixed Z probe. */ G29_TYPE GcodeSuite::G29() { @@ -270,6 +275,11 @@ G29_TYPE GcodeSuite::G29() { // Set and report "probing" state to host TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE, false)); + #if DISABLED(PROBE_MANUALLY) + // Potentially disable Fixed-Time Motion for probing + TERN_(FT_MOTION, FTM_DISABLE_IN_SCOPE()); + #endif + /** * On the initial G29 fetch command parameters. */ @@ -277,6 +287,11 @@ G29_TYPE GcodeSuite::G29() { probe.use_probing_tool(); + #ifdef EVENT_GCODE_BEFORE_G29 + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Before G29 G-code: ", EVENT_GCODE_BEFORE_G29); + gcode.process_subcommands_now(F(EVENT_GCODE_BEFORE_G29)); + #endif + #if ANY(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR) abl.abl_probe_index = -1; #endif @@ -382,7 +397,12 @@ G29_TYPE GcodeSuite::G29() { #if ABL_USES_GRID + constexpr feedRate_t min_probe_feedrate_mm_s = XY_PROBE_FEEDRATE_MIN; xy_probe_feedrate_mm_s = MMM_TO_MMS(parser.linearval('S', XY_PROBE_FEEDRATE)); + if (xy_probe_feedrate_mm_s < min_probe_feedrate_mm_s) { + xy_probe_feedrate_mm_s = min_probe_feedrate_mm_s; + SERIAL_ECHOLNPGM(GCODE_ERR_MSG("Feedrate (S) too low. (Using ", min_probe_feedrate_mm_s, ")")); + } const float x_min = probe.min_x(), x_max = probe.max_x(), y_min = probe.min_y(), y_max = probe.max_y(); @@ -431,6 +451,12 @@ G29_TYPE GcodeSuite::G29() { remember_feedrate_scaling_off(); #if ENABLED(PREHEAT_BEFORE_LEVELING) + #if ENABLED(SOVOL_SV06_RTS) + rts.updateTempE0(); + rts.updateTempBed(); + rts.sendData(1, Wait_VP); + rts.gotoPage(ID_ABL_HeatWait_L, ID_ABL_HeatWait_D); + #endif if (!abl.dryrun) probe.preheat_for_probing(LEVELING_NOZZLE_TEMP, TERN(EXTENSIBLE_UI, ExtUI::getLevelingBedTemp(), LEVELING_BED_TEMP) ); @@ -666,7 +692,7 @@ G29_TYPE GcodeSuite::G29() { inInc = -1; // Zag left } - zig ^= true; // zag + FLIP(zig); // zag // An index to print current state grid_count_t pt_index = (PR_OUTER_VAR) * (PR_INNER_SIZE) + 1; @@ -768,6 +794,12 @@ G29_TYPE GcodeSuite::G29() { abl.z_values[abl.meshCount.x][abl.meshCount.y] = z; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(abl.meshCount, z)); + #if ENABLED(SOVOL_SV06_RTS) + if (pt_index <= GRID_MAX_POINTS) rts.sendData(pt_index, AUTO_BED_LEVEL_ICON_VP); + rts.sendData(z * 100.0f, AUTO_BED_LEVEL_1POINT_VP + (pt_index - 1) * 2); + rts.gotoPage(ID_ABL_Wait_L, ID_ABL_Wait_D); + #endif + #endif abl.reenable = false; // Don't re-enable after modifying the mesh @@ -805,7 +837,7 @@ G29_TYPE GcodeSuite::G29() { #endif // AUTO_BED_LEVELING_3POINT - TERN_(HAS_STATUS_MESSAGE, ui.reset_status()); + ui.reset_status(); // Stow the probe. No raise for FIX_MOUNTED_PROBE. if (probe.stow()) { @@ -815,15 +847,15 @@ G29_TYPE GcodeSuite::G29() { } #endif // !PROBE_MANUALLY - // - // G29 Finishing Code - // - // Unless this is a dry run, auto bed leveling will - // definitely be enabled after this point. - // - // If code above wants to continue leveling, it should - // return or loop before this point. - // + /** + * G29 Finishing Code + * + * Unless this is a dry run, auto bed leveling will + * definitely be enabled after this point. + * + * If code above wants to continue leveling, it should + * return or loop before this point. + */ if (DEBUGGING(LEVELING)) DEBUG_POS("> probing complete", current_position); @@ -852,12 +884,12 @@ G29_TYPE GcodeSuite::G29() { // For LINEAR leveling calculate matrix, print reports, correct the position /** - * solve the plane equation ax + by + d = z + * Solve the plane equation ax + by + d = z * A is the matrix with rows [x y 1] for all the probed points * B is the vector of the Z positions - * the normal vector to the plane is formed by the coefficients of the + * The normal vector to the plane is formed by the coefficients of the * plane equation in the standard form, which is Vx*x+Vy*y+Vz*z+d = 0 - * so Vx = -a Vy = -b Vz = 1 (we want the vector facing towards positive Z + * so Vx = -a Vy = -b Vz = 1 (we want the vector facing towards positive Z). */ struct { float a, b, d; } plane_equation_coefficients; @@ -980,11 +1012,13 @@ G29_TYPE GcodeSuite::G29() { TERN_(HAS_BED_PROBE, probe.move_z_after_probing()); #ifdef EVENT_GCODE_AFTER_G29 - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Probe End Script: ", EVENT_GCODE_AFTER_G29); + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("After G29 G-code: ", EVENT_GCODE_AFTER_G29); planner.synchronize(); process_subcommands_now(F(EVENT_GCODE_AFTER_G29)); #endif + TERN_(SOVOL_SV06_RTS, RTS_AutoBedLevelPage()); + probe.use_probing_tool(false); report_current_position(); diff --git a/Marlin/src/gcode/bedlevel/mbl/G29.cpp b/Marlin/src/gcode/bedlevel/mbl/G29.cpp index c9c04b4f0c..0e48ec1449 100644 --- a/Marlin/src/gcode/bedlevel/mbl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/mbl/G29.cpp @@ -45,6 +45,10 @@ #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) #include "../../../core/debug_out.h" +#if ENABLED(FT_MOTION) + #include "../../module/ft_motion.h" +#endif + // Save 130 bytes with non-duplication of PSTR inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM(" not entered."); } @@ -63,6 +67,9 @@ inline void echo_not_entered(const char c) { SERIAL_CHAR(c); SERIAL_ECHOLNPGM(" */ void GcodeSuite::G29() { + // Potentially disable Fixed-Time Motion for probing + TERN_(FT_MOTION, FTM_DISABLE_IN_SCOPE()); + DEBUG_SECTION(log_G29, "G29", true); // G29 Q is also available if debugging @@ -91,7 +98,7 @@ void GcodeSuite::G29() { case MeshReport: SERIAL_ECHOPGM("Mesh Bed Leveling "); if (leveling_is_valid()) { - serialprintln_onoff(planner.leveling_active); + SERIAL_ECHOLN(ON_OFF(planner.leveling_active)); bedlevel.report_mesh(); } else diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index 0ab1ca6105..20b50cd7f1 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -28,6 +28,10 @@ #include "../../module/planner.h" #include "../../module/stepper.h" // for various +#if HAS_HOMING_CURRENT + #include "../../module/motion.h" // for set/restore_homing_current +#endif + #if HAS_MULTI_HOTEND #include "../../module/tool_change.h" #endif @@ -40,18 +44,26 @@ #include "../../feature/tmc_util.h" #endif -#include "../../module/probe.h" +#if HAS_BED_PROBE + #include "../../module/probe.h" +#endif #if ENABLED(BLTOUCH) #include "../../feature/bltouch.h" #endif +#if ENABLED(FT_MOTION) + #include "../../module/ft_motion.h" +#endif + #include "../../lcd/marlinui.h" #if ENABLED(EXTENSIBLE_UI) #include "../../lcd/extui/ui_api.h" #elif ENABLED(DWIN_CREALITY_LCD) #include "../../lcd/e3v2/creality/dwin.h" +#elif ENABLED(SOVOL_SV06_RTS) + #include "../../lcd/sovol_rts/sovol_rts.h" #endif #if ENABLED(LASER_FEATURE) @@ -75,6 +87,12 @@ const float minfr = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)), fr_mm_s = HYPOT(minfr, minfr); + // Set homing current to X and Y axis if defined + TERN_(X_HAS_HOME_CURRENT, set_homing_current(X_AXIS)); + #if Y_HAS_HOME_CURRENT && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX) + set_homing_current(Y_AXIS); + #endif + #if ENABLED(SENSORLESS_HOMING) sensorless_t stealth_states { NUM_AXIS_LIST( @@ -93,6 +111,11 @@ current_position.set(0.0, 0.0); + TERN_(X_HAS_HOME_CURRENT, restore_homing_current(X_AXIS)); + #if Y_HAS_HOME_CURRENT && NONE(CORE_IS_XY, MARKFORGED_XY, MARKFORGED_YX) + restore_homing_current(Y_AXIS); + #endif + #if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT) TERN_(X_SENSORLESS, tmc_disable_stallguard(stepperX, stealth_states.x)); TERN_(X2_SENSORLESS, tmc_disable_stallguard(stepperX2, stealth_states.x2)); @@ -106,6 +129,10 @@ #if ENABLED(Z_SAFE_HOMING) inline void home_z_safely() { + + // Potentially disable Fixed-Time Motion for homing + TERN_(FT_MOTION, FTM_DISABLE_IN_SCOPE()); + DEBUG_SECTION(log_G28, "home_z_safely", DEBUGGING(LEVELING)); // Disallow Z homing if X or Y homing is needed @@ -172,18 +199,24 @@ #endif // IMPROVE_HOMING_RELIABILITY /** - * G28: Home all axes according to settings + * G28: Auto Home * - * Parameters + * Home all axes according to settings * - * None Home to all axes with no parameters. + * Parameters: + * None Home all axes * With QUICK_HOME enabled XY will home together, then Z. * * L Force leveling state ON (if possible) or OFF after homing (Requires RESTORE_LEVELING_AFTER_G28 or ENABLE_LEVELING_AFTER_G28) * O Home only if the position is not known and trusted * R Raise by n mm/inches before homing + * H Hold the current X/Y position when executing a home Z, or if + * multiple axes are homed, the position when Z home is executed. + * When using a probe for Z Home, positions close to the edge may + * fail with position unreachable due to probe/nozzle offset. This + * can be used to avoid a model. * - * Cartesian/SCARA parameters + * Cartesian/SCARA parameters: * * X Home to the X endstop * Y Home to the Y endstop @@ -252,77 +285,13 @@ void GcodeSuite::G28() { // Reset to the XY plane TERN_(CNC_WORKSPACE_PLANES, workspace_plane = PLANE_XY); - #define _OR_HAS_CURR_HOME(N) HAS_CURRENT_HOME(N) || - #if MAIN_AXIS_MAP(_OR_HAS_CURR_HOME) MAP(_OR_HAS_CURR_HOME, X2, Y2, Z2, Z3, Z4) 0 - #define HAS_HOMING_CURRENT 1 - #endif - - #if HAS_HOMING_CURRENT - - #if ENABLED(DEBUG_LEVELING_FEATURE) - auto debug_current = [](FSTR_P const s, const int16_t a, const int16_t b) { - if (DEBUGGING(LEVELING)) { DEBUG_ECHOLN(s, F(" current: "), a, F(" -> "), b); } - }; - #else - #define debug_current(...) - #endif - - #define _SAVE_SET_CURRENT(A) \ - const int16_t saved_current_##A = stepper##A.getMilliamps(); \ - stepper##A.rms_current(A##_CURRENT_HOME); \ - debug_current(F(STR_##A), saved_current_##A, A##_CURRENT_HOME) - - #if HAS_CURRENT_HOME(X) - _SAVE_SET_CURRENT(X); - #endif - #if HAS_CURRENT_HOME(X2) - _SAVE_SET_CURRENT(X2); - #endif - #if HAS_CURRENT_HOME(Y) - _SAVE_SET_CURRENT(Y); - #endif - #if HAS_CURRENT_HOME(Y2) - _SAVE_SET_CURRENT(Y2); - #endif - #if HAS_CURRENT_HOME(Z) - _SAVE_SET_CURRENT(Z); - #endif - #if HAS_CURRENT_HOME(Z2) - _SAVE_SET_CURRENT(Z2); - #endif - #if HAS_CURRENT_HOME(Z3) - _SAVE_SET_CURRENT(Z3); - #endif - #if HAS_CURRENT_HOME(Z4) - _SAVE_SET_CURRENT(Z4); - #endif - #if HAS_CURRENT_HOME(I) - _SAVE_SET_CURRENT(I); - #endif - #if HAS_CURRENT_HOME(J) - _SAVE_SET_CURRENT(J); - #endif - #if HAS_CURRENT_HOME(K) - _SAVE_SET_CURRENT(K); - #endif - #if HAS_CURRENT_HOME(U) - _SAVE_SET_CURRENT(U); - #endif - #if HAS_CURRENT_HOME(V) - _SAVE_SET_CURRENT(V); - #endif - #if HAS_CURRENT_HOME(W) - _SAVE_SET_CURRENT(W); - #endif - #if SENSORLESS_STALLGUARD_DELAY - safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle - #endif - #endif // HAS_HOMING_CURRENT - #if ENABLED(IMPROVE_HOMING_RELIABILITY) motion_state_t saved_motion_state = begin_slow_homing(); #endif + // Potentially disable Fixed-Time Motion for homing + TERN_(FT_MOTION, FTM_DISABLE_IN_SCOPE()); + // Always home with tool 0 active #if HAS_MULTI_HOTEND #if DISABLED(DELTA) || ENABLED(DELTA_HOME_TO_SAFE_ZONE) @@ -330,7 +299,8 @@ void GcodeSuite::G28() { #endif // PARKING_EXTRUDER homing requires different handling of movement / solenoid activation, depending on the side of homing #if ENABLED(PARKING_EXTRUDER) - const bool pe_final_change_must_unpark = parking_extruder_unpark_after_homing(old_tool_index, X_HOME_DIR + 1 == old_tool_index * 2); + const bool homed_towards_tool = old_tool_index == TERN(X_HOME_TO_MIN, 0, 1), + pe_final_change_must_unpark = parking_extruder_unpark_after_homing(old_tool_index, homed_towards_tool); #endif tool_change(0, true); #endif @@ -361,9 +331,9 @@ void GcodeSuite::G28() { #else // !DELTA && !AXEL_TPARA - #define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS)))) + #define _UNSAFE(A) TERN0(Z_SAFE_HOMING, homeZZ && axis_should_home(_AXIS(A))) - const bool homeZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')), + const bool homeZZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')), NUM_AXIS_LIST_( // Other axes should be homed before Z safe-homing needX = _UNSAFE(X), needY = _UNSAFE(Y), needZ = false, // UNUSED needI = _UNSAFE(I), needJ = _UNSAFE(J), needK = _UNSAFE(K), @@ -372,7 +342,7 @@ void GcodeSuite::G28() { NUM_AXIS_LIST_( // Home each axis if needed or flagged homeX = needX || parser.seen_test('X'), homeY = needY || parser.seen_test('Y'), - homeZZ = homeZ, + homeZ = homeZZ, homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME), homeU = needU || parser.seen_test(AXIS7_NAME), homeV = needV || parser.seen_test(AXIS8_NAME), homeW = needW || parser.seen_test(AXIS9_NAME) @@ -394,7 +364,7 @@ void GcodeSuite::G28() { #if HAS_Z_AXIS - UNUSED(needZ); UNUSED(homeZZ); + UNUSED(needZ); // Z may home first, e.g., when homing away from the bed. // This is also permitted when homing with a Z endstop. @@ -417,7 +387,7 @@ void GcodeSuite::G28() { bool with_probe = ENABLED(HOMING_Z_WITH_PROBE); // Raise above the current Z (which should be synced in the planner) // The "height" for Z is a coordinate. But if Z is not trusted/homed make it relative. - if (seenR || !TERN(HOME_AFTER_DEACTIVATE, axis_is_trusted, axis_was_homed)(Z_AXIS)) { + if (seenR || !(z_min_trusted || axis_should_home(Z_AXIS))) { z_homing_height += current_position.z; with_probe = false; } @@ -478,8 +448,7 @@ void GcodeSuite::G28() { #if HAS_Y_AXIS // Home Y (after X) - if (DISABLED(HOME_Y_BEFORE_X) && doY) - homeaxis(Y_AXIS); + if (DISABLED(HOME_Y_BEFORE_X) && doY) homeaxis(Y_AXIS); #endif #if ALL(FOAMCUTTER_XYUV, HAS_J_AXIS) @@ -505,7 +474,12 @@ void GcodeSuite::G28() { #endif #if ENABLED(Z_SAFE_HOMING) - if (TERN1(POWER_LOSS_RECOVERY, !parser.seen_test('H'))) home_z_safely(); else homeaxis(Z_AXIS); + // H means hold the current X/Y position when probing. + // Otherwise move to the define safe X/Y position before homing Z. + if (!parser.seen_test('H')) + home_z_safely(); + else + homeaxis(Z_AXIS); #else homeaxis(Z_AXIS); #endif @@ -570,55 +544,6 @@ void GcodeSuite::G28() { // Clear endstop state for polled stallGuard endstops TERN_(SPI_ENDSTOPS, endstops.clear_endstop_state()); - #if HAS_HOMING_CURRENT - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore driver current..."); - #if HAS_CURRENT_HOME(X) - stepperX.rms_current(saved_current_X); - #endif - #if HAS_CURRENT_HOME(X2) - stepperX2.rms_current(saved_current_X2); - #endif - #if HAS_CURRENT_HOME(Y) - stepperY.rms_current(saved_current_Y); - #endif - #if HAS_CURRENT_HOME(Y2) - stepperY2.rms_current(saved_current_Y2); - #endif - #if HAS_CURRENT_HOME(Z) - stepperZ.rms_current(saved_current_Z); - #endif - #if HAS_CURRENT_HOME(Z2) - stepperZ2.rms_current(saved_current_Z2); - #endif - #if HAS_CURRENT_HOME(Z3) - stepperZ3.rms_current(saved_current_Z3); - #endif - #if HAS_CURRENT_HOME(Z4) - stepperZ4.rms_current(saved_current_Z4); - #endif - #if HAS_CURRENT_HOME(I) - stepperI.rms_current(saved_current_I); - #endif - #if HAS_CURRENT_HOME(J) - stepperJ.rms_current(saved_current_J); - #endif - #if HAS_CURRENT_HOME(K) - stepperK.rms_current(saved_current_K); - #endif - #if HAS_CURRENT_HOME(U) - stepperU.rms_current(saved_current_U); - #endif - #if HAS_CURRENT_HOME(V) - stepperV.rms_current(saved_current_V); - #endif - #if HAS_CURRENT_HOME(W) - stepperW.rms_current(saved_current_W); - #endif - #if SENSORLESS_STALLGUARD_DELAY - safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle - #endif - #endif // HAS_HOMING_CURRENT - // Move to a height where we can use the full xy-area TERN_(DELTA_HOME_TO_SAFE_ZONE, do_blocking_move_to_z(delta_clip_start_height)); @@ -650,6 +575,7 @@ void GcodeSuite::G28() { ui.refresh(); + TERN_(SOVOL_SV06_RTS, RTS_MoveAxisHoming()); TERN_(DWIN_CREALITY_LCD, dwinHomingDone()); TERN_(EXTENSIBLE_UI, ExtUI::onHomingDone()); diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp index 59e0db132a..1eabb30ffa 100644 --- a/Marlin/src/gcode/calibrate/G33.cpp +++ b/Marlin/src/gcode/calibrate/G33.cpp @@ -41,8 +41,8 @@ constexpr uint8_t _7P_STEP = 1, // 7-point step - to change number of calibration points _4P_STEP = _7P_STEP * 2, // 4-point step - NPP = _7P_STEP * 6; // number of calibration points on the radius -enum CalEnum : char { // the 7 main calibration points - add definitions if needed + NPP = _7P_STEP * 6; // Number of calibration points on the radius +enum CalEnum : char { // The 7 main calibration points - add definitions if needed CEN = 0, __A = 1, _AB = __A + _7P_STEP, @@ -63,9 +63,9 @@ float lcd_probe_pt(const xy_pos_t &xy); void ac_home() { endstops.enable(true); - TERN_(SENSORLESS_HOMING, endstops.set_z_sensorless_current(true)); + TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(true)); home_delta(); - TERN_(SENSORLESS_HOMING, endstops.set_z_sensorless_current(false)); + TERN_(IMPROVE_HOMING_RELIABILITY, planner.enable_stall_prevention(false)); endstops.not_homing(); } @@ -87,7 +87,7 @@ void ac_cleanup() { TERN_(HAS_BED_PROBE, probe.use_probing_tool(false)); } -void print_signed_float(FSTR_P const prefix, const_float_t f) { +void print_signed_float(FSTR_P const prefix, const float f) { SERIAL_ECHO(F(" "), prefix, C(':')); serial_offset(f); } @@ -154,7 +154,7 @@ static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool S2 += sq(z_pt[rad]); N++; } - return LROUND(SQRT(S2 / N) * 1000.0f) / 1000.0f + 0.00001f; + return LROUND(SQRT(S2 / N) * 1000.0f) * 0.001f + 0.00001f; } } return 0.00001f; @@ -197,13 +197,13 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi if (!_0p_calibration) { - if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // probe the center + if (!_7p_no_intermediates && !_7p_4_intermediates && !_7p_11_intermediates) { // Probe the center const xy_pos_t center{0}; z_pt[CEN] += calibration_probe(center, stow_after_each, probe_at_offset); if (isnan(z_pt[CEN])) return false; } - if (_7p_calibration) { // probe extra center points + if (_7p_calibration) { // Probe extra center points const float start = _7p_9_center ? float(_CA) + _7P_STEP / 3.0f : _7p_6_center ? float(_CA) : float(__C), steps = _7p_9_center ? _4P_STEP / 3.0f : _7p_6_center ? _7P_STEP : _4P_STEP; I_LOOP_CAL_PT(rad, start, steps) { @@ -216,7 +216,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi z_pt[CEN] /= float(_7p_2_intermediates ? 7 : probe_points); } - if (!_1p_calibration) { // probe the radius + if (!_1p_calibration) { // Probe the radius const CalEnum start = _4p_opposite_points ? _AB : __A; const float steps = _7p_14_intermediates ? _7P_STEP / 15.0f : // 15r * 6 + 10c = 100 _7p_11_intermediates ? _7P_STEP / 12.0f : // 12r * 6 + 9c = 81 @@ -241,7 +241,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi z_pt[uint8_t(LROUND(rad - interpol + NPP - 1)) % NPP + 1] += z_temp * sq(cos(RADIANS(interpol * 90))); z_pt[uint8_t(LROUND(rad - interpol)) % NPP + 1] += z_temp * sq(sin(RADIANS(interpol * 90))); } - zig_zag = !zig_zag; + FLIP(zig_zag); } if (_7p_intermed_points) LOOP_CAL_RAD(rad) @@ -254,10 +254,11 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi } /** - * kinematics routines and auto tune matrix scaling parameters: - * see https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for - * - formulae for approximative forward kinematics in the end-stop displacement matrix - * - definition of the matrix scaling parameters + * Kinematics routines and auto tune matrix scaling parameters + * + * NOTE: See https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for: + * - Formula for approximative forward kinematics in the end-stop displacement matrix + * - Definition of the matrix scaling parameters */ static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_at_pt_axis[NPP + 1], const float dcr) { xyz_pos_t pos{0}; @@ -314,7 +315,7 @@ static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], const float d static float auto_tune_h(const float dcr) { const float r_quot = dcr / delta_radius; - return RECIPROCAL(r_quot / (2.0f / 3.0f)); // (2/3)/CR + return RECIPROCAL(r_quot * (3.0f / 2.0f)); // (2/3)/CR } static float auto_tune_r(const float dcr) { @@ -346,43 +347,43 @@ static float auto_tune_a(const float dcr) { } /** - * G33 - Delta '1-4-7-point' Auto-Calibration - * Calibrate height, z_offset, endstops, delta radius, and tower angles. + * G33: Delta Auto Calibration + * + * Calibrate height, z_offset, endstops, delta radius, and tower angles. * * Parameters: + * P Number of probe points: + * P0 Normalizes end-stops and tower angle corrections only (no probing) + * P1 Probe center and set height only + * P2 Probe center and towers. Set height, endstops, and delta radius + * P3 Probe all positions - center, towers and opposite towers. Set all + * P4-P10 Probe all positions with intermediate locations, averaging them * - * Pn Number of probe points: - * P0 Normalizes calibration. - * P1 Calibrates height only with center probe. - * P2 Probe center and towers. Calibrate height, endstops and delta radius. - * P3 Probe all positions: center, towers and opposite towers. Calibrate all. - * P4-P10 Probe all positions at different intermediate locations and average them. + * R Temporarily reduce the size of the probe grid by the specified amount * - * Rn.nn Temporary reduce the probe grid by the specified amount (mm) + * T Disable tower angle corrections calibration (P3-P7) * - * T Don't calibrate tower angle corrections + * C Calibration precision; if omitted iterations stop at best achievable precision * - * Cn.nn Calibration precision; when omitted calibrates to maximum precision + * F<1-30> Run (“force”) this number of iterations and take the best result * - * Fn Force to run at least n iterations and take the best result + * V Verbose level: + * V0 Dry-run mode. Report settings and probe results. No calibration + * V1 Report start and end settings only + * V2 Report settings at each iteration + * V3 Report settings and probe results * - * Vn Verbose level: - * V0 Dry-run mode. Report settings and probe results. No calibration. - * V1 Report start and end settings only - * V2 Report settings at each iteration - * V3 Report settings and probe results + * E Engage the probe for each point * - * E Engage the probe for each point + * O Probe at probe-offset-relative positions instead of the required kinematic points * - * O Probe at offsetted probe positions (this is wrong but it seems to work) - * - * With SENSORLESS_PROBING: - * Use these flags to calibrate stall sensitivity: (e.g., `G33 P1 Y Z` to calibrate X only.) - * X Don't activate stallguard on X. - * Y Don't activate stallguard on Y. - * Z Don't activate stallguard on Z. - * - * S Save offset_sensorless_adj + * With HAS_DELTA_SENSORLESS_PROBING: + * Use these flags to calibrate stall sensitivity: + * Example: G33 P1 Y Z - to calibrate X only + * X Don't activate stallguard on X + * Y Don't activate stallguard on Y + * Z Don't activate stallguard on Z + * S Save offset_sensorless_adj */ void GcodeSuite::G33() { @@ -481,15 +482,15 @@ void GcodeSuite::G33() { caltower({ false, true, false }); // B caltower({ false, false, true }); // C - probe.test_sensitivity = { true, true, true }; // reset to all + probe.test_sensitivity = { true, true, true }; // Reset to all } #endif - do { // start iterations + do { // Start iterations float z_at_pt[NPP + 1] = { 0.0f }; - test_precision = zero_std_dev_old != 999.0f ? (zero_std_dev + zero_std_dev_old) / 2.0f : zero_std_dev; + test_precision = zero_std_dev_old != 999.0f ? (zero_std_dev + zero_std_dev_old) * 0.5f : zero_std_dev; iterations++; // Probe the points @@ -505,11 +506,11 @@ void GcodeSuite::G33() { if ((zero_std_dev < test_precision || iterations <= force_iterations) && zero_std_dev > calibration_precision) { #if !HAS_BED_PROBE - test_precision = 0.0f; // forced end + test_precision = 0.0f; // Forced end #endif if (zero_std_dev < zero_std_dev_min) { - // set roll-back point + // Set roll-back point e_old = delta_endstop_adj; r_old = delta_radius; h_old = delta_height; @@ -520,19 +521,20 @@ void GcodeSuite::G33() { float r_delta = 0.0f; /** - * convergence matrices: - * see https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for - * - definition of the matrix scaling parameters - * - matrices for 4 and 7 point calibration + * Convergence matrices + * + * NOTE: See https://github.com/LVD-AC/Marlin-AC/tree/1.1.x-AC/documentation for: + * - Definition of the matrix scaling parameters + * - Matrices for 4 and 7 point calibration */ - #define ZP(N,I) ((N) * z_at_pt[I] / 4.0f) // 4.0 = divider to normalize to integers + #define ZP(N,I) ((N) * z_at_pt[I] * 0.25f) // 4.0 = divider to normalize to integers #define Z12(I) ZP(12, I) #define Z4(I) ZP(4, I) #define Z2(I) ZP(2, I) #define Z1(I) ZP(1, I) #define Z0(I) ZP(0, I) - // calculate factors + // Calculate factors if (_7p_9_center) dcr *= 0.9f; h_factor = auto_tune_h(dcr); r_factor = auto_tune_r(dcr); @@ -541,22 +543,22 @@ void GcodeSuite::G33() { switch (probe_points) { case 0: - test_precision = 0.0f; // forced end + test_precision = 0.0f; // Forced end break; case 1: - test_precision = 0.0f; // forced end + test_precision = 0.0f; // Forced end LOOP_NUM_AXES(axis) e_delta[axis] = +Z4(CEN); break; case 2: - if (towers_set) { // see 4 point calibration (towers) matrix + if (towers_set) { // See 4 point calibration (towers) matrix e_delta.set((+Z4(__A) -Z2(__B) -Z2(__C)) * h_factor +Z4(CEN), (-Z2(__A) +Z4(__B) -Z2(__C)) * h_factor +Z4(CEN), (-Z2(__A) -Z2(__B) +Z4(__C)) * h_factor +Z4(CEN)); r_delta = (+Z4(__A) +Z4(__B) +Z4(__C) -Z12(CEN)) * r_factor; } - else { // see 4 point calibration (opposites) matrix + else { // See 4 point calibration (opposites) matrix e_delta.set((-Z4(_BC) +Z2(_CA) +Z2(_AB)) * h_factor +Z4(CEN), (+Z2(_BC) -Z4(_CA) +Z2(_AB)) * h_factor +Z4(CEN), (+Z2(_BC) +Z2(_CA) -Z4(_AB)) * h_factor +Z4(CEN)); @@ -564,13 +566,13 @@ void GcodeSuite::G33() { } break; - default: // see 7 point calibration (towers & opposites) matrix + default: // See 7 point calibration (towers & opposites) matrix e_delta.set((+Z2(__A) -Z1(__B) -Z1(__C) -Z2(_BC) +Z1(_CA) +Z1(_AB)) * h_factor +Z4(CEN), (-Z1(__A) +Z2(__B) -Z1(__C) +Z1(_BC) -Z2(_CA) +Z1(_AB)) * h_factor +Z4(CEN), (-Z1(__A) -Z1(__B) +Z2(__C) +Z1(_BC) +Z1(_CA) -Z2(_AB)) * h_factor +Z4(CEN)); r_delta = (+Z2(__A) +Z2(__B) +Z2(__C) +Z2(_BC) +Z2(_CA) +Z2(_AB) -Z12(CEN)) * r_factor; - if (towers_set) { // see 7 point tower angle calibration (towers & opposites) matrix + if (towers_set) { // See 7 point tower angle calibration (towers & opposites) matrix t_delta.set((+Z0(__A) -Z4(__B) +Z4(__C) +Z0(_BC) -Z4(_CA) +Z4(_AB) +Z0(CEN)) * a_factor, (+Z4(__A) +Z0(__B) -Z4(__C) +Z4(_BC) +Z0(_CA) -Z4(_AB) +Z0(CEN)) * a_factor, (-Z4(__A) +Z4(__B) +Z0(__C) -Z4(_BC) +Z4(_CA) +Z0(_AB) +Z0(CEN)) * a_factor); @@ -582,14 +584,14 @@ void GcodeSuite::G33() { delta_tower_angle_trim += t_delta; } else if (zero_std_dev >= test_precision) { - // roll back + // Roll back delta_endstop_adj = e_old; delta_radius = r_old; delta_height = h_old; delta_tower_angle_trim = a_old; } - if (verbose_level != 0) { // !dry run + if (verbose_level != 0) { // !Dry-run // Normalize angles to least-squares if (_angle_results) { @@ -598,7 +600,7 @@ void GcodeSuite::G33() { LOOP_NUM_AXES(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f; } - // adjust delta_height and endstops by the max amount + // Adjust delta_height and endstops by the max amount const float z_temp = _MAX(delta_endstop_adj.a, delta_endstop_adj.b, delta_endstop_adj.c); delta_height -= z_temp; LOOP_NUM_AXES(axis) delta_endstop_adj[axis] -= z_temp; @@ -606,7 +608,7 @@ void GcodeSuite::G33() { recalc_delta_settings(); NOMORE(zero_std_dev_min, zero_std_dev); - // print report + // Print report if (verbose_level == 3 || verbose_level == 0) { print_calibration_results(z_at_pt, _tower_results, _opposite_results); @@ -620,7 +622,7 @@ void GcodeSuite::G33() { #endif } - if (verbose_level != 0) { // !dry run + if (verbose_level != 0) { // !Dry-run if ((zero_std_dev >= test_precision && iterations > force_iterations) || zero_std_dev <= calibration_precision) { // end iterations SERIAL_ECHOPGM("Calibration OK"); SERIAL_ECHO_SP(32); @@ -634,7 +636,7 @@ void GcodeSuite::G33() { } SERIAL_EOL(); - MString<20> msg(F("Calibration sd:")); + MString<21> msg(F("Calibration sd:")); if (zero_std_dev_min < 1) msg.appendf(F("0.%03i"), (int)LROUND(zero_std_dev_min * 1000.0f)); else @@ -657,7 +659,7 @@ void GcodeSuite::G33() { print_calibration_settings(_endstop_results, _angle_results); } } - else { // dry run + else { // Dry-run FSTR_P const enddryrun = F("End DRY-RUN"); SERIAL_ECHO(enddryrun); SERIAL_ECHO_SP(35); diff --git a/Marlin/src/gcode/calibrate/G34.cpp b/Marlin/src/gcode/calibrate/G34.cpp index 9a0cb0054b..504dcd1c6f 100644 --- a/Marlin/src/gcode/calibrate/G34.cpp +++ b/Marlin/src/gcode/calibrate/G34.cpp @@ -40,7 +40,9 @@ #include "../../core/debug_out.h" /** - * G34 - Align the ends of the X gantry. See https://youtu.be/3jAFQdTk8iw + * G34: Mechanical Gantry Calibration + * + * Align the ends of the X gantry. See https://youtu.be/3jAFQdTk8iw * * - The carriage moves to GANTRY_CALIBRATION_SAFE_POSITION, also called the “pounce” position. * - If possible, the Z stepper current is reduced to the value specified by 'S' @@ -53,8 +55,8 @@ * - The machine is re-homed, according to GANTRY_CALIBRATION_COMMANDS_POST. * * Parameters: - * [S] - Current value to use for the raise move. (Default: GANTRY_CALIBRATION_CURRENT) - * [Z] - Extra distance past Z_MAX_POS to move the Z axis. (Default: GANTRY_CALIBRATION_EXTRA_HEIGHT) + * S Current value to use for the raise move. (Default: GANTRY_CALIBRATION_CURRENT) + * Z Extra distance past Z_MAX_POS to move the Z axis. (Default: GANTRY_CALIBRATION_EXTRA_HEIGHT) */ void GcodeSuite::G34() { @@ -95,37 +97,36 @@ void GcodeSuite::G34() { #if HAS_MOTOR_CURRENT_SPI const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT); - const uint32_t previous_current = stepper.motor_current_setting[Z_AXIS]; + const uint32_t previous_current_Z = stepper.motor_current_setting[Z_AXIS]; stepper.set_digipot_current(Z_AXIS, target_current); #elif HAS_MOTOR_CURRENT_PWM const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT); - const uint32_t previous_current = stepper.motor_current_setting[1]; // Z + const uint32_t previous_current_Z = stepper.motor_current_setting[1]; // Z stepper.set_digipot_current(1, target_current); #elif HAS_MOTOR_CURRENT_DAC const float target_current = parser.floatval('S', GANTRY_CALIBRATION_CURRENT); - const float previous_current = dac_amps(Z_AXIS, target_current); + const float previous_current_Z = dac_amps(Z_AXIS, target_current); stepper_dac.set_current_value(Z_AXIS, target_current); #elif HAS_MOTOR_CURRENT_I2C const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT); - previous_current = dac_amps(Z_AXIS); + const float previous_current_Z = dac_amps(Z_AXIS); digipot_i2c.set_current(Z_AXIS, target_current) #elif HAS_TRINAMIC_CONFIG const uint16_t target_current = parser.intval('S', GANTRY_CALIBRATION_CURRENT); - static uint16_t previous_current_arr[NUM_Z_STEPPERS]; - #if AXIS_IS_TMC(Z) - previous_current_arr[0] = stepperZ.getMilliamps(); + #if Z_IS_TRINAMIC + static uint16_t previous_current_Z = stepperZ.getMilliamps(); stepperZ.rms_current(target_current); #endif - #if AXIS_IS_TMC(Z2) - previous_current_arr[1] = stepperZ2.getMilliamps(); + #if Z2_IS_TRINAMIC + static uint16_t previous_current_Z2 = stepperZ2.getMilliamps(); stepperZ2.rms_current(target_current); #endif - #if AXIS_IS_TMC(Z3) - previous_current_arr[2] = stepperZ3.getMilliamps(); + #if Z3_IS_TRINAMIC + static uint16_t previous_current_Z3 = stepperZ3.getMilliamps(); stepperZ3.rms_current(target_current); #endif - #if AXIS_IS_TMC(Z4) - previous_current_arr[3] = stepperZ4.getMilliamps(); + #if Z4_IS_TRINAMIC + static uint16_t previous_current_Z4 = stepperZ4.getMilliamps(); stepperZ4.rms_current(target_current); #endif #endif @@ -140,26 +141,18 @@ void GcodeSuite::G34() { #endif #if HAS_MOTOR_CURRENT_SPI - stepper.set_digipot_current(Z_AXIS, previous_current); + stepper.set_digipot_current(Z_AXIS, previous_current_Z); #elif HAS_MOTOR_CURRENT_PWM - stepper.set_digipot_current(1, previous_current); + stepper.set_digipot_current(1, previous_current_Z); #elif HAS_MOTOR_CURRENT_DAC - stepper_dac.set_current_value(Z_AXIS, previous_current); + stepper_dac.set_current_value(Z_AXIS, previous_current_Z); #elif HAS_MOTOR_CURRENT_I2C - digipot_i2c.set_current(Z_AXIS, previous_current) + digipot_i2c.set_current(Z_AXIS, previous_current_Z) #elif HAS_TRINAMIC_CONFIG - #if AXIS_IS_TMC(Z) - stepperZ.rms_current(previous_current_arr[0]); - #endif - #if AXIS_IS_TMC(Z2) - stepperZ2.rms_current(previous_current_arr[1]); - #endif - #if AXIS_IS_TMC(Z3) - stepperZ3.rms_current(previous_current_arr[2]); - #endif - #if AXIS_IS_TMC(Z4) - stepperZ4.rms_current(previous_current_arr[3]); - #endif + TERN_(Z_IS_TRINAMIC, stepperZ.rms_current(previous_current_Z)); + TERN_(Z2_IS_TRINAMIC, stepperZ2.rms_current(previous_current_Z2)); + TERN_(Z3_IS_TRINAMIC, stepperZ3.rms_current(previous_current_Z3)); + TERN_(Z4_IS_TRINAMIC, stepperZ4.rms_current(previous_current_Z4)); #endif // Back off end plate, back to normal motion range diff --git a/Marlin/src/gcode/calibrate/G34_M422.cpp b/Marlin/src/gcode/calibrate/G34_M422.cpp index 7a27a61b9d..6c367ad882 100644 --- a/Marlin/src/gcode/calibrate/G34_M422.cpp +++ b/Marlin/src/gcode/calibrate/G34_M422.cpp @@ -56,23 +56,24 @@ #endif /** - * G34: Z-Stepper automatic alignment + * G34: Z Steppers Auto-Alignment * - * Manual stepper lock controls (reset by G28): - * L Unlock all steppers - * Z<1-4> Z stepper to lock / unlock - * S 0=UNLOCKED 1=LOCKED. If omitted, assume LOCKED. + * Parameters: + * Manual stepper lock controls (reset by G28): + * L Unlock all steppers + * Z Target specific Z stepper to lock/unlock (1-4) + * S Lock state; 0=UNLOCKED 1=LOCKED. If omitted, assume LOCKED * - * Examples: - * G34 Z1 ; Lock Z1 - * G34 L Z2 ; Unlock all, then lock Z2 - * G34 Z2 S0 ; Unlock Z2 + * With Z_STEPPER_AUTO_ALIGN: + * I Number of test iterations. If omitted, Z_STEPPER_ALIGN_ITERATIONS. (1-30) + * T Target Accuracy factor. If omitted, Z_STEPPER_ALIGN_ACC. (0.01-1.0) + * A Provide an Amplification value. If omitted, Z_STEPPER_ALIGN_AMP. (0.5-2.0) + * R Recalculate points based on current probe offsets * - * With Z_STEPPER_AUTO_ALIGN: - * I Number of tests. If omitted, Z_STEPPER_ALIGN_ITERATIONS. - * T Target Accuracy factor. If omitted, Z_STEPPER_ALIGN_ACC. - * A Provide an Amplification value. If omitted, Z_STEPPER_ALIGN_AMP. - * R Flag to recalculate points based on current probe offsets + * Example: + * G34 Z1 ; Lock Z1 + * G34 L Z2 ; Unlock all, then lock Z2 + * G34 Z2 S0 ; Unlock Z2 */ void GcodeSuite::G34() { @@ -142,6 +143,11 @@ void GcodeSuite::G34() { probe.use_probing_tool(); + #ifdef EVENT_GCODE_BEFORE_G34 + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Before G34 G-code: ", F(EVENT_GCODE_BEFORE_G34)); + gcode.process_subcommands_now(F(EVENT_GCODE_BEFORE_G34)); + #endif + TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false)); // Compute a worst-case clearance height to probe from. After the first @@ -212,7 +218,27 @@ void GcodeSuite::G34() { // Probe a Z height for each stepper. // Probing sanity check is disabled, as it would trigger even in normal cases because // current_position.z has been manually altered in the "dirty trick" above. - const float z_probed_height = probe.probe_at_point(DIFF_TERN(HAS_HOME_OFFSET, ppos, xy_pos_t(home_offset)), raise_after, 0, true, false, (Z_PROBE_LOW_POINT) - z_probe * 0.5f, z_probe * 0.5f); + + const float minz = (Z_PROBE_LOW_POINT) - (z_probe * 0.5f); + + if (DEBUGGING(LEVELING)) { + DEBUG_ECHOPGM("Z_PROBE_LOW_POINT: " STRINGIFY(Z_PROBE_LOW_POINT)); + DEBUG_ECHOLNPGM(" z_probe: ", p_float_t(z_probe, 3), + " Probe Tgt: ", p_float_t(minz, 3)); + } + + const float z_probed_height = probe.probe_at_point( + DIFF_TERN(HAS_HOME_OFFSET, ppos, xy_pos_t(home_offset)), // xy + raise_after, // raise_after + (DEBUGGING(LEVELING) || DEBUGGING(INFO)) ? 3 : 0, // verbose_level + true, false, // probe_relative, sanity_check + minz, // z_min_point + Z_TWEEN_SAFE_CLEARANCE // z_clearance + ); + + if (DEBUGGING(LEVELING)) + DEBUG_ECHOLN(F("Probing X"), ppos.x, FPSTR(SP_Y_STR), ppos.y, F(" Height = "), z_probed_height); + if (isnan(z_probed_height)) { SERIAL_ECHOLNPGM(STR_ERR_PROBING_FAILED); LCD_MESSAGE(MSG_LCD_PROBING_FAILED); @@ -236,7 +262,12 @@ void GcodeSuite::G34() { // Adapt the next probe clearance height based on the new measurements. // Safe_height = lowest distance to bed (= highest measurement) plus highest measured misalignment. z_maxdiff = z_measured_max - z_measured_min; - z_probe = (Z_TWEEN_SAFE_CLEARANCE + zoffs) + z_measured_max + z_maxdiff; //Not sure we need z_maxdiff, but leaving it in for safety. + + // The intent of the line below seems to be to clamp the probe depth on successive iterations of G34, but in reality if the amplification + // factor is not completely accurate, this was causing probing to fail as the probe stopped fractions of a mm from the trigger point + // on the second iteration very reliably. This may be restored with an uncertainty factor at some point, however its usefulness after + // all probe points have seen a successful probe is questionable. + //z_probe = (Z_TWEEN_SAFE_CLEARANCE + zoffs) + z_measured_max + z_maxdiff; // Not sure we need z_maxdiff, but leaving it in for safety. #if HAS_Z_STEPPER_ALIGN_STEPPER_XY // Replace the initial values in z_measured with calculated heights at @@ -276,7 +307,7 @@ void GcodeSuite::G34() { SERIAL_EOL(); - SString<15 + TERN0(TRIPLE_Z, 30) + TERN0(QUAD_Z, 45)> msg(F("1:2="), p_float_t(ABS(z_measured[1] - z_measured[0]), 3)); + SString<15 + TERN0(TRIPLE_Z, 30) + TERN0(QUAD_Z, 45)> msg(F("2-1="), p_float_t(ABS(z_measured[1] - z_measured[0]), 3)); #if TRIPLE_Z msg.append(F(" 3-2="), p_float_t(ABS(z_measured[2] - z_measured[1]), 3)) .append(F(" 3-1="), p_float_t(ABS(z_measured[2] - z_measured[0]), 3)); @@ -290,7 +321,7 @@ void GcodeSuite::G34() { msg.echoln(); ui.set_status(msg); - auto decreasing_accuracy = [](const_float_t v1, const_float_t v2) { + auto decreasing_accuracy = [](const float v1, const float v2) { if (v1 < v2 * 0.7f) { SERIAL_ECHOLNPGM("Decreasing Accuracy Detected."); LCD_MESSAGE(MSG_DECREASING_ACCURACY); @@ -339,7 +370,7 @@ void GcodeSuite::G34() { if (decreasing_accuracy(last_z_align_move[zstepper], z_align_abs)) { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " last_z_align_move = ", last_z_align_move[zstepper]); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " z_align_abs = ", z_align_abs); - adjustment_reverse = !adjustment_reverse; + FLIP(adjustment_reverse); } // Remember the alignment for the next iteration, but only if steppers move, @@ -359,7 +390,7 @@ void GcodeSuite::G34() { // Decreasing accuracy was detected so move was inverted. // Will match reversed Z steppers on dual steppers. Triple will need more work to map. if (adjustment_reverse) { - z_align_move = -z_align_move; + z_align_move *= -1; if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Z", zstepper + 1, " correction reversed to ", z_align_move); } #endif @@ -387,7 +418,7 @@ void GcodeSuite::G34() { SERIAL_ECHOLNPGM("G34 aborted."); else { SERIAL_ECHOLNPGM("Did ", iteration + (iteration != z_auto_align_iterations), " of ", z_auto_align_iterations); - SERIAL_ECHOLNPGM("Accuracy: ", p_float_t(z_maxdiff, 2)); + SERIAL_ECHOLNPGM("Accuracy: ", p_float_t(z_maxdiff, 3)); } // Stow the probe because the last call to probe.probe_at_point(...) @@ -401,10 +432,24 @@ void GcodeSuite::G34() { // Use the probed height from the last iteration to determine the Z height. // z_measured_min is used, because all steppers are aligned to z_measured_min. // Ideally, this would be equal to the 'z_probe * 0.5f' which was added earlier. - current_position.z -= z_measured_min - (Z_TWEEN_SAFE_CLEARANCE + zoffs); //we shouldn't want to subtract the clearance from here right? (Depends if we added it further up) + if (DEBUGGING(LEVELING)) + DEBUG_ECHOLNPGM( + "z_measured_min: ", p_float_t(z_measured_min, 3), + "Z_TWEEN_SAFE_CLEARANCE: ", p_float_t(Z_TWEEN_SAFE_CLEARANCE, 3), + "zoffs: ", p_float_t(zoffs, 3) + ); + + if (!err_break) + current_position.z -= z_measured_min - (Z_TWEEN_SAFE_CLEARANCE + zoffs); // We shouldn't want to subtract the clearance from here right? (Depends if we added it further up) sync_plan_position(); #endif + #ifdef EVENT_GCODE_AFTER_G34 + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("After G34 G-code: ", F(EVENT_GCODE_AFTER_G34)); + planner.synchronize(); + process_subcommands_now(F(EVENT_GCODE_AFTER_G34)); + #endif + probe.use_probing_tool(false); #if ALL(HAS_LEVELING, RESTORE_LEVELING_AFTER_G34) diff --git a/Marlin/src/gcode/calibrate/G425.cpp b/Marlin/src/gcode/calibrate/G425.cpp index 992d7c38e6..338c776885 100644 --- a/Marlin/src/gcode/calibrate/G425.cpp +++ b/Marlin/src/gcode/calibrate/G425.cpp @@ -100,7 +100,9 @@ enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES, - LIST_N(DOUBLE(SECONDARY_AXES), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM, UMINIMUM, UMAXIMUM, VMINIMUM, VMAXIMUM, WMINIMUM, WMAXIMUM) + LIST_N(DOUBLE(SECONDARY_AXES), + IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM, + UMINIMUM, UMAXIMUM, VMINIMUM, VMAXIMUM, WMINIMUM, WMAXIMUM) }; static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER; @@ -181,20 +183,6 @@ inline void park_above_object(measurements_t &m, const float uncertainty) { #endif -#if !PIN_EXISTS(CALIBRATION) - #include "../../module/probe.h" -#endif - -inline bool read_calibration_pin() { - return ( - #if PIN_EXISTS(CALIBRATION) - READ(CALIBRATION_PIN) != CALIBRATION_PIN_INVERTING - #else - PROBE_TRIGGERED() - #endif - ); -} - /** * Move along axis in the specified dir until the probe value becomes stop_state, * then return the axis value. @@ -205,18 +193,18 @@ inline bool read_calibration_pin() { * fast in - Fast vs. precise measurement */ float measuring_movement(const AxisEnum axis, const int dir, const bool stop_state, const bool fast) { - const float step = fast ? 0.25 : CALIBRATION_MEASUREMENT_RESOLUTION; const feedRate_t mms = fast ? MMM_TO_MMS(CALIBRATION_FEEDRATE_FAST) : MMM_TO_MMS(CALIBRATION_FEEDRATE_SLOW); const float limit = fast ? 50 : 5; destination = current_position; - for (float travel = 0; travel < limit; travel += step) { - destination[axis] += dir * step; - do_blocking_move_to((xyz_pos_t)destination, mms); - planner.synchronize(); - if (read_calibration_pin() == stop_state) break; - } - return destination[axis]; + destination[axis] += dir * limit; + endstops.enable_calibration_probe(true, stop_state); + do_blocking_move_to((xyz_pos_t)destination, mms); + endstops.enable_calibration_probe(false); + endstops.hit_on_purpose(); + set_current_from_steppers_for_axis(axis); + sync_plan_position(); + return current_position[axis]; } /** @@ -346,10 +334,22 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { probe_side(m, uncertainty, TOP); #endif - TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge)); + /** + * Allow Y axis to probe and compute values before X axis (or remaining arbitrary axes) + * to assist with centering in calibration object. Lulzbot saw issues with higher uncertainty + * values where the nozzle was catching on the edges of the cube, and this was intended to help + * ensure the probe object remained centered. + */ TERN_(CALIBRATION_MEASURE_FRONT, probe_side(m, uncertainty, FRONT, probe_top_at_edge)); - TERN_(CALIBRATION_MEASURE_LEFT, probe_side(m, uncertainty, LEFT, probe_top_at_edge)); TERN_(CALIBRATION_MEASURE_BACK, probe_side(m, uncertainty, BACK, probe_top_at_edge)); + + #if HAS_Y_CENTER + m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2; + m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y; + #endif + + TERN_(CALIBRATION_MEASURE_LEFT, probe_side(m, uncertainty, LEFT, probe_top_at_edge)); + TERN_(CALIBRATION_MEASURE_RIGHT, probe_side(m, uncertainty, RIGHT, probe_top_at_edge)); TERN_(CALIBRATION_MEASURE_IMIN, probe_side(m, uncertainty, IMINIMUM, probe_top_at_edge)); TERN_(CALIBRATION_MEASURE_IMAX, probe_side(m, uncertainty, IMAXIMUM, probe_top_at_edge)); TERN_(CALIBRATION_MEASURE_JMIN, probe_side(m, uncertainty, JMINIMUM, probe_top_at_edge)); @@ -365,7 +365,6 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { // Compute the measured center of the calibration object. TERN_(HAS_X_CENTER, m.obj_center.x = (m.obj_side[LEFT] + m.obj_side[RIGHT]) / 2); - TERN_(HAS_Y_CENTER, m.obj_center.y = (m.obj_side[FRONT] + m.obj_side[BACK]) / 2); TERN_(HAS_I_CENTER, m.obj_center.i = (m.obj_side[IMINIMUM] + m.obj_side[IMAXIMUM]) / 2); TERN_(HAS_J_CENTER, m.obj_center.j = (m.obj_side[JMINIMUM] + m.obj_side[JMAXIMUM]) / 2); TERN_(HAS_K_CENTER, m.obj_center.k = (m.obj_side[KMINIMUM] + m.obj_side[KMAXIMUM]) / 2); @@ -376,7 +375,6 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { // Compute the outside diameter of the nozzle at the height // at which it makes contact with the calibration object TERN_(HAS_X_CENTER, m.nozzle_outer_dimension.x = m.obj_side[RIGHT] - m.obj_side[LEFT] - dimensions.x); - TERN_(HAS_Y_CENTER, m.nozzle_outer_dimension.y = m.obj_side[BACK] - m.obj_side[FRONT] - dimensions.y); park_above_object(m, uncertainty); diff --git a/Marlin/src/gcode/calibrate/G76_M871.cpp b/Marlin/src/gcode/calibrate/G76_M871.cpp index bb69b75d50..a909ef4c84 100644 --- a/Marlin/src/gcode/calibrate/G76_M871.cpp +++ b/Marlin/src/gcode/calibrate/G76_M871.cpp @@ -38,47 +38,53 @@ #include "../../lcd/marlinui.h" /** - * G76: calibrate probe and/or bed temperature offsets - * Notes: - * - When calibrating probe, bed temperature is held constant. - * Compensation values are deltas to first probe measurement at probe temp. = 30°C. - * - When calibrating bed, probe temperature is held constant. - * Compensation values are deltas to first probe measurement at bed temp. = 60°C. - * - The hotend will not be heated at any time. - * - On my Průša MK3S clone I put a piece of paper between the probe and the hotend - * so the hotend fan would not cool my probe constantly. Alternatively you could just - * make sure the fan is not running while running the calibration process. + * G76: Probe Temperature Calibration * - * Probe calibration: - * - Moves probe to cooldown point. - * - Heats up bed to 100°C. - * - Moves probe to probing point (1mm above heatbed). - * - Waits until probe reaches target temperature (30°C). - * - Does a z-probing (=base value) and increases target temperature by 5°C. - * - Waits until probe reaches increased target temperature. - * - Does a z-probing (delta to base value will be a compensation value) and increases target temperature by 5°C. - * - Repeats last two steps until max. temperature reached or timeout (i.e. probe does not heat up any further). - * - Compensation values of higher temperatures will be extrapolated (using linear regression first). - * While this is not exact by any means it is still better than simply using the last compensation value. + * Calibrate probe and/or bed temperature offsets. * - * Bed calibration: - * - Moves probe to cooldown point. - * - Heats up bed to 60°C. - * - Moves probe to probing point (1mm above heatbed). - * - Waits until probe reaches target temperature (30°C). - * - Does a z-probing (=base value) and increases bed temperature by 5°C. - * - Moves probe to cooldown point. - * - Waits until probe is below 30°C and bed has reached target temperature. - * - Moves probe to probing point and waits until it reaches target temperature (30°C). - * - Does a z-probing (delta to base value will be a compensation value) and increases bed temperature by 5°C. - * - Repeats last four points until max. bed temperature reached (110°C) or timeout. - * - Compensation values of higher temperatures will be extrapolated (using linear regression first). - * While this is not exact by any means it is still better than simply using the last compensation value. + * Probe calibration: + * - Moves probe to cooldown point. + * - Heats up bed to 100°C. + * - Moves probe to probing point (1mm above heatbed). + * - Waits until probe reaches target temperature (30°C). + * - Does a z-probing (=base value) and increases target temperature by 5°C. + * - Waits until probe reaches increased target temperature. + * - Does a z-probing (delta to base value will be a compensation value) and increases target temperature by 5°C. + * - Repeats last two steps until max. temperature reached or timeout (i.e. probe does not heat up any further). + * - Compensation values of higher temperatures will be extrapolated (using linear regression first). + * While this is not exact by any means it is still better than simply using the last compensation value. * - * G76 [B | P] - * - no flag - Both calibration procedures will be run. - * - `B` - Run bed temperature calibration. - * - `P` - Run probe temperature calibration. + * Bed calibration: + * - Moves probe to cooldown point. + * - Heats up bed to 60°C. + * - Moves probe to probing point (1mm above heatbed). + * - Waits until probe reaches target temperature (30°C). + * - Does a z-probing (=base value) and increases bed temperature by 5°C. + * - Moves probe to cooldown point. + * - Waits until probe is below 30°C and bed has reached target temperature. + * - Moves probe to probing point and waits until it reaches target temperature (30°C). + * - Does a z-probing (delta to base value will be a compensation value) and increases bed temperature by 5°C. + * - Repeats last four points until max. bed temperature reached (110°C) or timeout. + * - Compensation values of higher temperatures will be extrapolated (using linear regression first). + * While this is not exact by any means it is still better than simply using the last compensation value. + * + * Usage: + * G76 [ B | P ] + * + * Parameters: + * None Run Both calibration procedures + * B Calibrate bed only + * P Calibrate probe only + * + * NOTES: + * - When calibrating probe, bed temperature is held constant. + * Compensation values are deltas to first probe measurement at probe temp. = 30°C. + * - When calibrating bed, probe temperature is held constant. + * Compensation values are deltas to first probe measurement at bed temp. = 60°C. + * - The hotend will not be heated at any time. + * - On my Průša MK3S clone I put a piece of paper between the probe and the hotend + * so the hotend fan would not cool my probe constantly. Alternatively you could just + * make sure the fan is not running while running the calibration process. */ #if ALL(PTC_PROBE, PTC_BED) @@ -291,26 +297,30 @@ #endif // PTC_PROBE && PTC_BED /** - * M871: Report / reset temperature compensation offsets. - * Note: This does not affect values in EEPROM until M500. + * M871: Probe Temperature Config * + * Report / reset temperature compensation offsets. + * NOTE: This does not affect values in EEPROM until M500. + * + * Usage: * M871 [ R | B | P | E ] * - * No Parameters - Print current offset values. + * Parameters: + * None Print current offset values * - * Select only one of these flags: - * R - Reset all offsets to zero (i.e., disable compensation). - * B - Manually set offset for bed - * P - Manually set offset for probe - * E - Manually set offset for extruder + * Select only one of these flags: + * R Reset all offsets to zero (i.e., disable compensation) + * B Manually set offset for bed + * P Manually set offset for probe + * E Manually set offset for extruder * - * With B, P, or E: - * I[index] - Index in the array - * V[value] - Adjustment in µm + * With B, P, or E: + * I Index in the array + * V Adjustment in µm */ void GcodeSuite::M871() { - if (parser.seen('R')) { + if (parser.seen_test('R')) { // Reset z-probe offsets to factory defaults ptc.clear_all_offsets(); SERIAL_ECHOLNPGM("Offsets reset to default."); diff --git a/Marlin/src/gcode/calibrate/M100.cpp b/Marlin/src/gcode/calibrate/M100.cpp index c05fe12fc3..ddfed4afe1 100644 --- a/Marlin/src/gcode/calibrate/M100.cpp +++ b/Marlin/src/gcode/calibrate/M100.cpp @@ -31,36 +31,36 @@ #include "../../MarlinCore.h" // for idle() /** - * M100 Free Memory Watcher + * M100: Free Memory Watcher * * This code watches the free memory block between the bottom of the heap and the top of the stack. * This memory block is initialized and watched via the M100 command. * - * M100 I Initializes the free memory block and prints vitals statistics about the area + * Parameters: + * I Initializes the free memory block and prints vitals statistics about the area * - * M100 F Identifies how much of the free memory block remains free and unused. It also - * detects and reports any corruption within the free memory block that may have - * happened due to errant firmware. + * F Identifies how much of the free memory block remains free and unused. It also + * detects and reports any corruption within the free memory block that may have + * happened due to errant firmware. * - * M100 D Does a hex display of the free memory block along with a flag for any errant - * data that does not match the expected value. + * D Does a hex display of the free memory block along with a flag for any errant + * data that does not match the expected value. * - * M100 C x Corrupts x locations within the free memory block. This is useful to check the - * correctness of the M100 F and M100 D commands. + * C x Corrupts x locations within the free memory block. This is useful to check the + * correctness of the M100 F and M100 D commands. * * Also, there are two support functions that can be called from a developer's C code. - * - * uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start); - * void M100_dump_routine(FSTR_P const title, const char * const start, const uintptr_t size); + * uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start); + * void M100_dump_routine(FSTR_P const title, const char * const start, const uintptr_t size); * * Initial version by Roxy-3D */ -#define M100_FREE_MEMORY_DUMPER // Enable for the `M100 D` Dump sub-command -#define M100_FREE_MEMORY_CORRUPTOR // Enable for the `M100 C` Corrupt sub-command +#define M100_FREE_MEMORY_DUMPER // Enable for the 'M100 D' Dump sub-command +#define M100_FREE_MEMORY_CORRUPTOR // Enable for the 'M100 C' Corrupt sub-command #define TEST_BYTE ((char) 0xE5) -#if ANY(__AVR__, IS_32BIT_TEENSY) +#if ANY(__AVR__, IS_32BIT_TEENSY) && !IS_TEENSY_40_41 extern char __bss_end; char *end_bss = &__bss_end, diff --git a/Marlin/src/gcode/calibrate/M425.cpp b/Marlin/src/gcode/calibrate/M425.cpp index 22d71aba58..5dab92a307 100644 --- a/Marlin/src/gcode/calibrate/M425.cpp +++ b/Marlin/src/gcode/calibrate/M425.cpp @@ -94,7 +94,7 @@ void GcodeSuite::M425() { #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) SERIAL_ECHOPGM(" Average measured backlash (mm):"); if (backlash.has_any_measurement()) { - LOOP_NUM_AXES(a) if (axis_can_calibrate(a) && backlash.has_measurement(AxisEnum(a))) { + LOOP_NUM_AXES(a) if (axis_can_calibrate(a) && backlash.has_measurement((AxisEnum)a)) { SERIAL_ECHOPGM_P((PGM_P)pgm_read_ptr(&SP_AXIS_STR[a]), backlash.get_measurement((AxisEnum)a)); } } @@ -109,25 +109,26 @@ void GcodeSuite::M425_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading_etc(forReplay, F(STR_BACKLASH_COMPENSATION)); - SERIAL_ECHOLNPGM_P( + SERIAL_ECHOPGM_P( PSTR(" M425 F"), backlash.get_correction() #ifdef BACKLASH_SMOOTHING_MM , PSTR(" S"), LINEAR_UNIT(backlash.get_smoothing_mm()) #endif - #if NUM_AXES - , LIST_N(DOUBLE(NUM_AXES), - SP_X_STR, LINEAR_UNIT(backlash.get_distance_mm(X_AXIS)), - SP_Y_STR, LINEAR_UNIT(backlash.get_distance_mm(Y_AXIS)), - SP_Z_STR, LINEAR_UNIT(backlash.get_distance_mm(Z_AXIS)), - SP_I_STR, I_AXIS_UNIT(backlash.get_distance_mm(I_AXIS)), - SP_J_STR, J_AXIS_UNIT(backlash.get_distance_mm(J_AXIS)), - SP_K_STR, K_AXIS_UNIT(backlash.get_distance_mm(K_AXIS)), - SP_U_STR, U_AXIS_UNIT(backlash.get_distance_mm(U_AXIS)), - SP_V_STR, V_AXIS_UNIT(backlash.get_distance_mm(V_AXIS)), - SP_W_STR, W_AXIS_UNIT(backlash.get_distance_mm(W_AXIS)) - ) - #endif ); + #if NUM_AXES + SERIAL_ECHOPGM_P(NUM_AXIS_PAIRED_LIST( + SP_X_STR, LINEAR_UNIT(backlash.get_distance_mm(X_AXIS)), + SP_Y_STR, LINEAR_UNIT(backlash.get_distance_mm(Y_AXIS)), + SP_Z_STR, LINEAR_UNIT(backlash.get_distance_mm(Z_AXIS)), + SP_I_STR, I_AXIS_UNIT(backlash.get_distance_mm(I_AXIS)), + SP_J_STR, J_AXIS_UNIT(backlash.get_distance_mm(J_AXIS)), + SP_K_STR, K_AXIS_UNIT(backlash.get_distance_mm(K_AXIS)), + SP_U_STR, U_AXIS_UNIT(backlash.get_distance_mm(U_AXIS)), + SP_V_STR, V_AXIS_UNIT(backlash.get_distance_mm(V_AXIS)), + SP_W_STR, W_AXIS_UNIT(backlash.get_distance_mm(W_AXIS)) + )); + #endif + SERIAL_EOL(); } #endif // BACKLASH_GCODE diff --git a/Marlin/src/gcode/calibrate/M48.cpp b/Marlin/src/gcode/calibrate/M48.cpp index 8dd951e054..0c4355f5b1 100644 --- a/Marlin/src/gcode/calibrate/M48.cpp +++ b/Marlin/src/gcode/calibrate/M48.cpp @@ -124,7 +124,7 @@ void GcodeSuite::M48() { max = -99999.9, // Largest value sampled so far sample_set[n_samples]; // Storage for sampled values - auto dev_report = [](const bool verbose, const_float_t mean, const_float_t sigma, const_float_t min, const_float_t max, const bool final=false) { + auto dev_report = [](const bool verbose, const float mean, const float sigma, const float min, const float max, const bool final=false) { if (verbose) { SERIAL_ECHOPGM("Mean: ", p_float_t(mean, 6)); if (!final) SERIAL_ECHOPGM(" Sigma: ", p_float_t(sigma, 6)); @@ -261,7 +261,19 @@ void GcodeSuite::M48() { #if HAS_STATUS_MESSAGE // Display M48 results in the status bar - ui.set_status_and_level(MString<30>(GET_TEXT_F(MSG_M48_DEVIATION), F(": "), w_float_t(sigma, 2, 6))); + if (MAX_MESSAGE_SIZE <= 20) { + // 12345678901234567890 + // Deviation: 0.123456 + ui.set_status_and_level(TS(GET_TEXT_F(MSG_M48_DEVIATION), F(": "), w_float_t(sigma, 2, 6))); + } else if (MAX_MESSAGE_SIZE <= 30) { + // 123456789012345678901234567890 + // Dev:0.12345, Max delta:0.12345 + ui.set_status_and_level(TS(GET_TEXT_F(MSG_M48_DEV), ':', w_float_t(sigma, 2, 5), F(", "), GET_TEXT(MSG_M48_MAX_DELTA), ':', w_float_t(_MAX(mean - min, max - mean), 2, 5))); + } else { + // 1234567890123456789012345678901234567890 + // Deviation: 1.23456, Max delta: 1.23456 + ui.set_status_and_level(TS(GET_TEXT_F(MSG_M48_DEVIATION), F(": "), w_float_t(sigma, 2, 6), F(", "), GET_TEXT(MSG_M48_MAX_DELTA), F(": "), w_float_t(_MAX(mean - min, max - mean), 2, 6))); + } #endif } diff --git a/Marlin/src/gcode/calibrate/M666.cpp b/Marlin/src/gcode/calibrate/M666.cpp index 4186290154..2584782cf3 100644 --- a/Marlin/src/gcode/calibrate/M666.cpp +++ b/Marlin/src/gcode/calibrate/M666.cpp @@ -39,7 +39,15 @@ #if ENABLED(DELTA) /** - * M666: Set delta endstop adjustment + * M666: Set Delta endstop adjustments + * + * Adjust the endstop offsets on a Delta printer. + * + * Parameters: + * None Report current offsets + * X Adjustment for the X actuator endstop + * Y Adjustment for the Y actuator endstop + * Z Adjustment for the Z actuator endstop */ void GcodeSuite::M666() { DEBUG_SECTION(log_M666, "M666", DEBUGGING(LEVELING)); @@ -74,14 +82,22 @@ #else /** - * M666: Set Dual Endstops offsets for X, Y, and/or Z. - * With no parameters report current offsets. + * M666: Set Dual Endstop Offsets * - * For Triple / Quad Z Endstops: - * Set Z2 Only: M666 S2 Z - * Set Z3 Only: M666 S3 Z - * Set Z4 Only: M666 S4 Z - * Set All: M666 Z + * Adjust the offsets for dual (or multiple) endstops. + * + * Parameters: + * None Report current offsets + * X Offset for the X axis endstops + * Y Offset for the Y axis endstops + * Z Offset for the Z axis endstops + * + * Example: + * For Triple / Quad Z Endstops: + * M666 S2 Z ; Set Z2 Only + * M666 S3 Z ; Set Z3 Only + * M666 S4 Z ; Set Z4 Only + * M666 Z ; Set All */ void GcodeSuite::M666() { if (!parser.seen_any()) return M666_report(); diff --git a/Marlin/src/gcode/calibrate/M852.cpp b/Marlin/src/gcode/calibrate/M852.cpp index 001160ae72..7cd9aaf718 100644 --- a/Marlin/src/gcode/calibrate/M852.cpp +++ b/Marlin/src/gcode/calibrate/M852.cpp @@ -28,12 +28,16 @@ #include "../../module/planner.h" /** - * M852: Get or set the machine skew factors. Reports current values with no arguments. + * M852: Bed Skew Compensation * - * S[xy_factor] - Alias for 'I' - * I[xy_factor] - New XY skew factor - * J[xz_factor] - New XZ skew factor - * K[yz_factor] - New YZ skew factor + * Get or set the machine skew factors; correct for misalignment + * + * Parameters: + * None Report current values + * S Alias for 'I' + * I New XY skew factor + * J New XZ skew factor + * K New YZ skew factor */ void GcodeSuite::M852() { if (!parser.seen("SIJK")) return M852_report(); diff --git a/Marlin/src/gcode/config/M200-M205.cpp b/Marlin/src/gcode/config/M200-M205.cpp index a0466fbeab..53d0b4b4a8 100644 --- a/Marlin/src/gcode/config/M200-M205.cpp +++ b/Marlin/src/gcode/config/M200-M205.cpp @@ -24,7 +24,7 @@ #include "../../MarlinCore.h" #include "../../module/planner.h" -#if DISABLED(NO_VOLUMETRICS) +#if HAS_VOLUMETRIC_EXTRUSION /** * M200: Set filament diameter and set E axis units to cubic units @@ -78,10 +78,10 @@ TERN_(MARLIN_SMALL_BUILD, return); if (!forReplay) { - report_heading(forReplay, F(STR_FILAMENT_SETTINGS), false); + report_heading(false, F(STR_FILAMENT_SETTINGS), false); if (!parser.volumetric_enabled) SERIAL_ECHOPGM(" (Disabled):"); SERIAL_EOL(); - report_echo_start(forReplay); + report_echo_start(false); } #if EXTRUDERS == 1 @@ -107,7 +107,7 @@ #endif } -#endif // !NO_VOLUMETRICS +#endif // HAS_VOLUMETRIC_EXTRUSION /** * M201: Set max acceleration in units/s^2 for print moves. @@ -124,8 +124,13 @@ * S : Speed factor percentage. */ void GcodeSuite::M201() { - if (!parser.seen("T" STR_AXES_LOGICAL TERN_(XY_FREQUENCY_LIMIT, "FS"))) + if (!parser.seen("T" STR_AXES_LOGICAL + #ifdef XY_FREQUENCY_LIMIT + "FS" + #endif + )) { return M201_report(); + } const int8_t target_extruder = get_target_extruder_from_command(); if (target_extruder < 0) return; @@ -147,30 +152,37 @@ void GcodeSuite::M201_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading_etc(forReplay, F(STR_MAX_ACCELERATION)); + + bool eol = false; + #if NUM_AXES - SERIAL_ECHOPGM_P( - LIST_N(DOUBLE(NUM_AXES), - PSTR(" M201 X"), LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[X_AXIS]), - SP_Y_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Y_AXIS]), - SP_Z_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Z_AXIS]), - SP_I_STR, I_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[I_AXIS]), - SP_J_STR, J_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[J_AXIS]), - SP_K_STR, K_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[K_AXIS]), - SP_U_STR, U_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[U_AXIS]), - SP_V_STR, V_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[V_AXIS]), - SP_W_STR, W_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[W_AXIS]) - ) - ); + eol = true; + SERIAL_ECHOPGM_P(NUM_AXIS_PAIRED_LIST( + PSTR(" M201 X"), LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[X_AXIS]), + SP_Y_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Y_AXIS]), + SP_Z_STR, LINEAR_UNIT(planner.settings.max_acceleration_mm_per_s2[Z_AXIS]), + SP_I_STR, I_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[I_AXIS]), + SP_J_STR, J_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[J_AXIS]), + SP_K_STR, K_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[K_AXIS]), + SP_U_STR, U_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[U_AXIS]), + SP_V_STR, V_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[V_AXIS]), + SP_W_STR, W_AXIS_UNIT(planner.settings.max_acceleration_mm_per_s2[W_AXIS]) + )); #endif #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS) + eol = true; SERIAL_ECHOPGM_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_acceleration_mm_per_s2[E_AXIS])); #endif - #if NUM_AXES || (HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)) - SERIAL_EOL(); + #ifdef XY_FREQUENCY_LIMIT + eol = true; + SERIAL_ECHOPGM_P(PSTR(" F"), planner.xy_freq_limit_hz); + SERIAL_ECHOPGM_P(PSTR(" S"), (planner.xy_freq_min_speed_factor * 100)); #endif + if (eol) SERIAL_EOL(); + #if ENABLED(DISTINCT_E_FACTORS) for (uint8_t i = 0; i < E_STEPPERS; ++i) { report_echo_start(forReplay); @@ -205,33 +217,34 @@ void GcodeSuite::M203_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading_etc(forReplay, F(STR_MAX_FEEDRATES)); + + bool eol = false; + #if NUM_AXES - SERIAL_ECHOPGM_P( - LIST_N(DOUBLE(NUM_AXES), - PSTR(" M203 X"), LINEAR_UNIT(planner.settings.max_feedrate_mm_s[X_AXIS]), - SP_Y_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Y_AXIS]), - SP_Z_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Z_AXIS]), - SP_I_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[I_AXIS]), - SP_J_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[J_AXIS]), - SP_K_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[K_AXIS]), - SP_U_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[U_AXIS]), - SP_V_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[V_AXIS]), - SP_W_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[W_AXIS]) - ) - ); + eol = true; + SERIAL_ECHOPGM_P(NUM_AXIS_PAIRED_LIST( + PSTR(" M203 X"), LINEAR_UNIT(planner.settings.max_feedrate_mm_s[X_AXIS]), + SP_Y_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Y_AXIS]), + SP_Z_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[Z_AXIS]), + SP_I_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[I_AXIS]), + SP_J_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[J_AXIS]), + SP_K_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[K_AXIS]), + SP_U_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[U_AXIS]), + SP_V_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[V_AXIS]), + SP_W_STR, LINEAR_UNIT(planner.settings.max_feedrate_mm_s[W_AXIS]) + )); #endif #if HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS) + eol = true; SERIAL_ECHOPGM_P(SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS])); #endif - #if NUM_AXES || (HAS_EXTRUDERS && DISABLED(DISTINCT_E_FACTORS)) - SERIAL_EOL(); - #endif + if (eol) SERIAL_EOL(); #if ENABLED(DISTINCT_E_FACTORS) for (uint8_t i = 0; i < E_STEPPERS; ++i) { - if (!forReplay) SERIAL_ECHO_START(); + report_echo_start(forReplay); SERIAL_ECHOLNPGM_P( PSTR(" M203 T"), i , SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS_N(i)]) @@ -360,7 +373,7 @@ void GcodeSuite::M205_report(const bool forReplay/*=true*/) { , PSTR(" J"), LINEAR_UNIT(planner.junction_deviation_mm) #endif #if ENABLED(CLASSIC_JERK) && NUM_AXES - , LIST_N(DOUBLE(NUM_AXES), + , NUM_AXIS_PAIRED_LIST( SP_X_STR, LINEAR_UNIT(planner.max_jerk.x), SP_Y_STR, LINEAR_UNIT(planner.max_jerk.y), SP_Z_STR, LINEAR_UNIT(planner.max_jerk.z), diff --git a/Marlin/src/gcode/config/M210.cpp b/Marlin/src/gcode/config/M210.cpp new file mode 100644 index 0000000000..f07e009b4a --- /dev/null +++ b/Marlin/src/gcode/config/M210.cpp @@ -0,0 +1,98 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../../inc/MarlinConfigPre.h" + +#if ENABLED(EDITABLE_HOMING_FEEDRATE) + +#include "../gcode.h" +#include "../../module/motion.h" + +/** + * M210 - Set homing feedrate for one or more axes + * in current units (in/mm) per minute + * + * X[feedrate] Set X axis homing feedrate + * Y[feedrate] Set Y axis homing feedrate + * Z[feedrate] Set Z axis homing feedrate + * A[feedrate] Set I axis homing feedrate (configured axis name applies) + * B[feedrate] Set J axis homing feedrate (configured axis name applies) + * C[feedrate] Set K axis homing feedrate (configured axis name applies) + * U[feedrate] Set U axis homing feedrate (configured axis name applies) + * V[feedrate] Set V axis homing feedrate (configured axis name applies) + * W[feedrate] Set W axis homing feedrate (configured axis name applies) + * + * With no arguments, report the current offsets. + */ +void GcodeSuite::M210() { + if (!parser.seen_any()) + return M210_report(); + + #if HAS_X_AXIS + if (parser.floatval(AXIS1_PARAM) > 0) homing_feedrate_mm_m.x = parser.value_axis_units(X_AXIS); + #endif + #if HAS_Y_AXIS + if (parser.floatval(AXIS2_PARAM) > 0) homing_feedrate_mm_m.y = parser.value_axis_units(Y_AXIS); + #endif + #if HAS_Z_AXIS + if (parser.floatval(AXIS3_PARAM) > 0) homing_feedrate_mm_m.z = parser.value_axis_units(Z_AXIS); + #endif + #if HAS_I_AXIS + if (parser.floatval(AXIS4_PARAM) > 0) homing_feedrate_mm_m.i = parser.value_axis_units(I_AXIS); + #endif + #if HAS_J_AXIS + if (parser.floatval(AXIS5_PARAM) > 0) homing_feedrate_mm_m.j = parser.value_axis_units(J_AXIS); + #endif + #if HAS_K_AXIS + if (parser.floatval(AXIS6_PARAM) > 0) homing_feedrate_mm_m.k = parser.value_axis_units(K_AXIS); + #endif + #if HAS_U_AXIS + if (parser.floatval(AXIS7_PARAM) > 0) homing_feedrate_mm_m.u = parser.value_axis_units(U_AXIS); + #endif + #if HAS_V_AXIS + if (parser.floatval(AXIS8_PARAM) > 0) homing_feedrate_mm_m.v = parser.value_axis_units(V_AXIS); + #endif + #if HAS_W_AXIS + if (parser.floatval(AXIS9_PARAM) > 0) homing_feedrate_mm_m.w = parser.value_axis_units(W_AXIS); + #endif +} + +void GcodeSuite::M210_report(const bool forReplay/*=true*/) { + TERN_(MARLIN_SMALL_BUILD, return); + + report_heading_etc(forReplay, F(STR_HOMING_FEEDRATE)); + + SERIAL_ECHOPGM(" M210"); + SERIAL_ECHOLNPGM_P(NUM_AXIS_PAIRED_LIST( + SP_X_STR, X_AXIS_UNIT(homing_feedrate_mm_m.x), + SP_Y_STR, Y_AXIS_UNIT(homing_feedrate_mm_m.y), + SP_Z_STR, Z_AXIS_UNIT(homing_feedrate_mm_m.z), + SP_I_STR, I_AXIS_UNIT(homing_feedrate_mm_m.i), + SP_J_STR, J_AXIS_UNIT(homing_feedrate_mm_m.j), + SP_K_STR, K_AXIS_UNIT(homing_feedrate_mm_m.k), + SP_U_STR, U_AXIS_UNIT(homing_feedrate_mm_m.u), + SP_V_STR, V_AXIS_UNIT(homing_feedrate_mm_m.v), + SP_W_STR, W_AXIS_UNIT(homing_feedrate_mm_m.w) + )); +} + +#endif // EDITABLE_HOMING_FEEDRATE diff --git a/Marlin/src/gcode/config/M218.cpp b/Marlin/src/gcode/config/M218.cpp index 006f9a1d2c..7d167e502d 100644 --- a/Marlin/src/gcode/config/M218.cpp +++ b/Marlin/src/gcode/config/M218.cpp @@ -65,7 +65,7 @@ void GcodeSuite::M218() { void GcodeSuite::M218_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); - report_heading_etc(forReplay, F(STR_HOTEND_OFFSETS)); + report_heading(forReplay, F(STR_HOTEND_OFFSETS)); for (uint8_t e = 1; e < HOTENDS; ++e) { report_echo_start(forReplay); SERIAL_ECHOLNPGM_P( diff --git a/Marlin/src/gcode/config/M220.cpp b/Marlin/src/gcode/config/M220.cpp index 6797df25d5..0d1e204800 100644 --- a/Marlin/src/gcode/config/M220.cpp +++ b/Marlin/src/gcode/config/M220.cpp @@ -24,16 +24,15 @@ #include "../../module/motion.h" /** - * M220: Set speed percentage factor, aka "Feed Rate" + * M220: Set Feedrate Percentage * - * Parameters - * S : Set the feed rate percentage factor + * Parameters: + * None Report the current speed percentage factor + * S Set the feed rate percentage factor * - * Report the current speed percentage factor if no parameter is specified - * - * For MMU2 and MMU2S devices... - * B : Flag to back up the current factor - * R : Flag to restore the last-saved factor + * For MMU2 and MMU2S devices: + * B Back up the current factor + * R Restore the last-saved factor */ void GcodeSuite::M220() { if (!parser.seen_any()) { diff --git a/Marlin/src/gcode/config/M301.cpp b/Marlin/src/gcode/config/M301.cpp index fe0eef772f..a47c03a8d4 100644 --- a/Marlin/src/gcode/config/M301.cpp +++ b/Marlin/src/gcode/config/M301.cpp @@ -28,22 +28,22 @@ #include "../../module/temperature.h" /** - * M301: Set PID parameters P I D (and optionally C, L) + * M301: Set Hotend PID * - * E[extruder] Default: 0 + * Set PID parameters P I D (and optionally C, L) * - * P[float] Kp term - * I[float] Ki term (unscaled) - * D[float] Kd term (unscaled) + * Parameters: + * E Default: 0 + * P Kp term + * I Ki term (unscaled) + * D Kd term (unscaled) * - * With PID_EXTRUSION_SCALING: + * With PID_EXTRUSION_SCALING: + * C Kc term + * L LPQ length * - * C[float] Kc term - * L[int] LPQ length - * - * With PID_FAN_SCALING: - * - * F[float] Kf term + * With PID_FAN_SCALING: + * F Kf term */ void GcodeSuite::M301() { // multi-extruder PID patch: M301 updates or prints a single extruder's PID values diff --git a/Marlin/src/gcode/config/M43.cpp b/Marlin/src/gcode/config/M43.cpp index 467a31cf44..deeabf8fac 100644 --- a/Marlin/src/gcode/config/M43.cpp +++ b/Marlin/src/gcode/config/M43.cpp @@ -63,34 +63,36 @@ inline void toggle_pins() { for (uint8_t i = start; i <= end; ++i) { pin_t pin = GET_PIN_MAP_PIN_M43(i); - if (!VALID_PIN(pin)) continue; + if (!isValidPin(pin)) continue; if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) { - report_pin_state_extended(pin, ignore_protection, true, F("Untouched ")); + printPinStateExt(pin, ignore_protection, true, F("Untouched ")); SERIAL_EOL(); } else { hal.watchdog_refresh(); - report_pin_state_extended(pin, ignore_protection, true, F("Pulsing ")); - #ifdef __STM32F1__ - const auto prior_mode = _GET_MODE(i); - #else - const bool prior_mode = GET_PINMODE(pin); - #endif + printPinStateExt(pin, ignore_protection, true, F("Pulsing ")); + const auto prior_mode = ( + #ifdef __STM32F1__ + _GET_MODE(i) + #else + getValidPinMode(pin) + #endif + ); #if AVR_AT90USB1286_FAMILY // Teensy IDEs don't know about these pins so must use FASTIO - if (pin == TEENSY_E2) { - SET_OUTPUT(TEENSY_E2); + if (pin == PIN_E2) { + SET_OUTPUT(PIN_E2); for (int16_t j = 0; j < repeat; j++) { - WRITE(TEENSY_E2, LOW); safe_delay(wait); - WRITE(TEENSY_E2, HIGH); safe_delay(wait); - WRITE(TEENSY_E2, LOW); safe_delay(wait); + WRITE(PIN_E2, LOW); safe_delay(wait); + WRITE(PIN_E2, HIGH); safe_delay(wait); + WRITE(PIN_E2, LOW); safe_delay(wait); } } - else if (pin == TEENSY_E3) { - SET_OUTPUT(TEENSY_E3); + else if (pin == PIN_E3) { + SET_OUTPUT(PIN_E3); for (int16_t j = 0; j < repeat; j++) { - WRITE(TEENSY_E3, LOW); safe_delay(wait); - WRITE(TEENSY_E3, HIGH); safe_delay(wait); - WRITE(TEENSY_E3, LOW); safe_delay(wait); + WRITE(PIN_E3, LOW); safe_delay(wait); + WRITE(PIN_E3, HIGH); safe_delay(wait); + WRITE(PIN_E3, LOW); safe_delay(wait); } } else @@ -152,37 +154,36 @@ inline void servo_probe_test() { SET_INPUT_PULLUP(PROBE_TEST_PIN); - // First, check for a probe that recognizes an advanced BLTouch sequence. - // In addition to STOW and DEPLOY, it uses SW MODE (and RESET in the beginning) - // to see if this is one of the following: BLTOUCH Classic 1.2, 1.3, or - // BLTouch Smart 1.0, 2.0, 2.2, 3.0, 3.1. But only if the user has actually - // configured a BLTouch as being present. If the user has not configured this, - // the BLTouch will be detected in the last phase of these tests (see further on). - bool blt = false; - // This code will try to detect a BLTouch probe or clone + /** + * This code will try to detect a BLTouch probe or clone. + * First, check for a probe that recognizes an advanced BLTouch sequence. + * In addition to STOW and DEPLOY, it uses SW MODE (and RESET in the beginning) + * to see if this is one of the following: BLTOUCH Classic 1.2, 1.3, or + * BLTouch Smart 1.0, 2.0, 2.2, 3.0, 3.1. But only if the user has actually + * configured a BLTouch as being present. If the user has not configured this, + * the BLTouch will be detected in the last phase of these tests (see further on). + */ #if ENABLED(BLTOUCH) - SERIAL_ECHOLNPGM(". Check for BLTOUCH"); - bltouch._reset(); - bltouch._stow(); - if (!PROBE_TRIGGERED()) { - bltouch._set_SW_mode(); - if (PROBE_TRIGGERED()) { - bltouch._deploy(); - if (!PROBE_TRIGGERED()) { - bltouch._stow(); - SERIAL_ECHOLNPGM("= BLTouch Classic 1.2, 1.3, Smart 1.0, 2.0, 2.2, 3.0, 3.1 detected."); - // Check for a 3.1 by letting the user trigger it, later - blt = true; - } - } - } + bool blt = false; + do { + SERIAL_ECHOLNPGM(". Check for BLTOUCH"); + bltouch._reset(); + bltouch._stow(); if ( PROBE_TRIGGERED()) break; + bltouch._set_SW_mode(); if (!PROBE_TRIGGERED()) break; + bltouch._deploy(); if ( PROBE_TRIGGERED()) break; + bltouch._stow(); + SERIAL_ECHOLNPGM("= BLTouch Classic 1.2, 1.3, Smart 1.0, 2.0, 2.2, 3.0, 3.1 detected."); + blt = true; // Check for a 3.1 by letting the user trigger it, later + } while(0); + #else + static constexpr bool blt = false; #endif // The following code is common to all kinds of servo probes. // Since it could be a real servo or a BLTouch (any kind) or a clone, // use only "common" functions - i.e. SERVO_MOVE. No bltouch.xxxx stuff. - // If it is already recognised as a being a BLTouch, no need for this test + // If it is already recognized as a being a BLTouch, no need for this test if (!blt) { // DEPLOY and STOW 4 times and see if the signal follows // Then it is a mechanical switch @@ -326,14 +327,14 @@ void GcodeSuite::M43() { bool can_watch = false; for (uint8_t i = first_pin; i <= last_pin; ++i) { pin_t pin = GET_PIN_MAP_PIN_M43(i); - if (!VALID_PIN(pin)) continue; + if (!isValidPin(pin)) continue; if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) continue; can_watch = true; pinMode(pin, INPUT_PULLUP); delay(1); /* - if (IS_ANALOG(pin)) - pin_state[pin - first_pin] = analogRead(DIGITAL_PIN_TO_ANALOG_PIN(pin)); // int16_t pin_state[...] + if (isAnalogPin(pin)) + pin_state[pin - first_pin] = analogRead(digitalPinToAnalogIndex(pin)); // int16_t pin_state[...] else //*/ pin_state[i - first_pin] = extDigitalRead(pin); @@ -369,17 +370,17 @@ void GcodeSuite::M43() { for (;;) { for (uint8_t i = first_pin; i <= last_pin; ++i) { const pin_t pin = GET_PIN_MAP_PIN_M43(i); - if (!VALID_PIN(pin)) continue; + if (!isValidPin(pin)) continue; if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) continue; const byte val = /* - IS_ANALOG(pin) - ? analogRead(DIGITAL_PIN_TO_ANALOG_PIN(pin)) : // int16_t val + isAnalogPin(pin) + ? analogRead(digitalPinToAnalogIndex(pin)) : // int16_t val : //*/ extDigitalRead(pin); if (val != pin_state[i - first_pin]) { - report_pin_state_extended(pin, ignore_protection, true); + printPinStateExt(pin, ignore_protection, true); pin_state[i - first_pin] = val; } } @@ -398,7 +399,7 @@ void GcodeSuite::M43() { // Report current state of selected pin(s) for (uint8_t i = first_pin; i <= last_pin; ++i) { const pin_t pin = GET_PIN_MAP_PIN_M43(i); - if (VALID_PIN(pin)) report_pin_state_extended(pin, ignore_protection, true); + if (isValidPin(pin)) printPinStateExt(pin, ignore_protection, true); } } } diff --git a/Marlin/src/gcode/config/M550.cpp b/Marlin/src/gcode/config/M550.cpp new file mode 100644 index 0000000000..88712786da --- /dev/null +++ b/Marlin/src/gcode/config/M550.cpp @@ -0,0 +1,59 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ + +#include "../../inc/MarlinConfig.h" + +#if ENABLED(CONFIGURABLE_MACHINE_NAME) + +#include "../gcode.h" +#include "../../MarlinCore.h" +#include "../../lcd/marlinui.h" + +/** + * M550: Set machine name + * + * Parameters: + * P "" Set the name using the 'P' parameter (RepRapFirmware) + * "" Set the name using the "string" parameter + */ +void GcodeSuite::M550() { + bool did_set = true; + + if (parser.seenval('P')) + machine_name = parser.value_string(); + else if (TERN(GCODE_QUOTED_STRINGS, false, parser.seen('P'))) + machine_name = parser.string_arg[0] == 'P' ? &parser.string_arg[1] : parser.string_arg; + else if (parser.has_string()) + machine_name = parser.string_arg; + else + did_set = false; + + if (did_set) { + machine_name.trim(); + ui.reset_status(false); + } + else + SERIAL_ECHOLNPGM("RepRap name: ", &machine_name); + +} + +#endif // CONFIGURABLE_MACHINE_NAME diff --git a/Marlin/src/gcode/config/M92.cpp b/Marlin/src/gcode/config/M92.cpp index 2a5eb30f55..48c3c5f2f0 100644 --- a/Marlin/src/gcode/config/M92.cpp +++ b/Marlin/src/gcode/config/M92.cpp @@ -101,7 +101,7 @@ void GcodeSuite::M92_report(const bool forReplay/*=true*/, const int8_t e/*=-1*/ report_heading_etc(forReplay, F(STR_STEPS_PER_UNIT)); #if NUM_AXES #define PRINT_EOL - SERIAL_ECHOPGM_P(LIST_N(DOUBLE(NUM_AXES), + SERIAL_ECHOPGM_P(NUM_AXIS_PAIRED_LIST( PSTR(" M92 X"), LINEAR_UNIT(planner.settings.axis_steps_per_mm[X_AXIS]), SP_Y_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Y_AXIS]), SP_Z_STR, LINEAR_UNIT(planner.settings.axis_steps_per_mm[Z_AXIS]), diff --git a/Marlin/src/gcode/control/M10-M11.cpp b/Marlin/src/gcode/control/M10_M11.cpp similarity index 95% rename from Marlin/src/gcode/control/M10-M11.cpp rename to Marlin/src/gcode/control/M10_M11.cpp index d5a69dcfcc..8fd299c546 100644 --- a/Marlin/src/gcode/control/M10-M11.cpp +++ b/Marlin/src/gcode/control/M10_M11.cpp @@ -20,6 +20,11 @@ * */ +/** + * gcode/control/M10_M11.cpp + * Air Evacuation + */ + #include "../../inc/MarlinConfig.h" #if ENABLED(AIR_EVACUATION) diff --git a/Marlin/src/gcode/control/M17_M18_M84.cpp b/Marlin/src/gcode/control/M17_M18_M84.cpp index 1742d288b3..90563889f3 100644 --- a/Marlin/src/gcode/control/M17_M18_M84.cpp +++ b/Marlin/src/gcode/control/M17_M18_M84.cpp @@ -78,7 +78,7 @@ void do_enable(const stepper_flags_t to_enable) { // Enable all flagged axes LOOP_NUM_AXES(a) { if (TEST(shall_enable, a)) { - stepper.enable_axis(AxisEnum(a)); // Mark and enable the requested axis + stepper.enable_axis((AxisEnum)a); // Mark and enable the requested axis DEBUG_ECHOLNPGM("Enabled ", AXIS_CHAR(a), " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... Enabled: ", hex_word(stepper.axis_enabled.bits)); also_enabled |= enable_overlap[a]; } @@ -153,7 +153,7 @@ void try_to_disable(const stepper_flags_t to_disable) { LOOP_NUM_AXES(a) if (TEST(to_disable.bits, a)) { DEBUG_ECHOPGM("Try to disable ", AXIS_CHAR(a), " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... "); - if (stepper.disable_axis(AxisEnum(a))) { // Mark the requested axis and request to disable + if (stepper.disable_axis((AxisEnum)a)) { // Mark the requested axis and request to disable DEBUG_ECHOPGM("OK"); still_enabled &= ~(_BV(a) | enable_overlap[a]); // If actually disabled, clear one or more tracked bits } diff --git a/Marlin/src/gcode/control/M211.cpp b/Marlin/src/gcode/control/M211.cpp index 471ca6c448..e51a9d5297 100644 --- a/Marlin/src/gcode/control/M211.cpp +++ b/Marlin/src/gcode/control/M211.cpp @@ -43,8 +43,7 @@ void GcodeSuite::M211_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading_etc(forReplay, F(STR_SOFT_ENDSTOPS)); - SERIAL_ECHOPGM(" M211 S", AS_DIGIT(soft_endstop._enabled), " ; "); - serialprintln_onoff(soft_endstop._enabled); + SERIAL_ECHOLNPGM(" M211 S", AS_DIGIT(soft_endstop._enabled), " ; ", ON_OFF(soft_endstop._enabled)); report_echo_start(forReplay); const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(), diff --git a/Marlin/src/gcode/control/M3-M5.cpp b/Marlin/src/gcode/control/M3-M5.cpp index ec24cf8a65..9b6a2fb2f7 100644 --- a/Marlin/src/gcode/control/M3-M5.cpp +++ b/Marlin/src/gcode/control/M3-M5.cpp @@ -47,6 +47,7 @@ * * Parameters: * S - Set power. S0 will turn the spindle/laser off. + * O - Set power in PWM units 0-255 * * If no PWM pin is defined then M3/M4 just turns it on or off. * @@ -91,7 +92,11 @@ void GcodeSuite::M3_M4(const bool is_M4) { auto get_s_power = [] { if (parser.seenval('S')) { const float v = parser.value_float(); - cutter.menuPower = cutter.unitPower = TERN(LASER_POWER_TRAP, v, cutter.power_to_range(v)); + cutter.menuPower = cutter.unitPower = TERN(LASER_POWER_TRAP, constrain( v, 0, CUTTER_POWER_MAX), cutter.power_to_range(v)); + } + else if (parser.seenval('O')) { // pwr in PWM units + const float v = parser.value_float(); + cutter.menuPower = cutter.unitPower = CUTTER_PWM_TO_SPWR(constrain(v, 0, 255)); } else if (cutter.cutter_mode == CUTTER_MODE_STANDARD) cutter.menuPower = cutter.unitPower = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP); diff --git a/Marlin/src/gcode/control/M42.cpp b/Marlin/src/gcode/control/M42.cpp index b995f208f5..4ac7834f90 100644 --- a/Marlin/src/gcode/control/M42.cpp +++ b/Marlin/src/gcode/control/M42.cpp @@ -46,15 +46,16 @@ void protected_pin_err() { /** * M42: Change pin status via G-Code * - * P Pin number (LED if omitted) - * For LPC1768 specify pin P1_02 as M42 P102, - * P1_20 as M42 P120, etc. + * Parameters: + * P Pin number (LED if omitted) + * For LPC1768 specify pin P1_02 as M42 P102, + * P1_20 as M42 P120, etc. * - * S Pin status from 0 - 255 - * I Flag to ignore Marlin's pin protection + * S Pin status from 0-255 + * I Flag to ignore Marlin's pin protection * - * T Pin mode: 0=INPUT 1=OUTPUT 2=INPUT_PULLUP 3=INPUT_PULLDOWN - * 4=INPUT_ANALOG 5=OUTPUT_OPEN_DRAIN + * T Pin mode: 0=INPUT | 1=OUTPUT | 2=INPUT_PULLUP | 3=INPUT_PULLDOWN + * 4=INPUT_ANALOG | 5=OUTPUT_OPEN_DRAIN */ void GcodeSuite::M42() { const int pin_index = PARSED_PIN_INDEX('P', GET_PIN_MAP_INDEX(LED_PIN)); diff --git a/Marlin/src/gcode/control/M605.cpp b/Marlin/src/gcode/control/M605.cpp index 167cdae4a9..bf5549262d 100644 --- a/Marlin/src/gcode/control/M605.cpp +++ b/Marlin/src/gcode/control/M605.cpp @@ -115,7 +115,7 @@ else if (!parser.seen('W')) // if no S or W parameter, the DXC mode gets reset to the user's default dual_x_carriage_mode = DEFAULT_DUAL_X_CARRIAGE_MODE; - #ifdef DEBUG_DXC_MODE + #if ENABLED(DEBUG_DXC_MODE) if (parser.seen('W')) { DEBUG_ECHO_START(); @@ -155,13 +155,16 @@ #elif ENABLED(MULTI_NOZZLE_DUPLICATION) /** - * M605: Set multi-nozzle duplication mode + * M605: Multi Nozzle Mode * - * S2 - Enable duplication mode - * P[mask] - Bit-mask of nozzles to include in the duplication set. - * A value of 0 disables duplication. - * E[index] - Last nozzle index to include in the duplication set. - * A value of 0 disables duplication. + * Set multi-nozzle duplication mode. + * + * Parameters: + * S2 Enable duplication mode + * P Bit-mask of nozzles to include in the duplication set + * A value of 0 disables duplication + * E Last nozzle index to include in the duplication set + * A value of 0 disables duplication */ void GcodeSuite::M605() { bool ena = false; @@ -173,8 +176,7 @@ set_duplication_enabled(ena && (duplication_e_mask >= 3)); } SERIAL_ECHO_START(); - SERIAL_ECHOPGM(STR_DUPLICATION_MODE); - serialprint_onoff(extruder_duplication_enabled); + SERIAL_ECHOPGM(STR_DUPLICATION_MODE, ON_OFF(extruder_duplication_enabled)); if (ena) { SERIAL_ECHOPGM(" ( "); HOTEND_LOOP() if (TEST(duplication_e_mask, e)) { SERIAL_ECHO(e); SERIAL_CHAR(' '); } diff --git a/Marlin/src/gcode/control/M80_M81.cpp b/Marlin/src/gcode/control/M80_M81.cpp index 2d3e407446..a37b0af680 100644 --- a/Marlin/src/gcode/control/M80_M81.cpp +++ b/Marlin/src/gcode/control/M80_M81.cpp @@ -34,7 +34,11 @@ #include "../../feature/power.h" #endif -#if HAS_SUICIDE +#if ENABLED(POWER_LOSS_RECOVERY) + #include "../../feature/powerloss.h" +#endif + +#if ANY(HAS_SUICIDE, CONFIGURABLE_MACHINE_NAME) #include "../../MarlinCore.h" #endif @@ -84,9 +88,15 @@ void GcodeSuite::M81() { ZERO(thermalManager.saved_fan_speed); #endif + TERN_(POWER_LOSS_RECOVERY, recovery.purge()); // Clear PLR on intentional shutdown + safe_delay(1000); // Wait 1 second before switching off - LCD_MESSAGE_F(MACHINE_NAME " " STR_OFF "."); + #if ENABLED(CONFIGURABLE_MACHINE_NAME) + ui.set_status(&MString<30>(&machine_name, ' ', F(STR_OFF), '.')); + #else + LCD_MESSAGE_F(MACHINE_NAME " " STR_OFF "."); + #endif bool delayed_power_off = false; @@ -112,9 +122,9 @@ void GcodeSuite::M81() { return; } - #if HAS_SUICIDE - suicide(); - #elif ENABLED(PSU_CONTROL) + #if ENABLED(PSU_CONTROL) powerManager.power_off_soon(); + #elif HAS_SUICIDE + suicide(); #endif } diff --git a/Marlin/src/gcode/control/M993_M994.cpp b/Marlin/src/gcode/control/M993_M994.cpp index bc634ae13c..392c37234e 100644 --- a/Marlin/src/gcode/control/M993_M994.cpp +++ b/Marlin/src/gcode/control/M993_M994.cpp @@ -49,7 +49,8 @@ void GcodeSuite::M993() { W25QXX.SPI_FLASH_BufferRead(buf, addr, COUNT(buf)); addr += COUNT(buf); card.write(buf, COUNT(buf)); - if (addr % (COUNT(buf) * 10) == 0) SERIAL_CHAR('.'); + if (!(addr % (COUNT(buf) * 10))) SERIAL_CHAR('.'); + if (!(addr % (COUNT(buf) * 32))) hal.watchdog_refresh(); } SERIAL_ECHOLNPGM(" done"); @@ -78,7 +79,8 @@ void GcodeSuite::M994() { card.read(buf, COUNT(buf)); W25QXX.SPI_FLASH_BufferWrite(buf, addr, COUNT(buf)); addr += COUNT(buf); - if (addr % (COUNT(buf) * 10) == 0) SERIAL_CHAR('.'); + if (!(addr % (COUNT(buf) * 10))) SERIAL_CHAR('.'); + if (!(addr % (COUNT(buf) * 32))) hal.watchdog_refresh(); } SERIAL_ECHOLNPGM(" done"); diff --git a/Marlin/src/gcode/control/M999.cpp b/Marlin/src/gcode/control/M999.cpp index b7d6db9f23..b4278fccad 100644 --- a/Marlin/src/gcode/control/M999.cpp +++ b/Marlin/src/gcode/control/M999.cpp @@ -36,7 +36,7 @@ * existing command buffer. */ void GcodeSuite::M999() { - marlin_state = MF_RUNNING; + marlin_state = MarlinState::MF_RUNNING; ui.reset_alert_level(); if (parser.boolval('S')) return; diff --git a/Marlin/src/gcode/control/T.cpp b/Marlin/src/gcode/control/T.cpp index 3c13fe231a..d5affa37e2 100644 --- a/Marlin/src/gcode/control/T.cpp +++ b/Marlin/src/gcode/control/T.cpp @@ -31,7 +31,9 @@ #include "../../module/motion.h" #endif -#if HAS_PRUSA_MMU2 +#if HAS_PRUSA_MMU3 + #include "../../feature/mmu3/mmu3.h" +#elif HAS_PRUSA_MMU2 #include "../../feature/mmu/mmu2.h" #endif @@ -41,14 +43,15 @@ /** * T0-T: Switch tool, usually switching extruders * - * F[units/min] Set the movement feedrate - * S1 Don't move the tool in XY after change + * Parameters: + * F Set the movement feedrate + * S1 Don't move the tool in XY after change * - * For PRUSA_MMU2(S) and EXTENDABLE_EMU_MMU2(S) - * T[n] G-code to extrude at least 38.10 mm at feedrate 19.02 mm/s must follow immediately to load to extruder wheels. - * T? G-code to extrude shouldn't have to follow. Load to extruder wheels is done automatically. - * Tx Same as T?, but nozzle doesn't have to be preheated. Tc requires a preheated nozzle to finish filament load. - * Tc Load to nozzle after filament was prepared by Tc and nozzle is already heated. + * For PRUSA_MMU2(S) and EXTENDABLE_EMU_MMU2(S) + * T G-code to extrude at least 38.10 mm at feedrate 19.02 mm/s must follow immediately to load to extruder wheels. + * T? G-code to extrude shouldn't have to follow. Load to extruder wheels is done automatically. + * Tx Same as T?, but nozzle doesn't have to be preheated. Tc requires a preheated nozzle to finish filament load. + * Tc Load to nozzle after filament was prepared by Tc and nozzle is already heated. */ void GcodeSuite::T(const int8_t tool_index) { @@ -66,7 +69,12 @@ void GcodeSuite::T(const int8_t tool_index) { // Count this command as movement / activity reset_stepper_timeout(); - #if HAS_PRUSA_MMU2 + #if HAS_PRUSA_MMU3 + if (parser.has_string()) { + mmu3.tool_change(parser.string_arg[0], uint8_t(tool_index)); // Special commands T?/Tx/Tc + return; + } + #elif HAS_PRUSA_MMU2 if (parser.string_arg) { mmu2.tool_change(parser.string_arg); // Special commands T?/Tx/Tc return; diff --git a/Marlin/src/gcode/eeprom/M500-M504.cpp b/Marlin/src/gcode/eeprom/M500-M504.cpp index c3962117e5..ffa6d5c686 100644 --- a/Marlin/src/gcode/eeprom/M500-M504.cpp +++ b/Marlin/src/gcode/eeprom/M500-M504.cpp @@ -22,7 +22,6 @@ #include "../gcode.h" #include "../../module/settings.h" -#include "../../core/serial.h" #include "../../inc/MarlinConfig.h" #if ENABLED(CONFIGURATION_EMBEDDING) diff --git a/Marlin/src/gcode/feature/advance/M900.cpp b/Marlin/src/gcode/feature/advance/M900.cpp index e8a16d952f..7631cd0e8c 100644 --- a/Marlin/src/gcode/feature/advance/M900.cpp +++ b/Marlin/src/gcode/feature/advance/M900.cpp @@ -22,22 +22,28 @@ #include "../../../inc/MarlinConfig.h" -#if ENABLED(LIN_ADVANCE) +#if HAS_LIN_ADVANCE_K #include "../../gcode.h" #include "../../../module/planner.h" +#include "../../../module/stepper.h" #if ENABLED(ADVANCE_K_EXTRA) - float other_extruder_advance_K[DISTINCT_E]; + float other_extruder_advance_K[EXTRUDERS]; uint8_t lin_adv_slot = 0; #endif /** * M900: Get or Set Linear Advance K-factor * T Which tool to address - * K Set current advance K factor (Slot 0). - * L Set secondary advance K factor (Slot 1). Requires ADVANCE_K_EXTRA. - * S<0/1> Activate slot 0 or 1. Requires ADVANCE_K_EXTRA. + * K Set current advance K factor (aka Slot 0). + * + * With ADVANCE_K_EXTRA: + * S<0/1> Activate slot 0 or 1. + * L Set secondary advance K factor (Slot 1). + * + * With SMOOTH_LIN_ADVANCE: + * U Set a tau value for LA smoothing */ void GcodeSuite::M900() { @@ -59,38 +65,43 @@ void GcodeSuite::M900() { } #endif - float &kref = planner.extruder_advance_K[E_INDEX_N(tool_index)], newK = kref; - const float oldK = newK; + const float oldK = planner.get_advance_k(tool_index); + float newK = oldK; + + #if ENABLED(SMOOTH_LIN_ADVANCE) + const float oldU = stepper.get_advance_tau(tool_index); + float newU = oldU; + #endif #if ENABLED(ADVANCE_K_EXTRA) - float &lref = other_extruder_advance_K[E_INDEX_N(tool_index)]; + float &lref = other_extruder_advance_K[tool_index]; - const bool old_slot = TEST(lin_adv_slot, tool_index), // The tool's current slot (0 or 1) - new_slot = parser.boolval('S', old_slot); // The passed slot (default = current) + const bool old_slot = TEST(lin_adv_slot, tool_index), // Each tool uses 1 bit to store its current slot (0 or 1) + new_slot = parser.boolval('S', old_slot); // The new slot (0 or 1) to set for the tool (default = no change) // If a new slot is being selected swap the current and // saved K values. Do here so K/L will apply correctly. if (new_slot != old_slot) { // Not the same slot? SET_BIT_TO(lin_adv_slot, tool_index, new_slot); // Update the slot for the tool - newK = lref; // Get new K value from backup - lref = oldK; // Save K to backup + newK = lref; // Get the backup K value (to apply below) + lref = oldK; // Back up the active K value } // Set the main K value. Apply if the main slot is active. if (parser.seenval('K')) { const float K = parser.value_float(); if (!WITHIN(K, 0, 10)) echo_value_oor('K'); - else if (new_slot) lref = K; // S1 Knn - else newK = K; // S0 Knn + else if (new_slot) lref = K; // S1 Knn (set main K in its backup slot) + else newK = K; // S0 Knn (use main K now) } // Set the extra K value. Apply if the extra slot is active. if (parser.seenval('L')) { const float L = parser.value_float(); if (!WITHIN(L, 0, 10)) echo_value_oor('L'); - else if (!new_slot) lref = L; // S0 Lnn - else newK = L; // S1 Lnn + else if (!new_slot) lref = L; // S0 Lnn (set extra K in its backup slot) + else newK = L; // S1 Lnn (use extra K now) } #else @@ -105,37 +116,59 @@ void GcodeSuite::M900() { #endif - if (newK != oldK) { + #if ENABLED(SMOOTH_LIN_ADVANCE) + if (parser.seenval('U')) { + const float tau = parser.value_float(); + if (WITHIN(tau, 0.0f, 0.5f)) + newU = tau; + else + echo_value_oor('U'); + } + #endif + + if (newK != oldK || TERN0(SMOOTH_LIN_ADVANCE, newU != oldU)) { planner.synchronize(); - kref = newK; + if (newK != oldK) planner.set_advance_k(newK, tool_index); + #if ENABLED(SMOOTH_LIN_ADVANCE) + if (newU != oldU) stepper.set_advance_tau(newU, tool_index); + #endif } if (!parser.seen_any()) { #if ENABLED(ADVANCE_K_EXTRA) - #if DISTINCT_E < 2 - SERIAL_ECHOLNPGM("Advance S", new_slot, " K", kref, "(S", !new_slot, " K", lref, ")"); + #if DISABLED(DISTINCT_E_FACTORS) + SERIAL_ECHOLNPGM("Advance S", new_slot, " K", newK, "(S", !new_slot, " K", lref, ")"); #else EXTRUDER_LOOP() { const bool slot = TEST(lin_adv_slot, e); - SERIAL_ECHOLNPGM("Advance T", e, " S", slot, " K", planner.extruder_advance_K[e], + SERIAL_ECHOLNPGM("Advance T", e, " S", slot, " K", planner.get_advance_k(e), "(S", !slot, " K", other_extruder_advance_K[e], ")"); } #endif - #else + #else // !ADVANCE_K_EXTRA SERIAL_ECHO_START(); - #if DISTINCT_E < 2 - SERIAL_ECHOLNPGM("Advance K=", planner.extruder_advance_K[0]); + #if DISABLED(DISTINCT_E_FACTORS) + SERIAL_ECHOPGM("Advance K=", planner.get_advance_k()); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM(" TAU=", stepper.get_advance_tau()); + #endif + SERIAL_EOL(); #else SERIAL_ECHOPGM("Advance K"); - EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), planner.extruder_advance_K[e]); + EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), planner.get_advance_k(e)); SERIAL_EOL(); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM("Advance TAU"); + EXTRUDER_LOOP() SERIAL_ECHO(C(' '), C('0' + e), C(':'), stepper.get_advance_tau(e)); + SERIAL_EOL(); + #endif #endif - #endif + #endif // !ADVANCE_K_EXTRA } } @@ -144,15 +177,21 @@ void GcodeSuite::M900_report(const bool forReplay/*=true*/) { TERN_(MARLIN_SMALL_BUILD, return); report_heading(forReplay, F(STR_LINEAR_ADVANCE)); - #if DISTINCT_E < 2 + DISTINCT_E_LOOP() { report_echo_start(forReplay); - SERIAL_ECHOLNPGM(" M900 K", planner.extruder_advance_K[0]); - #else - EXTRUDER_LOOP() { - report_echo_start(forReplay); - SERIAL_ECHOLNPGM(" M900 T", e, " K", planner.extruder_advance_K[e]); - } - #endif + SERIAL_ECHOPGM( + #if ENABLED(DISTINCT_E_FACTORS) + " M900 T", e, " K" + #else + " M900 K" + #endif + ); + SERIAL_ECHO(planner.get_advance_k(e)); + #if ENABLED(SMOOTH_LIN_ADVANCE) + SERIAL_ECHOPGM(" U", stepper.get_advance_tau(e)); + #endif + SERIAL_EOL(); + } } -#endif // LIN_ADVANCE +#endif // HAS_LIN_ADVANCE_K diff --git a/Marlin/src/gcode/feature/camera/M240.cpp b/Marlin/src/gcode/feature/camera/M240.cpp index bb1d3f9eee..23ec4ea1e7 100644 --- a/Marlin/src/gcode/feature/camera/M240.cpp +++ b/Marlin/src/gcode/feature/camera/M240.cpp @@ -47,7 +47,7 @@ #endif #ifdef PHOTO_RETRACT_MM - inline void e_move_m240(const float length, const_feedRate_t fr_mm_s) { + inline void e_move_m240(const float length, const feedRate_t fr_mm_s) { if (length && thermalManager.hotEnoughToExtrude(active_extruder)) unscaled_e_move(length, fr_mm_s); } diff --git a/Marlin/src/gcode/feature/cancel/M486.cpp b/Marlin/src/gcode/feature/cancel/M486.cpp index c1e90d1b96..237654e433 100644 --- a/Marlin/src/gcode/feature/cancel/M486.cpp +++ b/Marlin/src/gcode/feature/cancel/M486.cpp @@ -41,13 +41,13 @@ void GcodeSuite::M486() { if (parser.seen('T')) { cancelable.reset(); - cancelable.object_count = parser.intval('T', 1); + cancelable.state.object_count = parser.intval('T', 1); } if (parser.seenval('S')) cancelable.set_active_object(parser.value_int()); - if (parser.seen('C')) cancelable.cancel_active_object(); + if (parser.seen_test('C')) cancelable.cancel_active_object(); if (parser.seenval('P')) cancelable.cancel_object(parser.value_int()); diff --git a/Marlin/src/gcode/feature/controllerfan/M710.cpp b/Marlin/src/gcode/feature/controllerfan/M710.cpp index c8b5efa8cb..6bdfcd9b32 100644 --- a/Marlin/src/gcode/feature/controllerfan/M710.cpp +++ b/Marlin/src/gcode/feature/controllerfan/M710.cpp @@ -46,24 +46,22 @@ * M710 I127 A1 S255 D160 ; Set controller fan idle speed 50%, AutoMode On, Fan speed 100%, duration to 160 Secs */ void GcodeSuite::M710() { + if (!parser.seen("ADIRS")) return M710_report(); - const bool seenR = parser.seen('R'); - if (seenR) controllerFan.reset(); + if (parser.seen_test('R')) + controllerFan.reset(); - const bool seenS = parser.seenval('S'); - if (seenS) controllerFan.settings.active_speed = parser.value_byte(); + if (parser.seenval('S')) + controllerFan.settings.active_speed = parser.value_byte(); - const bool seenI = parser.seenval('I'); - if (seenI) controllerFan.settings.idle_speed = parser.value_byte(); + if (parser.seenval('I')) + controllerFan.settings.idle_speed = parser.value_byte(); - const bool seenA = parser.seenval('A'); - if (seenA) controllerFan.settings.auto_mode = parser.value_bool(); + if (parser.seenval('A')) + controllerFan.settings.auto_mode = parser.value_bool(); - const bool seenD = parser.seenval('D'); - if (seenD) controllerFan.settings.duration = parser.value_ushort(); - - if (!(seenR || seenS || seenI || seenA || seenD)) - M710_report(); + if (parser.seenval('D')) + controllerFan.settings.duration = parser.value_ushort(); } void GcodeSuite::M710_report(const bool forReplay/*=true*/) { diff --git a/Marlin/src/gcode/feature/digipot/M907-M910.cpp b/Marlin/src/gcode/feature/digipot/M907-M910.cpp index e36cf76e88..425d27a72d 100644 --- a/Marlin/src/gcode/feature/digipot/M907-M910.cpp +++ b/Marlin/src/gcode/feature/digipot/M907-M910.cpp @@ -46,15 +46,32 @@ * Set percentage of max current for all axes (Requires HAS_DIGIPOT_DAC) */ void GcodeSuite::M907() { + #if HAS_MOTOR_CURRENT_SPI if (!parser.seen("BS" STR_AXES_LOGICAL)) return M907_report(); - if (parser.seenval('S')) for (uint8_t i = 0; i < MOTOR_CURRENT_COUNT; ++i) stepper.set_digipot_current(i, parser.value_int()); - LOOP_LOGICAL_AXES(i) if (parser.seenval(IAXIS_CHAR(i))) stepper.set_digipot_current(i, parser.value_int()); // X Y Z (I J K U V W) E (map to drivers according to DIGIPOT_CHANNELS. Default with NUM_AXES 3: map X Y Z E to X Y Z E0) - // Additional extruders use B,C. - // TODO: Change these parameters because 'E' is used and D should be reserved for debugging. B? + // S - Set current in mA for all axes + if (parser.seenval('S')) + for (uint8_t i = 0; i < MOTOR_CURRENT_COUNT; ++i) + stepper.set_digipot_current(i, parser.value_int()); + + // X Y Z I J K U V W E + // Map to drivers according to pots addresses. + // Default with NUM_AXES 3: map X Y Z E to X Y Z E0. + LOOP_LOGICAL_AXES(i) + if (parser.seenval(IAXIS_CHAR(i))) + stepper.set_digipot_current(i, parser.value_int()); + + /** + * Additional extruders use B,C in this legacy protocol + * TODO: Update to allow for an index with X, Y, Z, E axis to isolate a single stepper + * and use configured axis names instead of IJKUVW. i.e., Match the behavior of + * other G-codes that set stepper-specific parameters. If necessary deprecate G-codes. + * Bonus Points: Standardize a method that all G-codes can use to refer to one or + * more steppers/drivers and apply to various G-codes. + */ #if E_STEPPERS >= 2 if (parser.seenval('B')) stepper.set_digipot_current(E_AXIS + 1, parser.value_int()); #if E_STEPPERS >= 3 @@ -68,58 +85,88 @@ void GcodeSuite::M907() { #define HAS_X_Y_XY_I_J_K_U_V_W 1 #endif - #if HAS_X_Y_XY_I_J_K_U_V_W || ANY_PIN(MOTOR_CURRENT_PWM_E, MOTOR_CURRENT_PWM_Z) + #if ANY(HAS_X_Y_XY_I_J_K_U_V_W, HAS_MOTOR_CURRENT_PWM_E, HAS_MOTOR_CURRENT_PWM_Z) if (!parser.seen("S" #if HAS_X_Y_XY_I_J_K_U_V_W - "XY" SECONDARY_AXIS_GANG("I", "J", "K", "U", "V", "W") - #endif - #if PIN_EXISTS(MOTOR_CURRENT_PWM_Z) - "Z" - #endif - #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) - "E" + NUM_AXIS_GANG("X", "Y",, "I", "J", "K", "U", "V", "W") #endif + TERN_(HAS_MOTOR_CURRENT_PWM_Z, "Z") + TERN_(HAS_MOTOR_CURRENT_PWM_E, "E") )) return M907_report(); - if (parser.seenval('S')) for (uint8_t a = 0; a < MOTOR_CURRENT_COUNT; ++a) stepper.set_digipot_current(a, parser.value_int()); + // S - Set all stepper current to the same value + if (parser.seenval('S')) { + const int16_t v = parser.value_int(); + for (uint8_t a = 0; a < MOTOR_CURRENT_COUNT; ++a) + stepper.set_digipot_current(a, v); + } - #if HAS_X_Y_XY_I_J_K_U_V_W - if (NUM_AXIS_GANG( - parser.seenval('X'), || parser.seenval('Y'), || false, - || parser.seenval('I'), || parser.seenval('J'), || parser.seenval('K'), - || parser.seenval('U'), || parser.seenval('V'), || parser.seenval('W') - )) stepper.set_digipot_current(0, parser.value_int()); - #endif - #if PIN_EXISTS(MOTOR_CURRENT_PWM_Z) - if (parser.seenval('Z')) stepper.set_digipot_current(1, parser.value_int()); - #endif - #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) - if (parser.seenval('E')) stepper.set_digipot_current(2, parser.value_int()); - #endif + // X Y I J K U V W - All aliases to set the current for "most axes." + // Only the value of the last given parameter is used. + if (ENABLED(HAS_X_Y_XY_I_J_K_U_V_W) && (NUM_AXIS_GANG( + parser.seenval('X'), || parser.seenval('Y'), || false, + || parser.seenval('I'), || parser.seenval('J'), || parser.seenval('K'), + || parser.seenval('U'), || parser.seenval('V'), || parser.seenval('W') + ))) + stepper.set_digipot_current(0, parser.value_int()); + + // Z - Set the current just for the Z axis + if (TERN0(HAS_MOTOR_CURRENT_PWM_Z, parser.seenval('Z'))) + stepper.set_digipot_current(1, parser.value_int()); + + // Z - Set the current just for the Extruder + if (TERN0(HAS_MOTOR_CURRENT_PWM_E, parser.seenval('E'))) + stepper.set_digipot_current(2, parser.value_int()); #endif #endif // HAS_MOTOR_CURRENT_PWM #if HAS_MOTOR_CURRENT_I2C - // this one uses actual amps in floating point - if (parser.seenval('S')) for (uint8_t q = 0; q < DIGIPOT_I2C_NUM_CHANNELS; ++q) digipot_i2c.set_current(q, parser.value_float()); - LOOP_LOGICAL_AXES(i) if (parser.seenval(IAXIS_CHAR(i))) digipot_i2c.set_current(i, parser.value_float()); // X Y Z (I J K U V W) E (map to drivers according to pots adresses. Default with NUM_AXES 3 X Y Z E: map to X Y Z E0) + // This current driver takes actual Amps in floating point + // rather than milli-amps or some scalar unit. + + // S - Set the same current in Amps on all channels + if (parser.seenval('S')) { + const float v = parser.value_float(); + for (uint8_t q = 0; q < DIGIPOT_I2C_NUM_CHANNELS; ++q) + digipot_i2c.set_current(q, v); + } + + // X Y Z I J K U V W E + // Map to drivers according to pots addresses. + // Default with NUM_AXES 3: map X Y Z E to X Y Z E0. + LOOP_LOGICAL_AXES(i) + if (parser.seenval(IAXIS_CHAR(i))) + digipot_i2c.set_current(i, parser.value_float()); + // Additional extruders use B,C,D. - // TODO: Change these parameters because 'E' is used and because 'D' should be reserved for debugging. B? + // TODO: Make parameters work like other axis-specific / stepper-specific. See above. #if E_STEPPERS >= 2 for (uint8_t i = E_AXIS + 1; i < _MAX(DIGIPOT_I2C_NUM_CHANNELS, (NUM_AXES + 3)); i++) - if (parser.seenval('B' + i - (E_AXIS + 1))) digipot_i2c.set_current(i, parser.value_float()); + if (parser.seenval('B' + i - (E_AXIS + 1))) + digipot_i2c.set_current(i, parser.value_float()); #endif - #endif + + #endif // HAS_MOTOR_CURRENT_I2C #if HAS_MOTOR_CURRENT_DAC + + // S - Set the same current percentage on all axes if (parser.seenval('S')) { const float dac_percent = parser.value_float(); - LOOP_LOGICAL_AXES(i) stepper_dac.set_current_percent(i, dac_percent); + LOOP_LOGICAL_AXES(i) + stepper_dac.set_current_percent(i, dac_percent); } - LOOP_LOGICAL_AXES(i) if (parser.seenval(IAXIS_CHAR(i))) stepper_dac.set_current_percent(i, parser.value_float()); // X Y Z (I J K U V W) E (map to drivers according to DAC_STEPPER_ORDER. Default with NUM_AXES 3: X Y Z E map to X Y Z E0) + + // X Y Z I J K U V W E + // Map to drivers according to pots addresses. + // Default with NUM_AXES 3: map X Y Z E to X Y Z E0. + LOOP_LOGICAL_AXES(i) + if (parser.seenval(IAXIS_CHAR(i))) + stepper_dac.set_current_percent(i, parser.value_float()); + #endif } @@ -131,9 +178,11 @@ void GcodeSuite::M907() { report_heading_etc(forReplay, F(STR_STEPPER_MOTOR_CURRENTS)); #if HAS_MOTOR_CURRENT_PWM SERIAL_ECHOLNPGM_P( // PWM-based has 3 values: - PSTR(" M907 X"), stepper.motor_current_setting[0] // X, Y, (I, J, K, U, V, W) - , SP_Z_STR, stepper.motor_current_setting[1] // Z - #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) + PSTR(" M907 X"), stepper.motor_current_setting[0] // X, Y, (I, J, K, U, V, W) + #if HAS_MOTOR_CURRENT_PWM_Z + , SP_Z_STR, stepper.motor_current_setting[1] // Z + #endif + #if HAS_MOTOR_CURRENT_PWM_E , SP_E_STR, stepper.motor_current_setting[2] // E #endif ); diff --git a/Marlin/src/gcode/feature/ft_motion/M493.cpp b/Marlin/src/gcode/feature/ft_motion/M493.cpp index 2da92b8582..6696ae64af 100644 --- a/Marlin/src/gcode/feature/ft_motion/M493.cpp +++ b/Marlin/src/gcode/feature/ft_motion/M493.cpp @@ -28,41 +28,50 @@ #include "../../../module/ft_motion.h" #include "../../../module/stepper.h" +void say_shaper_type(const AxisEnum a, bool &sep, const char axis_name) { + if (sep) SERIAL_ECHOPGM(" ; "); + SERIAL_CHAR(axis_name, '='); + switch (ftMotion.cfg.shaper[a]) { + default: break; + case ftMotionShaper_ZV: SERIAL_ECHOPGM("ZV"); break; + case ftMotionShaper_ZVD: SERIAL_ECHOPGM("ZVD"); break; + case ftMotionShaper_ZVDD: SERIAL_ECHOPGM("ZVDD"); break; + case ftMotionShaper_ZVDDD: SERIAL_ECHOPGM("ZVDDD"); break; + case ftMotionShaper_EI: SERIAL_ECHOPGM("EI"); break; + case ftMotionShaper_2HEI: SERIAL_ECHOPGM("2 Hump EI"); break; + case ftMotionShaper_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break; + case ftMotionShaper_MZV: SERIAL_ECHOPGM("MZV"); break; + } + sep = true; +} + void say_shaping() { + const ft_config_t &c = ftMotion.cfg; + // FT Enabled - SERIAL_ECHO_TERNARY(ftMotion.cfg.mode, "Fixed-Time Motion ", "en", "dis", "abled"); + SERIAL_ECHO_TERNARY(c.active, "Fixed-Time Motion ", "en", "dis", "abled"); // FT Shaping - #if HAS_X_AXIS - if (ftMotion.cfg.mode > ftMotionMode_ENABLED) { - SERIAL_ECHOPGM(" with "); - switch (ftMotion.cfg.mode) { - default: break; - case ftMotionMode_ZV: SERIAL_ECHOPGM("ZV"); break; - case ftMotionMode_ZVD: SERIAL_ECHOPGM("ZVD"); break; - case ftMotionMode_ZVDD: SERIAL_ECHOPGM("ZVDD"); break; - case ftMotionMode_ZVDDD: SERIAL_ECHOPGM("ZVDDD"); break; - case ftMotionMode_EI: SERIAL_ECHOPGM("EI"); break; - case ftMotionMode_2HEI: SERIAL_ECHOPGM("2 Hump EI"); break; - case ftMotionMode_3HEI: SERIAL_ECHOPGM("3 Hump EI"); break; - case ftMotionMode_MZV: SERIAL_ECHOPGM("MZV"); break; - //case ftMotionMode_DISCTF: SERIAL_ECHOPGM("discrete transfer functions"); break; - //case ftMotionMode_ULENDO_FBS: SERIAL_ECHOPGM("Ulendo FBS."); return; - } - SERIAL_ECHOPGM(" shaping"); - } - #endif - SERIAL_ECHOLNPGM("."); + const bool is_shaping = AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E); + bool sep = false; + if (is_shaping) { + #define STEPPER_E_NAME 'E' + #define _SAY_SHAPER(A) if (AXIS_IS_SHAPING(A)) say_shaper_type(_AXIS(A), sep, STEPPER_##A##_NAME); + SERIAL_ECHOPGM(" ("); + SHAPED_CODE(_SAY_SHAPER(A), _SAY_SHAPER(B), _SAY_SHAPER(C), _SAY_SHAPER(E)); + SERIAL_CHAR(')'); + } + SERIAL_EOL(); - const bool z_based = TERN0(HAS_DYNAMIC_FREQ_MM, ftMotion.cfg.dynFreqMode == dynFreqMode_Z_BASED), - g_based = TERN0(HAS_DYNAMIC_FREQ_G, ftMotion.cfg.dynFreqMode == dynFreqMode_MASS_BASED), + const bool z_based = TERN0(HAS_DYNAMIC_FREQ_MM, c.dynFreqMode == dynFreqMode_Z_BASED), + g_based = TERN0(HAS_DYNAMIC_FREQ_G, c.dynFreqMode == dynFreqMode_MASS_BASED), dynamic = z_based || g_based; // FT Dynamic Frequency Mode - if (ftMotion.cfg.modeHasShaper()) { + if (is_shaping) { #if HAS_DYNAMIC_FREQ SERIAL_ECHOPGM("Dynamic Frequency Mode "); - switch (ftMotion.cfg.dynFreqMode) { + switch (c.dynFreqMode) { default: case dynFreqMode_DISABLED: SERIAL_ECHOPGM("disabled"); break; #if HAS_DYNAMIC_FREQ_MM @@ -76,31 +85,35 @@ void say_shaping() { #endif #if HAS_X_AXIS - SERIAL_ECHO_TERNARY(dynamic, "X/A ", "base dynamic", "static", " compensator frequency: "); - SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq[X_AXIS], 2), F("Hz")); + SERIAL_CHAR(STEPPER_A_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_ECHO(p_float_t(c.baseFreq.x, 2), F("Hz")); #if HAS_DYNAMIC_FREQ - if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK[X_AXIS], 2), F("Hz/"), z_based ? F("mm") : F("g")); + if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(c.dynFreqK.x, 2), F("Hz/"), z_based ? F("mm") : F("g")); #endif SERIAL_EOL(); #endif #if HAS_Y_AXIS - SERIAL_ECHO_TERNARY(dynamic, "Y/B ", "base dynamic", "static", " compensator frequency: "); - SERIAL_ECHO(p_float_t(ftMotion.cfg.baseFreq[Y_AXIS], 2), F(" Hz")); + SERIAL_CHAR(STEPPER_B_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_ECHO(p_float_t(c.baseFreq.y, 2), F(" Hz")); #if HAS_DYNAMIC_FREQ - if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(ftMotion.cfg.dynFreqK[Y_AXIS], 2), F("Hz/"), z_based ? F("mm") : F("g")); + if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(c.dynFreqK.y, 2), F("Hz/"), z_based ? F("mm") : F("g")); + #endif + SERIAL_EOL(); + #endif + + #if ENABLED(FTM_SHAPER_Z) + SERIAL_CHAR(STEPPER_C_NAME); + SERIAL_ECHO_TERNARY(dynamic, " ", "base dynamic", "static", " shaper frequency: "); + SERIAL_ECHO(p_float_t(c.baseFreq.z, 2), F(" Hz")); + #if HAS_DYNAMIC_FREQ + if (dynamic) SERIAL_ECHO(F(" scaling: "), p_float_t(c.dynFreqK.z, 2), F("Hz/"), z_based ? F("mm") : F("g")); #endif SERIAL_EOL(); #endif } - - #if HAS_EXTRUDERS - SERIAL_ECHO_TERNARY(ftMotion.cfg.linearAdvEna, "Linear Advance ", "en", "dis", "abled"); - if (ftMotion.cfg.linearAdvEna) - SERIAL_ECHOLNPGM(". Gain: ", ftMotion.cfg.linearAdvK); - else - SERIAL_EOL(); - #endif } void GcodeSuite::M493_report(const bool forReplay/*=true*/) { @@ -108,126 +121,139 @@ void GcodeSuite::M493_report(const bool forReplay/*=true*/) { report_heading_etc(forReplay, F(STR_FT_MOTION)); const ft_config_t &c = ftMotion.cfg; - SERIAL_ECHOPGM(" M493 S", c.mode); - #if HAS_X_AXIS - SERIAL_ECHOPGM(" A", c.baseFreq[X_AXIS]); - #if HAS_Y_AXIS - SERIAL_ECHOPGM(" B", c.baseFreq[Y_AXIS]); + + SERIAL_ECHOLNPGM( + " M493 S", c.active + #if HAS_DYNAMIC_FREQ + , " D", c.dynFreqMode #endif - #endif + // Axis Synchronization + , " H", c.axis_sync_enabled + ); + #if HAS_DYNAMIC_FREQ - SERIAL_ECHOPGM(" D", c.dynFreqMode); - #if HAS_X_AXIS - SERIAL_ECHOPGM(" F", c.dynFreqK[X_AXIS]); - #if HAS_Y_AXIS - SERIAL_ECHOPGM(" H", c.dynFreqK[Y_AXIS]); - #endif - #endif + #define F_REPORT(A) , F(" F"), c.dynFreqK.A + #else + #define F_REPORT(A) #endif - #if HAS_EXTRUDERS - SERIAL_ECHOPGM(" P", c.linearAdvEna, " K", c.linearAdvK); - #endif - SERIAL_EOL(); + #define _REPORT_M493_AXIS(A) \ + SERIAL_ECHOLN(F(" M493 "), C(AXIS_CHAR(_AXIS(A))) \ + , F(" C"), c.shaper.A \ + , F(" A"), c.baseFreq.A \ + F_REPORT(A) \ + , F(" I"), c.zeta.A \ + , F(" Q"), c.vtol.A \ + ); + // Shaper type for each axis + SHAPED_MAP(_REPORT_M493_AXIS); } /** * M493: Set Fixed-time Motion Control parameters * - * S Set the motion / shaping mode. Shaping requires an X axis, at the minimum. + * S Set Fixed-Time motion mode on or off. + * 0: Fixed-Time Motion OFF (Standard Motion) + * 1: Fixed-Time Motion ON * - * 0: Standard Motion - * 1: Fixed-Time Motion - * 10: ZV : Zero Vibration - * 11: ZVD : Zero Vibration and Derivative - * 12: ZVDD : Zero Vibration, Derivative, and Double Derivative - * 13: ZVDDD : Zero Vibration, Derivative, Double Derivative, and Triple Derivative - * 14: EI : Extra-Intensive - * 15: 2HEI : 2-Hump Extra-Intensive - * 16: 3HEI : 3-Hump Extra-Intensive - * 17: MZV : Mass-based Zero Vibration + * V Flag to request version (Version 2+). (No reply = Version < 2) + * + * H Enable (1) or Disable (0) Axis Synchronization. + * + * Linear / Pressure Advance: * * P Enable (1) or Disable (0) Linear Advance pressure control * - * K Set Linear Advance gain + * Specifying Axes (for A,C,F,I,Q): * - * D Set Dynamic Frequency mode + * X/Y/Z/E : Flag the axes (or core steppers) on which to apply the given parameters + * If none are given then XY is assumed. + * + * Compensator / Input Shaper: + * + * C Set Compensator Mode (Input Shaper) for the specified axes + * Users / slicers must remember to set the mode for all relevant axes! + * 0: NONE : No input shaper + * 1: ZV : Zero Vibration + * 2: ZVD : Zero Vibration and Derivative + * 3: ZVDD : Zero Vibration, Derivative, and Double Derivative + * 4: ZVDDD : Zero Vibration, Derivative, Double Derivative, and Triple Derivative + * 5: EI : Extra-Intensive + * 6: 2HEI : 2-Hump Extra-Intensive + * 7: 3HEI : 3-Hump Extra-Intensive + * 8: MZV : Mass-based Zero Vibration + * + * A Set static/base frequency for the specified axes + * I Set damping ratio for the specified axes + * Q Set vibration tolerance (vtol) for the specified axes + * + * Dynamic Frequency Mode: + * + * D Set Dynamic Frequency mode (for all axis compensators) * 0: DISABLED * 1: Z-based (Requires a Z axis) * 2: Mass-based (Requires X and E axes) * - * A Set static/base frequency for the X axis - * F Set frequency scaling for the X axis - * I 0.0 Set damping ratio for the X axis - * Q 0.00 Set the vibration tolerance for the X axis + * F Set frequency scaling for the specified axes * - * B Set static/base frequency for the Y axis - * H Set frequency scaling for the Y axis - * J 0.0 Set damping ratio for the Y axis - * R 0.00 Set the vibration tolerance for the Y axis */ void GcodeSuite::M493() { - struct { bool update_n:1, update_a:1, reset_ft:1, report_h:1; } flag = { false }; + // Request version of FTM. (No response = Version < 2) + if (parser.seen('V') && !parser.has_value()) { + SERIAL_ECHOLNPGM("FTM V" STRINGIFY(FTM_VERSION)); + return; + } + + struct { bool update:1, report:1; } flag = { false }; if (!parser.seen_any()) - flag.report_h = true; + flag.report = true; // Parse 'S' mode parameter. - if (parser.seenval('S')) { - const ftMotionMode_t newmm = (ftMotionMode_t)parser.value_byte(); - - if (newmm != ftMotion.cfg.mode) { - switch (newmm) { - default: SERIAL_ECHOLNPGM("?Invalid control mode [S] value."); return; - #if HAS_X_AXIS - case ftMotionMode_ZV: - case ftMotionMode_ZVD: - case ftMotionMode_ZVDD: - case ftMotionMode_ZVDDD: - case ftMotionMode_EI: - case ftMotionMode_2HEI: - case ftMotionMode_3HEI: - case ftMotionMode_MZV: - //case ftMotionMode_ULENDO_FBS: - //case ftMotionMode_DISCTF: - flag.update_n = flag.update_a = true; - #endif - case ftMotionMode_DISABLED: flag.reset_ft = true; - case ftMotionMode_ENABLED: - ftMotion.cfg.mode = newmm; - flag.report_h = true; - break; - } + if (parser.seen('S')) { + const bool active = parser.value_bool(); + if (active != ftMotion.cfg.active) { + stepper.ftMotion_syncPosition(); + ftMotion.cfg.active = active; + flag.report = true; } } - #if HAS_EXTRUDERS + #if NUM_AXES_SHAPED > 0 - // Pressure control (linear advance) parameter. - if (parser.seen('P')) { - const bool val = parser.value_bool(); - ftMotion.cfg.linearAdvEna = val; - flag.report_h = true; - SERIAL_ECHO_TERNARY(val, "Linear Advance ", "en", "dis", "abled.\n"); + const bool seenC = parser.seenval('C'); + const ftMotionShaper_t shaperVal = seenC ? (ftMotionShaper_t)parser.value_byte() : ftMotionShaper_NONE; + const bool goodShaper = WITHIN(shaperVal, ftMotionShaper_NONE, ftMotionShaper_MZV); + if (seenC && !goodShaper) { + SERIAL_ECHOLN(F("?Invalid "), F("(C)ompensator value. (0-"), int(ftMotionShaper_MZV)); + return; } - - // Pressure control (linear advance) gain parameter. - if (parser.seenval('K')) { - const float val = parser.value_float(); - if (val >= 0.0f) { - ftMotion.cfg.linearAdvK = val; - flag.report_h = true; + auto set_shaper = [&](const AxisEnum axis, ftMotionShaper_t newsh) { + if (newsh != ftMotion.cfg.shaper[axis]) { + ftMotion.cfg.shaper[axis] = newsh; + flag.update = flag.report = true; } - else // Value out of range. - SERIAL_ECHOLNPGM("Linear Advance gain out of range."); + }; + if (seenC) { + #define _SET_SHAPER(A) set_shaper(_AXIS(A), shaperVal); + SHAPED_MAP(_SET_SHAPER); } - #endif // HAS_EXTRUDERS + #endif // NUM_AXES_SHAPED > 0 + + // Parse 'H' Axis Synchronization parameter. + if (parser.seenval('H')) { + const bool enabled = parser.value_bool(); + if (enabled != ftMotion.cfg.axis_sync_enabled) { + ftMotion.cfg.axis_sync_enabled = enabled; + flag.report = true; + } + } #if HAS_DYNAMIC_FREQ // Dynamic frequency mode parameter. if (parser.seenval('D')) { - if (ftMotion.cfg.modeHasShaper()) { + if (AXIS_IS_SHAPING(X) || AXIS_IS_SHAPING(Y) || AXIS_IS_SHAPING(Z) || AXIS_IS_SHAPING(E)) { const dynFreqMode_t val = dynFreqMode_t(parser.value_byte()); switch (val) { #if HAS_DYNAMIC_FREQ_MM @@ -238,15 +264,15 @@ void GcodeSuite::M493() { #endif case dynFreqMode_DISABLED: ftMotion.cfg.dynFreqMode = val; - flag.report_h = true; + flag.report = true; break; default: - SERIAL_ECHOLNPGM("?Invalid Dynamic Frequency Mode [D] value."); + SERIAL_ECHOLN(F("?Invalid "), F("(D)ynamic Frequency Mode value.")); break; } } else { - SERIAL_ECHOLNPGM("?Wrong shaper for [D] Dynamic Frequency mode."); + SERIAL_ECHOLNPGM("?Wrong shaper for (D)ynamic Frequency Mode ", ftMotion.cfg.dynFreqMode, "."); } } @@ -257,141 +283,245 @@ void GcodeSuite::M493() { #endif // HAS_DYNAMIC_FREQ + // Frequency parameter + const bool seenA = parser.seenval('A'); + const float baseFreqVal = seenA ? parser.value_float() : 0.0f; + const bool goodBaseFreq = seenA && WITHIN(baseFreqVal, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2); + if (seenA && !goodBaseFreq) + SERIAL_ECHOLN(F("?Invalid "), F("(A) Base Frequency value. ("), int(FTM_MIN_SHAPE_FREQ), C('-'), int((FTM_FS) / 2), C(')')); + + #if HAS_DYNAMIC_FREQ + // Dynamic Frequency parameter + const bool seenF = parser.seenval('F'); + const float baseDynFreqVal = seenF ? parser.value_float() : 0.0f; + if (seenF && !modeUsesDynFreq) + SERIAL_ECHOLNPGM("?Wrong mode for (F)requency scaling."); + #endif + + // Zeta parameter + const bool seenI = parser.seenval('I'); + const float zetaVal = seenI ? parser.value_float() : 0.0f; + const bool goodZeta = seenI && WITHIN(zetaVal, 0.01f, 1.0f); + if (seenI && !goodZeta) + SERIAL_ECHOLN(F("?Invalid "), F("(I) Zeta value. (0.01-1.0)")); // Zeta out of range + + // Vibration Tolerance parameter + const bool seenQ = parser.seenval('Q'); + const float vtolVal = seenQ ? parser.value_float() : 0.0f; + const bool goodVtol = seenQ && WITHIN(vtolVal, 0.00f, 1.0f); + if (seenQ && !goodVtol) + SERIAL_ECHOLN(F("?Invalid "), F("(Q) Vibration Tolerance value. (0.0-1.0)")); // VTol out of range + + const bool apply_xy = !parser.seen("XYZE"); + #if HAS_X_AXIS - // Parse frequency parameter (X axis). - if (parser.seenval('A')) { - if (ftMotion.cfg.modeHasShaper()) { - const float val = parser.value_float(); - // TODO: Frequency minimum is dependent on the shaper used; the above check isn't always correct. - if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) { - ftMotion.cfg.baseFreq[X_AXIS] = val; - flag.update_n = flag.reset_ft = flag.report_h = true; - } - else // Frequency out of range. - SERIAL_ECHOLNPGM("Invalid [", C('A'), "] frequency value."); - } - else // Mode doesn't use frequency. - SERIAL_ECHOLNPGM("Wrong mode for [", C('A'), "] frequency."); - } + if (apply_xy || parser.seen_test('X')) { - #if HAS_DYNAMIC_FREQ - // Parse frequency scaling parameter (X axis). - if (parser.seenval('F')) { - if (modeUsesDynFreq) { - ftMotion.cfg.dynFreqK[X_AXIS] = parser.value_float(); - flag.report_h = true; + // Parse X frequency parameter + if (seenA) { + if (AXIS_IS_SHAPING(X)) { + // TODO: Frequency minimum is dependent on the shaper used; the above check isn't always correct. + if (goodBaseFreq) { + ftMotion.cfg.baseFreq.x = baseFreqVal; + flag.update = flag.report = true; + } + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " [A] frequency."); + } + + #if HAS_DYNAMIC_FREQ + // Parse X frequency scaling parameter + if (seenF && modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.x = baseDynFreqVal; + flag.report = true; + } + #endif + + // Parse X zeta parameter + if (seenI) { + if (AXIS_IS_SHAPING(X)) { + if (goodZeta) { + ftMotion.cfg.zeta.x = zetaVal; + flag.update = true; + } } else - SERIAL_ECHOLNPGM("Wrong mode for [", C('F'), "] frequency scaling."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " zeta parameter."); } - #endif - // Parse zeta parameter (X axis). - if (parser.seenval('I')) { - const float val = parser.value_float(); - if (ftMotion.cfg.modeHasShaper()) { - if (WITHIN(val, 0.01f, 1.0f)) { - ftMotion.cfg.zeta[0] = val; - flag.update_n = flag.update_a = true; + // Parse X vtol parameter + if (seenQ) { + if (AXIS_IS_EISHAPING(X)) { + if (goodVtol) { + ftMotion.cfg.vtol.x = vtolVal; + flag.update = true; + } } else - SERIAL_ECHOLNPGM("Invalid X zeta [", C('I'), "] value."); // Zeta out of range. + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_A_NAME), " vtol parameter."); } - else - SERIAL_ECHOLNPGM("Wrong mode for zeta parameter."); - } - - // Parse vtol parameter (X axis). - if (parser.seenval('Q')) { - const float val = parser.value_float(); - if (ftMotion.cfg.modeHasShaper() && IS_EI_MODE(ftMotion.cfg.mode)) { - if (WITHIN(val, 0.00f, 1.0f)) { - ftMotion.cfg.vtol[0] = val; - flag.update_a = true; - } - else - SERIAL_ECHOLNPGM("Invalid X vtol [", C('Q'), "] value."); // VTol out of range. - } - else - SERIAL_ECHOLNPGM("Wrong mode for vtol parameter."); } #endif // HAS_X_AXIS #if HAS_Y_AXIS - // Parse frequency parameter (Y axis). - if (parser.seenval('B')) { - if (ftMotion.cfg.modeHasShaper()) { - const float val = parser.value_float(); - if (WITHIN(val, FTM_MIN_SHAPE_FREQ, (FTM_FS) / 2)) { - ftMotion.cfg.baseFreq[Y_AXIS] = val; - flag.update_n = flag.reset_ft = flag.report_h = true; - } - else // Frequency out of range. - SERIAL_ECHOLNPGM("Invalid frequency [", C('B'), "] value."); - } - else // Mode doesn't use frequency. - SERIAL_ECHOLNPGM("Wrong mode for [", C('B'), "] frequency."); - } + if (apply_xy || parser.seen_test('Y')) { - #if HAS_DYNAMIC_FREQ - // Parse frequency scaling parameter (Y axis). - if (parser.seenval('H')) { - if (modeUsesDynFreq) { - ftMotion.cfg.dynFreqK[Y_AXIS] = parser.value_float(); - flag.report_h = true; + // Parse Y frequency parameter + if (seenA) { + if (AXIS_IS_SHAPING(Y)) { + if (goodBaseFreq) { + ftMotion.cfg.baseFreq.y = baseFreqVal; + flag.update = flag.report = true; + } + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " [A] frequency."); + } + + #if HAS_DYNAMIC_FREQ + // Parse Y frequency scaling parameter + if (seenF && modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.y = baseDynFreqVal; + flag.report = true; + } + #endif + + // Parse Y zeta parameter + if (seenI) { + if (AXIS_IS_SHAPING(Y)) { + if (goodZeta) { + ftMotion.cfg.zeta.y = zetaVal; + flag.update = true; + } } else - SERIAL_ECHOLNPGM("Wrong mode for [", C('H'), "] frequency scaling."); + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " zeta parameter."); } - #endif - // Parse zeta parameter (Y axis). - if (parser.seenval('J')) { - const float val = parser.value_float(); - if (ftMotion.cfg.modeHasShaper()) { - if (WITHIN(val, 0.01f, 1.0f)) { - ftMotion.cfg.zeta[1] = val; - flag.update_n = flag.update_a = true; + // Parse Y vtol parameter + if (seenQ) { + if (AXIS_IS_EISHAPING(Y)) { + if (goodVtol) { + ftMotion.cfg.vtol.y = vtolVal; + flag.update = true; + } } else - SERIAL_ECHOLNPGM("Invalid Y zeta [", C('J'), "] value."); // Zeta Out of range + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_B_NAME), " vtol parameter."); } - else - SERIAL_ECHOLNPGM("Wrong mode for zeta parameter."); - } - - // Parse vtol parameter (Y axis). - if (parser.seenval('R')) { - const float val = parser.value_float(); - if (ftMotion.cfg.modeHasShaper() && IS_EI_MODE(ftMotion.cfg.mode)) { - if (WITHIN(val, 0.00f, 1.0f)) { - ftMotion.cfg.vtol[1] = val; - flag.update_a = true; - } - else - SERIAL_ECHOLNPGM("Invalid Y vtol [", C('R'), "] value."); // VTol out of range. - } - else - SERIAL_ECHOLNPGM("Wrong mode for vtol parameter."); } #endif // HAS_Y_AXIS - planner.synchronize(); + #if ENABLED(FTM_SHAPER_Z) - if (flag.update_n) ftMotion.refreshShapingN(); + if (parser.seen_test('Z')) { - if (flag.update_a) ftMotion.updateShapingA(); + // Parse Z frequency parameter + if (seenA) { + if (AXIS_IS_SHAPING(Z)) { + if (goodBaseFreq) { + ftMotion.cfg.baseFreq.z = baseFreqVal; + flag.update = flag.report = true; + } + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " [A] frequency."); + } - if (flag.reset_ft) { - stepper.ftMotion_syncPosition(); - ftMotion.reset(); - } + #if HAS_DYNAMIC_FREQ + // Parse Z frequency scaling parameter + if (seenF && modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.z = baseDynFreqVal; + flag.report = true; + } + #endif - if (flag.report_h) say_shaping(); + // Parse Z zeta parameter + if (seenI) { + if (AXIS_IS_SHAPING(Z)) { + if (goodZeta) { + ftMotion.cfg.zeta.z = zetaVal; + flag.update = true; + } + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " zeta parameter."); + } + + // Parse Z vtol parameter + if (seenQ) { + if (AXIS_IS_EISHAPING(Z)) { + if (goodVtol) { + ftMotion.cfg.vtol.z = vtolVal; + flag.update = true; + } + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C(STEPPER_C_NAME), " vtol parameter."); + } + } + + #endif // FTM_SHAPER_Z + + #if ENABLED(FTM_SHAPER_E) + + if (parser.seen_test('E')) { + + // Parse E frequency parameter + if (seenA) { + if (AXIS_IS_SHAPING(E)) { + if (goodBaseFreq) { + ftMotion.cfg.baseFreq.e = baseFreqVal; + flag.update = flag.report = true; + } + } + else // Mode doesn't use frequency. + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " [A] frequency."); + } + + #if HAS_DYNAMIC_FREQ + // Parse E frequency scaling parameter + if (seenF && modeUsesDynFreq) { + ftMotion.cfg.dynFreqK.e = baseDynFreqVal; + flag.report = true; + } + #endif + + // Parse E zeta parameter + if (seenI) { + if (AXIS_IS_SHAPING(E)) { + if (goodZeta) { + ftMotion.cfg.zeta.e = zetaVal; + flag.update = true; + } + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " zeta parameter."); + } + + // Parse E vtol parameter + if (seenQ) { + if (AXIS_IS_EISHAPING(E)) { + if (goodVtol) { + ftMotion.cfg.vtol.e = vtolVal; + flag.update = true; + } + } + else + SERIAL_ECHOLNPGM("?Wrong mode for ", C('E'), " vtol parameter."); + } + } + + #endif // FTM_SHAPER_E + + if (flag.update) ftMotion.update_shaping_params(); + + if (flag.report) say_shaping(); } #endif // FT_MOTION diff --git a/Marlin/src/gcode/feature/ft_motion/M494.cpp b/Marlin/src/gcode/feature/ft_motion/M494.cpp new file mode 100644 index 0000000000..840333f896 --- /dev/null +++ b/Marlin/src/gcode/feature/ft_motion/M494.cpp @@ -0,0 +1,137 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2025 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * 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 3 of the License, 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. If not, see . + * + */ +#include "../../../inc/MarlinConfigPre.h" + +#if ENABLED(FT_MOTION) + +#include "../../gcode.h" +#include "../../../module/ft_motion.h" +#include "../../../module/stepper.h" +#include "../../../module/planner.h" + +static FSTR_P get_trajectory_type_name() { + switch (ftMotion.getTrajectoryType()) { + default: + case TrajectoryType::TRAPEZOIDAL: return GET_TEXT_F(MSG_FTM_TRAPEZOIDAL); + case TrajectoryType::POLY5: return GET_TEXT_F(MSG_FTM_POLY5); + case TrajectoryType::POLY6: return GET_TEXT_F(MSG_FTM_POLY6); + } +} + +void say_ftm_settings() { + SERIAL_ECHOLN(F(" Trajectory: "), get_trajectory_type_name(), C('('), (uint8_t)ftMotion.getTrajectoryType(), C(')')); + + const ft_config_t &c = ftMotion.cfg; + + if (ftMotion.getTrajectoryType() == TrajectoryType::POLY6) + SERIAL_ECHOLNPGM(" Poly6 Overshoot: ", p_float_t(c.poly6_acceleration_overshoot, 3)); + + #if ENABLED(FTM_SMOOTHING) + #define _SMOO_REPORT(A) SERIAL_ECHOLN(F(" "), C(IAXIS_CHAR(_AXIS(A))), F(" smoothing time: "), p_float_t(c.smoothingTime.A, 3), C('s')); + CARTES_MAP(_SMOO_REPORT); + #endif +} + +void GcodeSuite::M494_report(const bool forReplay/*=true*/) { + TERN_(MARLIN_SMALL_BUILD, return); + + const ft_config_t &c = ftMotion.cfg; + + report_heading_etc(forReplay, F("FT Motion")); + SERIAL_ECHOPGM(" M494 T", (uint8_t)ftMotion.getTrajectoryType()); + + #if ENABLED(FTM_SMOOTHING) + SERIAL_ECHOPGM( + CARTES_PAIRED_LIST( + " X", c.smoothingTime.X, " Y", c.smoothingTime.Y, + " Z", c.smoothingTime.Z, " E", c.smoothingTime.E + ) + ); + #endif + + if (ftMotion.getTrajectoryType() == TrajectoryType::POLY6) + SERIAL_ECHOPGM(" O", c.poly6_acceleration_overshoot); + + SERIAL_EOL(); +} + +/** + * M494: Set Fixed-time Motion Control parameters + * + * Parameters: + * T Set trajectory generator type (0=TRAPEZOIDAL, 1=POLY5, 2=POLY6) + * O Set acceleration overshoot for POLY6 (1.25-1.875) + * X