Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sync channel for fast move_queue population #4128

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions config/sample-pwm-tool.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,64 @@
# See docs/Using_PWM_Tools.md for a more detailed description.

[output_pin TOOL]
pin: !ar9 # use your fan's pin number
pin: !PIN # use your fan's pin number
pwm: True
hardware_pwm: True
#hardware_pwm: True
# Using hardware PWM is optional.
cycle_time: 0.001
shutdown_value: 0
maximum_mcu_duration: 5
high_throughput: True
# This enables the high throughput mode.
# Suggested to only use on one output pin.

#maximum_mcu_duration: 5
# Warning: The maximum mcu duration will _not_ yet
# split long running moves.
# This will lead to a shutdown if individual moves
# do run longer.
# Default: 0 (disabled)
# Amount of time in which the host has to acknowledge
# a non-shutdown output value.
# Suggested value is around 5 seconds.
# Use a value that does not burn up your stock.
# Please note that during homing, your tool
# needs to be in default speed.
# needs to be in default speed (off), or else the MCU will
# do a safety-shutdown.

[output_pin TOOL_ENABLE]
pin: !OTHER_PIN
shutdown_value: 0

[gcode_macro M3]
gcode:
SET_PIN PIN=TOOL_ENABLE VALUE=1
{% set S = params.S|default(0.0)|float %}
SET_PIN PIN=TOOL VALUE={S / 255.0}

[gcode_macro M4]
gcode:
SET_PIN PIN=TOOL_ENABLE VALUE=1
{% set S = params.S|default(0.0)|float %}
SET_PIN PIN=TOOL VALUE={S / 255.0}

[gcode_macro M5]
gcode:
SET_PIN PIN=TOOL VALUE=0
SET_PIN PIN=TOOL_ENABLE VALUE=0


# Optional: LCD Menu Control

[menu __main __control __toolonoff]
type: input
enable: {'output_pin TOOL' in printer}
name: Fan: {'ON ' if menu.input else 'OFF'}
name: Tool: {'ON ' if menu.input else 'OFF'}
input: {printer['output_pin TOOL'].value}
input_min: 0
input_max: 1
input_step: 1
gcode:
M3 S{255 if menu.input else 0}
SET_PIN PIN=TOOL_ENABLE VALUE={1 if menu.input else 0}

[menu __main __control __toolspeed]
type: input
Expand Down
8 changes: 8 additions & 0 deletions docs/Config_Reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -3117,6 +3117,14 @@ pin:
# then the 'value' parameter can be specified using the desired
# amperage for the stepper. The default is to not scale the 'value'
# parameter.
high_throughput: False
# This enables the high throughput mode for using e.g. PWM
# controlled tools (see docs/Using_PWM_Tools.md)
# Suggested to only use on one output pin, currently.
# Warning: Setting a maximum_mcu_duration will _not_ yet
# split long running moves.
# This will lead to a shutdown if individual moves
# do run longer in high throughput mode.
```

### [static_digital_output]
Expand Down
14 changes: 4 additions & 10 deletions docs/Using_PWM_Tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,19 +21,13 @@ _fully on_ for the time it takes the MCU to start up again.
For good measure, it is recommended to _always_ wear appropriate
laser-goggles of the right wavelength if the laser is powered;
and to disconnect the laser when it is not needed.
Also, you should configure a safety timeout,
so that when your host or MCU encounters an error, the tool will stop.
Also, you should consider using a safety timeout,
so that when your host dies unexpectedly, the tool will stop.
An additional safety layer would be powering your tool through the hotend-heater pin.
In the configuration, the additional `[output_pin]` would be activated by `M3/M4` and disabled by `M5`.

For an example configuration, see [config/sample-pwm-tool.cfg](/config/sample-pwm-tool.cfg).

## Current Limitations

There is a limitation of how frequent PWM updates may occur.
While being very precise, a PWM update may only occur every 0.1 seconds,
rendering it almost useless for raster engraving.
However, there exists an [experimental branch](https://github.com/Cirromulus/klipper/tree/laser_tool) with its own tradeoffs.
In long term, it is planned to add this functionality to main-line klipper.

## Commands

`M3/M4 S<value>` : Set PWM duty-cycle. Values between 0 and 255.
Expand Down
12 changes: 10 additions & 2 deletions klippy/chelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,21 @@
, uint64_t start_clock, uint64_t end_clock);

struct steppersync *steppersync_alloc(struct serialqueue *sq
, struct stepcompress **sc_list, int sc_num, int move_num);
, struct stepcompress **sc_list, int sc_num
, struct sync_channel **pc_list, int pc_num, int move_num);
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss
, double time_offset, double mcu_freq);
int steppersync_flush(struct steppersync *ss, uint64_t move_clock);
"""

defs_sync_channel = """
struct sync_channel *sync_channel_alloc();
void sync_channel_free(struct sync_channel *pc);
int sync_channel_queue_msg(struct sync_channel *pc
, uint32_t *data, int len, uint64_t req_clock);
"""

defs_itersolve = """
int32_t itersolve_generate_steps(struct stepper_kinematics *sk
, double flush_time);
Expand Down Expand Up @@ -215,7 +223,7 @@

defs_all = [
defs_pyhelper, defs_serialqueue, defs_std, defs_stepcompress,
defs_itersolve, defs_trapq, defs_trdispatch,
defs_sync_channel, defs_itersolve, defs_trapq, defs_trdispatch,
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_extruder, defs_kin_shaper, defs_kin_idex,
Expand Down
68 changes: 66 additions & 2 deletions klippy/chelper/stepcompress.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,15 @@ struct stepcompress {
struct list_head history_list;
};

struct sync_channel {
// Buffer management
uint32_t *queue, *queue_end, *queue_pos, *queue_next;

// Message generation
struct list_head msg_queue;
//uint32_t oid;
};

struct step_move {
uint32_t interval;
uint16_t count;
Expand Down Expand Up @@ -316,6 +325,41 @@ stepcompress_get_step_dir(struct stepcompress *sc)
return sc->next_step_dir;
}

/****************************************************************
* Sync channel interface
****************************************************************/

struct sync_channel * __visible
sync_channel_alloc() //uint32_t oid)
{
struct sync_channel *pc = malloc(sizeof(*pc));
memset(pc, 0, sizeof(*pc));
list_init(&pc->msg_queue);
// pc->oid = oid; //not needed?
return pc;
}

void __visible
sync_channel_free(struct sync_channel *pc)
{
if (!pc)
return;
free(pc->queue);
message_queue_free(&pc->msg_queue);
free(pc);
}

void __visible
sync_channel_queue_msg(struct sync_channel *pc, uint32_t *data, int len,
uint64_t req_clock)
{
struct queue_message *qm = message_alloc_and_encode(data, len);
qm->req_clock = req_clock;
qm->min_clock = req_clock;

list_add_tail(&qm->node, &pc->msg_queue);
}

// Determine the "print time" of the last_step_clock
static void
calc_last_step_print_time(struct stepcompress *sc)
Expand Down Expand Up @@ -666,15 +710,19 @@ struct steppersync {
// Storage for associated stepcompress objects
struct stepcompress **sc_list;
int sc_num;
// Storage for associated synchronous message objects
struct sync_channel **pc_list;
int pc_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)
steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list,
int sc_num, struct sync_channel **pc_list,
int pc_num, int move_num)
{
struct steppersync *ss = malloc(sizeof(*ss));
memset(ss, 0, sizeof(*ss));
Expand All @@ -685,6 +733,10 @@ steppersync_alloc(struct serialqueue *sq, struct stepcompress **sc_list
memcpy(ss->sc_list, sc_list, sizeof(*sc_list)*sc_num);
ss->sc_num = sc_num;

ss->pc_list = malloc(sizeof(*pc_list)*pc_num);
memcpy(ss->pc_list, pc_list, sizeof(*pc_list)*pc_num);
ss->pc_num = pc_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;
Expand Down Expand Up @@ -771,6 +823,18 @@ steppersync_flush(struct steppersync *ss, uint64_t move_clock)
}
}
}
// sync_channel
for (i=0; i<ss->pc_num; i++) {
struct sync_channel *pc = ss->pc_list[i];
if (!list_empty(&pc->msg_queue)) {
struct queue_message *m = list_first_entry(
&pc->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;

Expand Down
9 changes: 7 additions & 2 deletions klippy/chelper/stepcompress.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,15 @@ int stepcompress_extract_old(struct stepcompress *sc
, struct pull_history_steps *p, int max
, uint64_t start_clock, uint64_t end_clock);

struct sync_channel *sync_channel_alloc(); //uint32_t oid); // not needed?
void sync_channel_free(struct sync_channel *pc);
void sync_channel_queue_msg(struct sync_channel *pc, uint32_t *data, int len,
uint64_t req_clock);

struct serialqueue;
struct steppersync *steppersync_alloc(
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num
, int move_num);
struct serialqueue *sq, struct stepcompress **sc_list, int sc_num,
struct sync_channel **pc_list, int pc_num, int move_num);
void steppersync_free(struct steppersync *ss);
void steppersync_set_time(struct steppersync *ss, double time_offset
, double mcu_freq);
Expand Down
16 changes: 11 additions & 5 deletions klippy/extras/output_pin.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@
# This file may be distributed under the terms of the GNU GPLv3 license.

PIN_MIN_TIME = 0.100
RESEND_HOST_TIME = 0.300 + PIN_MIN_TIME
RESEND_HOST_TIME = 0.300
MAX_SCHEDULE_TIME = 5.0

class PrinterOutputPin:
def __init__(self, config):
self.printer = config.get_printer()
ppins = self.printer.lookup_object('pins')
self.is_pwm = config.getboolean('pwm', False)
self._pin_min_time = PIN_MIN_TIME
if self.is_pwm:
self.mcu_pin = ppins.setup_pin('pwm', config.get('pin'))
cycle_time = config.getfloat('cycle_time', 0.100, above=0.,
Expand All @@ -37,6 +38,10 @@ def __init__(self, config):
self.mcu_pin.setup_start_value(
self.last_value, self.last_value, True)
else:
if config.getboolean('high_throughput', False):
self.mcu_pin.setup_high_throughput_mode()
self._pin_min_time = self.default_cycle_time

max_mcu_duration = config.getfloat('maximum_mcu_duration', 0.,
minval=0.500,
maxval=MAX_SCHEDULE_TIME)
Expand All @@ -58,9 +63,9 @@ def get_status(self, eventtime):
return {'value': self.last_value}
def _set_pin(self, print_time, value, cycle_time, is_resend=False):
if value == self.last_value and cycle_time == self.last_cycle_time:
if not is_resend:
if not is_resend or value == self.shutdown_value:
return
print_time = max(print_time, self.last_print_time + PIN_MIN_TIME)
Cirromulus marked this conversation as resolved.
Show resolved Hide resolved
print_time = max(print_time, self.last_print_time + self._pin_min_time)
if self.is_pwm:
self.mcu_pin.set_pwm(print_time, value, cycle_time)
else:
Expand Down Expand Up @@ -92,11 +97,12 @@ def _resend_current_val(self, eventtime):
systime = self.reactor.monotonic()
print_time = self.mcu_pin.get_mcu().estimated_print_time(systime)
time_diff = (self.last_print_time + self.resend_interval) - print_time

if time_diff > 0.:
# Reschedule for resend time
return systime + time_diff
self._set_pin(print_time + PIN_MIN_TIME,
self.last_value, self.last_cycle_time, True)
self._set_pin(print_time + RESEND_HOST_TIME/2, self.last_value,
self.last_cycle_time, True)
return systime + self.resend_interval

def load_config_prefix(config):
Expand Down
Loading