Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Fork of Smoothie by
Conveyor.cpp
00001 /* 00002 This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl) with additions from Sungeun K. Jeon (https://github.com/chamnit/grbl) 00003 Smoothie 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. 00004 Smoothie 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. 00005 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. 00006 */ 00007 00008 using namespace std; 00009 #include <vector> 00010 #include "libs/nuts_bolts.h" 00011 #include "libs/RingBuffer.h" 00012 #include "../communication/utils/Gcode.h" 00013 #include "libs/Module.h" 00014 #include "libs/Kernel.h" 00015 #include "Timer.h" // mbed.h lib 00016 #include "wait_api.h" // mbed.h lib 00017 #include "Block.h" 00018 #include "Conveyor.h" 00019 #include "Planner.h" 00020 #include "mri.h" 00021 00022 #define planner_queue_size_checksum CHECKSUM("planner_queue_size") 00023 00024 /* 00025 * The conveyor holds the queue of blocks, takes care of creating them, and starting the executing chain of blocks 00026 * 00027 * The Queue is implemented as a ringbuffer- with a twist 00028 * 00029 * Since delete() is not thread-safe, we must marshall deletable items out of ISR context 00030 * 00031 * To do this, we have implmented a *double* ringbuffer- two ringbuffers sharing the same ring, and one index pointer 00032 * 00033 * as in regular ringbuffers, HEAD always points to a clean, free block. We are free to prepare it as we see fit, at our leisure. 00034 * When the block is fully prepared, we increment the head pointer, and from that point we must not touch it anymore. 00035 * 00036 * also, as in regular ringbuffers, we can 'use' the TAIL block, and increment tail pointer when we're finished with it 00037 * 00038 * Both of these are implemented here- see queue_head_block() (where head is pushed) and on_idle() (where tail is consumed) 00039 * 00040 * The double ring is implemented by adding a third index pointer that lives in between head and tail. We call it gc_pending which describes its function rather than its operation 00041 * 00042 * in ISR context, we use HEAD as the head pointer, and gc_pending as the tail pointer. 00043 * As HEAD increments, ISR context can consume the new blocks which appear, and when we're finished with a block, we increment gc_pending to signal that they're finishd, and ready to be cleaned 00044 * 00045 * in IDLE context, we use gc_pending as the head pointer, and TAIL as the tail pointer. 00046 * When gc_pending != tail, we clean up the tail block (performing ISR-unsafe delete operations) and consume it (increment tail pointer), returning it to the pool of clean, unused blocks which HEAD is allowed to prepare for queueing 00047 * 00048 * Thus, our two ringbuffers exist sharing the one ring of blocks, and we safely marshall used blocks from ISR context to IDLE context for safe cleanup. 00049 */ 00050 00051 Conveyor::Conveyor(){ 00052 gc_pending = queue.tail_i; 00053 running = false; 00054 } 00055 00056 void Conveyor::on_module_loaded(){ 00057 register_for_event(ON_IDLE); 00058 register_for_event(ON_MAIN_LOOP); 00059 register_for_event(ON_CONFIG_RELOAD); 00060 00061 on_config_reload(this); 00062 } 00063 00064 // Delete blocks here, because they can't be deleted in interrupt context ( see Block.cpp:release ) 00065 // note that blocks get cleaned as they come off the tail, so head ALWAYS points to a cleaned block. 00066 void Conveyor::on_idle(void* argument){ 00067 if (queue.tail_i != gc_pending) 00068 { 00069 if (queue.is_empty()) 00070 __debugbreak(); 00071 else 00072 { 00073 // Cleanly delete block 00074 Block* block = queue.tail_ref(); 00075 // block->debug(); 00076 block->clear(); 00077 queue.consume_tail(); 00078 } 00079 } 00080 } 00081 00082 /* 00083 * In on_main_loop, we check whether the queue should be running, but isn't. 00084 * 00085 * The main trigger for this event is other pieces of code adding gcode to a block, but not pushing it. This occurs frequently with gcodes that must be executed at the correct point in the queue, but take zero time to execute. 00086 * Smoothie will happily attach many of such gcodes onto a single block, to save room in the queue. 00087 * 00088 * Any gcode which can potentially take time to execute, or might like to halt the queue MUST push the head block, otherwise gcodes that arrive later may get executed at the same time, and gcode execution order strictness would be violated. 00089 * 00090 * If we get back to main loop context and the block has gcode but isn't pushed, then we can safely push it and start the queue. 00091 * 00092 * 00093 * It's also theoretically possible that a race condition could occur where we pop the final block and stop the queue, while at the same time main loop is pushing head but thinks the queue is running and thus does not start it. 00094 * 00095 * In this case, we start the queue again when execution returns to main loop. 00096 * No stuttering or other visible effects could be caused by this event, as the planner will have set the last block to decelerate to zero, and the new block to accelerate from zero. 00097 * 00098 */ 00099 00100 void Conveyor::on_main_loop(void*) 00101 { 00102 if (running) 00103 return; 00104 00105 if (queue.is_empty()) 00106 { 00107 if (queue.head_ref()->gcodes.size()) 00108 { 00109 queue_head_block(); 00110 ensure_running(); 00111 } 00112 } 00113 else 00114 // queue not empty 00115 ensure_running(); 00116 } 00117 00118 void Conveyor::on_config_reload(void* argument) 00119 { 00120 queue.resize(THEKERNEL->config->value(planner_queue_size_checksum)->by_default(32)->as_number()); 00121 } 00122 00123 void Conveyor::append_gcode(Gcode* gcode) 00124 { 00125 gcode->mark_as_taken(); 00126 queue.head_ref()->append_gcode(gcode); 00127 } 00128 00129 // Process a new block in the queue 00130 void Conveyor::on_block_end(void* block) 00131 { 00132 if (queue.is_empty()) 00133 __debugbreak(); 00134 00135 gc_pending = queue.next(gc_pending); 00136 00137 // Return if queue is empty 00138 if (gc_pending == queue.head_i) 00139 { 00140 running = false; 00141 return; 00142 } 00143 00144 // Get a new block 00145 Block* next = this->queue.item_ref(gc_pending); 00146 00147 next->begin(); 00148 } 00149 00150 // Wait for the queue to be empty 00151 void Conveyor::wait_for_empty_queue() 00152 { 00153 while (!queue.is_empty()) 00154 { 00155 ensure_running(); 00156 THEKERNEL->call_event(ON_IDLE, this); 00157 } 00158 } 00159 00160 /* 00161 * push the pre-prepared head block onto the queue 00162 */ 00163 void Conveyor::queue_head_block() 00164 { 00165 while (queue.is_full()) 00166 { 00167 ensure_running(); 00168 THEKERNEL->call_event(ON_IDLE, this); 00169 } 00170 00171 queue.head_ref()->ready(); 00172 queue.produce_head(); 00173 } 00174 00175 void Conveyor::ensure_running() 00176 { 00177 if (!running) 00178 { 00179 if (gc_pending == queue.head_i) 00180 return; 00181 00182 running = true; 00183 queue.item_ref(gc_pending)->begin(); 00184 } 00185 } 00186 00187 // Debug function 00188 void Conveyor::dump_queue() 00189 { 00190 for (unsigned int index = queue.tail_i, i = 0; true; index = queue.next(index), i++ ) 00191 { 00192 THEKERNEL->streams->printf("block %03d > ", i); 00193 queue.item_ref(index)->debug(); 00194 00195 if (index == queue.head_i) 00196 break; 00197 } 00198 } 00199 00200 // feels hacky, but apparently the way to do it 00201 #include "HeapRing.cpp" 00202 template class HeapRing<Block>;
Generated on Tue Jul 12 2022 20:09:00 by
1.7.2
