Eliminate skipped steps due to inaccurate pulse timing#16128
Conversation
- Use the pulse timer to time every pulse, both high and low, to ensure a minimum duration that cannot be shortened by interrupts - Extract PULSE_PREP from PULSE_START. This allows the rising edges of all steppers to be closer to gether, and moves PULSE_PREP logic inside the timed portion of the low pulse.
|
Here is a picture showing the behavior change with an actual print. These are the same gcode printed with the same printer configuration. The only difference is that the cube on the write was printed with firmware including this PR. These were printed on an MKS sBase, which has built-in 8825 drivers. The printer is not very good or well-tuned, so there are some other artifacts in the print, but the main point is the straightness of the edges and consistency of extrusion. |
|
its marked as draft but i took the artistic freedom to add the do not merge label, just to be safe |
|
How does this interact with |
This is a good question. I didn't explicitly test it (yet). Thanks for the reminder. At one point I had it working so that specifying MINIMUM_STEPPER_PULSE=0 along with a MAXIMUM_STEPPER_RATE would essentially skip the first delay, but I think that behavior was lost when I reworked things to be more AVR-friendly. I will have to revisit that scenario. There is a lot of room for TMC improvement, in general. The timer interactions are quite slow, and I can get higher performance by simply delaying 100ns, rather than reading a timer, which takes about 230ns on LPC1768. I went back to the timer approach to avoid exploding AVR, where it is extremely important to perform as much logic as possible while waiting for timers. If this goes forward, I expect a follow-up to improve speed on 32-bit. It just would've added a lot of noise to this change trying to do everything at once. |
|
This is a reminder to myself to check the babystepping implementation as well, to see if it will need updating. |
|
I was surprised by the error on STM32 about F_CPU not being constant, but I see similar issues have been encountered before. That makes sense when considering the chip can probably be slowed down to save power. |
|
Thank you for taking a close look at this. Marlin's current approach is definitely imperfect, and I'm not surprised to see missed (micro) steps are happening. In an ideal world MCUs would give you a pin mode where the output turns itself LOW after some duration whenever it gets turned HIGH (or vice-versa)…. Maybe in the future. I'll review your approach more closely and look at the trade-offs. Pulse duration is a fundamental issue, so it will be good to get our algorithms working solidly. |
Change const variable to macros, because I am not sure whether the STM32 clock will report the correct rate at static initialization time, since F_CPU is not constant.
|
I'll be down in Austin all of next week so won't be able to try out any new ideas right away. I should be able to get on here semi-regularly to discuss ideas and concerns. I fully expect some controversy, as this is a significant paradigm shift to the timing mechanisms in this ISR, and I would be suspicious if there were not concerns. Right now my largest concern is the maximum stepper rate calculations. I would love to eliminate all those hard-coded cycle counts, and the maintenance that goes with them. Maintaining those cycle counts create a huge barrier of entry to anyone wanting to improve this code, and are likely not consistently perfect as compilers and processor architectures change. |
Most MCUs have that exact ability , but just for selected pins.... |
I'm in Austin all week too. All the time, really…. |
I wish we only had to support a single MCU and board, already organized with all these factors in mind. But of course we have to maintain some time-checking method for AVR (where it's adequate) and then we need to provide some time-based method for any STEP pins that don't have this special mode. So it's another thing we could check for as we now do with PWM, then use whenever possible…. Looking to the future, modern boards will benefit from a 32-bit-only firmware built from the ground up to:
…where we have our own methods to allocate SW SPI and SW Serial as needed. Two approaches rise to the top:
So far my preference is to build on top of RepRapFirmware because it has already solved some low level problems and provides a framework for adding new features. However, it doesn't have a full Hardware Abstraction Layer at the level of Marlin, but it supports a couple of ATSAM boards. |
I do not know if it call my experience fortunate or infortunate, but right now, there is no vendor supplied HAL that targets all the MPUs that are being brought into the market. RTOS support for new MCUs is lagging quite a bit, so moving to an RTOS would not solve anything for newer MCUs, as we would have to add support for them ourselves. So, i think your idea of building over Reprap firmware, it probably the most sane approach here. Marlin has to support a ton of different hardware platforms, thus, most of the time, there is simply no way to ensure specific hw features (such as PWM on STEP pins) is available. So, most of the time a software fallback must exist (as Marlin already does with mostly everything) The only thing you usually can count on are at least having 1 o 2 timers to create periodic interrupts... This pull request tries to improve the pulse width without using extra hardware resources, so, it is a good step in the right direction |
|
How high is our confidence in this change? If we merge this for the wider test audience, should we expect a small flood of support followup? |
I think a casual user won't notice anything changed. An MKS sBase user will sing it praises. A power user saturating their 32-bit CPU with LV8729 pulse interrupts might report a minor drop in max speed. I don't know whether anyone actually falls into that last category. I'm getting ready to run a couple sanity checks on AVR followed by STM32. I'll post another update later tonight. |
|
I have played with several different optimizations, in particular for drivers with very fast timing (TMC and LV8729). Some minimal improvement is possible, but at the cost of added churn and complexity. I decided not to push forward with those changes for now. |
|
I've tested this change with SQUARE_STEPPING on MKS GEN-L (AVR) in the context of the issue I am having ( #15473 ), and found no changes to better of worse. |
|
@sjasonsmith Yesterday afternoon and this morning I worked on the issue. After numerous tests with different bug fix versions. I have come to the conclusion that the your modifications were not related to my waves seen in the test objects. I often like to use threaded objects to test precision. It seems this particular filament is sensitiv to shrinkage when the walls have a certain minimum thickness, enough to be felt and seen. I did a slight change to the object and the issue was resolved. Subsequent runs on two different machines were all successful. |
|
@Lord-Quake, thanks for the update! |
|
@sjasonsmith OMG - I thought my printer was printing pretty good b4 this bug fix. This is the 1st fix that I have actually SEEN results from. I have not printed a lot since, but my printer is definitely much happier. GREAT JOB Jason. Mine is a CoreXY, home built from Carl Feneck's plans and upgraded with a piezo nozzle probe/endstop. My steppers do 1/32 microstepping and the roughness I saw previously was not signifficant, but was there and it is now gone. obviously, it was missing a few steps but at 1/32 it wasn't terribly obvious. THANK YOU. |
|
@ruggb, what controller and drivers do you use? I expect that most improvement will be seen by 32-bit DRV8825 users, but if it has helped other configurations I would like to know as well! |
|
I have an Arduino Mega 2560 with Ramps and 8825 drivers. |
* [cron] Bump distribution date * STM32F1 Flash-based EEPROM fixes (MarlinFirmware#16118) * Disable PRINTCOUNTER in SKR Mini E3 examples (MarlinFirmware#16110) * Fix compile error with disabled PIDTEMP (MarlinFirmware#16108) * Wanhao D6 uses TINYBOY2 (MarlinFirmware#16117) * Improve touch buttons behavior (MarlinFirmware#16109) * Update AZSMZ LCD link (MarlinFirmware#16106) * [cron] Bump distribution date * Update 3DFabXYZ settings (MarlinFirmware#16139) * [cron] Bump distribution date * Fix controller and SD on Robin Nano (MarlinFirmware#16187) * Correct MKS Robin Mini pins (MarlinFirmware#16178) * Formalize DAC percent strings (MarlinFirmware#16176) * Update Italian language (MarlinFirmware#16147) * Update french (objects, retract...) * Superscript 3 for mm3 * [cron] Bump distribution date * Add MKS Robin Pro, MKS Robin Lite3 (MarlinFirmware#16163) * Fix multiple servos with STM32 (MarlinFirmware#16151) * Use error message !! hints (MarlinFirmware#16145) * Update BTT comments for USB/SD Composite (MarlinFirmware#16130) * MKS 12864 OLED pins for SGEN-L (MarlinFirmware#16188) * Invert E dir of Geeetech A10 (MarlinFirmware#16149) * Disable SD_CHECK_AND_RETRY in BTT E3 configs (MarlinFirmware#16143) * Add a CI test for RAMBo + CNC (MarlinFirmware#16126) * Onboard (always-on) pullups support (MarlinFirmware#16144) * Tweak ExtUI Probeless Babystepping (MarlinFirmware#16177) * Fix RAMBo CNC test * Flsun QQ-S example config (MarlinFirmware#16204) * Add MKS Robin Mini EEPROM defines (MarlinFirmware#16203) * Fix compile error (macro substitution typo) (MarlinFirmware#16194) * Update M503 MBL G29 report (MarlinFirmware#16199) * Include Z in SCARA steps feedrate (MarlinFirmware#16193) * Cardreader read/write open methods * Tweak some config names * Improve A20M config * Move status screen defines * Fix bad #ifdef (MarlinFirmware#16227) * TOUCH_MI_DEPLOY_XPOS fallback to X_MIN_POS (MarlinFirmware#16226) * Fix MKS SGen-L SD detect pin (MarlinFirmware#16224) * Improve ESP32 HAL (EEPROM, watchdog) (MarlinFirmware#16228) * Fix G28 debug line, M569 calls (MarlinFirmware#16213) * Add SKR Mini E3 + Zonestar LCD warning (MarlinFirmware#16214) * STM32 Touch UI timings, Longer onboard pullups (MarlinFirmware#16219) * Update BTT002 platform (fixing SD init) (MarlinFirmware#16217) * Define more FAN pins for GT2560 (MarlinFirmware#16235) * Tweak E180 config * Update product links * [cron] Bump distribution date * Add dev-2.1.x to 'mfinfo' script * Move AutoBuildMarlin to its own repo * [cron] Bump distribution date * Tweak G34, fix a declaration * Sanity-check Z_STEPPER_AUTO_ALIGN coordinates (MarlinFirmware#16231) * Improve SKR mini E3 + Ender 3 settings (MarlinFirmware#16247) * Tweak code formatting * Allow TMC2209 to save/restore spreadCycle (MarlinFirmware#16153) Co-Authored-By: teemuatlut <teemu.mantykallio@live.fi> * Spindle/Laser pins for RADDS (MarlinFirmware#16119) * Improve pulse timing and step reliability (MarlinFirmware#16128) * BigTreeTech SKR v1.4 support (MarlinFirmware#16236) * Followup to TMC2209 spreadcycle patch * Remove obsolete TMC2209 comment * HOME_USING_SPREADCYCLE is obsolete Co-Authored-By: teemuatlut <teemu.mantykallio@live.fi> * Use MYSERIAL0 (not SerialUSB) for Malyan LCD * Followup for step timing (MarlinFirmware#16128) * Fix broken M100_dump_routine * Tweak sanity checks * Update test scripts to error on unknown (MarlinFirmware#16229) * Kossel Clear configuration (MarlinFirmware#16198) * Move pins debug condition * Use Github Actions for CI, date bump (MarlinFirmware#16269) * Fix HAL_STM32 + Arduino IDE SoftwareSerial conflict (MarlinFirmware#16266) * Improve Anet A6 config (MarlinFirmware#16280) * Fix G34 with Z_DUAL_STEPPER_DRIVERS compile (MarlinFirmware#16274) * Fix planner compile error (MarlinFirmware#16272) * Fix axis CS sanity check (MarlinFirmware#16271) * Add Tevo Nereus (w/ Robin Nano) config (MarlinFirmware#16207) * Don't test certain changes * Reset runout.ran_out on resume (MarlinFirmware#16230) * Step timing cleanup and rounding fix (MarlinFirmware#16258) * Add MRR_ESPA/_ESPE (ESP32) boards (MarlinFirmware#16238) * Add Ender-5 Pro config (MarlinFirmware#16221) * Add FLYBOARD (STM32F407ZG) (MarlinFirmware#16257) * Fix STM32 flush of TX (used by UBL) (MarlinFirmware#16197) * Flash leveling (for some STM32) (MarlinFirmware#16174) * Some ESP32 patches (MarlinFirmware#16297) * MKS SGen-L pins EEBF or EFBF scheme (MarlinFirmware#16296) * Release version 2.0.0 * Add Rumba32 support for PIO (MarlinFirmware#16202) * MKS Robin 2 (STM32F407ZE) base support (MarlinFirmware#16270) * Update Czech language (MarlinFirmware#16305) * Sync SKR E3 configs (MarlinFirmware#16301) * Add NOZZLE_AS_PROBE (no probe offsets) (MarlinFirmware#15929) * Version 2.0.1 * Update build status url Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com> Co-authored-by: randellhodges <rhodges@taxfodder.com> Co-authored-by: Jason Smith <jason.inet@gmail.com> Co-authored-by: Antti Andreimann <anttix@users.sourceforge.net> Co-authored-by: thisiskeithb <13375512+thisiskeithb@users.noreply.github.com> Co-authored-by: Tanguy Pruvot <tpruvot@users.noreply.github.com> Co-authored-by: Luu Lac <45380455+shitcreek@users.noreply.github.com> Co-authored-by: Alain Martel <alain74martel@gmail.com> Co-authored-by: Anders Sahlman <57940217+AndersSahlman@users.noreply.github.com> Co-authored-by: dagorel <37673727+dagorel@users.noreply.github.com> Co-authored-by: Giuliano Zaro <3684609+GMagician@users.noreply.github.com> Co-authored-by: MS1987 <lms228@163.com> Co-authored-by: MangaValk <patrickvalkmanga@hotmail.com> Co-authored-by: André Kjellstrup <andre.kjellstrup@gmail.com> Co-authored-by: Tobias Schürg <tobiasschuerg@gmail.com> Co-authored-by: Jeff Eberl <jeffeb3@gmail.com> Co-authored-by: ManuelMcLure <manuel@mclure.org> Co-authored-by: InsanityAutomation <38436470+InsanityAutomation@users.noreply.github.com> Co-authored-by: iain MacDonnell <github@dseven.org> Co-authored-by: Bo Herrmannsen <bo.herrmannsen@gmail.com> Co-authored-by: Luc <8822552+luc-github@users.noreply.github.com> Co-authored-by: danym21 <54744475+danym21@users.noreply.github.com> Co-authored-by: chzj333 <53591189+chzj333@users.noreply.github.com> Co-authored-by: Vertabreaker <opyrus@hotmail.com> Co-authored-by: Moshi Binyamini <MoshiBin@users.noreply.github.com> Co-authored-by: salami738 <24863070+salami738@users.noreply.github.com> Co-authored-by: elementfoundry <57408038+elementfoundry@users.noreply.github.com> Co-authored-by: teemuatlut <teemu.mantykallio@live.fi> Co-authored-by: Mario Costa <mario.silva.costa@gmail.com> Co-authored-by: BigTreeTech <38851044+bigtreetech@users.noreply.github.com> Co-authored-by: Markus Towara <mtowara@gmail.com> Co-authored-by: FLYmaker <49380822+FLYmaker@users.noreply.github.com> Co-authored-by: ferengi82 <christian.maurer@gmx.eu> Co-authored-by: petrzjunior <junior@zahradnik.cz>
|
I'm having the exact issue shown here, but using a arduino mega + a4988, i'm trying to seek the cause, but so far nothing mechanical seems wrong. i'll try burning the latest bugfix with this PR to see if it has any effect, the only "different" thing i have on my machine is that it uses two steppers for the Y (two drivers) to ensure no skew, its a simple cartesian.
|
|
So what are you waiting for? It is missing steps. Flyback diodes on the steppers may also help, but the fix in firmware is real. |
|
I'm also wondering why it's not upstream. Are there concerns about problems with the change? |
|
See where it says merged up above? It is upstream. |
|
OK, GitHub is just awful and lies that it was merged when it was actually committed as a squash (and doesn't provide any link to the squash commit, arrg). For others wondering, it's 1bad8f1. |
|
@sjasonsmith Sorry for digging up an old PR, but I am trying to figure out what was changed to Marlin to cause some issues with stepper control on the ESP32 when I came across this. Not really related to my issue, but I was looking through the code and have a few questions:
This means that if automatically assigns the value based on the driver type, regardless of what has been defined for
but this will always be true, since |
Can you point me to a specific instance you are curious about. It's been a while since I have thought about that. Certainly come of the comments int he configuration files aren't quite right, even before I changed anything. |
It is just a general observation. For example, in |
@vivian-ng if either of those is defined there is a need to time pulses. There are probably plenty of opportunities to clean up and improve that code further. You are probably right that it would be difficult for that to ever be false. |
|
The fact you caught a 'short pulse' on the oscilloscope (in the opening post) suggests to me we need to fully understand what is happening. We need to make sure all the setup and hold times are being met. It would be great if we could tie this to the layer shift problem we have been chasing for the last year. |
@Roxy-3D, the poblem was that the stepper ISR assumed that it would never be interrupted, and based all its timing decisions on offsets added to the initial value of the step timer. This isn't true, since systick and serial ISRs run at higher priority that the stepper. This created race conditions where interrupt at the wrong time would mess up its pulse generation. I haven't looked in detail, but that is probably not the case for the other setup times. I believe they are just simple delays, which could get longer if interrupted, but should never get shorter. |
|
OK... So with this understanding about the serial interrupts... Can we make a small tweak to the code to guarantee we never short change the length of that step pulse? If so, it would probably be worth while to do that and see if it helps with the layer shift problem. I certainly would be willing to run some tests with a change that did that! |
|
@Roxy-3D, that is what this PR was all about, it went in months ago. This thread just got re-awoken by someone asking a question about it. |
|
Thank You for the help understanding that! Good! |
|
I'm currently looking at an issue related to "noisy steppers with input shaping enabled" which seems to go away when Anyway, as I was poking around I noticed that this PR made an interesting change: - // Decrement the count of pending pulses to do
- --events_to_do;
- // For minimum pulse time wait after stopping pulses also
- if (events_to_do) {
- // Just wait for the requested pulse duration
- while (HAL_timer_get_count(PULSE_TIMER_NUM) < pulse_end) { /* nada */ }
- #if MINIMUM_STEPPER_PULSE
- // Add to the value, the time that the pulse must be active (to be used on the next loop)
- pulse_end += hal_timer_t(MIN_PULSE_TICKS);
- #endif
- }
- } while (events_to_do);
+ #if (MINIMUM_STEPPER_PULSE || MAXIMUM_STEPPER_RATE) && DISABLED(I2S_STEPPER_STREAM)
+ if (events_to_do) START_LOW_PULSE();
+ #endif
+ } while (--events_to_do);I noticed that if (events_to_do) START_TIMED_PULSE(); // formerly START_LOW_PULSE…so it could just be… START_TIMED_PULSE();Since the condition was left in place, it's not clear whether there was an intention to skip Can anyone verify that |
|
@thinkyhead in the current rendition of the code it looks like this: I think the intent is that it will use the timer to enforce a delay between steps when using ISR_MULTI_STEPS. The condition should most likely change to this, so that it only starts the timer if it is going to execute another pulse in the same ISR. In general, multi-stepping leads to terrible step spacing, and hopefully doesn't play much of a role on modern 32-bit MCUs. |
If this can be reproduced with a simple movement, then I would recommend replicating the configuration as much as possible in the simulator and capturing the simulated step waveform. Something may stand out that looks very different when enabling OLD_ADAPTIVE_MULTISTEPPING. |


Description
This is a rework of the way step pulses are timed, so that a minimum step can be guaranteed.
I am sure there will be some concerns and discussion surrounding this approach. While solving some problems, it introduces some complications, which I mention later in the PR. I will be away for a week, so wanted to post what I have so people can begin exploring the idea, or try it out on their printers if they find it promising.
I don't consider the code cleanup complete by any means (especially in stepper.h). I expect to need to put additional work into this when I get back, if we choose to use it.
How does it work before this change?
Example of pulse which is shorter than MINIMUM_STEPPER_PULSE (DRV8825)

Configured 2us pulse is < 1us due to SysTick interference during timer access.
Example of short pulse (and missed step) during multi-stepping

How does it work after this change?
XYZ Rise-Time Spread before change, on LPC1768:

XYZ Rise-Time Spread after change, on LPC1768:

Benefits
Concerns
Related Issues
#11205 - User tested an early experiment with a similar solution
#11047 - Probably will be fixed
#12403 - Fairly broad issue, likely resolves some of the issues, especially where sBase is mentioned