From 3f01b222b2b4f77cff66096dd5a18a64828e1fa4 Mon Sep 17 00:00:00 2001 From: X-Ryl669 Date: Thu, 28 Jan 2021 09:02:06 +0100 Subject: [PATCH] Refactor serial class with templates (#20783) --- Marlin/src/HAL/AVR/HAL.cpp | 7 + Marlin/src/HAL/AVR/HAL.h | 10 +- Marlin/src/HAL/AVR/MarlinSerial.cpp | 195 ++------------- Marlin/src/HAL/AVR/MarlinSerial.h | 91 +++---- Marlin/src/HAL/DUE/HAL.cpp | 7 + Marlin/src/HAL/DUE/HAL.h | 15 +- Marlin/src/HAL/DUE/MarlinSerial.cpp | 166 +------------ Marlin/src/HAL/DUE/MarlinSerial.h | 43 +--- Marlin/src/HAL/DUE/MarlinSerialUSB.cpp | 157 +----------- Marlin/src/HAL/DUE/MarlinSerialUSB.h | 47 +--- Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp | 4 +- .../src/HAL/ESP32/FlushableHardwareSerial.cpp | 5 +- .../src/HAL/ESP32/FlushableHardwareSerial.h | 7 +- Marlin/src/HAL/ESP32/HAL.cpp | 4 + Marlin/src/HAL/ESP32/HAL.h | 4 +- Marlin/src/HAL/ESP32/WebSocketSerial.cpp | 6 +- Marlin/src/HAL/ESP32/WebSocketSerial.h | 7 +- Marlin/src/HAL/LINUX/HAL.cpp | 2 +- Marlin/src/HAL/LINUX/HAL.h | 2 +- Marlin/src/HAL/LINUX/include/serial.h | 102 +------- Marlin/src/HAL/LPC1768/HAL.cpp | 2 + Marlin/src/HAL/LPC1768/HAL.h | 11 +- Marlin/src/HAL/LPC1768/MarlinSerial.cpp | 8 +- Marlin/src/HAL/LPC1768/MarlinSerial.h | 22 +- Marlin/src/HAL/SAMD51/HAL.cpp | 5 + Marlin/src/HAL/SAMD51/HAL.h | 14 +- Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.cpp | 6 +- Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h | 10 +- Marlin/src/HAL/STM32/HAL.cpp | 4 + Marlin/src/HAL/STM32/HAL.h | 11 +- Marlin/src/HAL/STM32/MarlinSerial.cpp | 2 +- Marlin/src/HAL/STM32/MarlinSerial.h | 38 ++- Marlin/src/HAL/STM32F1/HAL.cpp | 1 + Marlin/src/HAL/STM32F1/HAL.h | 5 +- Marlin/src/HAL/STM32F1/MarlinSerial.cpp | 20 +- Marlin/src/HAL/STM32F1/MarlinSerial.h | 33 +-- Marlin/src/HAL/STM32F1/msc_sd.cpp | 2 +- Marlin/src/HAL/STM32F1/msc_sd.h | 20 +- Marlin/src/HAL/TEENSY31_32/HAL.cpp | 3 + Marlin/src/HAL/TEENSY31_32/HAL.h | 12 +- Marlin/src/HAL/TEENSY35_36/HAL.cpp | 3 + Marlin/src/HAL/TEENSY35_36/HAL.h | 12 +- Marlin/src/HAL/TEENSY40_41/HAL.cpp | 3 + Marlin/src/HAL/TEENSY40_41/HAL.h | 14 +- Marlin/src/MarlinCore.cpp | 4 +- Marlin/src/core/macros.h | 26 ++ Marlin/src/core/serial.cpp | 17 +- Marlin/src/core/serial.h | 34 +-- Marlin/src/core/serial_base.h | 146 +++++++++++ Marlin/src/core/serial_hook.h | 230 ++++++++++++++++++ Marlin/src/feature/binary_stream.h | 18 +- Marlin/src/feature/host_actions.cpp | 10 +- .../src/gcode/feature/network/M552-M554.cpp | 3 +- Marlin/src/gcode/gcode.cpp | 4 +- Marlin/src/gcode/host/M118.cpp | 13 +- Marlin/src/gcode/queue.cpp | 12 +- Marlin/src/gcode/sd/M1001.cpp | 2 +- Marlin/src/inc/Conditionals_LCD.h | 1 + Marlin/src/lcd/extui/malyan_lcd.cpp | 4 +- Marlin/src/module/temperature.cpp | 4 +- Marlin/src/sd/cardreader.cpp | 12 +- Marlin/src/sd/cardreader.h | 2 +- buildroot/tests/run_tests | 2 +- docs/Serial.md | 44 ++++ 64 files changed, 782 insertions(+), 948 deletions(-) create mode 100644 Marlin/src/core/serial_base.h create mode 100644 Marlin/src/core/serial_hook.h create mode 100644 docs/Serial.md diff --git a/Marlin/src/HAL/AVR/HAL.cpp b/Marlin/src/HAL/AVR/HAL.cpp index 58d57c8ee5..4c45a5d78e 100644 --- a/Marlin/src/HAL/AVR/HAL.cpp +++ b/Marlin/src/HAL/AVR/HAL.cpp @@ -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 // ------------------------ diff --git a/Marlin/src/HAL/AVR/HAL.h b/Marlin/src/HAL/AVR/HAL.h index 8b95acb0ac..2b565bbe13 100644 --- a/Marlin/src/HAL/AVR/HAL.h +++ b/Marlin/src/HAL/AVR/HAL.h @@ -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." diff --git a/Marlin/src/HAL/AVR/MarlinSerial.cpp b/Marlin/src/HAL/AVR/MarlinSerial.cpp index 63599efd41..904395de1d 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.cpp +++ b/Marlin/src/HAL/AVR/MarlinSerial.cpp @@ -454,7 +454,7 @@ void MarlinSerial::flush() { } template -void MarlinSerial::write(const uint8_t c) { +size_t MarlinSerial::write(const uint8_t c) { if (Cfg::TX_SIZE == 0) { _written = true; @@ -480,7 +480,7 @@ void MarlinSerial::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::write(const uint8_t c) { // Enable TX ISR - Non atomic, but it will eventually enable TX ISR B_UDRIE = 1; } + return 1; } template @@ -556,161 +557,6 @@ void MarlinSerial::flushTX() { } } -/** - * Imports from print.h - */ - -template -void MarlinSerial::print(char c, int base) { - print((long)c, base); -} - -template -void MarlinSerial::print(unsigned char b, int base) { - print((unsigned long)b, base); -} - -template -void MarlinSerial::print(int n, int base) { - print((long)n, base); -} - -template -void MarlinSerial::print(unsigned int n, int base) { - print((unsigned long)n, base); -} - -template -void MarlinSerial::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 -void MarlinSerial::print(unsigned long n, int base) { - if (base == 0) write(n); - else printNumber(n, base); -} - -template -void MarlinSerial::print(double n, int digits) { - printFloat(n, digits); -} - -template -void MarlinSerial::println() { - print('\r'); - print('\n'); -} - -template -void MarlinSerial::println(const String& s) { - print(s); - println(); -} - -template -void MarlinSerial::println(const char c[]) { - print(c); - println(); -} - -template -void MarlinSerial::println(char c, int base) { - print(c, base); - println(); -} - -template -void MarlinSerial::println(unsigned char b, int base) { - print(b, base); - println(); -} - -template -void MarlinSerial::println(int n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(unsigned int n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(long n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(unsigned long n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(double n, int digits) { - print(n, digits); - println(); -} - -// Private Methods - -template -void MarlinSerial::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 -void MarlinSerial::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>::store_rxd_char(); @@ -720,11 +566,9 @@ ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _UDRE_vect)) { MarlinSerial>::_tx_udr_empty_irq(); } -// Preinstantiate -template class MarlinSerial>; - -// Instantiate -MarlinSerial> customizedSerial1; +// Because of the template definition above, it's required to instantiate the template to have all method generated +template class MarlinSerial< MarlinSerialCfg >; +MSerialT customizedSerial1(MSerialT::HasEmergencyParser); #ifdef SERIAL_PORT_2 @@ -737,12 +581,8 @@ MarlinSerial> customizedSerial1; MarlinSerial>::_tx_udr_empty_irq(); } - // Preinstantiate - template class MarlinSerial>; - - // Instantiate - MarlinSerial> customizedSerial2; - + template class MarlinSerial< MarlinSerialCfg >; + MSerialT2 customizedSerial2(MSerialT2::HasEmergencyParser); #endif #ifdef MMU2_SERIAL_PORT @@ -755,12 +595,8 @@ MarlinSerial> customizedSerial1; MarlinSerial>::_tx_udr_empty_irq(); } - // Preinstantiate - template class MarlinSerial>; - - // Instantiate - MarlinSerial> mmuSerial; - + template class MarlinSerial< MarlinSerialCfg >; + MSerialT3 mmuSerial(MSerialT3::HasEmergencyParser); #endif #ifdef LCD_SERIAL_PORT @@ -773,12 +609,9 @@ MarlinSerial> customizedSerial1; MarlinSerial>::_tx_udr_empty_irq(); } - // Preinstantiate - template class MarlinSerial>; - - // Instantiate - MarlinSerial> lcdSerial; - + template class MarlinSerial< LCDSerialCfg >; + MSerialT4 lcdSerial(MSerialT4::HasEmergencyParser); + #if HAS_DGUS_LCD template typename MarlinSerial::ring_buffer_pos_t MarlinSerial::get_tx_buffer_free() { @@ -796,7 +629,7 @@ MarlinSerial> customizedSerial1; // For AT90USB targets use the UART for BT interfacing #if defined(USBCON) && ENABLED(BLUETOOTH) - HardwareSerial bluetoothSerial; + MSerialT5 bluetoothSerial(false); #endif #endif // __AVR__ diff --git a/Marlin/src/HAL/AVR/MarlinSerial.h b/Marlin/src/HAL/AVR/MarlinSerial.h index 8a0423d143..93a3fb84d1 100644 --- a/Marlin/src/HAL/AVR/MarlinSerial.h +++ b/Marlin/src/HAL/AVR/MarlinSerial.h @@ -34,6 +34,7 @@ #include #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 @@ -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> customizedSerial1; + + typedef Serial0Type< MarlinSerial< MarlinSerialCfg > > MSerialT; + extern MSerialT customizedSerial1; #ifdef SERIAL_PORT_2 - - extern MarlinSerial> customizedSerial2; - + typedef Serial0Type< MarlinSerial< MarlinSerialCfg > > MSerialT2; + extern MSerialT2 customizedSerial2; #endif #endif // !USBCON @@ -294,7 +262,8 @@ static constexpr bool RX_OVERRUNS = false; }; - extern MarlinSerial> mmuSerial; + typedef Serial0Type< MarlinSerial< MMU2SerialCfg > > MSerialT3; + extern MSerial3 mmuSerial; #endif #ifdef LCD_SERIAL_PORT @@ -322,11 +291,13 @@ #endif }; - extern MarlinSerial> lcdSerial; + typedef Serial0Type< MarlinSerial< LCDSerialCfg > > MSerialT4; + extern MSerialT4 lcdSerial; #endif // Use the UART for Bluetooth in AT90USB configurations #if defined(USBCON) && ENABLED(BLUETOOTH) - extern HardwareSerial bluetoothSerial; + typedef Serial0Type MSerialT5; + extern MSerialT5 bluetoothSerial; #endif diff --git a/Marlin/src/HAL/DUE/HAL.cpp b/Marlin/src/HAL/DUE/HAL.cpp index 6ce85a4643..2ae70843f0 100644 --- a/Marlin/src/HAL/DUE/HAL.cpp +++ b/Marlin/src/HAL/DUE/HAL.cpp @@ -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 diff --git a/Marlin/src/HAL/DUE/HAL.h b/Marlin/src/HAL/DUE/HAL.h index 6a4d4f6149..78c8a800b9 100644 --- a/Marlin/src/HAL/DUE/HAL.h +++ b/Marlin/src/HAL/DUE/HAL.h @@ -36,9 +36,20 @@ #include -#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) diff --git a/Marlin/src/HAL/DUE/MarlinSerial.cpp b/Marlin/src/HAL/DUE/MarlinSerial.cpp index c9a372eeb1..50b84c0b1d 100644 --- a/Marlin/src/HAL/DUE/MarlinSerial.cpp +++ b/Marlin/src/HAL/DUE/MarlinSerial.cpp @@ -382,7 +382,7 @@ void MarlinSerial::flush() { } template -void MarlinSerial::write(const uint8_t c) { +size_t MarlinSerial::write(const uint8_t c) { _written = true; if (Cfg::TX_SIZE == 0) { @@ -400,7 +400,7 @@ void MarlinSerial::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::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 @@ -473,169 +474,16 @@ void MarlinSerial::flushTX() { } } -/** - * Imports from print.h - */ - -template -void MarlinSerial::print(char c, int base) { - print((long)c, base); -} - -template -void MarlinSerial::print(unsigned char b, int base) { - print((unsigned long)b, base); -} - -template -void MarlinSerial::print(int n, int base) { - print((long)n, base); -} - -template -void MarlinSerial::print(unsigned int n, int base) { - print((unsigned long)n, base); -} - -template -void MarlinSerial::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 -void MarlinSerial::print(unsigned long n, int base) { - if (base == 0) write(n); - else printNumber(n, base); -} - -template -void MarlinSerial::print(double n, int digits) { - printFloat(n, digits); -} - -template -void MarlinSerial::println() { - print('\r'); - print('\n'); -} - -template -void MarlinSerial::println(const String& s) { - print(s); - println(); -} - -template -void MarlinSerial::println(const char c[]) { - print(c); - println(); -} - -template -void MarlinSerial::println(char c, int base) { - print(c, base); - println(); -} - -template -void MarlinSerial::println(unsigned char b, int base) { - print(b, base); - println(); -} - -template -void MarlinSerial::println(int n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(unsigned int n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(long n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(unsigned long n, int base) { - print(n, base); - println(); -} - -template -void MarlinSerial::println(double n, int digits) { - print(n, digits); - println(); -} - -// Private Methods -template -void MarlinSerial::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 -void MarlinSerial::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>; // Define - MarlinSerial> customizedSerial1; // Instantiate + template class MarlinSerial< MarlinSerialCfg >; + MSerialT customizedSerial1(MarlinSerialCfg::EMERGENCYPARSER); #endif #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0 - template class MarlinSerial>; // Define - MarlinSerial> customizedSerial2; // Instantiate + template class MarlinSerial< MarlinSerialCfg >; + MSerialT2 customizedSerial2(MarlinSerialCfg::EMERGENCYPARSER); #endif #endif // ARDUINO_ARCH_SAM diff --git a/Marlin/src/HAL/DUE/MarlinSerial.h b/Marlin/src/HAL/DUE/MarlinSerial.h index a194eba2f3..7fc21264bb 100644 --- a/Marlin/src/HAL/DUE/MarlinSerial.h +++ b/Marlin/src/HAL/DUE/MarlinSerial.h @@ -30,11 +30,7 @@ #include #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> customizedSerial1; + typedef Serial0Type< MarlinSerial< MarlinSerialCfg > > MSerialT; + extern MSerialT customizedSerial1; #endif #if defined(SERIAL_PORT_2) && SERIAL_PORT_2 >= 0 - extern MarlinSerial> customizedSerial2; + typedef Serial0Type< MarlinSerial< MarlinSerialCfg > > MSerialT2; + extern MSerialT2 customizedSerial2; #endif diff --git a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp index a41dbfeb7a..d85aaf14b0 100644 --- a/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp +++ b/Marlin/src/HAL/DUE/MarlinSerialUSB.cpp @@ -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 diff --git a/Marlin/src/HAL/DUE/MarlinSerialUSB.h b/Marlin/src/HAL/DUE/MarlinSerialUSB.h index 2e3622e553..9643a8465a 100644 --- a/Marlin/src/HAL/DUE/MarlinSerialUSB.h +++ b/Marlin/src/HAL/DUE/MarlinSerialUSB.h @@ -27,20 +27,13 @@ */ #include "../../inc/MarlinConfig.h" - #if HAS_USB_SERIAL #include +#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 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 diff --git a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp index db5e82ec55..d92d332c1e 100644 --- a/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp +++ b/Marlin/src/HAL/DUE/usb/sd_mmc_spi_mem.cpp @@ -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 diff --git a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp index d4b2f42c53..cc5a4fc476 100644 --- a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp +++ b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.cpp @@ -24,10 +24,7 @@ #ifdef ARDUINO_ARCH_ESP32 -FlushableHardwareSerial::FlushableHardwareSerial(int uart_nr) - : HardwareSerial(uart_nr) -{} -FlushableHardwareSerial flushableSerial(0); +Serial0Type flushableSerial(false, 0); #endif // ARDUINO_ARCH_ESP32 diff --git a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h index b43caea13c..27df0be4b6 100644 --- a/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h +++ b/Marlin/src/HAL/ESP32/FlushableHardwareSerial.h @@ -24,14 +24,13 @@ #ifdef ARDUINO_ARCH_ESP32 #include +#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 flushableSerial; #endif // ARDUINO_ARCH_ESP32 diff --git a/Marlin/src/HAL/ESP32/HAL.cpp b/Marlin/src/HAL/ESP32/HAL.cpp index ead448d78d..6ff1446b1c 100644 --- a/Marlin/src/HAL/ESP32/HAL.cpp +++ b/Marlin/src/HAL/ESP32/HAL.cpp @@ -40,6 +40,10 @@ #endif #endif +#if ENABLED(ESP3D_WIFISUPPORT) + DefaultSerial MSerial(false, Serial2Socket); +#endif + // ------------------------ // Externs // ------------------------ diff --git a/Marlin/src/HAL/ESP32/HAL.h b/Marlin/src/HAL/ESP32/HAL.h index d485b5d1d3..3dc27c6493 100644 --- a/Marlin/src/HAL/ESP32/HAL.h +++ b/Marlin/src/HAL/ESP32/HAL.h @@ -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 diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp b/Marlin/src/HAL/ESP32/WebSocketSerial.cpp index ca7f47a1f8..8825742d38 100644 --- a/Marlin/src/HAL/ESP32/WebSocketSerial.cpp +++ b/Marlin/src/HAL/ESP32/WebSocketSerial.cpp @@ -29,7 +29,7 @@ #include "wifi.h" #include -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 diff --git a/Marlin/src/HAL/ESP32/WebSocketSerial.h b/Marlin/src/HAL/ESP32/WebSocketSerial.h index 7a25c6dc5e..c68792c8c1 100644 --- a/Marlin/src/HAL/ESP32/WebSocketSerial.h +++ b/Marlin/src/HAL/ESP32/WebSocketSerial.h @@ -22,6 +22,7 @@ #pragma once #include "../../inc/MarlinConfig.h" +#include "../../core/serial_hook.h" #include @@ -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 MSerialT; +extern MSerialT webSocketSerial; diff --git a/Marlin/src/HAL/LINUX/HAL.cpp b/Marlin/src/HAL/LINUX/HAL.cpp index ee9e31e140..771f1d2a08 100644 --- a/Marlin/src/HAL/LINUX/HAL.cpp +++ b/Marlin/src/HAL/LINUX/HAL.cpp @@ -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" { diff --git a/Marlin/src/HAL/LINUX/HAL.h b/Marlin/src/HAL/LINUX/HAL.h index 8eaee44cce..e4f4dd3fc3 100644 --- a/Marlin/src/HAL/LINUX/HAL.h +++ b/Marlin/src/HAL/LINUX/HAL.h @@ -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) diff --git a/Marlin/src/HAL/LINUX/include/serial.h b/Marlin/src/HAL/LINUX/include/serial.h index e916249389..2585be25bf 100644 --- a/Marlin/src/HAL/LINUX/include/serial.h +++ b/Marlin/src/HAL/LINUX/include/serial.h @@ -25,6 +25,7 @@ #if ENABLED(EMERGENCY_PARSER) #include "../../../feature/e_parser.h" #endif +#include "../../../core/serial_hook.h" #include #include @@ -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 receive_buffer; volatile RingBuffer transmit_buffer; volatile bool host_connected; }; + +typedef Serial0Type MSerialT; diff --git a/Marlin/src/HAL/LPC1768/HAL.cpp b/Marlin/src/HAL/LPC1768/HAL.cpp index 3614e95385..27aa569fae 100644 --- a/Marlin/src/HAL/LPC1768/HAL.cpp +++ b/Marlin/src/HAL/LPC1768/HAL.cpp @@ -29,6 +29,8 @@ #include "watchdog.h" #endif +DefaultSerial USBSerial(false, UsbSerial); + uint32_t HAL_adc_reading = 0; // U8glib required functions diff --git a/Marlin/src/HAL/LPC1768/HAL.h b/Marlin/src/HAL/LPC1768/HAL.h index c65fff4747..1dc4fe6ff9 100644 --- a/Marlin/src/HAL/LPC1768/HAL.h +++ b/Marlin/src/HAL/LPC1768/HAL.h @@ -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 diff --git a/Marlin/src/HAL/LPC1768/MarlinSerial.cpp b/Marlin/src/HAL/LPC1768/MarlinSerial.cpp index baad3f8f26..454ace33b2 100644 --- a/Marlin/src/HAL/LPC1768/MarlinSerial.cpp +++ b/Marlin/src/HAL/LPC1768/MarlinSerial.cpp @@ -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 diff --git a/Marlin/src/HAL/LPC1768/MarlinSerial.h b/Marlin/src/HAL/LPC1768/MarlinSerial.h index 8d6b64378a..de0f62f006 100644 --- a/Marlin/src/HAL/LPC1768/MarlinSerial.h +++ b/Marlin/src/HAL/LPC1768/MarlinSerial.h @@ -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 { public: - MarlinSerial(LPC_UART_TypeDef *UARTx) : - HardwareSerial(UARTx) - #if ENABLED(EMERGENCY_PARSER) - , emergency_state(EmergencyParser::State::EP_RESET) - #endif - { } + MarlinSerial(LPC_UART_TypeDef *UARTx) : HardwareSerial(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 *>(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 MSerialT; +extern MSerialT MSerial; +extern MSerialT MSerial1; +extern MSerialT MSerial2; +extern MSerialT MSerial3; diff --git a/Marlin/src/HAL/SAMD51/HAL.cpp b/Marlin/src/HAL/SAMD51/HAL.cpp index d985ef3787..a413c4cd80 100644 --- a/Marlin/src/HAL/SAMD51/HAL.cpp +++ b/Marlin/src/HAL/SAMD51/HAL.cpp @@ -24,6 +24,11 @@ #include #include +#ifdef ADAFRUIT_GRAND_CENTRAL_M4 + DefaultSerial MSerial(false, Serial); + DefaultSerial1 MSerial1(false, Serial1); +#endif + // ------------------------ // Local defines // ------------------------ diff --git a/Marlin/src/HAL/SAMD51/HAL.h b/Marlin/src/HAL/SAMD51/HAL.h index fd2eb59475..f28583c771 100644 --- a/Marlin/src/HAL/SAMD51/HAL.h +++ b/Marlin/src/HAL/SAMD51/HAL.h @@ -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 diff --git a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.cpp b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.cpp index fac67cf5a3..ce32eafee5 100644 --- a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.cpp +++ b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.cpp @@ -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(); } diff --git a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h index f3821d8d5a..b7293415d1 100644 --- a/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h +++ b/Marlin/src/HAL/SAMD51/MarlinSerial_AGCM4.h @@ -20,6 +20,10 @@ */ #pragma once -extern Uart Serial2; -extern Uart Serial3; -extern Uart Serial4; +#include "../../core/serial_hook.h" + +typedef Serial0Type UartT; + +extern UartT Serial2; +extern UartT Serial3; +extern UartT Serial4; diff --git a/Marlin/src/HAL/STM32/HAL.cpp b/Marlin/src/HAL/STM32/HAL.cpp index c886f9c0b9..b8db5b5e0b 100644 --- a/Marlin/src/HAL/STM32/HAL.cpp +++ b/Marlin/src/HAL/STM32/HAL.cpp @@ -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 diff --git a/Marlin/src/HAL/STM32/HAL.h b/Marlin/src/HAL/STM32/HAL.h index 9d0c421a19..29df0a5c6d 100644 --- a/Marlin/src/HAL/STM32/HAL.h +++ b/Marlin/src/HAL/STM32/HAL.h @@ -39,6 +39,9 @@ #ifdef USBCON #include + #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 diff --git a/Marlin/src/HAL/STM32/MarlinSerial.cpp b/Marlin/src/HAL/STM32/MarlinSerial.cpp index 4d9177248a..c420ce40cf 100644 --- a/Marlin/src/HAL/STM32/MarlinSerial.cpp +++ b/Marlin/src/HAL/STM32/MarlinSerial.cpp @@ -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) diff --git a/Marlin/src/HAL/STM32/MarlinSerial.h b/Marlin/src/HAL/STM32/MarlinSerial.h index 3611cc78d7..8cc4f0dd4c 100644 --- a/Marlin/src/HAL/STM32/MarlinSerial.h +++ b/Marlin/src/HAL/STM32/MarlinSerial.h @@ -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 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; diff --git a/Marlin/src/HAL/STM32F1/HAL.cpp b/Marlin/src/HAL/STM32F1/HAL.cpp index dfa99d83f4..c1e29a843c 100644 --- a/Marlin/src/HAL/STM32F1/HAL.cpp +++ b/Marlin/src/HAL/STM32F1/HAL.cpp @@ -84,6 +84,7 @@ #if defined(SERIAL_USB) && !HAS_SD_HOST_DRIVE USBSerial SerialUSB; + DefaultSerial MSerial(false, SerialUSB); #endif uint16_t HAL_adc_result; diff --git a/Marlin/src/HAL/STM32F1/HAL.h b/Marlin/src/HAL/STM32F1/HAL.h index c7eef639a5..30bf60b6e8 100644 --- a/Marlin/src/HAL/STM32F1/HAL.h +++ b/Marlin/src/HAL/STM32F1/HAL.h @@ -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 diff --git a/Marlin/src/HAL/STM32F1/MarlinSerial.cpp b/Marlin/src/HAL/STM32F1/MarlinSerial.cpp index 7c9625d64c..c404e81b35 100644 --- a/Marlin/src/HAL/STM32F1/MarlinSerial.cpp +++ b/Marlin/src/HAL/STM32F1/MarlinSerial.cpp @@ -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); \ } diff --git a/Marlin/src/HAL/STM32F1/MarlinSerial.h b/Marlin/src/HAL/STM32F1/MarlinSerial.h index 6aa94b64ff..692e97e618 100644 --- a/Marlin/src/HAL/STM32F1/MarlinSerial.h +++ b/Marlin/src/HAL/STM32F1/MarlinSerial.h @@ -26,28 +26,13 @@ #include #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 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 diff --git a/Marlin/src/HAL/STM32F1/msc_sd.cpp b/Marlin/src/HAL/STM32F1/msc_sd.cpp index ba722b8aeb..548a6dbc57 100644 --- a/Marlin/src/HAL/STM32F1/msc_sd.cpp +++ b/Marlin/src/HAL/STM32F1/msc_sd.cpp @@ -23,7 +23,7 @@ #define PRODUCT_ID 0x29 USBMassStorage MarlinMSC; -MarlinUSBCompositeSerial MarlinCompositeSerial; +Serial0Type MarlinCompositeSerial(true); #include "../../inc/MarlinConfig.h" diff --git a/Marlin/src/HAL/STM32F1/msc_sd.h b/Marlin/src/HAL/STM32F1/msc_sd.h index 1e4e4c44b1..151287f7a7 100644 --- a/Marlin/src/HAL/STM32F1/msc_sd.h +++ b/Marlin/src/HAL/STM32F1/msc_sd.h @@ -18,25 +18,9 @@ #include #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 MarlinCompositeSerial; void MSC_SD_init(); diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.cpp b/Marlin/src/HAL/TEENSY31_32/HAL.cpp index 8c3dd83377..51636d29bf 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.cpp +++ b/Marlin/src/HAL/TEENSY31_32/HAL.cpp @@ -31,6 +31,9 @@ #include +DefaultSerial MSerial(false); +USBSerialType USBSerial(false, SerialUSB); + uint16_t HAL_adc_result; static const uint8_t pin2sc1a[] = { diff --git a/Marlin/src/HAL/TEENSY31_32/HAL.h b/Marlin/src/HAL/TEENSY31_32/HAL.h index 11b5564a17..5273b38637 100644 --- a/Marlin/src/HAL/TEENSY31_32/HAL.h +++ b/Marlin/src/HAL/TEENSY31_32/HAL.h @@ -50,12 +50,18 @@ #define IS_TEENSY32 1 #endif -#define _MSERIAL(X) Serial##X +#include "../../core/serial_hook.h" +typedef Serial0Type DefaultSerial; +extern DefaultSerial MSerial; +typedef ForwardSerial0Type 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 diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.cpp b/Marlin/src/HAL/TEENSY35_36/HAL.cpp index 92907353b8..547681de5f 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.cpp +++ b/Marlin/src/HAL/TEENSY35_36/HAL.cpp @@ -31,6 +31,9 @@ #include +DefaultSerial MSerial(false); +USBSerialType USBSerial(false, SerialUSB); + uint16_t HAL_adc_result, HAL_adc_select; static const uint8_t pin2sc1a[] = { diff --git a/Marlin/src/HAL/TEENSY35_36/HAL.h b/Marlin/src/HAL/TEENSY35_36/HAL.h index 1d66fa5184..94c514bf62 100644 --- a/Marlin/src/HAL/TEENSY35_36/HAL.h +++ b/Marlin/src/HAL/TEENSY35_36/HAL.h @@ -53,12 +53,18 @@ #define IS_TEENSY35 1 #endif -#define _MSERIAL(X) Serial##X +#include "../../core/serial_hook.h" +typedef Serial0Type DefaultSerial; +extern DefaultSerial MSerial; +typedef ForwardSerial0Type 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 diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.cpp b/Marlin/src/HAL/TEENSY40_41/HAL.cpp index 5b1b4272f5..26449d7eb2 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.cpp +++ b/Marlin/src/HAL/TEENSY40_41/HAL.cpp @@ -32,6 +32,9 @@ #include +DefaultSerial MSerial(false); +USBSerialType USBSerial(false, SerialUSB); + uint16_t HAL_adc_result, HAL_adc_select; static const uint8_t pin2sc1a[] = { diff --git a/Marlin/src/HAL/TEENSY40_41/HAL.h b/Marlin/src/HAL/TEENSY40_41/HAL.h index b191c7de5f..6aa1e521a4 100644 --- a/Marlin/src/HAL/TEENSY40_41/HAL.h +++ b/Marlin/src/HAL/TEENSY40_41/HAL.h @@ -37,6 +37,10 @@ #include #include +#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 DefaultSerial; +extern DefaultSerial MSerial; +typedef ForwardSerial0Type 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 diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 4b6c281de2..ac18128eec 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -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"); diff --git a/Marlin/src/core/macros.h b/Marlin/src/core/macros.h index 63ef597034..a0ccebc078 100644 --- a/Marlin/src/core/macros.h +++ b/Marlin/src/core/macros.h @@ -314,6 +314,32 @@ #endif + // C++11 solution that is standard compliant. is not available on all platform + namespace Private { + template struct enable_if { }; + template struct enable_if { 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 structure containing 'value' set to true if the member exists + #define HAS_MEMBER_IMPL(Member) \ + namespace Private { \ + template struct HasMember_ ## Member { \ + template static Yes& test( decltype(&C::Member) ) ; \ + template static No& test(...); \ + enum { value = sizeof(test(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 FORCE_INLINE typename enable_if::value, Return>::type Call_ ## Method(T * t, Args... a) { return static_cast(t->Method(a...)); } \ + _UNUSED static Return Call_ ## Method(...) { return __VA_ARGS__; } \ + } + #define CALL_IF_EXISTS(Return, That, Method, ...) \ + static_cast(Private::Call_ ## Method(That, ##__VA_ARGS__)) + #else #define MIN_2(a,b) ((a)<(b)?(a):(b)) diff --git a/Marlin/src/core/serial.cpp b/Marlin/src/core/serial.cpp index 5871c61f99..365f28ba55 100644 --- a/Marlin/src/core/serial.cpp +++ b/Marlin/src/core/serial.cpp @@ -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 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) { diff --git a/Marlin/src/core/serial.h b/Marlin/src/core/serial.h index ae1ef4169f..4c0c32f7d8 100644 --- a/Marlin/src/core/serial.h +++ b/Marlin/src/core/serial.h @@ -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 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(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 diff --git a/Marlin/src/core/serial_base.h b/Marlin/src/core/serial_base.h new file mode 100644 index 0000000000..b60e3b5788 --- /dev/null +++ b/Marlin/src/core/serial_base.h @@ -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 . + * + */ +#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 +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(this)->write(c); } + // Called when the parser finished processing an instruction, usually build to nothing + void msgDone() { static_cast(this)->msgDone(); } + // Called upon initialization + void begin(const long baudRate) { static_cast(this)->begin(baudRate); } + // Called upon destruction + void end() { static_cast(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(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(this)->read(index); } + // Check if the serial port is connected (usually bypassed) + bool connected() { return static_cast(this)->connected(); } + // Redirect flush + void flush() { static_cast(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(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 diff --git a/Marlin/src/core/serial_hook.h b/Marlin/src/core/serial_hook.h new file mode 100644 index 0000000000..17cf8bdd7d --- /dev/null +++ b/Marlin/src/core/serial_hook.h @@ -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 . + * + */ +#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 +struct BaseSerial : public SerialBase< BaseSerial >, public SerialT { + typedef SerialBase< BaseSerial > 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(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 + 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 +struct ConditionalSerial : public SerialBase< ConditionalSerial > { + typedef SerialBase< ConditionalSerial > 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 +struct ForwardSerial : public SerialBase< ForwardSerial > { + typedef SerialBase< ForwardSerial > 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::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 +struct RuntimeSerial : public SerialBase< RuntimeSerial >, public SerialT { + typedef SerialBase< RuntimeSerial > 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 + RuntimeSerial(const bool e, Args... args) : BaseClassT(e), SerialT(args...) {} +}; + +// A class that's duplicating its output conditionally to 2 serial interface +template +struct MultiSerial : public SerialBase< MultiSerial > { + typedef SerialBase< MultiSerial > 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 diff --git a/Marlin/src/feature/binary_stream.h b/Marlin/src/feature/binary_stream.h index 32ebcce2f6..81d6e7184b 100644 --- a/Marlin/src/feature/binary_stream.h +++ b/Marlin/src/feature/binary_stream.h @@ -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 diff --git a/Marlin/src/feature/host_actions.cpp b/Marlin/src/feature/host_actions.cpp index a8b2b51dfc..5ba3a3e3d2 100644 --- a/Marlin/src/feature/host_actions.cpp +++ b/Marlin/src/feature/host_actions.cpp @@ -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); diff --git a/Marlin/src/gcode/feature/network/M552-M554.cpp b/Marlin/src/gcode/feature/network/M552-M554.cpp index d88c38cb5a..6ea15fefbf 100644 --- a/Marlin/src/gcode/feature/network/M552-M554.cpp +++ b/Marlin/src/gcode/feature/network/M552-M554.cpp @@ -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(':'); } } diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index a29289d8d1..f9173188dc 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -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; diff --git a/Marlin/src/gcode/host/M118.cpp b/Marlin/src/gcode/host/M118.cpp index 7a77861e2b..73115d5c0a 100644 --- a/Marlin/src/gcode/host/M118.cpp +++ b/Marlin/src/gcode/host/M118.cpp @@ -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); } diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 1059997edb..4c42f7e353 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -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; diff --git a/Marlin/src/gcode/sd/M1001.cpp b/Marlin/src/gcode/sd/M1001.cpp index cba0e51af1..bd355d96ba 100644 --- a/Marlin/src/gcode/sd/M1001.cpp +++ b/Marlin/src/gcode/sd/M1001.cpp @@ -82,7 +82,7 @@ void GcodeSuite::M1001() { // Announce SD file completion { - PORT_REDIRECT(SERIAL_BOTH); + PORT_REDIRECT(SERIAL_ALL); SERIAL_ECHOLNPGM(STR_FILE_PRINTED); } diff --git a/Marlin/src/inc/Conditionals_LCD.h b/Marlin/src/inc/Conditionals_LCD.h index 2acda7bd59..712ed39cf1 100644 --- a/Marlin/src/inc/Conditionals_LCD.h +++ b/Marlin/src/inc/Conditionals_LCD.h @@ -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 diff --git a/Marlin/src/lcd/extui/malyan_lcd.cpp b/Marlin/src/lcd/extui/malyan_lcd.cpp index bdbf3802ab..6c55eea16d 100644 --- a/Marlin/src/lcd/extui/malyan_lcd.cpp +++ b/Marlin/src/lcd/extui/malyan_lcd.cpp @@ -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")); } } diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 589baf7796..139f6b9045 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -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(); } diff --git a/Marlin/src/sd/cardreader.cpp b/Marlin/src/sd/cardreader.cpp index 2e9101ff84..f8489549d6 100644 --- a/Marlin/src/sd/cardreader.cpp +++ b/Marlin/src/sd/cardreader.cpp @@ -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); diff --git a/Marlin/src/sd/cardreader.h b/Marlin/src/sd/cardreader.h index 272e213800..10beea5ec4 100644 --- a/Marlin/src/sd/cardreader.h +++ b/Marlin/src/sd/cardreader.h @@ -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; diff --git a/buildroot/tests/run_tests b/buildroot/tests/run_tests index a0eef6a05f..c4286f4695 100755 --- a/buildroot/tests/run_tests +++ b/buildroot/tests/run_tests @@ -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" diff --git a/docs/Serial.md b/docs/Serial.md new file mode 100644 index 0000000000..317f5f8c93 --- /dev/null +++ b/docs/Serial.md @@ -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, ConditionalSerial > 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)* \ No newline at end of file