forked from osresearch/servostep
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservostep.ino
335 lines (279 loc) · 5.98 KB
/
servostep.ino
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
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
/** \file
* Closed loop servo with a stepper motor and quadrature encoder.
*
*/
#include "QuadDecode.h"
#define K_eps 10 // how close is "good enough"
#define MAX_SPEED 20000
#define SPEED_RAMP 100
static const float dt = 1.0 / 500; // 500 Hz update rate
static const unsigned dt_in_micros = 1.0e6 * dt;
//static const float max_j = 30000; // mm/s^3
static const float max_j = 30000; // mm/s^3
static const float max_a = 10000; // mm/s^2
static const float max_v = 2400; // mm/s
static const float steps_per_mm = (200 * 16) / 350.0;
static const float steps_per_tick = (200 * 16 / 4096);
static float a = 0;
static float j = 0;
static float v = 0;
#define STEP_PIN 10
#define DIR_PIN 13
void setup()
{
Serial.begin(115200);
pinMode(STEP_PIN, OUTPUT);
pinMode(DIR_PIN, OUTPUT);
// 50% duty cycle
analogWriteFrequency(STEP_PIN, 0);
analogWrite(STEP_PIN, 128);
}
static int16_t target;
void
servo_cmd(
int cmd
)
{
if (cmd < -65536 || cmd > +65535)
{
Serial.println("range");
return;
}
target = cmd;
}
void
read_command()
{
static int flush = 0;
static int new_cmd = 0;
static int sign = 1;
if (!Serial.available())
return;
const int c = Serial.read();
if (c == -1)
return;
if (c == '-')
{
sign = -1;
return;
}
if (c == '+')
{
sign = +1;
return;
}
if ('0' <= c && c <= '9')
{
new_cmd = 10 * new_cmd + c - '0';
return;
}
if (c == '\n')
{
if (!flush)
servo_cmd(sign * new_cmd);
flush = 0;
new_cmd = 0;
sign = 1;
return;
}
if (c == '\r')
return;
Serial.print('?');
new_cmd = 0;
flush = 1;
sign = 1;
return;
}
void velocity_cmd(int frequency)
{
int dir = 1;
if (frequency < 0)
{
frequency = -frequency;
dir = 0;
}
#if 0
uint32_t prescale;
for (prescale = 0; prescale < 7; prescale++)
{
uint32_t minfreq = (F_BUS >> 16) >> prescale;
if (frequency > minfreq)
break;
}
uint32_t mod = (((F_BUS >> prescale) + (frequency >> 1)) / frequency) - 1;
if (mod > 65535)
mod = 65535;
FTM0_SC = 0;
FTM0_MOD = mod;
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(prescale);
// make sure we don't glitch by not resetting FMT0_CNT
// FMT0_CNT = 0;
// should check that we haven't hit the overflow case
// if so our next pulse will be delayed
#endif
analogWriteFrequency(STEP_PIN, frequency);
// make sure we are going in the right diretion
digitalWriteFast(DIR_PIN, dir);
// and that our duty cycle is right
analogWrite(STEP_PIN, 128);
}
void
compute_speed(
int current_count,
int current_speed,
int target_count,
int target_speed,
int end_speed
)
{
// determine what phase we are in:
// accelerating towards the target speed,
// declerating towards the target speed
// maintaining speed
// decelerating towards the end speed
}
void loop()
{
// check for a command
read_command();
static unsigned last_now;
const unsigned now = micros();
if (now - last_now < dt_in_micros)
return;
last_now = now;
if (target < -max_v)
target = -max_v;
else
if (target > +max_v)
target = +max_v;
const float err_v = target - v;
const float eps = 1; // mm/s
// we want to start decelerating when our velocity is
// within this change. there is a little bit of slop here
const float max_v_change = a * a / max_j / 2 - 3;
if (fabs(err_v) < eps)
{
// hopefully we have hit our target
// and our acceleration ramped to zero so that
// this is not discontinuous
a = j = 0;
} else
if (err_v > max_v_change)
{
// we need to speed up to reach our desired speed.
// accelerate towards v
if (a < max_a)
j = +max_j;
else
j = 0;
} else
if (err_v > eps)
{
// our error is small, we need to start decelerating
if (a > -max_a)
j = -max_j;
else
j = 0;
} else
if (err_v < -max_v_change)
{
// accelerate towards target
if (a > -max_a)
j = -max_j;
else
j = 0;
} else
if (err_v < -eps)
{
// start decelerating
if (a < +max_a)
j = +max_j;
else
j = 0;
}
a += j * dt;
v += a * dt;
velocity_cmd(v * steps_per_mm);
Serial.printf("%+8.3f %+8.3f %+8.3f %+8d\r\n",
v,
a,
j,
QuadDecode.getCounter1()
);
#if 0
// compute the instantaneous delta in position
static int16_t old_count;
const int16_t count = QuadDecode.getCounter1();
int delta = count - old_count;
// fixup delta if it wraps around
if (delta > 32768)
delta -= 65536;
else
if (delta < -32768)
delta += 65536;
old_count = count;
// compute a smoothed velocity
static float speed;
speed = speed + delta - speed * (dt / 1.0e6);
if ((update_rate++ % 16) == 0)
{
//Serial.printf( "%d %+6d %+6d => %+6d %+6d\r\n", now, count, speed, target, current_speed);
Serial.printf( "%d %+6d %+6d %+6d => %+6d\r\n", now, count, delta, (int)(10 * speed), target);
}
compute_target(count, speed, target, target_speed, end_speed);
#if 0
static int old_target;
if (target != old_target)
{
velocity_cmd(target);
old_target = target;
}
#else
static int current_dir;
int error = target - count;
int dir = error > 0;
if (-K_eps < error && error < K_eps)
{
// stop the stepper by doing to a 0% duty cycle
cli();
analogWrite(STEP_PIN, 0);
sei();
current_speed = -1;
return;
}
if (current_speed == -1)
{
// we are resuming motion, restore the duty cycle
cli();
analogWrite(STEP_PIN, 128);
sei();
current_speed = 0;
}
if (current_dir != dir && current_speed > 0)
{
// we were going the other way, bring the speed back to 0
current_speed /= 2;
} else {
// ramp the speed up to the max
current_dir = dir;
if (current_speed < MAX_SPEED)
current_speed += SPEED_RAMP;
/*
current_speed = (current_speed * 255 + MAX_SPEED) / 256;
*/
}
// limit the speed to be proportional to the current change
// to avoid missing steps
const int delta_mag = abs(delta);
if (current_speed > (delta_mag+1) * 2000)
current_speed = (delta_mag+1) * 2000;
digitalWriteFast(DIR_PIN, current_dir);
cli();
// if interrupts are not disabled when these values are changed
// it seems that corruption of the registers can result.
analogWriteFrequency2(STEP_PIN, current_speed);
analogWrite(STEP_PIN, 128);
sei();
#endif
#endif
}