diff options
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.) + + + +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 Binary files differnew file mode 100644 index 00000000..3fabbad2 --- /dev/null +++ b/docs/img/paper-test.jpg 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 |