Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to connect output steppers - Its not an issue with library, more like need help in using it #29

Open
gaurav2827 opened this issue Apr 16, 2021 · 8 comments

Comments

@gaurav2827
Copy link

Thanks a lot for this wonderful library. Detailed work indeed!

I have gone through the "SingleJoystickFFB" in examples but unable to understand where or how to connect steppers in the arduino board. The sketch says pin 7, 6, 9 are outputs. Pin 9 gives PWM output (to motor driver) understood but unable to catch the idea of pin 6 & 7. I have used the previous Joystick library for my home cockpit building but I am new to controlling steppers.
Please help!

Regards,
Gaurav

@gaurav2827 gaurav2827 changed the title How to connect output steppers - Its not an issue with librarym more like need help in using it How to connect output steppers - Its not an issue with library, more like need help in using it Apr 16, 2021
@gaurav2827
Copy link
Author

Hi guys, anyone who can help me on this?

@YukMingLaw
Copy link
Owner

Hi,
pin7,6 are used to determine the direction of motor rotation.

1 similar comment
@YukMingLaw
Copy link
Owner

Hi,
pin7,6 are used to determine the direction of motor rotation.

@sgtnoodle
Copy link

Stepper motors require a special type of driver, and they commonly use two control pins, "direction" and "step". You need to precisely control the step signal to make the motor move.

You can either use a pwm peripheral and vary the frequency, or you can use a timer interrupt to toggle it with code. You likely won't be able to do what you're hoping in the main loop as this joystick library without using pwm because the library takes several milliseconds per cycle to do its calculations and USB accesses.

@gaurav2827
Copy link
Author

Hi,
pin7,6 are used to determine the direction of motor rotation.

Thanks for this. I am trying to use the example code but unable to get any output from arduino. My motor simply wont run or respond to anything. Its detected on fsforce.exe but nothing after that. No input is registered in the application or the windows game controller window.

If you have a working code, could you plz share in the example folder.

Regards

@sgtnoodle
Copy link

I wouldn't expect any sort of example to run out of the box without some amount of hardware specific tweaking. Here's my main sketch for a wheel with a DC motor driven by an H-bridge, and a SPI quadrature decoder IC. It uses a modified joystick library that I forked so I could fix some bugs and add default spring centering. You likely won't be able to even compile it, but you can see it as a reference of what all I had to do to get my wheel working.

#include <Joystick.h>
#include <LS7366.h>
#include <SPI.h>

#define ICR 600

#define DIR_PIN 8
#define PWM_PIN 9
#define BRAKE_PIN 10
#define LED_PIN 13

//#define COUNT_HALF_RANGE 61440  // 1.5 turn
#define COUNT_HALF_RANGE 51200  // 1.25 turn for 900 deg lock to lock

#define AXIS_HALF_RANGE 32767

#define ENABLE_JOYSTICK 1

#define ENABLE_SWEEP 0


// These were measured at about 12.3V
//#define MAX_RATE 35987.5f  // 1 turn
//#define MAX_RATE 23991.666666667f  // 1.5 turns
//#define MAX_RATE 28790.0f  // 1.25 turns

// This is at 14V.
// This number just worked out really nicely...
#define MAX_RATE 32767.0f  // 1.25 turns

#define HARDSTOP_DC (ICR * 0.8f)
#define HARDSTOP_FADE 1500.0f

#define FEEDBACK_DC (ICR * 0.25f)

// When applying a braking torque to the motor, there's a linear electrical power gain and a quadratic resistive power loss.
// When there's more electrical power than resistive loss, power gets dumped back into the supply, aka regen braking.
// If we knew the motor's and brake resistor's parameters, we could just calculate that power and compute the right
// average voltage to apply across the braking resistor. That's a lot of work, though, so instead we can just fudge it
// and measure voltage spikes with a multimeter. Too low, and the supply voltage will spike. Too high, and the resistor will
// get unnecessarily hot.
#define BRAKE_MULTIPLIER 0.5f

LS7366 myLS7366(7 /* chip select */);



class LowPassFilter
{
  public:
    LowPassFilter(float tc_sec)
      : _tc_sec(tc_sec),
        _tc_sec_reciprocal(1.0f/tc_sec)
    {}

    void reset(float output)
    {
      _output = output;
      _rate = 0.0f;
    }
    
    float output() const { return _output; }
    float rate() const { return _rate; }
    void update(float value, float dt_sec)
    {
      if (dt_sec >= _tc_sec)
      {
        _rate = (value - _output) / dt_sec;
      }
      else
      {
        _rate = (value - _output) * _tc_sec_reciprocal;
      }
      _output += _rate * dt_sec;
    }
    
  private:
    const float _tc_sec;
    const float _tc_sec_reciprocal;
    float _output = 0.0f;
    float _rate = 0.0f;
};


LowPassFilter pos_filter(0.01f);
// Low pass filter the position filter's rate to get acceleration.
LowPassFilter vel_filter(0.01f);

#if ENABLE_JOYSTICK
//X-axis & Y-axis REQUIRED
Joystick_ Joystick(JOYSTICK_DEFAULT_REPORT_ID, 
  JOYSTICK_TYPE_JOYSTICK, 0, 0,
  true, false, false, //X,Y,Z
  false, false, false,//Rx,Ry,Rz
  false, false, false, false, false);
#endif

Gains mygains[2];
EffectParams myeffectparams[2];
int32_t forces[2] = {0};

void setup(){

#if ENABLE_JOYSTICK
    Joystick.setXAxisRange(-AXIS_HALF_RANGE, AXIS_HALF_RANGE);    
    //set X Axis gains
    mygains[0].totalGain = 100;//0-100
    mygains[0].springGain = 100;//0-100
    mygains[0].damperGain = 100;
    mygains[0].inertiaGain = 100;
    mygains[0].frictionGain = 100;    
    //enable gains REQUIRED
    Joystick.setGains(mygains);
    Joystick.EnableAutoCenter(7500, 2000);

    myeffectparams[0].springMaxPosition = AXIS_HALF_RANGE;
    myeffectparams[0].damperMaxVelocity = MAX_RATE;
    myeffectparams[0].inertiaMaxAcceleration = 32767;
    myeffectparams[0].frictionMaxPositionChange = MAX_RATE;
    
    Joystick.begin(false /* Auto send state */);
#endif

    myLS7366.write_mode_register_0(FILTER_1 | DISABLE_INDX | FREE_RUN | QUADRX4);
    myLS7366.write_mode_register_1(NO_FLAGS | EN_CNTR | BYTE_4 );
    myLS7366.clear_counter();
    myLS7366.clear_status_register();
    myLS7366.write_data_register(4);    

    pos_filter.reset(0);

    setupPWM16(ICR);
    analogWrite16(PWM_PIN, 0);
    pinMode(DIR_PIN, OUTPUT);
    analogWrite16(BRAKE_PIN, 0);
    digitalWrite(LED_PIN, LOW);
    pinMode(LED_PIN, OUTPUT);
}


static float bound(float val, float minv, float maxv)
{
  return min(maxv, max(minv, val));
}

void loop()
{
//  DynamicHID().pidReportHandler.PrintEffect(0);
//  DynamicHID().pidReportHandler.PrintEffect(1);
  
  static unsigned long last_loop_run = micros();

#if ENABLE_SWEEP
  static unsigned long next_speed_change = micros();
  static int16_t current_dc = 0;
  static int16_t current_dc_step = 10;
#endif

  const unsigned long now = micros();
  const float dt_sec = static_cast<long>(now - last_loop_run) * 1e-6f;
  if (dt_sec >= 0.001f)
  {
    last_loop_run = now;
    const long count = myLS7366.read_counter();
    const float pos = count * (-static_cast<float>(AXIS_HALF_RANGE) / COUNT_HALF_RANGE);
    pos_filter.update(pos, dt_sec);
    vel_filter.update(pos_filter.rate(), dt_sec);

//    Serial.println(dt_sec*1e3);

    int16_t axis_pos;
    float hardstop_dc = 0.0f;
    if (pos_filter.output() > AXIS_HALF_RANGE)
    {
      axis_pos = AXIS_HALF_RANGE;
      hardstop_dc = max(-HARDSTOP_DC, (AXIS_HALF_RANGE - pos_filter.output()) * (HARDSTOP_DC / HARDSTOP_FADE));
    }
    else if (pos_filter.output() < -AXIS_HALF_RANGE)
    {
      axis_pos = -AXIS_HALF_RANGE;
      hardstop_dc = min(HARDSTOP_DC, (-AXIS_HALF_RANGE - pos_filter.output()) * (HARDSTOP_DC / HARDSTOP_FADE));
    }
    else
    {
      axis_pos = round(pos_filter.output());
    }

    float feedback_dc = 0.0f;
#if ENABLE_JOYSTICK    
    myeffectparams[0].springPosition = axis_pos;
    myeffectparams[0].damperVelocity = bound(pos_filter.rate(), -MAX_RATE, MAX_RATE);
    myeffectparams[0].inertiaAcceleration = bound(vel_filter.rate() * 0.1f, -32767, 32767);
    myeffectparams[0].frictionPositionChange = myeffectparams[0].damperVelocity;
    Joystick.setEffectParams(myeffectparams);
    Joystick.getForce(forces);
    Joystick.setXAxis(axis_pos);
    Joystick.sendState();
    digitalWrite(LED_PIN, forces[0] >= 255 || forces[0] <= -255);
    feedback_dc = forces[0] * (FEEDBACK_DC / 255);
#endif

#if ENABLE_SWEEP
    const long micros_until_sweep = next_speed_change - now;
    if (micros_until_sweep <= 0)
    {
      Serial.print(current_dc);
      Serial.print(", ");
      Serial.println(pos_filter.rate());
      current_dc += current_dc_step;
      if (current_dc >= ICR || current_dc <= -ICR)
      {
        current_dc_step = -current_dc_step;
      }
      digitalWrite(DIR_PIN, current_dc < 0);
      analogWrite16(PWM_PIN, current_dc >= 0 ? current_dc : -current_dc);
      next_speed_change += 1000000;
    }
#else
    const float feed_forward_duty_cycle = pos_filter.rate() * (ICR / MAX_RATE);
    const float duty_cycle = bound(feed_forward_duty_cycle + hardstop_dc + feedback_dc, -ICR, ICR);
    float brake_duty_cycle = 0.0f;
    if ((duty_cycle > 0 && duty_cycle < feed_forward_duty_cycle) ||
        (duty_cycle < 0 && duty_cycle > feed_forward_duty_cycle))
    {
       // We're regen braking. We need to turn on the brake resistor.
       brake_duty_cycle = bound(BRAKE_MULTIPLIER * sqrtf(feed_forward_duty_cycle * duty_cycle - duty_cycle * duty_cycle), 0, ICR);
    }    
    digitalWrite(DIR_PIN, duty_cycle < 0);
    analogWrite16(PWM_PIN, duty_cycle >= 0 ? duty_cycle : -duty_cycle);
    analogWrite16(BRAKE_PIN, brake_duty_cycle);
#endif

    static float max_accel = 0.0f;
    if (fabsf(vel_filter.rate()) > max_accel)
    {
      max_accel = fabsf(vel_filter.rate());
    }
//    Serial.println(max_accel);

#if 0
    Serial.print("now: ");
    Serial.println(now);
    Serial.print("dt_ms: ");
    Serial.println(dt_sec * 1e3f);
    Serial.print("count: ");
    Serial.println(count);
    Serial.print("filtered pos: ");
    Serial.println(pos_filter.output());
    Serial.print("filtered rate: ");
    Serial.println(pos_filter.rate());
    Serial.print("axis_pos: ");
    Serial.println(axis_pos);
    Serial.print("force: ");
    Serial.println(forces[0]);
#endif
  }
}

@gaurav2827
Copy link
Author

Wow this is intense! Guys i just want a simple sketch to make my flight sim yoke auto centre. Nothing more.

I have x and y axis for roll and pitch of flight stick, both connected to two dc motors with full h bridge. I can give pwm signal to motors and it moves within those commands if programmed without this library, i am just unable to use this library to auto centre my yoke using the signals from pots connected to the range of yoke. My yoke has physical end switches so no need to add a program for hard end stops...can someone plz help what i am missing from the example code. I used the same code to pass the DIRECTION and PWM to h bridge, but the motor simply doesn't move

@sgtnoodle
Copy link

sgtnoodle commented Aug 11, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants