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