31f7d8dc4e449f582fedb3f5c9174415d6434fae
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/>.
9 #include "StepTicker.h"
11 #include "libs/nuts_bolts.h"
12 #include "libs/Module.h"
13 #include "libs/Kernel.h"
14 #include "StepperMotor.h"
15 #include "StreamOutputPool.h"
19 #include "system_LPC17xx.h" // mbed.h lib
23 #ifdef STEPTICKER_DEBUG_PIN
24 // debug pins, only used if defined in src/makefile
26 GPIO
stepticker_debug_pin(STEPTICKER_DEBUG_PIN
);
27 #define SET_STEPTICKER_DEBUG_PIN(n) {if(n) stepticker_debug_pin.set(); else stepticker_debug_pin.clear(); }
29 #define SET_STEPTICKER_DEBUG_PIN(n)
32 StepTicker
*StepTicker::instance
;
34 StepTicker::StepTicker()
36 instance
= this; // setup the Singleton instance of the stepticker
38 // Configure the timer
39 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
40 LPC_TIM0
->MCR
= 3; // Match on MR0, reset on MR0, match on MR1
41 LPC_TIM0
->TCR
= 0; // Disable interrupt
43 LPC_SC
->PCONP
|= (1 << 2); // Power Ticker ON
44 LPC_TIM1
->MR0
= 1000000;
46 LPC_TIM1
->TCR
= 0; // Disable interrupt
48 // Default start values
49 this->set_frequency(100000);
50 this->set_unstep_time(100);
55 this->running
= false;
56 this->current_block
= nullptr;
58 #ifdef STEPTICKER_DEBUG_PIN
59 // setup debug pin if defined
60 stepticker_debug_pin
.output();
61 stepticker_debug_pin
= 0;
65 StepTicker::~StepTicker()
69 //called when everything is setup and interrupts can start
70 void StepTicker::start()
72 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
73 NVIC_EnableIRQ(TIMER1_IRQn
); // Enable interrupt handler
76 // Set the base stepping frequency
77 void StepTicker::set_frequency( float frequency
)
79 this->frequency
= frequency
;
80 this->period
= floorf((SystemCoreClock
/ 4.0F
) / frequency
); // SystemCoreClock/4 = Timer increments in a second
81 LPC_TIM0
->MR0
= this->period
;
82 LPC_TIM0
->TCR
= 3; // Reset
83 LPC_TIM0
->TCR
= 1; // start
86 // Set the reset delay
87 void StepTicker::set_unstep_time( float microseconds
)
89 uint32_t delay
= floorf((SystemCoreClock
/ 4.0F
) * (microseconds
/ 1000000.0F
)); // SystemCoreClock/4 = Timer increments in a second
90 LPC_TIM1
->MR0
= delay
;
93 // Reset step pins on any motor that was stepped
94 void StepTicker::unstep_tick()
96 for (int i
= 0; i
< num_motors
; i
++) {
98 this->motor
[i
]->unstep();
101 this->unstep
.reset();
104 extern "C" void TIMER1_IRQHandler (void)
106 LPC_TIM1
->IR
|= 1 << 0;
107 StepTicker::getInstance()->unstep_tick();
110 // The actual interrupt handler where we do all the work
111 extern "C" void TIMER0_IRQHandler (void)
113 // Reset interrupt register
114 LPC_TIM0
->IR
|= 1 << 0;
115 StepTicker::getInstance()->step_tick();
118 extern "C" void PendSV_Handler(void)
120 StepTicker::getInstance()->handle_finish();
123 // slightly lower priority than TIMER0, the whole end of block/start of block is done here allowing the timer to continue ticking
124 void StepTicker::handle_finish (void)
126 // all moves finished signal block is finished
127 if(finished_fnc
) finished_fnc();
131 void StepTicker::step_tick (void)
133 static uint32_t current_tick
= 0;
135 //SET_STEPTICKER_DEBUG_PIN(running ? 1 : 0);
137 // if nothing has been setup we ignore the ticks
139 // check if anything new available
140 if(THECONVEYOR
->get_next_block(¤t_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
148 if(THEKERNEL
->is_halted()) {
153 bool still_moving
= false;
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
++) {
156 if(current_block
->tick_info
[m
].steps_to_move
== 0) continue; // not active
158 current_block
->tick_info
[m
].steps_per_tick
+= current_block
->tick_info
[m
].acceleration_change
;
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
166 // steps/sec / tick frequency to get steps per tick
167 current_block
->tick_info
[m
].steps_per_tick
= current_block
->tick_info
[m
].plateau_rate
;
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
;
177 // protect against rounding errors and such
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;
183 current_block
->tick_info
[m
].counter
+= current_block
->tick_info
[m
].steps_per_tick
;
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
;
191 // we stepped so schedule an unstep
194 if(current_block
->tick_info
[m
].step_count
== current_block
->tick_info
[m
].steps_to_move
) {
196 current_block
->tick_info
[m
].steps_to_move
= 0;
197 motor
[m
]->moving
= false; // let motor know it is no longer moving
201 // see if any motors are still moving after this tick
202 if(motor
[m
]->moving
) still_moving
= true;
205 // do this after so we start at tick 0
206 current_tick
++; // count number of ticks
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
218 // see if any motors are still moving
220 SET_STEPTICKER_DEBUG_PIN(0);
222 // all moves finished
226 // do it here so there is no delay in ticks
227 THECONVEYOR
->block_finished();
229 if(THECONVEYOR
->get_next_block(¤t_block
)) { // returns false if no new block is available
230 running
= start_next_block(); // returns true if there is at least one motor with steps to issue
233 current_block
= nullptr;
237 // all moves finished
238 // we delegate the slow stuff to the pendsv handler which will run as soon as this interrupt exits
239 //NVIC_SetPendingIRQ(PendSV_IRQn); this doesn't work
240 //SCB->ICSR = 0x10000000; // SCB_ICSR_PENDSVSET_Msk;
244 // only called from the step tick ISR (single consumer)
245 bool StepTicker::start_next_block()
247 if(current_block
== nullptr) return false;
250 // need to prepare each active motor
251 for (uint8_t m
= 0; m
< num_motors
; m
++) {
252 if(current_block
->tick_info
[m
].steps_to_move
== 0) continue;
254 ok
= true; // mark at least one motor is moving
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
258 motor
[m
]->set_direction(current_block
->direction_bits
[m
]);
259 motor
[m
]->moving
= true; // also let motor know it is moving now
263 SET_STEPTICKER_DEBUG_PIN(1);
271 // returns index of the stepper motor in the array and bitset
272 int StepTicker::register_motor(StepperMotor
* m
)
274 motor
[num_motors
++] = m
;
275 return num_motors
- 1;