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