Motor intialisation succses is depedent on UART baudrate

With the B-G431B-ESC1 driver, If I increase baudrate above 500000 bps, the ADC readings report close to 0 values. The serial output works, but the motor initialization fails:(I’ve added current logging during current sense alignment routine)

At baudrate of 500k:
11:27:53.991 → MOT: Init
11:27:54.475 → MOT: Enable driver.
11:27:54.992 → MOT: Align sensor.
11:27:57.184 → MOT: sensor_direction==CCW
11:27:57.184 → MOT: PP check: OK!
11:27:57.896 → MOT: Zero elec. angle: 3.20
11:27:58.089 → MOT: Align current sense.
11:27:59.186 → Ia: 1.05
11:27:59.186 → Ib: -0.63
11:27:59.186 → Ic: -0.57
11:28:00.284 → MOT: Success: 1
11:28:00.284 → MOT: Ready.
11:28:00.284 → Motor ready.

At 1M: (fail most of the time)
11:31:48.069 → MOT: Init
11:31:48.582 → MOT: Enable driver.
11:31:49.066 → MOT: Align sensor.
11:31:51.288 → MOT: sensor_direction==CCW
11:31:51.288 → MOT: PP check: OK!
11:31:51.994 → MOT: Zero elec. angle: 0.00
11:31:52.186 → MOT: Align current sense.
11:31:53.282 → Ia: -0.01
11:31:53.282 → Ib: -0.03
11:31:53.282 → Ic: -0.05
11:31:53.282 → CS: Err too low current, rise voltage!
11:31:53.282 → MOT: Align error!
11:31:53.282 → MOT: Init FOC failed.
11:31:53.282 → Motor ready.

I was suspecting a timing issue that, if the code is run too quickly, would prevent ADC to properly initialize, or something else, so I try to disable SimpleFOCDebug::enable(&Serial); and sure enough, the motor never get initialized.

I’ll try to add delays in the FOC internal initialization function to narrow this down, but I was curious to know if someone have an idea about this.

Also note that at the moment I’m using interupt based ABI encoder driver.

Here is my code

void setup() {
// use monitoring with serial
Serial.begin(115200);
// enable more verbose output for debugging
// comment out if not needed
SimpleFOCDebug::enable(&Serial);
encoder.init();
encoder.enableInterrupts(doA, doB);
motor.linkSensor(&encoder);
driver.voltage_power_supply = 16;  // power supply voltage \[V\]
driver.init();

motor.linkDriver(&driver);

currentSense.linkDriver(&driver);
currentSense.init();
motor.linkCurrentSense(&currentSense);

motor.voltage_sensor_align = 1;

motor.torque_controller = TorqueControlType::foc_current;
motor.controller = MotionControlType::torque;

motor.PID_current_q.P = 2.;
motor.PID_current_q.I = 1000.0;
motor.PID_current_d.P = 2.;
motor.PID_current_d.I = 1000.0;

motor.current_limit = 3.0;//A
motor.voltage_limit = 4.0;//V

//motor.useMonitoring(Serial);

// initialize motor
motor.init();
// align encoder and start FOC
motor.initFOC();
// add target command T
command.add(‘T’, doTarget, “target angle”);

Serial.println(F(“Motor ready.”));
Serial.println(F(“Set the target angle using serial terminal:”));
\_delay(10000);
}

If I hack the debug print macro in SimpleFOCDebug.h to add a delay,
My motor pass the initialization at high baudrate (2M);
This confirms a timing issue / raise condition somewhere.


/*
#define SIMPLEFOC_DEBUG(msg, ...) \
    SimpleFOCDebug::println(F(msg), ##__VA_ARGS__)
*/

#define SIMPLEFOC_DEBUG(msg, ...)        \
    do {                                 \
        SimpleFOCDebug::println(F(msg), ##__VA_ARGS__); \
        delay(10);                       \
    } while (0)

Thank you very much for reporting this. I understand your description very well, but I am confused what the reason can be…

I’ve run SimpleFOC with very high serial baud rates on STM32 and did not run into this problem… but I’ve never tried it on the B-G431.

the first thing that came to mind is that increasing the serial speed reconfigures the clocks in some way that the ADC does not like… but the fact that you can remove the problem by adding the delay doesn’t fit with the misconfigured clock explanation. So this sounds more like some kind of race condition, like you say, but I can’t see it at all at the moment…

If you find out any more about it, we would be very grateful for your report :slight_smile:

Hi, I’ve spent some hours trying to figure this out today without much luck.

I’ve switched to hardware based encoder to be sure that it wasn’t related to something with interrupt, but I still had the same behavior, so definitely something else.

Then I wanted to check if the problem was from the ADC or the PWM.
In alignBLDCDriver, I’ve increased the number of current samples to have the time to check that one coil was indeed energized (PhaseCurrent_s c_a = readAverageCurrents(1000);), and yes it was the case, so the PWM was active during the current sampling;

I’ll try to enter more in the ADC code and report here.

I want to check that the samples are really taken when the low side mosfets are closed, since I do get values close to zero but not exactly zero, so the ADC is doing something.

Best,
Thomas.

Hi, I did some more tests:

It seems that a simple delay long enough is not a fix. The timing is quite sensitive, with a repeating pattern.

To get reliable results, I had to compile without any optimization (-O0).

I have serial debug enabled at 1Mbps.

I’m adding a delay in microseconds between driver.init(); and motor.linkDriver(&driver);

Here is the pattern

/*
0 fail
1  fail
15 fail
20 fail
25 fail
30 pass
40 pass
50 pass
55 pass
60 fail
70 fail
80 fail
90 fail
95 pass
100 pass
110 pass
120 fail
130 fail
140 fail
145 pass
150 pass
160 pass
170 fail
180 fail
190 fail
195 pass
200 pass
210 pass
220 fail
230 fail
240 fail
250 fail
255 fail
260 pass
270 pass
280 pass
290 fail
*/

Here is the setup code:

void setup() {
  _delay(2000);
  #ifdef USE_HW_ENC
    encoder._pinA = PB_6;
    encoder._pinB = PB_7_ALT1;
  #else
    encoder.enableInterrupts(doA, doB); 
  #endif

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

  encoder.init();
  
  motor.linkSensor(&encoder);
  driver.voltage_power_supply = 16;  // power supply voltage [V]
  driver.init();
  delayMicroseconds(255); // Depending on this delay, the current sensor alignment will pass ot fail (see list below)
  motor.linkDriver(&driver);
  currentSense.linkDriver(&driver);
  currentSense.init();

  motor.linkCurrentSense(&currentSense);
  motor.voltage_sensor_align = 1;
  motor.torque_controller = TorqueControlType::foc_current;
  motor.controller = MotionControlType::torque;

  motor.PID_current_q.P = 2.;
  motor.PID_current_q.I = 800.0;
  motor.PID_current_d.P = 2.;
  motor.PID_current_d.I = 1000.0;
  motor.LPF_current_q.Tf = 0.003; 
  motor.LPF_current_d.Tf = 0.003; 
  motor.current_limit = 3.0;//A
  motor.voltage_limit = 4.0;//V

 //motor.useMonitoring(Serial);
  
  // initialize motor
  motor.init();
  // align encoder and start FOC
  motor.initFOC();
  while(1);

Hi, Ok another update:

It seems that the synchronization of the ADC is broken.

I’ve added short callback to toogle a spare GPIO on ADC2 HAL_ADC_ConvCpltCallback:

#define SCOPE_GPIO_Port GPIOC
#define SCOPE_Pin       GPIO_PIN_6
#define SCOPE_TOGGLE()  (SCOPE_GPIO_Port->ODR ^= SCOPE_Pin)

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef *hadc){
  if (hadc->Instance == ADC2) {
    SCOPE_TOGGLE();
  }
}

This allows me to look at the PWM waveform and The ADC sampling time on a scope.
(In this experiment, I’ve reduced the PWM frequency to 5kHz to be sure that it was always centered)

Depending on the initialization delays, The ADC seems to be sampled on the High side mid-point of the PWM (when the low side mosfets are not conducting, thus the 0A measurements.

On ‘lucky’ delayed init, the ADC is locked on the Low side, so we can measure current and perform FOC.

I’m pretty sure this come from this section here: Arduino-FOC/src/current_sense/hardware_specific/stm32/b_g431/b_g431_mcu.cpp at f5ac6522995446ee7365e61cd0e4964dea36a5da · simplefoc/Arduino-FOC · GitHub
And especially on this line LL_TIM_SetTriggerOutput(cs_params->timer_handle->Instance, LL_TIM_TRGO_UPDATE); Where the ADC is set up to trigger on the update event

Sorry for the verbose monologue :wink:

I’ll try to investigate more, I’m not very familiar with STM32 peripheral so it might take some time. Is there a doc on the intended PWM and ADC setup for SimpleFOC?

Best,
Thomas

Hey @thomasfla,

Huh this might be a new issue introduced in the v2.3.5.
Could you maybe try the v2.3.4?

The pwm driver is rewritten from 2.3.4 to 2.3.5. I’ve updated the sync on all the stm32 architectures but I’ve just seen that I forgot the bg431 hardware specific code!

Basically these lines:

Should be:

If this is the issues, we will patch this ASAP.
But the easiest way to avoid the issue is to use the v2.3.4.

EDIT:
As a short explanation. In the earlier versions of the library we stopped the timers entirely when syncing the driver and the ADC. But from the v2.3.5 we are only pausing it. So it’s current state can be upcounting or downcounting, while before it was always starting with the up-counting (because it was stopped before). So till 2.3.5 we did not need to verify the counting direction before choosing how to downsample. From v2.3.5 we need to see in which dir did we stop the timer. And the bg431 code was not updated so sometimes it downsamples well (by chance) reading the low-side, and sometimes (if the magical delay is added) it samples the high side.

Hi, Thanks @Antun_Skuric downgrading to 2.3.4 seems indeed to fix the issue.

Simply swapping the code section from G4 did not work, since some symbols are missing.

I’m not in a rush and I’m happy to wait for a patch, or come back to it later.
Thanks a lot,

Best,
Thomas.

Ok great, thanks!

We have two/three small changes we wanted to make quickly in addition to this one, so we’ll try to do a patch iteration this week.

Hey @thomasfla,

I’ve updated the code fixing the issue. I unfortunately do not own one of these boards so I would appreciate it if you could test. Or someone else from the community if they are motivated :smiley:

The code is in this branch:

Thanks!

It works !

Thanks for the help and quick fix;