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/>.
8 #include "libs/Module.h"
9 #include "libs/Kernel.h"
16 #include "libs/nuts_bolts.h"
21 this->current_block
= NULL
;
22 this->step_events_completed
= 0;
26 //Called when the module has just been loaded
27 void Stepper::on_module_loaded(){
29 this->register_for_event(ON_BLOCK_BEGIN
);
30 this->register_for_event(ON_BLOCK_END
);
31 this->register_for_event(ON_PLAY
);
32 this->register_for_event(ON_PAUSE
);
35 this->on_config_reload(this);
37 // Acceleration ticker
38 this->kernel
->slow_ticker
->set_frequency(this->acceleration_ticks_per_second
);
39 this->kernel
->slow_ticker
->attach( this, &Stepper::trapezoid_generator_tick
);
41 // Initiate main_interrupt timer and step reset timer
42 LPC_TIM0
->MR0
= 10000;
43 LPC_TIM0
->MCR
= 11; // for MR0 and MR1, with no reset at MR1
44 NVIC_EnableIRQ(TIMER0_IRQn
);
45 NVIC_SetPriority(TIMER3_IRQn
, 4);
46 NVIC_SetPriority(TIMER0_IRQn
, 1);
47 NVIC_SetPriority(TIMER2_IRQn
, 2);
48 NVIC_SetPriority(TIMER1_IRQn
, 3);
52 // Step and Dir pins as outputs
53 this->step_gpio_port
->FIODIR
|= this->step_mask
;
54 this->dir_gpio_port
->FIODIR
|= this->dir_mask
;
58 // Get configuration from the config file
59 void Stepper::on_config_reload(void* argument
){
60 LPC_GPIO_TypeDef
*gpios
[5] ={LPC_GPIO0
,LPC_GPIO1
,LPC_GPIO2
,LPC_GPIO3
,LPC_GPIO4
};
61 this->microseconds_per_step_pulse
= this->kernel
->config
->value(microseconds_per_step_pulse_ckeckusm
)->by_default(5 )->as_number();
62 this->acceleration_ticks_per_second
= this->kernel
->config
->value(acceleration_ticks_per_second_checksum
)->by_default(100 )->as_number();
63 this->minimum_steps_per_minute
= this->kernel
->config
->value(minimum_steps_per_minute_checksum
)->by_default(1200 )->as_number();
64 this->base_stepping_frequency
= this->kernel
->config
->value(base_stepping_frequency_checksum
)->by_default(100000)->as_number();
65 this->step_gpio_port
= gpios
[(int)this->kernel
->config
->value(step_gpio_port_checksum
)->by_default(0 )->as_number()];
66 this->dir_gpio_port
= gpios
[(int)this->kernel
->config
->value(dir_gpio_port_checksum
)->by_default(0 )->as_number()];
67 this->alpha_step_pin
= this->kernel
->config
->value(alpha_step_pin_checksum
)->by_default(1 )->as_number();
68 this->beta_step_pin
= this->kernel
->config
->value(beta_step_pin_checksum
)->by_default(2 )->as_number();
69 this->gamma_step_pin
= this->kernel
->config
->value(gamma_step_pin_checksum
)->by_default(3 )->as_number();
70 this->alpha_dir_pin
= this->kernel
->config
->value(alpha_dir_pin_checksum
)->by_default(4 )->as_number();
71 this->beta_dir_pin
= this->kernel
->config
->value(beta_dir_pin_checksum
)->by_default(5 )->as_number();
72 this->gamma_dir_pin
= this->kernel
->config
->value(gamma_dir_pin_checksum
)->by_default(6 )->as_number();
73 this->step_mask
= ( 1 << this->alpha_step_pin
) + ( 1 << this->beta_step_pin
) + ( 1 << this->gamma_step_pin
);
74 this->dir_mask
= ( 1 << this->alpha_dir_pin
) + ( 1 << this->beta_dir_pin
) + ( 1 << this->gamma_dir_pin
);
75 this->step_bits
[ALPHA_STEPPER
] = this->alpha_step_pin
;
76 this->step_bits
[BETA_STEPPER
] = this->beta_step_pin
;
77 this->step_bits
[GAMMA_STEPPER
] = this->gamma_step_pin
;
78 this->step_invert_mask
= ( this->kernel
->config
->value( alpha_step_pin_checksum
)->is_inverted() << this->alpha_step_pin
) +
79 ( this->kernel
->config
->value( beta_step_pin_checksum
)->is_inverted() << this->beta_step_pin
) +
80 ( this->kernel
->config
->value( gamma_step_pin_checksum
)->is_inverted() << this->gamma_step_pin
);
81 this->dir_invert_mask
= ( this->kernel
->config
->value( alpha_dir_pin_checksum
)->is_inverted() << this->alpha_dir_pin
) +
82 ( this->kernel
->config
->value( beta_dir_pin_checksum
)->is_inverted() << this->beta_dir_pin
) +
83 ( this->kernel
->config
->value( gamma_dir_pin_checksum
)->is_inverted() << this->gamma_dir_pin
);
85 // Set the Timer interval for Match Register 1,
86 LPC_TIM0
->MR1
= (( SystemCoreClock
/4 ) / 1000000 ) * this->microseconds_per_step_pulse
;
89 // When the play/pause button is set to pause, or a module calls the ON_PAUSE event
90 void Stepper::on_pause(void* argument
){
95 // When the play/pause button is set to play, or a module calls the ON_PLAY event
96 void Stepper::on_play(void* argument
){
101 // A new block is popped from the queue
102 void Stepper::on_block_begin(void* argument
){
103 Block
* block
= static_cast<Block
*>(argument
);
105 // The stepper does not care about 0-blocks
106 if( block
->millimeters
== 0.0 ){ return; }
108 // Mark the new block as of interrest to us
112 for( int stpr
=ALPHA_STEPPER
; stpr
<=GAMMA_STEPPER
; stpr
++){ this->counters
[stpr
] = 0; this->stepped
[stpr
] = 0; }
113 this->step_events_completed
= 0;
115 this->current_block
= block
;
117 // This is just to save computing power and not do it every step
118 this->update_offsets();
120 // Setup acceleration for this block
121 this->trapezoid_generator_reset();
125 // Current block is discarded
126 void Stepper::on_block_end(void* argument
){
127 Block
* block
= static_cast<Block
*>(argument
);
128 LPC_TIM0
->MR0
= 1000000;
129 this->current_block
= NULL
; //stfu !
133 // MR0 is used to call the main stepping interrupt, and MR1 to reset the stepping pins
134 extern "C" void TIMER0_IRQHandler (void){
135 if((LPC_TIM0
->IR
>> 1) & 1){
136 LPC_TIM0
->IR
|= 1 << 1;
138 stepper
->step_gpio_port
->FIOSET
= stepper
->step_invert_mask
& stepper
->step_mask
;
139 stepper
->step_gpio_port
->FIOCLR
= ~stepper
->step_invert_mask
& stepper
->step_mask
;
141 if((LPC_TIM0
->IR
>> 0) & 1){
142 LPC_TIM0
->IR
|= 1 << 0;
143 stepper
->main_interrupt();
147 // "The Stepper Driver Interrupt" - This timer interrupt is the workhorse of Smoothie. It is executed at the rate set with
148 // config_step_timer. It pops blocks from the block_buffer and executes them by pulsing the stepper pins appropriately.
149 inline void Stepper::main_interrupt(){
151 // TODO: Explain this magic
152 // Step dir pins first, then step pinse, stepper drivers like to know the direction before the step signal comes in
154 this->dir_gpio_port
->FIOSET
= this->dir_invert_mask
& this->dir_mask
;
155 this->dir_gpio_port
->FIOCLR
= ~ this->dir_invert_mask
& this->dir_mask
;
157 this->dir_gpio_port
->FIOSET
= ( this->out_bits
^ this->dir_invert_mask
) & this->dir_mask
;
158 this->dir_gpio_port
->FIOCLR
= ( ~ ( this->out_bits
^ this->dir_invert_mask
) ) & this->dir_mask
;
160 this->step_gpio_port
->FIOSET
= ( this->out_bits
^ this->step_invert_mask
) & this->step_mask
;
161 this->step_gpio_port
->FIOCLR
= ( ~ ( this->out_bits
^ this->step_invert_mask
) ) & this->step_mask
;
163 if( this->current_block
!= NULL
){
164 // Set bits for direction and steps
165 this->out_bits
= this->current_block
->direction_bits
;
166 for( int stpr
=ALPHA_STEPPER
; stpr
<=GAMMA_STEPPER
; stpr
++){
167 this->counters
[stpr
] += this->counter_increment
;
168 if( this->counters
[stpr
] > this->offsets
[stpr
] && this->stepped
[stpr
] < this->current_block
->steps
[stpr
] ){
169 this->counters
[stpr
] -= this->offsets
[stpr
] ;
170 this->stepped
[stpr
]++;
171 this->out_bits
|= (1 << this->step_bits
[stpr
]);
174 // If current block is finished, reset pointer
175 this->step_events_completed
+= this->counter_increment
;
176 if( this->step_events_completed
>= this->current_block
->steps_event_count
<<16 ){
177 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
] ){
178 if( this->current_block
!= NULL
){
179 this->current_block
->release();
189 // We compute this here instead of each time in the interrupt
190 void Stepper::update_offsets(){
191 for( int stpr
=ALPHA_STEPPER
; stpr
<=GAMMA_STEPPER
; stpr
++){
192 this->offsets
[stpr
] = (int)floor((float)((float)(1<<16)*(float)((float)this->current_block
->steps_event_count
/ (float)this->current_block
->steps
[stpr
])));
196 // This is called ACCELERATION_TICKS_PER_SECOND times per second by the step_event
197 // interrupt. It can be assumed that the trapezoid-generator-parameters and the
198 // current_block stays untouched by outside handlers for the duration of this function call.
199 void Stepper::trapezoid_generator_tick() {
200 if(this->current_block
&& !this->trapezoid_generator_busy
) {
201 if(this->step_events_completed
< this->current_block
->accelerate_until
<<16) {
202 this->trapezoid_adjusted_rate
+= this->current_block
->rate_delta
;
203 if (this->trapezoid_adjusted_rate
> this->current_block
->nominal_rate
) {
204 this->trapezoid_adjusted_rate
= this->current_block
->nominal_rate
;
206 this->set_step_events_per_minute(this->trapezoid_adjusted_rate
);
207 } else if (this->step_events_completed
>= this->current_block
->decelerate_after
<<16) {
208 // NOTE: We will only reduce speed if the result will be > 0. This catches small
209 // rounding errors that might leave steps hanging after the last trapezoid tick.
210 if (this->trapezoid_adjusted_rate
> double(this->current_block
->rate_delta
) * 1.5) {
211 this->trapezoid_adjusted_rate
-= this->current_block
->rate_delta
;
213 this->trapezoid_adjusted_rate
= floor(double(this->trapezoid_adjusted_rate
/ 2 ));
215 if (this->trapezoid_adjusted_rate
< this->current_block
->final_rate
) {
216 this->trapezoid_adjusted_rate
= this->current_block
->final_rate
;
218 this->set_step_events_per_minute(this->trapezoid_adjusted_rate
);
220 // Make sure we cruise at exactly nominal rate
221 if (this->trapezoid_adjusted_rate
!= this->current_block
->nominal_rate
) {
222 this->trapezoid_adjusted_rate
= this->current_block
->nominal_rate
;
223 this->set_step_events_per_minute(this->trapezoid_adjusted_rate
);
229 // Initializes the trapezoid generator from the current block. Called whenever a new
231 void Stepper::trapezoid_generator_reset(){
232 this->trapezoid_adjusted_rate
= this->current_block
->initial_rate
;
233 this->trapezoid_tick_cycle_counter
= 0;
234 // Because this can be called directly from the main loop, it could be interrupted by the acceleration ticker, and that would be bad, so we use a flag
235 this->trapezoid_generator_busy
= true;
236 this->set_step_events_per_minute(this->trapezoid_adjusted_rate
);
237 this->trapezoid_generator_busy
= false;
240 void Stepper::set_step_events_per_minute( double steps_per_minute
){
242 // We do not step slower than this
243 if( steps_per_minute
< this->minimum_steps_per_minute
){ steps_per_minute
= this->minimum_steps_per_minute
; }
245 // The speed factor is the factor by which we must multiply the minimal step frequency to reach the maximum step frequency
246 // The higher, the smoother the movement will be, but the closer the maximum and minimum frequencies are, the smaller the factor is
247 double speed_factor
= this->base_stepping_frequency
/ (steps_per_minute
/60L);
248 if( speed_factor
< 1 ){ speed_factor
=1; }
249 this->counter_increment
= int(floor((1<<16)/floor(speed_factor
)));
251 // Set the Timer interval
252 LPC_TIM0
->MR0
= floor( ( SystemCoreClock
/4 ) / ( (steps_per_minute
/60L) * speed_factor
) );
254 if( LPC_TIM0
->MR0
< 150 ){
256 this->kernel
->serial
->printf("tim0mr0: %d, steps_per minute: %f \r\n", LPC_TIM0
->MR0
, steps_per_minute
);
259 // In case we change the Match Register to a value the Timer Counter has past
260 if( LPC_TIM0
->TC
>= LPC_TIM0
->MR0
){ LPC_TIM0
->TCR
= 3; LPC_TIM0
->TCR
= 1; }
262 this->kernel
->call_event(ON_SPEED_CHANGE
, this);