Can't reach nema17 max speed with AS5047P encoder

Hi! I stumbled upon a problem. I made bare metal test with bipolar nema17 stepper + a4988 driver. It reaches max speed at 43micros between on/off step signals without making a skip at 1/16 microstepping. But whenever I introduce "as5047p.readAngleDegree();" to read angle from encoder, some strange things happens. Lowering step on/off interval down to 106micros works as intended and arduino reports full 360degree rotation took ~1330ms. But if I lower interval value to 105micros, stepper start spinning twice as fast with full rotation report of ~700ms. Lowering interval below 105micros has no effect on speed. I tried to use different library, had same issue at 105micros but stepper was 3 times slower. Tried code without LCD and keypad - no change. Tried to read angle at step intervals - no change. Rewritten code in different and cleaner way - nothing.
Spent 2 days on this issue and nothing ;/

//***ANALOG MULTI BUTTON***//
////////////////////////////
#include <AnalogMultiButton.h>

// define the pin you want to use
const int BUTTONS_PIN = A0;

// set how many buttons you have connected
const int BUTTONS_TOTAL = 5;

// find out what the value of analogRead is when you press each of your buttons and put them in this array
// you can find this out by putting Serial.println(analogRead(BUTTONS_PIN)); in your loop() and opening the serial monitor to see the values
// make sure they are in order of smallest to largest
const int BUTTONS_VALUES[BUTTONS_TOTAL] = {0, 132, 305, 479, 720};

// you can also define constants for each of your buttons, which makes your code easier to read
// define these in the same order as the numbers in your BUTTONS_VALUES array, so whichever button has the smallest analogRead() number should come first
const int BUTTON_RIGHT = 0;
const int BUTTON_UP = 1;
const int BUTTON_DOWN = 2;
const int BUTTON_LEFT = 3;
const int BUTTON_SELECT = 4;

// make an AnalogMultiButton object, pass in the pin, total and values array
AnalogMultiButton buttons(BUTTONS_PIN, BUTTONS_TOTAL, BUTTONS_VALUES);

// define buttons update state machine parameters
unsigned long lastButtonUpdate = 0;
const long ButtonUpdateInterval = 10;
unsigned long lastTimeWasPressed = 0;

//***LIQUID CRYSTAL***//
///////////////////////
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
unsigned char brightness = 150;
const int backlightPin = 10;

//***AS5047P ENCODER***//
////////////////////////
#include <AS5047P.h>

// define the chip select port.
#define AS5047P_CHIP_SELECT_PORT 3

// define the spi bus speed
#define AS5047P_CUSTOM_SPI_BUS_SPEED 10000000

// initialize a new AS5047P sensor object.
AS5047P as5047p(AS5047P_CHIP_SELECT_PORT, AS5047P_CUSTOM_SPI_BUS_SPEED);

int angle;

//***STEPPER***//
////////////////
const int EnableCutterPin = 15;
const int CutterDirPin = 16;
const int CutterStepPin = 17;
int EnableCutterState = HIGH; // Cutter Disabled
int CutterDirState = HIGH; // Cutter spin clockwise
int CutterStepState = LOW; // Cutter step off
unsigned long CutterStepMicros = 0;
unsigned long stepInterval = 43;

//***GLOBAL VARIABLES***//
/////////////////////////
bool left = false;
unsigned long currentMicros;
unsigned long currentMillis;
unsigned long timePeriod = 0;

//***SETUP***//
//////////////
void setup() {
  pinMode(EnableCutterPin, OUTPUT); //Enable Cutter
  pinMode(CutterDirPin, OUTPUT); //Cutter Dir
  pinMode(CutterStepPin, OUTPUT); //Cutter Step
  pinMode(backlightPin, OUTPUT);
  lcd.begin(16, 2);
  analogWrite(backlightPin, brightness);

  // initialize the AS5047P sensor and hold if sensor can't be initialized..
  while (!as5047p.initSPI()) {
    lcd.print(F("No AS5047P"));
    delay(5000);
  }
}

//***MAIN PROGRAM***//
/////////////////////
void loop() {
  currentMicros = micros();
  currentMillis = millis();
  angle = as5047p.readAngleDegree();
  setInputFlags();
  leftStepper();
}

//***SET INPUT FLAGS***//
////////////////////////
void setInputFlags() {
  if (currentMillis - lastButtonUpdate >= ButtonUpdateInterval) {
    buttons.update();
    inputAction();
    lastButtonUpdate = currentMillis;
  }
}

//***INPUT ACTIONS***//
////////////////////////
void inputAction() {
  if (buttons.onPress(BUTTON_UP)) {
    stepInterval++;
    lcd.clear();
    lcd.print("Step interval");
    lcd.setCursor(0, 1);
    lcd.print(stepInterval);
  }
  if (buttons.onPress(BUTTON_DOWN)) {
    stepInterval--;
    lcd.clear();
    lcd.print("Step interval");
    lcd.setCursor(0, 1);
    lcd.print(stepInterval);
  }
  if (buttons.onPress(BUTTON_LEFT)) {
    left = true;
    lastTimeWasPressed = currentMillis;
  }
}

//***RUN STEPPER***//
////////////////////
void runStepper() {
  if (currentMicros - CutterStepMicros >= stepInterval) {
    CutterStepMicros = currentMicros;
    if (CutterStepState == LOW) {
      CutterStepState = HIGH;
    } else {
      CutterStepState = LOW;
    }
    digitalWrite(CutterStepPin, CutterStepState);
  }
}

//***LEFT STEPPER***//
///////////////////////
void leftStepper() {
  if (left == true) {
    if (angle <= 358) {
      digitalWrite(EnableCutterPin, LOW); // A4988 Enable pin enabled
      digitalWrite(CutterDirPin, HIGH); // Cutter spin clockwise
      runStepper();
    }
    else {
      left = false;
      digitalWrite(EnableCutterPin, HIGH); // A4988 Enable pin disabled
      currentMillis = millis();
      timePeriod = (currentMillis - lastTimeWasPressed);
      lcd.clear();
      lcd.setCursor(0, 1);
      lcd.print(timePeriod);
    }
  }
}

You could user Serial monitor and Serial.print, in the code, to tell what is going on inside the code.
Just take care that those prints does not fill the serial channel and causes delays.

Railroader:
You could user Serial monitor and Serial.print, in the code, to tell what is going on inside the code.
Just take care that those prints does not fill the serial channel and causes delays.

Completely removed LCD, so only thing using SPI is AS5047P. Issue persists.

//***ANALOG MULTI BUTTON***//
////////////////////////////
#include <AnalogMultiButton.h>

// define the pin you want to use
const int BUTTONS_PIN = A0;

// set how many buttons you have connected
const int BUTTONS_TOTAL = 5;

// find out what the value of analogRead is when you press each of your buttons and put them in this array
// you can find this out by putting Serial.println(analogRead(BUTTONS_PIN)); in your loop() and opening the serial monitor to see the values
// make sure they are in order of smallest to largest
const int BUTTONS_VALUES[BUTTONS_TOTAL] = {0, 132, 305, 479, 720};

// you can also define constants for each of your buttons, which makes your code easier to read
// define these in the same order as the numbers in your BUTTONS_VALUES array, so whichever button has the smallest analogRead() number should come first
const int BUTTON_RIGHT = 0;
const int BUTTON_UP = 1;
const int BUTTON_DOWN = 2;
const int BUTTON_LEFT = 3;
const int BUTTON_SELECT = 4;

// make an AnalogMultiButton object, pass in the pin, total and values array
AnalogMultiButton buttons(BUTTONS_PIN, BUTTONS_TOTAL, BUTTONS_VALUES);

// define buttons update state machine parameters
unsigned long lastButtonUpdate = 0;
const long ButtonUpdateInterval = 10;

//***AS5047P ENCODER***//
////////////////////////
#include <AS5047P.h>

// define the chip select port.
#define AS5047P_CHIP_SELECT_PORT 3

// define the spi bus speed
#define AS5047P_CUSTOM_SPI_BUS_SPEED 10000000

// initialize a new AS5047P sensor object.
AS5047P as5047p(AS5047P_CHIP_SELECT_PORT, AS5047P_CUSTOM_SPI_BUS_SPEED);

int angle;

//***STEPPER***//
////////////////
const int EnableCutterPin = 15;
const int CutterDirPin = 16;
const int CutterStepPin = 17;
int EnableCutterState = HIGH; // Cutter Disabled
int CutterDirState = HIGH; // Cutter spin clockwise
int CutterStepState = LOW; // Cutter step off
unsigned long CutterStepMicros = 0;
unsigned long stepInterval = 50;

//***GLOBAL VARIABLES***//
/////////////////////////
bool left = false;
unsigned long currentMicros;
unsigned long currentMillis;

//***SETUP***//
//////////////
void setup() {
  pinMode(EnableCutterPin, OUTPUT); //Enable Cutter
  pinMode(CutterDirPin, OUTPUT); //Cutter Dir
  pinMode(CutterStepPin, OUTPUT); //Cutter Step

  // initialize the AS5047P sensor and hold if sensor can't be initialized..
  while (!as5047p.initSPI()) {
    delay(5000);
  }
}

//***MAIN PROGRAM***//
/////////////////////
void loop() {
  currentMicros = micros();
  currentMillis = millis();
  angle = as5047p.readAngleDegree();
  setInputFlags();
  leftStepper();
}

//***SET INPUT FLAGS***//
////////////////////////
void setInputFlags() {
  if (currentMillis - lastButtonUpdate >= ButtonUpdateInterval) {
    buttons.update();
    inputAction();
    lastButtonUpdate = currentMillis;
  }
}

//***INPUT ACTIONS***//
////////////////////////
void inputAction() {
  if (buttons.onPress(BUTTON_UP)) {
    stepInterval = stepInterval + 10;
  }
  if (buttons.onPress(BUTTON_DOWN)) {
    stepInterval = stepInterval - 10;
  }
  if (buttons.onPress(BUTTON_LEFT)) {
    left = true;
  }
}

//***RUN STEPPER***//
////////////////////
void runStepper() {
  if (currentMicros - CutterStepMicros >= stepInterval) {
    CutterStepMicros = currentMicros;
    if (CutterStepState == LOW) {
      CutterStepState = HIGH;
    } else {
      CutterStepState = LOW;
    }
    digitalWrite(CutterStepPin, CutterStepState);
  }
}

//***LEFT STEPPER***//
///////////////////////
void leftStepper() {
  if (left == true) {
    if (angle <= 358) {
      digitalWrite(EnableCutterPin, LOW); // A4988 Enable pin enabled
      digitalWrite(CutterDirPin, HIGH); // Cutter spin clockwise
      runStepper();
    }
    else {
      left = false;
      digitalWrite(EnableCutterPin, HIGH); // A4988 Enable pin disabled
    }
  }
}

A nice frizzy picture. I was looking for the power supplies and the associated connections, they appear to be missing. A schematic would be much better. Post a link to the motor, Nema 17 only tells me some of its mechanical properties nothing electrical. Also links to the other devices and the particular Arduino you are using.

I would suggest setting up your Serial monitor at 115200 and inserting some short print statements to see when you are doing the stepping, etc.

gilshultz:
A nice frizzy picture. I was looking for the power supplies and the associated connections, they appear to be missing. A schematic would be much better. Post a link to the motor, Nema 17 only tells me some of its mechanical properties nothing electrical. Also links to the other devices and the particular Arduino you are using.

Sorry, I'm not good at schematics or rules to do them so professionals could read schematic. Power supplies are little triangles with 5V or 12V labels next to them. Wires that crosses each other are colored in different colors. Arduino itself is powered via USB port connected to PC. A4988 stepper driver is powered with lab power supply, voltage is set to 12V (can be increased up to 30V if needed). Every device shares common ground.
Stepper Buy Stepper Motor JK42HS40-0504 200 Botland - Robotic Shop
Arduino UNO R3 alternative, but works out of the box, no drivers needed Uno R3
Encoder AS5047P-TS_EK_AB ams-OSRAM USA INC. | Development Boards, Kits, Programmers | DigiKey
Stepper driver https://www.duino.lt/stepper/13607-arduino-zingsniniu-varikliu-valdiklis-stepstick-a4988.html
LCD Keypad Shield 16x2 LCD ekranas su mygtukais

blh64:
I would suggest setting up your Serial monitor at 115200 and inserting some short print statements to see when you are doing the stepping, etc.

To print micros when it is making step?

Replacing "as5047p.readAngleDegree();" with "as5047p.readAngleRaw();" sped up things a bit since it doesn't use float. But anomalies still occur. Now stepper runs faster, at 53us interval it starts to stutter. With 58us interval it runs smooth but arduino reports full 360degree rotation takes 600ms. By doing some math from given result I can see that interval is ~93us. It doesn't match with set interval of 58us.

Perhaps acceleration is too high? Start with longer pulses, then decrease the pulse width.

#define AS5047P_CUSTOM_SPI_BUS_SPEED 10000000

The SPI port on the Arduino UNO R3 won't work at 10MHz, you'll need to slow it down to 4MHz (default).

Stipa:
Completely removed LCD, so only thing using SPI is AS5047P. Issue persists.

//***RUN STEPPER***//

////////////////////
void runStepper() {
 if (currentMicros - CutterStepMicros >= stepInterval) {
   CutterStepMicros = currentMicros;
   if (CutterStepState == LOW) {
     CutterStepState = HIGH;
   } else {
     CutterStepState = LOW;
   }
   digitalWrite(CutterStepPin, CutterStepState);
 }
}

Considering you're talking about step intervals of 100-odd µs, this may be part of the problem. It is the kind of speeds that I'd consider using a timer interrupt for.
For better timing accuracy you may also change line 5 in that snippet to:

CutterStepMicros += stepInterval;

In loop() you have this call to an external library:

angle = as5047p.readAngleDegree();

This may take too long a time time to complete, especially if there are floating point calculations involved in that function, and as a result mess up your stepper timing. This is what a timer interrupt can prevent: it'd allow you to make a step in the middle of such a calculation.
In general I prefer to not use a currentMillis and currentMicros variable, instead calling micros() and millis() whenever you need the value.

wvmarle:
Considering you're talking about step intervals of 100-odd µs, this may be part of the problem. It is the kind of speeds that I'd consider using a timer interrupt for.
For better timing accuracy you may also change line 5 in that snippet to:

CutterStepMicros += stepInterval;

In loop() you have this call to an external library:

angle = as5047p.readAngleDegree();

This may take too long a time time to complete, especially if there are floating point calculations involved in that function, and as a result mess up your stepper timing. This is what a timer interrupt can prevent: it'd allow you to make a step in the middle of such a calculation.
In general I prefer to not use a currentMillis and currentMicros variable, instead calling micros() and millis() whenever you need the value.

Thanks for tips & tricks! But before getting my hands into timers and interrupts I would like to know if it is possible to change timer intervals outside "void setup()"? If not then, I think, I will take two arduinos, one for 3 steppers and second for 2 encoders. And try to communicate them somehow, send settings from one to another.

Stipa:
Thanks for tips & tricks! But before getting my hands into timers and interrupts I would like to know if it is possible to change timer intervals outside "void setup()"? If not then, I think, I will take two arduinos, one for 3 steppers and second for 2 encoders. And try to communicate them somehow, send settings from one to another.

What timer interval are you referring to? There are none in setup(). It is a far better idea to learn interrupts for this task and keep it all on one arduino than try to introduce a second one and take on the inter-arduino communication issue.

blh64:
What timer interval are you referring to? There are none in setup(). It is a far better idea to learn interrupts for this task and keep it all on one arduino than try to introduce a second one and take on the inter-arduino communication issue.

I have 0 knowledge of timers and interrupts. What I would need is basically interrupt every 50us and after a button press increase it by 5us.

Maybe start with TimerOne - Arduino Reference since it handles all the interrupt stuff

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