Commit | Line | Data |
---|---|---|
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> | |
14 | using namespace std; | |
15 | #include "libs/nuts_bolts.h" | |
16 | ||
17 | Timeout flipper; | |
18 | Stepper* stepper; | |
19 | ||
20 | Stepper::Stepper(){ | |
21 | this->current_block = NULL; | |
22 | this->divider = 0; | |
23 | } | |
24 | ||
25 | //Called when the module has just been loaded | |
26 | void 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 |
49 | void 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 | |
70 | extern "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 | |
83 | void 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. | |
89 | void 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 | ||
143 | void 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. | |
153 | void 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. | |
189 | void 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 | ||
196 | void 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 |