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