2016-05-25 18:37:40 +03:00
|
|
|
This document describes the overall code layout and major code flow of
|
|
|
|
Klipper.
|
|
|
|
|
|
|
|
Directory Layout
|
|
|
|
================
|
|
|
|
|
|
|
|
The **src/** directory contains the C source for the micro-controller
|
|
|
|
code. The **src/avr/** directory contains specific code for Atmel
|
2016-07-26 17:58:33 +03:00
|
|
|
ATmega micro-controllers. The **src/sam3x8e/** directory contains code
|
|
|
|
specific to the Arduino Due style ARM micro-controllers. The
|
|
|
|
**src/simulator/** contains code stubs that allow the micro-controller
|
|
|
|
to be test compiled on other architectures. The **src/generic/**
|
|
|
|
directory contains helper code that may be useful across different
|
|
|
|
host architectures. The build arranges for includes of
|
|
|
|
"board/somefile.h" to first look in the current architecture directory
|
|
|
|
(eg, src/avr/somefile.h) and then in the generic directory (eg,
|
|
|
|
src/generic/somefile.h).
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
The **klippy/** directory contains the C and Python source for the
|
|
|
|
host part of the firmware.
|
|
|
|
|
2016-07-26 17:58:33 +03:00
|
|
|
The **lib/** directory contains external 3rd-party library code that
|
|
|
|
is necessary to build some targets.
|
|
|
|
|
2016-05-25 18:37:40 +03:00
|
|
|
The **config/** directory contains example printer configuration
|
|
|
|
files.
|
|
|
|
|
|
|
|
The **scripts/** directory contains build-time scripts useful for
|
|
|
|
compiling the micro-controller code.
|
|
|
|
|
|
|
|
During compilation, the build may create an **out/** directory. This
|
|
|
|
contains temporary build time objects. The final micro-controller
|
2016-07-26 17:58:33 +03:00
|
|
|
object that is built is **out/klipper.elf.hex** on AVR and
|
|
|
|
**out/klipper.bin** on ARM.
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
Micro-controller code flow
|
|
|
|
==========================
|
|
|
|
|
2016-07-26 17:58:33 +03:00
|
|
|
Execution of the micro-controller code starts in architecture specific
|
|
|
|
code (eg, **src/avr/main.c**) which ultimately calls sched_main()
|
|
|
|
located in **src/sched.c**. The sched_main() code starts by running
|
|
|
|
all functions that have been tagged with the DECL_INIT() macro. It
|
|
|
|
then goes on to repeatedly run all functions tagged with the
|
|
|
|
DECL_TASK() macro.
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
One of the main task functions is command_task() located in
|
|
|
|
**src/command.c**. This function processes incoming serial commands
|
|
|
|
and runs the associated command function for them. Command functions
|
|
|
|
are declared using the DECL_COMMAND() macro.
|
|
|
|
|
|
|
|
Task, init, and command functions always run with interrupts enabled
|
|
|
|
(however, they can temporarily disable interrupts if needed). These
|
|
|
|
functions should never pause, delay, or do any work that lasts more
|
|
|
|
than a few micro-seconds. These functions schedule work at specific
|
|
|
|
times by scheduling timers.
|
|
|
|
|
2017-03-11 06:12:05 +03:00
|
|
|
Timer functions are scheduled by calling sched_add_timer() (located in
|
2016-05-25 18:37:40 +03:00
|
|
|
**src/sched.c**). The scheduler code will arrange for the given
|
|
|
|
function to be called at the requested clock time. Timer interrupts
|
2016-07-26 17:58:33 +03:00
|
|
|
are initially handled in an architecture specific interrupt handler
|
2017-03-27 23:38:01 +03:00
|
|
|
(eg, **src/avr/timer.c**) which calls sched_timer_dispatch() located
|
|
|
|
in **src/sched.c**. The timer interrupt leads to execution of schedule
|
|
|
|
timer functions. Timer functions always run with interrupts
|
2016-07-26 17:58:33 +03:00
|
|
|
disabled. The timer functions should always complete within a few
|
|
|
|
micro-seconds. At completion of the timer event, the function may
|
|
|
|
choose to reschedule itself.
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
In the event an error is detected the code can invoke shutdown() (a
|
|
|
|
macro which calls sched_shutdown() located in **src/sched.c**).
|
|
|
|
Invoking shutdown() causes all functions tagged with the
|
|
|
|
DECL_SHUTDOWN() macro to be run. Shutdown functions always run with
|
|
|
|
interrupts disabled.
|
|
|
|
|
|
|
|
Much of the functionality of the micro-controller involves working
|
|
|
|
with General-Purpose Input/Output pins (GPIO). In order to abstract
|
|
|
|
the low-level architecture specific code from the high-level task
|
2016-07-26 17:58:33 +03:00
|
|
|
code, all GPIO events are implemented in architectures specific
|
|
|
|
wrappers (eg, **src/avr/gpio.c**). The code is compiled with gcc's
|
|
|
|
"-flto -fwhole-program" optimization which does an excellent job of
|
|
|
|
inlining functions across compilation units, so most of these tiny
|
|
|
|
gpio functions are inlined into their callers, and there is no
|
|
|
|
run-time cost to using them.
|
2016-05-25 18:37:40 +03:00
|
|
|
|
|
|
|
Klippy code overview
|
|
|
|
====================
|
|
|
|
|
|
|
|
The host code (Klippy) is intended to run on a low-cost computer (such
|
|
|
|
as a Raspberry Pi) paired with the micro-controller. The code is
|
|
|
|
primarily written in Python, however it does use CFFI to implement
|
|
|
|
some functionality in C code.
|
|
|
|
|
|
|
|
Initial execution starts in **klippy/klippy.py**. This reads the
|
|
|
|
command-line arguments, opens the printer config file, instantiates
|
|
|
|
the main printer objects, and starts the serial connection. The main
|
|
|
|
execution of gcode commands is in the process_commands() method in
|
|
|
|
**klippy/gcode.py**. This code translates the gcode commands into
|
|
|
|
printer object calls, which frequently translate the actions to
|
|
|
|
commands to be executed on the micro-controller (as declared via the
|
|
|
|
DECL_COMMAND macro in the micro-controller code).
|
|
|
|
|
2016-11-12 04:22:39 +03:00
|
|
|
There are four threads in the Klippy host code. The main thread
|
2016-05-25 18:37:40 +03:00
|
|
|
handles incoming gcode commands. A second thread (which resides
|
|
|
|
entirely in the **klippy/serialqueue.c** C code) handles low-level IO
|
|
|
|
with the serial port. The third thread is used to process response
|
2016-07-26 17:58:33 +03:00
|
|
|
messages from the micro-controller in the Python code (see
|
2016-11-12 04:22:39 +03:00
|
|
|
**klippy/serialhdl.py**). The fourth thread writes debug messages to
|
|
|
|
the log (see **klippy/queuelogger.py**) so that the other threads
|
|
|
|
never block on log writes.
|
2017-04-11 00:47:38 +03:00
|
|
|
|
|
|
|
Code flow of a move command
|
|
|
|
===========================
|
|
|
|
|
|
|
|
A typical printer movement starts when a "G1" command is sent to the
|
|
|
|
Klippy host and it completes when the corresponding step pulses are
|
|
|
|
produced on the micro-controller. This section outlines the code flow
|
|
|
|
of a typical move command.
|
|
|
|
|
|
|
|
* Processing for a move command (eg, "G1 X10 F6000") starts in
|
|
|
|
gcode.py. The goal of gcode.py is to translate gcode into internal
|
|
|
|
calls. Changes in origin (eg, G92), changes in relative vs absolute
|
|
|
|
positions (eg, G90), and unit changes (eg, F6000=100mm/s) are
|
|
|
|
handled here. The code path for a move is: process_data() ->
|
|
|
|
process_commands() -> cmd_G1(). Ultimately the ToolHead class is
|
|
|
|
invoked to execute the actual request: cmd_G1() ->
|
|
|
|
ToolHead.move([10, 0, 0, 0], 100.0)
|
|
|
|
|
|
|
|
* The ToolHead class (in toolhead.py) handles "lookahead" and tracks
|
|
|
|
the timing of printing actions. The codepath for a move is:
|
|
|
|
ToolHead.move() (create a Move() object) -> MoveQueue.add_move()
|
|
|
|
(place move on lookahead queue) -> MoveQueue.flush() (determine
|
|
|
|
start/end velocity of each move) -> Move.set_junction() (perform
|
|
|
|
"trapezoid generation" for a move) -> Move.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
|
|
|
|
acceleration/cruising/deceleration. All the information is stored in
|
|
|
|
the Move() class and is in cartesian space in units of millimeters
|
|
|
|
and seconds. Times are stored relative to the start of the
|
|
|
|
print. 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
|
|
|
|
cartesian space to movement on each stepper. The kinematics classes
|
|
|
|
are in cartesian.py, corexy.py, delta.py, and extruder.py. The
|
|
|
|
kinematic class is given a chance to audit the move (ToolHead.move()
|
|
|
|
-> kin.check_move()) before it goes on the lookahead queue, but once
|
|
|
|
the move arrives in kin.move() the kinematic class is required to
|
|
|
|
handle the move as specified. The kinematic classes translate the
|
|
|
|
three parts of each move (acceleration, constant "cruising"
|
|
|
|
velocity, and deceleration) to the associated movement on each
|
|
|
|
stepper. Note that the extruder is handled in its own kinematic
|
|
|
|
class. Since the Move() class specifies the exact movement time and
|
|
|
|
since step pulses are sent to the micro-controller with specific
|
|
|
|
timing, stepper movements produced by 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
|
|
|
|
code. The code flow is: kin.move() -> MCU_Stepper.step_const() (in
|
|
|
|
mcu.py) -> stepcompress_push_const() (in stepcompress.c), or for
|
|
|
|
delta kinematics: DeltaKinematics.move() -> MCU_Stepper.step_delta()
|
|
|
|
-> stepcompress_push_delta(). The MCU_Stepper code just performs
|
|
|
|
unit and axis transformation (seconds to clock ticks and millimeters
|
|
|
|
to step distances), and calls the C code. The C code calculates the
|
|
|
|
stepper step times for each movement and fills an array (struct
|
|
|
|
stepcompress.queue) with the corresponding micro-controller clock
|
|
|
|
counter times (in 64bit integers) for every step. Here the
|
|
|
|
"micro-controller clock counter" value directly corresponds to the
|
|
|
|
micro-controller's hardware counter - it is relative to when the
|
|
|
|
micro-controller was last powered up.
|
|
|
|
|
|
|
|
* The next major step is to compress the steps: stepcompress_flush()
|
|
|
|
-> compress_bisect_add() (in stepcompress.c). This code generates
|
|
|
|
and encodes micro-controller "queue_step" commands. The compression
|
|
|
|
involves finding a series of queue_step interval/count/add
|
|
|
|
parameters that correspond to the list of micro-controller step
|
|
|
|
times. These "queue_step" commands are then queued, prioritized, and
|
|
|
|
sent to the micro-controller (via stepcompress.c:steppersync and
|
|
|
|
serialqueue.c:serialqueue).
|
|
|
|
|
|
|
|
* Processing of the queue_step commands on the micro-controller starts
|
|
|
|
in command.c which parses the command and calls
|
|
|
|
command_queue_step(). The command_queue_step() code (in stepper.c)
|
|
|
|
just appends the interval/count/add parameters to a per stepper
|
|
|
|
queue. Under normal operation the queue_step command is parsed and
|
|
|
|
queued at least 100ms before the time of the first step. Finally,
|
|
|
|
the generation of stepper events is done in stepper_event(). It's
|
|
|
|
called from the hardware timer interrupt at the scheduled time of
|
|
|
|
the first step. The stepper_event() code generates a step pulse and
|
|
|
|
then reschedules itself to run at the time of the next step pulse
|
|
|
|
for the given interval/count/add sequence. At a high-level, the
|
|
|
|
stepper_event() code does: do_step(); next_wake_time =
|
|
|
|
last_wake_time + interval; interval += add;
|
|
|
|
|
|
|
|
The above may seem like a lot of complexity to execute a
|
|
|
|
movement. However, the only really interesting parts are in the
|
|
|
|
ToolHead and kinematic classes. It's this part of the code which
|
|
|
|
specifies the movements and their timings. The remaining parts of the
|
|
|
|
processing is mostly just communication and plumbing.
|