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/>.
9 #include "StepTicker.h"
14 #include "libs/nuts_bolts.h"
15 #include "libs/Module.h"
16 #include "libs/Kernel.h"
17 #include "StepperMotor.h"
19 #include "system_LPC17xx.h" // mbed.h lib
23 // StepTicker handles the base frequency ticking for the Stepper Motors / Actuators
24 // It has a list of those, and calls their tick() functions at regular intervals
25 // They then do Bresenham stuff themselves
27 StepTicker
* global_step_ticker
;
29 StepTicker::StepTicker(){
30 global_step_ticker
= this;
32 // Configure the timer
33 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
34 LPC_TIM0
->MCR
= 3; // Match on MR0, reset on MR0, match on MR1
35 LPC_TIM0
->TCR
= 0; // Disable interrupt
37 LPC_SC
->PCONP
|= (1 << 2); // Power Ticker ON
38 LPC_TIM1
->MR0
= 1000000;
40 LPC_TIM1
->TCR
= 1; // Enable interrupt
42 // Default start values
43 this->moves_finished
= false;
44 this->reset_step_pins
= false;
45 this->set_frequency(0.001);
46 this->set_reset_delay(100);
47 this->last_duration
= 0;
48 for (int i
= 0; i
< 12; i
++){
49 this->active_motors
[i
] = NULL
;
51 this->active_motor_bm
= 0;
53 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
54 NVIC_EnableIRQ(TIMER1_IRQn
); // Enable interrupt handler
57 // Set the base stepping frequency
58 void StepTicker::set_frequency( float frequency
){
59 this->frequency
= frequency
;
60 this->period
= int(floor((SystemCoreClock
/4)/frequency
)); // SystemCoreClock/4 = Timer increments in a second
61 LPC_TIM0
->MR0
= this->period
;
62 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
63 LPC_TIM0
->TCR
= 3; // Reset
64 LPC_TIM0
->TCR
= 1; // Reset
68 // Set the reset delay
69 void StepTicker::set_reset_delay( float seconds
){
70 LPC_TIM1
->MR0
= int(floor(float(SystemCoreClock
/4)*( seconds
))); // SystemCoreClock/4 = Timer increments in a second
73 // Add a stepper motor object to our list of steppers we must take care of
74 StepperMotor
* StepTicker::add_stepper_motor(StepperMotor
* stepper_motor
){
75 this->stepper_motors
.push_back(stepper_motor
);
76 stepper_motor
->step_ticker
= this;
80 // Call tick() on each active motor
81 inline void StepTicker::tick(){
85 // We iterate over each active motor
86 for (i
= 0; i
< 12; i
++, bm
<<= 1){
87 if (this->active_motor_bm
& bm
){
88 this->active_motors
[i
]->tick();
94 // Call signal_mode_finished() on each active motor that asked to be signaled. We do this instead of inside of tick() so that
95 // all tick()s are called before we do the move finishing
96 void StepTicker::signal_moves_finished(){
100 for ( uint8_t motor
= 0; motor
< 12; motor
++, bitmask
<<= 1){
101 if (this->active_motor_bm
& bitmask
){
102 if(this->active_motors
[motor
]->is_move_finished
){
103 this->active_motors
[motor
]->signal_move_finished();
104 if(this->active_motors
[motor
]->moving
== false){
113 this->moves_finished
= false;
115 _isr_context
= false;
118 // Reset step pins on all active motors
119 inline void StepTicker::reset_tick(){
124 for (i
= 0, bm
= 1; i
< 12; i
++, bm
<<= 1)
126 if (this->active_motor_bm
& bm
)
127 this->active_motors
[i
]->unstep();
130 _isr_context
= false;
133 extern "C" void TIMER1_IRQHandler (void){
134 LPC_TIM1
->IR
|= 1 << 0;
135 global_step_ticker
->reset_tick();
138 // The actual interrupt handler where we do all the work
139 extern "C" void TIMER0_IRQHandler (void){
141 // Reset interrupt register
142 LPC_TIM0
->IR
|= 1 << 0;
145 uint16_t bitmask
= 1;
146 for (uint8_t motor
= 0; motor
< 12; motor
++, bitmask
<<= 1){
147 if (global_step_ticker
->active_motor_bm
& bitmask
){
148 global_step_ticker
->active_motors
[motor
]->tick();
152 // We may have set a pin on in this tick, now we start the timer to set it off
153 if( global_step_ticker
->reset_step_pins
){
156 global_step_ticker
->reset_step_pins
= false;
158 // Nothing happened, nothing after this really matters
159 // TODO : This could be a problem when we use Actuators instead of StepperMotors, because this flag is specific to step generation
160 LPC_TIM0
->MR0
= global_step_ticker
->period
;
164 // If a move finished in this tick, we have to tell the actuator to act accordingly
165 if( global_step_ticker
->moves_finished
){
167 // Do not get out of here before everything is nice and tidy
168 LPC_TIM0
->MR0
= 20000000;
170 global_step_ticker
->signal_moves_finished();
172 // If we went over the duration an interrupt is supposed to last, we have a problem
173 // That can happen tipically when we change blocks, where more than usual computation is done
174 // This can be OK, if we take notice of it, which we do now
175 if( LPC_TIM0
->TC
> global_step_ticker
->period
){ // TODO: remove the size condition
177 uint32_t start_tc
= LPC_TIM0
->TC
;
179 // How many ticks we want to skip ( this does not include the current tick, but we add the time we spent doing this computation last time )
180 uint32_t ticks_to_skip
= ( ( LPC_TIM0
->TC
+ global_step_ticker
->last_duration
) / global_step_ticker
->period
);
182 // Next step is now to reduce this to how many steps we can *actually* skip
183 uint32_t ticks_we_actually_can_skip
= ticks_to_skip
;
187 for (i
= 0, bm
= 1; i
< 12; i
++, bm
<<= 1)
189 if (global_step_ticker
->active_motor_bm
& bm
)
190 ticks_we_actually_can_skip
=
191 min(ticks_we_actually_can_skip
,
192 (uint32_t)((uint64_t)( (uint64_t)global_step_ticker
->active_motors
[i
]->fx_ticks_per_step
- (uint64_t)global_step_ticker
->active_motors
[i
]->fx_counter
) >> 32)
196 // Adding to MR0 for this time is not enough, we must also increment the counters ourself artificially
197 for (i
= 0, bm
= 1; i
< 12; i
++, bm
<<= 1)
199 if (global_step_ticker
->active_motor_bm
& bm
)
200 global_step_ticker
->active_motors
[i
]->fx_counter
+= (uint64_t)((uint64_t)(ticks_we_actually_can_skip
)<<32);
203 // When must we have our next MR0 ? ( +1 is here to account that we are actually doing a legit MR0 match here too, not only overtime )
204 LPC_TIM0
->MR0
= ( ticks_to_skip
+ 1 ) * global_step_ticker
->period
;
206 // This is so that we know how long this computation takes, and we can take it into account next time
207 int difference
= (int)(LPC_TIM0
->TC
) - (int)(start_tc
);
208 if( difference
> 0 ){ global_step_ticker
->last_duration
= (uint32_t)difference
; }
211 LPC_TIM0
->MR0
= global_step_ticker
->period
;
214 while( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
215 LPC_TIM0
->MR0
+= global_step_ticker
->period
;
223 // We make a list of steppers that want to be called so that we don't call them for nothing
224 void StepTicker::add_motor_to_active_list(StepperMotor
* motor
)
228 for (i
= 0, bm
= 1; i
< 12; i
++, bm
<<= 1)
230 if (this->active_motors
[i
] == motor
)
232 this->active_motor_bm
|= bm
;
233 if( this->active_motor_bm
!= 0 ){
234 LPC_TIM0
->TCR
= 1; // Enable interrupt
238 if (this->active_motors
[i
] == NULL
)
240 this->active_motors
[i
] = motor
;
241 this->active_motor_bm
|= bm
;
242 if( this->active_motor_bm
!= 0 ){
243 LPC_TIM0
->TCR
= 1; // Enable interrupt
251 // Remove a stepper from the list of active motors
252 void StepTicker::remove_motor_from_active_list(StepperMotor
* motor
)
255 for (i
= 0, bm
= 1; i
< 12; i
++, bm
<<= 1)
257 if (this->active_motors
[i
] == motor
)
259 this->active_motor_bm
&= ~bm
;
260 // If we have no motor to work on, disable the whole interrupt
261 if( this->active_motor_bm
== 0 ){
262 LPC_TIM0
->TCR
= 0; // Disable interrupt