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.

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