diff --git a/.editorconfig b/.editorconfig index b8f6ef7f8e32a..57a5b2fb5ea4f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -14,6 +14,10 @@ end_of_line = lf indent_style = space indent_size = 2 -[{*.py,*.conf,*.sublime-project}] +[{*.py}] +indent_style = space +indent_size = 4 + +[{*.conf,*.sublime-project}] indent_style = tab indent_size = 4 diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml deleted file mode 100644 index 0e6028a31df70..0000000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ /dev/null @@ -1,169 +0,0 @@ -name: 🪲 Report a bug -description: Create a bug report to help improve Marlin Firmware -title: "[BUG] (bug summary)" -body: - - type: markdown - attributes: - value: > - Do you want to ask a question? Are you looking for support? Please use one of the [support links](https://github.com/MarlinFirmware/Marlin/issues/new/choose). - - - type: markdown - attributes: - value: | - **Thank you for reporting a bug in Marlin Firmware!** - - ## Before Reporting a Bug - - - Read and understand Marlin's [Code of Conduct](https://github.com/MarlinFirmware/Marlin/blob/master/.github/code_of_conduct.md). You are expected to comply with it, including treating everyone with respect. - - - Test with the [`bugfix-2.0.x` branch](https://github.com/MarlinFirmware/Marlin/archive/bugfix-2.0.x.zip) to see whether the issue still exists. - - ## Instructions - - Please follow the instructions below. Failure to do so may result in your issue being closed. See [Contributing to Marlin](https://github.com/MarlinFirmware/Marlin/blob/2.0.x/.github/contributing.md) for additional guidelines. - - 1. Provide a good title starting with [BUG]. - 2. Fill out all sections of this bug report form. - 3. Always attach configuration files so we can build and test your setup. - - - type: dropdown - attributes: - label: Did you test the latest `bugfix-2.0.x` code? - description: >- - Always try the latest code to make sure the issue you are reporting is not already fixed. To download - the latest code just [click this link](https://github.com/MarlinFirmware/Marlin/archive/bugfix-2.0.x.zip). - options: - - Yes, and the problem still exists. - - No, but I will test it now! - validations: - required: true - - - type: markdown - attributes: - value: | - # Bug Details - - - type: textarea - attributes: - label: Bug Description - description: >- - Describe the bug in this section. Tell us what you were trying to do and what - happened that you did not expect. Provide a clear and concise description of the - problem and include as many details as possible. - placeholder: | - Marlin doesn't work. - validations: - required: true - - - type: input - attributes: - label: Bug Timeline - description: Is this a new bug or an old issue? When did it first start? - - - type: textarea - attributes: - label: Expected behavior - description: >- - What did you expect to happen? - placeholder: I expected it to move left. - - - type: textarea - attributes: - label: Actual behavior - description: What actually happened instead? - placeholder: It moved right instead of left. - - - type: textarea - attributes: - label: Steps to Reproduce - description: >- - Please describe the steps needed to reproduce the issue. - placeholder: | - 1. [First Step] ... - 2. [Second Step] ... - 3. [and so on] ... - - - type: markdown - attributes: - value: | - # Your Setup - - - type: input - attributes: - label: Version of Marlin Firmware - description: "See the About Menu on the LCD or the output of `M115`. NOTE: For older releases we only patch critical bugs." - validations: - required: true - - - type: input - attributes: - label: Printer model - description: Creality Ender 3, Prusa mini, or Kossel Delta? - - - type: input - attributes: - label: Electronics - description: Stock electronics, upgrade board, or something else? - - - type: input - attributes: - label: Add-ons - description: Please list any hardware add-ons that could be involved. - - - type: dropdown - attributes: - label: Bed Leveling - description: What kind of bed leveling compensation are you using? - options: - - UBL Bilinear mesh - - ABL Bilinear mesh - - ABL Linear grid - - ABL 3-point - - MBL Manual Bed Leveling - - No Bed Leveling - - - type: dropdown - attributes: - label: Your Slicer - description: Do you use Slic3r, Prusa Slicer, Simplify3D, IdeaMaker...? - options: - - Slic3r - - Simplify3D - - Prusa Slicer - - IdeaMaker - - Cura - - Other (explain below) - - - type: dropdown - attributes: - label: Host Software - description: Do you use OctoPrint, Repetier Host, Pronterface...? - options: - - SD Card (headless) - - Repetier Host - - OctoPrint - - Pronterface - - Cura - - Same as my slicer - - Other (explain below) - - - type: markdown - attributes: - value: >- - ## Other things to include - - Please also be sure to include these items to help with troubleshooting: - - * **A ZIP file** containing your `Configuration.h` and `Configuration_adv.h`. - (Please don't paste lengthy configuration text here.) - * **Log output** from the host. (`M111 S247` for maximum logging.) - * **Images or videos** demonstrating the problem, if it helps to make it clear. - * **A G-Code file** that exposes the problem, if not affecting _all_ G-code. - - If you've made any other modifications to the firmware, please describe them in detail in the space provided. - - When pasting formatted text into the box below don't forget to put ` ``` ` (on its own line) before and after to make it readable. - - - type: textarea - attributes: - label: Additional information & file uploads diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 3f5d6fe5517d9..0000000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,20 +0,0 @@ -blank_issues_enabled: false -contact_links: - - name: 📖 Marlin Documentation - url: http://marlinfw.org/ - about: Lots of documentation on installing and using Marlin. - - name: 👤 MarlinFirmware Facebook group - url: https://www.facebook.com/groups/1049718498464482 - about: Please ask and answer questions here. - - name: 🕹 Marlin on Discord - url: https://discord.gg/n5NJ59y - about: Join the Discord server for support and discussion. - - name: 🔗 Marlin Discussion Forum - url: http://forums.reprap.org/list.php?415 - about: A searchable web forum hosted by RepRap dot org. - - name: 📺 Marlin Videos on YouTube - url: https://www.youtube.com/results?search_query=marlin+firmware - about: Tutorials and more from Marlin users all around the world. Great for new users! - - name: 💸 Want to donate? - url: https://www.thinkyhead.com/donate-to-marlin - about: Please take a look at the various options to support Marlin Firmware's development financially! diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml deleted file mode 100644 index df1938ccd84a3..0000000000000 --- a/.github/ISSUE_TEMPLATE/feature_request.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: ✨ Request a feature -description: Request a new Marlin Firmware feature -title: "[FR] (feature summary)" -labels: 'T: Feature Request' -body: - - type: markdown - attributes: - value: > - Do you want to ask a question? Are you looking for support? Please use one of the [support links](https://github.com/MarlinFirmware/Marlin/issues/new/choose). - - - type: markdown - attributes: - value: > - **Thank you for requesting a new Marlin Firmware feature!** - - ## Before Requesting a Feature - - - Read and understand Marlin's [Code of Conduct](https://github.com/MarlinFirmware/Marlin/blob/master/.github/code_of_conduct.md). You are expected to comply with it, including treating everyone with respect. - - - Check the latest [`bugfix-2.0.x` branch](https://github.com/MarlinFirmware/Marlin/archive/bugfix-2.0.x.zip) to see if the feature already exists. - - - Before you proceed with your request, please consider if it is necessary to make it into a firmware feature, or if it may be better suited for a slicer or host feature. - - - type: textarea - attributes: - label: Is your feature request related to a problem? Please describe. - description: A clear description of the problem (e.g., "I need X but Marlin can't do it [...]"). - - - type: textarea - attributes: - label: Are you looking for hardware support? - description: Tell us the printer, board, or peripheral that needs support. - - - type: textarea - attributes: - label: Describe the feature you want - description: A clear description of the feature and how you think it should work. - validations: - required: true - - - type: textarea - attributes: - label: Additional context - description: Add any other context or screenshots about the feature request here. diff --git a/.github/contributing.md b/.github/contributing.md index 6bc7b5a00514c..3113c1847c59e 100644 --- a/.github/contributing.md +++ b/.github/contributing.md @@ -34,8 +34,11 @@ This project and everyone participating in it is governed by the [Marlin Code of We have a Message Board and a Facebook group where our knowledgable user community can provide helpful advice if you have questions. -* [Marlin RepRap forum](https://reprap.org/forum/list.php?415) -* [MarlinFirmware on Facebook](https://www.facebook.com/groups/1049718498464482/) +- [Marlin Documentation](https://marlinfw.org) - Official Marlin documentation +- Facebook Group ["Marlin Firmware"](https://www.facebook.com/groups/1049718498464482/) +- RepRap.org [Marlin Forum](https://forums.reprap.org/list.php?415) +- Facebook Group ["Marlin Firmware for 3D Printers"](https://www.facebook.com/groups/3Dtechtalk/) +- [Marlin Configuration](https://www.youtube.com/results?search_query=marlin+configuration) on YouTube If chat is more your speed, you can join the MarlinFirmware Discord server: @@ -50,13 +53,13 @@ If chat is more your speed, you can join the MarlinFirmware Discord server: This section guides you through submitting a Bug Report for Marlin. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports. -Before creating a Bug Report, please test the "nightly" development branch, as you might find out that you don't need to create one. When you are creating a Bug Report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](issue_template.md), the information it asks for helps us resolve issues faster. +Before creating a Bug Report, please test the "nightly" development branch, as you might find out that you don't need to create one. When you are creating a Bug Report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](ISSUE_TEMPLATE/bug_report.yml), the information it asks for helps us resolve issues faster. > **Note:** Regressions can happen. If you find a **Closed** issue that seems like your issue, go ahead and open a new issue and include a link to the original issue in the body of your new one. All you need to create a link is the issue number, preceded by #. For example, #8888. #### How Do I Submit A (Good) Bug Report? -Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Use the New Issue button to create an issue and provide the following information by filling in [the template](issue_template.md). +Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/). Use the New Issue button to create an issue and provide the following information by filling in [the template](ISSUE_TEMPLATE/bug_report.yml). Explain the problem and include additional details to help maintainers reproduce the problem: @@ -88,12 +91,12 @@ Include details about your configuration and environment: This section guides you through submitting a suggestion for Marlin, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion and find related suggestions. -Before creating a suggestion, please check [this list](#before-submitting-a-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion). Fill in [the template](issue_template.md), including the steps that you imagine you would take if the feature you're requesting existed. +Before creating a suggestion, please check [this list](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-feature-request). Fill in [the template](ISSUE_TEMPLATE/feature_request.yml), including the steps that you imagine you would take if the feature you're requesting existed. #### Before Submitting a Feature Request * **Check the [Marlin website](https://marlinfw.org/)** for tips — you might discover that the feature is already included. Most importantly, check if you're using [the latest version of Marlin](https://github.com/MarlinFirmware/Marlin/releases) and if you can get the desired behavior by changing [Marlin's config settings](https://marlinfw.org/docs/configuration/configuration.html). -* **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aissue)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. +* **Perform a [cursory search](https://github.com/MarlinFirmware/Marlin/issues?q=is%3Aopen+is%3Aissue+label%3A%22T%3A+Feature+Request%22)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. #### How Do I Submit A (Good) Feature Request? @@ -116,7 +119,7 @@ Unsure where to begin contributing to Marlin? You can start by looking through t ### Pull Requests -Pull Requests should always be targeted to working branches (e.g., `bugfix-1.1.x` and/or `bugfix-2.0.x`) and never to release branches (e.g., `1.1.x`). If this is your first Pull Request, please read our [Guide to Pull Requests](https://marlinfw.org/docs/development/getting_started_pull_requests.html) and Github's [Pull Request](https://help.github.com/articles/creating-a-pull-request/) documentation. +Pull Requests should always be targeted to working branches (e.g., `bugfix-2.0.x` and/or `bugfix-1.1.x`) and never to release branches (e.g., `2.0.x` and/or `1.1.x`). If this is your first Pull Request, please read our [Guide to Pull Requests](https://marlinfw.org/docs/development/getting_started_pull_requests.html) and Github's [Pull Request](https://help.github.com/articles/creating-a-pull-request/) documentation. * Fill in [the required template](pull_request_template.md). * Don't include issue numbers in the PR title. diff --git a/.github/issue_template.md b/.github/issue_template.md deleted file mode 100644 index 6cb34b8f588a7..0000000000000 --- a/.github/issue_template.md +++ /dev/null @@ -1,35 +0,0 @@ - - -### Description - - - -### Steps to Reproduce - - - -1. [First Step] -2. [Second Step] -3. [and so on...] - -**Expected behavior:** [What you expect to happen] - -**Actual behavior:** [What actually happens] - -#### Additional Information - -* Include a ZIP file containing your `Configuration.h` and `Configuration_adv.h` files. -* Provide pictures or links to videos that clearly demonstrate the issue. -* See [How Can I Contribute](#how-can-i-contribute) for additional guidelines. diff --git a/.github/lock.yml b/.github/lock.yml deleted file mode 100644 index c5ceff66b0a76..0000000000000 --- a/.github/lock.yml +++ /dev/null @@ -1,40 +0,0 @@ -# -# Configuration for Lock Threads - https://github.com/dessant/lock-threads-app -# - -# Number of days of inactivity before a closed issue or pull request is locked -daysUntilLock: 60 - -# Skip issues and pull requests created before a given timestamp. Timestamp must -# follow ISO 8601 (`YYYY-MM-DD`). Set to `false` to disable -skipCreatedBefore: false - -# Issues and pull requests with these labels will be ignored. Set to `[]` to disable -exemptLabels: [ 'no-locking' ] - -# Label to add before locking, such as `outdated`. Set to `false` to disable -lockLabel: false - -# Comment to post before locking. Set to `false` to disable -lockComment: > - This thread has been automatically locked since there has not been - any recent activity after it was closed. Please open a new issue for - related bugs. - -# Assign `resolved` as the reason for locking. Set to `false` to disable -setLockReason: true - -# Limit to only `issues` or `pulls` -# only: issues - -# Optionally, specify configuration settings just for `issues` or `pulls` -# issues: -# exemptLabels: -# - help-wanted -# lockLabel: outdated - -# pulls: -# daysUntilLock: 30 - -# Repository to extend settings from -# _extends: repo diff --git a/.github/workflows/bump-date.yml b/.github/workflows/bump-date.yml deleted file mode 100644 index a1942777d1aed..0000000000000 --- a/.github/workflows/bump-date.yml +++ /dev/null @@ -1,36 +0,0 @@ -# -# bump-date.yml -# Bump the distribution date once per day -# - -name: Bump Distribution Date - -on: - schedule: - - cron: '0 0 * * *' - -jobs: - bump_date: - name: Bump Distribution Date - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - steps: - - - name: Check out bugfix-2.0.x - uses: actions/checkout@v2 - with: - ref: bugfix-2.0.x - - - name: Bump Distribution Date - run: | - # Inline Bump Script - DIST=$( date +"%Y-%m-%d" ) - eval "sed -E -i 's/(#define +STRING_DISTRIBUTION_DATE) .*$/\1 \"$DIST\"/g' Marlin/src/inc/Version.h" && \ - eval "sed -E -i 's/(#define +STRING_DISTRIBUTION_DATE) .*$/\1 \"$DIST\"/g' Marlin/Version.h" && \ - git config user.name "${GITHUB_ACTOR}" && \ - git config user.email "${GITHUB_ACTOR}@users.noreply.github.com" && \ - git add . && \ - git commit -m "[cron] Bump distribution date ($DIST)" && \ - git push diff --git a/.github/workflows/check-pr.yml b/.github/workflows/check-pr.yml deleted file mode 100644 index 93a110f270d3e..0000000000000 --- a/.github/workflows/check-pr.yml +++ /dev/null @@ -1,33 +0,0 @@ -# -# check-pr.yml -# Close PRs directed at release branches -# - -name: PR Bad Target - -on: - pull_request_target: - types: [opened] - branches: - - 1.0.x - - 1.1.x - - 2.0.x - -jobs: - bad_target: - name: PR Bad Target - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - steps: - - uses: superbrothers/close-pull-request@v3 - with: - comment: > - Thanks for your contribution! Unfortunately we can't accept PRs directed at release branches. We make patches to the bugfix branches and only later do we push them out as releases. - - Please redo this PR starting with the `bugfix-2.0.x` branch and be careful to target `bugfix-2.0.x` when resubmitting the PR. - - It may help to set your fork's default branch to `bugfix-2.0.x`. - - See [this page](http://marlinfw.org/docs/development/getting_started_pull_requests.html) for full instructions. diff --git a/.github/workflows/clean-closed.yml b/.github/workflows/clean-closed.yml deleted file mode 100644 index befec4498f257..0000000000000 --- a/.github/workflows/clean-closed.yml +++ /dev/null @@ -1,39 +0,0 @@ -# -# clean-closed.yml -# Remove obsolete labels when an Issue or PR is closed -# - -name: Clean Closed - -on: - pull_request: - types: [closed] - issues: - types: [closed] - -jobs: - remove_label: - runs-on: ubuntu-latest - - strategy: - matrix: - label: - - "S: Don't Merge" - - "S: Hold for 2.1" - - "S: Please Merge" - - "S: Please Test" - - "help wanted" - - "Needs: Discussion" - - "Needs: Documentation" - - "Needs: More Data" - - "Needs: Patch" - - "Needs: Testing" - - "Needs: Work" - - steps: - - uses: actions/checkout@v2 - - name: Remove Labels - uses: actions-ecosystem/action-remove-labels@v1 - with: - github_token: ${{ github.token }} - labels: ${{ matrix.label }} diff --git a/.github/workflows/close-stale.yml b/.github/workflows/close-stale.yml deleted file mode 100644 index f017907d29349..0000000000000 --- a/.github/workflows/close-stale.yml +++ /dev/null @@ -1,28 +0,0 @@ -# -# close-stale.yml -# Close open issues after a period of inactivity -# - -name: Close Stale Issues - -on: - schedule: - - cron: "22 1 * * *" - -jobs: - stale: - name: Close Stale Issues - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - steps: - - uses: actions/stale@v3 - with: - repo-token: ${{ secrets.GITHUB_TOKEN }} - stale-issue-message: 'This issue has had no activity in the last 60 days. Please add a reply if you want to keep this issue active, otherwise it will be automatically closed within 10 days.' - days-before-stale: 60 - days-before-close: 10 - stale-issue-label: 'stale-closing-soon' - exempt-all-assignees: true - exempt-issue-labels: 'Bug: Confirmed !,T: Feature Request,Needs: Discussion,Needs: Documentation,Needs: More Data,Needs: Patch,Needs: Work,Needs: Testing,help wanted,no-locking' diff --git a/.github/workflows/lock-closed.yml b/.github/workflows/lock-closed.yml deleted file mode 100644 index 81145688288bd..0000000000000 --- a/.github/workflows/lock-closed.yml +++ /dev/null @@ -1,32 +0,0 @@ -# -# lock-closed.yml -# Lock closed issues after a period of inactivity -# - -name: Lock Closed Issues - -on: - schedule: - - cron: '0 1/13 * * *' - -jobs: - lock: - name: Lock Closed Issues - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - steps: - - uses: dessant/lock-threads@v2 - with: - github-token: ${{ github.token }} - process-only: 'issues' - issue-lock-inactive-days: '60' - issue-exclude-created-before: '2017-07-01T00:00:00Z' - issue-exclude-labels: 'no-locking' - issue-lock-labels: '' - issue-lock-comment: > - This issue has been automatically locked since there - has not been any recent activity after it was closed. - Please open a new issue for related bugs. - issue-lock-reason: '' diff --git a/.github/workflows/test-builds.yml b/.github/workflows/test-builds.yml deleted file mode 100644 index f5ce466d9051f..0000000000000 --- a/.github/workflows/test-builds.yml +++ /dev/null @@ -1,145 +0,0 @@ -# -# test-builds.yml -# Do test builds to catch compile errors -# - -name: CI - -on: - pull_request: - branches: - - bugfix-2.0.x - paths-ignore: - - config/** - - data/** - - docs/** - - '**/*.md' - push: - branches: - - bugfix-2.0.x - paths-ignore: - - config/** - - data/** - - docs/** - - '**/*.md' - -jobs: - test_builds: - name: Run All Tests - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - strategy: - matrix: - test-platform: - # Base Environments - - - DUE - - DUE_archim - - esp32 - - linux_native - - mega2560 - - at90usb1286_dfu - - teensy31 - - teensy35 - - teensy41 - - SAMD51_grandcentral_m4 - - # Extended AVR Environments - - - FYSETC_F6 - - mega1280 - - rambo - - sanguino1284p - - sanguino644p - - # STM32F1 (Maple) Environments - - #- STM32F103RC_btt_maple - - STM32F103RC_btt_USB_maple - - STM32F103RC_fysetc_maple - - STM32F103RC_meeb - - jgaurora_a5s_a1_maple - - STM32F103VE_longer_maple - #- mks_robin_maple - - mks_robin_lite_maple - - mks_robin_pro_maple - #- mks_robin_nano35_maple - #- STM32F103RET6_creality_maple - - STM32F103VE_ZM3E4V2_USB_maple - - # STM32 (ST) Environments - - - STM32F103RC_btt - #- STM32F103RC_btt_USB - - STM32F103RE_btt - - STM32F103RE_btt_USB - - STM32F103RET6_creality - - STM32F103VE_longer - - STM32F407VE_black - - STM32F401VE_STEVAL - - BIGTREE_BTT002 - - BIGTREE_SKR_PRO - - BIGTREE_GTR_V1_0 - - mks_robin - - ARMED - - FYSETC_S6 - - STM32F070CB_malyan - - STM32F070RB_malyan - - malyan_M300 - - FLYF407ZG - - rumba32 - - LERDGEX - - LERDGEK - - mks_robin_nano35 - - NUCLEO_F767ZI - - REMRAM_V1 - - BTT_SKR_SE_BX - - chitu_f103 - - Index_Mobo_Rev03 - - # Put lengthy tests last - - - LPC1768 - - LPC1769 - - # Non-working environment tests - #- at90usb1286_cdc - #- STM32F103CB_malyan - #- STM32F103RE - #- mks_robin_mini - - steps: - - - name: Check out the PR - uses: actions/checkout@v2 - - - name: Cache pip - uses: actions/cache@v2 - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Cache PlatformIO - uses: actions/cache@v2 - with: - path: ~/.platformio - key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} - - - name: Select Python 3.7 - uses: actions/setup-python@v2 - with: - python-version: '3.7' # Version range or exact version of a Python version to use, using semvers version range syntax. - architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified - - - name: Install PlatformIO - run: | - pip install -U https://github.com/platformio/platformio-core/archive/develop.zip - platformio update - - - name: Run ${{ matrix.test-platform }} Tests - run: | - make tests-single-ci TEST_TARGET=${{ matrix.test-platform }} diff --git a/.github/workflows/unlock-reopened.yml b/.github/workflows/unlock-reopened.yml deleted file mode 100644 index 614ef3fab297f..0000000000000 --- a/.github/workflows/unlock-reopened.yml +++ /dev/null @@ -1,22 +0,0 @@ -# -# unlock-reopened.yml -# Unlock an issue whenever it is re-opened -# - -name: "Unlock reopened issue" - -on: - issues: - types: [reopened] - -jobs: - unlock: - name: Unlock Reopened - if: github.repository == 'MarlinFirmware/Marlin' - - runs-on: ubuntu-latest - - steps: - - uses: OSDKDev/unlock-issues@v1.1 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore index bc603ba38b0d5..0b852d767325a 100755 --- a/.gitignore +++ b/.gitignore @@ -22,12 +22,16 @@ # Generated files _Version.h bdf2u8g +marlin_config.json +mczip.h +*.gen +*.sublime-workspace # # OS # applet/ -*.DS_Store +.DS_Store # # Misc @@ -37,7 +41,6 @@ applet/ *.rej *.bak *.idea -*.s *.i *.ii *.swp @@ -137,20 +140,19 @@ __vm/ vc-fileutils.settings # Visual Studio Code -.vscode -.vscode/.browse.c_cpp.db* -.vscode/c_cpp_properties.json -.vscode/launch.json -.vscode/*.db +.vscode/* +!.vscode/extensions.json #Simulation imgui.ini eeprom.dat +spi_flash.bin #cmake CMakeLists.txt src/CMakeLists.txt CMakeListsPrivate.txt +build/ # CLion cmake-build-* @@ -167,7 +169,3 @@ __pycache__ # IOLogger logs *_log.csv - -# Simulation / Native -eeprom.dat -imgui.ini diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000000000..f495d14f53e87 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,11 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "marlinfirmware.auto-build", + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index e90e90b1462df..99ccba028c188 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -35,7 +35,7 @@ * * Advanced settings can be found in Configuration_adv.h */ -#define CONFIGURATION_H_VERSION 02000902 +#define CONFIGURATION_H_VERSION 02000905 //=========================================================================== //============================= Getting Started ============================= @@ -57,15 +57,6 @@ * https://www.thingiverse.com/thing:1278865 */ -//=========================================================================== -//========================== DELTA / SCARA / TPARA ========================== -//=========================================================================== -// -// Download configurations from the link above and customize for your machine. -// Examples are located in config/examples/delta, .../SCARA, and .../TPARA. -// -//=========================================================================== - // @section info // Author info of this build printed to the host during boot and M115 @@ -94,6 +85,11 @@ // @section machine +// Choose the name from boards.h that matches your setup +#ifndef MOTHERBOARD + #define MOTHERBOARD BOARD_RAMPS_14_EFB +#endif + /** * 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. @@ -137,11 +133,6 @@ // Enable the Bluetooth serial interface on AT90USB devices //#define BLUETOOTH -// Choose the name from boards.h that matches your setup -#ifndef MOTHERBOARD - #define MOTHERBOARD BOARD_RAMPS_14_EFB -#endif - // Name displayed in the LCD "Ready" message and Info menu //#define CUSTOM_MACHINE_NAME "3D Printer" @@ -150,42 +141,63 @@ //#define MACHINE_UUID "00000000-0000-0000-0000-000000000000" /** - * Define the number of coordinated linear axes. - * See https://github.com/DerAndere1/Marlin/wiki - * Each linear axis gets its own stepper control and endstop: + * Stepper Drivers * - * Steppers: *_STEP_PIN, *_ENABLE_PIN, *_DIR_PIN, *_ENABLE_ON - * Endstops: *_STOP_PIN, USE_*MIN_PLUG, USE_*MAX_PLUG - * Axes: *_MIN_POS, *_MAX_POS, INVERT_*_DIR - * Planner: DEFAULT_AXIS_STEPS_PER_UNIT, DEFAULT_MAX_FEEDRATE - * DEFAULT_MAX_ACCELERATION, AXIS_RELATIVE_MODES, - * MICROSTEP_MODES, MANUAL_FEEDRATE + * These settings allow Marlin to tune stepper driver timing and enable advanced options for + * stepper drivers that support them. You may also override timing options in Configuration_adv.h. * - * :[3, 4, 5, 6] + * Use TMC2208/TMC2208_STANDALONE for TMC2225 drivers and TMC2209/TMC2209_STANDALONE for TMC2226 drivers. + * + * Options: A4988, A5984, DRV8825, LV8729, L6470, L6474, POWERSTEP01, + * TB6560, TB6600, TMC2100, + * TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE, + * TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE, + * TMC26X, TMC26X_STANDALONE, TMC2660, TMC2660_STANDALONE, + * TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE + * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'L6470', 'L6474', 'POWERSTEP01', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE'] */ -//#define LINEAR_AXES 3 +#define X_DRIVER_TYPE A4988 +#define Y_DRIVER_TYPE A4988 +#define Z_DRIVER_TYPE A4988 +//#define X2_DRIVER_TYPE A4988 +//#define Y2_DRIVER_TYPE A4988 +//#define Z2_DRIVER_TYPE A4988 +//#define Z3_DRIVER_TYPE A4988 +//#define Z4_DRIVER_TYPE A4988 +//#define I_DRIVER_TYPE A4988 +//#define J_DRIVER_TYPE A4988 +//#define K_DRIVER_TYPE A4988 +#define E0_DRIVER_TYPE A4988 +//#define E1_DRIVER_TYPE A4988 +//#define E2_DRIVER_TYPE A4988 +//#define E3_DRIVER_TYPE A4988 +//#define E4_DRIVER_TYPE A4988 +//#define E5_DRIVER_TYPE A4988 +//#define E6_DRIVER_TYPE A4988 +//#define E7_DRIVER_TYPE A4988 /** - * Axis codes for additional axes: - * This defines the axis code that is used in G-code commands to - * reference a specific axis. - * 'A' for rotational axis parallel to X - * 'B' for rotational axis parallel to Y - * 'C' for rotational axis parallel to Z - * 'U' for secondary linear axis parallel to X - * 'V' for secondary linear axis parallel to Y - * 'W' for secondary linear axis parallel to Z - * Regardless of the settings, firmware-internal axis IDs are - * I (AXIS4), J (AXIS5), K (AXIS6). + * Additional Axis Settings + * + * AXISn_NAME defines the letter used to refer to the axis in (most) G-code commands. + * By convention the names and roles are typically: + * 'A' : Rotational axis parallel to X + * 'B' : Rotational axis parallel to Y + * 'C' : Rotational axis parallel to Z + * 'U' : Secondary linear axis parallel to X + * 'V' : Secondary linear axis parallel to Y + * 'W' : Secondary linear axis parallel to Z + * + * Regardless of these settings the axes are internally named I, J, K. */ -#if LINEAR_AXES >= 4 +#ifdef I_DRIVER_TYPE #define AXIS4_NAME 'A' // :['A', 'B', 'C', 'U', 'V', 'W'] #endif -#if LINEAR_AXES >= 5 - #define AXIS5_NAME 'B' // :['A', 'B', 'C', 'U', 'V', 'W'] +#ifdef J_DRIVER_TYPE + #define AXIS5_NAME 'B' // :['B', 'C', 'U', 'V', 'W'] #endif -#if LINEAR_AXES >= 6 - #define AXIS6_NAME 'C' // :['A', 'B', 'C', 'U', 'V', 'W'] +#ifdef K_DRIVER_TYPE + #define AXIS6_NAME 'C' // :['C', 'U', 'V', 'W'] #endif // @section extruder @@ -371,8 +383,12 @@ //#define PS_OFF_SOUND // Beep 1s when power off #define PSU_ACTIVE_STATE LOW // Set 'LOW' for ATX, 'HIGH' for X-Box - //#define PSU_DEFAULT_OFF // Keep power off until enabled directly with M80 - //#define PSU_POWERUP_DELAY 250 // (ms) Delay for the PSU to warm up to full power + //#define PSU_DEFAULT_OFF // Keep power off until enabled directly with M80 + //#define PSU_POWERUP_DELAY 250 // (ms) Delay for the PSU to warm up to full power + //#define LED_POWEROFF_TIMEOUT 10000 // (ms) Turn off LEDs after power-off, with this amount of delay + + //#define POWER_OFF_TIMER // Enable M81 D to power off after a delay + //#define POWER_OFF_WAIT_FOR_COOLDOWN // Enable M81 S to power off only after cooldown //#define PSU_POWERUP_GCODE "M355 S1" // G-code to run after power-on (e.g., case light on) //#define PSU_POWEROFF_GCODE "M355 S0" // G-code to run before power-off (e.g., case light off) @@ -384,12 +400,14 @@ #define AUTO_POWER_CONTROLLERFAN #define AUTO_POWER_CHAMBER_FAN #define AUTO_POWER_COOLER_FAN - //#define AUTO_POWER_E_TEMP 50 // (°C) Turn on PSU if any extruder is over this temperature - //#define AUTO_POWER_CHAMBER_TEMP 30 // (°C) Turn on PSU if the chamber is over this temperature - //#define AUTO_POWER_COOLER_TEMP 26 // (°C) Turn on PSU if the cooler is over this temperature #define POWER_TIMEOUT 30 // (s) Turn off power if the machine is idle for this duration //#define POWER_OFF_DELAY 60 // (s) Delay of poweroff after M81 command. Useful to let fans run for extra time. #endif + #if EITHER(AUTO_POWER_CONTROL, POWER_OFF_WAIT_FOR_COOLDOWN) + //#define AUTO_POWER_E_TEMP 50 // (°C) PSU on if any extruder is over this temperature + //#define AUTO_POWER_CHAMBER_TEMP 30 // (°C) PSU on if the chamber is over this temperature + //#define AUTO_POWER_COOLER_TEMP 26 // (°C) PSU on if the cooler is over this temperature + #endif #endif //=========================================================================== @@ -431,6 +449,9 @@ * 5 : 100kΩ ATC Semitec 104GT-2/104NT-4-R025H42G - Used in ParCan, J-Head, and E3D, SliceEngineering 300°C * 501 : 100kΩ Zonestar - Tronxy X3A * 502 : 100kΩ Zonestar - used by hot bed in Zonestar Průša P802M + * 503 : 100kΩ Zonestar (Z8XM2) Heated Bed thermistor + * 504 : 100kΩ Zonestar P802QR2 (Part# QWG-104F-B3950) Hotend Thermistor + * 505 : 100kΩ Zonestar P802QR2 (Part# QWG-104F-3950) Bed Thermistor * 512 : 100kΩ RPW-Ultra hotend * 6 : 100kΩ EPCOS - Not as accurate as table #1 (created using a fluke thermocouple) * 7 : 100kΩ Honeywell 135-104LAG-J01 @@ -450,6 +471,7 @@ * 61 : 100kΩ Formbot/Vivedino 350°C Thermistor - beta 3950 * 66 : 4.7MΩ Dyze Design High Temperature Thermistor * 67 : 500kΩ SliceEngineering 450°C Thermistor + * 68 : PT100 amplifier board from Dyze Design * 70 : 100kΩ bq Hephestos 2 * 75 : 100kΩ Generic Silicon Heat Pad with NTC100K MGB18-104F39050L32 * 2000 : 100kΩ Ultimachine Rambo TDK NTCG104LH104KT1 NTC100K motherboard Thermistor @@ -579,19 +601,19 @@ //=========================================================================== //============================= PID Settings ================================ //=========================================================================== -// PID Tuning Guide here: https://reprap.org/wiki/PID_Tuning -// Comment the following line to disable PID and enable bang-bang. -#define PIDTEMP +// Enable PIDTEMP for PID control or MPCTEMP for Predictive Model. +// temperature control. Disable both for bang-bang heating. +#define PIDTEMP // See the PID Tuning Guide at https://reprap.org/wiki/PID_Tuning +//#define MPCTEMP // ** EXPERIMENTAL ** + #define BANG_MAX 255 // Limits current to nozzle while in bang-bang mode; 255=full current #define PID_MAX BANG_MAX // Limits current to nozzle 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_EDIT_MENU // Add PID editing to the "Advanced Settings" menu. (~700 bytes of PROGMEM) - //#define PID_AUTOTUNE_MENU // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of PROGMEM) //#define PID_PARAMS_PER_HOTEND // Uses separate PID parameters for each extruder (useful for mismatched extruders) - // Set/get with gcode: M301 E[extruder number, 0-2] + // Set/get with G-code: M301 E[extruder number, 0-2] #if ENABLED(PID_PARAMS_PER_HOTEND) // Specify up to one value per hotend here, according to your setup. @@ -604,7 +626,49 @@ #define DEFAULT_Ki 1.08 #define DEFAULT_Kd 114.00 #endif -#endif // PIDTEMP +#endif + +/** + * Model Predictive Control for hotend + * + * Use a physical model of the hotend to control temperature. When configured correctly + * this gives better responsiveness and stability than PID and it also removes the need + * for PID_EXTRUSION_SCALING and PID_FAN_SCALING. Use M306 T to autotune the model. + */ +#if ENABLED(MPCTEMP) + //#define MPC_EDIT_MENU // Add MPC editing to the "Advanced Settings" menu. (~1300 bytes of flash) + //#define MPC_AUTOTUNE_MENU // Add MPC auto-tuning to the "Advanced Settings" menu. (~350 bytes of flash) + + #define MPC_MAX BANG_MAX // (0..255) Current to nozzle while MPC is active. + #define MPC_HEATER_POWER { 40.0f } // (W) Heat cartridge powers. + + #define MPC_INCLUDE_FAN // Model the fan speed? + + // Measured physical constants from M306 + #define MPC_BLOCK_HEAT_CAPACITY { 16.7f } // (J/K) Heat block heat capacities. + #define MPC_SENSOR_RESPONSIVENESS { 0.22f } // (K/s per ∆K) Rate of change of sensor temperature from heat block. + #define MPC_AMBIENT_XFER_COEFF { 0.068f } // (W/K) Heat transfer coefficients from heat block to room air with fan off. + #if ENABLED(MPC_INCLUDE_FAN) + #define MPC_AMBIENT_XFER_COEFF_FAN255 { 0.097f } // (W/K) Heat transfer coefficients from heat block to room air with fan on full. + #endif + + // For one fan and multiple hotends MPC needs to know how to apply the fan cooling effect. + #if ENABLED(MPC_INCLUDE_FAN) + //#define MPC_FAN_0_ALL_HOTENDS + //#define MPC_FAN_0_ACTIVE_HOTEND + #endif + + #define FILAMENT_HEAT_CAPACITY_PERMM { 5.6e-3f } // 0.0056 J/K/mm for 1.75mm PLA (0.0149 J/K/mm for 2.85mm PLA). + //#define FILAMENT_HEAT_CAPACITY_PERMM { 3.6e-3f } // 0.0036 J/K/mm for 1.75mm PETG (0.0094 J/K/mm for 2.85mm PETG). + + // Advanced options + #define MPC_SMOOTHING_FACTOR 0.5f // (0.0...1.0) Noisy temperature sensors may need a lower value for stabilization. + #define MPC_MIN_AMBIENT_CHANGE 1.0f // (K/s) Modeled ambient temperature rate of change, when correcting model inaccuracies. + #define MPC_STEADYSTATE 0.5f // (K/s) Temperature change rate for steady state logic to be enforced. + + #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. +#endif //=========================================================================== //====================== PID > Bed Temperature Control ====================== @@ -698,6 +762,9 @@ //#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 // 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) + //#define PID_AUTOTUNE_MENU // Add PID auto-tuning to the "Advanced Settings" menu. (~250 bytes of flash) #endif // @section extruder @@ -756,6 +823,7 @@ //#define COREZX //#define COREZY //#define MARKFORGED_XY // MarkForged. See https://reprap.org/forum/read.php?152,504042 +//#define MARKFORGED_YX // Enable for a belt style printer with endless "Z" motion //#define BELTPRINTER @@ -767,6 +835,134 @@ #define POLAR_SEGMENTS_PER_SECOND 5 #endif +// Enable for DELTA kinematics and configure below +//#define DELTA +#if ENABLED(DELTA) + + // Make delta curves from many straight lines (linear interpolation). + // This is a trade-off between visible corners (not enough segments) + // and processor overload (too many expensive sqrt calls). + #define DELTA_SEGMENTS_PER_SECOND 200 + + // After homing move down to a height where XY movement is unconstrained + //#define DELTA_HOME_TO_SAFE_ZONE + + // Delta calibration menu + // uncomment to add three points calibration menu option. + // See http://minow.blogspot.com/index.html#4918805519571907051 + //#define DELTA_CALIBRATION_MENU + + // uncomment to add G33 Delta Auto-Calibration (Enable EEPROM_SETTINGS to store results) + //#define DELTA_AUTO_CALIBRATION + + // NOTE NB all values for DELTA_* values MUST be floating point, so always have a decimal point in them + + #if ENABLED(DELTA_AUTO_CALIBRATION) + // set the default number of probe points : n*n (1 -> 7) + #define DELTA_CALIBRATION_DEFAULT_POINTS 4 + #endif + + #if EITHER(DELTA_AUTO_CALIBRATION, DELTA_CALIBRATION_MENU) + // Set the steprate for papertest probing + #define PROBE_MANUALLY_STEP 0.05 // (mm) + #endif + + // Print surface diameter/2 minus unreachable space (avoid collisions with vertical towers). + #define DELTA_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) + + // Distance between bed and nozzle Z home position + #define DELTA_HEIGHT 250.00 // (mm) Get this value from G33 auto calibrate + + #define DELTA_ENDSTOP_ADJ { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate + + // Horizontal distance bridged by diagonal push rods when effector is centered. + #define DELTA_RADIUS 124.0 // (mm) Get this value from G33 auto calibrate + + // Trim adjustments for individual towers + // tower angle corrections for X and Y tower / rotate XYZ so Z tower angle = 0 + // measured in degrees anticlockwise looking from above the printer + #define DELTA_TOWER_ANGLE_TRIM { 0.0, 0.0, 0.0 } // Get these values from G33 auto calibrate + + // Delta radius and diagonal rod adjustments (mm) + //#define DELTA_RADIUS_TRIM_TOWER { 0.0, 0.0, 0.0 } + //#define DELTA_DIAGONAL_ROD_TRIM_TOWER { 0.0, 0.0, 0.0 } +#endif + +/** + * MORGAN_SCARA was developed by QHARLEY in South Africa in 2012-2013. + * Implemented and slightly reworked by JCERNY in June, 2014. + * + * Mostly Printed SCARA is an open source design by Tyler Williams. See: + * https://www.thingiverse.com/thing:2487048 + * https://www.thingiverse.com/thing:1241491 + */ +//#define MORGAN_SCARA +//#define MP_SCARA +#if EITHER(MORGAN_SCARA, MP_SCARA) + // If movement is choppy try lowering this value + #define SCARA_SEGMENTS_PER_SECOND 200 + + // Length of inner and outer support arms. Measure arm lengths precisely. + #define SCARA_LINKAGE_1 150 // (mm) + #define SCARA_LINKAGE_2 150 // (mm) + + // SCARA tower offset (position of Tower relative to bed zero position) + // This needs to be reasonably accurate as it defines the printbed position in the SCARA space. + #define SCARA_OFFSET_X 100 // (mm) + #define SCARA_OFFSET_Y -56 // (mm) + + #if ENABLED(MORGAN_SCARA) + + //#define DEBUG_SCARA_KINEMATICS + #define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly + + // Radius around the center where the arm cannot reach + #define MIDDLE_DEAD_ZONE_R 0 // (mm) + + #define THETA_HOMING_OFFSET 0 // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073 + #define PSI_HOMING_OFFSET 0 // Calculated from Calibration Guide and M364 / M114. See http://reprap.harleystudio.co.za/?page_id=1073 + + #elif ENABLED(MP_SCARA) + + #define SCARA_OFFSET_THETA1 12 // degrees + #define SCARA_OFFSET_THETA2 131 // degrees + + #endif + +#endif + +// Enable for TPARA kinematics and configure below +//#define AXEL_TPARA +#if ENABLED(AXEL_TPARA) + #define DEBUG_ROBOT_KINEMATICS + #define ROBOT_SEGMENTS_PER_SECOND 200 + + // Length of inner and outer support arms. Measure arm lengths precisely. + #define ROBOT_LINKAGE_1 120 // (mm) + #define ROBOT_LINKAGE_2 120 // (mm) + + // SCARA tower offset (position of Tower relative to bed zero position) + // This needs to be reasonably accurate as it defines the printbed position in the SCARA space. + #define ROBOT_OFFSET_X 0 // (mm) + #define ROBOT_OFFSET_Y 0 // (mm) + #define ROBOT_OFFSET_Z 0 // (mm) + + #define SCARA_FEEDRATE_SCALING // Convert XY feedrate from mm/s to degrees/s on the fly + + // Radius around the center where the arm cannot reach + #define MIDDLE_DEAD_ZONE_R 0 // (mm) + + // Calculated from Calibration Guide and M360 / M114. See http://reprap.harleystudio.co.za/?page_id=1073 + #define THETA_HOMING_OFFSET 0 + #define PSI_HOMING_OFFSET 0 +#endif + //=========================================================================== //============================== Endstop Settings =========================== //=========================================================================== @@ -842,44 +1038,6 @@ #define K_MAX_ENDSTOP_INVERTING false // Set to true to invert the logic of the endstop. #define Z_MIN_PROBE_ENDSTOP_INVERTING false // Set to true to invert the logic of the probe. -/** - * Stepper Drivers - * - * These settings allow Marlin to tune stepper driver timing and enable advanced options for - * stepper drivers that support them. You may also override timing options in Configuration_adv.h. - * - * A4988 is assumed for unspecified drivers. - * - * Use TMC2208/TMC2208_STANDALONE for TMC2225 drivers and TMC2209/TMC2209_STANDALONE for TMC2226 drivers. - * - * Options: A4988, A5984, DRV8825, LV8729, L6470, L6474, POWERSTEP01, - * TB6560, TB6600, TMC2100, - * TMC2130, TMC2130_STANDALONE, TMC2160, TMC2160_STANDALONE, - * TMC2208, TMC2208_STANDALONE, TMC2209, TMC2209_STANDALONE, - * TMC26X, TMC26X_STANDALONE, TMC2660, TMC2660_STANDALONE, - * TMC5130, TMC5130_STANDALONE, TMC5160, TMC5160_STANDALONE - * :['A4988', 'A5984', 'DRV8825', 'LV8729', 'L6470', 'L6474', 'POWERSTEP01', 'TB6560', 'TB6600', 'TMC2100', 'TMC2130', 'TMC2130_STANDALONE', 'TMC2160', 'TMC2160_STANDALONE', 'TMC2208', 'TMC2208_STANDALONE', 'TMC2209', 'TMC2209_STANDALONE', 'TMC26X', 'TMC26X_STANDALONE', 'TMC2660', 'TMC2660_STANDALONE', 'TMC5130', 'TMC5130_STANDALONE', 'TMC5160', 'TMC5160_STANDALONE'] - */ -#define X_DRIVER_TYPE A4988 -#define Y_DRIVER_TYPE A4988 -#define Z_DRIVER_TYPE A4988 -//#define X2_DRIVER_TYPE A4988 -//#define Y2_DRIVER_TYPE A4988 -//#define Z2_DRIVER_TYPE A4988 -//#define Z3_DRIVER_TYPE A4988 -//#define Z4_DRIVER_TYPE A4988 -//#define I_DRIVER_TYPE A4988 -//#define J_DRIVER_TYPE A4988 -//#define K_DRIVER_TYPE A4988 -#define E0_DRIVER_TYPE A4988 -//#define E1_DRIVER_TYPE A4988 -//#define E2_DRIVER_TYPE A4988 -//#define E3_DRIVER_TYPE A4988 -//#define E4_DRIVER_TYPE A4988 -//#define E5_DRIVER_TYPE A4988 -//#define E6_DRIVER_TYPE A4988 -//#define E7_DRIVER_TYPE A4988 - // Enable this feature if all enabled endstop pins are interrupt-capable. // This will remove the need to poll the interrupt pins, saving many CPU cycles. //#define ENDSTOP_INTERRUPTS_FEATURE @@ -961,9 +1119,9 @@ * M204 R Retract Acceleration * M204 T Travel Acceleration */ -#define DEFAULT_ACCELERATION 3000 // X, Y, Z and E acceleration for printing moves +#define DEFAULT_ACCELERATION 3000 // X, Y, Z ... and E acceleration for printing moves #define DEFAULT_RETRACT_ACCELERATION 3000 // E acceleration for retracts -#define DEFAULT_TRAVEL_ACCELERATION 3000 // X, Y, Z acceleration for travel (non printing) moves +#define DEFAULT_TRAVEL_ACCELERATION 3000 // X, Y, Z ... acceleration for travel (non printing) moves /** * Default Jerk limits (mm/s) @@ -1088,6 +1246,17 @@ */ //#define BLTOUCH +/** + * MagLev V4 probe by MDD + * + * This probe is deployed and activated by powering a built-in electromagnet. + */ +//#define MAGLEV4 +#if ENABLED(MAGLEV4) + //#define MAGLEV_TRIGGER_PIN 11 // Set to the connected digital output + #define MAGLEV_TRIGGER_DELAY 15 // Changing this risks overheating the coil +#endif + /** * Touch-MI Probe by hotends.fr * @@ -1134,9 +1303,37 @@ */ //#define SENSORLESS_PROBING -// -// For Z_PROBE_ALLEN_KEY see the Delta example configurations. -// +/** + * Allen key retractable z-probe as seen on many Kossel delta printers - https://reprap.org/wiki/Kossel#Automatic_bed_leveling_probe + * Deploys by touching z-axis belt. Retracts by pushing the probe down. + */ +//#define Z_PROBE_ALLEN_KEY +#if ENABLED(Z_PROBE_ALLEN_KEY) + // 2 or 3 sets of coordinates for deploying and retracting the spring loaded touch probe on G29, + // if servo actuated touch probe is not defined. Uncomment as appropriate for your printer/probe. + + #define Z_PROBE_ALLEN_KEY_DEPLOY_1 { 30.0, DELTA_PRINTABLE_RADIUS, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_1_FEEDRATE XY_PROBE_FEEDRATE + + #define Z_PROBE_ALLEN_KEY_DEPLOY_2 { 0.0, DELTA_PRINTABLE_RADIUS, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_2_FEEDRATE (XY_PROBE_FEEDRATE)/10 + + #define Z_PROBE_ALLEN_KEY_DEPLOY_3 { 0.0, (DELTA_PRINTABLE_RADIUS) * 0.75, 100.0 } + #define Z_PROBE_ALLEN_KEY_DEPLOY_3_FEEDRATE XY_PROBE_FEEDRATE + + #define Z_PROBE_ALLEN_KEY_STOW_1 { -64.0, 56.0, 23.0 } // Move the probe into position + #define Z_PROBE_ALLEN_KEY_STOW_1_FEEDRATE XY_PROBE_FEEDRATE + + #define Z_PROBE_ALLEN_KEY_STOW_2 { -64.0, 56.0, 3.0 } // Push it down + #define Z_PROBE_ALLEN_KEY_STOW_2_FEEDRATE (XY_PROBE_FEEDRATE)/10 + + #define Z_PROBE_ALLEN_KEY_STOW_3 { -64.0, 56.0, 50.0 } // Move it up to clear + #define Z_PROBE_ALLEN_KEY_STOW_3_FEEDRATE XY_PROBE_FEEDRATE + + #define Z_PROBE_ALLEN_KEY_STOW_4 { 0.0, 0.0, 50.0 } + #define Z_PROBE_ALLEN_KEY_STOW_4_FEEDRATE XY_PROBE_FEEDRATE + +#endif // Z_PROBE_ALLEN_KEY /** * Nozzle-to-Probe offsets { X, Y, Z } @@ -1220,6 +1417,15 @@ #endif #endif +/** + * Probe Enable / Disable + * The probe only provides a triggered signal when enabled. + */ +//#define PROBE_ENABLE_DISABLE +#if ENABLED(PROBE_ENABLE_DISABLE) + //#define PROBE_ENABLE_PIN -1 // Override the default pin here +#endif + /** * Multiple Probing * @@ -1675,18 +1881,18 @@ #endif // Add a menu item to move between bed corners for manual bed adjustment -//#define LEVEL_BED_CORNERS - -#if ENABLED(LEVEL_BED_CORNERS) - #define LEVEL_CORNERS_INSET_LFRB { 30, 30, 30, 30 } // (mm) Left, Front, Right, Back insets - #define LEVEL_CORNERS_HEIGHT 0.0 // (mm) Z height of nozzle at leveling points - #define LEVEL_CORNERS_Z_HOP 4.0 // (mm) Z height of nozzle between leveling points - //#define LEVEL_CENTER_TOO // Move to the center after the last corner - //#define LEVEL_CORNERS_USE_PROBE - #if ENABLED(LEVEL_CORNERS_USE_PROBE) - #define LEVEL_CORNERS_PROBE_TOLERANCE 0.1 - #define LEVEL_CORNERS_VERIFY_RAISED // After adjustment triggers the probe, re-probe to verify - //#define LEVEL_CORNERS_AUDIO_FEEDBACK +//#define LCD_BED_TRAMMING + +#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 leveling points + #define BED_TRAMMING_Z_HOP 4.0 // (mm) Z height of nozzle between leveling 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) + #define BED_TRAMMING_PROBE_TOLERANCE 0.1 // (mm) + #define BED_TRAMMING_VERIFY_RAISED // After adjustment triggers the probe, re-probe to verify + //#define BED_TRAMMING_AUDIO_FEEDBACK #endif /** @@ -1706,7 +1912,7 @@ * | 1 2 | | 1 4 | | 1 2 | | 2 | * LF --------- RF LF --------- RF LF --------- RF LF --------- RF */ - #define LEVEL_CORNERS_LEVELING_ORDER { LF, RF, RB, LB } + #define BED_TRAMMING_LEVELING_ORDER { LF, RF, RB, LB } #endif /** @@ -1822,11 +2028,12 @@ * M502 - Revert settings to "factory" defaults. (Follow with M500 to init the EEPROM.) */ //#define EEPROM_SETTINGS // Persistent storage with M500 and M501 -//#define DISABLE_M503 // Saves ~2700 bytes of PROGMEM. Disable for release! +//#define DISABLE_M503 // Saves ~2700 bytes of flash. Disable for release! #define EEPROM_CHITCHAT // Give feedback on EEPROM commands. Disable to save PROGMEM. #define EEPROM_BOOT_SILENT // Keep M503 quiet and only give errors during first load #if ENABLED(EEPROM_SETTINGS) //#define EEPROM_AUTO_INIT // Init EEPROM automatically on any errors. + //#define EEPROM_INIT_NOW // Init EEPROM on first boot after a new build. #endif // @@ -1852,7 +2059,7 @@ // @section temperature // -// Preheat Constants - Up to 5 are supported without changes +// Preheat Constants - Up to 6 are supported without changes // #define PREHEAT_1_LABEL "PLA" #define PREHEAT_1_TEMP_HOTEND 180 @@ -1882,8 +2089,7 @@ #if ENABLED(NOZZLE_PARK_FEATURE) // Specify a park position as { X, Y, Z_raise } #define NOZZLE_PARK_POINT { (X_MIN_POS + 10), (Y_MAX_POS - 10), 20 } - //#define NOZZLE_PARK_X_ONLY // X move only is required to park - //#define NOZZLE_PARK_Y_ONLY // Y move only is required to park + #define NOZZLE_PARK_MOVE 0 // Park motion: 0 = XY Move, 1 = X Only, 2 = Y Only, 3 = X before Y, 4 = Y before X #define NOZZLE_PARK_Z_RAISE_MIN 2 // (mm) Always raise Z by at least this distance #define NOZZLE_PARK_XY_FEEDRATE 100 // (mm/s) X and Y axes feedrate (also used for delta Z axis) #define NOZZLE_PARK_Z_FEEDRATE 5 // (mm/s) Z axis feedrate (not used for delta printers) @@ -2375,6 +2581,11 @@ //#define VIKI2 //#define miniVIKI +// +// Alfawise Ex8 printer LCD marked as WYH L12864 COG +// +//#define WYH_L12864 + // // MakerLab Mini Panel with graphic // controller and SD support - https://reprap.org/wiki/Mini_panel @@ -2444,6 +2655,11 @@ //#define FYSETC_MINI_12864_2_1 // Type A/B. NeoPixel RGB Backlight //#define FYSETC_GENERIC_12864_1_1 // Larger display with basic ON/OFF backlight. +// +// BigTreeTech Mini 12864 V1.0 is an alias for FYSETC_MINI_12864_2_1. Type A/B. NeoPixel RGB Backlight. +// +//#define BTT_MINI_12864_V1 + // // Factory display for Creality CR-10 // https://www.aliexpress.com/item/32833148327.html @@ -2543,38 +2759,38 @@ //========================== Extensible UI Displays =========================== //============================================================================= -// -// DGUS Touch Display with DWIN OS. (Choose one.) -// ORIGIN : https://www.aliexpress.com/item/32993409517.html -// FYSETC : https://www.aliexpress.com/item/32961471929.html -// MKS : https://www.aliexpress.com/item/1005002008179262.html -// -// Flash display with DGUS Displays for Marlin: -// - Format the SD card to FAT32 with an allocation size of 4kb. -// - Download files as specified for your type of display. -// - Plug the microSD card into the back of the display. -// - Boot the display and wait for the update to complete. -// -// ORIGIN (Marlin DWIN_SET) -// - Download https://github.com/coldtobi/Marlin_DGUS_Resources -// - Copy the downloaded DWIN_SET folder to the SD card. -// -// FYSETC (Supplier default) -// - Download https://github.com/FYSETC/FYSTLCD-2.0 -// - Copy the downloaded SCREEN folder to the SD card. -// -// HIPRECY (Supplier default) -// - Download https://github.com/HiPrecy/Touch-Lcd-LEO -// - Copy the downloaded DWIN_SET folder to the SD card. -// -// MKS (MKS-H43) (Supplier default) -// - Download https://github.com/makerbase-mks/MKS-H43 -// - Copy the downloaded DWIN_SET folder to the SD card. -// -// RELOADED (T5UID1) -// - Download https://github.com/Desuuuu/DGUS-reloaded/releases -// - Copy the downloaded DWIN_SET folder to the SD card. -// +/** + * DGUS Touch Display with DWIN OS. (Choose one.) + * ORIGIN : https://www.aliexpress.com/item/32993409517.html + * FYSETC : https://www.aliexpress.com/item/32961471929.html + * MKS : https://www.aliexpress.com/item/1005002008179262.html + * + * Flash display with DGUS Displays for Marlin: + * - Format the SD card to FAT32 with an allocation size of 4kb. + * - Download files as specified for your type of display. + * - Plug the microSD card into the back of the display. + * - Boot the display and wait for the update to complete. + * + * ORIGIN (Marlin DWIN_SET) + * - Download https://github.com/coldtobi/Marlin_DGUS_Resources + * - Copy the downloaded DWIN_SET folder to the SD card. + * + * FYSETC (Supplier default) + * - Download https://github.com/FYSETC/FYSTLCD-2.0 + * - Copy the downloaded SCREEN folder to the SD card. + * + * HIPRECY (Supplier default) + * - Download https://github.com/HiPrecy/Touch-Lcd-LEO + * - Copy the downloaded DWIN_SET folder to the SD card. + * + * MKS (MKS-H43) (Supplier default) + * - Download https://github.com/makerbase-mks/MKS-H43 + * - Copy the downloaded DWIN_SET folder to the SD card. + * + * RELOADED (T5UID1) + * - Download https://github.com/Desuuuu/DGUS-reloaded/releases + * - Copy the downloaded DWIN_SET folder to the SD card. + */ //#define DGUS_LCD_UI_ORIGIN //#define DGUS_LCD_UI_FYSETC //#define DGUS_LCD_UI_HIPRECY @@ -2588,9 +2804,6 @@ // Touch-screen LCD for Malyan M200/M300 printers // //#define MALYAN_LCD -#if ENABLED(MALYAN_LCD) - #define LCD_SERIAL_PORT 1 // Default is 1 for Malyan M200 -#endif // // Touch UI for FTDI EVE (FT800/FT810) displays @@ -2604,7 +2817,6 @@ //#define ANYCUBIC_LCD_I3MEGA //#define ANYCUBIC_LCD_CHIRON #if EITHER(ANYCUBIC_LCD_I3MEGA, ANYCUBIC_LCD_CHIRON) - #define LCD_SERIAL_PORT 3 // Default is 3 for Anycubic //#define ANYCUBIC_LCD_DEBUG #endif @@ -2612,9 +2824,6 @@ // 320x240 Nextion 2.8" serial TFT Resistive Touch Screen NX3224T028 // //#define NEXTION_TFT -#if ENABLED(NEXTION_TFT) - #define LCD_SERIAL_PORT 1 // Default is 1 for Nextion -#endif // // Third-party or vendor-customized controller interfaces. @@ -2636,32 +2845,32 @@ */ // -// 480x320, 3.5", SPI Display From MKS -// Normally used in MKS Robin Nano V2 +// 480x320, 3.5", SPI Display with Rotary Encoder from MKS +// Usually paired with MKS Robin Nano V2 & V3 // //#define MKS_TS35_V2_0 // // 320x240, 2.4", FSMC Display From MKS -// Normally used in MKS Robin Nano V1.2 +// Usually paired with MKS Robin Nano V1.2 // //#define MKS_ROBIN_TFT24 // // 320x240, 2.8", FSMC Display From MKS -// Normally used in MKS Robin Nano V1.2 +// Usually paired with MKS Robin Nano V1.2 // //#define MKS_ROBIN_TFT28 // // 320x240, 3.2", FSMC Display From MKS -// Normally used in MKS Robin Nano V1.2 +// Usually paired with MKS Robin Nano V1.2 // //#define MKS_ROBIN_TFT32 // // 480x320, 3.5", FSMC Display From MKS -// Normally used in MKS Robin Nano V1.2 +// Usually paired with MKS Robin Nano V1.2 // //#define MKS_ROBIN_TFT35 @@ -2672,7 +2881,7 @@ // // 320x240, 3.2", FSMC Display From MKS -// Normally used in MKS Robin +// Usually paired with MKS Robin // //#define MKS_ROBIN_TFT_V1_1R @@ -2702,10 +2911,15 @@ //#define ANET_ET5_TFT35 // -// 1024x600, 7", RGB Stock Display from BIQU-BX +// 1024x600, 7", RGB Stock Display with Rotary Encoder from BIQU-BX // //#define BIQU_BX_TFT70 +// +// 480x320, 3.5", SPI Stock Display with Rotary Encoder from BIQU B1 SE Series +// +//#define BTT_TFT35_SPI_V1_0 + // // Generic TFT with detailed options // @@ -2760,23 +2974,11 @@ // // Ender-3 v2 OEM display. A DWIN display with Rotary Encoder. // -//#define DWIN_CREALITY_LCD - -// -// Ender-3 v2 OEM display, enhanced. -// -//#define DWIN_CREALITY_LCD_ENHANCED - -// -// Ender-3 v2 OEM display with enhancements by Jacob Myers -// -//#define DWIN_CREALITY_LCD_JYERSUI - -// -// MarlinUI for Creality's DWIN display (and others) -// -//#define DWIN_MARLINUI_PORTRAIT -//#define DWIN_MARLINUI_LANDSCAPE +//#define DWIN_CREALITY_LCD // Creality UI +//#define DWIN_LCD_PROUI // Pro UI by MRiscoC +//#define DWIN_CREALITY_LCD_JYERSUI // Jyers UI by Jacob Myers +//#define DWIN_MARLINUI_PORTRAIT // MarlinUI (portrait orientation) +//#define DWIN_MARLINUI_LANDSCAPE // MarlinUI (landscape orientation) // // Touch Screen Settings @@ -2786,7 +2988,7 @@ #define BUTTON_DELAY_EDIT 50 // (ms) Button repeat delay for edit screens #define BUTTON_DELAY_MENU 250 // (ms) Button repeat delay for menus - //#define TOUCH_IDLE_SLEEP 300 // (secs) Turn off the TFT backlight if set (5mn) + //#define TOUCH_IDLE_SLEEP 300 // (s) Turn off the TFT backlight if set (5mn) #define TOUCH_SCREEN_CALIBRATION @@ -2812,6 +3014,11 @@ //#define REPRAPWORLD_KEYPAD //#define REPRAPWORLD_KEYPAD_MOVE_STEP 10.0 // (mm) Distance to move per key-press +// +// EasyThreeD ET-4000+ with button input and status LED +// +//#define EASYTHREED_UI + //============================================================================= //=============================== Extra Features ============================== //============================================================================= @@ -2822,9 +3029,6 @@ // :[1,2,3,4,5,6,7,8] //#define NUM_M106_FANS 1 -// Increase the FAN PWM frequency. Removes the PWM noise but increases heating in the FET/Arduino -//#define FAST_PWM_FAN - // 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. @@ -2894,30 +3098,31 @@ // Support for Adafruit NeoPixel LED driver //#define NEOPIXEL_LED #if ENABLED(NEOPIXEL_LED) - #define NEOPIXEL_TYPE NEO_GRBW // NEO_GRBW / NEO_GRB - four/three channel driver type (defined in Adafruit_NeoPixel.h) - //#define NEOPIXEL_PIN 4 // LED driving pin - //#define NEOPIXEL2_TYPE NEOPIXEL_TYPE - //#define NEOPIXEL2_PIN 5 - #define NEOPIXEL_PIXELS 30 // Number of LEDs in the strip. (Longest strip when NEOPIXEL2_SEPARATE is disabled.) - #define NEOPIXEL_IS_SEQUENTIAL // Sequential display for temperature change - LED by LED. Disable to change all LEDs at once. - #define NEOPIXEL_BRIGHTNESS 127 // Initial brightness (0-255) - //#define NEOPIXEL_STARTUP_TEST // Cycle through colors at startup + #define NEOPIXEL_TYPE NEO_GRBW // NEO_GRBW, NEO_RGBW, NEO_GRB, NEO_RBG, etc. + // See https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.h + //#define NEOPIXEL_PIN 4 // LED driving pin + //#define NEOPIXEL2_TYPE NEOPIXEL_TYPE + //#define NEOPIXEL2_PIN 5 + #define NEOPIXEL_PIXELS 30 // Number of LEDs in the strip. (Longest strip when NEOPIXEL2_SEPARATE is disabled.) + #define NEOPIXEL_IS_SEQUENTIAL // Sequential display for temperature change - LED by LED. Disable to change all LEDs at once. + #define NEOPIXEL_BRIGHTNESS 127 // Initial brightness (0-255) + //#define NEOPIXEL_STARTUP_TEST // Cycle through colors at startup // Support for second Adafruit NeoPixel LED driver controlled with M150 S1 ... //#define NEOPIXEL2_SEPARATE #if ENABLED(NEOPIXEL2_SEPARATE) - #define NEOPIXEL2_PIXELS 15 // Number of LEDs in the second strip - #define NEOPIXEL2_BRIGHTNESS 127 // Initial brightness (0-255) - #define NEOPIXEL2_STARTUP_TEST // Cycle through colors at startup + #define NEOPIXEL2_PIXELS 15 // Number of LEDs in the second strip + #define NEOPIXEL2_BRIGHTNESS 127 // Initial brightness (0-255) + #define NEOPIXEL2_STARTUP_TEST // Cycle through colors at startup #else - //#define NEOPIXEL2_INSERIES // Default behavior is NeoPixel 2 in parallel + //#define NEOPIXEL2_INSERIES // Default behavior is NeoPixel 2 in parallel #endif // Use some of the NeoPixel LEDs for static (background) lighting - //#define NEOPIXEL_BKGD_INDEX_FIRST 0 // Index of the first background LED - //#define NEOPIXEL_BKGD_INDEX_LAST 5 // Index of the last background LED + //#define NEOPIXEL_BKGD_INDEX_FIRST 0 // Index of the first background LED + //#define NEOPIXEL_BKGD_INDEX_LAST 5 // Index of the last background LED //#define NEOPIXEL_BKGD_COLOR { 255, 255, 255, 0 } // R, G, B, W - //#define NEOPIXEL_BKGD_ALWAYS_ON // Keep the backlight on when other NeoPixels are off + //#define NEOPIXEL_BKGD_ALWAYS_ON // Keep the backlight on when other NeoPixels are off #endif /** diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 86a7372ebdaaa..a6556b7108ffe 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -30,7 +30,7 @@ * * Basic settings can be found in Configuration.h */ -#define CONFIGURATION_ADV_H_VERSION 02000902 +#define CONFIGURATION_ADV_H_VERSION 02000905 //=========================================================================== //============================= Thermal Settings ============================ @@ -54,99 +54,105 @@ // Custom Thermistor 1000 parameters // #if TEMP_SENSOR_0 == 1000 - #define HOTEND0_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND0_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND0_BETA 3950 // Beta value + #define HOTEND0_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND0_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND0_BETA 3950 // Beta value #endif #if TEMP_SENSOR_1 == 1000 - #define HOTEND1_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND1_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND1_BETA 3950 // Beta value + #define HOTEND1_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND1_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND1_BETA 3950 // Beta value #endif #if TEMP_SENSOR_2 == 1000 - #define HOTEND2_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND2_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND2_BETA 3950 // Beta value + #define HOTEND2_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND2_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND2_BETA 3950 // Beta value #endif #if TEMP_SENSOR_3 == 1000 - #define HOTEND3_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND3_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND3_BETA 3950 // Beta value + #define HOTEND3_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND3_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND3_BETA 3950 // Beta value #endif #if TEMP_SENSOR_4 == 1000 - #define HOTEND4_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND4_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND4_BETA 3950 // Beta value + #define HOTEND4_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND4_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND4_BETA 3950 // Beta value #endif #if TEMP_SENSOR_5 == 1000 - #define HOTEND5_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND5_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND5_BETA 3950 // Beta value + #define HOTEND5_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND5_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND5_BETA 3950 // Beta value #endif #if TEMP_SENSOR_6 == 1000 - #define HOTEND6_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND6_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND6_BETA 3950 // Beta value + #define HOTEND6_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND6_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND6_BETA 3950 // Beta value #endif #if TEMP_SENSOR_7 == 1000 - #define HOTEND7_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define HOTEND7_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define HOTEND7_BETA 3950 // Beta value + #define HOTEND7_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define HOTEND7_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define HOTEND7_BETA 3950 // Beta value #endif #if TEMP_SENSOR_BED == 1000 - #define BED_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define BED_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define BED_BETA 3950 // Beta value + #define BED_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define BED_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define BED_BETA 3950 // Beta value #endif #if TEMP_SENSOR_CHAMBER == 1000 - #define CHAMBER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define CHAMBER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define CHAMBER_BETA 3950 // Beta value + #define CHAMBER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define CHAMBER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define CHAMBER_BETA 3950 // Beta value #endif #if TEMP_SENSOR_COOLER == 1000 - #define COOLER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define COOLER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define COOLER_BETA 3950 // Beta value + #define COOLER_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define COOLER_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define COOLER_BETA 3950 // Beta value #endif #if TEMP_SENSOR_PROBE == 1000 - #define PROBE_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define PROBE_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define PROBE_BETA 3950 // Beta value + #define PROBE_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define PROBE_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define PROBE_BETA 3950 // Beta value #endif #if TEMP_SENSOR_BOARD == 1000 - #define BOARD_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define BOARD_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define BOARD_BETA 3950 // Beta value + #define BOARD_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define BOARD_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define BOARD_BETA 3950 // Beta value #endif #if TEMP_SENSOR_REDUNDANT == 1000 - #define REDUNDANT_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor - #define REDUNDANT_RESISTANCE_25C_OHMS 100000 // Resistance at 25C - #define REDUNDANT_BETA 3950 // Beta value + #define REDUNDANT_PULLUP_RESISTOR_OHMS 4700 // Pullup resistor + #define REDUNDANT_RESISTANCE_25C_OHMS 100000 // Resistance at 25C + #define REDUNDANT_BETA 3950 // Beta value #endif /** - * Configuration options for MAX Thermocouples (-2, -3, -5). - * FORCE_HW_SPI: Ignore SCK/MOSI/MISO pins and just use the CS pin & default SPI bus. - * MAX31865_WIRES: Set the number of wires for the probe connected to a MAX31865 board, 2-4. Default: 2 - * MAX31865_50HZ: Enable 50Hz filter instead of the default 60Hz. + * Thermocouple Options — for MAX6675 (-2), MAX31855 (-3), and MAX31865 (-5). */ -//#define TEMP_SENSOR_FORCE_HW_SPI -//#define MAX31865_SENSOR_WIRES_0 2 +//#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_50HZ_FILTER + +//#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. + +//#define MAX31865_USE_AUTO_MODE // Read faster and more often than 1-shot; bias voltage always on; slight effect on RTD temperature. +//#define MAX31865_MIN_SAMPLING_TIME_MSEC 100 // (ms) 1-shot: minimum read interval. Reduces bias voltage effects by leaving sensor unpowered for longer intervals. +//#define MAX31865_IGNORE_INITIAL_FAULTY_READS 10 // Ignore some read faults (keeping the temperature reading) to work around a possible issue (#23439). + +//#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 /** * Hephestos 2 24V heated bed upgrade kit. @@ -186,7 +192,8 @@ //#define CHAMBER_FAN // Enable a fan on the chamber #if ENABLED(CHAMBER_FAN) - #define CHAMBER_FAN_MODE 2 // Fan control mode: 0=Static; 1=Linear increase when temp is higher than target; 2=V-shaped curve; 3=similar to 1 but fan is always on. + //#define CHAMBER_FAN_INDEX 2 // Index of a fan to repurpose as the chamber fan. (Default: first unused fan) + #define CHAMBER_FAN_MODE 2 // Fan control mode: 0=Static; 1=Linear increase when temp is higher than target; 2=V-shaped curve; 3=similar to 1 but fan is always on. #if CHAMBER_FAN_MODE == 0 #define CHAMBER_FAN_BASE 255 // Chamber fan PWM (0-255) #elif CHAMBER_FAN_MODE == 1 @@ -242,20 +249,6 @@ #endif #endif -// -// Laser Coolant Flow Meter -// -//#define LASER_COOLANT_FLOW_METER -#if ENABLED(LASER_COOLANT_FLOW_METER) - #define FLOWMETER_PIN 20 // Requires an external interrupt-enabled pin (e.g., RAMPS 2,3,18,19,20,21) - #define FLOWMETER_PPL 5880 // (pulses/liter) Flow meter pulses-per-liter on the input pin - #define FLOWMETER_INTERVAL 1000 // (ms) Flow rate calculation interval in milliseconds - #define FLOWMETER_SAFETY // Prevent running the laser without the minimum flow rate set below - #if ENABLED(FLOWMETER_SAFETY) - #define FLOWMETER_MIN_LITERS_PER_MINUTE 1.5 // (liters/min) Minimum flow required when enabled - #endif -#endif - /** * Thermal Protection provides additional protection to your printer from damage * and fire. Marlin always includes safe min and max temperature ranges which @@ -329,14 +322,22 @@ * Thermal Protection parameters for the laser cooler. */ #if ENABLED(THERMAL_PROTECTION_COOLER) - #define THERMAL_PROTECTION_COOLER_PERIOD 10 // Seconds - #define THERMAL_PROTECTION_COOLER_HYSTERESIS 3 // Degrees Celsius + #define THERMAL_PROTECTION_COOLER_PERIOD 10 // Seconds + #define THERMAL_PROTECTION_COOLER_HYSTERESIS 3 // Degrees Celsius /** * Laser cooling watch settings (M143/M193). */ - #define WATCH_COOLER_TEMP_PERIOD 60 // Seconds - #define WATCH_COOLER_TEMP_INCREASE 3 // Degrees Celsius + #define WATCH_COOLER_TEMP_PERIOD 60 // Seconds + #define WATCH_COOLER_TEMP_INCREASE 3 // Degrees Celsius +#endif + +#if ANY(THERMAL_PROTECTION_HOTENDS, THERMAL_PROTECTION_BED, THERMAL_PROTECTION_CHAMBER, THERMAL_PROTECTION_COOLER) + /** + * Thermal Protection Variance Monitor - EXPERIMENTAL. + * Kill the machine on a stuck temperature sensor. Disable if you get false positives. + */ + //#define THERMAL_PROTECTION_VARIANCE_MONITOR // Detect a sensor malfunction preventing temperature updates #endif #if ENABLED(PIDTEMP) @@ -414,7 +415,7 @@ */ #define AUTOTEMP #if ENABLED(AUTOTEMP) - #define AUTOTEMP_OLDWEIGHT 0.98 + #define AUTOTEMP_OLDWEIGHT 0.98 // Factor used to weight previous readings (0.0 < value < 1.0) // Turn on AUTOTEMP on M104/M109 by default using proportions set here //#define AUTOTEMP_PROPORTIONAL #if ENABLED(AUTOTEMP_PROPORTIONAL) @@ -539,30 +540,41 @@ //#define FAN_MAX_PWM 128 /** - * FAST PWM FAN Settings + * Fan Fast PWM * - * Use to change the FAST FAN PWM frequency (if enabled in Configuration.h) - * Combinations of PWM Modes, prescale values and TOP resolutions are used internally to produce a - * frequency as close as possible to the desired frequency. + * Combinations of PWM Modes, prescale values and TOP resolutions are used internally + * to produce a frequency as close as possible to the desired frequency. * - * FAST_PWM_FAN_FREQUENCY [undefined by default] + * FAST_PWM_FAN_FREQUENCY * Set this to your desired frequency. - * If left undefined this defaults to F = F_CPU/(2*255*1) - * i.e., F = 31.4kHz on 16MHz microcontrollers or F = 39.2kHz on 20MHz microcontrollers. - * These defaults are the same as with the old FAST_PWM_FAN implementation - no migration is required + * For AVR, if left undefined this defaults to F = F_CPU/(2*255*1) + * i.e., F = 31.4kHz on 16MHz microcontrollers or F = 39.2kHz on 20MHz microcontrollers. + * For non AVR, if left undefined this defaults to F = 1Khz. + * This F value is only to protect the hardware from an absence of configuration + * and not to complete it when users are not aware that the frequency must be specifically set to support the target board. + * * NOTE: Setting very low frequencies (< 10 Hz) may result in unexpected timer behavior. + * Setting very high frequencies can damage your hardware. * * USE_OCR2A_AS_TOP [undefined by default] * Boards that use TIMER2 for PWM have limitations resulting in only a few possible frequencies on TIMER2: - * 16MHz MCUs: [62.5KHz, 31.4KHz (default), 7.8KHz, 3.92KHz, 1.95KHz, 977Hz, 488Hz, 244Hz, 60Hz, 122Hz, 30Hz] - * 20MHz MCUs: [78.1KHz, 39.2KHz (default), 9.77KHz, 4.9KHz, 2.44KHz, 1.22KHz, 610Hz, 305Hz, 153Hz, 76Hz, 38Hz] + * 16MHz MCUs: [62.5kHz, 31.4kHz (default), 7.8kHz, 3.92kHz, 1.95kHz, 977Hz, 488Hz, 244Hz, 60Hz, 122Hz, 30Hz] + * 20MHz MCUs: [78.1kHz, 39.2kHz (default), 9.77kHz, 4.9kHz, 2.44kHz, 1.22kHz, 610Hz, 305Hz, 153Hz, 76Hz, 38Hz] * A greater range can be achieved by enabling USE_OCR2A_AS_TOP. But note that this option blocks the use of * PWM on pin OC2A. Only use this option if you don't need PWM on 0C2A. (Check your schematic.) * USE_OCR2A_AS_TOP sacrifices duty cycle control resolution to achieve this broader range of frequencies. */ +//#define FAST_PWM_FAN // Increase the fan PWM frequency. Removes the PWM noise but increases heating in the FET/Arduino #if ENABLED(FAST_PWM_FAN) - //#define FAST_PWM_FAN_FREQUENCY 31400 + //#define FAST_PWM_FAN_FREQUENCY 31400 // Define here to override the defaults below //#define USE_OCR2A_AS_TOP + #ifndef FAST_PWM_FAN_FREQUENCY + #ifdef __AVR__ + #define FAST_PWM_FAN_FREQUENCY ((F_CPU) / (2 * 255 * 1)) + #else + #define FAST_PWM_FAN_FREQUENCY 1000U + #endif + #endif #endif /** @@ -594,7 +606,6 @@ #define E7_AUTO_FAN_PIN -1 #define CHAMBER_AUTO_FAN_PIN -1 #define COOLER_AUTO_FAN_PIN -1 -#define COOLER_FAN_PIN -1 #define EXTRUDER_AUTO_FAN_TEMPERATURE 50 #define EXTRUDER_AUTO_FAN_SPEED 255 // 255 == full speed @@ -603,6 +614,40 @@ #define COOLER_AUTO_FAN_TEMPERATURE 18 #define COOLER_AUTO_FAN_SPEED 255 +/** + * Hotend Cooling Fans tachometers + * + * Define one or more tachometer pins to enable fan speed + * monitoring, and reporting of fan speeds with M123. + * + * NOTE: Only works with fans up to 7000 RPM. + */ +//#define FOURWIRES_FANS // Needed with AUTO_FAN when 4-wire PWM fans are installed +//#define E0_FAN_TACHO_PIN -1 +//#define E0_FAN_TACHO_PULLUP +//#define E0_FAN_TACHO_PULLDOWN +//#define E1_FAN_TACHO_PIN -1 +//#define E1_FAN_TACHO_PULLUP +//#define E1_FAN_TACHO_PULLDOWN +//#define E2_FAN_TACHO_PIN -1 +//#define E2_FAN_TACHO_PULLUP +//#define E2_FAN_TACHO_PULLDOWN +//#define E3_FAN_TACHO_PIN -1 +//#define E3_FAN_TACHO_PULLUP +//#define E3_FAN_TACHO_PULLDOWN +//#define E4_FAN_TACHO_PIN -1 +//#define E4_FAN_TACHO_PULLUP +//#define E4_FAN_TACHO_PULLDOWN +//#define E5_FAN_TACHO_PIN -1 +//#define E5_FAN_TACHO_PULLUP +//#define E5_FAN_TACHO_PULLDOWN +//#define E6_FAN_TACHO_PIN -1 +//#define E6_FAN_TACHO_PULLUP +//#define E6_FAN_TACHO_PULLDOWN +//#define E7_FAN_TACHO_PIN -1 +//#define E7_FAN_TACHO_PULLUP +//#define E7_FAN_TACHO_PULLDOWN + /** * Part-Cooling Fan Multiplexer * @@ -654,73 +699,6 @@ //#define CLOSED_LOOP_MOVE_COMPLETE_PIN -1 #endif -/** - * Dual Steppers / Dual Endstops - * - * This section will allow you to use extra E drivers to drive a second motor for X, Y, or Z axes. - * - * For example, set X_DUAL_STEPPER_DRIVERS setting to use a second motor. If the motors need to - * spin in opposite directions set INVERT_X2_VS_X_DIR. If the second motor needs its own endstop - * set X_DUAL_ENDSTOPS. This can adjust for "racking." Use X2_USE_ENDSTOP to set the endstop plug - * that should be used for the second endstop. Extra endstops will appear in the output of 'M119'. - * - * Use X_DUAL_ENDSTOP_ADJUSTMENT to adjust for mechanical imperfection. After homing both motors - * this offset is applied to the X2 motor. To find the offset home the X axis, and measure the error - * in X2. Dual endstop offsets can be set at runtime with 'M666 X Y Z'. - */ - -//#define X_DUAL_STEPPER_DRIVERS -#if ENABLED(X_DUAL_STEPPER_DRIVERS) - //#define INVERT_X2_VS_X_DIR // Enable if X2 direction signal is opposite to X - //#define X_DUAL_ENDSTOPS - #if ENABLED(X_DUAL_ENDSTOPS) - #define X2_USE_ENDSTOP _XMAX_ - #define X2_ENDSTOP_ADJUSTMENT 0 - #endif -#endif - -//#define Y_DUAL_STEPPER_DRIVERS -#if ENABLED(Y_DUAL_STEPPER_DRIVERS) - //#define INVERT_Y2_VS_Y_DIR // Enable if Y2 direction signal is opposite to Y - //#define Y_DUAL_ENDSTOPS - #if ENABLED(Y_DUAL_ENDSTOPS) - #define Y2_USE_ENDSTOP _YMAX_ - #define Y2_ENDSTOP_ADJUSTMENT 0 - #endif -#endif - -// -// For Z set the number of stepper drivers -// -#define NUM_Z_STEPPER_DRIVERS 1 // (1-4) Z options change based on how many - -#if NUM_Z_STEPPER_DRIVERS > 1 - // Enable if Z motor direction signals are the opposite of Z1 - //#define INVERT_Z2_VS_Z_DIR - //#define INVERT_Z3_VS_Z_DIR - //#define INVERT_Z4_VS_Z_DIR - - //#define Z_MULTI_ENDSTOPS - #if ENABLED(Z_MULTI_ENDSTOPS) - #define Z2_USE_ENDSTOP _XMAX_ - #define Z2_ENDSTOP_ADJUSTMENT 0 - #if NUM_Z_STEPPER_DRIVERS >= 3 - #define Z3_USE_ENDSTOP _YMAX_ - #define Z3_ENDSTOP_ADJUSTMENT 0 - #endif - #if NUM_Z_STEPPER_DRIVERS >= 4 - #define Z4_USE_ENDSTOP _ZMAX_ - #define Z4_ENDSTOP_ADJUSTMENT 0 - #endif - #endif -#endif - -// Drive the E axis with two synchronized steppers -//#define E_DUAL_STEPPER_DRIVERS -#if ENABLED(E_DUAL_STEPPER_DRIVERS) - //#define INVERT_E1_VS_E0_DIR // Enable if the E motors need opposite DIR states -#endif - /** * Dual X Carriage * @@ -751,18 +729,17 @@ */ //#define DUAL_X_CARRIAGE #if ENABLED(DUAL_X_CARRIAGE) - #define X1_MIN_POS X_MIN_POS // Set to X_MIN_POS - #define X1_MAX_POS X_BED_SIZE // Set a maximum so the first X-carriage can't hit the parked second X-carriage - #define X2_MIN_POS 80 // Set a minimum to ensure the second X-carriage can't hit the parked first X-carriage - #define X2_MAX_POS 353 // Set this to the distance between toolheads when both heads are homed - #define X2_HOME_DIR 1 // Set to 1. The second X-carriage always homes to the maximum endstop position - #define X2_HOME_POS X2_MAX_POS // Default X2 home position. Set to X2_MAX_POS. - // However: In this mode the HOTEND_OFFSET_X value for the second extruder provides a software - // override for X2_HOME_POS. This also allow recalibration of the distance between the two endstops - // without modifying the firmware (through the "M218 T1 X???" command). - // Remember: you should set the second extruder x-offset to 0 in your slicer. - - // This is the default power-up mode which can be later using M605. + #define X1_MIN_POS X_MIN_POS // Set to X_MIN_POS + #define X1_MAX_POS X_BED_SIZE // A max coordinate so the X1 carriage can't hit the parked X2 carriage + #define X2_MIN_POS 80 // A min coordinate so the X2 carriage can't hit the parked X1 carriage + #define X2_MAX_POS 353 // The max position of the X2 carriage, typically also the home position + #define X2_HOME_DIR 1 // Set to 1. The X2 carriage always homes to the max endstop position + #define X2_HOME_POS X2_MAX_POS // Default X2 home position. Set to X2_MAX_POS. + // NOTE: For Dual X Carriage use M218 T1 Xn to override the X2_HOME_POS. + // This allows recalibration of endstops distance without a rebuild. + // Remember to set the second extruder's X-offset to 0 in your slicer. + + // This is the default power-up mode which can be changed later using M605 S. #define DEFAULT_DUAL_X_CARRIAGE_MODE DXC_AUTO_PARK_MODE // Default x offset in duplication mode (typically set to half print bed width) @@ -772,6 +749,77 @@ //#define EVENT_GCODE_IDEX_AFTER_MODECHANGE "G28X" #endif +/** + * Multi-Stepper / Multi-Endstop + * + * When X2_DRIVER_TYPE is defined, this indicates that the X and X2 motors work in tandem. + * The following explanations for X also apply to Y and Z multi-stepper setups. + * Endstop offsets may be changed by 'M666 X Y Z' and stored to EEPROM. + * + * - Enable INVERT_X2_VS_X_DIR if the X2 motor requires an opposite DIR signal from X. + * + * - Enable X_DUAL_ENDSTOPS if the second motor has its own endstop, with adjustable offset. + * + * - Extra endstops are included in the output of 'M119'. + * + * - Set X_DUAL_ENDSTOP_ADJUSTMENT to the known error in the X2 endstop. + * Applied to the X2 motor on 'G28' / 'G28 X'. + * Get the offset by homing X and measuring the error. + * Also set with 'M666 X' and stored to EEPROM with 'M500'. + * + * - Use X2_USE_ENDSTOP to set the endstop plug by name. (_XMIN_, _XMAX_, _YMIN_, _YMAX_, _ZMIN_, _ZMAX_) + */ +#if HAS_X2_STEPPER && DISABLED(DUAL_X_CARRIAGE) + //#define INVERT_X2_VS_X_DIR // X2 direction signal is the opposite of X + //#define X_DUAL_ENDSTOPS // X2 has its own endstop + #if ENABLED(X_DUAL_ENDSTOPS) + #define X2_USE_ENDSTOP _XMAX_ // X2 endstop board plug. Don't forget to enable USE_*_PLUG. + #define X2_ENDSTOP_ADJUSTMENT 0 // X2 offset relative to X endstop + #endif +#endif + +#if HAS_DUAL_Y_STEPPERS + //#define INVERT_Y2_VS_Y_DIR // Y2 direction signal is the opposite of Y + //#define Y_DUAL_ENDSTOPS // Y2 has its own endstop + #if ENABLED(Y_DUAL_ENDSTOPS) + #define Y2_USE_ENDSTOP _YMAX_ // Y2 endstop board plug. Don't forget to enable USE_*_PLUG. + #define Y2_ENDSTOP_ADJUSTMENT 0 // Y2 offset relative to Y endstop + #endif +#endif + +// +// Multi-Z steppers +// +#ifdef Z2_DRIVER_TYPE + //#define INVERT_Z2_VS_Z_DIR // Z2 direction signal is the opposite of Z + + //#define Z_MULTI_ENDSTOPS // Other Z axes have their own endstops + #if ENABLED(Z_MULTI_ENDSTOPS) + #define Z2_USE_ENDSTOP _XMAX_ // Z2 endstop board plug. Don't forget to enable USE_*_PLUG. + #define Z2_ENDSTOP_ADJUSTMENT 0 // Z2 offset relative to Y endstop + #endif + #ifdef Z3_DRIVER_TYPE + //#define INVERT_Z3_VS_Z_DIR // Z3 direction signal is the opposite of Z + #if ENABLED(Z_MULTI_ENDSTOPS) + #define Z3_USE_ENDSTOP _YMAX_ // Z3 endstop board plug. Don't forget to enable USE_*_PLUG. + #define Z3_ENDSTOP_ADJUSTMENT 0 // Z3 offset relative to Y endstop + #endif + #endif + #ifdef Z4_DRIVER_TYPE + //#define INVERT_Z4_VS_Z_DIR // Z4 direction signal is the opposite of Z + #if ENABLED(Z_MULTI_ENDSTOPS) + #define Z4_USE_ENDSTOP _ZMAX_ // Z4 endstop board plug. Don't forget to enable USE_*_PLUG. + #define Z4_ENDSTOP_ADJUSTMENT 0 // Z4 offset relative to Y endstop + #endif + #endif +#endif + +// Drive the E axis with two synchronized steppers +//#define E_DUAL_STEPPER_DRIVERS +#if ENABLED(E_DUAL_STEPPER_DRIVERS) + //#define INVERT_E1_VS_E0_DIR // E direction signals are opposites +#endif + // Activate a solenoid on the active extruder with M380. Disable all with M381. // Define SOL0_PIN, SOL1_PIN, etc., for each extruder that has a solenoid. //#define EXT_SOLENOID @@ -853,12 +901,14 @@ //#define BLTOUCH_FORCE_MODE_SET /** - * Use "HIGH SPEED" mode for probing. + * Enable "HIGH SPEED" option for probing. * Danger: Disable if your probe sometimes fails. Only suitable for stable well-adjusted systems. * This feature was designed for Deltabots with very fast Z moves; however, higher speed Cartesians * might be able to use it. If the machine can't raise Z fast enough the BLTouch may go into ALARM. + * + * Set the default state here, change with 'M401 S' or UI, use M500 to save, M502 to reset. */ - //#define BLTOUCH_HS_MODE + //#define BLTOUCH_HS_MODE true // Safety: Enable voltage mode settings in the LCD menu. //#define BLTOUCH_LCD_VOLTAGE_MENU @@ -873,9 +923,12 @@ */ //#define Z_STEPPER_AUTO_ALIGN #if ENABLED(Z_STEPPER_AUTO_ALIGN) - // Define probe X and Y positions for Z1, Z2 [, Z3 [, Z4]] - // If not defined, probe limits will be used. - // Override with 'M422 S X Y' + /** + * Define probe X and Y positions for Z1, Z2 [, Z3 [, Z4]] + * These positions are machine-relative and do not shift with the M206 home offset! + * If not defined, probe limits will be used. + * Override with 'M422 S X Y'. + */ //#define Z_STEPPER_ALIGN_XY { { 10, 190 }, { 100, 10 }, { 190, 190 } } /** @@ -901,15 +954,17 @@ //#define Z_STEPPERS_ORIENTATION 0 #endif - // Provide Z stepper positions for more rapid convergence in bed alignment. - // Requires triple stepper drivers (i.e., set NUM_Z_STEPPER_DRIVERS to 3) - //#define Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - // Define Stepper XY positions for Z1, Z2, Z3 corresponding to - // the Z screw positions in the bed carriage. - // Define one position per Z stepper in stepper driver order. - #define Z_STEPPER_ALIGN_STEPPER_XY { { 210.7, 102.5 }, { 152.6, 220.0 }, { 94.5, 102.5 } } - #else + /** + * Z Stepper positions for more rapid convergence in bed alignment. + * Requires 3 or 4 Z steppers. + * + * Define Stepper XY positions for Z1, Z2, Z3... corresponding to the screw + * positions in the bed carriage, with one position per Z stepper in stepper + * driver order. + */ + //#define Z_STEPPER_ALIGN_STEPPER_XY { { 210.7, 102.5 }, { 152.6, 220.0 }, { 94.5, 102.5 } } + + #ifndef Z_STEPPER_ALIGN_STEPPER_XY // Amplification factor. Used to scale the correction step up or down in case // the stepper (spindle) position is farther out than the test point. #define Z_STEPPER_ALIGN_AMP 1.0 // Use a value > 1.0 NOTE: This may cause instability! @@ -1196,7 +1251,7 @@ // @section lcd -#if ANY(HAS_LCD_MENU, EXTENSIBLE_UI, HAS_DWIN_E3V2) +#if HAS_MANUAL_MOVE_MENU #define MANUAL_FEEDRATE { 50*60, 50*60, 4*60, 2*60 } // (mm/min) Feedrates for manual moves along X, Y, Z, E from panel #define FINE_MANUAL_MOVE 0.025 // (mm) Smallest manual move (< 0.1mm) applying to Z on most machines #if IS_ULTIPANEL @@ -1219,21 +1274,41 @@ #define FEEDRATE_CHANGE_BEEP_FREQUENCY 440 #endif -#if HAS_LCD_MENU +// +// LCD Backlight Timeout +// +//#define LCD_BACKLIGHT_TIMEOUT 30 // (s) Timeout before turning off the backlight + +#if HAS_BED_PROBE && EITHER(HAS_MARLINUI_MENU, HAS_TFT_LVGL_UI) + //#define PROBE_OFFSET_WIZARD // Add a Probe Z Offset calibration option to the LCD menu + #if ENABLED(PROBE_OFFSET_WIZARD) + /** + * Enable to init the Probe Z-Offset when starting the Wizard. + * Use a height slightly above the estimated nozzle-to-probe Z offset. + * For example, with an offset of -5, consider a starting height of -4. + */ + //#define PROBE_OFFSET_WIZARD_START_Z -4.0 + + // Set a convenient position to do the calibration (probing point and nozzle/bed-distance) + //#define PROBE_OFFSET_WIZARD_XY_POS { X_CENTER, Y_CENTER } + #endif +#endif + +#if HAS_MARLINUI_MENU - // Add Probe Z Offset calibration to the Z Probe Offsets menu #if HAS_BED_PROBE - //#define PROBE_OFFSET_WIZARD - #if ENABLED(PROBE_OFFSET_WIZARD) - // - // Enable to init the Probe Z-Offset when starting the Wizard. - // Use a height slightly above the estimated nozzle-to-probe Z offset. - // For example, with an offset of -5, consider a starting height of -4. - // - //#define PROBE_OFFSET_WIZARD_START_Z -4.0 - - // Set a convenient position to do the calibration (probing point and nozzle/bed-distance) - //#define PROBE_OFFSET_WIZARD_XY_POS { X_CENTER, Y_CENTER } + // Add calibration in the Probe Offsets menu to compensate for X-axis twist. + //#define X_AXIS_TWIST_COMPENSATION + #if ENABLED(X_AXIS_TWIST_COMPENSATION) + /** + * Enable to init the Probe Z-Offset when starting the Wizard. + * Use a height slightly above the estimated nozzle-to-probe Z offset. + * For example, with an offset of -5, consider a starting height of -4. + */ + #define XATC_START_Z 0.0 + #define XATC_MAX_POINTS 3 // Number of points to probe in the wizard + #define XATC_Y_POSITION Y_CENTER // (mm) Y position to probe + #define XATC_Z_OFFSETS { 0, 0, 0 } // Z offsets for X axis sample points #endif #endif @@ -1246,8 +1321,41 @@ // BACK menu items keep the highlight at the top //#define TURBO_BACK_MENU_ITEM - // Add a mute option to the LCD menu - //#define SOUND_MENU_ITEM + // Insert a menu for preheating at the top level to allow for quick access + //#define PREHEAT_SHORTCUT_MENU_ITEM + +#endif // HAS_MARLINUI_MENU + +#if ANY(HAS_DISPLAY, DWIN_LCD_PROUI, DWIN_CREALITY_LCD_JYERSUI) + //#define SOUND_MENU_ITEM // Add a mute option to the LCD menu + #define SOUND_ON_DEFAULT // Buzzer/speaker default enabled state +#endif + +#if EITHER(HAS_DISPLAY, DWIN_LCD_PROUI) + // The timeout to return to the status screen from sub-menus + //#define LCD_TIMEOUT_TO_STATUS 15000 // (ms) + + #if ENABLED(SHOW_BOOTSCREEN) + #define BOOTSCREEN_TIMEOUT 4000 // (ms) Total Duration to display the boot screen(s) + #if EITHER(HAS_MARLINUI_U8GLIB, TFT_COLOR_UI) + #define BOOT_MARLIN_LOGO_SMALL // Show a smaller Marlin logo on the Boot Screen (saving lots of flash) + #endif + #endif + + // Scroll a longer status message into view + //#define STATUS_MESSAGE_SCROLLING + + // Apply a timeout to low-priority status messages + //#define STATUS_MESSAGE_TIMEOUT_SEC 30 // (seconds) + + // On the Info Screen, display XY with one decimal place when possible + //#define LCD_DECIMAL_SMALL_XY + + // Add an 'M73' G-code to set the current percentage + //#define LCD_SET_PROGRESS_MANUALLY + + // Show the E position (filament used) during printing + //#define LCD_SHOW_E_TOTAL /** * LED Control Menu @@ -1275,38 +1383,11 @@ #endif #endif - // Insert a menu for preheating at the top level to allow for quick access - //#define PREHEAT_SHORTCUT_MENU_ITEM - -#endif // HAS_LCD_MENU - -#if HAS_DISPLAY - // The timeout (in ms) to return to the status screen from sub-menus - //#define LCD_TIMEOUT_TO_STATUS 15000 - - #if ENABLED(SHOW_BOOTSCREEN) - #define BOOTSCREEN_TIMEOUT 4000 // (ms) Total Duration to display the boot screen(s) - #if EITHER(HAS_MARLINUI_U8GLIB, TFT_COLOR_UI) - #define BOOT_MARLIN_LOGO_SMALL // Show a smaller Marlin logo on the Boot Screen (saving lots of flash) - #endif - #endif - - // Scroll a longer status message into view - //#define STATUS_MESSAGE_SCROLLING - - // On the Info Screen, display XY with one decimal place when possible - //#define LCD_DECIMAL_SMALL_XY - - // Add an 'M73' G-code to set the current percentage - //#define LCD_SET_PROGRESS_MANUALLY - - // Show the E position (filament used) during printing - //#define LCD_SHOW_E_TOTAL #endif // LCD Print Progress options #if EITHER(SDSUPPORT, LCD_SET_PROGRESS_MANUALLY) - #if ANY(HAS_MARLINUI_U8GLIB, EXTENSIBLE_UI, HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL, IS_DWIN_MARLINUI) + #if CAN_SHOW_REMAINING_TIME //#define SHOW_REMAINING_TIME // Display estimated time to completion #if ENABLED(SHOW_REMAINING_TIME) //#define USE_M73_REMAINING_TIME // Use remaining time from M73 command instead of estimation @@ -1448,33 +1529,23 @@ // LCD's font must contain the characters. Check your selected LCD language. //#define UTF_FILENAME_SUPPORT - // This allows hosts to request long names for files and folders with M33 - //#define LONG_FILENAME_HOST_SUPPORT + //#define LONG_FILENAME_HOST_SUPPORT // Get the long filename of a file/folder with 'M33 ' and list long filenames with 'M20 L' + //#define LONG_FILENAME_WRITE_SUPPORT // Create / delete files with long filenames via M28, M30, and Binary Transfer Protocol - // Enable this option to scroll long filenames in the SD card menu - //#define SCROLL_LONG_FILENAMES + //#define SCROLL_LONG_FILENAMES // Scroll long filenames in the SD card menu - // Leave the heaters on after Stop Print (not recommended!) - //#define SD_ABORT_NO_COOLDOWN + //#define SD_ABORT_NO_COOLDOWN // Leave the heaters on after Stop Print (not recommended!) /** - * This option allows you to abort SD printing when any endstop is triggered. - * This feature must be enabled with "M540 S1" or from the LCD menu. - * To have any effect, endstops must be enabled during SD printing. + * Abort SD printing when any endstop is triggered. + * This feature is enabled with 'M540 S1' or from the LCD menu. + * Endstops must be activated for this option to work. */ //#define SD_ABORT_ON_ENDSTOP_HIT - /** - * This option makes it easier to print the same SD Card file again. - * On print completion the LCD Menu will open with the file selected. - * You can just click to start the print, or navigate elsewhere. - */ - //#define SD_REPRINT_LAST_SELECTED_FILE + //#define SD_REPRINT_LAST_SELECTED_FILE // On print completion open the LCD Menu and select the same file - /** - * Auto-report SdCard status with M27 S - */ - //#define AUTO_REPORT_SD_STATUS + //#define AUTO_REPORT_SD_STATUS // Auto-report media status with 'M27 S' /** * Support for USB thumb drives using an Arduino USB Host Shield or @@ -1532,9 +1603,22 @@ #define SD_FIRMWARE_UPDATE_INACTIVE_VALUE 0xFF #endif + /** + * Enable this option if you have more than ~3K of unused flash space. + * Marlin will embed all settings in the firmware binary as compressed data. + * Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'. + * See docs/ConfigEmbedding.md for details on how to use 'mc-apply.py'. + */ + //#define CONFIGURATION_EMBEDDING + // Add an optimized binary file transfer mode, initiated with 'M28 B1' //#define BINARY_FILE_TRANSFER + #if ENABLED(BINARY_FILE_TRANSFER) + // Include extra facilities (e.g., 'M20 F') supporting firmware upload via BINARY_FILE_TRANSFER + //#define CUSTOM_FIRMWARE_UPLOAD + #endif + /** * Set this option to one of the following (or the board's defaults apply): * @@ -1549,7 +1633,10 @@ // Enable if SD detect is rendered useless (e.g., by using an SD extender) //#define NO_SD_DETECT - // Multiple volume support - EXPERIMENTAL. + /** + * Multiple volume support - EXPERIMENTAL. + * Adds 'M21 Pm' / 'M21 S' / 'M21 U' to mount SD Card / USB Drive. + */ //#define MULTI_VOLUME #if ENABLED(MULTI_VOLUME) #define VOLUME_SD_ONBOARD @@ -1583,14 +1670,25 @@ //#define XYZ_NO_FRAME #define XYZ_HOLLOW_FRAME - // A bigger font is available for edit items. Costs 3120 bytes of PROGMEM. + // A bigger font is available for edit items. Costs 3120 bytes of flash. // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese. //#define USE_BIG_EDIT_FONT - // A smaller font may be used on the Info Screen. Costs 2434 bytes of PROGMEM. + // A smaller font may be used on the Info Screen. Costs 2434 bytes of flash. // Western only. Not available for Cyrillic, Kana, Turkish, Greek, or Chinese. //#define USE_SMALL_INFOFONT + /** + * Graphical Display Sleep + * + * The U8G library provides sleep / wake functions for SH1106, SSD1306, + * SSD1309, and some other DOGM displays. + * Enable this option to save energy and prevent OLED pixel burn-in. + * Adds the menu item Configuration > LCD Timeout (m) to set a wait period + * from 0 (disabled) to 99 minutes. + */ + //#define DISPLAY_SLEEP_MINUTES 2 // (minutes) Timeout before turning off the screen + /** * ST7920-based LCDs can emulate a 16 x 4 character display using * the ST7920 character-generator for very fast screen updates. @@ -1603,7 +1701,7 @@ * Set STATUS_EXPIRE_SECONDS to zero to never clear the status. * This will prevent position updates from being displayed. */ - #if ENABLED(U8GLIB_ST7920) + #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 @@ -1632,7 +1730,7 @@ //#define STATUS_ALT_FAN_BITMAP // Use the alternative fan bitmap //#define STATUS_FAN_FRAMES 3 // :[0,1,2,3,4] Number of fan animation frames //#define STATUS_HEAT_PERCENT // Show heating in a progress bar - //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~‭3260 (or ~940) bytes of PROGMEM. + //#define BOOT_MARLIN_LOGO_ANIMATED // Animated Marlin logo. Costs ~3260 (or ~940) bytes of flash. // Frivolous Game Options //#define MARLIN_BRICKOUT @@ -1657,7 +1755,6 @@ // Additional options for DGUS / DWIN displays // #if HAS_DGUS_LCD - #define LCD_SERIAL_PORT 3 #define LCD_BAUDRATE 115200 #define DGUS_RX_BUFFER_SIZE 128 @@ -1913,6 +2010,7 @@ #define LIN_ADVANCE_K 0.22 // Unit: mm compression per 1mm/s extruder speed //#define LA_DEBUG // If enabled, this will generate debug information output over USB. //#define EXPERIMENTAL_SCURVE // Enable this option to permit S-Curve Acceleration + //#define ALLOW_LOW_EJERK // Allow a DEFAULT_EJERK value of <10. Recommended for direct drive hotends. #endif // @section leveling @@ -1988,59 +2086,69 @@ /** * Thermal Probe Compensation - * Probe measurements are adjusted to compensate for temperature distortion. - * Use G76 to calibrate this feature. Use M871 to set values manually. - * For a more detailed explanation of the process see G76_M871.cpp. + * + * Adjust probe measurements to compensate for distortion associated with the temperature + * of the probe, bed, and/or hotend. + * Use G76 to automatically calibrate this feature for probe and bed temperatures. + * (Extruder temperature/offset values must be calibrated manually.) + * Use M871 to set temperature/offset values manually. + * For more details see https://marlinfw.org/docs/features/probe_temp_compensation.html */ -#if HAS_BED_PROBE && TEMP_SENSOR_PROBE && TEMP_SENSOR_BED - // Enable thermal first layer compensation using bed and probe temperatures - #define PROBE_TEMP_COMPENSATION +//#define PTC_PROBE // Compensate based on probe temperature +//#define PTC_BED // Compensate based on bed temperature +//#define PTC_HOTEND // Compensate based on hotend temperature + +#if ANY(PTC_PROBE, PTC_BED, PTC_HOTEND) + /** + * If the probe is outside the defined range, use linear extrapolation with the closest + * point and the point with index PTC_LINEAR_EXTRAPOLATION. e.g., If set to 4 it will use the + * linear extrapolation between data[0] and data[4] for values below PTC_PROBE_START. + */ + //#define PTC_LINEAR_EXTRAPOLATION 4 + + #if ENABLED(PTC_PROBE) + // Probe temperature calibration generates a table of values starting at PTC_PROBE_START + // (e.g., 30), in steps of PTC_PROBE_RES (e.g., 5) with PTC_PROBE_COUNT (e.g., 10) samples. + #define PTC_PROBE_START 30 // (°C) + #define PTC_PROBE_RES 5 // (°C) + #define PTC_PROBE_COUNT 10 + #define PTC_PROBE_ZOFFS { 0 } // (µm) Z adjustments per sample + #endif + + #if ENABLED(PTC_BED) + // Bed temperature calibration builds a similar table. + #define PTC_BED_START 60 // (°C) + #define PTC_BED_RES 5 // (°C) + #define PTC_BED_COUNT 10 + #define PTC_BED_ZOFFS { 0 } // (µm) Z adjustments per sample + #endif + + #if ENABLED(PTC_HOTEND) + // Note: There is no automatic calibration for the hotend. Use M871. + #define PTC_HOTEND_START 180 // (°C) + #define PTC_HOTEND_RES 5 // (°C) + #define PTC_HOTEND_COUNT 20 + #define PTC_HOTEND_ZOFFS { 0 } // (µm) Z adjustments per sample + #endif - // Add additional compensation depending on hotend temperature - // Note: this values cannot be calibrated and have to be set manually - #if ENABLED(PROBE_TEMP_COMPENSATION) + // G76 options + #if BOTH(PTC_PROBE, PTC_BED) // Park position to wait for probe cooldown #define PTC_PARK_POS { 0, 0, 100 } // Probe position to probe and wait for probe to reach target temperature + //#define PTC_PROBE_POS { 12.0f, 7.3f } // Example: MK52 magnetic heatbed #define PTC_PROBE_POS { 90, 100 } - // Enable additional compensation using hotend temperature - // Note: this values cannot be calibrated automatically but have to be set manually - //#define USE_TEMP_EXT_COMPENSATION - - // Probe temperature calibration generates a table of values starting at PTC_SAMPLE_START - // (e.g., 30), in steps of PTC_SAMPLE_RES (e.g., 5) with PTC_SAMPLE_COUNT (e.g., 10) samples. - - //#define PTC_SAMPLE_START 30 // (°C) - //#define PTC_SAMPLE_RES 5 // (°C) - //#define PTC_SAMPLE_COUNT 10 - - // Bed temperature calibration builds a similar table. - - //#define BTC_SAMPLE_START 60 // (°C) - //#define BTC_SAMPLE_RES 5 // (°C) - //#define BTC_SAMPLE_COUNT 10 - - // The temperature the probe should be at while taking measurements during bed temperature - // calibration. - //#define BTC_PROBE_TEMP 30 // (°C) + // The temperature the probe should be at while taking measurements during + // bed temperature calibration. + #define PTC_PROBE_TEMP 30 // (°C) // Height above Z=0.0 to raise the nozzle. Lowering this can help the probe to heat faster. - // Note: the Z=0.0 offset is determined by the probe offset which can be set using M851. - //#define PTC_PROBE_HEATING_OFFSET 0.5 - - // Height to raise the Z-probe between heating and taking the next measurement. Some probes - // may fail to untrigger if they have been triggered for a long time, which can be solved by - // increasing the height the probe is raised to. - //#define PTC_PROBE_RAISE 15 - - // If the probe is outside of the defined range, use linear extrapolation using the closest - // point and the PTC_LINEAR_EXTRAPOLATION'th next point. E.g. if set to 4 it will use data[0] - // and data[4] to perform linear extrapolation for values below PTC_SAMPLE_START. - //#define PTC_LINEAR_EXTRAPOLATION 4 + // Note: The Z=0.0 offset is determined by the probe Z offset (e.g., as set with M851 Z). + #define PTC_PROBE_HEATING_OFFSET 0.5 #endif -#endif +#endif // PTC_PROBE || PTC_BED || PTC_HOTEND // @section extras @@ -2167,7 +2275,7 @@ #define BUFSIZE 4 // Transmission to Host Buffer Size -// To save 386 bytes of PROGMEM (and TX_BUFFER_SIZE+3 bytes of RAM) set to 0. +// 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. @@ -2249,6 +2357,15 @@ // For serial echo, the number of digits after the decimal point //#define SERIAL_FLOAT_PRECISION 4 +/** + * 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. + * + * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0. + * Otherwise, adjust according to your client and font. + */ +#define PROPORTIONAL_FONT_RATIO 1.0 + // @section extras /** @@ -2309,7 +2426,7 @@ /** * Extra G-code to run while executing tool-change commands. Can be used to use an additional - * stepper motor (I axis, see option LINEAR_AXES in Configuration.h) to drive the tool-changer. + * stepper motor (e.g., I axis in Configuration.h) to drive the tool-changer. */ //#define EVENT_GCODE_TOOLCHANGE_T0 "G28 A\nG1 A0" // Extra G-code to run while executing tool-change command T0 //#define EVENT_GCODE_TOOLCHANGE_T1 "G1 A10" // Extra G-code to run while executing tool-change command T1 @@ -2329,26 +2446,30 @@ #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) // Load / Unload #define TOOLCHANGE_FS_LENGTH 12 // (mm) Load / Unload length - #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH 0 // (mm) Extra length for better restart, fine tune by LCD/Gcode) + #define TOOLCHANGE_FS_EXTRA_RESUME_LENGTH 0 // (mm) Extra length for better restart. Adjust with LCD or M217 B. #define TOOLCHANGE_FS_RETRACT_SPEED (50*60) // (mm/min) (Unloading) #define TOOLCHANGE_FS_UNRETRACT_SPEED (25*60) // (mm/min) (On SINGLENOZZLE or Bowden loading must be slowed down) // Longer prime to clean out a SINGLENOZZLE #define TOOLCHANGE_FS_EXTRA_PRIME 0 // (mm) Extra priming length #define TOOLCHANGE_FS_PRIME_SPEED (4.6*60) // (mm/min) Extra priming feedrate - #define TOOLCHANGE_FS_WIPE_RETRACT 0 // (mm/min) Retract before cooling for less stringing, better wipe, etc. + #define TOOLCHANGE_FS_WIPE_RETRACT 0 // (mm) Retract before cooling for less stringing, better wipe, etc. // Cool after prime to reduce stringing #define TOOLCHANGE_FS_FAN -1 // Fan index or -1 to skip #define TOOLCHANGE_FS_FAN_SPEED 255 // 0-255 #define TOOLCHANGE_FS_FAN_TIME 10 // (seconds) - // Swap uninitialized extruder with TOOLCHANGE_FS_PRIME_SPEED for all lengths (recover + prime) - // (May break filament if not retracted beforehand.) - //#define TOOLCHANGE_FS_INIT_BEFORE_SWAP + // Use TOOLCHANGE_FS_PRIME_SPEED feedrate the first time each extruder is primed + //#define TOOLCHANGE_FS_SLOW_FIRST_PRIME - // Prime on the first T0 (If other, TOOLCHANGE_FS_INIT_BEFORE_SWAP applied) - // Enable it (M217 V[0/1]) before printing, to avoid unwanted priming on host connect + /** + * Prime T0 the first time T0 is sent to the printer: + * [ Power-On -> T0 { Activate & Prime T0 } -> T1 { Retract T0, Activate & Prime T1 } ] + * If disabled, no priming on T0 until switching back to T0 from another extruder: + * [ Power-On -> T0 { T0 Activated } -> T1 { Activate & Prime T1 } -> T0 { Retract T1, Activate & Prime T0 } ] + * Enable with M217 V1 before printing to avoid unwanted priming on host connect. + */ //#define TOOLCHANGE_FS_PRIME_FIRST_USED /** @@ -2597,6 +2718,7 @@ #define X_RSENSE 0.11 #define X_CHAIN_POS -1 // -1..0: Not chained. 1: MCU MOSI connected. 2: Next in chain, ... //#define X_INTERPOLATE true // Enable to override 'INTERPOLATE' for the X axis + //#define X_HOLD_MULTIPLIER 0.5 // Enable to override 'HOLD_MULTIPLIER' for the X axis #endif #if AXIS_IS_TMC(X2) @@ -2606,6 +2728,7 @@ #define X2_RSENSE 0.11 #define X2_CHAIN_POS -1 //#define X2_INTERPOLATE true + //#define X2_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Y) @@ -2615,6 +2738,7 @@ #define Y_RSENSE 0.11 #define Y_CHAIN_POS -1 //#define Y_INTERPOLATE true + //#define Y_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Y2) @@ -2624,6 +2748,7 @@ #define Y2_RSENSE 0.11 #define Y2_CHAIN_POS -1 //#define Y2_INTERPOLATE true + //#define Y2_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Z) @@ -2633,6 +2758,7 @@ #define Z_RSENSE 0.11 #define Z_CHAIN_POS -1 //#define Z_INTERPOLATE true + //#define Z_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Z2) @@ -2642,6 +2768,7 @@ #define Z2_RSENSE 0.11 #define Z2_CHAIN_POS -1 //#define Z2_INTERPOLATE true + //#define Z2_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Z3) @@ -2651,6 +2778,7 @@ #define Z3_RSENSE 0.11 #define Z3_CHAIN_POS -1 //#define Z3_INTERPOLATE true + //#define Z3_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(Z4) @@ -2660,6 +2788,7 @@ #define Z4_RSENSE 0.11 #define Z4_CHAIN_POS -1 //#define Z4_INTERPOLATE true + //#define Z4_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(I) @@ -2669,6 +2798,7 @@ #define I_RSENSE 0.11 #define I_CHAIN_POS -1 //#define I_INTERPOLATE true + //#define I_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(J) @@ -2678,6 +2808,7 @@ #define J_RSENSE 0.11 #define J_CHAIN_POS -1 //#define J_INTERPOLATE true + //#define J_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(K) @@ -2687,6 +2818,7 @@ #define K_RSENSE 0.11 #define K_CHAIN_POS -1 //#define K_INTERPOLATE true + //#define K_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E0) @@ -2695,6 +2827,7 @@ #define E0_RSENSE 0.11 #define E0_CHAIN_POS -1 //#define E0_INTERPOLATE true + //#define E0_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E1) @@ -2703,6 +2836,7 @@ #define E1_RSENSE 0.11 #define E1_CHAIN_POS -1 //#define E1_INTERPOLATE true + //#define E1_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E2) @@ -2711,6 +2845,7 @@ #define E2_RSENSE 0.11 #define E2_CHAIN_POS -1 //#define E2_INTERPOLATE true + //#define E2_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E3) @@ -2719,6 +2854,7 @@ #define E3_RSENSE 0.11 #define E3_CHAIN_POS -1 //#define E3_INTERPOLATE true + //#define E3_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E4) @@ -2727,6 +2863,7 @@ #define E4_RSENSE 0.11 #define E4_CHAIN_POS -1 //#define E4_INTERPOLATE true + //#define E4_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E5) @@ -2735,6 +2872,7 @@ #define E5_RSENSE 0.11 #define E5_CHAIN_POS -1 //#define E5_INTERPOLATE true + //#define E5_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E6) @@ -2743,6 +2881,7 @@ #define E6_RSENSE 0.11 #define E6_CHAIN_POS -1 //#define E6_INTERPOLATE true + //#define E6_HOLD_MULTIPLIER 0.5 #endif #if AXIS_IS_TMC(E7) @@ -2751,6 +2890,7 @@ #define E7_RSENSE 0.11 #define E7_CHAIN_POS -1 //#define E7_INTERPOLATE true + //#define E7_HOLD_MULTIPLIER 0.5 #endif /** @@ -2863,6 +3003,9 @@ //#define CHOPPER_TIMING_Z2 CHOPPER_TIMING_Z //#define CHOPPER_TIMING_Z3 CHOPPER_TIMING_Z //#define CHOPPER_TIMING_Z4 CHOPPER_TIMING_Z + //#define CHOPPER_TIMING_I CHOPPER_TIMING + //#define CHOPPER_TIMING_J CHOPPER_TIMING + //#define CHOPPER_TIMING_K CHOPPER_TIMING //#define CHOPPER_TIMING_E CHOPPER_TIMING // For Extruders (override below) //#define CHOPPER_TIMING_E1 CHOPPER_TIMING_E //#define CHOPPER_TIMING_E2 CHOPPER_TIMING_E @@ -3100,7 +3243,7 @@ #define Z4_SLEW_RATE 1 #endif - #if AXIS_DRIVER_TYPE_I(L6470) + #if AXIS_IS_L64XX(I) #define I_MICROSTEPS 128 #define I_OVERCURRENT 2000 #define I_STALLCURRENT 1500 @@ -3109,7 +3252,7 @@ #define I_SLEW_RATE 1 #endif - #if AXIS_DRIVER_TYPE_J(L6470) + #if AXIS_IS_L64XX(J) #define J_MICROSTEPS 128 #define J_OVERCURRENT 2000 #define J_STALLCURRENT 1500 @@ -3118,7 +3261,7 @@ #define J_SLEW_RATE 1 #endif - #if AXIS_DRIVER_TYPE_K(L6470) + #if AXIS_IS_L64XX(K) #define K_MICROSTEPS 128 #define K_OVERCURRENT 2000 #define K_STALLCURRENT 1500 @@ -3321,7 +3464,7 @@ * You'll need to select a pin for the ON/OFF function and optionally choose a 0-5V * hardware PWM pin for the speed control and a pin for the rotation direction. * - * See https://marlinfw.org/docs/configuration/laser_spindle.html for more config details. + * See https://marlinfw.org/docs/configuration/2.0.9/laser_spindle.html for more config details. */ //#define SPINDLE_FEATURE //#define LASER_FEATURE @@ -3331,7 +3474,10 @@ #define SPINDLE_LASER_USE_PWM // Enable if your controller supports setting the speed/power #if ENABLED(SPINDLE_LASER_USE_PWM) #define SPINDLE_LASER_PWM_INVERT false // Set to "true" if the speed/power goes up when you want it to go slower - #define SPINDLE_LASER_FREQUENCY 2500 // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC) + #define SPINDLE_LASER_FREQUENCY 2500 // (Hz) Spindle/laser frequency (only on supported HALs: AVR, ESP32, and LPC) + // ESP32: If SPINDLE_LASER_PWM_PIN is onboard then <=78125Hz. For I2S expander + // the frequency determines the PWM resolution. 2500Hz = 0-100, 977Hz = 0-255, ... + // (250000 / SPINDLE_LASER_FREQUENCY) = max value. #endif //#define AIR_EVACUATION // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11 @@ -3405,83 +3551,55 @@ #endif // Define the minimum and maximum test pulse time values for a laser test fire function - #define LASER_TEST_PULSE_MIN 1 // Used with Laser Control Menu - #define LASER_TEST_PULSE_MAX 999 // Caution: Menu may not show more than 3 characters + #define LASER_TEST_PULSE_MIN 1 // (ms) Used with Laser Control Menu + #define LASER_TEST_PULSE_MAX 999 // (ms) Caution: Menu may not show more than 3 characters + + #define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power + #define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop + + /** + * Laser Safety Timeout + * + * The laser should be turned off when there is no movement for a period of time. + * Consider material flammability, cut rate, and G-code order when setting this + * value. Too low and it could turn off during a very slow move; too high and + * the material could ignite. + */ + #define LASER_SAFETY_TIMEOUT_MS 1000 // (ms) /** - * Enable inline laser power to be handled in the planner / stepper routines. - * Inline power is specified by the I (inline) flag in an M3 command (e.g., M3 S20 I) - * or by the 'S' parameter in G0/G1/G2/G3 moves (see LASER_MOVE_POWER). + * Any M3 or G1/2/3/5 command with the 'I' parameter enables continuous inline power mode. * - * This allows the laser to keep in perfect sync with the planner and removes - * the powerup/down delay since lasers require negligible time. + * e.g., 'M3 I' enables continuous inline power which is processed by the planner. + * Power is stored in move blocks and applied when blocks are processed by the Stepper ISR. + * + * 'M4 I' sets dynamic mode which uses the current feedrate to calculate a laser power OCR value. + * + * Any move in dynamic mode will use the current feedrate to calculate the laser power. + * Feed rates are set by the F parameter of a move command e.g. G1 X0 Y10 F6000 + * Laser power would be calculated by bit shifting off 8 LSB's. In binary this is div 256. + * The calculation gives us ocr values from 0 to 255, values over F65535 will be set as 255 . + * More refined power control such as compesation for accell/decell will be addressed in future releases. + * + * M5 I clears inline mode and set power to 0, M5 sets the power output to 0 but leaves inline mode on. */ - //#define LASER_POWER_INLINE - - #if ENABLED(LASER_POWER_INLINE) - /** - * Scale the laser's power in proportion to the movement rate. - * - * - Sets the entry power proportional to the entry speed over the nominal speed. - * - Ramps the power up every N steps to approximate the speed trapezoid. - * - Due to the limited power resolution this is only approximate. - */ - #define LASER_POWER_INLINE_TRAPEZOID - - /** - * Continuously calculate the current power (nominal_power * current_rate / nominal_rate). - * Required for accurate power with non-trapezoidal acceleration (e.g., S_CURVE_ACCELERATION). - * This is a costly calculation so this option is discouraged on 8-bit AVR boards. - * - * LASER_POWER_INLINE_TRAPEZOID_CONT_PER defines how many step cycles there are between power updates. If your - * board isn't able to generate steps fast enough (and you are using LASER_POWER_INLINE_TRAPEZOID_CONT), increase this. - * Note that when this is zero it means it occurs every cycle; 1 means a delay wait one cycle then run, etc. - */ - //#define LASER_POWER_INLINE_TRAPEZOID_CONT - - /** - * Stepper iterations between power updates. Increase this value if the board - * can't keep up with the processing demands of LASER_POWER_INLINE_TRAPEZOID_CONT. - * Disable (or set to 0) to recalculate power on every stepper iteration. - */ - //#define LASER_POWER_INLINE_TRAPEZOID_CONT_PER 10 - - /** - * Include laser power in G0/G1/G2/G3/G5 commands with the 'S' parameter - */ - //#define LASER_MOVE_POWER - - #if ENABLED(LASER_MOVE_POWER) - // Turn off the laser on G0 moves with no power parameter. - // If a power parameter is provided, use that instead. - //#define LASER_MOVE_G0_OFF - - // Turn off the laser on G28 homing. - //#define LASER_MOVE_G28_OFF - #endif - - /** - * Inline flag inverted - * - * WARNING: M5 will NOT turn off the laser unless another move - * is done (so G-code files must end with 'M5 I'). - */ - //#define LASER_POWER_INLINE_INVERT - - /** - * Continuously apply inline power. ('M3 S3' == 'G1 S3' == 'M3 S3 I') - * - * The laser might do some weird things, so only enable this - * feature if you understand the implications. - */ - //#define LASER_POWER_INLINE_CONTINUOUS - - #else - #define SPINDLE_LASER_POWERUP_DELAY 50 // (ms) Delay to allow the spindle/laser to come up to speed/power - #define SPINDLE_LASER_POWERDOWN_DELAY 50 // (ms) Delay to allow the spindle to stop + /** + * Enable M3 commands for laser mode inline power planner syncing. + * This feature enables any M3 S-value to be injected into the block buffers while in + * CUTTER_MODE_CONTINUOUS. The option allows M3 laser power to be commited without waiting + * for a planner syncronization + */ + //#define LASER_POWER_SYNC - #endif + /** + * Scale the laser's power in proportion to the movement rate. + * + * - Sets the entry power proportional to the entry speed over the nominal speed. + * - Ramps the power up every N steps to approximate the speed trapezoid. + * - Due to the limited power resolution this is only approximate. + */ + //#define LASER_POWER_TRAP // // Laser I2C Ammeter (High precision INA226 low/high side module) @@ -3492,6 +3610,20 @@ #define I2C_AMMETER_SHUNT_RESISTOR 0.1 // (Ohms) Calibration shunt resistor value #endif + // + // Laser Coolant Flow Meter + // + //#define LASER_COOLANT_FLOW_METER + #if ENABLED(LASER_COOLANT_FLOW_METER) + #define FLOWMETER_PIN 20 // Requires an external interrupt-enabled pin (e.g., RAMPS 2,3,18,19,20,21) + #define FLOWMETER_PPL 5880 // (pulses/liter) Flow meter pulses-per-liter on the input pin + #define FLOWMETER_INTERVAL 1000 // (ms) Flow rate calculation interval in milliseconds + #define FLOWMETER_SAFETY // Prevent running the laser without the minimum flow rate set below + #if ENABLED(FLOWMETER_SAFETY) + #define FLOWMETER_MIN_LITERS_PER_MINUTE 1.5 // (liters/min) Minimum flow required when enabled + #endif + #endif + #endif #endif // SPINDLE_FEATURE || LASER_FEATURE @@ -3595,10 +3727,19 @@ */ //#define CNC_COORDINATE_SYSTEMS +/** + * Auto-report fan speed with M123 S + * Requires fans with tachometer pins + */ +//#define AUTO_REPORT_FANS + /** * Auto-report temperatures with M155 S */ #define AUTO_REPORT_TEMPERATURES +#if ENABLED(AUTO_REPORT_TEMPERATURES) && TEMP_SENSOR_REDUNDANT + //#define AUTO_REPORT_REDUNDANT // Include the "R" sensor in the auto-report +#endif /** * Auto-report position with M154 S @@ -3664,15 +3805,6 @@ //#define REPORT_FAN_CHANGE // Report the new fan speed when changed by M106 (and others) -/** - * 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. - * - * For clients that use a fixed-width font (like OctoPrint), leave this set to 1.0. - * Otherwise, adjust according to your client and font. - */ -#define PROPORTIONAL_FONT_RATIO 1.0 - /** * Spend 28 bytes of SRAM to optimize the G-code parser */ @@ -3836,9 +3968,13 @@ */ //#define HOST_ACTION_COMMANDS #if ENABLED(HOST_ACTION_COMMANDS) - //#define HOST_PAUSE_M76 - //#define HOST_PROMPT_SUPPORT - //#define HOST_START_MENU_ITEM // Add a menu item that tells the host to start + //#define HOST_PAUSE_M76 // Tell the host to pause in response to M76 + //#define HOST_PROMPT_SUPPORT // Initiate host prompts to get user feedback + #if ENABLED(HOST_PROMPT_SUPPORT) + //#define HOST_STATUS_NOTIFICATIONS // Send some status messages to the host as notifications + #endif + //#define HOST_START_MENU_ITEM // Add a menu item that tells the host to start + //#define HOST_SHUTDOWN_MENU_ITEM // Add a menu item that tells the host to shut down #endif /** @@ -3973,12 +4109,12 @@ /** * Instant freeze / unfreeze functionality - * Specified pin has pullup and connecting to ground will instantly pause motion. * Potentially useful for emergency stop that allows being resumed. */ //#define FREEZE_FEATURE #if ENABLED(FREEZE_FEATURE) //#define FREEZE_PIN 41 // Override the default (KILL) pin here + #define FREEZE_STATE LOW // State of pin indicating freeze #endif /** @@ -4212,3 +4348,6 @@ */ //#define SOFT_RESET_VIA_SERIAL // 'KILL' and '^X' commands will soft-reset the controller //#define SOFT_RESET_ON_KILL // Use a digital button to soft-reset the controller after KILL + +// Report uncleaned reset reason from register r2 instead of MCUSR. Supported by Optiboot on AVR. +//#define OPTIBOOT_RESET_REASON diff --git a/Marlin/Makefile b/Marlin/Makefile index d09e5828f5b2f..c72c1d5896077 100644 --- a/Marlin/Makefile +++ b/Marlin/Makefile @@ -109,7 +109,7 @@ LIQUID_TWI2 ?= 0 # This defines if Wire is needed WIRE ?= 0 -# This defines if Tone is needed (i.e SPEAKER is defined in Configuration.h) +# This defines if Tone is needed (i.e., SPEAKER is defined in Configuration.h) # Disabling this (and SPEAKER) saves approximately 350 bytes of memory. TONE ?= 1 @@ -132,7 +132,7 @@ 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) - @echo This version of GCC is likely broken. Enabling relocation workaround. + $(warning This GCC version $(CC_VER) is likely broken. Enabling relocation workaround.) RELOC_WORKAROUND = 1 endif @@ -207,11 +207,11 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1105) else ifeq ($(HARDWARE_MOTHERBOARD),1106) # MKS BASE v1.0 else ifeq ($(HARDWARE_MOTHERBOARD),1107) -# MKS v1.4 with A4982 stepper drivers +# MKS BASE v1.4 with Allegro A4982 stepper drivers else ifeq ($(HARDWARE_MOTHERBOARD),1108) -# MKS v1.5 with Allegro A4982 stepper drivers +# MKS BASE v1.5 with Allegro A4982 stepper drivers else ifeq ($(HARDWARE_MOTHERBOARD),1109) -# MKS v1.6 with Allegro A4982 stepper drivers +# MKS BASE v1.6 with Allegro A4982 stepper drivers else ifeq ($(HARDWARE_MOTHERBOARD),1110) # MKS BASE 1.0 with Heroic HR4982 stepper drivers else ifeq ($(HARDWARE_MOTHERBOARD),1111) @@ -219,93 +219,108 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1111) else ifeq ($(HARDWARE_MOTHERBOARD),1112) # MKS GEN L else ifeq ($(HARDWARE_MOTHERBOARD),1113) -# zrib V2.0 control board (Chinese RAMPS replica) -else ifeq ($(HARDWARE_MOTHERBOARD),1114) # BigTreeTech or BIQU KFB2.0 +else ifeq ($(HARDWARE_MOTHERBOARD),1114) +# zrib V2.0 (Chinese RAMPS replica) else ifeq ($(HARDWARE_MOTHERBOARD),1115) -# Felix 2.0+ Electronics Board (RAMPS like) +# zrib V5.2 (Chinese RAMPS replica) else ifeq ($(HARDWARE_MOTHERBOARD),1116) -# Invent-A-Part RigidBoard +# Felix 2.0+ Electronics Board (RAMPS like) else ifeq ($(HARDWARE_MOTHERBOARD),1117) -# Invent-A-Part RigidBoard V2 +# Invent-A-Part RigidBoard else ifeq ($(HARDWARE_MOTHERBOARD),1118) -# Sainsmart 2-in-1 board +# Invent-A-Part RigidBoard V2 else ifeq ($(HARDWARE_MOTHERBOARD),1119) -# Ultimaker +# Sainsmart 2-in-1 board else ifeq ($(HARDWARE_MOTHERBOARD),1120) -# Ultimaker (Older electronics. Pre 1.5.4. This is rare) +# Ultimaker else ifeq ($(HARDWARE_MOTHERBOARD),1121) +# Ultimaker (Older electronics. Pre 1.5.4. This is rare) +else ifeq ($(HARDWARE_MOTHERBOARD),1122) MCU ?= atmega1280 PROG_MCU ?= m1280 - # Azteeg X3 -else ifeq ($(HARDWARE_MOTHERBOARD),1122) -# Azteeg X3 Pro else ifeq ($(HARDWARE_MOTHERBOARD),1123) -# Ultimainboard 2.x (Uses TEMP_SENSOR 20) +# Azteeg X3 Pro else ifeq ($(HARDWARE_MOTHERBOARD),1124) -# Rumba +# Ultimainboard 2.x (Uses TEMP_SENSOR 20) else ifeq ($(HARDWARE_MOTHERBOARD),1125) -# Raise3D Rumba +# Rumba else ifeq ($(HARDWARE_MOTHERBOARD),1126) -# Rapide Lite RL200 Rumba +# Raise3D N series Rumba derivative else ifeq ($(HARDWARE_MOTHERBOARD),1127) -# Formbot T-Rex 2 Plus +# Rapide Lite 200 (v1, low-cost RUMBA clone with drv) else ifeq ($(HARDWARE_MOTHERBOARD),1128) -# Formbot T-Rex 3 +# Formbot T-Rex 2 Plus else ifeq ($(HARDWARE_MOTHERBOARD),1129) -# Formbot Raptor +# Formbot T-Rex 3 else ifeq ($(HARDWARE_MOTHERBOARD),1130) -# Formbot Raptor 2 +# Formbot Raptor else ifeq ($(HARDWARE_MOTHERBOARD),1131) -# bq ZUM Mega 3D +# Formbot Raptor 2 else ifeq ($(HARDWARE_MOTHERBOARD),1132) -# MakeBoard Mini v2.1.2 is a control board sold by MicroMake +# bq ZUM Mega 3D else ifeq ($(HARDWARE_MOTHERBOARD),1133) -# TriGorilla Anycubic version 1.3 based on RAMPS EFB +# MakeBoard Mini v2.1.2 by MicroMake else ifeq ($(HARDWARE_MOTHERBOARD),1134) -# TriGorilla Anycubic version 1.4 based on RAMPS EFB +# TriGorilla Anycubic version 1.3-based on RAMPS EFB else ifeq ($(HARDWARE_MOTHERBOARD),1135) -# TriGorilla Anycubic version 1.4 Rev 1.1 +# ... Ver 1.4 else ifeq ($(HARDWARE_MOTHERBOARD),1136) -# Creality: Ender-4, CR-8 +# ... Rev 1.1 (new servo pin order) else ifeq ($(HARDWARE_MOTHERBOARD),1137) -# Creality: CR10S, CR20, CR-X +# Creality: Ender-4, CR-8 else ifeq ($(HARDWARE_MOTHERBOARD),1138) -# Dagoma F5 +# Creality: CR10S, CR20, CR-X else ifeq ($(HARDWARE_MOTHERBOARD),1139) -# FYSETC F6 1.3 +# Dagoma F5 else ifeq ($(HARDWARE_MOTHERBOARD),1140) -# FYSETC F6 1.5 +# FYSETC F6 1.3 else ifeq ($(HARDWARE_MOTHERBOARD),1141) -# Duplicator i3 Plus +# FYSETC F6 1.4 else ifeq ($(HARDWARE_MOTHERBOARD),1142) -# VORON +# Wanhao Duplicator i3 Plus else ifeq ($(HARDWARE_MOTHERBOARD),1143) -# TRONXY V3 1.0 +# VORON Design else ifeq ($(HARDWARE_MOTHERBOARD),1144) -# Z-Bolt X Series +# Tronxy TRONXY-V3-1.0 else ifeq ($(HARDWARE_MOTHERBOARD),1145) -# TT OSCAR +# Z-Bolt X Series else ifeq ($(HARDWARE_MOTHERBOARD),1146) -# Overlord/Overlord Pro +# TT OSCAR else ifeq ($(HARDWARE_MOTHERBOARD),1147) -# ADIMLab Gantry v1 +# Overlord/Overlord Pro else ifeq ($(HARDWARE_MOTHERBOARD),1148) -# ADIMLab Gantry v2 +# ADIMLab Gantry v1 else ifeq ($(HARDWARE_MOTHERBOARD),1149) -# BIQU Tango V1 +# ADIMLab Gantry v2 else ifeq ($(HARDWARE_MOTHERBOARD),1150) -# MKS GEN L V2 +# BIQU Tango V1 else ifeq ($(HARDWARE_MOTHERBOARD),1151) -# MKS GEN L V2.1 +# MKS GEN L V2 else ifeq ($(HARDWARE_MOTHERBOARD),1152) -# Copymaster 3D +# MKS GEN L V2.1 else ifeq ($(HARDWARE_MOTHERBOARD),1153) -# Ortur 4 +# Copymaster 3D else ifeq ($(HARDWARE_MOTHERBOARD),1154) -# Tenlog D3 Hero +# Ortur 4 else ifeq ($(HARDWARE_MOTHERBOARD),1155) +# Tenlog D3 Hero IDEX printer +else ifeq ($(HARDWARE_MOTHERBOARD),1156) +# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Fan, Bed) +else ifeq ($(HARDWARE_MOTHERBOARD),1157) +# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend0, Hotend1, Hotend2, Bed) +else ifeq ($(HARDWARE_MOTHERBOARD),1158) +# Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed) +else ifeq ($(HARDWARE_MOTHERBOARD),1159) +# Longer LK1 PRO / Alfawise U20 Pro (PRO version) +else ifeq ($(HARDWARE_MOTHERBOARD),1160) +# Longer LKx PRO / Alfawise Uxx Pro (PRO version) +else ifeq ($(HARDWARE_MOTHERBOARD),1161) +# Zonestar zrib V5.3 (Chinese RAMPS replica) +else ifeq ($(HARDWARE_MOTHERBOARD),1162) +# Pxmalion Core I3 +else ifeq ($(HARDWARE_MOTHERBOARD),1163) # # RAMBo and derivatives @@ -358,20 +373,38 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1311) else ifeq ($(HARDWARE_MOTHERBOARD),1312) # Mega controller else ifeq ($(HARDWARE_MOTHERBOARD),1313) -# Geeetech GT2560 Rev B for Mecreator2 +# Geeetech GT2560 Rev A else ifeq ($(HARDWARE_MOTHERBOARD),1314) -# Geeetech GT2560 Rev. A +# Geeetech GT2560 Rev A+ (with auto level probe) else ifeq ($(HARDWARE_MOTHERBOARD),1315) -# Geeetech GT2560 Rev. A+ (with auto level probe) +# Geeetech GT2560 Rev B else ifeq ($(HARDWARE_MOTHERBOARD),1316) -# Geeetech GT2560 Rev B for A10(M/D) +# Geeetech GT2560 Rev B for A10(M/T/D) else ifeq ($(HARDWARE_MOTHERBOARD),1317) -# Geeetech GT2560 Rev B for A20(M/D) +# Geeetech GT2560 Rev B for A10(M/T/D) else ifeq ($(HARDWARE_MOTHERBOARD),1318) -# Einstart retrofit +# Geeetech GT2560 Rev B for Mecreator2 else ifeq ($(HARDWARE_MOTHERBOARD),1319) -# Wanhao 0ne+ i3 Mini +# Geeetech GT2560 Rev B for A20(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),1328) +# Mega controller & Protoneer CNC Shield V3.00 +else ifeq ($(HARDWARE_MOTHERBOARD),1329) # # ATmega1281, ATmega2561 @@ -445,6 +478,11 @@ else ifeq ($(HARDWARE_MOTHERBOARD),1510) HARDWARE_VARIANT ?= Sanguino MCU ?= atmega1284p PROG_MCU ?= m1284p +# ZoneStar ZMIB V2 +else ifeq ($(HARDWARE_MOTHERBOARD),1511) + HARDWARE_VARIANT ?= Sanguino + MCU ?= atmega1284p + PROG_MCU ?= m1284p # # Other ATmega644P, ATmega644, ATmega1284P diff --git a/Marlin/Version.h b/Marlin/Version.h index bd3bb0ae5fbc1..bbeb3084f065c 100644 --- a/Marlin/Version.h +++ b/Marlin/Version.h @@ -28,7 +28,7 @@ /** * Marlin release version identifier */ -//#define SHORT_BUILD_VERSION "2.0.9.2" +//#define SHORT_BUILD_VERSION "2.0.9.7" /** * Verbose version identifier which should contain a reference to the location @@ -41,7 +41,7 @@ * here we define this default string as the date where the latest release * version was tagged. */ -//#define STRING_DISTRIBUTION_DATE "2021-09-03" +//#define STRING_DISTRIBUTION_DATE "2023-07-04" /** * Defines a generic printer name to be output to the LCD after booting Marlin. diff --git a/Marlin/config.ini b/Marlin/config.ini new file mode 100644 index 0000000000000..0fb9fb0c9308b --- /dev/null +++ b/Marlin/config.ini @@ -0,0 +1,211 @@ +# +# Marlin Firmware +# config.ini - Options to apply before the build +# +[config:base] +ini_use_config = none + +# Load all config: sections in this file +;ini_use_config = all +# Load config file relative to Marlin/ +;ini_use_config = another.ini +# Download configurations from GitHub +;ini_use_config = example/Creality/Ender-5 Plus @ bugfix-2.1.x +# Download configurations from your server +;ini_use_config = https://me.myserver.com/path/to/configs +# Evaluate config:base and do a config dump +;ini_use_config = base +;config_export = 2 + +[config:minimal] +motherboard = BOARD_RAMPS_14_EFB +serial_port = 0 +baudrate = 250000 + +use_watchdog = on +thermal_protection_hotends = on +thermal_protection_hysteresis = 4 +thermal_protection_period = 40 + +bufsize = 4 +block_buffer_size = 16 +max_cmd_size = 96 + +extruders = 1 +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 = BANG_MAX +pid_functional_range = 10 + +default_kp = 22.20 +default_ki = 1.08 +default_kd = 114.00 + +x_driver_type = A4988 +y_driver_type = A4988 +z_driver_type = A4988 +e0_driver_type = A4988 + +x_bed_size = 200 +x_min_pos = 0 +x_max_pos = X_BED_SIZE + +y_bed_size = 200 +y_min_pos = 0 +y_max_pos = Y_BED_SIZE + +z_min_pos = 0 +z_max_pos = 200 + +x_home_dir = -1 +y_home_dir = -1 +z_home_dir = -1 + +use_xmin_plug = on +use_ymin_plug = on +use_zmin_plug = on + +x_min_endstop_inverting = false +y_min_endstop_inverting = false +z_min_endstop_inverting = false + +default_axis_steps_per_unit = { 80, 80, 400, 500 } +axis_relative_modes = { false, false, false, false } +default_max_feedrate = { 300, 300, 5, 25 } +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 + +invert_x_dir = false +invert_y_dir = true +invert_z_dir = false +invert_e0_dir = false + +invert_e_step_pin = false +invert_x_step_pin = false +invert_y_step_pin = false +invert_z_step_pin = false + +disable_x = false +disable_y = false +disable_z = false +disable_e = false + +proportional_font_ratio = 1.0 +default_nominal_filament_dia = 1.75 + +junction_deviation_mm = 0.013 + +default_acceleration = 3000 +default_travel_acceleration = 3000 +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] +bed_overshoot = 10 +busy_while_heating = on +default_ejerk = 5.0 +default_keepalive_interval = 2 +default_leveling_fade_height = 0.0 +disable_inactive_extruder = on +display_charset_hd44780 = JAPANESE +eeprom_boot_silent = on +eeprom_chitchat = on +endstoppullups = on +extrude_maxlength = 200 +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 +min_software_endstops = on +max_software_endstops = on +min_software_endstop_x = on +min_software_endstop_y = on +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_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)" +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 +auto_report_temperatures = on +autotemp = on +autotemp_oldweight = 0.98 +bed_check_interval = 5000 +default_stepper_deactive_time = 120 +default_volumetric_extruder_limit = 0.00 +disable_inactive_e = true +disable_inactive_x = true +disable_inactive_y = true +disable_inactive_z = true +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 +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 +tx_buffer_size = 0 +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.cpp b/Marlin/src/HAL/AVR/HAL.cpp index 708583b262a62..5382eb36a2bd5 100644 --- a/Marlin/src/HAL/AVR/HAL.cpp +++ b/Marlin/src/HAL/AVR/HAL.cpp @@ -23,6 +23,7 @@ #include "../../inc/MarlinConfig.h" #include "HAL.h" +#include #ifdef USBCON DefaultSerial1 MSerial0(false, Serial); @@ -35,13 +36,32 @@ // Public Variables // ------------------------ -//uint8_t MCUSR; +// Don't initialize/override variable (which would happen in .init4) +uint8_t MarlinHAL::reset_reason __attribute__((section(".noinit"))); // ------------------------ // Public functions // ------------------------ -void HAL_init() { +__attribute__((naked)) // Don't output function pro- and epilogue +__attribute__((used)) // Output the function, even if "not used" +__attribute__((section(".init3"))) // Put in an early user definable section +void save_reset_reason() { + #if ENABLED(OPTIBOOT_RESET_REASON) + __asm__ __volatile__( + A("STS %0, r2") + : "=m"(hal.reset_reason) + ); + #else + hal.reset_reason = MCUSR; + #endif + + // Clear within 16ms since WDRF bit enables a 16ms watchdog timer -> Boot loop + hal.clear_reset_source(); + wdt_disable(); +} + +void MarlinHAL::init() { // Init Servo Pins #define INIT_SERVO(N) OUT_WRITE(SERVO##N##_PIN, LOW) #if HAS_SERVO_0 @@ -56,9 +76,11 @@ void HAL_init() { #if HAS_SERVO_3 INIT_SERVO(3); #endif + + init_pwm_timers(); // Init user timers to default frequency - 1000HZ } -void HAL_reboot() { +void MarlinHAL::reboot() { #if ENABLED(USE_WATCHDOG) while (1) { /* run out the watchdog */ } #else @@ -67,6 +89,62 @@ void HAL_reboot() { #endif } +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #include + #include "../../MarlinCore.h" + + // Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s. + void MarlinHAL::watchdog_init() { + #if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S) + #define WDTO_NS WDTO_8S + #else + #define WDTO_NS WDTO_4S + #endif + #if ENABLED(WATCHDOG_RESET_MANUAL) + // Enable the watchdog timer, but only for the interrupt. + // Take care, as this requires the correct order of operation, with interrupts disabled. + // See the datasheet of any AVR chip for details. + wdt_reset(); + cli(); + _WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE); + _WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5 + // So worked for up to WDTO_2S + sei(); + wdt_reset(); + #else + wdt_enable(WDTO_NS); // The function handles the upper bit correct. + #endif + //delay(10000); // test it! + } + + //=========================================================================== + //=================================== ISR =================================== + //=========================================================================== + + // Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled. + #if ENABLED(WATCHDOG_RESET_MANUAL) + ISR(WDT_vect) { + sei(); // With the interrupt driven serial we need to allow interrupts. + SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED); + minkill(); // interrupt-safe final kill and infinite loop + } + #endif + + // Reset watchdog. MUST be called at least every 4 seconds after the + // first watchdog_init or AVR will go into emergency procedures. + void MarlinHAL::watchdog_refresh() { wdt_reset(); } + +#endif // USE_WATCHDOG + +// ------------------------ +// Free Memory Accessor +// ------------------------ + #if ENABLED(SDSUPPORT) #include "../../sd/SdFatUtil.h" @@ -74,20 +152,20 @@ void HAL_reboot() { #else // !SDSUPPORT -extern "C" { - extern char __bss_end; - extern char __heap_start; - extern void* __brkval; - - int freeMemory() { - int free_memory; - if ((int)__brkval == 0) - free_memory = ((int)&free_memory) - ((int)&__bss_end); - else - free_memory = ((int)&free_memory) - ((int)__brkval); - return free_memory; + extern "C" { + extern char __bss_end; + extern char __heap_start; + extern void* __brkval; + + int freeMemory() { + int free_memory; + if ((int)__brkval == 0) + free_memory = ((int)&free_memory) - ((int)&__bss_end); + else + free_memory = ((int)&free_memory) - ((int)__brkval); + return free_memory; + } } -} #endif // !SDSUPPORT diff --git a/Marlin/src/HAL/AVR/HAL.h b/Marlin/src/HAL/AVR/HAL.h index a22daf9b5c88a..1491867721587 100644 --- a/Marlin/src/HAL/AVR/HAL.h +++ b/Marlin/src/HAL/AVR/HAL.h @@ -19,16 +19,18 @@ */ #pragma once +/** + * HAL for Arduino AVR + */ + #include "../shared/Marduino.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include "math.h" #ifdef USBCON #include #else - #define HardwareSerial_h // Hack to prevent HardwareSerial.h header inclusion #include "MarlinSerial.h" #endif @@ -74,9 +76,9 @@ #define CRITICAL_SECTION_START() unsigned char _sreg = SREG; cli() #define CRITICAL_SECTION_END() SREG = _sreg #endif -#define ISRS_ENABLED() TEST(SREG, SREG_I) -#define ENABLE_ISRS() sei() -#define DISABLE_ISRS() cli() + +#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() // ------------------------ // Types @@ -84,16 +86,15 @@ typedef int8_t pin_t; -#define SHARED_SERVOS HAS_SERVOS -#define HAL_SERVO_LIB Servo +#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +class Servo; +typedef Servo hal_servo_t; // ------------------------ -// Public Variables +// Serial ports // ------------------------ -//extern uint8_t MCUSR; - -// Serial ports #ifdef USBCON #include "../../core/serial_hook.h" typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; @@ -142,89 +143,135 @@ typedef int8_t pin_t; #endif #endif -// ------------------------ -// Public functions -// ------------------------ +// +// ADC +// +#define HAL_ADC_VREF 5.0 +#define HAL_ADC_RESOLUTION 10 -void HAL_init(); +// +// 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) -//void cli(); +#define HAL_SENSITIVE_PINS 0, 1, -//void _delay_ms(const int delay); +#ifdef __AVR_AT90USB1286__ + #define JTAG_DISABLE() do{ MCUCR = 0x80; MCUCR = 0x80; }while(0) +#endif -inline void HAL_clear_reset_source() { MCUSR = 0; } -inline uint8_t HAL_get_reset_source() { return MCUSR; } +// AVR compatibility +#define strtof strtod -void HAL_reboot(); +// ------------------------ +// Free Memory Accessor +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif extern "C" int freeMemory(); -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop -// ADC -#ifdef DIDR2 - #define HAL_ANALOG_SELECT(ind) do{ if (ind < 8) SBI(DIDR0, ind); else SBI(DIDR2, ind & 0x07); }while(0) -#else - #define HAL_ANALOG_SELECT(ind) SBI(DIDR0, ind); -#endif +// ------------------------ +// MarlinHAL Class +// ------------------------ -inline void HAL_adc_init() { - ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07; - DIDR0 = 0; - #ifdef DIDR2 - DIDR2 = 0; - #endif -} +class MarlinHAL { +public: -#define SET_ADMUX_ADCSRA(ch) ADMUX = _BV(REFS0) | (ch & 0x07); SBI(ADCSRA, ADSC) -#ifdef MUX5 - #define HAL_START_ADC(ch) if (ch > 7) ADCSRB = _BV(MUX5); else ADCSRB = 0; SET_ADMUX_ADCSRA(ch) -#else - #define HAL_START_ADC(ch) ADCSRB = 0; SET_ADMUX_ADCSRA(ch) -#endif + // Earliest possible init, before setup() + MarlinHAL() {} -#define HAL_ADC_VREF 5.0 -#define HAL_ADC_RESOLUTION 10 -#define HAL_READ_ADC() ADC -#define HAL_ADC_READY() !TEST(ADCSRA, ADSC) + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + static void init(); // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -#define HAL_SENSITIVE_PINS 0, 1, + // Interrupts + static bool isr_state() { return TEST(SREG, SREG_I); } + static void isr_on() { sei(); } + static void isr_off() { cli(); } -#ifdef __AVR_AT90USB1286__ - #define JTAG_DISABLE() do{ MCUCR = 0x80; MCUCR = 0x80; }while(0) -#endif + static void delay_ms(const int ms) { _delay_ms(ms); } -// AVR compatibility -#define strtof strtod + // Tasks, called from idle() + static void idletask() {} -#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment + // Reset + static uint8_t reset_reason; + static uint8_t get_reset_source() { return reset_reason; } + static void clear_reset_source() { MCUSR = 0; } -/** - * set_pwm_frequency - * Sets the frequency of the timer corresponding to the provided pin - * as close as possible to the provided desired frequency. Internally - * calculates the required waveform generation mode, prescaler and - * resolution values required and sets the 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 FAN PWM Settings) - */ -void set_pwm_frequency(const pin_t pin, int f_desired); + // Free SRAM + static int freeMemory() { return ::freeMemory(); } -/** - * set_pwm_duty - * Sets the PWM duty cycle of the provided pin to the provided value - * Optionally allows inverting the duty cycle [default = false] - * Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255] - */ -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); + // + // ADC Methods + // + + // Called by Temperature::init once at startup + static void adc_init() { + ADCSRA = _BV(ADEN) | _BV(ADSC) | _BV(ADIF) | 0x07; + DIDR0 = 0; + #ifdef DIDR2 + DIDR2 = 0; + #endif + } + + // Called by Temperature::init for each sensor at startup + static void adc_enable(const uint8_t ch) { + #ifdef DIDR2 + if (ch > 7) { SBI(DIDR2, ch & 0x07); return; } + #endif + SBI(DIDR0, ch); + } + + // Begin ADC sampling on the given channel. Called from Temperature::isr! + static void adc_start(const uint8_t ch) { + #ifdef MUX5 + ADCSRB = ch > 7 ? _BV(MUX5) : 0; + #else + ADCSRB = 0; + #endif + ADMUX = _BV(REFS0) | (ch & 0x07); + SBI(ADCSRA, ADSC); + } + + // Is the ADC ready for reading? + static bool adc_ready() { return !TEST(ADCSRA, ADSC); } + + // The current value of the ADC register + static __typeof__(ADC) adc_value() { return ADC; } + + /** + * init_pwm_timers + * Set the default frequency for timers 2-5 to 1000HZ + */ + static void init_pwm_timers(); + + /** + * 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/AVR/HAL_SPI.cpp b/Marlin/src/HAL/AVR/HAL_SPI.cpp index 8784bb07b30e9..dc98f2f79e71b 100644 --- a/Marlin/src/HAL/AVR/HAL_SPI.cpp +++ b/Marlin/src/HAL/AVR/HAL_SPI.cpp @@ -35,22 +35,20 @@ void spiBegin() { #if PIN_EXISTS(SD_SS) - OUT_WRITE(SD_SS_PIN, HIGH); + // Do not init HIGH for boards with pin 4 used as Fans or Heaters or otherwise, not likely to have multiple SPI devices anyway. + #if defined(__AVR_ATmega644__) || defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644PA__) || defined(__AVR_ATmega1284P__) + // SS must be in output mode even it is not chip select + SET_OUTPUT(SD_SS_PIN); + #else + // set SS high - may be chip select for another SPI device + OUT_WRITE(SD_SS_PIN, HIGH); + #endif #endif SET_OUTPUT(SD_SCK_PIN); SET_INPUT(SD_MISO_PIN); SET_OUTPUT(SD_MOSI_PIN); - #if DISABLED(SOFTWARE_SPI) - // SS must be in output mode even it is not chip select - //SET_OUTPUT(SD_SS_PIN); - // set SS high - may be chip select for another SPI device - //#if SET_SPI_SS_HIGH - //WRITE(SD_SS_PIN, HIGH); - //#endif - // set a default rate - spiInit(1); - #endif + IF_DISABLED(SOFTWARE_SPI, spiInit(SPI_HALF_SPEED)); } #if NONE(SOFTWARE_SPI, FORCE_SOFT_SPI) diff --git a/Marlin/src/HAL/AVR/MarlinSerial.cpp b/Marlin/src/HAL/AVR/MarlinSerial.cpp index cd8bf5e6903b8..986462437c8f5 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.cpp +++ b/Marlin/src/HAL/AVR/MarlinSerial.cpp @@ -486,7 +486,7 @@ void MarlinSerial::write(const uint8_t c) { const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1); // If global interrupts are disabled (as the result of being called from an ISR)... - if (!ISRS_ENABLED()) { + if (!hal.isr_state()) { // Make room by polling if it is possible to transmit, and do so! while (i == tx_buffer.tail) { @@ -534,7 +534,7 @@ void MarlinSerial::flushTX() { if (!_written) return; // If global interrupts are disabled (as the result of being called from an ISR)... - if (!ISRS_ENABLED()) { + if (!hal.isr_state()) { // Wait until everything was transmitted - We must do polling, as interrupts are disabled while (tx_buffer.head != tx_buffer.tail || !B_TXC) { diff --git a/Marlin/src/HAL/AVR/MarlinSerial.h b/Marlin/src/HAL/AVR/MarlinSerial.h index 0565c7b9db9ee..7eb76000d66e4 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.h +++ b/Marlin/src/HAL/AVR/MarlinSerial.h @@ -191,13 +191,13 @@ rx_framing_errors; static ring_buffer_pos_t rx_max_enqueued; - static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_head(); + FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_head(); static volatile bool rx_tail_value_not_stable; static volatile uint16_t rx_tail_value_backup; - static FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value); - static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail(); + FORCE_INLINE static void atomic_set_rx_tail(ring_buffer_pos_t value); + FORCE_INLINE static ring_buffer_pos_t atomic_read_rx_tail(); public: FORCE_INLINE static void store_rxd_char(); @@ -217,7 +217,7 @@ #endif enum { HasEmergencyParser = Cfg::EMERGENCYPARSER }; - static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } + static bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; } FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; } diff --git a/Marlin/src/HAL/AVR/Servo.cpp b/Marlin/src/HAL/AVR/Servo.cpp index 526352b773396..0a1ef5337ae99 100644 --- a/Marlin/src/HAL/AVR/Servo.cpp +++ b/Marlin/src/HAL/AVR/Servo.cpp @@ -66,27 +66,26 @@ static volatile int8_t Channel[_Nbr_16timers]; // counter for the s /************ static functions common to all instances ***********************/ -static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) { - if (Channel[timer] < 0) - *TCNTn = 0; // channel set to -1 indicated that refresh interval completed so reset the timer - else { - if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && SERVO(timer, Channel[timer]).Pin.isActive) - extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated - } - - Channel[timer]++; // increment to the next channel - if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { - *OCRnA = *TCNTn + SERVO(timer, Channel[timer]).ticks; - if (SERVO(timer, Channel[timer]).Pin.isActive) // check if activated - extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high +static inline void handle_interrupts(const timer16_Sequence_t timer, volatile uint16_t* TCNTn, volatile uint16_t* OCRnA) { + int8_t cho = Channel[timer]; // Handle the prior Channel[timer] first + if (cho < 0) // Channel -1 indicates the refresh interval completed... + *TCNTn = 0; // ...so reset the timer + else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled? + extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW + + Channel[timer] = ++cho; // Handle the next channel (or 0) + if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) { + *OCRnA = *TCNTn + SERVO(timer, cho).ticks; // set compare to current ticks plus duration + if (SERVO(timer, cho).Pin.isActive) // activated? + extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH } else { // finished all channels so wait for the refresh period to expire before starting over - if (((unsigned)*TCNTn) + 4 < usToTicks(REFRESH_INTERVAL)) // allow a few ticks to ensure the next OCR1A not missed - *OCRnA = (unsigned int)usToTicks(REFRESH_INTERVAL); - else - *OCRnA = *TCNTn + 4; // at least REFRESH_INTERVAL has elapsed - Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + const unsigned int cval = ((unsigned)*TCNTn) + 32 / (SERVO_TIMER_PRESCALER), // allow 32 cycles to ensure the next OCR1A not missed + ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed + *OCRnA = max(cval, ival); + + Channel[timer] = -1; // reset the timer counter to 0 on the next call } } @@ -123,91 +122,102 @@ static inline void handle_interrupts(timer16_Sequence_t timer, volatile uint16_t /****************** end of static functions ******************************/ -void initISR(timer16_Sequence_t timer) { - #ifdef _useTimer1 - if (timer == _timer1) { - TCCR1A = 0; // normal counting mode - TCCR1B = _BV(CS11); // set prescaler of 8 - TCNT1 = 0; // clear the timer count - #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) - SBI(TIFR, OCF1A); // clear any pending interrupts; - SBI(TIMSK, OCIE1A); // enable the output compare interrupt - #else - // here if not ATmega8 or ATmega128 - SBI(TIFR1, OCF1A); // clear any pending interrupts; - SBI(TIMSK1, OCIE1A); // enable the output compare interrupt - #endif - #ifdef WIRING - timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); - #endif - } - #endif - - #ifdef _useTimer3 - if (timer == _timer3) { - TCCR3A = 0; // normal counting mode - TCCR3B = _BV(CS31); // set prescaler of 8 - TCNT3 = 0; // clear the timer count - #ifdef __AVR_ATmega128__ - SBI(TIFR, OCF3A); // clear any pending interrupts; - SBI(ETIMSK, OCIE3A); // enable the output compare interrupt - #else - SBI(TIFR3, OCF3A); // clear any pending interrupts; - SBI(TIMSK3, OCIE3A); // enable the output compare interrupt - #endif - #ifdef WIRING - timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only - #endif - } - #endif - - #ifdef _useTimer4 - if (timer == _timer4) { - TCCR4A = 0; // normal counting mode - TCCR4B = _BV(CS41); // set prescaler of 8 - TCNT4 = 0; // clear the timer count - TIFR4 = _BV(OCF4A); // clear any pending interrupts; - TIMSK4 = _BV(OCIE4A); // enable the output compare interrupt - } - #endif - - #ifdef _useTimer5 - if (timer == _timer5) { - TCCR5A = 0; // normal counting mode - TCCR5B = _BV(CS51); // set prescaler of 8 - TCNT5 = 0; // clear the timer count - TIFR5 = _BV(OCF5A); // clear any pending interrupts; - TIMSK5 = _BV(OCIE5A); // enable the output compare interrupt - } - #endif -} - -void finISR(timer16_Sequence_t timer) { - // Disable use of the given timer - #ifdef WIRING - if (timer == _timer1) { - CBI( - #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) - TIMSK1 +void initISR(const timer16_Sequence_t timer_index) { + switch (timer_index) { + default: break; + + #ifdef _useTimer1 + case _timer1: + TCCR1A = 0; // normal counting mode + TCCR1B = _BV(CS11); // set prescaler of 8 + TCNT1 = 0; // clear the timer count + #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega128__) + SBI(TIFR, OCF1A); // clear any pending interrupts; + SBI(TIMSK, OCIE1A); // enable the output compare interrupt #else - TIMSK + // here if not ATmega8 or ATmega128 + SBI(TIFR1, OCF1A); // clear any pending interrupts; + SBI(TIMSK1, OCIE1A); // enable the output compare interrupt #endif - , OCIE1A); // disable timer 1 output compare interrupt - timerDetach(TIMER1OUTCOMPAREA_INT); - } - else if (timer == _timer3) { - CBI( - #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) - TIMSK3 + #ifdef WIRING + timerAttach(TIMER1OUTCOMPAREA_INT, Timer1Service); + #endif + break; + #endif + + #ifdef _useTimer3 + case _timer3: + TCCR3A = 0; // normal counting mode + TCCR3B = _BV(CS31); // set prescaler of 8 + TCNT3 = 0; // clear the timer count + #ifdef __AVR_ATmega128__ + SBI(TIFR, OCF3A); // clear any pending interrupts; + SBI(ETIMSK, OCIE3A); // enable the output compare interrupt #else - ETIMSK + SBI(TIFR3, OCF3A); // clear any pending interrupts; + SBI(TIMSK3, OCIE3A); // enable the output compare interrupt + #endif + #ifdef WIRING + timerAttach(TIMER3OUTCOMPAREA_INT, Timer3Service); // for Wiring platform only #endif - , OCIE3A); // disable the timer3 output compare A interrupt - timerDetach(TIMER3OUTCOMPAREA_INT); + break; + #endif + + #ifdef _useTimer4 + case _timer4: + TCCR4A = 0; // normal counting mode + TCCR4B = _BV(CS41); // set prescaler of 8 + TCNT4 = 0; // clear the timer count + TIFR4 = _BV(OCF4A); // clear any pending interrupts; + TIMSK4 = _BV(OCIE4A); // enable the output compare interrupt + break; + #endif + + #ifdef _useTimer5 + case _timer5: + TCCR5A = 0; // normal counting mode + TCCR5B = _BV(CS51); // set prescaler of 8 + TCNT5 = 0; // clear the timer count + TIFR5 = _BV(OCF5A); // clear any pending interrupts; + TIMSK5 = _BV(OCIE5A); // enable the output compare interrupt + break; + #endif + } +} + +void finISR(const timer16_Sequence_t timer_index) { + // Disable use of the given timer + #ifdef WIRING + switch (timer_index) { + default: break; + + case _timer1: + CBI( + #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) + TIMSK1 + #else + TIMSK + #endif + , OCIE1A // disable timer 1 output compare interrupt + ); + timerDetach(TIMER1OUTCOMPAREA_INT); + break; + + case _timer3: + CBI( + #if defined(__AVR_ATmega1281__) || defined(__AVR_ATmega2561__) + TIMSK3 + #else + ETIMSK + #endif + , OCIE3A // disable the timer3 output compare A interrupt + ); + timerDetach(TIMER3OUTCOMPAREA_INT); + break; } #else // !WIRING // For arduino - in future: call here to a currently undefined function to reset the timer - UNUSED(timer); + UNUSED(timer_index); #endif } diff --git a/Marlin/src/HAL/AVR/endstop_interrupts.h b/Marlin/src/HAL/AVR/endstop_interrupts.h index 50f29c3356ce5..0ce8574c53d97 100644 --- a/Marlin/src/HAL/AVR/endstop_interrupts.h +++ b/Marlin/src/HAL/AVR/endstop_interrupts.h @@ -301,5 +301,6 @@ void setup_endstop_interrupts() { pciSetup(Z_MIN_PROBE_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 238c1124adeb6..0a384172c32a7 100644 --- a/Marlin/src/HAL/AVR/fast_pwm.cpp +++ b/Marlin/src/HAL/AVR/fast_pwm.cpp @@ -21,11 +21,7 @@ */ #ifdef __AVR__ -#include "../../inc/MarlinConfigPre.h" - -#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM - -#include "HAL.h" +#include "../../inc/MarlinConfig.h" struct Timer { volatile uint8_t* TCCRnQ[3]; // max 3 TCCR registers per timer @@ -33,250 +29,194 @@ struct Timer { volatile uint16_t* ICRn; // max 1 ICR register per timer uint8_t n; // the timer number [0->5] uint8_t q; // the timer output [0->2] (A->C) + bool isPWM; // True if pin is a "hardware timer" + bool isProtected; // True if timer is protected }; +// Macros for the Timer structure +#define _SET_WGMnQ(T, V) do{ \ + *(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V) & 0x3) << 0); \ + *(T.TCCRnQ)[1] = (*(T.TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \ + }while(0) + +// Set TCCR CS bits +#define _SET_CSn(T, V) (*(T.TCCRnQ)[1] = (*(T.TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0)) + +// Set TCCR COM bits +#define _SET_COMnQ(T, Q, V) (*(T.TCCRnQ)[0] = (*(T.TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q)))) + +// Set OCRnQ register +#define _SET_OCRnQ(T, Q, V) (*(T.OCRnQ)[Q] = int(V) & 0xFFFF) + +// Set ICRn register (one per timer) +#define _SET_ICRn(T, V) (*(T.ICRn) = int(V) & 0xFFFF) + /** - * get_pwm_timer - * Get the timer information and register of the provided pin. - * Return a Timer struct containing this information. - * Used by set_pwm_frequency, set_pwm_duty + * Return a Timer struct describing a pin's timer. */ -Timer get_pwm_timer(const pin_t pin) { +const Timer get_pwm_timer(const pin_t pin) { + uint8_t q = 0; + switch (digitalPinToTimer(pin)) { - // Protect reserved timers (TIMER0 & TIMER1) #ifdef TCCR0A - #if !AVR_AT90USB1286_FAMILY - case TIMER0A: - #endif - case TIMER0B: + IF_DISABLED(AVR_AT90USB1286_FAMILY, case TIMER0A:) #endif #ifdef TCCR1A case TIMER1A: case TIMER1B: #endif - break; - #if defined(TCCR2) || defined(TCCR2A) - #ifdef TCCR2 - case TIMER2: { - Timer timer = { - /*TCCRnQ*/ { &TCCR2, nullptr, nullptr }, - /*OCRnQ*/ { (uint16_t*)&OCR2, nullptr, nullptr }, - /*ICRn*/ nullptr, - /*n, q*/ 2, 0 - }; - } - #elif defined(TCCR2A) - #if ENABLED(USE_OCR2A_AS_TOP) - case TIMER2A: break; // protect TIMER2A - case TIMER2B: { - Timer timer = { - /*TCCRnQ*/ { &TCCR2A, &TCCR2B, nullptr }, - /*OCRnQ*/ { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, - /*ICRn*/ nullptr, - /*n, q*/ 2, 1 - }; - return timer; - } - #else - case TIMER2B: ++q; - case TIMER2A: { - Timer timer = { - /*TCCRnQ*/ { &TCCR2A, &TCCR2B, nullptr }, - /*OCRnQ*/ { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, - /*ICRn*/ nullptr, - 2, q - }; - return timer; - } - #endif - #endif + + break; // Protect reserved timers (TIMER0 & TIMER1) + + #ifdef TCCR0A + case TIMER0B: // Protected timer, but allow setting the duty cycle on OCR0B for pin D4 only + return Timer({ { &TCCR0A, nullptr, nullptr }, { (uint16_t*)&OCR0A, (uint16_t*)&OCR0B, nullptr }, nullptr, 0, 1, true, true }); + #endif + + #if HAS_TCCR2 + case TIMER2: + return Timer({ { &TCCR2, nullptr, nullptr }, { (uint16_t*)&OCR2, nullptr, nullptr }, nullptr, 2, 0, true, false }); + #elif ENABLED(USE_OCR2A_AS_TOP) + case TIMER2A: break; // Protect TIMER2A since its OCR is used by TIMER2B + case TIMER2B: + return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, 1, true, false }); + #elif defined(TCCR2A) + case TIMER2B: ++q; case TIMER2A: + return Timer({ { &TCCR2A, &TCCR2B, nullptr }, { (uint16_t*)&OCR2A, (uint16_t*)&OCR2B, nullptr }, nullptr, 2, q, true, false }); #endif + #ifdef OCR3C - case TIMER3C: ++q; - case TIMER3B: ++q; - case TIMER3A: { - Timer timer = { - /*TCCRnQ*/ { &TCCR3A, &TCCR3B, &TCCR3C }, - /*OCRnQ*/ { &OCR3A, &OCR3B, &OCR3C }, - /*ICRn*/ &ICR3, - /*n, q*/ 3, q - }; - return timer; - } + case TIMER3C: ++q; case TIMER3B: ++q; case TIMER3A: + return Timer({ { &TCCR3A, &TCCR3B, &TCCR3C }, { &OCR3A, &OCR3B, &OCR3C }, &ICR3, 3, q, true, false }); #elif defined(OCR3B) - case TIMER3B: ++q; - case TIMER3A: { - Timer timer = { - /*TCCRnQ*/ { &TCCR3A, &TCCR3B, nullptr }, - /*OCRnQ*/ { &OCR3A, &OCR3B, nullptr }, - /*ICRn*/ &ICR3, - /*n, q*/ 3, q - }; - return timer; - } + case TIMER3B: ++q; case TIMER3A: + return Timer({ { &TCCR3A, &TCCR3B, nullptr }, { &OCR3A, &OCR3B, nullptr }, &ICR3, 3, q, true, false }); #endif + #ifdef TCCR4A - case TIMER4C: ++q; - case TIMER4B: ++q; - case TIMER4A: { - Timer timer = { - /*TCCRnQ*/ { &TCCR4A, &TCCR4B, &TCCR4C }, - /*OCRnQ*/ { &OCR4A, &OCR4B, &OCR4C }, - /*ICRn*/ &ICR4, - /*n, q*/ 4, q - }; - return timer; - } + case TIMER4C: ++q; case TIMER4B: ++q; case TIMER4A: + return Timer({ { &TCCR4A, &TCCR4B, &TCCR4C }, { &OCR4A, &OCR4B, &OCR4C }, &ICR4, 4, q, true, false }); #endif + #ifdef TCCR5A - case TIMER5C: ++q; - case TIMER5B: ++q; - case TIMER5A: { - Timer timer = { - /*TCCRnQ*/ { &TCCR5A, &TCCR5B, &TCCR5C }, - /*OCRnQ*/ { &OCR5A, &OCR5B, &OCR5C }, - /*ICRn*/ &ICR5, - /*n, q*/ 5, q - }; - return timer; - } + case TIMER5C: ++q; case TIMER5B: ++q; case TIMER5A: + return Timer({ { &TCCR5A, &TCCR5B, &TCCR5C }, { &OCR5A, &OCR5B, &OCR5C }, &ICR5, 5, q, true, false }); #endif } - Timer timer = { - /*TCCRnQ*/ { nullptr, nullptr, nullptr }, - /*OCRnQ*/ { nullptr, nullptr, nullptr }, - /*ICRn*/ nullptr, - 0, 0 - }; - return timer; + + return Timer(); } -void set_pwm_frequency(const pin_t pin, int f_desired) { - Timer timer = get_pwm_timer(pin); - if (timer.n == 0) return; // Don't proceed if protected timer or not recognised - uint16_t size; - if (timer.n == 2) size = 255; else size = 65535; +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + const Timer timer = get_pwm_timer(pin); + if (timer.isProtected || !timer.isPWM) return; // Don't proceed if protected timer or not recognized + + const bool is_timer2 = timer.n == 2; + const uint16_t maxtop = is_timer2 ? 0xFF : 0xFFFF; - uint16_t res = 255; // resolution (TOP value) - uint8_t j = 0; // prescaler index - uint8_t wgm = 1; // waveform generation mode + uint16_t res = 0xFF; // resolution (TOP value) + uint8_t j = CS_NONE; // prescaler index + uint8_t wgm = WGM_PWM_PC_8; // waveform generation mode // Calculating the prescaler and resolution to use to achieve closest frequency if (f_desired != 0) { - int f = (F_CPU) / (2 * 1024 * size) + 1; // Initialize frequency as lowest (non-zero) achievable - uint16_t prescaler[] = { 0, 1, 8, /*TIMER2 ONLY*/32, 64, /*TIMER2 ONLY*/128, 256, 1024 }; - - // loop over prescaler values - LOOP_S_L_N(i, 1, 8) { - uint16_t res_temp_fast = 255, res_temp_phase_correct = 255; - if (timer.n == 2) { - // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP - #if ENABLED(USE_OCR2A_AS_TOP) - const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired); - res_temp_fast = rtf - 1; - res_temp_phase_correct = rtf / 2; + constexpr uint16_t prescaler[] = { 1, 8, (32), 64, (128), 256, 1024 }; // (*) are Timer 2 only + uint16_t f = (F_CPU) / (2 * 1024 * maxtop) + 1; // Start with the lowest non-zero frequency achievable (1 or 31) + + LOOP_L_N(i, COUNT(prescaler)) { // Loop through all prescaler values + const uint16_t p = prescaler[i]; + uint16_t res_fast_temp, res_pc_temp; + if (is_timer2) { + #if ENABLED(USE_OCR2A_AS_TOP) // No resolution calculation for TIMER2 unless enabled USE_OCR2A_AS_TOP + const uint16_t rft = (F_CPU) / (p * f_desired); + res_fast_temp = rft - 1; + res_pc_temp = rft / 2; + #else + res_fast_temp = res_pc_temp = maxtop; #endif } else { - // Skip TIMER2 specific prescalers when not TIMER2 - if (i == 3 || i == 5) continue; - const uint16_t rtf = (F_CPU) / (prescaler[i] * f_desired); - res_temp_fast = rtf - 1; - res_temp_phase_correct = rtf / 2; + if (p == 32 || p == 128) continue; // Skip TIMER2 specific prescalers when not TIMER2 + const uint16_t rft = (F_CPU) / (p * f_desired); + res_fast_temp = rft - 1; + res_pc_temp = rft / 2; } - LIMIT(res_temp_fast, 1U, size); - LIMIT(res_temp_phase_correct, 1U, size); + LIMIT(res_fast_temp, 1U, maxtop); + LIMIT(res_pc_temp, 1U, maxtop); + // Calculate frequencies of test prescaler and resolution values - const int f_temp_fast = (F_CPU) / (prescaler[i] * (1 + res_temp_fast)), - f_temp_phase_correct = (F_CPU) / (2 * prescaler[i] * res_temp_phase_correct), - f_diff = ABS(f - f_desired), - f_fast_diff = ABS(f_temp_fast - f_desired), - f_phase_diff = ABS(f_temp_phase_correct - f_desired); + const uint32_t f_diff = _MAX(f, f_desired) - _MIN(f, f_desired), + f_fast_temp = (F_CPU) / (p * (1 + res_fast_temp)), + f_fast_diff = _MAX(f_fast_temp, f_desired) - _MIN(f_fast_temp, f_desired), + f_pc_temp = (F_CPU) / (2 * p * res_pc_temp), + f_pc_diff = _MAX(f_pc_temp, f_desired) - _MIN(f_pc_temp, f_desired); - // If FAST values are closest to desired f - if (f_fast_diff < f_diff && f_fast_diff <= f_phase_diff) { - // Remember this combination - f = f_temp_fast; - res = res_temp_fast; - j = i; + if (f_fast_diff < f_diff && f_fast_diff <= f_pc_diff) { // FAST values are closest to desired f // Set the Wave Generation Mode to FAST PWM - if (timer.n == 2) { - wgm = ( - #if ENABLED(USE_OCR2A_AS_TOP) - WGM2_FAST_PWM_OCR2A - #else - WGM2_FAST_PWM - #endif - ); - } - else wgm = WGM_FAST_PWM_ICRn; + wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_FAST_PWM_OCR2A, WGM2_FAST_PWM)) : uint8_t(WGM_FAST_PWM_ICRn); + // Remember this combination + f = f_fast_temp; res = res_fast_temp; j = i + 1; } - // If PHASE CORRECT values are closes to desired f - else if (f_phase_diff < f_diff) { - f = f_temp_phase_correct; - res = res_temp_phase_correct; - j = i; + else if (f_pc_diff < f_diff) { // PHASE CORRECT values are closes to desired f // Set the Wave Generation Mode to PWM PHASE CORRECT - if (timer.n == 2) { - wgm = ( - #if ENABLED(USE_OCR2A_AS_TOP) - WGM2_PWM_PC_OCR2A - #else - WGM2_PWM_PC - #endif - ); - } - else wgm = WGM_PWM_PC_ICRn; + wgm = is_timer2 ? uint8_t(TERN(USE_OCR2A_AS_TOP, WGM2_PWM_PC_OCR2A, WGM2_PWM_PC)) : uint8_t(WGM_PWM_PC_ICRn); + f = f_pc_temp; res = res_pc_temp; j = i + 1; } } } - _SET_WGMnQ(timer.TCCRnQ, wgm); - _SET_CSn(timer.TCCRnQ, j); - if (timer.n == 2) { - #if ENABLED(USE_OCR2A_AS_TOP) - _SET_OCRnQ(timer.OCRnQ, 0, res); // Set OCR2A value (TOP) = res - #endif + _SET_WGMnQ(timer, wgm); + _SET_CSn(timer, j); + + if (is_timer2) { + TERN_(USE_OCR2A_AS_TOP, _SET_OCRnQ(timer, 0, res)); // Set OCR2A value (TOP) = res } else - _SET_ICRn(timer.ICRn, res); // Set ICRn value (TOP) = res + _SET_ICRn(timer, res); // Set ICRn value (TOP) = res } -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { // If v is 0 or v_size (max), digitalWrite to LOW or HIGH. - // Note that digitalWrite also disables pwm output for us (sets COM bit to 0) + // Note that digitalWrite also disables PWM output for us (sets COM bit to 0) if (v == 0) digitalWrite(pin, invert); else if (v == v_size) digitalWrite(pin, !invert); else { - Timer timer = get_pwm_timer(pin); - if (timer.n == 0) return; // Don't proceed if protected timer or not recognised - // Set compare output mode to CLEAR -> SET or SET -> CLEAR (if inverted) - _SET_COMnQ(timer.TCCRnQ, (timer.q - #ifdef TCCR2 - + (timer.q == 2) // COM20 is on bit 4 of TCCR2, thus requires q + 1 in the macro - #endif - ), COM_CLEAR_SET + invert - ); - - uint16_t top; - if (timer.n == 2) { // if TIMER2 - top = ( - #if ENABLED(USE_OCR2A_AS_TOP) - *timer.OCRnQ[0] // top = OCR2A - #else - 255 // top = 0xFF (max) - #endif - ); + const Timer timer = get_pwm_timer(pin); + if (timer.isPWM) { + if (timer.n == 0) { + _SET_COMnQ(timer, timer.q, COM_CLEAR_SET); // Only allow a TIMER0B select... + _SET_OCRnQ(timer, timer.q, v); // ...and OCR0B duty update. For output pin D4 no frequency changes are permitted. + } + else if (!timer.isProtected) { + const uint16_t top = timer.n == 2 ? TERN(USE_OCR2A_AS_TOP, *timer.OCRnQ[0], 255) : *timer.ICRn; + _SET_COMnQ(timer, SUM_TERN(HAS_TCCR2, timer.q, timer.q == 2), COM_CLEAR_SET + invert); // COM20 is on bit 4 of TCCR2, so +1 for q==2 + _SET_OCRnQ(timer, timer.q, uint16_t(uint32_t(v) * top / v_size)); // Scale 8/16-bit v to top value + } } else - top = *timer.ICRn; // top = ICRn - - _SET_OCRnQ(timer.OCRnQ, timer.q, v * float(top) / float(v_size)); // Scale 8/16-bit v to top value + digitalWrite(pin, v < v_size / 2 ? LOW : HIGH); } } -#endif // NEEDS_HARDWARE_PWM +void MarlinHAL::init_pwm_timers() { + // Init some timer frequencies to a default 1KHz + const pin_t pwm_pin[] = { + #ifdef __AVR_ATmega2560__ + 10, 5, 6, 46 + #elif defined(__AVR_ATmega1280__) + 12, 31 + #elif defined(__AVR_ATmega644__) || defined(__AVR_ATmega1284__) + 15, 6 + #elif defined(__AVR_AT90USB1286__) || defined(__AVR_mega64) || defined(__AVR_mega128) + 16, 24 + #endif + }; + + LOOP_L_N(i, COUNT(pwm_pin)) + set_pwm_frequency(pwm_pin[i], 1000); +} + #endif // __AVR__ diff --git a/Marlin/src/HAL/AVR/fastio.cpp b/Marlin/src/HAL/AVR/fastio.cpp index 8af3ef805efa7..5c6ef1891512c 100644 --- a/Marlin/src/HAL/AVR/fastio.cpp +++ b/Marlin/src/HAL/AVR/fastio.cpp @@ -245,7 +245,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb float count = 0; if (hz > 0 && (dca || dcb || dcc)) { count = float(F_CPU) / hz; // 1x prescaler, TOP for 16MHz base freq. - uint16_t prescaler; // Range of 30.5Hz (65535) 64.5KHz (>31) + uint16_t prescaler; // Range of 30.5Hz (65535) 64.5kHz (>31) if (count >= 255. * 256.) { prescaler = 1024; SET_CS(5, PRESCALER_1024); } else if (count >= 255. * 64.) { prescaler = 256; SET_CS(5, PRESCALER_256); } @@ -257,7 +257,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb const float pwm_top = round(count); // Get the rounded count ICR5 = (uint16_t)pwm_top - 1; // Subtract 1 for TOP - OCR5A = pwm_top * ABS(dca); // Update and scale DCs + OCR5A = pwm_top * ABS(dca); // Update and scale DCs OCR5B = pwm_top * ABS(dcb); OCR5C = pwm_top * ABS(dcc); _SET_COM(5, A, dca ? (dca < 0 ? COM_SET_CLEAR : COM_CLEAR_SET) : COM_NORMAL); // Set compare modes @@ -277,7 +277,7 @@ uint16_t set_pwm_frequency_hz(const_float_t hz, const float dca, const float dcb // Restore the default for Timer 5 SET_WGM(5, PWM_PC_8); // PWM 8-bit (Phase Correct) SET_COMS(5, NORMAL, NORMAL, NORMAL); // Do nothing - SET_CS(5, PRESCALER_64); // 16MHz / 64 = 250KHz + SET_CS(5, PRESCALER_64); // 16MHz / 64 = 250kHz OCR5A = OCR5B = OCR5C = 0; } return round(count); diff --git a/Marlin/src/HAL/AVR/fastio.h b/Marlin/src/HAL/AVR/fastio.h index f77d4f666c7db..51d3b311ee9d4 100644 --- a/Marlin/src/HAL/AVR/fastio.h +++ b/Marlin/src/HAL/AVR/fastio.h @@ -118,7 +118,7 @@ */ // Waveform Generation Modes -enum WaveGenMode : char { +enum WaveGenMode : uint8_t { WGM_NORMAL, // 0 WGM_PWM_PC_8, // 1 WGM_PWM_PC_9, // 2 @@ -138,19 +138,19 @@ enum WaveGenMode : char { }; // Wavefore Generation Modes (Timer 2 only) -enum WaveGenMode2 : char { - WGM2_NORMAL, // 0 - WGM2_PWM_PC, // 1 - WGM2_CTC_OCR2A, // 2 - WGM2_FAST_PWM, // 3 - WGM2_reserved_1, // 4 - WGM2_PWM_PC_OCR2A, // 5 - WGM2_reserved_2, // 6 - WGM2_FAST_PWM_OCR2A, // 7 +enum WaveGenMode2 : uint8_t { + WGM2_NORMAL, // 0 + WGM2_PWM_PC, // 1 + WGM2_CTC_OCR2A, // 2 + WGM2_FAST_PWM, // 3 + WGM2_reserved_1, // 4 + WGM2_PWM_PC_OCR2A, // 5 + WGM2_reserved_2, // 6 + WGM2_FAST_PWM_OCR2A, // 7 }; // Compare Modes -enum CompareMode : char { +enum CompareMode : uint8_t { COM_NORMAL, // 0 COM_TOGGLE, // 1 Non-PWM: OCnx ... Both PWM (WGM 9,11,14,15): OCnA only ... else NORMAL COM_CLEAR_SET, // 2 Non-PWM: OCnx ... Fast PWM: OCnx/Bottom ... PF-FC: OCnx Up/Down @@ -158,7 +158,7 @@ enum CompareMode : char { }; // Clock Sources -enum ClockSource : char { +enum ClockSource : uint8_t { CS_NONE, // 0 CS_PRESCALER_1, // 1 CS_PRESCALER_8, // 2 @@ -170,7 +170,7 @@ enum ClockSource : char { }; // Clock Sources (Timer 2 only) -enum ClockSource2 : char { +enum ClockSource2 : uint8_t { CS2_NONE, // 0 CS2_PRESCALER_1, // 1 CS2_PRESCALER_8, // 2 @@ -203,40 +203,33 @@ enum ClockSource2 : char { TCCR##T##B = (TCCR##T##B & ~(0x3 << WGM##T##2)) | (((int(V) >> 2) & 0x3) << WGM##T##2); \ }while(0) #define SET_WGM(T,V) _SET_WGM(T,WGM_##V) -// Runtime (see set_pwm_frequency): -#define _SET_WGMnQ(TCCRnQ, V) do{ \ - *(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << 0)) | (( int(V) & 0x3) << 0); \ - *(TCCRnQ)[1] = (*(TCCRnQ)[1] & ~(0x3 << 3)) | (((int(V) >> 2) & 0x3) << 3); \ - }while(0) // Set Clock Select bits // Ex: SET_CS3(PRESCALER_64); +#ifdef TCCR2 + #define HAS_TCCR2 1 +#endif #define _SET_CS(T,V) (TCCR##T##B = (TCCR##T##B & ~(0x7 << CS##T##0)) | ((int(V) & 0x7) << CS##T##0)) #define _SET_CS0(V) _SET_CS(0,V) #define _SET_CS1(V) _SET_CS(1,V) -#ifdef TCCR2 - #define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20)) -#else - #define _SET_CS2(V) _SET_CS(2,V) -#endif #define _SET_CS3(V) _SET_CS(3,V) #define _SET_CS4(V) _SET_CS(4,V) #define _SET_CS5(V) _SET_CS(5,V) #define SET_CS0(V) _SET_CS0(CS_##V) #define SET_CS1(V) _SET_CS1(CS_##V) -#ifdef TCCR2 + +#if HAS_TCCR2 + #define _SET_CS2(V) (TCCR2 = (TCCR2 & ~(0x7 << CS20)) | (int(V) << CS20)) #define SET_CS2(V) _SET_CS2(CS2_##V) #else + #define _SET_CS2(V) _SET_CS(2,V) #define SET_CS2(V) _SET_CS2(CS_##V) #endif + #define SET_CS3(V) _SET_CS3(CS_##V) #define SET_CS4(V) _SET_CS4(CS_##V) #define SET_CS5(V) _SET_CS5(CS_##V) #define SET_CS(T,V) SET_CS##T(V) -// Runtime (see set_pwm_frequency) -#define _SET_CSn(TCCRnQ, V) do{ \ - (*(TCCRnQ)[1] = (*(TCCRnQ[1]) & ~(0x7 << 0)) | ((int(V) & 0x7) << 0)); \ - }while(0) // Set Compare Mode bits // Ex: SET_COMS(4,CLEAR_SET,CLEAR_SET,CLEAR_SET); @@ -246,22 +239,6 @@ enum ClockSource2 : char { #define SET_COMB(T,V) SET_COM(T,B,V) #define SET_COMC(T,V) SET_COM(T,C,V) #define SET_COMS(T,V1,V2,V3) do{ SET_COMA(T,V1); SET_COMB(T,V2); SET_COMC(T,V3); }while(0) -// Runtime (see set_pwm_duty) -#define _SET_COMnQ(TCCRnQ, Q, V) do{ \ - (*(TCCRnQ)[0] = (*(TCCRnQ)[0] & ~(0x3 << (6-2*(Q)))) | (int(V) << (6-2*(Q)))); \ - }while(0) - -// Set OCRnQ register -// Runtime (see set_pwm_duty): -#define _SET_OCRnQ(OCRnQ, Q, V) do{ \ - (*(OCRnQ)[(Q)] = (0x0000) | (int(V) & 0xFFFF)); \ - }while(0) - -// Set ICRn register (one per timer) -// Runtime (see set_pwm_frequency) -#define _SET_ICRn(ICRn, V) do{ \ - (*(ICRn) = (0x0000) | (int(V) & 0xFFFF)); \ - }while(0) // Set Noise Canceler bit // Ex: SET_ICNC(2,1) diff --git a/Marlin/src/HAL/AVR/inc/SanityCheck.h b/Marlin/src/HAL/AVR/inc/SanityCheck.h index 79809b8f618d1..1c1d8d441351d 100644 --- a/Marlin/src/HAL/AVR/inc/SanityCheck.h +++ b/Marlin/src/HAL/AVR/inc/SanityCheck.h @@ -25,11 +25,43 @@ * Test AVR-specific configuration values for errors at compile-time. */ +/** + * Check for common serial pin conflicts + */ +#define CHECK_SERIAL_PIN(N) ( \ + X_STOP_PIN == N || Y_STOP_PIN == N || Z_STOP_PIN == N \ + || X_MIN_PIN == N || Y_MIN_PIN == N || Z_MIN_PIN == N \ + || X_MAX_PIN == N || Y_MAX_PIN == N || Z_MAX_PIN == N \ + || X_STEP_PIN == N || Y_STEP_PIN == N || Z_STEP_PIN == N \ + || X_DIR_PIN == N || Y_DIR_PIN == N || Z_DIR_PIN == N \ + || X_ENA_PIN == N || Y_ENA_PIN == N || Z_ENA_PIN == N \ +) +#if CONF_SERIAL_IS(0) // D0-D1. No known conflicts. +#endif +#if CONF_SERIAL_IS(1) && (CHECK_SERIAL_PIN(18) || CHECK_SERIAL_PIN(19)) + #error "Serial Port 1 pin D18 and/or D19 conflicts with another pin on the board." +#endif +#if CONF_SERIAL_IS(2) && (CHECK_SERIAL_PIN(16) || CHECK_SERIAL_PIN(17)) + #error "Serial Port 2 pin D16 and/or D17 conflicts with another pin on the board." +#endif +#if CONF_SERIAL_IS(3) && (CHECK_SERIAL_PIN(14) || CHECK_SERIAL_PIN(15)) + #error "Serial Port 3 pin D14 and/or D15 conflicts with another pin on the board." +#endif +#undef CHECK_SERIAL_PIN + /** * Checks for FAST PWM */ -#if ENABLED(FAST_PWM_FAN) && (ENABLED(USE_OCR2A_AS_TOP) && defined(TCCR2)) - #error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2" +#if ALL(FAST_PWM_FAN, USE_OCR2A_AS_TOP, HAS_TCCR2) + #error "USE_OCR2A_AS_TOP does not apply to devices with a single output TIMER2." +#endif + +/** + * Checks for SOFT PWM + */ +#if HAS_FAN0 && FAN_PIN == 9 && DISABLED(FAN_SOFT_PWM) && ENABLED(SPEAKER) + #error "FAN_PIN 9 Hardware PWM uses Timer 2 which conflicts with Arduino AVR Tone Timer (for SPEAKER)." + #error "Disable SPEAKER or enable FAN_SOFT_PWM." #endif /** @@ -42,7 +74,7 @@ #elif NUM_SERVOS > 0 && defined(_useTimer3) && (WITHIN(SPINDLE_LASER_PWM_PIN, 2, 3) || SPINDLE_LASER_PWM_PIN == 5) #error "Counter/Timer for SPINDLE_LASER_PWM_PIN is used by the servo system." #endif -#elif defined(SPINDLE_LASER_FREQUENCY) +#elif SPINDLE_LASER_FREQUENCY #error "SPINDLE_LASER_FREQUENCY requires SPINDLE_LASER_USE_PWM." #endif @@ -63,3 +95,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) #error "POSTMORTEM_DEBUGGING is not supported on AVR boards." #endif + +#if USING_PULLDOWNS + #error "PULLDOWN pin mode is not available on AVR boards." +#endif diff --git a/Marlin/src/HAL/AVR/math.h b/Marlin/src/HAL/AVR/math.h index 7ede4accc09e1..7dd1018ff1990 100644 --- a/Marlin/src/HAL/AVR/math.h +++ b/Marlin/src/HAL/AVR/math.h @@ -35,7 +35,7 @@ // C B A is longIn1 // D C B A is longIn2 // -static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) { +FORCE_INLINE static uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2) { uint8_t tmp1; uint8_t tmp2; uint16_t intRes; @@ -89,7 +89,7 @@ static FORCE_INLINE uint16_t MultiU24X32toH16(uint32_t longIn1, uint32_t longIn2 // uses: // r26 to store 0 // r27 to store the byte 1 of the 24 bit result -static FORCE_INLINE uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) { +FORCE_INLINE static uint16_t MultiU16X8toH16(uint8_t charIn1, uint16_t intIn2) { uint8_t tmp; uint16_t intRes; __asm__ __volatile__ ( diff --git a/Marlin/src/HAL/AVR/pinsDebug.h b/Marlin/src/HAL/AVR/pinsDebug.h index fcbb7af3e17e3..dab4e4471524d 100644 --- a/Marlin/src/HAL/AVR/pinsDebug.h +++ b/Marlin/src/HAL/AVR/pinsDebug.h @@ -74,7 +74,7 @@ #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) { - char *name_mem_pointer = (char*)pgm_read_ptr(&pin_array[x].name); + PGM_P const name_mem_pointer = (PGM_P)pgm_read_ptr(&pin_array[x].name); LOOP_L_N(y, MAX_NAME_LENGTH) { char temp_char = pgm_read_byte(name_mem_pointer + y); if (temp_char != 0) @@ -102,7 +102,7 @@ void PRINT_ARRAY_NAME(uint8_t x) { return true; \ } else return false - +#define ABTEST(N) defined(TCCR##N##A) && defined(COM##N##A1) /** * Print a pin's PWM status. @@ -113,7 +113,7 @@ static bool pwm_status(uint8_t pin) { switch (digitalPinToTimer_DEBUG(pin)) { - #if defined(TCCR0A) && defined(COM0A1) + #if ABTEST(0) #ifdef TIMER0A #if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs PWM_CASE(0, A); @@ -122,20 +122,20 @@ static bool pwm_status(uint8_t pin) { PWM_CASE(0, B); #endif - #if defined(TCCR1A) && defined(COM1A1) + #if ABTEST(1) PWM_CASE(1, A); PWM_CASE(1, B); - #if defined(COM1C1) && defined(TIMER1C) - PWM_CASE(1, C); - #endif + #if defined(COM1C1) && defined(TIMER1C) + PWM_CASE(1, C); + #endif #endif - #if defined(TCCR2A) && defined(COM2A1) + #if ABTEST(2) PWM_CASE(2, A); PWM_CASE(2, B); #endif - #if defined(TCCR3A) && defined(COM3A1) + #if ABTEST(3) PWM_CASE(3, A); PWM_CASE(3, B); #ifdef COM3C1 @@ -149,7 +149,7 @@ static bool pwm_status(uint8_t pin) { PWM_CASE(4, C); #endif - #if defined(TCCR5A) && defined(COM5A1) + #if ABTEST(5) PWM_CASE(5, A); PWM_CASE(5, B); PWM_CASE(5, C); @@ -166,16 +166,16 @@ static bool pwm_status(uint8_t pin) { const volatile uint8_t* const PWM_other[][3] PROGMEM = { { &TCCR0A, &TCCR0B, &TIMSK0 }, { &TCCR1A, &TCCR1B, &TIMSK1 }, - #if defined(TCCR2A) && defined(COM2A1) + #if ABTEST(2) { &TCCR2A, &TCCR2B, &TIMSK2 }, #endif - #if defined(TCCR3A) && defined(COM3A1) + #if ABTEST(3) { &TCCR3A, &TCCR3B, &TIMSK3 }, #endif #ifdef TCCR4A { &TCCR4A, &TCCR4B, &TIMSK4 }, #endif - #if defined(TCCR5A) && defined(COM5A1) + #if ABTEST(5) { &TCCR5A, &TCCR5B, &TIMSK5 }, #endif }; @@ -195,11 +195,11 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = { { (const uint8_t*)&OCR1A, (const uint8_t*)&OCR1B, 0 }, #endif - #if defined(TCCR2A) && defined(COM2A1) + #if ABTEST(2) { &OCR2A, &OCR2B, 0 }, #endif - #if defined(TCCR3A) && defined(COM3A1) + #if ABTEST(3) #ifdef COM3C1 { (const uint8_t*)&OCR3A, (const uint8_t*)&OCR3B, (const uint8_t*)&OCR3C }, #else @@ -211,7 +211,7 @@ const volatile uint8_t* const PWM_OCR[][3] PROGMEM = { { (const uint8_t*)&OCR4A, (const uint8_t*)&OCR4B, (const uint8_t*)&OCR4C }, #endif - #if defined(TCCR5A) && defined(COM5A1) + #if ABTEST(5) { (const uint8_t*)&OCR5A, (const uint8_t*)&OCR5B, (const uint8_t*)&OCR5C }, #endif }; @@ -281,7 +281,7 @@ void timer_prefix(uint8_t T, char L, uint8_t N) { // T - timer L - pwm N - static void pwm_details(uint8_t pin) { switch (digitalPinToTimer_DEBUG(pin)) { - #if defined(TCCR0A) && defined(COM0A1) + #if ABTEST(0) #ifdef TIMER0A #if !AVR_AT90USB1286_FAMILY // not available in Teensyduino type IDEs case TIMER0A: timer_prefix(0, 'A', 3); break; @@ -290,7 +290,7 @@ static void pwm_details(uint8_t pin) { case TIMER0B: timer_prefix(0, 'B', 3); break; #endif - #if defined(TCCR1A) && defined(COM1A1) + #if ABTEST(1) case TIMER1A: timer_prefix(1, 'A', 4); break; case TIMER1B: timer_prefix(1, 'B', 4); break; #if defined(COM1C1) && defined(TIMER1C) @@ -298,12 +298,12 @@ static void pwm_details(uint8_t pin) { #endif #endif - #if defined(TCCR2A) && defined(COM2A1) + #if ABTEST(2) case TIMER2A: timer_prefix(2, 'A', 3); break; case TIMER2B: timer_prefix(2, 'B', 3); break; #endif - #if defined(TCCR3A) && defined(COM3A1) + #if ABTEST(3) case TIMER3A: timer_prefix(3, 'A', 4); break; case TIMER3B: timer_prefix(3, 'B', 4); break; #ifdef COM3C1 @@ -317,7 +317,7 @@ static void pwm_details(uint8_t pin) { case TIMER4C: timer_prefix(4, 'C', 4); break; #endif - #if defined(TCCR5A) && defined(COM5A1) + #if ABTEST(5) case TIMER5A: timer_prefix(5, 'A', 4); break; case TIMER5B: timer_prefix(5, 'B', 4); break; case TIMER5C: timer_prefix(5, 'C', 4); break; @@ -351,7 +351,6 @@ static void pwm_details(uint8_t pin) { #endif } // pwm_details - #ifndef digitalRead_mod // Use Teensyduino's version of digitalRead - it doesn't disable the PWMs int digitalRead_mod(const int8_t pin) { // same as digitalRead except the PWM stop section has been removed const uint8_t port = digitalPinToPort_DEBUG(pin); @@ -397,3 +396,5 @@ static void pwm_details(uint8_t pin) { #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) + +#undef ABTEST diff --git a/Marlin/src/HAL/AVR/timers.h b/Marlin/src/HAL/AVR/timers.h index 82eb8b14b161f..33c3880b6b99f 100644 --- a/Marlin/src/HAL/AVR/timers.h +++ b/Marlin/src/HAL/AVR/timers.h @@ -34,14 +34,14 @@ typedef uint16_t hal_timer_t; #define HAL_TIMER_RATE ((F_CPU) / 8) // i.e., 2MHz or 2.5MHz -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 1 +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 1 #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 0 +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 0 #endif #define TEMP_TIMER_FREQUENCY ((F_CPU) / 64.0 / 256.0) @@ -58,13 +58,13 @@ typedef uint16_t hal_timer_t; #define DISABLE_STEPPER_DRIVER_INTERRUPT() CBI(TIMSK1, OCIE1A) #define STEPPER_ISR_ENABLED() TEST(TIMSK1, OCIE1A) -#define ENABLE_TEMPERATURE_INTERRUPT() SBI(TIMSK0, OCIE0B) -#define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0B) -#define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0B) +#define ENABLE_TEMPERATURE_INTERRUPT() SBI(TIMSK0, OCIE0A) +#define DISABLE_TEMPERATURE_INTERRUPT() CBI(TIMSK0, OCIE0A) +#define TEMPERATURE_ISR_ENABLED() TEST(TIMSK0, OCIE0A) FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) { switch (timer_num) { - case STEP_TIMER_NUM: + case MF_TIMER_STEP: // waveform generation = 0100 = CTC SET_WGM(1, CTC_OCRnA); @@ -84,10 +84,10 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) { TCNT1 = 0; break; - case TEMP_TIMER_NUM: + case MF_TIMER_TEMP: // Use timer0 for temperature measurement // Interleave temperature interrupt with millies interrupt - OCR0B = 128; + OCR0A = 128; break; } } @@ -109,12 +109,12 @@ FORCE_INLINE void HAL_timer_start(const uint8_t timer_num, const uint32_t) { * (otherwise, characters will be lost due to UART overflow). * Then: Stepper, Endstops, Temperature, and -finally- all others. */ -#define HAL_timer_isr_prologue(TIMER_NUM) -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP -/* 18 cycles maximum latency */ #ifndef HAL_STEP_TIMER_ISR +/* 18 cycles maximum latency */ #define HAL_STEP_TIMER_ISR() \ extern "C" void TIMER1_COMPA_vect() __attribute__ ((signal, naked, used, externally_visible)); \ extern "C" void TIMER1_COMPA_vect_bottom() asm ("TIMER1_COMPA_vect_bottom") __attribute__ ((used, externally_visible, noinline)); \ @@ -180,7 +180,7 @@ void TIMER1_COMPA_vect() { \ : \ : [timsk0] "i" ((uint16_t)&TIMSK0), \ [timsk1] "i" ((uint16_t)&TIMSK1), \ - [msk0] "M" ((uint8_t)(1<. - * - */ -#ifdef __AVR__ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#include "../../MarlinCore.h" - -// Initialize watchdog with 8s timeout, if possible. Otherwise, make it 4s. -void watchdog_init() { - #if ENABLED(WATCHDOG_DURATION_8S) && defined(WDTO_8S) - #define WDTO_NS WDTO_8S - #else - #define WDTO_NS WDTO_4S - #endif - #if ENABLED(WATCHDOG_RESET_MANUAL) - // Enable the watchdog timer, but only for the interrupt. - // Take care, as this requires the correct order of operation, with interrupts disabled. - // See the datasheet of any AVR chip for details. - wdt_reset(); - cli(); - _WD_CONTROL_REG = _BV(_WD_CHANGE_BIT) | _BV(WDE); - _WD_CONTROL_REG = _BV(WDIE) | (WDTO_NS & 0x07) | ((WDTO_NS & 0x08) << 2); // WDTO_NS directly does not work. bit 0-2 are consecutive in the register but the highest value bit is at bit 5 - // So worked for up to WDTO_2S - sei(); - wdt_reset(); - #else - wdt_enable(WDTO_NS); // The function handles the upper bit correct. - #endif - //delay(10000); // test it! -} - -//=========================================================================== -//=================================== ISR =================================== -//=========================================================================== - -// Watchdog timer interrupt, called if main program blocks >4sec and manual reset is enabled. -#if ENABLED(WATCHDOG_RESET_MANUAL) - ISR(WDT_vect) { - sei(); // With the interrupt driven serial we need to allow interrupts. - SERIAL_ERROR_MSG(STR_WATCHDOG_FIRED); - minkill(); // interrupt-safe final kill and infinite loop - } -#endif - -#endif // USE_WATCHDOG -#endif // __AVR__ diff --git a/Marlin/src/HAL/DUE/HAL.cpp b/Marlin/src/HAL/DUE/HAL.cpp index a3985652e71dd..4353f1649732a 100644 --- a/Marlin/src/HAL/DUE/HAL.cpp +++ b/Marlin/src/HAL/DUE/HAL.cpp @@ -25,7 +25,7 @@ #ifdef ARDUINO_ARCH_SAM #include "../../inc/MarlinConfig.h" -#include "HAL.h" +#include "../../MarlinCore.h" #include #include "usb/usb_task.h" @@ -34,39 +34,33 @@ // Public Variables // ------------------------ -uint16_t HAL_adc_result; +uint16_t MarlinHAL::adc_result; // ------------------------ // Public functions // ------------------------ -TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); +#if ENABLED(POSTMORTEM_DEBUGGING) + extern void install_min_serial(); +#endif -// HAL initialization task -void HAL_init() { - // Initialize the USB stack +void MarlinHAL::init() { #if ENABLED(SDSUPPORT) OUT_WRITE(SDSS, HIGH); // Try to set SDSS inactive before any other SPI users start up #endif - usb_task_init(); + usb_task_init(); // Initialize the USB stack TERN_(POSTMORTEM_DEBUGGING, install_min_serial()); // Install the min serial handler } -// HAL idle task -void HAL_idletask() { - // Perform USB stack housekeeping - usb_task_idle(); +void MarlinHAL::init_board() { + #ifdef BOARD_INIT + BOARD_INIT(); + #endif } -// Disable interrupts -void cli() { noInterrupts(); } - -// Enable interrupts -void sei() { interrupts(); } +void MarlinHAL::idletask() { usb_task_idle(); } // Perform USB stack housekeeping -void HAL_clear_reset_source() { } - -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { switch ((RSTC->RSTC_SR >> 8) & 0x07) { case 0: return RST_POWER_ON; case 1: return RST_BACKUP; @@ -77,13 +71,105 @@ uint8_t HAL_get_reset_source() { } } -void HAL_reboot() { rstc_start_software_reset(RSTC); } +void MarlinHAL::reboot() { rstc_start_software_reset(RSTC); } + +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + // Initialize watchdog - On SAM3X, Watchdog was already configured + // and enabled or disabled at startup, so no need to reconfigure it + // here. + void MarlinHAL::watchdog_init() { WDT_Restart(WDT); } // Reset watchdog to start clean + + // Reset watchdog. MUST be called at least every 4 seconds after the + // first watchdog_init or AVR will go into emergency procedures. + void MarlinHAL::watchdog_refresh() { watchdogReset(); } + +#endif + +// Override Arduino runtime to either config or disable the watchdog +// +// We need to configure the watchdog as soon as possible in the boot +// process, because watchdog initialization at hardware reset on SAM3X8E +// is unreliable, and there is risk of unintended resets if we delay +// that initialization to a later time. +void watchdogSetup() { + + #if ENABLED(USE_WATCHDOG) + + // 4 seconds timeout + uint32_t timeout = TERN(WATCHDOG_DURATION_8S, 8000, 4000); + + // Calculate timeout value in WDT counter ticks: This assumes + // the slow clock is running at 32.768 kHz watchdog + // frequency is therefore 32768 / 128 = 256 Hz + timeout = (timeout << 8) / 1000; + if (timeout == 0) + timeout = 1; + else if (timeout > 0xFFF) + 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. + + #if ENABLED(WATCHDOG_RESET_MANUAL) + // We enable the watchdog timer, but only for the interrupt. + + // Configure WDT to only trigger an interrupt + value |= WDT_MR_WDFIEN; // Enable WDT fault interrupt. -void _delay_ms(const int delay_ms) { - // Todo: port for Due? - delay(delay_ms); + // Disable WDT interrupt (just in case, to avoid triggering it!) + NVIC_DisableIRQ(WDT_IRQn); + + // We NEED memory barriers to ensure Interrupts are actually disabled! + // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) + __DSB(); + __ISB(); + + // Initialize WDT with the given parameters + WDT_Enable(WDT, value); + + // Configure and enable WDT interrupt. + NVIC_ClearPendingIRQ(WDT_IRQn); + NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups + NVIC_EnableIRQ(WDT_IRQn); + + #else + + // a WDT fault triggers a reset + value |= WDT_MR_WDRSTEN; + + // Initialize WDT with the given parameters + WDT_Enable(WDT, value); + + #endif + + // Reset the watchdog + WDT_Restart(WDT); + + #else + + // Make sure to completely disable the Watchdog + WDT_Disable(WDT); + + #endif } +// ------------------------ +// Free Memory Accessor +// ------------------------ + extern "C" { extern unsigned int _ebss; // end of bss section } @@ -95,18 +181,9 @@ int freeMemory() { } // ------------------------ -// ADC +// Serial Ports // ------------------------ -void HAL_adc_start_conversion(const uint8_t ch) { - HAL_adc_result = analogRead(ch); -} - -uint16_t HAL_adc_get_result() { - // nop - return HAL_adc_result; -} - // Forward the default serial ports #if USING_HW_SERIAL0 DefaultSerial1 MSerial0(false, Serial); diff --git a/Marlin/src/HAL/DUE/HAL.h b/Marlin/src/HAL/DUE/HAL.h index 92e26bcf43626..4d3f4823a5177 100644 --- a/Marlin/src/HAL/DUE/HAL.h +++ b/Marlin/src/HAL/DUE/HAL.h @@ -32,12 +32,15 @@ #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include #include "../../core/serial_hook.h" +// ------------------------ +// Serial ports +// ------------------------ + typedef ForwardSerial1Class< decltype(Serial) > DefaultSerial1; typedef ForwardSerial1Class< decltype(Serial1) > DefaultSerial2; typedef ForwardSerial1Class< decltype(Serial2) > DefaultSerial3; @@ -97,55 +100,38 @@ extern DefaultSerial4 MSerial3; #include "MarlinSerial.h" #include "MarlinSerialUSB.h" -// On AVR this is in math.h? -#define square(x) ((x)*(x)) +// ------------------------ +// Types +// ------------------------ typedef int8_t pin_t; -#define SHARED_SERVOS HAS_SERVOS -#define HAL_SERVO_LIB Servo +#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +class Servo; +typedef Servo hal_servo_t; // // Interrupts // -#define CRITICAL_SECTION_START() uint32_t primask = __get_PRIMASK(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_PRIMASK()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() - -void cli(); // Disable interrupts -void sei(); // Enable interrupts +#define sei() interrupts() +#define cli() noInterrupts() -void HAL_clear_reset_source(); // clear reset reason -uint8_t HAL_get_reset_source(); // get reset reason - -void HAL_reboot(); +#define CRITICAL_SECTION_START() const bool _irqon = hal.isr_state(); hal.isr_off() +#define CRITICAL_SECTION_END() if (_irqon) hal.isr_on() // // ADC // -extern uint16_t HAL_adc_result; // result of last ADC conversion +#define HAL_ADC_VREF 3.3 +#define HAL_ADC_RESOLUTION 10 #ifndef analogInputToDigitalPin #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) #endif -#define HAL_ANALOG_SELECT(ch) - -inline void HAL_adc_init() {}//todo - -#define HAL_ADC_VREF 3.3 -#define HAL_ADC_RESOLUTION 10 -#define HAL_START_ADC(ch) HAL_adc_start_conversion(ch) -#define HAL_READ_ADC() HAL_adc_result -#define HAL_ADC_READY() true - -void HAL_adc_start_conversion(const uint8_t ch); -uint16_t HAL_adc_get_result(); - // -// Pin Map +// Pin Mapping for M42, M43, M226 // #define GET_PIN_MAP_PIN(index) index #define GET_PIN_MAP_INDEX(pin) pin @@ -154,30 +140,19 @@ uint16_t HAL_adc_get_result(); // // Tone // -void toneInit(); void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0); void noTone(const pin_t _pin); -// Enable hooks into idle and setup for HAL -#define HAL_IDLETASK 1 -void HAL_idletask(); -void HAL_init(); - -// -// Utility functions -// -void _delay_ms(const int delay); +// ------------------------ +// Class Utilities +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif -int freeMemory(); - -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop #ifdef __cplusplus extern "C" { @@ -186,3 +161,73 @@ char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s #ifdef __cplusplus } #endif + +// Return free RAM between end of heap (or end bss) and whatever is current +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 + + // 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(); + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static uint16_t adc_result; + + // 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 uint8_t ch) {} + + // Begin ADC sampling on the given channel. Called from Temperature::isr! + static void adc_start(const uint8_t ch) { adc_result = analogRead(ch); } + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The 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. + * No inverting the duty cycle in this HAL. + * No changing the maximum size of the provided value to enable finer PWM duty control in this HAL. + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { + analogWrite(pin, v); + } + +}; diff --git a/Marlin/src/HAL/DUE/HAL_SPI.cpp b/Marlin/src/HAL/DUE/HAL_SPI.cpp index c5e8f2433d7d3..7e3fe01356458 100644 --- a/Marlin/src/HAL/DUE/HAL_SPI.cpp +++ b/Marlin/src/HAL/DUE/HAL_SPI.cpp @@ -31,8 +31,6 @@ /** * HAL for Arduino Due and compatible (SAM3X8E) - * - * For ARDUINO_ARCH_SAM */ #ifdef ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/DUE/MarlinSerial.cpp b/Marlin/src/HAL/DUE/MarlinSerial.cpp index fe62ff5607d51..638f7a100722d 100644 --- a/Marlin/src/HAL/DUE/MarlinSerial.cpp +++ b/Marlin/src/HAL/DUE/MarlinSerial.cpp @@ -406,7 +406,7 @@ size_t MarlinSerial::write(const uint8_t c) { const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1); // If global interrupts are disabled (as the result of being called from an ISR)... - if (!ISRS_ENABLED()) { + if (!hal.isr_state()) { // Make room by polling if it is possible to transmit, and do so! while (i == tx_buffer.tail) { @@ -454,7 +454,7 @@ void MarlinSerial::flushTX() { if (!_written) return; // If global interrupts are disabled (as the result of being called from an ISR)... - if (!ISRS_ENABLED()) { + if (!hal.isr_state()) { // Wait until everything was transmitted - We must do polling, as interrupts are disabled while (tx_buffer.head != tx_buffer.tail || !(HWUART->UART_SR & UART_SR_TXEMPTY)) { diff --git a/Marlin/src/HAL/DUE/MarlinSerial.h b/Marlin/src/HAL/DUE/MarlinSerial.h index 4a62e2834f7f4..5a61bffee0da0 100644 --- a/Marlin/src/HAL/DUE/MarlinSerial.h +++ b/Marlin/src/HAL/DUE/MarlinSerial.h @@ -118,7 +118,7 @@ class MarlinSerial { static size_t write(const uint8_t c); static void flushTX(); - static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } + static bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; } FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; } diff --git a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp index 67c597da80c42..8de2dc7924a0d 100644 --- a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp +++ b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp @@ -41,7 +41,7 @@ extern "C" { int udi_cdc_getc(); bool udi_cdc_is_tx_ready(); int udi_cdc_putc(int value); -}; +} // Pending character static int pending_char = -1; diff --git a/Marlin/src/HAL/DUE/HAL_MinSerial.cpp b/Marlin/src/HAL/DUE/MinSerial.cpp similarity index 98% rename from Marlin/src/HAL/DUE/HAL_MinSerial.cpp rename to Marlin/src/HAL/DUE/MinSerial.cpp index 93c4ed67d63c2..e5b3dbfe6f36e 100644 --- a/Marlin/src/HAL/DUE/HAL_MinSerial.cpp +++ b/Marlin/src/HAL/DUE/MinSerial.cpp @@ -25,7 +25,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) -#include "../shared/HAL_MinSerial.h" +#include "../shared/MinSerial.h" #include diff --git a/Marlin/src/HAL/DUE/Servo.cpp b/Marlin/src/HAL/DUE/Servo.cpp index 5524aa9cef47a..2dab88238dd6d 100644 --- a/Marlin/src/HAL/DUE/Servo.cpp +++ b/Marlin/src/HAL/DUE/Servo.cpp @@ -47,12 +47,12 @@ #include "../shared/servo.h" #include "../shared/servo_private.h" -static volatile int8_t Channel[_Nbr_16timers]; // counter for the servo being pulsed for each timer (or -1 if refresh interval) +static Flags<_Nbr_16timers> DisablePending; // ISR should disable the timer at the next timer reset // ------------------------ /// Interrupt handler for the TC0 channel 1. // ------------------------ -void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel); +void Servo_Handler(const timer16_Sequence_t, Tc*, const uint8_t); #ifdef _useTimer1 void HANDLER_FOR_TIMER1() { Servo_Handler(_timer1, TC_FOR_TIMER1, CHANNEL_FOR_TIMER1); } @@ -70,88 +70,92 @@ void Servo_Handler(timer16_Sequence_t timer, Tc *pTc, uint8_t channel); void HANDLER_FOR_TIMER5() { Servo_Handler(_timer5, TC_FOR_TIMER5, CHANNEL_FOR_TIMER5); } #endif -void Servo_Handler(timer16_Sequence_t timer, Tc *tc, uint8_t channel) { - // clear interrupt - tc->TC_CHANNEL[channel].TC_SR; - if (Channel[timer] < 0) - tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG; // channel set to -1 indicated that refresh interval completed so reset the timer - else if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && SERVO(timer, Channel[timer]).Pin.isActive) - extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, LOW); // pulse this channel low if activated - - Channel[timer]++; // increment to the next channel - if (SERVO_INDEX(timer, Channel[timer]) < ServoCount && Channel[timer] < SERVOS_PER_TIMER) { - tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer,Channel[timer]).ticks; - if (SERVO(timer,Channel[timer]).Pin.isActive) // check if activated - extDigitalWrite(SERVO(timer, Channel[timer]).Pin.nbr, HIGH); // its an active channel so pulse it high +void Servo_Handler(const timer16_Sequence_t timer, Tc *tc, const uint8_t channel) { + static int8_t Channel[_Nbr_16timers]; // Servo counters to pulse (or -1 for refresh interval) + int8_t cho = Channel[timer]; // Handle the prior Channel[timer] first + if (cho < 0) { // Channel -1 indicates the refresh interval completed... + tc->TC_CHANNEL[channel].TC_CCR |= TC_CCR_SWTRG; // ...so reset the timer + if (DisablePending[timer]) { + // Disabling only after the full servo period expires prevents + // pulses being too close together if immediately re-enabled. + DisablePending.clear(timer); + TC_Stop(tc, channel); + tc->TC_CHANNEL[channel].TC_SR; // clear interrupt + return; + } + } + else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled? + extDigitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW + + Channel[timer] = ++cho; // go to the next channel (or 0) + if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) { + tc->TC_CHANNEL[channel].TC_RA = tc->TC_CHANNEL[channel].TC_CV + SERVO(timer, cho).ticks; + if (SERVO(timer, cho).Pin.isActive) // activated? + extDigitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH } else { // finished all channels so wait for the refresh period to expire before starting over - tc->TC_CHANNEL[channel].TC_RA = - tc->TC_CHANNEL[channel].TC_CV < usToTicks(REFRESH_INTERVAL) - 4 - ? (unsigned int)usToTicks(REFRESH_INTERVAL) // allow a few ticks to ensure the next OCR1A not missed - : tc->TC_CHANNEL[channel].TC_CV + 4; // at least REFRESH_INTERVAL has elapsed - Channel[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel + const unsigned int cval = tc->TC_CHANNEL[channel].TC_CV + 128 / (SERVO_TIMER_PRESCALER), // allow 128 cycles to ensure the next CV not missed + ival = (unsigned int)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed + tc->TC_CHANNEL[channel].TC_RA = max(cval, ival); + + Channel[timer] = -1; // reset the timer CCR on the next call } + + tc->TC_CHANNEL[channel].TC_SR; // clear interrupt } static void _initISR(Tc *tc, uint32_t channel, uint32_t id, IRQn_Type irqn) { pmc_enable_periph_clk(id); TC_Configure(tc, channel, - TC_CMR_TCCLKS_TIMER_CLOCK3 | // MCK/32 - TC_CMR_WAVE | // Waveform mode - TC_CMR_WAVSEL_UP_RC ); // Counter running up and reset when equals to RC - - /* 84MHz, MCK/32, for 1.5ms: 3937 */ - TC_SetRA(tc, channel, 2625); // 1ms - - /* Configure and enable interrupt */ + TC_CMR_WAVE // Waveform mode + | TC_CMR_WAVSEL_UP_RC // Counter running up and reset when equal to RC + | (SERVO_TIMER_PRESCALER == 2 ? TC_CMR_TCCLKS_TIMER_CLOCK1 : 0) // MCK/2 + | (SERVO_TIMER_PRESCALER == 8 ? TC_CMR_TCCLKS_TIMER_CLOCK2 : 0) // MCK/8 + | (SERVO_TIMER_PRESCALER == 32 ? TC_CMR_TCCLKS_TIMER_CLOCK3 : 0) // MCK/32 + | (SERVO_TIMER_PRESCALER == 128 ? TC_CMR_TCCLKS_TIMER_CLOCK4 : 0) // MCK/128 + ); + + // Wait 1ms before the first ISR + TC_SetRA(tc, channel, (F_CPU) / (SERVO_TIMER_PRESCALER) / 1000UL); // 1ms + + // Configure and enable interrupt NVIC_EnableIRQ(irqn); - // TC_IER_CPAS: RA Compare - tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS; + tc->TC_CHANNEL[channel].TC_IER = TC_IER_CPAS; // TC_IER_CPAS: RA Compare // Enables the timer clock and performs a software reset to start the counting TC_Start(tc, channel); } -void initISR(timer16_Sequence_t timer) { - #ifdef _useTimer1 - if (timer == _timer1) - _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1); - #endif - #ifdef _useTimer2 - if (timer == _timer2) - _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2); - #endif - #ifdef _useTimer3 - if (timer == _timer3) - _initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3); - #endif - #ifdef _useTimer4 - if (timer == _timer4) - _initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4); - #endif - #ifdef _useTimer5 - if (timer == _timer5) - _initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5); - #endif +void initISR(const timer16_Sequence_t timer_index) { + CRITICAL_SECTION_START(); + const bool disable_soon = DisablePending[timer_index]; + DisablePending.clear(timer_index); + CRITICAL_SECTION_END(); + + if (!disable_soon) switch (timer_index) { + default: break; + #ifdef _useTimer1 + case _timer1: return _initISR(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1, ID_TC_FOR_TIMER1, IRQn_FOR_TIMER1); + #endif + #ifdef _useTimer2 + case _timer2: return _initISR(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2, ID_TC_FOR_TIMER2, IRQn_FOR_TIMER2); + #endif + #ifdef _useTimer3 + case _timer3: return _initISR(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3, ID_TC_FOR_TIMER3, IRQn_FOR_TIMER3); + #endif + #ifdef _useTimer4 + case _timer4: return _initISR(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4, ID_TC_FOR_TIMER4, IRQn_FOR_TIMER4); + #endif + #ifdef _useTimer5 + case _timer5: return _initISR(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5, ID_TC_FOR_TIMER5, IRQn_FOR_TIMER5); + #endif + } } -void finISR(timer16_Sequence_t) { - #ifdef _useTimer1 - TC_Stop(TC_FOR_TIMER1, CHANNEL_FOR_TIMER1); - #endif - #ifdef _useTimer2 - TC_Stop(TC_FOR_TIMER2, CHANNEL_FOR_TIMER2); - #endif - #ifdef _useTimer3 - TC_Stop(TC_FOR_TIMER3, CHANNEL_FOR_TIMER3); - #endif - #ifdef _useTimer4 - TC_Stop(TC_FOR_TIMER4, CHANNEL_FOR_TIMER4); - #endif - #ifdef _useTimer5 - TC_Stop(TC_FOR_TIMER5, CHANNEL_FOR_TIMER5); - #endif +void finISR(const timer16_Sequence_t timer_index) { + // Timer is disabled from the ISR, to ensure proper final pulse length. + DisablePending.set(timer_index); } #endif // HAS_SERVOS diff --git a/Marlin/src/HAL/DUE/ServoTimers.h b/Marlin/src/HAL/DUE/ServoTimers.h index c32c938253995..95bd404c80618 100644 --- a/Marlin/src/HAL/DUE/ServoTimers.h +++ b/Marlin/src/HAL/DUE/ServoTimers.h @@ -37,7 +37,7 @@ #define _useTimer5 #define TRIM_DURATION 2 // compensation ticks to trim adjust for digitalWrite delays -#define SERVO_TIMER_PRESCALER 32 // timer prescaler +#define SERVO_TIMER_PRESCALER 2 // timer prescaler /* TC0, chan 0 => TC0_Handler diff --git a/Marlin/src/HAL/DUE/Tone.cpp b/Marlin/src/HAL/DUE/Tone.cpp index 9beb6022237f0..4bc8142aba272 100644 --- a/Marlin/src/HAL/DUE/Tone.cpp +++ b/Marlin/src/HAL/DUE/Tone.cpp @@ -35,20 +35,20 @@ static pin_t tone_pin; volatile static int32_t toggles; -void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration) { +void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration/*=0*/) { tone_pin = _pin; toggles = 2 * frequency * duration / 1000; - HAL_timer_start(TONE_TIMER_NUM, 2 * frequency); + HAL_timer_start(MF_TIMER_TONE, 2 * frequency); } void noTone(const pin_t _pin) { - HAL_timer_disable_interrupt(TONE_TIMER_NUM); + HAL_timer_disable_interrupt(MF_TIMER_TONE); extDigitalWrite(_pin, LOW); } HAL_TONE_TIMER_ISR() { static uint8_t pin_state = 0; - HAL_timer_isr_prologue(TONE_TIMER_NUM); + HAL_timer_isr_prologue(MF_TIMER_TONE); if (toggles) { toggles--; diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp index 65bfd4f4e2d4a..8268cf307e083 100644 --- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp +++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_st7920_sw_spi.cpp @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if ENABLED(U8GLIB_ST7920) +#if IS_U8GLIB_ST7920 #include "../../../inc/MarlinConfig.h" #include "../../shared/Delay.h" @@ -182,5 +182,5 @@ uint8_t u8g_com_HAL_DUE_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_va } #endif // LIGHTWEIGHT_UI -#endif // U8GLIB_ST7920 +#endif // IS_U8GLIB_ST7920 #endif // ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp index 2b13c182d023a..68e3e74a45a0a 100644 --- a/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp +++ b/Marlin/src/HAL/DUE/dogm/u8g_com_HAL_DUE_sw_spi.cpp @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if HAS_MARLINUI_U8GLIB && DISABLED(U8GLIB_ST7920) +#if HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 #include "u8g_com_HAL_DUE_sw_spi_shared.h" @@ -141,5 +141,5 @@ uint8_t u8g_com_HAL_DUE_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void return 1; } -#endif // HAS_MARLINUI_U8GLIB && !U8GLIB_ST7920 +#endif // HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 #endif // ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/DUE/eeprom_flash.cpp b/Marlin/src/HAL/DUE/eeprom_flash.cpp index 7ce4a84df531c..607764155b0ac 100644 --- a/Marlin/src/HAL/DUE/eeprom_flash.cpp +++ b/Marlin/src/HAL/DUE/eeprom_flash.cpp @@ -199,8 +199,7 @@ static bool ee_PageWrite(uint16_t page, const void *data) { for (i = 0; i > 2; i++) pageContents[i] = (((uint32_t*)data)[i]) | (~(pageContents[i] ^ ((uint32_t*)data)[i])); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM PageWrite ", page); + DEBUG_ECHO_MSG("EEPROM PageWrite ", page); DEBUG_ECHOLNPGM(" in FLASH address ", (uint32_t)addrflash); DEBUG_ECHOLNPGM(" base address ", (uint32_t)getFlashStorage(0)); DEBUG_FLUSH(); @@ -245,8 +244,7 @@ static bool ee_PageWrite(uint16_t page, const void *data) { // Reenable interrupts __enable_irq(); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Unlock failure for page ", page); + DEBUG_ECHO_MSG("EEPROM Unlock failure for page ", page); return false; } @@ -270,8 +268,7 @@ static bool ee_PageWrite(uint16_t page, const void *data) { // Reenable interrupts __enable_irq(); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Write failure for page ", page); + DEBUG_ECHO_MSG("EEPROM Write failure for page ", page); return false; } @@ -286,8 +283,7 @@ static bool ee_PageWrite(uint16_t page, const void *data) { if (memcmp(getFlashStorage(page),data,PageSize)) { #ifdef EE_EMU_DEBUG - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Verify Write failure for page ", page); + DEBUG_ECHO_MSG("EEPROM Verify Write failure for page ", page); ee_Dump( page, (uint32_t *)addrflash); ee_Dump(-page, data); @@ -325,8 +321,7 @@ static bool ee_PageErase(uint16_t page) { uint16_t i; uint32_t addrflash = uint32_t(getFlashStorage(page)); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM PageErase ", page); + DEBUG_ECHO_MSG("EEPROM PageErase ", page); DEBUG_ECHOLNPGM(" in FLASH address ", (uint32_t)addrflash); DEBUG_ECHOLNPGM(" base address ", (uint32_t)getFlashStorage(0)); DEBUG_FLUSH(); @@ -370,8 +365,7 @@ static bool ee_PageErase(uint16_t page) { // Reenable interrupts __enable_irq(); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Unlock failure for page ",page); + DEBUG_ECHO_MSG("EEPROM Unlock failure for page ",page); return false; } @@ -394,8 +388,7 @@ static bool ee_PageErase(uint16_t page) { // Reenable interrupts __enable_irq(); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Erase failure for page ",page); + DEBUG_ECHO_MSG("EEPROM Erase failure for page ",page); return false; } @@ -410,8 +403,7 @@ static bool ee_PageErase(uint16_t page) { uint32_t * aligned_src = (uint32_t *) addrflash; for (i = 0; i < PageSize >> 2; i++) { if (*aligned_src++ != 0xFFFFFFFF) { - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Verify Erase failure for page ",page); + DEBUG_ECHO_MSG("EEPROM Verify Erase failure for page ",page); ee_Dump(page, (uint32_t *)addrflash); return false; } @@ -921,8 +913,7 @@ static void ee_Init() { // If all groups seem to be used, default to first group if (curGroup >= GroupCount) curGroup = 0; - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Current Group: ",curGroup); + DEBUG_ECHO_MSG("EEPROM Current Group: ",curGroup); DEBUG_FLUSH(); // Now, validate that all the other group pages are empty @@ -931,8 +922,7 @@ static void ee_Init() { for (int page = 0; page < PagesPerGroup; page++) { if (!ee_IsPageClean(grp * PagesPerGroup + page)) { - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Page ", page, " not clean on group ", grp); + DEBUG_ECHO_MSG("EEPROM Page ", page, " not clean on group ", grp); DEBUG_FLUSH(); ee_PageErase(grp * PagesPerGroup + page); } @@ -948,15 +938,13 @@ static void ee_Init() { } } - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Active page: ", curPage); + DEBUG_ECHO_MSG("EEPROM Active page: ", curPage); DEBUG_FLUSH(); // Make sure the pages following the first clean one are also clean for (int page = curPage + 1; page < PagesPerGroup; page++) { if (!ee_IsPageClean(curGroup * PagesPerGroup + page)) { - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("EEPROM Page ", page, " not clean on active group ", curGroup); + DEBUG_ECHO_MSG("EEPROM Page ", page, " not clean on active group ", curGroup); DEBUG_FLUSH(); ee_Dump(curGroup * PagesPerGroup + page, getFlashStorage(curGroup * PagesPerGroup + page)); ee_PageErase(curGroup * PagesPerGroup + page); diff --git a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp index d9fbabce21488..800915ff692be 100644 --- a/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp +++ b/Marlin/src/HAL/DUE/fastio/G2_PWM.cpp @@ -25,7 +25,7 @@ * is NOT used to directly toggle pins. The ISR writes to the pin assigned to * that interrupt. * - * All PWMs use the same repetition rate. The G2 needs about 10KHz min in order to + * All PWMs use the same repetition rate. The G2 needs about 10kHz min in order to * not have obvious ripple on the Vref signals. * * The data structures are setup to minimize the computation done by the ISR which diff --git a/Marlin/src/HAL/DUE/inc/SanityCheck.h b/Marlin/src/HAL/DUE/inc/SanityCheck.h index 87b09cf292572..13484f7029d10 100644 --- a/Marlin/src/HAL/DUE/inc/SanityCheck.h +++ b/Marlin/src/HAL/DUE/inc/SanityCheck.h @@ -25,6 +25,30 @@ * Test Arduino Due specific configuration values for errors at compile-time. */ +/** + * Check for common serial pin conflicts + */ +#define CHECK_SERIAL_PIN(N) ( \ + X_STOP_PIN == N || Y_STOP_PIN == N || Z_STOP_PIN == N \ + || X_MIN_PIN == N || Y_MIN_PIN == N || Z_MIN_PIN == N \ + || X_MAX_PIN == N || Y_MAX_PIN == N || Z_MAX_PIN == N \ + || X_STEP_PIN == N || Y_STEP_PIN == N || Z_STEP_PIN == N \ + || X_DIR_PIN == N || Y_DIR_PIN == N || Z_DIR_PIN == N \ + || X_ENA_PIN == N || Y_ENA_PIN == N || Z_ENA_PIN == N \ +) +#if CONF_SERIAL_IS(0) // D0-D1. No known conflicts. +#endif +#if CONF_SERIAL_IS(1) && (CHECK_SERIAL_PIN(18) || CHECK_SERIAL_PIN(19)) + #error "Serial Port 1 pin D18 and/or D19 conflicts with another pin on the board." +#endif +#if CONF_SERIAL_IS(2) && (CHECK_SERIAL_PIN(16) || CHECK_SERIAL_PIN(17)) + #error "Serial Port 2 pin D16 and/or D17 conflicts with another pin on the board." +#endif +#if CONF_SERIAL_IS(3) && (CHECK_SERIAL_PIN(14) || CHECK_SERIAL_PIN(15)) + #error "Serial Port 3 pin D14 and/or D15 conflicts with another pin on the board." +#endif +#undef CHECK_SERIAL_PIN + /** * HARDWARE VS. SOFTWARE SPI COMPATIBILITY * @@ -59,3 +83,7 @@ #if HAS_TMC_SW_SERIAL #error "TMC220x Software Serial is not supported on the DUE platform." #endif + +#if USING_PULLDOWNS + #error "PULLDOWN pin mode is not available on DUE boards." +#endif diff --git a/Marlin/src/HAL/DUE/pinsDebug.h b/Marlin/src/HAL/DUE/pinsDebug.h index f01c53c8ce067..df1ba415e9180 100644 --- a/Marlin/src/HAL/DUE/pinsDebug.h +++ b/Marlin/src/HAL/DUE/pinsDebug.h @@ -53,7 +53,7 @@ * The net result is that both the g_pinStatus[pin] array and the PIO_OSR register * needs to be looked at when determining if a pin is an input or an output. * - * b) Due has only pins 6, 7, 8 & 9 enabled for PWMs. FYI - they run at 1KHz + * b) Due has only pins 6, 7, 8 & 9 enabled for PWMs. FYI - they run at 1kHz * * c) NUM_DIGITAL_PINS does not include the analog pins * @@ -86,7 +86,6 @@ bool GET_PINMODE(int8_t pin) { // 1: output, 0: input || pwm_status(pin)); } - void pwm_details(int32_t pin) { if (pwm_status(pin)) { uint32_t chan = g_APinDescription[pin].ulPWMChannel; diff --git a/Marlin/src/HAL/DUE/timers.cpp b/Marlin/src/HAL/DUE/timers.cpp index 65073c510d7c4..e5647817b6f0c 100644 --- a/Marlin/src/HAL/DUE/timers.cpp +++ b/Marlin/src/HAL/DUE/timers.cpp @@ -42,7 +42,7 @@ // Private Variables // ------------------------ -const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = { +const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = { { TC0, 0, TC0_IRQn, 3}, // 0 - [servo timer5] { TC0, 1, TC1_IRQn, 0}, // 1 { TC0, 2, TC2_IRQn, 2}, // 2 - stepper @@ -66,9 +66,9 @@ const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = { */ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { - Tc *tc = TimerConfig[timer_num].pTimerRegs; - IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; - uint32_t channel = TimerConfig[timer_num].channel; + Tc *tc = timer_config[timer_num].pTimerRegs; + IRQn_Type irq = timer_config[timer_num].IRQ_Id; + uint32_t channel = timer_config[timer_num].channel; // Disable interrupt, just in case it was already enabled NVIC_DisableIRQ(irq); @@ -86,13 +86,20 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { pmc_set_writeprotect(false); pmc_enable_periph_clk((uint32_t)irq); - NVIC_SetPriority(irq, TimerConfig [timer_num].priority); + NVIC_SetPriority(irq, timer_config[timer_num].priority); // wave mode, reset counter on match with RC, - TC_Configure(tc, channel, TC_CMR_WAVE | TC_CMR_WAVSEL_UP_RC | TC_CMR_TCCLKS_TIMER_CLOCK1); + TC_Configure(tc, channel, + TC_CMR_WAVE + | TC_CMR_WAVSEL_UP_RC + | (HAL_TIMER_PRESCALER == 2 ? TC_CMR_TCCLKS_TIMER_CLOCK1 : 0) + | (HAL_TIMER_PRESCALER == 8 ? TC_CMR_TCCLKS_TIMER_CLOCK2 : 0) + | (HAL_TIMER_PRESCALER == 32 ? TC_CMR_TCCLKS_TIMER_CLOCK3 : 0) + | (HAL_TIMER_PRESCALER == 128 ? TC_CMR_TCCLKS_TIMER_CLOCK4 : 0) + ); // Set compare value - TC_SetRC(tc, channel, VARIANT_MCK / 2 / frequency); + TC_SetRC(tc, channel, VARIANT_MCK / (HAL_TIMER_PRESCALER) / frequency); // And start timer TC_Start(tc, channel); @@ -105,12 +112,12 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { } void HAL_timer_enable_interrupt(const uint8_t timer_num) { - IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + IRQn_Type irq = timer_config[timer_num].IRQ_Id; NVIC_EnableIRQ(irq); } void HAL_timer_disable_interrupt(const uint8_t timer_num) { - IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + IRQn_Type irq = timer_config[timer_num].IRQ_Id; NVIC_DisableIRQ(irq); // We NEED memory barriers to ensure Interrupts are actually disabled! @@ -125,7 +132,7 @@ static bool NVIC_GetEnabledIRQ(IRQn_Type IRQn) { } bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { - IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + IRQn_Type irq = timer_config[timer_num].IRQ_Id; return NVIC_GetEnabledIRQ(irq); } diff --git a/Marlin/src/HAL/DUE/timers.h b/Marlin/src/HAL/DUE/timers.h index 0e1ea07cc2e47..dc35c77e6384e 100644 --- a/Marlin/src/HAL/DUE/timers.h +++ b/Marlin/src/HAL/DUE/timers.h @@ -35,37 +35,38 @@ typedef uint32_t hal_timer_t; #define HAL_TIMER_TYPE_MAX 0xFFFFFFFF -#define HAL_TIMER_RATE ((F_CPU) / 2) // frequency of timers peripherals +#define HAL_TIMER_PRESCALER 2 +#define HAL_TIMER_RATE ((F_CPU) / (HAL_TIMER_PRESCALER)) // frequency of timers peripherals -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 2 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 2 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 4 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 4 // Timer Index for Temperature #endif -#ifndef TONE_TIMER_NUM - #define TONE_TIMER_NUM 6 // index of timer to use for beeper tones +#ifndef MF_TIMER_TONE + #define MF_TIMER_TONE 6 // index of timer to use for beeper tones #endif #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_PRESCALE (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US) +#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_PRESCALE (CYCLES_PER_MICROSECOND / STEPPER_TIMER_TICKS_PER_US) -#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 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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() void TC2_Handler() @@ -92,7 +93,7 @@ typedef struct { // Public Variables // ------------------------ -extern const tTimerConfig TimerConfig[]; +extern const tTimerConfig timer_config[]; // ------------------------ // Public functions @@ -101,17 +102,17 @@ extern const tTimerConfig TimerConfig[]; void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { - const tTimerConfig * const pConfig = &TimerConfig[timer_num]; + const tTimerConfig * const pConfig = &timer_config[timer_num]; pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_RC = compare; } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { - const tTimerConfig * const pConfig = &TimerConfig[timer_num]; + const tTimerConfig * const pConfig = &timer_config[timer_num]; return pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_RC; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { - const tTimerConfig * const pConfig = &TimerConfig[timer_num]; + const tTimerConfig * const pConfig = &timer_config[timer_num]; return pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_CV; } @@ -120,9 +121,9 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) { - const tTimerConfig * const pConfig = &TimerConfig[timer_num]; + const tTimerConfig * const pConfig = &timer_config[timer_num]; // Reading the status register clears the interrupt flag pConfig->pTimerRegs->TC_CHANNEL[pConfig->channel].TC_SR; } -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/DUE/upload_extra_script.py b/Marlin/src/HAL/DUE/upload_extra_script.py index d52a0a3642b45..ca12b3b54f43f 100644 --- a/Marlin/src/HAL/DUE/upload_extra_script.py +++ b/Marlin/src/HAL/DUE/upload_extra_script.py @@ -4,15 +4,16 @@ # Windows: bossac.exe # Other: leave unchanged # +import pioutil +if pioutil.is_pio_build(): + import platform + current_OS = platform.system() -import platform -current_OS = platform.system() + if current_OS == 'Windows': -if current_OS == 'Windows': + Import("env") - Import("env") - - # Use bossac.exe on Windows - env.Replace( - UPLOADCMD="bossac --info --unlock --write --verify --reset --erase -U false --boot $SOURCE" - ) + # Use bossac.exe on Windows + env.Replace( + UPLOADCMD="bossac --info --unlock --write --verify --reset --erase -U false --boot $SOURCE" + ) diff --git a/Marlin/src/HAL/DUE/usb/compiler.h b/Marlin/src/HAL/DUE/usb/compiler.h index f89e554c45623..633197914eefb 100644 --- a/Marlin/src/HAL/DUE/usb/compiler.h +++ b/Marlin/src/HAL/DUE/usb/compiler.h @@ -1059,7 +1059,7 @@ static inline void convert_64_bit_to_byte_array(uint64_t value, uint8_t *data) while (val_index < 8) { data[val_index++] = value & 0xFF; - value = value >> 8; + value >>= 8; } } 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 3dcbbaecd28f8..34cc256b30ff0 100644 --- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp +++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp @@ -10,7 +10,7 @@ #include "../../../sd/cardreader.h" extern "C" { -#include "sd_mmc_spi_mem.h" + #include "sd_mmc_spi_mem.h" } #define SD_MMC_BLOCK_SIZE 512 diff --git a/Marlin/src/HAL/DUE/watchdog.cpp b/Marlin/src/HAL/DUE/watchdog.cpp deleted file mode 100644 index e144db8291e37..0000000000000 --- a/Marlin/src/HAL/DUE/watchdog.cpp +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 ARDUINO_ARCH_SAM - -#include "../../inc/MarlinConfig.h" -#include "../../MarlinCore.h" -#include "watchdog.h" - -// Override Arduino runtime to either config or disable the watchdog -// -// We need to configure the watchdog as soon as possible in the boot -// process, because watchdog initialization at hardware reset on SAM3X8E -// is unreliable, and there is risk of unintended resets if we delay -// that initialization to a later time. -void watchdogSetup() { - - #if ENABLED(USE_WATCHDOG) - - // 4 seconds timeout - uint32_t timeout = TERN(WATCHDOG_DURATION_8S, 8000, 4000); - - // Calculate timeout value in WDT counter ticks: This assumes - // the slow clock is running at 32.768 kHz watchdog - // frequency is therefore 32768 / 128 = 256 Hz - timeout = (timeout << 8) / 1000; - if (timeout == 0) - timeout = 1; - else if (timeout > 0xFFF) - 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. - - #if ENABLED(WATCHDOG_RESET_MANUAL) - // We enable the watchdog timer, but only for the interrupt. - - // Configure WDT to only trigger an interrupt - value |= WDT_MR_WDFIEN; // Enable WDT fault interrupt. - - // Disable WDT interrupt (just in case, to avoid triggering it!) - NVIC_DisableIRQ(WDT_IRQn); - - // We NEED memory barriers to ensure Interrupts are actually disabled! - // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) - __DSB(); - __ISB(); - - // Initialize WDT with the given parameters - WDT_Enable(WDT, value); - - // Configure and enable WDT interrupt. - NVIC_ClearPendingIRQ(WDT_IRQn); - NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups - NVIC_EnableIRQ(WDT_IRQn); - - #else - - // a WDT fault triggers a reset - value |= WDT_MR_WDRSTEN; - - // Initialize WDT with the given parameters - WDT_Enable(WDT, value); - - #endif - - // Reset the watchdog - WDT_Restart(WDT); - - #else - - // Make sure to completely disable the Watchdog - WDT_Disable(WDT); - - #endif -} - -#if ENABLED(USE_WATCHDOG) - // Initialize watchdog - On SAM3X, Watchdog was already configured - // and enabled or disabled at startup, so no need to reconfigure it - // here. - void watchdog_init() { - // Reset watchdog to start clean - WDT_Restart(WDT); - } -#endif // USE_WATCHDOG - -#endif diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp index 6a66d519b33cd..29f3be3c028aa 100644 --- a/Marlin/src/HAL/ESP32/HAL.cpp +++ b/Marlin/src/HAL/ESP32/HAL.cpp @@ -52,7 +52,7 @@ // Externs // ------------------------ -portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; +portMUX_TYPE MarlinHAL::spinlock = portMUX_INITIALIZER_UNLOCKED; // ------------------------ // Local defines @@ -64,7 +64,8 @@ portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; // Public Variables // ------------------------ -uint16_t HAL_adc_result; +uint16_t MarlinHAL::adc_result; +pwm_pin_t MarlinHAL::pwm_pin_data[MAX_EXPANDER_BITS]; // ------------------------ // Private Variables @@ -73,9 +74,16 @@ uint16_t HAL_adc_result; esp_adc_cal_characteristics_t characteristics[ADC_ATTEN_MAX]; adc_atten_t attenuations[ADC1_CHANNEL_MAX] = {}; uint32_t thresholds[ADC_ATTEN_MAX]; -volatile int numPWMUsed = 0, - pwmPins[MAX_PWM_PINS], - pwmValues[MAX_PWM_PINS]; + +volatile int numPWMUsed = 0; +volatile struct { pin_t pin; int value; } pwmState[MAX_PWM_PINS]; + +pin_t chan_pin[CHANNEL_MAX_NUM + 1] = { 0 }; // PWM capable IOpins - not 0 or >33 on ESP32 + +struct { + uint32_t freq; // ledcReadFreq doesn't work if a duty hasn't been set yet! + uint16_t res; +} pwmInfo[(CHANNEL_MAX_NUM + 1) / 2]; // ------------------------ // Public functions @@ -95,20 +103,22 @@ volatile int numPWMUsed = 0, #endif #if ENABLED(USE_ESP32_EXIO) + HardwareSerial YSerial2(2); void Write_EXIO(uint8_t IO, uint8_t v) { - if (ISRS_ENABLED()) { - DISABLE_ISRS(); + if (hal.isr_state()) { + hal.isr_off(); YSerial2.write(0x80 | (((char)v) << 5) | (IO - 100)); - ENABLE_ISRS(); + hal.isr_on(); } else YSerial2.write(0x80 | (((char)v) << 5) | (IO - 100)); } + #endif -void HAL_init_board() { +void MarlinHAL::init_board() { #if ENABLED(USE_ESP32_TASK_WDT) esp_task_wdt_init(10, true); #endif @@ -154,27 +164,51 @@ void HAL_init_board() { #endif } -void HAL_idletask() { +void MarlinHAL::idletask() { #if BOTH(WIFISUPPORT, OTASUPPORT) OTA_handle(); #endif TERN_(ESP3D_WIFISUPPORT, esp3dlib.idletask()); } -void HAL_clear_reset_source() { } - -uint8_t HAL_get_reset_source() { return rtc_get_reset_reason(1); } +uint8_t MarlinHAL::get_reset_source() { return rtc_get_reset_reason(1); } -void HAL_reboot() { ESP.restart(); } +void MarlinHAL::reboot() { ESP.restart(); } void _delay_ms(int delay_ms) { delay(delay_ms); } // return free memory between end of heap (or end bss) and whatever is current -int freeMemory() { return ESP.getFreeHeap(); } +int MarlinHAL::freeMemory() { return ESP.getFreeHeap(); } + +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout + + extern "C" { + esp_err_t esp_task_wdt_reset(); + } + + void watchdogSetup() { + // do whatever. don't remove this function. + } + + void MarlinHAL::watchdog_init() { + // TODO + } + + // Reset watchdog. + void MarlinHAL::watchdog_refresh() { esp_task_wdt_reset(); } + +#endif // ------------------------ // ADC // ------------------------ + #define ADC1_CHANNEL(pin) ADC1_GPIO ## pin ## _CHANNEL adc1_channel_t get_channel(int pin) { @@ -196,22 +230,24 @@ void adc1_set_attenuation(adc1_channel_t chan, adc_atten_t atten) { } } -void HAL_adc_init() { +void MarlinHAL::adc_init() { // Configure ADC adc1_config_width(ADC_WIDTH_12Bit); // Configure channels only if used as (re-)configuring a pin for ADC that is used elsewhere might have adverse effects - TERN_(HAS_TEMP_ADC_0, adc1_set_attenuation(get_channel(TEMP_0_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_ADC_1, adc1_set_attenuation(get_channel(TEMP_1_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_ADC_2, adc1_set_attenuation(get_channel(TEMP_2_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_ADC_3, adc1_set_attenuation(get_channel(TEMP_3_PIN), ADC_ATTEN_11db)); - TERN_(HAS_TEMP_ADC_4, adc1_set_attenuation(get_channel(TEMP_4_PIN), ADC_ATTEN_11db)); - 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_COOLER, adc1_set_attenuation(get_channel(TEMP_COOLER_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_0, adc1_set_attenuation(get_channel(TEMP_0_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_1, adc1_set_attenuation(get_channel(TEMP_1_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_2, adc1_set_attenuation(get_channel(TEMP_2_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_3, adc1_set_attenuation(get_channel(TEMP_3_PIN), ADC_ATTEN_11db)); + TERN_(HAS_TEMP_ADC_4, adc1_set_attenuation(get_channel(TEMP_4_PIN), ADC_ATTEN_11db)); + 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)); // Note that adc2 is shared with the WiFi module, which has higher priority, so the conversion may fail. @@ -226,11 +262,16 @@ void HAL_adc_init() { } } -void HAL_adc_start_conversion(const uint8_t adc_pin) { - const adc1_channel_t chan = get_channel(adc_pin); +#ifndef ADC_REFERENCE_VOLTAGE + #define ADC_REFERENCE_VOLTAGE 3.3 +#endif + +void MarlinHAL::adc_start(const pin_t pin) { + const adc1_channel_t chan = get_channel(pin); uint32_t mv; esp_adc_cal_get_voltage((adc_channel_t)chan, &characteristics[attenuations[chan]], &mv); - HAL_adc_result = mv * 1023.0 / 3300.0; + + adc_result = mv * isr_float_t(1023) / isr_float_t(ADC_REFERENCE_VOLTAGE) / isr_float_t(1000); // Change the attenuation level based on the new reading adc_atten_t atten; @@ -247,25 +288,106 @@ void HAL_adc_start_conversion(const uint8_t adc_pin) { adc1_set_attenuation(chan, atten); } -void analogWrite(pin_t pin, int value) { - // Use ledc hardware for internal pins - if (pin < 34) { - static int cnt_channel = 1, pin_to_channel[40] = { 0 }; - if (pin_to_channel[pin] == 0) { - ledcAttachPin(pin, cnt_channel); - ledcSetup(cnt_channel, 490, 8); - ledcWrite(cnt_channel, value); - pin_to_channel[pin] = cnt_channel++; +// ------------------------ +// PWM +// ------------------------ + +int8_t channel_for_pin(const uint8_t pin) { + for (int i = 0; i <= CHANNEL_MAX_NUM; i++) + if (chan_pin[i] == pin) return i; + return -1; +} + +// get PWM channel for pin - if none then attach a new one +// return -1 if fail or invalid pin#, channel # (0-15) if success +int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res) { + if (!WITHIN(pin, 1, MAX_PWM_IOPIN)) return -1; // Not a hardware PWM pin! + int8_t cid = channel_for_pin(pin); + if (cid >= 0) return cid; + + // Find an empty adjacent channel (same timer & freq/res) + for (int i = 0; i <= CHANNEL_MAX_NUM; i++) { + if (chan_pin[i] == 0) { + if (chan_pin[i ^ 0x1] != 0) { + if (pwmInfo[i / 2].freq == freq && pwmInfo[i / 2].res == res) { + chan_pin[i] = pin; // Allocate PWM to this channel + ledcAttachPin(pin, i); + return i; + } + } + else if (cid == -1) // Pair of empty channels? + cid = i & 0xFE; // Save lower channel number } - ledcWrite(pin_to_channel[pin], value); + } + // not attached, is an empty timer slot avail? + if (cid >= 0) { + chan_pin[cid] = pin; + pwmInfo[cid / 2].freq = freq; + pwmInfo[cid / 2].res = res; + ledcSetup(cid, freq, res); + ledcAttachPin(pin, cid); + } + return cid; // -1 if no channel avail +} + +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=_BV(PWM_RESOLUTION)-1*/, const bool invert/*=false*/) { + #if ENABLED(I2S_STEPPER_STREAM) + if (pin > 127) { + const uint8_t pinlo = pin & 0x7F; + pwm_pin_t &pindata = pwm_pin_data[pinlo]; + const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, pindata.pwm_cycle_ticks); + if (duty == 0 || duty == pindata.pwm_cycle_ticks) { // max or min (i.e., on/off) + pindata.pwm_duty_ticks = 0; // turn off PWM for this pin + duty ? SBI32(i2s_port_data, pinlo) : CBI32(i2s_port_data, pinlo); // set pin level + } + else + pindata.pwm_duty_ticks = duty; // PWM duty count = # of 4µs ticks per full PWM cycle + } + else + #endif + { + const int8_t cid = get_pwm_channel(pin, PWM_FREQUENCY, PWM_RESOLUTION); + if (cid >= 0) { + const uint32_t duty = map(invert ? v_size - v : v, 0, v_size, 0, _BV(PWM_RESOLUTION)-1); + ledcWrite(cid, duty); + } + } +} + +int8_t MarlinHAL::set_pwm_frequency(const pin_t pin, const uint32_t f_desired) { + #if ENABLED(I2S_STEPPER_STREAM) + if (pin > 127) { + pwm_pin_data[pin & 0x7F].pwm_cycle_ticks = 1000000UL / f_desired / 4; // # of 4µs ticks per full PWM cycle + return 0; + } + else + #endif + { + const int8_t cid = channel_for_pin(pin); + if (cid >= 0) { + if (f_desired == ledcReadFreq(cid)) return cid; // no freq change + ledcDetachPin(chan_pin[cid]); + chan_pin[cid] = 0; // remove old freq channel + } + return get_pwm_channel(pin, f_desired, PWM_RESOLUTION); // try for new one + } +} + +// use hardware PWM if avail, if not then ISR +void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq/*=PWM_FREQUENCY*/, const uint16_t res/*=8*/) { // always 8 bit resolution! + // Use ledc hardware for internal pins + const int8_t cid = get_pwm_channel(pin, freq, res); + if (cid >= 0) { + ledcWrite(cid, value); // set duty value return; } + // not a hardware PWM pin OR no PWM channels available int idx = -1; // Search Pin for (int i = 0; i < numPWMUsed; ++i) - if (pwmPins[i] == pin) { idx = i; break; } + if (pwmState[i].pin == pin) { idx = i; break; } // not found ? if (idx < 0) { @@ -274,34 +396,34 @@ void analogWrite(pin_t pin, int value) { // Take new slot for pin idx = numPWMUsed; - pwmPins[idx] = pin; + pwmState[idx].pin = pin; // Start timer on first use - if (idx == 0) HAL_timer_start(PWM_TIMER_NUM, PWM_TIMER_FREQUENCY); + if (idx == 0) HAL_timer_start(MF_TIMER_PWM, PWM_TIMER_FREQUENCY); ++numPWMUsed; } // Use 7bit internal value - add 1 to have 100% high at 255 - pwmValues[idx] = (value + 1) / 2; + pwmState[idx].value = (value + 1) / 2; } // Handle PWM timer interrupt HAL_PWM_TIMER_ISR() { - HAL_timer_isr_prologue(PWM_TIMER_NUM); + HAL_timer_isr_prologue(MF_TIMER_PWM); static uint8_t count = 0; for (int i = 0; i < numPWMUsed; ++i) { if (count == 0) // Start of interval - WRITE(pwmPins[i], pwmValues[i] ? HIGH : LOW); - else if (pwmValues[i] == count) // End of duration - WRITE(pwmPins[i], LOW); + digitalWrite(pwmState[i].pin, pwmState[i].value ? HIGH : LOW); + else if (pwmState[i].value == count) // End of duration + digitalWrite(pwmState[i].pin, LOW); } // 128 for 7 Bit resolution count = (count + 1) & 0x7F; - HAL_timer_isr_epilogue(PWM_TIMER_NUM); + HAL_timer_isr_epilogue(MF_TIMER_PWM); } #endif // ARDUINO_ARCH_ESP32 diff --git a/Marlin/src/HAL/ESP32/HAL.h b/Marlin/src/HAL/ESP32/HAL.h index bc0ce4e037cff..ddfedf92eed98 100644 --- a/Marlin/src/HAL/ESP32/HAL.h +++ b/Marlin/src/HAL/ESP32/HAL.h @@ -32,7 +32,6 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include "i2s.h" #if ENABLED(WIFISUPPORT) @@ -49,8 +48,6 @@ // Defines // ------------------------ -extern portMUX_TYPE spinlock; - #define MYSERIAL1 flushableSerial #if EITHER(WIFISUPPORT, ESP3D_WIFISUPPORT) @@ -63,26 +60,33 @@ extern portMUX_TYPE spinlock; #endif #endif -#define CRITICAL_SECTION_START() portENTER_CRITICAL(&spinlock) -#define CRITICAL_SECTION_END() portEXIT_CRITICAL(&spinlock) -#define ISRS_ENABLED() (spinlock.owner == portMUX_FREE_VAL) -#define ENABLE_ISRS() if (spinlock.owner != portMUX_FREE_VAL) portEXIT_CRITICAL(&spinlock) -#define DISABLE_ISRS() portENTER_CRITICAL(&spinlock) +#define CRITICAL_SECTION_START() portENTER_CRITICAL(&hal.spinlock) +#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 +#ifndef MAX_EXPANDER_BITS + #define MAX_EXPANDER_BITS 32 // I2S expander bit width (max 32) +#endif // ------------------------ // Types // ------------------------ +typedef double isr_float_t; // FPU ops are used for single-precision, so use double for ISRs. typedef int16_t pin_t; -#define HAL_SERVO_LIB Servo +typedef struct pwm_pin { + uint32_t pwm_cycle_ticks = 1000000UL / (PWM_FREQUENCY) / 4; // # ticks per pwm cycle + uint32_t pwm_tick_count = 0; // current tick count + uint32_t pwm_duty_ticks = 0; // # of ticks for current duty cycle +} pwm_pin_t; -// ------------------------ -// Public Variables -// ------------------------ - -/** result of last ADC conversion */ -extern uint16_t HAL_adc_result; +class Servo; +typedef Servo hal_servo_t; // ------------------------ // Public functions @@ -91,57 +95,18 @@ extern uint16_t HAL_adc_result; // // Tone // -void toneInit(); void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0); void noTone(const pin_t _pin); +int8_t get_pwm_channel(const pin_t pin, const uint32_t freq, const uint16_t res); +void analogWrite(const pin_t pin, const uint16_t value, const uint32_t freq=PWM_FREQUENCY, const uint16_t res=8); -// clear reset reason -void HAL_clear_reset_source(); - -// reset reason -uint8_t HAL_get_reset_source(); - -void HAL_reboot(); - -void _delay_ms(int delay); - -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-function" -#endif - -int freeMemory(); - -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif - -void analogWrite(pin_t pin, int value); - -// ADC -#define HAL_ANALOG_SELECT(pin) - -void HAL_adc_init(); - -#define HAL_ADC_VREF 3.3 -#define HAL_ADC_RESOLUTION 10 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_result -#define HAL_ADC_READY() true - -void HAL_adc_start_conversion(const uint8_t adc_pin); - +// +// 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) -// Enable hooks into idle and setup for HAL -#define HAL_IDLETASK 1 -#define BOARD_INIT() HAL_init_board(); -void HAL_idletask(); -inline void HAL_init() {} -void HAL_init_board(); - #if ENABLED(USE_ESP32_EXIO) void Write_EXIO(uint8_t IO, uint8_t v); #endif @@ -186,3 +151,96 @@ FORCE_INLINE static void DELAY_CYCLES(uint32_t x) { } } + +// ------------------------ +// Class Utilities +// ------------------------ + +#pragma GCC diagnostic push +#if GCC_VERSION <= 50000 + #pragma GCC diagnostic ignored "-Wunused-function" +#endif + +int freeMemory(); + +#pragma GCC diagnostic pop + +void _delay_ms(const int ms); + +// ------------------------ +// MarlinHAL Class +// ------------------------ + +#define HAL_ADC_VREF 3.3 +#define HAL_ADC_RESOLUTION 10 + +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 + + // Interrupts + static portMUX_TYPE spinlock; + static bool isr_state() { return spinlock.owner == portMUX_FREE_VAL; } + 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); } + + // Tasks, called from idle() + static void idletask(); + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory(); + + static pwm_pin_t pwm_pin_data[MAX_EXPANDER_BITS]; + + // + // ADC Methods + // + + static uint16_t adc_result; + + // 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); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value() { return adc_result; } + + /** + * If not already allocated, allocate a hardware PWM channel + * to the pin and set the duty cycle.. + * 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); + + /** + * Allocate and set the frequency of a hardware PWM pin + * Returns -1 if no pin available. + */ + static int8_t set_pwm_frequency(const pin_t pin, const uint32_t f_desired); + +}; diff --git a/Marlin/src/HAL/ESP32/Servo.cpp b/Marlin/src/HAL/ESP32/Servo.cpp index fcf58485819e4..ca3950d07f751 100644 --- a/Marlin/src/HAL/ESP32/Servo.cpp +++ b/Marlin/src/HAL/ESP32/Servo.cpp @@ -31,20 +31,18 @@ // so we only allocate servo channels up high to avoid side effects with regards to analogWrite (fans, leds, laser pwm etc.) int Servo::channel_next_free = 12; -Servo::Servo() { - channel = channel_next_free++; -} +Servo::Servo() {} int8_t Servo::attach(const int inPin) { - if (channel >= CHANNEL_MAX_NUM) return -1; if (inPin > 0) pin = inPin; - - ledcSetup(channel, 50, 16); // channel X, 50 Hz, 16-bit depth - ledcAttachPin(pin, channel); - return true; + channel = get_pwm_channel(pin, 50u, 16u); + return channel; // -1 if no PWM avail. } -void Servo::detach() { ledcDetachPin(pin); } +// leave channel connected to servo - set duty to zero +void Servo::detach() { + if (channel >= 0) ledcWrite(channel, 0); +} int Servo::read() { return degrees; } @@ -52,7 +50,7 @@ void Servo::write(int inDegrees) { degrees = constrain(inDegrees, MIN_ANGLE, MAX_ANGLE); int us = map(degrees, MIN_ANGLE, MAX_ANGLE, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH); int duty = map(us, 0, TAU_USEC, 0, MAX_COMPARE); - ledcWrite(channel, duty); + if (channel >= 0) ledcWrite(channel, duty); // don't save duty for servos! } void Servo::move(const int value) { diff --git a/Marlin/src/HAL/ESP32/Servo.h b/Marlin/src/HAL/ESP32/Servo.h index 8542092d66ea1..1dbb416a83177 100644 --- a/Marlin/src/HAL/ESP32/Servo.h +++ b/Marlin/src/HAL/ESP32/Servo.h @@ -30,8 +30,7 @@ class Servo { MAX_PULSE_WIDTH = 2400, // Longest pulse sent to a servo TAU_MSEC = 20, TAU_USEC = (TAU_MSEC * 1000), - MAX_COMPARE = _BV(16) - 1, // 65535 - CHANNEL_MAX_NUM = 16; + MAX_COMPARE = _BV(16) - 1; // 65535 public: Servo(); diff --git a/Marlin/src/HAL/ESP32/Tone.cpp b/Marlin/src/HAL/ESP32/Tone.cpp index 376c0f32e1291..839c612b6a870 100644 --- a/Marlin/src/HAL/ESP32/Tone.cpp +++ b/Marlin/src/HAL/ESP32/Tone.cpp @@ -35,19 +35,19 @@ static pin_t tone_pin; volatile static int32_t toggles; -void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration) { +void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration/*=0*/) { tone_pin = _pin; toggles = 2 * frequency * duration / 1000; - HAL_timer_start(TONE_TIMER_NUM, 2 * frequency); + HAL_timer_start(MF_TIMER_TONE, 2 * frequency); } void noTone(const pin_t _pin) { - HAL_timer_disable_interrupt(TONE_TIMER_NUM); + HAL_timer_disable_interrupt(MF_TIMER_TONE); WRITE(_pin, LOW); } HAL_TONE_TIMER_ISR() { - HAL_timer_isr_prologue(TONE_TIMER_NUM); + HAL_timer_isr_prologue(MF_TIMER_TONE); if (toggles) { toggles--; diff --git a/Marlin/src/HAL/ESP32/i2s.cpp b/Marlin/src/HAL/ESP32/i2s.cpp index 557ea319e671e..cf337eeb46224 100644 --- a/Marlin/src/HAL/ESP32/i2s.cpp +++ b/Marlin/src/HAL/ESP32/i2s.cpp @@ -64,12 +64,9 @@ uint32_t i2s_port_data = 0; #define I2S_EXIT_CRITICAL() portEXIT_CRITICAL(&i2s_spinlock[i2s_num]) static inline void gpio_matrix_out_check(uint32_t gpio, uint32_t signal_idx, bool out_inv, bool oen_inv) { - //if pin = -1, do not need to configure - if (gpio != -1) { - PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); - gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT); - gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv); - } + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO); + gpio_set_direction((gpio_num_t)gpio, (gpio_mode_t)GPIO_MODE_DEF_OUTPUT); + gpio_matrix_out(gpio, signal_idx, out_inv, oen_inv); } static esp_err_t i2s_reset_fifo(i2s_port_t i2s_num) { @@ -256,13 +253,7 @@ int i2s_init() { I2S0.fifo_conf.dscr_en = 0; - I2S0.conf_chan.tx_chan_mod = ( - #if ENABLED(I2S_STEPPER_SPLIT_STREAM) - 4 - #else - 0 - #endif - ); + I2S0.conf_chan.tx_chan_mod = TERN(I2S_STEPPER_SPLIT_STREAM, 4, 0); I2S0.fifo_conf.tx_fifo_mod = 0; I2S0.conf.tx_mono = 0; @@ -313,9 +304,16 @@ int i2s_init() { xTaskCreatePinnedToCore(stepperTask, "StepperTask", 10000, nullptr, 1, nullptr, CONFIG_ARDUINO_RUNNING_CORE); // run I2S stepper task on same core as rest of Marlin // Route the i2s pins to the appropriate GPIO - gpio_matrix_out_check(I2S_DATA, I2S0O_DATA_OUT23_IDX, 0, 0); - gpio_matrix_out_check(I2S_BCK, I2S0O_BCK_OUT_IDX, 0, 0); - gpio_matrix_out_check(I2S_WS, I2S0O_WS_OUT_IDX, 0, 0); + // If a pin is not defined, no need to configure + #if defined(I2S_DATA) && I2S_DATA >= 0 + gpio_matrix_out_check(I2S_DATA, I2S0O_DATA_OUT23_IDX, 0, 0); + #endif + #if defined(I2S_BCK) && I2S_BCK >= 0 + gpio_matrix_out_check(I2S_BCK, I2S0O_BCK_OUT_IDX, 0, 0); + #endif + #if defined(I2S_WS) && I2S_WS >= 0 + gpio_matrix_out_check(I2S_WS, I2S0O_WS_OUT_IDX, 0, 0); + #endif // Start the I2S peripheral return i2s_start(I2S_NUM_0); @@ -339,6 +337,26 @@ uint8_t i2s_state(uint8_t pin) { } void i2s_push_sample() { + // Every 4µs (when space in DMA buffer) toggle each expander PWM output using + // the current duty cycle/frequency so they sync with any steps (once + // through the DMA/FIFO buffers). PWM signal inversion handled by other functions + LOOP_L_N(p, MAX_EXPANDER_BITS) { + if (hal.pwm_pin_data[p].pwm_duty_ticks > 0) { // pin has active pwm? + if (hal.pwm_pin_data[p].pwm_tick_count == 0) { + if (TEST32(i2s_port_data, p)) { // hi->lo + CBI32(i2s_port_data, p); + hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_cycle_ticks - hal.pwm_pin_data[p].pwm_duty_ticks; + } + else { // lo->hi + SBI32(i2s_port_data, p); + hal.pwm_pin_data[p].pwm_tick_count = hal.pwm_pin_data[p].pwm_duty_ticks; + } + } + else + hal.pwm_pin_data[p].pwm_tick_count--; + } + } + dma.current[dma.rw_pos++] = i2s_port_data; } diff --git a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h index 5f1c4b16019d2..3ca806897a894 100644 --- a/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h +++ b/Marlin/src/HAL/ESP32/inc/Conditionals_adv.h @@ -20,3 +20,10 @@ * */ #pragma once + +// +// Board-specific options need to be defined before HAL.h +// +#if MB(MKS_TINYBEE) + #define MAX_EXPANDER_BITS 24 // TinyBee has 3 x HC595 +#endif diff --git a/Marlin/src/HAL/ESP32/inc/SanityCheck.h b/Marlin/src/HAL/ESP32/inc/SanityCheck.h index 8bbc68d8715bc..3ccb15989f338 100644 --- a/Marlin/src/HAL/ESP32/inc/SanityCheck.h +++ b/Marlin/src/HAL/ESP32/inc/SanityCheck.h @@ -25,8 +25,8 @@ #error "EMERGENCY_PARSER is not yet implemented for ESP32. 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 on ESP32." +#if (ENABLED(SPINDLE_LASER_USE_PWM) && SPINDLE_LASER_FREQUENCY > 78125) || (ENABLED(FAST_PWM_FAN_FREQUENCY) && FAST_PWM_FAN_FREQUENCY > 78125) + #error "SPINDLE_LASER_FREQUENCY and FAST_PWM_FREQUENCY maximum value is 78125Hz for ESP32." #endif #if HAS_TMC_SW_SERIAL @@ -40,3 +40,15 @@ #if ENABLED(POSTMORTEM_DEBUGGING) #error "POSTMORTEM_DEBUGGING is not yet supported on ESP32." #endif + +#if MB(MKS_TINYBEE) && ENABLED(FAST_PWM_FAN) + #error "FAST_PWM_FAN is not available on TinyBee." +#endif + +#if USING_PULLDOWNS + #error "PULLDOWN pin mode is not available on ESP32 boards." +#endif + +#if BOTH(I2S_STEPPER_STREAM, LIN_ADVANCE) + #error "I2S stream is currently incompatible with LIN_ADVANCE." +#endif diff --git a/Marlin/src/HAL/ESP32/timers.cpp b/Marlin/src/HAL/ESP32/timers.cpp index 57662a6658822..c37ad2430cb20 100644 --- a/Marlin/src/HAL/ESP32/timers.cpp +++ b/Marlin/src/HAL/ESP32/timers.cpp @@ -41,7 +41,7 @@ static timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1}; -const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = { +const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = { { TIMER_GROUP_0, TIMER_0, STEPPER_TIMER_PRESCALE, stepTC_Handler }, // 0 - Stepper { TIMER_GROUP_0, TIMER_1, TEMP_TIMER_PRESCALE, tempTC_Handler }, // 1 - Temperature { TIMER_GROUP_1, TIMER_0, PWM_TIMER_PRESCALE, pwmTC_Handler }, // 2 - PWM @@ -53,7 +53,7 @@ const tTimerConfig TimerConfig [NUM_HARDWARE_TIMERS] = { // ------------------------ void IRAM_ATTR timer_isr(void *para) { - const tTimerConfig& timer = TimerConfig[(int)para]; + const tTimerConfig& timer = timer_config[(int)para]; // Retrieve the interrupt status and the counter value // from the timer that reported the interrupt @@ -81,8 +81,8 @@ void IRAM_ATTR timer_isr(void *para) { * @param timer_num timer number to initialize * @param frequency frequency of the timer */ -void HAL_timer_start(const uint8_t timer_num, uint32_t frequency) { - const tTimerConfig timer = TimerConfig[timer_num]; +void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { + const tTimerConfig timer = timer_config[timer_num]; timer_config_t config; config.divider = timer.divider; @@ -115,7 +115,7 @@ void HAL_timer_start(const uint8_t timer_num, uint32_t frequency) { * @param count threshold at which the interrupt is triggered */ void HAL_timer_set_compare(const uint8_t timer_num, hal_timer_t count) { - const tTimerConfig timer = TimerConfig[timer_num]; + const tTimerConfig timer = timer_config[timer_num]; timer_set_alarm_value(timer.group, timer.idx, count); } @@ -125,7 +125,7 @@ void HAL_timer_set_compare(const uint8_t timer_num, hal_timer_t count) { * @return the timer current threshold for the alarm to be triggered */ hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { - const tTimerConfig timer = TimerConfig[timer_num]; + const tTimerConfig timer = timer_config[timer_num]; uint64_t alarm_value; timer_get_alarm_value(timer.group, timer.idx, &alarm_value); @@ -139,7 +139,7 @@ hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { * @return the current counter of the alarm */ hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { - const tTimerConfig timer = TimerConfig[timer_num]; + const tTimerConfig timer = timer_config[timer_num]; uint64_t counter_value; timer_get_counter_value(timer.group, timer.idx, &counter_value); return counter_value; @@ -150,7 +150,7 @@ hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { * @param timer_num timer number to enable interrupts on */ void HAL_timer_enable_interrupt(const uint8_t timer_num) { - //const tTimerConfig timer = TimerConfig[timer_num]; + //const tTimerConfig timer = timer_config[timer_num]; //timer_enable_intr(timer.group, timer.idx); } @@ -159,12 +159,12 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num) { * @param timer_num timer number to disable interrupts on */ void HAL_timer_disable_interrupt(const uint8_t timer_num) { - //const tTimerConfig timer = TimerConfig[timer_num]; + //const tTimerConfig timer = timer_config[timer_num]; //timer_disable_intr(timer.group, timer.idx); } bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { - const tTimerConfig timer = TimerConfig[timer_num]; + const tTimerConfig timer = timer_config[timer_num]; return TG[timer.group]->int_ena.val | BIT(timer_num); } diff --git a/Marlin/src/HAL/ESP32/timers.h b/Marlin/src/HAL/ESP32/timers.h index a47697113d5d6..aa4e1551f0666 100644 --- a/Marlin/src/HAL/ESP32/timers.h +++ b/Marlin/src/HAL/ESP32/timers.h @@ -32,20 +32,20 @@ typedef uint64_t hal_timer_t; #define HAL_TIMER_TYPE_MAX 0xFFFFFFFFFFFFFFFFULL -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif -#ifndef PWM_TIMER_NUM - #define PWM_TIMER_NUM 2 // index of timer to use for PWM outputs +#ifndef MF_TIMER_PWM + #define MF_TIMER_PWM 2 // index of timer to use for PWM outputs #endif -#ifndef TONE_TIMER_NUM - #define TONE_TIMER_NUM 3 // index of timer for beeper tones +#ifndef MF_TIMER_TONE + #define MF_TIMER_TONE 3 // index of timer for beeper tones #endif #define HAL_TIMER_RATE APB_CLK_FREQ // frequency of timer peripherals @@ -79,12 +79,12 @@ typedef uint64_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP) +#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP) #ifndef HAL_TEMP_TIMER_ISR #define HAL_TEMP_TIMER_ISR() extern "C" void tempTC_Handler() @@ -121,13 +121,13 @@ typedef struct { // Public Variables // ------------------------ -extern const tTimerConfig TimerConfig[]; +extern const tTimerConfig timer_config[]; // ------------------------ // Public functions // ------------------------ -void HAL_timer_start (const uint8_t timer_num, uint32_t frequency); +void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t count); hal_timer_t HAL_timer_get_compare(const uint8_t timer_num); hal_timer_t HAL_timer_get_count(const uint8_t timer_num); @@ -136,5 +136,5 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num); void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); -#define HAL_timer_isr_prologue(TIMER_NUM) -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp new file mode 100644 index 0000000000000..a445035b2b430 --- /dev/null +++ b/Marlin/src/HAL/ESP32/u8g_esp32_spi.cpp @@ -0,0 +1,94 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2022 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * Copypaste of SAMD51 HAL developed by Giuliano Zaro (AKA GMagician) + * + * 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_ESP32 + +#include "../../inc/MarlinConfig.h" + +#if EITHER(MKS_MINI_12864, FYSETC_MINI_12864_2_1) + +#include +#include "../shared/HAL_SPI.h" +#include "HAL.h" +#include "SPI.h" + +static SPISettings spiConfig; + + +#ifndef LCD_SPI_SPEED + #ifdef SD_SPI_SPEED + #define LCD_SPI_SPEED SD_SPI_SPEED // Assume SPI speed shared with SD + #else + #define LCD_SPI_SPEED SPI_FULL_SPEED // Use full speed if SD speed is not supplied + #endif +#endif + +uint8_t u8g_eps_hw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) { + static uint8_t msgInitCount = 2; // Ignore all messages until 2nd U8G_COM_MSG_INIT + if (msgInitCount) { + if (msg == U8G_COM_MSG_INIT) msgInitCount--; + if (msgInitCount) return -1; + } + + switch (msg) { + case U8G_COM_MSG_STOP: break; + + case U8G_COM_MSG_INIT: + OUT_WRITE(DOGLCD_CS, HIGH); + OUT_WRITE(DOGLCD_A0, HIGH); + OUT_WRITE(LCD_RESET_PIN, HIGH); + u8g_Delay(5); + spiBegin(); + spiInit(LCD_SPI_SPEED); + break; + + case U8G_COM_MSG_ADDRESS: /* define cmd (arg_val = 0) or data mode (arg_val = 1) */ + WRITE(DOGLCD_A0, arg_val ? HIGH : LOW); + break; + + case U8G_COM_MSG_CHIP_SELECT: /* arg_val == 0 means HIGH level of U8G_PI_CS */ + WRITE(DOGLCD_CS, arg_val ? LOW : HIGH); + break; + + case U8G_COM_MSG_RESET: + WRITE(LCD_RESET_PIN, arg_val); + break; + + case U8G_COM_MSG_WRITE_BYTE: + spiSend((uint8_t)arg_val); + break; + + case U8G_COM_MSG_WRITE_SEQ: + uint8_t *ptr = (uint8_t*) arg_ptr; + while (arg_val > 0) { + spiSend(*ptr++); + arg_val--; + } + break; + } + return 1; +} + +#endif // EITHER(MKS_MINI_12864, FYSETC_MINI_12864_2_1) + +#endif // ARDUINO_ARCH_ESP32 diff --git a/Marlin/src/HAL/HAL.h b/Marlin/src/HAL/HAL.h index 0cd836af2b686..5186578019053 100644 --- a/Marlin/src/HAL/HAL.h +++ b/Marlin/src/HAL/HAL.h @@ -28,6 +28,7 @@ #endif #include HAL_PATH(.,HAL.h) +extern MarlinHAL hal; #define HAL_ADC_RANGE _BV(HAL_ADC_RESOLUTION) @@ -44,7 +45,3 @@ #ifndef PGMSTR #define PGMSTR(NAM,STR) const char NAM[] = STR #endif - -inline void watchdog_refresh() { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); -} diff --git a/Marlin/src/HAL/LINUX/HAL.cpp b/Marlin/src/HAL/LINUX/HAL.cpp index 0b679170ef171..db43f42eaafd3 100644 --- a/Marlin/src/HAL/LINUX/HAL.cpp +++ b/Marlin/src/HAL/LINUX/HAL.cpp @@ -24,6 +24,10 @@ #include "../../inc/MarlinConfig.h" #include "../shared/Delay.h" +// ------------------------ +// Serial ports +// ------------------------ + MSerialT usb_serial(TERN0(EMERGENCY_PARSER, true)); // U8glib required functions @@ -37,42 +41,21 @@ extern "C" { //************************// // return free heap space -int freeMemory() { - return 0; -} +int freeMemory() { return 0; } // ------------------------ // ADC // ------------------------ -void HAL_adc_init() { - -} - -void HAL_adc_enable_channel(const uint8_t ch) { - -} - -uint8_t active_ch = 0; -void HAL_adc_start_conversion(const uint8_t ch) { - active_ch = ch; -} - -bool HAL_adc_finished() { - return true; -} +uint8_t MarlinHAL::active_ch = 0; -uint16_t HAL_adc_get_result() { - pin_t pin = analogInputToDigitalPin(active_ch); +uint16_t MarlinHAL::adc_value() { + const pin_t pin = analogInputToDigitalPin(active_ch); if (!VALID_PIN(pin)) return 0; - uint16_t data = ((Gpio::get(pin) >> 2) & 0x3FF); + const uint16_t data = ((Gpio::get(pin) >> 2) & 0x3FF); return data; // return 10bit value as Marlin expects } -void HAL_pwm_init() { - -} - -void HAL_reboot() { /* Reset the application state and GPIO */ } +void MarlinHAL::reboot() { /* Reset the application state and GPIO */ } #endif // __PLAT_LINUX__ diff --git a/Marlin/src/HAL/LINUX/HAL.h b/Marlin/src/HAL/LINUX/HAL.h index 79639f4993ac5..22c3e521f0869 100644 --- a/Marlin/src/HAL/LINUX/HAL.h +++ b/Marlin/src/HAL/LINUX/HAL.h @@ -21,25 +21,42 @@ */ #pragma once -#define CPU_32_BIT +#include "../../inc/MarlinConfigPre.h" -#define F_CPU 100000000UL -#define SystemCoreClock F_CPU #include #include #include - #undef min #undef max - #include -void _printf (const char *format, ...); +#include "hardware/Clock.h" +#include "../shared/Marduino.h" +#include "../shared/math_32bit.h" +#include "../shared/HAL_SPI.h" +#include "fastio.h" +#include "serial.h" + +// ------------------------ +// Defines +// ------------------------ + +#define CPU_32_BIT +#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +#define F_CPU 100000000UL +#define SystemCoreClock F_CPU + +#define DELAY_CYCLES(x) Clock::delayCycles(x) + +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 + +void _printf(const char *format, ...); void _putc(uint8_t c); uint8_t _getc(); -//extern "C" volatile uint32_t _millis; - //arduino: Print.h #define DEC 10 #define HEX 16 @@ -49,67 +66,100 @@ uint8_t _getc(); #define B01 1 #define B10 2 -#include "hardware/Clock.h" - -#include "../shared/Marduino.h" -#include "../shared/math_32bit.h" -#include "../shared/HAL_SPI.h" -#include "fastio.h" -#include "watchdog.h" -#include "serial.h" - -#define SHARED_SERVOS HAS_SERVOS +// ------------------------ +// Serial ports +// ------------------------ extern MSerialT usb_serial; #define MYSERIAL1 usb_serial -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 - // // Interrupts // #define CRITICAL_SECTION_START() #define CRITICAL_SECTION_END() -#define ISRS_ENABLED() -#define ENABLE_ISRS() -#define DISABLE_ISRS() -inline void HAL_init() {} +// ADC +#define HAL_ADC_VREF 5.0 +#define HAL_ADC_RESOLUTION 10 + +// ------------------------ +// Class Utilities +// ------------------------ -// Utility functions +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif int freeMemory(); -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop -// ADC -#define HAL_ADC_VREF 5.0 -#define HAL_ADC_RESOLUTION 10 -#define HAL_ANALOG_SELECT(ch) HAL_adc_enable_channel(ch) -#define HAL_START_ADC(ch) HAL_adc_start_conversion(ch) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() true - -void HAL_adc_init(); -void HAL_adc_enable_channel(const uint8_t ch); -void HAL_adc_start_conversion(const uint8_t ch); -uint16_t HAL_adc_get_result(); - -// Reset source -inline void HAL_clear_reset_source(void) {} -inline uint8_t HAL_get_reset_source(void) { return RST_POWER_ON; } - -void HAL_reboot(); // Reset the application state and GPIO - -/* ---------------- Delay in cycles */ -FORCE_INLINE static void DELAY_CYCLES(uint64_t x) { - Clock::delayCycles(x); -} +// ------------------------ +// MarlinHAL Class +// ------------------------ + +class MarlinHAL { +public: + + // Earliest possible init, before setup() + MarlinHAL() {} + + // Watchdog + static void watchdog_init() {} + static void watchdog_refresh() {} + + static void init() {} // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Reset the application state and GPIO + + // Interrupts + static bool isr_state() { return true; } + static void isr_on() {} + static void isr_off() {} + + static void delay_ms(const int ms) { _delay_ms(ms); } + + // Tasks, called from idle() + static void idletask() {} + + // Reset + static constexpr uint8_t reset_reason = RST_POWER_ON; + static uint8_t get_reset_source() { return reset_reason; } + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static uint8_t active_ch; + + // 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 uint8_t) {} + + // Begin ADC sampling on the given channel + static void adc_start(const uint8_t ch) { active_ch = ch; } + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * Set the PWM duty cycle for the pin to the given value. + * No option to change the resolution or invert the duty cycle. + */ + 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_frequency(const pin_t, int) {} +}; diff --git a/Marlin/src/HAL/LINUX/arduino.cpp b/Marlin/src/HAL/LINUX/arduino.cpp index 4b56d02a389c2..075b4ccde2f4d 100644 --- a/Marlin/src/HAL/LINUX/arduino.cpp +++ b/Marlin/src/HAL/LINUX/arduino.cpp @@ -31,9 +31,7 @@ void cli() { } // Disable void sei() { } // Enable // Time functions -void _delay_ms(const int delay_ms) { - delay(delay_ms); -} +void _delay_ms(const int ms) { delay(ms); } uint32_t millis() { return (uint32_t)Clock::millis(); diff --git a/Marlin/src/HAL/LINUX/eeprom.cpp b/Marlin/src/HAL/LINUX/eeprom.cpp index 532f323c6edb0..f878bba6a51b6 100644 --- a/Marlin/src/HAL/LINUX/eeprom.cpp +++ b/Marlin/src/HAL/LINUX/eeprom.cpp @@ -69,12 +69,12 @@ bool PersistentStore::write_data(int &pos, const uint8_t *value, size_t size, ui std::size_t bytes_written = 0; for (std::size_t i = 0; i < size; i++) { - buffer[pos+i] = value[i]; - bytes_written ++; + buffer[pos + i] = value[i]; + bytes_written++; } crc16(crc, value, size); - pos = pos + size; + pos += size; return (bytes_written != size); // return true for any error } @@ -82,21 +82,21 @@ bool PersistentStore::read_data(int &pos, uint8_t *value, const size_t size, uin std::size_t bytes_read = 0; if (writing) { for (std::size_t i = 0; i < size; i++) { - value[i] = buffer[pos+i]; - bytes_read ++; + value[i] = buffer[pos + i]; + bytes_read++; } crc16(crc, value, size); } else { uint8_t temp[size]; for (std::size_t i = 0; i < size; i++) { - temp[i] = buffer[pos+i]; - bytes_read ++; + temp[i] = buffer[pos + i]; + bytes_read++; } crc16(crc, temp, size); } - pos = pos + size; + pos += size; return bytes_read != size; // return true for any error } diff --git a/Marlin/src/HAL/LINUX/hardware/Heater.h b/Marlin/src/HAL/LINUX/hardware/Heater.h index b17078d0b739e..6d590ce6c55e0 100644 --- a/Marlin/src/HAL/LINUX/hardware/Heater.h +++ b/Marlin/src/HAL/LINUX/hardware/Heater.h @@ -26,8 +26,8 @@ struct LowpassFilter { uint64_t data_delay = 0; uint16_t update(uint16_t value) { - data_delay = data_delay - (data_delay >> 6) + value; - return (uint16_t)(data_delay >> 6); + data_delay += value - (data_delay >> 6); + return uint16_t(data_delay >> 6); } }; diff --git a/Marlin/src/HAL/LINUX/include/Arduino.h b/Marlin/src/HAL/LINUX/include/Arduino.h index d4086e259a2f1..f05aaed88083e 100644 --- a/Marlin/src/HAL/LINUX/include/Arduino.h +++ b/Marlin/src/HAL/LINUX/include/Arduino.h @@ -59,10 +59,9 @@ typedef uint8_t byte; #endif #define sq(v) ((v) * (v)) -#define square(v) sq(v) #define constrain(value, arg_min, arg_max) ((value) < (arg_min) ? (arg_min) :((value) > (arg_max) ? (arg_max) : (value))) -//Interrupts +// Interrupts void cli(); // Disable void sei(); // Enable void attachInterrupt(uint32_t pin, void (*callback)(), uint32_t mode); @@ -74,8 +73,8 @@ extern "C" { } // Time functions -extern "C" void delay(const int milis); -void _delay_ms(const int delay); +extern "C" void delay(const int ms); +void _delay_ms(const int ms); void delayMicroseconds(unsigned long); uint32_t millis(); diff --git a/Marlin/src/HAL/LINUX/timers.h b/Marlin/src/HAL/LINUX/timers.h index 1beaea95abc6b..2d2a95774c1b0 100644 --- a/Marlin/src/HAL/LINUX/timers.h +++ b/Marlin/src/HAL/LINUX/timers.h @@ -37,14 +37,14 @@ typedef uint32_t hal_timer_t; #define HAL_TIMER_RATE ((SystemCoreClock) / 4) // frequency of timers peripherals -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif #define TEMP_TIMER_RATE 1000000 @@ -58,12 +58,12 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() extern "C" void TIMER0_IRQHandler() @@ -77,7 +77,6 @@ typedef uint32_t hal_timer_t; #define HAL_PWM_TIMER_ISR() extern "C" void TIMER3_IRQHandler() #define HAL_PWM_TIMER_IRQn - void HAL_timer_init(); void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); @@ -93,5 +92,5 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num); void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); -#define HAL_timer_isr_prologue(TIMER_NUM) -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/LINUX/watchdog.cpp b/Marlin/src/HAL/LINUX/watchdog.cpp deleted file mode 100644 index 84202e48b6c54..0000000000000 --- a/Marlin/src/HAL/LINUX/watchdog.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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_LINUX__ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout - -void watchdog_init() {} -void HAL_watchdog_refresh() {} - -#endif - -#endif // __PLAT_LINUX__ diff --git a/Marlin/src/HAL/LPC1768/HAL.cpp b/Marlin/src/HAL/LPC1768/HAL.cpp index cee9cfc5f744c..9ff3a6ba598f6 100644 --- a/Marlin/src/HAL/LPC1768/HAL.cpp +++ b/Marlin/src/HAL/LPC1768/HAL.cpp @@ -25,13 +25,9 @@ #include "../shared/Delay.h" #include "../../../gcode/parser.h" -#if ENABLED(USE_WATCHDOG) - #include "watchdog.h" -#endif - DefaultSerial1 USBSerial(false, UsbSerial); -uint32_t HAL_adc_reading = 0; +uint32_t MarlinHAL::adc_result = 0; // U8glib required functions extern "C" { @@ -41,8 +37,6 @@ extern "C" { void u8g_Delay(uint16_t val) { delay(val); } } -//************************// - // return free heap space int freeMemory() { char stack_end; @@ -54,33 +48,77 @@ int freeMemory() { return result; } -// scan command line for code -// return index into pin map array if found and the pin is valid. -// return dval if not found or not a valid pin. -int16_t PARSED_PIN_INDEX(const char code, const int16_t dval) { - const uint16_t val = (uint16_t)parser.intval(code, -1), port = val / 100, pin = val % 100; - const int16_t ind = (port < ((NUM_DIGITAL_PINS) >> 5) && pin < 32) ? ((port << 5) | pin) : -2; - return ind > -1 ? ind : dval; +void MarlinHAL::reboot() { NVIC_SystemReset(); } + +uint8_t MarlinHAL::get_reset_source() { + #if ENABLED(USE_WATCHDOG) + if (watchdog_timed_out()) return RST_WATCHDOG; + #endif + return RST_POWER_ON; } +void MarlinHAL::clear_reset_source() { watchdog_clear_timeout_flag(); } + void flashFirmware(const int16_t) { delay(500); // Give OS time to disconnect USB_Connect(false); // USB clear connection delay(1000); // Give OS time to notice - HAL_reboot(); + hal.reboot(); } -void HAL_clear_reset_source(void) { - TERN_(USE_WATCHDOG, watchdog_clear_timeout_flag()); -} +#if ENABLED(USE_WATCHDOG) -uint8_t HAL_get_reset_source(void) { - #if ENABLED(USE_WATCHDOG) - if (watchdog_timed_out()) return RST_WATCHDOG; - #endif - return RST_POWER_ON; -} + #include + + #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout + + void MarlinHAL::watchdog_init() { + #if ENABLED(WATCHDOG_RESET_MANUAL) + // We enable the watchdog timer, but only for the interrupt. + + // Configure WDT to only trigger an interrupt + // Disable WDT interrupt (just in case, to avoid triggering it!) + NVIC_DisableIRQ(WDT_IRQn); + + // We NEED memory barriers to ensure Interrupts are actually disabled! + // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) + __DSB(); + __ISB(); + + // Configure WDT to only trigger an interrupt + // Initialize WDT with the given parameters + WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_INT_ONLY); + + // Configure and enable WDT interrupt. + NVIC_ClearPendingIRQ(WDT_IRQn); + NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups + NVIC_EnableIRQ(WDT_IRQn); + #else + WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_RESET); + #endif + WDT_Start(WDT_TIMEOUT_US); + } + + void MarlinHAL::watchdog_refresh() { + WDT_Feed(); + #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) + TOGGLE(LED_PIN); // heartbeat indicator + #endif + } -void HAL_reboot() { NVIC_SystemReset(); } + // Timeout state + bool MarlinHAL::watchdog_timed_out() { return TEST(WDT_ReadTimeOutFlag(), 0); } + void MarlinHAL::watchdog_clear_timeout_flag() { WDT_ClrTimeOutFlag(); } + +#endif // USE_WATCHDOG + +// For M42/M43, scan command line for pin code +// return index into pin map array if found and the pin is valid. +// return dval if not found or not a valid pin. +int16_t PARSED_PIN_INDEX(const char code, const int16_t dval) { + const uint16_t val = (uint16_t)parser.intval(code, -1), port = val / 100, pin = val % 100; + const int16_t ind = (port < ((NUM_DIGITAL_PINS) >> 5) && pin < 32) ? ((port << 5) | pin) : -2; + return ind > -1 ? ind : dval; +} #endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/HAL.h b/Marlin/src/HAL/LPC1768/HAL.h index f241249804337..b0eeb983b4452 100644 --- a/Marlin/src/HAL/LPC1768/HAL.h +++ b/Marlin/src/HAL/LPC1768/HAL.h @@ -28,8 +28,6 @@ #define CPU_32_BIT -void HAL_init(); - #include #include #include @@ -40,19 +38,15 @@ extern "C" volatile uint32_t _millis; #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include "MarlinSerial.h" #include #include #include -// -// Default graphical display delays -// -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 +// ------------------------ +// Serial ports +// ------------------------ typedef ForwardSerial1Class< decltype(UsbSerial) > DefaultSerial1; extern DefaultSerial1 USBSerial; @@ -114,28 +108,12 @@ extern DefaultSerial1 USBSerial; // // Interrupts // -#define CRITICAL_SECTION_START() uint32_t primask = __get_PRIMASK(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_PRIMASK()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() -// -// Utility functions -// -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wunused-function" -#endif - -int freeMemory(); - -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() // -// ADC API +// ADC // #define ADC_MEDIAN_FILTER_SIZE (23) // Higher values increase step delay (phase shift), @@ -154,20 +132,9 @@ int freeMemory(); #define HAL_ADC_RESOLUTION 12 // 15 bit maximum, raw temperature is stored as int16_t #define HAL_ADC_FILTERED // Disable oversampling done in Marlin as ADC values already filtered in HAL -using FilteredADC = LPC176x::ADC; -extern uint32_t HAL_adc_reading; -[[gnu::always_inline]] inline void HAL_adc_start_conversion(const pin_t pin) { - HAL_adc_reading = FilteredADC::read(pin) >> (16 - HAL_ADC_RESOLUTION); // returns 16bit value, reduce to required bits -} -[[gnu::always_inline]] inline uint16_t HAL_adc_get_result() { - return HAL_adc_reading; -} - -#define HAL_adc_init() -#define HAL_ANALOG_SELECT(pin) FilteredADC::enable_channel(pin) -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() (true) +// +// Pin Mapping for M42, M43, M226 +// // Test whether the pin is valid constexpr bool VALID_PIN(const pin_t pin) { @@ -194,32 +161,107 @@ 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_IDLETASK 1 -void HAL_idletask(); +// ------------------------ +// Defines +// ------------------------ #define PLATFORM_M997_SUPPORT void flashFirmware(const int16_t); #define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment -/** - * set_pwm_frequency - * Set the frequency of the timer corresponding to the provided pin - * All Hardware PWM pins run at the same frequency and all - * Software PWM pins run at the same frequency - */ -void set_pwm_frequency(const pin_t pin, int f_desired); +// Default graphical display delays +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 -/** - * set_pwm_duty - * Set the PWM duty cycle of the provided pin to the provided value - * Optionally allows inverting the duty cycle [default = false] - * Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255] - */ -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); +// ------------------------ +// Free Memory Accessor +// ------------------------ + +#pragma GCC diagnostic push +#if GCC_VERSION <= 50000 + #pragma GCC diagnostic ignored "-Wunused-function" +#endif + +int freeMemory(); + +#pragma GCC diagnostic pop + +// ------------------------ +// MarlinHAL Class +// ------------------------ + +class MarlinHAL { +public: + + // Earliest possible init, before setup() + MarlinHAL() {} + + 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(ms); } + + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); + static bool watchdog_timed_out() IF_DISABLED(USE_WATCHDOG, { return false; }); + static void watchdog_clear_timeout_flag() IF_DISABLED(USE_WATCHDOG, {}); + + // Tasks, called from idle() + static void idletask(); + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source(); + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + using FilteredADC = LPC176x::ADC; + + // 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) { + FilteredADC::enable_channel(pin); + } + + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static uint32_t adc_result; + static void adc_start(const pin_t pin) { + adc_result = FilteredADC::read(pin) >> (16 - HAL_ADC_RESOLUTION); // returns 16bit value, reduce to required bits + } + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value() { return uint16_t(adc_result); } -// Reset source -void HAL_clear_reset_source(void); -uint8_t HAL_get_reset_source(void); + /** + * 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); -void HAL_reboot(); + /** + * Set the frequency of the timer corresponding to the provided pin + * All Hardware PWM pins will run at the same frequency and + * All Software PWM pins will 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/LPC1768/HAL_MinSerial.cpp b/Marlin/src/HAL/LPC1768/MinSerial.cpp similarity index 97% rename from Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp rename to Marlin/src/HAL/LPC1768/MinSerial.cpp index 57065c49ac834..7a1c038c0b0b6 100644 --- a/Marlin/src/HAL/LPC1768/HAL_MinSerial.cpp +++ b/Marlin/src/HAL/LPC1768/MinSerial.cpp @@ -26,7 +26,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) -#include "../shared/HAL_MinSerial.h" +#include "../shared/MinSerial.h" #include static void TX(char c) { _DBC(c); } diff --git a/Marlin/src/HAL/LPC1768/Servo.h b/Marlin/src/HAL/LPC1768/Servo.h index eb12fd20f4d87..f02f503a67daa 100644 --- a/Marlin/src/HAL/LPC1768/Servo.h +++ b/Marlin/src/HAL/LPC1768/Servo.h @@ -65,4 +65,5 @@ class libServo: public Servo { } }; -#define HAL_SERVO_LIB libServo +class libServo; +typedef libServo hal_servo_t; diff --git a/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp b/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp index 6570a599a4850..1991d79719599 100644 --- a/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp +++ b/Marlin/src/HAL/LPC1768/eeprom_sdcard.cpp @@ -90,15 +90,15 @@ bool PersistentStore::access_finish() { // to see errors that are happening in read_data / write_data static void debug_rw(const bool write, int &pos, const uint8_t *value, const size_t size, const FRESULT s, const size_t total=0) { #if ENABLED(DEBUG_SD_EEPROM_EMULATION) - PGM_P const rw_str = write ? PSTR("write") : PSTR("read"); + FSTR_P const rw_str = write ? F("write") : F("read"); SERIAL_CHAR(' '); - SERIAL_ECHOPGM_P(rw_str); + SERIAL_ECHOF(rw_str); SERIAL_ECHOLNPGM("_data(", pos, ",", *value, ",", size, ", ...)"); if (total) { SERIAL_ECHOPGM(" f_"); - SERIAL_ECHOPGM_P(rw_str); + SERIAL_ECHOF(rw_str); SERIAL_ECHOPGM("()=", s, "\n size=", size, "\n bytes_"); - SERIAL_ECHOLNPGM_P(write ? PSTR("written=") : PSTR("read="), total); + SERIAL_ECHOLNF(write ? F("written=") : F("read="), total); } else SERIAL_ECHOLNPGM(" f_lseek()=", s); diff --git a/Marlin/src/HAL/LPC1768/eeprom_wired.cpp b/Marlin/src/HAL/LPC1768/eeprom_wired.cpp index f9286a74ac88a..1bbc39d4a242f 100644 --- a/Marlin/src/HAL/LPC1768/eeprom_wired.cpp +++ b/Marlin/src/HAL/LPC1768/eeprom_wired.cpp @@ -34,7 +34,7 @@ #include "../shared/eeprom_api.h" #ifndef MARLIN_EEPROM_SIZE - #define MARLIN_EEPROM_SIZE 0x8000 // 32KB‬ + #define MARLIN_EEPROM_SIZE 0x8000 // 32K #endif size_t PersistentStore::capacity() { return MARLIN_EEPROM_SIZE; } diff --git a/Marlin/src/HAL/LPC1768/fast_pwm.cpp b/Marlin/src/HAL/LPC1768/fast_pwm.cpp index dd440b5e77307..6d2b1a9002c10 100644 --- a/Marlin/src/HAL/LPC1768/fast_pwm.cpp +++ b/Marlin/src/HAL/LPC1768/fast_pwm.cpp @@ -21,19 +21,17 @@ */ #ifdef TARGET_LPC1768 -#include "../../inc/MarlinConfigPre.h" - -#if NEEDS_HARDWARE_PWM // Specific meta-flag for features that mandate PWM - +#include "../../inc/MarlinConfig.h" #include -void set_pwm_frequency(const pin_t pin, int f_desired) { - LPC176x::pwm_set_frequency(pin, f_desired); +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 } -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { - LPC176x::pwm_write_ratio(pin, invert ? 1.0f - (float)v / v_size : (float)v / v_size); +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + LPC176x::pwm_set_frequency(pin, f_desired); } -#endif // NEEDS_HARDWARE_PWM #endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/inc/SanityCheck.h b/Marlin/src/HAL/LPC1768/inc/SanityCheck.h index 3ea054589ec35..8265d58a6e8fc 100644 --- a/Marlin/src/HAL/LPC1768/inc/SanityCheck.h +++ b/Marlin/src/HAL/LPC1768/inc/SanityCheck.h @@ -113,7 +113,7 @@ static_assert(DISABLED(BAUD_RATE_GCODE), "BAUD_RATE_GCODE is not yet supported o #define _IS_RX1_1 IS_RX1 #if IS_TX1(TMC_SW_SCK) #error "Serial port pins (1) conflict with other pins!" - #elif HAS_WIRED_LCD + #elif HAS_ROTARY_ENCODER #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) \ diff --git a/Marlin/src/HAL/LPC1768/include/SPI.h b/Marlin/src/HAL/LPC1768/include/SPI.h index ecd91f6a3b736..24f4759315bd8 100644 --- a/Marlin/src/HAL/LPC1768/include/SPI.h +++ b/Marlin/src/HAL/LPC1768/include/SPI.h @@ -77,7 +77,7 @@ class SPISettings { //uint32_t spiRate() const { return spi_speed; } - static inline uint32_t spiRate2Clock(uint32_t spiRate) { + static uint32_t spiRate2Clock(uint32_t spiRate) { uint32_t Marlin_speed[7]; // CPSR is always 2 Marlin_speed[0] = 8333333; //(SCR: 2) desired: 8,000,000 actual: 8,333,333 +4.2% SPI_FULL_SPEED Marlin_speed[1] = 4166667; //(SCR: 5) desired: 4,000,000 actual: 4,166,667 +4.2% SPI_HALF_SPEED diff --git a/Marlin/src/HAL/LPC1768/include/digipot_mcp4451_I2C_routines.c b/Marlin/src/HAL/LPC1768/include/digipot_mcp4451_I2C_routines.c index f442ab71c0bdb..c489c16e5ef6f 100644 --- a/Marlin/src/HAL/LPC1768/include/digipot_mcp4451_I2C_routines.c +++ b/Marlin/src/HAL/LPC1768/include/digipot_mcp4451_I2C_routines.c @@ -29,7 +29,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if MB(MKS_SBASE) +#if ENABLED(DIGIPOT_MCP4451) && MB(MKS_SBASE) #ifdef __cplusplus extern "C" { @@ -37,35 +37,6 @@ #include "digipot_mcp4451_I2C_routines.h" -// These two routines are exact copies of the lpc17xx_i2c.c routines. Couldn't link to -// to the lpc17xx_i2c.c routines so had to copy them into this file & rename them. - -static uint32_t _I2C_Start(LPC_I2C_TypeDef *I2Cx) { - // Reset STA, STO, SI - I2Cx->I2CONCLR = I2C_I2CONCLR_SIC|I2C_I2CONCLR_STOC|I2C_I2CONCLR_STAC; - - // Enter to Master Transmitter mode - I2Cx->I2CONSET = I2C_I2CONSET_STA; - - // Wait for complete - while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI)); - I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; - return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK); -} - -static void _I2C_Stop(LPC_I2C_TypeDef *I2Cx) { - // Make sure start bit is not active - if (I2Cx->I2CONSET & I2C_I2CONSET_STA) - I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; - - I2Cx->I2CONSET = I2C_I2CONSET_STO|I2C_I2CONSET_AA; - I2Cx->I2CONCLR = I2C_I2CONCLR_SIC; -} - -I2C_M_SETUP_Type transferMCfg; - -#define I2C_status (LPC_I2C1->I2STAT & I2C_STAT_CODE_BITMASK) - uint8_t digipot_mcp4451_start(uint8_t sla) { // send slave address and write bit // Sometimes TX data ACK or NAK status is returned. That mean the start state didn't // happen which means only the value of the slave address was send. Keep looping until @@ -102,5 +73,5 @@ uint8_t digipot_mcp4451_send_byte(uint8_t data) { } #endif -#endif // MB(MKS_SBASE) +#endif // DIGIPOT_MCP4451 && MKS_SBASE #endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/include/i2c_util.c b/Marlin/src/HAL/LPC1768/include/i2c_util.c index e52fb7c4de92e..4e24f23236eab 100644 --- a/Marlin/src/HAL/LPC1768/include/i2c_util.c +++ b/Marlin/src/HAL/LPC1768/include/i2c_util.c @@ -63,6 +63,32 @@ void configure_i2c(const uint8_t clock_option) { I2C_Cmd(I2CDEV_M, I2C_MASTER_MODE, ENABLE); } +////////////////////////////////////////////////////////////////////////////////////// +// These two routines are exact copies of the lpc17xx_i2c.c routines. Couldn't link to +// to the lpc17xx_i2c.c routines so had to copy them into this file & rename them. + +uint32_t _I2C_Start(LPC_I2C_TypeDef *I2Cx) { + // Reset STA, STO, SI + I2Cx->I2CONCLR = I2C_I2CONCLR_SIC|I2C_I2CONCLR_STOC|I2C_I2CONCLR_STAC; + + // Enter to Master Transmitter mode + I2Cx->I2CONSET = I2C_I2CONSET_STA; + + // Wait for complete + while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI)); + I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; + return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK); +} + +void _I2C_Stop(LPC_I2C_TypeDef *I2Cx) { + /* Make sure start bit is not active */ + if (I2Cx->I2CONSET & I2C_I2CONSET_STA) + I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; + + I2Cx->I2CONSET = I2C_I2CONSET_STO|I2C_I2CONSET_AA; + I2Cx->I2CONCLR = I2C_I2CONCLR_SIC; +} + #ifdef __cplusplus } #endif diff --git a/Marlin/src/HAL/LPC1768/include/i2c_util.h b/Marlin/src/HAL/LPC1768/include/i2c_util.h index a57f68a4071f9..1f1c19f2798b9 100644 --- a/Marlin/src/HAL/LPC1768/include/i2c_util.h +++ b/Marlin/src/HAL/LPC1768/include/i2c_util.h @@ -51,6 +51,11 @@ void configure_i2c(const uint8_t clock_option); +uint32_t _I2C_Start(LPC_I2C_TypeDef *I2Cx); +void _I2C_Stop(LPC_I2C_TypeDef *I2Cx); + +#define I2C_status (LPC_I2C1->I2STAT & I2C_STAT_CODE_BITMASK) + #ifdef __cplusplus } #endif diff --git a/Marlin/src/HAL/LPC1768/main.cpp b/Marlin/src/HAL/LPC1768/main.cpp index ef0dc42c78cad..419c99793fb8a 100644 --- a/Marlin/src/HAL/LPC1768/main.cpp +++ b/Marlin/src/HAL/LPC1768/main.cpp @@ -48,7 +48,7 @@ void SysTick_Callback() { disk_timerproc(); } TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); -void HAL_init() { +void MarlinHAL::init() { // Init LEDs #if PIN_EXISTS(LED) @@ -130,7 +130,7 @@ void HAL_init() { const millis_t usb_timeout = millis() + 2000; while (!USB_Configuration && PENDING(millis(), usb_timeout)) { delay(50); - HAL_idletask(); + idletask(); #if PIN_EXISTS(LED) TOGGLE(LED_PIN); // Flash quickly during USB initialization #endif @@ -142,7 +142,7 @@ void HAL_init() { } // HAL idle task -void HAL_idletask() { +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. diff --git a/Marlin/src/HAL/LPC1768/tft/xpt2046.h b/Marlin/src/HAL/LPC1768/tft/xpt2046.h index aba0799e445f8..7c456cf00e1be 100644 --- a/Marlin/src/HAL/LPC1768/tft/xpt2046.h +++ b/Marlin/src/HAL/LPC1768/tft/xpt2046.h @@ -65,8 +65,8 @@ class XPT2046 { static uint16_t getRawData(const XPTCoordinate coordinate); static bool isTouched(); - static inline void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); }; - static inline void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; + static void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); }; + static void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; #if ENABLED(TOUCH_BUTTONS_HW_SPI) static uint16_t HardwareIO(uint16_t data); #endif diff --git a/Marlin/src/HAL/LPC1768/timers.cpp b/Marlin/src/HAL/LPC1768/timers.cpp index a7a40584dabc3..bbb13f81da05c 100644 --- a/Marlin/src/HAL/LPC1768/timers.cpp +++ b/Marlin/src/HAL/LPC1768/timers.cpp @@ -40,7 +40,7 @@ void HAL_timer_init() { void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: LPC_TIM0->MCR = _BV(SBIT_MR0I) | _BV(SBIT_MR0R); // Match on MR0, reset on MR0, interrupts when NVIC enables them LPC_TIM0->MR0 = uint32_t(STEPPER_TIMER_RATE) / frequency; // Match value (period) to set frequency LPC_TIM0->TCR = _BV(SBIT_CNTEN); // Counter Enable @@ -49,7 +49,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { NVIC_EnableIRQ(TIMER0_IRQn); break; - case 1: + case MF_TIMER_TEMP: LPC_TIM1->MCR = _BV(SBIT_MR0I) | _BV(SBIT_MR0R); // Match on MR0, reset on MR0, interrupts when NVIC enables them LPC_TIM1->MR0 = uint32_t(TEMP_TIMER_RATE) / frequency; LPC_TIM1->TCR = _BV(SBIT_CNTEN); // Counter Enable diff --git a/Marlin/src/HAL/LPC1768/timers.h b/Marlin/src/HAL/LPC1768/timers.h index 4b638546856f5..c6d7bc632e2ed 100644 --- a/Marlin/src/HAL/LPC1768/timers.h +++ b/Marlin/src/HAL/LPC1768/timers.h @@ -60,17 +60,17 @@ typedef uint32_t hal_timer_t; #define HAL_TIMER_RATE ((F_CPU) / 4) // frequency of timers peripherals -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif -#ifndef PWM_TIMER_NUM - #define PWM_TIMER_NUM 3 // Timer Index for PWM +#ifndef MF_TIMER_PWM + #define MF_TIMER_PWM 3 // Timer Index for PWM #endif #define TEMP_TIMER_RATE 1000000 @@ -84,23 +84,23 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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(STEP_TIMER_NUM) + #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(TEMP_TIMER_NUM) + #define HAL_TEMP_TIMER_ISR() _HAL_TIMER_ISR(MF_TIMER_TEMP) #endif // Timer references by index -#define STEP_TIMER_PTR _HAL_TIMER(STEP_TIMER_NUM) -#define TEMP_TIMER_PTR _HAL_TIMER(TEMP_TIMER_NUM) +#define STEP_TIMER_PTR _HAL_TIMER(MF_TIMER_STEP) +#define TEMP_TIMER_PTR _HAL_TIMER(MF_TIMER_TEMP) // ------------------------ // Public functions @@ -110,38 +110,38 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { switch (timer_num) { - case 0: STEP_TIMER_PTR->MR0 = compare; break; // Stepper Timer Match Register 0 - case 1: TEMP_TIMER_PTR->MR0 = compare; break; // Temp Timer Match Register 0 + case MF_TIMER_STEP: STEP_TIMER_PTR->MR0 = compare; break; // Stepper Timer Match Register 0 + case MF_TIMER_TEMP: TEMP_TIMER_PTR->MR0 = compare; break; // Temp Timer Match Register 0 } } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { switch (timer_num) { - case 0: return STEP_TIMER_PTR->MR0; // Stepper Timer Match Register 0 - case 1: return TEMP_TIMER_PTR->MR0; // Temp Timer Match Register 0 + case MF_TIMER_STEP: return STEP_TIMER_PTR->MR0; // Stepper Timer Match Register 0 + case MF_TIMER_TEMP: return TEMP_TIMER_PTR->MR0; // Temp Timer Match Register 0 } return 0; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { switch (timer_num) { - case 0: return STEP_TIMER_PTR->TC; // Stepper Timer Count - case 1: return TEMP_TIMER_PTR->TC; // Temp Timer Count + case MF_TIMER_STEP: return STEP_TIMER_PTR->TC; // Stepper Timer Count + case MF_TIMER_TEMP: return TEMP_TIMER_PTR->TC; // Temp Timer Count } return 0; } FORCE_INLINE static void HAL_timer_enable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_EnableIRQ(TIMER0_IRQn); break; // Enable interrupt handler - case 1: NVIC_EnableIRQ(TIMER1_IRQn); break; // Enable interrupt handler + case MF_TIMER_STEP: NVIC_EnableIRQ(TIMER0_IRQn); break; // Enable interrupt handler + case MF_TIMER_TEMP: NVIC_EnableIRQ(TIMER1_IRQn); break; // Enable interrupt handler } } FORCE_INLINE static void HAL_timer_disable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_DisableIRQ(TIMER0_IRQn); break; // Disable interrupt handler - case 1: NVIC_DisableIRQ(TIMER1_IRQn); break; // Disable interrupt handler + case MF_TIMER_STEP: NVIC_DisableIRQ(TIMER0_IRQn); break; // Disable interrupt handler + case MF_TIMER_TEMP: NVIC_DisableIRQ(TIMER1_IRQn); break; // Disable interrupt handler } // We NEED memory barriers to ensure Interrupts are actually disabled! @@ -157,17 +157,17 @@ FORCE_INLINE static bool NVIC_GetEnableIRQ(IRQn_Type IRQn) { FORCE_INLINE static bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { switch (timer_num) { - case 0: return NVIC_GetEnableIRQ(TIMER0_IRQn); // Check if interrupt is enabled or not - case 1: return NVIC_GetEnableIRQ(TIMER1_IRQn); // Check if interrupt is enabled or not + case MF_TIMER_STEP: return NVIC_GetEnableIRQ(TIMER0_IRQn); // Check if interrupt is enabled or not + case MF_TIMER_TEMP: return NVIC_GetEnableIRQ(TIMER1_IRQn); // Check if interrupt is enabled or not } return false; } FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) { switch (timer_num) { - case 0: SBI(STEP_TIMER_PTR->IR, SBIT_CNTEN); break; - case 1: SBI(TEMP_TIMER_PTR->IR, SBIT_CNTEN); break; + case MF_TIMER_STEP: SBI(STEP_TIMER_PTR->IR, SBIT_CNTEN); break; + case MF_TIMER_TEMP: SBI(TEMP_TIMER_PTR->IR, SBIT_CNTEN); break; } } -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp b/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp index a48a820dc433d..e714c3c16da17 100644 --- a/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp +++ b/Marlin/src/HAL/LPC1768/u8g/LCD_I2C_routines.cpp @@ -36,40 +36,7 @@ extern int millis(); ////////////////////////////////////////////////////////////////////////////////////// -// These two routines are exact copies of the lpc17xx_i2c.c routines. Couldn't link to -// to the lpc17xx_i2c.c routines so had to copy them into this file & rename them. - -static uint32_t _I2C_Start(LPC_I2C_TypeDef *I2Cx) { - // Reset STA, STO, SI - I2Cx->I2CONCLR = I2C_I2CONCLR_SIC|I2C_I2CONCLR_STOC|I2C_I2CONCLR_STAC; - - // Enter to Master Transmitter mode - I2Cx->I2CONSET = I2C_I2CONSET_STA; - - // Wait for complete - while (!(I2Cx->I2CONSET & I2C_I2CONSET_SI)); - I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; - return (I2Cx->I2STAT & I2C_STAT_CODE_BITMASK); -} - -static void _I2C_Stop (LPC_I2C_TypeDef *I2Cx) { - /* Make sure start bit is not active */ - if (I2Cx->I2CONSET & I2C_I2CONSET_STA) - I2Cx->I2CONCLR = I2C_I2CONCLR_STAC; - - I2Cx->I2CONSET = I2C_I2CONSET_STO|I2C_I2CONSET_AA; - I2Cx->I2CONCLR = I2C_I2CONCLR_SIC; -} - -////////////////////////////////////////////////////////////////////////////////////// - -#define I2CDEV_S_ADDR 0x78 // from SSD1306 //actual address is 0x3C - shift left 1 with LSB set to 0 to indicate write - -#define BUFFER_SIZE 0x1 // only do single byte transfers with LCDs - -I2C_M_SETUP_Type transferMCfg; - -#define I2C_status (LPC_I2C1->I2STAT & I2C_STAT_CODE_BITMASK) +#define I2CDEV_S_ADDR 0x78 // From SSD1306 (actual address is 0x3C - shift left 1 with LSB set to 0 to indicate write) // Send slave address and write bit uint8_t u8g_i2c_start(const uint8_t sla) { @@ -115,7 +82,6 @@ uint8_t u8g_i2c_send_byte(uint8_t data) { void u8g_i2c_stop() { } - #ifdef __cplusplus } #endif diff --git a/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_st7920_sw_spi.cpp b/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_st7920_sw_spi.cpp index 039fa6769bbef..e159ebaa0ca8a 100644 --- a/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_st7920_sw_spi.cpp +++ b/Marlin/src/HAL/LPC1768/u8g/u8g_com_HAL_LPC1768_st7920_sw_spi.cpp @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if ENABLED(U8GLIB_ST7920) +#if IS_U8GLIB_ST7920 #include #include @@ -143,5 +143,5 @@ uint8_t u8g_com_HAL_LPC1768_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t ar return 1; } -#endif // U8GLIB_ST7920 +#endif // IS_U8GLIB_ST7920 #endif // TARGET_LPC1768 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 3308d03e79f90..f116a9b80aa67 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 @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfigPre.h" -#if HAS_MARLINUI_U8GLIB && DISABLED(U8GLIB_ST7920) +#if HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 #include #include "../../shared/HAL_SPI.h" @@ -205,5 +205,5 @@ uint8_t u8g_com_HAL_LPC1768_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, return 1; } -#endif // HAS_MARLINUI_U8GLIB && !U8GLIB_ST7920 +#endif // HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 #endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/LPC1768/upload_extra_script.py b/Marlin/src/HAL/LPC1768/upload_extra_script.py index fb3aaef7cd38c..efd46fdd63094 100755 --- a/Marlin/src/HAL/LPC1768/upload_extra_script.py +++ b/Marlin/src/HAL/LPC1768/upload_extra_script.py @@ -1,123 +1,135 @@ # -# sets output_port +# upload_extra_script.py +# set the output_port # if target_filename is found then that drive is used # else if target_drive is found then that drive is used # from __future__ import print_function -target_filename = "FIRMWARE.CUR" -target_drive = "REARM" +import pioutil +if pioutil.is_pio_build(): -import os,getpass,platform + target_filename = "FIRMWARE.CUR" + target_drive = "REARM" -current_OS = platform.system() -Import("env") + import platform -def print_error(e): - print('\nUnable to find destination disk (%s)\n' \ - 'Please select it in platformio.ini using the upload_port keyword ' \ - '(https://docs.platformio.org/en/latest/projectconf/section_env_upload.html) ' \ - 'or copy the firmware (.pio/build/%s/firmware.bin) manually to the appropriate disk\n' \ - %(e, env.get('PIOENV'))) + current_OS = platform.system() + Import("env") -def before_upload(source, target, env): - try: - # - # Find a disk for upload - # - upload_disk = 'Disk not found' - target_file_found = False - target_drive_found = False - if current_OS == 'Windows': - # - # 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 - from ctypes import windll + def print_error(e): + print('\nUnable to find destination disk (%s)\n' \ + 'Please select it in platformio.ini using the upload_port keyword ' \ + '(https://docs.platformio.org/en/latest/projectconf/section_env_upload.html) ' \ + 'or copy the firmware (.pio/build/%s/firmware.bin) manually to the appropriate disk\n' \ + %(e, env.get('PIOENV'))) - # getting list of drives - # https://stackoverflow.com/questions/827371/is-there-a-way-to-list-all-the-available-drive-letters-in-python - drives = [] - bitmask = windll.kernel32.GetLogicalDrives() - for letter in string.ascii_uppercase: - if bitmask & 1: - drives.append(letter) - bitmask >>= 1 + def before_upload(source, target, env): + try: + from pathlib import Path + # + # Find a disk for upload + # + upload_disk = 'Disk not found' + target_file_found = False + target_drive_found = False + if current_OS == 'Windows': + # + # 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 + from ctypes import windll + from pathlib import PureWindowsPath - for drive in drives: - 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)) - 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 - upload_disk = final_drive_name - if target_filename in volume_info: - if not target_file_found: - upload_disk = final_drive_name - target_file_found = True + # getting list of drives + # https://stackoverflow.com/questions/827371/is-there-a-way-to-list-all-the-available-drive-letters-in-python + drives = [] + bitmask = windll.kernel32.GetLogicalDrives() + for letter in string.ascii_uppercase: + if bitmask & 1: + drives.append(letter) + bitmask >>= 1 - elif current_OS == 'Linux': - # - # platformio.ini will accept this for a Linux upload port designation: 'upload_port = /media/media_name/drive' - # - drives = os.listdir(os.path.join(os.sep, 'media', getpass.getuser())) - if target_drive in drives: # If target drive is found, use it. - target_drive_found = True - upload_disk = os.path.join(os.sep, 'media', getpass.getuser(), target_drive) + os.sep - else: - for drive in drives: - try: - files = os.listdir(os.path.join(os.sep, 'media', getpass.getuser(), drive)) - except: - continue - else: - if target_filename in files: - upload_disk = os.path.join(os.sep, 'media', getpass.getuser(), drive) + os.sep - target_file_found = True - break - # - # set upload_port to drive if found - # + for drive in drives: + 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)) + 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 + upload_disk = PureWindowsPath(final_drive_name) + if target_filename in volume_info: + if not target_file_found: + upload_disk = PureWindowsPath(final_drive_name) + target_file_found = True - if target_file_found or target_drive_found: - env.Replace( - UPLOAD_FLAGS="-P$UPLOAD_PORT" - ) + elif current_OS == 'Linux': + # + # platformio.ini will accept this for a Linux upload port designation: 'upload_port = /media/media_name/drive' + # + import getpass + user = getpass.getuser() + mpath = Path('/media', user) + drives = [ x for x in mpath.iterdir() if x.is_dir() ] + if target_drive in drives: # If target drive is found, use it. + target_drive_found = True + upload_disk = mpath / target_drive + else: + for drive in drives: + try: + fpath = mpath / drive + filenames = [ x.name for x in fpath.iterdir() if x.is_file() ] + except: + continue + else: + if target_filename in filenames: + upload_disk = mpath / drive + target_file_found = True + break + # + # set upload_port to drive if found + # - elif current_OS == 'Darwin': # MAC - # - # platformio.ini will accept this for a OSX upload port designation: 'upload_port = /media/media_name/drive' - # - drives = os.listdir('/Volumes') # human readable names - if target_drive in drives and not target_file_found: # set upload if not found target file yet - target_drive_found = True - upload_disk = '/Volumes/' + target_drive + '/' - for drive in drives: - try: - filenames = os.listdir('/Volumes/' + drive + '/') # will get an error if the drive is protected - except: - continue - else: - if target_filename in filenames: - if not target_file_found: - upload_disk = '/Volumes/' + drive + '/' - target_file_found = True + if target_file_found or target_drive_found: + env.Replace( + UPLOAD_FLAGS="-P$UPLOAD_PORT" + ) - # - # Set upload_port to drive if found - # - if target_file_found or target_drive_found: - env.Replace(UPLOAD_PORT=upload_disk) - print('\nUpload disk: ', upload_disk, '\n') - else: - print_error('Autodetect Error') + elif current_OS == 'Darwin': # MAC + # + # platformio.ini will accept this for a OSX upload port designation: 'upload_port = /media/media_name/drive' + # + dpath = Path('/Volumes') # human readable names + drives = [ x for x in dpath.iterdir() if x.is_dir() ] + if target_drive in drives and not target_file_found: # set upload if not found target file yet + target_drive_found = True + upload_disk = dpath / target_drive + for drive in drives: + try: + fpath = dpath / drive # will get an error if the drive is protected + filenames = [ x.name for x in fpath.iterdir() if x.is_file() ] + except: + continue + else: + if target_filename in filenames: + upload_disk = dpath / drive + target_file_found = True + break - except Exception as e: - print_error(str(e)) + # + # Set upload_port to drive if found + # + if target_file_found or target_drive_found: + env.Replace(UPLOAD_PORT=str(upload_disk)) + print('\nUpload disk: ', upload_disk, '\n') + else: + print_error('Autodetect Error') -env.AddPreAction("upload", before_upload) + except Exception as e: + print_error(str(e)) + + env.AddPreAction("upload", before_upload) diff --git a/Marlin/src/HAL/LPC1768/watchdog.cpp b/Marlin/src/HAL/LPC1768/watchdog.cpp deleted file mode 100644 index f23ccf5b512d1..0000000000000 --- a/Marlin/src/HAL/LPC1768/watchdog.cpp +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 TARGET_LPC1768 - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include -#include "watchdog.h" - -#define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout - -void watchdog_init() { - #if ENABLED(WATCHDOG_RESET_MANUAL) - // We enable the watchdog timer, but only for the interrupt. - - // Configure WDT to only trigger an interrupt - // Disable WDT interrupt (just in case, to avoid triggering it!) - NVIC_DisableIRQ(WDT_IRQn); - - // We NEED memory barriers to ensure Interrupts are actually disabled! - // ( https://dzone.com/articles/nvic-disabling-interrupts-on-arm-cortex-m-and-the ) - __DSB(); - __ISB(); - - // Configure WDT to only trigger an interrupt - // Initialize WDT with the given parameters - WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_INT_ONLY); - - // Configure and enable WDT interrupt. - NVIC_ClearPendingIRQ(WDT_IRQn); - NVIC_SetPriority(WDT_IRQn, 0); // Use highest priority, so we detect all kinds of lockups - NVIC_EnableIRQ(WDT_IRQn); - #else - WDT_Init(WDT_CLKSRC_IRC, WDT_MODE_RESET); - #endif - WDT_Start(WDT_TIMEOUT_US); -} - -void HAL_watchdog_refresh() { - WDT_Feed(); - #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) - TOGGLE(LED_PIN); // heartbeat indicator - #endif -} - -// Timeout state -bool watchdog_timed_out() { return TEST(WDT_ReadTimeOutFlag(), 0); } -void watchdog_clear_timeout_flag() { WDT_ClrTimeOutFlag(); } - -#endif // USE_WATCHDOG -#endif // TARGET_LPC1768 diff --git a/Marlin/src/HAL/NATIVE_SIM/HAL.h b/Marlin/src/HAL/NATIVE_SIM/HAL.h index 235c24808cb4b..66203611447f6 100644 --- a/Marlin/src/HAL/NATIVE_SIM/HAL.h +++ b/Marlin/src/HAL/NATIVE_SIM/HAL.h @@ -21,18 +21,10 @@ */ #pragma once -#define CPU_32_BIT -#define HAL_IDLETASK -void HAL_idletask(); - -#define F_CPU 100000000 -#define SystemCoreClock F_CPU #include #include - #undef min #undef max - #include #include "pinmapping.h" @@ -40,8 +32,6 @@ void _printf (const char *format, ...); void _putc(uint8_t c); uint8_t _getc(); -//extern "C" volatile uint32_t _millis; - //arduino: Print.h #define DEC 10 #define HEX 16 @@ -55,10 +45,25 @@ uint8_t _getc(); #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include "serial.h" -#define SHARED_SERVOS HAS_SERVOS +// ------------------------ +// Defines +// ------------------------ + +#define CPU_32_BIT +#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +#define F_CPU 100000000 +#define SystemCoreClock F_CPU + +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 + +// ------------------------ +// Serial ports +// ------------------------ extern MSerialT serial_stream_0; extern MSerialT serial_stream_1; @@ -98,44 +103,19 @@ extern MSerialT serial_stream_3; #endif #endif - -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 - -// +// ------------------------ // Interrupts -// +// ------------------------ + #define CRITICAL_SECTION_START() #define CRITICAL_SECTION_END() -#define ISRS_ENABLED() -#define ENABLE_ISRS() -#define DISABLE_ISRS() - -inline void HAL_init() {} - -// Utility functions -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" -int freeMemory(); -#pragma GCC diagnostic pop +// ------------------------ // ADC +// ------------------------ + #define HAL_ADC_VREF 5.0 #define HAL_ADC_RESOLUTION 10 -#define HAL_ANALOG_SELECT(ch) HAL_adc_enable_channel(ch) -#define HAL_START_ADC(ch) HAL_adc_start_conversion(ch) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() true - -void HAL_adc_init(); -void HAL_adc_enable_channel(const uint8_t ch); -void HAL_adc_start_conversion(const uint8_t ch); -uint16_t HAL_adc_get_result(); - -// Reset source -inline void HAL_clear_reset_source(void) {} -inline uint8_t HAL_get_reset_source(void) { return RST_POWER_ON; } /* ---------------- Delay in cycles */ @@ -154,29 +134,22 @@ constexpr inline std::size_t strlen_constexpr(const char* str) { // https://github.com/gcc-mirror/gcc/blob/5c7634a0e5f202935aa6c11b6ea953b8bf80a00a/libstdc%2B%2B-v3/include/bits/char_traits.h#L329 if (str != nullptr) { std::size_t i = 0; - while (str[i] != '\0') { - ++i; - } - + while (str[i] != '\0') ++i; return i; } - return 0; } constexpr inline int strncmp_constexpr(const char* lhs, const char* rhs, std::size_t count) { // https://github.com/gcc-mirror/gcc/blob/13b9cbfc32fe3ac4c81c4dd9c42d141c8fb95db4/libstdc%2B%2B-v3/include/bits/char_traits.h#L655 - if (lhs == nullptr || rhs == nullptr) { + if (lhs == nullptr || rhs == nullptr) return rhs != nullptr ? -1 : 1; - } - for (std::size_t i = 0; i < count; ++i) { - if (lhs[i] != rhs[i]) { + for (std::size_t i = 0; i < count; ++i) + if (lhs[i] != rhs[i]) return lhs[i] < rhs[i] ? -1 : 1; - } else if (lhs[i] == '\0') { + else if (lhs[i] == '\0') return 0; - } - } return 0; } @@ -188,14 +161,11 @@ constexpr inline const char* strstr_constexpr(const char* str, const char* targe do { char sc = {}; do { - if ((sc = *str++) == '\0') { - return nullptr; - } + if ((sc = *str++) == '\0') return nullptr; } while (sc != c); } while (strncmp_constexpr(str, target, len) != 0); --str; } - return str; } @@ -206,12 +176,91 @@ constexpr inline char* strstr_constexpr(char* str, const char* target) { do { char sc = {}; do { - if ((sc = *str++) == '\0') { - return nullptr; - } + if ((sc = *str++) == '\0') return nullptr; } while (sc != c); } while (strncmp_constexpr(str, target, len) != 0); --str; } return str; } + +// ------------------------ +// Free Memory Accessor +// ------------------------ + +#pragma GCC diagnostic push +#if GCC_VERSION <= 50000 + #pragma GCC diagnostic ignored "-Wunused-function" +#endif + +int freeMemory(); + +#pragma GCC diagnostic pop + +// ------------------------ +// 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 true; } + static void isr_on() {} + static void isr_off() {} + + static void delay_ms(const int ms) { _delay_ms(ms); } + + // Tasks, called from idle() + static void idletask(); + + // Reset + static constexpr uint8_t reset_reason = RST_POWER_ON; + static uint8_t get_reset_source() { return reset_reason; } + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static uint8_t active_ch; + + // 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 uint8_t ch); + + // Begin ADC sampling on the given channel. Called from Temperature::isr! + static void adc_start(const uint8_t ch); + + // Is the ADC ready for reading? + static bool adc_ready(); + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * 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] + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { + analogWrite(pin, v); + } + +}; diff --git a/Marlin/src/HAL/NATIVE_SIM/tft/xpt2046.h b/Marlin/src/HAL/NATIVE_SIM/tft/xpt2046.h index 9ef1816c7b169..b131853643a8b 100644 --- a/Marlin/src/HAL/NATIVE_SIM/tft/xpt2046.h +++ b/Marlin/src/HAL/NATIVE_SIM/tft/xpt2046.h @@ -62,8 +62,8 @@ class XPT2046 { static uint16_t getRawData(const XPTCoordinate coordinate); static bool isTouched(); - static inline void DataTransferBegin(); - static inline void DataTransferEnd(); + static void DataTransferBegin(); + static void DataTransferEnd(); #if ENABLED(TOUCH_BUTTONS_HW_SPI) static uint16_t HardwareIO(uint16_t data); #endif diff --git a/Marlin/src/HAL/NATIVE_SIM/timers.h b/Marlin/src/HAL/NATIVE_SIM/timers.h index c61eb29e76cfc..be38d583b6861 100644 --- a/Marlin/src/HAL/NATIVE_SIM/timers.h +++ b/Marlin/src/HAL/NATIVE_SIM/timers.h @@ -37,17 +37,17 @@ typedef uint64_t hal_timer_t; #define HAL_TIMER_RATE ((SystemCoreClock) / 4) // frequency of timers peripherals -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif -#ifndef SYSTICK_TIMER_NUM - #define SYSTICK_TIMER_NUM 2 // Timer Index for Systick +#ifndef MF_TIMER_SYSTICK + #define MF_TIMER_SYSTICK 2 // Timer Index for Systick #endif #define SYSTICK_TIMER_FREQUENCY 1000 @@ -62,12 +62,12 @@ typedef uint64_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() extern "C" void TIMER0_IRQHandler() @@ -87,5 +87,5 @@ void HAL_timer_enable_interrupt(const uint8_t timer_num); void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); -#define HAL_timer_isr_prologue(TIMER_NUM) -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP 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 3b5acc1656cd7..91b7e0f67f76f 100644 --- a/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp +++ b/Marlin/src/HAL/NATIVE_SIM/u8g/LCD_pin_routines.cpp @@ -38,13 +38,13 @@ #ifdef __cplusplus extern "C" { #endif -void u8g_SetPinOutput(uint8_t internal_pin_number){SET_DIR_OUTPUT(internal_pin_number);} -void u8g_SetPinInput(uint8_t internal_pin_number){SET_DIR_INPUT(internal_pin_number);} -void u8g_SetPinLevel(uint8_t pin, uint8_t pin_status){WRITE_PIN(pin, pin_status);} -uint8_t u8g_GetPinLevel(uint8_t pin){return READ_PIN(pin);} -void usleep(uint64_t microsec){ -assert(false); // why we here? -} + +void u8g_SetPinOutput(uint8_t internal_pin_number) { SET_DIR_OUTPUT(internal_pin_number); } +void u8g_SetPinInput(uint8_t internal_pin_number) { SET_DIR_INPUT(internal_pin_number); } +void u8g_SetPinLevel(uint8_t pin, uint8_t pin_status) { WRITE_PIN(pin, pin_status); } +uint8_t u8g_GetPinLevel(uint8_t pin) { return READ_PIN(pin); } +void usleep(uint64_t microsec) { assert(false); /* why we here? */ } + #ifdef __cplusplus } #endif 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 c77c3d30f09a3..c384cdd75185a 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 @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfig.h" -#if ENABLED(U8GLIB_ST7920) +#if IS_U8GLIB_ST7920 #include #include "../../shared/Delay.h" @@ -167,5 +167,5 @@ uint8_t u8g_com_ST7920_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void } #endif -#endif // U8GLIB_ST7920 +#endif // IS_U8GLIB_ST7920 #endif // TARGET_LPC1768 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 085954803cf2e..7be84580b1331 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 @@ -57,7 +57,7 @@ #include "../../../inc/MarlinConfig.h" -#if HAS_MARLINUI_U8GLIB && DISABLED(U8GLIB_ST7920) +#if HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 #undef SPI_SPEED #define SPI_SPEED 2 // About 2 MHz @@ -208,8 +208,11 @@ uint8_t u8g_com_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_pt } #endif -#elif !ANY(TFT_COLOR_UI, TFT_CLASSIC_UI, TFT_LVGL_UI, HAS_MARLINUI_HD44780) && HAS_MARLINUI_U8GLIB +#elif NONE(TFT_COLOR_UI, TFT_CLASSIC_UI, TFT_LVGL_UI, HAS_MARLINUI_HD44780) && HAS_MARLINUI_U8GLIB + #include uint8_t u8g_com_sw_spi_fn(u8g_t *u8g, uint8_t msg, uint8_t arg_val, void *arg_ptr) {return 0;} -#endif // HAS_MARLINUI_U8GLIB && !U8GLIB_ST7920 + +#endif // HAS_MARLINUI_U8GLIB && !IS_U8GLIB_ST7920 + #endif // __PLAT_NATIVE_SIM__ diff --git a/Marlin/src/HAL/SAMD51/HAL.cpp b/Marlin/src/HAL/SAMD51/HAL.cpp index 8baad31bc7512..bd1c98bfa1d9a 100644 --- a/Marlin/src/HAL/SAMD51/HAL.cpp +++ b/Marlin/src/HAL/SAMD51/HAL.cpp @@ -42,10 +42,6 @@ #endif #endif -// ------------------------ -// Local defines -// ------------------------ - #define GET_TEMP_0_ADC() TERN(HAS_TEMP_ADC_0, PIN_TO_ADC(TEMP_0_PIN), -1) #define GET_TEMP_1_ADC() TERN(HAS_TEMP_ADC_1, PIN_TO_ADC(TEMP_1_PIN), -1) #define GET_TEMP_2_ADC() TERN(HAS_TEMP_ADC_2, PIN_TO_ADC(TEMP_2_PIN), -1) @@ -54,22 +50,28 @@ #define GET_TEMP_5_ADC() TERN(HAS_TEMP_ADC_5, PIN_TO_ADC(TEMP_5_PIN), -1) #define GET_TEMP_6_ADC() TERN(HAS_TEMP_ADC_6, PIN_TO_ADC(TEMP_6_PIN), -1) #define GET_TEMP_7_ADC() TERN(HAS_TEMP_ADC_7, PIN_TO_ADC(TEMP_7_PIN), -1) -#define GET_PROBE_ADC() TERN(HAS_TEMP_PROBE, PIN_TO_ADC(TEMP_PROBE_PIN), -1) #define GET_BED_ADC() TERN(HAS_TEMP_ADC_BED, PIN_TO_ADC(TEMP_BED_PIN), -1) #define GET_CHAMBER_ADC() TERN(HAS_TEMP_ADC_CHAMBER, PIN_TO_ADC(TEMP_CHAMBER_PIN), -1) +#define GET_PROBE_ADC() TERN(HAS_TEMP_ADC_PROBE, PIN_TO_ADC(TEMP_PROBE_PIN), -1) #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_FILAMENT_WIDTH_ADC() TERN(FILAMENT_WIDTH_SENSOR, PIN_TO_ADC(FILWIDTH_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) +#define GET_JOY_ADC_Z() TERN(HAS_JOY_ADC_Z, PIN_TO_ADC(JOY_Z_PIN), -1) #define IS_ADC_REQUIRED(n) ( \ GET_TEMP_0_ADC() == n || GET_TEMP_1_ADC() == n || GET_TEMP_2_ADC() == n || GET_TEMP_3_ADC() == n \ || GET_TEMP_4_ADC() == n || GET_TEMP_5_ADC() == n || GET_TEMP_6_ADC() == n || GET_TEMP_7_ADC() == n \ - || GET_PROBE_ADC() == n \ - || GET_BED_ADC() == n \ - || GET_CHAMBER_ADC() == n \ - || GET_COOLER_ADC() == n \ + || GET_BED_ADC() == n \ + || GET_CHAMBER_ADC() == n \ + || GET_PROBE_ADC() == n \ + || GET_COOLER_ADC() == n \ + || GET_BOARD_ADC() == n \ || GET_FILAMENT_WIDTH_ADC() == n \ - || GET_BUTTONS_ADC() == n \ + || GET_BUTTONS_ADC() == n \ + || GET_JOY_ADC_X() == n || GET_JOY_ADC_Y() == n || GET_JOY_ADC_Z() == n \ ) #if IS_ADC_REQUIRED(0) @@ -89,6 +91,152 @@ #define DMA_IS_REQUIRED 1 #endif +enum ADCIndex { + #if GET_TEMP_0_ADC() == 0 + TEMP_0, + #endif + #if GET_TEMP_1_ADC() == 0 + TEMP_1, + #endif + #if GET_TEMP_2_ADC() == 0 + TEMP_2, + #endif + #if GET_TEMP_3_ADC() == 0 + TEMP_3, + #endif + #if GET_TEMP_4_ADC() == 0 + TEMP_4, + #endif + #if GET_TEMP_5_ADC() == 0 + TEMP_5, + #endif + #if GET_TEMP_6_ADC() == 0 + TEMP_6, + #endif + #if GET_TEMP_7_ADC() == 0 + TEMP_7, + #endif + #if GET_BED_ADC() == 0 + TEMP_BED, + #endif + #if GET_CHAMBER_ADC() == 0 + TEMP_CHAMBER, + #endif + #if GET_PROBE_ADC() == 0 + TEMP_PROBE, + #endif + #if GET_COOLER_ADC() == 0 + TEMP_COOLER, + #endif + #if GET_BOARD_ADC() == 0 + TEMP_BOARD, + #endif + #if GET_FILAMENT_WIDTH_ADC() == 0 + FILWIDTH, + #endif + #if GET_BUTTONS_ADC() == 0 + ADC_KEY, + #endif + #if GET_JOY_ADC_X() == 0 + JOY_X, + #endif + #if GET_JOY_ADC_Y() == 0 + JOY_Y, + #endif + #if GET_JOY_ADC_Z() == 0 + JOY_Z, + #endif + #if GET_TEMP_0_ADC() == 1 + TEMP_0, + #endif + #if GET_TEMP_1_ADC() == 1 + TEMP_1, + #endif + #if GET_TEMP_2_ADC() == 1 + TEMP_2, + #endif + #if GET_TEMP_3_ADC() == 1 + TEMP_3, + #endif + #if GET_TEMP_4_ADC() == 1 + TEMP_4, + #endif + #if GET_TEMP_5_ADC() == 1 + TEMP_5, + #endif + #if GET_TEMP_6_ADC() == 1 + TEMP_6, + #endif + #if GET_TEMP_7_ADC() == 1 + TEMP_7, + #endif + #if GET_BED_ADC() == 1 + TEMP_BED, + #endif + #if GET_CHAMBER_ADC() == 1 + TEMP_CHAMBER, + #endif + #if GET_PROBE_ADC() == 1 + TEMP_PROBE, + #endif + #if GET_COOLER_ADC() == 1 + TEMP_COOLER, + #endif + #if GET_BOARD_ADC() == 1 + TEMP_BOARD, + #endif + #if GET_FILAMENT_WIDTH_ADC() == 1 + FILWIDTH, + #endif + #if GET_BUTTONS_ADC() == 1 + ADC_KEY, + #endif + #if GET_JOY_ADC_X() == 1 + JOY_X, + #endif + #if GET_JOY_ADC_Y() == 1 + JOY_Y, + #endif + #if GET_JOY_ADC_Z() == 1 + JOY_Z, + #endif + ADC_COUNT +}; + +#if ENABLED(USE_WATCHDOG) + + #define WDT_TIMEOUT_REG TERN(WATCHDOG_DURATION_8S, WDT_CONFIG_PER_CYC8192, WDT_CONFIG_PER_CYC4096) // 4 or 8 second timeout + + void MarlinHAL::watchdog_init() { + // The low-power oscillator used by the WDT runs at 32,768 Hz with + // a 1:32 prescale, thus 1024 Hz, though probably not super precise. + + // Setup WDT clocks + MCLK->APBAMASK.bit.OSC32KCTRL_ = true; + MCLK->APBAMASK.bit.WDT_ = true; + OSC32KCTRL->OSCULP32K.bit.EN1K = true; // Enable out 1K (this is what WDT uses) + + WDT->CTRLA.bit.ENABLE = false; // Disable watchdog for config + SYNC(WDT->SYNCBUSY.bit.ENABLE); + + WDT->INTENCLR.reg = WDT_INTENCLR_EW; // Disable early warning interrupt + WDT->CONFIG.reg = WDT_TIMEOUT_REG; // Set a 4s or 8s period for chip reset + + hal.watchdog_refresh(); + + WDT->CTRLA.reg = WDT_CTRLA_ENABLE; // Start watchdog now in normal mode + SYNC(WDT->SYNCBUSY.bit.ENABLE); + } + + // Reset watchdog. MUST be called at least every 4 seconds after the + // first watchdog_init or SAMD will go into emergency procedures. + void MarlinHAL::watchdog_refresh() { + SYNC(WDT->SYNCBUSY.bit.CLEAR); // Test first if previous is 'ongoing' to save time waiting for command execution + WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY; + } + +#endif + // ------------------------ // Types // ------------------------ @@ -106,12 +254,10 @@ // Private Variables // ------------------------ -uint16_t HAL_adc_result; - #if ADC_IS_REQUIRED // Pins used by ADC inputs. Order must be ADC0 inputs first then ADC1 - const uint8_t adc_pins[] = { + static constexpr uint8_t adc_pins[ADC_COUNT] = { // ADC0 pins #if GET_TEMP_0_ADC() == 0 TEMP_0_PIN, @@ -137,24 +283,36 @@ uint16_t HAL_adc_result; #if GET_TEMP_7_ADC() == 0 TEMP_7_PIN, #endif - #if GET_PROBE_ADC() == 0 - TEMP_PROBE_PIN, - #endif #if GET_BED_ADC() == 0 TEMP_BED_PIN, #endif #if GET_CHAMBER_ADC() == 0 TEMP_CHAMBER_PIN, #endif + #if GET_PROBE_ADC() == 0 + TEMP_PROBE_PIN, + #endif #if GET_COOLER_ADC() == 0 TEMP_COOLER_PIN, #endif + #if GET_BOARD_ADC() == 0 + TEMP_BOARD_PIN, + #endif #if GET_FILAMENT_WIDTH_ADC() == 0 FILWIDTH_PIN, #endif #if GET_BUTTONS_ADC() == 0 ADC_KEYPAD_PIN, #endif + #if GET_JOY_ADC_X() == 0 + JOY_X_PIN, + #endif + #if GET_JOY_ADC_Y() == 0 + JOY_Y_PIN, + #endif + #if GET_JOY_ADC_Z() == 0 + JOY_Z_PIN, + #endif // ADC1 pins #if GET_TEMP_0_ADC() == 1 TEMP_0_PIN, @@ -180,33 +338,44 @@ uint16_t HAL_adc_result; #if GET_TEMP_7_ADC() == 1 TEMP_7_PIN, #endif - #if GET_PROBE_ADC() == 1 - TEMP_PROBE_PIN, - #endif #if GET_BED_ADC() == 1 TEMP_BED_PIN, #endif #if GET_CHAMBER_ADC() == 1 TEMP_CHAMBER_PIN, #endif + #if GET_PROBE_ADC() == 1 + TEMP_PROBE_PIN, + #endif #if GET_COOLER_ADC() == 1 TEMP_COOLER_PIN, #endif + #if GET_BOARD_ADC() == 1 + TEMP_BOARD_PIN, + #endif #if GET_FILAMENT_WIDTH_ADC() == 1 FILWIDTH_PIN, #endif #if GET_BUTTONS_ADC() == 1 ADC_KEYPAD_PIN, #endif + #if GET_JOY_ADC_X() == 1 + JOY_X_PIN, + #endif + #if GET_JOY_ADC_Y() == 1 + JOY_Y_PIN, + #endif + #if GET_JOY_ADC_Z() == 1 + JOY_Z_PIN, + #endif }; - uint16_t HAL_adc_results[COUNT(adc_pins)]; + static uint16_t adc_results[ADC_COUNT]; #if ADC0_IS_REQUIRED - Adafruit_ZeroDMA adc0DMAProgram, - adc0DMARead; + Adafruit_ZeroDMA adc0DMAProgram, adc0DMARead; - const HAL_DMA_DAC_Registers adc0_dma_regs_list[] = { + static constexpr HAL_DMA_DAC_Registers adc0_dma_regs_list[ADC_COUNT] = { #if GET_TEMP_0_ADC() == 0 { PIN_TO_INPUTCTRL(TEMP_0_PIN) }, #endif @@ -231,34 +400,45 @@ uint16_t HAL_adc_result; #if GET_TEMP_7_ADC() == 0 { PIN_TO_INPUTCTRL(TEMP_7_PIN) }, #endif - #if GET_PROBE_ADC() == 0 - { PIN_TO_INPUTCTRL(TEMP_PROBE_PIN) }, - #endif #if GET_BED_ADC() == 0 { PIN_TO_INPUTCTRL(TEMP_BED_PIN) }, #endif #if GET_CHAMBER_ADC() == 0 { PIN_TO_INPUTCTRL(TEMP_CHAMBER_PIN) }, #endif + #if GET_PROBE_ADC() == 0 + { PIN_TO_INPUTCTRL(TEMP_PROBE_PIN) }, + #endif #if GET_COOLER_ADC() == 0 { PIN_TO_INPUTCTRL(TEMP_COOLER_PIN) }, #endif + #if GET_BOARD_ADC() == 0 + { PIN_TO_INPUTCTRL(TEMP_BOARD_PIN) }, + #endif #if GET_FILAMENT_WIDTH_ADC() == 0 { PIN_TO_INPUTCTRL(FILWIDTH_PIN) }, #endif #if GET_BUTTONS_ADC() == 0 { PIN_TO_INPUTCTRL(ADC_KEYPAD_PIN) }, #endif + #if GET_JOY_ADC_X() == 0 + { PIN_TO_INPUTCTRL(JOY_X_PIN) }, + #endif + #if GET_JOY_ADC_Y() == 0 + { PIN_TO_INPUTCTRL(JOY_Y_PIN) }, + #endif + #if GET_JOY_ADC_Z() == 0 + { PIN_TO_INPUTCTRL(JOY_Z_PIN) }, + #endif }; #define ADC0_AINCOUNT COUNT(adc0_dma_regs_list) #endif // ADC0_IS_REQUIRED #if ADC1_IS_REQUIRED - Adafruit_ZeroDMA adc1DMAProgram, - adc1DMARead; + Adafruit_ZeroDMA adc1DMAProgram, adc1DMARead; - const HAL_DMA_DAC_Registers adc1_dma_regs_list[] = { + static constexpr HAL_DMA_DAC_Registers adc1_dma_regs_list[ADC_COUNT] = { #if GET_TEMP_0_ADC() == 1 { PIN_TO_INPUTCTRL(TEMP_0_PIN) }, #endif @@ -283,24 +463,36 @@ uint16_t HAL_adc_result; #if GET_TEMP_7_ADC() == 1 { PIN_TO_INPUTCTRL(TEMP_7_PIN) }, #endif - #if GET_PROBE_ADC() == 1 - { PIN_TO_INPUTCTRL(TEMP_PROBE_PIN) }, - #endif #if GET_BED_ADC() == 1 { PIN_TO_INPUTCTRL(TEMP_BED_PIN) }, #endif #if GET_CHAMBER_ADC() == 1 { PIN_TO_INPUTCTRL(TEMP_CHAMBER_PIN) }, #endif + #if GET_PROBE_ADC() == 1 + { PIN_TO_INPUTCTRL(TEMP_PROBE_PIN) }, + #endif #if GET_COOLER_ADC() == 1 { PIN_TO_INPUTCTRL(TEMP_COOLER_PIN) }, #endif + #if GET_BOARD_ADC() == 1 + { PIN_TO_INPUTCTRL(TEMP_BOARD_PIN) }, + #endif #if GET_FILAMENT_WIDTH_ADC() == 1 { PIN_TO_INPUTCTRL(FILWIDTH_PIN) }, #endif #if GET_BUTTONS_ADC() == 1 { PIN_TO_INPUTCTRL(ADC_KEYPAD_PIN) }, #endif + #if GET_JOY_ADC_X() == 1 + { PIN_TO_INPUTCTRL(JOY_X_PIN) }, + #endif + #if GET_JOY_ADC_Y() == 1 + { PIN_TO_INPUTCTRL(JOY_Y_PIN) }, + #endif + #if GET_JOY_ADC_Z() == 1 + { PIN_TO_INPUTCTRL(JOY_Z_PIN) }, + #endif }; #define ADC1_AINCOUNT COUNT(adc1_dma_regs_list) @@ -312,9 +504,10 @@ uint16_t HAL_adc_result; // Private functions // ------------------------ -#if DMA_IS_REQUIRED +void MarlinHAL::dma_init() { + + #if DMA_IS_REQUIRED - void dma_init() { DmacDescriptor *descriptor; #if ADC0_IS_REQUIRED @@ -343,7 +536,7 @@ uint16_t HAL_adc_result; if (adc0DMARead.allocate() == DMA_STATUS_OK) { adc0DMARead.addDescriptor( (void *)&ADC0->RESULT.reg, // SRC - &HAL_adc_results, // DEST + &adc_results, // DEST ADC0_AINCOUNT, // CNT DMA_BEAT_SIZE_HWORD, false, // SRCINC @@ -380,7 +573,7 @@ uint16_t HAL_adc_result; if (adc1DMARead.allocate() == DMA_STATUS_OK) { adc1DMARead.addDescriptor( (void *)&ADC1->RESULT.reg, // SRC - &HAL_adc_results[ADC0_AINCOUNT], // DEST + &adc_results[ADC0_AINCOUNT], // DEST ADC1_AINCOUNT, // CNT DMA_BEAT_SIZE_HWORD, false, // SRCINC @@ -393,36 +586,28 @@ uint16_t HAL_adc_result; #endif DMAC->PRICTRL0.bit.RRLVLEN0 = true; // Activate round robin for DMA channels required by ADCs - } -#endif // DMA_IS_REQUIRED + #endif // DMA_IS_REQUIRED +} // ------------------------ // Public functions // ------------------------ // HAL initialization task -void HAL_init() { +void MarlinHAL::init() { TERN_(DMA_IS_REQUIRED, dma_init()); #if ENABLED(SDSUPPORT) - #if SD_CONNECTION_IS(ONBOARD) && PIN_EXISTS(SD_DETECT) + #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 #endif } -// HAL idle task -/* -void HAL_idletask() { -} -*/ - -void HAL_clear_reset_source() { } - #pragma push_macro("WDT") #undef WDT // Required to be able to use '.bit.WDT'. Compiler wrongly replace struct field with WDT define -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { RSTC_RCAUSE_Type resetCause; resetCause.reg = REG_RSTC_RCAUSE; @@ -436,7 +621,7 @@ uint8_t HAL_get_reset_source() { } #pragma pop_macro("WDT") -void HAL_reboot() { NVIC_SystemReset(); } +void MarlinHAL::reboot() { NVIC_SystemReset(); } extern "C" { void * _sbrk(int incr); @@ -454,9 +639,11 @@ int freeMemory() { // ADC // ------------------------ -void HAL_adc_init() { +uint16_t MarlinHAL::adc_result; + +void MarlinHAL::adc_init() { #if ADC_IS_REQUIRED - memset(HAL_adc_results, 0xFF, sizeof(HAL_adc_results)); // Fill result with invalid values + memset(adc_results, 0xFF, sizeof(adc_results)); // Fill result with invalid values LOOP_L_N(pi, COUNT(adc_pins)) pinPeripheral(adc_pins[pi], PIO_ANALOG); @@ -491,17 +678,13 @@ void HAL_adc_init() { #endif // ADC_IS_REQUIRED } -void HAL_adc_start_conversion(const uint8_t adc_pin) { +void MarlinHAL::adc_start(const pin_t pin) { #if ADC_IS_REQUIRED - LOOP_L_N(pi, COUNT(adc_pins)) { - if (adc_pin == adc_pins[pi]) { - HAL_adc_result = HAL_adc_results[pi]; - return; - } - } + LOOP_L_N(pi, COUNT(adc_pins)) + if (pin == adc_pins[pi]) { adc_result = adc_results[pi]; return; } #endif - HAL_adc_result = 0xFFFF; + adc_result = 0xFFFF; } #endif // __SAMD51__ diff --git a/Marlin/src/HAL/SAMD51/HAL.h b/Marlin/src/HAL/SAMD51/HAL.h index 491c3f82c4a67..79ba8021f4fdd 100644 --- a/Marlin/src/HAL/SAMD51/HAL.h +++ b/Marlin/src/HAL/SAMD51/HAL.h @@ -26,7 +26,6 @@ #include "../shared/math_32bit.h" #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #ifdef ADAFRUIT_GRAND_CENTRAL_M4 #include "MarlinSerial_AGCM4.h" @@ -89,46 +88,30 @@ typedef int8_t pin_t; -#define SHARED_SERVOS HAS_SERVOS -#define HAL_SERVO_LIB Servo +#define SHARED_SERVOS HAS_SERVOS // Use shared/servos.cpp + +class Servo; +typedef Servo hal_servo_t; // // Interrupts // -#define CRITICAL_SECTION_START() uint32_t primask = __get_PRIMASK(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_PRIMASK()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() - -#define cli() __disable_irq() // Disable interrupts -#define sei() __enable_irq() // Enable interrupts - -void HAL_clear_reset_source(); // clear reset reason -uint8_t HAL_get_reset_source(); // get reset reason +#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() -void HAL_reboot(); +#define cli() __disable_irq() // Disable interrupts +#define sei() __enable_irq() // Enable interrupts // // ADC // -extern uint16_t HAL_adc_result; // Most recent ADC conversion - -#define HAL_ANALOG_SELECT(pin) - -void HAL_adc_init(); //#define HAL_ADC_FILTERED // Disable Marlin's oversampling. The HAL filters ADC values. #define HAL_ADC_VREF 3.3 #define HAL_ADC_RESOLUTION 10 // ... 12 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_result -#define HAL_ADC_READY() true - -void HAL_adc_start_conversion(const uint8_t adc_pin); // -// Pin Map +// Pin Mapping for M42, M43, M226 // #define GET_PIN_MAP_PIN(index) index #define GET_PIN_MAP_INDEX(pin) pin @@ -137,37 +120,97 @@ void HAL_adc_start_conversion(const uint8_t adc_pin); // // Tone // -void toneInit(); void tone(const pin_t _pin, const unsigned int frequency, const unsigned long duration=0); void noTone(const pin_t _pin); -// Enable hooks into idle and setup for HAL -void HAL_init(); -/* -#define HAL_IDLETASK 1 -void HAL_idletask(); -*/ - -// -// Utility functions -// -FORCE_INLINE void _delay_ms(const int delay_ms) { delay(delay_ms); } +// ------------------------ +// Class Utilities +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif -int freeMemory(); - -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif - #ifdef __cplusplus extern "C" { #endif + char *dtostrf(double __val, signed char __width, unsigned char __prec, char *__s); + +extern "C" int freeMemory(); + #ifdef __cplusplus } #endif + +#pragma GCC diagnostic pop + +// ------------------------ +// 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() { 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() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static uint16_t adc_result; + + // 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 uint8_t ch) {} + + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static void adc_start(const pin_t pin); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The 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. + * 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); + } + +private: + static void dma_init(); +}; diff --git a/Marlin/src/HAL/SAMD51/Servo.cpp b/Marlin/src/HAL/SAMD51/Servo.cpp index 9bab8e89be23e..665322fe24bcc 100644 --- a/Marlin/src/HAL/SAMD51/Servo.cpp +++ b/Marlin/src/HAL/SAMD51/Servo.cpp @@ -53,7 +53,7 @@ static volatile int8_t currentServoIndex[_Nbr_16timers]; // index for the servo being pulsed for each timer (or -1 if refresh interval) FORCE_INLINE static uint16_t getTimerCount() { - Tc * const tc = TimerConfig[SERVO_TC].pTc; + Tc * const tc = timer_config[SERVO_TC].pTc; tc->COUNT16.CTRLBSET.reg = TC_CTRLBCLR_CMD_READSYNC; SYNC(tc->COUNT16.SYNCBUSY.bit.CTRLB || tc->COUNT16.SYNCBUSY.bit.COUNT); @@ -65,7 +65,7 @@ FORCE_INLINE static uint16_t getTimerCount() { // Interrupt handler for the TC // ---------------------------- HAL_SERVO_TIMER_ISR() { - Tc * const tc = TimerConfig[SERVO_TC].pTc; + Tc * const tc = timer_config[SERVO_TC].pTc; const timer16_Sequence_t timer = #ifndef _useTimer1 _timer2 @@ -77,7 +77,8 @@ HAL_SERVO_TIMER_ISR() { ; const uint8_t tcChannel = TIMER_TCCHANNEL(timer); - if (currentServoIndex[timer] < 0) { + int8_t cho = currentServoIndex[timer]; // Handle the prior servo first + if (cho < 0) { // Servo -1 indicates the refresh interval completed... #if defined(_useTimer1) && defined(_useTimer2) if (currentServoIndex[timer ^ 1] >= 0) { // Wait for both channels @@ -86,46 +87,38 @@ HAL_SERVO_TIMER_ISR() { return; } #endif - tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL; + tc->COUNT16.COUNT.reg = TC_COUNTER_START_VAL; // ...so reset the timer SYNC(tc->COUNT16.SYNCBUSY.bit.COUNT); } - else if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && SERVO(timer, currentServoIndex[timer]).Pin.isActive) - digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, LOW); // pulse this channel low if activated - - // Select the next servo controlled by this timer - currentServoIndex[timer]++; + else if (SERVO_INDEX(timer, cho) < ServoCount) // prior channel handled? + digitalWrite(SERVO(timer, cho).Pin.nbr, LOW); // pulse the prior channel LOW - if (SERVO_INDEX(timer, currentServoIndex[timer]) < ServoCount && currentServoIndex[timer] < SERVOS_PER_TIMER) { - if (SERVO(timer, currentServoIndex[timer]).Pin.isActive) // check if activated - digitalWrite(SERVO(timer, currentServoIndex[timer]).Pin.nbr, HIGH); // it's an active channel so pulse it high + currentServoIndex[timer] = ++cho; // go to the next channel (or 0) + if (cho < SERVOS_PER_TIMER && SERVO_INDEX(timer, cho) < ServoCount) { + if (SERVO(timer, cho).Pin.isActive) // activated? + digitalWrite(SERVO(timer, cho).Pin.nbr, HIGH); // yes: pulse HIGH - tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)SERVO(timer, currentServoIndex[timer]).ticks; + tc->COUNT16.CC[tcChannel].reg = getTimerCount() - (uint16_t)SERVO(timer, cho).ticks; } else { // finished all channels so wait for the refresh period to expire before starting over - currentServoIndex[timer] = -1; // this will get incremented at the end of the refresh period to start again at the first channel - - const uint16_t tcCounterValue = getTimerCount(); - - if ((TC_COUNTER_START_VAL - tcCounterValue) + 4UL < usToTicks(REFRESH_INTERVAL)) // allow a few ticks to ensure the next OCR1A not missed - tc->COUNT16.CC[tcChannel].reg = TC_COUNTER_START_VAL - (uint16_t)usToTicks(REFRESH_INTERVAL); - else - tc->COUNT16.CC[tcChannel].reg = (uint16_t)(tcCounterValue - 4UL); // at least REFRESH_INTERVAL has elapsed + currentServoIndex[timer] = -1; // reset the timer COUNT.reg on the next call + const uint16_t cval = getTimerCount() - 256 / (SERVO_TIMER_PRESCALER), // allow 256 cycles to ensure the next CV not missed + ival = (TC_COUNTER_START_VAL) - (uint16_t)usToTicks(REFRESH_INTERVAL); // at least REFRESH_INTERVAL has elapsed + tc->COUNT16.CC[tcChannel].reg = min(cval, ival); } if (tcChannel == 0) { SYNC(tc->COUNT16.SYNCBUSY.bit.CC0); - // Clear the interrupt - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; + tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC0; // Clear the interrupt } else { SYNC(tc->COUNT16.SYNCBUSY.bit.CC1); - // Clear the interrupt - tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC1; + tc->COUNT16.INTFLAG.reg = TC_INTFLAG_MC1; // Clear the interrupt } } -void initISR(timer16_Sequence_t timer) { - Tc * const tc = TimerConfig[SERVO_TC].pTc; +void initISR(const timer16_Sequence_t timer) { + Tc * const tc = timer_config[SERVO_TC].pTc; const uint8_t tcChannel = TIMER_TCCHANNEL(timer); static bool initialized = false; // Servo TC has been initialized @@ -201,9 +194,9 @@ void initISR(timer16_Sequence_t timer) { } } -void finISR(timer16_Sequence_t timer) { - Tc * const tc = TimerConfig[SERVO_TC].pTc; - const uint8_t tcChannel = TIMER_TCCHANNEL(timer); +void finISR(const timer16_Sequence_t timer_index) { + Tc * const tc = timer_config[SERVO_TC].pTc; + const uint8_t tcChannel = TIMER_TCCHANNEL(timer_index); // Disable the match channel interrupt request tc->COUNT16.INTENCLR.reg = (tcChannel == 0) ? TC_INTENCLR_MC0 : TC_INTENCLR_MC1; diff --git a/Marlin/src/HAL/SAMD51/inc/SanityCheck.h b/Marlin/src/HAL/SAMD51/inc/SanityCheck.h index 38c6dd9e08def..1b876c947dc7a 100644 --- a/Marlin/src/HAL/SAMD51/inc/SanityCheck.h +++ b/Marlin/src/HAL/SAMD51/inc/SanityCheck.h @@ -36,7 +36,7 @@ #error "OnBoard SPI BUS can't be shared with other devices." #endif -#if SERVO_TC == RTC_TIMER_NUM +#if SERVO_TC == MF_TIMER_RTC #error "Servos can't use RTC timer" #endif diff --git a/Marlin/src/HAL/SAMD51/timers.cpp b/Marlin/src/HAL/SAMD51/timers.cpp index 7a535299db9ab..1ad0e360736a1 100644 --- a/Marlin/src/HAL/SAMD51/timers.cpp +++ b/Marlin/src/HAL/SAMD51/timers.cpp @@ -31,13 +31,13 @@ // Local defines // -------------------------------------------------------------------------- -#define NUM_HARDWARE_TIMERS 8 +#define NUM_HARDWARE_TIMERS 9 // -------------------------------------------------------------------------- // Private Variables // -------------------------------------------------------------------------- -const tTimerConfig TimerConfig[NUM_HARDWARE_TIMERS+1] = { +const tTimerConfig timer_config[NUM_HARDWARE_TIMERS] = { { {.pTc=TC0}, TC0_IRQn, TC_PRIORITY(0) }, // 0 - stepper (assigned priority 2) { {.pTc=TC1}, TC1_IRQn, TC_PRIORITY(1) }, // 1 - stepper (needed by 32 bit timers) { {.pTc=TC2}, TC2_IRQn, 5 }, // 2 - tone (reserved by framework and fixed assigned priority 5) @@ -67,19 +67,19 @@ FORCE_INLINE void Disable_Irq(IRQn_Type irq) { // -------------------------------------------------------------------------- void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { - IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + IRQn_Type irq = timer_config[timer_num].IRQ_Id; // Disable interrupt, just in case it was already enabled Disable_Irq(irq); - if (timer_num == RTC_TIMER_NUM) { - Rtc * const rtc = TimerConfig[timer_num].pRtc; + if (timer_num == MF_TIMER_RTC) { + Rtc * const rtc = timer_config[timer_num].pRtc; // Disable timer interrupt rtc->MODE0.INTENCLR.reg = RTC_MODE0_INTENCLR_CMP0; // RTC clock setup - OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_XOSC32K; // External 32.768KHz oscillator + OSC32KCTRL->RTCCTRL.reg = OSC32KCTRL_RTCCTRL_RTCSEL_XOSC32K; // External 32.768kHz oscillator // Stop timer, just in case, to be able to reconfigure it rtc->MODE0.CTRLA.bit.ENABLE = false; @@ -101,7 +101,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { SYNC(rtc->MODE0.SYNCBUSY.bit.ENABLE); } else { - Tc * const tc = TimerConfig[timer_num].pTc; + Tc * const tc = timer_config[timer_num].pTc; // Disable timer interrupt tc->COUNT32.INTENCLR.reg = TC_INTENCLR_OVF; // disable overflow interrupt @@ -141,17 +141,17 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { } // Finally, enable IRQ - NVIC_SetPriority(irq, TimerConfig[timer_num].priority); + NVIC_SetPriority(irq, timer_config[timer_num].priority); NVIC_EnableIRQ(irq); } void HAL_timer_enable_interrupt(const uint8_t timer_num) { - const IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + const IRQn_Type irq = timer_config[timer_num].IRQ_Id; NVIC_EnableIRQ(irq); } void HAL_timer_disable_interrupt(const uint8_t timer_num) { - const IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + const IRQn_Type irq = timer_config[timer_num].IRQ_Id; Disable_Irq(irq); } @@ -161,7 +161,7 @@ static bool NVIC_GetEnabledIRQ(IRQn_Type IRQn) { } bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { - const IRQn_Type irq = TimerConfig[timer_num].IRQ_Id; + const IRQn_Type irq = timer_config[timer_num].IRQ_Id; return NVIC_GetEnabledIRQ(irq); } diff --git a/Marlin/src/HAL/SAMD51/timers.h b/Marlin/src/HAL/SAMD51/timers.h index dc6e38b7307cc..86e980c566903 100644 --- a/Marlin/src/HAL/SAMD51/timers.h +++ b/Marlin/src/HAL/SAMD51/timers.h @@ -25,21 +25,22 @@ // -------------------------------------------------------------------------- // Defines // -------------------------------------------------------------------------- -#define RTC_TIMER_NUM 8 // This is not a TC but a RTC typedef uint32_t hal_timer_t; #define HAL_TIMER_TYPE_MAX 0xFFFFFFFF #define HAL_TIMER_RATE F_CPU // frequency of timers peripherals -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#define MF_TIMER_RTC 8 // This is not a TC but a RTC + +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM RTC_TIMER_NUM // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP MF_TIMER_RTC // Timer Index for Temperature #endif #define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency @@ -52,30 +53,29 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#define ENABLE_TEMPERATURE_INTERRUPT() HAL_timer_enable_interrupt(MF_TIMER_TEMP) +#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(MF_TIMER_TEMP) -#define TC_PRIORITY(t) t == SERVO_TC ? 1 \ - : (t == STEP_TIMER_NUM || t == PULSE_TIMER_NUM) ? 2 \ - : (t == TEMP_TIMER_NUM) ? 6 \ - : 7 +#define TC_PRIORITY(t) ( t == SERVO_TC ? 1 \ + : (t == MF_TIMER_STEP || t == MF_TIMER_PULSE) ? 2 \ + : (t == MF_TIMER_TEMP) ? 6 : 7 ) #define _TC_HANDLER(t) void TC##t##_Handler() #define TC_HANDLER(t) _TC_HANDLER(t) #ifndef HAL_STEP_TIMER_ISR - #define HAL_STEP_TIMER_ISR() TC_HANDLER(STEP_TIMER_NUM) + #define HAL_STEP_TIMER_ISR() TC_HANDLER(MF_TIMER_STEP) #endif -#if STEP_TIMER_NUM != PULSE_TIMER_NUM - #define HAL_PULSE_TIMER_ISR() TC_HANDLER(PULSE_TIMER_NUM) +#if MF_TIMER_STEP != MF_TIMER_PULSE + #define HAL_PULSE_TIMER_ISR() TC_HANDLER(MF_TIMER_PULSE) #endif -#if TEMP_TIMER_NUM == RTC_TIMER_NUM +#if MF_TIMER_TEMP == MF_TIMER_RTC #define HAL_TEMP_TIMER_ISR() void RTC_Handler() #else - #define HAL_TEMP_TIMER_ISR() TC_HANDLER(TEMP_TIMER_NUM) + #define HAL_TEMP_TIMER_ISR() TC_HANDLER(MF_TIMER_TEMP) #endif // -------------------------------------------------------------------------- @@ -95,7 +95,7 @@ typedef struct { // Public Variables // -------------------------------------------------------------------------- -extern const tTimerConfig TimerConfig[]; +extern const tTimerConfig timer_config[]; // -------------------------------------------------------------------------- // Public functions @@ -104,20 +104,20 @@ extern const tTimerConfig TimerConfig[]; void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { - // Should never be called with timer RTC_TIMER_NUM - Tc * const tc = TimerConfig[timer_num].pTc; + // Should never be called with timer MF_TIMER_RTC + Tc * const tc = timer_config[timer_num].pTc; tc->COUNT32.CC[0].reg = compare; } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { - // Should never be called with timer RTC_TIMER_NUM - Tc * const tc = TimerConfig[timer_num].pTc; + // Should never be called with timer MF_TIMER_RTC + Tc * const tc = timer_config[timer_num].pTc; return (hal_timer_t)tc->COUNT32.CC[0].reg; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { - // Should never be called with timer RTC_TIMER_NUM - Tc * const tc = TimerConfig[timer_num].pTc; + // Should never be called with timer MF_TIMER_RTC + Tc * const tc = timer_config[timer_num].pTc; tc->COUNT32.CTRLBSET.reg = TC_CTRLBCLR_CMD_READSYNC; SYNC(tc->COUNT32.SYNCBUSY.bit.CTRLB || tc->COUNT32.SYNCBUSY.bit.COUNT); return tc->COUNT32.COUNT.reg; @@ -128,13 +128,13 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) { - if (timer_num == RTC_TIMER_NUM) { - Rtc * const rtc = TimerConfig[timer_num].pRtc; + if (timer_num == MF_TIMER_RTC) { + Rtc * const rtc = timer_config[timer_num].pRtc; // Clear interrupt flag rtc->MODE0.INTFLAG.reg = RTC_MODE0_INTFLAG_CMP0; } else { - Tc * const tc = TimerConfig[timer_num].pTc; + Tc * const tc = timer_config[timer_num].pTc; // Clear interrupt flag tc->COUNT32.INTFLAG.reg = TC_INTFLAG_OVF; } diff --git a/Marlin/src/HAL/SAMD51/watchdog.cpp b/Marlin/src/HAL/SAMD51/watchdog.cpp deleted file mode 100644 index 9de451836aebb..0000000000000 --- a/Marlin/src/HAL/SAMD51/watchdog.cpp +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * - * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] - * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician) - * - * 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 __SAMD51__ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#define WDT_TIMEOUT_REG TERN(WATCHDOG_DURATION_8S, WDT_CONFIG_PER_CYC8192, WDT_CONFIG_PER_CYC4096) // 4 or 8 second timeout - -void watchdog_init() { - // The low-power oscillator used by the WDT runs at 32,768 Hz with - // a 1:32 prescale, thus 1024 Hz, though probably not super precise. - - // Setup WDT clocks - MCLK->APBAMASK.bit.OSC32KCTRL_ = true; - MCLK->APBAMASK.bit.WDT_ = true; - OSC32KCTRL->OSCULP32K.bit.EN1K = true; // Enable out 1K (this is what WDT uses) - - WDT->CTRLA.bit.ENABLE = false; // Disable watchdog for config - SYNC(WDT->SYNCBUSY.bit.ENABLE); - - WDT->INTENCLR.reg = WDT_INTENCLR_EW; // Disable early warning interrupt - WDT->CONFIG.reg = WDT_TIMEOUT_REG; // Set a 4s or 8s period for chip reset - - HAL_watchdog_refresh(); - - WDT->CTRLA.reg = WDT_CTRLA_ENABLE; // Start watchdog now in normal mode - SYNC(WDT->SYNCBUSY.bit.ENABLE); -} - -#endif // USE_WATCHDOG - -#endif // __SAMD51__ diff --git a/Marlin/src/HAL/SAMD51/watchdog.h b/Marlin/src/HAL/SAMD51/watchdog.h deleted file mode 100644 index 2cd4788229b00..0000000000000 --- a/Marlin/src/HAL/SAMD51/watchdog.h +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * - * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] - * SAMD51 HAL developed by Giuliano Zaro (AKA GMagician) - * - * 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 - -// Initialize watchdog with a 4 second interrupt time -void watchdog_init(); - -// Reset watchdog. MUST be called at least every 4 seconds after the -// first watchdog_init or SAMD will go into emergency procedures. -inline void HAL_watchdog_refresh() { - SYNC(WDT->SYNCBUSY.bit.CLEAR); // Test first if previous is 'ongoing' to save time waiting for command execution - WDT->CLEAR.reg = WDT_CLEAR_CLEAR_KEY; -} diff --git a/Marlin/src/HAL/STM32/HAL.cpp b/Marlin/src/HAL/STM32/HAL.cpp index a04a24c1123c0..aff52f597f4df 100644 --- a/Marlin/src/HAL/STM32/HAL.cpp +++ b/Marlin/src/HAL/STM32/HAL.cpp @@ -24,14 +24,13 @@ #ifdef HAL_STM32 -#include "HAL.h" -#include "usb_serial.h" - #include "../../inc/MarlinConfig.h" #include "../shared/Delay.h" +#include "usb_serial.h" + #ifdef USBCON - DefaultSerial1 MSerial0(false, SerialUSB); + DefaultSerial1 MSerialUSB(false, SerialUSB); #endif #if ENABLED(SRAM_EEPROM_EMULATION) @@ -53,16 +52,18 @@ // Public Variables // ------------------------ -uint16_t HAL_adc_result; +uint16_t MarlinHAL::adc_result; // ------------------------ // Public functions // ------------------------ -TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); +#if ENABLED(POSTMORTEM_DEBUGGING) + extern void install_min_serial(); +#endif // HAL initialization task -void HAL_init() { +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. @@ -87,7 +88,7 @@ void HAL_init() { SetTimerInterruptPriorities(); - #if ENABLED(EMERGENCY_PARSER) && USBD_USE_CDC + #if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC) USB_Hook_init(); #endif @@ -103,7 +104,7 @@ void HAL_init() { } // HAL idle task -void HAL_idletask() { +void MarlinHAL::idletask() { #if HAS_SHARED_MEDIA // Stm32duino currently doesn't have a "loop/idle" method CDC_resume_receive(); @@ -111,9 +112,9 @@ void HAL_idletask() { #endif } -void HAL_clear_reset_source() { __HAL_RCC_CLEAR_RESET_FLAGS(); } +void MarlinHAL::reboot() { NVIC_SystemReset(); } -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { return #ifdef RCC_FLAG_IWDGRST // Some sources may not exist... RESET != __HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) ? RST_WATCHDOG : @@ -137,24 +138,37 @@ uint8_t HAL_get_reset_source() { ; } -void HAL_reboot() { NVIC_SystemReset(); } +void MarlinHAL::clear_reset_source() { __HAL_RCC_CLEAR_RESET_FLAGS(); } + +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout -void _delay_ms(const int delay_ms) { delay(delay_ms); } + #include + + void MarlinHAL::watchdog_init() { + IF_DISABLED(DISABLE_WATCHDOG_INIT, IWatchdog.begin(WDT_TIMEOUT_US)); + } + + void MarlinHAL::watchdog_refresh() { + IWatchdog.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 } -// ------------------------ -// ADC -// ------------------------ - -// TODO: Make sure this doesn't cause any delay -void HAL_adc_start_conversion(const uint8_t adc_pin) { HAL_adc_result = analogRead(adc_pin); } -uint16_t HAL_adc_get_result() { return HAL_adc_result; } - // Reset the system to initiate a firmware flash -void flashFirmware(const int16_t) { HAL_reboot(); } +WEAK void flashFirmware(const int16_t) { hal.reboot(); } // Maple Compatibility volatile uint32_t systick_uptime_millis = 0; diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h index a68e8a8c0e4af..3e85aca293d27 100644 --- a/Marlin/src/HAL/STM32/HAL.h +++ b/Marlin/src/HAL/STM32/HAL.h @@ -30,7 +30,6 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" #include "Servo.h" -#include "watchdog.h" #include "MarlinSerial.h" #include "../../inc/MarlinConfigPre.h" @@ -44,64 +43,74 @@ #define CPU_ST7920_DELAY_2 40 #define CPU_ST7920_DELAY_3 340 -// -// Serial Ports -// +// ------------------------ +// Serial ports +// ------------------------ #ifdef USBCON #include #include "../../core/serial_hook.h" typedef ForwardSerial1Class< decltype(SerialUSB) > DefaultSerial1; - extern DefaultSerial1 MSerial0; + extern DefaultSerial1 MSerialUSB; #endif #define _MSERIAL(X) MSerial##X #define MSERIAL(X) _MSERIAL(X) -#if SERIAL_PORT == -1 - #define MYSERIAL1 MSerial0 -#elif WITHIN(SERIAL_PORT, 1, 6) +#if WITHIN(SERIAL_PORT, 1, 6) #define MYSERIAL1 MSERIAL(SERIAL_PORT) +#elif !defined(USBCON) + #error "SERIAL_PORT must be from 1 to 6." +#elif SERIAL_PORT == -1 + #define MYSERIAL1 MSerialUSB #else - #error "SERIAL_PORT must be from 1 to 6. You can also use -1 if the board supports Native USB." + #error "SERIAL_PORT must be from 1 to 6, or -1 for Native USB." #endif #ifdef SERIAL_PORT_2 - #if SERIAL_PORT_2 == -1 - #define MYSERIAL2 MSerial0 - #elif WITHIN(SERIAL_PORT_2, 1, 6) + #if WITHIN(SERIAL_PORT_2, 1, 6) #define MYSERIAL2 MSERIAL(SERIAL_PORT_2) + #elif !defined(USBCON) + #error "SERIAL_PORT must be from 1 to 6." + #elif SERIAL_PORT_2 == -1 + #define MYSERIAL2 MSerialUSB #else - #error "SERIAL_PORT_2 must be from 1 to 6. You can also use -1 if the board supports Native USB." + #error "SERIAL_PORT_2 must be from 1 to 6, or -1 for Native USB." #endif #endif #ifdef SERIAL_PORT_3 - #if SERIAL_PORT_3 == -1 - #define MYSERIAL3 MSerial0 - #elif WITHIN(SERIAL_PORT_3, 1, 6) + #if WITHIN(SERIAL_PORT_3, 1, 6) #define MYSERIAL3 MSERIAL(SERIAL_PORT_3) + #elif !defined(USBCON) + #error "SERIAL_PORT must be from 1 to 6." + #elif SERIAL_PORT_3 == -1 + #define MYSERIAL3 MSerialUSB #else - #error "SERIAL_PORT_3 must be from 1 to 6. You can also use -1 if the board supports Native USB." + #error "SERIAL_PORT_3 must be from 1 to 6, or -1 for Native USB." #endif #endif #ifdef MMU2_SERIAL_PORT - #if MMU2_SERIAL_PORT == -1 - #define MMU2_SERIAL MSerial0 - #elif WITHIN(MMU2_SERIAL_PORT, 1, 6) + #if WITHIN(MMU2_SERIAL_PORT, 1, 6) #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) + #elif !defined(USBCON) + #error "SERIAL_PORT must be from 1 to 6." + #elif MMU2_SERIAL_PORT == -1 + #define MMU2_SERIAL MSerialUSB #else - #error "MMU2_SERIAL_PORT must be from 1 to 6. You can also use -1 if the board supports Native USB." + #error "MMU2_SERIAL_PORT must be from 1 to 6, or -1 for Native USB." #endif #endif #ifdef LCD_SERIAL_PORT - #if LCD_SERIAL_PORT == -1 - #define LCD_SERIAL MSerial0 - #elif WITHIN(LCD_SERIAL_PORT, 1, 6) + #if WITHIN(LCD_SERIAL_PORT, 1, 6) #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) + #elif !defined(USBCON) + #error "SERIAL_PORT must be from 1 to 6." + #elif LCD_SERIAL_PORT == -1 + #define LCD_SERIAL MSerialUSB #else - #error "LCD_SERIAL_PORT must be from 1 to 6. You can also use -1 if the board supports Native USB." + #error "LCD_SERIAL_PORT must be from 1 to 6, or -1 for Native USB." #endif #if HAS_DGUS_LCD #define SERIAL_GET_TX_BUFFER_FREE() LCD_SERIAL.availableForWrite() @@ -115,60 +124,81 @@ #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 ISRS_ENABLED() (!__get_PRIMASK()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() +// +// 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() -// On AVR this is in math.h? -#define square(x) ((x)*(x)) - // ------------------------ // Types // ------------------------ -typedef int16_t pin_t; +typedef double isr_float_t; // FPU ops are used for single-precision, so use double for ISRs. -#define HAL_SERVO_LIB libServo +#ifdef STM32G0B1xx + typedef int32_t pin_t; +#else + typedef int16_t pin_t; +#endif + +class libServo; +typedef libServo hal_servo_t; #define PAUSE_SERVO_OUTPUT() libServo::pause_all_servos() #define RESUME_SERVO_OUTPUT() libServo::resume_all_servos() // ------------------------ -// Public Variables +// ADC // ------------------------ -// result of last ADC conversion -extern uint16_t HAL_adc_result; +#ifdef ADC_RESOLUTION + #define HAL_ADC_RESOLUTION ADC_RESOLUTION +#else + #define HAL_ADC_RESOLUTION 12 +#endif -// ------------------------ -// Public functions -// ------------------------ +#define HAL_ADC_VREF 3.3 -// Memory related -#define __bss_end __bss_end__ +// +// 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) + +#ifdef STM32F1xx + #define JTAG_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_JTAGDISABLE) + #define JTAGSWD_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_DISABLE) + #define JTAGSWD_RESET() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_RESET); // Reset: FULL SWD+JTAG +#endif -// Enable hooks into setup for HAL -void HAL_init(); -#define HAL_IDLETASK 1 -void HAL_idletask(); +#define PLATFORM_M997_SUPPORT +void flashFirmware(const int16_t); + +// Maple Compatibility +typedef void (*systickCallback_t)(void); +void systick_attach_callback(systickCallback_t cb); +void HAL_SYSTICK_Callback(); -// Clear reset reason -void HAL_clear_reset_source(); +extern volatile uint32_t systick_uptime_millis; -// Reset reason -uint8_t HAL_get_reset_source(); +#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment -void HAL_reboot(); +// ------------------------ +// Class Utilities +// ------------------------ -void _delay_ms(const int delay); +// Memory related +#define __bss_end __bss_end__ extern "C" char* _sbrk(int incr); #pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" +#if GCC_VERSION <= 50000 + #pragma GCC diagnostic ignored "-Wunused-function" +#endif static inline int freeMemory() { volatile char top; @@ -177,62 +207,75 @@ static inline int freeMemory() { #pragma GCC diagnostic pop -// -// ADC -// +// ------------------------ +// MarlinHAL Class +// ------------------------ -#define HAL_ANALOG_SELECT(pin) pinMode(pin, INPUT) +class MarlinHAL { +public: -#ifdef ADC_RESOLUTION - #define HAL_ADC_RESOLUTION ADC_RESOLUTION -#else - #define HAL_ADC_RESOLUTION 12 -#endif + // Earliest possible init, before setup() + MarlinHAL() {} -#define HAL_ADC_VREF 3.3 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_result -#define HAL_ADC_READY() true + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -inline void HAL_adc_init() { analogReadResolution(HAL_ADC_RESOLUTION); } + static void init(); // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -void HAL_adc_start_conversion(const uint8_t adc_pin); + // Interrupts + static bool isr_state() { return !__get_PRIMASK(); } + static void isr_on() { sei(); } + static void isr_off() { cli(); } -uint16_t HAL_adc_get_result(); + static void delay_ms(const int ms) { delay(ms); } -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + // Tasks, called from idle() + static void idletask(); -#ifdef STM32F1xx - #define JTAG_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_JTAGDISABLE) - #define JTAGSWD_DISABLE() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_DISABLE) - #define JTAGSWD_RESET() AFIO_DBGAFR_CONFIG(AFIO_MAPR_SWJ_CFG_RESET); // Reset: FULL SWD+JTAG -#endif + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source(); -#define PLATFORM_M997_SUPPORT -void flashFirmware(const int16_t); + // Free SRAM + static int freeMemory() { return ::freeMemory(); } -// Maple Compatibility -typedef void (*systickCallback_t)(void); -void systick_attach_callback(systickCallback_t cb); -void HAL_SYSTICK_Callback(); + // + // ADC Methods + // -extern volatile uint32_t systick_uptime_millis; + static uint16_t adc_result; -#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment + // Called by Temperature::init once at startup + static void adc_init() { + analogReadResolution(HAL_ADC_RESOLUTION); + } -/** - * set_pwm_frequency - * Set the frequency of the timer corresponding to the provided pin - * All Timer PWM pins run at the same frequency - */ -void set_pwm_frequency(const pin_t pin, int f_desired); + // Called by Temperature::init for each sensor at startup + static void adc_enable(const pin_t pin) { pinMode(pin, INPUT); } -/** - * set_pwm_duty - * Set the PWM duty cycle of the provided pin to the provided value - * Optionally allows inverting the duty cycle [default = false] - * Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255] - */ -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static void adc_start(const pin_t pin) { adc_result = analogRead(pin); } + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The 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 v, const uint16_t v_size=255, 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/STM32/HAL_SPI.cpp b/Marlin/src/HAL/STM32/HAL_SPI.cpp index 8ee4761647856..40d320d5e8220 100644 --- a/Marlin/src/HAL/STM32/HAL_SPI.cpp +++ b/Marlin/src/HAL/STM32/HAL_SPI.cpp @@ -102,9 +102,9 @@ static SPISettings spiConfig; // Soft SPI receive byte uint8_t spiRec() { - DISABLE_ISRS(); // No interrupts during byte receive + hal.isr_off(); // No interrupts during byte receive const uint8_t data = HAL_SPI_STM32_SpiTransfer_Mode_3(0xFF); - ENABLE_ISRS(); // Enable interrupts + hal.isr_on(); // Enable interrupts return data; } @@ -116,9 +116,9 @@ static SPISettings spiConfig; // Soft SPI send byte void spiSend(uint8_t data) { - DISABLE_ISRS(); // No interrupts during byte send + hal.isr_off(); // No interrupts during byte send HAL_SPI_STM32_SpiTransfer_Mode_3(data); // Don't care what is received - ENABLE_ISRS(); // Enable interrupts + hal.isr_on(); // Enable interrupts } // Soft SPI send block diff --git a/Marlin/src/HAL/STM32/MarlinSPI.cpp b/Marlin/src/HAL/STM32/MarlinSPI.cpp index 7078d210dc4c0..f7c603d77e5cf 100644 --- a/Marlin/src/HAL/STM32/MarlinSPI.cpp +++ b/Marlin/src/HAL/STM32/MarlinSPI.cpp @@ -114,16 +114,19 @@ byte MarlinSPI::transfer(uint8_t _data) { return rxData; } +__STATIC_INLINE void LL_SPI_EnableDMAReq_RX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_RXDMAEN); } +__STATIC_INLINE void LL_SPI_EnableDMAReq_TX(SPI_TypeDef *SPIx) { SET_BIT(SPIx->CR2, SPI_CR2_TXDMAEN); } + uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16_t length) { const uint8_t ff = 0xFF; - //if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE) //only enable if disabled + //if (!LL_SPI_IsEnabled(_spi.handle)) // only enable if disabled __HAL_SPI_ENABLE(&_spi.handle); if (receiveBuf) { setupDma(_spi.handle, _dmaRx, DMA_PERIPH_TO_MEMORY, true); HAL_DMA_Start(&_dmaRx, (uint32_t)&(_spi.handle.Instance->DR), (uint32_t)receiveBuf, length); - SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_RXDMAEN); /* Enable Rx DMA Request */ + LL_SPI_EnableDMAReq_RX(_spi.handle.Instance); // Enable Rx DMA Request } // check for 2 lines transfer @@ -136,7 +139,7 @@ uint8_t MarlinSPI::dmaTransfer(const void *transmitBuf, void *receiveBuf, uint16 if (transmitBuf) { setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, mincTransmit); HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length); - SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_TXDMAEN); /* Enable Tx DMA Request */ + LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request } if (transmitBuf) { @@ -160,7 +163,7 @@ uint8_t MarlinSPI::dmaSend(const void * transmitBuf, uint16_t length, bool minc) setupDma(_spi.handle, _dmaTx, DMA_MEMORY_TO_PERIPH, minc); HAL_DMA_Start(&_dmaTx, (uint32_t)transmitBuf, (uint32_t)&(_spi.handle.Instance->DR), length); __HAL_SPI_ENABLE(&_spi.handle); - SET_BIT(_spi.handle.Instance->CR2, SPI_CR2_TXDMAEN); /* Enable Tx DMA Request */ + LL_SPI_EnableDMAReq_TX(_spi.handle.Instance); // Enable Tx DMA Request HAL_DMA_PollForTransfer(&_dmaTx, HAL_DMA_FULL_TRANSFER, HAL_MAX_DELAY); HAL_DMA_Abort(&_dmaTx); // DeInit objects diff --git a/Marlin/src/HAL/STM32/HAL_MinSerial.cpp b/Marlin/src/HAL/STM32/MinSerial.cpp similarity index 94% rename from Marlin/src/HAL/STM32/HAL_MinSerial.cpp rename to Marlin/src/HAL/STM32/MinSerial.cpp index 29826a890de41..b0fcff20c172a 100644 --- a/Marlin/src/HAL/STM32/HAL_MinSerial.cpp +++ b/Marlin/src/HAL/STM32/MinSerial.cpp @@ -28,8 +28,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) -#include "../shared/HAL_MinSerial.h" -#include "watchdog.h" +#include "../shared/MinSerial.h" /* Instruction Synchronization Barrier */ #define isb() __asm__ __volatile__ ("isb" : : : "memory") @@ -120,7 +119,7 @@ static void TX(char c) { #if WITHIN(SERIAL_PORT, 1, 6) constexpr uint32_t usart_sr_txe = _BV(7); while (!(regs->SR & usart_sr_txe)) { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); + hal.watchdog_refresh(); sw_barrier(); } regs->DR = c; @@ -135,7 +134,7 @@ void install_min_serial() { HAL_min_serial_out = &TX; } -#if DISABLED(DYNAMIC_VECTORTABLE) && DISABLED(STM32F0xx) // Cortex M0 can't jump to a symbol that's too far from the current function, so we work around this in exception_arm.cpp +#if NONE(DYNAMIC_VECTORTABLE, STM32F0xx, STM32G0xx) // Cortex M0 can't jump to a symbol that's too far from the current function, so we work around this in exception_arm.cpp extern "C" { __attribute__((naked)) void JumpHandler_ASM() { __asm__ __volatile__ ( diff --git a/Marlin/src/HAL/STM32/Sd2Card_sdio_stm32duino.cpp b/Marlin/src/HAL/STM32/Sd2Card_sdio_stm32duino.cpp deleted file mode 100644 index 54e1820c78e6b..0000000000000 --- a/Marlin/src/HAL/STM32/Sd2Card_sdio_stm32duino.cpp +++ /dev/null @@ -1,326 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 HAL_STM32 - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(SDIO_SUPPORT) - -#include -#include - -// use local drivers -#if defined(STM32F103xE) || defined(STM32F103xG) - #include - #include -#elif defined(STM32F4xx) - #include - #include - #include - #include -#elif defined(STM32F7xx) - #include - #include - #include - #include -#else - #error "SDIO only supported with STM32F103xE, STM32F103xG, STM32F4xx, or STM32F7xx." -#endif - -// Fixed -#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 - -SD_HandleTypeDef hsd; // create SDIO structure -// F4 supports one DMA for RX and another for TX, but Marlin will never -// do read and write at same time, so we use the same DMA for both. -DMA_HandleTypeDef hdma_sdio; - -/* - SDIO_INIT_CLK_DIV is 118 - SDIO clock frequency is 48MHz / (TRANSFER_CLOCK_DIV + 2) - SDIO init clock frequency should not exceed 400KHz = 48MHz / (118 + 2) - - Default TRANSFER_CLOCK_DIV is 2 (118 / 40) - Default SDIO clock frequency is 48MHz / (2 + 2) = 12 MHz - This might be too fast for stable SDIO operations - - MKS Robin board seems to have stable SDIO with BusWide 1bit and ClockDiv 8 i.e. 4.8MHz SDIO clock frequency - Additional testing is required as there are clearly some 4bit initialization problems -*/ - -#ifndef USBD_OK - #define USBD_OK 0 -#endif - -// Target Clock, configurable. Default is 18MHz, from STM32F1 -#ifndef SDIO_CLOCK - #define SDIO_CLOCK 18000000 // 18 MHz -#endif - -// SDIO retries, configurable. Default is 3, from STM32F1 -#ifndef SDIO_READ_RETRIES - #define SDIO_READ_RETRIES 3 -#endif - -// SDIO Max Clock (naming from STM Manual, don't change) -#define SDIOCLK 48000000 - -static uint32_t clock_to_divider(uint32_t clk) { - // limit the SDIO master clock to 8/3 of PCLK2. See STM32 Manuals - // Also limited to no more than 48Mhz (SDIOCLK). - const uint32_t pclk2 = HAL_RCC_GetPCLK2Freq(); - clk = min(clk, (uint32_t)(pclk2 * 8 / 3)); - clk = min(clk, (uint32_t)SDIOCLK); - // Round up divider, so we don't run the card over the speed supported, - // and subtract by 2, because STM32 will add 2, as written in the manual: - // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] - return pclk2 / clk + (pclk2 % clk != 0) - 2; -} - -void go_to_transfer_speed() { - /* Default SDIO peripheral configuration for SD card initialization */ - hsd.Init.ClockEdge = hsd.Init.ClockEdge; - hsd.Init.ClockBypass = hsd.Init.ClockBypass; - hsd.Init.ClockPowerSave = hsd.Init.ClockPowerSave; - hsd.Init.BusWide = hsd.Init.BusWide; - hsd.Init.HardwareFlowControl = hsd.Init.HardwareFlowControl; - hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK); - - /* Initialize SDIO peripheral interface with default configuration */ - SDIO_Init(hsd.Instance, hsd.Init); -} - -void SD_LowLevel_Init(void) { - uint32_t tempreg; - - __HAL_RCC_GPIOC_CLK_ENABLE(); //enable GPIO clocks - __HAL_RCC_GPIOD_CLK_ENABLE(); //enable GPIO clocks - - GPIO_InitTypeDef GPIO_InitStruct; - - GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; - GPIO_InitStruct.Pull = 1; //GPIO_NOPULL; - GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; - - #if DISABLED(STM32F1xx) - GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; - #endif - - GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_12; // D0 & SCK - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - - #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // define D1-D3 only if have a four bit wide SDIO bus - GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11; // D1-D3 - HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); - #endif - - // Configure PD.02 CMD line - GPIO_InitStruct.Pin = GPIO_PIN_2; - HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); - - // Setup DMA - #if defined(STM32F1xx) - hdma_sdio.Init.Mode = DMA_NORMAL; - hdma_sdio.Instance = DMA2_Channel4; - HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn); - #elif defined(STM32F4xx) - hdma_sdio.Init.Mode = DMA_PFCTRL; - hdma_sdio.Instance = DMA2_Stream3; - hdma_sdio.Init.Channel = DMA_CHANNEL_4; - hdma_sdio.Init.FIFOMode = DMA_FIFOMODE_ENABLE; - hdma_sdio.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; - hdma_sdio.Init.MemBurst = DMA_MBURST_INC4; - hdma_sdio.Init.PeriphBurst = DMA_PBURST_INC4; - HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); - #endif - HAL_NVIC_EnableIRQ(SDIO_IRQn); - hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE; - 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; - __HAL_LINKDMA(&hsd, hdmarx, hdma_sdio); - __HAL_LINKDMA(&hsd, hdmatx, hdma_sdio); - - #if defined(STM32F1xx) - __HAL_RCC_SDIO_CLK_ENABLE(); - __HAL_RCC_DMA2_CLK_ENABLE(); - #else - __HAL_RCC_SDIO_FORCE_RESET(); - delay(2); - __HAL_RCC_SDIO_RELEASE_RESET(); - delay(2); - __HAL_RCC_SDIO_CLK_ENABLE(); - - __HAL_RCC_DMA2_FORCE_RESET(); - delay(2); - __HAL_RCC_DMA2_RELEASE_RESET(); - delay(2); - __HAL_RCC_DMA2_CLK_ENABLE(); - #endif - - //Initialize the SDIO (with initial <400Khz Clock) - tempreg = 0; //Reset value - tempreg |= SDIO_CLKCR_CLKEN; // Clock enabled - tempreg |= SDIO_INIT_CLK_DIV; // Clock Divider. Clock = 48000 / (118 + 2) = 400Khz - // Keep the rest at 0 => HW_Flow Disabled, Rising Clock Edge, Disable CLK ByPass, Bus Width = 0, Power save Disable - SDIO->CLKCR = tempreg; - - // Power up the SDIO - SDIO_PowerState_ON(SDIO); - hsd.Instance = SDIO; -} - -void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { // application specific init - UNUSED(hsd); // Prevent unused argument(s) compilation warning - __HAL_RCC_SDIO_CLK_ENABLE(); // turn on SDIO clock -} - -bool SDIO_Init() { - uint8_t retryCnt = SDIO_READ_RETRIES; - - bool status; - hsd.Instance = SDIO; - hsd.State = HAL_SD_STATE_RESET; - - SD_LowLevel_Init(); - - uint8_t retry_Cnt = retryCnt; - for (;;) { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); - status = (bool) HAL_SD_Init(&hsd); - if (!status) break; - if (!--retry_Cnt) return false; // return failing status if retries are exhausted - } - - go_to_transfer_speed(); - - #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // go to 4 bit wide mode if pins are defined - retry_Cnt = retryCnt; - for (;;) { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); - if (!HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B)) break; // some cards are only 1 bit wide so a pass here is not required - if (!--retry_Cnt) break; - } - if (!retry_Cnt) { // wide bus failed, go back to one bit wide mode - hsd.State = (HAL_SD_StateTypeDef) 0; // HAL_SD_STATE_RESET - SD_LowLevel_Init(); - retry_Cnt = retryCnt; - for (;;) { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); - status = (bool) HAL_SD_Init(&hsd); - if (!status) break; - if (!--retry_Cnt) return false; // return failing status if retries are exhausted - } - go_to_transfer_speed(); - } - #endif - - return true; -} - -static bool SDIO_ReadWriteBlock_DMA(uint32_t block, const uint8_t *src, uint8_t *dst) { - if (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) return false; - - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); - - HAL_StatusTypeDef ret; - if (src) { - hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH; - HAL_DMA_Init(&hdma_sdio); - ret = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t *)src, block, 1); - } - else { - hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY; - HAL_DMA_Init(&hdma_sdio); - ret = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t *)dst, block, 1); - } - - if (ret != HAL_OK) { - HAL_DMA_Abort_IT(&hdma_sdio); - HAL_DMA_DeInit(&hdma_sdio); - return false; - } - - millis_t timeout = millis() + 500; - // Wait the transfer - while (hsd.State != HAL_SD_STATE_READY) { - if (ELAPSED(millis(), timeout)) { - HAL_DMA_Abort_IT(&hdma_sdio); - HAL_DMA_DeInit(&hdma_sdio); - return false; - } - } - - while (__HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_sdio)) != 0 - || __HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_sdio)) != 0) { /* nada */ } - - HAL_DMA_Abort_IT(&hdma_sdio); - HAL_DMA_DeInit(&hdma_sdio); - - timeout = millis() + 500; - while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) if (ELAPSED(millis(), timeout)) return false; - - return true; -} - -bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) { - 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) { - uint8_t retries = SDIO_READ_RETRIES; - while (retries--) if (SDIO_ReadWriteBlock_DMA(block, src, nullptr)) return true; - return false; -} - -bool SDIO_IsReady() { - return hsd.State == HAL_SD_STATE_READY; -} - -uint32_t SDIO_GetCardSize() { - return (uint32_t)(hsd.SdCard.BlockNbr) * (hsd.SdCard.BlockSize); -} - -#if defined(STM32F1xx) - #define DMA_IRQ_HANDLER DMA2_Channel4_5_IRQHandler -#elif defined(STM32F4xx) - #define DMA_IRQ_HANDLER DMA2_Stream3_IRQHandler -#else - #error "Unknown STM32 architecture." -#endif - -extern "C" void SDIO_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); } -extern "C" void DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&hdma_sdio); } - -#endif // SDIO_SUPPORT -#endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32/eeprom_flash.cpp b/Marlin/src/HAL/STM32/eeprom_flash.cpp index 252b057362c99..7c8cc8dd21e18 100644 --- a/Marlin/src/HAL/STM32/eeprom_flash.cpp +++ b/Marlin/src/HAL/STM32/eeprom_flash.cpp @@ -174,9 +174,9 @@ bool PersistentStore::access_finish() { UNLOCK_FLASH(); TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT()); - DISABLE_ISRS(); + hal.isr_off(); status = HAL_FLASHEx_Erase(&EraseInitStruct, &SectorError); - ENABLE_ISRS(); + hal.isr_on(); TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT()); if (status != HAL_OK) { DEBUG_ECHOLNPGM("HAL_FLASHEx_Erase=", status); @@ -229,9 +229,9 @@ bool PersistentStore::access_finish() { // output. Servo output still glitches with interrupts disabled, but recovers after the // erase. TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT()); - DISABLE_ISRS(); + hal.isr_off(); eeprom_buffer_flush(); - ENABLE_ISRS(); + hal.isr_on(); TERN_(HAS_PAUSE_SERVO_OUTPUT, RESUME_SERVO_OUTPUT()); eeprom_data_written = false; diff --git a/Marlin/src/HAL/STM32/fast_pwm.cpp b/Marlin/src/HAL/STM32/fast_pwm.cpp index 917e12615f1d6..a0d8ecc612c2d 100644 --- a/Marlin/src/HAL/STM32/fast_pwm.cpp +++ b/Marlin/src/HAL/STM32/fast_pwm.cpp @@ -24,39 +24,65 @@ #ifdef HAL_STM32 -#include "../../inc/MarlinConfigPre.h" +#include "../../inc/MarlinConfig.h" -#if NEEDS_HARDWARE_PWM +// Array to support sticky frequency sets per timer +static uint16_t timer_freq[TIMER_NUM]; -#include "HAL.h" -#include "timers.h" +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { + const uint16_t duty = invert ? v_size - v : v; + if (PWM_PIN(pin)) { + const PinName pin_name = digitalPinToPinName(pin); + TIM_TypeDef * const Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); -void set_pwm_frequency(const pin_t pin, int f_desired) { - if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer + const timer_index_t index = get_timer_index(Instance); + const bool needs_freq = (HardwareTimer_Handle[index] == nullptr); + if (needs_freq) // A new instance must be set to the default frequency of PWM_FREQUENCY + HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM)); - PinName pin_name = digitalPinToPinName(pin); - TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); // Get HAL timer instance + HardwareTimer * const HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this); + const uint32_t channel = STM_PIN_CHANNEL(pinmap_function(pin_name, PinMap_PWM)); + const TimerModes_t previousMode = HT->getMode(channel); + if (previousMode != TIMER_OUTPUT_COMPARE_PWM1) + HT->setMode(channel, TIMER_OUTPUT_COMPARE_PWM1, pin); - LOOP_S_L_N(i, 0, NUM_HARDWARE_TIMERS) // Protect used timers - if (timer_instance[i] && timer_instance[i]->getHandle()->Instance == Instance) - return; + if (needs_freq && timer_freq[index] == 0) // If the timer is unconfigured and no freq is set then default PWM_FREQUENCY + set_pwm_frequency(pin_name, PWM_FREQUENCY); // Set the frequency and save the value to the assigned index no. - pwm_start(pin_name, f_desired, 0, RESOLUTION_8B_COMPARE_FORMAT); + // Note the resolution is sticky here, the input can be upto 16 bits and that would require RESOLUTION_16B_COMPARE_FORMAT (16) + // If such a need were to manifest then we would need to calc the resolution based on the v_size parameter and add code for it. + HT->setCaptureCompare(channel, duty, RESOLUTION_8B_COMPARE_FORMAT); // Set the duty, the calc is done in the library :) + pinmap_pinout(pin_name, PinMap_PWM); // Make sure the pin output state is set. + if (previousMode != TIMER_OUTPUT_COMPARE_PWM1) HT->resume(); + } + else { + pinMode(pin, OUTPUT); + digitalWrite(pin, duty < v_size / 2 ? LOW : HIGH); + } } -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { - PinName pin_name = digitalPinToPinName(pin); - TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); - uint16_t adj_val = Instance->ARR * v / v_size; - if (invert) adj_val = Instance->ARR - adj_val; - - switch (get_pwm_channel(pin_name)) { - case TIM_CHANNEL_1: LL_TIM_OC_SetCompareCH1(Instance, adj_val); break; - case TIM_CHANNEL_2: LL_TIM_OC_SetCompareCH2(Instance, adj_val); break; - case TIM_CHANNEL_3: LL_TIM_OC_SetCompareCH3(Instance, adj_val); break; - case TIM_CHANNEL_4: LL_TIM_OC_SetCompareCH4(Instance, adj_val); break; - } +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { + if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer + const PinName pin_name = digitalPinToPinName(pin); + TIM_TypeDef * const Instance = (TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM); // Get HAL timer instance + const timer_index_t index = get_timer_index(Instance); + + // Protect used timers. + #ifdef STEP_TIMER + if (index == TIMER_INDEX(STEP_TIMER)) return; + #endif + #ifdef TEMP_TIMER + if (index == TIMER_INDEX(TEMP_TIMER)) return; + #endif + #if defined(PULSE_TIMER) && MF_TIMER_PULSE != MF_TIMER_STEP + if (index == TIMER_INDEX(PULSE_TIMER)) return; + #endif + + if (HardwareTimer_Handle[index] == nullptr) // If frequency is set before duty we need to create a handle here. + HardwareTimer_Handle[index]->__this = new HardwareTimer((TIM_TypeDef *)pinmap_peripheral(pin_name, PinMap_PWM)); + HardwareTimer * const HT = (HardwareTimer *)(HardwareTimer_Handle[index]->__this); + HT->setOverflow(f_desired, HERTZ_FORMAT); + timer_freq[index] = f_desired; // Save the last frequency so duty will not set the default for this timer number. } -#endif // NEEDS_HARDWARE_PWM #endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32/msc_sd.cpp b/Marlin/src/HAL/STM32/msc_sd.cpp index 4f85af0d446a7..a40bec9d644d6 100644 --- a/Marlin/src/HAL/STM32/msc_sd.cpp +++ b/Marlin/src/HAL/STM32/msc_sd.cpp @@ -57,7 +57,7 @@ class Sd2CardUSBMscHandler : public USBMscHandler { auto sd2card = diskIODriver(); // single block if (blkLen == 1) { - watchdog_refresh(); + hal.watchdog_refresh(); sd2card->writeBlock(blkAddr, pBuf); return true; } @@ -65,7 +65,7 @@ class Sd2CardUSBMscHandler : public USBMscHandler { // multi block optimization sd2card->writeStart(blkAddr, blkLen); while (blkLen--) { - watchdog_refresh(); + hal.watchdog_refresh(); sd2card->writeData(pBuf); pBuf += BLOCK_SIZE; } @@ -77,7 +77,7 @@ class Sd2CardUSBMscHandler : public USBMscHandler { auto sd2card = diskIODriver(); // single block if (blkLen == 1) { - watchdog_refresh(); + hal.watchdog_refresh(); sd2card->readBlock(blkAddr, pBuf); return true; } @@ -85,7 +85,7 @@ class Sd2CardUSBMscHandler : public USBMscHandler { // multi block optimization sd2card->readStart(blkAddr); while (blkLen--) { - watchdog_refresh(); + hal.watchdog_refresh(); sd2card->readData(pBuf); pBuf += BLOCK_SIZE; } diff --git a/Marlin/src/HAL/STM32/pinsDebug.h b/Marlin/src/HAL/STM32/pinsDebug.h index 73d850fc43132..55c64c8681924 100644 --- a/Marlin/src/HAL/STM32/pinsDebug.h +++ b/Marlin/src/HAL/STM32/pinsDebug.h @@ -79,7 +79,6 @@ // make a list of the Arduino pin numbers in the Port/Pin order // -#define _PIN_ADD_2(NAME_ALPHA, ARDUINO_NUM) { {NAME_ALPHA}, ARDUINO_NUM }, #define _PIN_ADD(NAME_ALPHA, ARDUINO_NUM) { NAME_ALPHA, ARDUINO_NUM }, #define PIN_ADD(NAME) _PIN_ADD(#NAME, NAME) @@ -108,14 +107,17 @@ const XrefInfo pin_xref[] PROGMEM = { /** * Translation of routines & variables used by pinsDebug.h */ -#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS + +#if PA0 >= NUM_DIGITAL_PINS + #define HAS_HIGH_ANALOG_PINS 1 +#endif +#define NUMBER_PINS_TOTAL NUM_DIGITAL_PINS + TERN0(HAS_HIGH_ANALOG_PINS, NUM_ANALOG_INPUTS) #define VALID_PIN(ANUM) ((ANUM) >= 0 && (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 PRINT_PORT(ANUM) port_print(ANUM) #define DIGITAL_PIN_TO_ANALOG_PIN(ANUM) -1 // will report analog pin number in the print port routine -#define GET_PIN_MAP_PIN_M43(Index) pin_xref[Index].Ard_num // x is a variable used to search pin_array #define GET_ARRAY_IS_DIGITAL(x) ((bool) pin_array[x].is_digital) @@ -123,6 +125,11 @@ const XrefInfo pin_xref[] PROGMEM = { #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 +// +// Pin Mapping for M43 +// +#define GET_PIN_MAP_PIN_M43(Index) pin_xref[Index].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 @@ -160,17 +167,20 @@ bool GET_PINMODE(const pin_t 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; +int8_t digital_pin_to_analog_pin(const pin_t Ard_num) { + if (WITHIN(Ard_num, NUM_ANALOG_FIRST, NUM_ANALOG_FIRST + NUM_ANALOG_INPUTS - 1)) + return Ard_num - NUM_ANALOG_FIRST; + + const uint32_t ind = digitalPinToAnalogInput(Ard_num); + 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 is_digital(const pin_t x) { - const uint8_t pin_mode = get_pin_mode(pin_array[x].pin); +bool is_digital(const pin_t Ard_num) { + const uint8_t pin_mode = get_pin_mode(pin_array[Ard_num].pin); return pin_mode == MODE_PIN_INPUT || pin_mode == MODE_PIN_OUTPUT; } @@ -196,10 +206,15 @@ void port_print(const pin_t Ard_num) { SERIAL_ECHO_SP(7); // Print number to be used with M42 - sprintf_P(buffer, PSTR(" M42 P%d "), Ard_num); - SERIAL_ECHO(buffer); - if (Ard_num < 10) SERIAL_CHAR(' '); - if (Ard_num < 100) SERIAL_CHAR(' '); + int calc_p = Ard_num % (NUM_DIGITAL_PINS + 1); + if (Ard_num > NUM_DIGITAL_PINS && calc_p > 7) calc_p += 8; + SERIAL_ECHOPGM(" M42 P", calc_p); + SERIAL_CHAR(' '); + if (calc_p < 100) { + SERIAL_CHAR(' '); + if (calc_p < 10) + SERIAL_CHAR(' '); + } } bool pwm_status(const pin_t Ard_num) { @@ -221,19 +236,19 @@ void pwm_details(const pin_t Ard_num) { case 'D' : alt_all = GPIOD->AFR[ind]; break; #ifdef PE_0 case 'E' : alt_all = GPIOE->AFR[ind]; break; - #elif defined (PF_0) + #elif defined(PF_0) case 'F' : alt_all = GPIOF->AFR[ind]; break; - #elif defined (PG_0) + #elif defined(PG_0) case 'G' : alt_all = GPIOG->AFR[ind]; break; - #elif defined (PH_0) + #elif defined(PH_0) case 'H' : alt_all = GPIOH->AFR[ind]; break; - #elif defined (PI_0) + #elif defined(PI_0) case 'I' : alt_all = GPIOI->AFR[ind]; break; - #elif defined (PJ_0) + #elif defined(PJ_0) case 'J' : alt_all = GPIOJ->AFR[ind]; break; - #elif defined (PK_0) + #elif defined(PK_0) case 'K' : alt_all = GPIOK->AFR[ind]; break; - #elif defined (PL_0) + #elif defined(PL_0) case 'L' : alt_all = GPIOL->AFR[ind]; break; #endif } diff --git a/Marlin/src/HAL/STM32/sdio.cpp b/Marlin/src/HAL/STM32/sdio.cpp new file mode 100644 index 0000000000000..41fe90b825406 --- /dev/null +++ b/Marlin/src/HAL/STM32/sdio.cpp @@ -0,0 +1,451 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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 HAL_STM32 + +#include "../../inc/MarlinConfig.h" + +#if ENABLED(SDIO_SUPPORT) + +#include "sdio.h" + +#include +#include + +#if defined(STM32F103xE) || defined(STM32F103xG) + #include + #include +#elif defined(STM32F4xx) + #include + #include + #include + #include +#elif defined(STM32F7xx) + #include + #include + #include + #include +#elif defined(STM32H7xx) + #define SDIO_FOR_STM32H7 + #include + #include + #include + #include +#else + #error "SDIO is only supported with STM32F103xE, STM32F103xG, STM32F4xx, STM32F7xx, and STM32H7xx." +#endif + +// SDIO Max Clock (naming from STM Manual, don't change) +#define SDIOCLK 48000000 + +// Target Clock, configurable. Default is 18MHz, from STM32F1 +#ifndef SDIO_CLOCK + #define SDIO_CLOCK 18000000 // 18 MHz +#endif + +SD_HandleTypeDef hsd; // SDIO structure + +static uint32_t clock_to_divider(uint32_t clk) { + #ifdef SDIO_FOR_STM32H7 + // SDMMC_CK frequency = sdmmc_ker_ck / [2 * CLKDIV]. + uint32_t sdmmc_clk = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC); + return sdmmc_clk / (2U * SDIO_CLOCK) + (sdmmc_clk % (2U * SDIO_CLOCK) != 0); + #else + // limit the SDIO master clock to 8/3 of PCLK2. See STM32 Manuals + // Also limited to no more than 48Mhz (SDIOCLK). + const uint32_t pclk2 = HAL_RCC_GetPCLK2Freq(); + clk = min(clk, (uint32_t)(pclk2 * 8 / 3)); + clk = min(clk, (uint32_t)SDIOCLK); + // Round up divider, so we don't run the card over the speed supported, + // and subtract by 2, because STM32 will add 2, as written in the manual: + // SDIO_CK frequency = SDIOCLK / [CLKDIV + 2] + return pclk2 / clk + (pclk2 % clk != 0) - 2; + #endif +} + +// Start the SDIO clock +void HAL_SD_MspInit(SD_HandleTypeDef *hsd) { + UNUSED(hsd); + #ifdef SDIO_FOR_STM32H7 + pinmap_pinout(PC_12, PinMap_SD); + pinmap_pinout(PD_2, PinMap_SD); + pinmap_pinout(PC_8, PinMap_SD); + #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // Define D1-D3 only for 4-bit wide SDIO bus + pinmap_pinout(PC_9, PinMap_SD); + pinmap_pinout(PC_10, PinMap_SD); + pinmap_pinout(PC_11, PinMap_SD); + #endif + __HAL_RCC_SDMMC1_CLK_ENABLE(); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + #else + __HAL_RCC_SDIO_CLK_ENABLE(); + #endif +} + +#ifdef SDIO_FOR_STM32H7 + + #define SD_TIMEOUT 1000 // ms + + extern "C" void SDMMC1_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); } + + uint8_t waitingRxCplt = 0, waitingTxCplt = 0; + void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsdio) { waitingTxCplt = 0; } + void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsdio) { waitingRxCplt = 0; } + + void HAL_SD_MspDeInit(SD_HandleTypeDef *hsd) { + __HAL_RCC_SDMMC1_FORCE_RESET(); delay(10); + __HAL_RCC_SDMMC1_RELEASE_RESET(); delay(10); + } + + bool SDIO_Init() { + HAL_StatusTypeDef sd_state = HAL_OK; + if (hsd.Instance == SDMMC1) HAL_SD_DeInit(&hsd); + + // HAL SD initialization + hsd.Instance = SDMMC1; + hsd.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + hsd.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + hsd.Init.BusWide = SDMMC_BUS_WIDE_1B; + hsd.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK); + sd_state = HAL_SD_Init(&hsd); + + #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) + if (sd_state == HAL_OK) + sd_state = HAL_SD_ConfigWideBusOperation(&hsd, SDMMC_BUS_WIDE_4B); + #endif + + return (sd_state == HAL_OK); + } + +#else // !SDIO_FOR_STM32H7 + + #define SD_TIMEOUT 500 // ms + + // SDIO retries, configurable. Default is 3, from STM32F1 + #ifndef SDIO_READ_RETRIES + #define SDIO_READ_RETRIES 3 + #endif + + // F4 supports one DMA for RX and another for TX, but Marlin will never + // do read and write at same time, so we use the same DMA for both. + DMA_HandleTypeDef hdma_sdio; + + #ifdef STM32F1xx + #define DMA_IRQ_HANDLER DMA2_Channel4_5_IRQHandler + #elif defined(STM32F4xx) + #define DMA_IRQ_HANDLER DMA2_Stream3_IRQHandler + #else + #error "Unknown STM32 architecture." + #endif + + extern "C" void SDIO_IRQHandler(void) { HAL_SD_IRQHandler(&hsd); } + extern "C" void DMA_IRQ_HANDLER(void) { HAL_DMA_IRQHandler(&hdma_sdio); } + + /* + SDIO_INIT_CLK_DIV is 118 + SDIO clock frequency is 48MHz / (TRANSFER_CLOCK_DIV + 2) + SDIO init clock frequency should not exceed 400kHz = 48MHz / (118 + 2) + + Default TRANSFER_CLOCK_DIV is 2 (118 / 40) + Default SDIO clock frequency is 48MHz / (2 + 2) = 12 MHz + This might be too fast for stable SDIO operations + + MKS Robin SDIO seems stable with BusWide 1bit and ClockDiv 8 (i.e., 4.8MHz SDIO clock frequency) + More testing is required as there are clearly some 4bit init problems. + */ + + void go_to_transfer_speed() { + /* Default SDIO peripheral configuration for SD card initialization */ + hsd.Init.ClockEdge = hsd.Init.ClockEdge; + hsd.Init.ClockBypass = hsd.Init.ClockBypass; + hsd.Init.ClockPowerSave = hsd.Init.ClockPowerSave; + hsd.Init.BusWide = hsd.Init.BusWide; + hsd.Init.HardwareFlowControl = hsd.Init.HardwareFlowControl; + hsd.Init.ClockDiv = clock_to_divider(SDIO_CLOCK); + + /* Initialize SDIO peripheral interface with default configuration */ + SDIO_Init(hsd.Instance, hsd.Init); + } + + void SD_LowLevel_Init() { + uint32_t tempreg; + + // Enable GPIO clocks + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + + GPIO_InitTypeDef GPIO_InitStruct; + + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = 1; // GPIO_NOPULL + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; + + #if DISABLED(STM32F1xx) + GPIO_InitStruct.Alternate = GPIO_AF12_SDIO; + #endif + + GPIO_InitStruct.Pin = GPIO_PIN_8 | GPIO_PIN_12; // D0 & SCK + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // define D1-D3 only if have a four bit wide SDIO bus + GPIO_InitStruct.Pin = GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11; // D1-D3 + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + #endif + + // Configure PD.02 CMD line + GPIO_InitStruct.Pin = GPIO_PIN_2; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + // Setup DMA + #ifdef STM32F1xx + hdma_sdio.Init.Mode = DMA_NORMAL; + hdma_sdio.Instance = DMA2_Channel4; + HAL_NVIC_EnableIRQ(DMA2_Channel4_5_IRQn); + #elif defined(STM32F4xx) + hdma_sdio.Init.Mode = DMA_PFCTRL; + hdma_sdio.Instance = DMA2_Stream3; + hdma_sdio.Init.Channel = DMA_CHANNEL_4; + hdma_sdio.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sdio.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sdio.Init.MemBurst = DMA_MBURST_INC4; + hdma_sdio.Init.PeriphBurst = DMA_PBURST_INC4; + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + #endif + HAL_NVIC_EnableIRQ(SDIO_IRQn); + hdma_sdio.Init.PeriphInc = DMA_PINC_DISABLE; + 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; + __HAL_LINKDMA(&hsd, hdmarx, hdma_sdio); + __HAL_LINKDMA(&hsd, hdmatx, hdma_sdio); + + #ifdef STM32F1xx + __HAL_RCC_SDIO_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + #else + __HAL_RCC_SDIO_FORCE_RESET(); delay(2); + __HAL_RCC_SDIO_RELEASE_RESET(); delay(2); + __HAL_RCC_SDIO_CLK_ENABLE(); + + __HAL_RCC_DMA2_FORCE_RESET(); delay(2); + __HAL_RCC_DMA2_RELEASE_RESET(); delay(2); + __HAL_RCC_DMA2_CLK_ENABLE(); + #endif + + // Initialize the SDIO (with initial <400Khz Clock) + tempreg = 0 // Reset value + | SDIO_CLKCR_CLKEN // Clock enabled + | SDIO_INIT_CLK_DIV; // Clock Divider. Clock = 48000 / (118 + 2) = 400Khz + // Keep the rest at 0 => HW_Flow Disabled, Rising Clock Edge, Disable CLK ByPass, Bus Width = 0, Power save Disable + SDIO->CLKCR = tempreg; + + // Power up the SDIO + SDIO_PowerState_ON(SDIO); + hsd.Instance = SDIO; + } + + bool SDIO_Init() { + uint8_t retryCnt = SDIO_READ_RETRIES; + + bool status; + hsd.Instance = SDIO; + hsd.State = HAL_SD_STATE_RESET; + + SD_LowLevel_Init(); + + uint8_t retry_Cnt = retryCnt; + for (;;) { + hal.watchdog_refresh(); + status = (bool) HAL_SD_Init(&hsd); + if (!status) break; + if (!--retry_Cnt) return false; // return failing status if retries are exhausted + } + + go_to_transfer_speed(); + + #if PINS_EXIST(SDIO_D1, SDIO_D2, SDIO_D3) // go to 4 bit wide mode if pins are defined + retry_Cnt = retryCnt; + for (;;) { + hal.watchdog_refresh(); + if (!HAL_SD_ConfigWideBusOperation(&hsd, SDIO_BUS_WIDE_4B)) break; // some cards are only 1 bit wide so a pass here is not required + if (!--retry_Cnt) break; + } + if (!retry_Cnt) { // wide bus failed, go back to one bit wide mode + hsd.State = (HAL_SD_StateTypeDef) 0; // HAL_SD_STATE_RESET + SD_LowLevel_Init(); + retry_Cnt = retryCnt; + for (;;) { + hal.watchdog_refresh(); + status = (bool) HAL_SD_Init(&hsd); + if (!status) break; + if (!--retry_Cnt) return false; // return failing status if retries are exhausted + } + go_to_transfer_speed(); + } + #endif + + return true; + } + + /** + * @brief Read or Write a block + * @details Read or Write a block with SDIO + * + * @param block The block index + * @param src The data buffer source for a write + * @param dst The data buffer destination for a read + * + * @return true on success + */ + static bool SDIO_ReadWriteBlock_DMA(uint32_t block, const uint8_t *src, uint8_t *dst) { + if (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) return false; + + hal.watchdog_refresh(); + + HAL_StatusTypeDef ret; + if (src) { + hdma_sdio.Init.Direction = DMA_MEMORY_TO_PERIPH; + HAL_DMA_Init(&hdma_sdio); + ret = HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)src, block, 1); + } + else { + hdma_sdio.Init.Direction = DMA_PERIPH_TO_MEMORY; + HAL_DMA_Init(&hdma_sdio); + ret = HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t*)dst, block, 1); + } + + if (ret != HAL_OK) { + HAL_DMA_Abort_IT(&hdma_sdio); + HAL_DMA_DeInit(&hdma_sdio); + return false; + } + + millis_t timeout = millis() + SD_TIMEOUT; + // Wait the transfer + while (hsd.State != HAL_SD_STATE_READY) { + if (ELAPSED(millis(), timeout)) { + HAL_DMA_Abort_IT(&hdma_sdio); + HAL_DMA_DeInit(&hdma_sdio); + return false; + } + } + + while (__HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TC_FLAG_INDEX(&hdma_sdio)) != 0 + || __HAL_DMA_GET_FLAG(&hdma_sdio, __HAL_DMA_GET_TE_FLAG_INDEX(&hdma_sdio)) != 0) { /* nada */ } + + HAL_DMA_Abort_IT(&hdma_sdio); + HAL_DMA_DeInit(&hdma_sdio); + + timeout = millis() + SD_TIMEOUT; + while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) if (ELAPSED(millis(), timeout)) return false; + + return true; + } + +#endif // !SDIO_FOR_STM32H7 + +/** + * @brief Read a block + * @details Read a block from media with SDIO + * + * @param block The block index + * @param src The block buffer + * + * @return true on success + */ +bool SDIO_ReadBlock(uint32_t block, uint8_t *dst) { + #ifdef SDIO_FOR_STM32H7 + + uint32_t timeout = HAL_GetTick() + SD_TIMEOUT; + + while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) + if (HAL_GetTick() >= timeout) return false; + + waitingRxCplt = 1; + if (HAL_SD_ReadBlocks_DMA(&hsd, (uint8_t*)dst, block, 1) != HAL_OK) + return false; + + timeout = HAL_GetTick() + SD_TIMEOUT; + while (waitingRxCplt) + if (HAL_GetTick() >= timeout) return false; + + return true; + + #else + + uint8_t retries = SDIO_READ_RETRIES; + while (retries--) if (SDIO_ReadWriteBlock_DMA(block, nullptr, dst)) return true; + return false; + + #endif +} + +/** + * @brief Write a block + * @details Write a block to media with SDIO + * + * @param block The block index + * @param src The block data + * + * @return true on success + */ +bool SDIO_WriteBlock(uint32_t block, const uint8_t *src) { + #ifdef SDIO_FOR_STM32H7 + + uint32_t timeout = HAL_GetTick() + SD_TIMEOUT; + + while (HAL_SD_GetCardState(&hsd) != HAL_SD_CARD_TRANSFER) + if (HAL_GetTick() >= timeout) return false; + + waitingTxCplt = 1; + if (HAL_SD_WriteBlocks_DMA(&hsd, (uint8_t*)src, block, 1) != HAL_OK) + return false; + + timeout = HAL_GetTick() + SD_TIMEOUT; + while (waitingTxCplt) + if (HAL_GetTick() >= timeout) return false; + + return true; + + #else + + uint8_t retries = SDIO_READ_RETRIES; + while (retries--) if (SDIO_ReadWriteBlock_DMA(block, src, nullptr)) return true; + return false; + + #endif +} + +bool SDIO_IsReady() { + return hsd.State == HAL_SD_STATE_READY; +} + +uint32_t SDIO_GetCardSize() { + return (uint32_t)(hsd.SdCard.BlockNbr) * (hsd.SdCard.BlockSize); +} + +#endif // SDIO_SUPPORT +#endif // HAL_STM32 diff --git a/Marlin/src/HAL/NATIVE_SIM/watchdog.h b/Marlin/src/HAL/STM32/sdio.h similarity index 84% rename from Marlin/src/HAL/NATIVE_SIM/watchdog.h rename to Marlin/src/HAL/STM32/sdio.h index 4e404c3887da2..cf5539c3c76d8 100644 --- a/Marlin/src/HAL/NATIVE_SIM/watchdog.h +++ b/Marlin/src/HAL/STM32/sdio.h @@ -21,7 +21,9 @@ */ #pragma once -#define WDT_TIMEOUT 4000000 // 4 second timeout - -void watchdog_init(); -void HAL_watchdog_refresh(); +#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 diff --git a/Marlin/src/HAL/STM32/tft/gt911.cpp b/Marlin/src/HAL/STM32/tft/gt911.cpp index 92e14edb2057c..180abc68b0546 100644 --- a/Marlin/src/HAL/STM32/tft/gt911.cpp +++ b/Marlin/src/HAL/STM32/tft/gt911.cpp @@ -159,24 +159,28 @@ void GT911::read_reg(uint16_t reg, uint8_t reg_len, uint8_t* r_data, uint8_t r_l void GT911::Init() { OUT_WRITE(GT911_RST_PIN, LOW); OUT_WRITE(GT911_INT_PIN, LOW); - delay(20); + delay(11); + WRITE(GT911_INT_PIN, HIGH); + delayMicroseconds(110); WRITE(GT911_RST_PIN, HIGH); + delay(6); + WRITE(GT911_INT_PIN, LOW); + delay(55); SET_INPUT(GT911_INT_PIN); sw_iic.init(); - uint8_t clear_reg = 0x0000; - write_reg(0x814E, 2, &clear_reg, 2); // Reset to 0 for start + uint8_t clear_reg = 0x00; + write_reg(0x814E, 2, &clear_reg, 1); // Reset to 0 for start } bool GT911::getFirstTouchPoint(int16_t *x, int16_t *y) { read_reg(0x814E, 2, ®.REG.status, 1); - if (reg.REG.status & 0x80) { + if (reg.REG.status >= 0x80 && reg.REG.status <= 0x85) { + read_reg(0x8150, 2, reg.map + 2, 38); uint8_t clear_reg = 0x00; write_reg(0x814E, 2, &clear_reg, 1); // Reset to 0 for start - read_reg(0x8150, 2, reg.map + 2, 8 * (reg.REG.status & 0x0F)); - // First touch point *x = ((reg.REG.point[0].xh & 0x0F) << 8) | reg.REG.point[0].xl; *y = ((reg.REG.point[0].yh & 0x0F) << 8) | reg.REG.point[0].yl; diff --git a/Marlin/src/HAL/STM32/tft/gt911.h b/Marlin/src/HAL/STM32/tft/gt911.h index 752a554d98ed7..6ecfe8b82ec77 100644 --- a/Marlin/src/HAL/STM32/tft/gt911.h +++ b/Marlin/src/HAL/STM32/tft/gt911.h @@ -23,7 +23,7 @@ #include "../../../inc/MarlinConfig.h" -#define GT911_SLAVE_ADDRESS 0xBA +#define GT911_SLAVE_ADDRESS 0x28 #if !PIN_EXISTS(GT911_RST) #error "GT911_RST_PIN is not defined." diff --git a/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp b/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp index 66cfd65995ddd..95871bf41f5c4 100644 --- a/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp +++ b/Marlin/src/HAL/STM32/tft/tft_ltdc.cpp @@ -372,9 +372,9 @@ void TFT_LTDC::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Cou if (MemoryIncrease == DMA_PINC_ENABLE) { DrawImage(x_min, y_cur, x_min + width, y_cur + height, Data); Data += width * height; - } else { - DrawRect(x_min, y_cur, x_min + width, y_cur + height, *Data); } + else + DrawRect(x_min, y_cur, x_min + width, y_cur + height, *Data); y_cur += height; } diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.cpp b/Marlin/src/HAL/STM32/tft/tft_spi.cpp index 790513e7edd97..2e18c8a64c069 100644 --- a/Marlin/src/HAL/STM32/tft/tft_spi.cpp +++ b/Marlin/src/HAL/STM32/tft/tft_spi.cpp @@ -161,11 +161,11 @@ uint32_t TFT_SPI::ReadID(uint16_t Reg) { for (i = 0; i < 4; i++) { #if TFT_MISO_PIN != TFT_MOSI_PIN //if (hspi->Init.Direction == SPI_DIRECTION_2LINES) { - while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} + while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {} SPIx.Instance->DR = 0; //} #endif - while ((SPIx.Instance->SR & SPI_FLAG_RXNE) != SPI_FLAG_RXNE) {} + while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_RXNE)) {} Data = (Data << 8) | SPIx.Instance->DR; } @@ -195,8 +195,8 @@ bool TFT_SPI::isBusy() { void TFT_SPI::Abort() { // Wait for any running spi - while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} - while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {} + while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {} + while ( __HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY)) {} // First, abort any running dma HAL_DMA_Abort(&DMAtx); // DeInit objects @@ -214,8 +214,8 @@ void TFT_SPI::Transmit(uint16_t Data) { SPIx.Instance->DR = Data; - while ((SPIx.Instance->SR & SPI_FLAG_TXE) != SPI_FLAG_TXE) {} - while ((SPIx.Instance->SR & SPI_FLAG_BSY) == SPI_FLAG_BSY) {} + while (!__HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_TXE)) {} + while ( __HAL_SPI_GET_FLAG(&SPIx, SPI_FLAG_BSY)) {} if (TFT_MISO_PIN != TFT_MOSI_PIN) __HAL_SPI_CLEAR_OVRFLAG(&SPIx); // Clear overrun flag in 2 Lines communication mode because received is not read @@ -242,5 +242,29 @@ void TFT_SPI::TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Coun Abort(); } +#if ENABLED(USE_SPI_DMA_TC) + + void TFT_SPI::TransmitDMA_IT(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count) { + + DMAtx.Init.MemInc = MemoryIncrease; + HAL_DMA_Init(&DMAtx); + + if (TFT_MISO_PIN == TFT_MOSI_PIN) + SPI_1LINE_TX(&SPIx); + + DataTransferBegin(); + + HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 5, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + HAL_DMA_Start_IT(&DMAtx, (uint32_t)Data, (uint32_t)&(SPIx.Instance->DR), Count); + __HAL_SPI_ENABLE(&SPIx); + + SET_BIT(SPIx.Instance->CR2, SPI_CR2_TXDMAEN); // Enable Tx DMA Request + } + + extern "C" void DMA2_Stream3_IRQHandler(void) { HAL_DMA_IRQHandler(&TFT_SPI::DMAtx); } + +#endif + #endif // HAS_SPI_TFT #endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32/tft/tft_spi.h b/Marlin/src/HAL/STM32/tft/tft_spi.h index 667b5f366b7ed..de051e2294591 100644 --- a/Marlin/src/HAL/STM32/tft/tft_spi.h +++ b/Marlin/src/HAL/STM32/tft/tft_spi.h @@ -36,20 +36,25 @@ #define LCD_READ_ID4 0xD3 // Read display identification information (0xD3 on ILI9341) #endif -#define DATASIZE_8BIT SPI_DATASIZE_8BIT -#define DATASIZE_16BIT SPI_DATASIZE_16BIT -#define TFT_IO_DRIVER TFT_SPI +#define DATASIZE_8BIT SPI_DATASIZE_8BIT +#define DATASIZE_16BIT SPI_DATASIZE_16BIT +#define TFT_IO_DRIVER TFT_SPI class TFT_SPI { private: static SPI_HandleTypeDef SPIx; - static DMA_HandleTypeDef DMAtx; + static uint32_t ReadID(uint16_t Reg); static void Transmit(uint16_t Data); static void TransmitDMA(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count); + #if ENABLED(USE_SPI_DMA_TC) + static void TransmitDMA_IT(uint32_t MemoryIncrease, uint16_t *Data, uint16_t Count); + #endif public: + static DMA_HandleTypeDef DMAtx; + static void Init(); static uint32_t GetID(); static bool isBusy(); @@ -63,6 +68,11 @@ class TFT_SPI { static void WriteReg(uint16_t Reg) { WRITE(TFT_A0_PIN, LOW); Transmit(Reg); WRITE(TFT_A0_PIN, HIGH); } static void WriteSequence(uint16_t *Data, uint16_t Count) { TransmitDMA(DMA_MINC_ENABLE, Data, Count); } + + #if ENABLED(USE_SPI_DMA_TC) + static void WriteSequenceIT(uint16_t *Data, uint16_t Count) { TransmitDMA_IT(DMA_MINC_ENABLE, Data, Count); } + #endif + static void WriteMultiple(uint16_t Color, uint16_t Count) { static uint16_t Data; Data = Color; TransmitDMA(DMA_MINC_DISABLE, &Data, Count); } static void WriteMultiple(uint16_t Color, uint32_t Count) { static uint16_t Data; Data = Color; diff --git a/Marlin/src/HAL/STM32/tft/xpt2046.h b/Marlin/src/HAL/STM32/tft/xpt2046.h index 2cff3e29d05be..71de6b00251b1 100644 --- a/Marlin/src/HAL/STM32/tft/xpt2046.h +++ b/Marlin/src/HAL/STM32/tft/xpt2046.h @@ -69,8 +69,8 @@ class XPT2046 { static uint16_t getRawData(const XPTCoordinate coordinate); static bool isTouched(); - static inline void DataTransferBegin() { if (SPIx.Instance) { HAL_SPI_Init(&SPIx); } WRITE(TOUCH_CS_PIN, LOW); }; - static inline void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; + static void DataTransferBegin() { if (SPIx.Instance) { HAL_SPI_Init(&SPIx); } WRITE(TOUCH_CS_PIN, LOW); }; + static void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; static uint16_t HardwareIO(uint16_t data); static uint16_t SoftwareIO(uint16_t data); static uint16_t IO(uint16_t data = 0) { return SPIx.Instance ? HardwareIO(data) : SoftwareIO(data); } diff --git a/Marlin/src/HAL/STM32/timers.cpp b/Marlin/src/HAL/STM32/timers.cpp index 9b69323ef5433..e68b59c46fee8 100644 --- a/Marlin/src/HAL/STM32/timers.cpp +++ b/Marlin/src/HAL/STM32/timers.cpp @@ -67,17 +67,17 @@ #endif #endif -#ifdef STM32F0xx +#if defined(STM32F0xx) || defined(STM32G0xx) #define MCU_STEP_TIMER 16 #define MCU_TEMP_TIMER 17 #elif defined(STM32F1xx) #define MCU_STEP_TIMER 4 #define MCU_TEMP_TIMER 2 #elif defined(STM32F401xC) || defined(STM32F401xE) - #define MCU_STEP_TIMER 9 + #define MCU_STEP_TIMER 9 // STM32F401 has no TIM6, TIM7, or TIM8 #define MCU_TEMP_TIMER 10 #elif defined(STM32F4xx) || defined(STM32F7xx) || defined(STM32H7xx) - #define MCU_STEP_TIMER 6 // STM32F401 has no TIM6, TIM7, or TIM8 + #define MCU_STEP_TIMER 6 #define MCU_TEMP_TIMER 14 // TIM7 is consumed by Software Serial if used. #endif @@ -97,9 +97,15 @@ #define STEP_TIMER_DEV _TIMER_DEV(STEP_TIMER) #define TEMP_TIMER_DEV _TIMER_DEV(TEMP_TIMER) -// ------------------------ +// -------------------------------------------------------------------------- +// Local defines +// -------------------------------------------------------------------------- + +#define NUM_HARDWARE_TIMERS 2 + +// -------------------------------------------------------------------------- // Private Variables -// ------------------------ +// -------------------------------------------------------------------------- HardwareTimer *timer_instance[NUM_HARDWARE_TIMERS] = { nullptr }; @@ -110,7 +116,7 @@ HardwareTimer *timer_instance[NUM_HARDWARE_TIMERS] = { nullptr }; uint32_t GetStepperTimerClkFreq() { // Timer input clocks vary between devices, and in some cases between timers on the same device. // Retrieve at runtime to ensure device compatibility. Cache result to avoid repeated overhead. - static uint32_t clkfreq = timer_instance[STEP_TIMER_NUM]->getTimerClkFreq(); + static uint32_t clkfreq = timer_instance[MF_TIMER_STEP]->getTimerClkFreq(); return clkfreq; } @@ -118,7 +124,7 @@ uint32_t GetStepperTimerClkFreq() { void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { if (!HAL_timer_initialized(timer_num)) { switch (timer_num) { - case STEP_TIMER_NUM: // STEPPER TIMER - use a 32bit timer if possible + case MF_TIMER_STEP: // STEPPER TIMER - use a 32bit timer if possible timer_instance[timer_num] = new HardwareTimer(STEP_TIMER_DEV); /* Set the prescaler to the final desired value. * This will change the effective ISR callback frequency but when @@ -137,7 +143,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { timer_instance[timer_num]->setPrescaleFactor(STEPPER_TIMER_PRESCALE); //the -1 is done internally timer_instance[timer_num]->setOverflow(_MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (HAL_TIMER_RATE) / (STEPPER_TIMER_PRESCALE) /* /frequency */), TICK_FORMAT); break; - case TEMP_TIMER_NUM: // TEMP TIMER - any available 16bit timer + case MF_TIMER_TEMP: // TEMP TIMER - any available 16bit timer timer_instance[timer_num] = new HardwareTimer(TEMP_TIMER_DEV); // The prescale factor is computed automatically for HERTZ_FORMAT timer_instance[timer_num]->setOverflow(frequency, HERTZ_FORMAT); @@ -157,10 +163,10 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { // These calls can be removed and replaced with // timer_instance[timer_num]->setInterruptPriority switch (timer_num) { - case STEP_TIMER_NUM: + case MF_TIMER_STEP: timer_instance[timer_num]->setInterruptPriority(STEP_TIMER_IRQ_PRIO, 0); break; - case TEMP_TIMER_NUM: + case MF_TIMER_TEMP: timer_instance[timer_num]->setInterruptPriority(TEMP_TIMER_IRQ_PRIO, 0); break; } @@ -170,10 +176,10 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_enable_interrupt(const uint8_t timer_num) { if (HAL_timer_initialized(timer_num) && !timer_instance[timer_num]->hasInterrupt()) { switch (timer_num) { - case STEP_TIMER_NUM: + case MF_TIMER_STEP: timer_instance[timer_num]->attachInterrupt(Step_Handler); break; - case TEMP_TIMER_NUM: + case MF_TIMER_TEMP: timer_instance[timer_num]->attachInterrupt(Temp_Handler); break; } @@ -297,16 +303,16 @@ enum TimerPurpose { TP_SERIAL, TP_TONE, TP_SERVO, TP_STEP, TP_TEMP }; // 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 - {TP_SERIAL, get_timer_num_from_base_address(timer_serial[0])}, // Set in variant.h, or as a define in platformio.h if not present in variant.h + { TP_SERIAL, get_timer_num_from_base_address(timer_serial[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h #endif #if ENABLED(SPEAKER) - {TP_TONE, get_timer_num_from_base_address(timer_tone[0])}, // Set in variant.h, or as a define in platformio.h if not present in variant.h + { TP_TONE, get_timer_num_from_base_address(timer_tone[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h #endif #if HAS_SERVOS - {TP_SERVO, get_timer_num_from_base_address(timer_servo[0])}, // Set in variant.h, or as a define in platformio.h if not present in variant.h + { TP_SERVO, get_timer_num_from_base_address(timer_servo[0]) }, // Set in variant.h, or as a define in platformio.h if not present in variant.h #endif - {TP_STEP, STEP_TIMER}, - {TP_TEMP, TEMP_TIMER}, + { TP_STEP, STEP_TIMER }, + { TP_TEMP, TEMP_TIMER }, }; static constexpr bool verify_no_timer_conflicts() { diff --git a/Marlin/src/HAL/STM32/timers.h b/Marlin/src/HAL/STM32/timers.h index 7a35e432102df..6828998198af1 100644 --- a/Marlin/src/HAL/STM32/timers.h +++ b/Marlin/src/HAL/STM32/timers.h @@ -40,17 +40,13 @@ #define hal_timer_t uint32_t #define HAL_TIMER_TYPE_MAX UINT16_MAX -#define NUM_HARDWARE_TIMERS 2 +// Marlin timer_instance[] content (unrelated to timer selection) +#define MF_TIMER_STEP 0 // Timer Index for Stepper +#define MF_TIMER_TEMP 1 // Timer Index for Temperature +#define MF_TIMER_PULSE MF_TIMER_STEP -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper -#endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM -#endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature -#endif +#define TIMER_INDEX_(T) TIMER##T##_INDEX // TIMER#_INDEX enums (timer_index_t) depend on TIM#_BASE defines. +#define TIMER_INDEX(T) TIMER_INDEX_(T) // Convert Timer ID to HardwareTimer_Handle index. #define TEMP_TIMER_FREQUENCY 1000 // Temperature::isr() is expected to be called at around 1kHz @@ -64,12 +60,12 @@ extern uint32_t GetStepperTimerClkFreq(); #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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(); @@ -120,5 +116,5 @@ FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const ha } } -#define HAL_timer_isr_prologue(TIMER_NUM) -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_prologue(T) NOOP +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/STM32/usb_serial.cpp b/Marlin/src/HAL/STM32/usb_serial.cpp index b607275db5bb3..0b2372f3a79d3 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 +#if ENABLED(EMERGENCY_PARSER) && (USBD_USE_CDC || USBD_USE_CDC_MSC) #include "usb_serial.h" #include "../../feature/e_parser.h" diff --git a/Marlin/src/HAL/STM32/watchdog.cpp b/Marlin/src/HAL/STM32/watchdog.cpp deleted file mode 100644 index 1eccdec4985dc..0000000000000 --- a/Marlin/src/HAL/STM32/watchdog.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 HAL_STM32 - -#include "../../inc/MarlinConfigPre.h" - -#if ENABLED(USE_WATCHDOG) - -#define WDT_TIMEOUT_US TERN(WATCHDOG_DURATION_8S, 8000000, 4000000) // 4 or 8 second timeout - -#include "../../inc/MarlinConfig.h" - -#include "watchdog.h" -#include - -void watchdog_init() { - #if DISABLED(DISABLE_WATCHDOG_INIT) - IWatchdog.begin(WDT_TIMEOUT_US); - #endif -} - -void HAL_watchdog_refresh() { - IWatchdog.reload(); - #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) - TOGGLE(LED_PIN); // heartbeat indicator - #endif -} - -#endif // USE_WATCHDOG -#endif // HAL_STM32 diff --git a/Marlin/src/HAL/STM32F1/HAL.cpp b/Marlin/src/HAL/STM32F1/HAL.cpp index df1ba33d4adef..4d3140001e826 100644 --- a/Marlin/src/HAL/STM32F1/HAL.cpp +++ b/Marlin/src/HAL/STM32F1/HAL.cpp @@ -79,10 +79,11 @@ #define SCB_AIRCR_PRIGROUP_Msk (7UL << SCB_AIRCR_PRIGROUP_Pos) /*!< SCB AIRCR: PRIGROUP Mask */ // ------------------------ -// Public Variables +// Serial ports // ------------------------ #if defined(SERIAL_USB) && !HAS_SD_HOST_DRIVE + USBSerial SerialUSB; DefaultSerial1 MSerial0(true, SerialUSB); @@ -112,148 +113,78 @@ #endif #endif -uint16_t HAL_adc_result; +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #include + + void watchdogSetup() { + // do whatever. don't remove this function. + } + + /** + * The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0). + */ + #define STM32F1_WD_RELOAD TERN(WATCHDOG_DURATION_8S, 1250, 625) // 4 or 8 second timeout + + /** + * @brief Initialize the independent hardware watchdog. + * + * @return No return + * + * @details The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0). + */ + void MarlinHAL::watchdog_init() { + #if DISABLED(DISABLE_WATCHDOG_INIT) + iwdg_init(IWDG_PRE_256, STM32F1_WD_RELOAD); + #endif + } + + // Reset watchdog. MUST be called every 4 or 8 seconds after the + // first watchdog_init or the STM32F1 will reset. + void MarlinHAL::watchdog_refresh() { + #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) + TOGGLE(LED_PIN); // heartbeat indicator + #endif + iwdg_feed(); + } + +#endif // USE_WATCHDOG // ------------------------ -// Private Variables +// ADC // ------------------------ -STM32ADC adc(ADC1); -const uint8_t adc_pins[] = { - #if HAS_TEMP_ADC_0 - TEMP_0_PIN, - #endif - #if HAS_TEMP_ADC_PROBE - TEMP_PROBE_PIN, - #endif - #if HAS_HEATED_BED - TEMP_BED_PIN, - #endif - #if HAS_TEMP_CHAMBER - TEMP_CHAMBER_PIN, - #endif - #if HAS_TEMP_COOLER - TEMP_COOLER_PIN, - #endif - #if HAS_TEMP_ADC_1 - TEMP_1_PIN, - #endif - #if HAS_TEMP_ADC_2 - TEMP_2_PIN, - #endif - #if HAS_TEMP_ADC_3 - TEMP_3_PIN, - #endif - #if HAS_TEMP_ADC_4 - TEMP_4_PIN, - #endif - #if HAS_TEMP_ADC_5 - TEMP_5_PIN, - #endif - #if HAS_TEMP_ADC_6 - TEMP_6_PIN, - #endif - #if HAS_TEMP_ADC_7 - TEMP_7_PIN, - #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - FILWIDTH_PIN, - #endif - #if HAS_ADC_BUTTONS - ADC_KEYPAD_PIN, - #endif - #if HAS_JOY_ADC_X - JOY_X_PIN, - #endif - #if HAS_JOY_ADC_Y - JOY_Y_PIN, - #endif - #if HAS_JOY_ADC_Z - JOY_Z_PIN, - #endif - #if ENABLED(POWER_MONITOR_CURRENT) - POWER_MONITOR_CURRENT_PIN, - #endif - #if ENABLED(POWER_MONITOR_VOLTAGE) - POWER_MONITOR_VOLTAGE_PIN, - #endif -}; +// Watch out for recursion here! Our pin_t is signed, so pass through to Arduino -> analogRead(uint8_t) -enum TempPinIndex : char { - #if HAS_TEMP_ADC_0 - TEMP_0, - #endif - #if HAS_TEMP_ADC_PROBE - TEMP_PROBE, - #endif - #if HAS_HEATED_BED - TEMP_BED, - #endif - #if HAS_TEMP_CHAMBER - TEMP_CHAMBER, - #endif - #if HAS_TEMP_COOLER - TEMP_COOLER_PIN, - #endif - #if HAS_TEMP_ADC_1 - TEMP_1, - #endif - #if HAS_TEMP_ADC_2 - TEMP_2, - #endif - #if HAS_TEMP_ADC_3 - TEMP_3, - #endif - #if HAS_TEMP_ADC_4 - TEMP_4, - #endif - #if HAS_TEMP_ADC_5 - TEMP_5, - #endif - #if HAS_TEMP_ADC_6 - TEMP_6, - #endif - #if HAS_TEMP_ADC_7 - TEMP_7, - #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - FILWIDTH, - #endif - #if HAS_ADC_BUTTONS - ADC_KEY, - #endif - #if HAS_JOY_ADC_X - JOY_X, - #endif - #if HAS_JOY_ADC_Y - JOY_Y, - #endif - #if HAS_JOY_ADC_Z - JOY_Z, - #endif - #if ENABLED(POWER_MONITOR_CURRENT) - POWERMON_CURRENT, - #endif - #if ENABLED(POWER_MONITOR_VOLTAGE) - POWERMON_VOLTS, - #endif - ADC_PIN_COUNT -}; +uint16_t analogRead(const pin_t pin) { + const bool is_analog = _GET_MODE(pin) == GPIO_INPUT_ANALOG; + return is_analog ? analogRead(uint8_t(pin)) : 0; +} -uint16_t HAL_adc_results[ADC_PIN_COUNT]; +// Wrapper to maple unprotected analogWrite +void analogWrite(const pin_t pin, int pwm_val8) { + if (PWM_PIN(pin)) analogWrite(uint8_t(pin), pwm_val8); +} + +uint16_t MarlinHAL::adc_result; // ------------------------ // Private functions // ------------------------ + static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { uint32_t reg_value; - uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); /* only values 0..7 are used */ + uint32_t PriorityGroupTmp = (PriorityGroup & (uint32_t)0x07); // only values 0..7 are used - reg_value = SCB->AIRCR; /* read old register configuration */ - reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); /* clear bits to change */ + reg_value = SCB->AIRCR; // read old register configuration + reg_value &= ~(SCB_AIRCR_VECTKEY_Msk | SCB_AIRCR_PRIGROUP_Msk); // clear bits to change reg_value = (reg_value | ((uint32_t)0x5FA << SCB_AIRCR_VECTKEY_Pos) | - (PriorityGroupTmp << 8)); /* Insert write key & priority group */ + (PriorityGroupTmp << 8)); // Insert write key & priority group SCB->AIRCR = reg_value; } @@ -261,6 +192,8 @@ static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { // Public functions // ------------------------ +void flashFirmware(const int16_t) { hal.reboot(); } + // // Leave PA11/PA12 intact if USBSerial is not used // @@ -280,7 +213,11 @@ static void NVIC_SetPriorityGrouping(uint32_t PriorityGroup) { TERN_(POSTMORTEM_DEBUGGING, extern void install_min_serial()); -void HAL_init() { +// ------------------------ +// MarlinHAL class +// ------------------------ + +void MarlinHAL::init() { NVIC_SetPriorityGrouping(0x3); #if PIN_EXISTS(LED) OUT_WRITE(LED_PIN, LOW); @@ -299,7 +236,7 @@ void HAL_init() { } // HAL idle task -void HAL_idletask() { +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. @@ -314,14 +251,11 @@ void HAL_idletask() { #endif } -void HAL_clear_reset_source() { } +void MarlinHAL::reboot() { nvic_sys_reset(); } -/** - * TODO: Check this and change or remove. - */ -uint8_t HAL_get_reset_source() { return RST_POWER_ON; } - -void _delay_ms(const int delay_ms) { delay(delay_ms); } +// ------------------------ +// Free Memory Accessor +// ------------------------ extern "C" { extern unsigned int _ebss; // end of bss section @@ -358,103 +292,96 @@ extern "C" { // ------------------------ // ADC // ------------------------ + +enum ADCIndex : uint8_t { + OPTITEM(HAS_TEMP_ADC_0, TEMP_0) + OPTITEM(HAS_TEMP_ADC_1, TEMP_1) + OPTITEM(HAS_TEMP_ADC_2, TEMP_2) + OPTITEM(HAS_TEMP_ADC_3, TEMP_3) + OPTITEM(HAS_TEMP_ADC_4, TEMP_4) + OPTITEM(HAS_TEMP_ADC_5, TEMP_5) + OPTITEM(HAS_TEMP_ADC_6, TEMP_6) + OPTITEM(HAS_TEMP_ADC_7, TEMP_7) + OPTITEM(HAS_HEATED_BED, TEMP_BED) + OPTITEM(HAS_TEMP_CHAMBER, TEMP_CHAMBER) + OPTITEM(HAS_TEMP_ADC_PROBE, TEMP_PROBE) + OPTITEM(HAS_TEMP_COOLER, TEMP_COOLER) + OPTITEM(HAS_TEMP_BOARD, TEMP_BOARD) + OPTITEM(FILAMENT_WIDTH_SENSOR, FILWIDTH) + OPTITEM(HAS_ADC_BUTTONS, ADC_KEY) + OPTITEM(HAS_JOY_ADC_X, JOY_X) + OPTITEM(HAS_JOY_ADC_Y, JOY_Y) + OPTITEM(HAS_JOY_ADC_Z, JOY_Z) + OPTITEM(POWER_MONITOR_CURRENT, POWERMON_CURRENT) + OPTITEM(POWER_MONITOR_VOLTAGE, POWERMON_VOLTS) + ADC_COUNT +}; + +static uint16_t adc_results[ADC_COUNT]; + // Init the AD in continuous capture mode -void HAL_adc_init() { +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(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) + }; + static STM32ADC adc(ADC1); // configure the ADC adc.calibrate(); - #if F_CPU > 72000000 - adc.setSampleRate(ADC_SMPR_71_5); // 71.5 ADC cycles - #else - adc.setSampleRate(ADC_SMPR_41_5); // 41.5 ADC cycles - #endif - adc.setPins((uint8_t *)adc_pins, ADC_PIN_COUNT); - adc.setDMA(HAL_adc_results, (uint16_t)ADC_PIN_COUNT, (uint32_t)(DMA_MINC_MODE | DMA_CIRC_MODE), nullptr); + adc.setSampleRate((F_CPU > 72000000) ? ADC_SMPR_71_5 : ADC_SMPR_41_5); // 71.5 or 41.5 ADC cycles + adc.setPins((uint8_t *)adc_pins, ADC_COUNT); + adc.setDMA(adc_results, uint16_t(ADC_COUNT), uint32_t(DMA_MINC_MODE | DMA_CIRC_MODE), nullptr); adc.setScanMode(); adc.setContinuous(); adc.startConversion(); } -void HAL_adc_start_conversion(const uint8_t adc_pin) { - //TEMP_PINS pin_index; - TempPinIndex pin_index; - switch (adc_pin) { +void MarlinHAL::adc_start(const pin_t pin) { + #define __TCASE(N,I) case N: pin_index = I; break; + #define _TCASE(C,N,I) TERN_(C, __TCASE(N, I)) + ADCIndex pin_index; + switch (pin) { default: return; - #if HAS_TEMP_ADC_0 - case TEMP_0_PIN: pin_index = TEMP_0; break; - #endif - #if HAS_TEMP_ADC_PROBE - case TEMP_PROBE_PIN: pin_index = TEMP_PROBE; break; - #endif - #if HAS_HEATED_BED - case TEMP_BED_PIN: pin_index = TEMP_BED; break; - #endif - #if HAS_TEMP_CHAMBER - case TEMP_CHAMBER_PIN: pin_index = TEMP_CHAMBER; break; - #endif - #if HAS_TEMP_COOLER - case TEMP_COOLER_PIN: pin_index = TEMP_COOLER; break; - #endif - #if HAS_TEMP_ADC_1 - case TEMP_1_PIN: pin_index = TEMP_1; break; - #endif - #if HAS_TEMP_ADC_2 - case TEMP_2_PIN: pin_index = TEMP_2; break; - #endif - #if HAS_TEMP_ADC_3 - case TEMP_3_PIN: pin_index = TEMP_3; break; - #endif - #if HAS_TEMP_ADC_4 - case TEMP_4_PIN: pin_index = TEMP_4; break; - #endif - #if HAS_TEMP_ADC_5 - case TEMP_5_PIN: pin_index = TEMP_5; break; - #endif - #if HAS_TEMP_ADC_6 - case TEMP_6_PIN: pin_index = TEMP_6; break; - #endif - #if HAS_TEMP_ADC_7 - case TEMP_7_PIN: pin_index = TEMP_7; break; - #endif - #if HAS_JOY_ADC_X - case JOY_X_PIN: pin_index = JOY_X; break; - #endif - #if HAS_JOY_ADC_Y - case JOY_Y_PIN: pin_index = JOY_Y; break; - #endif - #if HAS_JOY_ADC_Z - case JOY_Z_PIN: pin_index = JOY_Z; break; - #endif - #if ENABLED(FILAMENT_WIDTH_SENSOR) - case FILWIDTH_PIN: pin_index = FILWIDTH; break; - #endif - #if HAS_ADC_BUTTONS - case ADC_KEYPAD_PIN: pin_index = ADC_KEY; break; - #endif - #if ENABLED(POWER_MONITOR_CURRENT) - case POWER_MONITOR_CURRENT_PIN: pin_index = POWERMON_CURRENT; break; - #endif - #if ENABLED(POWER_MONITOR_VOLTAGE) - case POWER_MONITOR_VOLTAGE_PIN: pin_index = POWERMON_VOLTS; break; - #endif + _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_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_VOLTS) } - HAL_adc_result = HAL_adc_results[(int)pin_index] >> (12 - HAL_ADC_RESOLUTION); // shift out unused bits + adc_result = (adc_results[(int)pin_index] & 0xFFF) >> (12 - HAL_ADC_RESOLUTION); // shift out unused bits } -uint16_t HAL_adc_get_result() { return HAL_adc_result; } - -uint16_t analogRead(pin_t pin) { - const bool is_analog = _GET_MODE(pin) == GPIO_INPUT_ANALOG; - return is_analog ? analogRead(uint8_t(pin)) : 0; -} - -// Wrapper to maple unprotected analogWrite -void analogWrite(pin_t pin, int pwm_val8) { - if (PWM_PIN(pin)) - analogWrite(uint8_t(pin), pwm_val8); -} - -void HAL_reboot() { nvic_sys_reset(); } - -void flashFirmware(const int16_t) { HAL_reboot(); } - #endif // __STM32F1__ diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h index 3bdfb9703c643..b14b5f7e79260 100644 --- a/Marlin/src/HAL/STM32F1/HAL.h +++ b/Marlin/src/HAL/STM32F1/HAL.h @@ -34,7 +34,6 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include #include @@ -66,6 +65,10 @@ #endif #endif +// ------------------------ +// Serial ports +// ------------------------ + #ifdef SERIAL_USB typedef ForwardSerial1Class< USBSerial > DefaultSerial1; extern DefaultSerial1 MSerial0; @@ -141,11 +144,6 @@ #endif #endif -// Set interrupt grouping for this MCU -void HAL_init(); -#define HAL_IDLETASK 1 -void HAL_idletask(); - /** * TODO: review this to return 1 for pins that are not analog input */ @@ -158,15 +156,7 @@ void HAL_idletask(); #define NO_COMPILE_TIME_PWM #endif -#define CRITICAL_SECTION_START() uint32_t primask = __get_primask(); (void)__iCliRetVal() -#define CRITICAL_SECTION_END() if (!primask) (void)__iSeiRetVal() -#define ISRS_ENABLED() (!__get_primask()) -#define ENABLE_ISRS() ((void)__iSeiRetVal()) -#define DISABLE_ISRS() ((void)__iCliRetVal()) - -// On AVR this is in math.h? -#define square(x) ((x)*(x)) - +// Reset Reason #define RST_POWER_ON 1 #define RST_EXTERNAL 2 #define RST_BROWN_OUT 4 @@ -182,46 +172,63 @@ void HAL_idletask(); typedef int8_t pin_t; // ------------------------ -// Public Variables +// Interrupts // ------------------------ -// Result of last ADC conversion -extern uint16_t HAL_adc_result; +#define CRITICAL_SECTION_START() const bool irqon = !__get_primask(); (void)__iCliRetVal() +#define CRITICAL_SECTION_END() if (!irqon) (void)__iSeiRetVal() +#define cli() noInterrupts() +#define sei() interrupts() // ------------------------ -// Public functions +// ADC // ------------------------ -// Disable interrupts -#define cli() noInterrupts() +#ifdef ADC_RESOLUTION + #define HAL_ADC_RESOLUTION ADC_RESOLUTION +#else + #define HAL_ADC_RESOLUTION 12 +#endif -// Enable interrupts -#define sei() interrupts() +#define HAL_ADC_VREF 3.3 -// Memory related -#define __bss_end __bss_end__ +uint16_t analogRead(const pin_t pin); // need hal.adc_enable() first +void analogWrite(const pin_t pin, int pwm_val8); // PWM only! mul by 257 in maple!? -// Clear reset reason -void HAL_clear_reset_source(); +// +// 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) -// Reset reason -uint8_t HAL_get_reset_source(); +#define JTAG_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY) +#define JTAGSWD_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_NONE) -void HAL_reboot(); +#define PLATFORM_M997_SUPPORT +void flashFirmware(const int16_t); -void _delay_ms(const int delay); +#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment +#ifndef PWM_FREQUENCY + #define PWM_FREQUENCY 1000 // Default PWM Frequency +#endif -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wunused-function" +// ------------------------ +// Class Utilities +// ------------------------ -/* -extern "C" { - int freeMemory(); -} -*/ +// Memory related +#define __bss_end __bss_end__ + +void _delay_ms(const int ms); extern "C" char* _sbrk(int incr); +#pragma GCC diagnostic push +#if GCC_VERSION <= 50000 + #pragma GCC diagnostic ignored "-Wunused-function" +#endif + static inline int freeMemory() { volatile char top; return &top - _sbrk(0); @@ -229,54 +236,74 @@ static inline int freeMemory() { #pragma GCC diagnostic pop -// -// ADC -// +// ------------------------ +// MarlinHAL Class +// ------------------------ -#define HAL_ANALOG_SELECT(pin) pinMode(pin, INPUT_ANALOG); +class MarlinHAL { +public: -void HAL_adc_init(); + // Earliest possible init, before setup() + MarlinHAL() {} -#ifdef ADC_RESOLUTION - #define HAL_ADC_RESOLUTION ADC_RESOLUTION -#else - #define HAL_ADC_RESOLUTION 12 -#endif + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -#define HAL_ADC_VREF 3.3 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_result -#define HAL_ADC_READY() true + static void init(); // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -void HAL_adc_start_conversion(const uint8_t adc_pin); -uint16_t HAL_adc_get_result(); + // Interrupts + static bool isr_state() { return !__get_primask(); } + static void isr_on() { ((void)__iSeiRetVal()); } + static void isr_off() { ((void)__iCliRetVal()); } -uint16_t analogRead(pin_t pin); // need HAL_ANALOG_SELECT() first -void analogWrite(pin_t pin, int pwm_val8); // PWM only! mul by 257 in maple!? + static void delay_ms(const int ms) { delay(ms); } -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + // Tasks, called from idle() + static void idletask(); -#define JTAG_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_SW_ONLY) -#define JTAGSWD_DISABLE() afio_cfg_debug_ports(AFIO_DEBUG_NONE) + // Reset + static uint8_t get_reset_source() { return RST_POWER_ON; } + static void clear_reset_source() {} -#define PLATFORM_M997_SUPPORT -void flashFirmware(const int16_t); + // Free SRAM + static int freeMemory() { return ::freeMemory(); } -#define HAL_CAN_SET_PWM_FREQ // This HAL supports PWM Frequency adjustment + // + // ADC Methods + // -/** - * set_pwm_frequency - * Set the frequency of the timer corresponding to the provided pin - * All Timer PWM pins run at the same frequency - */ -void set_pwm_frequency(const pin_t pin, int f_desired); + static uint16_t adc_result; -/** - * set_pwm_duty - * Set the PWM duty cycle of the provided pin to the provided value - * Optionally allows inverting the duty cycle [default = false] - * Optionally allows changing the maximum size of the provided value to enable finer PWM duty control [default = 255] - */ -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size=255, const bool invert=false); + // 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) { pinMode(pin, INPUT_ANALOG); } + + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static void adc_start(const pin_t pin); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The 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] + * The timer must be pre-configured with set_pwm_frequency() if the default frequency is not desired. + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=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/STM32F1/HAL_MinSerial.cpp b/Marlin/src/HAL/STM32F1/MinSerial.cpp similarity index 97% rename from Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp rename to Marlin/src/HAL/STM32F1/MinSerial.cpp index 0fc3d014d4848..6cf68d8d8f456 100644 --- a/Marlin/src/HAL/STM32F1/HAL_MinSerial.cpp +++ b/Marlin/src/HAL/STM32F1/MinSerial.cpp @@ -26,8 +26,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) -#include "../shared/HAL_MinSerial.h" -#include "watchdog.h" +#include "../shared/MinSerial.h" #include #include @@ -82,7 +81,7 @@ static void TX(char c) { #if WITHIN(SERIAL_PORT, 1, 6) struct usart_dev* dev = MYSERIAL1.c_dev(); while (!(dev->regs->SR & USART_SR_TXE)) { - TERN_(USE_WATCHDOG, HAL_watchdog_refresh()); + hal.watchdog_refresh(); sw_barrier(); } dev->regs->DR = c; diff --git a/Marlin/src/HAL/STM32F1/SPI.cpp b/Marlin/src/HAL/STM32F1/SPI.cpp index 8bfa3d236a7cf..1ce2c7d3fd5dc 100644 --- a/Marlin/src/HAL/STM32F1/SPI.cpp +++ b/Marlin/src/HAL/STM32F1/SPI.cpp @@ -91,6 +91,14 @@ static const spi_pins board_spi_pins[] __FLASH__ = { static void *_spi3_this; #endif +/** + * @brief Wait until TXE (tx empty) flag is set and BSY (busy) flag unset. + */ +static inline void waitSpiTxEnd(spi_dev *spi_d) { + while (spi_is_tx_empty(spi_d) == 0) { /* nada */ } // wait until TXE=1 + while (spi_is_busy(spi_d) != 0) { /* nada */ } // wait until BSY=0 +} + /** * Constructor */ diff --git a/Marlin/src/HAL/STM32F1/SPI.h b/Marlin/src/HAL/STM32F1/SPI.h index 2467432e07184..13f4d5ed6cfe4 100644 --- a/Marlin/src/HAL/STM32F1/SPI.h +++ b/Marlin/src/HAL/STM32F1/SPI.h @@ -414,12 +414,4 @@ class SPIClass { */ }; -/** - * @brief Wait until TXE (tx empty) flag is set and BSY (busy) flag unset. - */ -static inline void waitSpiTxEnd(spi_dev *spi_d) { - while (spi_is_tx_empty(spi_d) == 0) { /* nada */ } // wait until TXE=1 - while (spi_is_busy(spi_d) != 0) { /* nada */ } // wait until BSY=0 -} - extern SPIClass SPI; diff --git a/Marlin/src/HAL/STM32F1/Servo.cpp b/Marlin/src/HAL/STM32F1/Servo.cpp index 36f7c6d51273e..47ffb631cf8fa 100644 --- a/Marlin/src/HAL/STM32F1/Servo.cpp +++ b/Marlin/src/HAL/STM32F1/Servo.cpp @@ -60,7 +60,7 @@ uint8_t ServoCount = 0; #define US_TO_ANGLE(us) int16_t(map((us), SERVO_DEFAULT_MIN_PW, SERVO_DEFAULT_MAX_PW, minAngle, maxAngle)) void libServo::servoWrite(uint8_t inPin, uint16_t duty_cycle) { - #ifdef SERVO0_TIMER_NUM + #ifdef MF_TIMER_SERVO0 if (servoIndex == 0) { pwmSetDuty(duty_cycle); return; @@ -74,7 +74,7 @@ void libServo::servoWrite(uint8_t inPin, uint16_t duty_cycle) { libServo::libServo() { servoIndex = ServoCount < MAX_SERVOS ? ServoCount++ : INVALID_SERVO; - timer_set_interrupt_priority(SERVO0_TIMER_NUM, SERVO0_TIMER_IRQ_PRIO); + HAL_timer_set_interrupt_priority(MF_TIMER_SERVO0, SERVO0_TIMER_IRQ_PRIO); } bool libServo::attach(const int32_t inPin, const int32_t inMinAngle, const int32_t inMaxAngle) { @@ -85,7 +85,7 @@ bool libServo::attach(const int32_t inPin, const int32_t inMinAngle, const int32 maxAngle = inMaxAngle; angle = -1; - #ifdef SERVO0_TIMER_NUM + #ifdef MF_TIMER_SERVO0 if (servoIndex == 0 && setupSoftPWM(inPin)) { pin = inPin; // set attached() return true; @@ -119,7 +119,7 @@ bool libServo::detach() { int32_t libServo::read() const { if (attached()) { - #ifdef SERVO0_TIMER_NUM + #ifdef MF_TIMER_SERVO0 if (servoIndex == 0) return angle; #endif timer_dev *tdev = PIN_MAP[pin].timer_device; @@ -141,35 +141,35 @@ void libServo::move(const int32_t value) { } } -#ifdef SERVO0_TIMER_NUM +#ifdef MF_TIMER_SERVO0 extern "C" void Servo_IRQHandler() { - static timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM); + static timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0); uint16_t SR = timer_get_status(tdev); if (SR & TIMER_SR_CC1IF) { // channel 1 off #ifdef SERVO0_PWM_OD - OUT_WRITE_OD(SERVO0_PIN, 1); // off + OUT_WRITE_OD(SERVO0_PIN, HIGH); // off #else - OUT_WRITE(SERVO0_PIN, 0); + OUT_WRITE(SERVO0_PIN, LOW); #endif timer_reset_status_bit(tdev, TIMER_SR_CC1IF_BIT); } if (SR & TIMER_SR_CC2IF) { // channel 2 resume #ifdef SERVO0_PWM_OD - OUT_WRITE_OD(SERVO0_PIN, 0); // on + OUT_WRITE_OD(SERVO0_PIN, LOW); // on #else - OUT_WRITE(SERVO0_PIN, 1); + OUT_WRITE(SERVO0_PIN, HIGH); #endif timer_reset_status_bit(tdev, TIMER_SR_CC2IF_BIT); } } bool libServo::setupSoftPWM(const int32_t inPin) { - timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM); + timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0); if (!tdev) return false; #ifdef SERVO0_PWM_OD - OUT_WRITE_OD(inPin, 1); + OUT_WRITE_OD(inPin, HIGH); #else - OUT_WRITE(inPin, 0); + OUT_WRITE(inPin, LOW); #endif timer_pause(tdev); @@ -189,7 +189,7 @@ void libServo::move(const int32_t value) { } void libServo::pwmSetDuty(const uint16_t duty_cycle) { - timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM); + timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0); timer_set_compare(tdev, 1, duty_cycle); timer_generate_update(tdev); if (duty_cycle) { @@ -200,15 +200,15 @@ void libServo::move(const int32_t value) { timer_disable_irq(tdev, 1); timer_disable_irq(tdev, 2); #ifdef SERVO0_PWM_OD - OUT_WRITE_OD(pin, 1); // off + OUT_WRITE_OD(pin, HIGH); // off #else - OUT_WRITE(pin, 0); + OUT_WRITE(pin, LOW); #endif } } void libServo::pauseSoftPWM() { // detach - timer_dev *tdev = get_timer_dev(SERVO0_TIMER_NUM); + timer_dev *tdev = HAL_get_timer_dev(MF_TIMER_SERVO0); timer_pause(tdev); pwmSetDuty(0); } diff --git a/Marlin/src/HAL/STM32F1/Servo.h b/Marlin/src/HAL/STM32F1/Servo.h index b6143de81d62a..745a1c93f07d4 100644 --- a/Marlin/src/HAL/STM32F1/Servo.h +++ b/Marlin/src/HAL/STM32F1/Servo.h @@ -35,7 +35,8 @@ #define SERVO_DEFAULT_MIN_ANGLE 0 #define SERVO_DEFAULT_MAX_ANGLE 180 -#define HAL_SERVO_LIB libServo +class libServo; +typedef libServo hal_servo_t; class libServo { public: diff --git a/Marlin/src/HAL/STM32F1/build_flags.py b/Marlin/src/HAL/STM32F1/build_flags.py index d0848d1c64383..970ca8b767f9f 100755 --- a/Marlin/src/HAL/STM32F1/build_flags.py +++ b/Marlin/src/HAL/STM32F1/build_flags.py @@ -30,25 +30,27 @@ # extra script for linker options else: - from SCons.Script import DefaultEnvironment - env = DefaultEnvironment() - env.Append( + 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" + "-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", + "-Os", + "-mcpu=cortex-m3", + "-ffreestanding", + "-mthumb", + "--specs=nano.specs", + "--specs=nosys.specs", + "-u_printf_float", ], - ) + ) diff --git a/Marlin/src/HAL/STM32F1/fast_pwm.cpp b/Marlin/src/HAL/STM32F1/fast_pwm.cpp index 884d482af5f15..297804a3ac42a 100644 --- a/Marlin/src/HAL/STM32F1/fast_pwm.cpp +++ b/Marlin/src/HAL/STM32F1/fast_pwm.cpp @@ -21,32 +21,57 @@ */ #ifdef __STM32F1__ -#include "../../inc/MarlinConfigPre.h" - -#if NEEDS_HARDWARE_PWM +#include "../../inc/MarlinConfig.h" #include -#include "HAL.h" -#include "timers.h" -void set_pwm_frequency(const pin_t pin, int f_desired) { +#define NR_TIMERS TERN(STM32_XL_DENSITY, 14, 8) // Maple timers, 14 for STM32_XL_DENSITY (F/G chips), 8 for HIGH density (C D E) + +static uint16_t timer_freq[NR_TIMERS]; + +inline uint8_t timer_and_index_for_pin(const pin_t pin, timer_dev **timer_ptr) { + *timer_ptr = PIN_MAP[pin].timer_device; + for (uint8_t i = 0; i < NR_TIMERS; i++) if (*timer_ptr == HAL_get_timer_dev(i)) + return i; + return 0; +} + +void MarlinHAL::set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { + const uint16_t duty = invert ? v_size - v : v; + if (PWM_PIN(pin)) { + timer_dev *timer; UNUSED(timer); + if (timer_freq[timer_and_index_for_pin(pin, &timer)] == 0) + set_pwm_frequency(pin, PWM_FREQUENCY); + const uint8_t channel = PIN_MAP[pin].timer_channel; + timer_set_compare(timer, channel, duty); + timer_set_mode(timer, channel, TIMER_PWM); // PWM Output Mode + } + else { + pinMode(pin, OUTPUT); + digitalWrite(pin, duty < v_size / 2 ? LOW : HIGH); + } +} + +void MarlinHAL::set_pwm_frequency(const pin_t pin, const uint16_t f_desired) { if (!PWM_PIN(pin)) return; // Don't proceed if no hardware timer - timer_dev *timer = PIN_MAP[pin].timer_device; - uint8_t channel = PIN_MAP[pin].timer_channel; + timer_dev *timer; UNUSED(timer); + timer_freq[timer_and_index_for_pin(pin, &timer)] = f_desired; // Protect used timers - if (timer == get_timer_dev(TEMP_TIMER_NUM)) return; - if (timer == get_timer_dev(STEP_TIMER_NUM)) return; - #if PULSE_TIMER_NUM != STEP_TIMER_NUM - if (timer == get_timer_dev(PULSE_TIMER_NUM)) return; + if (timer == HAL_get_timer_dev(MF_TIMER_TEMP)) return; + if (timer == HAL_get_timer_dev(MF_TIMER_STEP)) return; + #if MF_TIMER_PULSE != MF_TIMER_STEP + if (timer == HAL_get_timer_dev(MF_TIMER_PULSE)) return; #endif if (!(timer->regs.bas->SR & TIMER_CR1_CEN)) // Ensure the timer is enabled timer_init(timer); + const uint8_t channel = PIN_MAP[pin].timer_channel; timer_set_mode(timer, channel, TIMER_PWM); - uint16_t preload = 255; // Lock 255 PWM resolution for high frequencies + // Preload (resolution) cannot be equal to duty of 255 otherwise it may not result in digital off or on. + uint16_t preload = 254; int32_t prescaler = (HAL_TIMER_RATE) / (preload + 1) / f_desired - 1; if (prescaler > 65535) { // For low frequencies increase prescaler prescaler = 65535; @@ -57,12 +82,4 @@ void set_pwm_frequency(const pin_t pin, int f_desired) { timer_set_prescaler(timer, prescaler); } -void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t v_size/*=255*/, const bool invert/*=false*/) { - timer_dev *timer = PIN_MAP[pin].timer_device; - uint16_t max_val = timer->regs.bas->ARR * v / v_size; - if (invert) max_val = v_size - max_val; - pwmWrite(pin, max_val); -} - -#endif // NEEDS_HARDWARE_PWM #endif // __STM32F1__ diff --git a/Marlin/src/HAL/STM32F1/inc/SanityCheck.h b/Marlin/src/HAL/STM32F1/inc/SanityCheck.h index 2846155c351d8..fe8f6e0ec24b4 100644 --- a/Marlin/src/HAL/STM32F1/inc/SanityCheck.h +++ b/Marlin/src/HAL/STM32F1/inc/SanityCheck.h @@ -39,7 +39,7 @@ #error "SERIAL_STATS_DROPPED_RX is not supported on the STM32F1 platform." #endif -#if ENABLED(NEOPIXEL_LED) && DISABLED(MKS_MINI_12864_V3) +#if ENABLED(NEOPIXEL_LED) && DISABLED(FYSETC_MINI_12864_2_1) #error "NEOPIXEL_LED (Adafruit NeoPixel) is not supported for HAL/STM32F1. Comment out this line to proceed at your own risk!" #endif diff --git a/Marlin/src/HAL/STM32F1/onboard_sd.cpp b/Marlin/src/HAL/STM32F1/onboard_sd.cpp index df98c2c057af9..a3d8dcb2d57e9 100644 --- a/Marlin/src/HAL/STM32F1/onboard_sd.cpp +++ b/Marlin/src/HAL/STM32F1/onboard_sd.cpp @@ -38,8 +38,13 @@ #define SPI_CLOCK_MAX SPI_BAUD_PCLK_DIV_2 #endif -#define CS_LOW() WRITE(ONBOARD_SD_CS_PIN, LOW) // Set OnboardSPI cs low -#define CS_HIGH() WRITE(ONBOARD_SD_CS_PIN, HIGH) // Set OnboardSPI cs high +#if PIN_EXISTS(ONBOARD_SD_CS) && ONBOARD_SD_CS_PIN != SD_SS_PIN + #define CS_LOW() WRITE(ONBOARD_SD_CS_PIN, LOW) // Set OnboardSPI cs low + #define CS_HIGH() WRITE(ONBOARD_SD_CS_PIN, HIGH) // Set OnboardSPI cs high +#else + #define CS_LOW() + #define CS_HIGH() +#endif #define FCLK_FAST() ONBOARD_SD_SPI.setClockDivider(SPI_CLOCK_MAX) #define FCLK_SLOW() ONBOARD_SD_SPI.setClockDivider(SPI_BAUD_PCLK_DIV_256) @@ -278,7 +283,7 @@ DSTATUS disk_initialize ( if (drv) return STA_NOINIT; // Supports only drive 0 sd_power_on(); // Initialize SPI - if (Stat & STA_NODISK) return Stat; // Is a card existing in the soket? + if (Stat & STA_NODISK) return Stat; // Is a card existing in the socket? FCLK_SLOW(); for (n = 10; n; n--) xchg_spi(0xFF); // Send 80 dummy clocks diff --git a/Marlin/src/HAL/STM32F1/pinsDebug.h b/Marlin/src/HAL/STM32F1/pinsDebug.h index 27f4b6732bfb3..7828479658a9e 100644 --- a/Marlin/src/HAL/STM32F1/pinsDebug.h +++ b/Marlin/src/HAL/STM32F1/pinsDebug.h @@ -54,11 +54,11 @@ 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 -static inline int8_t get_pin_mode(pin_t pin) { +static int8_t get_pin_mode(pin_t pin) { return VALID_PIN(pin) ? _GET_MODE(pin) : -1; } -static inline pin_t DIGITAL_PIN_TO_ANALOG_PIN(pin_t pin) { +static pin_t DIGITAL_PIN_TO_ANALOG_PIN(pin_t pin) { if (!VALID_PIN(pin)) return -1; int8_t adc_channel = int8_t(PIN_MAP[pin].adc_channel); #ifdef NUM_ANALOG_INPUTS @@ -67,7 +67,7 @@ static inline pin_t DIGITAL_PIN_TO_ANALOG_PIN(pin_t pin) { return pin_t(adc_channel); } -static inline bool IS_ANALOG(pin_t pin) { +static bool IS_ANALOG(pin_t pin) { if (!VALID_PIN(pin)) return false; if (PIN_MAP[pin].adc_channel != ADCx) { #ifdef NUM_ANALOG_INPUTS @@ -78,11 +78,11 @@ static inline bool IS_ANALOG(pin_t pin) { return false; } -static inline bool GET_PINMODE(const pin_t pin) { +static bool GET_PINMODE(const pin_t pin) { return VALID_PIN(pin) && !IS_INPUT(pin); } -static inline bool GET_ARRAY_IS_DIGITAL(const int16_t array_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) #ifdef NUM_ANALOG_INPUTS @@ -93,7 +93,7 @@ static inline bool GET_ARRAY_IS_DIGITAL(const int16_t array_pin) { #include "../../inc/MarlinConfig.h" // Allow pins/pins.h to set density -static inline void pwm_details(const pin_t pin) { +static void pwm_details(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; @@ -113,7 +113,7 @@ static inline void pwm_details(const pin_t pin) { } } -static inline void print_port(pin_t pin) { +static void print_port(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/tft/tft_spi.cpp b/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp index 9bf6bbb32bc7f..f447cec811078 100644 --- a/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp +++ b/Marlin/src/HAL/STM32F1/tft/tft_spi.cpp @@ -30,7 +30,7 @@ SPIClass TFT_SPI::SPIx(1); void TFT_SPI::Init() { #if PIN_EXISTS(TFT_RESET) - OUT_WRITE(TFT_RST_PIN, HIGH); + OUT_WRITE(TFT_RESET_PIN, HIGH); delay(100); #endif diff --git a/Marlin/src/HAL/STM32F1/tft/xpt2046.h b/Marlin/src/HAL/STM32F1/tft/xpt2046.h index aba0799e445f8..7c456cf00e1be 100644 --- a/Marlin/src/HAL/STM32F1/tft/xpt2046.h +++ b/Marlin/src/HAL/STM32F1/tft/xpt2046.h @@ -65,8 +65,8 @@ class XPT2046 { static uint16_t getRawData(const XPTCoordinate coordinate); static bool isTouched(); - static inline void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); }; - static inline void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; + static void DataTransferBegin() { WRITE(TOUCH_CS_PIN, LOW); }; + static void DataTransferEnd() { WRITE(TOUCH_CS_PIN, HIGH); }; #if ENABLED(TOUCH_BUTTONS_HW_SPI) static uint16_t HardwareIO(uint16_t data); #endif diff --git a/Marlin/src/HAL/STM32F1/timers.cpp b/Marlin/src/HAL/STM32F1/timers.cpp index 8c2df1e216e7d..112c730b9accb 100644 --- a/Marlin/src/HAL/STM32F1/timers.cpp +++ b/Marlin/src/HAL/STM32F1/timers.cpp @@ -47,10 +47,7 @@ * TODO: Calculate Timer prescale value, so we get the 32bit to adjust */ - - - -void timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority) { +void HAL_timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority) { nvic_irq_num irq_num; switch (timer_num) { case 1: irq_num = NVIC_TIMER1_CC; break; @@ -73,7 +70,6 @@ void timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority) nvic_irq_set_priority(irq_num, priority); } - void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { /** * Give the Stepper ISR a higher priority (lower number) @@ -81,7 +77,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { */ switch (timer_num) { - case STEP_TIMER_NUM: + case MF_TIMER_STEP: timer_pause(STEP_TIMER_DEV); timer_set_mode(STEP_TIMER_DEV, STEP_TIMER_CHAN, TIMER_OUTPUT_COMPARE); // counter timer_set_count(STEP_TIMER_DEV, 0); @@ -91,11 +87,11 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { timer_set_compare(STEP_TIMER_DEV, STEP_TIMER_CHAN, _MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (STEPPER_TIMER_RATE) / frequency)); timer_no_ARR_preload_ARPE(STEP_TIMER_DEV); // Need to be sure no preload on ARR register timer_attach_interrupt(STEP_TIMER_DEV, STEP_TIMER_CHAN, stepTC_Handler); - timer_set_interrupt_priority(STEP_TIMER_NUM, STEP_TIMER_IRQ_PRIO); + HAL_timer_set_interrupt_priority(MF_TIMER_STEP, STEP_TIMER_IRQ_PRIO); timer_generate_update(STEP_TIMER_DEV); timer_resume(STEP_TIMER_DEV); break; - case TEMP_TIMER_NUM: + case MF_TIMER_TEMP: timer_pause(TEMP_TIMER_DEV); timer_set_mode(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, TIMER_OUTPUT_COMPARE); timer_set_count(TEMP_TIMER_DEV, 0); @@ -103,7 +99,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { timer_set_reload(TEMP_TIMER_DEV, 0xFFFF); timer_set_compare(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, _MIN(hal_timer_t(HAL_TIMER_TYPE_MAX), (F_CPU) / (TEMP_TIMER_PRESCALE) / frequency)); timer_attach_interrupt(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, tempTC_Handler); - timer_set_interrupt_priority(TEMP_TIMER_NUM, TEMP_TIMER_IRQ_PRIO); + HAL_timer_set_interrupt_priority(MF_TIMER_TEMP, TEMP_TIMER_IRQ_PRIO); timer_generate_update(TEMP_TIMER_DEV); timer_resume(TEMP_TIMER_DEV); break; @@ -112,31 +108,31 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_enable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case STEP_TIMER_NUM: ENABLE_STEPPER_DRIVER_INTERRUPT(); break; - case TEMP_TIMER_NUM: ENABLE_TEMPERATURE_INTERRUPT(); break; + case MF_TIMER_STEP: ENABLE_STEPPER_DRIVER_INTERRUPT(); break; + case MF_TIMER_TEMP: ENABLE_TEMPERATURE_INTERRUPT(); break; } } void HAL_timer_disable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case STEP_TIMER_NUM: DISABLE_STEPPER_DRIVER_INTERRUPT(); break; - case TEMP_TIMER_NUM: DISABLE_TEMPERATURE_INTERRUPT(); break; + case MF_TIMER_STEP: DISABLE_STEPPER_DRIVER_INTERRUPT(); break; + case MF_TIMER_TEMP: DISABLE_TEMPERATURE_INTERRUPT(); break; } } -static inline bool timer_irq_enabled(const timer_dev * const dev, const uint8_t interrupt) { +static inline bool HAL_timer_irq_enabled(const timer_dev * const dev, const uint8_t interrupt) { return bool(*bb_perip(&(dev->regs).gen->DIER, interrupt)); } bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { switch (timer_num) { - case STEP_TIMER_NUM: return timer_irq_enabled(STEP_TIMER_DEV, STEP_TIMER_CHAN); - case TEMP_TIMER_NUM: return timer_irq_enabled(TEMP_TIMER_DEV, TEMP_TIMER_CHAN); + case MF_TIMER_STEP: return HAL_timer_irq_enabled(STEP_TIMER_DEV, STEP_TIMER_CHAN); + case MF_TIMER_TEMP: return HAL_timer_irq_enabled(TEMP_TIMER_DEV, TEMP_TIMER_CHAN); } return false; } -timer_dev* get_timer_dev(int number) { +timer_dev* HAL_get_timer_dev(int number) { switch (number) { #if STM32_HAVE_TIMER(1) case 1: return &timer1; diff --git a/Marlin/src/HAL/STM32F1/timers.h b/Marlin/src/HAL/STM32F1/timers.h index c89d55a134b47..0cd807fc84799 100644 --- a/Marlin/src/HAL/STM32F1/timers.h +++ b/Marlin/src/HAL/STM32F1/timers.h @@ -65,30 +65,30 @@ typedef uint16_t hal_timer_t; * - Otherwise it uses Timer 8 on boards with STM32_HIGH_DENSITY * or Timer 4 on other boards. */ -#ifndef STEP_TIMER_NUM +#ifndef MF_TIMER_STEP #if defined(MCU_STM32F103CB) || defined(MCU_STM32F103C8) - #define STEP_TIMER_NUM 4 // For C8/CB boards, use timer 4 + #define MF_TIMER_STEP 4 // For C8/CB boards, use timer 4 #else - #define STEP_TIMER_NUM 5 // for other boards, five is fine. + #define MF_TIMER_STEP 5 // for other boards, five is fine. #endif #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 2 // Timer Index for Temperature - //#define TEMP_TIMER_NUM 4 // 2->4, Timer 2 for Stepper Current PWM +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 2 // Timer Index for Temperature + //#define MF_TIMER_TEMP 4 // 2->4, Timer 2 for Stepper Current PWM #endif #if MB(BTT_SKR_MINI_E3_V1_0, BTT_SKR_E3_DIP, BTT_SKR_MINI_E3_V1_2, MKS_ROBIN_LITE, MKS_ROBIN_E3D, MKS_ROBIN_E3) // SKR Mini E3 boards use PA8 as FAN_PIN, so TIMER 1 is used for Fan PWM. #ifdef STM32_HIGH_DENSITY - #define SERVO0_TIMER_NUM 8 // tone.cpp uses Timer 4 + #define MF_TIMER_SERVO0 8 // tone.cpp uses Timer 4 #else - #define SERVO0_TIMER_NUM 3 // tone.cpp uses Timer 8 + #define MF_TIMER_SERVO0 3 // tone.cpp uses Timer 8 #endif #else - #define SERVO0_TIMER_NUM 1 // SERVO0 or BLTOUCH + #define MF_TIMER_SERVO0 1 // SERVO0 or BLTOUCH #endif #define STEP_TIMER_IRQ_PRIO 2 @@ -98,22 +98,22 @@ typedef uint16_t hal_timer_t; #define TEMP_TIMER_PRESCALE 1000 // prescaler for setting Temp timer, 72Khz #define TEMP_TIMER_FREQUENCY 1000 // temperature interrupt frequency -#define STEPPER_TIMER_PRESCALE 18 // prescaler for setting stepper timer, 4Mhz -#define STEPPER_TIMER_RATE (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) // frequency of stepper timer -#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs +#define STEPPER_TIMER_PRESCALE 18 // prescaler for setting stepper timer, 4Mhz +#define STEPPER_TIMER_RATE (HAL_TIMER_RATE / STEPPER_TIMER_PRESCALE) // frequency of stepper timer +#define STEPPER_TIMER_TICKS_PER_US ((STEPPER_TIMER_RATE) / 1000000) // stepper timer ticks per µs -#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 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 -timer_dev* get_timer_dev(int number); -#define TIMER_DEV(num) get_timer_dev(num) -#define STEP_TIMER_DEV TIMER_DEV(STEP_TIMER_NUM) -#define TEMP_TIMER_DEV TIMER_DEV(TEMP_TIMER_NUM) +timer_dev* HAL_get_timer_dev(int number); +#define TIMER_DEV(num) HAL_get_timer_dev(num) +#define STEP_TIMER_DEV TIMER_DEV(MF_TIMER_STEP) +#define TEMP_TIMER_DEV TIMER_DEV(MF_TIMER_TEMP) #define ENABLE_STEPPER_DRIVER_INTERRUPT() timer_enable_irq(STEP_TIMER_DEV, STEP_TIMER_CHAN) #define DISABLE_STEPPER_DRIVER_INTERRUPT() timer_disable_irq(STEP_TIMER_DEV, STEP_TIMER_CHAN) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(MF_TIMER_STEP) #define ENABLE_TEMPERATURE_INTERRUPT() timer_enable_irq(TEMP_TIMER_DEV, TEMP_TIMER_CHAN) #define DISABLE_TEMPERATURE_INTERRUPT() timer_disable_irq(TEMP_TIMER_DEV, TEMP_TIMER_CHAN) @@ -138,8 +138,8 @@ extern "C" { // Public Variables // ------------------------ -//static HardwareTimer StepperTimer(STEP_TIMER_NUM); -//static HardwareTimer TempTimer(TEMP_TIMER_NUM); +//static HardwareTimer StepperTimer(MF_TIMER_STEP); +//static HardwareTimer TempTimer(MF_TIMER_TEMP); // ------------------------ // Public functions @@ -163,13 +163,13 @@ bool HAL_timer_interrupt_enabled(const uint8_t timer_num); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { switch (timer_num) { - case STEP_TIMER_NUM: + case MF_TIMER_STEP: // NOTE: WE have set ARPE = 0, which means the Auto reload register is not preloaded // and there is no need to use any compare, as in the timer mode used, setting ARR to the compare value // will result in exactly the same effect, ie triggering an interrupt, and on top, set counter to 0 timer_set_reload(STEP_TIMER_DEV, compare); // We reload direct ARR as needed during counting up break; - case TEMP_TIMER_NUM: + case MF_TIMER_TEMP: timer_set_compare(TEMP_TIMER_DEV, TEMP_TIMER_CHAN, compare); break; } @@ -177,18 +177,18 @@ FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const ha FORCE_INLINE static void HAL_timer_isr_prologue(const uint8_t timer_num) { switch (timer_num) { - case STEP_TIMER_NUM: - // No counter to clear - timer_generate_update(STEP_TIMER_DEV); - return; - case TEMP_TIMER_NUM: - timer_set_count(TEMP_TIMER_DEV, 0); - timer_generate_update(TEMP_TIMER_DEV); - return; + case MF_TIMER_STEP: + // No counter to clear + timer_generate_update(STEP_TIMER_DEV); + return; + case MF_TIMER_TEMP: + timer_set_count(TEMP_TIMER_DEV, 0); + timer_generate_update(TEMP_TIMER_DEV); + return; } } -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP // No command is available in framework to turn off ARPE bit, which is turned on by default in libmaple. // Needed here to reset ARPE=0 for stepper timer @@ -196,6 +196,6 @@ FORCE_INLINE static void timer_no_ARR_preload_ARPE(timer_dev *dev) { bb_peri_set_bit(&(dev->regs).gen->CR1, TIMER_CR1_ARPE_BIT, 0); } -void timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority); +void HAL_timer_set_interrupt_priority(uint_fast8_t timer_num, uint_fast8_t priority); #define TIMER_OC_NO_PRELOAD 0 // Need to disable preload also on compare registers. diff --git a/Marlin/src/HAL/STM32F1/watchdog.cpp b/Marlin/src/HAL/STM32F1/watchdog.cpp deleted file mode 100644 index b812a4fa6403a..0000000000000 --- a/Marlin/src/HAL/STM32F1/watchdog.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 . - * - */ - -/** - * HAL for stm32duino.com based on Libmaple and compatible (STM32F1) - */ - -#ifdef __STM32F1__ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include -#include "watchdog.h" - -/** - * The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0). - */ -#define STM32F1_WD_RELOAD TERN(WATCHDOG_DURATION_8S, 1250, 625) // 4 or 8 second timeout - -void HAL_watchdog_refresh() { - #if DISABLED(PINS_DEBUGGING) && PIN_EXISTS(LED) - TOGGLE(LED_PIN); // heartbeat indicator - #endif - iwdg_feed(); -} - -void watchdogSetup() { - // do whatever. don't remove this function. -} - -/** - * @brief Initialized the independent hardware watchdog. - * - * @return No return - * - * @details The watchdog clock is 40Khz. So for a 4s or 8s interval use a /256 preescaler and 625 or 1250 reload value (counts down to 0). - */ -void watchdog_init() { - #if DISABLED(DISABLE_WATCHDOG_INIT) - iwdg_init(IWDG_PRE_256, STM32F1_WD_RELOAD); - #endif -} - -#endif // USE_WATCHDOG -#endif // __STM32F1__ diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.cpp b/Marlin/src/HAL/TEENSY31_32/HAL.cpp index f08cf799e9e8b..2892368967e46 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.cpp +++ b/Marlin/src/HAL/TEENSY31_32/HAL.cpp @@ -31,6 +31,10 @@ #include +// ------------------------ +// Serial ports +// ------------------------ + #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) #if WITHIN(SERIAL_PORT, 0, 3) @@ -40,33 +44,13 @@ #endif USBSerialType USBSerial(false, SerialUSB); -uint16_t HAL_adc_result; - -static const uint8_t pin2sc1a[] = { - 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 0, 19, 3, 31, // 0-13, we treat them as A0-A13 - 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 (A0-A9) - 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, // 24-33 - 0+64, 19+64, 3+64, 31+64, // 34-37 (A10-A13) - 26, 22, 23, 27, 29, 30 // 38-43: temp. sensor, VREF_OUT, A14, bandgap, VREFH, VREFL. A14 isn't connected to anything in Teensy 3.0. -}; - -/* - // disable interrupts - void cli() { noInterrupts(); } - - // enable interrupts - void sei() { interrupts(); } -*/ +// ------------------------ +// MarlinHAL Class +// ------------------------ -void HAL_adc_init() { - analog_init(); - while (ADC0_SC3 & ADC_SC3_CAL) {}; // Wait for calibration to finish - NVIC_ENABLE_IRQ(IRQ_FTM1); -} +void MarlinHAL::reboot() { _reboot_Teensyduino_(); } -void HAL_clear_reset_source() { } - -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { switch (RCM_SRS0) { case 128: return RST_POWER_ON; break; case 64: return RST_EXTERNAL; break; @@ -78,7 +62,54 @@ uint8_t HAL_get_reset_source() { return 0; } -void HAL_reboot() { _reboot_Teensyduino_(); } +// ------------------------ +// Watchdog Timer +// ------------------------ + +#if ENABLED(USE_WATCHDOG) + + #define WDT_TIMEOUT_MS TERN(WATCHDOG_DURATION_8S, 8000, 4000) // 4 or 8 second timeout + + void MarlinHAL::watchdog_init() { + WDOG_TOVALH = 0; + WDOG_TOVALL = WDT_TIMEOUT_MS; + WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN; + } + + void MarlinHAL::watchdog_refresh() { + // Watchdog refresh sequence + WDOG_REFRESH = 0xA602; + WDOG_REFRESH = 0xB480; + } + +#endif + +// ------------------------ +// ADC +// ------------------------ + +void MarlinHAL::adc_init() { + analog_init(); + while (ADC0_SC3 & ADC_SC3_CAL) {}; // Wait for calibration to finish + NVIC_ENABLE_IRQ(IRQ_FTM1); +} + +void MarlinHAL::adc_start(const pin_t pin) { + static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 0, 19, 3, 31, // 0-13, we treat them as A0-A13 + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 (A0-A9) + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, // 24-33 + 0+64, 19+64, 3+64, 31+64, // 34-37 (A10-A13) + 26, 22, 23, 27, 29, 30 // 38-43: temp. sensor, VREF_OUT, A14, bandgap, VREFH, VREFL. A14 isn't connected to anything in Teensy 3.0. + }; + ADC0_SC1A = pin2sc1a[pin]; +} + +uint16_t MarlinHAL::adc_value() { return ADC0_RA; } + +// ------------------------ +// Free Memory Accessor +// ------------------------ extern "C" { extern char __bss_end; @@ -95,8 +126,4 @@ extern "C" { } } -void HAL_adc_start_conversion(const uint8_t adc_pin) { ADC0_SC1A = pin2sc1a[adc_pin]; } - -uint16_t HAL_adc_get_result() { return ADC0_RA; } - #endif // __MK20DX256__ diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.h b/Marlin/src/HAL/TEENSY31_32/HAL.h index aa195845fb17f..a7aa9f0da2115 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.h +++ b/Marlin/src/HAL/TEENSY31_32/HAL.h @@ -32,16 +32,12 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 - -//#undef MOTHERBOARD -//#define MOTHERBOARD BOARD_TEENSY31_32 +// ------------------------ +// Defines +// ------------------------ #define IS_32BIT_TEENSY 1 #define IS_TEENSY_31_32 1 @@ -49,6 +45,14 @@ #define IS_TEENSY32 1 #endif +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 + +// ------------------------ +// Serial ports +// ------------------------ + #include "../../core/serial_hook.h" #define Serial0 Serial @@ -72,58 +76,115 @@ extern USBSerialType USBSerial; #error "The required SERIAL_PORT must be from 0 to 3, or -1 for Native USB." #endif -#define HAL_SERVO_LIB libServo +// ------------------------ +// Types +// ------------------------ + +class libServo; +typedef libServo hal_servo_t; typedef int8_t pin_t; -#ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) -#endif +// ------------------------ +// Interrupts +// ------------------------ -#define CRITICAL_SECTION_START() uint32_t primask = __get_PRIMASK(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_PRIMASK()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() +uint32_t __get_PRIMASK(void); // CMSIS +#define CRITICAL_SECTION_START() const bool irqon = !__get_PRIMASK(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() -inline void HAL_init() {} +// ------------------------ +// ADC +// ------------------------ -// Clear the reset reason -void HAL_clear_reset_source(); +#ifndef analogInputToDigitalPin + #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) +#endif -// Get the reason for the reset -uint8_t HAL_get_reset_source(); +#define HAL_ADC_VREF 3.3 +#define HAL_ADC_RESOLUTION 10 -void HAL_reboot(); +// +// 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) -FORCE_INLINE void _delay_ms(const int delay_ms) { delay(delay_ms); } +// ------------------------ +// Class Utilities +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif extern "C" int freeMemory(); -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop -// ADC +// ------------------------ +// MarlinHAL Class +// ------------------------ -void HAL_adc_init(); +class MarlinHAL { +public: -#define HAL_ADC_VREF 3.3 -#define HAL_ADC_RESOLUTION 10 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() true + // Earliest possible init, before setup() + MarlinHAL() {} -#define HAL_ANALOG_SELECT(pin) + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -void HAL_adc_start_conversion(const uint8_t adc_pin); -uint16_t HAL_adc_get_result(); + static void init() {} // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + // 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() {} + + // 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 ch) {} + + // Begin ADC sampling on the given channel. Called from Temperature::isr! + static void adc_start(const pin_t ch); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * 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] + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { + analogWrite(pin, v); + } + +}; diff --git a/Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h b/Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h index 1efa76b1e9dfc..dbce187673c9e 100644 --- a/Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h +++ b/Marlin/src/HAL/TEENSY31_32/inc/SanityCheck.h @@ -40,3 +40,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.1/3.2." #endif + +#if USING_PULLDOWNS + #error "PULLDOWN pin mode is not available on Teensy 3.1/3.2 boards." +#endif diff --git a/Marlin/src/HAL/TEENSY31_32/timers.cpp b/Marlin/src/HAL/TEENSY31_32/timers.cpp index 7e01a38f89468..f217715a3fed0 100644 --- a/Marlin/src/HAL/TEENSY31_32/timers.cpp +++ b/Marlin/src/HAL/TEENSY31_32/timers.cpp @@ -47,7 +47,7 @@ FORCE_INLINE static void __DSB() { void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: FTM0_MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; FTM0_SC = 0x00; // Set this to zero before changing the modulus FTM0_CNT = 0x0000; // Reset the count to zero @@ -56,7 +56,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { FTM0_SC = (FTM_SC_CLKS(0b1) & FTM_SC_CLKS_MASK) | (FTM_SC_PS(FTM0_TIMER_PRESCALE_BITS) & FTM_SC_PS_MASK); // Bus clock 60MHz divided by prescaler 8 FTM0_C0SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSA; break; - case 1: + case MF_TIMER_TEMP: FTM1_MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; // Disable write protection, Enable FTM1 FTM1_SC = 0x00; // Set this to zero before changing the modulus FTM1_CNT = 0x0000; // Reset the count to zero @@ -70,15 +70,15 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_enable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_ENABLE_IRQ(IRQ_FTM0); break; - case 1: NVIC_ENABLE_IRQ(IRQ_FTM1); break; + case MF_TIMER_STEP: NVIC_ENABLE_IRQ(IRQ_FTM0); break; + case MF_TIMER_TEMP: NVIC_ENABLE_IRQ(IRQ_FTM1); break; } } void HAL_timer_disable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_DISABLE_IRQ(IRQ_FTM0); break; - case 1: NVIC_DISABLE_IRQ(IRQ_FTM1); break; + case MF_TIMER_STEP: NVIC_DISABLE_IRQ(IRQ_FTM0); break; + case MF_TIMER_TEMP: NVIC_DISABLE_IRQ(IRQ_FTM1); break; } // We NEED memory barriers to ensure Interrupts are actually disabled! @@ -89,20 +89,20 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num) { bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { switch (timer_num) { - case 0: return NVIC_IS_ENABLED(IRQ_FTM0); - case 1: return NVIC_IS_ENABLED(IRQ_FTM1); + case MF_TIMER_STEP: return NVIC_IS_ENABLED(IRQ_FTM0); + case MF_TIMER_TEMP: return NVIC_IS_ENABLED(IRQ_FTM1); } return false; } void HAL_timer_isr_prologue(const uint8_t timer_num) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: FTM0_CNT = 0x0000; FTM0_SC &= ~FTM_SC_TOF; // Clear FTM Overflow flag FTM0_C0SC &= ~FTM_CSC_CHF; // Clear FTM Channel Compare flag break; - case 1: + case MF_TIMER_TEMP: FTM1_CNT = 0x0000; FTM1_SC &= ~FTM_SC_TOF; // Clear FTM Overflow flag FTM1_C0SC &= ~FTM_CSC_CHF; // Clear FTM Channel Compare flag diff --git a/Marlin/src/HAL/TEENSY31_32/timers.h b/Marlin/src/HAL/TEENSY31_32/timers.h index 61b86735967e9..9fcbb6f232c93 100644 --- a/Marlin/src/HAL/TEENSY31_32/timers.h +++ b/Marlin/src/HAL/TEENSY31_32/timers.h @@ -46,14 +46,14 @@ typedef uint32_t hal_timer_t; #define HAL_TIMER_RATE (FTM0_TIMER_RATE) -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif #define TEMP_TIMER_FREQUENCY 1000 @@ -66,12 +66,12 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() extern "C" void ftm0_isr() //void TC3_Handler() @@ -84,23 +84,23 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { switch (timer_num) { - case 0: FTM0_C0V = compare; break; - case 1: FTM1_C0V = compare; break; + case MF_TIMER_STEP: FTM0_C0V = compare; break; + case MF_TIMER_TEMP: FTM1_C0V = compare; break; } } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { switch (timer_num) { - case 0: return FTM0_C0V; - case 1: return FTM1_C0V; + case MF_TIMER_STEP: return FTM0_C0V; + case MF_TIMER_TEMP: return FTM1_C0V; } return 0; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { switch (timer_num) { - case 0: return FTM0_CNT; - case 1: return FTM1_CNT; + case MF_TIMER_STEP: return FTM0_CNT; + case MF_TIMER_TEMP: return FTM1_CNT; } return 0; } @@ -110,4 +110,4 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); void HAL_timer_isr_prologue(const uint8_t timer_num); -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/TEENSY31_32/watchdog.cpp b/Marlin/src/HAL/TEENSY31_32/watchdog.cpp deleted file mode 100644 index 5e21236129dba..0000000000000 --- a/Marlin/src/HAL/TEENSY31_32/watchdog.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 __MK20DX256__ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#define WDT_TIMEOUT_MS TERN(WATCHDOG_DURATION_8S, 8000, 4000) // 4 or 8 second timeout - -void watchdog_init() { - WDOG_TOVALH = 0; - WDOG_TOVALL = WDT_TIMEOUT_MS; - WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN; -} - -#endif // USE_WATCHDOG - -#endif // __MK20DX256__ diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.cpp b/Marlin/src/HAL/TEENSY35_36/HAL.cpp index 046c00b56ed53..bc02ac1c4524d 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.cpp +++ b/Marlin/src/HAL/TEENSY35_36/HAL.cpp @@ -31,6 +31,10 @@ #include +// ------------------------ +// Serial ports +// ------------------------ + #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) #if WITHIN(SERIAL_PORT, 0, 3) @@ -39,42 +43,13 @@ USBSerialType USBSerial(false, SerialUSB); -uint16_t HAL_adc_result, HAL_adc_select; - -static const uint8_t pin2sc1a[] = { - 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 3, 19+128, 14+128, 15+128, // 0-13 -> A0-A13 - 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 are A0-A9 - 255, 255, 255, 255, 255, 255, 255, // 24-30 are digital only - 14+128, 15+128, 17, 18, 4+128, 5+128, 6+128, 7+128, 17+128, // 31-39 are A12-A20 - 255, 255, 255, 255, 255, 255, 255, 255, 255, // 40-48 are digital only - 10+128, 11+128, // 49-50 are A23-A24 - 255, 255, 255, 255, 255, 255, 255, // 51-57 are digital only - 255, 255, 255, 255, 255, 255, // 58-63 (sd card pins) are digital only - 3, 19+128, // 64-65 are A10-A11 - 23, 23+128,// 66-67 are A21-A22 (DAC pins) - 1, 1+128, // 68-69 are A25-A26 (unused USB host port on Teensy 3.5) - 26, // 70 is Temperature Sensor - 18+128 // 71 is Vref -}; - -/* - // disable interrupts - void cli() { noInterrupts(); } - - // enable interrupts - void sei() { interrupts(); } -*/ - -void HAL_adc_init() { - analog_init(); - while (ADC0_SC3 & ADC_SC3_CAL) {}; // Wait for calibration to finish - while (ADC1_SC3 & ADC_SC3_CAL) {}; // Wait for calibration to finish - NVIC_ENABLE_IRQ(IRQ_FTM1); -} +// ------------------------ +// MarlinHAL Class +// ------------------------ -void HAL_clear_reset_source() { } +void MarlinHAL::reboot() { _reboot_Teensyduino_(); } -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { switch (RCM_SRS0) { case 128: return RST_POWER_ON; break; case 64: return RST_EXTERNAL; break; @@ -86,45 +61,96 @@ uint8_t HAL_get_reset_source() { return 0; } -void HAL_reboot() { _reboot_Teensyduino_(); } +// ------------------------ +// Watchdog Timer +// ------------------------ -extern "C" { - extern char __bss_end; - extern char __heap_start; - extern void* __brkval; +#if ENABLED(USE_WATCHDOG) - int freeMemory() { - int free_memory; - if ((int)__brkval == 0) - free_memory = ((int)&free_memory) - ((int)&__bss_end); - else - free_memory = ((int)&free_memory) - ((int)__brkval); - return free_memory; + #define WDT_TIMEOUT_MS TERN(WATCHDOG_DURATION_8S, 8000, 4000) // 4 or 8 second timeout + + void MarlinHAL::watchdog_init() { + WDOG_TOVALH = 0; + WDOG_TOVALL = WDT_TIMEOUT_MS; + WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN; } + + void MarlinHAL::watchdog_refresh() { + // Watchdog refresh sequence + WDOG_REFRESH = 0xA602; + WDOG_REFRESH = 0xB480; + } + +#endif + +// ------------------------ +// ADC +// ------------------------ + +int8_t MarlinHAL::adc_select; + +void MarlinHAL::adc_init() { + analog_init(); + while (ADC0_SC3 & ADC_SC3_CAL) { /* Wait for calibration to finish */ } + while (ADC1_SC3 & ADC_SC3_CAL) { /* Wait for calibration to finish */ } + NVIC_ENABLE_IRQ(IRQ_FTM1); } -void HAL_adc_start_conversion(const uint8_t adc_pin) { +void MarlinHAL::adc_start(const pin_t adc_pin) { + static const uint8_t pin2sc1a[] = { + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, 3, 19+128, 14+128, 15+128, // 0-13 -> A0-A13 + 5, 14, 8, 9, 13, 12, 6, 7, 15, 4, // 14-23 are A0-A9 + 255, 255, 255, 255, 255, 255, 255, // 24-30 are digital only + 14+128, 15+128, 17, 18, 4+128, 5+128, 6+128, 7+128, 17+128, // 31-39 are A12-A20 + 255, 255, 255, 255, 255, 255, 255, 255, 255, // 40-48 are digital only + 10+128, 11+128, // 49-50 are A23-A24 + 255, 255, 255, 255, 255, 255, 255, // 51-57 are digital only + 255, 255, 255, 255, 255, 255, // 58-63 (sd card pins) are digital only + 3, 19+128, // 64-65 are A10-A11 + 23, 23+128,// 66-67 are A21-A22 (DAC pins) + 1, 1+128, // 68-69 are A25-A26 (unused USB host port on Teensy 3.5) + 26, // 70 is Temperature Sensor + 18+128 // 71 is Vref + }; const uint16_t pin = pin2sc1a[adc_pin]; if (pin == 0xFF) { - // Digital only - HAL_adc_select = -1; + adc_select = -1; // Digital only } else if (pin & 0x80) { - HAL_adc_select = 1; + adc_select = 1; ADC1_SC1A = pin & 0x7F; } else { - HAL_adc_select = 0; + adc_select = 0; ADC0_SC1A = pin; } } -uint16_t HAL_adc_get_result() { - switch (HAL_adc_select) { +uint16_t MarlinHAL::adc_value() { + switch (adc_select) { case 0: return ADC0_RA; case 1: return ADC1_RA; } return 0; } +// ------------------------ +// Free Memory Accessor +// ------------------------ + +extern "C" { + extern char __bss_end; + extern char __heap_start; + extern void* __brkval; + + int freeMemory() { + int free_memory; + if ((int)__brkval == 0) + free_memory = ((int)&free_memory) - ((int)&__bss_end); + else + free_memory = ((int)&free_memory) - ((int)__brkval); + return free_memory; + } +} + #endif // __MK64FX512__ || __MK66FX1M0__ diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.h b/Marlin/src/HAL/TEENSY35_36/HAL.h index 0093294a2aa5e..2a192e47189dd 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.h +++ b/Marlin/src/HAL/TEENSY35_36/HAL.h @@ -32,15 +32,10 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include #include -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 - // ------------------------ // Defines // ------------------------ @@ -53,6 +48,17 @@ #define IS_TEENSY35 1 #endif +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 + +#undef sq +#define sq(x) ((x)*(x)) + +// ------------------------ +// Serial ports +// ------------------------ + #include "../../core/serial_hook.h" #define Serial0 Serial @@ -76,61 +82,116 @@ extern USBSerialType USBSerial; #error "SERIAL_PORT must be from 0 to 3, or -1 for Native USB." #endif -#define HAL_SERVO_LIB libServo +// ------------------------ +// Types +// ------------------------ + +class libServo; +typedef libServo hal_servo_t; typedef int8_t pin_t; -#ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) -#endif +// ------------------------ +// Interrupts +// ------------------------ -#define CRITICAL_SECTION_START() uint32_t primask = __get_primask(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_primask()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() +#define CRITICAL_SECTION_START() const bool irqon = !__get_primask(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() -#undef sq -#define sq(x) ((x)*(x)) - -inline void HAL_init() {} +// ------------------------ +// ADC +// ------------------------ -// Clear reset reason -void HAL_clear_reset_source(); +#ifndef analogInputToDigitalPin + #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) +#endif -// Reset reason -uint8_t HAL_get_reset_source(); +#define HAL_ADC_VREF 3.3 +#define HAL_ADC_RESOLUTION 10 -void HAL_reboot(); +// +// 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) -FORCE_INLINE void _delay_ms(const int delay_ms) { delay(delay_ms); } +// ------------------------ +// Free Memory Accessor +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif extern "C" int freeMemory(); -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop -// ADC +// ------------------------ +// MarlinHAL Class +// ------------------------ -void HAL_adc_init(); +class MarlinHAL { +public: -#define HAL_ADC_VREF 3.3 -#define HAL_ADC_RESOLUTION 10 -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() true + // Earliest possible init, before setup() + MarlinHAL() {} -#define HAL_ANALOG_SELECT(pin) + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -void HAL_adc_start_conversion(const uint8_t adc_pin); -uint16_t HAL_adc_get_result(); + static void init() {} // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + // Interrupts + static bool isr_state() { return true; } + 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() {} + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source() {} + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static int8_t adc_select; + + // 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) {} + + // Begin ADC sampling on the given pin. Called from Temperature::isr! + static void adc_start(const pin_t pin); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * 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] + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { + analogWrite(pin, v); + } + +}; diff --git a/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h b/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h index eef2850550eb9..3308707371844 100644 --- a/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h +++ b/Marlin/src/HAL/TEENSY35_36/inc/SanityCheck.h @@ -40,3 +40,7 @@ #if ENABLED(POSTMORTEM_DEBUGGING) #error "POSTMORTEM_DEBUGGING is not yet supported on Teensy 3.5/3.6." #endif + +#if USING_PULLDOWNS + #error "PULLDOWN pin mode is not available on Teensy 3.5/3.6 boards." +#endif diff --git a/Marlin/src/HAL/TEENSY35_36/timers.cpp b/Marlin/src/HAL/TEENSY35_36/timers.cpp index 8067d091dd01b..39095fbd77af5 100644 --- a/Marlin/src/HAL/TEENSY35_36/timers.cpp +++ b/Marlin/src/HAL/TEENSY35_36/timers.cpp @@ -47,7 +47,7 @@ FORCE_INLINE static void __DSB() { void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: FTM0_MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; FTM0_SC = 0x00; // Set this to zero before changing the modulus FTM0_CNT = 0x0000; // Reset the count to zero @@ -56,7 +56,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { FTM0_SC = (FTM_SC_CLKS(0b1) & FTM_SC_CLKS_MASK) | (FTM_SC_PS(FTM0_TIMER_PRESCALE_BITS) & FTM_SC_PS_MASK); // Bus clock 60MHz divided by prescaler 8 FTM0_C0SC = FTM_CSC_CHIE | FTM_CSC_MSA | FTM_CSC_ELSA; break; - case 1: + case MF_TIMER_TEMP: FTM1_MODE = FTM_MODE_WPDIS | FTM_MODE_FTMEN; // Disable write protection, Enable FTM1 FTM1_SC = 0x00; // Set this to zero before changing the modulus FTM1_CNT = 0x0000; // Reset the count to zero @@ -70,15 +70,15 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_enable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_ENABLE_IRQ(IRQ_FTM0); break; - case 1: NVIC_ENABLE_IRQ(IRQ_FTM1); break; + case MF_TIMER_STEP: NVIC_ENABLE_IRQ(IRQ_FTM0); break; + case MF_TIMER_TEMP: NVIC_ENABLE_IRQ(IRQ_FTM1); break; } } void HAL_timer_disable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_DISABLE_IRQ(IRQ_FTM0); break; - case 1: NVIC_DISABLE_IRQ(IRQ_FTM1); break; + case MF_TIMER_STEP: NVIC_DISABLE_IRQ(IRQ_FTM0); break; + case MF_TIMER_TEMP: NVIC_DISABLE_IRQ(IRQ_FTM1); break; } // We NEED memory barriers to ensure Interrupts are actually disabled! @@ -89,20 +89,20 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num) { bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { switch (timer_num) { - case 0: return NVIC_IS_ENABLED(IRQ_FTM0); - case 1: return NVIC_IS_ENABLED(IRQ_FTM1); + case MF_TIMER_STEP: return NVIC_IS_ENABLED(IRQ_FTM0); + case MF_TIMER_TEMP: return NVIC_IS_ENABLED(IRQ_FTM1); } return false; } void HAL_timer_isr_prologue(const uint8_t timer_num) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: FTM0_CNT = 0x0000; FTM0_SC &= ~FTM_SC_TOF; // Clear FTM Overflow flag FTM0_C0SC &= ~FTM_CSC_CHF; // Clear FTM Channel Compare flag break; - case 1: + case MF_TIMER_TEMP: FTM1_CNT = 0x0000; FTM1_SC &= ~FTM_SC_TOF; // Clear FTM Overflow flag FTM1_C0SC &= ~FTM_CSC_CHF; // Clear FTM Channel Compare flag diff --git a/Marlin/src/HAL/TEENSY35_36/timers.h b/Marlin/src/HAL/TEENSY35_36/timers.h index 99269ac6571fa..8af79d73928e5 100644 --- a/Marlin/src/HAL/TEENSY35_36/timers.h +++ b/Marlin/src/HAL/TEENSY35_36/timers.h @@ -45,14 +45,14 @@ typedef uint32_t hal_timer_t; #define HAL_TIMER_RATE (FTM0_TIMER_RATE) -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif #define TEMP_TIMER_FREQUENCY 1000 @@ -65,12 +65,12 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() extern "C" void ftm0_isr() //void TC3_Handler() @@ -83,23 +83,23 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { switch (timer_num) { - case 0: FTM0_C0V = compare; break; - case 1: FTM1_C0V = compare; break; + case MF_TIMER_STEP: FTM0_C0V = compare; break; + case MF_TIMER_TEMP: FTM1_C0V = compare; break; } } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { switch (timer_num) { - case 0: return FTM0_C0V; - case 1: return FTM1_C0V; + case MF_TIMER_STEP: return FTM0_C0V; + case MF_TIMER_TEMP: return FTM1_C0V; } return 0; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { switch (timer_num) { - case 0: return FTM0_CNT; - case 1: return FTM1_CNT; + case MF_TIMER_STEP: return FTM0_CNT; + case MF_TIMER_TEMP: return FTM1_CNT; } return 0; } @@ -109,4 +109,4 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num); bool HAL_timer_interrupt_enabled(const uint8_t timer_num); void HAL_timer_isr_prologue(const uint8_t timer_num); -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/TEENSY35_36/watchdog.cpp b/Marlin/src/HAL/TEENSY35_36/watchdog.cpp deleted file mode 100644 index 3825e2792869a..0000000000000 --- a/Marlin/src/HAL/TEENSY35_36/watchdog.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 . - * - */ -#if defined(__MK64FX512__) || defined(__MK66FX1M0__) - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#define WDT_TIMEOUT_MS TERN(WATCHDOG_DURATION_8S, 8000, 4000) // 4 or 8 second timeout - -void watchdog_init() { - WDOG_TOVALH = 0; - WDOG_TOVALL = WDT_TIMEOUT_MS; - WDOG_STCTRLH = WDOG_STCTRLH_WDOGEN; -} - -#endif // USE_WATCHDOG - -#endif // __MK64FX512__ || __MK66FX1M0__ diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.cpp b/Marlin/src/HAL/TEENSY40_41/HAL.cpp index ccc8c2659c65f..1d02ab8575c3b 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.cpp +++ b/Marlin/src/HAL/TEENSY40_41/HAL.cpp @@ -33,6 +33,10 @@ #include "timers.h" #include +// ------------------------ +// Serial ports +// ------------------------ + #define _IMPLEMENT_SERIAL(X) DefaultSerial##X MSerial##X(false, Serial##X) #define IMPLEMENT_SERIAL(X) _IMPLEMENT_SERIAL(X) #if WITHIN(SERIAL_PORT, 0, 3) @@ -40,138 +44,167 @@ #endif USBSerialType USBSerial(false, SerialUSB); -uint16_t HAL_adc_result, HAL_adc_select; - -static const uint8_t pin2sc1a[] = { - 0x07, // 0/A0 AD_B1_02 - 0x08, // 1/A1 AD_B1_03 - 0x0C, // 2/A2 AD_B1_07 - 0x0B, // 3/A3 AD_B1_06 - 0x06, // 4/A4 AD_B1_01 - 0x05, // 5/A5 AD_B1_00 - 0x0F, // 6/A6 AD_B1_10 - 0x00, // 7/A7 AD_B1_11 - 0x0D, // 8/A8 AD_B1_08 - 0x0E, // 9/A9 AD_B1_09 - 0x01, // 24/A10 AD_B0_12 - 0x02, // 25/A11 AD_B0_13 - 0x83, // 26/A12 AD_B1_14 - only on ADC2, 3 - 0x84, // 27/A13 AD_B1_15 - only on ADC2, 4 - 0x07, // 14/A0 AD_B1_02 - 0x08, // 15/A1 AD_B1_03 - 0x0C, // 16/A2 AD_B1_07 - 0x0B, // 17/A3 AD_B1_06 - 0x06, // 18/A4 AD_B1_01 - 0x05, // 19/A5 AD_B1_00 - 0x0F, // 20/A6 AD_B1_10 - 0x00, // 21/A7 AD_B1_11 - 0x0D, // 22/A8 AD_B1_08 - 0x0E, // 23/A9 AD_B1_09 - 0x01, // 24/A10 AD_B0_12 - 0x02, // 25/A11 AD_B0_13 - 0x83, // 26/A12 AD_B1_14 - only on ADC2, 3 - 0x84, // 27/A13 AD_B1_15 - only on ADC2, 4 - #ifdef ARDUINO_TEENSY41 - 0xFF, // 28 - 0xFF, // 29 - 0xFF, // 30 - 0xFF, // 31 - 0xFF, // 32 - 0xFF, // 33 - 0xFF, // 34 - 0xFF, // 35 - 0xFF, // 36 - 0xFF, // 37 - 0x81, // 38/A14 AD_B1_12 - only on ADC2, 1 - 0x82, // 39/A15 AD_B1_13 - only on ADC2, 2 - 0x09, // 40/A16 AD_B1_04 - 0x0A, // 41/A17 AD_B1_05 - #endif -}; - -/* -// disable interrupts -void cli() { noInterrupts(); } - -// enable interrupts -void sei() { interrupts(); } -*/ - -void HAL_adc_init() { - analog_init(); - while (ADC1_GC & ADC_GC_CAL) ; - while (ADC2_GC & ADC_GC_CAL) ; +// ------------------------ +// FastIO +// ------------------------ + +bool is_output(pin_t pin) { + const struct digital_pin_bitband_and_config_table_struct *p; + p = digital_pin_to_info_PGM + pin; + return (*(p->reg + 1) & p->mask); } -void HAL_clear_reset_source() { - uint32_t reset_source = SRC_SRSR; - SRC_SRSR = reset_source; - } +// ------------------------ +// MarlinHAL Class +// ------------------------ + +void MarlinHAL::reboot() { _reboot_Teensyduino_(); } -uint8_t HAL_get_reset_source() { +uint8_t MarlinHAL::get_reset_source() { switch (SRC_SRSR & 0xFF) { case 1: return RST_POWER_ON; break; case 2: return RST_SOFTWARE; break; case 4: return RST_EXTERNAL; break; - // case 8: return RST_BROWN_OUT; break; + //case 8: return RST_BROWN_OUT; break; case 16: return RST_WATCHDOG; break; - case 64: return RST_JTAG; break; - // case 128: return RST_OVERTEMP; break; + case 64: return RST_JTAG; break; + //case 128: return RST_OVERTEMP; break; } return 0; } -void HAL_reboot() { _reboot_Teensyduino_(); } +void MarlinHAL::clear_reset_source() { + uint32_t reset_source = SRC_SRSR; + SRC_SRSR = reset_source; +} + +// ------------------------ +// Watchdog Timer +// ------------------------ -#define __bss_end _ebss +#if ENABLED(USE_WATCHDOG) -extern "C" { - extern char __bss_end; - extern char __heap_start; - extern void* __brkval; + #define WDT_TIMEOUT TERN(WATCHDOG_DURATION_8S, 8, 4) // 4 or 8 second timeout - // Doesn't work on Teensy 4.x - uint32_t freeMemory() { - uint32_t free_memory; - if ((uint32_t)__brkval == 0) - free_memory = ((uint32_t)&free_memory) - ((uint32_t)&__bss_end); - else - free_memory = ((uint32_t)&free_memory) - ((uint32_t)__brkval); - return free_memory; + constexpr uint8_t timeoutval = (WDT_TIMEOUT - 0.5f) / 0.5f; + + void MarlinHAL::watchdog_init() { + CCM_CCGR3 |= CCM_CCGR3_WDOG1(3); // enable WDOG1 clocks + WDOG1_WMCR = 0; // disable power down PDE + WDOG1_WCR |= WDOG_WCR_SRS | WDOG_WCR_WT(timeoutval); + WDOG1_WCR |= WDOG_WCR_WDE | WDOG_WCR_WDT | WDOG_WCR_SRE; } + + void MarlinHAL::watchdog_refresh() { + // Watchdog refresh sequence + WDOG1_WSR = 0x5555; + WDOG1_WSR = 0xAAAA; + } + +#endif + +// ------------------------ +// ADC +// ------------------------ + +int8_t MarlinHAL::adc_select; + +void MarlinHAL::adc_init() { + analog_init(); + while (ADC1_GC & ADC_GC_CAL) { /* wait */ } + while (ADC2_GC & ADC_GC_CAL) { /* wait */ } } -void HAL_adc_start_conversion(const uint8_t adc_pin) { +void MarlinHAL::adc_start(const pin_t adc_pin) { + static const uint8_t pin2sc1a[] = { + 0x07, // 0/A0 AD_B1_02 + 0x08, // 1/A1 AD_B1_03 + 0x0C, // 2/A2 AD_B1_07 + 0x0B, // 3/A3 AD_B1_06 + 0x06, // 4/A4 AD_B1_01 + 0x05, // 5/A5 AD_B1_00 + 0x0F, // 6/A6 AD_B1_10 + 0x00, // 7/A7 AD_B1_11 + 0x0D, // 8/A8 AD_B1_08 + 0x0E, // 9/A9 AD_B1_09 + 0x01, // 24/A10 AD_B0_12 + 0x02, // 25/A11 AD_B0_13 + 0x83, // 26/A12 AD_B1_14 - only on ADC2, 3 + 0x84, // 27/A13 AD_B1_15 - only on ADC2, 4 + 0x07, // 14/A0 AD_B1_02 + 0x08, // 15/A1 AD_B1_03 + 0x0C, // 16/A2 AD_B1_07 + 0x0B, // 17/A3 AD_B1_06 + 0x06, // 18/A4 AD_B1_01 + 0x05, // 19/A5 AD_B1_00 + 0x0F, // 20/A6 AD_B1_10 + 0x00, // 21/A7 AD_B1_11 + 0x0D, // 22/A8 AD_B1_08 + 0x0E, // 23/A9 AD_B1_09 + 0x01, // 24/A10 AD_B0_12 + 0x02, // 25/A11 AD_B0_13 + 0x83, // 26/A12 AD_B1_14 - only on ADC2, 3 + 0x84, // 27/A13 AD_B1_15 - only on ADC2, 4 + #ifdef ARDUINO_TEENSY41 + 0xFF, // 28 + 0xFF, // 29 + 0xFF, // 30 + 0xFF, // 31 + 0xFF, // 32 + 0xFF, // 33 + 0xFF, // 34 + 0xFF, // 35 + 0xFF, // 36 + 0xFF, // 37 + 0x81, // 38/A14 AD_B1_12 - only on ADC2, 1 + 0x82, // 39/A15 AD_B1_13 - only on ADC2, 2 + 0x09, // 40/A16 AD_B1_04 + 0x0A, // 41/A17 AD_B1_05 + #endif + }; const uint16_t pin = pin2sc1a[adc_pin]; if (pin == 0xFF) { - HAL_adc_select = -1; // Digital only + adc_select = -1; // Digital only } else if (pin & 0x80) { - HAL_adc_select = 1; + adc_select = 1; ADC2_HC0 = pin & 0x7F; } else { - HAL_adc_select = 0; + adc_select = 0; ADC1_HC0 = pin; } } -uint16_t HAL_adc_get_result() { - switch (HAL_adc_select) { +uint16_t MarlinHAL::adc_value() { + switch (adc_select) { case 0: - while (!(ADC1_HS & ADC_HS_COCO0)) ; // wait + while (!(ADC1_HS & ADC_HS_COCO0)) { /* wait */ } return ADC1_R0; case 1: - while (!(ADC2_HS & ADC_HS_COCO0)) ; // wait + while (!(ADC2_HS & ADC_HS_COCO0)) { /* wait */ } return ADC2_R0; } return 0; } -bool is_output(uint8_t pin) { - const struct digital_pin_bitband_and_config_table_struct *p; - p = digital_pin_to_info_PGM + pin; - return (*(p->reg + 1) & p->mask); +// ------------------------ +// Free Memory Accessor +// ------------------------ + +#define __bss_end _ebss + +extern "C" { + extern char __bss_end; + extern char __heap_start; + extern void* __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; + } } #endif // __IMXRT1062__ diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.h b/Marlin/src/HAL/TEENSY40_41/HAL.h index ea51f15ba1b51..c54a2e8a0b64c 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.h +++ b/Marlin/src/HAL/TEENSY40_41/HAL.h @@ -32,7 +32,6 @@ #include "../shared/HAL_SPI.h" #include "fastio.h" -#include "watchdog.h" #include #include @@ -41,10 +40,6 @@ #include "../../feature/ethernet.h" #endif -#define CPU_ST7920_DELAY_1 600 -#define CPU_ST7920_DELAY_2 750 -#define CPU_ST7920_DELAY_3 750 - // ------------------------ // Defines // ------------------------ @@ -55,7 +50,23 @@ #define IS_TEENSY41 1 #endif +#define CPU_ST7920_DELAY_1 600 +#define CPU_ST7920_DELAY_2 750 +#define CPU_ST7920_DELAY_3 750 + +#undef sq +#define sq(x) ((x)*(x)) + +// Don't place string constants in PROGMEM +#undef PSTR +#define PSTR(str) ({static const char *data = (str); &data[0];}) + +// ------------------------ +// Serial ports +// ------------------------ + #include "../../core/serial_hook.h" + #define Serial0 Serial #define _DECLARE_SERIAL(X) \ typedef ForwardSerial1Class DefaultSerial##X; \ @@ -89,71 +100,120 @@ extern USBSerialType USBSerial; #endif #endif -#define HAL_SERVO_LIB libServo +// ------------------------ +// Types +// ------------------------ -typedef int8_t pin_t; +class libServo; +typedef libServo hal_servo_t; -#ifndef analogInputToDigitalPin - #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) -#endif +typedef int8_t pin_t; -#define CRITICAL_SECTION_START() uint32_t primask = __get_primask(); __disable_irq() -#define CRITICAL_SECTION_END() if (!primask) __enable_irq() -#define ISRS_ENABLED() (!__get_primask()) -#define ENABLE_ISRS() __enable_irq() -#define DISABLE_ISRS() __disable_irq() +// ------------------------ +// Interrupts +// ------------------------ -#undef sq -#define sq(x) ((x)*(x)) +#define CRITICAL_SECTION_START() const bool irqon = !__get_primask(); __disable_irq() +#define CRITICAL_SECTION_END() if (irqon) __enable_irq() -// Don't place string constants in PROGMEM -#undef PSTR -#define PSTR(str) ({static const char *data = (str); &data[0];}) +// ------------------------ +// ADC +// ------------------------ -// Enable hooks into idle and setup for HAL -#define HAL_IDLETASK 1 -FORCE_INLINE void HAL_idletask() {} -FORCE_INLINE void HAL_init() {} +#ifndef analogInputToDigitalPin + #define analogInputToDigitalPin(p) ((p < 12U) ? (p) + 54U : -1) +#endif -// Clear reset reason -void HAL_clear_reset_source(); +#define HAL_ADC_VREF 3.3 +#define HAL_ADC_RESOLUTION 10 +#define HAL_ADC_FILTERED // turn off ADC oversampling -// Reset reason -uint8_t HAL_get_reset_source(); +// +// 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) -void HAL_reboot(); +// FastIO +bool is_output(pin_t pin); -FORCE_INLINE void _delay_ms(const int delay_ms) { delay(delay_ms); } +// ------------------------ +// Free Memory Accessor +// ------------------------ +#pragma GCC diagnostic push #if GCC_VERSION <= 50000 - #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-function" #endif extern "C" uint32_t freeMemory(); -#if GCC_VERSION <= 50000 - #pragma GCC diagnostic pop -#endif +#pragma GCC diagnostic pop -// ADC +// ------------------------ +// MarlinHAL Class +// ------------------------ -void HAL_adc_init(); +class MarlinHAL { +public: -#define HAL_ADC_VREF 3.3 -#define HAL_ADC_RESOLUTION 10 -#define HAL_ADC_FILTERED // turn off ADC oversampling -#define HAL_START_ADC(pin) HAL_adc_start_conversion(pin) -#define HAL_READ_ADC() HAL_adc_get_result() -#define HAL_ADC_READY() true + // Earliest possible init, before setup() + MarlinHAL() {} -#define HAL_ANALOG_SELECT(pin) + // Watchdog + static void watchdog_init() IF_DISABLED(USE_WATCHDOG, {}); + static void watchdog_refresh() IF_DISABLED(USE_WATCHDOG, {}); -void HAL_adc_start_conversion(const uint8_t adc_pin); -uint16_t HAL_adc_get_result(); + static void init() {} // Called early in setup() + static void init_board() {} // Called less early in setup() + static void reboot(); // Restart the firmware from 0x0 -#define GET_PIN_MAP_PIN(index) index -#define GET_PIN_MAP_INDEX(pin) pin -#define PARSED_PIN_INDEX(code, dval) parser.intval(code, dval) + // 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() {} + + // Reset + static uint8_t get_reset_source(); + static void clear_reset_source(); + + // Free SRAM + static int freeMemory() { return ::freeMemory(); } + + // + // ADC Methods + // + + static int8_t adc_select; + + // 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); + + // Is the ADC ready for reading? + static bool adc_ready() { return true; } + + // The current value of the ADC register + static uint16_t adc_value(); + + /** + * 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] + */ + static void set_pwm_duty(const pin_t pin, const uint16_t v, const uint16_t=255, const bool=false) { + analogWrite(pin, v); + } -bool is_output(uint8_t pin); +}; diff --git a/Marlin/src/HAL/TEENSY40_41/timers.cpp b/Marlin/src/HAL/TEENSY40_41/timers.cpp index 81c9b08c17a5a..ed99f65d6e269 100644 --- a/Marlin/src/HAL/TEENSY40_41/timers.cpp +++ b/Marlin/src/HAL/TEENSY40_41/timers.cpp @@ -30,7 +30,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { switch (timer_num) { - case 0: + case MF_TIMER_STEP: CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL; // turn off 24mhz mode CCM_CCGR1 |= CCM_CCGR1_GPT1_BUS(CCM_CCGR_ON); @@ -48,7 +48,7 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { attachInterruptVector(IRQ_GPT1, &stepTC_Handler); NVIC_SET_PRIORITY(IRQ_GPT1, 16); break; - case 1: + case MF_TIMER_TEMP: CCM_CSCMR1 &= ~CCM_CSCMR1_PERCLK_CLK_SEL; // turn off 24mhz mode CCM_CCGR0 |= CCM_CCGR0_GPT2_BUS(CCM_CCGR_ON); @@ -71,19 +71,15 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency) { void HAL_timer_enable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: - NVIC_ENABLE_IRQ(IRQ_GPT1); - break; - case 1: - NVIC_ENABLE_IRQ(IRQ_GPT2); - break; + case MF_TIMER_STEP: NVIC_ENABLE_IRQ(IRQ_GPT1); break; + case MF_TIMER_TEMP: NVIC_ENABLE_IRQ(IRQ_GPT2); break; } } void HAL_timer_disable_interrupt(const uint8_t timer_num) { switch (timer_num) { - case 0: NVIC_DISABLE_IRQ(IRQ_GPT1); break; - case 1: NVIC_DISABLE_IRQ(IRQ_GPT2); break; + case MF_TIMER_STEP: NVIC_DISABLE_IRQ(IRQ_GPT1); break; + case MF_TIMER_TEMP: NVIC_DISABLE_IRQ(IRQ_GPT2); break; } // We NEED memory barriers to ensure Interrupts are actually disabled! @@ -93,20 +89,16 @@ void HAL_timer_disable_interrupt(const uint8_t timer_num) { bool HAL_timer_interrupt_enabled(const uint8_t timer_num) { switch (timer_num) { - case 0: return (NVIC_IS_ENABLED(IRQ_GPT1)); - case 1: return (NVIC_IS_ENABLED(IRQ_GPT2)); + case MF_TIMER_STEP: return (NVIC_IS_ENABLED(IRQ_GPT1)); + case MF_TIMER_TEMP: return (NVIC_IS_ENABLED(IRQ_GPT2)); } return false; } void HAL_timer_isr_prologue(const uint8_t timer_num) { switch (timer_num) { - case 0: - GPT1_SR = GPT_IR_OF1IE; // clear OF3 bit - break; - case 1: - GPT2_SR = GPT_IR_OF1IE; // clear OF3 bit - break; + case MF_TIMER_STEP: GPT1_SR = GPT_IR_OF1IE; break; // clear OF3 bit + case MF_TIMER_TEMP: GPT2_SR = GPT_IR_OF1IE; break; // clear OF3 bit } asm volatile("dsb"); } diff --git a/Marlin/src/HAL/TEENSY40_41/timers.h b/Marlin/src/HAL/TEENSY40_41/timers.h index 556333d7f4082..77fe0953d3bd5 100644 --- a/Marlin/src/HAL/TEENSY40_41/timers.h +++ b/Marlin/src/HAL/TEENSY40_41/timers.h @@ -43,14 +43,14 @@ typedef uint32_t hal_timer_t; #define GPT1_TIMER_RATE (GPT_TIMER_RATE / GPT1_TIMER_PRESCALE) // 75MHz #define GPT2_TIMER_RATE (GPT_TIMER_RATE / GPT2_TIMER_PRESCALE) // 15MHz -#ifndef STEP_TIMER_NUM - #define STEP_TIMER_NUM 0 // Timer Index for Stepper +#ifndef MF_TIMER_STEP + #define MF_TIMER_STEP 0 // Timer Index for Stepper #endif -#ifndef PULSE_TIMER_NUM - #define PULSE_TIMER_NUM STEP_TIMER_NUM +#ifndef MF_TIMER_PULSE + #define MF_TIMER_PULSE MF_TIMER_STEP #endif -#ifndef TEMP_TIMER_NUM - #define TEMP_TIMER_NUM 1 // Timer Index for Temperature +#ifndef MF_TIMER_TEMP + #define MF_TIMER_TEMP 1 // Timer Index for Temperature #endif #define TEMP_TIMER_RATE 1000000 @@ -64,12 +64,12 @@ typedef uint32_t hal_timer_t; #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(STEP_TIMER_NUM) -#define DISABLE_STEPPER_DRIVER_INTERRUPT() HAL_timer_disable_interrupt(STEP_TIMER_NUM) -#define STEPPER_ISR_ENABLED() HAL_timer_interrupt_enabled(STEP_TIMER_NUM) +#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(TEMP_TIMER_NUM) -#define DISABLE_TEMPERATURE_INTERRUPT() HAL_timer_disable_interrupt(TEMP_TIMER_NUM) +#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() extern "C" void stepTC_Handler() // GPT1_Handler() @@ -87,27 +87,23 @@ void HAL_timer_start(const uint8_t timer_num, const uint32_t frequency); FORCE_INLINE static void HAL_timer_set_compare(const uint8_t timer_num, const hal_timer_t compare) { switch (timer_num) { - case 0: - GPT1_OCR1 = compare - 1; - break; - case 1: - GPT2_OCR1 = compare - 1; - break; + case MF_TIMER_STEP: GPT1_OCR1 = compare - 1; break; + case MF_TIMER_TEMP: GPT2_OCR1 = compare - 1; break; } } FORCE_INLINE static hal_timer_t HAL_timer_get_compare(const uint8_t timer_num) { switch (timer_num) { - case 0: return GPT1_OCR1; - case 1: return GPT2_OCR1; + case MF_TIMER_STEP: return GPT1_OCR1; + case MF_TIMER_TEMP: return GPT2_OCR1; } return 0; } FORCE_INLINE static hal_timer_t HAL_timer_get_count(const uint8_t timer_num) { switch (timer_num) { - case 0: return GPT1_CNT; - case 1: return GPT2_CNT; + case MF_TIMER_STEP: return GPT1_CNT; + case MF_TIMER_TEMP: return GPT2_CNT; } return 0; } @@ -118,4 +114,4 @@ bool HAL_timer_interrupt_enabled(const uint8_t timer_num); void HAL_timer_isr_prologue(const uint8_t timer_num); //void HAL_timer_isr_epilogue(const uint8_t timer_num) {} -#define HAL_timer_isr_epilogue(TIMER_NUM) +#define HAL_timer_isr_epilogue(T) NOOP diff --git a/Marlin/src/HAL/TEENSY40_41/watchdog.cpp b/Marlin/src/HAL/TEENSY40_41/watchdog.cpp deleted file mode 100644 index dd7c0aa92f094..0000000000000 --- a/Marlin/src/HAL/TEENSY40_41/watchdog.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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__ - -/** - * HAL Watchdog for Teensy 4.0 (IMXRT1062DVL6A) / 4.1 (IMXRT1062DVJ6A) - */ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(USE_WATCHDOG) - -#include "watchdog.h" - -#define WDT_TIMEOUT TERN(WATCHDOG_DURATION_8S, 8, 4) // 4 or 8 second timeout - -constexpr uint8_t timeoutval = (WDT_TIMEOUT - 0.5f) / 0.5f; - -void watchdog_init() { - CCM_CCGR3 |= CCM_CCGR3_WDOG1(3); // enable WDOG1 clocks - WDOG1_WMCR = 0; // disable power down PDE - WDOG1_WCR |= WDOG_WCR_SRS | WDOG_WCR_WT(timeoutval); - WDOG1_WCR |= WDOG_WCR_WDE | WDOG_WCR_WDT | WDOG_WCR_SRE; -} - -void HAL_watchdog_refresh() { - // Watchdog refresh sequence - WDOG1_WSR = 0x5555; - WDOG1_WSR = 0xAAAA; -} - -#endif // USE_WATCHDOG -#endif // __IMXRT1062__ diff --git a/Marlin/src/HAL/shared/Delay.cpp b/Marlin/src/HAL/shared/Delay.cpp index 32543c6d0abf4..c64376d25d9ab 100644 --- a/Marlin/src/HAL/shared/Delay.cpp +++ b/Marlin/src/HAL/shared/Delay.cpp @@ -108,13 +108,14 @@ #if ENABLED(MARLIN_DEV_MODE) void dump_delay_accuracy_check() { - auto report_call_time = [](PGM_P const name, PGM_P const unit, const uint32_t cycles, const uint32_t total, const bool do_flush=true) { + auto report_call_time = [](FSTR_P const name, FSTR_P const unit, const uint32_t cycles, const uint32_t total, const bool do_flush=true) { SERIAL_ECHOPGM("Calling "); - SERIAL_ECHOPGM_P(name); + SERIAL_ECHOF(name); SERIAL_ECHOLNPGM(" for ", cycles); - SERIAL_ECHOPGM_P(unit); + SERIAL_ECHOF(unit); SERIAL_ECHOLNPGM(" took: ", total); - SERIAL_ECHOPGM_P(unit); + SERIAL_CHAR(' '); + SERIAL_ECHOF(unit); if (do_flush) SERIAL_FLUSHTX(); }; @@ -126,41 +127,42 @@ constexpr uint32_t testValues[] = { 1, 5, 10, 20, 50, 100, 150, 200, 350, 500, 750, 1000 }; for (auto i : testValues) { s = micros(); DELAY_US(i); e = micros(); - report_call_time(PSTR("delay"), PSTR("us"), i, e - s); + report_call_time(F("delay"), F("us"), i, e - s); } if (HW_REG(_DWT_CTRL)) { + static FSTR_P cyc = F("cycles"); + static FSTR_P dcd = F("DELAY_CYCLES directly "); + for (auto i : testValues) { s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(i); e = HW_REG(_DWT_CYCCNT); - report_call_time(PSTR("runtime delay"), PSTR("cycles"), i, e - s); + report_call_time(F("runtime delay"), cyc, i, e - s); } // Measure the delay to call a real function compared to a function pointer s = HW_REG(_DWT_CYCCNT); delay_dwt(1); e = HW_REG(_DWT_CYCCNT); - report_call_time(PSTR("delay_dwt"), PSTR("cycles"), 1, e - s); - - static PGMSTR(dcd, "DELAY_CYCLES directly "); + report_call_time(F("delay_dwt"), cyc, 1, e - s); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 1); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 1, e - s, false); + report_call_time(dcd, cyc, 1, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES( 5); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 5, e - s, false); + report_call_time(dcd, cyc, 5, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(10); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 10, e - s, false); + report_call_time(dcd, cyc, 10, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(20); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 20, e - s, false); + report_call_time(dcd, cyc, 20, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(50); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 50, e - s, false); + report_call_time(dcd, cyc, 50, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(100); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 100, e - s, false); + report_call_time(dcd, cyc, 100, e - s, false); s = HW_REG(_DWT_CYCCNT); DELAY_CYCLES(200); e = HW_REG(_DWT_CYCCNT); - report_call_time(dcd, PSTR("cycles"), 200, e - s, false); + report_call_time(dcd, cyc, 200, e - s, false); } } #endif // MARLIN_DEV_MODE @@ -170,7 +172,7 @@ void calibrate_delay_loop() {} #if ENABLED(MARLIN_DEV_MODE) - void dump_delay_accuracy_check() { SERIAL_ECHOPGM_P(PSTR("N/A on this platform")); } + void dump_delay_accuracy_check() { SERIAL_ECHOPGM("N/A on this platform"); } #endif #endif diff --git a/Marlin/src/HAL/shared/Delay.h b/Marlin/src/HAL/shared/Delay.h index 04df35d88d6a6..a6795a78eaf4b 100644 --- a/Marlin/src/HAL/shared/Delay.h +++ b/Marlin/src/HAL/shared/Delay.h @@ -92,6 +92,12 @@ void calibrate_delay_loop(); #define DELAY_CYCLES(X) do { SmartDelay _smrtdly_X(X); } while(0) + #if GCC_VERSION <= 70000 + #define DELAY_CYCLES_VAR(X) DelayCycleFnc(X) + #else + #define DELAY_CYCLES_VAR DELAY_CYCLES + #endif + // 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)) @@ -160,6 +166,8 @@ void calibrate_delay_loop(); // Delay in microseconds #define DELAY_US(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL)) + #define DELAY_CYCLES_VAR DELAY_CYCLES + #elif defined(ESP32) || defined(__PLAT_LINUX__) || defined(__PLAT_NATIVE_SIM__) // DELAY_CYCLES specified inside platform @@ -200,9 +208,12 @@ void calibrate_delay_loop(); #endif #if ENABLED(DELAY_NS_ROUND_DOWN) - #define DELAY_NS(x) DELAY_CYCLES((x) * ((F_CPU) / 1000000UL) / 1000UL) // floor + #define _NS_TO_CYCLES(x) ( (x) * ((F_CPU) / 1000000UL) / 1000UL) // floor #elif ENABLED(DELAY_NS_ROUND_CLOSEST) - #define DELAY_NS(x) DELAY_CYCLES(((x) * ((F_CPU) / 1000000UL) + 500) / 1000UL) // round + #define _NS_TO_CYCLES(x) (((x) * ((F_CPU) / 1000000UL) + 500) / 1000UL) // round #else - #define DELAY_NS(x) DELAY_CYCLES(((x) * ((F_CPU) / 1000000UL) + 999) / 1000UL) // "ceil" + #define _NS_TO_CYCLES(x) (((x) * ((F_CPU) / 1000000UL) + 999) / 1000UL) // "ceil" #endif + +#define DELAY_NS(x) DELAY_CYCLES(_NS_TO_CYCLES(x)) +#define DELAY_NS_VAR(x) DELAY_CYCLES_VAR(_NS_TO_CYCLES(x)) diff --git a/Marlin/src/HAL/shared/HAL.cpp b/Marlin/src/HAL/shared/HAL.cpp new file mode 100644 index 0000000000000..4d92aedd9a1f5 --- /dev/null +++ b/Marlin/src/HAL/shared/HAL.cpp @@ -0,0 +1,36 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2022 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 . + * + */ + +/** + * HAL/shared/HAL.cpp + */ + +#include "../../inc/MarlinConfig.h" + +MarlinHAL hal; + +#if ENABLED(SOFT_RESET_VIA_SERIAL) + + // Global for use by e_parser.h + void HAL_reboot() { hal.reboot(); } + +#endif diff --git a/Marlin/src/HAL/shared/HAL_spi_L6470.cpp b/Marlin/src/HAL/shared/HAL_spi_L6470.cpp index bd85dbe7bd7ef..5d4ce89b2748e 100644 --- a/Marlin/src/HAL/shared/HAL_spi_L6470.cpp +++ b/Marlin/src/HAL/shared/HAL_spi_L6470.cpp @@ -92,9 +92,9 @@ uint8_t L64XX_Marlin::transfer_single(uint8_t data, int16_t ss_pin) { // First device in chain has data sent last extDigitalWrite(ss_pin, LOW); - DISABLE_ISRS(); // Disable interrupts during SPI transfer (can't allow partial command to chips) + hal.isr_off(); // Disable interrupts during SPI transfer (can't allow partial command to chips) const uint8_t data_out = L6470_SpiTransfer_Mode_3(data); - ENABLE_ISRS(); // Enable interrupts + hal.isr_on(); // Enable interrupts extDigitalWrite(ss_pin, HIGH); return data_out; @@ -107,9 +107,9 @@ uint8_t L64XX_Marlin::transfer_chain(uint8_t data, int16_t ss_pin, uint8_t chain extDigitalWrite(ss_pin, LOW); for (uint8_t i = L64XX::chain[0]; !L64xxManager.spi_abort && i >= 1; i--) { // Send data unless aborted - DISABLE_ISRS(); // Disable interrupts during SPI transfer (can't allow partial command to chips) + hal.isr_off(); // Disable interrupts during SPI transfer (can't allow partial command to chips) const uint8_t temp = L6470_SpiTransfer_Mode_3(uint8_t(i == chain_position ? data : dSPIN_NOP)); - ENABLE_ISRS(); // Enable interrupts + hal.isr_on(); // Enable interrupts if (i == chain_position) data_out = temp; } diff --git a/Marlin/src/HAL/shared/Marduino.h b/Marlin/src/HAL/shared/Marduino.h index ddc3451fbebed..0e2a021a3c4e9 100644 --- a/Marlin/src/HAL/shared/Marduino.h +++ b/Marlin/src/HAL/shared/Marduino.h @@ -39,7 +39,7 @@ #define DISABLED(V...) DO(DIS,&&,V) #undef _BV -#define _BV(b) (1UL << (b)) +#define _BV(b) (1 << (b)) #ifndef SBI #define SBI(A,B) (A |= _BV(B)) #endif @@ -87,3 +87,10 @@ #endif #include "progmem.h" + +class __FlashStringHelper; +typedef const __FlashStringHelper* FSTR_P; +#ifndef FPSTR + #define FPSTR(S) (reinterpret_cast(S)) +#endif +#define FTOP(S) (reinterpret_cast(S)) diff --git a/Marlin/src/HAL/shared/HAL_MinSerial.cpp b/Marlin/src/HAL/shared/MinSerial.cpp similarity index 97% rename from Marlin/src/HAL/shared/HAL_MinSerial.cpp rename to Marlin/src/HAL/shared/MinSerial.cpp index 9dda5fdf8c677..2e718d83dc12e 100644 --- a/Marlin/src/HAL/shared/HAL_MinSerial.cpp +++ b/Marlin/src/HAL/shared/MinSerial.cpp @@ -19,7 +19,7 @@ * along with this program. If not, see . * */ -#include "HAL_MinSerial.h" +#include "MinSerial.h" #if ENABLED(POSTMORTEM_DEBUGGING) diff --git a/Marlin/src/HAL/shared/HAL_MinSerial.h b/Marlin/src/HAL/shared/MinSerial.h similarity index 100% rename from Marlin/src/HAL/shared/HAL_MinSerial.h rename to Marlin/src/HAL/shared/MinSerial.h diff --git a/Marlin/src/HAL/shared/backtrace/backtrace.cpp b/Marlin/src/HAL/shared/backtrace/backtrace.cpp index ad88de8385acf..33e8e65154a04 100644 --- a/Marlin/src/HAL/shared/backtrace/backtrace.cpp +++ b/Marlin/src/HAL/shared/backtrace/backtrace.cpp @@ -25,7 +25,7 @@ #include "unwinder.h" #include "unwmemaccess.h" -#include "../HAL_MinSerial.h" +#include "../MinSerial.h" #include // Dump a backtrace entry diff --git a/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp b/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp index f1ee81ed4acbd..148927a19f52a 100644 --- a/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp +++ b/Marlin/src/HAL/shared/backtrace/unwarmbytab.cpp @@ -135,11 +135,11 @@ static UnwResult UnwTabExecuteInstructions(const UnwindCallbacks *cb, UnwTabStat while ((instruction = UnwTabGetNextInstruction(cb, ucb)) != -1) { if ((instruction & 0xC0) == 0x00) { // ARM_EXIDX_CMD_DATA_POP - /* vsp = vsp + (xxxxxx << 2) + 4 */ + /* vsp += (xxxxxx << 2) + 4 */ ucb->vrs[13] += ((instruction & 0x3F) << 2) + 4; } else if ((instruction & 0xC0) == 0x40) { // ARM_EXIDX_CMD_DATA_PUSH - /* vsp = vsp - (xxxxxx << 2) - 4 */ + /* vsp -= (xxxxxx << 2) - 4 */ ucb->vrs[13] -= ((instruction & 0x3F) << 2) - 4; } else if ((instruction & 0xF0) == 0x80) { diff --git a/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp b/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp index 2bde1e208d956..a4151b38c20e2 100644 --- a/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp +++ b/Marlin/src/HAL/shared/backtrace/unwmemaccess.cpp @@ -41,7 +41,7 @@ #define START_FLASH_ADDR 0x00000000 #define END_FLASH_ADDR 0x00080000 -#elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx) +#elif defined(__STM32F1__) || defined(STM32F1xx) || defined(STM32F0xx) || defined(STM32G0xx) // For STM32F103ZET6/STM32F103VET6/STM32F0xx // SRAM (0x20000000 - 0x20010000) (64kb) diff --git a/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp b/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp index 0f0f7c4807273..e54661c77071d 100644 --- a/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp +++ b/Marlin/src/HAL/shared/cpu_exception/exception_arm.cpp @@ -54,7 +54,7 @@ #include "exception_hook.h" #include "../backtrace/backtrace.h" -#include "../HAL_MinSerial.h" +#include "../MinSerial.h" #define HW_REG(X) (*((volatile unsigned long *)(X))) @@ -101,7 +101,7 @@ struct __attribute__((packed)) ContextSavedFrame { uint32_t ELR; }; -#if DISABLED(STM32F0xx) +#if NONE(STM32F0xx, STM32G0xx) extern "C" __attribute__((naked)) void CommonHandler_ASM() { __asm__ __volatile__ ( @@ -221,7 +221,7 @@ bool resume_from_fault() { // So we'll just need to refresh the watchdog for a while and then stop for the system to reboot uint32_t last = start; while (PENDING(last, end)) { - watchdog_refresh(); + hal.watchdog_refresh(); while (millis() == last) { /* nada */ } last = millis(); MinSerial::TX('.'); diff --git a/Marlin/src/HAL/shared/eeprom_api.h b/Marlin/src/HAL/shared/eeprom_api.h index 1f38639930dc8..cd744f82dc791 100644 --- a/Marlin/src/HAL/shared/eeprom_api.h +++ b/Marlin/src/HAL/shared/eeprom_api.h @@ -49,7 +49,7 @@ class PersistentStore { // Write one or more bytes of data // Return 'true' on write error - static inline bool write_data(const int pos, const uint8_t *value, const size_t size=sizeof(uint8_t)) { + static bool write_data(const int pos, const uint8_t *value, const size_t size=sizeof(uint8_t)) { int data_pos = pos; uint16_t crc = 0; return write_data(data_pos, value, size, &crc); @@ -57,11 +57,11 @@ class PersistentStore { // Write a single byte of data // Return 'true' on write error - static inline bool write_data(const int pos, const uint8_t value) { return write_data(pos, &value); } + static bool write_data(const int pos, const uint8_t value) { return write_data(pos, &value); } // Read one or more bytes of data // Return 'true' on read error - static inline bool read_data(const int pos, uint8_t *value, const size_t size=1) { + static bool read_data(const int pos, uint8_t *value, const size_t size=1) { int data_pos = pos; uint16_t crc = 0; return read_data(data_pos, value, size, &crc); diff --git a/Marlin/src/HAL/shared/math_32bit.h b/Marlin/src/HAL/shared/math_32bit.h index 87e9e6406ee45..1fb233e3e8963 100644 --- a/Marlin/src/HAL/shared/math_32bit.h +++ b/Marlin/src/HAL/shared/math_32bit.h @@ -26,6 +26,6 @@ /** * Math helper functions for 32 bit CPUs */ -static FORCE_INLINE uint32_t MultiU32X24toH32(uint32_t longIn1, uint32_t longIn2) { +FORCE_INLINE static uint32_t MultiU32X24toH32(uint32_t longIn1, uint32_t longIn2) { return ((uint64_t)longIn1 * longIn2 + 0x00800000) >> 24; } diff --git a/Marlin/src/HAL/shared/progmem.h b/Marlin/src/HAL/shared/progmem.h index 539d02705e973..4cd7663df9afe 100644 --- a/Marlin/src/HAL/shared/progmem.h +++ b/Marlin/src/HAL/shared/progmem.h @@ -38,7 +38,8 @@ #define PSTR(str) (str) #endif #ifndef F -#define F(str) (str) +class __FlashStringHelper; +#define F(str) (reinterpret_cast(PSTR(str))) #endif #ifndef _SFR_BYTE #define _SFR_BYTE(n) (n) @@ -110,7 +111,7 @@ #define strrchr_P(str, c) strrchr((str), (c)) #endif #ifndef strsep_P -#define strsep_P(strp, delim) strsep((strp), (delim)) +#define strsep_P(pstr, delim) strsep((pstr), (delim)) #endif #ifndef strspn_P #define strspn_P(str, chrs) strspn((str), (chrs)) diff --git a/Marlin/src/HAL/shared/servo.cpp b/Marlin/src/HAL/shared/servo.cpp index cfec6f3017377..b838800de654f 100644 --- a/Marlin/src/HAL/shared/servo.cpp +++ b/Marlin/src/HAL/shared/servo.cpp @@ -65,7 +65,7 @@ uint8_t ServoCount = 0; // the total number of attached /************ static functions common to all instances ***********************/ -static boolean isTimerActive(timer16_Sequence_t timer) { +static bool anyTimerChannelActive(const timer16_Sequence_t timer) { // returns true if any servo is active on this timer LOOP_L_N(channel, SERVOS_PER_TIMER) { if (SERVO(timer, channel).Pin.isActive) @@ -101,17 +101,18 @@ int8_t Servo::attach(const int inPin, const int inMin, const int inMax) { max = (MAX_PULSE_WIDTH - inMax) / 4; // initialize the timer if it has not already been initialized - timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); - if (!isTimerActive(timer)) initISR(timer); - servo_info[servoIndex].Pin.isActive = true; // this must be set after the check for isTimerActive + const timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if (!anyTimerChannelActive(timer)) initISR(timer); + servo_info[servoIndex].Pin.isActive = true; // this must be set after the check for anyTimerChannelActive return servoIndex; } void Servo::detach() { servo_info[servoIndex].Pin.isActive = false; - timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); - if (!isTimerActive(timer)) finISR(timer); + const timer16_Sequence_t timer = SERVO_INDEX_TO_TIMER(servoIndex); + if (!anyTimerChannelActive(timer)) finISR(timer); + //pinMode(servo_info[servoIndex].Pin.nbr, INPUT); // set servo pin to input } void Servo::write(int value) { diff --git a/Marlin/src/HAL/shared/servo_private.h b/Marlin/src/HAL/shared/servo_private.h index d85d8da8ba43d..10cc5a198821d 100644 --- a/Marlin/src/HAL/shared/servo_private.h +++ b/Marlin/src/HAL/shared/servo_private.h @@ -70,10 +70,10 @@ #define ticksToUs(_ticks) (unsigned(_ticks) * (SERVO_TIMER_PRESCALER) / clockCyclesPerMicrosecond()) // convenience macros -#define SERVO_INDEX_TO_TIMER(_servo_nbr) ((timer16_Sequence_t)(_servo_nbr / (SERVOS_PER_TIMER))) // returns the timer controlling this servo -#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % (SERVOS_PER_TIMER)) // returns the index of the servo on this timer -#define SERVO_INDEX(_timer,_channel) ((_timer*(SERVOS_PER_TIMER)) + _channel) // macro to access servo index by timer and channel -#define SERVO(_timer,_channel) (servo_info[SERVO_INDEX(_timer,_channel)]) // macro to access servo class by timer and channel +#define SERVO_INDEX_TO_TIMER(_servo_nbr) timer16_Sequence_t(_servo_nbr / (SERVOS_PER_TIMER)) // the timer controlling this servo +#define SERVO_INDEX_TO_CHANNEL(_servo_nbr) (_servo_nbr % (SERVOS_PER_TIMER)) // the index of the servo on this timer +#define SERVO_INDEX(_timer,_channel) ((_timer*(SERVOS_PER_TIMER)) + _channel) // servo index by timer and channel +#define SERVO(_timer,_channel) servo_info[SERVO_INDEX(_timer,_channel)] // servo class by timer and channel // Types @@ -94,5 +94,5 @@ extern ServoInfo_t servo_info[MAX_SERVOS]; // Public functions -extern void initISR(timer16_Sequence_t timer); -extern void finISR(timer16_Sequence_t timer); +void initISR(const timer16_Sequence_t timer_index); +void finISR(const timer16_Sequence_t timer_index); diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 1b9c8885b1fb0..e059c2edc4db3 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -74,8 +74,8 @@ #include "lcd/e3v2/common/encoder.h" #if ENABLED(DWIN_CREALITY_LCD) #include "lcd/e3v2/creality/dwin.h" - #elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "lcd/e3v2/enhanced/dwin.h" + #elif ENABLED(DWIN_LCD_PROUI) + #include "lcd/e3v2/proui/dwin.h" #elif ENABLED(DWIN_CREALITY_LCD_JYERSUI) #include "lcd/e3v2/jyersui/dwin.h" #endif @@ -97,7 +97,7 @@ #include "feature/host_actions.h" #endif -#if USE_BEEPER +#if HAS_BEEPER #include "libs/buzzer.h" #endif @@ -145,7 +145,7 @@ #include "feature/encoder_i2c.h" #endif -#if HAS_TRINAMIC_CONFIG && DISABLED(PSU_DEFAULT_OFF) +#if (HAS_TRINAMIC_CONFIG || HAS_TMC_SPI) && DISABLED(PSU_DEFAULT_OFF) #include "feature/tmc_util.h" #endif @@ -212,10 +212,18 @@ #include "module/tool_change.h" +#if HAS_FANCHECK + #include "feature/fancheck.h" +#endif + #if ENABLED(USE_CONTROLLER_FAN) #include "feature/controllerfan.h" #endif +#if HAS_PRUSA_MMU1 + #include "feature/mmu/mmu.h" +#endif + #if HAS_PRUSA_MMU2 #include "feature/mmu/mmu2.h" #endif @@ -240,6 +248,10 @@ #include "feature/power.h" #endif +#if ENABLED(EASYTHREED_UI) + #include "feature/easythreed_ui.h" +#endif + PGMSTR(M112_KILL_STR, "M112 Shutdown"); MarlinState marlin_state = MF_INITIALIZING; @@ -259,6 +271,7 @@ bool wait_for_heatup = true; while (wait_for_user && !(ms && ELAPSED(millis(), ms))) idle(TERN_(ADVANCED_PAUSE_FEATURE, no_sleep)); wait_for_user = false; + while (ui.button_pressed()) safe_delay(50); } #endif @@ -308,6 +321,10 @@ bool pin_is_protected(const pin_t pin) { #pragma GCC diagnostic pop +bool printer_busy() { + return planner.movesplanned() || printingIsActive(); +} + /** * A Print Job exists when the timer is running or SD is printing */ @@ -357,15 +374,15 @@ void startOrResumeJob() { TERN_(POWER_LOSS_RECOVERY, recovery.purge()); #ifdef EVENT_GCODE_SD_ABORT - queue.inject_P(PSTR(EVENT_GCODE_SD_ABORT)); + queue.inject(F(EVENT_GCODE_SD_ABORT)); #endif TERN_(PASSWORD_AFTER_SD_PRINT_ABORT, password.lock_machine()); } inline void finishSDPrinting() { - if (queue.enqueue_one_P(PSTR("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 = MF_RUNNING; // Signal to stop trying TERN_(PASSWORD_AFTER_SD_PRINT_END, password.lock_machine()); TERN_(DGUS_LCD_UI_MKS, ScreenHandler.SDPrintingFinished()); } @@ -400,38 +417,41 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { if (do_reset_timeout) gcode.reset_stepper_timeout(ms); if (gcode.stepper_max_timed_out(ms)) { - SERIAL_ERROR_MSG(STR_KILL_INACTIVE_TIME, parser.command_ptr); + SERIAL_ERROR_START(); + SERIAL_ECHOPGM(STR_KILL_PRE); + SERIAL_ECHOLNPGM(STR_KILL_INACTIVE_TIME, parser.command_ptr); kill(); } + const bool has_blocks = planner.has_blocks_queued(); // Any moves in the planner? + if (has_blocks) gcode.reset_stepper_timeout(ms); // Reset timeout for M18/M84, M85 max 'kill', and laser. + // M18 / M84 : Handle steppers inactive time timeout - if (gcode.stepper_inactive_time) { + #if HAS_DISABLE_INACTIVE_AXIS + if (gcode.stepper_inactive_time) { - static bool already_shutdown_steppers; // = false + static bool already_shutdown_steppers; // = false - // Any moves in the planner? Resets both the M18/M84 - // activity timeout and the M85 max 'kill' timeout - if (planner.has_blocks_queued()) - gcode.reset_stepper_timeout(ms); - else if (!do_reset_timeout && gcode.stepper_inactive_timeout()) { - if (!already_shutdown_steppers) { - already_shutdown_steppers = true; // L6470 SPI will consume 99% of free time without this - - // Individual axes will be disabled if configured - TERN_(DISABLE_INACTIVE_X, stepper.disable_axis(X_AXIS)); - TERN_(DISABLE_INACTIVE_Y, stepper.disable_axis(Y_AXIS)); - TERN_(DISABLE_INACTIVE_Z, stepper.disable_axis(Z_AXIS)); - TERN_(DISABLE_INACTIVE_I, stepper.disable_axis(I_AXIS)); - TERN_(DISABLE_INACTIVE_J, stepper.disable_axis(J_AXIS)); - TERN_(DISABLE_INACTIVE_K, stepper.disable_axis(K_AXIS)); - TERN_(DISABLE_INACTIVE_E, stepper.disable_e_steppers()); - - TERN_(AUTO_BED_LEVELING_UBL, ubl.steppers_were_disabled()); + if (!has_blocks && !do_reset_timeout && gcode.stepper_inactive_timeout()) { + if (!already_shutdown_steppers) { + already_shutdown_steppers = true; // L6470 SPI will consume 99% of free time without this + + // Individual axes will be disabled if configured + TERN_(DISABLE_INACTIVE_X, stepper.disable_axis(X_AXIS)); + TERN_(DISABLE_INACTIVE_Y, stepper.disable_axis(Y_AXIS)); + TERN_(DISABLE_INACTIVE_Z, stepper.disable_axis(Z_AXIS)); + TERN_(DISABLE_INACTIVE_I, stepper.disable_axis(I_AXIS)); + TERN_(DISABLE_INACTIVE_J, stepper.disable_axis(J_AXIS)); + TERN_(DISABLE_INACTIVE_K, stepper.disable_axis(K_AXIS)); + TERN_(DISABLE_INACTIVE_E, stepper.disable_e_steppers()); + + TERN_(AUTO_BED_LEVELING_UBL, bedlevel.steppers_were_disabled()); + } } + else + already_shutdown_steppers = false; } - else - already_shutdown_steppers = false; - } + #endif #if ENABLED(PHOTO_GCODE) && PIN_EXISTS(CHDK) // Check if CHDK should be set to LOW (after M240 set it HIGH) @@ -458,13 +478,15 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { // KILL the machine // ---------------------------------------------------------------- if (killCount >= KILL_DELAY) { - SERIAL_ERROR_MSG(STR_KILL_BUTTON); + SERIAL_ERROR_START(); + SERIAL_ECHOPGM(STR_KILL_PRE); + SERIAL_ECHOLNPGM(STR_KILL_BUTTON); kill(); } #endif #if HAS_FREEZE_PIN - Stepper::frozen = !READ(FREEZE_PIN); + stepper.frozen = READ(FREEZE_PIN) == FREEZE_STATE; #endif #if HAS_HOME @@ -474,7 +496,7 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { if (!IS_SD_PRINTING() && !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_MESSAGEPGM(MSG_AUTO_HOME); + LCD_MESSAGE(MSG_AUTO_HOME); queue.inject_P(G28_STR); } } @@ -493,14 +515,14 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { if (ELAPSED(ms, next_cub_ms_##N)) { \ next_cub_ms_##N = ms + CUB_DEBOUNCE_DELAY_##N; \ CODE; \ - queue.inject_P(PSTR(BUTTON##N##_GCODE)); \ - TERN_(HAS_LCD_MENU, ui.quick_feedback()); \ + queue.inject(F(BUTTON##N##_GCODE)); \ + TERN_(HAS_MARLINUI_MENU, ui.quick_feedback()); \ } \ } \ }while(0) - #define CHECK_CUSTOM_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, NOOP) - #define CHECK_BETTER_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, if (strlen(BUTTON##N##_DESC)) LCD_MESSAGEPGM_P(PSTR(BUTTON##N##_DESC))) + #define CHECK_CUSTOM_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, NOOP) + #define CHECK_BETTER_USER_BUTTON(N) _CHECK_CUSTOM_USER_BUTTON(N, if (strlen(BUTTON##N##_DESC)) LCD_MESSAGE_F(BUTTON##N##_DESC)) #if HAS_BETTER_USER_BUTTON(1) CHECK_BETTER_USER_BUTTON(1); @@ -629,6 +651,8 @@ inline void manage_inactivity(const bool no_stepper_sleep=false) { #endif #endif + TERN_(EASYTHREED_UI, easythreed_ui.run()); + TERN_(USE_CONTROLLER_FAN, controllerFan.update()); // Check if fan should be turned on to cool stepper drivers down TERN_(AUTO_POWER_CONTROL, powerManager.check(!ui.on_status_screen() || printJobOngoing() || printingIsPaused())); @@ -758,7 +782,7 @@ void idle(bool no_stepper_sleep/*=false*/) { manage_inactivity(no_stepper_sleep); // Manage Heaters (and Watchdog) - thermalManager.manage_heater(); + thermalManager.task(); // Max7219 heartbeat, animation, etc TERN_(MAX7219_DEBUG, max7219.idle_tasks()); @@ -770,10 +794,13 @@ void idle(bool no_stepper_sleep/*=false*/) { (void)check_tool_sensor_stats(active_extruder, true); // Handle filament runout sensors - TERN_(HAS_FILAMENT_SENSOR, runout.run()); + #if HAS_FILAMENT_SENSOR + if (TERN1(HAS_PRUSA_MMU2, !mmu2.enabled())) + runout.run(); + #endif // Run HAL idle tasks - TERN_(HAL_IDLETASK, HAL_idletask()); + hal.idletask(); // Check network connection TERN_(HAS_ETHERNET, ethernet.check()); @@ -802,10 +829,10 @@ void idle(bool no_stepper_sleep/*=false*/) { TERN_(PRINTCOUNTER, print_job_timer.tick()); // Update the Beeper queue - TERN_(USE_BEEPER, buzzer.tick()); + TERN_(HAS_BEEPER, buzzer.tick()); // Handle UI input / draw events - TERN(HAS_DWIN_E3V2_BASIC, DWIN_Update(), ui.update()); + TERN(DWIN_CREALITY_LCD, DWIN_Update(), ui.update()); // Run i2c Position Encoders #if ENABLED(I2C_POSITION_ENCODERS) @@ -825,6 +852,7 @@ void idle(bool no_stepper_sleep/*=false*/) { #if HAS_AUTO_REPORTING if (!gcode.autoreport_paused) { TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick()); + TERN_(AUTO_REPORT_FANS, fan_check.auto_reporter.tick()); TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick()); TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick()); TERN_(BUFFER_MONITORING, queue.auto_report_buffer_statistics()); @@ -852,16 +880,16 @@ void idle(bool no_stepper_sleep/*=false*/) { * Kill all activity and lock the machine. * After this the machine will need to be reset. */ -void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) { +void kill(FSTR_P const lcd_error/*=nullptr*/, FSTR_P const lcd_component/*=nullptr*/, const bool steppers_off/*=false*/) { thermalManager.disable_all_heaters(); TERN_(HAS_CUTTER, cutter.kill()); // Full cutter shutdown including ISR control // Echo the LCD message to serial for extra context - if (lcd_error) { SERIAL_ECHO_START(); SERIAL_ECHOLNPGM_P(lcd_error); } + if (lcd_error) { SERIAL_ECHO_START(); SERIAL_ECHOLNF(lcd_error); } - #if EITHER(HAS_DISPLAY, DWIN_CREALITY_LCD_ENHANCED) - ui.kill_screen(lcd_error ?: GET_TEXT(MSG_KILLED), lcd_component ?: NUL_STR); + #if HAS_DISPLAY + ui.kill_screen(lcd_error ?: GET_TEXT_F(MSG_KILLED), lcd_component ?: FPSTR(NUL_STR)); #else UNUSED(lcd_error); UNUSED(lcd_component); #endif @@ -872,7 +900,7 @@ void kill(PGM_P const lcd_error/*=nullptr*/, PGM_P const lcd_component/*=nullptr SERIAL_ERROR_MSG(STR_ERR_KILLED); #ifdef ACTION_ON_KILL - host_action_kill(); + hostui.kill(); #endif minkill(steppers_off); @@ -904,18 +932,18 @@ void minkill(const bool steppers_off/*=false*/) { // Wait for both KILL and ENC to be released while (TERN0(HAS_KILL, kill_state()) || TERN0(SOFT_RESET_ON_KILL, ui.button_pressed())) - watchdog_refresh(); + hal.watchdog_refresh(); // Wait for either KILL or ENC to be pressed again while (TERN1(HAS_KILL, !kill_state()) && TERN1(SOFT_RESET_ON_KILL, !ui.button_pressed())) - watchdog_refresh(); + hal.watchdog_refresh(); // Reboot the board - HAL_reboot(); + hal.reboot(); #else - for (;;) watchdog_refresh(); // Wait for RESET button or power-cycle + for (;;) hal.watchdog_refresh(); // Wait for RESET button or power-cycle #endif } @@ -935,7 +963,7 @@ void stop() { if (!IsStopped()) { SERIAL_ERROR_MSG(STR_ERR_STOPPED); - LCD_MESSAGEPGM(MSG_STOPPED); + LCD_MESSAGE(MSG_STOPPED); safe_delay(350); // allow enough time for messages to get out before stopping marlin_state = MF_STOPPED; } @@ -1023,7 +1051,7 @@ inline void tmc_standby_setup() { * • L64XX Stepper Drivers (SPI) * • Stepper Driver Reset: DISABLE * • TMC Stepper Drivers (SPI) - * • Run BOARD_INIT if defined + * • Run hal.init_board() for additional pins setup * • ESP WiFi * - Get the Reset Reason and report it * - Print startup messages and diagnostics @@ -1100,6 +1128,10 @@ void setup() { tmc_standby_setup(); // TMC Low Power Standby pins must be set early or they're not usable + // Check startup - does nothing if bootloader sets MCUSR to 0 + const byte mcu = hal.get_reset_source(); + hal.clear_reset_source(); + #if ENABLED(MARLIN_DEV_MODE) auto log_current_ms = [&](PGM_P const msg) { SERIAL_ECHO_START(); @@ -1144,9 +1176,13 @@ void setup() { #endif #endif - #if HAS_FREEZE_PIN + #if ENABLED(FREEZE_FEATURE) SETUP_LOG("FREEZE_PIN"); - SET_INPUT_PULLUP(FREEZE_PIN); + #if FREEZE_STATE + SET_INPUT_PULLDOWN(FREEZE_PIN); + #else + SET_INPUT_PULLUP(FREEZE_PIN); + #endif #endif #if HAS_SUICIDE @@ -1159,23 +1195,20 @@ void setup() { JTAGSWD_RESET(); #endif - #if EITHER(DISABLE_DEBUG, DISABLE_JTAG) + // Disable any hardware debug to free up pins for IO + #if ENABLED(DISABLE_DEBUG) && defined(JTAGSWD_DISABLE) delay(10); - // Disable any hardware debug to free up pins for IO - #if ENABLED(DISABLE_DEBUG) && defined(JTAGSWD_DISABLE) - SETUP_LOG("JTAGSWD_DISABLE"); - JTAGSWD_DISABLE(); - #elif defined(JTAG_DISABLE) - SETUP_LOG("JTAG_DISABLE"); - JTAG_DISABLE(); - #else - #error "DISABLE_(DEBUG|JTAG) is not supported for the selected MCU/Board." - #endif + SETUP_LOG("JTAGSWD_DISABLE"); + JTAGSWD_DISABLE(); + #elif ENABLED(DISABLE_JTAG) && defined(JTAG_DISABLE) + delay(10); + SETUP_LOG("JTAG_DISABLE"); + JTAG_DISABLE(); #endif TERN_(DYNAMIC_VECTORTABLE, hook_cpu_exceptions()); // If supported, install Marlin exception handlers at runtime - SETUP_RUN(HAL_init()); + SETUP_RUN(hal.init()); // Init and disable SPI thermocouples; this is still needed #if TEMP_SENSOR_0_IS_MAX_TC || (TEMP_SENSOR_REDUNDANT_IS_MAX_TC && REDUNDANT_TEMP_MATCH(SOURCE, E0)) @@ -1197,6 +1230,17 @@ void setup() { SETUP_RUN(tmc_serial_begin()); #endif + #if HAS_TMC_SPI + #if DISABLED(TMC_USE_SW_SPI) + SETUP_RUN(SPI.begin()); + #endif + SETUP_RUN(tmc_init_cs_pins()); + #endif + + #if HAS_L64XX + SETUP_RUN(L64xxManager.init()); // Set up SPI, init drivers + #endif + #if ENABLED(PSU_CONTROL) SETUP_LOG("PSU_CONTROL"); powerManager.init(); @@ -1206,37 +1250,22 @@ void setup() { SETUP_RUN(recovery.setup()); #endif - #if HAS_L64XX - SETUP_RUN(L64xxManager.init()); // Set up SPI, init drivers - #endif - #if HAS_STEPPER_RESET SETUP_RUN(disableStepperDrivers()); #endif - #if HAS_TMC_SPI - #if DISABLED(TMC_USE_SW_SPI) - SETUP_RUN(SPI.begin()); - #endif - SETUP_RUN(tmc_init_cs_pins()); - #endif - - #ifdef BOARD_INIT - SETUP_LOG("BOARD_INIT"); - BOARD_INIT(); - #endif + SETUP_RUN(hal.init_board()); SETUP_RUN(esp_wifi_init()); - // Check startup - does nothing if bootloader sets MCUSR to 0 - const byte mcu = HAL_get_reset_source(); - if (mcu & RST_POWER_ON) SERIAL_ECHOLNPGM(STR_POWERUP); - if (mcu & RST_EXTERNAL) SERIAL_ECHOLNPGM(STR_EXTERNAL_RESET); + // Report Reset Reason + if (mcu & RST_POWER_ON) SERIAL_ECHOLNPGM(STR_POWERUP); + if (mcu & RST_EXTERNAL) SERIAL_ECHOLNPGM(STR_EXTERNAL_RESET); if (mcu & RST_BROWN_OUT) SERIAL_ECHOLNPGM(STR_BROWNOUT_RESET); - if (mcu & RST_WATCHDOG) SERIAL_ECHOLNPGM(STR_WATCHDOG_RESET); - if (mcu & RST_SOFTWARE) SERIAL_ECHOLNPGM(STR_SOFTWARE_RESET); - HAL_clear_reset_source(); + if (mcu & RST_WATCHDOG) SERIAL_ECHOLNPGM(STR_WATCHDOG_RESET); + if (mcu & RST_SOFTWARE) SERIAL_ECHOLNPGM(STR_SOFTWARE_RESET); + // Identify myself as Marlin x.x.x SERIAL_ECHOLNPGM("Marlin " SHORT_BUILD_VERSION); #if defined(STRING_DISTRIBUTION_DATE) && defined(STRING_CONFIG_H_AUTHOR) SERIAL_ECHO_MSG( @@ -1245,13 +1274,13 @@ void setup() { ); #endif SERIAL_ECHO_MSG(" Compiled: " __DATE__); - SERIAL_ECHO_MSG(STR_FREE_MEMORY, freeMemory(), STR_PLANNER_BUFFER_BYTES, sizeof(block_t) * (BLOCK_BUFFER_SIZE)); + SERIAL_ECHO_MSG(STR_FREE_MEMORY, hal.freeMemory(), STR_PLANNER_BUFFER_BYTES, sizeof(block_t) * (BLOCK_BUFFER_SIZE)); // Some HAL need precise delay adjustment calibrate_delay_loop(); // Init buzzer pin(s) - #if USE_BEEPER + #if HAS_BEEPER SETUP_RUN(buzzer.init()); #endif @@ -1268,19 +1297,12 @@ void setup() { SETUP_RUN(controllerFan.setup()); #endif + TERN_(HAS_FANCHECK, fan_check.init()); + // UI must be initialized before EEPROM // (because EEPROM code calls the UI). - #if HAS_DWIN_E3V2_BASIC - SETUP_RUN(DWIN_Startup()); - #else - SETUP_RUN(ui.init()); - #if BOTH(HAS_WIRED_LCD, SHOW_BOOTSCREEN) - SETUP_RUN(ui.show_bootscreen()); - const millis_t bootscreen_ms = millis(); - #endif - SETUP_RUN(ui.reset_status()); // Load welcome message early. (Retained if no errors exist.) - #endif + SETUP_RUN(ui.init()); #if PIN_EXISTS(SAFE_POWER) #if HAS_DRIVER_SAFE_POWER_PROTECT @@ -1291,10 +1313,6 @@ void setup() { #endif #endif - #if ENABLED(PROBE_TARE) - SETUP_RUN(probe.tare_init()); - #endif - #if BOTH(SDSUPPORT, SDCARD_EEPROM_EMULATION) SETUP_RUN(card.mount()); // Mount media with settings before first_load #endif @@ -1302,6 +1320,15 @@ void setup() { SETUP_RUN(settings.first_load()); // Load data from EEPROM if available (or use defaults) // This also updates variables in the planner, elsewhere + #if BOTH(HAS_WIRED_LCD, SHOW_BOOTSCREEN) + SETUP_RUN(ui.show_bootscreen()); + const millis_t bootscreen_ms = millis(); + #endif + + #if ENABLED(PROBE_TARE) + SETUP_RUN(probe.tare_init()); + #endif + #if HAS_ETHERNET SETUP_RUN(ethernet.init()); #endif @@ -1320,6 +1347,10 @@ void setup() { SETUP_RUN(endstops.init()); // Init endstops and pullups + #if ENABLED(DELTA) && !HAS_SOFTWARE_ENDSTOPS + SETUP_RUN(refresh_delta_clip_start_height()); // Init safe delta height without soft endstops + #endif + SETUP_RUN(stepper.init()); // Init stepper. This enables interrupts! #if HAS_SERVOS @@ -1346,6 +1377,9 @@ void setup() { #endif #if HAS_BED_PROBE + #if PIN_EXISTS(PROBE_ENABLE) + OUT_WRITE(PROBE_ENABLE_PIN, LOW); // Disable + #endif SETUP_RUN(endstops.enable_z_probe(false)); #endif @@ -1476,6 +1510,10 @@ void setup() { SETUP_RUN(bltouch.init(/*set_voltage=*/true)); #endif + #if ENABLED(MAGLEV4) + OUT_WRITE(MAGLEV_TRIGGER_PIN, LOW); + #endif + #if ENABLED(I2C_POSITION_ENCODERS) SETUP_RUN(I2CPEM.init()); #endif @@ -1512,7 +1550,7 @@ void setup() { #endif #if ENABLED(USE_WATCHDOG) - SETUP_RUN(watchdog_init()); // Reinit watchdog after HAL_get_reset_source call + SETUP_RUN(hal.watchdog_init()); // Reinit watchdog after hal.get_reset_source call #endif #if ENABLED(EXTERNAL_CLOSED_LOOP_CONTROLLER) @@ -1521,15 +1559,11 @@ void setup() { #ifdef STARTUP_COMMANDS SETUP_LOG("STARTUP_COMMANDS"); - queue.inject_P(PSTR(STARTUP_COMMANDS)); + queue.inject(F(STARTUP_COMMANDS)); #endif #if ENABLED(HOST_PROMPT_SUPPORT) - SETUP_RUN(host_action_prompt_end()); - #endif - - #if HAS_TRINAMIC_CONFIG && DISABLED(PSU_DEFAULT_OFF) - SETUP_RUN(test_tmc_connection()); + SETUP_RUN(hostui.prompt_end()); #endif #if HAS_DRIVER_SAFE_POWER_PROTECT @@ -1547,15 +1581,11 @@ void setup() { #endif #if HAS_DWIN_E3V2_BASIC - Encoder_Configuration(); - HMI_Init(); - HMI_SetLanguageCache(); - HMI_StartFrame(true); - DWIN_StatusChanged_P(GET_TEXT(WELCOME_MSG)); + SETUP_RUN(DWIN_InitScreen()); #endif #if HAS_SERVICE_INTERVALS && !HAS_DWIN_E3V2_BASIC - ui.reset_status(true); // Show service messages or keep current status + SETUP_RUN(ui.reset_status(true)); // Show service messages or keep current status #endif #if ENABLED(MAX7219_DEBUG) @@ -1585,8 +1615,16 @@ void setup() { SETUP_RUN(password.lock_machine()); // Will not proceed until correct password provided #endif - #if BOTH(HAS_LCD_MENU, TOUCH_SCREEN_CALIBRATION) && EITHER(TFT_CLASSIC_UI, TFT_COLOR_UI) - ui.check_touch_calibration(); + #if BOTH(HAS_MARLINUI_MENU, TOUCH_SCREEN_CALIBRATION) && EITHER(TFT_CLASSIC_UI, TFT_COLOR_UI) + SETUP_RUN(ui.check_touch_calibration()); + #endif + + #if ENABLED(EASYTHREED_UI) + SETUP_RUN(easythreed_ui.init()); + #endif + + #if HAS_TRINAMIC_CONFIG && DISABLED(PSU_DEFAULT_OFF) + SETUP_RUN(test_tmc_connection()); #endif marlin_state = MF_RUNNING; @@ -1618,6 +1656,10 @@ void loop() { queue.advance(); + #if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN) + powerManager.checkAutoPowerOff(); + #endif + endstops.event_handler(); TERN_(HAS_TFT_LVGL_UI, printer_state_polling()); diff --git a/Marlin/src/MarlinCore.h b/Marlin/src/MarlinCore.h index c3698d616da0b..f80405a302ee1 100644 --- a/Marlin/src/MarlinCore.h +++ b/Marlin/src/MarlinCore.h @@ -38,7 +38,7 @@ inline void idle_no_sleep() { idle(true); } extern bool G38_did_trigger; // Flag from the ISR to indicate the endstop changed #endif -void kill(PGM_P const lcd_error=nullptr, PGM_P const lcd_component=nullptr, const bool steppers_off=false); +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); // Global State of the firmware @@ -61,6 +61,8 @@ bool printJobOngoing(); bool printingIsPaused(); void startOrResumeJob(); +bool printer_busy(); + extern bool wait_for_heatup; #if HAS_RESUME_CONTINUE diff --git a/Marlin/src/core/boards.h b/Marlin/src/core/boards.h index 82c68f3171265..55cab84ec9473 100644 --- a/Marlin/src/core/boards.h +++ b/Marlin/src/core/boards.h @@ -115,6 +115,8 @@ #define BOARD_RAMPS_S_12_EFFB 1159 // Ramps S 1.2 by Sakul.cz (Power outputs: Hotend, Fan0, Fan1, Bed) #define BOARD_LONGER3D_LK1_PRO 1160 // Longer LK1 PRO / Alfawise U20 Pro (PRO version) #define BOARD_LONGER3D_LKx_PRO 1161 // Longer LKx PRO / Alfawise Uxx Pro (PRO version) +#define BOARD_ZRIB_V53 1162 // Zonestar zrib V5.3 (Chinese RAMPS replica) +#define BOARD_PXMALION_CORE_I3 1163 // Pxmalion Core I3 // // RAMBo and derivatives @@ -159,9 +161,10 @@ #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 gcode only) +#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.) // // ATmega1281, ATmega2561 @@ -225,33 +228,34 @@ #define BOARD_RAMPS_14_RE_ARM_EFF 2002 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend, Fan0, Fan1) #define BOARD_RAMPS_14_RE_ARM_EEF 2003 // Re-ARM with RAMPS 1.4 (Power outputs: Hotend0, Hotend1, Fan) #define BOARD_RAMPS_14_RE_ARM_SF 2004 // Re-ARM with RAMPS 1.4 (Power outputs: Spindle, Controller Fan) -#define BOARD_MKS_SBASE 2005 // MKS-Sbase (Power outputs: Hotend0, Hotend1, Bed, Fan) +#define BOARD_MKS_SBASE 2005 // MKS-Sbase #define BOARD_AZSMZ_MINI 2006 // AZSMZ Mini -#define BOARD_BIQU_BQ111_A4 2007 // BIQU BQ111-A4 (Power outputs: Hotend, Fan, Bed) -#define BOARD_SELENA_COMPACT 2008 // Selena Compact (Power outputs: Hotend0, Hotend1, Bed0, Bed1, Fan0, Fan1) -#define BOARD_BIQU_B300_V1_0 2009 // BIQU B300_V1.0 (Power outputs: Hotend0, Fan, Bed, SPI Driver) -#define BOARD_MKS_SGEN_L 2010 // MKS-SGen-L (Power outputs: Hotend0, Hotend1, Bed, Fan) +#define BOARD_BIQU_BQ111_A4 2007 // BIQU BQ111-A4 +#define BOARD_SELENA_COMPACT 2008 // Selena Compact +#define BOARD_BIQU_B300_V1_0 2009 // BIQU B300_V1.0 +#define BOARD_MKS_SGEN_L 2010 // MKS-SGen-L #define BOARD_GMARSH_X6_REV1 2011 // GMARSH X6, revision 1 prototype -#define BOARD_BTT_SKR_V1_1 2012 // BigTreeTech SKR v1.1 (Power outputs: Hotend0, Hotend1, Fan, Bed) -#define BOARD_BTT_SKR_V1_3 2013 // BigTreeTech SKR v1.3 (Power outputs: Hotend0, Hotend1, Fan, Bed) -#define BOARD_BTT_SKR_V1_4 2014 // BigTreeTech SKR v1.4 (Power outputs: Hotend0, Hotend1, Fan, Bed) +#define BOARD_BTT_SKR_V1_1 2012 // BigTreeTech SKR v1.1 +#define BOARD_BTT_SKR_V1_3 2013 // BigTreeTech SKR v1.3 +#define BOARD_BTT_SKR_V1_4 2014 // BigTreeTech SKR v1.4 +#define BOARD_EMOTRONIC 2015 // eMotion-Tech eMotronic // // LPC1769 ARM Cortex M3 // -#define BOARD_MKS_SGEN 2500 // MKS-SGen (Power outputs: Hotend0, Hotend1, Bed, Fan) -#define BOARD_AZTEEG_X5_GT 2501 // Azteeg X5 GT (Power outputs: Hotend0, Hotend1, Bed, Fan) -#define BOARD_AZTEEG_X5_MINI 2502 // Azteeg X5 Mini (Power outputs: Hotend0, Bed, Fan) -#define BOARD_AZTEEG_X5_MINI_WIFI 2503 // Azteeg X5 Mini Wifi (Power outputs: Hotend0, Bed, Fan) +#define BOARD_MKS_SGEN 2500 // MKS-SGen +#define BOARD_AZTEEG_X5_GT 2501 // Azteeg X5 GT +#define BOARD_AZTEEG_X5_MINI 2502 // Azteeg X5 Mini +#define BOARD_AZTEEG_X5_MINI_WIFI 2503 // Azteeg X5 Mini Wifi #define BOARD_COHESION3D_REMIX 2504 // Cohesion3D ReMix #define BOARD_COHESION3D_MINI 2505 // Cohesion3D Mini #define BOARD_SMOOTHIEBOARD 2506 // Smoothieboard #define BOARD_TH3D_EZBOARD 2507 // TH3D EZBoard v1.0 -#define BOARD_BTT_SKR_V1_4_TURBO 2508 // BigTreeTech SKR v1.4 TURBO (Power outputs: Hotend0, Hotend1, Fan, Bed) -#define BOARD_MKS_SGEN_L_V2 2509 // MKS SGEN_L V2 (Power outputs: Hotend0, Hotend1, Bed, Fan) -#define BOARD_BTT_SKR_E3_TURBO 2510 // BigTreeTech SKR E3 Turbo (Power outputs: Hotend0, Hotend1, Bed, Fan0, Fan1) -#define BOARD_FLY_CDY 2511 // FLYmaker FLY CDY (Power outputs: Hotend0, Hotend1, Hotend2, Bed, Fan0, Fan1, Fan2) +#define BOARD_BTT_SKR_V1_4_TURBO 2508 // BigTreeTech SKR v1.4 TURBO +#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 // // SAM3X8E ARM Cortex M3 @@ -277,8 +281,8 @@ #define BOARD_RAMPS4DUE_EFF 3017 // RAMPS4DUE (Power outputs: Hotend, Fan0, Fan1) #define BOARD_RAMPS4DUE_EEF 3018 // RAMPS4DUE (Power outputs: Hotend0, Hotend1, Fan) #define BOARD_RAMPS4DUE_SF 3019 // RAMPS4DUE (Power outputs: Spindle, Controller Fan) -#define BOARD_RURAMPS4D_11 3020 // RuRAMPS4Duo v1.1 (Power outputs: Hotend0, Hotend1, Hotend2, Fan0, Fan1, Bed) -#define BOARD_RURAMPS4D_13 3021 // RuRAMPS4Duo v1.3 (Power outputs: Hotend0, Hotend1, Hotend2, Fan0, Fan1, Bed) +#define BOARD_RURAMPS4D_11 3020 // RuRAMPS4Duo v1.1 +#define BOARD_RURAMPS4D_13 3021 // RuRAMPS4Duo v1.3 #define BOARD_ULTRATRONICS_PRO 3022 // ReprapWorld Ultratronics Pro V1.0 #define BOARD_ARCHIM1 3023 // UltiMachine Archim1 (with DRV8825 drivers) #define BOARD_ARCHIM2 3024 // UltiMachine Archim2 (with TMC2130 drivers) @@ -290,7 +294,7 @@ // SAM3X8C ARM Cortex M3 // -#define BOARD_PRINTRBOARD_G2 3100 // PRINTRBOARD G2 +#define BOARD_PRINTRBOARD_G2 3100 // Printrboard G2 #define BOARD_ADSK 3101 // Arduino DUE Shield Kit (ADSK) // @@ -300,63 +304,70 @@ #define BOARD_MALYAN_M200_V2 4000 // STM32F070CB controller #define BOARD_MALYAN_M300 4001 // STM32F070-based delta #define BOARD_STM32F103RE 4002 // STM32F103RE Libmaple-based STM32F1 controller -#define BOARD_MALYAN_M200 4003 // STM32C8T6 Libmaple-based STM32F1 controller +#define BOARD_MALYAN_M200 4003 // STM32C8 Libmaple-based STM32F1 controller #define BOARD_STM3R_MINI 4004 // STM32F103RE Libmaple-based STM32F1 controller -#define BOARD_GTM32_PRO_VB 4005 // STM32F103VET6 controller -#define BOARD_GTM32_MINI 4006 // STM32F103VET6 controller -#define BOARD_GTM32_MINI_A30 4007 // STM32F103VET6 controller -#define BOARD_GTM32_REV_B 4008 // STM32F103VET6 controller +#define BOARD_GTM32_PRO_VB 4005 // STM32F103VE controller +#define BOARD_GTM32_MINI 4006 // STM32F103VE controller +#define BOARD_GTM32_MINI_A30 4007 // STM32F103VE controller +#define BOARD_GTM32_REV_B 4008 // STM32F103VE controller #define BOARD_MORPHEUS 4009 // STM32F103C8 / STM32F103CB Libmaple-based STM32F1 controller -#define BOARD_CHITU3D 4010 // Chitu3D (STM32F103RET6) -#define BOARD_MKS_ROBIN 4011 // MKS Robin (STM32F103ZET6) -#define BOARD_MKS_ROBIN_MINI 4012 // MKS Robin Mini (STM32F103VET6) -#define BOARD_MKS_ROBIN_NANO 4013 // MKS Robin Nano (STM32F103VET6) -#define BOARD_MKS_ROBIN_NANO_V2 4014 // MKS Robin Nano V2 (STM32F103VET6) -#define BOARD_MKS_ROBIN_LITE 4015 // MKS Robin Lite/Lite2 (STM32F103RCT6) -#define BOARD_MKS_ROBIN_LITE3 4016 // MKS Robin Lite3 (STM32F103RCT6) -#define BOARD_MKS_ROBIN_PRO 4017 // MKS Robin Pro (STM32F103ZET6) -#define BOARD_MKS_ROBIN_E3 4018 // MKS Robin E3 (STM32F103RCT6) -#define BOARD_MKS_ROBIN_E3_V1_1 4019 // MKS Robin E3 V1.1 (STM32F103RCT6) -#define BOARD_MKS_ROBIN_E3D 4020 // MKS Robin E3D (STM32F103RCT6) -#define BOARD_MKS_ROBIN_E3D_V1_1 4021 // MKS Robin E3D V1.1 (STM32F103RCT6) -#define BOARD_MKS_ROBIN_E3P 4022 // MKS Robin E3p (STM32F103VET6) +#define BOARD_CHITU3D 4010 // Chitu3D (STM32F103RE) +#define BOARD_MKS_ROBIN 4011 // MKS Robin (STM32F103ZE) +#define BOARD_MKS_ROBIN_MINI 4012 // MKS Robin Mini (STM32F103VE) +#define BOARD_MKS_ROBIN_NANO 4013 // MKS Robin Nano (STM32F103VE) +#define BOARD_MKS_ROBIN_NANO_V2 4014 // MKS Robin Nano V2 (STM32F103VE) +#define BOARD_MKS_ROBIN_LITE 4015 // MKS Robin Lite/Lite2 (STM32F103RC) +#define BOARD_MKS_ROBIN_LITE3 4016 // MKS Robin Lite3 (STM32F103RC) +#define BOARD_MKS_ROBIN_PRO 4017 // MKS Robin Pro (STM32F103ZE) +#define BOARD_MKS_ROBIN_E3 4018 // MKS Robin E3 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3_V1_1 4019 // MKS Robin E3 V1.1 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3D 4020 // MKS Robin E3D (STM32F103RC) +#define BOARD_MKS_ROBIN_E3D_V1_1 4021 // MKS Robin E3D V1.1 (STM32F103RC) +#define BOARD_MKS_ROBIN_E3P 4022 // MKS Robin E3p (STM32F103VE) #define BOARD_BTT_SKR_MINI_V1_1 4023 // BigTreeTech SKR Mini v1.1 (STM32F103RC) #define BOARD_BTT_SKR_MINI_E3_V1_0 4024 // BigTreeTech SKR Mini E3 (STM32F103RC) #define BOARD_BTT_SKR_MINI_E3_V1_2 4025 // BigTreeTech SKR Mini E3 V1.2 (STM32F103RC) #define BOARD_BTT_SKR_MINI_E3_V2_0 4026 // BigTreeTech SKR Mini E3 V2.0 (STM32F103RC / STM32F103RE) -#define BOARD_BTT_SKR_MINI_MZ_V1_0 4027 // BigTreeTech SKR Mini MZ V1.0 (STM32F103RC) -#define BOARD_BTT_SKR_E3_DIP 4028 // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE) -#define BOARD_BTT_SKR_CR6 4029 // BigTreeTech SKR CR6 v1.0 (STM32F103RE) -#define BOARD_JGAURORA_A5S_A1 4030 // JGAurora A5S A1 (STM32F103ZET6) -#define BOARD_FYSETC_AIO_II 4031 // FYSETC AIO_II -#define BOARD_FYSETC_CHEETAH 4032 // FYSETC Cheetah -#define BOARD_FYSETC_CHEETAH_V12 4033 // FYSETC Cheetah V1.2 -#define BOARD_LONGER3D_LK 4034 // Alfawise U20/U20+/U30 (Longer3D LK1/2) / STM32F103VET6 -#define BOARD_CCROBOT_MEEB_3DP 4035 // ccrobot-online.com MEEB_3DP (STM32F103RC) -#define BOARD_CHITU3D_V5 4036 // Chitu3D TronXY X5SA V5 Board -#define BOARD_CHITU3D_V6 4037 // Chitu3D TronXY X5SA V6 Board -#define BOARD_CHITU3D_V9 4038 // Chitu3D TronXY X5SA V9 Board -#define BOARD_CREALITY_V4 4039 // Creality v4.x (STM32F103RE) -#define BOARD_CREALITY_V427 4040 // Creality v4.2.7 (STM32F103RE) -#define BOARD_CREALITY_V4210 4041 // Creality v4.2.10 (STM32F103RE) as found in the CR-30 -#define BOARD_CREALITY_V431 4042 // Creality v4.3.1 (STM32F103RE) -#define BOARD_CREALITY_V431_A 4043 // Creality v4.3.1a (STM32F103RE) -#define BOARD_CREALITY_V431_B 4044 // Creality v4.3.1b (STM32F103RE) -#define BOARD_CREALITY_V431_C 4045 // Creality v4.3.1c (STM32F103RE) -#define BOARD_CREALITY_V431_D 4046 // Creality v4.3.1d (STM32F103RE) -#define BOARD_CREALITY_V452 4047 // Creality v4.5.2 (STM32F103RE) -#define BOARD_CREALITY_V453 4048 // Creality v4.5.3 (STM32F103RE) -#define BOARD_CREALITY_V24S1 4049 // Creality v2.4.S1 (STM32F103RE) v101 as found in the Ender 7 -#define BOARD_TRIGORILLA_PRO 4050 // Trigorilla Pro (STM32F103ZET6) -#define BOARD_FLY_MINI 4051 // FLYmaker FLY MINI (STM32F103RCT6) -#define BOARD_FLSUN_HISPEED 4052 // FLSUN HiSpeedV1 (STM32F103VET6) -#define BOARD_BEAST 4053 // STM32F103RET6 Libmaple-based controller -#define BOARD_MINGDA_MPX_ARM_MINI 4054 // STM32F103ZET6 Mingda MD-16 -#define BOARD_GTM32_PRO_VD 4055 // STM32F103VET6 controller -#define BOARD_ZONESTAR_ZM3E2 4056 // Zonestar ZM3E2 (STM32F103RCT6) -#define BOARD_ZONESTAR_ZM3E4 4057 // Zonestar ZM3E4 V1 (STM32F103VCT6) -#define BOARD_ZONESTAR_ZM3E4V2 4058 // Zonestar ZM3E4 V2 (STM32F103VCT6) -#define BOARD_ERYONE_ERY32_MINI 4059 // Eryone Ery32 mini (STM32F103VET6) +#define BOARD_BTT_SKR_MINI_E3_V3_0 4027 // BigTreeTech SKR Mini E3 V3.0 (STM32G0B1RE) +#define BOARD_BTT_SKR_MINI_MZ_V1_0 4028 // BigTreeTech SKR Mini MZ V1.0 (STM32F103RC) +#define BOARD_BTT_SKR_E3_DIP 4029 // BigTreeTech SKR E3 DIP V1.0 (STM32F103RC / STM32F103RE) +#define BOARD_BTT_SKR_CR6 4030 // BigTreeTech SKR CR6 v1.0 (STM32F103RE) +#define BOARD_JGAURORA_A5S_A1 4031 // JGAurora A5S A1 (STM32F103ZE) +#define BOARD_FYSETC_AIO_II 4032 // FYSETC AIO_II (STM32F103RC) +#define BOARD_FYSETC_CHEETAH 4033 // FYSETC Cheetah (STM32F103RC) +#define BOARD_FYSETC_CHEETAH_V12 4034 // FYSETC Cheetah V1.2 (STM32F103RC) +#define BOARD_LONGER3D_LK 4035 // Longer3D LK1/2 - Alfawise U20/U20+/U30 (STM32F103VE) +#define BOARD_CCROBOT_MEEB_3DP 4036 // ccrobot-online.com MEEB_3DP (STM32F103RC) +#define BOARD_CHITU3D_V5 4037 // Chitu3D TronXY X5SA V5 Board (STM32F103ZE) +#define BOARD_CHITU3D_V6 4038 // Chitu3D TronXY X5SA V6 Board (STM32F103ZE) +#define BOARD_CHITU3D_V9 4039 // Chitu3D TronXY X5SA V9 Board (STM32F103ZE) +#define BOARD_CREALITY_V4 4040 // Creality v4.x (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V422 4041 // Creality v4.2.2 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V423 4042 // Creality v4.2.3 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V425 4043 // Creality v4.2.5 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V427 4044 // Creality v4.2.7 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V4210 4045 // Creality v4.2.10 (STM32F103RC / STM32F103RE) as found in the CR-30 +#define BOARD_CREALITY_V431 4046 // Creality v4.3.1 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_A 4047 // Creality v4.3.1a (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_B 4048 // Creality v4.3.1b (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_C 4049 // Creality v4.3.1c (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V431_D 4050 // Creality v4.3.1d (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V452 4051 // Creality v4.5.2 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V453 4052 // Creality v4.5.3 (STM32F103RC / STM32F103RE) +#define BOARD_CREALITY_V24S1 4053 // Creality v2.4.S1 (STM32F103RC / STM32F103RE) v101 as found in the Ender-7 +#define BOARD_CREALITY_V24S1_301 4054 // Creality v2.4.S1_301 (STM32F103RC / STM32F103RE) v301 as found in the Ender-3 S1 +#define BOARD_CREALITY_V25S1 4055 // Creality v2.5.S1 (STM32F103RE) as found in the CR-10 Smart Pro +#define BOARD_TRIGORILLA_PRO 4056 // Trigorilla Pro (STM32F103ZE) +#define BOARD_FLY_MINI 4057 // FLYmaker FLY MINI (STM32F103RC) +#define BOARD_FLSUN_HISPEED 4058 // FLSUN HiSpeedV1 (STM32F103VE) +#define BOARD_BEAST 4059 // STM32F103RE Libmaple-based controller +#define BOARD_MINGDA_MPX_ARM_MINI 4060 // STM32F103ZE Mingda MD-16 +#define BOARD_GTM32_PRO_VD 4061 // STM32F103VE controller +#define BOARD_ZONESTAR_ZM3E2 4062 // Zonestar ZM3E2 (STM32F103RC) +#define BOARD_ZONESTAR_ZM3E4 4063 // Zonestar ZM3E4 V1 (STM32F103VC) +#define BOARD_ZONESTAR_ZM3E4V2 4064 // Zonestar ZM3E4 V2 (STM32F103VC) +#define BOARD_ERYONE_ERY32_MINI 4065 // Eryone Ery32 mini (STM32F103VE) +#define BOARD_PANDA_PI_V29 4066 // Panda Pi V2.9 - Standalone (STM32F103RC) // // ARM Cortex-M4F @@ -370,42 +381,47 @@ // #define BOARD_ARMED 4200 // Arm'ed STM32F4-based controller -#define BOARD_RUMBA32_V1_0 4201 // RUMBA32 STM32F446VET6 based controller from Aus3D -#define BOARD_RUMBA32_V1_1 4202 // RUMBA32 STM32F446VET6 based controller from Aus3D -#define BOARD_RUMBA32_MKS 4203 // RUMBA32 STM32F446VET6 based controller from Makerbase -#define BOARD_RUMBA32_BTT 4204 // RUMBA32 STM32F446VET6 based controller from BIGTREETECH +#define BOARD_RUMBA32_V1_0 4201 // RUMBA32 STM32F446VE based controller from Aus3D +#define BOARD_RUMBA32_V1_1 4202 // RUMBA32 STM32F446VE based controller from Aus3D +#define BOARD_RUMBA32_MKS 4203 // RUMBA32 STM32F446VE based controller from Makerbase +#define BOARD_RUMBA32_BTT 4204 // RUMBA32 STM32F446VE based controller from BIGTREETECH #define BOARD_BLACK_STM32F407VE 4205 // BLACK_STM32F407VE #define BOARD_BLACK_STM32F407ZE 4206 // BLACK_STM32F407ZE #define BOARD_STEVAL_3DP001V1 4207 // STEVAL-3DP001V1 3D PRINTER BOARD -#define BOARD_BTT_SKR_PRO_V1_1 4208 // BigTreeTech SKR Pro v1.1 (STM32F407ZGT6) -#define BOARD_BTT_SKR_PRO_V1_2 4209 // BigTreeTech SKR Pro v1.2 (STM32F407ZGT6) -#define BOARD_BTT_BTT002_V1_0 4210 // BigTreeTech BTT002 v1.0 (STM32F407VGT6) -#define BOARD_BTT_E3_RRF 4211 // BigTreeTech E3 RRF (STM32F407VGT6) -#define BOARD_BTT_SKR_V2_0_REV_A 4212 // BigTreeTech SKR v2.0 Rev A (STM32F407VGT6) -#define BOARD_BTT_SKR_V2_0_REV_B 4213 // BigTreeTech SKR v2.0 Rev B (STM32F407VGT6) +#define BOARD_BTT_SKR_PRO_V1_1 4208 // BigTreeTech SKR Pro v1.1 (STM32F407ZG) +#define BOARD_BTT_SKR_PRO_V1_2 4209 // BigTreeTech SKR Pro v1.2 (STM32F407ZG) +#define BOARD_BTT_BTT002_V1_0 4210 // BigTreeTech BTT002 v1.0 (STM32F407VG) +#define BOARD_BTT_E3_RRF 4211 // BigTreeTech E3 RRF (STM32F407VG) +#define BOARD_BTT_SKR_V2_0_REV_A 4212 // BigTreeTech SKR v2.0 Rev A (STM32F407VG) +#define BOARD_BTT_SKR_V2_0_REV_B 4213 // BigTreeTech SKR v2.0 Rev B (STM32F407VG/STM32F429VG) #define BOARD_BTT_GTR_V1_0 4214 // BigTreeTech GTR v1.0 (STM32F407IGT) -#define BOARD_BTT_OCTOPUS_V1_0 4215 // BigTreeTech Octopus v1.0 (STM32F446ZET6) -#define BOARD_BTT_OCTOPUS_V1_1 4216 // BigTreeTech Octopus v1.1 (STM32F446ZET6) -#define BOARD_BTT_OCTOPUS_PRO_V1_0 4217 // BigTreeTech Octopus Pro v1.0 (STM32F446ZET6/STM32F429ZGT6) +#define BOARD_BTT_OCTOPUS_V1_0 4215 // BigTreeTech Octopus v1.0 (STM32F446ZE) +#define BOARD_BTT_OCTOPUS_V1_1 4216 // BigTreeTech Octopus v1.1 (STM32F446ZE) +#define BOARD_BTT_OCTOPUS_PRO_V1_0 4217 // BigTreeTech Octopus Pro v1.0 (STM32F446ZE / STM32F429ZG) #define BOARD_LERDGE_K 4218 // Lerdge K (STM32F407ZG) #define BOARD_LERDGE_S 4219 // Lerdge S (STM32F407VE) #define BOARD_LERDGE_X 4220 // Lerdge X (STM32F407VE) -#define BOARD_VAKE403D 4221 // VAkE 403D (STM32F446VET6) -#define BOARD_FYSETC_S6 4222 // FYSETC S6 (STM32F446VET6) -#define BOARD_FYSETC_S6_V2_0 4223 // FYSETC S6 v2.0 (STM32F446VET6) -#define BOARD_FYSETC_SPIDER 4224 // FYSETC Spider (STM32F446VET6) +#define BOARD_VAKE403D 4221 // VAkE 403D (STM32F446VE) +#define BOARD_FYSETC_S6 4222 // FYSETC S6 (STM32F446VE) +#define BOARD_FYSETC_S6_V2_0 4223 // FYSETC S6 v2.0 (STM32F446VE) +#define BOARD_FYSETC_SPIDER 4224 // FYSETC Spider (STM32F446VE) #define BOARD_FLYF407ZG 4225 // FLYmaker FLYF407ZG (STM32F407ZG) #define BOARD_MKS_ROBIN2 4226 // MKS_ROBIN2 (STM32F407ZE) #define BOARD_MKS_ROBIN_PRO_V2 4227 // MKS Robin Pro V2 (STM32F407VE) #define BOARD_MKS_ROBIN_NANO_V3 4228 // MKS Robin Nano V3 (STM32F407VG) -#define BOARD_MKS_MONSTER8 4229 // MKS Monster8 (STM32F407VGT6) -#define BOARD_ANET_ET4 4230 // ANET ET4 V1.x (STM32F407VGT6) -#define BOARD_ANET_ET4P 4231 // ANET ET4P V1.x (STM32F407VGT6) -#define BOARD_FYSETC_CHEETAH_V20 4232 // FYSETC Cheetah V2.0 -#define BOARD_TH3D_EZBOARD_LITE_V2 4233 // TH3D EZBoard Lite v2.0 -#define BOARD_INDEX_REV03 4234 // Index PnP Controller REV03 (STM32F407VET6/VGT6) -#define BOARD_MKS_ROBIN_NANO_V1_3_F4 4235 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VET6) -#define BOARD_MKS_EAGLE 4236 // MKS Eagle (STM32F407VET6) +#define BOARD_MKS_ROBIN_NANO_V3_1 4229 // MKS Robin Nano V3.1 (STM32F407VE) +#define BOARD_MKS_MONSTER8_V1 4230 // MKS Monster8 V1 (STM32F407VE) +#define BOARD_MKS_MONSTER8_V2 4231 // MKS Monster8 V2 (STM32F407VE) +#define BOARD_ANET_ET4 4232 // ANET ET4 V1.x (STM32F407VG) +#define BOARD_ANET_ET4P 4233 // ANET ET4P V1.x (STM32F407VG) +#define BOARD_FYSETC_CHEETAH_V20 4234 // FYSETC Cheetah V2.0 (STM32F401RC) +#define BOARD_TH3D_EZBOARD_V2 4235 // TH3D EZBoard v2.0 (STM32F405RG) +#define BOARD_OPULO_LUMEN_REV3 4236 // Opulo Lumen PnP Controller REV3 (STM32F407VE / STM32F407VG) +#define BOARD_MKS_ROBIN_NANO_V1_3_F4 4237 // MKS Robin Nano V1.3 and MKS Robin Nano-S V1.3 (STM32F407VE) +#define BOARD_MKS_EAGLE 4238 // MKS Eagle (STM32F407VE) +#define BOARD_ARTILLERY_RUBY 4239 // Artillery Ruby (STM32F401RC) +#define BOARD_FYSETC_SPIDER_V2_2 4240 // FYSETC Spider V2.2 (STM32F446VE) +#define BOARD_CREALITY_V24S1_301F4 4241 // Creality v2.4.S1_301F4 (STM32F401RC) as found in the Ender-3 S1 F4 // // ARM Cortex M7 @@ -415,7 +431,10 @@ #define BOARD_TEENSY41 5001 // Teensy 4.1 #define BOARD_T41U5XBB 5002 // T41U5XBB Teensy 4.1 breakout board #define BOARD_NUCLEO_F767ZI 5003 // ST NUCLEO-F767ZI Dev Board -#define BOARD_BTT_SKR_SE_BX 5004 // BigTreeTech SKR SE BX (STM32H743II) +#define BOARD_BTT_SKR_SE_BX_V2 5004 // BigTreeTech SKR SE BX V2.0 (STM32H743II) +#define BOARD_BTT_SKR_SE_BX_V3 5005 // BigTreeTech SKR SE BX V3.0 (STM32H743II) +#define BOARD_BTT_SKR_V3_0 5006 // BigTreeTech SKR V3.0 (STM32H743VG) +#define BOARD_BTT_SKR_V3_0_EZ 5007 // BigTreeTech SKR V3.0 EZ (STM32H743VG) // // Espressif ESP32 WiFi @@ -425,15 +444,20 @@ #define BOARD_MRR_ESPA 6001 // MRR ESPA based on ESP32 (native pins only) #define BOARD_MRR_ESPE 6002 // MRR ESPE based on ESP32 (with I2S stepper stream) #define BOARD_E4D_BOX 6003 // E4d@BOX -#define BOARD_FYSETC_E4 6004 // FYSETC E4 -#define BOARD_PANDA_ZHU 6005 // Panda_ZHU -#define BOARD_PANDA_M4 6006 // Panda_M4 +#define BOARD_RESP32_CUSTOM 6004 // Rutilea ESP32 custom board +#define BOARD_FYSETC_E4 6005 // FYSETC E4 +#define BOARD_PANDA_ZHU 6006 // Panda_ZHU +#define BOARD_PANDA_M4 6007 // Panda_M4 +#define BOARD_MKS_TINYBEE 6008 // MKS TinyBee based on ESP32 (with I2S stepper stream) +#define BOARD_ENWI_ESPNP 6009 // enwi ESPNP based on ESP32 (with I2S stepper stream) // // SAMD51 ARM Cortex M4 // #define BOARD_AGCM4_RAMPS_144 6100 // RAMPS 1.4.4 +#define BOARD_BRICOLEMON_V1_0 6101 // Bricolemon +#define BOARD_BRICOLEMON_LITE_V1_0 6102 // Bricolemon Lite // // Custom board diff --git a/Marlin/src/core/debug_out.h b/Marlin/src/core/debug_out.h index 4e30a5306e9e6..eb1c91e507865 100644 --- a/Marlin/src/core/debug_out.h +++ b/Marlin/src/core/debug_out.h @@ -36,6 +36,8 @@ #undef DEBUG_ECHOLN #undef DEBUG_ECHOPGM #undef DEBUG_ECHOLNPGM +#undef DEBUG_ECHOF +#undef DEBUG_ECHOLNF #undef DEBUG_ECHOPGM_P #undef DEBUG_ECHOLNPGM_P #undef DEBUG_ECHOPAIR_F @@ -54,7 +56,7 @@ #if DEBUG_OUT #include "debug_section.h" - #define DEBUG_SECTION(N,S,D) SectionLog N(PSTR(S),D) + #define DEBUG_SECTION(N,S,D) SectionLog N(F(S),D) #define DEBUG_ECHO_START SERIAL_ECHO_START #define DEBUG_ERROR_START SERIAL_ERROR_START @@ -65,6 +67,8 @@ #define DEBUG_ECHOLN SERIAL_ECHOLN #define DEBUG_ECHOPGM SERIAL_ECHOPGM #define DEBUG_ECHOLNPGM SERIAL_ECHOLNPGM + #define DEBUG_ECHOF SERIAL_ECHOF + #define DEBUG_ECHOLNF SERIAL_ECHOLNF #define DEBUG_ECHOPGM SERIAL_ECHOPGM #define DEBUG_ECHOPGM_P SERIAL_ECHOPGM_P #define DEBUG_ECHOPAIR_F SERIAL_ECHOPAIR_F @@ -94,6 +98,8 @@ #define DEBUG_ECHOLN(...) NOOP #define DEBUG_ECHOPGM(...) NOOP #define DEBUG_ECHOLNPGM(...) NOOP + #define DEBUG_ECHOF(...) NOOP + #define DEBUG_ECHOLNF(...) NOOP #define DEBUG_ECHOPGM_P(...) NOOP #define DEBUG_ECHOLNPGM_P(...) NOOP #define DEBUG_ECHOPAIR_F(...) NOOP diff --git a/Marlin/src/core/debug_section.h b/Marlin/src/core/debug_section.h index ef1511e6f082a..6e23d9e4edb6c 100644 --- a/Marlin/src/core/debug_section.h +++ b/Marlin/src/core/debug_section.h @@ -26,22 +26,22 @@ class SectionLog { public: - SectionLog(PGM_P const msg=nullptr, bool inbug=true) { - the_msg = msg; - if ((debug = inbug)) echo_msg(PSTR(">>>")); + SectionLog(FSTR_P const fmsg=nullptr, bool inbug=true) { + the_msg = fmsg; + if ((debug = inbug)) echo_msg(F(">>>")); } - ~SectionLog() { if (debug) echo_msg(PSTR("<<<")); } + ~SectionLog() { if (debug) echo_msg(F("<<<")); } private: - PGM_P the_msg; + FSTR_P the_msg; bool debug; - void echo_msg(PGM_P const pre) { - SERIAL_ECHOPGM_P(pre); + void echo_msg(FSTR_P const fpre) { + SERIAL_ECHOF(fpre); if (the_msg) { SERIAL_CHAR(' '); - SERIAL_ECHOPGM_P(the_msg); + SERIAL_ECHOF(the_msg); } SERIAL_CHAR(' '); print_pos(current_position); diff --git a/Marlin/src/core/drivers.h b/Marlin/src/core/drivers.h index 0a76410274bb7..f6a8f0f126ac2 100644 --- a/Marlin/src/core/drivers.h +++ b/Marlin/src/core/drivers.h @@ -64,11 +64,11 @@ #define AXIS_DRIVER_TYPE_J(T) _AXIS_DRIVER_TYPE(J,T) #define AXIS_DRIVER_TYPE_K(T) _AXIS_DRIVER_TYPE(K,T) -#define AXIS_DRIVER_TYPE_X2(T) (EITHER(X_DUAL_STEPPER_DRIVERS, DUAL_X_CARRIAGE) && _AXIS_DRIVER_TYPE(X2,T)) -#define AXIS_DRIVER_TYPE_Y2(T) (ENABLED(Y_DUAL_STEPPER_DRIVERS) && _AXIS_DRIVER_TYPE(Y2,T)) -#define AXIS_DRIVER_TYPE_Z2(T) (NUM_Z_STEPPER_DRIVERS >= 2 && _AXIS_DRIVER_TYPE(Z2,T)) -#define AXIS_DRIVER_TYPE_Z3(T) (NUM_Z_STEPPER_DRIVERS >= 3 && _AXIS_DRIVER_TYPE(Z3,T)) -#define AXIS_DRIVER_TYPE_Z4(T) (NUM_Z_STEPPER_DRIVERS >= 4 && _AXIS_DRIVER_TYPE(Z4,T)) +#define AXIS_DRIVER_TYPE_X2(T) (HAS_X2_STEPPER && _AXIS_DRIVER_TYPE(X2,T)) +#define AXIS_DRIVER_TYPE_Y2(T) (HAS_DUAL_Y_STEPPERS && _AXIS_DRIVER_TYPE(Y2,T)) +#define AXIS_DRIVER_TYPE_Z2(T) (NUM_Z_STEPPERS >= 2 && _AXIS_DRIVER_TYPE(Z2,T)) +#define AXIS_DRIVER_TYPE_Z3(T) (NUM_Z_STEPPERS >= 3 && _AXIS_DRIVER_TYPE(Z3,T)) +#define AXIS_DRIVER_TYPE_Z4(T) (NUM_Z_STEPPERS >= 4 && _AXIS_DRIVER_TYPE(Z4,T)) #define AXIS_DRIVER_TYPE_E(N,T) (E_STEPPERS > N && _AXIS_DRIVER_TYPE(E##N,T)) #define AXIS_DRIVER_TYPE_E0(T) AXIS_DRIVER_TYPE_E(0,T) @@ -128,7 +128,7 @@ // 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,TMC26X) || 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) ) @@ -200,4 +200,4 @@ #define HAS_L64XX_NOT_L6474 1 #endif -#define AXIS_IS_L64XX(A) (AXIS_DRIVER_TYPE_##A(L6470) || AXIS_DRIVER_TYPE_##A(L6474) || AXIS_DRIVER_TYPE_##A(L6480) || AXIS_DRIVER_TYPE_##A(POWERSTEP01)) +#define AXIS_IS_L64XX(A) (AXIS_DRIVER_TYPE_##A(L6470) || AXIS_DRIVER_TYPE_##A(L6474) || AXIS_DRIVER_TYPE_##A(L6480) || AXIS_DRIVER_TYPE_##A(POWERSTEP01)) diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 03bffb8bd9ad4..f225f916aa184 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -105,6 +105,7 @@ #define STR_ENQUEUEING "enqueueing \"" #define STR_POWERUP "PowerUp" +#define STR_POWEROFF "PowerOff" #define STR_EXTERNAL_RESET " External Reset" #define STR_BROWNOUT_RESET " Brown out Reset" #define STR_WATCHDOG_RESET " Watchdog Reset" @@ -140,6 +141,7 @@ #define STR_RESEND "Resend: " #define STR_UNKNOWN_COMMAND "Unknown command: \"" #define STR_ACTIVE_EXTRUDER "Active Extruder: " +#define STR_ERR_FANSPEED "Fan speed E" #define STR_PROBE_OFFSET "Probe Offset" #define STR_SKEW_MIN "min_skew_factor: " @@ -197,16 +199,20 @@ #define STR_FILAMENT_CHANGE_INSERT_M108 "Insert filament and send M108" #define STR_FILAMENT_CHANGE_WAIT_M108 "Send M108 to resume" -#define STR_STOP_BLTOUCH "!! STOP called because of BLTouch error - restart with M999" -#define STR_STOP_UNHOMED "!! STOP called because of unhomed error - restart with M999" -#define STR_KILL_INACTIVE_TIME "!! KILL caused by too much inactive time - current command: " -#define STR_KILL_BUTTON "!! KILL caused by KILL button/pin" +#define STR_STOP_PRE "!! STOP called because of " +#define STR_STOP_POST " error - restart with M999" +#define STR_STOP_BLTOUCH "BLTouch" +#define STR_STOP_UNHOMED "unhomed" +#define STR_KILL_PRE "!! KILL caused by " +#define STR_KILL_INACTIVE_TIME "too much inactive time - current command: " +#define STR_KILL_BUTTON "KILL button/pin" // temperature.cpp strings -#define STR_PID_AUTOTUNE_START "PID Autotune start" -#define STR_PID_BAD_HEATER_ID "PID Autotune failed! Bad heater id" -#define STR_PID_TEMP_TOO_HIGH "PID Autotune failed! Temperature too high" -#define STR_PID_TIMEOUT "PID Autotune failed! timeout" +#define STR_PID_AUTOTUNE "PID Autotune" +#define STR_PID_AUTOTUNE_START " start" +#define STR_PID_BAD_HEATER_ID " failed! Bad heater id" +#define STR_PID_TEMP_TOO_HIGH " failed! Temperature too high" +#define STR_PID_TIMEOUT " failed! timeout" #define STR_BIAS " bias: " #define STR_D_COLON " d: " #define STR_T_MIN " min: " @@ -217,15 +223,19 @@ #define STR_KP " Kp: " #define STR_KI " Ki: " #define STR_KD " Kd: " -#define STR_PID_AUTOTUNE_FINISHED "PID Autotune finished! Put the last Kp, Ki and Kd constants from below into Configuration.h" +#define STR_PID_AUTOTUNE_FINISHED " finished! Put the last Kp, Ki and Kd constants from below into Configuration.h" #define STR_PID_DEBUG " PID_DEBUG " #define STR_PID_DEBUG_INPUT ": Input " #define STR_PID_DEBUG_OUTPUT " Output " -#define STR_PID_DEBUG_PTERM " pTerm " -#define STR_PID_DEBUG_ITERM " iTerm " -#define STR_PID_DEBUG_DTERM " dTerm " -#define STR_PID_DEBUG_CTERM " cTerm " #define STR_INVALID_EXTRUDER_NUM " - Invalid extruder number !" +#define STR_MPC_AUTOTUNE "MPC Autotune" +#define STR_MPC_AUTOTUNE_START " start for " STR_E +#define STR_MPC_AUTOTUNE_INTERRUPTED " interrupted!" +#define STR_MPC_AUTOTUNE_FINISHED " finished! Put the constants below into Configuration.h" +#define STR_MPC_COOLING_TO_AMBIENT "Cooling to ambient" +#define STR_MPC_HEATING_PAST_200 "Heating to over 200C" +#define STR_MPC_MEASURING_AMBIENT "Measuring ambient heatloss at " +#define STR_MPC_TEMPERATURE_ERROR "Temperature error" #define STR_HEATER_BED "bed" #define STR_HEATER_CHAMBER "chamber" @@ -239,6 +249,7 @@ #define STR_REDUNDANCY "Heater switched off. Temperature difference between temp sensors is too high !" #define STR_T_HEATING_FAILED "Heating failed" #define STR_T_THERMAL_RUNAWAY "Thermal Runaway" +#define STR_T_MALFUNCTION "Thermal Malfunction" #define STR_T_MAXTEMP "MAXTEMP triggered" #define STR_T_MINTEMP "MINTEMP triggered" #define STR_ERR_PROBING_FAILED "Probing Failed" @@ -252,7 +263,7 @@ #define STR_DEBUG_ERRORS "ERRORS" #define STR_DEBUG_DRYRUN "DRYRUN" #define STR_DEBUG_COMMUNICATION "COMMUNICATION" -#define STR_DEBUG_LEVELING "LEVELING" +#define STR_DEBUG_DETAIL "DETAIL" #define STR_PRINTER_LOCKED "Printer locked! (Unlock with M511 or LCD)" #define STR_WRONG_PASSWORD "Incorrect Password" @@ -300,10 +311,12 @@ #define STR_MATERIAL_HEATUP "Material heatup parameters" #define STR_LCD_CONTRAST "LCD Contrast" #define STR_LCD_BRIGHTNESS "LCD Brightness" +#define STR_DISPLAY_SLEEP "Display Sleep" #define STR_UI_LANGUAGE "UI Language" #define STR_Z_PROBE_OFFSET "Z-Probe Offset" #define STR_TEMPERATURE_UNITS "Temperature Units" #define STR_USER_THERMISTORS "User thermistors" +#define STR_DELAYED_POWEROFF "Delayed poweroff" // // Endstop Names used by Endstops::report_states @@ -339,9 +352,6 @@ #define STR_X "X" #define STR_Y "Y" #define STR_Z "Z" -#define STR_I AXIS4_STR -#define STR_J AXIS5_STR -#define STR_K AXIS6_STR #define STR_E "E" #if IS_KINEMATIC #define STR_A "A" @@ -358,115 +368,89 @@ #define STR_Z3 "Z3" #define STR_Z4 "Z4" -#define LCD_STR_A STR_A -#define LCD_STR_B STR_B -#define LCD_STR_C STR_C -#define LCD_STR_I STR_I -#define LCD_STR_J STR_J -#define LCD_STR_K STR_K -#define LCD_STR_E STR_E - // Extra Axis and Endstop Names -#if LINEAR_AXES >= 4 +#if HAS_I_AXIS #if AXIS4_NAME == 'A' - #define AXIS4_STR "A" + #define STR_I "A" #define STR_I_MIN "a_min" #define STR_I_MAX "a_max" #elif AXIS4_NAME == 'B' - #define AXIS4_STR "B" + #define STR_I "B" #define STR_I_MIN "b_min" #define STR_I_MAX "b_max" #elif AXIS4_NAME == 'C' - #define AXIS4_STR "C" + #define STR_I "C" #define STR_I_MIN "c_min" #define STR_I_MAX "c_max" #elif AXIS4_NAME == 'U' - #define AXIS4_STR "U" + #define STR_I "U" #define STR_I_MIN "u_min" #define STR_I_MAX "u_max" #elif AXIS4_NAME == 'V' - #define AXIS4_STR "V" + #define STR_I "V" #define STR_I_MIN "v_min" #define STR_I_MAX "v_max" #elif AXIS4_NAME == 'W' - #define AXIS4_STR "W" + #define STR_I "W" #define STR_I_MIN "w_min" #define STR_I_MAX "w_max" #else - #define AXIS4_STR "A" - #define STR_I_MIN "a_min" - #define STR_I_MAX "a_max" + #error "AXIS4_NAME can only be one of 'A', 'B', 'C', 'U', 'V', or 'W'." #endif #else - #define AXIS4_STR "" + #define STR_I "" #endif -#if LINEAR_AXES >= 5 - #if AXIS5_NAME == 'A' - #define AXIS5_STR "A" - #define STR_J_MIN "a_min" - #define STR_J_MAX "a_max" - #elif AXIS5_NAME == 'B' - #define AXIS5_STR "B" +#if HAS_J_AXIS + #if AXIS5_NAME == 'B' + #define STR_J "B" #define STR_J_MIN "b_min" #define STR_J_MAX "b_max" #elif AXIS5_NAME == 'C' - #define AXIS5_STR "C" + #define STR_J "C" #define STR_J_MIN "c_min" #define STR_J_MAX "c_max" #elif AXIS5_NAME == 'U' - #define AXIS5_STR "U" + #define STR_J "U" #define STR_J_MIN "u_min" #define STR_J_MAX "u_max" #elif AXIS5_NAME == 'V' - #define AXIS5_STR "V" + #define STR_J "V" #define STR_J_MIN "v_min" #define STR_J_MAX "v_max" #elif AXIS5_NAME == 'W' - #define AXIS5_STR "W" + #define STR_J "W" #define STR_J_MIN "w_min" #define STR_J_MAX "w_max" #else - #define AXIS5_STR "B" - #define STR_J_MIN "b_min" - #define STR_J_MAX "b_max" + #error "AXIS5_NAME can only be one of 'B', 'C', 'U', 'V', or 'W'." #endif #else - #define AXIS5_STR "" + #define STR_J "" #endif -#if LINEAR_AXES >= 6 - #if AXIS6_NAME == 'A' - #define AXIS6_STR "A" - #define STR_K_MIN "a_min" - #define STR_K_MAX "a_max" - #elif AXIS6_NAME == 'B' - #define AXIS6_STR "B" - #define STR_K_MIN "b_min" - #define STR_K_MAX "b_max" - #elif AXIS6_NAME == 'C' - #define AXIS6_STR "C" +#if HAS_K_AXIS + #if AXIS6_NAME == 'C' + #define STR_K "C" #define STR_K_MIN "c_min" #define STR_K_MAX "c_max" #elif AXIS6_NAME == 'U' - #define AXIS6_STR "U" + #define STR_K "U" #define STR_K_MIN "u_min" #define STR_K_MAX "u_max" #elif AXIS6_NAME == 'V' - #define AXIS6_STR "V" + #define STR_K "V" #define STR_K_MIN "v_min" #define STR_K_MAX "v_max" #elif AXIS6_NAME == 'W' - #define AXIS6_STR "W" + #define STR_K "W" #define STR_K_MIN "w_min" #define STR_K_MAX "w_max" #else - #define AXIS6_STR "C" - #define STR_K_MIN "c_min" - #define STR_K_MAX "c_max" + #error "AXIS6_NAME can only be one of 'C', 'U', 'V', or 'W'." #endif #else - #define AXIS6_STR "" + #define STR_K "" #endif #if EITHER(HAS_MARLINUI_HD44780, IS_TFTGLCD_PANEL) @@ -517,34 +501,34 @@ */ #if ENABLED(NUMBER_TOOLS_FROM_0) #define LCD_FIRST_TOOL 0 - #define LCD_STR_N0 "0" - #define LCD_STR_N1 "1" - #define LCD_STR_N2 "2" - #define LCD_STR_N3 "3" - #define LCD_STR_N4 "4" - #define LCD_STR_N5 "5" - #define LCD_STR_N6 "6" - #define LCD_STR_N7 "7" + #define STR_N0 "0" + #define STR_N1 "1" + #define STR_N2 "2" + #define STR_N3 "3" + #define STR_N4 "4" + #define STR_N5 "5" + #define STR_N6 "6" + #define STR_N7 "7" #else #define LCD_FIRST_TOOL 1 - #define LCD_STR_N0 "1" - #define LCD_STR_N1 "2" - #define LCD_STR_N2 "3" - #define LCD_STR_N3 "4" - #define LCD_STR_N4 "5" - #define LCD_STR_N5 "6" - #define LCD_STR_N6 "7" - #define LCD_STR_N7 "8" + #define STR_N0 "1" + #define STR_N1 "2" + #define STR_N2 "3" + #define STR_N3 "4" + #define STR_N4 "5" + #define STR_N5 "6" + #define STR_N6 "7" + #define STR_N7 "8" #endif -#define LCD_STR_E0 "E" LCD_STR_N0 -#define LCD_STR_E1 "E" LCD_STR_N1 -#define LCD_STR_E2 "E" LCD_STR_N2 -#define LCD_STR_E3 "E" LCD_STR_N3 -#define LCD_STR_E4 "E" LCD_STR_N4 -#define LCD_STR_E5 "E" LCD_STR_N5 -#define LCD_STR_E6 "E" LCD_STR_N6 -#define LCD_STR_E7 "E" LCD_STR_N7 +#define STR_E0 STR_E STR_N0 +#define STR_E1 STR_E STR_N1 +#define STR_E2 STR_E STR_N2 +#define STR_E3 STR_E STR_N3 +#define STR_E4 STR_E STR_N4 +#define STR_E5 STR_E STR_N5 +#define STR_E6 STR_E STR_N6 +#define STR_E7 STR_E STR_N7 // Include localized LCD Menu Messages diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 22c3767e1dc88..ad06763d457d0 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -33,6 +33,12 @@ #define _AXIS(A) (A##_AXIS) +#define _XSTOP_ 0x01 +#define _YSTOP_ 0x02 +#define _ZSTOP_ 0x03 +#define _ISTOP_ 0x04 +#define _JSTOP_ 0x05 +#define _KSTOP_ 0x06 #define _XMIN_ 0x11 #define _YMIN_ 0x12 #define _ZMIN_ 0x13 @@ -64,11 +70,11 @@ #define FORCE_INLINE __attribute__((always_inline)) inline #define NO_INLINE __attribute__((noinline)) #define _UNUSED __attribute__((unused)) -#define _O0 __attribute__((optimize("O0"))) -#define _Os __attribute__((optimize("Os"))) -#define _O1 __attribute__((optimize("O1"))) -#define _O2 __attribute__((optimize("O2"))) -#define _O3 __attribute__((optimize("O3"))) +#define __O0 __attribute__((optimize("O0"))) +#define __Os __attribute__((optimize("Os"))) +#define __O1 __attribute__((optimize("O1"))) +#define __O2 __attribute__((optimize("O2"))) +#define __O3 __attribute__((optimize("O3"))) #define IS_CONSTEXPR(...) __builtin_constant_p(__VA_ARGS__) // Only valid solution with C++14. Should use std::is_constant_evaluated() in C++20 instead @@ -125,13 +131,13 @@ #ifdef __cplusplus // C++11 solution that is standards compliant. - template static inline constexpr void NOLESS(V& v, const N n) { + template static constexpr void NOLESS(V& v, const N n) { if (n > v) v = n; } - template static inline constexpr void NOMORE(V& v, const N n) { + template static constexpr void NOMORE(V& v, const N n) { if (n < v) v = n; } - template static inline constexpr void LIMIT(V& v, const N1 n1, const N2 n2) { + template static constexpr void LIMIT(V& v, const N1 n1, const N2 n2) { if (n1 > v) v = n1; else if (n2 < v) v = n2; } @@ -160,7 +166,7 @@ #endif -// Macros to chain up to 14 conditions +// Macros to chain up to 40 conditions #define _DO_1(W,C,A) (_##W##_1(A)) #define _DO_2(W,C,A,B) (_##W##_1(A) C _##W##_1(B)) #define _DO_3(W,C,A,V...) (_##W##_1(A) C _DO_2(W,C,V)) @@ -176,6 +182,31 @@ #define _DO_13(W,C,A,V...) (_##W##_1(A) C _DO_12(W,C,V)) #define _DO_14(W,C,A,V...) (_##W##_1(A) C _DO_13(W,C,V)) #define _DO_15(W,C,A,V...) (_##W##_1(A) C _DO_14(W,C,V)) +#define _DO_16(W,C,A,V...) (_##W##_1(A) C _DO_15(W,C,V)) +#define _DO_17(W,C,A,V...) (_##W##_1(A) C _DO_16(W,C,V)) +#define _DO_18(W,C,A,V...) (_##W##_1(A) C _DO_17(W,C,V)) +#define _DO_19(W,C,A,V...) (_##W##_1(A) C _DO_18(W,C,V)) +#define _DO_20(W,C,A,V...) (_##W##_1(A) C _DO_19(W,C,V)) +#define _DO_21(W,C,A,V...) (_##W##_1(A) C _DO_20(W,C,V)) +#define _DO_22(W,C,A,V...) (_##W##_1(A) C _DO_21(W,C,V)) +#define _DO_23(W,C,A,V...) (_##W##_1(A) C _DO_22(W,C,V)) +#define _DO_24(W,C,A,V...) (_##W##_1(A) C _DO_23(W,C,V)) +#define _DO_25(W,C,A,V...) (_##W##_1(A) C _DO_24(W,C,V)) +#define _DO_26(W,C,A,V...) (_##W##_1(A) C _DO_25(W,C,V)) +#define _DO_27(W,C,A,V...) (_##W##_1(A) C _DO_26(W,C,V)) +#define _DO_28(W,C,A,V...) (_##W##_1(A) C _DO_27(W,C,V)) +#define _DO_29(W,C,A,V...) (_##W##_1(A) C _DO_28(W,C,V)) +#define _DO_30(W,C,A,V...) (_##W##_1(A) C _DO_29(W,C,V)) +#define _DO_31(W,C,A,V...) (_##W##_1(A) C _DO_30(W,C,V)) +#define _DO_32(W,C,A,V...) (_##W##_1(A) C _DO_31(W,C,V)) +#define _DO_33(W,C,A,V...) (_##W##_1(A) C _DO_32(W,C,V)) +#define _DO_34(W,C,A,V...) (_##W##_1(A) C _DO_33(W,C,V)) +#define _DO_35(W,C,A,V...) (_##W##_1(A) C _DO_34(W,C,V)) +#define _DO_36(W,C,A,V...) (_##W##_1(A) C _DO_35(W,C,V)) +#define _DO_37(W,C,A,V...) (_##W##_1(A) C _DO_36(W,C,V)) +#define _DO_38(W,C,A,V...) (_##W##_1(A) C _DO_37(W,C,V)) +#define _DO_39(W,C,A,V...) (_##W##_1(A) C _DO_38(W,C,V)) +#define _DO_40(W,C,A,V...) (_##W##_1(A) C _DO_39(W,C,V)) #define __DO_N(W,C,N,V...) _DO_##N(W,C,V) #define _DO_N(W,C,N,V...) __DO_N(W,C,N,V) #define DO(W,C,V...) (_DO_N(W,C,NUM_ARGS(V),V)) @@ -204,6 +235,8 @@ #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 _OPTITEM(A...) A, +#define OPTITEM(O,A...) TERN_(O,DEFER4(_OPTITEM)(A)) #define _OPTARG(A...) , A #define OPTARG(O,A...) TERN_(O,DEFER4(_OPTARG)(A)) #define _OPTCODE(A) A; @@ -245,12 +278,17 @@ #define NUMERIC_SIGNED(a) (NUMERIC(a) || (a) == '-' || (a) == '+') #define DECIMAL_SIGNED(a) (DECIMAL(a) || (a) == '-' || (a) == '+') #define COUNT(a) (sizeof(a)/sizeof(*a)) -#define ZERO(a) memset(a,0,sizeof(a)) +#define ZERO(a) memset((void*)a,0,sizeof(a)) #define COPY(a,b) do{ \ static_assert(sizeof(a[0]) == sizeof(b[0]), "COPY: '" STRINGIFY(a) "' and '" STRINGIFY(b) "' types (sizes) don't match!"); \ memcpy(&a[0],&b[0],_MIN(sizeof(a),sizeof(b))); \ }while(0) +#define CODE_16( A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N; O; P +#define CODE_15( A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N; O +#define CODE_14( A,B,C,D,E,F,G,H,I,J,K,L,M,N,...) A; B; C; D; E; F; G; H; I; J; K; L; M; N +#define CODE_13( A,B,C,D,E,F,G,H,I,J,K,L,M,...) A; B; C; D; E; F; G; H; I; J; K; L; M +#define CODE_12( A,B,C,D,E,F,G,H,I,J,K,L,...) A; B; C; D; E; F; G; H; I; J; K; L #define CODE_11( A,B,C,D,E,F,G,H,I,J,K,...) A; B; C; D; E; F; G; H; I; J; K #define CODE_10( A,B,C,D,E,F,G,H,I,J,...) A; B; C; D; E; F; G; H; I; J #define CODE_9( A,B,C,D,E,F,G,H,I,...) A; B; C; D; E; F; G; H; I @@ -312,7 +350,7 @@ #define _LIST_N(N,V...) LIST_##N(V) #define LIST_N(N,V...) _LIST_N(N,V) -#define LIST_N_1(N,K) _LIST_N(N,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K) +#define LIST_N_1(N,K) _LIST_N(N,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K,K) #define ARRAY_N(N,V...) { _LIST_N(N,V) } #define ARRAY_N_1(N,K) { LIST_N_1(N,K) } @@ -330,7 +368,7 @@ #undef ABS #ifdef __cplusplus - template static inline constexpr const T ABS(const T v) { return v >= 0 ? v : -v; } + template static constexpr const T ABS(const T v) { return v >= 0 ? v : -v; } #else #define ABS(a) ({__typeof__(a) _a = (a); _a >= 0 ? _a : -_a;}) #endif @@ -373,14 +411,14 @@ extern "C++" { // C++11 solution that is standards compliant. Return type is deduced automatically - template static inline constexpr auto _MIN(const L lhs, const R rhs) -> decltype(lhs + rhs) { + template static constexpr auto _MIN(const L lhs, const R rhs) -> decltype(lhs + rhs) { return lhs < rhs ? lhs : rhs; } - template static inline constexpr auto _MAX(const L lhs, const R rhs) -> decltype(lhs + rhs) { + template static constexpr auto _MAX(const L lhs, const R rhs) -> decltype(lhs + rhs) { return lhs > rhs ? lhs : rhs; } - template static inline constexpr const T _MIN(T V, Ts... Vs) { return _MIN(V, _MIN(Vs...)); } - template static inline constexpr const T _MAX(T V, Ts... Vs) { return _MAX(V, _MAX(Vs...)); } + template static constexpr const T _MIN(T V, Ts... Vs) { return _MIN(V, _MIN(Vs...)); } + template static constexpr const T _MAX(T V, Ts... Vs) { return _MAX(V, _MAX(Vs...)); } } @@ -594,8 +632,8 @@ #define IS_PROBE(V...) SECOND(V, 0) // Get the second item passed, or 0 #define PROBE() ~, 1 // Second item will be 1 if this is passed #define _NOT_0 PROBE() -#define NOT(x) IS_PROBE(_CAT(_NOT_, x)) // NOT('0') gets '1'. Anything else gets '0'. -#define _BOOL(x) NOT(NOT(x)) // NOT('0') gets '0'. Anything else gets '1'. +#define NOT(x) IS_PROBE(_CAT(_NOT_, x)) // NOT('0') gets '1'. Anything else gets '0'. +#define _BOOL(x) NOT(NOT(x)) // _BOOL('0') gets '0'. Anything else gets '1'. #define IF_ELSE(TF) _IF_ELSE(_BOOL(TF)) #define _IF_ELSE(TF) _CAT(_IF_, TF) @@ -609,7 +647,6 @@ #define HAS_ARGS(V...) _BOOL(FIRST(_END_OF_ARGUMENTS_ V)()) #define _END_OF_ARGUMENTS_() 0 - // Simple Inline IF Macros, friendly to use in other macro definitions #define IF(O, A, B) ((O) ? (A) : (B)) #define IF_0(O, A) IF(O, A, 0) @@ -662,13 +699,22 @@ #define RREPEAT2_S(S,N,OP,V...) EVAL1024(_RREPEAT2(S,SUB##S(N),OP,V)) #define RREPEAT2(N,OP,V...) RREPEAT2_S(0,N,OP,V) -// See https://github.com/swansontec/map-macro -#define MAP_OUT -#define MAP_END(...) -#define MAP_GET_END() 0, MAP_END -#define MAP_NEXT0(test, next, ...) next MAP_OUT -#define MAP_NEXT1(test, next) MAP_NEXT0 (test, next, 0) -#define MAP_NEXT(test, next) MAP_NEXT1 (MAP_GET_END test, next) -#define MAP0(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP1) (f, peek, __VA_ARGS__) -#define MAP1(f, x, peek, ...) f(x) MAP_NEXT (peek, MAP0) (f, peek, __VA_ARGS__) -#define MAP(f, ...) EVAL512 (MAP1 (f, __VA_ARGS__, (), 0)) +// Call OP(A) with each item as an argument +#define _MAP(_MAP_OP,A,V...) \ + _MAP_OP(A) \ + IF_ELSE(HAS_ARGS(V)) \ + ( DEFER2(__MAP)()(_MAP_OP,V) ) \ + ( /* Do nothing */ ) +#define __MAP() _MAP + +#define MAP(OP,V...) EVAL(_MAP(OP,V)) + +// Emit a list of OP(A) with the given items +#define _MAPLIST(_MAP_OP,A,V...) \ + _MAP_OP(A) \ + IF_ELSE(HAS_ARGS(V)) \ + ( , DEFER2(__MAPLIST)()(_MAP_OP,V) ) \ + ( /* Do nothing */ ) +#define __MAPLIST() _MAPLIST + +#define MAPLIST(OP,V...) EVAL(_MAPLIST(OP,V)) diff --git a/Marlin/src/core/multi_language.h b/Marlin/src/core/multi_language.h index 2106f946ac734..05a713e435dee 100644 --- a/Marlin/src/core/multi_language.h +++ b/Marlin/src/core/multi_language.h @@ -45,6 +45,7 @@ typedef const char Language_Str[]; // Set unused languages equal to each other so the // compiler can optimize away the conditionals. +#define LCD_LANGUAGE_1 LCD_LANGUAGE #ifndef LCD_LANGUAGE_2 #define LCD_LANGUAGE_2 LCD_LANGUAGE #endif @@ -80,6 +81,9 @@ typedef const char Language_Str[]; #endif #define GET_TEXT_F(MSG) FPSTR(GET_TEXT(MSG)) +#define GET_EN_TEXT(MSG) GET_LANG(en)::MSG +#define GET_EN_TEXT_F(MSG) FPSTR(GET_EN_TEXT(MSG)) + #define GET_LANGUAGE_NAME(INDEX) GET_LANG(LCD_LANGUAGE_##INDEX)::LANGUAGE #define LANG_CHARSIZE GET_TEXT(CHARSIZE) #define USE_WIDE_GLYPH (LANG_CHARSIZE > 2) diff --git a/Marlin/src/core/serial.cpp b/Marlin/src/core/serial.cpp index 8c9f4a8e4d160..990c892e64a1b 100644 --- a/Marlin/src/core/serial.cpp +++ b/Marlin/src/core/serial.cpp @@ -30,16 +30,15 @@ uint8_t marlin_debug_flags = MARLIN_DEBUG_NONE; // Commonly-used strings in serial output -PGMSTR(NUL_STR, ""); PGMSTR(SP_P_STR, " P"); PGMSTR(SP_T_STR, " T"); -PGMSTR(X_STR, "X"); PGMSTR(Y_STR, "Y"); PGMSTR(Z_STR, "Z"); PGMSTR(E_STR, "E"); -PGMSTR(X_LBL, "X:"); PGMSTR(Y_LBL, "Y:"); PGMSTR(Z_LBL, "Z:"); PGMSTR(E_LBL, "E:"); -PGMSTR(SP_A_STR, " A"); PGMSTR(SP_B_STR, " B"); PGMSTR(SP_C_STR, " C"); -PGMSTR(SP_X_STR, " X"); PGMSTR(SP_Y_STR, " Y"); PGMSTR(SP_Z_STR, " Z"); PGMSTR(SP_E_STR, " E"); -PGMSTR(SP_X_LBL, " X:"); PGMSTR(SP_Y_LBL, " Y:"); PGMSTR(SP_Z_LBL, " Z:"); PGMSTR(SP_E_LBL, " E:"); -PGMSTR(I_STR, AXIS4_STR); PGMSTR(J_STR, AXIS5_STR); PGMSTR(K_STR, AXIS6_STR); -PGMSTR(I_LBL, AXIS4_STR ":"); PGMSTR(J_LBL, AXIS5_STR ":"); PGMSTR(K_LBL, AXIS6_STR ":"); -PGMSTR(SP_I_STR, " " AXIS4_STR); PGMSTR(SP_J_STR, " " AXIS5_STR); PGMSTR(SP_K_STR, " " AXIS6_STR); -PGMSTR(SP_I_LBL, " " AXIS4_STR ":"); PGMSTR(SP_J_LBL, " " AXIS5_STR ":"); PGMSTR(SP_K_LBL, " " AXIS6_STR ":"); +PGMSTR(SP_A_STR, " A"); PGMSTR(SP_B_STR, " B"); PGMSTR(SP_C_STR, " C"); +PGMSTR(SP_P_STR, " P"); PGMSTR(SP_T_STR, " T"); PGMSTR(NUL_STR, ""); + +#define _N_STR(N) PGMSTR(N##_STR, STR_##N); +#define _N_LBL(N) PGMSTR(N##_LBL, STR_##N ":"); +#define _SP_N_STR(N) PGMSTR(SP_##N##_STR, " " STR_##N); +#define _SP_N_LBL(N) PGMSTR(SP_##N##_LBL, " " STR_##N ":"); +MAP(_N_STR, LOGICAL_AXIS_NAMES); MAP(_SP_N_STR, LOGICAL_AXIS_NAMES); +MAP(_N_LBL, LOGICAL_AXIS_NAMES); MAP(_SP_N_LBL, LOGICAL_AXIS_NAMES); // Hook Meatpack if it's enabled on the first leaf #if ENABLED(MEATPACK_ON_SERIAL_PORT_1) @@ -69,23 +68,31 @@ PGMSTR(SP_I_LBL, " " AXIS4_STR ":"); PGMSTR(SP_J_LBL, " " AXIS5_STR ":"); PGMSTR #endif -void serialprintPGM(PGM_P str) { +void serial_print_P(PGM_P str) { while (const char c = pgm_read_byte(str++)) SERIAL_CHAR(c); } -void serial_echo_start() { static PGMSTR(echomagic, "echo:"); serialprintPGM(echomagic); } -void serial_error_start() { static PGMSTR(errormagic, "Error:"); serialprintPGM(errormagic); } +void serial_echo_start() { serial_print(F("echo:")); } +void serial_error_start() { serial_print(F("Error:")); } void serial_spaces(uint8_t count) { count *= (PROPORTIONAL_FONT_RATIO); while (count--) SERIAL_CHAR(' '); } -void serial_ternary(const bool onoff, PGM_P const pre, PGM_P const on, PGM_P const off, PGM_P const post/*=nullptr*/) { - if (pre) serialprintPGM(pre); - serialprintPGM(onoff ? on : off); - if (post) serialprintPGM(post); +void serial_offset(const_float_t v, const uint8_t sp/*=0*/) { + if (v == 0 && sp == 1) + SERIAL_CHAR(' '); + else if (v > 0 || (v == 0 && sp == 2)) + SERIAL_CHAR('+'); + SERIAL_DECIMAL(v); +} + +void serial_ternary(const bool onoff, FSTR_P const pre, FSTR_P const on, FSTR_P const off, FSTR_P const post/*=nullptr*/) { + if (pre) serial_print(pre); + serial_print(onoff ? on : off); + if (post) serial_print(post); } -void serialprint_onoff(const bool onoff) { serialprintPGM(onoff ? PSTR(STR_ON) : PSTR(STR_OFF)); } +void serialprint_onoff(const bool onoff) { serial_print(onoff ? F(STR_ON) : F(STR_OFF)); } void serialprintln_onoff(const bool onoff) { serialprint_onoff(onoff); SERIAL_EOL(); } -void serialprint_truefalse(const bool tf) { serialprintPGM(tf ? PSTR("true") : PSTR("false")); } +void serialprint_truefalse(const bool tf) { serial_print(tf ? F("true") : F("false")); } void print_bin(uint16_t val) { for (uint8_t i = 16; i--;) { @@ -94,10 +101,10 @@ void print_bin(uint16_t val) { } } -void print_pos(LINEAR_AXIS_ARGS(const_float_t), PGM_P const prefix/*=nullptr*/, PGM_P const suffix/*=nullptr*/) { - if (prefix) serialprintPGM(prefix); +void print_pos(NUM_AXIS_ARGS(const_float_t), FSTR_P const prefix/*=nullptr*/, FSTR_P const suffix/*=nullptr*/) { + if (prefix) serial_print(prefix); SERIAL_ECHOPGM_P( - LIST_N(DOUBLE(LINEAR_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) + 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) ); - if (suffix) serialprintPGM(suffix); else SERIAL_EOL(); + if (suffix) serial_print(suffix); else SERIAL_EOL(); } diff --git a/Marlin/src/core/serial.h b/Marlin/src/core/serial.h index 05d80a48298d9..c19bc087833dd 100644 --- a/Marlin/src/core/serial.h +++ b/Marlin/src/core/serial.h @@ -28,19 +28,6 @@ #include "../feature/meatpack.h" #endif -// Commonly-used strings in serial output -extern const char NUL_STR[], - SP_X_STR[], SP_Y_STR[], SP_Z_STR[], - SP_A_STR[], SP_B_STR[], SP_C_STR[], SP_E_STR[], - SP_X_LBL[], SP_Y_LBL[], SP_Z_LBL[], SP_E_LBL[], - SP_I_STR[], SP_J_STR[], SP_K_STR[], - SP_I_LBL[], SP_J_LBL[], SP_K_LBL[], - SP_P_STR[], SP_T_STR[], - X_STR[], Y_STR[], Z_STR[], E_STR[], - I_STR[], J_STR[], K_STR[], - X_LBL[], Y_LBL[], Z_LBL[], E_LBL[], - I_LBL[], J_LBL[], K_LBL[]; - // // Debugging flags for use by M111 // @@ -87,7 +74,7 @@ extern uint8_t marlin_debug_flags; // interface with the ability to output to multiple serial ports. #if HAS_MULTI_SERIAL #define _PORT_REDIRECT(n,p) REMEMBER(n,multiSerial.portMask,p) - #define _PORT_RESTORE(n,p) RESTORE(n) + #define _PORT_RESTORE(n) RESTORE(n) #define SERIAL_ASSERT(P) if (multiSerial.portMask!=(P)) { debugger(); } // If we have a catchall, use that directly #ifdef SERIAL_CATCHALL @@ -167,13 +154,10 @@ inline void SERIAL_ECHO(serial_char_t x) { SERIAL_IMPL.write(x.c); } #define AS_CHAR(C) serial_char_t(C) #define AS_DIGIT(C) AS_CHAR('0' + (C)) -// SERIAL_ECHO_F prints a floating point value with optional precision -inline void SERIAL_ECHO_F(EnsureDouble x, int digit=2) { SERIAL_IMPL.print(x, digit); } - template void SERIAL_ECHOLN(T x) { SERIAL_IMPL.println(x); } -// SERIAL_PRINT works like SERIAL_ECHO but allow to specify the encoding base of the number printed +// SERIAL_PRINT works like SERIAL_ECHO but also takes the numeric base template void SERIAL_PRINT(T x, U y) { SERIAL_IMPL.print(x, y); } @@ -184,8 +168,20 @@ void SERIAL_PRINTLN(T x, PrintBase y) { SERIAL_IMPL.println(x, y); } inline void SERIAL_FLUSH() { SERIAL_IMPL.flush(); } inline void SERIAL_FLUSHTX() { SERIAL_IMPL.flushTX(); } -// Print a single PROGMEM string to serial -void serialprintPGM(PGM_P str); +// Serial echo and error prefixes +#define SERIAL_ECHO_START() serial_echo_start() +#define SERIAL_ERROR_START() serial_error_start() + +// Serial end-of-line +#define SERIAL_EOL() SERIAL_CHAR('\n') + +// Print a single PROGMEM, PGM_P, or PSTR() string. +void serial_print_P(PGM_P str); +inline void serial_println_P(PGM_P str) { serial_print_P(str); SERIAL_EOL(); } + +// Print a single FSTR_P, F(), or FPSTR() string. +inline void serial_print(FSTR_P const fstr) { serial_print_P(FTOP(fstr)); } +inline void serial_println(FSTR_P const fstr) { serial_println_P(FTOP(fstr)); } // // SERIAL_ECHOPGM... macros are used to output string-value pairs. @@ -195,8 +191,8 @@ void serialprintPGM(PGM_P str); #define __SEP_N(N,V...) _SEP_##N(V) #define _SEP_N(N,V...) __SEP_N(N,V) #define _SEP_N_REF() _SEP_N -#define _SEP_1(s) serialprintPGM(PSTR(s)); -#define _SEP_2(s,v) serial_echopair_PGM(PSTR(s),v); +#define _SEP_1(s) serial_print(F(s)); +#define _SEP_2(s,v) serial_echopair(F(s),v); #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) @@ -204,8 +200,8 @@ void serialprintPGM(PGM_P str); #define __SELP_N(N,V...) _SELP_##N(V) #define _SELP_N(N,V...) __SELP_N(N,V) #define _SELP_N_REF() _SELP_N -#define _SELP_1(s) serialprintPGM(PSTR(s "\n")); -#define _SELP_2(s,v) serial_echopair_PGM(PSTR(s),v); SERIAL_EOL(); +#define _SELP_1(s) serial_print(F(s "\n")); +#define _SELP_2(s,v) serial_echolnpair(F(s),v); #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) @@ -213,8 +209,8 @@ void serialprintPGM(PGM_P str); #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 -#define _SEP_1_P(p) serialprintPGM(p); -#define _SEP_2_P(p,v) serial_echopair_PGM(p,v); +#define _SEP_1_P(p) serial_print_P(p); +#define _SEP_2_P(p,v) serial_echopair_P(p,v); #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) @@ -222,11 +218,29 @@ void serialprintPGM(PGM_P str); #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 -#define _SELP_1_P(p) { serialprintPGM(p); SERIAL_EOL(); } -#define _SELP_2_P(p,v) { serial_echopair_PGM(p,v); SERIAL_EOL(); } +#define _SELP_1_P(p) serial_println_P(p) +#define _SELP_2_P(p,v) serial_echolnpair_P(p,v) #define _SELP_3_P(p,v,V...) { _SEP_2_P(p,v); DEFER2(_SELP_N_P_REF)()(TWO_ARGS(V),V); } #define SERIAL_ECHOLNPGM_P(V...) do{ EVAL(_SELP_N_P(TWO_ARGS(V),V)); }while(0) +// Print up to 20 pairs of values. Odd elements must be FSTR_P, F(), or FPSTR(). +#define __SEP_N_F(N,V...) _SEP_##N##_F(V) +#define _SEP_N_F(N,V...) __SEP_N_F(N,V) +#define _SEP_N_F_REF() _SEP_N_F +#define _SEP_1_F(p) serial_print(p); +#define _SEP_2_F(p,v) serial_echopair(p,v); +#define _SEP_3_F(p,v,V...) _SEP_2_F(p,v); DEFER2(_SEP_N_F_REF)()(TWO_ARGS(V),V); +#define SERIAL_ECHOF(V...) do{ EVAL(_SEP_N_F(TWO_ARGS(V),V)); }while(0) + +// Print up to 20 pairs of values followed by newline. Odd elements must be FSTR_P, F(), or FPSTR(). +#define __SELP_N_F(N,V...) _SELP_##N##_F(V) +#define _SELP_N_F(N,V...) __SELP_N_F(N,V) +#define _SELP_N_F_REF() _SELP_N_F +#define _SELP_1_F(p) serial_println(p) +#define _SELP_2_F(p,v) serial_echolnpair(p,v) +#define _SELP_3_F(p,v,V...) { _SEP_2_F(p,v); DEFER2(_SELP_N_F_REF)()(TWO_ARGS(V),V); } +#define SERIAL_ECHOLNF(V...) do{ EVAL(_SELP_N_F(TWO_ARGS(V),V)); }while(0) + #ifdef AllowDifferentTypeInList inline void SERIAL_ECHOLIST_IMPL() {} @@ -236,47 +250,49 @@ void serialprintPGM(PGM_P str); template void SERIAL_ECHOLIST_IMPL(T && t, Args && ... args) { SERIAL_IMPL.print(t); - serialprintPGM(PSTR(", ")); + serial_print(F(", ")); SERIAL_ECHOLIST_IMPL(args...); } template - void SERIAL_ECHOLIST(PGM_P const str, Args && ... args) { - SERIAL_IMPL.print(str); + void SERIAL_ECHOLIST(FSTR_P const str, Args && ... args) { + SERIAL_IMPL.print(FTOP(str)); SERIAL_ECHOLIST_IMPL(args...); } #else // Optimization if the listed type are all the same (seems to be the case in the codebase so use that instead) template - void SERIAL_ECHOLIST(PGM_P const str, Args && ... args) { - serialprintPGM(str); + void SERIAL_ECHOLIST(FSTR_P const fstr, Args && ... args) { + serial_print(fstr); typename Private::first_type_of::type values[] = { args... }; constexpr size_t argsSize = sizeof...(args); for (size_t i = 0; i < argsSize; i++) { - if (i) serialprintPGM(PSTR(", ")); + if (i) serial_print(F(", ")); SERIAL_IMPL.print(values[i]); } } #endif -#define SERIAL_ECHOPAIR_F_P(P,V...) do{ serialprintPGM(P); SERIAL_ECHO_F(V); }while(0) -#define SERIAL_ECHOLNPAIR_F_P(V...) do{ SERIAL_ECHOPAIR_F_P(V); SERIAL_EOL(); }while(0) +// SERIAL_ECHO_F prints a floating point value with optional precision +inline void SERIAL_ECHO_F(EnsureDouble x, int digit=2) { SERIAL_IMPL.print(x, digit); } + +#define SERIAL_ECHOPAIR_F_P(P,V...) do{ serial_print_P(P); SERIAL_ECHO_F(V); }while(0) +#define SERIAL_ECHOLNPAIR_F_P(P,V...) do{ SERIAL_ECHOPAIR_F_P(P,V); SERIAL_EOL(); }while(0) -#define SERIAL_ECHOPAIR_F(S,V...) SERIAL_ECHOPAIR_F_P(PSTR(S),V) -#define SERIAL_ECHOLNPAIR_F(V...) do{ SERIAL_ECHOPAIR_F(V); SERIAL_EOL(); }while(0) +#define SERIAL_ECHOPAIR_F_F(S,V...) do{ serial_print(S); SERIAL_ECHO_F(V); }while(0) +#define SERIAL_ECHOLNPAIR_F_F(S,V...) do{ SERIAL_ECHOPAIR_F_F(S,V); SERIAL_EOL(); }while(0) -#define SERIAL_ECHO_START() serial_echo_start() -#define SERIAL_ERROR_START() serial_error_start() -#define SERIAL_EOL() SERIAL_CHAR('\n') +#define SERIAL_ECHOPAIR_F(S,V...) SERIAL_ECHOPAIR_F_F(F(S),V) +#define SERIAL_ECHOLNPAIR_F(V...) do{ SERIAL_ECHOPAIR_F(V); SERIAL_EOL(); }while(0) -#define SERIAL_ECHO_MSG(V...) do{ SERIAL_ECHO_START(); SERIAL_ECHOLNPGM(V); }while(0) -#define SERIAL_ERROR_MSG(V...) do{ SERIAL_ERROR_START(); SERIAL_ECHOLNPGM(V); }while(0) +#define SERIAL_ECHO_MSG(V...) do{ SERIAL_ECHO_START(); SERIAL_ECHOLNPGM(V); }while(0) +#define SERIAL_ERROR_MSG(V...) do{ SERIAL_ERROR_START(); SERIAL_ECHOLNPGM(V); }while(0) -#define SERIAL_ECHO_SP(C) serial_spaces(C) +#define SERIAL_ECHO_SP(C) serial_spaces(C) -#define SERIAL_ECHO_TERNARY(TF, PRE, ON, OFF, POST) serial_ternary(TF, PSTR(PRE), PSTR(ON), PSTR(OFF), PSTR(POST)) +#define SERIAL_ECHO_TERNARY(TF, PRE, ON, OFF, POST) serial_ternary(TF, F(PRE), F(ON), F(OFF), F(POST)) #if SERIAL_FLOAT_PRECISION #define SERIAL_DECIMAL(V) SERIAL_PRINT(V, SERIAL_FLOAT_PRECISION) @@ -287,33 +303,72 @@ void serialprintPGM(PGM_P str); // // Functions for serial printing from PROGMEM. (Saves loads of SRAM.) // -inline void serial_echopair_PGM(PGM_P const s_P, serial_char_t v) { serialprintPGM(s_P); SERIAL_CHAR(v.c); } - -inline void serial_echopair_PGM(PGM_P const s_P, float v) { serialprintPGM(s_P); SERIAL_DECIMAL(v); } -inline void serial_echopair_PGM(PGM_P const s_P, double v) { serialprintPGM(s_P); SERIAL_DECIMAL(v); } -inline void serial_echopair_PGM(PGM_P const s_P, const char *v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +inline void serial_echopair_P(PGM_P const pstr, serial_char_t v) { serial_print_P(pstr); SERIAL_CHAR(v.c); } +inline void serial_echopair_P(PGM_P const pstr, float v) { serial_print_P(pstr); SERIAL_DECIMAL(v); } +inline void serial_echopair_P(PGM_P const pstr, double v) { serial_print_P(pstr); SERIAL_DECIMAL(v); } +//inline void serial_echopair_P(PGM_P const pstr, const char *v) { serial_print_P(pstr); SERIAL_ECHO(v); } +inline void serial_echopair_P(PGM_P const pstr, FSTR_P v) { serial_print_P(pstr); SERIAL_ECHOF(v); } // Default implementation for types without a specialization. Handles integers. template -void serial_echopair_PGM(PGM_P const s_P, T v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +inline void serial_echopair_P(PGM_P const pstr, T v) { serial_print_P(pstr); SERIAL_ECHO(v); } + +// Add a newline. +template +inline void serial_echolnpair_P(PGM_P const pstr, T v) { serial_echopair_P(pstr, v); SERIAL_EOL(); } -inline void serial_echopair_PGM(PGM_P const s_P, bool v) { serial_echopair_PGM(s_P, (int)v); } -inline void serial_echopair_PGM(PGM_P const s_P, void *v) { serial_echopair_PGM(s_P, (uintptr_t)v); } +// Catch-all for __FlashStringHelper * +template +inline void serial_echopair(FSTR_P const fstr, T v) { serial_echopair_P(FTOP(fstr), v); } + +// Add a newline to the serial output +template +inline void serial_echolnpair(FSTR_P const fstr, T v) { serial_echolnpair_P(FTOP(fstr), v); } void serial_echo_start(); void serial_error_start(); -void serial_ternary(const bool onoff, PGM_P const pre, PGM_P const on, PGM_P const off, PGM_P const post=nullptr); +void serial_ternary(const bool onoff, FSTR_P const pre, FSTR_P const on, FSTR_P const off, FSTR_P const post=nullptr); void serialprint_onoff(const bool onoff); void serialprintln_onoff(const bool onoff); void serialprint_truefalse(const bool tf); void serial_spaces(uint8_t count); +void serial_offset(const_float_t 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_pos(LINEAR_AXIS_ARGS(const_float_t), PGM_P const prefix=nullptr, PGM_P const suffix=nullptr); +void print_pos(NUM_AXIS_ARGS(const_float_t), FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr); -inline void print_pos(const xyz_pos_t &xyz, PGM_P const prefix=nullptr, PGM_P const suffix=nullptr) { - print_pos(LINEAR_AXIS_ELEM(xyz), prefix, suffix); +inline void print_pos(const xyz_pos_t &xyz, FSTR_P const prefix=nullptr, FSTR_P const suffix=nullptr) { + print_pos(NUM_AXIS_ELEM(xyz), prefix, suffix); } -#define SERIAL_POS(SUFFIX,VAR) do { print_pos(VAR, PSTR(" " STRINGIFY(VAR) "="), PSTR(" : " SUFFIX "\n")); }while(0) -#define SERIAL_XYZ(PREFIX,V...) do { print_pos(V, PSTR(PREFIX), nullptr); }while(0) +#define SERIAL_POS(SUFFIX,VAR) do { print_pos(VAR, F(" " STRINGIFY(VAR) "="), F(" : " SUFFIX "\n")); }while(0) +#define SERIAL_XYZ(PREFIX,V...) do { print_pos(V, F(PREFIX)); }while(0) + +// +// Commonly-used strings in serial output +// + +#define _N_STR(N) N##_STR +#define _N_LBL(N) N##_LBL +#define _N_STR_A(N) _N_STR(N)[] +#define _N_LBL_A(N) _N_LBL(N)[] +#define _SP_N_STR(N) SP_##N##_STR +#define _SP_N_LBL(N) SP_##N##_LBL +#define _SP_N_STR_A(N) _SP_N_STR(N)[] +#define _SP_N_LBL_A(N) _SP_N_LBL(N)[] + +extern const char SP_A_STR[], SP_B_STR[], SP_C_STR[], SP_P_STR[], SP_T_STR[], NUL_STR[], + MAPLIST(_N_STR_A, LOGICAL_AXIS_NAMES), MAPLIST(_SP_N_STR_A, LOGICAL_AXIS_NAMES), + MAPLIST(_N_LBL_A, LOGICAL_AXIS_NAMES), MAPLIST(_SP_N_LBL_A, LOGICAL_AXIS_NAMES); + +PGM_P const SP_AXIS_LBL[] PROGMEM = { MAPLIST(_SP_N_LBL, LOGICAL_AXIS_NAMES) }; +PGM_P const SP_AXIS_STR[] PROGMEM = { MAPLIST(_SP_N_STR, LOGICAL_AXIS_NAMES) }; + +#undef _N_STR +#undef _N_LBL +#undef _N_STR_A +#undef _N_LBL_A +#undef _SP_N_STR +#undef _SP_N_LBL +#undef _SP_N_STR_A +#undef _SP_N_LBL_A diff --git a/Marlin/src/core/serial_hook.h b/Marlin/src/core/serial_hook.h index 2019b389e4977..65c553c702592 100644 --- a/Marlin/src/core/serial_hook.h +++ b/Marlin/src/core/serial_hook.h @@ -37,13 +37,15 @@ class SerialMask { inline constexpr bool enabled(const SerialMask PortMask) const { return mask & PortMask.mask; } inline constexpr SerialMask combine(const SerialMask other) const { return SerialMask(mask | other.mask); } inline constexpr SerialMask operator<< (const int offset) const { return SerialMask(mask << offset); } - static inline SerialMask from(const serial_index_t index) { + static SerialMask from(const serial_index_t index) { if (index.valid()) return SerialMask(_BV(index.index)); return SerialMask(0); // A invalid index mean no output } constexpr SerialMask(const uint8_t mask) : mask(mask) {} - constexpr SerialMask(const SerialMask & other) : mask(other.mask) {} // Can't use = default here since not all framework support this + constexpr SerialMask(const SerialMask &rs) : mask(rs.mask) {} // Can't use = default here since not all frameworks support this + + SerialMask& operator=(const SerialMask &rs) { mask = rs.mask; return *this; } static constexpr uint8_t All = 0xFF; }; @@ -298,7 +300,7 @@ struct MultiSerial : public SerialBase< MultiSerial< REPEAT(NUM_SERIAL, _S_NAME) // Build the actual serial object depending on current configuration #define Serial1Class TERN(SERIAL_RUNTIME_HOOK, RuntimeSerial, BaseSerial) #define ForwardSerial1Class TERN(SERIAL_RUNTIME_HOOK, RuntimeSerial, ForwardSerial) -#ifdef HAS_MULTI_SERIAL +#if HAS_MULTI_SERIAL #define Serial2Class ConditionalSerial #if NUM_SERIAL >= 3 #define Serial3Class ConditionalSerial diff --git a/Marlin/src/core/types.h b/Marlin/src/core/types.h index e11a6cd03e4d8..e3c4b9e8343f3 100644 --- a/Marlin/src/core/types.h +++ b/Marlin/src/core/types.h @@ -26,13 +26,6 @@ #include "../inc/MarlinConfigPre.h" -class __FlashStringHelper; -typedef const __FlashStringHelper* FSTR_P; -#ifndef FPSTR - #define FPSTR(S) (reinterpret_cast(S)) -#endif -#define FTOP(S) (reinterpret_cast(S)) - // // Conditional type assignment magic. For example... // @@ -43,23 +36,40 @@ struct IF { typedef R type; }; template struct IF { typedef L type; }; -#define LINEAR_AXIS_GANG(V...) GANG_N(LINEAR_AXES, V) -#define LINEAR_AXIS_CODE(V...) CODE_N(LINEAR_AXES, V) -#define LINEAR_AXIS_LIST(V...) LIST_N(LINEAR_AXES, V) -#define LINEAR_AXIS_ARRAY(V...) { LINEAR_AXIS_LIST(V) } -#define LINEAR_AXIS_ARGS(T...) LINEAR_AXIS_LIST(T x, T y, T z, T i, T j, T k) -#define LINEAR_AXIS_ELEM(O) LINEAR_AXIS_LIST(O.x, O.y, O.z, O.i, O.j, O.k) -#define LINEAR_AXIS_DEFS(T,V) LINEAR_AXIS_LIST(T x=V, T y=V, T z=V, T i=V, T j=V, T k=V) - -#define LOGICAL_AXIS_GANG(E,V...) LINEAR_AXIS_GANG(V) GANG_ITEM_E(E) -#define LOGICAL_AXIS_CODE(E,V...) LINEAR_AXIS_CODE(V) CODE_ITEM_E(E) -#define LOGICAL_AXIS_LIST(E,V...) LINEAR_AXIS_LIST(V) LIST_ITEM_E(E) +#define NUM_AXIS_GANG(V...) GANG_N(NUM_AXES, V) +#define NUM_AXIS_CODE(V...) CODE_N(NUM_AXES, V) +#define NUM_AXIS_LIST(V...) LIST_N(NUM_AXES, V) +#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) +#define NUM_AXIS_ELEM(O) NUM_AXIS_LIST(O.x, O.y, O.z, O.i, O.j, O.k) +#define NUM_AXIS_DEFS(T,V) NUM_AXIS_LIST(T x=V, T y=V, T z=V, T i=V, T j=V, T k=V) + +#define MAIN_AXIS_NAMES NUM_AXIS_LIST(X, Y, Z, I, J, K) +#define MAIN_AXIS_MAP(F) MAP(F, MAIN_AXIS_NAMES) +#define STR_AXES_MAIN NUM_AXIS_GANG("X", "Y", "Z", STR_I, STR_J, STR_K) + +#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_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_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) #define LOGICAL_AXIS_ELEM(O) LOGICAL_AXIS_LIST(O.e, O.x, O.y, O.z, O.i, O.j, O.k) #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) -#define LOGICAL_AXES_STRING LOGICAL_AXIS_GANG("E", "X", "Y", "Z", AXIS4_STR, AXIS5_STR, AXIS6_STR) +#define LOGICAL_AXIS_NAMES LOGICAL_AXIS_LIST(E, X, Y, Z, I, J, K) +#define LOGICAL_AXIS_MAP(F) MAP(F, LOGICAL_AXIS_NAMES) + +#define STR_AXES_LOGICAL LOGICAL_AXIS_GANG("E", "X", "Y", "Z", STR_I, STR_J, STR_K) + +#define XYZ_GANG(V...) GANG_N(PRIMARY_LINEAR_AXES, V) +#define XYZ_CODE(V...) CODE_N(PRIMARY_LINEAR_AXES, V) + +#define SECONDARY_AXIS_GANG(V...) GANG_N(SECONDARY_AXES, V) +#define SECONDARY_AXIS_CODE(V...) CODE_N(SECONDARY_AXES, V) #if HAS_EXTRUDERS #define LIST_ITEM_E(N) , N @@ -71,6 +81,61 @@ struct IF { typedef L type; }; #define GANG_ITEM_E(N) #endif +#define AXIS_COLLISION(L) (AXIS4_NAME == L || AXIS5_NAME == L || AXIS6_NAME == L) + +// General Flags for some number of states +template +struct Flags { + typedef typename IF<(N>8), uint16_t, uint8_t>::type bits_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; + union { + bits_t b; + typename IF<(N>8), N16, N8>::type flag; + }; + void reset() { b = 0; } + void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } + void set(const int n) { b |= (bits_t)_BV(n); } + void clear(const int n) { b &= ~(bits_t)_BV(n); } + bool test(const int n) const { return TEST(b, n); } + const bool operator[](const int n) { return test(n); } + const bool operator[](const int n) const { return test(n); } + int size() const { return sizeof(b); } +}; + +// Specialization for a single bool flag +template<> +struct Flags<1> { + bool b; + void reset() { b = false; } + void set(const int n, const bool onoff) { onoff ? set(n) : clear(n); } + void set(const int) { b = true; } + void clear(const int) { b = false; } + bool test(const int) const { return b; } + bool& operator[](const int) { return b; } + bool operator[](const int) const { return b; } + int size() const { return sizeof(b); } +}; + +typedef Flags<8> flags_8_t; +typedef Flags<16> flags_16_t; + +// Flags for some axis states, with per-axis aliases xyzijkuvwe +typedef struct AxisFlags { + union { + struct Flags flags; + struct { bool LOGICAL_AXIS_LIST(e:1, x:1, y:1, z:1, i:1, j:1, k:1, u:1, v:1, w:1); }; + }; + void reset() { flags.reset(); } + void set(const int n) { flags.set(n); } + void set(const int n, const bool onoff) { flags.set(n, onoff); } + void clear(const int n) { flags.clear(n); } + bool test(const int n) const { return flags.test(n); } + bool operator[](const int n) { return flags[n]; } + bool operator[](const int n) const { return flags[n]; } + int size() const { return sizeof(flags); } +} axis_flags_t; + // // Enumerated axis indices // @@ -81,7 +146,7 @@ struct IF { typedef L type; }; enum AxisEnum : uint8_t { // Linear axes may be controlled directly or indirectly - LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS) + NUM_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS) // Extruder axes may be considered distinctly #define _EN_ITEM(N) , E##N##_AXIS @@ -89,7 +154,7 @@ enum AxisEnum : uint8_t { #undef _EN_ITEM // Core also keeps toolhead directions - #if EITHER(IS_CORE, MARKFORGED_XY) + #if ANY(IS_CORE, MARKFORGED_XY, MARKFORGED_YX) , X_HEAD, Y_HEAD, Z_HEAD #endif @@ -103,10 +168,10 @@ enum AxisEnum : uint8_t { // A, B, and C are for DELTA, SCARA, etc. , A_AXIS = X_AXIS - #if LINEAR_AXES >= 2 + #if HAS_Y_AXIS , B_AXIS = Y_AXIS #endif - #if LINEAR_AXES >= 3 + #if HAS_Z_AXIS , C_AXIS = Z_AXIS #endif @@ -120,9 +185,10 @@ typedef IF<(NUM_AXIS_ENUMS > 8), uint16_t, uint8_t>::type axis_bits_t; // Loop over axes // #define LOOP_ABC(VAR) LOOP_S_LE_N(VAR, A_AXIS, C_AXIS) -#define LOOP_LINEAR_AXES(VAR) LOOP_S_L_N(VAR, X_AXIS, LINEAR_AXES) +#define LOOP_NUM_AXES(VAR) LOOP_S_L_N(VAR, X_AXIS, NUM_AXES) #define LOOP_LOGICAL_AXES(VAR) LOOP_S_L_N(VAR, X_AXIS, LOGICAL_AXES) #define LOOP_DISTINCT_AXES(VAR) LOOP_S_L_N(VAR, X_AXIS, DISTINCT_AXES) +#define LOOP_DISTINCT_E(VAR) LOOP_L_N(VAR, DISTINCT_E) // // feedRate_t is just a humble float @@ -133,6 +199,7 @@ 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 // +typedef uint16_t raw_adc_t; typedef int16_t celsius_t; typedef float celsius_float_t; @@ -263,10 +330,10 @@ struct XYval { FI void set(const T px, const T py) { x = px; y = py; } FI void set(const T (&arr)[XY]) { x = arr[0]; y = arr[1]; } #endif - #if LINEAR_AXES > XY - FI void set(const T (&arr)[LINEAR_AXES]) { x = arr[0]; y = arr[1]; } + #if NUM_AXES > XY + FI void set(const T (&arr)[NUM_AXES]) { x = arr[0]; y = arr[1]; } #endif - #if LOGICAL_AXES > LINEAR_AXES + #if LOGICAL_AXES > NUM_AXES FI void set(const T (&arr)[LOGICAL_AXES]) { x = arr[0]; y = arr[1]; } #if DISTINCT_AXES > LOGICAL_AXES FI void set(const T (&arr)[DISTINCT_AXES]) { x = arr[0]; y = arr[1]; } @@ -388,60 +455,60 @@ struct XYval { template struct XYZval { union { - struct { T LINEAR_AXIS_ARGS(); }; - struct { T LINEAR_AXIS_LIST(a, b, c, u, v, w); }; - T pos[LINEAR_AXES]; + struct { T NUM_AXIS_ARGS(); }; + struct { T NUM_AXIS_LIST(a, b, c, _i, _j, _k); }; + T pos[NUM_AXES]; }; // Set all to 0 - FI void reset() { LINEAR_AXIS_GANG(x =, y =, z =, i =, j =, k =) 0; } + FI void reset() { NUM_AXIS_GANG(x =, y =, z =, i =, j =, k =) 0; } // Setters taking struct types and arrays FI void set(const T px) { x = px; } FI void set(const T px, const T py) { x = px; y = py; } FI void set(const XYval pxy) { x = pxy.x; y = pxy.y; } - FI void set(const XYval pxy, const T pz) { LINEAR_AXIS_CODE(x = pxy.x, y = pxy.y, z = pz, NOOP, NOOP, NOOP); } + FI void set(const XYval pxy, const T pz) { NUM_AXIS_CODE(x = pxy.x, y = pxy.y, z = pz, NOOP, NOOP, NOOP); } FI void set(const T (&arr)[XY]) { x = arr[0]; y = arr[1]; } #if HAS_Z_AXIS - FI void set(const T (&arr)[LINEAR_AXES]) { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5]); } - FI void set(LINEAR_AXIS_ARGS(const T)) { LINEAR_AXIS_CODE(a = x, b = y, c = z, u = i, v = j, w = k ); } + 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]); } + FI void set(NUM_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k); } #endif - #if LOGICAL_AXES > LINEAR_AXES - FI void set(const T (&arr)[LOGICAL_AXES]) { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5]); } - FI void set(LOGICAL_AXIS_ARGS(const T)) { LINEAR_AXIS_CODE(a = x, b = y, c = z, u = i, v = j, w = k ); } + #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]); } + FI void set(LOGICAL_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k); } #if DISTINCT_AXES > LOGICAL_AXES - FI void set(const T (&arr)[DISTINCT_AXES]) { LINEAR_AXIS_CODE(x = arr[0], y = arr[1], z = arr[2], i = arr[3], j = arr[4], k = arr[5]); } + 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]); } #endif #endif - #if LINEAR_AXES >= 4 + #if HAS_I_AXIS FI void set(const T px, const T py, const T pz) { x = px; y = py; z = pz; } #endif - #if LINEAR_AXES >= 5 + #if HAS_J_AXIS FI void set(const T px, const T py, const T pz, const T pi) { x = px; y = py; z = pz; i = pi; } #endif - #if LINEAR_AXES >= 6 + #if HAS_K_AXIS FI void set(const T px, const T py, const T pz, const T pi, const T pj) { x = px; y = py; z = pz; i = pi; j = pj; } #endif // Length reduced to one dimension - FI T magnitude() const { return (T)sqrtf(LINEAR_AXIS_GANG(x*x, + y*y, + z*z, + i*i, + j*j, + k*k)); } + FI T magnitude() const { return (T)sqrtf(NUM_AXIS_GANG(x*x, + y*y, + z*z, + i*i, + j*j, + k*k)); } // Pointer to the data as a simple array FI operator T* () { return pos; } // If any element is true then it's true - FI operator bool() { return LINEAR_AXIS_GANG(x, || y, || z, || i, || j, || k); } + FI operator bool() { return NUM_AXIS_GANG(x, || y, || z, || i, || j, || k); } // Explicit copy and copies with conversion FI XYZval copy() const { XYZval o = *this; return o; } - FI XYZval ABS() const { return LINEAR_AXIS_ARRAY(T(_ABS(x)), T(_ABS(y)), T(_ABS(z)), T(_ABS(i)), T(_ABS(j)), T(_ABS(k))); } - FI XYZval asInt() { return LINEAR_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k)); } - FI XYZval asInt() const { return LINEAR_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k)); } - FI XYZval asLong() { return LINEAR_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k)); } - FI XYZval asLong() const { return LINEAR_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k)); } - FI XYZval ROUNDL() { return LINEAR_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))); } - FI XYZval ROUNDL() const { return LINEAR_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))); } - FI XYZval asFloat() { return LINEAR_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k)); } - FI XYZval asFloat() const { return LINEAR_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k)); } - FI XYZval reciprocal() const { return LINEAR_AXIS_ARRAY(_RECIP(x), _RECIP(y), _RECIP(z), _RECIP(i), _RECIP(j), _RECIP(k)); } + FI 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))); } + FI XYZval asInt() { return NUM_AXIS_ARRAY(int16_t(x), int16_t(y), int16_t(z), int16_t(i), int16_t(j), int16_t(k)); } + FI 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)); } + FI XYZval asLong() { return NUM_AXIS_ARRAY(int32_t(x), int32_t(y), int32_t(z), int32_t(i), int32_t(j), int32_t(k)); } + FI 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)); } + FI XYZval ROUNDL() { 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))); } + FI 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))); } + FI XYZval asFloat() { return NUM_AXIS_ARRAY(static_cast(x), static_cast(y), static_cast(z), static_cast(i), static_cast(j), static_cast(k)); } + FI 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)); } + FI XYZval reciprocal() const { return NUM_AXIS_ARRAY(_RECIP(x), _RECIP(y), _RECIP(z), _RECIP(i), _RECIP(j), _RECIP(k)); } // Marlin workspace shifting is done with G92 and M206 FI XYZval asLogical() const { XYZval o = asFloat(); toLogical(o); return o; } @@ -452,78 +519,78 @@ struct XYZval { FI operator const XYval&() const { return *(const XYval*)this; } // Cast to a type with more fields by making a new object - FI operator XYZEval() const { return LINEAR_AXIS_ARRAY(x, y, z, i, j, k); } + FI operator XYZEval() const { return NUM_AXIS_ARRAY(x, y, z, i, j, k); } // Accessor via an AxisEnum (or any integer) [index] FI T& operator[](const int n) { return pos[n]; } FI const T& operator[](const int n) const { return pos[n]; } // Assignment operator overrides do the expected thing - FI XYZval& operator= (const T v) { set(ARRAY_N_1(LINEAR_AXES, v)); return *this; } + 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(LINEAR_AXIS_ELEM(rs)); return *this; } + FI XYZval& operator= (const XYZEval &rs) { set(NUM_AXIS_ELEM(rs)); return *this; } // Override other operators to get intuitive behaviors - FI XYZval operator+ (const XYval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator+ (const XYval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator- (const XYval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator- (const XYval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator* (const XYval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator* (const XYval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator/ (const XYval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator/ (const XYval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } - FI XYZval operator+ (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZval operator+ (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZval operator- (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZval operator- (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZval operator* (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZval operator* (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZval operator/ (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } - FI XYZval operator/ (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } - FI XYZval operator+ (const XYZEval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZval operator+ (const XYZEval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZval operator- (const XYZEval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZval operator- (const XYZEval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZval operator* (const XYZEval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZval operator* (const XYZEval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZval operator/ (const XYZEval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } - FI XYZval operator/ (const XYZEval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } - FI XYZval operator* (const float &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } - FI XYZval operator* (const float &v) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } - FI XYZval operator* (const int &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } - FI XYZval operator* (const int &v) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } - FI XYZval operator/ (const float &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } - FI XYZval operator/ (const float &v) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } - FI XYZval operator/ (const int &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } - FI XYZval operator/ (const int &v) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } - FI XYZval operator>>(const int &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(_RS(ls.x), _RS(ls.y), _RS(ls.z), _RS(ls.i), _RS(ls.j), _RS(ls.k) ); return ls; } - FI XYZval operator>>(const int &v) { XYZval ls = *this; LINEAR_AXIS_CODE(_RS(ls.x), _RS(ls.y), _RS(ls.z), _RS(ls.i), _RS(ls.j), _RS(ls.k) ); return ls; } - FI XYZval operator<<(const int &v) const { XYZval ls = *this; LINEAR_AXIS_CODE(_LS(ls.x), _LS(ls.y), _LS(ls.z), _LS(ls.i), _LS(ls.j), _LS(ls.k) ); return ls; } - FI XYZval operator<<(const int &v) { XYZval ls = *this; LINEAR_AXIS_CODE(_LS(ls.x), _LS(ls.y), _LS(ls.z), _LS(ls.i), _LS(ls.j), _LS(ls.k) ); return ls; } - FI const XYZval operator-() const { XYZval o = *this; LINEAR_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z, o.i = -i, o.j = -j, o.k = -k); return o; } - FI XYZval operator-() { XYZval o = *this; LINEAR_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z, o.i = -i, o.j = -j, o.k = -k); return o; } + FI XYZval operator+ (const XYval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator+ (const XYval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator- (const XYval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator- (const XYval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator* (const XYval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator* (const XYval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator/ (const XYval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator/ (const XYval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, NOOP , NOOP , NOOP , NOOP ); return ls; } + FI XYZval operator+ (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZval operator+ (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZval operator- (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZval operator- (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZval operator* (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZval operator* (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZval operator/ (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZval operator/ (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZval operator+ (const XYZEval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZval operator+ (const XYZEval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZval operator- (const XYZEval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZval operator- (const XYZEval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZval operator* (const XYZEval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZval operator* (const XYZEval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZval operator/ (const XYZEval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZval operator/ (const XYZEval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZval operator* (const float &v) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } + FI XYZval operator* (const float &v) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } + FI XYZval operator* (const int &v) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } + FI XYZval operator* (const int &v) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= v, ls.y *= v, ls.z *= v, ls.i *= v, ls.j *= v, ls.k *= v ); return ls; } + FI XYZval operator/ (const float &v) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } + FI XYZval operator/ (const float &v) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } + FI XYZval operator/ (const int &v) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } + FI XYZval operator/ (const int &v) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= v, ls.y /= v, ls.z /= v, ls.i /= v, ls.j /= v, ls.k /= v ); return ls; } + FI XYZval operator>>(const int &v) const { XYZval ls = *this; NUM_AXIS_CODE(_RS(ls.x), _RS(ls.y), _RS(ls.z), _RS(ls.i), _RS(ls.j), _RS(ls.k) ); return ls; } + FI XYZval operator>>(const int &v) { XYZval ls = *this; NUM_AXIS_CODE(_RS(ls.x), _RS(ls.y), _RS(ls.z), _RS(ls.i), _RS(ls.j), _RS(ls.k) ); return ls; } + FI XYZval operator<<(const int &v) const { XYZval ls = *this; NUM_AXIS_CODE(_LS(ls.x), _LS(ls.y), _LS(ls.z), _LS(ls.i), _LS(ls.j), _LS(ls.k) ); return ls; } + FI XYZval operator<<(const int &v) { XYZval ls = *this; NUM_AXIS_CODE(_LS(ls.x), _LS(ls.y), _LS(ls.z), _LS(ls.i), _LS(ls.j), _LS(ls.k) ); return ls; } + FI const XYZval operator-() const { XYZval o = *this; NUM_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z, o.i = -i, o.j = -j, o.k = -k); return o; } + FI XYZval operator-() { XYZval o = *this; NUM_AXIS_CODE(o.x = -x, o.y = -y, o.z = -z, o.i = -i, o.j = -j, o.k = -k); return o; } // Modifier operators - FI XYZval& operator+=(const XYval &rs) { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } - FI XYZval& operator-=(const XYval &rs) { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } - FI XYZval& operator*=(const XYval &rs) { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } - FI XYZval& operator/=(const XYval &rs) { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } - FI XYZval& operator+=(const XYZval &rs) { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } - FI XYZval& operator-=(const XYZval &rs) { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } - FI XYZval& operator*=(const XYZval &rs) { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } - FI XYZval& operator/=(const XYZval &rs) { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } - FI XYZval& operator+=(const XYZEval &rs) { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } - FI XYZval& operator-=(const XYZEval &rs) { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } - FI XYZval& operator*=(const XYZEval &rs) { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } - FI XYZval& operator/=(const XYZEval &rs) { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } - FI XYZval& operator*=(const float &v) { LINEAR_AXIS_CODE(x *= v, y *= v, z *= v, i *= v, j *= v, k *= v); return *this; } - FI XYZval& operator*=(const int &v) { LINEAR_AXIS_CODE(x *= v, y *= v, z *= v, i *= v, j *= v, k *= v); return *this; } - FI XYZval& operator>>=(const int &v) { LINEAR_AXIS_CODE(_RS(x), _RS(y), _RS(z), _RS(i), _RS(j), _RS(k)); return *this; } - FI XYZval& operator<<=(const int &v) { LINEAR_AXIS_CODE(_LS(x), _LS(y), _LS(z), _LS(i), _LS(j), _LS(k)); return *this; } + FI XYZval& operator+=(const XYval &rs) { NUM_AXIS_CODE(x += rs.x, y += rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } + FI XYZval& operator-=(const XYval &rs) { NUM_AXIS_CODE(x -= rs.x, y -= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } + FI XYZval& operator*=(const XYval &rs) { NUM_AXIS_CODE(x *= rs.x, y *= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } + FI XYZval& operator/=(const XYval &rs) { NUM_AXIS_CODE(x /= rs.x, y /= rs.y, NOOP, NOOP, NOOP, NOOP ); return *this; } + FI XYZval& operator+=(const XYZval &rs) { NUM_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } + FI XYZval& operator-=(const XYZval &rs) { NUM_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } + FI XYZval& operator*=(const XYZval &rs) { NUM_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } + FI XYZval& operator/=(const XYZval &rs) { NUM_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } + FI XYZval& operator+=(const XYZEval &rs) { NUM_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } + FI XYZval& operator-=(const XYZEval &rs) { NUM_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } + FI XYZval& operator*=(const XYZEval &rs) { NUM_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } + FI XYZval& operator/=(const XYZEval &rs) { NUM_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } + FI XYZval& operator*=(const float &v) { NUM_AXIS_CODE(x *= v, y *= v, z *= v, i *= v, j *= v, k *= v); return *this; } + FI XYZval& operator*=(const int &v) { NUM_AXIS_CODE(x *= v, y *= v, z *= v, i *= v, j *= v, k *= v); return *this; } + FI XYZval& operator>>=(const int &v) { NUM_AXIS_CODE(_RS(x), _RS(y), _RS(z), _RS(i), _RS(j), _RS(k)); return *this; } + FI XYZval& operator<<=(const int &v) { NUM_AXIS_CODE(_LS(x), _LS(y), _LS(z), _LS(i), _LS(j), _LS(k)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZEval &rs) { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } - FI bool operator==(const XYZEval &rs) const { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } + FI bool operator==(const XYZEval &rs) { return true NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } + 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); } FI bool operator!=(const XYZEval &rs) { return !operator==(rs); } FI bool operator!=(const XYZEval &rs) const { return !operator==(rs); } }; @@ -535,34 +602,36 @@ template struct XYZEval { union { struct { T LOGICAL_AXIS_ARGS(); }; - struct { T LOGICAL_AXIS_LIST(_e, a, b, c, u, v, w); }; + struct { T LOGICAL_AXIS_LIST(_e, a, b, c, _i, _j, _k); }; T pos[LOGICAL_AXES]; }; // Reset all to 0 FI void reset() { LOGICAL_AXIS_GANG(e =, x =, y =, z =, i =, j =, k =) 0; } - // Setters taking struct types and arrays - FI void set(const T px) { x = px; } - FI void set(const T px, const T py) { x = px; y = py; } - FI void set(const XYval pxy) { x = pxy.x; y = pxy.y; } - FI void set(const XYZval pxyz) { set(LINEAR_AXIS_ELEM(pxyz)); } - #if HAS_Z_AXIS - FI void set(LINEAR_AXIS_ARGS(const T)) { LINEAR_AXIS_CODE(a = x, b = y, c = z, u = i, v = j, w = k); } - #endif - #if LOGICAL_AXES > LINEAR_AXES - FI void set(const XYval pxy, const T pe) { set(pxy); 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, u = i, v = j, w = k); } - #endif - #if LINEAR_AXES >= 4 + // Setters for some number of linear axes, not all + FI void set(const T px) { x = px; } + FI void set(const T px, const T py) { x = px; y = py; } + #if HAS_I_AXIS FI void set(const T px, const T py, const T pz) { x = px; y = py; z = pz; } #endif - #if LINEAR_AXES >= 5 + #if HAS_J_AXIS FI void set(const T px, const T py, const T pz, const T pi) { x = px; y = py; z = pz; i = pi; } #endif - #if LINEAR_AXES >= 6 + #if HAS_K_AXIS FI void set(const T px, const T py, const T pz, const T pi, const T pj) { x = px; y = py; z = pz; i = pi; j = pj; } #endif + // Setters taking struct types and arrays + FI void set(const XYval pxy) { x = pxy.x; y = pxy.y; } + FI void set(const XYZval pxyz) { set(NUM_AXIS_ELEM(pxyz)); } + #if HAS_Z_AXIS + FI void set(NUM_AXIS_ARGS(const T)) { NUM_AXIS_CODE(a = x, b = y, c = z, _i = i, _j = j, _k = k); } + #endif + FI void set(const XYval pxy, const T pz) { set(pxy); TERN_(HAS_Z_AXIS, z = pz); } + #if LOGICAL_AXES > NUM_AXES + 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); } + #endif // Length reduced to one dimension FI T magnitude() const { return (T)sqrtf(LOGICAL_AXIS_GANG(+ e*e, + x*x, + y*y, + z*z, + i*i, + j*j, + k*k)); } @@ -599,9 +668,9 @@ struct XYZEval { FI const T& operator[](const int n) const { return pos[n]; } // Assignment operator overrides do the expected thing - FI XYZEval& operator= (const T v) { set(LIST_N_1(LINEAR_AXES, v)); return *this; } + 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(LINEAR_AXIS_ELEM(rs)); return *this; } + FI XYZEval& operator= (const XYZval &rs) { set(NUM_AXIS_ELEM(rs)); return *this; } // Override other operators to get intuitive behaviors FI XYZEval operator+ (const XYval &rs) const { XYZEval ls = *this; ls.x += rs.x; ls.y += rs.y; return ls; } @@ -612,14 +681,14 @@ struct XYZEval { FI XYZEval operator* (const XYval &rs) { XYZEval ls = *this; ls.x *= rs.x; ls.y *= rs.y; return ls; } FI XYZEval operator/ (const XYval &rs) const { XYZEval ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } FI XYZEval operator/ (const XYval &rs) { XYZEval ls = *this; ls.x /= rs.x; ls.y /= rs.y; return ls; } - FI XYZEval operator+ (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZEval operator+ (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } - FI XYZEval operator- (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZEval operator- (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } - FI XYZEval operator* (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZEval operator* (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } - FI XYZEval operator/ (const XYZval &rs) const { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } - FI XYZEval operator/ (const XYZval &rs) { XYZval ls = *this; LINEAR_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZEval operator+ (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZEval operator+ (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } + FI XYZEval operator- (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZEval operator- (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } + FI XYZEval operator* (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZEval operator* (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x *= rs.x, ls.y *= rs.y, ls.z *= rs.z, ls.i *= rs.i, ls.j *= rs.j, ls.k *= rs.k); return ls; } + FI XYZEval operator/ (const XYZval &rs) const { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } + FI XYZEval operator/ (const XYZval &rs) { XYZval ls = *this; NUM_AXIS_CODE(ls.x /= rs.x, ls.y /= rs.y, ls.z /= rs.z, ls.i /= rs.i, ls.j /= rs.j, ls.k /= rs.k); return ls; } FI XYZEval operator+ (const XYZEval &rs) const { XYZEval ls = *this; LOGICAL_AXIS_CODE(ls.e += rs.e, ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } FI XYZEval operator+ (const XYZEval &rs) { XYZEval ls = *this; LOGICAL_AXIS_CODE(ls.e += rs.e, ls.x += rs.x, ls.y += rs.y, ls.z += rs.z, ls.i += rs.i, ls.j += rs.j, ls.k += rs.k); return ls; } FI XYZEval operator- (const XYZEval &rs) const { XYZEval ls = *this; LOGICAL_AXIS_CODE(ls.e -= rs.e, ls.x -= rs.x, ls.y -= rs.y, ls.z -= rs.z, ls.i -= rs.i, ls.j -= rs.j, ls.k -= rs.k); return ls; } @@ -648,10 +717,10 @@ struct XYZEval { FI XYZEval& operator-=(const XYval &rs) { x -= rs.x; y -= rs.y; return *this; } FI XYZEval& operator*=(const XYval &rs) { x *= rs.x; y *= rs.y; return *this; } FI XYZEval& operator/=(const XYval &rs) { x /= rs.x; y /= rs.y; return *this; } - FI XYZEval& operator+=(const XYZval &rs) { LINEAR_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } - FI XYZEval& operator-=(const XYZval &rs) { LINEAR_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } - FI XYZEval& operator*=(const XYZval &rs) { LINEAR_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } - FI XYZEval& operator/=(const XYZval &rs) { LINEAR_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } + FI XYZEval& operator+=(const XYZval &rs) { NUM_AXIS_CODE(x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } + FI XYZEval& operator-=(const XYZval &rs) { NUM_AXIS_CODE(x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } + FI XYZEval& operator*=(const XYZval &rs) { NUM_AXIS_CODE(x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } + FI XYZEval& operator/=(const XYZval &rs) { NUM_AXIS_CODE(x /= rs.x, y /= rs.y, z /= rs.z, i /= rs.i, j /= rs.j, k /= rs.k); return *this; } FI XYZEval& operator+=(const XYZEval &rs) { LOGICAL_AXIS_CODE(e += rs.e, x += rs.x, y += rs.y, z += rs.z, i += rs.i, j += rs.j, k += rs.k); return *this; } FI XYZEval& operator-=(const XYZEval &rs) { LOGICAL_AXIS_CODE(e -= rs.e, x -= rs.x, y -= rs.y, z -= rs.z, i -= rs.i, j -= rs.j, k -= rs.k); return *this; } FI XYZEval& operator*=(const XYZEval &rs) { LOGICAL_AXIS_CODE(e *= rs.e, x *= rs.x, y *= rs.y, z *= rs.z, i *= rs.i, j *= rs.j, k *= rs.k); return *this; } @@ -661,8 +730,8 @@ struct XYZEval { FI XYZEval& operator<<=(const int &v) { LOGICAL_AXIS_CODE(_LS(e), _LS(x), _LS(y), _LS(z), _LS(i), _LS(j), _LS(k)); return *this; } // Exact comparisons. For floats a "NEAR" operation may be better. - FI bool operator==(const XYZval &rs) { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } - FI bool operator==(const XYZval &rs) const { return true LINEAR_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } + FI bool operator==(const XYZval &rs) { return true NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k); } + 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); } FI bool operator!=(const XYZval &rs) { return !operator==(rs); } FI bool operator!=(const XYZval &rs) const { return !operator==(rs); } }; diff --git a/Marlin/src/core/utility.cpp b/Marlin/src/core/utility.cpp index 8aeec89f020bd..e4fd52592475c 100644 --- a/Marlin/src/core/utility.cpp +++ b/Marlin/src/core/utility.cpp @@ -29,10 +29,10 @@ void safe_delay(millis_t ms) { while (ms > 50) { ms -= 50; delay(50); - thermalManager.manage_heater(); + thermalManager.task(); } delay(ms); - thermalManager.manage_heater(); // This keeps us safe if too many small safe_delay() calls are made + thermalManager.task(); // This keeps us safe if too many small safe_delay() calls are made } // A delay to provide brittle hosts time to receive bytes @@ -51,7 +51,7 @@ void safe_delay(millis_t ms) { #include "../module/probe.h" #include "../module/motion.h" - #include "../module/stepper.h" + #include "../module/planner.h" #include "../libs/numtostr.h" #include "../feature/bedlevel/bedlevel.h" @@ -60,7 +60,8 @@ void safe_delay(millis_t ms) { TERN_(DELTA, "Delta") TERN_(IS_SCARA, "SCARA") TERN_(IS_CORE, "Core") - TERN_(MARKFORGED_XY, "MarkForged") + TERN_(MARKFORGED_XY, "MarkForgedXY") + TERN_(MARKFORGED_YX, "MarkForgedYX") TERN_(IS_CARTESIAN, "Cartesian") ); @@ -73,7 +74,8 @@ void safe_delay(millis_t ms) { TERN_(Z_PROBE_SLED, "Z_PROBE_SLED") TERN_(Z_PROBE_ALLEN_KEY, "Z_PROBE_ALLEN_KEY") TERN_(SOLENOID_PROBE, "SOLENOID_PROBE") - TERN(PROBE_SELECTED, "", "NONE") + TERN_(MAGLEV4, "MAGLEV4") + IF_DISABLED(PROBE_SELECTED, "NONE") ); #if HAS_BED_PROBE @@ -92,9 +94,9 @@ void safe_delay(millis_t ms) { SERIAL_ECHOPGM(" (Aligned With"); if (probe.offset_xy.y > 0) - SERIAL_ECHOPGM_P(ENABLED(IS_SCARA) ? PSTR("-Distal") : PSTR("-Back")); + SERIAL_ECHOF(F(TERN(IS_SCARA, "-Distal", "-Back"))); else if (probe.offset_xy.y < 0) - SERIAL_ECHOPGM_P(ENABLED(IS_SCARA) ? PSTR("-Proximal") : PSTR("-Front")); + SERIAL_ECHOF(F(TERN(IS_SCARA, "-Proximal", "-Front"))); else if (probe.offset_xy.x != 0) SERIAL_ECHOPGM("-Center"); @@ -102,7 +104,7 @@ void safe_delay(millis_t ms) { #endif - SERIAL_ECHOPGM_P(probe.offset.z < 0 ? PSTR("Below") : probe.offset.z > 0 ? PSTR("Above") : PSTR("Same Z as")); + SERIAL_ECHOF(probe.offset.z < 0 ? F("Below") : probe.offset.z > 0 ? F("Above") : F("Same Z as")); SERIAL_ECHOLNPGM(" Nozzle)"); #endif @@ -123,20 +125,17 @@ void safe_delay(millis_t ms) { #endif #if ABL_PLANAR SERIAL_ECHOPGM("ABL Adjustment"); - LOOP_LINEAR_AXES(a) { - const float v = planner.get_axis_position_mm(AxisEnum(a)) - current_position[a]; - SERIAL_CHAR(' ', AXIS_CHAR(a)); - if (v > 0) SERIAL_CHAR('+'); - SERIAL_DECIMAL(v); + 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]); } #else #if ENABLED(AUTO_BED_LEVELING_UBL) SERIAL_ECHOPGM("UBL Adjustment Z"); - const float rz = ubl.get_z_correction(current_position); #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) SERIAL_ECHOPGM("ABL Adjustment Z"); - const float rz = bilinear_z_offset(current_position); #endif + const float rz = bedlevel.get_z_correction(current_position); SERIAL_ECHO(ftostr43sign(rz, '+')); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) if (planner.z_fade_height) { @@ -156,11 +155,13 @@ void safe_delay(millis_t ms) { SERIAL_ECHOPGM("Mesh Bed Leveling"); if (planner.leveling_active) { SERIAL_ECHOLNPGM(" (enabled)"); - SERIAL_ECHOPGM("MBL Adjustment Z", ftostr43sign(mbl.get_z(current_position), '+')); + const float z_offset = bedlevel.get_z_offset(), + z_correction = bedlevel.get_z_correction(current_position); + SERIAL_ECHOPGM("MBL Adjustment Z", ftostr43sign(z_offset + z_correction, '+')); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) if (planner.z_fade_height) { SERIAL_ECHOPGM(" (", ftostr43sign( - mbl.get_z(current_position, planner.fade_scaling_factor_for_z(current_position.z)), '+' + z_offset + z_correction * planner.fade_scaling_factor_for_z(current_position.z), '+' )); SERIAL_CHAR(')'); } diff --git a/Marlin/src/core/utility.h b/Marlin/src/core/utility.h index d248091ce5752..a3cd7941bbb84 100644 --- a/Marlin/src/core/utility.h +++ b/Marlin/src/core/utility.h @@ -59,6 +59,11 @@ void safe_delay(millis_t ms); // Delay ensuring that temperatures are #define log_machine_info() NOOP #endif +/** + * A restorer instance remembers a variable's value before setting a + * new value, then restores the old value when it goes out of scope. + * Put operator= on your type to get extended behavior on value change. + */ template class restorer { T& ref_; @@ -77,10 +82,13 @@ class restorer { // 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); - -#if LINEAR_AXES <= XYZ +#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'); #define AXIS_CHAR(A) axis_codes[A] + #define IAXIS_CHAR(A) iaxis_codes[A] #endif diff --git a/Marlin/src/feature/adc/adc_mcp3426.cpp b/Marlin/src/feature/adc/adc_mcp3426.cpp new file mode 100644 index 0000000000000..49bb67ef6d5c7 --- /dev/null +++ b/Marlin/src/feature/adc/adc_mcp3426.cpp @@ -0,0 +1,104 @@ +/** + * 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 . + * + */ + +/** + * adc_mcp3426.cpp - library for MicroChip MCP3426 I2C A/D converter + * + * For implementation details, please take a look at the datasheet: + * https://www.microchip.com/en-us/product/MCP3426 + */ + +#include "../../inc/MarlinConfig.h" + +#if ENABLED(HAS_MCP3426_ADC) + +#include "adc_mcp3426.h" + +// Read the ADC value from MCP342X on a specific channel +int16_t MCP3426::ReadValue(uint8_t channel, uint8_t gain, uint8_t address) { + Error = false; + + #if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM) + Wire.setSDA(pin_t(I2C_SDA_PIN)); + Wire.setSCL(pin_t(I2C_SCL_PIN)); + #endif + + Wire.begin(); // No address joins the BUS as the master + + Wire.beginTransmission(I2C_ADDRESS(address)); + + // Continuous Conversion Mode, 16 bit, Channel 1, Gain x4 + // 26 = 0b00011000 + // RXXCSSGG + // R = Ready Bit + // XX = Channel (00=1, 01=2, 10=3 (MCP3428), 11=4 (MCP3428)) + // C = Conversion Mode Bit (1= Continuous Conversion Mode (Default)) + // SS = Sample rate, 10=15 samples per second @ 16 bits + // GG = Gain 00 =x1 + uint8_t controlRegister = 0b00011000; + + if (channel == 2) controlRegister |= 0b00100000; // Select channel 2 + + if (gain == 2) + controlRegister |= 0b00000001; + else if (gain == 4) + controlRegister |= 0b00000010; + else if (gain == 8) + controlRegister |= 0b00000011; + + Wire.write(controlRegister); + if (Wire.endTransmission() != 0) { + Error = true; + return 0; + } + + const uint8_t len = 3; + uint8_t buffer[len] = {}; + + do { + Wire.requestFrom(I2C_ADDRESS(address), len); + if (Wire.available() != len) { + Error = true; + return 0; + } + + for (uint8_t i = 0; i < len; ++i) + buffer[i] = Wire.read(); + + // Is conversion ready, if not loop around again + } while ((buffer[2] & 0x80) != 0); + + union TwoBytesToInt16 { + uint8_t bytes[2]; + int16_t integervalue; + }; + TwoBytesToInt16 ConversionUnion; + + ConversionUnion.bytes[1] = buffer[0]; + ConversionUnion.bytes[0] = buffer[1]; + + return ConversionUnion.integervalue; +} + +MCP3426 mcp3426; + +#endif // HAS_MCP3426_ADC diff --git a/Marlin/src/feature/adc/adc_mcp3426.h b/Marlin/src/feature/adc/adc_mcp3426.h new file mode 100644 index 0000000000000..af48593369e1a --- /dev/null +++ b/Marlin/src/feature/adc/adc_mcp3426.h @@ -0,0 +1,38 @@ +/** + * 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 + +/** + * Arduino library for MicroChip MCP3426 I2C A/D converter. + * https://www.microchip.com/en-us/product/MCP3426 + */ + +#include +#include + +class MCP3426 { + public: + int16_t ReadValue(uint8_t channel, uint8_t gain, uint8_t address); + bool Error; +}; + +extern MCP3426 mcp3426; diff --git a/Marlin/src/feature/babystep.h b/Marlin/src/feature/babystep.h index f8037678ca16f..5693afb4fc5b1 100644 --- a/Marlin/src/feature/babystep.h +++ b/Marlin/src/feature/babystep.h @@ -54,7 +54,7 @@ class Babystep { #if ENABLED(BABYSTEP_DISPLAY_TOTAL) static int16_t axis_total[BS_TOTAL_IND(Z_AXIS) + 1]; // Total babysteps since G28 - static inline void reset_total(const AxisEnum axis) { + static void reset_total(const AxisEnum axis) { if (TERN1(BABYSTEP_XY, axis == Z_AXIS)) axis_total[BS_TOTAL_IND(axis)] = 0; } @@ -63,7 +63,7 @@ class Babystep { static void add_steps(const AxisEnum axis, const int16_t distance); static void add_mm(const AxisEnum axis, const_float_t mm); - static inline bool has_steps() { + static bool has_steps() { return steps[BS_AXIS_IND(X_AXIS)] || steps[BS_AXIS_IND(Y_AXIS)] || steps[BS_AXIS_IND(Z_AXIS)]; } @@ -71,7 +71,7 @@ class Babystep { // Called by the Temperature or Stepper ISR to // apply accumulated babysteps to the axes. // - static inline void task() { + static void task() { LOOP_LE_N(i, BS_AXIS_IND(Z_AXIS)) step_axis(BS_AXIS(i)); } diff --git a/Marlin/src/feature/backlash.cpp b/Marlin/src/feature/backlash.cpp index b646e19f15aba..13e2cd99eccb6 100644 --- a/Marlin/src/feature/backlash.cpp +++ b/Marlin/src/feature/backlash.cpp @@ -29,6 +29,9 @@ #include "../module/motion.h" #include "../module/planner.h" +axis_bits_t Backlash::last_direction_bits; +xyz_long_t Backlash::residual_error{0}; + #ifdef BACKLASH_DISTANCE_MM #if ENABLED(BACKLASH_GCODE) xyz_float_t Backlash::distance_mm = BACKLASH_DISTANCE_MM; @@ -38,7 +41,7 @@ #endif #if ENABLED(BACKLASH_GCODE) - uint8_t Backlash::correction = (BACKLASH_CORRECTION) * 0xFF; + uint8_t Backlash::correction = (BACKLASH_CORRECTION) * all_on; #ifdef BACKLASH_SMOOTHING_MM float Backlash::smoothing_mm = BACKLASH_SMOOTHING_MM; #endif @@ -61,10 +64,9 @@ Backlash backlash; */ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block) { - static axis_bits_t last_direction_bits; axis_bits_t changed_dir = last_direction_bits ^ dm; // Ignore direction change unless steps are taken in that direction - #if DISABLED(CORE_BACKLASH) || ENABLED(MARKFORGED_XY) + #if DISABLED(CORE_BACKLASH) || EITHER(MARKFORGED_XY, MARKFORGED_YX) if (!da) CBI(changed_dir, X_AXIS); if (!db) CBI(changed_dir, Y_AXIS); if (!dc) CBI(changed_dir, Z_AXIS); @@ -83,7 +85,7 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const #endif last_direction_bits ^= changed_dir; - if (correction == 0) return; + if (!correction && !residual_error) return; #ifdef BACKLASH_SMOOTHING_MM // The segment proportion is a value greater than 0.0 indicating how much residual_error @@ -91,39 +93,28 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const // smoothing distance. Since the computation of this proportion involves a floating point // division, defer computation until needed. float segment_proportion = 0; - - // Residual error carried forward across multiple segments, so correction can be applied - // to segments where there is no direction change. - static xyz_long_t residual_error{0}; - #else - // No direction change, no correction. - if (!changed_dir) return; - // No leftover residual error from segment to segment - xyz_long_t residual_error{0}; #endif - const float f_corr = float(correction) / 255.0f; + const float f_corr = float(correction) / all_on; - LOOP_LINEAR_AXES(axis) { + LOOP_NUM_AXES(axis) { if (distance_mm[axis]) { - const bool reversing = TEST(dm,axis); + const bool reverse = TEST(dm, axis); // When an axis changes direction, add axis backlash to the residual error if (TEST(changed_dir, axis)) - residual_error[axis] += (reversing ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; + residual_error[axis] += (reverse ? -f_corr : f_corr) * distance_mm[axis] * planner.settings.axis_steps_per_mm[axis]; // Decide how much of the residual error to correct in this segment int32_t error_correction = residual_error[axis]; + if (reverse != (error_correction < 0)) + error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps + #ifdef BACKLASH_SMOOTHING_MM if (error_correction && smoothing_mm != 0) { - // Take up a portion of the residual_error in this segment, but only when - // the current segment travels in the same direction as the correction - if (reversing == (error_correction < 0)) { - if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm); - error_correction = CEIL(segment_proportion * error_correction); - } - else - error_correction = 0; // Don't take up any backlash in this segment, as it would subtract steps + // Take up a portion of the residual_error in this segment + if (segment_proportion == 0) segment_proportion = _MIN(1.0f, block->millimeters / smoothing_mm); + error_correction = CEIL(segment_proportion * error_correction); } #endif @@ -153,6 +144,57 @@ void Backlash::add_correction_steps(const int32_t &da, const int32_t &db, const } } +int32_t Backlash::get_applied_steps(const AxisEnum axis) { + if (axis >= NUM_AXES) return 0; + + const bool reverse = TEST(last_direction_bits, axis); + + const int32_t residual_error_axis = residual_error[axis]; + + // At startup it is assumed the last move was forwards. So the applied + // steps will always be a non-positive number. + + if (!reverse) 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]; + return full_error_axis - residual_error_axis; +} + +class Backlash::StepAdjuster { + private: + xyz_long_t applied_steps; + public: + StepAdjuster() { + LOOP_NUM_AXES(axis) applied_steps[axis] = backlash.get_applied_steps((AxisEnum)axis); + } + ~StepAdjuster() { + // after backlash compensation parameter changes, ensure applied step count does not change + LOOP_NUM_AXES(axis) residual_error[axis] += backlash.get_applied_steps((AxisEnum)axis) - applied_steps[axis]; + } +}; + +#if ENABLED(BACKLASH_GCODE) + + void Backlash::set_correction_uint8(const uint8_t v) { + StepAdjuster adjuster; + correction = v; + } + + void Backlash::set_distance_mm(const AxisEnum axis, const float v) { + StepAdjuster adjuster; + distance_mm[axis] = v; + } + + #ifdef BACKLASH_SMOOTHING_MM + void Backlash::set_smoothing_mm(const float v) { + StepAdjuster adjuster; + smoothing_mm = v; + } + #endif + +#endif + #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) #include "../module/probe.h" diff --git a/Marlin/src/feature/backlash.h b/Marlin/src/feature/backlash.h index 4d4e2940382c2..0bace526e53f4 100644 --- a/Marlin/src/feature/backlash.h +++ b/Marlin/src/feature/backlash.h @@ -24,21 +24,22 @@ #include "../inc/MarlinConfigPre.h" #include "../module/planner.h" -constexpr uint8_t all_on = 0xFF, all_off = 0x00; - class Backlash { public: + static constexpr uint8_t all_on = 0xFF, all_off = 0x00; + +private: + static axis_bits_t last_direction_bits; + static xyz_long_t residual_error; + #if ENABLED(BACKLASH_GCODE) - static xyz_float_t distance_mm; static uint8_t correction; + static xyz_float_t distance_mm; #ifdef BACKLASH_SMOOTHING_MM static float smoothing_mm; #endif - - static inline void set_correction(const_float_t v) { correction = _MAX(0, _MIN(1.0, v)) * all_on; } - static inline float get_correction() { return float(ui8_to_percent(correction)) / 100.0f; } #else - static constexpr uint8_t correction = (BACKLASH_CORRECTION) * 0xFF; + static constexpr uint8_t correction = (BACKLASH_CORRECTION) * all_on; static const xyz_float_t distance_mm; #ifdef BACKLASH_SMOOTHING_MM static constexpr float smoothing_mm = BACKLASH_SMOOTHING_MM; @@ -46,14 +47,14 @@ class Backlash { #endif #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) - private: - static xyz_float_t measured_mm; - static xyz_uint8_t measured_count; - public: - static void measure_with_probe(); + static xyz_float_t measured_mm; + static xyz_uint8_t measured_count; #endif - static inline float get_measurement(const AxisEnum a) { + class StepAdjuster; + +public: + static float get_measurement(const AxisEnum a) { UNUSED(a); // Return the measurement averaged over all readings return TERN(MEASURE_BACKLASH_WHEN_PROBING @@ -62,16 +63,34 @@ class Backlash { ); } - static inline bool has_measurement(const AxisEnum a) { + static bool has_measurement(const AxisEnum a) { UNUSED(a); return TERN0(MEASURE_BACKLASH_WHEN_PROBING, measured_count[a] > 0); } - static inline bool has_any_measurement() { + static bool has_any_measurement() { return has_measurement(X_AXIS) || has_measurement(Y_AXIS) || has_measurement(Z_AXIS); } - void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block); + static void add_correction_steps(const int32_t &da, const int32_t &db, const int32_t &dc, const axis_bits_t dm, block_t * const block); + static int32_t get_applied_steps(const AxisEnum axis); + + #if ENABLED(BACKLASH_GCODE) + static void set_correction_uint8(const uint8_t v); + static uint8_t get_correction_uint8() { return correction; } + 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];} + #ifdef BACKLASH_SMOOTHING_MM + static void set_smoothing_mm(const float v); + static float get_smoothing_mm() {return smoothing_mm;} + #endif + #endif + + #if ENABLED(MEASURE_BACKLASH_WHEN_PROBING) + static void measure_with_probe(); + #endif }; extern Backlash backlash; diff --git a/Marlin/src/feature/bedlevel/abl/abl.h b/Marlin/src/feature/bedlevel/abl/abl.h deleted file mode 100644 index 3d54c55695e87..0000000000000 --- a/Marlin/src/feature/bedlevel/abl/abl.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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" - -extern xy_pos_t bilinear_grid_spacing, bilinear_start; -extern xy_float_t bilinear_grid_factor; -extern bed_mesh_t z_values; -float bilinear_z_offset(const xy_pos_t &raw); - -void extrapolate_unprobed_bed_level(); -void print_bilinear_leveling_grid(); -void refresh_bed_level(); -#if ENABLED(ABL_BILINEAR_SUBDIVISION) - void print_bilinear_leveling_grid_virt(); - void bed_level_virt_interpolate(); -#endif - -#if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) - void bilinear_line_to_destination(const_feedRate_t scaled_fr_mm_s, uint16_t x_splits=0xFFFF, uint16_t y_splits=0xFFFF); -#endif - -#define _GET_MESH_X(I) float(bilinear_start.x + (I) * bilinear_grid_spacing.x) -#define _GET_MESH_Y(J) float(bilinear_start.y + (J) * bilinear_grid_spacing.y) -#define Z_VALUES_ARR z_values diff --git a/Marlin/src/feature/bedlevel/abl/abl.cpp b/Marlin/src/feature/bedlevel/abl/bbl.cpp similarity index 78% rename from Marlin/src/feature/bedlevel/abl/abl.cpp rename to Marlin/src/feature/bedlevel/abl/bbl.cpp index ece7481981955..be0e862cc16a1 100644 --- a/Marlin/src/feature/bedlevel/abl/abl.cpp +++ b/Marlin/src/feature/bedlevel/abl/bbl.cpp @@ -35,14 +35,19 @@ #include "../../../lcd/extui/ui_api.h" #endif -xy_pos_t bilinear_grid_spacing, bilinear_start; -xy_float_t bilinear_grid_factor; -bed_mesh_t z_values; +LevelingBilinear bedlevel; + +xy_pos_t LevelingBilinear::grid_spacing, + LevelingBilinear::grid_start; +xy_float_t LevelingBilinear::grid_factor; +bed_mesh_t LevelingBilinear::z_values; +xy_pos_t LevelingBilinear::cached_rel; +xy_int8_t LevelingBilinear::cached_g; /** * Extrapolate a single point from its neighbors */ -static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) { +void LevelingBilinear::extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir) { if (!isnan(z_values[x][y])) return; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPGM("Extrapolate ["); @@ -92,11 +97,26 @@ static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t #endif #endif +void LevelingBilinear::reset() { + grid_start.reset(); + grid_spacing.reset(); + GRID_LOOP(x, y) { + z_values[x][y] = NAN; + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0)); + } +} + +void LevelingBilinear::set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start) { + grid_spacing = _grid_spacing; + grid_start = _grid_start; + grid_factor = grid_spacing.reciprocal(); +} + /** * Fill in the unprobed points (corners of circular print surface) * using linear extrapolation, away from the center. */ -void extrapolate_unprobed_bed_level() { +void LevelingBilinear::extrapolate_unprobed_bed_level() { #ifdef HALF_IN_X constexpr uint8_t ctrx2 = 0, xend = GRID_MAX_POINTS_X - 1; #else @@ -131,35 +151,31 @@ void extrapolate_unprobed_bed_level() { #endif extrapolate_one_point(x2, y2, -1, -1); // right-above - - } - } -void print_bilinear_leveling_grid() { +void LevelingBilinear::print_leveling_grid(const bed_mesh_t* _z_values /*= NULL*/) { + // print internal grid(s) or just the one passed as a parameter SERIAL_ECHOLNPGM("Bilinear Leveling Grid:"); - print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 3, - [](const uint8_t ix, const uint8_t iy) { return z_values[ix][iy]; } - ); + print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 3, _z_values ? *_z_values[0] : z_values[0]); + + #if ENABLED(ABL_BILINEAR_SUBDIVISION) + if (!_z_values) { + SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:"); + print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5, z_values_virt[0]); + } + #endif } #if ENABLED(ABL_BILINEAR_SUBDIVISION) - #define ABL_GRID_POINTS_VIRT_X GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1 - #define ABL_GRID_POINTS_VIRT_Y GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1 #define ABL_TEMP_POINTS_X (GRID_MAX_POINTS_X + 2) #define ABL_TEMP_POINTS_Y (GRID_MAX_POINTS_Y + 2) - float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y]; - xy_pos_t bilinear_grid_spacing_virt; - xy_float_t bilinear_grid_factor_virt; - - void print_bilinear_leveling_grid_virt() { - SERIAL_ECHOLNPGM("Subdivided with CATMULL ROM Leveling Grid:"); - print_2d_array(ABL_GRID_POINTS_VIRT_X, ABL_GRID_POINTS_VIRT_Y, 5, - [](const uint8_t ix, const uint8_t iy) { return z_values_virt[ix][iy]; } - ); - } + float LevelingBilinear::z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y]; + xy_pos_t LevelingBilinear::grid_spacing_virt; + xy_float_t LevelingBilinear::grid_factor_virt; #define LINEAR_EXTRAPOLATION(E, I) ((E) * 2 - (I)) - float bed_level_virt_coord(const uint8_t x, const uint8_t y) { + float LevelingBilinear::bed_level_virt_coord(const uint8_t x, const uint8_t y) { uint8_t ep = 0, ip = 1; if (x > (GRID_MAX_POINTS_X) + 1 || y > (GRID_MAX_POINTS_Y) + 1) { // The requested point requires extrapolating two points beyond the mesh. @@ -204,7 +220,7 @@ void print_bilinear_leveling_grid() { return z_values[x - 1][y - 1]; } - static float bed_level_virt_cmr(const float p[4], const uint8_t i, const float t) { + float LevelingBilinear::bed_level_virt_cmr(const float p[4], const uint8_t i, const float t) { return ( p[i-1] * -t * sq(1 - t) + p[i] * (2 - 5 * sq(t) + 3 * t * sq(t)) @@ -213,7 +229,7 @@ void print_bilinear_leveling_grid() { ) * 0.5f; } - static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) { + float LevelingBilinear::bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty) { float row[4], column[4]; LOOP_L_N(i, 4) { LOOP_L_N(j, 4) { @@ -224,9 +240,9 @@ void print_bilinear_leveling_grid() { return bed_level_virt_cmr(row, 1, tx); } - void bed_level_virt_interpolate() { - bilinear_grid_spacing_virt = bilinear_grid_spacing / (BILINEAR_SUBDIVISIONS); - bilinear_grid_factor_virt = bilinear_grid_spacing_virt.reciprocal(); + void LevelingBilinear::bed_level_virt_interpolate() { + grid_spacing_virt = grid_spacing / (BILINEAR_SUBDIVISIONS); + grid_factor_virt = grid_spacing_virt.reciprocal(); LOOP_L_N(y, GRID_MAX_POINTS_Y) LOOP_L_N(x, GRID_MAX_POINTS_X) LOOP_L_N(ty, BILINEAR_SUBDIVISIONS) @@ -242,40 +258,42 @@ void print_bilinear_leveling_grid() { ); } } + #endif // ABL_BILINEAR_SUBDIVISION // Refresh after other values have been updated -void refresh_bed_level() { - bilinear_grid_factor = bilinear_grid_spacing.reciprocal(); +void LevelingBilinear::refresh_bed_level() { TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); + cached_rel.x = cached_rel.y = -999.999; + cached_g.x = cached_g.y = -99; } #if ENABLED(ABL_BILINEAR_SUBDIVISION) - #define ABL_BG_SPACING(A) bilinear_grid_spacing_virt.A - #define ABL_BG_FACTOR(A) bilinear_grid_factor_virt.A + #define ABL_BG_SPACING(A) grid_spacing_virt.A + #define ABL_BG_FACTOR(A) grid_factor_virt.A #define ABL_BG_POINTS_X ABL_GRID_POINTS_VIRT_X #define ABL_BG_POINTS_Y ABL_GRID_POINTS_VIRT_Y #define ABL_BG_GRID(X,Y) z_values_virt[X][Y] #else - #define ABL_BG_SPACING(A) bilinear_grid_spacing.A - #define ABL_BG_FACTOR(A) bilinear_grid_factor.A + #define ABL_BG_SPACING(A) grid_spacing.A + #define ABL_BG_FACTOR(A) grid_factor.A #define ABL_BG_POINTS_X GRID_MAX_POINTS_X #define ABL_BG_POINTS_Y GRID_MAX_POINTS_Y #define ABL_BG_GRID(X,Y) z_values[X][Y] #endif // Get the Z adjustment for non-linear bed leveling -float bilinear_z_offset(const xy_pos_t &raw) { +float LevelingBilinear::get_z_correction(const xy_pos_t &raw) { static float z1, d2, z3, d4, L, D; - static xy_pos_t prev { -999.999, -999.999 }, ratio; + static xy_pos_t ratio; // Whole units for the grid line indices. Constrained within bounds. - static xy_int8_t thisg, nextg, lastg { -99, -99 }; + static xy_int8_t thisg, nextg; // XY relative to the probed area - xy_pos_t rel = raw - bilinear_start.asFloat(); + xy_pos_t rel = raw - grid_start.asFloat(); #if ENABLED(EXTRAPOLATE_BEYOND_GRID) #define FAR_EDGE_OR_BOX 2 // Keep using the last grid box @@ -283,8 +301,8 @@ float bilinear_z_offset(const xy_pos_t &raw) { #define FAR_EDGE_OR_BOX 1 // Just use the grid far edge #endif - if (prev.x != rel.x) { - prev.x = rel.x; + if (cached_rel.x != rel.x) { + cached_rel.x = rel.x; ratio.x = rel.x * ABL_BG_FACTOR(x); const float gx = constrain(FLOOR(ratio.x), 0, ABL_BG_POINTS_X - (FAR_EDGE_OR_BOX)); ratio.x -= gx; // Subtract whole to get the ratio within the grid box @@ -298,10 +316,10 @@ float bilinear_z_offset(const xy_pos_t &raw) { nextg.x = _MIN(thisg.x + 1, ABL_BG_POINTS_X - 1); } - if (prev.y != rel.y || lastg.x != thisg.x) { + if (cached_rel.y != rel.y || cached_g.x != thisg.x) { - if (prev.y != rel.y) { - prev.y = rel.y; + if (cached_rel.y != rel.y) { + cached_rel.y = rel.y; ratio.y = rel.y * ABL_BG_FACTOR(y); const float gy = constrain(FLOOR(ratio.y), 0, ABL_BG_POINTS_Y - (FAR_EDGE_OR_BOX)); ratio.y -= gy; @@ -315,8 +333,8 @@ float bilinear_z_offset(const xy_pos_t &raw) { nextg.y = _MIN(thisg.y + 1, ABL_BG_POINTS_Y - 1); } - if (lastg != thisg) { - lastg = thisg; + if (cached_g != thisg) { + cached_g = thisg; // Z at the box corners z1 = ABL_BG_GRID(thisg.x, thisg.y); // left-front d2 = ABL_BG_GRID(thisg.x, nextg.y) - z1; // left-back (delta) @@ -336,8 +354,8 @@ float bilinear_z_offset(const xy_pos_t &raw) { /* static float last_offset = 0; if (ABS(last_offset - offset) > 0.2) { - SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", bilinear_grid_spacing.x, " -> thisg.x=", thisg.x); - SERIAL_ECHOLNPGM(" y=", rel.y, " / ", bilinear_grid_spacing.y, " -> thisg.y=", thisg.y); + SERIAL_ECHOLNPGM("Sudden Shift at x=", rel.x, " / ", grid_spacing.x, " -> thisg.x=", thisg.x); + SERIAL_ECHOLNPGM(" y=", rel.y, " / ", grid_spacing.y, " -> thisg.y=", thisg.y); SERIAL_ECHOLNPGM(" ratio.x=", ratio.x, " ratio.y=", ratio.y); SERIAL_ECHOLNPGM(" z1=", z1, " z2=", z2, " z3=", z3, " z4=", z4); SERIAL_ECHOLNPGM(" L=", L, " R=", R, " offset=", offset); @@ -350,13 +368,13 @@ float bilinear_z_offset(const xy_pos_t &raw) { #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) - #define CELL_INDEX(A,V) ((V - bilinear_start.A) * ABL_BG_FACTOR(A)) + #define CELL_INDEX(A,V) ((V - grid_start.A) * ABL_BG_FACTOR(A)) /** * Prepare a bilinear-leveled linear move on Cartesian, * splitting the move where it crosses grid borders. */ - void bilinear_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) }; @@ -384,7 +402,7 @@ float bilinear_z_offset(const xy_pos_t &raw) { // Split on the X grid line CBI(x_splits, gc.x); end = destination; - destination.x = bilinear_start.x + ABL_BG_SPACING(x) * gc.x; + destination.x = grid_start.x + ABL_BG_SPACING(x) * gc.x; normalized_dist = (destination.x - current_position.x) / (end.x - current_position.x); destination.y = LINE_SEGMENT_END(y); } @@ -393,7 +411,7 @@ float bilinear_z_offset(const xy_pos_t &raw) { // Split on the Y grid line CBI(y_splits, gc.y); end = destination; - destination.y = bilinear_start.y + ABL_BG_SPACING(y) * gc.y; + destination.y = grid_start.y + ABL_BG_SPACING(y) * gc.y; normalized_dist = (destination.y - current_position.y) / (end.y - current_position.y); destination.x = LINE_SEGMENT_END(x); } @@ -409,11 +427,11 @@ float bilinear_z_offset(const xy_pos_t &raw) { destination.e = LINE_SEGMENT_END(e); // Do the split and look for more borders - bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits); + line_to_destination(scaled_fr_mm_s, x_splits, y_splits); // Restore destination from stack destination = end; - bilinear_line_to_destination(scaled_fr_mm_s, x_splits, y_splits); + line_to_destination(scaled_fr_mm_s, x_splits, y_splits); } #endif // IS_CARTESIAN && !SEGMENT_LEVELED_MOVES diff --git a/Marlin/src/feature/bedlevel/abl/bbl.h b/Marlin/src/feature/bedlevel/abl/bbl.h new file mode 100644 index 0000000000000..c2be4fee821c4 --- /dev/null +++ b/Marlin/src/feature/bedlevel/abl/bbl.h @@ -0,0 +1,70 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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" + +class LevelingBilinear { +public: + static bed_mesh_t z_values; + static xy_pos_t grid_spacing, grid_start; + +private: + static xy_float_t grid_factor; + static xy_pos_t cached_rel; + static xy_int8_t cached_g; + + static void extrapolate_one_point(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir); + + #if ENABLED(ABL_BILINEAR_SUBDIVISION) + #define ABL_GRID_POINTS_VIRT_X (GRID_MAX_CELLS_X * (BILINEAR_SUBDIVISIONS) + 1) + #define ABL_GRID_POINTS_VIRT_Y (GRID_MAX_CELLS_Y * (BILINEAR_SUBDIVISIONS) + 1) + + static float z_values_virt[ABL_GRID_POINTS_VIRT_X][ABL_GRID_POINTS_VIRT_Y]; + static xy_pos_t grid_spacing_virt; + static xy_float_t grid_factor_virt; + + static float bed_level_virt_coord(const uint8_t x, const uint8_t y); + static float bed_level_virt_cmr(const float p[4], const uint8_t i, const float t); + static float bed_level_virt_2cmr(const uint8_t x, const uint8_t y, const_float_t tx, const_float_t ty); + static void bed_level_virt_interpolate(); + #endif + +public: + static void reset(); + static void set_grid(const xy_pos_t& _grid_spacing, const xy_pos_t& _grid_start); + static void extrapolate_unprobed_bed_level(); + static void print_leveling_grid(const bed_mesh_t* _z_values = NULL); + static void refresh_bed_level(); + static bool has_mesh() { return !!grid_spacing.x; } + static bool mesh_is_valid() { return has_mesh(); } + static float get_mesh_x(const uint8_t i) { return grid_start.x + i * grid_spacing.x; } + static float get_mesh_y(const uint8_t j) { return grid_start.y + j * grid_spacing.y; } + static float get_z_correction(const xy_pos_t &raw); + 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); + #endif +}; + +extern LevelingBilinear bedlevel; diff --git a/Marlin/src/feature/bedlevel/bedlevel.cpp b/Marlin/src/feature/bedlevel/bedlevel.cpp index 8e03632de44d5..2207884c36376 100644 --- a/Marlin/src/feature/bedlevel/bedlevel.cpp +++ b/Marlin/src/feature/bedlevel/bedlevel.cpp @@ -47,14 +47,11 @@ #endif bool leveling_is_valid() { - return TERN1(MESH_BED_LEVELING, mbl.has_mesh()) - && TERN1(AUTO_BED_LEVELING_BILINEAR, !!bilinear_grid_spacing.x) - && TERN1(AUTO_BED_LEVELING_UBL, ubl.mesh_is_valid()); + return TERN1(HAS_MESH, bedlevel.mesh_is_valid()); } /** - * Turn bed leveling on or off, fixing the current - * position as-needed. + * Turn bed leveling on or off, correcting the current position. * * Disable: Current position = physical position * Enable: Current position = "unleveled" physical position @@ -65,30 +62,25 @@ void set_bed_leveling_enabled(const bool enable/*=true*/) { if (can_change && enable != planner.leveling_active) { + auto _report_leveling = []{ + if (DEBUGGING(LEVELING)) { + if (planner.leveling_active) + DEBUG_POS("Leveling ON", current_position); + else + DEBUG_POS("Leveling OFF", current_position); + } + }; + + _report_leveling(); planner.synchronize(); - #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - // Force bilinear_z_offset to re-calculate next time - const xyz_pos_t reset { -9999.999, -9999.999, 0 }; - (void)bilinear_z_offset(reset); - #endif - - if (planner.leveling_active) { // leveling from on to off - if (DEBUGGING(LEVELING)) DEBUG_POS("Leveling ON", current_position); - // change unleveled current_position to physical current_position without moving steppers. - planner.apply_leveling(current_position); - planner.leveling_active = false; // disable only AFTER calling apply_leveling - if (DEBUGGING(LEVELING)) DEBUG_POS("...Now OFF", current_position); - } - else { // leveling from off to on - if (DEBUGGING(LEVELING)) DEBUG_POS("Leveling OFF", current_position); - planner.leveling_active = true; // enable BEFORE calling unapply_leveling, otherwise ignored - // change physical current_position to unleveled current_position without moving steppers. - planner.unapply_leveling(current_position); - if (DEBUGGING(LEVELING)) DEBUG_POS("...Now ON", current_position); - } + // Get the corrected leveled / unleveled position + planner.apply_modifiers(current_position); // Physical position with all modifiers + planner.leveling_active ^= true; // Toggle leveling between apply and unapply + planner.unapply_modifiers(current_position); // Logical position with modifiers removed sync_plan_position(); + _report_leveling(); } } @@ -122,23 +114,9 @@ TemporaryBedLevelingState::TemporaryBedLevelingState(const bool enable) : saved( */ void reset_bed_level() { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("reset_bed_level"); - #if ENABLED(AUTO_BED_LEVELING_UBL) - ubl.reset(); - #else - set_bed_leveling_enabled(false); - #if ENABLED(MESH_BED_LEVELING) - mbl.reset(); - #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) - bilinear_start.reset(); - bilinear_grid_spacing.reset(); - GRID_LOOP(x, y) { - z_values[x][y] = NAN; - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, 0)); - } - #elif ABL_PLANAR - planner.bed_level_matrix.set_to_identity(); - #endif - #endif + IF_DISABLED(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(false)); + TERN_(HAS_MESH, bedlevel.reset()); + TERN_(ABL_PLANAR, planner.bed_level_matrix.set_to_identity()); } #if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING) @@ -156,7 +134,7 @@ void reset_bed_level() { /** * Print calibration results for plotting or manual frame adjustment. */ - void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, element_2d_fn fn) { + void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, const float *values) { #ifndef SCAD_MESH_OUTPUT LOOP_L_N(x, sx) { serial_spaces(precision + (x < 10 ? 3 : 2)); @@ -176,7 +154,7 @@ void reset_bed_level() { #endif LOOP_L_N(x, sx) { SERIAL_CHAR(' '); - const float offset = fn(x, y); + const float offset = values[x * sy + y]; if (!isnan(offset)) { if (offset >= 0) SERIAL_CHAR('+'); SERIAL_ECHO_F(offset, int(precision)); diff --git a/Marlin/src/feature/bedlevel/bedlevel.h b/Marlin/src/feature/bedlevel/bedlevel.h index 63f032eee87bc..aeafec10d6abc 100644 --- a/Marlin/src/feature/bedlevel/bedlevel.h +++ b/Marlin/src/feature/bedlevel/bedlevel.h @@ -62,16 +62,13 @@ class TemporaryBedLevelingState { typedef float bed_mesh_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y]; #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - #include "abl/abl.h" + #include "abl/bbl.h" #elif ENABLED(AUTO_BED_LEVELING_UBL) #include "ubl/ubl.h" #elif ENABLED(MESH_BED_LEVELING) #include "mbl/mesh_bed_leveling.h" #endif - #define Z_VALUES(X,Y) Z_VALUES_ARR[X][Y] - #define _GET_MESH_POS(M) { _GET_MESH_X(M.a), _GET_MESH_Y(M.b) } - #if EITHER(AUTO_BED_LEVELING_BILINEAR, MESH_BED_LEVELING) #include @@ -81,7 +78,7 @@ class TemporaryBedLevelingState { /** * Print calibration results for plotting or manual frame adjustment. */ - void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, element_2d_fn fn); + void print_2d_array(const uint8_t sx, const uint8_t sy, const uint8_t precision, const float *values); #endif @@ -92,7 +89,7 @@ class TemporaryBedLevelingState { bool valid() const { return pos.x >= 0 && pos.y >= 0; } #if ENABLED(AUTO_BED_LEVELING_UBL) xy_pos_t meshpos() { - return { ubl.mesh_index_to_xpos(pos.x), ubl.mesh_index_to_ypos(pos.y) }; + return { bedlevel.get_mesh_x(pos.x), bedlevel.get_mesh_y(pos.y) }; } #endif operator xy_int8_t&() { return pos; } diff --git a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp index 51cf28f890057..193cbbf7654ab 100644 --- a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp +++ b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.cpp @@ -32,7 +32,7 @@ #include "../../../lcd/extui/ui_api.h" #endif - mesh_bed_leveling mbl; + mesh_bed_leveling bedlevel; float mesh_bed_leveling::z_offset, mesh_bed_leveling::z_values[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y], @@ -125,9 +125,7 @@ void mesh_bed_leveling::report_mesh() { SERIAL_ECHOPAIR_F(STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_Y) " mesh. Z offset: ", z_offset, 5); SERIAL_ECHOLNPGM("\nMeasured points:"); - print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 5, - [](const uint8_t ix, const uint8_t iy) { return z_values[ix][iy]; } - ); + print_2d_array(GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y, 5, z_values[0]); } #endif // MESH_BED_LEVELING diff --git a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h index cc54695771893..1a8e693e81806 100644 --- a/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h +++ b/Marlin/src/feature/bedlevel/mbl/mesh_bed_leveling.h @@ -34,9 +34,6 @@ enum MeshLevelingState : char { #define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X)) #define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y)) -#define _GET_MESH_X(I) mbl.index_to_xpos[I] -#define _GET_MESH_Y(J) mbl.index_to_ypos[J] -#define Z_VALUES_ARR mbl.z_values class mesh_bed_leveling { public: @@ -56,9 +53,11 @@ class mesh_bed_leveling { return false; } + 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 inline void zigzag(const int8_t index, int8_t &px, int8_t &py) { + static void zigzag(const int8_t index, int8_t &px, int8_t &py) { px = index % (GRID_MAX_POINTS_X); py = index / (GRID_MAX_POINTS_X); if (py & 1) px = (GRID_MAX_POINTS_X) - 1 - px; // Zig zag @@ -70,6 +69,9 @@ class mesh_bed_leveling { set_z(px, py, z); } + 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 int8_t cell_index_x(const_float_t x) { int8_t cx = (x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST); return constrain(cx, 0, GRID_MAX_CELLS_X - 1); @@ -78,10 +80,10 @@ class mesh_bed_leveling { int8_t cy = (y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST); return constrain(cy, 0, GRID_MAX_CELLS_Y - 1); } - static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) { + static xy_int8_t cell_indexes(const_float_t x, const_float_t y) { return { cell_index_x(x), cell_index_y(y) }; } - static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } + static xy_int8_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) { int8_t px = (x - (MESH_MIN_X) + 0.5f * (MESH_X_DIST)) * RECIPROCAL(MESH_X_DIST); @@ -91,10 +93,10 @@ class mesh_bed_leveling { 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 inline xy_int8_t probe_indexes(const_float_t x, const_float_t y) { + static xy_int8_t probe_indexes(const_float_t x, const_float_t y) { return { probe_index_x(x), probe_index_y(y) }; } - static inline xy_int8_t probe_indexes(const xy_pos_t &xy) { return probe_indexes(xy.x, xy.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) { const float delta_z = (z2 - z1) / (a2 - a1), @@ -102,12 +104,9 @@ class mesh_bed_leveling { return z1 + delta_a * delta_z; } - static float get_z(const xy_pos_t &pos - OPTARG(ENABLE_LEVELING_FADE_HEIGHT, const_float_t factor=1.0f) - ) { - #if DISABLED(ENABLE_LEVELING_FADE_HEIGHT) - constexpr float factor = 1.0f; - #endif + static float get_z_offset() { return z_offset; } + + static float get_z_correction(const xy_pos_t &pos) { const xy_int8_t ind = cell_indexes(pos); const float x1 = index_to_xpos[ind.x], x2 = index_to_xpos[ind.x+1], y1 = index_to_xpos[ind.y], y2 = index_to_xpos[ind.y+1], @@ -115,7 +114,7 @@ class mesh_bed_leveling { z2 = calc_z0(pos.x, x1, z_values[ind.x][ind.y+1], x2, z_values[ind.x+1][ind.y+1]), zf = calc_z0(pos.y, y1, z1, y2, z2); - return z_offset + zf * factor; + return zf; } #if IS_CARTESIAN && DISABLED(SEGMENT_LEVELED_MOVES) @@ -123,4 +122,4 @@ class mesh_bed_leveling { #endif }; -extern mesh_bed_leveling mbl; +extern mesh_bed_leveling bedlevel; diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.cpp b/Marlin/src/feature/bedlevel/ubl/ubl.cpp index 00cb5ed7382e9..2aa50be34d262 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl.cpp @@ -26,7 +26,7 @@ #include "../bedlevel.h" -unified_bed_leveling ubl; +unified_bed_leveling bedlevel; #include "../../../MarlinCore.h" #include "../../../gcode/gcode.h" @@ -180,10 +180,8 @@ void unified_bed_leveling::display_map(const uint8_t map_type) { SERIAL_EOL(); serial_echo_column_labels(eachsp - 2); } - else { - SERIAL_ECHOPGM(" for "); - SERIAL_ECHOPGM_P(csv ? PSTR("CSV:\n") : PSTR("LCD:\n")); - } + else + SERIAL_ECHOPGM(" for ", csv ? F("CSV:\n") : F("LCD:\n")); // Add XY probe offset from extruder because probe.probe_at_point() subtracts them when // moving to the XY position to be measured. This ensures better agreement between @@ -213,10 +211,10 @@ void unified_bed_leveling::display_map(const uint8_t map_type) { // TODO: Display on Graphical LCD } else if (isnan(f)) - SERIAL_ECHOPGM_P(human ? PSTR(" . ") : PSTR("NAN")); + SERIAL_ECHOF(human ? F(" . ") : F("NAN")); else if (human || csv) { - if (human && f >= 0.0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0) - SERIAL_ECHO_F(f, 3); // Positive: 5 digits, Negative: 6 digits + if (human && f >= 0) SERIAL_CHAR(f > 0 ? '+' : ' '); // Display sign also for positive numbers (' ' for 0) + SERIAL_DECIMAL(f); // Positive: 5 digits, Negative: 6 digits } if (csv && i < (GRID_MAX_POINTS_X) - 1) SERIAL_CHAR('\t'); @@ -281,10 +279,10 @@ bool unified_bed_leveling::sanity_check() { } #endif - process_subcommands_now_P(G28_STR); // Home - process_subcommands_now_P(PSTR(ALIGN_GCODE "\n" // Align multi z axis if available - PROBE_GCODE "\n" // Build mesh with available hardware - "G29P3\nG29P3")); // Ensure mesh is complete by running smart fill twice + process_subcommands_now(FPSTR(G28_STR)); // Home + process_subcommands_now(F(ALIGN_GCODE "\n" // Align multi z axis if available + PROBE_GCODE "\n" // Build mesh with available hardware + "G29P3\nG29P3")); // Ensure mesh is complete by running smart fill twice if (parser.seenval('S')) { char umw_gcode[32]; @@ -292,9 +290,9 @@ bool unified_bed_leveling::sanity_check() { queue.inject(umw_gcode); } - process_subcommands_now_P(PSTR("G29A\nG29F10\n" // Set UBL Active & Fade 10 - "M140S0\nM104S0\n" // Turn off heaters - "M500")); // Store settings + process_subcommands_now(F("G29A\nG29F10\n" // Set UBL Active & Fade 10 + "M140S0\nM104S0\n" // Turn off heaters + "M500")); // Store settings } #endif // UBL_MESH_WIZARD diff --git a/Marlin/src/feature/bedlevel/ubl/ubl.h b/Marlin/src/feature/bedlevel/ubl/ubl.h index ffabadd990b38..a7103d6e18c46 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl.h +++ b/Marlin/src/feature/bedlevel/ubl/ubl.h @@ -70,20 +70,19 @@ class unified_bed_leveling { static void move_z_with_encoder(const_float_t 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 fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0; + static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , 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 bool G29_parse_parameters() __O0; static void shift_mesh_height(); - 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 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_3pts(const_float_t z1, const_float_t z2, const_float_t z3); 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); - static inline bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) { + static bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) { return smart_fill_one(pos.x, pos.y, dir.x, dir.y); } - static void smart_fill_mesh(); #if ENABLED(UBL_DEVEL_DEBUGGING) static void g29_what_command(); @@ -98,17 +97,18 @@ class unified_bed_leveling { static void report_state(); static void save_ubl_active_state_and_disable(); static void restore_ubl_active_state_and_leave(); - static void display_map(const uint8_t) _O0; - static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0; - static mesh_index_pair find_furthest_invalid_mesh_point() _O0; + static void display_map(const uint8_t) __O0; + static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) __O0; + 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 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 G29() __O0; // O0 for no optimization + static void smart_fill_wlsf(const_float_t ) __O2; // O2 gives smaller code than Os on A2560 static int8_t storage_slot; @@ -120,11 +120,11 @@ class unified_bed_leveling { static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X], _mesh_index_to_ypos[GRID_MAX_POINTS_Y]; - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU static bool lcd_map_control; static void steppers_were_disabled(); #else - static inline void steppers_were_disabled() {} + static void steppers_were_disabled() {} #endif static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time @@ -157,10 +157,10 @@ class unified_bed_leveling { return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1); } - static inline xy_int8_t cell_indexes(const_float_t x, const_float_t y) { + static xy_int8_t cell_indexes(const_float_t x, const_float_t y) { return { cell_index_x(x), cell_index_y(y) }; } - static inline xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); } + static xy_int8_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) { const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST); @@ -170,7 +170,7 @@ class unified_bed_leveling { 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; } - static inline xy_int8_t closest_indexes(const xy_pos_t &xy) { + static xy_int8_t closest_indexes(const xy_pos_t &xy) { return { closest_x_index(xy.x), closest_y_index(xy.y) }; } @@ -203,7 +203,7 @@ class unified_bed_leveling { * 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 inline 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_t 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)) { @@ -215,7 +215,7 @@ class unified_bed_leveling { return _UBL_OUTER_Z_RAISE; } - const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST), + const float xratio = (rx0 - get_mesh_x(x1_i)) * RECIPROCAL(MESH_X_DIST), z1 = z_values[x1_i][yi]; return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array @@ -226,7 +226,7 @@ class unified_bed_leveling { // // See comments above for z_correction_for_x_on_horizontal_mesh_line // - static inline 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_t 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)) { @@ -238,7 +238,7 @@ class unified_bed_leveling { return _UBL_OUTER_Z_RAISE; } - const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST), + const float yratio = (ry0 - get_mesh_y(y1_i)) * RECIPROCAL(MESH_Y_DIST), z1 = z_values[xi][y1_i]; return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array @@ -264,16 +264,17 @@ class unified_bed_leveling { return UBL_Z_RAISE_WHEN_OFF_MESH; #endif - const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1; - const float z1 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][cy], mesh_index_to_xpos(cx + 1), z_values[mx][cy]); - const float z2 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][my], mesh_index_to_xpos(cx + 1), z_values[mx][my]); - float z0 = calc_z0(ry0, mesh_index_to_ypos(cy), z1, mesh_index_to_ypos(cy + 1), z2); + const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1, + x0 = get_mesh_x(cx), x1 = get_mesh_x(cx + 1); + const float z1 = calc_z0(rx0, x0, z_values[cx][cy], x1, z_values[mx][cy]), + z2 = calc_z0(rx0, x0, z_values[cx][my], x1, z_values[mx][my]); + float z0 = calc_z0(ry0, get_mesh_y(cy), z1, get_mesh_y(cy + 1), z2); - if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN - z0 = 0.0; // in ubl.z_values[][] and propagate through the - // calculations. If our correction is NAN, we throw it out - // because part of the Mesh is undefined and we don't have the - // information we need to complete the height correction. + if (isnan(z0)) { // If part of the Mesh is undefined, it will show up as NAN + z0 = 0.0; // in z_values[][] and propagate through the calculations. + // If our correction is NAN, we throw it out because part of + // the Mesh is undefined and we don't have the information + // needed to complete the height correction. if (DEBUGGING(MESH_ADJUST)) DEBUG_ECHOLNPGM("??? Yikes! NAN in "); } @@ -285,12 +286,14 @@ class unified_bed_leveling { return z0; } - static inline float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); } + static float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); } - static inline float mesh_index_to_xpos(const uint8_t i) { + static constexpr float get_z_offset() { return 0.0f; } + + static float get_mesh_x(const uint8_t i) { return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST); } - static inline float mesh_index_to_ypos(const uint8_t i) { + static float get_mesh_y(const uint8_t i) { return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST); } @@ -300,18 +303,14 @@ class unified_bed_leveling { static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e); #endif - static inline bool mesh_is_valid() { + static bool mesh_is_valid() { GRID_LOOP(x, y) if (isnan(z_values[x][y])) return false; return true; } }; // class unified_bed_leveling -extern unified_bed_leveling ubl; - -#define _GET_MESH_X(I) ubl.mesh_index_to_xpos(I) -#define _GET_MESH_Y(J) ubl.mesh_index_to_ypos(J) -#define Z_VALUES_ARR ubl.z_values +extern unified_bed_leveling bedlevel; // Prevent debugging propagating to other files #include "../../../core/debug_out.h" diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp index 750c63f7c64bc..6b45e483ad9d9 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_G29.cpp @@ -31,7 +31,6 @@ #include "../../../libs/hex_print.h" #include "../../../module/settings.h" #include "../../../lcd/marlinui.h" -#include "../../../module/stepper.h" #include "../../../module/planner.h" #include "../../../module/motion.h" #include "../../../module/probe.h" @@ -57,7 +56,7 @@ #define UBL_G29_P31 -#if HAS_LCD_MENU +#if HAS_MARLINUI_MENU bool unified_bed_leveling::lcd_map_control = false; @@ -316,7 +315,7 @@ void unified_bed_leveling::G29() { planner.synchronize(); // Send 'N' to force homing before G29 (internal only) if (axes_should_home() || parser.seen_test('N')) gcode.home_all_axes(); - TERN_(HAS_MULTI_HOTEND, if (active_extruder) tool_change(0)); + TERN_(HAS_MULTI_HOTEND, if (active_extruder != 0) tool_change(0, true)); } // Invalidate one or more nearby mesh points, possibly all. @@ -346,13 +345,14 @@ 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, -1, 2)) { - SERIAL_ECHOLNPGM("Invalid test_pattern value. (-1 to 2)\n"); + 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"); return; } - SERIAL_ECHOLNPGM("Loading test_pattern values.\n"); + SERIAL_ECHOLNPGM("Applying test pattern.\n"); switch (test_pattern) { + default: case -1: TERN_(UBL_DEVEL_DEBUGGING, g29_eeprom_dump()); break; case 0: @@ -366,13 +366,13 @@ void unified_bed_leveling::G29() { case 1: LOOP_L_N(x, GRID_MAX_POINTS_X) { // Create a diagonal line several Mesh cells thick that is raised + const uint8_t x2 = x + (x < (GRID_MAX_POINTS_Y) - 1 ? 1 : -1); z_values[x][x] += 9.999f; - z_values[x][x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1] += 9.999f; // We want the altered line several mesh points thick + z_values[x][x2] += 9.999f; // We want the altered line several mesh points thick #if ENABLED(EXTENSIBLE_UI) ExtUI::onMeshUpdate(x, x, z_values[x][x]); - ExtUI::onMeshUpdate(x, (x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1), z_values[x][x + (x < (GRID_MAX_POINTS_Y) - 1) ? 1 : -1]); + ExtUI::onMeshUpdate(x, (x2), z_values[x][x2]); #endif - } break; @@ -442,7 +442,7 @@ void unified_bed_leveling::G29() { #endif // HAS_BED_PROBE case 2: { - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU // // Manually Probe Mesh in areas that can't be reached by the probe // @@ -554,7 +554,7 @@ void unified_bed_leveling::G29() { } case 4: // Fine Tune (i.e., Edit) the Mesh - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU fine_tune_mesh(param.XY_pos, parser.seen_test('T')); #else SERIAL_ECHOLNPGM("?P4 is only available when an LCD is present."); @@ -645,7 +645,7 @@ void unified_bed_leveling::G29() { LEAVE: - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU ui.reset_alert_level(); ui.quick_feedback(); ui.reset_status(); @@ -656,13 +656,13 @@ void unified_bed_leveling::G29() { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Probe End Script: ", Z_PROBE_END_SCRIPT); if (probe_deployed) { planner.synchronize(); - gcode.process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT)); + gcode.process_subcommands_now(F(Z_PROBE_END_SCRIPT)); } #else UNUSED(probe_deployed); #endif - TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index)); + TERN_(HAS_MULTI_HOTEND, if (old_tool_index != 0) tool_change(old_tool_index)); return; } @@ -724,7 +724,9 @@ void unified_bed_leveling::shift_mesh_height() { void unified_bed_leveling::probe_entire_mesh(const xy_pos_t &nearby, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) { probe.deploy(); // Deploy before ui.capture() to allow for PAUSE_BEFORE_DEPLOY_STOW - TERN_(HAS_LCD_MENU, ui.capture()); + TERN_(HAS_MARLINUI_MENU, ui.capture()); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart()); + TERN_(DWIN_LCD_PROUI, DWIN_LevelingStart()); save_ubl_active_state_and_disable(); // No bed level correction so only raw data is obtained uint8_t count = GRID_MAX_POINTS; @@ -736,9 +738,9 @@ void unified_bed_leveling::shift_mesh_height() { const uint8_t point_num = (GRID_MAX_POINTS - count) + 1; SERIAL_ECHOLNPGM("Probing mesh point ", point_num, "/", GRID_MAX_POINTS, "."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), point_num, int(GRID_MAX_POINTS))); - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU if (ui.button_pressed()) { ui.quick_feedback(false); // Preserve button state for click-and-hold SERIAL_ECHOLNPGM("\nMesh only partially populated.\n"); @@ -746,6 +748,7 @@ void unified_bed_leveling::shift_mesh_height() { ui.quick_feedback(); ui.release(); probe.stow(); // Release UI before stow to allow for PAUSE_BEFORE_DEPLOY_STOW + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); return restore_ubl_active_state_and_leave(); } #endif @@ -773,9 +776,9 @@ void unified_bed_leveling::shift_mesh_height() { TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(best.pos, ExtUI::G29_FINISH)); // Release UI during stow to allow for PAUSE_BEFORE_DEPLOY_STOW - TERN_(HAS_LCD_MENU, ui.release()); + TERN_(HAS_MARLINUI_MENU, ui.release()); probe.stow(); - TERN_(HAS_LCD_MENU, ui.capture()); + TERN_(HAS_MARLINUI_MENU, ui.capture()); probe.move_z_after_probing(); @@ -785,20 +788,23 @@ void unified_bed_leveling::shift_mesh_height() { constrain(nearby.x - probe.offset_xy.x, MESH_MIN_X, MESH_MAX_X), constrain(nearby.y - probe.offset_xy.y, MESH_MIN_Y, MESH_MAX_Y) ); + + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); + TERN_(DWIN_LCD_PROUI, DWIN_LevelingDone()); } #endif // HAS_BED_PROBE -void set_message_with_feedback(PGM_P const msg_P) { - #if HAS_LCD_MENU - ui.set_status_P(msg_P); +void set_message_with_feedback(FSTR_P const fstr) { + #if HAS_MARLINUI_MENU + ui.set_status(fstr); ui.quick_feedback(); #else - UNUSED(msg_P); + UNUSED(fstr); #endif } -#if HAS_LCD_MENU +#if HAS_MARLINUI_MENU typedef void (*clickFunc_t)(); @@ -850,7 +856,7 @@ void set_message_with_feedback(PGM_P const msg_P) { planner.synchronize(); SERIAL_ECHOPGM("Place shim under nozzle"); - LCD_MESSAGEPGM(MSG_UBL_BC_INSERT); + LCD_MESSAGE(MSG_UBL_BC_INSERT); ui.return_to_status(); echo_and_take_a_measurement(); @@ -859,7 +865,7 @@ void set_message_with_feedback(PGM_P const msg_P) { planner.synchronize(); SERIAL_ECHOPGM("Remove shim"); - LCD_MESSAGEPGM(MSG_UBL_BC_REMOVE); + LCD_MESSAGE(MSG_UBL_BC_REMOVE); echo_and_take_a_measurement(); const float z2 = measure_point_with_encoder(); @@ -884,6 +890,7 @@ void set_message_with_feedback(PGM_P const msg_P) { */ 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) { ui.capture(); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart()); save_ubl_active_state_and_disable(); // No bed level correction so only raw data is obtained do_blocking_move_to_xy_z(current_position, z_clearance); @@ -897,15 +904,11 @@ void set_message_with_feedback(PGM_P const msg_P) { // It doesn't matter if the probe can't reach the NAN location. This is a manual probe. if (!location.valid()) continue; - const xyz_pos_t ppos = { - mesh_index_to_xpos(lpos.x), - mesh_index_to_ypos(lpos.y), - z_clearance - }; + const xyz_pos_t ppos = { get_mesh_x(lpos.x), get_mesh_y(lpos.y), z_clearance }; if (!position_is_reachable(ppos)) break; // SHOULD NOT OCCUR (find_closest_mesh_point only returns reachable points) - LCD_MESSAGEPGM(MSG_UBL_MOVING_TO_NEXT); + LCD_MESSAGE(MSG_UBL_MOVING_TO_NEXT); do_blocking_move_to(ppos); do_z_clearance(z_clearance); @@ -917,11 +920,11 @@ void set_message_with_feedback(PGM_P const msg_P) { if (parser.seen_test('B')) { SERIAL_ECHOPGM("Place Shim & Measure"); - LCD_MESSAGEPGM(MSG_UBL_BC_INSERT); + LCD_MESSAGE(MSG_UBL_BC_INSERT); } else { SERIAL_ECHOPGM("Measure"); - LCD_MESSAGEPGM(MSG_UBL_BC_INSERT2); + LCD_MESSAGE(MSG_UBL_BC_INSERT2); } const float z_step = 0.01f; // 0.01mm per encoder tick, occasionally step @@ -947,6 +950,8 @@ void set_message_with_feedback(PGM_P const msg_P) { restore_ubl_active_state_and_leave(); do_blocking_move_to_xy_z(pos, Z_CLEARANCE_DEPLOY_PROBE); + + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); } /** @@ -974,7 +979,7 @@ void set_message_with_feedback(PGM_P const msg_P) { save_ubl_active_state_and_disable(); - LCD_MESSAGEPGM(MSG_UBL_FINE_TUNE_MESH); + LCD_MESSAGE(MSG_UBL_FINE_TUNE_MESH); ui.capture(); // Take over control of the LCD encoder do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_PROBES); // Move to the given XY with probe clearance @@ -994,11 +999,7 @@ void set_message_with_feedback(PGM_P const msg_P) { done_flags.mark(lpos); // Mark this location as 'adjusted' so a new // location is used on the next loop - const xyz_pos_t raw = { - mesh_index_to_xpos(lpos.x), - mesh_index_to_ypos(lpos.y), - Z_CLEARANCE_BETWEEN_PROBES - }; + const xyz_pos_t raw = { get_mesh_x(lpos.x), get_mesh_y(lpos.y), Z_CLEARANCE_BETWEEN_PROBES }; if (!position_is_reachable(raw)) break; // SHOULD NOT OCCUR (find_closest_mesh_point_of_type only returns reachable) @@ -1039,7 +1040,7 @@ void set_message_with_feedback(PGM_P const msg_P) { if (_click_and_hold([]{ ui.return_to_status(); do_z_clearance(Z_CLEARANCE_BETWEEN_PROBES); - set_message_with_feedback(GET_TEXT(MSG_EDITING_STOPPED)); + set_message_with_feedback(GET_TEXT_F(MSG_EDITING_STOPPED)); })) break; // TODO: Disable leveling here so the Z value becomes the 'native' Z value. @@ -1060,7 +1061,7 @@ void set_message_with_feedback(PGM_P const msg_P) { do_blocking_move_to_xy_z(pos, Z_CLEARANCE_BETWEEN_PROBES); - LCD_MESSAGEPGM(MSG_UBL_DONE_EDITING_MESH); + LCD_MESSAGE(MSG_UBL_DONE_EDITING_MESH); SERIAL_ECHOLNPGM("Done Editing Mesh"); if (lcd_map_control) @@ -1069,7 +1070,7 @@ void set_message_with_feedback(PGM_P const msg_P) { ui.return_to_status(); } -#endif // HAS_LCD_MENU +#endif // HAS_MARLINUI_MENU /** * Parse and validate most G29 parameters, store for use by G29 functions. @@ -1077,7 +1078,7 @@ void set_message_with_feedback(PGM_P const msg_P) { bool unified_bed_leveling::G29_parse_parameters() { bool err_flag = false; - set_message_with_feedback(GET_TEXT(MSG_UBL_DOING_G29)); + set_message_with_feedback(GET_TEXT_F(MSG_UBL_DOING_G29)); param.C_constant = 0; param.R_repetition = 0; @@ -1200,7 +1201,7 @@ void unified_bed_leveling::save_ubl_active_state_and_disable() { ubl_state_recursion_chk++; if (ubl_state_recursion_chk != 1) { SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row."); - set_message_with_feedback(GET_TEXT(MSG_UBL_SAVE_ERROR)); + set_message_with_feedback(GET_TEXT_F(MSG_UBL_SAVE_ERROR)); return; } #endif @@ -1209,15 +1210,16 @@ void unified_bed_leveling::save_ubl_active_state_and_disable() { } void unified_bed_leveling::restore_ubl_active_state_and_leave() { - TERN_(HAS_LCD_MENU, ui.release()); + TERN_(HAS_MARLINUI_MENU, ui.release()); #if ENABLED(UBL_DEVEL_DEBUGGING) if (--ubl_state_recursion_chk) { SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times."); - set_message_with_feedback(GET_TEXT(MSG_UBL_RESTORE_ERROR)); + set_message_with_feedback(GET_TEXT_F(MSG_UBL_RESTORE_ERROR)); return; } #endif set_bed_leveling_enabled(ubl_state_at_invocation); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); } mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() { @@ -1230,7 +1232,7 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() { if (!isnan(z_values[i][j])) continue; // Skip valid mesh points // Skip unreachable points - if (!probe.can_reach(mesh_index_to_xpos(i), mesh_index_to_ypos(j))) + if (!probe.can_reach(get_mesh_x(i), get_mesh_y(j))) continue; found_a_NAN = true; @@ -1282,11 +1284,11 @@ mesh_index_pair unified_bed_leveling::find_furthest_invalid_mesh_point() { static bool test_func(uint8_t i, uint8_t j, void *data) { find_closest_t *d = (find_closest_t*)data; - if ( d->type == CLOSEST || d->type == (isnan(ubl.z_values[i][j]) ? INVALID : REAL) + if ( d->type == CLOSEST || d->type == (isnan(bedlevel.z_values[i][j]) ? INVALID : REAL) || (d->type == SET_IN_BITMAP && !d->done_flags->marked(i, j)) ) { // Found a Mesh Point of the specified type! - const xy_pos_t mpos = { ubl.mesh_index_to_xpos(i), ubl.mesh_index_to_ypos(j) }; + const xy_pos_t mpos = { bedlevel.get_mesh_x(i), bedlevel.get_mesh_y(j) }; // If using the probe as the reference there are some unreachable locations. // Also for round beds, there are grid points outside the bed the nozzle can't reach. @@ -1330,7 +1332,7 @@ mesh_index_pair unified_bed_leveling::find_closest_mesh_point_of_type(const Mesh || (type == SET_IN_BITMAP && !done_flags->marked(i, j)) ) { // Found a Mesh Point of the specified type! - const xy_pos_t mpos = { mesh_index_to_xpos(i), mesh_index_to_ypos(j) }; + const xy_pos_t mpos = { get_mesh_x(i), get_mesh_y(j) }; // If using the probe as the reference there are some unreachable locations. // Also for round beds, there are grid points outside the bed the nozzle can't reach. @@ -1386,10 +1388,10 @@ typedef struct { uint8_t sx, ex, sy, ey; bool yfirst; } smart_fill_info; void unified_bed_leveling::smart_fill_mesh() { static const smart_fill_info - info0 PROGMEM = { 0, GRID_MAX_POINTS_X, 0, GRID_MAX_POINTS_Y - 2, false }, // Bottom of the mesh looking up - info1 PROGMEM = { 0, GRID_MAX_POINTS_X, GRID_MAX_POINTS_Y - 1, 0, false }, // Top of the mesh looking down - info2 PROGMEM = { 0, GRID_MAX_POINTS_X - 2, 0, GRID_MAX_POINTS_Y, true }, // Left side of the mesh looking right - info3 PROGMEM = { GRID_MAX_POINTS_X - 1, 0, 0, GRID_MAX_POINTS_Y, true }; // Right side of the mesh looking left + info0 PROGMEM = { 0, GRID_MAX_POINTS_X, 0, (GRID_MAX_POINTS_Y) - 2, false }, // Bottom of the mesh looking up + info1 PROGMEM = { 0, GRID_MAX_POINTS_X, (GRID_MAX_POINTS_Y) - 1, 0, false }, // Top of the mesh looking down + info2 PROGMEM = { 0, (GRID_MAX_POINTS_X) - 2, 0, GRID_MAX_POINTS_Y, true }, // Left side of the mesh looking right + info3 PROGMEM = { (GRID_MAX_POINTS_X) - 1, 0, 0, GRID_MAX_POINTS_Y, true }; // Right side of the mesh looking left static const smart_fill_info * const info[] PROGMEM = { &info0, &info1, &info2, &info3 }; LOOP_L_N(i, COUNT(info)) { @@ -1438,7 +1440,7 @@ void unified_bed_leveling::smart_fill_mesh() { if (do_3_pt_leveling) { SERIAL_ECHOLNPGM("Tilting mesh (1/3)"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 1/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 1/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); measured_z = probe.probe_at_point(points[0], PROBE_PT_RAISE, param.V_verbosity); if (isnan(measured_z)) @@ -1457,7 +1459,7 @@ void unified_bed_leveling::smart_fill_mesh() { if (!abort_flag) { SERIAL_ECHOLNPGM("Tilting mesh (2/3)"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 2/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 2/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); measured_z = probe.probe_at_point(points[1], PROBE_PT_RAISE, param.V_verbosity); #ifdef VALIDATE_MESH_TILT @@ -1477,7 +1479,7 @@ void unified_bed_leveling::smart_fill_mesh() { if (!abort_flag) { SERIAL_ECHOLNPGM("Tilting mesh (3/3)"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " 3/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " 3/3"), GET_TEXT(MSG_LCD_TILTING_MESH))); measured_z = probe.probe_at_point(points[2], PROBE_PT_LAST_STOW, param.V_verbosity); #ifdef VALIDATE_MESH_TILT @@ -1518,7 +1520,7 @@ void unified_bed_leveling::smart_fill_mesh() { if (!abort_flag) { SERIAL_ECHOLNPGM("Tilting mesh point ", point_num, "/", total_points, "\n"); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points)); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_LCD_TILTING_MESH), point_num, total_points)); measured_z = probe.probe_at_point(rpos, parser.seen_test('E') ? PROBE_PT_STOW : PROBE_PT_RAISE, param.V_verbosity); // TODO: Needs error handling @@ -1578,9 +1580,7 @@ void unified_bed_leveling::smart_fill_mesh() { matrix_3x3 rotation = matrix_3x3::create_look_at(vector_3(lsf_results.A, lsf_results.B, 1)); GRID_LOOP(i, j) { - float mx = mesh_index_to_xpos(i), - my = mesh_index_to_ypos(j), - mz = z_values[i][j]; + float mx = get_mesh_x(i), my = get_mesh_y(j), mz = z_values[i][j]; if (DEBUGGING(LEVELING)) { DEBUG_ECHOPAIR_F("before rotation = [", mx, 7); @@ -1609,7 +1609,7 @@ void unified_bed_leveling::smart_fill_mesh() { } if (DEBUGGING(LEVELING)) { - rotation.debug(PSTR("rotation matrix:\n")); + rotation.debug(F("rotation matrix:\n")); DEBUG_ECHOPAIR_F("LSF Results A=", lsf_results.A, 7); DEBUG_ECHOPAIR_F(" B=", lsf_results.B, 7); DEBUG_ECHOLNPAIR_F(" D=", lsf_results.D, 7); @@ -1636,14 +1636,14 @@ void unified_bed_leveling::smart_fill_mesh() { auto normed = [&](const xy_pos_t &pos, const_float_t zadd) { return normal.x * pos.x + normal.y * pos.y + zadd; }; - auto debug_pt = [](PGM_P const pre, const xy_pos_t &pos, const_float_t zadd) { - d_from(); SERIAL_ECHOPGM_P(pre); + auto debug_pt = [](FSTR_P const pre, const xy_pos_t &pos, const_float_t zadd) { + d_from(); SERIAL_ECHOF(pre); DEBUG_ECHO_F(normed(pos, zadd), 6); DEBUG_ECHOLNPAIR_F(" Z error = ", zadd - get_z_correction(pos), 6); }; - debug_pt(PSTR("1st point: "), probe_pt[0], normal.z * z1); - debug_pt(PSTR("2nd point: "), probe_pt[1], normal.z * z2); - debug_pt(PSTR("3rd point: "), probe_pt[2], normal.z * z3); + debug_pt(F("1st point: "), probe_pt[0], normal.z * z1); + debug_pt(F("2nd point: "), probe_pt[1], normal.z * z2); + debug_pt(F("3rd point: "), probe_pt[2], normal.z * z3); d_from(); DEBUG_ECHOPGM("safe home with Z="); DEBUG_ECHOLNPAIR_F("0 : ", normed(safe_homing_xy, 0), 6); d_from(); DEBUG_ECHOPGM("safe home with Z="); @@ -1677,18 +1677,18 @@ void unified_bed_leveling::smart_fill_mesh() { xy_pos_t ppos; LOOP_L_N(ix, GRID_MAX_POINTS_X) { - ppos.x = mesh_index_to_xpos(ix); + ppos.x = get_mesh_x(ix); LOOP_L_N(iy, GRID_MAX_POINTS_Y) { - ppos.y = mesh_index_to_ypos(iy); + ppos.y = get_mesh_y(iy); if (isnan(z_values[ix][iy])) { // undefined mesh point at (ppos.x,ppos.y), compute weighted LSF from original valid mesh points. incremental_LSF_reset(&lsf_results); xy_pos_t rpos; LOOP_L_N(jx, GRID_MAX_POINTS_X) { - rpos.x = mesh_index_to_xpos(jx); + rpos.x = get_mesh_x(jx); LOOP_L_N(jy, GRID_MAX_POINTS_Y) { if (TEST(bitmap[jx], jy)) { - rpos.y = mesh_index_to_ypos(jy); + rpos.y = get_mesh_y(jy); const float rz = z_values[jx][jy], w = 1.0f + weight_scaled / (rpos - ppos).magnitude(); incremental_WLSF(&lsf_results, rpos, rz, w); @@ -1747,7 +1747,7 @@ void unified_bed_leveling::smart_fill_mesh() { SERIAL_ECHOPGM("X-Axis Mesh Points at: "); LOOP_L_N(i, GRID_MAX_POINTS_X) { - SERIAL_ECHO_F(LOGICAL_X_POSITION(mesh_index_to_xpos(i)), 3); + SERIAL_ECHO_F(LOGICAL_X_POSITION(get_mesh_x(i)), 3); SERIAL_ECHOPGM(" "); serial_delay(25); } @@ -1755,7 +1755,7 @@ void unified_bed_leveling::smart_fill_mesh() { SERIAL_ECHOPGM("Y-Axis Mesh Points at: "); LOOP_L_N(i, GRID_MAX_POINTS_Y) { - SERIAL_ECHO_F(LOGICAL_Y_POSITION(mesh_index_to_ypos(i)), 3); + SERIAL_ECHO_F(LOGICAL_Y_POSITION(get_mesh_y(i)), 3); SERIAL_ECHOPGM(" "); serial_delay(25); } diff --git a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp index f7e98c9fa77d2..18110c67fa8ec 100644 --- a/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp +++ b/Marlin/src/feature/bedlevel/ubl/ubl_motion.cpp @@ -26,7 +26,6 @@ #include "../bedlevel.h" #include "../../../module/planner.h" -#include "../../../module/stepper.h" #include "../../../module/motion.h" #if ENABLED(DELTA) @@ -36,8 +35,18 @@ #include "../../../MarlinCore.h" #include +//#define DEBUG_UBL_MOTION +#define DEBUG_OUT ENABLED(DEBUG_UBL_MOTION) +#include "../../../core/debug_out.h" + #if !UBL_SEGMENTED + // TODO: The first and last parts of a move might result in very short segment(s) + // after getting split on the cell boundary, so moves like that should not + // get split. This will be most common for moves that start/end near the + // 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) { /** * Much of the nozzle movement will be within the same cell. So we will do as little computation @@ -76,8 +85,8 @@ #endif // The distance is always MESH_X_DIST so multiply by the constant reciprocal. - const float xratio = (end.x - mesh_index_to_xpos(iend.x)) * RECIPROCAL(MESH_X_DIST), - yratio = (end.y - mesh_index_to_ypos(iend.y)) * RECIPROCAL(MESH_Y_DIST), + const float xratio = (end.x - get_mesh_x(iend.x)) * RECIPROCAL(MESH_X_DIST), + yratio = (end.y - get_mesh_y(iend.y)) * RECIPROCAL(MESH_Y_DIST), z1 = z_values[iend.x][iend.y ] + xratio * (z_values[iend.x + 1][iend.y ] - z_values[iend.x][iend.y ]), z2 = z_values[iend.x][iend.y + 1] + xratio * (z_values[iend.x + 1][iend.y + 1] - z_values[iend.x][iend.y + 1]); @@ -139,7 +148,7 @@ icell.y += ineg.y; // Line going down? Just go to the bottom. while (icell.y != iend.y + ineg.y) { icell.y += iadd.y; - const float next_mesh_line_y = mesh_index_to_ypos(icell.y); + const float next_mesh_line_y = get_mesh_y(icell.y); /** * Skip the calculations for an infinite slope. @@ -155,7 +164,7 @@ // Replace NAN corrections with 0.0 to prevent NAN propagation. if (isnan(z0)) z0 = 0.0; - dest.y = mesh_index_to_ypos(icell.y); + dest.y = get_mesh_y(icell.y); /** * Without this check, it's possible to generate a zero length move, as in the case where @@ -176,7 +185,9 @@ dest.z += z0; planner.buffer_segment(dest, scaled_fr_mm_s, extruder); - } //else printf("FIRST MOVE PRUNED "); + } + else + DEBUG_ECHOLNPGM("[ubl] skip Y segment"); } // At the final destination? Usually not, but when on a Y Mesh Line it's completed. @@ -196,7 +207,7 @@ while (icell.x != iend.x + ineg.x) { icell.x += iadd.x; - dest.x = mesh_index_to_xpos(icell.x); + dest.x = get_mesh_x(icell.x); dest.y = ratio * dest.x + c; // Calculate Y at the next X mesh line float z0 = z_correction_for_y_on_vertical_mesh_line(dest.y, icell.x, icell.y) @@ -225,7 +236,9 @@ dest.z += z0; if (!planner.buffer_segment(dest, scaled_fr_mm_s, extruder)) break; - } //else printf("FIRST MOVE PRUNED "); + } + else + DEBUG_ECHOLNPGM("[ubl] skip Y segment"); } if (xy_pos_t(current_position) != xy_pos_t(end)) @@ -245,8 +258,8 @@ while (cnt) { - const float next_mesh_line_x = mesh_index_to_xpos(icell.x + iadd.x), - next_mesh_line_y = mesh_index_to_ypos(icell.y + iadd.y); + const float next_mesh_line_x = get_mesh_x(icell.x + iadd.x), + next_mesh_line_y = get_mesh_y(icell.y + iadd.y); dest.y = ratio * next_mesh_line_x + c; // Calculate Y at the next X mesh line dest.x = (next_mesh_line_y - c) / ratio; // Calculate X at the next Y mesh line @@ -340,7 +353,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 @@ -360,11 +373,12 @@ #endif NOLESS(segments, 1U); // Must have at least one segment - const float inv_segments = 1.0f / segments, // Reciprocal to save calculation - segment_xyz_mm = SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments; // Length of each segment + const float inv_segments = 1.0f / segments; // Reciprocal to save calculation + // Add hints to help optimize the move + PlannerHints hints(SQRT(cart_xy_mm_2 + sq(total.z)) * inv_segments); // Length of each segment #if ENABLED(SCARA_FEEDRATE_SCALING) - const float inv_duration = scaled_fr_mm_s / segment_xyz_mm; + hints.inv_duration = scaled_fr_mm_s / hints.millimeters; #endif xyze_float_t diff = total * inv_segments; @@ -378,13 +392,9 @@ if (!planner.leveling_active || !planner.leveling_active_at_z(destination.z)) { while (--segments) { raw += diff; - planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm - OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) - ); + planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints); } - planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, segment_xyz_mm - OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) - ); + planner.buffer_line(destination, scaled_fr_mm_s, active_extruder, hints); return false; // Did not set current from destination } @@ -423,7 +433,7 @@ if (isnan(z_x0y1)) z_x0y1 = 0; // in order to avoid isnan tests per cell, if (isnan(z_x1y1)) z_x1y1 = 0; // thus guessing zero for undefined points - const xy_pos_t pos = { mesh_index_to_xpos(icell.x), mesh_index_to_ypos(icell.y) }; + const xy_pos_t pos = { get_mesh_x(icell.x), get_mesh_y(icell.y) }; xy_pos_t cell = raw - pos; const float z_xmy0 = (z_x1y0 - z_x0y0) * RECIPROCAL(MESH_X_DIST), // z slope per x along y0 (lower left to lower right) @@ -450,13 +460,10 @@ if (--segments == 0) raw = destination; // if this is last segment, use destination for exact const float z_cxcy = (z_cxy0 + z_cxym * cell.y) // interpolated mesh z height along cell.x at cell.y - #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - * fade_scaling_factor // apply fade factor to interpolated mesh height - #endif - ; + TERN_(ENABLE_LEVELING_FADE_HEIGHT, * fade_scaling_factor); // apply fade factor to interpolated height const float oldz = raw.z; raw.z += z_cxcy; - planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, segment_xyz_mm OPTARG(SCARA_FEEDRATE_SCALING, inv_duration) ); + planner.buffer_line(raw, scaled_fr_mm_s, active_extruder, hints); raw.z = oldz; if (segments == 0) // done with last segment diff --git a/Marlin/src/feature/bltouch.cpp b/Marlin/src/feature/bltouch.cpp index 49a10f62b1559..10d3131aedcb2 100644 --- a/Marlin/src/feature/bltouch.cpp +++ b/Marlin/src/feature/bltouch.cpp @@ -28,7 +28,12 @@ BLTouch bltouch; -bool BLTouch::last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain +bool BLTouch::od_5v_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain +#ifdef BLTOUCH_HS_MODE + bool BLTouch::high_speed_mode; // Initialized by settings.load, 0 = Low Speed; 1 = High Speed +#else + constexpr bool BLTouch::high_speed_mode; +#endif #include "../module/servo.h" #include "../module/probe.h" @@ -40,7 +45,7 @@ void stop(); bool BLTouch::command(const BLTCommand cmd, const millis_t &ms) { if (DEBUGGING(LEVELING)) SERIAL_ECHOLNPGM("BLTouch Command :", cmd); - MOVE_SERVO(Z_PROBE_SERVO_NR, cmd); + servo[Z_PROBE_SERVO_NR].move(cmd); safe_delay(_MAX(ms, (uint32_t)BLTOUCH_DELAY)); // BLTOUCH_DELAY is also the *minimum* delay return triggered(); } @@ -63,18 +68,17 @@ void BLTouch::init(const bool set_voltage/*=false*/) { #else - if (DEBUGGING(LEVELING)) { - DEBUG_ECHOLNPGM("last_written_mode - ", last_written_mode); - DEBUG_ECHOLNPGM("config mode - " - #if ENABLED(BLTOUCH_SET_5V_MODE) - "BLTOUCH_SET_5V_MODE" - #else - "OD" - #endif - ); - } + #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") ")"); + } + #endif - const bool should_set = last_written_mode != ENABLED(BLTOUCH_SET_5V_MODE); + const bool should_set = od_5v_mode != ENABLED(BLTOUCH_SET_5V_MODE); #endif @@ -107,11 +111,8 @@ bool BLTouch::deploy_proc() { // Last attempt to DEPLOY if (_deploy_query_alarm()) { // The deploy might have failed or the probe is actually triggered (nozzle too low?) again - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Recovery Failed"); - - SERIAL_ERROR_MSG(STR_STOP_BLTOUCH); // Tell the user something is wrong, needs action - stop(); // but it's not too bad, no need to kill, allow restart - + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Deploy Failed"); + probe.probe_error_stop(); // Something is wrong, needs action, but not too bad, allow restart return true; // Tell our caller we goofed in case he cares to know } } @@ -149,12 +150,8 @@ bool BLTouch::stow_proc() { // But one more STOW will catch that // Last attempt to STOW if (_stow_query_alarm()) { // so if there is now STILL an ALARM condition: - - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Recovery Failed"); - - SERIAL_ERROR_MSG(STR_STOP_BLTOUCH); // Tell the user something is wrong, needs action - stop(); // but it's not too bad, no need to kill, allow restart - + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("BLTouch Stow Failed"); + probe.probe_error_stop(); // Something is wrong, needs action, but not too bad, allow restart return true; // Tell our caller we goofed in case he cares to know } } @@ -193,7 +190,7 @@ void BLTouch::mode_conv_proc(const bool M5V) { _mode_store(); if (M5V) _set_5V_mode(); else _set_OD_mode(); _stow(); - last_written_mode = M5V; + od_5v_mode = M5V; } #endif // BLTOUCH diff --git a/Marlin/src/feature/bltouch.h b/Marlin/src/feature/bltouch.h index 9ecccb4256f40..fa857bb96ab6f 100644 --- a/Marlin/src/feature/bltouch.h +++ b/Marlin/src/feature/bltouch.h @@ -23,10 +23,6 @@ #include "../inc/MarlinConfigPre.h" -#if DISABLED(BLTOUCH_HS_MODE) - #define BLTOUCH_SLOW_MODE 1 -#endif - // BLTouch commands are sent as servo angles typedef unsigned char BLTCommand; @@ -70,8 +66,17 @@ typedef unsigned char BLTCommand; class BLTouch { public: + static void init(const bool set_voltage=false); - static bool last_written_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain + static bool od_5v_mode; // Initialized by settings.load, 0 = Open Drain; 1 = 5V Drain + + #ifdef BLTOUCH_HS_MODE + static bool high_speed_mode; // Initialized by settings.load, 0 = Low Speed; 1 = High Speed + #else + static constexpr bool high_speed_mode = false; + #endif + + static float z_extra_clearance() { return high_speed_mode ? 7 : 0; } // DEPLOY and STOW are wrapped for error handling - these are used by homing and by probing static bool deploy() { return deploy_proc(); } diff --git a/Marlin/src/feature/cancel_object.cpp b/Marlin/src/feature/cancel_object.cpp index 9d50bfc0d4128..bffd2bb72020d 100644 --- a/Marlin/src/feature/cancel_object.cpp +++ b/Marlin/src/feature/cancel_object.cpp @@ -46,7 +46,7 @@ void CancelObject::set_active_object(const int8_t obj) { #if BOTH(HAS_STATUS_MESSAGE, CANCEL_OBJECTS_REPORTING) if (active_object >= 0) - ui.status_printf_P(0, PSTR(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object)); + ui.status_printf(0, F(S_FMT " %i"), GET_TEXT(MSG_PRINTING_OBJECT), int(active_object)); else ui.reset_status(); #endif diff --git a/Marlin/src/feature/cancel_object.h b/Marlin/src/feature/cancel_object.h index 1d2d77f20334e..62548a371937d 100644 --- a/Marlin/src/feature/cancel_object.h +++ b/Marlin/src/feature/cancel_object.h @@ -32,10 +32,10 @@ class CancelObject { static void cancel_object(const int8_t obj); static void uncancel_object(const int8_t obj); static void report(); - static inline bool is_canceled(const int8_t obj) { return TEST(canceled, obj); } - static inline void clear_active_object() { set_active_object(-1); } - static inline void cancel_active_object() { cancel_object(active_object); } - static inline void reset() { canceled = 0x0000; object_count = 0; clear_active_object(); } + static bool is_canceled(const int8_t obj) { return TEST(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(); } }; extern CancelObject cancelable; diff --git a/Marlin/src/feature/caselight.cpp b/Marlin/src/feature/caselight.cpp index 1baef6d46845b..eb580a6d62697 100644 --- a/Marlin/src/feature/caselight.cpp +++ b/Marlin/src/feature/caselight.cpp @@ -39,7 +39,6 @@ CaseLight caselight; bool CaseLight::on = CASE_LIGHT_DEFAULT_ON; #if CASE_LIGHT_IS_COLOR_LED - #include "leds/leds.h" 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]) }; #endif @@ -65,12 +64,22 @@ void CaseLight::update(const bool sflag) { #endif #if CASE_LIGHT_IS_COLOR_LED - leds.set_color(LEDColor(color.r, color.g, color.b OPTARG(HAS_WHITE_LED, color.w), n10ct)); + #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))); + 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))); + #endif #else // !CASE_LIGHT_IS_COLOR_LED #if CASELIGHT_USES_BRIGHTNESS if (pin_is_pwm()) - analogWrite(pin_t(CASE_LIGHT_PIN), ( + hal.set_pwm_duty(pin_t(CASE_LIGHT_PIN), ( #if CASE_LIGHT_MAX_PWM == 255 n10ct #else diff --git a/Marlin/src/feature/caselight.h b/Marlin/src/feature/caselight.h index b2e82f9b838f8..17e1222acbfaf 100644 --- a/Marlin/src/feature/caselight.h +++ b/Marlin/src/feature/caselight.h @@ -27,10 +27,6 @@ #include "leds/leds.h" // for LEDColor #endif -#if NONE(CASE_LIGHT_NO_BRIGHTNESS, CASE_LIGHT_IS_COLOR_LED) || ENABLED(CASE_LIGHT_USE_NEOPIXEL) - #define CASELIGHT_USES_BRIGHTNESS 1 -#endif - class CaseLight { public: static bool on; @@ -49,8 +45,8 @@ class CaseLight { } static void update(const bool sflag); - static inline void update_brightness() { update(false); } - static inline void update_enabled() { update(true); } + static void update_brightness() { update(false); } + static void update_enabled() { update(true); } #if ENABLED(CASE_LIGHT_IS_COLOR_LED) private: diff --git a/Marlin/src/feature/controllerfan.cpp b/Marlin/src/feature/controllerfan.cpp index 5efddbb11169d..f42bf52ae40a0 100644 --- a/Marlin/src/feature/controllerfan.cpp +++ b/Marlin/src/feature/controllerfan.cpp @@ -58,7 +58,7 @@ void ControllerFan::update() { // - At least one stepper driver is enabled // - The heated bed is enabled // - TEMP_SENSOR_BOARD is reporting >= CONTROLLER_FAN_MIN_BOARD_TEMP - const ena_mask_t axis_mask = TERN(CONTROLLER_FAN_USE_Z_ONLY, _BV(Z_AXIS), ~TERN0(CONTROLLER_FAN_IGNORE_Z, _BV(Z_AXIS))); + 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) || TERN0(HAS_CONTROLLER_FAN_MIN_BOARD_TEMP, thermalManager.wholeDegBoard() >= CONTROLLER_FAN_MIN_BOARD_TEMP) @@ -72,9 +72,14 @@ void ControllerFan::update() { ? settings.active_speed : settings.idle_speed ); - // Allow digital or PWM fan output (see M42 handling) - WRITE(CONTROLLER_FAN_PIN, speed); - analogWrite(pin_t(CONTROLLER_FAN_PIN), speed); + #if ENABLED(FAN_SOFT_PWM) + thermalManager.soft_pwm_controller_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); + #endif } } diff --git a/Marlin/src/feature/controllerfan.h b/Marlin/src/feature/controllerfan.h index 55f2d5cfc7aa2..55eb2359b0677 100644 --- a/Marlin/src/feature/controllerfan.h +++ b/Marlin/src/feature/controllerfan.h @@ -60,9 +60,9 @@ class ControllerFan { #else static const controllerFan_settings_t &settings; #endif - static inline bool state() { return speed > 0; } - static inline void init() { reset(); } - static inline void reset() { TERN_(CONTROLLER_FAN_EDITABLE, settings = controllerFan_defaults); } + static bool state() { return speed > 0; } + static void init() { reset(); } + static void reset() { TERN_(CONTROLLER_FAN_EDITABLE, settings = controllerFan_defaults); } static void setup(); static void update(); }; diff --git a/Marlin/src/feature/dac/dac_dac084s085.cpp b/Marlin/src/feature/dac/dac_dac084s085.cpp index b88aaf802bdff..772bb68de42ef 100644 --- a/Marlin/src/feature/dac/dac_dac084s085.cpp +++ b/Marlin/src/feature/dac/dac_dac084s085.cpp @@ -11,7 +11,6 @@ #include "dac_dac084s085.h" #include "../../MarlinCore.h" -#include "../../module/stepper.h" #include "../../HAL/shared/Delay.h" dac084s085::dac084s085() { } diff --git a/Marlin/src/feature/dac/stepper_dac.cpp b/Marlin/src/feature/dac/stepper_dac.cpp index ff730e93c62c2..f5664bc598555 100644 --- a/Marlin/src/feature/dac/stepper_dac.cpp +++ b/Marlin/src/feature/dac/stepper_dac.cpp @@ -29,7 +29,6 @@ #if HAS_MOTOR_CURRENT_DAC #include "stepper_dac.h" -#include "../../MarlinCore.h" // for SP_X_LBL... bool dac_present = false; constexpr xyze_uint8_t dac_order = DAC_STEPPER_ORDER; @@ -60,7 +59,7 @@ int StepperDAC::init() { } void StepperDAC::set_current_value(const uint8_t channel, uint16_t val) { - if (!dac_present) return; + if (!(dac_present && channel < LOGICAL_AXES)) return; NOMORE(val, uint16_t(DAC_STEPPER_MAX)); @@ -85,13 +84,11 @@ void StepperDAC::print_values() { if (!dac_present) return; SERIAL_ECHO_MSG("Stepper current values in % (Amps):"); SERIAL_ECHO_START(); - SERIAL_ECHOPGM_P(SP_X_LBL, dac_perc(X_AXIS), PSTR(" ("), dac_amps(X_AXIS), PSTR(")")); - #if HAS_Y_AXIS - SERIAL_ECHOPGM_P(SP_Y_LBL, dac_perc(Y_AXIS), PSTR(" ("), dac_amps(Y_AXIS), PSTR(")")); - #endif - #if HAS_Z_AXIS - SERIAL_ECHOPGM_P(SP_Z_LBL, dac_perc(Z_AXIS), PSTR(" ("), dac_amps(Z_AXIS), PSTR(")")); - #endif + LOOP_LOGICAL_AXES(a) { + SERIAL_CHAR(' ', IAXIS_CHAR(a), ':'); + SERIAL_ECHO(dac_perc(a)); + 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(")")); #endif diff --git a/Marlin/src/feature/digipot/digipot_mcp4018.cpp b/Marlin/src/feature/digipot/digipot_mcp4018.cpp index 37853ff428bca..3f2ecbfcdc0b1 100644 --- a/Marlin/src/feature/digipot/digipot_mcp4018.cpp +++ b/Marlin/src/feature/digipot/digipot_mcp4018.cpp @@ -31,9 +31,13 @@ // Settings for the I2C based DIGIPOT (MCP4018) based on WT150 -#define DIGIPOT_A4988_Rsx 0.250 -#define DIGIPOT_A4988_Vrefmax 1.666 -#define DIGIPOT_MCP4018_MAX_VALUE 127 +#ifndef DIGIPOT_A4988_Rsx + #define DIGIPOT_A4988_Rsx 0.250 +#endif +#ifndef DIGIPOT_A4988_Vrefmax + #define DIGIPOT_A4988_Vrefmax 1.666 +#endif +#define DIGIPOT_MCP4018_MAX_VALUE 127 #define DIGIPOT_A4988_Itripmax(Vref) ((Vref) / (8.0 * DIGIPOT_A4988_Rsx)) diff --git a/Marlin/src/feature/direct_stepping.cpp b/Marlin/src/feature/direct_stepping.cpp index ce979145a12c9..13cf71e07695e 100644 --- a/Marlin/src/feature/direct_stepping.cpp +++ b/Marlin/src/feature/direct_stepping.cpp @@ -52,13 +52,13 @@ namespace DirectStepping { volatile bool SerialPageManager::fatal_error; template - volatile PageState SerialPageManager::page_states[Cfg::NUM_PAGES]; + volatile PageState SerialPageManager::page_states[Cfg::PAGE_COUNT]; template volatile bool SerialPageManager::page_states_dirty; template - uint8_t SerialPageManager::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE]; + uint8_t SerialPageManager::pages[Cfg::PAGE_COUNT][Cfg::PAGE_SIZE]; template uint8_t SerialPageManager::checksum; @@ -74,7 +74,7 @@ namespace DirectStepping { template void SerialPageManager::init() { - for (int i = 0 ; i < Cfg::NUM_PAGES ; i++) + for (int i = 0 ; i < Cfg::PAGE_COUNT ; i++) page_states[i] = PageState::FREE; fatal_error = false; @@ -143,14 +143,16 @@ namespace DirectStepping { // special case for 8-bit, check if rolled back to 0 if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes if (write_byte_idx) return true; - } else { - if (write_byte_idx < write_page_size) return true; } - } else if (Cfg::DIRECTIONAL) { - if (write_byte_idx != Cfg::PAGE_SIZE) return true; - } else { - if (write_byte_idx < write_page_size) return true; + else if (write_byte_idx < write_page_size) + return true; + } + else if (Cfg::DIRECTIONAL) { + if (write_byte_idx != Cfg::PAGE_SIZE) + return true; } + else if (write_byte_idx < write_page_size) + return true; state = State::CHECKSUM; return true; @@ -161,11 +163,10 @@ namespace DirectStepping { return true; } case State::UNFAIL: - if (c == 0) { + if (c == 0) set_page_state(write_page_idx, PageState::FREE); - } else { + else fatal_error = true; - } state = State::MONITOR; return true; } @@ -174,7 +175,7 @@ namespace DirectStepping { template void SerialPageManager::write_responses() { if (fatal_error) { - kill(GET_TEXT(MSG_BAD_PAGE)); + kill(GET_TEXT_F(MSG_BAD_PAGE)); return; } @@ -183,10 +184,10 @@ namespace DirectStepping { SERIAL_CHAR(Cfg::CONTROL_CHAR); constexpr int state_bits = 2; - constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits; + constexpr int n_bytes = Cfg::PAGE_COUNT >> state_bits; volatile uint8_t bits_b[n_bytes] = { 0 }; - for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) { + for (page_idx_t i = 0 ; i < Cfg::PAGE_COUNT ; i++) { bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7); } diff --git a/Marlin/src/feature/direct_stepping.h b/Marlin/src/feature/direct_stepping.h index b3007731cdb2e..962310281edb7 100644 --- a/Marlin/src/feature/direct_stepping.h +++ b/Marlin/src/feature/direct_stepping.h @@ -68,10 +68,10 @@ namespace DirectStepping { static State state; static volatile bool fatal_error; - static volatile PageState page_states[Cfg::NUM_PAGES]; + static volatile PageState page_states[Cfg::PAGE_COUNT]; static volatile bool page_states_dirty; - static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE]; + static uint8_t pages[Cfg::PAGE_COUNT][Cfg::PAGE_SIZE]; static uint8_t checksum; static write_byte_idx_t write_byte_idx; static page_idx_t write_page_idx; @@ -87,8 +87,8 @@ namespace DirectStepping { struct config_t { static constexpr char CONTROL_CHAR = '!'; - static constexpr int NUM_PAGES = num_pages; - static constexpr int NUM_AXES = num_axes; + static constexpr int PAGE_COUNT = num_pages; + static constexpr int AXIS_COUNT = num_axes; static constexpr int BITS_SEGMENT = bits_segment; static constexpr int DIRECTIONAL = dir ? 1 : 0; static constexpr int SEGMENTS = segments; @@ -96,10 +96,10 @@ namespace DirectStepping { static constexpr int NUM_SEGMENTS = _BV(BITS_SEGMENT); static constexpr int SEGMENT_STEPS = _BV(BITS_SEGMENT - DIRECTIONAL) - 1; static constexpr int TOTAL_STEPS = SEGMENT_STEPS * SEGMENTS; - static constexpr int PAGE_SIZE = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8; + static constexpr int PAGE_SIZE = (AXIS_COUNT * BITS_SEGMENT * SEGMENTS) / 8; typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t; - typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t; + typedef typename TypeSelector<(PAGE_COUNT>256), uint16_t, uint8_t>::type page_idx_t; }; template diff --git a/Marlin/src/feature/e_parser.h b/Marlin/src/feature/e_parser.h index 3723caa35eea0..fda1ba144bc4d 100644 --- a/Marlin/src/feature/e_parser.h +++ b/Marlin/src/feature/e_parser.h @@ -41,7 +41,9 @@ extern bool wait_for_user, wait_for_heatup; void quickresume_stepper(); #endif -void HAL_reboot(); +#if ENABLED(SOFT_RESET_VIA_SERIAL) + void HAL_reboot(); +#endif class EmergencyParser { @@ -199,7 +201,7 @@ class EmergencyParser { case EP_M112: killed_by_M112 = true; break; case EP_M410: quickstop_by_M410 = true; break; #if ENABLED(HOST_PROMPT_SUPPORT) - case EP_M876SN: host_response_handler(M876_reason); break; + case EP_M876SN: hostui.handle_response(M876_reason); break; #endif #if ENABLED(REALTIME_REPORTING_COMMANDS) case EP_GRBL_STATUS: report_current_position_moving(); break; diff --git a/Marlin/src/feature/easythreed_ui.cpp b/Marlin/src/feature/easythreed_ui.cpp new file mode 100644 index 0000000000000..b15daffc09bee --- /dev/null +++ b/Marlin/src/feature/easythreed_ui.cpp @@ -0,0 +1,236 @@ +/** + * 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 . + * + */ + +#include "../inc/MarlinConfigPre.h" + +#if ENABLED(EASYTHREED_UI) + +#include "easythreed_ui.h" +#include "pause.h" +#include "../module/temperature.h" +#include "../module/printcounter.h" +#include "../sd/cardreader.h" +#include "../gcode/queue.h" +#include "../module/motion.h" +#include "../module/planner.h" +#include "../MarlinCore.h" + +EasythreedUI easythreed_ui; + +#define BTN_DEBOUNCE_MS 20 + +void EasythreedUI::init() { + SET_INPUT_PULLUP(BTN_HOME); SET_OUTPUT(BTN_HOME_GND); + SET_INPUT_PULLUP(BTN_FEED); SET_OUTPUT(BTN_FEED_GND); + SET_INPUT_PULLUP(BTN_RETRACT); SET_OUTPUT(BTN_RETRACT_GND); + SET_INPUT_PULLUP(BTN_PRINT); + SET_OUTPUT(EASYTHREED_LED_PIN); +} + +void EasythreedUI::run() { + blinkLED(); + loadButton(); + printButton(); +} + +enum LEDInterval : uint16_t { + LED_OFF = 0, + LED_ON = 4000, + LED_BLINK_0 = 2500, + LED_BLINK_1 = 1500, + LED_BLINK_2 = 1000, + LED_BLINK_3 = 800, + LED_BLINK_4 = 500, + LED_BLINK_5 = 300, + LED_BLINK_6 = 150, + LED_BLINK_7 = 50 +}; + +uint16_t blink_interval_ms = LED_ON; // Status LED on Start button + +void EasythreedUI::blinkLED() { + static millis_t prev_blink_interval_ms = 0, blink_start_ms = 0; + + if (blink_interval_ms == LED_OFF) { WRITE(EASYTHREED_LED_PIN, HIGH); return; } // OFF + if (blink_interval_ms >= LED_ON) { WRITE(EASYTHREED_LED_PIN, LOW); return; } // ON + + const millis_t ms = millis(); + if (prev_blink_interval_ms != blink_interval_ms) { + prev_blink_interval_ms = blink_interval_ms; + blink_start_ms = 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)) + WRITE(EASYTHREED_LED_PIN, HIGH); + else + blink_start_ms = ms; +} + +// +// Filament Load/Unload Button +// Load/Unload buttons are a 3 position switch with a common center ground. +// +void EasythreedUI::loadButton() { + if (printingIsActive()) return; + + enum FilamentStatus : uint8_t { FS_IDLE, FS_PRESS, FS_CHECK, FS_PROCEED }; + static uint8_t filament_status = FS_IDLE; + static millis_t filament_time = 0; + + switch (filament_status) { + + case FS_IDLE: + if (!READ(BTN_RETRACT) || !READ(BTN_FEED)) { // If feed/retract switch is toggled... + filament_status++; // ...proceed to next test. + filament_time = millis(); + } + break; + + case FS_PRESS: + 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 + filament_status++; + } + else + filament_status = FS_IDLE; // Switch not toggled long enough + } + break; + + case FS_CHECK: + if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop) + blink_interval_ms = LED_ON; // LED on steady + filament_status = FS_IDLE; + thermalManager.disable_all_heaters(); + } + else if (thermalManager.hotEnoughToExtrude(0)) { // Is the hotend hot enough to move material? + filament_status++; // Proceed to feed / retract. + blink_interval_ms = LED_BLINK_5; // Blink ~3 times per second + } + break; + + case FS_PROCEED: { + // Feed or Retract just once. Hard abort all moves and return to idle on swicth release. + static bool flag = false; + if (READ(BTN_RETRACT) && READ(BTN_FEED)) { // Switch in center position (stop) + flag = false; // Restore flag to false + filament_status = FS_IDLE; // Go back to idle state + quickstop_stepper(); // Hard-stop all the steppers ... now! + thermalManager.disable_all_heaters(); // And disable all the heaters + blink_interval_ms = LED_ON; + } + else if (!flag) { + flag = true; + queue.inject(!READ(BTN_RETRACT) ? F("G91\nG0 E10 F180\nG0 E-120 F180\nM104 S0") : F("G91\nG0 E100 F120\nM104 S0")); + } + } break; + } + +} + +#if HAS_STEPPER_RESET + void disableStepperDrivers(); +#endif + +// +// Print Start/Pause/Resume Button +// +void EasythreedUI::printButton() { + enum KeyStatus : uint8_t { KS_IDLE, KS_PRESS, KS_PROCEED }; + static uint8_t key_status = KS_IDLE; + static millis_t key_time = 0; + + enum PrintFlag : uint8_t { PF_START, PF_PAUSE, PF_RESUME }; + static PrintFlag print_key_flag = PF_START; + + const millis_t ms = millis(); + + switch (key_status) { + case KS_IDLE: + if (!READ(BTN_PRINT)) { // Print/Pause/Resume button pressed? + key_time = ms; // Save start time + key_status++; // Go to debounce test + } + break; + + case KS_PRESS: + 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 + 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? + blink_interval_ms = LED_OFF; // Turn off LED + print_key_flag = PF_START; + return; // Bail out + } + card.ls(); // List all files to serial output + const uint16_t filecnt = card.countFilesInWorkDir(); // Count printable files in cwd + if (filecnt == 0) return; // None are printable? + card.selectFileByIndex(filecnt); // Select the last file according to current sort options + card.openAndPrintFile(card.filename); // Start printing it + break; + } + case PF_PAUSE: { // Pause printing (not currently firing) + if (!printingIsActive()) break; + blink_interval_ms = LED_ON; // Set indicator to steady ON + queue.inject(F("M25")); // Queue Pause + print_key_flag = PF_RESUME; // The "Print" button now resumes the print + break; + } + case PF_RESUME: { // Resume printing + if (printingIsActive()) break; + blink_interval_ms = LED_BLINK_2; // Blink the indicator LED at 1 second intervals + queue.inject(F("M24")); // Queue resume + print_key_flag = PF_PAUSE; // The "Print" button now pauses the print + break; + } + } + } + else { // Register a longer press + if (print_key_flag == PF_START && !printingIsActive()) { // While not printing, this moves Z up 10mm + blink_interval_ms = LED_ON; + queue.inject(F("G91\nG0 Z10 F600\nG90")); // Raise Z soon after returning to main loop + } + else { // While printing, cancel print + card.abortFilePrintSoon(); // There is a delay while the current steps play out + blink_interval_ms = LED_OFF; // Turn off LED + } + planner.synchronize(); // Wait for commands already in the planner to finish + TERN_(HAS_STEPPER_RESET, disableStepperDrivers()); // Disable all steppers - now! + print_key_flag = PF_START; // The "Print" button now starts a new print + } + break; + } +} +#endif // EASYTHREED_UI diff --git a/Marlin/src/HAL/TEENSY31_32/watchdog.h b/Marlin/src/feature/easythreed_ui.h similarity index 74% rename from Marlin/src/HAL/TEENSY31_32/watchdog.h rename to Marlin/src/feature/easythreed_ui.h index b8b46a403013f..af9ad2d090b15 100644 --- a/Marlin/src/HAL/TEENSY31_32/watchdog.h +++ b/Marlin/src/feature/easythreed_ui.h @@ -1,6 +1,6 @@ /** * Marlin 3D Printer Firmware - * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] * * Based on Sprinter and grbl. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm @@ -21,14 +21,15 @@ */ #pragma once -#include "HAL.h" +class EasythreedUI { + public: + static void init(); + static void run(); -// Arduino Due core now has watchdog support + private: + static void blinkLED(); + static void loadButton(); + static void printButton(); +}; -void watchdog_init(); - -inline void HAL_watchdog_refresh() { - // Watchdog refresh sequence - WDOG_REFRESH = 0xA602; - WDOG_REFRESH = 0xB480; -} +extern EasythreedUI easythreed_ui; diff --git a/Marlin/src/feature/encoder_i2c.cpp b/Marlin/src/feature/encoder_i2c.cpp index bed24f0525b03..092ce0f8b8520 100644 --- a/Marlin/src/feature/encoder_i2c.cpp +++ b/Marlin/src/feature/encoder_i2c.cpp @@ -49,7 +49,7 @@ void I2CPositionEncoder::init(const uint8_t address, const AxisEnum axis) { initialized = true; - SERIAL_ECHOLNPGM("Setting up encoder on ", AS_CHAR(axis_codes[encoderAxis]), " axis, addr = ", address); + SERIAL_ECHOLNPGM("Setting up encoder on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis, addr = ", address); position = get_position(); } @@ -67,7 +67,7 @@ void I2CPositionEncoder::update() { /* if (trusted) { //commented out as part of the note below trusted = false; - SERIAL_ECHOLNPGM("Fault detected on ", AS_CHAR(axis_codes[encoderAxis]), " axis encoder. Disengaging error correction until module is trusted again."); + SERIAL_ECHOLNPGM("Fault detected on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis encoder. Disengaging error correction until module is trusted again."); } */ return; @@ -92,7 +92,7 @@ void I2CPositionEncoder::update() { if (millis() - lastErrorTime > I2CPE_TIME_TRUSTED) { trusted = true; - SERIAL_ECHOLNPGM("Untrusted encoder module on ", AS_CHAR(axis_codes[encoderAxis]), " axis has been fault-free for set duration, reinstating error correction."); + SERIAL_ECHOLNPGM("Untrusted encoder module on ", AS_CHAR(AXIS_CHAR(encoderAxis)), " axis has been fault-free for set duration, reinstating error correction."); //the encoder likely lost its place when the error occurred, so we'll reset and use the printer's //idea of where it the axis is to re-initialize @@ -153,7 +153,7 @@ void I2CPositionEncoder::update() { #ifdef I2CPE_ERR_THRESH_ABORT if (ABS(error) > I2CPE_ERR_THRESH_ABORT * planner.settings.axis_steps_per_mm[encoderAxis]) { - //kill(PSTR("Significant Error")); + //kill(F("Significant Error")); SERIAL_ECHOLNPGM("Axis error over threshold, aborting!", error); safe_delay(5000); } @@ -172,7 +172,7 @@ void I2CPositionEncoder::update() { float sumP = 0; LOOP_L_N(i, I2CPE_ERR_PRST_ARRAY_SIZE) sumP += errPrst[i]; const int32_t errorP = int32_t(sumP * RECIPROCAL(I2CPE_ERR_PRST_ARRAY_SIZE)); - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" : CORRECT ERR ", errorP * planner.mm_per_step[encoderAxis], "mm"); babystep.add_steps(encoderAxis, -LROUND(errorP)); errPrstIdx = 0; @@ -192,7 +192,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_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" : LARGE ERR ", error, "; diffSum=", diffSum); errorCount++; nextErrorCountTime = ms + I2CPE_ERR_CNT_DEBOUNCE_MS; @@ -212,7 +212,7 @@ void I2CPositionEncoder::set_homed() { homed = trusted = true; #ifdef I2CPE_DEBUG - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" axis encoder homed, offset of ", zeroOffset, " ticks."); #endif } @@ -223,7 +223,7 @@ void I2CPositionEncoder::set_unhomed() { homed = trusted = false; #ifdef I2CPE_DEBUG - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" axis encoder unhomed."); #endif } @@ -231,8 +231,8 @@ void I2CPositionEncoder::set_unhomed() { bool I2CPositionEncoder::passes_test(const bool report) { if (report) { if (H != I2CPE_MAG_SIG_GOOD) SERIAL_ECHOPGM("Warning. "); - SERIAL_CHAR(axis_codes[encoderAxis]); - serial_ternary(H == I2CPE_MAG_SIG_BAD, PSTR(" axis "), PSTR("magnetic strip "), PSTR("encoder ")); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); + serial_ternary(H == I2CPE_MAG_SIG_BAD, F(" axis "), F("magnetic strip "), F("encoder ")); switch (H) { case I2CPE_MAG_SIG_GOOD: case I2CPE_MAG_SIG_MID: @@ -252,7 +252,7 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) { error = ABS(diff) > 10000 ? 0 : diff; // Huge error is a bad reading if (report) { - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" axis target=", target, "mm; actual=", actual, "mm; err=", error, "mm"); } @@ -262,7 +262,7 @@ float I2CPositionEncoder::get_axis_error_mm(const bool report) { int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) { if (!active) { if (report) { - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" axis encoder not active!"); } return 0; @@ -287,7 +287,7 @@ int32_t I2CPositionEncoder::get_axis_error_steps(const bool report) { errorPrev = error; if (report) { - SERIAL_CHAR(axis_codes[encoderAxis]); + SERIAL_CHAR(AXIS_CHAR(encoderAxis)); SERIAL_ECHOLNPGM(" axis target=", target, "; actual=", encoderCountInStepperTicksScaled, "; err=", error); } @@ -337,7 +337,7 @@ bool I2CPositionEncoder::test_axis() { ec = false; xyze_pos_t startCoord, endCoord; - LOOP_LINEAR_AXES(a) { + LOOP_NUM_AXES(a) { startCoord[a] = planner.get_axis_position_mm((AxisEnum)a); endCoord[a] = planner.get_axis_position_mm((AxisEnum)a); } @@ -395,7 +395,7 @@ void I2CPositionEncoder::calibrate_steps_mm(const uint8_t iter) { travelDistance = endDistance - startDistance; xyze_pos_t startCoord, endCoord; - LOOP_LINEAR_AXES(a) { + LOOP_NUM_AXES(a) { startCoord[a] = planner.get_axis_position_mm((AxisEnum)a); endCoord[a] = planner.get_axis_position_mm((AxisEnum)a); } @@ -489,7 +489,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_1_TICKS_REV); #endif #ifdef I2CPE_ENC_1_INVERT - encoders[i].set_inverted(I2CPE_ENC_1_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_1_INVERT)); #endif #ifdef I2CPE_ENC_1_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_1_EC_METHOD); @@ -518,7 +518,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_2_TICKS_REV); #endif #ifdef I2CPE_ENC_2_INVERT - encoders[i].set_inverted(I2CPE_ENC_2_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_2_INVERT)); #endif #ifdef I2CPE_ENC_2_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_2_EC_METHOD); @@ -547,7 +547,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_3_TICKS_REV); #endif #ifdef I2CPE_ENC_3_INVERT - encoders[i].set_inverted(I2CPE_ENC_3_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_3_INVERT)); #endif #ifdef I2CPE_ENC_3_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_3_EC_METHOD); @@ -576,7 +576,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_4_TICKS_REV); #endif #ifdef I2CPE_ENC_4_INVERT - encoders[i].set_inverted(I2CPE_ENC_4_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_4_INVERT)); #endif #ifdef I2CPE_ENC_4_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_4_EC_METHOD); @@ -605,7 +605,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_5_TICKS_REV); #endif #ifdef I2CPE_ENC_5_INVERT - encoders[i].set_inverted(I2CPE_ENC_5_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_5_INVERT)); #endif #ifdef I2CPE_ENC_5_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_5_EC_METHOD); @@ -634,7 +634,7 @@ void I2CPositionEncodersMgr::init() { encoders[i].set_stepper_ticks(I2CPE_ENC_6_TICKS_REV); #endif #ifdef I2CPE_ENC_6_INVERT - encoders[i].set_inverted(I2CPE_ENC_6_INVERT); + encoders[i].set_inverted(ENABLED(I2CPE_ENC_6_INVERT)); #endif #ifdef I2CPE_ENC_6_EC_METHOD encoders[i].set_ec_method(I2CPE_ENC_6_EC_METHOD); @@ -657,7 +657,7 @@ void I2CPositionEncodersMgr::report_position(const int8_t idx, const bool units, else { if (noOffset) { const int32_t raw_count = encoders[idx].get_raw_count(); - SERIAL_CHAR(axis_codes[encoders[idx].get_axis()], ' '); + SERIAL_CHAR(AXIS_CHAR(encoders[idx).get_axis()], ' '); for (uint8_t j = 31; j > 0; j--) SERIAL_ECHO((bool)(0x00000001 & (raw_count >> j))); @@ -712,7 +712,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_codes[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)); } @@ -756,7 +756,7 @@ int8_t I2CPositionEncodersMgr::parse() { if (!parser.has_value()) { SERIAL_ECHOLNPGM("?A seen, but no address specified! [30-200]"); return I2CPE_PARSE_ERR; - }; + } I2CPE_addr = parser.value_byte(); if (!WITHIN(I2CPE_addr, 30, 200)) { // reserve the first 30 and last 55 @@ -775,7 +775,7 @@ int8_t I2CPositionEncodersMgr::parse() { if (!parser.has_value()) { SERIAL_ECHOLNPGM("?I seen, but no index specified! [0-", I2CPE_ENCODER_CNT - 1, "]"); return I2CPE_PARSE_ERR; - }; + } I2CPE_idx = parser.value_byte(); if (I2CPE_idx >= I2CPE_ENCODER_CNT) { @@ -791,7 +791,7 @@ int8_t I2CPositionEncodersMgr::parse() { I2CPE_anyaxis = parser.seen_axis(); return I2CPE_PARSE_OK; -}; +} /** * M860: Report the position(s) of position encoder module(s). @@ -814,7 +814,7 @@ void I2CPositionEncodersMgr::M860() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen_test(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen_test(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) report_position(idx, hasU, hasO); } @@ -841,7 +841,7 @@ void I2CPositionEncodersMgr::M861() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) report_status(idx); } @@ -869,7 +869,7 @@ void I2CPositionEncodersMgr::M862() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) test_axis(idx); } @@ -900,7 +900,7 @@ void I2CPositionEncodersMgr::M863() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) calibrate_steps_mm(idx, iterations); } @@ -934,7 +934,7 @@ void I2CPositionEncodersMgr::M864() { if (!parser.has_value()) { SERIAL_ECHOLNPGM("?S seen, but no address specified! [30-200]"); return; - }; + } newAddress = parser.value_byte(); if (!WITHIN(newAddress, 30, 200)) { @@ -976,7 +976,7 @@ void I2CPositionEncodersMgr::M865() { if (!I2CPE_addr) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) report_module_firmware(encoders[idx].get_address()); } @@ -1007,7 +1007,7 @@ void I2CPositionEncodersMgr::M866() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) { if (hasR) @@ -1045,7 +1045,7 @@ void I2CPositionEncodersMgr::M867() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(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; @@ -1081,7 +1081,7 @@ void I2CPositionEncodersMgr::M868() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(i))) { const uint8_t idx = idx_from_axis(AxisEnum(i)); if ((int8_t)idx >= 0) { if (newThreshold != -9999) @@ -1115,7 +1115,7 @@ void I2CPositionEncodersMgr::M869() { if (I2CPE_idx == 0xFF) { LOOP_LOGICAL_AXES(i) { - if (!I2CPE_anyaxis || parser.seen(axis_codes[i])) { + if (!I2CPE_anyaxis || parser.seen(AXIS_CHAR(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 50fb27a135cae..f25fe2ea6bc4d 100644 --- a/Marlin/src/feature/encoder_i2c.h +++ b/Marlin/src/feature/encoder_i2c.h @@ -261,32 +261,32 @@ class I2CPositionEncodersMgr { static void report_error_count(const int8_t idx, const AxisEnum axis) { CHECK_IDX(); - SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis is ", encoders[idx].get_error_count()); + SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(AXIS_CHAR(axis)), " axis is ", encoders[idx].get_error_count()); } static void reset_error_count(const int8_t idx, const AxisEnum axis) { CHECK_IDX(); encoders[idx].set_error_count(0); - SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(axis_codes[axis]), " axis has been reset."); + SERIAL_ECHOLNPGM("Error count on ", AS_CHAR(AXIS_CHAR(axis)), " axis has been reset."); } static void enable_ec(const int8_t idx, const bool enabled, const AxisEnum axis) { CHECK_IDX(); encoders[idx].set_ec_enabled(enabled); - SERIAL_ECHOPGM("Error correction on ", AS_CHAR(axis_codes[axis])); + SERIAL_ECHOPGM("Error correction on ", AS_CHAR(AXIS_CHAR(axis))); SERIAL_ECHO_TERNARY(encoders[idx].get_ec_enabled(), " axis is ", "en", "dis", "abled.\n"); } static void set_ec_threshold(const int8_t idx, const float newThreshold, const AxisEnum axis) { CHECK_IDX(); encoders[idx].set_ec_threshold(newThreshold); - SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis set to ", newThreshold, "mm."); + SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(AXIS_CHAR(axis)), " axis set to ", newThreshold, "mm."); } static void get_ec_threshold(const int8_t idx, const AxisEnum axis) { CHECK_IDX(); const float threshold = encoders[idx].get_ec_threshold(); - SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(axis_codes[axis]), " axis is ", threshold, "mm."); + SERIAL_ECHOLNPGM("Error correct threshold for ", AS_CHAR(AXIS_CHAR(axis)), " axis is ", threshold, "mm."); } static int8_t idx_from_axis(const AxisEnum axis) { diff --git a/Marlin/src/feature/fancheck.cpp b/Marlin/src/feature/fancheck.cpp new file mode 100644 index 0000000000000..126b79b0a409f --- /dev/null +++ b/Marlin/src/feature/fancheck.cpp @@ -0,0 +1,207 @@ +/** + * 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 . + * + */ + +/** + * fancheck.cpp - fan tachometer check + */ + +#include "../inc/MarlinConfig.h" + +#if HAS_FANCHECK + +#include "fancheck.h" +#include "../module/temperature.h" + +#if HAS_AUTO_FAN && EXTRUDER_AUTO_FAN_SPEED != 255 && DISABLED(FOURWIRES_FANS) + bool FanCheck::measuring = false; +#endif +Flags FanCheck::tacho_state; +uint16_t FanCheck::edge_counter[TACHO_COUNT]; +uint8_t FanCheck::rps[TACHO_COUNT]; +FanCheck::TachoError FanCheck::error = FanCheck::TachoError::NONE; +bool FanCheck::enabled; + +void FanCheck::init() { + #define _TACHINIT(N) TERN(E##N##_FAN_TACHO_PULLUP, SET_INPUT_PULLUP, TERN(E##N##_FAN_TACHO_PULLDOWN, SET_INPUT_PULLDOWN, SET_INPUT))(E##N##_FAN_TACHO_PIN) + #if HAS_E0_FAN_TACHO + _TACHINIT(0); + #endif + #if HAS_E1_FAN_TACHO + _TACHINIT(1); + #endif + #if HAS_E2_FAN_TACHO + _TACHINIT(2); + #endif + #if HAS_E3_FAN_TACHO + _TACHINIT(3); + #endif + #if HAS_E4_FAN_TACHO + _TACHINIT(4); + #endif + #if HAS_E5_FAN_TACHO + _TACHINIT(5); + #endif + #if HAS_E6_FAN_TACHO + _TACHINIT(6); + #endif + #if HAS_E7_FAN_TACHO + _TACHINIT(7); + #endif +} + +void FanCheck::update_tachometers() { + bool status; + + #define _TACHO_CASE(N) case N: status = READ(E##N##_FAN_TACHO_PIN); break; + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + #if HAS_E0_FAN_TACHO + _TACHO_CASE(0) + #endif + #if HAS_E1_FAN_TACHO + _TACHO_CASE(1) + #endif + #if HAS_E2_FAN_TACHO + _TACHO_CASE(2) + #endif + #if HAS_E3_FAN_TACHO + _TACHO_CASE(3) + #endif + #if HAS_E4_FAN_TACHO + _TACHO_CASE(4) + #endif + #if HAS_E5_FAN_TACHO + _TACHO_CASE(5) + #endif + #if HAS_E6_FAN_TACHO + _TACHO_CASE(6) + #endif + #if HAS_E7_FAN_TACHO + _TACHO_CASE(7) + #endif + default: continue; + } + + if (status != tacho_state[f]) { + if (measuring) ++edge_counter[f]; + tacho_state.set(f, status); + } + } +} + +void FanCheck::compute_speed(uint16_t elapsedTime) { + static uint8_t errors_count[TACHO_COUNT]; + static uint8_t fan_reported_errors_msk = 0; + + uint8_t fan_error_msk = 0; + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + TERN_(HAS_E0_FAN_TACHO, case 0:) + TERN_(HAS_E1_FAN_TACHO, case 1:) + TERN_(HAS_E2_FAN_TACHO, case 2:) + TERN_(HAS_E3_FAN_TACHO, case 3:) + TERN_(HAS_E4_FAN_TACHO, case 4:) + TERN_(HAS_E5_FAN_TACHO, case 5:) + TERN_(HAS_E6_FAN_TACHO, case 6:) + TERN_(HAS_E7_FAN_TACHO, case 7:) + // Compute fan speed + rps[f] = edge_counter[f] * float(250) / elapsedTime; + edge_counter[f] = 0; + + // Check fan speed + constexpr int8_t max_extruder_fan_errors = TERN(HAS_PWMFANCHECK, 10000, 5000) / Temperature::fan_update_interval_ms; + + if (rps[f] >= 20 || TERN0(HAS_AUTO_FAN, thermalManager.autofan_speed[f] == 0)) + errors_count[f] = 0; + else if (errors_count[f] < max_extruder_fan_errors) + ++errors_count[f]; + else if (enabled) + SBI(fan_error_msk, f); + break; + } + } + + // Drop the error when all fans are ok + if (!fan_error_msk && error == TachoError::REPORTED) error = TachoError::FIXED; + + if (error == TachoError::FIXED && !printJobOngoing() && !printingIsPaused()) { + error = TachoError::NONE; // if the issue has been fixed while the printer is idle, reenable immediately + ui.reset_alert_level(); + } + + if (fan_error_msk & ~fan_reported_errors_msk) { + // Handle new faults only + LOOP_L_N(f, TACHO_COUNT) if (TEST(fan_error_msk, f)) report_speed_error(f); + } + fan_reported_errors_msk = fan_error_msk; +} + +void FanCheck::report_speed_error(uint8_t fan) { + if (printJobOngoing()) { + if (error == TachoError::NONE) { + if (thermalManager.degTargetHotend(fan) != 0) { + kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT)); + error = TachoError::REPORTED; + } + else + error = TachoError::DETECTED; // Plans error for next processed command + } + } + else if (!printingIsPaused()) { + thermalManager.setTargetHotend(0, fan); // Always disable heating + if (error == TachoError::NONE) error = TachoError::REPORTED; + } + + SERIAL_ERROR_MSG(STR_ERR_FANSPEED, fan); + LCD_ALERTMESSAGE(MSG_FAN_SPEED_FAULT); +} + +void FanCheck::print_fan_states() { + LOOP_L_N(s, 2) { + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + TERN_(HAS_E0_FAN_TACHO, case 0:) + TERN_(HAS_E1_FAN_TACHO, case 1:) + TERN_(HAS_E2_FAN_TACHO, case 2:) + TERN_(HAS_E3_FAN_TACHO, case 3:) + TERN_(HAS_E4_FAN_TACHO, case 4:) + TERN_(HAS_E5_FAN_TACHO, case 5:) + TERN_(HAS_E6_FAN_TACHO, case 6:) + TERN_(HAS_E7_FAN_TACHO, case 7:) + SERIAL_ECHOPGM("E", f); + if (s == 0) + SERIAL_ECHOPGM(":", 60 * rps[f], " RPM "); + else + SERIAL_ECHOPGM("@:", TERN(HAS_AUTO_FAN, thermalManager.autofan_speed[f], 255), " "); + break; + } + } + } + SERIAL_EOL(); +} + +#if ENABLED(AUTO_REPORT_FANS) + AutoReporter FanCheck::auto_reporter; + void FanCheck::AutoReportFan::report() { print_fan_states(); } +#endif + +#endif // HAS_FANCHECK diff --git a/Marlin/src/feature/fancheck.h b/Marlin/src/feature/fancheck.h new file mode 100644 index 0000000000000..b13a34fb19ebb --- /dev/null +++ b/Marlin/src/feature/fancheck.h @@ -0,0 +1,89 @@ +/** + * 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 + +#include "../inc/MarlinConfig.h" + +#if HAS_FANCHECK + +#include "../MarlinCore.h" +#include "../lcd/marlinui.h" + +#if ENABLED(AUTO_REPORT_FANS) + #include "../libs/autoreport.h" +#endif + +#if ENABLED(PARK_HEAD_ON_PAUSE) + #include "../gcode/queue.h" +#endif + +/** + * fancheck.h + */ +#define TACHO_COUNT TERN(HAS_E7_FAN_TACHO, 8, TERN(HAS_E6_FAN_TACHO, 7, TERN(HAS_E5_FAN_TACHO, 6, TERN(HAS_E4_FAN_TACHO, 5, TERN(HAS_E3_FAN_TACHO, 4, TERN(HAS_E2_FAN_TACHO, 3, TERN(HAS_E1_FAN_TACHO, 2, 1))))))) + +class FanCheck { + private: + + enum class TachoError : uint8_t { NONE, DETECTED, REPORTED, FIXED }; + + #if HAS_PWMFANCHECK + static bool measuring; // For future use (3 wires PWM controlled fans) + #else + static constexpr bool measuring = true; + #endif + static Flags tacho_state; + static uint16_t edge_counter[TACHO_COUNT]; + static uint8_t rps[TACHO_COUNT]; + static TachoError error; + + static void report_speed_error(uint8_t fan); + + public: + + static bool enabled; + + static void init(); + static void update_tachometers(); + static void compute_speed(uint16_t elapsedTime); + static void print_fan_states(); + #if HAS_PWMFANCHECK + static void toggle_measuring() { measuring = !measuring; } + static bool is_measuring() { return measuring; } + #endif + + static void check_deferred_error() { + if (error == TachoError::DETECTED) { + error = TachoError::REPORTED; + TERN(PARK_HEAD_ON_PAUSE, queue.inject(F("M125")), kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT))); + } + } + + #if ENABLED(AUTO_REPORT_FANS) + struct AutoReportFan { static void report(); }; + static AutoReporter auto_reporter; + #endif +}; + +extern FanCheck fan_check; + +#endif // HAS_FANCHECK diff --git a/Marlin/src/feature/filwidth.h b/Marlin/src/feature/filwidth.h index e63d3d719ffb6..9eb1e77762ff0 100644 --- a/Marlin/src/feature/filwidth.h +++ b/Marlin/src/feature/filwidth.h @@ -41,9 +41,9 @@ class FilamentWidthSensor { FilamentWidthSensor() { init(); } static void init(); - static inline void enable(const bool ena) { enabled = ena; } + static void enable(const bool ena) { enabled = ena; } - static inline void set_delay_cm(const uint8_t cm) { + static void set_delay_cm(const uint8_t cm) { meas_delay_cm = _MIN(cm, MAX_MEASUREMENT_DELAY); } @@ -67,18 +67,18 @@ class FilamentWidthSensor { } // Convert raw measurement to mm - static inline float raw_to_mm(const uint16_t v) { return v * 5.0f * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); } - static inline float raw_to_mm() { return raw_to_mm(raw); } + static float raw_to_mm(const uint16_t v) { return v * float(ADC_VREF) * RECIPROCAL(float(MAX_RAW_THERMISTOR_VALUE)); } + static float raw_to_mm() { return raw_to_mm(raw); } // A scaled reading is ready // Divide to get to 0-16384 range since we used 1/128 IIR filter approach - static inline void reading_ready() { raw = accum >> 10; } + static void reading_ready() { raw = accum >> 10; } // Update mm from the raw measurement - static inline void update_measured_mm() { measured_mm = raw_to_mm(); } + static void update_measured_mm() { measured_mm = raw_to_mm(); } // Update ring buffer used to delay filament measurements - static inline void advance_e(const_float_t e_move) { + static void advance_e(const_float_t e_move) { // Increment counters with the E distance e_count += e_move; @@ -106,7 +106,7 @@ class FilamentWidthSensor { } // Dynamically set the volumetric multiplier based on the delayed width measurement. - static inline void update_volumetric() { + static void update_volumetric() { if (enabled) { int8_t read_index = index_r - meas_delay_cm; if (read_index < 0) read_index += MMD_CM; // Loop around buffer if needed diff --git a/Marlin/src/feature/fwretract.cpp b/Marlin/src/feature/fwretract.cpp index 4077d8d1c2098..28355640d223f 100644 --- a/Marlin/src/feature/fwretract.cpp +++ b/Marlin/src/feature/fwretract.cpp @@ -34,7 +34,6 @@ FWRetract fwretract; // Single instance - this calls the constructor #include "../module/motion.h" #include "../module/planner.h" -#include "../module/stepper.h" #include "../gcode/gcode.h" @@ -45,7 +44,7 @@ FWRetract fwretract; // Single instance - this calls the constructor // private: #if HAS_MULTI_EXTRUDER - bool FWRetract::retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted + Flags FWRetract::retracted_swap; // Which extruders are swap-retracted #endif // public: @@ -56,7 +55,7 @@ fwretract_settings_t FWRetract::settings; // M207 S F Z W, M208 S F bool FWRetract::autoretract_enabled; // M209 S - Autoretract switch #endif -bool FWRetract::retracted[EXTRUDERS]; // Which extruders are currently retracted +Flags FWRetract::retracted; // Which extruders are currently retracted float FWRetract::current_retract[EXTRUDERS], // Retract value used by planner FWRetract::current_hop; @@ -73,10 +72,10 @@ void FWRetract::reset() { settings.swap_retract_recover_feedrate_mm_s = RETRACT_RECOVER_FEEDRATE_SWAP; current_hop = 0.0; - LOOP_L_N(i, EXTRUDERS) { - retracted[i] = false; - E_TERN_(retracted_swap[i] = false); - current_retract[i] = 0.0; + retracted.reset(); + EXTRUDER_LOOP() { + E_TERN_(retracted_swap.clear(e)); + current_retract[e] = 0.0; } } @@ -111,10 +110,10 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) " swapping ", swapping, " active extruder ", active_extruder ); - LOOP_L_N(i, EXTRUDERS) { - SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i])); + EXTRUDER_LOOP() { + SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e])); #if HAS_MULTI_EXTRUDER - SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i])); + SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e])); #endif } SERIAL_ECHOLNPGM("current_position.z ", current_position.z); @@ -173,21 +172,21 @@ void FWRetract::retract(const bool retracting E_OPTARG(bool swapping/*=false*/)) TERN_(RETRACT_SYNC_MIXING, mixer.T(old_mixing_tool)); // Restore original mixing tool - retracted[active_extruder] = retracting; // Active extruder now retracted / recovered + retracted.set(active_extruder, retracting); // Active extruder now retracted / recovered // If swap retract/recover update the retracted_swap flag too #if HAS_MULTI_EXTRUDER - if (swapping) retracted_swap[active_extruder] = retracting; + if (swapping) retracted_swap.set(active_extruder, retracting); #endif /* // debugging SERIAL_ECHOLNPGM("retracting ", AS_DIGIT(retracting)); SERIAL_ECHOLNPGM("swapping ", AS_DIGIT(swapping)); SERIAL_ECHOLNPGM("active_extruder ", active_extruder); - LOOP_L_N(i, EXTRUDERS) { - SERIAL_ECHOLNPGM("retracted[", i, "] ", AS_DIGIT(retracted[i])); + EXTRUDER_LOOP() { + SERIAL_ECHOLNPGM("retracted[", e, "] ", AS_DIGIT(retracted[e])); #if HAS_MULTI_EXTRUDER - SERIAL_ECHOLNPGM("retracted_swap[", i, "] ", AS_DIGIT(retracted_swap[i])); + SERIAL_ECHOLNPGM("retracted_swap[", e, "] ", AS_DIGIT(retracted_swap[e])); #endif } SERIAL_ECHOLNPGM("current_position.z ", current_position.z); diff --git a/Marlin/src/feature/fwretract.h b/Marlin/src/feature/fwretract.h index d6d0432e3aa70..db2a62c8d4107 100644 --- a/Marlin/src/feature/fwretract.h +++ b/Marlin/src/feature/fwretract.h @@ -43,7 +43,7 @@ typedef struct { class FWRetract { private: #if HAS_MULTI_EXTRUDER - static bool retracted_swap[EXTRUDERS]; // Which extruders are swap-retracted + static Flags retracted_swap; // Which extruders are swap-retracted #endif public: @@ -55,7 +55,7 @@ class FWRetract { static constexpr bool autoretract_enabled = false; #endif - static bool retracted[EXTRUDERS]; // Which extruders are currently retracted + static Flags retracted; // Which extruders are currently retracted static float current_retract[EXTRUDERS], // Retract value used by planner current_hop; // Hop value used by planner @@ -63,9 +63,7 @@ class FWRetract { static void reset(); - static void refresh_autoretract() { - LOOP_L_N(i, EXTRUDERS) retracted[i] = false; - } + static void refresh_autoretract() { retracted.reset(); } static void enable_autoretract(const bool enable) { #if ENABLED(FWRETRACT_AUTORETRACT) diff --git a/Marlin/src/feature/host_actions.cpp b/Marlin/src/feature/host_actions.cpp index 62e60320f7638..c03a6bc5976e6 100644 --- a/Marlin/src/feature/host_actions.cpp +++ b/Marlin/src/feature/host_actions.cpp @@ -24,10 +24,10 @@ #if ENABLED(HOST_ACTION_COMMANDS) -#include "host_actions.h" - //#define DEBUG_HOST_ACTIONS +#include "host_actions.h" + #if ENABLED(ADVANCED_PAUSE_FEATURE) #include "pause.h" #include "../gcode/queue.h" @@ -37,37 +37,54 @@ #include "runout.h" #endif -void host_action(PGM_P const pstr, const bool eol) { +HostUI hostui; + +void HostUI::action(FSTR_P const fstr, const bool eol) { PORT_REDIRECT(SerialMask::All); SERIAL_ECHOPGM("//action:"); - SERIAL_ECHOPGM_P(pstr); + SERIAL_ECHOF(fstr); if (eol) SERIAL_EOL(); } #ifdef ACTION_ON_KILL - void host_action_kill() { host_action(PSTR(ACTION_ON_KILL)); } + void HostUI::kill() { action(F(ACTION_ON_KILL)); } #endif #ifdef ACTION_ON_PAUSE - void host_action_pause(const bool eol/*=true*/) { host_action(PSTR(ACTION_ON_PAUSE), eol); } + void HostUI::pause(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSE), eol); } #endif #ifdef ACTION_ON_PAUSED - void host_action_paused(const bool eol/*=true*/) { host_action(PSTR(ACTION_ON_PAUSED), eol); } + void HostUI::paused(const bool eol/*=true*/) { action(F(ACTION_ON_PAUSED), eol); } #endif #ifdef ACTION_ON_RESUME - void host_action_resume() { host_action(PSTR(ACTION_ON_RESUME)); } + void HostUI::resume() { action(F(ACTION_ON_RESUME)); } #endif #ifdef ACTION_ON_RESUMED - void host_action_resumed() { host_action(PSTR(ACTION_ON_RESUMED)); } + void HostUI::resumed() { action(F(ACTION_ON_RESUMED)); } #endif #ifdef ACTION_ON_CANCEL - void host_action_cancel() { host_action(PSTR(ACTION_ON_CANCEL)); } + void HostUI::cancel() { action(F(ACTION_ON_CANCEL)); } #endif #ifdef ACTION_ON_START - void host_action_start() { host_action(PSTR(ACTION_ON_START)); } + void HostUI::start() { action(F(ACTION_ON_START)); } +#endif + +#if ENABLED(G29_RETRY_AND_RECOVER) + #ifdef ACTION_ON_G29_RECOVER + void HostUI::g29_recover() { action(F(ACTION_ON_G29_RECOVER)); } + #endif + #ifdef ACTION_ON_G29_FAILURE + void HostUI::g29_failure() { action(F(ACTION_ON_G29_FAILURE)); } + #endif +#endif + +#ifdef SHUTDOWN_ACTION + void HostUI::shutdown() { action(F(SHUTDOWN_ACTION)); } #endif #if ENABLED(HOST_PROMPT_SUPPORT) + PromptReason HostUI::host_prompt_reason = PROMPT_NOT_DEFINED; + PGMSTR(CONTINUE_STR, "Continue"); PGMSTR(DISMISS_STR, "Dismiss"); @@ -75,64 +92,64 @@ void host_action(PGM_P const pstr, const bool eol) { extern bool wait_for_user; #endif - PromptReason host_prompt_reason = PROMPT_NOT_DEFINED; - - void host_action_notify(const char * const message) { + void HostUI::notify(const char * const cstr) { PORT_REDIRECT(SerialMask::All); - host_action(PSTR("notification "), false); - SERIAL_ECHOLN(message); + action(F("notification "), false); + SERIAL_ECHOLN(cstr); } - void host_action_notify_P(PGM_P const message) { + void HostUI::notify_P(PGM_P const pstr) { PORT_REDIRECT(SerialMask::All); - host_action(PSTR("notification "), false); - SERIAL_ECHOLNPGM_P(message); + action(F("notification "), false); + SERIAL_ECHOLNPGM_P(pstr); } - void host_action_prompt(PGM_P const ptype, const bool eol=true) { + void HostUI::prompt(FSTR_P const ptype, const bool eol/*=true*/) { PORT_REDIRECT(SerialMask::All); - host_action(PSTR("prompt_"), false); - SERIAL_ECHOPGM_P(ptype); + action(F("prompt_"), false); + SERIAL_ECHOF(ptype); if (eol) SERIAL_EOL(); } - void host_action_prompt_plus(PGM_P const ptype, PGM_P const pstr, const char extra_char='\0') { - host_action_prompt(ptype, false); + void HostUI::prompt_plus(FSTR_P const ptype, FSTR_P const fstr, const char extra_char/*='\0'*/) { + prompt(ptype, false); PORT_REDIRECT(SerialMask::All); SERIAL_CHAR(' '); - SERIAL_ECHOPGM_P(pstr); + SERIAL_ECHOF(fstr); if (extra_char != '\0') SERIAL_CHAR(extra_char); SERIAL_EOL(); } - void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char/*='\0'*/) { - host_action_prompt_end(); + void HostUI::prompt_begin(const PromptReason reason, FSTR_P const fstr, const char extra_char/*='\0'*/) { + prompt_end(); host_prompt_reason = reason; - host_action_prompt_plus(PSTR("begin"), pstr, extra_char); + prompt_plus(F("begin"), fstr, extra_char); } - void host_action_prompt_button(PGM_P const pstr) { host_action_prompt_plus(PSTR("button"), pstr); } - void host_action_prompt_end() { host_action_prompt(PSTR("end")); } - void host_action_prompt_show() { host_action_prompt(PSTR("show")); } - - void _host_prompt_show(PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) { - if (btn1) host_action_prompt_button(btn1); - if (btn2) host_action_prompt_button(btn2); - host_action_prompt_show(); + void HostUI::prompt_button(FSTR_P const fstr) { prompt_plus(F("button"), fstr); } + void HostUI::prompt_end() { prompt(F("end")); } + void HostUI::prompt_show() { prompt(F("show")); } + + void HostUI::_prompt_show(FSTR_P const btn1, FSTR_P const btn2) { + if (btn1) prompt_button(btn1); + if (btn2) prompt_button(btn2); + prompt_show(); } - void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) { - host_action_prompt_begin(reason, pstr); - _host_prompt_show(btn1, btn2); + void HostUI::prompt_do(const PromptReason reason, FSTR_P const fstr, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) { + prompt_begin(reason, fstr); + _prompt_show(btn1, btn2); } - void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1/*=nullptr*/, PGM_P const btn2/*=nullptr*/) { - host_action_prompt_begin(reason, pstr, extra_char); - _host_prompt_show(btn1, btn2); + void HostUI::prompt_do(const PromptReason reason, FSTR_P const fstr, const char extra_char, FSTR_P const btn1/*=nullptr*/, FSTR_P const btn2/*=nullptr*/) { + prompt_begin(reason, fstr, extra_char); + _prompt_show(btn1, btn2); } - void filament_load_host_prompt() { - const bool disable_to_continue = TERN0(HAS_FILAMENT_SENSOR, runout.filament_ran_out); - host_prompt_do(PROMPT_FILAMENT_RUNOUT, PSTR("Paused"), PSTR("PurgeMore"), - disable_to_continue ? PSTR("DisableRunout") : CONTINUE_STR - ); - } + #if ENABLED(ADVANCED_PAUSE_FEATURE) + void HostUI::filament_load_prompt() { + const bool disable_to_continue = TERN0(HAS_FILAMENT_SENSOR, runout.filament_ran_out); + prompt_do(PROMPT_FILAMENT_RUNOUT, F("Paused"), F("PurgeMore"), + disable_to_continue ? F("DisableRunout") : FPSTR(CONTINUE_STR) + ); + } + #endif // // Handle responses from the host, such as: @@ -141,7 +158,7 @@ void host_action(PGM_P const pstr, const bool eol) { // - Resume Print response // - Dismissal of info // - void host_response_handler(const uint8_t response) { + void HostUI::handle_response(const uint8_t response) { const PromptReason hpr = host_prompt_reason; host_prompt_reason = PROMPT_NOT_DEFINED; // Reset now ahead of logic switch (hpr) { diff --git a/Marlin/src/feature/host_actions.h b/Marlin/src/feature/host_actions.h index 065b59d755edd..41d66b82ec9ba 100644 --- a/Marlin/src/feature/host_actions.h +++ b/Marlin/src/feature/host_actions.h @@ -24,34 +24,8 @@ #include "../inc/MarlinConfigPre.h" #include "../HAL/shared/Marduino.h" -void host_action(PGM_P const pstr, const bool eol=true); - -#ifdef ACTION_ON_KILL - void host_action_kill(); -#endif -#ifdef ACTION_ON_PAUSE - void host_action_pause(const bool eol=true); -#endif -#ifdef ACTION_ON_PAUSED - void host_action_paused(const bool eol=true); -#endif -#ifdef ACTION_ON_RESUME - void host_action_resume(); -#endif -#ifdef ACTION_ON_RESUMED - void host_action_resumed(); -#endif -#ifdef ACTION_ON_CANCEL - void host_action_cancel(); -#endif -#ifdef ACTION_ON_START - void host_action_start(); -#endif - #if ENABLED(HOST_PROMPT_SUPPORT) - extern const char CONTINUE_STR[], DISMISS_STR[]; - enum PromptReason : uint8_t { PROMPT_NOT_DEFINED, PROMPT_FILAMENT_RUNOUT, @@ -61,21 +35,80 @@ void host_action(PGM_P const pstr, const bool eol=true); PROMPT_INFO }; - extern PromptReason host_prompt_reason; +#endif - void host_response_handler(const uint8_t response); - void host_action_notify(const char * const message); - void host_action_notify_P(PGM_P const message); - void host_action_prompt_begin(const PromptReason reason, PGM_P const pstr, const char extra_char='\0'); - void host_action_prompt_button(PGM_P const pstr); - void host_action_prompt_end(); - void host_action_prompt_show(); - void host_prompt_do(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr); - void host_prompt_do(const PromptReason reason, PGM_P const pstr, const char extra_char, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr); - inline void host_prompt_open(const PromptReason reason, PGM_P const pstr, PGM_P const btn1=nullptr, PGM_P const btn2=nullptr) { - if (host_prompt_reason == PROMPT_NOT_DEFINED) host_prompt_do(reason, pstr, btn1, btn2); - } +class HostUI { + public: - void filament_load_host_prompt(); + static void action(FSTR_P const fstr, const bool eol=true); -#endif + #ifdef ACTION_ON_KILL + static void kill(); + #endif + #ifdef ACTION_ON_PAUSE + static void pause(const bool eol=true); + #endif + #ifdef ACTION_ON_PAUSED + static void paused(const bool eol=true); + #endif + #ifdef ACTION_ON_RESUME + static void resume(); + #endif + #ifdef ACTION_ON_RESUMED + static void resumed(); + #endif + #ifdef ACTION_ON_CANCEL + static void cancel(); + #endif + #ifdef ACTION_ON_START + static void start(); + #endif + #ifdef SHUTDOWN_ACTION + static void shutdown(); + #endif + + #if ENABLED(G29_RETRY_AND_RECOVER) + #ifdef ACTION_ON_G29_RECOVER + static void g29_recover(); + #endif + #ifdef ACTION_ON_G29_FAILURE + static void g29_failure(); + #endif + #endif + + #if ENABLED(HOST_PROMPT_SUPPORT) + private: + static void prompt(FSTR_P const ptype, const bool eol=true); + static void prompt_plus(FSTR_P const ptype, FSTR_P const fstr, const char extra_char='\0'); + static void prompt_show(); + static void _prompt_show(FSTR_P const btn1, FSTR_P const btn2); + + public: + static PromptReason host_prompt_reason; + + static void handle_response(const uint8_t response); + + static void notify_P(PGM_P const message); + static void notify(FSTR_P const fmsg) { notify_P(FTOP(fmsg)); } + static void notify(const char * const message); + + static void prompt_begin(const PromptReason reason, FSTR_P const fstr, const char extra_char='\0'); + static void prompt_button(FSTR_P const fstr); + static void prompt_end(); + static void prompt_do(const PromptReason reason, FSTR_P const pstr, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr); + static void prompt_do(const PromptReason reason, FSTR_P const pstr, const char extra_char, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr); + static void prompt_open(const PromptReason reason, FSTR_P const pstr, FSTR_P const btn1=nullptr, FSTR_P const btn2=nullptr) { + if (host_prompt_reason == PROMPT_NOT_DEFINED) prompt_do(reason, pstr, btn1, btn2); + } + + #if ENABLED(ADVANCED_PAUSE_FEATURE) + static void filament_load_prompt(); + #endif + + #endif + +}; + +extern HostUI hostui; + +extern const char CONTINUE_STR[], DISMISS_STR[]; diff --git a/Marlin/src/feature/hotend_idle.cpp b/Marlin/src/feature/hotend_idle.cpp index b962743ed0456..4b137f42da7bd 100644 --- a/Marlin/src/feature/hotend_idle.cpp +++ b/Marlin/src/feature/hotend_idle.cpp @@ -77,7 +77,7 @@ void HotendIdleProtection::check() { void HotendIdleProtection::timed_out() { next_protect_ms = 0; SERIAL_ECHOLNPGM("Hotend Idle Timeout"); - LCD_MESSAGEPGM(MSG_HOTEND_IDLE_TIMEOUT); + LCD_MESSAGE(MSG_HOTEND_IDLE_TIMEOUT); HOTEND_LOOP() { if ((HOTEND_IDLE_NOZZLE_TARGET) < thermalManager.degTargetHotend(e)) thermalManager.setTargetHotend(HOTEND_IDLE_NOZZLE_TARGET, e); diff --git a/Marlin/src/feature/joystick.cpp b/Marlin/src/feature/joystick.cpp index 7f91c1549b9e1..acab5d7437a20 100644 --- a/Marlin/src/feature/joystick.cpp +++ b/Marlin/src/feature/joystick.cpp @@ -68,13 +68,13 @@ Joystick joystick; void Joystick::report() { SERIAL_ECHOPGM("Joystick"); #if HAS_JOY_ADC_X - SERIAL_ECHOPGM_P(SP_X_STR, JOY_X(x.raw)); + SERIAL_ECHOPGM_P(SP_X_STR, JOY_X(x.getraw())); #endif #if HAS_JOY_ADC_Y - SERIAL_ECHOPGM_P(SP_Y_STR, JOY_Y(y.raw)); + SERIAL_ECHOPGM_P(SP_Y_STR, JOY_Y(y.getraw())); #endif #if HAS_JOY_ADC_Z - SERIAL_ECHOPGM_P(SP_Z_STR, JOY_Z(z.raw)); + SERIAL_ECHOPGM_P(SP_Z_STR, JOY_Z(z.getraw())); #endif #if HAS_JOY_ADC_EN SERIAL_ECHO_TERNARY(READ(JOY_EN_PIN), " EN=", "HIGH (dis", "LOW (en", "abled)"); @@ -91,29 +91,29 @@ Joystick joystick; if (READ(JOY_EN_PIN)) return; #endif - auto _normalize_joy = [](float &axis_jog, const int16_t raw, const int16_t (&joy_limits)[4]) { + auto _normalize_joy = [](float &axis_jog, const raw_adc_t raw, const raw_adc_t (&joy_limits)[4]) { if (WITHIN(raw, joy_limits[0], joy_limits[3])) { // within limits, check deadzone if (raw > joy_limits[2]) axis_jog = (raw - joy_limits[2]) / float(joy_limits[3] - joy_limits[2]); else if (raw < joy_limits[1]) - axis_jog = (raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]); // negative value + axis_jog = int16_t(raw - joy_limits[1]) / float(joy_limits[1] - joy_limits[0]); // negative value // Map normal to jog value via quadratic relationship axis_jog = SIGN(axis_jog) * sq(axis_jog); } }; #if HAS_JOY_ADC_X - static constexpr int16_t joy_x_limits[4] = JOY_X_LIMITS; - _normalize_joy(norm_jog.x, JOY_X(x.raw), joy_x_limits); + static constexpr raw_adc_t joy_x_limits[4] = JOY_X_LIMITS; + _normalize_joy(norm_jog.x, JOY_X(x.getraw()), joy_x_limits); #endif #if HAS_JOY_ADC_Y - static constexpr int16_t joy_y_limits[4] = JOY_Y_LIMITS; - _normalize_joy(norm_jog.y, JOY_Y(y.raw), joy_y_limits); + static constexpr raw_adc_t joy_y_limits[4] = JOY_Y_LIMITS; + _normalize_joy(norm_jog.y, JOY_Y(y.getraw()), joy_y_limits); #endif #if HAS_JOY_ADC_Z - static constexpr int16_t joy_z_limits[4] = JOY_Z_LIMITS; - _normalize_joy(norm_jog.z, JOY_Z(z.raw), joy_z_limits); + static constexpr raw_adc_t joy_z_limits[4] = JOY_Z_LIMITS; + _normalize_joy(norm_jog.z, JOY_Z(z.getraw()), joy_z_limits); #endif } @@ -163,7 +163,7 @@ Joystick joystick; // norm_jog values of [-1 .. 1] maps linearly to [-feedrate .. feedrate] xyz_float_t move_dist{0}; float hypot2 = 0; - LOOP_LINEAR_AXES(i) if (norm_jog[i]) { + LOOP_NUM_AXES(i) if (norm_jog[i]) { move_dist[i] = seg_time * norm_jog[i] * TERN(EXTENSIBLE_UI, manual_feedrate_mm_s, planner.settings.max_feedrate_mm_s)[i]; hypot2 += sq(move_dist[i]); } @@ -172,8 +172,9 @@ Joystick joystick; current_position += move_dist; apply_motion_limits(current_position); const float length = sqrt(hypot2); + PlannerHints hints(length); injecting_now = true; - planner.buffer_line(current_position, length / seg_time, active_extruder, length); + planner.buffer_line(current_position, length / seg_time, active_extruder, hints); injecting_now = false; } } diff --git a/Marlin/src/feature/leds/leds.cpp b/Marlin/src/feature/leds/leds.cpp index 17e31afa5a731..2a53a7c884e64 100644 --- a/Marlin/src/feature/leds/leds.cpp +++ b/Marlin/src/feature/leds/leds.cpp @@ -42,7 +42,7 @@ #include "pca9533.h" #endif -#if ENABLED(CASE_LIGHT_USE_RGB_LED) +#if EITHER(CASE_LIGHT_USE_RGB_LED, CASE_LIGHT_USE_NEOPIXEL) #include "../../feature/caselight.h" #endif @@ -95,6 +95,10 @@ void LEDLights::set_color(const LEDColor &incol #endif #endif + #if BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL) + // Update brightness only if caselight is ON or switching leds off + if (caselight.on || incol.is_off()) + #endif neo.set_brightness(incol.i); #if ENABLED(NEOPIXEL_IS_SEQUENTIAL) @@ -106,6 +110,10 @@ void LEDLights::set_color(const LEDColor &incol } #endif + #if BOTH(CASE_LIGHT_MENU, CASE_LIGHT_USE_NEOPIXEL) + // Update color only if caselight is ON or switching leds off + if (caselight.on || incol.is_off()) + #endif neo.set_color(neocolor); #endif @@ -121,11 +129,11 @@ void LEDLights::set_color(const LEDColor &incol // This variant uses 3-4 separate pins for the RGB(W) components. // If the pins can do PWM then their intensity will be set. - #define _UPDATE_RGBW(C,c) do { \ - if (PWM_PIN(RGB_LED_##C##_PIN)) \ - analogWrite(pin_t(RGB_LED_##C##_PIN), c); \ - else \ - WRITE(RGB_LED_##C##_PIN, c ? HIGH : LOW); \ + #define _UPDATE_RGBW(C,c) do { \ + if (PWM_PIN(RGB_LED_##C##_PIN)) \ + hal.set_pwm_duty(pin_t(RGB_LED_##C##_PIN), c); \ + else \ + WRITE(RGB_LED_##C##_PIN, c ? HIGH : LOW); \ }while(0) #define UPDATE_RGBW(C,c) _UPDATE_RGBW(C, TERN1(CASE_LIGHT_USE_RGB_LED, caselight.on) ? incol.c : 0) UPDATE_RGBW(R,r); UPDATE_RGBW(G,g); UPDATE_RGBW(B,b); @@ -150,7 +158,7 @@ void LEDLights::set_color(const LEDColor &incol void LEDLights::toggle() { if (lights_on) set_off(); else update(); } #endif -#ifdef LED_BACKLIGHT_TIMEOUT +#if LED_POWEROFF_TIMEOUT > 0 millis_t LEDLights::led_off_time; // = 0 diff --git a/Marlin/src/feature/leds/leds.h b/Marlin/src/feature/leds/leds.h index 74964b51a8e46..8649dd014fbfd 100644 --- a/Marlin/src/feature/leds/leds.h +++ b/Marlin/src/feature/leds/leds.h @@ -54,6 +54,8 @@ typedef struct LEDColor { OPTARG(NEOPIXEL_LED, i(NEOPIXEL_BRIGHTNESS)) {} + 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)) : r(r), g(g), b(b) OPTARG(HAS_WHITE_LED, w(w)) OPTARG(NEOPIXEL_LED, i(i)) {} @@ -68,11 +70,6 @@ typedef struct LEDColor { return *this; } - LEDColor& operator=(const LEDColor &right) { - if (this != &right) memcpy(this, &right, sizeof(LEDColor)); - return *this; - } - bool operator==(const LEDColor &right) { if (this == &right) return true; return 0 == memcmp(this, &right, sizeof(LEDColor)); @@ -118,7 +115,7 @@ class LEDLights { OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false) ); - static inline void set_color(uint8_t r, uint8_t g, uint8_t b + static void set_color(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) OPTARG(NEOPIXEL_IS_SEQUENTIAL, bool isSequence=false) @@ -126,23 +123,23 @@ class LEDLights { set_color(LEDColor(r, g, b OPTARG(HAS_WHITE_LED, w) OPTARG(NEOPIXEL_LED, i)) OPTARG(NEOPIXEL_IS_SEQUENTIAL, isSequence)); } - static inline void set_off() { set_color(LEDColorOff()); } - static inline void set_green() { set_color(LEDColorGreen()); } - static inline void set_white() { set_color(LEDColorWhite()); } + static void set_off() { set_color(LEDColorOff()); } + static void set_green() { set_color(LEDColorGreen()); } + static void set_white() { set_color(LEDColorWhite()); } #if ENABLED(LED_COLOR_PRESETS) static const LEDColor defaultLEDColor; - static inline void set_default() { set_color(defaultLEDColor); } - static inline void set_red() { set_color(LEDColorRed()); } - static inline void set_orange() { set_color(LEDColorOrange()); } - static inline void set_yellow() { set_color(LEDColorYellow()); } - static inline void set_blue() { set_color(LEDColorBlue()); } - static inline void set_indigo() { set_color(LEDColorIndigo()); } - static inline void set_violet() { set_color(LEDColorViolet()); } + 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()); } #endif #if ENABLED(PRINTER_EVENT_LEDS) - static inline LEDColor get_color() { return lights_on ? color : LEDColorOff(); } + static LEDColor get_color() { return lights_on ? color : LEDColorOff(); } #endif #if ANY(LED_CONTROL_MENU, PRINTER_EVENT_LEDS, CASE_LIGHT_IS_COLOR_LED) @@ -154,15 +151,15 @@ class LEDLights { static void toggle(); // swap "off" with color #endif #if EITHER(LED_CONTROL_MENU, CASE_LIGHT_USE_RGB_LED) - static inline void update() { set_color(color); } + static void update() { set_color(color); } #endif - #ifdef LED_BACKLIGHT_TIMEOUT + #if LED_POWEROFF_TIMEOUT > 0 private: static millis_t led_off_time; public: - static inline void reset_timeout(const millis_t &ms) { - led_off_time = ms + LED_BACKLIGHT_TIMEOUT; + static void reset_timeout(const millis_t &ms) { + led_off_time = ms + LED_POWEROFF_TIMEOUT; if (!lights_on) update(); } static void update_timeout(const bool power_on); @@ -181,7 +178,7 @@ extern LEDLights leds; static void set_color(const LEDColor &color); - static inline void set_color(uint8_t r, uint8_t g, uint8_t b + static void set_color(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) ) { @@ -191,26 +188,26 @@ extern LEDLights leds; )); } - static inline void set_off() { set_color(LEDColorOff()); } - static inline void set_green() { set_color(LEDColorGreen()); } - static inline void set_white() { set_color(LEDColorWhite()); } + static void set_off() { set_color(LEDColorOff()); } + static void set_green() { set_color(LEDColorGreen()); } + static void set_white() { set_color(LEDColorWhite()); } #if ENABLED(NEO2_COLOR_PRESETS) static const LEDColor defaultLEDColor; - static inline void set_default() { set_color(defaultLEDColor); } - static inline void set_red() { set_color(LEDColorRed()); } - static inline void set_orange() { set_color(LEDColorOrange()); } - static inline void set_yellow() { set_color(LEDColorYellow()); } - static inline void set_blue() { set_color(LEDColorBlue()); } - static inline void set_indigo() { set_color(LEDColorIndigo()); } - static inline void set_violet() { set_color(LEDColorViolet()); } + 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()); } #endif #if ENABLED(NEOPIXEL2_SEPARATE) static LEDColor color; // last non-off color static bool lights_on; // the last set color was "on" static void toggle(); // swap "off" with color - static inline void update() { set_color(color); } + static void update() { set_color(color); } #endif }; diff --git a/Marlin/src/feature/leds/neopixel.cpp b/Marlin/src/feature/leds/neopixel.cpp index 2654e9a1df5a6..4f104234f15c3 100644 --- a/Marlin/src/feature/leds/neopixel.cpp +++ b/Marlin/src/feature/leds/neopixel.cpp @@ -35,7 +35,7 @@ #endif Marlin_NeoPixel neo; -int8_t Marlin_NeoPixel::neoindex; +pixel_index_t Marlin_NeoPixel::neoindex; Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIXEL_TYPE + NEO_KHZ800); #if CONJOINED_NEOPIXEL @@ -44,14 +44,14 @@ Adafruit_NeoPixel Marlin_NeoPixel::adaneo1(NEOPIXEL_PIXELS, NEOPIXEL_PIN, NEOPIX #ifdef NEOPIXEL_BKGD_INDEX_FIRST - void Marlin_NeoPixel::set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w) { - for (int background_led = NEOPIXEL_BKGD_INDEX_FIRST; background_led <= NEOPIXEL_BKGD_INDEX_LAST; background_led++) + void Marlin_NeoPixel::set_background_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w) { + for (int background_led = NEOPIXEL_BKGD_INDEX_FIRST; background_led <= NEOPIXEL_BKGD_INDEX_LAST; background_led++) set_pixel_color(background_led, adaneo1.Color(r, g, b, w)); } void Marlin_NeoPixel::reset_background_color() { constexpr uint8_t background_color[4] = NEOPIXEL_BKGD_COLOR; - set_background_color(background_color[0], background_color[1], background_color[2], background_color[3]); + set_background_color(background_color); } #endif @@ -108,7 +108,7 @@ void Marlin_NeoPixel::init() { set_color(adaneo1.Color TERN(LED_USER_PRESET_STARTUP, (LED_USER_PRESET_RED, LED_USER_PRESET_GREEN, LED_USER_PRESET_BLUE, LED_USER_PRESET_WHITE), - (0, 0, 0, 0)) + (255, 255, 255, 255)) ); } @@ -116,7 +116,7 @@ void Marlin_NeoPixel::init() { Marlin_NeoPixel2 neo2; - int8_t Marlin_NeoPixel2::neoindex; + pixel_index_t Marlin_NeoPixel2::neoindex; Adafruit_NeoPixel Marlin_NeoPixel2::adaneo(NEOPIXEL2_PIXELS, NEOPIXEL2_PIN, NEOPIXEL2_TYPE); void Marlin_NeoPixel2::set_color(const uint32_t color) { diff --git a/Marlin/src/feature/leds/neopixel.h b/Marlin/src/feature/leds/neopixel.h index b2c16459f5046..d71aa25770fc7 100644 --- a/Marlin/src/feature/leds/neopixel.h +++ b/Marlin/src/feature/leds/neopixel.h @@ -63,7 +63,13 @@ #endif // ------------------------ -// Function prototypes +// Types +// ------------------------ + +typedef IF<(TERN0(NEOPIXEL_LED, NEOPIXEL_PIXELS > 127)), int16_t, int8_t>::type pixel_index_t; + +// ------------------------ +// Classes // ------------------------ class Marlin_NeoPixel { @@ -74,7 +80,7 @@ class Marlin_NeoPixel { #endif public: - static int8_t neoindex; + static pixel_index_t neoindex; static void init(); static void set_color_startup(const uint32_t c); @@ -82,16 +88,17 @@ class Marlin_NeoPixel { static void set_color(const uint32_t c); #ifdef NEOPIXEL_BKGD_INDEX_FIRST - static void set_background_color(uint8_t r, uint8_t g, uint8_t b, uint8_t w); + static void set_background_color(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t w); + static void set_background_color(const uint8_t (&rgbw)[4]) { set_background_color(rgbw[0], rgbw[1], rgbw[2], rgbw[3]); } static void reset_background_color(); #endif - static inline void begin() { + static void begin() { adaneo1.begin(); TERN_(CONJOINED_NEOPIXEL, adaneo2.begin()); } - static inline void set_pixel_color(const uint16_t n, const uint32_t c) { + static void set_pixel_color(const uint16_t n, const uint32_t c) { #if ENABLED(NEOPIXEL2_INSERIES) if (n >= NEOPIXEL_PIXELS) adaneo2.setPixelColor(n - (NEOPIXEL_PIXELS), c); else adaneo1.setPixelColor(n, c); @@ -101,12 +108,12 @@ class Marlin_NeoPixel { #endif } - static inline void set_brightness(const uint8_t b) { + static void set_brightness(const uint8_t b) { adaneo1.setBrightness(b); TERN_(CONJOINED_NEOPIXEL, adaneo2.setBrightness(b)); } - static inline void show() { + static void show() { // Some platforms cannot maintain PWM output when NeoPixel disables interrupts for long durations. TERN_(HAS_PAUSE_SERVO_OUTPUT, PAUSE_SERVO_OUTPUT()); adaneo1.show(); @@ -122,11 +129,11 @@ class Marlin_NeoPixel { } // Accessors - static inline uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); } + static uint16_t pixels() { return adaneo1.numPixels() * TERN1(NEOPIXEL2_INSERIES, 2); } - static inline uint8_t brightness() { return adaneo1.getBrightness(); } + static uint8_t brightness() { return adaneo1.getBrightness(); } - static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED, uint8_t w)) { + 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)); } }; @@ -150,25 +157,25 @@ extern Marlin_NeoPixel neo; static Adafruit_NeoPixel adaneo; public: - static int8_t neoindex; + static pixel_index_t neoindex; static void init(); static void set_color_startup(const uint32_t c); static void set_color(const uint32_t c); - static inline void begin() { adaneo.begin(); } - static inline void set_pixel_color(const uint16_t n, const uint32_t c) { adaneo.setPixelColor(n, c); } - static inline void set_brightness(const uint8_t b) { adaneo.setBrightness(b); } - static inline void show() { + static void begin() { adaneo.begin(); } + static void set_pixel_color(const uint16_t n, const uint32_t c) { adaneo.setPixelColor(n, c); } + static void set_brightness(const uint8_t b) { adaneo.setBrightness(b); } + static void show() { adaneo.show(); adaneo.setPin(NEOPIXEL2_PIN); } // Accessors - static inline uint16_t pixels() { return adaneo.numPixels();} - static inline uint8_t brightness() { return adaneo.getBrightness(); } - static inline uint32_t Color(uint8_t r, uint8_t g, uint8_t b OPTARG(HAS_WHITE_LED2, uint8_t w)) { + static uint16_t pixels() { return adaneo.numPixels();} + 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)); } }; diff --git a/Marlin/src/feature/leds/printer_event_leds.h b/Marlin/src/feature/leds/printer_event_leds.h index b2201433d8211..2a4342e8f55cb 100644 --- a/Marlin/src/feature/leds/printer_event_leds.h +++ b/Marlin/src/feature/leds/printer_event_leds.h @@ -36,32 +36,32 @@ class PrinterEventLEDs { static bool leds_off_after_print; #endif - static inline void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); } + static void set_done() { TERN(LED_COLOR_PRESETS, leds.set_default(), leds.set_off()); } public: #if HAS_TEMP_HOTEND - static inline LEDColor onHotendHeatingStart() { old_intensity = 0; return leds.get_color(); } + static LEDColor 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 inline LEDColor onBedHeatingStart() { old_intensity = 127; return leds.get_color(); } + static LEDColor 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 inline LEDColor onChamberHeatingStart() { old_intensity = 127; return leds.get_color(); } + static LEDColor 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 inline void onHeatingDone() { leds.set_white(); } - static inline void onPidTuningDone(LEDColor c) { leds.set_color(c); } + static void onHeatingDone() { leds.set_white(); } + static void onPidTuningDone(LEDColor c) { leds.set_color(c); } #endif #if ENABLED(SDSUPPORT) - static inline void onPrintCompleted() { + static void onPrintCompleted() { leds.set_green(); #if HAS_LEDS_OFF_FLAG leds_off_after_print = true; @@ -71,7 +71,7 @@ class PrinterEventLEDs { #endif } - static inline void onResumeAfterWait() { + static void onResumeAfterWait() { #if HAS_LEDS_OFF_FLAG if (leds_off_after_print) { set_done(); diff --git a/Marlin/src/feature/max7219.cpp b/Marlin/src/feature/max7219.cpp index e13c6f5b973c3..ef698f888c430 100644 --- a/Marlin/src/feature/max7219.cpp +++ b/Marlin/src/feature/max7219.cpp @@ -44,7 +44,6 @@ #include "max7219.h" #include "../module/planner.h" -#include "../module/stepper.h" #include "../MarlinCore.h" #include "../HAL/shared/Delay.h" @@ -124,11 +123,10 @@ uint8_t Max7219::suspended; // = 0; #define SIG_DELAY() DELAY_NS(250) #endif -void Max7219::error(const char * const func, const int32_t v1, const int32_t v2/*=-1*/) { +void Max7219::error(FSTR_P const func, const int32_t v1, const int32_t v2/*=-1*/) { #if ENABLED(MAX7219_ERRORS) SERIAL_ECHOPGM("??? Max7219::"); - SERIAL_ECHOPGM_P(func); - SERIAL_CHAR('('); + SERIAL_ECHOF(func, AS_CHAR('(')); SERIAL_ECHO(v1); if (v2 > 0) SERIAL_ECHOPGM(", ", v2); SERIAL_CHAR(')'); @@ -268,24 +266,24 @@ void Max7219::set(const uint8_t line, const uint8_t bits) { // Modify a single LED bit and send the changed line void Max7219::led_set(const uint8_t x, const uint8_t y, const bool on) { - if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_set"), x, y); + if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_set"), x, y); if (BIT_7219(x, y) == on) return; XOR_7219(x, y); refresh_unit_line(LED_IND(x, y)); } void Max7219::led_on(const uint8_t x, const uint8_t y) { - if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_on"), x, y); + if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_on"), x, y); led_set(x, y, true); } void Max7219::led_off(const uint8_t x, const uint8_t y) { - if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_off"), x, y); + if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_off"), x, y); led_set(x, y, false); } void Max7219::led_toggle(const uint8_t x, const uint8_t y) { - if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(PSTR("led_toggle"), x, y); + if (x >= MAX7219_X_LEDS || y >= MAX7219_Y_LEDS) return error(F("led_toggle"), x, y); led_set(x, y, !BIT_7219(x, y)); } @@ -328,13 +326,13 @@ void Max7219::fill() { } void Max7219::clear_row(const uint8_t row) { - if (row >= MAX7219_Y_LEDS) return error(PSTR("clear_row"), row); + if (row >= MAX7219_Y_LEDS) return error(F("clear_row"), row); LOOP_L_N(x, MAX7219_X_LEDS) CLR_7219(x, row); send_row(row); } void Max7219::clear_column(const uint8_t col) { - if (col >= MAX7219_X_LEDS) return error(PSTR("set_column"), col); + if (col >= MAX7219_X_LEDS) return error(F("set_column"), col); LOOP_L_N(y, MAX7219_Y_LEDS) CLR_7219(col, y); send_column(col); } @@ -345,7 +343,7 @@ void Max7219::clear_column(const uint8_t col) { * once with a single call to the function (if rotated 90° or 270°). */ void Max7219::set_row(const uint8_t row, const uint32_t val) { - if (row >= MAX7219_Y_LEDS) return error(PSTR("set_row"), row); + if (row >= MAX7219_Y_LEDS) return error(F("set_row"), row); uint32_t mask = _BV32(MAX7219_X_LEDS - 1); LOOP_L_N(x, MAX7219_X_LEDS) { if (val & mask) SET_7219(x, row); else CLR_7219(x, row); @@ -360,7 +358,7 @@ void Max7219::set_row(const uint8_t row, const uint32_t val) { * once with a single call to the function (if rotated 0° or 180°). */ void Max7219::set_column(const uint8_t col, const uint32_t val) { - if (col >= MAX7219_X_LEDS) return error(PSTR("set_column"), col); + if (col >= MAX7219_X_LEDS) return error(F("set_column"), col); uint32_t mask = _BV32(MAX7219_Y_LEDS - 1); LOOP_L_N(y, MAX7219_Y_LEDS) { if (val & mask) SET_7219(col, y); else CLR_7219(col, y); @@ -371,56 +369,56 @@ void Max7219::set_column(const uint8_t col, const uint32_t val) { void Max7219::set_rows_16bits(const uint8_t y, uint32_t val) { #if MAX7219_X_LEDS == 8 - if (y > MAX7219_Y_LEDS - 2) return error(PSTR("set_rows_16bits"), y, val); + if (y > MAX7219_Y_LEDS - 2) return error(F("set_rows_16bits"), y, val); set_row(y + 1, val); val >>= 8; set_row(y + 0, val); #else // at least 16 bits on each row - if (y > MAX7219_Y_LEDS - 1) return error(PSTR("set_rows_16bits"), y, val); + if (y > MAX7219_Y_LEDS - 1) return error(F("set_rows_16bits"), y, val); set_row(y, val); #endif } void Max7219::set_rows_32bits(const uint8_t y, uint32_t val) { #if MAX7219_X_LEDS == 8 - if (y > MAX7219_Y_LEDS - 4) return error(PSTR("set_rows_32bits"), y, val); + if (y > MAX7219_Y_LEDS - 4) return error(F("set_rows_32bits"), y, val); set_row(y + 3, val); val >>= 8; set_row(y + 2, val); val >>= 8; set_row(y + 1, val); val >>= 8; set_row(y + 0, val); #elif MAX7219_X_LEDS == 16 - if (y > MAX7219_Y_LEDS - 2) return error(PSTR("set_rows_32bits"), y, val); + if (y > MAX7219_Y_LEDS - 2) return error(F("set_rows_32bits"), y, val); set_row(y + 1, val); val >>= 16; set_row(y + 0, val); #else // at least 24 bits on each row. In the 3 matrix case, just display the low 24 bits - if (y > MAX7219_Y_LEDS - 1) return error(PSTR("set_rows_32bits"), y, val); + if (y > MAX7219_Y_LEDS - 1) return error(F("set_rows_32bits"), y, val); set_row(y, val); #endif } void Max7219::set_columns_16bits(const uint8_t x, uint32_t val) { #if MAX7219_Y_LEDS == 8 - if (x > MAX7219_X_LEDS - 2) return error(PSTR("set_columns_16bits"), x, val); + if (x > MAX7219_X_LEDS - 2) return error(F("set_columns_16bits"), x, val); set_column(x + 0, val); val >>= 8; set_column(x + 1, val); #else // at least 16 bits in each column - if (x > MAX7219_X_LEDS - 1) return error(PSTR("set_columns_16bits"), x, val); + if (x > MAX7219_X_LEDS - 1) return error(F("set_columns_16bits"), x, val); set_column(x, val); #endif } void Max7219::set_columns_32bits(const uint8_t x, uint32_t val) { #if MAX7219_Y_LEDS == 8 - if (x > MAX7219_X_LEDS - 4) return error(PSTR("set_rows_32bits"), x, val); + if (x > MAX7219_X_LEDS - 4) return error(F("set_rows_32bits"), x, val); set_column(x + 3, val); val >>= 8; set_column(x + 2, val); val >>= 8; set_column(x + 1, val); val >>= 8; set_column(x + 0, val); #elif MAX7219_Y_LEDS == 16 - if (x > MAX7219_X_LEDS - 2) return error(PSTR("set_rows_32bits"), x, val); + if (x > MAX7219_X_LEDS - 2) return error(F("set_rows_32bits"), x, val); set_column(x + 1, val); val >>= 16; set_column(x + 0, val); #else // at least 24 bits on each row. In the 3 matrix case, just display the low 24 bits - if (x > MAX7219_X_LEDS - 1) return error(PSTR("set_rows_32bits"), x, val); + if (x > MAX7219_X_LEDS - 1) return error(F("set_rows_32bits"), x, val); set_column(x, val); #endif } diff --git a/Marlin/src/feature/max7219.h b/Marlin/src/feature/max7219.h index 3e5b62db2f75b..809bda6d4b351 100644 --- a/Marlin/src/feature/max7219.h +++ b/Marlin/src/feature/max7219.h @@ -42,6 +42,8 @@ * a Max7219_Set_Row(). The opposite is true for rotations of 0 or 180 degrees. */ +#include "../inc/MarlinConfig.h" + #ifndef MAX7219_ROTATE #define MAX7219_ROTATE 0 #endif @@ -86,13 +88,13 @@ class Max7219 { static void send(const uint8_t reg, const uint8_t data); // Refresh all units - static inline void refresh() { for (uint8_t i = 0; i < 8; i++) refresh_line(i); } + static void refresh() { for (uint8_t i = 0; i < 8; i++) refresh_line(i); } // Suspend / resume updates to the LED unit // Use these methods to speed up multiple changes // or to apply updates from interrupt context. - static inline void suspend() { suspended++; } - static inline void resume() { suspended--; suspended |= 0x80; } + static void suspend() { suspended++; } + static void resume() { suspended--; suspended |= 0x80; } // Update a single native line on all units static void refresh_line(const uint8_t line); @@ -140,7 +142,7 @@ class Max7219 { private: static uint8_t suspended; - static void error(const char * const func, const int32_t v1, const int32_t v2=-1); + static void error(FSTR_P const func, const int32_t v1, const int32_t v2=-1); static void noop(); static void set(const uint8_t line, const uint8_t bits); static void send_row(const uint8_t row); diff --git a/Marlin/src/feature/meatpack.cpp b/Marlin/src/feature/meatpack.cpp index 2edcd7478a5cc..07ff41e5be228 100644 --- a/Marlin/src/feature/meatpack.cpp +++ b/Marlin/src/feature/meatpack.cpp @@ -26,7 +26,7 @@ * Algorithm & Implementation: Scott Mudge - mail@scottmudge.com * Date: Dec. 2020 * - * Character Frequencies from ~30 MB of comment-stripped gcode: + * Character Frequencies from ~30 MB of comment-stripped G-code: * '1' -> 4451136 '4' -> 1353273 '\n' -> 1087683 '-' -> 90242 * '0' -> 4253577 '9' -> 1352147 'G' -> 1075806 'Z' -> 34109 * ' ' -> 3053297 '3' -> 1262929 'X' -> 975742 'M' -> 11879 @@ -169,10 +169,9 @@ 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] "); - SERIAL_ECHOPGM(MeatPack_ProtocolVersion " "); + SERIAL_ECHOPGM("[MP] " MeatPack_ProtocolVersion " "); serialprint_onoff(TEST(state, MPConfig_Bit_Active)); - SERIAL_ECHOPGM_P(TEST(state, MPConfig_Bit_NoSpaces) ? PSTR(" NSP\n") : PSTR(" ESP\n")); + SERIAL_ECHOF(TEST(state, MPConfig_Bit_NoSpaces) ? F(" NSP\n") : F(" ESP\n")); } /** diff --git a/Marlin/src/feature/meatpack.h b/Marlin/src/feature/meatpack.h index a56e65b6cc30f..98a535e5923f0 100644 --- a/Marlin/src/feature/meatpack.h +++ b/Marlin/src/feature/meatpack.h @@ -29,7 +29,7 @@ * Specifically optimized for 3D printing G-Code, this is a zero-cost data compression method * which packs ~180-190% more data into the same amount of bytes going to the CNC controller. * As a majority of G-Code can be represented by a restricted alphabet, I performed histogram - * analysis on a wide variety of 3D printing gcode samples, and found ~93% of all gcode could + * analysis on a wide variety of 3D printing G-code samples, and found ~93% of all G-code could * be represented by the same 15-character alphabet. * * This allowed me to design a system of packing 2 8-bit characters into a single byte, assuming @@ -38,7 +38,7 @@ * * Combined with some logic to allow commingling of full-width characters outside of this 15- * character alphabet (at the cost of an extra 8-bits per full-width character), and by stripping - * out unnecessary comments, the end result is gcode which is roughly half the original size. + * out unnecessary comments, the end result is G-code which is roughly half the original size. * * Why did I do this? I noticed micro-stuttering and other data-bottleneck issues while printing * objects with high curvature, especially at high speeds. There is also the issue of the limited diff --git a/Marlin/src/feature/mixing.cpp b/Marlin/src/feature/mixing.cpp index 9ebc90127f43f..b1a069e3205e4 100644 --- a/Marlin/src/feature/mixing.cpp +++ b/Marlin/src/feature/mixing.cpp @@ -63,7 +63,7 @@ void Mixer::normalize(const uint8_t tool_index) { #ifdef MIXER_NORMALIZER_DEBUG SERIAL_ECHOPGM("Mixer: Old relation : [ "); MIXER_STEPPER_LOOP(i) { - SERIAL_ECHO_F(collector[i] / csum, 3); + SERIAL_DECIMAL(collector[i] / csum); SERIAL_CHAR(' '); } SERIAL_ECHOLNPGM("]"); diff --git a/Marlin/src/feature/mixing.h b/Marlin/src/feature/mixing.h index f700c7b65b7d6..85d52d69c8f31 100644 --- a/Marlin/src/feature/mixing.h +++ b/Marlin/src/feature/mixing.h @@ -126,7 +126,7 @@ class Mixer { static mixer_perc_t mix[MIXING_STEPPERS]; // Scratch array for the Mix in proportion to 100 - static inline void copy_mix_to_color(mixer_comp_t (&tcolor)[MIXING_STEPPERS]) { + static void copy_mix_to_color(mixer_comp_t (&tcolor)[MIXING_STEPPERS]) { // Scale each component to the largest one in terms of COLOR_A_MASK // So the largest component will be COLOR_A_MASK and the other will be in proportion to it const float scale = (COLOR_A_MASK) * RECIPROCAL(_MAX( @@ -145,7 +145,7 @@ class Mixer { #endif } - static inline void update_mix_from_vtool(const uint8_t j=selected_vtool) { + static void update_mix_from_vtool(const uint8_t j=selected_vtool) { float ctot = 0; MIXER_STEPPER_LOOP(i) ctot += color[j][i]; //MIXER_STEPPER_LOOP(i) mix[i] = 100.0f * color[j][i] / ctot; @@ -165,7 +165,7 @@ class Mixer { #if HAS_DUAL_MIXING // Update the virtual tool from an edited mix - static inline void update_vtool_from_mix() { + static void update_vtool_from_mix() { copy_mix_to_color(color[selected_vtool]); TERN_(GRADIENT_MIX, refresh_gradient()); // MIXER_STEPPER_LOOP(i) collector[i] = mix[i]; @@ -182,7 +182,7 @@ class Mixer { // 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_planner_z(); - static inline void gradient_control(const_float_t z) { + static void gradient_control(const_float_t z) { if (gradient.enabled) { if (z >= gradient.end_z) T(gradient.end_vtool); @@ -191,7 +191,7 @@ class Mixer { } } - static inline void update_mix_from_gradient() { + static void update_mix_from_gradient() { float ctot = 0; MIXER_STEPPER_LOOP(i) ctot += gradient.color[i]; MIXER_STEPPER_LOOP(i) mix[i] = (mixer_perc_t)CEIL(100.0f * gradient.color[i] / ctot); diff --git a/Marlin/src/feature/mmu/mmu.cpp b/Marlin/src/feature/mmu/mmu.cpp index 6340f3c3017bd..58c49ed224541 100644 --- a/Marlin/src/feature/mmu/mmu.cpp +++ b/Marlin/src/feature/mmu/mmu.cpp @@ -24,9 +24,9 @@ #if HAS_PRUSA_MMU1 -#include "../MarlinCore.h" -#include "../module/planner.h" -#include "../module/stepper.h" +#include "../../MarlinCore.h" +#include "../../module/planner.h" +#include "../../module/stepper.h" void mmu_init() { SET_OUTPUT(E_MUX0_PIN); diff --git a/Marlin/src/feature/mmu/mmu2-serial-protocol.md b/Marlin/src/feature/mmu/mmu2-serial-protocol.md index 7ff0901742af8..93135e406f366 100644 --- a/Marlin/src/feature/mmu/mmu2-serial-protocol.md +++ b/Marlin/src/feature/mmu/mmu2-serial-protocol.md @@ -51,7 +51,7 @@ When done, the MMU sends - MMU => 'ok\n' -We don't wait for a response here but immediately continue with the next gcode which should +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. diff --git a/Marlin/src/feature/mmu/mmu2.cpp b/Marlin/src/feature/mmu/mmu2.cpp index 3727c8c86dde4..a4718b53d9d80 100644 --- a/Marlin/src/feature/mmu/mmu2.cpp +++ b/Marlin/src/feature/mmu/mmu2.cpp @@ -54,7 +54,7 @@ MMU2 mmu2; #define MMU_CMD_TIMEOUT 45000UL // 45s timeout for mmu commands (except P0) #define MMU_P0_TIMEOUT 3000UL // Timeout for P0 command: 3seconds -#define MMU2_COMMAND(S) tx_str_P(PSTR(S "\n")) +#define MMU2_COMMAND(S) tx_str(F(S "\n")) #if ENABLED(MMU_EXTRUDER_SENSOR) uint8_t mmu_idl_sens = 0; @@ -143,6 +143,11 @@ uint8_t MMU2::get_current_tool() { #define FILAMENT_PRESENT() (READ(FIL_RUNOUT1_PIN) != FIL_RUNOUT1_STATE) #endif +void mmu2_attn_buzz(const bool two=false) { + BUZZ(200, 404); + if (two) { BUZZ(10, 0); BUZZ(200, 404); } +} + void MMU2::mmu_loop() { switch (state) { @@ -229,17 +234,17 @@ void MMU2::mmu_loop() { if (cmd) { if (WITHIN(cmd, MMU_CMD_T0, MMU_CMD_T0 + EXTRUDERS - 1)) { // tool change - int filament = cmd - MMU_CMD_T0; + const int filament = cmd - MMU_CMD_T0; DEBUG_ECHOLNPGM("MMU <= T", filament); - tx_printf_P(PSTR("T%d\n"), filament); + tx_printf(F("T%d\n"), filament); TERN_(MMU_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)) { // load - int filament = cmd - MMU_CMD_L0; + const int filament = cmd - MMU_CMD_L0; DEBUG_ECHOLNPGM("MMU <= L", filament); - tx_printf_P(PSTR("L%d\n"), filament); + tx_printf(F("L%d\n"), filament); state = 3; // wait for response } else if (cmd == MMU_CMD_C0) { @@ -257,9 +262,9 @@ void MMU2::mmu_loop() { } else if (WITHIN(cmd, MMU_CMD_E0, MMU_CMD_E0 + EXTRUDERS - 1)) { // eject filament - int filament = cmd - MMU_CMD_E0; + const int filament = cmd - MMU_CMD_E0; DEBUG_ECHOLNPGM("MMU <= E", filament); - tx_printf_P(PSTR("E%d\n"), filament); + tx_printf(F("E%d\n"), filament); state = 3; // wait for response } else if (cmd == MMU_CMD_R0) { @@ -270,9 +275,9 @@ void MMU2::mmu_loop() { } else if (WITHIN(cmd, MMU_CMD_F0, MMU_CMD_F0 + EXTRUDERS - 1)) { // filament type - int filament = cmd - MMU_CMD_F0; + const int filament = cmd - MMU_CMD_F0; DEBUG_ECHOLNPGM("MMU <= F", filament, " ", cmd_arg); - tx_printf_P(PSTR("F%d %d\n"), filament, cmd_arg); + tx_printf(F("F%d %d\n"), filament, cmd_arg); state = 3; // wait for response } @@ -356,13 +361,15 @@ void MMU2::mmu_loop() { */ bool MMU2::rx_start() { // check for start message - return rx_str_P(PSTR("start\n")); + return rx_str(F("start\n")); } /** * Check if the data received ends with the given string. */ -bool MMU2::rx_str_P(const char *str) { +bool MMU2::rx_str(FSTR_P fstr) { + PGM_P pstr = FTOP(fstr); + uint8_t i = strlen(rx_buffer); while (MMU2_SERIAL.available()) { @@ -375,14 +382,14 @@ bool MMU2::rx_str_P(const char *str) { } rx_buffer[i] = '\0'; - uint8_t len = strlen_P(str); + uint8_t len = strlen_P(pstr); if (i < len) return false; - str += len; + pstr += len; while (len--) { - char c0 = pgm_read_byte(str--), c1 = rx_buffer[i--]; + char c0 = pgm_read_byte(pstr--), c1 = rx_buffer[i--]; if (c0 == c1) continue; if (c0 == '\r' && c1 == '\n') continue; // match cr as lf if (c0 == '\n' && c1 == '\r') continue; // match lf as cr @@ -394,19 +401,19 @@ bool MMU2::rx_str_P(const char *str) { /** * Transfer data to MMU, no argument */ -void MMU2::tx_str_P(const char *str) { +void MMU2::tx_str(FSTR_P fstr) { clear_rx_buffer(); - uint8_t len = strlen_P(str); - LOOP_L_N(i, len) MMU2_SERIAL.write(pgm_read_byte(str++)); + PGM_P pstr = FTOP(fstr); + while (const char c = pgm_read_byte(pstr)) { MMU2_SERIAL.write(c); pstr++; } prev_request = millis(); } /** * Transfer data to MMU, single argument */ -void MMU2::tx_printf_P(const char *format, int argument = -1) { +void MMU2::tx_printf(FSTR_P format, int argument = -1) { clear_rx_buffer(); - uint8_t len = sprintf_P(tx_buffer, format, argument); + const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument); LOOP_L_N(i, len) MMU2_SERIAL.write(tx_buffer[i]); prev_request = millis(); } @@ -414,9 +421,9 @@ void MMU2::tx_printf_P(const char *format, int argument = -1) { /** * Transfer data to MMU, two arguments */ -void MMU2::tx_printf_P(const char *format, int argument1, int argument2) { +void MMU2::tx_printf(FSTR_P format, int argument1, int argument2) { clear_rx_buffer(); - uint8_t len = sprintf_P(tx_buffer, format, argument1, argument2); + const uint8_t len = sprintf_P(tx_buffer, FTOP(format), argument1, argument2); LOOP_L_N(i, len) MMU2_SERIAL.write(tx_buffer[i]); prev_request = millis(); } @@ -433,7 +440,7 @@ void MMU2::clear_rx_buffer() { * Check if we received 'ok' from MMU */ bool MMU2::rx_ok() { - if (rx_str_P(PSTR("ok\n"))) { + if (rx_str(F("ok\n"))) { prev_P0_request = millis(); return true; } @@ -446,12 +453,12 @@ bool MMU2::rx_ok() { void MMU2::check_version() { if (buildnr < MMU_REQUIRED_FW_BUILDNR) { SERIAL_ERROR_MSG("Invalid MMU2 firmware. Version >= " STRINGIFY(MMU_REQUIRED_FW_BUILDNR) " required."); - kill(GET_TEXT(MSG_KILL_MMU2_FIRMWARE)); + kill(GET_TEXT_F(MSG_KILL_MMU2_FIRMWARE)); } } static void mmu2_not_responding() { - LCD_MESSAGEPGM(MSG_MMU2_NOT_RESPONDING); + LCD_MESSAGE(MSG_MMU2_NOT_RESPONDING); BUZZ(100, 659); BUZZ(200, 698); BUZZ(100, 659); @@ -487,7 +494,7 @@ static void mmu2_not_responding() { if (index != extruder) { stepper.disable_extruder(); - ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); + ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); command(MMU_CMD_T0 + index); manage_response(true, true); @@ -523,7 +530,7 @@ static void mmu2_not_responding() { while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_filament_to_nozzle(index); #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -542,7 +549,7 @@ static void mmu2_not_responding() { active_extruder = 0; } #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -573,7 +580,7 @@ static void mmu2_not_responding() { command(MMU_CMD_U0); manage_response(true, true); } - ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); + ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); mmu_loading_flag = true; command(MMU_CMD_T0 + index); manage_response(true, true); @@ -611,7 +618,7 @@ static void mmu2_not_responding() { while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_filament_to_nozzle(index); #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -631,7 +638,7 @@ static void mmu2_not_responding() { extruder = index; active_extruder = 0; #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -671,7 +678,7 @@ static void mmu2_not_responding() { if (index != extruder) { stepper.disable_extruder(); - ui.status_printf_P(0, GET_TEXT(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); + ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(index + 1)); command(MMU_CMD_T0 + index); manage_response(true, true); command(MMU_CMD_C0); @@ -705,7 +712,7 @@ static void mmu2_not_responding() { while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(100); load_filament_to_nozzle(index); #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -724,7 +731,7 @@ static void mmu2_not_responding() { extruder = index; active_extruder = 0; #else - BUZZ(400, 40); + ERR_BUZZ(); #endif } break; @@ -808,26 +815,27 @@ void MMU2::manage_response(const bool move_axes, const bool turn_off_nozzle) { if (turn_off_nozzle && resume_hotend_temp) { thermalManager.setTargetHotend(resume_hotend_temp, active_extruder); - LCD_MESSAGEPGM(MSG_HEATING); - BUZZ(200, 40); + LCD_MESSAGE(MSG_HEATING); + ERR_BUZZ(); while (!thermalManager.wait_for_hotend(active_extruder, false)) safe_delay(1000); } - if (move_axes && all_axes_homed()) { - LCD_MESSAGEPGM(MSG_MMU2_RESUMING); - BUZZ(198, 404); BUZZ(4, 0); BUZZ(198, 404); + LCD_MESSAGE(MSG_MMU2_RESUMING); + mmu2_attn_buzz(true); + + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + if (move_axes && all_axes_homed()) { // Move XY to starting position, then Z do_blocking_move_to_xy(resume_position, feedRate_t(NOZZLE_PARK_XY_FEEDRATE)); // Move Z_AXIS to saved position do_blocking_move_to_z(resume_position.z, feedRate_t(NOZZLE_PARK_Z_FEEDRATE)); } - else { - BUZZ(198, 404); BUZZ(4, 0); BUZZ(198, 404); - LCD_MESSAGEPGM(MSG_MMU2_RESUMING); - } + + #pragma GCC diagnostic pop } } } @@ -842,7 +850,7 @@ void MMU2::set_filament_type(const uint8_t index, const uint8_t filamentType) { } void MMU2::filament_runout() { - queue.inject_P(PSTR(MMU2_FILAMENT_RUNOUT_SCRIPT)); + queue.inject(F(MMU2_FILAMENT_RUNOUT_SCRIPT)); planner.synchronize(); } @@ -853,7 +861,7 @@ void MMU2::filament_runout() { if (cmd == MMU_CMD_NONE && last_cmd == MMU_CMD_C0) { if (present && !mmu2s_triggered) { DEBUG_ECHOLNPGM("MMU <= 'A'"); - tx_str_P(PSTR("A\n")); + tx_str(F("A\n")); } // Slowly spin the extruder during C0 else { @@ -896,7 +904,7 @@ void MMU2::load_filament(const uint8_t index) { command(MMU_CMD_L0 + index); manage_response(false, false); - BUZZ(200, 404); + mmu2_attn_buzz(); } /** @@ -907,8 +915,8 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) { if (!_enabled) return false; if (thermalManager.tooColdToExtrude(active_extruder)) { - BUZZ(200, 404); - LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD); + mmu2_attn_buzz(); + LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD); return false; } @@ -922,7 +930,7 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) { extruder = index; active_extruder = 0; load_to_nozzle(); - BUZZ(200, 404); + mmu2_attn_buzz(); } return success; } @@ -931,7 +939,7 @@ bool MMU2::load_filament_to_nozzle(const uint8_t index) { * Load filament to nozzle of multimaterial printer * * This function is used only after T? (user select filament) and M600 (change filament). - * It is not used after T0 .. T4 command (select filament), in such case, gcode is responsible for loading + * It is not used after T0 .. T4 command (select filament), in such case, G-code is responsible for loading * filament to nozzle. */ void MMU2::load_to_nozzle() { @@ -943,12 +951,12 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) { if (!_enabled) return false; if (thermalManager.tooColdToExtrude(active_extruder)) { - BUZZ(200, 404); - LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD); + mmu2_attn_buzz(); + LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD); return false; } - LCD_MESSAGEPGM(MSG_MMU2_EJECTING_FILAMENT); + LCD_MESSAGE(MSG_MMU2_EJECTING_FILAMENT); stepper.enable_extruder(); current_position.e -= MMU2_FILAMENTCHANGE_EJECT_FEED; @@ -958,13 +966,12 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) { manage_response(false, false); if (recover) { - LCD_MESSAGEPGM(MSG_MMU2_EJECT_RECOVER); - BUZZ(200, 404); - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("MMU2 Eject Recover"), CONTINUE_STR)); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("MMU2 Eject Recover"))); + LCD_MESSAGE(MSG_MMU2_EJECT_RECOVER); + mmu2_attn_buzz(); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, F("MMU2 Eject Recover"), FPSTR(CONTINUE_STR))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(F("MMU2 Eject Recover"))); TERN_(HAS_RESUME_CONTINUE, wait_for_user_response()); - BUZZ(200, 404); - BUZZ(200, 404); + mmu2_attn_buzz(true); command(MMU_CMD_R0); manage_response(false, false); @@ -977,7 +984,7 @@ bool MMU2::eject_filament(const uint8_t index, const bool recover) { set_runout_valid(false); - BUZZ(200, 404); + mmu2_attn_buzz(); stepper.disable_extruder(); @@ -992,8 +999,8 @@ bool MMU2::unload() { if (!_enabled) return false; if (thermalManager.tooColdToExtrude(active_extruder)) { - BUZZ(200, 404); - LCD_ALERTMESSAGEPGM(MSG_HOTEND_TOO_COLD); + mmu2_attn_buzz(); + LCD_ALERTMESSAGE(MSG_HOTEND_TOO_COLD); return false; } @@ -1003,7 +1010,7 @@ bool MMU2::unload() { command(MMU_CMD_U0); manage_response(false, true); - BUZZ(200, 404); + mmu2_attn_buzz(); // no active tool extruder = MMU2_NO_TOOL; @@ -1024,8 +1031,7 @@ void MMU2::execute_extruder_sequence(const E_Step * sequence, int steps) { const float es = pgm_read_float(&(step->extrude)); const feedRate_t fr_mm_m = pgm_read_float(&(step->feedRate)); - DEBUG_ECHO_START(); - DEBUG_ECHOLNPGM("E step ", es, "/", fr_mm_m); + DEBUG_ECHO_MSG("E step ", es, "/", fr_mm_m); current_position.e += es; line_to_current_position(MMM_TO_MMS(fr_mm_m)); diff --git a/Marlin/src/feature/mmu/mmu2.h b/Marlin/src/feature/mmu/mmu2.h index 95338a518410d..7d3d9ec4df38a 100644 --- a/Marlin/src/feature/mmu/mmu2.h +++ b/Marlin/src/feature/mmu/mmu2.h @@ -43,7 +43,7 @@ class MMU2 { static void init(); static void reset(); - static inline bool enabled() { return _enabled; } + static bool enabled() { return _enabled; } static void mmu_loop(); static void tool_change(const uint8_t index); static void tool_change(const char *special); @@ -57,10 +57,10 @@ class MMU2 { static bool eject_filament(const uint8_t index, const bool recover); private: - static bool rx_str_P(const char *str); - static void tx_str_P(const char *str); - static void tx_printf_P(const char *format, const int argument); - static void tx_printf_P(const char *format, const int argument1, const int argument2); + static bool rx_str(FSTR_P fstr); + static void tx_str(FSTR_P fstr); + static void tx_printf(FSTR_P ffmt, const int argument); + static void tx_printf(FSTR_P ffmt, const int argument1, const int argument2); static void clear_rx_buffer(); static bool rx_ok(); @@ -99,7 +99,7 @@ class MMU2 { static millis_t prev_request, prev_P0_request; static char rx_buffer[MMU_RX_SIZE], tx_buffer[MMU_TX_SIZE]; - static inline void set_runout_valid(const bool valid) { + static void set_runout_valid(const bool valid) { finda_runout_valid = valid; #if HAS_FILAMENT_SENSOR if (valid) runout.reset(); diff --git a/Marlin/src/feature/password/password.cpp b/Marlin/src/feature/password/password.cpp index 4e841c243cf03..1d376cc586cd2 100644 --- a/Marlin/src/feature/password/password.cpp +++ b/Marlin/src/feature/password/password.cpp @@ -40,7 +40,7 @@ uint32_t Password::value, Password::value_entry; // void Password::lock_machine() { is_locked = true; - TERN_(HAS_LCD_MENU, authenticate_user(ui.status_screen, screen_password_entry)); + TERN_(HAS_MARLINUI_MENU, authenticate_user(ui.status_screen, screen_password_entry)); } // @@ -55,7 +55,7 @@ void Password::authentication_check() { is_locked = true; SERIAL_ECHOLNPGM(STR_WRONG_PASSWORD); } - TERN_(HAS_LCD_MENU, authentication_done()); + TERN_(HAS_MARLINUI_MENU, authentication_done()); } #endif // PASSWORD_FEATURE diff --git a/Marlin/src/feature/password/password.h b/Marlin/src/feature/password/password.h index 829d222e20b92..208765b212259 100644 --- a/Marlin/src/feature/password/password.h +++ b/Marlin/src/feature/password/password.h @@ -33,7 +33,7 @@ class Password { static void lock_machine(); static void authentication_check(); - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU static void access_menu_password(); static void authentication_done(); static void media_gatekeeper(); diff --git a/Marlin/src/feature/pause.cpp b/Marlin/src/feature/pause.cpp index d54326116ef8b..1c2ea59d4daf4 100644 --- a/Marlin/src/feature/pause.cpp +++ b/Marlin/src/feature/pause.cpp @@ -35,10 +35,17 @@ #include "../gcode/gcode.h" #include "../module/motion.h" #include "../module/planner.h" -#include "../module/stepper.h" #include "../module/printcounter.h" #include "../module/temperature.h" +#if HAS_EXTRUDERS + #include "../module/stepper.h" +#endif + +#if ENABLED(AUTO_BED_LEVELING_UBL) + #include "bedlevel/bedlevel.h" +#endif + #if ENABLED(FWRETRACT) #include "fwretract.h" #endif @@ -53,13 +60,13 @@ #if ENABLED(EXTENSIBLE_UI) #include "../lcd/extui/ui_api.h" -#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../lcd/e3v2/proui/dwin.h" #endif #include "../lcd/marlinui.h" -#if HAS_BUZZER +#if HAS_SOUND #include "../libs/buzzer.h" #endif @@ -94,10 +101,10 @@ fil_change_settings_t fc_settings[EXTRUDERS]; #define _PMSG(L) L##_LCD #endif -#if HAS_BUZZER +#if HAS_SOUND static void impatient_beep(const int8_t max_beep_count, const bool restart=false) { - if (TERN0(HAS_LCD_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return; + if (TERN0(HAS_MARLINUI_MENU, pause_mode == PAUSE_MODE_PAUSE_PRINT)) return; static millis_t next_buzz = 0; static int8_t runout_beep = 0; @@ -194,11 +201,11 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load KEEPALIVE_STATE(PAUSED_FOR_USER); wait_for_user = true; // LCD click or M108 will clear this - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("Load Filament"))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(F("Load Filament"))); #if ENABLED(HOST_PROMPT_SUPPORT) const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, active_extruder); - host_prompt_do(PROMPT_USER_CONTINUE, PSTR("Load Filament T"), tool, CONTINUE_STR); + hostui.prompt_do(PROMPT_USER_CONTINUE, F("Load Filament T"), tool, FPSTR(CONTINUE_STR)); #endif while (wait_for_user) { @@ -252,9 +259,8 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load if (show_lcd) ui.pause_show_message(PAUSE_MESSAGE_PURGE); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_FILAMENT_CHANGE_PURGE))); - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR)); - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_Popup_Confirm(ICON_BLTouch, GET_TEXT(MSG_FILAMENT_CHANGE_PURGE), CONTINUE_STR)); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_FILAMENT_CHANGE_PURGE), FPSTR(CONTINUE_STR))); wait_for_user = true; // A click or M108 breaks the purge_length loop for (float purge_count = purge_length; purge_count > 0 && wait_for_user; --purge_count) unscaled_e_move(1, ADVANCED_PAUSE_PURGE_FEEDRATE); @@ -271,14 +277,14 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load unscaled_e_move(purge_length, ADVANCED_PAUSE_PURGE_FEEDRATE); } - TERN_(HOST_PROMPT_SUPPORT, filament_load_host_prompt()); // Initiate another host prompt. + TERN_(HOST_PROMPT_SUPPORT, hostui.filament_load_prompt()); // Initiate another host prompt. #if M600_PURGE_MORE_RESUMABLE if (show_lcd) { // Show "Purge More" / "Resume" menu and wait for reply KEEPALIVE_STATE(PAUSED_FOR_USER); wait_for_user = false; - #if EITHER(HAS_LCD_MENU, DWIN_CREALITY_LCD_ENHANCED) + #if EITHER(HAS_MARLINUI_MENU, DWIN_LCD_PROUI) ui.pause_show_message(PAUSE_MESSAGE_OPTION); // Also sets PAUSE_RESPONSE_WAIT_FOR #else pause_menu_response = PAUSE_RESPONSE_WAIT_FOR; @@ -291,7 +297,7 @@ bool load_filament(const_float_t slow_load_length/*=0*/, const_float_t fast_load } while (TERN0(M600_PURGE_MORE_RESUMABLE, pause_menu_response == PAUSE_RESPONSE_EXTRUDE_MORE)); #endif - TERN_(HOST_PROMPT_SUPPORT, host_action_prompt_end()); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_end()); return true; } @@ -397,13 +403,14 @@ bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool #if ENABLED(HOST_ACTION_COMMANDS) #ifdef ACTION_ON_PAUSED - host_action_paused(); + hostui.paused(); #elif defined(ACTION_ON_PAUSE) - host_action_pause(); + hostui.pause(); #endif #endif - TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Pause"), DISMISS_STR)); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_open(PROMPT_INFO, F("Pause"), FPSTR(DISMISS_STR))); + TERN_(DWIN_LCD_PROUI, DWIN_Print_Pause()); // Indicate that the printer is paused ++did_pause_print; @@ -441,7 +448,15 @@ bool pause_print(const_float_t retract, const xyz_pos_t &park_point, const bool // Initial retract before move to filament change position if (retract && thermalManager.hotEnoughToExtrude(active_extruder)) { DEBUG_ECHOLNPGM("... retract:", retract); + + #if ENABLED(AUTO_BED_LEVELING_UBL) + const bool leveling_was_enabled = planner.leveling_active; // save leveling state + set_bed_leveling_enabled(false); // turn off leveling + #endif + unscaled_e_move(retract, PAUSE_PARK_RETRACT_FEEDRATE); + + TERN_(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(leveling_was_enabled)); // restore leveling } // If axes don't need to home then the nozzle can park @@ -486,7 +501,7 @@ void show_continue_prompt(const bool is_reload) { ui.pause_show_message(is_reload ? PAUSE_MESSAGE_INSERT : PAUSE_MESSAGE_WAITING); SERIAL_ECHO_START(); - SERIAL_ECHOPGM_P(is_reload ? PSTR(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : PSTR(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n")); + SERIAL_ECHOF(is_reload ? F(_PMSG(STR_FILAMENT_CHANGE_INSERT) "\n") : F(_PMSG(STR_FILAMENT_CHANGE_WAIT) "\n")); } void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep_count/*=0*/ DXC_ARGS) { @@ -512,8 +527,8 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep // Wait for filament insert by user and press button KEEPALIVE_STATE(PAUSED_FOR_USER); - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_NOZZLE_PARKED), CONTINUE_STR)); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_NOZZLE_PARKED))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_NOZZLE_PARKED), FPSTR(CONTINUE_STR))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_NOZZLE_PARKED))); wait_for_user = true; // LCD click or M108 will clear this while (wait_for_user) { impatient_beep(max_beep_count); @@ -528,17 +543,17 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep ui.pause_show_message(PAUSE_MESSAGE_HEAT); SERIAL_ECHO_MSG(_PMSG(STR_FILAMENT_CHANGE_HEAT)); - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_HEATER_TIMEOUT), GET_TEXT(MSG_REHEAT))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_HEATER_TIMEOUT), GET_TEXT_F(MSG_REHEAT))); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_HEATER_TIMEOUT))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_HEATER_TIMEOUT))); TERN_(HAS_RESUME_CONTINUE, wait_for_user_response(0, true)); // Wait for LCD click or M108 - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_INFO, GET_TEXT(MSG_REHEATING))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_INFO, GET_TEXT_F(MSG_REHEATING))); - TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged_P(GET_TEXT(MSG_REHEATING))); + TERN_(EXTENSIBLE_UI, ExtUI::onStatusChanged(GET_TEXT_F(MSG_REHEATING))); - TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATING))); + TERN_(DWIN_LCD_PROUI, LCD_MESSAGE(MSG_REHEATING)); // Re-enable the heaters if they timed out HOTEND_LOOP() thermalManager.reset_hotend_idle_timer(e); @@ -554,9 +569,9 @@ void wait_for_confirmation(const bool is_reload/*=false*/, const int8_t max_beep HOTEND_LOOP() thermalManager.heater_idle[e].start(nozzle_timeout); - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, GET_TEXT(MSG_REHEATDONE), CONTINUE_STR)); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(GET_TEXT(MSG_REHEATDONE))); - TERN_(DWIN_CREALITY_LCD_ENHANCED, ui.set_status_P(GET_TEXT(MSG_REHEATDONE))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, GET_TEXT_F(MSG_REHEATDONE), FPSTR(CONTINUE_STR))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(GET_TEXT_F(MSG_REHEATDONE))); + TERN_(DWIN_LCD_PROUI, LCD_MESSAGE(MSG_REHEATDONE)); IF_DISABLED(PAUSE_REHEAT_FAST_RESUME, wait_for_user = true); @@ -641,9 +656,16 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ prepare_internal_move_to_destination(NOZZLE_PARK_Z_FEEDRATE); } + #if ENABLED(AUTO_BED_LEVELING_UBL) + const bool leveling_was_enabled = planner.leveling_active; // save leveling state + set_bed_leveling_enabled(false); // turn off leveling + #endif + // Unretract unscaled_e_move(PAUSE_PARK_RETRACT_LENGTH, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE)); + TERN_(AUTO_BED_LEVELING_UBL, set_bed_leveling_enabled(leveling_was_enabled)); // restore leveling + // Intelligent resuming #if ENABLED(FWRETRACT) // If retracted before goto pause @@ -653,8 +675,9 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ // If resume_position is negative if (resume_position.e < 0) unscaled_e_move(resume_position.e, feedRate_t(PAUSE_PARK_RETRACT_FEEDRATE)); - #if ADVANCED_PAUSE_RESUME_PRIME != 0 - unscaled_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE)); + #ifdef ADVANCED_PAUSE_RESUME_PRIME + if (ADVANCED_PAUSE_RESUME_PRIME != 0) + unscaled_e_move(ADVANCED_PAUSE_RESUME_PRIME, feedRate_t(ADVANCED_PAUSE_PURGE_FEEDRATE)); #endif // Now all extrusion positions are resumed and ready to be confirmed @@ -664,14 +687,14 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ ui.pause_show_message(PAUSE_MESSAGE_STATUS); #ifdef ACTION_ON_RESUMED - host_action_resumed(); + hostui.resumed(); #elif defined(ACTION_ON_RESUME) - host_action_resume(); + hostui.resume(); #endif --did_pause_print; - TERN_(HOST_PROMPT_SUPPORT, host_prompt_open(PROMPT_INFO, PSTR("Resuming"), DISMISS_STR)); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_open(PROMPT_INFO, F("Resuming"), FPSTR(DISMISS_STR))); // Resume the print job timer if it was running if (print_job_timer.isPaused()) print_job_timer.start(); @@ -691,9 +714,13 @@ void resume_print(const_float_t slow_load_length/*=0*/, const_float_t fast_load_ TERN_(HAS_FILAMENT_SENSOR, runout.reset()); - TERN_(HAS_STATUS_MESSAGE, ui.reset_status()); - TERN_(HAS_LCD_MENU, ui.return_to_status()); - TERN_(DWIN_CREALITY_LCD_ENHANCED, HMI_ReturnScreen()); + #if ENABLED(DWIN_LCD_PROUI) + DWIN_Print_Resume(); + HMI_ReturnScreen(); + #else + ui.reset_status(); + ui.return_to_status(); + #endif } #endif // ADVANCED_PAUSE_FEATURE diff --git a/Marlin/src/feature/pause.h b/Marlin/src/feature/pause.h index d2c45e44a5df1..134b1d1b3294d 100644 --- a/Marlin/src/feature/pause.h +++ b/Marlin/src/feature/pause.h @@ -73,17 +73,10 @@ extern fil_change_settings_t fc_settings[EXTRUDERS]; extern uint8_t did_pause_print; -#if ENABLED(DUAL_X_CARRIAGE) - #define DXC_PARAMS , const int8_t DXC_ext=-1 - #define DXC_ARGS , const int8_t DXC_ext - #define DXC_PASS , DXC_ext - #define DXC_SAY , " dxc:", int(DXC_ext) -#else - #define DXC_PARAMS - #define DXC_ARGS - #define DXC_PASS - #define DXC_SAY -#endif +#define DXC_PARAMS OPTARG(DUAL_X_CARRIAGE, const int8_t DXC_ext=-1) +#define DXC_ARGS OPTARG(DUAL_X_CARRIAGE, const int8_t DXC_ext) +#define DXC_PASS OPTARG(DUAL_X_CARRIAGE, DXC_ext) +#define DXC_SAY OPTARG(DUAL_X_CARRIAGE, " dxc:", int(DXC_ext)) // Pause the print. If unload_length is set, do a Filament Unload bool pause_print( diff --git a/Marlin/src/feature/power.cpp b/Marlin/src/feature/power.cpp index 8116bd2e446d6..8a16628bac459 100644 --- a/Marlin/src/feature/power.cpp +++ b/Marlin/src/feature/power.cpp @@ -24,10 +24,14 @@ * power.cpp - power control */ -#include "../inc/MarlinConfig.h" +#include "../inc/MarlinConfigPre.h" + +#if EITHER(PSU_CONTROL, AUTO_POWER_CONTROL) #include "power.h" -#include "../module/stepper.h" +#include "../module/planner.h" +#include "../module/stepper/indirection.h" // for restore_stepper_drivers +#include "../module/temperature.h" #include "../MarlinCore.h" #if ENABLED(PS_OFF_SOUND) @@ -38,12 +42,11 @@ #include "../gcode/gcode.h" #endif -#if EITHER(PSU_CONTROL, AUTO_POWER_CONTROL) - Power powerManager; bool Power::psu_on; #if ENABLED(AUTO_POWER_CONTROL) + #include "../module/stepper.h" #include "../module/temperature.h" #if BOTH(USE_CONTROLLER_FAN, AUTO_POWER_CONTROLLERFAN) @@ -75,6 +78,10 @@ void Power::power_on() { if (psu_on) return; + #if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN) + cancelAutoPowerOff(); + #endif + OUT_WRITE(PS_ON_PIN, PSU_ACTIVE_STATE); psu_on = true; safe_delay(PSU_POWERUP_DELAY); @@ -82,20 +89,23 @@ void Power::power_on() { TERN_(HAS_TRINAMIC_CONFIG, safe_delay(PSU_POWERUP_DELAY)); #ifdef PSU_POWERUP_GCODE - GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWERUP_GCODE)); + gcode.process_subcommands_now(F(PSU_POWERUP_GCODE)); #endif } /** * Power off if the power is currently on. * Processes any PSU_POWEROFF_GCODE and makes a PS_OFF_SOUND if enabled. - * */ void Power::power_off() { + SERIAL_ECHOLNPGM(STR_POWEROFF); + + TERN_(HAS_SUICIDE, suicide()); + if (!psu_on) return; #ifdef PSU_POWEROFF_GCODE - GcodeSuite::process_subcommands_now_P(PSTR(PSU_POWEROFF_GCODE)); + gcode.process_subcommands_now(F(PSU_POWEROFF_GCODE)); #endif #if ENABLED(PS_OFF_SOUND) @@ -104,8 +114,57 @@ void Power::power_off() { OUT_WRITE(PS_ON_PIN, !PSU_ACTIVE_STATE); psu_on = false; + + #if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN) + cancelAutoPowerOff(); + #endif } +#if EITHER(AUTO_POWER_CONTROL, POWER_OFF_WAIT_FOR_COOLDOWN) + + bool Power::is_cooling_needed() { + #if HAS_HOTEND && AUTO_POWER_E_TEMP + HOTEND_LOOP() if (thermalManager.degHotend(e) >= (AUTO_POWER_E_TEMP)) return true; + #endif + + #if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP + if (thermalManager.degChamber() >= (AUTO_POWER_CHAMBER_TEMP)) return true; + #endif + + #if HAS_COOLER && AUTO_POWER_COOLER_TEMP + if (thermalManager.degCooler() >= (AUTO_POWER_COOLER_TEMP)) return true; + #endif + + return false; + } + +#endif + +#if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN) + + #if ENABLED(POWER_OFF_TIMER) + millis_t Power::power_off_time = 0; + void Power::setPowerOffTimer(const millis_t delay_ms) { power_off_time = millis() + delay_ms; } + #endif + + #if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN) + bool Power::power_off_on_cooldown = false; + void Power::setPowerOffOnCooldown(const bool ena) { power_off_on_cooldown = ena; } + #endif + + void Power::cancelAutoPowerOff() { + TERN_(POWER_OFF_TIMER, power_off_time = 0); + TERN_(POWER_OFF_WAIT_FOR_COOLDOWN, power_off_on_cooldown = false); + } + + void Power::checkAutoPowerOff() { + if (TERN1(POWER_OFF_TIMER, !power_off_time) && TERN1(POWER_OFF_WAIT_FOR_COOLDOWN, !power_off_on_cooldown)) return; + if (TERN0(POWER_OFF_WAIT_FOR_COOLDOWN, power_off_on_cooldown && is_cooling_needed())) return; + if (TERN0(POWER_OFF_TIMER, power_off_time && PENDING(millis(), power_off_time))) return; + power_off(); + } + +#endif // POWER_OFF_TIMER || POWER_OFF_WAIT_FOR_COOLDOWN #if ENABLED(AUTO_POWER_CONTROL) @@ -149,19 +208,7 @@ void Power::power_off() { if (TERN0(HAS_HEATED_BED, thermalManager.degTargetBed() > 0 || thermalManager.temp_bed.soft_pwm_amount > 0)) return true; - #if HAS_HOTEND && AUTO_POWER_E_TEMP - HOTEND_LOOP() if (thermalManager.degHotend(e) >= (AUTO_POWER_E_TEMP)) return true; - #endif - - #if HAS_HEATED_CHAMBER && AUTO_POWER_CHAMBER_TEMP - if (thermalManager.degChamber() >= (AUTO_POWER_CHAMBER_TEMP)) return true; - #endif - - #if HAS_COOLER && AUTO_POWER_COOLER_TEMP - if (thermalManager.degCooler() >= (AUTO_POWER_COOLER_TEMP)) return true; - #endif - - return false; + return is_cooling_needed(); } /** @@ -193,7 +240,6 @@ void Power::power_off() { /** * Power off with a delay. Power off is triggered by check() after the delay. - * */ void Power::power_off_soon() { lastPowerOn = millis() - SEC_TO_MS(POWER_TIMEOUT) + SEC_TO_MS(POWER_OFF_DELAY); diff --git a/Marlin/src/feature/power.h b/Marlin/src/feature/power.h index 7f5a97e6df830..839366ca602bd 100644 --- a/Marlin/src/feature/power.h +++ b/Marlin/src/feature/power.h @@ -25,7 +25,7 @@ * power.h - power control */ -#if ENABLED(AUTO_POWER_CONTROL) +#if EITHER(AUTO_POWER_CONTROL, POWER_OFF_TIMER) #include "../core/millis_t.h" #endif @@ -37,20 +37,36 @@ class Power { static void power_on(); static void power_off(); - #if ENABLED(AUTO_POWER_CONTROL) && POWER_OFF_DELAY > 0 - static void power_off_soon(); - #else - static inline void power_off_soon() { power_off(); } - #endif + #if EITHER(POWER_OFF_TIMER, POWER_OFF_WAIT_FOR_COOLDOWN) + #if ENABLED(POWER_OFF_TIMER) + static millis_t power_off_time; + static void setPowerOffTimer(const millis_t delay_ms); + #endif + #if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN) + static bool power_off_on_cooldown; + static void setPowerOffOnCooldown(const bool ena); + #endif + static void cancelAutoPowerOff(); + static void checkAutoPowerOff(); + #endif - #if ENABLED(AUTO_POWER_CONTROL) - static void check(const bool pause); + #if ENABLED(AUTO_POWER_CONTROL) && POWER_OFF_DELAY > 0 + static void power_off_soon(); + #else + static void power_off_soon() { power_off(); } + #endif - private: - static millis_t lastPowerOn; - static bool is_power_needed(); + #if ENABLED(AUTO_POWER_CONTROL) + static void check(const bool pause); - #endif + private: + static millis_t lastPowerOn; + static bool is_power_needed(); + static bool is_cooling_needed(); + #elif ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN) + private: + static bool is_cooling_needed(); + #endif }; extern Power powerManager; diff --git a/Marlin/src/feature/power_monitor.cpp b/Marlin/src/feature/power_monitor.cpp index 1937a54102a8d..5a9db1ec24a1a 100644 --- a/Marlin/src/feature/power_monitor.cpp +++ b/Marlin/src/feature/power_monitor.cpp @@ -26,7 +26,7 @@ #include "power_monitor.h" -#if HAS_LCD_MENU +#if HAS_MARLINUI_MENU #include "../lcd/marlinui.h" #include "../lcd/lcdprint.h" #endif @@ -53,7 +53,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor void PowerMonitor::draw_current() { const float amps = getAmps(); lcd_put_u8str(amps < 100 ? ftostr31ns(amps) : ui16tostr4rj((uint16_t)amps)); - lcd_put_wchar('A'); + lcd_put_lchar('A'); } #endif @@ -61,7 +61,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor void PowerMonitor::draw_voltage() { const float volts = getVolts(); lcd_put_u8str(volts < 100 ? ftostr31ns(volts) : ui16tostr4rj((uint16_t)volts)); - lcd_put_wchar('V'); + lcd_put_lchar('V'); } #endif @@ -69,7 +69,7 @@ PowerMonitor power_monitor; // Single instance - this calls the constructor void PowerMonitor::draw_power() { const float power = getPower(); lcd_put_u8str(power < 100 ? ftostr31ns(power) : ui16tostr4rj((uint16_t)power)); - lcd_put_wchar('W'); + lcd_put_lchar('W'); } #endif diff --git a/Marlin/src/feature/power_monitor.h b/Marlin/src/feature/power_monitor.h index f6e0b292e30cf..fa06909053338 100644 --- a/Marlin/src/feature/power_monitor.h +++ b/Marlin/src/feature/power_monitor.h @@ -32,7 +32,7 @@ struct pm_lpf_t { uint32_t filter_buf; float value; void add_sample(const uint16_t sample) { - filter_buf = filter_buf - (filter_buf >> K_VALUE) + (uint32_t(sample) << K_SCALE); + filter_buf += (uint32_t(sample) << K_SCALE) - (filter_buf >> K_VALUE); } void capture() { value = filter_buf * (SCALE * (1.0f / (1UL << (PM_K_VALUE + PM_K_SCALE)))); diff --git a/Marlin/src/feature/powerloss.cpp b/Marlin/src/feature/powerloss.cpp index 8db31daa40332..d4450adcd8539 100644 --- a/Marlin/src/feature/powerloss.cpp +++ b/Marlin/src/feature/powerloss.cpp @@ -54,6 +54,10 @@ uint32_t PrintJobRecovery::cmd_sdpos, // = 0 #include "../module/temperature.h" #include "../core/serial.h" +#if HOMING_Z_WITH_PROBE + #include "../module/probe.h" +#endif + #if ENABLED(FWRETRACT) #include "fwretract.h" #endif @@ -104,13 +108,18 @@ void PrintJobRecovery::changed() { * * If a saved state exists send 'M1000 S' to initiate job recovery. */ -void PrintJobRecovery::check() { +bool PrintJobRecovery::check() { //if (!card.isMounted()) card.mount(); + bool success = false; if (card.isMounted()) { load(); - if (!valid()) return cancel(); - queue.inject_P(PSTR("M1000S")); + success = valid(); + if (!success) + cancel(); + else + queue.inject(F("M1000S")); } + return success; } /** @@ -130,7 +139,7 @@ void PrintJobRecovery::load() { (void)file.read(&info, sizeof(info)); close(); } - debug(PSTR("Load")); + debug(F("Load")); } /** @@ -178,7 +187,8 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW info.valid_foot = info.valid_head; // Machine state - info.current_position = current_position; + // info.sdpos and info.current_position are pre-filled from the Stepper ISR + info.feedrate = uint16_t(MMS_TO_MMM(feedrate_mm_s)); info.zraise = zraise; info.flag.raised = raised; // Was Z raised before power-off? @@ -191,7 +201,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW #if DISABLED(NO_VOLUMETRICS) info.flag.volumetric_enabled = parser.volumetric_enabled; #if HAS_MULTI_EXTRUDER - for (int8_t e = 0; e < EXTRUDERS; e++) info.filament_size[e] = planner.filament_size[e]; + EXTRUDER_LOOP() info.filament_size[e] = planner.filament_size[e]; #else if (parser.volumetric_enabled) info.filament_size[0] = planner.filament_size[active_extruder]; #endif @@ -244,7 +254,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW #if POWER_LOSS_RETRACT_LEN // Retract filament now - gcode.process_subcommands_now_P(PSTR("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN))); + gcode.process_subcommands_now(F("G1 F3000 E-" STRINGIFY(POWER_LOSS_RETRACT_LEN))); #endif #if POWER_LOSS_ZRAISE @@ -265,6 +275,10 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW #endif +#endif // POWER_LOSS_PIN + +#if PIN_EXISTS(POWER_LOSS) || ENABLED(DEBUG_POWER_LOSS_RECOVERY) + /** * An outage was detected by a sensor pin. * - If not SD printing, let the machine turn off on its own with no "KILL" screen @@ -273,7 +287,7 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW * - If backup power is available Retract E and Raise Z * - Go to the KILL screen */ - void PrintJobRecovery::_outage() { + void PrintJobRecovery::_outage(TERN_(DEBUG_POWER_LOSS_RECOVERY, const bool simulated/*=false*/)) { #if ENABLED(BACKUP_POWER_SUPPLY) static bool lock = false; if (lock) return; // No re-entrance from idle() during retract_and_lift() @@ -301,17 +315,23 @@ void PrintJobRecovery::save(const bool force/*=false*/, const float zraise/*=POW retract_and_lift(zraise); #endif - kill(GET_TEXT(MSG_OUTAGE_RECOVERY)); + if (TERN0(DEBUG_POWER_LOSS_RECOVERY, simulated)) { + card.fileHasFinished(); + current_position.reset(); + sync_plan_position(); + } + else + kill(GET_TEXT_F(MSG_OUTAGE_RECOVERY)); } -#endif +#endif // POWER_LOSS_PIN || DEBUG_POWER_LOSS_RECOVERY /** * Save the recovery info the recovery file */ void PrintJobRecovery::write() { - debug(PSTR("Write")); + debug(F("Write")); open(false); file.seekSet(0); @@ -337,7 +357,7 @@ void PrintJobRecovery::resume() { #if HAS_LEVELING // Make sure leveling is off before any G92 and G28 - gcode.process_subcommands_now_P(PSTR("M420 S0 Z0")); + gcode.process_subcommands_now(F("M420 S0 Z0")); #endif #if HAS_HEATED_BED @@ -373,7 +393,7 @@ void PrintJobRecovery::resume() { // establish the current position as best we can. // - gcode.process_subcommands_now_P(PSTR("G92.9E0")); // Reset E to 0 + gcode.process_subcommands_now(F("G92.9E0")); // Reset E to 0 #if Z_HOME_TO_MAX @@ -390,14 +410,12 @@ void PrintJobRecovery::resume() { #if ENABLED(POWER_LOSS_RECOVER_ZHOME) && defined(POWER_LOSS_ZHOME_POS) #define HOMING_Z_DOWN 1 - #else - #define HOME_XY_ONLY 1 #endif float z_now = info.flag.raised ? z_raised : z_print; - // Reset E to 0 and set Z to the real position - #if HOME_XY_ONLY + #if !HOMING_Z_DOWN + // Set Z to the real position sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 3, str_1)); gcode.process_subcommands_now(cmd); #endif @@ -409,15 +427,15 @@ void PrintJobRecovery::resume() { gcode.process_subcommands_now(cmd); } - // Home XY with no Z raise, and also home Z here if Z isn't homing down below. - gcode.process_subcommands_now_P(PSTR("G28R0" TERN_(HOME_XY_ONLY, "XY"))); // No raise during G28 + // Home XY with no Z raise + gcode.process_subcommands_now(F("G28R0XY")); // No raise during G28 #endif #if HOMING_Z_DOWN // Move to a safe XY position and home Z while avoiding the print. - constexpr xy_pos_t p = POWER_LOSS_ZHOME_POS; - sprintf_P(cmd, PSTR("G1X%sY%sF1000\nG28Z"), dtostrf(p.x, 1, 3, str_1), dtostrf(p.y, 1, 3, str_2)); + const xy_pos_t p = xy_pos_t(POWER_LOSS_ZHOME_POS) TERN_(HOMING_Z_WITH_PROBE, - probe.offset_xy); + sprintf_P(cmd, PSTR("G1X%sY%sF1000\nG28HZ"), dtostrf(p.x, 1, 3, str_1), dtostrf(p.y, 1, 3, str_2)); gcode.process_subcommands_now(cmd); #endif @@ -431,7 +449,7 @@ void PrintJobRecovery::resume() { sprintf_P(cmd, PSTR("M420S%cZ%s"), '0' + (char)info.flag.leveling, dtostrf(info.fade, 1, 1, str_1)); gcode.process_subcommands_now(cmd); - #if HOME_XY_ONLY + #if !HOMING_Z_DOWN // The physical Z was adjusted at power-off so undo the M420S1 correction to Z with G92.9. sprintf_P(cmd, PSTR("G92.9Z%s"), dtostrf(z_now, 1, 1, str_1)); gcode.process_subcommands_now(cmd); @@ -448,7 +466,7 @@ void PrintJobRecovery::resume() { // Recover volumetric extrusion state #if DISABLED(NO_VOLUMETRICS) #if HAS_MULTI_EXTRUDER - for (int8_t e = 0; e < EXTRUDERS; e++) { + EXTRUDER_LOOP() { sprintf_P(cmd, PSTR("M200T%iD%s"), e, dtostrf(info.filament_size[e], 1, 3, str_1)); gcode.process_subcommands_now(cmd); } @@ -498,10 +516,10 @@ void PrintJobRecovery::resume() { // Restore retract and hop state from an active `G10` command #if ENABLED(FWRETRACT) - LOOP_L_N(e, EXTRUDERS) { + EXTRUDER_LOOP() { if (info.retract[e] != 0.0) { fwretract.current_retract[e] = info.retract[e]; - fwretract.retracted[e] = true; + fwretract.retracted.set(e); } } fwretract.current_hop = info.retract_hop; @@ -513,17 +531,17 @@ void PrintJobRecovery::resume() { // Un-retract if there was a retract at outage #if ENABLED(BACKUP_POWER_SUPPLY) && POWER_LOSS_RETRACT_LEN > 0 - gcode.process_subcommands_now_P(PSTR("G1E" STRINGIFY(POWER_LOSS_RETRACT_LEN) "F3000")); + gcode.process_subcommands_now(F("G1F3000E" STRINGIFY(POWER_LOSS_RETRACT_LEN))); #endif // Additional purge on resume if configured #if POWER_LOSS_PURGE_LEN - sprintf_P(cmd, PSTR("G1 E%d F3000"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN)); + sprintf_P(cmd, PSTR("G1F3000E%d"), (POWER_LOSS_PURGE_LEN) + (POWER_LOSS_RETRACT_LEN)); gcode.process_subcommands_now(cmd); #endif #if ENABLED(NOZZLE_CLEAN_FEATURE) - gcode.process_subcommands_now_P(PSTR("G12")); + gcode.process_subcommands_now(F("G12")); #endif // Move back over to the saved XY @@ -549,7 +567,7 @@ void PrintJobRecovery::resume() { TERN_(HAS_HOME_OFFSET, home_offset = info.home_offset); TERN_(HAS_POSITION_SHIFT, position_shift = info.position_shift); #if HAS_HOME_OFFSET || HAS_POSITION_SHIFT - LOOP_LINEAR_AXES(i) update_workspace_offset((AxisEnum)i); + LOOP_NUM_AXES(i) update_workspace_offset((AxisEnum)i); #endif // Relative axis modes @@ -575,8 +593,8 @@ void PrintJobRecovery::resume() { #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - void PrintJobRecovery::debug(PGM_P const prefix) { - DEBUG_ECHOPGM_P(prefix); + void PrintJobRecovery::debug(FSTR_P const prefix) { + DEBUG_ECHOF(prefix); DEBUG_ECHOLNPGM(" Job Recovery Info...\nvalid_head:", info.valid_head, " valid_foot:", info.valid_foot); if (info.valid_head) { if (info.valid_head == info.valid_foot) { @@ -599,7 +617,7 @@ void PrintJobRecovery::resume() { #if HAS_HOME_OFFSET DEBUG_ECHOPGM("home_offset: "); - LOOP_LINEAR_AXES(i) { + LOOP_NUM_AXES(i) { if (i) DEBUG_CHAR(','); DEBUG_DECIMAL(info.home_offset[i]); } @@ -608,7 +626,7 @@ void PrintJobRecovery::resume() { #if HAS_POSITION_SHIFT DEBUG_ECHOPGM("position_shift: "); - LOOP_LINEAR_AXES(i) { + LOOP_NUM_AXES(i) { if (i) DEBUG_CHAR(','); DEBUG_DECIMAL(info.position_shift[i]); } @@ -621,7 +639,7 @@ void PrintJobRecovery::resume() { #if DISABLED(NO_VOLUMETRICS) DEBUG_ECHOPGM("filament_size:"); - LOOP_L_N(i, EXTRUDERS) DEBUG_ECHOLNPGM(" ", info.filament_size[i]); + EXTRUDER_LOOP() DEBUG_ECHOLNPGM(" ", info.filament_size[e]); DEBUG_EOL(); #endif @@ -653,7 +671,7 @@ void PrintJobRecovery::resume() { #if ENABLED(FWRETRACT) DEBUG_ECHOPGM("retract: "); - for (int8_t e = 0; e < EXTRUDERS; e++) { + EXTRUDER_LOOP() { DEBUG_ECHO(info.retract[e]); if (e < EXTRUDERS - 1) DEBUG_CHAR(','); } diff --git a/Marlin/src/feature/powerloss.h b/Marlin/src/feature/powerloss.h index 6a13c92df7976..33d9dc007c0d1 100644 --- a/Marlin/src/feature/powerloss.h +++ b/Marlin/src/feature/powerloss.h @@ -152,7 +152,7 @@ class PrintJobRecovery { static void init(); static void prepare(); - static inline void setup() { + static void setup() { #if PIN_EXISTS(POWER_LOSS) #if ENABLED(POWER_LOSS_PULLUP) SET_INPUT_PULLUP(POWER_LOSS_PIN); @@ -165,28 +165,28 @@ class PrintJobRecovery { } // Track each command's file offsets - static inline uint32_t command_sdpos() { return sdpos[queue_index_r]; } - static inline void commit_sdpos(const uint8_t index_w) { sdpos[index_w] = cmd_sdpos; } + static uint32_t command_sdpos() { return sdpos[queue_index_r]; } + static void commit_sdpos(const uint8_t index_w) { sdpos[index_w] = cmd_sdpos; } static bool enabled; static void enable(const bool onoff); static void changed(); - static inline bool exists() { return card.jobRecoverFileExists(); } - static inline void open(const bool read) { card.openJobRecoveryFile(read); } - static inline void close() { file.close(); } + static bool exists() { return card.jobRecoverFileExists(); } + static void open(const bool read) { card.openJobRecoveryFile(read); } + static void close() { file.close(); } - static void check(); + static bool check(); static void resume(); static void purge(); - static inline void cancel() { purge(); IF_DISABLED(NO_SD_AUTOSTART, card.autofile_begin()); } + static void cancel() { purge(); } static void load(); static void save(const bool force=ENABLED(SAVE_EACH_CMD_MODE), const float zraise=POWER_LOSS_ZRAISE, const bool raised=false); #if PIN_EXISTS(POWER_LOSS) - static inline void outage() { + static void outage() { static constexpr uint8_t OUTAGE_THRESHOLD = 3; static uint8_t outage_counter = 0; if (enabled && READ(POWER_LOSS_PIN) == POWER_LOSS_STATE) { @@ -199,14 +199,14 @@ class PrintJobRecovery { #endif // The referenced file exists - static inline bool interrupted_file_exists() { return card.fileExists(info.sd_filename); } + static bool interrupted_file_exists() { return card.fileExists(info.sd_filename); } - static inline bool valid() { return info.valid() && interrupted_file_exists(); } + static bool valid() { return info.valid() && interrupted_file_exists(); } #if ENABLED(DEBUG_POWER_LOSS_RECOVERY) - static void debug(PGM_P const prefix); + static void debug(FSTR_P const prefix); #else - static inline void debug(PGM_P const) {} + static void debug(FSTR_P const) {} #endif private: @@ -216,9 +216,9 @@ class PrintJobRecovery { static void retract_and_lift(const_float_t zraise); #endif - #if PIN_EXISTS(POWER_LOSS) + #if PIN_EXISTS(POWER_LOSS) || ENABLED(DEBUG_POWER_LOSS_RECOVERY) friend class GcodeSuite; - static void _outage(); + static void _outage(TERN_(DEBUG_POWER_LOSS_RECOVERY, const bool simulated=false)); #endif }; diff --git a/Marlin/src/feature/probe_temp_comp.cpp b/Marlin/src/feature/probe_temp_comp.cpp index deae447568119..b5f636e698c95 100644 --- a/Marlin/src/feature/probe_temp_comp.cpp +++ b/Marlin/src/feature/probe_temp_comp.cpp @@ -22,38 +22,54 @@ #include "../inc/MarlinConfigPre.h" -#if ENABLED(PROBE_TEMP_COMPENSATION) +#if HAS_PTC //#define DEBUG_PTC // Print extra debug output with 'M871' #include "probe_temp_comp.h" #include +#include "../module/temperature.h" -ProbeTempComp temp_comp; +ProbeTempComp ptc; -int16_t ProbeTempComp::z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // = {0} - ProbeTempComp::z_offsets_bed[cali_info_init[TSI_BED].measurements]; // = {0} +#if ENABLED(PTC_PROBE) + constexpr int16_t z_offsets_probe_default[PTC_PROBE_COUNT] = PTC_PROBE_ZOFFS; + int16_t ProbeTempComp::z_offsets_probe[PTC_PROBE_COUNT] = PTC_PROBE_ZOFFS; +#endif -#if ENABLED(USE_TEMP_EXT_COMPENSATION) - int16_t ProbeTempComp::z_offsets_ext[cali_info_init[TSI_EXT].measurements]; // = {0} +#if ENABLED(PTC_BED) + constexpr int16_t z_offsets_bed_default[PTC_BED_COUNT] = PTC_BED_ZOFFS; + int16_t ProbeTempComp::z_offsets_bed[PTC_BED_COUNT] = PTC_BED_ZOFFS; #endif -int16_t *ProbeTempComp::sensor_z_offsets[TSI_COUNT] = { - ProbeTempComp::z_offsets_probe, ProbeTempComp::z_offsets_bed - OPTARG(USE_TEMP_EXT_COMPENSATION, ProbeTempComp::z_offsets_ext) -}; +#if ENABLED(PTC_HOTEND) + constexpr int16_t z_offsets_hotend_default[PTC_HOTEND_COUNT] = PTC_HOTEND_ZOFFS; + int16_t ProbeTempComp::z_offsets_hotend[PTC_HOTEND_COUNT] = PTC_HOTEND_ZOFFS; +#endif -const temp_calib_t ProbeTempComp::cali_info[TSI_COUNT] = { - cali_info_init[TSI_PROBE], cali_info_init[TSI_BED] - OPTARG(USE_TEMP_EXT_COMPENSATION, cali_info_init[TSI_EXT]) +int16_t *ProbeTempComp::sensor_z_offsets[TSI_COUNT] = { + #if ENABLED(PTC_PROBE) + ProbeTempComp::z_offsets_probe, + #endif + #if ENABLED(PTC_BED) + ProbeTempComp::z_offsets_bed, + #endif + #if ENABLED(PTC_HOTEND) + ProbeTempComp::z_offsets_hotend, + #endif }; -constexpr xyz_pos_t ProbeTempComp::park_point; -constexpr xy_pos_t ProbeTempComp::measure_point; -constexpr celsius_t ProbeTempComp::probe_calib_bed_temp; +constexpr temp_calib_t ProbeTempComp::cali_info[TSI_COUNT]; uint8_t ProbeTempComp::calib_idx; // = 0 float ProbeTempComp::init_measurement; // = 0.0 +bool ProbeTempComp::enabled = true; + +void ProbeTempComp::reset() { + TERN_(PTC_PROBE, LOOP_L_N(i, PTC_PROBE_COUNT) z_offsets_probe[i] = z_offsets_probe_default[i]); + TERN_(PTC_BED, LOOP_L_N(i, PTC_BED_COUNT) z_offsets_bed[i] = z_offsets_bed_default[i]); + TERN_(PTC_HOTEND, LOOP_L_N(i, PTC_HOTEND_COUNT) z_offsets_hotend[i] = z_offsets_hotend_default[i]); +} void ProbeTempComp::clear_offsets(const TempSensorID tsi) { LOOP_L_N(i, cali_info[tsi].measurements) @@ -71,11 +87,10 @@ void ProbeTempComp::print_offsets() { LOOP_L_N(s, TSI_COUNT) { celsius_t temp = cali_info[s].start_temp; for (int16_t i = -1; i < cali_info[s].measurements; ++i) { - SERIAL_ECHOPGM_P(s == TSI_BED ? PSTR("Bed") : - #if ENABLED(USE_TEMP_EXT_COMPENSATION) - s == TSI_EXT ? PSTR("Extruder") : - #endif - PSTR("Probe") + SERIAL_ECHOF( + TERN_(PTC_BED, s == TSI_BED ? F("Bed") :) + TERN_(PTC_HOTEND, s == TSI_EXT ? F("Extruder") :) + F("Probe") ); SERIAL_ECHOLNPGM( " temp: ", temp, @@ -100,21 +115,13 @@ void ProbeTempComp::prepare_new_calibration(const_float_t init_meas_z) { } void ProbeTempComp::push_back_new_measurement(const TempSensorID tsi, const_float_t meas_z) { - switch (tsi) { - case TSI_PROBE: - case TSI_BED: - //case TSI_EXT: - if (calib_idx >= cali_info[tsi].measurements) return; - sensor_z_offsets[tsi][calib_idx++] = static_cast(meas_z * 1000.0f - init_measurement * 1000.0f); - default: break; - } + if (calib_idx >= cali_info[tsi].measurements) return; + sensor_z_offsets[tsi][calib_idx++] = static_cast((meas_z - init_measurement) * 1000.0f); } bool ProbeTempComp::finish_calibration(const TempSensorID tsi) { - if (tsi != TSI_PROBE && tsi != TSI_BED) return false; - - if (calib_idx < 3) { - SERIAL_ECHOLNPGM("!Insufficient measurements (min. 3)."); + if (!calib_idx) { + SERIAL_ECHOLNPGM("!No measurements."); clear_offsets(tsi); return false; } @@ -130,16 +137,15 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) { SERIAL_ECHOLNPGM("Got ", calib_idx, " measurements. "); if (linear_regression(tsi, k, d)) { SERIAL_ECHOPGM("Applying linear extrapolation"); - calib_idx--; for (; calib_idx < measurements; ++calib_idx) { - const celsius_float_t temp = start_temp + float(calib_idx) * res_temp; + const celsius_float_t temp = start_temp + float(calib_idx + 1) * res_temp; data[calib_idx] = static_cast(k * temp + d); } } else { // Simply use the last measured value for higher temperatures SERIAL_ECHOPGM("Failed to extrapolate"); - const int16_t last_val = data[calib_idx]; + const int16_t last_val = data[calib_idx-1]; for (; calib_idx < measurements; ++calib_idx) data[calib_idx] = last_val; } @@ -157,7 +163,7 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) { // Restrict the max. offset difference between two probings if (calib_idx > 0 && ABS(data[calib_idx - 1] - data[calib_idx]) > 800) { SERIAL_ECHOLNPGM("!Invalid Z-offset between two probings detected (0-0.8)."); - clear_offsets(TSI_PROBE); + clear_offsets(tsi); return false; } } @@ -165,11 +171,18 @@ bool ProbeTempComp::finish_calibration(const TempSensorID tsi) { return true; } +void ProbeTempComp::apply_compensation(float &meas_z) { + if (!enabled) return; + TERN_(PTC_BED, compensate_measurement(TSI_BED, thermalManager.degBed(), meas_z)); + TERN_(PTC_PROBE, compensate_measurement(TSI_PROBE, thermalManager.degProbe(), meas_z)); + TERN_(PTC_HOTEND, compensate_measurement(TSI_EXT, thermalManager.degHotend(0), meas_z)); +} + void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z) { const uint8_t measurements = cali_info[tsi].measurements; const celsius_t start_temp = cali_info[tsi].start_temp, - end_temp = cali_info[tsi].end_temp, - res_temp = cali_info[tsi].temp_resolution; + res_temp = cali_info[tsi].temp_resolution, + end_temp = start_temp + measurements * res_temp; const int16_t * const data = sensor_z_offsets[tsi]; // Given a data index, return { celsius, zoffset } in the form { x, y } @@ -208,9 +221,7 @@ void ProbeTempComp::compensate_measurement(const TempSensorID tsi, const celsius } bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d) { - if (tsi != TSI_PROBE && tsi != TSI_BED) return false; - - if (!WITHIN(calib_idx, 2, cali_info[tsi].measurements)) return false; + if (!WITHIN(calib_idx, 1, cali_info[tsi].measurements)) return false; const celsius_t start_temp = cali_info[tsi].start_temp, res_temp = cali_info[tsi].temp_resolution; @@ -243,4 +254,4 @@ bool ProbeTempComp::linear_regression(const TempSensorID tsi, float &k, float &d return true; } -#endif // PROBE_TEMP_COMPENSATION +#endif // HAS_PTC diff --git a/Marlin/src/feature/probe_temp_comp.h b/Marlin/src/feature/probe_temp_comp.h index f24b9acd9b188..42348db684732 100644 --- a/Marlin/src/feature/probe_temp_comp.h +++ b/Marlin/src/feature/probe_temp_comp.h @@ -24,9 +24,13 @@ #include "../inc/MarlinConfig.h" enum TempSensorID : uint8_t { - TSI_PROBE, - TSI_BED, - #if ENABLED(USE_TEMP_EXT_COMPENSATION) + #if ENABLED(PTC_PROBE) + TSI_PROBE, + #endif + #if ENABLED(PTC_BED) + TSI_BED, + #endif + #if ENABLED(PTC_HOTEND) TSI_EXT, #endif TSI_COUNT @@ -35,8 +39,7 @@ enum TempSensorID : uint8_t { typedef struct { uint8_t measurements; // Max. number of measurements to be stored (35 - 80°C) celsius_t temp_resolution, // Resolution in °C between measurements - start_temp, // Base measurement; z-offset == 0 - end_temp; + start_temp; // Base measurement; z-offset == 0 } temp_calib_t; /** @@ -45,89 +48,55 @@ typedef struct { * measurement errors/shifts due to changed temperature. */ -// Probe temperature calibration constants -#ifndef PTC_SAMPLE_COUNT - #define PTC_SAMPLE_COUNT 10 -#endif -#ifndef PTC_SAMPLE_RES - #define PTC_SAMPLE_RES 5 -#endif -#ifndef PTC_SAMPLE_START - #define PTC_SAMPLE_START 30 -#endif -#define PTC_SAMPLE_END (PTC_SAMPLE_START + (PTC_SAMPLE_COUNT) * PTC_SAMPLE_RES) - -// Bed temperature calibration constants -#ifndef BTC_PROBE_TEMP - #define BTC_PROBE_TEMP 30 -#endif -#ifndef BTC_SAMPLE_COUNT - #define BTC_SAMPLE_COUNT 10 -#endif -#ifndef BTC_SAMPLE_RES - #define BTC_SAMPLE_RES 5 -#endif -#ifndef BTC_SAMPLE_START - #define BTC_SAMPLE_START 60 -#endif -#define BTC_SAMPLE_END (BTC_SAMPLE_START + (BTC_SAMPLE_COUNT) * BTC_SAMPLE_RES) - -#ifndef PTC_PROBE_HEATING_OFFSET - #define PTC_PROBE_HEATING_OFFSET 0.5f -#endif - -#ifndef PTC_PROBE_RAISE - #define PTC_PROBE_RAISE 10 -#endif - -static constexpr temp_calib_t cali_info_init[TSI_COUNT] = { - { PTC_SAMPLE_COUNT, PTC_SAMPLE_RES, PTC_SAMPLE_START, PTC_SAMPLE_END }, // Probe - { BTC_SAMPLE_COUNT, BTC_SAMPLE_RES, BTC_SAMPLE_START, BTC_SAMPLE_END }, // Bed - #if ENABLED(USE_TEMP_EXT_COMPENSATION) - { 20, 5, 180, 180 + 5 * 20 } // Extruder - #endif -}; - class ProbeTempComp { public: - static const temp_calib_t cali_info[TSI_COUNT]; - - // Where to park nozzle to wait for probe cooldown - static constexpr xyz_pos_t park_point = PTC_PARK_POS; - - // XY coordinates of nozzle for probing the bed - static constexpr xy_pos_t measure_point = PTC_PROBE_POS; // Coordinates to probe - //measure_point = { 12.0f, 7.3f }; // Coordinates for the MK52 magnetic heatbed - - static constexpr celsius_t probe_calib_bed_temp = BED_MAX_TARGET, // Bed temperature while calibrating probe - bed_calib_probe_temp = BTC_PROBE_TEMP; // Probe temperature while calibrating bed - - static int16_t *sensor_z_offsets[TSI_COUNT], - z_offsets_probe[cali_info_init[TSI_PROBE].measurements], // (µm) - z_offsets_bed[cali_info_init[TSI_BED].measurements]; // (µm) - - #if ENABLED(USE_TEMP_EXT_COMPENSATION) - static int16_t z_offsets_ext[cali_info_init[TSI_EXT].measurements]; // (µm) + static constexpr temp_calib_t cali_info[TSI_COUNT] = { + #if ENABLED(PTC_PROBE) + { PTC_PROBE_COUNT, PTC_PROBE_RES, PTC_PROBE_START }, // Probe + #endif + #if ENABLED(PTC_BED) + { PTC_BED_COUNT, PTC_BED_RES, PTC_BED_START }, // Bed + #endif + #if ENABLED(PTC_HOTEND) + { PTC_HOTEND_COUNT, PTC_HOTEND_RES, PTC_HOTEND_START }, // Extruder + #endif + }; + + static int16_t *sensor_z_offsets[TSI_COUNT]; + #if ENABLED(PTC_PROBE) + static int16_t z_offsets_probe[PTC_PROBE_COUNT]; // (µm) + #endif + #if ENABLED(PTC_BED) + static int16_t z_offsets_bed[PTC_BED_COUNT]; // (µm) + #endif + #if ENABLED(PTC_HOTEND) + static int16_t z_offsets_hotend[PTC_HOTEND_COUNT]; // (µm) #endif - static inline void reset_index() { calib_idx = 0; }; - static inline uint8_t get_index() { return calib_idx; } - static void clear_offsets(const TempSensorID tsi); - static inline void clear_all_offsets() { - clear_offsets(TSI_BED); - clear_offsets(TSI_PROBE); - TERN_(USE_TEMP_EXT_COMPENSATION, clear_offsets(TSI_EXT)); + static void reset_index() { calib_idx = 0; }; + static uint8_t get_index() { return calib_idx; } + static void reset(); + static void clear_all_offsets() { + TERN_(PTC_PROBE, clear_offsets(TSI_PROBE)); + TERN_(PTC_BED, clear_offsets(TSI_BED)); + TERN_(PTC_HOTEND, clear_offsets(TSI_EXT)); } 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 bool finish_calibration(const TempSensorID tsi); - static void compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z); + static void set_enabled(const bool ena) { enabled = ena; } + + // Apply all temperature compensation adjustments + static void apply_compensation(float &meas_z); private: static uint8_t calib_idx; + static bool enabled; + + static void clear_offsets(const TempSensorID tsi); /** * Base value. Temperature compensation values will be deltas @@ -140,6 +109,8 @@ class ProbeTempComp { * to allow generating values of higher temperatures. */ static bool linear_regression(const TempSensorID tsi, float &k, float &d); + + static void compensate_measurement(const TempSensorID tsi, const celsius_t temp, float &meas_z); }; -extern ProbeTempComp temp_comp; +extern ProbeTempComp ptc; diff --git a/Marlin/src/feature/repeat.h b/Marlin/src/feature/repeat.h index 0f4d9425b7682..fc11e4a9e2cff 100644 --- a/Marlin/src/feature/repeat.h +++ b/Marlin/src/feature/repeat.h @@ -38,8 +38,8 @@ class Repeat { static repeat_marker_t marker[MAX_REPEAT_NESTING]; static uint8_t index; public: - static inline void reset() { index = 0; } - static inline bool is_active() { + static void reset() { index = 0; } + static bool is_active() { LOOP_L_N(i, index) if (marker[i].counter) return true; return false; } diff --git a/Marlin/src/feature/runout.cpp b/Marlin/src/feature/runout.cpp index 1c5637835992c..98b6bd051061b 100644 --- a/Marlin/src/feature/runout.cpp +++ b/Marlin/src/feature/runout.cpp @@ -68,8 +68,8 @@ bool FilamentMonitorBase::enabled = true, #if ENABLED(EXTENSIBLE_UI) #include "../lcd/extui/ui_api.h" -#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../lcd/e3v2/proui/dwin.h" #endif void event_filament_runout(const uint8_t extruder) { @@ -88,7 +88,7 @@ void event_filament_runout(const uint8_t extruder) { #endif TERN_(EXTENSIBLE_UI, ExtUI::onFilamentRunout(ExtUI::getTool(extruder))); - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_FilamentRunout(extruder)); + TERN_(DWIN_LCD_PROUI, DWIN_FilamentRunout(extruder)); #if ANY(HOST_PROMPT_SUPPORT, HOST_ACTION_COMMANDS, MULTI_FILAMENT_SENSOR) const char tool = '0' + TERN0(MULTI_FILAMENT_SENSOR, extruder); @@ -96,8 +96,7 @@ void event_filament_runout(const uint8_t extruder) { //action:out_of_filament #if ENABLED(HOST_PROMPT_SUPPORT) - host_action_prompt_begin(PROMPT_FILAMENT_RUNOUT, PSTR("FilamentRunout T"), tool); - host_action_prompt_show(); + hostui.prompt_do(PROMPT_FILAMENT_RUNOUT, F("FilamentRunout T"), tool); //action:out_of_filament #endif const bool run_runout_script = !runout.host_handling; @@ -109,18 +108,18 @@ void event_filament_runout(const uint8_t extruder) { || TERN0(ADVANCED_PAUSE_FEATURE, strstr(FILAMENT_RUNOUT_SCRIPT, "M25")) ) ) { - host_action_paused(false); + hostui.paused(false); } else { // Legacy Repetier command for use until newer version supports standard dialog // To be removed later when pause command also triggers dialog #ifdef ACTION_ON_FILAMENT_RUNOUT - host_action(PSTR(ACTION_ON_FILAMENT_RUNOUT " T"), false); + hostui.action(F(ACTION_ON_FILAMENT_RUNOUT " T"), false); SERIAL_CHAR(tool); SERIAL_EOL(); #endif - host_action_pause(false); + hostui.pause(false); } SERIAL_ECHOPGM(" " ACTION_REASON_ON_FILAMENT_RUNOUT " "); SERIAL_CHAR(tool); @@ -140,7 +139,7 @@ void event_filament_runout(const uint8_t extruder) { SERIAL_ECHOPGM("Runout Command: "); SERIAL_ECHOLNPGM(FILAMENT_RUNOUT_SCRIPT); #endif - queue.inject_P(PSTR(FILAMENT_RUNOUT_SCRIPT)); + queue.inject(F(FILAMENT_RUNOUT_SCRIPT)); #endif } } diff --git a/Marlin/src/feature/runout.h b/Marlin/src/feature/runout.h index 918e65bb22e3e..e74d857a79eda 100644 --- a/Marlin/src/feature/runout.h +++ b/Marlin/src/feature/runout.h @@ -83,30 +83,30 @@ class TFilamentMonitor : public FilamentMonitorBase { static sensor_t sensor; public: - static inline void setup() { + static void setup() { sensor.setup(); reset(); } - static inline void reset() { + static void reset() { filament_ran_out = false; response.reset(); } // Call this method when filament is present, // so the response can reset its counter. - static inline void filament_present(const uint8_t extruder) { + static void filament_present(const uint8_t extruder) { response.filament_present(extruder); } #if HAS_FILAMENT_RUNOUT_DISTANCE - static inline float& runout_distance() { return response.runout_distance_mm; } - static inline void set_runout_distance(const_float_t mm) { response.runout_distance_mm = mm; } + static float& runout_distance() { return response.runout_distance_mm; } + static void set_runout_distance(const_float_t 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. - static inline void block_completed(const block_t * const b) { + static void block_completed(const block_t * const b) { if (enabled) { response.block_completed(b); sensor.block_completed(b); @@ -114,7 +114,7 @@ class TFilamentMonitor : public FilamentMonitorBase { } // Give the response a chance to update its counter. - static inline void run() { + static void run() { if (enabled && !filament_ran_out && (printingIsActive() || did_pause_print)) { TERN_(HAS_FILAMENT_RUNOUT_DISTANCE, cli()); // Prevent RunoutResponseDelayed::block_completed from accumulating here response.run(); @@ -168,12 +168,12 @@ class FilamentSensorBase { * Called by FilamentSensorSwitch::run when filament is detected. * Called by FilamentSensorEncoder::block_completed when motion is detected. */ - static inline void filament_present(const uint8_t extruder) { + static void filament_present(const uint8_t extruder) { runout.filament_present(extruder); // ...which calls response.filament_present(extruder) } public: - static inline void setup() { + static void setup() { #define _INIT_RUNOUT_PIN(P,S,U,D) do{ if (ENABLED(U)) SET_INPUT_PULLUP(P); else if (ENABLED(D)) SET_INPUT_PULLDOWN(P); else SET_INPUT(P); }while(0) #define INIT_RUNOUT_PIN(N) _INIT_RUNOUT_PIN(FIL_RUNOUT##N##_PIN, FIL_RUNOUT##N##_STATE, FIL_RUNOUT##N##_PULLUP, FIL_RUNOUT##N##_PULLDOWN) #if NUM_RUNOUT_SENSORS >= 1 @@ -205,14 +205,14 @@ class FilamentSensorBase { } // Return a bitmask of runout pin states - static inline uint8_t poll_runout_pins() { + static uint8_t poll_runout_pins() { #define _OR_RUNOUT(N) | (READ(FIL_RUNOUT##N##_PIN) ? _BV((N) - 1) : 0) return (0 REPEAT_1(NUM_RUNOUT_SENSORS, _OR_RUNOUT)); #undef _OR_RUNOUT } // Return a bitmask of runout flag states (1 bits always indicates runout) - static inline uint8_t poll_runout_states() { + static uint8_t poll_runout_states() { return poll_runout_pins() ^ uint8_t(0 #if NUM_RUNOUT_SENSORS >= 1 | (FIL_RUNOUT1_STATE ? 0 : _BV(1 - 1)) @@ -254,7 +254,7 @@ class FilamentSensorBase { private: static uint8_t motion_detected; - static inline void poll_motion_sensor() { + static void poll_motion_sensor() { static uint8_t old_state; const uint8_t new_state = poll_runout_pins(), change = old_state ^ new_state; @@ -273,7 +273,7 @@ class FilamentSensorBase { } public: - static inline void block_completed(const block_t * const b) { + 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. if (TEST(motion_detected, b->extruder)) @@ -283,7 +283,7 @@ class FilamentSensorBase { motion_detected = 0; } - static inline void run() { poll_motion_sensor(); } + static void run() { poll_motion_sensor(); } }; #else @@ -294,7 +294,7 @@ class FilamentSensorBase { */ class FilamentSensorSwitch : public FilamentSensorBase { private: - static inline bool poll_runout_state(const uint8_t extruder) { + static bool poll_runout_state(const uint8_t extruder) { const uint8_t runout_states = poll_runout_states(); #if MULTI_FILAMENT_SENSOR if ( !TERN0(DUAL_X_CARRIAGE, idex_is_duplicating()) @@ -307,9 +307,9 @@ class FilamentSensorBase { } public: - static inline void block_completed(const block_t * const) {} + static void block_completed(const block_t * const) {} - static inline void run() { + static void run() { LOOP_L_N(s, NUM_RUNOUT_SENSORS) { const bool out = poll_runout_state(s); if (!out) filament_present(s); @@ -317,7 +317,7 @@ class FilamentSensorBase { static uint8_t was_out; // = 0 if (out != TEST(was_out, s)) { TBI(was_out, s); - SERIAL_ECHOLNPGM_P(PSTR("Filament Sensor "), '0' + s, out ? PSTR(" OUT") : PSTR(" IN")); + SERIAL_ECHOLNF(F("Filament Sensor "), AS_DIGIT(s), out ? F(" OUT") : F(" IN")); } #endif } @@ -341,34 +341,34 @@ class FilamentSensorBase { public: static float runout_distance_mm; - static inline void reset() { + static void reset() { LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i); } - static inline void run() { + 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; LOOP_L_N(i, NUM_RUNOUT_SENSORS) - SERIAL_ECHOPGM_P(i ? PSTR(", ") : PSTR("Remaining mm: "), runout_mm_countdown[i]); + SERIAL_ECHOF(i ? F(", ") : F("Remaining mm: "), runout_mm_countdown[i]); SERIAL_EOL(); } #endif } - static inline uint8_t has_run_out() { + static uint8_t has_run_out() { uint8_t runout_flags = 0; LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_mm_countdown[i] < 0) SBI(runout_flags, i); return runout_flags; } - static inline void filament_present(const uint8_t extruder) { + static void filament_present(const uint8_t extruder) { runout_mm_countdown[extruder] = runout_distance_mm; } - static inline void block_completed(const block_t * const b) { + static void block_completed(const block_t * const b) { if (b->steps.x || b->steps.y || b->steps.z || did_pause_print) { // Allow pause purge move to re-trigger runout state // Only trigger on extrusion with XYZ movement to allow filament change and retract/recover. const uint8_t e = b->extruder; @@ -389,23 +389,23 @@ class FilamentSensorBase { static int8_t runout_count[NUM_RUNOUT_SENSORS]; public: - static inline void reset() { + static void reset() { LOOP_L_N(i, NUM_RUNOUT_SENSORS) filament_present(i); } - static inline void run() { + static void run() { LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] >= 0) runout_count[i]--; } - static inline uint8_t has_run_out() { + static uint8_t has_run_out() { uint8_t runout_flags = 0; LOOP_L_N(i, NUM_RUNOUT_SENSORS) if (runout_count[i] < 0) SBI(runout_flags, i); return runout_flags; } - static inline void block_completed(const block_t * const) { } + static void block_completed(const block_t * const) { } - static inline void filament_present(const uint8_t extruder) { + static void filament_present(const uint8_t extruder) { runout_count[extruder] = runout_threshold; } }; diff --git a/Marlin/src/feature/solenoid.cpp b/Marlin/src/feature/solenoid.cpp index b6795d1a1ef8b..861e44ed05deb 100644 --- a/Marlin/src/feature/solenoid.cpp +++ b/Marlin/src/feature/solenoid.cpp @@ -27,31 +27,25 @@ #include "solenoid.h" #include "../module/motion.h" // for active_extruder - -// PARKING_EXTRUDER options alter the default behavior of solenoids, this ensures compliance of M380-381 - -#if ENABLED(PARKING_EXTRUDER) - #include "../module/tool_change.h" -#endif +#include "../module/tool_change.h" // Used primarily with MANUAL_SOLENOID_CONTROL -static void set_solenoid(const uint8_t num, const bool active) { - const uint8_t value = active ? PE_MAGNET_ON_STATE : !PE_MAGNET_ON_STATE; - #define _SOL_CASE(N) case N: TERN_(HAS_SOLENOID_##N, OUT_WRITE(SOL##N##_PIN, value)); break; +static void set_solenoid(const uint8_t num, const uint8_t state) { + #define _SOL_CASE(N) case N: TERN_(HAS_SOLENOID_##N, OUT_WRITE(SOL##N##_PIN, state)); break; switch (num) { REPEAT(8, _SOL_CASE) default: SERIAL_ECHO_MSG(STR_INVALID_SOLENOID); break; } #if ENABLED(PARKING_EXTRUDER) - if (!active && active_extruder == num) // If active extruder's solenoid is disabled, carriage is considered parked + if (state == LOW && active_extruder == num) // If active extruder's solenoid is disabled, carriage is considered parked parking_extruder_set_parked(true); #endif } -void enable_solenoid(const uint8_t num) { set_solenoid(num, true); } -void disable_solenoid(const uint8_t num) { set_solenoid(num, false); } -void enable_solenoid_on_active_extruder() { } +// PARKING_EXTRUDER options alter the default behavior of solenoids to ensure compliance of M380-381 +void enable_solenoid(const uint8_t num) { set_solenoid(num, TERN1(PARKING_EXTRUDER, PE_MAGNET_ON_STATE)); } +void disable_solenoid(const uint8_t num) { set_solenoid(num, TERN0(PARKING_EXTRUDER, !PE_MAGNET_ON_STATE)); } void disable_all_solenoids() { #define _SOL_DISABLE(N) TERN_(HAS_SOLENOID_##N, disable_solenoid(N)); diff --git a/Marlin/src/feature/solenoid.h b/Marlin/src/feature/solenoid.h index 2ba4983fb000e..3131aeb868d47 100644 --- a/Marlin/src/feature/solenoid.h +++ b/Marlin/src/feature/solenoid.h @@ -21,7 +21,6 @@ */ #pragma once -void enable_solenoid_on_active_extruder(); void disable_all_solenoids(); void enable_solenoid(const uint8_t num); void disable_solenoid(const uint8_t num); diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp index ea6fc4990e950..da38646a3674d 100644 --- a/Marlin/src/feature/spindle_laser.cpp +++ b/Marlin/src/feature/spindle_laser.cpp @@ -39,17 +39,26 @@ #endif SpindleLaser cutter; -uint8_t SpindleLaser::power; +bool SpindleLaser::enable_state; // Virtual enable state, controls enable pin if present and or apply power if > 0 +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 ENABLED(LASER_FEATURE) - cutter_test_pulse_t SpindleLaser::testPulse = 50; // Test fire Pulse time ms value. + 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 + feedRate_t SpindleLaser::feedrate_mm_m = 1500, + SpindleLaser::last_feedrate_mm_m; // = 0 // (mm/min) Track feedrate changes for dynamic power #endif -bool SpindleLaser::isReady; // Ready to apply power setting from the UI to OCR -cutter_power_t SpindleLaser::menuPower, // Power set via LCD menu in PWM, PERCENT, or RPM - SpindleLaser::unitPower; // LCD status power in PWM, PERCENT, or RPM -#if ENABLED(MARLIN_DEV_MODE) - cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K -#endif +bool SpindleLaser::isReadyForUI = false; // Ready to apply power setting from the UI to OCR +CutterMode SpindleLaser::cutter_mode = CUTTER_MODE_STANDARD; // Default is standard mode + +constexpr cutter_cpower_t SpindleLaser::power_floor; +cutter_power_t SpindleLaser::menuPower = 0, // Power value via LCD menu in PWM, PERCENT, or RPM based on configured format set by CUTTER_POWER_UNIT. + SpindleLaser::unitPower = 0; // Unit power is in PWM, PERCENT, or RPM based on CUTTER_POWER_UNIT. + +cutter_frequency_t SpindleLaser::frequency; // PWM frequency setting; range: 2K - 50K + #define SPINDLE_LASER_PWM_OFF TERN(SPINDLE_LASER_PWM_INVERT, 255, 0) /** @@ -57,20 +66,20 @@ cutter_power_t SpindleLaser::menuPower, // Power s */ void SpindleLaser::init() { #if ENABLED(SPINDLE_SERVO) - MOVE_SERVO(SPINDLE_SERVO_NR, SPINDLE_SERVO_MIN); + servo[SPINDLE_SERVO_NR].move(SPINDLE_SERVO_MIN); #else OUT_WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); // Init spindle to off #endif #if ENABLED(SPINDLE_CHANGE_DIR) - OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR ? 255 : 0); // Init rotation to clockwise (M3) + OUT_WRITE(SPINDLE_DIR_PIN, SPINDLE_INVERT_DIR); // Init rotation to clockwise (M3) + #endif + #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY + frequency = SPINDLE_LASER_FREQUENCY; + hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY); #endif #if ENABLED(SPINDLE_LASER_USE_PWM) SET_PWM(SPINDLE_LASER_PWM_PIN); - analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed - #endif - #if ENABLED(HAL_CAN_SET_PWM_FREQ) && defined(SPINDLE_LASER_FREQUENCY) - set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY); - TERN_(MARLIN_DEV_MODE, frequency = SPINDLE_LASER_FREQUENCY); + hal.set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_PWM_OFF); // Set to lowest speed #endif #if ENABLED(AIR_EVACUATION) OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); // Init Vacuum/Blower OFF @@ -78,9 +87,7 @@ void SpindleLaser::init() { #if ENABLED(AIR_ASSIST) OUT_WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_ACTIVE); // Init Air Assist OFF #endif - #if ENABLED(I2C_AMMETER) - ammeter.init(); // Init I2C Ammeter - #endif + TERN_(I2C_AMMETER, ammeter.init()); // Init I2C Ammeter } #if ENABLED(SPINDLE_LASER_USE_PWM) @@ -90,12 +97,10 @@ void SpindleLaser::init() { * @param ocr Power value */ void SpindleLaser::_set_ocr(const uint8_t ocr) { - #if NEEDS_HARDWARE_PWM && SPINDLE_LASER_FREQUENCY - set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), TERN(MARLIN_DEV_MODE, frequency, SPINDLE_LASER_FREQUENCY)); - set_pwm_duty(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); - #else - analogWrite(pin_t(SPINDLE_LASER_PWM_PIN), ocr ^ SPINDLE_LASER_PWM_OFF); + #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); } void SpindleLaser::set_ocr(const uint8_t ocr) { @@ -110,36 +115,41 @@ void SpindleLaser::init() { #endif // SPINDLE_LASER_USE_PWM /** - * Apply power for laser/spindle + * Apply power for Laser or Spindle * * Apply cutter power value for PWM, Servo, and on/off pin. * - * @param opwr Power value. Range 0 to MAX. When 0 disable spindle/laser. + * @param opwr Power value. Range 0 to MAX. */ void SpindleLaser::apply_power(const uint8_t opwr) { - static uint8_t last_power_applied = 0; - if (opwr == last_power_applied) return; - last_power_applied = opwr; - power = opwr; - #if ENABLED(SPINDLE_LASER_USE_PWM) - if (cutter.unitPower == 0 && CUTTER_UNIT_IS(RPM)) { - ocr_off(); - isReady = false; - } - else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled()) { - set_ocr(power); - isReady = true; - } - else { - ocr_off(); - isReady = false; - } - #elif ENABLED(SPINDLE_SERVO) - MOVE_SERVO(SPINDLE_SERVO_NR, power); - #else - WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); - isReady = true; - #endif + 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 + 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) + ocr_off(); + else if (ENABLED(CUTTER_POWER_RELATIVE) || enabled() || opwr == 0) { + set_ocr(opwr); + isReadyForUI = true; + } + else + ocr_off(); + #elif ENABLED(SPINDLE_SERVO) + MOVE_SERVO(SPINDLE_SERVO_NR, power); + #else + WRITE(SPINDLE_LASER_ENA_PIN, enabled() ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); + isReadyForUI = true; + #endif + } + else { + #if PIN_EXISTS(SPINDLE_LASER_ENA) + WRITE(SPINDLE_LASER_ENA_PIN, !SPINDLE_LASER_ACTIVE_STATE); + #endif + isReadyForUI = false; // Only used for UI display updates. + TERN_(SPINDLE_LASER_USE_PWM, ocr_off()); + } } #if ENABLED(SPINDLE_CHANGE_DIR) @@ -163,8 +173,8 @@ void SpindleLaser::apply_power(const uint8_t opwr) { #if ENABLED(AIR_ASSIST) // Enable / disable air assist - void SpindleLaser::air_assist_enable() { WRITE(AIR_ASSIST_PIN, AIR_ASSIST_PIN); } // Turn ON - void SpindleLaser::air_assist_disable() { WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_PIN); } // Turn OFF + void SpindleLaser::air_assist_enable() { WRITE(AIR_ASSIST_PIN, AIR_ASSIST_ACTIVE); } // Turn ON + void SpindleLaser::air_assist_disable() { WRITE(AIR_ASSIST_PIN, !AIR_ASSIST_ACTIVE); } // Turn OFF void SpindleLaser::air_assist_toggle() { TOGGLE(AIR_ASSIST_PIN); } // Toggle state #endif diff --git a/Marlin/src/feature/spindle_laser.h b/Marlin/src/feature/spindle_laser.h index ba82c4d7319a2..c4923a0ec5acb 100644 --- a/Marlin/src/feature/spindle_laser.h +++ b/Marlin/src/feature/spindle_laser.h @@ -30,98 +30,101 @@ #include "spindle_laser_types.h" -#if USE_BEEPER +#if HAS_BEEPER #include "../libs/buzzer.h" #endif -#if ENABLED(LASER_POWER_INLINE) - #include "../module/planner.h" -#endif +// Inline laser power +#include "../module/planner.h" #define PCT_TO_PWM(X) ((X) * 255 / 100) #define PCT_TO_SERVO(X) ((X) * 180 / 100) -#ifndef SPEED_POWER_INTERCEPT - #define SPEED_POWER_INTERCEPT 0 -#endif - -// #define _MAP(N,S1,S2,D1,D2) ((N)*_MAX((D2)-(D1),0)/_MAX((S2)-(S1),1)+(D1)) +// Laser/Cutter operation mode +enum CutterMode : int8_t { + CUTTER_MODE_ERROR = -1, + CUTTER_MODE_STANDARD, // M3 power is applied directly and waits for planner moves to sync. + CUTTER_MODE_CONTINUOUS, // M3 or G1/2/3 move power is controlled within planner blocks, set with 'M3 I', cleared with 'M5 I'. + CUTTER_MODE_DYNAMIC // M4 laser power is proportional to the feed rate, set with 'M4 I', cleared with 'M5 I'. +}; class SpindleLaser { public: - 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)), - max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX); + static CutterMode cutter_mode; - static const inline 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_t pct) { return uint8_t(PCT_TO_PWM(pct)); } // cpower = configured values (e.g., SPEED_POWER_MAX) - // Convert configured power range to a percentage - static const inline uint8_t cpwr_to_pct(const cutter_cpower_t cpwr) { - constexpr cutter_cpower_t power_floor = TERN(CUTTER_POWER_RELATIVE, SPEED_POWER_MIN, 0), - power_range = SPEED_POWER_MAX - power_floor; - return cpwr ? round(100.0f * (cpwr - power_floor) / power_range) : 0; + 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; } - // Convert a cpower (e.g., SPEED_POWER_STARTUP) to unit power (upwr, upower), - // which can be PWM, Percent, Servo angle, or RPM (rel/abs). - static const inline cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power - const cutter_power_t upwr = ( + // Convert config defines from RPM to %, angle or PWM when in Spindle mode + // and convert from PERCENT to PWM when in Laser mode + static constexpr cutter_power_t cpwr_to_upwr(const cutter_cpower_t cpwr) { // STARTUP power to Unit power + return ( #if ENABLED(SPINDLE_FEATURE) - // Spindle configured values are in RPM + // Spindle configured define values are in RPM #if CUTTER_UNIT_IS(RPM) - cpwr // to RPM - #elif CUTTER_UNIT_IS(PERCENT) // to PCT - cpwr_to_pct(cpwr) - #elif CUTTER_UNIT_IS(SERVO) // to SERVO angle - PCT_TO_SERVO(cpwr_to_pct(cpwr)) - #else // to PWM - PCT_TO_PWM(cpwr_to_pct(cpwr)) + cpwr // to same + #elif CUTTER_UNIT_IS(PERCENT) + cpwr_to_pct(cpwr) // to Percent + #elif CUTTER_UNIT_IS(SERVO) + PCT_TO_SERVO(cpwr_to_pct(cpwr)) // to SERVO angle + #else + PCT_TO_PWM(cpwr_to_pct(cpwr)) // to PWM #endif #else - // Laser configured values are in PCT + // Laser configured define values are in Percent #if CUTTER_UNIT_IS(PWM255) - PCT_TO_PWM(cpwr) + PCT_TO_PWM(cpwr) // to PWM #else - cpwr // to RPM/PCT + cpwr // to same #endif #endif ); - return upwr; } - static const cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); } - static const cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); } + static constexpr cutter_power_t mpower_min() { return cpwr_to_upwr(SPEED_POWER_MIN); } + static constexpr cutter_power_t mpower_max() { return cpwr_to_upwr(SPEED_POWER_MAX); } #if ENABLED(LASER_FEATURE) - static cutter_test_pulse_t testPulse; // Test fire Pulse ms value + static cutter_test_pulse_t testPulse; // (ms) Test fire pulse duration + static uint8_t last_block_power; // Track power changes for dynamic power + + static feedRate_t feedrate_mm_m, last_feedrate_mm_m; // (mm/min) Track feedrate changes for dynamic power + static bool laser_feedrate_changed() { + const bool changed = last_feedrate_mm_m != feedrate_mm_m; + if (changed) last_feedrate_mm_m = feedrate_mm_m; + return changed; + } #endif - static bool isReady; // Ready to apply power setting from the UI to OCR - static uint8_t power; + static bool isReadyForUI; // Ready to apply power setting from the UI to OCR + static bool enable_state; + static uint8_t power, + last_power_applied; // Basic power state tracking - #if ENABLED(MARLIN_DEV_MODE) - static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K - #endif + static cutter_frequency_t frequency; // Set PWM frequency; range: 2K-50K 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 void init(); - #if ENABLED(MARLIN_DEV_MODE) - static inline void refresh_frequency() { set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); } + #if ENABLED(HAL_CAN_SET_PWM_FREQ) && SPINDLE_LASER_FREQUENCY + static void refresh_frequency() { hal.set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), frequency); } #endif // Modifying this function should update everywhere - static inline bool enabled(const cutter_power_t opwr) { return opwr > 0; } - static inline bool enabled() { return enabled(power); } + static bool enabled(const cutter_power_t opwr) { return opwr > 0; } + static bool enabled() { return enable_state; } static void apply_power(const uint8_t inpow); FORCE_INLINE static void refresh() { apply_power(power); } - FORCE_INLINE static void set_power(const uint8_t upwr) { power = upwr; refresh(); } #if ENABLED(SPINDLE_LASER_USE_PWM) @@ -132,13 +135,12 @@ class SpindleLaser { public: static void set_ocr(const uint8_t ocr); - static inline void ocr_set_power(const uint8_t ocr) { power = ocr; set_ocr(ocr); } static void ocr_off(); /** * Update output for power->OCR translation */ - static inline uint8_t upower_to_ocr(const cutter_power_t upwr) { + static uint8_t upower_to_ocr(const cutter_power_t upwr) { return uint8_t( #if CUTTER_UNIT_IS(PWM255) upwr @@ -150,201 +152,178 @@ class SpindleLaser { ); } - /** - * Correct power to configured range - */ - static inline cutter_power_t power_to_range(const cutter_power_t pwr) { - return power_to_range(pwr, _CUTTER_POWER(CUTTER_POWER_UNIT)); - } - - static inline cutter_power_t power_to_range(const cutter_power_t pwr, const uint8_t pwrUnit) { - if (pwr <= 0) return 0; - cutter_power_t upwr; - switch (pwrUnit) { - case _CUTTER_POWER_PWM255: - upwr = cutter_power_t( - (pwr < pct_to_ocr(min_pct)) ? pct_to_ocr(min_pct) // Use minimum if set below - : (pwr > pct_to_ocr(max_pct)) ? pct_to_ocr(max_pct) // Use maximum if set above - : pwr - ); - break; - case _CUTTER_POWER_PERCENT: - upwr = cutter_power_t( - (pwr < min_pct) ? min_pct // Use minimum if set below - : (pwr > max_pct) ? max_pct // Use maximum if set above - : pwr // PCT - ); - break; - case _CUTTER_POWER_RPM: - upwr = cutter_power_t( - (pwr < SPEED_POWER_MIN) ? SPEED_POWER_MIN // Use minimum if set below - : (pwr > SPEED_POWER_MAX) ? SPEED_POWER_MAX // Use maximum if set above - : pwr // Calculate OCR value - ); - break; - default: break; - } - return upwr; - } #endif // SPINDLE_LASER_USE_PWM /** - * Enable/Disable spindle/laser - * @param enable true = enable; false = disable + * Correct power to configured range */ - static inline void set_enabled(const bool enable) { - uint8_t value = 0; - if (enable) { - #if ENABLED(SPINDLE_LASER_USE_PWM) - if (power) - value = power; - else if (unitPower) - value = upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP)); - #else - value = 255; - #endif + 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)), + max_pct = TERN(SPINDLE_FEATURE, 100, SPEED_POWER_MAX); + if (pwr <= 0) return 0; + cutter_power_t upwr; + switch (pwrUnit) { + case _CUTTER_POWER_PWM255: { // PWM + const uint8_t pmin = pct_to_ocr(min_pct), pmax = pct_to_ocr(max_pct); + upwr = cutter_power_t(constrain(pwr, pmin, pmax)); + } break; + case _CUTTER_POWER_PERCENT: // Percent + upwr = cutter_power_t(constrain(pwr, min_pct, max_pct)); + break; + case _CUTTER_POWER_RPM: // Calculate OCR value + upwr = cutter_power_t(constrain(pwr, SPEED_POWER_MIN, SPEED_POWER_MAX)); + break; + default: break; } - set_power(value); + return upwr; } - static inline void disable() { isReady = false; set_enabled(false); } - /** - * Wait for spindle to spin up or spin down + * Enable Laser or Spindle output. + * It's important to prevent changing the power output value during inline cutter operation. + * Inline power is adjusted in the planner to support LASER_TRAP_POWER and CUTTER_MODE_DYNAMIC mode. + * + * This method accepts one of the following control states: + * + * - For CUTTER_MODE_STANDARD the cutter power is either full on/off or ocr-based and it will apply + * SPEED_POWER_STARTUP if no value is assigned. * - * @param on true = state to on; false = state to off. + * - For CUTTER_MODE_CONTINUOUS inline and power remains where last set and the cutter output enable flag is set. + * + * - CUTTER_MODE_DYNAMIC is also inline-based and it just sets the enable output flag. + * + * - For CUTTER_MODE_ERROR set the output enable_state flag directly and set power to 0 for any mode. + * This mode allows a global power shutdown action to occur. */ - static inline void power_delay(const bool on) { - #if DISABLED(LASER_POWER_INLINE) - safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY); + static void set_enabled(const bool enable) { + switch (cutter_mode) { + case CUTTER_MODE_STANDARD: + apply_power(enable ? TERN(SPINDLE_LASER_USE_PWM, (power ?: (unitPower ? upower_to_ocr(cpwr_to_upwr(SPEED_POWER_STARTUP)) : 0)), 255) : 0); + break; + case CUTTER_MODE_CONTINUOUS: + TERN_(LASER_FEATURE, set_inline_enabled(enable)); + break; + case CUTTER_MODE_DYNAMIC: + TERN_(LASER_FEATURE, set_inline_enabled(enable)); + break; + case CUTTER_MODE_ERROR: // Error mode, no enable and kill power. + enable_state = false; + apply_power(0); + } + #if SPINDLE_LASER_ENA_PIN + WRITE(SPINDLE_LASER_ENA_PIN, enable ? SPINDLE_LASER_ACTIVE_STATE : !SPINDLE_LASER_ACTIVE_STATE); #endif + enable_state = enable; + } + + static void disable() { isReadyForUI = false; set_enabled(false); } + + // Wait for spindle/laser to startup or shutdown + static void power_delay(const bool on) { + safe_delay(on ? SPINDLE_LASER_POWERUP_DELAY : SPINDLE_LASER_POWERDOWN_DELAY); } #if ENABLED(SPINDLE_CHANGE_DIR) static void set_reverse(const bool reverse); static bool is_reverse() { return READ(SPINDLE_DIR_PIN) == SPINDLE_INVERT_DIR; } #else - static inline void set_reverse(const bool) {} + static void set_reverse(const bool) {} static bool is_reverse() { return false; } #endif #if ENABLED(AIR_EVACUATION) - static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor - static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor - static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor - static inline bool air_evac_state() { // Get current state + static void air_evac_enable(); // Turn On Cutter Vacuum or Laser Blower motor + static void air_evac_disable(); // Turn Off Cutter Vacuum or Laser Blower motor + static void air_evac_toggle(); // Toggle Cutter Vacuum or Laser Blower motor + static bool air_evac_state() { // Get current state return (READ(AIR_EVACUATION_PIN) == AIR_EVACUATION_ACTIVE); } #endif #if ENABLED(AIR_ASSIST) - static void air_assist_enable(); // Turn on air assist - static void air_assist_disable(); // Turn off air assist - static void air_assist_toggle(); // Toggle air assist - static inline bool air_assist_state() { // Get current state + static void air_assist_enable(); // Turn on air assist + static void air_assist_disable(); // Turn off air assist + static void air_assist_toggle(); // Toggle air assist + static bool air_assist_state() { // Get current state return (READ(AIR_ASSIST_PIN) == AIR_ASSIST_ACTIVE); } #endif - #if HAS_LCD_MENU - static inline void enable_with_dir(const bool reverse) { - isReady = true; - const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255); - if (menuPower) - power = ocr; - else - menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP); - unitPower = menuPower; - set_reverse(reverse); - set_enabled(true); - } - FORCE_INLINE static void enable_forward() { enable_with_dir(false); } - FORCE_INLINE static void enable_reverse() { enable_with_dir(true); } - FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); } + #if HAS_MARLINUI_MENU + + #if ENABLED(SPINDLE_FEATURE) + static void enable_with_dir(const bool reverse) { + isReadyForUI = true; + const uint8_t ocr = TERN(SPINDLE_LASER_USE_PWM, upower_to_ocr(menuPower), 255); + if (menuPower) + power = ocr; + else + menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP); + unitPower = menuPower; + set_reverse(reverse); + set_enabled(true); + } + FORCE_INLINE static void enable_forward() { enable_with_dir(false); } + FORCE_INLINE static void enable_reverse() { enable_with_dir(true); } + FORCE_INLINE static void enable_same_dir() { enable_with_dir(is_reverse()); } + #endif // SPINDLE_FEATURE #if ENABLED(SPINDLE_LASER_USE_PWM) - static inline void update_from_mpower() { - if (isReady) power = upower_to_ocr(menuPower); + static void update_from_mpower() { + if (isReadyForUI) power = upower_to_ocr(menuPower); unitPower = menuPower; } #endif #if ENABLED(LASER_FEATURE) + // Toggle the laser on/off with menuPower. Apply SPEED_POWER_STARTUP if it was 0 on entry. + static void laser_menu_toggle(const bool state) { + set_enabled(state); + if (state) { + if (!menuPower) menuPower = cpwr_to_upwr(SPEED_POWER_STARTUP); + power = upower_to_ocr(menuPower); + apply_power(power); + } + } + /** * Test fire the laser using the testPulse ms duration * Also fires with any PWM power that was previous set * If not set defaults to 80% power */ - static inline void test_fire_pulse() { - TERN_(USE_BEEPER, buzzer.tone(30, 3000)); - enable_forward(); // Turn Laser on (Spindle speak but same funct) + static void test_fire_pulse() { + BUZZ(30, 3000); + cutter_mode = CUTTER_MODE_STANDARD;// Menu needs standard mode. + laser_menu_toggle(true); // Laser On delay(testPulse); // Delay for time set by user in pulse ms menu screen. - disable(); // Turn laser off + laser_menu_toggle(false); // Laser Off } - #endif + #endif // LASER_FEATURE - #endif // HAS_LCD_MENU + #endif // HAS_MARLINUI_MENU - #if ENABLED(LASER_POWER_INLINE) - /** - * Inline power adds extra fields to the planner block - * to handle laser power and scale to movement speed. - */ + #if ENABLED(LASER_FEATURE) - // Force disengage planner power control - static inline void inline_disable() { - isReady = false; - unitPower = 0; - planner.laser_inline.status.isPlanned = false; - planner.laser_inline.status.isEnabled = false; - planner.laser_inline.power = 0; + // Dynamic mode rate calculation + static uint8_t calc_dynamic_power() { + if (feedrate_mm_m > 65535) return 255; // Too fast, go always on + uint16_t rate = uint16_t(feedrate_mm_m); // 16 bits from the G-code parser float input + rate >>= 8; // Take the G-code input e.g. F40000 and shift off the lower bits to get an OCR value from 1-255 + return uint8_t(rate); } // Inline modes of all other functions; all enable planner inline power control - static inline void set_inline_enabled(const bool enable) { - if (enable) - inline_power(255); - else { - isReady = false; - unitPower = menuPower = 0; - planner.laser_inline.status.isPlanned = false; - TERN(SPINDLE_LASER_USE_PWM, inline_ocr_power, inline_power)(0); - } - } + static void set_inline_enabled(const bool enable) { planner.laser_inline.status.isEnabled = enable;} // Set the power for subsequent movement blocks - static void inline_power(const cutter_power_t upwr) { - unitPower = menuPower = upwr; - #if ENABLED(SPINDLE_LASER_USE_PWM) - #if ENABLED(SPEED_POWER_RELATIVE) && !CUTTER_UNIT_IS(RPM) // relative mode does not turn laser off at 0, except for RPM - planner.laser_inline.status.isEnabled = true; - planner.laser_inline.power = upower_to_ocr(upwr); - isReady = true; - #else - inline_ocr_power(upower_to_ocr(upwr)); - #endif - #else - planner.laser_inline.status.isEnabled = enabled(upwr); - planner.laser_inline.power = upwr; - isReady = enabled(upwr); - #endif + static void inline_power(const cutter_power_t cpwr) { + TERN(SPINDLE_LASER_USE_PWM, power = planner.laser_inline.power = cpwr, planner.laser_inline.power = cpwr > 0 ? 255 : 0); } - static inline void inline_direction(const bool) { /* never */ } - - #if ENABLED(SPINDLE_LASER_USE_PWM) - static inline void inline_ocr_power(const uint8_t ocrpwr) { - isReady = ocrpwr > 0; - planner.laser_inline.status.isEnabled = ocrpwr > 0; - planner.laser_inline.power = ocrpwr; - } - #endif - #endif // LASER_POWER_INLINE + #endif // LASER_FEATURE - static inline void kill() { - TERN_(LASER_POWER_INLINE, inline_disable()); - disable(); - } + static void kill() { disable(); } }; extern SpindleLaser cutter; diff --git a/Marlin/src/feature/spindle_laser_types.h b/Marlin/src/feature/spindle_laser_types.h index 0075e54819003..2f36a68a1a320 100644 --- a/Marlin/src/feature/spindle_laser_types.h +++ b/Marlin/src/feature/spindle_laser_types.h @@ -28,12 +28,34 @@ #include "../inc/MarlinConfigPre.h" +#define MSG_CUTTER(M) _MSG_CUTTER(M) + +#ifndef SPEED_POWER_INTERCEPT + #define SPEED_POWER_INTERCEPT 0 +#endif #if ENABLED(SPINDLE_FEATURE) #define _MSG_CUTTER(M) MSG_SPINDLE_##M + #ifndef SPEED_POWER_MIN + #define SPEED_POWER_MIN 5000 + #endif + #ifndef SPEED_POWER_MAX + #define SPEED_POWER_MAX 30000 + #endif + #ifndef SPEED_POWER_STARTUP + #define SPEED_POWER_STARTUP 25000 + #endif #else #define _MSG_CUTTER(M) MSG_LASER_##M + #ifndef SPEED_POWER_MIN + #define SPEED_POWER_MIN 0 + #endif + #ifndef SPEED_POWER_MAX + #define SPEED_POWER_MAX 255 + #endif + #ifndef SPEED_POWER_STARTUP + #define SPEED_POWER_STARTUP 255 + #endif #endif -#define MSG_CUTTER(M) _MSG_CUTTER(M) typedef IF<(SPEED_POWER_MAX > 255), uint16_t, uint8_t>::type cutter_cpower_t; @@ -52,12 +74,10 @@ typedef IF<(SPEED_POWER_MAX > 255), uint16_t, uint8_t>::type cutter_cpower_t; #endif #endif +typedef uint16_t cutter_frequency_t; + #if ENABLED(LASER_FEATURE) typedef uint16_t cutter_test_pulse_t; #define CUTTER_MENU_PULSE_TYPE uint16_3 -#endif - -#if ENABLED(MARLIN_DEV_MODE) - typedef uint16_t cutter_frequency_t; #define CUTTER_MENU_FREQUENCY_TYPE uint16_5 #endif diff --git a/Marlin/src/feature/stepper_driver_safety.cpp b/Marlin/src/feature/stepper_driver_safety.cpp index c7da5d2ff7f6a..11b90954b4f51 100644 --- a/Marlin/src/feature/stepper_driver_safety.cpp +++ b/Marlin/src/feature/stepper_driver_safety.cpp @@ -28,11 +28,11 @@ static uint32_t axis_plug_backward = 0; -void stepper_driver_backward_error(PGM_P str) { +void stepper_driver_backward_error(FSTR_P const fstr) { SERIAL_ERROR_START(); - SERIAL_ECHOPGM_P(str); + SERIAL_ECHOF(fstr); SERIAL_ECHOLNPGM(" driver is backward!"); - ui.status_printf_P(2, PSTR(S_FMT S_FMT), str, GET_TEXT(MSG_DRIVER_BACKWARD)); + ui.status_printf(2, F(S_FMT S_FMT), FTOP(fstr), GET_TEXT(MSG_DRIVER_BACKWARD)); } void stepper_driver_backward_check() { @@ -45,7 +45,7 @@ void stepper_driver_backward_check() { delay(20); \ if (READ(AXIS##_ENABLE_PIN) == false) { \ SBI(axis_plug_backward, BIT); \ - stepper_driver_backward_error(PSTR(STRINGIFY(AXIS))); \ + stepper_driver_backward_error(F(STRINGIFY(AXIS))); \ } \ }while(0) @@ -82,12 +82,12 @@ void stepper_driver_backward_check() { void stepper_driver_backward_report() { if (!axis_plug_backward) return; - auto _report_if_backward = [](PGM_P axis, uint8_t bit) { + auto _report_if_backward = [](FSTR_P const axis, uint8_t bit) { if (TEST(axis_plug_backward, bit)) stepper_driver_backward_error(axis); }; - #define REPORT_BACKWARD(axis, bit) TERN_(HAS_##axis##_ENABLE, _report_if_backward(PSTR(STRINGIFY(axis)), bit)) + #define REPORT_BACKWARD(axis, bit) TERN_(HAS_##axis##_ENABLE, _report_if_backward(F(STRINGIFY(axis)), bit)) REPORT_BACKWARD(X, 0); REPORT_BACKWARD(X2, 1); diff --git a/Marlin/src/feature/tmc_util.cpp b/Marlin/src/feature/tmc_util.cpp index 97fedf13c59cf..ef3fb3a248bde 100644 --- a/Marlin/src/feature/tmc_util.cpp +++ b/Marlin/src/feature/tmc_util.cpp @@ -33,17 +33,12 @@ #include "../gcode/gcode.h" #if ENABLED(TMC_DEBUG) - #include "../module/planner.h" #include "../libs/hex_print.h" #if ENABLED(MONITOR_DRIVER_STATUS) static uint16_t report_tmc_status_interval; // = 0 #endif #endif -#if HAS_LCD_MENU - #include "../module/stepper.h" -#endif - /** * Check for over temperature or short to ground error flags. * Report and log warning of overtemperature condition. @@ -212,7 +207,7 @@ if (data.is_ot) SERIAL_ECHOLNPGM("overtemperature"); if (data.is_s2g) SERIAL_ECHOLNPGM("coil short circuit"); TERN_(TMC_DEBUG, tmc_report_all()); - kill(PSTR("Driver error")); + kill(F("Driver error")); } #endif @@ -421,12 +416,10 @@ if (monitor_tmc_driver(stepperI, need_update_error_counters, need_debug_reporting)) step_current_down(stepperI); #endif - #if AXIS_IS_TMC(J) if (monitor_tmc_driver(stepperJ, need_update_error_counters, need_debug_reporting)) step_current_down(stepperJ); #endif - #if AXIS_IS_TMC(K) if (monitor_tmc_driver(stepperK, need_update_error_counters, need_debug_reporting)) step_current_down(stepperK); @@ -472,12 +465,8 @@ void tmc_set_report_interval(const uint16_t update_interval) { if ((report_tmc_status_interval = update_interval)) SERIAL_ECHOLNPGM("axis:pwm_scale" - #if HAS_STEALTHCHOP - "/curr_scale" - #endif - #if HAS_STALLGUARD - "/mech_load" - #endif + TERN_(HAS_STEALTHCHOP, "/curr_scale") + TERN_(HAS_STALLGUARD, "/mech_load") "|flags|warncount" ); } @@ -561,7 +550,7 @@ }; template - static void print_vsense(TMC &st) { SERIAL_ECHOPGM_P(st.vsense() ? PSTR("1=.18") : PSTR("0=.325")); } + static void print_vsense(TMC &st) { SERIAL_ECHOF(st.vsense() ? F("1=.18") : F("0=.325")); } #if HAS_DRIVER(TMC2130) || HAS_DRIVER(TMC5130) static void _tmc_status(TMC2130Stepper &st, const TMC_debug_enum i) { @@ -732,7 +721,7 @@ SERIAL_ECHO(st.cs()); SERIAL_ECHOPGM("/31"); break; - case TMC_VSENSE: SERIAL_ECHOPGM_P(st.vsense() ? PSTR("1=.165") : PSTR("0=.310")); break; + case TMC_VSENSE: SERIAL_ECHOF(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; @@ -1184,69 +1173,6 @@ #endif // USE_SENSORLESS -#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(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 - template static bool test_connection(TMC &st) { SERIAL_ECHOPGM("Testing "); @@ -1256,15 +1182,14 @@ static bool test_connection(TMC &st) { if (test_result > 0) SERIAL_ECHOPGM("Error: All "); - const char *stat; + FSTR_P stat; switch (test_result) { default: - case 0: stat = PSTR("OK"); break; - case 1: stat = PSTR("HIGH"); break; - case 2: stat = PSTR("LOW"); break; + case 0: stat = F("OK"); break; + case 1: stat = F("HIGH"); break; + case 2: stat = F("LOW"); break; } - SERIAL_ECHOPGM_P(stat); - SERIAL_EOL(); + SERIAL_ECHOLNF(stat); return test_result; } @@ -1342,7 +1267,70 @@ void test_tmc_connection(LOGICAL_AXIS_ARGS(const bool)) { #endif } - if (axis_connection) LCD_MESSAGEPGM(MSG_ERROR_TMC); + 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(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 1f7d5cf1a5432..c10bab62749d7 100644 --- a/Marlin/src/feature/tmc_util.h +++ b/Marlin/src/feature/tmc_util.h @@ -45,6 +45,12 @@ constexpr uint16_t _tmc_thrs(const uint16_t msteps, const uint32_t thrs, const u return 12650000UL * msteps / (256 * thrs * spmm); } +typedef struct { + uint8_t toff; + int8_t hend; + uint8_t hstrt; +} chopper_timing_t; + template class TMCStorage { protected: @@ -58,13 +64,13 @@ class TMCStorage { uint8_t otpw_count = 0, error_count = 0; bool flag_otpw = false; - inline bool getOTPW() { return flag_otpw; } - inline void clear_otpw() { flag_otpw = 0; } + bool getOTPW() { return flag_otpw; } + void clear_otpw() { flag_otpw = 0; } #endif - inline uint16_t getMilliamps() { return val_mA; } + uint16_t getMilliamps() { return val_mA; } - inline void printLabel() { + void printLabel() { SERIAL_CHAR(AXIS_LETTER); if (DRIVER_ID > '0') SERIAL_CHAR(DRIVER_ID); } @@ -91,55 +97,61 @@ class TMCMarlin : public TMC, public TMCStorage { 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) {} - inline uint16_t rms_current() { return TMC::rms_current(); } - inline void rms_current(uint16_t mA) { + uint16_t rms_current() { return TMC::rms_current(); } + void rms_current(uint16_t mA) { this->val_mA = mA; TMC::rms_current(mA); } - inline void rms_current(const uint16_t mA, const float mult) { + void rms_current(const uint16_t mA, const float mult) { this->val_mA = mA; TMC::rms_current(mA, mult); } - inline uint16_t get_microstep_counter() { return TMC::MSCNT(); } + uint16_t get_microstep_counter() { return TMC::MSCNT(); } #if HAS_STEALTHCHOP - inline bool get_stealthChop() { return this->en_pwm_mode(); } - inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } - inline void refresh_stepping_mode() { this->en_pwm_mode(this->stored.stealthChop_enabled); } - inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); } - inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_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) { + TMC::toff(ct.toff); + TMC::hysteresis_end(ct.hend); + TMC::hysteresis_start(ct.hstrt); + } + #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { return _tmc_thrs(this->microsteps(), this->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])); - TERN_(HAS_LCD_MENU, this->stored.hybrid_thrs = thrs); + TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs); } #endif #if USE_SENSORLESS - inline int16_t homing_threshold() { return TMC::sgt(); } + int16_t homing_threshold() { return TMC::sgt(); } void homing_threshold(int16_t sgt_val) { sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max); TMC::sgt(sgt_val); - TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val); + TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val); } #if ENABLED(SPI_ENDSTOPS) bool test_stall_status(); #endif #endif - #if HAS_LCD_MENU - inline void refresh_stepper_current() { rms_current(this->val_mA); } + #if HAS_MARLINUI_MENU + void refresh_stepper_current() { rms_current(this->val_mA); } #if ENABLED(HYBRID_THRESHOLD) - inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } + void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } #endif #if USE_SENSORLESS - inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } + void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } #endif #endif @@ -161,39 +173,45 @@ class TMCMarlin : public TMC220 {} uint16_t rms_current() { return TMC2208Stepper::rms_current(); } - inline void rms_current(const uint16_t mA) { + void rms_current(const uint16_t mA) { this->val_mA = mA; TMC2208Stepper::rms_current(mA); } - inline void rms_current(const uint16_t mA, const float mult) { + void rms_current(const uint16_t mA, const float mult) { this->val_mA = mA; TMC2208Stepper::rms_current(mA, mult); } - inline uint16_t get_microstep_counter() { return TMC2208Stepper::MSCNT(); } + uint16_t get_microstep_counter() { return TMC2208Stepper::MSCNT(); } #if HAS_STEALTHCHOP - inline bool get_stealthChop() { return !this->en_spreadCycle(); } - inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } - inline void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); } - inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); } - inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); } + bool get_stealthChop() { return !this->en_spreadCycle(); } + bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } + void refresh_stepping_mode() { this->en_spreadCycle(!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) { + TMC2208Stepper::toff(ct.toff); + TMC2208Stepper::hysteresis_end(ct.hend); + TMC2208Stepper::hysteresis_start(ct.hstrt); + } + #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { return _tmc_thrs(this->microsteps(), this->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])); - TERN_(HAS_LCD_MENU, this->stored.hybrid_thrs = thrs); + TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs); } #endif - #if HAS_LCD_MENU - inline void refresh_stepper_current() { rms_current(this->val_mA); } + #if HAS_MARLINUI_MENU + void refresh_stepper_current() { rms_current(this->val_mA); } #if ENABLED(HYBRID_THRESHOLD) - inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } + void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } #endif #endif }; @@ -209,50 +227,56 @@ class TMCMarlin : public TMC220 {} uint8_t get_address() { return slave_address; } uint16_t rms_current() { return TMC2209Stepper::rms_current(); } - inline void rms_current(const uint16_t mA) { + void rms_current(const uint16_t mA) { this->val_mA = mA; TMC2209Stepper::rms_current(mA); } - inline void rms_current(const uint16_t mA, const float mult) { + void rms_current(const uint16_t mA, const float mult) { this->val_mA = mA; TMC2209Stepper::rms_current(mA, mult); } - inline uint16_t get_microstep_counter() { return TMC2209Stepper::MSCNT(); } + uint16_t get_microstep_counter() { return TMC2209Stepper::MSCNT(); } #if HAS_STEALTHCHOP - inline bool get_stealthChop() { return !this->en_spreadCycle(); } - inline bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } - inline void refresh_stepping_mode() { this->en_spreadCycle(!this->stored.stealthChop_enabled); } - inline void set_stealthChop(const bool stch) { this->stored.stealthChop_enabled = stch; refresh_stepping_mode(); } - inline bool toggle_stepping_mode() { set_stealthChop(!this->stored.stealthChop_enabled); return get_stealthChop(); } + bool get_stealthChop() { return !this->en_spreadCycle(); } + bool get_stored_stealthChop() { return this->stored.stealthChop_enabled; } + void refresh_stepping_mode() { this->en_spreadCycle(!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) { + TMC2209Stepper::toff(ct.toff); + TMC2209Stepper::hysteresis_end(ct.hend); + TMC2209Stepper::hysteresis_start(ct.hstrt); + } + #if ENABLED(HYBRID_THRESHOLD) uint32_t get_pwm_thrs() { return _tmc_thrs(this->microsteps(), this->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_LCD_MENU, this->stored.hybrid_thrs = thrs); + TERN_(HAS_MARLINUI_MENU, this->stored.hybrid_thrs = thrs); } #endif #if USE_SENSORLESS - inline int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); } + int16_t homing_threshold() { return TMC2209Stepper::SGTHRS(); } void homing_threshold(int16_t sgt_val) { sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max); TMC2209Stepper::SGTHRS(sgt_val); - TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val); + TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val); } #endif - #if HAS_LCD_MENU - inline void refresh_stepper_current() { rms_current(this->val_mA); } + #if HAS_MARLINUI_MENU + void refresh_stepper_current() { rms_current(this->val_mA); } #if ENABLED(HYBRID_THRESHOLD) - inline void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } + void refresh_hybrid_thrs() { set_pwm_thrs(this->stored.hybrid_thrs); } #endif #if USE_SENSORLESS - inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } + void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } #endif #endif @@ -269,27 +293,33 @@ class TMCMarlin : public TMC266 TMCMarlin(const uint16_t CS, const float RS, const uint16_t pinMOSI, const uint16_t pinMISO, const uint16_t pinSCK, const uint8_t) : TMC2660Stepper(CS, RS, pinMOSI, pinMISO, pinSCK) {} - inline uint16_t rms_current() { return TMC2660Stepper::rms_current(); } - inline void rms_current(const uint16_t mA) { + uint16_t rms_current() { return TMC2660Stepper::rms_current(); } + void rms_current(const uint16_t mA) { this->val_mA = mA; TMC2660Stepper::rms_current(mA); } - inline uint16_t get_microstep_counter() { return TMC2660Stepper::mstep(); } + uint16_t get_microstep_counter() { return TMC2660Stepper::mstep(); } + + void set_chopper_times(const chopper_timing_t &ct) { + TMC2660Stepper::toff(ct.toff); + TMC2660Stepper::hysteresis_end(ct.hend); + TMC2660Stepper::hysteresis_start(ct.hstrt); + } #if USE_SENSORLESS - inline int16_t homing_threshold() { return TMC2660Stepper::sgt(); } + int16_t homing_threshold() { return TMC2660Stepper::sgt(); } void homing_threshold(int16_t sgt_val) { sgt_val = (int16_t)constrain(sgt_val, sgt_min, sgt_max); TMC2660Stepper::sgt(sgt_val); - TERN_(HAS_LCD_MENU, this->stored.homing_thrs = sgt_val); + TERN_(HAS_MARLINUI_MENU, this->stored.homing_thrs = sgt_val); } #endif - #if HAS_LCD_MENU - inline void refresh_stepper_current() { rms_current(this->val_mA); } + #if HAS_MARLINUI_MENU + void refresh_stepper_current() { rms_current(this->val_mA); } #if USE_SENSORLESS - inline void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } + void refresh_homing_thrs() { homing_threshold(this->stored.homing_thrs); } #endif #endif @@ -297,43 +327,6 @@ class TMCMarlin : public TMC266 sgt_max = 63; }; -template -void tmc_print_current(TMC &st) { - st.printLabel(); - SERIAL_ECHOLNPGM(" driver current: ", st.getMilliamps()); -} - -#if ENABLED(MONITOR_DRIVER_STATUS) - template - void tmc_report_otpw(TMC &st) { - st.printLabel(); - SERIAL_ECHOPGM(" temperature prewarn triggered: "); - serialprint_truefalse(st.getOTPW()); - SERIAL_EOL(); - } - template - void tmc_clear_otpw(TMC &st) { - st.clear_otpw(); - st.printLabel(); - SERIAL_ECHOLNPGM(" prewarn flag cleared"); - } -#endif -#if ENABLED(HYBRID_THRESHOLD) - template - void tmc_print_pwmthrs(TMC &st) { - st.printLabel(); - SERIAL_ECHOLNPGM(" stealthChop max speed: ", st.get_pwm_thrs()); - } -#endif -#if USE_SENSORLESS - template - void tmc_print_sgt(TMC &st) { - st.printLabel(); - SERIAL_ECHOPGM(" homing sensitivity: "); - SERIAL_PRINTLN(st.homing_threshold(), PrintBase::Dec); - } -#endif - void monitor_tmc_drivers(); void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); @@ -355,7 +348,7 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); #if USE_SENSORLESS // Track enabled status of stealthChop and only re-enable where applicable - struct sensorless_t { bool LINEAR_AXIS_ARGS(), x2, y2, z2, z3, z4; }; + struct sensorless_t { bool NUM_AXIS_ARGS(), x2, y2, z2, z3, z4; }; #if ENABLED(IMPROVE_HOMING_RELIABILITY) extern millis_t sg_guard_period; @@ -389,8 +382,8 @@ void test_tmc_connection(LOGICAL_AXIS_DECL(const bool, true)); #endif // USE_SENSORLESS +#endif // HAS_TRINAMIC_CONFIG + #if HAS_TMC_SPI void tmc_init_cs_pins(); #endif - -#endif // HAS_TRINAMIC_CONFIG diff --git a/Marlin/src/feature/twibus.cpp b/Marlin/src/feature/twibus.cpp index 5f5209cdd4802..9aec6b030537f 100644 --- a/Marlin/src/feature/twibus.cpp +++ b/Marlin/src/feature/twibus.cpp @@ -28,13 +28,24 @@ #include +#include "../libs/hex_print.h" + TWIBus i2c; TWIBus::TWIBus() { #if I2C_SLAVE_ADDRESS == 0 - Wire.begin(); // No address joins the BUS as the master + + #if PINS_EXIST(I2C_SCL, I2C_SDA) && DISABLED(SOFT_I2C_EEPROM) + Wire.setSDA(pin_t(I2C_SDA_PIN)); + Wire.setSCL(pin_t(I2C_SCL_PIN)); + #endif + + Wire.begin(); // No address joins the BUS as the master + #else - Wire.begin(I2C_SLAVE_ADDRESS); // Join the bus as a slave + + Wire.begin(I2C_SLAVE_ADDRESS); // Join the bus as a slave + #endif reset(); } @@ -45,33 +56,32 @@ void TWIBus::reset() { } void TWIBus::address(const uint8_t adr) { - if (!WITHIN(adr, 8, 127)) { + if (!WITHIN(adr, 8, 127)) SERIAL_ECHO_MSG("Bad I2C address (8-127)"); - } addr = adr; - debug(PSTR("address"), adr); + debug(F("address"), adr); } void TWIBus::addbyte(const char c) { if (buffer_s >= COUNT(buffer)) return; buffer[buffer_s++] = c; - debug(PSTR("addbyte"), c); + debug(F("addbyte"), c); } void TWIBus::addbytes(char src[], uint8_t bytes) { - debug(PSTR("addbytes"), bytes); + debug(F("addbytes"), bytes); while (bytes--) addbyte(*src++); } void TWIBus::addstring(char str[]) { - debug(PSTR("addstring"), str); + debug(F("addstring"), str); while (char c = *str++) addbyte(c); } void TWIBus::send() { - debug(PSTR("send"), addr); + debug(F("send"), addr); Wire.beginTransmission(I2C_ADDRESS(addr)); Wire.write(buffer, buffer_s); @@ -81,21 +91,60 @@ void TWIBus::send() { } // static -void TWIBus::echoprefix(uint8_t bytes, const char pref[], uint8_t adr) { +void TWIBus::echoprefix(uint8_t bytes, FSTR_P const pref, uint8_t adr) { SERIAL_ECHO_START(); - SERIAL_ECHOPGM_P(pref); + SERIAL_ECHOF(pref); SERIAL_ECHOPGM(": from:", adr, " bytes:", bytes, " data:"); } // static -void TWIBus::echodata(uint8_t bytes, const char pref[], uint8_t adr) { +void TWIBus::echodata(uint8_t bytes, FSTR_P const pref, uint8_t adr, const uint8_t style/*=0*/) { + union TwoBytesToInt16 { uint8_t bytes[2]; int16_t integervalue; }; + TwoBytesToInt16 ConversionUnion; + echoprefix(bytes, pref, adr); - while (bytes-- && Wire.available()) SERIAL_CHAR(Wire.read()); + + while (bytes-- && Wire.available()) { + int value = Wire.read(); + switch (style) { + + // Style 1, HEX DUMP + case 1: + SERIAL_CHAR(hex_nybble((value & 0xF0) >> 4)); + SERIAL_CHAR(hex_nybble(value & 0x0F)); + if (bytes) SERIAL_CHAR(' '); + break; + + // Style 2, signed two byte integer (int16) + case 2: + if (bytes == 1) + ConversionUnion.bytes[1] = (uint8_t)value; + else if (bytes == 0) { + ConversionUnion.bytes[0] = (uint8_t)value; + // Output value in base 10 (standard decimal) + SERIAL_ECHO(ConversionUnion.integervalue); + } + break; + + // Style 3, unsigned byte, base 10 (uint8) + case 3: + SERIAL_ECHO(value); + if (bytes) SERIAL_CHAR(' '); + break; + + // Default style (zero), raw serial output + default: + // This can cause issues with some serial consoles, Pronterface is an example where things go wrong + SERIAL_CHAR(value); + break; + } + } + SERIAL_EOL(); } -void TWIBus::echobuffer(const char pref[], uint8_t adr) { - echoprefix(buffer_s, pref, adr); +void TWIBus::echobuffer(FSTR_P const prefix, uint8_t adr) { + echoprefix(buffer_s, prefix, adr); LOOP_L_N(i, buffer_s) SERIAL_CHAR(buffer[i]); SERIAL_EOL(); } @@ -103,22 +152,22 @@ void TWIBus::echobuffer(const char pref[], uint8_t adr) { bool TWIBus::request(const uint8_t bytes) { if (!addr) return false; - debug(PSTR("request"), bytes); + debug(F("request"), bytes); // requestFrom() is a blocking function if (Wire.requestFrom(I2C_ADDRESS(addr), bytes) == 0) { - debug("request fail", I2C_ADDRESS(addr)); + debug(F("request fail"), I2C_ADDRESS(addr)); return false; } return true; } -void TWIBus::relay(const uint8_t bytes) { - debug(PSTR("relay"), bytes); +void TWIBus::relay(const uint8_t bytes, const uint8_t style/*=0*/) { + debug(F("relay"), bytes); if (request(bytes)) - echodata(bytes, PSTR("i2c-reply"), addr); + echodata(bytes, F("i2c-reply"), addr, style); } uint8_t TWIBus::capture(char *dst, const uint8_t bytes) { @@ -127,7 +176,7 @@ uint8_t TWIBus::capture(char *dst, const uint8_t bytes) { while (count < bytes && Wire.available()) dst[count++] = Wire.read(); - debug(PSTR("capture"), count); + debug(F("capture"), count); return count; } @@ -140,12 +189,12 @@ void TWIBus::flush() { #if I2C_SLAVE_ADDRESS > 0 void TWIBus::receive(uint8_t bytes) { - debug(PSTR("receive"), bytes); - echodata(bytes, PSTR("i2c-receive"), 0); + debug(F("receive"), bytes); + echodata(bytes, F("i2c-receive"), 0); } void TWIBus::reply(char str[]/*=nullptr*/) { - debug(PSTR("reply"), str); + debug(F("reply"), str); if (str) { reset(); @@ -170,18 +219,16 @@ void TWIBus::flush() { #if ENABLED(DEBUG_TWIBUS) // static - void TWIBus::prefix(const char func[]) { - SERIAL_ECHOPGM("TWIBus::"); - SERIAL_ECHOPGM_P(func); - SERIAL_ECHOPGM(": "); + void TWIBus::prefix(FSTR_P const func) { + SERIAL_ECHOPGM("TWIBus::", func, ": "); } - void TWIBus::debug(const char func[], uint32_t adr) { + void TWIBus::debug(FSTR_P const func, uint32_t adr) { if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(adr); } } - void TWIBus::debug(const char func[], char c) { + void TWIBus::debug(FSTR_P const func, char c) { if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(c); } } - void TWIBus::debug(const char func[], char str[]) { + void TWIBus::debug(FSTR_P const func, char str[]) { if (DEBUGGING(INFO)) { prefix(func); SERIAL_ECHOLN(str); } } diff --git a/Marlin/src/feature/twibus.h b/Marlin/src/feature/twibus.h index 59391534824cd..806e2a147a7d3 100644 --- a/Marlin/src/feature/twibus.h +++ b/Marlin/src/feature/twibus.h @@ -142,7 +142,7 @@ class TWIBus { * * @param bytes the number of bytes to request */ - static void echoprefix(uint8_t bytes, const char prefix[], uint8_t adr); + static void echoprefix(uint8_t bytes, FSTR_P const prefix, uint8_t adr); /** * @brief Echo data on the bus to serial @@ -150,8 +150,9 @@ class TWIBus { * to serial in a parser-friendly format. * * @param bytes the number of bytes to request + * @param style Output format for the bytes, 0 = Raw byte [default], 1 = Hex characters, 2 = uint16_t */ - static void echodata(uint8_t bytes, const char prefix[], uint8_t adr); + static void echodata(uint8_t bytes, FSTR_P const prefix, uint8_t adr, const uint8_t style=0); /** * @brief Echo data in the buffer to serial @@ -160,7 +161,7 @@ class TWIBus { * * @param bytes the number of bytes to request */ - void echobuffer(const char prefix[], uint8_t adr); + void echobuffer(FSTR_P const prefix, uint8_t adr); /** * @brief Request data from the slave device and wait. @@ -192,10 +193,11 @@ class TWIBus { * @brief Request data from the slave device, echo to serial. * @details Request a number of bytes from a slave device and output * the returned data to serial in a parser-friendly format. + * @style Output format for the bytes, 0 = raw byte [default], 1 = Hex characters, 2 = uint16_t * * @param bytes the number of bytes to request */ - void relay(const uint8_t bytes); + void relay(const uint8_t bytes, const uint8_t style=0); #if I2C_SLAVE_ADDRESS > 0 @@ -237,17 +239,16 @@ class TWIBus { * @brief Prints a debug message * @details Prints a simple debug message "TWIBus::function: value" */ - static void prefix(const char func[]); - static void debug(const char func[], uint32_t adr); - static void debug(const char func[], char c); - static void debug(const char func[], char adr[]); - static inline void debug(const char func[], uint8_t v) { debug(func, (uint32_t)v); } + static void prefix(FSTR_P const func); + static void debug(FSTR_P const func, uint32_t adr); + static void debug(FSTR_P const func, char c); + static void debug(FSTR_P const func, char adr[]); #else - static inline void debug(const char[], uint32_t) {} - static inline void debug(const char[], char) {} - static inline void debug(const char[], char[]) {} - static inline void debug(const char[], uint8_t) {} + static void debug(FSTR_P const, uint32_t) {} + static void debug(FSTR_P const, char) {} + static void debug(FSTR_P const, char[]) {} #endif + static void debug(FSTR_P const func, uint8_t v) { debug(func, (uint32_t)v); } }; extern TWIBus i2c; diff --git a/Marlin/src/feature/x_twist.cpp b/Marlin/src/feature/x_twist.cpp new file mode 100644 index 0000000000000..b5ad25cba87d7 --- /dev/null +++ b/Marlin/src/feature/x_twist.cpp @@ -0,0 +1,67 @@ +/** + * 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 . + * + */ +#include "../inc/MarlinConfig.h" + +#if ENABLED(X_AXIS_TWIST_COMPENSATION) + +#include "x_twist.h" +#include "../module/probe.h" + +XATC xatc; + +bool XATC::enabled; +float XATC::spacing, XATC::start; +xatc_array_t XATC::z_offset; // Initialized by settings.load() + +void XATC::reset() { + constexpr float xzo[] = XATC_Z_OFFSETS; + static_assert(COUNT(xzo) == XATC_MAX_POINTS, "XATC_Z_OFFSETS is the wrong size."); + COPY(z_offset, xzo); + start = probe.min_x(); + spacing = (probe.max_x() - start) / (XATC_MAX_POINTS - 1); + enabled = true; +} + +void XATC::print_points() { + SERIAL_ECHOLNPGM(" X-Twist Correction:"); + LOOP_L_N(x, XATC_MAX_POINTS) { + SERIAL_CHAR(' '); + if (!isnan(z_offset[x])) + serial_offset(z_offset[x]); + else + LOOP_L_N(i, 6) SERIAL_CHAR(i ? '=' : ' '); + } + SERIAL_EOL(); +} + +float lerp(const_float_t t, const_float_t a, const_float_t b) { return a + t * (b - a); } + +float XATC::compensation(const xy_pos_t &raw) { + if (!enabled) return 0; + if (NEAR_ZERO(spacing)) return 0; + float t = (raw.x - start) / spacing; + const int i = constrain(FLOOR(t), 0, XATC_MAX_POINTS - 2); + t -= i; + return lerp(t, z_offset[i], z_offset[i + 1]); +} + +#endif // X_AXIS_TWIST_COMPENSATION diff --git a/Marlin/src/feature/x_twist.h b/Marlin/src/feature/x_twist.h new file mode 100644 index 0000000000000..6a2ff279013a6 --- /dev/null +++ b/Marlin/src/feature/x_twist.h @@ -0,0 +1,40 @@ +/** + * 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 + +#include "../inc/MarlinConfigPre.h" + +typedef float xatc_array_t[XATC_MAX_POINTS]; + +class XATC { + static bool enabled; +public: + static float spacing, start; + static xatc_array_t z_offset; + + static void reset(); + static void set_enabled(const bool ena) { enabled = ena; } + static float compensation(const xy_pos_t &raw); + static void print_points(); +}; + +extern XATC xatc; diff --git a/Marlin/src/feature/z_stepper_align.cpp b/Marlin/src/feature/z_stepper_align.cpp index 1b4eb44749036..9dba21d8219d8 100644 --- a/Marlin/src/feature/z_stepper_align.cpp +++ b/Marlin/src/feature/z_stepper_align.cpp @@ -33,35 +33,35 @@ ZStepperAlign z_stepper_align; -xy_pos_t ZStepperAlign::xy[NUM_Z_STEPPER_DRIVERS]; +xy_pos_t ZStepperAlign::xy[NUM_Z_STEPPERS]; -#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - xy_pos_t ZStepperAlign::stepper_xy[NUM_Z_STEPPER_DRIVERS]; +#if HAS_Z_STEPPER_ALIGN_STEPPER_XY + xy_pos_t ZStepperAlign::stepper_xy[NUM_Z_STEPPERS]; #endif void ZStepperAlign::reset_to_default() { #ifdef Z_STEPPER_ALIGN_XY constexpr xy_pos_t xy_init[] = Z_STEPPER_ALIGN_XY; - static_assert(COUNT(xy_init) == NUM_Z_STEPPER_DRIVERS, + static_assert(COUNT(xy_init) == NUM_Z_STEPPERS, "Z_STEPPER_ALIGN_XY requires " - #if NUM_Z_STEPPER_DRIVERS == 4 + #if NUM_Z_STEPPERS == 4 "four {X,Y} entries (Z, Z2, Z3, and Z4)." - #elif NUM_Z_STEPPER_DRIVERS == 3 + #elif NUM_Z_STEPPERS == 3 "three {X,Y} entries (Z, Z2, and Z3)." #else "two {X,Y} entries (Z and Z2)." #endif ); - #define VALIDATE_ALIGN_POINT(N) static_assert(N >= NUM_Z_STEPPER_DRIVERS || Probe::build_time::can_reach(xy_init[N]), \ + #define VALIDATE_ALIGN_POINT(N) static_assert(N >= NUM_Z_STEPPERS || Probe::build_time::can_reach(xy_init[N]), \ "Z_STEPPER_ALIGN_XY point " STRINGIFY(N) " is not reachable with the default NOZZLE_TO_PROBE offset and PROBING_MARGIN.") VALIDATE_ALIGN_POINT(0); VALIDATE_ALIGN_POINT(1); VALIDATE_ALIGN_POINT(2); VALIDATE_ALIGN_POINT(3); #else // !Z_STEPPER_ALIGN_XY const xy_pos_t xy_init[] = { - #if NUM_Z_STEPPER_DRIVERS >= 3 // First probe point... + #if NUM_Z_STEPPERS >= 3 // First probe point... #if !Z_STEPPERS_ORIENTATION { probe.min_x(), probe.min_y() }, // SW #elif Z_STEPPERS_ORIENTATION == 1 @@ -73,7 +73,7 @@ void ZStepperAlign::reset_to_default() { #else #error "Z_STEPPERS_ORIENTATION must be from 0 to 3 (first point SW, NW, NE, SE)." #endif - #if NUM_Z_STEPPER_DRIVERS == 4 // 3 more points... + #if NUM_Z_STEPPERS == 4 // 3 more points... #if !Z_STEPPERS_ORIENTATION { probe.min_x(), probe.max_y() }, { probe.max_x(), probe.max_y() }, { probe.max_x(), probe.min_y() } // SW #elif Z_STEPPERS_ORIENTATION == 1 @@ -103,14 +103,14 @@ void ZStepperAlign::reset_to_default() { COPY(xy, xy_init); - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY constexpr xy_pos_t stepper_xy_init[] = Z_STEPPER_ALIGN_STEPPER_XY; static_assert( - COUNT(stepper_xy_init) == NUM_Z_STEPPER_DRIVERS, + COUNT(stepper_xy_init) == NUM_Z_STEPPERS, "Z_STEPPER_ALIGN_STEPPER_XY requires " - #if NUM_Z_STEPPER_DRIVERS == 4 + #if NUM_Z_STEPPERS == 4 "four {X,Y} entries (Z, Z2, Z3, and Z4)." - #elif NUM_Z_STEPPER_DRIVERS == 3 + #elif NUM_Z_STEPPERS == 3 "three {X,Y} entries (Z, Z2, and Z3)." #endif ); diff --git a/Marlin/src/feature/z_stepper_align.h b/Marlin/src/feature/z_stepper_align.h index e1b235b52cb32..f3f9abb845b9c 100644 --- a/Marlin/src/feature/z_stepper_align.h +++ b/Marlin/src/feature/z_stepper_align.h @@ -29,10 +29,10 @@ class ZStepperAlign { public: - static xy_pos_t xy[NUM_Z_STEPPER_DRIVERS]; + static xy_pos_t xy[NUM_Z_STEPPERS]; - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - static xy_pos_t stepper_xy[NUM_Z_STEPPER_DRIVERS]; + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY + static xy_pos_t stepper_xy[NUM_Z_STEPPERS]; #endif static void reset_to_default(); diff --git a/Marlin/src/gcode/bedlevel/G26.cpp b/Marlin/src/gcode/bedlevel/G26.cpp index ba14e6f0b4841..aa6e0c1f0c54a 100644 --- a/Marlin/src/gcode/bedlevel/G26.cpp +++ b/Marlin/src/gcode/bedlevel/G26.cpp @@ -71,8 +71,8 @@ * pliers while holding the LCD Click wheel in a depressed state. If you do not have * an LCD, you must specify a value if you use P. * - * Q # Multiplier Retraction Multiplier. Normally not needed. Retraction defaults to 1.0mm and - * un-retraction is at 1.2mm These numbers will be scaled by the specified amount + * Q # Multiplier Retraction Multiplier. (Normally not needed.) During G26 retraction will use the length + * specified by this parameter (1mm by default). Recover will be 1.2x the retract distance. * * R # Repeat Prints the number of patterns given as a parameter, starting at the current location. * If a parameter isn't given, every point will be printed unless G26 is interrupted. @@ -107,7 +107,6 @@ #include "../../MarlinCore.h" #include "../../module/planner.h" -#include "../../module/stepper.h" #include "../../module/motion.h" #include "../../module/tool_change.h" #include "../../module/temperature.h" @@ -156,15 +155,15 @@ constexpr float g26_e_axis_feedrate = 0.025; static MeshFlags circle_flags; float g26_random_deviation = 0.0; -#if HAS_LCD_MENU +#if HAS_MARLINUI_MENU /** * If the LCD is clicked, cancel, wait for release, return true */ bool user_canceled() { if (!ui.button_pressed()) return false; // Return if the button isn't pressed - ui.set_status_P(GET_TEXT(MSG_G26_CANCELED), 99); - TERN_(HAS_LCD_MENU, ui.quick_feedback()); + ui.set_status(GET_TEXT_F(MSG_G26_CANCELED), 99); + TERN_(HAS_MARLINUI_MENU, ui.quick_feedback()); ui.wait_for_release(); return true; } @@ -293,10 +292,10 @@ typedef struct { if (circle_flags.marked(p1.x, p1.y) && circle_flags.marked(p2.x, p2.y)) { xyz_pos_t s, e; - s.x = _GET_MESH_X(p1.x) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx; - e.x = _GET_MESH_X(p2.x) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx; - s.y = _GET_MESH_Y(p1.y) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy; - e.y = _GET_MESH_Y(p2.y) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy; + s.x = bedlevel.get_mesh_x(p1.x) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx; + e.x = bedlevel.get_mesh_x(p2.x) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dx; + s.y = bedlevel.get_mesh_y(p1.y) + (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy; + e.y = bedlevel.get_mesh_y(p2.y) - (INTERSECTION_CIRCLE_RADIUS - (CROSSHAIRS_SIZE)) * dy; s.z = e.z = layer_height; #if HAS_ENDSTOPS @@ -306,7 +305,7 @@ typedef struct { LIMIT(e.x, X_MIN_POS + 1, X_MAX_POS - 1); #endif - if (position_is_reachable(s.x, s.y) && position_is_reachable(e.x, e.y)) + if (position_is_reachable(s) && position_is_reachable(e)) print_line_from_here_to_there(s, e); } } @@ -323,9 +322,9 @@ typedef struct { if (bed_temp > 25) { #if HAS_WIRED_LCD - ui.set_status_P(GET_TEXT(MSG_G26_HEATING_BED), 99); + ui.set_status(GET_TEXT_F(MSG_G26_HEATING_BED), 99); ui.quick_feedback(); - TERN_(HAS_LCD_MENU, ui.capture()); + TERN_(HAS_MARLINUI_MENU, ui.capture()); #endif thermalManager.setTargetBed(bed_temp); @@ -342,7 +341,7 @@ typedef struct { // Start heating the active nozzle #if HAS_WIRED_LCD - ui.set_status_P(GET_TEXT(MSG_G26_HEATING_NOZZLE), 99); + ui.set_status(GET_TEXT_F(MSG_G26_HEATING_NOZZLE), 99); ui.quick_feedback(); #endif thermalManager.setTargetHotend(hotend_temp, active_extruder); @@ -365,14 +364,14 @@ typedef struct { bool prime_nozzle() { const feedRate_t fr_slow_e = planner.settings.max_feedrate_mm_s[E_AXIS] / 15.0f; - #if HAS_LCD_MENU && !HAS_TOUCH_BUTTONS // ui.button_pressed issue with touchscreen + #if HAS_MARLINUI_MENU && !HAS_TOUCH_BUTTONS // ui.button_pressed issue with touchscreen #if ENABLED(PREVENT_LENGTHY_EXTRUDE) float Total_Prime = 0.0; #endif if (prime_flag == -1) { // The user wants to control how much filament gets purged ui.capture(); - ui.set_status_P(GET_TEXT(MSG_G26_MANUAL_PRIME), 99); + ui.set_status(GET_TEXT_F(MSG_G26_MANUAL_PRIME), 99); ui.chirp(); destination = current_position; @@ -399,7 +398,7 @@ typedef struct { ui.wait_for_release(); - ui.set_status_P(GET_TEXT(MSG_G26_PRIME_DONE), 99); + ui.set_status(GET_TEXT_F(MSG_G26_PRIME_DONE), 99); ui.quick_feedback(); ui.release(); } @@ -407,7 +406,7 @@ typedef struct { #endif { #if HAS_WIRED_LCD - ui.set_status_P(GET_TEXT(MSG_G26_FIXED_LENGTH), 99); + ui.set_status(GET_TEXT_F(MSG_G26_FIXED_LENGTH), 99); ui.quick_feedback(); #endif destination = current_position; @@ -448,7 +447,7 @@ typedef struct { GRID_LOOP(i, j) { if (!circle_flags.marked(i, j)) { // We found a circle that needs to be printed - const xy_pos_t m = { _GET_MESH_X(i), _GET_MESH_Y(j) }; + const xy_pos_t m = { bedlevel.get_mesh_x(i), bedlevel.get_mesh_y(j) }; // Get the distance to this intersection float f = (pos - m).magnitude(); @@ -520,7 +519,7 @@ void GcodeSuite::G26() { g26.keep_heaters_on = parser.boolval('K'); // Accept 'I' if temperature presets are defined - #if PREHEAT_COUNT + #if HAS_PREHEAT const uint8_t preset_index = parser.seenval('I') ? _MIN(parser.value_byte(), PREHEAT_COUNT - 1) + 1 : 0; #endif @@ -530,7 +529,7 @@ void GcodeSuite::G26() { celsius_t bedtemp = 0; // Use the 'I' index if temperature presets are defined - #if PREHEAT_COUNT + #if HAS_PREHEAT if (preset_index) bedtemp = ui.material_preset[preset_index - 1].bed_temp; #endif @@ -579,7 +578,7 @@ void GcodeSuite::G26() { if (parser.seen('P')) { if (!parser.has_value()) { - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU g26.prime_flag = -1; #else SERIAL_ECHOLNPGM("?Prime length must be specified when not using an LCD."); @@ -613,7 +612,7 @@ void GcodeSuite::G26() { celsius_t noztemp = 0; // Accept 'I' if temperature presets are defined - #if PREHEAT_COUNT + #if HAS_PREHEAT if (preset_index) noztemp = ui.material_preset[preset_index - 1].hotend_temp; #endif @@ -638,7 +637,7 @@ void GcodeSuite::G26() { // Get repeat from 'R', otherwise do one full circuit int16_t g26_repeats; - #if HAS_LCD_MENU + #if HAS_MARLINUI_MENU g26_repeats = parser.intval('R', GRID_MAX_POINTS + 1); #else if (parser.seen('R')) @@ -699,7 +698,7 @@ void GcodeSuite::G26() { move_to(destination, 0.0); move_to(destination, g26.ooze_amount); - TERN_(HAS_LCD_MENU, ui.capture()); + TERN_(HAS_MARLINUI_MENU, ui.capture()); #if DISABLED(ARC_SUPPORT) @@ -729,7 +728,7 @@ void GcodeSuite::G26() { if (location.valid()) { TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_START)); - const xy_pos_t circle = _GET_MESH_POS(location.pos); + const xy_pos_t circle = { bedlevel.get_mesh_x(location.pos.a), bedlevel.get_mesh_y(location.pos.b) }; // If this mesh location is outside the printable radius, skip it. if (!position_is_reachable(circle)) continue; @@ -738,8 +737,8 @@ void GcodeSuite::G26() { // which is always drawn counter-clockwise. const xy_int8_t st = location; const bool f = st.y == 0, - r = st.x >= GRID_MAX_POINTS_X - 1, - b = st.y >= GRID_MAX_POINTS_Y - 1; + r = st.x >= (GRID_MAX_POINTS_X) - 1, + b = st.y >= (GRID_MAX_POINTS_Y) - 1; #if ENABLED(ARC_SUPPORT) @@ -795,7 +794,7 @@ void GcodeSuite::G26() { destination = current_position; } - if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation + if (TERN0(HAS_MARLINUI_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation #else // !ARC_SUPPORT @@ -819,7 +818,7 @@ void GcodeSuite::G26() { for (int8_t ind = start_ind; ind <= end_ind; ind++) { - if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation + if (TERN0(HAS_MARLINUI_MENU, user_canceled())) goto LEAVE; // Check if the user wants to stop the Mesh Validation xyz_float_t p = { circle.x + _COS(ind ), circle.y + _SIN(ind ), g26.layer_height }, q = { circle.x + _COS(ind + 1), circle.y + _SIN(ind + 1), g26.layer_height }; @@ -846,7 +845,7 @@ void GcodeSuite::G26() { g26.connect_neighbor_with_line(location.pos, 0, 1); planner.synchronize(); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location.pos, ExtUI::G26_POINT_FINISH)); - if (TERN0(HAS_LCD_MENU, user_canceled())) goto LEAVE; + if (TERN0(HAS_MARLINUI_MENU, user_canceled())) goto LEAVE; } SERIAL_FLUSH(); // Prevent host M105 buffer overrun. @@ -854,7 +853,7 @@ void GcodeSuite::G26() { } while (--g26_repeats && location.valid()); LEAVE: - ui.set_status_P(GET_TEXT(MSG_G26_LEAVING), -1); + ui.set_status(GET_TEXT_F(MSG_G26_LEAVING), -1); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(location, ExtUI::G26_FINISH)); g26.retract_filament(destination); @@ -866,7 +865,7 @@ void GcodeSuite::G26() { planner.calculate_volumetric_multipliers(); #endif - TERN_(HAS_LCD_MENU, ui.release()); // Give back control of the LCD + TERN_(HAS_MARLINUI_MENU, ui.release()); // Give back control of the LCD if (!g26.keep_heaters_on) { TERN_(HAS_HEATED_BED, thermalManager.setTargetBed(0)); diff --git a/Marlin/src/gcode/bedlevel/G35.cpp b/Marlin/src/gcode/bedlevel/G35.cpp index 8d5c0573617c7..dd828bf0c8730 100644 --- a/Marlin/src/gcode/bedlevel/G35.cpp +++ b/Marlin/src/gcode/bedlevel/G35.cpp @@ -33,6 +33,10 @@ #include "../../module/tool_change.h" #endif +#if ENABLED(BLTOUCH) + #include "../../feature/bltouch.h" +#endif + #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) #include "../../core/debug_out.h" @@ -92,7 +96,7 @@ void GcodeSuite::G35() { TERN_(HAS_DUPLICATION_MODE, set_duplication_enabled(false)); // Home only Z axis when X and Y is trusted, otherwise all axes, if needed before this procedure - if (!all_axes_trusted()) process_subcommands_now_P(PSTR("G28Z")); + if (!all_axes_trusted()) process_subcommands_now(F("G28Z")); bool err_break = false; @@ -102,7 +106,9 @@ void GcodeSuite::G35() { // In BLTOUCH HS mode, the probe travels in a deployed state. // Users of G35 might have a badly misaligned bed, so raise Z by the // length of the deployed pin (BLTOUCH stroke < 7mm) - do_blocking_move_to_z(SUM_TERN(BLTOUCH_HS_MODE, Z_CLEARANCE_BETWEEN_PROBES, 7)); + + // Unsure if this is even required. The probe seems to lift correctly after probe done. + do_blocking_move_to_z(SUM_TERN(BLTOUCH, Z_CLEARANCE_BETWEEN_PROBES, bltouch.z_extra_clearance())); const float z_probed_height = probe.probe_at_point(tramming_points[i], PROBE_PT_RAISE, 0, true); if (isnan(z_probed_height)) { @@ -116,7 +122,7 @@ void GcodeSuite::G35() { if (DEBUGGING(LEVELING)) { DEBUG_ECHOPGM("Probing point ", i + 1, " ("); - DEBUG_ECHOPGM_P((char *)pgm_read_ptr(&tramming_point_name[i])); + DEBUG_ECHOF(FPSTR(pgm_read_ptr(&tramming_point_name[i]))); DEBUG_CHAR(')'); DEBUG_ECHOLNPGM_P(SP_X_STR, tramming_points[i].x, SP_Y_STR, tramming_points[i].y, SP_Z_STR, z_probed_height); } @@ -149,7 +155,7 @@ void GcodeSuite::G35() { // Restore the active tool after homing #if HAS_MULTI_HOTEND - tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous toolhead if not PARKING_EXTRUDER + if (old_tool_index != 0) tool_change(old_tool_index, DISABLED(PARKING_EXTRUDER)); // Fetch previous toolhead if not PARKING_EXTRUDER #endif #if BOTH(HAS_LEVELING, RESTORE_LEVELING_AFTER_G35) diff --git a/Marlin/src/gcode/bedlevel/G42.cpp b/Marlin/src/gcode/bedlevel/G42.cpp index a2896ed6c7056..cb5ed97406048 100644 --- a/Marlin/src/gcode/bedlevel/G42.cpp +++ b/Marlin/src/gcode/bedlevel/G42.cpp @@ -48,8 +48,8 @@ void GcodeSuite::G42() { // Move to current_position, as modified by I, J, P parameters destination = current_position; - if (hasI) destination.x = _GET_MESH_X(ix); - if (hasJ) destination.y = _GET_MESH_Y(iy); + 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')) { diff --git a/Marlin/src/gcode/bedlevel/M420.cpp b/Marlin/src/gcode/bedlevel/M420.cpp index d76e08dee6107..277f95b9ffe16 100644 --- a/Marlin/src/gcode/bedlevel/M420.cpp +++ b/Marlin/src/gcode/bedlevel/M420.cpp @@ -67,14 +67,17 @@ void GcodeSuite::M420() { const float x_min = probe.min_x(), x_max = probe.max_x(), y_min = probe.min_y(), y_max = probe.max_y(); #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - bilinear_start.set(x_min, y_min); - bilinear_grid_spacing.set((x_max - x_min) / (GRID_MAX_CELLS_X), - (y_max - y_min) / (GRID_MAX_CELLS_Y)); + xy_pos_t start, spacing; + start.set(x_min, y_min); + spacing.set((x_max - x_min) / (GRID_MAX_CELLS_X), + (y_max - y_min) / (GRID_MAX_CELLS_Y)); + bedlevel.set_grid(spacing, start); #endif GRID_LOOP(x, y) { - Z_VALUES(x, y) = 0.001 * random(-200, 200); - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y))); + bedlevel.z_values[x][y] = 0.001 * random(-200, 200); + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, bedlevel.z_values[x][y])); } + TERN_(AUTO_BED_LEVELING_BILINEAR, bedlevel.refresh_bed_level()); SERIAL_ECHOPGM("Simulated " STRINGIFY(GRID_MAX_POINTS_X) "x" STRINGIFY(GRID_MAX_POINTS_Y) " mesh "); SERIAL_ECHOPGM(" (", x_min); SERIAL_CHAR(','); SERIAL_ECHO(y_min); @@ -98,7 +101,7 @@ void GcodeSuite::M420() { set_bed_leveling_enabled(false); #if ENABLED(EEPROM_SETTINGS) - const int8_t storage_slot = parser.has_value() ? parser.value_int() : ubl.storage_slot; + const int8_t storage_slot = parser.has_value() ? parser.value_int() : bedlevel.storage_slot; const int16_t a = settings.calc_num_meshes(); if (!a) { @@ -113,7 +116,7 @@ void GcodeSuite::M420() { } settings.load_mesh(storage_slot); - ubl.storage_slot = storage_slot; + bedlevel.storage_slot = storage_slot; #else @@ -125,10 +128,10 @@ void GcodeSuite::M420() { // L or V display the map info if (parser.seen("LV")) { - ubl.display_map(parser.byteval('T')); + bedlevel.display_map(parser.byteval('T')); SERIAL_ECHOPGM("Mesh is "); - if (!ubl.mesh_is_valid()) SERIAL_ECHOPGM("in"); - SERIAL_ECHOLNPGM("valid\nStorage slot: ", ubl.storage_slot); + if (!bedlevel.mesh_is_valid()) SERIAL_ECHOPGM("in"); + SERIAL_ECHOLNPGM("valid\nStorage slot: ", bedlevel.storage_slot); } #endif // AUTO_BED_LEVELING_UBL @@ -145,7 +148,7 @@ void GcodeSuite::M420() { #if ENABLED(AUTO_BED_LEVELING_UBL) set_bed_leveling_enabled(false); - ubl.adjust_mesh_to_mean(true, cval); + bedlevel.adjust_mesh_to_mean(true, cval); #else @@ -153,7 +156,7 @@ void GcodeSuite::M420() { // Get the sum and average of all mesh values float mesh_sum = 0; - GRID_LOOP(x, y) mesh_sum += Z_VALUES(x, y); + GRID_LOOP(x, y) mesh_sum += bedlevel.z_values[x][y]; const float zmean = mesh_sum / float(GRID_MAX_POINTS); #else // midrange @@ -161,7 +164,7 @@ void GcodeSuite::M420() { // Find the low and high mesh values. float lo_val = 100, hi_val = -100; GRID_LOOP(x, y) { - const float z = Z_VALUES(x, y); + const float z = bedlevel.z_values[x][y]; NOMORE(lo_val, z); NOLESS(hi_val, z); } @@ -175,10 +178,10 @@ void GcodeSuite::M420() { set_bed_leveling_enabled(false); // Subtract the mean from all values GRID_LOOP(x, y) { - Z_VALUES(x, y) -= zmean; - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, Z_VALUES(x, y))); + bedlevel.z_values[x][y] -= zmean; + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, bedlevel.z_values[x][y])); } - TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); + TERN_(AUTO_BED_LEVELING_BILINEAR, bedlevel.refresh_bed_level()); } #endif @@ -195,15 +198,14 @@ void GcodeSuite::M420() { // V to print the matrix or mesh if (seenV) { #if ABL_PLANAR - planner.bed_level_matrix.debug(PSTR("Bed Level Correction Matrix:")); + planner.bed_level_matrix.debug(F("Bed Level Correction Matrix:")); #else if (leveling_is_valid()) { #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - print_bilinear_leveling_grid(); - TERN_(ABL_BILINEAR_SUBDIVISION, print_bilinear_leveling_grid_virt()); + bedlevel.print_leveling_grid(); #elif ENABLED(MESH_BED_LEVELING) SERIAL_ECHOLNPGM("Mesh Bed Level data:"); - mbl.report_mesh(); + bedlevel.report_mesh(); #endif } #endif @@ -243,15 +245,15 @@ void GcodeSuite::M420() { } void GcodeSuite::M420_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR( + report_heading_etc(forReplay, F( TERN(MESH_BED_LEVELING, "Mesh Bed Leveling", TERN(AUTO_BED_LEVELING_UBL, "Unified Bed Leveling", "Auto Bed Leveling")) )); - SERIAL_ECHOPGM_P( - PSTR(" M420 S"), planner.leveling_active + SERIAL_ECHOF( + F(" M420 S"), planner.leveling_active #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - , SP_Z_STR, LINEAR_UNIT(planner.z_fade_height) + , FPSTR(SP_Z_STR), LINEAR_UNIT(planner.z_fade_height) #endif - , PSTR(" ; Leveling ") + , F(" ; Leveling ") ); serialprintln_onoff(planner.leveling_active); } diff --git a/Marlin/src/gcode/bedlevel/abl/G29.cpp b/Marlin/src/gcode/bedlevel/abl/G29.cpp index 0eb13dba96391..a540eae2631b5 100644 --- a/Marlin/src/gcode/bedlevel/abl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/abl/G29.cpp @@ -32,19 +32,9 @@ #include "../../../feature/bedlevel/bedlevel.h" #include "../../../module/motion.h" #include "../../../module/planner.h" -#include "../../../module/stepper.h" #include "../../../module/probe.h" #include "../../queue.h" -#if ENABLED(PROBE_TEMP_COMPENSATION) - #include "../../../feature/probe_temp_comp.h" - #include "../../../module/temperature.h" -#endif - -#if HAS_STATUS_MESSAGE - #include "../../../lcd/marlinui.h" -#endif - #if ENABLED(AUTO_BED_LEVELING_LINEAR) #include "../../../libs/least_squares_fit.h" #endif @@ -53,21 +43,22 @@ #include "../../../libs/vector_3.h" #endif -#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) -#include "../../../core/debug_out.h" - +#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(DWIN_CREALITY_LCD_ENHANCED) - #include "../../../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../../../lcd/e3v2/proui/dwin.h" #endif #if HAS_MULTI_HOTEND #include "../../../module/tool_change.h" #endif +#define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) +#include "../../../core/debug_out.h" + #if ABL_USES_GRID #if ENABLED(PROBE_Y_FIRST) #define PR_OUTER_VAR abl.meshCount.x @@ -82,7 +73,20 @@ #endif #endif -#define G29_RETURN(b) return TERN_(G29_RETRY_AND_RECOVER, b) +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_(HAS_DWIN_E3V2_BASIC, DWIN_LevelingDone()); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); + } +} + +#define G29_RETURN(retry, did) do{ \ + pre_g29_return(TERN0(G29_RETRY_AND_RECOVER, retry), did); \ + return TERN_(G29_RETRY_AND_RECOVER, retry); \ +}while(0) // For manual probing values persist over multiple G29 class G29_State { @@ -93,6 +97,10 @@ class G29_State { bool dryrun, reenable; + #if HAS_MULTI_HOTEND + uint8_t tool_index; + #endif + #if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR) int abl_probe_index; #endif @@ -123,6 +131,7 @@ class G29_State { #if ENABLED(AUTO_BED_LEVELING_BILINEAR) float Z_offset; + bed_mesh_t z_values; #endif #if ENABLED(AUTO_BED_LEVELING_LINEAR) @@ -219,51 +228,63 @@ class G29_State { G29_TYPE GcodeSuite::G29() { DEBUG_SECTION(log_G29, "G29", DEBUGGING(LEVELING)); + // Leveling state is persistent when done manually with multiple G29 commands TERN_(PROBE_MANUALLY, static) G29_State abl; - TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE)); - + // Keep powered steppers from timing out reset_stepper_timeout(); + // Q = Query leveling and G29 state const bool seenQ = EITHER(DEBUG_LEVELING_FEATURE, PROBE_MANUALLY) && parser.seen_test('Q'); // G29 Q is also available if debugging #if ENABLED(DEBUG_LEVELING_FEATURE) if (seenQ || DEBUGGING(LEVELING)) log_machine_info(); - if (DISABLED(PROBE_MANUALLY) && seenQ) G29_RETURN(false); + if (DISABLED(PROBE_MANUALLY) && seenQ) G29_RETURN(false, false); #endif + // A = Abort manual probing + // C = Generate fake probe points (DEBUG_LEVELING_FEATURE) const bool seenA = TERN0(PROBE_MANUALLY, parser.seen_test('A')), no_action = seenA || seenQ, faux = ENABLED(DEBUG_LEVELING_FEATURE) && DISABLED(PROBE_MANUALLY) ? parser.boolval('C') : no_action; - if (!no_action && planner.leveling_active && parser.boolval('O')) { // Auto-level only if needed + // O = Don't level if leveling is already active + if (!no_action && planner.leveling_active && parser.boolval('O')) { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> Auto-level not needed, skip"); - G29_RETURN(false); + G29_RETURN(false, false); } // Send 'N' to force homing before G29 (internal only) if (parser.seen_test('N')) - process_subcommands_now_P(TERN(CAN_SET_LEVELING_AFTER_G28, PSTR("G28L0"), G28_STR)); + process_subcommands_now(TERN(CAN_SET_LEVELING_AFTER_G28, F("G28L0"), FPSTR(G28_STR))); // Don't allow auto-leveling without homing first - if (homing_needed_error()) G29_RETURN(false); + if (homing_needed_error()) G29_RETURN(false, false); + // 3-point leveling gets points from the probe class #if ENABLED(AUTO_BED_LEVELING_3POINT) vector_3 points[3]; probe.get_three_points(points); #endif + // Storage for ABL Linear results #if ENABLED(AUTO_BED_LEVELING_LINEAR) struct linear_fit_data lsf_results; #endif + // Set and report "probing" state to host + TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE, false)); + /** * On the initial G29 fetch command parameters. */ if (!g29_in_progress) { - TERN_(HAS_MULTI_HOTEND, if (active_extruder) tool_change(0)); + #if HAS_MULTI_HOTEND + abl.tool_index = active_extruder; + if (active_extruder != 0) tool_change(0, true); + #endif #if EITHER(PROBE_MANUALLY, AUTO_BED_LEVELING_LINEAR) abl.abl_probe_index = -1; @@ -277,35 +298,43 @@ G29_TYPE GcodeSuite::G29() { if (seen_w) { if (!leveling_is_valid()) { SERIAL_ERROR_MSG("No bilinear grid"); - G29_RETURN(false); + G29_RETURN(false, false); } const float rz = parser.seenval('Z') ? RAW_Z_POSITION(parser.value_linear_units()) : current_position.z; if (!WITHIN(rz, -10, 10)) { SERIAL_ERROR_MSG("Bad Z value"); - G29_RETURN(false); + G29_RETURN(false, false); } const float rx = RAW_X_POSITION(parser.linearval('X', NAN)), ry = RAW_Y_POSITION(parser.linearval('Y', NAN)); int8_t i = parser.byteval('I', -1), j = parser.byteval('J', -1); + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + if (!isnan(rx) && !isnan(ry)) { // Get nearest i / j from rx / ry - i = (rx - bilinear_start.x + 0.5 * abl.gridSpacing.x) / abl.gridSpacing.x; - j = (ry - bilinear_start.y + 0.5 * abl.gridSpacing.y) / abl.gridSpacing.y; + i = (rx - bedlevel.grid_start.x) / bedlevel.grid_spacing.x + 0.5f; + j = (ry - bedlevel.grid_start.y) / bedlevel.grid_spacing.y + 0.5f; LIMIT(i, 0, (GRID_MAX_POINTS_X) - 1); LIMIT(j, 0, (GRID_MAX_POINTS_Y) - 1); } + + #pragma GCC diagnostic pop + if (WITHIN(i, 0, (GRID_MAX_POINTS_X) - 1) && WITHIN(j, 0, (GRID_MAX_POINTS_Y) - 1)) { set_bed_leveling_enabled(false); - z_values[i][j] = rz; - TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); + bedlevel.z_values[i][j] = rz; + bedlevel.refresh_bed_level(); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(i, j, rz)); - set_bed_leveling_enabled(abl.reenable); - if (abl.reenable) report_current_position(); + if (abl.reenable) { + set_bed_leveling_enabled(true); + report_current_position(); + } } - G29_RETURN(false); + G29_RETURN(false, false); } // parser.seen_test('W') #else @@ -317,13 +346,13 @@ G29_TYPE GcodeSuite::G29() { // Jettison bed leveling data if (!seen_w && parser.seen_test('J')) { reset_bed_level(); - G29_RETURN(false); + G29_RETURN(false, false); } abl.verbose_level = parser.intval('V'); if (!WITHIN(abl.verbose_level, 0, 4)) { SERIAL_ECHOLNPGM("?(V)erbose level implausible (0-4)."); - G29_RETURN(false); + G29_RETURN(false, false); } abl.dryrun = parser.boolval('D') || TERN0(PROBE_MANUALLY, no_action); @@ -344,11 +373,11 @@ G29_TYPE GcodeSuite::G29() { if (!WITHIN(abl.grid_points.x, 2, GRID_MAX_POINTS_X)) { SERIAL_ECHOLNPGM("?Probe points (X) implausible (2-" STRINGIFY(GRID_MAX_POINTS_X) ")."); - G29_RETURN(false); + G29_RETURN(false, false); } if (!WITHIN(abl.grid_points.y, 2, GRID_MAX_POINTS_Y)) { SERIAL_ECHOLNPGM("?Probe points (Y) implausible (2-" STRINGIFY(GRID_MAX_POINTS_Y) ")."); - G29_RETURN(false); + G29_RETURN(false, false); } abl.abl_points = abl.grid_points.x * abl.grid_points.y; @@ -383,7 +412,7 @@ G29_TYPE GcodeSuite::G29() { " F", abl.probe_position_lf.y, " B", abl.probe_position_rb.y); } SERIAL_ECHOLNPGM("? (L,R,F,B) out of bounds."); - G29_RETURN(false); + G29_RETURN(false, false); } // Probe at the points of a lattice grid @@ -404,15 +433,22 @@ G29_TYPE GcodeSuite::G29() { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("> 3-point Leveling"); points[0].z = points[1].z = points[2].z = 0; // Probe at 3 arbitrary points #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) - TERN_(EXTENSIBLE_UI, ExtUI::onMeshLevelingStart()); - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshLevelingStart()); + TERN_(DWIN_LCD_PROUI, DWIN_LevelingStart()); #endif + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart()); + if (!faux) { remember_feedrate_scaling_off(); #if ENABLED(PREHEAT_BEFORE_LEVELING) - if (!abl.dryrun) probe.preheat_for_probing(LEVELING_NOZZLE_TEMP, LEVELING_BED_TEMP); + if (!abl.dryrun) probe.preheat_for_probing(LEVELING_NOZZLE_TEMP, + #if BOTH(DWIN_LCD_PROUI, HAS_HEATED_BED) + HMI_data.BedLevT + #else + LEVELING_BED_TEMP + #endif + ); #endif } @@ -421,29 +457,29 @@ G29_TYPE GcodeSuite::G29() { if (!no_action) set_bed_leveling_enabled(false); // Deploy certain probes before starting probing - #if HAS_BED_PROBE - if (ENABLED(BLTOUCH)) - do_z_clearance(Z_CLEARANCE_DEPLOY_PROBE); - else if (probe.deploy()) { + #if ENABLED(BLTOUCH) + do_z_clearance(Z_CLEARANCE_DEPLOY_PROBE); + #elif HAS_BED_PROBE + if (probe.deploy()) { // (returns true on deploy failure) set_bed_leveling_enabled(abl.reenable); - G29_RETURN(false); + G29_RETURN(false, true); } #endif #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - if (TERN1(PROBE_MANUALLY, !no_action) - && (abl.gridSpacing != bilinear_grid_spacing || abl.probe_position_lf != bilinear_start) + if (!abl.dryrun + && (abl.gridSpacing != bedlevel.grid_spacing || abl.probe_position_lf != bedlevel.grid_start) ) { // Reset grid to 0.0 or "not probed". (Also disables ABL) reset_bed_level(); - // Initialize a grid with the given dimensions - bilinear_grid_spacing = abl.gridSpacing; - bilinear_start = abl.probe_position_lf; - // Can't re-enable (on error) until the new grid is written abl.reenable = false; } + + // Pre-populate local Z values from the stored mesh + TERN_(IS_KINEMATIC, COPY(abl.z_values, bedlevel.z_values)); + #endif // AUTO_BED_LEVELING_BILINEAR } // !g29_in_progress @@ -475,7 +511,8 @@ G29_TYPE GcodeSuite::G29() { SERIAL_ECHOLNPGM("idle"); } - if (no_action) G29_RETURN(false); + // For 'A' or 'Q' exit with success state + if (no_action) G29_RETURN(false, true); if (abl.abl_probe_index == 0) { // For the initial G29 S2 save software endstop state @@ -510,7 +547,7 @@ G29_TYPE GcodeSuite::G29() { #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) const float newz = abl.measured_z + abl.Z_offset; - z_values[abl.meshCount.x][abl.meshCount.y] = newz; + abl.z_values[abl.meshCount.x][abl.meshCount.y] = newz; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(abl.meshCount, newz)); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM_P(PSTR("Save X"), abl.meshCount.x, SP_Y_STR, abl.meshCount.y, SP_Z_STR, abl.measured_z + abl.Z_offset); @@ -550,7 +587,7 @@ G29_TYPE GcodeSuite::G29() { // Disable software endstops to allow manual adjustment // If G29 is not completed, they will not be re-enabled SET_SOFT_ENDSTOP_LOOSE(true); - G29_RETURN(false); + G29_RETURN(false, true); } else { // Leveling done! Fall through to G29 finishing code below @@ -568,7 +605,7 @@ G29_TYPE GcodeSuite::G29() { // Disable software endstops to allow manual adjustment // If G29 is not completed, they will not be re-enabled SET_SOFT_ENDSTOP_LOOSE(true); - G29_RETURN(false); + G29_RETURN(false, true); } else { @@ -600,8 +637,6 @@ G29_TYPE GcodeSuite::G29() { bool zig = PR_OUTER_SIZE & 1; // Always end at RIGHT and BACK_PROBE_BED_POSITION - abl.measured_z = 0; - // Outer loop is X with PROBE_Y_FIRST enabled // Outer loop is Y with PROBE_Y_FIRST disabled for (PR_OUTER_VAR = 0; PR_OUTER_VAR < PR_OUTER_SIZE && !isnan(abl.measured_z); PR_OUTER_VAR++) { @@ -636,7 +671,7 @@ G29_TYPE GcodeSuite::G29() { if (TERN0(IS_KINEMATIC, !probe.can_reach(abl.probePos))) continue; if (abl.verbose_level) SERIAL_ECHOLNPGM("Probing mesh point ", pt_index, "/", abl.abl_points, "."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), int(pt_index), int(abl.abl_points))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), int(pt_index), int(abl.abl_points))); abl.measured_z = faux ? 0.001f * random(-100, 101) : probe.probe_at_point(abl.probePos, raise_after, abl.verbose_level); @@ -645,12 +680,6 @@ G29_TYPE GcodeSuite::G29() { break; // Breaks out of both loops } - #if ENABLED(PROBE_TEMP_COMPENSATION) - temp_comp.compensate_measurement(TSI_BED, thermalManager.degBed(), abl.measured_z); - temp_comp.compensate_measurement(TSI_PROBE, thermalManager.degProbe(), abl.measured_z); - TERN_(USE_TEMP_EXT_COMPENSATION, temp_comp.compensate_measurement(TSI_EXT, thermalManager.degHotend(), abl.measured_z)); - #endif - #if ENABLED(AUTO_BED_LEVELING_LINEAR) abl.mean += abl.measured_z; @@ -664,12 +693,12 @@ G29_TYPE GcodeSuite::G29() { #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) const float z = abl.measured_z + abl.Z_offset; - z_values[abl.meshCount.x][abl.meshCount.y] = z; + abl.z_values[abl.meshCount.x][abl.meshCount.y] = z; TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(abl.meshCount, z)); #endif - abl.reenable = false; + abl.reenable = false; // Don't re-enable after modifying the mesh idle_no_sleep(); } // inner @@ -681,7 +710,7 @@ G29_TYPE GcodeSuite::G29() { LOOP_L_N(i, 3) { if (abl.verbose_level) SERIAL_ECHOLNPGM("Probing point ", i + 1, "/3."); - TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/3"), GET_TEXT(MSG_PROBING_POINT), int(i + 1))); + TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/3"), GET_TEXT(MSG_PROBING_POINT), int(i + 1))); // Retain the last probe position abl.probePos = xy_pos_t(points[i]); @@ -735,12 +764,16 @@ G29_TYPE GcodeSuite::G29() { if (!isnan(abl.measured_z)) { #if ENABLED(AUTO_BED_LEVELING_BILINEAR) - if (!abl.dryrun) extrapolate_unprobed_bed_level(); - print_bilinear_leveling_grid(); - - refresh_bed_level(); + if (abl.dryrun) + bedlevel.print_leveling_grid(&abl.z_values); + else { + bedlevel.set_grid(abl.gridSpacing, abl.probe_position_lf); + COPY(bedlevel.z_values, abl.z_values); + TERN_(IS_KINEMATIC, bedlevel.extrapolate_unprobed_bed_level()); + bedlevel.refresh_bed_level(); - TERN_(ABL_BILINEAR_SUBDIVISION, print_bilinear_leveling_grid_virt()); + bedlevel.print_leveling_grid(); + } #elif ENABLED(AUTO_BED_LEVELING_LINEAR) @@ -783,8 +816,8 @@ G29_TYPE GcodeSuite::G29() { float min_diff = 999; - auto print_topo_map = [&](PGM_P const title, const bool get_min) { - SERIAL_ECHOPGM_P(title); + auto print_topo_map = [&](FSTR_P const title, const bool get_min) { + SERIAL_ECHOF(title); for (int8_t yy = abl.grid_points.y - 1; yy >= 0; yy--) { LOOP_L_N(xx, abl.grid_points.x) { const int ind = abl.indexIntoAB[xx][yy]; @@ -802,19 +835,19 @@ G29_TYPE GcodeSuite::G29() { SERIAL_EOL(); }; - print_topo_map(PSTR("\nBed Height Topography:\n" - " +--- BACK --+\n" - " | |\n" - " L | (+) | R\n" - " E | | I\n" - " F | (-) N (+) | G\n" - " T | | H\n" - " | (-) | T\n" - " | |\n" - " O-- FRONT --+\n" - " (0,0)\n"), true); + print_topo_map(F("\nBed Height Topography:\n" + " +--- BACK --+\n" + " | |\n" + " L | (+) | R\n" + " E | | I\n" + " F | (-) N (+) | G\n" + " T | | H\n" + " | (-) | T\n" + " | |\n" + " O-- FRONT --+\n" + " (0,0)\n"), true); if (abl.verbose_level > 3) - print_topo_map(PSTR("\nCorrected Bed Height vs. Bed Topology:\n"), false); + print_topo_map(F("\nCorrected Bed Height vs. Bed Topology:\n"), false); } // abl.topography_map @@ -825,7 +858,7 @@ G29_TYPE GcodeSuite::G29() { // For LINEAR and 3POINT leveling correct the current position if (abl.verbose_level > 0) - planner.bed_level_matrix.debug(PSTR("\n\nBed Level Correction Matrix:")); + planner.bed_level_matrix.debug(F("\n\nBed Level Correction Matrix:")); if (!abl.dryrun) { // @@ -850,49 +883,41 @@ G29_TYPE GcodeSuite::G29() { current_position = converted; if (DEBUGGING(LEVELING)) DEBUG_POS("G29 corrected XYZ", current_position); - } - #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) + abl.reenable = true; + } - if (!abl.dryrun) { - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("G29 uncorrected Z:", current_position.z); + // Auto Bed Leveling is complete! Enable if possible. + if (abl.reenable) { + planner.leveling_active = true; + sync_plan_position(); + } - // Unapply the offset because it is going to be immediately applied - // and cause compensation movement in Z - const float fade_scaling_factor = TERN(ENABLE_LEVELING_FADE_HEIGHT, planner.fade_scaling_factor_for_z(current_position.z), 1); - current_position.z -= fade_scaling_factor * bilinear_z_offset(current_position); + #elif ENABLED(AUTO_BED_LEVELING_BILINEAR) - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM(" corrected Z:", current_position.z); - } + // Auto Bed Leveling is complete! Enable if possible. + if (!abl.dryrun || abl.reenable) set_bed_leveling_enabled(true); - #endif // ABL_PLANAR + #endif - // Auto Bed Leveling is complete! Enable if possible. - planner.leveling_active = !abl.dryrun || abl.reenable; } // !isnan(abl.measured_z) // Restore state after probing if (!faux) restore_feedrate_and_scaling(); - // Sync the planner from the current_position - if (planner.leveling_active) sync_plan_position(); - TERN_(HAS_BED_PROBE, probe.move_z_after_probing()); #ifdef Z_PROBE_END_SCRIPT if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Probe End Script: ", Z_PROBE_END_SCRIPT); planner.synchronize(); - process_subcommands_now_P(PSTR(Z_PROBE_END_SCRIPT)); + process_subcommands_now(F(Z_PROBE_END_SCRIPT)); #endif - TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedLeveling()); + TERN_(HAS_MULTI_HOTEND, if (abl.tool_index != 0) tool_change(abl.tool_index)); report_current_position(); - TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE)); - - G29_RETURN(isnan(abl.measured_z)); - + G29_RETURN(isnan(abl.measured_z), true); } #endif // HAS_ABL_NOT_UBL diff --git a/Marlin/src/gcode/bedlevel/abl/M421.cpp b/Marlin/src/gcode/bedlevel/abl/M421.cpp index 182dc32515b66..3272ea1bd2273 100644 --- a/Marlin/src/gcode/bedlevel/abl/M421.cpp +++ b/Marlin/src/gcode/bedlevel/abl/M421.cpp @@ -58,11 +58,11 @@ void GcodeSuite::M421() { sy = iy >= 0 ? iy : 0, ey = iy >= 0 ? iy : GRID_MAX_POINTS_Y - 1; LOOP_S_LE_N(x, sx, ex) { LOOP_S_LE_N(y, sy, ey) { - z_values[x][y] = zval + (hasQ ? z_values[x][y] : 0); - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, z_values[x][y])); + bedlevel.z_values[x][y] = zval + (hasQ ? bedlevel.z_values[x][y] : 0); + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(x, y, bedlevel.z_values[x][y])); } } - TERN_(ABL_BILINEAR_SUBDIVISION, bed_level_virt_interpolate()); + bedlevel.refresh_bed_level(); } else SERIAL_ERROR_MSG(STR_ERR_MESH_XY); diff --git a/Marlin/src/gcode/bedlevel/mbl/G29.cpp b/Marlin/src/gcode/bedlevel/mbl/G29.cpp index 11e503f0136bb..227964e9156ae 100644 --- a/Marlin/src/gcode/bedlevel/mbl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/mbl/G29.cpp @@ -36,12 +36,12 @@ #include "../../../libs/buzzer.h" #include "../../../lcd/marlinui.h" #include "../../../module/motion.h" -#include "../../../module/stepper.h" +#include "../../../module/planner.h" #if ENABLED(EXTENSIBLE_UI) #include "../../../lcd/extui/ui_api.h" -#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../../../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../../../lcd/e3v2/proui/dwin.h" #endif #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) @@ -75,8 +75,6 @@ void GcodeSuite::G29() { } #endif - TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE)); - static int mbl_probe_index = -1; MeshLevelingState state = (MeshLevelingState)parser.byteval('S', (int8_t)MeshReport); @@ -85,6 +83,8 @@ void GcodeSuite::G29() { return; } + TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE)); + int8_t ix, iy; ix = iy = 0; @@ -93,18 +93,19 @@ void GcodeSuite::G29() { SERIAL_ECHOPGM("Mesh Bed Leveling "); if (leveling_is_valid()) { serialprintln_onoff(planner.leveling_active); - mbl.report_mesh(); + bedlevel.report_mesh(); } else SERIAL_ECHOLNPGM("has no data."); break; case MeshStart: - mbl.reset(); + bedlevel.reset(); mbl_probe_index = 0; if (!ui.wait_for_move) { - queue.inject_P(parser.seen_test('N') ? PSTR("G28" TERN(CAN_SET_LEVELING_AFTER_G28, "L0", "") "\nG29S2") : PSTR("G29S2")); - TERN_(EXTENSIBLE_UI, ExtUI::onMeshLevelingStart()); + queue.inject(parser.seen_test('N') ? F("G28" TERN(CAN_SET_LEVELING_AFTER_G28, "L0", "") "\nG29S2") : F("G29S2")); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingStart()); + TERN_(DWIN_LCD_PROUI, DWIN_LevelingStart()); return; } state = MeshNext; @@ -117,16 +118,19 @@ void GcodeSuite::G29() { // For each G29 S2... if (mbl_probe_index == 0) { // Move close to the bed before the first point - do_blocking_move_to_z(0.4f + do_blocking_move_to_z( #ifdef MANUAL_PROBE_START_Z - + (MANUAL_PROBE_START_Z) - 0.4f + MANUAL_PROBE_START_Z + #else + 0.4f #endif ); } else { // Save Z for the previous mesh position - mbl.set_zigzag_z(mbl_probe_index - 1, current_position.z); + bedlevel.set_zigzag_z(mbl_probe_index - 1, current_position.z); TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, current_position.z)); + TERN_(DWIN_LCD_PROUI, DWIN_MeshUpdate(_MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS), current_position.z)); SET_SOFT_ENDSTOP_LOOSE(false); } // If there's another point to sample, move there with optional lift. @@ -134,8 +138,8 @@ void GcodeSuite::G29() { // Disable software endstops to allow manual adjustment // If G29 is left hanging without completion they won't be re-enabled! SET_SOFT_ENDSTOP_LOOSE(true); - mbl.zigzag(mbl_probe_index++, ix, iy); - _manual_goto_xy({ mbl.index_to_xpos[ix], mbl.index_to_ypos[iy] }); + bedlevel.zigzag(mbl_probe_index++, ix, iy); + _manual_goto_xy({ bedlevel.index_to_xpos[ix], bedlevel.index_to_ypos[iy] }); } else { // Move to the after probing position @@ -152,9 +156,8 @@ void GcodeSuite::G29() { // After recording the last point, activate home and activate mbl_probe_index = -1; SERIAL_ECHOLNPGM("Mesh probing done."); - TERN_(HAS_STATUS_MESSAGE, ui.set_status(GET_TEXT(MSG_MESH_DONE))); - BUZZ(100, 659); - BUZZ(100, 698); + TERN_(HAS_STATUS_MESSAGE, LCD_MESSAGE(MSG_MESH_DONE)); + OKAY_BUZZ(); home_all_axes(); set_bed_leveling_enabled(true); @@ -166,6 +169,7 @@ void GcodeSuite::G29() { #endif TERN_(LCD_BED_LEVELING, ui.wait_for_move = false); + TERN_(EXTENSIBLE_UI, ExtUI::onLevelingDone()); } break; @@ -191,9 +195,9 @@ void GcodeSuite::G29() { return echo_not_entered('J'); if (parser.seenval('Z')) { - mbl.z_values[ix][iy] = parser.value_linear_units(); - TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, mbl.z_values[ix][iy])); - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ix, iy, mbl.z_values[ix][iy])); + bedlevel.z_values[ix][iy] = parser.value_linear_units(); + TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ix, iy, bedlevel.z_values[ix][iy])); + TERN_(DWIN_LCD_PROUI, DWIN_MeshUpdate(ix, iy, bedlevel.z_values[ix][iy])); } else return echo_not_entered('Z'); @@ -201,7 +205,7 @@ void GcodeSuite::G29() { case MeshSetZOffset: if (parser.seenval('Z')) - mbl.z_offset = parser.value_linear_units(); + bedlevel.z_offset = parser.value_linear_units(); else return echo_not_entered('Z'); break; @@ -214,7 +218,7 @@ void GcodeSuite::G29() { if (state == MeshNext) { SERIAL_ECHOLNPGM("MBL G29 point ", _MIN(mbl_probe_index, GRID_MAX_POINTS), " of ", GRID_MAX_POINTS); - if (mbl_probe_index > 0) TERN_(HAS_STATUS_MESSAGE, ui.status_printf_P(0, PSTR(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), _MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS))); + if (mbl_probe_index > 0) TERN_(HAS_STATUS_MESSAGE, ui.status_printf(0, F(S_FMT " %i/%i"), GET_TEXT(MSG_PROBING_POINT), _MIN(mbl_probe_index, GRID_MAX_POINTS), int(GRID_MAX_POINTS))); } report_current_position(); diff --git a/Marlin/src/gcode/bedlevel/mbl/M421.cpp b/Marlin/src/gcode/bedlevel/mbl/M421.cpp index 1368ab0bef98a..e23683d55f34f 100644 --- a/Marlin/src/gcode/bedlevel/mbl/M421.cpp +++ b/Marlin/src/gcode/bedlevel/mbl/M421.cpp @@ -43,9 +43,9 @@ */ void GcodeSuite::M421() { const bool hasX = parser.seen('X'), hasI = parser.seen('I'); - const int8_t ix = hasI ? parser.value_int() : hasX ? mbl.probe_index_x(RAW_X_POSITION(parser.value_linear_units())) : -1; + const int8_t ix = hasI ? parser.value_int() : hasX ? bedlevel.probe_index_x(RAW_X_POSITION(parser.value_linear_units())) : -1; const bool hasY = parser.seen('Y'), hasJ = parser.seen('J'); - const int8_t iy = hasJ ? parser.value_int() : hasY ? mbl.probe_index_y(RAW_Y_POSITION(parser.value_linear_units())) : -1; + const int8_t iy = hasJ ? parser.value_int() : hasY ? bedlevel.probe_index_y(RAW_Y_POSITION(parser.value_linear_units())) : -1; const bool hasZ = parser.seen('Z'), hasQ = !hasZ && parser.seen('Q'); if (int(hasI && hasJ) + int(hasX && hasY) != 1 || !(hasZ || hasQ)) @@ -53,7 +53,7 @@ void GcodeSuite::M421() { else if (ix < 0 || iy < 0) SERIAL_ERROR_MSG(STR_ERR_MESH_XY); else - mbl.set_z(ix, iy, parser.value_linear_units() + (hasQ ? mbl.z_values[ix][iy] : 0)); + bedlevel.set_z(ix, iy, parser.value_linear_units() + (hasQ ? bedlevel.z_values[ix][iy] : 0)); } #endif // MESH_BED_LEVELING diff --git a/Marlin/src/gcode/bedlevel/ubl/G29.cpp b/Marlin/src/gcode/bedlevel/ubl/G29.cpp index 932503d72b973..90deab3d2e3e3 100644 --- a/Marlin/src/gcode/bedlevel/ubl/G29.cpp +++ b/Marlin/src/gcode/bedlevel/ubl/G29.cpp @@ -39,7 +39,7 @@ void GcodeSuite::G29() { TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_PROBE)); - ubl.G29(); + bedlevel.G29(); TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE)); } diff --git a/Marlin/src/gcode/bedlevel/ubl/M421.cpp b/Marlin/src/gcode/bedlevel/ubl/M421.cpp index e6f0ef1f8907c..ff74f4c6f744f 100644 --- a/Marlin/src/gcode/bedlevel/ubl/M421.cpp +++ b/Marlin/src/gcode/bedlevel/ubl/M421.cpp @@ -33,8 +33,8 @@ #if ENABLED(EXTENSIBLE_UI) #include "../../../lcd/extui/ui_api.h" -#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../../../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../../../lcd/e3v2/proui/dwin.h" #endif /** @@ -56,7 +56,7 @@ void GcodeSuite::M421() { hasZ = parser.seen('Z'), hasQ = !hasZ && parser.seen('Q'); - if (hasC) ij = ubl.find_closest_mesh_point_of_type(CLOSEST, current_position); + if (hasC) ij = bedlevel.find_closest_mesh_point_of_type(CLOSEST, current_position); // Test for bad parameter combinations if (int(hasC) + int(hasI && hasJ) != 1 || !(hasZ || hasQ || hasN)) @@ -66,10 +66,10 @@ void GcodeSuite::M421() { else if (!WITHIN(ij.x, 0, GRID_MAX_POINTS_X - 1) || !WITHIN(ij.y, 0, GRID_MAX_POINTS_Y - 1)) SERIAL_ERROR_MSG(STR_ERR_MESH_XY); else { - float &zval = ubl.z_values[ij.x][ij.y]; // Altering this Mesh Point + float &zval = bedlevel.z_values[ij.x][ij.y]; // Altering this Mesh Point zval = hasN ? NAN : parser.value_linear_units() + (hasQ ? zval : 0); // N=NAN, Z=NEWVAL, or Q=ADDVAL TERN_(EXTENSIBLE_UI, ExtUI::onMeshUpdate(ij.x, ij.y, zval)); // Ping ExtUI in case it's showing the mesh - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_MeshUpdate(ij.x, ij.y, zval)); + TERN_(DWIN_LCD_PROUI, DWIN_MeshUpdate(ij.x, ij.y, zval)); } } diff --git a/Marlin/src/gcode/calibrate/G28.cpp b/Marlin/src/gcode/calibrate/G28.cpp index 01c6050155576..f7b480a3e347b 100644 --- a/Marlin/src/gcode/calibrate/G28.cpp +++ b/Marlin/src/gcode/calibrate/G28.cpp @@ -24,8 +24,9 @@ #include "../gcode.h" -#include "../../module/stepper.h" #include "../../module/endstops.h" +#include "../../module/planner.h" +#include "../../module/stepper.h" // for various #if HAS_MULTI_HOTEND #include "../../module/tool_change.h" @@ -51,15 +52,15 @@ #include "../../lcd/extui/ui_api.h" #elif ENABLED(DWIN_CREALITY_LCD) #include "../../lcd/e3v2/creality/dwin.h" -#elif ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../../lcd/e3v2/enhanced/dwin.h" +#elif ENABLED(DWIN_LCD_PROUI) + #include "../../lcd/e3v2/proui/dwin.h" #endif #if HAS_L64XX // set L6470 absolute position registers to counts #include "../../libs/L64XX/L64XX_Marlin.h" #endif -#if ENABLED(LASER_MOVE_G28_OFF) +#if ENABLED(LASER_FEATURE) #include "../../feature/spindle_laser.h" #endif @@ -76,42 +77,33 @@ const int x_axis_home_dir = TOOL_X_HOME_DIR(active_extruder); - const float mlx = max_length(X_AXIS), - mly = max_length(Y_AXIS), - mlratio = mlx > mly ? mly / mlx : mlx / mly, - fr_mm_s = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)) * SQRT(sq(mlratio) + 1.0); + // Use a higher diagonal feedrate so axes move at homing speed + const float minfr = _MIN(homing_feedrate(X_AXIS), homing_feedrate(Y_AXIS)), + fr_mm_s = HYPOT(minfr, minfr); #if ENABLED(SENSORLESS_HOMING) sensorless_t stealth_states { - tmc_enable_stallguard(stepperX) - , tmc_enable_stallguard(stepperY) - , false - , false - #if AXIS_HAS_STALLGUARD(X2) - || tmc_enable_stallguard(stepperX2) - #endif - , false - #if AXIS_HAS_STALLGUARD(Y2) - || tmc_enable_stallguard(stepperY2) - #endif + NUM_AXIS_LIST( + TERN0(X_SENSORLESS, tmc_enable_stallguard(stepperX)), + TERN0(Y_SENSORLESS, tmc_enable_stallguard(stepperY)), + false, false, false, false + ) + , TERN0(X2_SENSORLESS, tmc_enable_stallguard(stepperX2)) + , TERN0(Y2_SENSORLESS, tmc_enable_stallguard(stepperY2)) }; #endif - do_blocking_move_to_xy(1.5 * mlx * x_axis_home_dir, 1.5 * mly * Y_HOME_DIR, fr_mm_s); + do_blocking_move_to_xy(1.5 * max_length(X_AXIS) * x_axis_home_dir, 1.5 * max_length(Y_AXIS) * Y_HOME_DIR, fr_mm_s); endstops.validate_homing_move(); current_position.set(0.0, 0.0); #if ENABLED(SENSORLESS_HOMING) && DISABLED(ENDSTOPS_ALWAYS_ON_DEFAULT) - tmc_disable_stallguard(stepperX, stealth_states.x); - tmc_disable_stallguard(stepperY, stealth_states.y); - #if AXIS_HAS_STALLGUARD(X2) - tmc_disable_stallguard(stepperX2, stealth_states.x2); - #endif - #if AXIS_HAS_STALLGUARD(Y2) - tmc_disable_stallguard(stepperY2, stealth_states.y2); - #endif + TERN_(X_SENSORLESS, tmc_disable_stallguard(stepperX, stealth_states.x)); + TERN_(X2_SENSORLESS, tmc_disable_stallguard(stepperX2, stealth_states.x2)); + TERN_(Y_SENSORLESS, tmc_disable_stallguard(stepperY, stealth_states.y)); + TERN_(Y2_SENSORLESS, tmc_disable_stallguard(stepperY2, stealth_states.y2)); #endif } @@ -156,7 +148,7 @@ homeaxis(Z_AXIS); } else { - LCD_MESSAGEPGM(MSG_ZPROBE_OUT); + LCD_MESSAGE(MSG_ZPROBE_OUT); SERIAL_ECHO_MSG(STR_ZPROBE_OUT_SER); } } @@ -178,7 +170,7 @@ motion_state.jerk_state = planner.max_jerk; planner.max_jerk.set(0, 0 OPTARG(DELTA, 0)); #endif - planner.reset_acceleration_rates(); + planner.refresh_acceleration_rates(); return motion_state; } @@ -187,7 +179,7 @@ planner.settings.max_acceleration_mm_per_s2[Y_AXIS] = motion_state.acceleration.y; TERN_(DELTA, planner.settings.max_acceleration_mm_per_s2[Z_AXIS] = motion_state.acceleration.z); TERN_(HAS_CLASSIC_JERK, planner.max_jerk = motion_state.jerk_state); - planner.reset_acceleration_rates(); + planner.refresh_acceleration_rates(); } #endif // IMPROVE_HOMING_RELIABILITY @@ -214,9 +206,12 @@ void GcodeSuite::G28() { DEBUG_SECTION(log_G28, "G28", DEBUGGING(LEVELING)); if (DEBUGGING(LEVELING)) log_machine_info(); - TERN_(LASER_MOVE_G28_OFF, cutter.set_inline_enabled(false)); // turn off laser - - TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_HOMING)); + /* + * Set the laser power to false to stop the planner from processing the current power setting. + */ + #if ENABLED(LASER_FEATURE) + planner.laser_inline.status.isPowered = false; + #endif #if ENABLED(DUAL_X_CARRIAGE) bool IDEX_saved_duplication_state = extruder_duplication_enabled; @@ -225,7 +220,7 @@ void GcodeSuite::G28() { #if ENABLED(MARLIN_DEV_MODE) if (parser.seen_test('S')) { - LOOP_LINEAR_AXES(a) set_axis_is_at_home((AxisEnum)a); + LOOP_NUM_AXES(a) set_axis_is_at_home((AxisEnum)a); sync_plan_position(); SERIAL_ECHOLNPGM("Simulated Homing"); report_current_position(); @@ -239,7 +234,12 @@ void GcodeSuite::G28() { return; } - TERN_(HAS_DWIN_E3V2_BASIC, DWIN_StartHoming()); + #if ENABLED(FULL_REPORT_TO_HOST_FEATURE) + const M_StateEnum old_grblstate = M_State_grbl; + set_and_report_grblstate(M_HOMING); + #endif + + TERN_(HAS_DWIN_E3V2_BASIC, DWIN_HomingStart()); TERN_(EXTENSIBLE_UI, ExtUI::onHomingStart()); planner.synchronize(); // Wait for planner moves to finish! @@ -264,53 +264,71 @@ void GcodeSuite::G28() { reset_stepper_timeout(); #define HAS_CURRENT_HOME(N) (defined(N##_CURRENT_HOME) && N##_CURRENT_HOME != N##_CURRENT) - #if HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2) || HAS_CURRENT_HOME(I) || HAS_CURRENT_HOME(J) || HAS_CURRENT_HOME(K) || (ENABLED(DELTA) && HAS_CURRENT_HOME(Z)) + #if HAS_CURRENT_HOME(X) || HAS_CURRENT_HOME(X2) || HAS_CURRENT_HOME(Y) || HAS_CURRENT_HOME(Y2) || (ENABLED(DELTA) && HAS_CURRENT_HOME(Z)) || HAS_CURRENT_HOME(I) || HAS_CURRENT_HOME(J) || HAS_CURRENT_HOME(K) #define HAS_HOMING_CURRENT 1 #endif #if HAS_HOMING_CURRENT - auto debug_current = [](PGM_P const s, const int16_t a, const int16_t b) { - DEBUG_ECHOPGM_P(s); DEBUG_ECHOLNPGM(" current: ", a, " -> ", b); + auto debug_current = [](FSTR_P const s, const int16_t a, const int16_t b) { + DEBUG_ECHOF(s); DEBUG_ECHOLNPGM(" current: ", a, " -> ", b); }; #if HAS_CURRENT_HOME(X) const int16_t tmc_save_current_X = stepperX.getMilliamps(); stepperX.rms_current(X_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(PSTR("X"), tmc_save_current_X, X_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_X), tmc_save_current_X, X_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(X2) const int16_t tmc_save_current_X2 = stepperX2.getMilliamps(); stepperX2.rms_current(X2_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(PSTR("X2"), tmc_save_current_X2, X2_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_X2), tmc_save_current_X2, X2_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(Y) const int16_t tmc_save_current_Y = stepperY.getMilliamps(); stepperY.rms_current(Y_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(PSTR("Y"), tmc_save_current_Y, Y_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_Y), tmc_save_current_Y, Y_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(Y2) const int16_t tmc_save_current_Y2 = stepperY2.getMilliamps(); stepperY2.rms_current(Y2_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(PSTR("Y2"), tmc_save_current_Y2, Y2_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_Y2), tmc_save_current_Y2, Y2_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(I) const int16_t tmc_save_current_I = stepperI.getMilliamps(); stepperI.rms_current(I_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(F(AXIS4_STR), tmc_save_current_I, I_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_I), tmc_save_current_I, I_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(J) const int16_t tmc_save_current_J = stepperJ.getMilliamps(); stepperJ.rms_current(J_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(F(AXIS5_STR), tmc_save_current_J, J_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_J), tmc_save_current_J, J_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(K) const int16_t tmc_save_current_K = stepperK.getMilliamps(); stepperK.rms_current(K_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(F(AXIS6_STR), tmc_save_current_K, K_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_K), tmc_save_current_K, K_CURRENT_HOME); #endif #if HAS_CURRENT_HOME(Z) && ENABLED(DELTA) const int16_t tmc_save_current_Z = stepperZ.getMilliamps(); stepperZ.rms_current(Z_CURRENT_HOME); - if (DEBUGGING(LEVELING)) debug_current(PSTR("Z"), tmc_save_current_Z, Z_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_Z), tmc_save_current_Z, Z_CURRENT_HOME); + #endif + #if HAS_CURRENT_HOME(I) + const int16_t tmc_save_current_I = stepperI.getMilliamps(); + stepperI.rms_current(I_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_I), tmc_save_current_I, I_CURRENT_HOME); + #endif + #if HAS_CURRENT_HOME(J) + const int16_t tmc_save_current_J = stepperJ.getMilliamps(); + stepperJ.rms_current(J_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_J), tmc_save_current_J, J_CURRENT_HOME); + #endif + #if HAS_CURRENT_HOME(K) + const int16_t tmc_save_current_K = stepperK.getMilliamps(); + stepperK.rms_current(K_CURRENT_HOME); + if (DEBUGGING(LEVELING)) debug_current(F(STR_K), tmc_save_current_K, K_CURRENT_HOME); + #endif + #if SENSORLESS_STALLGUARD_DELAY + safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle #endif #endif @@ -355,21 +373,21 @@ void GcodeSuite::G28() { #define _UNSAFE(A) (homeZ && TERN0(Z_SAFE_HOMING, axes_should_home(_BV(A##_AXIS)))) const bool homeZ = TERN0(HAS_Z_AXIS, parser.seen_test('Z')), - LINEAR_AXIS_LIST( // Other axes should be homed before Z safe-homing + 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) ), - LINEAR_AXIS_LIST( // Home each axis if needed or flagged + NUM_AXIS_LIST( // Home each axis if needed or flagged homeX = needX || parser.seen_test('X'), homeY = needY || parser.seen_test('Y'), homeZZ = homeZ, - homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME), + homeI = needI || parser.seen_test(AXIS4_NAME), homeJ = needJ || parser.seen_test(AXIS5_NAME), homeK = needK || parser.seen_test(AXIS6_NAME) ), - home_all = LINEAR_AXIS_GANG( // Home-all if all or none are flagged + home_all = NUM_AXIS_GANG( // Home-all if all or none are flagged homeX == homeX, && homeY == homeX, && homeZ == homeX, && homeI == homeX, && homeJ == homeX, && homeK == homeX ), - LINEAR_AXIS_LIST( + NUM_AXIS_LIST( doX = home_all || homeX, doY = home_all || homeY, doZ = home_all || homeZ, doI = home_all || homeI, doJ = home_all || homeJ, doK = home_all || homeK ); @@ -382,9 +400,10 @@ void GcodeSuite::G28() { TERN_(HOME_Z_FIRST, if (doZ) homeaxis(Z_AXIS)); - const float z_homing_height = parser.seenval('R') ? parser.value_linear_units() : Z_HOMING_HEIGHT; + const bool seenR = parser.seenval('R'); + const float z_homing_height = seenR ? parser.value_linear_units() : Z_HOMING_HEIGHT; - if (z_homing_height && (LINEAR_AXIS_GANG(doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK))) { + if (z_homing_height && (seenR || NUM_AXIS_GANG(doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK))) { // Raise Z before homing any other axes and z is not already high enough (never lower z) if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Raise Z (before homing) by ", z_homing_height); do_z_clearance(z_homing_height); @@ -438,20 +457,20 @@ void GcodeSuite::G28() { stepper.set_separate_multi_axis(false); #endif - TERN(Z_SAFE_HOMING, home_z_safely(), homeaxis(Z_AXIS)); + #if ENABLED(Z_SAFE_HOMING) + if (TERN1(POWER_LOSS_RECOVERY, !parser.seen_test('H'))) home_z_safely(); else homeaxis(Z_AXIS); + #else + homeaxis(Z_AXIS); + #endif probe.move_z_after_homing(); } #endif - #if LINEAR_AXES >= 4 - if (doI) homeaxis(I_AXIS); - #endif - #if LINEAR_AXES >= 5 - if (doJ) homeaxis(J_AXIS); - #endif - #if LINEAR_AXES >= 6 - if (doK) homeaxis(K_AXIS); - #endif + SECONDARY_AXIS_CODE( + if (doI) homeaxis(I_AXIS), + if (doJ) homeaxis(J_AXIS), + if (doK) homeaxis(K_AXIS) + ); sync_plan_position(); @@ -534,19 +553,22 @@ void GcodeSuite::G28() { #if HAS_CURRENT_HOME(K) stepperK.rms_current(tmc_save_current_K); #endif + #if SENSORLESS_STALLGUARD_DELAY + safe_delay(SENSORLESS_STALLGUARD_DELAY); // Short delay needed to settle + #endif #endif // HAS_HOMING_CURRENT ui.refresh(); - TERN_(HAS_DWIN_E3V2_BASIC, DWIN_CompletedHoming()); - TERN_(EXTENSIBLE_UI, ExtUI::onHomingComplete()); + TERN_(HAS_DWIN_E3V2_BASIC, DWIN_HomingDone()); + TERN_(EXTENSIBLE_UI, ExtUI::onHomingDone()); report_current_position(); if (ENABLED(NANODLP_Z_SYNC) && (doZ || ENABLED(NANODLP_ALL_AXIS))) SERIAL_ECHOLNPGM(STR_Z_MOVE_COMP); - TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE)); + TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(old_grblstate)); #if HAS_L64XX // Set L6470 absolute position registers to counts @@ -554,7 +576,7 @@ void GcodeSuite::G28() { // If not, this will need a PROGMEM directive and an accessor. #define _EN_ITEM(N) , E_AXIS static constexpr AxisEnum L64XX_axis_xref[MAX_L64XX] = { - LINEAR_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS), + NUM_AXIS_LIST(X_AXIS, Y_AXIS, Z_AXIS, I_AXIS, J_AXIS, K_AXIS), X_AXIS, Y_AXIS, Z_AXIS, Z_AXIS, Z_AXIS REPEAT(E_STEPPERS, _EN_ITEM) }; diff --git a/Marlin/src/gcode/calibrate/G33.cpp b/Marlin/src/gcode/calibrate/G33.cpp index 24a985299f42a..656c23cb78ff0 100644 --- a/Marlin/src/gcode/calibrate/G33.cpp +++ b/Marlin/src/gcode/calibrate/G33.cpp @@ -27,7 +27,7 @@ #include "../gcode.h" #include "../../module/delta.h" #include "../../module/motion.h" -#include "../../module/stepper.h" +#include "../../module/planner.h" #include "../../module/endstops.h" #include "../../lcd/marlinui.h" @@ -69,13 +69,11 @@ enum CalEnum : char { // the 7 main calibration points - float lcd_probe_pt(const xy_pos_t &xy); -float dcr; - void ac_home() { endstops.enable(true); - TERN_(HAS_DELTA_SENSORLESS_PROBING, probe.set_homing_current(true)); + TERN_(SENSORLESS_HOMING, endstops.set_homing_current(true)); home_delta(); - TERN_(HAS_DELTA_SENSORLESS_PROBING, probe.set_homing_current(false)); + TERN_(SENSORLESS_HOMING, endstops.set_homing_current(false)); endstops.not_homing(); } @@ -97,12 +95,10 @@ void ac_cleanup(TERN_(HAS_MULTI_HOTEND, const uint8_t old_tool_index)) { TERN_(HAS_MULTI_HOTEND, tool_change(old_tool_index, true)); } -void print_signed_float(PGM_P const prefix, const_float_t f) { +void print_signed_float(FSTR_P const prefix, const_float_t f) { SERIAL_ECHOPGM(" "); - SERIAL_ECHOPGM_P(prefix); - SERIAL_CHAR(':'); - if (f >= 0) SERIAL_CHAR('+'); - SERIAL_ECHO_F(f, 2); + SERIAL_ECHOF(prefix, AS_CHAR(':')); + serial_offset(f); } /** @@ -111,24 +107,23 @@ void print_signed_float(PGM_P const prefix, const_float_t f) { static void print_calibration_settings(const bool end_stops, const bool tower_angles) { SERIAL_ECHOPGM(".Height:", delta_height); if (end_stops) { - print_signed_float(PSTR("Ex"), delta_endstop_adj.a); - print_signed_float(PSTR("Ey"), delta_endstop_adj.b); - print_signed_float(PSTR("Ez"), delta_endstop_adj.c); + print_signed_float(F("Ex"), delta_endstop_adj.a); + print_signed_float(F("Ey"), delta_endstop_adj.b); + print_signed_float(F("Ez"), delta_endstop_adj.c); } if (end_stops && tower_angles) { - SERIAL_ECHOPGM(" Radius:", delta_radius); - SERIAL_EOL(); + SERIAL_ECHOLNPGM(" Radius:", delta_radius); SERIAL_CHAR('.'); SERIAL_ECHO_SP(13); } if (tower_angles) { - print_signed_float(PSTR("Tx"), delta_tower_angle_trim.a); - print_signed_float(PSTR("Ty"), delta_tower_angle_trim.b); - print_signed_float(PSTR("Tz"), delta_tower_angle_trim.c); + print_signed_float(F("Tx"), delta_tower_angle_trim.a); + print_signed_float(F("Ty"), delta_tower_angle_trim.b); + print_signed_float(F("Tz"), delta_tower_angle_trim.c); } - if ((!end_stops && tower_angles) || (end_stops && !tower_angles)) { // XOR + if (end_stops != tower_angles) SERIAL_ECHOPGM(" Radius:", delta_radius); - } + SERIAL_EOL(); } @@ -137,11 +132,11 @@ static void print_calibration_settings(const bool end_stops, const bool tower_an */ static void print_calibration_results(const float z_pt[NPP + 1], const bool tower_points, const bool opposite_points) { SERIAL_ECHOPGM(". "); - print_signed_float(PSTR("c"), z_pt[CEN]); + print_signed_float(F("c"), z_pt[CEN]); if (tower_points) { - print_signed_float(PSTR(" x"), z_pt[__A]); - print_signed_float(PSTR(" y"), z_pt[__B]); - print_signed_float(PSTR(" z"), z_pt[__C]); + print_signed_float(F(" x"), z_pt[__A]); + print_signed_float(F(" y"), z_pt[__B]); + print_signed_float(F(" z"), z_pt[__C]); } if (tower_points && opposite_points) { SERIAL_EOL(); @@ -149,9 +144,9 @@ static void print_calibration_results(const float z_pt[NPP + 1], const bool towe SERIAL_ECHO_SP(13); } if (opposite_points) { - print_signed_float(PSTR("yz"), z_pt[_BC]); - print_signed_float(PSTR("zx"), z_pt[_CA]); - print_signed_float(PSTR("xy"), z_pt[_AB]); + print_signed_float(F("yz"), z_pt[_BC]); + print_signed_float(F("zx"), z_pt[_CA]); + print_signed_float(F("xy"), z_pt[_AB]); } SERIAL_EOL(); } @@ -179,7 +174,7 @@ static float std_dev_points(float z_pt[NPP + 1], const bool _0p_cal, const bool */ static float calibration_probe(const xy_pos_t &xy, const bool stow, const bool probe_at_offset) { #if HAS_BED_PROBE - return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, true, probe_at_offset); + return probe.probe_at_point(xy, stow ? PROBE_PT_STOW : PROBE_PT_RAISE, 0, probe_at_offset, false); #else UNUSED(stow); return lcd_probe_pt(xy); @@ -189,7 +184,7 @@ static float calibration_probe(const xy_pos_t &xy, const bool stow, const bool p /** * - Probe a grid */ -static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const bool towers_set, const bool stow_after_each, const bool probe_at_offset) { +static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_points, const float dcr, const bool towers_set, const bool stow_after_each, const bool probe_at_offset) { const bool _0p_calibration = probe_points == 0, _1p_calibration = probe_points == 1 || probe_points == -1, _4p_calibration = probe_points == 2, @@ -273,7 +268,7 @@ static bool probe_calibration_points(float z_pt[NPP + 1], const int8_t probe_poi * - formulae 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]) { +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}; LOOP_CAL_ALL(rad) { @@ -285,7 +280,7 @@ static void reverse_kinematics_probe_points(float z_pt[NPP + 1], abc_float_t mm_ } } -static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1]) { +static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], float z_pt[NPP + 1], const float dcr) { const float r_quot = dcr / delta_radius; #define ZPP(N,I,A) (((1.0f + r_quot * (N)) / 3.0f) * mm_at_pt_axis[I].A) @@ -304,19 +299,19 @@ static void forward_kinematics_probe_points(abc_float_t mm_at_pt_axis[NPP + 1], z_pt[_AB] = Zp1(_AB, a) + Zp1(_AB, b) + Zm2(_AB, c); } -static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t delta_e, const float delta_r, abc_float_t delta_t) { +static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], const float dcr, abc_float_t delta_e, const float delta_r, abc_float_t delta_t) { const float z_center = z_pt[CEN]; abc_float_t diff_mm_at_pt_axis[NPP + 1], new_mm_at_pt_axis[NPP + 1]; - reverse_kinematics_probe_points(z_pt, diff_mm_at_pt_axis); + reverse_kinematics_probe_points(z_pt, diff_mm_at_pt_axis, dcr); delta_radius += delta_r; delta_tower_angle_trim += delta_t; recalc_delta_settings(); - reverse_kinematics_probe_points(z_pt, new_mm_at_pt_axis); + reverse_kinematics_probe_points(z_pt, new_mm_at_pt_axis, dcr); LOOP_CAL_ALL(rad) diff_mm_at_pt_axis[rad] -= new_mm_at_pt_axis[rad] + delta_e; - forward_kinematics_probe_points(diff_mm_at_pt_axis, z_pt); + forward_kinematics_probe_points(diff_mm_at_pt_axis, z_pt, dcr); LOOP_CAL_RAD(rad) z_pt[rad] -= z_pt[CEN] - z_center; z_pt[CEN] = z_center; @@ -326,31 +321,31 @@ static void calc_kinematics_diff_probe_points(float z_pt[NPP + 1], abc_float_t d recalc_delta_settings(); } -static float auto_tune_h() { +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 } -static float auto_tune_r() { +static float auto_tune_r(const float dcr) { constexpr float diff = 0.01f, delta_r = diff; float r_fac = 0.0f, z_pt[NPP + 1] = { 0.0f }; abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f }; - calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t); + calc_kinematics_diff_probe_points(z_pt, dcr, delta_e, delta_r, delta_t); r_fac = -(z_pt[__A] + z_pt[__B] + z_pt[__C] + z_pt[_BC] + z_pt[_CA] + z_pt[_AB]) / 6.0f; r_fac = diff / r_fac / 3.0f; // 1/(3*delta_Z) return r_fac; } -static float auto_tune_a() { +static float auto_tune_a(const float dcr) { constexpr float diff = 0.01f, delta_r = 0.0f; float a_fac = 0.0f, z_pt[NPP + 1] = { 0.0f }; abc_float_t delta_e = { 0.0f }, delta_t = { 0.0f }; delta_t.reset(); - LOOP_LINEAR_AXES(axis) { + LOOP_NUM_AXES(axis) { delta_t[axis] = diff; - calc_kinematics_diff_probe_points(z_pt, delta_e, delta_r, delta_t); + calc_kinematics_diff_probe_points(z_pt, dcr, delta_e, delta_r, delta_t); delta_t[axis] = 0; a_fac += z_pt[uint8_t((axis * _4P_STEP) - _7P_STEP + NPP) % NPP + 1] / 6.0f; a_fac -= z_pt[uint8_t((axis * _4P_STEP) + 1 + _7P_STEP)] / 6.0f; @@ -372,7 +367,7 @@ static float auto_tune_a() { * P3 Probe all positions: center, towers and opposite towers. Calibrate all. * P4-P10 Probe all positions at different intermediate locations and average them. * - * Rn.nn override default calibration Radius + * Rn.nn Temporary reduce the probe grid by the specified amount (mm) * * T Don't calibrate tower angle corrections * @@ -388,13 +383,15 @@ static float auto_tune_a() { * * E Engage the probe for each point * - * O Probe at offset points (this is wrong but it seems to work) + * 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 */ void GcodeSuite::G33() { @@ -406,27 +403,18 @@ void GcodeSuite::G33() { return; } - const bool probe_at_offset = TERN0(HAS_PROBE_XY_OFFSET, parser.boolval('O')), + const bool probe_at_offset = TERN0(HAS_PROBE_XY_OFFSET, parser.seen_test('O')), towers_set = !parser.seen_test('T'); - float max_dcr = dcr = DELTA_PRINTABLE_RADIUS; + // The calibration radius is set to a calculated value + float dcr = probe_at_offset ? DELTA_PRINTABLE_RADIUS : DELTA_PRINTABLE_RADIUS - PROBING_MARGIN; #if HAS_PROBE_XY_OFFSET - // For offset probes the calibration radius is set to a safe but non-optimal value - dcr -= HYPOT(probe.offset_xy.x, probe.offset_xy.y); - if (probe_at_offset) { - // With probe positions both probe and nozzle need to be within the printable area - max_dcr = dcr; - } - // else with nozzle positions there is a risk of the probe being outside the bed - // but as long the nozzle stays within the printable area there is no risk of - // the effector crashing into the towers. + const float total_offset = HYPOT(probe.offset_xy.x, probe.offset_xy.y); + dcr -= probe_at_offset ? _MAX(total_offset, PROBING_MARGIN) : total_offset; #endif - - if (parser.seenval('R')) dcr = parser.value_float(); - if (!WITHIN(dcr, 0, max_dcr)) { - SERIAL_ECHOLNPGM("?calibration (R)adius implausible."); - return; - } + NOMORE(dcr, DELTA_PRINTABLE_RADIUS); + if (parser.seenval('R')) dcr -= _MAX(parser.value_float(), 0.0f); + TERN_(HAS_DELTA_SENSORLESS_PROBING, dcr *= sensorless_radius_factor); const float calibration_precision = parser.floatval('C', 0.0f); if (calibration_precision < 0) { @@ -449,9 +437,8 @@ void GcodeSuite::G33() { const bool stow_after_each = parser.seen_test('E'); #if HAS_DELTA_SENSORLESS_PROBING - probe.test_sensitivity.x = !parser.seen_test('X'); - TERN_(HAS_Y_AXIS, probe.test_sensitivity.y = !parser.seen_test('Y')); - TERN_(HAS_Z_AXIS, probe.test_sensitivity.z = !parser.seen_test('Z')); + probe.test_sensitivity = { !parser.seen_test('X'), !parser.seen_test('Y'), !parser.seen_test('Z') }; + const bool do_save_offset_adj = parser.seen_test('S'); #endif const bool _0p_calibration = probe_points == 0, @@ -479,9 +466,10 @@ void GcodeSuite::G33() { // Report settings PGM_P const checkingac = PSTR("Checking... AC"); SERIAL_ECHOPGM_P(checkingac); + SERIAL_ECHOPGM(" at radius:", dcr); if (verbose_level == 0) SERIAL_ECHOPGM(" (DRY-RUN)"); SERIAL_EOL(); - ui.set_status_P(checkingac); + ui.set_status(checkingac); print_calibration_settings(_endstop_results, _angle_results); @@ -489,6 +477,25 @@ void GcodeSuite::G33() { if (!_0p_calibration) ac_home(); + #if HAS_DELTA_SENSORLESS_PROBING + if (verbose_level > 0 && do_save_offset_adj) { + offset_sensorless_adj.reset(); + + auto caltower = [&](Probe::sense_bool_t s){ + float z_at_pt[NPP + 1]; + LOOP_CAL_ALL(rad) z_at_pt[rad] = 0.0f; + probe.test_sensitivity = s; + if (probe_calibration_points(z_at_pt, 1, dcr, false, false, probe_at_offset)) + probe.set_offset_sensorless_adj(z_at_pt[CEN]); + }; + caltower({ true, false, false }); // A + caltower({ false, true, false }); // B + caltower({ false, false, true }); // C + + probe.test_sensitivity = { true, true, true }; // reset to all + } + #endif + do { // start iterations float z_at_pt[NPP + 1] = { 0.0f }; @@ -498,7 +505,7 @@ void GcodeSuite::G33() { // Probe the points zero_std_dev_old = zero_std_dev; - if (!probe_calibration_points(z_at_pt, probe_points, towers_set, stow_after_each, probe_at_offset)) { + if (!probe_calibration_points(z_at_pt, probe_points, dcr, towers_set, stow_after_each, probe_at_offset)) { SERIAL_ECHOLNPGM("Correct delta settings with M665 and M666"); return ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index)); } @@ -538,10 +545,10 @@ void GcodeSuite::G33() { // calculate factors if (_7p_9_center) dcr *= 0.9f; - h_factor = auto_tune_h(); - r_factor = auto_tune_r(); - a_factor = auto_tune_a(); - dcr /= 0.9f; + h_factor = auto_tune_h(dcr); + r_factor = auto_tune_r(dcr); + a_factor = auto_tune_a(dcr); + if (_7p_9_center) dcr /= 0.9f; switch (probe_points) { case 0: @@ -550,7 +557,7 @@ void GcodeSuite::G33() { case 1: test_precision = 0.0f; // forced end - LOOP_LINEAR_AXES(axis) e_delta[axis] = +Z4(CEN); + LOOP_NUM_AXES(axis) e_delta[axis] = +Z4(CEN); break; case 2: @@ -598,22 +605,31 @@ void GcodeSuite::G33() { // Normalize angles to least-squares if (_angle_results) { float a_sum = 0.0f; - LOOP_LINEAR_AXES(axis) a_sum += delta_tower_angle_trim[axis]; - LOOP_LINEAR_AXES(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f; + LOOP_NUM_AXES(axis) a_sum += delta_tower_angle_trim[axis]; + LOOP_NUM_AXES(axis) delta_tower_angle_trim[axis] -= a_sum / 3.0f; } // 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_LINEAR_AXES(axis) delta_endstop_adj[axis] -= z_temp; + LOOP_NUM_AXES(axis) delta_endstop_adj[axis] -= z_temp; } recalc_delta_settings(); NOMORE(zero_std_dev_min, zero_std_dev); // print report - if (verbose_level == 3 || verbose_level == 0) + if (verbose_level == 3 || verbose_level == 0) { print_calibration_results(z_at_pt, _tower_results, _opposite_results); + #if HAS_DELTA_SENSORLESS_PROBING + if (verbose_level == 0 && probe_points == 1) { + if (do_save_offset_adj) + probe.set_offset_sensorless_adj(z_at_pt[CEN]); + else + probe.refresh_largest_sensorless_adj(); + } + #endif + } if (verbose_level != 0) { // !dry run if ((zero_std_dev >= test_precision && iterations > force_iterations) || zero_std_dev <= calibration_precision) { // end iterations @@ -653,13 +669,13 @@ void GcodeSuite::G33() { } } else { // dry run - PGM_P const enddryrun = PSTR("End DRY-RUN"); - SERIAL_ECHOPGM_P(enddryrun); + FSTR_P const enddryrun = F("End DRY-RUN"); + SERIAL_ECHOF(enddryrun); SERIAL_ECHO_SP(35); SERIAL_ECHOLNPAIR_F("std dev:", zero_std_dev, 3); char mess[21]; - strcpy_P(mess, enddryrun); + strcpy_P(mess, FTOP(enddryrun)); strcpy_P(&mess[11], PSTR(" sd:")); if (zero_std_dev < 1) sprintf_P(&mess[15], PSTR("0.%03i"), (int)LROUND(zero_std_dev * 1000.0f)); @@ -674,6 +690,9 @@ void GcodeSuite::G33() { ac_cleanup(TERN_(HAS_MULTI_HOTEND, old_tool_index)); TERN_(FULL_REPORT_TO_HOST_FEATURE, set_and_report_grblstate(M_IDLE)); + #if HAS_DELTA_SENSORLESS_PROBING + probe.test_sensitivity = { true, true, true }; + #endif } #endif // DELTA_AUTO_CALIBRATION diff --git a/Marlin/src/gcode/calibrate/G34.cpp b/Marlin/src/gcode/calibrate/G34.cpp index f335a123114a5..1be3952ffe2e2 100644 --- a/Marlin/src/gcode/calibrate/G34.cpp +++ b/Marlin/src/gcode/calibrate/G34.cpp @@ -26,9 +26,12 @@ #include "../gcode.h" #include "../../module/motion.h" -#include "../../module/stepper.h" #include "../../module/endstops.h" +#if ANY(HAS_MOTOR_CURRENT_SPI, HAS_MOTOR_CURRENT_PWM, HAS_TRINAMIC_CONFIG) + #include "../../module/stepper.h" +#endif + #if HAS_LEVELING #include "../../feature/bedlevel/bedlevel.h" #endif @@ -47,7 +50,7 @@ void GcodeSuite::G34() { TemporaryGlobalEndstopsState unlock_z(false); #ifdef GANTRY_CALIBRATION_COMMANDS_PRE - gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_PRE)); + process_subcommands_now(F(GANTRY_CALIBRATION_COMMANDS_PRE)); if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Sub Commands Processed"); #endif @@ -79,7 +82,7 @@ void GcodeSuite::G34() { 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[Z_AXIS]; + const uint32_t previous_current = 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); @@ -91,7 +94,7 @@ void GcodeSuite::G34() { 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_STEPPER_DRIVERS]; + static uint16_t previous_current_arr[NUM_Z_STEPPERS]; #if AXIS_IS_TMC(Z) previous_current_arr[0] = stepperZ.getMilliamps(); stepperZ.rms_current(target_current); @@ -114,10 +117,6 @@ void GcodeSuite::G34() { if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Final Z Move"); do_blocking_move_to_z(zgrind, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE)); - // Back off end plate, back to normal motion range - if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Backoff"); - do_blocking_move_to_z(zpounce, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE)); - #if _REDUCE_CURRENT // Reset current to original values if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Restore Current"); @@ -146,9 +145,13 @@ void GcodeSuite::G34() { #endif #endif + // Back off end plate, back to normal motion range + if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Z Backoff"); + do_blocking_move_to_z(zpounce, MMM_TO_MMS(GANTRY_CALIBRATION_FEEDRATE)); + #ifdef GANTRY_CALIBRATION_COMMANDS_POST if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Running Post Commands"); - gcode.process_subcommands_now_P(PSTR(GANTRY_CALIBRATION_COMMANDS_POST)); + process_subcommands_now(F(GANTRY_CALIBRATION_COMMANDS_POST)); #endif SET_SOFT_ENDSTOP_LOOSE(false); diff --git a/Marlin/src/gcode/calibrate/G34_M422.cpp b/Marlin/src/gcode/calibrate/G34_M422.cpp index dd1dd5622ac16..8cf652cd8411c 100644 --- a/Marlin/src/gcode/calibrate/G34_M422.cpp +++ b/Marlin/src/gcode/calibrate/G34_M422.cpp @@ -31,7 +31,7 @@ #include "../../module/stepper.h" #include "../../module/planner.h" #include "../../module/probe.h" -#include "../../lcd/marlinui.h" // for LCD_MESSAGEPGM +#include "../../lcd/marlinui.h" // for LCD_MESSAGE #if HAS_LEVELING #include "../../feature/bedlevel/bedlevel.h" @@ -41,16 +41,20 @@ #include "../../module/tool_change.h" #endif -#if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) +#if HAS_Z_STEPPER_ALIGN_STEPPER_XY #include "../../libs/least_squares_fit.h" #endif +#if ENABLED(BLTOUCH) + #include "../../feature/bltouch.h" +#endif + #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE) #include "../../core/debug_out.h" -#if NUM_Z_STEPPER_DRIVERS >= 3 +#if NUM_Z_STEPPERS >= 3 #define TRIPLE_Z 1 - #if NUM_Z_STEPPER_DRIVERS >= 4 + #if NUM_Z_STEPPERS >= 4 #define QUAD_Z 1 #endif #endif @@ -118,7 +122,7 @@ void GcodeSuite::G34() { break; } - const float z_auto_align_amplification = TERN(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, Z_STEPPER_ALIGN_AMP, parser.floatval('A', Z_STEPPER_ALIGN_AMP)); + const float z_auto_align_amplification = TERN(HAS_Z_STEPPER_ALIGN_STEPPER_XY, Z_STEPPER_ALIGN_AMP, parser.floatval('A', Z_STEPPER_ALIGN_AMP)); if (!WITHIN(ABS(z_auto_align_amplification), 0.5f, 2.0f)) { SERIAL_ECHOLNPGM("?(A)mplification out of bounds (0.5-2.0)."); break; @@ -149,7 +153,7 @@ void GcodeSuite::G34() { // In BLTOUCH HS mode, the probe travels in a deployed state. // Users of G34 might have a badly misaligned bed, so raise Z by the // length of the deployed pin (BLTOUCH stroke < 7mm) - #define Z_BASIC_CLEARANCE (Z_CLEARANCE_BETWEEN_PROBES + 7.0f * BOTH(BLTOUCH, BLTOUCH_HS_MODE)) + #define Z_BASIC_CLEARANCE (Z_CLEARANCE_BETWEEN_PROBES + TERN0(BLTOUCH, bltouch.z_extra_clearance())) // Compute a worst-case clearance height to probe from. After the first // iteration this will be re-calculated based on the actual bed position @@ -175,16 +179,16 @@ void GcodeSuite::G34() { // Now, the Z origin lies below the build plate. That allows to probe deeper, before run_z_probe throws an error. // This hack is un-done at the end of G34 - either by re-homing, or by using the probed heights of the last iteration. - #if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - float last_z_align_move[NUM_Z_STEPPER_DRIVERS] = ARRAY_N_1(NUM_Z_STEPPER_DRIVERS, 10000.0f); + #if !HAS_Z_STEPPER_ALIGN_STEPPER_XY + float last_z_align_move[NUM_Z_STEPPERS] = ARRAY_N_1(NUM_Z_STEPPERS, 10000.0f); #else float last_z_align_level_indicator = 10000.0f; #endif - float z_measured[NUM_Z_STEPPER_DRIVERS] = { 0 }, + float z_measured[NUM_Z_STEPPERS] = { 0 }, z_maxdiff = 0.0f, amplification = z_auto_align_amplification; - #if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if !HAS_Z_STEPPER_ALIGN_STEPPER_XY bool adjustment_reverse = false; #endif @@ -213,23 +217,25 @@ void GcodeSuite::G34() { float z_measured_max = -100000.0f; // Probe all positions (one per Z-Stepper) - LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) { + LOOP_L_N(i, NUM_Z_STEPPERS) { // iteration odd/even --> downward / upward stepper sequence - const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPER_DRIVERS - 1 - i : i; + const uint8_t iprobe = (iteration & 1) ? NUM_Z_STEPPERS - 1 - i : i; // Safe clearance even on an incline if ((iteration == 0 || i > 0) && z_probe > current_position.z) do_blocking_move_to_z(z_probe); + xy_pos_t &ppos = z_stepper_align.xy[iprobe]; + if (DEBUGGING(LEVELING)) - DEBUG_ECHOLNPGM_P(PSTR("Probing X"), z_stepper_align.xy[iprobe].x, SP_Y_STR, z_stepper_align.xy[iprobe].y); + DEBUG_ECHOLNPGM_P(PSTR("Probing X"), ppos.x, SP_Y_STR, ppos.y); // 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(z_stepper_align.xy[iprobe], raise_after, 0, true, false); + 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); if (isnan(z_probed_height)) { SERIAL_ECHOLNPGM("Probing failed"); - LCD_MESSAGEPGM(MSG_LCD_PROBING_FAILED); + LCD_MESSAGE(MSG_LCD_PROBING_FAILED); err_break = true; break; } @@ -252,7 +258,7 @@ void GcodeSuite::G34() { z_maxdiff = z_measured_max - z_measured_min; z_probe = Z_BASIC_CLEARANCE + z_measured_max + z_maxdiff; - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY // Replace the initial values in z_measured with calculated heights at // each stepper position. This allows the adjustment algorithm to be // shared between both possible probing mechanisms. @@ -266,20 +272,20 @@ void GcodeSuite::G34() { // This allows the actual adjustment logic to be shared by both algorithms. linear_fit_data lfd; incremental_LSF_reset(&lfd); - LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) { + LOOP_L_N(i, NUM_Z_STEPPERS) { SERIAL_ECHOLNPGM("PROBEPT_", i, ": ", z_measured[i]); incremental_LSF(&lfd, z_stepper_align.xy[i], z_measured[i]); } finish_incremental_LSF(&lfd); z_measured_min = 100000.0f; - LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) { + LOOP_L_N(i, NUM_Z_STEPPERS) { z_measured[i] = -(lfd.A * z_stepper_align.stepper_xy[i].x + lfd.B * z_stepper_align.stepper_xy[i].y + lfd.D); z_measured_min = _MIN(z_measured_min, z_measured[i]); } SERIAL_ECHOLNPGM( - LIST_N(DOUBLE(NUM_Z_STEPPER_DRIVERS), + LIST_N(DOUBLE(NUM_Z_STEPPERS), "Calculated Z1=", z_measured[0], " Z2=", z_measured[1], " Z3=", z_measured[2], @@ -303,7 +309,7 @@ void GcodeSuite::G34() { #if HAS_STATUS_MESSAGE char fstr1[10]; - char msg[6 + (6 + 5) * NUM_Z_STEPPER_DRIVERS + 1] + char msg[6 + (6 + 5) * NUM_Z_STEPPERS + 1] #if TRIPLE_Z , fstr2[10], fstr3[10] #if QUAD_Z @@ -328,25 +334,25 @@ void GcodeSuite::G34() { auto decreasing_accuracy = [](const_float_t v1, const_float_t v2) { if (v1 < v2 * 0.7f) { SERIAL_ECHOLNPGM("Decreasing Accuracy Detected."); - LCD_MESSAGEPGM(MSG_DECREASING_ACCURACY); + LCD_MESSAGE(MSG_DECREASING_ACCURACY); return true; } return false; }; - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY // Check if the applied corrections go in the correct direction. // Calculate the sum of the absolute deviations from the mean of the probe measurements. // Compare to the last iteration to ensure it's getting better. // Calculate mean value as a reference float z_measured_mean = 0.0f; - LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) z_measured_mean += z_measured[zstepper]; - z_measured_mean /= NUM_Z_STEPPER_DRIVERS; + LOOP_L_N(zstepper, NUM_Z_STEPPERS) z_measured_mean += z_measured[zstepper]; + z_measured_mean /= NUM_Z_STEPPERS; // Calculate the sum of the absolute deviations from the mean value float z_align_level_indicator = 0.0f; - LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) + LOOP_L_N(zstepper, NUM_Z_STEPPERS) z_align_level_indicator += ABS(z_measured[zstepper] - z_measured_mean); // If it's getting worse, stop and throw an error @@ -361,12 +367,12 @@ void GcodeSuite::G34() { bool success_break = true; // Correct the individual stepper offsets - LOOP_L_N(zstepper, NUM_Z_STEPPER_DRIVERS) { + LOOP_L_N(zstepper, NUM_Z_STEPPERS) { // Calculate current stepper move float z_align_move = z_measured[zstepper] - z_measured_min; const float z_align_abs = ABS(z_align_move); - #if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if !HAS_Z_STEPPER_ALIGN_STEPPER_XY // Optimize one iteration's correction based on the first measurements if (z_align_abs) amplification = (iteration == 1) ? _MIN(last_z_align_move[zstepper] / z_align_abs, 2.0f) : z_auto_align_amplification; @@ -390,7 +396,7 @@ void GcodeSuite::G34() { // Lock all steppers except one stepper.set_all_z_lock(true, zstepper); - #if DISABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) + #if !HAS_Z_STEPPER_ALIGN_STEPPER_XY // 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) { @@ -411,7 +417,7 @@ void GcodeSuite::G34() { if (success_break) { SERIAL_ECHOLNPGM("Target accuracy achieved."); - LCD_MESSAGEPGM(MSG_ACCURACY_ACHIEVED); + LCD_MESSAGE(MSG_ACCURACY_ACHIEVED); break; } @@ -433,7 +439,7 @@ void GcodeSuite::G34() { // After this operation the z position needs correction set_axis_never_homed(Z_AXIS); // Home Z after the alignment procedure - process_subcommands_now_P(PSTR("G28Z")); + process_subcommands_now(F("G28Z")); #else // 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. @@ -463,7 +469,7 @@ void GcodeSuite::G34() { * * S : Index of the probe point to set * - * With Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS: + * With Z_STEPPER_ALIGN_STEPPER_XY: * W : Index of the Z stepper position to set * The W and S parameters may not be combined. * @@ -482,42 +488,43 @@ void GcodeSuite::M422() { return; } - const bool is_probe_point = parser.seen('S'); + const bool is_probe_point = parser.seen_test('S'); - if (TERN0(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, is_probe_point && parser.seen('W'))) { + if (TERN0(HAS_Z_STEPPER_ALIGN_STEPPER_XY, is_probe_point && parser.seen_test('W'))) { SERIAL_ECHOLNPGM("?(S) and (W) may not be combined."); return; } - xy_pos_t *pos_dest = ( - TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !is_probe_point ? z_stepper_align.stepper_xy :) + xy_pos_t * const pos_dest = ( + TERN_(HAS_Z_STEPPER_ALIGN_STEPPER_XY, !is_probe_point ? z_stepper_align.stepper_xy :) z_stepper_align.xy ); - if (!is_probe_point && TERN1(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, !parser.seen('W'))) { - SERIAL_ECHOLNPGM("?(S)" TERN_(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS, " or (W)") " is required."); + if (!is_probe_point && TERN1(HAS_Z_STEPPER_ALIGN_STEPPER_XY, !parser.seen_test('W'))) { + SERIAL_ECHOLNPGM("?(S)" TERN_(HAS_Z_STEPPER_ALIGN_STEPPER_XY, " or (W)") " is required."); return; } // Get the Probe Position Index or Z Stepper Index - int8_t position_index; - if (is_probe_point) { - position_index = parser.intval('S') - 1; - if (!WITHIN(position_index, 0, int8_t(NUM_Z_STEPPER_DRIVERS) - 1)) { - SERIAL_ECHOLNPGM("?(S) Probe-position index invalid."); - return; - } - } + int8_t position_index = 1; + FSTR_P err_string = F("?(S) Probe-position"); + if (is_probe_point) + position_index = parser.intval('S'); else { - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - position_index = parser.intval('W') - 1; - if (!WITHIN(position_index, 0, NUM_Z_STEPPER_DRIVERS - 1)) { - SERIAL_ECHOLNPGM("?(W) Z-stepper index invalid."); - return; - } + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY + err_string = F("?(W) Z-stepper"); + position_index = parser.intval('W'); #endif } + if (!WITHIN(position_index, 1, NUM_Z_STEPPERS)) { + SERIAL_ECHOF(err_string); + SERIAL_ECHOLNPGM(" index invalid (1.." STRINGIFY(NUM_Z_STEPPERS) ")."); + return; + } + + --position_index; + const xy_pos_t pos = { parser.floatval('X', pos_dest[position_index].x), parser.floatval('Y', pos_dest[position_index].y) @@ -538,8 +545,8 @@ void GcodeSuite::M422() { } void GcodeSuite::M422_report(const bool forReplay/*=true*/) { - report_heading(forReplay, PSTR(STR_Z_AUTO_ALIGN)); - LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) { + report_heading(forReplay, F(STR_Z_AUTO_ALIGN)); + LOOP_L_N(i, NUM_Z_STEPPERS) { report_echo_start(forReplay); SERIAL_ECHOLNPGM_P( PSTR(" M422 S"), i + 1, @@ -547,8 +554,8 @@ void GcodeSuite::M422_report(const bool forReplay/*=true*/) { SP_Y_STR, z_stepper_align.xy[i].y ); } - #if ENABLED(Z_STEPPER_ALIGN_KNOWN_STEPPER_POSITIONS) - LOOP_L_N(i, NUM_Z_STEPPER_DRIVERS) { + #if HAS_Z_STEPPER_ALIGN_STEPPER_XY + LOOP_L_N(i, NUM_Z_STEPPERS) { report_echo_start(forReplay); SERIAL_ECHOLNPGM_P( PSTR(" M422 W"), i + 1, diff --git a/Marlin/src/gcode/calibrate/G425.cpp b/Marlin/src/gcode/calibrate/G425.cpp index 23a66dd0c5931..f4d05ae89e5b8 100644 --- a/Marlin/src/gcode/calibrate/G425.cpp +++ b/Marlin/src/gcode/calibrate/G425.cpp @@ -73,22 +73,22 @@ #if BOTH(CALIBRATION_MEASURE_LEFT, CALIBRATION_MEASURE_RIGHT) #define HAS_X_CENTER 1 #endif -#if HAS_Y_AXIS && BOTH(CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK) +#if ALL(HAS_Y_AXIS, CALIBRATION_MEASURE_FRONT, CALIBRATION_MEASURE_BACK) #define HAS_Y_CENTER 1 #endif -#if LINEAR_AXES >= 4 && BOTH(CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX) +#if ALL(HAS_I_AXIS, CALIBRATION_MEASURE_IMIN, CALIBRATION_MEASURE_IMAX) #define HAS_I_CENTER 1 #endif -#if LINEAR_AXES >= 5 && BOTH(CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX) +#if ALL(HAS_J_AXIS, CALIBRATION_MEASURE_JMIN, CALIBRATION_MEASURE_JMAX) #define HAS_J_CENTER 1 #endif -#if LINEAR_AXES >= 6 && BOTH(CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX) +#if ALL(HAS_K_AXIS, CALIBRATION_MEASURE_KMIN, CALIBRATION_MEASURE_KMAX) #define HAS_K_CENTER 1 #endif enum side_t : uint8_t { TOP, RIGHT, FRONT, LEFT, BACK, NUM_SIDES, - LIST_N(DOUBLE(SUB3(LINEAR_AXES)), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM) + LIST_N(DOUBLE(SECONDARY_AXES), IMINIMUM, IMAXIMUM, JMINIMUM, JMAXIMUM, KMINIMUM, KMAXIMUM) }; static constexpr xyz_pos_t true_center CALIBRATION_OBJECT_CENTER; @@ -105,13 +105,27 @@ struct measurements_t { }; #if ENABLED(BACKLASH_GCODE) - #define TEMPORARY_BACKLASH_CORRECTION(value) REMEMBER(tbst, backlash.correction, value) + class restorer_correction { + const uint8_t val_; + public: + restorer_correction(const uint8_t temp_val) : val_(backlash.get_correction_uint8()) { backlash.set_correction_uint8(temp_val); } + ~restorer_correction() { backlash.set_correction_uint8(val_); } + }; + + #define TEMPORARY_BACKLASH_CORRECTION(value) restorer_correction restorer_tbst(value) #else #define TEMPORARY_BACKLASH_CORRECTION(value) #endif #if ENABLED(BACKLASH_GCODE) && defined(BACKLASH_SMOOTHING_MM) - #define TEMPORARY_BACKLASH_SMOOTHING(value) REMEMBER(tbsm, backlash.smoothing_mm, value) + class restorer_smoothing { + const float val_; + public: + restorer_smoothing(const float temp_val) : val_(backlash.get_smoothing_mm()) { backlash.set_smoothing_mm(temp_val); } + ~restorer_smoothing() { backlash.set_smoothing_mm(val_); } + }; + + #define TEMPORARY_BACKLASH_SMOOTHING(value) restorer_smoothing restorer_tbsm(value) #else #define TEMPORARY_BACKLASH_SMOOTHING(value) #endif @@ -241,14 +255,15 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t park_above_object(m, uncertainty); + #define _ACASE(N,A,B) case A: dir = -1; case B: axis = N##_AXIS; break + #define _PCASE(N) _ACASE(N, N##MINIMUM, N##MAXIMUM) + switch (side) { #if AXIS_CAN_CALIBRATE(X) - case RIGHT: dir = -1; - case LEFT: axis = X_AXIS; break; + _ACASE(X, RIGHT, LEFT); #endif - #if LINEAR_AXES >= 2 && AXIS_CAN_CALIBRATE(Y) - case BACK: dir = -1; - case FRONT: axis = Y_AXIS; break; + #if HAS_Y_AXIS && AXIS_CAN_CALIBRATE(Y) + _ACASE(Y, BACK, FRONT); #endif #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z) case TOP: { @@ -258,17 +273,14 @@ inline void probe_side(measurements_t &m, const float uncertainty, const side_t return; } #endif - #if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I) - case IMINIMUM: dir = -1; - case IMAXIMUM: axis = I_AXIS; break; + #if HAS_I_AXIS && AXIS_CAN_CALIBRATE(I) + _PCASE(I); #endif - #if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J) - case JMINIMUM: dir = -1; - case JMAXIMUM: axis = J_AXIS; break; + #if HAS_J_AXIS && AXIS_CAN_CALIBRATE(J) + _PCASE(J); #endif - #if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K) - case KMINIMUM: dir = -1; - case KMAXIMUM: axis = K_AXIS; break; + #if HAS_K_AXIS && AXIS_CAN_CALIBRATE(K) + _PCASE(K); #endif default: return; } @@ -340,7 +352,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { // The difference between the known and the measured location // of the calibration object is the positional error - LINEAR_AXIS_CODE( + NUM_AXIS_CODE( m.pos_error.x = TERN0(HAS_X_CENTER, true_center.x - m.obj_center.x), m.pos_error.y = TERN0(HAS_Y_CENTER, true_center.y - m.obj_center.y), m.pos_error.z = true_center.z - m.obj_center.z, @@ -370,7 +382,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { SERIAL_ECHOLNPGM(" Back: ", m.obj_side[BACK]); #endif #endif - #if LINEAR_AXES >= 4 + #if HAS_I_AXIS #if ENABLED(CALIBRATION_MEASURE_IMIN) SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.obj_side[IMINIMUM]); #endif @@ -378,7 +390,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.obj_side[IMAXIMUM]); #endif #endif - #if LINEAR_AXES >= 5 + #if HAS_J_AXIS #if ENABLED(CALIBRATION_MEASURE_JMIN) SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.obj_side[JMINIMUM]); #endif @@ -386,7 +398,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.obj_side[JMAXIMUM]); #endif #endif - #if LINEAR_AXES >= 6 + #if HAS_K_AXIS #if ENABLED(CALIBRATION_MEASURE_KMIN) SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.obj_side[KMINIMUM]); #endif @@ -439,7 +451,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { #if HAS_Z_AXIS && AXIS_CAN_CALIBRATE(Z) SERIAL_ECHOLNPGM(" Top: ", m.backlash[TOP]); #endif - #if LINEAR_AXES >= 4 && AXIS_CAN_CALIBRATE(I) + #if HAS_I_AXIS && AXIS_CAN_CALIBRATE(I) #if ENABLED(CALIBRATION_MEASURE_IMIN) SERIAL_ECHOLNPGM(" " STR_I_MIN ": ", m.backlash[IMINIMUM]); #endif @@ -447,7 +459,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { SERIAL_ECHOLNPGM(" " STR_I_MAX ": ", m.backlash[IMAXIMUM]); #endif #endif - #if LINEAR_AXES >= 5 && AXIS_CAN_CALIBRATE(J) + #if HAS_J_AXIS && AXIS_CAN_CALIBRATE(J) #if ENABLED(CALIBRATION_MEASURE_JMIN) SERIAL_ECHOLNPGM(" " STR_J_MIN ": ", m.backlash[JMINIMUM]); #endif @@ -455,7 +467,7 @@ inline void probe_sides(measurements_t &m, const float uncertainty) { SERIAL_ECHOLNPGM(" " STR_J_MAX ": ", m.backlash[JMAXIMUM]); #endif #endif - #if LINEAR_AXES >= 6 && AXIS_CAN_CALIBRATE(K) + #if HAS_K_AXIS && AXIS_CAN_CALIBRATE(K) #if ENABLED(CALIBRATION_MEASURE_KMIN) SERIAL_ECHOLNPGM(" " STR_K_MIN ": ", m.backlash[KMINIMUM]); #endif @@ -526,7 +538,7 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) { { // New scope for TEMPORARY_BACKLASH_CORRECTION - TEMPORARY_BACKLASH_CORRECTION(all_off); + TEMPORARY_BACKLASH_CORRECTION(backlash.all_off); TEMPORARY_BACKLASH_SMOOTHING(0.0f); probe_sides(m, uncertainty); @@ -534,45 +546,45 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) { #if ENABLED(BACKLASH_GCODE) #if HAS_X_CENTER - backlash.distance_mm.x = (m.backlash[LEFT] + m.backlash[RIGHT]) / 2; + backlash.set_distance_mm(X_AXIS, (m.backlash[LEFT] + m.backlash[RIGHT]) / 2); #elif ENABLED(CALIBRATION_MEASURE_LEFT) - backlash.distance_mm.x = m.backlash[LEFT]; + backlash.set_distance_mm(X_AXIS, m.backlash[LEFT]); #elif ENABLED(CALIBRATION_MEASURE_RIGHT) - backlash.distance_mm.x = m.backlash[RIGHT]; + backlash.set_distance_mm(X_AXIS, m.backlash[RIGHT]); #endif #if HAS_Y_CENTER - backlash.distance_mm.y = (m.backlash[FRONT] + m.backlash[BACK]) / 2; + backlash.set_distance_mm(Y_AXIS, (m.backlash[FRONT] + m.backlash[BACK]) / 2); #elif ENABLED(CALIBRATION_MEASURE_FRONT) - backlash.distance_mm.y = m.backlash[FRONT]; + backlash.set_distance_mm(Y_AXIS, m.backlash[FRONT]); #elif ENABLED(CALIBRATION_MEASURE_BACK) - backlash.distance_mm.y = m.backlash[BACK]; + backlash.set_distance_mm(Y_AXIS, m.backlash[BACK]); #endif - TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.distance_mm.z = m.backlash[TOP]); + TERN_(HAS_Z_AXIS, if (AXIS_CAN_CALIBRATE(Z)) backlash.set_distance_mm(Z_AXIS, m.backlash[TOP])); #if HAS_I_CENTER - backlash.distance_mm.i = (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2; + backlash.set_distance_mm(I_AXIS, (m.backlash[IMINIMUM] + m.backlash[IMAXIMUM]) / 2); #elif ENABLED(CALIBRATION_MEASURE_IMIN) - backlash.distance_mm.i = m.backlash[IMINIMUM]; + backlash.set_distance_mm(I_AXIS, m.backlash[IMINIMUM]); #elif ENABLED(CALIBRATION_MEASURE_IMAX) - backlash.distance_mm.i = m.backlash[IMAXIMUM]; + backlash.set_distance_mm(I_AXIS, m.backlash[IMAXIMUM]); #endif #if HAS_J_CENTER - backlash.distance_mm.j = (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2; + backlash.set_distance_mm(J_AXIS, (m.backlash[JMINIMUM] + m.backlash[JMAXIMUM]) / 2); #elif ENABLED(CALIBRATION_MEASURE_JMIN) - backlash.distance_mm.j = m.backlash[JMINIMUM]; + backlash.set_distance_mm(J_AXIS, m.backlash[JMINIMUM]); #elif ENABLED(CALIBRATION_MEASURE_JMAX) - backlash.distance_mm.j = m.backlash[JMAXIMUM]; + backlash.set_distance_mm(J_AXIS, m.backlash[JMAXIMUM]); #endif #if HAS_K_CENTER - backlash.distance_mm.k = (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2; + backlash.set_distance_mm(K_AXIS, (m.backlash[KMINIMUM] + m.backlash[KMAXIMUM]) / 2); #elif ENABLED(CALIBRATION_MEASURE_KMIN) - backlash.distance_mm.k = m.backlash[KMINIMUM]; + backlash.set_distance_mm(K_AXIS, m.backlash[KMINIMUM]); #elif ENABLED(CALIBRATION_MEASURE_KMAX) - backlash.distance_mm.k = m.backlash[KMAXIMUM]; + backlash.set_distance_mm(K_AXIS, m.backlash[KMAXIMUM]); #endif #endif // BACKLASH_GCODE @@ -583,9 +595,9 @@ inline void calibrate_backlash(measurements_t &m, const float uncertainty) { // allowed directions to take up any backlash { // New scope for TEMPORARY_BACKLASH_CORRECTION - TEMPORARY_BACKLASH_CORRECTION(all_on); + TEMPORARY_BACKLASH_CORRECTION(backlash.all_on); TEMPORARY_BACKLASH_SMOOTHING(0.0f); - const xyz_float_t move = LINEAR_AXIS_ARRAY( + const xyz_float_t move = NUM_AXIS_ARRAY( AXIS_CAN_CALIBRATE(X) * 3, AXIS_CAN_CALIBRATE(Y) * 3, AXIS_CAN_CALIBRATE(Z) * 3, AXIS_CAN_CALIBRATE(I) * 3, AXIS_CAN_CALIBRATE(J) * 3, AXIS_CAN_CALIBRATE(K) * 3 ); @@ -613,7 +625,7 @@ inline void update_measurements(measurements_t &m, const AxisEnum axis) { * - Call calibrate_backlash() beforehand for best accuracy */ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const uint8_t extruder) { - TEMPORARY_BACKLASH_CORRECTION(all_on); + TEMPORARY_BACKLASH_CORRECTION(backlash.all_on); TEMPORARY_BACKLASH_SMOOTHING(0.0f); TERN(HAS_MULTI_HOTEND, set_nozzle(m, extruder), UNUSED(extruder)); @@ -650,7 +662,7 @@ inline void calibrate_toolhead(measurements_t &m, const float uncertainty, const * uncertainty in - How far away from the object to begin probing */ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty) { - TEMPORARY_BACKLASH_CORRECTION(all_on); + TEMPORARY_BACKLASH_CORRECTION(backlash.all_on); TEMPORARY_BACKLASH_SMOOTHING(0.0f); HOTEND_LOOP() calibrate_toolhead(m, uncertainty, e); @@ -666,7 +678,7 @@ inline void calibrate_all_toolheads(measurements_t &m, const float uncertainty) * 1) For each nozzle, touch top and sides of object to determine object position and * nozzle offsets. Do a fast but rough search over a wider area. * 2) With the first nozzle, touch top and sides of object to determine backlash values - * for all axis (if BACKLASH_GCODE is enabled) + * for all axes (if BACKLASH_GCODE is enabled) * 3) For each nozzle, touch top and sides of object slowly to determine precise * position of object. Adjust coordinate system and nozzle offsets so probed object * location corresponds to known object location with a high degree of precision. @@ -676,7 +688,7 @@ inline void calibrate_all() { TERN_(HAS_HOTEND_OFFSET, reset_hotend_offsets()); - TEMPORARY_BACKLASH_CORRECTION(all_on); + TEMPORARY_BACKLASH_CORRECTION(backlash.all_on); TEMPORARY_BACKLASH_SMOOTHING(0.0f); // Do a fast and rough calibration of the toolheads @@ -709,7 +721,7 @@ inline void calibrate_all() { void GcodeSuite::G425() { #ifdef CALIBRATION_SCRIPT_PRE - GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_PRE)); + process_subcommands_now(F(CALIBRATION_SCRIPT_PRE)); #endif if (homing_needed_error()) return; @@ -745,7 +757,7 @@ void GcodeSuite::G425() { SET_SOFT_ENDSTOP_LOOSE(false); #ifdef CALIBRATION_SCRIPT_POST - GcodeSuite::process_subcommands_now_P(PSTR(CALIBRATION_SCRIPT_POST)); + process_subcommands_now(F(CALIBRATION_SCRIPT_POST)); #endif } diff --git a/Marlin/src/gcode/calibrate/G76_M192_M871.cpp b/Marlin/src/gcode/calibrate/G76_M192_M871.cpp deleted file mode 100644 index 946701050e704..0000000000000 --- a/Marlin/src/gcode/calibrate/G76_M192_M871.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/** - * Marlin 3D Printer Firmware - * Copyright (c) 2020 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 . - * - */ - -/** - * G76_M871.cpp - Temperature calibration/compensation for z-probing - */ - -#include "../../inc/MarlinConfig.h" - -#if ENABLED(PROBE_TEMP_COMPENSATION) - -#include "../gcode.h" -#include "../../module/motion.h" -#include "../../module/planner.h" -#include "../../module/probe.h" -#include "../../feature/bedlevel/bedlevel.h" -#include "../../module/temperature.h" -#include "../../module/probe.h" -#include "../../feature/probe_temp_comp.h" -#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. - * - * 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. - * - * 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. - * - * G76 [B | P] - * - no flag - Both calibration procedures will be run. - * - `B` - Run bed temperature calibration. - * - `P` - Run probe temperature calibration. - */ - -static void say_waiting_for() { SERIAL_ECHOPGM("Waiting for "); } -static void say_waiting_for_probe_heating() { say_waiting_for(); SERIAL_ECHOLNPGM("probe heating."); } -static void say_successfully_calibrated() { SERIAL_ECHOPGM("Successfully calibrated"); } -static void say_failed_to_calibrate() { SERIAL_ECHOPGM("!Failed to calibrate"); } - -void GcodeSuite::G76() { - // Check if heated bed is available and z-homing is done with probe - #if TEMP_SENSOR_BED == 0 || !(HOMING_Z_WITH_PROBE) - return; - #endif - - auto report_temps = [](millis_t &ntr, millis_t timeout=0) { - idle_no_sleep(); - const millis_t ms = millis(); - if (ELAPSED(ms, ntr)) { - ntr = ms + 1000; - thermalManager.print_heater_states(active_extruder); - } - return (timeout && ELAPSED(ms, timeout)); - }; - - auto wait_for_temps = [&](const celsius_t tb, const celsius_t tp, millis_t &ntr, const millis_t timeout=0) { - say_waiting_for(); SERIAL_ECHOLNPGM("bed and probe temperature."); - while (thermalManager.wholeDegBed() != tb || thermalManager.wholeDegProbe() > tp) - if (report_temps(ntr, timeout)) return true; - return false; - }; - - auto g76_probe = [](const TempSensorID sid, celsius_t &targ, const xy_pos_t &nozpos) { - do_z_clearance(5.0); // Raise nozzle before probing - const float measured_z = probe.probe_at_point(nozpos, PROBE_PT_STOW, 0, false); // verbose=0, probe_relative=false - if (isnan(measured_z)) - SERIAL_ECHOLNPGM("!Received NAN. Aborting."); - else { - SERIAL_ECHOLNPAIR_F("Measured: ", measured_z); - if (targ == cali_info_init[sid].start_temp) - temp_comp.prepare_new_calibration(measured_z); - else - temp_comp.push_back_new_measurement(sid, measured_z); - targ += cali_info_init[sid].temp_resolution; - } - return measured_z; - }; - - #if ENABLED(BLTOUCH) - // Make sure any BLTouch error condition is cleared - bltouch_command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); - set_bltouch_deployed(false); - #endif - - bool do_bed_cal = parser.boolval('B'), do_probe_cal = parser.boolval('P'); - if (!do_bed_cal && !do_probe_cal) do_bed_cal = do_probe_cal = true; - - // Synchronize with planner - planner.synchronize(); - - const xyz_pos_t parkpos = temp_comp.park_point, - probe_pos_xyz = xyz_pos_t(temp_comp.measure_point) + xyz_pos_t({ 0.0f, 0.0f, PTC_PROBE_HEATING_OFFSET }), - noz_pos_xyz = probe_pos_xyz - probe.offset_xy; // Nozzle position based on probe position - - if (do_bed_cal || do_probe_cal) { - // Ensure park position is reachable - bool reachable = position_is_reachable(parkpos) || WITHIN(parkpos.z, Z_MIN_POS - fslop, Z_MAX_POS + fslop); - if (!reachable) - SERIAL_ECHOLNPGM("!Park"); - else { - // Ensure probe position is reachable - reachable = probe.can_reach(probe_pos_xyz); - if (!reachable) SERIAL_ECHOLNPGM("!Probe"); - } - - if (!reachable) { - SERIAL_ECHOLNPGM(" position unreachable - aborting."); - return; - } - - process_subcommands_now_P(G28_STR); - } - - remember_feedrate_scaling_off(); - - /****************************************** - * Calibrate bed temperature offsets - ******************************************/ - - // Report temperatures every second and handle heating timeouts - millis_t next_temp_report = millis() + 1000; - - auto report_targets = [&](const celsius_t tb, const celsius_t tp) { - SERIAL_ECHOLNPGM("Target Bed:", tb, " Probe:", tp); - }; - - if (do_bed_cal) { - - celsius_t target_bed = cali_info_init[TSI_BED].start_temp, - target_probe = temp_comp.bed_calib_probe_temp; - - say_waiting_for(); SERIAL_ECHOLNPGM(" cooling."); - while (thermalManager.wholeDegBed() > target_bed || thermalManager.wholeDegProbe() > target_probe) - report_temps(next_temp_report); - - // Disable leveling so it won't mess with us - TERN_(HAS_LEVELING, set_bed_leveling_enabled(false)); - - for (;;) { - thermalManager.setTargetBed(target_bed); - - report_targets(target_bed, target_probe); - - // Park nozzle - do_blocking_move_to(parkpos); - - // Wait for heatbed to reach target temp and probe to cool below target temp - if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + MIN_TO_MS(15))) { - SERIAL_ECHOLNPGM("!Bed heating timeout."); - break; - } - - // Move the nozzle to the probing point and wait for the probe to reach target temp - do_blocking_move_to(noz_pos_xyz); - say_waiting_for_probe_heating(); - SERIAL_EOL(); - while (thermalManager.wholeDegProbe() < target_probe) - report_temps(next_temp_report); - - const float measured_z = g76_probe(TSI_BED, target_bed, noz_pos_xyz); - if (isnan(measured_z) || target_bed > (BED_MAX_TARGET)) break; - } - - SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index()); - if (temp_comp.finish_calibration(TSI_BED)) { - say_successfully_calibrated(); - SERIAL_ECHOLNPGM(" bed."); - } - else { - say_failed_to_calibrate(); - SERIAL_ECHOLNPGM(" bed. Values reset."); - } - - // Cleanup - thermalManager.setTargetBed(0); - TERN_(HAS_LEVELING, set_bed_leveling_enabled(true)); - } // do_bed_cal - - /******************************************** - * Calibrate probe temperature offsets - ********************************************/ - - if (do_probe_cal) { - - // Park nozzle - do_blocking_move_to(parkpos); - - // Initialize temperatures - const celsius_t target_bed = temp_comp.probe_calib_bed_temp; - thermalManager.setTargetBed(target_bed); - - celsius_t target_probe = cali_info_init[TSI_PROBE].start_temp; - - report_targets(target_bed, target_probe); - - // Wait for heatbed to reach target temp and probe to cool below target temp - wait_for_temps(target_bed, target_probe, next_temp_report); - - // Disable leveling so it won't mess with us - TERN_(HAS_LEVELING, set_bed_leveling_enabled(false)); - - bool timeout = false; - for (;;) { - // Move probe to probing point and wait for it to reach target temperature - do_blocking_move_to(noz_pos_xyz); - - say_waiting_for_probe_heating(); - SERIAL_ECHOLNPGM(" Bed:", target_bed, " Probe:", target_probe); - const millis_t probe_timeout_ms = millis() + SEC_TO_MS(900UL); - while (thermalManager.degProbe() < target_probe) { - if (report_temps(next_temp_report, probe_timeout_ms)) { - SERIAL_ECHOLNPGM("!Probe heating timed out."); - timeout = true; - break; - } - } - if (timeout) break; - - const float measured_z = g76_probe(TSI_PROBE, target_probe, noz_pos_xyz); - if (isnan(measured_z) || target_probe > cali_info_init[TSI_PROBE].end_temp) break; - } - - SERIAL_ECHOLNPGM("Retrieved measurements: ", temp_comp.get_index()); - if (temp_comp.finish_calibration(TSI_PROBE)) - say_successfully_calibrated(); - else - say_failed_to_calibrate(); - SERIAL_ECHOLNPGM(" probe."); - - // Cleanup - thermalManager.setTargetBed(0); - TERN_(HAS_LEVELING, set_bed_leveling_enabled(true)); - - SERIAL_ECHOLNPGM("Final compensation values:"); - temp_comp.print_offsets(); - } // do_probe_cal - - restore_feedrate_and_scaling(); -} - -/** - * M871: Report / reset temperature compensation offsets. - * Note: This does not affect values in EEPROM until M500. - * - * M871 [ R | B | P | E ] - * - * No Parameters - 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 - * - * With B, P, or E: - * I[index] - Index in the array - * V[value] - Adjustment in µm - */ -void GcodeSuite::M871() { - - if (parser.seen('R')) { - // Reset z-probe offsets to factory defaults - temp_comp.clear_all_offsets(); - SERIAL_ECHOLNPGM("Offsets reset to default."); - } - else if (parser.seen("BPE")) { - if (!parser.seenval('V')) return; - const int16_t offset_val = parser.value_int(); - if (!parser.seenval('I')) return; - const int16_t idx = parser.value_int(); - const TempSensorID mod = (parser.seen('B') ? TSI_BED : - #if ENABLED(USE_TEMP_EXT_COMPENSATION) - parser.seen('E') ? TSI_EXT : - #endif - TSI_PROBE - ); - if (idx > 0 && temp_comp.set_offset(mod, idx - 1, offset_val)) - SERIAL_ECHOLNPGM("Set value: ", offset_val); - else - SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant)."); - - } - else // Print current Z-probe adjustments. Note: Values in EEPROM might differ. - temp_comp.print_offsets(); -} - -/** - * M192: Wait for probe temperature sensor to reach a target - * - * Select only one of these flags: - * R - Wait for heating or cooling - * S - Wait only for heating - */ -void GcodeSuite::M192() { - if (DEBUGGING(DRYRUN)) return; - - const bool no_wait_for_cooling = parser.seenval('S'); - if (!no_wait_for_cooling && ! parser.seenval('R')) { - SERIAL_ERROR_MSG("No target temperature set."); - return; - } - - const celsius_t target_temp = parser.value_celsius(); - ui.set_status_P(thermalManager.isProbeBelowTemp(target_temp) ? GET_TEXT(MSG_PROBE_HEATING) : GET_TEXT(MSG_PROBE_COOLING)); - thermalManager.wait_for_probe(target_temp, no_wait_for_cooling); -} - -#endif // PROBE_TEMP_COMPENSATION diff --git a/Marlin/src/gcode/calibrate/G76_M871.cpp b/Marlin/src/gcode/calibrate/G76_M871.cpp new file mode 100644 index 0000000000000..c484d4f1b770e --- /dev/null +++ b/Marlin/src/gcode/calibrate/G76_M871.cpp @@ -0,0 +1,339 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2020 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 . + * + */ + +/** + * G76_M871.cpp - Temperature calibration/compensation for z-probing + */ + +#include "../../inc/MarlinConfig.h" + +#if HAS_PTC + +#include "../gcode.h" +#include "../../module/motion.h" +#include "../../module/planner.h" +#include "../../module/probe.h" +#include "../../feature/bedlevel/bedlevel.h" +#include "../../module/temperature.h" +#include "../../module/probe.h" +#include "../../feature/probe_temp_comp.h" +#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. + * + * 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. + * + * 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. + * + * G76 [B | P] + * - no flag - Both calibration procedures will be run. + * - `B` - Run bed temperature calibration. + * - `P` - Run probe temperature calibration. + */ + +#if BOTH(PTC_PROBE, PTC_BED) + + static void say_waiting_for() { SERIAL_ECHOPGM("Waiting for "); } + static void say_waiting_for_probe_heating() { say_waiting_for(); SERIAL_ECHOLNPGM("probe heating."); } + static void say_successfully_calibrated() { SERIAL_ECHOPGM("Successfully calibrated"); } + static void say_failed_to_calibrate() { SERIAL_ECHOPGM("!Failed to calibrate"); } + + void GcodeSuite::G76() { + auto report_temps = [](millis_t &ntr, millis_t timeout=0) { + idle_no_sleep(); + const millis_t ms = millis(); + if (ELAPSED(ms, ntr)) { + ntr = ms + 1000; + thermalManager.print_heater_states(active_extruder); + } + return (timeout && ELAPSED(ms, timeout)); + }; + + auto wait_for_temps = [&](const celsius_t tb, const celsius_t tp, millis_t &ntr, const millis_t timeout=0) { + say_waiting_for(); SERIAL_ECHOLNPGM("bed and probe temperature."); + while (thermalManager.wholeDegBed() != tb || thermalManager.wholeDegProbe() > tp) + if (report_temps(ntr, timeout)) return true; + return false; + }; + + auto g76_probe = [](const TempSensorID sid, celsius_t &targ, const xy_pos_t &nozpos) { + do_z_clearance(5.0); // Raise nozzle before probing + ptc.set_enabled(false); + const float measured_z = probe.probe_at_point(nozpos, PROBE_PT_STOW, 0, false); // verbose=0, probe_relative=false + ptc.set_enabled(true); + if (isnan(measured_z)) + SERIAL_ECHOLNPGM("!Received NAN. Aborting."); + else { + SERIAL_ECHOLNPAIR_F("Measured: ", measured_z); + if (targ == ProbeTempComp::cali_info[sid].start_temp) + ptc.prepare_new_calibration(measured_z); + else + ptc.push_back_new_measurement(sid, measured_z); + targ += ProbeTempComp::cali_info[sid].temp_resolution; + } + return measured_z; + }; + + #if ENABLED(BLTOUCH) + // Make sure any BLTouch error condition is cleared + bltouch_command(BLTOUCH_RESET, BLTOUCH_RESET_DELAY); + set_bltouch_deployed(false); + #endif + + bool do_bed_cal = parser.boolval('B'), do_probe_cal = parser.boolval('P'); + if (!do_bed_cal && !do_probe_cal) do_bed_cal = do_probe_cal = true; + + // Synchronize with planner + planner.synchronize(); + + #ifndef PTC_PROBE_HEATING_OFFSET + #define PTC_PROBE_HEATING_OFFSET 0 + #endif + const xyz_pos_t parkpos = PTC_PARK_POS, + probe_pos_xyz = xyz_pos_t(PTC_PROBE_POS) + xyz_pos_t({ 0.0f, 0.0f, PTC_PROBE_HEATING_OFFSET }), + noz_pos_xyz = probe_pos_xyz - probe.offset_xy; // Nozzle position based on probe position + + if (do_bed_cal || do_probe_cal) { + // Ensure park position is reachable + bool reachable = position_is_reachable(parkpos) || WITHIN(parkpos.z, Z_MIN_POS - fslop, Z_MAX_POS + fslop); + if (!reachable) + SERIAL_ECHOLNPGM("!Park"); + else { + // Ensure probe position is reachable + reachable = probe.can_reach(probe_pos_xyz); + if (!reachable) SERIAL_ECHOLNPGM("!Probe"); + } + + if (!reachable) { + SERIAL_ECHOLNPGM(" position unreachable - aborting."); + return; + } + + process_subcommands_now(FPSTR(G28_STR)); + } + + remember_feedrate_scaling_off(); + + /****************************************** + * Calibrate bed temperature offsets + ******************************************/ + + // Report temperatures every second and handle heating timeouts + millis_t next_temp_report = millis() + 1000; + + auto report_targets = [&](const celsius_t tb, const celsius_t tp) { + SERIAL_ECHOLNPGM("Target Bed:", tb, " Probe:", tp); + }; + + if (do_bed_cal) { + + celsius_t target_bed = PTC_BED_START, + target_probe = PTC_PROBE_TEMP; + + say_waiting_for(); SERIAL_ECHOLNPGM(" cooling."); + while (thermalManager.wholeDegBed() > target_bed || thermalManager.wholeDegProbe() > target_probe) + report_temps(next_temp_report); + + // Disable leveling so it won't mess with us + TERN_(HAS_LEVELING, set_bed_leveling_enabled(false)); + + for (uint8_t idx = 0; idx <= PTC_BED_COUNT; idx++) { + thermalManager.setTargetBed(target_bed); + + report_targets(target_bed, target_probe); + + // Park nozzle + do_blocking_move_to(parkpos); + + // Wait for heatbed to reach target temp and probe to cool below target temp + if (wait_for_temps(target_bed, target_probe, next_temp_report, millis() + MIN_TO_MS(15))) { + SERIAL_ECHOLNPGM("!Bed heating timeout."); + break; + } + + // Move the nozzle to the probing point and wait for the probe to reach target temp + do_blocking_move_to(noz_pos_xyz); + say_waiting_for_probe_heating(); + SERIAL_EOL(); + while (thermalManager.wholeDegProbe() < target_probe) + report_temps(next_temp_report); + + const float measured_z = g76_probe(TSI_BED, target_bed, noz_pos_xyz); + if (isnan(measured_z) || target_bed > (BED_MAX_TARGET)) break; + } + + SERIAL_ECHOLNPGM("Retrieved measurements: ", ptc.get_index()); + if (ptc.finish_calibration(TSI_BED)) { + say_successfully_calibrated(); + SERIAL_ECHOLNPGM(" bed."); + } + else { + say_failed_to_calibrate(); + SERIAL_ECHOLNPGM(" bed. Values reset."); + } + + // Cleanup + thermalManager.setTargetBed(0); + TERN_(HAS_LEVELING, set_bed_leveling_enabled(true)); + } // do_bed_cal + + /******************************************** + * Calibrate probe temperature offsets + ********************************************/ + + if (do_probe_cal) { + + // Park nozzle + do_blocking_move_to(parkpos); + + // Initialize temperatures + const celsius_t target_bed = BED_MAX_TARGET; + thermalManager.setTargetBed(target_bed); + + celsius_t target_probe = PTC_PROBE_START; + + report_targets(target_bed, target_probe); + + // Wait for heatbed to reach target temp and probe to cool below target temp + wait_for_temps(target_bed, target_probe, next_temp_report); + + // Disable leveling so it won't mess with us + TERN_(HAS_LEVELING, set_bed_leveling_enabled(false)); + + bool timeout = false; + for (uint8_t idx = 0; idx <= PTC_PROBE_COUNT; idx++) { + // Move probe to probing point and wait for it to reach target temperature + do_blocking_move_to(noz_pos_xyz); + + say_waiting_for_probe_heating(); + SERIAL_ECHOLNPGM(" Bed:", target_bed, " Probe:", target_probe); + const millis_t probe_timeout_ms = millis() + SEC_TO_MS(900UL); + while (thermalManager.degProbe() < target_probe) { + if (report_temps(next_temp_report, probe_timeout_ms)) { + SERIAL_ECHOLNPGM("!Probe heating timed out."); + timeout = true; + break; + } + } + if (timeout) break; + + const float measured_z = g76_probe(TSI_PROBE, target_probe, noz_pos_xyz); + if (isnan(measured_z)) break; + } + + SERIAL_ECHOLNPGM("Retrieved measurements: ", ptc.get_index()); + if (ptc.finish_calibration(TSI_PROBE)) + say_successfully_calibrated(); + else + say_failed_to_calibrate(); + SERIAL_ECHOLNPGM(" probe."); + + // Cleanup + thermalManager.setTargetBed(0); + TERN_(HAS_LEVELING, set_bed_leveling_enabled(true)); + + SERIAL_ECHOLNPGM("Final compensation values:"); + ptc.print_offsets(); + } // do_probe_cal + + restore_feedrate_and_scaling(); + } + +#endif // PTC_PROBE && PTC_BED + +/** + * M871: Report / reset temperature compensation offsets. + * Note: This does not affect values in EEPROM until M500. + * + * M871 [ R | B | P | E ] + * + * No Parameters - 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 + * + * With B, P, or E: + * I[index] - Index in the array + * V[value] - Adjustment in µm + */ +void GcodeSuite::M871() { + + if (parser.seen('R')) { + // Reset z-probe offsets to factory defaults + ptc.clear_all_offsets(); + SERIAL_ECHOLNPGM("Offsets reset to default."); + } + else if (parser.seen("BPE")) { + if (!parser.seenval('V')) return; + const int16_t offset_val = parser.value_int(); + if (!parser.seenval('I')) return; + const int16_t idx = parser.value_int(); + const TempSensorID mod = TERN_(PTC_BED, parser.seen_test('B') ? TSI_BED :) + TERN_(PTC_HOTEND, parser.seen_test('E') ? TSI_EXT :) + TERN_(PTC_PROBE, parser.seen_test('P') ? TSI_PROBE :) TSI_COUNT; + if (mod == TSI_COUNT) + SERIAL_ECHOLNPGM("!Invalid sensor."); + else if (idx > 0 && ptc.set_offset(mod, idx - 1, offset_val)) + SERIAL_ECHOLNPGM("Set value: ", offset_val); + else + SERIAL_ECHOLNPGM("!Invalid index. Failed to set value (note: value at index 0 is constant)."); + } + else // Print current Z-probe adjustments. Note: Values in EEPROM might differ. + ptc.print_offsets(); +} + +#endif // HAS_PTC diff --git a/Marlin/src/gcode/calibrate/M100.cpp b/Marlin/src/gcode/calibrate/M100.cpp index 0e2d42907a252..338392b59746a 100644 --- a/Marlin/src/gcode/calibrate/M100.cpp +++ b/Marlin/src/gcode/calibrate/M100.cpp @@ -51,7 +51,7 @@ * 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(PGM_P const title, const char * const start, const uintptr_t size); + * void M100_dump_routine(FSTR_P const title, const char * const start, const uintptr_t size); * * Initial version by Roxy-3D */ @@ -182,8 +182,8 @@ inline int32_t count_test_bytes(const char * const start_free_memory) { } } - void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size) { - SERIAL_ECHOLNPGM_P(title); + void M100_dump_routine(FSTR_P const title, const char * const start, const uintptr_t size) { + SERIAL_ECHOLNF(title); // // Round the start and end locations to produce full lines of output // @@ -196,8 +196,8 @@ inline int32_t count_test_bytes(const char * const start_free_memory) { #endif // M100_FREE_MEMORY_DUMPER -inline int check_for_free_memory_corruption(PGM_P const title) { - SERIAL_ECHOPGM_P(title); +inline int check_for_free_memory_corruption(FSTR_P const title) { + SERIAL_ECHOF(title); char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end; int n = end_free_memory - start_free_memory; @@ -217,7 +217,7 @@ inline int check_for_free_memory_corruption(PGM_P const title) { // idle(); serial_delay(20); #if ENABLED(M100_FREE_MEMORY_DUMPER) - M100_dump_routine(PSTR(" Memory corruption detected with end_free_memory + * M48 * P = Number of sampled points (4-50, default 10) * X = Sample X position * Y = Sample Y position @@ -47,6 +51,7 @@ * E = Engage Z probe for each reading * L = Number of legs of movement before probe * S = Schizoid (Or Star if you prefer) + * C = Enable probe temperature compensation (0 or 1, default 1) * * This function requires the machine to be homed before invocation. */ @@ -79,7 +84,7 @@ void GcodeSuite::M48() { }; if (!probe.can_reach(test_position)) { - ui.set_status_P(GET_TEXT(MSG_M48_OUT_OF_BOUNDS), 99); + ui.set_status(GET_TEXT_F(MSG_M48_OUT_OF_BOUNDS), 99); SERIAL_ECHOLNPGM("? (X,Y) out of bounds."); return; } @@ -107,6 +112,8 @@ void GcodeSuite::M48() { set_bed_leveling_enabled(false); #endif + TERN_(HAS_PTC, ptc.set_enabled(!parser.seen('C') || parser.value_bool())); + // Work with reasonable feedrates remember_feedrate_scaling_off(); @@ -144,7 +151,7 @@ void GcodeSuite::M48() { LOOP_L_N(n, n_samples) { #if HAS_STATUS_MESSAGE // Display M48 progress in the status bar - ui.status_printf_P(0, PSTR(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples)); + ui.status_printf(0, F(S_FMT ": %d/%d"), GET_TEXT(MSG_M48_POINT), int(n + 1), int(n_samples)); #endif // When there are "legs" of movement move around the point before probing @@ -260,7 +267,7 @@ void GcodeSuite::M48() { #if HAS_STATUS_MESSAGE // Display M48 results in the status bar char sigma_str[8]; - ui.status_printf_P(0, PSTR(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str)); + ui.status_printf(0, F(S_FMT ": %s"), GET_TEXT(MSG_M48_DEVIATION), dtostrf(sigma, 2, 6, sigma_str)); #endif } @@ -269,6 +276,9 @@ void GcodeSuite::M48() { // Re-enable bed level correction if it had been on TERN_(HAS_LEVELING, set_bed_leveling_enabled(was_enabled)); + // Re-enable probe temperature correction + TERN_(HAS_PTC, ptc.set_enabled(true)); + report_current_position(); } diff --git a/Marlin/src/gcode/calibrate/M665.cpp b/Marlin/src/gcode/calibrate/M665.cpp index 11de1ce43459b..7dc657a61b43f 100644 --- a/Marlin/src/gcode/calibrate/M665.cpp +++ b/Marlin/src/gcode/calibrate/M665.cpp @@ -62,7 +62,7 @@ } void GcodeSuite::M665_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_DELTA_SETTINGS)); + report_heading_etc(forReplay, F(STR_DELTA_SETTINGS)); SERIAL_ECHOLNPGM_P( PSTR(" M665 L"), LINEAR_UNIT(delta_diagonal_rod) , PSTR(" R"), LINEAR_UNIT(delta_radius) @@ -86,13 +86,13 @@ * * Parameters: * - * S[segments-per-second] - Segments-per-second + * S[segments] - Segments-per-second * * Without NO_WORKSPACE_OFFSETS: * - * P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle - * T[theta-offset] - Theta offset, added to the elbow (B/Y) angle - * Z[z-offset] - Z offset, added to Z + * P[theta-psi-offset] - Theta-Psi offset, added to the shoulder (A/X) angle + * T[theta-offset] - Theta offset, added to the elbow (B/Y) angle + * Z[z-offset] - Z offset, added to Z * * A, P, and X are all aliases for the shoulder angle * B, T, and Y are all aliases for the elbow angle @@ -132,7 +132,7 @@ } void GcodeSuite::M665_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_SCARA_SETTINGS " (" STR_S_SEG_PER_SEC TERN_(HAS_SCARA_OFFSET, " " STR_SCARA_P_T_Z) ")")); + report_heading_etc(forReplay, F(STR_SCARA_SETTINGS " (" STR_S_SEG_PER_SEC TERN_(HAS_SCARA_OFFSET, " " STR_SCARA_P_T_Z) ")")); SERIAL_ECHOLNPGM_P( PSTR(" M665 S"), segments_per_second #if HAS_SCARA_OFFSET @@ -152,18 +152,35 @@ * * Parameters: * - * S[segments-per-second] - Segments-per-second + * S[segments] - Segments-per-second + * L[left] - Work area minimum X + * R[right] - Work area maximum X + * T[top] - Work area maximum Y + * B[bottom] - Work area minimum Y + * H[length] - Maximum belt length */ void GcodeSuite::M665() { - if (parser.seenval('S')) - segments_per_second = parser.value_float(); - else - M665_report(); + if (!parser.seen_any()) return M665_report(); + if (parser.seenval('S')) segments_per_second = parser.value_float(); + if (parser.seenval('L')) draw_area_min.x = parser.value_linear_units(); + if (parser.seenval('R')) draw_area_max.x = parser.value_linear_units(); + if (parser.seenval('T')) draw_area_max.y = parser.value_linear_units(); + if (parser.seenval('B')) draw_area_min.y = parser.value_linear_units(); + if (parser.seenval('H')) polargraph_max_belt_len = parser.value_linear_units(); + draw_area_size.x = draw_area_max.x - draw_area_min.x; + draw_area_size.y = draw_area_max.y - draw_area_min.y; } void GcodeSuite::M665_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_POLARGRAPH_SETTINGS " (" STR_S_SEG_PER_SEC ")")); - SERIAL_ECHOLNPGM(" M665 S", segments_per_second); + report_heading_etc(forReplay, F(STR_POLARGRAPH_SETTINGS)); + SERIAL_ECHOLNPGM_P( + PSTR(" M665 S"), LINEAR_UNIT(segments_per_second), + PSTR(" L"), LINEAR_UNIT(draw_area_min.x), + PSTR(" R"), LINEAR_UNIT(draw_area_max.x), + SP_T_STR, LINEAR_UNIT(draw_area_max.y), + SP_B_STR, LINEAR_UNIT(draw_area_min.y), + PSTR(" H"), LINEAR_UNIT(polargraph_max_belt_len) + ); } #endif diff --git a/Marlin/src/gcode/calibrate/M666.cpp b/Marlin/src/gcode/calibrate/M666.cpp index c4149c23520ab..90fad1811c8c9 100644 --- a/Marlin/src/gcode/calibrate/M666.cpp +++ b/Marlin/src/gcode/calibrate/M666.cpp @@ -44,8 +44,8 @@ void GcodeSuite::M666() { DEBUG_SECTION(log_M666, "M666", DEBUGGING(LEVELING)); bool is_err = false, is_set = false; - LOOP_LINEAR_AXES(i) { - if (parser.seen(AXIS_CHAR(i))) { + LOOP_NUM_AXES(i) { + if (parser.seenval(AXIS_CHAR(i))) { is_set = true; const float v = parser.value_linear_units(); if (v > 0) @@ -61,7 +61,7 @@ } void GcodeSuite::M666_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT)); + report_heading_etc(forReplay, F(STR_ENDSTOP_ADJUSTMENT)); SERIAL_ECHOLNPGM_P( PSTR(" M666 X"), LINEAR_UNIT(delta_endstop_adj.a) , SP_Y_STR, LINEAR_UNIT(delta_endstop_adj.b) @@ -93,19 +93,19 @@ #if ENABLED(Z_MULTI_ENDSTOPS) if (parser.seenval('Z')) { const float z_adj = parser.value_linear_units(); - #if NUM_Z_STEPPER_DRIVERS == 2 + #if NUM_Z_STEPPERS == 2 endstops.z2_endstop_adj = z_adj; #else const int ind = parser.intval('S'); #define _SET_ZADJ(N) if (!ind || ind == N) endstops.z##N##_endstop_adj = z_adj; - REPEAT_S(2, INCREMENT(NUM_Z_STEPPER_DRIVERS), _SET_ZADJ) + REPEAT_S(2, INCREMENT(NUM_Z_STEPPERS), _SET_ZADJ) #endif } #endif } void GcodeSuite::M666_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_ENDSTOP_ADJUSTMENT)); + report_heading_etc(forReplay, F(STR_ENDSTOP_ADJUSTMENT)); SERIAL_ECHOPGM(" M666"); #if ENABLED(X_DUAL_ENDSTOPS) SERIAL_ECHOLNPGM_P(SP_X_STR, LINEAR_UNIT(endstops.x2_endstop_adj)); @@ -114,11 +114,11 @@ SERIAL_ECHOLNPGM_P(SP_Y_STR, LINEAR_UNIT(endstops.y2_endstop_adj)); #endif #if ENABLED(Z_MULTI_ENDSTOPS) - #if NUM_Z_STEPPER_DRIVERS >= 3 + #if NUM_Z_STEPPERS >= 3 SERIAL_ECHOPGM(" S2 Z", LINEAR_UNIT(endstops.z3_endstop_adj)); report_echo_start(forReplay); SERIAL_ECHOPGM(" M666 S3 Z", LINEAR_UNIT(endstops.z3_endstop_adj)); - #if NUM_Z_STEPPER_DRIVERS >= 4 + #if NUM_Z_STEPPERS >= 4 report_echo_start(forReplay); SERIAL_ECHOPGM(" M666 S4 Z", LINEAR_UNIT(endstops.z4_endstop_adj)); #endif diff --git a/Marlin/src/gcode/calibrate/M852.cpp b/Marlin/src/gcode/calibrate/M852.cpp index c4361b89f315d..6c661dcd61d9b 100644 --- a/Marlin/src/gcode/calibrate/M852.cpp +++ b/Marlin/src/gcode/calibrate/M852.cpp @@ -92,8 +92,8 @@ void GcodeSuite::M852() { } void GcodeSuite::M852_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_SKEW_FACTOR)); - SERIAL_ECHOPAIR_F(" M851 I", planner.skew_factor.xy, 6); + report_heading_etc(forReplay, F(STR_SKEW_FACTOR)); + SERIAL_ECHOPAIR_F(" M852 I", planner.skew_factor.xy, 6); #if ENABLED(SKEW_CORRECTION_FOR_Z) SERIAL_ECHOPAIR_F(" J", planner.skew_factor.xz, 6); SERIAL_ECHOPAIR_F(" K", planner.skew_factor.yz, 6); diff --git a/Marlin/src/gcode/config/M200-M205.cpp b/Marlin/src/gcode/config/M200-M205.cpp index 2880bd9943070..4190d40a17ab2 100644 --- a/Marlin/src/gcode/config/M200-M205.cpp +++ b/Marlin/src/gcode/config/M200-M205.cpp @@ -76,7 +76,7 @@ void GcodeSuite::M200_report(const bool forReplay/*=true*/) { if (!forReplay) { - report_heading(forReplay, PSTR(STR_FILAMENT_SETTINGS), false); + report_heading(forReplay, F(STR_FILAMENT_SETTINGS), false); if (!parser.volumetric_enabled) SERIAL_ECHOPGM(" (Disabled):"); SERIAL_EOL(); report_echo_start(forReplay); @@ -93,12 +93,12 @@ } #else SERIAL_ECHOLNPGM(" M200 S", parser.volumetric_enabled); - LOOP_L_N(i, EXTRUDERS) { + EXTRUDER_LOOP() { report_echo_start(forReplay); SERIAL_ECHOLNPGM( - " M200 T", i, " D", LINEAR_UNIT(planner.filament_size[i]) + " M200 T", e, " D", LINEAR_UNIT(planner.filament_size[e]) #if ENABLED(VOLUMETRIC_EXTRUDER_LIMIT) - , " L", LINEAR_UNIT(planner.volumetric_extruder_limit[i]) + , " L", LINEAR_UNIT(planner.volumetric_extruder_limit[e]) #endif ); } @@ -108,12 +108,21 @@ #endif // !NO_VOLUMETRICS /** - * M201: Set max acceleration in units/s^2 for print moves (M201 X1000 Y1000) + * M201: Set max acceleration in units/s^2 for print moves. * - * With multiple extruders use T to specify which one. + * X : Max Acceleration for X + * Y : Max Acceleration for Y + * Z : Max Acceleration for Z + * ... : etc + * E : Max Acceleration for Extruder + * T : Extruder index to set + * + * With XY_FREQUENCY_LIMIT: + * F : Frequency limit for XY...IJKUVW + * S : Speed factor percentage. */ void GcodeSuite::M201() { - if (!parser.seen("T" LOGICAL_AXES_STRING)) + if (!parser.seen("T" STR_AXES_LOGICAL TERN_(XY_FREQUENCY_LIMIT, "FS"))) return M201_report(); const int8_t target_extruder = get_target_extruder_from_command(); @@ -121,21 +130,21 @@ void GcodeSuite::M201() { #ifdef XY_FREQUENCY_LIMIT if (parser.seenval('F')) planner.set_frequency_limit(parser.value_byte()); - if (parser.seenval('G')) planner.xy_freq_min_speed_factor = constrain(parser.value_float(), 1, 100) / 100; + if (parser.seenval('S')) planner.xy_freq_min_speed_factor = constrain(parser.value_float(), 1, 100) / 100; #endif LOOP_LOGICAL_AXES(i) { - if (parser.seenval(axis_codes[i])) { - const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i); - planner.set_max_acceleration(a, parser.value_axis_units((AxisEnum)a)); + if (parser.seenval(AXIS_CHAR(i))) { + const AxisEnum a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? E_AXIS_N(target_extruder) : (AxisEnum)i), (AxisEnum)i); + planner.set_max_acceleration(a, parser.value_axis_units(a)); } } } void GcodeSuite::M201_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_MAX_ACCELERATION)); + report_heading_etc(forReplay, F(STR_MAX_ACCELERATION)); SERIAL_ECHOLNPGM_P( - LIST_N(DOUBLE(LINEAR_AXES), + 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]), @@ -164,23 +173,23 @@ void GcodeSuite::M201_report(const bool forReplay/*=true*/) { * With multiple extruders use T to specify which one. */ void GcodeSuite::M203() { - if (!parser.seen("T" LOGICAL_AXES_STRING)) + if (!parser.seen("T" STR_AXES_LOGICAL)) return M203_report(); const int8_t target_extruder = get_target_extruder_from_command(); if (target_extruder < 0) return; LOOP_LOGICAL_AXES(i) - if (parser.seenval(axis_codes[i])) { - const uint8_t a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? uint8_t(E_AXIS_N(target_extruder)) : i), i); - planner.set_max_feedrate(a, parser.value_axis_units((AxisEnum)a)); + if (parser.seenval(AXIS_CHAR(i))) { + const AxisEnum a = TERN(HAS_EXTRUDERS, (i == E_AXIS ? E_AXIS_N(target_extruder) : (AxisEnum)i), (AxisEnum)i); + planner.set_max_feedrate(a, parser.value_axis_units(a)); } } void GcodeSuite::M203_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_MAX_FEEDRATES)); + report_heading_etc(forReplay, F(STR_MAX_FEEDRATES)); SERIAL_ECHOLNPGM_P( - LIST_N(DOUBLE(LINEAR_AXES), + 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]), @@ -194,7 +203,7 @@ void GcodeSuite::M203_report(const bool forReplay/*=true*/) { ); #if ENABLED(DISTINCT_E_FACTORS) LOOP_L_N(i, E_STEPPERS) { - SERIAL_ECHO_START(); + if (!forReplay) SERIAL_ECHO_START(); SERIAL_ECHOLNPGM_P( PSTR(" M203 T"), i , SP_E_STR, VOLUMETRIC_UNIT(planner.settings.max_feedrate_mm_s[E_AXIS_N(i)]) @@ -224,7 +233,7 @@ void GcodeSuite::M204() { } void GcodeSuite::M204_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_ACCELERATION_P_R_T)); + report_heading_etc(forReplay, F(STR_ACCELERATION_P_R_T)); SERIAL_ECHOLNPGM_P( PSTR(" M204 P"), LINEAR_UNIT(planner.settings.acceleration) , PSTR(" R"), LINEAR_UNIT(planner.settings.retract_acceleration) @@ -253,7 +262,7 @@ void GcodeSuite::M205() { if (parser.seenval('S')) planner.settings.min_feedrate_mm_s = parser.value_linear_units(); if (parser.seenval('T')) planner.settings.min_travel_feedrate_mm_s = parser.value_linear_units(); #if HAS_JUNCTION_DEVIATION - #if HAS_CLASSIC_JERK && (AXIS4_NAME == 'J' || AXIS5_NAME == 'J' || AXIS6_NAME == 'J') + #if HAS_CLASSIC_JERK && AXIS_COLLISION('J') #error "Can't set_max_jerk for 'J' axis because 'J' is used for Junction Deviation." #endif if (parser.seenval('J')) { @@ -285,11 +294,16 @@ void GcodeSuite::M205() { } void GcodeSuite::M205_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR( + report_heading_etc(forReplay, F( "Advanced (B S T" TERN_(HAS_JUNCTION_DEVIATION, " J") - TERN_(HAS_CLASSIC_JERK, " X Y Z") - TERN_(HAS_CLASSIC_E_JERK, " E") + #if HAS_CLASSIC_JERK + NUM_AXIS_GANG( + " X", " Y", " Z", + " " STR_I "", " " STR_J "", " " STR_K "" + ) + #endif + TERN_(HAS_CLASSIC_E_JERK, " E") ")" )); SERIAL_ECHOLNPGM_P( @@ -300,7 +314,7 @@ void GcodeSuite::M205_report(const bool forReplay/*=true*/) { , PSTR(" J"), LINEAR_UNIT(planner.junction_deviation_mm) #endif #if HAS_CLASSIC_JERK - , LIST_N(DOUBLE(LINEAR_AXES), + , LIST_N(DOUBLE(NUM_AXES), 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/M217.cpp b/Marlin/src/gcode/config/M217.cpp index 923a27d7dfedf..1c8085835574c 100644 --- a/Marlin/src/gcode/config/M217.cpp +++ b/Marlin/src/gcode/config/M217.cpp @@ -34,25 +34,28 @@ #include "../../MarlinCore.h" // for SP_X_STR, etc. /** - * M217 - Set SINGLENOZZLE toolchange parameters + * M217 - Set toolchange parameters * * // Tool change command * Q Prime active tool and exit * * // Tool change settings - * S[linear] Swap length - * B[linear] Extra Swap length - * E[linear] Prime length - * P[linear/m] Prime speed - * R[linear/m] Retract speed - * U[linear/m] UnRetract speed - * V[linear] 0/1 Enable auto prime first extruder used - * W[linear] 0/1 Enable park & Z Raise - * X[linear] Park X (Requires TOOLCHANGE_PARK) - * Y[linear] Park Y (Requires TOOLCHANGE_PARK) - * Z[linear] Z Raise - * F[linear] Fan Speed 0-255 - * G[linear/s] Fan time + * S[linear] Swap length + * B[linear] Extra Swap resume length + * E[linear] Extra Prime length (as used by M217 Q) + * P[linear/min] Prime speed + * R[linear/min] Retract speed + * U[linear/min] UnRetract speed + * V[linear] 0/1 Enable auto prime first extruder used + * W[linear] 0/1 Enable park & Z Raise + * X[linear] Park X (Requires TOOLCHANGE_PARK) + * Y[linear] Park Y (Requires TOOLCHANGE_PARK) + * I[linear] Park I (Requires TOOLCHANGE_PARK and NUM_AXES >= 4) + * J[linear] Park J (Requires TOOLCHANGE_PARK and NUM_AXES >= 5) + * K[linear] Park K (Requires TOOLCHANGE_PARK and NUM_AXES >= 6) + * Z[linear] Z Raise + * F[speed] Fan Speed 0-255 + * D[seconds] Fan time * * Tool migration settings * A[0|1] Enable auto-migration on runout @@ -76,8 +79,8 @@ void GcodeSuite::M217() { if (parser.seenval('R')) { const int16_t v = parser.value_linear_units(); toolchange_settings.retract_speed = constrain(v, 10, 5400); } if (parser.seenval('U')) { const int16_t v = parser.value_linear_units(); toolchange_settings.unretract_speed = constrain(v, 10, 5400); } #if TOOLCHANGE_FS_FAN >= 0 && HAS_FAN - if (parser.seenval('F')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_speed = constrain(v, 0, 255); } - if (parser.seenval('G')) { const int16_t v = parser.value_linear_units(); toolchange_settings.fan_time = constrain(v, 1, 30); } + if (parser.seenval('F')) { const uint16_t v = parser.value_ushort(); toolchange_settings.fan_speed = constrain(v, 0, 255); } + if (parser.seenval('D')) { const uint16_t v = parser.value_ushort(); toolchange_settings.fan_time = constrain(v, 1, 30); } #endif #endif @@ -88,10 +91,23 @@ void GcodeSuite::M217() { #if ENABLED(TOOLCHANGE_PARK) if (parser.seenval('W')) { toolchange_settings.enable_park = parser.value_linear_units(); } if (parser.seenval('X')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.x = constrain(v, X_MIN_POS, X_MAX_POS); } - if (parser.seenval('Y')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.y = constrain(v, Y_MIN_POS, Y_MAX_POS); } + #if HAS_Y_AXIS + if (parser.seenval('Y')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.y = constrain(v, Y_MIN_POS, Y_MAX_POS); } + #endif + #if HAS_I_AXIS + if (parser.seenval('I')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.i = constrain(v, I_MIN_POS, I_MAX_POS); } + #endif + #if HAS_J_AXIS + if (parser.seenval('J')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.j = constrain(v, J_MIN_POS, J_MAX_POS); } + #endif + #if HAS_K_AXIS + if (parser.seenval('K')) { const int16_t v = parser.value_linear_units(); toolchange_settings.change_point.k = constrain(v, K_MIN_POS, K_MAX_POS); } + #endif #endif - if (parser.seenval('Z')) { toolchange_settings.z_raise = parser.value_linear_units(); } + #if HAS_Z_AXIS + if (parser.seenval('Z')) { toolchange_settings.z_raise = parser.value_linear_units(); } + #endif #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) migration.target = 0; // 0 = disabled @@ -131,19 +147,19 @@ void GcodeSuite::M217() { } void GcodeSuite::M217_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_TOOL_CHANGING)); + report_heading_etc(forReplay, F(STR_TOOL_CHANGING)); SERIAL_ECHOPGM(" M217"); #if ENABLED(TOOLCHANGE_FILAMENT_SWAP) SERIAL_ECHOPGM(" S", LINEAR_UNIT(toolchange_settings.swap_length)); SERIAL_ECHOPGM_P(SP_B_STR, LINEAR_UNIT(toolchange_settings.extra_resume), - SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime), - SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed)); + SP_E_STR, LINEAR_UNIT(toolchange_settings.extra_prime), + SP_P_STR, LINEAR_UNIT(toolchange_settings.prime_speed)); SERIAL_ECHOPGM(" R", LINEAR_UNIT(toolchange_settings.retract_speed), " U", LINEAR_UNIT(toolchange_settings.unretract_speed), " F", toolchange_settings.fan_speed, - " G", toolchange_settings.fan_time); + " D", toolchange_settings.fan_time); #if ENABLED(TOOLCHANGE_MIGRATION_FEATURE) SERIAL_ECHOPGM(" A", migration.automode); @@ -151,9 +167,22 @@ void GcodeSuite::M217_report(const bool forReplay/*=true*/) { #endif #if ENABLED(TOOLCHANGE_PARK) + { SERIAL_ECHOPGM(" W", LINEAR_UNIT(toolchange_settings.enable_park)); - SERIAL_ECHOPGM_P(SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x)); - SERIAL_ECHOPGM_P(SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y)); + SERIAL_ECHOPGM_P( + SP_X_STR, LINEAR_UNIT(toolchange_settings.change_point.x) + #if HAS_Y_AXIS + , SP_Y_STR, LINEAR_UNIT(toolchange_settings.change_point.y) + #endif + #if SECONDARY_AXES >= 1 + , LIST_N(DOUBLE(SECONDARY_AXES), + SP_I_STR, I_AXIS_UNIT(toolchange_settings.change_point.i), + SP_J_STR, J_AXIS_UNIT(toolchange_settings.change_point.j), + SP_K_STR, K_AXIS_UNIT(toolchange_settings.change_point.k) + ) + #endif + ); + } #endif #if ENABLED(TOOLCHANGE_FS_PRIME_FIRST_USED) diff --git a/Marlin/src/gcode/config/M218.cpp b/Marlin/src/gcode/config/M218.cpp index c95cd6c1b93c5..c39447a28d380 100644 --- a/Marlin/src/gcode/config/M218.cpp +++ b/Marlin/src/gcode/config/M218.cpp @@ -57,7 +57,7 @@ void GcodeSuite::M218() { } void GcodeSuite::M218_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_HOTEND_OFFSETS)); + report_heading_etc(forReplay, F(STR_HOTEND_OFFSETS)); LOOP_S_L_N(e, 1, HOTENDS) { report_echo_start(forReplay); SERIAL_ECHOPGM_P( diff --git a/Marlin/src/gcode/config/M281.cpp b/Marlin/src/gcode/config/M281.cpp index ac91f08cb4b0e..e4ef3ab40b8cf 100644 --- a/Marlin/src/gcode/config/M281.cpp +++ b/Marlin/src/gcode/config/M281.cpp @@ -47,15 +47,15 @@ void GcodeSuite::M281() { return; } #endif - if (parser.seen('L')) servo_angles[servo_index][0] = parser.value_int(); - if (parser.seen('U')) servo_angles[servo_index][1] = parser.value_int(); + if (parser.seenval('L')) servo_angles[servo_index][0] = parser.value_int(); + if (parser.seenval('U')) servo_angles[servo_index][1] = parser.value_int(); } else SERIAL_ERROR_MSG("Servo ", servo_index, " out of range"); } void GcodeSuite::M281_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_SERVO_ANGLES)); + report_heading_etc(forReplay, F(STR_SERVO_ANGLES)); LOOP_L_N(i, NUM_SERVOS) { switch (i) { default: break; diff --git a/Marlin/src/gcode/config/M301.cpp b/Marlin/src/gcode/config/M301.cpp index db882b3b6505a..fc9f1883d6161 100644 --- a/Marlin/src/gcode/config/M301.cpp +++ b/Marlin/src/gcode/config/M301.cpp @@ -79,7 +79,7 @@ void GcodeSuite::M301() { } void GcodeSuite::M301_report(const bool forReplay/*=true*/ E_OPTARG(const int8_t eindex/*=-1*/)) { - report_heading(forReplay, PSTR(STR_HOTEND_PID)); + report_heading(forReplay, F(STR_HOTEND_PID)); IF_DISABLED(HAS_MULTI_EXTRUDER, constexpr int8_t eindex = -1); HOTEND_LOOP() { if (e == eindex || eindex == -1) { diff --git a/Marlin/src/gcode/config/M302.cpp b/Marlin/src/gcode/config/M302.cpp index 57c049e19404b..9f4d569d7b2e5 100644 --- a/Marlin/src/gcode/config/M302.cpp +++ b/Marlin/src/gcode/config/M302.cpp @@ -27,6 +27,10 @@ #include "../gcode.h" #include "../../module/temperature.h" +#if ENABLED(DWIN_LCD_PROUI) + #include "../../lcd/e3v2/proui/dwin_defines.h" +#endif + /** * M302: Allow cold extrudes, or set the minimum extrude temperature * @@ -47,6 +51,7 @@ void GcodeSuite::M302() { if (seen_S) { thermalManager.extrude_min_temp = parser.value_celsius(); thermalManager.allow_cold_extrude = (thermalManager.extrude_min_temp == 0); + TERN_(DWIN_LCD_PROUI, HMI_data.ExtMinT = thermalManager.extrude_min_temp); } if (parser.seen('P')) @@ -55,7 +60,7 @@ void GcodeSuite::M302() { // Report current state SERIAL_ECHO_START(); SERIAL_ECHOPGM("Cold extrudes are "); - SERIAL_ECHOPGM_P(thermalManager.allow_cold_extrude ? PSTR("en") : PSTR("dis")); + SERIAL_ECHOF(thermalManager.allow_cold_extrude ? F("en") : F("dis")); SERIAL_ECHOLNPGM("abled (min temp ", thermalManager.extrude_min_temp, "C)"); } } diff --git a/Marlin/src/gcode/config/M304.cpp b/Marlin/src/gcode/config/M304.cpp index 05ee4bad8061d..c970288238f5a 100644 --- a/Marlin/src/gcode/config/M304.cpp +++ b/Marlin/src/gcode/config/M304.cpp @@ -36,14 +36,14 @@ */ void GcodeSuite::M304() { if (!parser.seen("PID")) return M304_report(); - if (parser.seen('P')) thermalManager.temp_bed.pid.Kp = parser.value_float(); - if (parser.seen('I')) thermalManager.temp_bed.pid.Ki = scalePID_i(parser.value_float()); - if (parser.seen('D')) thermalManager.temp_bed.pid.Kd = scalePID_d(parser.value_float()); + if (parser.seenval('P')) thermalManager.temp_bed.pid.Kp = parser.value_float(); + if (parser.seenval('I')) thermalManager.temp_bed.pid.Ki = scalePID_i(parser.value_float()); + if (parser.seenval('D')) thermalManager.temp_bed.pid.Kd = scalePID_d(parser.value_float()); } void GcodeSuite::M304_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_BED_PID)); - SERIAL_ECHO_MSG( + report_heading_etc(forReplay, F(STR_BED_PID)); + SERIAL_ECHOLNPGM( " M304 P", thermalManager.temp_bed.pid.Kp , " I", unscalePID_i(thermalManager.temp_bed.pid.Ki) , " D", unscalePID_d(thermalManager.temp_bed.pid.Kd) diff --git a/Marlin/src/gcode/config/M305.cpp b/Marlin/src/gcode/config/M305.cpp index 6957eef050dd7..e7746923b3188 100644 --- a/Marlin/src/gcode/config/M305.cpp +++ b/Marlin/src/gcode/config/M305.cpp @@ -52,19 +52,19 @@ void GcodeSuite::M305() { if (t_index >= (USER_THERMISTORS) || (do_set && t_index < 0)) SERIAL_ECHO_MSG("!Invalid index. (0 <= P <= ", USER_THERMISTORS - 1, ")"); else if (do_set) { - if (parser.seen('R')) // Pullup resistor value + if (parser.seenval('R')) // Pullup resistor value if (!thermalManager.set_pull_up_res(t_index, parser.value_float())) SERIAL_ECHO_MSG("!Invalid series resistance. (0 < R < 1000000)"); - if (parser.seen('T')) // Resistance at 25C + if (parser.seenval('T')) // Resistance at 25C if (!thermalManager.set_res25(t_index, parser.value_float())) SERIAL_ECHO_MSG("!Invalid 25C resistance. (0 < T < 10000000)"); - if (parser.seen('B')) // Beta value + if (parser.seenval('B')) // Beta value if (!thermalManager.set_beta(t_index, parser.value_float())) SERIAL_ECHO_MSG("!Invalid beta. (0 < B < 1000000)"); - if (parser.seen('C')) // Steinhart-Hart C coefficient + if (parser.seenval('C')) // Steinhart-Hart C coefficient if (!thermalManager.set_sh_coeff(t_index, parser.value_float())) SERIAL_ECHO_MSG("!Invalid Steinhart-Hart C coeff. (-0.01 < C < +0.01)"); } // If not setting then report parameters diff --git a/Marlin/src/gcode/config/M309.cpp b/Marlin/src/gcode/config/M309.cpp index 01c4e62347880..577023292e2d4 100644 --- a/Marlin/src/gcode/config/M309.cpp +++ b/Marlin/src/gcode/config/M309.cpp @@ -42,7 +42,7 @@ void GcodeSuite::M309() { } void GcodeSuite::M309_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_CHAMBER_PID)); + report_heading_etc(forReplay, F(STR_CHAMBER_PID)); SERIAL_ECHOLNPGM( " M309 P", thermalManager.temp_chamber.pid.Kp , " I", unscalePID_i(thermalManager.temp_chamber.pid.Ki) diff --git a/Marlin/src/gcode/config/M43.cpp b/Marlin/src/gcode/config/M43.cpp index 08b45efa15bf3..688b94c9bf39f 100644 --- a/Marlin/src/gcode/config/M43.cpp +++ b/Marlin/src/gcode/config/M43.cpp @@ -65,12 +65,12 @@ inline void toggle_pins() { pin_t pin = GET_PIN_MAP_PIN_M43(i); if (!VALID_PIN(pin)) continue; if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) { - report_pin_state_extended(pin, ignore_protection, true, PSTR("Untouched ")); + report_pin_state_extended(pin, ignore_protection, true, F("Untouched ")); SERIAL_EOL(); } else { - watchdog_refresh(); - report_pin_state_extended(pin, ignore_protection, true, PSTR("Pulsing ")); + hal.watchdog_refresh(); + report_pin_state_extended(pin, ignore_protection, true, F("Pulsing ")); #ifdef __STM32F1__ const auto prior_mode = _GET_MODE(i); #else @@ -98,10 +98,10 @@ inline void toggle_pins() { { pinMode(pin, OUTPUT); for (int16_t j = 0; j < repeat; j++) { - watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait); - watchdog_refresh(); extDigitalWrite(pin, 1); safe_delay(wait); - watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait); - watchdog_refresh(); + hal.watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait); + hal.watchdog_refresh(); extDigitalWrite(pin, 1); safe_delay(wait); + hal.watchdog_refresh(); extDigitalWrite(pin, 0); safe_delay(wait); + hal.watchdog_refresh(); } } #ifdef __STM32F1__ @@ -198,10 +198,10 @@ inline void servo_probe_test() { uint8_t i = 0; SERIAL_ECHOLNPGM(". Deploy & stow 4 times"); do { - MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy + servo[probe_index].move(servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy safe_delay(500); deploy_state = READ(PROBE_TEST_PIN); - MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][1]); // Stow + servo[probe_index].move(servo_angles[Z_PROBE_SERVO_NR][1]); // Stow safe_delay(500); stow_state = READ(PROBE_TEST_PIN); } while (++i < 4); @@ -226,7 +226,7 @@ inline void servo_probe_test() { } // Ask the user for a trigger event and measure the pulse width. - MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy + servo[probe_index].move(servo_angles[Z_PROBE_SERVO_NR][0]); // Deploy safe_delay(500); SERIAL_ECHOLNPGM("** Please trigger probe within 30 sec **"); uint16_t probe_counter = 0; @@ -256,7 +256,7 @@ inline void servo_probe_test() { } else SERIAL_ECHOLNPGM("FAIL: Noise detected - please re-run test"); - MOVE_SERVO(probe_index, servo_angles[Z_PROBE_SERVO_NR][1]); // Stow + servo[probe_index].move(servo_angles[Z_PROBE_SERVO_NR][1]); // Stow return; } } @@ -303,7 +303,7 @@ void GcodeSuite::M43() { if (parser.seen('E')) { endstops.monitor_flag = parser.value_bool(); SERIAL_ECHOPGM("endstop monitor "); - SERIAL_ECHOPGM_P(endstops.monitor_flag ? PSTR("en") : PSTR("dis")); + SERIAL_ECHOF(endstops.monitor_flag ? F("en") : F("dis")); SERIAL_ECHOLNPGM("abled"); return; } @@ -313,7 +313,7 @@ void GcodeSuite::M43() { // 'P' Get the range of pins to test or watch uint8_t first_pin = PARSED_PIN_INDEX('P', 0), - last_pin = parser.seenval('P') ? first_pin : NUMBER_PINS_TOTAL - 1; + last_pin = parser.seenval('P') ? first_pin : TERN(HAS_HIGH_ANALOG_PINS, NUM_DIGITAL_PINS, NUMBER_PINS_TOTAL) - 1; if (first_pin > last_pin) return; @@ -333,19 +333,19 @@ void GcodeSuite::M43() { if (M43_NEVER_TOUCH(i) || (!ignore_protection && pin_is_protected(pin))) continue; 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[...] - else - //*/ - pin_state[i - first_pin] = extDigitalRead(pin); + /* + if (IS_ANALOG(pin)) + pin_state[pin - first_pin] = analogRead(DIGITAL_PIN_TO_ANALOG_PIN(pin)); // int16_t pin_state[...] + else + //*/ + pin_state[i - first_pin] = extDigitalRead(pin); } #if HAS_RESUME_CONTINUE KEEPALIVE_STATE(PAUSED_FOR_USER); wait_for_user = true; - TERN_(HOST_PROMPT_SUPPORT, host_prompt_do(PROMPT_USER_CONTINUE, PSTR("M43 Wait Called"), CONTINUE_STR)); - TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired_P(PSTR("M43 Wait Called"))); + TERN_(HOST_PROMPT_SUPPORT, hostui.prompt_do(PROMPT_USER_CONTINUE, F("M43 Wait Called"), FPSTR(CONTINUE_STR))); + TERN_(EXTENSIBLE_UI, ExtUI::onUserConfirmRequired(F("M43 Wait Called"))); #endif for (;;) { diff --git a/Marlin/src/gcode/config/M540.cpp b/Marlin/src/gcode/config/M540.cpp index 54d52f3a31e22..e751248dd6417 100644 --- a/Marlin/src/gcode/config/M540.cpp +++ b/Marlin/src/gcode/config/M540.cpp @@ -25,7 +25,7 @@ #if ENABLED(SD_ABORT_ON_ENDSTOP_HIT) #include "../gcode.h" -#include "../../module/stepper.h" +#include "../../module/planner.h" /** * M540: Set whether SD card print should abort on endstop hit (M540 S<0|1>) diff --git a/Marlin/src/gcode/config/M672.cpp b/Marlin/src/gcode/config/M672.cpp index af74230516e1a..257b49471f61b 100644 --- a/Marlin/src/gcode/config/M672.cpp +++ b/Marlin/src/gcode/config/M672.cpp @@ -53,7 +53,7 @@ // b7 b6 b5 b4 ~b4 ... hi bits, NOT last bit // b3 b2 b1 b0 ~b0 ... lo bits, NOT last bit // -void M672_send(uint8_t b) { // bit rate requirement: 1KHz +/- 30% +void M672_send(uint8_t b) { // bit rate requirement: 1kHz +/- 30% LOOP_L_N(bits, 14) { switch (bits) { default: { OUT_WRITE(SMART_EFFECTOR_MOD_PIN, !!(b & 0x80)); b <<= 1; break; } // send bit, shift next into place diff --git a/Marlin/src/gcode/config/M92.cpp b/Marlin/src/gcode/config/M92.cpp index ef11533114381..b2ec3bec677a4 100644 --- a/Marlin/src/gcode/config/M92.cpp +++ b/Marlin/src/gcode/config/M92.cpp @@ -24,7 +24,7 @@ #include "../../module/planner.h" /** - * M92: Set axis steps-per-unit for one or more axes, X, Y, Z, and E. + * M92: Set axis steps-per-unit for one or more axes, X, Y, Z, [I, [J, [K]]] and E. * (Follows the same syntax as G92) * * With multiple extruders use T to specify which one. @@ -43,11 +43,11 @@ void GcodeSuite::M92() { if (target_extruder < 0) return; // No arguments? Show M92 report. - if (!parser.seen(LOGICAL_AXES_STRING TERN_(MAGIC_NUMBERS_GCODE, "HL"))) + if (!parser.seen(STR_AXES_LOGICAL TERN_(MAGIC_NUMBERS_GCODE, "HL"))) return M92_report(true, target_extruder); LOOP_LOGICAL_AXES(i) { - if (parser.seenval(axis_codes[i])) { + if (parser.seenval(AXIS_CHAR(i))) { if (TERN1(HAS_EXTRUDERS, i != E_AXIS)) planner.settings.axis_steps_per_mm[i] = parser.value_per_axis_units((AxisEnum)i); else { @@ -91,8 +91,8 @@ void GcodeSuite::M92() { } void GcodeSuite::M92_report(const bool forReplay/*=true*/, const int8_t e/*=-1*/) { - report_heading_etc(forReplay, PSTR(STR_STEPS_PER_UNIT)); - SERIAL_ECHOPGM_P(LIST_N(DOUBLE(LINEAR_AXES), + report_heading_etc(forReplay, F(STR_STEPS_PER_UNIT)); + SERIAL_ECHOPGM_P(LIST_N(DOUBLE(NUM_AXES), 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/M108_M112_M410.cpp b/Marlin/src/gcode/control/M108_M112_M410.cpp index 309c806c8fc5b..39f9c04e1915b 100644 --- a/Marlin/src/gcode/control/M108_M112_M410.cpp +++ b/Marlin/src/gcode/control/M108_M112_M410.cpp @@ -40,7 +40,7 @@ void GcodeSuite::M108() { * M112: Full Shutdown */ void GcodeSuite::M112() { - kill(M112_KILL_STR, nullptr, true); + kill(FPSTR(M112_KILL_STR), nullptr, true); } /** diff --git a/Marlin/src/gcode/control/M111.cpp b/Marlin/src/gcode/control/M111.cpp index 69d20b4c5d222..a92d334ae9d38 100644 --- a/Marlin/src/gcode/control/M111.cpp +++ b/Marlin/src/gcode/control/M111.cpp @@ -26,7 +26,7 @@ * M111: Set the debug level */ void GcodeSuite::M111() { - if (parser.seen('S')) marlin_debug_flags = parser.byteval('S'); + if (parser.seenval('S')) marlin_debug_flags = parser.value_byte(); static PGMSTR(str_debug_1, STR_DEBUG_ECHO); static PGMSTR(str_debug_2, STR_DEBUG_INFO); @@ -34,12 +34,12 @@ void GcodeSuite::M111() { static PGMSTR(str_debug_8, STR_DEBUG_DRYRUN); static PGMSTR(str_debug_16, STR_DEBUG_COMMUNICATION); #if ENABLED(DEBUG_LEVELING_FEATURE) - static PGMSTR(str_debug_lvl, STR_DEBUG_LEVELING); + static PGMSTR(str_debug_detail, STR_DEBUG_DETAIL); #endif static PGM_P const debug_strings[] PROGMEM = { str_debug_1, str_debug_2, str_debug_4, str_debug_8, str_debug_16, - TERN_(DEBUG_LEVELING_FEATURE, str_debug_lvl) + TERN_(DEBUG_LEVELING_FEATURE, str_debug_detail) }; SERIAL_ECHO_START(); @@ -49,7 +49,7 @@ void GcodeSuite::M111() { LOOP_L_N(i, COUNT(debug_strings)) { if (TEST(marlin_debug_flags, i)) { if (comma++) SERIAL_CHAR(','); - SERIAL_ECHOPGM_P((char*)pgm_read_ptr(&debug_strings[i])); + SERIAL_ECHOPGM_P((PGM_P)pgm_read_ptr(&debug_strings[i])); } } } diff --git a/Marlin/src/gcode/control/M17_M18_M84.cpp b/Marlin/src/gcode/control/M17_M18_M84.cpp index 82df20ac45f19..ebe48d95d7b00 100644 --- a/Marlin/src/gcode/control/M17_M18_M84.cpp +++ b/Marlin/src/gcode/control/M17_M18_M84.cpp @@ -23,6 +23,8 @@ #include "../gcode.h" #include "../../MarlinCore.h" // for stepper_inactive_time, disable_e_steppers #include "../../lcd/marlinui.h" +#include "../../module/motion.h" // for e_axis_mask +#include "../../module/planner.h" #include "../../module/stepper.h" #if ENABLED(AUTO_BED_LEVELING_UBL) @@ -33,8 +35,8 @@ #include "../../core/debug_out.h" #include "../../libs/hex_print.h" -inline axis_flags_t selected_axis_bits() { - axis_flags_t selected{0}; +inline stepper_flags_t selected_axis_bits() { + stepper_flags_t selected{0}; #if HAS_EXTRUDERS if (parser.seen('E')) { if (E_TERN0(parser.has_value())) { @@ -43,10 +45,10 @@ inline axis_flags_t selected_axis_bits() { selected.bits = _BV(INDEX_OF_AXIS(E_AXIS, e)); } else - selected.bits = selected.e_bits(); + selected.bits = e_axis_mask; } #endif - selected.bits |= LINEAR_AXIS_GANG( + selected.bits |= NUM_AXIS_GANG( (parser.seen_test('X') << X_AXIS), | (parser.seen_test('Y') << Y_AXIS), | (parser.seen_test('Z') << Z_AXIS), @@ -58,7 +60,7 @@ inline axis_flags_t selected_axis_bits() { } // Enable specified axes and warn about other affected axes -void do_enable(const axis_flags_t to_enable) { +void do_enable(const stepper_flags_t to_enable) { const ena_mask_t was_enabled = stepper.axis_enabled.bits, shall_enable = to_enable.bits & ~was_enabled; @@ -69,15 +71,15 @@ void do_enable(const axis_flags_t to_enable) { ena_mask_t also_enabled = 0; // Track steppers enabled due to overlap // Enable all flagged axes - LOOP_LINEAR_AXES(a) { + LOOP_NUM_AXES(a) { if (TEST(shall_enable, a)) { stepper.enable_axis(AxisEnum(a)); // Mark and enable the requested axis - DEBUG_ECHOLNPGM("Enabled ", axis_codes[a], " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... Enabled: ", hex_word(stepper.axis_enabled.bits)); + 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]; } } #if HAS_EXTRUDERS - LOOP_L_N(e, EXTRUDERS) { + EXTRUDER_LOOP() { const uint8_t a = INDEX_OF_AXIS(E_AXIS, e); if (TEST(shall_enable, a)) { stepper.ENABLE_EXTRUDER(e); @@ -89,7 +91,7 @@ void do_enable(const axis_flags_t to_enable) { if ((also_enabled &= ~(shall_enable | was_enabled))) { SERIAL_CHAR('('); - LOOP_LINEAR_AXES(a) if (TEST(also_enabled, a)) SERIAL_CHAR(axis_codes[a], ' '); + LOOP_NUM_AXES(a) if (TEST(also_enabled, a)) SERIAL_CHAR(AXIS_CHAR(a), ' '); #if HAS_EXTRUDERS #define _EN_ALSO(N) if (TEST(also_enabled, INDEX_OF_AXIS(E_AXIS, N))) SERIAL_CHAR('E', '0' + N, ' '); REPEAT(EXTRUDERS, _EN_ALSO) @@ -125,23 +127,17 @@ void GcodeSuite::M17() { stepper.enable_e_steppers(); } #endif - LINEAR_AXIS_CODE( - if (parser.seen_test('X')) stepper.enable_axis(X_AXIS), - if (parser.seen_test('Y')) stepper.enable_axis(Y_AXIS), - if (parser.seen_test('Z')) stepper.enable_axis(Z_AXIS), - if (parser.seen_test(AXIS4_NAME)) stepper.enable_axis(I_AXIS), - if (parser.seen_test(AXIS5_NAME)) stepper.enable_axis(J_AXIS), - if (parser.seen_test(AXIS6_NAME)) stepper.enable_axis(K_AXIS) - ); + LOOP_NUM_AXES(a) + if (parser.seen_test(AXIS_CHAR(a))) stepper.enable_axis((AxisEnum)a); } } else { - LCD_MESSAGEPGM(MSG_NO_MOVE); + LCD_MESSAGE(MSG_NO_MOVE); stepper.enable_all_steppers(); } } -void try_to_disable(const axis_flags_t to_disable) { +void try_to_disable(const stepper_flags_t to_disable) { ena_mask_t still_enabled = to_disable.bits & stepper.axis_enabled.bits; DEBUG_ECHOLNPGM("Enabled: ", hex_word(stepper.axis_enabled.bits), " To Disable: ", hex_word(to_disable.bits), " | ", hex_word(still_enabled)); @@ -149,9 +145,9 @@ void try_to_disable(const axis_flags_t to_disable) { if (!still_enabled) return; // Attempt to disable all flagged axes - LOOP_LINEAR_AXES(a) + LOOP_NUM_AXES(a) if (TEST(to_disable.bits, a)) { - DEBUG_ECHOPGM("Try to disable ", axis_codes[a], " (", a, ") with overlap ", hex_word(enable_overlap[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 DEBUG_ECHOPGM("OK"); still_enabled &= ~(_BV(a) | enable_overlap[a]); // If actually disabled, clear one or more tracked bits @@ -161,7 +157,7 @@ void try_to_disable(const axis_flags_t to_disable) { DEBUG_ECHOLNPGM(" ... still_enabled=", hex_word(still_enabled)); } #if HAS_EXTRUDERS - LOOP_L_N(e, EXTRUDERS) { + EXTRUDER_LOOP() { const uint8_t a = INDEX_OF_AXIS(E_AXIS, e); if (TEST(to_disable.bits, a)) { DEBUG_ECHOPGM("Try to disable E", AS_DIGIT(e), " (", a, ") with overlap ", hex_word(enable_overlap[a]), " ... "); @@ -178,7 +174,7 @@ void try_to_disable(const axis_flags_t to_disable) { auto overlap_warning = [](const ena_mask_t axis_bits) { SERIAL_ECHOPGM(" not disabled. Shared with"); - LOOP_LINEAR_AXES(a) if (TEST(axis_bits, a)) SERIAL_CHAR(' ', axis_codes[a]); + LOOP_NUM_AXES(a) if (TEST(axis_bits, a)) SERIAL_ECHOPGM_P((PGM_P)pgm_read_ptr(&SP_AXIS_STR[a])); #if HAS_EXTRUDERS #define _EN_STILLON(N) if (TEST(axis_bits, INDEX_OF_AXIS(E_AXIS, N))) SERIAL_CHAR(' ', 'E', '0' + N); REPEAT(EXTRUDERS, _EN_STILLON) @@ -187,14 +183,14 @@ void try_to_disable(const axis_flags_t to_disable) { }; // If any of the requested axes are still enabled, give a warning - LOOP_LINEAR_AXES(a) { + LOOP_NUM_AXES(a) { if (TEST(still_enabled, a)) { - SERIAL_CHAR(axis_codes[a]); + SERIAL_CHAR(AXIS_CHAR(a)); overlap_warning(stepper.axis_enabled.bits & enable_overlap[a]); } } #if HAS_EXTRUDERS - LOOP_L_N(e, EXTRUDERS) { + EXTRUDER_LOOP() { const uint8_t a = INDEX_OF_AXIS(E_AXIS, e); if (TEST(still_enabled, a)) { SERIAL_CHAR('E', '0' + e); @@ -213,7 +209,16 @@ void try_to_disable(const axis_flags_t to_disable) { void GcodeSuite::M18_M84() { if (parser.seenval('S')) { reset_stepper_timeout(); - stepper_inactive_time = parser.value_millis_from_seconds(); + #if HAS_DISABLE_INACTIVE_AXIS + const millis_t ms = parser.value_millis_from_seconds(); + #if LASER_SAFETY_TIMEOUT_MS > 0 + if (ms && ms <= LASER_SAFETY_TIMEOUT_MS) { + SERIAL_ECHO_MSG("M18 timeout must be > ", MS_TO_SEC(LASER_SAFETY_TIMEOUT_MS + 999), " s for laser safety."); + return; + } + #endif + stepper_inactive_time = ms; + #endif } else { if (parser.seen_axis()) { @@ -229,19 +234,13 @@ void GcodeSuite::M18_M84() { stepper.disable_e_steppers(); } #endif - LINEAR_AXIS_CODE( - if (parser.seen_test('X')) stepper.disable_axis(X_AXIS), - if (parser.seen_test('Y')) stepper.disable_axis(Y_AXIS), - if (parser.seen_test('Z')) stepper.disable_axis(Z_AXIS), - if (parser.seen_test(AXIS4_NAME)) stepper.disable_axis(I_AXIS), - if (parser.seen_test(AXIS5_NAME)) stepper.disable_axis(J_AXIS), - if (parser.seen_test(AXIS6_NAME)) stepper.disable_axis(K_AXIS) - ); + LOOP_NUM_AXES(a) + if (parser.seen_test(AXIS_CHAR(a))) stepper.disable_axis((AxisEnum)a); } } else planner.finish_and_disable(); - TERN_(AUTO_BED_LEVELING_UBL, ubl.steppers_were_disabled()); + TERN_(AUTO_BED_LEVELING_UBL, bedlevel.steppers_were_disabled()); } } diff --git a/Marlin/src/gcode/control/M211.cpp b/Marlin/src/gcode/control/M211.cpp index a837d795339be..95ae052a7bd4b 100644 --- a/Marlin/src/gcode/control/M211.cpp +++ b/Marlin/src/gcode/control/M211.cpp @@ -40,15 +40,15 @@ void GcodeSuite::M211() { } void GcodeSuite::M211_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_SOFT_ENDSTOPS)); + report_heading_etc(forReplay, F(STR_SOFT_ENDSTOPS)); SERIAL_ECHOPGM(" M211 S", AS_DIGIT(soft_endstop._enabled), " ; "); serialprintln_onoff(soft_endstop._enabled); report_echo_start(forReplay); const xyz_pos_t l_soft_min = soft_endstop.min.asLogical(), l_soft_max = soft_endstop.max.asLogical(); - print_pos(l_soft_min, PSTR(STR_SOFT_MIN), PSTR(" ")); - print_pos(l_soft_max, PSTR(STR_SOFT_MAX)); + print_pos(l_soft_min, F(STR_SOFT_MIN), F(" ")); + print_pos(l_soft_max, F(STR_SOFT_MAX)); } #endif // HAS_SOFTWARE_ENDSTOPS diff --git a/Marlin/src/gcode/control/M226.cpp b/Marlin/src/gcode/control/M226.cpp index 63f022e82bd84..4eb3db4bc31fb 100644 --- a/Marlin/src/gcode/control/M226.cpp +++ b/Marlin/src/gcode/control/M226.cpp @@ -26,7 +26,7 @@ #include "../gcode.h" #include "../../MarlinCore.h" // for pin_is_protected and idle() -#include "../../module/stepper.h" +#include "../../module/planner.h" void protected_pin_err(); diff --git a/Marlin/src/gcode/control/M280.cpp b/Marlin/src/gcode/control/M280.cpp index 2a8e73eafbf27..82981e44bcee8 100644 --- a/Marlin/src/gcode/control/M280.cpp +++ b/Marlin/src/gcode/control/M280.cpp @@ -48,7 +48,7 @@ void GcodeSuite::M280() { const int anew = parser.value_int(); if (anew >= 0) { #if ENABLED(POLARGRAPH) - if (parser.seen('T')) { // (ms) Total duration of servo move + if (parser.seenval('T')) { // (ms) Total duration of servo move const int16_t t = constrain(parser.value_int(), 0, 10000); const int aold = servo[servo_index].read(); millis_t now = millis(); @@ -56,14 +56,14 @@ void GcodeSuite::M280() { while (PENDING(now, end)) { safe_delay(50); now = _MIN(millis(), end); - MOVE_SERVO(servo_index, LROUND(aold + (anew - aold) * (float(now - start) / t))); + servo[servo_index].move(LROUND(aold + (anew - aold) * (float(now - start) / t))); } } #endif // POLARGRAPH - MOVE_SERVO(servo_index, anew); + servo[servo_index].move(anew); } else - DETACH_SERVO(servo_index); + servo[servo_index].detach(); } else SERIAL_ECHO_MSG(" Servo ", servo_index, ": ", servo[servo_index].read()); diff --git a/Marlin/src/gcode/control/M282.cpp b/Marlin/src/gcode/control/M282.cpp index e6f5ce7dccc3a..3ac5ac9f5bfc5 100644 --- a/Marlin/src/gcode/control/M282.cpp +++ b/Marlin/src/gcode/control/M282.cpp @@ -36,7 +36,7 @@ void GcodeSuite::M282() { const int servo_index = parser.value_int(); if (WITHIN(servo_index, 0, NUM_SERVOS - 1)) - DETACH_SERVO(servo_index); + servo[servo_index].detach(); else SERIAL_ECHO_MSG("Servo ", servo_index, " out of range"); diff --git a/Marlin/src/gcode/control/M3-M5.cpp b/Marlin/src/gcode/control/M3-M5.cpp index ecae8b06c69fe..5d5d44e8bfe8f 100644 --- a/Marlin/src/gcode/control/M3-M5.cpp +++ b/Marlin/src/gcode/control/M3-M5.cpp @@ -26,24 +26,34 @@ #include "../gcode.h" #include "../../feature/spindle_laser.h" -#include "../../module/stepper.h" +#include "../../module/planner.h" /** * Laser: * M3 - Laser ON/Power (Ramped power) - * M4 - Laser ON/Power (Continuous power) + * M4 - Laser ON/Power (Ramped power) + * M5 - Set power output to 0 (leaving inline mode unchanged). + * + * M3I - Enable continuous inline power to be processed by the planner, with power + * calculated and set in the planner blocks, processed inline during stepping. + * Within inline mode M3 S-Values will set the power for the next moves e.g. G1 X10 Y10 powers on with the last S-Value. + * M3I must be set before using planner-synced M3 inline S-Values (LASER_POWER_SYNC). + * + * M4I - Set dynamic mode which calculates laser power OCR based on the current feedrate. + * + * M5I - Clear inline mode and set power to 0. * * Spindle: * M3 - Spindle ON (Clockwise) * M4 - Spindle ON (Counter-clockwise) + * M5 - Spindle OFF * * Parameters: - * S - Set power. S0 will turn the spindle/laser off, except in relative mode. - * O - Set power and OCR (oscillator count register) + * S - Set power. S0 will turn the spindle/laser off. * - * If no PWM pin is defined then M3/M4 just turns it on. + * If no PWM pin is defined then M3/M4 just turns it on or off. * - * At least 12.8KHz (50Hz * 256) is needed for Spindle PWM. + * At least 12.8kHz (50Hz * 256) is needed for Spindle PWM. * Hardware PWM is required on AVR. ISRs are too slow. * * NOTE: WGM for timers 3, 4, and 5 must be either Mode 1 or Mode 5. @@ -66,75 +76,81 @@ * PWM duty cycle goes from 0 (off) to 255 (always on). */ void GcodeSuite::M3_M4(const bool is_M4) { + #if LASER_SAFETY_TIMEOUT_MS > 0 + reset_stepper_timeout(); // Reset timeout to allow subsequent G-code to power the laser (imm.) + #endif + + if (cutter.cutter_mode == CUTTER_MODE_STANDARD) + planner.synchronize(); // Wait for previous movement commands (G0/G1/G2/G3) to complete before changing power + + #if ENABLED(LASER_FEATURE) + if (parser.seen_test('I')) { + cutter.cutter_mode = is_M4 ? CUTTER_MODE_DYNAMIC : CUTTER_MODE_CONTINUOUS; + cutter.inline_power(0); + cutter.set_enabled(true); + } + #endif + auto get_s_power = [] { + float u; if (parser.seenval('S')) { - const float spwr = parser.value_float(); - #if ENABLED(SPINDLE_SERVO) - cutter.unitPower = spwr; - #else - cutter.unitPower = TERN(SPINDLE_LASER_USE_PWM, - cutter.power_to_range(cutter_power_t(round(spwr))), - spwr > 0 ? 255 : 0); - #endif + const float v = parser.value_float(); + u = TERN(LASER_POWER_TRAP, v, cutter.power_to_range(v)); } - else - cutter.unitPower = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP); - return cutter.unitPower; + else if (cutter.cutter_mode == CUTTER_MODE_STANDARD) + u = cutter.cpwr_to_upwr(SPEED_POWER_STARTUP); + + cutter.menuPower = cutter.unitPower = u; + + // PWM not implied, power converted to OCR from unit definition and on/off if not PWM. + cutter.power = TERN(SPINDLE_LASER_USE_PWM, cutter.upower_to_ocr(u), u > 0 ? 255 : 0); + return u; }; - #if ENABLED(LASER_POWER_INLINE) - if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) { - // Laser power in inline mode - cutter.inline_direction(is_M4); // Should always be unused - #if ENABLED(SPINDLE_LASER_USE_PWM) - if (parser.seen('O')) { - cutter.unitPower = cutter.power_to_range(parser.value_byte(), 0); - cutter.inline_ocr_power(cutter.unitPower); // The OCR is a value from 0 to 255 (uint8_t) - } - else - cutter.inline_power(cutter.upower_to_ocr(get_s_power())); + if (cutter.cutter_mode == CUTTER_MODE_CONTINUOUS || cutter.cutter_mode == CUTTER_MODE_DYNAMIC) { // Laser power in inline mode + #if ENABLED(LASER_FEATURE) + planner.laser_inline.status.isPowered = true; // M3 or M4 is powered either way + get_s_power(); // Update cutter.power if seen + #if ENABLED(LASER_POWER_SYNC) + // With power sync we only set power so it does not effect queued inline power sets + planner.buffer_sync_block(BLOCK_BIT_LASER_PWR); // Send the flag, queueing inline power #else - cutter.set_inline_enabled(true); + planner.synchronize(); + cutter.inline_power(cutter.power); #endif - return; - } - // Non-inline, standard case - cutter.inline_disable(); // Prevent future blocks re-setting the power - #endif - - planner.synchronize(); // Wait for previous movement commands (G0/G0/G2/G3) to complete before changing power - cutter.set_reverse(is_M4); - - #if ENABLED(SPINDLE_LASER_USE_PWM) - if (parser.seenval('O')) { - cutter.unitPower = cutter.power_to_range(parser.value_byte(), 0); - cutter.ocr_set_power(cutter.unitPower); // The OCR is a value from 0 to 255 (uint8_t) - } - else - cutter.set_power(cutter.upower_to_ocr(get_s_power())); - #elif ENABLED(SPINDLE_SERVO) - cutter.set_power(get_s_power()); - #else + #endif + } + else { cutter.set_enabled(true); - #endif - cutter.menuPower = cutter.unitPower; + get_s_power(); + cutter.apply_power( + #if ENABLED(SPINDLE_SERVO) + cutter.unitPower + #elif ENABLED(SPINDLE_LASER_USE_PWM) + cutter.upower_to_ocr(cutter.unitPower) + #else + cutter.unitPower > 0 ? 255 : 0 + #endif + ); + TERN_(SPINDLE_CHANGE_DIR, cutter.set_reverse(is_M4)); + } } /** * M5 - Cutter OFF (when moves are complete) */ void GcodeSuite::M5() { - #if ENABLED(LASER_POWER_INLINE) - if (parser.seen('I') == DISABLED(LASER_POWER_INLINE_INVERT)) { - cutter.set_inline_enabled(false); // Laser power in inline mode - return; - } - // Non-inline, standard case - cutter.inline_disable(); // Prevent future blocks re-setting the power - #endif planner.synchronize(); - cutter.set_enabled(false); - cutter.menuPower = cutter.unitPower; + cutter.power = 0; + cutter.apply_power(0); // M5 just kills power, leaving inline mode unchanged + if (cutter.cutter_mode != CUTTER_MODE_STANDARD) { + if (parser.seen_test('I')) { + TERN_(LASER_FEATURE, cutter.inline_power(cutter.power)); + cutter.set_enabled(false); // Needs to happen while we are in inline mode to clear inline power. + cutter.cutter_mode = CUTTER_MODE_STANDARD; // Switch from inline to standard mode. + } + } + cutter.set_enabled(false); // Disable enable output setting } #endif // HAS_CUTTER diff --git a/Marlin/src/gcode/control/M350_M351.cpp b/Marlin/src/gcode/control/M350_M351.cpp index a92238e4bbf2f..ac6b5a329b70a 100644 --- a/Marlin/src/gcode/control/M350_M351.cpp +++ b/Marlin/src/gcode/control/M350_M351.cpp @@ -27,35 +27,45 @@ #include "../gcode.h" #include "../../module/stepper.h" +#if NUM_AXES == XYZ && EXTRUDERS >= 1 + #define HAS_M350_B_PARAM 1 // "5th axis" (after E0) for an original XYZEB setup. + #if AXIS_COLLISION('B') + #error "M350 parameter 'B' collision with axis name." + #endif +#endif + /** * M350: Set axis microstepping modes. S sets mode for all drivers. * * Warning: Steps-per-unit remains unchanged. */ void GcodeSuite::M350() { - if (parser.seen('S')) LOOP_LE_N(i, 4) stepper.microstep_mode(i, parser.value_byte()); - LOOP_LOGICAL_AXES(i) if (parser.seen(axis_codes[i])) stepper.microstep_mode(i, parser.value_byte()); - if (parser.seen('B')) stepper.microstep_mode(4, parser.value_byte()); + if (parser.seen('S')) LOOP_DISTINCT_AXES(i) stepper.microstep_mode(i, parser.value_byte()); + LOOP_LOGICAL_AXES(i) if (parser.seenval(AXIS_CHAR(i))) stepper.microstep_mode(i, parser.value_byte()); + TERN_(HAS_M350_B_PARAM, if (parser.seenval('B')) stepper.microstep_mode(E_AXIS + 1, parser.value_byte())); stepper.microstep_readings(); } /** - * M351: Toggle MS1 MS2 pins directly with axis codes X Y Z E B + * M351: Toggle MS1 MS2 pins directly with axis codes X Y Z . . . E [B] * S# determines MS1, MS2 or MS3, X# sets the pin high/low. + * + * Parameter 'B' sets "5th axis" (after E0) only for an original XYZEB setup. */ void GcodeSuite::M351() { + const int8_t bval = TERN(HAS_M350_B_PARAM, parser.byteval('B', -1), -1); UNUSED(bval); if (parser.seenval('S')) switch (parser.value_byte()) { case 1: - LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, parser.value_byte(), -1, -1); - if (parser.seenval('B')) stepper.microstep_ms(4, parser.value_byte(), -1, -1); + LOOP_LOGICAL_AXES(i) if (parser.seenval(AXIS_CHAR(i))) stepper.microstep_ms(i, parser.value_byte(), -1, -1); + TERN_(HAS_M350_B_PARAM, if (bval >= 0) stepper.microstep_ms(E_AXIS + 1, bval != 0, -1, -1)); break; case 2: - LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, parser.value_byte(), -1); - if (parser.seenval('B')) stepper.microstep_ms(4, -1, parser.value_byte(), -1); + LOOP_LOGICAL_AXES(i) if (parser.seenval(AXIS_CHAR(i))) stepper.microstep_ms(i, -1, parser.value_byte(), -1); + TERN_(HAS_M350_B_PARAM, if (bval >= 0) stepper.microstep_ms(E_AXIS + 1, -1, bval != 0, -1)); break; case 3: - LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.microstep_ms(i, -1, -1, parser.value_byte()); - if (parser.seenval('B')) stepper.microstep_ms(4, -1, -1, parser.value_byte()); + LOOP_LOGICAL_AXES(i) if (parser.seenval(AXIS_CHAR(i))) stepper.microstep_ms(i, -1, -1, parser.value_byte()); + TERN_(HAS_M350_B_PARAM, if (bval >= 0) stepper.microstep_ms(E_AXIS + 1, -1, -1, bval != 0)); break; } stepper.microstep_readings(); diff --git a/Marlin/src/gcode/control/M380_M381.cpp b/Marlin/src/gcode/control/M380_M381.cpp index 3f5b252465438..6bcec891e2818 100644 --- a/Marlin/src/gcode/control/M380_M381.cpp +++ b/Marlin/src/gcode/control/M380_M381.cpp @@ -37,7 +37,7 @@ void GcodeSuite::M380() { #if ENABLED(MANUAL_SOLENOID_CONTROL) enable_solenoid(parser.intval('S', active_extruder)); #else - enable_solenoid_on_active_extruder(); + enable_solenoid(active_extruder); #endif } diff --git a/Marlin/src/gcode/control/M400.cpp b/Marlin/src/gcode/control/M400.cpp index 9a5ad4e9df80f..6058fb894e976 100644 --- a/Marlin/src/gcode/control/M400.cpp +++ b/Marlin/src/gcode/control/M400.cpp @@ -21,7 +21,7 @@ */ #include "../gcode.h" -#include "../../module/stepper.h" +#include "../../module/planner.h" /** * M400: Finish all moves diff --git a/Marlin/src/gcode/control/M42.cpp b/Marlin/src/gcode/control/M42.cpp index eead971a012c3..1b3a29d10056a 100644 --- a/Marlin/src/gcode/control/M42.cpp +++ b/Marlin/src/gcode/control/M42.cpp @@ -52,7 +52,7 @@ void protected_pin_err() { * S Pin status from 0 - 255 * I Flag to ignore Marlin's pin protection * - * M Pin mode: 0=INPUT 1=OUTPUT 2=INPUT_PULLUP 3=INPUT_PULLDOWN + * T Pin mode: 0=INPUT 1=OUTPUT 2=INPUT_PULLUP 3=INPUT_PULLDOWN */ void GcodeSuite::M42() { const int pin_index = PARSED_PIN_INDEX('P', GET_PIN_MAP_INDEX(LED_PIN)); @@ -63,7 +63,7 @@ void GcodeSuite::M42() { if (!parser.boolval('I') && pin_is_protected(pin)) return protected_pin_err(); bool avoidWrite = false; - if (parser.seenval('M')) { + if (parser.seenval('T')) { switch (parser.value_byte()) { case 0: pinMode(pin, INPUT); avoidWrite = true; break; case 1: pinMode(pin, OUTPUT); break; @@ -126,10 +126,10 @@ void GcodeSuite::M42() { extDigitalWrite(pin, pin_status); #ifdef ARDUINO_ARCH_STM32 - // A simple I/O will be set to 0 by analogWrite() + // A simple I/O will be set to 0 by hal.set_pwm_duty() if (pin_status <= 1 && !PWM_PIN(pin)) return; #endif - analogWrite(pin, pin_status); + hal.set_pwm_duty(pin, pin_status); } #endif // DIRECT_PIN_CONTROL diff --git a/Marlin/src/gcode/control/M605.cpp b/Marlin/src/gcode/control/M605.cpp index 08efaab59d75e..e3ca43e17fd5f 100644 --- a/Marlin/src/gcode/control/M605.cpp +++ b/Marlin/src/gcode/control/M605.cpp @@ -28,7 +28,6 @@ #include "../gcode.h" #include "../../module/motion.h" -#include "../../module/stepper.h" #include "../../module/tool_change.h" #include "../../module/planner.h" @@ -64,7 +63,7 @@ void GcodeSuite::M605() { planner.synchronize(); - if (parser.seen('S')) { + if (parser.seenval('S')) { const DualXMode previous_mode = dual_x_carriage_mode; dual_x_carriage_mode = (DualXMode)parser.value_byte(); @@ -78,8 +77,8 @@ case DXC_DUPLICATION_MODE: // Set the X offset, but no less than the safety gap - if (parser.seen('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS)); - if (parser.seen('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff(); + if (parser.seenval('X')) duplicate_extruder_x_offset = _MAX(parser.value_linear_units(), (X2_MIN_POS) - (X1_MIN_POS)); + if (parser.seenval('R')) duplicate_extruder_temp_offset = parser.value_celsius_diff(); // Always switch back to tool 0 if (active_extruder != 0) tool_change(0); break; @@ -110,7 +109,7 @@ set_duplication_enabled(false); #ifdef EVENT_GCODE_IDEX_AFTER_MODECHANGE - gcode.process_subcommands_now_P(PSTR(EVENT_GCODE_IDEX_AFTER_MODECHANGE)); + process_subcommands_now(F(EVENT_GCODE_IDEX_AFTER_MODECHANGE)); #endif } else if (!parser.seen('W')) // if no S or W parameter, the DXC mode gets reset to the user's default @@ -146,7 +145,7 @@ HOTEND_LOOP() { DEBUG_ECHOPGM_P(SP_T_STR, e); - LOOP_LINEAR_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", AS_CHAR(AXIS_CHAR(a) | 0x20), "=", hotend_offset[e][a]); + LOOP_NUM_AXES(a) DEBUG_ECHOPGM(" hotend_offset[", e, "].", AS_CHAR(AXIS_CHAR(a) | 0x20), "=", hotend_offset[e][a]); DEBUG_EOL(); } DEBUG_EOL(); diff --git a/Marlin/src/gcode/control/M80_M81.cpp b/Marlin/src/gcode/control/M80_M81.cpp index 149613ee15c86..90b25e7ed34db 100644 --- a/Marlin/src/gcode/control/M80_M81.cpp +++ b/Marlin/src/gcode/control/M80_M81.cpp @@ -25,7 +25,7 @@ #include "../../module/temperature.h" #include "../../module/planner.h" // for planner.finish_and_disable #include "../../module/printcounter.h" // for print_job_timer.stop -#include "../../lcd/marlinui.h" // for LCD_MESSAGEPGM_P +#include "../../lcd/marlinui.h" // for LCD_MESSAGE_F #include "../../inc/MarlinConfig.h" @@ -48,7 +48,7 @@ // S: Report the current power supply state and exit if (parser.seen('S')) { - SERIAL_ECHOPGM_P(powerManager.psu_on ? PSTR("PS:1\n") : PSTR("PS:0\n")); + SERIAL_ECHOF(powerManager.psu_on ? F("PS:1\n") : F("PS:0\n")); return; } @@ -63,7 +63,7 @@ OUT_WRITE(SUICIDE_PIN, !SUICIDE_PIN_STATE); #endif - TERN_(HAS_LCD_MENU, ui.reset_status()); + TERN_(HAS_MARLINUI_MENU, ui.reset_status()); } #endif // PSU_CONTROL @@ -74,26 +74,47 @@ * This code should ALWAYS be available for FULL SHUTDOWN! */ void GcodeSuite::M81() { - thermalManager.disable_all_heaters(); planner.finish_and_disable(); + thermalManager.cooldown(); print_job_timer.stop(); - #if HAS_FAN - thermalManager.zero_fan_speeds(); - #if ENABLED(PROBING_FANS_OFF) - thermalManager.fans_paused = false; - ZERO(thermalManager.saved_fan_speed); - #endif + #if BOTH(HAS_FAN, PROBING_FANS_OFF) + thermalManager.fans_paused = false; + ZERO(thermalManager.saved_fan_speed); #endif safe_delay(1000); // Wait 1 second before switching off + LCD_MESSAGE_F(MACHINE_NAME " " STR_OFF "."); + + bool delayed_power_off = false; + + #if ENABLED(POWER_OFF_TIMER) + if (parser.seenval('D')) { + uint16_t delay = parser.value_ushort(); + if (delay > 1) { // skip already observed 1s delay + delayed_power_off = true; + powerManager.setPowerOffTimer(SEC_TO_MS(delay - 1)); + } + } + #endif + + #if ENABLED(POWER_OFF_WAIT_FOR_COOLDOWN) + if (parser.boolval('S')) { + delayed_power_off = true; + powerManager.setPowerOffOnCooldown(true); + } + #endif + + if (delayed_power_off) { + SERIAL_ECHOLNPGM(STR_DELAYED_POWEROFF); + return; + } + #if HAS_SUICIDE suicide(); #elif ENABLED(PSU_CONTROL) powerManager.power_off_soon(); #endif - - LCD_MESSAGEPGM_P(PSTR(MACHINE_NAME " " STR_OFF ".")); } diff --git a/Marlin/src/gcode/control/M85.cpp b/Marlin/src/gcode/control/M85.cpp index 9c8c02c59aa0d..ee868349ed753 100644 --- a/Marlin/src/gcode/control/M85.cpp +++ b/Marlin/src/gcode/control/M85.cpp @@ -29,7 +29,14 @@ void GcodeSuite::M85() { if (parser.seen('S')) { reset_stepper_timeout(); - max_inactive_time = parser.value_millis_from_seconds(); + const millis_t ms = parser.value_millis_from_seconds(); + #if LASER_SAFETY_TIMEOUT_MS > 0 + if (ms && ms <= LASER_SAFETY_TIMEOUT_MS) { + SERIAL_ECHO_MSG("M85 timeout must be > ", MS_TO_SEC(LASER_SAFETY_TIMEOUT_MS + 999), " s for laser safety."); + return; + } + #endif + max_inactive_time = ms; } } diff --git a/Marlin/src/gcode/control/M997.cpp b/Marlin/src/gcode/control/M997.cpp index 73d795bcefc80..74ed8b0d073e8 100644 --- a/Marlin/src/gcode/control/M997.cpp +++ b/Marlin/src/gcode/control/M997.cpp @@ -24,8 +24,8 @@ #if ENABLED(PLATFORM_M997_SUPPORT) -#if ENABLED(DWIN_CREALITY_LCD_ENHANCED) - #include "../../lcd/e3v2/enhanced/dwin.h" +#if ENABLED(DWIN_LCD_PROUI) + #include "../../lcd/e3v2/proui/dwin.h" #endif /** @@ -33,7 +33,7 @@ */ void GcodeSuite::M997() { - TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_RebootScreen()); + TERN_(DWIN_LCD_PROUI, DWIN_RebootScreen()); flashFirmware(parser.intval('S')); diff --git a/Marlin/src/gcode/control/M999.cpp b/Marlin/src/gcode/control/M999.cpp index b7219673a3d66..b7d6db9f23128 100644 --- a/Marlin/src/gcode/control/M999.cpp +++ b/Marlin/src/gcode/control/M999.cpp @@ -22,7 +22,7 @@ #include "../gcode.h" -#include "../../lcd/marlinui.h" // for lcd_reset_alert_level +#include "../../lcd/marlinui.h" // for ui.reset_alert_level #include "../../MarlinCore.h" // for marlin_state #include "../queue.h" // for flush_and_request_resend diff --git a/Marlin/src/gcode/eeprom/M500-M504.cpp b/Marlin/src/gcode/eeprom/M500-M504.cpp index a06e98ad1ea79..412d0033558ff 100644 --- a/Marlin/src/gcode/eeprom/M500-M504.cpp +++ b/Marlin/src/gcode/eeprom/M500-M504.cpp @@ -25,6 +25,11 @@ #include "../../core/serial.h" #include "../../inc/MarlinConfig.h" +#if ENABLED(CONFIGURATION_EMBEDDING) + #include "../../sd/SdBaseFile.h" + #include "../../mczip.h" +#endif + /** * M500: Store settings in EEPROM */ @@ -50,9 +55,24 @@ void GcodeSuite::M502() { /** * M503: print settings currently in memory + * + * S : Include / exclude header comments in the output. (Default: S1) + * + * With CONFIGURATION_EMBEDDING: + * C : Save the full Marlin configuration to SD Card as "mc.zip" */ void GcodeSuite::M503() { (void)settings.report(!parser.boolval('S', true)); + + #if ENABLED(CONFIGURATION_EMBEDDING) + if (parser.seen_test('C')) { + SdBaseFile file; + const uint16_t size = sizeof(mc_zip); + // Need to create the config size on the SD card + if (file.open("mc.zip", O_WRITE|O_CREAT) && file.write(pgm_read_ptr(mc_zip), size) != -1 && file.close()) + SERIAL_ECHO_MSG("Configuration saved as 'mc.zip'"); + } + #endif } #endif // !DISABLE_M503 diff --git a/Marlin/src/gcode/feature/L6470/M122.cpp b/Marlin/src/gcode/feature/L6470/M122.cpp index 1e5b37e4b76f7..4a5629b049209 100644 --- a/Marlin/src/gcode/feature/L6470/M122.cpp +++ b/Marlin/src/gcode/feature/L6470/M122.cpp @@ -47,10 +47,10 @@ inline void L6470_say_status(const L64XX_axis_t axis) { } #endif SERIAL_ECHOPGM("\n...OUTPUT: "); - SERIAL_ECHOPGM_P(sh.STATUS_AXIS & STATUS_HIZ ? PSTR("OFF") : PSTR("ON ")); + SERIAL_ECHOF(sh.STATUS_AXIS & STATUS_HIZ ? F("OFF") : F("ON ")); SERIAL_ECHOPGM(" BUSY: "); echo_yes_no((sh.STATUS_AXIS & STATUS_BUSY) == 0); SERIAL_ECHOPGM(" DIR: "); - SERIAL_ECHOPGM_P((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? PSTR("FORWARD") : PSTR("REVERSE")); + SERIAL_ECHOF((((sh.STATUS_AXIS & STATUS_DIR) >> 4) ^ L64xxManager.index_to_dir[axis]) ? F("FORWARD") : F("REVERSE")); if (sh.STATUS_AXIS_LAYOUT == L6480_STATUS_LAYOUT) { SERIAL_ECHOPGM(" Last Command: "); if (sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD) SERIAL_ECHOPGM("VALID"); @@ -67,7 +67,7 @@ inline void L6470_say_status(const L64XX_axis_t axis) { SERIAL_ECHOPGM(" Last Command: "); if (!(sh.STATUS_AXIS & sh.STATUS_AXIS_WRONG_CMD)) SERIAL_ECHOPGM("IN"); SERIAL_ECHOPGM("VALID "); - SERIAL_ECHOPGM_P(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? PSTR("COMPLETED ") : PSTR("Not PERFORMED")); + SERIAL_ECHOF(sh.STATUS_AXIS & sh.STATUS_AXIS_NOTPERF_CMD ? F("COMPLETED ") : F("Not PERFORMED")); SERIAL_ECHOPGM("\n...THERMAL: ", !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_SD) ? "SHUTDOWN " : !(sh.STATUS_AXIS & sh.STATUS_AXIS_TH_WRN) ? "WARNING " : "OK "); } SERIAL_ECHOPGM(" OVERCURRENT:"); echo_yes_no((sh.STATUS_AXIS & sh.STATUS_AXIS_OCD) == 0); diff --git a/Marlin/src/gcode/feature/L6470/M906.cpp b/Marlin/src/gcode/feature/L6470/M906.cpp index bab355fe38c2b..ee0211dbadf28 100644 --- a/Marlin/src/gcode/feature/L6470/M906.cpp +++ b/Marlin/src/gcode/feature/L6470/M906.cpp @@ -24,6 +24,10 @@ #if HAS_L64XX +#if AXIS_COLLISION('I') + #error "M906 parameter 'I' collision with axis name." +#endif + #include "../../gcode.h" #include "../../../libs/L64XX/L64XX_Marlin.h" #include "../../../module/stepper/indirection.h" @@ -107,7 +111,7 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) { SERIAL_ECHOPGM("...MicroSteps: ", MicroSteps, " ADC_OUT: ", L6470_ADC_out); SERIAL_ECHOPGM(" Vs_compensation: "); - SERIAL_ECHOPGM_P((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? PSTR("ENABLED ") : PSTR("DISABLED")); + SERIAL_ECHOF((motor.GetParam(sh.L6470_AXIS_CONFIG) & CONFIG_EN_VSCOMP) ? F("ENABLED ") : F("DISABLED")); SERIAL_ECHOLNPGM(" Compensation coefficient: ~", comp_coef * 0.01f); SERIAL_ECHOPGM("...KVAL_HOLD: ", motor.GetParam(L6470_KVAL_HOLD), @@ -202,13 +206,12 @@ void L64XX_report_current(L64XX &motor, const L64XX_axis_t axis) { * On L6474 this sets the TVAL register (same address). * * I - select which driver(s) to change on multi-driver axis - * 0 - (default) all drivers on the axis or E0 - * 1 - monitor only X, Y, Z or E1 - * 2 - monitor only X2, Y2, Z2 or E2 - * 3 - monitor only Z3 or E3 - * 4 - monitor only Z4 or E4 - * 5 - monitor only E5 - * Xxxx, Yxxx, Zxxx, Exxx - axis to change (optional) + * (default) all drivers on the axis + * 0 - monitor only the first XYZ... driver + * 1 - monitor only X2, Y2, Z2 + * 2 - monitor only Z3 + * 3 - monitor only Z4 + * Xxxx, Yxxx, Zxxx, Axxx, Bxxx, Cxxx, Exxx - axis to change (optional) * L6474 - current in mA (4A max) * All others - 0-255 * @@ -227,11 +230,13 @@ void GcodeSuite::M906() { uint8_t report_current = true; - #if HAS_L64XX - const uint8_t index = parser.byteval('I'); + #if AXIS_IS_L64XX(X2) || AXIS_IS_L64XX(Y2) || AXIS_IS_L64XX(Z2) || AXIS_IS_L64XX(Z3) || AXIS_IS_L64XX(Z4) + const int8_t index = parser.byteval('I', -1); + #else + constexpr int8_t index = -1; #endif - LOOP_LOGICAL_AXES(i) if (uint16_t value = parser.intval(axis_codes[i])) { + LOOP_LOGICAL_AXES(i) if (uint16_t value = parser.intval(AXIS_CHAR(i))) { report_current = false; @@ -241,73 +246,72 @@ void GcodeSuite::M906() { } switch (i) { - case X_AXIS: - #if AXIS_IS_L64XX(X) - if (index == 0) L6470_SET_KVAL_HOLD(X); - #endif - #if AXIS_IS_L64XX(X2) - if (index == 1) L6470_SET_KVAL_HOLD(X2); - #endif - break; - - #if HAS_Y_AXIS + #if AXIS_IS_L64XX(X) || AXIS_IS_L64XX(X2) + case X_AXIS: + #if AXIS_IS_L64XX(X) + if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(X); + #endif + #if AXIS_IS_L64XX(X2) + if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(X2); + #endif + break; + #endif + + #if AXIS_IS_L64XX(Y) || AXIS_IS_L64XX(Y2) case Y_AXIS: #if AXIS_IS_L64XX(Y) - if (index == 0) L6470_SET_KVAL_HOLD(Y); + if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(Y); #endif #if AXIS_IS_L64XX(Y2) - if (index == 1) L6470_SET_KVAL_HOLD(Y2); + if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(Y2); #endif break; #endif - #if HAS_Z_AXIS + #if AXIS_IS_L64XX(Z) || AXIS_IS_L64XX(Z2) || AXIS_IS_L64XX(Z3) || AXIS_IS_L64XX(Z4) case Z_AXIS: #if AXIS_IS_L64XX(Z) - if (index == 0) L6470_SET_KVAL_HOLD(Z); + if (index < 0 || index == 0) L6470_SET_KVAL_HOLD(Z); #endif #if AXIS_IS_L64XX(Z2) - if (index == 1) L6470_SET_KVAL_HOLD(Z2); + if (index < 0 || index == 1) L6470_SET_KVAL_HOLD(Z2); #endif #if AXIS_IS_L64XX(Z3) - if (index == 2) L6470_SET_KVAL_HOLD(Z3); + if (index < 0 || index == 2) L6470_SET_KVAL_HOLD(Z3); #endif - #if AXIS_DRIVER_TYPE_Z4(L6470) - if (index == 3) L6470_SET_KVAL_HOLD(Z4); + #if AXIS_IS_L64XX(Z4) + if (index < 0 || index == 3) L6470_SET_KVAL_HOLD(Z4); #endif break; #endif - #if E_STEPPERS + #if AXIS_IS_L64XX(E0) || AXIS_IS_L64XX(E1) || AXIS_IS_L64XX(E2) || AXIS_IS_L64XX(E3) || AXIS_IS_L64XX(E4) || AXIS_IS_L64XX(E5) || AXIS_IS_L64XX(E6) || AXIS_IS_L64XX(E7) case E_AXIS: { - const int8_t target_e_stepper = get_target_e_stepper_from_command(0); - if (target_e_stepper < 0) return; - switch (target_e_stepper) { - #if AXIS_IS_L64XX(E0) - case 0: L6470_SET_KVAL_HOLD(E0); break; - #endif - #if AXIS_IS_L64XX(E1) - case 1: L6470_SET_KVAL_HOLD(E1); break; - #endif - #if AXIS_IS_L64XX(E2) - case 2: L6470_SET_KVAL_HOLD(E2); break; - #endif - #if AXIS_IS_L64XX(E3) - case 3: L6470_SET_KVAL_HOLD(E3); break; - #endif - #if AXIS_IS_L64XX(E4) - case 4: L6470_SET_KVAL_HOLD(E4); break; - #endif - #if AXIS_IS_L64XX(E5) - case 5: L6470_SET_KVAL_HOLD(E5); break; - #endif - #if AXIS_IS_L64XX(E6) - case 6: L6470_SET_KVAL_HOLD(E6); break; - #endif - #if AXIS_IS_L64XX(E7) - case 7: L6470_SET_KVAL_HOLD(E7); break; - #endif - } + const int8_t eindex = get_target_e_stepper_from_command(-2); + #if AXIS_IS_L64XX(E0) + if (eindex < 0 || eindex == 0) L6470_SET_KVAL_HOLD(E0); + #endif + #if AXIS_IS_L64XX(E1) + if (eindex < 0 || eindex == 1) L6470_SET_KVAL_HOLD(E1); + #endif + #if AXIS_IS_L64XX(E2) + if (eindex < 0 || eindex == 2) L6470_SET_KVAL_HOLD(E2); + #endif + #if AXIS_IS_L64XX(E3) + if (eindex < 0 || eindex == 3) L6470_SET_KVAL_HOLD(E3); + #endif + #if AXIS_IS_L64XX(E4) + if (eindex < 0 || eindex == 4) L6470_SET_KVAL_HOLD(E4); + #endif + #if AXIS_IS_L64XX(E5) + if (eindex < 0 || eindex == 5) L6470_SET_KVAL_HOLD(E5); + #endif + #if AXIS_IS_L64XX(E6) + if (eindex < 0 || eindex == 6) L6470_SET_KVAL_HOLD(E6); + #endif + #if AXIS_IS_L64XX(E7) + if (eindex < 0 || eindex == 7) L6470_SET_KVAL_HOLD(E7); + #endif } break; #endif } diff --git a/Marlin/src/gcode/feature/L6470/M916-918.cpp b/Marlin/src/gcode/feature/L6470/M916-M918.cpp similarity index 98% rename from Marlin/src/gcode/feature/L6470/M916-918.cpp rename to Marlin/src/gcode/feature/L6470/M916-M918.cpp index ad0a91111dc21..9e1f1b98da347 100644 --- a/Marlin/src/gcode/feature/L6470/M916-918.cpp +++ b/Marlin/src/gcode/feature/L6470/M916-M918.cpp @@ -138,10 +138,10 @@ void GcodeSuite::M916() { do { // turn the motor(s) both directions sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); // get the status after the motors have stopped planner.synchronize(); @@ -266,10 +266,10 @@ void GcodeSuite::M917() { DEBUG_ECHOLNPGM(" OCD threshold : ", (OCD_TH_val + 1) * 375); sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(final_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(final_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); planner.synchronize(); @@ -308,8 +308,7 @@ void GcodeSuite::M917() { L64xxManager.set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold); } DEBUG_ECHOLNPGM("."); - gcode.reset_stepper_timeout(); // keep steppers powered - watchdog_refresh(); + reset_stepper_timeout(); // keep steppers powered safe_delay(5000); status_composite_temp = 0; for (j = 0; j < driver_count; j++) { @@ -615,10 +614,10 @@ void GcodeSuite::M918() { DEBUG_ECHOLNPGM("...feedrate = ", current_feedrate); sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_min), uint16_t(current_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); sprintf_P(gcode_string, PSTR("G0 %s%03d F%03d"), temp_axis_string, uint16_t(position_max), uint16_t(current_feedrate)); - gcode.process_subcommands_now_P(gcode_string); + process_subcommands_now(gcode_string); planner.synchronize(); diff --git a/Marlin/src/gcode/feature/adc/M3426.cpp b/Marlin/src/gcode/feature/adc/M3426.cpp new file mode 100644 index 0000000000000..2820c8b880937 --- /dev/null +++ b/Marlin/src/gcode/feature/adc/M3426.cpp @@ -0,0 +1,68 @@ +/** + * 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 . + * + */ + +#include "../../../inc/MarlinConfig.h" + +#if ENABLED(HAS_MCP3426_ADC) + +#include "../../gcode.h" + +#include "../../../feature/adc/adc_mcp3426.h" + +#define MCP3426_BASE_ADDR (0b1101 << 3) + +/** + * M3426: Read 16 bit (signed) value from I2C MCP3426 ADC device + * + * M3426 C channel 1 or 2 + * M3426 G gain 1, 2, 4 or 8 + * M3426 I 0 or 1, invert reply + */ +void GcodeSuite::M3426() { + uint8_t channel = parser.byteval('C', 1), // Channel 1 or 2 + gain = parser.byteval('G', 1), // Gain 1, 2, 4, or 8 + address = parser.byteval('A', 3); // Address 0-7 (or 104-111) + const bool inverted = parser.boolval('I'); + + if (address <= 7) address += MCP3426_BASE_ADDR; + + if (WITHIN(channel, 1, 2) && (gain == 1 || gain == 2 || gain == 4 || gain == 8) && WITHIN(address, MCP3426_BASE_ADDR, MCP3426_BASE_ADDR + 7)) { + int16_t result = mcp3426.ReadValue(channel, gain, address); + + if (mcp3426.Error == false) { + if (inverted) { + // Should we invert the reading (32767 - ADC value) ? + // Caters to end devices that expect values to increase when in reality they decrease. + // e.g., A pressure sensor in a vacuum when the end device expects a positive pressure. + result = INT16_MAX - result; + } + //SERIAL_ECHOPGM(STR_OK); + SERIAL_ECHOLNPGM("V:", result, " C:", channel, " G:", gain, " I:", inverted ? 1 : 0); + } + else + SERIAL_ERROR_MSG("MCP342X i2c error"); + } + else + SERIAL_ERROR_MSG("MCP342X Bad request"); +} + +#endif // HAS_MCP3426_ADC diff --git a/Marlin/src/gcode/feature/advance/M900.cpp b/Marlin/src/gcode/feature/advance/M900.cpp index 4ed601bbe873f..db09faa883109 100644 --- a/Marlin/src/gcode/feature/advance/M900.cpp +++ b/Marlin/src/gcode/feature/advance/M900.cpp @@ -26,7 +26,6 @@ #include "../../gcode.h" #include "../../../module/planner.h" -#include "../../../module/stepper.h" #if ENABLED(EXTRA_LIN_ADVANCE_K) float other_extruder_advance_K[EXTRUDERS]; @@ -117,10 +116,10 @@ void GcodeSuite::M900() { #if EXTRUDERS < 2 SERIAL_ECHOLNPGM("Advance S", new_slot, " K", kref, "(S", !new_slot, " K", lref, ")"); #else - LOOP_L_N(i, EXTRUDERS) { - const bool slot = TEST(lin_adv_slot, i); - SERIAL_ECHOLNPGM("Advance T", i, " S", slot, " K", planner.extruder_advance_K[i], - "(S", !slot, " K", other_extruder_advance_K[i], ")"); + EXTRUDER_LOOP() { + const bool slot = TEST(lin_adv_slot, e); + SERIAL_ECHOLNPGM("Advance T", e, " S", slot, " K", planner.extruder_advance_K[e], + "(S", !slot, " K", other_extruder_advance_K[e], ")"); SERIAL_EOL(); } #endif @@ -132,9 +131,9 @@ void GcodeSuite::M900() { SERIAL_ECHOLNPGM("Advance K=", planner.extruder_advance_K[0]); #else SERIAL_ECHOPGM("Advance K"); - LOOP_L_N(i, EXTRUDERS) { - SERIAL_CHAR(' ', '0' + i, ':'); - SERIAL_DECIMAL(planner.extruder_advance_K[i]); + EXTRUDER_LOOP() { + SERIAL_CHAR(' ', '0' + e, ':'); + SERIAL_DECIMAL(planner.extruder_advance_K[e]); } SERIAL_EOL(); #endif @@ -145,14 +144,14 @@ void GcodeSuite::M900() { } void GcodeSuite::M900_report(const bool forReplay/*=true*/) { - report_heading(forReplay, PSTR(STR_LINEAR_ADVANCE)); + report_heading(forReplay, F(STR_LINEAR_ADVANCE)); #if EXTRUDERS < 2 report_echo_start(forReplay); SERIAL_ECHOLNPGM(" M900 K", planner.extruder_advance_K[0]); #else - LOOP_L_N(i, EXTRUDERS) { + EXTRUDER_LOOP() { report_echo_start(forReplay); - SERIAL_ECHOLNPGM(" M900 T", i, " K", planner.extruder_advance_K[i]); + SERIAL_ECHOLNPGM(" M900 T", e, " K", planner.extruder_advance_K[e]); } #endif } diff --git a/Marlin/src/gcode/feature/cancel/M486.cpp b/Marlin/src/gcode/feature/cancel/M486.cpp index 1f14ae0fd26cc..c1e90d1b96108 100644 --- a/Marlin/src/gcode/feature/cancel/M486.cpp +++ b/Marlin/src/gcode/feature/cancel/M486.cpp @@ -44,14 +44,14 @@ void GcodeSuite::M486() { cancelable.object_count = parser.intval('T', 1); } - if (parser.seen('S')) + if (parser.seenval('S')) cancelable.set_active_object(parser.value_int()); if (parser.seen('C')) cancelable.cancel_active_object(); - if (parser.seen('P')) cancelable.cancel_object(parser.value_int()); + if (parser.seenval('P')) cancelable.cancel_object(parser.value_int()); - if (parser.seen('U')) cancelable.uncancel_object(parser.value_int()); + if (parser.seenval('U')) cancelable.uncancel_object(parser.value_int()); } #endif // CANCEL_OBJECTS diff --git a/Marlin/src/gcode/feature/clean/G12.cpp b/Marlin/src/gcode/feature/clean/G12.cpp index b19932eb983dc..0113170f1d9cb 100644 --- a/Marlin/src/gcode/feature/clean/G12.cpp +++ b/Marlin/src/gcode/feature/clean/G12.cpp @@ -45,12 +45,14 @@ * X, Y, Z : Specify axes to move during cleaning. Default: ALL. */ void GcodeSuite::G12() { + // Don't allow nozzle cleaning without homing first - if (homing_needed_error()) return; + constexpr main_axes_bits_t clean_axis_mask = main_axes_mask & ~TERN0(NOZZLE_CLEAN_NO_Z, Z_AXIS) & ~TERN0(NOZZLE_CLEAN_NO_Y, Y_AXIS); + if (homing_needed_error(clean_axis_mask)) return; #ifdef WIPE_SEQUENCE_COMMANDS if (!parser.seen_any()) { - gcode.process_subcommands_now_P(PSTR(WIPE_SEQUENCE_COMMANDS)); + process_subcommands_now(F(WIPE_SEQUENCE_COMMANDS)); return; } #endif diff --git a/Marlin/src/gcode/feature/controllerfan/M710.cpp b/Marlin/src/gcode/feature/controllerfan/M710.cpp index eede16b5bd424..b98d88845dece 100644 --- a/Marlin/src/gcode/feature/controllerfan/M710.cpp +++ b/Marlin/src/gcode/feature/controllerfan/M710.cpp @@ -67,7 +67,7 @@ void GcodeSuite::M710() { } void GcodeSuite::M710_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_CONTROLLER_FAN)); + report_heading_etc(forReplay, F(STR_CONTROLLER_FAN)); SERIAL_ECHOLNPGM(" M710" " S", int(controllerFan.settings.active_speed), " I", int(controllerFan.settings.idle_speed), diff --git a/Marlin/src/gcode/feature/digipot/M907-M910.cpp b/Marlin/src/gcode/feature/digipot/M907-M910.cpp index a0b5c48e82a55..372cb4b8e3914 100644 --- a/Marlin/src/gcode/feature/digipot/M907-M910.cpp +++ b/Marlin/src/gcode/feature/digipot/M907-M910.cpp @@ -39,51 +39,76 @@ #endif /** - * M907: Set digital trimpot motor current using axis codes X, Y, Z, E, B, S + * M907: Set digital trimpot motor current using axis codes X [Y] [Z] [I] [J] [K] [E] + * B - Special case for E1 (Requires DIGIPOTSS_PIN or DIGIPOT_MCP4018 or DIGIPOT_MCP4451) + * C - Special case for E2 (Requires DIGIPOTSS_PIN or DIGIPOT_MCP4018 or DIGIPOT_MCP4451) + * S - Set current in mA for all axes (Requires DIGIPOTSS_PIN or DIGIPOT_MCP4018 or DIGIPOT_MCP4451), or + * Set percentage of max current for all axes (Requires HAS_DIGIPOT_DAC) */ void GcodeSuite::M907() { #if HAS_MOTOR_CURRENT_SPI - if (!parser.seen("BS" LOGICAL_AXES_STRING)) + if (!parser.seen("BS" STR_AXES_LOGICAL)) return M907_report(); - LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) stepper.set_digipot_current(i, parser.value_int()); - if (parser.seenval('B')) stepper.set_digipot_current(4, parser.value_int()); - if (parser.seenval('S')) LOOP_LE_N(i, 4) stepper.set_digipot_current(i, parser.value_int()); + if (parser.seenval('S')) LOOP_L_N(i, MOTOR_CURRENT_COUNT) 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) 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? + #if E_STEPPERS >= 2 + if (parser.seenval('B')) stepper.set_digipot_current(E_AXIS + 1, parser.value_int()); + #if E_STEPPERS >= 3 + if (parser.seenval('C')) stepper.set_digipot_current(E_AXIS + 2, parser.value_int()); + #endif + #endif #elif HAS_MOTOR_CURRENT_PWM - if (!parser.seen( - #if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY) - "XY" + #if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY, MOTOR_CURRENT_PWM_I, MOTOR_CURRENT_PWM_J, MOTOR_CURRENT_PWM_K) + #define HAS_X_Y_XY_I_J_K 1 + #endif + + #if HAS_X_Y_XY_I_J_K || ANY_PIN(MOTOR_CURRENT_PWM_E, MOTOR_CURRENT_PWM_Z) + + if (!parser.seen("S" + #if HAS_X_Y_XY_I_J_K + "XY" SECONDARY_AXIS_GANG("I", "J", "K") + #endif + #if PIN_EXISTS(MOTOR_CURRENT_PWM_Z) + "Z" + #endif + #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) + "E" + #endif + )) return M907_report(); + + if (parser.seenval('S')) LOOP_L_N(a, MOTOR_CURRENT_COUNT) stepper.set_digipot_current(a, parser.value_int()); + + #if HAS_X_Y_XY_I_J_K + if (NUM_AXIS_GANG( + parser.seenval('X'), || parser.seenval('Y'), || false, + || parser.seenval('I'), || parser.seenval('J'), || parser.seenval('K') + )) stepper.set_digipot_current(0, parser.value_int()); #endif #if PIN_EXISTS(MOTOR_CURRENT_PWM_Z) - "Z" + if (parser.seenval('Z')) stepper.set_digipot_current(1, parser.value_int()); #endif #if PIN_EXISTS(MOTOR_CURRENT_PWM_E) - "E" + if (parser.seenval('E')) stepper.set_digipot_current(2, parser.value_int()); #endif - )) return M907_report(); - #if ANY_PIN(MOTOR_CURRENT_PWM_X, MOTOR_CURRENT_PWM_Y, MOTOR_CURRENT_PWM_XY) - if (parser.seenval('X') || parser.seenval('Y')) 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 - #endif + #endif // HAS_MOTOR_CURRENT_PWM #if HAS_MOTOR_CURRENT_I2C // this one uses actual amps in floating point - LOOP_LOGICAL_AXES(i) if (parser.seenval(axis_codes[i])) digipot_i2c.set_current(i, parser.value_float()); - // Additional extruders use B,C,D for channels 4,5,6. - // TODO: Change these parameters because 'E' is used. B? - #if HAS_EXTRUDERS - for (uint8_t i = E_AXIS + 1; i < DIGIPOT_I2C_NUM_CHANNELS; i++) + if (parser.seenval('S')) LOOP_L_N(q, DIGIPOT_I2C_NUM_CHANNELS) 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) E (map to drivers according to pots adresses. Default with NUM_AXES 3 X Y Z E: map to X Y Z E0) + // Additional extruders use B,C,D. + // TODO: Change these parameters because 'E' is used and because 'D' should be reserved for debugging. B? + #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()); #endif #endif @@ -91,30 +116,36 @@ void GcodeSuite::M907() { #if HAS_MOTOR_CURRENT_DAC if (parser.seenval('S')) { const float dac_percent = parser.value_float(); - LOOP_LE_N(i, 4) 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(axis_codes[i])) stepper_dac.set_current_percent(i, parser.value_float()); + 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) E (map to drivers according to DAC_STEPPER_ORDER. Default with NUM_AXES 3: X Y Z E map to X Y Z E0) #endif } #if HAS_MOTOR_CURRENT_SPI || HAS_MOTOR_CURRENT_PWM void GcodeSuite::M907_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_STEPPER_MOTOR_CURRENTS)); + 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 and Y + SERIAL_ECHOLNPGM_P( // PWM-based has 3 values: + PSTR(" M907 X"), stepper.motor_current_setting[0] // X, Y, (I, J, K) , SP_Z_STR, stepper.motor_current_setting[1] // Z , SP_E_STR, stepper.motor_current_setting[2] // E ); #elif HAS_MOTOR_CURRENT_SPI SERIAL_ECHOPGM(" M907"); // SPI-based has 5 values: LOOP_LOGICAL_AXES(q) { // X Y Z (I J K) E (map to X Y Z (I J K) E0 by default) - SERIAL_CHAR(' ', axis_codes[q]); + SERIAL_CHAR(' ', IAXIS_CHAR(q)); SERIAL_ECHO(stepper.motor_current_setting[q]); } - SERIAL_CHAR(' ', 'B'); // B (maps to E1 by default) - SERIAL_ECHOLN(stepper.motor_current_setting[4]); + #if E_STEPPERS >= 2 + SERIAL_ECHOPGM_P(PSTR(" B"), stepper.motor_current_setting[E_AXIS + 1] // B (maps to E1 with NUM_AXES 3 according to DIGIPOT_CHANNELS) + #if E_STEPPERS >= 3 + , PSTR(" C"), stepper.motor_current_setting[E_AXIS + 2] // C (mapping to E2 must be defined by DIGIPOT_CHANNELS) + #endif + ); + #endif + SERIAL_EOL(); #endif } diff --git a/Marlin/src/gcode/feature/fwretract/M207-M209.cpp b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp index 040a09a8e0900..173c2894dcd6a 100644 --- a/Marlin/src/gcode/feature/fwretract/M207-M209.cpp +++ b/Marlin/src/gcode/feature/fwretract/M207-M209.cpp @@ -38,7 +38,7 @@ void GcodeSuite::M207() { fwretract.M207(); } void GcodeSuite::M207_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_RETRACT_S_F_Z)); + report_heading_etc(forReplay, F(STR_RETRACT_S_F_Z)); fwretract.M207_report(); } @@ -53,7 +53,7 @@ void GcodeSuite::M207_report(const bool forReplay/*=true*/) { void GcodeSuite::M208() { fwretract.M208(); } void GcodeSuite::M208_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_RECOVER_S_F)); + report_heading_etc(forReplay, F(STR_RECOVER_S_F)); fwretract.M208_report(); } @@ -68,7 +68,7 @@ void GcodeSuite::M208_report(const bool forReplay/*=true*/) { void GcodeSuite::M209() { fwretract.M209(); } void GcodeSuite::M209_report(const bool forReplay/*=true*/) { - report_heading_etc(forReplay, PSTR(STR_AUTO_RETRACT_S)); + report_heading_etc(forReplay, F(STR_AUTO_RETRACT_S)); fwretract.M209_report(); } diff --git a/Marlin/src/gcode/feature/i2c/M260_M261.cpp b/Marlin/src/gcode/feature/i2c/M260_M261.cpp index 438d1527f5f78..cf9bb7e583f45 100644 --- a/Marlin/src/gcode/feature/i2c/M260_M261.cpp +++ b/Marlin/src/gcode/feature/i2c/M260_M261.cpp @@ -45,10 +45,10 @@ */ void GcodeSuite::M260() { // Set the target address - if (parser.seen('A')) i2c.address(parser.value_byte()); + if (parser.seenval('A')) i2c.address(parser.value_byte()); // Add a new byte to the buffer - if (parser.seen('B')) i2c.addbyte(parser.value_byte()); + if (parser.seenval('B')) i2c.addbyte(parser.value_byte()); // Flush the buffer to the bus if (parser.seen('S')) i2c.send(); @@ -60,15 +60,16 @@ void GcodeSuite::M260() { /** * M261: Request X bytes from I2C slave device * - * Usage: M261 A B + * Usage: M261 A B S