gpiocmds: Use move queue for digital output pins

Signed-off-by: Pascal Pieper <accounts@pascalpieper.de>
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Pascal Pieper 2020-11-25 11:36:00 -05:00 committed by Kevin O'Connor
parent e8ec1801ff
commit 9cdf9bb6ec
3 changed files with 77 additions and 30 deletions

View File

@ -113,18 +113,18 @@ This section lists some commonly used config commands.
digital output mode and set to an initial value as specified by digital output mode and set to an initial value as specified by
'value' (0 for low, 1 for high). Creating a digital_out object 'value' (0 for low, 1 for high). Creating a digital_out object
allows the host to schedule GPIO updates for the given pin at allows the host to schedule GPIO updates for the given pin at
specified times (see the schedule_digital_out command described specified times (see the queue_digital_out command described below).
below). Should the micro-controller software go into shutdown mode Should the micro-controller software go into shutdown mode then all
then all configured digital_out objects will be set to configured digital_out objects will be set to 'default_value'. The
'default_value'. The 'max_duration' parameter is used to implement a 'max_duration' parameter is used to implement a safety check - if it
safety check - if it is non-zero then it is the maximum number of is non-zero then it is the maximum number of clock ticks that the
clock ticks that the host may set the given GPIO to a non-default host may set the given GPIO to a non-default value without further
value without further updates. For example, if the default_value is updates. For example, if the default_value is zero and the
zero and the max_duration is 16000 then if the host sets the gpio to max_duration is 16000 then if the host sets the gpio to a value of
a value of one then it must schedule another update to the gpio pin one then it must schedule another update to the gpio pin (to either
(to either zero or one) within 16000 clock ticks. This safety zero or one) within 16000 clock ticks. This safety feature can be
feature can be used with heater pins to ensure the host does not used with heater pins to ensure the host does not enable the heater
enable the heater and then go off-line. and then go off-line.
* `config_pwm_out oid=%c pin=%u cycle_ticks=%u value=%hu * `config_pwm_out oid=%c pin=%u cycle_ticks=%u value=%hu
default_value=%hu max_duration=%u` : This command creates an default_value=%hu max_duration=%u` : This command creates an
@ -191,21 +191,21 @@ Common commands
This section lists some commonly used run-time commands. It is likely This section lists some commonly used run-time commands. It is likely
only of interest to developers looking to gain insight into Klipper. only of interest to developers looking to gain insight into Klipper.
* `schedule_digital_out oid=%c clock=%u value=%c` : This command will * `queue_digital_out oid=%c clock=%u value=%c` : This command will
schedule a change to a digital output GPIO pin at the given clock schedule a change to a digital output GPIO pin at the given clock
time. To use this command a 'config_digital_out' command with the time. To use this command a 'config_digital_out' command with the
same 'oid' parameter must have been issued during micro-controller same 'oid' parameter must have been issued during micro-controller
configuration. configuration.
* `queue_pwm_out oid=%c clock=%u value=%hu` : Schedules a change to a * `queue_pwm_out oid=%c clock=%u value=%hu` : Schedules a change to a
hardware PWM output pin. See the 'schedule_digital_out' and hardware PWM output pin. See the 'queue_digital_out' and
'config_pwm_out' commands for more info. 'config_pwm_out' commands for more info.
* `schedule_soft_pwm_out oid=%c clock=%u on_ticks=%u` : Schedules a * `schedule_soft_pwm_out oid=%c clock=%u on_ticks=%u` : Schedules a
change to a software PWM output pin. Because the output switching is change to a software PWM output pin. Because the output switching is
implemented in the micro-controller software, it is recommended that implemented in the micro-controller software, it is recommended that
the sum of on_ticks and off_ticks parameters corresponds to a time the sum of on_ticks and off_ticks parameters corresponds to a time
of 10ms or greater. See the 'schedule_digital_out' and of 10ms or greater. See the 'queue_digital_out' and
'config_soft_pwm_out' commands for more info. 'config_soft_pwm_out' commands for more info.
* `query_analog_in oid=%c clock=%u sample_ticks=%u sample_count=%c * `query_analog_in oid=%c clock=%u sample_ticks=%u sample_count=%c

View File

@ -143,18 +143,19 @@ class MCU_digital_out:
self._mcu.add_config_cmd("set_digital_out pin=%s value=%d" self._mcu.add_config_cmd("set_digital_out pin=%s value=%d"
% (self._pin, self._start_value)) % (self._pin, self._start_value))
return return
self._mcu.request_move_queue_slot()
self._oid = self._mcu.create_oid() self._oid = self._mcu.create_oid()
self._mcu.add_config_cmd( self._mcu.add_config_cmd(
"config_digital_out oid=%d pin=%s value=%d default_value=%d" "config_digital_out oid=%d pin=%s value=%d default_value=%d"
" max_duration=%d" % ( " max_duration=%d"
self._oid, self._pin, self._start_value, self._shutdown_value, % (self._oid, self._pin, self._start_value, self._shutdown_value,
self._mcu.seconds_to_clock(self._max_duration))) self._mcu.seconds_to_clock(self._max_duration)))
self._mcu.add_config_cmd("update_digital_out oid=%d value=%d" self._mcu.add_config_cmd("update_digital_out oid=%d value=%d"
% (self._oid, self._start_value), % (self._oid, self._start_value),
on_restart=True) on_restart=True)
cmd_queue = self._mcu.alloc_command_queue() cmd_queue = self._mcu.alloc_command_queue()
self._set_cmd = self._mcu.lookup_command( self._set_cmd = self._mcu.lookup_command(
"schedule_digital_out oid=%c clock=%u value=%c", cq=cmd_queue) "queue_digital_out oid=%c clock=%u value=%c", cq=cmd_queue)
def set_digital(self, print_time, value): def set_digital(self, print_time, value):
clock = self._mcu.print_time_to_clock(print_time) clock = self._mcu.print_time_to_clock(print_time)
self._set_cmd.send([self._oid, clock, (not not value) ^ self._invert], self._set_cmd.send([self._oid, clock, (not not value) ^ self._invert],

View File

@ -20,7 +20,14 @@ struct digital_out_s {
struct timer timer; struct timer timer;
struct gpio_out pin; struct gpio_out pin;
uint32_t max_duration; uint32_t max_duration;
uint8_t value, default_value; uint8_t default_value;
struct move_queue_head mq;
};
struct digital_move {
struct move_node node;
uint32_t waketime;
uint8_t value;
}; };
static uint_fast8_t static uint_fast8_t
@ -32,13 +39,33 @@ digital_end_event(struct timer *timer)
static uint_fast8_t static uint_fast8_t
digital_out_event(struct timer *timer) digital_out_event(struct timer *timer)
{ {
// Apply next update and remove it from queue
struct digital_out_s *d = container_of(timer, struct digital_out_s, timer); struct digital_out_s *d = container_of(timer, struct digital_out_s, timer);
gpio_out_write(d->pin, d->value); struct move_node *mn = move_queue_pop(&d->mq);
if (d->value == d->default_value || !d->max_duration) struct digital_move *m = container_of(mn, struct digital_move, node);
uint8_t value = m->value;
gpio_out_write(d->pin, value);
move_free(m);
// Check if more updates queued
if (move_queue_empty(&d->mq)) {
if (value == d->default_value || !d->max_duration)
return SF_DONE; return SF_DONE;
// Start the safety timeout
d->timer.waketime += d->max_duration; d->timer.waketime += d->max_duration;
d->timer.func = digital_end_event; d->timer.func = digital_end_event;
return SF_RESCHEDULE; return SF_RESCHEDULE;
}
// Schedule next update
struct move_node *nn = move_queue_first(&d->mq);
uint32_t wake = container_of(nn, struct digital_move, node)->waketime;
if (value != d->default_value && d->max_duration
&& timer_is_before(d->timer.waketime + d->max_duration, wake))
shutdown("Scheduled digital out event will exceed max_duration");
d->timer.waketime = wake;
return SF_RESCHEDULE;
} }
void void
@ -50,29 +77,46 @@ command_config_digital_out(uint32_t *args)
d->pin = pin; d->pin = pin;
d->default_value = args[3]; d->default_value = args[3];
d->max_duration = args[4]; d->max_duration = args[4];
d->timer.func = digital_out_event;
move_queue_setup(&d->mq, sizeof(struct digital_move));
} }
DECL_COMMAND(command_config_digital_out, DECL_COMMAND(command_config_digital_out,
"config_digital_out oid=%c pin=%u value=%c default_value=%c" "config_digital_out oid=%c pin=%u value=%c default_value=%c"
" max_duration=%u"); " max_duration=%u");
void void
command_schedule_digital_out(uint32_t *args) command_queue_digital_out(uint32_t *args)
{ {
struct digital_out_s *d = oid_lookup(args[0], command_config_digital_out); struct digital_out_s *d = oid_lookup(args[0], command_config_digital_out);
struct digital_move *m = move_alloc();
m->waketime = args[1];
m->value = args[2];
irq_disable();
int need_add_timer = move_queue_push(&m->node, &d->mq);
irq_enable();
if (!need_add_timer)
return;
// queue was empty and a timer needs to be added
sched_del_timer(&d->timer); sched_del_timer(&d->timer);
if (d->timer.func == digital_end_event
&& timer_is_before(d->timer.waketime, m->waketime))
shutdown("Scheduled digital out event will exceed max_duration");
d->timer.func = digital_out_event; d->timer.func = digital_out_event;
d->timer.waketime = args[1]; d->timer.waketime = m->waketime;
d->value = args[2];
sched_add_timer(&d->timer); sched_add_timer(&d->timer);
} }
DECL_COMMAND(command_schedule_digital_out, DECL_COMMAND(command_queue_digital_out,
"schedule_digital_out oid=%c clock=%u value=%c"); "queue_digital_out oid=%c clock=%u value=%c");
void void
command_update_digital_out(uint32_t *args) command_update_digital_out(uint32_t *args)
{ {
struct digital_out_s *d = oid_lookup(args[0], command_config_digital_out); struct digital_out_s *d = oid_lookup(args[0], command_config_digital_out);
sched_del_timer(&d->timer); sched_del_timer(&d->timer);
if (!move_queue_empty(&d->mq))
shutdown("update_digital_out not valid with active queue");
uint8_t value = args[1]; uint8_t value = args[1];
gpio_out_write(d->pin, value); gpio_out_write(d->pin, value);
if (value != d->default_value && d->max_duration) { if (value != d->default_value && d->max_duration) {
@ -90,6 +134,8 @@ digital_out_shutdown(void)
struct digital_out_s *d; struct digital_out_s *d;
foreach_oid(i, d, command_config_digital_out) { foreach_oid(i, d, command_config_digital_out) {
gpio_out_write(d->pin, d->default_value); gpio_out_write(d->pin, d->default_value);
d->timer.func = digital_out_event;
move_queue_clear(&d->mq);
} }
} }
DECL_SHUTDOWN(digital_out_shutdown); DECL_SHUTDOWN(digital_out_shutdown);