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/>.
12 #include "libs/nuts_bolts.h"
13 #include "libs/Module.h"
14 #include "libs/Kernel.h"
15 #include "StepTicker.h"
16 #include "system_LPC17xx.h" // mbed.h lib
19 StepTicker
* global_step_ticker
;
21 StepTicker::StepTicker(){
22 global_step_ticker
= this;
23 LPC_TIM0
->MR0
= 10000000; // Initial dummy value for Match Register
24 LPC_TIM0
->MR1
= 10000000;
25 LPC_TIM0
->MCR
= 11; // Match on MR0, reset on MR0, match on MR1
26 LPC_TIM0
->TCR
= 1; // Enable interrupt
29 this->set_frequency(0.001);
30 this->set_reset_delay(100);
31 this->last_duration
= 0;
32 NVIC_EnableIRQ(TIMER0_IRQn
); // Enable interrupt handler
35 void StepTicker::set_frequency( double frequency
){
36 this->frequency
= frequency
;
37 this->period
= int(floor((SystemCoreClock
/4)/frequency
)); // SystemCoreClock/4 = Timer increments in a second
38 LPC_TIM0
->MR0
= this->period
;
39 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
40 LPC_TIM0
->TCR
= 3; // Reset
41 LPC_TIM0
->TCR
= 1; // Reset
45 void StepTicker::set_reset_delay( double seconds
){
46 this->delay
= int(floor(double(SystemCoreClock
/4)*( seconds
))); // SystemCoreClock/4 = Timer increments in a second
49 // Add a stepper motor object to our list of steppers we must take care of
50 StepperMotor
* StepTicker::add_stepper_motor(StepperMotor
* stepper_motor
){
51 this->stepper_motors
.push_back(stepper_motor
);
52 stepper_motor
->step_ticker
= this;
53 this->has_axes
= true;
57 inline void StepTicker::tick(){
58 for(unsigned int i
=0; i
< this->stepper_motors
.size(); i
++){
59 this->stepper_motors
[i
]->tick();
63 inline void StepTicker::reset_tick(){
64 for(unsigned int i
=0; i
< this->stepper_motors
.size(); i
++){
65 this->stepper_motors
[i
]->step_pin
->set(0);
69 extern "C" void TIMER0_IRQHandler (void){
70 uint32_t start_time
= LPC_TIM0
->TC
;
71 global_step_ticker
->debug
++;
73 if( !global_step_ticker
->has_axes
){
74 if((LPC_TIM0
->IR
>> 0) & 1){ LPC_TIM0
->IR
|= 1 << 0; }
75 if((LPC_TIM0
->IR
>> 1) & 1){ LPC_TIM0
->IR
|= 1 << 1; }
79 LPC_TIM0
->MR1
= 2000000;
81 if((LPC_TIM0
->IR
>> 0) & 1){ // If interrupt register set for MR0
82 // Do not get out of here before everything is nice and tidy
84 //if( global_step_ticker->debug % 1000 == 1 ){ printf("start: tc: %u mr0: %u mr1: %u d: %u\r\n", LPC_TIM0->TC, LPC_TIM0->MR0, LPC_TIM0->MR1, global_step_ticker->debug); }
86 LPC_TIM0
->MR0
= 2000000;
88 LPC_TIM0
->IR
|= 1 << 0; // Reset it
91 uint32_t aa
= LPC_TIM0
->TC
;
92 global_step_ticker
->tick();
93 uint32_t bb
= LPC_TIM0
->TC
;
95 // Maybe we have spent enough time in this interrupt so that we have to reset the pins ourself
96 if( LPC_TIM0
->TC
> global_step_ticker
->delay
){
97 global_step_ticker
->reset_tick();
99 // Else we have to trigger this a tad later, using MR1
100 LPC_TIM0
->MR1
= global_step_ticker
->delay
;
102 uint32_t cc
= LPC_TIM0
->TC
;
104 // If we went over the duration an interrupt is supposed to last, we have a problem
105 // That can happen tipically when we change blocks, where more than usual computation is done
106 // This can be OK, if we take notice of it, which we do now
107 if( LPC_TIM0
->TC
> global_step_ticker
->period
){ // TODO : remove the size condition
109 uint32_t start_tc
= LPC_TIM0
->TC
;
111 // How many ticks we want to skip ( this does not include the current tick, but we add the time we spent doing this computation last time )
112 uint32_t ticks_to_skip
= ( ( LPC_TIM0
->TC
+ global_step_ticker
->last_duration
) / global_step_ticker
->period
);
114 // Next step is now to reduce this to how many steps we can *actually* skip
115 uint32_t ticks_we_actually_can_skip
= ticks_to_skip
;
116 for(unsigned int i
=0; i
< global_step_ticker
->stepper_motors
.size(); i
++){
117 StepperMotor
* stepper
= global_step_ticker
->stepper_motors
[i
];
118 if( stepper
->moving
){ ticks_we_actually_can_skip
= min( ticks_we_actually_can_skip
, (uint32_t)((uint64_t)( (uint64_t)stepper
->fx_ticks_per_step
- (uint64_t)stepper
->fx_counter
) >> 32) ); }
121 // If the number of ticks we can actually skip is smaller than the number we wanted to skip, there is something wrong in the settings
122 if( ticks_we_actually_can_skip
< ticks_to_skip
){ }
124 // Adding to MR0 for this time is not enough, we must also increment the counters ourself artificially
125 for(unsigned int i
=0; i
< global_step_ticker
->stepper_motors
.size(); i
++){
126 StepperMotor
* stepper
= global_step_ticker
->stepper_motors
[i
];
127 if( stepper
->moving
){ stepper
->fx_counter
+= (uint64_t)((uint64_t)(ticks_we_actually_can_skip
)<<32); }
130 // When must we have our next MR0 ? ( +1 is here to account that we are actually a legit MR0 too, not only overtime )
131 LPC_TIM0
->MR0
= ( ticks_we_actually_can_skip
+ 1 ) * global_step_ticker
->period
;
133 // This is so that we know how long this computation takes, and we can take it into account next time
134 global_step_ticker
->last_duration
= LPC_TIM0
->TC
- start_tc
;
136 if( global_step_ticker
->debug
% 50 == 1 || 0 ){
139 printf("tc:%5u ld:%5u prd:%5u tts:%5u twcas:%5u nmr0:%5u \r\n", start_tc
, global_step_ticker
->last_duration
, global_step_ticker
->period
, ticks_to_skip
, ticks_we_actually_can_skip
, LPC_TIM0
->MR0
);
140 for(unsigned int j
=0; j
< global_step_ticker
->stepper_motors
.size(); j
++){
141 StepperMotor
* stepper
= global_step_ticker
->stepper_motors
[j
];
142 if( stepper
->moving
){
143 uint32_t dd
= (uint32_t)((uint64_t)( (uint64_t)stepper
->fx_ticks_per_step
- (uint64_t)stepper
->fx_counter
) >> 32) ;
144 printf(" %u: %u\r\n", j
, dd
);
153 LPC_TIM0
->MR0
= global_step_ticker
->period
;
157 // Else obviously it's MR1
158 LPC_TIM0
->IR
|= 1 << 1; // Reset it
160 global_step_ticker
->reset_tick();
163 //printf("end: tc: %u mr0: %u mr1: %u d: %u\r\n", LPC_TIM0->TC, LPC_TIM0->MR0, LPC_TIM0->MR1, global_step_ticker->debug);
165 if( LPC_TIM0
->TC
> LPC_TIM0
->MR0
){
166 LPC_TIM0
->TCR
= 3; // Reset
167 LPC_TIM0
->TCR
= 1; // Reset