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