Controlling the current speed of the stepper motor

We are currently working on controlling stepper motors in Arduino by sending the speed of rotation of the stepper motor from a program written in Delphi, through the COM port to Arduino.
So far, it is not possible to get the number in the sketch from Optel3d via COM. Can't help? Some kind of blockage.

I have three sketches:

When this sketch is running, when sending a byte from the program, the arduino catches it, but does not distinguish its value - the stepper motor rotates at a constant speed.

When this sketch runs, a byte is sent from the program - but the sketch doesn't work. I mean, nothing moves.

The number is sent from the COM port monitor - everything works, the engine spins as it should.

Our Delphi program has a special button that, when pressed, calls a procedure with arguments SendToCOMPort(ComPort, Speed), where Speed is the rotation speed that is set in a special field on our form, and when the button is released, SendToCOMPort(ComPort, 0) is called ).

First of all, you must ensure that both ends uses the same baudrate and instead of sending strings, try to send bytes..

//Delphi
Code: byte = 5;
ComPort.Write(Code, 1); //Send one byte

//Arduino
if (Serial.available() > 0)
{
  uint8_t code = Serial.read(); //Receive one byte
  //Do something with code
}

Another thing is that the line stepper.begin(RPM, MICROSTEPS); should only be called once in void setup(). To change the RPM, you should call stepper.setRPM(n), increase RPM with 20: stepper.setRPM(stepper.getRPM() + 20) and decrease is just changing "+" to "-".

In general, the whole point turned out to be that it was not a number that was transmitted from Delphi to the COM port, but a character that needed to be read in Arduino as a character.

of what?

I'm currently working with the NonBlocking.ino sketch. Our task is to start the rotation of the motor from Delphi and during its rotation to control its speed also from Delphi. From the StepperDriver library I tried to download the NonBlocking.ino sketch for testing - the stepper motor does not spin. What's wrong?


/*
 * Example using non-blocking mode to move until a switch is triggered.
 *
 * Copyright (C)2015-2017 Laurentiu Badea
 *
 * This file may be redistributed under the terms of the MIT license.
 * A copy of this license has been included with this distribution in the file LICENSE.
 */
#include <Arduino.h>

// this pin should connect to Ground when want to stop the motor
#define STOPPER_PIN 4

// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 200
#define RPM 120
// Microstepping mode. If you hardwired it to save pins, set to the same value here.
#define MICROSTEPS 16

#define DIR 8
#define STEP 9
#define SLEEP 13 // optional (just delete SLEEP from everywhere if not used)

/*
 * Choose one of the sections below that match your board
 */

#include "DRV8834.h"
#define M0 10
#define M1 11
DRV8834 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, M0, M1);

// #include "A4988.h"
// #define MS1 10
// #define MS2 11
// #define MS3 12
// A4988 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MS1, MS2, MS3);

// #include "DRV8825.h"
// #define MODE0 10
// #define MODE1 11
// #define MODE2 12
// DRV8825 stepper(MOTOR_STEPS, DIR, STEP, SLEEP, MODE0, MODE1, MODE2);

// #include "DRV8880.h"
// #define M0 10
// #define M1 11
// #define TRQ0 6
// #define TRQ1 7
// DRV8880 stepper(MOTORS_STEPS, DIR, STEP, SLEEP, M0, M1, TRQ0, TRQ1);

// #include "BasicStepperDriver.h" // generic
// BasicStepperDriver stepper(DIR, STEP);

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

    // Configure stopper pin to read HIGH unless grounded
    pinMode(STOPPER_PIN, INPUT_PULLUP);

    stepper.begin(RPM, MICROSTEPS);
    // if using enable/disable on ENABLE pin (active LOW) instead of SLEEP uncomment next line
    // stepper.setEnableActiveState(LOW);
    stepper.enable();

    // set current level (for DRV8880 only). Valid percent values are 25, 50, 75 or 100.
    // stepper.setCurrent(100);

    Serial.println("START");

    // set the motor to move continuously for a reasonable time to hit the stopper
    // let's say 100 complete revolutions (arbitrary number)
    stepper.startMove(100 * MOTOR_STEPS * MICROSTEPS);     // in microsteps
    // stepper.startRotate(100 * 360);                     // or in degrees
}

void loop() {
    // first, check if stopper was hit
    if (digitalRead(STOPPER_PIN) == LOW){
        Serial.println("STOPPER REACHED");

        /*
         * Choosing stop() vs startBrake():
         *
         * constant speed mode, they are the same (stop immediately)
         * linear (accelerated) mode with brake, the motor will go past the stopper a bit
         */

        // stepper.stop(); // Закомментировал временно
        // stepper.startBrake();
    }

    // motor control loop - send pulse and return how long to wait until next pulse
    unsigned wait_time_micros = stepper.nextAction();

    // 0 wait time indicates the motor has stopped
    if (wait_time_micros <= 0) {
        stepper.disable();       // comment out to keep motor powered
        delay(3600000);
    }

    // (optional) execute other code if we have enough time
    if (wait_time_micros > 100){
        // other code here
    }
}

This code does not work - SD does not spin. What's wrong?

#define LED_BUILTIN D11
#define LED_RED     D12
#define adc_pin0    A0
bool LOW = false;
bool LED = LOW;

#include <Arduino.h>
#include "BasicStepperDriver.h"

// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 3600
byte RPM = 60;
byte Angle = 10;

// Since microstepping is set externally, make sure this matches the selected mode
// If it doesn't, the motor will move at a different RPM than chosen
// 1=full step, 2=half step etc.
#define MICROSTEPS 1

// All the wires needed for full functionality
#define DIR 6
#define STEP 5
BasicStepperDriver stepper(MOTOR_STEPS, DIR, STEP);

uint8_t incomingByte = 0;

// the setup function runs once when you press reset or power the board
void setup() {
  Serial.begin(250000);
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_RED, OUTPUT); 
  stepper.begin(RPM, MICROSTEPS);
}

// the loop function runs over and over again forever
void loop() {
  if (Serial.available() < 1) return;  
  incomingByte = Serial.read(); 
  switch( incomingByte )
  { 
    case '0': RPM = 0; break;
    case '1': RPM = 10; break;
    case '2': RPM = 20; break;
    case '3': RPM = 30; break;
    case '4': RPM = 40; break;
    case '5': RPM = 50; break;
    case '6': RPM = 60; break;
    case '7': RPM = 70; break;
    case '8': RPM = 80; break;
    case '9': RPM = 90; break;
    default: break;
  }
//  RPM = incomingByte;
//  stepper.begin(RPM, MICROSTEPS);
//  stepper.rotate( 100 );
  stepper.setRPM(RPM);
  //delay(1);
}

I can see you have forever blocked yourself from using serial.Print() to help in debugging your code. Why did you do this and not use an Arduino with more than one serial port?

You never tell the stepper to move, no calls to stepper.move() or stepper.rotate(). Calling stepper.setRPM() changes the speed of the stepper but does not move it.

How can I make it so that at the beginning the stepper is launched, this stepper works in parallel with the program, and then, using the COM port, the rotation speed of the stepper motor is set and changed?

change over to use a different steppermotor-library.

The steppermotor-library MobaTools written by user @MicroBahner
is creating the step-pulses in the background. This means you can execute code in parallel to the step-pulse creation.

The library has even more functions for

  • driving RC-servos
  • fading LEDs
  • debouncing buttons
  • non-blocking timing

If you have installed the MobaTools library with the library-manager there is subfolder with demo-codes with multiple examples

Take a look into the examples. If you have questions how to adapt the demo-code to your needs
ask in the forum

EDIT:

what is critical for any steppermotor-library is using such a high baudrate of 250000 baud

Serial.begin(250000);

Do you really need such a high baudrate because you are changing the steppermotor-speed every 0,01 seconds??

Which microcontroller-board are you using?

best regards Stefan

the complete code is well organised in functions where each function does a senseful SUB-unit of things:

  • using the MobaTools-library for creating the step-pulses

  • check serial inputbuffer if serial data has arrived

  • if serial data has arrived process data to set a new stepper-motorspeed

  • check a momentary pushbutton to switch a variable between "true" and "false" which makes the steppermotor move / stop

All this is done in a non-blocking way.
This means the whole code is all the time responsive to

  • check if new data has arrived
  • check if the start / stop-button has been pressed
  • creating the step-pulses based on a timer--interrupt "in the background"
    (this is done by the MobaTools-library automatically)

writing non-blocking code

This non-blocking is achieved through

let do function loop() all looping

all other functions work in a quickly jump in / quickly jump out manner
If really all functions can rely on "the code will call me in about 100 microseconds again and again and again"
These functions do not need any inner while-loop or for-loop
And zero while-loops and zero-for-loops are allowed

special case creating step-pulses

step-pulses need an extreme tight timing ! Variating a single microsecond between two pulses will make the stepper-motor "stuttering"
The MobaTools-library achives this requirement through a timer-interrupt that gets called absolutely regulary. Offering this for up to 6 stepper-motors limits the maximum achievable speed. As long as you don't use high resolution microstepping the achievable speeds with using an arduino is sufficient.
If you need indeed more than 1/4 microstepping at high rpms you must use a faster microcontroller like an ESP32

// a comlex functionality doing multiple things "in parallel"
// requires quite a lot of lines of code
// at the end of this file the structure of the code is epxlained

#define ProjectName "MobaTools serial receive Speed Start Stop Bounce "

// define IO-states for inputs with pull-up-resistors
// pull-up-resistors invert the logig
#define unPressed HIGH
//#define pressed   LOW

const byte ToggleButtonPin = 4;
const byte dirPin          = 5;
const byte stepPin         = 6;
// ( 1600 steps / revolution = 1/8 Microstep )
const int STEPS_REVOLUTION = 1600;

const long baudrate = 115200;

#include <MobaTools.h> // stepper-motor-library

MoToStepper myStepper( STEPS_REVOLUTION, STEPDIR );

const long  targetPos = 8000; // stepper moves between 0 and targetpos
long nextPos;

//Array for storing serial received data
const unsigned int numChars = 128;
char receivedChars[numChars];
boolean newData = false;

unsigned int StepperSpeed = 1000;

bool activationMode = false;


void setup() {
  Serial.begin(baudrate); // adjust baudrate in the serial monitor to match the number
  Serial.println( F("Setup-Start") );
  printFileNameDateTime();

  pinMode (LED_BUILTIN, OUTPUT);  // used for indicating logging active or not
  digitalWrite(LED_BUILTIN, LOW);
  // wire button between IO-pin and GND
  // Pull-up-resistor inverts the logic
  // unpressed: IO-pin detects HIGH
  // pressed:   IO-Pin detects LOW
  pinMode(ToggleButtonPin, INPUT_PULLUP);
  SetUpStepperMotor();
  Serial.print( F("start / stop button must be conected to IO-PIN no ") );
  Serial.println(ToggleButtonPin);
  Serial.println( F("send speed-commands through serial monitor with <speed>") );
  Serial.println( F("example <500>") );
  
  Serial.println( F("press button to start / stop steppermotor") );
}


void loop () {

  recvWithStartEndMarkers(); // non-blocking checking if serial data is received
  // receive with Start- / Endmarker means the data must have
  // a leading "<" and a trailing ">" for switching newData to true
  if (newData) { // if a valid command is received
    newData = false;
    SetStepperMotorSpeed();
  }

  activationMode = GetToggleSwitchState(); // must be executed all the time
  execute_if_Active(activationMode);       // function that does what its name says
}


void SetUpStepperMotor() {
  myStepper.attach( stepPin, dirPin );
  //myStepper.attachEnable( enaPin, 10, LOW );        // Enable Pin aktivieren ( LOW=aktiv )

  // the maximum steprate pulses per second is limited
  // due to the kind of how the step-pulses are created with a timer-interrupt
  // Arduino Uno, Mega Nano 2500 steps / second
  // ESP8266 6250 steps / second
  // STM32F103 20000 steps / second
  // ESP32 30000 steps / second
  myStepper.setSpeed( StepperSpeed );
  myStepper.setRampLen( 100 );                       // Rampenlänge 100 Steps bei 20U/min
  Serial.print( F("step-pin is IO-pin no ") );
  Serial.println(stepPin);

  Serial.print( F("dir-pin is IO-pin no ") );
  Serial.println(dirPin);
}


bool GetToggleSwitchState() {
  // "static" makes variables persistant over function calls
  static bool toggleState     = false;

  static byte buttonStateOld = unPressed;
  unsigned long buttonDebounceTime = 100;
  unsigned long buttonDebounceTimer = 0;

  byte buttonStateNew;

  if ( TimePeriodIsOver(buttonDebounceTimer, buttonDebounceTime) ) {
    // if more time than buttonDebounceTime has passed by
    // this means let pass by some time until
    // bouncing of the button is over
    buttonStateNew = digitalRead(ToggleButtonPin);

    if (buttonStateNew != buttonStateOld) {
      // if button-state has changed
      buttonStateOld = buttonStateNew;
      if (buttonStateNew == unPressed) {
        // if button is released
        toggleState = !toggleState; // toggle state-variable
      } // the attention-mark is the NOT operator
    }   // which simply inverts the boolean state
  }     // !true  = false   NOT true  is false
  //       !false = true    NOT false is true
  return toggleState;
}


void SetStepperMotorSpeed() {

  Serial.print( F("I received #") );
  Serial.print(receivedChars);
  Serial.println( F("#") );

  StepperSpeed = atoi (receivedChars);
  if (StepperSpeed > 0) {
    myStepper.setSpeed( StepperSpeed );
    Serial.print( F("set speed to ") );
    Serial.print(StepperSpeed);
    Serial.println();
  }
  else {
    Serial.println( F("no valid speed-data") );
  }
}


void RunStepperMotor() {
  // if steppermotor has reached target-position and
  // does NOT move anymore
  if ( !myStepper.moving() ) {
    Serial.print( F("arrived at position ") );
    Serial.println(myStepper.currentPosition() );
    Serial.println();
    
    if ( nextPos == 0 ) {   // if is at startposition 0
      nextPos = targetPos; // drive to position max
    }
    else { // if is at endposition
      nextPos = 0; // drive to position zero
    }
    Serial.print( F("moving to position ") );
    Serial.println(nextPos);
    myStepper.moveTo( nextPos ); // start creating step-pulses
  }
}


void execute_if_Active(bool p_IsActivated) {

  if (p_IsActivated) {
    RunStepperMotor();
  }
  else { // DE-activated
    myStepper.stop();
  }

  PrintToSerialMonitor(p_IsActivated); // deactivate through commenting if not wanted
}


// helper-function ignore at first
void printFileNameDateTime() {
  Serial.print( F("File   : ") );
  Serial.println( F(__FILE__) );
  Serial.print( F("Date   : ") );
  Serial.println( F(__DATE__) );
  Serial.print( F("Project: ") );
  Serial.println( F(ProjectName) );
}

// ignore at first
// helper-function for easy to use non-blocking timing
boolean TimePeriodIsOver (unsigned long & expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod) has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

// helper-function
void PrintToSerialMonitor(boolean p_IsActivated) {
  static bool lastIsActivated;

  // only in case the activation-mode has CHANGED print ONE time
  if (p_IsActivated != lastIsActivated) {
    // only if state of parameter p_logIsActivated has changed
    if (p_IsActivated) {
      Serial.println();
      Serial.println( F("start executing") );
      Serial.println();
      digitalWrite(LED_BUILTIN, HIGH);
    }
    else { // not activated
      Serial.println();
      Serial.println( F("stopp executing") );
      Serial.println();
      digitalWrite(LED_BUILTIN, LOW);
    }

    lastIsActivated = p_IsActivated; // update variable lastSDlogActive
  }
}


void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static unsigned int ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  // If data is in the receive-buffer .available() > 0
  // .available() returns how MANY bytes are in the receive-buffer
  while (Serial.available() > 0 && newData == false) {
    rc = Serial.read();

    if (recvInProgress == true) { // if startmarker is found set flag to true
      if (rc != endMarker) {      // if the received byte is NOT the endmarker
        receivedChars[ndx] = rc;  // append the received byte at the end of char-array
        ndx++;                    // increase array-index by 1
        if (ndx >= numChars) {    // if message is longer than max number of chars the arraybuffer can hold
          ndx = numChars - 1;     // reduce array-index => byte is trhown away
        }
      } // if (rc != endMarker) {
      else { // it IS the endmarker
        receivedChars[ndx] = '\0'; // terminate the string through adding a zero
        recvInProgress = false;    // receiving of command is finished
        ndx = 0;
        newData = true;            // command is ready to be used
      }
    } // if (recvInProgress == true) {

    else if (rc == startMarker) {  // if the byte is the startmarker
      recvInProgress = true;       // set boolean flag receive in progress to true
    }
  } // while (Serial.available() > 0 && newData == false) {
}

/* OVERVIEW: ###################################################
  this demo-code does quite a lot of things

  the code has a hierachical structure
  the code is organised in SUB-units where each SUB-unit is
  doing a senseful unit of things like

  - receive serial data
  - Set StepperMotorSpeed
  - check if start/stop-button is pressed
  - depending on beeing in state "active" or "inactive"
    drive stepper-motor or not
  - create step-pulses to make the stepper-motor drive back and forth

  - helper-functios for
    - print source-code-filename to serial monitor
    - measure how much time has passed by since a referencetime
      (non-blocking timing)
    - print info to serial monitor to make visible what the code is doing


  in more Detail: ################################################

  toggle-switch:
  there is the function GetToggleSwitchState()
  GetToggleSwitchState() returns a "true"  or a "false"
  this transforms a momentary pushbutton into a toggle-switch
  press push-button => toggle-switch is in ON-position
  press push-button => toggle-switch is in OFF-position
  push-ON, Push-OFF, Push-ON, Push-OFF etc. etc.

  In function loop the state of GetToggleSwitchState()
  is checked ALL the time. Depending on variable activationMode
  beeing true or false the
  function execute_if_Active() is creating step-pulses
  or just pausing -----------------------------------------------


  receive serial data:
  The function recvWithStartEndMarkers()
  is checking for newly arrived serial data
  it is doing this in a receive byte for byte
  and NON-blocking manner.
  NON-blocking is important that the code stays responsive to
  button-presses ALL the time
  If the code receives bytes that do not follow the pattern
  leading startmarker "<"  trailing endmarker ">" the received bytes
  are just thrown away -------------------------------------------


  set steppermotor speed:
  If a complete command like "<500>" is received the variable newData
  is set true and then the function SetStepperMotorSpeed()
  processes the command.
  In case the command is invalid the steppermotor-speed stays the same
  in case the command is valid the stepper-motor-speed is changed
  -----------------------------------------------------------------

  some helper-functions:

*/

best regards Stefan

We are using single board computer "ODYSSEY - X86J4105800" with "Arduino Coprocessor ATSAMD21 Core ARM Cortex-M0+". Can it work with the MobaTools library?
And another stupid question: how can I download and install Mobatools from Github? Can I download the corresponding cpp files and compile them myself?

I tried Mobatools in Arduino MKR Wifi 1010(ATSAMD21 Core) but the library did not work in it. However, using timer interrupts as in that library is a good way to go.

I have written the code based on the following thread on timer interrupts.

#include <Arduino.h>

// Motor steps per revolution. Most steppers are 200 steps or 1.8 degrees/step
#define MOTOR_STEPS 3600
byte RPM = 60;
byte Angle = 10;

// All the wires needed for full functionality
#define DIR 6
#define STEP 7

uint8_t incomingByte = 0;

// Set timer TC4 to call the TC4_Handler every 5ms: (8 * (29999 + 1)) / 48MHz = 5ms
void setup() {
  PORT->Group[PORTA].DIRSET.reg = PORT_PA21;               // Set D7 as a digital output
  
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |                 // Enable GCLK0 for TC4 and TC5
                      GCLK_CLKCTRL_GEN_GCLK0 |             // Select GCLK0 at 48MHz
                      GCLK_CLKCTRL_ID_TC4_TC5;             // Feed GCLK0 output to TC4 and TC5
  while (GCLK->STATUS.bit.SYNCBUSY);                       // Wait for synchronization
 
  TC4->COUNT16.CC[0].reg = 29999;                          // Set the TC4 CC0 register as the TOP value in match frequency mode
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  NVIC_SetPriority(TC4_IRQn, 0);    // Set the Nested Vector Interrupt Controller (NVIC) priority for TC4 to 0 (highest)
  NVIC_EnableIRQ(TC4_IRQn);         // Connect TC4 to Nested Vector Interrupt Controller (NVIC)

  TC4->COUNT16.INTENSET.reg = TC_INTENSET_OVF;             // Enable TC4 overflow (OVF) interrupts
 
  TC4->COUNT16.CTRLA.reg |= TC_CTRLA_PRESCSYNC_PRESC |     // Reset timer on the next prescaler clock
                            TC_CTRLA_PRESCALER_DIV8 |      // Set prescaler to 8, 48MHz/8 = 6MHz
                            TC_CTRLA_WAVEGEN_MFRQ |        // Put the timer TC4 into match frequency (MFRQ) mode 
                            TC_CTRLA_MODE_COUNT16;         // Set the timer to 16-bit mode      
  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

//  TC4->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable the TC4 timer
//  while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization

  Serial.begin(250000);
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(DIR, OUTPUT);
}

void TC4_Handler()                                         // Interrupt Service Routine (ISR) for timer TC4
{      
  //if (TC4->COUNT16.INTFLAG.bit.OVF && TC4->COUNT16.INTENSET.bit.OVF)       // Optionally check for overflow (OVF) interrupt      
  //{   
    PORT->Group[PORTA].OUTTGL.reg = PORT_PA21;             // Toggle the D7 output
    TC4->COUNT16.INTFLAG.reg = TC_INTFLAG_OVF;             // Clear the OVF interrupt flag
  //}
}

// the loop function runs over and over again forever
void loop() {
  if (Serial.available() < 1) return;  
  incomingByte = Serial.read(); 
  switch( incomingByte )
  { 
    case '0': RPM = 0; break;
    case '1': RPM = 10; break;
    case '2': RPM = 20; break;
    case '3': RPM = 30; break;
    case '4': RPM = 40; break;
    case '5': RPM = 50; break;
    case '6': RPM = 60; break;
    case '7': RPM = 70; break;
    case '8': RPM = 80; break;
    case '9': RPM = 90; break;
    default: break;
  }
//  stepper.begin(RPM, MICROSTEPS);
//  stepper.rotate( 100 );
  //stepper.setRPM(RPM);
  if (RPM == 0)
  {
    TC4->COUNT16.CTRLA.bit.ENABLE = 0;                       // Disable the TC4 timer
    while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
  }
  else
  {
    if (RPM > 0)
    {
      digitalWrite(DIR, LOW);
    }
    else
    {
      digitalWrite(DIR, HIGH);
    }
    
    float step_width_sec = 60.0f / abs(RPM) / MOTOR_STEPS;
    int request_count = step_width_sec * 3000000;
    TC4->COUNT16.CC[0].reg = request_count;
    TC4->COUNT16.CTRLA.bit.ENABLE = 1;                       // Enable the TC4 timer
    while (TC4->COUNT16.STATUS.bit.SYNCBUSY);                // Wait for synchronization
    
  }
  //delay(1);
}

I have not been able to verify the operation on the actual device, but it appears to be working correctly as measured with an oscilloscope.
Note that the code does not rotate initially for safety reasons and that pulses are output on PIN 7.

I hope this help you.

EDIT: I forgot to make RPM absolute when calculating the step period.