Stepper motor control

Hi I need an stepper motor to oscillate between -30deg and +30 degrees and I want it to start by the press of a button and stop at the press of the same button. I have written a code that does that below. In the set up it uses two end switches mounted each side of a arm the is attached to the motor that triggers interrupts when the motor is at -40deg and +40deg. It then center's the motor at 0deg this is seen as some type of calibrating step. After this, the user is supposed to press the start/stop button on which the arduino should start implementing the super loop. Also I want the two end switches at the beginning to be triggered on a falling edge, but when ever I change the the interrupt mode from LOW to FALLING it skips the entire calibration stage. I am using a TB6600 stepper driver to power the 4 wire stepper motor. And all three switches is end switches. Any advice?

const int stepPin = 5; 
const int dirPin = 6; 
const int enPin = 7;
const int pos40 = 2;
const int neg40 = 3;
const int start = 8;
volatile bool set1 = true;
bool startState = 0;

int j = 1;
int k = 1;
int x;
int counter;

const float stepper_angle = 1.8;
const int micro_step = 16;
const int gear_ratio = 30;
float angle_step;
float curr_angle;

void posFourty(){
	curr_angle = 40;
  j = 0;
}
  	
void negFourty(){
	curr_angle = -40;
  set1 = false;
}

ISR(PCINT0_vect){
    if(digitalRead(start) == LOW){
        startState = !startState;
      }
  }


void setup() {
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  pinMode(pos40,INPUT_PULLUP);
  pinMode(neg40,INPUT_PULLUP);
  pinMode(start, INPUT_PULLUP);
  delay(50);
  attachInterrupt(digitalPinToInterrupt(pos40),posFourty,LOW); //Decleration of interrupts
  attachInterrupt(digitalPinToInterrupt(neg40),negFourty,LOW); 

  PCICR |= B00000001;
  PCMSK0 |= B00000001;

  digitalWrite(enPin,HIGH);

  angle_step = (stepper_angle/micro_step)/gear_ratio;
  curr_angle = 0.00;

  Serial.begin(115200);
  Serial.println("\t---    STARTING   ---");
  delay(500);
  digitalWrite(dirPin,HIGH);          // Start moving the motor clockwise
  Serial.println("\t+++  Calebrating  +++");

  while(set1 == true){                //Start of calibration stage
    if(j == 1){
    	digitalWrite(stepPin,LOW);      //This part should run until pos40 button is pressed 
    	delayMicroseconds(50);
    	digitalWrite(stepPin,HIGH);
    	delayMicroseconds(50);
    }else {
    	counter ++;
        digitalWrite(dirPin,LOW);     //This part should run until neg40 is pressed
      	digitalWrite(stepPin,LOW);
      	delayMicroseconds(50);
      	digitalWrite(stepPin,HIGH);
      	delayMicroseconds(50);
    }
  }
  digitalWrite(dirPin,HIGH);
  int n = counter/2;
 
  for(int x = 0; x < n; x++){         
    delayMicroseconds(50);
    digitalWrite(stepPin,HIGH);
    delayMicroseconds(50);
    curr_angle = 0.00;                //Motor is now in at 0deg
  }                                   //End of calibrtion stage

Serial.println("\t---Centre position---");
Serial.println("\t---   Hit start   ---");
delay(1000);
}
void loop() 
{  
Serial.println(startState);

  while(startState){
  	while(k == 1) {
  		Serial.print(startState);
      for(int x = 0; x < 8000; x++){
    		if(startState == 0){
          break;
        }
        Serial.print(x);
    		Serial.print("\t");
    		Serial.println(curr_angle);
        digitalWrite(stepPin,LOW);
    		delayMicroseconds(50);
    		digitalWrite(stepPin,HIGH);
    		delayMicroseconds(50);
        curr_angle += angle_step;
        k = 0;
  }
    }
    Serial.print(startState);
    digitalWrite(dirPin,LOW);
    for(x=0; x < 16000; x++){ 	  //Positve rotation
    	if(startState == 0){
          break;
      }
      Serial.print(x);
    	Serial.print("\t");
    	Serial.println(curr_angle);
      digitalWrite(stepPin,LOW);
    	delayMicroseconds(50);
    	digitalWrite(stepPin,HIGH);
    	delayMicroseconds(50);
      curr_angle -= angle_step;
    }
    Serial.print(startState);
    digitalWrite(dirPin,HIGH);
    for(x=0; x < 16000; x++ ){
    	if(startState == 0){
        break;
      }
      Serial.print(x);
    	Serial.print("\t");
    	Serial.println(curr_angle);
      digitalWrite(stepPin,LOW);
    	delayMicroseconds(50);
    	digitalWrite(stepPin,HIGH);
    	delayMicroseconds(50);
      curr_angle += angle_step;
  }
  }// end of while(startState)
  
}// end of loop



Any variable used in an interrupt service routine should be declared as volatile
You missed a few

Thank you for pointing it out, but it sadly didn't solve the problem.

Which arduino are you using?

An UNO, so I only have those two hardware interrupt pins

There must be noise pick-up on the wires between the UNO and limit switches.
Try connecting a 4.7K resistor between pin 2 and 5V and also between pin 3 and 5V.

Don't use interrupts to read the state of mechanical switches/buttons. You can safely read the switch state in your calibration routine. No interrupt needed. Interruts make things complicated and produce more problems than they solve. Interrupts should only be used if a very fast reaction is needed - and fast means from the processor point of view.

Why do you need 2 limit switches when using a stepper? If you reach the first limit switch you know where the stepper is. All other positions can be computetd from there.

Hi, @unic02

Can we please have a circuit diagram?
An image of a hand drawn schematic will be fine, include ALL power supplies, component names and pin labels.

Can you please post some images of your project so we can see your component layout, especially the mechanics of the stepper and limit switches.

Thanks.. Tom.. :smiley: :+1: :coffee: :australia:
PS, I'm off to bed... 38 mins past midnight. :sleeping: :sleeping: :sleeping: :sleeping: :sleeping: :sleeping:

Mechanical switches bounce, have you thought of that? How are they wired?

Thank you very much I'll give that a try but I need the two switches because the machine is mechanically set up in such a way that I am not a 100% sure at what degrees it will trigger the limit switch I only know the limit switches can be mount exactly the same distance away from 0 degrees.

I had a simillar problem where I would read false readings from the end stop buttons when the motors where running. Using isolated cables solved the problem, you could give it a try

Thanks everyone for your help, all the advice helped a lot I decided to rewrite the cod with all the new knowledge in mind and now everything works perfectly.

Here is the working code if anyone is interested:

const int stepPin = 5; 
const int dirPin = 6; 
const int enPin = 7;
const int pos40 = 2;
const int neg40 = 3;
const int start = 4;
volatile bool set1 = 1;
volatile bool set2 = 1;
volatile bool set3 = 0;
volatile bool set4 = 1;
volatile bool startState = 0;
volatile bool p40 = 1;
int counter = 0;

const float stepper_angle = 1.8;
const int micro_step = 16;
const int gear_ratio = 30;
float angle_step;
volatile float curr_angle;

void setup() {
  pinMode(stepPin,OUTPUT); 
  pinMode(dirPin,OUTPUT);
  pinMode(enPin,OUTPUT);
  pinMode(pos40,INPUT_PULLUP);
  pinMode(neg40,INPUT_PULLUP);
  pinMode(start, INPUT_PULLUP);

  attachInterrupt(digitalPinToInterrupt(pos40),posFourty,FALLING); //Decleration of interrupts
  attachInterrupt(digitalPinToInterrupt(neg40),negFourty,FALLING); 

  angle_step = (stepper_angle/micro_step)/gear_ratio;
  curr_angle = 0.00;

  Serial.begin(115200);
  Serial.println("\t---    STARTING   ---");
  digitalWrite(enPin,HIGH);
  digitalWrite(dirPin,HIGH);          // Start moving the motor clockwise
  Serial.println("\t+++  Calebrating  +++");

  while(set1){
    while(set2){
      pulse();
      }
      digitalWrite(dirPin,LOW);
    while(set3){
      pulse();
      counter++;
    }
  }
  for(int x=0; x<=counter/2; x++){
  digitalWrite(dirPin,HIGH);
  pulse();
  }
  curr_angle = 0.00;
  Serial.println("\t---Centre position---");
  Serial.println("\t---   Hit start   ---"); //End of calibrtion stage
}

void loop() {
  if(digitalRead(start) == LOW){
    startState = !startState;
    delay(200);
  }
  while(startState == 1){
      if(set4 == 1){
        digitalWrite(dirPin,HIGH);
        for(int y = 0; y<8000; y++){
          Serial.print(y);
          Serial.print("\t");
          Serial.println(curr_angle);
          pulse();
          curr_angle += angle_step;
        }
      }
      set4 = 0;
      digitalWrite(dirPin,LOW);
      for(int z = 0; z < 16000; z++){
        Serial.print(z);
        Serial.print("\t");
        Serial.println(curr_angle);
        pulse();
        curr_angle -= angle_step;
        if(digitalRead(start) == LOW){
          startState = 0;
          delay(200);
          break;
        }
      }
      if(startState == 0){
        break;
      }
      digitalWrite(dirPin,HIGH);
      for(int a = 0; a < 16000; a++){
        Serial.print(a);
        Serial.print("\t");
        Serial.println(curr_angle);
        pulse();
        curr_angle += angle_step;
        if(digitalRead(start) == LOW){
          startState = 0;
          delay(200);
          break;
        }
      }
      if(startState == 0){
        break;
      }
  }

}
void pulse() {
  digitalWrite(stepPin,LOW);      //This part should run until pos40 button is pressed 
  delayMicroseconds(50);
  digitalWrite(stepPin,HIGH);
  delayMicroseconds(50);
  return;
}

void posFourty(){
 set2 = 0;
 set3 = 1;
}

void negFourty(){
 set3 = 0;
 set1 = 0;
}

Interrupts are WAY overkill for human input. The 'FALLING' may not work because of button bounce.

Why are you using the Pin Change interrupts as well as the External interrupts?

To make the code clearer you should probably use the bit named

  PCICR |= _BV(PCIE0));
  PCMSK0 |= _BV(PCINT0);  // PB0 (Arduino Pin 8)

One of the reasons why you use "normally closed" end switches.
The other reason is safety. Protection against wire/circuit failure.

(potentially problematic) interrupts are for very fast changing signals, which a limit switch is not.
Leo..

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.