Another turntable project

I need some help here.

I have a Technics direct drive PMSM that I’m driving with an STM32F4. In openloop it runs tolerably well, but speed stability is still well off the 1970’s analog PLL.

This is the openloop code and it starts the motor and I’m able to send T and L commands to the STM32 via the USB

// Open loop motor control example
#include <SimpleFOC.h>


// BLDC motor & driver instance
// BLDCMotor motor = BLDCMotor(pole pair number);
BLDCMotor motor = BLDCMotor(10, 27);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(PA7, PA6, PA5, PA3);   // STM32


// Stepper motor & driver instance
//StepperMotor motor = StepperMotor(50);
//StepperDriver4PWM driver = StepperDriver4PWM(9, 5, 10, 6,  8);


//target variable
float target_velocity = 0;
float target = 0.0f;
LowPassFilter targetFilter = LowPassFilter(5.4f); // 1s time constant 


// instantiate the commander
Commander command = Commander(Serial);
void doTarget(char* cmd) { command.scalar(&target_velocity, cmd); }
void doLimit(char* cmd) { command.scalar(&motor.voltage_limit, cmd); }

void setup() {

  // 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 = 6.0;
  driver.init();
  // link the motor and the driver
  motor.linkDriver(&driver);

  // 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.current_limit = 0.5;   // [Amps]
 
  // open loop control config
  motor.controller = MotionControlType::velocity_openloop;

  // init motor hardware
  motor.init();

  // add target command T
  command.add('T', doTarget, "target velocity");
  command.add('L', doLimit, "voltage limit");

  Serial.begin(115200);
  Serial.println("Motor ready!");
  Serial.println("Set target velocity [rad/s]");
  Serial.println("Set voltage limit");
  _delay(1000);


}

void loop() {

  // open loop velocity movement
  // using motor.voltage_limit and motor.velocity_limit
  // to turn the motor "backwards", just set a negative target_velocity 
  // command (T 3.4903) 33.33rpm = 3.48rad/s on test SP10

  motor.move(targetFilter(target_velocity));

  // user communication
  command.run();
}

My issue is if the platter is disturbed it takes more than 30seconds for it to stabilize again. I’m looking to close the loop and add a PID.

My first issue the sensorless code will not send serial commands to the STM32 like the previous code does via the USB.

// Sensorless FOC for STM32

#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include "encoders/MXLEMMING_observer/MXLEMMINGObserverSensor.h"

// Stepper motor & BLDC driver instance
BLDCMotor motor = BLDCMotor(10, 27);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
 BLDCDriver3PWM driver = BLDCDriver3PWM(PA7, PA6, PA5, PA3);   // STM32


// MXLEMMING observer sensor instance
MXLEMMINGObserverSensor observer = MXLEMMINGObserverSensor(motor);

// inline current sensor instance
// ACS712-05B has the resolution of 0.185mV per Amp
LowsideCurrentSense current_sense = LowsideCurrentSense(185.0f, PA1, PA2);

// commander communication instance
Commander command = Commander(Serial);
void doMotor(char* cmd){ command.motor(&motor, cmd); }

void setup() {

  // use monitoring with serial 
  Serial.begin(115200);
  // enable more verbose output for debugging
  // comment out if not needed
  SimpleFOCDebug::enable(&Serial);

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

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

  // set control loop type to be used
  motor.controller = MotionControlType::velocity;
  motor.torque_controller = TorqueControlType::foc_current;

  // current sense init and linking
  current_sense.init();
  motor.linkCurrentSense(&current_sense);

  // initialise motor
  motor.init();
  // skip the sensor alignment
  motor.sensor_direction= Direction::CW;
  motor.zero_electric_angle = 0;
  motor.initFOC();


  // subscribe motor to the commander
  command.add('M', doMotor, "motor");
  
  // Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
  Serial.println("Motor ready.");

  _delay(1000);
}


void loop() {
  // iterative setting FOC phase voltage
  motor.loopFOC();

  // iterative function setting the outter loop target
  motor.move();

  // motor monitoring
  motor.monitor();

  // user communication
  command.run();
}

I’ve solved my issue.

STM32F401CDU / Windows 10

Programming via STLink V2. Once code is loaded I connect the USB to the PC and select the correct com port in my case it’s Com5. You also need to ground pin A10.

Open Device manager Properties / Port Settings and set the bit rate as per the rate in the code 115200. You need to plug the USB in. In the IDE select Com5 (in my case) then reboot the STM32 so the Serial.println prints the text in the serial monitor.

I also found in the Arduino IDE when loading the sketch I needed to set Com5 back to Com1 in Tools / Port otherwise I got a com port error when uploading every time.

A win, I now have the motor spinning with sensorless closed loop. Stability is better than openloop and recovery after platter disturbance is now about 3 seconds. This is looking promising.

I have searched for a couple of hours to no avail to be able to control motor speed and ramp up.

The motor needs a helping hand to start and to control speed the only way is for me to vary the input power supply voltage.

First question is why the M commands are not working? I thought M3.49 0.5 should set speed to 3.49rad/s and 0.5A current.

The serial monitor prints motor alignment Motor Ready etc and echos the M command data.

// Open loop motor control example
// NOTE; Ground Pin PA10 to get DFU and Serial commandT working
/* This code is working 
To get Serial print Ln to display in the Srial Monitor
1 - Ensure Com1 is selected in Tools/Port IDE
2 - Plug USB into STLink and load Code
3 - Ground Pin A10
4 - Plug USBC into PC and in Device Manager select the Com port assigned to the STM32 in this case it was Com5 set bit rate to 115200
5 - Select Com5 in Arduino IDE and reboot STM32 */ 
// Sensorless FOC for STM32

#include <SimpleFOC.h>
#include <SimpleFOCDrivers.h>
#include "encoders/MXLEMMING_observer/MXLEMMINGObserverSensor.h"

// Stepper motor & BLDC driver instance
BLDCMotor motor = BLDCMotor(10, 7.26, 17, 0.016);
// BLDCDriver3PWM driver = BLDCDriver3PWM(pwmA, pwmB, pwmC, Enable(optional));
BLDCDriver3PWM driver = BLDCDriver3PWM(PA6, PA7, PA5, PA3);   // STM32


// MXLEMMING observer sensor instance
MXLEMMINGObserverSensor observer = MXLEMMINGObserverSensor(motor);

// inline current sensor instance
// ACS712-05B has the resolution of 0.185mV per Amp
LowsideCurrentSense current_sense = LowsideCurrentSense(185.0f, PA1, PA2);


// commander communication instance
Commander command = Commander(Serial);
void doMotor(char* cmd){ command.motor(&motor, cmd); }


void setup() {

  // use monitoring with serial 
  Serial.begin(115200);
  // enable more verbose output for debugging
  // comment out if not needed
  SimpleFOCDebug::enable(&Serial);

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

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

  // current = voltage / resistance, so try to be well under 1Amp
  motor.current_limit = 0.5;   // [Amps]

  // set control loop type to be used
  motor.controller = MotionControlType::velocity;
  motor.torque_controller = TorqueControlType::foc_current;

  // current sense init and linking
  current_sense.init();
  motor.linkCurrentSense(&current_sense);

  // initialise motor
  motor.init();
  // skip the sensor alignment
  motor.sensor_direction= Direction::CW;
  motor.zero_electric_angle = 0;
  motor.initFOC();


  // subscribe motor to the commander
  command.add('M', doMotor, "motor");

  
  // Run user commands to configure and the motor (find the full command list in docs.simplefoc.com)
  Serial.println("Motor ready.");

  _delay(1000);
}


void loop() {
  // iterative setting FOC phase voltage
  motor.loopFOC();

  // iterative function setting the outter loop target
  motor.move();

  // motor monitoring
  motor.monitor();

  // user communication
  command.run();
}