From 8fe742031013afecfeb68fdb6d2707b670be9e48 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 20 Feb 2016 17:35:35 -0800 Subject: [PATCH 1/2] Use a separate serial line buffer --- Marlin/Marlin.h | 4 +- Marlin/Marlin_main.cpp | 185 ++++++++++++++++++++++------------------- Marlin/cardreader.cpp | 15 ++-- Marlin/cardreader.h | 1 + Marlin/stepper.cpp | 2 +- Marlin/ultralcd.cpp | 17 ++-- 6 files changed, 119 insertions(+), 105 deletions(-) diff --git a/Marlin/Marlin.h b/Marlin/Marlin.h index 42f8af2c6..97ceb2abf 100644 --- a/Marlin/Marlin.h +++ b/Marlin/Marlin.h @@ -223,8 +223,8 @@ extern bool Running; inline bool IsRunning() { return Running; } inline bool IsStopped() { return !Running; } -bool enqueuecommand(const char* cmd); //put a single ASCII command at the end of the current buffer or return false when it is full -void enqueuecommands_P(const char* cmd); //put one or many ASCII commands at the end of the current buffer, read from flash +bool enqueue_and_echo_command(const char* cmd, bool say_ok=false); //put a single ASCII command at the end of the current buffer or return false when it is full +void enqueue_and_echo_commands_P(const char* cmd); //put one or many ASCII commands at the end of the current buffer, read from flash void prepare_arc_move(char isclockwise); void clamp_to_software_endstops(float target[3]); diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 7fe9786e0..031da1f36 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -276,9 +276,7 @@ const char echomagic[] PROGMEM = "echo:"; const char axis_codes[NUM_AXIS] = {'X', 'Y', 'Z', 'E'}; static bool relative_mode = false; //Determines Absolute or Relative Coordinates -static char serial_char; static int serial_count = 0; -static boolean comment_mode = false; static char* seen_pointer; ///< A pointer to find chars in the command string (X, Y, Z, E, etc.) const char* queued_commands_P = NULL; /* pointer to the current line in the active sequence of commands, or NULL when none */ const int sensitive_pins[] = SENSITIVE_PINS; ///< Sensitive pin list for M42 @@ -409,9 +407,7 @@ static uint8_t target_extruder; static bool filrunoutEnqueued = false; #endif -#if ENABLED(SDSUPPORT) - static bool fromsd[BUFSIZE]; -#endif +static bool send_ok[BUFSIZE]; #if HAS_SERVOS Servo servo[NUM_SERVOS]; @@ -483,7 +479,7 @@ static bool drain_queued_commands_P() { char c; while ((c = cmd[i]) && c != '\n') i++; // find the end of this gcode command cmd[i] = '\0'; - if (enqueuecommand(cmd)) { // buffer was not full (else we will retry later) + if (enqueue_and_echo_command(cmd)) { // buffer was not full (else we will retry later) if (c) queued_commands_P += i + 1; // move to next command else @@ -497,32 +493,45 @@ static bool drain_queued_commands_P() { * Aborts the current queue, if any. * Note: drain_queued_commands_P() must be called repeatedly to drain the commands afterwards */ -void enqueuecommands_P(const char* pgcode) { +void enqueue_and_echo_commands_P(const char* pgcode) { queued_commands_P = pgcode; drain_queued_commands_P(); // first command executed asap (when possible) } /** - * Copy a command directly into the main command buffer, from RAM. - * - * This is done in a non-safe way and needs a rework someday. - * Returns false if it doesn't add any command + * Once a new command is in the ring buffer, call this to commit it */ -bool enqueuecommand(const char* cmd) { - if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false; - - // This is dangerous if a mixing of serial and this happens - char* command = command_queue[cmd_queue_index_w]; - strcpy(command, cmd); - SERIAL_ECHO_START; - SERIAL_ECHOPGM(MSG_Enqueueing); - SERIAL_ECHO(command); - SERIAL_ECHOLNPGM("\""); +inline void _commit_command(bool say_ok) { + send_ok[cmd_queue_index_w] = say_ok; cmd_queue_index_w = (cmd_queue_index_w + 1) % BUFSIZE; commands_in_queue++; +} + +/** + * Copy a command directly into the main command buffer, from RAM. + * Returns true if successfully adds the command + */ +inline bool _enqueuecommand(const char* cmd, bool say_ok=false) { + if (*cmd == ';' || commands_in_queue >= BUFSIZE) return false; + strcpy(command_queue[cmd_queue_index_w], cmd); + _commit_command(say_ok); return true; } +/** + * Enqueue with Serial Echo + */ +bool enqueue_and_echo_command(const char* cmd, bool say_ok/*=false*/) { + if (_enqueuecommand(cmd, say_ok)) { + SERIAL_ECHO_START; + SERIAL_ECHOPGM(MSG_Enqueueing); + SERIAL_ECHO(cmd); + SERIAL_ECHOLNPGM("\""); + return true; + } + return false; +} + void setup_killpin() { #if HAS_KILL SET_INPUT(KILL_PIN); @@ -679,9 +688,8 @@ void setup() { SERIAL_ECHOPGM(MSG_PLANNER_BUFFER_BYTES); SERIAL_ECHOLN((int)sizeof(block_t)*BLOCK_BUFFER_SIZE); - #if ENABLED(SDSUPPORT) - for (int8_t i = 0; i < BUFSIZE; i++) fromsd[i] = false; - #endif + // Send "ok" after commands by default + for (int8_t i = 0; i < BUFSIZE; i++) send_ok[i] = true; // loads data from EEPROM if available else uses defaults (and resets step acceleration rate) Config_RetrieveSettings(); @@ -740,7 +748,7 @@ void setup() { * - Call LCD update */ void loop() { - if (commands_in_queue < BUFSIZE - 1) get_command(); + if (commands_in_queue < BUFSIZE) get_command(); #if ENABLED(SDSUPPORT) card.checkautostart(false); @@ -800,9 +808,12 @@ void gcode_line_error(const char* err, bool doFlush = true) { */ void get_command() { + static char serial_line_buffer[MAX_CMD_SIZE]; + static boolean serial_comment_mode = false; + if (drain_queued_commands_P()) return; // priority is given to non-serial commands - #if ENABLED(NO_TIMEOUTS) + #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 static millis_t last_command_time = 0; millis_t ms = millis(); @@ -817,29 +828,21 @@ void get_command() { // while (commands_in_queue < BUFSIZE && MYSERIAL.available() > 0) { - #if ENABLED(NO_TIMEOUTS) - last_command_time = ms; - #endif - - serial_char = MYSERIAL.read(); + char serial_char = MYSERIAL.read(); // - // If the character ends the line, or the line is full... + // If the character ends the line // - if (serial_char == '\n' || serial_char == '\r' || serial_count >= MAX_CMD_SIZE - 1) { + if (serial_char == '\n' || serial_char == '\r') { - // end of line == end of comment - comment_mode = false; + serial_comment_mode = false; // end of line == end of comment if (!serial_count) return; // empty lines just exit - char* command = command_queue[cmd_queue_index_w]; - command[serial_count] = 0; // terminate string + serial_line_buffer[serial_count] = 0; // terminate string + serial_count = 0; //reset buffer - // this item in the queue is not from sd - #if ENABLED(SDSUPPORT) - fromsd[cmd_queue_index_w] = false; - #endif + char* command = serial_line_buffer; while (*command == ' ') command++; // skip any leading spaces char* npos = (*command == 'N') ? command : NULL; // Require the N parameter to start the line @@ -904,44 +907,56 @@ void get_command() { // If command was e-stop process now if (strcmp(command, "M112") == 0) kill(PSTR(MSG_KILLED)); - cmd_queue_index_w = (cmd_queue_index_w + 1) % BUFSIZE; - commands_in_queue += 1; + #if defined(NO_TIMEOUTS) && NO_TIMEOUTS > 0 + last_command_time = ms; + #endif - serial_count = 0; //clear buffer + // Add the command to the queue + _enqueuecommand(serial_line_buffer, true); + } + else if (serial_count >= MAX_CMD_SIZE - 1) { + // Keep fetching, but ignore normal characters beyond the max length + // The command will be injected when EOL is reached } else if (serial_char == '\\') { // Handle escapes - if (MYSERIAL.available() > 0 && commands_in_queue < BUFSIZE) { + if (MYSERIAL.available() > 0) { // if we have one more character, copy it over serial_char = MYSERIAL.read(); - command_queue[cmd_queue_index_w][serial_count++] = serial_char; + serial_line_buffer[serial_count++] = serial_char; } // otherwise do nothing } - else { // its not a newline, carriage return or escape char - if (serial_char == ';') comment_mode = true; - if (!comment_mode) command_queue[cmd_queue_index_w][serial_count++] = serial_char; + else { // it's not a newline, carriage return or escape char + if (serial_char == ';') serial_comment_mode = true; + if (!serial_comment_mode) serial_line_buffer[serial_count++] = serial_char; } - } + + } // queue has space, serial has data #if ENABLED(SDSUPPORT) - if (!card.sdprinting || serial_count) return; + static bool stop_buffering = false, + sd_comment_mode = false; + + if (!card.sdprinting) return; // '#' stops reading from SD to the buffer prematurely, so procedural macro calls are possible - // if it occurs, stop_buffering is triggered and the buffer is ran dry. + // if it occurs, stop_buffering is triggered and the buffer is run dry. // this character _can_ occur in serial com, due to checksums. however, no checksums are used in SD printing - static bool stop_buffering = false; if (commands_in_queue == 0) stop_buffering = false; - while (!card.eof() && commands_in_queue < BUFSIZE && !stop_buffering) { + uint16_t sd_count = 0; + bool card_eof = card.eof(); + while (commands_in_queue < BUFSIZE && !card_eof && !stop_buffering) { int16_t n = card.get(); - serial_char = (char)n; - if (serial_char == '\n' || serial_char == '\r' || - ((serial_char == '#' || serial_char == ':') && !comment_mode) || - serial_count >= (MAX_CMD_SIZE - 1) || n == -1 + char sd_char = (char)n; + card_eof = card.eof(); + if (card_eof || n == -1 + || sd_char == '\n' || sd_char == '\r' + || ((sd_char == '#' || sd_char == ':') && !sd_comment_mode) ) { - if (card.eof()) { + if (card_eof) { SERIAL_PROTOCOLLNPGM(MSG_FILE_PRINTED); print_job_stop_ms = millis(); char time[30]; @@ -954,24 +969,24 @@ void get_command() { card.printingHasFinished(); card.checkautostart(true); } - if (serial_char == '#') stop_buffering = true; + if (sd_char == '#') stop_buffering = true; - if (!serial_count) { - comment_mode = false; //for new command - return; //if empty line - } - command_queue[cmd_queue_index_w][serial_count] = 0; //terminate string - // if (!comment_mode) { - fromsd[cmd_queue_index_w] = true; - commands_in_queue += 1; - cmd_queue_index_w = (cmd_queue_index_w + 1) % BUFSIZE; - // } - comment_mode = false; //for new command - serial_count = 0; //clear buffer + sd_comment_mode = false; //for new command + + if (!sd_count) continue; //skip empty lines + + command_queue[cmd_queue_index_w][sd_count] = '\0'; //terminate string + sd_count = 0; //clear buffer + + _commit_command(false); + } + else if (sd_count >= MAX_CMD_SIZE - 1) { + // Keep fetching, but ignore normal characters beyond the max length + // The command will be injected when EOL is reached } else { - if (serial_char == ';') comment_mode = true; - if (!comment_mode) command_queue[cmd_queue_index_w][serial_count++] = serial_char; + if (sd_char == ';') sd_comment_mode = true; + if (!sd_comment_mode) command_queue[cmd_queue_index_w][sd_count++] = sd_char; } } @@ -2654,7 +2669,7 @@ inline void gcode_G28() { case MeshStart: mbl.reset(); probe_point = 0; - enqueuecommands_P(PSTR("G28\nG29 S2")); + enqueue_and_echo_commands_P(PSTR("G28\nG29 S2")); break; case MeshNext: @@ -2693,7 +2708,7 @@ inline void gcode_G28() { SERIAL_PROTOCOLLNPGM("Mesh probing done."); probe_point = -1; mbl.active = 1; - enqueuecommands_P(PSTR("G28")); + enqueue_and_echo_commands_P(PSTR("G28")); } break; @@ -3215,7 +3230,7 @@ inline void gcode_G28() { SERIAL_ECHOLNPGM(Z_PROBE_END_SCRIPT); } #endif - enqueuecommands_P(PSTR(Z_PROBE_END_SCRIPT)); + enqueue_and_echo_commands_P(PSTR(Z_PROBE_END_SCRIPT)); st_synchronize(); #endif @@ -3374,7 +3389,7 @@ inline void gcode_M17() { } /** - * M23: Select a file + * M23: Open a file */ inline void gcode_M23() { card.openFile(current_command_args, true); @@ -5244,7 +5259,7 @@ inline void gcode_M428() { SERIAL_ERRORLNPGM(MSG_ERR_M428_TOO_FAR); LCD_ALERTMESSAGEPGM("Err: Too far!"); #if HAS_BUZZER - enqueuecommands_P(PSTR("M300 S40 P200")); + enqueue_and_echo_commands_P(PSTR("M300 S40 P200")); #endif err = true; break; @@ -5258,7 +5273,7 @@ inline void gcode_M428() { sync_plan_position(); LCD_ALERTMESSAGEPGM("Offset applied."); #if HAS_BUZZER - enqueuecommands_P(PSTR("M300 S659 P200\nM300 S698 P200")); + enqueue_and_echo_commands_P(PSTR("M300 S659 P200\nM300 S698 P200")); #endif } } @@ -6304,9 +6319,7 @@ void FlushSerialRequestResend() { void ok_to_send() { refresh_cmd_timeout(); - #if ENABLED(SDSUPPORT) - if (fromsd[cmd_queue_index_r]) return; - #endif + if (!send_ok[cmd_queue_index_r]) return; SERIAL_PROTOCOLPGM(MSG_OK); #if ENABLED(ADVANCED_OK) char* p = command_queue[cmd_queue_index_r]; @@ -6997,7 +7010,7 @@ void manage_inactivity(bool ignore_stepper_queue/*=false*/) { filrunout(); #endif - if (commands_in_queue < BUFSIZE - 1) get_command(); + if (commands_in_queue < BUFSIZE) get_command(); millis_t ms = millis(); @@ -7054,7 +7067,7 @@ void manage_inactivity(bool ignore_stepper_queue/*=false*/) { const int HOME_DEBOUNCE_DELAY = 2500; if (!READ(HOME_PIN)) { if (!homeDebounceCount) { - enqueuecommands_P(PSTR("G28")); + enqueue_and_echo_commands_P(PSTR("G28")); LCD_MESSAGEPGM(MSG_AUTO_HOME); } if (homeDebounceCount < HOME_DEBOUNCE_DELAY) @@ -7180,7 +7193,7 @@ void kill(const char* lcd_msg) { void filrunout() { if (!filrunoutEnqueued) { filrunoutEnqueued = true; - enqueuecommands_P(PSTR(FILAMENT_RUNOUT_SCRIPT)); + enqueue_and_echo_commands_P(PSTR(FILAMENT_RUNOUT_SCRIPT)); st_synchronize(); } } diff --git a/Marlin/cardreader.cpp b/Marlin/cardreader.cpp index dbbbd551e..e508190c4 100644 --- a/Marlin/cardreader.cpp +++ b/Marlin/cardreader.cpp @@ -243,6 +243,14 @@ void CardReader::release() { cardOK = false; } +void CardReader::openAndPrintFile(const char *name) { + char cmd[4 + (FILENAME_LENGTH + 1) * MAX_DIR_DEPTH + 2]; // Room for "M23 ", names with slashes, a null, and one extra + sprintf_P(cmd, PSTR("M23 %s"), name); + for (char *c = &cmd[4]; *c; c++) *c = tolower(*c); + enqueue_and_echo_command(cmd); + enqueue_and_echo_commands_P(PSTR("M24")); +} + void CardReader::startFileprint() { if (cardOK) sdprinting = true; @@ -500,10 +508,7 @@ void CardReader::checkautostart(bool force) { while (root.readDir(p, NULL) > 0) { for (int8_t i = 0; i < (int8_t)strlen((char*)p.name); i++) p.name[i] = tolower(p.name[i]); if (p.name[9] != '~' && strncmp((char*)p.name, autoname, 5) == 0) { - char cmd[4 + (FILENAME_LENGTH + 1) * (MAX_DIR_DEPTH) + 2]; - sprintf_P(cmd, PSTR("M23 %s"), autoname); - enqueuecommand(cmd); - enqueuecommands_P(PSTR("M24")); + openAndPrintFile(autoname); found = true; } } @@ -589,7 +594,7 @@ void CardReader::printingHasFinished() { sdprinting = false; if (SD_FINISHED_STEPPERRELEASE) { //finishAndDisableSteppers(); - enqueuecommands_P(PSTR(SD_FINISHED_RELEASECOMMAND)); + enqueue_and_echo_commands_P(PSTR(SD_FINISHED_RELEASECOMMAND)); } autotempShutdown(); } diff --git a/Marlin/cardreader.h b/Marlin/cardreader.h index 78f2d289f..db68710d7 100644 --- a/Marlin/cardreader.h +++ b/Marlin/cardreader.h @@ -23,6 +23,7 @@ public: void removeFile(char* name); void closefile(bool store_location=false); void release(); + void openAndPrintFile(const char *name); void startFileprint(); void pauseSDPrint(); void getStatus(); diff --git a/Marlin/stepper.cpp b/Marlin/stepper.cpp index 72ee0dfa9..f579112f2 100644 --- a/Marlin/stepper.cpp +++ b/Marlin/stepper.cpp @@ -617,7 +617,7 @@ ISR(TIMER1_COMPA_vect) { current_block = NULL; plan_discard_current_block(); #ifdef SD_FINISHED_RELEASECOMMAND - if ((cleaning_buffer_counter == 1) && (SD_FINISHED_STEPPERRELEASE)) enqueuecommands_P(PSTR(SD_FINISHED_RELEASECOMMAND)); + if ((cleaning_buffer_counter == 1) && (SD_FINISHED_STEPPERRELEASE)) enqueue_and_echo_commands_P(PSTR(SD_FINISHED_RELEASECOMMAND)); #endif cleaning_buffer_counter--; OCR1A = 200; diff --git a/Marlin/ultralcd.cpp b/Marlin/ultralcd.cpp index 6f9fce382..1e2fbb37b 100644 --- a/Marlin/ultralcd.cpp +++ b/Marlin/ultralcd.cpp @@ -479,7 +479,7 @@ static void lcd_main_menu() { */ void lcd_set_home_offsets() { // M428 Command - enqueuecommands_P(PSTR("M428")); + enqueue_and_echo_commands_P(PSTR("M428")); lcd_return_to_status(); } @@ -1504,7 +1504,7 @@ menu_edit_type(unsigned long, long5, ftostr5, 0.01) lcd_move_y(); } static void reprapworld_keypad_move_home() { - enqueuecommands_P((PSTR("G28"))); // move all axis home + enqueue_and_echo_commands_P((PSTR("G28"))); // move all axis home } #endif // REPRAPWORLD_KEYPAD @@ -1556,18 +1556,13 @@ void lcd_quick_feedback() { */ static void menu_action_back(menuFunc_t func) { lcd_goto_menu(func); } static void menu_action_submenu(menuFunc_t func) { lcd_save_previous_menu(); lcd_goto_menu(func); } -static void menu_action_gcode(const char* pgcode) { enqueuecommands_P(pgcode); } +static void menu_action_gcode(const char* pgcode) { enqueue_and_echo_commands_P(pgcode); } static void menu_action_function(menuFunc_t func) { (*func)(); } #if ENABLED(SDSUPPORT) static void menu_action_sdfile(const char* filename, char* longFilename) { - char cmd[30]; - char* c; - sprintf_P(cmd, PSTR("M23 %s"), filename); - for (c = &cmd[4]; *c; c++) *c = tolower(*c); - enqueuecommand(cmd); - enqueuecommands_P(PSTR("M24")); + card.openAndPrintFile(filename); lcd_return_to_status(); } @@ -2313,7 +2308,7 @@ char* ftostr52(const float& x) { current_position[Z_AXIS] = MESH_HOME_SEARCH_Z; line_to_current(Z_AXIS); mbl.active = 1; - enqueuecommands_P(PSTR("G28")); + enqueue_and_echo_commands_P(PSTR("G28")); lcd_return_to_status(); } else { @@ -2357,7 +2352,7 @@ char* ftostr52(const float& x) { static void lcd_level_bed() { axis_known_position[X_AXIS] = axis_known_position[Y_AXIS] = axis_known_position[Z_AXIS] = false; mbl.reset(); - enqueuecommands_P(PSTR("G28")); + enqueue_and_echo_commands_P(PSTR("G28")); lcdDrawUpdate = 2; lcd_goto_menu(_lcd_level_bed_homing); } From 80277cc9c8396337377bf01b4198f9b6da5a4d9a Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Fri, 18 Mar 2016 17:37:31 -0700 Subject: [PATCH 2/2] drain_queued_commands_P now returns true if there are more --- Marlin/Marlin_main.cpp | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/Marlin/Marlin_main.cpp b/Marlin/Marlin_main.cpp index 031da1f36..2097c3f5d 100644 --- a/Marlin/Marlin_main.cpp +++ b/Marlin/Marlin_main.cpp @@ -463,29 +463,28 @@ extern "C" { #endif //!SDSUPPORT /** - * Inject the next command from the command queue, when possible - * Return false only if no command was pending + * Inject the next "immediate" command, when possible. + * Return true if any immediate commands remain to inject. */ static bool drain_queued_commands_P() { - if (!queued_commands_P) return false; - - // Get the next 30 chars from the sequence of gcodes to run - char cmd[30]; - strncpy_P(cmd, queued_commands_P, sizeof(cmd) - 1); - cmd[sizeof(cmd) - 1] = '\0'; - - // Look for the end of line, or the end of sequence - size_t i = 0; - char c; - while ((c = cmd[i]) && c != '\n') i++; // find the end of this gcode command - cmd[i] = '\0'; - if (enqueue_and_echo_command(cmd)) { // buffer was not full (else we will retry later) - if (c) - queued_commands_P += i + 1; // move to next command - else - queued_commands_P = NULL; // will have no more commands in the sequence + if (queued_commands_P != NULL) { + // Get the next gcode to run + size_t i = 0; + char c; + while ((c = queued_commands_P[i++]) && c != '\n') { }; + if (i > 1) { + char cmd[i]; + strncpy_P(cmd, queued_commands_P, i - 1); + cmd[i - 1] = '\0'; + if (enqueue_and_echo_command(cmd)) { // buffer was not full (else we will retry later) + if (c) + queued_commands_P += i; // move to next command + else + queued_commands_P = NULL; // no more commands in the sequence + } + } } - return true; + return (queued_commands_P != NULL); // any more left to add? } /**