30c4cfc76906d483777afd5fcf95c24021f9316c
[clinton/Smoothieware.git] / src / libs / StepTicker.cpp
1 /*
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/>.
6 */
7
8
9 #include "StepTicker.h"
10
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
17 #include <math.h>
18 #include <mri.h>
19
20 #ifdef STEPTICKER_DEBUG_PIN
21 #include "gpio.h"
22 extern GPIO stepticker_debug_pin;
23 #endif
24
25 extern bool _isr_context;
26
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
30
31 StepTicker* StepTicker::global_step_ticker;
32
33 StepTicker::StepTicker(){
34 StepTicker::global_step_ticker = this;
35
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
40
41 LPC_SC->PCONP |= (1 << 2); // Power Ticker ON
42 LPC_TIM1->MR0 = 1000000;
43 LPC_TIM1->MCR = 1;
44 LPC_TIM1->TCR = 1; // Enable interrupt
45
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;
54 this->num_motors= 0;
55 this->active_motor.reset();
56 this->acceleration_tick_cnt= 0;
57
58 NVIC_EnableIRQ(TIMER0_IRQn); // Enable interrupt handler
59 NVIC_EnableIRQ(TIMER1_IRQn); // Enable interrupt handler
60 }
61
62 StepTicker::~StepTicker() {
63 }
64
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
73 }
74 }
75
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;
80 }
81
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);
85 }
86
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(){
90 _isr_context = true;
91
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){
97 // if (motor > 0){
98 // motor--;
99 // bitmask >>= 1;
100 // }
101 // }
102 }
103 }
104 this->a_move_finished = false;
105
106 _isr_context = false;
107 }
108
109 // Reset step pins on all active motors
110 inline void StepTicker::reset_tick(){
111 _isr_context = true;
112
113 for (int i = 0; i < num_motors; i++) {
114 if(this->active_motor[i])
115 this->motor[i]->unstep();
116 }
117
118 _isr_context = false;
119 }
120
121 extern "C" void TIMER1_IRQHandler (void){
122 LPC_TIM1->IR |= 1 << 0;
123 StepTicker::global_step_ticker->reset_tick();
124 }
125
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();
129 }
130
131 extern "C" void PendSV_Handler(void) {
132 StepTicker::global_step_ticker->PendSV_IRQHandler();
133 }
134
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]();
143 }
144 }
145
146 if(this->a_move_finished) {
147 #ifdef STEPTICKER_DEBUG_PIN
148 stepticker_debug_pin= 1;
149 #endif
150
151 this->signal_a_move_finished();
152
153 #ifdef STEPTICKER_DEBUG_PIN
154 stepticker_debug_pin= 0;
155 #endif
156 }
157
158 this->pending_sv= false;
159 }
160
161 void StepTicker::TIMER0_IRQHandler (void){
162 // Reset interrupt register
163 LPC_TIM0->IR |= 1 << 0;
164 LPC_TIM0->MR0 = this->period;
165
166 // Step pins
167 for (uint32_t motor = 0; motor < num_motors; motor++){
168 if (this->active_motor[motor]){
169 this->motor[motor]->tick();
170 }
171 }
172
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 ){
175 LPC_TIM1->TCR = 3;
176 LPC_TIM1->TCR = 1;
177 this->reset_step_pins = false;
178 }
179
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;
184 }
185
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;
192 }
193 }
194
195 // returns index of the stepper motor in the array and bitset
196 int StepTicker::register_motor(StepperMotor* motor)
197 {
198 this->motor.push_back(motor);
199 this->num_motors= this->motor.size();
200 return this->num_motors-1;
201 }
202
203 // activate the specified motor, must have been registered
204 void StepTicker::add_motor_to_active_list(StepperMotor* motor)
205 {
206 bool enabled= active_motor.any(); // see if interrupt was previously enabled
207 active_motor[motor->index]= 1;
208 if(!enabled) {
209 LPC_TIM0->TCR = 1; // Enable interrupt
210 }
211 }
212
213 // Remove a stepper from the list of active motors
214 void StepTicker::remove_motor_from_active_list(StepperMotor* motor)
215 {
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
220 }
221 }