Quellcode durchsuchen

Fix and improve G-code queue (#21122)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
X-Ryl669 vor 3 Jahren
Ursprung
Commit
ec42be346d
Es ist kein Account mit der E-Mail-Adresse des Committers verbunden

+ 6
- 0
Marlin/Configuration_adv.h Datei anzeigen

2016
   //#define SERIAL_STATS_DROPPED_RX
2016
   //#define SERIAL_STATS_DROPPED_RX
2017
 #endif
2017
 #endif
2018
 
2018
 
2019
+// Monitor RX buffer usage
2020
+// Dump an error to the serial port if the serial receive buffer overflows.
2021
+// If you see these errors, increase the RX_BUFFER_SIZE value.
2022
+// Not supported on all platforms.
2023
+//#define RX_BUFFER_MONITOR
2024
+
2019
 /**
2025
 /**
2020
  * Emergency Command Parser
2026
  * Emergency Command Parser
2021
  *
2027
  *

+ 1
- 1
Marlin/src/MarlinCore.cpp Datei anzeigen

407
  */
407
  */
408
 inline void manage_inactivity(const bool ignore_stepper_queue=false) {
408
 inline void manage_inactivity(const bool ignore_stepper_queue=false) {
409
 
409
 
410
-  if (queue.length < BUFSIZE) queue.get_available_commands();
410
+  queue.get_available_commands();
411
 
411
 
412
   const millis_t ms = millis();
412
   const millis_t ms = millis();
413
 
413
 

+ 37
- 0
Marlin/src/core/bug_on.h Datei anzeigen

1
+/**
2
+ * Marlin 3D Printer Firmware
3
+ * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4
+ *
5
+ * Copyright (c) 2021 X-Ryl669 [https://blog.cyril.by]
6
+ *
7
+ * This program is free software: you can redistribute it and/or modify
8
+ * it under the terms of the GNU General Public License as published by
9
+ * the Free Software Foundation, either version 3 of the License, or
10
+ * (at your option) any later version.
11
+ *
12
+ * This program is distributed in the hope that it will be useful,
13
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
+ * GNU General Public License for more details.
16
+ *
17
+ * You should have received a copy of the GNU General Public License
18
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
+ *
20
+ */
21
+#pragma once
22
+
23
+// We need SERIAL_ECHOPAIR and macros.h
24
+#include "serial.h"
25
+
26
+#if ENABLED(POSTMORTEM_DEBUGGING)
27
+  // Useful macro for stopping the CPU on an unexpected condition
28
+  // This is used like SERIAL_ECHOPAIR, that is: a key-value call of the local variables you want
29
+  // to dump to the serial port before stopping the CPU.
30
+  #define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": "); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); *(char*)0 = 42; } while(0)
31
+#elif ENABLED(MARLIN_DEV_MODE)
32
+  // Don't stop the CPU here, but at least dump the bug on the serial port
33
+  #define BUG_ON(V...) do { SERIAL_ECHOPAIR(ONLY_FILENAME, __LINE__, ": BUG!\n"); SERIAL_ECHOLNPAIR(V); SERIAL_FLUSHTX(); } while(0)
34
+#else
35
+  // Release mode, let's ignore the bug
36
+  #define BUG_ON(V...) NOOP
37
+#endif

+ 1
- 0
Marlin/src/core/language.h Datei anzeigen

131
 #define STR_WATCHDOG_FIRED                  "Watchdog timeout. Reset required."
131
 #define STR_WATCHDOG_FIRED                  "Watchdog timeout. Reset required."
132
 #define STR_ERR_KILLED                      "Printer halted. kill() called!"
132
 #define STR_ERR_KILLED                      "Printer halted. kill() called!"
133
 #define STR_ERR_STOPPED                     "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"
133
 #define STR_ERR_STOPPED                     "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"
134
+#define STR_ERR_SERIAL_MISMATCH             "Serial status mismatch"
134
 #define STR_BUSY_PROCESSING                 "busy: processing"
135
 #define STR_BUSY_PROCESSING                 "busy: processing"
135
 #define STR_BUSY_PAUSED_FOR_USER            "busy: paused for user"
136
 #define STR_BUSY_PAUSED_FOR_USER            "busy: paused for user"
136
 #define STR_BUSY_PAUSED_FOR_INPUT           "busy: paused for input"
137
 #define STR_BUSY_PAUSED_FOR_INPUT           "busy: paused for input"

+ 25
- 0
Marlin/src/core/macros.h Datei anzeigen

349
   #define CALL_IF_EXISTS(Return, That, Method, ...) \
349
   #define CALL_IF_EXISTS(Return, That, Method, ...) \
350
     static_cast<Return>(Private::Call_ ## Method(That, ##__VA_ARGS__))
350
     static_cast<Return>(Private::Call_ ## Method(That, ##__VA_ARGS__))
351
 
351
 
352
+  // Compile-time string manipulation
353
+  namespace CompileTimeString {
354
+    // Simple compile-time parser to find the position of the end of a string
355
+    constexpr const char* findStringEnd(const char *str) {
356
+      return *str ? findStringEnd(str + 1) : str;
357
+    }
358
+
359
+    // Check whether a string contains a slash
360
+    constexpr bool containsSlash(const char *str) {
361
+      return *str == '/' ? true : (*str ? containsSlash(str + 1) : false);
362
+    }
363
+    // Find the last position of the slash
364
+    constexpr const char* findLastSlashPos(const char* str) {
365
+      return *str == '/' ? (str + 1) : findLastSlashPos(str - 1);
366
+    }
367
+    // Compile-time evaluation of the last part of a file path
368
+    // Typically used to shorten the path to file in compiled strings
369
+    // CompileTimeString::baseName(__FILE__) returns "macros.h" and not /path/to/Marlin/src/core/macros.h
370
+    constexpr const char* baseName(const char* str) {
371
+      return containsSlash(str) ? findLastSlashPos(findStringEnd(str)) : str;
372
+    }
373
+  }
374
+
375
+  #define ONLY_FILENAME CompileTimeString::baseName(__FILE__)
376
+
352
 #else
377
 #else
353
 
378
 
354
   #define MIN_2(a,b)      ((a)<(b)?(a):(b))
379
   #define MIN_2(a,b)      ((a)<(b)?(a):(b))

+ 4
- 0
Marlin/src/core/serial.cpp Datei anzeigen

52
   #endif
52
   #endif
53
 #endif
53
 #endif
54
 
54
 
55
+#if ENABLED(MEATPACK)
56
+  MeatpackSerial<decltype(_SERIAL_IMPL)> mpSerial(false, _SERIAL_IMPL);
57
+#endif
58
+
55
 void serialprintPGM(PGM_P str) {
59
 void serialprintPGM(PGM_P str) {
56
   while (const char c = pgm_read_byte(str++)) SERIAL_CHAR(c);
60
   while (const char c = pgm_read_byte(str++)) SERIAL_CHAR(c);
57
 }
61
 }

+ 14
- 3
Marlin/src/core/serial.h Datei anzeigen

24
 #include "../inc/MarlinConfig.h"
24
 #include "../inc/MarlinConfig.h"
25
 #include "serial_hook.h"
25
 #include "serial_hook.h"
26
 
26
 
27
+#if ENABLED(MEATPACK)
28
+  #include "../feature/meatpack.h"
29
+#endif
30
+
27
 // Commonly-used strings in serial output
31
 // Commonly-used strings in serial output
28
 extern const char NUL_STR[], SP_P_STR[], SP_T_STR[],
32
 extern const char NUL_STR[], SP_P_STR[], SP_T_STR[],
29
                   X_STR[], Y_STR[], Z_STR[], E_STR[],
33
                   X_STR[], Y_STR[], Z_STR[], E_STR[],
69
     typedef MultiSerial<decltype(MYSERIAL0), TERN(HAS_ETHERNET, ConditionalSerial<decltype(MYSERIAL1)>, decltype(MYSERIAL1)), 0>      SerialOutputT;
73
     typedef MultiSerial<decltype(MYSERIAL0), TERN(HAS_ETHERNET, ConditionalSerial<decltype(MYSERIAL1)>, decltype(MYSERIAL1)), 0>      SerialOutputT;
70
   #endif
74
   #endif
71
   extern SerialOutputT          multiSerial;
75
   extern SerialOutputT          multiSerial;
72
-  #define SERIAL_IMPL           multiSerial
76
+  #define _SERIAL_IMPL          multiSerial
73
 #else
77
 #else
74
   #define _PORT_REDIRECT(n,p)   NOOP
78
   #define _PORT_REDIRECT(n,p)   NOOP
75
   #define _PORT_RESTORE(n)      NOOP
79
   #define _PORT_RESTORE(n)      NOOP
76
   #define SERIAL_ASSERT(P)      NOOP
80
   #define SERIAL_ASSERT(P)      NOOP
77
-  #define SERIAL_IMPL           MYSERIAL0
81
+  #define _SERIAL_IMPL          MYSERIAL0
82
+#endif
83
+
84
+#if ENABLED(MEATPACK)
85
+  extern MeatpackSerial<decltype(_SERIAL_IMPL)> mpSerial;
86
+  #define SERIAL_IMPL          mpSerial
87
+#else
88
+  #define SERIAL_IMPL          _SERIAL_IMPL
78
 #endif
89
 #endif
79
 
90
 
80
 #define SERIAL_OUT(WHAT, V...)  (void)SERIAL_IMPL.WHAT(V)
91
 #define SERIAL_OUT(WHAT, V...)  (void)SERIAL_IMPL.WHAT(V)
294
 #endif
305
 #endif
295
 
306
 
296
 #define SERIAL_ECHOPGM_P(P)         (serialprintPGM(P))
307
 #define SERIAL_ECHOPGM_P(P)         (serialprintPGM(P))
297
-#define SERIAL_ECHOLNPGM_P(P)       (serialprintPGM(P "\n"))
308
+#define SERIAL_ECHOLNPGM_P(P)       do{ serialprintPGM(P); SERIAL_EOL(); }while(0)
298
 
309
 
299
 #define SERIAL_ECHOPGM(S)           (serialprintPGM(PSTR(S)))
310
 #define SERIAL_ECHOPGM(S)           (serialprintPGM(PSTR(S)))
300
 #define SERIAL_ECHOLNPGM(S)         (serialprintPGM(PSTR(S "\n")))
311
 #define SERIAL_ECHOLNPGM(S)         (serialprintPGM(PSTR(S "\n")))

+ 4
- 6
Marlin/src/feature/meatpack.cpp Datei anzeigen

110
   if (TEST(state, MPConfig_Bit_Active)) {                   // Is MeatPack active?
110
   if (TEST(state, MPConfig_Bit_Active)) {                   // Is MeatPack active?
111
     if (!full_char_count) {                                 // No literal characters to fetch?
111
     if (!full_char_count) {                                 // No literal characters to fetch?
112
       uint8_t buf[2] = { 0, 0 };
112
       uint8_t buf[2] = { 0, 0 };
113
-      register const uint8_t res = unpack_chars(c, buf);    // Decode the byte into one or two characters.
113
+      const uint8_t res = unpack_chars(c, buf);             // Decode the byte into one or two characters.
114
       if (res & kFirstCharIsLiteral) {                      // The 1st character couldn't be packed.
114
       if (res & kFirstCharIsLiteral) {                      // The 1st character couldn't be packed.
115
         ++full_char_count;                                  // So the next stream byte is a full character.
115
         ++full_char_count;                                  // So the next stream byte is a full character.
116
         if (res & kSecondCharIsLiteral) ++full_char_count;  // The 2nd character couldn't be packed. Another stream byte is a full character.
116
         if (res & kSecondCharIsLiteral) ++full_char_count;  // The 2nd character couldn't be packed. Another stream byte is a full character.
147
   #if ENABLED(MP_DEBUG)
147
   #if ENABLED(MP_DEBUG)
148
     if (chars_decoded < 1024) {
148
     if (chars_decoded < 1024) {
149
       ++chars_decoded;
149
       ++chars_decoded;
150
-      DEBUG_ECHOPGM("RB: ");
151
-      MYSERIAL.print((char)c);
152
-      DEBUG_EOL();
150
+      DEBUG_ECHOLNPAIR("RB: ", AS_CHAR(c));
153
     }
151
     }
154
   #endif
152
   #endif
155
 }
153
 }
200
   }
198
   }
201
 
199
 
202
   if (cmd_is_next) {                      // Were two command bytes received?
200
   if (cmd_is_next) {                      // Were two command bytes received?
203
-    PORT_REDIRECT(serial_ind);
201
+    PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));
204
     handle_command((MeatPack_Command)c);  // Then the byte is a MeatPack command
202
     handle_command((MeatPack_Command)c);  // Then the byte is a MeatPack command
205
     cmd_is_next = false;
203
     cmd_is_next = false;
206
     return;
204
     return;
219
   if (char_out_count) {
217
   if (char_out_count) {
220
     res = char_out_count;
218
     res = char_out_count;
221
     char_out_count = 0;
219
     char_out_count = 0;
222
-    for (register uint8_t i = 0; i < res; ++i)
220
+    for (uint8_t i = 0; i < res; ++i)
223
       out[i] = (char)char_out_buf[i];
221
       out[i] = (char)char_out_buf[i];
224
   }
222
   }
225
   return res;
223
   return res;

+ 56
- 3
Marlin/src/feature/meatpack.h Datei anzeigen

49
 #pragma once
49
 #pragma once
50
 
50
 
51
 #include <stdint.h>
51
 #include <stdint.h>
52
+#include "../core/serial_hook.h"
52
 
53
 
53
 /**
54
 /**
54
  * Commands sent to MeatPack to control its behavior.
55
  * Commands sent to MeatPack to control its behavior.
78
 };
79
 };
79
 
80
 
80
 class MeatPack {
81
 class MeatPack {
81
-private:
82
-  friend class GCodeQueue;
83
 
82
 
84
   // Utility definitions
83
   // Utility definitions
85
   static const uint8_t kCommandByte         = 0b11111111,
84
   static const uint8_t kCommandByte         = 0b11111111,
99
                  char_out_count;  // Stores number of characters to be read out.
98
                  char_out_count;  // Stores number of characters to be read out.
100
   static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters
99
   static uint8_t char_out_buf[2]; // Output buffer for caching up to 2 characters
101
 
100
 
101
+public:
102
   // Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences,
102
   // Pass in a character rx'd by SD card or serial. Automatically parses command/ctrl sequences,
103
   // and will control state internally.
103
   // and will control state internally.
104
   static void handle_rx_char(const uint8_t c, const serial_index_t serial_ind);
104
   static void handle_rx_char(const uint8_t c, const serial_index_t serial_ind);
113
 
113
 
114
   static void reset_state();
114
   static void reset_state();
115
   static void report_state();
115
   static void report_state();
116
-  static uint8_t unpacked_char(register const uint8_t in);
117
   static uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out);
116
   static uint8_t unpack_chars(const uint8_t pk, uint8_t* __restrict const chars_out);
118
   static void handle_command(const MeatPack_Command c);
117
   static void handle_command(const MeatPack_Command c);
119
   static void handle_output_char(const uint8_t c);
118
   static void handle_output_char(const uint8_t c);
121
 };
120
 };
122
 
121
 
123
 extern MeatPack meatpack;
122
 extern MeatPack meatpack;
123
+
124
+// Implement the MeatPack serial class so it's transparent to rest of the code
125
+template <typename SerialT>
126
+struct MeatpackSerial : public SerialBase <MeatpackSerial < SerialT >> {
127
+  typedef SerialBase< MeatpackSerial<SerialT> > BaseClassT;
128
+
129
+  SerialT & out;
130
+
131
+  char serialBuffer[2];
132
+  uint8_t charCount;
133
+  uint8_t readIndex;
134
+
135
+  NO_INLINE size_t write(uint8_t c) { return out.write(c); }
136
+  void flush()                      { out.flush();  }
137
+  void begin(long br)               { out.begin(br); readIndex = 0; }
138
+  void end()                        { out.end(); }
139
+
140
+  void msgDone()                    { out.msgDone(); }
141
+  // Existing instances implement Arduino's operator bool, so use that if it's available
142
+  bool connected()                  { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
143
+  void flushTX()                    { CALL_IF_EXISTS(void, &out, flushTX); }
144
+
145
+  int available(uint8_t index) {
146
+    // There is a potential issue here with multiserial, since it'll return its decoded buffer whatever the serial index here.
147
+    // So, instead of doing MeatpackSerial<MultiSerial<...>> we should do MultiSerial<MeatpackSerial<...>, MeatpackSerial<...>>
148
+    // TODO, let's fix this later on
149
+
150
+    if (charCount) return charCount;          // The buffer still has data
151
+    if (out.available(index) <= 0) return 0;  // No data to read
152
+
153
+    // Don't read in read method, instead do it here, so we can make progress in the read method
154
+    const int r = out.read(index);
155
+    if (r == -1) return 0;  // This is an error from the underlying serial code
156
+    meatpack.handle_rx_char((uint8_t)r, index);
157
+    charCount = meatpack.get_result_char(serialBuffer);
158
+    readIndex = 0;
159
+
160
+    return charCount;
161
+  }
162
+
163
+  int readImpl(const uint8_t index) {
164
+    // Not enough char to make progress?
165
+    if (charCount == 0 && available(index) == 0) return -1;
166
+
167
+    charCount--;
168
+    return serialBuffer[readIndex++];
169
+  }
170
+
171
+  int read(uint8_t index) { return readImpl(index); }
172
+  int available()         { return available(0); }
173
+  int read()              { return readImpl(0); }
174
+
175
+  MeatpackSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
176
+};

+ 36
- 42
Marlin/src/gcode/calibrate/M100.cpp Datei anzeigen

51
  * Also, there are two support functions that can be called from a developer's C code.
51
  * Also, there are two support functions that can be called from a developer's C code.
52
  *
52
  *
53
  *    uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
53
  *    uint16_t check_for_free_memory_corruption(PGM_P const free_memory_start);
54
- *    void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
54
+ *    void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size);
55
  *
55
  *
56
  * Initial version by Roxy-3D
56
  * Initial version by Roxy-3D
57
  */
57
  */
151
    *  the block. If so, it may indicate memory corruption due to a bad pointer.
151
    *  the block. If so, it may indicate memory corruption due to a bad pointer.
152
    *  Unexpected bytes are flagged in the right column.
152
    *  Unexpected bytes are flagged in the right column.
153
    */
153
    */
154
-  inline void dump_free_memory(char *start_free_memory, char *end_free_memory) {
154
+  void dump_free_memory(char *start_free_memory, char *end_free_memory) {
155
     //
155
     //
156
     // Start and end the dump on a nice 16 byte boundary
156
     // Start and end the dump on a nice 16 byte boundary
157
     // (even though the values are not 16-byte aligned).
157
     // (even though the values are not 16-byte aligned).
182
     }
182
     }
183
   }
183
   }
184
 
184
 
185
-  void M100_dump_routine(PGM_P const title, const char * const start, const char * const end) {
186
-    serialprintPGM(title);
187
-    SERIAL_EOL();
185
+  void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size) {
186
+    SERIAL_ECHOLNPGM_P(title);
188
     //
187
     //
189
     // Round the start and end locations to produce full lines of output
188
     // Round the start and end locations to produce full lines of output
190
     //
189
     //
190
+    const char * const end = start + size - 1;
191
     dump_free_memory(
191
     dump_free_memory(
192
       (char*)(uintptr_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
192
       (char*)(uintptr_t(uint32_t(start) & ~0xFUL)), // Align to 16-byte boundary
193
       (char*)(uintptr_t(uint32_t(end)   |  0xFUL))  // Align end_free_memory to the 15th byte (at or above end_free_memory)
193
       (char*)(uintptr_t(uint32_t(end)   |  0xFUL))  // Align end_free_memory to the 15th byte (at or above end_free_memory)
197
 #endif // M100_FREE_MEMORY_DUMPER
197
 #endif // M100_FREE_MEMORY_DUMPER
198
 
198
 
199
 inline int check_for_free_memory_corruption(PGM_P const title) {
199
 inline int check_for_free_memory_corruption(PGM_P const title) {
200
-  serialprintPGM(title);
200
+  SERIAL_ECHOPGM_P(title);
201
 
201
 
202
   char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
202
   char *start_free_memory = free_memory_start, *end_free_memory = free_memory_end;
203
   int n = end_free_memory - start_free_memory;
203
   int n = end_free_memory - start_free_memory;
204
 
204
 
205
-  SERIAL_ECHOPAIR("\nfmc() n=", n);
206
-  SERIAL_ECHOPAIR("\nfree_memory_start=", hex_address(free_memory_start));
207
-  SERIAL_ECHOLNPAIR("  end_free_memory=", hex_address(end_free_memory));
205
+  SERIAL_ECHOLNPAIR("\nfmc() n=", n,
206
+                    "\nfree_memory_start=", hex_address(free_memory_start),
207
+                    "  end=", hex_address(end_free_memory));
208
 
208
 
209
   if (end_free_memory < start_free_memory)  {
209
   if (end_free_memory < start_free_memory)  {
210
     SERIAL_ECHOPGM(" end_free_memory < Heap ");
210
     SERIAL_ECHOPGM(" end_free_memory < Heap ");
211
-    // SET_INPUT_PULLUP(63);           // if the developer has a switch wired up to their controller board
212
-    // safe_delay(5);                  // this code can be enabled to pause the display as soon as the
213
-    // while ( READ(63))               // malfunction is detected.   It is currently defaulting to a switch
214
-    //   idle();                       // being on pin-63 which is unassigend and available on most controller
215
-    // safe_delay(20);                 // boards.
216
-    // while ( !READ(63))
217
-    //   idle();
211
+    //SET_INPUT_PULLUP(63);           // if the developer has a switch wired up to their controller board
212
+    //safe_delay(5);                  // this code can be enabled to pause the display as soon as the
213
+    //while ( READ(63))               // malfunction is detected.   It is currently defaulting to a switch
214
+    //  idle();                       // being on pin-63 which is unassigend and available on most controller
215
+    //safe_delay(20);                 // boards.
216
+    //while ( !READ(63))
217
+    //  idle();
218
     serial_delay(20);
218
     serial_delay(20);
219
     #if ENABLED(M100_FREE_MEMORY_DUMPER)
219
     #if ENABLED(M100_FREE_MEMORY_DUMPER)
220
-      M100_dump_routine(PSTR("   Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, (const char*)0x21FF);
220
+      M100_dump_routine(PSTR("   Memory corruption detected with end_free_memory<Heap\n"), (const char*)0x1B80, 0x0680);
221
     #endif
221
     #endif
222
   }
222
   }
223
 
223
 
227
     if (start_free_memory[i] == TEST_BYTE) {
227
     if (start_free_memory[i] == TEST_BYTE) {
228
       int32_t j = count_test_bytes(start_free_memory + i);
228
       int32_t j = count_test_bytes(start_free_memory + i);
229
       if (j > 8) {
229
       if (j > 8) {
230
-        // SERIAL_ECHOPAIR("Found ", j);
231
-        // SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
230
+        //SERIAL_ECHOPAIR("Found ", j);
231
+        //SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(start_free_memory + i));
232
         i += j;
232
         i += j;
233
         block_cnt++;
233
         block_cnt++;
234
-        SERIAL_ECHOPAIR(" (", block_cnt);
235
-        SERIAL_ECHOPAIR(") found=", j);
236
-        SERIAL_ECHOLNPGM("   ");
234
+        SERIAL_ECHOLNPAIR(" (", block_cnt, ") found=", j);
237
       }
235
       }
238
     }
236
     }
239
   }
237
   }
269
     if (*addr == TEST_BYTE) {
267
     if (*addr == TEST_BYTE) {
270
       const int32_t j = count_test_bytes(addr);
268
       const int32_t j = count_test_bytes(addr);
271
       if (j > 8) {
269
       if (j > 8) {
272
-        SERIAL_ECHOPAIR("Found ", j);
273
-        SERIAL_ECHOLNPAIR(" bytes free at ", hex_address(addr));
270
+        SERIAL_ECHOLNPAIR("Found ", j, " bytes free at ", hex_address(addr));
274
         if (j > max_cnt) {
271
         if (j > max_cnt) {
275
           max_cnt  = j;
272
           max_cnt  = j;
276
           max_addr = addr;
273
           max_addr = addr;
280
       }
277
       }
281
     }
278
     }
282
   }
279
   }
283
-  if (block_cnt > 1) {
284
-    SERIAL_ECHOLNPGM("\nMemory Corruption detected in free memory area.");
285
-    SERIAL_ECHOPAIR("\nLargest free block is ", max_cnt);
286
-    SERIAL_ECHOLNPAIR(" bytes at ", hex_address(max_addr));
287
-  }
280
+  if (block_cnt > 1) SERIAL_ECHOLNPAIR(
281
+    "\nMemory Corruption detected in free memory area."
282
+    "\nLargest free block is ", max_cnt, " bytes at ", hex_address(max_addr)
283
+  );
288
   SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
284
   SERIAL_ECHOLNPAIR("check_for_free_memory_corruption() = ", check_for_free_memory_corruption(PSTR("M100 F ")));
289
 }
285
 }
290
 
286
 
294
    *  Corrupt <num> locations in the free memory pool and report the corrupt addresses.
290
    *  Corrupt <num> locations in the free memory pool and report the corrupt addresses.
295
    *  This is useful to check the correctness of the M100 D and the M100 F commands.
291
    *  This is useful to check the correctness of the M100 D and the M100 F commands.
296
    */
292
    */
297
-  inline void corrupt_free_memory(char *start_free_memory, const uint32_t size) {
293
+  inline void corrupt_free_memory(char *start_free_memory, const uintptr_t size) {
298
     start_free_memory += 8;
294
     start_free_memory += 8;
299
     const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
295
     const uint32_t near_top = top_of_stack() - start_free_memory - 250, // -250 to avoid interrupt activity that's altered the stack.
300
                    j = near_top / (size + 1);
296
                    j = near_top / (size + 1);
301
 
297
 
302
-    SERIAL_ECHOLNPGM("Corrupting free memory block.\n");
298
+    SERIAL_ECHOLNPGM("Corrupting free memory block.");
303
     for (uint32_t i = 1; i <= size; i++) {
299
     for (uint32_t i = 1; i <= size; i++) {
304
       char * const addr = start_free_memory + i * j;
300
       char * const addr = start_free_memory + i * j;
305
       *addr = i;
301
       *addr = i;
322
     return;
318
     return;
323
   }
319
   }
324
 
320
 
325
-  start_free_memory += 8;       // move a few bytes away from the heap just because we don't want
326
-                  // to be altering memory that close to it.
321
+  start_free_memory += 8; // move a few bytes away from the heap just because we
322
+                          // don't want to be altering memory that close to it.
327
   memset(start_free_memory, TEST_BYTE, size);
323
   memset(start_free_memory, TEST_BYTE, size);
328
 
324
 
329
   SERIAL_ECHO(size);
325
   SERIAL_ECHO(size);
342
  * M100: Free Memory Check
338
  * M100: Free Memory Check
343
  */
339
  */
344
 void GcodeSuite::M100() {
340
 void GcodeSuite::M100() {
345
-
346
   char *sp = top_of_stack();
341
   char *sp = top_of_stack();
347
   if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
342
   if (!free_memory_end) free_memory_end = sp - MEMORY_END_CORRECTION;
348
-  SERIAL_ECHOPAIR("\nbss_end               : ", hex_address(end_bss));
349
-  if (heaplimit) SERIAL_ECHOPAIR("\n__heaplimit           : ", hex_address(heaplimit));
350
-  SERIAL_ECHOPAIR("\nfree_memory_start     : ", hex_address(free_memory_start));
343
+                  SERIAL_ECHOPAIR("\nbss_end               : ", hex_address(end_bss));
344
+  if (heaplimit)  SERIAL_ECHOPAIR("\n__heaplimit           : ", hex_address(heaplimit));
345
+                  SERIAL_ECHOPAIR("\nfree_memory_start     : ", hex_address(free_memory_start));
351
   if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit          : ", hex_address(stacklimit));
346
   if (stacklimit) SERIAL_ECHOPAIR("\n__stacklimit          : ", hex_address(stacklimit));
352
-  SERIAL_ECHOPAIR("\nfree_memory_end       : ", hex_address(free_memory_end));
353
-  if (MEMORY_END_CORRECTION)  SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION: ", MEMORY_END_CORRECTION);
354
-  SERIAL_ECHOLNPAIR("\nStack Pointer         : ", hex_address(sp));
347
+                  SERIAL_ECHOPAIR("\nfree_memory_end       : ", hex_address(free_memory_end));
348
+  if (MEMORY_END_CORRECTION)
349
+                  SERIAL_ECHOPAIR("\nMEMORY_END_CORRECTION : ", MEMORY_END_CORRECTION);
350
+                  SERIAL_ECHOLNPAIR("\nStack Pointer       : ", hex_address(sp));
355
 
351
 
356
   // Always init on the first invocation of M100
352
   // Always init on the first invocation of M100
357
   static bool m100_not_initialized = true;
353
   static bool m100_not_initialized = true;
369
     return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
365
     return free_memory_pool_report(free_memory_start, free_memory_end - free_memory_start);
370
 
366
 
371
   #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
367
   #if ENABLED(M100_FREE_MEMORY_CORRUPTOR)
372
-
373
     if (parser.seen('C'))
368
     if (parser.seen('C'))
374
       return corrupt_free_memory(free_memory_start, parser.value_int());
369
       return corrupt_free_memory(free_memory_start, parser.value_int());
375
-
376
   #endif
370
   #endif
377
 }
371
 }
378
 
372
 

+ 11
- 16
Marlin/src/gcode/gcode.cpp Datei anzeigen

260
 
260
 
261
 #endif // HAS_LEVELING && G29_RETRY_AND_RECOVER
261
 #endif // HAS_LEVELING && G29_RETRY_AND_RECOVER
262
 
262
 
263
-//
264
-// Placeholders for non-migrated codes
265
-//
266
-#if ENABLED(M100_FREE_MEMORY_WATCHER)
267
-  extern void M100_dump_routine(PGM_P const title, const char * const start, const char * const end);
268
-#endif
269
-
270
 /**
263
 /**
271
  * Process the parsed command and dispatch it to its handler
264
  * Process the parsed command and dispatch it to its handler
272
  */
265
  */
994
   SERIAL_OUT(msgDone); // Call the msgDone serial hook to signal command processing done
987
   SERIAL_OUT(msgDone); // Call the msgDone serial hook to signal command processing done
995
 }
988
 }
996
 
989
 
990
+#if ENABLED(M100_FREE_MEMORY_DUMPER)
991
+  void M100_dump_routine(PGM_P const title, const char * const start, const uintptr_t size);
992
+#endif
993
+
997
 /**
994
 /**
998
  * Process a single command and dispatch it to its handler
995
  * Process a single command and dispatch it to its handler
999
  * This is called from the main loop()
996
  * This is called from the main loop()
1000
  */
997
  */
1001
 void GcodeSuite::process_next_command() {
998
 void GcodeSuite::process_next_command() {
1002
-  char * const current_command = queue.command_buffer[queue.index_r];
999
+  GCodeQueue::CommandLine &command = queue.ring_buffer.peek_next_command();
1003
 
1000
 
1004
-  PORT_REDIRECT(SERIAL_PORTMASK(queue.port[queue.index_r]));
1001
+  PORT_REDIRECT(SERIAL_PORTMASK(command.port));
1005
 
1002
 
1006
-  #if ENABLED(POWER_LOSS_RECOVERY)
1007
-    recovery.queue_index_r = queue.index_r;
1008
-  #endif
1003
+  TERN_(POWER_LOSS_RECOVERY, recovery.queue_index_r = queue.ring_buffer.index_r);
1009
 
1004
 
1010
   if (DEBUGGING(ECHO)) {
1005
   if (DEBUGGING(ECHO)) {
1011
     SERIAL_ECHO_START();
1006
     SERIAL_ECHO_START();
1012
-    SERIAL_ECHOLN(current_command);
1007
+    SERIAL_ECHOLN(command.buffer);
1013
     #if ENABLED(M100_FREE_MEMORY_DUMPER)
1008
     #if ENABLED(M100_FREE_MEMORY_DUMPER)
1014
-      SERIAL_ECHOPAIR("slot:", queue.index_r);
1015
-      M100_dump_routine(PSTR("   Command Queue:"), &queue.command_buffer[0][0], &queue.command_buffer[BUFSIZE - 1][MAX_CMD_SIZE - 1]);
1009
+      SERIAL_ECHOPAIR("slot:", queue.ring_buffer.index_r);
1010
+      M100_dump_routine(PSTR("   Command Queue:"), (const char*)&queue.ring_buffer, sizeof(queue.ring_buffer));
1016
     #endif
1011
     #endif
1017
   }
1012
   }
1018
 
1013
 
1019
   // Parse the next command in the queue
1014
   // Parse the next command in the queue
1020
-  parser.parse(current_command);
1015
+  parser.parse(command.buffer);
1021
   process_parsed_command();
1016
   process_parsed_command();
1022
 }
1017
 }
1023
 
1018
 

+ 1
- 1
Marlin/src/gcode/host/M110.cpp Datei anzeigen

29
 void GcodeSuite::M110() {
29
 void GcodeSuite::M110() {
30
 
30
 
31
   if (parser.seenval('N'))
31
   if (parser.seenval('N'))
32
-    queue.last_N[queue.command_port()] = parser.value_long();
32
+    queue.set_current_line_number(parser.value_long());
33
 
33
 
34
 }
34
 }

+ 1
- 3
Marlin/src/gcode/host/M118.cpp Datei anzeigen

52
     while (*p == ' ') ++p;
52
     while (*p == ' ') ++p;
53
   }
53
   }
54
 
54
 
55
-  #if HAS_MULTI_SERIAL
56
-    PORT_REDIRECT(WITHIN(port, 0, NUM_SERIAL) ? (port ? _BV(port - 1) : SERIAL_ALL) : multiSerial.portMask);
57
-  #endif
55
+  PORT_REDIRECT(WITHIN(port, 0, NUM_SERIAL) ? (port ? SERIAL_PORTMASK(port - 1) : SERIAL_ALL) : multiSerial.portMask);
58
 
56
 
59
   if (hasE) SERIAL_ECHO_START();
57
   if (hasE) SERIAL_ECHO_START();
60
   if (hasA) SERIAL_ECHOPGM("//");
58
   if (hasA) SERIAL_ECHOPGM("//");

+ 164
- 190
Marlin/src/gcode/queue.cpp Datei anzeigen

35
 #include "../module/planner.h"
35
 #include "../module/planner.h"
36
 #include "../module/temperature.h"
36
 #include "../module/temperature.h"
37
 #include "../MarlinCore.h"
37
 #include "../MarlinCore.h"
38
+#include "../core/bug_on.h"
38
 
39
 
39
 #if ENABLED(PRINTER_EVENT_LEDS)
40
 #if ENABLED(PRINTER_EVENT_LEDS)
40
   #include "../feature/leds/printer_event_leds.h"
41
   #include "../feature/leds/printer_event_leds.h"
48
   #include "../feature/binary_stream.h"
49
   #include "../feature/binary_stream.h"
49
 #endif
50
 #endif
50
 
51
 
51
-#if ENABLED(MEATPACK)
52
-  #include "../feature/meatpack.h"
53
-#endif
54
-
55
 #if ENABLED(POWER_LOSS_RECOVERY)
52
 #if ENABLED(POWER_LOSS_RECOVERY)
56
   #include "../feature/powerloss.h"
53
   #include "../feature/powerloss.h"
57
 #endif
54
 #endif
67
   static millis_t last_command_time = 0;
64
   static millis_t last_command_time = 0;
68
 #endif
65
 #endif
69
 
66
 
70
-/**
71
- * GCode line number handling. Hosts may opt to include line numbers when
72
- * sending commands to Marlin, and lines will be checked for sequentiality.
73
- * M110 N<int> sets the current line number.
74
- */
75
-long GCodeQueue::last_N[NUM_SERIAL];
76
-
77
-/**
78
- * GCode Command Queue
79
- * A simple ring buffer of BUFSIZE command strings.
80
- *
81
- * Commands are copied into this buffer by the command injectors
82
- * (immediate, serial, sd card) and they are processed sequentially by
83
- * the main loop. The gcode.process_next_command method parses the next
84
- * command and hands off execution to individual handler functions.
85
- */
86
-uint8_t GCodeQueue::length = 0,  // Count of commands in the queue
87
-        GCodeQueue::index_r = 0, // Ring buffer read position
88
-        GCodeQueue::index_w = 0; // Ring buffer write position
89
-
90
-char GCodeQueue::command_buffer[BUFSIZE][MAX_CMD_SIZE];
67
+GCodeQueue::SerialState GCodeQueue::serial_state[NUM_SERIAL] = { 0 };
68
+GCodeQueue::RingBuffer GCodeQueue::ring_buffer = { 0 };
91
 
69
 
92
-/*
93
- * The port that the command was received on
94
- */
95
-#if HAS_MULTI_SERIAL
96
-  serial_index_t GCodeQueue::port[BUFSIZE];
70
+#if NO_TIMEOUTS > 0
71
+  static millis_t last_command_time = 0;
97
 #endif
72
 #endif
98
 
73
 
99
 /**
74
 /**
100
  * Serial command injection
75
  * Serial command injection
101
  */
76
  */
102
 
77
 
103
-// Number of characters read in the current line of serial input
104
-static int serial_count[NUM_SERIAL] = { 0 };
105
-
106
-bool send_ok[BUFSIZE];
107
-
108
 /**
78
 /**
109
  * Next Injected PROGMEM Command pointer. (nullptr == empty)
79
  * Next Injected PROGMEM Command pointer. (nullptr == empty)
110
  * Internal commands are enqueued ahead of serial / SD commands.
80
  * Internal commands are enqueued ahead of serial / SD commands.
116
  */
86
  */
117
 char GCodeQueue::injected_commands[64]; // = { 0 }
87
 char GCodeQueue::injected_commands[64]; // = { 0 }
118
 
88
 
119
-GCodeQueue::GCodeQueue() {
120
-  // Send "ok" after commands by default
121
-  LOOP_L_N(i, COUNT(send_ok)) send_ok[i] = true;
122
-}
123
-
124
-/**
125
- * Check whether there are any commands yet to be executed
126
- */
127
-bool GCodeQueue::has_commands_queued() {
128
-  return queue.length || injected_commands_P || injected_commands[0];
129
-}
130
-
131
-/**
132
- * Clear the Marlin command queue
133
- */
134
-void GCodeQueue::clear() {
135
-  index_r = index_w = length = 0;
136
-}
137
 
89
 
138
-/**
139
- * Once a new command is in the ring buffer, call this to commit it
140
- */
141
-void GCodeQueue::_commit_command(bool say_ok
90
+void GCodeQueue::RingBuffer::commit_command(bool skip_ok
142
   #if HAS_MULTI_SERIAL
91
   #if HAS_MULTI_SERIAL
143
     , serial_index_t serial_ind/*=-1*/
92
     , serial_index_t serial_ind/*=-1*/
144
   #endif
93
   #endif
145
 ) {
94
 ) {
146
-  send_ok[index_w] = say_ok;
147
-  TERN_(HAS_MULTI_SERIAL, port[index_w] = serial_ind);
95
+  commands[index_w].skip_ok = skip_ok;
96
+  TERN_(HAS_MULTI_SERIAL, commands[index_w].port = serial_ind);
148
   TERN_(POWER_LOSS_RECOVERY, recovery.commit_sdpos(index_w));
97
   TERN_(POWER_LOSS_RECOVERY, recovery.commit_sdpos(index_w));
149
-  if (++index_w >= BUFSIZE) index_w = 0;
150
-  length++;
98
+  advance_pos(index_w, 1);
151
 }
99
 }
152
 
100
 
153
 /**
101
 /**
155
  * Return true if the command was successfully added.
103
  * Return true if the command was successfully added.
156
  * Return false for a full buffer, or if the 'command' is a comment.
104
  * Return false for a full buffer, or if the 'command' is a comment.
157
  */
105
  */
158
-bool GCodeQueue::_enqueue(const char* cmd, bool say_ok/*=false*/
106
+bool GCodeQueue::RingBuffer::enqueue(const char* cmd, bool skip_ok/*=true*/
159
   #if HAS_MULTI_SERIAL
107
   #if HAS_MULTI_SERIAL
160
     , serial_index_t serial_ind/*=-1*/
108
     , serial_index_t serial_ind/*=-1*/
161
   #endif
109
   #endif
162
 ) {
110
 ) {
163
   if (*cmd == ';' || length >= BUFSIZE) return false;
111
   if (*cmd == ';' || length >= BUFSIZE) return false;
164
-  strcpy(command_buffer[index_w], cmd);
165
-  _commit_command(say_ok
112
+  strcpy(commands[index_w].buffer, cmd);
113
+  commit_command(skip_ok
166
     #if HAS_MULTI_SERIAL
114
     #if HAS_MULTI_SERIAL
167
       , serial_ind
115
       , serial_ind
168
     #endif
116
     #endif
175
  * Return true if the command was consumed
123
  * Return true if the command was consumed
176
  */
124
  */
177
 bool GCodeQueue::enqueue_one(const char* cmd) {
125
 bool GCodeQueue::enqueue_one(const char* cmd) {
178
-
179
-  //SERIAL_ECHOPGM("enqueue_one(\"");
180
-  //SERIAL_ECHO(cmd);
181
-  //SERIAL_ECHOPGM("\") \n");
126
+  //SERIAL_ECHOLNPAIR("enqueue_one(\"", cmd, "\")");
182
 
127
 
183
   if (*cmd == 0 || ISEOL(*cmd)) return true;
128
   if (*cmd == 0 || ISEOL(*cmd)) return true;
184
 
129
 
185
-  if (_enqueue(cmd)) {
130
+  if (ring_buffer.enqueue(cmd)) {
186
     SERIAL_ECHO_MSG(STR_ENQUEUEING, cmd, "\"");
131
     SERIAL_ECHO_MSG(STR_ENQUEUEING, cmd, "\"");
187
     return true;
132
     return true;
188
   }
133
   }
260
   char cmd[i + 1];
205
   char cmd[i + 1];
261
   memcpy_P(cmd, p, i);
206
   memcpy_P(cmd, p, i);
262
   cmd[i] = '\0';
207
   cmd[i] = '\0';
263
-  return _enqueue(cmd);
208
+  return ring_buffer.enqueue(cmd);
264
 }
209
 }
265
 
210
 
266
 /**
211
 /**
291
  *   P<int>  Planner space remaining
236
  *   P<int>  Planner space remaining
292
  *   B<int>  Block queue space remaining
237
  *   B<int>  Block queue space remaining
293
  */
238
  */
294
-void GCodeQueue::ok_to_send() {
239
+void GCodeQueue::RingBuffer::ok_to_send() {
295
   #if NO_TIMEOUTS > 0
240
   #if NO_TIMEOUTS > 0
296
     // Start counting from the last command's execution
241
     // Start counting from the last command's execution
297
     last_command_time = millis();
242
     last_command_time = millis();
298
   #endif
243
   #endif
244
+  CommandLine &command = commands[index_r];
299
   #if HAS_MULTI_SERIAL
245
   #if HAS_MULTI_SERIAL
300
-    const serial_index_t serial_ind = command_port();
246
+    const serial_index_t serial_ind = command.port;
301
     if (serial_ind < 0) return;
247
     if (serial_ind < 0) return;
302
     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
248
     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
303
   #endif
249
   #endif
304
-  if (!send_ok[index_r]) return;
250
+  if (command.skip_ok) return;
305
   SERIAL_ECHOPGM(STR_OK);
251
   SERIAL_ECHOPGM(STR_OK);
306
   #if ENABLED(ADVANCED_OK)
252
   #if ENABLED(ADVANCED_OK)
307
-    char* p = command_buffer[index_r];
253
+    char* p = command.buffer;
308
     if (*p == 'N') {
254
     if (*p == 'N') {
309
       SERIAL_CHAR(' ', *p++);
255
       SERIAL_CHAR(' ', *p++);
310
       while (NUMERIC_SIGNED(*p))
256
       while (NUMERIC_SIGNED(*p))
321
  * indicate that a command needs to be re-sent.
267
  * indicate that a command needs to be re-sent.
322
  */
268
  */
323
 void GCodeQueue::flush_and_request_resend() {
269
 void GCodeQueue::flush_and_request_resend() {
324
-  const serial_index_t serial_ind = command_port();
270
+  const serial_index_t serial_ind = ring_buffer.command_port();
325
   #if HAS_MULTI_SERIAL
271
   #if HAS_MULTI_SERIAL
326
     if (serial_ind < 0) return;                   // Never mind. Command came from SD or Flash Drive
272
     if (serial_ind < 0) return;                   // Never mind. Command came from SD or Flash Drive
327
     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
273
     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
328
   #endif
274
   #endif
329
   SERIAL_FLUSH();
275
   SERIAL_FLUSH();
330
   SERIAL_ECHOPGM(STR_RESEND);
276
   SERIAL_ECHOPGM(STR_RESEND);
331
-  SERIAL_ECHOLN(last_N[serial_ind] + 1);
332
-  ok_to_send();
277
+  SERIAL_ECHOLN(serial_state[serial_ind].last_N + 1);
333
 }
278
 }
334
 
279
 
335
-
336
 // Multiserial already handle the dispatch to/from multiple port by itself
280
 // Multiserial already handle the dispatch to/from multiple port by itself
337
 inline bool serial_data_available(uint8_t index = SERIAL_ALL) {
281
 inline bool serial_data_available(uint8_t index = SERIAL_ALL) {
338
   if (index == SERIAL_ALL) {
282
   if (index == SERIAL_ALL) {
339
     for (index = 0; index < NUM_SERIAL; index++) {
283
     for (index = 0; index < NUM_SERIAL; index++) {
340
-      if (SERIAL_IMPL.available(index) > 0) return true;
284
+      const int a = SERIAL_IMPL.available(index);
285
+      #if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
286
+        if (a > RX_BUFFER_SIZE - 2) {
287
+          PORT_REDIRECT(SERIAL_PORTMASK(index));
288
+          SERIAL_ERROR_MSG("RX BUF overflow, increase RX_BUFFER_SIZE: ", a);
289
+        }
290
+      #endif
291
+      if (a > 0) return true;
341
     }
292
     }
342
     return false;
293
     return false;
343
   }
294
   }
344
-  return SERIAL_IMPL.available(index) > 0;
295
+  const int a = SERIAL_IMPL.available(index);
296
+  #if BOTH(RX_BUFFER_MONITOR, RX_BUFFER_SIZE)
297
+    if (a > RX_BUFFER_SIZE - 2) {
298
+      PORT_REDIRECT(SERIAL_PORTMASK(index));
299
+      SERIAL_ERROR_MSG("RX BUF overflow, increase RX_BUFFER_SIZE: ", a);
300
+    }
301
+  #endif
302
+
303
+  return a > 0;
345
 }
304
 }
346
 
305
 
347
 inline int read_serial(const uint8_t index) { return SERIAL_IMPL.read(index); }
306
 inline int read_serial(const uint8_t index) { return SERIAL_IMPL.read(index); }
349
 void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) {
308
 void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) {
350
   PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
309
   PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
351
   SERIAL_ERROR_START();
310
   SERIAL_ERROR_START();
352
-  serialprintPGM(err);
353
-  SERIAL_ECHOLN(last_N[serial_ind]);
354
-  while (read_serial(serial_ind) != -1);      // Clear out the RX buffer
311
+  SERIAL_ECHOPGM_P(err);
312
+  SERIAL_ECHOLN(serial_state[serial_ind].last_N);
313
+  while (read_serial(serial_ind) != -1) { /* nada */ } // Clear out the RX buffer. Why don't use flush here ?
355
   flush_and_request_resend();
314
   flush_and_request_resend();
356
-  serial_count[serial_ind] = 0;
315
+  serial_state[serial_ind].count = 0;
357
 }
316
 }
358
 
317
 
359
 FORCE_INLINE bool is_M29(const char * const cmd) {  // matches "M29" & "M29 ", but not "M290", etc
318
 FORCE_INLINE bool is_M29(const char * const cmd) {  // matches "M29" & "M29 ", but not "M290", etc
440
  * left on the serial port.
399
  * left on the serial port.
441
  */
400
  */
442
 void GCodeQueue::get_serial_commands() {
401
 void GCodeQueue::get_serial_commands() {
443
-  static char serial_line_buffer[NUM_SERIAL][MAX_CMD_SIZE];
444
-
445
-  static uint8_t serial_input_state[NUM_SERIAL] = { PS_NORMAL };
446
-
447
   #if ENABLED(BINARY_FILE_TRANSFER)
402
   #if ENABLED(BINARY_FILE_TRANSFER)
448
     if (card.flag.binary_mode) {
403
     if (card.flag.binary_mode) {
449
       /**
404
       /**
451
        * receive buffer (which limits the packet size to MAX_CMD_SIZE).
406
        * receive buffer (which limits the packet size to MAX_CMD_SIZE).
452
        * The receive buffer also limits the packet size for reliable transmission.
407
        * The receive buffer also limits the packet size for reliable transmission.
453
        */
408
        */
454
-      binaryStream[card.transfer_port_index].receive(serial_line_buffer[card.transfer_port_index]);
409
+      binaryStream[card.transfer_port_index].receive(serial_state[card.transfer_port_index].line_buffer);
455
       return;
410
       return;
456
     }
411
     }
457
   #endif
412
   #endif
460
   // send "wait" to indicate Marlin is still waiting.
415
   // send "wait" to indicate Marlin is still waiting.
461
   #if NO_TIMEOUTS > 0
416
   #if NO_TIMEOUTS > 0
462
     const millis_t ms = millis();
417
     const millis_t ms = millis();
463
-    if (length == 0 && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
418
+    if (ring_buffer.empty() && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
464
       SERIAL_ECHOLNPGM(STR_WAIT);
419
       SERIAL_ECHOLNPGM(STR_WAIT);
465
       last_command_time = ms;
420
       last_command_time = ms;
466
     }
421
     }
467
   #endif
422
   #endif
468
 
423
 
469
-  /**
470
-   * Loop while serial characters are incoming and the queue is not full
471
-   */
472
-  while (length < BUFSIZE && serial_data_available()) {
424
+  // Loop while serial characters are incoming and the queue is not full
425
+  for (bool hadData = true; hadData;) {
426
+    // Unless a serial port has data, this will exit on next iteration
427
+    hadData = false;
428
+
473
     LOOP_L_N(p, NUM_SERIAL) {
429
     LOOP_L_N(p, NUM_SERIAL) {
430
+      // Check if the queue is full and exit if it is.
431
+      if (ring_buffer.full()) return;
474
 
432
 
475
-      const int c = read_serial(p);
476
-      if (c < 0) continue;
477
-
478
-      #if ENABLED(MEATPACK)
479
-        meatpack.handle_rx_char(uint8_t(c), p);
480
-        char c_res[2] = { 0, 0 };
481
-        const uint8_t char_count = meatpack.get_result_char(c_res);
482
-      #else
483
-        constexpr uint8_t char_count = 1;
484
-      #endif
433
+      // No data for this port ? Skip it
434
+      if (!serial_data_available(p)) continue;
485
 
435
 
486
-      LOOP_L_N(char_index, char_count) {
487
-        const char serial_char = TERN(MEATPACK, c_res[char_index], c);
436
+      // Ok, we have some data to process, let's make progress here
437
+      hadData = true;
488
 
438
 
489
-        if (ISEOL(serial_char)) {
439
+      const int c = read_serial(p);
440
+      if (c < 0) {
441
+        // This should never happen, let's log it
442
+        PORT_REDIRECT(SERIAL_PORTMASK(p));     // Reply to the serial port that sent the command
443
+        // Crash here to get more information why it failed
444
+        BUG_ON("SP available but read -1");
445
+        SERIAL_ERROR_MSG(STR_ERR_SERIAL_MISMATCH);
446
+        SERIAL_FLUSH();
447
+        continue;
448
+      }
490
 
449
 
491
-          // Reset our state, continue if the line was empty
492
-          if (process_line_done(serial_input_state[p], serial_line_buffer[p], serial_count[p]))
493
-            continue;
450
+      const char serial_char = (char)c;
451
+      SerialState &serial = serial_state[p];
494
 
452
 
495
-          char* command = serial_line_buffer[p];
453
+      if (ISEOL(serial_char)) {
496
 
454
 
497
-          while (*command == ' ') command++;                   // Skip leading spaces
498
-          char *npos = (*command == 'N') ? command : nullptr;  // Require the N parameter to start the line
455
+        // Reset our state, continue if the line was empty
456
+        if (process_line_done(serial.input_state, serial.line_buffer, serial.count))
457
+          continue;
499
 
458
 
500
-          if (npos) {
459
+        char* command = serial.line_buffer;
501
 
460
 
502
-            const bool M110 = !!strstr_P(command, PSTR("M110"));
461
+        while (*command == ' ') command++;                   // Skip leading spaces
462
+        char *npos = (*command == 'N') ? command : nullptr;  // Require the N parameter to start the line
503
 
463
 
504
-            if (M110) {
505
-              char* n2pos = strchr(command + 4, 'N');
506
-              if (n2pos) npos = n2pos;
507
-            }
464
+        if (npos) {
508
 
465
 
509
-            const long gcode_N = strtol(npos + 1, nullptr, 10);
466
+          const bool M110 = !!strstr_P(command, PSTR("M110"));
510
 
467
 
511
-            if (gcode_N != last_N[p] + 1 && !M110)
512
-              return gcode_line_error(PSTR(STR_ERR_LINE_NO), p);
468
+          if (M110) {
469
+            char* n2pos = strchr(command + 4, 'N');
470
+            if (n2pos) npos = n2pos;
471
+          }
513
 
472
 
514
-            char *apos = strrchr(command, '*');
515
-            if (apos) {
516
-              uint8_t checksum = 0, count = uint8_t(apos - command);
517
-              while (count) checksum ^= command[--count];
518
-              if (strtol(apos + 1, nullptr, 10) != checksum)
519
-                return gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
520
-            }
521
-            else
522
-              return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
473
+          const long gcode_N = strtol(npos + 1, nullptr, 10);
523
 
474
 
524
-            last_N[p] = gcode_N;
475
+          if (gcode_N != serial.last_N + 1 && !M110) {
476
+            // In case of error on a serial port, don't prevent other serial port from making progress
477
+            gcode_line_error(PSTR(STR_ERR_LINE_NO), p);
478
+            break;
525
           }
479
           }
526
-          #if ENABLED(SDSUPPORT)
527
-            // Pronterface "M29" and "M29 " has no line number
528
-            else if (card.flag.saving && !is_M29(command))
529
-              return gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
530
-          #endif
531
 
480
 
532
-          //
533
-          // Movement commands give an alert when the machine is stopped
534
-          //
535
-
536
-          if (IsStopped()) {
537
-            char* gpos = strchr(command, 'G');
538
-            if (gpos) {
539
-              switch (strtol(gpos + 1, nullptr, 10)) {
540
-                case 0: case 1:
541
-                #if ENABLED(ARC_SUPPORT)
542
-                  case 2: case 3:
543
-                #endif
544
-                #if ENABLED(BEZIER_CURVE_SUPPORT)
545
-                  case 5:
546
-                #endif
547
-                  PORT_REDIRECT(SERIAL_PORTMASK(p));     // Reply to the serial port that sent the command
548
-                  SERIAL_ECHOLNPGM(STR_ERR_STOPPED);
549
-                  LCD_MESSAGEPGM(MSG_STOPPED);
550
-                  break;
551
-              }
481
+          char *apos = strrchr(command, '*');
482
+          if (apos) {
483
+            uint8_t checksum = 0, count = uint8_t(apos - command);
484
+            while (count) checksum ^= command[--count];
485
+            if (strtol(apos + 1, nullptr, 10) != checksum) {
486
+              // In case of error on a serial port, don't prevent other serial port from making progress
487
+              gcode_line_error(PSTR(STR_ERR_CHECKSUM_MISMATCH), p);
488
+              break;
552
             }
489
             }
553
           }
490
           }
491
+          else {
492
+            // In case of error on a serial port, don't prevent other serial port from making progress
493
+            gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
494
+            break;
495
+          }
496
+
497
+          serial.last_N = gcode_N;
498
+        }
499
+        #if ENABLED(SDSUPPORT)
500
+          // Pronterface "M29" and "M29 " has no line number
501
+          else if (card.flag.saving && !is_M29(command)) {
502
+            gcode_line_error(PSTR(STR_ERR_NO_CHECKSUM), p);
503
+            break;
504
+          }
505
+        #endif
554
 
506
 
555
-          #if DISABLED(EMERGENCY_PARSER)
556
-            // Process critical commands early
557
-            if (command[0] == 'M') switch (command[3]) {
558
-              case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break;
559
-              case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break;
560
-              case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break;
507
+        //
508
+        // Movement commands give an alert when the machine is stopped
509
+        //
510
+
511
+        if (IsStopped()) {
512
+          char* gpos = strchr(command, 'G');
513
+          if (gpos) {
514
+            switch (strtol(gpos + 1, nullptr, 10)) {
515
+              case 0: case 1:
516
+              #if ENABLED(ARC_SUPPORT)
517
+                case 2: case 3:
518
+              #endif
519
+              #if ENABLED(BEZIER_CURVE_SUPPORT)
520
+                case 5:
521
+              #endif
522
+                PORT_REDIRECT(SERIAL_PORTMASK(p));     // Reply to the serial port that sent the command
523
+                SERIAL_ECHOLNPGM(STR_ERR_STOPPED);
524
+                LCD_MESSAGEPGM(MSG_STOPPED);
525
+                break;
561
             }
526
             }
562
-          #endif
527
+          }
528
+        }
563
 
529
 
564
-          #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0
565
-            last_command_time = ms;
566
-          #endif
530
+        #if DISABLED(EMERGENCY_PARSER)
531
+          // Process critical commands early
532
+          if (command[0] == 'M') switch (command[3]) {
533
+            case '8': if (command[2] == '0' && command[1] == '1') { wait_for_heatup = false; TERN_(HAS_LCD_MENU, wait_for_user = false); } break;
534
+            case '2': if (command[2] == '1' && command[1] == '1') kill(M112_KILL_STR, nullptr, true); break;
535
+            case '0': if (command[1] == '4' && command[2] == '1') quickstop_stepper(); break;
536
+          }
537
+        #endif
567
 
538
 
568
-          // Add the command to the queue
569
-          _enqueue(serial_line_buffer[p], true
570
-            #if HAS_MULTI_SERIAL
571
-              , p
572
-            #endif
573
-          );
574
-        }
575
-        else
576
-          process_stream_char(serial_char, serial_input_state[p], serial_line_buffer[p], serial_count[p]);
539
+        #if NO_TIMEOUTS > 0
540
+          last_command_time = ms;
541
+        #endif
577
 
542
 
578
-      } // char_count loop
543
+        // Add the command to the queue
544
+        ring_buffer.enqueue(serial.line_buffer, false
545
+          #if HAS_MULTI_SERIAL
546
+            , p
547
+          #endif
548
+        );
549
+      }
550
+      else
551
+        process_stream_char(serial_char, serial.input_state, serial.line_buffer, serial.count);
579
 
552
 
580
     } // NUM_SERIAL loop
553
     } // NUM_SERIAL loop
581
   } // queue has space, serial has data
554
   } // queue has space, serial has data
595
     if (!IS_SD_PRINTING()) return;
568
     if (!IS_SD_PRINTING()) return;
596
 
569
 
597
     int sd_count = 0;
570
     int sd_count = 0;
598
-    while (length < BUFSIZE && !card.eof()) {
571
+    while (!ring_buffer.full() && !card.eof()) {
599
       const int16_t n = card.get();
572
       const int16_t n = card.get();
600
       const bool card_eof = card.eof();
573
       const bool card_eof = card.eof();
601
       if (n < 0 && !card_eof) { SERIAL_ERROR_MSG(STR_SD_ERR_READ); continue; }
574
       if (n < 0 && !card_eof) { SERIAL_ERROR_MSG(STR_SD_ERR_READ); continue; }
602
 
575
 
576
+      CommandLine &command = ring_buffer.commands[ring_buffer.index_w];
603
       const char sd_char = (char)n;
577
       const char sd_char = (char)n;
604
       const bool is_eol = ISEOL(sd_char);
578
       const bool is_eol = ISEOL(sd_char);
605
       if (is_eol || card_eof) {
579
       if (is_eol || card_eof) {
606
 
580
 
581
+
607
         // Reset stream state, terminate the buffer, and commit a non-empty command
582
         // Reset stream state, terminate the buffer, and commit a non-empty command
608
         if (!is_eol && sd_count) ++sd_count;          // End of file with no newline
583
         if (!is_eol && sd_count) ++sd_count;          // End of file with no newline
609
-        if (!process_line_done(sd_input_state, command_buffer[index_w], sd_count)) {
584
+        if (!process_line_done(sd_input_state, command.buffer, sd_count)) {
610
 
585
 
611
           // M808 L saves the sdpos of the next line. M808 loops to a new sdpos.
586
           // M808 L saves the sdpos of the next line. M808 loops to a new sdpos.
612
-          TERN_(GCODE_REPEAT_MARKERS, repeat.early_parse_M808(command_buffer[index_w]));
587
+          TERN_(GCODE_REPEAT_MARKERS, repeat.early_parse_M808(command.buffer));
613
 
588
 
614
           // Put the new command into the buffer (no "ok" sent)
589
           // Put the new command into the buffer (no "ok" sent)
615
-          _commit_command(false);
590
+          ring_buffer.commit_command(true);
616
 
591
 
617
-          // Prime Power-Loss Recovery for the NEXT _commit_command
592
+          // Prime Power-Loss Recovery for the NEXT commit_command
618
           TERN_(POWER_LOSS_RECOVERY, recovery.cmd_sdpos = card.getIndex());
593
           TERN_(POWER_LOSS_RECOVERY, recovery.cmd_sdpos = card.getIndex());
619
         }
594
         }
620
 
595
 
621
         if (card.eof()) card.fileHasFinished();         // Handle end of file reached
596
         if (card.eof()) card.fileHasFinished();         // Handle end of file reached
622
       }
597
       }
623
       else
598
       else
624
-        process_stream_char(sd_char, sd_input_state, command_buffer[index_w], sd_count);
599
+        process_stream_char(sd_char, sd_input_state, command.buffer, sd_count);
625
     }
600
     }
626
   }
601
   }
627
 
602
 
634
  *  - The SD card file being actively printed
609
  *  - The SD card file being actively printed
635
  */
610
  */
636
 void GCodeQueue::get_available_commands() {
611
 void GCodeQueue::get_available_commands() {
612
+  if (ring_buffer.full()) return;
637
 
613
 
638
   get_serial_commands();
614
   get_serial_commands();
639
 
615
 
649
   if (process_injected_command_P() || process_injected_command()) return;
625
   if (process_injected_command_P() || process_injected_command()) return;
650
 
626
 
651
   // Return if the G-code buffer is empty
627
   // Return if the G-code buffer is empty
652
-  if (!length) return;
628
+  if (ring_buffer.empty()) return;
653
 
629
 
654
   #if ENABLED(SDSUPPORT)
630
   #if ENABLED(SDSUPPORT)
655
 
631
 
656
     if (card.flag.saving) {
632
     if (card.flag.saving) {
657
-      char* command = command_buffer[index_r];
658
-      if (is_M29(command)) {
633
+      char * const cmd = ring_buffer.peek_next_command_string();
634
+      if (is_M29(cmd)) {
659
         // M29 closes the file
635
         // M29 closes the file
660
         card.closefile();
636
         card.closefile();
661
         SERIAL_ECHOLNPGM(STR_FILE_SAVED);
637
         SERIAL_ECHOLNPGM(STR_FILE_SAVED);
673
       }
649
       }
674
       else {
650
       else {
675
         // Write the string from the read buffer to SD
651
         // Write the string from the read buffer to SD
676
-        card.write_command(command);
652
+        card.write_command(cmd);
677
         if (card.flag.logging)
653
         if (card.flag.logging)
678
           gcode.process_next_command(); // The card is saving because it's logging
654
           gcode.process_next_command(); // The card is saving because it's logging
679
         else
655
         else
690
   #endif // SDSUPPORT
666
   #endif // SDSUPPORT
691
 
667
 
692
   // The queue may be reset by a command handler or by code invoked by idle() within a handler
668
   // The queue may be reset by a command handler or by code invoked by idle() within a handler
693
-  --length;
694
-  if (++index_r >= BUFSIZE) index_r = 0;
695
-
669
+  ring_buffer.advance_pos(ring_buffer.index_r, -1);
696
 }
670
 }

+ 67
- 32
Marlin/src/gcode/queue.h Datei anzeigen

31
 class GCodeQueue {
31
 class GCodeQueue {
32
 public:
32
 public:
33
   /**
33
   /**
34
-   * GCode line number handling. Hosts may include line numbers when sending
35
-   * commands to Marlin, and lines will be checked for sequentiality.
36
-   * M110 N<int> sets the current line number.
34
+   * The buffers per serial port.
37
    */
35
    */
36
+  struct SerialState {
37
+    /**
38
+     * GCode line number handling. Hosts may include line numbers when sending
39
+     * commands to Marlin, and lines will be checked for sequentiality.
40
+     * M110 N<int> sets the current line number.
41
+     */
42
+    long last_N;
43
+    int count;                        //!< Number of characters read in the current line of serial input
44
+    char line_buffer[MAX_CMD_SIZE];   //!< The current line accumulator
45
+    uint8_t input_state;              //!< The input state
46
+  };
38
 
47
 
39
-  static long last_N[NUM_SERIAL];
48
+  static SerialState serial_state[NUM_SERIAL]; //!< Serial states for each serial port
40
 
49
 
41
   /**
50
   /**
42
    * GCode Command Queue
51
    * GCode Command Queue
43
-   * A simple ring buffer of BUFSIZE command strings.
52
+   * A simple (circular) ring buffer of BUFSIZE command strings.
44
    *
53
    *
45
    * Commands are copied into this buffer by the command injectors
54
    * Commands are copied into this buffer by the command injectors
46
    * (immediate, serial, sd card) and they are processed sequentially by
55
    * (immediate, serial, sd card) and they are processed sequentially by
47
    * the main loop. The gcode.process_next_command method parses the next
56
    * the main loop. The gcode.process_next_command method parses the next
48
    * command and hands off execution to individual handler functions.
57
    * command and hands off execution to individual handler functions.
49
    */
58
    */
50
-  static uint8_t length,  // Count of commands in the queue
51
-                 index_r; // Ring buffer read position
52
-
53
-  static char command_buffer[BUFSIZE][MAX_CMD_SIZE];
59
+  struct CommandLine {
60
+    char buffer[MAX_CMD_SIZE];                    //!< The command buffer
61
+    bool skip_ok;                                 //!< Skip sending ok when command is processed?
62
+    TERN_(HAS_MULTI_SERIAL, serial_index_t port); //!< Serial port the command was received on
63
+  };
54
 
64
 
55
   /**
65
   /**
56
-   * The port that the command was received on
66
+   * A handy ring buffer type
57
    */
67
    */
58
-  #if HAS_MULTI_SERIAL
59
-    static serial_index_t port[BUFSIZE];
60
-  #endif
61
-  static inline serial_index_t command_port() { return TERN0(HAS_MULTI_SERIAL, port[index_r]); }
68
+  struct RingBuffer {
69
+    uint8_t length,                 //!< Number of commands in the queue
70
+            index_r,                //!< Ring buffer's read position
71
+            index_w;                //!< Ring buffer's write position
72
+    CommandLine commands[BUFSIZE];  //!< The ring buffer of commands
73
+
74
+    inline serial_index_t command_port() const { return TERN0(HAS_MULTI_SERIAL, commands[index_r].port); }
75
+
76
+    inline void clear() { length = index_r = index_w = 0; }
77
+
78
+    void advance_pos(uint8_t &p, const int inc) { if (++p >= BUFSIZE) p = 0; length += inc; }
79
+
80
+    void commit_command(bool skip_ok
81
+      #if HAS_MULTI_SERIAL
82
+        , serial_index_t serial_ind=-1
83
+      #endif
84
+    );
85
+
86
+    bool enqueue(const char* cmd, bool skip_ok = true
87
+      #if HAS_MULTI_SERIAL
88
+        , serial_index_t serial_ind=-1
89
+      #endif
90
+    );
91
+
92
+    void ok_to_send();
93
+
94
+    inline bool full(uint8_t cmdCount=1) const { return length > (BUFSIZE - cmdCount); }
95
+
96
+    inline bool empty() const { return length == 0; }
62
 
97
 
63
-  GCodeQueue();
98
+    inline CommandLine& peek_next_command() { return commands[index_r]; }
99
+
100
+    inline char* peek_next_command_string() { return peek_next_command().buffer; }
101
+  };
102
+
103
+  /**
104
+   * The ring buffer of commands
105
+   */
106
+  static RingBuffer ring_buffer;
64
 
107
 
65
   /**
108
   /**
66
    * Clear the Marlin command queue
109
    * Clear the Marlin command queue
67
    */
110
    */
68
-  static void clear();
111
+  static void clear() { ring_buffer.clear(); }
69
 
112
 
70
   /**
113
   /**
71
    * Next Injected Command (PROGMEM) pointer. (nullptr == empty)
114
    * Next Injected Command (PROGMEM) pointer. (nullptr == empty)
112
   /**
155
   /**
113
    * Check whether there are any commands yet to be executed
156
    * Check whether there are any commands yet to be executed
114
    */
157
    */
115
-  static bool has_commands_queued();
158
+  static bool has_commands_queued() { return ring_buffer.length || injected_commands_P || injected_commands[0]; }
116
 
159
 
117
   /**
160
   /**
118
    * Get the next command in the queue, optionally log it to SD, then dispatch it
161
    * Get the next command in the queue, optionally log it to SD, then dispatch it
136
    *   P<int>  Planner space remaining
179
    *   P<int>  Planner space remaining
137
    *   B<int>  Block queue space remaining
180
    *   B<int>  Block queue space remaining
138
    */
181
    */
139
-  static void ok_to_send();
182
+  static inline void ok_to_send() { ring_buffer.ok_to_send(); }
140
 
183
 
141
   /**
184
   /**
142
    * Clear the serial line and request a resend of
185
    * Clear the serial line and request a resend of
144
    */
187
    */
145
   static void flush_and_request_resend();
188
   static void flush_and_request_resend();
146
 
189
 
147
-private:
190
+  /**
191
+   * (Re)Set the current line number for the last received command
192
+   */
193
+  static inline void set_current_line_number(long n) { serial_state[ring_buffer.command_port()].last_N = n; }
148
 
194
 
149
-  static uint8_t index_w;  // Ring buffer write position
195
+private:
150
 
196
 
151
   static void get_serial_commands();
197
   static void get_serial_commands();
152
 
198
 
154
     static void get_sdcard_commands();
200
     static void get_sdcard_commands();
155
   #endif
201
   #endif
156
 
202
 
157
-  static void _commit_command(bool say_ok
158
-    #if HAS_MULTI_SERIAL
159
-      , serial_index_t serial_ind=-1
160
-    #endif
161
-  );
162
-
163
-  static bool _enqueue(const char* cmd, bool say_ok=false
164
-    #if HAS_MULTI_SERIAL
165
-      , serial_index_t serial_ind=-1
166
-    #endif
167
-  );
168
-
169
   // Process the next "immediate" command (PROGMEM)
203
   // Process the next "immediate" command (PROGMEM)
170
   static bool process_injected_command_P();
204
   static bool process_injected_command_P();
171
 
205
 
180
 
214
 
181
   static void gcode_line_error(PGM_P const err, const serial_index_t serial_ind);
215
   static void gcode_line_error(PGM_P const err, const serial_index_t serial_ind);
182
 
216
 
217
+  friend class GcodeSuite;
183
 };
218
 };
184
 
219
 
185
 extern GCodeQueue queue;
220
 extern GCodeQueue queue;

+ 1
- 1
Marlin/src/lcd/extui/lib/mks_ui/draw_keyboard.cpp Datei anzeigen

162
           draw_return_ui();
162
           draw_return_ui();
163
           break;
163
           break;
164
         case GCodeCommand:
164
         case GCodeCommand:
165
-          if (queue.length <= (BUFSIZE - 3)) {
165
+          if (!queue.ring_buffer.full(3)) {
166
             // Hook anything that goes to the serial port
166
             // Hook anything that goes to the serial port
167
             MYSERIAL0.setHook(lv_serial_capt_hook, lv_eom_hook, 0);
167
             MYSERIAL0.setHook(lv_serial_capt_hook, lv_eom_hook, 0);
168
             queue.enqueue_one_now(ret_ta_txt);
168
             queue.enqueue_one_now(ret_ta_txt);

+ 1
- 1
Marlin/src/lcd/extui/lib/mks_ui/draw_manuaLevel.cpp Datei anzeigen

46
 
46
 
47
   switch (obj->mks_obj_id) {
47
   switch (obj->mks_obj_id) {
48
     case ID_M_POINT1 ... ID_M_POINT5:
48
     case ID_M_POINT1 ... ID_M_POINT5:
49
-      if (queue.length == 0) {
49
+      if (queue.ring_buffer.empty()) {
50
         if (uiCfg.leveling_first_time) {
50
         if (uiCfg.leveling_first_time) {
51
           uiCfg.leveling_first_time = false;
51
           uiCfg.leveling_first_time = false;
52
           queue.inject_P(G28_STR);
52
           queue.inject_P(G28_STR);

+ 1
- 1
Marlin/src/lcd/extui/lib/mks_ui/draw_move_motor.cpp Datei anzeigen

54
 static void event_handler(lv_obj_t *obj, lv_event_t event) {
54
 static void event_handler(lv_obj_t *obj, lv_event_t event) {
55
   char str_1[16];
55
   char str_1[16];
56
   if (event != LV_EVENT_RELEASED) return;
56
   if (event != LV_EVENT_RELEASED) return;
57
-  if (queue.length <= (BUFSIZE - 3)) {
57
+  if (!queue.ring_buffer.full(3)) {
58
     bool do_inject = true;
58
     bool do_inject = true;
59
     float dist = uiCfg.move_dist;
59
     float dist = uiCfg.move_dist;
60
     switch (obj->mks_obj_id) {
60
     switch (obj->mks_obj_id) {

+ 2
- 2
Marlin/src/lcd/extui/lib/mks_ui/wifi_module.cpp Datei anzeigen

1613
       if (wifiTransError.flag != 0x1) WIFI_IO1_RESET();
1613
       if (wifiTransError.flag != 0x1) WIFI_IO1_RESET();
1614
       getDataF = 1;
1614
       getDataF = 1;
1615
     }
1615
     }
1616
-    if (need_ok_later &&  (queue.length < BUFSIZE)) {
1616
+    if (need_ok_later && !queue.ring_buffer.full()) {
1617
       need_ok_later = false;
1617
       need_ok_later = false;
1618
       send_to_wifi((uint8_t *)"ok\r\n", strlen("ok\r\n"));
1618
       send_to_wifi((uint8_t *)"ok\r\n", strlen("ok\r\n"));
1619
     }
1619
     }
1772
   static int wifi_read_count = 0;
1772
   static int wifi_read_count = 0;
1773
 
1773
 
1774
   if (espGcodeFifo.wait_tick > 5) {
1774
   if (espGcodeFifo.wait_tick > 5) {
1775
-    while ((queue.length < BUFSIZE) && (espGcodeFifo.r != espGcodeFifo.w)) {
1775
+    while (!queue.ring_buffer.full() && (espGcodeFifo.r != espGcodeFifo.w)) {
1776
 
1776
 
1777
       espGcodeFifo.wait_tick = 0;
1777
       espGcodeFifo.wait_tick = 0;
1778
 
1778
 

+ 59
- 0
docs/Queue.md Datei anzeigen

1
+# Marlin's command queue concept
2
+
3
+Marlin Firmware processes G-code commands as they arrive from multiple sources, including the SD card and one or more serial ports such as USB-connected hosts, WiFi, Bluetooth, and so on.
4
+
5
+Marlin is also continuously processing the commands at the front of the queue, converting them into signals for many physical actuators such as motors, heaters, lasers, and RGB LEDs.
6
+
7
+The firmware needs to maintain continuity and timing so the command senders remain unblocked, while still performing physical movements and other actions in real-time, respecting the physical limits of stepper motors and other peripherals.
8
+
9
+To keep things flowing Marlin feeds a single queue of G-code commands from all inputs, inserting them in the order received. Movement commands immediately go into the Planner Buffer, if there is room. The buffering of a move is considered the completion of the command, so if a non-movement command has to occur after a move is done, and not just after a move is buffered, then there has to be an `M400` to wait for the Planner Buffer to finish.
10
+
11
+Whenever the command queue gets full the sender needs to wait for space to open up, and the host may need to re-send the last command again. Marlin does some handshaking to keep the host informed during a print job, described below.
12
+
13
+An opposite problem called "planner starvation" occurs when Marlin receives many short and fast moves in a row so the Planner Buffer gets completed very quickly. In this case the host can't send commands fast enough to prevent the Planner Buffer from emptying out. Planner starvation causes obvious stuttering and is commonly seen on overloaded deltabots during small curves. Marlin has strategies to mitigate this issue, but sometimes a model has to be re-sliced (or the G-code has to be post-processed with Arc Welder) just to stay within the machine's inherent limits.
14
+
15
+Here's a basic flowchart of Marlin command processing:
16
+```
17
++------+                                Marlin's GCodeQueue
18
+|      |                             +--------------------------------------+     +-----------+
19
+| Host |                             |  SerialState         RingBuffer      |     |           |
20
+|      |             Marlin          |  NUM_SERIAL           BUF_SIZE       |     | Marlin    |
21
++--+---+        R/TX_BUFFER_SIZE     |    +---+        +------------------+ |     |           |
22
+   |             +------------+      |    |   |        |                  | |     | GCode     |
23
+   |             |            |      |    |   |        |   MAX_CMD_SIZE   +-+-----> processor |
24
+   |             | Platform   |      |    |   | On EOL | +--------------+ | r_pos |           |
25
+   +-------------> serial's   +----------->   +--------> |   G-code     | | |     +-----------+
26
+                 | buffer     |      |    |   |  w_pos | |   command    | | |
27
+                 |            |      |    |   |        | |    line      | | |
28
+                 +------------+      |    +---+        | +--------------+ | |
29
+                                     |  Line buffer    |    x BUF_SIZE    | |
30
+                                     |                 |                  | |
31
+                                     |                 |                  | |
32
+                                     |                 |                  | |
33
+                                     |                 |                  | |
34
+                                     |                 +------------------+ |
35
+                                     |                                      |
36
+                                     |                                      |
37
+                                     |                                      |
38
+                                     +--------------------------------------+
39
+```
40
+
41
+Marlin is a single-threaded application with a main `loop()` that manages the command queue and an `idle()` routine that manages the hardware. The command queue is handled in two stages:
42
+1. The `idle()` routine reads all inputs and attempts to enqueue any completed command lines.
43
+2. The main `loop()` gets the command at the front the G-code queue (if any) and runs it. Each G-code command blocks the main loop, preventing the queue from advancing until it returns. To keep essential tasks and the UI running, any commands that run a long process need to call `idle()` frequently.
44
+
45
+## Synchronization
46
+
47
+To maintain synchronization Marlin replies "`ok`" to the host as soon as the command has been enqueued. This lets the host know that it can send another command, and well-behaved hosts will wait for this message. With `ADVANCED_OK` enabled the `ok` message includes extra information (such as the number of slots left in the queue).
48
+
49
+If no data is available on the serial buffer, Marlin can be configured to periodically send a "`wait`" message to the host. This was the only method of "host keepalive" provided in Marlin 1.0, but today the better options are `HOST_KEEPALIVE` and `ADVANCED_OK`.
50
+
51
+## Limitation of the design
52
+
53
+Some limitations to the design are evident:
54
+1. Whenever the G-code processor is busy processing a command, the G-code queue cannot advance.
55
+2. A long command like `G29` causes commands to pile up and to fill the queue, making the host wait.
56
+3. Each serial input requires a buffer large enough for a complete G-code line. This is set by `MAX_CMD_SIZE` with a default value of 96.
57
+4. Since serial buffer sizes are likely used as ring buffers themselves, as an optimization their sizes must be a power of 2 (64 or 128 bytes recommended).
58
+5. If a host sends too much G-code at once it can saturate the `GCodeQueue`. This doesn't do anything to improve the processing rate of Marlin since only one command can be dispatched per loop iteration.
59
+6. With the previous point in mind, it's clear that the longstanding wisdom that you don't need a large `BUF_SIZE` is not just apocryphal. The default value of 4 is typically just fine for a single serial port. (And, if you decide to send a `G25` to pause the machine, the wait will be much shorter!)

Laden…
Abbrechen
Speichern