Blame | Last modification | View Log | Download
/** Copyright (C) 2004 Darren Hutchinson (dbh@gbdt.com.au)** This program is free software; you can redistribute it and/or modify* it under the terms of the GNU Library General Public License as published by* the Free Software Foundation; either version 2 of the License, or (at your* option) any later version.** This program 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 Library General Public* License for more details.** You should have received a copy of the GNU Library General Public License* along with this software; see the file COPYING. If not, write to* the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,* MA 02111-1307, USA.** $Id: stepper.c,v 1.9 2004/04/05 06:42:15 dbh Exp $*//* This file converts the RA and DEC speed indications into drive values for* the stepper motor coils.*/#include <inttypes.h>#include <avr/io.h>#include <avr/interrupt.h>#include "eq6.h"#include "combine.h"#include "stepper.h"int8_t trackingRate = 0;uint8_t transRatio = DEF_TRANS_RATIO;#define STEPS_PER_CYCLE 32L /* Steps per cycle (complete set of phases) */#define CYCLES_PER_ROTN 12L /* Cycles per stepper motor rotation */#define SIDERIAL_LCM (long)(3 * 16) /* Divides to give all speeds */#define WORM_RATIO 180L /* Tooth on worm gear */#define SECS_PER_SDAY 86164L /* 23h, 56m, 4s [Sidereal] */#define SECS_PER_DAY 86400L /* 24h, 0m, 0s [Solar] */#define SECS_PER_LDAY 89309L /* 1 + 1/27.3 sidereal days *//* Structure holding information used to generate stepper pulses* that generate motion at the siderial, solar, and lunar tracking* rates*/struct trackRate_s{uint16_t div; // tintuint16_t adj; // add/drop one int every ....uint8_t doDropInt; // drop ints if true, add extra ints if false} trackRateTable[3];/* Define the stepping table. This defines the excitation to be used* over a complete "cycle" of the stepper motor** These are signed, four bit values. Coil 1 is the LSN, Coil 2 is the MSN*/#if 1/* Step table. Values scaled such that one coil is always fully driven.* Gives lots of torque, but the actual travel is lumpy*/uint8_t microTable[STEPS_PER_CYCLE] ={_EX_ENTRY(EX_0, EX_P_1), _EX_ENTRY(EX_P_0_2, EX_P_1),_EX_ENTRY(EX_P_0_4, EX_P_1), _EX_ENTRY(EX_P_0_67, EX_P_1),_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_0_67),_EX_ENTRY(EX_P_1, EX_P_0_4), _EX_ENTRY(EX_P_1, EX_P_0_2),_EX_ENTRY(EX_P_1, EX_0), _EX_ENTRY(EX_P_1, EX_M_0_2),_EX_ENTRY(EX_P_1, EX_M_0_4), _EX_ENTRY(EX_P_1, EX_M_0_67),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_0_67, EX_M_1),_EX_ENTRY(EX_P_0_4, EX_M_1), _EX_ENTRY(EX_P_0_2, EX_M_1),_EX_ENTRY(EX_0, EX_M_1), _EX_ENTRY(EX_M_0_2, EX_M_1),_EX_ENTRY(EX_M_0_4, EX_M_1), _EX_ENTRY(EX_M_0_67, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_0_67),_EX_ENTRY(EX_M_1, EX_M_0_4), _EX_ENTRY(EX_M_1, EX_M_0_2),_EX_ENTRY(EX_M_1, EX_0), _EX_ENTRY(EX_M_1, EX_P_0_2),_EX_ENTRY(EX_M_1, EX_P_0_4), _EX_ENTRY(EX_M_1, EX_P_0_67),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_0_67, EX_P_1),_EX_ENTRY(EX_M_0_4, EX_P_1), _EX_ENTRY(EX_M_0_2, EX_P_1),};#else/* Conventional microstep table. Torque vector with magnitude 1. Gives* less torque that the first table, but the change in smoothness doesn't* seem to be worth the loss of torque*/uint8_t microTable[STEPS_PER_CYCLE] ={_EX_ENTRY(EX_0, EX_P_1), _EX_ENTRY(EX_P_0_2, EX_P_1),_EX_ENTRY(EX_P_0_2, EX_P_1), _EX_ENTRY(EX_P_0_4, EX_P_0_67),_EX_ENTRY(EX_P_0_67, EX_P_0_67), _EX_ENTRY(EX_P_0_67, EX_P_0_4),_EX_ENTRY(EX_P_1, EX_P_0_2), _EX_ENTRY(EX_P_1, EX_P_0_2),_EX_ENTRY(EX_P_1, EX_0), _EX_ENTRY(EX_P_1, EX_M_0_2),_EX_ENTRY(EX_P_1, EX_M_0_2), _EX_ENTRY(EX_P_0_67, EX_M_0_4),_EX_ENTRY(EX_P_0_67, EX_M_0_67), _EX_ENTRY(EX_P_0_4, EX_M_0_67),_EX_ENTRY(EX_P_0_2, EX_M_1), _EX_ENTRY(EX_P_0_2, EX_M_1),_EX_ENTRY(EX_0, EX_M_1), _EX_ENTRY(EX_M_0_2, EX_M_1),_EX_ENTRY(EX_M_0_2, EX_M_1), _EX_ENTRY(EX_M_0_4, EX_M_0_67),_EX_ENTRY(EX_M_0_67, EX_M_0_67), _EX_ENTRY(EX_M_0_67, EX_M_0_4),_EX_ENTRY(EX_M_1, EX_M_0_2), _EX_ENTRY(EX_M_1, EX_M_0_2),_EX_ENTRY(EX_M_1, EX_0), _EX_ENTRY(EX_M_1, EX_P_0_2),_EX_ENTRY(EX_M_1, EX_P_0_2), _EX_ENTRY(EX_M_0_67, EX_P_0_4),_EX_ENTRY(EX_M_0_67, EX_P_0_67), _EX_ENTRY(EX_M_0_4, EX_P_0_67),_EX_ENTRY(EX_M_0_2, EX_P_1), _EX_ENTRY(EX_M_0_2, EX_P_1)};#endif /* 0 */uint8_t halfTable[STEPS_PER_CYCLE] ={_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_0), _EX_ENTRY(EX_P_1, EX_0),_EX_ENTRY(EX_P_1, EX_0), _EX_ENTRY(EX_P_1, EX_0),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_0, EX_M_1), _EX_ENTRY(EX_0, EX_M_1),_EX_ENTRY(EX_0, EX_M_1), _EX_ENTRY(EX_0, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_0), _EX_ENTRY(EX_M_1, EX_0),_EX_ENTRY(EX_M_1, EX_0), _EX_ENTRY(EX_M_1, EX_0),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1),_EX_ENTRY(EX_0, EX_P_1), _EX_ENTRY(EX_0, EX_P_1),_EX_ENTRY(EX_0, EX_P_1), _EX_ENTRY(EX_0, EX_P_1),};uint8_t fullTable[STEPS_PER_CYCLE] ={_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_P_1), _EX_ENTRY(EX_P_1, EX_P_1),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_P_1, EX_M_1), _EX_ENTRY(EX_P_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_M_1), _EX_ENTRY(EX_M_1, EX_M_1),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1),_EX_ENTRY(EX_M_1, EX_P_1), _EX_ENTRY(EX_M_1, EX_P_1)};/* Setup the table of divisors of the siderial interrupt use to* achieve the required tracking rate.*/struct{uint8_t divisor; // Siderial interrupts per stepuint8_t flags; // Control flags#define USE_RELAY 0 // Activate the magic relay [RA only]#define USE_MICRO 1 // Use the microstep table} rateConvert[] ={[SPEED_0_X] = {1, _BV(USE_MICRO)}, // Special value[SPEED_0_33_X] = {3 * SIDERIAL_LCM, _BV(USE_MICRO)},[SPEED_0_67_X] = {(3 * SIDERIAL_LCM) / 2, _BV(USE_MICRO)},[SPEED_1_X] = {SIDERIAL_LCM, _BV(USE_MICRO)},[SPEED_1_33_X] = {(3 * SIDERIAL_LCM) / 4, _BV(USE_MICRO)},[SPEED_1_67_X] = {(3 * SIDERIAL_LCM) / 5, _BV(USE_MICRO)},[SPEED_2_X] = {SIDERIAL_LCM / 2, _BV(USE_MICRO) | _BV(USE_RELAY)},[SPEED_4_X] = {SIDERIAL_LCM / 4, _BV(USE_MICRO) | _BV(USE_RELAY)},[SPEED_8_X] = {SIDERIAL_LCM / 8, _BV(USE_MICRO) | _BV(USE_RELAY)},[SPEED_16_X] = {SIDERIAL_LCM / 16, _BV(USE_RELAY)},[SPEED_SPIN] = {SIDERIAL_LCM / 16, 0}};/* Create the instance of the stepper excitation info*/struct excitation_s raExcitation;struct excitation_s decExcitation;/* Define instances of stepper state info*/struct stepState_s raState;struct stepState_s decState;uint8_t doHalfStep = 0;/* Info for tracking rate correction */uint16_t adjCtr;uint16_t adjLimit;uint16_t doDropInt;/* stepperInit() initializes the state of the stepper code.** The current implementation uses a single 16-bit timer with a fixed* period shared between RA and DEC.** Passed:* Nothing** Returns:* Nothing** Notes:* An alternate implementation would use a pair of 16 bit timers with* their timeouts set to the step period. This would minimize the* number of interrupts, but would take an extra timer.** The current implementation is preferred until we're sure the extra* timer isn't needed elsewhere or until there is a performance* problem caused by the extra interrupt load caused by having* multiple interrupts per step.*/voidstepperInit(void){/* Initialize the excitation state */raExcitation.excitation = EX_0;raExcitation.useRelay = 0;raState.pExcite = &raExcitation;decExcitation.excitation = EX_0;decState.pExcite = &decExcitation;/* Initialize the siderial rate timer */TIMSK |= _BV(OCIE1A);}/* calculateRateEntry() creates an entry in the rate table** Passed:* pEntry Pointer to entry* transRatio Transmission (gearbox) ratio* secPerDay Seconds per sideral/lunar/solar dat** Returns:* nothing*/static voidcalculateRateEntry( struct trackRate_s *pEntry,uint8_t transRatio,uint32_t secsPerDay){/* To do this calculation would (without optimization) would need about* 40 bit arithmetic, which isn't available in this copmiler.** To get the required precision down to below 32 bits the* numerator and denominator are divided through by 1280.** This gives an exact result for 8/16 MHz with a 180 tooth wormgear** The formula gives the clock divisor for the siderial clock interrupt* divisor:** (CLK_RATE * SECS_PER_SDAY) / (MECH_DIV * STEPS_PER_CYCLE * SIDERIAL_LCM)** This, by itself, does not give great accuracy because the divisor is* an integer. With a typical divisor of about 1500 there is a maximum error* of about 1 / 3000 (0.033%).** For most purposes this should be accurate enough, but the accuracy can* be improved by adding or dropping the occasional interrupt.** This function calculates both the divisor and how often to* drop an timer interrupt, or to insert an extra one to improve the* timer accuracy*/#define CLK_FACTOR 1280L // Common divisor for numerator & divisor#define WORM_CLK_LCM (WORM_RATIO * CYCLES_PER_ROTN * SIDERIAL_LCM)#define SCALED_CLK (CLK_RATE / CLK_FACTOR)#define MIN_DIV 1000 /* Minimum allowed divisor to avoid* interrupt saturation*/long top;long bottom;long div; // Clock divisor for interrupt generationlong adj; // Add/drop adjustmentlong adj_denom;top = SCALED_CLK * secsPerDay;bottom = (WORM_CLK_LCM / CLK_FACTOR) * transRatio * STEPS_PER_CYCLE;/* Calculate divisor, round to nearest integer */div = (top + (bottom / 2)) / bottom;/* Calculate adjustment */adj = SCALED_CLK * secsPerDay;adj_denom = (div * bottom) - (SCALED_CLK * secsPerDay);adj /= adj_denom;/* Fill in the entry */pEntry->div = (div > MIN_DIV) ? div : MIN_DIV;if (adj >= 0){pEntry->doDropInt = 0;pEntry->adj = (adj >= (1L << 16)) ? 0 : adj;}else{pEntry->doDropInt = 1;pEntry->adj = (adj <= -(1L << 16)) ? 0 : -adj;}}/* setupRateTable() fills the tracking rate table with values* that are correct for the transmission ratio of the system.** Passed* transRatio Transmission (gearbox) ratio** Returns* nothing*/voidsetupRateTable(uint8_t transRatio){calculateRateEntry(&trackRateTable[0], transRatio, SECS_PER_SDAY);calculateRateEntry(&trackRateTable[1], transRatio, SECS_PER_DAY);calculateRateEntry(&trackRateTable[2], transRatio, SECS_PER_LDAY);}/* setTrackRate() sets the tracking rate used by the stepper module.** Passed:* rate The tracking rate (index)** Returns:* Nothing** Note:* If an illegal rate is entered the current rate will not be changed*/voidsetTrackRate(int8_t rate){/* If the track rate is <0 then disable siderial rate use in* combine.c and return, leaving the current clock steup*/trackingRate = rate;if (rate < 0){noTrack = 1;return;}/* Do nothing if the rate is not supported */if (rate >= (sizeof(trackRateTable) / sizeof(struct trackRate_s)))return;/* Enable tracking */noTrack = 0;/* Update the tracking rate timer */OCR1A = trackRateTable[rate].div;TCNT1 = 0;TCCR1A = 0;TCCR1B = _BV(WGM12) | _BV(CS10);/* Update adjustment data */adjCtr = 0;adjLimit = trackRateTable[rate].adj;doDropInt = trackRateTable[rate].doDropInt;}/* setSpeed() is called by by the combiner to set the requested speed* for the axis** Passed:* pState Axis state* rate Requested rate** Returns:* Nothing** Notes:* setRaSpeed() and setDecSpeed() are wrappers used by the combiner*/static voidsetSpeed(struct stepState_s *pState, int8_t speed){/* If the current speed is zero then start the clock */if (pState->clkDivRatio == 0)pState->clkDivRatio = 1; // Almost immediate clockpState->reqSpeed = speed;}voidsetRaSpeed(int8_t speed){setSpeed(&raState, speed);}voidsetDecSpeed(int8_t speed){setSpeed(&decState, speed);}/* setTickRate() is called by the state machine to set the clock interrupt* rate.** Passed* pState The axis state* tickRate The clock rate to set** Returns* nothing*/voidsetTickRate(struct stepState_s *pState, uint8_t tickRate){pState->clkDivRatio = rateConvert[tickRate].divisor;}/* stepperProcess is the state machine that makes this whole thing* work! It is executed each axis interrupt to run the state machine* that handles operation and backlash processing.** Like the other state machines in the program it takes advantage* of the GNU computed goto to operate very efficiently.** Passed* pState The axis state** Returns* Nothing*/#define _GET_TABLE(f) ((f) ? (doHalfStep ? halfTable : microTable) : fullTable)voidstepperProcess(struct stepState_s *pState){// Step up the initial state pointerif (pState->pState == 0)pState->pState = &&enter_idle_pos;/* Make sure both finPos and finNeg are not set - that will* lead to a loop as the code tries to meet both!*/if (pState->finPos && pState->finNeg)pState->finPos = pState->finNeg = 0;// Jump to the current stategoto *pState->pState;/* There are six states in the machine** - idle_pos Idle (last move in positive direction)* - spin_pos Taking up backlash in positive direction* - move_pos Moving in the positive direction** There are "negative" versions of these states.** Just to make things simple we use the "idle" state as a central* decision point.*/enter_idle_pos:/* We're about to move into the idle_pos state. We end up here if* we're stopping or changing direction*/if (pState->reqSpeed == SPEED_0_X){/* We're going to stop - if we're in the correct direction then* stop, else start spinning in the other direction*/if (pState->finNeg)goto enter_spin_neg;else{/* Stop now! */setTickRate(pState, SPEED_0_X);pState->pExcite->excitation = EX_0;pState->pExcite->useRelay = 0;// For this state just call the entry point each interruptpState->pState = &&enter_idle_pos;}}else if (pState->reqSpeed > SPEED_0_X){/* We're now moving in the positive direction. As we are* already engaged in the positive direction we can start* running*/goto enter_move_pos;}else{/* Must be a negative move direction. Take up the backlash* in the negative direction*/goto enter_spin_neg;}return;enter_idle_neg:/* We're about to move into the idle_neg state. We end up here if* we're stopping or changing direction*/if (pState->reqSpeed == SPEED_0_X){/* We're going to stop - if we're in the correct direction then* stop, else start spinning in the other direction*/if (pState->finPos)goto enter_spin_pos;else{/* Stop now! */setTickRate(pState, SPEED_0_X);pState->pExcite->excitation = EX_0;pState->pExcite->useRelay = 0;// For this state just call the entry point each interruptpState->pState = &&enter_idle_neg;}}else if (pState->reqSpeed < SPEED_0_X){/* We're now moving in the negative direction. As we are* already engaged in the negative direction we can start* running*/goto enter_move_neg;}else{/* Must be a positive move direction. Take up the backlash* in the positive direction*/goto enter_spin_pos;}return;enter_spin_pos:/* Spin in the positive direction to take up backlash in the* gear chain*/if (pState->backlash == 0){/* No backlash - go to the idle_pos state which will take us* to the correct place*/goto enter_idle_pos;}else{uint8_t flags = rateConvert[SPEED_SPIN].flags;/* There is a backlash setting - get ready to spin! */pState->count = 0;setTickRate(pState, SPEED_SPIN);pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));pState->pExcite->useRelay = flags & _BV(USE_RELAY);pState->pState = &&run_spin_pos;// Fall through to run the spin state}run_spin_pos:// Update excitation valuepState->pExcite->excitation = pState->pTable[pState->stepCtr];pState->stepCtr = (pState->stepCtr + 1) & (STEPS_PER_CYCLE - 1);/* Check the count. If we've spun enough then go back to the* idle_pos state which will send us the right way*/if (++pState->count > pState->backlash)goto enter_idle_pos;return;enter_spin_neg:/* Spin in the negative direction to take up backlash in the* gear chain*/if (pState->backlash == 0){/* No backlash - go to the idle_neg state which will take us* to the correct place*/goto enter_idle_neg;}else{uint8_t flags = rateConvert[SPEED_SPIN].flags;/* There is a backlash setting - get ready to spin! */pState->count = 0;setTickRate(pState, SPEED_SPIN);pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));pState->pExcite->useRelay = flags & _BV(USE_RELAY);pState->pState = &&run_spin_neg;// Fall through to run the spin state}run_spin_neg:// Update excitation valuepState->pExcite->excitation = pState->pTable[pState->stepCtr];pState->stepCtr = (pState->stepCtr - 1) & (STEPS_PER_CYCLE - 1);/* Check the count. If we've spun enough then go back to the* idle_neg state which will send us the right way*/if (++pState->count > pState->backlash)goto enter_idle_neg;return;enter_move_pos:/* Start moving in the positive direction. Save the requested* speed as the current speed so we can detect changes in the* requested speed*/if (pState->reqSpeed > SPEED_0_X){uint8_t flags = rateConvert[pState->reqSpeed].flags;setTickRate(pState, pState->reqSpeed);pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));pState->pExcite->useRelay = flags & _BV(USE_RELAY);pState->pState = &&run_move_pos;pState->curSpeed = pState->reqSpeed;/* Fall through to move action */}else{/* We're not going in the positive direction any more */goto enter_idle_pos;}return;run_move_pos:if (pState->curSpeed == pState->reqSpeed){/* We're still moving at the same speed. Do it*/pState->pExcite->excitation = pState->pTable[pState->stepCtr];pState->stepCtr = (pState->stepCtr + 1) & (STEPS_PER_CYCLE - 1);}else{/* Go baxk to idle_pos that will decide the next state */goto enter_idle_pos;}return;enter_move_neg:/* Start moving in the negative direction. Save the requested* speed as the current speed so we can detect changes in the* requested speed*/if (pState->reqSpeed < SPEED_0_X){uint8_t flags = rateConvert[-pState->reqSpeed].flags;setTickRate(pState, -pState->reqSpeed);pState->pTable = _GET_TABLE(flags & _BV(USE_MICRO));pState->pExcite->useRelay = flags & _BV(USE_RELAY);pState->pState = &&run_move_neg;pState->curSpeed = pState->reqSpeed;/* Fall through to move action */}else{/* We're not going in the negative direction any more. Stop and* continue from there*/goto enter_idle_neg;}return;run_move_neg:if (pState->curSpeed == pState->reqSpeed){/* We're still moving at the same speed. Do it*/pState->pExcite->excitation = pState->pTable[pState->stepCtr];pState->stepCtr = (pState->stepCtr - 1) & (STEPS_PER_CYCLE - 1);}else{/* Go back to the idle_neg. It will determine the next state */goto enter_idle_neg;}return;}/* stepperInt() is called each siderial interrupt. This is divided down* in software to derive the actual stepper timing.** Passed:* Nothing** Returns:* Nothing*/SIGNAL(SIG_OUTPUT_COMPARE1A){/* Update the tracking rate adjustment counter */++adjCtr;/* If we're dropping then drop if necessary */if (doDropInt && adjLimit && adjCtr >= adjLimit){/* Drop interrupt */adjCtr = 0;return;}do_again:/* Run the state machine for the DEC and RA axis */if (raState.clkDivRatio != 0 && ++raState.divCtr >= raState.clkDivRatio){// Execute the RA state machineraState.divCtr = 0;stepperProcess(&raState);}if (decState.clkDivRatio != 0 && ++decState.divCtr >= decState.clkDivRatio){// Execute the DEC state machinedecState.divCtr = 0;stepperProcess(&decState);}/* If we need to "insert" an interrupt do it now */if (!doDropInt && adjLimit && adjCtr >= adjLimit){adjCtr = 0;goto do_again;}}