docs: Reword and reformat parts of move code flow in Code_Overview

Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
Kevin O'Connor 2017-04-13 13:41:58 -04:00
parent daff83ee9a
commit dc645d76b4
1 changed files with 63 additions and 55 deletions

View File

@ -115,56 +115,63 @@ Klippy host and it completes when the corresponding step pulses are
produced on the micro-controller. This section outlines the code flow produced on the micro-controller. This section outlines the code flow
of a typical move command. of a typical move command.
* Processing for a move command (eg, "G1 X10 F6000") starts in * Processing for a move command starts in gcode.py. The goal of
gcode.py. The goal of gcode.py is to translate gcode into internal gcode.py is to translate G-code into internal calls. Changes in
calls. Changes in origin (eg, G92), changes in relative vs absolute origin (eg, G92), changes in relative vs absolute positions (eg,
positions (eg, G90), and unit changes (eg, F6000=100mm/s) are G90), and unit changes (eg, F6000=100mm/s) are handled here. The
handled here. The code path for a move is: process_data() -> code path for a move is: `process_data() -> process_commands() ->
process_commands() -> cmd_G1(). Ultimately the ToolHead class is cmd_G1()`. Ultimately the ToolHead class is invoked to execute the
invoked to execute the actual request: cmd_G1() -> actual request: `cmd_G1() -> ToolHead.move()`
ToolHead.move([10, 0, 0, 0], 100.0)
* The ToolHead class (in toolhead.py) handles "lookahead" and tracks * The ToolHead class (in toolhead.py) handles "lookahead" and tracks
the timing of printing actions. The codepath for a move is: the timing of printing actions. The codepath for a move is:
ToolHead.move() (create a Move() object) -> MoveQueue.add_move() `ToolHead.move() -> MoveQueue.add_move() -> MoveQueue.flush() ->
(place move on lookahead queue) -> MoveQueue.flush() (determine Move.set_junction() -> Move.move()`.
start/end velocity of each move) -> Move.set_junction() (perform * ToolHead.move() creates a Move() object with the parameters of the
"trapezoid generation" for a move) -> Move.move(). The "trapezoid move (in cartesian space and in units of seconds and millimeters).
generator" breaks every move into three parts: a constant * MoveQueue.add_move() places the move object on the "lookahead"
acceleration phase, followed by a constant velocity phase, followed queue.
by a constant deceleration phase. Every move contains these three * MoveQueue.flush() determines the start and end velocities of each
phases in this order, but some phases may be of zero duration. When move.
Move.move() is called, everything about the move is known - its * Move.set_junction() implements the "trapezoid generator" on a
start location, its end location, its acceleration, its move. The "trapezoid generator" breaks every move into three parts:
a constant acceleration phase, followed by a constant velocity
phase, followed by a constant deceleration phase. Every move
contains these three phases in this order, but some phases may be of
zero duration.
* When Move.move() is called, everything about the move is known -
its start location, its end location, its acceleration, its
start/crusing/end velocity, and distance traveled during start/crusing/end velocity, and distance traveled during
acceleration/cruising/deceleration. All the information is stored in acceleration/cruising/deceleration. All the information is stored in
the Move() class and is in cartesian space in units of millimeters the Move() class and is in cartesian space in units of millimeters
and seconds. Times are stored relative to the start of the and seconds. Times are stored relative to the start of the print.
print. The move is then handed off to the kinematics classes:
Move.move() -> kin.move() The move is then handed off to the kinematics classes: `Move.move()
-> kin.move()`
* The goal of the kinematics classes is to translate the movement in * The goal of the kinematics classes is to translate the movement in
cartesian space to movement on each stepper. The kinematics classes cartesian space to movement on each stepper. The kinematics classes
are in cartesian.py, corexy.py, delta.py, and extruder.py. The are in cartesian.py, corexy.py, delta.py, and extruder.py. The
kinematic class is given a chance to audit the move (ToolHead.move() kinematic class is given a chance to audit the move
-> kin.check_move()) before it goes on the lookahead queue, but once (`ToolHead.move() -> kin.check_move()`) before it goes on the
the move arrives in kin.move() the kinematic class is required to lookahead queue, but once the move arrives in *kin*.move() the
handle the move as specified. The kinematic classes translate the kinematic class is required to handle the move as specified. The
three parts of each move (acceleration, constant "cruising" kinematic classes translate the three parts of each move
velocity, and deceleration) to the associated movement on each (acceleration, constant "cruising" velocity, and deceleration) to
stepper. Note that the extruder is handled in its own kinematic the associated movement on each stepper. Note that the extruder is
class. Since the Move() class specifies the exact movement time and handled in its own kinematic class. Since the Move() class specifies
since step pulses are sent to the micro-controller with specific the exact movement time and since step pulses are sent to the
timing, stepper movements produced by the extruder class will be in micro-controller with specific timing, stepper movements produced by
sync with head movement even though the code is kept separate. the extruder class will be in sync with head movement even though
the code is kept separate.
* For efficiency reasons, the stepper pulse times are generated in C * For efficiency reasons, the stepper pulse times are generated in C
code. The code flow is: kin.move() -> MCU_Stepper.step_const() (in code. The code flow is: `kin.move() -> MCU_Stepper.step_const() ->
mcu.py) -> stepcompress_push_const() (in stepcompress.c), or for stepcompress_push_const()`, or for delta kinematics:
delta kinematics: DeltaKinematics.move() -> MCU_Stepper.step_delta() `DeltaKinematics.move() -> MCU_Stepper.step_delta() ->
-> stepcompress_push_delta(). The MCU_Stepper code just performs stepcompress_push_delta()`. The MCU_Stepper code just performs unit
unit and axis transformation (seconds to clock ticks and millimeters and axis transformation (seconds to clock ticks and millimeters to
to step distances), and calls the C code. The C code calculates the step distances), and calls the C code. The C code calculates the
stepper step times for each movement and fills an array (struct stepper step times for each movement and fills an array (struct
stepcompress.queue) with the corresponding micro-controller clock stepcompress.queue) with the corresponding micro-controller clock
counter times (in 64bit integers) for every step. Here the counter times (in 64bit integers) for every step. Here the
@ -172,28 +179,29 @@ of a typical move command.
micro-controller's hardware counter - it is relative to when the micro-controller's hardware counter - it is relative to when the
micro-controller was last powered up. micro-controller was last powered up.
* The next major step is to compress the steps: stepcompress_flush() * The next major step is to compress the steps: `stepcompress_flush()
-> compress_bisect_add() (in stepcompress.c). This code generates -> compress_bisect_add()` (in stepcompress.c). This code generates
and encodes micro-controller "queue_step" commands. The compression and encodes a series of micro-controller "queue_step" commands that
involves finding a series of queue_step interval/count/add correspond to the list of stepper step times built in the previous
parameters that correspond to the list of micro-controller step stage. These "queue_step" commands are then queued, prioritized, and
times. These "queue_step" commands are then queued, prioritized, and
sent to the micro-controller (via stepcompress.c:steppersync and sent to the micro-controller (via stepcompress.c:steppersync and
serialqueue.c:serialqueue). serialqueue.c:serialqueue).
* Processing of the queue_step commands on the micro-controller starts * Processing of the queue_step commands on the micro-controller starts
in command.c which parses the command and calls in command.c which parses the command and calls
command_queue_step(). The command_queue_step() code (in stepper.c) `command_queue_step()`. The command_queue_step() code (in stepper.c)
just appends the interval/count/add parameters to a per stepper just appends the parameters of each queue_step command to a per
queue. Under normal operation the queue_step command is parsed and stepper queue. Under normal operation the queue_step command is
queued at least 100ms before the time of the first step. Finally, parsed and queued at least 100ms before the time of its first
the generation of stepper events is done in stepper_event(). It's step. Finally, the generation of stepper events is done in
called from the hardware timer interrupt at the scheduled time of `stepper_event()`. It's called from the hardware timer interrupt at
the first step. The stepper_event() code generates a step pulse and the scheduled time of the first step. The stepper_event() code
then reschedules itself to run at the time of the next step pulse generates a step pulse and then reschedules itself to run at the
for the given interval/count/add sequence. At a high-level, the time of the next step pulse for the given queue_step parameters. The
stepper_event() code does: do_step(); next_wake_time = parameters for each queue_step command are "interval", "count", and
last_wake_time + interval; interval += add; "add". At a high-level, stepper_event() runs the following, 'count'
times: `do_step(); next_wake_time = last_wake_time + interval;
interval += add;`
The above may seem like a lot of complexity to execute a The above may seem like a lot of complexity to execute a
movement. However, the only really interesting parts are in the movement. However, the only really interesting parts are in the