mirror of
https://github.com/andreili/klipper.git
synced 2025-09-20 11:54:42 +02:00
Merge branch 'Klipper3d:master' into master
This commit is contained in:
commit
b0617c7266
@ -4944,8 +4944,8 @@ detection_length: 7.0
|
||||
# a state change on the switch_pin
|
||||
# Default is 7 mm.
|
||||
extruder:
|
||||
# The name of the extruder section this sensor is associated with.
|
||||
# This parameter must be provided.
|
||||
# The name of the extruder or extruder_stepper section this sensor
|
||||
# is associated with. This parameter must be provided.
|
||||
switch_pin:
|
||||
#pause_on_runout:
|
||||
#runout_gcode:
|
||||
|
17
docs/FAQ.md
17
docs/FAQ.md
@ -98,17 +98,16 @@ bootloaders.
|
||||
|
||||
## Can I run Klipper on something other than a Raspberry Pi 3?
|
||||
|
||||
The recommended hardware is a Raspberry Pi 2, Raspberry Pi 3, or
|
||||
Raspberry Pi 4.
|
||||
The recommended hardware is a Raspberry Pi Zero2w, Raspberry Pi 3,
|
||||
Raspberry Pi 4 or Raspberry Pi 5. Klipper will also run on other SBC
|
||||
devices as well as x86 hardware, as described below.
|
||||
|
||||
Klipper will run on a Raspberry Pi 1 and on the Raspberry Pi Zero, but
|
||||
these boards don't have enough processing power to run OctoPrint
|
||||
Klipper will run on a Raspberry Pi 1, 2 and on the Raspberry Pi Zero1,
|
||||
but these boards don't have enough processing power to run Klipper
|
||||
well. It is common for print stalls to occur on these slower machines
|
||||
when printing directly from OctoPrint. (The printer may move faster
|
||||
than OctoPrint can send movement commands.) If you wish to run on one
|
||||
one of these slower boards anyway, consider using the "virtual_sdcard"
|
||||
feature when printing (see
|
||||
[config reference](Config_Reference.md#virtual_sdcard) for details).
|
||||
when printing (The printer may move faster than Klipper can send
|
||||
movement commands.) It is not reccomended to run Kliper on these older
|
||||
machines.
|
||||
|
||||
For running on the Beaglebone, see the
|
||||
[Beaglebone specific installation instructions](Beaglebone.md).
|
||||
|
@ -56,6 +56,8 @@ defs_stepcompress = """
|
||||
, uint64_t start_clock, uint64_t end_clock);
|
||||
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||
, struct stepper_kinematics *sk);
|
||||
struct stepper_kinematics *stepcompress_get_stepper_kinematics(
|
||||
struct stepcompress *sc);
|
||||
"""
|
||||
|
||||
defs_steppersync = """
|
||||
@ -76,11 +78,14 @@ defs_itersolve = """
|
||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||
, double step_dist);
|
||||
struct trapq *itersolve_get_trapq(struct stepper_kinematics *sk);
|
||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||
, double x, double y, double z);
|
||||
void itersolve_set_position(struct stepper_kinematics *sk
|
||||
, double x, double y, double z);
|
||||
double itersolve_get_commanded_pos(struct stepper_kinematics *sk);
|
||||
double itersolve_get_gen_steps_pre_active(struct stepper_kinematics *sk);
|
||||
double itersolve_get_gen_steps_post_active(struct stepper_kinematics *sk);
|
||||
"""
|
||||
|
||||
defs_trapq = """
|
||||
@ -157,8 +162,6 @@ defs_kin_extruder = """
|
||||
"""
|
||||
|
||||
defs_kin_shaper = """
|
||||
double input_shaper_get_step_generation_window(
|
||||
struct stepper_kinematics *sk);
|
||||
int input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis
|
||||
, int n, double a[], double t[]);
|
||||
int input_shaper_set_sk(struct stepper_kinematics *sk
|
||||
@ -274,6 +277,28 @@ def do_build_code(cmd):
|
||||
logging.error(msg)
|
||||
raise Exception(msg)
|
||||
|
||||
# Build the main c_helper.so c code library
|
||||
def check_build_c_library():
|
||||
srcdir = os.path.dirname(os.path.realpath(__file__))
|
||||
srcfiles = get_abs_files(srcdir, SOURCE_FILES)
|
||||
ofiles = get_abs_files(srcdir, OTHER_FILES)
|
||||
destlib = get_abs_files(srcdir, [DEST_LIB])[0]
|
||||
if not check_build_code(srcfiles+ofiles+[__file__], destlib):
|
||||
# Code already built
|
||||
return destlib
|
||||
# Select command line options
|
||||
if check_gcc_option(SSE_FLAGS):
|
||||
cmd = "%s %s %s" % (GCC_CMD, SSE_FLAGS, COMPILE_ARGS)
|
||||
else:
|
||||
cmd = "%s %s" % (GCC_CMD, COMPILE_ARGS)
|
||||
# Invoke compiler
|
||||
logging.info("Building C code module %s", DEST_LIB)
|
||||
tempdestlib = get_abs_files(srcdir, ["_temp_" + DEST_LIB])[0]
|
||||
do_build_code(cmd % (tempdestlib, ' '.join(srcfiles)))
|
||||
# Rename from temporary file to final file name
|
||||
os.rename(tempdestlib, destlib)
|
||||
return destlib
|
||||
|
||||
FFI_main = None
|
||||
FFI_lib = None
|
||||
pyhelper_logging_callback = None
|
||||
@ -286,17 +311,9 @@ def logging_callback(msg):
|
||||
def get_ffi():
|
||||
global FFI_main, FFI_lib, pyhelper_logging_callback
|
||||
if FFI_lib is None:
|
||||
srcdir = os.path.dirname(os.path.realpath(__file__))
|
||||
srcfiles = get_abs_files(srcdir, SOURCE_FILES)
|
||||
ofiles = get_abs_files(srcdir, OTHER_FILES)
|
||||
destlib = get_abs_files(srcdir, [DEST_LIB])[0]
|
||||
if check_build_code(srcfiles+ofiles+[__file__], destlib):
|
||||
if check_gcc_option(SSE_FLAGS):
|
||||
cmd = "%s %s %s" % (GCC_CMD, SSE_FLAGS, COMPILE_ARGS)
|
||||
else:
|
||||
cmd = "%s %s" % (GCC_CMD, COMPILE_ARGS)
|
||||
logging.info("Building C code module %s", DEST_LIB)
|
||||
do_build_code(cmd % (destlib, ' '.join(srcfiles)))
|
||||
# Check if library needs to be built, and build if so
|
||||
destlib = check_build_c_library()
|
||||
# Open library
|
||||
FFI_main = cffi.FFI()
|
||||
for d in defs_all:
|
||||
FFI_main.cdef(d)
|
||||
|
@ -248,6 +248,12 @@ itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||
sk->step_dist = step_dist;
|
||||
}
|
||||
|
||||
struct trapq * __visible
|
||||
itersolve_get_trapq(struct stepper_kinematics *sk)
|
||||
{
|
||||
return sk->tq;
|
||||
}
|
||||
|
||||
double __visible
|
||||
itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||
, double x, double y, double z)
|
||||
@ -273,3 +279,15 @@ itersolve_get_commanded_pos(struct stepper_kinematics *sk)
|
||||
{
|
||||
return sk->commanded_pos;
|
||||
}
|
||||
|
||||
double __visible
|
||||
itersolve_get_gen_steps_pre_active(struct stepper_kinematics *sk)
|
||||
{
|
||||
return sk->gen_steps_pre_active;
|
||||
}
|
||||
|
||||
double __visible
|
||||
itersolve_get_gen_steps_post_active(struct stepper_kinematics *sk)
|
||||
{
|
||||
return sk->gen_steps_post_active;
|
||||
}
|
||||
|
@ -31,10 +31,13 @@ double itersolve_check_active(struct stepper_kinematics *sk, double flush_time);
|
||||
int32_t itersolve_is_active_axis(struct stepper_kinematics *sk, char axis);
|
||||
void itersolve_set_trapq(struct stepper_kinematics *sk, struct trapq *tq
|
||||
, double step_dist);
|
||||
struct trapq *itersolve_get_trapq(struct stepper_kinematics *sk);
|
||||
double itersolve_calc_position_from_coord(struct stepper_kinematics *sk
|
||||
, double x, double y, double z);
|
||||
void itersolve_set_position(struct stepper_kinematics *sk
|
||||
, double x, double y, double z);
|
||||
double itersolve_get_commanded_pos(struct stepper_kinematics *sk);
|
||||
double itersolve_get_gen_steps_pre_active(struct stepper_kinematics *sk);
|
||||
double itersolve_get_gen_steps_post_active(struct stepper_kinematics *sk);
|
||||
|
||||
#endif // itersolve.h
|
||||
|
@ -239,14 +239,6 @@ input_shaper_set_shaper_params(struct stepper_kinematics *sk, char axis
|
||||
return status;
|
||||
}
|
||||
|
||||
double __visible
|
||||
input_shaper_get_step_generation_window(struct stepper_kinematics *sk)
|
||||
{
|
||||
struct input_shaper *is = container_of(sk, struct input_shaper, sk);
|
||||
return is->sk.gen_steps_pre_active > is->sk.gen_steps_post_active
|
||||
? is->sk.gen_steps_pre_active : is->sk.gen_steps_post_active;
|
||||
}
|
||||
|
||||
struct stepper_kinematics * __visible
|
||||
input_shaper_alloc(void)
|
||||
{
|
||||
|
@ -10,7 +10,6 @@
|
||||
#include <stdio.h> // fprintf
|
||||
#include <string.h> // strerror
|
||||
#include <time.h> // struct timespec
|
||||
#include <linux/prctl.h> // PR_SET_NAME
|
||||
#include <sys/prctl.h> // prctl
|
||||
#include "compiler.h" // __visible
|
||||
#include "pyhelper.h" // get_monotonic
|
||||
|
@ -674,6 +674,13 @@ stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||
sc->sk = sk;
|
||||
}
|
||||
|
||||
// Report current stepper_kinematics
|
||||
struct stepper_kinematics * __visible
|
||||
stepcompress_get_stepper_kinematics(struct stepcompress *sc)
|
||||
{
|
||||
return sc->sk;
|
||||
}
|
||||
|
||||
// Generate steps (via itersolve) and flush
|
||||
int32_t
|
||||
stepcompress_generate_steps(struct stepcompress *sc, double gen_steps_time
|
||||
|
@ -41,6 +41,8 @@ int stepcompress_extract_old(struct stepcompress *sc
|
||||
struct stepper_kinematics;
|
||||
void stepcompress_set_stepper_kinematics(struct stepcompress *sc
|
||||
, struct stepper_kinematics *sk);
|
||||
struct stepper_kinematics *stepcompress_get_stepper_kinematics(
|
||||
struct stepcompress *sc);
|
||||
int32_t stepcompress_generate_steps(struct stepcompress *sc
|
||||
, double gen_steps_time
|
||||
, uint64_t flush_clock);
|
||||
|
@ -284,7 +284,7 @@ class BME280:
|
||||
self.chip_type, self.i2c.i2c_address))
|
||||
|
||||
# Reset chip
|
||||
self.write_register('RESET', [RESET_CHIP_VALUE], wait=True)
|
||||
self.write_register('RESET', [RESET_CHIP_VALUE])
|
||||
self.reactor.pause(self.reactor.monotonic() + .5)
|
||||
|
||||
# Make sure non-volatile memory has been copied to registers
|
||||
@ -394,7 +394,7 @@ class BME280:
|
||||
self.write_register('CTRL_HUM', self.os_hum)
|
||||
# Enter normal (periodic) mode
|
||||
meas = self.os_temp << 5 | self.os_pres << 2 | MODE_PERIODIC
|
||||
self.write_register('CTRL_MEAS', meas, wait=True)
|
||||
self.write_register('CTRL_MEAS', meas)
|
||||
|
||||
if self.chip_type == 'BME680':
|
||||
self.write_register('CONFIG', self.iir_filter << 2)
|
||||
@ -528,7 +528,7 @@ class BME280:
|
||||
|
||||
# Enter forced mode
|
||||
meas = self.os_temp << 5 | self.os_pres << 2 | MODE
|
||||
self.write_register('CTRL_MEAS', meas, wait=True)
|
||||
self.write_register('CTRL_MEAS', meas)
|
||||
max_sample_time = self.max_sample_time
|
||||
if run_gas:
|
||||
max_sample_time += self.gas_heat_duration / 1000
|
||||
@ -776,15 +776,12 @@ class BME280:
|
||||
params = self.i2c.i2c_read(regs, read_len)
|
||||
return bytearray(params['response'])
|
||||
|
||||
def write_register(self, reg_name, data, wait = False):
|
||||
def write_register(self, reg_name, data):
|
||||
if type(data) is not list:
|
||||
data = [data]
|
||||
reg = self.chip_registers[reg_name]
|
||||
data.insert(0, reg)
|
||||
if not wait:
|
||||
self.i2c.i2c_write(data)
|
||||
else:
|
||||
self.i2c.i2c_write_wait_ack(data)
|
||||
self.i2c.i2c_write(data)
|
||||
|
||||
def get_status(self, eventtime):
|
||||
data = {
|
||||
|
@ -180,6 +180,13 @@ class MCU_I2C:
|
||||
self.cmd_queue = self.mcu.alloc_command_queue()
|
||||
self.mcu.register_config_callback(self.build_config)
|
||||
self.i2c_write_cmd = self.i2c_read_cmd = None
|
||||
printer = self.mcu.get_printer()
|
||||
printer.register_event_handler("klippy:connect", self._handle_connect)
|
||||
# backward support i2c_write inside the init section
|
||||
self._to_write = []
|
||||
def _handle_connect(self):
|
||||
for data in self._to_write:
|
||||
self.i2c_write(data)
|
||||
def get_oid(self):
|
||||
return self.oid
|
||||
def get_mcu(self):
|
||||
@ -207,16 +214,10 @@ class MCU_I2C:
|
||||
cq=self.cmd_queue)
|
||||
def i2c_write(self, data, minclock=0, reqclock=0):
|
||||
if self.i2c_write_cmd is None:
|
||||
# Send setup message via mcu initialization
|
||||
data_msg = "".join(["%02x" % (x,) for x in data])
|
||||
self.mcu.add_config_cmd("i2c_write oid=%d data=%s" % (
|
||||
self.oid, data_msg), is_init=True)
|
||||
self._to_write.append(data)
|
||||
return
|
||||
self.i2c_write_cmd.send([self.oid, data],
|
||||
minclock=minclock, reqclock=reqclock)
|
||||
def i2c_write_wait_ack(self, data, minclock=0, reqclock=0):
|
||||
self.i2c_write_cmd.send_wait_ack([self.oid, data],
|
||||
minclock=minclock, reqclock=reqclock)
|
||||
minclock=minclock, reqclock=reqclock)
|
||||
def i2c_read(self, write, read_len, retry=True):
|
||||
return self.i2c_read_cmd.send([self.oid, write, read_len], retry)
|
||||
|
||||
|
@ -236,6 +236,8 @@ class PrinterLCD:
|
||||
except:
|
||||
logging.exception("Error during display screen update")
|
||||
self.lcd_chip.flush()
|
||||
if self.redraw_request_pending:
|
||||
return self.redraw_time
|
||||
return eventtime + REDRAW_TIME
|
||||
def request_redraw(self):
|
||||
if self.redraw_request_pending:
|
||||
|
@ -15,6 +15,8 @@ class PrinterExtruderStepper:
|
||||
self.handle_connect)
|
||||
def handle_connect(self):
|
||||
self.extruder_stepper.sync_to_extruder(self.extruder_name)
|
||||
def find_past_position(self, print_time):
|
||||
return self.extruder_stepper.find_past_position(print_time)
|
||||
def get_status(self, eventtime):
|
||||
return self.extruder_stepper.get_status(eventtime)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Utility for manually moving a stepper for diagnostic purposes
|
||||
#
|
||||
# Copyright (C) 2018-2019 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2018-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import math, logging
|
||||
@ -10,7 +10,6 @@ BUZZ_DISTANCE = 1.
|
||||
BUZZ_VELOCITY = BUZZ_DISTANCE / .250
|
||||
BUZZ_RADIANS_DISTANCE = math.radians(1.)
|
||||
BUZZ_RADIANS_VELOCITY = BUZZ_RADIANS_DISTANCE / .250
|
||||
STALL_TIME = 0.100
|
||||
|
||||
# Calculate a move's accel_t, cruise_t, and cruise_v
|
||||
def calc_move_time(dist, speed, accel):
|
||||
@ -56,24 +55,16 @@ class ForceMove:
|
||||
raise self.printer.config_error("Unknown stepper %s" % (name,))
|
||||
return self.steppers[name]
|
||||
def _force_enable(self, stepper):
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
print_time = toolhead.get_last_move_time()
|
||||
stepper_name = stepper.get_name()
|
||||
stepper_enable = self.printer.lookup_object('stepper_enable')
|
||||
enable = stepper_enable.lookup_enable(stepper.get_name())
|
||||
was_enable = enable.is_motor_enabled()
|
||||
if not was_enable:
|
||||
enable.motor_enable(print_time)
|
||||
toolhead.dwell(STALL_TIME)
|
||||
return was_enable
|
||||
def _restore_enable(self, stepper, was_enable):
|
||||
if not was_enable:
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.dwell(STALL_TIME)
|
||||
print_time = toolhead.get_last_move_time()
|
||||
stepper_enable = self.printer.lookup_object('stepper_enable')
|
||||
enable = stepper_enable.lookup_enable(stepper.get_name())
|
||||
enable.motor_disable(print_time)
|
||||
toolhead.dwell(STALL_TIME)
|
||||
did_enable = stepper_enable.set_motors_enable([stepper_name], True)
|
||||
return did_enable
|
||||
def _restore_enable(self, stepper, did_enable):
|
||||
if not did_enable:
|
||||
return
|
||||
stepper_name = stepper.get_name()
|
||||
stepper_enable = self.printer.lookup_object('stepper_enable')
|
||||
stepper_enable.set_motors_enable([stepper_name], False)
|
||||
def manual_move(self, stepper, dist, speed, accel=0.):
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.flush_step_generation()
|
||||
@ -85,7 +76,7 @@ class ForceMove:
|
||||
self.trapq_append(self.trapq, print_time, accel_t, cruise_t, accel_t,
|
||||
0., 0., 0., axis_r, 0., 0., 0., cruise_v, accel)
|
||||
print_time = print_time + accel_t + cruise_t + accel_t
|
||||
toolhead.note_mcu_movequeue_activity(print_time)
|
||||
self.motion_queuing.note_mcu_movequeue_activity(print_time)
|
||||
toolhead.dwell(accel_t + cruise_t + accel_t)
|
||||
toolhead.flush_step_generation()
|
||||
stepper.set_trapq(prev_trapq)
|
||||
@ -100,7 +91,7 @@ class ForceMove:
|
||||
def cmd_STEPPER_BUZZ(self, gcmd):
|
||||
stepper = self._lookup_stepper(gcmd)
|
||||
logging.info("Stepper buzz %s", stepper.get_name())
|
||||
was_enable = self._force_enable(stepper)
|
||||
did_enable = self._force_enable(stepper)
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
dist, speed = BUZZ_DISTANCE, BUZZ_VELOCITY
|
||||
if stepper.units_in_radians():
|
||||
@ -110,7 +101,7 @@ class ForceMove:
|
||||
toolhead.dwell(.050)
|
||||
self.manual_move(stepper, -dist, speed)
|
||||
toolhead.dwell(.450)
|
||||
self._restore_enable(stepper, was_enable)
|
||||
self._restore_enable(stepper, did_enable)
|
||||
cmd_FORCE_MOVE_help = "Manually move a stepper; invalidates kinematics"
|
||||
def cmd_FORCE_MOVE(self, gcmd):
|
||||
stepper = self._lookup_stepper(gcmd)
|
||||
|
@ -92,7 +92,7 @@ class GCodeMove:
|
||||
def _get_gcode_position(self):
|
||||
p = [lp - bp for lp, bp in zip(self.last_position, self.base_position)]
|
||||
p[3] /= self.extrude_factor
|
||||
return p
|
||||
return p[:4]
|
||||
def _get_gcode_speed(self):
|
||||
return self.speed / self.speed_factor
|
||||
def _get_gcode_speed_override(self):
|
||||
@ -107,7 +107,7 @@ class GCodeMove:
|
||||
'absolute_extrude': self.absolute_extrude,
|
||||
'homing_origin': self.Coord(*self.homing_position[:4]),
|
||||
'position': self.Coord(*self.last_position[:4]),
|
||||
'gcode_position': self.Coord(*move_position[:4]),
|
||||
'gcode_position': self.Coord(*move_position),
|
||||
}
|
||||
def reset_last_position(self):
|
||||
if self.is_printer_ready:
|
||||
|
@ -75,7 +75,8 @@ class Heater:
|
||||
# No significant change in value - can suppress update
|
||||
return
|
||||
pwm_time = read_time + self.pwm_delay
|
||||
self.next_pwm_time = pwm_time + 0.75 * MAX_HEAT_TIME
|
||||
self.next_pwm_time = (pwm_time + MAX_HEAT_TIME
|
||||
- (3. * self.pwm_delay + 0.001))
|
||||
self.last_pwm_value = value
|
||||
self.mcu_pwm.set_pwm(pwm_time, value)
|
||||
#logging.debug("%s: pwm=%.3f@%.3f (from %.3f@%.3f [%.3f])",
|
||||
|
@ -146,12 +146,8 @@ class InputShaper:
|
||||
is_sk = self._get_input_shaper_stepper_kinematics(s)
|
||||
if is_sk is None:
|
||||
continue
|
||||
old_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk)
|
||||
self.toolhead.flush_step_generation()
|
||||
ffi_lib.input_shaper_update_sk(is_sk)
|
||||
new_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk)
|
||||
if old_delay != new_delay:
|
||||
self.toolhead.note_step_generation_scan_time(new_delay,
|
||||
old_delay)
|
||||
def _update_input_shaping(self, error=None):
|
||||
self.toolhead.flush_step_generation()
|
||||
ffi_main, ffi_lib = chelper.get_ffi()
|
||||
@ -163,16 +159,11 @@ class InputShaper:
|
||||
is_sk = self._get_input_shaper_stepper_kinematics(s)
|
||||
if is_sk is None:
|
||||
continue
|
||||
old_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk)
|
||||
for shaper in self.shapers:
|
||||
if shaper in failed_shapers:
|
||||
continue
|
||||
if not shaper.set_shaper_kinematics(is_sk):
|
||||
failed_shapers.append(shaper)
|
||||
new_delay = ffi_lib.input_shaper_get_step_generation_window(is_sk)
|
||||
if old_delay != new_delay:
|
||||
self.toolhead.note_step_generation_scan_time(new_delay,
|
||||
old_delay)
|
||||
if failed_shapers:
|
||||
error = error or self.printer.command_error
|
||||
raise error("Failed to configure shaper(s) %s with given parameters"
|
||||
|
@ -22,6 +22,7 @@ class ManualStepper:
|
||||
self.velocity = config.getfloat('velocity', 5., above=0.)
|
||||
self.accel = self.homing_accel = config.getfloat('accel', 0., minval=0.)
|
||||
self.next_cmd_time = 0.
|
||||
self.commanded_pos = 0.
|
||||
self.pos_min = config.getfloat('position_min', None)
|
||||
self.pos_max = config.getfloat('position_max', None)
|
||||
# Setup iterative solver
|
||||
@ -48,21 +49,16 @@ class ManualStepper:
|
||||
else:
|
||||
self.next_cmd_time = print_time
|
||||
def do_enable(self, enable):
|
||||
self.sync_print_time()
|
||||
stepper_names = [s.get_name() for s in self.steppers]
|
||||
stepper_enable = self.printer.lookup_object('stepper_enable')
|
||||
if enable:
|
||||
for s in self.steppers:
|
||||
se = stepper_enable.lookup_enable(s.get_name())
|
||||
se.motor_enable(self.next_cmd_time)
|
||||
else:
|
||||
for s in self.steppers:
|
||||
se = stepper_enable.lookup_enable(s.get_name())
|
||||
se.motor_disable(self.next_cmd_time)
|
||||
self.sync_print_time()
|
||||
stepper_enable.set_motors_enable(stepper_names, enable)
|
||||
def do_set_position(self, setpos):
|
||||
self.rail.set_position([setpos, 0., 0.])
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.flush_step_generation()
|
||||
self.commanded_pos = setpos
|
||||
self.rail.set_position([self.commanded_pos, 0., 0.])
|
||||
def _submit_move(self, movetime, movepos, speed, accel):
|
||||
cp = self.rail.get_commanded_position()
|
||||
cp = self.commanded_pos
|
||||
dist = movepos - cp
|
||||
axis_r, accel_t, cruise_t, cruise_v = force_move.calc_move_time(
|
||||
dist, speed, accel)
|
||||
@ -70,13 +66,13 @@ class ManualStepper:
|
||||
accel_t, cruise_t, accel_t,
|
||||
cp, 0., 0., axis_r, 0., 0.,
|
||||
0., cruise_v, accel)
|
||||
self.commanded_pos = movepos
|
||||
return movetime + accel_t + cruise_t + accel_t
|
||||
def do_move(self, movepos, speed, accel, sync=True):
|
||||
self.sync_print_time()
|
||||
self.next_cmd_time = self._submit_move(self.next_cmd_time, movepos,
|
||||
speed, accel)
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.note_mcu_movequeue_activity(self.next_cmd_time)
|
||||
self.motion_queuing.note_mcu_movequeue_activity(self.next_cmd_time)
|
||||
if sync:
|
||||
self.sync_print_time()
|
||||
def do_homing_move(self, movepos, speed, accel, triggered, check_trigger):
|
||||
@ -149,7 +145,7 @@ class ManualStepper:
|
||||
self.instant_corner_v = instant_corner_v
|
||||
self.gaxis_limit_velocity = limit_velocity
|
||||
self.gaxis_limit_accel = limit_accel
|
||||
toolhead.add_extra_axis(self, self.get_position()[0])
|
||||
toolhead.add_extra_axis(self, self.commanded_pos)
|
||||
def process_move(self, print_time, move, ea_index):
|
||||
axis_r = move.axes_r[ea_index]
|
||||
start_pos = move.start_pos[ea_index]
|
||||
@ -161,6 +157,7 @@ class ManualStepper:
|
||||
start_pos, 0., 0.,
|
||||
1., 0., 0.,
|
||||
start_v, cruise_v, accel)
|
||||
self.commanded_pos = move.end_pos[ea_index]
|
||||
def check_move(self, move, ea_index):
|
||||
# Check move is in bounds
|
||||
movepos = move.end_pos[ea_index]
|
||||
@ -185,9 +182,10 @@ class ManualStepper:
|
||||
return self.trapq
|
||||
# Toolhead wrappers to support homing
|
||||
def flush_step_generation(self):
|
||||
self.sync_print_time()
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.flush_step_generation()
|
||||
def get_position(self):
|
||||
return [self.rail.get_commanded_position(), 0., 0., 0.]
|
||||
return [self.commanded_pos, 0., 0., 0.]
|
||||
def set_position(self, newpos, homing_axes=""):
|
||||
self.do_set_position(newpos[0])
|
||||
def get_last_move_time(self):
|
||||
@ -198,15 +196,16 @@ class ManualStepper:
|
||||
def drip_move(self, newpos, speed, drip_completion):
|
||||
# Submit move to trapq
|
||||
self.sync_print_time()
|
||||
maxtime = self._submit_move(self.next_cmd_time, newpos[0],
|
||||
speed, self.homing_accel)
|
||||
start_time = self.next_cmd_time
|
||||
end_time = self._submit_move(start_time, newpos[0],
|
||||
speed, self.homing_accel)
|
||||
# Drip updates to motors
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.drip_update_time(maxtime, drip_completion)
|
||||
self.motion_queuing.drip_update_time(start_time, end_time,
|
||||
drip_completion)
|
||||
# Clear trapq of any remaining parts of movement
|
||||
reactor = self.printer.get_reactor()
|
||||
self.motion_queuing.wipe_trapq(self.trapq)
|
||||
self.rail.set_position([newpos[0], 0., 0.])
|
||||
self.rail.set_position([self.commanded_pos, 0., 0.])
|
||||
self.sync_print_time()
|
||||
def get_kinematics(self):
|
||||
return self
|
||||
|
@ -6,23 +6,53 @@
|
||||
import logging
|
||||
import chelper
|
||||
|
||||
BGFLUSH_LOW_TIME = 0.200
|
||||
BGFLUSH_BATCH_TIME = 0.200
|
||||
BGFLUSH_EXTRA_TIME = 0.250
|
||||
MOVE_HISTORY_EXPIRE = 30.
|
||||
MIN_KIN_TIME = 0.100
|
||||
MOVE_BATCH_TIME = 0.500
|
||||
STEPCOMPRESS_FLUSH_TIME = 0.050
|
||||
SDS_CHECK_TIME = 0.001 # step+dir+step filter in stepcompress.c
|
||||
|
||||
DRIP_SEGMENT_TIME = 0.050
|
||||
DRIP_TIME = 0.100
|
||||
|
||||
class PrinterMotionQueuing:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.printer = printer = config.get_printer()
|
||||
self.reactor = printer.get_reactor()
|
||||
# Low level C allocations
|
||||
self.trapqs = []
|
||||
self.stepcompress = []
|
||||
self.steppersyncs = []
|
||||
self.flush_callbacks = []
|
||||
# Low-level C flushing calls
|
||||
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
|
||||
# Flush notification callbacks
|
||||
self.flush_callbacks = []
|
||||
# History expiration
|
||||
self.clear_history_time = 0.
|
||||
is_debug = self.printer.get_start_args().get('debugoutput') is not None
|
||||
self.is_debugoutput = is_debug
|
||||
# Flush tracking
|
||||
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
||||
self.do_kick_flush_timer = True
|
||||
self.last_flush_time = self.last_step_gen_time = 0.
|
||||
self.need_flush_time = self.need_step_gen_time = 0.
|
||||
self.check_flush_lookahead_cb = (lambda e: None)
|
||||
# MCU tracking
|
||||
self.all_mcus = [m for n, m in printer.lookup_objects(module='mcu')]
|
||||
self.mcu = self.all_mcus[0]
|
||||
self.can_pause = True
|
||||
if self.mcu.is_fileoutput():
|
||||
self.can_pause = False
|
||||
# Kinematic step generation scan window time tracking
|
||||
self.need_calc_kin_flush_delay = True
|
||||
self.kin_flush_delay = SDS_CHECK_TIME
|
||||
# Register handlers
|
||||
printer.register_event_handler("klippy:shutdown", self._handle_shutdown)
|
||||
def allocate_trapq(self):
|
||||
ffi_main, ffi_lib = chelper.get_ffi()
|
||||
trapq = ffi_main.gc(ffi_lib.trapq_alloc(), ffi_lib.trapq_free)
|
||||
@ -53,8 +83,7 @@ class PrinterMotionQueuing:
|
||||
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):
|
||||
def _flush_motion_queues(self, must_flush_time, max_step_gen_time):
|
||||
# Invoke flush callbacks (if any)
|
||||
for cb in self.flush_callbacks:
|
||||
cb(must_flush_time, max_step_gen_time)
|
||||
@ -72,8 +101,9 @@ class PrinterMotionQueuing:
|
||||
raise mcu.error("Internal error in MCU '%s' stepcompress"
|
||||
% (mcu.get_name(),))
|
||||
# Determine maximum history to keep
|
||||
trapq_free_time = max_step_gen_time - self.kin_flush_delay
|
||||
clear_history_time = self.clear_history_time
|
||||
if self.is_debugoutput:
|
||||
if not self.can_pause:
|
||||
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:
|
||||
@ -85,16 +115,127 @@ class PrinterMotionQueuing:
|
||||
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.)
|
||||
self.trapq_finalize_moves(trapq, self.reactor.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)
|
||||
# Hack to globally invoke mcu check_active()
|
||||
for m in self.all_mcus:
|
||||
m.check_active(self.last_step_gen_time, eventtime)
|
||||
# Calculate history expiration
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
self.clear_history_time = est_print_time - MOVE_HISTORY_EXPIRE
|
||||
return False, ""
|
||||
# Kinematic step generation scan window time tracking
|
||||
def get_kin_flush_delay(self):
|
||||
return self.kin_flush_delay
|
||||
def _calc_kin_flush_delay(self):
|
||||
self.need_calc_kin_flush_delay = False
|
||||
ffi_main, ffi_lib = chelper.get_ffi()
|
||||
kin_flush_delay = SDS_CHECK_TIME
|
||||
for mcu, sc in self.stepcompress:
|
||||
sk = ffi_lib.stepcompress_get_stepper_kinematics(sc)
|
||||
if sk == ffi_main.NULL:
|
||||
continue
|
||||
trapq = ffi_lib.itersolve_get_trapq(sk)
|
||||
if trapq == ffi_main.NULL:
|
||||
continue
|
||||
pre_active = ffi_lib.itersolve_get_gen_steps_pre_active(sk)
|
||||
post_active = ffi_lib.itersolve_get_gen_steps_post_active(sk)
|
||||
kin_flush_delay = max(kin_flush_delay, pre_active, post_active)
|
||||
self.kin_flush_delay = kin_flush_delay
|
||||
# Flush tracking
|
||||
def _handle_shutdown(self):
|
||||
self.can_pause = False
|
||||
def setup_lookahead_flush_callback(self, check_flush_lookahead_cb):
|
||||
self.check_flush_lookahead_cb = check_flush_lookahead_cb
|
||||
def advance_flush_time(self, target_time, lazy_target=False):
|
||||
want_flush_time = want_step_gen_time = target_time
|
||||
if lazy_target:
|
||||
# Account for step gen scan windows and optimize step compression
|
||||
want_step_gen_time -= self.kin_flush_delay
|
||||
want_flush_time = want_step_gen_time - STEPCOMPRESS_FLUSH_TIME
|
||||
want_flush_time = max(want_flush_time, self.last_flush_time)
|
||||
flush_time = self.last_flush_time
|
||||
if want_flush_time > flush_time + 10. * MOVE_BATCH_TIME:
|
||||
# Use closer startup time when coming out of idle state
|
||||
curtime = self.reactor.monotonic()
|
||||
est_print_time = self.mcu.estimated_print_time(curtime)
|
||||
flush_time = max(flush_time, est_print_time)
|
||||
while 1:
|
||||
flush_time = min(flush_time + MOVE_BATCH_TIME, want_flush_time)
|
||||
# Generate steps via itersolve
|
||||
want_sg_wave = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
||||
want_step_gen_time)
|
||||
step_gen_time = max(self.last_step_gen_time, want_sg_wave,
|
||||
flush_time)
|
||||
self._flush_motion_queues(flush_time, step_gen_time)
|
||||
self.last_flush_time = flush_time
|
||||
self.last_step_gen_time = step_gen_time
|
||||
if flush_time >= want_flush_time:
|
||||
break
|
||||
def flush_all_steps(self):
|
||||
self.need_calc_kin_flush_delay = True
|
||||
self.advance_flush_time(self.need_step_gen_time)
|
||||
def calc_step_gen_restart(self, est_print_time):
|
||||
if self.need_calc_kin_flush_delay:
|
||||
self._calc_kin_flush_delay()
|
||||
kin_time = max(est_print_time + MIN_KIN_TIME, self.last_step_gen_time)
|
||||
return kin_time + self.kin_flush_delay
|
||||
def _flush_handler(self, eventtime):
|
||||
try:
|
||||
# Check if flushing is done via lookahead queue
|
||||
ret = self.check_flush_lookahead_cb(eventtime)
|
||||
if ret is not None:
|
||||
return ret
|
||||
# Flush motion queues
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
while 1:
|
||||
end_flush = self.need_flush_time + BGFLUSH_EXTRA_TIME
|
||||
if self.last_flush_time >= end_flush:
|
||||
self.do_kick_flush_timer = True
|
||||
return self.reactor.NEVER
|
||||
buffer_time = self.last_flush_time - est_print_time
|
||||
if buffer_time > BGFLUSH_LOW_TIME:
|
||||
return eventtime + buffer_time - BGFLUSH_LOW_TIME
|
||||
ftime = est_print_time + BGFLUSH_LOW_TIME + BGFLUSH_BATCH_TIME
|
||||
self.advance_flush_time(min(end_flush, ftime))
|
||||
except:
|
||||
logging.exception("Exception in flush_handler")
|
||||
self.printer.invoke_shutdown("Exception in flush_handler")
|
||||
return self.reactor.NEVER
|
||||
def note_mcu_movequeue_activity(self, mq_time, is_step_gen=True):
|
||||
if is_step_gen:
|
||||
mq_time += self.kin_flush_delay
|
||||
self.need_step_gen_time = max(self.need_step_gen_time, mq_time)
|
||||
self.need_flush_time = max(self.need_flush_time, mq_time)
|
||||
if self.do_kick_flush_timer:
|
||||
self.do_kick_flush_timer = False
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||
def drip_update_time(self, start_time, end_time, drip_completion):
|
||||
# Disable background flushing from timer
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
|
||||
self.do_kick_flush_timer = False
|
||||
# Flush in segments until drip_completion signal
|
||||
flush_delay = DRIP_TIME + STEPCOMPRESS_FLUSH_TIME + self.kin_flush_delay
|
||||
flush_time = start_time
|
||||
while flush_time < end_time:
|
||||
if drip_completion.test():
|
||||
break
|
||||
curtime = self.reactor.monotonic()
|
||||
est_print_time = self.mcu.estimated_print_time(curtime)
|
||||
wait_time = flush_time - est_print_time - flush_delay
|
||||
if wait_time > 0. and self.can_pause:
|
||||
# Pause before sending more steps
|
||||
drip_completion.wait(curtime + wait_time)
|
||||
continue
|
||||
flush_time = min(flush_time + DRIP_SEGMENT_TIME, end_time)
|
||||
self.note_mcu_movequeue_activity(flush_time)
|
||||
self.advance_flush_time(flush_time, lazy_target=True)
|
||||
# Restore background flushing
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||
self.advance_flush_time(self.need_step_gen_time)
|
||||
|
||||
def load_config(config):
|
||||
return PrinterMotionQueuing(config)
|
||||
|
@ -20,8 +20,8 @@ class GCodeRequestQueue:
|
||||
self.rqueue = []
|
||||
self.next_min_flush_time = 0.
|
||||
self.toolhead = None
|
||||
motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||
motion_queuing.register_flush_callback(self._flush_notification)
|
||||
self.motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||
self.motion_queuing.register_flush_callback(self._flush_notification)
|
||||
printer.register_event_handler("klippy:connect", self._handle_connect)
|
||||
def _handle_connect(self):
|
||||
self.toolhead = self.printer.lookup_object('toolhead')
|
||||
@ -51,11 +51,12 @@ class GCodeRequestQueue:
|
||||
del rqueue[:pos+1]
|
||||
self.next_min_flush_time = next_time + max(min_wait, min_sched_time)
|
||||
# Ensure following queue items are flushed
|
||||
self.toolhead.note_mcu_movequeue_activity(self.next_min_flush_time,
|
||||
is_step_gen=False)
|
||||
self.motion_queuing.note_mcu_movequeue_activity(
|
||||
self.next_min_flush_time, is_step_gen=False)
|
||||
def _queue_request(self, print_time, value):
|
||||
self.rqueue.append((print_time, value))
|
||||
self.toolhead.note_mcu_movequeue_activity(print_time, is_step_gen=False)
|
||||
self.motion_queuing.note_mcu_movequeue_activity(
|
||||
print_time, is_step_gen=False)
|
||||
def queue_gcode_request(self, value):
|
||||
self.toolhead.register_lookahead_callback(
|
||||
(lambda pt: self._queue_request(pt, value)))
|
||||
|
@ -16,8 +16,8 @@ class MCU_queued_pwm:
|
||||
self._max_duration = 2.
|
||||
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)
|
||||
self._motion_queuing = printer.load_object(config, 'motion_queuing')
|
||||
self._stepqueue = self._motion_queuing.allocate_stepcompress(mcu, oid)
|
||||
ffi_main, ffi_lib = chelper.get_ffi()
|
||||
self._stepcompress_queue_mq_msg = ffi_lib.stepcompress_queue_mq_msg
|
||||
mcu.register_config_callback(self._build_config)
|
||||
@ -62,8 +62,8 @@ class MCU_queued_pwm:
|
||||
if self._duration_ticks >= 1<<31:
|
||||
raise config_error("PWM pin max duration too large")
|
||||
if self._duration_ticks:
|
||||
motion_queuing = printer.lookup_object('motion_queuing')
|
||||
motion_queuing.register_flush_callback(self._flush_notification)
|
||||
self._motion_queuing.register_flush_callback(
|
||||
self._flush_notification)
|
||||
if self._hardware_pwm:
|
||||
self._pwm_max = self._mcu.get_constant_float("PWM_MAX")
|
||||
self._default_value = self._shutdown_value * self._pwm_max
|
||||
@ -116,8 +116,8 @@ class MCU_queued_pwm:
|
||||
# Continue flushing to resend time
|
||||
wakeclock += self._duration_ticks
|
||||
wake_print_time = self._mcu.clock_to_print_time(wakeclock)
|
||||
self._toolhead.note_mcu_movequeue_activity(wake_print_time,
|
||||
is_step_gen=False)
|
||||
self._motion_queuing.note_mcu_movequeue_activity(wake_print_time,
|
||||
is_step_gen=False)
|
||||
def set_pwm(self, print_time, value):
|
||||
clock = self._mcu.print_time_to_clock(print_time)
|
||||
if self._invert:
|
||||
|
@ -80,10 +80,10 @@ class SHT3X:
|
||||
|
||||
def _init_sht3x(self):
|
||||
# Device Soft Reset
|
||||
self.i2c.i2c_write_wait_ack(SHT3X_CMD['OTHER']['BREAK'])
|
||||
self.i2c.i2c_write(SHT3X_CMD['OTHER']['BREAK'])
|
||||
# Break takes ~ 1ms
|
||||
self.reactor.pause(self.reactor.monotonic() + .0015)
|
||||
self.i2c.i2c_write_wait_ack(SHT3X_CMD['OTHER']['SOFTRESET'])
|
||||
self.i2c.i2c_write(SHT3X_CMD['OTHER']['SOFTRESET'])
|
||||
# Wait <=1.5ms after reset
|
||||
self.reactor.pause(self.reactor.monotonic() + .0015)
|
||||
|
||||
@ -97,7 +97,7 @@ class SHT3X:
|
||||
logging.warning("sht3x: Reading status - checksum error!")
|
||||
|
||||
# Enable periodic mode
|
||||
self.i2c.i2c_write_wait_ack(
|
||||
self.i2c.i2c_write(
|
||||
SHT3X_CMD['PERIODIC']['2HZ']['HIGH_REP']
|
||||
)
|
||||
# Wait <=15.5ms for first measurement
|
||||
|
@ -1,6 +1,6 @@
|
||||
# Support for enable pins on stepper motor drivers
|
||||
#
|
||||
# Copyright (C) 2019-2021 Kevin O'Connor <kevin@koconnor.net>
|
||||
# Copyright (C) 2019-2025 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
@ -88,30 +88,38 @@ class PrinterStepperEnable:
|
||||
name = mcu_stepper.get_name()
|
||||
enable = setup_enable_pin(self.printer, config.get('enable_pin', None))
|
||||
self.enable_lines[name] = EnableTracking(mcu_stepper, enable)
|
||||
def set_motors_enable(self, stepper_names, enable):
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
# Flush steps to ensure all auto enable callbacks invoked
|
||||
toolhead.flush_step_generation()
|
||||
print_time = None
|
||||
did_change = False
|
||||
for stepper_name in stepper_names:
|
||||
el = self.enable_lines[stepper_name]
|
||||
if el.is_motor_enabled() == enable:
|
||||
continue
|
||||
if print_time is None:
|
||||
# Dwell for sufficient delay from any previous auto enable
|
||||
if not enable:
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
print_time = toolhead.get_last_move_time()
|
||||
if enable:
|
||||
el.motor_enable(print_time)
|
||||
else:
|
||||
el.motor_disable(print_time)
|
||||
did_change = True
|
||||
# Dwell to ensure sufficient delay prior to a future auto enable
|
||||
if did_change and not enable:
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
return did_change
|
||||
def motor_off(self):
|
||||
self.set_motors_enable(self.get_steppers(), False)
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
print_time = toolhead.get_last_move_time()
|
||||
for el in self.enable_lines.values():
|
||||
el.motor_disable(print_time)
|
||||
toolhead.get_kinematics().clear_homing_state("xyz")
|
||||
self.printer.send_event("stepper_enable:motor_off", print_time)
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
def motor_debug_enable(self, stepper, enable):
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
print_time = toolhead.get_last_move_time()
|
||||
el = self.enable_lines[stepper]
|
||||
if enable:
|
||||
el.motor_enable(print_time)
|
||||
logging.info("%s has been manually enabled", stepper)
|
||||
else:
|
||||
el.motor_disable(print_time)
|
||||
logging.info("%s has been manually disabled", stepper)
|
||||
toolhead.dwell(DISABLE_STALL_TIME)
|
||||
self.printer.send_event("stepper_enable:motor_off")
|
||||
def get_status(self, eventtime):
|
||||
steppers = { name: et.is_motor_enabled()
|
||||
for (name, et) in self.enable_lines.items() }
|
||||
for (name, et) in self.enable_lines.items() }
|
||||
return {'steppers': steppers}
|
||||
def _handle_request_restart(self, print_time):
|
||||
self.motor_off()
|
||||
@ -126,7 +134,11 @@ class PrinterStepperEnable:
|
||||
% (stepper_name,))
|
||||
return
|
||||
stepper_enable = gcmd.get_int('ENABLE', 1)
|
||||
self.motor_debug_enable(stepper_name, stepper_enable)
|
||||
self.set_motors_enable([stepper_name], stepper_enable)
|
||||
if stepper_enable:
|
||||
logging.info("%s has been manually enabled", stepper_name)
|
||||
else:
|
||||
logging.info("%s has been manually disabled", stepper_name)
|
||||
def lookup_enable(self, name):
|
||||
if name not in self.enable_lines:
|
||||
raise self.printer.config_error("Unknown stepper '%s'" % (name,))
|
||||
|
@ -29,7 +29,6 @@ class SX1509(object):
|
||||
self._ppins = self._printer.lookup_object("pins")
|
||||
self._ppins.register_chip("sx1509_" + self._name, self)
|
||||
self._mcu = self._i2c.get_mcu()
|
||||
self._mcu.register_config_callback(self._build_config)
|
||||
self._oid = self._i2c.get_oid()
|
||||
self._last_clock = 0
|
||||
# Set up registers default values
|
||||
@ -37,22 +36,26 @@ class SX1509(object):
|
||||
REG_PULLUP : 0, REG_PULLDOWN : 0,
|
||||
REG_INPUT_DISABLE : 0, REG_ANALOG_DRIVER_ENABLE : 0}
|
||||
self.reg_i_on_dict = {reg : 0 for reg in REG_I_ON}
|
||||
def _build_config(self):
|
||||
config.get_printer().register_event_handler("klippy:connect",
|
||||
self.handle_connect)
|
||||
def handle_connect(self):
|
||||
# Reset the chip, Default RegClock/RegMisc 0x0
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||
self._oid, REG_RESET, 0x12))
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||
self._oid, REG_RESET, 0x34))
|
||||
self._i2c.i2c_write([REG_RESET, 0x12])
|
||||
self._i2c.i2c_write([REG_RESET, 0x34])
|
||||
# Enable Oscillator
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||
self._oid, REG_CLOCK, (1 << 6)))
|
||||
self._i2c.i2c_write([REG_CLOCK, (1 << 6)])
|
||||
# Setup Clock Divider
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||
self._oid, REG_MISC, (1 << 4)))
|
||||
self._i2c.i2c_write([REG_MISC, (1 << 4)])
|
||||
# Transfer all regs with their initial cached state
|
||||
for _reg, _data in self.reg_dict.items():
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%04x" % (
|
||||
self._oid, _reg, _data), is_init=True)
|
||||
reactor = self._mcu.get_printer().get_reactor()
|
||||
for _reg in self.reg_dict:
|
||||
curtime = reactor.monotonic()
|
||||
printtime = self._mcu.estimated_print_time(curtime)
|
||||
self.send_register(_reg, printtime)
|
||||
for reg in self.reg_i_on_dict:
|
||||
curtime = reactor.monotonic()
|
||||
printtime = self._mcu.estimated_print_time(curtime)
|
||||
self.send_register(reg, printtime)
|
||||
def setup_pin(self, pin_type, pin_params):
|
||||
if pin_type == 'digital_out' and pin_params['pin'][0:4] == "PIN_":
|
||||
return SX1509_digital_out(self, pin_params)
|
||||
@ -159,15 +162,6 @@ class SX1509_pwm(object):
|
||||
raise pins.error("SX1509_pwm must have hardware_pwm enabled")
|
||||
if self._max_duration:
|
||||
raise pins.error("SX1509 pins are not suitable for heaters")
|
||||
# Send initial value
|
||||
self._sx1509.set_register(self._i_on_reg,
|
||||
~int(255 * self._start_value) & 0xFF)
|
||||
self._mcu.add_config_cmd("i2c_write oid=%d data=%02x%02x" % (
|
||||
self._sx1509.get_oid(),
|
||||
self._i_on_reg,
|
||||
self._sx1509.reg_i_on_dict[self._i_on_reg]
|
||||
),
|
||||
is_init=True)
|
||||
def get_mcu(self):
|
||||
return self._mcu
|
||||
def setup_max_duration(self, max_duration):
|
||||
@ -181,6 +175,8 @@ class SX1509_pwm(object):
|
||||
shutdown_value = 1. - shutdown_value
|
||||
self._start_value = max(0., min(1., start_value))
|
||||
self._shutdown_value = max(0., min(1., shutdown_value))
|
||||
self._sx1509.set_register(self._i_on_reg,
|
||||
~int(255 * self._start_value) & 0xFF)
|
||||
def set_pwm(self, print_time, value):
|
||||
self._sx1509.set_register(self._i_on_reg, ~int(255 * value)
|
||||
if not self._invert
|
||||
|
@ -132,7 +132,7 @@ class TemperatureProbe:
|
||||
self.start_pos = []
|
||||
|
||||
# Register GCode Commands
|
||||
pname = self.name.split(maxsplit=1)[-1]
|
||||
pname = self.name.split(None, 1)[-1]
|
||||
self.gcode.register_mux_command(
|
||||
"TEMPERATURE_PROBE_CALIBRATE", "PROBE", pname,
|
||||
self.cmd_TEMPERATURE_PROBE_CALIBRATE,
|
||||
@ -357,8 +357,8 @@ class TemperatureProbe:
|
||||
self._check_homed()
|
||||
probe = self._get_probe()
|
||||
probe_name = probe.get_status(None)["name"]
|
||||
short_name = probe_name.split(maxsplit=1)[-1]
|
||||
if short_name != self.name.split(maxsplit=1)[-1]:
|
||||
short_name = probe_name.split(None, 1)[-1]
|
||||
if short_name != self.name.split(None, 1)[-1]:
|
||||
raise self.gcode.error(
|
||||
"[%s] not linked to registered probe [%s]."
|
||||
% (self.name, probe_name)
|
||||
@ -588,7 +588,7 @@ class EddyDriftCompensation:
|
||||
temps[idx] = cur_temp
|
||||
probe_samples[idx].append(sample)
|
||||
return True
|
||||
sect_name = "probe_eddy_current " + self.name.split(maxsplit=1)[-1]
|
||||
sect_name = "probe_eddy_current " + self.name.split(None, 1)[-1]
|
||||
self.printer.lookup_object(sect_name).add_client(_on_bulk_data_recd)
|
||||
for i in range(DRIFT_SAMPLE_COUNT):
|
||||
if i == 0:
|
||||
|
@ -262,6 +262,9 @@ FieldFormatters.update({
|
||||
"adc_temp": (lambda v: "0x%04x(%.1fC)" % (v, ((v - 2038) / 7.7))),
|
||||
"adc_vsupply": (lambda v: "0x%04x(%.3fV)" % (v, v * 0.009732)),
|
||||
"adc_ain": (lambda v: "0x%04x(%.3fmV)" % (v, v * 0.3052)),
|
||||
"overvoltage_vth": (lambda v: "0x%04x(%.3fV)" % (v, v * 0.009732)),
|
||||
"overtempprewarning_vth": (lambda v:
|
||||
"0x%04x(%.1fC)" % (v, ((v - 2038) / 7.7))),
|
||||
})
|
||||
|
||||
|
||||
|
@ -79,7 +79,7 @@ class ZAdjustStatus:
|
||||
self.applied = False
|
||||
def get_status(self, eventtime):
|
||||
return {'applied': self.applied}
|
||||
def _motor_off(self, print_time):
|
||||
def _motor_off(self):
|
||||
self.reset()
|
||||
|
||||
class RetryHelper:
|
||||
|
@ -430,18 +430,17 @@ class GCodeIO:
|
||||
self.gcode.request_restart('exit')
|
||||
pending_commands.append("")
|
||||
# Handle case where multiple commands pending
|
||||
if self.is_processing_data or len(pending_commands) > 1:
|
||||
if len(pending_commands) < 20:
|
||||
# Check for M112 out-of-order
|
||||
for line in lines:
|
||||
if self.m112_r.match(line) is not None:
|
||||
self.gcode.cmd_M112(None)
|
||||
if self.is_processing_data:
|
||||
if len(pending_commands) >= 20:
|
||||
# Stop reading input
|
||||
self.reactor.unregister_fd(self.fd_handle)
|
||||
self.fd_handle = None
|
||||
return
|
||||
if len(pending_commands) < 20:
|
||||
# Check for M112 out-of-order
|
||||
for line in lines:
|
||||
if self.m112_r.match(line) is not None:
|
||||
self.gcode.cmd_M112(None)
|
||||
if self.is_processing_data:
|
||||
if len(pending_commands) >= 20:
|
||||
# Stop reading input
|
||||
self.reactor.unregister_fd(self.fd_handle)
|
||||
self.fd_handle = None
|
||||
return
|
||||
# Process commands
|
||||
self.is_processing_data = True
|
||||
while pending_commands:
|
||||
|
@ -69,14 +69,16 @@ class ExtruderStepper:
|
||||
if not pressure_advance:
|
||||
new_smooth_time = 0.
|
||||
toolhead = self.printer.lookup_object("toolhead")
|
||||
if new_smooth_time != old_smooth_time:
|
||||
toolhead.note_step_generation_scan_time(
|
||||
new_smooth_time * .5, old_delay=old_smooth_time * .5)
|
||||
ffi_main, ffi_lib = chelper.get_ffi()
|
||||
espa = ffi_lib.extruder_set_pressure_advance
|
||||
toolhead.register_lookahead_callback(
|
||||
lambda print_time: espa(self.sk_extruder, print_time,
|
||||
pressure_advance, new_smooth_time))
|
||||
if new_smooth_time != old_smooth_time:
|
||||
# Need full kinematic flush to change the smooth time
|
||||
toolhead.flush_step_generation()
|
||||
espa(self.sk_extruder, 0., pressure_advance, new_smooth_time)
|
||||
else:
|
||||
toolhead.register_lookahead_callback(
|
||||
lambda print_time: espa(self.sk_extruder, print_time,
|
||||
pressure_advance, new_smooth_time))
|
||||
self.pressure_advance = pressure_advance
|
||||
self.pressure_advance_smooth_time = smooth_time
|
||||
cmd_SET_PRESSURE_ADVANCE_help = "Set pressure advance parameters"
|
||||
|
@ -84,7 +84,7 @@ class CommandQueryWrapper:
|
||||
|
||||
# Wrapper around command sending
|
||||
class CommandWrapper:
|
||||
def __init__(self, serial, msgformat, cmd_queue=None):
|
||||
def __init__(self, serial, msgformat, cmd_queue=None, debugoutput=False):
|
||||
self._serial = serial
|
||||
msgparser = serial.get_msgparser()
|
||||
self._cmd = msgparser.lookup_command(msgformat)
|
||||
@ -92,6 +92,9 @@ class CommandWrapper:
|
||||
cmd_queue = serial.get_default_command_queue()
|
||||
self._cmd_queue = cmd_queue
|
||||
self._msgtag = msgparser.lookup_msgid(msgformat) & 0xffffffff
|
||||
if debugoutput:
|
||||
# Can't use send_wait_ack when in debugging mode
|
||||
self.send_wait_ack = self.send
|
||||
def send(self, data=(), minclock=0, reqclock=0):
|
||||
cmd = self._cmd.encode(data)
|
||||
self._serial.raw_send(cmd, minclock, reqclock, self._cmd_queue)
|
||||
@ -888,7 +891,8 @@ class MCU:
|
||||
def alloc_command_queue(self):
|
||||
return self._serial.alloc_command_queue()
|
||||
def lookup_command(self, msgformat, cq=None):
|
||||
return CommandWrapper(self._serial, msgformat, cq)
|
||||
return CommandWrapper(self._serial, msgformat, cq,
|
||||
debugoutput=self.is_fileoutput())
|
||||
def lookup_query_command(self, msgformat, respformat, oid=None,
|
||||
cq=None, is_async=False):
|
||||
return CommandQueryWrapper(self._serial, msgformat, respformat, oid,
|
||||
|
@ -1,6 +1,6 @@
|
||||
# File descriptor and timer event helper
|
||||
#
|
||||
# Copyright (C) 2016-2020 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.
|
||||
import os, gc, select, math, time, logging, queue
|
||||
@ -14,6 +14,7 @@ class ReactorTimer:
|
||||
def __init__(self, callback, waketime):
|
||||
self.callback = callback
|
||||
self.waketime = waketime
|
||||
self.timer_is_running = False
|
||||
|
||||
class ReactorCompletion:
|
||||
class sentinel: pass
|
||||
@ -118,6 +119,8 @@ class SelectReactor:
|
||||
return tuple(self._last_gc_times)
|
||||
# Timers
|
||||
def update_timer(self, timer_handler, waketime):
|
||||
if timer_handler.timer_is_running:
|
||||
return
|
||||
timer_handler.waketime = waketime
|
||||
self._next_timer = min(self._next_timer, waketime)
|
||||
def register_timer(self, callback, waketime=NEVER):
|
||||
@ -155,7 +158,9 @@ class SelectReactor:
|
||||
waketime = t.waketime
|
||||
if eventtime >= waketime:
|
||||
t.waketime = self.NEVER
|
||||
t.timer_is_running = True
|
||||
t.waketime = waketime = t.callback(eventtime)
|
||||
t.timer_is_running = False
|
||||
if g_dispatch is not self._g_dispatch:
|
||||
self._next_timer = min(self._next_timer, waketime)
|
||||
self._end_greenlet(g_dispatch)
|
||||
|
@ -193,25 +193,13 @@ class LookAheadQueue:
|
||||
BUFFER_TIME_LOW = 1.0
|
||||
BUFFER_TIME_HIGH = 2.0
|
||||
BUFFER_TIME_START = 0.250
|
||||
BGFLUSH_LOW_TIME = 0.200
|
||||
BGFLUSH_BATCH_TIME = 0.200
|
||||
BGFLUSH_EXTRA_TIME = 0.250
|
||||
MIN_KIN_TIME = 0.100
|
||||
MOVE_BATCH_TIME = 0.500
|
||||
STEPCOMPRESS_FLUSH_TIME = 0.050
|
||||
SDS_CHECK_TIME = 0.001 # step+dir+step filter in stepcompress.c
|
||||
|
||||
DRIP_SEGMENT_TIME = 0.050
|
||||
DRIP_TIME = 0.100
|
||||
|
||||
# Main code to track events (and their timing) on the printer toolhead
|
||||
class ToolHead:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
self.reactor = self.printer.get_reactor()
|
||||
self.all_mcus = [
|
||||
m for n, m in self.printer.lookup_objects(module='mcu')]
|
||||
self.mcu = self.all_mcus[0]
|
||||
self.mcu = self.printer.lookup_object('mcu')
|
||||
self.lookahead = LookAheadQueue()
|
||||
self.lookahead.set_flush_time(BUFFER_TIME_HIGH)
|
||||
self.commanded_pos = [0., 0., 0., 0.]
|
||||
@ -236,16 +224,10 @@ class ToolHead:
|
||||
self.print_time = 0.
|
||||
self.special_queuing_state = "NeedPrime"
|
||||
self.priming_timer = None
|
||||
# Flush tracking
|
||||
self.flush_timer = self.reactor.register_timer(self._flush_handler)
|
||||
self.do_kick_flush_timer = True
|
||||
self.last_flush_time = self.min_restart_time = 0.
|
||||
self.need_flush_time = self.step_gen_time = 0.
|
||||
# Kinematic step generation scan window time tracking
|
||||
self.kin_flush_delay = SDS_CHECK_TIME
|
||||
self.kin_flush_times = []
|
||||
# Setup for generating moves
|
||||
self.motion_queuing = self.printer.load_object(config, 'motion_queuing')
|
||||
self.motion_queuing.setup_lookahead_flush_callback(
|
||||
self._check_flush_lookahead)
|
||||
self.trapq = self.motion_queuing.allocate_trapq()
|
||||
self.trapq_append = self.motion_queuing.lookup_trapq_append()
|
||||
# Create kinematics class
|
||||
@ -268,33 +250,15 @@ class ToolHead:
|
||||
# Register handlers
|
||||
self.printer.register_event_handler("klippy:shutdown",
|
||||
self._handle_shutdown)
|
||||
# Print time and flush tracking
|
||||
def _advance_flush_time(self, flush_time):
|
||||
flush_time = max(flush_time, self.last_flush_time)
|
||||
# Generate steps via itersolve
|
||||
sg_flush_want = min(flush_time + STEPCOMPRESS_FLUSH_TIME,
|
||||
self.print_time - self.kin_flush_delay)
|
||||
sg_flush_time = max(sg_flush_want, flush_time)
|
||||
trapq_free_time = sg_flush_time - self.kin_flush_delay
|
||||
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.last_flush_time = flush_time
|
||||
# Print time tracking
|
||||
def _advance_move_time(self, next_print_time):
|
||||
pt_delay = self.kin_flush_delay + STEPCOMPRESS_FLUSH_TIME
|
||||
flush_time = max(self.last_flush_time, self.print_time - pt_delay)
|
||||
self.print_time = max(self.print_time, next_print_time)
|
||||
want_flush_time = max(flush_time, self.print_time - pt_delay)
|
||||
while 1:
|
||||
flush_time = min(flush_time + MOVE_BATCH_TIME, want_flush_time)
|
||||
self._advance_flush_time(flush_time)
|
||||
if flush_time >= want_flush_time:
|
||||
break
|
||||
self.motion_queuing.advance_flush_time(self.print_time,
|
||||
lazy_target=True)
|
||||
def _calc_print_time(self):
|
||||
curtime = self.reactor.monotonic()
|
||||
est_print_time = self.mcu.estimated_print_time(curtime)
|
||||
kin_time = max(est_print_time + MIN_KIN_TIME, self.min_restart_time)
|
||||
kin_time += self.kin_flush_delay
|
||||
kin_time = self.motion_queuing.calc_step_gen_restart(est_print_time)
|
||||
min_print_time = max(est_print_time + BUFFER_TIME_START, kin_time)
|
||||
if min_print_time > self.print_time:
|
||||
self.print_time = min_print_time
|
||||
@ -328,10 +292,10 @@ class ToolHead:
|
||||
for cb in move.timing_callbacks:
|
||||
cb(next_move_time)
|
||||
# Generate steps for moves
|
||||
self.note_mcu_movequeue_activity(next_move_time + self.kin_flush_delay)
|
||||
self.motion_queuing.note_mcu_movequeue_activity(next_move_time)
|
||||
self._advance_move_time(next_move_time)
|
||||
def _flush_lookahead(self):
|
||||
# Transit from "NeedPrime"/"Priming"/"Drip"/main state to "NeedPrime"
|
||||
# Transit from "NeedPrime"/"Priming"/main state to "NeedPrime"
|
||||
self._process_lookahead()
|
||||
self.special_queuing_state = "NeedPrime"
|
||||
self.need_check_pause = -1.
|
||||
@ -339,8 +303,7 @@ class ToolHead:
|
||||
self.check_stall_time = 0.
|
||||
def flush_step_generation(self):
|
||||
self._flush_lookahead()
|
||||
self._advance_flush_time(self.step_gen_time)
|
||||
self.min_restart_time = max(self.min_restart_time, self.print_time)
|
||||
self.motion_queuing.flush_all_steps()
|
||||
def get_last_move_time(self):
|
||||
if self.special_queuing_state:
|
||||
self._flush_lookahead()
|
||||
@ -391,35 +354,21 @@ class ToolHead:
|
||||
logging.exception("Exception in priming_handler")
|
||||
self.printer.invoke_shutdown("Exception in priming_handler")
|
||||
return self.reactor.NEVER
|
||||
def _flush_handler(self, eventtime):
|
||||
try:
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
if not self.special_queuing_state:
|
||||
# In "main" state - flush lookahead if buffer runs low
|
||||
print_time = self.print_time
|
||||
buffer_time = print_time - est_print_time
|
||||
if buffer_time > BUFFER_TIME_LOW:
|
||||
# Running normally - reschedule check
|
||||
return eventtime + buffer_time - BUFFER_TIME_LOW
|
||||
# Under ran low buffer mark - flush lookahead queue
|
||||
self._flush_lookahead()
|
||||
if print_time != self.print_time:
|
||||
self.check_stall_time = self.print_time
|
||||
# In "NeedPrime"/"Priming" state - flush queues if needed
|
||||
while 1:
|
||||
end_flush = self.need_flush_time + BGFLUSH_EXTRA_TIME
|
||||
if self.last_flush_time >= end_flush:
|
||||
self.do_kick_flush_timer = True
|
||||
return self.reactor.NEVER
|
||||
buffer_time = self.last_flush_time - est_print_time
|
||||
if buffer_time > BGFLUSH_LOW_TIME:
|
||||
return eventtime + buffer_time - BGFLUSH_LOW_TIME
|
||||
ftime = est_print_time + BGFLUSH_LOW_TIME + BGFLUSH_BATCH_TIME
|
||||
self._advance_flush_time(min(end_flush, ftime))
|
||||
except:
|
||||
logging.exception("Exception in flush_handler")
|
||||
self.printer.invoke_shutdown("Exception in flush_handler")
|
||||
return self.reactor.NEVER
|
||||
def _check_flush_lookahead(self, eventtime):
|
||||
if self.special_queuing_state:
|
||||
return None
|
||||
# In "main" state - flush lookahead if buffer runs low
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
print_time = self.print_time
|
||||
buffer_time = print_time - est_print_time
|
||||
if buffer_time > BUFFER_TIME_LOW:
|
||||
# Running normally - reschedule check
|
||||
return eventtime + buffer_time - BUFFER_TIME_LOW
|
||||
# Under ran low buffer mark - flush lookahead queue
|
||||
self._flush_lookahead()
|
||||
if print_time != self.print_time:
|
||||
self.check_stall_time = self.print_time
|
||||
return None
|
||||
# Movement commands
|
||||
def get_position(self):
|
||||
return list(self.commanded_pos)
|
||||
@ -491,32 +440,6 @@ class ToolHead:
|
||||
def get_extra_axes(self):
|
||||
return [None, None, None] + self.extra_axes
|
||||
# Homing "drip move" handling
|
||||
def drip_update_time(self, next_print_time, drip_completion):
|
||||
# Transition from "NeedPrime"/"Priming"/main state to "Drip" state
|
||||
self.special_queuing_state = "Drip"
|
||||
self.need_check_pause = self.reactor.NEVER
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NEVER)
|
||||
self.do_kick_flush_timer = False
|
||||
self.lookahead.set_flush_time(BUFFER_TIME_HIGH)
|
||||
self.check_stall_time = 0.
|
||||
# Update print_time in segments until drip_completion signal
|
||||
flush_delay = DRIP_TIME + STEPCOMPRESS_FLUSH_TIME + self.kin_flush_delay
|
||||
while self.print_time < next_print_time:
|
||||
if drip_completion.test():
|
||||
break
|
||||
curtime = self.reactor.monotonic()
|
||||
est_print_time = self.mcu.estimated_print_time(curtime)
|
||||
wait_time = self.print_time - est_print_time - flush_delay
|
||||
if wait_time > 0. and self.can_pause:
|
||||
# Pause before sending more steps
|
||||
drip_completion.wait(curtime + wait_time)
|
||||
continue
|
||||
npt = min(self.print_time + DRIP_SEGMENT_TIME, next_print_time)
|
||||
self.note_mcu_movequeue_activity(npt + self.kin_flush_delay)
|
||||
self._advance_move_time(npt)
|
||||
# Exit "Drip" state
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||
self.flush_step_generation()
|
||||
def _drip_load_trapq(self, submit_move):
|
||||
# Queue move into trapezoid motion queue (trapq)
|
||||
if submit_move.move_d:
|
||||
@ -524,18 +447,17 @@ class ToolHead:
|
||||
self.lookahead.add_move(submit_move)
|
||||
moves = self.lookahead.flush()
|
||||
self._calc_print_time()
|
||||
next_move_time = self.print_time
|
||||
start_time = end_time = self.print_time
|
||||
for move in moves:
|
||||
self.trapq_append(
|
||||
self.trapq, next_move_time,
|
||||
self.trapq, end_time,
|
||||
move.accel_t, move.cruise_t, move.decel_t,
|
||||
move.start_pos[0], move.start_pos[1], move.start_pos[2],
|
||||
move.axes_r[0], move.axes_r[1], move.axes_r[2],
|
||||
move.start_v, move.cruise_v, move.accel)
|
||||
next_move_time = (next_move_time + move.accel_t
|
||||
+ move.cruise_t + move.decel_t)
|
||||
end_time = end_time + move.accel_t + move.cruise_t + move.decel_t
|
||||
self.lookahead.reset()
|
||||
return next_move_time
|
||||
return start_time, end_time
|
||||
def drip_move(self, newpos, speed, drip_completion):
|
||||
# Create and verify move is valid
|
||||
newpos = newpos[:3] + self.commanded_pos[3:]
|
||||
@ -543,23 +465,20 @@ class ToolHead:
|
||||
if move.move_d:
|
||||
self.kin.check_move(move)
|
||||
# Make sure stepper movement doesn't start before nominal start time
|
||||
self.dwell(self.kin_flush_delay)
|
||||
kin_flush_delay = self.motion_queuing.get_kin_flush_delay()
|
||||
self.dwell(kin_flush_delay)
|
||||
# Transmit move in "drip" mode
|
||||
self._process_lookahead()
|
||||
next_move_time = self._drip_load_trapq(move)
|
||||
self.drip_update_time(next_move_time, drip_completion)
|
||||
start_time, end_time = self._drip_load_trapq(move)
|
||||
self.motion_queuing.drip_update_time(start_time, end_time,
|
||||
drip_completion)
|
||||
# Move finished; cleanup any remnants on trapq
|
||||
self.motion_queuing.wipe_trapq(self.trapq)
|
||||
# Misc commands
|
||||
def stats(self, eventtime):
|
||||
max_queue_time = max(self.print_time, self.last_flush_time)
|
||||
for m in self.all_mcus:
|
||||
m.check_active(max_queue_time, eventtime)
|
||||
est_print_time = self.mcu.estimated_print_time(eventtime)
|
||||
buffer_time = self.print_time - est_print_time
|
||||
is_active = buffer_time > -60. or not self.special_queuing_state
|
||||
if self.special_queuing_state == "Drip":
|
||||
buffer_time = 0.
|
||||
return is_active, "print_time=%.3f buffer_time=%.3f print_stall=%d" % (
|
||||
self.print_time, max(buffer_time, 0.), self.print_stall)
|
||||
def check_busy(self, eventtime):
|
||||
@ -588,27 +507,12 @@ class ToolHead:
|
||||
return self.kin
|
||||
def get_trapq(self):
|
||||
return self.trapq
|
||||
def note_step_generation_scan_time(self, delay, old_delay=0.):
|
||||
self.flush_step_generation()
|
||||
if old_delay:
|
||||
self.kin_flush_times.pop(self.kin_flush_times.index(old_delay))
|
||||
if delay:
|
||||
self.kin_flush_times.append(delay)
|
||||
new_delay = max(self.kin_flush_times + [SDS_CHECK_TIME])
|
||||
self.kin_flush_delay = new_delay
|
||||
def register_lookahead_callback(self, callback):
|
||||
last_move = self.lookahead.get_last()
|
||||
if last_move is None:
|
||||
callback(self.get_last_move_time())
|
||||
return
|
||||
last_move.timing_callbacks.append(callback)
|
||||
def note_mcu_movequeue_activity(self, mq_time, is_step_gen=True):
|
||||
self.need_flush_time = max(self.need_flush_time, mq_time)
|
||||
if is_step_gen:
|
||||
self.step_gen_time = max(self.step_gen_time, mq_time)
|
||||
if self.do_kick_flush_timer:
|
||||
self.do_kick_flush_timer = False
|
||||
self.reactor.update_timer(self.flush_timer, self.reactor.NOW)
|
||||
def get_max_velocity(self):
|
||||
return self.max_velocity, self.max_accel
|
||||
def _calc_junction_deviation(self):
|
||||
@ -662,7 +566,7 @@ class ToolHeadCommandHelper:
|
||||
msg = ("max_velocity: %.6f\n"
|
||||
"max_accel: %.6f\n"
|
||||
"minimum_cruise_ratio: %.6f\n"
|
||||
"square_corner_velocity: %.6f" % (mv, ma, scv, mcr))
|
||||
"square_corner_velocity: %.6f" % (mv, ma, mcr, scv))
|
||||
self.printer.set_rollover_info("toolhead", "toolhead: %s" % (msg,))
|
||||
if (max_velocity is None and max_accel is None
|
||||
and square_corner_velocity is None and min_cruise_ratio is None):
|
||||
|
@ -170,6 +170,14 @@ BOARD_DEFS = {
|
||||
"firmware_path": "ZNP_ROBIN_NANO.bin",
|
||||
"current_firmware_path": "ZNP_ROBIN_NANO.CUR"
|
||||
},
|
||||
'qidi-x6': {
|
||||
'mcu': "stm32f401xc",
|
||||
'spi_bus': "spi2",
|
||||
'cs_pin': "PB12",
|
||||
'skip_verify': False,
|
||||
'firmware_path': 'X_4.bin',
|
||||
'current_firmware_path': 'X_4.CUR'
|
||||
},
|
||||
'qidi-x7': {
|
||||
'mcu': "stm32f401xc",
|
||||
'spi_bus': "spi2",
|
||||
@ -228,6 +236,9 @@ BOARD_ALIASES = {
|
||||
'robin_v3': BOARD_DEFS['monster8'],
|
||||
'btt-skrat-v1.0': BOARD_DEFS['btt-skrat'],
|
||||
'chitu-v6': BOARD_DEFS['chitu-v6'],
|
||||
'qidi-x-smart3': BOARD_DEFS['qidi-x6'],
|
||||
'qidi-x-plus3': BOARD_DEFS['qidi-x6'],
|
||||
'qidi-x-max3': BOARD_DEFS['qidi-x6'],
|
||||
'qidi-q1-pro': BOARD_DEFS['qidi-x7'],
|
||||
'qidi-plus4': BOARD_DEFS['qidi-x7']
|
||||
}
|
||||
|
@ -302,7 +302,7 @@ choice
|
||||
config STM32_FLASH_START_C000
|
||||
bool "48KiB bootloader" if MACH_STM32F4x5 || MACH_STM32F401
|
||||
config STM32_FLASH_START_10000
|
||||
bool "64KiB bootloader" if MACH_STM32F103 || MACH_STM32F4
|
||||
bool "64KiB bootloader" if MACH_STM32F103 || MACH_STM32F4 || MACH_N32G45x
|
||||
|
||||
config STM32_FLASH_START_800
|
||||
bool "2KiB bootloader" if MACH_STM32F103
|
||||
|
@ -94,7 +94,7 @@ src-$(CONFIG_HAVE_GPIO_SDIO) += stm32/sdio.c
|
||||
target-y += $(OUT)klipper.bin
|
||||
|
||||
$(OUT)klipper.bin: $(OUT)klipper.elf
|
||||
@echo " Creating hex file $@"
|
||||
@echo " Creating bin file $@"
|
||||
$(Q)$(OBJCOPY) -O binary $< $@
|
||||
|
||||
# Flash rules
|
||||
|
@ -14,6 +14,8 @@
|
||||
#include "sched.h" // sched_shutdown
|
||||
#include "n32g45x_adc.h" // ADC
|
||||
|
||||
#define ADC_INVALID_PIN 0xFF
|
||||
|
||||
DECL_CONSTANT("ADC_MAX", 4095);
|
||||
|
||||
#define ADC_TEMPERATURE_PIN 0xfe
|
||||
@ -21,42 +23,42 @@ DECL_ENUMERATION("pin", "ADC_TEMPERATURE", ADC_TEMPERATURE_PIN);
|
||||
|
||||
static const uint8_t adc_pins[] = {
|
||||
// ADC1
|
||||
0, GPIO('A', 0), GPIO('A', 1), GPIO('A', 6),
|
||||
GPIO('A', 3), GPIO('F', 4), 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
ADC_TEMPERATURE_PIN, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
ADC_INVALID_PIN, GPIO('A', 0), GPIO('A', 1), GPIO('A', 6),
|
||||
GPIO('A', 3), GPIO('F', 4), ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_TEMPERATURE_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
// ADC2
|
||||
0, GPIO('A', 4), GPIO('A', 5), GPIO('B', 1),
|
||||
ADC_INVALID_PIN, GPIO('A', 4), GPIO('A', 5), GPIO('B', 1),
|
||||
GPIO('A', 7), GPIO('C', 4), GPIO('C', 0), GPIO('C', 1),
|
||||
GPIO('C', 2), GPIO('C', 3), GPIO('F', 2), GPIO('A', 2),
|
||||
GPIO('C', 5), GPIO('B', 2), 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
GPIO('C', 5), GPIO('B', 2), ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
#if CONFIG_MACH_N32G455 // ADC3/4 for G455 only
|
||||
// ADC3
|
||||
0, GPIO('B', 11), GPIO('E', 9), GPIO('E', 13),
|
||||
ADC_INVALID_PIN, GPIO('B', 11), GPIO('E', 9), GPIO('E', 13),
|
||||
GPIO('E', 12), GPIO('B', 13), GPIO('E', 8), GPIO('D', 10),
|
||||
GPIO('D', 11), GPIO('D', 12), GPIO('D', 13), GPIO('D', 14),
|
||||
GPIO('B', 0), GPIO('E', 7), GPIO('E', 10), GPIO('E', 11),
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
// ADC4
|
||||
0, GPIO('E', 14), GPIO('E', 15), GPIO('B', 12),
|
||||
GPIO('B', 14), GPIO('B', 15), 0, 0,
|
||||
0, 0, 0, 0,
|
||||
GPIO('D', 8), GPIO('D', 9), 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
0, 0, 0, 0,
|
||||
ADC_INVALID_PIN, GPIO('E', 14), GPIO('E', 15), GPIO('B', 12),
|
||||
GPIO('B', 14), GPIO('B', 15), ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
GPIO('D', 8), GPIO('D', 9), ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN, ADC_INVALID_PIN,
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -66,3 +66,19 @@ max_velocity: 300
|
||||
max_accel: 3000
|
||||
max_z_velocity: 5
|
||||
max_z_accel: 100
|
||||
|
||||
[filament_switch_sensor runout_switch]
|
||||
switch_pin = PD4
|
||||
|
||||
[filament_motion_sensor runout_encoder]
|
||||
switch_pin = PD5
|
||||
detection_length = 4
|
||||
extruder = extruder
|
||||
|
||||
[filament_switch_sensor runout_switch1]
|
||||
switch_pin = PL4
|
||||
|
||||
[filament_motion_sensor runout_encoder1]
|
||||
switch_pin = PL6
|
||||
detection_length = 4
|
||||
extruder = extruder_stepper my_extra_stepper
|
||||
|
@ -10,6 +10,10 @@ MANUAL_STEPPER STEPPER=basic_stepper MOVE=5
|
||||
MANUAL_STEPPER STEPPER=basic_stepper MOVE=12 SPEED=12 ACCEL=9000.2
|
||||
MANUAL_STEPPER STEPPER=basic_stepper ENABLE=0
|
||||
|
||||
# Test long move
|
||||
MANUAL_STEPPER STEPPER=basic_stepper MOVE=300 SPEED=10 ACCEL=2000
|
||||
MANUAL_STEPPER STEPPER=basic_stepper MOVE=100 SPEED=10 ACCEL=2000
|
||||
|
||||
# Test homing move
|
||||
MANUAL_STEPPER STEPPER=homing_stepper ENABLE=1
|
||||
MANUAL_STEPPER STEPPER=homing_stepper SET_POSITION=0
|
||||
@ -29,6 +33,10 @@ G28
|
||||
G1 X20 Y20 Z10
|
||||
G1 A10 X22
|
||||
|
||||
# Verify position query commands work with extra axis
|
||||
GET_POSITION
|
||||
M114
|
||||
|
||||
# Test unregistering
|
||||
MANUAL_STEPPER STEPPER=basic_stepper GCODE_AXIS=
|
||||
G1 X15
|
||||
|
Loading…
x
Reference in New Issue
Block a user