Implement endstops using new motion control
[clinton/Smoothieware.git] / src / libs / StepTicker.cpp
CommitLineData
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#include "libs/nuts_bolts.h"
12#include "libs/Module.h"
13#include "libs/Kernel.h"
5673fe39 14#include "StepperMotor.h"
c9cc5e06 15#include "StreamOutputPool.h"
8b260c2c 16#include "Block.h"
f6542ad9 17#include "Conveyor.h"
8b260c2c 18
da3a10b9 19#include "system_LPC17xx.h" // mbed.h lib
61134a65 20#include <math.h>
bd0f7508
AW
21#include <mri.h>
22
9e089978 23#ifdef STEPTICKER_DEBUG_PIN
265bab4b 24// debug pins, only used if defined in src/makefile
9e089978 25#include "gpio.h"
265bab4b 26GPIO stepticker_debug_pin(STEPTICKER_DEBUG_PIN);
ccb06c1d
JM
27#define SET_STEPTICKER_DEBUG_PIN(n) {if(n) stepticker_debug_pin.set(); else stepticker_debug_pin.clear(); }
28#else
29#define SET_STEPTICKER_DEBUG_PIN(n)
9e089978
JM
30#endif
31
8b260c2c 32StepTicker *StepTicker::instance;
61134a65 33
8b260c2c
JM
34StepTicker::StepTicker()
35{
36 instance = this; // setup the Singleton instance of the stepticker
93694d6b
AW
37
38 // Configure the timer
8aea2a35 39 LPC_TIM0->MR0 = 10000000; // Initial dummy value for Match Register
813727fb 40 LPC_TIM0->MCR = 3; // Match on MR0, reset on MR0, match on MR1
8aea2a35 41 LPC_TIM0->TCR = 0; // Disable interrupt
796c9f32 42
8aea2a35 43 LPC_SC->PCONP |= (1 << 2); // Power Ticker ON
813727fb
AW
44 LPC_TIM1->MR0 = 1000000;
45 LPC_TIM1->MCR = 1;
aed1f6ca 46 LPC_TIM1->TCR = 0; // Disable interrupt
813727fb 47
7b49793d 48 // Default start values
3b1acdaa 49 this->set_frequency(100000);
8b260c2c
JM
50 this->set_unstep_time(100);
51
52 this->unstep.reset();
53 this->num_motors = 0;
54
b5708347 55 this->running = false;
a19a873f 56 this->current_block = nullptr;
265bab4b
JM
57
58 #ifdef STEPTICKER_DEBUG_PIN
59 // setup debug pin if defined
60 stepticker_debug_pin.output();
61 stepticker_debug_pin= 0;
62 #endif
3b1e82d2
AW
63}
64
8b260c2c
JM
65StepTicker::~StepTicker()
66{
3eadcfee
JM
67}
68
b772a11c 69//called when everything is setup and interrupts can start
8b260c2c
JM
70void StepTicker::start()
71{
dc3542cf
JM
72 NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler
73 NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler
dc3542cf
JM
74}
75
921bdb42 76// Set the base stepping frequency
8b260c2c
JM
77void StepTicker::set_frequency( float frequency )
78{
3b1e82d2 79 this->frequency = frequency;
8b260c2c 80 this->period = floorf((SystemCoreClock / 4.0F) / frequency); // SystemCoreClock/4 = Timer increments in a second
feb204be 81 LPC_TIM0->MR0 = this->period;
1598a726
JM
82 LPC_TIM0->TCR = 3; // Reset
83 LPC_TIM0->TCR = 1; // start
3b1e82d2
AW
84}
85
921bdb42 86// Set the reset delay
8b260c2c
JM
87void StepTicker::set_unstep_time( float microseconds )
88{
89 uint32_t delay = floorf((SystemCoreClock / 4.0F) * (microseconds / 1000000.0F)); // SystemCoreClock/4 = Timer increments in a second
aed1f6ca 90 LPC_TIM1->MR0 = delay;
3b1e82d2
AW
91}
92
aed1f6ca 93// Reset step pins on any motor that was stepped
8b260c2c
JM
94void StepTicker::unstep_tick()
95{
1fce036c 96 for (int i = 0; i < num_motors; i++) {
8b260c2c 97 if(this->unstep[i]) {
1fce036c 98 this->motor[i]->unstep();
aed1f6ca 99 }
3b1e82d2 100 }
13256955 101 this->unstep.reset();
3b1e82d2
AW
102}
103
8b260c2c
JM
104extern "C" void TIMER1_IRQHandler (void)
105{
7b49793d 106 LPC_TIM1->IR |= 1 << 0;
8b260c2c 107 StepTicker::getInstance()->unstep_tick();
813727fb
AW
108}
109
921bdb42 110// The actual interrupt handler where we do all the work
8b260c2c
JM
111extern "C" void TIMER0_IRQHandler (void)
112{
1598a726
JM
113 // Reset interrupt register
114 LPC_TIM0->IR |= 1 << 0;
115 StepTicker::getInstance()->step_tick();
dc3542cf
JM
116}
117
8b260c2c
JM
118extern "C" void PendSV_Handler(void)
119{
1598a726 120 StepTicker::getInstance()->handle_finish();
16220afe
JM
121}
122
123// slightly lower priority than TIMER0, the whole end of block/start of block is done here allowing the timer to continue ticking
1598a726 124void StepTicker::handle_finish (void)
8b260c2c 125{
8b260c2c
JM
126 // all moves finished signal block is finished
127 if(finished_fnc) finished_fnc();
cb2e6bc6
JM
128}
129
8b260c2c 130// step clock
1598a726 131void StepTicker::step_tick (void)
8b260c2c
JM
132{
133 static uint32_t current_tick = 0;
134
ccb06c1d 135 //SET_STEPTICKER_DEBUG_PIN(running ? 1 : 0);
8b260c2c 136
a19a873f 137 // if nothing has been setup we ignore the ticks
b5708347 138 if(!running){
a19a873f
JM
139 // check if anything new available
140 if(THECONVEYOR->get_next_block(&current_block)) { // returns false if no new block is available
141 running= start_next_block(); // returns true if there is at least one motor with steps to issue
142 if(!running) return;
143 }else{
144 return;
145 }
146 }
147
148 if(THEKERNEL->is_halted()) {
149 running= false;
150 return;
b5708347 151 }
8b260c2c 152
b5708347 153 bool still_moving= false;
8b260c2c
JM
154 // foreach motor, if it is active see if time to issue a step to that motor
155 for (uint8_t m = 0; m < num_motors; m++) {
f6542ad9 156 if(current_block->tick_info[m].steps_to_move == 0) continue; // not active
8b260c2c 157
f6542ad9 158 current_block->tick_info[m].steps_per_tick += current_block->tick_info[m].acceleration_change;
d1d120e1 159
f6542ad9
JM
160 if(current_tick == current_block->tick_info[m].next_accel_event) {
161 if(current_tick == current_block->accelerate_until) { // We are done accelerating, deceleration becomes 0 : plateau
162 current_block->tick_info[m].acceleration_change = 0;
163 if(current_block->decelerate_after < current_block->total_move_ticks) {
164 current_block->tick_info[m].next_accel_event = current_block->decelerate_after;
165 if(current_tick != current_block->decelerate_after) { // We are plateauing
1598a726 166 // steps/sec / tick frequency to get steps per tick
f6542ad9 167 current_block->tick_info[m].steps_per_tick = current_block->tick_info[m].plateau_rate;
8b260c2c
JM
168 }
169 }
170 }
171
f6542ad9
JM
172 if(current_tick == current_block->decelerate_after) { // We start decelerating
173 current_block->tick_info[m].acceleration_change = current_block->tick_info[m].deceleration_change;
8b260c2c
JM
174 }
175 }
176
177 // protect against rounding errors and such
f6542ad9
JM
178 if(current_block->tick_info[m].steps_per_tick <= 0) {
179 current_block->tick_info[m].counter = STEPTICKER_FPSCALE; // we force completion this step by setting to 1.0
180 current_block->tick_info[m].steps_per_tick = 0;
8b260c2c
JM
181 }
182
f6542ad9 183 current_block->tick_info[m].counter += current_block->tick_info[m].steps_per_tick;
8b260c2c 184
f6542ad9
JM
185 if(current_block->tick_info[m].counter >= STEPTICKER_FPSCALE) { // >= 1.0 step time
186 current_block->tick_info[m].counter -= STEPTICKER_FPSCALE; // -= 1.0F;
187 ++current_block->tick_info[m].step_count;
8b260c2c
JM
188
189 // step the motor
c8bac202 190 bool ismoving= motor[m]->step(); // returns false if the moving flag was set to false externally (probes, endstops etc)
778093ce 191 // we stepped so schedule an unstep
8b260c2c
JM
192 unstep.set(m);
193
c8bac202 194 if(!ismoving || current_block->tick_info[m].step_count == current_block->tick_info[m].steps_to_move) {
8b260c2c 195 // done
f6542ad9 196 current_block->tick_info[m].steps_to_move = 0;
d1d120e1 197 motor[m]->moving= false; // let motor know it is no longer moving
8b260c2c 198 }
aed1f6ca 199 }
b5708347
JM
200
201 // see if any motors are still moving after this tick
202 if(motor[m]->moving) still_moving= true;
12800c08 203 }
4464301d 204
1ae56063
JM
205 // do this after so we start at tick 0
206 current_tick++; // count number of ticks
207
aed1f6ca
JM
208 // We may have set a pin on in this tick, now we reset the timer to set it off
209 // Note there could be a race here if we run another tick before the unsteps have happened,
210 // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue
211 // also it takes at least 2us to get here so even when set to 1us pulse width it will still be about 3us
8b260c2c 212 if( unstep.any()) {
bd0f7508
AW
213 LPC_TIM1->TCR = 3;
214 LPC_TIM1->TCR = 1;
a157d099 215 }
3b1acdaa 216
b5708347
JM
217
218 // see if any motors are still moving
8b260c2c 219 if(!still_moving) {
ccb06c1d 220 SET_STEPTICKER_DEBUG_PIN(0);
d1d120e1 221
1598a726 222 // all moves finished
8b260c2c
JM
223 current_tick = 0;
224
f6542ad9 225 // get next block
8b260c2c 226 // do it here so there is no delay in ticks
a19a873f 227 THECONVEYOR->block_finished();
f6542ad9 228
a19a873f 229 if(THECONVEYOR->get_next_block(&current_block)) { // returns false if no new block is available
f6542ad9 230 running= start_next_block(); // returns true if there is at least one motor with steps to issue
a19a873f 231
f6542ad9
JM
232 }else{
233 current_block= nullptr;
234 running= false;
235 }
8b260c2c
JM
236
237 // all moves finished
f095cddd 238 // we delegate the slow stuff to the pendsv handler which will run as soon as this interrupt exits
16220afe 239 //NVIC_SetPendingIRQ(PendSV_IRQn); this doesn't work
b5708347 240 //SCB->ICSR = 0x10000000; // SCB_ICSR_PENDSVSET_Msk;
bd0f7508 241 }
3b1e82d2
AW
242}
243
d1d120e1 244// only called from the step tick ISR (single consumer)
f6542ad9 245bool StepTicker::start_next_block()
d1d120e1 246{
f6542ad9 247 if(current_block == nullptr) return false;
d1d120e1 248
f6542ad9 249 bool ok= false;
d1d120e1 250 // need to prepare each active motor
d1d120e1 251 for (uint8_t m = 0; m < num_motors; m++) {
f6542ad9 252 if(current_block->tick_info[m].steps_to_move == 0) continue;
d1d120e1 253
f6542ad9 254 ok= true; // mark at least one motor is moving
d1d120e1
JM
255 // set direction bit here
256 // NOTE this would be at least 10us before first step pulse.
257 // TODO does this need to be done sooner, if so how without delaying next tick
f6542ad9 258 motor[m]->set_direction(current_block->direction_bits[m]);
d1d120e1 259 motor[m]->moving= true; // also let motor know it is moving now
d1d120e1
JM
260 }
261
f6542ad9
JM
262 if(ok) {
263 SET_STEPTICKER_DEBUG_PIN(1);
433d636f
JM
264 return true;
265 }
266
267 return false;
796c9f32
AW
268}
269
f6542ad9 270
8b260c2c
JM
271// returns index of the stepper motor in the array and bitset
272int StepTicker::register_motor(StepperMotor* m)
6e0063ab 273{
8b260c2c
JM
274 motor[num_motors++] = m;
275 return num_motors - 1;
796c9f32 276}