G6 Direct Stepping (#17853)
This commit is contained in:
		| @@ -1663,6 +1663,16 @@ | ||||
| // Support for G5 with XYZE destination and IJPQ offsets. Requires ~2666 bytes. | ||||
| //#define BEZIER_CURVE_SUPPORT | ||||
|  | ||||
| /** | ||||
|  * Direct Stepping | ||||
|  * | ||||
|  * Comparable to the method used by Klipper, G6 direct stepping significantly | ||||
|  * reduces motion calculations, increases top printing speeds, and results in | ||||
|  * less step aliasing by calculating all motions in advance. | ||||
|  * Preparing your G-code: https://github.com/colinrgodsey/step-daemon | ||||
|  */ | ||||
| //#define DIRECT_STEPPING | ||||
|  | ||||
| /** | ||||
|  * G38 Probe Target | ||||
|  * | ||||
| @@ -1731,14 +1741,16 @@ | ||||
| //================================= Buffers ================================= | ||||
| //=========================================================================== | ||||
|  | ||||
| // @section hidden | ||||
| // @section motion | ||||
|  | ||||
| // The number of linear motions that can be in the plan at any give time. | ||||
| // THE BLOCK_BUFFER_SIZE NEEDS TO BE A POWER OF 2 (e.g. 8, 16, 32) because shifts and ors are used to do the ring-buffering. | ||||
| #if ENABLED(SDSUPPORT) | ||||
|   #define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller | ||||
| // The number of lineear moves that can be in the planner at once. | ||||
| // The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g. 8, 16, 32) | ||||
| #if BOTH(SDSUPPORT, DIRECT_STEPPING) | ||||
|   #define BLOCK_BUFFER_SIZE  8 | ||||
| #elif ENABLED(SDSUPPORT) | ||||
|   #define BLOCK_BUFFER_SIZE 16 | ||||
| #else | ||||
|   #define BLOCK_BUFFER_SIZE 16 // maximize block buffer | ||||
|   #define BLOCK_BUFFER_SIZE 16 | ||||
| #endif | ||||
|  | ||||
| // @section serial | ||||
|   | ||||
| @@ -43,6 +43,10 @@ | ||||
|   #include "MarlinSerial.h" | ||||
|   #include "../../MarlinCore.h" | ||||
|  | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     #include "../../feature/direct_stepping.h" | ||||
|   #endif | ||||
|  | ||||
|   template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_r MarlinSerial<Cfg>::rx_buffer = { 0, 0, { 0 } }; | ||||
|   template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_t MarlinSerial<Cfg>::tx_buffer = { 0 }; | ||||
|   template<typename Cfg> bool     MarlinSerial<Cfg>::_written = false; | ||||
| @@ -131,6 +135,18 @@ | ||||
|  | ||||
|     static EmergencyParser::State emergency_state; // = EP_RESET | ||||
|  | ||||
|     // This must read the R_UCSRA register before reading the received byte to detect error causes | ||||
|     if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes; | ||||
|     if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns; | ||||
|     if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors; | ||||
|  | ||||
|     // Read the character from the USART | ||||
|     uint8_t c = R_UDR; | ||||
|  | ||||
|     #if ENABLED(DIRECT_STEPPING) | ||||
|       if (page_manager.maybe_store_rxd_char(c)) return; | ||||
|     #endif | ||||
|  | ||||
|     // Get the tail - Nothing can alter its value while this ISR is executing, but there's | ||||
|     // a chance that this ISR interrupted the main process while it was updating the index. | ||||
|     // The backup mechanism ensures the correct value is always returned. | ||||
| @@ -142,14 +158,6 @@ | ||||
|     // Get the next element | ||||
|     ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1); | ||||
|  | ||||
|     // This must read the R_UCSRA register before reading the received byte to detect error causes | ||||
|     if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes; | ||||
|     if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns; | ||||
|     if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors; | ||||
|  | ||||
|     // Read the character from the USART | ||||
|     uint8_t c = R_UDR; | ||||
|  | ||||
|     if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c); | ||||
|  | ||||
|     // If the character is to be stored at the index just before the tail | ||||
|   | ||||
| @@ -59,6 +59,10 @@ | ||||
| #include "gcode/parser.h" | ||||
| #include "gcode/queue.h" | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   #include "feature/direct_stepping.h" | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(TOUCH_BUTTONS) | ||||
|   #include "feature/touch/xpt2046.h" | ||||
| #endif | ||||
| @@ -713,6 +717,9 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) { | ||||
|  | ||||
|   // Handle Joystick jogging | ||||
|   TERN_(POLL_JOG, joystick.inject_jog_moves()); | ||||
|  | ||||
|   // Direct Stepping | ||||
|   TERN_(DIRECT_STEPPING, page_manager.write_responses()); | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -1124,6 +1131,10 @@ void setup() { | ||||
|     SETUP_RUN(max7219.init()); | ||||
|   #endif | ||||
|  | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     SETUP_RUN(page_manager.init()); | ||||
|   #endif | ||||
|  | ||||
|   marlin_state = MF_RUNNING; | ||||
|  | ||||
|   SETUP_LOG("setup() completed."); | ||||
|   | ||||
							
								
								
									
										273
									
								
								Marlin/src/feature/direct_stepping.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										273
									
								
								Marlin/src/feature/direct_stepping.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,273 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 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 "../inc/MarlinConfigPre.h" | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|  | ||||
| #include "direct_stepping.h" | ||||
|  | ||||
| #include "../MarlinCore.h" | ||||
|  | ||||
| #define CHECK_PAGE(I, R) do{                                \ | ||||
|   if (I >= sizeof(page_states) / sizeof(page_states[0])) {  \ | ||||
|     fatal_error = true;                                     \ | ||||
|     return R;                                               \ | ||||
|   }                                                         \ | ||||
| }while(0) | ||||
|  | ||||
| #define CHECK_PAGE_STATE(I, R, S) do { \ | ||||
|   CHECK_PAGE(I, R);                    \ | ||||
|   if (page_states[I] != S) {           \ | ||||
|     fatal_error = true;                \ | ||||
|     return R;                          \ | ||||
|   }                                    \ | ||||
| }while(0) | ||||
|  | ||||
| namespace DirectStepping { | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   State SerialPageManager<Cfg>::state; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   volatile bool SerialPageManager<Cfg>::fatal_error; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   volatile PageState SerialPageManager<Cfg>::page_states[Cfg::NUM_PAGES]; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   volatile bool SerialPageManager<Cfg>::page_states_dirty; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   millis_t SerialPageManager<Cfg>::next_response; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   uint8_t SerialPageManager<Cfg>::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE]; | ||||
|            | ||||
|   template<typename Cfg> | ||||
|   uint8_t SerialPageManager<Cfg>::checksum; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_byte_idx; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   typename Cfg::page_idx_t SerialPageManager<Cfg>::write_page_idx; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_page_size; | ||||
|  | ||||
|   template <typename Cfg> | ||||
|   void SerialPageManager<Cfg>::init() { | ||||
|     for (int i = 0 ; i < Cfg::NUM_PAGES ; i++) | ||||
|       page_states[i] = PageState::FREE; | ||||
|  | ||||
|     fatal_error = false; | ||||
|     next_response = 0; | ||||
|     state = State::NEWLINE; | ||||
|  | ||||
|     page_states_dirty = false; | ||||
|  | ||||
|     SERIAL_ECHOLNPGM("pages_ready"); | ||||
|   } | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   FORCE_INLINE bool SerialPageManager<Cfg>::maybe_store_rxd_char(uint8_t c) { | ||||
|     switch (state) { | ||||
|       default: | ||||
|       case State::MONITOR: | ||||
|         switch (c) { | ||||
|           case '\n': | ||||
|           case '\r': | ||||
|             state = State::NEWLINE; | ||||
|           default: | ||||
|             return false; | ||||
|         } | ||||
|       case State::NEWLINE: | ||||
|         switch (c) { | ||||
|           case Cfg::CONTROL_CHAR: | ||||
|             state = State::ADDRESS; | ||||
|             return true; | ||||
|           case '\n': | ||||
|           case '\r': | ||||
|             state = State::NEWLINE; | ||||
|             return false; | ||||
|           default: | ||||
|             state = State::MONITOR; | ||||
|             return false; | ||||
|         } | ||||
|       case State::ADDRESS: | ||||
|         //TODO: 16 bit address, State::ADDRESS2 | ||||
|         write_page_idx = c; | ||||
|         write_byte_idx = 0; | ||||
|         checksum = 0; | ||||
|  | ||||
|         CHECK_PAGE(write_page_idx, true); | ||||
|  | ||||
|         if (page_states[write_page_idx] == PageState::FAIL) { | ||||
|           // Special case for fail | ||||
|           state = State::UNFAIL; | ||||
|           return true; | ||||
|         } | ||||
|  | ||||
|         set_page_state(write_page_idx, PageState::WRITING); | ||||
|  | ||||
|         state = Cfg::DIRECTIONAL ? State::COLLECT : State::SIZE; | ||||
|  | ||||
|         return true; | ||||
|       case State::SIZE: | ||||
|         // Zero means full page size | ||||
|         write_page_size = c; | ||||
|         state = State::COLLECT; | ||||
|         return true; | ||||
|       case State::COLLECT: | ||||
|         pages[write_page_idx][write_byte_idx++] = c; | ||||
|         checksum ^= c; | ||||
|  | ||||
|         // check if still collecting | ||||
|         if (Cfg::PAGE_SIZE == 256) { | ||||
|           // special case for 8-bit, check if rolled back to 0 | ||||
|           if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes | ||||
|             if (write_byte_idx) return true; | ||||
|           } else { | ||||
|             if (write_byte_idx < write_page_size) return true; | ||||
|           } | ||||
|         } else if (Cfg::DIRECTIONAL) { | ||||
|           if (write_byte_idx != Cfg::PAGE_SIZE) return true; | ||||
|         } else { | ||||
|           if (write_byte_idx < write_page_size) return true; | ||||
|         } | ||||
|  | ||||
|         state = State::CHECKSUM; | ||||
|         return true; | ||||
|       case State::CHECKSUM: { | ||||
|         const PageState page_state = (checksum == c) ? PageState::OK : PageState::FAIL; | ||||
|         set_page_state(write_page_idx, page_state); | ||||
|         state = State::MONITOR; | ||||
|         return true; | ||||
|       } | ||||
|       case State::UNFAIL: | ||||
|         if (c == 0) { | ||||
|           set_page_state(write_page_idx, PageState::FREE); | ||||
|         } else { | ||||
|           fatal_error = true; | ||||
|         } | ||||
|         state = State::MONITOR; | ||||
|         return true; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   template <typename Cfg> | ||||
|   void SerialPageManager<Cfg>::write_responses() { | ||||
|     if (fatal_error) { | ||||
|       kill(GET_TEXT(MSG_BAD_PAGE)); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     // Runs on a set interval also, as responses may get lost. | ||||
|     if (next_response && next_response < millis()) { | ||||
|       page_states_dirty = true; | ||||
|     } | ||||
|  | ||||
|     if (!page_states_dirty) return; | ||||
|  | ||||
|     page_states_dirty = false; | ||||
|     next_response = millis() + Cfg::RESPONSE_INTERVAL_MS; | ||||
|  | ||||
|     SERIAL_ECHO(Cfg::CONTROL_CHAR); | ||||
|     constexpr int state_bits = 2; | ||||
|     constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits; | ||||
|     volatile uint8_t bits_b[n_bytes] = { 0 }; | ||||
|  | ||||
|     for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) { | ||||
|       bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7); | ||||
|     } | ||||
|  | ||||
|     uint8_t crc = 0; | ||||
|     for (uint8_t i = 0 ; i < n_bytes ; i++) { | ||||
|       crc ^= bits_b[i]; | ||||
|       SERIAL_ECHO(bits_b[i]); | ||||
|     } | ||||
|  | ||||
|     SERIAL_ECHO(crc); | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
|  | ||||
|   template <typename Cfg> | ||||
|   FORCE_INLINE void SerialPageManager<Cfg>::set_page_state(const page_idx_t page_idx, const PageState page_state) { | ||||
|     CHECK_PAGE(page_idx,); | ||||
|  | ||||
|     page_states[page_idx] = page_state; | ||||
|     page_states_dirty = true; | ||||
|   } | ||||
|  | ||||
|   template <> | ||||
|   FORCE_INLINE uint8_t *PageManager::get_page(const page_idx_t page_idx) { | ||||
|     CHECK_PAGE(page_idx, nullptr); | ||||
|  | ||||
|     return pages[page_idx]; | ||||
|   } | ||||
|  | ||||
|   template <> | ||||
|   FORCE_INLINE void PageManager::free_page(const page_idx_t page_idx) { | ||||
|     set_page_state(page_idx, PageState::FREE); | ||||
|   } | ||||
|  | ||||
| }; | ||||
|  | ||||
| DirectStepping::PageManager page_manager; | ||||
|  | ||||
| const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS] PROGMEM = { | ||||
|  | ||||
|   #if STEPPER_PAGE_FORMAT == SP_4x4D_128 | ||||
|  | ||||
|     { 1, 1, 1, 1, 1, 1, 1, 0 }, //  0 = -7 | ||||
|     { 1, 1, 1, 0, 1, 1, 1, 0 }, //  1 = -6 | ||||
|     { 0, 1, 1, 0, 1, 0, 1, 1 }, //  2 = -5 | ||||
|     { 0, 1, 0, 1, 0, 1, 0, 1 }, //  3 = -4 | ||||
|     { 0, 1, 0, 0, 1, 0, 0, 1 }, //  4 = -3 | ||||
|     { 0, 0, 1, 0, 0, 0, 1, 0 }, //  5 = -2 | ||||
|     { 0, 0, 0, 0, 1, 0, 0, 0 }, //  6 = -1 | ||||
|     { 0, 0, 0, 0, 0, 0, 0, 0 }, //  7 =  0 | ||||
|     { 0, 0, 0, 0, 1, 0, 0, 0 }, //  8 =  1 | ||||
|     { 0, 0, 1, 0, 0, 0, 1, 0 }, //  9 =  2 | ||||
|     { 0, 1, 0, 0, 1, 0, 0, 1 }, // 10 =  3 | ||||
|     { 0, 1, 0, 1, 0, 1, 0, 1 }, // 11 =  4 | ||||
|     { 0, 1, 1, 0, 1, 0, 1, 1 }, // 12 =  5 | ||||
|     { 1, 1, 1, 0, 1, 1, 1, 0 }, // 13 =  6 | ||||
|     { 1, 1, 1, 1, 1, 1, 1, 0 }, // 14 =  7 | ||||
|     { 0 } | ||||
|  | ||||
|   #elif STEPPER_PAGE_FORMAT == SP_4x2_256 | ||||
|  | ||||
|     { 0, 0, 0, 0 }, // 0 | ||||
|     { 0, 1, 0, 0 }, // 1 | ||||
|     { 1, 0, 1, 0 }, // 2 | ||||
|     { 1, 1, 1, 0 }, // 3 | ||||
|  | ||||
|   #elif STEPPER_PAGE_FORMAT == SP_4x1_512 | ||||
|  | ||||
|     {0} // Uncompressed format, table not used | ||||
|  | ||||
|   #endif | ||||
|  | ||||
| }; | ||||
|  | ||||
| #endif // DIRECT_STEPPING | ||||
							
								
								
									
										137
									
								
								Marlin/src/feature/direct_stepping.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Marlin/src/feature/direct_stepping.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,137 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 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/>. | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
|  | ||||
| #include "../inc/MarlinConfig.h" | ||||
|  | ||||
| namespace DirectStepping { | ||||
|  | ||||
|   enum State : char { | ||||
|     MONITOR, NEWLINE, ADDRESS, SIZE, COLLECT, CHECKSUM, UNFAIL | ||||
|   }; | ||||
|  | ||||
|   enum PageState : uint8_t { | ||||
|     FREE, WRITING, OK, FAIL | ||||
|   }; | ||||
|  | ||||
|   // Static state used for stepping through direct stepping pages | ||||
|   struct page_step_state_t { | ||||
|     // Current page | ||||
|     uint8_t *page; | ||||
|     // Current segment | ||||
|     uint16_t segment_idx; | ||||
|     // Current steps within segment | ||||
|     uint8_t segment_steps; | ||||
|     // Segment delta | ||||
|     xyze_uint8_t sd; | ||||
|     // Block delta | ||||
|     xyze_int_t bd; | ||||
|   }; | ||||
|  | ||||
|   template<typename Cfg> | ||||
|   class SerialPageManager { | ||||
|   public: | ||||
|  | ||||
|     typedef typename Cfg::page_idx_t page_idx_t; | ||||
|  | ||||
|     static bool maybe_store_rxd_char(uint8_t c); | ||||
|     static void write_responses(); | ||||
|  | ||||
|     // common methods for page managers | ||||
|     static void init(); | ||||
|     static uint8_t *get_page(const page_idx_t page_idx); | ||||
|     static void free_page(const page_idx_t page_idx); | ||||
|  | ||||
|   protected: | ||||
|  | ||||
|     typedef typename Cfg::write_byte_idx_t write_byte_idx_t; | ||||
|  | ||||
|     static State state; | ||||
|     static volatile bool fatal_error; | ||||
|  | ||||
|     static volatile PageState page_states[Cfg::NUM_PAGES]; | ||||
|     static volatile bool page_states_dirty; | ||||
|     static millis_t next_response; | ||||
|  | ||||
|     static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE]; | ||||
|     static uint8_t checksum; | ||||
|     static write_byte_idx_t write_byte_idx; | ||||
|     static page_idx_t write_page_idx; | ||||
|     static write_byte_idx_t write_page_size; | ||||
|  | ||||
|     static void set_page_state(const page_idx_t page_idx, const PageState page_state); | ||||
|   }; | ||||
|  | ||||
|   template<bool b, typename T, typename F> struct TypeSelector { typedef T type;} ; | ||||
|   template<typename T, typename F> struct TypeSelector<false, T, F> { typedef F type; }; | ||||
|  | ||||
|   template <int num_pages, int num_axes, int bits_segment, bool dir, int segments> | ||||
|   struct config_t { | ||||
|     static constexpr char CONTROL_CHAR  = '!'; | ||||
|  | ||||
|     static constexpr int NUM_PAGES      = num_pages; | ||||
|     static constexpr int NUM_AXES       = num_axes; | ||||
|     static constexpr int BITS_SEGMENT   = bits_segment; | ||||
|     static constexpr int DIRECTIONAL    = dir ? 1 : 0; | ||||
|     static constexpr int SEGMENTS       = segments; | ||||
|  | ||||
|     static constexpr int RAW            = (BITS_SEGMENT == 1) ? 1 : 0; | ||||
|     static constexpr int NUM_SEGMENTS   = 1 << BITS_SEGMENT; | ||||
|     static constexpr int SEGMENT_STEPS  = 1 << (BITS_SEGMENT - DIRECTIONAL - RAW); | ||||
|     static constexpr int TOTAL_STEPS    = SEGMENT_STEPS * SEGMENTS; | ||||
|     static constexpr int PAGE_SIZE      = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8; | ||||
|  | ||||
|     static constexpr millis_t RESPONSE_INTERVAL_MS = 50; | ||||
|  | ||||
|     typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t; | ||||
|     typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t; | ||||
|   }; | ||||
|  | ||||
|   template <uint8_t num_pages> | ||||
|   using SP_4x4D_128 = config_t<num_pages, 4, 4, true,  128>; | ||||
|  | ||||
|   template <uint8_t num_pages> | ||||
|   using SP_4x2_256  = config_t<num_pages, 4, 2, false, 256>; | ||||
|  | ||||
|   template <uint8_t num_pages> | ||||
|   using SP_4x1_512  = config_t<num_pages, 4, 1, false, 512>; | ||||
|  | ||||
|   // configured types | ||||
|   typedef STEPPER_PAGE_FORMAT<STEPPER_PAGES> Config; | ||||
|  | ||||
|   template class PAGE_MANAGER<Config>; | ||||
|   typedef PAGE_MANAGER<Config> PageManager; | ||||
| }; | ||||
|  | ||||
| #define SP_4x4D_128 1 | ||||
| //#define SP_4x4_128 2 | ||||
| //#define SP_4x2D_256 3 | ||||
| #define SP_4x2_256 4 | ||||
| #define SP_4x1_512 5 | ||||
|  | ||||
| typedef typename DirectStepping::Config::page_idx_t page_idx_t; | ||||
|  | ||||
| // TODO: use config | ||||
| typedef DirectStepping::page_step_state_t page_step_state_t; | ||||
|  | ||||
| extern const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS]; | ||||
| extern DirectStepping::PageManager page_manager; | ||||
| @@ -261,6 +261,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { | ||||
|         case 5: G5(); break;                                      // G5: Cubic B_spline | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(DIRECT_STEPPING) | ||||
|         case 6: G6(); break;                                      // G6: Direct Stepper Move | ||||
|       #endif | ||||
|  | ||||
|       #if ENABLED(FWRETRACT) | ||||
|         case 10: G10(); break;                                    // G10: Retract / Swap Retract | ||||
|         case 11: G11(); break;                                    // G11: Recover / Swap Recover | ||||
|   | ||||
| @@ -402,6 +402,8 @@ private: | ||||
|  | ||||
|   TERN_(BEZIER_CURVE_SUPPORT, static void G5()); | ||||
|  | ||||
|   TERN_(DIRECT_STEPPING, static void G6()); | ||||
|  | ||||
|   #if ENABLED(FWRETRACT) | ||||
|     static void G10(); | ||||
|     static void G11(); | ||||
|   | ||||
| @@ -99,5 +99,7 @@ void GcodeSuite::G92() { | ||||
|   if    (sync_XYZ) sync_plan_position(); | ||||
|   else if (sync_E) sync_plan_position_e(); | ||||
|  | ||||
|   report_current_position(); | ||||
|   #if DISABLED(DIRECT_STEPPING) | ||||
|     report_current_position(); | ||||
|   #endif | ||||
| } | ||||
|   | ||||
							
								
								
									
										61
									
								
								Marlin/src/gcode/motion/G6.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								Marlin/src/gcode/motion/G6.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| /** | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 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 "../../inc/MarlinConfig.h" | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|  | ||||
| #include "../../feature/direct_stepping.h" | ||||
|  | ||||
| #include "../gcode.h" | ||||
| #include "../../module/planner.h" | ||||
|  | ||||
| /** | ||||
|  * G6: Direct Stepper Move | ||||
|  */ | ||||
| void GcodeSuite::G6() { | ||||
|   // TODO: feedrate support? | ||||
|   if (parser.seen('R')) | ||||
|     planner.last_page_step_rate = parser.value_ulong(); | ||||
|  | ||||
|   if (!DirectStepping::Config::DIRECTIONAL) { | ||||
|     if (parser.seen('X')) planner.last_page_dir.x = !!parser.value_byte(); | ||||
|     if (parser.seen('Y')) planner.last_page_dir.y = !!parser.value_byte(); | ||||
|     if (parser.seen('Z')) planner.last_page_dir.z = !!parser.value_byte(); | ||||
|     if (parser.seen('E')) planner.last_page_dir.e = !!parser.value_byte(); | ||||
|   } | ||||
|  | ||||
|   // No index means we just set the state | ||||
|   if (!parser.seen('I')) return; | ||||
|  | ||||
|   // No speed is set, can't schedule the move | ||||
|   if (!planner.last_page_step_rate) return; | ||||
|  | ||||
|   const page_idx_t page_idx = (page_idx_t) parser.value_ulong(); | ||||
|  | ||||
|   uint16_t num_steps = DirectStepping::Config::TOTAL_STEPS; | ||||
|   if (parser.seen('S')) num_steps = parser.value_ushort(); | ||||
|  | ||||
|   planner.buffer_page(page_idx, 0, num_steps); | ||||
|   reset_stepper_timeout(); | ||||
| } | ||||
|  | ||||
| #endif // DIRECT_STEPPING | ||||
| @@ -323,6 +323,18 @@ | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   #ifndef STEPPER_PAGES | ||||
|     #define STEPPER_PAGES 16 | ||||
|   #endif | ||||
|   #ifndef STEPPER_PAGE_FORMAT | ||||
|     #define STEPPER_PAGE_FORMAT SP_4x2_256 | ||||
|   #endif | ||||
|   #ifndef PAGE_MANAGER | ||||
|     #define PAGE_MANAGER SerialPageManager | ||||
|   #endif | ||||
| #endif | ||||
|  | ||||
| // | ||||
| // SD Card connection methods | ||||
| // Defined here so pins and sanity checks can use them | ||||
|   | ||||
| @@ -2923,3 +2923,12 @@ static_assert(   _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2) | ||||
| #if SAVED_POSITIONS > 256 | ||||
|   #error "SAVED_POSITIONS must be an integer from 0 to 256." | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Sanity checks for stepper chunk support | ||||
|  */ | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   #if ENABLED(LIN_ADVANCE) | ||||
|     #error "DIRECT_STEPPING is incompatible with LIN_ADVANCE. Enable in external planner if possible." | ||||
|   #endif | ||||
| #endif | ||||
|   | ||||
| @@ -577,6 +577,9 @@ namespace Language_en { | ||||
|   PROGMEM Language_Str MSG_SNAKE                           = _UxGT("Sn4k3"); | ||||
|   PROGMEM Language_Str MSG_MAZE                            = _UxGT("Maze"); | ||||
|  | ||||
|   PROGMEM Language_Str MSG_BAD_PAGE                        = _UxGT("Bad page index"); | ||||
|   PROGMEM Language_Str MSG_BAD_PAGE_SPEED                  = _UxGT("Bad page speed"); | ||||
|  | ||||
|   // | ||||
|   // Filament Change screens show up to 3 lines on a 4-line display | ||||
|   //                        ...or up to 2 lines on a 3-line display | ||||
|   | ||||
| @@ -151,6 +151,11 @@ float Planner::steps_to_mm[XYZE_N];             // (mm) Millimeters per step | ||||
|   uint8_t Planner::last_extruder = 0;     // Respond to extruder change | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   uint32_t Planner::last_page_step_rate = 0; | ||||
|   xyze_bool_t Planner::last_page_dir{0}; | ||||
| #endif | ||||
|  | ||||
| #if EXTRUDERS | ||||
|   int16_t Planner::flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100); // Extrusion factor for each extruder | ||||
|   float Planner::e_factor[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(1.0f); // The flow percentage and volumetric multiplier combine to scale E movement | ||||
| @@ -235,6 +240,10 @@ void Planner::init() { | ||||
|   TERN_(ABL_PLANAR, bed_level_matrix.set_to_identity()); | ||||
|   clear_block_buffer(); | ||||
|   delay_before_delivering = 0; | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     last_page_step_rate = 0; | ||||
|     last_page_dir.reset(); | ||||
|   #endif | ||||
| } | ||||
|  | ||||
| #if ENABLED(S_CURVE_ACCELERATION) | ||||
| @@ -906,7 +915,7 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e | ||||
|       streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the | ||||
|       planner buffer that don't change with the addition of a new block, as describe above. In addition, | ||||
|       this block can never be less than block_buffer_tail and will always be pushed forward and maintain | ||||
|       this requirement when encountered by the Planner::discard_current_block() routine during a cycle. | ||||
|       this requirement when encountered by the Planner::release_current_block() routine during a cycle. | ||||
|  | ||||
|   NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short | ||||
|   line segments, like G2/3 arcs or complex curves, may seem to move slow. This is because there simply isn't | ||||
| @@ -994,8 +1003,8 @@ void Planner::reverse_pass() { | ||||
|     // Perform the reverse pass | ||||
|     block_t *current = &block_buffer[block_index]; | ||||
|  | ||||
|     // Only consider non sync blocks | ||||
|     if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) { | ||||
|     // Only consider non sync and page blocks | ||||
|     if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(current)) { | ||||
|       reverse_pass_kernel(current, next); | ||||
|       next = current; | ||||
|     } | ||||
| @@ -1089,8 +1098,8 @@ void Planner::forward_pass() { | ||||
|     // Perform the forward pass | ||||
|     block = &block_buffer[block_index]; | ||||
|  | ||||
|     // Skip SYNC blocks | ||||
|     if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION)) { | ||||
|     // Skip SYNC and page blocks | ||||
|     if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(block)) { | ||||
|       // If there's no previous block or the previous block is not | ||||
|       // BUSY (thus, modifiable) run the forward_pass_kernel. Otherwise, | ||||
|       // the previous block became BUSY, so assume the current block's | ||||
| @@ -1139,8 +1148,8 @@ void Planner::recalculate_trapezoids() { | ||||
|  | ||||
|     next = &block_buffer[block_index]; | ||||
|  | ||||
|     // Skip sync blocks | ||||
|     if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION)) { | ||||
|     // Skip sync and page blocks | ||||
|     if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(next)) { | ||||
|       next_entry_speed = SQRT(next->entry_speed_sqr); | ||||
|  | ||||
|       if (block) { | ||||
| @@ -2717,6 +2726,69 @@ bool Planner::buffer_line(const float &rx, const float &ry, const float &rz, con | ||||
|   #endif | ||||
| } // buffer_line() | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|  | ||||
|   void Planner::buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps) { | ||||
|     if (!last_page_step_rate) { | ||||
|       kill(GET_TEXT(MSG_BAD_PAGE_SPEED)); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     uint8_t next_buffer_head; | ||||
|     block_t * const block = get_next_free_block(next_buffer_head); | ||||
|  | ||||
|     block->flag = BLOCK_FLAG_IS_PAGE; | ||||
|  | ||||
|     #if FAN_COUNT > 0 | ||||
|       FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i]; | ||||
|     #endif | ||||
|  | ||||
|     #if EXTRUDERS > 1 | ||||
|       block->extruder = extruder; | ||||
|     #endif | ||||
|  | ||||
|     block->page_idx = page_idx; | ||||
|  | ||||
|     block->step_event_count = num_steps; | ||||
|     block->initial_rate = | ||||
|       block->final_rate = | ||||
|       block->nominal_rate = last_page_step_rate; // steps/s | ||||
|  | ||||
|     block->accelerate_until = 0; | ||||
|     block->decelerate_after = block->step_event_count; | ||||
|  | ||||
|     // Will be set to last direction later if directional format. | ||||
|     block->direction_bits = 0; | ||||
|  | ||||
|     #define PAGE_UPDATE_DIR(AXIS) \ | ||||
|       if (!last_page_dir[_AXIS(AXIS)]) SBI(block->direction_bits, _AXIS(AXIS)); | ||||
|  | ||||
|     if (!DirectStepping::Config::DIRECTIONAL) { | ||||
|       PAGE_UPDATE_DIR(X); | ||||
|       PAGE_UPDATE_DIR(Y); | ||||
|       PAGE_UPDATE_DIR(Z); | ||||
|       PAGE_UPDATE_DIR(E); | ||||
|     } | ||||
|  | ||||
|     // If this is the first added movement, reload the delay, otherwise, cancel it. | ||||
|     if (block_buffer_head == block_buffer_tail) { | ||||
|       // If it was the first queued block, restart the 1st block delivery delay, to | ||||
|       // give the planner an opportunity to queue more movements and plan them | ||||
|       // As there are no queued movements, the Stepper ISR will not touch this | ||||
|       // variable, so there is no risk setting this here (but it MUST be done | ||||
|       // before the following line!!) | ||||
|       delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE; | ||||
|     } | ||||
|  | ||||
|     // Move buffer head | ||||
|     block_buffer_head = next_buffer_head; | ||||
|  | ||||
|     enable_all_steppers(); | ||||
|     stepper.wake_up(); | ||||
|   } | ||||
|  | ||||
| #endif // DIRECT_STEPPING | ||||
|  | ||||
| /** | ||||
|  * Directly set the planner ABC position (and stepper positions) | ||||
|  * converting mm (or angles for SCARA) into steps. | ||||
|   | ||||
| @@ -66,6 +66,13 @@ | ||||
|   #include "../feature/spindle_laser_types.h" | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   #include "../feature/direct_stepping.h" | ||||
|   #define IS_PAGE(B) TEST(B->flag, BLOCK_BIT_IS_PAGE) | ||||
| #else | ||||
|   #define IS_PAGE(B) false | ||||
| #endif | ||||
|  | ||||
| // Feedrate for manual moves | ||||
| #ifdef MANUAL_FEEDRATE | ||||
|   constexpr xyze_feedrate_t _mf = MANUAL_FEEDRATE, | ||||
| @@ -90,13 +97,21 @@ enum BlockFlagBit : char { | ||||
|  | ||||
|   // Sync the stepper counts from the block | ||||
|   BLOCK_BIT_SYNC_POSITION | ||||
|  | ||||
|   // Direct stepping page | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     , BLOCK_BIT_IS_PAGE | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| enum BlockFlag : char { | ||||
|   BLOCK_FLAG_RECALCULATE          = _BV(BLOCK_BIT_RECALCULATE), | ||||
|   BLOCK_FLAG_NOMINAL_LENGTH       = _BV(BLOCK_BIT_NOMINAL_LENGTH), | ||||
|   BLOCK_FLAG_CONTINUED            = _BV(BLOCK_BIT_CONTINUED), | ||||
|   BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION) | ||||
|     BLOCK_FLAG_RECALCULATE          = _BV(BLOCK_BIT_RECALCULATE) | ||||
|   , BLOCK_FLAG_NOMINAL_LENGTH       = _BV(BLOCK_BIT_NOMINAL_LENGTH) | ||||
|   , BLOCK_FLAG_CONTINUED            = _BV(BLOCK_BIT_CONTINUED) | ||||
|   , BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION) | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     , BLOCK_FLAG_IS_PAGE            = _BV(BLOCK_BIT_IS_PAGE) | ||||
|   #endif | ||||
| }; | ||||
|  | ||||
| #if ENABLED(LASER_POWER_INLINE) | ||||
| @@ -180,6 +195,10 @@ typedef struct block_t { | ||||
|            final_rate,                      // The minimal rate at exit | ||||
|            acceleration_steps_per_s2;       // acceleration steps/sec^2 | ||||
|  | ||||
|   #if ENABLED(DIRECT_STEPPING) | ||||
|     page_idx_t page_idx;                    // Page index used for direct stepping | ||||
|   #endif | ||||
|  | ||||
|   #if HAS_CUTTER | ||||
|     cutter_power_t cutter_power;            // Power level for Spindle, Laser, etc. | ||||
|   #endif | ||||
| @@ -296,6 +315,11 @@ class Planner { | ||||
|       static uint8_t last_extruder;                 // Respond to extruder change | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(DIRECT_STEPPING) | ||||
|       static uint32_t last_page_step_rate;          // Last page step rate given | ||||
|       static xyze_bool_t last_page_dir;             // Last page direction given | ||||
|     #endif | ||||
|  | ||||
|     #if EXTRUDERS | ||||
|       static int16_t flow_percentage[EXTRUDERS];    // Extrusion factor for each extruder | ||||
|       static float e_factor[EXTRUDERS];             // The flow percentage and volumetric multiplier combine to scale E movement | ||||
| @@ -726,6 +750,10 @@ class Planner { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     #if ENABLED(DIRECT_STEPPING) | ||||
|       static void buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps); | ||||
|     #endif | ||||
|  | ||||
|     /** | ||||
|      * Set the planner.position and individual stepper positions. | ||||
|      * Used by G92, G28, G29, and other procedures. | ||||
| @@ -811,10 +839,10 @@ class Planner { | ||||
|     static block_t* get_current_block(); | ||||
|  | ||||
|     /** | ||||
|      * "Discard" the block and "release" the memory. | ||||
|      * "Release" the current block so its slot can be reused. | ||||
|      * Called when the current block is no longer needed. | ||||
|      */ | ||||
|     FORCE_INLINE static void discard_current_block() { | ||||
|     FORCE_INLINE static void release_current_block() { | ||||
|       if (has_blocks_queued()) | ||||
|         block_buffer_tail = next_block_index(block_buffer_tail); | ||||
|     } | ||||
|   | ||||
| @@ -230,6 +230,10 @@ uint32_t Stepper::advance_divisor = 0, | ||||
|   uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER; | ||||
| #endif | ||||
|  | ||||
| #if ENABLED(DIRECT_STEPPING) | ||||
|   page_step_state_t Stepper::page_step_state; | ||||
| #endif | ||||
|  | ||||
| int32_t Stepper::ticks_nominal = -1; | ||||
| #if DISABLED(S_CURVE_ACCELERATION) | ||||
|   uint32_t Stepper::acc_step_rate; // needed for deceleration start point | ||||
| @@ -1520,11 +1524,7 @@ void Stepper::pulse_phase_isr() { | ||||
|   // If we must abort the current block, do so! | ||||
|   if (abort_current_block) { | ||||
|     abort_current_block = false; | ||||
|     if (current_block) { | ||||
|       axis_did_move = 0; | ||||
|       current_block = nullptr; | ||||
|       planner.discard_current_block(); | ||||
|     } | ||||
|     if (current_block) discard_current_block(); | ||||
|   } | ||||
|  | ||||
|   // If there is no current block, do nothing | ||||
| @@ -1558,46 +1558,160 @@ void Stepper::pulse_phase_isr() { | ||||
|       } \ | ||||
|     }while(0) | ||||
|  | ||||
|     // Start an active pulse, if Bresenham says so, and update position | ||||
|     // Start an active pulse if needed | ||||
|     #define PULSE_START(AXIS) do{ \ | ||||
|       if (step_needed[_AXIS(AXIS)]) { \ | ||||
|         _APPLY_STEP(AXIS, !_INVERT_STEP_PIN(AXIS), 0); \ | ||||
|       } \ | ||||
|     }while(0) | ||||
|  | ||||
|     // Stop an active pulse, if any, and adjust error term | ||||
|     // Stop an active pulse if needed | ||||
|     #define PULSE_STOP(AXIS) do { \ | ||||
|       if (step_needed[_AXIS(AXIS)]) { \ | ||||
|         _APPLY_STEP(AXIS, _INVERT_STEP_PIN(AXIS), 0); \ | ||||
|       } \ | ||||
|     }while(0) | ||||
|  | ||||
|     // Determine if pulses are needed | ||||
|     #if HAS_X_STEP | ||||
|       PULSE_PREP(X); | ||||
|     #endif | ||||
|     #if HAS_Y_STEP | ||||
|       PULSE_PREP(Y); | ||||
|     #endif | ||||
|     #if HAS_Z_STEP | ||||
|       PULSE_PREP(Z); | ||||
|     #endif | ||||
|     // Direct Stepping page? | ||||
|     const bool is_page = IS_PAGE(current_block); | ||||
|  | ||||
|     #if ENABLED(DIRECT_STEPPING) | ||||
|  | ||||
|       if (is_page) { | ||||
|  | ||||
|         #if STEPPER_PAGE_FORMAT == SP_4x4D_128 | ||||
|  | ||||
|           #define PAGE_SEGMENT_UPDATE(AXIS, VALUE, MID) do{ \ | ||||
|                  if ((VALUE) == MID) {}                     \ | ||||
|             else if ((VALUE) <  MID) SBI(dm, _AXIS(AXIS));  \ | ||||
|             else                     CBI(dm, _AXIS(AXIS));  \ | ||||
|             page_step_state.sd[_AXIS(AXIS)] = VALUE;        \ | ||||
|             page_step_state.bd[_AXIS(AXIS)] += VALUE;       \ | ||||
|           }while(0) | ||||
|  | ||||
|           #define PAGE_PULSE_PREP(AXIS) do{ \ | ||||
|             step_needed[_AXIS(AXIS)] =      \ | ||||
|               pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x7]); \ | ||||
|           }while(0) | ||||
|  | ||||
|           switch (page_step_state.segment_steps) { | ||||
|             case 8: | ||||
|               page_step_state.segment_idx += 2; | ||||
|               page_step_state.segment_steps = 0; | ||||
|               // fallthru | ||||
|             case 0: { | ||||
|               const uint8_t low = page_step_state.page[page_step_state.segment_idx], | ||||
|                            high = page_step_state.page[page_step_state.segment_idx + 1]; | ||||
|               uint8_t dm = last_direction_bits; | ||||
|  | ||||
|               PAGE_SEGMENT_UPDATE(X, low >> 4, 7); | ||||
|               PAGE_SEGMENT_UPDATE(Y, low & 0xF, 7); | ||||
|               PAGE_SEGMENT_UPDATE(Z, high >> 4, 7); | ||||
|               PAGE_SEGMENT_UPDATE(E, high & 0xF, 7); | ||||
|  | ||||
|               if (dm != last_direction_bits) { | ||||
|                 last_direction_bits = dm; | ||||
|                 set_directions(); | ||||
|               } | ||||
|             } break; | ||||
|  | ||||
|             default: break; | ||||
|           } | ||||
|  | ||||
|           PAGE_PULSE_PREP(X), | ||||
|           PAGE_PULSE_PREP(Y), | ||||
|           PAGE_PULSE_PREP(Z), | ||||
|           PAGE_PULSE_PREP(E); | ||||
|  | ||||
|           page_step_state.segment_steps++; | ||||
|  | ||||
|         #elif STEPPER_PAGE_FORMAT == SP_4x2_256 | ||||
|  | ||||
|           #define PAGE_SEGMENT_UPDATE(AXIS, VALUE) \ | ||||
|             page_step_state.sd[_AXIS(AXIS)] = VALUE; \ | ||||
|             page_step_state.bd[_AXIS(AXIS)] += VALUE; | ||||
|  | ||||
|           #define PAGE_PULSE_PREP(AXIS) do{ \ | ||||
|             step_needed[_AXIS(AXIS)] =      \ | ||||
|               pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x3]); \ | ||||
|           }while(0) | ||||
|  | ||||
|           switch (page_step_state.segment_steps) { | ||||
|             case 4: | ||||
|               page_step_state.segment_idx++; | ||||
|               page_step_state.segment_steps = 0; | ||||
|               // fallthru | ||||
|             case 0: { | ||||
|               const uint8_t b = page_step_state.page[page_step_state.segment_idx]; | ||||
|               PAGE_SEGMENT_UPDATE(X, (b >> 6) & 0x3); | ||||
|               PAGE_SEGMENT_UPDATE(Y, (b >> 4) & 0x3); | ||||
|               PAGE_SEGMENT_UPDATE(Z, (b >> 2) & 0x3); | ||||
|               PAGE_SEGMENT_UPDATE(E, (b >> 0) & 0x3); | ||||
|             } break; | ||||
|             default: break; | ||||
|           } | ||||
|  | ||||
|           PAGE_PULSE_PREP(X); | ||||
|           PAGE_PULSE_PREP(Y); | ||||
|           PAGE_PULSE_PREP(Z); | ||||
|           PAGE_PULSE_PREP(E); | ||||
|  | ||||
|           page_step_state.segment_steps++; | ||||
|  | ||||
|         #elif STEPPER_PAGE_FORMAT == SP_4x1_512 | ||||
|  | ||||
|           #define PAGE_PULSE_PREP(AXIS, BITS) do{             \ | ||||
|             step_needed[_AXIS(AXIS)] = (steps >> BITS) & 0x1; \ | ||||
|             if (step_needed[_AXIS(AXIS)])                     \ | ||||
|               page_step_state.bd[_AXIS(AXIS)]++;              \ | ||||
|           }while(0) | ||||
|  | ||||
|           uint8_t steps = page_step_state.page[page_step_state.segment_idx >> 1]; | ||||
|  | ||||
|           if (page_step_state.segment_idx & 0x1) steps >>= 4; | ||||
|  | ||||
|           PAGE_PULSE_PREP(X, 3); | ||||
|           PAGE_PULSE_PREP(Y, 2); | ||||
|           PAGE_PULSE_PREP(Z, 1); | ||||
|           PAGE_PULSE_PREP(E, 0); | ||||
|  | ||||
|           page_step_state.segment_idx++; | ||||
|  | ||||
|     #if EITHER(LIN_ADVANCE, MIXING_EXTRUDER) | ||||
|       delta_error.e += advance_dividend.e; | ||||
|       if (delta_error.e >= 0) { | ||||
|         count_position.e += count_direction.e; | ||||
|         #if ENABLED(LIN_ADVANCE) | ||||
|           delta_error.e -= advance_divisor; | ||||
|           // Don't step E here - But remember the number of steps to perform | ||||
|           motor_direction(E_AXIS) ? --LA_steps : ++LA_steps; | ||||
|         #else | ||||
|           step_needed.e = true; | ||||
|           #error "Unknown direct stepping page format!" | ||||
|         #endif | ||||
|       } | ||||
|     #elif HAS_E0_STEP | ||||
|       PULSE_PREP(E); | ||||
|     #endif | ||||
|  | ||||
|     #endif // DIRECT_STEPPING | ||||
|  | ||||
|     if (!is_page) { | ||||
|       // Determine if pulses are needed | ||||
|       #if HAS_X_STEP | ||||
|         PULSE_PREP(X); | ||||
|       #endif | ||||
|       #if HAS_Y_STEP | ||||
|         PULSE_PREP(Y); | ||||
|       #endif | ||||
|       #if HAS_Z_STEP | ||||
|         PULSE_PREP(Z); | ||||
|       #endif | ||||
|  | ||||
|       #if EITHER(LIN_ADVANCE, MIXING_EXTRUDER) | ||||
|         delta_error.e += advance_dividend.e; | ||||
|         if (delta_error.e >= 0) { | ||||
|           count_position.e += count_direction.e; | ||||
|           #if ENABLED(LIN_ADVANCE) | ||||
|             delta_error.e -= advance_divisor; | ||||
|             // Don't step E here - But remember the number of steps to perform | ||||
|             motor_direction(E_AXIS) ? --LA_steps : ++LA_steps; | ||||
|           #else | ||||
|             step_needed.e = true; | ||||
|           #endif | ||||
|         } | ||||
|       #elif HAS_E0_STEP | ||||
|         PULSE_PREP(E); | ||||
|       #endif | ||||
|     } | ||||
|  | ||||
|     #if ISR_MULTI_STEPS | ||||
|       if (firstStep) | ||||
| @@ -1676,14 +1790,28 @@ uint32_t Stepper::block_phase_isr() { | ||||
|   // If there is a current block | ||||
|   if (current_block) { | ||||
|  | ||||
|     // If current block is finished, reset pointer | ||||
|     // If current block is finished, reset pointer and finalize state | ||||
|     if (step_events_completed >= step_event_count) { | ||||
|       #if ENABLED(DIRECT_STEPPING) | ||||
|         #if STEPPER_PAGE_FORMAT == SP_4x4D_128 | ||||
|           #define PAGE_SEGMENT_UPDATE_POS(AXIS) \ | ||||
|             count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] - 128 * 7; | ||||
|         #elif STEPPER_PAGE_FORMAT == SP_4x1_512 || STEPPER_PAGE_FORMAT == SP_4x2_256 | ||||
|           #define PAGE_SEGMENT_UPDATE_POS(AXIS) \ | ||||
|             count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] * count_direction[_AXIS(AXIS)]; | ||||
|         #endif | ||||
|  | ||||
|         if (IS_PAGE(current_block)) { | ||||
|           PAGE_SEGMENT_UPDATE_POS(X); | ||||
|           PAGE_SEGMENT_UPDATE_POS(Y); | ||||
|           PAGE_SEGMENT_UPDATE_POS(Z); | ||||
|           PAGE_SEGMENT_UPDATE_POS(E); | ||||
|         } | ||||
|       #endif | ||||
|       #ifdef FILAMENT_RUNOUT_DISTANCE_MM | ||||
|         runout.block_completed(current_block); | ||||
|       #endif | ||||
|       axis_did_move = 0; | ||||
|       current_block = nullptr; | ||||
|       planner.discard_current_block(); | ||||
|       discard_current_block(); | ||||
|     } | ||||
|     else { | ||||
|       // Step events not completed yet... | ||||
| @@ -1867,7 +1995,7 @@ uint32_t Stepper::block_phase_isr() { | ||||
|       // Sync block? Sync the stepper counts and return | ||||
|       while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) { | ||||
|         _set_position(current_block->position); | ||||
|         planner.discard_current_block(); | ||||
|         discard_current_block(); | ||||
|  | ||||
|         // Try to get a new block | ||||
|         if (!(current_block = planner.get_current_block())) | ||||
| @@ -1878,6 +2006,23 @@ uint32_t Stepper::block_phase_isr() { | ||||
|  | ||||
|       TERN_(POWER_LOSS_RECOVERY, recovery.info.sdpos = current_block->sdpos); | ||||
|  | ||||
|       #if ENABLED(DIRECT_STEPPING) | ||||
|         if (IS_PAGE(current_block)) { | ||||
|           page_step_state.segment_steps = 0; | ||||
|           page_step_state.segment_idx = 0; | ||||
|           page_step_state.page = page_manager.get_page(current_block->page_idx); | ||||
|           page_step_state.bd.reset(); | ||||
|  | ||||
|           if (DirectStepping::Config::DIRECTIONAL) | ||||
|             current_block->direction_bits = last_direction_bits; | ||||
|  | ||||
|           if (!page_step_state.page) { | ||||
|             discard_current_block(); | ||||
|             return interval; | ||||
|           } | ||||
|         } | ||||
|       #endif | ||||
|  | ||||
|       // Flag all moving axes for proper endstop handling | ||||
|  | ||||
|       #if IS_CORE | ||||
|   | ||||
| @@ -334,6 +334,10 @@ class Stepper { | ||||
|       static uint32_t nextBabystepISR; | ||||
|     #endif | ||||
|  | ||||
|     #if ENABLED(DIRECT_STEPPING) | ||||
|       static page_step_state_t page_step_state; | ||||
|     #endif | ||||
|  | ||||
|     static int32_t ticks_nominal; | ||||
|     #if DISABLED(S_CURVE_ACCELERATION) | ||||
|       static uint32_t acc_step_rate; // needed for deceleration start point | ||||
| @@ -426,6 +430,17 @@ class Stepper { | ||||
|     static void report_a_position(const xyz_long_t &pos); | ||||
|     static void report_positions(); | ||||
|  | ||||
|     // Discard current block and free any resources | ||||
|     FORCE_INLINE static void discard_current_block() { | ||||
|       #if ENABLED(DIRECT_STEPPING) | ||||
|         if (IS_PAGE(current_block)) | ||||
|           page_manager.free_page(current_block->page_idx); | ||||
|       #endif | ||||
|       current_block = nullptr; | ||||
|       axis_did_move = 0; | ||||
|       planner.release_current_block(); | ||||
|     } | ||||
|  | ||||
|     // Quickly stop all steppers | ||||
|     FORCE_INLINE static void quick_stop() { abort_current_block = true; } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user