Browse Source

G6 Direct Stepping (#17853)

Colin Godsey 4 years ago
parent
commit
8a22ef0c83
No account linked to committer's email address

+ 18
- 6
Marlin/Configuration_adv.h View File

@@ -1664,6 +1664,16 @@
1664 1664
 //#define BEZIER_CURVE_SUPPORT
1665 1665
 
1666 1666
 /**
1667
+ * Direct Stepping
1668
+ *
1669
+ * Comparable to the method used by Klipper, G6 direct stepping significantly
1670
+ * reduces motion calculations, increases top printing speeds, and results in
1671
+ * less step aliasing by calculating all motions in advance.
1672
+ * Preparing your G-code: https://github.com/colinrgodsey/step-daemon
1673
+ */
1674
+//#define DIRECT_STEPPING
1675
+
1676
+/**
1667 1677
  * G38 Probe Target
1668 1678
  *
1669 1679
  * This option adds G38.2 and G38.3 (probe towards target)
@@ -1731,14 +1741,16 @@
1731 1741
 //================================= Buffers =================================
1732 1742
 //===========================================================================
1733 1743
 
1734
-// @section hidden
1744
+// @section motion
1735 1745
 
1736
-// The number of linear motions that can be in the plan at any give time.
1737
-// 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.
1738
-#if ENABLED(SDSUPPORT)
1739
-  #define BLOCK_BUFFER_SIZE 16 // SD,LCD,Buttons take more memory, block buffer needs to be smaller
1746
+// The number of lineear moves that can be in the planner at once.
1747
+// The value of BLOCK_BUFFER_SIZE must be a power of 2 (e.g. 8, 16, 32)
1748
+#if BOTH(SDSUPPORT, DIRECT_STEPPING)
1749
+  #define BLOCK_BUFFER_SIZE  8
1750
+#elif ENABLED(SDSUPPORT)
1751
+  #define BLOCK_BUFFER_SIZE 16
1740 1752
 #else
1741
-  #define BLOCK_BUFFER_SIZE 16 // maximize block buffer
1753
+  #define BLOCK_BUFFER_SIZE 16
1742 1754
 #endif
1743 1755
 
1744 1756
 // @section serial

+ 16
- 8
Marlin/src/HAL/AVR/MarlinSerial.cpp View File

@@ -43,6 +43,10 @@
43 43
   #include "MarlinSerial.h"
44 44
   #include "../../MarlinCore.h"
45 45
 
46
+  #if ENABLED(DIRECT_STEPPING)
47
+    #include "../../feature/direct_stepping.h"
48
+  #endif
49
+
46 50
   template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_r MarlinSerial<Cfg>::rx_buffer = { 0, 0, { 0 } };
47 51
   template<typename Cfg> typename MarlinSerial<Cfg>::ring_buffer_t MarlinSerial<Cfg>::tx_buffer = { 0 };
48 52
   template<typename Cfg> bool     MarlinSerial<Cfg>::_written = false;
@@ -131,6 +135,18 @@
131 135
 
132 136
     static EmergencyParser::State emergency_state; // = EP_RESET
133 137
 
138
+    // This must read the R_UCSRA register before reading the received byte to detect error causes
139
+    if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
140
+    if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
141
+    if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
142
+
143
+    // Read the character from the USART
144
+    uint8_t c = R_UDR;
145
+
146
+    #if ENABLED(DIRECT_STEPPING)
147
+      if (page_manager.maybe_store_rxd_char(c)) return;
148
+    #endif
149
+
134 150
     // Get the tail - Nothing can alter its value while this ISR is executing, but there's
135 151
     // a chance that this ISR interrupted the main process while it was updating the index.
136 152
     // The backup mechanism ensures the correct value is always returned.
@@ -142,14 +158,6 @@
142 158
     // Get the next element
143 159
     ring_buffer_pos_t i = (ring_buffer_pos_t)(h + 1) & (ring_buffer_pos_t)(Cfg::RX_SIZE - 1);
144 160
 
145
-    // This must read the R_UCSRA register before reading the received byte to detect error causes
146
-    if (Cfg::DROPPED_RX && B_DOR && !++rx_dropped_bytes) --rx_dropped_bytes;
147
-    if (Cfg::RX_OVERRUNS && B_DOR && !++rx_buffer_overruns) --rx_buffer_overruns;
148
-    if (Cfg::RX_FRAMING_ERRORS && B_FE && !++rx_framing_errors) --rx_framing_errors;
149
-
150
-    // Read the character from the USART
151
-    uint8_t c = R_UDR;
152
-
153 161
     if (Cfg::EMERGENCYPARSER) emergency_parser.update(emergency_state, c);
154 162
 
155 163
     // If the character is to be stored at the index just before the tail

+ 11
- 0
Marlin/src/MarlinCore.cpp View File

@@ -59,6 +59,10 @@
59 59
 #include "gcode/parser.h"
60 60
 #include "gcode/queue.h"
61 61
 
62
+#if ENABLED(DIRECT_STEPPING)
63
+  #include "feature/direct_stepping.h"
64
+#endif
65
+
62 66
 #if ENABLED(TOUCH_BUTTONS)
63 67
   #include "feature/touch/xpt2046.h"
64 68
 #endif
@@ -713,6 +717,9 @@ void idle(TERN_(ADVANCED_PAUSE_FEATURE, bool no_stepper_sleep/*=false*/)) {
713 717
 
714 718
   // Handle Joystick jogging
715 719
   TERN_(POLL_JOG, joystick.inject_jog_moves());
720
+
721
+  // Direct Stepping
722
+  TERN_(DIRECT_STEPPING, page_manager.write_responses());
716 723
 }
717 724
 
718 725
 /**
@@ -1124,6 +1131,10 @@ void setup() {
1124 1131
     SETUP_RUN(max7219.init());
1125 1132
   #endif
1126 1133
 
1134
+  #if ENABLED(DIRECT_STEPPING)
1135
+    SETUP_RUN(page_manager.init());
1136
+  #endif
1137
+
1127 1138
   marlin_state = MF_RUNNING;
1128 1139
 
1129 1140
   SETUP_LOG("setup() completed.");

+ 273
- 0
Marlin/src/feature/direct_stepping.cpp View File

@@ -0,0 +1,273 @@
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
+#include "../inc/MarlinConfigPre.h"
23
+
24
+#if ENABLED(DIRECT_STEPPING)
25
+
26
+#include "direct_stepping.h"
27
+
28
+#include "../MarlinCore.h"
29
+
30
+#define CHECK_PAGE(I, R) do{                                \
31
+  if (I >= sizeof(page_states) / sizeof(page_states[0])) {  \
32
+    fatal_error = true;                                     \
33
+    return R;                                               \
34
+  }                                                         \
35
+}while(0)
36
+
37
+#define CHECK_PAGE_STATE(I, R, S) do { \
38
+  CHECK_PAGE(I, R);                    \
39
+  if (page_states[I] != S) {           \
40
+    fatal_error = true;                \
41
+    return R;                          \
42
+  }                                    \
43
+}while(0)
44
+
45
+namespace DirectStepping {
46
+
47
+  template<typename Cfg>
48
+  State SerialPageManager<Cfg>::state;
49
+
50
+  template<typename Cfg>
51
+  volatile bool SerialPageManager<Cfg>::fatal_error;
52
+
53
+  template<typename Cfg>
54
+  volatile PageState SerialPageManager<Cfg>::page_states[Cfg::NUM_PAGES];
55
+
56
+  template<typename Cfg>
57
+  volatile bool SerialPageManager<Cfg>::page_states_dirty;
58
+
59
+  template<typename Cfg>
60
+  millis_t SerialPageManager<Cfg>::next_response;
61
+
62
+  template<typename Cfg>
63
+  uint8_t SerialPageManager<Cfg>::pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
64
+          
65
+  template<typename Cfg>
66
+  uint8_t SerialPageManager<Cfg>::checksum;
67
+
68
+  template<typename Cfg>
69
+  typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_byte_idx;
70
+
71
+  template<typename Cfg>
72
+  typename Cfg::page_idx_t SerialPageManager<Cfg>::write_page_idx;
73
+
74
+  template<typename Cfg>
75
+  typename Cfg::write_byte_idx_t SerialPageManager<Cfg>::write_page_size;
76
+
77
+  template <typename Cfg>
78
+  void SerialPageManager<Cfg>::init() {
79
+    for (int i = 0 ; i < Cfg::NUM_PAGES ; i++)
80
+      page_states[i] = PageState::FREE;
81
+
82
+    fatal_error = false;
83
+    next_response = 0;
84
+    state = State::NEWLINE;
85
+
86
+    page_states_dirty = false;
87
+
88
+    SERIAL_ECHOLNPGM("pages_ready");
89
+  }
90
+
91
+  template<typename Cfg>
92
+  FORCE_INLINE bool SerialPageManager<Cfg>::maybe_store_rxd_char(uint8_t c) {
93
+    switch (state) {
94
+      default:
95
+      case State::MONITOR:
96
+        switch (c) {
97
+          case '\n':
98
+          case '\r':
99
+            state = State::NEWLINE;
100
+          default:
101
+            return false;
102
+        }
103
+      case State::NEWLINE:
104
+        switch (c) {
105
+          case Cfg::CONTROL_CHAR:
106
+            state = State::ADDRESS;
107
+            return true;
108
+          case '\n':
109
+          case '\r':
110
+            state = State::NEWLINE;
111
+            return false;
112
+          default:
113
+            state = State::MONITOR;
114
+            return false;
115
+        }
116
+      case State::ADDRESS:
117
+        //TODO: 16 bit address, State::ADDRESS2
118
+        write_page_idx = c;
119
+        write_byte_idx = 0;
120
+        checksum = 0;
121
+
122
+        CHECK_PAGE(write_page_idx, true);
123
+
124
+        if (page_states[write_page_idx] == PageState::FAIL) {
125
+          // Special case for fail
126
+          state = State::UNFAIL;
127
+          return true;
128
+        }
129
+
130
+        set_page_state(write_page_idx, PageState::WRITING);
131
+
132
+        state = Cfg::DIRECTIONAL ? State::COLLECT : State::SIZE;
133
+
134
+        return true;
135
+      case State::SIZE:
136
+        // Zero means full page size
137
+        write_page_size = c;
138
+        state = State::COLLECT;
139
+        return true;
140
+      case State::COLLECT:
141
+        pages[write_page_idx][write_byte_idx++] = c;
142
+        checksum ^= c;
143
+
144
+        // check if still collecting
145
+        if (Cfg::PAGE_SIZE == 256) {
146
+          // special case for 8-bit, check if rolled back to 0
147
+          if (Cfg::DIRECTIONAL || !write_page_size) { // full 256 bytes
148
+            if (write_byte_idx) return true;
149
+          } else {
150
+            if (write_byte_idx < write_page_size) return true;
151
+          }
152
+        } else if (Cfg::DIRECTIONAL) {
153
+          if (write_byte_idx != Cfg::PAGE_SIZE) return true;
154
+        } else {
155
+          if (write_byte_idx < write_page_size) return true;
156
+        }
157
+
158
+        state = State::CHECKSUM;
159
+        return true;
160
+      case State::CHECKSUM: {
161
+        const PageState page_state = (checksum == c) ? PageState::OK : PageState::FAIL;
162
+        set_page_state(write_page_idx, page_state);
163
+        state = State::MONITOR;
164
+        return true;
165
+      }
166
+      case State::UNFAIL:
167
+        if (c == 0) {
168
+          set_page_state(write_page_idx, PageState::FREE);
169
+        } else {
170
+          fatal_error = true;
171
+        }
172
+        state = State::MONITOR;
173
+        return true;
174
+    }
175
+  }
176
+
177
+  template <typename Cfg>
178
+  void SerialPageManager<Cfg>::write_responses() {
179
+    if (fatal_error) {
180
+      kill(GET_TEXT(MSG_BAD_PAGE));
181
+      return;
182
+    }
183
+
184
+    // Runs on a set interval also, as responses may get lost.
185
+    if (next_response && next_response < millis()) {
186
+      page_states_dirty = true;
187
+    }
188
+
189
+    if (!page_states_dirty) return;
190
+
191
+    page_states_dirty = false;
192
+    next_response = millis() + Cfg::RESPONSE_INTERVAL_MS;
193
+
194
+    SERIAL_ECHO(Cfg::CONTROL_CHAR);
195
+    constexpr int state_bits = 2;
196
+    constexpr int n_bytes = Cfg::NUM_PAGES >> state_bits;
197
+    volatile uint8_t bits_b[n_bytes] = { 0 };
198
+
199
+    for (page_idx_t i = 0 ; i < Cfg::NUM_PAGES ; i++) {
200
+      bits_b[i >> state_bits] |= page_states[i] << ((i * state_bits) & 0x7);
201
+    }
202
+
203
+    uint8_t crc = 0;
204
+    for (uint8_t i = 0 ; i < n_bytes ; i++) {
205
+      crc ^= bits_b[i];
206
+      SERIAL_ECHO(bits_b[i]);
207
+    }
208
+
209
+    SERIAL_ECHO(crc);
210
+    SERIAL_EOL();
211
+  }
212
+
213
+  template <typename Cfg>
214
+  FORCE_INLINE void SerialPageManager<Cfg>::set_page_state(const page_idx_t page_idx, const PageState page_state) {
215
+    CHECK_PAGE(page_idx,);
216
+
217
+    page_states[page_idx] = page_state;
218
+    page_states_dirty = true;
219
+  }
220
+
221
+  template <>
222
+  FORCE_INLINE uint8_t *PageManager::get_page(const page_idx_t page_idx) {
223
+    CHECK_PAGE(page_idx, nullptr);
224
+
225
+    return pages[page_idx];
226
+  }
227
+
228
+  template <>
229
+  FORCE_INLINE void PageManager::free_page(const page_idx_t page_idx) {
230
+    set_page_state(page_idx, PageState::FREE);
231
+  }
232
+
233
+};
234
+
235
+DirectStepping::PageManager page_manager;
236
+
237
+const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS] PROGMEM = {
238
+
239
+  #if STEPPER_PAGE_FORMAT == SP_4x4D_128
240
+
241
+    { 1, 1, 1, 1, 1, 1, 1, 0 }, //  0 = -7
242
+    { 1, 1, 1, 0, 1, 1, 1, 0 }, //  1 = -6
243
+    { 0, 1, 1, 0, 1, 0, 1, 1 }, //  2 = -5
244
+    { 0, 1, 0, 1, 0, 1, 0, 1 }, //  3 = -4
245
+    { 0, 1, 0, 0, 1, 0, 0, 1 }, //  4 = -3
246
+    { 0, 0, 1, 0, 0, 0, 1, 0 }, //  5 = -2
247
+    { 0, 0, 0, 0, 1, 0, 0, 0 }, //  6 = -1
248
+    { 0, 0, 0, 0, 0, 0, 0, 0 }, //  7 =  0
249
+    { 0, 0, 0, 0, 1, 0, 0, 0 }, //  8 =  1
250
+    { 0, 0, 1, 0, 0, 0, 1, 0 }, //  9 =  2
251
+    { 0, 1, 0, 0, 1, 0, 0, 1 }, // 10 =  3
252
+    { 0, 1, 0, 1, 0, 1, 0, 1 }, // 11 =  4
253
+    { 0, 1, 1, 0, 1, 0, 1, 1 }, // 12 =  5
254
+    { 1, 1, 1, 0, 1, 1, 1, 0 }, // 13 =  6
255
+    { 1, 1, 1, 1, 1, 1, 1, 0 }, // 14 =  7
256
+    { 0 }
257
+
258
+  #elif STEPPER_PAGE_FORMAT == SP_4x2_256
259
+
260
+    { 0, 0, 0, 0 }, // 0
261
+    { 0, 1, 0, 0 }, // 1
262
+    { 1, 0, 1, 0 }, // 2
263
+    { 1, 1, 1, 0 }, // 3
264
+
265
+  #elif STEPPER_PAGE_FORMAT == SP_4x1_512
266
+
267
+    {0} // Uncompressed format, table not used
268
+
269
+  #endif
270
+
271
+};
272
+
273
+#endif // DIRECT_STEPPING

+ 137
- 0
Marlin/src/feature/direct_stepping.h View File

@@ -0,0 +1,137 @@
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
+#pragma once
23
+
24
+#include "../inc/MarlinConfig.h"
25
+
26
+namespace DirectStepping {
27
+
28
+  enum State : char {
29
+    MONITOR, NEWLINE, ADDRESS, SIZE, COLLECT, CHECKSUM, UNFAIL
30
+  };
31
+
32
+  enum PageState : uint8_t {
33
+    FREE, WRITING, OK, FAIL
34
+  };
35
+
36
+  // Static state used for stepping through direct stepping pages
37
+  struct page_step_state_t {
38
+    // Current page
39
+    uint8_t *page;
40
+    // Current segment
41
+    uint16_t segment_idx;
42
+    // Current steps within segment
43
+    uint8_t segment_steps;
44
+    // Segment delta
45
+    xyze_uint8_t sd;
46
+    // Block delta
47
+    xyze_int_t bd;
48
+  };
49
+
50
+  template<typename Cfg>
51
+  class SerialPageManager {
52
+  public:
53
+
54
+    typedef typename Cfg::page_idx_t page_idx_t;
55
+
56
+    static bool maybe_store_rxd_char(uint8_t c);
57
+    static void write_responses();
58
+
59
+    // common methods for page managers
60
+    static void init();
61
+    static uint8_t *get_page(const page_idx_t page_idx);
62
+    static void free_page(const page_idx_t page_idx);
63
+
64
+  protected:
65
+
66
+    typedef typename Cfg::write_byte_idx_t write_byte_idx_t;
67
+
68
+    static State state;
69
+    static volatile bool fatal_error;
70
+
71
+    static volatile PageState page_states[Cfg::NUM_PAGES];
72
+    static volatile bool page_states_dirty;
73
+    static millis_t next_response;
74
+
75
+    static uint8_t pages[Cfg::NUM_PAGES][Cfg::PAGE_SIZE];
76
+    static uint8_t checksum;
77
+    static write_byte_idx_t write_byte_idx;
78
+    static page_idx_t write_page_idx;
79
+    static write_byte_idx_t write_page_size;
80
+
81
+    static void set_page_state(const page_idx_t page_idx, const PageState page_state);
82
+  };
83
+
84
+  template<bool b, typename T, typename F> struct TypeSelector { typedef T type;} ;
85
+  template<typename T, typename F> struct TypeSelector<false, T, F> { typedef F type; };
86
+
87
+  template <int num_pages, int num_axes, int bits_segment, bool dir, int segments>
88
+  struct config_t {
89
+    static constexpr char CONTROL_CHAR  = '!';
90
+
91
+    static constexpr int NUM_PAGES      = num_pages;
92
+    static constexpr int NUM_AXES       = num_axes;
93
+    static constexpr int BITS_SEGMENT   = bits_segment;
94
+    static constexpr int DIRECTIONAL    = dir ? 1 : 0;
95
+    static constexpr int SEGMENTS       = segments;
96
+
97
+    static constexpr int RAW            = (BITS_SEGMENT == 1) ? 1 : 0;
98
+    static constexpr int NUM_SEGMENTS   = 1 << BITS_SEGMENT;
99
+    static constexpr int SEGMENT_STEPS  = 1 << (BITS_SEGMENT - DIRECTIONAL - RAW);
100
+    static constexpr int TOTAL_STEPS    = SEGMENT_STEPS * SEGMENTS;
101
+    static constexpr int PAGE_SIZE      = (NUM_AXES * BITS_SEGMENT * SEGMENTS) / 8;
102
+
103
+    static constexpr millis_t RESPONSE_INTERVAL_MS = 50;
104
+
105
+    typedef typename TypeSelector<(PAGE_SIZE>256), uint16_t, uint8_t>::type write_byte_idx_t;
106
+    typedef typename TypeSelector<(NUM_PAGES>256), uint16_t, uint8_t>::type page_idx_t;
107
+  };
108
+
109
+  template <uint8_t num_pages>
110
+  using SP_4x4D_128 = config_t<num_pages, 4, 4, true,  128>;
111
+
112
+  template <uint8_t num_pages>
113
+  using SP_4x2_256  = config_t<num_pages, 4, 2, false, 256>;
114
+
115
+  template <uint8_t num_pages>
116
+  using SP_4x1_512  = config_t<num_pages, 4, 1, false, 512>;
117
+
118
+  // configured types
119
+  typedef STEPPER_PAGE_FORMAT<STEPPER_PAGES> Config;
120
+
121
+  template class PAGE_MANAGER<Config>;
122
+  typedef PAGE_MANAGER<Config> PageManager;
123
+};
124
+
125
+#define SP_4x4D_128 1
126
+//#define SP_4x4_128 2
127
+//#define SP_4x2D_256 3
128
+#define SP_4x2_256 4
129
+#define SP_4x1_512 5
130
+
131
+typedef typename DirectStepping::Config::page_idx_t page_idx_t;
132
+
133
+// TODO: use config
134
+typedef DirectStepping::page_step_state_t page_step_state_t;
135
+
136
+extern const uint8_t segment_table[DirectStepping::Config::NUM_SEGMENTS][DirectStepping::Config::SEGMENT_STEPS];
137
+extern DirectStepping::PageManager page_manager;

+ 4
- 0
Marlin/src/gcode/gcode.cpp View File

@@ -261,6 +261,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
261 261
         case 5: G5(); break;                                      // G5: Cubic B_spline
262 262
       #endif
263 263
 
264
+      #if ENABLED(DIRECT_STEPPING)
265
+        case 6: G6(); break;                                      // G6: Direct Stepper Move
266
+      #endif
267
+
264 268
       #if ENABLED(FWRETRACT)
265 269
         case 10: G10(); break;                                    // G10: Retract / Swap Retract
266 270
         case 11: G11(); break;                                    // G11: Recover / Swap Recover

+ 2
- 0
Marlin/src/gcode/gcode.h View File

@@ -402,6 +402,8 @@ private:
402 402
 
403 403
   TERN_(BEZIER_CURVE_SUPPORT, static void G5());
404 404
 
405
+  TERN_(DIRECT_STEPPING, static void G6());
406
+
405 407
   #if ENABLED(FWRETRACT)
406 408
     static void G10();
407 409
     static void G11();

+ 3
- 1
Marlin/src/gcode/geometry/G92.cpp View File

@@ -99,5 +99,7 @@ void GcodeSuite::G92() {
99 99
   if    (sync_XYZ) sync_plan_position();
100 100
   else if (sync_E) sync_plan_position_e();
101 101
 
102
-  report_current_position();
102
+  #if DISABLED(DIRECT_STEPPING)
103
+    report_current_position();
104
+  #endif
103 105
 }

+ 61
- 0
Marlin/src/gcode/motion/G6.cpp View File

@@ -0,0 +1,61 @@
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
+#include "../../inc/MarlinConfig.h"
23
+
24
+#if ENABLED(DIRECT_STEPPING)
25
+
26
+#include "../../feature/direct_stepping.h"
27
+
28
+#include "../gcode.h"
29
+#include "../../module/planner.h"
30
+
31
+/**
32
+ * G6: Direct Stepper Move
33
+ */
34
+void GcodeSuite::G6() {
35
+  // TODO: feedrate support?
36
+  if (parser.seen('R'))
37
+    planner.last_page_step_rate = parser.value_ulong();
38
+
39
+  if (!DirectStepping::Config::DIRECTIONAL) {
40
+    if (parser.seen('X')) planner.last_page_dir.x = !!parser.value_byte();
41
+    if (parser.seen('Y')) planner.last_page_dir.y = !!parser.value_byte();
42
+    if (parser.seen('Z')) planner.last_page_dir.z = !!parser.value_byte();
43
+    if (parser.seen('E')) planner.last_page_dir.e = !!parser.value_byte();
44
+  }
45
+
46
+  // No index means we just set the state
47
+  if (!parser.seen('I')) return;
48
+
49
+  // No speed is set, can't schedule the move
50
+  if (!planner.last_page_step_rate) return;
51
+
52
+  const page_idx_t page_idx = (page_idx_t) parser.value_ulong();
53
+
54
+  uint16_t num_steps = DirectStepping::Config::TOTAL_STEPS;
55
+  if (parser.seen('S')) num_steps = parser.value_ushort();
56
+
57
+  planner.buffer_page(page_idx, 0, num_steps);
58
+  reset_stepper_timeout();
59
+}
60
+
61
+#endif // DIRECT_STEPPING

+ 12
- 0
Marlin/src/inc/Conditionals_adv.h View File

@@ -323,6 +323,18 @@
323 323
   #endif
324 324
 #endif
325 325
 
326
+#if ENABLED(DIRECT_STEPPING)
327
+  #ifndef STEPPER_PAGES
328
+    #define STEPPER_PAGES 16
329
+  #endif
330
+  #ifndef STEPPER_PAGE_FORMAT
331
+    #define STEPPER_PAGE_FORMAT SP_4x2_256
332
+  #endif
333
+  #ifndef PAGE_MANAGER
334
+    #define PAGE_MANAGER SerialPageManager
335
+  #endif
336
+#endif
337
+
326 338
 //
327 339
 // SD Card connection methods
328 340
 // Defined here so pins and sanity checks can use them

+ 9
- 0
Marlin/src/inc/SanityCheck.h View File

@@ -2923,3 +2923,12 @@ static_assert(   _ARR_TEST(3,0) && _ARR_TEST(3,1) && _ARR_TEST(3,2)
2923 2923
 #if SAVED_POSITIONS > 256
2924 2924
   #error "SAVED_POSITIONS must be an integer from 0 to 256."
2925 2925
 #endif
2926
+
2927
+/**
2928
+ * Sanity checks for stepper chunk support
2929
+ */
2930
+#if ENABLED(DIRECT_STEPPING)
2931
+  #if ENABLED(LIN_ADVANCE)
2932
+    #error "DIRECT_STEPPING is incompatible with LIN_ADVANCE. Enable in external planner if possible."
2933
+  #endif
2934
+#endif

+ 3
- 0
Marlin/src/lcd/language/language_en.h View File

@@ -577,6 +577,9 @@ namespace Language_en {
577 577
   PROGMEM Language_Str MSG_SNAKE                           = _UxGT("Sn4k3");
578 578
   PROGMEM Language_Str MSG_MAZE                            = _UxGT("Maze");
579 579
 
580
+  PROGMEM Language_Str MSG_BAD_PAGE                        = _UxGT("Bad page index");
581
+  PROGMEM Language_Str MSG_BAD_PAGE_SPEED                  = _UxGT("Bad page speed");
582
+
580 583
   //
581 584
   // Filament Change screens show up to 3 lines on a 4-line display
582 585
   //                        ...or up to 2 lines on a 3-line display

+ 79
- 7
Marlin/src/module/planner.cpp View File

@@ -151,6 +151,11 @@ float Planner::steps_to_mm[XYZE_N];             // (mm) Millimeters per step
151 151
   uint8_t Planner::last_extruder = 0;     // Respond to extruder change
152 152
 #endif
153 153
 
154
+#if ENABLED(DIRECT_STEPPING)
155
+  uint32_t Planner::last_page_step_rate = 0;
156
+  xyze_bool_t Planner::last_page_dir{0};
157
+#endif
158
+
154 159
 #if EXTRUDERS
155 160
   int16_t Planner::flow_percentage[EXTRUDERS] = ARRAY_BY_EXTRUDERS1(100); // Extrusion factor for each extruder
156 161
   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() {
235 240
   TERN_(ABL_PLANAR, bed_level_matrix.set_to_identity());
236 241
   clear_block_buffer();
237 242
   delay_before_delivering = 0;
243
+  #if ENABLED(DIRECT_STEPPING)
244
+    last_page_step_rate = 0;
245
+    last_page_dir.reset();
246
+  #endif
238 247
 }
239 248
 
240 249
 #if ENABLED(S_CURVE_ACCELERATION)
@@ -906,7 +915,7 @@ void Planner::calculate_trapezoid_for_block(block_t* const block, const float &e
906 915
       streaming operating conditions. Use for planning optimizations by avoiding recomputing parts of the
907 916
       planner buffer that don't change with the addition of a new block, as describe above. In addition,
908 917
       this block can never be less than block_buffer_tail and will always be pushed forward and maintain
909
-      this requirement when encountered by the Planner::discard_current_block() routine during a cycle.
918
+      this requirement when encountered by the Planner::release_current_block() routine during a cycle.
910 919
 
911 920
   NOTE: Since the planner only computes on what's in the planner buffer, some motions with lots of short
912 921
   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() {
994 1003
     // Perform the reverse pass
995 1004
     block_t *current = &block_buffer[block_index];
996 1005
 
997
-    // Only consider non sync blocks
998
-    if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION)) {
1006
+    // Only consider non sync and page blocks
1007
+    if (!TEST(current->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(current)) {
999 1008
       reverse_pass_kernel(current, next);
1000 1009
       next = current;
1001 1010
     }
@@ -1089,8 +1098,8 @@ void Planner::forward_pass() {
1089 1098
     // Perform the forward pass
1090 1099
     block = &block_buffer[block_index];
1091 1100
 
1092
-    // Skip SYNC blocks
1093
-    if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION)) {
1101
+    // Skip SYNC and page blocks
1102
+    if (!TEST(block->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(block)) {
1094 1103
       // If there's no previous block or the previous block is not
1095 1104
       // BUSY (thus, modifiable) run the forward_pass_kernel. Otherwise,
1096 1105
       // the previous block became BUSY, so assume the current block's
@@ -1139,8 +1148,8 @@ void Planner::recalculate_trapezoids() {
1139 1148
 
1140 1149
     next = &block_buffer[block_index];
1141 1150
 
1142
-    // Skip sync blocks
1143
-    if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION)) {
1151
+    // Skip sync and page blocks
1152
+    if (!TEST(next->flag, BLOCK_BIT_SYNC_POSITION) && !IS_PAGE(next)) {
1144 1153
       next_entry_speed = SQRT(next->entry_speed_sqr);
1145 1154
 
1146 1155
       if (block) {
@@ -2717,6 +2726,69 @@ bool Planner::buffer_line(const float &rx, const float &ry, const float &rz, con
2717 2726
   #endif
2718 2727
 } // buffer_line()
2719 2728
 
2729
+#if ENABLED(DIRECT_STEPPING)
2730
+
2731
+  void Planner::buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps) {
2732
+    if (!last_page_step_rate) {
2733
+      kill(GET_TEXT(MSG_BAD_PAGE_SPEED));
2734
+      return;
2735
+    }
2736
+
2737
+    uint8_t next_buffer_head;
2738
+    block_t * const block = get_next_free_block(next_buffer_head);
2739
+
2740
+    block->flag = BLOCK_FLAG_IS_PAGE;
2741
+
2742
+    #if FAN_COUNT > 0
2743
+      FANS_LOOP(i) block->fan_speed[i] = thermalManager.fan_speed[i];
2744
+    #endif
2745
+
2746
+    #if EXTRUDERS > 1
2747
+      block->extruder = extruder;
2748
+    #endif
2749
+
2750
+    block->page_idx = page_idx;
2751
+
2752
+    block->step_event_count = num_steps;
2753
+    block->initial_rate =
2754
+      block->final_rate =
2755
+      block->nominal_rate = last_page_step_rate; // steps/s
2756
+
2757
+    block->accelerate_until = 0;
2758
+    block->decelerate_after = block->step_event_count;
2759
+
2760
+    // Will be set to last direction later if directional format.
2761
+    block->direction_bits = 0;
2762
+
2763
+    #define PAGE_UPDATE_DIR(AXIS) \
2764
+      if (!last_page_dir[_AXIS(AXIS)]) SBI(block->direction_bits, _AXIS(AXIS));
2765
+
2766
+    if (!DirectStepping::Config::DIRECTIONAL) {
2767
+      PAGE_UPDATE_DIR(X);
2768
+      PAGE_UPDATE_DIR(Y);
2769
+      PAGE_UPDATE_DIR(Z);
2770
+      PAGE_UPDATE_DIR(E);
2771
+    }
2772
+
2773
+    // If this is the first added movement, reload the delay, otherwise, cancel it.
2774
+    if (block_buffer_head == block_buffer_tail) {
2775
+      // If it was the first queued block, restart the 1st block delivery delay, to
2776
+      // give the planner an opportunity to queue more movements and plan them
2777
+      // As there are no queued movements, the Stepper ISR will not touch this
2778
+      // variable, so there is no risk setting this here (but it MUST be done
2779
+      // before the following line!!)
2780
+      delay_before_delivering = BLOCK_DELAY_FOR_1ST_MOVE;
2781
+    }
2782
+
2783
+    // Move buffer head
2784
+    block_buffer_head = next_buffer_head;
2785
+
2786
+    enable_all_steppers();
2787
+    stepper.wake_up();
2788
+  }
2789
+
2790
+#endif // DIRECT_STEPPING
2791
+
2720 2792
 /**
2721 2793
  * Directly set the planner ABC position (and stepper positions)
2722 2794
  * converting mm (or angles for SCARA) into steps.

+ 34
- 6
Marlin/src/module/planner.h View File

@@ -66,6 +66,13 @@
66 66
   #include "../feature/spindle_laser_types.h"
67 67
 #endif
68 68
 
69
+#if ENABLED(DIRECT_STEPPING)
70
+  #include "../feature/direct_stepping.h"
71
+  #define IS_PAGE(B) TEST(B->flag, BLOCK_BIT_IS_PAGE)
72
+#else
73
+  #define IS_PAGE(B) false
74
+#endif
75
+
69 76
 // Feedrate for manual moves
70 77
 #ifdef MANUAL_FEEDRATE
71 78
   constexpr xyze_feedrate_t _mf = MANUAL_FEEDRATE,
@@ -90,13 +97,21 @@ enum BlockFlagBit : char {
90 97
 
91 98
   // Sync the stepper counts from the block
92 99
   BLOCK_BIT_SYNC_POSITION
100
+
101
+  // Direct stepping page
102
+  #if ENABLED(DIRECT_STEPPING)
103
+    , BLOCK_BIT_IS_PAGE
104
+  #endif
93 105
 };
94 106
 
95 107
 enum BlockFlag : char {
96
-  BLOCK_FLAG_RECALCULATE          = _BV(BLOCK_BIT_RECALCULATE),
97
-  BLOCK_FLAG_NOMINAL_LENGTH       = _BV(BLOCK_BIT_NOMINAL_LENGTH),
98
-  BLOCK_FLAG_CONTINUED            = _BV(BLOCK_BIT_CONTINUED),
99
-  BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION)
108
+    BLOCK_FLAG_RECALCULATE          = _BV(BLOCK_BIT_RECALCULATE)
109
+  , BLOCK_FLAG_NOMINAL_LENGTH       = _BV(BLOCK_BIT_NOMINAL_LENGTH)
110
+  , BLOCK_FLAG_CONTINUED            = _BV(BLOCK_BIT_CONTINUED)
111
+  , BLOCK_FLAG_SYNC_POSITION        = _BV(BLOCK_BIT_SYNC_POSITION)
112
+  #if ENABLED(DIRECT_STEPPING)
113
+    , BLOCK_FLAG_IS_PAGE            = _BV(BLOCK_BIT_IS_PAGE)
114
+  #endif
100 115
 };
101 116
 
102 117
 #if ENABLED(LASER_POWER_INLINE)
@@ -180,6 +195,10 @@ typedef struct block_t {
180 195
            final_rate,                      // The minimal rate at exit
181 196
            acceleration_steps_per_s2;       // acceleration steps/sec^2
182 197
 
198
+  #if ENABLED(DIRECT_STEPPING)
199
+    page_idx_t page_idx;                    // Page index used for direct stepping
200
+  #endif
201
+
183 202
   #if HAS_CUTTER
184 203
     cutter_power_t cutter_power;            // Power level for Spindle, Laser, etc.
185 204
   #endif
@@ -296,6 +315,11 @@ class Planner {
296 315
       static uint8_t last_extruder;                 // Respond to extruder change
297 316
     #endif
298 317
 
318
+    #if ENABLED(DIRECT_STEPPING)
319
+      static uint32_t last_page_step_rate;          // Last page step rate given
320
+      static xyze_bool_t last_page_dir;             // Last page direction given
321
+    #endif
322
+
299 323
     #if EXTRUDERS
300 324
       static int16_t flow_percentage[EXTRUDERS];    // Extrusion factor for each extruder
301 325
       static float e_factor[EXTRUDERS];             // The flow percentage and volumetric multiplier combine to scale E movement
@@ -726,6 +750,10 @@ class Planner {
726 750
       );
727 751
     }
728 752
 
753
+    #if ENABLED(DIRECT_STEPPING)
754
+      static void buffer_page(const page_idx_t page_idx, const uint8_t extruder, const uint16_t num_steps);
755
+    #endif
756
+
729 757
     /**
730 758
      * Set the planner.position and individual stepper positions.
731 759
      * Used by G92, G28, G29, and other procedures.
@@ -811,10 +839,10 @@ class Planner {
811 839
     static block_t* get_current_block();
812 840
 
813 841
     /**
814
-     * "Discard" the block and "release" the memory.
842
+     * "Release" the current block so its slot can be reused.
815 843
      * Called when the current block is no longer needed.
816 844
      */
817
-    FORCE_INLINE static void discard_current_block() {
845
+    FORCE_INLINE static void release_current_block() {
818 846
       if (has_blocks_queued())
819 847
         block_buffer_tail = next_block_index(block_buffer_tail);
820 848
     }

+ 179
- 34
Marlin/src/module/stepper.cpp View File

@@ -230,6 +230,10 @@ uint32_t Stepper::advance_divisor = 0,
230 230
   uint32_t Stepper::nextBabystepISR = BABYSTEP_NEVER;
231 231
 #endif
232 232
 
233
+#if ENABLED(DIRECT_STEPPING)
234
+  page_step_state_t Stepper::page_step_state;
235
+#endif
236
+
233 237
 int32_t Stepper::ticks_nominal = -1;
234 238
 #if DISABLED(S_CURVE_ACCELERATION)
235 239
   uint32_t Stepper::acc_step_rate; // needed for deceleration start point
@@ -1520,11 +1524,7 @@ void Stepper::pulse_phase_isr() {
1520 1524
   // If we must abort the current block, do so!
1521 1525
   if (abort_current_block) {
1522 1526
     abort_current_block = false;
1523
-    if (current_block) {
1524
-      axis_did_move = 0;
1525
-      current_block = nullptr;
1526
-      planner.discard_current_block();
1527
-    }
1527
+    if (current_block) discard_current_block();
1528 1528
   }
1529 1529
 
1530 1530
   // If there is no current block, do nothing
@@ -1558,46 +1558,160 @@ void Stepper::pulse_phase_isr() {
1558 1558
       } \
1559 1559
     }while(0)
1560 1560
 
1561
-    // Start an active pulse, if Bresenham says so, and update position
1561
+    // Start an active pulse if needed
1562 1562
     #define PULSE_START(AXIS) do{ \
1563 1563
       if (step_needed[_AXIS(AXIS)]) { \
1564 1564
         _APPLY_STEP(AXIS, !_INVERT_STEP_PIN(AXIS), 0); \
1565 1565
       } \
1566 1566
     }while(0)
1567 1567
 
1568
-    // Stop an active pulse, if any, and adjust error term
1568
+    // Stop an active pulse if needed
1569 1569
     #define PULSE_STOP(AXIS) do { \
1570 1570
       if (step_needed[_AXIS(AXIS)]) { \
1571 1571
         _APPLY_STEP(AXIS, _INVERT_STEP_PIN(AXIS), 0); \
1572 1572
       } \
1573 1573
     }while(0)
1574 1574
 
1575
-    // Determine if pulses are needed
1576
-    #if HAS_X_STEP
1577
-      PULSE_PREP(X);
1578
-    #endif
1579
-    #if HAS_Y_STEP
1580
-      PULSE_PREP(Y);
1581
-    #endif
1582
-    #if HAS_Z_STEP
1583
-      PULSE_PREP(Z);
1584
-    #endif
1575
+    // Direct Stepping page?
1576
+    const bool is_page = IS_PAGE(current_block);
1577
+
1578
+    #if ENABLED(DIRECT_STEPPING)
1579
+
1580
+      if (is_page) {
1581
+
1582
+        #if STEPPER_PAGE_FORMAT == SP_4x4D_128
1583
+
1584
+          #define PAGE_SEGMENT_UPDATE(AXIS, VALUE, MID) do{ \
1585
+                 if ((VALUE) == MID) {}                     \
1586
+            else if ((VALUE) <  MID) SBI(dm, _AXIS(AXIS));  \
1587
+            else                     CBI(dm, _AXIS(AXIS));  \
1588
+            page_step_state.sd[_AXIS(AXIS)] = VALUE;        \
1589
+            page_step_state.bd[_AXIS(AXIS)] += VALUE;       \
1590
+          }while(0)
1591
+
1592
+          #define PAGE_PULSE_PREP(AXIS) do{ \
1593
+            step_needed[_AXIS(AXIS)] =      \
1594
+              pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x7]); \
1595
+          }while(0)
1596
+
1597
+          switch (page_step_state.segment_steps) {
1598
+            case 8:
1599
+              page_step_state.segment_idx += 2;
1600
+              page_step_state.segment_steps = 0;
1601
+              // fallthru
1602
+            case 0: {
1603
+              const uint8_t low = page_step_state.page[page_step_state.segment_idx],
1604
+                           high = page_step_state.page[page_step_state.segment_idx + 1];
1605
+              uint8_t dm = last_direction_bits;
1606
+
1607
+              PAGE_SEGMENT_UPDATE(X, low >> 4, 7);
1608
+              PAGE_SEGMENT_UPDATE(Y, low & 0xF, 7);
1609
+              PAGE_SEGMENT_UPDATE(Z, high >> 4, 7);
1610
+              PAGE_SEGMENT_UPDATE(E, high & 0xF, 7);
1611
+
1612
+              if (dm != last_direction_bits) {
1613
+                last_direction_bits = dm;
1614
+                set_directions();
1615
+              }
1616
+            } break;
1617
+
1618
+            default: break;
1619
+          }
1620
+
1621
+          PAGE_PULSE_PREP(X),
1622
+          PAGE_PULSE_PREP(Y),
1623
+          PAGE_PULSE_PREP(Z),
1624
+          PAGE_PULSE_PREP(E);
1625
+
1626
+          page_step_state.segment_steps++;
1627
+
1628
+        #elif STEPPER_PAGE_FORMAT == SP_4x2_256
1629
+
1630
+          #define PAGE_SEGMENT_UPDATE(AXIS, VALUE) \
1631
+            page_step_state.sd[_AXIS(AXIS)] = VALUE; \
1632
+            page_step_state.bd[_AXIS(AXIS)] += VALUE;
1633
+
1634
+          #define PAGE_PULSE_PREP(AXIS) do{ \
1635
+            step_needed[_AXIS(AXIS)] =      \
1636
+              pgm_read_byte(&segment_table[page_step_state.sd[_AXIS(AXIS)]][page_step_state.segment_steps & 0x3]); \
1637
+          }while(0)
1638
+
1639
+          switch (page_step_state.segment_steps) {
1640
+            case 4:
1641
+              page_step_state.segment_idx++;
1642
+              page_step_state.segment_steps = 0;
1643
+              // fallthru
1644
+            case 0: {
1645
+              const uint8_t b = page_step_state.page[page_step_state.segment_idx];
1646
+              PAGE_SEGMENT_UPDATE(X, (b >> 6) & 0x3);
1647
+              PAGE_SEGMENT_UPDATE(Y, (b >> 4) & 0x3);
1648
+              PAGE_SEGMENT_UPDATE(Z, (b >> 2) & 0x3);
1649
+              PAGE_SEGMENT_UPDATE(E, (b >> 0) & 0x3);
1650
+            } break;
1651
+            default: break;
1652
+          }
1653
+
1654
+          PAGE_PULSE_PREP(X);
1655
+          PAGE_PULSE_PREP(Y);
1656
+          PAGE_PULSE_PREP(Z);
1657
+          PAGE_PULSE_PREP(E);
1658
+
1659
+          page_step_state.segment_steps++;
1660
+
1661
+        #elif STEPPER_PAGE_FORMAT == SP_4x1_512
1662
+
1663
+          #define PAGE_PULSE_PREP(AXIS, BITS) do{             \
1664
+            step_needed[_AXIS(AXIS)] = (steps >> BITS) & 0x1; \
1665
+            if (step_needed[_AXIS(AXIS)])                     \
1666
+              page_step_state.bd[_AXIS(AXIS)]++;              \
1667
+          }while(0)
1668
+
1669
+          uint8_t steps = page_step_state.page[page_step_state.segment_idx >> 1];
1670
+
1671
+          if (page_step_state.segment_idx & 0x1) steps >>= 4;
1672
+
1673
+          PAGE_PULSE_PREP(X, 3);
1674
+          PAGE_PULSE_PREP(Y, 2);
1675
+          PAGE_PULSE_PREP(Z, 1);
1676
+          PAGE_PULSE_PREP(E, 0);
1677
+
1678
+          page_step_state.segment_idx++;
1585 1679
 
1586
-    #if EITHER(LIN_ADVANCE, MIXING_EXTRUDER)
1587
-      delta_error.e += advance_dividend.e;
1588
-      if (delta_error.e >= 0) {
1589
-        count_position.e += count_direction.e;
1590
-        #if ENABLED(LIN_ADVANCE)
1591
-          delta_error.e -= advance_divisor;
1592
-          // Don't step E here - But remember the number of steps to perform
1593
-          motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
1594 1680
         #else
1595
-          step_needed.e = true;
1681
+          #error "Unknown direct stepping page format!"
1596 1682
         #endif
1597 1683
       }
1598
-    #elif HAS_E0_STEP
1599
-      PULSE_PREP(E);
1600
-    #endif
1684
+
1685
+    #endif // DIRECT_STEPPING
1686
+
1687
+    if (!is_page) {
1688
+      // Determine if pulses are needed
1689
+      #if HAS_X_STEP
1690
+        PULSE_PREP(X);
1691
+      #endif
1692
+      #if HAS_Y_STEP
1693
+        PULSE_PREP(Y);
1694
+      #endif
1695
+      #if HAS_Z_STEP
1696
+        PULSE_PREP(Z);
1697
+      #endif
1698
+
1699
+      #if EITHER(LIN_ADVANCE, MIXING_EXTRUDER)
1700
+        delta_error.e += advance_dividend.e;
1701
+        if (delta_error.e >= 0) {
1702
+          count_position.e += count_direction.e;
1703
+          #if ENABLED(LIN_ADVANCE)
1704
+            delta_error.e -= advance_divisor;
1705
+            // Don't step E here - But remember the number of steps to perform
1706
+            motor_direction(E_AXIS) ? --LA_steps : ++LA_steps;
1707
+          #else
1708
+            step_needed.e = true;
1709
+          #endif
1710
+        }
1711
+      #elif HAS_E0_STEP
1712
+        PULSE_PREP(E);
1713
+      #endif
1714
+    }
1601 1715
 
1602 1716
     #if ISR_MULTI_STEPS
1603 1717
       if (firstStep)
@@ -1676,14 +1790,28 @@ uint32_t Stepper::block_phase_isr() {
1676 1790
   // If there is a current block
1677 1791
   if (current_block) {
1678 1792
 
1679
-    // If current block is finished, reset pointer
1793
+    // If current block is finished, reset pointer and finalize state
1680 1794
     if (step_events_completed >= step_event_count) {
1795
+      #if ENABLED(DIRECT_STEPPING)
1796
+        #if STEPPER_PAGE_FORMAT == SP_4x4D_128
1797
+          #define PAGE_SEGMENT_UPDATE_POS(AXIS) \
1798
+            count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] - 128 * 7;
1799
+        #elif STEPPER_PAGE_FORMAT == SP_4x1_512 || STEPPER_PAGE_FORMAT == SP_4x2_256
1800
+          #define PAGE_SEGMENT_UPDATE_POS(AXIS) \
1801
+            count_position[_AXIS(AXIS)] += page_step_state.bd[_AXIS(AXIS)] * count_direction[_AXIS(AXIS)];
1802
+        #endif
1803
+
1804
+        if (IS_PAGE(current_block)) {
1805
+          PAGE_SEGMENT_UPDATE_POS(X);
1806
+          PAGE_SEGMENT_UPDATE_POS(Y);
1807
+          PAGE_SEGMENT_UPDATE_POS(Z);
1808
+          PAGE_SEGMENT_UPDATE_POS(E);
1809
+        }
1810
+      #endif
1681 1811
       #ifdef FILAMENT_RUNOUT_DISTANCE_MM
1682 1812
         runout.block_completed(current_block);
1683 1813
       #endif
1684
-      axis_did_move = 0;
1685
-      current_block = nullptr;
1686
-      planner.discard_current_block();
1814
+      discard_current_block();
1687 1815
     }
1688 1816
     else {
1689 1817
       // Step events not completed yet...
@@ -1867,7 +1995,7 @@ uint32_t Stepper::block_phase_isr() {
1867 1995
       // Sync block? Sync the stepper counts and return
1868 1996
       while (TEST(current_block->flag, BLOCK_BIT_SYNC_POSITION)) {
1869 1997
         _set_position(current_block->position);
1870
-        planner.discard_current_block();
1998
+        discard_current_block();
1871 1999
 
1872 2000
         // Try to get a new block
1873 2001
         if (!(current_block = planner.get_current_block()))
@@ -1878,6 +2006,23 @@ uint32_t Stepper::block_phase_isr() {
1878 2006
 
1879 2007
       TERN_(POWER_LOSS_RECOVERY, recovery.info.sdpos = current_block->sdpos);
1880 2008
 
2009
+      #if ENABLED(DIRECT_STEPPING)
2010
+        if (IS_PAGE(current_block)) {
2011
+          page_step_state.segment_steps = 0;
2012
+          page_step_state.segment_idx = 0;
2013
+          page_step_state.page = page_manager.get_page(current_block->page_idx);
2014
+          page_step_state.bd.reset();
2015
+
2016
+          if (DirectStepping::Config::DIRECTIONAL)
2017
+            current_block->direction_bits = last_direction_bits;
2018
+
2019
+          if (!page_step_state.page) {
2020
+            discard_current_block();
2021
+            return interval;
2022
+          }
2023
+        }
2024
+      #endif
2025
+
1881 2026
       // Flag all moving axes for proper endstop handling
1882 2027
 
1883 2028
       #if IS_CORE

+ 15
- 0
Marlin/src/module/stepper.h View File

@@ -334,6 +334,10 @@ class Stepper {
334 334
       static uint32_t nextBabystepISR;
335 335
     #endif
336 336
 
337
+    #if ENABLED(DIRECT_STEPPING)
338
+      static page_step_state_t page_step_state;
339
+    #endif
340
+
337 341
     static int32_t ticks_nominal;
338 342
     #if DISABLED(S_CURVE_ACCELERATION)
339 343
       static uint32_t acc_step_rate; // needed for deceleration start point
@@ -426,6 +430,17 @@ class Stepper {
426 430
     static void report_a_position(const xyz_long_t &pos);
427 431
     static void report_positions();
428 432
 
433
+    // Discard current block and free any resources
434
+    FORCE_INLINE static void discard_current_block() {
435
+      #if ENABLED(DIRECT_STEPPING)
436
+        if (IS_PAGE(current_block))
437
+          page_manager.free_page(current_block->page_idx);
438
+      #endif
439
+      current_block = nullptr;
440
+      axis_did_move = 0;
441
+      planner.release_current_block();
442
+    }
443
+
429 444
     // Quickly stop all steppers
430 445
     FORCE_INLINE static void quick_stop() { abort_current_block = true; }
431 446
 

+ 3
- 2
buildroot/share/tests/mega2560-tests View File

@@ -71,8 +71,9 @@ opt_set NUM_SERVOS 1
71 71
 opt_enable ZONESTAR_LCD Z_PROBE_SERVO_NR Z_SERVO_ANGLES DEACTIVATE_SERVOS_AFTER_MOVE BOOT_MARLIN_LOGO_ANIMATED \
72 72
            AUTO_BED_LEVELING_3POINT DEBUG_LEVELING_FEATURE EEPROM_SETTINGS EEPROM_CHITCHAT M114_DETAIL \
73 73
            NO_VOLUMETRICS EXTENDED_CAPABILITIES_REPORT AUTO_REPORT_TEMPERATURES AUTOTEMP G38_PROBE_TARGET JOYSTICK \
74
-           PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING
75
-exec_test $1 $2 "RAMPS | ZONESTAR_LCD | MMU2 | Servo Probe | ABL 3-Pt | Debug Leveling | EEPROM | G38 ..."
74
+           PRUSA_MMU2 MMU2_MENUS PRUSA_MMU2_S_MODE DIRECT_STEPPING \
75
+           FILAMENT_RUNOUT_SENSOR NOZZLE_PARK_FEATURE ADVANCED_PAUSE_FEATURE Z_SAFE_HOMING
76
+exec_test $1 $2 "RAMPS | ZONESTAR + Chinese | MMU2 | Servo | 3-Point + Debug | G38 ..."
76 77
 
77 78
 #
78 79
 # Test MINIRAMBO with PWM_MOTOR_CURRENT and many features

Loading…
Cancel
Save