Simple driver for 5 phase Vexta stepper (1000 steps/revolution)

I recently salvaged parts from a discarded Shimadzu laboratory spectrophotometer and was surprised that the diffraction grating was directly driven by a 5-phase, ten wire Vexta stepper motor. The optics were in perfect shape and useful for other projects, but there is little information on the web about how to drive a five phase stepper, and I could find no working Arduino examples.

Of course, you can buy a five phase driver from Oriental Motor Company (Vexta manufacturer) for about USD $200, but another option is to use an Arduino Pro Mini, and 3 cheap (USD $5) brushed DC dual motor drivers from Pololu.

Motor specs: Vexta PX33M-A-C6 0.21A, 33 Ohms per phase, 0.36 degrees/step.

The low current rating and moderate winding resistance means that direct drive from 5-6V is possible, avoiding the need for a chopper stepper driver. I decided to use 3 Pololu DRV8835 dual full bridge modules, and with dead simple code the motor is capable of executing 500 steps/sec from a dead stop, without skipping a single step. Half step drive is available, for 2000 steps/revolution, capable of better than 500 steps/second.

Click on the in_action.mpg link in attachments to see it move in real time.

Important information below, used as a guide in creating the working example.

  1. Winding information: there is confusion on the web about motor coil and wiring color code with lead assignment. I started with this diagram, which supposedly reveals the coil and winding start/finish order:
    10wire_pentagon_connection.png

But the motor connector had white/yellow and grey/black reversed from the sequence above. The driver works with swapped connections (as long as both pairs of the two connections are reversed) but obeying the connector wire order resulted in the smoothest and presumably correct operation. That is, swap white/yellow and black/gray.

The decision to swap those leads is supported by this alternative assignment of wire colors and winding identifications. Evidently Oriental Motor is inconsistent with wire color codes.

  1. Coil excitation sequence, half and full step. Used to create the excitation lookup tables.

  2. Wiring diagram for two of the phases (micro used was the Pro Mini). The other three follow this pattern. Complete pin assignments and motor/driver connections are described in the code below. Image on Pololu product page: Pololu - DRV8835 Dual Motor Driver Carrier

  3. TOTALLY UNOPTIMIZED but fully functional code. Note that the drive tables are cyclic permutations of each other, so only one is required (ten bytes of storage in the optimized version).

// five phase drive for Vexta PX33M-A-C6 0.21A, 33R per phase, 0.36 degrees/step
// S. J. Remington 10/2020

// 3x Pololu DRV8835 dual motor driver
// **IN/IN mode selected**
// IN1 IN2 OUT1 OUT2 Truth Table
// 0   0   open open
// 1   0   H    L
// 0   1   L    H

// Vexta models PK and PX, wire colors in order around the motor as provided by manufacturer
//A - blue > red
//B - white > yellow (swapped)
//C - brown > purple
//D - black > gray  (swapped)
//E - orange > green

// drive sequences from 10_wire_sequence.png
// NOTE that these tables are cyclic permutations of each other, so only one is actually required.
//
// This is the simple, brute force RAM wasting version.
/*
  // half step
  //             1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20
  int AB[20] = { 1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 1, 1, 1, 1, 1, 1, 1, 1};
  int CD[20] = {-1,-1,-1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,-1,-1,-1,-1,-1,-1};
  int EF[20] = { 1, 1, 1, 1, 1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 1, 1, 1, 1};
  int GH[20] = {-1,-1,-1,-1,-1,-1,-1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,-1,-1};
  int JK[20] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0};
*/
// full step (1000 steps/revolution)
int AB[10] = {  0, -1, -1, -1, -1, 0, 1, 1, 1, 1};
int CD[10] = { -1, 0, 1, 1, 1, 1, 0, -1, -1, -1};
int EF[10] = {  1, 1, 0, -1, -1, -1, -1, 0, 1, 1};
int GH[10] = { -1, -1, -1, 0, 1, 1, 1, 1, 0, -1};
int JK[10] = {  1, 1, 1, 1, 0, -1, -1, -1, -1, 0};
//

int index = 0;
int index_max = 10;  //set to 10 or 20, depending on table selected above.

void setup() {

  //pin -> motor phase, wire color, driver in/out pins & driver chip assignments
  pinMode(2, OUTPUT); //A blue, I1, O1 MD1A
  pinMode(3, OUTPUT); //A red, I2, O2, MD1A
  pinMode(4, OUTPUT); //B yellow, I1, O1, MD1B
  pinMode(5, OUTPUT); //B white, I2, O2, MD1B
  pinMode(6, OUTPUT); //C brown, I1, O1, MD2A
  pinMode(7, OUTPUT); //C purple, I2, O2, MD2A
  pinMode(8, OUTPUT); //D grey, I1, O1, MD2B
  pinMode(9, OUTPUT); //D black, I2, O2, MD2B
  pinMode(11, OUTPUT); //E orange, I1, O1, MD3A
  pinMode(12, OUTPUT); //E green, I2, O2, MD3A

}

void loop() {  //execute 1000 steps at full speed, from dead stop
  for (int i = 0; i < 1000; i++) {
    if (AB[index] > 0) {
      digitalWrite(2, 1);
      digitalWrite(3, 0);
    }
    if (AB[index] == 0) {
      digitalWrite(2, 0);
      digitalWrite(3, 0);
    }
    if (AB[index] < 0) {
      digitalWrite(2, 0);
      digitalWrite(3, 1);
    }

    if (CD[index] > 0)  {
      digitalWrite(4, 1);
      digitalWrite(5, 0);
    }
    if (CD[index] == 0) {
      digitalWrite(4, 0);
      digitalWrite(5, 0);
    }
    if (CD[index] < 0)  {
      digitalWrite(4, 0);
      digitalWrite(5, 1);
    }

    if (EF[index] > 0)  {
      digitalWrite(6, 1);
      digitalWrite(7, 0);
    }
    if (EF[index] == 0) {
      digitalWrite(6, 0);
      digitalWrite(7, 0);
    }
    if (EF[index] < 0)  {
      digitalWrite(6, 0);
      digitalWrite(7, 1);
    }

    if (GH[index] > 0)  {
      digitalWrite(8, 1);
      digitalWrite(9, 0);
    }
    if (GH[index] == 0) {
      digitalWrite(8, 0);
      digitalWrite(9, 0);
    }
    if (GH[index] < 0)  {
      digitalWrite(8, 0);
      digitalWrite(9, 1);
    }

    if (JK[index] > 0)  {
      digitalWrite(11, 1);
      digitalWrite(12, 0);
    }
    if (JK[index] == 0) {
      digitalWrite(11, 0);
      digitalWrite(12, 0);
    }
    if (JK[index] < 0)  {
      digitalWrite(11, 0);
      digitalWrite(12, 1);
    }
    delay(2); //500 steps/second OK
    index++;  //forward motion.
//    index--; //reverse motion
    if (index >= index_max) index = 0;
    if (index < 0) index = index_max - 1; //fixed
  }
  delay(500);
}

For the pentagon wiring option, five half bridge drivers are used. A couple of L298 drivers would work fine with this motor. Step sequences for the pentagon connection are appended in the PDF file from Oriental Motor.

10wire_pentagon_connection.png

vexta_pentagon_phase_sequence.pdf (59.2 KB)

in_action.mpg (1.59 MB)

Code optimized for drive table storage:

//indexing tables optimized, half step mode selected
// five phase drive for Vexta PX33M-A-C6 0.21A, 33R per phase, 0.36 degrees/step
// S. J. Remington 10/2020

// 3x Pololu DRV8835 dual motor driver
// **IN/IN mode selected**
// Truth Table
// IN1 IN2 OUT1 OUT2
// 0   0   open open
// 1   0   H    L
// 0   1   L    H

// Vexta PK PX, wire colors -> APPARENT phase and coil polarity assignments
//A - blue > red        (start > finish)
//B - yellow > white
//C - brown > purple
//D - gray > black
//E - orange > green

// drive sequences from 10_wire_sequence.png

// half step (2000 steps/rev). Set index_max=20, skip=8
//                       1  2   3   4    5   6    7   8   9   10 11 12 13 14 15 16 17 18 19 20
  int coil_drive[20] = { 1, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,  1,  1,  1,  1,  1, 1,  1,  1};
  int index_max = 20;
  int skip = 8;

/*
// full step (1000 steps/revolution), set index_max=10, skip=4
int coil_drive[10] = {  0, -1, -1, -1, -1, 0, 1, 1, 1, 1};
int index_max = 10;
int skip = 4;
*/

//
// pin assignment table: coil, {start,finish}
//                       A       B       C       D       E
int pin_table [5][2] = {{2, 3}, {4, 5}, {6, 7}, {8, 9}, {11, 12}};
int index = 0;

void setup() {
  Serial.begin(9600);
  int i;
  for (i = 0; i < 10; i++) pinMode(pin_table[i], OUTPUT);  //motor control pins
}

void loop() {

  int k, permuted_indices[5];

  //execute 2000 steps at full speed, from dead stop
  for (int i = 0; i < 2000; i++) {
    permuted_indices[0] = index;
    permuted_indices[1] = (permuted_indices[0] + skip) % index_max;
    permuted_indices[2] = (permuted_indices[1] + skip) % index_max;
    permuted_indices[3] = (permuted_indices[2] + skip) % index_max;
    permuted_indices[4] = (permuted_indices[3] + skip) % index_max;
    for (int j = 0; j < 5; j++) { //loop over coils
      k = permuted_indices[j];
      if (coil_drive[k] > 0) {
        digitalWrite(pin_table[j][0], 1); // plus
        digitalWrite(pin_table[j][1], 0);
      }
      if (coil_drive[k] == 0) {
        digitalWrite(pin_table[j][0], 0); // off
        digitalWrite(pin_table[j][1], 0);
      }
      if (coil_drive[k] < 0) {
        digitalWrite(pin_table[j][0], 0); // minus
        digitalWrite(pin_table[j][1], 1);
      }
    } // end for j
    delay(2); //500 steps/second OK
    index++;  //forward
//    index--;  //reverse
    if (index >= index_max) index = 0;
    if (index < 0 )index = index_max - 1;
  }
  delay(500); //pause after one revolution for skipped step test.
}