Explorar el Código

Combined LPC / Serial fixes (#21178)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
X-Ryl669 hace 3 años
padre
commit
f003e52009
No account linked to committer's email address

+ 5
- 6
Marlin/src/HAL/DUE/MarlinSerialUSB.cpp Ver fichero

@@ -92,12 +92,11 @@ int MarlinSerialUSB::read() {
92 92
   return c;
93 93
 }
94 94
 
95
-bool MarlinSerialUSB::available() {
96
-    /* If Pending chars */
97
-  return pending_char >= 0 ||
98
-    /* or USB CDC enumerated and configured on the PC side and some
99
-       bytes where sent to us */
100
-      (usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
95
+int MarlinSerialUSB::available() {
96
+  if (pending_char > 0) return pending_char;
97
+  return pending_char == 0 ||
98
+    // or USB CDC enumerated and configured on the PC side and some bytes where sent to us */
99
+    (usb_task_cdc_isenabled() && udi_cdc_is_rx_ready());
101 100
 }
102 101
 
103 102
 void MarlinSerialUSB::flush() { }

+ 1
- 1
Marlin/src/HAL/DUE/MarlinSerialUSB.h Ver fichero

@@ -39,7 +39,7 @@ struct MarlinSerialUSB {
39 39
   int peek();
40 40
   int read();
41 41
   void flush();
42
-  bool available();
42
+  int available();
43 43
   size_t write(const uint8_t c);
44 44
 
45 45
   #if ENABLED(SERIAL_STATS_DROPPED_RX)

+ 34
- 8
Marlin/src/HAL/LPC1768/MarlinSerial.cpp Ver fichero

@@ -25,20 +25,46 @@
25 25
 #include "MarlinSerial.h"
26 26
 
27 27
 #if ANY_SERIAL_IS(0)
28
-  MSerialT MSerial(true, LPC_UART0);
29
-  extern "C" void UART0_IRQHandler() { MSerial.IRQHandler(); }
28
+  MarlinSerial _MSerial(LPC_UART0);
29
+  MSerialT MSerial(true, _MSerial);
30
+  extern "C" void UART0_IRQHandler() { _MSerial.IRQHandler(); }
30 31
 #endif
31 32
 #if ANY_SERIAL_IS(1)
32
-  MSerialT MSerial1(true, (LPC_UART_TypeDef *) LPC_UART1);
33
-  extern "C" void UART1_IRQHandler() { MSerial1.IRQHandler(); }
33
+  MarlinSerial _MSerial1((LPC_UART_TypeDef *) LPC_UART1);
34
+  MSerialT MSerial1(true, _MSerial1);
35
+  extern "C" void UART1_IRQHandler() { _MSerial1.IRQHandler(); }
34 36
 #endif
35 37
 #if ANY_SERIAL_IS(2)
36
-  MSerialT MSerial2(true, LPC_UART2);
37
-  extern "C" void UART2_IRQHandler() { MSerial2.IRQHandler(); }
38
+  MarlinSerial _MSerial2(LPC_UART2);
39
+  MSerialT MSerial2(true, _MSerial2);
40
+  extern "C" void UART2_IRQHandler() { _MSerial2.IRQHandler(); }
38 41
 #endif
39 42
 #if ANY_SERIAL_IS(3)
40
-  MSerialT MSerial3(true, LPC_UART3);
41
-  extern "C" void UART3_IRQHandler() { MSerial3.IRQHandler(); }
43
+  MarlinSerial _MSerial3(LPC_UART3);
44
+  MSerialT MSerial3(true, _MSerial3);
45
+  extern "C" void UART3_IRQHandler() { _MSerial3.IRQHandler(); }
46
+#endif
47
+
48
+#if ENABLED(EMERGENCY_PARSER)
49
+
50
+  bool MarlinSerial::recv_callback(const char c) {
51
+    // Need to figure out which serial port we are and react in consequence (Marlin does not have CONTAINER_OF macro)
52
+    if (false) {}
53
+    #if ANY_SERIAL_IS(0)
54
+      else if (this == &_MSerial) emergency_parser.update(MSerial.emergency_state, c);
55
+    #endif
56
+    #if ANY_SERIAL_IS(1)
57
+      else if (this == &_MSerial1) emergency_parser.update(MSerial1.emergency_state, c);
58
+    #endif
59
+    #if ANY_SERIAL_IS(2)
60
+      else if (this == &_MSerial2) emergency_parser.update(MSerial2.emergency_state, c);
61
+    #endif
62
+    #if ANY_SERIAL_IS(3)
63
+      else if (this == &_MSerial3) emergency_parser.update(MSerial3.emergency_state, c);
64
+    #endif
65
+    return true;
66
+  }
67
+
42 68
 #endif
43 69
 
44 70
 #endif // TARGET_LPC1768

+ 11
- 5
Marlin/src/HAL/LPC1768/MarlinSerial.h Ver fichero

@@ -47,15 +47,21 @@ public:
47 47
   void end() {}
48 48
 
49 49
   #if ENABLED(EMERGENCY_PARSER)
50
-    bool recv_callback(const char c) override {
51
-      emergency_parser.update(static_cast<Serial0Type<MarlinSerial> *>(this)->emergency_state, c);
52
-      return true; // do not discard character
53
-    }
50
+    bool recv_callback(const char c) override;
54 51
   #endif
55 52
 };
56 53
 
57
-typedef Serial0Type<MarlinSerial> MSerialT;
54
+// On LPC176x framework, HardwareSerial does not implement the same interface as Arduino's Serial, so overloads
55
+// of 'available' and 'read' method are not used in this multiple inheritance scenario.
56
+// Instead, use a ForwardSerial here that adapts the interface.
57
+typedef ForwardSerial0Type<MarlinSerial> MSerialT;
58 58
 extern MSerialT MSerial;
59 59
 extern MSerialT MSerial1;
60 60
 extern MSerialT MSerial2;
61 61
 extern MSerialT MSerial3;
62
+
63
+// Consequently, we can't use a RuntimeSerial either. The workaround would be to use a RuntimeSerial<ForwardSerial<MarlinSerial>> type here
64
+// Right now, let's ignore this until it's actually required.
65
+#if ENABLED(SERIAL_RUNTIME_HOOK)
66
+  #error "SERIAL_RUNTIME_HOOK is not yet supported for LPC176x."
67
+#endif

+ 2
- 2
Marlin/src/HAL/LPC1768/usb_serial.cpp Ver fichero

@@ -29,8 +29,8 @@
29 29
 
30 30
 EmergencyParser::State emergency_state;
31 31
 
32
-bool CDC_RecvCallback(const char buffer) {
33
-  emergency_parser.update(emergency_state, buffer);
32
+bool CDC_RecvCallback(const char c) {
33
+  emergency_parser.update(emergency_state, c);
34 34
   return true;
35 35
 }
36 36
 

+ 2
- 2
Marlin/src/HAL/shared/Delay.cpp Ver fichero

@@ -51,7 +51,7 @@
51 51
   // Use hardware cycle counter instead, it's much safer
52 52
   void delay_dwt(uint32_t count) {
53 53
     // Reuse the ASM_CYCLES_PER_ITERATION variable to avoid wasting another useless variable
54
-    register uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
54
+    uint32_t start = HW_REG(_DWT_CYCCNT) - ASM_CYCLES_PER_ITERATION, elapsed;
55 55
     do {
56 56
       elapsed = HW_REG(_DWT_CYCCNT) - start;
57 57
     } while (elapsed < count);
@@ -114,7 +114,7 @@
114 114
         serialprintPGM(unit);
115 115
         SERIAL_ECHOLNPAIR(" took: ", total);
116 116
         serialprintPGM(unit);
117
-        if (do_flush) SERIAL_FLUSH();
117
+        if (do_flush) SERIAL_FLUSHTX();
118 118
       };
119 119
 
120 120
       uint32_t s, e;

+ 0
- 1
Marlin/src/core/serial.h Ver fichero

@@ -58,7 +58,6 @@ extern uint8_t marlin_debug_flags;
58 58
 //
59 59
 // Serial redirection
60 60
 //
61
-typedef int8_t serial_index_t;
62 61
 #define SERIAL_ALL 0x7F
63 62
 #if HAS_MULTI_SERIAL
64 63
   #define _PORT_REDIRECT(n,p)   REMEMBER(n,multiSerial.portMask,p)

+ 1
- 1
Marlin/src/core/serial_base.h Ver fichero

@@ -79,7 +79,7 @@ struct SerialBase {
79 79
   void end()                        { static_cast<Child*>(this)->end(); }
80 80
   /** Check for available data from the port
81 81
       @param index  The port index, usually 0 */
82
-  bool available(uint8_t index = 0) { return static_cast<Child*>(this)->available(index); }
82
+  int available(uint8_t index = 0)  { return static_cast<Child*>(this)->available(index); }
83 83
   /** Read a value from the port
84 84
       @param index  The port index, usually 0 */
85 85
   int  read(uint8_t index = 0)      { return static_cast<Child*>(this)->read(index); }

+ 43
- 37
Marlin/src/core/serial_hook.h Ver fichero

@@ -24,6 +24,9 @@
24 24
 #include "macros.h"
25 25
 #include "serial_base.h"
26 26
 
27
+// Used in multiple places
28
+typedef int8_t serial_index_t;
29
+
27 30
 // The most basic serial class: it dispatch to the base serial class with no hook whatsoever. This will compile to nothing but the base serial class
28 31
 template <class SerialT>
29 32
 struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT {
@@ -35,10 +38,11 @@ struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT {
35 38
 
36 39
   void msgDone() {}
37 40
 
38
-  bool available(uint8_t index) { return index == 0 && SerialT::available(); }
39
-  int read(uint8_t index)       { return index == 0 ? SerialT::read() : -1; }
40
-  bool connected()              { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
41
-  void flushTX()                { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
41
+  // We don't care about indices here, since if one can call us, it's the right index anyway
42
+  int available(uint8_t)  { return (int)SerialT::available(); }
43
+  int read(uint8_t)       { return (int)SerialT::read(); }
44
+  bool connected()        { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; }
45
+  void flushTX()          { CALL_IF_EXISTS(void, static_cast<SerialT*>(this), flushTX); }
42 46
 
43 47
   // We have 2 implementation of the same method in both base class, let's say which one we want
44 48
   using SerialT::available;
@@ -65,18 +69,19 @@ struct ConditionalSerial : public SerialBase< ConditionalSerial<SerialT> > {
65 69
   bool    & condition;
66 70
   SerialT & out;
67 71
   NO_INLINE size_t write(uint8_t c) { if (condition) return out.write(c); return 0; }
68
-  void flush()            { if (condition) out.flush();  }
69
-  void begin(long br)     { out.begin(br); }
70
-  void end()              { out.end(); }
72
+  void flush()                      { if (condition) out.flush();  }
73
+  void begin(long br)               { out.begin(br); }
74
+  void end()                        { out.end(); }
71 75
 
72 76
   void msgDone() {}
73
-  bool connected()              { return CALL_IF_EXISTS(bool, &out, connected); }
74
-  void flushTX()                { CALL_IF_EXISTS(void, &out, flushTX); }
77
+  bool connected()          { return CALL_IF_EXISTS(bool, &out, connected); }
78
+  void flushTX()            { CALL_IF_EXISTS(void, &out, flushTX); }
79
+
80
+  int available(uint8_t )   { return (int)out.available(); }
81
+  int read(uint8_t )        { return (int)out.read(); }
82
+  int available()           { return (int)out.available(); }
83
+  int read()                { return (int)out.read(); }
75 84
 
76
-  bool available(uint8_t index) { return index == 0 && out.available(); }
77
-  int read(uint8_t index)       { return index == 0 ? out.read() : -1; }
78
-  using BaseClassT::available;
79
-  using BaseClassT::read;
80 85
 
81 86
   ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {}
82 87
 };
@@ -97,10 +102,10 @@ struct ForwardSerial : public SerialBase< ForwardSerial<SerialT> > {
97 102
   bool connected()              { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; }
98 103
   void flushTX()                { CALL_IF_EXISTS(void, &out, flushTX); }
99 104
 
100
-  bool available(uint8_t index) { return index == 0 && out.available(); }
101
-  int read(uint8_t index)       { return index == 0 ? out.read() : -1; }
102
-  bool available()              { return out.available(); }
103
-  int read()                    { return out.read(); }
105
+  int available(uint8_t)        { return (int)out.available(); }
106
+  int read(uint8_t)             { return (int)out.read(); }
107
+  int available()               { return (int)out.available(); }
108
+  int read()                    { return (int)out.read(); }
104 109
 
105 110
   ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {}
106 111
 };
@@ -125,8 +130,8 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public Seria
125 130
     if (eofHook) eofHook(userPointer);
126 131
   }
127 132
 
128
-  bool available(uint8_t index) { return index == 0 && SerialT::available(); }
129
-  int read(uint8_t index)       { return index == 0 ? SerialT::read() : -1; }
133
+  int available(uint8_t)  { return (int)SerialT::available(); }
134
+  int read(uint8_t)       { return (int)SerialT::read(); }
130 135
   using SerialT::available;
131 136
   using SerialT::read;
132 137
   using SerialT::flush;
@@ -157,21 +162,22 @@ struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public Seria
157 162
 
158 163
   // Forward constructor
159 164
   template <typename... Args>
160
-  RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...) {}
165
+  RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...), writeHook(0), eofHook(0), userPointer(0) {}
161 166
 };
162 167
 
163 168
 // A class that's duplicating its output conditionally to 2 serial interface
164
-template <class Serial0T, class Serial1T, const uint8_t offset = 0>
165
-struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset> > {
166
-  typedef SerialBase< MultiSerial<Serial0T, Serial1T, offset> > BaseClassT;
169
+template <class Serial0T, class Serial1T, const uint8_t offset = 0, const uint8_t step = 1>
170
+struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset, step> > {
171
+  typedef SerialBase< MultiSerial<Serial0T, Serial1T, offset, step> > BaseClassT;
167 172
 
168 173
   uint8_t    portMask;
169 174
   Serial0T & serial0;
170 175
   Serial1T & serial1;
171 176
 
172 177
   enum Masks {
173
-    FirstOutputMask   =  (1 << offset),
174
-    SecondOutputMask  =  (1 << (offset + 1)),
178
+    UsageMask         =  ((1 << step) - 1), // A bit mask containing as many bits as step
179
+    FirstOutputMask   =  (UsageMask << offset),
180
+    SecondOutputMask  =  (UsageMask << (offset + step)),
175 181
     AllMask           = FirstOutputMask | SecondOutputMask,
176 182
   };
177 183
 
@@ -185,19 +191,19 @@ struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset>
185 191
     if (portMask & FirstOutputMask)   serial0.msgDone();
186 192
     if (portMask & SecondOutputMask)  serial1.msgDone();
187 193
   }
188
-  bool available(uint8_t index) {
189
-    switch(index) {
190
-      case 0 + offset: return serial0.available();
191
-      case 1 + offset: return serial1.available();
192
-      default: return false;
193
-    }
194
+  int available(uint8_t index) {
195
+    if (index >= 0 + offset && index < step + offset)
196
+      return serial0.available(index);
197
+    else if (index >= step + offset && index < 2 * step + offset)
198
+      return serial1.available(index);
199
+    return false;
194 200
   }
195
-  NO_INLINE int read(uint8_t index) {
196
-    switch(index) {
197
-      case 0 + offset: return serial0.read();
198
-      case 1 + offset: return serial1.read();
199
-      default: return -1;
200
-    }
201
+  int read(uint8_t index) {
202
+    if (index >= 0 + offset && index < step + offset)
203
+      return serial0.read(index);
204
+    else if (index >= step + offset && index < 2 * step + offset)
205
+      return serial1.read(index);
206
+    return -1;
201 207
   }
202 208
   void begin(const long br) {
203 209
     if (portMask & FirstOutputMask)   serial0.begin(br);

+ 17
- 19
Marlin/src/gcode/queue.cpp Ver fichero

@@ -63,6 +63,10 @@ GCodeQueue queue;
63 63
 // Frequently used G-code strings
64 64
 PGMSTR(G28_STR, "G28");
65 65
 
66
+#if NO_TIMEOUTS > 0
67
+  static millis_t last_command_time = 0;
68
+#endif
69
+
66 70
 /**
67 71
  * GCode line number handling. Hosts may opt to include line numbers when
68 72
  * sending commands to Marlin, and lines will be checked for sequentiality.
@@ -288,6 +292,10 @@ void GCodeQueue::enqueue_now_P(PGM_P const pgcode) {
288 292
  *   B<int>  Block queue space remaining
289 293
  */
290 294
 void GCodeQueue::ok_to_send() {
295
+  #if NO_TIMEOUTS > 0
296
+    // Start counting from the last command's execution
297
+    last_command_time = millis();
298
+  #endif
291 299
   #if HAS_MULTI_SERIAL
292 300
     const serial_index_t serial_ind = command_port();
293 301
     if (serial_ind < 0) return;
@@ -324,29 +332,20 @@ void GCodeQueue::flush_and_request_resend() {
324 332
   ok_to_send();
325 333
 }
326 334
 
327
-inline bool serial_data_available() {
328
-  byte data_available = 0;
329
-  if (MYSERIAL0.available()) data_available++;
330
-  #ifdef SERIAL_PORT_2
331
-    const bool port2_open = TERN1(HAS_ETHERNET, ethernet.have_telnet_client);
332
-    if (port2_open && MYSERIAL1.available()) data_available++;
333
-  #endif
334
-  return data_available > 0;
335
-}
336 335
 
337
-inline int read_serial(const uint8_t index) {
338
-  switch (index) {
339
-    case 0: return MYSERIAL0.read();
340
-    case 1: {
341
-      #if HAS_MULTI_SERIAL
342
-        const bool port2_open = TERN1(HAS_ETHERNET, ethernet.have_telnet_client);
343
-        if (port2_open) return MYSERIAL1.read();
344
-      #endif
336
+// Multiserial already handle the dispatch to/from multiple port by itself
337
+inline bool serial_data_available(uint8_t index = SERIAL_ALL) {
338
+  if (index == SERIAL_ALL) {
339
+    for (index = 0; index < NUM_SERIAL; index++) {
340
+      if (SERIAL_IMPL.available(index) > 0) return true;
345 341
     }
346
-    default: return -1;
342
+    return false;
347 343
   }
344
+  return SERIAL_IMPL.available(index) > 0;
348 345
 }
349 346
 
347
+inline int read_serial(const uint8_t index) { return SERIAL_IMPL.read(index); }
348
+
350 349
 void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) {
351 350
   PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
352 351
   SERIAL_ERROR_START();
@@ -460,7 +459,6 @@ void GCodeQueue::get_serial_commands() {
460 459
   // If the command buffer is empty for too long,
461 460
   // send "wait" to indicate Marlin is still waiting.
462 461
   #if NO_TIMEOUTS > 0
463
-    static millis_t last_command_time = 0;
464 462
     const millis_t ms = millis();
465 463
     if (length == 0 && !serial_data_available() && ELAPSED(ms, last_command_time + NO_TIMEOUTS)) {
466 464
       SERIAL_ECHOLNPGM(STR_WAIT);

+ 14
- 3
docs/Serial.md Ver fichero

@@ -30,14 +30,25 @@ In the `Marlin/src/core/serial_hook.h` file, the different serial feature are de
30 30
 Since all the types above are using CRTP, it's possible to combine them to get the appropriate functionality.
31 31
 This is easily done via type definition of the feature.
32 32
 
33
-For example, to present a serial interface that's outputting to 2 serial port, the first one being hooked at runtime and the second one connected to a runtime switchable telnet client, you'll declare the type to use as:
33
+For example, to create a single serial interface with 2 serial outputs (one enabled at runtime and the other switchable):
34 34
 ```cpp
35 35
 typedef MultiSerial< RuntimeSerial<Serial>, ConditionalSerial<TelnetClient> > Serial0Type;
36 36
 ```
37 37
 
38
+To send the same output to 4 serial ports you could nest `MultiSerial` like this:
39
+```cpp
40
+typedef MultiSerial< MultiSerial< BaseSerial<Serial>, BaseSerial<Serial1> >, MultiSerial< BaseSerial<Serial2>, BaseSerial<Serial3>, 2, 1>, 0, 2> Serial0Type;
41
+```
42
+The magical numbers here are the step and offset for computing the serial port. Simplifying the above monster a bit:
43
+```cpp
44
+MS< A = MS<a, b, offset=0, step=1>, B=MS<c, d, offset=2, step=1>, offset=0, step=2>
45
+```
46
+This means that the underlying multiserial A (with output to `a,b`) is available from offset = 0 to offset + step = 1 (default value).
47
+The multiserial B (with output to `c,d`) is available from offset = 2 (the next step from the root multiserial) to offset + step = 3.
48
+In practice, the root multiserial will redirect any index/mask `offset` to `offset + step - 1` to its first leaf, and any index/mask `offset + step` to `offset + 2*step - 1` to its second leaf.
49
+
38 50
 ## Emergency parser
39
-By default, the serial base interface provide an emergency parser that's only enable for serial classes that support it.
40
-Because of this condition, all underlying type takes a first `bool emergencyParserEnabled` argument to their constructor. You must take into account this parameter when defining the actual type used.
51
+By default, the serial base interface provide an emergency parser that's only enable for serial classes that support it. Because of this condition, all underlying types take a first `bool emergencyParserEnabled` argument to their constructor. You must take into account this parameter when defining the actual type used.
41 52
 
42 53
 ## SERIAL macros
43 54
 The following macros are defined (in `serial.h`) to output data to the serial ports:

Loading…
Cancelar
Guardar