Jump to content

Running X27-168 stepper motors direct from nano - development help requested


lesthegrngo

Recommended Posts

Hi all again.

 

After my experiments with stepper drivers and microstepping to try and improve the quality of the movement of stepper motors, I have been searching around to see if there is a better solution available.

 

To summarise, the movement I have with EasyDriver and A4988 stepper motor drivers is not smooth, and is accompanied by quite a loud chattering noise. Research on other forums has allowed me to find out that you can run an X27-168 stepper motor direct from the nano by connecting four digital pins. The benefits of this are

 

1) there is no requirement for a 12v power supply to the stepper driver, reducing some complexity, as the stepper motor uses very low current

 

2) the correct stepper pulsing is obtained, which means finer movement control

 

3) it is very quiet

 

4) you do not need a separate stepper motor driver board.

 

The downside of course is that you need four nano pins for each stepper, however if you were only using one nano per stepper motor anyway, it won't really make much difference. If you are running multiple stepper motors per Arduino (as I do) then you will of course reduce the maximum number of stepper motors each one can drive.

 

Of course that's the theory. I have a simple sketch and library that runs the stepper motor all the way to the anticlockwise stop, then clockwise to a predetermined position. It really is smooth and quiet in operation during testing, but the real proof is if we can get it to run a gauge using DSC BIOS. My thoughts were to get the following sketch that Craig very generously let us all have on his site, and modify it to take the new code and library to drive the stepper directly.

 

This is where I am asking for help. I am OK doing simple sketches, but the ability to take this and modify Craig's sketch is beyond my skills. So is anyone out there who is sufficiently versed in writing Arduino sketches able to take the sketch below and modify it? I am happy to give the libraries and basic sketch (can't be posted here as code as they are too big)

 

#define DCSBIOS_IRQ_SERIAL

#include <AccelStepper.h>
#include "DcsBios.h"

struct StepperConfig {
 unsigned int maxSteps;
 unsigned int acceleration;
 unsigned int maxSpeed;
};


class Vid29Stepper : public DcsBios::Int16Buffer {
 private:
   AccelStepper& stepper;
   StepperConfig& stepperConfig;
   unsigned int (*map_function)(unsigned int);
   unsigned char initState;
 public:
   Vid29Stepper(unsigned int address, AccelStepper& stepper, StepperConfig& stepperConfig, unsigned int (*map_function)(unsigned int))
   : Int16Buffer(address), stepper(stepper), stepperConfig(stepperConfig), map_function(map_function), initState(0) {
   }

   virtual void loop() {
     if (initState == 0) { // not initialized yet
       stepper.setMaxSpeed(stepperConfig.maxSpeed);
       stepper.setAcceleration(stepperConfig.acceleration);
       stepper.moveTo(-((long)stepperConfig.maxSteps));
       initState = 1;
     }
     if (initState == 1) { // zeroing
       stepper.run();
       if (stepper.currentPosition() <= -((long)stepperConfig.maxSteps)) {
         stepper.setCurrentPosition(0);
         initState = 2;
         stepper.moveTo(stepperConfig.maxSteps/2);
       }
     }
     if (initState == 2) { // running normally
       if (hasUpdatedData()) {
         unsigned int newPosition = map_function(getData());
         newPosition = constrain(newPosition, 0, stepperConfig.maxSteps);
         stepper.moveTo(newPosition);
       }
       stepper.run();
     }
   }
};

/* modify below this line */

/* define stepper parameters
  multiple Vid29Stepper instances can share the same StepperConfig object */
struct StepperConfig stepperConfig = {
 500,  // maxSteps
 1000, // maxSpeed
 1000 // acceleration
 };

// note cs im testing with 11 going to step (middle on easy drier) and 10 doing to direction (right on easy driver)
// cs so in the code going on the basis that the first named number is step and the second is direction
// define AccelStepper instance
AccelStepper stepper(AccelStepper::DRIVER, 10, 11);
// define Vid29Stepper class that uses the AccelStepper instance defined in the line above
//           +-- arbitrary name
//           |   +-- Address of stepper data (from control reference)
//           |   |       +-- name of AccelStepper instance
//           v   v       v        v-- StepperConfig struct instance
Vid29Stepper lHydPressBuffer(0x10c2, stepper, stepperConfig, [](unsigned int newValue) -> unsigned int {
 /* this function needs to map newValue to the correct number of steps */
 return map(newValue, 65535, 0, 0, stepperConfig.maxSteps);
}); 


void setup() {
 DcsBios::setup();
}

void loop() {
 DcsBios::loop();
}

 

 

Cheers

 

Les

Link to comment
Share on other sites

Have you considered not integrating the two, but using them separately?

i.e. leave your gauge code as-is, and run the steppers of another Nano.

 

I'm not sure what stepper driver code you're looking at, but if it can accept direction and step commands, it might be worth considering that, rather than cobbling together two sets of code that might not like working together.

Don't forget the hassle with the altimeter OLEDs. It might happen with the needles if you try to integrate too much.

Link to comment
Share on other sites

I see what you are getting at, but having the ability to run a gauge direct off a nano is still something I would like to be able to do

 

This is where I have got to with a simple sketch, but using BoboBear's diagnostic program I can't make it respond to the DCS BIOS inputs

 

#define DCSBIOS_IRQ_SERIAL

#include "DcsBios.h"
#include "SwitecX25.h"

// 315 degrees of range = 315x3 steps = 945 steps

unsigned int maxSteps = 945;


// declare motor with 945 steps on pins 8-11
SwitecX25 motor1(maxSteps, 8, 9, 10, 11);

//motor1.zero();   // this is a slow, blocking operation


void setup(void) {
 //Serial.begin(9600);
 DcsBios::setup();

 motor1.zero();   // this is a slow, blocking operation

}


void onLEngTempChange(unsigned int newValue) {
 return map(newValue, 0, 65535, 0, maxSteps);
 motor1.setPosition(newValue);
}
DcsBios::IntegerBuffer lEngTempBuffer(0x10b4, 0xffff, 0, onLEngTempChange);



void loop(void) {

 motor1.update();
 DcsBios::loop();

}

 

The gauge will zero, and then not do any more

 

If I add the line "motor1.setPosition(940);" it will zero then move to near the end of the clockwise travel as you would expect, so the setPosition argument works

 

#define DCSBIOS_IRQ_SERIAL

#include "DcsBios.h"
#include "SwitecX25.h"

// 315 degrees of range = 315x3 steps = 945 steps

unsigned int maxSteps = 945;


// declare motor with 945 steps on pins 8-11
SwitecX25 motor1(maxSteps, 8, 9, 10, 11);

//motor1.zero();   // this is a slow, blocking operation


void setup(void) {
 //Serial.begin(9600);
 DcsBios::setup();

 motor1.zero();   // this is a slow, blocking operation
 motor1.setPosition(940);

}


void onLEngTempChange(unsigned int newValue) {
 return map(newValue, 0, 65535, 0, maxSteps);
 motor1.setPosition(newValue);
}
DcsBios::IntegerBuffer lEngTempBuffer(0x10b4, 0xffff, 0, onLEngTempChange);



void loop(void) {

 motor1.update();
 DcsBios::loop();

}

 

I'm pretty certain that the syntax has something to do with it

 

Cheers

 

Les

Link to comment
Share on other sites

Try defining the value for motor 1 position as a global variable, then set it in the onLEngTempChange routine and put the motor1.setPosition line in the main loop.

 

OR, try putting the motor1.update(); in the onLEngTempChange function.

Link to comment
Share on other sites

Yep, my code to blame, but thankfully there are some guys out there who are really savvy with this

 

this works

 

#define DCSBIOS_DEFAULT_SERIAL

#include "DcsBios.h"
#include "SwitecX25.h"

// 315 degrees of range = 315x3 steps = 945 steps
int newValue;
unsigned int maxSteps = 945;


// declare motor1 with 945 steps on pins 8-11
SwitecX25 motor1(maxSteps, 8, 9, 10, 11);





void setup(void) {
 
 DcsBios::setup();

 motor1.zero();   // this is a slow, blocking operation
 motor1.setPosition(940);

}


void onLEngTempChange(unsigned int newValue)
{
 unsigned int position =  map(newValue, 0, 65535, 0, 945);
 motor1.setPosition(position);
}
DcsBios::IntegerBuffer lEngTempBuffer(0x10b4, 0xffff, 0, onLEngTempChange);


void loop(void) {

 
 motor1.update();
 DcsBios::loop();

}

 

Even a very quick trial with the new code shows a very very different quality in terms of the smoothness and quietness of the stepper movement to the inputs. This is definitely the right direction to take, even though it does come with some complications in more physical ways.

 

I think I have to look at the acceleration and max speed values in the libraries, as they probably will allow even more improvement - when I did my experiments with microstepping I noticed that too fast a max speed would end up in very staccato pointer movement unless making large changes. Lowering the speed will probably help if I can work out how to do so

 

This looks pretty hopeful guys

 

Cheers

 

Les

Link to comment
Share on other sites

I have to report more disappointment, I'm sorry to say.

 

While the movement of the pointer using BoboBears diagnostic tool is smoother, once in game the quality of the movement is virtually indistinguishable from the gauges using driver boards.

 

I need to look at it in more depth, maybe the rate that the gauge data is being sent means that the refresh rate is too low to support smooth movement. Whatever, it's not the Panacea I was hoping for

 

Les

Link to comment
Share on other sites

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...