freeze edge for merging with master
[clinton/Smoothieware.git] / src / modules / robot / Stepper.cpp
CommitLineData
4cff3ded
AW
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).
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 "mbed.h"
13#include <vector>
14using namespace std;
15#include "libs/nuts_bolts.h"
16
17Timeout flipper;
18Stepper* stepper;
19
20Stepper::Stepper(){
21 this->current_block = NULL;
22 this->divider = 0;
23}
24
25//Called when the module has just been loaded
26void Stepper::on_module_loaded(){
27 stepper = this;
28 this->register_for_event(ON_STEPPER_WAKE_UP);
29
30 // Get onfiguration
da24d6ae 31 this->on_config_reload(this);
4cff3ded
AW
32
33 // Acceleration timer
34 this->acceleration_ticker.attach_us(this, &Stepper::trapezoid_generator_tick, 1000000/this->acceleration_ticks_per_second);
35
36 // Initiate main_interrupt timer and step reset timer
37 LPC_TIM0->MR0 = 10000;
38 LPC_TIM0->MR1 = 500;
39 LPC_TIM0->MCR = 11; // for MR0 and MR1, with no reset at MR1
40 NVIC_EnableIRQ(TIMER0_IRQn);
41 NVIC_SetPriority(TIMER3_IRQn, 1);
42
43 // Step and Dir pins as outputs
44 this->step_gpio_port->FIODIR |= this->step_mask;
45 this->dir_gpio_port->FIODIR |= this->dir_mask;
46
47}
48
da24d6ae
AW
49void Stepper::on_config_reload(void* argument){
50 LPC_GPIO_TypeDef *gpios[5] ={LPC_GPIO0,LPC_GPIO1,LPC_GPIO2,LPC_GPIO3,LPC_GPIO4};
51 this->microseconds_per_step_pulse = this->kernel->config->get(microseconds_per_step_pulse_ckeckusm );
52 this->acceleration_ticks_per_second = this->kernel->config->get(acceleration_ticks_per_second_checksum);
53 this->minimum_steps_per_minute = this->kernel->config->get(minimum_steps_per_minute_checksum );
54 this->base_stepping_frequency = this->kernel->config->get(base_stepping_frequency_checksum );
55 this->step_gpio_port = gpios[(int)this->kernel->config->get(step_gpio_port_checksum )];
56 this->dir_gpio_port = gpios[(int)this->kernel->config->get(dir_gpio_port_checksum )];
57 this->alpha_step_pin = this->kernel->config->get(alpha_step_pin_checksum );
58 this->beta_step_pin = this->kernel->config->get(beta_step_pin_checksum );
59 this->gamma_step_pin = this->kernel->config->get(gamma_step_pin_checksum );
60 this->alpha_dir_pin = this->kernel->config->get(alpha_dir_pin_checksum );
61 this->beta_dir_pin = this->kernel->config->get(beta_dir_pin_checksum );
62 this->gamma_dir_pin = this->kernel->config->get(gamma_dir_pin_checksum );
63 this->step_mask = ( 1 << this->alpha_step_pin ) + ( 1 << this->beta_step_pin ) + ( 1 << this->gamma_step_pin );
64 this->dir_mask = ( 1 << this->alpha_dir_pin ) + ( 1 << this->beta_dir_pin ) + ( 1 << this->gamma_dir_pin );
65}
66
67
4cff3ded
AW
68// Timer0 ISR
69// MR0 is used to call the main stepping interrupt, and MR1 to reset the stepping pins
70extern "C" void TIMER0_IRQHandler (void){
71 if((LPC_TIM0->IR >> 1) & 1){
72 LPC_TIM0->IR |= 1 << 1;
73 stepper->step_gpio_port->FIOCLR = stepper->step_mask;
74 }
75 if((LPC_TIM0->IR >> 0) & 1){
76 LPC_TIM0->IR |= 1 << 0;
77 stepper->main_interrupt();
78 }
79}
80
81
82//Called ( apriori only by the planner ) when Stepper asked to wake up : means we have work to do. Argument is the queue so that we can use it. //TODO : Get rid of this
83void Stepper::on_stepper_wake_up(void* argument){
84 LPC_TIM0->TCR = 1;
85}
86
87// "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Smoothie. It is executed at the rate set with
88// config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately.
89void Stepper::main_interrupt(){
90
91 // Step dir pins first, then step pinse, stepper drivers like to know the direction before the step signal comes in
92 this->dir_gpio_port->FIOCLR = this->dir_mask;
93 this->dir_gpio_port->FIOSET = this->out_bits & this->dir_mask;
94 this->step_gpio_port->FIOSET = this->out_bits & this->step_mask;
95
96 // If there is no current block, attempt to pop one from the buffer, if there is none, go to sleep, if there is none, go to sleep
97 if( this->current_block == NULL ){
98 this->current_block = this->kernel->planner->get_current_block();
99 if( this->current_block != NULL ){
100 this->trapezoid_generator_reset();
101 this->update_offsets();
102 for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){ this->counters[stpr] = 0; this->stepped[stpr] = 0; }
103 this->step_events_completed = 0;
104 this->kernel->call_event(ON_BLOCK_BEGIN, this->current_block);
105 }else{
106 // Go to sleep
107 LPC_TIM0->MR0 = 10000;
108 LPC_TIM0->MR1 = 500;
109 LPC_TIM0->TCR = 0;
110 }
111 }
112
113
114 if( this->current_block != NULL ){
115 // Set bits for direction and steps
116 this->out_bits = this->current_block->direction_bits;
117 unsigned short step_bits[3] = {this->alpha_step_pin, this->beta_step_pin, this->gamma_step_pin};
118 for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){
119 this->counters[stpr] += (1<<16)>>this->divider; // Basically += 1/divider if we were in floating point arythmetic, but here the counters precision is 1/(2^16).
120 if( this->counters[stpr] > this->offsets[stpr] && this->stepped[stpr] < this->current_block->steps[stpr] ){
121 this->counters[stpr] -= this->offsets[stpr] ;
122 this->stepped[stpr]++;
123 this->out_bits |= (1 << step_bits[stpr]);
124 }
125 }
126
127 // If current block is finished, reset pointer
128 this->step_events_completed += (1<<16)>>this->divider; // /this->divider;
129 if( this->step_events_completed >= this->current_block->steps_event_count<<16 ){
130 if( this->stepped[ALPHA_STEPPER] == this->current_block->steps[ALPHA_STEPPER] && this->stepped[BETA_STEPPER] == this->current_block->steps[BETA_STEPPER] && this->stepped[GAMMA_STEPPER] == this->current_block->steps[GAMMA_STEPPER] ){
131 this->kernel->call_event(ON_BLOCK_END, this->current_block);
132 this->current_block->pop_and_execute_gcode(this->kernel);
133 this->current_block = NULL;
134 this->kernel->planner->discard_current_block();
135 }
136 }
137
138 }else{
139 this->out_bits = 0;
140 }
141}
142
143void Stepper::update_offsets(){
144 for( int stpr=ALPHA_STEPPER; stpr<=GAMMA_STEPPER; stpr++){
145 this->offsets[stpr] = (int)floor((float)((float)(1<<16)*(float)((float)this->current_block->steps_event_count / (float)this->current_block->steps[stpr])));
146 }
147}
148
149
150// This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event
151// interrupt. It can be assumed that the trapezoid-generator-parameters and the
152// current_block stays untouched by outside handlers for the duration of this function call.
153void Stepper::trapezoid_generator_tick() {
154
155 if( this->trapezoid_generator_busy || this->kernel->planner->computing ){ return; }
156 this->trapezoid_generator_busy = true;
157 if(this->current_block) {
158 if (this->step_events_completed < this->current_block->accelerate_until) {
159 this->trapezoid_adjusted_rate += this->current_block->rate_delta;
160 if (this->trapezoid_adjusted_rate > this->current_block->nominal_rate ) {
161 this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
162 }
163 this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
164 } else if (this->step_events_completed > this->current_block->decelerate_after) {
165 // NOTE: We will only reduce speed if the result will be > 0. This catches small
166 // rounding errors that might leave steps hanging after the last trapezoid tick.
167 if (this->trapezoid_adjusted_rate > this->current_block->rate_delta) {
168 this->trapezoid_adjusted_rate -= this->current_block->rate_delta;
169 }
170 if (this->trapezoid_adjusted_rate < this->current_block->final_rate) {
171 this->trapezoid_adjusted_rate = this->current_block->final_rate;
172 }
173 this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
174 } else {
175 // Make sure we cruise at exactly nominal rate
176 if (this->trapezoid_adjusted_rate != this->current_block->nominal_rate) {
177 this->trapezoid_adjusted_rate = this->current_block->nominal_rate;
178 this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
179 }
180 }
181 }
182 this->trapezoid_generator_busy = false;
183
184}
185
186
187// Initializes the trapezoid generator from the current block. Called whenever a new
188// block begins.
189void Stepper::trapezoid_generator_reset(){
190 this->trapezoid_adjusted_rate = this->current_block->initial_rate;
191 this->trapezoid_tick_cycle_counter = 0;
192 this->set_step_events_per_minute(this->trapezoid_adjusted_rate);
193 this->trapezoid_generator_busy = false;
194}
195
196void Stepper::set_step_events_per_minute( double steps_per_minute ){
197 if( ! this->current_block || this->kernel->planner->computing ){ return; }
198
199 // We do not step slower than this
200 if( steps_per_minute < this->minimum_steps_per_minute ){ steps_per_minute = this->minimum_steps_per_minute; } //TODO: Get from config
201
202 // The speed factor is the factor by which we must multiply the minimal step frequency to reach the maximum step frequency
203 // The higher, the smoother the movement will be, but the closer the maximum and minimum frequencies are, the smaller the factor is
204 double speed_factor = this->base_stepping_frequency / (steps_per_minute/60L); //TODO: Get from config
205 if( speed_factor < 1 ){ speed_factor==1; }
206
207 // Find bit corresponding to the biggest power of two that is smaller than the divider we want
208 // This is used instead of a floating point number for performance reasons
209 int i=0;
210 while(1<<i+1 < speed_factor){ i++; } // Probably not optimal ...
211 this->divider = i;
212
213 // Set the Timer interval
214 LPC_TIM0->MR0 = floor( ( SystemCoreClock/4 ) / ( (steps_per_minute/60L) * (1<<this->divider) ) );
215
216 // In case we change the Match Register to a value the Timer Counter has past
217 if( LPC_TIM0->TC >= LPC_TIM0->MR0 ){ LPC_TIM0->TCR = 3; LPC_TIM0->TCR = 1; }
218
219 // Set the Timer interval for Match Register 1,
220 LPC_TIM0->MR1 = (( SystemCoreClock/4 ) / 1000000 ) * this->microseconds_per_step_pulse;
221
222 this->kernel->call_event(ON_SPEED_CHANGE, this);
223
224}
225