Commit | Line | Data |
---|---|---|
7b49793d | 1 | /* |
cd011f58 AW |
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. | |
7b49793d | 5 | You should have received a copy of the GNU General Public License along with Smoothie. If not, see <http://www.gnu.org/licenses/>. |
cd011f58 AW |
6 | */ |
7 | ||
8 | ||
5673fe39 | 9 | #include "StepTicker.h" |
cd011f58 | 10 | |
3b1e82d2 AW |
11 | using namespace std; |
12 | #include <vector> | |
5673fe39 | 13 | |
3b1e82d2 AW |
14 | #include "libs/nuts_bolts.h" |
15 | #include "libs/Module.h" | |
16 | #include "libs/Kernel.h" | |
5673fe39 MM |
17 | #include "StepperMotor.h" |
18 | ||
da3a10b9 | 19 | #include "system_LPC17xx.h" // mbed.h lib |
3b1e82d2 | 20 | |
bd0f7508 AW |
21 | #include <mri.h> |
22 | ||
921bdb42 AW |
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 | |
3b1e82d2 AW |
26 | |
27 | StepTicker* global_step_ticker; | |
28 | ||
29 | StepTicker::StepTicker(){ | |
30 | global_step_ticker = this; | |
93694d6b AW |
31 | |
32 | // Configure the timer | |
8aea2a35 | 33 | LPC_TIM0->MR0 = 10000000; // Initial dummy value for Match Register |
813727fb | 34 | LPC_TIM0->MCR = 3; // Match on MR0, reset on MR0, match on MR1 |
8aea2a35 | 35 | LPC_TIM0->TCR = 0; // Disable interrupt |
796c9f32 | 36 | |
8aea2a35 | 37 | LPC_SC->PCONP |= (1 << 2); // Power Ticker ON |
813727fb AW |
38 | LPC_TIM1->MR0 = 1000000; |
39 | LPC_TIM1->MCR = 1; | |
8aea2a35 | 40 | LPC_TIM1->TCR = 1; // Enable interrupt |
813727fb | 41 | |
7b49793d | 42 | // Default start values |
bd0f7508 AW |
43 | this->moves_finished = false; |
44 | this->reset_step_pins = false; | |
feb204be | 45 | this->debug = 0; |
650ed0a8 AW |
46 | this->has_axes = 0; |
47 | this->set_frequency(0.001); | |
48 | this->set_reset_delay(100); | |
feb204be | 49 | this->last_duration = 0; |
19f961f6 | 50 | for (int i = 0; i < 12; i++){ |
6e0063ab | 51 | this->active_motors[i] = NULL; |
19f961f6 | 52 | } |
6e0063ab | 53 | this->active_motor_bm = 0; |
796c9f32 | 54 | |
921bdb42 | 55 | NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler |
813727fb | 56 | NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler |
3b1e82d2 AW |
57 | } |
58 | ||
921bdb42 | 59 | // Set the base stepping frequency |
1ad23cd3 | 60 | void StepTicker::set_frequency( float frequency ){ |
3b1e82d2 | 61 | this->frequency = frequency; |
7b49793d | 62 | this->period = int(floor((SystemCoreClock/4)/frequency)); // SystemCoreClock/4 = Timer increments in a second |
feb204be | 63 | LPC_TIM0->MR0 = this->period; |
3b1e82d2 AW |
64 | if( LPC_TIM0->TC > LPC_TIM0->MR0 ){ |
65 | LPC_TIM0->TCR = 3; // Reset | |
66 | LPC_TIM0->TCR = 1; // Reset | |
67 | } | |
68 | } | |
69 | ||
921bdb42 | 70 | // Set the reset delay |
1ad23cd3 MM |
71 | void StepTicker::set_reset_delay( float seconds ){ |
72 | this->delay = int(floor(float(SystemCoreClock/4)*( seconds ))); // SystemCoreClock/4 = Timer increments in a second | |
813727fb | 73 | LPC_TIM1->MR0 = this->delay; |
3b1e82d2 AW |
74 | } |
75 | ||
feb204be | 76 | // Add a stepper motor object to our list of steppers we must take care of |
670fa10b | 77 | StepperMotor* StepTicker::add_stepper_motor(StepperMotor* stepper_motor){ |
feb204be | 78 | this->stepper_motors.push_back(stepper_motor); |
7b49793d | 79 | stepper_motor->step_ticker = this; |
650ed0a8 | 80 | this->has_axes = true; |
feb204be AW |
81 | return stepper_motor; |
82 | } | |
83 | ||
6b080aff | 84 | // Call tick() on each active motor |
7b49793d | 85 | inline void StepTicker::tick(){ |
4df07f88 | 86 | _isr_context = true; |
6e0063ab | 87 | int i; |
12aad2a0 AW |
88 | uint32_t bm = 1; |
89 | // We iterate over each active motor | |
90 | for (i = 0; i < 12; i++, bm <<= 1){ | |
91 | if (this->active_motor_bm & bm){ | |
6e0063ab MM |
92 | this->active_motors[i]->tick(); |
93 | } | |
3b1e82d2 | 94 | } |
4df07f88 | 95 | _isr_context = false; |
3b1e82d2 AW |
96 | } |
97 | ||
7b49793d | 98 | // Call signal_mode_finished() on each active motor that asked to be signaled. We do this instead of inside of tick() so that |
6b080aff | 99 | // all tick()s are called before we do the move finishing |
bd0f7508 | 100 | void StepTicker::signal_moves_finished(){ |
4df07f88 MM |
101 | _isr_context = true; |
102 | ||
39a14e08 AG |
103 | uint16_t bitmask = 1; |
104 | for ( uint8_t motor = 0; motor < 12; motor++, bitmask <<= 1){ | |
12800c08 AW |
105 | if (this->active_motor_bm & bitmask){ |
106 | if(this->active_motors[motor]->is_move_finished){ | |
107 | this->active_motors[motor]->signal_move_finished(); | |
108 | if(this->active_motors[motor]->moving == false){ | |
109 | if (motor > 0){ | |
110 | motor--; | |
111 | bitmask >>= 1; | |
6e0063ab MM |
112 | } |
113 | } | |
114 | } | |
bd0f7508 | 115 | } |
bd0f7508 AW |
116 | } |
117 | this->moves_finished = false; | |
4df07f88 MM |
118 | |
119 | _isr_context = false; | |
bd0f7508 AW |
120 | } |
121 | ||
6b080aff | 122 | // Reset step pins on all active motors |
feb204be | 123 | inline void StepTicker::reset_tick(){ |
4df07f88 MM |
124 | _isr_context = true; |
125 | ||
6e0063ab MM |
126 | int i; |
127 | uint32_t bm; | |
128 | for (i = 0, bm = 1; i < 12; i++, bm <<= 1) | |
129 | { | |
130 | if (this->active_motor_bm & bm) | |
9c5fa39a | 131 | this->active_motors[i]->unstep(); |
3b1e82d2 | 132 | } |
4df07f88 MM |
133 | |
134 | _isr_context = false; | |
3b1e82d2 AW |
135 | } |
136 | ||
813727fb | 137 | extern "C" void TIMER1_IRQHandler (void){ |
7b49793d | 138 | LPC_TIM1->IR |= 1 << 0; |
813727fb AW |
139 | global_step_ticker->reset_tick(); |
140 | } | |
141 | ||
921bdb42 | 142 | // The actual interrupt handler where we do all the work |
3b1e82d2 | 143 | extern "C" void TIMER0_IRQHandler (void){ |
b2b0b56d | 144 | |
8aea2a35 | 145 | // Reset interrupt register |
813727fb | 146 | LPC_TIM0->IR |= 1 << 0; |
4464301d | 147 | |
7b49793d | 148 | // Step pins |
39a14e08 AG |
149 | uint16_t bitmask = 1; |
150 | for (uint8_t motor = 0; motor < 12; motor++, bitmask <<= 1){ | |
12800c08 AW |
151 | if (global_step_ticker->active_motor_bm & bitmask){ |
152 | global_step_ticker->active_motors[motor]->tick(); | |
153 | } | |
154 | } | |
4464301d | 155 | |
bd0f7508 AW |
156 | // We may have set a pin on in this tick, now we start the timer to set it off |
157 | if( global_step_ticker->reset_step_pins ){ | |
158 | LPC_TIM1->TCR = 3; | |
159 | LPC_TIM1->TCR = 1; | |
160 | global_step_ticker->reset_step_pins = false; | |
12aad2a0 AW |
161 | }else{ |
162 | // Nothing happened, nothing after this really matters | |
d337942a | 163 | // TODO : This could be a problem when we use Actuators instead of StepperMotors, because this flag is specific to step generation |
8aea2a35 | 164 | LPC_TIM0->MR0 = global_step_ticker->period; |
12aad2a0 | 165 | return; |
bd0f7508 | 166 | } |
19f961f6 | 167 | |
bd0f7508 | 168 | // If a move finished in this tick, we have to tell the actuator to act accordingly |
19f961f6 AW |
169 | if( global_step_ticker->moves_finished ){ |
170 | ||
171 | // Do not get out of here before everything is nice and tidy | |
f550bd20 | 172 | LPC_TIM0->MR0 = 20000000; |
19f961f6 AW |
173 | |
174 | global_step_ticker->signal_moves_finished(); | |
175 | ||
176 | // If we went over the duration an interrupt is supposed to last, we have a problem | |
177 | // That can happen tipically when we change blocks, where more than usual computation is done | |
178 | // This can be OK, if we take notice of it, which we do now | |
179 | if( LPC_TIM0->TC > global_step_ticker->period ){ // TODO: remove the size condition | |
180 | ||
19f961f6 | 181 | uint32_t start_tc = LPC_TIM0->TC; |
19f961f6 AW |
182 | |
183 | // 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 ) | |
184 | uint32_t ticks_to_skip = ( ( LPC_TIM0->TC + global_step_ticker->last_duration ) / global_step_ticker->period ); | |
185 | ||
186 | // Next step is now to reduce this to how many steps we can *actually* skip | |
187 | uint32_t ticks_we_actually_can_skip = ticks_to_skip; | |
188 | ||
189 | int i; | |
190 | uint32_t bm; | |
191 | for (i = 0, bm = 1; i < 12; i++, bm <<= 1) | |
192 | { | |
193 | if (global_step_ticker->active_motor_bm & bm) | |
194 | ticks_we_actually_can_skip = | |
195 | min(ticks_we_actually_can_skip, | |
196 | (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) | |
197 | ); | |
198 | } | |
7b49793d | 199 | |
19f961f6 AW |
200 | // Adding to MR0 for this time is not enough, we must also increment the counters ourself artificially |
201 | for (i = 0, bm = 1; i < 12; i++, bm <<= 1) | |
202 | { | |
203 | if (global_step_ticker->active_motor_bm & bm) | |
204 | global_step_ticker->active_motors[i]->fx_counter += (uint64_t)((uint64_t)(ticks_we_actually_can_skip)<<32); | |
205 | } | |
650ed0a8 | 206 | |
19f961f6 AW |
207 | // 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 ) |
208 | LPC_TIM0->MR0 = ( ticks_to_skip + 1 ) * global_step_ticker->period; | |
650ed0a8 | 209 | |
19f961f6 AW |
210 | // This is so that we know how long this computation takes, and we can take it into account next time |
211 | int difference = (int)(LPC_TIM0->TC) - (int)(start_tc); | |
212 | if( difference > 0 ){ global_step_ticker->last_duration = (uint32_t)difference; } | |
6e0063ab | 213 | |
19f961f6 AW |
214 | }else{ |
215 | LPC_TIM0->MR0 = global_step_ticker->period; | |
813727fb | 216 | } |
650ed0a8 | 217 | |
19f961f6 AW |
218 | while( LPC_TIM0->TC > LPC_TIM0->MR0 ){ |
219 | LPC_TIM0->MR0 += global_step_ticker->period; | |
feb204be AW |
220 | } |
221 | ||
bd0f7508 | 222 | } |
813727fb | 223 | |
3b1e82d2 AW |
224 | } |
225 | ||
bd0f7508 | 226 | |
796c9f32 | 227 | // We make a list of steppers that want to be called so that we don't call them for nothing |
670fa10b | 228 | void StepTicker::add_motor_to_active_list(StepperMotor* motor) |
6e0063ab MM |
229 | { |
230 | uint32_t bm; | |
231 | int i; | |
232 | for (i = 0, bm = 1; i < 12; i++, bm <<= 1) | |
233 | { | |
234 | if (this->active_motors[i] == motor) | |
235 | { | |
236 | this->active_motor_bm |= bm; | |
f550bd20 | 237 | if( this->active_motor_bm != 0 ){ |
8aea2a35 AW |
238 | LPC_TIM0->TCR = 1; // Enable interrupt |
239 | } | |
6e0063ab MM |
240 | return; |
241 | } | |
242 | if (this->active_motors[i] == NULL) | |
243 | { | |
244 | this->active_motors[i] = motor; | |
245 | this->active_motor_bm |= bm; | |
f550bd20 | 246 | if( this->active_motor_bm != 0 ){ |
8aea2a35 AW |
247 | LPC_TIM0->TCR = 1; // Enable interrupt |
248 | } | |
6e0063ab | 249 | return; |
796c9f32 | 250 | } |
796c9f32 | 251 | } |
6e0063ab | 252 | return; |
796c9f32 AW |
253 | } |
254 | ||
6b080aff | 255 | // Remove a stepper from the list of active motors |
670fa10b | 256 | void StepTicker::remove_motor_from_active_list(StepperMotor* motor) |
6e0063ab MM |
257 | { |
258 | uint32_t bm; int i; | |
259 | for (i = 0, bm = 1; i < 12; i++, bm <<= 1) | |
260 | { | |
261 | if (this->active_motors[i] == motor) | |
262 | { | |
263 | this->active_motor_bm &= ~bm; | |
8aea2a35 AW |
264 | // If we have no motor to work on, disable the whole interrupt |
265 | if( this->active_motor_bm == 0 ){ | |
dc4ca298 | 266 | LPC_TIM0->TCR = 0; // Disable interrupt |
8aea2a35 | 267 | } |
6e0063ab MM |
268 | return; |
269 | } | |
796c9f32 | 270 | } |
796c9f32 | 271 | } |