I want to post another working example for the splitboard controller with the STM32F030 MCU.
The main parameters are 24V@50kHz, velocity mode with voltage-torque control.
This time I took the motor parameters from @OiD-W
I found that the PID values have a very small work-zone. A bit off and the motor runs crazy.
That’s why I added the accel2target ramp up in the main loop. It allows you to watch the motor going through the paces. Enough time to pull the plug when it gets funny
/*
10" hoverboard splitboard with STM32F030C8T6 MCU and hall sensors
*/
#include <SimpleFOC.h> // has to be dev-branch for both libs
#include <SimpleFOCDrivers.h>
#include <encoders/smoothing/SmoothingSensor.h>
// Hall sensor instance
// - hallA, hallB, hallC - HallSensor A, B and C pins
// - pp - pole pairs
HallSensor sensor = HallSensor(PA0, PF1, PC15, 15); // blue, green, yellow, 15PP
// Interrupt routine intialisation
void doA(){sensor.handleA();}
void doB(){sensor.handleB();}
void doC(){sensor.handleC();}
BLDCMotor motor = BLDCMotor(15, 0.75, 19.0, 0.00119); // 0.00036858); 0.75, 19, 0.00119
// BLDCDriver6PWM driver = BLDCDriver6PWM(UH, UL, VH, VL, WH, WL);
BLDCDriver6PWM driver = BLDCDriver6PWM(PA8,PB13,PA9,PB14,PA10,PB15); // blue, green, yellow gate driver pins
// instantiate the smoothing sensor, providing the real sensor as a constructor argument
SmoothingSensor smooth = SmoothingSensor(sensor, motor);
// instantiate the commander
float target_velocity = 0;
float accel2target = 0;
bool LED = true;
void SerialComm()
{
switch(Serial2.read())
{
case '?':
Serial2.print("sensor_direction ");
Serial2.print(motor.sensor_direction);
Serial2.print(", zero_electric_angle ");
Serial2.println(motor.zero_electric_angle);
break;
case 's': Serial2.print("shaft_velocity "); Serial2.println(motor.shaft_velocity); break;
case 'a':
Serial2.print("shaft_angle ");
Serial2.print(motor.shaft_angle);
Serial2.print(", electric_rotations ");
Serial2.print(sensor.electric_rotations);
Serial2.print(", electric_sector ");
Serial2.println(sensor.electric_sector);
break;
case 'T': target_velocity = Serial2.parseFloat(); Serial2.print("Set ");
case 't': Serial2.print("target "); Serial2.println(target_velocity); break;
case 'V': motor.voltage_limit = Serial2.parseFloat(); Serial2.print("Set ");
case 'v': Serial2.print("motor.voltage_limit "); Serial2.println(motor.voltage_limit); break;
case 'O': motor.PID_velocity.output_ramp = Serial2.parseFloat(); Serial2.print("Set ");
case 'o': Serial2.print("motor.PID_velocity.output_ramp "); Serial2.println(motor.PID_velocity.output_ramp); break;
case 'F': motor.LPF_velocity.Tf = Serial2.parseFloat(); Serial2.print("Set ");
case 'f': Serial2.print("motor.LPF_velocity.Tf "); Serial2.println(motor.LPF_velocity.Tf); break;
case 'P': motor.PID_velocity.P = Serial2.parseFloat(); Serial2.print("Set ");
case 'p': Serial2.print("motor.PID_velocity.P "); Serial2.println(motor.PID_velocity.P); break;
case 'I': motor.PID_velocity.I = Serial2.parseFloat(); Serial2.print("Set ");
case 'i': Serial2.print("motor.PID_velocity.I "); Serial2.println(motor.PID_velocity.I); break;
case 'D': motor.PID_velocity.D = Serial2.parseFloat(); Serial2.print("Set ");
case 'd': Serial2.print("motor.PID_velocity.D "); Serial2.println(motor.PID_velocity.D); break;
case 'W': motor.P_angle.P = Serial2.parseFloat(); Serial2.print("Set ");
case 'w': Serial2.print("motor.P_angle.P "); Serial2.println(motor.P_angle.P); break;
// motor.zero_electric_angle
case 'Z': motor.zero_electric_angle = Serial2.parseFloat(); Serial2.print("Set ");
case 'z': Serial2.print("motor.zero_electric_angle "); Serial2.println(motor.zero_electric_angle); break;
case ' ': target_velocity = 0; motor.voltage_limit = 0;
}
}
void setup() {
pinMode(PB9,OUTPUT); // onboard LED for heartbeat signal
// monitoring port
Serial2.begin(115200);
while(!Serial2){};
digitalWrite(PB9,LED);
// initialise encoder hardware
sensor.init();
// hardware interrupt enable
sensor.enableInterrupts(doA, doB, doC);
// set SmoothingSensor phase correction for hall sensors
//smooth.phase_correction = -_PI_6;
motor.linkSensor(&smooth); // link the motor to the smoothing sensor
// driver config
// power supply voltage [V]
driver.pwm_frequency = 50000;
driver.voltage_power_supply =24;
driver.voltage_limit = 24;
driver.dead_zone = 0.1f; // default 0.05f
driver.init();
// link the motor and the driver
motor.linkDriver(&driver);
// aligning voltage [V]
motor.voltage_sensor_align = 6;
// set motion control loop to be used
motor.torque_controller = TorqueControlType::voltage;
// motor.foc_modulation = FOCModulationType::Trapezoid_120;
motor.foc_modulation = FOCModulationType::SinePWM;
// motor.controller = MotionControlType::torque; // set motion control loop to be used
motor.controller = MotionControlType::velocity;
// controller configuration
// default parameters in defaults.h
// velocity low pass filtering time constant
motor.PID_velocity.P = 0.3f;
motor.PID_velocity.I = 0.3f;
motor.LPF_velocity.Tf = 0.04f; // 0.05
// default voltage_power_supply
motor.voltage_limit = 0.5f * driver.voltage_limit;
motor.current_limit = 10;
// jerk control using voltage voltage ramp
// default value is 300 volts per sec ~ 0.3V per millisecond
motor.PID_velocity.output_ramp = 1000;
// comment out if not needed
motor.useMonitoring(Serial2);
// initialize motor
motor.init();
// align sensor and start FOC
motor.zero_electric_angle = 0.00f; // rad
motor.sensor_direction = Direction::CCW; // CW or CCW
motor.initFOC();
_delay(1000);
}
void loop() {
digitalWrite(PB9,LED);
if (LED == true){
LED = false;}
else {LED = true;}
motor.loopFOC();
//motor.monitor();
motor.move(accel2target);
// user communication
if(Serial2.available() > 0){
SerialComm();
};
if (target_velocity != accel2target){
if (target_velocity < accel2target) {accel2target -= 0.01f;}
else {accel2target += 0.01f;}
}
//Serial2.println(motor.shaft_velocity);
}