/*
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);
}
}