Keeping accurate timing of steps?

I’m trying to write my own stepper library to better understand and customize my code. I’m pretty new to this kind of programming and have some questions. The code below is only test code.

I’m using a timer interrupt and have the steps counted and reported every second but the steps reported don’t always match the number of steps that should be called. I’d like to have some interfaces that utilize inputs of steps over real time and think the error is too large to be overlooked.

I’m aware of Bresenham, is this something that would fix what I’m seeing? There is a reference here: https://forum.arduino.cc/index.php?topic=351925.0 but they were not using an interrupt.

Please take a look and point out anything I should fix, add, or streamline. Thanks.

#include <TimerOne.h>

#define E1_STEP_PIN        36
#define E1_DIR_PIN         34
#define E1_ENABLE_PIN      30

#define E0_STEP_PIN        26
#define E0_DIR_PIN         28
#define E0_ENABLE_PIN      24

#define X_MIN_PIN           3
#define X_MAX_PIN           2

#define Y_MIN_PIN          14
#define Y_MAX_PIN          15

#define Z_MIN_PIN          18
#define Z_MAX_PIN          19

#define LCD_BEEP           37
#define LED                13

int stepsPerSecond = 1000;
unsigned long previousMillis_count = 0;

int count = 0;

void setup() {
  Timer1.initialize(100);                    //Priority Loop (Steppers)
  Timer1.attachInterrupt(ISR_stepperManager);

  Serial.begin(9600);
  Serial.println("Serial");

  pinMode(E1_STEP_PIN, OUTPUT);   //Slide
  pinMode(E1_DIR_PIN, OUTPUT);
  pinMode(E1_ENABLE_PIN, OUTPUT);

  pinMode(Z_MIN_PIN, INPUT);
  pinMode(Z_MAX_PIN, INPUT);
  digitalWrite(E1_DIR_PIN, LOW);
  digitalWrite(E1_ENABLE_PIN, LOW);

}

int mode = 0;
void loop() {

/**********************************
 * mode:# //stepping mode 2, 4, other = 1
 * speed:### //steps per second
 * s //what is the set speed
 */
  String incomingString = "";
  if (Serial.available() > 0)
  {
    incomingString = Serial.readString();
    Serial.println(incomingString);
    if (incomingString.startsWith("speed")) {
      stepsPerSecond = (incomingString.substring(incomingString.indexOf(":") + 1).toInt());
    } else if (incomingString == "s") {
      Serial.println(stepsPerSecond);
    } else if (incomingString.startsWith("mode")) {
      mode = (incomingString.substring(incomingString.indexOf(":") + 1).toInt());
    }
  }

  unsigned long currentMillis = millis();   //Capture time
  if ((currentMillis - previousMillis_count) > 1000) {
    Serial.println(count);
    count = 0;
    previousMillis_count = currentMillis;
  }

}

unsigned long previousMicros = 0;
void ISR_stepperManager() {
  unsigned long currentMicros = micros();   //Capture time
  if ((currentMicros - previousMicros) >= (1000000 / stepsPerSecond )) {
    if (mode == 4) {          //Quad step mode
      for (int i = 1; i <= 4; i++) {
        takeStep(E1_STEP_PIN);
      }
    } else if (mode == 2) {   //Double step mode
      for (int i = 1; i <= 2; i++) {
        takeStep(E1_STEP_PIN);
      }
    } else {                  //Else single step
      takeStep(E1_STEP_PIN);
    }
    previousMicros = currentMicros;
  }
}

void takeStep(int STEPPER) {
  digitalWrite(STEPPER, HIGH);
  count++;
  digitalWrite(STEPPER, LOW);
}

Please take a look and point out anything I should fix, add, or streamline.

How about delete? Like all the commented out code!

Like the use of Strings where there are not needed.

count is used in the timer interrupt handler and in loop(), but it is not declared volatile. It MUST be.

count is a multi-byte variable. There is currently nothing to prevent the interrupt from firing, and incrementing count, while you are in the processing of resetting it. There should be.

For interrupt, have a look at this series: http://hackaday.com/2015/09/18/embed-with-elliot-interrupts-the-good/

PaulS:
Like the use of Strings where there are not needed.

Can you give me an example? I'm pulling the whole string from the buffer instead of char by char if that's what you are referring to? Alternatives?

PaulS:
count is used in the timer interrupt handler and in loop(), but it is not declared volatile. It MUST be.

Thanks, that fixed the counting problem at some speeds but the stepper seems to be locked up at other speeds and does not change which has nothing to do with the count variable. For example in mode 1, steps per second of over 1000 seem to get locked to certain intervals like 1500 and 1666. Is this due to the interrupt frequency?

Nevermind, I'll answer it: yes it is the interrupt.

1600 steps/s
1000000/1600 = 625us per step
interval of 100us rounds up 625 to 700us per step

1000000/700 = 1428 steps/s which is what I'm getting -___-

So.... how do I avoid this? I need to be able to receive serial at a fairly high rate and start having issues if I make the interval any faster. I'm going to look over the site septillion posted.

I'd rather use the interrupts in C code but am having a hard time wrapping my head around it.

So Marlin uses:

uint16_t timer = (uint16_t)((uint32_t)2000000 / (uint32_t)stepsPerSecond);
OCR1A = timer;

According to Marlin Firmware: Stepper Interrupt Timing – The Smell of Molten Projects in the Morning
And my own research into the Marlin firmware stepper.cpp and stepper.h

Does anyone have a simple tut on using interrupts in C? Most of the ones I've seen go over my head.

I've tried using the Timer1.setPeriod(microseconds); and it works fine. However, how would I set the speed for different steppers in the same interrupt? Now it's getting complicated.

I read this before but found the answer here:

How many steps per second do you need for each motor? I control 3 motors for a small lathe without using interrupts (just micros() ) and I also take data from my PC for every move (sequence of steps).

...R

So.... how do I avoid this? I need to be able to receive serial at a fairly high rate

Well, stuffing your head in the sand, while Serial.readString() farts around waiting for data is NOT the answer.

Read EACH character as it becomes available. Store the character in a NULL terminated char array. When the end-of-command marker arrives, parse the data.

Tested out the stepping without serial, lcd, etc. and it seems to be ok with my new stepping code.

Going to use this string for communication based lightly on G-code so I can save code to a file in the future for programmed moves.

L00 is a speed only input for live control
L## will be for toggle and setting commands

L00 S7500 P4000 T4000

I'm using an array now for stepper speeds based on S,P,T

Current step rate is now

int stepRate[] = {DEFAULT_SPEED_S, DEFAULT_SPEED_P, DEFAULT_SPEED_T};

I'm trying to write code to parse the chars as they enter to

int newStepRate[] = {0, 0, 0};  //{s, p, t}

The logic is proving a little difficult for me. Should I while() for each set or try to do it char/cycle

If you've seen a simple example of this somewhere, I would be happy to see it. Marlin's is a bit too complex for me to analyze.

My threads are overlapping, moving the discussion to the project page: