Subversion Repositories svnkaklik

Rev

Details | Last modification | View Log

Rev Author Line No. Line
508 kaklik 1
/* 
2
 * Copyright (C) 2004 Darren Hutchinson (dbh@gbdt.com.au)
3
 * 
4
 * This program is free software; you can redistribute it and/or modify
5
 * it under the terms of the GNU Library General Public License as published by
6
 * the Free Software Foundation; either version 2 of the License, or (at your
7
 * option) any later version.
8
 *
9
 * This program is distributed in the hope that it will be useful, but
10
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
12
 * License for more details.
13
 * 
14
 * You should have received a copy of the GNU Library General Public License
15
 * along with this software; see the file COPYING.  If not, write to
16
 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
17
 * MA 02111-1307, USA. 
18
 *
19
 * $Id: stepper.c,v 1.9 2004/04/05 06:42:15 dbh Exp $
20
 */
21
 
22
/* This file converts the RA and DEC speed indications into drive values for
23
 * the stepper motor coils.
24
 */
25
 
26
#include <inttypes.h>
27
#include <avr/io.h>
28
#include <avr/interrupt.h>
29
 
30
#include "eq6.h"
31
#include "combine.h"
32
 
33
#include "stepper.h"
34
 
35
int8_t  trackingRate = 0;
36
uint8_t transRatio = DEF_TRANS_RATIO;
37
 
38
#define STEPS_PER_CYCLE     32L  /* Steps per cycle (complete set of phases) */
39
#define CYCLES_PER_ROTN     12L  /* Cycles per stepper motor rotation */
40
#define SIDERIAL_LCM        (long)(3 * 16)    /* Divides to give all speeds */
41
#define WORM_RATIO          180L /* Tooth on worm gear */
42
 
43
#define SECS_PER_SDAY       86164L      /* 23h, 56m, 4s [Sidereal] */
44
#define SECS_PER_DAY        86400L      /* 24h, 0m, 0s [Solar] */
45
#define SECS_PER_LDAY       89309L      /* 1 + 1/27.3 sidereal days */
46
 
47
/* Structure holding information used to generate stepper pulses
48
 * that generate motion at the siderial, solar, and lunar tracking
49
 * rates
50
 */
51
struct trackRate_s
52
{
53
    uint16_t    div;            // tint
54
    uint16_t    adj;            // add/drop one int every ....
55
    uint8_t     doDropInt;      // drop ints if true, add extra ints if false
56
} trackRateTable[3];
57
 
58
/* Define the stepping table. This defines the excitation to be used
59
 * over a complete "cycle" of the stepper motor
60
 *
61
 * These are signed, four bit values. Coil 1 is the LSN, Coil 2 is the MSN
62
 */
63
 
64
#if 1
65
/* Step table. Values scaled such that one coil is always fully driven.
66
 * Gives lots of torque, but the actual travel is lumpy
67
 */
68
uint8_t    microTable[STEPS_PER_CYCLE] =
69
{
70
    _EX_ENTRY(EX_0, EX_P_1),            _EX_ENTRY(EX_P_0_2, EX_P_1),
71
    _EX_ENTRY(EX_P_0_4, EX_P_1),        _EX_ENTRY(EX_P_0_67, EX_P_1),
72
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_0_67),
73
    _EX_ENTRY(EX_P_1, EX_P_0_4),        _EX_ENTRY(EX_P_1, EX_P_0_2),
74
 
75
    _EX_ENTRY(EX_P_1, EX_0),            _EX_ENTRY(EX_P_1, EX_M_0_2),
76
    _EX_ENTRY(EX_P_1, EX_M_0_4),        _EX_ENTRY(EX_P_1, EX_M_0_67),
77
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_0_67, EX_M_1),
78
    _EX_ENTRY(EX_P_0_4, EX_M_1),        _EX_ENTRY(EX_P_0_2, EX_M_1),
79
 
80
    _EX_ENTRY(EX_0, EX_M_1),            _EX_ENTRY(EX_M_0_2, EX_M_1),
81
    _EX_ENTRY(EX_M_0_4, EX_M_1),        _EX_ENTRY(EX_M_0_67, EX_M_1),
82
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_0_67),
83
    _EX_ENTRY(EX_M_1, EX_M_0_4),        _EX_ENTRY(EX_M_1, EX_M_0_2),
84
 
85
    _EX_ENTRY(EX_M_1, EX_0),            _EX_ENTRY(EX_M_1, EX_P_0_2),
86
    _EX_ENTRY(EX_M_1, EX_P_0_4),        _EX_ENTRY(EX_M_1, EX_P_0_67),
87
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_0_67, EX_P_1),
88
    _EX_ENTRY(EX_M_0_4, EX_P_1),        _EX_ENTRY(EX_M_0_2, EX_P_1),
89
};
90
#else
91
/* Conventional microstep table. Torque vector with magnitude 1. Gives
92
 * less torque that the first table, but the change in smoothness doesn't
93
 * seem to be worth the loss of torque
94
 */
95
uint8_t    microTable[STEPS_PER_CYCLE] =
96
{
97
    _EX_ENTRY(EX_0, EX_P_1),            _EX_ENTRY(EX_P_0_2, EX_P_1),
98
    _EX_ENTRY(EX_P_0_2, EX_P_1),        _EX_ENTRY(EX_P_0_4, EX_P_0_67),
99
    _EX_ENTRY(EX_P_0_67, EX_P_0_67),    _EX_ENTRY(EX_P_0_67, EX_P_0_4),
100
    _EX_ENTRY(EX_P_1, EX_P_0_2),        _EX_ENTRY(EX_P_1, EX_P_0_2),
101
 
102
    _EX_ENTRY(EX_P_1, EX_0),            _EX_ENTRY(EX_P_1, EX_M_0_2),
103
    _EX_ENTRY(EX_P_1, EX_M_0_2),        _EX_ENTRY(EX_P_0_67, EX_M_0_4),
104
    _EX_ENTRY(EX_P_0_67, EX_M_0_67),    _EX_ENTRY(EX_P_0_4, EX_M_0_67),
105
    _EX_ENTRY(EX_P_0_2, EX_M_1),        _EX_ENTRY(EX_P_0_2, EX_M_1),
106
 
107
    _EX_ENTRY(EX_0, EX_M_1),            _EX_ENTRY(EX_M_0_2, EX_M_1),
108
    _EX_ENTRY(EX_M_0_2, EX_M_1),        _EX_ENTRY(EX_M_0_4, EX_M_0_67),
109
    _EX_ENTRY(EX_M_0_67, EX_M_0_67),    _EX_ENTRY(EX_M_0_67, EX_M_0_4),
110
    _EX_ENTRY(EX_M_1, EX_M_0_2),        _EX_ENTRY(EX_M_1, EX_M_0_2),
111
 
112
    _EX_ENTRY(EX_M_1, EX_0),            _EX_ENTRY(EX_M_1, EX_P_0_2),
113
    _EX_ENTRY(EX_M_1, EX_P_0_2),        _EX_ENTRY(EX_M_0_67, EX_P_0_4),
114
    _EX_ENTRY(EX_M_0_67, EX_P_0_67),    _EX_ENTRY(EX_M_0_4, EX_P_0_67),
115
    _EX_ENTRY(EX_M_0_2, EX_P_1),        _EX_ENTRY(EX_M_0_2, EX_P_1)
116
};
117
#endif /* 0 */
118
 
119
uint8_t    halfTable[STEPS_PER_CYCLE] = 
120
{
121
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
122
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
123
 
124
    _EX_ENTRY(EX_P_1, EX_0),            _EX_ENTRY(EX_P_1, EX_0),
125
    _EX_ENTRY(EX_P_1, EX_0),            _EX_ENTRY(EX_P_1, EX_0),
126
 
127
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
128
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
129
 
130
    _EX_ENTRY(EX_0, EX_M_1),            _EX_ENTRY(EX_0, EX_M_1),
131
    _EX_ENTRY(EX_0, EX_M_1),            _EX_ENTRY(EX_0, EX_M_1),
132
 
133
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
134
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
135
 
136
    _EX_ENTRY(EX_M_1, EX_0),            _EX_ENTRY(EX_M_1, EX_0),
137
    _EX_ENTRY(EX_M_1, EX_0),            _EX_ENTRY(EX_M_1, EX_0),
138
 
139
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1),
140
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1),
141
 
142
    _EX_ENTRY(EX_0, EX_P_1),            _EX_ENTRY(EX_0, EX_P_1),
143
    _EX_ENTRY(EX_0, EX_P_1),            _EX_ENTRY(EX_0, EX_P_1),
144
};
145
 
146
uint8_t    fullTable[STEPS_PER_CYCLE] =
147
{
148
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
149
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
150
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
151
    _EX_ENTRY(EX_P_1, EX_P_1),          _EX_ENTRY(EX_P_1, EX_P_1),
152
 
153
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
154
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
155
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
156
    _EX_ENTRY(EX_P_1, EX_M_1),          _EX_ENTRY(EX_P_1, EX_M_1),
157
 
158
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
159
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
160
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
161
    _EX_ENTRY(EX_M_1, EX_M_1),          _EX_ENTRY(EX_M_1, EX_M_1),
162
 
163
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1),
164
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1),
165
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1),
166
    _EX_ENTRY(EX_M_1, EX_P_1),          _EX_ENTRY(EX_M_1, EX_P_1)
167
};
168
 
169
/* Setup the table of divisors of the siderial interrupt use to
170
 * achieve the required tracking rate.
171
 */
172
struct
173
{
174
    uint8_t        divisor;             // Siderial interrupts per step
175
    uint8_t        flags;               // Control flags
176
#define USE_RELAY        0              // Activate the magic relay [RA only]
177
#define USE_MICRO        1              // Use the microstep table
178
} rateConvert[] =
179
{
180
    [SPEED_0_X] = {1, _BV(USE_MICRO)},  // Special value
181
    [SPEED_0_33_X] = {3 * SIDERIAL_LCM, _BV(USE_MICRO)},
182
    [SPEED_0_67_X] = {(3 * SIDERIAL_LCM) / 2, _BV(USE_MICRO)},
183
    [SPEED_1_X] = {SIDERIAL_LCM, _BV(USE_MICRO)},
184
    [SPEED_1_33_X] = {(3 * SIDERIAL_LCM) / 4,  _BV(USE_MICRO)},
185
    [SPEED_1_67_X] = {(3 * SIDERIAL_LCM) / 5,  _BV(USE_MICRO)},
186
    [SPEED_2_X] = {SIDERIAL_LCM / 2, _BV(USE_MICRO) | _BV(USE_RELAY)},
187
    [SPEED_4_X] = {SIDERIAL_LCM / 4, _BV(USE_MICRO) | _BV(USE_RELAY)},
188
    [SPEED_8_X] = {SIDERIAL_LCM / 8, _BV(USE_MICRO) | _BV(USE_RELAY)},
189
    [SPEED_16_X] = {SIDERIAL_LCM / 16, _BV(USE_RELAY)},
190
 
191
    [SPEED_SPIN] = {SIDERIAL_LCM / 16, 0}
192
};
193
 
194
/* Create the instance of the stepper excitation info
195
 */
196
struct excitation_s     raExcitation;
197
struct excitation_s     decExcitation;
198
 
199
/* Define instances of stepper state info
200
 */
201
struct stepState_s      raState;
202
struct stepState_s      decState;
203
uint8_t                 doHalfStep = 0;
204
 
205
/* Info for tracking rate correction */
206
uint16_t        adjCtr;
207
uint16_t        adjLimit;
208
uint16_t        doDropInt;
209
 
210
/* stepperInit() initializes the state of the stepper code.
211
 *
212
 * The current implementation uses a single 16-bit timer with a fixed
213
 * period shared between RA and DEC.
214
 *
215
 * Passed:
216
 *      Nothing
217
 *
218
 * Returns:
219
 *      Nothing
220
 *
221
 * Notes:
222
 * An alternate implementation would use a pair of 16 bit timers with 
223
 * their timeouts set to the step period. This would minimize the
224
 * number of interrupts, but would take an extra timer.
225
 *
226
 * The current implementation is preferred until we're sure the extra
227
 * timer isn't needed elsewhere or until there is a performance
228
 * problem caused by the extra interrupt load caused by having
229
 * multiple interrupts per step.
230
 */
231
void
232
stepperInit(void)
233
{
234
    /* Initialize the excitation state */
235
    raExcitation.excitation = EX_0;
236
    raExcitation.useRelay = 0;
237
    raState.pExcite = &raExcitation;       
238
 
239
    decExcitation.excitation = EX_0;
240
    decState.pExcite = &decExcitation;       
241
 
242
    /* Initialize the siderial rate timer */
243
 
244
    TIMSK |= _BV(OCIE1A);
245
}
246
 
247
 
248
/* calculateRateEntry() creates an entry in the rate table
249
 *
250
 * Passed:
251
 *      pEntry          Pointer to entry
252
 *      transRatio      Transmission (gearbox) ratio
253
 *      secPerDay       Seconds per sideral/lunar/solar dat
254
 *
255
 * Returns:
256
 *      nothing
257
 */
258
static void
259
calculateRateEntry(             struct trackRate_s *pEntry, 
260
                                uint8_t transRatio, 
261
                                uint32_t secsPerDay)
262
{
263
    /* To do this calculation would (without optimization) would need about
264
     * 40 bit arithmetic, which isn't available in this copmiler.
265
     *
266
     * To get the required precision down to below 32 bits the
267
     * numerator and denominator are divided through by 1280.
268
     *
269
     * This gives an exact result for 8/16 MHz with a 180 tooth wormgear
270
     *
271
     * The formula gives the clock divisor for the siderial clock interrupt
272
     * divisor:
273
     *
274
     * (CLK_RATE * SECS_PER_SDAY) / (MECH_DIV * STEPS_PER_CYCLE * SIDERIAL_LCM)
275
     *
276
     * This, by itself, does not give great accuracy because the divisor is
277
     * an integer. With a typical divisor of about 1500 there is a maximum error
278
     * of about 1 / 3000 (0.033%).
279
     *
280
     * For most purposes this should be accurate enough, but the accuracy can
281
     * be improved by adding or dropping the occasional interrupt.
282
     *
283
     * This function calculates both the divisor and how often to
284
     * drop an timer interrupt, or to insert an extra one to improve the
285
     * timer accuracy
286
     */
287
#define CLK_FACTOR      1280L   // Common divisor for numerator & divisor
288
#define WORM_CLK_LCM    (WORM_RATIO * CYCLES_PER_ROTN * SIDERIAL_LCM)
289
#define SCALED_CLK      (CLK_RATE / CLK_FACTOR)
290
 
291
#define MIN_DIV         1000    /* Minimum allowed divisor to avoid
292
                                 * interrupt saturation
293
                                 */
294
 
295
    long        top;
296
    long        bottom;
297
    long        div;    // Clock divisor for interrupt generation
298
    long        adj;    // Add/drop adjustment
299
    long        adj_denom;
300
 
301
    top = SCALED_CLK * secsPerDay;
302
    bottom = (WORM_CLK_LCM / CLK_FACTOR) * transRatio * STEPS_PER_CYCLE;
303
 
304
    /* Calculate divisor, round to nearest integer */
305
    div = (top + (bottom / 2)) / bottom;
306
 
307
    /* Calculate adjustment */
308
    adj = SCALED_CLK * secsPerDay;
309
    adj_denom = (div * bottom) - (SCALED_CLK * secsPerDay);
310
 
311
    adj /= adj_denom;
312
 
313
    /* Fill in the entry */
314
    pEntry->div = (div > MIN_DIV) ? div : MIN_DIV;
315
 
316
    if (adj >= 0)
317
    {
318
        pEntry->doDropInt = 0;
319
        pEntry->adj = (adj >= (1L << 16)) ? 0 : adj;
320
    }
321
    else
322
    {
323
        pEntry->doDropInt = 1;
324
        pEntry->adj = (adj <= -(1L << 16)) ? 0 : -adj;
325
    }
326
}
327
 
328
/* setupRateTable() fills the tracking rate table with values
329
 * that are correct for the transmission ratio of the system.
330
 *
331
 * Passed
332
 *      transRatio      Transmission (gearbox) ratio
333
 *
334
 * Returns
335
 *      nothing
336
 */
337
void
338
setupRateTable(uint8_t transRatio)
339
{
340
    calculateRateEntry(&trackRateTable[0], transRatio, SECS_PER_SDAY);
341
    calculateRateEntry(&trackRateTable[1], transRatio, SECS_PER_DAY);
342
    calculateRateEntry(&trackRateTable[2], transRatio, SECS_PER_LDAY);
343
}
344
 
345
/* setTrackRate() sets the tracking rate used by the stepper module.
346
 *
347
 * Passed:
348
 *      rate            The tracking rate (index)
349
 *
350
 * Returns:
351
 *      Nothing
352
 *
353
 * Note:
354
 *      If an illegal rate is entered the current rate will not be changed
355
 */
356
void
357
setTrackRate(int8_t rate)
358
{
359
    /* If the track rate is <0 then disable siderial rate use in
360
     * combine.c and return, leaving the current clock steup
361
     */
362
    trackingRate = rate;
363
 
364
    if (rate < 0)
365
    {
366
        noTrack = 1;
367
        return;
368
    }    
369
 
370
    /* Do nothing if the rate is not supported */
371
    if (rate >= (sizeof(trackRateTable) / sizeof(struct trackRate_s)))
372
        return;
373
 
374
    /* Enable tracking */
375
    noTrack = 0;
376
 
377
    /* Update the tracking rate timer */
378
    OCR1A = trackRateTable[rate].div;
379
    TCNT1 = 0;
380
    TCCR1A = 0;
381
    TCCR1B = _BV(WGM12) | _BV(CS10);
382
 
383
    /* Update adjustment data */
384
    adjCtr = 0;
385
    adjLimit = trackRateTable[rate].adj;
386
    doDropInt = trackRateTable[rate].doDropInt;
387
}
388
 
389
/* setSpeed() is called by by the combiner to set the requested speed
390
 * for the axis
391
 *
392
 * Passed:
393
 *      pState          Axis state
394
 *      rate            Requested rate
395
 *
396
 * Returns:
397
 *      Nothing
398
 *
399
 * Notes:
400
 *      setRaSpeed() and setDecSpeed() are wrappers used by the combiner
401
 */
402
static void
403
setSpeed(struct stepState_s *pState, int8_t speed)
404
{
405
    /* If the current speed is zero then start the clock */
406
    if (pState->clkDivRatio == 0)
407
        pState->clkDivRatio = 1;        // Almost immediate clock
408
 
409
    pState->reqSpeed = speed;
410
 
411
}
412
 
413
void
414
setRaSpeed(int8_t speed)
415
{
416
    setSpeed(&raState, speed);
417
}
418
 
419
void
420
setDecSpeed(int8_t speed)
421
{
422
    setSpeed(&decState, speed);
423
}
424
 
425
/* setTickRate() is called by the state machine to set the clock interrupt
426
 * rate.
427
 *
428
 * Passed
429
 *      pState          The axis state
430
 *      tickRate        The clock rate to set
431
 *
432
 * Returns
433
 *      nothing
434
 */
435
void
436
setTickRate(struct stepState_s *pState, uint8_t tickRate)
437
{
438
    pState->clkDivRatio = rateConvert[tickRate].divisor;
439
}
440
 
441
/* stepperProcess is the state machine that makes this whole thing
442
 * work! It is executed each axis interrupt to run the state machine
443
 * that handles operation and backlash processing.
444
 *
445
 * Like the other state machines in the program it takes advantage
446
 * of the GNU computed goto to operate very efficiently.
447
 *
448
 * Passed
449
 *      pState          The axis state
450
 *
451
 * Returns
452
 *      Nothing
453
 */
454
#define _GET_TABLE(f)   ((f) ? (doHalfStep ? halfTable : microTable) : fullTable)
455
 
456
void
457
stepperProcess(struct stepState_s *pState)
458
{
459
    // Step up the initial state pointer
460
    if (pState->pState == 0)
461
        pState->pState = &&enter_idle_pos;
462
 
463
    /* Make sure both finPos and finNeg are not set - that will
464
     * lead to a loop as the code tries to meet both!
465
     */
466
    if (pState->finPos && pState->finNeg)
467
        pState->finPos = pState->finNeg = 0;
468
 
469
    // Jump to the current state
470
    goto *pState->pState;
471
 
472
    /* There are six states in the machine
473
     *
474
     * - idle_pos       Idle (last move in positive direction)
475
     * - spin_pos       Taking up backlash in positive direction
476
     * - move_pos       Moving in the positive direction
477
     *
478
     * There are "negative" versions of these states.
479
     *
480
     * Just to make things simple we use the "idle" state as a central
481
     * decision point.
482
     */
483
 
484
enter_idle_pos:
485
    /* We're about to move into the idle_pos state. We end up here if
486
     * we're stopping or changing direction
487
     */
488
    if (pState->reqSpeed == SPEED_0_X)
489
    {
490
        /* We're going to stop - if we're in the correct direction then
491
         * stop, else start spinning in the other direction
492
         */
493
        if (pState->finNeg)
494
            goto enter_spin_neg;
495
        else
496
        {
497
            /* Stop now! */
498
            setTickRate(pState, SPEED_0_X);
499
            pState->pExcite->excitation = EX_0;
500
            pState->pExcite->useRelay = 0;
501
 
502
            // For this state just call the entry point each interrupt
503
            pState->pState = &&enter_idle_pos;
504
        }
505
    }
506
    else if (pState->reqSpeed > SPEED_0_X)
507
    {
508
        /* We're now moving in the positive direction. As we are
509
         * already engaged in the positive direction we can start
510
         * running
511
         */
512
        goto enter_move_pos;
513
    }
514
    else
515
    {
516
        /* Must be a negative move direction. Take up the backlash
517
         * in the negative direction
518
         */
519
        goto enter_spin_neg;
520
    }
521
 
522
    return;
523
 
524
enter_idle_neg:
525
    /* We're about to move into the idle_neg state. We end up here if
526
     * we're stopping or changing direction
527
     */
528
    if (pState->reqSpeed == SPEED_0_X)
529
    {
530
        /* We're going to stop - if we're in the correct direction then
531
         * stop, else start spinning in the other direction
532
         */
533
        if (pState->finPos)
534
            goto enter_spin_pos;
535
        else
536
        {
537
            /* Stop now! */
538
            setTickRate(pState, SPEED_0_X);
539
            pState->pExcite->excitation = EX_0;
540
            pState->pExcite->useRelay = 0;
541
 
542
            // For this state just call the entry point each interrupt
543
            pState->pState = &&enter_idle_neg;
544
        }
545
    }
546
    else if (pState->reqSpeed < SPEED_0_X)
547
    {
548
        /* We're now moving in the negative direction. As we are
549
         * already engaged in the negative direction we can start
550
         * running
551
         */
552
        goto enter_move_neg;
553
    }
554
    else
555
    {
556
        /* Must be a positive move direction. Take up the backlash
557
         * in the positive direction
558
         */
559
        goto enter_spin_pos;
560
    }
561
    return;
562
 
563
enter_spin_pos:
564
    /* Spin in the positive direction to take up backlash in the
565
     * gear chain
566
     */
567
    if (pState->backlash == 0)
568
    {
569
        /* No backlash - go to the idle_pos state which will take us
570
         * to the correct place
571
         */
572
        goto enter_idle_pos;
573
    }
574
    else
575
    {
576
        uint8_t flags = rateConvert[SPEED_SPIN].flags;
577
 
578
        /* There is a backlash setting - get ready to spin! */
579
        pState->count = 0;
580
 
581
        setTickRate(pState, SPEED_SPIN);
582
        pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));
583
        pState->pExcite->useRelay = flags & _BV(USE_RELAY);
584
        pState->pState = &&run_spin_pos;
585
 
586
        // Fall through to run the spin state
587
    }
588
 
589
run_spin_pos:
590
    // Update excitation value
591
    pState->pExcite->excitation = pState->pTable[pState->stepCtr];
592
    pState->stepCtr = (pState->stepCtr + 1) & (STEPS_PER_CYCLE - 1);
593
 
594
    /* Check the count. If we've spun enough then go back to the
595
     * idle_pos state which will send us the right way
596
     */
597
    if (++pState->count > pState->backlash)
598
       goto enter_idle_pos;
599
    return;
600
 
601
enter_spin_neg:
602
    /* Spin in the negative direction to take up backlash in the
603
     * gear chain
604
     */
605
    if (pState->backlash == 0)
606
    {
607
        /* No backlash - go to the idle_neg state which will take us
608
         * to the correct place
609
         */
610
        goto enter_idle_neg;
611
    }
612
    else
613
    {
614
        uint8_t flags = rateConvert[SPEED_SPIN].flags;
615
 
616
        /* There is a backlash setting - get ready to spin! */
617
        pState->count = 0;
618
 
619
        setTickRate(pState, SPEED_SPIN);
620
        pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));
621
        pState->pExcite->useRelay = flags & _BV(USE_RELAY);
622
        pState->pState = &&run_spin_neg;
623
 
624
        // Fall through to run the spin state
625
    }
626
 
627
run_spin_neg:
628
    // Update excitation value
629
    pState->pExcite->excitation = pState->pTable[pState->stepCtr];
630
    pState->stepCtr = (pState->stepCtr - 1) & (STEPS_PER_CYCLE - 1);
631
 
632
    /* Check the count. If we've spun enough then go back to the
633
     * idle_neg state which will send us the right way
634
     */
635
    if (++pState->count > pState->backlash)
636
       goto enter_idle_neg;
637
 
638
    return;
639
 
640
enter_move_pos:
641
    /* Start moving in the positive direction. Save the requested
642
     * speed as the current speed so we can detect changes in the
643
     * requested speed
644
     */
645
    if (pState->reqSpeed > SPEED_0_X)
646
    {
647
        uint8_t flags = rateConvert[pState->reqSpeed].flags;
648
 
649
        setTickRate(pState, pState->reqSpeed);
650
        pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));
651
        pState->pExcite->useRelay = flags & _BV(USE_RELAY);
652
        pState->pState = &&run_move_pos;
653
        pState->curSpeed = pState->reqSpeed;
654
 
655
        /* Fall through to move action */
656
    }
657
    else
658
    {
659
        /* We're not going in the positive direction any more */
660
        goto enter_idle_pos;
661
    }
662
    return;
663
 
664
run_move_pos:
665
    if (pState->curSpeed == pState->reqSpeed)
666
    {
667
        /* We're still moving at the same speed. Do it
668
         */
669
        pState->pExcite->excitation = pState->pTable[pState->stepCtr];
670
        pState->stepCtr = (pState->stepCtr + 1) & (STEPS_PER_CYCLE - 1);
671
    }
672
    else
673
    {
674
        /* Go baxk to idle_pos that will decide the next state */
675
        goto enter_idle_pos;
676
    }
677
    return;
678
 
679
enter_move_neg:
680
    /* Start moving in the negative direction. Save the requested
681
     * speed as the current speed so we can detect changes in the
682
     * requested speed
683
     */
684
    if (pState->reqSpeed < SPEED_0_X)
685
    {
686
        uint8_t flags = rateConvert[-pState->reqSpeed].flags;
687
 
688
        setTickRate(pState, -pState->reqSpeed);
689
        pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));
690
        pState->pExcite->useRelay = flags & _BV(USE_RELAY);
691
        pState->pState = &&run_move_neg;
692
        pState->curSpeed = pState->reqSpeed;
693
 
694
        /* Fall through to move action */
695
    }
696
    else
697
    {
698
        /* We're not going in the negative direction any more. Stop and
699
         * continue from there
700
         */
701
        goto enter_idle_neg;
702
    }
703
    return;
704
 
705
run_move_neg:
706
    if (pState->curSpeed == pState->reqSpeed)
707
    {
708
        /* We're still moving at the same speed. Do it
709
         */
710
        pState->pExcite->excitation = pState->pTable[pState->stepCtr];
711
        pState->stepCtr = (pState->stepCtr - 1) & (STEPS_PER_CYCLE - 1);
712
    }
713
    else
714
    {
715
        /* Go back to the idle_neg. It will determine the next state */
716
        goto enter_idle_neg;
717
    }
718
    return;
719
}
720
 
721
/* stepperInt() is called each siderial interrupt. This is divided down
722
 * in software to derive the actual stepper timing.
723
 *
724
 * Passed:
725
 *      Nothing
726
 *
727
 * Returns:
728
 *      Nothing
729
 */
730
SIGNAL(SIG_OUTPUT_COMPARE1A)
731
{
732
    /* Update the tracking rate adjustment counter */
733
    ++adjCtr;
734
 
735
    /* If we're dropping then drop if necessary */
736
    if (doDropInt && adjLimit && adjCtr >= adjLimit)
737
    {
738
        /* Drop interrupt */
739
        adjCtr = 0;
740
        return;
741
    }
742
 
743
do_again:
744
    /* Run the state machine for the DEC and RA axis */
745
    if (raState.clkDivRatio != 0 && ++raState.divCtr >= raState.clkDivRatio)
746
    {
747
        // Execute the RA state machine
748
        raState.divCtr = 0;
749
        stepperProcess(&raState);
750
    }
751
 
752
    if (decState.clkDivRatio != 0 && ++decState.divCtr >= decState.clkDivRatio)
753
    {
754
        // Execute the DEC state machine
755
        decState.divCtr = 0;
756
        stepperProcess(&decState);
757
    }
758
 
759
    /* If we need to "insert" an interrupt do it now */
760
    if (!doDropInt && adjLimit && adjCtr >= adjLimit)
761
    {
762
        adjCtr = 0;
763
        goto do_again;
764
    }
765
}