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.
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.
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.
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.