Intended for scenarios when FINDA used to be OFF but accidentally became ON without a reason.
The user is obliged to inspect FINDA and tune its switching.
If FSENSOR_DIDNT_SWITCH_ON was caused by misaligned Idler, rehoming it may fix the issue when auto retrying -> no user intervention.
So first invalidate homing flags as the user may have moved the Idler or Selector accidentally.
Beware: we may run into issues when FINDA or FSensor do not work correctly. Selector may rely on the presumed filament position and actually cut it accidentally when trying to rehome.
It is yet to be seen if something like this can actually happen.
MMU-191
This PR is a different solution to what @gudnimg found in PR#233 / PFW-1404.
The benefit of this approach is the fact, that the button press event is generated when the button is pressed and not after it has been released.
The downside is obvious:
CPUFLASH: +28B
RAM: +1B
If QUEUE_FULL error happened, it would be overwritten as INTERNAL
and incorrectly display FW_RUNTIME_ERROR on MK3S.
The fix is to add handling for ERRInternal in every StepInner() and forward the error
to higher application layer
The previous commits by @leptun were correct but there has been one call to Disable axis (and TMC) hidden in `InitMovement`.
Therefore `InitMovement` has been split into 2 separate functions - one is there to allow the original full axis reinit, but the other `InitMovementNoReinitAxis` now only prepares a move without reiniting the TMC driver.
This approach seems to have the benefit of fixing the Idler creep over time.
The disadvantage is the fact, that setting StallGuard threshold is no longer called. We may need to add a special piece of code to handle/apply SGTHRS change at runtime like before.
With PWM_FREQ=2 (fPWM = 2/512 fclk), the idler parked current was quite high since the setting was not actually respected (scientifically tested using hand). In order to allow lower current regulation in stealthchop, TBL and PWM_FREQ need to be adjusted. The lower both of these values, the better. Since TBL affects both stealthchop and spreadcycle, I chose to only modify PWM_FREQ.
This new PWM_FREQ results in half the previous stealthchop PWM frequency and also halves the low current limit (so if previously the minimum achievable current was 300mA, now it would be 150mA). I confirmed that this setting works correctly on a 24V supply. There is no audible noise from stealthChop PWM and the hold current is indeed reduced now (again, scientifically tested using my hand).
This PR is just about tuning the intermediate Idler positions so that the filament gets relieved from the Pulley safely while the Extruder gears take over and the activeSlot+1 filament is not affected by its Idler bearing.
On a test bench filament was creeping about 0.5mm per Toolchange.
Changing the intermediate position from exact middle into 3/4 practically elliminated the creep. We can play with these constants more if necessary in the future.
Unfortunately, there is no way to differentiate between an optimized gpio write (safe always on the atmega32u4) and an unoptimized write (read-modify-write, dangerous if any other pin on that Port is used in an ISR).
While very quickly polling the tmc registers, I noticed that the moving stepper would do some random extra steps. That can only be explained by the following sequence of actions:
- the spi code reads the PORT register
- ISR toggles the step line, changing the value in the PORT register
- the spi code writes the upated PORT back, resetting the step line to the old state
After making the writes atomic, the stepping issue disappeared and the driver checks also worked correctly
- rename stall*guard to StallGuard (match name with the vendor)
- separate TMC2130 module from EEPROM (they do not need to know about each other at all)
- separate SGTHRS settings from motion - moved to globals like all other "global" parameters
- improved EEPROM storage for SGTHRS
This needs some investigation if it is really possible to push the Registers into PROGMEM. I think it should be possible, but the compiler is currently not collaborating.
It is not critical though as we have lots of free RAM at the moment (I can't believe I wrote this on an AVR project :) )
This is to solve a potential problem while feeding to printer's drive gears - while disengaging the Idler, the Pulley was still rotating to avoid grinding the filament (printer is pulling it).
Other filaments could have moved a bit when the Idler's bearings ran over them while the Pulley was still rotating slowly -> the filament could have been moved into the Selector's path causing trouble (especially when not used in the print).
Therefore, the Idler disengages partially now - moves into an intermediate position between the slots.
Then, the Pulley is completely stopped and after that the Idler does a full disengage like before.
We cannot get a constexpr definition of the register addresses, and any
reinterpret cast is currently illegal for a constexpr in c++17.
Change the SPI0, TIFR and TIMSK to volatile const pointers instead.
SPI0 has volatile members instead of marking the entire struct as
volatile, which is probably not a good idea as it technically drops the
volatile from the original pointer.
It looks like we don't have to reset the whole ToolChange state machine when resolving an error with the middle button (Retry) - but jump straight into the feeding phase.
The reasons are multiple:
- If an error happens during the unload phase, it is handled separately in the UnloadFilament state machine
- If an error happens during the feeding phase, the unload has been already successfully completed.
And when restarted from the very beginning, the ToolChange does the last retract sequence from the UnloadFilament phase
-> that is not healthy, because the filament gets pushed away from the Pulley and causes another error.
Sets the UI module into a mode when the printer is in charge of processing the buttons (from all sources).
That means the MMU will detect its buttons but it will not react upon them.
This mode is important for error recovery when the printer needs to do some stuff before the MMU (like preheating the nozzle).
It looks we have some kind of leak when filament sensor state is not completely coherent with FINDA state.
This is yet to be discovered and fixed with some unit tests.
If the MMU receives a command LoadFilament with a slot number SL we reject
the command to avoid moving the selector (effectively cutting the piece of filament present in FINDA).
That includes the scenario when the selector is standing at the very same slot SL, because the filament could be held by the printer (i.e. loaded in the nozzle).
There is one special case though - same slot AND filament load state == InSelector (it MUST NOT be anywhere farther)
Using U_mm inside a compilation unit will force the compiler to generate
a runtime copy of the function, and we don't want that.
But there seems also to be an optimization problem with gcc <= 7.x where
even when declaring an inline function constexpr, if called enough
times, the compiler will choose _not_ to evaluate the function at
compile time and thus avoid our compile-time float->integer
conversions...
For this reason, split the body of the function in two parts: the actual
function that uses AxisUnits at runtime for calling motion.PlanMove, and
a wrapper that forces the conversion. By marking this function as
always_inline, the body is correctly evaluated at compile time at each
call site.
It turned out FINDA needs running timer to perform BlockingInit() correctly.
Therefore setup() was split into setup() (no IRQ) and setup2() (IRQ enabled).
Then, finally, the check for FINDA state became reliable upon start of the FW.
Originally, only FeedingToBondtech was reported to the printer.
With PR#173 we have this operation separated into a fast and a slow stage (for MK3S with the chimney).
It looks like the printer could benefit from knowing if the MMU is still pushing fast
or when it entered the slow stage (to prevent ramming hard the Bondtech gears)
Along with this new state being reported, we also introduce a new ErrorCode::FSENSOR_TOO_EARLY
which basically means that the fsensor triggered in the fast feeding stage.
The acceleration_rate should really by a premultiplication by 1<<24 so
that the division in Step() (while calculating the acc_step_rate) can be
computed again with a right shift.
This was incorrectly changed to F_CPU, which was close enough but would
cause the acceleration to be always slighly slower than expected.
Fix the ratio, but keep the multiplication in fixed-point to avoid a
float conversion.
Correctly compute both the number of steps and direction when
under/overflowing the current position by performing a relative move.
This makes a repeated PlanMove() _always_ perform the move correcly,
even when the upper-level code might require to handle the overflow
itself for measurement.
Add tests for this condition by exposing the internal CurBlockShift() to
the motion unit tests.
This commit looks horribly complex, but the main idea is to have each of the logic::commands
report their terminal OK state in the same way. That allow for leveraging this very moment
to initiate the idle timeout.
Additionally, I wanted to hide the logic of idle mode detection, which resulted in moving the
top level logic from main.cpp into logic/idle_mode.cpp and a set of additional files to compile
in unit tests.
- circular buffer can return its count of elements (even though a better solution may be implemeted later)
- stub_motion can handle multiple planned moves
- improved load/unload filament tests
This PR brings the option to move the selector directly using
buttons of the MMU - obviously while the MMU is idle and no
filament is stuck in the selector.
Left/Right buttons move the selector Left/Right.
Middle button performs a LoadFilament (into the MMU) on the active slot.
With this PR a change of LoadFilament behavior is also introduced.
Now, LoadFilament spins the Pulley for infinite time while waiting
for either FINDA trigger and/or a button pressed.
While motion queuing is safe, code that relies on the current block
needs to run with the isr disabled.
Protect AbortPlannedMoves and CurPosition from isr' interference by
using a RAII guard.
The principle has been implemented, but the TMC is not providing
the right data for some reason - homing doesn't work at all right now.
Also, after solving the physical homing, unit tests must be updated.
This PR brings the following improvements:
- unifies the error handling of TMC and Homing/Stallguard errors on all motorized modules (Idler, Selector, Pulley)
- now we distinguish between Homing and TMC errors + we have a separate handling of these two kinds into CommandBase unified for all motorized modules
- adds unit tests to verify the function
- fixes SetFINDAStateAndDebounce (didn't obey the press parameter before)
Introduce axisUnitToTruncatedUnit to convert from an AxisUnit (now
conveniently returned from Motion::CurPosition) to a physical unit *but*
directly into a truncated integer type, avoiding conversions to long
double types at runtime.
The related function truncatedUnit perform the same truncation of a
constant unit, so that the result of axisUnitToTruncatedUnit and
truncatedUnit(unit) result in the same type for clarity.
Both functions accept a pre-multiplier, which is applied at compile
time for constant values when optimizations are enabled.
Fixes the motion stutters generally happening as more than a single
axis are active and one completes in the middle of the motion.
Do not generate a spourious interval as one axis exits the queue. This
short interval didn't account for the minimal stepping quantum,
potentially causing a timer overflow.
This solves a number of issues - if FINDA or FSensor failed,
the unload was never "complete" - filament was stuck in the selector
blocking it from normal operation.
Now, after all errors have been resolved, filament is explicitly FED
into FINDA and then RETRACTED to Pulley.
Slower loading speed is necessary for precise detection
of filament sensor trigger and starting rotation of the E-motor.
Experimentally it turned out speeds above 80mm/s tend to cause timing issues
(sometimes one can hear a crack as MMU' or the printer's drive gears
slip while pushing the filament).
Such a timing issue then causes blobs in purge towers.
On the other hand - 80mm/s for the fast part of filament load
seems not only absolutely reliable, but also very quiet.
120mm/s for unload is much louder (we may slow it down later)
Solves an interesting tiny issue introduced in the previous commits.
When we start with the filament in selector, the corresponding LED
is set to ON. However, all of the logic state machines only operated
on the LED pair of the active slot -> the starting LED may have been
left ON in some edge cases.
Now, this is resolved by clearing all other LEDs except for the active
slot where appropriate.
Both movable components now perform homing sequences transparently
whenever the logic layer invalidates the homingValid flag.
That reflects the fact, that the user may have moved the Idler or Selector
while trying to resolve a HW issue with un/loading filament.
Basic rules:
- Idler gets rehomed immediately and then moves into the target slot position
- Selector rehomes once it is possible - i.e. when filament load state
is AtPulley - then it immediately and spontanneously executes the homing
sequence and then returns to the desired state
Motivation:
- resolve startup issues (EEPROM says we have filament, but FINDA is not triggered)
- resolve accidental moves of Idler and/or Selector while
digging out stuck filament from the unit