VESC Support - 1kW of Power!

VESC hardware support is coming to SimpleFOC! :fireworks: :dizzy: :boom:

If you count clones and spinoffs, there are about 20 different VESC hardware options out there but their pinouts are similar and all use stm32f405 chips. They are all based off Benjamin Vedders designs (v4.12 + v6.4). I’m looking at the venerable 4.1.2 version which can do about 1kW, but there are bigger boards that can do ~10kW!!

Here is an attempt at describing the peripherals that you might be able to use on this board. Most of these are untested by me:

I’ll post on this thread when I get new stuff working (e.g. can, i2c, spi, usb, current sensing)

I’m diverting my efforts from b-g431b-esc! (aka drone kit) to this board type and have already ordered a dual board! :slight_smile:

Be aware that newer boards have drv8301 (instead of drv8302) so will need additional SPI config before they will work.


Hi @Owen_Williams,
I have tested simpleFOC with VESC4.12 (Velocity profile), and it works well, but I have got an issue with the dead_zone when I put this one at 0.02 or 0.05, the current increases abnormally in increasing the speed of the motor (I think that the transistors are in short circuit). When I put the dead_zone at 0.2 (even 0.4) that improves ??? it seems that depends on the frequency pwm and of motor velocity (???).

I am with a PWM of 40KHz.

What is the dead_zone have you put ?

Another question, I tried to put loopFOC() in creating an interrupt on TIM1 :
extern “C” void TIM1_UP_TIM10_IRQHandler(void) {
if (TIM1->SR & TIM_SR_UIF ) {…
But there is a conflict : “.pio/build/genericSTM32F405RG/FrameworkArduino/HardwareTimer.cpp.o:HardwareTimer.cpp:(.text.TIM1_UP_TIM10_IRQHandler+0x0): first defined here”

How to do ?

Thank you.

@Owen_Williams ,
Another thing, I have observed that the DRV8302 heats a lot ( in letting my finger on it :wink: )

Great to hear someone else is trying out out VESC! I had a bit of a wobble with it at the weekend when I thought I’d bricked it. I think it happened when I configured SENS1 (which is also PA0/wkup pin) and it would then no longer program using stlink. There is no reset button but NRST is available on swd port so I need to hold it low then connect using stmprogrammer and then remove NRST from gnd after about 1.5 seconds. Not obvious. I got clues to a solution here.

Anyway - the drv8302 like many drivers has hardware dead time insertion. The DTC pin has a 10K resistor to ground. From the datasheet it says that dead time is controlled by resistors on this pin, the docs say 0 to 150 kΩ (50 ns to 500 ns) so I’d guess we have 100ns of deadtime??

I’ve not set the deadtime in software - until you raised your concerns I would have said it could be disabled. I’ve not changed the PWM frequency.

How many watts are you putting through it? What motor have you got it connected to? I’ve taken my hoverboard motor up 50 rad/s (unloaded) and it was drawing 30W (at 24V) with no noticeable heat at all.

Are you in closed loop? Things can get hot in openloop.

regarding your timer interrupt. I think you are going a little too low level. stm32duino wants to manage that method. BTW the way the TIM1 channel is heavily used by the simplefoc already. Stay away from Tim1-ch1, Tim1-ch2, Tim1-ch3 as these are used by PWM but I think you can set a change interupt handler on Tim1-ch4.

I recorded a series of youtube videos on stm32duino timers a while back - I’m not much of an authority on it - but there didn’t seem much out there.
Part2 is probably of interest. The code is here:

Look what arrived today! The board on the right is a dual vesc 4.2.

1 Like

Thank you @Owen_Williams,
I am in closed loop and with 24V of power supply.
The driver works better with driver.dead_zone = 0.4 and driver.pwm_frequency = 20000.
With this dead_zone, I get around 5000rpm max (520rd/s) and my motor (8 pôles, hall sensors) can normally reach 9000rpm at 24V.

Wow great the dual vesc 4.2 !

That doesn’t look like my VESC. Where did you buy it? 5000rpm - you may have set a SimpleFOC speed record!
What power were you drawing? A little more than my 30W I suspect :wink:

At those speeds it may be worth switching to the trapezoidal_120 or 150 modulation type. They may use less cpu than sine or space vector. They are however not well tested.

Hey @Marc_O,

You are using very very high values of dead_zone.
Dead zone of 0.2 means that 20% of your duty cycle you have both both mosfets open. This means the largest voltage you can set to the motor is 20% less than your supply voltage.
And 0.4 means its 40%. So if you are using dead zone of 0.4 and you have a power supply of 24V you can only set 0.6*24 = 14.4V

I’ve looked into the IRFS7530 mosfets (datasheet ), they have the rise-up and fall-down time of about 200ns = 0.2micros.
Which means that on 40kHz, the 2% dead zone (0.02) would make for 0.25micros of dead time for rise-up and fall-down. This is too close to the mosfet values and that is probably why you have seen the heating.

dead_time = dead_zone/freq_pwm/2 = 0.02/40000/2 = 0.25us

For example, as a conservative value to be safe, you should have a dead time for both rising and falling of about 1us. And on 40kHz you would need dead_zone of:

dead_zone = dead_time*freq_pwm*2 = 1e-6*40000*2 = 0.08

So 8%, which is a lot actually.

So once when you lower the frequency to 20kHz:

dead_zone = dead_time*freq_pwm*2 = 1e-6*20000*2 = 0.04

Which is a bit better.

Hi @Owen_Williams,
My VESC 4.12 directly comes from this link :
I am effectively without load at the moment because I test it before to implement the current reading to control a true torque and to increase the torque, so. Indeed, the motor is power, I thought it will be interesting to try this motor with this drive and SimpleFOC.

For your information, the trapezoidal_120 works with this motor, but not the 150 modulation…
As you know, with this type of modulation the current increases a little bit more versus SVM or SINUS (and it is normal).

Thank you @Antun_Skuric,
I suspected this, from where my ??? for 0.4, but at this frequency 40KHz and 0.02, even with 0.05, the DRV8302 heated and the transistors too from 2500-3000rpm…And I noted that it also depended on the velocity of the motor, this gap current/velocity (current jump).
When I reduced my frequency and increased the dead zone the motor worked better and the power supply current was clearly less (i.e from1.8 to 0.5A), but I am going to try to investigate with an oscilloscope.

I think you might be hitting the limit of the voltage based FOC control. For these kinds of velocities I think that misalignment due to not measuring the phase currents is becoming much higher and causing the rise of the current. And it is not clear to me why is it that dead_zone is helping to solve this (maybe it is just reducing the voltage you set) but I think without current measuring you might not be able to run the motor on such a high velocities with simplefoc.

We will be implementing the low-side sensing soon (that will be the next one we support). :smiley:

Thank you @Antun_Skuric for your prompt reply.

I also thought there is the slew rate to take account in the dead time (4R7+ Rg + capacitor gate of the mosfet).

One other crazy experiment that might lead nowhere is to use attachSectorCallback method on hallsensor to give you changes to the hall sector and set phase voltage on driver directly each time sector shifts. You’d skip calling loopFOC/move and reimplement control logic yourself. This may be a dead end but if it worked, could be useful for super high speed where mcu is not fast enough to do proper foc.

Hi @Antun_Skuric,
I have begun to implement the iq / id current control, and I confirm you that I get up to >7000rpm now. It is not perfect for the moment, because I am yet bad synchronized, and I don’t know yet well the STM32.

Hi @Owen_Williams,
Can you explain me how to proceed to use attachSectorCallBack method with a code example, please ?

Thank you guys.

I can’t provide a code example. But I can explain the idea which may or may not make sense, you be the judge. :grin:

So hall sensors undergo 6 transitions per electric revolution. I call these ‘sectors’ and this callback will tell you which sector you have just arrived in.

It’s possible that this info could be used to directly decide where to place the magnetic field (calling driver.setVoltage?). You’d be changing the electrical angle 60degrees each commutation.

I’m not sure how this would be achieved, i was hypothesising you might be able to skip some of the maths and get faster speeds.

If you have current control working, I’d be very interested!

Hi @Owen_Williams,
you will find a piece of code here below needed for the current measure:

void Init_ADC(void){
    // ADC Setup
     RCC->APB2ENR |= RCC_APB2ENR_ADC3EN;                        // clock for ADC3
     RCC->APB2ENR |= RCC_APB2ENR_ADC2EN;                        // clock for ADC2
     RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;                        // clock for ADC1
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN;                       // Enable clock for GPIOC
     RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;                       // Enable clock for GPIOB
     ADC->CCR = 0x00000016;                                     // Regular simultaneous mode only
     ADC1->CR2 |= ADC_CR2_ADON;                                 // ADC1 ON
     ADC1->SQR3 = 0x0000009;                                    // use PB_1 as input- ADC1_IN9
     ADC2->CR2 |= ADC_CR2_ADON;                                 // ADC2 ON
     ADC2->SQR3 = 0x00000008;                                   // use PB_0 as input - ADC2_IN8
     ADC3->CR2 |= ADC_CR2_ADON;                                 // ADC3 ON
     ADC3->SQR3 = 0x0000000C;                                   // use PC_2 as input - ADC3_IN12
     // GPIO config
     GPIOB->MODER |= 0x0000000F;                                // Alternate function, PB_0, PB_1 are analog inputs 
     GPIOC->MODER |= 0x30;                                      // PC_2 as analog input 
     // See the F405 reference manual 
     ADC1->SMPR2 |= (1<<27);                                    // 15 cycles on CH_9
     ADC2->SMPR2 |= (1<<24);                                    // 15 cycles on CH_8
     ADC3->SMPR1 |= (1<<6);                                     // 15 cycles on CH_12

int offset_1, offset_2;

void zero_current(int *offset_1, int *offset_2){                                // Measure zero-offset of the current sensors
    int adc1_offset = 0;
    int adc2_offset = 0;
    int n = 1024;
    for (int i = 0; i<n; i++){                                                  // Average n samples of the ADC
        ADC1->CR2  |= ADC_CR2_SWSTART;                                          // Begin sample and conversion
        adc2_offset += ADC2->DR;
        adc1_offset += ADC1->DR;
    *offset_1 = adc1_offset/n;
    *offset_2 = adc2_offset/n;

    // Synchronize on TIM1 through TIM8 in the regular mode

Then the current reading is :

#define RSHUNT 0.001f
#define GAMP 10.0f
#define VREF 3.3f
#define ADCOUNT 4095.0f
#define R_pullup 39000.0f
#define R_pulldown 2200.0f
#define R_bridge ((R_pullup+R_pulldown)/R_pulldown)
#define V_SCALE ((VREF/ADCOUNT)*R_bridge)
#define I_SCALE (VREF/(ADCOUNT*RSHUNT*GAMP)) // Amps per A/D Count : 3,3V/(4096*Rshunt*Gamp) 

 float bat = (float)(ADC3->DR) * V_SCALE;                                               
 float c = (float)(ADC1->DR - offset_1) * I_SCALE;
 float a = (float)(ADC2->DR - offset_2) * I_SCALE;
 float b = ( -(a+c) );

and then you execute your Clarke/Park transform with these 3 currents, and so on.