Are interrupts bad for me? Using an encoder to control a stepper

Hello,

I have a feedback system that consists of a 5 V battery pack, a UNO, a ULN2003 drive, a 28BJ-48 stepper and a KY040 rotary encoder. The shaft of the encoder is connected to the one of the motor, so that every revolution of the motor's shaft corresponds to one revolution of the encoder's.
For background I will try to explain first what I achieved so far:

My first bit of code (Program A) has the following operation and works fine:

  1. Initialize current encoder position (y) at 0
  2. Begin serial communication and wait for an integer number as input (r).
  3. Calculate error as e = r - y
  4. Move CKW while e>0, CCKW while e<0, stay idle while e == 0
  5. Using interrupts update y and e (either +1 or -1) every time the encoder moves one step
  6. Iterate from step 4)

My aim is to create a new code (Program B) which allows me to track a non-constant reference signal.

I did this by defining r as an array instead, which for simplicity I first defined as an array made up of only the value 20 (eventually I will have to track a signal of randomly generated values - I am open to suggestions if there is a better way of doing this). I then introduced a for loop to check at every iteration the current value of r, so that practically the previous operation would iterate from step 2). In theory, the motor should rotate 20 encoder steps, as if I inputted 20 in Program A, however what actually occurs is that the motor starts to vibrate but the shaft never fully turns. I believe the issue is the fact that by using interrupts the entire operation is halted when the encoder is between a step (stepper increment is lower than encoders), however I am not sure and cannot think of an alternative anyways. What do you think?

Program 2:

  // ================= Declarations =================
  
// Encoder
const int PinCLK = 2;           // Encoder CLK (White)
const int PinDT = 3;            // Encoder DT (Brown)
int pos_last = 0;                 // Last position
volatile int y = 0;               // Current position

// Motor
int Pin1 = 8;              // Driver pin 1 (Yellow) 
int Pin2 = 9;              // Driver pin 2 (Orange) 
int Pin3 = 10;             // Driver pin 3 (Green) 
int Pin4 = 11;             // Driver pin 4 (Blue)
int v = 1;                   // Motor speed (1 max speed)

int r[24] = {20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20};
boolean ref_bool = false;
int i = 0;
int e = 1;                 // Tracking error

  // ================= Functions =================

void idle (){
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, LOW);
}
  
  void CCKW (){
 // 1
 digitalWrite(Pin1, HIGH);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, LOW);
 delay(v);
 // 2
 digitalWrite(Pin1, HIGH);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, LOW);
 delay (v);
 // 3
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, LOW);
 delay(v);
 // 4
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin4, LOW);
 delay(v);
 // 5
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin4, LOW);
 delay(v);
 // 6
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin4, HIGH);
 delay (v);
 // 7
 digitalWrite(Pin1, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, HIGH);
 delay(v);
 // 8
 digitalWrite(Pin1, HIGH);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin4, HIGH);
 delay(v);
}

void CKW(){
 // 1
 digitalWrite(Pin4, HIGH);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin1, LOW);
 delay(v);
 // 2
 digitalWrite(Pin4, HIGH);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin1, LOW);
 delay (v);
 // 3
 digitalWrite(Pin4, LOW);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin1, LOW);
 delay(v);
 // 4
 digitalWrite(Pin4, LOW);
 digitalWrite(Pin3, HIGH);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin1, LOW);
 delay(v);
 // 5
 digitalWrite(Pin4, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin1, LOW);
 delay(v);
 // 6
 digitalWrite(Pin4, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin2, HIGH);
 digitalWrite(Pin1, HIGH);
 delay (v);
 // 7
 digitalWrite(Pin4, LOW);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin1, HIGH);
 delay(v);
 // 8
 digitalWrite(Pin4, HIGH);
 digitalWrite(Pin3, LOW);
 digitalWrite(Pin2, LOW);
 digitalWrite(Pin1, HIGH);
 delay(v);
}

// Interrupt
void isr ()  {                                                            
  static unsigned long t_interrupt_last = 0;
  unsigned long t_interrupt = millis();

  // If interrupts come faster than 5ms, assume it's a bounce and ignore
  if (t_interrupt - t_interrupt_last > 5) {
    if (digitalRead(PinDT) == LOW)
    {
      y-- ; 
    }
    else {
      y++ ; 
    }
  }
  // Keep track of when we were here last (no more than every 5ms)
  t_interrupt_last = t_interrupt;
}

// Track: If the current rotary switch position has changed then update everything
void track() {
  if (y != pos_last) {

    // Keep track of this new value
    pos_last = y;
  }
}  
  // ================= Setup =================

void setup() {
  Serial.begin(9600);

  // Encoder
  pinMode(PinCLK,INPUT);
  pinMode(PinDT, INPUT);
  // Motor
  pinMode(Pin1, OUTPUT);
  pinMode(Pin2, OUTPUT);
  pinMode(Pin3, OUTPUT);
  pinMode(Pin4, OUTPUT);

  // Attach the routine to service the interrupts
  attachInterrupt(digitalPinToInterrupt(PinCLK), isr, LOW);
}

  // ================= Loop =================

void loop() {
  for(i=1;i<24;i=i+1) { // Iterate through array 
    if(e<0) {
      track();
      CKW();
      e = r[i] - y;
      i=i+1;
    }
    if(e>0) {
      track();
      CCKW();
      e = r[i] - y;
      i=i+1;
    }   
    if(e==0) {
      track();
      idle();
      e = r[i] - y;
      i=i+1;
    }  
  }
}

Please let me know if I should move this thread somewhere else.

"
The power consumption of the motor is around 240mA.
Because the motor draws too much power, it is best to power it directly from an external 5V power supply rather than drawing that power from the Arduino."

Are you following this advice I found on-line?
PAul

Yes I am. I am 100% sure the wiring/hardware is correct, in fact my 'Program A' works fine.

Perhaps the outcome from the serial monitor might be helpful. I printed in this order: y-r--i---e

The result is shown in figure 1. I then manually helped the encoder move one step and this made the system work again for a couple of iteration, until it stopped again, as seen in figure 2.

1.JPG

2.JPG

1.JPG

2.JPG

Track should be using a critical section to read y since its multi-byte and changed by an ISR:

void track() {
  noInterrupts () ;
  pos_last = y ;  // read the volatile in a critical section
  interrupts () ; 
}

Secondly your order is all wrong in loop().

You are checking e, then updating it.

You should updating e just before checking it, and not fiddling with i, the loop
variable, within the loop:

void loop() {
  for(i=1;i<24;i=i+1) { // Iterate through array
    track() ;
    e = r[i] - last_pos;  // last_pos is our copy of the volatile y, so use it
    if(e<0) {
      CKW();
    }
    if(e>0) {
      CCKW();
    }  
  }
}

I don't understand the array r[] - why are you cycling through it every time?

Why are you doing 8 half-steps per call to CKW or CCKW? Surely you only want to
do one (half)step at a time?

Thank you for your answer! It is very helpful.

MarkT:
I don't understand the array r[] - why are you cycling through it every time?

As you have probably seen I am not an expert, so I just didn't deal with that yet as it shouldn't affect the outcome (as long as it cycles through it once I am happy). Could I just insert a break to solve the issue?

MarkT:
Why are you doing 8 half-steps per call to CKW or CCKW? Surely you only want to
do one (half)step at a time?

I found the CKW() and CCKW() functions in this forum and was under the impression that those 8 steps are required for one (half)step. Is that wrong?

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