Refactor serial class with templates (#20783)
This commit is contained in:
		
							parent
							
								
									c929fb52dd
								
							
						
					
					
						commit
						3f01b222b2
					
				| @ -24,6 +24,13 @@ | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| #include "HAL.h" | ||||
| 
 | ||||
| #ifdef USBCON | ||||
|   DefaultSerial MSerial(false, Serial); | ||||
|   #ifdef BLUETOOTH | ||||
|     BTSerial btSerial(false, bluetoothSerial); | ||||
|   #endif | ||||
| #endif | ||||
| 
 | ||||
| // ------------------------
 | ||||
| // Public Variables
 | ||||
| // ------------------------
 | ||||
|  | ||||
| @ -82,7 +82,15 @@ typedef int8_t pin_t; | ||||
| 
 | ||||
| // Serial ports
 | ||||
| #ifdef USBCON | ||||
|   #define MYSERIAL0 TERN(BLUETOOTH, bluetoothSerial, Serial) | ||||
|   #include "../../core/serial_hook.h"  | ||||
|   typedef ForwardSerial0Type< decltype(Serial) > DefaultSerial; | ||||
|   extern DefaultSerial MSerial; | ||||
|   #ifdef BLUETOOTH | ||||
|     typedef ForwardSerial0Type< decltype(bluetoothSerial) > BTSerial; | ||||
|     extern BTSerial btSerial; | ||||
|   #endif | ||||
|    | ||||
|   #define MYSERIAL0 TERN(BLUETOOTH, btSerial, MSerial) | ||||
| #else | ||||
|   #if !WITHIN(SERIAL_PORT, -1, 3) | ||||
|     #error "SERIAL_PORT must be from -1 to 3. Please update your configuration." | ||||
|  | ||||
| @ -454,7 +454,7 @@ void MarlinSerial<Cfg>::flush() { | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
| size_t MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|   if (Cfg::TX_SIZE == 0) { | ||||
| 
 | ||||
|     _written = true; | ||||
| @ -480,7 +480,7 @@ void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|       // location". This makes sure flush() won't return until the bytes
 | ||||
|       // actually got written
 | ||||
|       B_TXC = 1; | ||||
|       return; | ||||
|       return 1; | ||||
|     } | ||||
| 
 | ||||
|     const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1); | ||||
| @ -510,6 +510,7 @@ void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|     // Enable TX ISR - Non atomic, but it will eventually enable TX ISR
 | ||||
|     B_UDRIE = 1; | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| @ -556,161 +557,6 @@ void MarlinSerial<Cfg>::flushTX() { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Imports from print.h | ||||
|  */ | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(char c, int base) { | ||||
|   print((long)c, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned char b, int base) { | ||||
|   print((unsigned long)b, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(int n, int base) { | ||||
|   print((long)n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned int n, int base) { | ||||
|   print((unsigned long)n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(long n, int base) { | ||||
|   if (base == 0) write(n); | ||||
|   else if (base == 10) { | ||||
|     if (n < 0) { print('-'); n = -n; } | ||||
|     printNumber(n, 10); | ||||
|   } | ||||
|   else | ||||
|     printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned long n, int base) { | ||||
|   if (base == 0) write(n); | ||||
|   else printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(double n, int digits) { | ||||
|   printFloat(n, digits); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println() { | ||||
|   print('\r'); | ||||
|   print('\n'); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(const String& s) { | ||||
|   print(s); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(const char c[]) { | ||||
|   print(c); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(char c, int base) { | ||||
|   print(c, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned char b, int base) { | ||||
|   print(b, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(double n, int digits) { | ||||
|   print(n, digits); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| // Private Methods
 | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::printNumber(unsigned long n, uint8_t base) { | ||||
|   if (n) { | ||||
|     unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
 | ||||
|     int8_t i = 0; | ||||
|     while (n) { | ||||
|       buf[i++] = n % base; | ||||
|       n /= base; | ||||
|     } | ||||
|     while (i--) | ||||
|       print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10))); | ||||
|   } | ||||
|   else | ||||
|     print('0'); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::printFloat(double number, uint8_t digits) { | ||||
|   // Handle negative numbers
 | ||||
|   if (number < 0.0) { | ||||
|     print('-'); | ||||
|     number = -number; | ||||
|   } | ||||
| 
 | ||||
|   // Round correctly so that print(1.999, 2) prints as "2.00"
 | ||||
|   double rounding = 0.5; | ||||
|   LOOP_L_N(i, digits) rounding *= 0.1; | ||||
|   number += rounding; | ||||
| 
 | ||||
|   // Extract the integer part of the number and print it
 | ||||
|   unsigned long int_part = (unsigned long)number; | ||||
|   double remainder = number - (double)int_part; | ||||
|   print(int_part); | ||||
| 
 | ||||
|   // Print the decimal point, but only if there are digits beyond
 | ||||
|   if (digits) { | ||||
|     print('.'); | ||||
|     // Extract digits from the remainder one at a time
 | ||||
|     while (digits--) { | ||||
|       remainder *= 10.0; | ||||
|       int toPrint = int(remainder); | ||||
|       print(toPrint); | ||||
|       remainder -= toPrint; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // Hookup ISR handlers
 | ||||
| ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _RX_vect)) { | ||||
|   MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::store_rxd_char(); | ||||
| @ -720,11 +566,9 @@ ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _UDRE_vect)) { | ||||
|   MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::_tx_udr_empty_irq(); | ||||
| } | ||||
| 
 | ||||
| // Preinstantiate
 | ||||
| template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>; | ||||
| 
 | ||||
| // Instantiate
 | ||||
| MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
| // Because of the template definition above, it's required to instantiate the template to have all method generated
 | ||||
| template class MarlinSerial< MarlinSerialCfg<SERIAL_PORT> >; | ||||
| MSerialT customizedSerial1(MSerialT::HasEmergencyParser); | ||||
| 
 | ||||
| #ifdef SERIAL_PORT_2 | ||||
| 
 | ||||
| @ -737,12 +581,8 @@ MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
|     MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>::_tx_udr_empty_irq(); | ||||
|   } | ||||
| 
 | ||||
|   // Preinstantiate
 | ||||
|   template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>; | ||||
| 
 | ||||
|   // Instantiate
 | ||||
|   MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2; | ||||
| 
 | ||||
|   template class MarlinSerial< MarlinSerialCfg<SERIAL_PORT_2> >; | ||||
|   MSerialT2 customizedSerial2(MSerialT2::HasEmergencyParser); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef MMU2_SERIAL_PORT | ||||
| @ -755,12 +595,8 @@ MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
|     MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>>::_tx_udr_empty_irq(); | ||||
|   } | ||||
| 
 | ||||
|   // Preinstantiate
 | ||||
|   template class MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>>; | ||||
| 
 | ||||
|   // Instantiate
 | ||||
|   MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>> mmuSerial; | ||||
| 
 | ||||
|   template class MarlinSerial< MarlinSerialCfg<MMU2_SERIAL_PORT> >; | ||||
|   MSerialT3 mmuSerial(MSerialT3::HasEmergencyParser); | ||||
| #endif | ||||
| 
 | ||||
| #ifdef LCD_SERIAL_PORT | ||||
| @ -773,11 +609,8 @@ MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
|     MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>>::_tx_udr_empty_irq(); | ||||
|   } | ||||
| 
 | ||||
|   // Preinstantiate
 | ||||
|   template class MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>>; | ||||
| 
 | ||||
|   // Instantiate
 | ||||
|   MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>> lcdSerial; | ||||
|   template class MarlinSerial< LCDSerialCfg<LCD_SERIAL_PORT> >; | ||||
|   MSerialT4 lcdSerial(MSerialT4::HasEmergencyParser); | ||||
|   | ||||
|   #if HAS_DGUS_LCD | ||||
|     template<typename Cfg> | ||||
| @ -796,7 +629,7 @@ MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
| 
 | ||||
| // For AT90USB targets use the UART for BT interfacing
 | ||||
| #if defined(USBCON) && ENABLED(BLUETOOTH) | ||||
|   HardwareSerial bluetoothSerial; | ||||
|   MSerialT5 bluetoothSerial(false); | ||||
| #endif | ||||
| 
 | ||||
| #endif // __AVR__
 | ||||
|  | ||||
| @ -34,6 +34,7 @@ | ||||
| #include <WString.h> | ||||
| 
 | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| #ifndef SERIAL_PORT | ||||
|   #define SERIAL_PORT 0 | ||||
| @ -135,10 +136,6 @@ | ||||
|     UART_DECL(3); | ||||
|   #endif | ||||
| 
 | ||||
|   #define DEC 10 | ||||
|   #define HEX 16 | ||||
|   #define OCT 8 | ||||
|   #define BIN 2 | ||||
|   #define BYTE 0 | ||||
| 
 | ||||
|   // Templated type selector
 | ||||
| @ -202,60 +199,30 @@ | ||||
|     static FORCE_INLINE void atomic_set_rx_tail(ring_buffer_pos_t value); | ||||
|     static FORCE_INLINE ring_buffer_pos_t atomic_read_rx_tail(); | ||||
| 
 | ||||
|     public: | ||||
| 
 | ||||
|   public: | ||||
|     FORCE_INLINE static void store_rxd_char(); | ||||
|     FORCE_INLINE static void _tx_udr_empty_irq(); | ||||
| 
 | ||||
|     public: | ||||
|       MarlinSerial() {}; | ||||
|       static void begin(const long); | ||||
|       static void end(); | ||||
|       static int peek(); | ||||
|       static int read(); | ||||
|       static void flush(); | ||||
|       static ring_buffer_pos_t available(); | ||||
|       static void write(const uint8_t c); | ||||
|       static void flushTX(); | ||||
|       #if HAS_DGUS_LCD | ||||
|         static ring_buffer_pos_t get_tx_buffer_free(); | ||||
|       #endif | ||||
|   public: | ||||
|     static void begin(const long); | ||||
|     static void end(); | ||||
|     static int peek(); | ||||
|     static int read(); | ||||
|     static void flush(); | ||||
|     static ring_buffer_pos_t available(); | ||||
|     static size_t write(const uint8_t c); | ||||
|     static void flushTX(); | ||||
|     #if HAS_DGUS_LCD | ||||
|       static ring_buffer_pos_t get_tx_buffer_free(); | ||||
|     #endif | ||||
| 
 | ||||
|       static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } | ||||
|     enum { HasEmergencyParser = Cfg::EMERGENCYPARSER }; | ||||
|     static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } | ||||
| 
 | ||||
|       FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; } | ||||
|       FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; } | ||||
|       FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; } | ||||
|       FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; } | ||||
| 
 | ||||
|       FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); } | ||||
|       FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } | ||||
|       FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } | ||||
|       FORCE_INLINE static void print(const char* str) { write(str); } | ||||
| 
 | ||||
|       static void print(char, int = BYTE); | ||||
|       static void print(unsigned char, int = BYTE); | ||||
|       static void print(int, int = DEC); | ||||
|       static void print(unsigned int, int = DEC); | ||||
|       static void print(long, int = DEC); | ||||
|       static void print(unsigned long, int = DEC); | ||||
|       static void print(double, int = 2); | ||||
| 
 | ||||
|       static void println(const String& s); | ||||
|       static void println(const char[]); | ||||
|       static void println(char, int = BYTE); | ||||
|       static void println(unsigned char, int = BYTE); | ||||
|       static void println(int, int = DEC); | ||||
|       static void println(unsigned int, int = DEC); | ||||
|       static void println(long, int = DEC); | ||||
|       static void println(unsigned long, int = DEC); | ||||
|       static void println(double, int = 2); | ||||
|       static void println(); | ||||
|       operator bool() { return true; } | ||||
| 
 | ||||
|     private: | ||||
|       static void printNumber(unsigned long, const uint8_t); | ||||
|       static void printFloat(double, uint8_t); | ||||
|     FORCE_INLINE static uint8_t dropped() { return Cfg::DROPPED_RX ? rx_dropped_bytes : 0; } | ||||
|     FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; } | ||||
|     FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; } | ||||
|     FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; } | ||||
|   }; | ||||
| 
 | ||||
|   template <uint8_t serial> | ||||
| @ -270,12 +237,13 @@ | ||||
|     static constexpr bool RX_FRAMING_ERRORS = ENABLED(SERIAL_STATS_RX_FRAMING_ERRORS); | ||||
|     static constexpr bool MAX_RX_QUEUED     = ENABLED(SERIAL_STATS_MAX_RX_QUEUED); | ||||
|   }; | ||||
|   extern MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
| 
 | ||||
|   typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT> > > MSerialT;  | ||||
|   extern MSerialT customizedSerial1; | ||||
| 
 | ||||
|   #ifdef SERIAL_PORT_2 | ||||
| 
 | ||||
|     extern MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2; | ||||
| 
 | ||||
|     typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT_2> > > MSerialT2;  | ||||
|     extern MSerialT2 customizedSerial2; | ||||
|   #endif | ||||
| 
 | ||||
| #endif // !USBCON
 | ||||
| @ -294,7 +262,8 @@ | ||||
|     static constexpr bool RX_OVERRUNS       = false; | ||||
|   }; | ||||
| 
 | ||||
|   extern MarlinSerial<MMU2SerialCfg<MMU2_SERIAL_PORT>> mmuSerial; | ||||
|   typedef Serial0Type< MarlinSerial< MMU2SerialCfg<MMU2_SERIAL_PORT> > > MSerialT3;  | ||||
|   extern MSerial3 mmuSerial; | ||||
| #endif | ||||
| 
 | ||||
| #ifdef LCD_SERIAL_PORT | ||||
| @ -322,11 +291,13 @@ | ||||
|     #endif | ||||
|   }; | ||||
| 
 | ||||
|   extern MarlinSerial<LCDSerialCfg<LCD_SERIAL_PORT>> lcdSerial; | ||||
| 
 | ||||
|   typedef Serial0Type< MarlinSerial< LCDSerialCfg<LCD_SERIAL_PORT> > > MSerialT4;  | ||||
|   extern MSerialT4 lcdSerial; | ||||
| #endif | ||||
| 
 | ||||
| // Use the UART for Bluetooth in AT90USB configurations
 | ||||
| #if defined(USBCON) && ENABLED(BLUETOOTH) | ||||
|   extern HardwareSerial bluetoothSerial; | ||||
|   typedef Serial0Type<HardwareSerial> MSerialT5; | ||||
|   extern MSerialT5 bluetoothSerial; | ||||
| #endif | ||||
|  | ||||
| @ -102,4 +102,11 @@ uint16_t HAL_adc_get_result() { | ||||
|   return HAL_adc_result; | ||||
| } | ||||
| 
 | ||||
| // Forward the default serial port
 | ||||
| DefaultSerial MSerial(false, Serial); | ||||
| 
 | ||||
| DefaultSerial1 MSerial1(false, Serial1); | ||||
| DefaultSerial2 MSerial2(false, Serial2); | ||||
| DefaultSerial3 MSerial3(false, Serial3); | ||||
| 
 | ||||
| #endif // ARDUINO_ARCH_SAM
 | ||||
|  | ||||
| @ -36,9 +36,20 @@ | ||||
| 
 | ||||
| #include <stdint.h> | ||||
| 
 | ||||
| #define _MSERIAL(X) Serial##X | ||||
| #include "../../core/serial_hook.h" | ||||
| typedef ForwardSerial0Type< decltype(Serial) > DefaultSerial; | ||||
| extern DefaultSerial MSerial; | ||||
| 
 | ||||
| typedef ForwardSerial0Type< decltype(Serial1) > DefaultSerial1; | ||||
| typedef ForwardSerial0Type< decltype(Serial2) > DefaultSerial2; | ||||
| typedef ForwardSerial0Type< decltype(Serial3) > DefaultSerial3; | ||||
| extern DefaultSerial1 MSerial1; | ||||
| extern DefaultSerial2 MSerial2; | ||||
| extern DefaultSerial3 MSerial3; | ||||
| 
 | ||||
| #define _MSERIAL(X) MSerial##X | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| #define Serial0 Serial | ||||
| #define MSerial0 MSerial | ||||
| 
 | ||||
| // Define MYSERIAL0/1 before MarlinSerial includes!
 | ||||
| #if SERIAL_PORT == -1 || ENABLED(EMERGENCY_PARSER) | ||||
|  | ||||
| @ -382,7 +382,7 @@ void MarlinSerial<Cfg>::flush() { | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
| size_t MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|   _written = true; | ||||
| 
 | ||||
|   if (Cfg::TX_SIZE == 0) { | ||||
| @ -400,7 +400,7 @@ void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|     // XOFF char at the RX isr, but it is properly handled there
 | ||||
|     if (!(HWUART->UART_IMR & UART_IMR_TXRDY) && (HWUART->UART_SR & UART_SR_TXRDY)) { | ||||
|       HWUART->UART_THR = c; | ||||
|       return; | ||||
|       return 1; | ||||
|     } | ||||
| 
 | ||||
|     const uint8_t i = (tx_buffer.head + 1) & (Cfg::TX_SIZE - 1); | ||||
| @ -428,6 +428,7 @@ void MarlinSerial<Cfg>::write(const uint8_t c) { | ||||
|     // Enable TX isr - Non atomic, but it will eventually enable TX isr
 | ||||
|     HWUART->UART_IER = UART_IER_TXRDY; | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| @ -473,169 +474,16 @@ void MarlinSerial<Cfg>::flushTX() { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Imports from print.h | ||||
|  */ | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(char c, int base) { | ||||
|   print((long)c, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned char b, int base) { | ||||
|   print((unsigned long)b, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(int n, int base) { | ||||
|   print((long)n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned int n, int base) { | ||||
|   print((unsigned long)n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(long n, int base) { | ||||
|   if (base == 0) write(n); | ||||
|   else if (base == 10) { | ||||
|     if (n < 0) { print('-'); n = -n; } | ||||
|     printNumber(n, 10); | ||||
|   } | ||||
|   else | ||||
|     printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(unsigned long n, int base) { | ||||
|   if (base == 0) write(n); | ||||
|   else printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::print(double n, int digits) { | ||||
|   printFloat(n, digits); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println() { | ||||
|   print('\r'); | ||||
|   print('\n'); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(const String& s) { | ||||
|   print(s); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(const char c[]) { | ||||
|   print(c); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(char c, int base) { | ||||
|   print(c, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned char b, int base) { | ||||
|   print(b, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(unsigned long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::println(double n, int digits) { | ||||
|   print(n, digits); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| // Private Methods
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::printNumber(unsigned long n, uint8_t base) { | ||||
|   if (n) { | ||||
|     unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
 | ||||
|     int8_t i = 0; | ||||
|     while (n) { | ||||
|       buf[i++] = n % base; | ||||
|       n /= base; | ||||
|     } | ||||
|     while (i--) | ||||
|       print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10))); | ||||
|   } | ||||
|   else | ||||
|     print('0'); | ||||
| } | ||||
| 
 | ||||
| template<typename Cfg> | ||||
| void MarlinSerial<Cfg>::printFloat(double number, uint8_t digits) { | ||||
|   // Handle negative numbers
 | ||||
|   if (number < 0.0) { | ||||
|     print('-'); | ||||
|     number = -number; | ||||
|   } | ||||
| 
 | ||||
|   // Round correctly so that print(1.999, 2) prints as "2.00"
 | ||||
|   double rounding = 0.5; | ||||
|   LOOP_L_N(i, digits) rounding *= 0.1; | ||||
|   number += rounding; | ||||
| 
 | ||||
|   // Extract the integer part of the number and print it
 | ||||
|   unsigned long int_part = (unsigned long)number; | ||||
|   double remainder = number - (double)int_part; | ||||
|   print(int_part); | ||||
| 
 | ||||
|   // Print the decimal point, but only if there are digits beyond
 | ||||
|   if (digits) { | ||||
|     print('.'); | ||||
|     // Extract digits from the remainder one at a time
 | ||||
|     while (digits--) { | ||||
|       remainder *= 10.0; | ||||
|       int toPrint = int(remainder); | ||||
|       print(toPrint); | ||||
|       remainder -= toPrint; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| // If not using the USB port as serial port
 | ||||
| #if SERIAL_PORT >= 0 | ||||
|   template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>;      // Define
 | ||||
|   MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1;   // Instantiate
 | ||||
|   template class MarlinSerial< MarlinSerialCfg<SERIAL_PORT> >; | ||||
|   MSerialT customizedSerial1(MarlinSerialCfg<SERIAL_PORT>::EMERGENCYPARSER); | ||||
| #endif | ||||
| 
 | ||||
| #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0 | ||||
|   template class MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>>;    // Define
 | ||||
|   MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2; // Instantiate
 | ||||
|   template class MarlinSerial< MarlinSerialCfg<SERIAL_PORT_2> >; | ||||
|   MSerialT2 customizedSerial2(MarlinSerialCfg<SERIAL_PORT_2>::EMERGENCYPARSER); | ||||
| #endif | ||||
| 
 | ||||
| #endif // ARDUINO_ARCH_SAM
 | ||||
|  | ||||
| @ -30,11 +30,7 @@ | ||||
| #include <WString.h> | ||||
| 
 | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
| 
 | ||||
| #define DEC 10 | ||||
| #define HEX 16 | ||||
| #define OCT 8 | ||||
| #define BIN 2 | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| // Define constants and variables for buffering incoming serial data.  We're
 | ||||
| // using a ring buffer (I think), in which rx_buffer_head is the index of the
 | ||||
| @ -119,7 +115,7 @@ public: | ||||
|   static int read(); | ||||
|   static void flush(); | ||||
|   static ring_buffer_pos_t available(); | ||||
|   static void write(const uint8_t c); | ||||
|   static size_t write(const uint8_t c); | ||||
|   static void flushTX(); | ||||
| 
 | ||||
|   static inline bool emergency_parser_enabled() { return Cfg::EMERGENCYPARSER; } | ||||
| @ -128,35 +124,6 @@ public: | ||||
|   FORCE_INLINE static uint8_t buffer_overruns() { return Cfg::RX_OVERRUNS ? rx_buffer_overruns : 0; } | ||||
|   FORCE_INLINE static uint8_t framing_errors() { return Cfg::RX_FRAMING_ERRORS ? rx_framing_errors : 0; } | ||||
|   FORCE_INLINE static ring_buffer_pos_t rxMaxEnqueued() { return Cfg::MAX_RX_QUEUED ? rx_max_enqueued : 0; } | ||||
| 
 | ||||
|   FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); } | ||||
|   FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } | ||||
|   FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } | ||||
|   FORCE_INLINE static void print(const char* str) { write(str); } | ||||
| 
 | ||||
|   static void print(char, int = 0); | ||||
|   static void print(unsigned char, int = 0); | ||||
|   static void print(int, int = DEC); | ||||
|   static void print(unsigned int, int = DEC); | ||||
|   static void print(long, int = DEC); | ||||
|   static void print(unsigned long, int = DEC); | ||||
|   static void print(double, int = 2); | ||||
| 
 | ||||
|   static void println(const String& s); | ||||
|   static void println(const char[]); | ||||
|   static void println(char, int = 0); | ||||
|   static void println(unsigned char, int = 0); | ||||
|   static void println(int, int = DEC); | ||||
|   static void println(unsigned int, int = DEC); | ||||
|   static void println(long, int = DEC); | ||||
|   static void println(unsigned long, int = DEC); | ||||
|   static void println(double, int = 2); | ||||
|   static void println(); | ||||
|   operator bool() { return true; } | ||||
| 
 | ||||
| private: | ||||
|   static void printNumber(unsigned long, const uint8_t); | ||||
|   static void printFloat(double, uint8_t); | ||||
| }; | ||||
| 
 | ||||
| // Serial port configuration
 | ||||
| @ -174,9 +141,11 @@ struct MarlinSerialCfg { | ||||
| }; | ||||
| 
 | ||||
| #if SERIAL_PORT >= 0 | ||||
|   extern MarlinSerial<MarlinSerialCfg<SERIAL_PORT>> customizedSerial1; | ||||
|   typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT> > > MSerialT; | ||||
|   extern MSerialT customizedSerial1; | ||||
| #endif | ||||
| 
 | ||||
| #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0 | ||||
|   extern MarlinSerial<MarlinSerialCfg<SERIAL_PORT_2>> customizedSerial2; | ||||
|   typedef Serial0Type< MarlinSerial< MarlinSerialCfg<SERIAL_PORT_2> > > MSerialT2; | ||||
|   extern MSerialT2 customizedSerial2; | ||||
| #endif | ||||
|  | ||||
| @ -50,10 +50,6 @@ extern "C" { | ||||
| // Pending character
 | ||||
| static int pending_char = -1; | ||||
| 
 | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   static EmergencyParser::State emergency_state; // = EP_RESET
 | ||||
| #endif | ||||
| 
 | ||||
| // Public Methods
 | ||||
| void MarlinSerialUSB::begin(const long) {} | ||||
| 
 | ||||
| @ -111,13 +107,13 @@ bool MarlinSerialUSB::available() { | ||||
| void MarlinSerialUSB::flush() { } | ||||
| void MarlinSerialUSB::flushTX() { } | ||||
| 
 | ||||
| void MarlinSerialUSB::write(const uint8_t c) { | ||||
| size_t MarlinSerialUSB::write(const uint8_t c) { | ||||
| 
 | ||||
|   /* Do not even bother sending anything if USB CDC is not enumerated
 | ||||
|      or not configured on the PC side or there is no program on the PC | ||||
|      listening to our messages */ | ||||
|   if (!usb_task_cdc_isenabled() || !usb_task_cdc_dtr_active()) | ||||
|     return; | ||||
|     return 0; | ||||
| 
 | ||||
|   /* Wait until the PC has read the pending to be sent data */ | ||||
|   while (usb_task_cdc_isenabled() && | ||||
| @ -129,161 +125,20 @@ void MarlinSerialUSB::write(const uint8_t c) { | ||||
|      or not configured on the PC side or there is no program on the PC | ||||
|      listening to our messages at this point */ | ||||
|   if (!usb_task_cdc_isenabled() || !usb_task_cdc_dtr_active()) | ||||
|     return; | ||||
|     return 0; | ||||
| 
 | ||||
|   // Fifo full
 | ||||
|   //  udi_cdc_signal_overrun();
 | ||||
|   udi_cdc_putc(c); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * Imports from print.h | ||||
|  */ | ||||
| 
 | ||||
| void MarlinSerialUSB::print(char c, int base) { | ||||
|   print((long)c, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(unsigned char b, int base) { | ||||
|   print((unsigned long)b, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(int n, int base) { | ||||
|   print((long)n, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(unsigned int n, int base) { | ||||
|   print((unsigned long)n, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(long n, int base) { | ||||
|   if (base == 0) | ||||
|     write(n); | ||||
|   else if (base == 10) { | ||||
|     if (n < 0) { | ||||
|       print('-'); | ||||
|       n = -n; | ||||
|     } | ||||
|     printNumber(n, 10); | ||||
|   } | ||||
|   else | ||||
|     printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(unsigned long n, int base) { | ||||
|   if (base == 0) write(n); | ||||
|   else printNumber(n, base); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::print(double n, int digits) { | ||||
|   printFloat(n, digits); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println() { | ||||
|   print('\r'); | ||||
|   print('\n'); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(const String& s) { | ||||
|   print(s); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(const char c[]) { | ||||
|   print(c); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(char c, int base) { | ||||
|   print(c, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(unsigned char b, int base) { | ||||
|   print(b, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(unsigned int n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(unsigned long n, int base) { | ||||
|   print(n, base); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::println(double n, int digits) { | ||||
|   print(n, digits); | ||||
|   println(); | ||||
| } | ||||
| 
 | ||||
| // Private Methods
 | ||||
| 
 | ||||
| void MarlinSerialUSB::printNumber(unsigned long n, uint8_t base) { | ||||
|   if (n) { | ||||
|     unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
 | ||||
|     int8_t i = 0; | ||||
|     while (n) { | ||||
|       buf[i++] = n % base; | ||||
|       n /= base; | ||||
|     } | ||||
|     while (i--) | ||||
|       print((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10))); | ||||
|   } | ||||
|   else | ||||
|     print('0'); | ||||
| } | ||||
| 
 | ||||
| void MarlinSerialUSB::printFloat(double number, uint8_t digits) { | ||||
|   // Handle negative numbers
 | ||||
|   if (number < 0.0) { | ||||
|     print('-'); | ||||
|     number = -number; | ||||
|   } | ||||
| 
 | ||||
|   // Round correctly so that print(1.999, 2) prints as "2.00"
 | ||||
|   double rounding = 0.5; | ||||
|   LOOP_L_N(i, digits) | ||||
|     rounding *= 0.1; | ||||
| 
 | ||||
|   number += rounding; | ||||
| 
 | ||||
|   // Extract the integer part of the number and print it
 | ||||
|   unsigned long int_part = (unsigned long)number; | ||||
|   double remainder = number - (double)int_part; | ||||
|   print(int_part); | ||||
| 
 | ||||
|   // Print the decimal point, but only if there are digits beyond
 | ||||
|   if (digits) { | ||||
|     print('.'); | ||||
|     // Extract digits from the remainder one at a time
 | ||||
|     while (digits--) { | ||||
|       remainder *= 10.0; | ||||
|       int toPrint = int(remainder); | ||||
|       print(toPrint); | ||||
|       remainder -= toPrint; | ||||
|     } | ||||
|   } | ||||
|   return 1; | ||||
| } | ||||
| 
 | ||||
| // Preinstantiate
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   MarlinSerialUSB customizedSerial1; | ||||
|   MSerialT customizedSerial1(TERN0(EMERGENCY_PARSER, true)); | ||||
| #endif | ||||
| #if SERIAL_PORT_2 == -1 | ||||
|   MarlinSerialUSB customizedSerial2; | ||||
|   MSerialT customizedSerial2(TERN0(EMERGENCY_PARSER, true)); | ||||
| #endif | ||||
| 
 | ||||
| #endif // HAS_USB_SERIAL
 | ||||
|  | ||||
| @ -27,20 +27,13 @@ | ||||
|  */ | ||||
| 
 | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| 
 | ||||
| #if HAS_USB_SERIAL | ||||
| 
 | ||||
| #include <WString.h> | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| #define DEC 10 | ||||
| #define HEX 16 | ||||
| #define OCT 8 | ||||
| #define BIN 2 | ||||
| 
 | ||||
| class MarlinSerialUSB { | ||||
| 
 | ||||
| public: | ||||
|   MarlinSerialUSB() {}; | ||||
| struct MarlinSerialUSB { | ||||
|   static void begin(const long); | ||||
|   static void end(); | ||||
|   static int peek(); | ||||
| @ -48,7 +41,7 @@ public: | ||||
|   static void flush(); | ||||
|   static void flushTX(); | ||||
|   static bool available(); | ||||
|   static void write(const uint8_t c); | ||||
|   static size_t write(const uint8_t c); | ||||
| 
 | ||||
|   #if ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|     FORCE_INLINE static uint32_t dropped() { return 0; } | ||||
| @ -57,43 +50,15 @@ public: | ||||
|   #if ENABLED(SERIAL_STATS_MAX_RX_QUEUED) | ||||
|     FORCE_INLINE static int rxMaxEnqueued() { return 0; } | ||||
|   #endif | ||||
| 
 | ||||
|   FORCE_INLINE static void write(const char* str) { while (*str) write(*str++); } | ||||
|   FORCE_INLINE static void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } | ||||
|   FORCE_INLINE static void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } | ||||
|   FORCE_INLINE static void print(const char* str) { write(str); } | ||||
| 
 | ||||
|   static void print(char, int = 0); | ||||
|   static void print(unsigned char, int = 0); | ||||
|   static void print(int, int = DEC); | ||||
|   static void print(unsigned int, int = DEC); | ||||
|   static void print(long, int = DEC); | ||||
|   static void print(unsigned long, int = DEC); | ||||
|   static void print(double, int = 2); | ||||
| 
 | ||||
|   static void println(const String& s); | ||||
|   static void println(const char[]); | ||||
|   static void println(char, int = 0); | ||||
|   static void println(unsigned char, int = 0); | ||||
|   static void println(int, int = DEC); | ||||
|   static void println(unsigned int, int = DEC); | ||||
|   static void println(long, int = DEC); | ||||
|   static void println(unsigned long, int = DEC); | ||||
|   static void println(double, int = 2); | ||||
|   static void println(); | ||||
|   operator bool() { return true; } | ||||
| 
 | ||||
| private: | ||||
|   static void printNumber(unsigned long, const uint8_t); | ||||
|   static void printFloat(double, uint8_t); | ||||
| }; | ||||
| typedef Serial0Type<MarlinSerialUSB> MSerialT; | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   extern MarlinSerialUSB customizedSerial1; | ||||
|   extern MSerialT customizedSerial1; | ||||
| #endif | ||||
| 
 | ||||
| #if SERIAL_PORT_2 == -1 | ||||
|   extern MarlinSerialUSB customizedSerial2; | ||||
|   extern MSerialT customizedSerial2; | ||||
| #endif | ||||
| 
 | ||||
| #endif // HAS_USB_SERIAL
 | ||||
|  | ||||
| @ -68,7 +68,7 @@ Ctrl_status sd_mmc_spi_usb_read_10(uint32_t addr, uint16_t nb_sector) { | ||||
|   { | ||||
|     char buffer[80]; | ||||
|     sprintf_P(buffer, PSTR("SDRD: %d @ 0x%08x\n"), nb_sector, addr); | ||||
|     PORT_REDIRECT(0); | ||||
|     PORT_REDIRECT(SERIAL_PORTMASK(0)); | ||||
|     SERIAL_ECHO(buffer); | ||||
|   } | ||||
|   #endif | ||||
| @ -108,7 +108,7 @@ Ctrl_status sd_mmc_spi_usb_write_10(uint32_t addr, uint16_t nb_sector) { | ||||
|   { | ||||
|     char buffer[80]; | ||||
|     sprintf_P(buffer, PSTR("SDWR: %d @ 0x%08x\n"), nb_sector, addr); | ||||
|     PORT_REDIRECT(0); | ||||
|     PORT_REDIRECT(SERIAL_PORTMASK(0)); | ||||
|     SERIAL_ECHO(buffer); | ||||
|   } | ||||
|   #endif | ||||
|  | ||||
| @ -24,10 +24,7 @@ | ||||
| 
 | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
| 
 | ||||
| FlushableHardwareSerial::FlushableHardwareSerial(int uart_nr) | ||||
|     : HardwareSerial(uart_nr) | ||||
| {} | ||||
| 
 | ||||
| FlushableHardwareSerial flushableSerial(0); | ||||
| Serial0Type<FlushableHardwareSerial> flushableSerial(false, 0); | ||||
| 
 | ||||
| #endif // ARDUINO_ARCH_ESP32
 | ||||
|  | ||||
| @ -24,14 +24,13 @@ | ||||
| #ifdef ARDUINO_ARCH_ESP32 | ||||
| 
 | ||||
| #include <HardwareSerial.h> | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| class FlushableHardwareSerial : public HardwareSerial { | ||||
| public: | ||||
|   FlushableHardwareSerial(int uart_nr); | ||||
| 
 | ||||
|   inline void flushTX() { /* No need to flush the hardware serial, but defined here for compatibility. */ } | ||||
|   FlushableHardwareSerial(int uart_nr) : HardwareSerial(uart_nr) {} | ||||
| }; | ||||
| 
 | ||||
| extern FlushableHardwareSerial flushableSerial; | ||||
| extern Serial0Type<FlushableHardwareSerial> flushableSerial; | ||||
| 
 | ||||
| #endif // ARDUINO_ARCH_ESP32
 | ||||
|  | ||||
| @ -40,6 +40,10 @@ | ||||
|   #endif | ||||
| #endif | ||||
| 
 | ||||
| #if ENABLED(ESP3D_WIFISUPPORT) | ||||
|   DefaultSerial MSerial(false, Serial2Socket); | ||||
| #endif | ||||
| 
 | ||||
| // ------------------------
 | ||||
| // Externs
 | ||||
| // ------------------------
 | ||||
|  | ||||
| @ -55,7 +55,9 @@ extern portMUX_TYPE spinlock; | ||||
| 
 | ||||
| #if EITHER(WIFISUPPORT, ESP3D_WIFISUPPORT) | ||||
|   #if ENABLED(ESP3D_WIFISUPPORT) | ||||
|     #define MYSERIAL1 Serial2Socket | ||||
|     typedef ForwardSerial0Type< decltype(Serial2Socket) > DefaultSerial; | ||||
|     extern DefaultSerial MSerial; | ||||
|     #define MYSERIAL1 MSerial | ||||
|   #else | ||||
|     #define MYSERIAL1 webSocketSerial | ||||
|   #endif | ||||
|  | ||||
| @ -29,7 +29,7 @@ | ||||
| #include "wifi.h" | ||||
| #include <ESPAsyncWebServer.h> | ||||
| 
 | ||||
| WebSocketSerial webSocketSerial; | ||||
| MSerialT webSocketSerial(false); | ||||
| AsyncWebSocket ws("/ws"); // TODO Move inside the class.
 | ||||
| 
 | ||||
| // RingBuffer impl
 | ||||
| @ -144,9 +144,5 @@ size_t WebSocketSerial::write(const uint8_t* buffer, size_t size) { | ||||
|   return written; | ||||
| } | ||||
| 
 | ||||
| void WebSocketSerial::flushTX() { | ||||
|   // No need to do anything as there's no benefit to sending partial lines over the websocket connection.
 | ||||
| } | ||||
| 
 | ||||
| #endif // WIFISUPPORT
 | ||||
| #endif // ARDUINO_ARCH_ESP32
 | ||||
|  | ||||
| @ -22,6 +22,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| #include <Stream.h> | ||||
| 
 | ||||
| @ -68,12 +69,9 @@ public: | ||||
|   int peek(); | ||||
|   int read(); | ||||
|   void flush(); | ||||
|   void flushTX(); | ||||
|   size_t write(const uint8_t c); | ||||
|   size_t write(const uint8_t* buffer, size_t size); | ||||
| 
 | ||||
|   operator bool() { return true; } | ||||
| 
 | ||||
|   #if ENABLED(SERIAL_STATS_DROPPED_RX) | ||||
|     FORCE_INLINE uint32_t dropped() { return 0; } | ||||
|   #endif | ||||
| @ -83,4 +81,5 @@ public: | ||||
|   #endif | ||||
| }; | ||||
| 
 | ||||
| extern WebSocketSerial webSocketSerial; | ||||
| typedef Serial0Type<WebSocketSerial> MSerialT; | ||||
| extern MSerialT webSocketSerial; | ||||
|  | ||||
| @ -24,7 +24,7 @@ | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| #include "../shared/Delay.h" | ||||
| 
 | ||||
| HalSerial usb_serial; | ||||
| MSerialT usb_serial(TERN0(EMERGENCY_PARSER, true)); | ||||
| 
 | ||||
| // U8glib required functions
 | ||||
| extern "C" { | ||||
|  | ||||
| @ -60,7 +60,7 @@ uint8_t _getc(); | ||||
| 
 | ||||
| #define SHARED_SERVOS HAS_SERVOS | ||||
| 
 | ||||
| extern HalSerial usb_serial; | ||||
| extern MSerialT usb_serial; | ||||
| #define MYSERIAL0 usb_serial | ||||
| 
 | ||||
| #define ST7920_DELAY_1 DELAY_NS(600) | ||||
|  | ||||
| @ -25,6 +25,7 @@ | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   #include "../../../feature/e_parser.h" | ||||
| #endif | ||||
| #include "../../../core/serial_hook.h" | ||||
| 
 | ||||
| #include <stdarg.h> | ||||
| #include <stdio.h> | ||||
| @ -73,19 +74,11 @@ private: | ||||
|   volatile uint32_t index_read; | ||||
| }; | ||||
| 
 | ||||
| class HalSerial { | ||||
| public: | ||||
| 
 | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     EmergencyParser::State emergency_state; | ||||
|     static inline bool emergency_parser_enabled() { return true; } | ||||
|   #endif | ||||
| 
 | ||||
| struct HalSerial { | ||||
|   HalSerial() { host_connected = true; } | ||||
| 
 | ||||
|   void begin(int32_t) {} | ||||
| 
 | ||||
|   void end() {} | ||||
|   void end()          {} | ||||
| 
 | ||||
|   int peek() { | ||||
|     uint8_t value; | ||||
| @ -100,7 +93,7 @@ public: | ||||
|     return transmit_buffer.write(c); | ||||
|   } | ||||
| 
 | ||||
|   operator bool() { return host_connected; } | ||||
|   bool connected() { return host_connected; } | ||||
| 
 | ||||
|   uint16_t available() { | ||||
|     return (uint16_t)receive_buffer.available(); | ||||
| @ -117,92 +110,9 @@ public: | ||||
|       while (transmit_buffer.available()) { /* nada */ } | ||||
|   } | ||||
| 
 | ||||
|   void printf(const char *format, ...) { | ||||
|     static char buffer[256]; | ||||
|     va_list vArgs; | ||||
|     va_start(vArgs, format); | ||||
|     int length = vsnprintf((char *) buffer, 256, (char const *) format, vArgs); | ||||
|     va_end(vArgs); | ||||
|     if (length > 0 && length < 256) { | ||||
|       if (host_connected) { | ||||
|         for (int i = 0; i < length;) { | ||||
|           if (transmit_buffer.write(buffer[i])) { | ||||
|             ++i; | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   #define DEC 10 | ||||
|   #define HEX 16 | ||||
|   #define OCT 8 | ||||
|   #define BIN 2 | ||||
| 
 | ||||
|   void print_bin(uint32_t value, uint8_t num_digits) { | ||||
|     uint32_t mask = 1 << (num_digits -1); | ||||
|     for (uint8_t i = 0; i < num_digits; i++) { | ||||
|       if (!(i %  4) && i) write(' '); | ||||
|       if (!(i % 16) && i) write(' '); | ||||
|       if (value & mask)   write('1'); | ||||
|       else                write('0'); | ||||
|       value <<= 1; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   void print(const char value[]) { printf("%s" , value); } | ||||
|   void print(char value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 8); | ||||
|     else if (nbase == OCT) printf("%3o", value); | ||||
|     else if (nbase == HEX) printf("%2X", value); | ||||
|     else if (nbase == DEC ) printf("%d", value); | ||||
|     else printf("%c" , value); | ||||
|   } | ||||
|   void print(unsigned char value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 8); | ||||
|     else if (nbase == OCT) printf("%3o", value); | ||||
|     else if (nbase == HEX) printf("%2X", value); | ||||
|     else printf("%u" , value); | ||||
|   } | ||||
|   void print(int value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 16); | ||||
|     else if (nbase == OCT) printf("%6o", value); | ||||
|     else if (nbase == HEX) printf("%4X", value); | ||||
|     else printf("%d", value); | ||||
|   } | ||||
|   void print(unsigned int value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 16); | ||||
|     else if (nbase == OCT) printf("%6o", value); | ||||
|     else if (nbase == HEX) printf("%4X", value); | ||||
|     else printf("%u" , value); | ||||
|   } | ||||
|   void print(long value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 32); | ||||
|     else if (nbase == OCT) printf("%11o", value); | ||||
|     else if (nbase == HEX) printf("%8X", value); | ||||
|     else printf("%ld" , value); | ||||
|   } | ||||
|   void print(unsigned long value, int nbase = 0) { | ||||
|     if (nbase == BIN) print_bin(value, 32); | ||||
|     else if (nbase == OCT) printf("%11o", value); | ||||
|     else if (nbase == HEX) printf("%8X", value); | ||||
|     else printf("%lu" , value); | ||||
|   } | ||||
|   void print(float value, int round = 6)  { printf("%f" , value); } | ||||
|   void print(double value, int round = 6) { printf("%f" , value); } | ||||
| 
 | ||||
|   void println(const char value[]) { printf("%s\n" , value); } | ||||
|   void println(char value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(unsigned char value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(int value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(unsigned int value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(long value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(unsigned long value, int nbase = 0) { print(value, nbase); println(); } | ||||
|   void println(float value, int round = 6) { printf("%f\n" , value); } | ||||
|   void println(double value, int round = 6) { printf("%f\n" , value); } | ||||
|   void println() { print('\n'); } | ||||
| 
 | ||||
|   volatile RingBuffer<uint8_t, 128> receive_buffer; | ||||
|   volatile RingBuffer<uint8_t, 128> transmit_buffer; | ||||
|   volatile bool host_connected; | ||||
| }; | ||||
| 
 | ||||
| typedef Serial0Type<HalSerial> MSerialT; | ||||
|  | ||||
| @ -29,6 +29,8 @@ | ||||
|   #include "watchdog.h" | ||||
| #endif | ||||
| 
 | ||||
| DefaultSerial USBSerial(false, UsbSerial); | ||||
| 
 | ||||
| uint32_t HAL_adc_reading = 0; | ||||
| 
 | ||||
| // U8glib required functions
 | ||||
|  | ||||
| @ -60,12 +60,15 @@ extern "C" volatile uint32_t _millis; | ||||
|   #define ST7920_DELAY_3 DELAY_NS(750) | ||||
| #endif | ||||
| 
 | ||||
| typedef ForwardSerial0Type< decltype(UsbSerial) > DefaultSerial; | ||||
| extern DefaultSerial USBSerial; | ||||
| 
 | ||||
| #define _MSERIAL(X) MSerial##X | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| #define MSerial0 MSerial | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   #define MYSERIAL0 UsbSerial | ||||
|   #define MYSERIAL0 USBSerial | ||||
| #elif WITHIN(SERIAL_PORT, 0, 3) | ||||
|   #define MYSERIAL0 MSERIAL(SERIAL_PORT) | ||||
| #else | ||||
| @ -74,7 +77,7 @@ extern "C" volatile uint32_t _millis; | ||||
| 
 | ||||
| #ifdef SERIAL_PORT_2 | ||||
|   #if SERIAL_PORT_2 == -1 | ||||
|     #define MYSERIAL1 UsbSerial | ||||
|     #define MYSERIAL1 USBSerial | ||||
|   #elif WITHIN(SERIAL_PORT_2, 0, 3) | ||||
|     #define MYSERIAL1 MSERIAL(SERIAL_PORT_2) | ||||
|   #else | ||||
| @ -84,7 +87,7 @@ extern "C" volatile uint32_t _millis; | ||||
| 
 | ||||
| #ifdef MMU2_SERIAL_PORT | ||||
|   #if MMU2_SERIAL_PORT == -1 | ||||
|     #define MMU2_SERIAL UsbSerial | ||||
|     #define MMU2_SERIAL USBSerial | ||||
|   #elif WITHIN(MMU2_SERIAL_PORT, 0, 3) | ||||
|     #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) | ||||
|   #else | ||||
| @ -94,7 +97,7 @@ extern "C" volatile uint32_t _millis; | ||||
| 
 | ||||
| #ifdef LCD_SERIAL_PORT | ||||
|   #if LCD_SERIAL_PORT == -1 | ||||
|     #define LCD_SERIAL UsbSerial | ||||
|     #define LCD_SERIAL USBSerial | ||||
|   #elif WITHIN(LCD_SERIAL_PORT, 0, 3) | ||||
|     #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) | ||||
|   #else | ||||
|  | ||||
| @ -25,19 +25,19 @@ | ||||
| #include "MarlinSerial.h" | ||||
| 
 | ||||
| #if USING_SERIAL_0 | ||||
|   MarlinSerial MSerial(LPC_UART0); | ||||
|   MSerialT MSerial(true, LPC_UART0); | ||||
|   extern "C" void UART0_IRQHandler() { MSerial.IRQHandler(); } | ||||
| #endif | ||||
| #if USING_SERIAL_1 | ||||
|   MarlinSerial MSerial1((LPC_UART_TypeDef *) LPC_UART1); | ||||
|   MSerialT MSerial1(true, (LPC_UART_TypeDef *) LPC_UART1); | ||||
|   extern "C" void UART1_IRQHandler() { MSerial1.IRQHandler(); } | ||||
| #endif | ||||
| #if USING_SERIAL_2 | ||||
|   MarlinSerial MSerial2(LPC_UART2); | ||||
|   MSerialT MSerial2(true, LPC_UART2); | ||||
|   extern "C" void UART2_IRQHandler() { MSerial2.IRQHandler(); } | ||||
| #endif | ||||
| #if USING_SERIAL_3 | ||||
|   MarlinSerial MSerial3(LPC_UART3); | ||||
|   MSerialT MSerial3(true, LPC_UART3); | ||||
|   extern "C" void UART3_IRQHandler() { MSerial3.IRQHandler(); } | ||||
| #endif | ||||
| 
 | ||||
|  | ||||
| @ -28,6 +28,7 @@ | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   #include "../../feature/e_parser.h" | ||||
| #endif | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| #ifndef SERIAL_PORT | ||||
|   #define SERIAL_PORT 0 | ||||
| @ -41,27 +42,20 @@ | ||||
| 
 | ||||
| class MarlinSerial : public HardwareSerial<RX_BUFFER_SIZE, TX_BUFFER_SIZE> { | ||||
| public: | ||||
|   MarlinSerial(LPC_UART_TypeDef *UARTx) : | ||||
|     HardwareSerial<RX_BUFFER_SIZE, TX_BUFFER_SIZE>(UARTx) | ||||
|     #if ENABLED(EMERGENCY_PARSER) | ||||
|       , emergency_state(EmergencyParser::State::EP_RESET) | ||||
|     #endif | ||||
|     { } | ||||
|   MarlinSerial(LPC_UART_TypeDef *UARTx) : HardwareSerial<RX_BUFFER_SIZE, TX_BUFFER_SIZE>(UARTx) { } | ||||
| 
 | ||||
|   void end() {} | ||||
| 
 | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     bool recv_callback(const char c) override { | ||||
|       emergency_parser.update(emergency_state, c); | ||||
|       emergency_parser.update(static_cast<Serial0Type<MarlinSerial> *>(this)->emergency_state, c); | ||||
|       return true; // do not discard character
 | ||||
|     } | ||||
| 
 | ||||
|     EmergencyParser::State emergency_state; | ||||
|     static inline bool emergency_parser_enabled() { return true; } | ||||
|   #endif | ||||
| }; | ||||
| 
 | ||||
| extern MarlinSerial MSerial; | ||||
| extern MarlinSerial MSerial1; | ||||
| extern MarlinSerial MSerial2; | ||||
| extern MarlinSerial MSerial3; | ||||
| typedef Serial0Type<MarlinSerial> MSerialT; | ||||
| extern MSerialT MSerial; | ||||
| extern MSerialT MSerial1; | ||||
| extern MSerialT MSerial2; | ||||
| extern MSerialT MSerial3; | ||||
|  | ||||
| @ -24,6 +24,11 @@ | ||||
| #include <Adafruit_ZeroDMA.h> | ||||
| #include <wiring_private.h> | ||||
| 
 | ||||
| #ifdef ADAFRUIT_GRAND_CENTRAL_M4 | ||||
|   DefaultSerial MSerial(false, Serial); | ||||
|   DefaultSerial1 MSerial1(false, Serial1); | ||||
| #endif | ||||
| 
 | ||||
| // ------------------------
 | ||||
| // Local defines
 | ||||
| // ------------------------
 | ||||
|  | ||||
| @ -32,15 +32,19 @@ | ||||
|   #include "MarlinSerial_AGCM4.h" | ||||
| 
 | ||||
|   // Serial ports
 | ||||
|   typedef ForwardSerial0Type< decltype(Serial) > DefaultSerial; | ||||
|   extern DefaultSerial MSerial; | ||||
|   typedef ForwardSerial0Type< decltype(Serial1) > DefaultSerial1; | ||||
|   extern DefaultSerial1 MSerial1; | ||||
| 
 | ||||
|   // MYSERIAL0 required before MarlinSerial includes!
 | ||||
| 
 | ||||
|   #define __MSERIAL(X) Serial##X | ||||
|   #define __MSERIAL(X) MSerial##X | ||||
|   #define _MSERIAL(X) __MSERIAL(X) | ||||
|   #define MSERIAL(X) _MSERIAL(INCREMENT(X)) | ||||
| 
 | ||||
|   #if SERIAL_PORT == -1 | ||||
|     #define MYSERIAL0 Serial | ||||
|     #define MYSERIAL0 MSerial | ||||
|   #elif WITHIN(SERIAL_PORT, 0, 3) | ||||
|     #define MYSERIAL0 MSERIAL(SERIAL_PORT) | ||||
|   #else | ||||
| @ -49,7 +53,7 @@ | ||||
| 
 | ||||
|   #ifdef SERIAL_PORT_2 | ||||
|     #if SERIAL_PORT_2 == -1 | ||||
|       #define MYSERIAL1 Serial | ||||
|       #define MYSERIAL1 MSerial | ||||
|     #elif WITHIN(SERIAL_PORT_2, 0, 3) | ||||
|       #define MYSERIAL1 MSERIAL(SERIAL_PORT_2) | ||||
|     #else | ||||
| @ -59,7 +63,7 @@ | ||||
| 
 | ||||
|   #ifdef MMU2_SERIAL_PORT | ||||
|     #if MMU2_SERIAL_PORT == -1 | ||||
|       #define MMU2_SERIAL Serial | ||||
|       #define MMU2_SERIAL MSerial | ||||
|     #elif WITHIN(MMU2_SERIAL_PORT, 0, 3) | ||||
|       #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) | ||||
|     #else | ||||
| @ -69,7 +73,7 @@ | ||||
| 
 | ||||
|   #ifdef LCD_SERIAL_PORT | ||||
|     #if LCD_SERIAL_PORT == -1 | ||||
|       #define LCD_SERIAL Serial | ||||
|       #define LCD_SERIAL MSerial | ||||
|     #elif WITHIN(LCD_SERIAL_PORT, 0, 3) | ||||
|       #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) | ||||
|     #else | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| 
 | ||||
| #if USING_SERIAL_1 | ||||
|   Uart Serial2(&sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); | ||||
|   UartT Serial2(false, &sercom4, PIN_SERIAL2_RX, PIN_SERIAL2_TX, PAD_SERIAL2_RX, PAD_SERIAL2_TX); | ||||
|   void SERCOM4_0_Handler() { Serial2.IrqHandler(); } | ||||
|   void SERCOM4_1_Handler() { Serial2.IrqHandler(); } | ||||
|   void SERCOM4_2_Handler() { Serial2.IrqHandler(); } | ||||
| @ -36,7 +36,7 @@ | ||||
| #endif | ||||
| 
 | ||||
| #if USING_SERIAL_2 | ||||
|   Uart Serial3(&sercom1, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, PAD_SERIAL3_TX); | ||||
|   UartT Serial3(false, &sercom1, PIN_SERIAL3_RX, PIN_SERIAL3_TX, PAD_SERIAL3_RX, PAD_SERIAL3_TX); | ||||
|   void SERCOM1_0_Handler() { Serial3.IrqHandler(); } | ||||
|   void SERCOM1_1_Handler() { Serial3.IrqHandler(); } | ||||
|   void SERCOM1_2_Handler() { Serial3.IrqHandler(); } | ||||
| @ -44,7 +44,7 @@ | ||||
| #endif | ||||
| 
 | ||||
| #if USING_SERIAL_3 | ||||
|   Uart Serial4(&sercom5, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, PAD_SERIAL4_TX); | ||||
|   UartT Serial4(false, &sercom5, PIN_SERIAL4_RX, PIN_SERIAL4_TX, PAD_SERIAL4_RX, PAD_SERIAL4_TX); | ||||
|   void SERCOM5_0_Handler() { Serial4.IrqHandler(); } | ||||
|   void SERCOM5_1_Handler() { Serial4.IrqHandler(); } | ||||
|   void SERCOM5_2_Handler() { Serial4.IrqHandler(); } | ||||
|  | ||||
| @ -20,6 +20,10 @@ | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| extern Uart Serial2; | ||||
| extern Uart Serial3; | ||||
| extern Uart Serial4; | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| typedef Serial0Type<Uart> UartT; | ||||
| 
 | ||||
| extern UartT Serial2; | ||||
| extern UartT Serial3; | ||||
| extern UartT Serial4; | ||||
|  | ||||
| @ -28,6 +28,10 @@ | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| #include "../shared/Delay.h" | ||||
| 
 | ||||
| #ifdef USBCON | ||||
|   DefaultSerial MSerial(false, SerialUSB); | ||||
| #endif | ||||
| 
 | ||||
| #if ENABLED(SRAM_EEPROM_EMULATION) | ||||
|   #if STM32F7xx | ||||
|     #include <stm32f7xx_ll_pwr.h> | ||||
|  | ||||
| @ -39,6 +39,9 @@ | ||||
| 
 | ||||
| #ifdef USBCON | ||||
|   #include <USBSerial.h> | ||||
|   #include "../../core/serial_hook.h"  | ||||
|   typedef ForwardSerial0Type< decltype(SerialUSB) > DefaultSerial; | ||||
|   extern DefaultSerial MSerial; | ||||
| #endif | ||||
| 
 | ||||
| // ------------------------
 | ||||
| @ -48,7 +51,7 @@ | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   #define MYSERIAL0 SerialUSB | ||||
|   #define MYSERIAL0 MSerial | ||||
| #elif WITHIN(SERIAL_PORT, 1, 6) | ||||
|   #define MYSERIAL0 MSERIAL(SERIAL_PORT) | ||||
| #else | ||||
| @ -57,7 +60,7 @@ | ||||
| 
 | ||||
| #ifdef SERIAL_PORT_2 | ||||
|   #if SERIAL_PORT_2 == -1 | ||||
|     #define MYSERIAL1 SerialUSB | ||||
|     #define MYSERIAL1 MSerial | ||||
|   #elif WITHIN(SERIAL_PORT_2, 1, 6) | ||||
|     #define MYSERIAL1 MSERIAL(SERIAL_PORT_2) | ||||
|   #else | ||||
| @ -67,7 +70,7 @@ | ||||
| 
 | ||||
| #ifdef MMU2_SERIAL_PORT | ||||
|   #if MMU2_SERIAL_PORT == -1 | ||||
|     #define MMU2_SERIAL SerialUSB | ||||
|     #define MMU2_SERIAL MSerial | ||||
|   #elif WITHIN(MMU2_SERIAL_PORT, 1, 6) | ||||
|     #define MMU2_SERIAL MSERIAL(MMU2_SERIAL_PORT) | ||||
|   #else | ||||
| @ -77,7 +80,7 @@ | ||||
| 
 | ||||
| #ifdef LCD_SERIAL_PORT | ||||
|   #if LCD_SERIAL_PORT == -1 | ||||
|     #define LCD_SERIAL SerialUSB | ||||
|     #define LCD_SERIAL MSerial | ||||
|   #elif WITHIN(LCD_SERIAL_PORT, 1, 6) | ||||
|     #define LCD_SERIAL MSERIAL(LCD_SERIAL_PORT) | ||||
|   #else | ||||
|  | ||||
| @ -35,7 +35,7 @@ | ||||
| 
 | ||||
| #define DECLARE_SERIAL_PORT(ser_num) \ | ||||
|   void _rx_complete_irq_ ## ser_num (serial_t * obj); \ | ||||
|   MarlinSerial MSerial ## ser_num (USART ## ser_num, &_rx_complete_irq_ ## ser_num); \ | ||||
|   MSerialT MSerial ## ser_num (true, USART ## ser_num, &_rx_complete_irq_ ## ser_num); \ | ||||
|   void _rx_complete_irq_ ## ser_num (serial_t * obj) { MSerial ## ser_num ._rx_complete_irq(obj); } | ||||
| 
 | ||||
| #define DECLARE_SERIAL_PORT_EXP(ser_num) DECLARE_SERIAL_PORT(ser_num) | ||||
|  | ||||
| @ -24,21 +24,15 @@ | ||||
|   #include "../../feature/e_parser.h" | ||||
| #endif | ||||
| 
 | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| typedef void (*usart_rx_callback_t)(serial_t * obj); | ||||
| 
 | ||||
| class MarlinSerial : public HardwareSerial { | ||||
| public: | ||||
| struct MarlinSerial : public HardwareSerial { | ||||
|   MarlinSerial(void* peripheral, usart_rx_callback_t rx_callback) : | ||||
|       HardwareSerial(peripheral), _rx_callback(rx_callback) | ||||
|       #if ENABLED(EMERGENCY_PARSER) | ||||
|         , emergency_state(EmergencyParser::State::EP_RESET) | ||||
|       #endif | ||||
|   { } | ||||
| 
 | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     static inline bool emergency_parser_enabled() { return true; } | ||||
|   #endif | ||||
| 
 | ||||
|   void begin(unsigned long baud, uint8_t config); | ||||
|   inline void begin(unsigned long baud) { begin(baud, SERIAL_8N1); } | ||||
| 
 | ||||
| @ -46,19 +40,17 @@ public: | ||||
| 
 | ||||
| protected: | ||||
|   usart_rx_callback_t _rx_callback; | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     EmergencyParser::State emergency_state; | ||||
|   #endif | ||||
| }; | ||||
| 
 | ||||
| extern MarlinSerial MSerial1; | ||||
| extern MarlinSerial MSerial2; | ||||
| extern MarlinSerial MSerial3; | ||||
| extern MarlinSerial MSerial4; | ||||
| extern MarlinSerial MSerial5; | ||||
| extern MarlinSerial MSerial6; | ||||
| extern MarlinSerial MSerial7; | ||||
| extern MarlinSerial MSerial8; | ||||
| extern MarlinSerial MSerial9; | ||||
| extern MarlinSerial MSerial10; | ||||
| extern MarlinSerial MSerialLP1; | ||||
| typedef Serial0Type<MarlinSerial> MSerialT; | ||||
| extern MSerialT MSerial1; | ||||
| extern MSerialT MSerial2; | ||||
| extern MSerialT MSerial3; | ||||
| extern MSerialT MSerial4; | ||||
| extern MSerialT MSerial5; | ||||
| extern MSerialT MSerial6; | ||||
| extern MSerialT MSerial7; | ||||
| extern MSerialT MSerial8; | ||||
| extern MSerialT MSerial9; | ||||
| extern MSerialT MSerial10; | ||||
| extern MSerialT MSerialLP1; | ||||
|  | ||||
| @ -84,6 +84,7 @@ | ||||
| 
 | ||||
| #if defined(SERIAL_USB) && !HAS_SD_HOST_DRIVE | ||||
|   USBSerial SerialUSB; | ||||
|   DefaultSerial MSerial(false, SerialUSB); | ||||
| #endif | ||||
| 
 | ||||
| uint16_t HAL_adc_result; | ||||
|  | ||||
| @ -61,8 +61,11 @@ | ||||
| #endif | ||||
| 
 | ||||
| #ifdef SERIAL_USB | ||||
|   typedef ForwardSerial0Type< USBSerial > DefaultSerial; | ||||
|   extern DefaultSerial MSerial; | ||||
| 
 | ||||
|   #if !HAS_SD_HOST_DRIVE | ||||
|     #define UsbSerial Serial | ||||
|     #define UsbSerial MSerial | ||||
|   #else | ||||
|     #define UsbSerial MarlinCompositeSerial | ||||
|   #endif | ||||
|  | ||||
| @ -28,7 +28,7 @@ | ||||
| 
 | ||||
| // Copied from ~/.platformio/packages/framework-arduinoststm32-maple/STM32F1/system/libmaple/usart_private.h
 | ||||
| // Changed to handle Emergency Parser
 | ||||
| static inline __always_inline void my_usart_irq(ring_buffer *rb, ring_buffer *wb, usart_reg_map *regs, MarlinSerial &serial) { | ||||
| static inline __always_inline void my_usart_irq(ring_buffer *rb, ring_buffer *wb, usart_reg_map *regs, MSerialT &serial) { | ||||
|  /* Handle RXNEIE and TXEIE interrupts.
 | ||||
|   * RXNE signifies availability of a byte in DR. | ||||
|   * | ||||
| @ -90,20 +90,20 @@ constexpr bool serial_handles_emergency(int port) { | ||||
|   ; | ||||
| } | ||||
| 
 | ||||
| #define DEFINE_HWSERIAL_MARLIN(name, n)   \ | ||||
|   MarlinSerial name(USART##n,             \ | ||||
|             BOARD_USART##n##_TX_PIN,      \ | ||||
|             BOARD_USART##n##_RX_PIN,      \ | ||||
|             serial_handles_emergency(n)); \ | ||||
|   extern "C" void __irq_usart##n(void) {  \ | ||||
| #define DEFINE_HWSERIAL_MARLIN(name, n)     \ | ||||
|   MSerialT name(serial_handles_emergency(n),\ | ||||
|             USART##n,                       \ | ||||
|             BOARD_USART##n##_TX_PIN,        \ | ||||
|             BOARD_USART##n##_RX_PIN);       \ | ||||
|   extern "C" void __irq_usart##n(void) {    \ | ||||
|     my_usart_irq(USART##n->rb, USART##n->wb, USART##n##_BASE, MSerial##n); \ | ||||
|   } | ||||
| 
 | ||||
| #define DEFINE_HWSERIAL_UART_MARLIN(name, n) \ | ||||
|   MarlinSerial name(UART##n,                 \ | ||||
|   MSerialT name(serial_handles_emergency(n), \ | ||||
|           UART##n,                           \ | ||||
|           BOARD_USART##n##_TX_PIN,           \ | ||||
|           BOARD_USART##n##_RX_PIN,           \ | ||||
|           serial_handles_emergency(n));      \ | ||||
|           BOARD_USART##n##_RX_PIN);          \ | ||||
|   extern "C" void __irq_usart##n(void) {     \ | ||||
|     my_usart_irq(UART##n->rb, UART##n->wb, UART##n##_BASE, MSerial##n); \ | ||||
|   } | ||||
|  | ||||
| @ -26,28 +26,13 @@ | ||||
| #include <WString.h> | ||||
| 
 | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   #include "../../feature/e_parser.h" | ||||
| #endif | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| // Increase priority of serial interrupts, to reduce overflow errors
 | ||||
| #define UART_IRQ_PRIO 1 | ||||
| 
 | ||||
| class MarlinSerial : public HardwareSerial { | ||||
| public: | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     const bool ep_enabled; | ||||
|     EmergencyParser::State emergency_state; | ||||
|     inline bool emergency_parser_enabled() { return ep_enabled; } | ||||
|   #endif | ||||
| 
 | ||||
|   MarlinSerial(struct usart_dev *usart_device, uint8 tx_pin, uint8 rx_pin, bool TERN_(EMERGENCY_PARSER, ep_capable)) : | ||||
|     HardwareSerial(usart_device, tx_pin, rx_pin) | ||||
|     #if ENABLED(EMERGENCY_PARSER) | ||||
|       , ep_enabled(ep_capable) | ||||
|       , emergency_state(EmergencyParser::State::EP_RESET) | ||||
|     #endif | ||||
|     { } | ||||
| struct MarlinSerial : public HardwareSerial { | ||||
|   MarlinSerial(struct usart_dev *usart_device, uint8 tx_pin, uint8 rx_pin) : HardwareSerial(usart_device, tx_pin, rx_pin) { } | ||||
| 
 | ||||
|   #ifdef UART_IRQ_PRIO | ||||
|     // Shadow the parent methods to set IRQ priority after begin()
 | ||||
| @ -62,10 +47,12 @@ public: | ||||
|   #endif | ||||
| }; | ||||
| 
 | ||||
| extern MarlinSerial MSerial1; | ||||
| extern MarlinSerial MSerial2; | ||||
| extern MarlinSerial MSerial3; | ||||
| typedef Serial0Type<MarlinSerial> MSerialT; | ||||
| 
 | ||||
| extern MSerialT MSerial1; | ||||
| extern MSerialT MSerial2; | ||||
| extern MSerialT MSerial3; | ||||
| #if EITHER(STM32_HIGH_DENSITY, STM32_XL_DENSITY) | ||||
|   extern MarlinSerial MSerial4; | ||||
|   extern MarlinSerial MSerial5; | ||||
|   extern MSerialT MSerial4; | ||||
|   extern MSerialT MSerial5; | ||||
| #endif | ||||
|  | ||||
| @ -23,7 +23,7 @@ | ||||
| #define PRODUCT_ID 0x29 | ||||
| 
 | ||||
| USBMassStorage MarlinMSC; | ||||
| MarlinUSBCompositeSerial MarlinCompositeSerial; | ||||
| Serial0Type<USBCompositeSerial> MarlinCompositeSerial(true); | ||||
| 
 | ||||
| #include "../../inc/MarlinConfig.h" | ||||
| 
 | ||||
|  | ||||
| @ -18,25 +18,9 @@ | ||||
| #include <USBComposite.h> | ||||
| 
 | ||||
| #include "../../inc/MarlinConfigPre.h" | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   #include "../../feature/e_parser.h" | ||||
| #endif | ||||
| 
 | ||||
| class MarlinUSBCompositeSerial : public USBCompositeSerial { | ||||
| public: | ||||
|   MarlinUSBCompositeSerial() : USBCompositeSerial() | ||||
|     #if ENABLED(EMERGENCY_PARSER) | ||||
|       , emergency_state(EmergencyParser::State::EP_RESET) | ||||
|     #endif | ||||
|     { } | ||||
| 
 | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     EmergencyParser::State emergency_state; | ||||
|     inline bool emergency_parser_enabled() { return true; } | ||||
|   #endif | ||||
| }; | ||||
| #include "../../core/serial_hook.h" | ||||
| 
 | ||||
| extern USBMassStorage MarlinMSC; | ||||
| extern MarlinUSBCompositeSerial MarlinCompositeSerial; | ||||
| extern Serial0Type<USBCompositeSerial> MarlinCompositeSerial; | ||||
| 
 | ||||
| void MSC_SD_init(); | ||||
|  | ||||
| @ -31,6 +31,9 @@ | ||||
| 
 | ||||
| #include <Wire.h> | ||||
| 
 | ||||
| DefaultSerial MSerial(false); | ||||
| USBSerialType USBSerial(false, SerialUSB); | ||||
| 
 | ||||
| uint16_t HAL_adc_result; | ||||
| 
 | ||||
| static const uint8_t pin2sc1a[] = { | ||||
|  | ||||
| @ -50,12 +50,18 @@ | ||||
|   #define IS_TEENSY32 1 | ||||
| #endif | ||||
| 
 | ||||
| #define _MSERIAL(X) Serial##X | ||||
| #include "../../core/serial_hook.h" | ||||
| typedef Serial0Type<decltype(Serial)> DefaultSerial; | ||||
| extern DefaultSerial MSerial; | ||||
| typedef ForwardSerial0Type<decltype(SerialUSB)> USBSerialType; | ||||
| extern USBSerialType USBSerial; | ||||
| 
 | ||||
| #define _MSERIAL(X) MSerial##X | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| #define Serial0 Serial | ||||
| #define MSerial0 MSerial | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   #define MYSERIAL0 SerialUSB | ||||
|   #define MYSERIAL0 USBSerial | ||||
| #elif WITHIN(SERIAL_PORT, 0, 3) | ||||
|   #define MYSERIAL0 MSERIAL(SERIAL_PORT) | ||||
| #endif | ||||
|  | ||||
| @ -31,6 +31,9 @@ | ||||
| 
 | ||||
| #include <Wire.h> | ||||
| 
 | ||||
| DefaultSerial MSerial(false); | ||||
| USBSerialType USBSerial(false, SerialUSB); | ||||
| 
 | ||||
| uint16_t HAL_adc_result, HAL_adc_select; | ||||
| 
 | ||||
| static const uint8_t pin2sc1a[] = { | ||||
|  | ||||
| @ -53,12 +53,18 @@ | ||||
|   #define IS_TEENSY35 1 | ||||
| #endif | ||||
| 
 | ||||
| #define _MSERIAL(X) Serial##X | ||||
| #include "../../core/serial_hook.h" | ||||
| typedef Serial0Type<decltype(Serial)> DefaultSerial; | ||||
| extern DefaultSerial MSerial; | ||||
| typedef ForwardSerial0Type<decltype(SerialUSB)> USBSerialType; | ||||
| extern USBSerialType USBSerial; | ||||
| 
 | ||||
| #define _MSERIAL(X) MSerial##X | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| #define Serial0 Serial | ||||
| #define MSerial0 MSerial | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   #define MYSERIAL0 SerialUSB | ||||
|   #define MYSERIAL0 USBSerial | ||||
| #elif WITHIN(SERIAL_PORT, 0, 3) | ||||
|   #define MYSERIAL0 MSERIAL(SERIAL_PORT) | ||||
| #endif | ||||
|  | ||||
| @ -32,6 +32,9 @@ | ||||
| 
 | ||||
| #include <Wire.h> | ||||
| 
 | ||||
| DefaultSerial MSerial(false); | ||||
| USBSerialType USBSerial(false, SerialUSB); | ||||
| 
 | ||||
| uint16_t HAL_adc_result, HAL_adc_select; | ||||
| 
 | ||||
| static const uint8_t pin2sc1a[] = { | ||||
|  | ||||
| @ -37,6 +37,10 @@ | ||||
| #include <stdint.h> | ||||
| #include <util/atomic.h> | ||||
| 
 | ||||
| #if HAS_ETHERNET | ||||
|   #include "../../feature/ethernet.h" | ||||
| #endif | ||||
| 
 | ||||
| //#define ST7920_DELAY_1 DELAY_NS(600)
 | ||||
| //#define ST7920_DELAY_2 DELAY_NS(750)
 | ||||
| //#define ST7920_DELAY_3 DELAY_NS(750)
 | ||||
| @ -51,9 +55,15 @@ | ||||
|   #define IS_TEENSY41 1 | ||||
| #endif | ||||
| 
 | ||||
| #define _MSERIAL(X) Serial##X | ||||
| #include "../../core/serial_hook.h" | ||||
| typedef Serial0Type<decltype(Serial)> DefaultSerial; | ||||
| extern DefaultSerial MSerial; | ||||
| typedef ForwardSerial0Type<decltype(SerialUSB)> USBSerialType; | ||||
| extern USBSerialType USBSerial; | ||||
| 
 | ||||
| #define _MSERIAL(X) MSerial##X | ||||
| #define MSERIAL(X) _MSERIAL(X) | ||||
| #define Serial0 Serial | ||||
| #define MSerial0 MSerial | ||||
| 
 | ||||
| #if SERIAL_PORT == -1 | ||||
|   #define MYSERIAL0 SerialUSB | ||||
|  | ||||
| @ -920,12 +920,12 @@ void setup() { | ||||
| 
 | ||||
|   MYSERIAL0.begin(BAUDRATE); | ||||
|   millis_t serial_connect_timeout = millis() + 1000UL; | ||||
|   while (!MYSERIAL0 && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } | ||||
|   while (!MYSERIAL0.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } | ||||
| 
 | ||||
|   #if HAS_MULTI_SERIAL && !HAS_ETHERNET | ||||
|     MYSERIAL1.begin(BAUDRATE); | ||||
|     serial_connect_timeout = millis() + 1000UL; | ||||
|     while (!MYSERIAL1 && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } | ||||
|     while (!MYSERIAL1.connected() && PENDING(millis(), serial_connect_timeout)) { /*nada*/ } | ||||
|   #endif | ||||
|   SERIAL_ECHOLNPGM("start"); | ||||
| 
 | ||||
|  | ||||
| @ -314,6 +314,32 @@ | ||||
| 
 | ||||
|   #endif | ||||
| 
 | ||||
|   // C++11 solution that is standard compliant. <type_traits> is not available on all platform
 | ||||
|   namespace Private { | ||||
|     template<bool, typename _Tp = void> struct enable_if { }; | ||||
|     template<typename _Tp>              struct enable_if<true, _Tp> { typedef _Tp type; }; | ||||
|   } | ||||
|   // C++11 solution using SFINAE to detect the existance of a member in a class at compile time. 
 | ||||
|   // It creates a HasMember<Type> structure containing 'value' set to true if the member exists  
 | ||||
|   #define HAS_MEMBER_IMPL(Member) \ | ||||
|     namespace Private { \ | ||||
|       template <typename Type, typename Yes=char, typename No=long> struct HasMember_ ## Member { \ | ||||
|         template <typename C> static Yes& test( decltype(&C::Member) ) ; \ | ||||
|         template <typename C> static No& test(...); \ | ||||
|         enum { value = sizeof(test<Type>(0)) == sizeof(Yes) }; }; \ | ||||
|     } | ||||
| 
 | ||||
|   // Call the method if it exists, but do nothing if it does not. The method is detected at compile time.
 | ||||
|   // If the method exists, this is inlined and does not cost anything. Else, an "empty" wrapper is created, returning a default value
 | ||||
|   #define CALL_IF_EXISTS_IMPL(Return, Method, ...) \ | ||||
|     HAS_MEMBER_IMPL(Method) \ | ||||
|     namespace Private { \ | ||||
|       template <typename T, typename ... Args> FORCE_INLINE typename enable_if<HasMember_ ## Method <T>::value, Return>::type Call_ ## Method(T * t, Args... a) { return static_cast<Return>(t->Method(a...)); } \ | ||||
|                                                       _UNUSED static                                                  Return  Call_ ## Method(...) { return __VA_ARGS__; } \ | ||||
|     } | ||||
|   #define CALL_IF_EXISTS(Return, That, Method, ...) \ | ||||
|     static_cast<Return>(Private::Call_ ## Method(That, ##__VA_ARGS__)) | ||||
| 
 | ||||
| #else | ||||
| 
 | ||||
|   #define MIN_2(a,b)      ((a)<(b)?(a):(b)) | ||||
|  | ||||
| @ -23,6 +23,10 @@ | ||||
| #include "serial.h" | ||||
| #include "../inc/MarlinConfig.h" | ||||
| 
 | ||||
| #if HAS_ETHERNET | ||||
|   #include "../feature/ethernet.h" | ||||
| #endif | ||||
| 
 | ||||
| uint8_t marlin_debug_flags = MARLIN_DEBUG_NONE; | ||||
| 
 | ||||
| // Commonly-used strings in serial output
 | ||||
| @ -34,7 +38,18 @@ PGMSTR(SP_X_STR, " X");  PGMSTR(SP_Y_STR, " Y");  PGMSTR(SP_Z_STR, " Z");  PGMST | ||||
| PGMSTR(SP_X_LBL, " X:"); PGMSTR(SP_Y_LBL, " Y:"); PGMSTR(SP_Z_LBL, " Z:"); PGMSTR(SP_E_LBL, " E:"); | ||||
| 
 | ||||
| #if HAS_MULTI_SERIAL | ||||
|   serial_index_t serial_port_index = 0; | ||||
|   #ifdef SERIAL_CATCHALL | ||||
|     SerialOutputT multiSerial(MYSERIAL, SERIAL_CATCHALL); | ||||
|   #else | ||||
|     #if HAS_ETHERNET | ||||
|       // Runtime checking of the condition variable
 | ||||
|       ConditionalSerial<decltype(MYSERIAL1)> serialOut1(ethernet.have_telnet_client, MYSERIAL1, false); // Takes reference here
 | ||||
|     #else | ||||
|       // Don't pay for runtime checking a true variable, instead use the output directly
 | ||||
|       #define serialOut1 MYSERIAL1 | ||||
|     #endif | ||||
|     SerialOutputT multiSerial(MYSERIAL0, serialOut1); | ||||
|   #endif | ||||
| #endif | ||||
| 
 | ||||
| void serialprintPGM(PGM_P str) { | ||||
|  | ||||
| @ -22,10 +22,7 @@ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../inc/MarlinConfig.h" | ||||
| 
 | ||||
| #if HAS_ETHERNET | ||||
|   #include "../feature/ethernet.h" | ||||
| #endif | ||||
| #include "serial_hook.h" | ||||
| 
 | ||||
| // Commonly-used strings in serial output
 | ||||
| extern const char NUL_STR[], SP_P_STR[], SP_T_STR[], | ||||
| @ -62,40 +59,33 @@ extern uint8_t marlin_debug_flags; | ||||
| // Serial redirection
 | ||||
| //
 | ||||
| typedef int8_t serial_index_t; | ||||
| #define SERIAL_BOTH 0x7F | ||||
| 
 | ||||
| #define SERIAL_ALL 0x7F | ||||
| #if HAS_MULTI_SERIAL | ||||
|   extern serial_index_t serial_port_index; | ||||
|   #define _PORT_REDIRECT(n,p)   REMEMBER(n,serial_port_index,p) | ||||
|   #define _PORT_RESTORE(n)      RESTORE(n) | ||||
| 
 | ||||
|   #define _PORT_REDIRECT(n,p)   REMEMBER(n,multiSerial.portMask,p) | ||||
|   #define SERIAL_ASSERT(P)      if(multiSerial.portMask!=(P)){ debugger(); } | ||||
|   #ifdef SERIAL_CATCHALL | ||||
|     #define SERIAL_OUT(WHAT, V...) (void)CAT(MYSERIAL,SERIAL_CATCHALL).WHAT(V) | ||||
|     typedef MultiSerial<decltype(MYSERIAL), decltype(SERIAL_CATCHALL), 0> SerialOutputT; | ||||
|   #else | ||||
|     #define SERIAL_OUT(WHAT, V...) do{ \ | ||||
|       const bool port2_open = TERN1(HAS_ETHERNET, ethernet.have_telnet_client); \ | ||||
|       if ( serial_port_index == 0 || serial_port_index == SERIAL_BOTH)                (void)MYSERIAL0.WHAT(V); \ | ||||
|       if ((serial_port_index == 1 || serial_port_index == SERIAL_BOTH) && port2_open) (void)MYSERIAL1.WHAT(V); \ | ||||
|     }while(0) | ||||
|     typedef MultiSerial<decltype(MYSERIAL0), TERN(HAS_ETHERNET, ConditionalSerial<decltype(MYSERIAL1)>, decltype(MYSERIAL1)), 0>      SerialOutputT; | ||||
|   #endif | ||||
| 
 | ||||
|   #define SERIAL_ASSERT(P)      if(serial_port_index!=(P)){ debugger(); } | ||||
|   extern SerialOutputT          multiSerial; | ||||
|   #define SERIAL_IMPL           multiSerial | ||||
| #else | ||||
|   #define _PORT_REDIRECT(n,p)   NOOP | ||||
|   #define _PORT_RESTORE(n)      NOOP | ||||
|   #define SERIAL_OUT(WHAT, V...) (void)MYSERIAL0.WHAT(V) | ||||
|   #define SERIAL_ASSERT(P)      NOOP | ||||
|   #define SERIAL_IMPL           MYSERIAL0 | ||||
| #endif | ||||
| 
 | ||||
| #define SERIAL_OUT(WHAT, V...)  (void)SERIAL_IMPL.WHAT(V) | ||||
| 
 | ||||
| #define PORT_REDIRECT(p)        _PORT_REDIRECT(1,p) | ||||
| #define PORT_RESTORE()          _PORT_RESTORE(1) | ||||
| #define SERIAL_PORTMASK(P)      _BV(P) | ||||
| 
 | ||||
| #define SERIAL_ECHO(x)          SERIAL_OUT(print, x) | ||||
| #define SERIAL_ECHO_F(V...)     SERIAL_OUT(print, V) | ||||
| #define SERIAL_ECHOLN(x)        SERIAL_OUT(println, x) | ||||
| #define SERIAL_PRINT(x,b)       SERIAL_OUT(print, x, b) | ||||
| #define SERIAL_PRINTLN(x,b)     SERIAL_OUT(println, x, b) | ||||
| #define SERIAL_PRINTF(V...)     SERIAL_OUT(printf, V) | ||||
| #define SERIAL_FLUSH()          SERIAL_OUT(flush) | ||||
| 
 | ||||
| #ifdef ARDUINO_ARCH_STM32 | ||||
|  | ||||
							
								
								
									
										146
									
								
								Marlin/src/core/serial_base.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										146
									
								
								Marlin/src/core/serial_base.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,146 @@ | ||||
| /**
 | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
 | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "../inc/MarlinConfigPre.h" | ||||
| 
 | ||||
| #if ENABLED(EMERGENCY_PARSER) | ||||
|   #include "../feature/e_parser.h" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef DEC | ||||
|   #define DEC 10 | ||||
|   #define HEX 16 | ||||
|   #define OCT 8 | ||||
|   #define BIN 2 | ||||
| #endif | ||||
| 
 | ||||
| // flushTX is not implemented in all HAL, so use SFINAE to call the method where it is.
 | ||||
| CALL_IF_EXISTS_IMPL(void, flushTX ); | ||||
| CALL_IF_EXISTS_IMPL(bool, connected, true); | ||||
| 
 | ||||
| // Using Curiously Recurring Template Pattern here to avoid virtual table cost when compiling.
 | ||||
| // Since the real serial class is known at compile time, this results in compiler writing a completely
 | ||||
| // efficient code
 | ||||
| template <class Child> | ||||
| struct SerialBase { | ||||
|   #if ENABLED(EMERGENCY_PARSER) | ||||
|     const bool ep_enabled; | ||||
|     EmergencyParser::State emergency_state; | ||||
|     inline bool emergency_parser_enabled() { return ep_enabled; } | ||||
|     SerialBase(bool ep_capable) : ep_enabled(ep_capable), emergency_state(EmergencyParser::State::EP_RESET) {} | ||||
|   #else | ||||
|     SerialBase(const bool) {} | ||||
|   #endif | ||||
| 
 | ||||
|   // Static dispatch methods below:
 | ||||
|   // The most important method here is where it all ends to:
 | ||||
|   size_t write(uint8_t c)           { return static_cast<Child*>(this)->write(c); } | ||||
|   // Called when the parser finished processing an instruction, usually build to nothing
 | ||||
|   void msgDone()                    { static_cast<Child*>(this)->msgDone(); } | ||||
|   // Called upon initialization
 | ||||
|   void begin(const long baudRate)   { static_cast<Child*>(this)->begin(baudRate); } | ||||
|   // Called upon destruction
 | ||||
|   void end()                        { static_cast<Child*>(this)->end(); } | ||||
|   /** Check for available data from the port
 | ||||
|       @param index  The port index, usually 0 */ | ||||
|   bool available(uint8_t index = 0) { return static_cast<Child*>(this)->available(index); } | ||||
|   /** Read a value from the port
 | ||||
|       @param index  The port index, usually 0 */ | ||||
|   int  read(uint8_t index = 0)      { return static_cast<Child*>(this)->read(index); } | ||||
|   // Check if the serial port is connected (usually bypassed)
 | ||||
|   bool connected()                  { return static_cast<Child*>(this)->connected(); } | ||||
|   // Redirect flush
 | ||||
|   void flush()                      { static_cast<Child*>(this)->flush(); } | ||||
|   // Not all implementation have a flushTX, so let's call them only if the child has the implementation
 | ||||
|   void flushTX()                    { CALL_IF_EXISTS(void, static_cast<Child*>(this), flushTX); } | ||||
| 
 | ||||
|   // Glue code here
 | ||||
|   FORCE_INLINE void write(const char* str)                    { while (*str) write(*str++); } | ||||
|   FORCE_INLINE void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } | ||||
|   FORCE_INLINE void print(const char* str)                    { write(str); } | ||||
|   FORCE_INLINE void print(char c, int base = 0)               { print((long)c, base); } | ||||
|   FORCE_INLINE void print(unsigned char c, int base = 0)      { print((unsigned long)c, base); } | ||||
|   FORCE_INLINE void print(int c, int base = DEC)              { print((long)c, base); } | ||||
|   FORCE_INLINE void print(unsigned int c, int base = DEC)     { print((unsigned long)c, base); } | ||||
|   void print(long c, int base = DEC)            { if (!base) write(c); write((const uint8_t*)"-", c < 0); printNumber(c < 0 ? -c : c, base); } | ||||
|   void print(unsigned long c, int base = DEC)   { printNumber(c, base); } | ||||
|   void print(double c, int digits = 2)          { printFloat(c, digits); } | ||||
| 
 | ||||
|   FORCE_INLINE void println(const char s[])                  { print(s); println(); } | ||||
|   FORCE_INLINE void println(char c, int base = 0)            { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(unsigned char c, int base = 0)   { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(int c, int base = DEC)           { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(unsigned int c, int base = DEC)  { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(long c, int base = DEC)          { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(unsigned long c, int base = DEC) { print(c, base); println(); } | ||||
|   FORCE_INLINE void println(double c, int digits = 2)        { print(c, digits); println(); } | ||||
|   void println()                                { write("\r\n"); } | ||||
| 
 | ||||
|   // Print a number with the given base
 | ||||
|   void printNumber(unsigned long n, const uint8_t base) { | ||||
|     if (n) { | ||||
|       unsigned char buf[8 * sizeof(long)]; // Enough space for base 2
 | ||||
|       int8_t i = 0; | ||||
|       while (n) { | ||||
|         buf[i++] = n % base; | ||||
|         n /= base; | ||||
|       } | ||||
|       while (i--) write((char)(buf[i] + (buf[i] < 10 ? '0' : 'A' - 10))); | ||||
|     } | ||||
|     else write('0'); | ||||
|   } | ||||
| 
 | ||||
|   // Print a decimal number
 | ||||
|   void printFloat(double number, uint8_t digits) { | ||||
|     // Handle negative numbers
 | ||||
|     if (number < 0.0) { | ||||
|       write('-'); | ||||
|       number = -number; | ||||
|     } | ||||
| 
 | ||||
|     // Round correctly so that print(1.999, 2) prints as "2.00"
 | ||||
|     double rounding = 0.5; | ||||
|     LOOP_L_N(i, digits) rounding *= 0.1; | ||||
|     number += rounding; | ||||
| 
 | ||||
|     // Extract the integer part of the number and print it
 | ||||
|     unsigned long int_part = (unsigned long)number; | ||||
|     double remainder = number - (double)int_part; | ||||
|     printNumber(int_part, 10); | ||||
| 
 | ||||
|     // Print the decimal point, but only if there are digits beyond
 | ||||
|     if (digits) { | ||||
|       write('.'); | ||||
|       // Extract digits from the remainder one at a time
 | ||||
|       while (digits--) { | ||||
|         remainder *= 10.0; | ||||
|         int toPrint = int(remainder); | ||||
|         printNumber(toPrint, 10); | ||||
|         remainder -= toPrint; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| // All serial instances will be built by chaining the features required for the function in a form of a template
 | ||||
| // type definition
 | ||||
							
								
								
									
										230
									
								
								Marlin/src/core/serial_hook.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								Marlin/src/core/serial_hook.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,230 @@ | ||||
| /**
 | ||||
|  * Marlin 3D Printer Firmware | ||||
|  * Copyright (c) 2020 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
 | ||||
|  * | ||||
|  * Based on Sprinter and grbl. | ||||
|  * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm | ||||
|  * | ||||
|  * This program is free software: you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU General Public License as published by | ||||
|  * the Free Software Foundation, either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 | ||||
|  * | ||||
|  */ | ||||
| #pragma once | ||||
| 
 | ||||
| #include "serial_base.h" | ||||
| 
 | ||||
| // 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
 | ||||
| template <class SerialT> | ||||
| struct BaseSerial : public SerialBase< BaseSerial<SerialT> >, public SerialT { | ||||
|   typedef SerialBase< BaseSerial<SerialT> > BaseClassT; | ||||
| 
 | ||||
|   // It's required to implement a write method here to help compiler disambiguate what method to call
 | ||||
|   using SerialT::write; | ||||
|   using SerialT::flush; | ||||
| 
 | ||||
|   void msgDone() {} | ||||
| 
 | ||||
|   bool available(uint8_t index) { return index == 0 && SerialT::available(); } | ||||
|   int read(uint8_t index)       { return index == 0 ? SerialT::read() : -1; } | ||||
|   bool connected()              { return CALL_IF_EXISTS(bool, static_cast<SerialT*>(this), connected);; } | ||||
|   // We have 2 implementation of the same method in both base class, let's say which one we want
 | ||||
|   using SerialT::available; | ||||
|   using SerialT::read; | ||||
|   using SerialT::begin; | ||||
|   using SerialT::end; | ||||
| 
 | ||||
|   using BaseClassT::print; | ||||
|   using BaseClassT::println; | ||||
| 
 | ||||
|   BaseSerial(const bool e) : BaseClassT(e) {} | ||||
| 
 | ||||
|   // Forward constructor
 | ||||
|   template <typename... Args> | ||||
|   BaseSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...) {} | ||||
| }; | ||||
| 
 | ||||
| // A serial with a condition checked at runtime for its output
 | ||||
| // A bit less efficient than static dispatching but since it's only used for ethernet's serial output right now, it's ok.
 | ||||
| template <class SerialT> | ||||
| struct ConditionalSerial : public SerialBase< ConditionalSerial<SerialT> > { | ||||
|   typedef SerialBase< ConditionalSerial<SerialT> > BaseClassT; | ||||
| 
 | ||||
|   bool    & condition; | ||||
|   SerialT & out; | ||||
|   size_t write(uint8_t c) { if (condition) return out.write(c); return 0; } | ||||
|   void flush()            { if (condition) out.flush();  } | ||||
|   void begin(long br)     { out.begin(br); } | ||||
|   void end()              { out.end(); } | ||||
| 
 | ||||
|   void msgDone() {} | ||||
|   bool connected()              { return CALL_IF_EXISTS(bool, &out, connected); } | ||||
| 
 | ||||
|   bool available(uint8_t index) { return index == 0 && out.available(); } | ||||
|   int read(uint8_t index)       { return index == 0 ? out.read() : -1; } | ||||
|   using BaseClassT::available; | ||||
|   using BaseClassT::read; | ||||
| 
 | ||||
|   ConditionalSerial(bool & conditionVariable, SerialT & out, const bool e) : BaseClassT(e), condition(conditionVariable), out(out) {} | ||||
| }; | ||||
| 
 | ||||
| // A simple foward class that taking a reference to an existing serial instance (likely created in their respective framework)
 | ||||
| template <class SerialT> | ||||
| struct ForwardSerial : public SerialBase< ForwardSerial<SerialT> > { | ||||
|   typedef SerialBase< ForwardSerial<SerialT> > BaseClassT; | ||||
| 
 | ||||
|   SerialT & out; | ||||
|   size_t write(uint8_t c) { return out.write(c); } | ||||
|   void flush()            { out.flush();  } | ||||
|   void begin(long br)     { out.begin(br); } | ||||
|   void end()              { out.end(); } | ||||
| 
 | ||||
|   void msgDone() {} | ||||
|   // Existing instances implement Arduino's operator bool, so use that if it's available
 | ||||
|   bool connected()              { return Private::HasMember_connected<SerialT>::value ? CALL_IF_EXISTS(bool, &out, connected) : (bool)out; } | ||||
| 
 | ||||
|   bool available(uint8_t index) { return index == 0 && out.available(); } | ||||
|   int read(uint8_t index)       { return index == 0 ? out.read() : -1; } | ||||
|   bool available()              { return out.available(); } | ||||
|   int read()                    { return out.read(); } | ||||
| 
 | ||||
|   ForwardSerial(const bool e, SerialT & out) : BaseClassT(e), out(out) {} | ||||
| }; | ||||
| 
 | ||||
| // A class that's can be hooked and unhooked at runtime, useful to capturing the output of the serial interface
 | ||||
| template <class SerialT> | ||||
| struct RuntimeSerial : public SerialBase< RuntimeSerial<SerialT> >, public SerialT { | ||||
|   typedef SerialBase< RuntimeSerial<SerialT> > BaseClassT; | ||||
|   typedef void (*WriteHook)(void * userPointer, uint8_t c); | ||||
|   typedef void (*EndOfMessageHook)(void * userPointer); | ||||
| 
 | ||||
|   WriteHook        writeHook; | ||||
|   EndOfMessageHook eofHook; | ||||
|   void *           userPointer; | ||||
| 
 | ||||
|   size_t write(uint8_t c) { | ||||
|     if (writeHook) writeHook(userPointer, c); | ||||
|     return SerialT::write(c); | ||||
|   } | ||||
| 
 | ||||
|   void msgDone() { | ||||
|     if (eofHook) eofHook(userPointer); | ||||
|   } | ||||
| 
 | ||||
|   bool available(uint8_t index) { return index == 0 && SerialT::available(); } | ||||
|   int read(uint8_t index)       { return index == 0 ? SerialT::read() : -1; } | ||||
|   using SerialT::available; | ||||
|   using SerialT::read; | ||||
|   using SerialT::flush; | ||||
|   using SerialT::begin; | ||||
|   using SerialT::end; | ||||
| 
 | ||||
|   using BaseClassT::print; | ||||
|   using BaseClassT::println; | ||||
|    | ||||
| 
 | ||||
|   void setHook(WriteHook writeHook = 0, EndOfMessageHook eofHook = 0, void * userPointer = 0) { | ||||
|     // Order is important here as serial code can be called inside interrupts
 | ||||
|     // When setting a hook, the user pointer must be set first so if writeHook is called as soon as it's set, it'll be valid
 | ||||
|     if (userPointer) this->userPointer = userPointer; | ||||
|     this->writeHook = writeHook; | ||||
|     this->eofHook = eofHook; | ||||
|     // Order is important here because of asynchronous access here
 | ||||
|     // When unsetting a hook, the user pointer must be unset last so that any pending writeHook is still using the old pointer
 | ||||
|     if (!userPointer) this->userPointer = 0; | ||||
|   } | ||||
| 
 | ||||
|   RuntimeSerial(const bool e) : BaseClassT(e), writeHook(0), eofHook(0), userPointer(0) {} | ||||
| 
 | ||||
|   // Forward constructor
 | ||||
|   template <typename... Args> | ||||
|   RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...) {} | ||||
| }; | ||||
| 
 | ||||
| // A class that's duplicating its output conditionally to 2 serial interface
 | ||||
| template <class Serial0T, class Serial1T, const uint8_t offset = 0> | ||||
| struct MultiSerial : public SerialBase< MultiSerial<Serial0T, Serial1T, offset> > { | ||||
|   typedef SerialBase< MultiSerial<Serial0T, Serial1T, offset> > BaseClassT; | ||||
| 
 | ||||
|   uint8_t    portMask; | ||||
|   Serial0T & serial0; | ||||
|   Serial1T & serial1; | ||||
| 
 | ||||
|   enum Masks { | ||||
|     FirstOutputMask   =  (1 << offset), | ||||
|     SecondOutputMask  =  (1 << (offset + 1)), | ||||
|     AllMask           = FirstOutputMask | SecondOutputMask, | ||||
|   }; | ||||
| 
 | ||||
|   size_t write(uint8_t c) { | ||||
|     size_t ret = 0; | ||||
|     if (portMask & FirstOutputMask)   ret = serial0.write(c); | ||||
|     if (portMask & SecondOutputMask)  ret = serial1.write(c) | ret; | ||||
|     return ret; | ||||
|   } | ||||
|   void msgDone() { | ||||
|     if (portMask & FirstOutputMask)   serial0.msgDone(); | ||||
|     if (portMask & SecondOutputMask)  serial1.msgDone(); | ||||
|   } | ||||
|   bool available(uint8_t index) { | ||||
|     switch(index) { | ||||
|       case 0 + offset: return serial0.available(); | ||||
|       case 1 + offset: return serial1.available(); | ||||
|       default: return false; | ||||
|     } | ||||
|   } | ||||
|   int read(uint8_t index) { | ||||
|     switch(index) { | ||||
|       case 0 + offset: return serial0.read(); | ||||
|       case 1 + offset: return serial1.read(); | ||||
|       default: return -1; | ||||
|     } | ||||
|   } | ||||
|   void begin(const long br) { | ||||
|     if (portMask & FirstOutputMask)   serial0.begin(br); | ||||
|     if (portMask & SecondOutputMask)  serial1.begin(br); | ||||
|   } | ||||
|   void end() { | ||||
|     if (portMask & FirstOutputMask)   serial0.end(); | ||||
|     if (portMask & SecondOutputMask)  serial1.end(); | ||||
|   } | ||||
|   bool connected() { | ||||
|     bool ret = true; | ||||
|     if (portMask & FirstOutputMask)   ret = CALL_IF_EXISTS(bool, &serial0, connected); | ||||
|     if (portMask & SecondOutputMask)  ret = ret && CALL_IF_EXISTS(bool, &serial1, connected); | ||||
|     return ret; | ||||
|   } | ||||
| 
 | ||||
|   using BaseClassT::available; | ||||
|   using BaseClassT::read; | ||||
| 
 | ||||
|   // Redirect flush
 | ||||
|   void flush()      { | ||||
|     if (portMask & FirstOutputMask)   serial0.flush(); | ||||
|     if (portMask & SecondOutputMask)  serial1.flush(); | ||||
|   } | ||||
|   void flushTX()    { | ||||
|     if (portMask & FirstOutputMask)   CALL_IF_EXISTS(void, &serial0, flushTX); | ||||
|     if (portMask & SecondOutputMask)  CALL_IF_EXISTS(void, &serial1, flushTX); | ||||
|   } | ||||
| 
 | ||||
|   MultiSerial(Serial0T & serial0, Serial1T & serial1, int8_t mask = AllMask, const bool e = false) : | ||||
|     BaseClassT(e), | ||||
|     portMask(mask), serial0(serial0), serial1(serial1) {} | ||||
| }; | ||||
| 
 | ||||
| // Build the actual serial object depending on current configuration
 | ||||
| #define Serial0Type TERN(SERIAL_RUNTIME_HOOK, RuntimeSerial, BaseSerial) | ||||
| #define ForwardSerial0Type TERN(SERIAL_RUNTIME_HOOK, RuntimeSerial, ForwardSerial) | ||||
| #ifdef HAS_MULTI_SERIAL | ||||
|   #define Serial1Type ConditionalSerial | ||||
| #endif | ||||
| @ -30,23 +30,11 @@ | ||||
| #endif | ||||
| 
 | ||||
| inline bool bs_serial_data_available(const uint8_t index) { | ||||
|   switch (index) { | ||||
|     case 0: return MYSERIAL0.available(); | ||||
|     #if HAS_MULTI_SERIAL | ||||
|       case 1: return MYSERIAL1.available(); | ||||
|     #endif | ||||
|   } | ||||
|   return false; | ||||
|   return SERIAL_IMPL.available(index); | ||||
| } | ||||
| 
 | ||||
| inline int bs_read_serial(const uint8_t index) { | ||||
|   switch (index) { | ||||
|     case 0: return MYSERIAL0.read(); | ||||
|     #if HAS_MULTI_SERIAL | ||||
|       case 1: return MYSERIAL1.read(); | ||||
|     #endif | ||||
|   } | ||||
|   return -1; | ||||
|   return SERIAL_IMPL.read(index); | ||||
| } | ||||
| 
 | ||||
| #if ENABLED(BINARY_STREAM_COMPRESSION) | ||||
| @ -297,7 +285,7 @@ public: | ||||
|     millis_t transfer_window = millis() + RX_TIMESLICE; | ||||
| 
 | ||||
|     #if ENABLED(SDSUPPORT) | ||||
|       PORT_REDIRECT(card.transfer_port_index); | ||||
|       PORT_REDIRECT(SERIAL_PORTMASK(card.transfer_port_index)); | ||||
|     #endif | ||||
| 
 | ||||
|     #pragma GCC diagnostic push | ||||
|  | ||||
| @ -38,7 +38,7 @@ | ||||
| #endif | ||||
| 
 | ||||
| void host_action(PGM_P const pstr, const bool eol) { | ||||
|   PORT_REDIRECT(SERIAL_BOTH); | ||||
|   PORT_REDIRECT(SERIAL_ALL); | ||||
|   SERIAL_ECHOPGM("//action:"); | ||||
|   serialprintPGM(pstr); | ||||
|   if (eol) SERIAL_EOL(); | ||||
| @ -78,20 +78,20 @@ void host_action(PGM_P const pstr, const bool eol) { | ||||
|   PromptReason host_prompt_reason = PROMPT_NOT_DEFINED; | ||||
| 
 | ||||
|   void host_action_notify(const char * const message) { | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     host_action(PSTR("notification "), false); | ||||
|     SERIAL_ECHOLN(message); | ||||
|   } | ||||
| 
 | ||||
|   void host_action_notify_P(PGM_P const message) { | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     host_action(PSTR("notification "), false); | ||||
|     serialprintPGM(message); | ||||
|     SERIAL_EOL(); | ||||
|   } | ||||
| 
 | ||||
|   void host_action_prompt(PGM_P const ptype, const bool eol=true) { | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     host_action(PSTR("prompt_"), false); | ||||
|     serialprintPGM(ptype); | ||||
|     if (eol) SERIAL_EOL(); | ||||
| @ -99,7 +99,7 @@ void host_action(PGM_P const pstr, const bool eol) { | ||||
| 
 | ||||
|   void host_action_prompt_plus(PGM_P const ptype, PGM_P const pstr, const char extra_char='\0') { | ||||
|     host_action_prompt(ptype, false); | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     SERIAL_CHAR(' '); | ||||
|     serialprintPGM(pstr); | ||||
|     if (extra_char != '\0') SERIAL_CHAR(extra_char); | ||||
|  | ||||
| @ -47,7 +47,8 @@ void MAC_report() { | ||||
|     Ethernet.MACAddress(mac); | ||||
|     SERIAL_ECHOPGM("  MAC: "); | ||||
|     LOOP_L_N(i, 6) { | ||||
|       SERIAL_PRINTF("%02X", mac[i]); | ||||
|       if (mac[i] < 16) SERIAL_CHAR('0'); | ||||
|       SERIAL_PRINT(mac[i], HEX); | ||||
|       if (i < 5) SERIAL_CHAR(':'); | ||||
|     } | ||||
|   } | ||||
|  | ||||
| @ -986,6 +986,8 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { | ||||
|   } | ||||
| 
 | ||||
|   if (!no_ok) queue.ok_to_send(); | ||||
| 
 | ||||
|   SERIAL_OUT(msgDone); // Call the msgDone serial hook to signal command processing done
 | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
| @ -995,7 +997,7 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { | ||||
| void GcodeSuite::process_next_command() { | ||||
|   char * const current_command = queue.command_buffer[queue.index_r]; | ||||
| 
 | ||||
|   PORT_REDIRECT(queue.port[queue.index_r]); | ||||
|   PORT_REDIRECT(SERIAL_PORTMASK(queue.port[queue.index_r])); | ||||
| 
 | ||||
|   #if ENABLED(POWER_LOSS_RECOVERY) | ||||
|     recovery.queue_index_r = queue.index_r; | ||||
|  | ||||
| @ -53,21 +53,14 @@ void GcodeSuite::M118() { | ||||
|   } | ||||
| 
 | ||||
|   #if HAS_MULTI_SERIAL | ||||
|     const serial_index_t old_serial = serial_port_index; | ||||
|     const int8_t old_serial = multiSerial.portMask; | ||||
|     if (WITHIN(port, 0, NUM_SERIAL)) | ||||
|       serial_port_index = ( | ||||
|         port == 0 ? SERIAL_BOTH | ||||
|         : port == 1 ? 0 | ||||
|         #if HAS_MULTI_SERIAL | ||||
|           : port == 2 ? 1 | ||||
|         #endif | ||||
|         : SERIAL_PORT | ||||
|       ); | ||||
|       multiSerial.portMask = port ? _BV(port - 1) : SERIAL_ALL; | ||||
|   #endif | ||||
| 
 | ||||
|   if (hasE) SERIAL_ECHO_START(); | ||||
|   if (hasA) SERIAL_ECHOPGM("//"); | ||||
|   SERIAL_ECHOLN(p); | ||||
| 
 | ||||
|   TERN_(HAS_MULTI_SERIAL, serial_port_index = old_serial); | ||||
|   TERN_(HAS_MULTI_SERIAL, multiSerial.portMask = old_serial); | ||||
| } | ||||
|  | ||||
| @ -290,8 +290,8 @@ void GCodeQueue::enqueue_now_P(PGM_P const pgcode) { | ||||
| void GCodeQueue::ok_to_send() { | ||||
|   #if HAS_MULTI_SERIAL | ||||
|     const serial_index_t serial_ind = command_port(); | ||||
|     if (serial_ind < 0) return;                   // Never mind. Command came from SD or Flash Drive
 | ||||
|     PORT_REDIRECT(serial_ind);                    // Reply to the serial port that sent the command
 | ||||
|     if (serial_ind < 0) return; | ||||
|     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
 | ||||
|   #endif | ||||
|   if (!send_ok[index_r]) return; | ||||
|   SERIAL_ECHOPGM(STR_OK); | ||||
| @ -317,7 +317,7 @@ void GCodeQueue::flush_and_request_resend() { | ||||
|   const serial_index_t serial_ind = command_port(); | ||||
|   #if HAS_MULTI_SERIAL | ||||
|     if (serial_ind < 0) return;                   // Never mind. Command came from SD or Flash Drive
 | ||||
|     PORT_REDIRECT(serial_ind);                    // Reply to the serial port that sent the command
 | ||||
|     PORT_REDIRECT(SERIAL_PORTMASK(serial_ind));   // Reply to the serial port that sent the command
 | ||||
|   #endif | ||||
|   SERIAL_FLUSH(); | ||||
|   SERIAL_ECHOPGM(STR_RESEND); | ||||
| @ -349,11 +349,11 @@ inline int read_serial(const uint8_t index) { | ||||
| } | ||||
| 
 | ||||
| void GCodeQueue::gcode_line_error(PGM_P const err, const serial_index_t serial_ind) { | ||||
|   PORT_REDIRECT(serial_ind);                      // Reply to the serial port that sent the command
 | ||||
|   PORT_REDIRECT(SERIAL_PORTMASK(serial_ind)); // Reply to the serial port that sent the command
 | ||||
|   SERIAL_ERROR_START(); | ||||
|   serialprintPGM(err); | ||||
|   SERIAL_ECHOLN(last_N[serial_ind]); | ||||
|   while (read_serial(serial_ind) != -1);          // Clear out the RX buffer
 | ||||
|   while (read_serial(serial_ind) != -1);      // Clear out the RX buffer
 | ||||
|   flush_and_request_resend(); | ||||
|   serial_count[serial_ind] = 0; | ||||
| } | ||||
| @ -547,7 +547,7 @@ void GCodeQueue::get_serial_commands() { | ||||
|                 #if ENABLED(BEZIER_CURVE_SUPPORT) | ||||
|                   case 5: | ||||
|                 #endif | ||||
|                   PORT_REDIRECT(p);                      // Reply to the serial port that sent the command
 | ||||
|                   PORT_REDIRECT(SERIAL_PORTMASK(p));     // Reply to the serial port that sent the command
 | ||||
|                   SERIAL_ECHOLNPGM(STR_ERR_STOPPED); | ||||
|                   LCD_MESSAGEPGM(MSG_STOPPED); | ||||
|                   break; | ||||
|  | ||||
| @ -82,7 +82,7 @@ void GcodeSuite::M1001() { | ||||
| 
 | ||||
|   // Announce SD file completion
 | ||||
|   { | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     SERIAL_ECHOLNPGM(STR_FILE_PRINTED); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -320,6 +320,7 @@ | ||||
| // FSMC/SPI TFT Panels (LVGL)
 | ||||
| #if ENABLED(TFT_LVGL_UI) | ||||
|   #define HAS_TFT_LVGL_UI 1 | ||||
|   #define SERIAL_RUNTIME_HOOK 1 | ||||
| #endif | ||||
| 
 | ||||
| // FSMC/SPI TFT Panels
 | ||||
|  | ||||
| @ -414,8 +414,8 @@ void update_usb_status(const bool forceUpdate) { | ||||
|   // This is mildly different than stock, which
 | ||||
|   // appears to use the usb discovery status.
 | ||||
|   // This is more logical.
 | ||||
|   if (last_usb_connected_status != MYSERIAL0 || forceUpdate) { | ||||
|     last_usb_connected_status = MYSERIAL0; | ||||
|   if (last_usb_connected_status != MYSERIAL0.connected() || forceUpdate) { | ||||
|     last_usb_connected_status = MYSERIAL0.connected(); | ||||
|     write_to_lcd_P(last_usb_connected_status ? PSTR("{R:UC}\r\n") : PSTR("{R:UD}\r\n")); | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -253,7 +253,7 @@ const char str_t_thermal_runaway[] PROGMEM = STR_T_THERMAL_RUNAWAY, | ||||
|      */ | ||||
|     void Temperature::report_fan_speed(const uint8_t target) { | ||||
|       if (target >= FAN_COUNT) return; | ||||
|       PORT_REDIRECT(SERIAL_BOTH); | ||||
|       PORT_REDIRECT(SERIAL_ALL); | ||||
|       SERIAL_ECHOLNPAIR("M106 P", target, " S", fan_speed[target]); | ||||
|     } | ||||
|   #endif | ||||
| @ -3130,7 +3130,7 @@ void Temperature::tick() { | ||||
|     void Temperature::auto_report_temperatures() { | ||||
|       if (auto_report_temp_interval && ELAPSED(millis(), next_temp_report_ms)) { | ||||
|         next_temp_report_ms = millis() + 1000UL * auto_report_temp_interval; | ||||
|         PORT_REDIRECT(SERIAL_BOTH); | ||||
|         PORT_REDIRECT(SERIAL_ALL); | ||||
|         print_heater_states(active_extruder); | ||||
|         SERIAL_EOL(); | ||||
|       } | ||||
|  | ||||
| @ -550,12 +550,11 @@ void openFailed(const char * const fname) { | ||||
| 
 | ||||
| void announceOpen(const uint8_t doing, const char * const path) { | ||||
|   if (doing) { | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     PORT_REDIRECT(SERIAL_ALL); | ||||
|     SERIAL_ECHO_START(); | ||||
|     SERIAL_ECHOPGM("Now "); | ||||
|     serialprintPGM(doing == 1 ? PSTR("doing") : PSTR("fresh")); | ||||
|     SERIAL_ECHOLNPAIR(" file: ", path); | ||||
|     PORT_RESTORE(); | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| @ -617,10 +616,11 @@ void CardReader::openFileRead(char * const path, const uint8_t subcall_type/*=0* | ||||
|     filesize = file.fileSize(); | ||||
|     sdpos = 0; | ||||
| 
 | ||||
|     PORT_REDIRECT(SERIAL_BOTH); | ||||
|     SERIAL_ECHOLNPAIR(STR_SD_FILE_OPENED, fname, STR_SD_SIZE, filesize); | ||||
|     SERIAL_ECHOLNPGM(STR_SD_FILE_SELECTED); | ||||
|     PORT_RESTORE(); | ||||
|     { // Don't remove this block, as the PORT_REDIRECT is a RAII
 | ||||
|       PORT_REDIRECT(SERIAL_ALL); | ||||
|       SERIAL_ECHOLNPAIR(STR_SD_FILE_OPENED, fname, STR_SD_SIZE, filesize); | ||||
|       SERIAL_ECHOLNPGM(STR_SD_FILE_SELECTED); | ||||
|     } | ||||
| 
 | ||||
|     selectFileByName(fname); | ||||
|     ui.set_status(longFilename[0] ? longFilename : fname); | ||||
|  | ||||
| @ -174,7 +174,7 @@ public: | ||||
|   #if ENABLED(AUTO_REPORT_SD_STATUS) | ||||
|     static void auto_report_sd_status(); | ||||
|     static inline void set_auto_report_interval(uint8_t v) { | ||||
|       TERN_(HAS_MULTI_SERIAL, auto_report_port = serial_port_index); | ||||
|       TERN_(HAS_MULTI_SERIAL, auto_report_port = serialHook.portMask); | ||||
|       NOMORE(v, 60); | ||||
|       auto_report_sd_interval = v; | ||||
|       next_sd_report_ms = millis() + 1000UL * v; | ||||
|  | ||||
| @ -20,7 +20,7 @@ exec_test () { | ||||
|   if [[ -z "$VERBOSE_PLATFORMIO" ]] ; then | ||||
|     silent="--silent" | ||||
|   else | ||||
|     silent="" | ||||
|     silent="-v" | ||||
|   fi | ||||
|   if platformio run --project-dir $1 -e $2 $silent; then | ||||
|     printf "\033[0;32mPassed\033[0m\n" | ||||
|  | ||||
							
								
								
									
										44
									
								
								docs/Serial.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								docs/Serial.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,44 @@ | ||||
| # Serial port architecture in Marlin | ||||
| 
 | ||||
| Marlin is targeting a pletora of different CPU architecture and platforms. Each of these platforms has its own serial interface. | ||||
| While many provide a Arduino-like Serial class, it's not all of them, and the differences in the existing API create a very complex brain teaser for writing code that works more or less on each platform. | ||||
| 
 | ||||
| Moreover, many platform have intrinsic needs about serial port (like forwarding the output on multiple serial port, providing a *serial-like* telnet server, mixing USB-based serial port with SD card emulation) that are difficult to handle cleanly in the other platform serial logic. | ||||
| 
 | ||||
| 
 | ||||
| Starting with version `2.0.9`, Marlin provides a common interface for its serial needs. | ||||
| 
 | ||||
| ## Common interface | ||||
| 
 | ||||
| This interface is declared in `Marlin/src/core/serial_base.h`  | ||||
| Any implementation will need to follow this interface for being used transparently in Marlin's codebase. | ||||
| 
 | ||||
| The implementation was written to prioritize performance over abstraction, so the base interface is not using virtual inheritance to avoid the cost of virtual dispatching while calling methods. | ||||
| Instead, the Curiously Recurring Template Pattern (**CRTP**) is used so that, upon compilation, the interface abstraction does not incur a performance cost. | ||||
| 
 | ||||
| Because some platform do not follow the same interface, the missing method in the actual low-level implementation are detected via SFINAE and a wrapper is generated when such method are missing. See `CALL_IF_EXISTS` macro in `Marlin/src/core/macros.h` for the documentation of this technic.   | ||||
| 
 | ||||
| ## Composing the desired feature | ||||
| The different specificities for each architecture are provided by composing the serial type based on desired functionality. | ||||
| In the `Marlin/src/core/serial_hook.h` file, the different serial feature are declared and defined in each templated type: | ||||
| 1. `BaseSerial` is a simple 1:1 wrapper to the underlying, Arduino compatible, `Serial`'s class. It derives from it. You'll use this if the platform does not do anything specific for the `Serial` object (for example, if an interrupt callback calls directly the serial **instance** in the platform's framework code, this is not the right class to use). This wrapper is completely inlined so that it does not generate any code upon compilation. `BaseSerial` constructor forwards any parameter to the platform's `Serial`'s constructor. | ||||
| 2. `ForwardSerial` is a composing wrapper. It references an actual Arduino compatible `Serial` instance. You'll use this if the instance is declared in the platform's framework and is being referred directly in the framework. This is not as efficient as the `BaseSerial` implementation since static dereferencing is done for each method call (it'll still be faster than virtual dispatching) | ||||
| 3. `ConditionalSerial` is working a bit like the `ForwardSerial` interface, but it checks a boolean condition before calling the referenced instance. You'll use it when the serial output can be switch off at runtime, for example in a *telnet* like serial output that should not emit any packet if no client is connected. | ||||
| 4. `RuntimeSerial` is providing a runtime-modifiable hooking method for its `write` and `msgDone` method. You'll use it if you need to capture the serial output of Marlin, for example to display the G-Code parser's output on a GUI interface. The hooking interface is setup via the `setHook` method. | ||||
| 5. `MultiSerial` is a runtime modifiable serial output multiplexer. It can output (*respectively input*) to 2 different interface based on a port *mask*. You'll use this if you need to output the same serial stream to multiple port. You can plug a `MultiSerial` to itself to duplicate to more than 2 ports.  | ||||
| 
 | ||||
| ## Plumbing | ||||
| Since all the types above are using CRTP, it's possible to combine them to get the appropriate functionality.  | ||||
| This is easily done via type definition of the feature.  | ||||
| 
 | ||||
| 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: | ||||
| ``` | ||||
| typedef MultiSerial< RuntimeSerial<Serial>, ConditionalSerial<TelnetClient> > Serial0Type; | ||||
| ``` | ||||
| 
 | ||||
| ## Emergency parser | ||||
| 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 type takes a first `bool emergencyParserEnabled` argument to their constructor. You must take into account this parameter when defining the actual type used.  | ||||
| 
 | ||||
| 
 | ||||
| *This document was written by [X-Ryl669](https://blog.cyril.by) and is under [CC-SA license](https://creativecommons.org/licenses/by-sa)* | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user