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/>.
12 #include "libs/nuts_bolts.h"
13 #include "libs/Module.h"
14 #include "libs/Kernel.h"
15 #include "StepTicker.h"
16 #include "system_LPC17xx.h" // mbed.h lib
18 // StepTicker handles the base frequency ticking for the Stepper Motors / Actuators
19 // It has a list of those, and calls their tick() functions at regular intervals
20 // They then do Bresenham stuff themselves
22 StepTicker
* global_step_ticker
;
24 StepTicker::StepTicker(){
25 global_step_ticker
= this;
26 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
27 LPC_TIM0
->MCR
= 3; // Match on MR0, reset on MR0, match on MR1
28 LPC_TIM0
->TCR
= 1; // Enable interrupt
30 LPC_SC
->PCONP
|= (1 << 2); // Power Ticker ON
31 LPC_TIM1
->MR0
= 1000000;
33 LPC_TIM1
->TCR
= 1; // Enable interrupt
35 // Default start values
38 this->set_frequency(0.001);
39 this->set_reset_delay(100);
40 this->last_duration
= 0;
41 this->active_motors
[0] = NULL
;
43 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
44 NVIC_EnableIRQ(TIMER1_IRQn
); // Enable interrupt handler
47 // Set the base stepping frequency
48 void StepTicker::set_frequency( double frequency
){
49 this->frequency
= frequency
;
50 this->period
= int(floor((SystemCoreClock
/4)/frequency
)); // SystemCoreClock/4 = Timer increments in a second
51 LPC_TIM0
->MR0
= this->period
;
52 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
53 LPC_TIM0
->TCR
= 3; // Reset
54 LPC_TIM0
->TCR
= 1; // Reset
58 // Set the reset delay
59 void StepTicker::set_reset_delay( double seconds
){
60 this->delay
= int(floor(double(SystemCoreClock
/4)*( seconds
))); // SystemCoreClock/4 = Timer increments in a second
61 LPC_TIM1
->MR0
= this->delay
;
64 // Add a stepper motor object to our list of steppers we must take care of
65 StepperMotor
* StepTicker::add_stepper_motor(StepperMotor
* stepper_motor
){
66 this->stepper_motors
.push_back(stepper_motor
);
67 stepper_motor
->step_ticker
= this;
68 this->has_axes
= true;
72 inline void StepTicker::tick(){
73 uint8_t current_id
= 0;
74 StepperMotor
* current
= this->active_motors
[0];
75 while(current
!= NULL
){
78 current
= this->active_motors
[current_id
];
82 inline void StepTicker::reset_tick(){
84 uint8_t current_id
= 0;
85 StepperMotor
* current
= this->active_motors
[0];
86 while(current
!= NULL
){
87 current
->step_pin
->set(0);
88 if( current
->exit_tick
){
89 if( current
->remove_from_active_list_next_tick
){
90 current
->remove_from_active_list_next_tick
= false;
92 this->remove_motor_from_active_list(current
);
97 current
= this->active_motors
[current_id
];
101 extern "C" void TIMER1_IRQHandler (void){
102 LPC_TIM1
->IR
|= 1 << 0;
103 global_step_ticker
->reset_tick();
106 // The actual interrupt handler where we do all the work
107 extern "C" void TIMER0_IRQHandler (void){
109 LPC_GPIO1
->FIOSET
= 1<<18;
111 uint32_t start_time
= LPC_TIM0
->TC
;
113 LPC_TIM0
->IR
|= 1 << 0;
115 global_step_ticker
->debug
++;
117 // If no axes enabled, just ignore for now
118 if( !global_step_ticker
->has_axes
){
120 LPC_GPIO1
->FIOCLR
= 1<<18;
125 // Do not get out of here before everything is nice and tidy
126 LPC_TIM0
->MR0
= 2000000;
129 global_step_ticker
->tick();
134 // If we went over the duration an interrupt is supposed to last, we have a problem
135 // That can happen tipically when we change blocks, where more than usual computation is done
136 // This can be OK, if we take notice of it, which we do now
137 if( LPC_TIM0
->TC
> global_step_ticker
->period
){ // TODO : remove the size condition
139 uint32_t start_tc
= LPC_TIM0
->TC
;
141 // 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 )
142 uint32_t ticks_to_skip
= ( ( LPC_TIM0
->TC
+ global_step_ticker
->last_duration
) / global_step_ticker
->period
);
144 // Next step is now to reduce this to how many steps we can *actually* skip
145 uint32_t ticks_we_actually_can_skip
= ticks_to_skip
;
146 for(unsigned int i
=0; i
< global_step_ticker
->stepper_motors
.size(); i
++){
147 StepperMotor
* stepper
= global_step_ticker
->stepper_motors
[i
];
148 if( stepper
->moving
){ ticks_we_actually_can_skip
= min( ticks_we_actually_can_skip
, (uint32_t)((uint64_t)( (uint64_t)stepper
->fx_ticks_per_step
- (uint64_t)stepper
->fx_counter
) >> 32) ); }
151 // If the number of ticks we can actually skip is smaller than the number we wanted to skip, there is something wrong in the settings
152 //if( ticks_we_actually_can_skip < ticks_to_skip ){ }
154 // Adding to MR0 for this time is not enough, we must also increment the counters ourself artificially
155 for(unsigned int i
=0; i
< global_step_ticker
->stepper_motors
.size(); i
++){
156 StepperMotor
* stepper
= global_step_ticker
->stepper_motors
[i
];
157 if( stepper
->moving
){ stepper
->fx_counter
+= (uint64_t)((uint64_t)(ticks_we_actually_can_skip
)<<32); }
160 // When must we have our next MR0 ? ( +1 is here to account that we are actually a legit MR0 too, not only overtime )
161 LPC_TIM0
->MR0
= ( ticks_we_actually_can_skip
+ 1 ) * global_step_ticker
->period
;
162 //LPC_TIM0->MR0 = ( ticks_to_skip + 1 ) * global_step_ticker->period;
164 // This is so that we know how long this computation takes, and we can take it into account next time
165 global_step_ticker
->last_duration
= LPC_TIM0
->TC
- start_tc
;
169 LPC_TIM0
->MR0
= global_step_ticker
->period
;
174 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
175 // LPC_TIM0->TCR = 3; // Reset
176 // LPC_TIM0->TCR = 1; // Reset
177 LPC_TIM0
->TC
= LPC_TIM0
->MR0
- 2;
181 LPC_GPIO1
->FIOCLR
= 1<<18;
186 // We make a list of steppers that want to be called so that we don't call them for nothing
187 void StepTicker::add_motor_to_active_list(StepperMotor
* motor
){
188 //printf("adding %p with exit: %u \r\n", motor, motor->exit_tick );
189 uint8_t current_id
= 0;
190 StepperMotor
* current
= this->active_motors
[0];
191 while(current
!= NULL
){
192 if( current
== motor
){
193 return; // Motor already in list
196 current
= this->active_motors
[current_id
];
198 this->active_motors
[current_id
] = motor
;
199 this->active_motors
[current_id
+1] = NULL
;
203 void StepTicker::remove_motor_from_active_list(StepperMotor
* motor
){
204 //printf("removing %p with exit: %u \r\n", motor, motor->exit_tick );
205 uint8_t current_id
= 0;
207 StepperMotor
* current
= this->active_motors
[0];
208 while( current
!= NULL
){
209 if( current
== motor
){ offset
++; }
210 this->active_motors
[current_id
] = this->active_motors
[current_id
+offset
];
212 current
= this->active_motors
[current_id
+offset
];
214 this->active_motors
[current_id
] = NULL
;