Merge pull request #6054 from thinkyhead/rc_ubl_renewal
UBL for RCBugFix — cleanup, rebase, patch2.0.x
commit
2c630a1b5c
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,331 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016, 2017 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Marlin.h"
|
||||
#include "math.h"
|
||||
|
||||
#ifndef UNIFIED_BED_LEVELING_H
|
||||
#define UNIFIED_BED_LEVELING_H
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
#define UBL_OK false
|
||||
#define UBL_ERR true
|
||||
|
||||
typedef struct {
|
||||
int x_index, y_index;
|
||||
float distance; // Not always used. But when populated, it is the distance
|
||||
// from the search location
|
||||
} mesh_index_pair;
|
||||
|
||||
struct vector { double dx, dy, dz; };
|
||||
|
||||
enum Mesh_Point_Type { INVALID, REAL, SET_IN_BITMAP };
|
||||
|
||||
bool axis_unhomed_error(bool, bool, bool);
|
||||
void dump(char *str, float f);
|
||||
bool G29_lcd_clicked();
|
||||
void probe_entire_mesh(float, float, bool, bool);
|
||||
void UBL_line_to_destination(const float&, const float&, const float&, const float&, const float&, uint8_t);
|
||||
void manually_probe_remaining_mesh(float, float, float, float, bool);
|
||||
struct vector tilt_mesh_based_on_3pts(float, float, float);
|
||||
void new_set_bed_level_equation_3pts(float, float, float);
|
||||
float measure_business_card_thickness(float);
|
||||
mesh_index_pair find_closest_mesh_point_of_type(Mesh_Point_Type, float, float, bool, unsigned int[16]);
|
||||
void Find_Mean_Mesh_Height();
|
||||
void Shift_Mesh_Height();
|
||||
bool G29_Parameter_Parsing();
|
||||
void G29_What_Command();
|
||||
void G29_EEPROM_Dump();
|
||||
void G29_Kompare_Current_Mesh_to_Stored_Mesh();
|
||||
void fine_tune_mesh(float, float, float, bool);
|
||||
void bit_clear(uint16_t bits[16], uint8_t x, uint8_t y);
|
||||
void bit_set(uint16_t bits[16], uint8_t x, uint8_t y);
|
||||
bool is_bit_set(uint16_t bits[16], uint8_t x, uint8_t y);
|
||||
char *ftostr43sign(const float&, char);
|
||||
|
||||
void gcode_G26();
|
||||
void gcode_G28();
|
||||
void gcode_G29();
|
||||
extern char conv[9];
|
||||
|
||||
void save_UBL_active_state_and_disable();
|
||||
void restore_UBL_active_state_and_leave();
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if ENABLED(ULTRA_LCD)
|
||||
extern char lcd_status_message[];
|
||||
void lcd_quick_feedback();
|
||||
#endif
|
||||
|
||||
enum MBLStatus { MBL_STATUS_NONE = 0, MBL_STATUS_HAS_MESH_BIT = 0, MBL_STATUS_ACTIVE_BIT = 1 };
|
||||
|
||||
#define MESH_X_DIST ((float(UBL_MESH_MAX_X) - float(UBL_MESH_MIN_X)) / (float(UBL_MESH_NUM_X_POINTS) - 1.0))
|
||||
#define MESH_Y_DIST ((float(UBL_MESH_MAX_Y) - float(UBL_MESH_MIN_Y)) / (float(UBL_MESH_NUM_Y_POINTS) - 1.0))
|
||||
|
||||
extern bool G26_Debug_flag;
|
||||
extern float last_specified_z;
|
||||
extern float fade_scaling_factor_for_current_height;
|
||||
extern float z_values[UBL_MESH_NUM_X_POINTS][UBL_MESH_NUM_Y_POINTS];
|
||||
extern float mesh_index_to_X_location[UBL_MESH_NUM_X_POINTS + 1]; // +1 just because of paranoia that we might end up on the
|
||||
extern float mesh_index_to_Y_location[UBL_MESH_NUM_Y_POINTS + 1]; // the last Mesh Line and that is the start of a whole new cell
|
||||
|
||||
class bed_leveling {
|
||||
public:
|
||||
struct ubl_state {
|
||||
bool active = false;
|
||||
float z_offset = 0.0;
|
||||
int EEPROM_storage_slot = -1,
|
||||
n_x = UBL_MESH_NUM_X_POINTS,
|
||||
n_y = UBL_MESH_NUM_Y_POINTS;
|
||||
float mesh_x_min = UBL_MESH_MIN_X,
|
||||
mesh_y_min = UBL_MESH_MIN_Y,
|
||||
mesh_x_max = UBL_MESH_MAX_X,
|
||||
mesh_y_max = UBL_MESH_MAX_Y,
|
||||
mesh_x_dist = MESH_X_DIST,
|
||||
mesh_y_dist = MESH_Y_DIST,
|
||||
G29_Correction_Fade_Height = 10.0,
|
||||
G29_Fade_Height_Multiplier = 1.0 / 10.0; // It is cheaper to do a floating point multiply than a floating
|
||||
// point divide. So, we keep this number in both forms. The first
|
||||
// is for the user. The second one is the one that is actually used
|
||||
// again and again and again during the correction calculations.
|
||||
|
||||
unsigned char padding[24]; // This is just to allow room to add state variables without
|
||||
// changing the location of data structures in the EEPROM.
|
||||
// This is for compatability with future versions to keep
|
||||
// people from having to regenerate thier mesh data.
|
||||
//
|
||||
// If you change the contents of this struct, please adjust
|
||||
// the padding[] to keep the size the same!
|
||||
} state, pre_initialized;
|
||||
|
||||
bed_leveling();
|
||||
// ~bed_leveling(); // No destructor because this object never goes away!
|
||||
|
||||
void display_map(int);
|
||||
|
||||
void reset();
|
||||
void invalidate();
|
||||
|
||||
void store_state();
|
||||
void load_state();
|
||||
void store_mesh(int);
|
||||
void load_mesh(int);
|
||||
|
||||
bool sanity_check();
|
||||
|
||||
FORCE_INLINE float map_x_index_to_bed_location(int8_t i){ return ((float) UBL_MESH_MIN_X) + (((float) MESH_X_DIST) * (float) i); };
|
||||
FORCE_INLINE float map_y_index_to_bed_location(int8_t i){ return ((float) UBL_MESH_MIN_Y) + (((float) MESH_Y_DIST) * (float) i); };
|
||||
|
||||
void set_z(const int8_t px, const int8_t py, const float z) { z_values[px][py] = z; }
|
||||
|
||||
int8_t get_cell_index_x(float x) {
|
||||
int8_t cx = (x - (UBL_MESH_MIN_X)) * (1.0 / (MESH_X_DIST));
|
||||
return constrain(cx, 0, (UBL_MESH_NUM_X_POINTS) - 1); // -1 is appropriate if we want all movement to the X_MAX
|
||||
} // position. But with this defined this way, it is possible
|
||||
// to extrapolate off of this point even further out. Probably
|
||||
// that is OK because something else should be keeping that from
|
||||
// happening and should not be worried about at this level.
|
||||
int8_t get_cell_index_y(float y) {
|
||||
int8_t cy = (y - (UBL_MESH_MIN_Y)) * (1.0 / (MESH_Y_DIST));
|
||||
return constrain(cy, 0, (UBL_MESH_NUM_Y_POINTS) - 1); // -1 is appropriate if we want all movement to the Y_MAX
|
||||
} // position. But with this defined this way, it is possible
|
||||
// to extrapolate off of this point even further out. Probably
|
||||
// that is OK because something else should be keeping that from
|
||||
// happening and should not be worried about at this level.
|
||||
|
||||
int8_t find_closest_x_index(float x) {
|
||||
int8_t px = (x - (UBL_MESH_MIN_X) + (MESH_X_DIST) * 0.5) * (1.0 / (MESH_X_DIST));
|
||||
return (px >= 0 && px < (UBL_MESH_NUM_X_POINTS)) ? px : -1;
|
||||
}
|
||||
|
||||
int8_t find_closest_y_index(float y) {
|
||||
int8_t py = (y - (UBL_MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * (1.0 / (MESH_Y_DIST));
|
||||
return (py >= 0 && py < (UBL_MESH_NUM_Y_POINTS)) ? py : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* z2 --|
|
||||
* z0 | |
|
||||
* | | + (z2-z1)
|
||||
* z1 | | |
|
||||
* ---+-------------+--------+-- --|
|
||||
* a1 a0 a2
|
||||
* |<---delta_a---------->|
|
||||
*
|
||||
* calc_z0 is the basis for all the Mesh Based correction. It is used to
|
||||
* find the expected Z Height at a position between two known Z-Height locations
|
||||
*
|
||||
* It is farly expensive with its 4 floating point additions and 2 floating point
|
||||
* multiplications.
|
||||
*/
|
||||
inline float calc_z0(float a0, float a1, float z1, float a2, float z2) {
|
||||
float delta_z = (z2 - z1);
|
||||
float delta_a = (a0 - a1) / (a2 - a1);
|
||||
return z1 + delta_a * delta_z;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_z_correction_at_Y_intercept(float x0, int x1_i, int yi) only takes
|
||||
* three parameters. It assumes the x0 point is on a Mesh line denoted by yi. In theory
|
||||
* we could use get_cell_index_x(float x) to obtain the 2nd parameter x1_i but any code calling
|
||||
* the get_z_correction_along_vertical_mesh_line_at_specific_X routine will already have
|
||||
* the X index of the x0 intersection available and we don't want to perform any extra floating
|
||||
* point operations.
|
||||
*/
|
||||
inline float get_z_correction_along_horizontal_mesh_line_at_specific_X(float x0, int x1_i, int yi) {
|
||||
if (x1_i < 0 || yi < 0 || x1_i >= UBL_MESH_NUM_X_POINTS || yi >= UBL_MESH_NUM_Y_POINTS) {
|
||||
SERIAL_ECHOPAIR("? in get_z_correction_along_horizontal_mesh_line_at_specific_X(x0=", x0);
|
||||
SERIAL_ECHOPAIR(",x1_i=", x1_i);
|
||||
SERIAL_ECHOPAIR(",yi=", yi);
|
||||
SERIAL_CHAR(')');
|
||||
SERIAL_EOL;
|
||||
return NAN;
|
||||
}
|
||||
|
||||
const float a0ma1diva2ma1 = (x0 - mesh_index_to_X_location[x1_i]) * (1.0 / (MESH_X_DIST)),
|
||||
z1 = z_values[x1_i][yi],
|
||||
z2 = z_values[x1_i + 1][yi],
|
||||
dz = (z2 - z1);
|
||||
|
||||
return z1 + a0ma1diva2ma1 * dz;
|
||||
}
|
||||
|
||||
//
|
||||
// See comments above for get_z_correction_along_horizontal_mesh_line_at_specific_X
|
||||
//
|
||||
inline float get_z_correction_along_vertical_mesh_line_at_specific_Y(float y0, int xi, int y1_i) {
|
||||
if (xi < 0 || y1_i < 0 || xi >= UBL_MESH_NUM_X_POINTS || y1_i >= UBL_MESH_NUM_Y_POINTS) {
|
||||
SERIAL_ECHOPAIR("? in get_z_correction_along_vertical_mesh_line_at_specific_X(y0=", y0);
|
||||
SERIAL_ECHOPAIR(", x1_i=", xi);
|
||||
SERIAL_ECHOPAIR(", yi=", y1_i);
|
||||
SERIAL_CHAR(')');
|
||||
SERIAL_EOL;
|
||||
return NAN;
|
||||
}
|
||||
|
||||
const float a0ma1diva2ma1 = (y0 - mesh_index_to_Y_location[y1_i]) * (1.0 / (MESH_Y_DIST)),
|
||||
z1 = z_values[xi][y1_i],
|
||||
z2 = z_values[xi][y1_i + 1],
|
||||
dz = (z2 - z1);
|
||||
|
||||
return z1 + a0ma1diva2ma1 * dz;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the generic Z-Correction. It works anywhere within a Mesh Cell. It first
|
||||
* does a linear interpolation along both of the bounding X-Mesh-Lines to find the
|
||||
* Z-Height at both ends. Then it does a linear interpolation of these heights based
|
||||
* on the Y position within the cell.
|
||||
*/
|
||||
float get_z_correction(float x0, float y0) {
|
||||
int8_t cx = get_cell_index_x(x0),
|
||||
cy = get_cell_index_y(y0);
|
||||
|
||||
if (cx < 0 || cy < 0 || cx >= UBL_MESH_NUM_X_POINTS || cy >= UBL_MESH_NUM_Y_POINTS) {
|
||||
|
||||
SERIAL_ECHOPAIR("? in get_z_correction(x0=", x0);
|
||||
SERIAL_ECHOPAIR(", y0=", y0);
|
||||
SERIAL_CHAR(')');
|
||||
SERIAL_EOL;
|
||||
|
||||
#if ENABLED(ULTRA_LCD)
|
||||
strcpy(lcd_status_message, "get_z_correction() indexes out of range.");
|
||||
lcd_quick_feedback();
|
||||
#endif
|
||||
return 0.0; // this used to return state.z_offset
|
||||
}
|
||||
|
||||
float z1 = calc_z0(x0,
|
||||
map_x_index_to_bed_location(cx), z_values[cx][cy],
|
||||
map_x_index_to_bed_location(cx + 1), z_values[cx + 1][cy]);
|
||||
float z2 = calc_z0(x0,
|
||||
map_x_index_to_bed_location(cx), z_values[cx][cy + 1],
|
||||
map_x_index_to_bed_location(cx + 1), z_values[cx + 1][cy + 1]);
|
||||
float z0 = calc_z0(y0,
|
||||
map_y_index_to_bed_location(cy), z1,
|
||||
map_y_index_to_bed_location(cy + 1), z2);
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
SERIAL_ECHOPAIR(" raw get_z_correction(", x0);
|
||||
SERIAL_ECHOPAIR(",", y0);
|
||||
SERIAL_ECHOPGM(")=");
|
||||
SERIAL_PROTOCOL_F(z0, 6);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
SERIAL_ECHOPGM(" >>>---> ");
|
||||
SERIAL_PROTOCOL_F(z0, 6);
|
||||
SERIAL_EOL;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in blm.z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
|
||||
#if ENABLED(DEBUG_LEVELING_FEATURE)
|
||||
if (DEBUGGING(MESH_ADJUST)) {
|
||||
SERIAL_ECHOPGM("??? Yikes! NAN in get_z_correction( ");
|
||||
SERIAL_ECHO(x0);
|
||||
SERIAL_ECHOPGM(", ");
|
||||
SERIAL_ECHO(y0);
|
||||
SERIAL_ECHOLNPGM(" )");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
return z0; // there used to be a +state.z_offset on this line
|
||||
}
|
||||
|
||||
/**
|
||||
* This routine is used to scale the Z correction depending upon the current nozzle height. It is
|
||||
* optimized for speed. It avoids floating point operations by checking if the requested scaling
|
||||
* factor is going to be the same as the last time the function calculated a value. If so, it just
|
||||
* returns it.
|
||||
*
|
||||
* If it must do a calcuation, it will return a scaling factor of 0.0 if the UBL System is not active
|
||||
* or if the current Z Height is past the specified 'Fade Height'
|
||||
*/
|
||||
FORCE_INLINE float fade_scaling_factor_for_Z(float current_z) {
|
||||
if (last_specified_z == current_z)
|
||||
return fade_scaling_factor_for_current_height;
|
||||
|
||||
last_specified_z = current_z;
|
||||
fade_scaling_factor_for_current_height =
|
||||
state.active && current_z < state.G29_Correction_Fade_Height
|
||||
? 1.0 - (current_z * state.G29_Fade_Height_Multiplier)
|
||||
: 0.0;
|
||||
return fade_scaling_factor_for_current_height;
|
||||
}
|
||||
};
|
||||
|
||||
extern bed_leveling blm;
|
||||
extern int Unified_Bed_Leveling_EEPROM_start;
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
||||
#endif // UNIFIED_BED_LEVELING_H
|
@ -0,0 +1,296 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "Marlin.h"
|
||||
#include "math.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
#include "UBL.h"
|
||||
#include "hex_print_routines.h"
|
||||
|
||||
/**
|
||||
* These variables used to be declared inside the bed_leveling class. We are going to still declare
|
||||
* them within the .cpp file for bed leveling. But there is only one instance of the bed leveling
|
||||
* object and we can get rid of a level of inderection by not making them 'member data'. So, in the
|
||||
* interest of speed, we do it this way. When we move to a 32-Bit processor, they can be moved
|
||||
* back inside the bed leveling class.
|
||||
*/
|
||||
float last_specified_z,
|
||||
fade_scaling_factor_for_current_height,
|
||||
z_values[UBL_MESH_NUM_X_POINTS][UBL_MESH_NUM_Y_POINTS],
|
||||
mesh_index_to_X_location[UBL_MESH_NUM_X_POINTS + 1], // +1 just because of paranoia that we might end up on the
|
||||
mesh_index_to_Y_location[UBL_MESH_NUM_Y_POINTS + 1]; // the last Mesh Line and that is the start of a whole new cell
|
||||
|
||||
bed_leveling::bed_leveling() {
|
||||
for (uint8_t i = 0; i <= UBL_MESH_NUM_X_POINTS; i++) // We go one past what we expect to ever need for safety
|
||||
mesh_index_to_X_location[i] = double(UBL_MESH_MIN_X) + double(MESH_X_DIST) * double(i);
|
||||
|
||||
for (uint8_t i = 0; i <= UBL_MESH_NUM_Y_POINTS; i++) // We go one past what we expect to ever need for safety
|
||||
mesh_index_to_Y_location[i] = double(UBL_MESH_MIN_Y) + double(MESH_Y_DIST) * double(i);
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
void bed_leveling::store_state() {
|
||||
int k = E2END - sizeof(blm.state);
|
||||
eeprom_write_block((void *)&blm.state, (void *)k, sizeof(blm.state));
|
||||
}
|
||||
|
||||
void bed_leveling::load_state() {
|
||||
int k = E2END - sizeof(blm.state);
|
||||
eeprom_read_block((void *)&blm.state, (void *)k, sizeof(blm.state));
|
||||
|
||||
if (sanity_check())
|
||||
SERIAL_PROTOCOLLNPGM("?In load_state() sanity_check() failed.\n");
|
||||
|
||||
// These lines can go away in a few weeks. They are just
|
||||
// to make sure people updating thier firmware won't be using
|
||||
if (blm.state.G29_Fade_Height_Multiplier != 1.0 / blm.state.G29_Correction_Fade_Height) { // an incomplete Bed_Leveling.state structure. For speed
|
||||
blm.state.G29_Fade_Height_Multiplier = 1.0 / blm.state.G29_Correction_Fade_Height; // we now multiply by the inverse of the Fade Height instead of
|
||||
store_state(); // dividing by it. Soon... all of the old structures will be
|
||||
} // updated, but until then, we try to ease the transition
|
||||
// for our Beta testers.
|
||||
}
|
||||
|
||||
void bed_leveling::load_mesh(int m) {
|
||||
int k = E2END - sizeof(blm.state),
|
||||
j = (k - Unified_Bed_Leveling_EEPROM_start) / sizeof(z_values);
|
||||
|
||||
if (m == -1) {
|
||||
SERIAL_PROTOCOLLNPGM("?No mesh saved in EEPROM. Zeroing mesh in memory.\n");
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m < 0 || m >= j || Unified_Bed_Leveling_EEPROM_start <= 0) {
|
||||
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available to load mesh.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
j = k - (m + 1) * sizeof(z_values);
|
||||
eeprom_read_block((void *)&z_values , (void *)j, sizeof(z_values));
|
||||
|
||||
SERIAL_PROTOCOLPGM("Mesh loaded from slot ");
|
||||
SERIAL_PROTOCOL(m);
|
||||
SERIAL_PROTOCOLPGM(" at offset 0x");
|
||||
prt_hex_word(j);
|
||||
SERIAL_EOL;
|
||||
}
|
||||
|
||||
void bed_leveling:: store_mesh(int m) {
|
||||
int k = E2END - sizeof(state),
|
||||
j = (k - Unified_Bed_Leveling_EEPROM_start) / sizeof(z_values);
|
||||
|
||||
if (m < 0 || m >= j || Unified_Bed_Leveling_EEPROM_start <= 0) {
|
||||
SERIAL_PROTOCOLLNPGM("?EEPROM storage not available to load mesh.\n");
|
||||
SERIAL_PROTOCOL(m);
|
||||
SERIAL_PROTOCOLLNPGM(" mesh slots available.\n");
|
||||
SERIAL_PROTOCOLLNPAIR("E2END : ", E2END);
|
||||
SERIAL_PROTOCOLLNPAIR("k : ", k);
|
||||
SERIAL_PROTOCOLLNPAIR("j : ", j);
|
||||
SERIAL_PROTOCOLLNPAIR("m : ", m);
|
||||
SERIAL_EOL;
|
||||
return;
|
||||
}
|
||||
|
||||
j = k - (m + 1) * sizeof(z_values);
|
||||
eeprom_write_block((const void *)&z_values, (void *)j, sizeof(z_values));
|
||||
|
||||
SERIAL_PROTOCOLPGM("Mesh saved in slot ");
|
||||
SERIAL_PROTOCOL(m);
|
||||
SERIAL_PROTOCOLPGM(" at offset 0x");
|
||||
prt_hex_word(j);
|
||||
SERIAL_EOL;
|
||||
}
|
||||
|
||||
void bed_leveling::reset() {
|
||||
state.active = false;
|
||||
state.z_offset = 0;
|
||||
state.EEPROM_storage_slot = -1;
|
||||
|
||||
ZERO(z_values);
|
||||
|
||||
last_specified_z = -999.9; // We can't pre-initialize these values in the declaration
|
||||
fade_scaling_factor_for_current_height = 0.0; // due to C++11 constraints
|
||||
}
|
||||
|
||||
void bed_leveling::invalidate() {
|
||||
prt_hex_word((unsigned int)this);
|
||||
SERIAL_EOL;
|
||||
|
||||
state.active = false;
|
||||
state.z_offset = 0;
|
||||
for (int x = 0; x < UBL_MESH_NUM_X_POINTS; x++)
|
||||
for (int y = 0; y < UBL_MESH_NUM_Y_POINTS; y++)
|
||||
z_values[x][y] = NAN;
|
||||
}
|
||||
|
||||
void bed_leveling::display_map(int map_type) {
|
||||
float f, current_xi, current_yi;
|
||||
int8_t i, j;
|
||||
UNUSED(map_type);
|
||||
|
||||
SERIAL_PROTOCOLLNPGM("\nBed Topography Report:\n");
|
||||
|
||||
SERIAL_ECHOPAIR("(", 0);
|
||||
SERIAL_ECHOPAIR(", ", UBL_MESH_NUM_Y_POINTS - 1);
|
||||
SERIAL_ECHOPGM(") ");
|
||||
|
||||
current_xi = blm.get_cell_index_x(current_position[X_AXIS] + (MESH_X_DIST) / 2.0);
|
||||
current_yi = blm.get_cell_index_y(current_position[Y_AXIS] + (MESH_Y_DIST) / 2.0);
|
||||
|
||||
for (i = 0; i < UBL_MESH_NUM_X_POINTS - 1; i++)
|
||||
SERIAL_ECHOPGM(" ");
|
||||
|
||||
SERIAL_ECHOPAIR("(", UBL_MESH_NUM_X_POINTS - 1);
|
||||
SERIAL_ECHOPAIR(",", UBL_MESH_NUM_Y_POINTS - 1);
|
||||
SERIAL_ECHOLNPGM(")");
|
||||
|
||||
// if (map_type || 1) {
|
||||
SERIAL_ECHOPAIR("(", UBL_MESH_MIN_X);
|
||||
SERIAL_ECHOPAIR(",", UBL_MESH_MAX_Y);
|
||||
SERIAL_CHAR(')');
|
||||
|
||||
for (i = 0; i < UBL_MESH_NUM_X_POINTS - 1; i++)
|
||||
SERIAL_ECHOPGM(" ");
|
||||
|
||||
SERIAL_ECHOPAIR("(", UBL_MESH_MAX_X);
|
||||
SERIAL_ECHOPAIR(",", UBL_MESH_MAX_Y);
|
||||
SERIAL_ECHOLNPGM(")");
|
||||
// }
|
||||
|
||||
for (j = UBL_MESH_NUM_Y_POINTS - 1; j >= 0; j--) {
|
||||
for (i = 0; i < UBL_MESH_NUM_X_POINTS; i++) {
|
||||
f = z_values[i][j];
|
||||
|
||||
// is the nozzle here? if so, mark the number
|
||||
SERIAL_CHAR(i == current_xi && j == current_yi ? '[' : ' ');
|
||||
|
||||
if (isnan(f))
|
||||
SERIAL_PROTOCOLPGM(" . ");
|
||||
else {
|
||||
// if we don't do this, the columns won't line up nicely
|
||||
if (f >= 0.0) SERIAL_CHAR(' ');
|
||||
SERIAL_PROTOCOL_F(f, 5);
|
||||
idle();
|
||||
}
|
||||
if (i == current_xi && j == current_yi) // is the nozzle here? if so, finish marking the number
|
||||
SERIAL_CHAR(']');
|
||||
else
|
||||
SERIAL_PROTOCOL(" ");
|
||||
|
||||
SERIAL_CHAR(' ');
|
||||
}
|
||||
SERIAL_EOL;
|
||||
if (j) { // we want the (0,0) up tight against the block of numbers
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_EOL;
|
||||
}
|
||||
}
|
||||
|
||||
// if (map_type) {
|
||||
SERIAL_ECHOPAIR("(", int(UBL_MESH_MIN_X));
|
||||
SERIAL_ECHOPAIR(",", int(UBL_MESH_MIN_Y));
|
||||
SERIAL_ECHOPGM(") ");
|
||||
|
||||
for (i = 0; i < UBL_MESH_NUM_X_POINTS - 1; i++)
|
||||
SERIAL_ECHOPGM(" ");
|
||||
|
||||
SERIAL_ECHOPAIR("(", int(UBL_MESH_MAX_X));
|
||||
SERIAL_ECHOPAIR(",", int(UBL_MESH_MIN_Y));
|
||||
SERIAL_CHAR(')');
|
||||
// }
|
||||
|
||||
SERIAL_ECHOPAIR("(", 0);
|
||||
SERIAL_ECHOPAIR(",", 0);
|
||||
SERIAL_ECHOPGM(") ");
|
||||
|
||||
for (i = 0; i < UBL_MESH_NUM_X_POINTS - 1; i++)
|
||||
SERIAL_ECHOPGM(" ");
|
||||
|
||||
SERIAL_ECHOPAIR("(", UBL_MESH_NUM_X_POINTS-1);
|
||||
SERIAL_ECHOPAIR(",", 0);
|
||||
SERIAL_CHAR(')');
|
||||
|
||||
SERIAL_CHAR(' ');
|
||||
SERIAL_EOL;
|
||||
}
|
||||
|
||||
bool bed_leveling::sanity_check() {
|
||||
uint8_t error_flag = 0;
|
||||
|
||||
if (state.n_x != UBL_MESH_NUM_X_POINTS) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_NUM_X_POINTS set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.n_y != UBL_MESH_NUM_Y_POINTS) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_NUM_Y_POINTS set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_x_min != UBL_MESH_MIN_X) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_MIN_X set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_y_min != UBL_MESH_MIN_Y) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_MIN_Y set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_x_max != UBL_MESH_MAX_X) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_MAX_X set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_y_max != UBL_MESH_MAX_Y) {
|
||||
SERIAL_PROTOCOLLNPGM("?UBL_MESH_MAX_Y set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_x_dist != MESH_X_DIST) {
|
||||
SERIAL_PROTOCOLLNPGM("?MESH_X_DIST set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
if (state.mesh_y_dist != MESH_Y_DIST) {
|
||||
SERIAL_PROTOCOLLNPGM("?MESH_Y_DIST set wrong\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
int k = E2END - sizeof(blm.state),
|
||||
j = (k - Unified_Bed_Leveling_EEPROM_start) / sizeof(z_values);
|
||||
|
||||
if (j < 1) {
|
||||
SERIAL_PROTOCOLLNPGM("?No EEPROM storage available for a mesh of this size.\n");
|
||||
error_flag++;
|
||||
}
|
||||
|
||||
// SERIAL_PROTOCOLPGM("?sanity_check() return value: ");
|
||||
// SERIAL_PROTOCOL(error_flag);
|
||||
// SERIAL_EOL;
|
||||
|
||||
return !!error_flag;
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,553 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
#include "Marlin.h"
|
||||
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL)
|
||||
|
||||
#include "UBL.h"
|
||||
#include "planner.h"
|
||||
#include <avr/io.h>
|
||||
#include <math.h>
|
||||
|
||||
extern void set_current_to_destination();
|
||||
extern bool G26_Debug_flag;
|
||||
void debug_current_and_destination(char *title);
|
||||
|
||||
void wait_for_button_press();
|
||||
|
||||
void UBL_line_to_destination(const float &x_end, const float &y_end, const float &z_end, const float &e_end, const float &feed_rate, uint8_t extruder) {
|
||||
|
||||
int cell_start_xi, cell_start_yi, cell_dest_xi, cell_dest_yi;
|
||||
int left_flag, down_flag;
|
||||
int current_xi, current_yi;
|
||||
int dxi, dyi, xi_cnt, yi_cnt;
|
||||
bool use_X_dist, inf_normalized_flag, inf_m_flag;
|
||||
float x_start, y_start;
|
||||
float x, y, z1, z2, z0 /*, z_optimized */;
|
||||
float next_mesh_line_x, next_mesh_line_y, a0ma1diva2ma1;
|
||||
float on_axis_distance, e_normalized_dist, e_position, e_start, z_normalized_dist, z_position, z_start;
|
||||
float dx, dy, adx, ady, m, c;
|
||||
|
||||
//
|
||||
// Much of the nozzle movement will be within the same cell. So we will do as little computation
|
||||
// as possible to determine if this is the case. If this move is within the same cell, we will
|
||||
// just do the required Z-Height correction, call the Planner's buffer_line() routine, and leave
|
||||
//
|
||||
|
||||
x_start = current_position[X_AXIS];
|
||||
y_start = current_position[Y_AXIS];
|
||||
z_start = current_position[Z_AXIS];
|
||||
e_start = current_position[E_AXIS];
|
||||
|
||||
cell_start_xi = blm.get_cell_index_x(x_start);
|
||||
cell_start_yi = blm.get_cell_index_y(y_start);
|
||||
cell_dest_xi = blm.get_cell_index_x(x_end);
|
||||
cell_dest_yi = blm.get_cell_index_y(y_end);
|
||||
|
||||
if (G26_Debug_flag!=0) {
|
||||
SERIAL_ECHOPGM(" UBL_line_to_destination(xe=");
|
||||
SERIAL_ECHO(x_end);
|
||||
SERIAL_ECHOPGM(",ye=");
|
||||
SERIAL_ECHO(y_end);
|
||||
SERIAL_ECHOPGM(",ze=");
|
||||
SERIAL_ECHO(z_end);
|
||||
SERIAL_ECHOPGM(",ee=");
|
||||
SERIAL_ECHO(e_end);
|
||||
SERIAL_ECHOPGM(")\n");
|
||||
debug_current_and_destination( (char *) "Start of UBL_line_to_destination()");
|
||||
}
|
||||
|
||||
if ((cell_start_xi == cell_dest_xi) && (cell_start_yi == cell_dest_yi)) { // if the whole move is within the same cell,
|
||||
// we don't need to break up the move
|
||||
//
|
||||
// If we are moving off the print bed, we are going to allow the move at this level.
|
||||
// But we detect it and isolate it. For now, we just pass along the request.
|
||||
//
|
||||
|
||||
if (cell_dest_xi<0 || cell_dest_yi<0 || cell_dest_xi >= UBL_MESH_NUM_X_POINTS || cell_dest_yi >= UBL_MESH_NUM_Y_POINTS) {
|
||||
|
||||
// Note: There is no Z Correction in this case. We are off the grid and don't know what
|
||||
// a reasonable correction would be.
|
||||
|
||||
planner.buffer_line(x_end, y_end, z_end + blm.state.z_offset, e_end, feed_rate, extruder);
|
||||
set_current_to_destination();
|
||||
if (G26_Debug_flag!=0) {
|
||||
debug_current_and_destination( (char *) "out of bounds in UBL_line_to_destination()");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// we can optimize some floating point operations here. We could call float get_z_correction(float x0, float y0) to
|
||||
// generate the correction for us. But we can lighten the load on the CPU by doing a modified version of the function.
|
||||
// We are going to only calculate the amount we are from the first mesh line towards the second mesh line once.
|
||||
// We will use this fraction in both of the original two Z Height calculations for the bi-linear interpolation. And,
|
||||
// instead of doing a generic divide of the distance, we know the distance is MESH_X_DIST so we can use the preprocessor
|
||||
// to create a 1-over number for us. That will allow us to do a floating point multiply instead of a floating point divide.
|
||||
|
||||
FINAL_MOVE:
|
||||
a0ma1diva2ma1 = (x_end - mesh_index_to_X_location[cell_dest_xi]) * (float) (1.0 / MESH_X_DIST);
|
||||
|
||||
z1 = z_values[cell_dest_xi][cell_dest_yi] +
|
||||
(z_values[cell_dest_xi + 1][cell_dest_yi] - z_values[cell_dest_xi][cell_dest_yi]) * a0ma1diva2ma1;
|
||||
|
||||
z2 = z_values[cell_dest_xi][cell_dest_yi+1] +
|
||||
(z_values[cell_dest_xi+1][cell_dest_yi+1] - z_values[cell_dest_xi][cell_dest_yi+1]) * a0ma1diva2ma1;
|
||||
|
||||
// we are done with the fractional X distance into the cell. Now with the two Z-Heights we have calculated, we
|
||||
// are going to apply the Y-Distance into the cell to interpolate the final Z correction.
|
||||
|
||||
a0ma1diva2ma1 = (y_end - mesh_index_to_Y_location[cell_dest_yi]) * (float) (1.0 / MESH_Y_DIST);
|
||||
|
||||
z0 = z1 + (z2 - z1) * a0ma1diva2ma1;
|
||||
|
||||
// debug code to use non-optimized get_z_correction() and to do a sanity check
|
||||
// that the correct value is being passed to planner.buffer_line()
|
||||
//
|
||||
/*
|
||||
z_optimized = z0;
|
||||
z0 = blm.get_z_correction( x_end, y_end);
|
||||
if ( fabs(z_optimized - z0) > .01 || isnan(z0) || isnan(z_optimized) ) {
|
||||
debug_current_and_destination( (char *) "FINAL_MOVE: z_correction()");
|
||||
if ( isnan(z0) ) SERIAL_ECHO(" z0==NAN ");
|
||||
if ( isnan(z_optimized) ) SERIAL_ECHO(" z_optimized==NAN ");
|
||||
SERIAL_ECHOPAIR(" x_end=", x_end);
|
||||
SERIAL_ECHOPAIR(" y_end=", y_end);
|
||||
SERIAL_ECHOPAIR(" z0=", z0);
|
||||
SERIAL_ECHOPAIR(" z_optimized=", z_optimized);
|
||||
SERIAL_ECHOPAIR(" err=",fabs(z_optimized - z0));
|
||||
SERIAL_EOL;
|
||||
}
|
||||
*/
|
||||
z0 = z0 * blm.fade_scaling_factor_for_Z( z_end );
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
}
|
||||
|
||||
planner.buffer_line(x_end, y_end, z_end + z0 + blm.state.z_offset, e_end, feed_rate, extruder);
|
||||
if (G26_Debug_flag!=0) {
|
||||
debug_current_and_destination( (char *) "FINAL_MOVE in UBL_line_to_destination()");
|
||||
}
|
||||
set_current_to_destination();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// If we get here, we are processing a move that crosses at least one Mesh Line. We will check
|
||||
// for the simple case of just crossing X or just crossing Y Mesh Lines after we get all the details
|
||||
// of the move figured out. We can process the easy case of just crossing an X or Y Mesh Line with less
|
||||
// computation and in fact most lines are of this nature. We will check for that in the following
|
||||
// blocks of code:
|
||||
|
||||
left_flag = 0;
|
||||
down_flag = 0;
|
||||
inf_m_flag = false;
|
||||
inf_normalized_flag = false;
|
||||
|
||||
dx = x_end - x_start;
|
||||
dy = y_end - y_start;
|
||||
|
||||
if (dx<0.0) { // figure out which way we need to move to get to the next cell
|
||||
dxi = -1;
|
||||
adx = -dx; // absolute value of dx. We already need to check if dx and dy are negative.
|
||||
}
|
||||
else { // We may as well generate the appropriate values for adx and ady right now
|
||||
dxi = 1; // to save setting up the abs() function call and actually doing the call.
|
||||
adx = dx;
|
||||
}
|
||||
if (dy<0.0) {
|
||||
dyi = -1;
|
||||
ady = -dy; // absolute value of dy
|
||||
}
|
||||
else {
|
||||
dyi = 1;
|
||||
ady = dy;
|
||||
}
|
||||
|
||||
if (dx<0.0) left_flag = 1;
|
||||
if (dy<0.0) down_flag = 1;
|
||||
if (cell_start_xi == cell_dest_xi) dxi = 0;
|
||||
if (cell_start_yi == cell_dest_yi) dyi = 0;
|
||||
|
||||
//
|
||||
// Compute the scaling factor for the extruder for each partial move.
|
||||
// We need to watch out for zero length moves because it will cause us to
|
||||
// have an infinate scaling factor. We are stuck doing a floating point
|
||||
// divide to get our scaling factor, but after that, we just multiply by this
|
||||
// number. We also pick our scaling factor based on whether the X or Y
|
||||
// component is larger. We use the biggest of the two to preserve precision.
|
||||
//
|
||||
if ( adx > ady ) {
|
||||
use_X_dist = true;
|
||||
on_axis_distance = x_end-x_start;
|
||||
}
|
||||
else {
|
||||
use_X_dist = false;
|
||||
on_axis_distance = y_end-y_start;
|
||||
}
|
||||
e_position = e_end - e_start;
|
||||
e_normalized_dist = e_position / on_axis_distance;
|
||||
|
||||
z_position = z_end - z_start;
|
||||
z_normalized_dist = z_position / on_axis_distance;
|
||||
|
||||
if (e_normalized_dist==INFINITY || e_normalized_dist==-INFINITY) {
|
||||
inf_normalized_flag = true;
|
||||
}
|
||||
current_xi = cell_start_xi;
|
||||
current_yi = cell_start_yi;
|
||||
|
||||
m = dy / dx;
|
||||
c = y_start - m*x_start;
|
||||
if (m == INFINITY || m == -INFINITY) {
|
||||
inf_m_flag = true;
|
||||
}
|
||||
//
|
||||
// This block handles vertical lines. These are lines that stay within the same
|
||||
// X Cell column. They do not need to be perfectly vertical. They just can
|
||||
// not cross into another X Cell column.
|
||||
//
|
||||
if (dxi == 0) { // Check for a vertical line
|
||||
current_yi += down_flag; // Line is heading down, we just want to go to the bottom
|
||||
while (current_yi != cell_dest_yi + down_flag) {
|
||||
current_yi += dyi;
|
||||
next_mesh_line_y = mesh_index_to_Y_location[current_yi];
|
||||
if (inf_m_flag) {
|
||||
x = x_start; // if the slope of the line is infinite, we won't do the calculations
|
||||
}
|
||||
// we know the next X is the same so we can recover and continue!
|
||||
else {
|
||||
x = (next_mesh_line_y - c) / m; // Calculate X at the next Y mesh line
|
||||
}
|
||||
|
||||
z0 = blm.get_z_correction_along_horizontal_mesh_line_at_specific_X(x, current_xi, current_yi);
|
||||
|
||||
//
|
||||
// debug code to use non-optimized get_z_correction() and to do a sanity check
|
||||
// that the correct value is being passed to planner.buffer_line()
|
||||
//
|
||||
/*
|
||||
z_optimized = z0;
|
||||
z0 = blm.get_z_correction( x, next_mesh_line_y);
|
||||
if ( fabs(z_optimized - z0) > .01 || isnan(z0) || isnan(z_optimized) ) {
|
||||
debug_current_and_destination( (char *) "VERTICAL z_correction()");
|
||||
if ( isnan(z0) ) SERIAL_ECHO(" z0==NAN ");
|
||||
if ( isnan(z_optimized) ) SERIAL_ECHO(" z_optimized==NAN ");
|
||||
SERIAL_ECHOPAIR(" x=", x);
|
||||
SERIAL_ECHOPAIR(" next_mesh_line_y=", next_mesh_line_y);
|
||||
SERIAL_ECHOPAIR(" z0=", z0);
|
||||
SERIAL_ECHOPAIR(" z_optimized=", z_optimized);
|
||||
SERIAL_ECHOPAIR(" err=",fabs(z_optimized-z0));
|
||||
SERIAL_ECHO("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
z0 = z0 * blm.fade_scaling_factor_for_Z( z_end );
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
}
|
||||
y = mesh_index_to_Y_location[current_yi];
|
||||
|
||||
// Without this check, it is possible for the algorythm to generate a zero length move in the case
|
||||
// where the line is heading down and it is starting right on a Mesh Line boundary. For how often that
|
||||
// happens, it might be best to remove the check and always 'schedule' the move because
|
||||
// the planner.buffer_line() routine will filter it if that happens.
|
||||
if ( y!=y_start) {
|
||||
if ( inf_normalized_flag == false ) {
|
||||
on_axis_distance = y - y_start; // we don't need to check if the extruder position
|
||||
e_position = e_start + on_axis_distance * e_normalized_dist; // is based on X or Y because this is a vertical move
|
||||
z_position = z_start + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = e_start;
|
||||
z_position = z_start;
|
||||
}
|
||||
|
||||
planner.buffer_line(x, y, z_position + z0 + blm.state.z_offset, e_position, feed_rate, extruder);
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
//
|
||||
// Check if we are at the final destination. Usually, we won't be, but if it is on a Y Mesh Line, we are done.
|
||||
//
|
||||
if (G26_Debug_flag!=0) {
|
||||
debug_current_and_destination( (char *) "vertical move done in UBL_line_to_destination()");
|
||||
}
|
||||
if (current_position[X_AXIS] != x_end || current_position[Y_AXIS] != y_end) {
|
||||
goto FINAL_MOVE;
|
||||
}
|
||||
set_current_to_destination();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
// This block handles horizontal lines. These are lines that stay within the same
|
||||
// Y Cell row. They do not need to be perfectly horizontal. They just can
|
||||
// not cross into another Y Cell row.
|
||||
//
|
||||
|
||||
if (dyi == 0) { // Check for a horiziontal line
|
||||
current_xi += left_flag; // Line is heading left, we just want to go to the left
|
||||
// edge of this cell for the first move.
|
||||
while (current_xi != cell_dest_xi + left_flag) {
|
||||
current_xi += dxi;
|
||||
next_mesh_line_x = mesh_index_to_X_location[current_xi];
|
||||
y = m * next_mesh_line_x + c; // Calculate X at the next Y mesh line
|
||||
|
||||
z0 = blm.get_z_correction_along_vertical_mesh_line_at_specific_Y(y, current_xi, current_yi);
|
||||
|
||||
//
|
||||
// debug code to use non-optimized get_z_correction() and to do a sanity check
|
||||
// that the correct value is being passed to planner.buffer_line()
|
||||
//
|
||||
/*
|
||||
z_optimized = z0;
|
||||
z0 = blm.get_z_correction( next_mesh_line_x, y);
|
||||
if ( fabs(z_optimized - z0) > .01 || isnan(z0) || isnan(z_optimized) ) {
|
||||
debug_current_and_destination( (char *) "HORIZONTAL z_correction()");
|
||||
if ( isnan(z0) ) SERIAL_ECHO(" z0==NAN ");
|
||||
if ( isnan(z_optimized) ) SERIAL_ECHO(" z_optimized==NAN ");
|
||||
SERIAL_ECHOPAIR(" next_mesh_line_x=", next_mesh_line_x);
|
||||
SERIAL_ECHOPAIR(" y=", y);
|
||||
SERIAL_ECHOPAIR(" z0=", z0);
|
||||
SERIAL_ECHOPAIR(" z_optimized=", z_optimized);
|
||||
SERIAL_ECHOPAIR(" err=",fabs(z_optimized-z0));
|
||||
SERIAL_ECHO("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
z0 = z0 * blm.fade_scaling_factor_for_Z( z_end );
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
}
|
||||
x = mesh_index_to_X_location[current_xi];
|
||||
|
||||
// Without this check, it is possible for the algorythm to generate a zero length move in the case
|
||||
// where the line is heading left and it is starting right on a Mesh Line boundary. For how often
|
||||
// that happens, it might be best to remove the check and always 'schedule' the move because
|
||||
// the planner.buffer_line() routine will filter it if that happens.
|
||||
if ( x!=x_start) {
|
||||
if ( inf_normalized_flag == false ) {
|
||||
on_axis_distance = x - x_start; // we don't need to check if the extruder position
|
||||
e_position = e_start + on_axis_distance * e_normalized_dist; // is based on X or Y because this is a horizontal move
|
||||
z_position = z_start + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = e_start;
|
||||
z_position = z_start;
|
||||
}
|
||||
|
||||
planner.buffer_line(x, y, z_position + z0 + blm.state.z_offset, e_position, feed_rate, extruder);
|
||||
} //else printf("FIRST MOVE PRUNED ");
|
||||
}
|
||||
if (G26_Debug_flag!=0) {
|
||||
debug_current_and_destination( (char *) "horizontal move done in UBL_line_to_destination()");
|
||||
}
|
||||
if (current_position[X_AXIS] != x_end || current_position[Y_AXIS] != y_end) {
|
||||
goto FINAL_MOVE;
|
||||
}
|
||||
set_current_to_destination();
|
||||
return;
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
// This block handles the generic case of a line crossing both X and Y
|
||||
// Mesh lines.
|
||||
//
|
||||
//
|
||||
//
|
||||
//
|
||||
|
||||
xi_cnt = cell_start_xi - cell_dest_xi;
|
||||
if ( xi_cnt < 0 ) {
|
||||
xi_cnt = -xi_cnt;
|
||||
}
|
||||
|
||||
yi_cnt = cell_start_yi - cell_dest_yi;
|
||||
if ( yi_cnt < 0 ) {
|
||||
yi_cnt = -yi_cnt;
|
||||
}
|
||||
|
||||
current_xi += left_flag;
|
||||
current_yi += down_flag;
|
||||
|
||||
while ( xi_cnt>0 || yi_cnt>0 ) {
|
||||
|
||||
next_mesh_line_x = mesh_index_to_X_location[current_xi + dxi];
|
||||
next_mesh_line_y = mesh_index_to_Y_location[current_yi + dyi];
|
||||
|
||||
y = m * next_mesh_line_x + c; // Calculate Y at the next X mesh line
|
||||
x = (next_mesh_line_y-c) / m; // Calculate X at the next Y mesh line (we don't have to worry
|
||||
// about m being equal to 0.0 If this was the case, we would have
|
||||
// detected this as a vertical line move up above and we wouldn't
|
||||
// be down here doing a generic type of move.
|
||||
|
||||
if ((left_flag && (x>next_mesh_line_x)) || (!left_flag && (x<next_mesh_line_x))) { // Check if we hit the Y line first
|
||||
//
|
||||
// Yes! Crossing a Y Mesh Line next
|
||||
//
|
||||
z0 = blm.get_z_correction_along_horizontal_mesh_line_at_specific_X(x, current_xi-left_flag, current_yi+dyi);
|
||||
|
||||
//
|
||||
// debug code to use non-optimized get_z_correction() and to do a sanity check
|
||||
// that the correct value is being passed to planner.buffer_line()
|
||||
//
|
||||
|
||||
/*
|
||||
|
||||
z_optimized = z0;
|
||||
|
||||
z0 = blm.get_z_correction( x, next_mesh_line_y);
|
||||
if ( fabs(z_optimized - z0) > .01 || isnan(z0) || isnan(z_optimized) ) {
|
||||
debug_current_and_destination( (char *) "General_1: z_correction()");
|
||||
if ( isnan(z0) ) SERIAL_ECHO(" z0==NAN ");
|
||||
if ( isnan(z_optimized) ) SERIAL_ECHO(" z_optimized==NAN "); {
|
||||
SERIAL_ECHOPAIR(" x=", x);
|
||||
}
|
||||
SERIAL_ECHOPAIR(" next_mesh_line_y=", next_mesh_line_y);
|
||||
SERIAL_ECHOPAIR(" z0=", z0);
|
||||
SERIAL_ECHOPAIR(" z_optimized=", z_optimized);
|
||||
SERIAL_ECHOPAIR(" err=",fabs(z_optimized-z0));
|
||||
SERIAL_ECHO("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
z0 = z0 * blm.fade_scaling_factor_for_Z( z_end );
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
}
|
||||
|
||||
if ( inf_normalized_flag == false ) {
|
||||
if ( use_X_dist ) {
|
||||
on_axis_distance = x - x_start;
|
||||
}
|
||||
else {
|
||||
on_axis_distance = next_mesh_line_y - y_start;
|
||||
}
|
||||
e_position = e_start + on_axis_distance * e_normalized_dist;
|
||||
z_position = z_start + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = e_start;
|
||||
z_position = z_start;
|
||||
}
|
||||
planner.buffer_line(x, next_mesh_line_y, z_position + z0 + blm.state.z_offset, e_position, feed_rate, extruder);
|
||||
current_yi += dyi;
|
||||
yi_cnt--;
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Yes! Crossing a X Mesh Line next
|
||||
//
|
||||
z0 = blm.get_z_correction_along_vertical_mesh_line_at_specific_Y(y, current_xi+dxi, current_yi-down_flag);
|
||||
|
||||
|
||||
//
|
||||
// debug code to use non-optimized get_z_correction() and to do a sanity check
|
||||
// that the correct value is being passed to planner.buffer_line()
|
||||
//
|
||||
/*
|
||||
z_optimized = z0;
|
||||
z0 = blm.get_z_correction( next_mesh_line_x, y);
|
||||
if ( fabs(z_optimized - z0) > .01 || isnan(z0) || isnan(z_optimized) ) {
|
||||
debug_current_and_destination( (char *) "General_2: z_correction()");
|
||||
if ( isnan(z0) ) SERIAL_ECHO(" z0==NAN ");
|
||||
if ( isnan(z_optimized) ) SERIAL_ECHO(" z_optimized==NAN ");
|
||||
SERIAL_ECHOPAIR(" next_mesh_line_x=", next_mesh_line_x);
|
||||
SERIAL_ECHOPAIR(" y=", y);
|
||||
SERIAL_ECHOPAIR(" z0=", z0);
|
||||
SERIAL_ECHOPAIR(" z_optimized=", z_optimized);
|
||||
SERIAL_ECHOPAIR(" err=",fabs(z_optimized-z0));
|
||||
SERIAL_ECHO("\n");
|
||||
}
|
||||
*/
|
||||
|
||||
z0 = z0 * blm.fade_scaling_factor_for_Z( z_end );
|
||||
|
||||
if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
|
||||
z0 = 0.0; // in z_values[][] and propagate through the
|
||||
// calculations. If our correction is NAN, we throw it out
|
||||
// because part of the Mesh is undefined and we don't have the
|
||||
// information we need to complete the height correction.
|
||||
}
|
||||
if ( inf_normalized_flag == false ) {
|
||||
if ( use_X_dist ) {
|
||||
on_axis_distance = next_mesh_line_x - x_start;
|
||||
}
|
||||
else {
|
||||
on_axis_distance = y - y_start;
|
||||
}
|
||||
e_position = e_start + on_axis_distance * e_normalized_dist;
|
||||
z_position = z_start + on_axis_distance * z_normalized_dist;
|
||||
}
|
||||
else {
|
||||
e_position = e_start;
|
||||
z_position = z_start;
|
||||
}
|
||||
|
||||
planner.buffer_line(next_mesh_line_x, y, z_position + z0 + blm.state.z_offset, e_position, feed_rate, extruder);
|
||||
current_xi += dxi;
|
||||
xi_cnt--;
|
||||
}
|
||||
}
|
||||
if (G26_Debug_flag) {
|
||||
debug_current_and_destination( (char *) "generic move done in UBL_line_to_destination()");
|
||||
}
|
||||
if (current_position[0] != x_end || current_position[1] != y_end) {
|
||||
goto FINAL_MOVE;
|
||||
}
|
||||
set_current_to_destination();
|
||||
return;
|
||||
}
|
||||
|
||||
void wait_for_button_press() {
|
||||
// if ( !been_to_2_6 )
|
||||
//return; // bob - I think this should be commented out
|
||||
|
||||
SET_INPUT_PULLUP(66); // Roxy's Left Switch is on pin 66. Right Switch is on pin 65
|
||||
SET_OUTPUT(64);
|
||||
while (READ(66) & 0x01) idle();
|
||||
|
||||
delay(50);
|
||||
while (!(READ(66) & 0x01)) idle();
|
||||
delay(50);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#include "Marlin.h"
|
||||
#if ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(M100_FREE_MEMORY_WATCHER)
|
||||
|
||||
#include "hex_print_routines.h"
|
||||
|
||||
void prt_hex_nibble(uint8_t n) {
|
||||
if (n <= 9)
|
||||
SERIAL_ECHO(n);
|
||||
else
|
||||
SERIAL_ECHO((char)('A' + n - 10));
|
||||
delay(3);
|
||||
}
|
||||
|
||||
void prt_hex_byte(uint8_t b) {
|
||||
prt_hex_nibble((b & 0xF0) >> 4);
|
||||
prt_hex_nibble(b & 0x0F);
|
||||
}
|
||||
|
||||
void prt_hex_word(uint16_t w) {
|
||||
prt_hex_byte((w & 0xFF00) >> 8);
|
||||
prt_hex_byte(w & 0x0FF);
|
||||
}
|
||||
|
||||
#endif // AUTO_BED_LEVELING_UBL || M100_FREE_MEMORY_WATCHER
|
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef HEX_PRINT_ROUTINES_H
|
||||
#define HEX_PRINT_ROUTINES_H
|
||||
|
||||
//
|
||||
// 3 support routines to print hex numbers. We can print a nibble, byte and word
|
||||
//
|
||||
void prt_hex_nibble(uint8_t n);
|
||||
void prt_hex_byte(uint8_t b);
|
||||
void prt_hex_word(uint16_t w);
|
||||
|
||||
#endif // HEX_PRINT_ROUTINES_H
|
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Marlin 3D Printer Firmware
|
||||
* Copyright (C) 2016 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
|
||||
*
|
||||
* Based on Sprinter and grbl.
|
||||
* Copyright (C) 2011 Camiel Gubbels / Erik van der Zalm
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// Generic Silicon Heat Pad with NTC 100K thermistor ( Beta 25/50 3950K)
|
||||
//
|
||||
// Many of the generic silicon heat pads use the MGB18-104F39050L32 Thermistor It is used for various
|
||||
// wattage and voltage heat pads. This table is correct if this part is used. It has been
|
||||
// optimized to provide good granularity around the 60 C. and 110 C. which corrisponds to bed temperatures
|
||||
// for PLA and ABS. If you are printing higher temperature filament such as nylon you can uncomment
|
||||
// the higher earlier entries in the table to give better accuracy. But for speed reasons, if these
|
||||
// temperatures are not going to be used, it is better to leave them commented out.
|
||||
|
||||
const short temptable_75[][2] PROGMEM = { // Generic Silicon Heat Pad with NTC 100K MGB18-104F39050L32 thermistor
|
||||
{ (short) ( 111.06 * OVERSAMPLENR ), 200 }, // v=0.542 r=571.747 res=0.501 degC/count
|
||||
// { (short) ( 174.87 * OVERSAMPLENR ), 175 }, // v=0.854 r=967.950 res=0.311 degC/count These values are valid. But they serve no
|
||||
// { (short) ( 191.64 * OVERSAMPLENR ), 170 }, // v=0.936 r=1082.139 res=0.284 degC/count purpose. It is better to delete them so
|
||||
// { (short) ( 209.99 * OVERSAMPLENR ), 165 }, // v=1.025 r=1212.472 res=0.260 degC/count the search is quicker and get to the meaningful
|
||||
// { (short) ( 230.02 * OVERSAMPLENR ), 160 }, // v=1.123 r=1361.590 res=0.239 degC/count part of the table sooner.
|
||||
// { (short) ( 251.80 * OVERSAMPLENR ), 155 }, // v=1.230 r=1532.621 res=0.220 degC/count
|
||||
{ (short) ( 275.43 * OVERSAMPLENR ), 150 }, // v=1.345 r=1729.283 res=0.203 degC/count
|
||||
// { (short) ( 300.92 * OVERSAMPLENR ), 145 }, // v=1.469 r=1956.004 res=0.189 degC/coun
|
||||
{ (short) ( 328.32 * OVERSAMPLENR ), 140 }, // v=1.603 r=2218.081 res=0.176 degC/count
|
||||
{ (short) ( 388.65 * OVERSAMPLENR ), 130 }, // v=1.898 r=2874.980 res=0.156 degC/count
|
||||
{ (short) ( 421.39 * OVERSAMPLENR ), 125 }, // v=2.058 r=3286.644 res=0.149 degC/count
|
||||
{ (short) ( 455.65 * OVERSAMPLENR ), 120 }, // v=2.225 r=3768.002 res=0.143 degC/count
|
||||
{ (short) ( 491.17 * OVERSAMPLENR ), 115 }, // v=2.398 r=4332.590 res=0.139 degC/count
|
||||
{ (short) ( 527.68 * OVERSAMPLENR ), 110 }, // v=2.577 r=4996.905 res=0.136 degC/count
|
||||
{ (short) ( 564.81 * OVERSAMPLENR ), 105 }, // v=2.758 r=5781.120 res=0.134 degC/count
|
||||
{ (short) ( 602.19 * OVERSAMPLENR ), 100 }, // v=2.940 r=6710.000 res=0.134 degC/count
|
||||
{ (short) ( 676.03 * OVERSAMPLENR ), 90 }, // v=3.301 r=9131.018 res=0.138 degC/count
|
||||
{ (short) ( 745.85 * OVERSAMPLENR ), 80 }, // v=3.642 r=12602.693 res=0.150 degC/count
|
||||
{ (short) ( 778.31 * OVERSAMPLENR ), 75 }, // v=3.800 r=14889.001 res=0.159 degC/count
|
||||
{ (short) ( 808.75 * OVERSAMPLENR ), 70 }, // v=3.949 r=17658.700 res=0.171 degC/count
|
||||
{ (short) ( 836.94 * OVERSAMPLENR ), 65 }, // v=4.087 r=21028.040 res=0.185 degC/count
|
||||
{ (short) ( 862.74 * OVERSAMPLENR ), 60 }, // v=4.213 r=25144.568 res=0.204 degC/count
|
||||
{ (short) ( 886.08 * OVERSAMPLENR ), 55 }, // v=4.327 r=30196.449 res=0.227 degC/count
|
||||
{ (short) ( 906.97 * OVERSAMPLENR ), 50 }, // v=4.429 r=36424.838 res=0.255 degC/count
|
||||
{ (short) ( 941.65 * OVERSAMPLENR ), 40 }, // v=4.598 r=53745.337 res=0.333 degC/count
|
||||
{ (short) ( 967.76 * OVERSAMPLENR ), 30 }, // v=4.725 r=80880.630 res=0.452 degC/count
|
||||
{ (short) ( 978.03 * OVERSAMPLENR ), 25 }, // v=4.776 r=100000.000 res=0.535 degC/count
|
||||
{ (short) ( 981.68 * OVERSAMPLENR ), 23 }, // v=4.793 r=109024.395 res=0.573 degC/count
|
||||
{ (short) ( 983.41 * OVERSAMPLENR ), 22 }, // v=4.802 r=113875.430 res=0.594 degC/count
|
||||
{ (short) ( 985.08 * OVERSAMPLENR ), 21 }, // v=4.810 r=118968.955 res=0.616 degC/count
|
||||
{ (short) ( 986.70 * OVERSAMPLENR ), 20 }, // v=4.818 r=124318.354 res=0.638 degC/count
|
||||
{ (short) ( 993.94 * OVERSAMPLENR ), 15 }, // v=4.853 r=155431.302 res=0.768 degC/count
|
||||
{ (short) ( 999.96 * OVERSAMPLENR ), 10 }, // v=4.883 r=195480.023 res=0.934 degC/count
|
||||
{ (short) (1008.95 * OVERSAMPLENR ), 0 } // v=4.926 r=314997.575 res=1.418 degC/count
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue