Merge branch 'MarlinFirmware:bugfix-2.1.x' into FTM_Fix_Menu_Axis_Sync

This commit is contained in:
narno2202 2026-01-23 08:23:43 +01:00 committed by GitHub
commit 2042d57eed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
42 changed files with 659 additions and 415 deletions

View file

@ -8,19 +8,19 @@ In the interest of fostering an open and welcoming environment, we as contributo
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities

View file

@ -11,18 +11,21 @@ The following is a set of guidelines for contributing to Marlin, hosted by the [
[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question)
[How Can I Contribute?](#how-can-i-contribute)
* [Reporting Bugs](#reporting-bugs)
* [Suggesting Features or Changes](#suggesting-features-or-changes)
* [Your First Code Contribution](#your-first-code-contribution)
* [Pull Requests](#pull-requests)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Features or Changes](#suggesting-features-or-changes)
- [Your First Code Contribution](#your-first-code-contribution)
- [Pull Requests](#pull-requests)
[Styleguides](#styleguides)
* [Git Commit Messages](#git-commit-messages)
* [C++ Coding Standards](#c++-coding-standards)
* [Documentation Styleguide](#documentation)
- [Git Commit Messages](#git-commit-messages)
- [C++ Coding Standards](#c++-coding-standards)
- [Documentation Styleguide](#documentation)
[Additional Notes](#additional-notes)
* [Issue and Pull Request Labels](#issue-and-pull-request-labels)
- [Issue and Pull Request Labels](#issue-and-pull-request-labels)
## Code of Conduct
@ -31,6 +34,7 @@ This project and everyone participating in it is governed by the [Marlin Code of
## I don't want to read this whole thing I just have a question!!!
> [!NOTE]
>
> Please don't file an issue to ask a question. You'll get faster results by using the resources below.
We have a Message Board and a Facebook group where our knowledgable user community can provide helpful advice if you have questions.
@ -43,10 +47,10 @@ We have a Message Board and a Facebook group where our knowledgable user communi
If chat is more your speed, you can join the MarlinFirmware Discord server:
* Use the link https://discord.com/servers/marlin-firmware-461605380783472640 to join up as a General User.
* Even though our Discord is pretty active, it may take a while for community members to respond — please be patient!
* Use the `#general` channel for general questions or discussion about Marlin.
* Other channels exist for certain topics or are limited to Patrons. Check the channel list.
- Use the link https://discord.com/servers/marlin-firmware-461605380783472640 to join up as a General User.
- Even though our Discord is pretty active, it may take a while for community members to respond — please be patient!
- Use the `#general` channel for general questions or discussion about Marlin.
- Other channels exist for certain topics or are limited to Patrons. Check the channel list.
## How Can I Contribute?
@ -57,6 +61,7 @@ This section guides you through submitting a Bug Report for Marlin. Following th
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?
@ -65,29 +70,29 @@ Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/).
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Marlin, e.g. which command exactly you used in the terminal, or how you started Marlin otherwise. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a keyboard shortcut or an Marlin command, and if so which one?
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets or log output in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Include detailed log output** especially for probing and leveling. See below for usage of `DEBUG_LEVELING_FEATURE`.
* **Include screenshots, links to videos, etc.** which clearly demonstrate the problem.
* **Include G-code** (if relevant) that reliably causes the problem to show itself.
* **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
- **Use a clear and descriptive title** for the issue to identify the problem.
- **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started Marlin, e.g. which command exactly you used in the terminal, or how you started Marlin otherwise. When listing steps, **don't just say what you did, but explain how you did it**. For example, if you moved the cursor to the end of a line, explain if you used the mouse, or a keyboard shortcut or an Marlin command, and if so which one?
- **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets or log output in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
- **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
- **Explain which behavior you expected to see instead and why.**
- **Include detailed log output** especially for probing and leveling. See below for usage of `DEBUG_LEVELING_FEATURE`.
- **Include screenshots, links to videos, etc.** which clearly demonstrate the problem.
- **Include G-code** (if relevant) that reliably causes the problem to show itself.
- **If the problem wasn't triggered by a specific action**, describe what you were doing before the problem happened and share more information using the guidelines below.
Provide more context:
* **Can you reproduce the problem with a minimum of options enabled?**
* **Did the problem start happening recently** (e.g. after updating to a new version of Marlin) or was this always a problem?
* If the problem started happening recently, **can you reproduce the problem in an older version of Marlin?** What's the most recent version in which the problem doesn't happen? You can download older versions of Marlin from [the releases page](https://github.com/MarlinFirmware/Marlin/releases).
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
- **Can you reproduce the problem with a minimum of options enabled?**
- **Did the problem start happening recently** (e.g. after updating to a new version of Marlin) or was this always a problem?
- If the problem started happening recently, **can you reproduce the problem in an older version of Marlin?** What's the most recent version in which the problem doesn't happen? You can download older versions of Marlin from [the releases page](https://github.com/MarlinFirmware/Marlin/releases).
- **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of Marlin are you using?** Marlin's exact version and build date can be seen in the startup message when a host connects to Marlin, or in the LCD Info menu (if enabled).
* **What kind of 3D Printer and electronics are you using**?
* **What kind of add-ons (probe, filament sensor) do you have**?
* **Include your Configuration files.** Make a ZIP file containing `Configuration.h` and `Configuration_adv.h` and drop it on your reply.
- **Which version of Marlin are you using?** Marlin's exact version and build date can be seen in the startup message when a host connects to Marlin, or in the LCD Info menu (if enabled).
- **What kind of 3D Printer and electronics are you using**?
- **What kind of add-ons (probe, filament sensor) do you have**?
- **Include your Configuration files.** Make a ZIP file containing `Configuration.h` and `Configuration_adv.h` and drop it on your reply.
### Suggesting Features or Changes
@ -97,52 +102,52 @@ Before creating a suggestion, please check [this list](https://github.com/Marlin
#### 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%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.
- **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%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?
Feature Requests are tracked as [GitHub issues](https://guides.github.com/features/issues/). Please follow these guidelines in your request:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the requested feature** in as much detail as possible.
* **Provide specific examples to demonstrate the steps**.
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
* **Include screenshots and links to videos** which demonstrate the feature or point out the part of Marlin to which the request is related.
* **Explain why this feature would be useful** to most Marlin users.
* **Name other firmwares that have this feature, if any.**
- **Use a clear and descriptive title** for the issue to identify the suggestion.
- **Provide a step-by-step description of the requested feature** in as much detail as possible.
- **Provide specific examples to demonstrate the steps**.
- **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
- **Include screenshots and links to videos** which demonstrate the feature or point out the part of Marlin to which the request is related.
- **Explain why this feature would be useful** to most Marlin users.
- **Name other firmwares that have this feature, if any.**
### Your First Code Contribution
Unsure where to begin contributing to Marlin? You can start by looking through these `good-first-issue` and `help-wanted` issues:
* [Beginner issues][good-first-issue] - issues which should only require a few lines of code, and a test or two.
* [Help Wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues.
- [Beginner issues][good-first-issue] - issues which should only require a few lines of code, and a test or two.
- [Help Wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues.
### Pull Requests
Pull Requests should always be targeted to working branches (e.g., `bugfix-2.1.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.
* Include pictures, diagrams, and links to videos in your Pull Request to demonstrate your changes, if needed.
* Follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website.
* Document new code with clear and concise comments.
* End all files with a newline.
- Fill in [the required template](pull_request_template.md).
- Don't include issue numbers in the PR title.
- Include pictures, diagrams, and links to videos in your Pull Request to demonstrate your changes, if needed.
- Follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website.
- Document new code with clear and concise comments.
- End all files with a newline.
## Styleguides
### Git Commit Messages
* Use the present tense ("Add feature" not "Added feature").
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...").
* Limit the first line to 72 characters or fewer.
* Reference issues and Pull Requests liberally after the first line.
- Use the present tense ("Add feature" not "Added feature").
- Use the imperative mood ("Move cursor to..." not "Moves cursor to...").
- Limit the first line to 72 characters or fewer.
- Reference issues and Pull Requests liberally after the first line.
### C++ Coding Standards
* Please read and follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. Failure to follow these guidelines will delay evaluation and acceptance of Pull Requests.
- Please read and follow the [Coding Standards](https://marlinfw.org/docs/development/coding_standards.html) posted on our website. Failure to follow these guidelines will delay evaluation and acceptance of Pull Requests.
### Documentation
* Guidelines for documentation are still under development. In-general, be clear, concise, and to-the-point.
- Guidelines for documentation are still under development. In-general, be clear, concise, and to-the-point.

View file

@ -1238,6 +1238,25 @@
#define FTM_FS 1000 // (Hz) Frequency for trajectory generation.
#define FTM_MIN_SHAPE_FREQ 20 // (Hz) Minimum shaping frequency, lower consumes more RAM
/**
* TMC2208 / TMC2208_STANDALONE drivers require a brief pause after a DIR change
* to prevent a standstill shutdown when using StealthChop (the standalone default).
* These options cause FT Motion to delay for > 750µs after a DIR change on a given axis.
* Disable only if you are certain that this can never happen with your TMC2208s.
*/
#if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_X
#endif
#if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_Y
#endif
#if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_Z
#endif
#if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_E
#endif
#endif // FT_MOTION
/**
@ -1782,6 +1801,12 @@
*/
//#define SD_SPI_SPEED SPI_HALF_SPEED
/**
* Reinit the LCD after SD Card insert/remove or when entering the menu.
* Required for some LCDs that use shared SPI with an external SD Card reader.
*/
#define REINIT_NOISY_LCD
// The standard SD detect circuit reads LOW when media is inserted and HIGH when empty.
// Enable this option and set to HIGH if your SD cards are incorrectly detected.
//#define SD_DETECT_STATE HIGH
@ -2801,8 +2826,8 @@
*
* Adds support for commands:
* S000 : Report State and Position while moving.
* P000 : Instant Pause / Hold while moving.
* R000 : Resume from Pause / Hold.
* P000 : Instant Pause / Hold while moving. Enable SOFT_FEED_HOLD for soft deceleration.
* R000 : Resume from Pause / Hold. Enable SOFT_FEED_HOLD for soft acceleration.
*
* - During Hold all Emergency Parser commands are available, as usual.
* - Enable NANODLP_Z_SYNC and NANODLP_ALL_AXIS for move command end-state reports.
@ -4432,15 +4457,38 @@
#endif
/**
* Instant freeze / unfreeze functionality
* Potentially useful for rapid stop that allows being resumed. Halts stepper movement.
* Note this does NOT pause spindles, lasers, fans, heaters or any other auxiliary device.
* @section interface
* Freeze / Unfreeze
*
* Pause / Hold that keeps power available and does not stop the spindle can be initiated by
* the FREEZE_PIN. Halts instantly (default) or performs a soft feed hold that decelerates and
* halts movement at FREEZE_JERK (requires SOFT_FEED_HOLD).
* Motion can be resumed by using the FREEZE_PIN.
*
* NOTE: Controls Laser PWM but does NOT pause Spindle, Fans, Heaters or other devices.
* @section freeze
*/
//#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
//#define FREEZE_PIN -1 // Override the default (KILL) pin here
#define FREEZE_STATE LOW // State of pin indicating freeze
#endif
#if ANY(FREEZE_FEATURE, REALTIME_REPORTING_COMMANDS)
/**
* Command P000 (REALTIME_REPORTING_COMMANDS and EMERGENCY_PARSER) or
* FREEZE_PIN (FREEZE_FEATURE) initiates a soft feed hold that keeps
* power available and does not stop the spindle.
*
* The soft feed hold decelerates and halts movement at FREEZE_JERK.
* Motion can be resumed with command R000 (requires REALTIME_REPORTING_COMMANDS) or
* by using the FREEZE_PIN (requires FREEZE_FEATURE).
*
* NOTE: Affects Laser PWM but DOES NOT pause Spindle, Fans, Heaters or other devices.
*/
//#define SOFT_FEED_HOLD
#if ENABLED(SOFT_FEED_HOLD)
#define FREEZE_JERK 2 // (mm/s) Completely halt when motion has decelerated below this value
#endif
#endif
/**

View file

@ -41,7 +41,7 @@
* here we define this default string as the date where the latest release
* version was tagged.
*/
//#define STRING_DISTRIBUTION_DATE "2026-01-11"
//#define STRING_DISTRIBUTION_DATE "2026-01-23"
/**
* The protocol for communication to the host. Protocol indicates communication

View file

@ -271,7 +271,8 @@ void MarlinHAL::adc_start(const pin_t pin) {
uint32_t mv;
esp_adc_cal_get_voltage((adc_channel_t)chan, &characteristics[attenuations[chan]], &mv);
adc_result = (mv * 1023) * (1000.f / (ADC_REFERENCE_VOLTAGE));
static constexpr uint32_t adc_divisor = uint32_t((ADC_REFERENCE_VOLTAGE) * 1000UL);
adc_result = (mv * 1023UL) / adc_divisor;
// Change the attenuation level based on the new reading
adc_atten_t atten;

View file

@ -49,9 +49,13 @@ SCB->VTOR = ((uint32_t) APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk);
Just searching for `SCB->VTOR` should yield some results. From there, you just need to look at the value that's assigned to it. The example uses `APP_START_ADDRESS`.
> [!NOTE] Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search.
> [!NOTE]
>
> Some vendors publish incomplete source code. But they sometimes leave version control related files in the repo, which can contain previous version of files that were removed. Find these by including folders like `.git` or `.svn` in your search.
> [!NOTE] The example is based on the [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo.
> [!NOTE]
>
> The example is based on the [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/main/firmware/Sources/.svn/pristine/ec/ec82bcb480b511906bc3e6658450e3a803ab9813.svn-base#L96) which actually includes deleted files in its repo.
2. Using a linker script
@ -69,7 +73,9 @@ MEMORY
}
```
> [!NOTE] This example is based on [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55)
> [!NOTE]
>
> This example is based on [Voxelab-64/Aquila_X2](//github.com/Voxelab-64/Aquila_X2/blob/d1f23adf96920996b979bc31023d1dce236d05db/firmware/Sources/main/hdsc32core/hc32f46x_flash.ld#L55)
## Documentation on the HC32F460

View file

@ -39,12 +39,16 @@ static void spi_init(spi_t *obj, uint32_t speed, spi_mode_e mode, uint8_t msb, u
void MarlinSPI::setClockDivider(uint8_t _div) {
_speed = spi_getClkFreq(&_spi);// / _div;
_clockDivider = _div;
if (_clockDivider != _div) {
_clockDivider = _div;
_mustInit = true;
}
}
void MarlinSPI::begin(void) {
//TODO: only call spi_init if any parameter changed!!
if (!_mustInit) return;
spi_init(&_spi, _speed, _dataMode, _bitOrder, _dataSize);
_mustInit = false;
}
void MarlinSPI::setupDma(SPI_HandleTypeDef &_spiHandle, DMA_HandleTypeDef &_dmaHandle, uint32_t direction, bool minc) {

View file

@ -76,15 +76,23 @@ public:
/* These methods are deprecated and kept for compatibility.
* Use SPISettings with SPI.beginTransaction() to configure SPI parameters.
*/
void setBitOrder(BitOrder _order) { _bitOrder = _order; }
void setBitOrder(BitOrder order) {
if (_bitOrder == order) return;
_bitOrder = order;
_mustInit = true;
}
void setDataMode(uint8_t _mode) {
switch (_mode) {
void setDataMode(uint8_t mode) {
auto previous_mode = _dataMode;
switch (mode) {
case SPI_MODE0: _dataMode = SPI_MODE_0; break;
case SPI_MODE1: _dataMode = SPI_MODE_1; break;
case SPI_MODE2: _dataMode = SPI_MODE_2; break;
case SPI_MODE3: _dataMode = SPI_MODE_3; break;
default: return;
}
if (previous_mode != _dataMode)
_mustInit = true;
}
void setClockDivider(uint8_t _div);
@ -104,4 +112,5 @@ private:
pin_t _misoPin;
pin_t _sckPin;
pin_t _ssPin;
bool _mustInit = true;
};

View file

@ -260,6 +260,10 @@
#include "feature/rs485.h"
#endif
#if ENABLED(SOFT_FEED_HOLD)
#include "feature/e_parser.h"
#endif
/**
* Spin in place here while keeping temperature processing alive
*/
@ -514,8 +518,14 @@ void Marlin::manage_inactivity(const bool no_stepper_sleep/*=false*/) {
}
#endif
#if ENABLED(FREEZE_FEATURE)
stepper.frozen = READ(FREEZE_PIN) == FREEZE_STATE;
// Handle the FREEZE button
#if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD)
stepper.set_frozen_triggered(
TERN0(FREEZE_FEATURE, READ(FREEZE_PIN) == FREEZE_STATE)
#if ALL(SOFT_FEED_HOLD, REALTIME_REPORTING_COMMANDS)
|| realtime_ramping_pause_flag
#endif
);
#endif
#if HAS_HOME
@ -1221,7 +1231,7 @@ void setup() {
#endif
#endif
#if ENABLED(FREEZE_FEATURE)
#if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN)
SETUP_LOG("FREEZE_PIN");
#if FREEZE_STATE
SET_INPUT_PULLDOWN(FREEZE_PIN);

View file

@ -52,6 +52,11 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
#define MAIN_AXIS_NAMES_LC NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w)
#define NUM_AXIS_CALL(G) do { NUM_AXIS_CODE(G(X_AXIS), G(Y_AXIS), G(Z_AXIS), G(I_AXIS), G(J_AXIS), G(K_AXIS), G(U_AXIS), G(V_AXIS), G(W_AXIS)); } while(0)
#define STR_AXES_MAIN NUM_AXIS_GANG("X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W)
#define NUM_AXIS_ANY(x, y, z, i, j, k, u, v, w) (false \
NUM_AXIS_GANG(|| (x), || (y), || (z), || (i), || (j), || (k), || (u), || (v), || (w)))
#define NUM_AXIS_ALL(x, y, z, i, j, k, u, v, w) ((NUM_AXES > 0) \
NUM_AXIS_GANG(&& (x), && (y), && (z), && (i), && (j), && (k), && (u), && (v), && (w)))
#define NUM_AXIS_NONE(V...) !NUM_AXIS_ANY(V)
#define LOGICAL_AXIS_GANG(N,V...) NUM_AXIS_GANG(V) GANG_ITEM_E(N)
#define LOGICAL_AXIS_CODE(N,V...) NUM_AXIS_CODE(V) CODE_ITEM_E(N)
@ -71,6 +76,11 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
#define LOGICAL_AXIS_MAP_LC(F) MAP(F, LOGICAL_AXIS_NAMES_LC)
#define LOGICAL_AXIS_CALL(G) do { LOGICAL_AXIS_CODE(G(E_AXIS), G(X_AXIS), G(Y_AXIS), G(Z_AXIS), G(I_AXIS), G(J_AXIS), G(K_AXIS), G(U_AXIS), G(V_AXIS), G(W_AXIS)); } while(0)
#define STR_AXES_LOGICAL LOGICAL_AXIS_GANG("E", "X", "Y", "Z", STR_I, STR_J, STR_K, STR_U, STR_V, STR_W)
#define LOGICAL_AXIS_ANY(e, x, y, z, i, j, k, u, v, w) (false \
LOGICAL_AXIS_GANG(|| (e), || (x), || (y), || (z), || (i), || (j), || (k), || (u), || (v), || (w)))
#define LOGICAL_AXIS_ALL(e, x, y, z, i, j, k, u, v, w) ((LOGICAL_AXES > 0) \
LOGICAL_AXIS_GANG(&& (e), && (x), && (y), && (z), && (i), && (j), && (k), && (u), && (v), && (w)))
#define LOGICAL_AXIS_NONE(V...) !LOGICAL_AXIS_ANY(V)
#define NUM_AXIS_PAIRED_LIST(V...) LIST_N(DOUBLE(NUM_AXES), V)
#define LOGICAL_AXIS_PAIRED_LIST(EA,EB,V...) NUM_AXIS_PAIRED_LIST(V) LIST_ITEM_E(EA) LIST_ITEM_E(EB)
@ -145,10 +155,14 @@ template <class L, class R> struct IF<true, L, R> { typedef L type; };
#define XY_ARRAY(V...) ARRAY_N(XY_COUNT, V)
#define XY_CODE(V...) CODE_N(XY_COUNT, V)
#define XY_GANG(V...) GANG_N(XY_COUNT, V)
#define XY_ANY(x,y) (false XY_GANG(|| (x), || (y)))
#define XY_ALL(x,y) ((NUM_AXES > 0) XY_GANG(&& (x), && (y)))
#define XYZ_LIST(V...) LIST_N(XYZ_COUNT, V)
#define XYZ_ARRAY(V...) ARRAY_N(XYZ_COUNT, V)
#define XYZ_CODE(V...) CODE_N(XYZ_COUNT, V)
#define XYZ_GANG(V...) GANG_N(XYZ_COUNT, V)
#define XYZ_ANY(x,y,z) (false XYZ_GANG(|| (x), || (y), || (z)))
#define XYZ_ALL(x,y,z) ((NUM_AXES > 0) XYZ_GANG(&& (x), && (y), && (z)))
#if HAS_ROTATIONAL_AXES
#define ROTATIONAL_AXIS_GANG(V...) GANG_N(ROTATIONAL_AXES, V)
@ -646,8 +660,8 @@ struct XYval {
// Exact comparisons. For floats a "NEAR" operation may be better.
FI bool operator==(const XYval<T> &rs) const { return x == rs.x && y == rs.y; }
FI bool operator==(const XYZval<T> &rs) const { return ENABLED(HAS_X_AXIS) XY_GANG(&& x == rs.x, && y == rs.y); }
FI bool operator==(const XYZEval<T> &rs) const { return ENABLED(HAS_X_AXIS) XY_GANG(&& x == rs.x, && y == rs.y); }
FI bool operator==(const XYZval<T> &rs) const { return XY_ALL(x == rs.x, y == rs.y); }
FI bool operator==(const XYZEval<T> &rs) const { return XY_ALL(x == rs.x, y == rs.y); }
FI bool operator!=(const XYval<T> &rs) const { return !operator==(rs); }
FI bool operator!=(const XYZval<T> &rs) const { return !operator==(rs); }
FI bool operator!=(const XYZEval<T> &rs) const { return !operator==(rs); }
@ -661,15 +675,15 @@ struct XYval {
FI bool operator> (const XYval<T> &rs) const { return x > rs.x && y > rs.y; }
FI bool operator>=(const XYval<T> &rs) const { return x >= rs.x && y >= rs.y; }
FI bool operator< (const XYZval<T> &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); }
FI bool operator<=(const XYZval<T> &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); }
FI bool operator> (const XYZval<T> &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); }
FI bool operator>=(const XYZval<T> &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); }
FI bool operator< (const XYZval<T> &rs) const { return XY_ALL(x < rs.x, y < rs.y); }
FI bool operator<=(const XYZval<T> &rs) const { return XY_ALL(x <= rs.x, y <= rs.y); }
FI bool operator> (const XYZval<T> &rs) const { return XY_ALL(x > rs.x, y > rs.y); }
FI bool operator>=(const XYZval<T> &rs) const { return XY_ALL(x >= rs.x, y >= rs.y); }
FI bool operator< (const XYZEval<T> &rs) const { return true XY_GANG(&& x < rs.x, && y < rs.y); }
FI bool operator<=(const XYZEval<T> &rs) const { return true XY_GANG(&& x <= rs.x, && y <= rs.y); }
FI bool operator> (const XYZEval<T> &rs) const { return true XY_GANG(&& x > rs.x, && y > rs.y); }
FI bool operator>=(const XYZEval<T> &rs) const { return true XY_GANG(&& x >= rs.x, && y >= rs.y); }
FI bool operator< (const XYZEval<T> &rs) const { return XY_ALL(x < rs.x, y < rs.y); }
FI bool operator<=(const XYZEval<T> &rs) const { return XY_ALL(x <= rs.x, y <= rs.y); }
FI bool operator> (const XYZEval<T> &rs) const { return XY_ALL(x > rs.x, y > rs.y); }
FI bool operator>=(const XYZEval<T> &rs) const { return XY_ALL(x >= rs.x, y >= rs.y); }
};
@ -738,7 +752,7 @@ struct XYZval {
// Pointer to the data as a simple array
explicit FI operator T* () { return pos; }
// If any element is true then it's true
FI constexpr operator bool() const { return 0 NUM_AXIS_GANG(|| x, || y, || z, || i, || j, || k, || u, || v, || w); }
FI constexpr operator bool() const { return NUM_AXIS_ANY(x, y, z, i, j, k, u, v, w); }
// Smallest element
FI constexpr T small() const { return TERN0(HAS_X_AXIS, _MIN(NUM_AXIS_LIST(x, y, z, i, j, k, u, v, w))); }
// Largest element
@ -824,22 +838,22 @@ struct XYZval {
FI XYZval<T>& operator<<=(const int &p) { NUM_AXIS_CODE(_LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; }
// Exact comparisons. For floats a "NEAR" operation may be better.
FI bool operator==(const XYZEval<T> &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); }
FI bool operator==(const XYZEval<T> &rs) const { return NUM_AXIS_ALL(x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); }
FI bool operator!=(const XYZEval<T> &rs) const { return !operator==(rs); }
// Exact comparison to a single value
FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); }
FI bool operator==(const T &p) const { return NUM_AXIS_ALL(x == p, y == p, z == p, i == p, j == p, k == p, u == p, v == p, w == p); }
FI bool operator!=(const T &p) const { return !operator==(p); }
FI bool operator< (const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); }
FI bool operator<=(const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); }
FI bool operator> (const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); }
FI bool operator>=(const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); }
FI bool operator< (const XYZval<T> &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); }
FI bool operator<=(const XYZval<T> &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); }
FI bool operator> (const XYZval<T> &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); }
FI bool operator>=(const XYZval<T> &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); }
FI bool operator< (const XYZEval<T> &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); }
FI bool operator<=(const XYZEval<T> &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); }
FI bool operator> (const XYZEval<T> &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); }
FI bool operator>=(const XYZEval<T> &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); }
FI bool operator< (const XYZEval<T> &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); }
FI bool operator<=(const XYZEval<T> &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); }
FI bool operator> (const XYZEval<T> &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); }
FI bool operator>=(const XYZEval<T> &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); }
};
@ -909,7 +923,7 @@ struct XYZEval {
// Pointer to the data as a simple array
explicit FI operator T* () { return pos; }
// If any element is true then it's true
FI constexpr operator bool() const { return 0 LOGICAL_AXIS_GANG(|| e, || x, || y, || z, || i, || j, || k, || u, || v, || w); }
FI constexpr operator bool() const { return LOGICAL_AXIS_ANY(e, x, y, z, i, j, k, u, v, w); }
// Smallest element
FI constexpr T small() const { return _MIN(LOGICAL_AXIS_LIST(e, x, y, z, i, j, k, u, v, w)); }
// Largest element
@ -998,24 +1012,24 @@ struct XYZEval {
FI XYZEval<T>& operator<<=(const int &p) { LOGICAL_AXIS_CODE(_LSE(e), _LSE(x), _LSE(y), _LSE(z), _LSE(i), _LSE(j), _LSE(k), _LSE(u), _LSE(v), _LSE(w)); return *this; }
// Exact comparisons. For floats a "NEAR" operation may be better.
FI bool operator==(const XYZval<T> &rs) const { return ENABLED(HAS_X_AXIS) NUM_AXIS_GANG(&& x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); }
FI bool operator==(const XYZEval<T> &rs) const { return ANY(HAS_X_AXIS, HAS_EXTRUDERS) LOGICAL_AXIS_GANG(&& e == rs.e, && x == rs.x, && y == rs.y, && z == rs.z, && i == rs.i, && j == rs.j, && k == rs.k, && u == rs.u, && v == rs.v, && w == rs.w); }
FI bool operator==(const XYZval<T> &rs) const { return NUM_AXIS_ALL(x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); }
FI bool operator==(const XYZEval<T> &rs) const { return LOGICAL_AXIS_ALL(e == rs.e, x == rs.x, y == rs.y, z == rs.z, i == rs.i, j == rs.j, k == rs.k, u == rs.u, v == rs.v, w == rs.w); }
FI bool operator!=(const XYZval<T> &rs) const { return !operator==(rs); }
FI bool operator!=(const XYZEval<T> &rs) const { return !operator==(rs); }
// Exact comparison to a single value
FI bool operator==(const T &p) const { return ENABLED(HAS_X_AXIS) LOGICAL_AXIS_GANG(&& e == p, && x == p, && y == p, && z == p, && i == p, && j == p, && k == p, && u == p, && v == p, && w == p); }
FI bool operator==(const T &p) const { return LOGICAL_AXIS_ALL(e == p, x == p, y == p, z == p, i == p, j == p, k == p, u == p, v == p, w == p); }
FI bool operator!=(const T &p) const { return !operator==(p); }
FI bool operator< (const XYZEval<T> &rs) const { return true LOGICAL_AXIS_GANG(&& e < rs.e, && x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); }
FI bool operator<=(const XYZEval<T> &rs) const { return true LOGICAL_AXIS_GANG(&& e <= rs.e, && x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); }
FI bool operator> (const XYZEval<T> &rs) const { return true LOGICAL_AXIS_GANG(&& e > rs.e, && x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); }
FI bool operator>=(const XYZEval<T> &rs) const { return true LOGICAL_AXIS_GANG(&& e >= rs.e, && x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); }
FI bool operator< (const XYZEval<T> &rs) const { return LOGICAL_AXIS_ALL(e < rs.e, x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); }
FI bool operator<=(const XYZEval<T> &rs) const { return LOGICAL_AXIS_ALL(e <= rs.e, x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); }
FI bool operator> (const XYZEval<T> &rs) const { return LOGICAL_AXIS_ALL(e > rs.e, x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); }
FI bool operator>=(const XYZEval<T> &rs) const { return LOGICAL_AXIS_ALL(e >= rs.e, x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); }
FI bool operator< (const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x < rs.x, && y < rs.y, && z < rs.z, && i < rs.i, && j < rs.j, && k < rs.k, && u < rs.u, && v < rs.v, && w < rs.w); }
FI bool operator<=(const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x <= rs.x, && y <= rs.y, && z <= rs.z, && i <= rs.i, && j <= rs.j, && k <= rs.k, && u <= rs.u, && v <= rs.v, && w <= rs.w); }
FI bool operator> (const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x > rs.x, && y > rs.y, && z > rs.z, && i > rs.i, && j > rs.j, && k > rs.k, && u > rs.u, && v > rs.v, && w > rs.w); }
FI bool operator>=(const XYZval<T> &rs) const { return true NUM_AXIS_GANG(&& x >= rs.x, && y >= rs.y, && z >= rs.z, && i >= rs.i, && j >= rs.j, && k >= rs.k, && u >= rs.u, && v >= rs.v, && w >= rs.w); }
FI bool operator< (const XYZval<T> &rs) const { return NUM_AXIS_ALL(x < rs.x, y < rs.y, z < rs.z, i < rs.i, j < rs.j, k < rs.k, u < rs.u, v < rs.v, w < rs.w); }
FI bool operator<=(const XYZval<T> &rs) const { return NUM_AXIS_ALL(x <= rs.x, y <= rs.y, z <= rs.z, i <= rs.i, j <= rs.j, k <= rs.k, u <= rs.u, v <= rs.v, w <= rs.w); }
FI bool operator> (const XYZval<T> &rs) const { return NUM_AXIS_ALL(x > rs.x, y > rs.y, z > rs.z, i > rs.i, j > rs.j, k > rs.k, u > rs.u, v > rs.v, w > rs.w); }
FI bool operator>=(const XYZval<T> &rs) const { return NUM_AXIS_ALL(x >= rs.x, y >= rs.y, z >= rs.z, i >= rs.i, j >= rs.j, k >= rs.k, u >= rs.u, v >= rs.v, w >= rs.w); }
};

View file

@ -60,6 +60,10 @@ EmergencyParser emergency_parser;
void quickresume_stepper();
#endif
#if ENABLED(SOFT_FEED_HOLD)
bool realtime_ramping_pause_flag = false;
#endif
void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
auto uppercase = [](char c) {
return TERN0(GCODE_CASE_INSENSITIVE, WITHIN(c, 'a', 'z')) ? c + 'A' - 'a' : c;
@ -223,8 +227,8 @@ void EmergencyParser::update(EmergencyParser::State &state, const uint8_t c) {
#endif
#if ENABLED(REALTIME_REPORTING_COMMANDS)
case EP_GRBL_STATUS: report_current_position_moving(); break;
case EP_GRBL_PAUSE: quickpause_stepper(); break;
case EP_GRBL_RESUME: quickresume_stepper(); break;
case EP_GRBL_PAUSE: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = true, quickpause_stepper()); break;
case EP_GRBL_RESUME: TERN(SOFT_FEED_HOLD, realtime_ramping_pause_flag = false, quickresume_stepper()); break;
#endif
#if ENABLED(SOFT_RESET_VIA_SERIAL)
case EP_KILL: hal.reboot(); break;

View file

@ -93,3 +93,7 @@ private:
};
extern EmergencyParser emergency_parser;
#if ENABLED(SOFT_FEED_HOLD)
extern bool realtime_ramping_pause_flag;
#endif

View file

@ -377,7 +377,7 @@ void GcodeSuite::G28() {
float z_homing_height = seenR ? parser.value_linear_units() : Z_CLEARANCE_FOR_HOMING;
// Check for any lateral motion that might require clearance
const bool may_skate = seenR NUM_AXIS_GANG(|| doX, || doY, || TERN0(Z_SAFE_HOMING, doZ), || doI, || doJ, || doK, || doU, || doV, || doW);
const bool may_skate = seenR && NUM_AXIS_ANY(doX, doY, TERN0(Z_SAFE_HOMING, doZ), doI, doJ, doK, doU, doV, doW);
if (seenR && z_homing_height == 0) {
if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("R0 = No Z raise");

View file

@ -420,8 +420,6 @@
#undef Z_PROBE_LOW_POINT
#undef MULTIPLE_PROBING
#undef EXTRA_PROBING
#undef PROBE_OFFSET_ZMIN
#undef PROBE_OFFSET_ZMAX
#undef PAUSE_BEFORE_DEPLOY_STOW
#undef PAUSE_PROBE_DEPLOY_WHEN_TRIGGERED
#undef PROBING_HEATERS_OFF
@ -437,6 +435,11 @@
#undef NOZZLE_TO_PROBE_OFFSET
#endif
#if NONE(BABYSTEPPING, HAS_BED_PROBE, HAS_WORKSPACE_OFFSET)
#undef PROBE_OFFSET_ZMIN
#undef PROBE_OFFSET_ZMAX
#endif
#if ENABLED(BELTPRINTER) && !defined(HOME_Y_BEFORE_X)
#define HOME_Y_BEFORE_X
#endif

View file

@ -360,30 +360,6 @@
#define HAS_FTM_EI_SHAPING 1
#endif
/**
* TMC2208 Direction-Flip Delay
*
* Some TMC2208 / TMC2208_STANDALONE drivers may require a short delay after a DIR change
* to prevent a standstill error, especially when using stealthChop (the standalone default).
*
* When enabled for an axis, FT Motion will hold that axis for > 750µs after a DIR change
* by holding its trajectory coordinate constant for a multiple of FTM_TS frames. For the
* default FTM_FS = 1000, it is a single 1ms frame.
*
* Other axes keep moving normally, and the wait is canceled if the axis flips again.
*/
#if AXIS_DRIVER_TYPE_X(TMC2208) || AXIS_DRIVER_TYPE_X(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_X 1
#endif
#if AXIS_DRIVER_TYPE_Y(TMC2208) || AXIS_DRIVER_TYPE_Y(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_Y 1
#endif
#if AXIS_DRIVER_TYPE_Z(TMC2208) || AXIS_DRIVER_TYPE_Z(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_Z 1
#endif
#if HAS_E_DRIVER(TMC2208) || HAS_E_DRIVER(TMC2208_STANDALONE)
#define FTM_DIR_CHANGE_HOLD_E 1
#endif
#if ANY(FTM_DIR_CHANGE_HOLD_X, FTM_DIR_CHANGE_HOLD_Y, FTM_DIR_CHANGE_HOLD_Z, FTM_DIR_CHANGE_HOLD_E)
#define HAS_FTM_DIR_CHANGE_HOLD 1
#endif

View file

@ -605,11 +605,9 @@
#endif
#endif
#if HAS_SD_DETECT && NONE(HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED)
#define REINIT_NOISY_LCD 1 // Have the LCD re-init on SD insertion
#endif
#endif // HAS_MEDIA
#else // !HAS_MEDIA
#undef REINIT_NOISY_LCD
#endif
/**
* Power Supply
@ -3049,9 +3047,10 @@
#endif
// User Interface
#if ENABLED(FREEZE_FEATURE) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL)
#if ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !PIN_EXISTS(FREEZE) && PIN_EXISTS(KILL)
#define FREEZE_PIN KILL_PIN
#elif PIN_EXISTS(KILL) && TERN1(FREEZE_FEATURE, KILL_PIN != FREEZE_PIN)
#define FREEZE_STOLE_KILL_PIN_WARNING 1
#elif PIN_EXISTS(KILL) && TERN1(HAS_FREEZE_PIN, KILL_PIN != FREEZE_PIN)
#define HAS_KILL 1
#endif
#if PIN_EXISTS(HOME)

View file

@ -579,8 +579,12 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
/**
* Instant Freeze
*/
#if ENABLED(FREEZE_FEATURE) && !(PIN_EXISTS(FREEZE) && defined(FREEZE_STATE))
#error "FREEZE_FEATURE requires both FREEZE_PIN and FREEZE_STATE."
#if ENABLED(SOFT_FEED_HOLD) && !defined(FREEZE_JERK)
#error "SOFT_FEED_HOLD requires FREEZE_JERK."
#elif ENABLED(FREEZE_FEATURE) && DISABLED(NO_FREEZE_PIN) && !(defined(FREEZE_PIN) && defined(FREEZE_STATE))
#error "FREEZE_FEATURE requires FREEZE_PIN and FREEZE_STATE."
#elif ENABLED(NO_FREEZE_PIN) && !(defined(REALTIME_REPORTING_COMMANDS))
#error "NO_FREEZE_PIN requires REALTIME_REPORTING_COMMANDS."
#endif
/**
@ -866,8 +870,8 @@ static_assert(COUNT(arm) == LOGICAL_AXES, "AXIS_RELATIVE_MODES must contain " _L
#if ENABLED(NONLINEAR_EXTRUSION)
#if HAS_MULTI_EXTRUDER
#error "NONLINEAR_EXTRUSION doesn't currently support multi-extruder setups."
#elif DISABLED(CPU_32_BIT)
#error "NONLINEAR_EXTRUSION requires a 32-bit CPU."
#elif NONE(CPU_32_BIT, NO_STANDARD_MOTION)
#error "NONLINEAR_EXTRUSION requires a 32-bit CPU or NO_STANDARD_MOTION."
#endif
#endif
@ -2711,6 +2715,22 @@ static_assert(NUM_SERVOS <= NUM_SERVO_PLUGS, "NUM_SERVOS (or some servo index) i
#elif ENABLED(NEO2_COLOR_PRESETS) && DISABLED(NEOPIXEL2_SEPARATE)
#error "NEO2_COLOR_PRESETS requires NEOPIXEL2_SEPARATE to be enabled."
#endif
#ifdef BOARD_NEOPIXEL_MAX
#if (NEOPIXEL_PIXELS > BOARD_NEOPIXEL_MAX && NEOPIXEL_PIN == BOARD_NEOPIXEL_PIN) || (NEOPIXEL_PIXELS > BOARD_NEOPIXEL_MAX && DISABLED(NEOPIXEL2_SEPARATE) && NEOPIXEL2_PIN == BOARD_NEOPIXEL_PIN)
#if ENABLED(BOARD_HAS_DCDC5V)
#error "NEOPIXEL_PIXELS exceeds the recommended maximum for your MOTHERBOARD."
#elif MB(BTT_SKR_MINI_E3_V2_0, BTT_SKR_MINI_E3_V3_0)
static_assert(false, "\nNEOPIXEL_PIXELS exceeds the recommended maximum for your MOTHERBOARD.\nConsider adding a 5V DC-DC converter and #define BOARD_HAS_DCDC5V to your Configuration.h.");
#endif
#endif
#if ENABLED(NEOPIXEL2_SEPARATE) && NEOPIXEL2_PIXELS > BOARD_NEOPIXEL_MAX && NEOPIXEL2_PIN == BOARD_NEOPIXEL_PIN
#if ENABLED(BOARD_HAS_DCDC5V)
#error "NEOPIXEL2_PIXELS exceeds the recommended maximum for your MOTHERBOARD."
#elif MB(BTT_SKR_MINI_E3_V2_0, BTT_SKR_MINI_E3_V3_0)
static_assert(false, "\nNEOPIXEL2_PIXELS exceeds the recommended maximum for your MOTHERBOARD.\nConsider adding a 5V DC-DC converter and #define BOARD_HAS_DCDC5V to your Configuration.h.");
#endif
#endif
#endif
#endif
#if DISABLED(NO_COMPILE_TIME_PWM)
@ -4495,14 +4515,12 @@ static_assert(_PLUS_TEST(3), "DEFAULT_MAX_ACCELERATION values must be positive."
#error "EMERGENCY_PARSER is required with FTM_RESONANCE_TEST (to cancel the test)."
#endif
#if !HAS_STANDARD_MOTION
#if ENABLED(NONLINEAR_EXTRUSION)
#error "NONLINEAR_EXTRUSION is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(SMOOTH_LIN_ADVANCE)
#if ENABLED(SMOOTH_LIN_ADVANCE)
#error "SMOOTH_LIN_ADVANCE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(MIXING_EXTRUDER)
#error "MIXING_EXTRUDER is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(FREEZE_FEATURE)
#error "FREEZE_FEATURE is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(SOFT_FEED_HOLD)
#error "SOFT_FEED_HOLD is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(DIRECT_STEPPING)
#error "DIRECT_STEPPING is not yet available in FT_MOTION. Disable NO_STANDARD_MOTION if you require it."
#elif ENABLED(DIFFERENTIAL_EXTRUDER)

View file

@ -42,7 +42,7 @@
* version was tagged.
*/
#ifndef STRING_DISTRIBUTION_DATE
#define STRING_DISTRIBUTION_DATE "2026-01-11"
#define STRING_DISTRIBUTION_DATE "2026-01-23"
#endif
/**

View file

@ -941,9 +941,6 @@
#if ENABLED(I2S_STEPPER_STREAM)
#warning "FT_MOTION has not been tested with I2S_STEPPER_STREAM."
#endif
#if ENABLED(NONLINEAR_EXTRUSION)
#warning "NONLINEAR_EXTRUSION does not (currently) operate when FT_MOTION is the active motion system."
#endif
#if ENABLED(LIN_ADVANCE)
#warning "Be aware that FT_MOTION K factor is now set with M900 K (same as LIN_ADVANCE)."
#if DISABLED(FTM_SMOOTHING)
@ -993,3 +990,22 @@
#if ALL(SMOOTH_LIN_ADVANCE, MIXING_EXTRUDER)
#warning "SMOOTH_LIN_ADVANCE with MIXING_EXTRUDER is untested. Use with caution."
#endif
/**
* Some LCDs need re-init to deal with flaky SPI bus sharing
*/
#if HAS_SD_DETECT && NONE(HAS_GRAPHICAL_TFT, LCD_USE_DMA_FSMC, HAS_FSMC_GRAPHICAL_TFT, HAS_SPI_GRAPHICAL_TFT, IS_DWIN_MARLINUI, EXTENSIBLE_UI, HAS_DWIN_E3V2, HAS_U8GLIB_I2C_OLED)
#define RECOMMEND_REINIT_NOISY_LCD 1
#endif
#if RECOMMEND_REINIT_NOISY_LCD && DISABLED(REINIT_NOISY_LCD)
#warning "It is recommended to enable REINIT_NOISY_LCD with your LCD controller model."
#elif !RECOMMEND_REINIT_NOISY_LCD && ENABLED(REINIT_NOISY_LCD)
#warning "REINIT_NOISY_LCD is probably not required with your LCD controller model."
#endif
/**
* FREEZE_FEATURE may override the KILL_PIN
*/
#if FREEZE_STOLE_KILL_PIN_WARNING
#warning "FREEZE_FEATURE uses KILL_PIN replacing the KILL button. Define a separate FREEZE_PIN if you don't want this behavior."
#endif

View file

@ -2168,6 +2168,13 @@ void autoHome() { queue.inject_P(G28_STR); }
#if HAS_ZOFFSET_ITEM
#ifndef PROBE_OFFSET_ZMIN
#define PROBE_OFFSET_ZMIN -20
#endif
#ifndef PROBE_OFFSET_ZMAX
#define PROBE_OFFSET_ZMAX 20
#endif
void applyZOffset() { TERN_(EEPROM_SETTINGS, settings.save()); }
void liveZOffset() {
#if ANY(BABYSTEP_ZPROBE_OFFSET, JUST_BABYSTEP)

View file

@ -203,4 +203,4 @@ namespace ExtUI {
void onAxisEnabled(const axis_t) {}
}
#endif // DGUS_LCD_UI_RELOADED
#endif // DGUS_LCD_UI_E3S1PRO

View file

@ -43,7 +43,7 @@ inline void action_mmu2_load_to_nozzle(const uint8_t tool) {
ui.reset_status();
ui.return_to_status();
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(tool + 1));
TERN(HAS_PRUSA_MMU3, mmu3.load_to_nozzle(tool), mmu2.load_to_nozzle(tool));
TERN(HAS_PRUSA_MMU3, mmu3, mmu2).load_to_nozzle(tool);
ui.reset_status();
}
@ -51,7 +51,7 @@ void _mmu2_load_to_feeder(const uint8_t tool) {
ui.reset_status();
ui.return_to_status();
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_LOADING_FILAMENT), int(tool + 1));
TERN(HAS_PRUSA_MMU3, mmu3.load_to_feeder(tool), mmu2.load_to_feeder(tool));
TERN(HAS_PRUSA_MMU3, mmu3, mmu2).load_to_feeder(tool);
ui.reset_status();
}
@ -82,7 +82,8 @@ void _mmu2_eject_filament(uint8_t index) {
ui.reset_status();
ui.return_to_status();
ui.status_printf(0, GET_TEXT_F(MSG_MMU2_EJECTING_FILAMENT), int(index + 1));
if (mmu3.eject_filament(index, true)) ui.reset_status();
if (TERN(HAS_PRUSA_MMU3, mmu3, mmu2).eject_filament(index, true))
ui.reset_status();
}
void _mmu2_cut_filament(uint8_t index) {
@ -97,7 +98,7 @@ void action_mmu2_unload_filament() {
ui.reset_status();
ui.return_to_status();
LCD_MESSAGE(MSG_MMU2_UNLOADING_FILAMENT);
while (!TERN(HAS_PRUSA_MMU3, mmu3.unload(), mmu2.unload())) {
while (!TERN(HAS_PRUSA_MMU3, mmu3, mmu2).unload()) {
safe_delay(50);
TERN(HAS_PRUSA_MMU3, MMU3::marlin_idle(true), marlin.idle());
}
@ -342,7 +343,7 @@ void menu_mmu2_choose_filament() {
//
void menu_mmu2_pause() {
feeder_index = mmu3.get_current_tool();
feeder_index = TERN(HAS_PRUSA_MMU3, mmu3, mmu2).get_current_tool();
START_MENU();
#if LCD_HEIGHT > 2
STATIC_ITEM(MSG_FILAMENT_CHANGE_HEADER, SS_DEFAULT|SS_INVERT);

View file

@ -380,7 +380,7 @@ bool FTMotion::plan_next_block() {
if (current_block->is_sync_pos()) stepper._set_position(current_block->position);
continue;
}
ensure_float_precision();
ensure_extruder_float_precision();
#if ENABLED(POWER_LOSS_RECOVERY)
recovery.info.sdpos = current_block->sdpos;
@ -441,7 +441,7 @@ bool FTMotion::plan_next_block() {
* resolution = 2^(floor(log2(|x|)) - 23)
* By resetting at ±1'000mm (1 meter), we get a minimum resolution of ~ 0.00006mm, enough for smoothing to work well.
*/
void FTMotion::ensure_float_precision() {
void FTMotion::ensure_extruder_float_precision() {
constexpr float FTM_POSITION_WRAP_THRESHOLD = 1000; // (mm) Reset when position exceeds this to prevent floating point precision loss
if (ABS(endPos_prevBlock.E) < FTM_POSITION_WRAP_THRESHOLD) return;
@ -462,6 +462,9 @@ bool FTMotion::plan_next_block() {
// Offset linear advance previous position
prev_traj_e += offset;
// Make sure the difference is accounted-for in the past
last_target_traj.e += offset;
// Offset stepper current position
const int64_t delta_steps_q48_16 = offset * planner.settings.axis_steps_per_mm[block_extruder_axis] * (1ULL << 16);
stepping.curr_steps_q48_16.E += delta_steps_q48_16;
@ -475,21 +478,42 @@ xyze_float_t FTMotion::calc_traj_point(const float dist) {
LOGICAL_AXIS_MAP_LC(_SET_TRAJ);
#if FTM_HAS_LIN_ADVANCE
const float advK = planner.get_advance_k();
if (advK) {
const float traj_e = traj_coords.e;
if (use_advance_lead) {
// Don't apply LA to retract/unretract blocks
const float e_rate = (traj_e - prev_traj_e) * (FTM_FS);
// Apply LA/NLE only to printing (not retract/unretract) blocks
if (use_advance_lead) {
const float advK = planner.get_advance_k();
if (advK || TERN0(NONLINEAR_EXTRUSION, stepper.ne.settings.enabled)) {
float traj_e = traj_coords.e;
const float traj_e_delta = traj_e - prev_traj_e; // extruder delta in mm, always positive for use_advance_lead (printing moves)
const float e_rate = traj_e_delta * FTM_FS; // extruder velocity in mm/s
traj_coords.e += e_rate * advK;
#if ENABLED(NONLINEAR_EXTRUSION)
if (stepper.ne.settings.enabled) {
const nonlinear_coeff_t &coeff = stepper.ne.settings.coeff;
const float multiplier = max(coeff.C, coeff.A * sq(e_rate) + coeff.B * e_rate + coeff.C),
nle_term = traj_e_delta * (multiplier - 1);
traj_coords.e += nle_term;
traj_e += nle_term;
startPos.e += nle_term;
endPos_prevBlock.e += nle_term;
}
#endif
prev_traj_e = traj_e;
}
prev_traj_e = traj_e;
}
#endif
#endif // FTM_HAS_LIN_ADVANCE
// Update shaping parameters if needed.
switch (cfg.dynFreqMode) {
#if HAS_DYNAMIC_FREQ_MM
case dynFreqMode_Z_BASED: {
static float oldz = 0.0f;
const float z = traj_coords.z;
@ -506,9 +530,11 @@ xyze_float_t FTMotion::calc_traj_point(const float dist) {
shaping.refresh_largest_delay_samples();
}
} break;
#endif
#if HAS_DYNAMIC_FREQ_G
case dynFreqMode_MASS_BASED:
// Update constantly. The optimization done for Z value makes
// less sense for E, as E is expected to constantly change.
@ -520,6 +546,7 @@ xyze_float_t FTMotion::calc_traj_point(const float dist) {
#endif
shaping.refresh_largest_delay_samples();
break;
#endif
default: break;

View file

@ -403,7 +403,7 @@ class FTMotion {
static void fill_stepper_plan_buffer();
static xyze_float_t calc_traj_point(const float dist);
static bool plan_next_block();
static void ensure_float_precision() IF_DISABLED(HAS_EXTRUDERS, {});
static void ensure_extruder_float_precision() IF_DISABLED(HAS_EXTRUDERS, {});
}; // class FTMotion

View file

@ -46,8 +46,6 @@ class ResonanceGenerator {
ResonanceGenerator();
void planRunout(const float duration);
void reset();
void start(const xyze_pos_t &spos, const float t) {

View file

@ -37,7 +37,7 @@ public:
* @param nominal_speed Peak feedrate [mm/s]
* @param distance Total distance to travel [mm]
*/
virtual void plan(const float initial_speed, const float final_speed, const float acceleration, float nominal_speed, const float distance) = 0;
virtual void plan(const float initial_speed, const float final_speed, const float acceleration, const float nominal_speed, const float distance) = 0;
/**
* Plan a zero-motion trajectory for a specific duration.

View file

@ -21,7 +21,7 @@
*/
#pragma once
#include "trajectory_generator.h"
#include "trajectory_trapezoidal.h"
#include <math.h>
/**
@ -30,27 +30,15 @@
* Acceleration starts and ends at zero. The jerk, snap, and crackle are such that
* the distance and phase durations match those of a trapezoidal profile.
*/
class Poly5TrajectoryGenerator : public TrajectoryGenerator {
class Poly5TrajectoryGenerator : public TrapezoidalTrajectoryGenerator {
public:
Poly5TrajectoryGenerator() = default;
void plan(const float initial_speed, const float final_speed, const float acceleration, float nominal_speed, const float distance) override {
this->initial_speed = initial_speed;
void plan(const float initial_speed_in, const float final_speed_in, const float acceleration_in, const float nominal_speed_in, const float distance_in) override {
// Use base class to calculate T1, T2, T3, actual nominal (top) speed and basic positions
TrapezoidalTrajectoryGenerator::plan(initial_speed_in, final_speed_in, acceleration_in, nominal_speed_in, distance_in);
// Calculate timing phases using the same logic as trapezoidal generator
const float one_over_acc = 1.0f / acceleration;
const float ldiff = distance + 0.5f * one_over_acc * (sq(initial_speed) + sq(final_speed));
T2 = ldiff / nominal_speed - one_over_acc * nominal_speed;
if (T2 < 0.0f) {
T2 = 0.0f;
nominal_speed = SQRT(ldiff * acceleration);
}
this->nominal_speed = nominal_speed;
T1 = (nominal_speed - initial_speed) * one_over_acc;
T3 = (nominal_speed - final_speed) * one_over_acc;
const float final_speed = final_speed_in; // just for consistency with the other parameters that otherwise shadow the member variables
const float d1 = (initial_speed + nominal_speed) * T1 * 0.5f;
const float T1_2 = sq(T1);
@ -65,8 +53,7 @@ public:
acc_c5 = (6.0f * d1 - 3.0f * (initial_speed + nominal_speed) * T1) / T1_5;
pos_before_coast = d1;
// Coast phase
pos_after_coast = pos_before_coast + nominal_speed * T2;
// Coast phase - already calculated by base class
// Deceration phase
const float d3 = (nominal_speed + final_speed) * T3 * 0.5f;
@ -82,33 +69,24 @@ public:
dec_c5 = (6.0f * d3 - 3.0f * (nominal_speed + final_speed) * T3) / T3_5;
}
void planRunout(const float duration) override {
reset();
T2 = duration;
}
float getDistanceAtTime(const float t) const override {
if (t < T1) {
// Acceration phase
return t * (acc_c1 + sq(t) * (acc_c3 + t * (acc_c4 + t * acc_c5)));
}
else if (t <= (T1 + T2)) {
else if (t <= T1_plus_T2) {
// Coasting phase
return pos_before_coast + this->nominal_speed * (t - T1);
return pos_before_coast + nominal_speed * (t - T1);
}
// Deceration phase
const float tau = t - (T1 + T2);
const float tau = t - T1_plus_T2;
return pos_after_coast + tau * (dec_c1 + sq(tau) * (dec_c3 + tau * (dec_c4 + tau * dec_c5)));
}
float getTotalDuration() const override { return T1 + T2 + T3; }
void reset() override {
acc_c1 = acc_c3 = acc_c4 = acc_c5 = 0.0f;
dec_c1 = dec_c3 = dec_c4 = dec_c5 = 0.0f;
T1 = T2 = T3 = 0.0f;
initial_speed = nominal_speed = 0.0f;
pos_before_coast = pos_after_coast = 0.0f;
TrapezoidalTrajectoryGenerator::reset();
}
private:
@ -117,8 +95,4 @@ private:
float acc_c1 = 0.0f, acc_c3 = 0.0f, acc_c4 = 0.0f, acc_c5 = 0.0f;
// deceleration coefficients
float dec_c1 = 0.0f, dec_c3 = 0.0f, dec_c4 = 0.0f, dec_c5 = 0.0f;
// timestamps of each phase
float T1 = 0.0f, T2 = 0.0f, T3 = 0.0f;
float initial_speed = 0.0f, nominal_speed = 0.0f;
float pos_before_coast = 0.0f, pos_after_coast = 0.0f;
};

View file

@ -30,27 +30,11 @@
Poly6TrajectoryGenerator::Poly6TrajectoryGenerator() {}
void Poly6TrajectoryGenerator::plan(const float initial_speed, const float final_speed, const float acceleration, float nominal_speed, const float distance) {
this->initial_speed = initial_speed;
void Poly6TrajectoryGenerator::plan(const float initial_speed_in, const float final_speed_in, const float acceleration_in, const float nominal_speed_in, const float distance_in) {
// Use base class to calculate T1, T2, T3 and basic positions
TrapezoidalTrajectoryGenerator::plan(initial_speed_in, final_speed_in, acceleration_in, nominal_speed_in, distance_in);
// --- Trapezoid timings (unchanged) ---
const float invA = 1.0f / acceleration;
const float ldiff = distance + 0.5f * invA * (sq(initial_speed) + sq(final_speed));
T2 = ldiff / nominal_speed - invA * nominal_speed;
if (T2 < 0.0f) {
T2 = 0.0f;
nominal_speed = SQRT(ldiff * acceleration);
}
this->nominal_speed = nominal_speed;
T1 = (nominal_speed - initial_speed) * invA;
T3 = (nominal_speed - final_speed) * invA;
// Distances at phase boundaries (trapezoid areas)
pos_before_coast = 0.5f * (initial_speed + nominal_speed) * T1;
pos_after_coast = pos_before_coast + nominal_speed * T2;
const float final_speed = final_speed_in; // just for consistency with the other parameters that otherwise shadow the member variables
// --- Build sextic (in position) for each phase ---
// We start from a quintic-in-position s5(u) that meets endpoints with a(0)=a(1)=0,
@ -105,11 +89,6 @@ void Poly6TrajectoryGenerator::plan(const float initial_speed, const float final
}
void Poly6TrajectoryGenerator::planRunout(const float duration) {
reset();
T2 = duration;
}
float Poly6TrajectoryGenerator::getDistanceAtTime(const float t) const {
if (t < T1) {
// Accel phase: u=t/T1
@ -117,27 +96,25 @@ float Poly6TrajectoryGenerator::getDistanceAtTime(const float t) const {
return s5_u(0.0f, initial_speed, T1, acc_c3, acc_c4, acc_c5, u)
+ acc_c6 * K_u(0.0f, initial_speed, T1, u); // K added as pure shape (position domain)
}
else if (t <= (T1 + T2)) {
else if (t <= T1_plus_T2) {
// Coast
return pos_before_coast + this->nominal_speed * (t - T1);
return pos_before_coast + nominal_speed * (t - T1);
}
// Decel phase
const float tau = t - (T1 + T2),
const float tau = t - T1_plus_T2,
u = tau / T3;
return s5_u(pos_after_coast, this->nominal_speed, T3, dec_c3, dec_c4, dec_c5, u)
+ dec_c6 * K_u(pos_after_coast, this->nominal_speed, T3, u);
return s5_u(pos_after_coast, nominal_speed, T3, dec_c3, dec_c4, dec_c5, u)
+ dec_c6 * K_u(pos_after_coast, nominal_speed, T3, u);
}
float Poly6TrajectoryGenerator::getTotalDuration() const { return T1 + T2 + T3; }
void Poly6TrajectoryGenerator::reset() {
T1 = T2 = T3 = 0.0f;
initial_speed = nominal_speed = 0.0f;
pos_before_coast = pos_after_coast = 0.0f;
// Reset polynomial coefficients
acc_c3 = acc_c4 = acc_c5 = 0.0f;
dec_c3 = dec_c4 = dec_c5 = 0.0f;
acc_c6 = dec_c6 = 0.0f;
// Call base class reset to handle inherited members
TrapezoidalTrajectoryGenerator::reset();
}
#endif // FTM_POLYS

View file

@ -21,7 +21,7 @@
*/
#pragma once
#include "trajectory_generator.h"
#include "trajectory_trapezoidal.h"
#include <math.h>
/**
@ -31,18 +31,14 @@
* - a(mid-phase) = overshoot * a_max (accel) and = -overshoot * a_max (decel)
* - v,a start/end at 0 within each phase; joins are continuous.
*/
class Poly6TrajectoryGenerator : public TrajectoryGenerator {
class Poly6TrajectoryGenerator : public TrapezoidalTrajectoryGenerator {
public:
Poly6TrajectoryGenerator();
void plan(const float initial_speed, const float final_speed, const float acceleration, float nominal_speed, const float distance) override;
void planRunout(const float duration) override;
void plan(const float initial_speed, const float final_speed, const float acceleration, const float nominal_speed, const float distance) override;
float getDistanceAtTime(const float t) const override;
float getTotalDuration() const override;
void reset() override;
private:
@ -64,12 +60,7 @@ private:
return cu(u) * cu(um1);
}
// Timings and kinematics
float T1 = 0.0f, T2 = 0.0f, T3 = 0.0f;
float initial_speed = 0.0f, nominal_speed = 0.0f;
float pos_before_coast = 0.0f, pos_after_coast = 0.0f;
// New phase polynomials
// New phase polynomials (only the polynomial coefficients are specific to Poly6)
float acc_c3 = 0.0f, acc_c4 = 0.0f, acc_c5 = 0.0f, acc_c6 = 0.0f;
float dec_c3 = 0.0f, dec_c4 = 0.0f, dec_c5 = 0.0f, dec_c6 = 0.0f;
};

View file

@ -33,9 +33,14 @@ class TrapezoidalTrajectoryGenerator : public TrajectoryGenerator {
public:
TrapezoidalTrajectoryGenerator() = default;
void plan(const float initial_speed, const float final_speed, const float acceleration, float nominal_speed, const float distance) override {
this->initial_speed = initial_speed;
this->acceleration = acceleration;
void plan(const float initial_speed_in, const float final_speed_in, const float acceleration_in, const float nominal_speed_in, const float distance_in) override {
initial_speed = initial_speed_in;
acceleration = acceleration_in;
nominal_speed = nominal_speed_in;
const float distance = distance_in;
const float final_speed = final_speed_in; // just for consistency
const float one_over_accel = 1.0f / acceleration;
const float ldiff = distance + 0.5f * one_over_accel * (sq(initial_speed) + sq(final_speed));
@ -46,7 +51,6 @@ public:
nominal_speed = SQRT(ldiff * acceleration);
}
this->nominal_speed = nominal_speed;
T1 = (nominal_speed - initial_speed) * one_over_accel;
T3 = (nominal_speed - final_speed) * one_over_accel;
@ -56,42 +60,48 @@ public:
// Calculate the distance traveled during the coast phase
pos_after_coast = pos_before_coast + nominal_speed * T2;
// Cache frequently used sums for performance
T1_plus_T2 = T1 + T2;
total_duration = T1_plus_T2 + T3;
}
float getDistanceAtTime(const float t) const override {
if (t < T1) {
// Acceleration phase
return (this->initial_speed * t) + (0.5f * this->acceleration * sq(t));
return (initial_speed * t) + (0.5f * acceleration * sq(t));
}
else if (t <= (T1 + T2)) {
else if (t <= T1_plus_T2) {
// Coasting phase
return pos_before_coast + nominal_speed * (t - T1);
}
// Deceleration phase
const float tau_decel = t - (T1 + T2);
return pos_after_coast + this->nominal_speed * tau_decel - 0.5f * this->acceleration * sq(tau_decel);
const float tau_decel = t - T1_plus_T2;
return pos_after_coast + nominal_speed * tau_decel - 0.5f * acceleration * sq(tau_decel);
}
float getTotalDuration() const override {
return T1 + T2 + T3;
return total_duration;
}
void planRunout(const float duration) override {
reset();
T2 = duration; // Coast at zero speed for the entire duration
T2 = T1_plus_T2 = total_duration = duration; // Coast at zero speed for the entire duration
}
void reset() override {
T1 = T2 = T3 = 0.0f;
this->initial_speed = this->nominal_speed = this->acceleration = 0.0f;
T1 = T2 = T3 = T1_plus_T2 = total_duration = 0.0f;
initial_speed = nominal_speed = acceleration = 0.0f;
pos_before_coast = pos_after_coast = 0.0f;
}
private:
// Internal trajectory parameters - kept private
protected:
// Internal trajectory parameters - made protected for inheritance
float T1 = 0.0f; // Duration of acceleration phase [s]
float T2 = 0.0f; // Duration of coasting phase [s]
float T3 = 0.0f; // Duration of deceleration phase [s]
float T1_plus_T2 = 0.0f; // Cached sum of T1 + T2 for performance
float total_duration = 0.0f; // Cached total duration T1 + T2 + T3
float initial_speed = 0.0f; // Starting feedrate [mm/s]
float nominal_speed = 0.0f; // Peak feedrate [mm/s]
float acceleration = 0.0f; // Acceleration [mm/s²]

View file

@ -1438,7 +1438,7 @@ FORCE_INLINE void segment_idle(millis_t &next_idle_ms) {
float get_move_distance(const xyze_pos_t &diff OPTARG(HAS_ROTATIONAL_AXES, bool &is_cartesian_move)) {
#if NUM_AXES
if (!(NUM_AXIS_GANG(diff.x, || diff.y, /* skip z */, || diff.i, || diff.j, || diff.k, || diff.u, || diff.v, || diff.w)))
if (NUM_AXIS_NONE(diff.x, diff.y, 0, diff.i, diff.j, diff.k, diff.u, diff.v, diff.w))
return TERN0(HAS_Z_AXIS, ABS(diff.z));
#if ENABLED(ARTICULATED_ROBOT_ARM)

View file

@ -1372,7 +1372,7 @@ void Planner::check_axes_activity() {
float high = 0.0f;
for (uint8_t b = block_buffer_tail; b != block_buffer_head; b = next_block_index(b)) {
const block_t * const block = &block_buffer[b];
if (NUM_AXIS_GANG(block->steps.x, || block->steps.y, || block->steps.z, || block->steps.i, || block->steps.j, || block->steps.k, || block->steps.u, || block->steps.v, || block->steps.w)) {
if (XYZ_HAS_STEPS(block)) {
const float se = float(block->steps.e) / block->step_event_count * block->nominal_speed; // mm/sec
NOLESS(high, se);
}
@ -2023,12 +2023,7 @@ bool Planner::_populate_block(
bool cartesian_move = hints.cartesian_move;
#endif
if (true NUM_AXIS_GANG(
&& block->steps.a < MIN_STEPS_PER_SEGMENT, && block->steps.b < MIN_STEPS_PER_SEGMENT, && block->steps.c < MIN_STEPS_PER_SEGMENT,
&& block->steps.i < MIN_STEPS_PER_SEGMENT, && block->steps.j < MIN_STEPS_PER_SEGMENT, && block->steps.k < MIN_STEPS_PER_SEGMENT,
&& block->steps.u < MIN_STEPS_PER_SEGMENT, && block->steps.v < MIN_STEPS_PER_SEGMENT, && block->steps.w < MIN_STEPS_PER_SEGMENT
)
) {
if (!XYZ_HAS_ENOUGH_STEPS(block)) {
block->millimeters = TERN0(HAS_EXTRUDERS, ABS(dist_mm.e));
}
else {
@ -2098,11 +2093,7 @@ bool Planner::_populate_block(
E_TERN_(block->extruder = extruder);
#if ENABLED(AUTO_POWER_CONTROL)
if (NUM_AXIS_GANG(
block->steps.x, || block->steps.y, || block->steps.z,
|| block->steps.i, || block->steps.j, || block->steps.k,
|| block->steps.u, || block->steps.v, || block->steps.w
)) powerManager.power_on();
if (XYZ_HAS_STEPS(block)) powerManager.power_on();
#endif
// Enable active axes
@ -2352,7 +2343,7 @@ bool Planner::_populate_block(
#if ANY(LIN_ADVANCE, FTM_HAS_LIN_ADVANCE)
bool use_adv_lead = false;
#endif
if (!ANY_AXIS_MOVES(block)) { // Is this a retract / recover move?
if (!XYZ_HAS_STEPS(block)) { // Is this a retract / recover move?
accel = CEIL(settings.retract_acceleration * steps_per_mm); // Convert to: acceleration steps/sec^2
}
else {
@ -2437,6 +2428,9 @@ bool Planner::_populate_block(
block->acceleration_steps_per_s2 = accel;
#if DISABLED(S_CURVE_ACCELERATION)
block->acceleration_rate = uint32_t(accel * (float(_BV32(24)) / (STEPPER_TIMER_RATE)));
#elif ENABLED(SOFT_FEED_HOLD)
// No need to waste time calculating the linear acceleration rate until the freeze_pin is triggered, leave this 0
block->acceleration_rate = 0;
#endif
#endif
block->acceleration = accel / steps_per_mm;

View file

@ -254,7 +254,8 @@ typedef struct PlannerBlock {
#if ENABLED(S_CURVE_ACCELERATION)
uint32_t acceleration_time_inverse, // Inverse of acceleration and deceleration periods, expressed as integer. Scale depends on CPU being used
deceleration_time_inverse;
#elif HAS_STANDARD_MOTION
#endif
#if ENABLED(HAS_STANDARD_MOTION) && (DISABLED(S_CURVE_ACCELERATION) || ENABLED(FREEZE_FEATURE))
uint32_t acceleration_rate; // Acceleration rate in (2^24 steps)/timer_ticks*s
#endif
@ -1188,10 +1189,18 @@ class Planner {
#define PLANNER_XY_FEEDRATE_MM_S 60.0f
#endif
#define ANY_AXIS_MOVES(BLOCK) \
(false NUM_AXIS_GANG( \
|| BLOCK->steps.a, || BLOCK->steps.b, || BLOCK->steps.c, \
|| BLOCK->steps.i, || BLOCK->steps.j, || BLOCK->steps.k, \
|| BLOCK->steps.u, || BLOCK->steps.v, || BLOCK->steps.w))
#define XYZ_HAS_STEPS(B) NUM_AXIS_ANY( \
B->steps.a, B->steps.b, B->steps.c, \
B->steps.i, B->steps.j, B->steps.k, \
B->steps.u, B->steps.v, B->steps.w)
#if MIN_STEPS_PER_SEGMENT <= 1
#define XYZ_HAS_ENOUGH_STEPS XYZ_HAS_STEPS
#else
#define XYZ_HAS_ENOUGH_STEPS(B) NUM_AXIS_ANY( \
B->steps.a >= MIN_STEPS_PER_SEGMENT, B->steps.b >= MIN_STEPS_PER_SEGMENT, B->steps.c >= MIN_STEPS_PER_SEGMENT, \
B->steps.i >= MIN_STEPS_PER_SEGMENT, B->steps.j >= MIN_STEPS_PER_SEGMENT, B->steps.k >= MIN_STEPS_PER_SEGMENT, \
B->steps.u >= MIN_STEPS_PER_SEGMENT, B->steps.v >= MIN_STEPS_PER_SEGMENT, B->steps.w >= MIN_STEPS_PER_SEGMENT)
#endif
extern Planner planner;

View file

@ -199,8 +199,14 @@ uint32_t Stepper::acceleration_time, Stepper::deceleration_time;
constexpr uint8_t Stepper::oversampling_factor; // = 0
#endif
#if ENABLED(FREEZE_FEATURE)
bool Stepper::frozen; // = false
#if ANY(SOFT_FEED_HOLD, FREEZE_FEATURE)
frozen_state_t Stepper::frozen_state; // Frozen flags
#endif
#if ENABLED(SOFT_FEED_HOLD)
uint32_t Stepper::frozen_time; // How much time has passed since frozen_state was triggered?
#if ENABLED(LASER_FEATURE)
uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion
#endif
#endif
// Delta error variables for the Bresenham line tracer
@ -1846,7 +1852,11 @@ void Stepper::isr() {
if (!current_block || step_events_completed >= step_event_count) return;
// Skipping step processing causes motion to freeze
if (TERN0(FREEZE_FEATURE, frozen)) return;
#if ENABLED(SOFT_FEED_HOLD)
if (frozen_state.triggered && frozen_state.solid) return;
#elif ENABLED(FREEZE_FEATURE)
if (frozen_state.state == 0) return;
#endif
// Count of pending loops and events for this iteration
const uint32_t pending_events = step_event_count - step_events_completed;
@ -2418,6 +2428,10 @@ void Stepper::isr() {
// If no queued movements, just wait 1ms for the next block
hal_timer_t interval = (STEPPER_TIMER_RATE) / 1000UL;
// Frozen solid?? Exit and do not fetch blocks.
if (TERN0(SOFT_FEED_HOLD, frozen_state.triggered && frozen_state.solid))
return interval;
// If there is a current block
if (current_block) {
// If current block is finished, reset pointer and finalize state
@ -2460,11 +2474,16 @@ void Stepper::isr() {
// acc_step_rate is in steps/second
// Modify acc_step_rate if the machine is freezing
TERN_(SOFT_FEED_HOLD, check_frozen_time(acc_step_rate));
// step_rate to timer interval and steps per stepper isr
interval = calc_multistep_timer_interval(acc_step_rate << oversampling_factor);
acceleration_time += interval;
deceleration_time = 0; // Reset since we're doing acceleration first.
TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval));
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(acc_step_rate << oversampling_factor);
@ -2526,10 +2545,14 @@ void Stepper::isr() {
#endif
TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate));
// step_rate to timer interval and steps per stepper isr
interval = calc_multistep_timer_interval(step_rate << oversampling_factor);
deceleration_time += interval;
TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_DECELERATION, interval));
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(step_rate << oversampling_factor);
@ -2576,21 +2599,25 @@ void Stepper::isr() {
else { // Must be in cruise phase otherwise
// Calculate the ticks_nominal for this nominal speed, if not done yet
if (ticks_nominal == 0) {
if (ticks_nominal == 0 || TERN0(SOFT_FEED_HOLD, frozen_time)) {
uint32_t step_rate = current_block->nominal_rate;
TERN_(SOFT_FEED_HOLD, check_frozen_time(step_rate));
// step_rate to timer interval and loops for the nominal speed
ticks_nominal = calc_multistep_timer_interval(current_block->nominal_rate << oversampling_factor);
ticks_nominal = calc_multistep_timer_interval(step_rate << oversampling_factor);
deceleration_time = ticks_nominal / 2;
// Prepare for deceleration
IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = current_block->nominal_rate);
IF_DISABLED(S_CURVE_ACCELERATION, acc_step_rate = step_rate);
TERN_(SMOOTH_LIN_ADVANCE, curr_step_rate = current_block->nominal_rate);
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(current_block->nominal_rate << oversampling_factor);
calc_nonlinear_e(step_rate << oversampling_factor);
#if HAS_ROUGH_LIN_ADVANCE
if (la_active)
la_interval = calc_timer_interval(current_block->nominal_rate >> current_block->la_scaling);
la_interval = calc_timer_interval(step_rate >> current_block->la_scaling);
#endif
// Adjust Laser Power - Cruise
@ -2610,6 +2637,8 @@ void Stepper::isr() {
// The timer interval is just the nominal value for the nominal speed
interval = ticks_nominal;
TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_CRUISE, interval));
}
}
@ -2631,9 +2660,12 @@ void Stepper::isr() {
#endif
}
else { // !current_block
TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_STATIONARY, interval));
#if ENABLED(LASER_FEATURE)
// If no movement in dynamic mode turn Laser off
if (cutter.cutter_mode == CUTTER_MODE_DYNAMIC)
cutter.apply_power(0); // No movement in dynamic mode so turn Laser off
cutter.apply_power(0);
#endif
}
@ -2905,7 +2937,7 @@ void Stepper::isr() {
ne.edividend = advance_dividend.e;
const float scale = (float(ne.edividend) / advance_divisor) * planner.mm_per_step[E_AXIS_N(current_block->extruder)];
ne.scale_q24 = _BV32(24) * scale;
if (ne.settings.enabled && current_block->direction_bits.e && ANY_AXIS_MOVES(current_block)) {
if (ne.settings.enabled && current_block->direction_bits.e && XYZ_HAS_STEPS(current_block)) {
ne.q24.A = _BV32(24) * ne.settings.coeff.A;
ne.q24.B = _BV32(24) * ne.settings.coeff.B;
ne.q24.C = _BV32(24) * ne.settings.coeff.C;
@ -2916,13 +2948,22 @@ void Stepper::isr() {
}
#endif
uint32_t initial_rate = current_block->initial_rate;
#if ENABLED(SOFT_FEED_HOLD)
if (frozen_time) check_frozen_time(initial_rate);
#endif
// Calculate the initial timer interval
interval = calc_multistep_timer_interval(current_block->initial_rate << oversampling_factor);
interval = calc_multistep_timer_interval(initial_rate << oversampling_factor);
TERN_(SOFT_FEED_HOLD, check_frozen_state(FREEZE_ACCELERATION, interval));
// Initialize ac/deceleration time as if half the time passed.
acceleration_time = deceleration_time = interval / 2;
// Apply Nonlinear Extrusion, if enabled
calc_nonlinear_e(current_block->initial_rate << oversampling_factor);
calc_nonlinear_e(initial_rate << oversampling_factor);
#if ENABLED(LIN_ADVANCE)
#if ENABLED(SMOOTH_LIN_ADVANCE)
@ -2930,7 +2971,7 @@ void Stepper::isr() {
#else
if (la_active) {
const uint32_t la_step_rate = la_advance_steps < current_block->max_adv_steps ? current_block->la_advance_rate : 0;
la_interval = calc_timer_interval((current_block->initial_rate + la_step_rate) >> current_block->la_scaling);
la_interval = calc_timer_interval((initial_rate + la_step_rate) >> current_block->la_scaling);
}
#endif
#endif
@ -2959,7 +3000,7 @@ void Stepper::isr() {
const bool forward_e = step_rate > 0;
#if ENABLED(NONLINEAR_EXTRUSION)
if (ne.settings.enabled && forward_e && ANY_AXIS_MOVES(current_block)) {
if (ne.settings.enabled && forward_e && XYZ_HAS_STEPS(current_block)) {
// Maximum polynomial value is just above 1, like 1.05..1.2, less than 2 anyway, so we can use 30 bits for fractional part
int32_t vd_q30 = ne.q30.A * sq(step_rate) + ne.q30.B * step_rate;
NOLESS(vd_q30, 0);
@ -3676,11 +3717,11 @@ void Stepper::report_positions() {
#endif
// Only wait for axes without edge stepping
const bool any_wait = false LOGICAL_AXIS_GANG(
|| (!e_axis_has_dedge && step_bits.E),
|| (!AXIS_HAS_DEDGE(X) && step_bits.X), || (!AXIS_HAS_DEDGE(Y) && step_bits.Y), || (!AXIS_HAS_DEDGE(Z) && step_bits.Z),
|| (!AXIS_HAS_DEDGE(I) && step_bits.I), || (!AXIS_HAS_DEDGE(J) && step_bits.J), || (!AXIS_HAS_DEDGE(K) && step_bits.K),
|| (!AXIS_HAS_DEDGE(U) && step_bits.U), || (!AXIS_HAS_DEDGE(V) && step_bits.V), || (!AXIS_HAS_DEDGE(W) && step_bits.W)
const bool any_wait = LOGICAL_AXIS_ANY(
!e_axis_has_dedge && step_bits.E,
!AXIS_HAS_DEDGE(X) && step_bits.X, !AXIS_HAS_DEDGE(Y) && step_bits.Y, !AXIS_HAS_DEDGE(Z) && step_bits.Z,
!AXIS_HAS_DEDGE(I) && step_bits.I, !AXIS_HAS_DEDGE(J) && step_bits.J, !AXIS_HAS_DEDGE(K) && step_bits.K,
!AXIS_HAS_DEDGE(U) && step_bits.U, !AXIS_HAS_DEDGE(V) && step_bits.V, !AXIS_HAS_DEDGE(W) && step_bits.W
);
// Allow pulses to be registered by stepper drivers
@ -3876,3 +3917,105 @@ void Stepper::report_positions() {
}
#endif // BABYSTEPPING
#if ENABLED(SOFT_FEED_HOLD)
void Stepper::set_frozen_solid(const bool state) {
if (state == frozen_state.solid) return;
frozen_state.solid = true;
#if ENABLED(LASER_FEATURE)
if (state) {
frozen_last_laser_power = cutter.last_power_applied;
cutter.apply_power(0); // No movement in dynamic mode so turn Laser off
}
else
cutter.apply_power(frozen_last_laser_power); // Restore frozen laser power
#endif
#if ENABLED(REALTIME_REPORTING_COMMANDS)
set_and_report_grblstate(state ? M_HOLD : M_RUNNING);
#endif
}
void Stepper::check_frozen_time(uint32_t &step_rate) {
// If frozen_time is 0 there is no need to modify the current step_rate
if (!frozen_time) return;
#if ENABLED(S_CURVE_ACCELERATION)
// If the machine is configured to use S_CURVE_ACCELERATION standard ramp acceleration
// rate will not have been calculated at this point
if (!current_block->acceleration_rate)
current_block->acceleration_rate = uint32_t(current_block->acceleration_steps_per_s2 * (float(1UL << 24) / (STEPPER_TIMER_RATE)));
#endif
const uint32_t freeze_rate = STEP_MULTIPLY(frozen_time, current_block->acceleration_rate);
const uint32_t min_step_rate = current_block->steps_per_mm * (FREEZE_JERK);
if (step_rate > freeze_rate)
step_rate -= freeze_rate;
else
step_rate = 0;
if (step_rate <= min_step_rate) {
set_frozen_solid(true);
step_rate = min_step_rate;
}
}
void Stepper::check_frozen_state(const FreezePhase phase, const uint32_t interval) {
switch (phase) {
case FREEZE_STATIONARY:
// If triggered while stationary immediately set solid flag
if (frozen_state.triggered) {
frozen_time = 0;
set_frozen_solid(true);
}
else
set_frozen_solid(false);
break;
case FREEZE_ACCELERATION:
// If frozen state is activated during the acceleration phase of a block we need to double our decceleration efforts
if (frozen_state.triggered) {
if (!frozen_state.solid) frozen_time += interval * 2;
}
else
set_frozen_solid(false);
break;
case FREEZE_DECELERATION:
// If frozen state is deactivated during the deceleration phase we need to double our acceleration efforts
if (!frozen_state.triggered) {
if (frozen_time) {
if (frozen_time > interval * 2)
frozen_time -= interval * 2;
else
frozen_time = 0;
}
set_frozen_solid(false);
}
break;
case FREEZE_CRUISE:
// During cruise stage acceleration/deceleration take place at regular rate
if (frozen_state.triggered) {
if (!frozen_state.solid) frozen_time += interval;
}
else {
if (frozen_time) {
if (frozen_time > interval)
frozen_time -= interval;
else {
frozen_time = 0;
ticks_nominal = 0; // Reset ticks_nominal to allow for recalculation of interval at nominal_rate
}
}
set_frozen_solid(false);
}
break;
}
}
#endif // SOFT_FEED_HOLD

View file

@ -292,12 +292,14 @@ constexpr ena_mask_t enable_overlap[] = {
#define NONLINEAR_EXTRUSION_Q24 1
#endif
typedef struct {
float A, B, C;
void reset() { A = B = 0.0f; C = 1.0f; }
} nonlinear_coeff_t;
typedef struct {
bool enabled;
struct {
float A, B, C;
void reset() { A = B = 0.0f; C = 1.0f; }
} coeff;
nonlinear_coeff_t coeff;
void reset() {
enabled = ENABLED(NONLINEAR_EXTRUSION_DEFAULT_ON);
coeff.reset();
@ -318,6 +320,26 @@ constexpr ena_mask_t enable_overlap[] = {
#endif // NONLINEAR_EXTRUSION
#if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD)
typedef union {
uint8_t state;
struct { bool triggered:1, solid:1; };
} frozen_state_t;
enum FrozenState { FROZEN_TRIGGERED, FROZEN_SOLID };
#if ENABLED(SOFT_FEED_HOLD)
enum FreezePhase : uint8_t {
FREEZE_STATIONARY,
FREEZE_ACCELERATION,
FREEZE_DECELERATION,
FREEZE_CRUISE
};
#endif
#endif
//
// Stepper class definition
//
@ -367,8 +389,12 @@ class Stepper {
static constexpr uint8_t last_moved_extruder = 0;
#endif
#if ENABLED(FREEZE_FEATURE)
static bool frozen; // Set this flag to instantly freeze motion
#if ANY(FREEZE_FEATURE, SOFT_FEED_HOLD)
static frozen_state_t frozen_state; // Frozen flags
static void set_frozen_triggered(const bool state) { frozen_state.triggered = state; }
#if ENABLED(SOFT_FEED_HOLD)
static bool is_frozen_triggered() { return frozen_state.triggered; }
#endif
#endif
#if ENABLED(NONLINEAR_EXTRUSION)
@ -800,6 +826,15 @@ class Stepper {
static void ftMotion_stepper();
#endif
#if ENABLED(SOFT_FEED_HOLD)
static uint32_t frozen_time; // How much time passed since frozen_state was triggered?
#if ENABLED(LASER_FEATURE)
static uint8_t frozen_last_laser_power; // Saved laser power prior to halting motion
#endif
static void check_frozen_state(const FreezePhase type, const uint32_t interval);
static void check_frozen_time(uint32_t &step_rate);
static void set_frozen_solid(const bool state);
#endif
};
extern Stepper stepper;

View file

@ -555,7 +555,7 @@
#elif MB(STM32F103RE)
#include "stm32f1/pins_STM32F1R.h" // STM32F1 env:STM32F103RE env:STM32F103RE_maple
#elif MB(MALYAN_M200)
#include "stm32f1/pins_MALYAN_M200.h" // STM32F1 env:STM32F103CB_malyan env:STM32F103CB_malyan_maple
#include "stm32f1/pins_MALYAN_M200.h" // STM32F1 env:STM32F103CB_malyan
#elif MB(STM3R_MINI)
#include "stm32f1/pins_STM3R_MINI.h" // STM32F1 env:STM32F103VE env:STM32F103RE_maple
#elif MB(GTM32_PRO_VB)

View file

@ -69,8 +69,11 @@
// Release PA13/PA14 (led, usb control) from SWD pins
#define DISABLE_DEBUG
#ifndef BOARD_NEOPIXEL_PIN
#define BOARD_NEOPIXEL_PIN PA8 // LED driving pin
#define BOARD_NEOPIXEL_PIN PA8 // LED driving pin
#ifndef BOARD_HAS_DCDC5V
#define BOARD_NEOPIXEL_MAX 7 // Max number of NEOPIXELS supported on this board
#else
#define BOARD_NEOPIXEL_MAX 29 // With 5V DC-DC converter
#endif
#ifndef PS_ON_PIN

View file

@ -453,8 +453,11 @@
//
// NeoPixel
//
#ifndef BOARD_NEOPIXEL_PIN
#define BOARD_NEOPIXEL_PIN PA8 // LED driving pin
#define BOARD_NEOPIXEL_PIN PA8 // LED driving pin
#ifndef BOARD_HAS_DCDC5V
#define BOARD_NEOPIXEL_MAX 7 // Max number of NEOPIXELS supported on this board
#else
#define BOARD_NEOPIXEL_MAX 29 // With 5V DC-DC converter
#endif
// Pins for documentation and sanity checks only.

View file

@ -61,9 +61,9 @@ def copy_config_files(branch, config_path, dest_dir):
else:
debug_print(f"{fname} not found in {src_dir}")
def fetch_config_files(branch, config_path, dest_dir):
def fetch_config_files(cref, config_path, dest_dir):
config_path_url = config_path.replace(' ', '%20')
base_url = f"https://raw.githubusercontent.com/MarlinFirmware/Configurations/{branch}/config/{config_path_url}"
base_url = f"https://raw.githubusercontent.com/MarlinFirmware/Configurations/{cref}/config/{config_path_url}"
for file in CONFIG_FILES:
url = f"{base_url}/{file}"
@ -80,8 +80,8 @@ def fetch_config_files(branch, config_path, dest_dir):
else:
raise
def fetch_configs(branch, config_path):
print(f"Fetching {config_path} configurations from {branch}...")
def fetch_configs(cref, config_path):
print(f"Fetching {config_path} configurations from {cref}...")
marlin_dir = Path("Marlin")
if not marlin_dir.exists():
@ -89,37 +89,37 @@ def fetch_configs(branch, config_path):
sys.exit(1)
if os.environ.get('GITHUB_ACTIONS'): # Running on GitHub ?
copy_config_files(branch, config_path, marlin_dir)
copy_config_files(cref, config_path, marlin_dir)
else:
fetch_config_files(branch, config_path, marlin_dir)
fetch_config_files(cref, config_path, marlin_dir)
def main():
branch = get_current_branch()
if not branch:
mbranch = get_current_branch()
if not mbranch:
print("Not a git repository or no branch found.")
sys.exit(1)
if branch.startswith("bugfix-2."):
branch = branch
elif branch.endswith("bugfix-2.1.x"):
branch = "bugfix-2.1.x"
elif branch.endswith("-2.1.x") or branch == "2.1.x":
branch = "latest-2.1.x"
elif branch.endswith("-2.0.x") or branch == "2.0.x":
branch = "latest-2.0.x"
elif branch.endswith("-1.1.x") or branch == "1.1.x":
branch = "latest-1.1.x"
elif branch.endswith("-1.0.x") or branch == "1.0.x":
branch = "latest-1.0.x"
if mbranch.startswith("bugfix-"):
cref = mbranch
elif mbranch.startswith("lts-"):
cref = mbranch.replace("lts-", "latest-")
elif mbranch.endswith("-2.1.x") or mbranch == "2.1.x":
cref = "latest-2.1.x"
elif mbranch.endswith("-2.0.x") or mbranch == "2.0.x":
cref = "latest-2.0.x"
elif mbranch.endswith("-1.1.x") or mbranch == "1.1.x":
cref = "latest-1.1.x"
elif mbranch == "1.0.x":
cref = "latest-1.0.x"
else:
branch = "bugfix-2.1.x"
cref = "bugfix-2.1.x"
if len(sys.argv) > 1:
arg = sys.argv[1]
if ':' in arg:
part1, part2 = arg.split(':', 1)
config_path = part2
branch = part1
cref = part1
else:
config_path = arg
config_path = 'examples/'+config_path
@ -134,7 +134,7 @@ def main():
except FileNotFoundError:
print("restore_configs not found, skipping.")
fetch_configs(branch, config_path)
fetch_configs(cref, config_path)
if __name__ == "__main__":
main()

View file

@ -1,35 +0,0 @@
{
"build": {
"core": "maple",
"cpu": "cortex-m3",
"extra_flags": "-DARDUINO_GENERIC_STM32F103C -DMCU_STM32F103CB",
"f_cpu": "72000000L",
"hwids": [
["0x1EAF", "0x0003"],
["0x1EAF", "0x0004"]
],
"ldscript": "jtagOffset.ld",
"mcu": "stm32f103cb",
"variant": "malyanM200",
"vec_tab_addr": "0x8002000"
},
"debug": {
"jlink_device": "STM32F103CB",
"openocd_target": "stm32f1x",
"svd_path": "STM32F103xx.svd"
},
"platform": "ststm32",
"frameworks": ["arduino"],
"name": "Malyan STM32F103CB (20k RAM. 128k Flash)",
"upload": {
"disable_flushing": false,
"maximum_ram_size": 20480,
"maximum_size": 131072,
"protocol": "serial",
"require_upload_port": true,
"use_1200bps_touch": false,
"wait_for_upload_port": false
},
"url": "https://www.st.com/content/st_com/en/products/microcontrollers/stm32-32-bit-arm-cortex-mcus/stm32f1-series/stm32f103/stm32f103cb.html",
"vendor": "Generic"
}

View file

@ -14,8 +14,9 @@ opt_set MOTHERBOARD BOARD_RAMPS4DUE_EFB \
E0_AUTO_FAN_PIN 8 FANMUX0_PIN 53 EXTRUDER_AUTO_FAN_SPEED 100 \
TEMP_SENSOR_CHAMBER 3 TEMP_CHAMBER_PIN 6 HEATER_CHAMBER_PIN 45 \
BACKLASH_MEASUREMENT_FEEDRATE 600 \
TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"'
opt_enable S_CURVE_ACCELERATION EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \
TRAMMING_POINT_XY '{{20,20},{20,20},{20,20},{20,20},{20,20}}' TRAMMING_POINT_NAME_5 '"Point 5"' \
FREEZE_PIN 17
opt_enable S_CURVE_ACCELERATION FREEZE_FEATURE SOFT_FEED_HOLD EEPROM_SETTINGS GCODE_MACROS GCODE_MACROS_IN_EEPROM \
FIX_MOUNTED_PROBE Z_SAFE_HOMING CODEPENDENT_XY_HOMING \
ASSISTED_TRAMMING REPORT_TRAMMING_MM ASSISTED_TRAMMING_WAIT_POSITION \
EEPROM_SETTINGS SDSUPPORT BINARY_FILE_TRANSFER \

View file

@ -326,30 +326,19 @@ extra_scripts = ${STM32F1_maple.extra_scripts}
buildroot/share/PlatformIO/scripts/jgaurora_a5s_a1_with_bootloader.py
build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY
#
# Malyan M200 (STM32F103CB)
#
[env:STM32F103CB_malyan_maple]
extends = STM32F1_maple
board = marlin_malyanM200
build_flags = ${STM32F1_maple.build_flags}
-DMCU_STM32F103CB -D__STM32F1__=1 -std=c++1y -DSERIAL_USB -ffunction-sections -fdata-sections
-Wl,--gc-sections -DDEBUG_LEVEL=0
lib_ignore = ${STM32F1_maple.lib_ignore}
SoftwareSerialM
#
# Chitu boards like Tronxy X5s (STM32F103ZET6)
#
[env:chitu_f103_maple]
extends = STM32F1_maple
board = marlin_maple_CHITU_F103
extra_scripts = ${STM32F1_maple.extra_scripts}
pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py
buildroot/share/PlatformIO/scripts/chitu_crypt.py
build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY -DSTM32_FLASH_SIZE=512
build_unflags = ${STM32F1_maple.build_unflags}
-DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG= -DERROR_LED_PORT=GPIOE -DERROR_LED_PIN=6
extends = STM32F1_maple
board = marlin_maple_CHITU_F103
extra_scripts = ${STM32F1_maple.extra_scripts}
pre:buildroot/share/PlatformIO/scripts/STM32F1_create_variant.py
buildroot/share/PlatformIO/scripts/chitu_crypt.py
build_flags = ${STM32F1_maple.build_flags} -DSTM32F1xx -DSTM32_XL_DENSITY -DSTM32_FLASH_SIZE=512
build_unflags = ${STM32F1_maple.build_unflags}
-DCONFIG_MAPLE_MINI_NO_DISABLE_DEBUG= -DERROR_LED_PORT=GPIOE -DERROR_LED_PIN=6
board_build.crypt_chitu = update.cbd
#
# Some Chitu V5 boards have a problem with GPIO init.