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"
16 #include "system_LPC17xx.h" // mbed.h lib
20 #ifdef STEPTICKER_DEBUG_PIN
22 extern GPIO stepticker_debug_pin
;
26 // StepTicker handles the base frequency ticking for the Stepper Motors / Actuators
27 // It has a list of those, and calls their tick() functions at regular intervals
28 // They then do Bresenham stuff themselves
30 StepTicker
* StepTicker::global_step_ticker
;
32 StepTicker::StepTicker(){
33 StepTicker::global_step_ticker
= this;
35 // Configure the timer
36 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
37 LPC_TIM0
->MCR
= 3; // Match on MR0, reset on MR0, match on MR1
38 LPC_TIM0
->TCR
= 0; // Disable interrupt
40 LPC_SC
->PCONP
|= (1 << 2); // Power Ticker ON
41 LPC_TIM1
->MR0
= 1000000;
43 LPC_TIM1
->TCR
= 0; // Disable interrupt
46 LPC_SC
->PCONP
|= (1L<<16); // RIT Power
47 LPC_SC
->PCLKSEL1
&= ~(3L << 26); // Clear PCLK_RIT bits;
48 LPC_SC
->PCLKSEL1
|= (1L << 26); // Set PCLK_RIT bits to 0x01;
49 LPC_RIT
->RICOMPVAL
= (uint32_t)(((SystemCoreClock
/ 1000000L) * 1000)-1); // 1ms period
50 LPC_RIT
->RICOUNTER
= 0;
51 // Set counter clear/reset after interrupt
52 LPC_RIT
->RICTRL
|= (2L); //RITENCLR
53 LPC_RIT
->RICTRL
&= ~(8L); // disable
54 //NVIC_SetVector(RIT_IRQn, (uint32_t)&_ritisr);
56 // Default start values
57 this->a_move_finished
= false;
58 this->do_move_finished
= 0;
60 this->set_frequency(100000);
61 this->set_reset_delay(100);
62 this->set_acceleration_ticks_per_second(1000);
64 this->active_motor
.reset();
68 StepTicker::~StepTicker() {
71 //called when everythinf is setup and interrupts can start
72 void StepTicker::start() {
73 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
74 NVIC_EnableIRQ(TIMER1_IRQn
); // Enable interrupt handler
75 NVIC_EnableIRQ(RIT_IRQn
);
78 // Set the base stepping frequency
79 void StepTicker::set_frequency( float frequency
){
80 this->frequency
= frequency
;
81 this->period
= floorf((SystemCoreClock
/4.0F
)/frequency
); // SystemCoreClock/4 = Timer increments in a second
82 LPC_TIM0
->MR0
= this->period
;
83 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
84 LPC_TIM0
->TCR
= 3; // Reset
85 LPC_TIM0
->TCR
= 1; // Reset
89 // Set the reset delay
90 void StepTicker::set_reset_delay( float microseconds
){
91 uint32_t delay
= floorf((SystemCoreClock
/4.0F
)*(microseconds
/1000000.0F
)); // SystemCoreClock/4 = Timer increments in a second
92 LPC_TIM1
->MR0
= delay
;
95 // this is the number of acceleration ticks per second
96 void StepTicker::set_acceleration_ticks_per_second(uint32_t acceleration_ticks_per_second
) {
97 uint32_t us
= roundf(1000000.0F
/acceleration_ticks_per_second
); // period in microseconds
98 LPC_RIT
->RICOMPVAL
= (uint32_t)(((SystemCoreClock
/ 1000000L) * us
)-1); // us
99 LPC_RIT
->RICOUNTER
= 0;
100 LPC_RIT
->RICTRL
|= (8L); // Enable rit
103 // Synchronize the acceleration timer, and optionally schedule it to fire now
104 void StepTicker::synchronize_acceleration(bool fire_now
) {
105 LPC_RIT
->RICOUNTER
= 0;
107 NVIC_SetPendingIRQ(RIT_IRQn
);
109 if(NVIC_GetPendingIRQ(RIT_IRQn
)) {
110 // clear pending interrupt so it does not interrupt immediately
111 LPC_RIT
->RICTRL
|= 1L; // also clear the interrupt in case it fired
112 NVIC_ClearPendingIRQ(RIT_IRQn
);
118 // Call signal_move_finished() on each active motor that asked to be signaled. We do this instead of inside of tick() so that
119 // all tick()s are called before we do the move finishing
120 void StepTicker::signal_a_move_finished(){
121 for (int motor
= 0; motor
< num_motors
; motor
++){
122 if (this->active_motor
[motor
] && this->motor
[motor
]->is_move_finished
){
123 this->motor
[motor
]->signal_move_finished();
124 // Theoretically this does nothing and the reason for it is currently unknown and/or forgotten
125 // if(this->motor[motor]->moving == false){
135 // Reset step pins on any motor that was stepped
136 inline void StepTicker::unstep_tick(){
137 for (int i
= 0; i
< num_motors
; i
++) {
139 this->motor
[i
]->unstep();
142 this->unstep
.reset();
145 extern "C" void TIMER1_IRQHandler (void){
146 LPC_TIM1
->IR
|= 1 << 0;
147 StepTicker::global_step_ticker
->unstep_tick();
150 // The actual interrupt handler where we do all the work
151 extern "C" void TIMER0_IRQHandler (void){
152 StepTicker::global_step_ticker
->TIMER0_IRQHandler();
155 extern "C" void RIT_IRQHandler (void){
156 LPC_RIT
->RICTRL
|= 1L;
157 StepTicker::global_step_ticker
->acceleration_tick();
160 extern "C" void PendSV_Handler(void) {
161 StepTicker::global_step_ticker
->PendSV_IRQHandler();
164 // slightly lower priority than TIMER0, the whole end of block/start of block is done here allowing the timer to continue ticking
165 void StepTicker::PendSV_IRQHandler (void) {
167 if(this->do_move_finished
.load() > 0) {
168 this->do_move_finished
--;
169 #ifdef STEPTICKER_DEBUG_PIN
170 stepticker_debug_pin
= 1;
173 this->signal_a_move_finished();
175 #ifdef STEPTICKER_DEBUG_PIN
176 stepticker_debug_pin
= 0;
181 // run in RIT lower priority than PendSV
182 void StepTicker::acceleration_tick() {
183 // call registered acceleration handlers
184 for (size_t i
= 0; i
< acceleration_tick_handlers
.size(); ++i
) {
185 acceleration_tick_handlers
[i
]();
189 void StepTicker::TIMER0_IRQHandler (void){
190 // Reset interrupt register
191 LPC_TIM0
->IR
|= 1 << 0;
192 tick_cnt
++; // count number of ticks
194 // Step pins NOTE takes 1.2us when nothing to step, 1.8-2us for one motor stepped and 2.6us when two motors stepped, 3.167us when three motors stepped
195 for (uint32_t motor
= 0; motor
< num_motors
; motor
++){
196 // send tick to all active motors
197 if(this->active_motor
[motor
] && this->motor
[motor
]->tick()){
198 // we stepped so schedule an unstep
199 this->unstep
[motor
]= 1;
203 // We may have set a pin on in this tick, now we reset the timer to set it off
204 // Note there could be a race here if we run another tick before the unsteps have happened,
205 // right now it takes about 3-4us but if the unstep were near 10uS or greater it would be an issue
206 // also it takes at least 2us to get here so even when set to 1us pulse width it will still be about 3us
207 if( this->unstep
.any()){
211 // just let it run it will fire every 143 seconds
213 // LPC_TIM1->TCR = 0; // disable interrupt, no point in it running if nothing to do
216 if(this->a_move_finished
) {
217 this->a_move_finished
= false;
218 this->do_move_finished
++; // Note this is an atomic variable because it is updated in two interrupts of different priorities so can be pre-empted
221 // If a move finished in this tick, we have to tell the actuator to act accordingly
222 if(this->do_move_finished
.load() > 0){
223 // we delegate the slow stuff to the pendsv handler which will run as soon as this interrupt exits
224 //NVIC_SetPendingIRQ(PendSV_IRQn); this doesn't work
225 SCB
->ICSR
= 0x10000000; // SCB_ICSR_PENDSVSET_Msk;
229 // returns index of the stepper motor in the array and bitset
230 int StepTicker::register_motor(StepperMotor
* motor
)
232 this->motor
.push_back(motor
);
233 this->num_motors
= this->motor
.size();
234 return this->num_motors
-1;
237 // activate the specified motor, must have been registered
238 void StepTicker::add_motor_to_active_list(StepperMotor
* motor
)
240 bool enabled
= active_motor
.any(); // see if interrupt was previously enabled
241 active_motor
[motor
->index
]= 1;
243 LPC_TIM0
->TCR
= 1; // Enable interrupt
247 // Remove a stepper from the list of active motors
248 void StepTicker::remove_motor_from_active_list(StepperMotor
* motor
)
250 active_motor
[motor
->index
]= 0;
251 // If we have no motor to work on, disable the whole interrupt
252 if(this->active_motor
.none()){
253 LPC_TIM0
->TCR
= 0; // Disable interrupt