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