aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/example-extras.cfg53
-rw-r--r--config/kit-voron2-2018.cfg222
-rw-r--r--config/kit-voron2-250mm.cfg299
-rw-r--r--docs/Bed_Level.md211
-rw-r--r--docs/Config_checks.md17
-rw-r--r--docs/Delta_Calibrate.md29
-rwxr-xr-xdocs/Features.md4
-rw-r--r--docs/G-Codes.md60
-rw-r--r--docs/Overview.md3
-rw-r--r--docs/Probe_Calibrate.md66
-rw-r--r--docs/img/paper-test.jpgbin0 -> 30745 bytes
-rw-r--r--klippy/extras/bus.py49
-rw-r--r--klippy/extras/manual_probe.py163
-rw-r--r--klippy/extras/manual_stepper.py117
-rw-r--r--klippy/extras/probe.py71
-rw-r--r--klippy/extras/tmc2130.py4
-rw-r--r--klippy/extras/tmc2208.py2
-rw-r--r--klippy/toolhead.py2
-rw-r--r--src/Makefile2
-rw-r--r--src/atsam/Kconfig1
-rw-r--r--src/atsam/Makefile2
-rw-r--r--src/atsam/gpio.h10
-rw-r--r--src/atsam/hard_pwm.c96
-rw-r--r--src/atsamd/gpio.h4
-rw-r--r--src/spi_software.c81
-rw-r--r--src/spi_software.h11
-rw-r--r--src/spicmds.c39
-rw-r--r--test/klippy/manual_stepper.cfg22
-rw-r--r--test/klippy/manual_stepper.test24
-rw-r--r--test/klippy/multi_z.test24
-rw-r--r--test/klippy/printers.test2
-rw-r--r--test/klippy/z_virtual_endstop.test13
32 files changed, 1397 insertions, 306 deletions
diff --git a/config/example-extras.cfg b/config/example-extras.cfg
index 36ff7fec..c5b02957 100644
--- a/config/example-extras.cfg
+++ b/config/example-extras.cfg
@@ -598,6 +598,25 @@
# with "!". This parameter must be provided.
+# Manual steppers (one may define any number of sections with a
+# "manual_stepper" prefix). These are steppers that are controlled by
+# the MANUAL_STEPPER g-code command. For example: "MANUAL_STEPPER
+# STEPPER=my_stepper MOVE=10 SPEED=5". See the docs/G-Codes.md file
+# for a description of the MANUAL_STEPPER command. The steppers are
+# not connected to the normal printer kinematics.
+#[manual_stepper my_stepper]
+#step_pin:
+#dir_pin:
+#enable_pin:
+#step_distance:
+# See the "[stepper_x]" section in example.cfg for a description of
+# these parameters.
+#endstop_pin:
+# Endstop switch detection pin. If specified, then one may perform
+# "homing moves" by adding a STOP_ON_ENDSTOP parameter to
+# MANUAL_STEPPER movement commands.
+
+
# Run-time configurable output pins (one may define any number of
# sections with an "output_pin" prefix). Pins configured here will be
# setup as output pins and one may modify them at run-time using
@@ -661,6 +680,13 @@
# The pin corresponding to the AD5206 chip select line. This pin
# will be set to low at the start of SPI messages and raised to high
# after the message completes. This parameter must be provided.
+#spi_bus:
+#spi_speed:
+#spi_software_sclk_pin:
+#spi_software_mosi_pin:
+#spi_software_miso_pin:
+# These optional parameters allow one to customize the SPI settings
+# used to communicate with the chip.
#channel_1:
#channel_2:
#channel_3:
@@ -768,6 +794,13 @@
# The pin corresponding to the TMC2130 chip select line. This pin
# will be set to low at the start of SPI messages and raised to high
# after the message completes. This parameter must be provided.
+#spi_bus:
+#spi_speed:
+#spi_software_sclk_pin:
+#spi_software_mosi_pin:
+#spi_software_miso_pin:
+# These optional parameters allow one to customize the SPI settings
+# used to communicate with the chip.
#microsteps:
# The number of microsteps to configure the driver to use. Valid
# values are 1, 2, 4, 8, 16, 32, 64, 128, 256. This parameter must
@@ -889,6 +922,11 @@
#spi_speed: 2000000
# SPI bus frequency used to communicate with the TMC2660 stepper
# driver. The default is 2000000.
+#spi_software_sclk_pin:
+#spi_software_mosi_pin:
+#spi_software_miso_pin:
+# These optional parameters allow one to customize the SPI settings
+# used to communicate with the chip.
#microsteps:
# The number of microsteps to configure the driver to use. Valid
# values are 1, 2, 4, 8, 16, 32, 64, 128, 256. This parameter must
@@ -1024,8 +1062,15 @@
# provided when using an uc1701 display.
#cs_pin:
#dc_pin:
+#spi_bus:
+#spi_speed:
+#spi_software_sclk_pin:
+#spi_software_mosi_pin:
+#spi_software_miso_pin:
# The pins connected to an ssd1306 type lcd when in "4-wire" spi
-# mode. The default is to use i2c mode for ssd1306 displays.
+# mode. The parameters that start with "spi_" are optional and they
+# control the spi settings used to communicate with the chip. The
+# default is to use i2c mode for ssd1306 displays.
#menu_root:
# Entry point for menu, root menu container name. If this parameter
# is not provided then default menu root is used. When provided
@@ -1121,6 +1166,12 @@
#spi_speed: 4000000
# The SPI speed (in hz) to use when communicating with the chip.
# The default is 4000000.
+#spi_bus:
+#spi_software_sclk_pin:
+#spi_software_mosi_pin:
+#spi_software_miso_pin:
+# These optional parameters allow one to customize the SPI settings
+# used to communicate with the chip.
#sensor_pin:
# The chip select line for the sensor chip. This parameter must be
# provided.
diff --git a/config/kit-voron2-2018.cfg b/config/kit-voron2-2018.cfg
deleted file mode 100644
index b48890b7..00000000
--- a/config/kit-voron2-2018.cfg
+++ /dev/null
@@ -1,222 +0,0 @@
-# This file is an example configuration for the Voron 2 CoreXY printer
-# running on two RAMPS boards. This file was created by "Maglin".
-# X/Y/E steppers/endstops/thermisters/heaters are on one MCU/RAMPS
-# board while Z steppers/mechanical switch/endstop_pin are on the
-# second MCU/RAMPS labeled z.
-
-# This file is only an example - be sure to review and update it
-# according to the specifics of your printer. See the example.cfg and
-# example-extras.cfg files for a description of available parameters.
-
-[mcu]
-# mcu for X/Y/E steppers main MCU
-serial: /dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0-port0
-pin_map: arduino
-
-[mcu z]
-# mcu for the Z steppers
-serial: /dev/serial/by-path/platform-3f980000.usb-usb-0:1.2:1.0-port0
-pin_map: arduino
-
-[stepper_x]
-# use preceding ! to invert logic and ^ to activate internal 5V pullup
-# this is for all pin definitions. Not all pins have interal pullups
-step_pin: ar54
-dir_pin: ar55
-enable_pin: !ar38
-step_distance: 0.0125
-endstop_pin: ^ar2
-position_min: 0
-position_endstop: 248
-position_max: 248
-homing_speed: 50
-
-[stepper_y]
-step_pin: ar60
-dir_pin: ar61
-enable_pin: !ar56
-step_distance: 0.0125
-endstop_pin: ^ar15
-position_min: -5
-position_endstop: 245
-position_max: 245
-homing_speed: 50
-
-[stepper_z]
-# X stepper pins on MCU Z
-step_pin: z:ar54
-dir_pin: z:ar55
-enable_pin: !z:ar38
-step_distance: 0.00625
-# probe:z_virtual_endstop is a virtual pin definition only available if
-# a probe section is defined
-#endstop_pin: probe:z_virtual_endstop
-# mechanical switch on mcu Z X min endstop pin
-endstop_pin: ^z:ar3
-position_endstop: -0.376
-position_min: -5
-position_max: 245
-homing_speed: 12
-
-[stepper_z1]
-# Y stepper pins on MCU Z
-step_pin: z:ar60
-dir_pin: !z:ar61
-enable_pin: !z:ar56
-step_distance: 0.00625
-
-[stepper_z2]
-# Z stepper pins on MCU Z
-step_pin: z:ar46
-dir_pin: z:ar48
-enable_pin: !z:ar62
-step_distance: 0.00625
-
-[stepper_z3]
-# E0 stepper pins on MCU Z
-step_pin: z:ar26
-dir_pin: !z:ar28
-enable_pin: !z:ar24
-step_distance: 0.00625
-
-# extended G-Code command Z_TILT_ADJUST can be used to level gantry
-[z_tilt]
-# belt locations from origin 0,0
-z_positions:
- -56,-17
- -56,322
- 311,322
- 311,-17
-# probing locations for gantry leveling
-points:
- 50,50
- 50,195
- 195,195
- 195,50
-# travel speed between probe points
-speed: 150
-# Move Z to this position for safe probing
-horizontal_move_z: 15
-
-# this is required for gantry leveling and replaces your G28 command
-# with the gcode used here. Used to home X/Y/Z with mechanical switches
-[homing_override]
-set_position_z: 0
-gcode:
- G90
- G0 Z15 F600
- G28 X0 Y0
- G0 X248 Y225 F3000
- G28 Z
- G0 Z15 F6000
-
-# macro to level the gantry. use G32 in the terminal to call
-[gcode_macro g32]
-gcode:
- Z_TILT_ADJUST
- Z_TILT_ADJUST
- Z_TILT_ADJUST
- G28
- G0 X125 Y125 Z125 F3600
-
-# Use print_start for you slicer starting script
-[gcode_macro print_start]
-gcode:
- G1 X0 Y15 Z0.3 F7000
- G92 E0
- G1 E14 F600
- G92 E0
- G1 X60.0 E9.0 F1000.0
- G1 X100.0 E12.5 F1000.0
- G1 E12 F1000.0
- G92 E-0.5
-
-# Use print_end for you slicer ending script
-[gcode_macro print_end]
-gcode:
- M104 S0
- M140 S0
- M107
- G92 E0
- G91
- G1 Z10 E-10 F3000
- G90
- G0 X125 Y245 F1000
-
-[extruder]
-# on E0 stepper pins of main MCU
-step_pin: ar26
-dir_pin: ar28
-enable_pin: !ar24
-step_distance: 0.003339
-nozzle_diameter: 0.400
-filament_diameter: 1.750
-max_extrude_only_distance: 100.0
-heater_pin: ar10
-max_power: 1.0
-sensor_type: ATC Semitec 104GT-2
-sensor_pin: analog13
-control: pid
-pid_Kp: 21.759
-pid_Ki: 1.107
-pid_Kd: 106.889
-min_temp: 0
-max_temp: 300
-
-# thermally controlled hotend fan
-[heater_fan my_nozzle_fan]
-# Located on Z MCU on fan D9
-pin: z:ar9
-
-[probe]
-# Z_Min pins on MCU Z (must be on same MCU as steppers)
-pin: ^!z:ar18
-z_offset: 1.15
-speed: 2.0
-
-[heater_bed]
-heater_pin: ar8
-# NTC 100K MGB18-104F39050L32 is for Kenovo thermistors
-sensor_type: NTC 100K MGB18-104F39050L32
-sensor_pin: analog14
-# pid gives you better control over bed heat
-control: pid
-pid_Kp: 63.832
-pid_Ki: 3.404
-pid_Kd: 299.213
-min_temp: 0
-max_temp: 130
-
-# print cooling fan
-[fan]
-# On z MCU on extruder heater pin D10
-pin: z:ar10
-
-# "RepRapDiscount 2004 Smart Controller" type displays
-#[display]
-#lcd_type: hd44780
-#rs_pin: ar16
-#e_pin: ar17
-#d4_pin: ar23
-#d5_pin: ar25
-#d6_pin: ar27
-#d7_pin: ar29
-
-# "RepRapDiscount 128x64 Full Graphic Smart Controller" type displays
-[display]
-lcd_type: st7920
-cs_pin: ar16
-sclk_pin: ar23
-sid_pin: ar17
-
-[printer]
-# settings below are the max and can't be commanded over in gcode
-kinematics: corexy
-max_velocity: 500
-max_accel: 3000
-max_z_velocity: 100
-max_z_accel: 50
-
-[idle_timeout]
-# high motor off time so I don't have to relevel gantry often
-timeout: 6000
diff --git a/config/kit-voron2-250mm.cfg b/config/kit-voron2-250mm.cfg
new file mode 100644
index 00000000..73257af6
--- /dev/null
+++ b/config/kit-voron2-250mm.cfg
@@ -0,0 +1,299 @@
+# VORON2 250mm config
+
+# This is a base printer.cfg file for the VORON2 printer and matches the manual/build guide exactly
+# for controllers used (dual RAMPS) and pin layout for all connected components.
+# Created by "Boff" with help from the VORON community.
+
+# For other build sizes, controllers, or non-standard pin connections, please see
+# https://github.com/mzbotreprap/VORON/tree/master/Firmware/Klipper/Voron_2.1/Klipper/Configurations
+# for other example Klipper configs created by the VORON community.
+
+# This file is only an example - be sure to review and update it
+# according to the specifics of your printer. See the example.cfg and
+# example-extras.cfg files for a description of available Klipper parameters.
+
+# AND PLEASE READ THROUGH THE KLIPPER DOCUMENTATION FIRST!
+# https://github.com/KevinOConnor/klipper/tree/master/docs
+
+# *** THINGS TO CHANGE/CHECK: ***
+# Arduino paths [mcu] section
+# Thermistor types [extruder] and [heater_bed] sections - See 'sensor types' list at end of file
+# FSR switch (z endstop) location [homing_override] section
+# FSR switch (z endstop) offset for Z0 [stepper_z] section
+# Probe points [quad_gantry_level] section
+# Min & Max gantry corner postions [quad_gantry_level] section
+# PID tune [extruder] and [heater_bed] sections
+# Fine tune E steps [extruder] section
+
+[mcu]
+# Mcu for X/Y/E steppers
+serial: /dev/serial/by-id/**INSERT_YOUR_ARDUINO_DEFINITION_HERE**
+# Obtain definition by "ls -l /dev/serial/by-id/"
+pin_map: arduino
+restart_method: arduino
+
+[mcu z]
+# Mcu for Z steppers
+serial: /dev/serial/by-id/**INSERT_YOUR_ARDUINO_DEFINITION_HERE**
+# Obtain definition by "ls -l /dev/serial/by-id/"
+pin_map: arduino
+restart_method: arduino
+
+[printer]
+kinematics: corexy
+max_velocity: 350
+max_accel: 3000
+max_z_velocity: 50
+max_z_accel: 350
+
+[stepper_x]
+# B Stepper
+step_pin: ar54
+dir_pin: !ar55
+enable_pin: !ar38
+# X on mcu_xye
+step_distance: 0.0125
+# 80 steps per mm - 1.8 deg - 1/16 microstepping
+endstop_pin: ^ar2
+# X_MAX on mcu_xye
+position_min: 0
+position_endstop: 250
+position_max: 250
+homing_speed: 100
+homing_retract_dist: 5
+
+[stepper_y]
+# A Stepper
+step_pin: ar60
+dir_pin: !ar61
+enable_pin: !ar56
+# Y on mcu_xye
+step_distance: 0.0125
+# 80 steps per mm - 1.8 deg - 1/16 microstepping
+endstop_pin: ^ar15
+# Y_MAX on mcu_xye
+position_min: 0
+position_endstop: 250
+position_max: 250
+homing_speed: 100
+homing_retract_dist: 5
+
+[stepper_z]
+# Z0 Stepper - Front Left
+step_pin: z:ar54
+dir_pin: !z:ar55
+enable_pin: !z:ar38
+# X on mcu_z
+step_distance: 0.00250
+# 400 steps per mm - 1.8 deg - 1/16 microstepping
+endstop_pin: ^!z:ar18
+# Z_MIN on mcu_z
+position_endstop: -0.2
+# Offset (in mm) for nozzle to bed off z switch
+position_max: 250
+position_min: -2
+# Set to -2 to allow room for squaring gantry with quad_gantry_level
+homing_speed: 15.0
+second_homing_speed: 3.0
+homing_retract_dist: 3.0
+
+[stepper_z1]
+# Z1 Stepper - Rear Left
+step_pin: z:ar60
+dir_pin: z:ar61
+enable_pin: !z:ar56
+# Y on mcu_z
+step_distance: 0.00250
+# 400 steps per mm - 1.8 deg - 1/16 microstepping
+
+[stepper_z2]
+# Z2 Stepper - Rear Right
+step_pin: z:ar46
+dir_pin: !z:ar48
+enable_pin: !z:ar62
+# Z on mcu_z
+step_distance: 0.00250
+# 400 steps per mm - 1.8 deg - 1/16 microstepping
+
+[stepper_z3]
+# Z3 Stepper - Front Right
+step_pin: z:ar26
+dir_pin: z:ar28
+enable_pin: !z:ar24
+# E0 on mcu_z
+step_distance: 0.00250
+# 400 steps per mm - 1.8 deg - 1/16 microstepping
+
+[extruder]
+step_pin: ar26
+dir_pin: ar28
+enable_pin: !ar24
+# E0 on mcu_xye
+step_distance: 0.00180180
+# 555 steps per mm - 1.8 deg - 1/16 microstepping (Mobius2)
+nozzle_diameter: 0.400
+filament_diameter: 1.750
+max_extrude_only_distance: 780.0
+# This is set high to allow the load/unload filament macros to run
+heater_pin: ar10
+# D10 on mcu_xye
+max_power: 1.0
+sensor_type: NTC 100K beta 3950
+sensor_pin: analog13
+# T0 on mcu_xye
+smooth_time: 3.0
+control: pid
+pid_Kp: 16.430
+pid_Ki: 0.755
+pid_Kd: 89.337
+min_extrude_temp: 170
+min_temp: 0
+max_temp: 270
+
+[probe]
+# Inductive Probe
+pin: ^z:ar19
+# Z_MAX on mcu_z
+x_offset: 0.0
+y_offset: 25.0
+z_offset: 0.00
+speed: 2.0
+
+[fan]
+# Print cooling fan
+pin: ar9
+# D9 on mcu_xye
+kick_start_time: 0.500
+
+[heater_fan hotend_fan]
+# Hotend fan
+pin: z:ar9
+# D9 on mcu_z
+kick_start_time: 0.500
+heater: extruder
+heater_temp: 50.0
+
+[heater_fan controller_fan]
+# Controller fan
+pin: z:ar10
+# D10 on mcu_z
+kick_start_time: 0.500
+heater: heater_bed
+heater_temp: 45.0
+
+[heater_fan exhaust_fan]
+# Exhaust fan
+pin: z:ar8
+# D8 on mcu_z
+kick_start_time: 0.500
+heater: heater_bed
+heater_temp: 60.0
+
+[heater_bed]
+heater_pin: z:ar11
+# D11 (servo) on mcu_z
+sensor_type: NTC 100K MGB18-104F39050L32
+sensor_pin: z:analog15
+# T2 on mcu_z
+smooth_time: 3.0
+max_power: 0.75
+control: pid
+pid_Kp: 47.690
+pid_Ki: 1.556
+pid_Kd: 365.338
+min_temp: 0
+max_temp: 110
+
+[homing_override]
+axes: z
+set_position_z: 0
+gcode:
+ G90
+ G0 Z5 F600
+ G28 X Y
+ G0 X179 Y249.5 F3600
+# XY Location of the FSR Switch
+ G28 Z
+ G0 Z10 F1800
+ G0 X125 Y125 Z20 F3600
+
+[quad_gantry_level]
+# Use QUAD_GANTRY_LEVEL to level a gantry.
+gantry_corners:
+ -55,-7
+ 305, 320
+# Min & Max gantry corners - measure from nozzle at MIN (0,0) and MAX (250,250) to respective belt positions
+points:
+ 25,0
+ 25,200
+ 225,200
+ 225,0
+# Probe points
+speed: 200
+horizontal_move_z: 6
+samples: 4
+# Number of times to probe a point
+sample_retract_dist: 6.0
+# How far to retract (in mm) from the probe point for multi-probe samples
+
+[display]
+# RepRapDiscount 128x64 Full Graphic Smart Controller
+lcd_type: st7920
+cs_pin: z:ar16
+sclk_pin: z:ar23
+sid_pin: z:ar17
+# LCD connector on mcu_z
+menu_timeout: 40
+encoder_pins: ^z:ar33, ^z:ar31
+click_pin: ^!z:ar35
+kill_pin: ^!z:ar41
+
+
+### Macros ###
+
+[gcode_macro G32]
+gcode:
+ G28
+ QUAD_GANTRY_LEVEL
+ QUAD_GANTRY_LEVEL
+ G0 X125 Y125 Z20 F6000
+
+[gcode_macro PRINT_START]
+# Use PRINT_START for the slicer starting script - PLEASE CUSTOMISE THE SCRIPT FOR YOUR SLICER OF CHOICE
+gcode:
+ M117 Homing... ; display message
+ G28 ; home all axes
+ G1 Z20 F3000 ; move nozzle away from bed
+ M117 Preheat (Print) ; display message
+ M104 S0 ; turn off hotend while waiting for bed to get to temp
+
+[gcode_macro PRINT_END]
+# Use PRINT_END for the slicer ending script - PLEASE CUSTOMISE THE SCRIPT FOR YOUR SLICER OF CHOICE
+gcode:
+ M400 ; wait for buffer to clear
+ G92 E0 ; zero the extruder
+ G1 E-4.0 F3600 ; retract
+ G91 ; relative positioning
+ G0 Z1.00 X20.0 Y20.0 F20000 ; move nozzle to remove stringing
+ M104 S0 ; turn off hotend
+ M140 S0 ; turn off bed
+ M106 S0 ; turn off fan
+ G1 Z20 F3000 ; move nozzle up 20mm
+ G90 ; absolute positioning
+ G0 X125 Y245 F3600 ; park nozzle at rear
+ M117 Finished! ; display message
+
+[gcode_macro UNLOAD_FILAMENT]
+gcode:
+ M83
+ G1 E10 F300
+ G1 E-780 F1800
+ M82
+
+[gcode_macro LOAD_FILAMENT]
+gcode:
+ M83
+ G1 E750 F1800
+ G1 E30 F300
+ G1 E15 F150
+ M82
diff --git a/docs/Bed_Level.md b/docs/Bed_Level.md
new file mode 100644
index 00000000..4fd28f23
--- /dev/null
+++ b/docs/Bed_Level.md
@@ -0,0 +1,211 @@
+Bed leveling (sometimes also referred to as "bed tramming") is
+critical to getting high quality prints. If a bed is not properly
+"leveled" it can lead to poor bed adhesion, "warping", and subtle
+problems throughout the print. This document serves as a guide to
+performing bed leveling in Klipper.
+
+It's important to understand the goal of bed leveling. If the printer
+is commanded to a position `X0 Y0 Z10` during a print, then the goal
+is for the printer's nozzle to be exactly 10mm from the printer's
+bed. Further, should the printer then be commanded to a position of
+`X50 Z10` the goal is for the nozzle to maintain an exact distance of
+10mm from the bed during that entire horizontal move.
+
+In order to get good quality prints the printer should be calibrated
+so that Z distances are accurate to within about 25 microns (.025mm).
+This is a small distance - significantly smaller than the width of a
+typical human hair. This scale can not be measured "by eye". Subtle
+effects (such as heat expansion) impact measurements at this scale.
+The secret to getting high accuracy is to use a repeatable process and
+to use a leveling method that leverages the high accuracy of the
+printer's own motion system.
+
+# Choose the appropriate calibration mechanism
+
+Different types of printers use different methods for performing bed
+leveling. All of them ultimately depend on the "paper test" (described
+below). However, the actual process for a particular type of printer
+is described in other documents.
+
+Prior to running any of these calibration tools, be sure to run the
+checks described in the [config check document](Config_checks.md). It
+is necessary to verify basic printer motion before performing bed
+leveling.
+
+For printers with an "automatic Z probe" be sure to calibrate the
+probe following the directions in the
+[Probe Calibrate](Probe_Calibrate.md) document. For delta printers,
+see the [Delta Calibrate](Delta_Calibrate.md) document.
+
+During calibration it may be necessary to set the printer's Z
+`position_min` to a negative number (eg, `position_min = -2`). The
+printer enforces boundary checks even during calibration
+routines. Setting a negative number allows the printer to move below
+the nominal position of the bed, which may help when trying to
+determine the actual bed position.
+
+# The "paper test"
+
+The primary bed calibration mechanism is the "paper test". It involves
+placing a regular piece of "copy machine paper" between the printer's
+bed and nozzle, and then commanding the nozzle to different Z heights
+until one feels a small amount of friction when pushing the paper back
+and forth.
+
+It is important to understand the "paper test" even if one has an
+"automatic Z probe". The probe itself often needs to be calibrated to
+get good results. That probe calibration is done using this "paper
+test".
+
+In order to perform the paper test, cut a small rectangular piece of
+paper using a pair of scissors (eg, 5x3 cm). The paper generally has a
+width of around 100 microns (0.100mm). (The exact width of the paper
+isn't crucial.)
+
+The first step of the paper test is to inspect the printer's nozzle
+and bed. Make sure there is no plastic (or other debris) on the nozzle
+or bed.
+
+**Inspect the nozzle and bed to ensure no plastic is present!**
+
+If one always prints on a particular tape or printing surface then one
+may perform the paper test with that tape/surface in place. However,
+note that tape itself has a width and different tapes (or any other
+printing surface) will impact Z measurements. Be sure to rerun the
+paper test to measure each type of surface that is in use.
+
+If there is plastic on the nozzle then heat up the extruder and use a
+metal tweezers to remove that plastic. Wait for the extruder to fully
+cool to room temperature before continuing with the paper test. While
+the nozzle is cooling, use the metal tweezers to remove any plastic
+that may ooze out.
+
+**Always perform the paper test when both nozzle and bed are at room
+temperature!**
+
+When the nozzle is heated, its position (relative to the bed) changes
+due to thermal expansion. This thermal expansion is typically around a
+100 microns, which is about the same width as a typical piece of
+printer paper. The exact amount of thermal expansion isn't crucial,
+just as the exact width of the paper isn't crucial. Start with the
+assumption that the two are equal (see below for a method of
+determining the difference between the two widths).
+
+It may seem odd to calibrate the distance at room temperature when the
+goal is to have a consistent distance when heated. However, if one
+calibrates when the nozzle is heated, it tends to impart small amounts
+of molten plastic on to the paper, which changes the amount of
+friction felt. That makes it harder to get a good calibration.
+Calibrating while the bed/nozzle is hot also greatly increases the
+risk of burning oneself. The amount of thermal expansion is stable, so
+it is easily accounted for later in the calibration process.
+
+**Use an automated tool to determine precise Z heights!**
+
+Klipper has several helper scripts available (eg, MANUAL_PROBE,
+Z_ENDSTOP_CALIBRATE, PROBE_CALIBRATE, DELTA_CALIBRATE). Choose one of
+them and follow the directions in the documents described above.
+
+Run the appropriate command in the OctoPrint terminal window. The
+script will prompt for user interaction in the OctoPrint terminal
+output. It will look something like:
+```
+Recv: // Starting manual Z probe. Use TESTZ to adjust position.
+Recv: // Finish with ACCEPT or ABORT command.
+Recv: // Z position: ?????? --> 5.000 <-- ??????
+```
+
+The current height of the nozzle (as the printer currently understands
+it) is shown between the "--> <--". The number to the right is the
+height of the last probe attempt just greater than the current height,
+and to the left is the last probe attempt less than the current height
+(or ?????? if no attempt has been made).
+
+Place the paper between the nozzle and bed. It can be useful to fold a
+corner of the paper so that it is easier to grab. (Try not to push
+down on the bed when moving the paper back and forth.)
+
+![paper-test](img/paper-test.jpg)
+
+Use the TESTZ command to request the nozzle to move closer to the
+paper. For example:
+```
+TESTZ Z=-.1
+```
+
+The TESTZ command will move the nozzle a relative distance from the
+nozzle's current position. (So, `Z=-.1` requests the nozzle to move
+closer to the bed by .1mm.) After the nozzle stops moving, push the
+paper back and forth to check if the nozzle is in contact with the
+paper and to feel the amount of friction. Continue issuing TESTZ
+commands until one feels a small amount of friction when testing with
+the paper.
+
+If too much friction is found then one can use a positive Z value to
+move the nozzle up. It is also possible to use `TESTZ Z=+` or `TESTZ
+Z=-` to "bisect" the last position - that is to move to a position
+half way between two positions. For example, if one received the
+following prompt from a TESTZ command:
+```
+Recv: // Z position: 0.130 --> 0.230 <-- 0.280
+```
+Then a `TESTZ Z=-` would move the nozzle to a Z position of 0.180
+(half way between 0.130 and 0.230). One can use this feature to help
+rapidly narrow down to a consistent friction. It is also possible to
+use `Z=++` and `Z=--` to return directly to a past measurement - for
+example, after the above prompt a `TESTZ Z=--` command would move the
+nozzle to a Z position of 0.130.
+
+After finding a small amount of friction run the ACCEPT command:
+```
+ACCEPT
+```
+This will accept the given Z height and proceed with the given
+calibration tool.
+
+The exact amount of friction felt isn't crucial, just as the amount of
+thermal expansion and exact width of the paper isn't crucial. Just try
+to obtain the same amount of friction each time one runs the test.
+
+If something goes wrong during the test, one can use the `ABORT`
+command to exit the calibration tool.
+
+# Determining Thermal Expansion
+
+After successfully performing bed leveling, one may go on to calculate
+a more precise value for the combined impact of "thermal expansion",
+"width of the paper", and "amount of friction felt during the paper
+test".
+
+This type of calculation is generally not needed as most users find
+the simple "paper test" provides good results.
+
+The easiest way to make this calculation is to print a test object
+that has straight walls on all sides. The large hollow square found in
+[docs/prints/square.stl](prints/square.stl) can be used for this.
+When slicing the object, make sure the slicer uses the same layer
+height and extrusion widths for the first level that it does for all
+subsequent layers. Use a coarse layer height (the layer height should
+be around 75% of the nozzle diameter) and do not use a brim or raft.
+
+Print the test object, wait for it to cool, and remove it from the
+bed. Inspect the lowest layer of the object. (It may also be useful to
+run a finger or nail along the bottom edge.) If one finds the bottom
+layer bulges out slightly along all sides of the object then it
+indicates the nozzle was slightly closer to the bed then it should
+be. One can issue a `SET_GCODE_OFFSET Z=+.010` command to increase the
+height. In subsequent prints one can inspect for this behavior and
+make further adjustment as needed. Adjustments of this type are
+typically in 10s of microns (.010mm).
+
+If the bottom layer consistently appears narrower than subsequent
+layers then one can use the SET_GCODE_OFFSET command to make a
+negative Z adjustment. If one is unsure, then one can decrease the Z
+adjustment until the bottom layer of prints exhibit a small bulge, and
+then back-off until it disappears.
+
+The easiest way to apply the desired Z adjustment is to create a
+START_PRINT g-code macro, arrange for the slicer to call that macro
+during the start of each print, and add a SET_GCODE_OFFSET command to
+that macro. See the [slicers](Slicers.md) document for further
+details.
diff --git a/docs/Config_checks.md b/docs/Config_checks.md
index 69c30462..fb7cd852 100644
--- a/docs/Config_checks.md
+++ b/docs/Config_checks.md
@@ -152,16 +152,13 @@ command is: `PID_CALIBRATE HEATER=heater_bed TARGET=60`
### Next steps
This guide is intended to help with basic verification of pin settings
-in the Klipper configuration file. It may be necessary to perform
-detailed printer calibration - a number of guides are available online
-to help with this (for example, do a web search for "3d printer
-calibration").
-
-See the [Slicers](Slicers.md) document for information on configuring
-a slicer with Klipper. If one is using traditional endstop switches
-with Trinamic stepper motor drivers then see the
-[Endstop Phase](Endstop_Phase.md) document. If using a delta printer,
-see the [Delta Calibrate](Delta_Calibrate.md) document.
+in the Klipper configuration file. Be sure to read the
+[bed leveling](Bed_Level.md) guide. Also see the [Slicers](Slicers.md)
+document for information on configuring a slicer with Klipper.
After one has verified that basic printing works, it is a good idea to
consider calibrating [pressure advance](Pressure_Advance.md).
+
+It may be necessary to perform other types of detailed printer
+calibration - a number of guides are available online to help with
+this (for example, do a web search for "3d printer calibration").
diff --git a/docs/Delta_Calibrate.md b/docs/Delta_Calibrate.md
index 528021a9..ecc287c9 100644
--- a/docs/Delta_Calibrate.md
+++ b/docs/Delta_Calibrate.md
@@ -7,7 +7,12 @@ printer motion on a delta printer. Each one of these parameters has a
non-obvious and non-linear impact and it is difficult to calibrate
them manually. In contrast, the software calibration code can provide
excellent results with just a few minutes of time. No special probing
-hardware is necessary to get good results.
+hardware is necessary.
+
+Ultimately, the delta calibration is dependent on the precision of the
+tower endstop switches. If one is using Trinamic stepper motor drivers
+then consider enabling [endstop phase](Endstop_Phase.md) detection to
+improve the accuracy of those switches.
Basic delta calibration
=======================
@@ -32,16 +37,18 @@ the bed. It is typical to permit this during calibration by updating
the config so that the printer's `minimum_z_position=-5`. (Once
calibration completes, one can remove this setting from the config.)
-There are two ways to perform the probing - manual probing and
-automatic probing. Automatic probing utilizes a hardware device
+There are two ways to perform the probing - manual probing
+(`DELTA_CALIBRATE METHOD=manual`) and automatic probing
+(`DELTA_CALIBRATE`). Automatic probing utilizes a hardware device
capable of triggering when the toolhead is at a set distance from the
-bed. Manual probing involves using the "paper test" to determine the
-height at each probe point. It is recommended to use manual probing
-for delta calibration. A number of common printer kits come with
-probes that are not sufficiently accurate (specifically, small
-differences in arm length can cause effector tilt which can skew an
-automatic probe). Manual probing only takes a few minutes and it
-eliminates error introduced by the probe.
+bed. The manual probing method will move the head near the bed and
+then wait for the user to follow the
+["paper test"](Bed_Level.md#the-paper-test) steps. It is recommended
+to use manual probing for delta calibration. A number of common
+printer kits come with probes that are not sufficiently accurate
+(specifically, small differences in arm length can cause effector tilt
+which can skew an automatic probe). Manual probing only takes a few
+minutes and it eliminates error introduced by the probe.
To perform the basic probe, make sure the config has a
[delta_calibrate] section defined and run:
@@ -56,7 +63,7 @@ SAVE_CONFIG
```
The basic calibration should provide delta parameters that are
-accurate enough for basic printing. If this is a new printer, this is
+accurate enough for basic printing. If this is a new printer, this is
a good time to print some basic objects and verify general
functionality.
diff --git a/docs/Features.md b/docs/Features.md
index 1e2eb46b..9249f612 100755
--- a/docs/Features.md
+++ b/docs/Features.md
@@ -94,8 +94,8 @@ Klipper supports many standard 3d printer features:
* Support for run-time configuration of TMC2130, TMC2208, TMC2224, and
TMC2660 stepper motor drivers. There is also support for current
- control of traditional stepper drivers via AD5206 and MCP4451
- digipots.
+ control of traditional stepper drivers via AD5206, MCP4451, MCP4728,
+ and PWM pins.
* Support for common LCD displays attached directly to the printer. A
default menu is also available.
diff --git a/docs/G-Codes.md b/docs/G-Codes.md
index f854a976..b9260b43 100644
--- a/docs/G-Codes.md
+++ b/docs/G-Codes.md
@@ -121,6 +121,23 @@ The following standard commands are supported:
- `STEPPER_BUZZ STEPPER=<config_name>`: Move the given stepper forward
one mm and then backward one mm, repeated 10 times. This is a
diagnostic tool to help verify stepper connectivity.
+- `MANUAL_PROBE [SPEED=<speed>]`: Run a helper script useful for
+ measuring the height of the nozzle at a given location. If SPEED is
+ specified, it sets the speed of TESTZ commands (the default is
+ 5mm/s). During a manual probe, the following additional commands are
+ available:
+ - `ACCEPT`: This command accepts the current Z position and
+ concludes the manual probing tool.
+ - `ABORT`: This command terminates the manual probing tool.
+ - `TESTZ Z=<value>`: This command moves the nozzle up or down by the
+ amount specified in "value". For example, `TESTZ Z=-.1` would move
+ the nozzle down .1mm while `TESTZ Z=.1` would move the nozzle up
+ .1mm. The value may also be `+`, `-`, `++`, or `--` to move the
+ nozzle up or down an amount relative to previous attempts.
+- `Z_ENDSTOP_CALIBRATE [SPEED=<speed>]`: Run a helper script useful
+ for calibrating a Z position_endstop config setting. See the
+ MANUAL_PROBE command for details on the parameters and the
+ additional commands available while the tool is active.
- `RESTART`: This will cause the host software to reload its config
and perform an internal reset. This command will not clear error
state from the micro-controller (see FIRMWARE_RESTART) nor will it
@@ -148,6 +165,21 @@ enabled:
- `SET_SERVO SERVO=config_name [WIDTH=<seconds>] [ENABLE=<0|1>]`
- `SET_SERVO SERVO=config_name [ANGLE=<degrees>] [ENABLE=<0|1>]`
+## Manual stepper Commands
+
+The following command is available when a "manual_stepper" config
+section is enabled:
+- `MANUAL_STEPPER STEPPER=config_name [ENABLE=[0|1]]
+ [SET_POSITION=<pos>]
+ [MOVE=<pos> SPEED=<speed> [STOP_ON_ENDSTOP=1]]`: This command will
+ alter the state of the stepper. Use the ENABLE parameter to
+ enable/disable the stepper. Use the SET_POSITION parameter to force
+ the stepper to think it is at the given position. Use the MOVE
+ parameter to request a movement to the given position at the given
+ SPEED. If STOP_ON_ENDSTOP is specified then the move will end early
+ should the endstop report as triggered (use STOP_ON_ENDSTOP=-1 to
+ stop early should the endstop report not triggered).
+
## Probe
The following commands are available when a "probe" config section is
@@ -155,6 +187,10 @@ enabled:
- `PROBE`: Move the nozzle downwards until the probe triggers.
- `QUERY_PROBE`: Report the current status of the probe ("triggered"
or "open").
+- `PROBE_CALIBRATE [SPEED=<speed>]`: Run a helper script useful for
+ calibrating the probe's z_offset. See the MANUAL_PROBE command for
+ details on the parameters and the additional commands available
+ while the tool is active.
## BLTouch
@@ -172,10 +208,10 @@ The following commands are available when the "delta_calibrate" config
section is enabled:
- `DELTA_CALIBRATE [METHOD=manual]`: This command will probe seven
points on the bed and recommend updated endstop positions, tower
- angles, and radius.
- - `NEXT`: If manual bed probing is enabled, then one can use this
- command to move to the next probing point during a DELTA_CALIBRATE
- operation.
+ angles, and radius. If METHOD=manual is specified then the manual
+ probing tool is activated - see the MANUAL_PROBE command above for
+ details on the additional commands available while this tool is
+ active.
- `DELTA_ANALYZE`: This command is used during enhanced delta
calibration. See [Delta Calibrate](Delta_Calibrate.md) for details.
@@ -185,10 +221,10 @@ The following commands are available when the "bed_tilt" config
section is enabled:
- `BED_TILT_CALIBRATE [METHOD=manual]`: This command will probe the
points specified in the config and then recommend updated x and y
- tilt adjustments.
- - `NEXT`: If manual bed probing is enabled, then one can use this
- command to move to the next probing point during a
- BED_TILT_CALIBRATE operation.
+ tilt adjustments. If METHOD=manual is specified then the manual
+ probing tool is activated - see the MANUAL_PROBE command above for
+ details on the additional commands available while this tool is
+ active.
## Mesh Bed Leveling
@@ -197,10 +233,10 @@ section is enabled:
- `BED_MESH_CALIBRATE [METHOD=manual]`: This command probes the bed
using generated points specified by the parameters in the
config. After probing, a mesh is generated and z-movement is
- adjusted according to the mesh.
- - `NEXT`: If manual bed probing is enabled, then one can use this
- command to move to the next probing point during a
- BED_MESH_CALIBRATE operation.
+ adjusted according to the mesh. If METHOD=manual is specified then
+ the manual probing tool is activated - see the MANUAL_PROBE command
+ above for details on the additional commands available while this
+ tool is active.
- `BED_MESH_OUTPUT`: This command outputs the current probed z values
and current mesh values to the terminal.
- `BED_MESH_MAP`: This command probes the bed in a similar fashion
diff --git a/docs/Overview.md b/docs/Overview.md
index ce69e513..30b3c768 100644
--- a/docs/Overview.md
+++ b/docs/Overview.md
@@ -14,6 +14,9 @@ as a reference for the config file. See the [Slicers](Slicers.md)
document for information on configuring a slicer with Klipper. See the
[Endstop Phase](Endstop_Phase.md) document for information on
Klipper's "stepper phase adjusted endstop" system. See the
+[Bed Level](Bed_Level.md) document for information on bed leveling
+with Klipper. See the [Probe Calibrate](Probe_Calibrate.md) document
+for information on calibrating automatic Z probes. See the
[Delta Calibrate](Delta_Calibrate.md) document for information on
calibrating delta printers. The
[Pressure Advance](Pressure_Advance.md) document contains information
diff --git a/docs/Probe_Calibrate.md b/docs/Probe_Calibrate.md
new file mode 100644
index 00000000..de517504
--- /dev/null
+++ b/docs/Probe_Calibrate.md
@@ -0,0 +1,66 @@
+This document describes the method for calibrating the x, y, and z
+offsets of an "automatic z probe" in Klipper. This is useful for users
+that have a `[probe]` or `[bltouch]` section in their config file.
+
+# Calibrating probe X and Y offsets
+
+To calibrate the X and Y offset, navigate to the OctoPrint "Control"
+tab, home the printer, and then use the OctoPrint jogging buttons to
+move the head to a position near the center of the bed.
+
+Place a piece of blue painters tape (or similar) on the bed underneath
+the probe. Navigate to the OctoPrint "Terminal" tab and issue a PROBE
+command:
+```
+PROBE
+```
+Place a mark on the tape directly under where the probe is (or use a
+similar method to note the location on the bed).
+
+Issue a `GET_POSITION` command and record the toolhead XY location
+reported by that command. For example if one sees:
+```
+Recv: // toolhead: X:46.500000 Y:27.000000 Z:15.000000 E:0.000000
+```
+then one would record a probe X position of 46.5 and probe Y position
+of 27.
+
+After recording the probe position, issue a series of G1 commands
+until the nozzle is directly above the mark on the bed. For example,
+one might issue:
+```
+G1 F300 X57 Y30 Z15
+```
+to move the nozzle to an X position of 57 and Y of 30. Once one finds
+the position directly above the mark, use the `GET_POSITION` command
+to report that position. This is the nozzle position.
+
+The x_offset is then the `nozzle_x_position - probe_x_position` and
+y_offset is similarly the `nozzle_y_position - probe_y_position`.
+Update the printer.cfg file with the given values, remove the
+tape/marks from the bed, and then issue a `RESTART` command so that
+the new values take effect.
+
+# Calibrating probe Z offset
+
+Providing an accurate probe Z offset is critical to obtaining high
+quality prints.
+
+Start by homing the printer and then move the head to a position near
+the center of the bed. Navigate to the OctoPrint terminal tab and run
+the `PROBE_CALIBRATE` command to start Klipper's probe calibration
+tool.
+
+This tool will perform an automatic probe, then lift the head, move
+the nozzle over the location of the probe point, and start the manual
+probe tool. Once the manual probe tool starts, perform the
+["paper test"](Bed_Level.md#the-paper-test) to determine the actual
+nozzle height at the given point.
+
+If the nozzle does not move to a position above the probe point, then
+`ABORT` the manual probe tool and perform the XY probe offset
+calibration described above.
+
+After completing the "paper test", use the `ACCEPT` command to
+accept the current Z height and then use the `SAVE_CONFIG` command to
+save the given probe z_offset to the config file.
diff --git a/docs/img/paper-test.jpg b/docs/img/paper-test.jpg
new file mode 100644
index 00000000..3fabbad2
--- /dev/null
+++ b/docs/img/paper-test.jpg
Binary files differ
diff --git a/klippy/extras/bus.py b/klippy/extras/bus.py
index 2808d6fe..7c7a4454 100644
--- a/klippy/extras/bus.py
+++ b/klippy/extras/bus.py
@@ -12,22 +12,33 @@ import mcu
# Helper code for working with devices connected to an MCU via an SPI bus
class MCU_SPI:
- def __init__(self, mcu, bus, pin, mode, speed, shutdown_seq):
+ def __init__(self, mcu, bus, pin, mode, speed, shutdown_seq, sw_pins=None):
self.mcu = mcu
shutdown_msg = "".join(["%02x" % (x,) for x in shutdown_seq])
self.oid = self.mcu.create_oid()
- if pin is None:
- self.config_msg = (
+ if pin is not None:
+ # Set all CS pins high before first config_spi
+ self.mcu.add_config_cmd("set_digital_out pin=%s value=1" % (pin,))
+ if sw_pins is not None:
+ software_spi_oid = self.mcu.create_oid()
+ self.config_msgs = [
+ "config_software_spi oid=%d sclk_pin=%s mosi_pin=%s miso_pin=%s"
+ " mode=%d rate=%d" % (
+ software_spi_oid, sw_pins[0], sw_pins[1], sw_pins[2],
+ mode, speed),
+ "config_spi_from_software oid=%d sw_oid=%d pin=%s"
+ " shutdown_msg=%s" % (
+ self.oid, software_spi_oid, pin, shutdown_msg)]
+ elif pin is None:
+ self.config_msgs = [
"config_spi_without_cs oid=%d bus=%d mode=%d rate=%d"
" shutdown_msg=%s" % (
- self.oid, bus, mode, speed, shutdown_msg))
+ self.oid, bus, mode, speed, shutdown_msg)]
else:
- # Set all CS pins high before first config_spi
- self.mcu.add_config_cmd("set_digital_out pin=%s value=1" % (pin,))
- self.config_msg = (
+ self.config_msgs = [
"config_spi oid=%d bus=%d pin=%s mode=%d rate=%d"
" shutdown_msg=%s" % (
- self.oid, bus, pin, mode, speed, shutdown_msg))
+ self.oid, bus, pin, mode, speed, shutdown_msg)]
self.cmd_queue = self.mcu.alloc_command_queue()
self.mcu.register_config_callback(self.build_config)
self.spi_send_cmd = self.spi_transfer_cmd = None
@@ -38,7 +49,8 @@ class MCU_SPI:
def get_command_queue(self):
return self.cmd_queue
def build_config(self):
- self.mcu.add_config_cmd(self.config_msg)
+ for msg in self.config_msgs:
+ self.mcu.add_config_cmd(msg)
self.spi_send_cmd = self.mcu.lookup_command(
"spi_send oid=%c data=%*s", cq=self.cmd_queue)
self.spi_transfer_cmd = self.mcu.lookup_command(
@@ -68,11 +80,24 @@ def MCU_SPI_from_config(config, mode, pin_option="cs_pin",
ppins.reset_pin_sharing(cs_pin_params)
pin = None
# Load bus parameters
+ mcu = cs_pin_params['chip']
speed = config.getint('spi_speed', default_speed, minval=100000)
- bus = config.getint('spi_bus', 0, minval=0)
+ if config.get('spi_software_sclk_pin', None) is not None:
+ sw_pin_names = ['spi_software_%s_pin' % (name,)
+ for name in ['sclk', 'mosi', 'miso']]
+ sw_pin_params = [ppins.lookup_pin(config.get(name), share_type=name)
+ for name in sw_pin_names]
+ for pin_params in sw_pin_params:
+ if pin_params['chip'] != mcu:
+ raise ppins.error("%s: spi pins must be on same mcu" % (
+ config.get_name(),))
+ sw_pins = tuple([pin_params['pin'] for pin_params in sw_pin_params])
+ bus = None
+ else:
+ bus = config.getint('spi_bus', 0, minval=0)
+ sw_pins = None
# Create MCU_SPI object
- mcu = cs_pin_params['chip']
- return MCU_SPI(mcu, bus, pin, mode, speed, shutdown_seq)
+ return MCU_SPI(mcu, bus, pin, mode, speed, shutdown_seq, sw_pins)
######################################################################
diff --git a/klippy/extras/manual_probe.py b/klippy/extras/manual_probe.py
new file mode 100644
index 00000000..a1fc63cf
--- /dev/null
+++ b/klippy/extras/manual_probe.py
@@ -0,0 +1,163 @@
+# Helper script for manual z height probing
+#
+# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+import logging, bisect
+import homing
+
+class ManualProbe:
+ def __init__(self, config):
+ self.printer = config.get_printer()
+ # Register commands
+ self.gcode = self.printer.lookup_object('gcode')
+ self.gcode.register_command('MANUAL_PROBE', self.cmd_MANUAL_PROBE,
+ desc=self.cmd_MANUAL_PROBE_help)
+ self.z_position_endstop = None
+ if config.has_section('stepper_z'):
+ zconfig = config.getsection('stepper_z')
+ if zconfig.get_prefix_options('position_endstop'):
+ self.z_position_endstop = zconfig.getfloat('position_endstop')
+ self.gcode.register_command(
+ 'Z_ENDSTOP_CALIBRATE', self.cmd_Z_ENDSTOP_CALIBRATE,
+ desc=self.cmd_Z_ENDSTOP_CALIBRATE_help)
+ def manual_probe_finalize(self, kin_pos):
+ if kin_pos is not None:
+ self.gcode.respond_info("Z position is %.3f" % (kin_pos[2],))
+ cmd_MANUAL_PROBE_help = "Start manual probe helper script"
+ def cmd_MANUAL_PROBE(self, params):
+ ManualProbeHelper(self.printer, params, self.manual_probe_finalize)
+ def z_endstop_finalize(self, kin_pos):
+ if kin_pos is None:
+ return
+ z_pos = self.z_position_endstop - kin_pos[2]
+ self.gcode.respond_info(
+ "stepper_z: position_endstop: %.3f\n"
+ "The SAVE_CONFIG command will update the printer config file\n"
+ "with the above and restart the printer." % (z_pos,))
+ configfile = self.printer.lookup_object('configfile')
+ configfile.set('stepper_z', 'position_endstop', "%.3f" % (z_pos,))
+ cmd_Z_ENDSTOP_CALIBRATE_help = "Calibrate a Z endstop"
+ def cmd_Z_ENDSTOP_CALIBRATE(self, params):
+ ManualProbeHelper(self.printer, params, self.z_endstop_finalize)
+
+Z_BOB_MINIMUM = 0.500
+BISECT_MAX = 0.200
+
+# Helper script to determine a Z height
+class ManualProbeHelper:
+ def __init__(self, printer, params, finalize_callback):
+ self.printer = printer
+ self.finalize_callback = finalize_callback
+ self.gcode = self.printer.lookup_object('gcode')
+ self.toolhead = self.printer.lookup_object('toolhead')
+ self.speed = self.gcode.get_float("SPEED", params, 5.)
+ self.past_positions = []
+ self.last_toolhead_pos = self.last_kinematics_pos = None
+ # Register commands
+ try:
+ self.gcode.register_command('ACCEPT', self.cmd_ACCEPT,
+ desc=self.cmd_ACCEPT_help)
+ except self.gcode.error as e:
+ self.gcode.respond_error(
+ "Already in a manual Z probe. Use ABORT to abort it.")
+ self.finalize_callback(None)
+ return
+ self.gcode.register_command('NEXT', self.cmd_ACCEPT)
+ self.gcode.register_command('ABORT', self.cmd_ABORT,
+ desc=self.cmd_ABORT_help)
+ self.gcode.register_command('TESTZ', self.cmd_TESTZ,
+ desc=self.cmd_TESTZ_help)
+ self.gcode.respond_info(
+ "Starting manual Z probe. Use TESTZ to adjust position.\n"
+ "Finish with ACCEPT or ABORT command.")
+ self.report_z_status()
+ def get_kinematics_pos(self):
+ toolhead_pos = self.toolhead.get_position()
+ if toolhead_pos == self.last_toolhead_pos:
+ return self.last_kinematics_pos
+ self.toolhead.get_last_move_time()
+ kin_pos = self.toolhead.get_kinematics().calc_position()
+ self.last_toolhead_pos = toolhead_pos
+ self.last_kinematics_pos = kin_pos
+ return kin_pos
+ def move_z(self, z_pos):
+ curpos = self.toolhead.get_position()
+ try:
+ if curpos[2] - z_pos < Z_BOB_MINIMUM:
+ curpos[2] = z_pos + Z_BOB_MINIMUM
+ self.toolhead.move(curpos, self.speed)
+ curpos[2] = z_pos
+ self.toolhead.move(curpos, self.speed)
+ except homing.EndstopError as e:
+ self.finalize(False)
+ raise self.gcode.error(str(e))
+ def report_z_status(self, warn_no_change=False, prev_pos=None):
+ # Get position
+ kin_pos = self.get_kinematics_pos()
+ z_pos = kin_pos[2]
+ if warn_no_change and z_pos == prev_pos:
+ self.gcode.respond_info(
+ "WARNING: No change in position (reached stepper resolution)")
+ # Find recent positions that were tested
+ pp = self.past_positions
+ next_pos = bisect.bisect_left(pp, z_pos)
+ prev_pos = next_pos - 1
+ if next_pos < len(pp) and pp[next_pos] == z_pos:
+ next_pos += 1
+ prev_str = next_str = "??????"
+ if prev_pos >= 0:
+ prev_str = "%.3f" % (pp[prev_pos],)
+ if next_pos < len(pp):
+ next_str = "%.3f" % (pp[next_pos],)
+ # Find recent positions
+ self.gcode.respond_info("Z position: %s --> %.3f <-- %s" % (
+ prev_str, z_pos, next_str))
+ cmd_ACCEPT_help = "Accept the current Z position"
+ def cmd_ACCEPT(self, params):
+ self.finalize(True)
+ cmd_ABORT_help = "Abort manual Z probing tool"
+ def cmd_ABORT(self, params):
+ self.finalize(False)
+ cmd_TESTZ_help = "Move to new Z height"
+ def cmd_TESTZ(self, params):
+ # Store current position for later reference
+ kin_pos = self.get_kinematics_pos()
+ z_pos = kin_pos[2]
+ pp = self.past_positions
+ insert_pos = bisect.bisect_left(pp, z_pos)
+ if insert_pos >= len(pp) or pp[insert_pos] != z_pos:
+ pp.insert(insert_pos, z_pos)
+ # Determine next position to move to
+ req = self.gcode.get_str("Z", params)
+ if req in ('+', '++'):
+ check_z = 9999999999999.9
+ if insert_pos < len(self.past_positions) - 1:
+ check_z = self.past_positions[insert_pos + 1]
+ if req == '+':
+ check_z = (check_z + z_pos) / 2.
+ next_z_pos = min(check_z, z_pos + BISECT_MAX)
+ elif req in ('-', '--'):
+ check_z = -9999999999999.9
+ if insert_pos > 0:
+ check_z = self.past_positions[insert_pos - 1]
+ if req == '-':
+ check_z = (check_z + z_pos) / 2.
+ next_z_pos = max(check_z, z_pos - BISECT_MAX)
+ else:
+ next_z_pos = z_pos + self.gcode.get_float("Z", params)
+ # Move to given position and report it
+ self.move_z(next_z_pos)
+ self.report_z_status(next_z_pos != z_pos, z_pos)
+ def finalize(self, success):
+ self.gcode.register_command('ACCEPT', None)
+ self.gcode.register_command('NEXT', None)
+ self.gcode.register_command('ABORT', None)
+ self.gcode.register_command('TESTZ', None)
+ kin_pos = None
+ if success:
+ kin_pos = self.get_kinematics_pos()
+ self.finalize_callback(kin_pos)
+
+def load_config(config):
+ return ManualProbe(config)
diff --git a/klippy/extras/manual_stepper.py b/klippy/extras/manual_stepper.py
new file mode 100644
index 00000000..fdb9fc10
--- /dev/null
+++ b/klippy/extras/manual_stepper.py
@@ -0,0 +1,117 @@
+# Support for a manual controlled stepper
+#
+# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+#
+# This file may be distributed under the terms of the GNU GPLv3 license.
+import stepper, homing, chelper
+
+ENDSTOP_SAMPLE_TIME = .000015
+ENDSTOP_SAMPLE_COUNT = 4
+
+class ManualStepper:
+ def __init__(self, config):
+ self.printer = config.get_printer()
+ if config.get('endstop_pin', None) is not None:
+ self.can_home = True
+ self.stepper = stepper.PrinterRail(
+ config, need_position_minmax=False, default_position_endstop=0.)
+ else:
+ self.can_home = False
+ self.stepper = stepper.PrinterStepper(config)
+ self.next_cmd_time = 0.
+ # Setup iterative solver
+ ffi_main, ffi_lib = chelper.get_ffi()
+ self.cmove = ffi_main.gc(ffi_lib.move_alloc(), ffi_lib.free)
+ self.move_fill = ffi_lib.move_fill
+ self.stepper.setup_itersolve('cartesian_stepper_alloc', 'x')
+ self.stepper.set_max_jerk(9999999.9, 9999999.9)
+ # Register commands
+ stepper_name = config.get_name().split()[1]
+ self.gcode = self.printer.lookup_object('gcode')
+ self.gcode.register_mux_command('MANUAL_STEPPER', "STEPPER",
+ stepper_name, self.cmd_MANUAL_STEPPER,
+ desc=self.cmd_MANUAL_STEPPER_help)
+ self.printer.register_event_handler(
+ "toolhead:motor_off", self.handle_motor_off)
+ def sync_print_time(self):
+ toolhead = self.printer.lookup_object('toolhead')
+ print_time = toolhead.get_last_move_time()
+ if self.next_cmd_time > print_time:
+ toolhead.dwell(self.next_cmd_time - print_time)
+ else:
+ self.next_cmd_time = print_time
+ def do_enable(self, enable):
+ self.sync_print_time()
+ self.stepper.motor_enable(self.next_cmd_time, enable)
+ self.sync_print_time()
+ def do_set_position(self, setpos):
+ self.stepper.set_position([setpos, 0., 0.])
+ def do_move(self, movepos, speed):
+ self.sync_print_time()
+ cp = self.stepper.get_commanded_position()
+ dist = movepos - cp
+ move_t = abs(dist / speed)
+ self.move_fill(self.cmove, self.next_cmd_time, 0., move_t, 0.,
+ cp, 0., 0., dist, 0., 0., 0., speed, 0.)
+ self.stepper.step_itersolve(self.cmove)
+ self.next_cmd_time += move_t
+ self.sync_print_time()
+ def do_homing_move(self, movepos, speed, triggered):
+ if not self.can_home:
+ raise self.gcode.error("No endstop for this manual stepper")
+ # Notify endstops of upcoming home
+ endstops = self.stepper.get_endstops()
+ for mcu_endstop, name in endstops:
+ mcu_endstop.home_prepare()
+ # Start endstop checking
+ self.sync_print_time()
+ for mcu_endstop, name in endstops:
+ min_step_dist = min([s.get_step_dist()
+ for s in mcu_endstop.get_steppers()])
+ mcu_endstop.home_start(
+ self.next_cmd_time, ENDSTOP_SAMPLE_TIME, ENDSTOP_SAMPLE_COUNT,
+ min_step_dist / speed, triggered=triggered)
+ # Issue move
+ self.do_move(movepos, speed)
+ # Wait for endstops to trigger
+ error = None
+ for mcu_endstop, name in endstops:
+ try:
+ mcu_endstop.home_wait(self.next_cmd_time)
+ except mcu_endstop.TimeoutError as e:
+ if error is None:
+ error = "Failed to home %s: %s" % (name, str(e))
+ for mcu_endstop, name in endstops:
+ try:
+ mcu_endstop.home_finalize()
+ except homing.EndstopError as e:
+ if error is None:
+ error = str(e)
+ self.sync_print_time()
+ if error is not None:
+ raise self.gcode.error(error)
+ cmd_MANUAL_STEPPER_help = "Command a manually configured stepper"
+ def cmd_MANUAL_STEPPER(self, params):
+ if 'ENABLE' in params:
+ self.do_enable(self.gcode.get_int('ENABLE', params))
+ if 'SET_POSITION' in params:
+ setpos = self.gcode.get_float('SET_POSITION', params)
+ self.do_set_position(setpos)
+ homing_move = self.gcode.get_int('STOP_ON_ENDSTOP', params, 0)
+ if homing_move:
+ movepos = self.gcode.get_float('MOVE', params)
+ speed = self.gcode.get_float('SPEED', params, above=0.)
+ if 'ENABLE' not in params and not self.stepper.is_motor_enabled():
+ self.do_enable(True)
+ self.do_homing_move(movepos, speed, homing_move > 0)
+ elif 'MOVE' in params:
+ movepos = self.gcode.get_float('MOVE', params)
+ speed = self.gcode.get_float('SPEED', params, above=0.)
+ if 'ENABLE' not in params and not self.stepper.is_motor_enabled():
+ self.do_enable(True)
+ self.do_move(movepos, speed)
+ def handle_motor_off(self, print_time):
+ self.do_enable(0)
+
+def load_config_prefix(config):
+ return ManualStepper(config)
diff --git a/klippy/extras/probe.py b/klippy/extras/probe.py
index 24223cfb..3d0bf775 100644
--- a/klippy/extras/probe.py
+++ b/klippy/extras/probe.py
@@ -1,9 +1,9 @@
# Z-Probe support
#
-# Copyright (C) 2017-2018 Kevin O'Connor <kevin@koconnor.net>
+# Copyright (C) 2017-2019 Kevin O'Connor <kevin@koconnor.net>
#
# This file may be distributed under the terms of the GNU GPLv3 license.
-import pins, homing
+import pins, homing, manual_probe
HINT_TIMEOUT = """
Make sure to home the printer before probing. If the probe
@@ -15,6 +15,7 @@ the Z axis minimum position so the probe can travel further
class PrinterProbe:
def __init__(self, config, mcu_probe):
self.printer = config.get_printer()
+ self.name = config.get_name()
self.mcu_probe = mcu_probe
self.speed = config.getfloat('speed', 5.0)
self.x_offset = config.getfloat('x_offset', 0.)
@@ -31,10 +32,12 @@ class PrinterProbe:
self.printer.lookup_object('pins').register_chip('probe', self)
# Register PROBE/QUERY_PROBE commands
self.gcode = self.printer.lookup_object('gcode')
- self.gcode.register_command(
- 'PROBE', self.cmd_PROBE, desc=self.cmd_PROBE_help)
- self.gcode.register_command(
- 'QUERY_PROBE', self.cmd_QUERY_PROBE, desc=self.cmd_QUERY_PROBE_help)
+ self.gcode.register_command('PROBE', self.cmd_PROBE,
+ desc=self.cmd_PROBE_help)
+ self.gcode.register_command('QUERY_PROBE', self.cmd_QUERY_PROBE,
+ desc=self.cmd_QUERY_PROBE_help)
+ self.gcode.register_command('PROBE_CALIBRATE', self.cmd_PROBE_CALIBRATE,
+ desc=self.cmd_PROBE_CALIBRATE_help)
def setup_pin(self, pin_type, pin_params):
if pin_type != 'endstop' or pin_params['pin'] != 'z_virtual_endstop':
raise pins.error("Probe virtual endstop only useful as endstop pin")
@@ -50,9 +53,10 @@ class PrinterProbe:
pos = toolhead.get_position()
pos[2] = self.z_position
endstops = [(self.mcu_probe, "probe")]
+ verify = self.printer.get_start_args().get('debugoutput') is None
try:
homing_state.homing_move(pos, endstops, self.speed,
- probe_pos=True, verify_movement=True)
+ probe_pos=True, verify_movement=verify)
except homing.EndstopError as e:
reason = str(e)
if "Timeout during endstop homing" in reason:
@@ -70,6 +74,32 @@ class PrinterProbe:
res = self.mcu_probe.query_endstop_wait()
self.gcode.respond_info(
"probe: %s" % (["open", "TRIGGERED"][not not res],))
+ def probe_calibrate_finalize(self, kin_pos):
+ if kin_pos is None:
+ return
+ z_pos = self.z_offset - kin_pos[2]
+ self.gcode.respond_info(
+ "%s: z_offset: %.3f\n"
+ "The SAVE_CONFIG command will update the printer config file\n"
+ "with the above and restart the printer." % (self.name, z_pos))
+ configfile = self.printer.lookup_object('configfile')
+ configfile.set(self.name, 'z_offset', "%.3f" % (z_pos,))
+ cmd_PROBE_CALIBRATE_help = "Calibrate the probe's z_offset"
+ def cmd_PROBE_CALIBRATE(self, params):
+ # Perform initial probe
+ self.cmd_PROBE(params)
+ # Move away from the bed
+ toolhead = self.printer.lookup_object('toolhead')
+ curpos = toolhead.get_position()
+ curpos[2] += 5.
+ toolhead.move(curpos, self.speed)
+ # Move the nozzle over the probe point
+ curpos[0] += self.x_offset
+ curpos[1] += self.y_offset
+ toolhead.move(curpos, self.speed)
+ # Start manual probe
+ manual_probe.ManualProbeHelper(self.printer, params,
+ self.probe_calibrate_finalize)
# Endstop wrapper that enables probe specific features
class ProbeEndstopWrapper:
@@ -139,7 +169,7 @@ class ProbePointsHelper:
'sample_retract_dist', 2., above=0.)
# Internal probing state
self.results = []
- self.busy = False
+ self.busy = self.manual_probe = False
self.gcode = self.toolhead = None
def get_lift_speed(self):
return self.lift_speed
@@ -177,6 +207,9 @@ class ProbePointsHelper:
self._finalize(False)
raise self.gcode.error(str(e))
self.gcode.reset_last_position()
+ if self.manual_probe:
+ manual_probe.ManualProbeHelper(self.printer, {},
+ self._manual_probe_finalize)
def _automatic_probe_point(self):
positions = []
for i in range(self.samples):
@@ -199,13 +232,14 @@ class ProbePointsHelper:
probe = self.printer.lookup_object('probe', None)
method = self.gcode.get_str('METHOD', params, 'automatic').lower()
if probe is not None and method == 'automatic':
+ self.manual_probe = False
self.lift_speed = min(self.speed, probe.speed)
self.probe_offsets = probe.get_offsets()
if self.horizontal_move_z < self.probe_offsets[2]:
raise self.gcode.error("horizontal_move_z can't be less than"
" probe's z_offset")
else:
- probe = None
+ self.manual_probe = True
self.lift_speed = self.speed
self.probe_offsets = (0., 0., 0.)
# Start probe
@@ -213,27 +247,20 @@ class ProbePointsHelper:
self.busy = True
self._lift_z(self.horizontal_move_z, speed=self.speed)
self._move_next()
- if probe is None:
- # Setup for manual probing
- self.gcode.register_command('NEXT', None)
- self.gcode.register_command('NEXT', self.cmd_NEXT,
- desc=self.cmd_NEXT_help)
- else:
+ if not self.manual_probe:
# Perform automatic probing
while self.busy:
self._automatic_probe_point()
self._move_next()
- cmd_NEXT_help = "Move to the next XY position to probe"
- def cmd_NEXT(self, params):
- # Record current position for manual probe
- self.toolhead.get_last_move_time()
- self.results.append(self.toolhead.get_kinematics().calc_position())
- # Move to next position
+ def _manual_probe_finalize(self, kin_pos):
+ if kin_pos is None:
+ self._finalize(False)
+ return
+ self.results.append(kin_pos)
self._move_next()
def _finalize(self, success):
self.busy = False
self.gcode.reset_last_position()
- self.gcode.register_command('NEXT', None)
if success:
self.finalize_callback(self.probe_offsets, self.results)
diff --git a/klippy/extras/tmc2130.py b/klippy/extras/tmc2130.py
index acb1ca51..0374a747 100644
--- a/klippy/extras/tmc2130.py
+++ b/klippy/extras/tmc2130.py
@@ -129,7 +129,7 @@ def get_config_stealthchop(config, tmc_freq):
velocity = config.getfloat('stealthchop_threshold', 0., minval=0.)
if not velocity:
return mres, False, 0
- stepper_name = config.get_name().split()[1]
+ stepper_name = " ".join(config.get_name().split()[1:])
stepper_config = config.getsection(stepper_name)
step_dist = stepper_config.getfloat('step_distance')
step_dist_256 = step_dist / (1 << mres)
@@ -144,7 +144,7 @@ def get_config_stealthchop(config, tmc_freq):
class TMC2130:
def __init__(self, config):
self.printer = config.get_printer()
- self.name = config.get_name().split()[1]
+ self.name = config.get_name().split()[-1]
self.spi = bus.MCU_SPI_from_config(config, 3, default_speed=4000000)
# Allow virtual endstop to be created
self.diag1_pin = config.get('diag1_pin', None)
diff --git a/klippy/extras/tmc2208.py b/klippy/extras/tmc2208.py
index 60bf5a20..ef476e3b 100644
--- a/klippy/extras/tmc2208.py
+++ b/klippy/extras/tmc2208.py
@@ -259,7 +259,7 @@ def decode_tmc2208_read(reg, data):
class TMC2208:
def __init__(self, config):
self.printer = config.get_printer()
- self.name = config.get_name().split()[1]
+ self.name = config.get_name().split()[-1]
self.printer.register_event_handler("klippy:connect",
self.handle_connect)
# pin setup
diff --git a/klippy/toolhead.py b/klippy/toolhead.py
index cae280c7..32bda0fc 100644
--- a/klippy/toolhead.py
+++ b/klippy/toolhead.py
@@ -239,6 +239,7 @@ class ToolHead:
self.move_queue.set_flush_time(self.buffer_time_high)
self.printer.try_load_module(config, "idle_timeout")
self.printer.try_load_module(config, "statistics")
+ self.printer.try_load_module(config, "manual_probe")
# Setup iterative solver
ffi_main, ffi_lib = chelper.get_ffi()
self.cmove = ffi_main.gc(ffi_lib.move_alloc(), ffi_lib.free)
@@ -371,6 +372,7 @@ class ToolHead:
self.kin.motor_off(last_move_time)
for ext in kinematics.extruder.get_printer_extruders(self.printer):
ext.motor_off(last_move_time)
+ self.printer.send_event("toolhead:motor_off", last_move_time)
self.dwell(STALL_TIME)
logging.debug('; Max time of %f', last_move_time)
def wait_moves(self):
diff --git a/src/Makefile b/src/Makefile
index 02386d9c..546dc0a5 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -7,4 +7,4 @@ src-$(CONFIG_HAVE_GPIO_SPI) += spicmds.c thermocouple.c
src-$(CONFIG_HAVE_GPIO_I2C) += i2ccmds.c
src-$(CONFIG_HAVE_GPIO_HARD_PWM) += pwmcmds.c
src-$(CONFIG_HAVE_GPIO_BITBANGING) += lcd_st7920.c lcd_hd44780.c buttons.c \
- tmcuart.c
+ tmcuart.c spi_software.c
diff --git a/src/atsam/Kconfig b/src/atsam/Kconfig
index 2ac185ac..48ec8eec 100644
--- a/src/atsam/Kconfig
+++ b/src/atsam/Kconfig
@@ -9,6 +9,7 @@ config ATSAM_SELECT
select HAVE_GPIO_ADC
select HAVE_GPIO_I2C
select HAVE_GPIO_SPI
+ select HAVE_GPIO_HARD_PWM
select HAVE_GPIO_BITBANGING
config BOARD_DIRECTORY
diff --git a/src/atsam/Makefile b/src/atsam/Makefile
index 18c25006..781b204e 100644
--- a/src/atsam/Makefile
+++ b/src/atsam/Makefile
@@ -29,7 +29,7 @@ eflags-$(CONFIG_MACH_SAM4E) += -T lib/sam4e/gcc/gcc/sam4e8e_flash.ld
CFLAGS_klipper.elf += $(eflags-y) --specs=nano.specs --specs=nosys.specs
# Add source files
-src-y += atsam/main.c atsam/gpio.c atsam/i2c.c atsam/spi.c
+src-y += atsam/main.c atsam/gpio.c atsam/i2c.c atsam/spi.c atsam/hard_pwm.c
src-y += generic/crc16_ccitt.c generic/alloc.c
src-y += generic/armcm_irq.c generic/armcm_timer.c
usbserial-$(CONFIG_MACH_SAM3X) := atsam/sam3_usb.c
diff --git a/src/atsam/gpio.h b/src/atsam/gpio.h
index e106e094..5b5c8139 100644
--- a/src/atsam/gpio.h
+++ b/src/atsam/gpio.h
@@ -1,5 +1,5 @@
-#ifndef __SAM3_GPIO_H
-#define __SAM3_GPIO_H
+#ifndef __ATSAM_GPIO_H
+#define __ATSAM_GPIO_H
#include <stdint.h> // uint32_t
@@ -21,6 +21,12 @@ struct gpio_in gpio_in_setup(uint8_t pin, int8_t pull_up);
void gpio_in_reset(struct gpio_in g, int8_t pull_up);
uint8_t gpio_in_read(struct gpio_in g);
+struct gpio_pwm {
+ void *reg;
+};
+struct gpio_pwm gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val);
+void gpio_pwm_write(struct gpio_pwm g, uint8_t val);
+
struct gpio_adc {
uint32_t chan;
};
diff --git a/src/atsam/hard_pwm.c b/src/atsam/hard_pwm.c
new file mode 100644
index 00000000..7d1881a6
--- /dev/null
+++ b/src/atsam/hard_pwm.c
@@ -0,0 +1,96 @@
+// Hardware PWM support on atsam
+//
+// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "command.h" // shutdown
+#include "gpio.h" // gpio_pwm_write
+#include "internal.h" // GPIO
+#include "sched.h" // sched_shutdown
+
+struct gpio_pwm_info {
+ uint8_t gpio, channel, ptype;
+};
+
+static const struct gpio_pwm_info pwm_regs[] = {
+#if CONFIG_MACH_SAM3X
+ { GPIO('A', 21), 0, 'B' },
+ { GPIO('B', 16), 0, 'B' },
+ { GPIO('A', 12), 1, 'B' },
+ { GPIO('B', 17), 1, 'B' },
+ { GPIO('A', 20), 2, 'B' },
+ { GPIO('B', 18), 2, 'B' },
+ { GPIO('A', 0), 3, 'B' },
+ { GPIO('B', 19), 3, 'B' },
+#if CONFIG_MACH_SAM3X8E
+ { GPIO('C', 2), 0, 'B' },
+ { GPIO('C', 4), 1, 'B' },
+ { GPIO('C', 6), 2, 'B' },
+ { GPIO('C', 8), 3, 'B' },
+ { GPIO('C', 21), 4, 'B' },
+ { GPIO('C', 22), 5, 'B' },
+ { GPIO('C', 23), 6, 'B' },
+ { GPIO('C', 24), 7, 'B' },
+#endif
+#elif CONFIG_MACH_SAM4
+ { GPIO('A', 19), 0, 'B' },
+ { GPIO('B', 5), 0, 'B' },
+ { GPIO('C', 0), 0, 'B' },
+ { GPIO('C', 13), 0, 'B' },
+ { GPIO('A', 20), 1, 'B' },
+ { GPIO('B', 12), 1, 'A' },
+ { GPIO('C', 1), 1, 'B' },
+ { GPIO('C', 15), 1, 'B' },
+ { GPIO('A', 16), 2, 'C' },
+ { GPIO('A', 30), 2, 'A' },
+ { GPIO('B', 13), 2, 'A' },
+ { GPIO('C', 2), 2, 'B' },
+ { GPIO('A', 15), 3, 'C' },
+ { GPIO('C', 3), 3, 'B' },
+ { GPIO('C', 22), 3, 'B' },
+#endif
+};
+
+#define MAX_PWM 255
+
+DECL_CONSTANT(PWM_MAX, MAX_PWM);
+
+struct gpio_pwm
+gpio_pwm_setup(uint8_t pin, uint32_t cycle_time, uint8_t val)
+{
+ // Find pin in pwm_regs table
+ const struct gpio_pwm_info *p = pwm_regs;
+ for (; ; p++) {
+ if (p >= &pwm_regs[ARRAY_SIZE(pwm_regs)])
+ shutdown("Not a valid PWM pin");
+ if (p->gpio == pin)
+ break;
+ }
+
+ // Map cycle_time to pwm clock divisor
+ uint32_t div;
+ for (div=0; div<10; div++)
+ if (cycle_time < ((MAX_PWM << (div + 1)) + (MAX_PWM << div)) / 2)
+ break;
+
+ // Enable clock
+ enable_pclock(ID_PWM);
+
+ // Enable PWM output
+ if (PWM->PWM_SR & (1 << p->channel))
+ shutdown("PWM channel already in use");
+ gpio_peripheral(pin, p->ptype, 0);
+ PWM->PWM_CH_NUM[p->channel].PWM_CMR = div;
+ PWM->PWM_CH_NUM[p->channel].PWM_CPRD = MAX_PWM;
+ PWM->PWM_CH_NUM[p->channel].PWM_CDTY = val;
+ PWM->PWM_ENA = 1 << p->channel;
+
+ return (struct gpio_pwm){ (void*)&PWM->PWM_CH_NUM[p->channel].PWM_CDTYUPD };
+}
+
+void
+gpio_pwm_write(struct gpio_pwm g, uint8_t val)
+{
+ *(volatile uint32_t*)g.reg = val;
+}
diff --git a/src/atsamd/gpio.h b/src/atsamd/gpio.h
index 0a89f0a1..ba15e719 100644
--- a/src/atsamd/gpio.h
+++ b/src/atsamd/gpio.h
@@ -1,5 +1,5 @@
-#ifndef __SAM3X8E_GPIO_H
-#define __SAM3X8E_GPIO_H
+#ifndef __ATSAMD_GPIO_H
+#define __ATSAMD_GPIO_H
#include <stdint.h>
diff --git a/src/spi_software.c b/src/spi_software.c
new file mode 100644
index 00000000..385b4f87
--- /dev/null
+++ b/src/spi_software.c
@@ -0,0 +1,81 @@
+// Software SPI emulation
+//
+// Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net>
+//
+// This file may be distributed under the terms of the GNU GPLv3 license.
+
+#include "board/irq.h" // gpio_out_setup
+#include "board/gpio.h" // gpio_out_setup
+#include "basecmd.h" // oid_alloc
+#include "command.h" // DECL_COMMAND
+#include "sched.h" // sched_shutdown
+
+struct spi_software {
+ struct gpio_out sclk, mosi;
+ struct gpio_in miso;
+ uint8_t mode;
+};
+
+void
+command_config_software_spi(uint32_t *args)
+{
+ uint8_t oid = args[0], sclk_pin = args[1], mosi_pin = args[2];
+ uint8_t miso_pin = args[3], mode = args[4];
+ if (mode > 3)
+ shutdown("Invalid spi mode");
+
+ struct spi_software *spi = oid_alloc(oid, command_config_software_spi
+ , sizeof(*spi));
+
+ spi->sclk = gpio_out_setup(sclk_pin, 0);
+ spi->mosi = gpio_out_setup(mosi_pin, 0);
+ spi->miso = gpio_in_setup(miso_pin, 1);
+}
+DECL_COMMAND(command_config_software_spi,
+ "config_software_spi oid=%c sclk_pin=%u mosi_pin=%u miso_pin=%u"
+ " mode=%u rate=%u");
+
+struct spi_software *
+spi_software_oid_lookup(uint8_t oid)
+{
+ return oid_lookup(oid, command_config_software_spi);
+}
+
+void
+spi_software_prepare(struct spi_software *ss)
+{
+ gpio_out_write(ss->sclk, ss->mode < 2 ? 0 : 1);
+}
+
+void
+spi_software_transfer(struct spi_software *ss, uint8_t receive_data
+ , uint8_t len, uint8_t *data)
+{
+ while (len--) {
+ uint8_t outbuf = *data;
+ uint8_t inbuf = 0;
+ for (uint_fast8_t i = 0; i < 8; i++) {
+ if (ss->mode & 0x01) {
+ // MODE 1 & 3
+ gpio_out_toggle(ss->sclk);
+ gpio_out_write(ss->mosi, outbuf & 0x80);
+ outbuf <<= 1;
+ gpio_out_toggle(ss->sclk);
+ inbuf <<= 1;
+ inbuf |= gpio_in_read(ss->miso);
+ } else {
+ // MODE 0 & 2
+ gpio_out_write(ss->mosi, outbuf & 0x80);
+ outbuf <<= 1;
+ gpio_out_toggle(ss->sclk);
+ inbuf <<= 1;
+ inbuf |= gpio_in_read(ss->miso);
+ gpio_out_toggle(ss->sclk);
+ }
+ }
+
+ if (receive_data)
+ *data = inbuf;
+ data++;
+ }
+}
diff --git a/src/spi_software.h b/src/spi_software.h
new file mode 100644
index 00000000..549866f2
--- /dev/null
+++ b/src/spi_software.h
@@ -0,0 +1,11 @@
+#ifndef __SPI_SOFTWARE_H
+#define __SPI_SOFTWARE_H
+
+#include <stdint.h> // uint8_t
+
+struct spi_software *spi_software_oid_lookup(uint8_t oid);
+void spi_software_prepare(struct spi_software *ss);
+void spi_software_transfer(struct spi_software *ss, uint8_t receive_data
+ , uint8_t len, uint8_t *data);
+
+#endif // spi_software.h
diff --git a/src/spicmds.c b/src/spicmds.c
index 0017c56b..99e3b3a6 100644
--- a/src/spicmds.c
+++ b/src/spicmds.c
@@ -5,14 +5,19 @@
// This file may be distributed under the terms of the GNU GPLv3 license.
#include <string.h> // memcpy
+#include "autoconf.h" // CONFIG_HAVE_GPIO_BITBANGING
#include "board/gpio.h" // gpio_out_write
#include "basecmd.h" // oid_alloc
#include "command.h" // DECL_COMMAND
#include "sched.h" // DECL_SHUTDOWN
+#include "spi_software.h" // spi_software_setup
#include "spicmds.h" // spidev_transfer
struct spidev_s {
- struct spi_config spi_config;
+ union {
+ struct spi_config spi_config;
+ struct spi_software *spi_software;
+ };
struct gpio_out pin;
uint8_t flags;
uint8_t shutdown_msg_len;
@@ -20,7 +25,7 @@ struct spidev_s {
};
enum {
- SF_HAVE_PIN = 1,
+ SF_HAVE_PIN = 1, SF_SOFTWARE = 2,
};
void
@@ -58,6 +63,26 @@ DECL_COMMAND(command_config_spi_without_cs,
"config_spi_without_cs oid=%c bus=%u mode=%u rate=%u"
" shutdown_msg=%*s");
+void
+command_config_spi_from_software(uint32_t *args)
+{
+ uint8_t shutdown_msg_len = args[3];
+ struct spi_software *sspi = spi_software_oid_lookup(args[1]);
+ struct spidev_s *spi = oid_alloc(args[0], command_config_spi
+ , sizeof(*spi) + shutdown_msg_len);
+ spi->pin = gpio_out_setup(args[2], 1);
+ spi->flags = SF_HAVE_PIN | SF_SOFTWARE;
+ spi->spi_software = sspi;
+ spi->shutdown_msg_len = shutdown_msg_len;
+ uint8_t *shutdown_msg = (void*)(size_t)args[4];
+ memcpy(spi->shutdown_msg, shutdown_msg, shutdown_msg_len);
+}
+#if CONFIG_HAVE_GPIO_BITBANGING
+DECL_COMMAND(command_config_spi_from_software,
+ "config_spi_from_software oid=%c sw_oid=%u pin=%u"
+ " shutdown_msg=%*s");
+#endif
+
struct spidev_s *
spidev_oid_lookup(uint8_t oid)
{
@@ -68,12 +93,18 @@ void
spidev_transfer(struct spidev_s *spi, uint8_t receive_data
, uint8_t data_len, uint8_t *data)
{
- spi_prepare(spi->spi_config);
+ if (CONFIG_HAVE_GPIO_BITBANGING && spi->flags & SF_SOFTWARE)
+ spi_software_prepare(spi->spi_software);
+ else
+ spi_prepare(spi->spi_config);
if (spi->flags & SF_HAVE_PIN)
gpio_out_write(spi->pin, 0);
- spi_transfer(spi->spi_config, receive_data, data_len, data);
+ if (CONFIG_HAVE_GPIO_BITBANGING && spi->flags & SF_SOFTWARE)
+ spi_software_transfer(spi->spi_software, receive_data, data_len, data);
+ else
+ spi_transfer(spi->spi_config, receive_data, data_len, data);
if (spi->flags & SF_HAVE_PIN)
gpio_out_write(spi->pin, 1);
diff --git a/test/klippy/manual_stepper.cfg b/test/klippy/manual_stepper.cfg
new file mode 100644
index 00000000..4ca734a3
--- /dev/null
+++ b/test/klippy/manual_stepper.cfg
@@ -0,0 +1,22 @@
+# Test config for manual_stepper
+[manual_stepper basic_stepper]
+step_pin: ar54
+dir_pin: ar55
+enable_pin: !ar38
+step_distance: .0125
+
+[manual_stepper homing_stepper]
+step_pin: ar60
+dir_pin: !ar61
+enable_pin: !ar56
+step_distance: .0125
+endstop_pin: ^ar14
+
+[mcu]
+serial: /dev/ttyACM0
+pin_map: arduino
+
+[printer]
+kinematics: none
+max_velocity: 300
+max_accel: 3000
diff --git a/test/klippy/manual_stepper.test b/test/klippy/manual_stepper.test
new file mode 100644
index 00000000..4f682331
--- /dev/null
+++ b/test/klippy/manual_stepper.test
@@ -0,0 +1,24 @@
+# Test case for manual_stepper
+CONFIG manual_stepper.cfg
+DICTIONARY atmega2560-16mhz.dict
+
+# Test basic moves
+MANUAL_STEPPER STEPPER=basic_stepper ENABLE=1
+MANUAL_STEPPER STEPPER=basic_stepper SET_POSITION=0
+MANUAL_STEPPER STEPPER=basic_stepper MOVE=10 SPEED=10
+MANUAL_STEPPER STEPPER=basic_stepper MOVE=5 SPEED=5
+MANUAL_STEPPER STEPPER=basic_stepper MOVE=12 SPEED=12
+MANUAL_STEPPER STEPPER=basic_stepper ENABLE=0
+
+# Test homing move
+MANUAL_STEPPER STEPPER=homing_stepper ENABLE=1
+MANUAL_STEPPER STEPPER=homing_stepper SET_POSITION=0
+MANUAL_STEPPER STEPPER=homing_stepper MOVE=10 SPEED=10
+MANUAL_STEPPER STEPPER=homing_stepper ENABLE=0
+
+# Test motor off
+M84
+
+# Verify stepper_buzz
+STEPPER_BUZZ STEPPER="manual_stepper basic_stepper"
+STEPPER_BUZZ STEPPER="manual_stepper homing_stepper"
diff --git a/test/klippy/multi_z.test b/test/klippy/multi_z.test
index 5d7980d7..41ea9fea 100644
--- a/test/klippy/multi_z.test
+++ b/test/klippy/multi_z.test
@@ -27,6 +27,30 @@ G1 Z2 X2 Y3
PROBE
QUERY_PROBE
+# Test manual probe commands
+PROBE_CALIBRATE
+ABORT
+PROBE_CALIBRATE SPEED=7.3
+TESTZ Z=-.2
+TESTZ Z=-.3
+TESTZ Z=+.1
+TESTZ Z=++
+TESTZ Z=--
+TESTZ Z=+
+TESTZ Z=-
+ACCEPT
+Z_ENDSTOP_CALIBRATE
+TESTZ Z=-.1
+TESTZ Z=+.2
+ACCEPT
+MANUAL_PROBE
+TESTZ Z=--
+ABORT
+
+# Do regular probe
+PROBE
+QUERY_PROBE
+
# Verify stepper_buzz
STEPPER_BUZZ STEPPER=stepper_z
STEPPER_BUZZ STEPPER=stepper_z1
diff --git a/test/klippy/printers.test b/test/klippy/printers.test
index e66f7954..b2d60695 100644
--- a/test/klippy/printers.test
+++ b/test/klippy/printers.test
@@ -76,4 +76,4 @@ CONFIG ../../config/generic-replicape.cfg
DICTIONARY atmega2560-16mhz.dict zboard=atmega2560-16mhz.dict auxboard=atmega2560-16mhz.dict
CONFIG ../../config/example-multi-mcu.cfg
DICTIONARY atmega2560-16mhz.dict z=atmega2560-16mhz.dict
-CONFIG ../../config/kit-voron2-2018.cfg
+CONFIG ../../config/kit-voron2-250mm.cfg
diff --git a/test/klippy/z_virtual_endstop.test b/test/klippy/z_virtual_endstop.test
index d9a07497..85e7a3eb 100644
--- a/test/klippy/z_virtual_endstop.test
+++ b/test/klippy/z_virtual_endstop.test
@@ -21,5 +21,18 @@ G1 Z5 X0 Y0
PROBE
QUERY_PROBE
+# Test PROBE_CALIBRATE
+PROBE_CALIBRATE
+ABORT
+PROBE_CALIBRATE SPEED=7.3
+TESTZ Z=-.2
+TESTZ Z=-.3
+TESTZ Z=+.1
+TESTZ Z=++
+TESTZ Z=--
+TESTZ Z=+
+TESTZ Z=-
+ACCEPT
+
# Move again
G1 Z9