My Marlin configs for Fabrikator Mini and CTC i3 Pro B
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

ubl.h 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /**
  2. * Marlin 3D Printer Firmware
  3. * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
  4. *
  5. * Based on Sprinter and grbl.
  6. * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
  7. *
  8. * This program is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  20. *
  21. */
  22. #pragma once
  23. //#define UBL_DEVEL_DEBUGGING
  24. #include "../../../module/motion.h"
  25. #define DEBUG_OUT ENABLED(DEBUG_LEVELING_FEATURE)
  26. #include "../../../core/debug_out.h"
  27. #define UBL_VERSION "1.01"
  28. #define UBL_OK false
  29. #define UBL_ERR true
  30. enum MeshPointType : char { INVALID, REAL, SET_IN_BITMAP, CLOSEST };
  31. // External references
  32. struct mesh_index_pair;
  33. #define MESH_X_DIST (float(MESH_MAX_X - (MESH_MIN_X)) / (GRID_MAX_CELLS_X))
  34. #define MESH_Y_DIST (float(MESH_MAX_Y - (MESH_MIN_Y)) / (GRID_MAX_CELLS_Y))
  35. #if ENABLED(OPTIMIZED_MESH_STORAGE)
  36. typedef int16_t mesh_store_t[GRID_MAX_POINTS_X][GRID_MAX_POINTS_Y];
  37. #endif
  38. typedef struct {
  39. bool C_seen;
  40. int8_t KLS_storage_slot;
  41. uint8_t R_repetition,
  42. V_verbosity,
  43. P_phase,
  44. T_map_type;
  45. float B_shim_thickness,
  46. C_constant;
  47. xy_pos_t XY_pos;
  48. xy_bool_t XY_seen;
  49. #if HAS_BED_PROBE
  50. uint8_t J_grid_size;
  51. #endif
  52. } G29_parameters_t;
  53. class unified_bed_leveling {
  54. private:
  55. static G29_parameters_t param;
  56. #if IS_NEWPANEL
  57. static void move_z_with_encoder(const_float_t multiplier);
  58. static float measure_point_with_encoder();
  59. static float measure_business_card_thickness();
  60. static void manually_probe_remaining_mesh(const xy_pos_t&, const_float_t , const_float_t , const bool) _O0;
  61. static void fine_tune_mesh(const xy_pos_t &pos, const bool do_ubl_mesh_map) _O0;
  62. #endif
  63. static bool G29_parse_parameters() _O0;
  64. static void shift_mesh_height();
  65. static void probe_entire_mesh(const xy_pos_t &near, const bool do_ubl_mesh_map, const bool stow_probe, const bool do_furthest) _O0;
  66. static void tilt_mesh_based_on_3pts(const_float_t z1, const_float_t z2, const_float_t z3);
  67. static void tilt_mesh_based_on_probed_grid(const bool do_ubl_mesh_map);
  68. static bool smart_fill_one(const uint8_t x, const uint8_t y, const int8_t xdir, const int8_t ydir);
  69. static bool smart_fill_one(const xy_uint8_t &pos, const xy_uint8_t &dir) {
  70. return smart_fill_one(pos.x, pos.y, dir.x, dir.y);
  71. }
  72. static void smart_fill_mesh();
  73. #if ENABLED(UBL_DEVEL_DEBUGGING)
  74. static void g29_what_command();
  75. static void g29_eeprom_dump();
  76. static void g29_compare_current_mesh_to_stored_mesh();
  77. #endif
  78. public:
  79. static void echo_name();
  80. static void report_current_mesh();
  81. static void report_state();
  82. static void save_ubl_active_state_and_disable();
  83. static void restore_ubl_active_state_and_leave();
  84. static void display_map(const uint8_t) _O0;
  85. static mesh_index_pair find_closest_mesh_point_of_type(const MeshPointType, const xy_pos_t&, const bool=false, MeshFlags *done_flags=nullptr) _O0;
  86. static mesh_index_pair find_furthest_invalid_mesh_point() _O0;
  87. static void reset();
  88. static void invalidate();
  89. static void set_all_mesh_points_to_value(const_float_t value);
  90. static void adjust_mesh_to_mean(const bool cflag, const_float_t value);
  91. static bool sanity_check();
  92. static void G29() _O0; // O0 for no optimization
  93. static void smart_fill_wlsf(const_float_t ) _O2; // O2 gives smaller code than Os on A2560
  94. static int8_t storage_slot;
  95. static bed_mesh_t z_values;
  96. #if ENABLED(OPTIMIZED_MESH_STORAGE)
  97. static void set_store_from_mesh(const bed_mesh_t &in_values, mesh_store_t &stored_values);
  98. static void set_mesh_from_store(const mesh_store_t &stored_values, bed_mesh_t &out_values);
  99. #endif
  100. static const float _mesh_index_to_xpos[GRID_MAX_POINTS_X],
  101. _mesh_index_to_ypos[GRID_MAX_POINTS_Y];
  102. #if HAS_MARLINUI_MENU
  103. static bool lcd_map_control;
  104. static void steppers_were_disabled();
  105. #else
  106. static void steppers_were_disabled() {}
  107. #endif
  108. static volatile int16_t encoder_diff; // Volatile because buttons may change it at interrupt time
  109. unified_bed_leveling();
  110. FORCE_INLINE static void set_z(const int8_t px, const int8_t py, const_float_t z) { z_values[px][py] = z; }
  111. static int8_t cell_index_x_raw(const_float_t x) {
  112. return FLOOR((x - (MESH_MIN_X)) * RECIPROCAL(MESH_X_DIST));
  113. }
  114. static int8_t cell_index_y_raw(const_float_t y) {
  115. return FLOOR((y - (MESH_MIN_Y)) * RECIPROCAL(MESH_Y_DIST));
  116. }
  117. static int8_t cell_index_x_valid(const_float_t x) {
  118. return WITHIN(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
  119. }
  120. static int8_t cell_index_y_valid(const_float_t y) {
  121. return WITHIN(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
  122. }
  123. static int8_t cell_index_x(const_float_t x) {
  124. return constrain(cell_index_x_raw(x), 0, GRID_MAX_CELLS_X - 1);
  125. }
  126. static int8_t cell_index_y(const_float_t y) {
  127. return constrain(cell_index_y_raw(y), 0, GRID_MAX_CELLS_Y - 1);
  128. }
  129. static xy_int8_t cell_indexes(const_float_t x, const_float_t y) {
  130. return { cell_index_x(x), cell_index_y(y) };
  131. }
  132. static xy_int8_t cell_indexes(const xy_pos_t &xy) { return cell_indexes(xy.x, xy.y); }
  133. static int8_t closest_x_index(const_float_t x) {
  134. const int8_t px = (x - (MESH_MIN_X) + (MESH_X_DIST) * 0.5) * RECIPROCAL(MESH_X_DIST);
  135. return WITHIN(px, 0, (GRID_MAX_POINTS_X) - 1) ? px : -1;
  136. }
  137. static int8_t closest_y_index(const_float_t y) {
  138. const int8_t py = (y - (MESH_MIN_Y) + (MESH_Y_DIST) * 0.5) * RECIPROCAL(MESH_Y_DIST);
  139. return WITHIN(py, 0, (GRID_MAX_POINTS_Y) - 1) ? py : -1;
  140. }
  141. static xy_int8_t closest_indexes(const xy_pos_t &xy) {
  142. return { closest_x_index(xy.x), closest_y_index(xy.y) };
  143. }
  144. /**
  145. * z2 --|
  146. * z0 | |
  147. * | | + (z2-z1)
  148. * z1 | | |
  149. * ---+-------------+--------+-- --|
  150. * a1 a0 a2
  151. * |<---delta_a---------->|
  152. *
  153. * calc_z0 is the basis for all the Mesh Based correction. It is used to
  154. * find the expected Z Height at a position between two known Z-Height locations.
  155. *
  156. * It is fairly expensive with its 4 floating point additions and 2 floating point
  157. * multiplications.
  158. */
  159. FORCE_INLINE static float calc_z0(const_float_t a0, const_float_t a1, const_float_t z1, const_float_t a2, const_float_t z2) {
  160. return z1 + (z2 - z1) * (a0 - a1) / (a2 - a1);
  161. }
  162. #ifdef UBL_Z_RAISE_WHEN_OFF_MESH
  163. #define _UBL_OUTER_Z_RAISE UBL_Z_RAISE_WHEN_OFF_MESH
  164. #else
  165. #define _UBL_OUTER_Z_RAISE NAN
  166. #endif
  167. /**
  168. * z_correction_for_x_on_horizontal_mesh_line is an optimization for
  169. * the case where the printer is making a vertical line that only crosses horizontal mesh lines.
  170. */
  171. static float z_correction_for_x_on_horizontal_mesh_line(const_float_t rx0, const int x1_i, const int yi) {
  172. if (!WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(yi, 0, (GRID_MAX_POINTS_Y) - 1)) {
  173. if (DEBUGGING(LEVELING)) {
  174. if (WITHIN(x1_i, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("yi"); else DEBUG_ECHOPGM("x1_i");
  175. DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_x_on_horizontal_mesh_line(rx0=", rx0, ",x1_i=", x1_i, ",yi=", yi, ")");
  176. }
  177. // The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
  178. return _UBL_OUTER_Z_RAISE;
  179. }
  180. const float xratio = (rx0 - mesh_index_to_xpos(x1_i)) * RECIPROCAL(MESH_X_DIST),
  181. z1 = z_values[x1_i][yi];
  182. return z1 + xratio * (z_values[_MIN(x1_i, (GRID_MAX_POINTS_X) - 2) + 1][yi] - z1); // Don't allow x1_i+1 to be past the end of the array
  183. // If it is, it is clamped to the last element of the
  184. // z_values[][] array and no correction is applied.
  185. }
  186. //
  187. // See comments above for z_correction_for_x_on_horizontal_mesh_line
  188. //
  189. static float z_correction_for_y_on_vertical_mesh_line(const_float_t ry0, const int xi, const int y1_i) {
  190. if (!WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1) || !WITHIN(y1_i, 0, (GRID_MAX_POINTS_Y) - 1)) {
  191. if (DEBUGGING(LEVELING)) {
  192. if (WITHIN(xi, 0, (GRID_MAX_POINTS_X) - 1)) DEBUG_ECHOPGM("y1_i"); else DEBUG_ECHOPGM("xi");
  193. DEBUG_ECHOLNPGM(" out of bounds in z_correction_for_y_on_vertical_mesh_line(ry0=", ry0, ", xi=", xi, ", y1_i=", y1_i, ")");
  194. }
  195. // The requested location is off the mesh. Return UBL_Z_RAISE_WHEN_OFF_MESH or NAN.
  196. return _UBL_OUTER_Z_RAISE;
  197. }
  198. const float yratio = (ry0 - mesh_index_to_ypos(y1_i)) * RECIPROCAL(MESH_Y_DIST),
  199. z1 = z_values[xi][y1_i];
  200. return z1 + yratio * (z_values[xi][_MIN(y1_i, (GRID_MAX_POINTS_Y) - 2) + 1] - z1); // Don't allow y1_i+1 to be past the end of the array
  201. // If it is, it is clamped to the last element of the
  202. // z_values[][] array and no correction is applied.
  203. }
  204. /**
  205. * This is the generic Z-Correction. It works anywhere within a Mesh Cell. It first
  206. * does a linear interpolation along both of the bounding X-Mesh-Lines to find the
  207. * Z-Height at both ends. Then it does a linear interpolation of these heights based
  208. * on the Y position within the cell.
  209. */
  210. static float get_z_correction(const_float_t rx0, const_float_t ry0) {
  211. const int8_t cx = cell_index_x(rx0), cy = cell_index_y(ry0); // return values are clamped
  212. /**
  213. * Check if the requested location is off the mesh. If so, and
  214. * UBL_Z_RAISE_WHEN_OFF_MESH is specified, that value is returned.
  215. */
  216. #ifdef UBL_Z_RAISE_WHEN_OFF_MESH
  217. if (!WITHIN(rx0, MESH_MIN_X, MESH_MAX_X) || !WITHIN(ry0, MESH_MIN_Y, MESH_MAX_Y))
  218. return UBL_Z_RAISE_WHEN_OFF_MESH;
  219. #endif
  220. const uint8_t mx = _MIN(cx, (GRID_MAX_POINTS_X) - 2) + 1, my = _MIN(cy, (GRID_MAX_POINTS_Y) - 2) + 1;
  221. const float z1 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][cy], mesh_index_to_xpos(cx + 1), z_values[mx][cy]);
  222. const float z2 = calc_z0(rx0, mesh_index_to_xpos(cx), z_values[cx][my], mesh_index_to_xpos(cx + 1), z_values[mx][my]);
  223. float z0 = calc_z0(ry0, mesh_index_to_ypos(cy), z1, mesh_index_to_ypos(cy + 1), z2);
  224. if (isnan(z0)) { // if part of the Mesh is undefined, it will show up as NAN
  225. z0 = 0.0; // in ubl.z_values[][] and propagate through the
  226. // calculations. If our correction is NAN, we throw it out
  227. // because part of the Mesh is undefined and we don't have the
  228. // information we need to complete the height correction.
  229. if (DEBUGGING(MESH_ADJUST)) DEBUG_ECHOLNPGM("??? Yikes! NAN in ");
  230. }
  231. if (DEBUGGING(MESH_ADJUST)) {
  232. DEBUG_ECHOPGM("get_z_correction(", rx0, ", ", ry0);
  233. DEBUG_ECHOLNPAIR_F(") => ", z0, 6);
  234. }
  235. return z0;
  236. }
  237. static float get_z_correction(const xy_pos_t &pos) { return get_z_correction(pos.x, pos.y); }
  238. static float mesh_index_to_xpos(const uint8_t i) {
  239. return i < (GRID_MAX_POINTS_X) ? pgm_read_float(&_mesh_index_to_xpos[i]) : MESH_MIN_X + i * (MESH_X_DIST);
  240. }
  241. static float mesh_index_to_ypos(const uint8_t i) {
  242. return i < (GRID_MAX_POINTS_Y) ? pgm_read_float(&_mesh_index_to_ypos[i]) : MESH_MIN_Y + i * (MESH_Y_DIST);
  243. }
  244. #if UBL_SEGMENTED
  245. static bool line_to_destination_segmented(const_feedRate_t scaled_fr_mm_s);
  246. #else
  247. static void line_to_destination_cartesian(const_feedRate_t scaled_fr_mm_s, const uint8_t e);
  248. #endif
  249. static bool mesh_is_valid() {
  250. GRID_LOOP(x, y) if (isnan(z_values[x][y])) return false;
  251. return true;
  252. }
  253. }; // class unified_bed_leveling
  254. extern unified_bed_leveling ubl;
  255. #define _GET_MESH_X(I) ubl.mesh_index_to_xpos(I)
  256. #define _GET_MESH_Y(J) ubl.mesh_index_to_ypos(J)
  257. #define Z_VALUES_ARR ubl.z_values
  258. // Prevent debugging propagating to other files
  259. #include "../../../core/debug_out.h"