Stepper Sync Wheel: Group Sync

A small tweak to that code produces a sync pulse for each full sine wave of microstepping current, aligned with the step pulses. The sync pulse occurs on the rising edge of the current waveform (because I set it up that way) and has 50% duty cycle to allow triggering at either zero-current microstep.

Then pix like this happen:

Microstepping Group Sync

Microstepping Group Sync

The traces:

  • top = 1/group sync
  • middle = winding current at 500 mA/div
  • bottom = step pulse, 1/microstep

The big jump just before the zero-current microstep on the decreasing-current sides of the sine wave indicates that it’s hard to get all the current out of the windings at 12 V. A detail view of those steps shows that the current is 50% higher than it should be at the start of the zero-current microstep, having completely missed the last microstep:

Decreasing Current

Decreasing Current

Which, of course, is why I’m doing all this: to explore that kind of behavior.

You may find the generated sync pulses are off by ±1 microstep from the expected start of the zero-current microstep, because the optical 1/rev signal threshold may line up perversely with the start of a microstep. You can twist the sync wheel just slightly on the shaft, but it’s pretty much a matter of shaking the dice to see if a better combination comes up. Doesn’t make any real difference to the scope triggering, though, as any stable sync alignment is as good as any other.

The code uses the 1/rev optical sync pulse once, to get the initial alignment, so whacking the wheel as it rotates may cause the generated sync pulses to skip a beat or twenty. The result remains stable, just at a different alignment.

One could argue that you really don’t need the 1/rev optical signal at all, but I find it comforting to use an absolute rotational reference to lock the pulses in (pretty nearly) the same place every time. If you’re stuck with an in-place motor, then you probably don’t have a 1/rev signal and you must wing it.

The Arduino source code:

// Stepper motor driver synchronization
// Ed Nisley KE4ZNU June 2011

//-- Stepper parameters

#define SYNC_OFFSET            8        // steps from 1/rev pulse to start of first 4-full-step group

#define FULL_STEPS_PER_REV    200
#define MICROSTEPPING        8

#define GROUPS_PER_REV (FULL_STEPS_PER_REV / 4)
#define STEPS_PER_GROUP (MICROSTEPPING * 4)

//-- Pin definitions, all of which depend on internal hardware: do *not* change

#define PIN_REV        2                // INT0 = positive 1/rev pulse from optical switch
#define PIN_STEP    5                // T1 = positive 1/step pulse from stepper driver
#define PIN_TRIGGER    9                // OC1A = positive trigger pulse to scope

//-- Trace outputs may be chosen freely

#define PIN_TRACE_A    10
#define PIN_TRACE_B    11
#define PIN_TRACE_C    12

#define PIN_LED        13                // standard Arduino LED

//---------------------
// State Variables

word PulseCounter;

//---------------------
// Useful routines

//--- Input & output pins

void TogglePin(char bitpin) {
digitalWrite(bitpin,!digitalRead(bitpin));    // toggle the bit based on previous output
}

//----------------
// Initializations

void setup() {

pinMode(PIN_REV,INPUT);        // INT0 1/rev pulse from wheel

pinMode(PIN_STEP,INPUT);        // T1 step pulse from stepper driver

pinMode(PIN_LED,OUTPUT);
digitalWrite(PIN_LED,LOW);

pinMode(PIN_TRACE_A,OUTPUT);
pinMode(PIN_TRACE_B,OUTPUT);
pinMode(PIN_TRACE_C,OUTPUT);

//--- Prepare Timer1 to count external stepper drive pulses

TCCR1B = B00001000;                // Timer1: Mode 4 = CTC, TOP = OCR1A, clock stopped

pinMode(PIN_TRIGGER,OUTPUT);        // OC1A to scope trigger

//-- Wait for rising edge of 1/rev pulse from optical switch

TCCR1A = B11000000;                        // COM1A set on compare
TCNT1 = 0;                                // ensure we start from zero
OCR1A = SYNC_OFFSET;                        // set step counter

while(!digitalRead(PIN_REV)) {            // stall until 1/rev input rises
TogglePin(PIN_TRACE_A);
}

//-- Got it, fire up the timer to count steps to start of first group

TCCR1B |= B00000111;                        // enable clock from T1 pin, rising edge

digitalWrite(PIN_LED,HIGH);                // show we got here
digitalWrite(PIN_TRACE_A,LOW);

while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
continue;
}
TIFR1 |= _BV(OCF1A);                        // clear match flag

//-- Scope sync pulse is now active, we can enter the main loop

}

//----------------
// The main event

void loop() {

//-- Scope sync pulse active

digitalWrite(PIN_LED,LOW);                // show we got here
digitalWrite(PIN_TRACE_B,LOW);

//-- Set up for first half of the group, sync high -> low

TCCR1A = B10000000;                        // COM1A clear on compare
OCR1A = (STEPS_PER_GROUP / 2) - 1;

while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
continue;
}
TIFR1 |= _BV(OCF1A);                        // clear match flag
digitalWrite(PIN_TRACE_B,LOW);

//-- Set up for the second half, sync low -> high

TCCR1A = B11000000;                        // COM1A set on compare
OCR1A = (STEPS_PER_GROUP - (STEPS_PER_GROUP / 2)) - 1;    // may be odd, so allow for that

while(!(TIFR1 & _BV(OCF1A))) {            // wait for compare
digitalWrite(PIN_TRACE_B,digitalRead(PIN_STEP));
continue;
}
TIFR1 |= _BV(OCF1A);                        // clear match flag
digitalWrite(PIN_TRACE_B,LOW);

//-- Shut down counter and wait for end of 1/rev pulse

#if 0
TCCR1B &= ~B00000111;                        // turn off timer clock

while(digitalRead(PIN_REV)) {                // stall until 1/rev pulse goes low again
TogglePin(PIN_TRACE_C);
}
digitalWrite(PIN_TRACE_B,LOW);
#endif

}
About these ads