6 SPWM AC Motor control

Hi guys, I want to control AC Motor through power module dsPICDEM™ MC1H 3-Phase High Voltage Power Module. So as to do it I need to generate 6 SPWM signals, 3 of them 120 degrees shifted and 3 of them inverted with a small deadband to prevent shoot-through (at least that what I think). I managed to generate 6 signals using Arduino Mega 2560, I invert LOW signals using 3 not gates. For some reason motor doesn’t move so I assume I have an error in the code. Could you please take a look at it and give some suggestions?

float x = 0;
const float pi = 3.1415;
const float y = pi / 30;

//Phase 1
#define OUT1High 2
#define OUT1Low  3
const float ph1 = 2 * pi / 3;
unsigned char p1High = 0;
unsigned char pp1High = 0;
unsigned char p1Low = 0;
unsigned char pp1Low = 0;

//Phase 2
#define OUT2High 5
#define OUT2Low  6
const float ph2 = 4 * pi / 3;
unsigned char p2High = 0;
unsigned char pp2High = 0;
unsigned char p2Low = 0;
unsigned char pp2Low = 0;

//Phase 3
#define OUT3High 8
#define OUT3Low  9
const float ph3 = 2 * pi;
unsigned char p3High = 0;
unsigned char pp3High = 0;
unsigned char p3Low = 0;
unsigned char pp3Low = 0;

unsigned long previousMillis = 0;
const long interval = 20; //frequency

void setup() {
  //Phase 1
  pinMode(OUT1High, OUTPUT);
  pinMode(OUT1Low, OUTPUT);

  //Phase 2
  pinMode(OUT2High, OUTPUT);
  pinMode(OUT2Low, OUTPUT);

  //Phase 3
  pinMode(OUT3High, OUTPUT);
  pinMode(OUT3Low, OUTPUT);

void loop()  {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    x = x + y;
      analogWrite(OUT1High, p1High);
      analogWrite(OUT1Low, p1Low);
      pp1High = 126 * sin(0.96 * x + ph1);
      p1High = pp1High + 128;
      pp1Low = 126 * sin(x + ph1);
      p1Low = pp1Low + 128;

      Serial.print(" ");
     // Serial.print(" ");
      analogWrite(OUT2High, p2High);
      analogWrite(OUT2Low, p2Low);
      pp2High = 126 * sin(0.96 * x + ph2);
      p2High = pp2High + 128;
      pp2Low = 126 * sin(x + ph2);
      p2Low = pp2Low + 128;

      Serial.print(" ");
     // Serial.print(" ");
      analogWrite(OUT3High, p3High);
      analogWrite(OUT3Low, p3Low);
      pp3High = 126 * sin(0.96 * x + ph3);
      p3High = pp3High + 128;
      pp3Low = 126 * sin(x + ph3);
      p3Low = pp3Low + 128;

      Serial.print(" ");
    if (x >= 2 * pi) x = 0;

You need to be looking at the output on a 'scope in the first instance - get the waveforms wrong and you could trash the bridge or motor...

I’m going to upload scope’s waveforms tomorrow as I don’t have oscilloscope at home. However, deadband is introduced in the code, and I checked outputs before plugging it into the motor.

pp2High = 126 * sin(0.96 * x + ph2); <— x multiplier shorten the sin wave effectively creating deadband
p2High = pp2High + 128;

pp2Low = 126 * sin(x + ph2); <— reference sine wave
p2Low = pp2Low + 128;

Are there any obvious errors in the code?

Thanks for response!

hi , so i'm in need of youre code so i've uploaded to my board and the put a scope on it, well it looks like in theory you should get an inverted PWM on pin 2 and 3 , but on my scope i can clearly see that the 2 pwms ar the same , even when i conect it to 2 leds, they fade in and out at the same time so i belive this is the problem

i've made a GIF with my scope and atached two leds to pin 2 and 3 , as you can see the leds are in phase and light up simultanyous not opposite as it should be , i belive

here is a link to tthe GIF:


Hi, I didn't have time to take pics today but definitely tomorrow I'm going to upload it. The thing is that Low side has to be inverted using simple not gate, I just couldn't get it inverted in software. I can assure you that dead-band will be visible with a proper resolution.

Its possible to invert any PWM signal by direct programming the relevant registers. If you figure out which timers are doing which pins then you'll see in the datasheet there are 2 bits per pin for each timer in one of the control registers (TCCR?A) that control function and sense of that pin.

You'll then have to directly use the output registers to set the output duty cycle (analogWrite always overwrites the registers), ie OCR?? = value

I upload pictures of the scope, signal has been put through not gate and factor x was increased to x = 0.85 to show dead-band properly (how can i measure time of it?). Do you know any good tutorials that can introduce me to that MarkT? I have watched couple and I got the idea but I find it difficult to work on binary and hexadecimal numbers. Do you have any thoughts about the code?

Quick update, I found moment when both of the sides are HIGH, it may be the main issue of the code. Do you know how to get rid of that? Please find the attachment.

Zakux: Quick update, I found moment when both of the sides are HIGH, it may be the main issue of the code. Do you know how to get rid of that? Please find the attachment.

Whoops, yes that will explode the bridge nicely! And I mean explode. You are probably changing the time duty cycle at the wrong point in the PWM cycle - if you do it right they will both update synchronously at a safe point in the cycle - this depends on PWM mode, so read that datasheet!

Yes, you have to get good at binary and hexadecimal, there's no avoiding that, do some practice. You pretty much have to memorize powers of two upto 4096 or so. 1 2 4 8 16 32 64 128 256 512 1024 2048 4096 etc. Of course you can memorize them in hex, 0x1 0x2 0x4 0x8 0x10 0x20 0x40 0x80 0x100 0x200 0x400 0x800 0x1000, which is a lot easier.

It will be worth making some hardware dead-time prevention circuits if driving that module, otherwise the first software error will release the magic smoke dramatically.

The device seems to have opto-isolation in the gate drive using HCPL4603 fast opto couplers, so required deadtime is probably on the order of several microseconds.

Have you considered the advantages of working at lower voltages while developing, high power high voltage hardware is totally unforgiving and you will probably need to replace the hardware a few times whatever it is, so choose cheap low voltage stuff initially until you've shaken out many bugs.

Thank you for guidance, I'm going to work on it straight off!

I came across one of your codes for MEGA and I will try to work on it while learning how to use registers and timers. One thing that bothers me is how did you came up with equation for cosines?

 int newu = (cosine [phase & 0x3FF] * amplitude + 0x1F) >> 6 ;      // recomputing variables
 int newv = (cosine [(phase + 0x155) & 0x3FF] * amplitude + 0x1F) >> 6 ;
 int neww = - newu - newv ;

is it standard procedure or it can be find somewhere ?