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
;
25 extern bool _isr_context
;
27 // StepTicker handles the base frequency ticking for the Stepper Motors / Actuators
28 // It has a list of those, and calls their tick() functions at regular intervals
29 // They then do Bresenham stuff themselves
31 StepTicker
* StepTicker::global_step_ticker
;
33 StepTicker::StepTicker(){
34 StepTicker::global_step_ticker
= this;
36 // Configure the timer
37 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
38 LPC_TIM0
->MCR
= 3; // Match on MR0, reset on MR0, match on MR1
39 LPC_TIM0
->TCR
= 0; // Disable interrupt
41 LPC_SC
->PCONP
|= (1 << 2); // Power Ticker ON
42 LPC_TIM1
->MR0
= 1000000;
44 LPC_TIM1
->TCR
= 1; // Enable interrupt
46 // Default start values
47 this->a_move_finished
= false;
48 this->pending_sv
= false;
49 this->reset_step_pins
= false;
50 this->set_frequency(0.001);
51 this->set_reset_delay(100);
52 this->set_acceleration_ticks_per_second(1000);
53 this->last_duration
= 0;
55 this->active_motor
.reset();
56 this->acceleration_tick_cnt
= 0;
58 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
59 NVIC_EnableIRQ(TIMER1_IRQn
); // Enable interrupt handler
62 StepTicker::~StepTicker() {
65 // Set the base stepping frequency
66 void StepTicker::set_frequency( float frequency
){
67 this->frequency
= frequency
;
68 this->period
= floorf((SystemCoreClock
/4.0F
)/frequency
); // SystemCoreClock/4 = Timer increments in a second
69 LPC_TIM0
->MR0
= this->period
;
70 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
71 LPC_TIM0
->TCR
= 3; // Reset
72 LPC_TIM0
->TCR
= 1; // Reset
76 // Set the reset delay
77 void StepTicker::set_reset_delay( float seconds
){
78 this->delay
= floorf((SystemCoreClock
/4.0F
)*seconds
); // SystemCoreClock/4 = Timer increments in a second
79 LPC_TIM1
->MR0
= this->delay
;
82 // this is the number of stepper ticks (100,000/sec) per acceleration tick
83 void StepTicker::set_acceleration_ticks_per_second(uint32_t acceleration_ticks_per_second
) {
84 this->acceleration_tick_period
= floorf(this->frequency
/acceleration_ticks_per_second
);
87 // Call signal_move_finished() on each active motor that asked to be signaled. We do this instead of inside of tick() so that
88 // all tick()s are called before we do the move finishing
89 void StepTicker::signal_a_move_finished(){
92 for (int motor
= 0; motor
< num_motors
; motor
++){
93 if (this->active_motor
[motor
] && this->motor
[motor
]->is_move_finished
){
94 this->motor
[motor
]->signal_move_finished();
95 // Theoretically this does nothing and the reason for it is currently unknown and/or forgotten
96 // if(this->motor[motor]->moving == false){
104 this->a_move_finished
= false;
106 _isr_context
= false;
109 // Reset step pins on all active motors
110 inline void StepTicker::reset_tick(){
113 for (int i
= 0; i
< num_motors
; i
++) {
114 if(this->active_motor
[i
])
115 this->motor
[i
]->unstep();
118 _isr_context
= false;
121 extern "C" void TIMER1_IRQHandler (void){
122 LPC_TIM1
->IR
|= 1 << 0;
123 StepTicker::global_step_ticker
->reset_tick();
126 // The actual interrupt handler where we do all the work
127 extern "C" void TIMER0_IRQHandler (void){
128 StepTicker::global_step_ticker
->TIMER0_IRQHandler();
131 extern "C" void PendSV_Handler(void) {
132 StepTicker::global_step_ticker
->PendSV_IRQHandler();
135 // slightly lower priority than TIMER0, the whole end of block/start of block is done here allowing the timer to continue ticking
136 // also handles the acceleration tick
137 void StepTicker::PendSV_IRQHandler (void){
138 if(this->do_acceleration_tick
) {
139 this->do_acceleration_tick
= false;
140 // call registered acceleration handlers
141 for (size_t i
= 0; i
< acceleration_tick_handlers
.size(); ++i
) {
142 acceleration_tick_handlers
[i
]();
146 if(this->a_move_finished
) {
147 #ifdef STEPTICKER_DEBUG_PIN
148 stepticker_debug_pin
= 1;
151 this->signal_a_move_finished();
153 #ifdef STEPTICKER_DEBUG_PIN
154 stepticker_debug_pin
= 0;
158 this->pending_sv
= false;
161 void StepTicker::TIMER0_IRQHandler (void){
162 // Reset interrupt register
163 LPC_TIM0
->IR
|= 1 << 0;
164 LPC_TIM0
->MR0
= this->period
;
167 for (uint32_t motor
= 0; motor
< num_motors
; motor
++){
168 if (this->active_motor
[motor
]){
169 this->motor
[motor
]->tick();
173 // We may have set a pin on in this tick, now we start the timer to set it off
174 if( this->reset_step_pins
){
177 this->reset_step_pins
= false;
180 // do an acceleration tick, after we have done everything else
181 if(++this->acceleration_tick_cnt
>= this->acceleration_tick_period
) {
182 this->acceleration_tick_cnt
= 0;
183 this->do_acceleration_tick
= true;
186 // If a move finished in this tick, we have to tell the actuator to act accordingly
187 if(!this->pending_sv
&& (this->a_move_finished
|| this->do_acceleration_tick
)){
188 this->pending_sv
= true; // don't multiple trigger pendsv
189 // we delegate the slow stuff to the pendsv handler which will run as soon as this interrupt exits
190 //NVIC_SetPendingIRQ(PendSV_IRQn); this doesn't work
191 SCB
->ICSR
= 0x10000000; // SCB_ICSR_PENDSVSET_Msk;
195 // returns index of the stepper motor in the array and bitset
196 int StepTicker::register_motor(StepperMotor
* motor
)
198 this->motor
.push_back(motor
);
199 this->num_motors
= this->motor
.size();
200 return this->num_motors
-1;
203 // activate the specified motor, must have been registered
204 void StepTicker::add_motor_to_active_list(StepperMotor
* motor
)
206 bool enabled
= active_motor
.any(); // see if interrupt was previously enabled
207 active_motor
[motor
->index
]= 1;
209 LPC_TIM0
->TCR
= 1; // Enable interrupt
213 // Remove a stepper from the list of active motors
214 void StepTicker::remove_motor_from_active_list(StepperMotor
* motor
)
216 active_motor
[motor
->index
]= 0;
217 // If we have no motor to work on, disable the whole interrupt
218 if(this->active_motor
.none()){
219 LPC_TIM0
->TCR
= 0; // Disable interrupt