STM32f446RE, hall sensors two motor, one MCU control problem

Hello everyone!

First post on the forum so any advice is welcome.
I am trying to use a STM32f446re to control two BLDC’s (chinese hoverboard) motors with hall sensors.
I already succeeded in doing this on a STM32f103c8 MCU and this seemed to work good enough, but now I have been having trouble running both motors at the same time.
I have been switching pins around all week but the problems are:

  • one works and the other one doesn’t depending on which one gets declared first.
  • and a side problem: one of the motors usually, when using different pins then in my current code, does not want to turn by itself, it only turns in jerky motions when assisted.

If anyone has some good tips(also regarding the post itself, I am open to suggestions)

Here is the code I use:

#include <Arduino.h>
#include <SimpleFOC.h>
#include <HardwareSerial.h>

//HardwareSerial Serial(PB11, PB10); G = B11 W = B10
//HardwareSerial Serial(D0, D1);

#define pp 15

//connections with drv8302
#define EN_GATE PA8 //red
#define M_PWM PB15 //orange
#define M_OC PB6 //purple
#define OC_ADJ PB13 //brown

#define EN_GATE2 PA12 //red
#define M_PWM2 PA11 //orange
#define M_OC2 PB12 //purple
#define OC_ADJ2 PB13 //brown

//motor pins to drv8302
#define INH_A PA15
#define INH_B PB2
#define INH_C PB10

#define INH_A2 PC6 //PWM_A
#define INH_B2 PB5 //PWM_B
#define INH_C2 PC8 //PWM_C

//define hall pins A to C YGB !one of the connectors is wrong order!
#define hallA PA0
#define hallB PA1
#define hallC PA4

#define hallA2 PB4
#define hallB2 PC1
#define hallC2 PC0

//#define interval 5000

//motor
BLDCMotor motor = BLDCMotor(pp);
BLDCMotor motor2 = BLDCMotor(pp);

//driver
BLDCDriver3PWM driver = BLDCDriver3PWM(INH_A, INH_B, INH_C, EN_GATE);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(INH_A2, INH_B2, INH_C2, EN_GATE2);

//hallsensor setup
HallSensor sensor = HallSensor(hallA, hallB , hallC , pp);

  void doA(){sensor.handleA();}
  void doB(){sensor.handleB();}
  void doC(){sensor.handleC();}

HallSensor sensor2 = HallSensor(hallA2, hallB2 , hallC2 , pp);
//create pointers to channel interrupt functions#define hallA2 A2
  void doA2(){sensor2.handleA();}
  void doB2(){sensor2.handleB();}
  void doC2(){sensor2.handleC();}



String inString = "";

void readString();

void setup() {

  Serial.begin(115200);
  SimpleFOCDebug::enable(&Serial);
  
  delay(5000);

  sensor.pullup = Pullup::USE_EXTERN; //2k2 ohm
  sensor2.pullup = Pullup::USE_EXTERN; //2k2 ohm

  pinMode(hallA,INPUT);
  pinMode(hallB,INPUT);
  pinMode(hallC,INPUT);

  pinMode(hallA2,INPUT);
  pinMode(hallB2,INPUT);
  pinMode(hallC2,INPUT);

  //motor1
  pinMode(M_OC,OUTPUT);
  digitalWrite(M_OC,LOW);

  pinMode(M_PWM,OUTPUT);
  digitalWrite(M_PWM,HIGH);

  pinMode(OC_ADJ,OUTPUT);
  digitalWrite(OC_ADJ,HIGH);

  //motor2
  pinMode(M_OC2,OUTPUT);
  digitalWrite(M_OC2,LOW);

  pinMode(M_PWM2,OUTPUT);
  digitalWrite(M_PWM2,HIGH);

  pinMode(OC_ADJ2,OUTPUT);
  digitalWrite(OC_ADJ2,HIGH);


  driver.voltage_power_supply = 30;
  driver.voltage_limit = 30;  
  driver.init();
  motor.linkDriver(&driver);
  motor.voltage_sensor_align = 1; //3

  driver2.voltage_power_supply = 30;
  driver2.voltage_limit = 30;
  driver2.init();
  motor2.linkDriver(&driver2);
  motor2.voltage_sensor_align = 1; 

  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
  motor2.foc_modulation = FOCModulationType::SpaceVectorPWM;

  motor.controller = MotionControlType::velocity;
  motor2.controller = MotionControlType::velocity;

  motor.PID_velocity.P = 1; //0.2 //1
  motor.PID_velocity.I = 3.5; //20 //3.5 //6  
  // motor.PID_velocity.D = 0.0001; //0.0001
  motor.LPF_velocity.Tf = 0.2; //0.2

  motor2.PID_velocity.P = 1; //0.2
  motor2.PID_velocity.I = 3.5; //20
  // motor2.PID_velocity.D = 0.0001; //0.001
  motor2.LPF_velocity.Tf = 0.2; //0,1

  motor.voltage_limit = 2;//30
  motor.velocity_limit = 30;

  motor2.voltage_limit = 2;
  motor2.velocity_limit = 30;

  //monitoring port
  motor.useMonitoring(Serial);
  motor.monitor_variables = _MON_VEL; //| _MON_VOLT_Q

  motor2.useMonitoring(Serial); 
  motor2.monitor_variables =   _MON_VEL;  //| _MON_VOLT_Q 

  driver.pwm_frequency = 20000; //20000
  driver2.pwm_frequency = 20000;

  motor.PID_velocity.output_ramp = 60;
  motor2.PID_velocity.output_ramp = 60;//1000

  sensor.init();
  delay(100);
  sensor.enableInterrupts(doA,doB,doC);
  delay(100);
  motor.linkSensor(&sensor);
  delay(100);
  motor.init();
  delay(100);
  motor.initFOC();
  delay(100);
  motor.disable();

  _delay(1000);

  sensor2.init();
  delay(100);
  sensor2.enableInterrupts(doA2,doB2,doC2);
  delay(100); 
  motor2.linkSensor(&sensor2);
  delay(100);
  motor2.init();
  delay(100);
  motor2.initFOC();
  delay(100);
  motor.enable();
  _delay(1000);


}

int i = 1;
float target1 = 0.0;
float target2 = 0.0;

void loop() {  
  // FOC algorithm function
  readString();
  
  motor.loopFOC();
  motor.move(target1); 
  motor2.loopFOC();   
  motor2.move(target2);

  //motor.monitor();
  //motor2.monitor2();

}

void readString() {
   if (Serial.available()) {

    inString = Serial.readStringUntil('\n');

    if (inString.length() > 0) {
      int v1Index = inString.indexOf(",");
      if (v1Index != -1 ) {
         String v1Value = inString.substring(0 , v1Index);
         String v2Value = inString.substring(v1Index + 1);
         target1 = v1Value.toFloat();
         target2 = v2Value.toFloat();

    }
  }
}
}


Hi,
AFAIK, simpleFOC doesn’t work with two hall sensor definitions (yet)?
You could try to strip down your code to sensors_only and rotate motors by hand, to monitor if that works.

It should work with 2 hall sensors.
What is not supported on stm32 is 2 low side current sensing.

Well, I forgot to mention it, But I tried this and similarly to running the motors only the sensor declared last works properly. But I think @Candas1 is correct, I have seen some example code on here that is supposed to work with two hall sensors. Could it be a pin problem? I am not that familiar with these boards, I read that all pins can do interrupts but maybe there is some sort of conflict?

Hey @Peteh,

Did I understand correctly, 2x motors + 2x Hall sensors setup worked on f1 chip but not on f4 chip?

Hi @Antun_Skuric

I just came back from some testing and found the solution! It was a interrupt pin problem that had to do with there only being 16 external interrupt lines that were being multiplexed.

So in short for people with the same problem: External interrupt pins are grouped together by number for example: PA0, PB0 and PC0 all belong to the same group (external interrupt line) and if you use PA0 as an external interrupt you cannot use PB0, PC0,… as external interrupt pins anymore. You can still use them for other purposes! It is explained in more detail on this website I found: https://embedded-lab.com/blog/stm32-external-interrupt/

Thank you everyone for the fast responses!

I updated my code if anyone is interested in the pins I used:

#include <Arduino.h>
#include <SimpleFOC.h>
#include <HardwareSerial.h>

//HardwareSerial Serial(PB11, PB10); G = B11 W = B10
//HardwareSerial Serial(D0, D1);

#define pp 15

//connections with drv8302
#define M_OC PC4 //purple
#define M_PWM PB13 //orange
#define OC_ADJ PB14 //brown
#define EN_GATE PB15 //red

#define M_OC2 PB1 //purple
#define M_PWM2 PB12 //orange
#define OC_ADJ2 PA11 //brown
#define EN_GATE2 PA12 //red

//motor pins to drv8302
#define INH_A PA15
#define INH_B PB2
#define INH_C PB10

#define INH_A2 PC6 //PWM_A
#define INH_B2 PB5 //PWM_B
#define INH_C2 PC8 //PWM_C

//define hall pins A to C YGB beware of multiplexing of interrup lines
#define hallA PA9
#define hallB PA8
#define hallC PA10

#define hallA2 PB4
#define hallB2 PC7
#define hallC2 PB6

//#define interval 5000

//motor
BLDCMotor motor = BLDCMotor(pp);
BLDCMotor motor2 = BLDCMotor(pp);

//driver
BLDCDriver3PWM driver = BLDCDriver3PWM(INH_A, INH_B, INH_C, EN_GATE);
BLDCDriver3PWM driver2 = BLDCDriver3PWM(INH_A2, INH_B2, INH_C2, EN_GATE2);

//hallsensor setup
HallSensor sensor = HallSensor(hallA, hallB , hallC , pp);

  void doA(){sensor.handleA();}
  void doB(){sensor.handleB();}
  void doC(){sensor.handleC();}

HallSensor sensor2 = HallSensor(hallA2, hallB2 , hallC2 , pp);
//create pointers to channel interrupt functions#define hallA2 A2
  void doA2(){sensor2.handleA();}
  void doB2(){sensor2.handleB();}
  void doC2(){sensor2.handleC();}



String inString = "";

void readString();

void setup() {

  Serial.begin(115200);
  SimpleFOCDebug::enable(&Serial);
  
  delay(5000);

  sensor.pullup = Pullup::USE_EXTERN; //2k2 ohm
  sensor2.pullup = Pullup::USE_EXTERN; //2k2 ohm

  // pinMode(hallA,INPUT);
  // pinMode(hallB,INPUT);
  // pinMode(hallC,INPUT);

  // pinMode(hallA2,INPUT);
  // pinMode(hallB2,INPUT);
  // pinMode(hallC2,INPUT);

  //motor1
  pinMode(M_OC,OUTPUT);
  digitalWrite(M_OC,LOW);

  pinMode(M_PWM,OUTPUT);
  digitalWrite(M_PWM,HIGH);

  pinMode(OC_ADJ,OUTPUT);
  digitalWrite(OC_ADJ,HIGH);

  //motor2
  pinMode(M_OC2,OUTPUT);
  digitalWrite(M_OC2,LOW);

  pinMode(M_PWM2,OUTPUT);
  digitalWrite(M_PWM2,HIGH);

  pinMode(OC_ADJ2,OUTPUT);
  digitalWrite(OC_ADJ2,HIGH);


  driver.voltage_power_supply = 30;
  driver.voltage_limit = 30;  
  driver.init();
  motor.linkDriver(&driver);
  motor.voltage_sensor_align = 1; //3

  driver2.voltage_power_supply = 30;
  driver2.voltage_limit = 30;
  driver2.init();
  motor2.linkDriver(&driver2);
  motor2.voltage_sensor_align = 1; 

  motor.foc_modulation = FOCModulationType::SpaceVectorPWM;
  motor2.foc_modulation = FOCModulationType::SpaceVectorPWM;

  motor.controller = MotionControlType::velocity;
  motor2.controller = MotionControlType::velocity;

  motor.PID_velocity.P = 1; //0.2 //1
  motor.PID_velocity.I = 3.5; //20 //3.5 //6  
  // motor.PID_velocity.D = 0.0001; //0.0001
  motor.LPF_velocity.Tf = 0.2; //0.2

  motor2.PID_velocity.P = 1; //0.2
  motor2.PID_velocity.I = 3.5; //20
  // motor2.PID_velocity.D = 0.0001; //0.001
  motor2.LPF_velocity.Tf = 0.2; //0,1

  motor.voltage_limit = 2;//30
  motor.velocity_limit = 30;

  motor2.voltage_limit = 2;
  motor2.velocity_limit = 30;

  //monitoring port
  motor.useMonitoring(Serial);
  motor.monitor_variables = _MON_VEL; //| _MON_VOLT_Q

  motor2.useMonitoring(Serial); 
  motor2.monitor_variables =   _MON_VEL;  //| _MON_VOLT_Q 

  driver.pwm_frequency = 20000; //20000
  driver2.pwm_frequency = 20000;

  motor.PID_velocity.output_ramp = 60;
  motor2.PID_velocity.output_ramp = 60;//1000

  sensor.init();
  delay(100);
  sensor.enableInterrupts(doA,doB,doC);
  delay(100);
  motor.linkSensor(&sensor);
  delay(100);
  motor.init();
  delay(100);
  motor.initFOC();
  delay(100);
  motor.disable();

  _delay(1000);

  sensor2.init();
  delay(100);
  sensor2.enableInterrupts(doA2,doB2,doC2);
  delay(100); 
  motor2.linkSensor(&sensor2);
  delay(100);
  motor2.init();
  delay(100);
  motor2.initFOC();
  delay(100);
  motor.enable();
  _delay(1000);


}

int i = 1;
float target1 = 0.0;
float target2 = 0.0;

void loop() {  
  // FOC algorithm function
  readString();
  
  motor.loopFOC();
  motor.move(target1); 
  motor2.loopFOC();   
  motor2.move(target2);

  motor.monitor();
  //motor2.monitor2();

}

void readString() {
   if (Serial.available()) {

    inString = Serial.readStringUntil('\n');

    if (inString.length() > 0) {
      int v1Index = inString.indexOf(",");
      if (v1Index != -1 ) {
         String v1Value = inString.substring(0 , v1Index);
         String v2Value = inString.substring(v1Index + 1);
         target1 = v1Value.toFloat();
         target2 = v2Value.toFloat();

    }
  }
}
}


2 Likes

Nice!
Thanks for this, this is gonna help a lot of people!

1 Like

Hello @Peteh
I am working on a similar project for controlling 3 BLDC motor using Simple FOC with STM32G473 microcontroller. I am not a software expert and therefore wanted to know which platform did you use to flash or program simple FOC in your project?
STM32G473 is supported by arduino but I am not sure if there is any way I can program it with Platformio. Coould you please let me know what platform did you use and also wether there is any way I can flash it using platformio.
thanks

Hello @Candas1,
I wanted to know more about why does stm32 cannot support low side current sensing for 2 motors. As microcontrollers like stm32g473 does have necessary hardware like enough number of OPAMPS, that are required to do the job. Thank you in advance.

Simplefoc doesn’t support current sensing and more than 1 motor on stm32.

I am working on a code that will make it possible but it’s not ready yet.

Thanks for your reply @Candas1 .
I have designed and also developed a PCB with stm32g473 microcontroller to control 3 BLDC motors. Somehow, I missed to consider that simpleFOC does not support low side current sensing for more then 1 motor. The PCB is ready now and I cannont change it to ESP32 now, so is there any chance I or any possible modifications that I can make to the code in order to make this project run. Or I have to just use it with BEMF approach?

Try to make simplefoc work in closed loop with all 3 motors and BEMF for now.
And then use current sensing on each motor separately to make sure your design is ok and report back on which Timer and ADC are used for each motor.

I have advance control timer with complementary PWM i.e. TIM1, TIM8 and TIM20. 3 different timers for 3 different motors with 6PWM approach. For current sensing I am using low side current sense for phase U and Phase W. I am using inbuilt OPAMPS for that purpose. As there are 3 motors and I want to sense 2 phases per motor so I have used total 6 inbuilt OPAMPS.

It will be very difficult for SimpleFOC to handle such cases shortherm.
SimpleFOC won’t handle the inbuilt OPAMPS, you will need to initialize it yourself.
You can check how it’s done for the b-g431b-esc1 driver that is board specific.

Also you need to check for each of the pins what ADC it is on for your particular variant in peripheralpins.c

Would it be possible with that approach to use current sense for more then 1 motor?

No, I am telling you even for one motor you need to initialize the Opamps yourself, because this information cannot be retrieved by the library, it’s in comments and in the datasheet.

And if each motor needs several ADC’s for current sampling, it won’t work neither for a single motor.

You have a very specific use case, you should create your own thread and share more details about the pins/timers/ADCs there.