Skip to content

Commit

Permalink
Fixed clock difference calculation when changing cycle_time of PWM co…
Browse files Browse the repository at this point in the history
…mmands.

Added set_cycle_time to move_queue in ht mode to retain order of commands.

Signed-off-by: Pascal Pieper <[email protected]>
  • Loading branch information
Cirromulus committed Nov 13, 2022
1 parent 0d9a09a commit b93ac33
Showing 1 changed file with 56 additions and 36 deletions.
92 changes: 56 additions & 36 deletions klippy/mcu.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,10 +334,9 @@ def _build_config(self):
self._start_value * self._pwm_max))
return
self._oid = self._mcu.create_oid()
cmd_queue = None
if not self._is_ht:
self._mcu.request_move_queue_slot()
cmd_queue = self._mcu.alloc_command_queue()
cmd_queue = self._mcu.alloc_command_queue(
uses_move_queue=True, high_throughput=self._is_ht
)

self._mcu.add_config_cmd(
"config_pwm_out oid=%d pin=%s cycle_ticks=%d value=%d"
Expand All @@ -351,7 +350,7 @@ def _build_config(self):
on_restart=True)
self._set_cmd = self._mcu.lookup_command(
"queue_pwm_out oid=%c clock=%u value=%hu", cq=cmd_queue,
fast_track=self._is_ht)
high_throughput=self._is_ht)
return
# Software PWM
if self._shutdown_value not in [0., 1.]:
Expand All @@ -363,10 +362,9 @@ def _build_config(self):
if cycle_ticks >= 1<<31:
raise pins.error("PWM pin cycle time too large")
self._oid = self._mcu.create_oid()
cmd_queue = None
if not self._is_ht:
self._mcu.request_move_queue_slot()
cmd_queue = self._mcu.alloc_command_queue()
cmd_queue = self._mcu.alloc_command_queue(
uses_move_queue=True, high_throughput=self._is_ht
)
self._mcu.add_config_cmd(
"config_digital_out oid=%d pin=%s value=%d"
" default_value=%d max_duration=%d"
Expand All @@ -378,18 +376,19 @@ def _build_config(self):
self._last_cycle_ticks = cycle_ticks
svalue = int(self._start_value * cycle_ticks + 0.5)
self._mcu.add_config_cmd(
"queue_digital_out oid=%d clock=%d on_ticks=%d"
"queue_digital_out oid=%d clock=%d on_ticks=%u"
% (self._oid, self._last_clock, svalue), is_init=True)
self._set_cmd = self._mcu.lookup_command(
"queue_digital_out oid=%c clock=%u on_ticks=%u", cq=cmd_queue,
fast_track=self._is_ht)
high_throughput=self._is_ht)
self._set_cycle_ticks = self._mcu.lookup_command(
"set_digital_out_pwm_cycle oid=%c cycle_ticks=%u", cq=cmd_queue)
"set_digital_out_pwm_cycle oid=%c cycle_ticks=%u", cq=cmd_queue,
high_throughput=self._is_ht) # this is a lie
def set_pwm(self, print_time, value, cycle_time=None):
req_clock = self._mcu.print_time_to_clock(print_time)
# FIXME: let sync_channel replace uncommitted values
# so that this is not necessary
minclock = self._last_clock
# sync_channel can't replace uncommitted values
# so this max is necessary
clock = max(req_clock, self._last_clock + self._min_clock_diff)
if self._invert:
value = 1. - value
Expand All @@ -406,14 +405,17 @@ def set_pwm(self, print_time, value, cycle_time=None):
if cycle_ticks >= 1<<31:
raise self._mcu.get_printer().command_error(
"PWM cycle time too large")

# Make sure move queue is flushed as
# cycle time updates are not queueable (yet)
# Value is a rough estimate and should be calculated
clock += self._mcu.print_time_to_clock(.100)

self._set_cycle_ticks.send([self._oid, cycle_ticks],
minclock=minclock, reqclock=clock)
# FIXME: this makes sure new cycle_ticks is applied
# _before_ new on_ticks arrive
clock = self._mcu.print_time_to_clock(print_time +
self._last_cycle_ticks)
self._last_cycle_ticks = cycle_ticks
self._min_clock_diff = cycle_ticks

on_ticks = int(max(0., min(1., value)) * float(cycle_ticks) + 0.5)
self._set_cmd.send([self._oid, clock & 0xFFFFFFFF, on_ticks],
minclock=minclock, reqclock=clock)
Expand Down Expand Up @@ -553,16 +555,20 @@ def send(self, data=(), minclock=0, reqclock=0):
self._serial.raw_send(cmd, minclock, reqclock, self._cmd_queue)

class FastCommandWrapper:
def __init__(self, mcu, cmd_id, sync_channel, queue_msg_fn):
def __init__(self, mcu, cmd_id, cmd_queue, queue_msg_fn):
self._mcu = mcu
self._cmd_id = cmd_id
self._sync_channel = sync_channel
if not cmd_queue:
cmd_queue = alloc_command_queue(
uses_move_queue=True, high_throughput=True
)
self._sync_channel_obj = cmd_queue
self._queue_msg_fn = queue_msg_fn
self._reactor = mcu.get_printer().get_reactor()
self._toolhead = mcu.get_printer().lookup_object('toolhead')
def send(self, data, minclock, reqclock):
data.insert(0, self._cmd_id)
self._queue_msg_fn(self._sync_channel, data, len(data), reqclock)
self._queue_msg_fn(self._sync_channel_obj, data, len(data), reqclock)
print_time = self._mcu.clock_to_print_time(reqclock)
# TODO: Is here actually `_async_` needed?
self._reactor.register_async_callback(
Expand Down Expand Up @@ -624,7 +630,7 @@ def __init__(self, config, clocksync):

#
self._sync_channels = []
self._active_fasttrack_queues = 0
self._active_ht_queues = 0
#

# Stats
Expand Down Expand Up @@ -894,22 +900,36 @@ def get_name(self):
return self._name
def register_response(self, cb, msg, oid=None):
self._serial.register_response(cb, msg, oid)
def alloc_command_queue(self):
return self._serial.alloc_command_queue()
def lookup_command(self, msgformat, cq=None, fast_track=False):
if not fast_track:
return CommandWrapper(self._serial, msgformat, cq)

if self._active_fasttrack_queues > 0:
raise error ("Currently, only one high throughput pin is allowed!")
self._active_fasttrack_queues = self._active_fasttrack_queues + 1
sync_channel = self._ffi_main.gc(
def alloc_command_queue(self, uses_move_queue=False, high_throughput=False):
if high_throughput:
if not uses_move_queue:
raise error (
"high throughput mode only works with move_queue items!"
)
if self._active_ht_queues > 0:
raise error (
"Currently, only one high throughput pin "
"is currently supported!"
)
self._active_ht_queues = self._active_ht_queues + 1
cq = self._ffi_main.gc(
self._ffi_lib.sync_channel_alloc(),
self._ffi_lib.sync_channel_free)
self.register_sync_channel(sync_channel)
return FastCommandWrapper(self, self.lookup_command_tag(msgformat),
sync_channel,
self._ffi_lib.sync_channel_queue_msg)
self.register_sync_channel(cq)
return cq
else:
# TODO: Automatically determine command's usage of move queue
if uses_move_queue:
self.request_move_queue_slot()
return self._serial.alloc_command_queue()

def lookup_command(self, msgformat, cq=None, high_throughput=False):
if high_throughput:
return FastCommandWrapper(self, self.lookup_command_tag(msgformat),
cq,
self._ffi_lib.sync_channel_queue_msg)
else:
return CommandWrapper(self._serial, msgformat, cq)

def lookup_query_command(self, msgformat, respformat, oid=None,
cq=None, is_async=False):
Expand Down

0 comments on commit b93ac33

Please sign in to comment.