From 7310110ec0fa49fd014506cc1d0672c62cbbeb30 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 1 Apr 2017 22:29:35 -0500 Subject: [PATCH 1/8] Fix logic of UBL::fade_scaling_factor_for_z --- Marlin/Marlin_main.cpp | 4 ++-- Marlin/UBL.h | 29 +++++++++++------------------ Marlin/UBL_Bed_Leveling.cpp | 7 +++---- Marlin/UBL_G29.cpp | 3 +-- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 7d4a95506..322448e01 100755 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -10175,6 +10175,8 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { /** * Prepare a linear move in a Cartesian setup. * If Mesh Bed Leveling is enabled, perform a mesh move. + * + * Returns true if the caller didn't update current_position. */ inline bool prepare_move_to_destination_cartesian() { // Do not use feedrate_percentage for E or Z only moves @@ -10190,9 +10192,7 @@ void set_current_from_steppers_for_axis(const AxisEnum axis) { else #elif ENABLED(AUTO_BED_LEVELING_UBL) if (ubl.state.active) { - ubl_line_to_destination(MMS_SCALED(feedrate_mm_s), active_extruder); - return false; } else diff --git a/Marlin/UBL.h b/Marlin/UBL.h index ea726b457..f96d23e66 100644 --- a/Marlin/UBL.h +++ b/Marlin/UBL.h @@ -98,9 +98,6 @@ float g29_correction_fade_height = 10.0, g29_fade_height_multiplier = 1.0 / 10.0; // It's cheaper to do a floating point multiply than divide, // so keep this value and its reciprocal. - #else - const float g29_correction_fade_height = 10.0, - g29_fade_height_multiplier = 1.0 / 10.0; #endif // If you change this struct, adjust TOTAL_STRUCT_SIZE @@ -118,8 +115,7 @@ class unified_bed_leveling { private: - static float last_specified_z, - fade_scaling_factor_for_current_height; + static float last_specified_z; public: @@ -307,32 +303,29 @@ } /** - * This routine is used to scale the Z correction depending upon the current nozzle height. It is - * optimized for speed. It avoids floating point operations by checking if the requested scaling - * factor is going to be the same as the last time the function calculated a value. If so, it just - * returns it. + * This function sets the Z leveling fade factor based on the given Z height, + * only re-calculating when necessary. * - * It returns a scaling factor of 1.0 if UBL is inactive. - * It returns a scaling factor of 0.0 if Z is past the specified 'Fade Height' + * Returns 1.0 if g29_correction_fade_height is 0.0. + * Returns 0.0 if Z is past the specified 'Fade Height'. */ #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) static FORCE_INLINE float fade_scaling_factor_for_z(const float &lz) { + if (state.g29_correction_fade_height == 0.0) return 1.0; + + static float fade_scaling_factor = 1.0; const float rz = RAW_Z_POSITION(lz); if (last_specified_z != rz) { last_specified_z = rz; - fade_scaling_factor_for_current_height = - state.active && rz < state.g29_correction_fade_height + fade_scaling_factor = + rz < state.g29_correction_fade_height ? 1.0 - (rz * state.g29_fade_height_multiplier) : 0.0; } - return fade_scaling_factor_for_current_height; + return fade_scaling_factor; } - #else - - static constexpr float fade_scaling_factor_for_z(const float &lz) { UNUSED(lz); return 1.0; } - #endif }; // class unified_bed_leveling diff --git a/Marlin/UBL_Bed_Leveling.cpp b/Marlin/UBL_Bed_Leveling.cpp index e173247fe..414099b76 100644 --- a/Marlin/UBL_Bed_Leveling.cpp +++ b/Marlin/UBL_Bed_Leveling.cpp @@ -61,7 +61,6 @@ float unified_bed_leveling::z_values[UBL_MESH_NUM_X_POINTS][UBL_MESH_NUM_Y_POINTS], unified_bed_leveling::last_specified_z, - unified_bed_leveling::fade_scaling_factor_for_current_height, unified_bed_leveling::mesh_index_to_xpos[UBL_MESH_NUM_X_POINTS + 1], // +1 safety margin for now, until determinism prevails unified_bed_leveling::mesh_index_to_ypos[UBL_MESH_NUM_Y_POINTS + 1]; @@ -102,8 +101,9 @@ * updated, but until then, we try to ease the transition * for our Beta testers. */ - if (ubl.state.g29_fade_height_multiplier != 1.0 / ubl.state.g29_correction_fade_height) { - ubl.state.g29_fade_height_multiplier = 1.0 / ubl.state.g29_correction_fade_height; + const float recip = ubl.state.g29_correction_fade_height ? 1.0 / ubl.state.g29_correction_fade_height : 1.0; + if (ubl.state.g29_fade_height_multiplier != recip) { + ubl.state.g29_fade_height_multiplier = recip; store_state(); } #endif @@ -160,7 +160,6 @@ ZERO(z_values); last_specified_z = -999.9; - fade_scaling_factor_for_current_height = 0.0; } void unified_bed_leveling::invalidate() { diff --git a/Marlin/UBL_G29.cpp b/Marlin/UBL_G29.cpp index 9b7ccadd9..9512f61b2 100644 --- a/Marlin/UBL_G29.cpp +++ b/Marlin/UBL_G29.cpp @@ -1132,8 +1132,7 @@ safe_delay(50); #if ENABLED(ENABLE_LEVELING_FADE_HEIGHT) - SERIAL_PROTOCOLPAIR("g29_correction_fade_height : ", ubl.state.g29_correction_fade_height); - SERIAL_EOL; + SERIAL_PROTOCOLLNPAIR("g29_correction_fade_height : ", ubl.state.g29_correction_fade_height); #endif SERIAL_PROTOCOLPGM("z_offset: "); From 790a3e9a8cc5a97d82a4dcda9c637256368fb108 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 1 Apr 2017 23:03:40 -0500 Subject: [PATCH 2/8] Remove extraneous check of UBL_G26_MESH_EDITING --- Marlin/Marlin_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 322448e01..4741afcf8 100755 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -8903,7 +8903,7 @@ void process_next_command() { gcode_G28(); break; - #if PLANNER_LEVELING && !ENABLED(AUTO_BED_LEVELING_UBL) || ENABLED(AUTO_BED_LEVELING_UBL) && ENABLED(UBL_G26_MESH_EDITING) + #if PLANNER_LEVELING || ENABLED(AUTO_BED_LEVELING_UBL) case 29: // G29 Detailed Z probe, probes the bed at 3 or more points, // or provides access to the UBL System if enabled. gcode_G29(); From e19fbd27cebb2f9c421abe916e1f82d18702da8e Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 1 Apr 2017 23:07:14 -0500 Subject: [PATCH 3/8] Minor spelling patch --- Marlin/G26_Mesh_Validation_Tool.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/G26_Mesh_Validation_Tool.cpp b/Marlin/G26_Mesh_Validation_Tool.cpp index b67b080ce..6f22afeaa 100644 --- a/Marlin/G26_Mesh_Validation_Tool.cpp +++ b/Marlin/G26_Mesh_Validation_Tool.cpp @@ -183,8 +183,8 @@ int i, xi, yi; mesh_index_pair location; - // Don't allow Mesh Validation without homing first - // If the paramter parsing did not go OK, we abort the command + // Don't allow Mesh Validation without homing first, + // or if the parameter parsing did not go OK, abort if (axis_unhomed_error(true, true, true) || parse_G26_parameters()) return; if (current_position[Z_AXIS] < Z_CLEARANCE_BETWEEN_PROBES) { From 34e2420b9bd8531961fcf2d8bf08023df8224a66 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 1 Apr 2017 23:15:41 -0500 Subject: [PATCH 4/8] Apply const args, clean up find_closest_circle_to_print --- Marlin/G26_Mesh_Validation_Tool.cpp | 31 ++++++++++++++--------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/Marlin/G26_Mesh_Validation_Tool.cpp b/Marlin/G26_Mesh_Validation_Tool.cpp index 6f22afeaa..fc07275c1 100644 --- a/Marlin/G26_Mesh_Validation_Tool.cpp +++ b/Marlin/G26_Mesh_Validation_Tool.cpp @@ -156,7 +156,7 @@ // won't leave us in a bad state. float valid_trig_angle(float); - mesh_index_pair find_closest_circle_to_print(float, float); + mesh_index_pair find_closest_circle_to_print(const float&, const float&); static float extrusion_multiplier = EXTRUSION_MULTIPLIER, retraction_multiplier = RETRACTION_MULTIPLIER, @@ -391,8 +391,8 @@ return d; } - mesh_index_pair find_closest_circle_to_print( float X, float Y) { - float f, mx, my, dx, dy, closest = 99999.99; + mesh_index_pair find_closest_circle_to_print(const float &X, const float &Y) { + float closest = 99999.99; mesh_index_pair return_val; return_val.x_index = return_val.y_index = -1; @@ -400,28 +400,27 @@ for (uint8_t i = 0; i < UBL_MESH_NUM_X_POINTS; i++) { for (uint8_t j = 0; j < UBL_MESH_NUM_Y_POINTS; j++) { if (!is_bit_set(circle_flags, i, j)) { - mx = ubl.mesh_index_to_xpos[i]; // We found a circle that needs to be printed - my = ubl.mesh_index_to_ypos[j]; + const float mx = ubl.mesh_index_to_xpos[i], // We found a circle that needs to be printed + my = ubl.mesh_index_to_ypos[j]; - dx = X - mx; // Get the distance to this intersection - dy = Y - my; - f = HYPOT(dx, dy); + // Get the distance to this intersection + float f = HYPOT(X - mx, Y - my); - dx = x_pos - mx; // It is possible that we are being called with the values - dy = y_pos - my; // to let us find the closest circle to the start position. - f += HYPOT(dx, dy) / 15.0; // But if this is not the case, - // we are going to add in a small - // weighting to the distance calculation to help it choose - // a better place to continue. + // It is possible that we are being called with the values + // to let us find the closest circle to the start position. + // But if this is not the case, add a small weighting to the + // distance calculation to help it choose a better place to continue. + f += HYPOT(x_pos - mx, y_pos - my) / 15.0; + // Add in the specified amount of Random Noise to our search if (random_deviation > 1.0) - f += random(0.0, random_deviation); // Add in the specified amount of Random Noise to our search + f += random(0.0, random_deviation); if (f < closest) { closest = f; // We found a closer location that is still return_val.x_index = i; // un-printed --- save the data for it return_val.y_index = j; - return_val.distance= closest; + return_val.distance = closest; } } } From 0e6ec34228d3f2775952fd0a21b1e3ff8a110159 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 2 Apr 2017 00:45:39 -0500 Subject: [PATCH 5/8] Use PSTR versions of lcd_status... to save SRAM --- Marlin/G26_Mesh_Validation_Tool.cpp | 2 +- Marlin/UBL.h | 1 + Marlin/UBL_G29.cpp | 24 +++++++++++++----------- Marlin/ultralcd.cpp | 8 ++++---- 4 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Marlin/G26_Mesh_Validation_Tool.cpp b/Marlin/G26_Mesh_Validation_Tool.cpp index fc07275c1..d00369d57 100644 --- a/Marlin/G26_Mesh_Validation_Tool.cpp +++ b/Marlin/G26_Mesh_Validation_Tool.cpp @@ -128,7 +128,7 @@ extern bool code_value_bool(); extern bool code_has_value(); extern void lcd_init(); - extern void lcd_setstatuspgm(const char* const message, uint8_t level); + extern void lcd_setstatuspgm(const char* const message, const uint8_t level); #define PLANNER_XY_FEEDRATE() (min(planner.max_feedrate_mm_s[X_AXIS], planner.max_feedrate_mm_s[Y_AXIS])) //bob bool prepare_move_to_destination_cartesian(); void line_to_destination(); diff --git a/Marlin/UBL.h b/Marlin/UBL.h index f96d23e66..c62e05a31 100644 --- a/Marlin/UBL.h +++ b/Marlin/UBL.h @@ -29,6 +29,7 @@ #if ENABLED(AUTO_BED_LEVELING_UBL) + #define UBL_VERSION "1.00" #define UBL_OK false #define UBL_ERR true diff --git a/Marlin/UBL_G29.cpp b/Marlin/UBL_G29.cpp index 9512f61b2..3d877cc8d 100644 --- a/Marlin/UBL_G29.cpp +++ b/Marlin/UBL_G29.cpp @@ -307,7 +307,8 @@ static float x_pos, y_pos, measured_z, card_thickness = 0.0, ubl_constant = 0.0; #if ENABLED(ULTRA_LCD) - void lcd_setstatus(const char* message, bool persist); + extern void lcd_setstatus(const char* message, const bool persist); + extern void lcd_setstatuspgm(const char* message, const uint8_t level); #endif void gcode_G29() { @@ -655,7 +656,7 @@ if (ELAPSED(millis(), nxt)) { SERIAL_PROTOCOLLNPGM("\nZ-Offset Adjustment Stopped."); do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE); - lcd_setstatus("Z-Offset Stopped", true); + lcd_setstatuspgm("Z-Offset Stopped"); restore_ubl_active_state_and_leave(); goto LEAVE; } @@ -673,7 +674,8 @@ LEAVE: #if ENABLED(ULTRA_LCD) - lcd_setstatus(" ", true); + lcd_reset_alert_level(); + lcd_setstatuspgm(""); lcd_quick_feedback(); #endif @@ -977,7 +979,7 @@ bool g29_parameter_parsing() { #if ENABLED(ULTRA_LCD) - lcd_setstatus("Doing G29 UBL !", true); + lcd_setstatuspgm("Doing G29 UBL!"); lcd_quick_feedback(); #endif @@ -1088,7 +1090,7 @@ ubl_state_recursion_chk++; if (ubl_state_recursion_chk != 1) { SERIAL_ECHOLNPGM("save_ubl_active_state_and_disabled() called multiple times in a row."); - lcd_setstatus("save_UBL_active() error", true); + lcd_setstatuspgm("save_UBL_active() error"); lcd_quick_feedback(); return; } @@ -1099,7 +1101,7 @@ void restore_ubl_active_state_and_leave() { if (--ubl_state_recursion_chk) { SERIAL_ECHOLNPGM("restore_ubl_active_state_and_leave() called too many times."); - lcd_setstatus("restore_UBL_active() error", true); + lcd_setstatuspgm("restore_UBL_active() error"); lcd_quick_feedback(); return; } @@ -1114,7 +1116,7 @@ void g29_what_command() { const uint16_t k = E2END - ubl.eeprom_start; - SERIAL_PROTOCOLPGM("Unified Bed Leveling System Version 1.00 "); + SERIAL_PROTOCOLPGM("Unified Bed Leveling System Version " UBL_VERSION " "); if (ubl.state.active) SERIAL_PROTOCOLCHAR('A'); else @@ -1339,7 +1341,7 @@ memset(not_done, 0xFF, sizeof(not_done)); #if ENABLED(ULTRA_LCD) - lcd_setstatus("Fine Tuning Mesh.", true); + lcd_setstatuspgm("Fine Tuning Mesh"); #endif do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE); @@ -1398,7 +1400,7 @@ lcd_return_to_status(); //SERIAL_PROTOCOLLNPGM("\nFine Tuning of Mesh Stopped."); do_blocking_move_to_z(Z_CLEARANCE_DEPLOY_PROBE); - lcd_setstatus("Mesh Editing Stopped", true); + lcd_setstatuspgm("Mesh Editing Stopped"); while (ubl_lcd_clicked()) idle(); @@ -1426,9 +1428,9 @@ do_blocking_move_to_xy(lx, ly); #if ENABLED(ULTRA_LCD) - lcd_setstatus("Done Editing Mesh", true); + lcd_setstatuspgm("Done Editing Mesh"); #endif - SERIAL_ECHOLNPGM("Done Editing Mesh."); + SERIAL_ECHOLNPGM("Done Editing Mesh"); } #endif // AUTO_BED_LEVELING_UBL \ No newline at end of file diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index f40742442..d41281c92 100755 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -677,7 +677,7 @@ void kill_screen(const char* lcd_msg) { thermalManager.autotempShutdown(); #endif wait_for_heatup = false; - lcd_setstatus(MSG_PRINT_ABORTED, true); + lcd_setstatuspgm(PSTR(MSG_PRINT_ABORTED), true); } #endif // SDSUPPORT @@ -3552,13 +3552,13 @@ void lcd_finishstatus(bool persist=false) { bool lcd_hasstatus() { return (lcd_status_message[0] != '\0'); } -void lcd_setstatus(const char* const message, bool persist) { +void lcd_setstatus(const char * const message, const bool persist) { if (lcd_status_message_level > 0) return; strncpy(lcd_status_message, message, 3 * (LCD_WIDTH)); lcd_finishstatus(persist); } -void lcd_setstatuspgm(const char* const message, uint8_t level) { +void lcd_setstatuspgm(const char * const message, const uint8_t level) { if (level < lcd_status_message_level) return; lcd_status_message_level = level; strncpy_P(lcd_status_message, message, 3 * (LCD_WIDTH)); @@ -3575,7 +3575,7 @@ void status_printf(uint8_t level, const char *status, ...) { lcd_finishstatus(level > 0); } -void lcd_setalertstatuspgm(const char* const message) { +void lcd_setalertstatuspgm(const char * const message) { lcd_setstatuspgm(message, 1); #if ENABLED(ULTIPANEL) lcd_return_to_status(); From 7a7a80e6c5faf3fe87f6c0732c617e5ddd2f5cbf Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 2 Apr 2017 00:46:37 -0500 Subject: [PATCH 6/8] status_printf => lcd_status_printf_P --- Marlin/Marlin_main.cpp | 14 ++++---------- Marlin/endstops.cpp | 4 +--- Marlin/ultralcd.cpp | 6 +++--- Marlin/ultralcd.h | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 4741afcf8..27ef4f610 100755 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -1789,15 +1789,10 @@ static void clean_up_after_endstop_or_probe_move() { SERIAL_ECHOLNPGM(" " MSG_FIRST); #if ENABLED(ULTRA_LCD) - char message[3 * (LCD_WIDTH) + 1] = ""; // worst case is kana.utf with up to 3*LCD_WIDTH+1 - strcat_P(message, PSTR(MSG_HOME " ")); - if (xx) strcat_P(message, PSTR(MSG_X)); - if (yy) strcat_P(message, PSTR(MSG_Y)); - if (zz) strcat_P(message, PSTR(MSG_Z)); - strcat_P(message, PSTR(" " MSG_FIRST)); - lcd_setstatus(message); + lcd_status_printf_P(0, PSTR(MSG_HOME " %s%s%s " MSG_FIRST), xx ? MSG_X : "", yy ? MSG_Y : "", zz ? MSG_Z : ""); #endif return true; + } return false; } @@ -5153,7 +5148,6 @@ inline void gcode_M31() { char buffer[21]; duration_t elapsed = print_job_timer.duration(); elapsed.toString(buffer); - lcd_setstatus(buffer); SERIAL_ECHO_START; @@ -5700,7 +5694,7 @@ inline void gcode_M104() { } #endif - if (code_value_temp_abs() > thermalManager.degHotend(target_extruder)) status_printf(0, PSTR("E%i %s"), target_extruder + 1, MSG_HEATING); + if (code_value_temp_abs() > thermalManager.degHotend(target_extruder)) lcd_status_printf_P(0, PSTR("E%i %s"), target_extruder + 1, MSG_HEATING); } #if ENABLED(AUTOTEMP) @@ -5898,7 +5892,7 @@ inline void gcode_M109() { else print_job_timer.start(); #endif - if (thermalManager.isHeatingHotend(target_extruder)) status_printf(0, PSTR("E%i %s"), target_extruder + 1, MSG_HEATING); + if (thermalManager.isHeatingHotend(target_extruder)) lcd_status_printf_P(0, PSTR("E%i %s"), target_extruder + 1, MSG_HEATING); } #if ENABLED(AUTOTEMP) diff --git a/Marlin/endstops.cpp b/Marlin/endstops.cpp index 54a512787..a80a07dc1 100644 --- a/Marlin/endstops.cpp +++ b/Marlin/endstops.cpp @@ -170,9 +170,7 @@ void Endstops::report_state() { SERIAL_EOL; #if ENABLED(ULTRA_LCD) - char msg[3 * strlen(MSG_LCD_ENDSTOPS) + 8 + 1]; // Room for a UTF 8 string - sprintf_P(msg, PSTR(MSG_LCD_ENDSTOPS " %c %c %c %c"), chrX, chrY, chrZ, chrP); - lcd_setstatus(msg); + lcd_status_printf_P(0, PSTR(MSG_LCD_ENDSTOPS " %c %c %c %c"), chrX, chrY, chrZ, chrP); #endif hit_on_purpose(); diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index d41281c92..5c36b5724 100755 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -3565,12 +3565,12 @@ void lcd_setstatuspgm(const char * const message, const uint8_t level) { lcd_finishstatus(level > 0); } -void status_printf(uint8_t level, const char *status, ...) { +void lcd_status_printf_P(const uint8_t level, const char * const fmt, ...) { if (level < lcd_status_message_level) return; lcd_status_message_level = level; va_list args; - va_start(args, status); - vsnprintf_P(lcd_status_message, 3 * (LCD_WIDTH), status, args); + va_start(args, fmt); + vsnprintf_P(lcd_status_message, 3 * (LCD_WIDTH), fmt, args); va_end(args); lcd_finishstatus(level > 0); } diff --git a/Marlin/ultralcd.h b/Marlin/ultralcd.h index 071323157..0b32c09b6 100644 --- a/Marlin/ultralcd.h +++ b/Marlin/ultralcd.h @@ -39,7 +39,7 @@ bool lcd_hasstatus(); void lcd_setstatus(const char* message, const bool persist=false); void lcd_setstatuspgm(const char* message, const uint8_t level=0); - void status_printf(uint8_t level, const char *Status, ...); + void lcd_status_printf_P(const uint8_t level, const char * const fmt, ...); void lcd_setalertstatuspgm(const char* message); void lcd_reset_alert_level(); void lcd_kill_screen(); @@ -154,7 +154,7 @@ inline bool lcd_hasstatus() { return false; } inline void lcd_setstatus(const char* const message, const bool persist=false) { UNUSED(message); UNUSED(persist); } inline void lcd_setstatuspgm(const char* const message, const uint8_t level=0) { UNUSED(message); UNUSED(level); } - inline void status_printf(uint8_t level, const char *status, ...) { UNUSED(level); UNUSED(status); } + inline void lcd_status_printf_P(const uint8_t level, const char * const fmt, ...) { UNUSED(level); UNUSED(fmt); } inline void lcd_buttons_update() {} inline void lcd_reset_alert_level() {} inline bool lcd_detected() { return true; } From eaa66f3c464f2e09bb093e5f0285ced971b48724 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 2 Apr 2017 00:47:20 -0500 Subject: [PATCH 7/8] Indent MarlinSerial code --- Marlin/MarlinSerial.cpp | 831 ++++++++++++++++++++-------------------- Marlin/MarlinSerial.h | 197 +++++----- 2 files changed, 508 insertions(+), 520 deletions(-) diff --git a/Marlin/MarlinSerial.cpp b/Marlin/MarlinSerial.cpp index 38e1b51cf..2004d3657 100644 --- a/Marlin/MarlinSerial.cpp +++ b/Marlin/MarlinSerial.cpp @@ -33,495 +33,490 @@ #include "stepper.h" #include "Marlin.h" -#ifndef USBCON -// this next line disables the entire HardwareSerial.cpp, -// this is so I can support Attiny series and any other chip without a UART -#if defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H) +// Disable HardwareSerial.cpp to support chips without a UART (Attiny, etc.) -#if UART_PRESENT(SERIAL_PORT) - ring_buffer_r rx_buffer = { { 0 }, 0, 0 }; - #if TX_BUFFER_SIZE > 0 - ring_buffer_t tx_buffer = { { 0 }, 0, 0 }; - static bool _written; +#if !defined(USBCON) && (defined(UBRRH) || defined(UBRR0H) || defined(UBRR1H) || defined(UBRR2H) || defined(UBRR3H)) + + #if UART_PRESENT(SERIAL_PORT) + ring_buffer_r rx_buffer = { { 0 }, 0, 0 }; + #if TX_BUFFER_SIZE > 0 + ring_buffer_t tx_buffer = { { 0 }, 0, 0 }; + static bool _written; + #endif #endif -#endif + #if ENABLED(EMERGENCY_PARSER) + + #include "language.h" + + // Currently looking for: M108, M112, M410 + // If you alter the parser please don't forget to update the capabilities in Conditionals_post.h + + FORCE_INLINE void emergency_parser(const unsigned char c) { + + static e_parser_state state = state_RESET; -FORCE_INLINE void store_char(unsigned char c) { - CRITICAL_SECTION_START; - uint8_t h = rx_buffer.head; - uint8_t i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1); + switch (state) { + case state_RESET: + switch (c) { + case ' ': break; + case 'N': state = state_N; break; + case 'M': state = state_M; break; + default: state = state_IGNORE; + } + break; + + case state_N: + switch (c) { + case '0': case '1': case '2': + case '3': case '4': case '5': + case '6': case '7': case '8': + case '9': case '-': case ' ': break; + case 'M': state = state_M; break; + default: state = state_IGNORE; + } + break; + + case state_M: + switch (c) { + case ' ': break; + case '1': state = state_M1; break; + case '4': state = state_M4; break; + default: state = state_IGNORE; + } + break; - // if we should be storing the received character into the location - // just before the tail (meaning that the head would advance to the - // current location of the tail), we're about to overflow the buffer - // and so we don't write the character or advance the head. - if (i != rx_buffer.tail) { - rx_buffer.buffer[h] = c; - rx_buffer.head = i; + case state_M1: + switch (c) { + case '0': state = state_M10; break; + case '1': state = state_M11; break; + default: state = state_IGNORE; + } + break; + + case state_M10: + state = (c == '8') ? state_M108 : state_IGNORE; + break; + + case state_M11: + state = (c == '2') ? state_M112 : state_IGNORE; + break; + + case state_M4: + state = (c == '1') ? state_M41 : state_IGNORE; + break; + + case state_M41: + state = (c == '0') ? state_M410 : state_IGNORE; + break; + + case state_IGNORE: + if (c == '\n') state = state_RESET; + break; + + default: + if (c == '\n') { + switch (state) { + case state_M108: + wait_for_user = wait_for_heatup = false; + break; + case state_M112: + kill(PSTR(MSG_KILLED)); + break; + case state_M410: + quickstop_stepper(); + break; + default: + break; + } + state = state_RESET; + } + } } - CRITICAL_SECTION_END; - #if ENABLED(EMERGENCY_PARSER) - emergency_parser(c); #endif -} -#if TX_BUFFER_SIZE > 0 - FORCE_INLINE void _tx_udr_empty_irq(void) { - // If interrupts are enabled, there must be more data in the output - // buffer. Send the next byte - uint8_t t = tx_buffer.tail; - uint8_t c = tx_buffer.buffer[t]; - tx_buffer.tail = (t + 1) & (TX_BUFFER_SIZE - 1); + FORCE_INLINE void store_char(unsigned char c) { + CRITICAL_SECTION_START; + uint8_t h = rx_buffer.head; + uint8_t i = (uint8_t)(h + 1) & (RX_BUFFER_SIZE - 1); + + // if we should be storing the received character into the location + // just before the tail (meaning that the head would advance to the + // current location of the tail), we're about to overflow the buffer + // and so we don't write the character or advance the head. + if (i != rx_buffer.tail) { + rx_buffer.buffer[h] = c; + rx_buffer.head = i; + } + CRITICAL_SECTION_END; - M_UDRx = c; + #if ENABLED(EMERGENCY_PARSER) + emergency_parser(c); + #endif + } - // clear the TXC bit -- "can be cleared by writing a one to its bit - // location". This makes sure flush() won't return until the bytes - // actually got written - SBI(M_UCSRxA, M_TXCx); + #if TX_BUFFER_SIZE > 0 - if (tx_buffer.head == tx_buffer.tail) { - // Buffer empty, so disable interrupts - CBI(M_UCSRxB, M_UDRIEx); - } - } + FORCE_INLINE void _tx_udr_empty_irq(void) { + // If interrupts are enabled, there must be more data in the output + // buffer. Send the next byte + uint8_t t = tx_buffer.tail; + uint8_t c = tx_buffer.buffer[t]; + tx_buffer.tail = (t + 1) & (TX_BUFFER_SIZE - 1); - #ifdef M_USARTx_UDRE_vect - ISR(M_USARTx_UDRE_vect) { - _tx_udr_empty_irq(); + M_UDRx = c; + + // clear the TXC bit -- "can be cleared by writing a one to its bit + // location". This makes sure flush() won't return until the bytes + // actually got written + SBI(M_UCSRxA, M_TXCx); + + if (tx_buffer.head == tx_buffer.tail) { + // Buffer empty, so disable interrupts + CBI(M_UCSRxB, M_UDRIEx); + } } - #endif -#endif // TX_BUFFER_SIZE + #ifdef M_USARTx_UDRE_vect + ISR(M_USARTx_UDRE_vect) { + _tx_udr_empty_irq(); + } + #endif -#ifdef M_USARTx_RX_vect - ISR(M_USARTx_RX_vect) { - unsigned char c = M_UDRx; - store_char(c); - } -#endif + #endif // TX_BUFFER_SIZE -// Constructors //////////////////////////////////////////////////////////////// + #ifdef M_USARTx_RX_vect + ISR(M_USARTx_RX_vect) { + unsigned char c = M_UDRx; + store_char(c); + } + #endif -MarlinSerial::MarlinSerial() { } + // Public Methods -// Public Methods ////////////////////////////////////////////////////////////// + void MarlinSerial::begin(long baud) { + uint16_t baud_setting; + bool useU2X = true; -void MarlinSerial::begin(long baud) { - uint16_t baud_setting; - bool useU2X = true; + #if F_CPU == 16000000UL && SERIAL_PORT == 0 + // hard-coded exception for compatibility with the bootloader shipped + // with the Duemilanove and previous boards and the firmware on the 8U2 + // on the Uno and Mega 2560. + if (baud == 57600) { + useU2X = false; + } + #endif - #if F_CPU == 16000000UL && SERIAL_PORT == 0 - // hard-coded exception for compatibility with the bootloader shipped - // with the Duemilanove and previous boards and the firmware on the 8U2 - // on the Uno and Mega 2560. - if (baud == 57600) { - useU2X = false; + if (useU2X) { + M_UCSRxA = _BV(M_U2Xx); + baud_setting = (F_CPU / 4 / baud - 1) / 2; + } + else { + M_UCSRxA = 0; + baud_setting = (F_CPU / 8 / baud - 1) / 2; } - #endif - if (useU2X) { - M_UCSRxA = _BV(M_U2Xx); - baud_setting = (F_CPU / 4 / baud - 1) / 2; - } - else { - M_UCSRxA = 0; - baud_setting = (F_CPU / 8 / baud - 1) / 2; - } + // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) + M_UBRRxH = baud_setting >> 8; + M_UBRRxL = baud_setting; - // assign the baud_setting, a.k.a. ubbr (USART Baud Rate Register) - M_UBRRxH = baud_setting >> 8; - M_UBRRxL = baud_setting; + SBI(M_UCSRxB, M_RXENx); + SBI(M_UCSRxB, M_TXENx); + SBI(M_UCSRxB, M_RXCIEx); + #if TX_BUFFER_SIZE > 0 + CBI(M_UCSRxB, M_UDRIEx); + _written = false; + #endif + } - SBI(M_UCSRxB, M_RXENx); - SBI(M_UCSRxB, M_TXENx); - SBI(M_UCSRxB, M_RXCIEx); - #if TX_BUFFER_SIZE > 0 + void MarlinSerial::end() { + CBI(M_UCSRxB, M_RXENx); + CBI(M_UCSRxB, M_TXENx); + CBI(M_UCSRxB, M_RXCIEx); CBI(M_UCSRxB, M_UDRIEx); - _written = false; - #endif -} - -void MarlinSerial::end() { - CBI(M_UCSRxB, M_RXENx); - CBI(M_UCSRxB, M_TXENx); - CBI(M_UCSRxB, M_RXCIEx); - CBI(M_UCSRxB, M_UDRIEx); -} - -void MarlinSerial::checkRx(void) { - if (TEST(M_UCSRxA, M_RXCx)) { - uint8_t c = M_UDRx; - store_char(c); } -} - -int MarlinSerial::peek(void) { - CRITICAL_SECTION_START; - int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail]; - CRITICAL_SECTION_END; - return v; -} - -int MarlinSerial::read(void) { - int v; - CRITICAL_SECTION_START; - uint8_t t = rx_buffer.tail; - if (rx_buffer.head == t) { - v = -1; - } - else { - v = rx_buffer.buffer[t]; - rx_buffer.tail = (uint8_t)(t + 1) & (RX_BUFFER_SIZE - 1); + + void MarlinSerial::checkRx(void) { + if (TEST(M_UCSRxA, M_RXCx)) { + uint8_t c = M_UDRx; + store_char(c); } - CRITICAL_SECTION_END; - return v; -} - -uint8_t MarlinSerial::available(void) { - CRITICAL_SECTION_START; - uint8_t h = rx_buffer.head, - t = rx_buffer.tail; - CRITICAL_SECTION_END; - return (uint8_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1); -} - -void MarlinSerial::flush(void) { - // RX - // don't reverse this or there may be problems if the RX interrupt - // occurs after reading the value of rx_buffer_head but before writing - // the value to rx_buffer_tail; the previous value of rx_buffer_head - // may be written to rx_buffer_tail, making it appear as if the buffer - // were full, not empty. - CRITICAL_SECTION_START; - rx_buffer.head = rx_buffer.tail; - CRITICAL_SECTION_END; -} - -#if TX_BUFFER_SIZE > 0 - uint8_t MarlinSerial::availableForWrite(void) { + } + + int MarlinSerial::peek(void) { CRITICAL_SECTION_START; - uint8_t h = tx_buffer.head; - uint8_t t = tx_buffer.tail; + int v = rx_buffer.head == rx_buffer.tail ? -1 : rx_buffer.buffer[rx_buffer.tail]; + CRITICAL_SECTION_END; + return v; + } + + int MarlinSerial::read(void) { + int v; + CRITICAL_SECTION_START; + uint8_t t = rx_buffer.tail; + if (rx_buffer.head == t) { + v = -1; + } + else { + v = rx_buffer.buffer[t]; + rx_buffer.tail = (uint8_t)(t + 1) & (RX_BUFFER_SIZE - 1); + } CRITICAL_SECTION_END; - return (uint8_t)(TX_BUFFER_SIZE + h - t) & (TX_BUFFER_SIZE - 1); + return v; } - void MarlinSerial::write(uint8_t c) { - _written = true; + uint8_t MarlinSerial::available(void) { CRITICAL_SECTION_START; - bool emty = (tx_buffer.head == tx_buffer.tail); + uint8_t h = rx_buffer.head, + t = rx_buffer.tail; CRITICAL_SECTION_END; - // If the buffer and the data register is empty, just write the byte - // to the data register and be done. This shortcut helps - // significantly improve the effective datarate at high (> - // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. - if (emty && TEST(M_UCSRxA, M_UDREx)) { + return (uint8_t)(RX_BUFFER_SIZE + h - t) & (RX_BUFFER_SIZE - 1); + } + + void MarlinSerial::flush(void) { + // RX + // don't reverse this or there may be problems if the RX interrupt + // occurs after reading the value of rx_buffer_head but before writing + // the value to rx_buffer_tail; the previous value of rx_buffer_head + // may be written to rx_buffer_tail, making it appear as if the buffer + // were full, not empty. + CRITICAL_SECTION_START; + rx_buffer.head = rx_buffer.tail; + CRITICAL_SECTION_END; + } + + #if TX_BUFFER_SIZE > 0 + uint8_t MarlinSerial::availableForWrite(void) { CRITICAL_SECTION_START; - M_UDRx = c; - SBI(M_UCSRxA, M_TXCx); + uint8_t h = tx_buffer.head; + uint8_t t = tx_buffer.tail; CRITICAL_SECTION_END; - return; - } - uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1); - - // If the output buffer is full, there's nothing for it other than to - // wait for the interrupt handler to empty it a bit - while (i == tx_buffer.tail) { - if (!TEST(SREG, SREG_I)) { - // Interrupts are disabled, so we'll have to poll the data - // register empty flag ourselves. If it is set, pretend an - // interrupt has happened and call the handler to free up - // space for us. - if (TEST(M_UCSRxA, M_UDREx)) - _tx_udr_empty_irq(); - } else { - // nop, the interrupt handler will free up space for us - } + return (uint8_t)(TX_BUFFER_SIZE + h - t) & (TX_BUFFER_SIZE - 1); } - tx_buffer.buffer[tx_buffer.head] = c; - { CRITICAL_SECTION_START; - tx_buffer.head = i; - SBI(M_UCSRxB, M_UDRIEx); + void MarlinSerial::write(uint8_t c) { + _written = true; + CRITICAL_SECTION_START; + bool emty = (tx_buffer.head == tx_buffer.tail); CRITICAL_SECTION_END; - } - return; - } + // If the buffer and the data register is empty, just write the byte + // to the data register and be done. This shortcut helps + // significantly improve the effective datarate at high (> + // 500kbit/s) bitrates, where interrupt overhead becomes a slowdown. + if (emty && TEST(M_UCSRxA, M_UDREx)) { + CRITICAL_SECTION_START; + M_UDRx = c; + SBI(M_UCSRxA, M_TXCx); + CRITICAL_SECTION_END; + return; + } + uint8_t i = (tx_buffer.head + 1) & (TX_BUFFER_SIZE - 1); + + // If the output buffer is full, there's nothing for it other than to + // wait for the interrupt handler to empty it a bit + while (i == tx_buffer.tail) { + if (!TEST(SREG, SREG_I)) { + // Interrupts are disabled, so we'll have to poll the data + // register empty flag ourselves. If it is set, pretend an + // interrupt has happened and call the handler to free up + // space for us. + if (TEST(M_UCSRxA, M_UDREx)) + _tx_udr_empty_irq(); + } else { + // nop, the interrupt handler will free up space for us + } + } - void MarlinSerial::flushTX(void) { - // TX - // If we have never written a byte, no need to flush. This special - // case is needed since there is no way to force the TXC (transmit - // complete) bit to 1 during initialization - if (!_written) + tx_buffer.buffer[tx_buffer.head] = c; + { CRITICAL_SECTION_START; + tx_buffer.head = i; + SBI(M_UCSRxB, M_UDRIEx); + CRITICAL_SECTION_END; + } return; - - while (TEST(M_UCSRxB, M_UDRIEx) || !TEST(M_UCSRxA, M_TXCx)) { - if (!TEST(SREG, SREG_I) && TEST(M_UCSRxB, M_UDRIEx)) - // Interrupts are globally disabled, but the DR empty - // interrupt should be enabled, so poll the DR empty flag to - // prevent deadlock - if (TEST(M_UCSRxA, M_UDREx)) - _tx_udr_empty_irq(); } - // If we get here, nothing is queued anymore (DRIE is disabled) and - // the hardware finished tranmission (TXC is set). -} - -#else - void MarlinSerial::write(uint8_t c) { - while (!TEST(M_UCSRxA, M_UDREx)) - ; - M_UDRx = c; + + void MarlinSerial::flushTX(void) { + // TX + // If we have never written a byte, no need to flush. This special + // case is needed since there is no way to force the TXC (transmit + // complete) bit to 1 during initialization + if (!_written) + return; + + while (TEST(M_UCSRxB, M_UDRIEx) || !TEST(M_UCSRxA, M_TXCx)) { + if (!TEST(SREG, SREG_I) && TEST(M_UCSRxB, M_UDRIEx)) + // Interrupts are globally disabled, but the DR empty + // interrupt should be enabled, so poll the DR empty flag to + // prevent deadlock + if (TEST(M_UCSRxA, M_UDREx)) + _tx_udr_empty_irq(); + } + // If we get here, nothing is queued anymore (DRIE is disabled) and + // the hardware finished tranmission (TXC is set). } -#endif -// end NEW + #else + void MarlinSerial::write(uint8_t c) { + while (!TEST(M_UCSRxA, M_UDREx)) + ; + M_UDRx = c; + } + #endif -/// imports from print.h + // end NEW + /// imports from print.h -void MarlinSerial::print(char c, int base) { - print((long) c, base); -} -void MarlinSerial::print(unsigned char b, int base) { - print((unsigned long) b, base); -} + void MarlinSerial::print(char c, int base) { + print((long) c, base); + } -void MarlinSerial::print(int n, int base) { - print((long) n, base); -} + void MarlinSerial::print(unsigned char b, int base) { + print((unsigned long) b, base); + } -void MarlinSerial::print(unsigned int n, int base) { - print((unsigned long) n, base); -} + void MarlinSerial::print(int n, int base) { + print((long) n, base); + } -void MarlinSerial::print(long n, int base) { - if (base == 0) { - write(n); + void MarlinSerial::print(unsigned int n, int base) { + print((unsigned long) n, base); } - else if (base == 10) { - if (n < 0) { - print('-'); - n = -n; + + void MarlinSerial::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); } - printNumber(n, 10); } - else { - printNumber(n, base); + + void MarlinSerial::print(unsigned long n, int base) { + if (base == 0) write(n); + else printNumber(n, base); } -} - -void MarlinSerial::print(unsigned long n, int base) { - if (base == 0) write(n); - else printNumber(n, base); -} - -void MarlinSerial::print(double n, int digits) { - printFloat(n, digits); -} - -void MarlinSerial::println(void) { - print('\r'); - print('\n'); -} - -void MarlinSerial::println(const String& s) { - print(s); - println(); -} - -void MarlinSerial::println(const char c[]) { - print(c); - println(); -} - -void MarlinSerial::println(char c, int base) { - print(c, base); - println(); -} - -void MarlinSerial::println(unsigned char b, int base) { - print(b, base); - println(); -} - -void MarlinSerial::println(int n, int base) { - print(n, base); - println(); -} - -void MarlinSerial::println(unsigned int n, int base) { - print(n, base); - println(); -} - -void MarlinSerial::println(long n, int base) { - print(n, base); - println(); -} - -void MarlinSerial::println(unsigned long n, int base) { - print(n, base); - println(); -} - -void MarlinSerial::println(double n, int digits) { - print(n, digits); - println(); -} - -// Private Methods ///////////////////////////////////////////////////////////// - -void MarlinSerial::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))); + + void MarlinSerial::print(double n, int digits) { + printFloat(n, digits); } - else - print('0'); -} - -void MarlinSerial::printFloat(double number, uint8_t digits) { - // Handle negative numbers - if (number < 0.0) { - print('-'); - number = -number; + + void MarlinSerial::println(void) { + print('\r'); + print('\n'); } - // Round correctly so that print(1.999, 2) prints as "2.00" - double rounding = 0.5; - for (uint8_t i = 0; i < digits; ++i) - 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; - } + void MarlinSerial::println(const String& s) { + print(s); + println(); } -} -// Preinstantiate Objects ////////////////////////////////////////////////////// + void MarlinSerial::println(const char c[]) { + print(c); + println(); + } -MarlinSerial customizedSerial; + void MarlinSerial::println(char c, int base) { + print(c, base); + println(); + } -#endif // whole file -#endif // !USBCON + void MarlinSerial::println(unsigned char b, int base) { + print(b, base); + println(); + } -// For AT90USB targets use the UART for BT interfacing -#if defined(USBCON) && ENABLED(BLUETOOTH) - HardwareSerial bluetoothSerial; -#endif + void MarlinSerial::println(int n, int base) { + print(n, base); + println(); + } -#if ENABLED(EMERGENCY_PARSER) + void MarlinSerial::println(unsigned int n, int base) { + print(n, base); + println(); + } - // Currently looking for: M108, M112, M410 - // If you alter the parser please don't forget to update the capabilities in Conditionals_post.h + void MarlinSerial::println(long n, int base) { + print(n, base); + println(); + } - FORCE_INLINE void emergency_parser(unsigned char c) { + void MarlinSerial::println(unsigned long n, int base) { + print(n, base); + println(); + } - static e_parser_state state = state_RESET; + void MarlinSerial::println(double n, int digits) { + print(n, digits); + println(); + } - switch (state) { - case state_RESET: - switch (c) { - case ' ': break; - case 'N': state = state_N; break; - case 'M': state = state_M; break; - default: state = state_IGNORE; - } - break; - - case state_N: - switch (c) { - case '0': case '1': case '2': - case '3': case '4': case '5': - case '6': case '7': case '8': - case '9': case '-': case ' ': break; - case 'M': state = state_M; break; - default: state = state_IGNORE; - } - break; - - case state_M: - switch (c) { - case ' ': break; - case '1': state = state_M1; break; - case '4': state = state_M4; break; - default: state = state_IGNORE; - } - break; + // Private Methods - case state_M1: - switch (c) { - case '0': state = state_M10; break; - case '1': state = state_M11; break; - default: state = state_IGNORE; - } - break; - - case state_M10: - state = (c == '8') ? state_M108 : state_IGNORE; - break; - - case state_M11: - state = (c == '2') ? state_M112 : state_IGNORE; - break; - - case state_M4: - state = (c == '1') ? state_M41 : state_IGNORE; - break; - - case state_M41: - state = (c == '0') ? state_M410 : state_IGNORE; - break; - - case state_IGNORE: - if (c == '\n') state = state_RESET; - break; - - default: - if (c == '\n') { - switch (state) { - case state_M108: - wait_for_user = wait_for_heatup = false; - break; - case state_M112: - kill(PSTR(MSG_KILLED)); - break; - case state_M410: - quickstop_stepper(); - break; - default: - break; - } - state = state_RESET; - } + void MarlinSerial::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 MarlinSerial::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; + for (uint8_t i = 0; i < digits; ++i) + rounding *= 0.1; + + number += rounding; + + // Extract the integer part of the number and print it + unsigned long int_part = (unsigned long)number; + double remainder = number - (double)int_part; + print(int_part); + + // Print the decimal point, but only if there are digits beyond + if (digits) { + print('.'); + // Extract digits from the remainder one at a time + while (digits--) { + remainder *= 10.0; + int toPrint = int(remainder); + print(toPrint); + remainder -= toPrint; + } + } + } + + // Preinstantiate + MarlinSerial customizedSerial; + +#endif // !USBCON && (UBRRH || UBRR0H || UBRR1H || UBRR2H || UBRR3H) + +// For AT90USB targets use the UART for BT interfacing +#if defined(USBCON) && ENABLED(BLUETOOTH) + HardwareSerial bluetoothSerial; #endif diff --git a/Marlin/MarlinSerial.h b/Marlin/MarlinSerial.h index 87f4f6a92..7ec7b2c26 100644 --- a/Marlin/MarlinSerial.h +++ b/Marlin/MarlinSerial.h @@ -29,8 +29,8 @@ */ -#ifndef MarlinSerial_h -#define MarlinSerial_h +#ifndef MARLINSERIAL_H +#define MARLINSERIAL_H #include "MarlinConfig.h" @@ -52,125 +52,118 @@ #define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##number##suffix #endif -// Registers used by MarlinSerial class (these are expanded -// depending on selected serial port -#define M_UCSRxA SERIAL_REGNAME(UCSR,SERIAL_PORT,A) // defines M_UCSRxA to be UCSRnA where n is the serial port number -#define M_UCSRxB SERIAL_REGNAME(UCSR,SERIAL_PORT,B) -#define M_RXENx SERIAL_REGNAME(RXEN,SERIAL_PORT,) -#define M_TXENx SERIAL_REGNAME(TXEN,SERIAL_PORT,) -#define M_TXCx SERIAL_REGNAME(TXC,SERIAL_PORT,) -#define M_RXCIEx SERIAL_REGNAME(RXCIE,SERIAL_PORT,) -#define M_UDREx SERIAL_REGNAME(UDRE,SERIAL_PORT,) -#define M_UDRIEx SERIAL_REGNAME(UDRIE,SERIAL_PORT,) -#define M_UDRx SERIAL_REGNAME(UDR,SERIAL_PORT,) -#define M_UBRRxH SERIAL_REGNAME(UBRR,SERIAL_PORT,H) -#define M_UBRRxL SERIAL_REGNAME(UBRR,SERIAL_PORT,L) -#define M_RXCx SERIAL_REGNAME(RXC,SERIAL_PORT,) -#define M_USARTx_RX_vect SERIAL_REGNAME(USART,SERIAL_PORT,_RX_vect) -#define M_U2Xx SERIAL_REGNAME(U2X,SERIAL_PORT,) +// Registers used by MarlinSerial class (expanded depending on selected serial port) +#define M_UCSRxA SERIAL_REGNAME(UCSR,SERIAL_PORT,A) // defines M_UCSRxA to be UCSRnA where n is the serial port number +#define M_UCSRxB SERIAL_REGNAME(UCSR,SERIAL_PORT,B) +#define M_RXENx SERIAL_REGNAME(RXEN,SERIAL_PORT,) +#define M_TXENx SERIAL_REGNAME(TXEN,SERIAL_PORT,) +#define M_TXCx SERIAL_REGNAME(TXC,SERIAL_PORT,) +#define M_RXCIEx SERIAL_REGNAME(RXCIE,SERIAL_PORT,) +#define M_UDREx SERIAL_REGNAME(UDRE,SERIAL_PORT,) +#define M_UDRIEx SERIAL_REGNAME(UDRIE,SERIAL_PORT,) +#define M_UDRx SERIAL_REGNAME(UDR,SERIAL_PORT,) +#define M_UBRRxH SERIAL_REGNAME(UBRR,SERIAL_PORT,H) +#define M_UBRRxL SERIAL_REGNAME(UBRR,SERIAL_PORT,L) +#define M_RXCx SERIAL_REGNAME(RXC,SERIAL_PORT,) +#define M_USARTx_RX_vect SERIAL_REGNAME(USART,SERIAL_PORT,_RX_vect) +#define M_U2Xx SERIAL_REGNAME(U2X,SERIAL_PORT,) #define M_USARTx_UDRE_vect SERIAL_REGNAME(USART,SERIAL_PORT,_UDRE_vect) - #define DEC 10 #define HEX 16 #define OCT 8 #define BIN 2 #define BYTE 0 - #ifndef USBCON -// 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 -// location to which to write the next incoming character and rx_buffer_tail -// is the index of the location from which to read. -// 256 is the max limit due to uint8_t head and tail. Use only powers of 2. (...,16,32,64,128,256) -#ifndef RX_BUFFER_SIZE - #define RX_BUFFER_SIZE 128 -#endif -#ifndef TX_BUFFER_SIZE - #define TX_BUFFER_SIZE 32 -#endif -#if !((RX_BUFFER_SIZE == 256) ||(RX_BUFFER_SIZE == 128) ||(RX_BUFFER_SIZE == 64) ||(RX_BUFFER_SIZE == 32) ||(RX_BUFFER_SIZE == 16) ||(RX_BUFFER_SIZE == 8) ||(RX_BUFFER_SIZE == 4) ||(RX_BUFFER_SIZE == 2)) - #error "RX_BUFFER_SIZE has to be a power of 2 and >= 2" -#endif -#if !((TX_BUFFER_SIZE == 256) ||(TX_BUFFER_SIZE == 128) ||(TX_BUFFER_SIZE == 64) ||(TX_BUFFER_SIZE == 32) ||(TX_BUFFER_SIZE == 16) ||(TX_BUFFER_SIZE == 8) ||(TX_BUFFER_SIZE == 4) ||(TX_BUFFER_SIZE == 2) ||(TX_BUFFER_SIZE == 0)) - #error TX_BUFFER_SIZE has to be a power of 2 or 0 -#endif - -struct ring_buffer_r { - unsigned char buffer[RX_BUFFER_SIZE]; - volatile uint8_t head; - volatile uint8_t tail; -}; + // 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 + // location to which to write the next incoming character and rx_buffer_tail + // is the index of the location from which to read. + // 256 is the max limit due to uint8_t head and tail. Use only powers of 2. (...,16,32,64,128,256) + #ifndef RX_BUFFER_SIZE + #define RX_BUFFER_SIZE 128 + #endif + #ifndef TX_BUFFER_SIZE + #define TX_BUFFER_SIZE 32 + #endif + #if !((RX_BUFFER_SIZE == 256) ||(RX_BUFFER_SIZE == 128) ||(RX_BUFFER_SIZE == 64) ||(RX_BUFFER_SIZE == 32) ||(RX_BUFFER_SIZE == 16) ||(RX_BUFFER_SIZE == 8) ||(RX_BUFFER_SIZE == 4) ||(RX_BUFFER_SIZE == 2)) + #error "RX_BUFFER_SIZE has to be a power of 2 and >= 2" + #endif + #if !((TX_BUFFER_SIZE == 256) ||(TX_BUFFER_SIZE == 128) ||(TX_BUFFER_SIZE == 64) ||(TX_BUFFER_SIZE == 32) ||(TX_BUFFER_SIZE == 16) ||(TX_BUFFER_SIZE == 8) ||(TX_BUFFER_SIZE == 4) ||(TX_BUFFER_SIZE == 2) ||(TX_BUFFER_SIZE == 0)) + #error TX_BUFFER_SIZE has to be a power of 2 or 0 + #endif -#if TX_BUFFER_SIZE > 0 - struct ring_buffer_t { - unsigned char buffer[TX_BUFFER_SIZE]; + struct ring_buffer_r { + unsigned char buffer[RX_BUFFER_SIZE]; volatile uint8_t head; volatile uint8_t tail; }; -#endif -#if UART_PRESENT(SERIAL_PORT) - extern ring_buffer_r rx_buffer; #if TX_BUFFER_SIZE > 0 - extern ring_buffer_t tx_buffer; + struct ring_buffer_t { + unsigned char buffer[TX_BUFFER_SIZE]; + volatile uint8_t head; + volatile uint8_t tail; + }; #endif -#endif - -#if ENABLED(EMERGENCY_PARSER) - #include "language.h" - void emergency_parser(unsigned char c); -#endif -class MarlinSerial { //: public Stream - - public: - MarlinSerial(); - static void begin(long); - static void end(); - static int peek(void); - static int read(void); - static void flush(void); - static uint8_t available(void); - static void checkRx(void); - static void write(uint8_t c); + #if UART_PRESENT(SERIAL_PORT) + extern ring_buffer_r rx_buffer; #if TX_BUFFER_SIZE > 0 - static uint8_t availableForWrite(void); - static void flushTX(void); + extern ring_buffer_t tx_buffer; #endif + #endif + + class MarlinSerial { //: public Stream + + public: + MarlinSerial() {}; + static void begin(long); + static void end(); + static int peek(void); + static int read(void); + static void flush(void); + static uint8_t available(void); + static void checkRx(void); + static void write(uint8_t c); + #if TX_BUFFER_SIZE > 0 + static uint8_t availableForWrite(void); + static void flushTX(void); + #endif + + private: + static void printNumber(unsigned long, uint8_t); + static void printFloat(double, uint8_t); + + public: + static FORCE_INLINE void write(const char* str) { while (*str) write(*str++); } + static FORCE_INLINE void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } + static FORCE_INLINE void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } + static FORCE_INLINE 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(void); + }; + + extern MarlinSerial customizedSerial; - private: - static void printNumber(unsigned long, uint8_t); - static void printFloat(double, uint8_t); - - public: - static FORCE_INLINE void write(const char* str) { while (*str) write(*str++); } - static FORCE_INLINE void write(const uint8_t* buffer, size_t size) { while (size--) write(*buffer++); } - static FORCE_INLINE void print(const String& s) { for (int i = 0; i < (int)s.length(); i++) write(s[i]); } - static FORCE_INLINE 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(void); -}; - -extern MarlinSerial customizedSerial; #endif // !USBCON // Use the UART for Bluetooth in AT90USB configurations @@ -178,4 +171,4 @@ extern MarlinSerial customizedSerial; extern HardwareSerial bluetoothSerial; #endif -#endif +#endif // MARLINSERIAL_H From 2cbc7b4b73da8ae18dff7502cd520c551cfd4b7c Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sun, 2 Apr 2017 01:04:54 -0500 Subject: [PATCH 8/8] Relocate serial macros and functions --- Marlin/Marlin.h | 62 +---------------------------- Marlin/Marlin_main.cpp | 10 ----- Marlin/serial.cpp | 34 ++++++++++++++++ Marlin/serial.h | 88 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 123 insertions(+), 71 deletions(-) create mode 100644 Marlin/serial.cpp create mode 100644 Marlin/serial.h diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h index 218291b4c..86d4bb033 100644 --- a/Marlin/Marlin.h +++ b/Marlin/Marlin.h @@ -39,19 +39,7 @@ #include "types.h" #include "fastio.h" #include "utility.h" - -#ifdef USBCON - #include "HardwareSerial.h" - #if ENABLED(BLUETOOTH) - #define MYSERIAL bluetoothSerial - #else - #define MYSERIAL Serial - #endif // BLUETOOTH -#else - #include "MarlinSerial.h" - #define MYSERIAL customizedSerial -#endif - +#include "serial.h" #include "WString.h" #if ENABLED(PRINTCOUNTER) @@ -60,54 +48,6 @@ #include "stopwatch.h" #endif -extern const char echomagic[] PROGMEM; -extern const char errormagic[] PROGMEM; - -#define SERIAL_CHAR(x) (MYSERIAL.write(x)) -#define SERIAL_EOL SERIAL_CHAR('\n') - -#define SERIAL_PROTOCOLCHAR(x) SERIAL_CHAR(x) -#define SERIAL_PROTOCOL(x) (MYSERIAL.print(x)) -#define SERIAL_PROTOCOL_F(x,y) (MYSERIAL.print(x,y)) -#define SERIAL_PROTOCOLPGM(x) (serialprintPGM(PSTR(x))) -#define SERIAL_PROTOCOLLN(x) do{ MYSERIAL.print(x); SERIAL_EOL; }while(0) -#define SERIAL_PROTOCOLLNPGM(x) (serialprintPGM(PSTR(x "\n"))) -#define SERIAL_PROTOCOLPAIR(name, value) (serial_echopair_P(PSTR(name),(value))) -#define SERIAL_PROTOCOLLNPAIR(name, value) do{ SERIAL_PROTOCOLPAIR(name, value); SERIAL_EOL; }while(0) - -#define SERIAL_ECHO_START (serialprintPGM(echomagic)) -#define SERIAL_ECHO(x) SERIAL_PROTOCOL(x) -#define SERIAL_ECHOPGM(x) SERIAL_PROTOCOLPGM(x) -#define SERIAL_ECHOLN(x) SERIAL_PROTOCOLLN(x) -#define SERIAL_ECHOLNPGM(x) SERIAL_PROTOCOLLNPGM(x) -#define SERIAL_ECHOPAIR(name,value) SERIAL_PROTOCOLPAIR(name, value) -#define SERIAL_ECHOLNPAIR(name, value) SERIAL_PROTOCOLLNPAIR(name, value) -#define SERIAL_ECHO_F(x,y) SERIAL_PROTOCOL_F(x,y) - -#define SERIAL_ERROR_START (serialprintPGM(errormagic)) -#define SERIAL_ERROR(x) SERIAL_PROTOCOL(x) -#define SERIAL_ERRORPGM(x) SERIAL_PROTOCOLPGM(x) -#define SERIAL_ERRORLN(x) SERIAL_PROTOCOLLN(x) -#define SERIAL_ERRORLNPGM(x) SERIAL_PROTOCOLLNPGM(x) - -void serial_echopair_P(const char* s_P, const char *v); -void serial_echopair_P(const char* s_P, char v); -void serial_echopair_P(const char* s_P, int v); -void serial_echopair_P(const char* s_P, long v); -void serial_echopair_P(const char* s_P, float v); -void serial_echopair_P(const char* s_P, double v); -void serial_echopair_P(const char* s_P, unsigned int v); -void serial_echopair_P(const char* s_P, unsigned long v); -FORCE_INLINE void serial_echopair_P(const char* s_P, uint8_t v) { serial_echopair_P(s_P, (int)v); } -FORCE_INLINE void serial_echopair_P(const char* s_P, uint16_t v) { serial_echopair_P(s_P, (int)v); } -FORCE_INLINE void serial_echopair_P(const char* s_P, bool v) { serial_echopair_P(s_P, (int)v); } -FORCE_INLINE void serial_echopair_P(const char* s_P, void *v) { serial_echopair_P(s_P, (unsigned long)v); } - -// Things to write to serial from Program memory. Saves 400 to 2k of RAM. -FORCE_INLINE void serialprintPGM(const char* str) { - while (char ch = pgm_read_byte(str++)) MYSERIAL.write(ch); -} - void idle( #if ENABLED(FILAMENT_CHANGE_FEATURE) bool no_stepper_sleep = false // pass true to keep steppers from disabling on timeout diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 27ef4f610..79771ae06 100755 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -447,8 +447,6 @@ volatile bool wait_for_heatup = true; volatile bool wait_for_user = false; #endif -const char errormagic[] PROGMEM = "Error:"; -const char echomagic[] PROGMEM = "echo:"; const char axis_codes[XYZE] = {'X', 'Y', 'Z', 'E'}; // Number of characters read in the current line of serial input @@ -696,14 +694,6 @@ void set_current_from_steppers_for_axis(const AxisEnum axis); void plan_cubic_move(const float offset[4]); #endif -void serial_echopair_P(const char* s_P, const char *v) { serialprintPGM(s_P); SERIAL_ECHO(v); } -void serial_echopair_P(const char* s_P, char v) { serialprintPGM(s_P); SERIAL_CHAR(v); } -void serial_echopair_P(const char* s_P, int v) { serialprintPGM(s_P); SERIAL_ECHO(v); } -void serial_echopair_P(const char* s_P, long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } -void serial_echopair_P(const char* s_P, float v) { serialprintPGM(s_P); SERIAL_ECHO(v); } -void serial_echopair_P(const char* s_P, double v) { serialprintPGM(s_P); SERIAL_ECHO(v); } -void serial_echopair_P(const char* s_P, unsigned long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } - void tool_change(const uint8_t tmp_extruder, const float fr_mm_s=0.0, bool no_move=false); static void report_current_position(); diff --git a/Marlin/serial.cpp b/Marlin/serial.cpp new file mode 100644 index 000000000..9b5ae139e --- /dev/null +++ b/Marlin/serial.cpp @@ -0,0 +1,34 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 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 . + * + */ + +#include "serial.h" + +const char errormagic[] PROGMEM = "Error:"; +const char echomagic[] PROGMEM = "echo:"; + +void serial_echopair_P(const char* s_P, const char *v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_P(const char* s_P, char v) { serialprintPGM(s_P); SERIAL_CHAR(v); } +void serial_echopair_P(const char* s_P, int v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_P(const char* s_P, long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_P(const char* s_P, float v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_P(const char* s_P, double v) { serialprintPGM(s_P); SERIAL_ECHO(v); } +void serial_echopair_P(const char* s_P, unsigned long v) { serialprintPGM(s_P); SERIAL_ECHO(v); } diff --git a/Marlin/serial.h b/Marlin/serial.h new file mode 100644 index 000000000..864a9d8f6 --- /dev/null +++ b/Marlin/serial.h @@ -0,0 +1,88 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (C) 2016 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 . + * + */ + +#ifndef __SERIAL_H__ +#define __SERIAL_H__ + +#ifdef USBCON + #include "HardwareSerial.h" + #if ENABLED(BLUETOOTH) + #define MYSERIAL bluetoothSerial + #else + #define MYSERIAL Serial + #endif // BLUETOOTH +#else + #include "MarlinSerial.h" + #define MYSERIAL customizedSerial +#endif + +extern const char echomagic[] PROGMEM; +extern const char errormagic[] PROGMEM; + +#define SERIAL_CHAR(x) (MYSERIAL.write(x)) +#define SERIAL_EOL SERIAL_CHAR('\n') + +#define SERIAL_PROTOCOLCHAR(x) SERIAL_CHAR(x) +#define SERIAL_PROTOCOL(x) (MYSERIAL.print(x)) +#define SERIAL_PROTOCOL_F(x,y) (MYSERIAL.print(x,y)) +#define SERIAL_PROTOCOLPGM(x) (serialprintPGM(PSTR(x))) +#define SERIAL_PROTOCOLLN(x) do{ MYSERIAL.print(x); SERIAL_EOL; }while(0) +#define SERIAL_PROTOCOLLNPGM(x) (serialprintPGM(PSTR(x "\n"))) +#define SERIAL_PROTOCOLPAIR(name, value) (serial_echopair_P(PSTR(name),(value))) +#define SERIAL_PROTOCOLLNPAIR(name, value) do{ SERIAL_PROTOCOLPAIR(name, value); SERIAL_EOL; }while(0) + +#define SERIAL_ECHO_START (serialprintPGM(echomagic)) +#define SERIAL_ECHO(x) SERIAL_PROTOCOL(x) +#define SERIAL_ECHOPGM(x) SERIAL_PROTOCOLPGM(x) +#define SERIAL_ECHOLN(x) SERIAL_PROTOCOLLN(x) +#define SERIAL_ECHOLNPGM(x) SERIAL_PROTOCOLLNPGM(x) +#define SERIAL_ECHOPAIR(name,value) SERIAL_PROTOCOLPAIR(name, value) +#define SERIAL_ECHOLNPAIR(name, value) SERIAL_PROTOCOLLNPAIR(name, value) +#define SERIAL_ECHO_F(x,y) SERIAL_PROTOCOL_F(x,y) + +#define SERIAL_ERROR_START (serialprintPGM(errormagic)) +#define SERIAL_ERROR(x) SERIAL_PROTOCOL(x) +#define SERIAL_ERRORPGM(x) SERIAL_PROTOCOLPGM(x) +#define SERIAL_ERRORLN(x) SERIAL_PROTOCOLLN(x) +#define SERIAL_ERRORLNPGM(x) SERIAL_PROTOCOLLNPGM(x) + +void serial_echopair_P(const char* s_P, const char *v); +void serial_echopair_P(const char* s_P, char v); +void serial_echopair_P(const char* s_P, int v); +void serial_echopair_P(const char* s_P, long v); +void serial_echopair_P(const char* s_P, float v); +void serial_echopair_P(const char* s_P, double v); +void serial_echopair_P(const char* s_P, unsigned int v); +void serial_echopair_P(const char* s_P, unsigned long v); +FORCE_INLINE void serial_echopair_P(const char* s_P, uint8_t v) { serial_echopair_P(s_P, (int)v); } +FORCE_INLINE void serial_echopair_P(const char* s_P, uint16_t v) { serial_echopair_P(s_P, (int)v); } +FORCE_INLINE void serial_echopair_P(const char* s_P, bool v) { serial_echopair_P(s_P, (int)v); } +FORCE_INLINE void serial_echopair_P(const char* s_P, void *v) { serial_echopair_P(s_P, (unsigned long)v); } + +// +// Functions for serial printing from PROGMEM. (Saves loads of SRAM.) +// +FORCE_INLINE void serialprintPGM(const char* str) { + while (char ch = pgm_read_byte(str++)) MYSERIAL.write(ch); +} + +#endif // __SERIAL_H__