mirror of
https://github.com/andreili/klipper.git
synced 2025-08-23 11:24:06 +02:00
Merge branch 'Klipper3d:master' into master
This commit is contained in:
commit
7018075f70
@ -292,33 +292,6 @@ probe_count: 5, 3
|
|||||||
z-offset. Note that this coordinate must NOT be in a location specified as
|
z-offset. Note that this coordinate must NOT be in a location specified as
|
||||||
a `faulty_region` if a probe is necessary.
|
a `faulty_region` if a probe is necessary.
|
||||||
|
|
||||||
#### The deprecated relative_reference_index
|
|
||||||
|
|
||||||
Existing configurations using the `relative_reference_index` option must be
|
|
||||||
updated to use the `zero_reference_position`. The response to the
|
|
||||||
[BED_MESH_OUTPUT PGP=1](#output) gcode command will include the (X, Y)
|
|
||||||
coordinate associated with the index; this position may be used as the value for
|
|
||||||
the `zero_reference_position`. The output will look similar to the following:
|
|
||||||
|
|
||||||
```
|
|
||||||
// bed_mesh: generated points
|
|
||||||
// Index | Tool Adjusted | Probe
|
|
||||||
// 0 | (1.0, 1.0) | (24.0, 6.0)
|
|
||||||
// 1 | (36.7, 1.0) | (59.7, 6.0)
|
|
||||||
// 2 | (72.3, 1.0) | (95.3, 6.0)
|
|
||||||
// 3 | (108.0, 1.0) | (131.0, 6.0)
|
|
||||||
... (additional generated points)
|
|
||||||
// bed_mesh: relative_reference_index 24 is (131.5, 108.0)
|
|
||||||
```
|
|
||||||
|
|
||||||
_Note: The above output is also printed in `klippy.log` during initialization._
|
|
||||||
|
|
||||||
Using the example above we see that the `relative_reference_index` is
|
|
||||||
printed along with its coordinate. Thus the `zero_reference_position`
|
|
||||||
is `131.5, 108`.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Faulty Regions
|
### Faulty Regions
|
||||||
|
|
||||||
It is possible for some areas of a bed to report inaccurate results when
|
It is possible for some areas of a bed to report inaccurate results when
|
||||||
|
@ -8,6 +8,11 @@ All dates in this document are approximate.
|
|||||||
|
|
||||||
## Changes
|
## Changes
|
||||||
|
|
||||||
|
20250811: Support for the `max_accel_to_decel` parameter in the
|
||||||
|
`[printer]` config section has been removed and support for the
|
||||||
|
`ACCEL_TO_DECEL` parameter in the `SET_VELOCITY_LIMIT` command has
|
||||||
|
been removed. These capabilities were deprecated on 20240313.
|
||||||
|
|
||||||
20250721: The `[pca9632]` and `[mcp4018]` modules no longer accept the
|
20250721: The `[pca9632]` and `[mcp4018]` modules no longer accept the
|
||||||
`scl_pin` and `sda_pin` options. Use `i2c_software_scl_pin` and
|
`scl_pin` and `sda_pin` options. Use `i2c_software_scl_pin` and
|
||||||
`i2c_software_sda_pin` instead.
|
`i2c_software_sda_pin` instead.
|
||||||
|
@ -126,8 +126,6 @@ max_accel:
|
|||||||
# decelerate to zero at each corner. The value specified here may be
|
# decelerate to zero at each corner. The value specified here may be
|
||||||
# changed at runtime using the SET_VELOCITY_LIMIT command. The
|
# changed at runtime using the SET_VELOCITY_LIMIT command. The
|
||||||
# default is 5mm/s.
|
# default is 5mm/s.
|
||||||
#max_accel_to_decel:
|
|
||||||
# This parameter is deprecated and should no longer be used.
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### [stepper]
|
### [stepper]
|
||||||
@ -740,7 +738,6 @@ max_velocity:
|
|||||||
max_accel:
|
max_accel:
|
||||||
#minimum_cruise_ratio:
|
#minimum_cruise_ratio:
|
||||||
#square_corner_velocity:
|
#square_corner_velocity:
|
||||||
#max_accel_to_decel:
|
|
||||||
#max_z_velocity:
|
#max_z_velocity:
|
||||||
#max_z_accel:
|
#max_z_accel:
|
||||||
|
|
||||||
|
@ -17,16 +17,16 @@ COMPILE_ARGS = ("-Wall -g -O2 -shared -fPIC"
|
|||||||
" -o %s %s")
|
" -o %s %s")
|
||||||
SSE_FLAGS = "-mfpmath=sse -msse2"
|
SSE_FLAGS = "-mfpmath=sse -msse2"
|
||||||
SOURCE_FILES = [
|
SOURCE_FILES = [
|
||||||
'pyhelper.c', 'serialqueue.c', 'stepcompress.c', 'itersolve.c', 'trapq.c',
|
'pyhelper.c', 'serialqueue.c', 'stepcompress.c', 'steppersync.c',
|
||||||
'pollreactor.c', 'msgblock.c', 'trdispatch.c',
|
'itersolve.c', 'trapq.c', 'pollreactor.c', 'msgblock.c', 'trdispatch.c',
|
||||||
'kin_cartesian.c', 'kin_corexy.c', 'kin_corexz.c', 'kin_delta.c',
|
'kin_cartesian.c', 'kin_corexy.c', 'kin_corexz.c', 'kin_delta.c',
|
||||||
'kin_deltesian.c', 'kin_polar.c', 'kin_rotary_delta.c', 'kin_winch.c',
|
'kin_deltesian.c', 'kin_polar.c', 'kin_rotary_delta.c', 'kin_winch.c',
|
||||||
'kin_extruder.c', 'kin_shaper.c', 'kin_idex.c', 'kin_generic.c'
|
'kin_extruder.c', 'kin_shaper.c', 'kin_idex.c', 'kin_generic.c'
|
||||||
]
|
]
|
||||||
DEST_LIB = "c_helper.so"
|
DEST_LIB = "c_helper.so"
|
||||||
OTHER_FILES = [
|
OTHER_FILES = [
|
||||||
'list.h', 'serialqueue.h', 'stepcompress.h', 'itersolve.h', 'pyhelper.h',
|
'list.h', 'serialqueue.h', 'stepcompress.h', 'steppersync.h',
|
||||||
'trapq.h', 'pollreactor.h', 'msgblock.h'
|
'itersolve.h', 'pyhelper.h', 'trapq.h', 'pollreactor.h', 'msgblock.h'
|
||||||
]
|
]
|
||||||
|
|
||||||
defs_stepcompress = """
|
defs_stepcompress = """
|
||||||
@ -54,25 +54,28 @@ defs_stepcompress = """
|
|||||||
int stepcompress_extract_old(struct stepcompress *sc
|
int stepcompress_extract_old(struct stepcompress *sc
|
||||||
, struct pull_history_steps *p, int max
|
, struct pull_history_steps *p, int max
|
||||||
, uint64_t start_clock, uint64_t end_clock);
|
, uint64_t start_clock, uint64_t end_clock);
|
||||||
|
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
|
, struct stepper_kinematics *sk);
|
||||||
|
"""
|
||||||
|
|
||||||
|
defs_steppersync = """
|
||||||
struct steppersync *steppersync_alloc(struct serialqueue *sq
|
struct steppersync *steppersync_alloc(struct serialqueue *sq
|
||||||
, struct stepcompress **sc_list, int sc_num, int move_num);
|
, struct stepcompress **sc_list, int sc_num, int move_num);
|
||||||
void steppersync_free(struct steppersync *ss);
|
void steppersync_free(struct steppersync *ss);
|
||||||
void steppersync_set_time(struct steppersync *ss
|
void steppersync_set_time(struct steppersync *ss
|
||||||
, double time_offset, double mcu_freq);
|
, double time_offset, double mcu_freq);
|
||||||
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
int32_t steppersync_generate_steps(struct steppersync *ss
|
||||||
, uint64_t clear_history_clock);
|
, double gen_steps_time, uint64_t flush_clock);
|
||||||
|
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
|
||||||
|
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||||
"""
|
"""
|
||||||
|
|
||||||
defs_itersolve = """
|
defs_itersolve = """
|
||||||
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
|
||||||
, double flush_time);
|
|
||||||
double itersolve_check_active(struct stepper_kinematics *sk
|
double itersolve_check_active(struct stepper_kinematics *sk
|
||||||
, double flush_time);
|
, double flush_time);
|
||||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq);
|
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
void itersolve_set_stepcompress(struct stepper_kinematics *sk
|
, double step_dist);
|
||||||
, struct stepcompress *sc, double step_dist);
|
|
||||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||||
, double x, double y, double z);
|
, double x, double y, double z);
|
||||||
void itersolve_set_position(struct stepper_kinematics *sk
|
void itersolve_set_position(struct stepper_kinematics *sk
|
||||||
@ -228,7 +231,7 @@ defs_std = """
|
|||||||
|
|
||||||
defs_all = [
|
defs_all = [
|
||||||
defs_pyhelper, defs_serialqueue, defs_std, defs_stepcompress,
|
defs_pyhelper, defs_serialqueue, defs_std, defs_stepcompress,
|
||||||
defs_itersolve, defs_trapq, defs_trdispatch,
|
defs_steppersync, defs_itersolve, defs_trapq, defs_trdispatch,
|
||||||
defs_kin_cartesian, defs_kin_corexy, defs_kin_corexz, defs_kin_delta,
|
defs_kin_cartesian, defs_kin_corexy, defs_kin_corexz, defs_kin_delta,
|
||||||
defs_kin_deltesian, defs_kin_polar, defs_kin_rotary_delta, defs_kin_winch,
|
defs_kin_deltesian, defs_kin_polar, defs_kin_rotary_delta, defs_kin_winch,
|
||||||
defs_kin_extruder, defs_kin_shaper, defs_kin_idex,
|
defs_kin_extruder, defs_kin_shaper, defs_kin_idex,
|
||||||
|
@ -26,8 +26,8 @@ struct timepos {
|
|||||||
|
|
||||||
// Generate step times for a portion of a move
|
// Generate step times for a portion of a move
|
||||||
static int32_t
|
static int32_t
|
||||||
itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
itersolve_gen_steps_range(struct stepper_kinematics *sk, struct stepcompress *sc
|
||||||
, double abs_start, double abs_end)
|
, struct move *m, double abs_start, double abs_end)
|
||||||
{
|
{
|
||||||
sk_calc_callback calc_position_cb = sk->calc_position_cb;
|
sk_calc_callback calc_position_cb = sk->calc_position_cb;
|
||||||
double half_step = .5 * sk->step_dist;
|
double half_step = .5 * sk->step_dist;
|
||||||
@ -37,7 +37,7 @@ itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
|||||||
if (end > m->move_t)
|
if (end > m->move_t)
|
||||||
end = m->move_t;
|
end = m->move_t;
|
||||||
struct timepos old_guess = {start, sk->commanded_pos}, guess = old_guess;
|
struct timepos old_guess = {start, sk->commanded_pos}, guess = old_guess;
|
||||||
int sdir = stepcompress_get_step_dir(sk->sc);
|
int sdir = stepcompress_get_step_dir(sc);
|
||||||
int is_dir_change = 0, have_bracket = 0, check_oscillate = 0;
|
int is_dir_change = 0, have_bracket = 0, check_oscillate = 0;
|
||||||
double target = sk->commanded_pos + (sdir ? half_step : -half_step);
|
double target = sk->commanded_pos + (sdir ? half_step : -half_step);
|
||||||
double last_time=start, low_time=start, high_time=start + SEEK_TIME_RESET;
|
double last_time=start, low_time=start, high_time=start + SEEK_TIME_RESET;
|
||||||
@ -99,13 +99,13 @@ itersolve_gen_steps_range(struct stepper_kinematics *sk, struct move *m
|
|||||||
if (!have_bracket || high_time - low_time > .000000001) {
|
if (!have_bracket || high_time - low_time > .000000001) {
|
||||||
if (!is_dir_change && rel_dist >= -half_step)
|
if (!is_dir_change && rel_dist >= -half_step)
|
||||||
// Avoid rollback if stepper fully reaches step position
|
// Avoid rollback if stepper fully reaches step position
|
||||||
stepcompress_commit(sk->sc);
|
stepcompress_commit(sc);
|
||||||
// Guess is not close enough - guess again with new time
|
// Guess is not close enough - guess again with new time
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Found next step - submit it
|
// Found next step - submit it
|
||||||
int ret = stepcompress_append(sk->sc, sdir, m->print_time, guess.time);
|
int ret = stepcompress_append(sc, sdir, m->print_time, guess.time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
target = sdir ? target+half_step+half_step : target-half_step-half_step;
|
target = sdir ? target+half_step+half_step : target-half_step-half_step;
|
||||||
@ -143,8 +143,9 @@ check_active(struct stepper_kinematics *sk, struct move *m)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Generate step times for a range of moves on the trapq
|
// Generate step times for a range of moves on the trapq
|
||||||
int32_t __visible
|
int32_t
|
||||||
itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
itersolve_generate_steps(struct stepper_kinematics *sk, struct stepcompress *sc
|
||||||
|
, double flush_time)
|
||||||
{
|
{
|
||||||
double last_flush_time = sk->last_flush_time;
|
double last_flush_time = sk->last_flush_time;
|
||||||
sk->last_flush_time = flush_time;
|
sk->last_flush_time = flush_time;
|
||||||
@ -170,15 +171,15 @@ itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
|||||||
while (--skip_count && pm->print_time > abs_start)
|
while (--skip_count && pm->print_time > abs_start)
|
||||||
pm = list_prev_entry(pm, node);
|
pm = list_prev_entry(pm, node);
|
||||||
do {
|
do {
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, pm, abs_start
|
int32_t ret = itersolve_gen_steps_range(
|
||||||
, flush_time);
|
sk, sc, pm, abs_start, flush_time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
pm = list_next_entry(pm, node);
|
pm = list_next_entry(pm, node);
|
||||||
} while (pm != m);
|
} while (pm != m);
|
||||||
}
|
}
|
||||||
// Generate steps for this move
|
// Generate steps for this move
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, m, last_flush_time
|
int32_t ret = itersolve_gen_steps_range(sk, sc, m, last_flush_time
|
||||||
, flush_time);
|
, flush_time);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
@ -195,8 +196,8 @@ itersolve_generate_steps(struct stepper_kinematics *sk, double flush_time)
|
|||||||
double abs_end = force_steps_time;
|
double abs_end = force_steps_time;
|
||||||
if (abs_end > flush_time)
|
if (abs_end > flush_time)
|
||||||
abs_end = flush_time;
|
abs_end = flush_time;
|
||||||
int32_t ret = itersolve_gen_steps_range(sk, m, last_flush_time
|
int32_t ret = itersolve_gen_steps_range(
|
||||||
, abs_end);
|
sk, sc, m, last_flush_time, abs_end);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
skip_count = 1;
|
skip_count = 1;
|
||||||
@ -240,16 +241,10 @@ itersolve_is_active_axis(struct stepper_kinematics *sk, char axis)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void __visible
|
void __visible
|
||||||
itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq)
|
itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
|
, double step_dist)
|
||||||
{
|
{
|
||||||
sk->tq = tq;
|
sk->tq = tq;
|
||||||
}
|
|
||||||
|
|
||||||
void __visible
|
|
||||||
itersolve_set_stepcompress(struct stepper_kinematics *sk
|
|
||||||
, struct stepcompress *sc, double step_dist)
|
|
||||||
{
|
|
||||||
sk->sc = sc;
|
|
||||||
sk->step_dist = step_dist;
|
sk->step_dist = step_dist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +26,11 @@ struct stepper_kinematics {
|
|||||||
};
|
};
|
||||||
|
|
||||||
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
|
||||||
, double flush_time);
|
, struct stepcompress *sc, double flush_time);
|
||||||
double itersolve_check_active(struct stepper_kinematics *sk, double flush_time);
|
double itersolve_check_active(struct stepper_kinematics *sk, double flush_time);
|
||||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq);
|
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||||
void itersolve_set_stepcompress(struct stepper_kinematics *sk
|
, double step_dist);
|
||||||
, struct stepcompress *sc, double step_dist);
|
|
||||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||||
, double x, double y, double z);
|
, double x, double y, double z);
|
||||||
void itersolve_set_position(struct stepper_kinematics *sk
|
void itersolve_set_position(struct stepper_kinematics *sk
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Stepper pulse schedule compression
|
// Stepper pulse schedule compression
|
||||||
//
|
//
|
||||||
// Copyright (C) 2016-2021 Kevin O'Connor <kevin@koconnor.net>
|
// Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
//
|
//
|
||||||
// This file may be distributed under the terms of the GNU GPLv3 license.
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
@ -21,6 +21,7 @@
|
|||||||
#include <stdlib.h> // malloc
|
#include <stdlib.h> // malloc
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
#include "compiler.h" // DIV_ROUND_UP
|
#include "compiler.h" // DIV_ROUND_UP
|
||||||
|
#include "itersolve.h" // itersolve_generate_steps
|
||||||
#include "pyhelper.h" // errorf
|
#include "pyhelper.h" // errorf
|
||||||
#include "serialqueue.h" // struct queue_message
|
#include "serialqueue.h" // struct queue_message
|
||||||
#include "stepcompress.h" // stepcompress_alloc
|
#include "stepcompress.h" // stepcompress_alloc
|
||||||
@ -46,6 +47,8 @@ struct stepcompress {
|
|||||||
// History tracking
|
// History tracking
|
||||||
int64_t last_position;
|
int64_t last_position;
|
||||||
struct list_head history_list;
|
struct list_head history_list;
|
||||||
|
// Itersolve reference
|
||||||
|
struct stepper_kinematics *sk;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct step_move {
|
struct step_move {
|
||||||
@ -276,9 +279,9 @@ stepcompress_set_invert_sdir(struct stepcompress *sc, uint32_t invert_sdir)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to free items from the history_list
|
// Expire the stepcompress history older than the given clock
|
||||||
static void
|
void
|
||||||
free_history(struct stepcompress *sc, uint64_t end_clock)
|
stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock)
|
||||||
{
|
{
|
||||||
while (!list_empty(&sc->history_list)) {
|
while (!list_empty(&sc->history_list)) {
|
||||||
struct history_steps *hs = list_last_entry(
|
struct history_steps *hs = list_last_entry(
|
||||||
@ -290,13 +293,6 @@ free_history(struct stepcompress *sc, uint64_t end_clock)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Expire the stepcompress history older than the given clock
|
|
||||||
static void
|
|
||||||
stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock)
|
|
||||||
{
|
|
||||||
free_history(sc, end_clock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free memory associated with a 'stepcompress' object
|
// Free memory associated with a 'stepcompress' object
|
||||||
void __visible
|
void __visible
|
||||||
stepcompress_free(struct stepcompress *sc)
|
stepcompress_free(struct stepcompress *sc)
|
||||||
@ -305,7 +301,7 @@ stepcompress_free(struct stepcompress *sc)
|
|||||||
return;
|
return;
|
||||||
free(sc->queue);
|
free(sc->queue);
|
||||||
message_queue_free(&sc->msg_queue);
|
message_queue_free(&sc->msg_queue);
|
||||||
free_history(sc, UINT64_MAX);
|
stepcompress_history_expire(sc, UINT64_MAX);
|
||||||
free(sc);
|
free(sc);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,6 +317,12 @@ stepcompress_get_step_dir(struct stepcompress *sc)
|
|||||||
return sc->next_step_dir;
|
return sc->next_step_dir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct list_head *
|
||||||
|
stepcompress_get_msg_queue(struct stepcompress *sc)
|
||||||
|
{
|
||||||
|
return &sc->msg_queue;
|
||||||
|
}
|
||||||
|
|
||||||
// Determine the "print time" of the last_step_clock
|
// Determine the "print time" of the last_step_clock
|
||||||
static void
|
static void
|
||||||
calc_last_step_print_time(struct stepcompress *sc)
|
calc_last_step_print_time(struct stepcompress *sc)
|
||||||
@ -330,7 +332,7 @@ calc_last_step_print_time(struct stepcompress *sc)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the conversion rate of 'print_time' to mcu clock
|
// Set the conversion rate of 'print_time' to mcu clock
|
||||||
static void
|
void
|
||||||
stepcompress_set_time(struct stepcompress *sc
|
stepcompress_set_time(struct stepcompress *sc
|
||||||
, double time_offset, double mcu_freq)
|
, double time_offset, double mcu_freq)
|
||||||
{
|
{
|
||||||
@ -664,164 +666,25 @@ stepcompress_extract_old(struct stepcompress *sc, struct pull_history_steps *p
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Store a reference to stepper_kinematics
|
||||||
/****************************************************************
|
|
||||||
* Step compress synchronization
|
|
||||||
****************************************************************/
|
|
||||||
|
|
||||||
// The steppersync object is used to synchronize the output of mcu
|
|
||||||
// step commands. The mcu can only queue a limited number of step
|
|
||||||
// commands - this code tracks when items on the mcu step queue become
|
|
||||||
// free so that new commands can be transmitted. It also ensures the
|
|
||||||
// mcu step queue is ordered between steppers so that no stepper
|
|
||||||
// starves the other steppers of space in the mcu step queue.
|
|
||||||
|
|
||||||
struct steppersync {
|
|
||||||
// Serial port
|
|
||||||
struct serialqueue *sq;
|
|
||||||
struct command_queue *cq;
|
|
||||||
// Storage for associated stepcompress objects
|
|
||||||
struct stepcompress **sc_list;
|
|
||||||
int sc_num;
|
|
||||||
// Storage for list of pending move clocks
|
|
||||||
uint64_t *move_clocks;
|
|
||||||
int num_move_clocks;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Allocate a new 'steppersync' object
|
|
||||||
struct steppersync * __visible
|
|
||||||
steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list
|
|
||||||
, int sc_num, int move_num)
|
|
||||||
{
|
|
||||||
struct steppersync *ss = malloc(sizeof(*ss));
|
|
||||||
memset(ss, 0, sizeof(*ss));
|
|
||||||
ss->sq = sq;
|
|
||||||
ss->cq = serialqueue_alloc_commandqueue();
|
|
||||||
|
|
||||||
ss->sc_list = malloc(sizeof(*sc_list)*sc_num);
|
|
||||||
memcpy(ss->sc_list, sc_list, sizeof(*sc_list)*sc_num);
|
|
||||||
ss->sc_num = sc_num;
|
|
||||||
|
|
||||||
ss->move_clocks = malloc(sizeof(*ss->move_clocks)*move_num);
|
|
||||||
memset(ss->move_clocks, 0, sizeof(*ss->move_clocks)*move_num);
|
|
||||||
ss->num_move_clocks = move_num;
|
|
||||||
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free memory associated with a 'steppersync' object
|
|
||||||
void __visible
|
void __visible
|
||||||
steppersync_free(struct steppersync *ss)
|
stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
|
, struct stepper_kinematics *sk)
|
||||||
{
|
{
|
||||||
if (!ss)
|
sc->sk = sk;
|
||||||
return;
|
|
||||||
free(ss->sc_list);
|
|
||||||
free(ss->move_clocks);
|
|
||||||
serialqueue_free_commandqueue(ss->cq);
|
|
||||||
free(ss);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the conversion rate of 'print_time' to mcu clock
|
// Generate steps (via itersolve) and flush
|
||||||
void __visible
|
int32_t
|
||||||
steppersync_set_time(struct steppersync *ss, double time_offset
|
stepcompress_generate_steps(struct stepcompress *sc, double gen_steps_time
|
||||||
, double mcu_freq)
|
, uint64_t flush_clock)
|
||||||
{
|
{
|
||||||
int i;
|
if (!sc->sk)
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
return 0;
|
||||||
struct stepcompress *sc = ss->sc_list[i];
|
// Generate steps
|
||||||
stepcompress_set_time(sc, time_offset, mcu_freq);
|
int32_t ret = itersolve_generate_steps(sc->sk, sc, gen_steps_time);
|
||||||
}
|
if (ret)
|
||||||
}
|
return ret;
|
||||||
|
// Flush steps
|
||||||
// Expire the stepcompress history before the given clock time
|
return stepcompress_flush(sc, flush_clock);
|
||||||
static void
|
|
||||||
steppersync_history_expire(struct steppersync *ss, uint64_t end_clock)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < ss->sc_num; i++)
|
|
||||||
{
|
|
||||||
struct stepcompress *sc = ss->sc_list[i];
|
|
||||||
stepcompress_history_expire(sc, end_clock);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement a binary heap algorithm to track when the next available
|
|
||||||
// 'struct move' in the mcu will be available
|
|
||||||
static void
|
|
||||||
heap_replace(struct steppersync *ss, uint64_t req_clock)
|
|
||||||
{
|
|
||||||
uint64_t *mc = ss->move_clocks;
|
|
||||||
int nmc = ss->num_move_clocks, pos = 0;
|
|
||||||
for (;;) {
|
|
||||||
int child1_pos = 2*pos+1, child2_pos = 2*pos+2;
|
|
||||||
uint64_t child2_clock = child2_pos < nmc ? mc[child2_pos] : UINT64_MAX;
|
|
||||||
uint64_t child1_clock = child1_pos < nmc ? mc[child1_pos] : UINT64_MAX;
|
|
||||||
if (req_clock <= child1_clock && req_clock <= child2_clock) {
|
|
||||||
mc[pos] = req_clock;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (child1_clock < child2_clock) {
|
|
||||||
mc[pos] = child1_clock;
|
|
||||||
pos = child1_pos;
|
|
||||||
} else {
|
|
||||||
mc[pos] = child2_clock;
|
|
||||||
pos = child2_pos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find and transmit any scheduled steps prior to the given 'move_clock'
|
|
||||||
int __visible
|
|
||||||
steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
|
||||||
, uint64_t clear_history_clock)
|
|
||||||
{
|
|
||||||
// Flush each stepcompress to the specified move_clock
|
|
||||||
int i;
|
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
|
||||||
int ret = stepcompress_flush(ss->sc_list[i], move_clock);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Order commands by the reqclock of each pending command
|
|
||||||
struct list_head msgs;
|
|
||||||
list_init(&msgs);
|
|
||||||
for (;;) {
|
|
||||||
// Find message with lowest reqclock
|
|
||||||
uint64_t req_clock = MAX_CLOCK;
|
|
||||||
struct queue_message *qm = NULL;
|
|
||||||
for (i=0; i<ss->sc_num; i++) {
|
|
||||||
struct stepcompress *sc = ss->sc_list[i];
|
|
||||||
if (!list_empty(&sc->msg_queue)) {
|
|
||||||
struct queue_message *m = list_first_entry(
|
|
||||||
&sc->msg_queue, struct queue_message, node);
|
|
||||||
if (m->req_clock < req_clock) {
|
|
||||||
qm = m;
|
|
||||||
req_clock = m->req_clock;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!qm || (qm->min_clock && req_clock > move_clock))
|
|
||||||
break;
|
|
||||||
|
|
||||||
uint64_t next_avail = ss->move_clocks[0];
|
|
||||||
if (qm->min_clock)
|
|
||||||
// The qm->min_clock field is overloaded to indicate that
|
|
||||||
// the command uses the 'move queue' and to store the time
|
|
||||||
// that move queue item becomes available.
|
|
||||||
heap_replace(ss, qm->min_clock);
|
|
||||||
// Reset the min_clock to its normal meaning (minimum transmit time)
|
|
||||||
qm->min_clock = next_avail;
|
|
||||||
|
|
||||||
// Batch this command
|
|
||||||
list_del(&qm->node);
|
|
||||||
list_add_tail(&qm->node, &msgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Transmit commands
|
|
||||||
if (!list_empty(&msgs))
|
|
||||||
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
|
|
||||||
|
|
||||||
steppersync_history_expire(ss, clear_history_clock);
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,13 @@ void stepcompress_fill(struct stepcompress *sc, uint32_t max_error
|
|||||||
, int32_t set_next_step_dir_msgtag);
|
, int32_t set_next_step_dir_msgtag);
|
||||||
void stepcompress_set_invert_sdir(struct stepcompress *sc
|
void stepcompress_set_invert_sdir(struct stepcompress *sc
|
||||||
, uint32_t invert_sdir);
|
, uint32_t invert_sdir);
|
||||||
|
void stepcompress_history_expire(struct stepcompress *sc, uint64_t end_clock);
|
||||||
void stepcompress_free(struct stepcompress *sc);
|
void stepcompress_free(struct stepcompress *sc);
|
||||||
uint32_t stepcompress_get_oid(struct stepcompress *sc);
|
uint32_t stepcompress_get_oid(struct stepcompress *sc);
|
||||||
int stepcompress_get_step_dir(struct stepcompress *sc);
|
int stepcompress_get_step_dir(struct stepcompress *sc);
|
||||||
|
struct list_head *stepcompress_get_msg_queue(struct stepcompress *sc);
|
||||||
|
void stepcompress_set_time(struct stepcompress *sc
|
||||||
|
, double time_offset, double mcu_freq);
|
||||||
int stepcompress_append(struct stepcompress *sc, int sdir
|
int stepcompress_append(struct stepcompress *sc, int sdir
|
||||||
, double print_time, double step_time);
|
, double print_time, double step_time);
|
||||||
int stepcompress_commit(struct stepcompress *sc);
|
int stepcompress_commit(struct stepcompress *sc);
|
||||||
@ -34,15 +38,11 @@ int stepcompress_queue_mq_msg(struct stepcompress *sc, uint64_t req_clock
|
|||||||
int stepcompress_extract_old(struct stepcompress *sc
|
int stepcompress_extract_old(struct stepcompress *sc
|
||||||
, struct pull_history_steps *p, int max
|
, struct pull_history_steps *p, int max
|
||||||
, uint64_t start_clock, uint64_t end_clock);
|
, uint64_t start_clock, uint64_t end_clock);
|
||||||
|
struct stepper_kinematics;
|
||||||
struct serialqueue;
|
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||||
struct steppersync *steppersync_alloc(
|
, struct stepper_kinematics *sk);
|
||||||
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num
|
int32_t stepcompress_generate_steps(struct stepcompress *sc
|
||||||
, int move_num);
|
, double gen_steps_time
|
||||||
void steppersync_free(struct steppersync *ss);
|
, uint64_t flush_clock);
|
||||||
void steppersync_set_time(struct steppersync *ss, double time_offset
|
|
||||||
, double mcu_freq);
|
|
||||||
int steppersync_flush(struct steppersync *ss, uint64_t move_clock
|
|
||||||
, uint64_t clear_history_clock);
|
|
||||||
|
|
||||||
#endif // stepcompress.h
|
#endif // stepcompress.h
|
||||||
|
177
klippy/chelper/steppersync.c
Normal file
177
klippy/chelper/steppersync.c
Normal file
@ -0,0 +1,177 @@
|
|||||||
|
// Stepper step transmit synchronization
|
||||||
|
//
|
||||||
|
// Copyright (C) 2016-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
//
|
||||||
|
// This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
|
||||||
|
// The steppersync object is used to synchronize the output of mcu
|
||||||
|
// step commands. The mcu can only queue a limited number of step
|
||||||
|
// commands - this code tracks when items on the mcu step queue become
|
||||||
|
// free so that new commands can be transmitted. It also ensures the
|
||||||
|
// mcu step queue is ordered between steppers so that no stepper
|
||||||
|
// starves the other steppers of space in the mcu step queue.
|
||||||
|
|
||||||
|
#include <stddef.h> // offsetof
|
||||||
|
#include <stdlib.h> // malloc
|
||||||
|
#include <string.h> // memset
|
||||||
|
#include "compiler.h" // __visible
|
||||||
|
#include "serialqueue.h" // struct queue_message
|
||||||
|
#include "stepcompress.h" // stepcompress_flush
|
||||||
|
#include "steppersync.h" // steppersync_alloc
|
||||||
|
|
||||||
|
struct steppersync {
|
||||||
|
// Serial port
|
||||||
|
struct serialqueue *sq;
|
||||||
|
struct command_queue *cq;
|
||||||
|
// Storage for associated stepcompress objects
|
||||||
|
struct stepcompress **sc_list;
|
||||||
|
int sc_num;
|
||||||
|
// Storage for list of pending move clocks
|
||||||
|
uint64_t *move_clocks;
|
||||||
|
int num_move_clocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Allocate a new 'steppersync' object
|
||||||
|
struct steppersync * __visible
|
||||||
|
steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list
|
||||||
|
, int sc_num, int move_num)
|
||||||
|
{
|
||||||
|
struct steppersync *ss = malloc(sizeof(*ss));
|
||||||
|
memset(ss, 0, sizeof(*ss));
|
||||||
|
ss->sq = sq;
|
||||||
|
ss->cq = serialqueue_alloc_commandqueue();
|
||||||
|
|
||||||
|
ss->sc_list = malloc(sizeof(*sc_list)*sc_num);
|
||||||
|
memcpy(ss->sc_list, sc_list, sizeof(*sc_list)*sc_num);
|
||||||
|
ss->sc_num = sc_num;
|
||||||
|
|
||||||
|
ss->move_clocks = malloc(sizeof(*ss->move_clocks)*move_num);
|
||||||
|
memset(ss->move_clocks, 0, sizeof(*ss->move_clocks)*move_num);
|
||||||
|
ss->num_move_clocks = move_num;
|
||||||
|
|
||||||
|
return ss;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free memory associated with a 'steppersync' object
|
||||||
|
void __visible
|
||||||
|
steppersync_free(struct steppersync *ss)
|
||||||
|
{
|
||||||
|
if (!ss)
|
||||||
|
return;
|
||||||
|
free(ss->sc_list);
|
||||||
|
free(ss->move_clocks);
|
||||||
|
serialqueue_free_commandqueue(ss->cq);
|
||||||
|
free(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the conversion rate of 'print_time' to mcu clock
|
||||||
|
void __visible
|
||||||
|
steppersync_set_time(struct steppersync *ss, double time_offset
|
||||||
|
, double mcu_freq)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
stepcompress_set_time(sc, time_offset, mcu_freq);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate steps and flush stepcompress objects
|
||||||
|
int32_t __visible
|
||||||
|
steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
|
||||||
|
, uint64_t flush_clock)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
int32_t ret = stepcompress_generate_steps(sc, gen_steps_time
|
||||||
|
, flush_clock);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expire the stepcompress history before the given clock time
|
||||||
|
void __visible
|
||||||
|
steppersync_history_expire(struct steppersync *ss, uint64_t end_clock)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
stepcompress_history_expire(sc, end_clock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implement a binary heap algorithm to track when the next available
|
||||||
|
// 'struct move' in the mcu will be available
|
||||||
|
static void
|
||||||
|
heap_replace(struct steppersync *ss, uint64_t req_clock)
|
||||||
|
{
|
||||||
|
uint64_t *mc = ss->move_clocks;
|
||||||
|
int nmc = ss->num_move_clocks, pos = 0;
|
||||||
|
for (;;) {
|
||||||
|
int child1_pos = 2*pos+1, child2_pos = 2*pos+2;
|
||||||
|
uint64_t child2_clock = child2_pos < nmc ? mc[child2_pos] : UINT64_MAX;
|
||||||
|
uint64_t child1_clock = child1_pos < nmc ? mc[child1_pos] : UINT64_MAX;
|
||||||
|
if (req_clock <= child1_clock && req_clock <= child2_clock) {
|
||||||
|
mc[pos] = req_clock;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (child1_clock < child2_clock) {
|
||||||
|
mc[pos] = child1_clock;
|
||||||
|
pos = child1_pos;
|
||||||
|
} else {
|
||||||
|
mc[pos] = child2_clock;
|
||||||
|
pos = child2_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find and transmit any scheduled steps prior to the given 'move_clock'
|
||||||
|
int __visible
|
||||||
|
steppersync_flush(struct steppersync *ss, uint64_t move_clock)
|
||||||
|
{
|
||||||
|
// Order commands by the reqclock of each pending command
|
||||||
|
struct list_head msgs;
|
||||||
|
list_init(&msgs);
|
||||||
|
for (;;) {
|
||||||
|
// Find message with lowest reqclock
|
||||||
|
uint64_t req_clock = MAX_CLOCK;
|
||||||
|
struct queue_message *qm = NULL;
|
||||||
|
int i;
|
||||||
|
for (i=0; i<ss->sc_num; i++) {
|
||||||
|
struct stepcompress *sc = ss->sc_list[i];
|
||||||
|
struct list_head *sc_mq = stepcompress_get_msg_queue(sc);
|
||||||
|
if (!list_empty(sc_mq)) {
|
||||||
|
struct queue_message *m = list_first_entry(
|
||||||
|
sc_mq, struct queue_message, node);
|
||||||
|
if (m->req_clock < req_clock) {
|
||||||
|
qm = m;
|
||||||
|
req_clock = m->req_clock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!qm || (qm->min_clock && req_clock > move_clock))
|
||||||
|
break;
|
||||||
|
|
||||||
|
uint64_t next_avail = ss->move_clocks[0];
|
||||||
|
if (qm->min_clock)
|
||||||
|
// The qm->min_clock field is overloaded to indicate that
|
||||||
|
// the command uses the 'move queue' and to store the time
|
||||||
|
// that move queue item becomes available.
|
||||||
|
heap_replace(ss, qm->min_clock);
|
||||||
|
// Reset the min_clock to its normal meaning (minimum transmit time)
|
||||||
|
qm->min_clock = next_avail;
|
||||||
|
|
||||||
|
// Batch this command
|
||||||
|
list_del(&qm->node);
|
||||||
|
list_add_tail(&qm->node, &msgs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit commands
|
||||||
|
if (!list_empty(&msgs))
|
||||||
|
serialqueue_send_batch(ss->sq, ss->cq, &msgs);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
18
klippy/chelper/steppersync.h
Normal file
18
klippy/chelper/steppersync.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef STEPPERSYNC_H
|
||||||
|
#define STEPPERSYNC_H
|
||||||
|
|
||||||
|
#include <stdint.h> // uint64_t
|
||||||
|
|
||||||
|
struct serialqueue;
|
||||||
|
struct steppersync *steppersync_alloc(
|
||||||
|
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num
|
||||||
|
, int move_num);
|
||||||
|
void steppersync_free(struct steppersync *ss);
|
||||||
|
void steppersync_set_time(struct steppersync *ss, double time_offset
|
||||||
|
, double mcu_freq);
|
||||||
|
int32_t steppersync_generate_steps(struct steppersync *ss, double gen_steps_time
|
||||||
|
, uint64_t flush_clock);
|
||||||
|
void steppersync_history_expire(struct steppersync *ss, uint64_t end_clock);
|
||||||
|
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
|
||||||
|
|
||||||
|
#endif // steppersync.h
|
@ -33,10 +33,10 @@ class ForceMove:
|
|||||||
self.printer = config.get_printer()
|
self.printer = config.get_printer()
|
||||||
self.steppers = {}
|
self.steppers = {}
|
||||||
# Setup iterative solver
|
# Setup iterative solver
|
||||||
|
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||||
|
self.trapq = self.motion_queuing.allocate_trapq()
|
||||||
|
self.trapq_append = self.motion_queuing.lookup_trapq_append()
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
|
||||||
self.trapq_append = ffi_lib.trapq_append
|
|
||||||
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
|
||||||
self.stepper_kinematics = ffi_main.gc(
|
self.stepper_kinematics = ffi_main.gc(
|
||||||
ffi_lib.cartesian_stepper_alloc(b'x'), ffi_lib.free)
|
ffi_lib.cartesian_stepper_alloc(b'x'), ffi_lib.free)
|
||||||
# Register commands
|
# Register commands
|
||||||
@ -85,14 +85,12 @@ class ForceMove:
|
|||||||
self.trapq_append(self.trapq, print_time, accel_t, cruise_t, accel_t,
|
self.trapq_append(self.trapq, print_time, accel_t, cruise_t, accel_t,
|
||||||
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
|
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
|
||||||
print_time = print_time + accel_t + cruise_t + accel_t
|
print_time = print_time + accel_t + cruise_t + accel_t
|
||||||
stepper.generate_steps(print_time)
|
|
||||||
self.trapq_finalize_moves(self.trapq, print_time + 99999.9,
|
|
||||||
print_time + 99999.9)
|
|
||||||
stepper.set_trapq(prev_trapq)
|
|
||||||
stepper.set_stepper_kinematics(prev_sk)
|
|
||||||
toolhead.note_mcu_movequeue_activity(print_time)
|
toolhead.note_mcu_movequeue_activity(print_time)
|
||||||
toolhead.dwell(accel_t + cruise_t + accel_t)
|
toolhead.dwell(accel_t + cruise_t + accel_t)
|
||||||
toolhead.flush_step_generation()
|
toolhead.flush_step_generation()
|
||||||
|
stepper.set_trapq(prev_trapq)
|
||||||
|
stepper.set_stepper_kinematics(prev_sk)
|
||||||
|
self.motion_queuing.wipe_trapq(self.trapq)
|
||||||
def _lookup_stepper(self, gcmd):
|
def _lookup_stepper(self, gcmd):
|
||||||
name = gcmd.get('STEPPER')
|
name = gcmd.get('STEPPER')
|
||||||
if name not in self.steppers:
|
if name not in self.steppers:
|
||||||
|
@ -25,10 +25,9 @@ class ManualStepper:
|
|||||||
self.pos_min = config.getfloat('position_min', None)
|
self.pos_min = config.getfloat('position_min', None)
|
||||||
self.pos_max = config.getfloat('position_max', None)
|
self.pos_max = config.getfloat('position_max', None)
|
||||||
# Setup iterative solver
|
# Setup iterative solver
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||||
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
self.trapq = self.motion_queuing.allocate_trapq()
|
||||||
self.trapq_append = ffi_lib.trapq_append
|
self.trapq_append = self.motion_queuing.lookup_trapq_append()
|
||||||
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
|
||||||
self.rail.setup_itersolve('cartesian_stepper_alloc', b'x')
|
self.rail.setup_itersolve('cartesian_stepper_alloc', b'x')
|
||||||
self.rail.set_trapq(self.trapq)
|
self.rail.set_trapq(self.trapq)
|
||||||
# Registered with toolhead as an axtra axis
|
# Registered with toolhead as an axtra axis
|
||||||
@ -76,9 +75,6 @@ class ManualStepper:
|
|||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
||||||
speed, accel)
|
speed, accel)
|
||||||
self.rail.generate_steps(self.next_cmd_time)
|
|
||||||
self.trapq_finalize_moves(self.trapq, self.next_cmd_time + 99999.9,
|
|
||||||
self.next_cmd_time + 99999.9)
|
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
toolhead.note_mcu_movequeue_activity(self.next_cmd_time)
|
toolhead.note_mcu_movequeue_activity(self.next_cmd_time)
|
||||||
if sync:
|
if sync:
|
||||||
@ -138,7 +134,6 @@ class ManualStepper:
|
|||||||
raise gcmd.error("Must unregister axis first")
|
raise gcmd.error("Must unregister axis first")
|
||||||
# Unregister
|
# Unregister
|
||||||
toolhead.remove_extra_axis(self)
|
toolhead.remove_extra_axis(self)
|
||||||
toolhead.unregister_step_generator(self.rail.generate_steps)
|
|
||||||
self.axis_gcode_id = None
|
self.axis_gcode_id = None
|
||||||
return
|
return
|
||||||
if (len(gcode_axis) != 1 or not gcode_axis.isupper()
|
if (len(gcode_axis) != 1 or not gcode_axis.isupper()
|
||||||
@ -155,7 +150,6 @@ class ManualStepper:
|
|||||||
self.gaxis_limit_velocity = limit_velocity
|
self.gaxis_limit_velocity = limit_velocity
|
||||||
self.gaxis_limit_accel = limit_accel
|
self.gaxis_limit_accel = limit_accel
|
||||||
toolhead.add_extra_axis(self, self.get_position()[0])
|
toolhead.add_extra_axis(self, self.get_position()[0])
|
||||||
toolhead.register_step_generator(self.rail.generate_steps)
|
|
||||||
def process_move(self, print_time, move, ea_index):
|
def process_move(self, print_time, move, ea_index):
|
||||||
axis_r = move.axes_r[ea_index]
|
axis_r = move.axes_r[ea_index]
|
||||||
start_pos = move.start_pos[ea_index]
|
start_pos = move.start_pos[ea_index]
|
||||||
@ -208,10 +202,10 @@ class ManualStepper:
|
|||||||
speed, self.homing_accel)
|
speed, self.homing_accel)
|
||||||
# Drip updates to motors
|
# Drip updates to motors
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
toolhead = self.printer.lookup_object('toolhead')
|
||||||
toolhead.drip_update_time(maxtime, drip_completion, self.steppers)
|
toolhead.drip_update_time(maxtime, drip_completion)
|
||||||
# Clear trapq of any remaining parts of movement
|
# Clear trapq of any remaining parts of movement
|
||||||
reactor = self.printer.get_reactor()
|
reactor = self.printer.get_reactor()
|
||||||
self.trapq_finalize_moves(self.trapq, reactor.NEVER, 0)
|
self.motion_queuing.wipe_trapq(self.trapq)
|
||||||
self.rail.set_position([newpos[0], 0., 0.])
|
self.rail.set_position([newpos[0], 0., 0.])
|
||||||
self.sync_print_time()
|
self.sync_print_time()
|
||||||
def get_kinematics(self):
|
def get_kinematics(self):
|
||||||
|
100
klippy/extras/motion_queuing.py
Normal file
100
klippy/extras/motion_queuing.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
# Helper code for low-level motion queuing and flushing
|
||||||
|
#
|
||||||
|
# Copyright (C) 2025 Kevin O'Connor <kevin@koconnor.net>
|
||||||
|
#
|
||||||
|
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||||
|
import logging
|
||||||
|
import chelper
|
||||||
|
|
||||||
|
MOVE_HISTORY_EXPIRE = 30.
|
||||||
|
|
||||||
|
class PrinterMotionQueuing:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.trapqs = []
|
||||||
|
self.stepcompress = []
|
||||||
|
self.steppersyncs = []
|
||||||
|
self.flush_callbacks = []
|
||||||
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
|
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
||||||
|
self.steppersync_generate_steps = ffi_lib.steppersync_generate_steps
|
||||||
|
self.steppersync_flush = ffi_lib.steppersync_flush
|
||||||
|
self.steppersync_history_expire = ffi_lib.steppersync_history_expire
|
||||||
|
self.clear_history_time = 0.
|
||||||
|
is_debug = self.printer.get_start_args().get('debugoutput') is not None
|
||||||
|
self.is_debugoutput = is_debug
|
||||||
|
def allocate_trapq(self):
|
||||||
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
|
trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
||||||
|
self.trapqs.append(trapq)
|
||||||
|
return trapq
|
||||||
|
def allocate_stepcompress(self, mcu, oid):
|
||||||
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
|
sc = ffi_main.gc(ffi_lib.stepcompress_alloc(oid),
|
||||||
|
ffi_lib.stepcompress_free)
|
||||||
|
self.stepcompress.append((mcu, sc))
|
||||||
|
return sc
|
||||||
|
def allocate_steppersync(self, mcu, serialqueue, move_count):
|
||||||
|
stepqueues = []
|
||||||
|
for sc_mcu, sc in self.stepcompress:
|
||||||
|
if sc_mcu is mcu:
|
||||||
|
stepqueues.append(sc)
|
||||||
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
|
ss = ffi_main.gc(
|
||||||
|
ffi_lib.steppersync_alloc(serialqueue, stepqueues, len(stepqueues),
|
||||||
|
move_count),
|
||||||
|
ffi_lib.steppersync_free)
|
||||||
|
self.steppersyncs.append((mcu, ss))
|
||||||
|
return ss
|
||||||
|
def register_flush_callback(self, callback):
|
||||||
|
self.flush_callbacks.append(callback)
|
||||||
|
def unregister_flush_callback(self, callback):
|
||||||
|
if callback in self.flush_callbacks:
|
||||||
|
fcbs = list(self.flush_callbacks)
|
||||||
|
fcbs.remove(callback)
|
||||||
|
self.flush_callbacks = fcbs
|
||||||
|
def flush_motion_queues(self, must_flush_time, max_step_gen_time,
|
||||||
|
trapq_free_time):
|
||||||
|
# Invoke flush callbacks (if any)
|
||||||
|
for cb in self.flush_callbacks:
|
||||||
|
cb(must_flush_time, max_step_gen_time)
|
||||||
|
# Generate stepper movement and transmit
|
||||||
|
for mcu, ss in self.steppersyncs:
|
||||||
|
clock = max(0, mcu.print_time_to_clock(must_flush_time))
|
||||||
|
# Generate steps
|
||||||
|
ret = self.steppersync_generate_steps(ss, max_step_gen_time, clock)
|
||||||
|
if ret:
|
||||||
|
raise mcu.error("Internal error in MCU '%s' stepcompress"
|
||||||
|
% (mcu.get_name(),))
|
||||||
|
# Flush steps from steppersync
|
||||||
|
ret = self.steppersync_flush(ss, clock)
|
||||||
|
if ret:
|
||||||
|
raise mcu.error("Internal error in MCU '%s' stepcompress"
|
||||||
|
% (mcu.get_name(),))
|
||||||
|
# Determine maximum history to keep
|
||||||
|
clear_history_time = self.clear_history_time
|
||||||
|
if self.is_debugoutput:
|
||||||
|
clear_history_time = trapq_free_time - MOVE_HISTORY_EXPIRE
|
||||||
|
# Move processed trapq moves to history list, and expire old history
|
||||||
|
for trapq in self.trapqs:
|
||||||
|
self.trapq_finalize_moves(trapq, trapq_free_time,
|
||||||
|
clear_history_time)
|
||||||
|
# Clean up old history entries in stepcompress objects
|
||||||
|
for mcu, ss in self.steppersyncs:
|
||||||
|
clock = max(0, mcu.print_time_to_clock(clear_history_time))
|
||||||
|
self.steppersync_history_expire(ss, clock)
|
||||||
|
def wipe_trapq(self, trapq):
|
||||||
|
# Expire any remaining movement in the trapq (force to history list)
|
||||||
|
NEVER = 9999999999999999.
|
||||||
|
self.trapq_finalize_moves(trapq, NEVER, 0.)
|
||||||
|
def lookup_trapq_append(self):
|
||||||
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
|
return ffi_lib.trapq_append
|
||||||
|
def stats(self, eventtime):
|
||||||
|
mcu = self.printer.lookup_object('mcu')
|
||||||
|
est_print_time = mcu.estimated_print_time(eventtime)
|
||||||
|
self.clear_history_time = est_print_time - MOVE_HISTORY_EXPIRE
|
||||||
|
return False, ""
|
||||||
|
|
||||||
|
def load_config(config):
|
||||||
|
return PrinterMotionQueuing(config)
|
@ -20,16 +20,17 @@ class GCodeRequestQueue:
|
|||||||
self.rqueue = []
|
self.rqueue = []
|
||||||
self.next_min_flush_time = 0.
|
self.next_min_flush_time = 0.
|
||||||
self.toolhead = None
|
self.toolhead = None
|
||||||
mcu.register_flush_callback(self._flush_notification)
|
motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||||
|
motion_queuing.register_flush_callback(self._flush_notification)
|
||||||
printer.register_event_handler("klippy:connect", self._handle_connect)
|
printer.register_event_handler("klippy:connect", self._handle_connect)
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
self.toolhead = self.printer.lookup_object('toolhead')
|
self.toolhead = self.printer.lookup_object('toolhead')
|
||||||
def _flush_notification(self, print_time, clock):
|
def _flush_notification(self, must_flush_time, max_step_gen_time):
|
||||||
min_sched_time = self.mcu.min_schedule_time()
|
min_sched_time = self.mcu.min_schedule_time()
|
||||||
rqueue = self.rqueue
|
rqueue = self.rqueue
|
||||||
while rqueue:
|
while rqueue:
|
||||||
next_time = max(rqueue[0][0], self.next_min_flush_time)
|
next_time = max(rqueue[0][0], self.next_min_flush_time)
|
||||||
if next_time > print_time:
|
if next_time > must_flush_time:
|
||||||
return
|
return
|
||||||
# Skip requests that have been overridden with a following request
|
# Skip requests that have been overridden with a following request
|
||||||
pos = 0
|
pos = 0
|
||||||
@ -50,10 +51,11 @@ class GCodeRequestQueue:
|
|||||||
del rqueue[:pos+1]
|
del rqueue[:pos+1]
|
||||||
self.next_min_flush_time = next_time + max(min_wait, min_sched_time)
|
self.next_min_flush_time = next_time + max(min_wait, min_sched_time)
|
||||||
# Ensure following queue items are flushed
|
# Ensure following queue items are flushed
|
||||||
self.toolhead.note_mcu_movequeue_activity(self.next_min_flush_time)
|
self.toolhead.note_mcu_movequeue_activity(self.next_min_flush_time,
|
||||||
|
is_step_gen=False)
|
||||||
def _queue_request(self, print_time, value):
|
def _queue_request(self, print_time, value):
|
||||||
self.rqueue.append((print_time, value))
|
self.rqueue.append((print_time, value))
|
||||||
self.toolhead.note_mcu_movequeue_activity(print_time)
|
self.toolhead.note_mcu_movequeue_activity(print_time, is_step_gen=False)
|
||||||
def queue_gcode_request(self, value):
|
def queue_gcode_request(self, value):
|
||||||
self.toolhead.register_lookahead_callback(
|
self.toolhead.register_lookahead_callback(
|
||||||
(lambda pt: self._queue_request(pt, value)))
|
(lambda pt: self._queue_request(pt, value)))
|
||||||
|
@ -9,18 +9,18 @@ class error(Exception):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
class MCU_queued_pwm:
|
class MCU_queued_pwm:
|
||||||
def __init__(self, pin_params):
|
def __init__(self, config, pin_params):
|
||||||
self._mcu = pin_params['chip']
|
self._mcu = mcu = pin_params['chip']
|
||||||
self._hardware_pwm = False
|
self._hardware_pwm = False
|
||||||
self._cycle_time = 0.100
|
self._cycle_time = 0.100
|
||||||
self._max_duration = 2.
|
self._max_duration = 2.
|
||||||
self._oid = self._mcu.create_oid()
|
self._oid = oid = mcu.create_oid()
|
||||||
|
printer = mcu.get_printer()
|
||||||
|
motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||||
|
self._stepqueue = motion_queuing.allocate_stepcompress(mcu, oid)
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
self._stepqueue = ffi_main.gc(ffi_lib.stepcompress_alloc(self._oid),
|
|
||||||
ffi_lib.stepcompress_free)
|
|
||||||
self._mcu.register_stepqueue(self._stepqueue)
|
|
||||||
self._stepcompress_queue_mq_msg = ffi_lib.stepcompress_queue_mq_msg
|
self._stepcompress_queue_mq_msg = ffi_lib.stepcompress_queue_mq_msg
|
||||||
self._mcu.register_config_callback(self._build_config)
|
mcu.register_config_callback(self._build_config)
|
||||||
self._pin = pin_params['pin']
|
self._pin = pin_params['pin']
|
||||||
self._invert = pin_params['invert']
|
self._invert = pin_params['invert']
|
||||||
self._start_value = self._shutdown_value = float(self._invert)
|
self._start_value = self._shutdown_value = float(self._invert)
|
||||||
@ -29,7 +29,6 @@ class MCU_queued_pwm:
|
|||||||
self._pwm_max = 0.
|
self._pwm_max = 0.
|
||||||
self._set_cmd_tag = None
|
self._set_cmd_tag = None
|
||||||
self._toolhead = None
|
self._toolhead = None
|
||||||
printer = self._mcu.get_printer()
|
|
||||||
printer.register_event_handler("klippy:connect", self._handle_connect)
|
printer.register_event_handler("klippy:connect", self._handle_connect)
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
self._toolhead = self._mcu.get_printer().lookup_object("toolhead")
|
self._toolhead = self._mcu.get_printer().lookup_object("toolhead")
|
||||||
@ -47,12 +46,13 @@ class MCU_queued_pwm:
|
|||||||
self._start_value = max(0., min(1., start_value))
|
self._start_value = max(0., min(1., start_value))
|
||||||
self._shutdown_value = max(0., min(1., shutdown_value))
|
self._shutdown_value = max(0., min(1., shutdown_value))
|
||||||
def _build_config(self):
|
def _build_config(self):
|
||||||
config_error = self._mcu.get_printer().config_error
|
printer = self._mcu.get_printer()
|
||||||
|
config_error = printer.config_error
|
||||||
if self._max_duration and self._start_value != self._shutdown_value:
|
if self._max_duration and self._start_value != self._shutdown_value:
|
||||||
raise config_error("Pin with max duration must have start"
|
raise config_error("Pin with max duration must have start"
|
||||||
" value equal to shutdown value")
|
" value equal to shutdown value")
|
||||||
cmd_queue = self._mcu.alloc_command_queue()
|
cmd_queue = self._mcu.alloc_command_queue()
|
||||||
curtime = self._mcu.get_printer().get_reactor().monotonic()
|
curtime = printer.get_reactor().monotonic()
|
||||||
printtime = self._mcu.estimated_print_time(curtime)
|
printtime = self._mcu.estimated_print_time(curtime)
|
||||||
self._last_clock = self._mcu.print_time_to_clock(printtime + 0.200)
|
self._last_clock = self._mcu.print_time_to_clock(printtime + 0.200)
|
||||||
cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
|
cycle_ticks = self._mcu.seconds_to_clock(self._cycle_time)
|
||||||
@ -62,7 +62,8 @@ class MCU_queued_pwm:
|
|||||||
if self._duration_ticks >= 1<<31:
|
if self._duration_ticks >= 1<<31:
|
||||||
raise config_error("PWM pin max duration too large")
|
raise config_error("PWM pin max duration too large")
|
||||||
if self._duration_ticks:
|
if self._duration_ticks:
|
||||||
self._mcu.register_flush_callback(self._flush_notification)
|
motion_queuing = printer.lookup_object('motion_queuing')
|
||||||
|
motion_queuing.register_flush_callback(self._flush_notification)
|
||||||
if self._hardware_pwm:
|
if self._hardware_pwm:
|
||||||
self._pwm_max = self._mcu.get_constant_float("PWM_MAX")
|
self._pwm_max = self._mcu.get_constant_float("PWM_MAX")
|
||||||
self._default_value = self._shutdown_value * self._pwm_max
|
self._default_value = self._shutdown_value * self._pwm_max
|
||||||
@ -115,14 +116,16 @@ class MCU_queued_pwm:
|
|||||||
# Continue flushing to resend time
|
# Continue flushing to resend time
|
||||||
wakeclock += self._duration_ticks
|
wakeclock += self._duration_ticks
|
||||||
wake_print_time = self._mcu.clock_to_print_time(wakeclock)
|
wake_print_time = self._mcu.clock_to_print_time(wakeclock)
|
||||||
self._toolhead.note_mcu_movequeue_activity(wake_print_time)
|
self._toolhead.note_mcu_movequeue_activity(wake_print_time,
|
||||||
|
is_step_gen=False)
|
||||||
def set_pwm(self, print_time, value):
|
def set_pwm(self, print_time, value):
|
||||||
clock = self._mcu.print_time_to_clock(print_time)
|
clock = self._mcu.print_time_to_clock(print_time)
|
||||||
if self._invert:
|
if self._invert:
|
||||||
value = 1. - value
|
value = 1. - value
|
||||||
v = int(max(0., min(1., value)) * self._pwm_max + 0.5)
|
v = int(max(0., min(1., value)) * self._pwm_max + 0.5)
|
||||||
self._send_update(clock, v)
|
self._send_update(clock, v)
|
||||||
def _flush_notification(self, print_time, clock):
|
def _flush_notification(self, must_flush_time, max_step_gen_time):
|
||||||
|
clock = self._mcu.print_time_to_clock(must_flush_time)
|
||||||
if self._last_value != self._default_value:
|
if self._last_value != self._default_value:
|
||||||
while clock >= self._last_clock + self._duration_ticks:
|
while clock >= self._last_clock + self._duration_ticks:
|
||||||
self._send_update(self._last_clock + self._duration_ticks,
|
self._send_update(self._last_clock + self._duration_ticks,
|
||||||
@ -134,7 +137,7 @@ class PrinterOutputPin:
|
|||||||
ppins = self.printer.lookup_object('pins')
|
ppins = self.printer.lookup_object('pins')
|
||||||
# Determine pin type
|
# Determine pin type
|
||||||
pin_params = ppins.lookup_pin(config.get('pin'), can_invert=True)
|
pin_params = ppins.lookup_pin(config.get('pin'), can_invert=True)
|
||||||
self.mcu_pin = MCU_queued_pwm(pin_params)
|
self.mcu_pin = MCU_queued_pwm(config, pin_params)
|
||||||
max_duration = self.mcu_pin.get_mcu().max_nominal_duration()
|
max_duration = self.mcu_pin.get_mcu().max_nominal_duration()
|
||||||
cycle_time = config.getfloat('cycle_time', 0.100, above=0.,
|
cycle_time = config.getfloat('cycle_time', 0.100, above=0.,
|
||||||
maxval=max_duration)
|
maxval=max_duration)
|
||||||
|
@ -148,8 +148,7 @@ class ResonanceTestExecutor:
|
|||||||
last_v = last_t = last_accel = last_freq = 0.
|
last_v = last_t = last_accel = last_freq = 0.
|
||||||
for next_t, accel, freq in test_seq:
|
for next_t, accel, freq in test_seq:
|
||||||
t_seg = next_t - last_t
|
t_seg = next_t - last_t
|
||||||
toolhead.cmd_M204(self.gcode.create_gcode_command(
|
toolhead.set_max_velocities(None, abs(accel), None, None)
|
||||||
"M204", "M204", {"S": abs(accel)}))
|
|
||||||
v = last_v + accel * t_seg
|
v = last_v + accel * t_seg
|
||||||
abs_v = abs(v)
|
abs_v = abs(v)
|
||||||
if abs_v < 0.000001:
|
if abs_v < 0.000001:
|
||||||
@ -182,8 +181,7 @@ class ResonanceTestExecutor:
|
|||||||
if last_v:
|
if last_v:
|
||||||
d_decel = -.5 * last_v2 / old_max_accel
|
d_decel = -.5 * last_v2 / old_max_accel
|
||||||
decel_X, decel_Y = axis.get_point(d_decel)
|
decel_X, decel_Y = axis.get_point(d_decel)
|
||||||
toolhead.cmd_M204(self.gcode.create_gcode_command(
|
toolhead.set_max_velocities(None, old_max_accel, None, None)
|
||||||
"M204", "M204", {"S": old_max_accel}))
|
|
||||||
toolhead.move([X + decel_X, Y + decel_Y] + tpos[2:], abs(last_v))
|
toolhead.move([X + decel_X, Y + decel_Y] + tpos[2:], abs(last_v))
|
||||||
# Restore the original acceleration values
|
# Restore the original acceleration values
|
||||||
self.gcode.run_script_from_command(
|
self.gcode.run_script_from_command(
|
||||||
|
@ -65,8 +65,8 @@ class PrinterStats:
|
|||||||
def generate_stats(self, eventtime):
|
def generate_stats(self, eventtime):
|
||||||
stats = [cb(eventtime) for cb in self.stats_cb]
|
stats = [cb(eventtime) for cb in self.stats_cb]
|
||||||
if max([s[0] for s in stats]):
|
if max([s[0] for s in stats]):
|
||||||
logging.info("Stats %.1f: %s", eventtime,
|
stats_str = ' '.join([s[1] for s in stats if s[1]])
|
||||||
' '.join([s[1] for s in stats]))
|
logging.info("Stats %.1f: %s", eventtime, stats_str)
|
||||||
return eventtime + 1.
|
return eventtime + 1.
|
||||||
|
|
||||||
def load_config(config):
|
def load_config(config):
|
||||||
|
@ -36,7 +36,6 @@ class CartKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
|
self.max_z_velocity = config.getfloat('max_z_velocity', max_velocity,
|
||||||
|
@ -20,7 +20,6 @@ class CoreXYKinematics:
|
|||||||
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
|
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'z')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -20,7 +20,6 @@ class CoreXZKinematics:
|
|||||||
self.rails[2].setup_itersolve('corexz_stepper_alloc', b'-')
|
self.rails[2].setup_itersolve('corexz_stepper_alloc', b'-')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -52,7 +52,6 @@ class DeltaKinematics:
|
|||||||
r.setup_itersolve('delta_stepper_alloc', a, t[0], t[1])
|
r.setup_itersolve('delta_stepper_alloc', a, t[0], t[1])
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
self.need_home = True
|
self.need_home = True
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
|
@ -40,7 +40,6 @@ class DeltesianKinematics:
|
|||||||
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'y')
|
self.rails[2].setup_itersolve('cartesian_stepper_alloc', b'y')
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
self.limits = [(1.0, -1.0)] * 3
|
self.limits = [(1.0, -1.0)] * 3
|
||||||
# X axis limits
|
# X axis limits
|
||||||
min_angle = config.getfloat('min_angle', MIN_ANGLE,
|
min_angle = config.getfloat('min_angle', MIN_ANGLE,
|
||||||
|
@ -39,8 +39,6 @@ class ExtruderStepper:
|
|||||||
self.name, self.cmd_SYNC_EXTRUDER_MOTION,
|
self.name, self.cmd_SYNC_EXTRUDER_MOTION,
|
||||||
desc=self.cmd_SYNC_EXTRUDER_MOTION_help)
|
desc=self.cmd_SYNC_EXTRUDER_MOTION_help)
|
||||||
def _handle_connect(self):
|
def _handle_connect(self):
|
||||||
toolhead = self.printer.lookup_object('toolhead')
|
|
||||||
toolhead.register_step_generator(self.stepper.generate_steps)
|
|
||||||
self._set_pressure_advance(self.config_pa, self.config_smooth_time)
|
self._set_pressure_advance(self.config_pa, self.config_smooth_time)
|
||||||
def get_status(self, eventtime):
|
def get_status(self, eventtime):
|
||||||
return {'pressure_advance': self.pressure_advance,
|
return {'pressure_advance': self.pressure_advance,
|
||||||
@ -165,10 +163,9 @@ class PrinterExtruder:
|
|||||||
self.instant_corner_v = config.getfloat(
|
self.instant_corner_v = config.getfloat(
|
||||||
'instantaneous_corner_velocity', 1., minval=0.)
|
'instantaneous_corner_velocity', 1., minval=0.)
|
||||||
# Setup extruder trapq (trapezoidal motion queue)
|
# Setup extruder trapq (trapezoidal motion queue)
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||||
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
self.trapq = self.motion_queuing.allocate_trapq()
|
||||||
self.trapq_append = ffi_lib.trapq_append
|
self.trapq_append = self.motion_queuing.lookup_trapq_append()
|
||||||
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
|
||||||
# Setup extruder stepper
|
# Setup extruder stepper
|
||||||
self.extruder_stepper = None
|
self.extruder_stepper = None
|
||||||
if (config.get('step_pin', None) is not None
|
if (config.get('step_pin', None) is not None
|
||||||
|
@ -108,7 +108,6 @@ class GenericCartesianKinematics:
|
|||||||
self._load_kinematics(config)
|
self._load_kinematics(config)
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
self.dc_module = None
|
self.dc_module = None
|
||||||
if self.dc_carriages:
|
if self.dc_carriages:
|
||||||
pcs = [dc.get_dual_carriage() for dc in self.dc_carriages]
|
pcs = [dc.get_dual_carriage() for dc in self.dc_carriages]
|
||||||
|
@ -39,7 +39,6 @@ class HybridCoreXYKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -39,7 +39,6 @@ class HybridCoreXZKinematics:
|
|||||||
'safe_distance', None, minval=0.))
|
'safe_distance', None, minval=0.))
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -21,7 +21,6 @@ class PolarKinematics:
|
|||||||
for s in r.get_steppers() ]
|
for s in r.get_steppers() ]
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
max_velocity, max_accel = toolhead.get_max_velocity()
|
max_velocity, max_accel = toolhead.get_max_velocity()
|
||||||
self.max_z_velocity = config.getfloat(
|
self.max_z_velocity = config.getfloat(
|
||||||
|
@ -52,7 +52,6 @@ class RotaryDeltaKinematics:
|
|||||||
math.radians(a), ua, la)
|
math.radians(a), ua, la)
|
||||||
for s in self.get_steppers():
|
for s in self.get_steppers():
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
self.need_home = True
|
self.need_home = True
|
||||||
self.limit_xy2 = -1.
|
self.limit_xy2 = -1.
|
||||||
|
@ -21,7 +21,6 @@ class WinchKinematics:
|
|||||||
self.anchors.append(a)
|
self.anchors.append(a)
|
||||||
s.setup_itersolve('winch_stepper_alloc', *a)
|
s.setup_itersolve('winch_stepper_alloc', *a)
|
||||||
s.set_trapq(toolhead.get_trapq())
|
s.set_trapq(toolhead.get_trapq())
|
||||||
toolhead.register_step_generator(s.generate_steps)
|
|
||||||
# Setup boundary checks
|
# Setup boundary checks
|
||||||
acoords = list(zip(*self.anchors))
|
acoords = list(zip(*self.anchors))
|
||||||
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
|
self.axes_min = toolhead.Coord(*[min(a) for a in acoords], e=0.)
|
||||||
|
@ -605,9 +605,7 @@ class MCU:
|
|||||||
self._max_stepper_error = config.getfloat('max_stepper_error', 0.000025,
|
self._max_stepper_error = config.getfloat('max_stepper_error', 0.000025,
|
||||||
minval=0.)
|
minval=0.)
|
||||||
self._reserved_move_slots = 0
|
self._reserved_move_slots = 0
|
||||||
self._stepqueues = []
|
|
||||||
self._steppersync = None
|
self._steppersync = None
|
||||||
self._flush_callbacks = []
|
|
||||||
# Stats
|
# Stats
|
||||||
self._get_status_info = {}
|
self._get_status_info = {}
|
||||||
self._stats_sumsq_base = 0.
|
self._stats_sumsq_base = 0.
|
||||||
@ -770,13 +768,12 @@ class MCU:
|
|||||||
move_count = config_params['move_count']
|
move_count = config_params['move_count']
|
||||||
if move_count < self._reserved_move_slots:
|
if move_count < self._reserved_move_slots:
|
||||||
raise error("Too few moves available on MCU '%s'" % (self._name,))
|
raise error("Too few moves available on MCU '%s'" % (self._name,))
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ss_move_count = move_count - self._reserved_move_slots
|
||||||
self._steppersync = ffi_main.gc(
|
motion_queuing = self._printer.lookup_object('motion_queuing')
|
||||||
ffi_lib.steppersync_alloc(self._serial.get_serialqueue(),
|
self._steppersync = motion_queuing.allocate_steppersync(
|
||||||
self._stepqueues, len(self._stepqueues),
|
self, self._serial.get_serialqueue(), ss_move_count)
|
||||||
move_count-self._reserved_move_slots),
|
self._ffi_lib.steppersync_set_time(self._steppersync,
|
||||||
ffi_lib.steppersync_free)
|
0., self._mcu_freq)
|
||||||
ffi_lib.steppersync_set_time(self._steppersync, 0., self._mcu_freq)
|
|
||||||
# Log config information
|
# Log config information
|
||||||
move_msg = "Configured MCU '%s' (%d moves)" % (self._name, move_count)
|
move_msg = "Configured MCU '%s' (%d moves)" % (self._name, move_count)
|
||||||
logging.info(move_msg)
|
logging.info(move_msg)
|
||||||
@ -971,27 +968,8 @@ class MCU:
|
|||||||
def _firmware_restart_bridge(self):
|
def _firmware_restart_bridge(self):
|
||||||
self._firmware_restart(True)
|
self._firmware_restart(True)
|
||||||
# Move queue tracking
|
# Move queue tracking
|
||||||
def register_stepqueue(self, stepqueue):
|
|
||||||
self._stepqueues.append(stepqueue)
|
|
||||||
def request_move_queue_slot(self):
|
def request_move_queue_slot(self):
|
||||||
self._reserved_move_slots += 1
|
self._reserved_move_slots += 1
|
||||||
def register_flush_callback(self, callback):
|
|
||||||
self._flush_callbacks.append(callback)
|
|
||||||
def flush_moves(self, print_time, clear_history_time):
|
|
||||||
if self._steppersync is None:
|
|
||||||
return
|
|
||||||
clock = self.print_time_to_clock(print_time)
|
|
||||||
if clock < 0:
|
|
||||||
return
|
|
||||||
for cb in self._flush_callbacks:
|
|
||||||
cb(print_time, clock)
|
|
||||||
clear_history_clock = \
|
|
||||||
max(0, self.print_time_to_clock(clear_history_time))
|
|
||||||
ret = self._ffi_lib.steppersync_flush(self._steppersync, clock,
|
|
||||||
clear_history_clock)
|
|
||||||
if ret:
|
|
||||||
raise error("Internal error in MCU '%s' stepcompress"
|
|
||||||
% (self._name,))
|
|
||||||
def check_active(self, print_time, eventtime):
|
def check_active(self, print_time, eventtime):
|
||||||
if self._steppersync is None:
|
if self._steppersync is None:
|
||||||
return
|
return
|
||||||
|
@ -19,22 +19,23 @@ MIN_OPTIMIZED_BOTH_EDGE_DURATION = 0.000000150
|
|||||||
|
|
||||||
# Interface to low-level mcu and chelper code
|
# Interface to low-level mcu and chelper code
|
||||||
class MCU_stepper:
|
class MCU_stepper:
|
||||||
def __init__(self, name, step_pin_params, dir_pin_params,
|
def __init__(self, config, step_pin_params, dir_pin_params,
|
||||||
rotation_dist, steps_per_rotation,
|
rotation_dist, steps_per_rotation,
|
||||||
step_pulse_duration=None, units_in_radians=False):
|
step_pulse_duration=None, units_in_radians=False):
|
||||||
self._name = name
|
self._name = config.get_name()
|
||||||
self._rotation_dist = rotation_dist
|
self._rotation_dist = rotation_dist
|
||||||
self._steps_per_rotation = steps_per_rotation
|
self._steps_per_rotation = steps_per_rotation
|
||||||
self._step_pulse_duration = step_pulse_duration
|
self._step_pulse_duration = step_pulse_duration
|
||||||
self._units_in_radians = units_in_radians
|
self._units_in_radians = units_in_radians
|
||||||
self._step_dist = rotation_dist / steps_per_rotation
|
self._step_dist = rotation_dist / steps_per_rotation
|
||||||
self._mcu = step_pin_params['chip']
|
self._mcu = mcu = step_pin_params['chip']
|
||||||
self._oid = oid = self._mcu.create_oid()
|
self._oid = oid = mcu.create_oid()
|
||||||
self._mcu.register_config_callback(self._build_config)
|
mcu.register_config_callback(self._build_config)
|
||||||
self._step_pin = step_pin_params['pin']
|
self._step_pin = step_pin_params['pin']
|
||||||
self._invert_step = step_pin_params['invert']
|
self._invert_step = step_pin_params['invert']
|
||||||
if dir_pin_params['chip'] is not self._mcu:
|
printer = mcu.get_printer()
|
||||||
raise self._mcu.get_printer().config_error(
|
if dir_pin_params['chip'] is not mcu:
|
||||||
|
raise printer.config_error(
|
||||||
"Stepper dir pin must be on same mcu as step pin")
|
"Stepper dir pin must be on same mcu as step pin")
|
||||||
self._dir_pin = dir_pin_params['pin']
|
self._dir_pin = dir_pin_params['pin']
|
||||||
self._invert_dir = self._orig_invert_dir = dir_pin_params['invert']
|
self._invert_dir = self._orig_invert_dir = dir_pin_params['invert']
|
||||||
@ -42,17 +43,15 @@ class MCU_stepper:
|
|||||||
self._mcu_position_offset = 0.
|
self._mcu_position_offset = 0.
|
||||||
self._reset_cmd_tag = self._get_position_cmd = None
|
self._reset_cmd_tag = self._get_position_cmd = None
|
||||||
self._active_callbacks = []
|
self._active_callbacks = []
|
||||||
|
motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||||
|
self._stepqueue = motion_queuing.allocate_stepcompress(mcu, oid)
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
self._stepqueue = ffi_main.gc(ffi_lib.stepcompress_alloc(oid),
|
|
||||||
ffi_lib.stepcompress_free)
|
|
||||||
ffi_lib.stepcompress_set_invert_sdir(self._stepqueue, self._invert_dir)
|
ffi_lib.stepcompress_set_invert_sdir(self._stepqueue, self._invert_dir)
|
||||||
self._mcu.register_stepqueue(self._stepqueue)
|
|
||||||
self._stepper_kinematics = None
|
self._stepper_kinematics = None
|
||||||
self._itersolve_generate_steps = ffi_lib.itersolve_generate_steps
|
|
||||||
self._itersolve_check_active = ffi_lib.itersolve_check_active
|
self._itersolve_check_active = ffi_lib.itersolve_check_active
|
||||||
self._trapq = ffi_main.NULL
|
self._trapq = ffi_main.NULL
|
||||||
self._mcu.get_printer().register_event_handler('klippy:connect',
|
printer.register_event_handler('klippy:connect',
|
||||||
self._query_mcu_position)
|
self._query_mcu_position)
|
||||||
def get_mcu(self):
|
def get_mcu(self):
|
||||||
return self._mcu
|
return self._mcu
|
||||||
def get_name(self, short=False):
|
def get_name(self, short=False):
|
||||||
@ -135,7 +134,7 @@ class MCU_stepper:
|
|||||||
mcu_pos = self.get_mcu_position()
|
mcu_pos = self.get_mcu_position()
|
||||||
self._rotation_dist = rotation_dist
|
self._rotation_dist = rotation_dist
|
||||||
self._step_dist = rotation_dist / self._steps_per_rotation
|
self._step_dist = rotation_dist / self._steps_per_rotation
|
||||||
self.set_stepper_kinematics(self._stepper_kinematics)
|
self.set_trapq(self._trapq)
|
||||||
self._set_mcu_position(mcu_pos)
|
self._set_mcu_position(mcu_pos)
|
||||||
def get_dir_inverted(self):
|
def get_dir_inverted(self):
|
||||||
return self._invert_dir, self._orig_invert_dir
|
return self._invert_dir, self._orig_invert_dir
|
||||||
@ -193,7 +192,7 @@ class MCU_stepper:
|
|||||||
mcu_pos = self.get_mcu_position()
|
mcu_pos = self.get_mcu_position()
|
||||||
self._stepper_kinematics = sk
|
self._stepper_kinematics = sk
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
ffi_lib.itersolve_set_stepcompress(sk, self._stepqueue, self._step_dist)
|
ffi_lib.stepcompress_set_stepper_kinematics(self._stepqueue, sk);
|
||||||
self.set_trapq(self._trapq)
|
self.set_trapq(self._trapq)
|
||||||
self._set_mcu_position(mcu_pos)
|
self._set_mcu_position(mcu_pos)
|
||||||
return old_sk
|
return old_sk
|
||||||
@ -229,27 +228,32 @@ class MCU_stepper:
|
|||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
if tq is None:
|
if tq is None:
|
||||||
tq = ffi_main.NULL
|
tq = ffi_main.NULL
|
||||||
ffi_lib.itersolve_set_trapq(self._stepper_kinematics, tq)
|
ffi_lib.itersolve_set_trapq(self._stepper_kinematics,
|
||||||
|
tq, self._step_dist)
|
||||||
old_tq = self._trapq
|
old_tq = self._trapq
|
||||||
self._trapq = tq
|
self._trapq = tq
|
||||||
return old_tq
|
return old_tq
|
||||||
def add_active_callback(self, cb):
|
def add_active_callback(self, cb):
|
||||||
self._active_callbacks.append(cb)
|
self._active_callbacks.append(cb)
|
||||||
def generate_steps(self, flush_time):
|
if len(self._active_callbacks) == 1:
|
||||||
# Check for activity if necessary
|
printer = self._mcu.get_printer()
|
||||||
if self._active_callbacks:
|
motion_queuing = printer.lookup_object('motion_queuing')
|
||||||
sk = self._stepper_kinematics
|
motion_queuing.register_flush_callback(self._check_active)
|
||||||
ret = self._itersolve_check_active(sk, flush_time)
|
def _check_active(self, must_flush_time, max_step_gen_time):
|
||||||
if ret:
|
|
||||||
cbs = self._active_callbacks
|
|
||||||
self._active_callbacks = []
|
|
||||||
for cb in cbs:
|
|
||||||
cb(ret)
|
|
||||||
# Generate steps
|
|
||||||
sk = self._stepper_kinematics
|
sk = self._stepper_kinematics
|
||||||
ret = self._itersolve_generate_steps(sk, flush_time)
|
ret = self._itersolve_check_active(sk, max_step_gen_time)
|
||||||
if ret:
|
if not ret:
|
||||||
raise error("Internal error in stepcompress")
|
# Stepper motor still not active
|
||||||
|
return
|
||||||
|
# Motor is active, disable future checking
|
||||||
|
printer = self._mcu.get_printer()
|
||||||
|
motion_queuing = printer.lookup_object('motion_queuing')
|
||||||
|
motion_queuing.unregister_flush_callback(self._check_active)
|
||||||
|
cbs = self._active_callbacks
|
||||||
|
self._active_callbacks = []
|
||||||
|
# Invoke callbacks
|
||||||
|
for cb in cbs:
|
||||||
|
cb(ret)
|
||||||
def is_active_axis(self, axis):
|
def is_active_axis(self, axis):
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
ffi_main, ffi_lib = chelper.get_ffi()
|
||||||
a = axis.encode()
|
a = axis.encode()
|
||||||
@ -258,7 +262,6 @@ class MCU_stepper:
|
|||||||
# Helper code to build a stepper object from a config section
|
# Helper code to build a stepper object from a config section
|
||||||
def PrinterStepper(config, units_in_radians=False):
|
def PrinterStepper(config, units_in_radians=False):
|
||||||
printer = config.get_printer()
|
printer = config.get_printer()
|
||||||
name = config.get_name()
|
|
||||||
# Stepper definition
|
# Stepper definition
|
||||||
ppins = printer.lookup_object('pins')
|
ppins = printer.lookup_object('pins')
|
||||||
step_pin = config.get('step_pin')
|
step_pin = config.get('step_pin')
|
||||||
@ -269,7 +272,7 @@ def PrinterStepper(config, units_in_radians=False):
|
|||||||
config, units_in_radians, True)
|
config, units_in_radians, True)
|
||||||
step_pulse_duration = config.getfloat('step_pulse_duration', None,
|
step_pulse_duration = config.getfloat('step_pulse_duration', None,
|
||||||
minval=0., maxval=.001)
|
minval=0., maxval=.001)
|
||||||
mcu_stepper = MCU_stepper(name, step_pin_params, dir_pin_params,
|
mcu_stepper = MCU_stepper(config, step_pin_params, dir_pin_params,
|
||||||
rotation_dist, steps_per_rotation,
|
rotation_dist, steps_per_rotation,
|
||||||
step_pulse_duration, units_in_radians)
|
step_pulse_duration, units_in_radians)
|
||||||
# Register with helper modules
|
# Register with helper modules
|
||||||
@ -442,9 +445,6 @@ class GenericPrinterRail:
|
|||||||
def setup_itersolve(self, alloc_func, *params):
|
def setup_itersolve(self, alloc_func, *params):
|
||||||
for stepper in self.steppers:
|
for stepper in self.steppers:
|
||||||
stepper.setup_itersolve(alloc_func, *params)
|
stepper.setup_itersolve(alloc_func, *params)
|
||||||
def generate_steps(self, flush_time):
|
|
||||||
for stepper in self.steppers:
|
|
||||||
stepper.generate_steps(flush_time)
|
|
||||||
def set_trapq(self, trapq):
|
def set_trapq(self, trapq):
|
||||||
for stepper in self.steppers:
|
for stepper in self.steppers:
|
||||||
stepper.set_trapq(trapq)
|
stepper.set_trapq(trapq)
|
||||||
|
@ -200,7 +200,6 @@ MIN_KIN_TIME = 0.100
|
|||||||
MOVE_BATCH_TIME = 0.500
|
MOVE_BATCH_TIME = 0.500
|
||||||
STEPCOMPRESS_FLUSH_TIME = 0.050
|
STEPCOMPRESS_FLUSH_TIME = 0.050
|
||||||
SDS_CHECK_TIME = 0.001 # step+dir+step filter in stepcompress.c
|
SDS_CHECK_TIME = 0.001 # step+dir+step filter in stepcompress.c
|
||||||
MOVE_HISTORY_EXPIRE = 30.
|
|
||||||
|
|
||||||
DRIP_SEGMENT_TIME = 0.050
|
DRIP_SEGMENT_TIME = 0.050
|
||||||
DRIP_TIME = 0.100
|
DRIP_TIME = 0.100
|
||||||
@ -219,17 +218,8 @@ class ToolHead:
|
|||||||
# Velocity and acceleration control
|
# Velocity and acceleration control
|
||||||
self.max_velocity = config.getfloat('max_velocity', above=0.)
|
self.max_velocity = config.getfloat('max_velocity', above=0.)
|
||||||
self.max_accel = config.getfloat('max_accel', above=0.)
|
self.max_accel = config.getfloat('max_accel', above=0.)
|
||||||
min_cruise_ratio = 0.5
|
|
||||||
if config.getfloat('minimum_cruise_ratio', None) is None:
|
|
||||||
req_accel_to_decel = config.getfloat('max_accel_to_decel', None,
|
|
||||||
above=0.)
|
|
||||||
if req_accel_to_decel is not None:
|
|
||||||
config.deprecate('max_accel_to_decel')
|
|
||||||
min_cruise_ratio = 1. - min(1., (req_accel_to_decel
|
|
||||||
/ self.max_accel))
|
|
||||||
self.min_cruise_ratio = config.getfloat('minimum_cruise_ratio',
|
self.min_cruise_ratio = config.getfloat('minimum_cruise_ratio',
|
||||||
min_cruise_ratio,
|
0.5, below=1., minval=0.)
|
||||||
below=1., minval=0.)
|
|
||||||
self.square_corner_velocity = config.getfloat(
|
self.square_corner_velocity = config.getfloat(
|
||||||
'square_corner_velocity', 5., minval=0.)
|
'square_corner_velocity', 5., minval=0.)
|
||||||
self.junction_deviation = self.max_accel_to_decel = 0.
|
self.junction_deviation = self.max_accel_to_decel = 0.
|
||||||
@ -250,18 +240,14 @@ class ToolHead:
|
|||||||
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
||||||
self.do_kick_flush_timer = True
|
self.do_kick_flush_timer = True
|
||||||
self.last_flush_time = self.min_restart_time = 0.
|
self.last_flush_time = self.min_restart_time = 0.
|
||||||
self.need_flush_time = self.step_gen_time = self.clear_history_time = 0.
|
self.need_flush_time = self.step_gen_time = 0.
|
||||||
# Kinematic step generation scan window time tracking
|
# Kinematic step generation scan window time tracking
|
||||||
self.kin_flush_delay = SDS_CHECK_TIME
|
self.kin_flush_delay = SDS_CHECK_TIME
|
||||||
self.kin_flush_times = []
|
self.kin_flush_times = []
|
||||||
# Setup iterative solver
|
# Setup for generating moves
|
||||||
ffi_main, ffi_lib = chelper.get_ffi()
|
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||||
self.trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
self.trapq = self.motion_queuing.allocate_trapq()
|
||||||
self.trapq_append = ffi_lib.trapq_append
|
self.trapq_append = self.motion_queuing.lookup_trapq_append()
|
||||||
self.trapq_finalize_moves = ffi_lib.trapq_finalize_moves
|
|
||||||
# Motion flushing
|
|
||||||
self.step_generators = []
|
|
||||||
self.flush_trapqs = [self.trapq]
|
|
||||||
# Create kinematics class
|
# Create kinematics class
|
||||||
gcode = self.printer.lookup_object('gcode')
|
gcode = self.printer.lookup_object('gcode')
|
||||||
self.Coord = gcode.Coord
|
self.Coord = gcode.Coord
|
||||||
@ -279,20 +265,9 @@ class ToolHead:
|
|||||||
msg = "Error loading kinematics '%s'" % (kin_name,)
|
msg = "Error loading kinematics '%s'" % (kin_name,)
|
||||||
logging.exception(msg)
|
logging.exception(msg)
|
||||||
raise config.error(msg)
|
raise config.error(msg)
|
||||||
# Register commands
|
# Register handlers
|
||||||
gcode.register_command('G4', self.cmd_G4)
|
|
||||||
gcode.register_command('M400', self.cmd_M400)
|
|
||||||
gcode.register_command('SET_VELOCITY_LIMIT',
|
|
||||||
self.cmd_SET_VELOCITY_LIMIT,
|
|
||||||
desc=self.cmd_SET_VELOCITY_LIMIT_help)
|
|
||||||
gcode.register_command('M204', self.cmd_M204)
|
|
||||||
self.printer.register_event_handler("klippy:shutdown",
|
self.printer.register_event_handler("klippy:shutdown",
|
||||||
self._handle_shutdown)
|
self._handle_shutdown)
|
||||||
# Load some default modules
|
|
||||||
modules = ["gcode_move", "homing", "idle_timeout", "statistics",
|
|
||||||
"manual_probe", "tuning_tower", "garbage_collection"]
|
|
||||||
for module_name in modules:
|
|
||||||
self.printer.load_object(config, module_name)
|
|
||||||
# Print time and flush tracking
|
# Print time and flush tracking
|
||||||
def _advance_flush_time(self, flush_time):
|
def _advance_flush_time(self, flush_time):
|
||||||
flush_time = max(flush_time, self.last_flush_time)
|
flush_time = max(flush_time, self.last_flush_time)
|
||||||
@ -300,19 +275,10 @@ class ToolHead:
|
|||||||
sg_flush_want = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
sg_flush_want = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
||||||
self.print_time - self.kin_flush_delay)
|
self.print_time - self.kin_flush_delay)
|
||||||
sg_flush_time = max(sg_flush_want, flush_time)
|
sg_flush_time = max(sg_flush_want, flush_time)
|
||||||
for sg in self.step_generators:
|
trapq_free_time = sg_flush_time - self.kin_flush_delay
|
||||||
sg(sg_flush_time)
|
self.motion_queuing.flush_motion_queues(flush_time, sg_flush_time,
|
||||||
|
trapq_free_time)
|
||||||
self.min_restart_time = max(self.min_restart_time, sg_flush_time)
|
self.min_restart_time = max(self.min_restart_time, sg_flush_time)
|
||||||
# Free trapq entries that are no longer needed
|
|
||||||
clear_history_time = self.clear_history_time
|
|
||||||
if not self.can_pause:
|
|
||||||
clear_history_time = flush_time - MOVE_HISTORY_EXPIRE
|
|
||||||
free_time = sg_flush_time - self.kin_flush_delay
|
|
||||||
for trapq in self.flush_trapqs:
|
|
||||||
self.trapq_finalize_moves(trapq, free_time, clear_history_time)
|
|
||||||
# Flush stepcompress and mcu steppersync
|
|
||||||
for m in self.all_mcus:
|
|
||||||
m.flush_moves(flush_time, clear_history_time)
|
|
||||||
self.last_flush_time = flush_time
|
self.last_flush_time = flush_time
|
||||||
def _advance_move_time(self, next_print_time):
|
def _advance_move_time(self, next_print_time):
|
||||||
pt_delay = self.kin_flush_delay + STEPCOMPRESS_FLUSH_TIME
|
pt_delay = self.kin_flush_delay + STEPCOMPRESS_FLUSH_TIME
|
||||||
@ -362,8 +328,7 @@ class ToolHead:
|
|||||||
for cb in move.timing_callbacks:
|
for cb in move.timing_callbacks:
|
||||||
cb(next_move_time)
|
cb(next_move_time)
|
||||||
# Generate steps for moves
|
# Generate steps for moves
|
||||||
self.note_mcu_movequeue_activity(next_move_time + self.kin_flush_delay,
|
self.note_mcu_movequeue_activity(next_move_time + self.kin_flush_delay)
|
||||||
set_step_gen_time=True)
|
|
||||||
self._advance_move_time(next_move_time)
|
self._advance_move_time(next_move_time)
|
||||||
def _flush_lookahead(self):
|
def _flush_lookahead(self):
|
||||||
# Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime"
|
# Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime"
|
||||||
@ -506,39 +471,27 @@ class ToolHead:
|
|||||||
eventtime = self.reactor.pause(eventtime + 0.100)
|
eventtime = self.reactor.pause(eventtime + 0.100)
|
||||||
def set_extruder(self, extruder, extrude_pos):
|
def set_extruder(self, extruder, extrude_pos):
|
||||||
# XXX - should use add_extra_axis
|
# XXX - should use add_extra_axis
|
||||||
prev_ea_trapq = self.extra_axes[0].get_trapq()
|
|
||||||
if prev_ea_trapq in self.flush_trapqs:
|
|
||||||
self.flush_trapqs.remove(prev_ea_trapq)
|
|
||||||
self.extra_axes[0] = extruder
|
self.extra_axes[0] = extruder
|
||||||
self.commanded_pos[3] = extrude_pos
|
self.commanded_pos[3] = extrude_pos
|
||||||
ea_trapq = extruder.get_trapq()
|
|
||||||
if ea_trapq is not None:
|
|
||||||
self.flush_trapqs.append(ea_trapq)
|
|
||||||
def get_extruder(self):
|
def get_extruder(self):
|
||||||
return self.extra_axes[0]
|
return self.extra_axes[0]
|
||||||
def add_extra_axis(self, ea, axis_pos):
|
def add_extra_axis(self, ea, axis_pos):
|
||||||
self._flush_lookahead()
|
self._flush_lookahead()
|
||||||
self.extra_axes.append(ea)
|
self.extra_axes.append(ea)
|
||||||
self.commanded_pos.append(axis_pos)
|
self.commanded_pos.append(axis_pos)
|
||||||
ea_trapq = ea.get_trapq()
|
|
||||||
if ea_trapq is not None:
|
|
||||||
self.flush_trapqs.append(ea_trapq)
|
|
||||||
self.printer.send_event("toolhead:update_extra_axes")
|
self.printer.send_event("toolhead:update_extra_axes")
|
||||||
def remove_extra_axis(self, ea):
|
def remove_extra_axis(self, ea):
|
||||||
self._flush_lookahead()
|
self._flush_lookahead()
|
||||||
if ea not in self.extra_axes:
|
if ea not in self.extra_axes:
|
||||||
return
|
return
|
||||||
ea_index = self.extra_axes.index(ea) + 3
|
ea_index = self.extra_axes.index(ea) + 3
|
||||||
ea_trapq = ea.get_trapq()
|
|
||||||
if ea_trapq in self.flush_trapqs:
|
|
||||||
self.flush_trapqs.remove(ea_trapq)
|
|
||||||
self.commanded_pos.pop(ea_index)
|
self.commanded_pos.pop(ea_index)
|
||||||
self.extra_axes.pop(ea_index - 3)
|
self.extra_axes.pop(ea_index - 3)
|
||||||
self.printer.send_event("toolhead:update_extra_axes")
|
self.printer.send_event("toolhead:update_extra_axes")
|
||||||
def get_extra_axes(self):
|
def get_extra_axes(self):
|
||||||
return [None, None, None] + self.extra_axes
|
return [None, None, None] + self.extra_axes
|
||||||
# Homing "drip move" handling
|
# Homing "drip move" handling
|
||||||
def drip_update_time(self, next_print_time, drip_completion, addstepper=()):
|
def drip_update_time(self, next_print_time, drip_completion):
|
||||||
# Transition from "NeedPrime"/"Priming"/main state to "Drip" state
|
# Transition from "NeedPrime"/"Priming"/main state to "Drip" state
|
||||||
self.special_queuing_state = "Drip"
|
self.special_queuing_state = "Drip"
|
||||||
self.need_check_pause = self.reactor.NEVER
|
self.need_check_pause = self.reactor.NEVER
|
||||||
@ -559,10 +512,7 @@ class ToolHead:
|
|||||||
drip_completion.wait(curtime + wait_time)
|
drip_completion.wait(curtime + wait_time)
|
||||||
continue
|
continue
|
||||||
npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time)
|
npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time)
|
||||||
self.note_mcu_movequeue_activity(npt + self.kin_flush_delay,
|
self.note_mcu_movequeue_activity(npt + self.kin_flush_delay)
|
||||||
set_step_gen_time=True)
|
|
||||||
for stepper in addstepper:
|
|
||||||
stepper.generate_steps(npt)
|
|
||||||
self._advance_move_time(npt)
|
self._advance_move_time(npt)
|
||||||
# Exit "Drip" state
|
# Exit "Drip" state
|
||||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||||
@ -599,14 +549,13 @@ class ToolHead:
|
|||||||
next_move_time = self._drip_load_trapq(move)
|
next_move_time = self._drip_load_trapq(move)
|
||||||
self.drip_update_time(next_move_time, drip_completion)
|
self.drip_update_time(next_move_time, drip_completion)
|
||||||
# Move finished; cleanup any remnants on trapq
|
# Move finished; cleanup any remnants on trapq
|
||||||
self.trapq_finalize_moves(self.trapq, self.reactor.NEVER, 0)
|
self.motion_queuing.wipe_trapq(self.trapq)
|
||||||
# Misc commands
|
# Misc commands
|
||||||
def stats(self, eventtime):
|
def stats(self, eventtime):
|
||||||
max_queue_time = max(self.print_time, self.last_flush_time)
|
max_queue_time = max(self.print_time, self.last_flush_time)
|
||||||
for m in self.all_mcus:
|
for m in self.all_mcus:
|
||||||
m.check_active(max_queue_time, eventtime)
|
m.check_active(max_queue_time, eventtime)
|
||||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||||
self.clear_history_time = est_print_time - MOVE_HISTORY_EXPIRE
|
|
||||||
buffer_time = self.print_time - est_print_time
|
buffer_time = self.print_time - est_print_time
|
||||||
is_active = buffer_time > -60. or not self.special_queuing_state
|
is_active = buffer_time > -60. or not self.special_queuing_state
|
||||||
if self.special_queuing_state == "Drip":
|
if self.special_queuing_state == "Drip":
|
||||||
@ -639,11 +588,6 @@ class ToolHead:
|
|||||||
return self.kin
|
return self.kin
|
||||||
def get_trapq(self):
|
def get_trapq(self):
|
||||||
return self.trapq
|
return self.trapq
|
||||||
def register_step_generator(self, handler):
|
|
||||||
self.step_generators.append(handler)
|
|
||||||
def unregister_step_generator(self, handler):
|
|
||||||
if handler in self.step_generators:
|
|
||||||
self.step_generators.remove(handler)
|
|
||||||
def note_step_generation_scan_time(self, delay, old_delay=0.):
|
def note_step_generation_scan_time(self, delay, old_delay=0.):
|
||||||
self.flush_step_generation()
|
self.flush_step_generation()
|
||||||
if old_delay:
|
if old_delay:
|
||||||
@ -658,9 +602,9 @@ class ToolHead:
|
|||||||
callback(self.get_last_move_time())
|
callback(self.get_last_move_time())
|
||||||
return
|
return
|
||||||
last_move.timing_callbacks.append(callback)
|
last_move.timing_callbacks.append(callback)
|
||||||
def note_mcu_movequeue_activity(self, mq_time, set_step_gen_time=False):
|
def note_mcu_movequeue_activity(self, mq_time, is_step_gen=True):
|
||||||
self.need_flush_time = max(self.need_flush_time, mq_time)
|
self.need_flush_time = max(self.need_flush_time, mq_time)
|
||||||
if set_step_gen_time:
|
if is_step_gen:
|
||||||
self.step_gen_time = max(self.step_gen_time, mq_time)
|
self.step_gen_time = max(self.step_gen_time, mq_time)
|
||||||
if self.do_kick_flush_timer:
|
if self.do_kick_flush_timer:
|
||||||
self.do_kick_flush_timer = False
|
self.do_kick_flush_timer = False
|
||||||
@ -671,29 +615,8 @@ class ToolHead:
|
|||||||
scv2 = self.square_corner_velocity**2
|
scv2 = self.square_corner_velocity**2
|
||||||
self.junction_deviation = scv2 * (math.sqrt(2.) - 1.) / self.max_accel
|
self.junction_deviation = scv2 * (math.sqrt(2.) - 1.) / self.max_accel
|
||||||
self.max_accel_to_decel = self.max_accel * (1. - self.min_cruise_ratio)
|
self.max_accel_to_decel = self.max_accel * (1. - self.min_cruise_ratio)
|
||||||
def cmd_G4(self, gcmd):
|
def set_max_velocities(self, max_velocity, max_accel,
|
||||||
# Dwell
|
square_corner_velocity, min_cruise_ratio):
|
||||||
delay = gcmd.get_float('P', 0., minval=0.) / 1000.
|
|
||||||
self.dwell(delay)
|
|
||||||
def cmd_M400(self, gcmd):
|
|
||||||
# Wait for current moves to finish
|
|
||||||
self.wait_moves()
|
|
||||||
cmd_SET_VELOCITY_LIMIT_help = "Set printer velocity limits"
|
|
||||||
def cmd_SET_VELOCITY_LIMIT(self, gcmd):
|
|
||||||
max_velocity = gcmd.get_float('VELOCITY', None, above=0.)
|
|
||||||
max_accel = gcmd.get_float('ACCEL', None, above=0.)
|
|
||||||
square_corner_velocity = gcmd.get_float(
|
|
||||||
'SQUARE_CORNER_VELOCITY', None, minval=0.)
|
|
||||||
min_cruise_ratio = gcmd.get_float(
|
|
||||||
'MINIMUM_CRUISE_RATIO', None, minval=0., below=1.)
|
|
||||||
if min_cruise_ratio is None:
|
|
||||||
req_accel_to_decel = gcmd.get_float('ACCEL_TO_DECEL',
|
|
||||||
None, above=0.)
|
|
||||||
if req_accel_to_decel is not None and max_accel is not None:
|
|
||||||
min_cruise_ratio = 1. - min(1., req_accel_to_decel / max_accel)
|
|
||||||
elif req_accel_to_decel is not None and max_accel is None:
|
|
||||||
min_cruise_ratio = 1. - min(1., (req_accel_to_decel
|
|
||||||
/ self.max_accel))
|
|
||||||
if max_velocity is not None:
|
if max_velocity is not None:
|
||||||
self.max_velocity = max_velocity
|
self.max_velocity = max_velocity
|
||||||
if max_accel is not None:
|
if max_accel is not None:
|
||||||
@ -703,12 +626,43 @@ class ToolHead:
|
|||||||
if min_cruise_ratio is not None:
|
if min_cruise_ratio is not None:
|
||||||
self.min_cruise_ratio = min_cruise_ratio
|
self.min_cruise_ratio = min_cruise_ratio
|
||||||
self._calc_junction_deviation()
|
self._calc_junction_deviation()
|
||||||
|
return (self.max_velocity, self.max_accel,
|
||||||
|
self.square_corner_velocity, self.min_cruise_ratio)
|
||||||
|
|
||||||
|
# Support common G-Code commands relative to the toolhead
|
||||||
|
class ToolHeadCommandHelper:
|
||||||
|
def __init__(self, config):
|
||||||
|
self.printer = config.get_printer()
|
||||||
|
self.toolhead = self.printer.lookup_object("toolhead")
|
||||||
|
# Register commands
|
||||||
|
gcode = self.printer.lookup_object('gcode')
|
||||||
|
gcode.register_command('G4', self.cmd_G4)
|
||||||
|
gcode.register_command('M400', self.cmd_M400)
|
||||||
|
gcode.register_command('SET_VELOCITY_LIMIT',
|
||||||
|
self.cmd_SET_VELOCITY_LIMIT,
|
||||||
|
desc=self.cmd_SET_VELOCITY_LIMIT_help)
|
||||||
|
gcode.register_command('M204', self.cmd_M204)
|
||||||
|
def cmd_G4(self, gcmd):
|
||||||
|
# Dwell
|
||||||
|
delay = gcmd.get_float('P', 0., minval=0.) / 1000.
|
||||||
|
self.toolhead.dwell(delay)
|
||||||
|
def cmd_M400(self, gcmd):
|
||||||
|
# Wait for current moves to finish
|
||||||
|
self.toolhead.wait_moves()
|
||||||
|
cmd_SET_VELOCITY_LIMIT_help = "Set printer velocity limits"
|
||||||
|
def cmd_SET_VELOCITY_LIMIT(self, gcmd):
|
||||||
|
max_velocity = gcmd.get_float('VELOCITY', None, above=0.)
|
||||||
|
max_accel = gcmd.get_float('ACCEL', None, above=0.)
|
||||||
|
square_corner_velocity = gcmd.get_float(
|
||||||
|
'SQUARE_CORNER_VELOCITY', None, minval=0.)
|
||||||
|
min_cruise_ratio = gcmd.get_float(
|
||||||
|
'MINIMUM_CRUISE_RATIO', None, minval=0., below=1.)
|
||||||
|
mv, ma, scv, mcr = self.toolhead.set_max_velocities(
|
||||||
|
max_velocity, max_accel, square_corner_velocity, min_cruise_ratio)
|
||||||
msg = ("max_velocity: %.6f\n"
|
msg = ("max_velocity: %.6f\n"
|
||||||
"max_accel: %.6f\n"
|
"max_accel: %.6f\n"
|
||||||
"minimum_cruise_ratio: %.6f\n"
|
"minimum_cruise_ratio: %.6f\n"
|
||||||
"square_corner_velocity: %.6f" % (
|
"square_corner_velocity: %.6f" % (mv, ma, scv, mcr))
|
||||||
self.max_velocity, self.max_accel,
|
|
||||||
self.min_cruise_ratio, self.square_corner_velocity))
|
|
||||||
self.printer.set_rollover_info("toolhead", "toolhead: %s" % (msg,))
|
self.printer.set_rollover_info("toolhead", "toolhead: %s" % (msg,))
|
||||||
if (max_velocity is None and max_accel is None
|
if (max_velocity is None and max_accel is None
|
||||||
and square_corner_velocity is None and min_cruise_ratio is None):
|
and square_corner_velocity is None and min_cruise_ratio is None):
|
||||||
@ -725,9 +679,16 @@ class ToolHead:
|
|||||||
% (gcmd.get_commandline(),))
|
% (gcmd.get_commandline(),))
|
||||||
return
|
return
|
||||||
accel = min(p, t)
|
accel = min(p, t)
|
||||||
self.max_accel = accel
|
self.toolhead.set_max_velocities(None, accel, None, None)
|
||||||
self._calc_junction_deviation()
|
|
||||||
|
|
||||||
def add_printer_objects(config):
|
def add_printer_objects(config):
|
||||||
config.get_printer().add_object('toolhead', ToolHead(config))
|
printer = config.get_printer()
|
||||||
|
printer.add_object('toolhead', ToolHead(config))
|
||||||
|
ToolHeadCommandHelper(config)
|
||||||
|
# Load default extruder objects
|
||||||
kinematics.extruder.add_printer_objects(config)
|
kinematics.extruder.add_printer_objects(config)
|
||||||
|
# Load some default modules
|
||||||
|
modules = ["gcode_move", "homing", "idle_timeout", "statistics",
|
||||||
|
"manual_probe", "tuning_tower", "garbage_collection"]
|
||||||
|
for module_name in modules:
|
||||||
|
printer.load_object(config, module_name)
|
||||||
|
@ -169,6 +169,14 @@ BOARD_DEFS = {
|
|||||||
"cs_pin": "PB12",
|
"cs_pin": "PB12",
|
||||||
"firmware_path": "ZNP_ROBIN_NANO.bin",
|
"firmware_path": "ZNP_ROBIN_NANO.bin",
|
||||||
"current_firmware_path": "ZNP_ROBIN_NANO.CUR"
|
"current_firmware_path": "ZNP_ROBIN_NANO.CUR"
|
||||||
|
},
|
||||||
|
'qidi-x7': {
|
||||||
|
'mcu': "stm32f401xc",
|
||||||
|
'spi_bus': "spi2",
|
||||||
|
'cs_pin': "PB12",
|
||||||
|
'skip_verify': False,
|
||||||
|
'firmware_path': 'qd_mcu.bin',
|
||||||
|
'current_firmware_path': 'qd_mcu.CUR'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -219,7 +227,9 @@ BOARD_ALIASES = {
|
|||||||
'fysetc-s6-v2': BOARD_DEFS['fysetc-spider'],
|
'fysetc-s6-v2': BOARD_DEFS['fysetc-spider'],
|
||||||
'robin_v3': BOARD_DEFS['monster8'],
|
'robin_v3': BOARD_DEFS['monster8'],
|
||||||
'btt-skrat-v1.0': BOARD_DEFS['btt-skrat'],
|
'btt-skrat-v1.0': BOARD_DEFS['btt-skrat'],
|
||||||
'chitu-v6': BOARD_DEFS['chitu-v6']
|
'chitu-v6': BOARD_DEFS['chitu-v6'],
|
||||||
|
'qidi-q1-pro': BOARD_DEFS['qidi-x7'],
|
||||||
|
'qidi-plus4': BOARD_DEFS['qidi-x7']
|
||||||
}
|
}
|
||||||
|
|
||||||
def list_boards():
|
def list_boards():
|
||||||
|
@ -44,11 +44,15 @@ struct i2c_info {
|
|||||||
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB10_PB11", "PB10,PB11");
|
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB10_PB11", "PB10,PB11");
|
||||||
DECL_ENUMERATION("i2c_bus", "i2c2_PB13_PB14", 4);
|
DECL_ENUMERATION("i2c_bus", "i2c2_PB13_PB14", 4);
|
||||||
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB13_PB14", "PB13,PB14");
|
DECL_CONSTANT_STR("BUS_PINS_i2c2_PB13_PB14", "PB13,PB14");
|
||||||
|
DECL_ENUMERATION("i2c_bus", "i2c2_PA7_PA6", 5);
|
||||||
|
DECL_CONSTANT_STR("BUS_PINS_i2c2_PA7_PA6", "PA7,PA6");
|
||||||
#ifdef I2C3
|
#ifdef I2C3
|
||||||
DECL_ENUMERATION("i2c_bus", "i2c3_PB3_PB4", 5);
|
DECL_ENUMERATION("i2c_bus", "i2c3_PB3_PB4", 6);
|
||||||
DECL_CONSTANT_STR("BUS_PINS_i2c3_PB3_PB4", "PB3,PB4");
|
DECL_CONSTANT_STR("BUS_PINS_i2c3_PB3_PB4", "PB3,PB4");
|
||||||
DECL_ENUMERATION("i2c_bus", "i2c3_PC0_PC1", 6);
|
DECL_ENUMERATION("i2c_bus", "i2c3_PC0_PC1", 7);
|
||||||
DECL_CONSTANT_STR("BUS_PINS_i2c3_PC0_PC1", "PC0,PC1");
|
DECL_CONSTANT_STR("BUS_PINS_i2c3_PC0_PC1", "PC0,PC1");
|
||||||
|
DECL_ENUMERATION("i2c_bus", "i2c3_PA7_PA6", 8);
|
||||||
|
DECL_CONSTANT_STR("BUS_PINS_i2c3_PA7_PA6", "PA7,PA6");
|
||||||
#endif
|
#endif
|
||||||
#elif CONFIG_MACH_STM32L4
|
#elif CONFIG_MACH_STM32L4
|
||||||
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
|
DECL_ENUMERATION("i2c_bus", "i2c1_PB6_PB7", 0);
|
||||||
@ -105,9 +109,11 @@ static const struct i2c_info i2c_bus[] = {
|
|||||||
{ I2C1, GPIO('A', 9), GPIO('A', 10), GPIO_FUNCTION(6) },
|
{ I2C1, GPIO('A', 9), GPIO('A', 10), GPIO_FUNCTION(6) },
|
||||||
{ I2C2, GPIO('B', 10), GPIO('B', 11), GPIO_FUNCTION(6) },
|
{ I2C2, GPIO('B', 10), GPIO('B', 11), GPIO_FUNCTION(6) },
|
||||||
{ I2C2, GPIO('B', 13), GPIO('B', 14), GPIO_FUNCTION(6) },
|
{ I2C2, GPIO('B', 13), GPIO('B', 14), GPIO_FUNCTION(6) },
|
||||||
|
{ I2C2, GPIO('A', 7), GPIO('A', 6), GPIO_FUNCTION(8) },
|
||||||
#ifdef I2C3
|
#ifdef I2C3
|
||||||
{ I2C3, GPIO('B', 3), GPIO('B', 4), GPIO_FUNCTION(6) },
|
{ I2C3, GPIO('B', 3), GPIO('B', 4), GPIO_FUNCTION(6) },
|
||||||
{ I2C3, GPIO('C', 0), GPIO('C', 1), GPIO_FUNCTION(6) },
|
{ I2C3, GPIO('C', 0), GPIO('C', 1), GPIO_FUNCTION(6) },
|
||||||
|
{ I2C3, GPIO('A', 7), GPIO('A', 6), GPIO_FUNCTION(9) },
|
||||||
#endif
|
#endif
|
||||||
#elif CONFIG_MACH_STM32L4
|
#elif CONFIG_MACH_STM32L4
|
||||||
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(4) },
|
{ I2C1, GPIO('B', 6), GPIO('B', 7), GPIO_FUNCTION(4) },
|
||||||
|
@ -36,7 +36,7 @@ SET_GCODE_OFFSET Z=.1
|
|||||||
M206 Z-.2
|
M206 Z-.2
|
||||||
SET_GCODE_OFFSET Z_ADJUST=-.1
|
SET_GCODE_OFFSET Z_ADJUST=-.1
|
||||||
|
|
||||||
SET_VELOCITY_LIMIT ACCEL=100 VELOCITY=20 SQUARE_CORNER_VELOCITY=1 ACCEL_TO_DECEL=200
|
SET_VELOCITY_LIMIT ACCEL=100 VELOCITY=20 SQUARE_CORNER_VELOCITY=1 MINIMUM_CRUISE_RATIO=0
|
||||||
M204 S500
|
M204 S500
|
||||||
|
|
||||||
SET_PRESSURE_ADVANCE EXTRUDER=extruder ADVANCE=.001
|
SET_PRESSURE_ADVANCE EXTRUDER=extruder ADVANCE=.001
|
||||||
|
Loading…
x
Reference in New Issue
Block a user