Foc init failed on R4 minima however same code is working on Giga

Hello Foc Community.

I’m using angle control mode through the v2 shield to create a variable resistance lever.
On the Arduino Giga my code works great but the noise is horrible. I found this thread fixing the issue but it isn’t compatible with the shield without jumper wiring to the more optimal timer pins. SimpleFOC on Arduino Giga - BLDC Motor buzzing - #34 by runger

So, I thought id try the R4 Minima as the compatibility hardware chart said it worked for up to 6 pwm (I want to stack thee shields eventually) and i don’t need current sense.
I’m using the dev library as recommended here Arduino Uno r4 (minima and wifi)
However I’m getting an error message;

Setup Started.

—PWM Config—

DRV: pwm pin: 5

DRV: pwm channel: 2

DRV: pwm A/B: B

DRV: pwm freq: 24000

DRV: pwm range: 1000

DRV: pwm clkdiv: 0

—PWM Config—

DRV: pwm pin: 9

DRV: pwm channel: 7

DRV: pwm A/B: B

DRV: pwm freq: 24000

DRV: pwm range: 1000

DRV: pwm clkdiv: 0

—PWM Config—

DRV: pwm pin: 6

DRV: pwm channel: 0

DRV: pwm A/B: B

DRV: pwm freq: 24000

DRV: pwm range: 1000

DRV: pwm clkdiv: 0

DRV: starting timer: 2

DRV: starting timer: 7

DRV: starting timer: 0

DRV: timers started

MOT: Monitor enabled!

MOT: Init

MOT: Enable driver.

MOT: Align sensor.

MOT: Failed to notice movement

MOT: Init FOC failed.

FOC init failed!


Here is my code;

#include <SimpleFOC.h>

float step = 0.001;                   // Step size for increment/decrement
bool increasing = true;         // Direction flag: true = increasing, false = decreasing
//  motor instance
//  driver instance
// BLDCMotor(pole pair number, phase resistance (optional) );
BLDCMotor motor = BLDCMotor(11);
// BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(5, 9, 6, 8);

// sensor instance
Encoder encoder = Encoder(3, 2, 500);
// interrupt routine initialisation
void doA() {
  encoder.handleA();
}
void doB() {
  encoder.handleB();
}


void setup() {

Serial.begin(115200);
SimpleFOCDebug::enable(&Serial);
delay(5000); // connect the serial console within 5s of booting
Serial.println("Setup Started.");

  //  driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 20;
  // limit the maximal dc voltage the driver can set
  // as a protection measure for the low-resistance motors
  // this value is fixed on startup
  driver.voltage_limit = 20;

  // init the sensor
  // initialise encoder hardware

  
  encoder.init();
  // hardware interrupt enable
  encoder.enableInterrupts(doA, doB);


  // link the motor to the sensor
  motor.linkSensor(&encoder);

  // init the driver
  if (!driver.init()) {
    Serial.println("Driver init failed!");
    return;
  }

  // link driver
  motor.linkDriver(&driver);

  //  motor conf
  // limiting motor movements
  // limit the voltage to be set to the motor
  // start very low for high resistance motors
  // current = voltage / resistance, so try to be well under 1Amp
  motor.voltage_limit = 3.0;  // [V]


  // set motion control loop to be used
  motor.torque_controller = TorqueControlType::voltage;
  motor.controller = MotionControlType::angle;


  // angle loop controller
  motor.P_angle.P = 20;

  // use monitoring with serial
  // comment out if not needed
  motor.useMonitoring(Serial);

  // initialize motor
  if (!motor.init()) {
    Serial.println("Motor init failed!");
    return;
  }

  
  // align sensor and start FOC
  if (!motor.initFOC()) {
    Serial.println("FOC init failed!");
    return;
  }

  // set the initial motor target
  motor.target = 0.0;  // Rad

   delay(1000);
}

void loop() {



    // Check the direction and update the value
    if (increasing) {

      motor.voltage_limit += step;  // volts
      motor.PID_velocity.limit = motor.voltage_limit;

      if (motor.voltage_limit >= 8.0) {  // Switch to decreasing if max value reached
        increasing = false;
      }
    } else {

      motor.voltage_limit -= step;  // volts
      motor.PID_velocity.limit = motor.voltage_limit;

      if (motor.voltage_limit <= 3.0) {  // Switch to increasing if min value reached
        increasing = true;
      }
    }

  Serial.println(motor.voltage_limit);

  // main FOC algorithm function
  motor.loopFOC();

  // Motion control function
  motor.move();

}

Any advice would be greatly appreciated!
Thank you!

Yes, it’s very unfortunate the Giga pinout doesn’t work for our shield :frowning:

For the Minima, it looks like a sensor issue, the driver is initializing ok, it seems, but then no movement. Often this has to do with the sensor, but of course it could also mean no PWM…

Do you have an oscilloscope or logic analyzer with which you could check the PWM pins?

Is the sensor working well if you use one of our sensor test examples?

Thank you so much for such a quick response!

The sensor is working well in the same set up using the test code.
I don’t have an oscilloscope or logic analyser but I’ve measured the pwm of each channel during start up with another board. If this doesn’t help id be happy to find an oscilloscope and get back to you.

Here are the results;

----------------------------
PWM A: High Time = 0 us, Low Time = 0 us, Duty Cycle = 0.00%
PWM B: High Time = 0 us, Low Time = 0 us, Duty Cycle = 0.00%
PWM C: High Time = 0 us, Low Time = 6831 us, Duty Cycle = 0.00%
-----------------------------
PWM A: High Time = 0 us, Low Time = 15 us, Duty Cycle = 0.00%
PWM B: High Time = 18 us, Low Time = 25 us, Duty Cycle = 41.86%
PWM C: High Time = 18 us, Low Time = 18 us, Duty Cycle = 50.00%
-----------------------------
PWM A: High Time = 24 us, Low Time = 18 us, Duty Cycle = 57.14%
PWM B: High Time = 24 us, Low Time = 18 us, Duty Cycle = 57.14%
PWM C: High Time = 15 us, Low Time = 22 us, Duty Cycle = 40.54%
-----------------------------
PWM A: High Time = 16 us, Low Time = 27 us, Duty Cycle = 37.21%
PWM B: High Time = 27 us, Low Time = 15 us, Duty Cycle = 64.29%
PWM C: High Time = 20 us, Low Time = 22 us, Duty Cycle = 47.62%
-----------------------------
PWM A: High Time = 17 us, Low Time = 26 us, Duty Cycle = 39.53%
PWM B: High Time = 19 us, Low Time = 24 us, Duty Cycle = 44.19%
PWM C: High Time = 21 us, Low Time = 15 us, Duty Cycle = 58.33%
-----------------------------
PWM A: High Time = 25 us, Low Time = 17 us, Duty Cycle = 59.52%
PWM B: High Time = 15 us, Low Time = 28 us, Duty Cycle = 34.88%
PWM C: High Time = 23 us, Low Time = 19 us, Duty Cycle = 54.76%
-----------------------------
PWM A: High Time = 26 us, Low Time = 16 us, Duty Cycle = 61.90%
PWM B: High Time = 15 us, Low Time = 21 us, Duty Cycle = 41.67%
PWM C: High Time = 22 us, Low Time = 20 us, Duty Cycle = 52.38%
-----------------------------
PWM A: High Time = 18 us, Low Time = 25 us, Duty Cycle = 41.86%
PWM B: High Time = 18 us, Low Time = 24 us, Duty Cycle = 42.86%
PWM C: High Time = 28 us, Low Time = 15 us, Duty Cycle = 65.12%
-----------------------------
PWM A: High Time = 16 us, Low Time = 27 us, Duty Cycle = 37.21%
PWM B: High Time = 25 us, Low Time = 16 us, Duty Cycle = 60.98%
PWM C: High Time = 22 us, Low Time = 21 us, Duty Cycle = 51.16%
-----------------------------
PWM A: High Time = 23 us, Low Time = 19 us, Duty Cycle = 54.76%
PWM B: High Time = 25 us, Low Time = 18 us, Duty Cycle = 58.14%
PWM C: High Time = 15 us, Low Time = 27 us, Duty Cycle = 35.71%
-----------------------------
PWM A: High Time = 28 us, Low Time = 15 us, Duty Cycle = 65.12%
PWM B: High Time = 18 us, Low Time = 24 us, Duty Cycle = 42.86%
PWM C: High Time = 18 us, Low Time = 24 us, Duty Cycle = 42.86%
-----------------------------
PWM A: High Time = 0 us, Low Time = 0 us, Duty Cycle = 0.00%
PWM B: High Time = 0 us, Low Time = 0 us, Duty Cycle = 0.00%
PWM C: High Time = 0 us, Low Time = 0 us, Duty Cycle = 0.00%
-----------------------------

This is the code i used to get that out put.

const int pwmA = 5; // Pin for PWM A
const int pwmB = 9; // Pin for PWM B
const int pwmC = 6; // Pin for PWM C

unsigned long highTimeA, lowTimeA, highTimeB, lowTimeB, highTimeC, lowTimeC;
float dutyCycleA, dutyCycleB, dutyCycleC;

void setup() {
  pinMode(pwmA, INPUT);
  pinMode(pwmB, INPUT);
  pinMode(pwmC, INPUT);
  Serial.begin(9600);
}

void loop() {
  // Measure HIGH and LOW times for PWM A
  highTimeA = pulseIn(pwmA, HIGH);
  lowTimeA = pulseIn(pwmA, LOW);

  // Measure HIGH and LOW times for PWM B
  highTimeB = pulseIn(pwmB, HIGH);
  lowTimeB = pulseIn(pwmB, LOW);

  // Measure HIGH and LOW times for PWM C
  highTimeC = pulseIn(pwmC, HIGH);
  lowTimeC = pulseIn(pwmC, LOW);

  // Calculate duty cycle for each PWM signal
  if (highTimeA + lowTimeA > 0) {
    dutyCycleA = (highTimeA * 100.0) / (highTimeA + lowTimeA);
  } else {
    dutyCycleA = 0; // No signal detected
  }

  if (highTimeB + lowTimeB > 0) {
    dutyCycleB = (highTimeB * 100.0) / (highTimeB + lowTimeB);
  } else {
    dutyCycleB = 0; // No signal detected
  }

  if (highTimeC + lowTimeC > 0) {
    dutyCycleC = (highTimeC * 100.0) / (highTimeC + lowTimeC);
  } else {
    dutyCycleC = 0; // No signal detected
  }

  // Print results to Serial Monitor
  Serial.print("PWM A: High Time = ");
  Serial.print(highTimeA);
  Serial.print(" us, Low Time = ");
  Serial.print(lowTimeA);
  Serial.print(" us, Duty Cycle = ");
  Serial.print(dutyCycleA);
  Serial.println("%");

  Serial.print("PWM B: High Time = ");
  Serial.print(highTimeB);
  Serial.print(" us, Low Time = ");
  Serial.print(lowTimeB);
  Serial.print(" us, Duty Cycle = ");
  Serial.print(dutyCycleB);
  Serial.println("%");

  Serial.print("PWM C: High Time = ");
  Serial.print(highTimeC);
  Serial.print(" us, Low Time = ");
  Serial.print(lowTimeC);
  Serial.print(" us, Duty Cycle = ");
  Serial.print(dutyCycleC);
  Serial.println("%");

  Serial.println("-----------------------------");
}

I’t might be worth mentioning that I get the same errors from an arduino uno, but still the giga works. Maybe that helps narrow it down?
MOT: Monitor enabled!
MOT: Init
MOT: Enable driver.
MOT: Align sensor.
MOT: Failed to notice movement
MOT: Init FOC failed.
FOC init failed!

Thank you!

Very ingenious to use the other MCU as your logic analyzer! :star_struck:

This tells us PWM is working and also sensor is working, :slight_smile: but not together in closed loop mode :frowning:

Things to check

  1. First test should be in closed loop torque voltage mode, it’s the simplest one to get working
  2. Make sure the number of pole pairs is configured correctly on the motor
  3. Is the enable pin configured?
  4. Are the voltage limits set high enough to allow movement?
  5. Are the voltage limits set low enough to make sure the driver protections aren’t kicking in?

Thank you!
While testing closed loop torque voltage mode i was having some trouble with the “getting started” driver example to run as expected. The quest led me to the “driver.pwm_frequency =” command. On the R4 it worked from 490hz to 9000hz but it was far more responsive at 490hz (which i think is the default for the R4).
However the noise was still pretty bad.
I thought id push forward and see if i could stack the shields. but upon adapting my code it returned that channel 1 was busy while initiating the second motor. It looked like a conflict between pins 3 and 10.

I hope the use of the driver.pwm_frequency command helps shed some light on what might be the issue!

Here’s the updated code;


#include <SimpleFOC.h>

float step = 0.001;      // Step size for increment/decrement
bool increasing = true;  // Direction flag: true = increasing, false = decreasing
//  motor instance
//  driver instance
// BLDCMotor(pole pair number, phase resistance (optional) );
BLDCMotor motor = BLDCMotor(11);
// BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(5, 9, 6, 8);

// sensor instance
Encoder encoder = Encoder(3, 2, 500);
// interrupt routine initialisation
void doA() {
  encoder.handleA();
}
void doB() {
  encoder.handleB();
}



void setup() {

  Serial.begin(115200);
  SimpleFOCDebug::enable(&Serial);
  delay(1000);  // connect the serial console within 5s of booting
  Serial.println("Setup Started.");
  // pwm frequency to be used [Hz]
  driver.pwm_frequency = 490;
  //  driver config
  // power supply voltage [V]
  driver.voltage_power_supply = 20;
  // limit the maximal dc voltage the driver can set
  // as a protection measure for the low-resistance motors
  // this value is fixed on startup
  driver.voltage_limit = 20;

  // init the sensor
  // initialise encoder hardware
  // init the driver
  if (!driver.init()) {
    Serial.println("Driver init failed!");
    return;
  }

  encoder.init();
  // hardware interrupt enable
  encoder.enableInterrupts(doA, doB);


  // link the motor to the sensor
  motor.linkSensor(&encoder);

  // link driver
  motor.linkDriver(&driver);

  //  motor conf
  // limiting motor movements
  // limit the voltage to be set to the motor
  // start very low for high resistance motors
  // current = voltage / resistance, so try to be well under 1Amp
  motor.voltage_limit = 3;  // [V]


  // set motion control loop to be used
  motor.torque_controller = TorqueControlType::voltage;
  motor.controller = MotionControlType::angle;


  // angle loop controller
  motor.P_angle.P = 20;

  // use monitoring with serial
  // comment out if not needed
  motor.useMonitoring(Serial);

  // initialize motor
  if (!motor.init()) {
    Serial.println("Motor init failed!");
    return;
  }


  // align sensor and start FOC
  if (!motor.initFOC()) {
    Serial.println("FOC init failed!");
    return;
  }

  // set the initial motor target
  motor.target = 0.0;  // Rad

  delay(1000);
}

void loop() {



  // Check the direction and update the value
  if (increasing) {

    motor.voltage_limit += step;  // volts
    motor.PID_velocity.limit = motor.voltage_limit;

    if (motor.voltage_limit >= 8.0) {  // Switch to decreasing if max value reached
      increasing = false;
    }
  } else {

    motor.voltage_limit -= step;  // volts
    motor.PID_velocity.limit = motor.voltage_limit;

    if (motor.voltage_limit <= 3.0) {  // Switch to increasing if min value reached
      increasing = true;
    }
  }

  //Serial.println(motor.voltage_limit);

  // main FOC algorithm function
  motor.loopFOC();

  // Motion control function
  motor.move();
}```

@runger Like you suggested the voltage limits where set too low to allow movement. I needed to change the motor.voltage_sensor_align value not just the motor.voltage_limit as i previously thought. Thank you very much for helping with that!

Now both of my v2 shields are configured and running great individually with the test code and there own pinouts on the same hardware stack (both shields stacked on R4).
However when I try and combine the code to use both at once I run in to an issue. The first motor initialises no problem and runs great but the second shows “DRV: channel in use: 1” on the monitor serial print.
Pin 11 uses timer GTIOC1A and pin 3 uses timer GTIOC1B. Is that why pin 3 is blocked in initialisation?

Below is the Monitor print and my code.
Thank you!

MOT: Enable driver.
MOT: Align sensor.
MOT: sensor_direction==CCW
MOT: PP check: fail - estimated pp: 12.74
MOT: Zero elec. angle: 2.83
MOT: No current sense.
MOT: Ready.
DRV: channel in use: 1
—PWM Config—
DRV: pwm pin: 10
DRV: pwm channel: 3
DRV: pwm A/B: B
DRV: pwm freq: 24000
DRV: pwm range: 1000
DRV: pwm clkdiv: 0
—PWM Config—
DRV: pwm pin: 6
DRV: pwm channel: 0
DRV: pwm A/B: B
DRV: pwm freq: 24000
DRV: pwm range: 1000
DRV: pwm clkdiv: 0
MOT: Monitor enabled!
MOT: Init not possible, driver not initialized
MOT: Align sensor.
MOT: Failed to notice movement
MOT: Init FOC failed.
Motors ready.
Set the targets angle using serial terminal:

Here is the code I’ve used.

#include <SimpleFOC.h>
/////////////////////////////// RIGHT MOTOR
// BLDC motor & driver instance
BLDCMotor motorR = BLDCMotor(11);
BLDCDriver3PWM driverR = BLDCDriver3PWM(5, 9, 11, 8);

// encoder instance
Encoder encoderR = Encoder(12, 2, 500);

// Interrupt routine intialisation
// channel A and B callbacks
void doAR() {
  encoderR.handleA();
}
void doBR() {
  encoderR.handleB();
}

// angle set point variable
float target_angleR = 0;
// instantiate the commander
Commander commandR = Commander(Serial);
void doTargetR(char* cmd) {
  commandR.scalar(&target_angleR, cmd);
}

/////////////////////////////////LEFT MOTOR
// BLDC motor & driver instance
BLDCMotor motorL = BLDCMotor(11);
BLDCDriver3PWM driverL = BLDCDriver3PWM(3, 10, 6, 7);

Encoder encoderL = Encoder(A5, A4, 500);
// interrupt routine initialisation
void doAL() {
  encoderL.handleA();
}
void doBL() {
  encoderL.handleB();
}

// angle set point variable
float target_angleL = 0;
// instantiate the commander
Commander commandL = Commander(Serial);
void doTargetL(char* cmd) {
  commandL.scalar(&target_angleL, cmd);
}
////////////////////////////////////////////

void setup() {

  // use monitoring with serial
  Serial.begin(115200);
  // enable more verbose output for debugging
  // comment out if not needed
  SimpleFOCDebug::enable(&Serial);
  /////////////////////////////// RIGHT MOTOR
  // initialize encoder sensor hardware
  encoderR.init();
  encoderR.enableInterrupts(doAR, doBR);
  // link the motor to the sensor
  motorR.linkSensor(&encoderR);

  // driver config
  // power supply voltage [V]
  driverR.voltage_power_supply = 20;
  driverR.init();
  // link the motor and the driver
  motorR.linkDriver(&driverR);

  // aligning voltage [V]
  motorR.voltage_sensor_align = 7;

  // set motion control loop to be used
  motorR.controller = MotionControlType::angle;

  // angle P controller
  motorR.P_angle.P = 20;
  //  maximal velocity of the position control
  motorR.velocity_limit = 20;

  // comment out if not needed
  motorR.useMonitoring(Serial);

  // initialize motor
  motorR.init();
  // align encoder and start FOC
  motorR.initFOC();

  // add target command T
  commandR.add('R', doTargetR, "target angle");

/////////////////////////////////LEFT MOTOR
  // initialize encoder sensor hardware
  encoderL.init();
  encoderL.enableInterrupts(doAL, doBL);
  // link the motor to the sensor
  motorL.linkSensor(&encoderL);

  // driver config
  // power supply voltage [V]
  driverL.voltage_power_supply = 20;
  driverL.init();
  // link the motor and the driver
  motorL.linkDriver(&driverL);

  // aligning voltage [V]
  motorL.voltage_sensor_align = 7;

  // set motion control loop to be used
  motorL.controller = MotionControlType::angle;

  // angle P controller
  motorL.P_angle.P = 20;
  //  maximal velocity of the position control
  motorL.velocity_limit = 20;

  // comment out if not needed
  motorL.useMonitoring(Serial);

  // initialize motor
  motorL.init();
  // align encoder and start FOC
  motorL.initFOC();

  // add target command T
  commandL.add('L', doTargetL, "target angle");
  /////////////////////////////////////////////

  Serial.println(F("Motors ready."));
  Serial.println(F("Set the targets angle using serial terminal:"));
  delay(1000);
}

void loop() {
  // main FOC algorithm function
  // the faster you run this function the better
  // Arduino UNO loop  ~1kHz
  // Bluepill loop ~10kHz
  motorR.loopFOC();
  motorL.loopFOC();

  // Motion control function
  // velocity, position or voltage (defined in motor.controller)
  // this function can be run at much lower frequency than loopFOC() function
  // You can also use motor.move() and set the motor.target in the code
  motorR.move(target_angleR);
  motorL.move(target_angleL);

  // function intended to be used with serial plotter to monitor motor variables
  // significantly slowing the execution down!!!!
  // motor.monitor();

  // user communication
  commandR.run();
  commandL.run();
}