forked from Traumflug/Teacup_Firmware
-
Notifications
You must be signed in to change notification settings - Fork 0
/
timer.c
233 lines (193 loc) · 6.4 KB
/
timer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
#include "timer.h"
/** \file
\brief Timer management - step pulse clock and system clock
Teacup uses timer1 to generate both step pulse clock and system clock.
We achieve this by using the output compare registers to generate the two clocks while the timer free-runs.
Teacup has tried numerous timer management methods, and this is the best so far.
*/
#include <avr/interrupt.h>
#include "memory_barrier.h"
#include "arduino.h"
#include "config.h"
#ifdef HOST
#include "dda_queue.h"
#endif
/// how often we overflow and update our clock; with F_CPU=16MHz, max is < 4.096ms (TICK_TIME = 65535)
#define TICK_TIME 2 MS
/// convert back to ms from cpu ticks so our system clock runs properly if you change TICK_TIME
#define TICK_TIME_MS (TICK_TIME / (F_CPU / 1000))
/// time until next step, as output compare register is too small for long step times
uint32_t next_step_time;
#ifdef ACCELERATION_TEMPORAL
/// unwanted extra delays, ideally always zero
uint32_t step_extra_time = 0;
#endif /* ACCELERATION_TEMPORAL */
/// every time our clock fires, we increment this so we know when 10ms has elapsed
uint8_t clock_counter_10ms = 0;
/// keep track of when 250ms has elapsed
uint8_t clock_counter_250ms = 0;
/// keep track of when 1s has elapsed
uint8_t clock_counter_1s = 0;
/// flags to tell main loop when above have elapsed
volatile uint8_t clock_flag_10ms = 0;
volatile uint8_t clock_flag_250ms = 0;
volatile uint8_t clock_flag_1s = 0;
/// comparator B is the system clock, happens every TICK_TIME
ISR(TIMER1_COMPB_vect) {
// save status register
uint8_t sreg_save = SREG;
// set output compare register to the next clock tick
OCR1B = (OCR1B + TICK_TIME) & 0xFFFF;
/*
clock stuff
*/
clock_counter_10ms += TICK_TIME_MS;
if (clock_counter_10ms >= 10) {
clock_counter_10ms -= 10;
clock_flag_10ms = 1;
clock_counter_250ms++;
if (clock_counter_250ms >= 25) {
clock_counter_250ms = 0;
clock_flag_250ms = 1;
clock_counter_1s++;
if (clock_counter_1s >= 4) {
clock_counter_1s = 0;
clock_flag_1s = 1;
}
}
}
// restore status register
MEMORY_BARRIER();
SREG = sreg_save;
}
#ifdef HOST
/// comparator A is the step timer. It has higher priority then B.
ISR(TIMER1_COMPA_vect) {
// save status register
uint8_t sreg_save = SREG;
// Check if this is a real step, or just a next_step_time "overflow"
if (next_step_time < 65536) {
// step!
#ifdef DEBUG_LED_PIN
WRITE(DEBUG_LED_PIN, 1);
#endif
// disable this interrupt. if we set a new timeout, it will be re-enabled when appropriate
TIMSK1 &= ~MASK(OCIE1A);
// stepper tick
queue_step();
// led off
#ifdef DEBUG_LED_PIN
WRITE(DEBUG_LED_PIN, 0);
#endif
return;
}
next_step_time -= 65536;
// similar algorithm as described in setTimer below.
if (next_step_time < 65536) {
OCR1A = (OCR1A + next_step_time) & 0xFFFF;
} else if(next_step_time < 75536){
OCR1A = (OCR1A - 10000) & 0xFFFF;
next_step_time += 10000;
}
// leave OCR1A as it was
// restore status register
MEMORY_BARRIER();
SREG = sreg_save;
}
#endif /* ifdef HOST */
/// initialise timer and enable system clock interrupt.
/// step interrupt is enabled later when we start using it
void timer_init()
{
// no outputs
TCCR1A = 0;
// Normal Mode
TCCR1B = MASK(CS10);
// set up "clock" comparator for first tick
OCR1B = TICK_TIME & 0xFFFF;
// enable interrupt
TIMSK1 = MASK(OCIE1B);
}
#ifdef HOST
/*! Specify how long until the step timer should fire.
\param delay in CPU ticks
This enables the step interrupt, but also disables interrupts globally.
So, if you use it from inside the step interrupt, make sure to do so
as late as possible. If you use it from outside the step interrupt,
do a sei() after it to make the interrupt actually fire.
*/
void setTimer(uint32_t delay)
{
uint16_t step_start = 0;
#ifdef ACCELERATION_TEMPORAL
uint16_t current_time;
uint32_t earliest_time, actual_time;
#endif /* ACCELERATION_TEMPORAL */
// re-enable clock interrupt in case we're recovering from emergency stop
TIMSK1 |= MASK(OCIE1B);
// An interrupt would make all our timing calculations invalid,
// so stop that here.
cli();
CLI_SEI_BUG_MEMORY_BARRIER();
// Assume all steps belong to one move. Within one move the delay is
// from one step to the next one, which should be more or less the same
// as from one step interrupt to the next one. The last step interrupt happend
// at OCR1A, so start delay from there.
step_start = OCR1A;
if (next_step_time == 0) {
// new move, take current time as start value
step_start = TCNT1;
}
next_step_time = delay;
#ifdef ACCELERATION_TEMPORAL
// 300 = safe number of cpu cycles until the interrupt actually happens
current_time = TCNT1;
earliest_time = (uint32_t)current_time + 300;
if (current_time < step_start) // timer counter did overflow recently
earliest_time += 0x00010000;
actual_time = (uint32_t)step_start + next_step_time;
// Setting the interrupt earlier than it can happen obviously doesn't
// make sense. To keep the "belongs to one move" idea, add an extra,
// remember this extra and compensate the extra if a longer delay comes in.
if (earliest_time > actual_time) {
step_extra_time += (earliest_time - actual_time);
next_step_time = earliest_time - (uint32_t)step_start;
}
else if (step_extra_time) {
if (step_extra_time < actual_time - earliest_time) {
next_step_time -= step_extra_time;
step_extra_time = 0;
}
else {
step_extra_time -= (actual_time - earliest_time);
next_step_time -= (actual_time - earliest_time);
}
}
#endif /* ACCELERATION_TEMPORAL */
// Now we know how long we actually want to delay, so set the timer.
if (next_step_time < 65536) {
// set the comparator directly to the next real step
OCR1A = (next_step_time + step_start) & 0xFFFF;
}
else if (next_step_time < 75536) {
// Next comparator interrupt would have to trigger another
// interrupt within a short time (possibly within 1 cycle).
// Avoid the impossible by firing the interrupt earlier.
OCR1A = (step_start - 10000) & 0xFFFF;
next_step_time += 10000;
}
else {
OCR1A = step_start;
}
// Enable this interrupt, but only do it after disabling
// global interrupts (see above). This will cause push any possible
// timer1a interrupt to the far side of the return, protecting the
// stack from recursively clobbering memory.
TIMSK1 |= MASK(OCIE1A);
}
/// stop timers - emergency stop
void timer_stop() {
// disable all interrupts
TIMSK1 = 0;
}
#endif /* ifdef HOST */