Skip to content

Commit

Permalink
Minor serial code cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkyhead committed Feb 13, 2021
1 parent d4fb372 commit fd24779
Show file tree
Hide file tree
Showing 6 changed files with 16 additions and 17 deletions.
2 changes: 1 addition & 1 deletion Marlin/src/HAL/AVR/MarlinSerial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions Marlin/src/core/serial.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -72,13 +73,15 @@ 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

#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)

//
Expand Down
12 changes: 6 additions & 6 deletions Marlin/src/core/serial_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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) {}
};
Expand Down Expand Up @@ -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);
}

Expand Down
6 changes: 1 addition & 5 deletions Marlin/src/gcode/host/M118.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
3 changes: 2 additions & 1 deletion Marlin/src/libs/autoreport.h
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
};
7 changes: 3 additions & 4 deletions docs/Serial.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.
Expand All @@ -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;
```

Expand Down

0 comments on commit fd24779

Please sign in to comment.