Go Down

Topic: question re' controlling stepper with potentiometer (easy driver) (Read 3340 times) previous topic - next topic

raschemmel

#60
Jul 16, 2014, 05:17 am Last Edit: Jul 16, 2014, 03:40 pm by raschemmel Reason: 1
@Robin,
Run this.
I am running my steppers with a 25uS delay between steps:
I measured the step pulse width and delay on a scope. (see attached).
See attached short video of the test.
I had to increase the step pulse width to 25uS to get the delay below 40 uS.



Code: [Select]
// testing a stepper motor with a Pololu A4988 driver board or equivalent
// on an Uno the onboard led will flash with each step
// as posted on Arduino Forum at http://forum.arduino.cc/index.php?topic=208905.0

byte directionPin = 7;
byte stepPin = 8;
int numberOfSteps = 3000;
byte ledPin = 13;
int pulseWidthMicros = 25;  // microseconds
int uSbetweenSteps = 25; // microseconds


void setup()
{

 Serial.begin(9600);
 Serial.println("Starting StepperTest");
 digitalWrite(ledPin, LOW);
 
 delay(200);

 pinMode(directionPin, OUTPUT);
 pinMode(stepPin, OUTPUT);
 pinMode(ledPin, OUTPUT);
 


}

void loop()
{
digitalWrite(directionPin, HIGH);
 for(int n = 0; n < numberOfSteps; n++) {
   digitalWrite(stepPin, HIGH);
   delayMicroseconds(pulseWidthMicros);
   digitalWrite(stepPin, LOW);
   
   delayMicroseconds(uSbetweenSteps);
   
   digitalWrite(ledPin, !digitalRead(ledPin));
 }
 
 delay(500);
 

 digitalWrite(directionPin, LOW);
 for(int n = 0; n < numberOfSteps; n++) {
   digitalWrite(stepPin, HIGH);
   delayMicroseconds(pulseWidthMicros);
   digitalWrite(stepPin, LOW);
   
   delayMicroseconds(uSbetweenSteps);
   
   digitalWrite(ledPin, !digitalRead(ledPin));
 }
 
}
   
Arduino UNOs, Pro-Minis, ATMega328, ATtiny85, LCDs, MCP4162, keypads,<br />DS18B20s,74c922,nRF24L01, RS232, SD card, RC fixed wing, quadcopter

Cretster

Get well soon also!

Robin posted code I think on the first page of the thread which has a suitable line. There are two lines of code where it has pulse length and delay. I think the delay between steps was 25ms so you can just reduce that. I took it down to 1ms but at 0 the stepper didn't work correctly.

Robin2


@Robin,
Run this.
I am running my steppers with a 25uS delay between steps:



Thanks, will try it later. Certainly seems faster than mine - but are those full steps or microsteps?

By the way your step interval is a bit over 50usecss as you have 25 twice. I've discovered (with advice from someone else here) that there is no need for delay(pulseWidthMicros); so you could drop that and change the other number to 50.

From reading the Allegro A4988 datasheet it is the rising pulse that triggers the step - always good to go back to the source even belatedly.

...R

PS...  @Cretster,  @Raschemmel is using my code but has changed the delay from millis to micros - though he hasn't updated his comment !   ...R

raschemmel

#63
Jul 16, 2014, 03:15 pm Last Edit: Jul 16, 2014, 03:43 pm by raschemmel Reason: 1
Code: [Select]
  delayMicroseconds(pulseWidthMicros);

Code: [Select]
    int uSbetweenSteps = 25; // milliseconds 

@ Cretst,
Yes, that is correct. I didn't change the comment (see above) .   until just now when I read yours.
I just changed it now to this:
Code: [Select]
  int uSbetweenSteps = 25; // microseconds

And you're right about the delay because that scope setting is 20uS (not 10).
And Robin , it is 1/16th step microsteps.
Arduino UNOs, Pro-Minis, ATMega328, ATtiny85, LCDs, MCP4162, keypads,<br />DS18B20s,74c922,nRF24L01, RS232, SD card, RC fixed wing, quadcopter

Robin2

With 50usecs between steps and 1/16 microsteps that's about 800usecs between full steps which is pretty much the same as I got with full steps - I think anything less than 850 gave very little torque. And I think I got it down to the equivalent of 600usecs between full steps with 1/4 micro stepping - i.e. 150usecs between each quarter step.

...R

raschemmel

Ok. I didn't do the math for calculating for full step so originally we were talking about apples and oranges.
Arduino UNOs, Pro-Minis, ATMega328, ATtiny85, LCDs, MCP4162, keypads,<br />DS18B20s,74c922,nRF24L01, RS232, SD card, RC fixed wing, quadcopter

Cretster

I've got mine at 50us now and I love how it's moving so time to start thinking about the code for making it actually do what I want, as per the logic already discussed!

Cretster

And now I have got a serial print readout giving me the values from the pot output ranging from -512 to 511 across its full range.

So far so good. :)

Cretster

#68
Jul 17, 2014, 02:19 am Last Edit: Jul 17, 2014, 02:20 am by Cretster Reason: 1
Quite pleased as I have now got the stepper working so that it changes direction depending whether the pot is turned to one side or the other.

No speed control yet, but I'm pleased with this.  This would actually do what I needed at a primitive level but no point stopping at this stage now it's getting to be fun, and speed would be useful to cope with varying weather conditions (affects the timing of the shots)!!  :D

This code is probably not great but it's a mashup of different examples and some ad-lib logic, but it does actually work.
So I need to work out controlling speed next, then probably last for now some sort of vaguely civilised start/end process perhaps (not quite sure what).

Code: [Select]
byte directionPin = 12;
byte stepPin = 13;
int numberOfSteps = 5;
byte ledPin = 13;
int uSbetweenSteps = 50; // microseconds
int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor
int sensorValueX = 0;  // new var to deduct 512 from Val
int dir = 0; // notional serial output flag for direction


void setup()
{
 Serial.begin(9600);
 Serial.println("Starting StepperTest");
 digitalWrite(ledPin, LOW);
 delay(2000);
 pinMode(directionPin, OUTPUT);
 pinMode(stepPin, OUTPUT);
 pinMode(ledPin, OUTPUT);
}

void loop()
{
 sensorValue = analogRead(sensorPin);    
 sensorValueX = sensorValue-512; // JD+
 
if
  (sensorValueX < 0)
  {digitalWrite(directionPin, HIGH); // Turn one way if the pot is turned low
  {dir = 1;}} // and set the direction flag
else
  {digitalWrite(directionPin, LOW); // Turn the other way if the pot is turned high
  {dir = 0;}}  // and set the direction flag

Serial.println(dir); //report the direction flag

 for(int n = 0; n < numberOfSteps; n++)
 {
   digitalWrite(stepPin, HIGH);
   delay(uSbetweenSteps);
   digitalWrite(ledPin, !digitalRead(ledPin));
 }
 
}

raschemmel

I did something similar for a post once. The OP wanted a program to control an actuator but it WAS NOT  a stepper.
The following code shows how I got it to turn one direction when you moved the pot from the center toward the positive terminal of the pot and the other directrion when you moved the pot from the center toward the negative terminal of the pot. In the center the dc motor was stopped. I don't know if there is anything here of use to use but here it is anyway.


Code: [Select]
  // Adafruit Motor shield library
// copyright Adafruit Industries LLC, 2009
// this code is public domain, enjoy!

#include <AFMotor.h>
int STATE =0;
int val=0;
AF_DCMotor motor(1);
int  pot =A0;
int  pot1 =A1;
int  pot2 =A2;
int  pot3 =A3;
int  pot4 =A4;
int  pot5 =A5;
void setup()
{
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println("Motor test!");
  pinMode(pot,INPUT);
  // turn on motor
  motor.setSpeed(200);

  motor.run(RELEASE);
}

void loop()
{
  // uint8_t i;
 
 
 
   int val = analogRead(pot);
   Serial.print("pot= ");
   Serial.println(val);
   if (val>512)
       STATE =1;
   if (val<513)
       STATE =2;
     
 
   switch (STATE)
  {
    case 1:
      //do something when var equals 1
          Serial.println("FORWARD");
          motor.run(FORWARD);
          val = map(val,513, 1023, 0, 255);
           Serial.print("val= ");
           Serial.println(val);
          motor.setSpeed(val); 
          //delay(1000);
         
      break;
    case 2:
      //do something when var equals 2
           Serial.println("REVERSE");
          motor.run(BACKWARD);
          val = map(val, 512, 0, 0, 255);
          Serial.print("val= ");
           Serial.println(val);
          motor.setSpeed(val); 
          //delay(1000);
      break;
    default:
      // if nothing else matches, do the default
      // default is optional
      delay(10);
  }
   

  Serial.println("tech");
  //motor.run(RELEASE);
  delay(100);
Arduino UNOs, Pro-Minis, ATMega328, ATtiny85, LCDs, MCP4162, keypads,<br />DS18B20s,74c922,nRF24L01, RS232, SD card, RC fixed wing, quadcopter

Robin2


Quite pleased as I have now got the stepper working so that it changes direction depending whether the pot is turned to one side or the other.


Before your code becomes complicated may I strongly recommend that you divide it up into separate small functions to keep each activity in its own box. It will make the project much easier to manage as it grows.

...R

Cretster

Not a bad idea yeah.

I think for the speed I'll define another variable derived from existing positive or negative 0-512 value, then unsign it like this to give the basis for a step delay to control the speed:

Code: [Select]
if

                Val < 0

then

                Val2 = Val * - 1

else

                Val2 = Val


Robin2

#72
Jul 17, 2014, 07:42 pm Last Edit: Jul 17, 2014, 07:47 pm by Robin2 Reason: 1
I think what you are looking for is the abs() function.

Code: [Select]
val2 = abs(val);

Even faster (if a little less clear) is

Code: [Select]
val2 = val & 0b0111111111111111;

which turns the sign bit to 0 even if it is 1. (not tested)

...R


Cretster

Ahh, that's great thanks!  I was looking for some sort of 'unsign' function and never thought to look for absolute!  Long day.

Well what I've done works, BUT, it needs improving greatly because the trouble is the values I've got are very very non linear due to the numbers involved.  My brain is a bit fried after work today but I'll have a think what sort of maths would restrict the range it's working in.  Also I need to do some sort of inversion to make it play nicer around the mid point.

So currently, the mid point of the pot is the swap point for forwards/backwards, but due to the way the numbers work, either direction is at it's fastest immediately at that point, so changing directions is a very abrupt event!  So I'll work out the inverse basically.

But aside from that, nearly all the speed control that's in any way useful is within a tiny movement of the centre.  So that's what I need to improve. 

But it works at a crude level so I'm quite pleased. :)


Code: [Select]
// testing a stepper motor with a Pololu A4988 driver board or equivalent
// on an Uno the onboard led will flash with each step
// as posted on Arduino Forum at http://forum.arduino.cc/index.php?topic=208905.0

byte directionPin = 12;
byte stepPin = 13;
int numberOfSteps = 5;
byte ledPin = 13;
int uSbetweenSteps = 50; // microseconds
int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor
int sensorValueX = 0;  // new var to deduct 512 from Val
int dir = 0; // notional serial output flag for direction
int stepdelaytime = 0;

void setup()
{
  Serial.begin(9600);
  Serial.println("Starting StepperTest");
  digitalWrite(ledPin, LOW);
  delay(2000);
  pinMode(directionPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  sensorValue = analogRead(sensorPin);   
  sensorValueX = sensorValue-512; // JD+
 
if
   (sensorValueX < 0)
   {
     digitalWrite(directionPin, HIGH); // Turn one way if the pot is turned low
   {dir = 1;}
   {stepdelaytime = sensorValueX * - 1;}

} // and set the direction flag
else
   {digitalWrite(directionPin, LOW); // Turn the other way if the pot is turned high
   {dir = 0;}
  {stepdelaytime = sensorValueX;}
}  // and set the direction flag

Serial.println(dir); //report the direction flag

  for(int n = 0; n < numberOfSteps; n++)
  {
    digitalWrite(stepPin, HIGH);
    delay(stepdelaytime);
    digitalWrite(ledPin, !digitalRead(ledPin));
  }
 
}

Cretster

Right - this code is not pretty nor optimal, and I think there are bits I can remove now that are superfluous, but as a basic control that does exactly what I want, it works!!

Code: [Select]
// testing a stepper motor with a Pololu A4988 driver board or equivalent
// on an Uno the onboard led will flash with each step
// as posted on Arduino Forum at http://forum.arduino.cc/index.php?topic=208905.0

byte directionPin = 12;
byte stepPin = 13;
int numberOfSteps = 5;
byte ledPin = 13;
int uSbetweenSteps = 50; // microseconds
int sensorPin = A0;    // select the input pin for the potentiometer
int sensorValue = 0;  // variable to store the value coming from the sensor
int sensorValueX = 0;  // new var to deduct 512 from Val
int sensorValueY = 1;  // new var to deduct 512 from Val

int dir = 0; // notional serial output flag for direction
int stepdelaytime = 0;
int stepdelaytime2 = 0;
int stepdelaytime2b = 1;
int delaytime = 1;

void setup()
{
  Serial.begin(9600);
  Serial.println("Starting StepperTest");
  digitalWrite(ledPin, LOW);
  delay(2000);
  pinMode(directionPin, OUTPUT);
  pinMode(stepPin, OUTPUT);
  pinMode(ledPin, OUTPUT);
}

void loop()
{
  sensorValue = analogRead(sensorPin);   
  sensorValueX = sensorValue-512; // JD+
        //Serial.println(sensorValue);
     
      //Serial.println(sensorValueX);
  if
    (sensorValueX == 0)
    {
    sensorValueY = 1;
    }
  else
    {
    sensorValueY = sensorValueX;
    };
     
     
     if
         (sensorValueY < 0)
     {
     digitalWrite(directionPin, HIGH); // Turn one way if the pot is turned low
     {dir = 1;}
     {stepdelaytime = sensorValueY * - 1;}

     } // and set the direction flag
       
     else
     {digitalWrite(directionPin, LOW); // Turn the other way if the pot is turned high
     {dir = 0;}
     {stepdelaytime = sensorValueY;}
     }  // and set the direction flag

     stepdelaytime2 = sqrt(stepdelaytime)/2; // this is to help improve the control behaviour
                                             // of the knob output and reduce the range of delay
       if
    (stepdelaytime2 == 0) // a value of zero causes the code to stop so we weed this out for now with default 1
    {
    stepdelaytime2b = 1;
    }
  else
    {
    stepdelaytime2b = stepdelaytime2;
    };
delaytime = 11/stepdelaytime2b; // this is to invert the speed control about its centre point.
// 11 is the max value thus dividing into it gives the inverse and the speed control goes the right way now

      //Serial.println(delaytime);

     for(int n = 0; n < numberOfSteps; n++)
     {
     digitalWrite(stepPin, HIGH);
     delay(delaytime);
     digitalWrite(ledPin, !digitalRead(ledPin));
     }
   
}


Probably makes experience people cringe but it works and I'm happy for the minute.
The speed control is still not perfectly linear but it's way better and quite usable. 
The centre point of the pot gives the control over direction, then speed increases as you rotate it further in either direction.

I set the centre to default 1 and not allow zero since it was stopping the motor & code, but I think now I just need to let a zero value to write LOW for the step command to make it stop?

Anyway - very happy that I at least have something I can use even though it needs refining.

One other question - is there any way I can auto intent all of the code?  I hate not being able to do that!

Thanks  :D

Go Up