Don't chain header includes if we don't have to, use predeclaration if we only need...
[clinton/Smoothieware.git] / src / modules / robot / Stepper.cpp
CommitLineData
7b49793d 1/*
5886a464 2 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)
4cff3ded
AW
3 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.
4 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.
7b49793d 5 You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>.
4cff3ded
AW
6*/
7
5673fe39
MM
8#include "Stepper.h"
9
4cff3ded
AW
10#include "libs/Module.h"
11#include "libs/Kernel.h"
4cff3ded 12#include "Planner.h"
3fceb8eb 13#include "Conveyor.h"
5673fe39
MM
14#include "StepperMotor.h"
15
4cff3ded
AW
16#include <vector>
17using namespace std;
5673fe39 18
4cff3ded 19#include "libs/nuts_bolts.h"
2f7d3dba 20#include "libs/Hook.h"
5673fe39 21
db453125
AW
22#include <mri.h>
23
24
edac9072 25// The stepper reacts to blocks that have XYZ movement to transform them into actual stepper motor moves
d337942a 26// TODO: This does accel, accel should be in StepperMotor
edac9072 27
4cff3ded 28Stepper* stepper;
4464301d
AW
29uint32_t previous_step_count;
30uint32_t skipped_speed_updates;
813727fb
AW
31uint32_t speed_ticks_counter;
32
4cff3ded
AW
33Stepper::Stepper(){
34 this->current_block = NULL;
81b547a1 35 this->paused = false;
650ed0a8 36 this->trapezoid_generator_busy = false;
1013f6a3 37 this->force_speed_update = false;
4464301d 38 skipped_speed_updates = 0;
4cff3ded
AW
39}
40
41//Called when the module has just been loaded
42void Stepper::on_module_loaded(){
43 stepper = this;
476dcb96 44 register_for_event(ON_CONFIG_RELOAD);
3a4fa0c1
AW
45 this->register_for_event(ON_BLOCK_BEGIN);
46 this->register_for_event(ON_BLOCK_END);
6b833f7d 47 this->register_for_event(ON_GCODE_EXECUTE);
31e600f6 48 this->register_for_event(ON_GCODE_RECEIVED);
befcf5cc
AW
49 this->register_for_event(ON_PLAY);
50 this->register_for_event(ON_PAUSE);
7b49793d 51
4cff3ded 52 // Get onfiguration
7b49793d 53 this->on_config_reload(this);
4cff3ded 54
ded56b35 55 // Acceleration ticker
314ab8f7 56 this->acceleration_tick_hook = THEKERNEL->slow_ticker->attach( this->acceleration_ticks_per_second, this, &Stepper::trapezoid_generator_tick );
4cff3ded 57
feb204be 58 // Attach to the end_of_move stepper event
314ab8f7
MM
59 THEKERNEL->robot->alpha_stepper_motor->attach(this, &Stepper::stepper_motor_finished_move );
60 THEKERNEL->robot->beta_stepper_motor->attach( this, &Stepper::stepper_motor_finished_move );
61 THEKERNEL->robot->gamma_stepper_motor->attach(this, &Stepper::stepper_motor_finished_move );
4cff3ded
AW
62}
63
2bb8b390 64// Get configuration from the config file
da24d6ae 65void Stepper::on_config_reload(void* argument){
7b49793d 66
314ab8f7 67 this->acceleration_ticks_per_second = THEKERNEL->config->value(acceleration_ticks_per_second_checksum)->by_default(100 )->as_number();
0ac1713f 68 this->minimum_steps_per_second = THEKERNEL->config->value(minimum_steps_per_minute_checksum )->by_default(3000 )->as_number() / 60.0F;
bee725fc 69
5a884140
MM
70 // Steppers start off by default
71 this->turn_enable_pins_off();
da24d6ae
AW
72}
73
befcf5cc
AW
74// When the play/pause button is set to pause, or a module calls the ON_PAUSE event
75void Stepper::on_pause(void* argument){
81b547a1 76 this->paused = true;
314ab8f7
MM
77 THEKERNEL->robot->alpha_stepper_motor->pause();
78 THEKERNEL->robot->beta_stepper_motor->pause();
79 THEKERNEL->robot->gamma_stepper_motor->pause();
befcf5cc
AW
80}
81
82// When the play/pause button is set to play, or a module calls the ON_PLAY event
83void Stepper::on_play(void* argument){
7b49793d 84 // TODO: Re-compute the whole queue for a cold-start
81b547a1 85 this->paused = false;
314ab8f7
MM
86 THEKERNEL->robot->alpha_stepper_motor->unpause();
87 THEKERNEL->robot->beta_stepper_motor->unpause();
88 THEKERNEL->robot->gamma_stepper_motor->unpause();
befcf5cc
AW
89}
90
31e600f6
JM
91void Stepper::on_gcode_received(void* argument){
92 Gcode* gcode = static_cast<Gcode*>(argument);
93 // Attach gcodes to the last block for on_gcode_execute
94 if( gcode->has_m && (gcode->m == 84 || gcode->m == 17 || gcode->m == 18 )) {
e0ee24ed 95 THEKERNEL->conveyor->append_gcode(gcode);
1cf31736 96 }
31e600f6
JM
97}
98
edac9072 99// React to enable/disable gcodes
6b833f7d
MM
100void Stepper::on_gcode_execute(void* argument){
101 Gcode* gcode = static_cast<Gcode*>(argument);
102
e6b5ae25
AW
103 if( gcode->has_m){
104 if( gcode->m == 17 ){
7b49793d 105 this->turn_enable_pins_on();
831bade1 106 }
d3ed57b2 107 if( (gcode->m == 84 || gcode->m == 18) && !gcode->has_letter('E') ){
7b49793d 108 this->turn_enable_pins_off();
6b833f7d
MM
109 }
110 }
111}
112
edac9072 113// Enable steppers
831bade1 114void Stepper::turn_enable_pins_on(){
78d0e16a 115 for (StepperMotor* m : THEKERNEL->robot->actuators)
9c5fa39a 116 m->enable(true);
831bade1
AW
117 this->enable_pins_status = true;
118}
119
edac9072 120// Disable steppers
831bade1 121void Stepper::turn_enable_pins_off(){
78d0e16a 122 for (StepperMotor* m : THEKERNEL->robot->actuators)
9c5fa39a 123 m->enable(false);
831bade1
AW
124 this->enable_pins_status = false;
125}
126
7b49793d 127// A new block is popped from the queue
3a4fa0c1
AW
128void Stepper::on_block_begin(void* argument){
129 Block* block = static_cast<Block*>(argument);
130
131 // The stepper does not care about 0-blocks
1cf31736 132 if( block->millimeters == 0.0F ){ return; }
7b49793d 133
4464301d
AW
134 // Mark the new block as of interrest to us
135 if( block->steps[ALPHA_STEPPER] > 0 || block->steps[BETA_STEPPER] > 0 || block->steps[GAMMA_STEPPER] > 0 ){
136 block->take();
4464301d
AW
137 }else{
138 return;
139 }
813727fb 140
7b49793d 141 // We can't move with the enable pins off
831bade1
AW
142 if( this->enable_pins_status == false ){
143 this->turn_enable_pins_on();
144 }
145
7b49793d 146 // Setup : instruct stepper motors to move
314ab8f7
MM
147 if( block->steps[ALPHA_STEPPER] > 0 ){ THEKERNEL->robot->alpha_stepper_motor->move( ( block->direction_bits >> 0 ) & 1 , block->steps[ALPHA_STEPPER] ); }
148 if( block->steps[BETA_STEPPER ] > 0 ){ THEKERNEL->robot->beta_stepper_motor->move( ( block->direction_bits >> 1 ) & 1 , block->steps[BETA_STEPPER ] ); }
149 if( block->steps[GAMMA_STEPPER] > 0 ){ THEKERNEL->robot->gamma_stepper_motor->move( ( block->direction_bits >> 2 ) & 1 , block->steps[GAMMA_STEPPER] ); }
3a4fa0c1 150
1a2d88eb 151 this->current_block = block;
feb204be 152
7b49793d 153 // Setup acceleration for this block
1a2d88eb 154 this->trapezoid_generator_reset();
ded56b35 155
feb204be 156 // Find the stepper with the more steps, it's the one the speed calculations will want to follow
314ab8f7
MM
157 this->main_stepper = THEKERNEL->robot->alpha_stepper_motor;
158 if( THEKERNEL->robot->beta_stepper_motor->steps_to_move > this->main_stepper->steps_to_move ){ this->main_stepper = THEKERNEL->robot->beta_stepper_motor; }
159 if( THEKERNEL->robot->gamma_stepper_motor->steps_to_move > this->main_stepper->steps_to_move ){ this->main_stepper = THEKERNEL->robot->gamma_stepper_motor; }
feb204be 160
edac9072 161 // Set the initial speed for this move
9883efb8
AW
162 this->trapezoid_generator_tick(0);
163
2f7d3dba 164 // Synchronise the acceleration curve with the stepping
658b8a40 165 this->synchronize_acceleration(0);
2f7d3dba 166
3a4fa0c1
AW
167}
168
169// Current block is discarded
170void Stepper::on_block_end(void* argument){
813727fb 171 this->current_block = NULL; //stfu !
3a4fa0c1 172}
da24d6ae 173
feb204be 174// When a stepper motor has finished it's assigned movement
83ecfc46 175uint32_t Stepper::stepper_motor_finished_move(uint32_t dummy){
81b547a1 176
feb204be 177 // We care only if none is still moving
314ab8f7 178 if( THEKERNEL->robot->alpha_stepper_motor->moving || THEKERNEL->robot->beta_stepper_motor->moving || THEKERNEL->robot->gamma_stepper_motor->moving ){ return 0; }
7b49793d 179
feb204be 180 // This block is finished, release it
4cff3ded 181 if( this->current_block != NULL ){
7b49793d 182 this->current_block->release();
4cff3ded 183 }
7b49793d 184
9e672116 185 return 0;
4cff3ded
AW
186}
187
83ecfc46 188
4cff3ded
AW
189// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event
190// interrupt. It can be assumed that the trapezoid-generator-parameters and the
191// current_block stays untouched by outside handlers for the duration of this function call.
8b8b3339 192uint32_t Stepper::trapezoid_generator_tick( uint32_t dummy ) {
1013f6a3 193
edac9072 194 // Do not do the accel math for nothing
813727fb 195 if(this->current_block && !this->paused && this->main_stepper->moving ) {
1cf31736
JM
196
197 // Store this here because we use it a lot down there
813727fb 198 uint32_t current_steps_completed = this->main_stepper->stepped;
7b49793d 199
edac9072 200 // Do not accel, just set the value
813727fb
AW
201 if( this->force_speed_update ){
202 this->force_speed_update = false;
da947c62 203 this->set_step_events_per_second(this->trapezoid_adjusted_rate);
bd0f7508 204 return 0;
813727fb 205 }
6b080aff 206
edac9072 207 // If we are accelerating
69735c09 208 if(current_steps_completed <= this->current_block->accelerate_until + 1) {
1cf31736 209 // Increase speed
edac9072 210 this->trapezoid_adjusted_rate += this->current_block->rate_delta;
ded56b35
AW
211 if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) {
212 this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
213 }
da947c62 214 this->set_step_events_per_second(this->trapezoid_adjusted_rate);
1cf31736
JM
215
216 // If we are decelerating
2f7d3dba 217 }else if (current_steps_completed > this->current_block->decelerate_after) {
1cf31736 218 // Reduce speed
edac9072 219 // NOTE: We will only reduce speed if the result will be > 0. This catches small
ded56b35 220 // rounding errors that might leave steps hanging after the last trapezoid tick.
da947c62 221 if(this->trapezoid_adjusted_rate > this->current_block->rate_delta * 1.5F) {
47b3f3ae 222 this->trapezoid_adjusted_rate -= this->current_block->rate_delta;
ded56b35 223 }else{
da947c62 224 this->trapezoid_adjusted_rate = this->current_block->rate_delta * 1.5F;
ded56b35 225 }
650ed0a8 226 if(this->trapezoid_adjusted_rate < this->current_block->final_rate ) {
ded56b35 227 this->trapezoid_adjusted_rate = this->current_block->final_rate;
7b49793d 228 }
da947c62 229 this->set_step_events_per_second(this->trapezoid_adjusted_rate);
1cf31736
JM
230
231 // If we are cruising
83ecfc46 232 }else {
ded56b35
AW
233 // Make sure we cruise at exactly nominal rate
234 if (this->trapezoid_adjusted_rate != this->current_block->nominal_rate) {
235 this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
da947c62 236 this->set_step_events_per_second(this->trapezoid_adjusted_rate);
ded56b35 237 }
1013f6a3 238 }
ded56b35 239 }
1013f6a3 240
b852a30c 241 return 0;
4cff3ded
AW
242}
243
83ecfc46
AW
244
245
4cff3ded
AW
246// Initializes the trapezoid generator from the current block. Called whenever a new
247// block begins.
650ed0a8 248inline void Stepper::trapezoid_generator_reset(){
4cff3ded 249 this->trapezoid_adjusted_rate = this->current_block->initial_rate;
1013f6a3 250 this->force_speed_update = true;
4cff3ded 251 this->trapezoid_tick_cycle_counter = 0;
4464301d
AW
252 previous_step_count = 0;
253 skipped_speed_updates = 0;
813727fb 254 speed_ticks_counter = 0;
4cff3ded
AW
255}
256
feb204be 257// Update the speed for all steppers
da947c62
MM
258void Stepper::set_step_events_per_second( float steps_per_second )
259{
7b49793d 260 // We do not step slower than this
0ac1713f 261 //steps_per_second = max(steps_per_second, this->minimum_steps_per_second);
da947c62
MM
262 if( steps_per_second < this->minimum_steps_per_second ){
263 steps_per_second = this->minimum_steps_per_second;
813727fb 264 }
4cff3ded 265
feb204be 266 // Instruct the stepper motors
da947c62
MM
267 if( THEKERNEL->robot->alpha_stepper_motor->moving ){ THEKERNEL->robot->alpha_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[ALPHA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
268 if( THEKERNEL->robot->beta_stepper_motor->moving ){ THEKERNEL->robot->beta_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[BETA_STEPPER ] / (float)this->current_block->steps_event_count ) ); }
269 if( THEKERNEL->robot->gamma_stepper_motor->moving ){ THEKERNEL->robot->gamma_stepper_motor->set_speed( steps_per_second * ( (float)this->current_block->steps[GAMMA_STEPPER] / (float)this->current_block->steps_event_count ) ); }
4cff3ded 270
edac9072 271 // Other modules might want to know the speed changed
314ab8f7 272 THEKERNEL->call_event(ON_SPEED_CHANGE, this);
4464301d 273
4cff3ded
AW
274}
275
7b49793d 276// This function has the role of making sure acceleration and deceleration curves have their
0ac1713f 277// rhythm synchronized. The accel/decel must start at the same moment as the speed update routine
2f7d3dba
AW
278// This is caller in "step just occured" or "block just began" ( step Timer ) context, so we need to be fast.
279// All we do is reset the other timer so that it does what we want
280uint32_t Stepper::synchronize_acceleration(uint32_t dummy){
281
2f7d3dba
AW
282 // No move was done, this is called from on_block_begin
283 // This means we setup the accel timer in a way where it gets called right after
7b49793d 284 // we exit this step interrupt, and so that it is then in synch with
2f7d3dba
AW
285 if( this->main_stepper->stepped == 0 ){
286 // Whatever happens, we must call the accel interrupt asap
287 // Because it will set the initial rate
288 // We also want to synchronize in case we start accelerating or decelerating now
7b49793d
MM
289
290 // Accel interrupt must happen asap
2f7d3dba
AW
291 NVIC_SetPendingIRQ(TIMER2_IRQn);
292 // Synchronize both counters
293 LPC_TIM2->TC = LPC_TIM0->TC;
7b49793d
MM
294
295 // If we start decelerating after this, we must ask the actuator to warn us
2f7d3dba
AW
296 // so we can do what we do in the "else" bellow
297 if( this->current_block->decelerate_after > 0 && this->current_block->decelerate_after < this->main_stepper->steps_to_move ){
298 this->main_stepper->attach_signal_step(this->current_block->decelerate_after, this, &Stepper::synchronize_acceleration);
7b49793d 299 }
2f7d3dba
AW
300 }else{
301 // If we are called not at the first steps, this means we are beginning deceleration
7b49793d 302 NVIC_SetPendingIRQ(TIMER2_IRQn);
2f7d3dba 303 // Synchronize both counters
7b49793d 304 LPC_TIM2->TC = LPC_TIM0->TC;
2f7d3dba
AW
305 }
306
658b8a40 307 return 0;
2f7d3dba
AW
308}
309