PWM Deadtime compensation

I will try to show how dead-time compensation can help improve the waveform when I finally get the wheel to rotate :sweat_smile:

You can see the effect of deatime in this picture shared by @Juan-Antonio_Soren_E in this thread

Reducing the deadtime can definitely help but you cannot make it zero.
That’s where deadtime compensation could help.

I saw in some experiments it was greatly improving the waveform but it’s hard to see an improvement in noise or torque ripple on motors with a high number of pole pairs.

1 Like

Interesting, sorry I hadn’t seen this. I mostly solved the problem but solving it at a lower level is a better idea. Will read up on dead time compensation. I am now once again investigating dead time as a possible source of issues on the Lepton 2.0

An example from another very educational open source FOC firmware

It’s a gold mine

1 Like

That looks pretty easy to implement actually… but it does need current sensing.

I’m not sure I fully understand it - following the code it seems that is just slightly increasing the duty cycle when current is negative, and decreasing it slightly when current is positive?

I shall have to read more about it.

Yes it should be as easy as that.
This other variant mentioned in this article

maps the deadtime compensation when it’s below a threshold current so that it’s not just on or off.

The value of the deadtime compensation should be less than the programmed deadtime. In my case I was increasing it until I was satisfied with the phase current waveform.
MESC firmware has an interesting method to measure the effective deadtime, by increasing the pwm until phase current is visible in the measurement.

And this is another interesting article about deadtime compensation I had in my bookmarks

1 Like

I am not yet working on it, but more pictures of how the dead time impacts the current waveform

And why:

That’s interesting, but it would depend on the implementation?

For example, our software 6-PWM dead-time applies the dead-time in equal proportions to the high and low side, and would not be subject to the phase shift shown in the diagram.

I’d have to look them up, but IIRC the hardware dead-time of at least some of the MCUs is also symmetrical.

So this would be a problem seen more in combination with external drivers applying dead-time in hardware?

If we’re diving into the details of PWM generation at this level, a concern I have is that our PWM output is not synchronised to the hardware - this means that when setting the PWM levels (typically once per main loop cycle) there is a random chance that we hit the PWM timer reset while changing the values. Normally the PWM peripheral registers used are buffered, so its not a problem writing the values per se, but what it means is that one or two phases could switch to new values one PWM cycle earlier than the other phases.
(This is assuming the user has taken care to use pins from the same timer. On many MCUs we allow using different timers, in which case the phases aren’t necessarily in sync).
The solution could be to use interrupt/DMA driven setting of the PWM values, to ensure they are all changed at a time that guarantees they are applied together in the next PWM cycle.

If I find time (lol, don’t hold your breath) I’ll set up a test to see if it makes any difference.

We need to differentiate the programmed deadtime and the effective deadtime.
The shift is due to the turn ON and turn OFF delay of the mosfet, and that depends on the direction of the current.
In figure c, when the current is positive, the ON time is smaller than the ideal pwm(a), so you add a conpensation factor to the duty cycle.
In figure d, when the current is negative, the ON time is higher than the ideal pwm(a), so you substract a conpensation factor from the duty cycle.

We are not touching the timer deadtime configuration, just compensating the waveform.
This could probably be implemented before setting the pwm here and could look like:

if (abs(Ua) > threshold) Ua += sign(current.a) * dtcomp;
if (abs(Ub) > threshold) Ub += sign(current.b) * dtcomp;
if (abs(Uc) > threshold) Uc += sign(current.c) * dtcomp;

This is possible only if the phase currents are available, so current sensing should be used, and with 2 shunts, the 3rd phase current should be derived as Ia + Ib + Ic = 0
Close to 0 current, the deadtime is not compensated.
Dtcomp depends on the range in the driver.

The diagram also shows that the real middle of the pwm is shifted, but that’s another topic.

1 Like

I started to play around with this.
Using the openloop mode is the best, as you can spin the motor slow enough and the current is high enough to see a proper waveform

One challenge is the quality of the phase current samples as it’s not filtered.

Hope I am not bothering you with this here.
I am struggling with getting this to work because the phase current is noisy, I need to think about it, maybe I can do a reverse clark and park of the already filtered d and q currents.
I wanted to show you how it looks like when it works but I cheated by using the duty cycles instead of the phase current, which should not be a problem without load.

I just added this:

center = driver->voltage_limit/2;
Ua += (Ua > center ? 1:-1) * dtcomp;
Ub += (Ub > center ? 1:-1) * dtcomp;
Uc += (Uc > center ? 1:-1) * dtcomp;

The compensation has to be surprisingly high for me (0.7v), but the phase current waveform becomes sinusoidal.
I recorded the waveform and the motor.

1 Like

The waveform link doesn’t work for me

Oups, can you try again ?

Anybody interested in giving this a try ?
It should already give good results in openloop at slow speed/low load.
I just want to make sure I am not fighting with a problem only I have with my setup.

I can summarize what I did so far.

I declared a global variable for deadtime compensation so I can update it and observe the waveform.

float dtcomp = 0;

Here I added the code for the deadtime compensation.

if (dtcomp > 0){
  center = driver->voltage_limit/2;
  Ua += (Ua > center ? 1:-1) * dtcomp;
  Ub += (Ub > center ? 1:-1) * dtcomp;
  Uc += (Uc > center ? 1:-1) * dtcomp;
// set the voltages in driver
driver->setPwm(Ua, Ub, Uc);

Here, I compensate the voltage command so that the amplitude is same with or without the deadtime compensation.

if (dtcomp > 0 && abs(dtcomp) > abs(Uq)) Uq -= _sign(Uq) * dtcomp; 

I ended up compensating for 0.70V, which in my case is almost the power supply voltage * Deadtime% * 2.
I am not sure if it’s a coincidence, based on this paper I am compensating both for the deadtime and the stiction.

A supplementary test using a current
sensor and the torque sensor on motor M4 found that, while
current production starts at d = 0.071, external torque is not
felt until d = 0.083. This indicates that the deadtime ddt =
0.071 is the deadtime duty cycle for this motor driver and the
stiction is dst = 0.012 or Vst = 60 mV at the tested location

I want to experiment by checking at what minimum pwm duty cycle the current starts flowing, and at what minimum pwm duty cycle movement is visible. I think this could even be measured by software.

This method is not taking all the ripple/cogging sources as does the anticogging map mentioned in the paper, but can probably already give good results with little tuning without an encoder.

@runger I believe this thread was created because of the deadtime compensation discussions, but please feel free to move my posts to a new thread if you think it doesn’t belong here, because I have more bla bla and screenshots coming :rofl: Thanks in advance

I’m interested in the stiction-compensation part, because a windturbine likes no_load conditions to start. But I’m not planning to use this ESC1 board.
I guess the code is universal for other powerstages as well? At least, when they use the same current sensing method?

I’m surprised to see everyone playing with this evaluation board. To me it seems, it has major issues with excessive heat under load?
Currently I’m looking for the most mature mid-power range controller available…and the list of candidates is pretty short :frowning:

I think the effect of deadtime is relevant for any board.
The stiction might depend on the motor.

Bring them on!! :smiley:

From my point of view this is all fine, but if you want me to split the thread or something, let me know.

No wonder given the price of this (subsidized) hardware!
I think people underestimate the difficulty of interfacing with its minuscule solder pads, and don’t check the integration options (eg no SPI) in advance, because it’s actually quite complex to do so…

I believe Valentine has produced an updated design, renamed to Quadrans, there’s a forum post about it…
I’ve done an updated version based on the STM32G431, which I’ll test and then share as soon as I can.

@Anthony_Douglas what do you say? It’s your thread

Uh oh, IDK, take the discussion however you like, it will not undo what has been done, people can still read the stuff above, which mostly answers the question of how to change dead time on the board. I think we are mostly done with that subject, I would just make a new post for organizational reasons of the forum if the subject is changed but all good to me.

Moved to new thread by popular demand :smiley: