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.

L64XX_Marlin.cpp 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938
  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 <http://www.gnu.org/licenses/>.
  20. *
  21. */
  22. /**
  23. * The monitor_driver routines are a close copy of the TMC code
  24. */
  25. #include "../../inc/MarlinConfig.h"
  26. #if HAS_L64XX
  27. #include "L64XX_Marlin.h"
  28. L64XX_Marlin L64xxManager;
  29. #include "../../module/stepper/indirection.h"
  30. #include "../../gcode/gcode.h"
  31. #include "../../module/planner.h"
  32. #include "../../HAL/shared/Delay.h"
  33. void echo_yes_no(const bool yes) { serialprintPGM(yes ? PSTR(" YES") : PSTR(" NO ")); }
  34. static const char str_X[] PROGMEM = "X ", str_Y[] PROGMEM = "Y ", str_Z[] PROGMEM = "Z ",
  35. str_X2[] PROGMEM = "X2", str_Y2[] PROGMEM = "Y2",
  36. str_Z2[] PROGMEM = "Z2", str_Z3[] PROGMEM = "Z3", str_Z4[] PROGMEM = "Z4",
  37. str_E0[] PROGMEM = "E0", str_E1[] PROGMEM = "E1",
  38. str_E2[] PROGMEM = "E2", str_E3[] PROGMEM = "E3",
  39. str_E4[] PROGMEM = "E4", str_E5[] PROGMEM = "E5",
  40. str_E6[] PROGMEM = "E6", str_E7[] PROGMEM = "E7"
  41. ;
  42. PGM_P const L64XX_Marlin::index_to_axis[] PROGMEM = {
  43. str_X, str_Y, str_Z, str_X2, str_Y2, str_Z2, str_Z3, str_Z4,
  44. str_E0, str_E1, str_E2, str_E3, str_E4, str_E5, str_E6, str_E7
  45. };
  46. #define DEBUG_OUT ENABLED(L6470_CHITCHAT)
  47. #include "../../core/debug_out.h"
  48. uint8_t L64XX_Marlin::dir_commands[MAX_L64XX]; // array to hold direction command for each driver
  49. const uint8_t L64XX_Marlin::index_to_dir[MAX_L64XX] = {
  50. INVERT_X_DIR, INVERT_Y_DIR, INVERT_Z_DIR
  51. , (INVERT_X_DIR) // X2
  52. #if ENABLED(X_DUAL_STEPPER_DRIVERS)
  53. ^ (INVERT_X2_VS_X_DIR)
  54. #endif
  55. , (INVERT_Y_DIR) // Y2
  56. #if ENABLED(Y_DUAL_STEPPER_DRIVERS)
  57. ^ (INVERT_Y2_VS_Y_DIR)
  58. #endif
  59. , INVERT_Z_DIR, INVERT_Z_DIR, INVERT_Z_DIR // Z2,Z3,Z4
  60. , INVERT_E0_DIR, INVERT_E1_DIR, INVERT_E2_DIR, INVERT_E3_DIR
  61. , INVERT_E4_DIR, INVERT_E5_DIR, INVERT_E6_DIR, INVERT_E7_DIR
  62. };
  63. volatile uint8_t L64XX_Marlin::spi_abort = false;
  64. uint8_t L64XX_Marlin::spi_active = false;
  65. L64XX_Marlin::L64XX_shadow_t L64XX_Marlin::shadow;
  66. //uint32_t UVLO_ADC = 0x0400; // ADC undervoltage event
  67. void L6470_populate_chain_array() {
  68. #define _L6470_INIT_SPI(Q) do{ stepper##Q.set_chain_info(Q, Q##_CHAIN_POS); }while(0)
  69. #if AXIS_IS_L64XX(X)
  70. _L6470_INIT_SPI(X);
  71. #endif
  72. #if AXIS_IS_L64XX(X2)
  73. _L6470_INIT_SPI(X2);
  74. #endif
  75. #if AXIS_IS_L64XX(Y)
  76. _L6470_INIT_SPI(Y);
  77. #endif
  78. #if AXIS_IS_L64XX(Y2)
  79. _L6470_INIT_SPI(Y2);
  80. #endif
  81. #if AXIS_IS_L64XX(Z)
  82. _L6470_INIT_SPI(Z);
  83. #endif
  84. #if AXIS_IS_L64XX(Z2)
  85. _L6470_INIT_SPI(Z2);
  86. #endif
  87. #if AXIS_IS_L64XX(Z3)
  88. _L6470_INIT_SPI(Z3);
  89. #endif
  90. #if AXIS_IS_L64XX(Z4)
  91. _L6470_INIT_SPI(Z4);
  92. #endif
  93. #if AXIS_IS_L64XX(E0)
  94. _L6470_INIT_SPI(E0);
  95. #endif
  96. #if AXIS_IS_L64XX(E1)
  97. _L6470_INIT_SPI(E1);
  98. #endif
  99. #if AXIS_IS_L64XX(E2)
  100. _L6470_INIT_SPI(E2);
  101. #endif
  102. #if AXIS_IS_L64XX(E3)
  103. _L6470_INIT_SPI(E3);
  104. #endif
  105. #if AXIS_IS_L64XX(E4)
  106. _L6470_INIT_SPI(E4);
  107. #endif
  108. #if AXIS_IS_L64XX(E5)
  109. _L6470_INIT_SPI(E5);
  110. #endif
  111. #if AXIS_IS_L64XX(E6)
  112. _L6470_INIT_SPI(E6);
  113. #endif
  114. #if AXIS_IS_L64XX(E7)
  115. _L6470_INIT_SPI(E7);
  116. #endif
  117. }
  118. /**
  119. * Some status bit positions & definitions differ per driver.
  120. * Copy info to known locations to simplfy check/display logic.
  121. * 1. Copy stepper status
  122. * 2. Copy status bit definitions
  123. * 3. Copy status layout
  124. * 4. Make all error bits active low (as needed)
  125. */
  126. uint16_t L64XX_Marlin::get_stepper_status(L64XX &st) {
  127. shadow.STATUS_AXIS_RAW = st.getStatus();
  128. shadow.STATUS_AXIS = shadow.STATUS_AXIS_RAW;
  129. shadow.STATUS_AXIS_LAYOUT = st.L6470_status_layout;
  130. shadow.AXIS_OCD_TH_MAX = st.OCD_TH_MAX;
  131. shadow.AXIS_STALL_TH_MAX = st.STALL_TH_MAX;
  132. shadow.AXIS_OCD_CURRENT_CONSTANT_INV = st.OCD_CURRENT_CONSTANT_INV;
  133. shadow.AXIS_STALL_CURRENT_CONSTANT_INV = st.STALL_CURRENT_CONSTANT_INV;
  134. shadow.L6470_AXIS_CONFIG = st.L64XX_CONFIG;
  135. shadow.L6470_AXIS_STATUS = st.L64XX_STATUS;
  136. shadow.STATUS_AXIS_OCD = st.STATUS_OCD;
  137. shadow.STATUS_AXIS_SCK_MOD = st.STATUS_SCK_MOD;
  138. shadow.STATUS_AXIS_STEP_LOSS_A = st.STATUS_STEP_LOSS_A;
  139. shadow.STATUS_AXIS_STEP_LOSS_B = st.STATUS_STEP_LOSS_B;
  140. shadow.STATUS_AXIS_TH_SD = st.STATUS_TH_SD;
  141. shadow.STATUS_AXIS_TH_WRN = st.STATUS_TH_WRN;
  142. shadow.STATUS_AXIS_UVLO = st.STATUS_UVLO;
  143. shadow.STATUS_AXIS_WRONG_CMD = st.STATUS_WRONG_CMD;
  144. shadow.STATUS_AXIS_CMD_ERR = st.STATUS_CMD_ERR;
  145. shadow.STATUS_AXIS_NOTPERF_CMD = st.STATUS_NOTPERF_CMD;
  146. switch (shadow.STATUS_AXIS_LAYOUT) {
  147. case L6470_STATUS_LAYOUT: { // L6470
  148. shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
  149. shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
  150. break;
  151. }
  152. case L6474_STATUS_LAYOUT: { // L6474
  153. shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD ;
  154. shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_WRONG_CMD | shadow.STATUS_AXIS_NOTPERF_CMD); // invert just error bits that are active high
  155. break;
  156. }
  157. case L6480_STATUS_LAYOUT: { // L6480 & powerSTEP01
  158. shadow.L6470_ERROR_MASK = shadow.STATUS_AXIS_UVLO | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD | shadow.STATUS_AXIS_OCD | shadow.STATUS_AXIS_STEP_LOSS_A | shadow.STATUS_AXIS_STEP_LOSS_B;
  159. shadow.STATUS_AXIS ^= (shadow.STATUS_AXIS_CMD_ERR | shadow.STATUS_AXIS_TH_WRN | shadow.STATUS_AXIS_TH_SD); // invert just error bits that are active high
  160. break;
  161. }
  162. }
  163. return shadow.STATUS_AXIS;
  164. }
  165. void L64XX_Marlin::init() { // Set up SPI and then init chips
  166. ENABLE_RESET_L64XX_CHIPS(LOW); // hardware reset of drivers
  167. DELAY_US(100);
  168. ENABLE_RESET_L64XX_CHIPS(HIGH);
  169. DELAY_US(1000); // need about 650µs for the chip(s) to fully start up
  170. L6470_populate_chain_array(); // Set up array to control where in the SPI transfer sequence a particular stepper's data goes
  171. spi_init(); // Since L64XX SPI pins are unset we must init SPI here
  172. init_to_defaults(); // init the chips
  173. }
  174. uint16_t L64XX_Marlin::get_status(const L64XX_axis_t axis) {
  175. #define STATUS_L6470(Q) get_stepper_status(stepper##Q)
  176. switch (axis) {
  177. default: break;
  178. #if AXIS_IS_L64XX(X)
  179. case X : return STATUS_L6470(X);
  180. #endif
  181. #if AXIS_IS_L64XX(Y)
  182. case Y : return STATUS_L6470(Y);
  183. #endif
  184. #if AXIS_IS_L64XX(Z)
  185. case Z : return STATUS_L6470(Z);
  186. #endif
  187. #if AXIS_IS_L64XX(X2)
  188. case X2: return STATUS_L6470(X2);
  189. #endif
  190. #if AXIS_IS_L64XX(Y2)
  191. case Y2: return STATUS_L6470(Y2);
  192. #endif
  193. #if AXIS_IS_L64XX(Z2)
  194. case Z2: return STATUS_L6470(Z2);
  195. #endif
  196. #if AXIS_IS_L64XX(Z3)
  197. case Z3: return STATUS_L6470(Z3);
  198. #endif
  199. #if AXIS_IS_L64XX(Z4)
  200. case Z4: return STATUS_L6470(Z4);
  201. #endif
  202. #if AXIS_IS_L64XX(E0)
  203. case E0: return STATUS_L6470(E0);
  204. #endif
  205. #if AXIS_IS_L64XX(E1)
  206. case E1: return STATUS_L6470(E1);
  207. #endif
  208. #if AXIS_IS_L64XX(E2)
  209. case E2: return STATUS_L6470(E2);
  210. #endif
  211. #if AXIS_IS_L64XX(E3)
  212. case E3: return STATUS_L6470(E3);
  213. #endif
  214. #if AXIS_IS_L64XX(E4)
  215. case E4: return STATUS_L6470(E4);
  216. #endif
  217. #if AXIS_IS_L64XX(E5)
  218. case E5: return STATUS_L6470(E5);
  219. #endif
  220. #if AXIS_IS_L64XX(E6)
  221. case E6: return STATUS_L6470(E6);
  222. #endif
  223. #if AXIS_IS_L64XX(E7)
  224. case E7: return STATUS_L6470(E7);
  225. #endif
  226. }
  227. return 0; // Not needed but kills a compiler warning
  228. }
  229. uint32_t L64XX_Marlin::get_param(const L64XX_axis_t axis, const uint8_t param) {
  230. #define GET_L6470_PARAM(Q) L6470_GETPARAM(param, Q)
  231. switch (axis) {
  232. default: break;
  233. #if AXIS_IS_L64XX(X)
  234. case X : return GET_L6470_PARAM(X);
  235. #endif
  236. #if AXIS_IS_L64XX(Y)
  237. case Y : return GET_L6470_PARAM(Y);
  238. #endif
  239. #if AXIS_IS_L64XX(Z)
  240. case Z : return GET_L6470_PARAM(Z);
  241. #endif
  242. #if AXIS_IS_L64XX(X2)
  243. case X2: return GET_L6470_PARAM(X2);
  244. #endif
  245. #if AXIS_IS_L64XX(Y2)
  246. case Y2: return GET_L6470_PARAM(Y2);
  247. #endif
  248. #if AXIS_IS_L64XX(Z2)
  249. case Z2: return GET_L6470_PARAM(Z2);
  250. #endif
  251. #if AXIS_IS_L64XX(Z3)
  252. case Z3: return GET_L6470_PARAM(Z3);
  253. #endif
  254. #if AXIS_IS_L64XX(Z4)
  255. case Z4: return GET_L6470_PARAM(Z4);
  256. #endif
  257. #if AXIS_IS_L64XX(E0)
  258. case E0: return GET_L6470_PARAM(E0);
  259. #endif
  260. #if AXIS_IS_L64XX(E1)
  261. case E1: return GET_L6470_PARAM(E1);
  262. #endif
  263. #if AXIS_IS_L64XX(E2)
  264. case E2: return GET_L6470_PARAM(E2);
  265. #endif
  266. #if AXIS_IS_L64XX(E3)
  267. case E3: return GET_L6470_PARAM(E3);
  268. #endif
  269. #if AXIS_IS_L64XX(E4)
  270. case E4: return GET_L6470_PARAM(E4);
  271. #endif
  272. #if AXIS_IS_L64XX(E5)
  273. case E5: return GET_L6470_PARAM(E5);
  274. #endif
  275. #if AXIS_IS_L64XX(E6)
  276. case E6: return GET_L6470_PARAM(E6);
  277. #endif
  278. #if AXIS_IS_L64XX(E7)
  279. case E7: return GET_L6470_PARAM(E7);
  280. #endif
  281. }
  282. return 0; // not needed but kills a compiler warning
  283. }
  284. void L64XX_Marlin::set_param(const L64XX_axis_t axis, const uint8_t param, const uint32_t value) {
  285. #define SET_L6470_PARAM(Q) stepper##Q.SetParam(param, value)
  286. switch (axis) {
  287. default: break;
  288. #if AXIS_IS_L64XX(X)
  289. case X : SET_L6470_PARAM(X); break;
  290. #endif
  291. #if AXIS_IS_L64XX(Y)
  292. case Y : SET_L6470_PARAM(Y); break;
  293. #endif
  294. #if AXIS_IS_L64XX(Z)
  295. case Z : SET_L6470_PARAM(Z); break;
  296. #endif
  297. #if AXIS_IS_L64XX(X2)
  298. case X2: SET_L6470_PARAM(X2); break;
  299. #endif
  300. #if AXIS_IS_L64XX(Y2)
  301. case Y2: SET_L6470_PARAM(Y2); break;
  302. #endif
  303. #if AXIS_IS_L64XX(Z2)
  304. case Z2: SET_L6470_PARAM(Z2); break;
  305. #endif
  306. #if AXIS_IS_L64XX(Z3)
  307. case Z3: SET_L6470_PARAM(Z3); break;
  308. #endif
  309. #if AXIS_IS_L64XX(Z4)
  310. case Z4: SET_L6470_PARAM(Z4); break;
  311. #endif
  312. #if AXIS_IS_L64XX(E0)
  313. case E0: SET_L6470_PARAM(E0); break;
  314. #endif
  315. #if AXIS_IS_L64XX(E1)
  316. case E1: SET_L6470_PARAM(E1); break;
  317. #endif
  318. #if AXIS_IS_L64XX(E2)
  319. case E2: SET_L6470_PARAM(E2); break;
  320. #endif
  321. #if AXIS_IS_L64XX(E3)
  322. case E3: SET_L6470_PARAM(E3); break;
  323. #endif
  324. #if AXIS_IS_L64XX(E4)
  325. case E4: SET_L6470_PARAM(E4); break;
  326. #endif
  327. #if AXIS_IS_L64XX(E5)
  328. case E5: SET_L6470_PARAM(E5); break;
  329. #endif
  330. #if AXIS_IS_L64XX(E6)
  331. case E6: SET_L6470_PARAM(E6); break;
  332. #endif
  333. #if AXIS_IS_L64XX(E7)
  334. case E7: SET_L6470_PARAM(E7); break;
  335. #endif
  336. }
  337. }
  338. inline void echo_min_max(const char a, const float &min, const float &max) {
  339. DEBUG_CHAR(' '); DEBUG_CHAR(a);
  340. DEBUG_ECHOPAIR(" min = ", min);
  341. DEBUG_ECHOLNPAIR(" max = ", max);
  342. }
  343. inline void echo_oct_used(const float &oct, const uint8_t stall) {
  344. DEBUG_ECHOPAIR("over_current_threshold used : ", oct);
  345. serialprintPGM(stall ? PSTR(" (Stall") : PSTR(" (OCD"));
  346. DEBUG_ECHOLNPGM(" threshold)");
  347. }
  348. inline void err_out_of_bounds() { DEBUG_ECHOLNPGM("Test aborted - motion out of bounds"); }
  349. uint8_t L64XX_Marlin::get_user_input(uint8_t &driver_count, L64XX_axis_t axis_index[3], char axis_mon[3][3],
  350. float &position_max, float &position_min, float &final_feedrate, uint8_t &kval_hold,
  351. uint8_t over_current_flag, uint8_t &OCD_TH_val, uint8_t &STALL_TH_val, uint16_t &over_current_threshold
  352. ) {
  353. // Return TRUE if the calling routine needs to abort/kill
  354. uint16_t displacement = 0; // " = 0" to eliminate compiler warning
  355. uint8_t j; // general purpose counter
  356. if (!all_axes_homed()) {
  357. DEBUG_ECHOLNPGM("Test aborted - home all before running this command");
  358. return true;
  359. }
  360. uint8_t found_displacement = false;
  361. LOOP_XYZE(i) if (uint16_t _displacement = parser.intval(axis_codes[i])) {
  362. found_displacement = true;
  363. displacement = _displacement;
  364. uint8_t axis_offset = parser.byteval('J');
  365. axis_mon[0][0] = axis_codes[i]; // Axis first character, one of XYZE
  366. const bool single_or_e = axis_offset >= 2 || axis_mon[0][0] == 'E',
  367. one_or_more = !single_or_e && axis_offset == 0;
  368. uint8_t driver_count_local = 0; // Can't use "driver_count" directly as a subscript because it's passed by reference
  369. if (single_or_e) // Single axis, E0, or E1
  370. axis_mon[0][1] = axis_offset + '0'; // Index given by 'J' parameter
  371. if (single_or_e || one_or_more) {
  372. for (j = 0; j < MAX_L64XX; j++) { // Count up the drivers on this axis
  373. PGM_P str = (PGM_P)pgm_read_ptr(&index_to_axis[j]); // Get a PGM_P from progmem
  374. const char c = pgm_read_byte(str); // Get a char from progmem
  375. if (axis_mon[0][0] == c) { // For each stepper on this axis...
  376. char *mon = axis_mon[driver_count_local];
  377. *mon++ = c; // Copy the 3 letter axis name
  378. *mon++ = pgm_read_byte(&str[1]); // to the axis_mon array
  379. *mon = pgm_read_byte(&str[2]);
  380. axis_index[driver_count_local] = (L64XX_axis_t)j; // And store the L64XX axis index
  381. driver_count_local++;
  382. }
  383. }
  384. if (one_or_more) driver_count = driver_count_local;
  385. }
  386. break; // only take first axis found
  387. }
  388. if (!found_displacement) {
  389. DEBUG_ECHOLNPGM("Test aborted - AXIS with displacement is required");
  390. return true;
  391. }
  392. //
  393. // Position calcs & checks
  394. //
  395. const float X_center = LOGICAL_X_POSITION(current_position.x),
  396. Y_center = LOGICAL_Y_POSITION(current_position.y),
  397. Z_center = LOGICAL_Z_POSITION(current_position.z),
  398. E_center = current_position.e;
  399. switch (axis_mon[0][0]) {
  400. default: position_max = position_min = 0; break;
  401. case 'X': {
  402. position_min = X_center - displacement;
  403. position_max = X_center + displacement;
  404. echo_min_max('X', position_min, position_max);
  405. if (false
  406. #ifdef X_MIN_POS
  407. || position_min < (X_MIN_POS)
  408. #endif
  409. #ifdef X_MAX_POS
  410. || position_max > (X_MAX_POS)
  411. #endif
  412. ) {
  413. err_out_of_bounds();
  414. return true;
  415. }
  416. } break;
  417. case 'Y': {
  418. position_min = Y_center - displacement;
  419. position_max = Y_center + displacement;
  420. echo_min_max('Y', position_min, position_max);
  421. if (false
  422. #ifdef Y_MIN_POS
  423. || position_min < (Y_MIN_POS)
  424. #endif
  425. #ifdef Y_MAX_POS
  426. || position_max > (Y_MAX_POS)
  427. #endif
  428. ) {
  429. err_out_of_bounds();
  430. return true;
  431. }
  432. } break;
  433. case 'Z': {
  434. position_min = Z_center - displacement;
  435. position_max = Z_center + displacement;
  436. echo_min_max('Z', position_min, position_max);
  437. if (false
  438. #ifdef Z_MIN_POS
  439. || position_min < (Z_MIN_POS)
  440. #endif
  441. #ifdef Z_MAX_POS
  442. || position_max > (Z_MAX_POS)
  443. #endif
  444. ) {
  445. err_out_of_bounds();
  446. return true;
  447. }
  448. } break;
  449. case 'E': {
  450. position_min = E_center - displacement;
  451. position_max = E_center + displacement;
  452. echo_min_max('E', position_min, position_max);
  453. } break;
  454. }
  455. //
  456. // Work on the drivers
  457. //
  458. LOOP_L_N(k, driver_count) {
  459. uint8_t not_found = true;
  460. for (j = 1; j <= L64XX::chain[0]; j++) {
  461. PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[L64XX::chain[j]]);
  462. if (pgm_read_byte(&str[0]) == axis_mon[k][0] && pgm_read_byte(&str[1]) == axis_mon[k][1]) { // See if a L6470 driver
  463. not_found = false;
  464. break;
  465. }
  466. }
  467. if (not_found) {
  468. driver_count = k;
  469. axis_mon[k][0] = ' '; // mark this entry invalid
  470. break;
  471. }
  472. }
  473. if (driver_count == 0) {
  474. DEBUG_ECHOLNPGM("Test aborted - not a L6470 axis");
  475. return true;
  476. }
  477. DEBUG_ECHOPGM("Monitoring:");
  478. for (j = 0; j < driver_count; j++) DEBUG_ECHOPAIR(" ", axis_mon[j]);
  479. DEBUG_EOL();
  480. // now have a list of driver(s) to monitor
  481. //
  482. // TVAL & kVAL_HOLD checks & settings
  483. //
  484. const L64XX_shadow_t &sh = shadow;
  485. get_status(axis_index[0]); // populate shadow array
  486. if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474 - use TVAL
  487. uint16_t TVAL_current = parser.ushortval('T');
  488. if (TVAL_current) {
  489. uint8_t TVAL_count = (TVAL_current / sh.AXIS_STALL_CURRENT_CONSTANT_INV) - 1;
  490. LIMIT(TVAL_count, 0, sh.AXIS_STALL_TH_MAX);
  491. for (j = 0; j < driver_count; j++)
  492. set_param(axis_index[j], L6474_TVAL, TVAL_count);
  493. }
  494. // only print the tval from one of the drivers
  495. kval_hold = get_param(axis_index[0], L6474_TVAL);
  496. DEBUG_ECHOLNPAIR("TVAL current (mA) = ", (kval_hold + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV);
  497. }
  498. else {
  499. kval_hold = parser.byteval('K');
  500. if (kval_hold) {
  501. DEBUG_ECHOLNPAIR("kval_hold = ", kval_hold);
  502. for (j = 0; j < driver_count; j++)
  503. set_param(axis_index[j], L6470_KVAL_HOLD, kval_hold);
  504. }
  505. else {
  506. // only print the KVAL_HOLD from one of the drivers
  507. kval_hold = get_param(axis_index[0], L6470_KVAL_HOLD);
  508. DEBUG_ECHOLNPAIR("KVAL_HOLD = ", kval_hold);
  509. }
  510. }
  511. //
  512. // Overcurrent checks & settings
  513. //
  514. if (over_current_flag) {
  515. uint8_t OCD_TH_val_local = 0, // compiler thinks OCD_TH_val is unused if use it directly
  516. STALL_TH_val_local = 0; // just in case ...
  517. over_current_threshold = parser.intval('I');
  518. if (over_current_threshold) {
  519. OCD_TH_val_local = over_current_threshold/375;
  520. LIMIT(OCD_TH_val_local, 0, 15);
  521. STALL_TH_val_local = over_current_threshold/31.25;
  522. LIMIT(STALL_TH_val_local, 0, 127);
  523. uint16_t OCD_TH_actual = (OCD_TH_val_local + 1) * 375,
  524. STALL_TH_actual = (STALL_TH_val_local + 1) * 31.25;
  525. if (OCD_TH_actual < STALL_TH_actual) {
  526. OCD_TH_val_local++;
  527. OCD_TH_actual = (OCD_TH_val_local + 1) * 375;
  528. }
  529. DEBUG_ECHOLNPAIR("over_current_threshold specified: ", over_current_threshold);
  530. if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
  531. echo_oct_used((OCD_TH_val_local + 1) * 375, false);
  532. #define SET_OVER_CURRENT(Q) do { stepper##Q.SetParam(L6470_STALL_TH, STALL_TH_val_local); stepper##Q.SetParam(L6470_OCD_TH, OCD_TH_val_local);} while (0)
  533. for (j = 0; j < driver_count; j++) {
  534. set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
  535. set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
  536. }
  537. }
  538. else {
  539. // only get & print the OVER_CURRENT values from one of the drivers
  540. STALL_TH_val_local = get_param(axis_index[0], L6470_STALL_TH);
  541. OCD_TH_val_local = get_param(axis_index[0], L6470_OCD_TH);
  542. if (!(sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT)) echo_oct_used((STALL_TH_val_local + 1) * 31.25, true);
  543. echo_oct_used((OCD_TH_val_local + 1) * 375, false);
  544. } // over_current_threshold
  545. for (j = 0; j < driver_count; j++) { // set all drivers on axis the same
  546. set_param(axis_index[j], L6470_STALL_TH, STALL_TH_val_local);
  547. set_param(axis_index[j], L6470_OCD_TH, OCD_TH_val_local);
  548. }
  549. OCD_TH_val = OCD_TH_val_local; // force compiler to update the main routine's copy
  550. STALL_TH_val = STALL_TH_val_local; // force compiler to update the main routine's copy
  551. } // end of overcurrent
  552. //
  553. // Feedrate
  554. //
  555. final_feedrate = parser.floatval('F');
  556. if (final_feedrate == 0) {
  557. static constexpr float default_max_feedrate[] = DEFAULT_MAX_FEEDRATE;
  558. const uint8_t num_feedrates = COUNT(default_max_feedrate);
  559. for (j = 0; j < num_feedrates; j++) {
  560. if (axis_codes[j] == axis_mon[0][0]) {
  561. final_feedrate = default_max_feedrate[j];
  562. break;
  563. }
  564. }
  565. if (j == 3 && num_feedrates > 4) { // have more than one extruder feedrate
  566. uint8_t extruder_num = axis_mon[0][1] - '0';
  567. if (j <= num_feedrates - extruder_num) // have a feedrate specifically for this extruder
  568. final_feedrate = default_max_feedrate[j + extruder_num];
  569. else
  570. final_feedrate = default_max_feedrate[3]; // use E0 feedrate for this extruder
  571. }
  572. final_feedrate *= 60; // convert to mm/minute
  573. } // end of feedrate
  574. return false; // FALSE indicates no user input problems
  575. }
  576. void L64XX_Marlin::say_axis(const L64XX_axis_t axis, const uint8_t label/*=true*/) {
  577. if (label) SERIAL_ECHOPGM("AXIS:");
  578. const char * const str = L64xxManager.index_to_axis[axis];
  579. SERIAL_CHAR(' ', str[0], str[1], ' ');
  580. }
  581. #if ENABLED(L6470_CHITCHAT)
  582. // Assumes status bits have been inverted
  583. void L64XX_Marlin::error_status_decode(const uint16_t status, const L64XX_axis_t axis,
  584. const uint16_t _status_axis_th_sd, const uint16_t _status_axis_th_wrn,
  585. const uint16_t _status_axis_step_loss_a, const uint16_t _status_axis_step_loss_b,
  586. const uint16_t _status_axis_ocd, const uint8_t _status_axis_layout
  587. ) {
  588. say_axis(axis);
  589. DEBUG_ECHOPGM(" THERMAL: ");
  590. serialprintPGM((status & _status_axis_th_sd) ? PSTR("SHUTDOWN") : (status & _status_axis_th_wrn) ? PSTR("WARNING ") : PSTR("OK "));
  591. DEBUG_ECHOPGM(" OVERCURRENT: ");
  592. echo_yes_no((status & _status_axis_ocd) != 0);
  593. if (!(_status_axis_layout == L6474_STATUS_LAYOUT)) { // L6474 doesn't have these bits
  594. DEBUG_ECHOPGM(" STALL: ");
  595. echo_yes_no((status & (_status_axis_step_loss_a | _status_axis_step_loss_b)) != 0);
  596. }
  597. DEBUG_EOL();
  598. }
  599. #endif
  600. //////////////////////////////////////////////////////////////////////////////////////////////////
  601. ////
  602. //// MONITOR_L6470_DRIVER_STATUS routines
  603. ////
  604. //////////////////////////////////////////////////////////////////////////////////////////////////
  605. #if ENABLED(MONITOR_L6470_DRIVER_STATUS)
  606. bool L64XX_Marlin::monitor_paused = false; // Flag to skip monitor during M122, M906, M916, M917, M918, etc.
  607. struct L6470_driver_data {
  608. uint8_t driver_index;
  609. uint32_t driver_status;
  610. uint8_t is_otw;
  611. uint8_t otw_counter;
  612. uint8_t is_ot;
  613. uint8_t is_hi_Z;
  614. uint8_t com_counter;
  615. };
  616. L6470_driver_data driver_L6470_data[] = {
  617. #if AXIS_IS_L64XX(X)
  618. { 0, 0, 0, 0, 0, 0, 0 },
  619. #endif
  620. #if AXIS_IS_L64XX(Y)
  621. { 1, 0, 0, 0, 0, 0, 0 },
  622. #endif
  623. #if AXIS_IS_L64XX(Z)
  624. { 2, 0, 0, 0, 0, 0, 0 },
  625. #endif
  626. #if AXIS_IS_L64XX(X2)
  627. { 3, 0, 0, 0, 0, 0, 0 },
  628. #endif
  629. #if AXIS_IS_L64XX(Y2)
  630. { 4, 0, 0, 0, 0, 0, 0 },
  631. #endif
  632. #if AXIS_IS_L64XX(Z2)
  633. { 5, 0, 0, 0, 0, 0, 0 },
  634. #endif
  635. #if AXIS_IS_L64XX(Z3)
  636. { 6, 0, 0, 0, 0, 0, 0 },
  637. #endif
  638. #if AXIS_IS_L64XX(Z4)
  639. { 7, 0, 0, 0, 0, 0, 0 },
  640. #endif
  641. #if AXIS_IS_L64XX(E0)
  642. { 8, 0, 0, 0, 0, 0, 0 },
  643. #endif
  644. #if AXIS_IS_L64XX(E1)
  645. { 9, 0, 0, 0, 0, 0, 0 },
  646. #endif
  647. #if AXIS_IS_L64XX(E2)
  648. { 10, 0, 0, 0, 0, 0, 0 },
  649. #endif
  650. #if AXIS_IS_L64XX(E3)
  651. { 11, 0, 0, 0, 0, 0, 0 },
  652. #endif
  653. #if AXIS_IS_L64XX(E4)
  654. { 12, 0, 0, 0, 0, 0, 0 },
  655. #endif
  656. #if AXIS_IS_L64XX(E5)
  657. { 13, 0, 0, 0, 0, 0, 0 }
  658. #endif
  659. #if AXIS_IS_L64XX(E6)
  660. { 14, 0, 0, 0, 0, 0, 0 }
  661. #endif
  662. #if AXIS_IS_L64XX(E7)
  663. { 16, 0, 0, 0, 0, 0, 0 }
  664. #endif
  665. };
  666. void L64XX_Marlin::append_stepper_err(char* &p, const uint8_t stepper_index, const char * const err/*=nullptr*/) {
  667. PGM_P const str = (PGM_P)pgm_read_ptr(&index_to_axis[stepper_index]);
  668. p += sprintf_P(p, PSTR("Stepper %c%c "), pgm_read_byte(&str[0]), pgm_read_byte(&str[1]));
  669. if (err) p += sprintf_P(p, err);
  670. }
  671. void L64XX_Marlin::monitor_update(L64XX_axis_t stepper_index) {
  672. if (spi_abort) return; // don't do anything if set_directions() has occurred
  673. const L64XX_shadow_t &sh = shadow;
  674. get_status(stepper_index); // get stepper status and details
  675. uint16_t status = sh.STATUS_AXIS;
  676. uint8_t kval_hold, tval;
  677. char temp_buf[120], *p = temp_buf;
  678. uint8_t j;
  679. for (j = 0; j < L64XX::chain[0]; j++) // find the table for this stepper
  680. if (driver_L6470_data[j].driver_index == stepper_index) break;
  681. driver_L6470_data[j].driver_status = status;
  682. uint16_t _status = ~status; // all error bits are active low
  683. if (status == 0 || status == 0xFFFF) { // com problem
  684. if (driver_L6470_data[j].com_counter == 0) { // warn user when it first happens
  685. driver_L6470_data[j].com_counter++;
  686. append_stepper_err(p, stepper_index, PSTR(" - communications lost\n"));
  687. DEBUG_ECHO(temp_buf);
  688. }
  689. else {
  690. driver_L6470_data[j].com_counter++;
  691. if (driver_L6470_data[j].com_counter > 240) { // remind of com problem about every 2 minutes
  692. driver_L6470_data[j].com_counter = 1;
  693. append_stepper_err(p, stepper_index, PSTR(" - still no communications\n"));
  694. DEBUG_ECHO(temp_buf);
  695. }
  696. }
  697. }
  698. else {
  699. if (driver_L6470_data[j].com_counter) { // comms re-established
  700. driver_L6470_data[j].com_counter = 0;
  701. append_stepper_err(p, stepper_index, PSTR(" - communications re-established\n.. setting all drivers to default values\n"));
  702. DEBUG_ECHO(temp_buf);
  703. init_to_defaults();
  704. }
  705. else {
  706. // no com problems - do the usual checks
  707. if (_status & sh.L6470_ERROR_MASK) {
  708. append_stepper_err(p, stepper_index);
  709. if (status & STATUS_HIZ) { // The driver has shut down. HiZ is active high
  710. driver_L6470_data[j].is_hi_Z = true;
  711. p += sprintf_P(p, PSTR("%cIS SHUT DOWN"), ' ');
  712. //if (_status & sh.STATUS_AXIS_TH_SD) { // strange - TH_SD never seems to go active, must be implied by the HiZ and TH_WRN
  713. if (_status & sh.STATUS_AXIS_TH_WRN) { // over current shutdown
  714. p += sprintf_P(p, PSTR("%cdue to over temperature"), ' ');
  715. driver_L6470_data[j].is_ot = true;
  716. if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
  717. tval = get_param(stepper_index, L6474_TVAL) - 2 * KVAL_HOLD_STEP_DOWN;
  718. set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
  719. p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (2 * KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
  720. }
  721. else {
  722. kval_hold = get_param(stepper_index, L6470_KVAL_HOLD) - 2 * KVAL_HOLD_STEP_DOWN;
  723. set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
  724. p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), 2 * KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
  725. }
  726. }
  727. else
  728. driver_L6470_data[j].is_ot = false;
  729. }
  730. else {
  731. driver_L6470_data[j].is_hi_Z = false;
  732. if (_status & sh.STATUS_AXIS_TH_WRN) { // have an over temperature warning
  733. driver_L6470_data[j].is_otw = true;
  734. driver_L6470_data[j].otw_counter++;
  735. kval_hold = get_param(stepper_index, L6470_KVAL_HOLD);
  736. if (driver_L6470_data[j].otw_counter > 4) { // otw present for 2 - 2.5 seconds, reduce KVAL_HOLD
  737. driver_L6470_data[j].otw_counter = 0;
  738. driver_L6470_data[j].is_otw = true;
  739. if (sh.STATUS_AXIS_LAYOUT == L6474_STATUS_LAYOUT) { // L6474
  740. tval = get_param(stepper_index, L6474_TVAL) - KVAL_HOLD_STEP_DOWN;
  741. set_param(stepper_index, L6474_TVAL, tval); // reduce TVAL
  742. p += sprintf_P(p, PSTR(" - TVAL reduced by %d to %d mA"), uint16_t (KVAL_HOLD_STEP_DOWN * sh.AXIS_STALL_CURRENT_CONSTANT_INV), uint16_t ((tval + 1) * sh.AXIS_STALL_CURRENT_CONSTANT_INV)); // let user know
  743. }
  744. else {
  745. kval_hold = get_param(stepper_index, L6470_KVAL_HOLD) - KVAL_HOLD_STEP_DOWN;
  746. set_param(stepper_index, L6470_KVAL_HOLD, kval_hold); // reduce KVAL_HOLD
  747. p += sprintf_P(p, PSTR(" - KVAL_HOLD reduced by %d to %d"), KVAL_HOLD_STEP_DOWN, kval_hold); // let user know
  748. }
  749. }
  750. else if (driver_L6470_data[j].otw_counter)
  751. p += sprintf_P(p, PSTR("%c- thermal warning"), ' '); // warn user
  752. }
  753. }
  754. #if ENABLED(L6470_STOP_ON_ERROR)
  755. if (_status & (sh.STATUS_AXIS_UVLO | sh.STATUS_AXIS_TH_WRN | sh.STATUS_AXIS_TH_SD))
  756. kill(temp_buf);
  757. #endif
  758. #if ENABLED(L6470_CHITCHAT)
  759. if (_status & sh.STATUS_AXIS_OCD)
  760. p += sprintf_P(p, PSTR("%c over current"), ' ');
  761. if (_status & (sh.STATUS_AXIS_STEP_LOSS_A | sh.STATUS_AXIS_STEP_LOSS_B))
  762. p += sprintf_P(p, PSTR("%c stall"), ' ');
  763. if (_status & sh.STATUS_AXIS_UVLO)
  764. p += sprintf_P(p, PSTR("%c under voltage lock out"), ' ');
  765. p += sprintf_P(p, PSTR("%c\n"), ' ');
  766. #endif
  767. DEBUG_ECHOLN(temp_buf); // print the error message
  768. }
  769. else {
  770. driver_L6470_data[j].is_ot = false;
  771. driver_L6470_data[j].otw_counter = 0; //clear out warning indicators
  772. driver_L6470_data[j].is_otw = false;
  773. } // end usual checks
  774. } // comms established but have errors
  775. } // comms re-established
  776. } // end monitor_update()
  777. void L64XX_Marlin::monitor_driver() {
  778. static millis_t next_cOT = 0;
  779. if (ELAPSED(millis(), next_cOT)) {
  780. next_cOT = millis() + 500;
  781. if (!monitor_paused) { // Skip during M122, M906, M916, M917 or M918 (could steal status result from test)
  782. spi_active = true; // Tell set_directions() a series of SPI transfers is underway
  783. #if AXIS_IS_L64XX(X)
  784. monitor_update(X);
  785. #endif
  786. #if AXIS_IS_L64XX(Y)
  787. monitor_update(Y);
  788. #endif
  789. #if AXIS_IS_L64XX(Z)
  790. monitor_update(Z);
  791. #endif
  792. #if AXIS_IS_L64XX(X2)
  793. monitor_update(X2);
  794. #endif
  795. #if AXIS_IS_L64XX(Y2)
  796. monitor_update(Y2);
  797. #endif
  798. #if AXIS_IS_L64XX(Z2)
  799. monitor_update(Z2);
  800. #endif
  801. #if AXIS_IS_L64XX(Z3)
  802. monitor_update(Z3);
  803. #endif
  804. #if AXIS_IS_L64XX(Z4)
  805. monitor_update(Z4);
  806. #endif
  807. #if AXIS_IS_L64XX(E0)
  808. monitor_update(E0);
  809. #endif
  810. #if AXIS_IS_L64XX(E1)
  811. monitor_update(E1);
  812. #endif
  813. #if AXIS_IS_L64XX(E2)
  814. monitor_update(E2);
  815. #endif
  816. #if AXIS_IS_L64XX(E3)
  817. monitor_update(E3);
  818. #endif
  819. #if AXIS_IS_L64XX(E4)
  820. monitor_update(E4);
  821. #endif
  822. #if AXIS_IS_L64XX(E5)
  823. monitor_update(E5);
  824. #endif
  825. #if ENABLED(L6470_DEBUG)
  826. if (report_L6470_status) DEBUG_EOL();
  827. #endif
  828. spi_active = false; // done with all SPI transfers - clear handshake flags
  829. spi_abort = false;
  830. }
  831. }
  832. }
  833. #endif // MONITOR_L6470_DRIVER_STATUS
  834. #endif // HAS_L64XX