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