From 02212df832f0546962aeaa748c2a373c7d66f088 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Thu, 22 Jul 2021 18:28:01 -0500 Subject: [PATCH] merge followup --- Marlin/Configuration_adv.h | 22 +- Marlin/src/gcode/gcode.cpp | 4 - Marlin/src/gcode/gcode.h | 4 +- Marlin/src/gcode/gcode_d.cpp | 479 ++++++++++++------------- Marlin/src/gcode/queue.cpp | 114 +++--- Marlin/src/gcode/queue.h | 68 +--- Marlin/src/gcode/stats/M576.cpp | 52 --- buildroot/tests/STM32F103RET6_creality | 2 +- 8 files changed, 308 insertions(+), 437 deletions(-) delete mode 100644 Marlin/src/gcode/stats/M576.cpp diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 95bc00ef95f1..70346324216c 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -2234,14 +2234,6 @@ // Some clients will have this feature soon. This could make the NO_TIMEOUTS unnecessary. //#define ADVANCED_OK -/** - * Buffer monitoring - * - * To help diagnose print quality issues stemming from command buffers being empty, - * we add M576 which enables reporting of buffer empty - */ -//#define BUFFER_MONITORING - // Printrun may have trouble receiving long strings all at once. // This option inserts short delays between lines of serial output. #define SERIAL_OVERRUN_PROTECTION @@ -4182,13 +4174,13 @@ // Enable Marlin dev mode which adds some special commands //#define MARLIN_DEV_MODE -/** - * Buffer monitoring - * - * To help diagnose print quality issues stemming from command buffers being empty, - * we add D576 which enables reporting of buffer empty. Requires MARLIN_DEV_MODE. - */ -//#define BUFFER_MONITORING +#if ENABLED(MARLIN_DEV_MODE) + /** + * D576 - Buffer Monitoring + * To help diagnose print quality issues stemming from empty command buffers. + */ + //#define BUFFER_MONITORING +#endif /** * Postmortem Debugging captures misbehavior and outputs the CPU status and backtrace to serial. diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index f919ab7c2222..12fd231ca8e4 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -851,10 +851,6 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { case 575: M575(); break; // M575: Set serial baudrate #endif - #if ENABLED(BUFFER_MONITORING) // M576: Buffer monitoring - case 576: M576(); break; - #endif - #if ENABLED(ADVANCED_PAUSE_FEATURE) case 600: M600(); break; // M600: Pause for Filament Change case 603: M603(); break; // M603: Configure Filament Change diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index 048151a8d309..769b496f22dd 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -241,6 +241,7 @@ * M553 - Get or set IP netmask. (Requires enabled Ethernet port) * M554 - Get or set IP gateway. (Requires enabled Ethernet port) * M569 - Enable stealthChop on an axis. (Requires at least one _DRIVER_TYPE to be TMC2130/2160/2208/2209/5130/5160) + * M575 - Change the serial baud rate. (Requires BAUD_RATE_GCODE) * M600 - Pause for filament change: "M600 X Y Z E L". (Requires ADVANCED_PAUSE_FEATURE) * M603 - Configure filament change: "M603 T U L". (Requires ADVANCED_PAUSE_FEATURE) * M605 - Set Dual X-Carriage movement mode: "M605 S [X] [R]". (Requires DUAL_X_CARRIAGE) @@ -297,6 +298,7 @@ * M997 - Perform in-application firmware update * M999 - Restart after being stopped by error * D... - Custom Development G-code. Add hooks to 'gcode_D.cpp' for developers to test features. (Requires MARLIN_DEV_MODE) + * D576 - Set buffer monitoring options. (Requires BUFFER_MONITORING) * * "T" Codes * @@ -973,8 +975,6 @@ class GcodeSuite { static void M575(); #endif - TERN_(BUFFER_MONITORING, static void M576()); - #if ENABLED(ADVANCED_PAUSE_FEATURE) static void M600(); static void M603(); diff --git a/Marlin/src/gcode/gcode_d.cpp b/Marlin/src/gcode/gcode_d.cpp index eb08d39406ff..b317a1781548 100644 --- a/Marlin/src/gcode/gcode_d.cpp +++ b/Marlin/src/gcode/gcode_d.cpp @@ -23,280 +23,279 @@ #if ENABLED(MARLIN_DEV_MODE) - #include "gcode.h" +#include "gcode.h" - #if ENABLED(BUFFER_MONITORING) - #include "queue.h" - #endif - - #include "../module/settings.h" - #include "../module/temperature.h" - #include "../libs/hex_print.h" - #include "../HAL/shared/eeprom_if.h" - #include "../HAL/shared/Delay.h" - #include "../sd/cardreader.h" - #include "../MarlinCore.h" // for kill +#if ENABLED(BUFFER_MONITORING) + #include "queue.h" +#endif - extern void dump_delay_accuracy_check(); +#include "../module/settings.h" +#include "../module/temperature.h" +#include "../libs/hex_print.h" +#include "../HAL/shared/eeprom_if.h" +#include "../HAL/shared/Delay.h" +#include "../sd/cardreader.h" +#include "../MarlinCore.h" // for kill - /** - * Dn: G-code for development and testing - * - * See https://reprap.org/wiki/G-code#D:_Debug_codes - * - * Put whatever else you need here to test ongoing development. - */ - void GcodeSuite::D(const int16_t dcode) { - switch (dcode) { +extern void dump_delay_accuracy_check(); - case -1: - for (;;) { /* loop forever (watchdog reset) */ } +/** + * Dn: G-code for development and testing + * + * See https://reprap.org/wiki/G-code#D:_Debug_codes + * + * Put whatever else you need here to test ongoing development. + */ +void GcodeSuite::D(const int16_t dcode) { + switch (dcode) { - case 0: - HAL_reboot(); - break; + case -1: + for (;;) { /* loop forever (watchdog reset) */ } - case 10: - kill(PSTR("D10"), PSTR("KILL TEST"), parser.seen_test('P')); - break; + case 0: + HAL_reboot(); + break; - case 1: { - // Zero or pattern-fill the EEPROM data - #if ENABLED(EEPROM_SETTINGS) - persistentStore.access_start(); - size_t total = persistentStore.capacity(); - int pos = 0; - const uint8_t value = 0x0; - while (total--) persistentStore.write_data(pos, &value, 1); - persistentStore.access_finish(); - #else - settings.reset(); - settings.save(); - #endif - HAL_reboot(); - } break; + case 10: + kill(PSTR("D10"), PSTR("KILL TEST"), parser.seen_test('P')); + break; - case 2: { // D2 Read / Write SRAM - #define SRAM_SIZE 8192 + case 1: { + // Zero or pattern-fill the EEPROM data + #if ENABLED(EEPROM_SETTINGS) + persistentStore.access_start(); + size_t total = persistentStore.capacity(); + int pos = 0; + const uint8_t value = 0x0; + while (total--) persistentStore.write_data(pos, &value, 1); + persistentStore.access_finish(); + #else + settings.reset(); + settings.save(); + #endif + HAL_reboot(); + } break; + + case 2: { // D2 Read / Write SRAM + #define SRAM_SIZE 8192 + uint8_t *pointer = parser.hex_adr_val('A'); + uint16_t len = parser.ushortval('C', 1); + uintptr_t addr = (uintptr_t)pointer; + NOMORE(addr, size_t(SRAM_SIZE - 1)); + NOMORE(len, SRAM_SIZE - addr); + if (parser.seenval('X')) { + // Write the hex bytes after the X + uint16_t val = parser.hex_val('X'); + while (len--) { + *pointer = val; + pointer++; + } + } + else { + while (len--) print_hex_byte(*(pointer++)); + SERIAL_EOL(); + } + } break; + + #if ENABLED(EEPROM_SETTINGS) + case 3: { // D3 Read / Write EEPROM uint8_t *pointer = parser.hex_adr_val('A'); uint16_t len = parser.ushortval('C', 1); uintptr_t addr = (uintptr_t)pointer; - NOMORE(addr, size_t(SRAM_SIZE - 1)); - NOMORE(len, SRAM_SIZE - addr); + NOMORE(addr, size_t(persistentStore.capacity() - 1)); + NOMORE(len, persistentStore.capacity() - addr); if (parser.seenval('X')) { - // Write the hex bytes after the X uint16_t val = parser.hex_val('X'); - while (len--) { - *pointer = val; - pointer++; - } - } - else { - while (len--) print_hex_byte(*(pointer++)); - SERIAL_EOL(); - } - } break; - - #if ENABLED(EEPROM_SETTINGS) - case 3: { // D3 Read / Write EEPROM - uint8_t *pointer = parser.hex_adr_val('A'); - uint16_t len = parser.ushortval('C', 1); - uintptr_t addr = (uintptr_t)pointer; - NOMORE(addr, size_t(persistentStore.capacity() - 1)); - NOMORE(len, persistentStore.capacity() - addr); - if (parser.seenval('X')) { - uint16_t val = parser.hex_val('X'); - #if ENABLED(EEPROM_SETTINGS) - persistentStore.access_start(); - while (len--) { - int pos = 0; - persistentStore.write_data(pos, (uint8_t *)&val, sizeof(val)); - } - SERIAL_EOL(); - persistentStore.access_finish(); - #else - SERIAL_ECHOLNPGM("NO EEPROM"); - #endif - } - else { - // Read bytes from EEPROM - #if ENABLED(EEPROM_SETTINGS) - persistentStore.access_start(); + #if ENABLED(EEPROM_SETTINGS) + persistentStore.access_start(); + while (len--) { int pos = 0; - uint8_t val; - while (len--) if (!persistentStore.read_data(pos, &val, 1)) print_hex_byte(val); - SERIAL_EOL(); - persistentStore.access_finish(); - #else - SERIAL_ECHOLNPGM("NO EEPROM"); - len = 0; - #endif + persistentStore.write_data(pos, (uint8_t *)&val, sizeof(val)); + } SERIAL_EOL(); - } - } break; - #endif - - case 4: { // D4 Read / Write PIN - //const bool is_out = parser.boolval('F'); - //const uint8_t pin = parser.byteval('P'), - // val = parser.byteval('V', LOW); - if (parser.seenval('X')) { - // TODO: Write the hex bytes after the X - //while (len--) { - //} + persistentStore.access_finish(); + #else + SERIAL_ECHOLNPGM("NO EEPROM"); + #endif } else { - //while (len--) { - //// TODO: Read bytes from EEPROM - // print_hex_byte(eeprom_read_byte(adr++)); - //} + // Read bytes from EEPROM + #if ENABLED(EEPROM_SETTINGS) + persistentStore.access_start(); + int pos = 0; + uint8_t val; + while (len--) if (!persistentStore.read_data(pos, &val, 1)) print_hex_byte(val); + SERIAL_EOL(); + persistentStore.access_finish(); + #else + SERIAL_ECHOLNPGM("NO EEPROM"); + len = 0; + #endif SERIAL_EOL(); } } break; - - case 5: { // D5 Read / Write onboard Flash - #define FLASH_SIZE 1024 - uint8_t *pointer = parser.hex_adr_val('A'); - uint16_t len = parser.ushortval('C', 1); - uintptr_t addr = (uintptr_t)pointer; - NOMORE(addr, size_t(FLASH_SIZE - 1)); - NOMORE(len, FLASH_SIZE - addr); - if (parser.seenval('X')) { - // TODO: Write the hex bytes after the X - //while (len--) {} - } - else { - //while (len--) { - //// TODO: Read bytes from EEPROM - // print_hex_byte(eeprom_read_byte(adr++)); - //} - SERIAL_EOL(); + #endif + + case 4: { // D4 Read / Write PIN + //const bool is_out = parser.boolval('F'); + //const uint8_t pin = parser.byteval('P'), + // val = parser.byteval('V', LOW); + if (parser.seenval('X')) { + // TODO: Write the hex bytes after the X + //while (len--) { + //} + } + else { + //while (len--) { + //// TODO: Read bytes from EEPROM + // print_hex_byte(eeprom_read_byte(adr++)); + //} + SERIAL_EOL(); + } + } break; + + case 5: { // D5 Read / Write onboard Flash + #define FLASH_SIZE 1024 + uint8_t *pointer = parser.hex_adr_val('A'); + uint16_t len = parser.ushortval('C', 1); + uintptr_t addr = (uintptr_t)pointer; + NOMORE(addr, size_t(FLASH_SIZE - 1)); + NOMORE(len, FLASH_SIZE - addr); + if (parser.seenval('X')) { + // TODO: Write the hex bytes after the X + //while (len--) {} + } + else { + //while (len--) { + //// TODO: Read bytes from EEPROM + // print_hex_byte(eeprom_read_byte(adr++)); + //} + SERIAL_EOL(); + } + } break; + + case 6: // D6 Check delay loop accuracy + dump_delay_accuracy_check(); + break; + + case 7: // D7 dump the current serial port type (hence configuration) + SERIAL_ECHOLNPAIR("Current serial configuration RX_BS:", RX_BUFFER_SIZE, ", TX_BS:", TX_BUFFER_SIZE); + SERIAL_ECHOLN(gtn(&SERIAL_IMPL)); + break; + + case 100: { // D100 Disable heaters and attempt a hard hang (Watchdog Test) + SERIAL_ECHOLNPGM("Disabling heaters and attempting to trigger Watchdog"); + SERIAL_ECHOLNPGM("(USE_WATCHDOG " TERN(USE_WATCHDOG, "ENABLED", "DISABLED") ")"); + thermalManager.disable_all_heaters(); + delay(1000); // Allow time to print + DISABLE_ISRS(); + // Use a low-level delay that does not rely on interrupts to function + // Do not spin forever, to avoid thermal risks if heaters are enabled and + // watchdog does not work. + for (int i = 10000; i--;) DELAY_US(1000UL); + ENABLE_ISRS(); + SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset."); + } break; + + #if ENABLED(SDSUPPORT) + + case 101: { // D101 Test SD Write + card.openFileWrite("test.gco"); + if (!card.isFileOpen()) { + SERIAL_ECHOLNPAIR("Failed to open test.gco to write."); + return; } - } break; + __attribute__((aligned(sizeof(size_t)))) uint8_t buf[512]; - case 6: // D6 Check delay loop accuracy - dump_delay_accuracy_check(); - break; + uint16_t c; + for (c = 0; c < COUNT(buf); c++) + buf[c] = 'A' + (c % ('Z' - 'A')); - case 7: // D7 dump the current serial port type (hence configuration) - SERIAL_ECHOLNPAIR("Current serial configuration RX_BS:", RX_BUFFER_SIZE, ", TX_BS:", TX_BUFFER_SIZE); - SERIAL_ECHOLN(gtn(&SERIAL_IMPL)); - break; - - case 100: { // D100 Disable heaters and attempt a hard hang (Watchdog Test) - SERIAL_ECHOLNPGM("Disabling heaters and attempting to trigger Watchdog"); - SERIAL_ECHOLNPGM("(USE_WATCHDOG " TERN(USE_WATCHDOG, "ENABLED", "DISABLED") ")"); - thermalManager.disable_all_heaters(); - delay(1000); // Allow time to print - DISABLE_ISRS(); - // Use a low-level delay that does not rely on interrupts to function - // Do not spin forever, to avoid thermal risks if heaters are enabled and - // watchdog does not work. - for (int i = 10000; i--;) DELAY_US(1000UL); - ENABLE_ISRS(); - SERIAL_ECHOLNPGM("FAILURE: Watchdog did not trigger board reset."); + c = 1024 * 4; + while (c--) { + TERN_(USE_WATCHDOG, watchdog_refresh()); + card.write(buf, COUNT(buf)); + } + SERIAL_ECHOLNPGM(" done"); + card.closefile(); } break; - #if ENABLED(SDSUPPORT) - - case 101: { // D101 Test SD Write - card.openFileWrite("test.gco"); - if (!card.isFileOpen()) { - SERIAL_ECHOLNPAIR("Failed to open test.gco to write."); - return; - } - __attribute__((aligned(sizeof(size_t)))) uint8_t buf[512]; - - uint16_t c; - for (c = 0; c < COUNT(buf); c++) - buf[c] = 'A' + (c % ('Z' - 'A')); - - c = 1024 * 4; - while (c--) { - TERN_(USE_WATCHDOG, watchdog_refresh()); - card.write(buf, COUNT(buf)); - } - SERIAL_ECHOLNPGM(" done"); - card.closefile(); - } break; - - case 102: { // D102 Test SD Read - char testfile[] = "test.gco"; - card.openFileRead(testfile); - if (!card.isFileOpen()) { - SERIAL_ECHOLNPAIR("Failed to open test.gco to read."); - return; - } - __attribute__((aligned(sizeof(size_t)))) uint8_t buf[512]; - uint16_t c = 1024 * 4; - while (c--) { - TERN_(USE_WATCHDOG, watchdog_refresh()); - card.read(buf, COUNT(buf)); - bool error = false; - for (uint16_t i = 0; i < COUNT(buf); i++) { - if (buf[i] != ('A' + (i % ('Z' - 'A')))) { - error = true; - break; - } - } - if (error) { - SERIAL_ECHOLNPGM(" Read error!"); + case 102: { // D102 Test SD Read + char testfile[] = "test.gco"; + card.openFileRead(testfile); + if (!card.isFileOpen()) { + SERIAL_ECHOLNPAIR("Failed to open test.gco to read."); + return; + } + __attribute__((aligned(sizeof(size_t)))) uint8_t buf[512]; + uint16_t c = 1024 * 4; + while (c--) { + TERN_(USE_WATCHDOG, watchdog_refresh()); + card.read(buf, COUNT(buf)); + bool error = false; + for (uint16_t i = 0; i < COUNT(buf); i++) { + if (buf[i] != ('A' + (i % ('Z' - 'A')))) { + error = true; break; } } - SERIAL_ECHOLNPGM(" done"); - card.closefile(); - } break; - - #endif // SDSUPPORT - - #if ENABLED(POSTMORTEM_DEBUGGING) - - case 451: { // Trigger all kind of faults to test exception catcher - SERIAL_ECHOLNPGM("Disabling heaters"); - thermalManager.disable_all_heaters(); - delay(1000); // Allow time to print - volatile uint8_t type[5] = { parser.byteval('T', 1) }; - - // The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code - switch (type[0]) { - case 1: default: *(int*)0 = 451; break; // Write at bad address - case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM) - case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this) - case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction + if (error) { + SERIAL_ECHOLNPGM(" Read error!"); + break; } - break; } + SERIAL_ECHOLNPGM(" done"); + card.closefile(); + } break; - #endif - - #if ENABLED(BUFFER_MONITORING) + #endif // SDSUPPORT - /** - * D576: Return buffer stats, and optionally set auto-report interval. - * Usage: D576 [S] - * - * When called, printer emits the following output: - * "D576 P B PU PD BU BD" - * Where: - * P: Planner buffers available - * B: Command buffers available - * PU: Planner buffer underruns since last report - * PD: Maximum time in ms planner buffer was empty since last report - * BU: Command buffer underruns since last report - * BD: Maximum time in ms command buffer was empty since last report - */ - case 576: { - if (parser.seenval('S')) { - queue.set_auto_report_interval((uint8_t)parser.value_byte()); - } + #if ENABLED(POSTMORTEM_DEBUGGING) - queue.report_buffer_statistics(); - break; + case 451: { // Trigger all kind of faults to test exception catcher + SERIAL_ECHOLNPGM("Disabling heaters"); + thermalManager.disable_all_heaters(); + delay(1000); // Allow time to print + volatile uint8_t type[5] = { parser.byteval('T', 1) }; + + // The code below is obviously wrong and it's full of quirks to fool the compiler from optimizing away the code + switch (type[0]) { + case 1: default: *(int*)0 = 451; break; // Write at bad address + case 2: { volatile int a = 0; volatile int b = 452 / a; *(int*)&a = b; } break; // Divide by zero (some CPUs accept this, like ARM) + case 3: { *(uint32_t*)&type[1] = 453; volatile int a = *(int*)&type[1]; type[0] = a / 255; } break; // Unaligned access (some CPUs accept this) + case 4: { volatile void (*func)() = (volatile void (*)()) 0xE0000000; func(); } break; // Invalid instruction } + break; + } + + #endif + + #if ENABLED(BUFFER_MONITORING) + + /** + * D576: Return buffer stats or set the auto-report interval. + * Usage: D576 [S] + * + * With no parameters emits the following output: + * "D576 P B PU PD BU BD" + * Where: + * P : Planner buffers free + * B : Command buffers free + * PU: Planner buffer underruns (since the last report) + * PD: Longest duration (ms) the planner buffer was empty (since the last report) + * BU: Command buffer underruns (since the last report) + * BD: Longest duration (ms) command buffer was empty (since the last report) + */ + case 576: { + if (parser.seenval('S')) + queue.set_auto_report_interval((uint8_t)parser.value_byte()); + else + queue.report_buffer_statistics(); + break; + } - #endif // BUFFER_MONITORING - } + #endif // BUFFER_MONITORING } +} -#endif +#endif // MARLIN_DEV_MODE diff --git a/Marlin/src/gcode/queue.cpp b/Marlin/src/gcode/queue.cpp index 446ce8d44126..fa83c82ddc87 100644 --- a/Marlin/src/gcode/queue.cpp +++ b/Marlin/src/gcode/queue.cpp @@ -71,37 +71,22 @@ GCodeQueue::RingBuffer GCodeQueue::ring_buffer = { 0 }; * Track buffer underruns */ #if ENABLED(BUFFER_MONITORING) - uint32_t GCodeQueue::command_buffer_underruns = 0; - bool GCodeQueue::command_buffer_empty = false; - millis_t GCodeQueue::max_command_buffer_empty_duration = 0; - millis_t GCodeQueue::command_buffer_empty_at = 0; - - uint32_t GCodeQueue::planner_buffer_underruns = 0; - bool GCodeQueue::planner_buffer_empty = false; - millis_t GCodeQueue::max_planner_buffer_empty_duration = 0; - millis_t GCodeQueue::planner_buffer_empty_at = 0; + uint32_t GCodeQueue::command_buffer_underruns = 0, + GCodeQueue::planner_buffer_underruns = 0; + bool GCodeQueue::command_buffer_empty = false, + GCodeQueue::planner_buffer_empty = false; + millis_t GCodeQueue::max_command_buffer_empty_duration = 0, + GCodeQueue::max_planner_buffer_empty_duration = 0, + GCodeQueue::command_buffer_empty_at = 0, + GCodeQueue::planner_buffer_empty_at = 0; uint8_t GCodeQueue::auto_buffer_report_interval; millis_t GCodeQueue::next_buffer_report_ms; #endif -/* - * Track buffer underruns +/** + * Serial command injection */ -#if ENABLED(BUFFER_MONITORING) - uint32_t GCodeQueue::command_buffer_underruns = 0; - bool GCodeQueue::command_buffer_empty = false; - millis_t GCodeQueue::max_command_buffer_empty_duration = 0; - millis_t GCodeQueue::command_buffer_empty_at = 0; - - uint32_t GCodeQueue::planner_buffer_underruns = 0; - bool GCodeQueue::planner_buffer_empty = false; - millis_t GCodeQueue::max_planner_buffer_empty_duration = 0; - millis_t GCodeQueue::planner_buffer_empty_at = 0; - - uint8_t GCodeQueue::auto_buffer_report_interval; - millis_t GCodeQueue::next_buffer_report_ms; -#endif /** * Next Injected PROGMEM Command pointer. (nullptr == empty) @@ -114,7 +99,6 @@ PGM_P GCodeQueue::injected_commands_P; // = nullptr */ char GCodeQueue::injected_commands[64]; // = { 0 } - void GCodeQueue::RingBuffer::commit_command(bool skip_ok OPTARG(HAS_MULTI_SERIAL, serial_index_t serial_ind/*=-1*/) ) { @@ -666,12 +650,9 @@ void GCodeQueue::advance() { #if ENABLED(BUFFER_MONITORING) if (command_buffer_empty) { - static millis_t command_buffer_empty_duration; - command_buffer_empty_duration = millis() - command_buffer_empty_at; - if (command_buffer_empty_duration > max_command_buffer_empty_duration) { - max_command_buffer_empty_duration = command_buffer_empty_duration; - } command_buffer_empty = false; + const millis_t command_buffer_empty_duration = millis() - command_buffer_empty_at; + NOLESS(max_command_buffer_empty_duration, command_buffer_empty_duration); } #endif @@ -718,50 +699,39 @@ void GCodeQueue::advance() { } #if ENABLED(BUFFER_MONITORING) -void GCodeQueue::report_buffer_statistics() { - SERIAL_ECHO("D576"); - SERIAL_ECHOLNPAIR_P(SP_P_STR, int(planner.moves_free()), - SP_B_STR, int(BUFSIZE - ring_buffer.length), - PSTR(" PU"), queue.planner_buffer_underruns, - PSTR(" PD"), queue.max_planner_buffer_empty_duration, - PSTR(" BU"), queue.command_buffer_underruns, - PSTR(" BD"), queue.max_command_buffer_empty_duration, - ); - - command_buffer_underruns = 0; - max_command_buffer_empty_duration = 0; - - planner_buffer_underruns = 0; - max_planner_buffer_empty_duration = 0; -} -void GCodeQueue::auto_report_buffer_statistics() { - // Bit of a hack to try to catch planner buffer underruns without having logic - // running inside Stepper::block_phase_isr - if (planner.movesplanned() == 0) { - if (!planner_buffer_empty) { // if the planner buffer wasn't empty, but now it is - planner_buffer_empty = true; - planner_buffer_underruns++; - planner_buffer_empty_at = millis(); - } - } else if (planner_buffer_empty) { // if the planner buffer was empty, but now it ain't - static millis_t planner_buffer_empty_duration; - planner_buffer_empty_duration = millis() - planner_buffer_empty_at; + void GCodeQueue::report_buffer_statistics() { + SERIAL_ECHOLNPAIR("D576" + " P:", planner.moves_free(), " ", -queue.planner_buffer_underruns, " (", queue.max_planner_buffer_empty_duration, ")" + " B:", BUFSIZE - ring_buffer.length, " ", -queue.command_buffer_underruns, " (", queue.max_command_buffer_empty_duration, ")" + ); + command_buffer_underruns = planner_buffer_underruns = 0; + max_command_buffer_empty_duration = max_planner_buffer_empty_duration = 0; + } - // if it's longer than the currently tracked max duration, replace it - if (planner_buffer_empty_duration > max_planner_buffer_empty_duration) { - max_planner_buffer_empty_duration = planner_buffer_empty_duration; + void GCodeQueue::auto_report_buffer_statistics() { + // Bit of a hack to try to catch planner buffer underruns without having logic + // running inside Stepper::block_phase_isr + const millis_t ms = millis(); + if (planner.movesplanned() == 0) { + if (!planner_buffer_empty) { // the planner buffer wasn't empty, but now it is + planner_buffer_empty = true; + planner_buffer_underruns++; + planner_buffer_empty_at = ms; + } + } + else if (planner_buffer_empty) { // the planner buffer was empty, but now it's not + planner_buffer_empty = false; + const millis_t planner_buffer_empty_duration = ms - planner_buffer_empty_at; + NOLESS(max_planner_buffer_empty_duration, planner_buffer_empty_duration); // if it's longer than the currently tracked max duration, replace it } - planner_buffer_empty = false; - } - - if (queue.auto_buffer_report_interval && ELAPSED(millis(), queue.next_buffer_report_ms)) { - queue.next_buffer_report_ms = millis() + 1000UL * queue.auto_buffer_report_interval; - PORT_REDIRECT(SERIAL_BOTH); - report_buffer_statistics(); - PORT_RESTORE(); + if (queue.auto_buffer_report_interval && ELAPSED(ms, queue.next_buffer_report_ms)) { + queue.next_buffer_report_ms = ms + 1000UL * queue.auto_buffer_report_interval; + PORT_REDIRECT(SERIAL_BOTH); + report_buffer_statistics(); + PORT_RESTORE(); + } } -} -#endif +#endif // BUFFER_MONITORING diff --git a/Marlin/src/gcode/queue.h b/Marlin/src/gcode/queue.h index 6f9ed8df6cf8..6bcf4a97e4be 100644 --- a/Marlin/src/gcode/queue.h +++ b/Marlin/src/gcode/queue.h @@ -192,63 +192,22 @@ class GCodeQueue { */ static void flush_and_request_resend(const serial_index_t serial_ind); - #if ENABLED(BUFFER_MONITORING) - /** - * Track buffer underruns - */ - static uint32_t command_buffer_underruns; - static bool command_buffer_empty; - static millis_t max_command_buffer_empty_duration; - static millis_t command_buffer_empty_at; - - static uint32_t planner_buffer_underruns; - static bool planner_buffer_empty; - static millis_t max_planner_buffer_empty_duration; - static millis_t planner_buffer_empty_at; - - /** - * Report buffer statistics to the host to be able to detect buffer underruns - * - * Returns "M576 " followed by: - * P Planner space remaining - * B Command buffer space remaining - * PU Number of planner buffer underruns since last report - * PD Max time in ms the planner buffer was empty since last report - * BU Number of command buffer underruns since last report - * BD Max time in ms the command buffer was empty since last report - */ - static void report_buffer_statistics(); - - static uint8_t auto_buffer_report_interval; - static millis_t next_buffer_report_ms; - static void auto_report_buffer_statistics(); - static inline void set_auto_report_interval(uint8_t v) { - NOMORE(v, 60); - auto_buffer_report_interval = v; - next_buffer_report_ms = millis() + 1000UL * v; - } - #endif - -private: - /** * (Re)Set the current line number for the last received command */ static inline void set_current_line_number(long n) { serial_state[ring_buffer.command_port().index].last_N = n; } #if ENABLED(BUFFER_MONITORING) - /** - * Track buffer underruns - */ - static uint32_t command_buffer_underruns; - static bool command_buffer_empty; - static millis_t max_command_buffer_empty_duration; - static millis_t command_buffer_empty_at; - - static uint32_t planner_buffer_underruns; - static bool planner_buffer_empty; - static millis_t max_planner_buffer_empty_duration; - static millis_t planner_buffer_empty_at; + + private: + + /** + * Track buffer underruns + */ + static uint32_t command_buffer_underruns, planner_buffer_underruns; + static bool command_buffer_empty, planner_buffer_empty; + static millis_t max_command_buffer_empty_duration, max_planner_buffer_empty_duration, + command_buffer_empty_at, planner_buffer_empty_at; /** * Report buffer statistics to the host to be able to detect buffer underruns @@ -265,14 +224,21 @@ class GCodeQueue { static uint8_t auto_buffer_report_interval; static millis_t next_buffer_report_ms; + + public: + static void auto_report_buffer_statistics(); + static inline void set_auto_report_interval(uint8_t v) { NOMORE(v, 60); auto_buffer_report_interval = v; next_buffer_report_ms = millis() + 1000UL * v; } + #endif // BUFFER_MONITORING +private: + static void get_serial_commands(); #if ENABLED(SDSUPPORT) diff --git a/Marlin/src/gcode/stats/M576.cpp b/Marlin/src/gcode/stats/M576.cpp deleted file mode 100644 index 969f7834183e..000000000000 --- a/Marlin/src/gcode/stats/M576.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/** - * 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 . - * - */ - -#include "../../inc/MarlinConfig.h" -#include "../../MarlinCore.h" // for SP_P/B_STR, etc. - -#if ENABLED(BUFFER_MONITORING) - -#include "../gcode.h" -#include "../queue.h" - -/** - * M576: Return buffer stats, and optionally set auto-report interval. - * Usage: M576 [S] - * - * When called, printer emits the following output: - * "M576 P B PU PD BU BD" - * Where: - * P: Planner buffers available - * B: Command buffers available - * PU: Planner buffer underruns since last report - * PD: Maximum time in ms planner buffer was empty since last report - * BU: Command buffer underruns since last report - * BD: Maximum time in ms command buffer was empty since last report - */ -void GcodeSuite::M576() { - if (parser.seenval('S')) - queue.set_auto_report_interval((uint8_t)parser.value_byte()); - - queue.report_buffer_statistics(); -} - -#endif // BUFFER_MONITORING diff --git a/buildroot/tests/STM32F103RET6_creality b/buildroot/tests/STM32F103RET6_creality index a3f885147e6e..8348b6347df0 100755 --- a/buildroot/tests/STM32F103RET6_creality +++ b/buildroot/tests/STM32F103RET6_creality @@ -10,7 +10,7 @@ set -e # Build with configs included in the PR # use_example_configs "Creality/Ender-3 V2" -opt_enable MARLIN_DEV_MODE +opt_enable MARLIN_DEV_MODE BUFFER_MONITORING exec_test $1 $2 "Ender 3 v2" "$3" use_example_configs "Creality/Ender-3 V2"