pwm_tool: Add support for maximum_mcu_duration

Implement the maximum_mcu_duration config parameter along with its
associated queue flushing.

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2023-11-30 11:06:16 -05:00
parent 1e5f688b53
commit 2defd7374a
2 changed files with 53 additions and 27 deletions

View File

@ -3149,6 +3149,7 @@ pin:
# The pin to configure as an output. This parameter must be provided. # The pin to configure as an output. This parameter must be provided.
#value: #value:
#shutdown_value: #shutdown_value:
#maximum_mcu_duration:
#cycle_time: 0.100 #cycle_time: 0.100
#hardware_pwm: False #hardware_pwm: False
#scale: #scale:

View File

@ -5,8 +5,8 @@
# 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.
import chelper import chelper
PIN_MIN_TIME = 0.100
MAX_SCHEDULE_TIME = 5.0 MAX_SCHEDULE_TIME = 5.0
CLOCK_SYNC_EXTRA_TIME = 0.050
class error(Exception): class error(Exception):
pass pass
@ -27,9 +27,15 @@ class MCU_queued_pwm:
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)
self._last_clock = self._cycle_ticks = 0 self._last_clock = self._last_value = self._default_value = 0
self._duration_ticks = 0
self._pwm_max = 0. self._pwm_max = 0.
self._set_cmd_tag = None self._set_cmd_tag = None
self._toolhead = None
printer = self._mcu.get_printer()
printer.register_event_handler("klippy:connect", self._handle_connect)
def _handle_connect(self):
self._toolhead = self._mcu.get_printer().lookup_object("toolhead")
def get_mcu(self): def get_mcu(self):
return self._mcu return self._mcu
def setup_max_duration(self, max_duration): def setup_max_duration(self, max_duration):
@ -52,20 +58,26 @@ class MCU_queued_pwm:
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)
mdur_ticks = self._mcu.seconds_to_clock(self._max_duration) if cycle_ticks >= 1<<31:
if mdur_ticks >= 1<<31: raise pins.error("PWM pin cycle time too large")
self._duration_ticks = self._mcu.seconds_to_clock(self._max_duration)
if self._duration_ticks >= 1<<31:
raise pins.error("PWM pin max duration too large") raise pins.error("PWM pin max duration too large")
if self._duration_ticks:
self._mcu.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._mcu.add_config_cmd( self._mcu.add_config_cmd(
"config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d" "config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d"
" default_value=%d max_duration=%d" " default_value=%d max_duration=%d"
% (self._oid, self._pin, cycle_ticks, % (self._oid, self._pin, cycle_ticks,
self._start_value * self._pwm_max, self._start_value * self._pwm_max,
self._shutdown_value * self._pwm_max, mdur_ticks)) self._default_value, self._duration_ticks))
svalue = int(self._start_value * self._pwm_max + 0.5) self._last_value = int(self._start_value * self._pwm_max + 0.5)
self._mcu.add_config_cmd("queue_pwm_out oid=%d clock=%d value=%d" self._mcu.add_config_cmd("queue_pwm_out oid=%d clock=%d value=%d"
% (self._oid, self._last_clock, svalue), % (self._oid, self._last_clock,
self._last_value),
on_restart=True) on_restart=True)
self._set_cmd_tag = self._mcu.lookup_command( self._set_cmd_tag = self._mcu.lookup_command(
"queue_pwm_out oid=%c clock=%u value=%hu", "queue_pwm_out oid=%c clock=%u value=%hu",
@ -74,39 +86,50 @@ class MCU_queued_pwm:
# Software PWM # Software PWM
if self._shutdown_value not in [0., 1.]: if self._shutdown_value not in [0., 1.]:
raise pins.error("shutdown value must be 0.0 or 1.0 on soft pwm") raise pins.error("shutdown value must be 0.0 or 1.0 on soft pwm")
if cycle_ticks >= 1<<31:
raise pins.error("PWM pin cycle time too large")
self._mcu.add_config_cmd( self._mcu.add_config_cmd(
"config_digital_out oid=%d pin=%s value=%d" "config_digital_out oid=%d pin=%s value=%d"
" default_value=%d max_duration=%d" " default_value=%d max_duration=%d"
% (self._oid, self._pin, self._start_value >= 1.0, % (self._oid, self._pin, self._start_value >= 1.0,
self._shutdown_value >= 0.5, mdur_ticks)) self._shutdown_value >= 0.5, self._duration_ticks))
self._default_value = int(self._shutdown_value >= 0.5) * cycle_ticks
self._mcu.add_config_cmd( self._mcu.add_config_cmd(
"set_digital_out_pwm_cycle oid=%d cycle_ticks=%d" "set_digital_out_pwm_cycle oid=%d cycle_ticks=%d"
% (self._oid, cycle_ticks)) % (self._oid, cycle_ticks))
self._cycle_ticks = cycle_ticks self._pwm_max = float(cycle_ticks)
svalue = int(self._start_value * cycle_ticks + 0.5) self._last_value = int(self._start_value * self._pwm_max + 0.5)
self._mcu.add_config_cmd( self._mcu.add_config_cmd(
"queue_digital_out oid=%d clock=%d on_ticks=%d" "queue_digital_out oid=%d clock=%d on_ticks=%d"
% (self._oid, self._last_clock, svalue), is_init=True) % (self._oid, self._last_clock, self._last_value), is_init=True)
self._set_cmd_tag = self._mcu.lookup_command( self._set_cmd_tag = self._mcu.lookup_command(
"queue_digital_out oid=%c clock=%u on_ticks=%u", "queue_digital_out oid=%c clock=%u on_ticks=%u",
cq=cmd_queue).get_command_tag() cq=cmd_queue).get_command_tag()
def set_pwm(self, print_time, value): def _send_update(self, clock, val):
clock = self._mcu.print_time_to_clock(print_time) self._last_clock = clock = max(self._last_clock, clock)
minclock = self._last_clock self._last_value = val
self._last_clock = clock data = (self._set_cmd_tag, self._oid, clock & 0xffffffff, val)
if self._invert:
value = 1. - value
max_count = self._cycle_ticks
if self._hardware_pwm:
max_count = self._pwm_max
v = int(max(0., min(1., value)) * max_count + 0.5)
data = (self._set_cmd_tag, self._oid, clock & 0xffffffff, v)
ret = self._stepcompress_queue_mq_msg(self._stepqueue, clock, ret = self._stepcompress_queue_mq_msg(self._stepqueue, clock,
data, len(data)) data, len(data))
if ret: if ret:
raise error("Internal error in stepcompress") raise error("Internal error in stepcompress")
# Notify toolhead so that it will flush this update
wakeclock = clock
if self._last_value != self._default_value:
# Continue flushing to resend time
wakeclock += self._duration_ticks
wake_print_time = self._mcu.clock_to_print_time(wakeclock)
self._toolhead.note_kinematic_activity(wake_print_time
+ CLOCK_SYNC_EXTRA_TIME)
def set_pwm(self, print_time, value):
clock = self._mcu.print_time_to_clock(print_time)
if self._invert:
value = 1. - value
v = int(max(0., min(1., value)) * self._pwm_max + 0.5)
self._send_update(clock, v)
def _flush_notification(self, print_time, clock):
if self._last_value != self._default_value:
while clock >= self._last_clock + self._duration_ticks:
self._send_update(self._last_clock + self._duration_ticks,
self._last_value)
class PrinterOutputPin: class PrinterOutputPin:
def __init__(self, config): def __init__(self, config):
@ -121,7 +144,11 @@ class PrinterOutputPin:
self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm) self.mcu_pin.setup_cycle_time(cycle_time, hardware_pwm)
self.scale = config.getfloat('scale', 1., above=0.) self.scale = config.getfloat('scale', 1., above=0.)
self.last_print_time = 0. self.last_print_time = 0.
self.mcu_pin.setup_max_duration(0.) # Support mcu checking for maximum duration
max_mcu_duration = config.getfloat('maximum_mcu_duration', 0.,
minval=0.500,
maxval=MAX_SCHEDULE_TIME)
self.mcu_pin.setup_max_duration(max_mcu_duration)
# Determine start and shutdown values # Determine start and shutdown values
self.last_value = config.getfloat( self.last_value = config.getfloat(
'value', 0., minval=0., maxval=self.scale) / self.scale 'value', 0., minval=0., maxval=self.scale) / self.scale
@ -143,8 +170,6 @@ class PrinterOutputPin:
self.mcu_pin.set_pwm(print_time, value) self.mcu_pin.set_pwm(print_time, value)
self.last_value = value self.last_value = value
self.last_print_time = print_time self.last_print_time = print_time
toolhead = self.printer.lookup_object('toolhead')
toolhead.note_kinematic_activity(print_time)
cmd_SET_PIN_help = "Set the value of an output pin" cmd_SET_PIN_help = "Set the value of an output pin"
def cmd_SET_PIN(self, gcmd): def cmd_SET_PIN(self, gcmd):
# Read requested value # Read requested value