smoothie port to mbed online compiler (smoothieware.org)
For documentation, license, ..., please check http://smoothieware.org/
This version has been tested with a 3 axis machine
modules/tools/extruder/Extruder.cpp
- Committer:
- scachat
- Date:
- 2012-07-31
- Revision:
- 0:31e91bb0ef3c
File content as of revision 0:31e91bb0ef3c:
/* This file is part of Smoothie (http://smoothieware.org/). The motion control part is heavily based on Grbl (https://github.com/simen/grbl). 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. 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. You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. */ #include "libs/Module.h" #include "libs/Kernel.h" #include "modules/robot/Player.h" #include "modules/robot/Block.h" #include "modules/tools/extruder/Extruder.h" #define extruder_step_pin_checksum 40763 #define extruder_dir_pin_checksum 57277 #define extruder_en_pin_checksum 8017 Extruder::Extruder() { this->absolute_mode = true; this->direction = 1; this->acceleration_lock = false; this->step_counter = 0; this->counter_increment = 0; this->paused = false; } void Extruder::on_module_loaded() { // Do not do anything if not enabledd if( this->kernel->config->value( extruder_module_enable_checksum )->by_default(false)->as_bool() == false ){ return; } // Settings this->on_config_reload(this); // We work on the same Block as Stepper, so we need to know when it gets a new one and drops one this->register_for_event(ON_BLOCK_BEGIN); this->register_for_event(ON_BLOCK_END); this->register_for_event(ON_GCODE_EXECUTE); this->register_for_event(ON_PLAY); this->register_for_event(ON_PAUSE); // Start values this->start_position = 0; this->target_position = 0; this->current_position = 0; this->current_block = NULL; this->mode = OFF; // Update speed every *acceleration_ticks_per_second* // TODO: Make this an independent setting this->kernel->slow_ticker->attach( this->kernel->stepper->acceleration_ticks_per_second , this, &Extruder::acceleration_tick ); // Initiate main_interrupt timer and step reset timer this->kernel->step_ticker->attach( this, &Extruder::stepping_tick ); this->kernel->step_ticker->reset_attach( this, &Extruder::reset_step_pin ); } // Get config void Extruder::on_config_reload(void* argument){ this->microseconds_per_step_pulse = this->kernel->config->value(microseconds_per_step_pulse_checksum)->by_default(5)->as_number(); this->steps_per_millimeter = this->kernel->config->value(extruder_steps_per_mm_checksum )->by_default(1)->as_number(); this->feed_rate = this->kernel->config->value(default_feed_rate_checksum )->by_default(1)->as_number(); this->acceleration = this->kernel->config->value(acceleration_checksum )->by_default(1)->as_number(); this->step_pin = this->kernel->config->value(extruder_step_pin_checksum )->by_default("1.22" )->as_pin()->as_output(); this->dir_pin = this->kernel->config->value(extruder_dir_pin_checksum )->by_default("1.19" )->as_pin()->as_output(); this->en_pin = this->kernel->config->value(extruder_en_pin_checksum )->by_default("0.19" )->as_pin()->as_output(); } // When the play/pause button is set to pause, or a module calls the ON_PAUSE event void Extruder::on_pause(void* argument){ this->paused = true; } // When the play/pause button is set to play, or a module calls the ON_PLAY event void Extruder::on_play(void* argument){ this->paused = false; } // Compute extrusion speed based on parameters and gcode distance of travel void Extruder::on_gcode_execute(void* argument){ Gcode* gcode = static_cast<Gcode*>(argument); // Absolute/relative mode if( gcode->has_letter('M')){ int code = (int) gcode->get_value('M'); if( code == 82 ){ this->absolute_mode = true; } if( code == 83 ){ this->absolute_mode = false; } if( code == 84 ){ this->en_pin->set(0); } } // The mode is OFF by default, and SOLO or FOLLOW only if we need to extrude this->mode = OFF; if( gcode->has_letter('G') ){ // G92: Reset extruder position if( gcode->get_value('G') == 92 ){ if( gcode->has_letter('E') ){ this->current_position = gcode->get_value('E'); this->target_position = this->current_position; this->start_position = this->current_position; } }else{ // Extrusion length from 'G' Gcode if( gcode->has_letter('E' )){ // Get relative extrusion distance depending on mode ( in absolute mode we must substract target_position ) double relative_extrusion_distance = gcode->get_value('E'); if( this->absolute_mode == true ){ relative_extrusion_distance = relative_extrusion_distance - this->target_position; } // If the robot is moving, we follow it's movement, otherwise, we move alone if( fabs(gcode->millimeters_of_travel) < 0.0001 ){ // With floating numbers, we can have 0 != 0 ... beeeh this->mode = SOLO; this->travel_distance = relative_extrusion_distance; if( gcode->has_letter('F') ){ this->feed_rate = gcode->get_value('F'); } }else{ this->mode = FOLLOW; // We move proportionally to the robot's movement this->travel_ratio = relative_extrusion_distance / gcode->millimeters_of_travel; } this->en_pin->set(1); } } } } // When a new block begins, either follow the robot, or step by ourselves ( or stay back and do nothing ) void Extruder::on_block_begin(void* argument){ Block* block = static_cast<Block*>(argument); if( this->mode == SOLO ){ // In solo mode we take the block so we can move even if the stepper has nothing to do block->take(); this->current_block = block; this->start_position = this->target_position; this->target_position = this->start_position + this->travel_distance ; this->travel_ratio = 0.2; // TODO : Make a real acceleration thing if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; } this->set_speed(int(floor((this->feed_rate/60)*this->steps_per_millimeter)));//Speed in steps per second }else if( this->mode == FOLLOW ){ // In non-solo mode, we just follow the stepper module this->current_block = block; this->start_position = this->target_position; this->target_position = this->start_position + ( this->current_block->millimeters * this->travel_ratio ); if( this->target_position > this->current_position ){ this->direction = 1; }else if( this->target_position < this->current_position ){ this->direction = -1; } this->acceleration_tick(0); } } // When a block ends, pause the stepping interrupt void Extruder::on_block_end(void* argument){ Block* block = static_cast<Block*>(argument); this->current_block = NULL; } // Called periodically to change the speed to match acceleration or to match the speed of the robot uint32_t Extruder::acceleration_tick(uint32_t dummy){ // Avoid trying to work when we really shouldn't ( between blocks or re-entry ) if( this->current_block == NULL || this->acceleration_lock || this->paused ){ return 0; } this->acceleration_lock = true; // In solo mode, we mode independently from the robot if( this->mode == SOLO ){ // TODO : Do real acceleration here this->travel_ratio += 0.03; if( this->travel_ratio > 1 ){ this->travel_ratio = 1; } this->set_speed( int(floor(((this->feed_rate/60)*this->steps_per_millimeter)*this->travel_ratio)) ); // Speed in steps per second // In follow mode we match the speed of the robot, + eventually advance }else if( this->mode == FOLLOW ){ Stepper* stepper = this->kernel->stepper; // Just for convenience // Strategy : // * Find where in the block will the stepper be at the next tick ( if the block will have ended then, don't change speed ) // * Find what position this is for us // * Find what speed we must go at to be at that position for the next acceleration tick // TODO : This works, but PLEASE PLEASE PLEASE if you know a better way to do it, do it better, I don't find this elegant at all, it's just the best I could think of // UPDATE: Yes, this sucks, I have ideas on how to do it better. If this is really bugging you, open a ticket and I'll make it a priority int ticks_forward = 3; // We need to take those values here, and then use those instead of the live values, because using the live values inside the loop can break things ( infinite loops etc ... ) double next_stepper_rate = stepper->trapezoid_adjusted_rate; double step_events_completed = (double(double(stepper->step_events_completed)/double(1<<16))); double position = ( this->current_position - this->start_position ) * this->direction ; double length = fabs( this->start_position - this->target_position ); double last_ratio = -1; // Do the startegy above, but if it does not work, look a bit further and try again, and again ... while(1){ // Find the position where we should be at the next tick double next_ratio = double( step_events_completed + ( next_stepper_rate / 60 / ((double(stepper->acceleration_ticks_per_second)/ticks_forward)) ) ) / double( this->current_block->steps_event_count ); double next_relative_position = ( length * next_ratio ); // Advance // TODO: Proper advance configuration double advance = double(next_stepper_rate) * ( 0.00001 * 0.15 ) * 0.4 ; //double advance = 0; next_relative_position += ( advance ); // TODO : all of those "if->return" is very hacky, we should do the math in a way where most of those don't happen, but that requires doing tons of drawing ... if( last_ratio == next_ratio ){ this->acceleration_lock = false; return 0; }else{ last_ratio = next_ratio; } if( next_ratio == 0 || next_ratio > 1 ){ this->acceleration_lock = false; return 0; } if( ticks_forward > 1000 ){ this->acceleration_lock = false; return 0; } // This is very ugly // Hack : We have not looked far enough, we compute how far ahead we must look to get a relevant value if( position > next_relative_position ){ double far_back = position - next_relative_position; double far_back_ratio = far_back / length; double move_duration = double( this->current_block->steps_event_count ) / ( double(next_stepper_rate) / 60 ) ; double ticks_in_a_move = floor( stepper->acceleration_ticks_per_second * move_duration +0.5); double ratio_per_tick = 1 / ticks_in_a_move; double ticks_to_equilibrium = ceil(far_back_ratio / ratio_per_tick) + 1; ticks_forward += ticks_to_equilibrium; // Because this is a loop, and we can be interrupted by the stepping interrupt, if that interrupt changes block, the new block may not be solo, and we may get trapped into an infinite loop if( this->mode != FOLLOW ){ this->acceleration_lock = false; return 0; } continue; } // Finally, compute the speed to get to that next position double next_absolute_position = this->start_position + ( this->direction * next_relative_position ); double steps_to_next_tick = ( next_relative_position - position ) * this->steps_per_millimeter; double speed_to_next_tick = steps_to_next_tick / ( 1 / double(double(this->kernel->stepper->acceleration_ticks_per_second) / ticks_forward) ); // Change stepping speed this->set_speed( speed_to_next_tick ); this->acceleration_lock = false; return 0; } } this->acceleration_lock = false; return 0; } // Convenience function to set stepping speed void Extruder::set_speed( int steps_per_second ){ if( steps_per_second < 10 ){ steps_per_second = 10; } // TODO : Proper limit config value if( steps_per_second > (this->feed_rate*double(this->steps_per_millimeter))/60 ){ steps_per_second = (this->feed_rate*double(this->steps_per_millimeter))/60; } this->counter_increment = int(floor(double(1<<16)/double(this->kernel->stepper->base_stepping_frequency / steps_per_second))); } inline uint32_t Extruder::stepping_tick(uint32_t dummy){ if( this->paused ){ return 0; } this->step_counter += this->counter_increment; if( this->step_counter > 1<<16 ){ this->step_counter -= 1<<16; // If we still have steps to do // TODO: Step using the same timer as the robot, and count steps instead of absolute float position if( ( this->current_position < this->target_position && this->direction == 1 ) || ( this->current_position > this->target_position && this->direction == -1 ) ){ this->current_position += (double(double(1)/double(this->steps_per_millimeter)))*double(this->direction); this->dir_pin->set((this->direction > 0) ? 1 : 0); this->step_pin->set(1); }else{ // Move finished if( this->mode == SOLO && this->current_block != NULL ){ // In follow mode, the robot takes and releases the block, in solo mode we do this->current_block->release(); } } } return 0; } uint32_t Extruder::reset_step_pin(uint32_t dummy){ this->step_pin->set(0); return 0; }