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