mirror of
				https://github.com/Klipper3d/klipper.git
				synced 2025-10-29 17:26:14 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			613 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			613 lines
		
	
	
		
			33 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Code overview
 | |
| 
 | |
| 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/atsam/**, **src/atsamd/**, **src/avr/**,
 | |
| **src/linux/**, **src/lpc176x/**, **src/pru/**, and **src/stm32/**
 | |
| directories contain architecture specific micro-controller code. 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
 | |
| 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).
 | |
| 
 | |
| The **klippy/** directory contains the host software. Most of the host
 | |
| software is written in Python, however the **klippy/chelper/**
 | |
| directory contains some C code helpers. The **klippy/kinematics/**
 | |
| directory contains the robot kinematics code. The **klippy/extras/**
 | |
| directory contains the host code extensible "modules".
 | |
| 
 | |
| The **lib/** directory contains external 3rd-party library code that
 | |
| is necessary to build some targets.
 | |
| 
 | |
| The **config/** directory contains example printer configuration
 | |
| files.
 | |
| 
 | |
| The **scripts/** directory contains build-time scripts useful for
 | |
| compiling the micro-controller code.
 | |
| 
 | |
| The **test/** directory contains automated test cases.
 | |
| 
 | |
| During compilation, the build may create an **out/** directory. This
 | |
| contains temporary build time objects. The final micro-controller
 | |
| object that is built is **out/klipper.elf.hex** on AVR and
 | |
| **out/klipper.bin** on ARM.
 | |
| 
 | |
| ## Micro-controller code flow
 | |
| 
 | |
| 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.
 | |
| 
 | |
| One of the main task functions is command_dispatch() located in
 | |
| **src/command.c**. This function is called from the board specific
 | |
| input/output code (eg, **src/avr/serial.c**,
 | |
| **src/generic/serial_irq.c**) and it runs the command functions
 | |
| associated with the commands found in the input stream. Command
 | |
| functions are declared using the DECL_COMMAND() macro (see the
 | |
| [protocol](Protocol.md) document for more information).
 | |
| 
 | |
| Task, init, and command functions always run with interrupts enabled
 | |
| (however, they can temporarily disable interrupts if needed). These
 | |
| functions should avoid long pauses, delays, or do work that lasts a
 | |
| significant time. (Long delays in these "task" functions result in
 | |
| scheduling jitter for other "tasks" - delays over 100us may become
 | |
| noticeable, delays over 500us may result in command retransmissions,
 | |
| delays over 100ms may result in watchdog reboots.) These functions
 | |
| schedule work at specific times by scheduling timers.
 | |
| 
 | |
| Timer functions are scheduled by calling sched_add_timer() (located in
 | |
| **src/sched.c**). The scheduler code will arrange for the given
 | |
| function to be called at the requested clock time. Timer interrupts
 | |
| are initially handled in an architecture specific interrupt handler
 | |
| (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 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.
 | |
| 
 | |
| 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
 | |
| code, all GPIO events are implemented in architecture 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.
 | |
| 
 | |
| ## 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 G-code commands is in the _process_commands() method in
 | |
| **klippy/gcode.py**. This code translates the G-code 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).
 | |
| 
 | |
| There are several threads in the Klipper host code:
 | |
| * There is a Python "main thread" that handles incoming G-Code
 | |
|   commands and is the starting point for most actions. This thread
 | |
|   runs the [reactor](https://en.wikipedia.org/wiki/Reactor_pattern)
 | |
|   (**klippy/reactor.py**) and most high-level actions originate from
 | |
|   IO and timer event callbacks from that reactor.
 | |
| * A thread for writing messages to the log so that the other threads
 | |
|   do not block on log writes. This thread resides in the
 | |
|   **klippy/queuelogger.py** code and its multi-threaded nature is not
 | |
|   exposed to the main Python thread.
 | |
| * A thread per micro-controller that performs the low-level reading
 | |
|   and writing of messages to that micro-controller. It resides in the
 | |
|   **klippy/chelper/serialqueue.c** C code and its multi-threaded
 | |
|   nature is not exposed to the Python code.
 | |
| * A thread per micro-controller for processing messages received from
 | |
|   that micro-controller in the Python code. This thread is created in
 | |
|   **klippy/serialhdl.py**. Care must be taken in Python callbacks
 | |
|   invoked from this thread as this thread may directly interact with
 | |
|   the main Python thread.
 | |
| * A thread per stepper motor that calculates the timing of stepper
 | |
|   motor step pulses and compresses those times. This thread resides in
 | |
|   the **klippy/chelper/steppersync.c** C code and its multi-threaded
 | |
|   nature is not exposed to the Python code.
 | |
| 
 | |
| ## 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. The [kinematics](Kinematics.md) document
 | |
| provides further information on the mechanics of moves.
 | |
| 
 | |
| * Processing for a move command starts in gcode.py. The goal of
 | |
|   gcode.py is to translate G-code into internal calls. A G1 command
 | |
|   will invoke cmd_G1() in klippy/extras/gcode_move.py. The
 | |
|   gcode_move.py code handles changes in origin (eg, G92), changes in
 | |
|   relative vs absolute positions (eg, G90), and unit changes (eg,
 | |
|   F6000=100mm/s). 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()`
 | |
| 
 | |
| * The ToolHead class (in toolhead.py) handles "look-ahead" and tracks
 | |
|   the timing of printing actions. The main codepath for a move is:
 | |
|   `ToolHead.move() -> LookAheadQueue.add_move()`, then
 | |
|   `ToolHead.move() -> ToolHead._process_lookahead() ->
 | |
|   LookAheadQueue.flush() -> Move.set_junction()`, and then
 | |
|   `ToolHead._process_lookahead() -> trapq_append()`.
 | |
|   * ToolHead.move() creates a Move() object with the parameters of the
 | |
|   move (in cartesian space and in units of seconds and millimeters).
 | |
|   * The kinematics class is given the opportunity to audit each move
 | |
|   (`ToolHead.move() -> kin.check_move()`). The kinematics classes are
 | |
|   located in the klippy/kinematics/ directory. The check_move() code
 | |
|   may raise an error if the move is not valid. If check_move()
 | |
|   completes successfully then the underlying kinematics must be able
 | |
|   to handle the move.
 | |
|   * LookAheadQueue.add_move() places the move object on the
 | |
|   "look-ahead" queue.
 | |
|   * LookAheadQueue.flush() determines the start and end velocities of
 | |
|   each move.
 | |
|   * Move.set_junction() implements the "trapezoid generator" on a
 | |
|   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 ToolHead._process_lookahead() resumes, everything about the
 | |
|   move is known - its start location, its end location, its
 | |
|   acceleration, its start/cruising/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.
 | |
|   * The moves are then placed on a "trapezoid motion queue" via
 | |
|   trapq_append() (in klippy/chelper/trapq.c). The trapq stores all the
 | |
|   information in the Move() class in a C struct accessible to the host
 | |
|   C code.
 | |
| 
 | |
| * Note that the extruder is handled in its own kinematic class:
 | |
|   `ToolHead._process_lookahead() -> PrinterExtruder.process_move()`.
 | |
|   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, stepper motion is generated in the C code in
 | |
|   a thread per stepper motor. The threads are notified when steps
 | |
|   should be generated by the motion_queuing module
 | |
|   (klippy/extras/motion_queuing.py):
 | |
|   `PrinterMotionQueuing._flush_handler() ->
 | |
|   PrinterMotionQueuing._advance_move_time() ->
 | |
|   steppersyncmgr_gen_steps() -> se_start_gen_steps()`.
 | |
| 
 | |
| * Klipper uses an
 | |
|   [iterative solver](https://en.wikipedia.org/wiki/Root-finding_algorithm)
 | |
|   to generate the step times for each stepper. The step times are
 | |
|   generated from the background thread (klippy/chelper/steppersync.c):
 | |
|   `se_background_thread() -> se_generate_steps() ->
 | |
|   itersolve_generate_steps() -> itersolve_gen_steps_range()` (in
 | |
|   klippy/chelper/itersolve.c). The goal of the iterative solver is to
 | |
|   find step times given a function that calculates a stepper position
 | |
|   from a time. This is done by repeatedly "guessing" various times
 | |
|   until the stepper position formula returns the desired position of
 | |
|   the next step on the stepper. The feedback produced from each guess
 | |
|   is used to improve future guesses so that the process rapidly
 | |
|   converges to the desired time. The kinematic stepper position
 | |
|   formulas are located in the klippy/chelper/ directory (eg,
 | |
|   kin_cart.c, kin_corexy.c, kin_delta.c, kin_extruder.c).
 | |
| 
 | |
| * After the iterative solver calculates the step times they are added
 | |
|   to an array: `itersolve_gen_steps_range() -> stepcompress_append()`
 | |
|   (in klippy/chelper/stepcompress.c). The array (struct
 | |
|   stepcompress.queue) stores the corresponding micro-controller clock
 | |
|   counter times 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 klippy/chelper/stepcompress.c). This
 | |
|   code generates and encodes a series of micro-controller "queue_step"
 | |
|   commands that correspond to the list of stepper step times built in
 | |
|   the previous stage. These "queue_step" commands are then queued,
 | |
|   prioritized, and sent to the micro-controller (via
 | |
|   steppersync.c:steppersync and serialqueue.c:serialqueue).
 | |
| 
 | |
| * Processing of the queue_step commands on the micro-controller starts
 | |
|   in src/command.c which parses the command and calls
 | |
|   `command_queue_step()`. The command_queue_step() code (in
 | |
|   src/stepper.c) just appends the parameters of each queue_step
 | |
|   command to a per stepper queue. Under normal operation the
 | |
|   queue_step command is parsed and queued at least 100ms before the
 | |
|   time of its 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
 | |
|   queue_step parameters. The parameters for each queue_step command
 | |
|   are "interval", "count", and "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 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.
 | |
| 
 | |
| ## Adding a host module
 | |
| 
 | |
| The Klippy host code has a dynamic module loading capability. If a
 | |
| config section named "[my_module]" is found in the printer config file
 | |
| then the software will automatically attempt to load the python module
 | |
| klippy/extras/my_module.py . This module system is the preferred
 | |
| method for adding new functionality to Klipper.
 | |
| 
 | |
| The easiest way to add a new module is to use an existing module as a
 | |
| reference - see **klippy/extras/servo.py** as an example.
 | |
| 
 | |
| The following may also be useful:
 | |
| * Execution of the module starts in the module level `load_config()`
 | |
|   function (for config sections of the form [my_module]) or in
 | |
|   `load_config_prefix()` (for config sections of the form
 | |
|   [my_module my_name]). This function is passed a "config" object and
 | |
|   it must return a new "printer object" associated with the given
 | |
|   config section.
 | |
| * During the process of instantiating a new printer object, the config
 | |
|   object can be used to read parameters from the given config
 | |
|   section. This is done using `config.get()`, `config.getfloat()`,
 | |
|   `config.getint()`, etc. methods. Be sure to read all values from the
 | |
|   config during the construction of the printer object - if the user
 | |
|   specifies a config parameter that is not read during this phase then
 | |
|   it will be assumed it is a typo in the config and an error will be
 | |
|   raised.
 | |
| * Use the `config.get_printer()` method to obtain a reference to the
 | |
|   main "printer" class. This "printer" class stores references to all
 | |
|   the "printer objects" that have been instantiated. Use the
 | |
|   `printer.lookup_object()` method to find references to other printer
 | |
|   objects. Almost all functionality (even core kinematic modules) are
 | |
|   encapsulated in one of these printer objects. Note, though, that
 | |
|   when a new module is instantiated, not all other printer objects
 | |
|   will have been instantiated. The "gcode" and "pins" modules will
 | |
|   always be available, but for other modules it is a good idea to
 | |
|   defer the lookup.
 | |
| * Register event handlers using the `printer.register_event_handler()`
 | |
|   method if the code needs to be called during "events" raised by
 | |
|   other printer objects. Each event name is a string, and by
 | |
|   convention it is the name of the main source module that raises the
 | |
|   event along with a short name for the action that is occurring (eg,
 | |
|   "klippy:connect"). The parameters passed to each event handler are
 | |
|   specific to the given event (as are exception handling and execution
 | |
|   context). Two common startup events are:
 | |
|   * klippy:connect - This event is generated after all printer objects
 | |
|     are instantiated. It is commonly used to lookup other printer
 | |
|     objects, to verify config settings, and to perform an initial
 | |
|     "handshake" with printer hardware.
 | |
|   * klippy:ready - This event is generated after all connect handlers
 | |
|     have completed successfully. It indicates the printer is
 | |
|     transitioning to a state ready to handle normal operations. Do not
 | |
|     raise an error in this callback.
 | |
| * If there is an error in the user's config, be sure to raise it
 | |
|   during the `load_config()` or "connect event" phases. Use either
 | |
|   `raise config.error("my error")` or `raise printer.config_error("my
 | |
|   error")` to report the error.
 | |
| * Do not store a reference to the `config` object in a class member
 | |
|   variable (nor in any similar location that may persist past initial
 | |
|   module loading). The `config` object is a reference to a "config
 | |
|   loading phase" class and it is not valid to invoke its methods after
 | |
|   the "config loading phase" has completed.
 | |
| * Use the "pins" module to configure a pin on a micro-controller. This
 | |
|   is typically done with something similar to
 | |
|   `printer.lookup_object("pins").setup_pin("pwm",
 | |
|   config.get("my_pin"))`. The returned object can then be commanded at
 | |
|   run-time.
 | |
| * If the printer object defines a `get_status()` method then the
 | |
|   module can export [status information](Status_Reference.md) via
 | |
|   [macros](Command_Templates.md) and via the
 | |
|   [API Server](API_Server.md). The `get_status()` method must return a
 | |
|   Python dictionary with keys that are strings and values that are
 | |
|   integers, floats, strings, lists, dictionaries, True, False, or
 | |
|   None. Tuples (and named tuples) may also be used (these appear as
 | |
|   lists when accessed via the API Server). Lists and dictionaries that
 | |
|   are exported must be treated as "immutable" - if their contents
 | |
|   change then a new object must be returned from `get_status()`,
 | |
|   otherwise the API Server will not detect those changes.
 | |
| * If the module needs access to system timing or external file
 | |
|   descriptors then use `printer.get_reactor()` to obtain access to the
 | |
|   global "event reactor" class. This reactor class allows one to
 | |
|   schedule timers, wait for input on file descriptors, and to "sleep"
 | |
|   the host code.
 | |
| * Do not use global variables. All state should be stored in the
 | |
|   printer object returned from the `load_config()` function. This is
 | |
|   important as otherwise the RESTART command may not perform as
 | |
|   expected. Also, for similar reasons, if any external files (or
 | |
|   sockets) are opened then be sure to register a "klippy:disconnect"
 | |
|   event handler and close them from that callback.
 | |
| * Avoid accessing the internal member variables (or calling methods
 | |
|   that start with an underscore) of other printer objects. Observing
 | |
|   this convention makes it easier to manage future changes.
 | |
| * It is recommended to assign a value to all member variables in the
 | |
|   Python constructor of Python classes. (And therefore avoid utilizing
 | |
|   Python's ability to dynamically create new member variables.)
 | |
| * If a Python variable is to store a floating point value then it is
 | |
|   recommended to always assign and manipulate that variable with
 | |
|   floating point constants (and never use integer constants). For
 | |
|   example, prefer `self.speed = 1.` over `self.speed = 1`, and prefer
 | |
|   `self.speed = 2. * x` over `self.speed = 2 * x`. Consistent use of
 | |
|   floating point values can avoid hard to debug quirks in Python type
 | |
|   conversions.
 | |
| * If submitting the module for inclusion in the main Klipper code, be
 | |
|   sure to place a copyright notice at the top of the module. See the
 | |
|   existing modules for the preferred format.
 | |
| 
 | |
| ## Adding new kinematics
 | |
| 
 | |
| This section provides some tips on adding support to Klipper for
 | |
| additional types of printer kinematics. This type of activity requires
 | |
| excellent understanding of the math formulas for the target
 | |
| kinematics. It also requires software development skills - though one
 | |
| should only need to update the host software.
 | |
| 
 | |
| Useful steps:
 | |
| 1. Start by studying the
 | |
|    "[code flow of a move](#code-flow-of-a-move-command)" section and
 | |
|    the [Kinematics document](Kinematics.md).
 | |
| 2. Review the existing kinematic classes in the klippy/kinematics/
 | |
|    directory. The kinematic classes are tasked with converting a move
 | |
|    in cartesian coordinates to the movement on each stepper. One
 | |
|    should be able to copy one of these files as a starting point.
 | |
| 3. Implement the C stepper kinematic position functions for each
 | |
|    stepper if they are not already available (see kin_cart.c,
 | |
|    kin_corexy.c, and kin_delta.c in klippy/chelper/). The function
 | |
|    should call `move_get_coord()` to convert a given move time (in
 | |
|    seconds) to a cartesian coordinate (in millimeters), and then
 | |
|    calculate the desired stepper position (in millimeters) from that
 | |
|    cartesian coordinate.
 | |
| 4. Implement the `calc_position()` method in the new kinematics class.
 | |
|    This method calculates the position of the toolhead in cartesian
 | |
|    coordinates from the position of each stepper. It does not need to
 | |
|    be efficient as it is typically only called during homing and
 | |
|    probing operations.
 | |
| 5. Other methods. Implement the `check_move()`, `get_status()`,
 | |
|    `get_steppers()`, `home()`, `clear_homing_state()`, and `set_position()`
 | |
|    methods. These functions are typically used to provide kinematic
 | |
|    specific checks. However, at the start of development one can use
 | |
|    boiler-plate code here.
 | |
| 6. Implement test cases. Create a g-code file with a series of moves
 | |
|    that can test important cases for the given kinematics. Follow the
 | |
|    [debugging documentation](Debugging.md) to convert this g-code file
 | |
|    to micro-controller commands. This is useful to exercise corner
 | |
|    cases and to check for regressions.
 | |
| 
 | |
| ## Porting to a new micro-controller
 | |
| 
 | |
| This section provides some tips on porting Klipper's micro-controller
 | |
| code to a new architecture. This type of activity requires good
 | |
| knowledge of embedded development and hands-on access to the target
 | |
| micro-controller.
 | |
| 
 | |
| Useful steps:
 | |
| 1. Start by identifying any 3rd party libraries that will be used
 | |
|    during the port. Common examples include "CMSIS" wrappers and
 | |
|    manufacturer "HAL" libraries. All 3rd party code needs to be GNU
 | |
|    GPLv3 compatible. The 3rd party code should be committed to the
 | |
|    Klipper lib/ directory. Update the lib/README file with information
 | |
|    on where and when the library was obtained. It is preferable to
 | |
|    copy the code into the Klipper repository unchanged, but if any
 | |
|    changes are required then those changes should be listed explicitly
 | |
|    in the lib/README file.
 | |
| 2. Create a new architecture sub-directory in the src/ directory and
 | |
|    add initial Kconfig and Makefile support. Use the existing
 | |
|    architectures as a guide. The src/simulator provides a basic
 | |
|    example of a minimum starting point.
 | |
| 3. The first main coding task is to bring up communication support to
 | |
|    the target board. This is the most difficult step in a new port.
 | |
|    Once basic communication is working, the remaining steps tend to be
 | |
|    much easier. It is typical to use a UART type serial device during
 | |
|    initial development as these types of hardware devices are
 | |
|    generally easier to enable and control. During this phase, make
 | |
|    liberal use of helper code from the src/generic/ directory (check
 | |
|    how src/simulator/Makefile includes the generic C code into the
 | |
|    build). It is also necessary to define timer_read_time() (which
 | |
|    returns the current system clock) in this phase, but it is not
 | |
|    necessary to fully support timer irq handling.
 | |
| 4. Get familiar with the the console.py tool (as described in the
 | |
|    [debugging document](Debugging.md)) and verify connectivity to the
 | |
|    micro-controller with it. This tool translates the low-level
 | |
|    micro-controller communication protocol to a human readable form.
 | |
| 5. Add support for timer dispatch from hardware interrupts. See
 | |
|    Klipper
 | |
|    [commit 970831ee](https://github.com/Klipper3d/klipper/commit/970831ee0d3b91897196e92270d98b2a3067427f)
 | |
|    as an example of steps 1-5 done for the LPC176x architecture.
 | |
| 6. Bring up basic GPIO input and output support. See Klipper
 | |
|    [commit c78b9076](https://github.com/Klipper3d/klipper/commit/c78b90767f19c9e8510c3155b89fb7ad64ca3c54)
 | |
|    as an example of this.
 | |
| 7. Bring up additional peripherals - for example see Klipper commit
 | |
|    [65613aed](https://github.com/Klipper3d/klipper/commit/65613aeddfb9ef86905cb1dade9e773a02ef3c27),
 | |
|    [c812a40a](https://github.com/Klipper3d/klipper/commit/c812a40a3782415e454b04bf7bd2158a6f0ec8b5),
 | |
|    and
 | |
|    [c381d03a](https://github.com/Klipper3d/klipper/commit/c381d03aad5c3ee761169b7c7bced519cc14da29).
 | |
| 8. Create a sample Klipper config file in the config/ directory. Test
 | |
|    the micro-controller with the main klippy.py program.
 | |
| 9. Consider adding build test cases in the test/ directory.
 | |
| 
 | |
| Additional coding tips:
 | |
| 1. Avoid using "C bitfields" to access IO registers; prefer direct
 | |
|    read and write operations of 32bit, 16bit, or 8bit integers. The C
 | |
|    language specifications don't clearly specify how the compiler must
 | |
|    implement C bitfields (eg, endianness, and bit layout), and it's
 | |
|    difficult to determine what IO operations will occur on a C
 | |
|    bitfield read or write.
 | |
| 2. Prefer writing explicit values to IO registers instead of using
 | |
|    read-modify-write operations. That is, if updating a field in an IO
 | |
|    register where the other fields have known values, then it is
 | |
|    preferable to explicitly write the full contents of the register.
 | |
|    Explicit writes produce code that is smaller, faster, and easier to
 | |
|    debug.
 | |
| 
 | |
| ## Coordinate Systems
 | |
| 
 | |
| Internally, Klipper primarily tracks the position of the toolhead in
 | |
| cartesian coordinates that are relative to the coordinate system
 | |
| specified in the config file. That is, most of the Klipper code will
 | |
| never experience a change in coordinate systems. If the user makes a
 | |
| request to change the origin (eg, a `G92` command) then that effect is
 | |
| obtained by translating future commands to the primary coordinate
 | |
| system.
 | |
| 
 | |
| However, in some cases it is useful to obtain the toolhead position in
 | |
| some other coordinate system and Klipper has several tools to
 | |
| facilitate that. This can be seen by running the GET_POSITION
 | |
| command. For example:
 | |
| 
 | |
| ```
 | |
| Send: GET_POSITION
 | |
| Recv: // mcu: stepper_a:-2060 stepper_b:-1169 stepper_c:-1613
 | |
| Recv: // stepper: stepper_a:457.254159 stepper_b:466.085669 stepper_c:465.382132
 | |
| Recv: // kinematic: X:8.339144 Y:-3.131558 Z:233.347121
 | |
| Recv: // toolhead: X:8.338078 Y:-3.123175 Z:233.347878 E:0.000000
 | |
| Recv: // gcode: X:8.338078 Y:-3.123175 Z:233.347878 E:0.000000
 | |
| Recv: // gcode base: X:0.000000 Y:0.000000 Z:0.000000 E:0.000000
 | |
| Recv: // gcode homing: X:0.000000 Y:0.000000 Z:0.000000
 | |
| ```
 | |
| 
 | |
| The "mcu" position (`stepper.get_mcu_position()` in the code) is the
 | |
| total number of steps the micro-controller has issued in a positive
 | |
| direction minus the number of steps issued in a negative direction
 | |
| since the micro-controller was last reset. If the robot is in motion
 | |
| when the query is issued then the reported value includes moves
 | |
| buffered on the micro-controller, but does not include moves on the
 | |
| look-ahead queue.
 | |
| 
 | |
| The "stepper" position (`stepper.get_commanded_position()`) is the
 | |
| position of the given stepper as tracked by the kinematics code. This
 | |
| generally corresponds to the position (in mm) of the carriage along
 | |
| its rail, relative to the position_endstop specified in the config
 | |
| file. (Some kinematics track stepper positions in radians instead of
 | |
| millimeters.) If the robot is in motion when the query is issued then
 | |
| the reported value includes moves buffered on the micro-controller,
 | |
| but does not include moves on the look-ahead queue. One may use the
 | |
| `toolhead.flush_step_generation()` or `toolhead.wait_moves()` calls to
 | |
| fully flush the look-ahead and step generation code.
 | |
| 
 | |
| The "kinematic" position (`kin.calc_position()`) is the cartesian
 | |
| position of the toolhead as derived from "stepper" positions and is
 | |
| relative to the coordinate system specified in the config file. This
 | |
| may differ from the requested cartesian position due to the
 | |
| granularity of the stepper motors. If the robot is in motion when the
 | |
| "stepper" positions are taken then the reported value includes moves
 | |
| buffered on the micro-controller, but does not include moves on the
 | |
| look-ahead queue. One may use the `toolhead.flush_step_generation()`
 | |
| or `toolhead.wait_moves()` calls to fully flush the look-ahead and
 | |
| step generation code.
 | |
| 
 | |
| The "toolhead" position (`toolhead.get_position()`) is the last
 | |
| requested position of the toolhead in cartesian coordinates relative
 | |
| to the coordinate system specified in the config file. If the robot is
 | |
| in motion when the query is issued then the reported value includes
 | |
| all requested moves (even those in buffers waiting to be issued to the
 | |
| stepper motor drivers).
 | |
| 
 | |
| The "gcode" position is the last requested position from a `G1` (or
 | |
| `G0`) command in cartesian coordinates relative to the coordinate
 | |
| system specified in the config file. This may differ from the
 | |
| "toolhead" position if a g-code transformation (eg, bed_mesh,
 | |
| bed_tilt, skew_correction) is in effect. This may differ from the
 | |
| actual coordinates specified in the last `G1` command if the g-code
 | |
| origin has been changed (eg, `G92`, `SET_GCODE_OFFSET`, `M221`). The
 | |
| `M114` command (`gcode_move.get_status()['gcode_position']`) will
 | |
| report the last g-code position relative to the current g-code
 | |
| coordinate system.
 | |
| 
 | |
| The "gcode base" is the location of the g-code origin in cartesian
 | |
| coordinates relative to the coordinate system specified in the config
 | |
| file. Commands such as `G92`, `SET_GCODE_OFFSET`, and `M221` alter
 | |
| this value.
 | |
| 
 | |
| The "gcode homing" is the location to use for the g-code origin (in
 | |
| cartesian coordinates relative to the coordinate system specified in
 | |
| the config file) after a `G28` home command. The `SET_GCODE_OFFSET`
 | |
| command can alter this value.
 | |
| 
 | |
| ## Time
 | |
| 
 | |
| Fundamental to the operation of Klipper is the handling of clocks,
 | |
| times, and timestamps. Klipper executes actions on the printer by
 | |
| scheduling events to occur in the near future. For example, to turn on
 | |
| a fan, the code might schedule a change to a GPIO pin in a 100ms. It
 | |
| is rare for the code to attempt to take an instantaneous action. Thus,
 | |
| the handling of time within Klipper is critical to correct operation.
 | |
| 
 | |
| There are three types of times tracked internally in the Klipper host
 | |
| software:
 | |
| * System time. The system time uses the system's monotonic clock - it
 | |
|   is a floating point number stored as seconds and it is (generally)
 | |
|   relative to when the host computer was last started. System times
 | |
|   have limited use in the software - they are primarily used when
 | |
|   interacting with the operating system. Within the host code, system
 | |
|   times are frequently stored in variables named *eventtime* or
 | |
|   *curtime*.
 | |
| * Print time. The print time is synchronized to the main
 | |
|   micro-controller clock (the micro-controller defined in the "[mcu]"
 | |
|   config section). It is a floating point number stored as seconds and
 | |
|   is relative to when the main mcu was last restarted. It is possible
 | |
|   to convert from a "print time" to the main micro-controller's
 | |
|   hardware clock by multiplying the print time by the mcu's statically
 | |
|   configured frequency rate. The high-level host code uses print times
 | |
|   to calculate almost all physical actions (eg, head movement, heater
 | |
|   changes, etc.). Within the host code, print times are generally
 | |
|   stored in variables named *print_time* or *move_time*.
 | |
| * MCU clock. This is the hardware clock counter on each
 | |
|   micro-controller. It is stored as an integer and its update rate is
 | |
|   relative to the frequency of the given micro-controller. The host
 | |
|   software translates its internal times to clocks before transmission
 | |
|   to the mcu. The mcu code only ever tracks time in clock
 | |
|   ticks. Within the host code, clock values are tracked as 64bit
 | |
|   integers, while the mcu code uses 32bit integers. Within the host
 | |
|   code, clocks are generally stored in variables with names containing
 | |
|   *clock* or *ticks*.
 | |
| 
 | |
| Conversion between the different time formats is primarily implemented
 | |
| in the **klippy/clocksync.py** code.
 | |
| 
 | |
| Some things to be aware of when reviewing the code:
 | |
| * 32bit and 64bit clocks: To reduce bandwidth and to improve
 | |
|   micro-controller efficiency, clocks on the micro-controller are
 | |
|   tracked as 32bit integers. When comparing two clocks in the mcu
 | |
|   code, the `timer_is_before()` function must always be used to ensure
 | |
|   integer rollovers are handled properly. The host software converts
 | |
|   32bit clocks to 64bit clocks by appending the high-order bits from
 | |
|   the last mcu timestamp it has received - no message from the mcu is
 | |
|   ever more than 2^31 clock ticks in the future or past so this
 | |
|   conversion is never ambiguous. The host converts from 64bit clocks
 | |
|   to 32bit clocks by simply truncating the high-order bits. To ensure
 | |
|   there is no ambiguity in this conversion, the
 | |
|   **klippy/chelper/serialqueue.c** code will buffer messages until
 | |
|   they are within 2^31 clock ticks of their target time.
 | |
| * Multiple micro-controllers: The host software supports using
 | |
|   multiple micro-controllers on a single printer. In this case, the
 | |
|   "MCU clock" of each micro-controller is tracked separately. The
 | |
|   clocksync.py code handles clock drift between micro-controllers by
 | |
|   modifying the way it converts from "print time" to "MCU clock". On
 | |
|   secondary mcus, the mcu frequency that is used in this conversion is
 | |
|   regularly updated to account for measured drift.
 |