Arduino Motor Drive Shield V2

Hello,

This PCA9685 based motor shield should be supported. I am trying to use simplefoc in voltage control mode. My goal is to apply lower phase voltage to a stepper to avoid hitting the current limit (the stepper is rated at 3.6V/1.5A and the board is rated for 1.2A only). If thats possible is there an example project which I can use to start? Basically any code example using the Arduino Motor Drive Shield V2 would be great.

Having said that, i know from other threads that PCA9685 is not the best choice and this motor shield has no current or voltage sensor. I just would like to get familiar with the library while waiting for the delivery of my simplefocshield :slight_smile:

Best regards,
Paul

In case anyone comes up across the same problem: I have managed to code a Stepper Driver Class for the Adafruit Motorshield V2 using some basic Methods of the Adafruit_MotorShield library.

Ino-File:

#include "StepperDriverAdafruitMotorshieldV2.h"

//Adafruit Motorshield Port M3 & M4
#define PWMA 2
#define AIN2 3
#define AIN1 4
#define PWMB 7
#define BIN2 6
#define BIN1 5

int in1[] = {AIN1,AIN2};
int in2[] = {BIN1,BIN2};
StepperDriverAdafruitMotorshieldV2 driver=StepperDriverAdafruitMotorshieldV2(PWMA, in1, PWMB,in2);

void setup() {
  // put your setup code here, to run once:
  driver.pwm_frequency = 1000;
  // power supply voltage [V]
  driver.voltage_power_supply = 11;
  // Max DC voltage allowed - default voltage_power_supply
  driver.voltage_limit = 11;
  
  // driver init
  driver.init();

  // enable driver
  driver.enable();

  _delay(1000);
}

void loop() {
  // put your main code here, to run repeatedly:
  driver.setPwm(6,3);
}

StepperDriverAdafruitMotorshieldV2.cpp

#include "StepperDriverAdafruitMotorshieldV2.h"

StepperDriverAdafruitMotorshieldV2::StepperDriverAdafruitMotorshieldV2(int _pwm1, int* _in1, int _pwm2, int* _in2, int en1, int en2){
  // Pin initialization
  pwm1 = _pwm1; // phase 1 pwm pin number
  dir1a = _in1[0]; // phase 1 INA pin number
  dir1b = _in1[1]; // phase 1 INB pin number
  pwm2 = _pwm2; // phase 2 pwm pin number
  dir2a = _in2[0]; // phase 2 INA pin number
  dir2b = _in2[1]; // phase 2 INB pin number

  // enable_pin pin
  enable_pin1 = en1;
  enable_pin2 = en2;

  // default power-supply value
  voltage_power_supply = DEF_POWER_SUPPLY;
  voltage_limit = NOT_SET;
  pwm_frequency = NOT_SET;

  AFMS = Adafruit_MotorShield();

}

// enable motor driver
void  StepperDriverAdafruitMotorshieldV2::enable(){
    // enable_pin the driver - if enable_pin pin available
    if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, HIGH);
    if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, HIGH);
    // set zero to PWM
    setPwm(0,0);
}

// disable motor driver
void StepperDriverAdafruitMotorshieldV2::disable()
{
  // set zero to PWM
  setPwm(0, 0);
  // disable the driver - if enable_pin pin available
  if ( _isset(enable_pin1) ) digitalWrite(enable_pin1, LOW);
  if ( _isset(enable_pin2) ) digitalWrite(enable_pin2, LOW);

}

// init hardware pins
int StepperDriverAdafruitMotorshieldV2::init() {
  AFMS.begin(pwm_frequency);
  return 0;
}

// Set voltage to the pwm pin
void StepperDriverAdafruitMotorshieldV2::setPwm(float Ua, float Ub) {
  float duty_cycle1(0.0f),duty_cycle2(0.0f);
  
  // limit the voltage in driver
  Ua = _constrain(Ua, -voltage_limit, voltage_limit);
  Ub = _constrain(Ub, -voltage_limit, voltage_limit);

  // hardware specific writing
  duty_cycle1 = _constrain(abs(Ua)/voltage_power_supply,0.0f,1.0f);
  duty_cycle2 = _constrain(abs(Ub)/voltage_power_supply,0.0f,1.0f);

  AFMS.setPWM(pwm1,duty_cycle1*4095);  
  if (Ua>0)  {        
    AFMS.setPin(dir1a,LOW);
    AFMS.setPin(dir1b,HIGH);    
  } else {        
    AFMS.setPin(dir1a,HIGH);
    AFMS.setPin(dir1b,LOW);
  }

  AFMS.setPWM(pwm2,duty_cycle2*4095);
  if (Ub>0)  {        
    AFMS.setPin(dir2a,LOW);
    AFMS.setPin(dir2b,HIGH);    
  } else {        
    AFMS.setPin(dir2a,HIGH);
    AFMS.setPin(dir2b,LOW);
  }


}

StepperDriverAdafruitMotorshieldV2.h

#ifndef STEPPER_DRIVER_ADAMOTOV2_h
#define STEPPER_DRIVER_ADAMOTOV2_h
#include <SimpleFOC.h>
#include <Adafruit_MotorShield.h>

class StepperDriverAdafruitMotorshieldV2: public StepperDriver
{
  public:
    StepperDriverAdafruitMotorshieldV2(int pwm1, int* in1, int pwm2, int* in2, int en1 = NOT_SET, int en2 = NOT_SET);
    /**  Motor hardware init function */
  	int init() override;
    /** Motor disable function */
  	void disable() override;
    /** Motor enable function */
    void enable() override;

    // hardware variables
    int pwm1; //!< phase 1 pwm pin number
    int dir1a; //!< phase 1 INA pin number
    int dir1b; //!< phase 1 INB pin number
    int pwm2; //!< phase 2 pwm pin number
    int dir2a; //!< phase 2 INA pin number
    int dir2b; //!< phase 2 INB pin number
    int enable_pin1; //!< enable pin number phase 1
    int enable_pin2; //!< enable pin number phase 2

    // Adafruit Motorshield object
    Adafruit_MotorShield AFMS;

    /** 
     * Set phase voltages to the harware 
     * 
     * @param Ua phase A voltage
     * @param Ub phase B voltage
    */
    void setPwm(float Ua, float Ub) override;

  private:
};

#endif
1 Like

Hey @Paul!

Thanks for sharing, awesome work!
As far as I was able to see from their code the PCA9685 seems to be using the i2c to set the pwm, this is interesting. :smiley:

Maybe with some adaptations we can include it in the drivers repo, what do you think @runger ?

How is the performance of your setup?
What kind of sensor are you using?

The performance is good, but far away from true FOC :). Basically I run a small stepper in openloop position mode, no current sensor or position sensor. Concerning the I2C interface, thats correct. I just did not want to write my own interface and ended up using the methods from Adafruitlibrary. At least I can now set the phase voltage level, which Adafruit library does not support. Using the finished driver with the Motor class was easy. Simplefoc is a great library :+1: