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
Extruder.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). 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 #include "Extruder.h" 00009 00010 #include "libs/Module.h" 00011 #include "libs/Kernel.h" 00012 00013 #include "modules/robot/Conveyor.h" 00014 #include "modules/robot/Block.h" 00015 #include "StepperMotor.h" 00016 00017 #include <mri.h> 00018 00019 #define extruder_module_enable_checksum CHECKSUM("extruder_module_enable") 00020 #define extruder_steps_per_mm_checksum CHECKSUM("extruder_steps_per_mm") 00021 #define extruder_acceleration_checksum CHECKSUM("extruder_acceleration") 00022 #define extruder_step_pin_checksum CHECKSUM("extruder_step_pin") 00023 #define extruder_dir_pin_checksum CHECKSUM("extruder_dir_pin") 00024 #define extruder_en_pin_checksum CHECKSUM("extruder_en_pin") 00025 #define extruder_max_speed_checksum CHECKSUM("extruder_max_speed") 00026 00027 #define extruder_checksum CHECKSUM("extruder") 00028 00029 #define default_feed_rate_checksum CHECKSUM("default_feed_rate") 00030 #define steps_per_mm_checksum CHECKSUM("steps_per_mm") 00031 #define acceleration_checksum CHECKSUM("acceleration") 00032 #define step_pin_checksum CHECKSUM("step_pin") 00033 #define dir_pin_checksum CHECKSUM("dir_pin") 00034 #define en_pin_checksum CHECKSUM("en_pin") 00035 #define max_speed_checksum CHECKSUM("max_speed") 00036 00037 #define max(a,b) (((a) > (b)) ? (a) : (b)) 00038 00039 /* The extruder module controls a filament extruder for 3D printing: http://en.wikipedia.org/wiki/Fused_deposition_modeling 00040 * It can work in two modes : either the head does not move, and the extruder moves the filament at a specified speed ( SOLO mode here ) 00041 * or the head moves, and the extruder moves plastic at a speed proportional to the movement of the head ( FOLLOW mode here ). 00042 */ 00043 00044 Extruder::Extruder( uint16_t config_identifier ) { 00045 this->absolute_mode = true; 00046 this->paused = false; 00047 this->single_config = false; 00048 this->identifier = config_identifier; 00049 } 00050 00051 void Extruder::on_module_loaded() { 00052 00053 // Settings 00054 this->on_config_reload(this); 00055 00056 // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one 00057 register_for_event(ON_CONFIG_RELOAD); 00058 this->register_for_event(ON_BLOCK_BEGIN); 00059 this->register_for_event(ON_BLOCK_END); 00060 this->register_for_event(ON_GCODE_RECEIVED); 00061 this->register_for_event(ON_GCODE_EXECUTE); 00062 this->register_for_event(ON_PLAY); 00063 this->register_for_event(ON_PAUSE); 00064 this->register_for_event(ON_SPEED_CHANGE); 00065 00066 // Start values 00067 this->target_position = 0; 00068 this->current_position = 0; 00069 this->unstepped_distance = 0; 00070 this->current_block = NULL; 00071 this->mode = OFF; 00072 00073 // Update speed every *acceleration_ticks_per_second* 00074 // TODO: Make this an independent setting 00075 THEKERNEL->slow_ticker->attach( THEKERNEL->stepper->acceleration_ticks_per_second , this, &Extruder::acceleration_tick ); 00076 00077 // Stepper motor object for the extruder 00078 this->stepper_motor = THEKERNEL->step_ticker->add_stepper_motor( new StepperMotor(step_pin, dir_pin, en_pin) ); 00079 this->stepper_motor->attach(this, &Extruder::stepper_motor_finished_move ); 00080 00081 } 00082 00083 // Get config 00084 void Extruder::on_config_reload(void* argument){ 00085 00086 // If this module uses the old "single extruder" configuration style 00087 if( this->single_config ){ 00088 00089 this->steps_per_millimeter = THEKERNEL->config->value(extruder_steps_per_mm_checksum )->by_default(1)->as_number(); 00090 this->acceleration = THEKERNEL->config->value(extruder_acceleration_checksum )->by_default(1000)->as_number(); 00091 this->max_speed = THEKERNEL->config->value(extruder_max_speed_checksum )->by_default(1000)->as_number(); 00092 this->feed_rate = THEKERNEL->config->value(default_feed_rate_checksum )->by_default(1000)->as_number(); 00093 00094 this->step_pin.from_string( THEKERNEL->config->value(extruder_step_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00095 this->dir_pin.from_string( THEKERNEL->config->value(extruder_dir_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00096 this->en_pin.from_string( THEKERNEL->config->value(extruder_en_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00097 00098 }else{ 00099 // If this module was created with the new multi extruder configuration style 00100 00101 this->steps_per_millimeter = THEKERNEL->config->value(extruder_checksum, this->identifier, steps_per_mm_checksum )->by_default(1)->as_number(); 00102 this->acceleration = THEKERNEL->config->value(extruder_checksum, this->identifier, acceleration_checksum )->by_default(1000)->as_number(); 00103 this->max_speed = THEKERNEL->config->value(extruder_checksum, this->identifier, max_speed_checksum )->by_default(1000)->as_number(); 00104 this->feed_rate = THEKERNEL->config->value( default_feed_rate_checksum )->by_default(1000)->as_number(); 00105 00106 this->step_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, step_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00107 this->dir_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, dir_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00108 this->en_pin.from_string( THEKERNEL->config->value(extruder_checksum, this->identifier, en_pin_checksum )->by_default("nc" )->as_string())->as_output(); 00109 00110 } 00111 00112 } 00113 00114 00115 // When the play/pause button is set to pause, or a module calls the ON_PAUSE event 00116 void Extruder::on_pause(void* argument){ 00117 this->paused = true; 00118 this->stepper_motor->pause(); 00119 } 00120 00121 // When the play/pause button is set to play, or a module calls the ON_PLAY event 00122 void Extruder::on_play(void* argument){ 00123 this->paused = false; 00124 this->stepper_motor->unpause(); 00125 } 00126 00127 00128 void Extruder::on_gcode_received(void *argument){ 00129 Gcode *gcode = static_cast<Gcode*>(argument); 00130 00131 // Gcodes to execute immediately 00132 if (gcode->has_m){ 00133 if (gcode->m == 114){ 00134 gcode->stream->printf("E:%4.1f ", this->current_position); 00135 gcode->add_nl = true; 00136 gcode->mark_as_taken(); 00137 00138 }else if (gcode->m == 92 ){ 00139 float spm = this->steps_per_millimeter; 00140 if (gcode->has_letter('E')) 00141 spm = gcode->get_value('E'); 00142 gcode->stream->printf("E:%g ", spm); 00143 gcode->add_nl = true; 00144 gcode->mark_as_taken(); 00145 00146 }else if (gcode->m == 500 || gcode->m == 503){// M500 saves some volatile settings to config override file, M503 just prints the settings 00147 gcode->stream->printf(";E Steps per mm:\nM92 E%1.4f\n", this->steps_per_millimeter); 00148 gcode->mark_as_taken(); 00149 return; 00150 } 00151 } 00152 00153 // Gcodes to pass along to on_gcode_execute 00154 if( ( gcode->has_m && (gcode->m == 17 || gcode->m == 18 || gcode->m == 82 || gcode->m == 83 || gcode->m == 84 || gcode->m == 92 ) ) || ( gcode->has_g && gcode->g == 92 && gcode->has_letter('E') ) || ( gcode->has_g && ( gcode->g == 90 || gcode->g == 91 ) ) ){ 00155 THEKERNEL->conveyor->append_gcode(gcode); 00156 } 00157 00158 // Add to the queue for on_gcode_execute to process 00159 if( gcode->has_g && gcode->g < 4 && gcode->has_letter('E') ){ 00160 if( !gcode->has_letter('X') && !gcode->has_letter('Y') && !gcode->has_letter('Z') ){ 00161 THEKERNEL->conveyor->append_gcode(gcode); 00162 // This is a solo move, we add an empty block to the queue to prevent subsequent gcodes being executed at the same time 00163 THEKERNEL->conveyor->queue_head_block(); 00164 } 00165 }else{ 00166 // This is for follow move 00167 00168 } 00169 } 00170 00171 // Compute extrusion speed based on parameters and gcode distance of travel 00172 void Extruder::on_gcode_execute(void* argument){ 00173 Gcode* gcode = static_cast<Gcode*>(argument); 00174 00175 // Absolute/relative mode 00176 if( gcode->has_m ){ 00177 if( gcode->m == 17 ){ this->en_pin.set(0); } 00178 if( gcode->m == 18 ){ this->en_pin.set(1); } 00179 if( gcode->m == 82 ){ this->absolute_mode = true; } 00180 if( gcode->m == 83 ){ this->absolute_mode = false; } 00181 if( gcode->m == 84 ){ this->en_pin.set(1); } 00182 if (gcode->m == 92 ){ 00183 if (gcode->has_letter('E')){ 00184 this->steps_per_millimeter = gcode->get_value('E'); 00185 } 00186 } 00187 } 00188 00189 // The mode is OFF by default, and SOLO or FOLLOW only if we need to extrude 00190 this->mode = OFF; 00191 00192 if( gcode->has_g ){ 00193 // G92: Reset extruder position 00194 if( gcode->g == 92 ){ 00195 gcode->mark_as_taken(); 00196 if( gcode->has_letter('E') ){ 00197 this->current_position = gcode->get_value('E'); 00198 this->target_position = this->current_position; 00199 this->unstepped_distance = 0; 00200 }else if( gcode->get_num_args() == 0){ 00201 this->current_position = 0.0; 00202 this->target_position = this->current_position; 00203 this->unstepped_distance = 0; 00204 } 00205 }else if ((gcode->g == 0) || (gcode->g == 1)){ 00206 // Extrusion length from 'G' Gcode 00207 if( gcode->has_letter('E' )){ 00208 // Get relative extrusion distance depending on mode ( in absolute mode we must substract target_position ) 00209 float extrusion_distance = gcode->get_value('E'); 00210 float relative_extrusion_distance = extrusion_distance; 00211 if (this->absolute_mode) 00212 { 00213 relative_extrusion_distance -= this->target_position; 00214 this->target_position = extrusion_distance; 00215 } 00216 else 00217 { 00218 this->target_position += relative_extrusion_distance; 00219 } 00220 00221 // If the robot is moving, we follow it's movement, otherwise, we move alone 00222 if( fabs(gcode->millimeters_of_travel) < 0.0001 ){ // With floating numbers, we can have 0 != 0 ... beeeh. For more info see : http://upload.wikimedia.org/wikipedia/commons/0/0a/Cain_Henri_Vidal_Tuileries.jpg 00223 this->mode = SOLO; 00224 this->travel_distance = relative_extrusion_distance; 00225 }else{ 00226 // We move proportionally to the robot's movement 00227 this->mode = FOLLOW; 00228 this->travel_ratio = relative_extrusion_distance / gcode->millimeters_of_travel; 00229 // TODO: check resulting flowrate, limit robot speed if it exceeds max_speed 00230 } 00231 00232 this->en_pin.set(0); 00233 } 00234 if (gcode->has_letter('F')) 00235 { 00236 feed_rate = gcode->get_value('F') / THEKERNEL->robot->seconds_per_minute; 00237 if (feed_rate > max_speed) 00238 feed_rate = max_speed; 00239 } 00240 }else if( gcode->g == 90 ){ this->absolute_mode = true; 00241 }else if( gcode->g == 91 ){ this->absolute_mode = false; 00242 } 00243 } 00244 } 00245 00246 // When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing ) 00247 void Extruder::on_block_begin(void* argument){ 00248 Block* block = static_cast<Block*>(argument); 00249 00250 00251 if( this->mode == SOLO ){ 00252 // In solo mode we take the block so we can move even if the stepper has nothing to do 00253 00254 this->current_position += this->travel_distance ; 00255 00256 int steps_to_step = abs(int(floor(this->steps_per_millimeter * (this->travel_distance +this->unstepped_distance) ))); 00257 00258 if ( this->travel_distance > 0 ){ 00259 this->unstepped_distance += this->travel_distance -(steps_to_step/this->steps_per_millimeter); //catch any overflow 00260 } else { 00261 this->unstepped_distance += this->travel_distance +(steps_to_step/this->steps_per_millimeter); //catch any overflow 00262 } 00263 00264 if( steps_to_step != 0 ){ 00265 00266 // We take the block, we have to release it or everything gets stuck 00267 block->take(); 00268 this->current_block = block; 00269 00270 this->stepper_motor->steps_per_second = 0; 00271 this->stepper_motor->move( ( this->travel_distance > 0 ), steps_to_step); 00272 00273 }else{ 00274 this->current_block = NULL; 00275 } 00276 00277 }else if( this->mode == FOLLOW ){ 00278 // In non-solo mode, we just follow the stepper module 00279 this->travel_distance = block->millimeters * this->travel_ratio; 00280 00281 this->current_position += this->travel_distance; 00282 00283 int steps_to_step = abs(int(floor(this->steps_per_millimeter * (this->travel_distance + this->unstepped_distance) ))); 00284 00285 if ( this->travel_distance > 0 ){ 00286 this->unstepped_distance += this->travel_distance -(steps_to_step/this->steps_per_millimeter); //catch any overflow 00287 } else { 00288 this->unstepped_distance += this->travel_distance +(steps_to_step/this->steps_per_millimeter); //catch any overflow 00289 } 00290 00291 if( steps_to_step != 0 ){ 00292 block->take(); 00293 this->current_block = block; 00294 00295 this->stepper_motor->move( ( this->travel_distance > 0 ), steps_to_step ); 00296 this->on_speed_change(0); // initialise speed in case we get called first 00297 }else{ 00298 this->current_block = NULL; 00299 } 00300 00301 }else if( this->mode == OFF ){ 00302 // No movement means we must reset our speed 00303 this->current_block = NULL; 00304 //this->stepper_motor->set_speed(0); 00305 00306 } 00307 00308 } 00309 00310 // When a block ends, pause the stepping interrupt 00311 void Extruder::on_block_end(void* argument){ 00312 this->current_block = NULL; 00313 } 00314 00315 // Called periodically to change the speed to match acceleration or to match the speed of the robot 00316 uint32_t Extruder::acceleration_tick(uint32_t dummy){ 00317 00318 // Avoid trying to work when we really shouldn't ( between blocks or re-entry ) 00319 if( this->current_block == NULL || this->paused || this->mode != SOLO ){ return 0; } 00320 00321 uint32_t current_rate = this->stepper_motor->steps_per_second; 00322 uint32_t target_rate = int(floor(this->feed_rate * this->steps_per_millimeter)); 00323 00324 if( current_rate < target_rate ){ 00325 uint32_t rate_increase = int(floor((this->acceleration/THEKERNEL->stepper->acceleration_ticks_per_second)*this->steps_per_millimeter)); 00326 current_rate = min( target_rate, current_rate + rate_increase ); 00327 } 00328 if( current_rate > target_rate ){ current_rate = target_rate; } 00329 00330 // steps per second 00331 this->stepper_motor->set_speed(max(current_rate, THEKERNEL->stepper->minimum_steps_per_second)); 00332 00333 return 0; 00334 } 00335 00336 // Speed has been updated for the robot's stepper, we must update accordingly 00337 void Extruder::on_speed_change( void* argument ){ 00338 00339 // Avoid trying to work when we really shouldn't ( between blocks or re-entry ) 00340 if( this->current_block == NULL || this->paused || this->mode != FOLLOW || this->stepper_motor->moving != true ){ return; } 00341 00342 /* 00343 * nominal block duration = current block's steps / ( current block's nominal rate ) 00344 * nominal extruder rate = extruder steps / nominal block duration 00345 * actual extruder rate = nominal extruder rate * ( ( stepper's steps per second ) / ( current block's nominal rate ) ) 00346 * or actual extruder rate = ( ( extruder steps * ( current block's nominal_rate ) ) / current block's steps ) * ( ( stepper's steps per second ) / ( current block's nominal rate ) ) 00347 * or simplified : extruder steps * ( stepper's steps per second ) ) / current block's steps 00348 * or even : ( stepper steps per second ) * ( extruder steps / current block's steps ) 00349 */ 00350 00351 this->stepper_motor->set_speed( max( ( THEKERNEL->stepper->trapezoid_adjusted_rate) * ( (float)this->stepper_motor->steps_to_move / (float)this->current_block->steps_event_count ), THEKERNEL->stepper->minimum_steps_per_second ) ); 00352 00353 } 00354 00355 00356 00357 // When the stepper has finished it's move 00358 uint32_t Extruder::stepper_motor_finished_move(uint32_t dummy){ 00359 00360 //printf("extruder releasing\r\n"); 00361 00362 if (this->current_block){ // this should always be true, but sometimes it isn't. TODO: find out why 00363 Block* block = this->current_block; 00364 this->current_block = NULL; 00365 block->release(); 00366 } 00367 return 0; 00368 00369 } 00370
Generated on Tue Jul 12 2022 20:09:01 by
1.7.2
