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