luni64 / TeensyStep

Fast Stepper Motor Library for Teensy boards
https://luni64.github.io/TeensyStep/
MIT License
274 stars 56 forks source link

Missing steps while alternating between abs -400 and abs 400 #106

Closed BennoG closed 3 years ago

BennoG commented 3 years ago

I want to make a project where mirrors and a laser are controlled by stepper motors to draw simple images, I picked a sample from this page. When I looked at the scope images of the current in the winding (1/16 micro stepping). I noticed there was a slight shift in the position of the stand still.


#include "TeensyStep.h"

constexpr size_t NBSTEPPERS       = 2;           // no need for #defines in c++
const uint8_t DIRS[NBSTEPPERS]    = {22, 14};
const uint8_t STEPS[NBSTEPPERS]   = {21, 23};
Stepper stepperMotors[NBSTEPPERS] = {Stepper(STEPS[0], DIRS[0]), Stepper(STEPS[1], DIRS[1])};
StepControl controller;

void initSteppers()
{
    // c++ alternative
    for(Stepper& stepper : stepperMotors)
    {
        stepper
            .setMaxSpeed(40000)
            .setAcceleration(2000000);
    }
}

void doCheck()
{
    static int loopCount = 0;
    static int nextPos = 400;
    static bool oldRunning  = false;
    static uint32_t oldMS   = 0;
    static int32_t oldPos   = INT32_MAX;
    static int32_t oldSpeed = 0;
    bool running            = controller.isRunning();
    int32_t pos             = stepperMotors[0].getPosition();
    int32_t speed           = controller.getCurrentSpeed();

    if ((loopCount++ % 50) == 0)
    {
      if (oldRunning != running || oldPos != pos || oldSpeed != speed)
      {
          uint32_t diff = millis() - oldMS;
          Serial.printf("Pos:%6d, Speed:%6d state: %s after %d ms\n", pos, speed, running ? "running" : "stopped",diff);
          oldPos     = pos;
          oldRunning = running;
          oldSpeed   = speed;
          oldMS += diff;
      }
    }
    if (!running)
    {
      stepperMotors[0].setTargetAbs(nextPos);
      controller.moveAsync(stepperMotors[0]);
      nextPos = -nextPos;
    }
}

IntervalTimer t;

void setup()
{
    //while (!Serial) {}          // need to wait until serial is ready, otherwise you'll miss all Serial output for about 600ms
    initSteppers();

    t.begin(doCheck, 2'000);  // check in the background, every 100ms instead of polling in a tight loop
    Serial.println("start ---------------------------------");
 }

void loop()
{
}

Counting the edges of the scope revealed there was sometimes 799 edges and not always 100 It looks like 1 out of 10 moves has 1 step too short. In the above code https://www.ans-net.nl/picotech/missing-steps-on-alternate+-400.psdata Only useful if you have picoscope software installed

BennoG commented 3 years ago

Side note on a teensy 3.2 the step rate comes not above 19.550 steps / sec

BennoG commented 3 years ago

image

Looks like clearStepPin() is not called in the middle of the movement, at the start of deceleration ?

And always 285 steps before the stand still.

luni64 commented 3 years ago

I'll have a look at this later this week.

luni64 commented 3 years ago

Meanwhile I tested your code and can reproduce the issue.
I had a closer look at it and found that the reason for it is quite clear: Your doCheck() interrupt service routine calls printf() with a lot of parameters which takes quite some time. If doCheck() happens to be invoked about the same time the step pin is set high the clearStep() function can not be called since doCheck prevents further interrupt calls while it runs.

To demonstrate the problem I set pin12 high at the beginning of doCheck and back to low on exit. Here a LA trace showing the situation:

image

doCheck takes some 80µs to finish and blocks the resetting of the step pin during this time and you effectively loose one step. If you comment out printf() the ISR only takes about 1µs and everything works:

image

Side note on a teensy 3.2 the step rate comes not above 19.550 steps / sec

That's clear. The max acceleration the library can handle is 500'000 stp/s^2 and your 2'000'000 setting is capped to this value.

The usual kinematic equations give: v^2 = 2 as (where v = speed (step/s), a = acceleration (step/s^2) and s = distance (steps))

Since you need to decelerate to zero after the acceleration you only have 400 steps for the acceleration and get:

v = sqrt(2 500'000 steps/s^2 400 steps) = 20'000 steps/s. I.e. at an acceleration of 500k stp/s^2 the motor ends up at a speed of 20kHz after 400 steps.

To reach the max speed you need an acceleration distance of at least:

s = v^2 / 2a = 40'000^2 / (2*500'000) = 1600 steps

Of course the calculations assume continuous acceleration and the library uses discrete acceleration steps. So, the results should be regarded as estimation only. Here a measurement with nextPos = 1600:

image

Hope that helps

BennoG commented 3 years ago

I picked a sample you wrote in another answer, but will move the printf out of the interrupt. I want the high acceleration because there is only a mirror on the motor and nothing else. Or move to a galvo motor in stead of a stepper motor. I will experiment some more thanks for the clarification.