kudos to @leptun for this original and actually a very clean idea.
For the start we report "Reset finished" which in fact corresponds with the MMU state pretty closely
and plays nicely even with the protocol implementation.
And, since the default startup command is the noCommand, which always returns "Finished"
the implementation is clean and straightforward - the response to the first Q0 messages
will look like "X0 F" until a command (T, L, U ...) has been issued.
My MM-control-board v0.3 has following ADC readings in DEBUG_BUTTONS
- none = 1023
- left = 169
- mid = 91-92
- right = 0
As the comparison was larger than 0 MY MMU2 right button wasn't detected.
This introduces a new #define UNITTEST_MOTION which is used to control
the testing scenario:
- Normal tests, we allow the stub to override the built-in definition.
- For motion tests, we stub the lower-level classes and test the
effective implementation
We also repeat the prototype of the function, which IMHO is more
readable and more flexible: we need to use inline for the real
definition, which would require even more macros otherwise.
That implies changing motor's mode from SpreadCycle into StealtMode (or vice versa)
requires a stand still MMU with no other command (i.e. motor moves) being performed.
This elegantly solves the synchronization problem of TMC2130 mode change, as it results
in severe jerking while a motor is moving.
The change in protocol is minimal - M0/M1 first return `M0 A` (accepted) and another `Q0` then
returns `M0 F` (finished). The MK4 counterpart may ignore the additional report if necessary
as the mode change is done immediately (shortly after responding with `M0 A`)
Motion::SetMode(axis, mode) was incorrectly looping through all axes,
setting the same axis three times.
Fix this and introduce Motion::SetMode(mode) which actually loops
through all axes (see PR #110)
If the queue is full and a new move is queued, panic!
Introduce a new error code QUEUE_FULL to help diagnose situations where
the queue is handled improperly: likely one of the state machines not
waiting for the previous actions to finish.
PulseGen::PlanMove returns a boolean if the queue cannot be moved.
We could extend this to Motion::PlanMove, however all moves would then
have to check for this. Having a global check such as this ensures
we never ignore such situation.
we shall think about the Pulley as well, it looks like it should get its
own class just like Idler and Selector as it behaves very similarly in terms
of stepping and error checking
Test performed by moving 500mm of filament back&forth 5 times across 5
pulleys of a MMU2 unit.
The error with this factor is centered around +/- 1.5mm depending on the
pulley and tension on the idler.
Allow to chain moves by adding one extra parameter to the PlanMove[to]
functions: ending speed.
A move will always be accelerated from the last speed towards end ending
speed. The following:
PlanMove(100._mm, 50._mm_s, 50._mm_s);
PlanMove(200._mm, 100._mm_s);
Will first move the axis 100mm, accelerating towards 50mm/s, then
accelerate again to 100mm/s. The move will for then decelerate towards a
full stop after reaching 300mm in total.
Acceleration can be changed for each segment, so that a custom
acceleration curve can be created:
SetAcceleration(10._mm_s2);
PlanMove(100._mm, 50._mm_s, 50._mm_s);
SetAcceleration(100._mm_s2);
PlanMove(100._mm, 50._mm_s, 50._mm_s);
The ending speed might not always be reached, depending on the current
acceleration settings. The new function "Rate()" will return the ending
feedrate of the last move, if necessary.
AbortPlannedMoves accepts a new "halt" parameter to control how moves
will be chanined when interrupting the current move. By default
(halt=true) the move is completely interrupted.
When halt=false is requested, a subsequent move will be chained starting
at the currently aborted velocity. This allows to chain moves in reponse
to events, for example to accelerate the pulley without stopping as soon
as the FINDA is triggered, it's sufficient to interrupt the current move
followed by a new one:
PlanMove(maximum_loading_lenght, slow_feedrate);
... wait for PINDA trigger ...
AbortPlannedMoves(true);
PlanMove(bowden_lenght, fast_feedrate);
will seamlessy continue loading and transition to the fast feedrate.
Jerk control has been simplified. It now handles only the maximal
velocity change of the last segment, which doesn't require reverse
planning.
Add a new parameter "halt" (default to true) to control the stopping
behavior:
- halt=true: no subsequent moves will be planned, motions stops abruptly
- half=false: a new move will be chained after the current one
Move motion.Step() directly inside the __AVR__ code, silencing an unused
variable warning.
Calling motion.Step() without getting or setting the timer is not useful
anyway.
Avoid calling PulseGen::Step() on idle axes by checking for a non-zero
queue size (which is more efficient to compute).
Increase stepTimerQuantum to 128us to ensure acceleration can be
computed in realtime for 3 axes at the same time.
Fix the logic of the static assertion, which was flipped: we need to
create slices larger than the maximal step frequency in order to ensure
no axis is starved while moving.
TogglePin was incorrectly using the PINx pin for toggling, causing the
current pins (in addition to the requested ones) to be toggled, causing
stepping havoc.
This is a tentative/crude implementation of an Init and ISR for the MMU
in order to check the motion API.
We remove the "extern void Isr", declaring it "static inline" instead.
We need to inline the ISR here in order to avoid the function call.
Include the missing speed_table data in the executable. This bumps the
code size to ~60% of the flash.
Implemement motion::Init to setup the ISR and timers, and replace the
call in main from tmc::Init to motion::Init. Motion will init each
driver every time the axis is enabled, so there should be no need for
a global module initialization (we need SPI, but this is initialized
earlier on by it's own module anyway).
The timer is currently setup without any HAL or proper TIMER1 wrapper.
This is to be improved later.
The real MMU unit seems to slow down quite a bit during acceleration.
At this point we need to inline some methods in PulseGen to avoid
overhead, however this breaks the stubs.
The parameter config::AxisConfig::uSteps was supposed to be
microstepping resolution, but it's instead being used as the driver's
MRES directly.
To avoid a runtime conversion, rename the field to mRes and define a new
enum listing all the possible (and valid) microstepping resolutions.
This simplifies the code and makes clear the stepsPerUnit scale.
Assign correct stepsPerUnit to all axes as a result, including working
limits.
Besides Unload Filament, which only operates on active slot, all other
top level state machines check the validity of the command's parameter.
If the parameter is out of range for available slots, they return
ErrorCode::INVALID_TOOL now.
Extracted from the previous FW, may need some tuning based on units selected
for each of these axes (degrees, millimeters) - waiting for an update
of the motion implementation.
Updated starting conditions of the unit tests to reflect the global configuration.
MMU-58
- Distinguish among FINDA on/off failuje
- The same applies to newly introduced Filament sensor errors
- Add TMC init error
- Add a communication error ID - to be used on the printer
This allows Unit<> and AxisUnit<> to be scaled with a fraction as
expected, promoting the scaler to the same unit:
0.2_mm * 10 => 2_mm (mm*f => mm)
Multiplication type is commutative:
10 * 0.2_mm => 2_mm (f*mm => mm)
Division isn't:
0.2_mm / 10 => 0.02_mm (mm*1/f => mm)
10 / 0.2_mm => error (illegal type conversion)
Introduces a nasty hack to forcefully write into the constexpr SPI descriptor's registers
(which is the correct way in ASM, but kind of cumbersome in C++ now)
Please note Windows CRLF sequence is not supported,
but a separate CR xor a separate LF works now.
This is a workaround for stupid terminals for debugging purposes,
and it is not necessary for the protocol to work on its own.
It may be removed in the future.
- Move unit/step conversion to modules/axisunit.h
- Unify motion::unitToAxisUnit<> and motion::unitToSteps<>,
making conversion in other modules just as easy as motion.
- Improve the documentation