Read a potentiometer continuously? on STM32 and analogRead()

One thing that surprised me the first time I piloted this configuration with SimpleFOC

With a B-G431B-ESC1
motor.linkCurrentSense(&currentSense);
is put before

currentSense.init();
currentSense.skip_align = true;
motor.linkCurrentSense(&currentSense);
...
motor.init();
motor.initFOC();

https://github.com/simplefoc/Arduino-FOC/blob/master/examples/hardware_specific_examples/B_G431B_ESC1/B_G431B_ESC1.ino

And with STM32F405RGT6 you have to put
current_sense.linkDriver(&driver);
In this order, otherwise I can’t get it to work
As in your Github example, but the current measurement doesn’t work?

motor.init();
current_sense.linkDriver(&driver);
current_sense.init();  
current_sense.skip_align = true;
motor.linkCurrentSense(&current_sense);
...
motor.initFOC();

https://github.com/simplefoc/Arduino-FOC/blob/master/examples/hardware_specific_examples/Odrive_examples/odrive_example_spi/odrive_example_spi.ino

if this is a clue to the problem ?..

@dekutree64
doesn’t change the problem motor.disable|enable

void PIDController::reset(){
    integral_prev = 0.0f;
    output_prev = 0.0f;
    error_prev = 0.0f;
    timestamp_prev = _micros();
}

I repeat, look at the scale.
With the delay the peaks are much lower.

@Candas1
Ok I see the difference

yes much smaller, but i think simply adding a delay is not a solution but rather a work around. the real problem lies somewhere in the fact that the pid isn’t updating when the motor is disabled and also that you sample bad currents. so the solution would be to: restart pid with an output value close to the motor bemf to have a smooth transition, sample once the current readings, from then on the pid could take over like normal

There must several problems.
But for sure the phase current sampled while the driver is off shouldn’t be used.
That’s why I used the delay.

It’s not just about the PID.

1 Like

If I also disable the ADC’s external trigger bit, there is no peak anymore when the motor is not moving.

CLEAR_BIT(ADC1->CR2, ADC_CR2_JEXTTRIG);
motor.disable();
…
motor.enable();
SET_BIT(ADC1->CR2, ADC_CR2_JEXTTRIG);

This is better than the delay but something at the timer level would be easier to implement in the driver.

But there is still a peak if the motor was spinning, this peak is related to the PID:

If I also reset the dq pids I see no peak anymore.

This alternative is from the timer’s point of view:

LL_TIM_DisableUpdateEvent(TIM1);
LL_TIM_EnableUpdateEvent(TIM1);

When Timer’s Update Event is disabled, the update event is not generated anymore, which is used for triggering the adc

:slight_smile:
It would also be nice to protect the current overflow if the delay is exceeded.
Me @runger’s solution
driver.voltage_power_supply = 24.0f ;
driver.voltage_limit = 20.0f ;
is fine with me, but a user could fry equipment if there’s no protection …

Very interesting! But normally when the motor is disabled, then the current sense values are not retrieved because _readADCVoltageLowSide won’t get called…

So what exactly is happening? Does it mean after re-enabling, it is retrieving at least one old value? When adding the delay, the peak is less, because the old value’s influence is reduced via the LPF?
And when disabling the trigger, we don’t get the old value, so we don’t get the same peak?

What happens (if I may ask you to try it) if instead of disabling the trigger, you instead call _readADCVoltageLowSide() for each pin, or maybe getPhaseCurrents() once after motor.enable() but before the call to loopFOC()?
→ that might get rid of the old value?

Just to summarize the findings:
With low side current sense, if phase currents are sampled while the low side mosfet is OFF, the phase currents are completely off. This happens if the duty cycle is very high, or when the driver is disabled.

While the motor is disabled, _readADCVoltageLowSide is not called but you still have those wrong phase current values either in the DMA buffer or in the injected adc registers, those values are used the next time _readADCVoltageLowSide is called.

I don’t think running getPhaseCurrents() before loopfoc will help as loopfoc will get those bad values again.

To me this is not a bug.
You need to know that with Low side current sensing, you shouldn’t reach 100% duty cycle so you have to limit the voltage. I am not sure the library should do this for you.

Yes, you’re right, we would have to wait for the next update from the ADC, which can only yield correct values after the PWM has been set.

It’s a tricky case, but we should find a way to take care of it. So far your method of disabling the trigger is the best idea we have.
So for a future release of SimpleFOC:

  • we should extend the disable() function to also call a currentsense.disable() - same for enable(). So calling motor.dis/enable() forwards this intent to driver and current sense, who can deal with it if needed.
  • current sense: we can then call a new method in the hardware API to disable the triggers, which is MCU specific.
  • additionally, the motor.enable() function should call pid.reset() for all the PIDs.

Just something I forgot to mention.
To limit the duty cycle/voltage, there are 2 options.

What @runger proposed it to reduce the driver voltage limit.

The other option is to keep driver.voltage_power_supply = driver.voltage_limit as it is by default, but reduce motor.voltage_limit.


Usually with SVPWM you should use this value:

motor.voltage_limit = driver.voltage_power_supply * 0.58f;

With low side current sensing you can reduce it:

motor.voltage_limit = driver.voltage_power_supply * 0.45f;

The disadvantage of this solution is that you sacrifice % of duty cycle on top and bottom so the maximum line to line voltage is even lower.
The advantage is that the modulation is centered, and the conduction losses are equal between high side and low side mosfets. I am not sure how much it matters in reality.

Sorry I hope I could explain it clearly

1 Like

Is this going in the right direction?

1 Like

Thanks.
When it’s merged I can try to write those functions for STM32F1.
It should be very similar for all stm32 families.

Because it is part of the current sense, I will try to disable the ADC external trigger, that might be easier and safer.

LL_ADC_INJ_StartConversionExtTrig(cs_params->adc_handle,LL_ADC_INJ_TRIG_EXT_RISING)
LL_ADC_INJ_StopConversionExtTrig(cs_params->adc_handle)

Or this option but on the timer:

LL_TIM_DisableUpdateEvent(cs_params->timer_handle);
LL_TIM_EnableUpdateEvent(cs_params->timer_handle);

I was tempted to buy one for testing and I came across this MKS XDrive MINI



It’s surprisingly cheap(34e) although it has an encoder included.

Great find!
Really inexpensive, with an AS5047P…
I think it’s a really good card, well supported by SimpleFOC.

Good amount of pins available, too. Assuming the Mosfets are decent, looks like an interesting board

@Candas1 Hello,
I think there’s a serious bug in the current version of dev ?
I’ve just downloaded the latest version of dev to try it out,
I connected my power supply (without sending commands to the motor), and it immediately went into current protection.
I quickly switched off the power supply, put the old firmware back on (version downloaded on 8 January) and everything’s fine …