-1
\$\begingroup\$

I am currently counting steps of the movement of my stepper motor in a ISR, but cannot figure out where i should read in the incremented value (step_count) for further processing. The motor moves when it "told" to do so. It seems very crucial where i place the read, as I am getting different results according to where i place it.

I added the full code down below.

This is my ISR routine:

volatile speed_profile profile
ISR(TIMER1_COMPA_vect)
{
  // Holds next delay period.
  unsigned int new_first_step_delay;

  // Remember the last step delay used when accelrating.
  static int last_accel_delay;

  // Counting steps when moving.
  static unsigned int step_count = 0;

  // Keep track of remainder from new_step-delay calculation to incrase accurancy
  static unsigned int rest = 0;
  OCR1A = profile.first_step_delay;  
  switch (profile.run_state)
  {

    case STOP:     
      step_count = 0;
      profile.moved_steps = step_count; // step_count gets read by an global variable where should i place this . 
      rest = 0;
      break;

    case ACCEL:
      step_count++;
      profile.accel_count++;
      //do something

      // Chech if we should start decelration.
      if (step_count >= profile.decel_start)
      {
        profile.accel_count = profile.decel_length;
        profile.run_state = DECEL;
      }

      // Chech if we hitted max speed.
      else if (new_first_step_delay <= profile.min_time_delay)
      {
        last_accel_delay = new_first_step_delay;
        new_first_step_delay = profile.min_time_delay;
        rest = 0;
        profile.run_state = RUN;
      }
      break;
    case RUN:
      digitalWrite(step_pin,!digitalRead(step_pin));
      step_count++;
       //Do something

      if (step_count >= profile.decel_start) 
      {
        profile.accel_count = profile.decel_length;
        // Start decelration with same delay as accel ended with.
        new_first_step_delay = last_accel_delay;
        profile.run_state = DECEL;
      }
      break;
    case DECEL:
      digitalWrite(step_pin,!digitalRead(step_pin));      
      step_count++;
      profile.moved_steps = step_count;
      // do something
      // Check if we at last step
      if(profile.accel_count >= 0)
      {
        profile.run_state = STOP;
        global_state = false;
      }

      break;

  }
    profile.first_step_delay = new_first_step_delay;  

} 

Placing the read here makes it impossible to process, as in it wont let me manipulate it, when i read it into a different variable and process the other variable, I get the original step_value, even though the processing (in this case normalizing the value), is performed on the other variable before it is printed on the screen (Yes.. I don't get that either). A second drawback of placing it here is, that when the motor is being told to move 1000 steps, it does not increment the step value, but telling it again to move 1000 steps changes the value to 1000.. making the step count one command behind.

Placing the read at the end of the ISR routine makes it possible to process the variable, but the value it contains, is inaccurate, as in it doesn't contain the latest value, which mess up with the tracking of the step-count.

It should be noted that I use an interrupt guard when i read the value of profile.moved_steps for further processing, I choose not to directly interact the with variable to ensure I not am writing/reading or does half in case of an interrupt is being called, which I suspect could be the issue? The operation performed inside the guard, is a simple read of the value into to temp variable, which is then processed.

So my question where do i place the instruction

 profile.moved_steps = step_count;

such that i get the latest value, and don't make it stuck such that i can't perform any operations with it.

profile us instantiated as volatile.

I guess i could keep guessing? but my gut is telling that something very wrong?.. Am I having the incorrect values?

here is a minimal working example:

Main.ino

#include "speed_profile.h"


void setup() {
  // put your setup code here, to run once:
  cli();
  output_pin_setup();
  timer1_setup();
  sei();
}

void loop() 
{
      int motor_steps = 23400;//-9600; //23400 -  100%
      // Accelration to use.
      int motor_acceleration = 500;//500;
      // Deceleration to use.
      int motor_deceleration = 500;//500;
      // Speed to use.
      int motor_speed = 1000; // 1000
      compute_speed_profile(motor_steps, motor_acceleration, motor_deceleration, motor_speed); 
}

.cpp

#include "speed_profile.h"


volatile speed_profile profile;

ros::NodeHandle nh;

std_msgs::Int16 status_step_count;
ros::Publisher chatter("tipper_status", &status_step_count);

bool global_state = false;
int received_data = 0;

void call_back_control( const std_msgs::Empty & msg)
{
  global_state = true;

}
void output_pin_setup()
{
  pinMode(en_pin, OUTPUT);
  pinMode(dir_pin, OUTPUT);
  pinMode(step_pin, OUTPUT);
  nh.initNode();
  nh.advertise(chatter);
  nh.subscribe(sub_control);
  //nh.subscribe(sub_command);
  profile.moved_steps = 0;
  profile.current_step_position = 0;
  delay(1000);
  nh.getHardware()->setBaud(57600);

}

void timer1_setup()
{
  // Tells what part of speed ramp we are in.
  profile.run_state = STOP;

  // Timer/Counter 1 in mode 4 CTC (Not running).
  TCCR1B = (1 << WGM12);

  // Timer/Counter 1 Output Compare A Match Interrupt enable.
  TIMSK1 = (1 << OCIE1A);
}


ros::Subscriber<std_msgs::Empty> sub_control("tipper_control", &call_back_control );

void compute_speed_profile(signed int motor_steps, unsigned int motor_accel, unsigned int motor_decel, unsigned int motor_speed)
{
  while (global_state == false)
  {
    //do nothing
    status_step_count.data = profile.current_step_position; //profile.current_step_position;
    chatter.publish( &status_step_count);
    nh.spinOnce();
    delay(1);

  }

  profile.min_time_delay = A_T_x100 / 500;
  profile.first_step_delay = (T1_FREQ_148 * sqrt(A_SQ / 100)) / 100;
  profile.decel_length = -(3000 - 1500);
  profile.decel_start = 3000 + profile.decel_length;
  profile.first_step_delay = profile.min_time_delay;
  profile.run_state = ACCEL;
  profile.accel_count = 0;

  OCR1A = 10;
  // Set Timer/Counter to divide clock by 8
  TCCR1B |= ((0 << CS12) | (1 << CS11) | (0 << CS10));

  while (global_state == true)
  { 
    cli();
    int temp =  profile.moved_steps;     // Keeping ISR disable shortest time possible
    sei();

    if (profile.dir == CCW)
    {
      profile.current_step_position -=   temp;
    }
    else
    {
      profile.current_step_position =  temp;
    }    

    status_step_count.data = profile.current_step_position;
    chatter.publish( &status_step_count);

    nh.spinOnce();
    delay(1);
  }
}


ISR(TIMER1_COMPA_vect)
{
  // Holds next delay period.
  unsigned int new_first_step_delay;

  // Remember the last step delay used when accelrating.
  static int last_accel_delay;

  // Counting steps when moving.
  static unsigned int step_count = 0;

  // Keep track of remainder from new_step-delay calculation to incrase accurancy
  static unsigned int rest = 0;
  OCR1A = profile.first_step_delay;
  switch (profile.run_state)
  {

    case STOP:
      step_count = 0;
      rest = 0;
      TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // Stop the timer,  No clock source
      break;

    case ACCEL:
      step_count++;
      profile.accel_count++;
      if (new_first_step_delay <= profile.min_time_delay)
      {
        last_accel_delay = new_first_step_delay;
        new_first_step_delay = profile.min_time_delay;
        rest = 0;
        profile.run_state = RUN;
      }
      break;
    case RUN:
      step_count++;
      if (step_count >= profile.decel_start)
      {
        profile.accel_count = profile.decel_length;
        // Start decelration with same delay as accel ended with.
        new_first_step_delay = last_accel_delay;
        profile.run_state = DECEL;
      }
      break;
    case DECEL:
      step_count++;

      if (profile.accel_count >= 0)
      {

        profile.run_state = STOP;
        global_state = false;
      }

      break;

  }
  profile.moved_steps = step_count;
  profile.first_step_delay = new_first_step_delay;

}

.h

#ifndef speed_profile_h
#define speed_profile_h


#include <Arduino.h> 
#include <ros.h>
#include <std_msgs/Int16.h>
#include <std_msgs/Empty.h>

// Timer/Counter 1 running on 3,686MHz / 8 = 460,75kHz (2,17uS). (T1-FREQ 460750)
#define T1_FREQ 1382400

//! Number of (full)steps per round on stepper motor in use.
#define FSPR 1600

// Maths constants. To simplify maths when calculating in compute_speed_profile().
#define ALPHA (2*3.14159/FSPR)                    // 2*pi/spr
#define A_T_x100 ((long)(ALPHA*T1_FREQ*100))     // (ALPHA / T1_FREQ)*100
#define T1_FREQ_148 ((int)((T1_FREQ*0.676)/100)) // divided by 100 and scaled by 0.676
#define A_SQ (long)(ALPHA*2*10000000000)         // ALPHA*2*10000000000
#define A_x20000 (int)(ALPHA*20000)              // ALPHA*20000

// Speed ramp states
#define STOP  0
#define ACCEL 1
#define DECEL 2
#define RUN   3


// Motor direction 
#define CW  0
#define CCW 1

typedef struct 
{
 volatile unsigned char run_state : 3; // Determining the state of the speed profile
 volatile unsigned char dir: 1; // Determining the direction the motor has to move - Start being CCW 
 volatile unsigned int first_step_delay; // Period time for the next timer_delay, determines the acceleration rate. 
 volatile unsigned int decel_start; //  Determines at which step the deceleration should begin. 
 volatile signed int decel_length; // Set the deceleration length
 volatile signed int min_time_delay; //minium time required time_delay for achieving the wanted speed.
 volatile signed int accel_count; // counter used when computing step_delay for acceleration/decelleration. 
 volatile unsigned int moved_steps; // Used by ros to publish current tipper position
 volatile unsigned int current_step_position; // contains the current step_position
 volatile unsigned int step_counter; //used for debug purpose

}speed_profile;


void computation_of_speed_profile(signed int motor_steps, unsigned int motor_accel, unsigned int motor_decel, unsigned int motor_speed);
#endif
\$\endgroup\$

closed as unclear what you're asking by Chris Stratton, Voltage Spike, Daniel Grillo, Autistic, brhans Oct 26 '16 at 21:07

Please clarify your specific problem or add additional details to highlight exactly what you need. As it's currently written, it’s hard to tell exactly what you're asking. See the How to Ask page for help clarifying this question. If this question can be reworded to fit the rules in the help center, please edit the question.

  • \$\begingroup\$ So.. the way i done my example is correct. I my case is global_variable a struct i've defined, but its still instantiated as a volatile.. \$\endgroup\$ – J.Down Oct 7 '16 at 22:30
  • \$\begingroup\$ Could you elaborate on atomic access? \$\endgroup\$ – J.Down Oct 7 '16 at 22:32
  • 1
    \$\begingroup\$ You're going to need to be more clear about what you observe. What is the "final value" and when is it observed? It should be obvious that a change made within the ISR won't be seen by the code in the main thread of execution until that code has a chance to access the variable. \$\endgroup\$ – Chris Stratton Oct 7 '16 at 23:06
  • 1
    \$\begingroup\$ I guess i need to become specific.. So i have added my specific case here.. Hope it makes it easier to understand \$\endgroup\$ – J.Down Oct 7 '16 at 23:36
  • 1
    \$\begingroup\$ Ok... I added the full code. Hope that will make things a bit clearer to understand. \$\endgroup\$ – J.Down Oct 8 '16 at 0:37
1
\$\begingroup\$

Having two different variables to do the job of one variable seems strange. Initialize some_variable outside ISR and make it volatile so that compiler wouldn't optimize it making it constant which happens if you use some kind of optimization. That way just increment your variable whenever wanted

\$\endgroup\$
  • \$\begingroup\$ the two variables exist to create a way to read the value available in the ISR scope, inside the while(1) scope. \$\endgroup\$ – J.Down Oct 7 '16 at 22:43
  • \$\begingroup\$ If you really need to, you might use some temporary variable. Though I don't see any point for that or at least your question doesn't provide enough information for me to do that \$\endgroup\$ – Artūras Jonkus Oct 7 '16 at 22:52
  • \$\begingroup\$ I tried as you suggested to just use the global variable, but that seem to result in the same problem as before. \$\endgroup\$ – J.Down Oct 7 '16 at 23:01

Not the answer you're looking for? Browse other questions tagged or ask your own question.