Driving SLA7062M Stepper motor controler

Status
Not open for further replies.

BReeves

Well-known member
Hi folks, been a while.. Have this idea of building a Stepper motor controller for the Y axis on my Mini Mill using a Teensy driving a SLA7062M Stepper IC. Reason for the SLA7062 is because several years ago I purchased a CNC kit to build a CNC router which never happened. That kit has 3 stepper motors that are large enough to do what I need plus the driver IC's. Pretty sure the motors will need to use the high power drivers, haven't checked but pretty sure the Teensy will not be able to drive the motors directly as shown on the PJRC site. The kit was designed to be controlled by a computer parallel port using freeware CNC software. I really do not want or need to do a full CNC conversion but it would be nice to have the Y axis motorized and would like to replace the PC with a Teensy.

Off the top of my head the functions I would like are as follows.

Move table at a given speed, set with a keypad?
Move table to limits both left and right.
Move table to a given position, keypad entry?
Move table from that position to a set position, keypad entry?

That is all I can think of for now.

Searching for Teensy + the SLA7062M IC hasn't been fruitful and am guessing I will pretty much need to start from scratch. I have the data sheet for the IC and have used the Teensy on several projects and would like to sty with it for many reasons.

Guess what I'm asking is has anyone driven the SLA7062 with a Teensy and do you think this idea is even feasible?
Thanks
 
Steppers aren't generally complex to drive, though this
https://media.digikey.com/pdf/Data Sheets/Sanken/SLA7060-62,65-67M.pdf
suggests they do have some special features, but you would be setting those in setup and leaving them alone in terms of micro stepping mode and reset.

Actual drive pin seems to be clock and direction (you don't need to track phases yourself) so you don't need a library so much as a function to clock out X steps, possibly with a ramp in/ramp out function given your mill will have a bit of inertia.
 
Thanks, been doing a little research and right now the biggest issue is laying out a circuit board. I use Express PCB software for allot of reasons and have not been able to locate a board component for the IC. It's fairly easy to create a component in Express but was hoping I didn't have to. I would like to build up a test board with the IC and a Teensy 2.0. I did a board component for the Teensy some time ago and looks like I will have no choice but to make one for the IC.

I have most everything I will need to build the test board in my electronics stash. This may take a while but I will be updating this thread with my progress as I go. Probably shouldn't be designing around that IC but I have 8 of the durn things and it looks to be a quick way to get where I want to go.
 
@BReeves "Mini Mill" sounds like you are using rather small motors? NEMA17?
In that case, rather than designing your own stepper driver board I'd recommend to get one of the now omnipresent DRV8825 or A4988 boards. They are good for about 1A phase current and you'll get them for < 10$, (< 2$ if you don't mind china clones ). This will get you going quickly without the hassle of first designing a board. If you need more power later on, you can still upgrade to a more professional driver.

There are some stepper libraries around. Most famous is the AccelStepper library. You can also try my TeensyStep.
 
The motors I have came from Hobby CNC years ago. Not sure exactly what they are, from what I could find they may be NEMA23's. 3.6VDC 2AMP 127 OZ-IN. Thought when I get my test setup working I could tell if they were going to be big enough for the mill. Appreciate the links will give me a starting place when I get to the Teensy code.
 
Well I have the circuit board finished, I think.. Still need to double check everything before I actually order the boards. Basically followed the schematic from the data sheet, ran every control wire for the stepper IC to the Teensy which will give complete control from the Teensy. Added connectors for a key pad and numeric display from Adafruit. Not sure if a basic 4 X 3 key pad will give me enough keys, thinking I can use # or * for the decimal and the other one for enter At least it will be a start, will probably know more about what is needed when I get into the code it will take to make this all work.

Didn't see a way to upload an image to the forum, put it on my old business web site.
http://www.public.somethinxtra.com/Board1.jpg
.
 
Just updated the image in the above link to what I believe is the final version. I updated it for a 4x4 keypad to have a few more buttons and fixed a few issues discovered when double checking the connections. Also added a jumper to disconnect power from the Teensy when it is connected to the computer. Have all the parts ordered including the boards. When everything gets here and I get one built I will be able to go to work on the code.
 
It works, sorta...
Started with the basic motor functions.
I can enter a speed on the keypad and the motor will run at that speed. Right now it's just numbers that sets the value of the delayMicroseconds() call in the loop. Discovered I can go from 600 to 9999 with the motor I am using. Eventually I hope to do a conversion from inches per second but that will have to wait till I get it hooked to the mill to see how the delay value relates to the actual table speed.
'A' key on the keypad starts and stops the motor.
'B' key will toggle the direction Forward/Reverse.
Uses two 4 digit 7 segment Numeric displays to display the speed and if it's set to Forward or Reverse. Have an Alpha Numeric display ordered to play with. Eventually I hope to be able to enter a floating point number for table movement and may need all 8 digits of the existing display.

That's about it for now, it's usable as is for power feed. I have the mill in pieces, in the middle of upgrading it with the Fixed column large table kit from Little Machine Shop, installing a DRO and figuring out how to mount and connect the stepper motor. Will be a while before I get back to the next generation of software for the Teensy.
 
Hope you'll share a photo when it's working with the machine.

To put a picture in your message, click this icon.

sc.png

Click the "From Computer" tab. If the uploader doesn't work on your browser or is too complicated, look for the "Basic Uploader" link.
 
Will do. About have the mill back together, waiting for a friend to stop by and help me move it back over to it's home bench. Even in pieces two of the pieces weigh more than I do :) once I get it moved I can finish putting it together, I need it working to make the mount and drive connection for the motor.

In the meantime I have added the ability to Jog X number of steps to the Teensy code. Enter a number, press the Jog button (D on the keypad) every time Run is pressed it will move the steeper the number of steps entered. Pressing the Free Run button (C) puts it back in continuous run mode. This about wraps up all the functions I will need for a power feed. Thinking ahead I have already added a 4 pin header to the circuit board for the left over Teensy pins. May need limit switches or additional function switches when I start looking at the more sophisticated functions I would like it to have. Fun project...
 
Pretty happy with the way my stepper motor powered X axis on my mini mill is now working. Thought I would go ahead and show my code. Not the most experienced coder in the world but it works. Still plan on making a video of it operating but that will need to wait till I get it packaged and finish the mill installation.

Basically the way it works....
Enter a speed number in Inches Per Minute on the Keypad and hit the Run (C) key. When the Go (A) key is pressed it will run continuously till the Go (A) key is pressed again.
Direction can be changed with the Direction (B) key, it toggles between Left And Right. The last digit on the Alpha display shows the present direction L or R.
Enter a whole or decimal (Decimal point is the # key) number on the keypad and press the Jog (D). Now when Go is pressed the table will move at the speed set with Run in the direction last selected with the Direction key as far as the number entered in inches. Resolution is 3 decimal places.
The Jog function will compensate for backlash when changing directions and can be stopped with a push button.

Two values are stored in the EEPROM and can be set by switching a toggle switch to Program mode.
Micro Steeping, enter 2 for 1/2, 4 for 1/4, 8 for 1/8, 16 for 1/16 and hit the Run button to store.
Backlash, enter a decimal number and hit the Jog key to store.

I have the ExpressPCB files but the boards need to be ordered using ExpressPCB's MiniBoard service which means you get 3 boards for about $50.00.
I ordered a couple extra SLA7062 IC's from an eBay seller that is selling pulls, was afraid of the $5.00 chips from China. Odds are pretty good they are fake and will not work.
The stepper motor I am using is a Unipolar NEMA23 127oz 1.8deg motor I had from HobbyCNC. The SLA7062 will not work with Bipolar motors.
As said in the code comments schematis is basically the same as the schematic shown on the SLA7062 data sheet, Teensy2.0 pin connections can be derived from the code.

Code:
/*
Stepper motor powered X axis for Mini Mill using SLA7062 Stepper Control IC.
Board is basically the same as the schematic on the SLA7062 spec sheet. Board Layout 
using ExpressPC's free software.
For Teensy 2.0. 
4X4 keypad for input.
Toggle switch on Pin 7 to place controller in program mode for writing Micro Step and Backlash 
settings to EEPROM.
Push button switch on Pin 0 for interrupt driven emergency stop in Jog mode.
Adafruit 7 segment 4 digit display for numbers and 4 digit Alphanumeric for mode display
Two modes of operation.
Run at entered Inches Per Minute speed and Jog an entered distance at the Run speed.
Version 3 for Revision-2 Board 9-28-2017
*/
#include <Keypad.h>
#include "Adafruit_LEDBackpack.h"
#include "Adafruit_GFX.h"
#include <EEPROM.h>

Adafruit_7segment matrix1 = Adafruit_7segment();
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();

const byte ROWS = 4; //four rows
const byte COLS = 4; //Four columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};

byte rowPins[ROWS] = {15, 14, 13, 12}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {19, 18, 17, 16}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

int SpeedIPM = 10;     // Free Run Speed in Inches Per Minute
int SpeedVal;          // Calculated delay
float SteepsVal = 0;  // Number of steps to jog 
float NewNumber = 0;  // entered number
byte NumIndex = 0;    // String index for key entry
char NumString[11];   // String to hold keys
bool isNumber = false; // Keypad Number or Function
bool AddDecPoint = false; // for float numbers
bool RunForward = true;  // Forward or Reverse
bool LastDirection = RunForward; // For backlash compensation
bool Running = false;   // Start/Stop flag
bool Continuous = true; // Run mode
byte SavedState = 0; // Save state Run or Jog from program mode.
bool isJoging = false; // We are in Jog mode
float InchSteps = 0;    // Calculated steps to move 1 inch
bool ProgMode = false; // In program mode or not
int EpAddress = 0;  // EEPROM Address
char EpValue = 0;   // EEPROM Value
bool DoneOnce = false;
const int StepsPerRev = 200;  // Motor Steps per revolution(MSPR)
const float ScrewIPR = 0.050; //Lead screw inches per revolution(SIPM)

#define BLSH 0
#define FRAS 1
#define PROG 2

float BkLash = .016; // Backlash (BLSH) compensation when changing direction.
byte FracStepSet = 2; // (FRAS) 2 for 1/2, 4 for 1/4, 8 for 1/8, 16 for 1/16
                             
const int DisablePin = 4;
const int ResetPin = 8;
const int DirectionPin = 10;
const int ClockPin = 9;
const int M2Pin = 2;
const int M1Pin = 3;
const int SyncPin = 1;
const int EmerStopPin = 7; // Future for IRQ based Stop
const int ProgPin = 0; // Closed = Program mode for backlash and Micro Step select

// The following pins are routed to header pins on Revision-2 Board
//const int RtLimitPin = 20; // ?Future for limit switch to HIGH INPUT
//const int LtLimitPin = 21; // ?Future for limit switch to HIGH INPUT

void EmergencyStop() { // Interrupt function
  digitalWrite(DisablePin, LOW); // Disable motor
  Running = false;  // Exit Jog Loop
}

float GetDelayinUseconds(int ipm) {
  float n = 60 / (ipm * InchSteps);
  return(n * 1000000);
}

void ZeroDisplay() {
    matrix1.setBrightness(3);
    matrix1.writeDigitNum(0, 0x00); 
    matrix1.writeDigitNum(1, 0x00); 
    matrix1.writeDigitNum(3, 0x00); 
    matrix1.writeDigitNum(4, 0x00); 
    matrix1.writeDisplay();
    alpha4.setBrightness(3);
    alpha4.clear();
    alpha4.writeDisplay();
}
void UpdateDisplay() {
    if(Continuous) {
      alpha4.writeDigitAscii(0, 'R');
      alpha4.writeDigitAscii(1, 'U');
      alpha4.writeDigitAscii(2, 'N', true);
      if(SpeedIPM != 0){
        matrix1.print(SpeedIPM, DEC);
        matrix1.writeDisplay();
      }
    }
    else { // if Jog
      alpha4.writeDigitAscii(0, 'J');
      alpha4.writeDigitAscii(1, 'O');
      alpha4.writeDigitAscii(2, 'G', true);
      if(SteepsVal != 0){
        if(SteepsVal == ceil(SteepsVal)){ // test for float
           matrix1.print(int(SteepsVal), DEC);
        }
        else {
           matrix1.print(SteepsVal, 3);
        }
        matrix1.writeDisplay();
      }
    }
    if(RunForward){
      alpha4.writeDigitAscii(3, 'L');
    }
    else {
      alpha4.writeDigitAscii(3, 'R');    
    }
    alpha4.writeDisplay();
}
void ShowProgramMode(byte mode) {
  if(mode == FRAS) {
    alpha4.writeDigitAscii(0, 'F');
    alpha4.writeDigitAscii(1, 'R');
    alpha4.writeDigitAscii(2, 'A');
    alpha4.writeDigitAscii(3, 'S');
  }
  else if(mode == BLSH) {
    alpha4.writeDigitAscii(0, 'B');
    alpha4.writeDigitAscii(1, 'K');
    alpha4.writeDigitAscii(2, 'L');
    alpha4.writeDigitAscii(3, 'S');
  }
  else { // PROG
    ZeroDisplay();
    alpha4.writeDigitAscii(0, 'P');
    alpha4.writeDigitAscii(1, 'R');
    alpha4.writeDigitAscii(2, 'O');
    alpha4.writeDigitAscii(3, 'G');
    NumIndex = 0; // reset input string
    NumString[NumIndex] = '\0';
  }      
  alpha4.writeDisplay();
}
void SetStepPins() {
  switch (FracStepSet) { 
    case 2: // 1/2 step Max with this chip
      digitalWrite(M1Pin, HIGH);
      digitalWrite(M2Pin, HIGH); 
      break;
    case 4: // 1/4 step
      digitalWrite(M1Pin, HIGH);
      digitalWrite(M2Pin, LOW); 
      break;
    case 8: // 1/8 step
      digitalWrite(M1Pin, LOW);
      digitalWrite(M2Pin, HIGH); 
      break;
    case 16: // 1/16 step
      digitalWrite(M1Pin, LOW);
      digitalWrite(M2Pin, LOW); 
      break;
  }
}
void EpromWrite(byte whatfunction){
  if(whatfunction == BLSH) {
    for (EpAddress = 0; EpAddress < 4; EpAddress++) {
      EEPROM.write(EpAddress, NumString[EpAddress]);
    }
  }
  if(whatfunction == FRAS) {
    EEPROM.write( 4, NumString[0]);
  
  }
}

void EpromRead(){
  for (EpAddress = 0; EpAddress < 5; EpAddress++){
    switch (EpAddress) {
      case 0:
        EpValue = EEPROM.read(EpAddress);
        if(EpValue == 46) {
          strcpy(NumString, ".");
        }
        break;
      case 1 ... 3:
        EpValue = EEPROM.read(EpAddress);
        NumString[EpAddress] = EpValue; // - 48;
        NumString[EpAddress + 1] = '\0';
        if(EpAddress == 3) {
          BkLash = atof(NumString);
          NumString[0] = '\0';
        }
        break;
      case 4: // 
        FracStepSet = EEPROM.read(EpAddress) - 48;
        if(FracStepSet == 1) {
          FracStepSet = 16;
        }  
        break;
    }    
  }
} 

void setup() {
  Serial.begin(9600); 
  pinMode(DisablePin, OUTPUT);
  digitalWrite(DisablePin, LOW); // Disabled
  
  pinMode(DirectionPin, OUTPUT);
  digitalWrite(DirectionPin, LOW); // Forward
  
  pinMode(M1Pin, OUTPUT);
  pinMode(M2Pin, OUTPUT); 
  pinMode(ClockPin, OUTPUT); // High advances motor one step
  digitalWrite(ClockPin, LOW);

  pinMode(SyncPin, OUTPUT);
  digitalWrite(SyncPin, HIGH); 
  pinMode(ResetPin, OUTPUT);
  digitalWrite(ResetPin, LOW);
  
  pinMode(EmerStopPin, INPUT_PULLUP); 
  attachInterrupt(EmerStopPin, EmergencyStop, FALLING);
  pinMode(ProgPin, INPUT_PULLUP);
 
  matrix1.begin(0x70); // Numeric display, default address
  alpha4.begin(0x72); // Alpha display address Jumper A1 on backpack for 72

  if(EEPROM.read(4) != 255){
    EpromRead();
  }
  SetStepPins(); 
  InchSteps = 1 / (ScrewIPR / (StepsPerRev * FracStepSet)); 
  SpeedVal = GetDelayinUseconds(SpeedIPM);
//  Serial.println(SpeedVal); // checking math
//  Serial.println(InchSteps); // checking math
  ZeroDisplay();     
  ShowProgramMode(BLSH); // Show saved settings on startup
  matrix1.print(BkLash, 3);
  matrix1.writeDisplay();
  delay(3000);
  ShowProgramMode(FRAS);
  matrix1.print(FracStepSet, DEC);
  matrix1.writeDisplay();
  delay(3000);  
  UpdateDisplay();
}  
void loop(){
  if(!Running) { // Only check switch if not running
    if(digitalRead(ProgPin)==LOW){
      if(!DoneOnce) {
        ShowProgramMode(PROG);
        if(Continuous) {
          SavedState = 0;
        }
        else {
          SavedState = 1;
        }
      }
      DoneOnce = true;
      ProgMode = true;  
    }
    else if(digitalRead(ProgPin) == HIGH){
      ProgMode = false; 
      if(DoneOnce){
        if(SavedState == 0) { // Restore running state
          Continuous = true; 
          isJoging = false; 
        }
        else {
          Continuous = false;
          isJoging = true;  
        }
        UpdateDisplay(); 
      }  
      DoneOnce = false;
    }
  }  
  char key = keypad.getKey();
  if (key != NO_KEY){
    switch (key) { // What key was pressed
      case '0' ... '9':
        isNumber = true;
        break;
      case '*': // Clear / Reset to free run not running
        isNumber = false;
        Running = false;
        ZeroDisplay();
        alpha4.blinkRate(0);
        alpha4.writeDisplay();
        digitalWrite(DisablePin, LOW); // Disabled
        RunForward = true;
        digitalWrite(DirectionPin, LOW); // Left
        Continuous = true;
        isJoging = false;
        NewNumber = 0;
        NumIndex = 0;
        SpeedIPM = 10;
        NumString[NumIndex] = '\0';
        UpdateDisplay();
        break;
      case '#': // Dec Point
        isNumber = true;
        break;
      case 'A': // Go / stop
        isNumber = false;
        Running = !Running;
        if(Running) {
          UpdateDisplay();
          alpha4.blinkRate(2);
          alpha4.writeDisplay();
          digitalWrite(DisablePin, HIGH); // Enabled
        }
        else {
          alpha4.blinkRate(0);
          alpha4.writeDisplay();
          digitalWrite(DisablePin, LOW); // Disabled
          LastDirection = RunForward;
        }
        break;
      case 'B': // Direction
        isNumber = false;
        if(!Running) { // Only change if stopped
          RunForward = !RunForward;
          if(RunForward){
            digitalWrite(DirectionPin, LOW); // Left
            alpha4.writeDigitAscii(3, 'L');
          }
          else {
            digitalWrite(DirectionPin, HIGH); // Right   
            alpha4.writeDigitAscii(3, 'R');
          }
          alpha4.writeDisplay();
        }
        break;
      case 'C': // Run Continuous And Step in program mode
        isNumber = false;
        if(ProgMode) {
          ShowProgramMode(FRAS);
          // (FRAS) 2 for 1/2, 4 for 1/4, 8 for 1/8, 16 for 1/16
          switch (NumString[0]) {
            case '2':
              FracStepSet = 2;
              SetStepPins(); 
              EpromWrite(FRAS);
              break;
            case '4':
              FracStepSet = 4;
              SetStepPins(); 
              EpromWrite(FRAS);
              break;
            case '8':
              FracStepSet = 8;
              SetStepPins(); 
              EpromWrite(FRAS);
              break;
           case '1': // Assume it's 16
              FracStepSet = 16;
              SetStepPins(); 
              EpromWrite(FRAS);
              break;
            default: 
              FracStepSet = 2;
              SetStepPins(); 
              EpromWrite(FRAS);
              break; 
          }
          delay(1000);
          ShowProgramMode(PROG);
          // go back to start in program mode..
        }
        else {
          if(NewNumber != 0) {
            SpeedIPM = int(NewNumber);
            if(SpeedIPM < 1) { // Set some limits
              SpeedIPM = 1;
            }
            if(SpeedIPM > 20) {
              SpeedIPM = 20;
            }
            SpeedVal = GetDelayinUseconds(SpeedIPM);
            NewNumber = 0;
            NumIndex = 0;
            NumString[NumIndex] = '\0';
          }
          isJoging = false;
          Continuous = true;
          UpdateDisplay();
        }  
        break;
      case 'D': // Jog, Steps And Backlash value in program mode
        isNumber = false;
        if(ProgMode) {
          ShowProgramMode(BLSH);
          BkLash = atof(NumString);
          EpromWrite(BLSH);
          delay(1000);
          ShowProgramMode(PROG);
          // go back to start in program mode..
        }
        else {
          Continuous = false;
          isJoging = true;
          if(NewNumber != 0) {
            SteepsVal = NewNumber;
            NewNumber = 0;
            NumIndex = 0;
            NumString[NumIndex] = '\0';
          }
          UpdateDisplay();
        } 
        break;
    }
    if(isNumber) {
      if(Continuous || isJoging) { // # pressed with active function
        isJoging = false;
        Continuous = false;
        NewNumber = 0;
        NumIndex = 0;
        AddDecPoint = false;
        NumString[NumIndex] = '\0';
        ZeroDisplay(); // Set display to 0's
      }
      if(key == '#') { // Dec point
        NumString[NumIndex++] = '.';
        NumString[NumIndex] = '\0';
        AddDecPoint = true;  
      }
      else {
        if(NumIndex > 3) { // Limit number of digits entered
          if(AddDecPoint && NumIndex > 4) {
            return;  
          }
          else if(AddDecPoint == false) {
            return;
          }
        }
        NumString[NumIndex++] = key;
        NumString[NumIndex] = '\0';
        NewNumber = atof(NumString);
        if(AddDecPoint) {
          matrix1.print(NewNumber, 3);
        }
        else {
           matrix1.print(int(NewNumber), DEC);
        }
        matrix1.writeDisplay();
      }
    }
  }  // If Key
  if(isJoging) {
    if(Running) {
      int steeping = SteepsVal * InchSteps;
      if(LastDirection != RunForward) { // add back lash value to SteepsVal
        steeping = (SteepsVal + BkLash) * InchSteps;
        LastDirection = RunForward; // Reset flag
      }
      while(steeping > 0){
        digitalWrite(ClockPin, HIGH);
        delayMicroseconds(40);
        digitalWrite(ClockPin, LOW);
        delayMicroseconds(SpeedVal);
        steeping--;
        if(Running == false){   // Emergency stop
          break;
        }
      }
    }
    Running = false; 
    alpha4.blinkRate(0);
    alpha4.writeDisplay();
    delay(100); // just to make sure the motor has finished moving
    digitalWrite(DisablePin, LOW); // Disable stepper
  }
  if(Running) {
    digitalWrite(ClockPin, HIGH);
    delayMicroseconds(40);
    digitalWrite(ClockPin, LOW);
    delayMicroseconds(SpeedVal);
  }
}
 
Status
Not open for further replies.
Back
Top