Minor serial code cleanup
This commit is contained in:
		
				
					committed by
					
						 Scott Lahteine
						Scott Lahteine
					
				
			
			
				
	
			
			
			
						parent
						
							92b5f06bf9
						
					
				
				
					commit
					8bca8e5ba0
				
			| @@ -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; | ||||
| ``` | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user