Minor serial code cleanup
This commit is contained in:
parent
d4fb372899
commit
fd2477923c
|
@ -566,7 +566,7 @@ ISR(SERIAL_REGNAME(USART, SERIAL_PORT, _UDRE_vect)) {
|
|||
MarlinSerial<MarlinSerialCfg<SERIAL_PORT>>::_tx_udr_empty_irq();
|
||||
}
|
||||
|
||||
// Because of the template definition above, it's required to instantiate the template to have all method generated
|
||||
// Because of the template definition above, it's required to instantiate the template to have all methods generated
|
||||
template class MarlinSerial< MarlinSerialCfg<SERIAL_PORT> >;
|
||||
MSerialT customizedSerial1(MSerialT::HasEmergencyParser);
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ typedef int8_t serial_index_t;
|
|||
#define SERIAL_ALL 0x7F
|
||||
#if HAS_MULTI_SERIAL
|
||||
#define _PORT_REDIRECT(n,p) REMEMBER(n,multiSerial.portMask,p)
|
||||
#define _PORT_RESTORE(n,p) RESTORE(n)
|
||||
#define SERIAL_ASSERT(P) if(multiSerial.portMask!=(P)){ debugger(); }
|
||||
#ifdef SERIAL_CATCHALL
|
||||
typedef MultiSerial<decltype(MYSERIAL), decltype(SERIAL_CATCHALL), 0> SerialOutputT;
|
||||
|
@ -72,6 +73,7 @@ typedef int8_t serial_index_t;
|
|||
#define SERIAL_IMPL multiSerial
|
||||
#else
|
||||
#define _PORT_REDIRECT(n,p) NOOP
|
||||
#define _PORT_RESTORE(n) NOOP
|
||||
#define SERIAL_ASSERT(P) NOOP
|
||||
#define SERIAL_IMPL MYSERIAL0
|
||||
#endif
|
||||
|
@ -79,6 +81,7 @@ typedef int8_t serial_index_t;
|
|||
#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)
|
||||
|
||||
//
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
#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(void, flushTX);
|
||||
CALL_IF_EXISTS_IMPL(bool, connected, true);
|
||||
|
||||
// In order to catch usage errors in code, we make the base to encode number explicit
|
||||
|
@ -42,14 +42,14 @@ enum class PrintBase {
|
|||
Bin = 2
|
||||
};
|
||||
|
||||
// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than
|
||||
// A simple forward struct that prevent the compiler to select print(double, int) as a default overload for any type different than
|
||||
// double or float. For double or float, a conversion exists so the call will be transparent
|
||||
struct EnsureDouble {
|
||||
double a;
|
||||
FORCE_INLINE operator double() { return a; }
|
||||
// If the compiler breaks on ambiguity here, it's likely because you're calling print(X, base) with X not a double or a float, and a
|
||||
// base that's not one of PrintBase's value. This exact code is made to detect such error, you NEED to set a base explicitely like this:
|
||||
// SERIAL_PRINT(v, PrintBase::Hex)
|
||||
// SERIAL_PRINT(v, PrintBase::Hex)
|
||||
FORCE_INLINE EnsureDouble(double a) : a(a) {}
|
||||
FORCE_INLINE EnsureDouble(float a) : a(a) {}
|
||||
};
|
||||
|
@ -147,13 +147,13 @@ struct SerialBase {
|
|||
else write('0');
|
||||
}
|
||||
void printNumber(signed long n, const uint8_t base) {
|
||||
if (base == 10 && n < 0) {
|
||||
if (base == 10 && n < 0) {
|
||||
n = -n; // This works because all platforms Marlin's builds on are using 2-complement encoding for negative number
|
||||
// On such CPU, changing the sign of a number is done by inverting the bits and adding one, so if n = 0x80000000 = -2147483648 then
|
||||
// -n = 0x7FFFFFFF + 1 => 0x80000000 = 2147483648 (if interpreted as unsigned) or -2147483648 if interpreted as signed.
|
||||
// On non 2-complement CPU, there would be no possible representation for 2147483648.
|
||||
write('-');
|
||||
}
|
||||
write('-');
|
||||
}
|
||||
printNumber((unsigned long)n , base);
|
||||
}
|
||||
|
||||
|
|
|
@ -53,14 +53,10 @@ void GcodeSuite::M118() {
|
|||
}
|
||||
|
||||
#if HAS_MULTI_SERIAL
|
||||
const int8_t old_serial = multiSerial.portMask;
|
||||
if (WITHIN(port, 0, NUM_SERIAL))
|
||||
multiSerial.portMask = port ? _BV(port - 1) : SERIAL_ALL;
|
||||
PORT_REDIRECT(WITHIN(port, 0, NUM_SERIAL) ? (port ? _BV(port - 1) : SERIAL_ALL) : multiSerial.portMask);
|
||||
#endif
|
||||
|
||||
if (hasE) SERIAL_ECHO_START();
|
||||
if (hasA) SERIAL_ECHOPGM("//");
|
||||
SERIAL_ECHOLN(p);
|
||||
|
||||
TERN_(HAS_MULTI_SERIAL, multiSerial.portMask = old_serial);
|
||||
}
|
||||
|
|
|
@ -42,8 +42,9 @@ struct AutoReporter {
|
|||
const millis_t ms = millis();
|
||||
if (ELAPSED(ms, next_report_ms)) {
|
||||
next_report_ms = ms + SEC_TO_MS(report_interval);
|
||||
TERN_(HAS_MULTI_SERIAL, PORT_REDIRECT(report_port_mask));
|
||||
PORT_REDIRECT(report_port_mask);
|
||||
Helper::report();
|
||||
//PORT_RESTORE();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
# 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.
|
||||
Marlin is targeting a plethora 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
|
||||
|
@ -16,7 +15,7 @@ Any implementation will need to follow this interface for being used transparent
|
|||
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.
|
||||
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 the `CALL_IF_EXISTS` macro in `Marlin/src/core/macros.h` for documentation of this technique.
|
||||
|
||||
## Composing the desired feature
|
||||
The different specificities for each architecture are provided by composing the serial type based on desired functionality.
|
||||
|
@ -32,7 +31,7 @@ Since all the types above are using CRTP, it's possible to combine them to get t
|
|||
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:
|
||||
```
|
||||
```cpp
|
||||
typedef MultiSerial< RuntimeSerial<Serial>, ConditionalSerial<TelnetClient> > Serial0Type;
|
||||
```
|
||||
|
||||
|
|
Loading…
Reference in a new issue