PWM for 3-phase Inverter

baso_syahrul:
hi ,
if anyone can help me. my experimet is about inverter 3 phase. i use arduino for generate 3-phase and i connect it with driver IR21362. i can't figure out how to generate pwm 3 phase from my arduino uno.
thanks for ur attention and any help.

3-phase what? sinusoidal or trapezoidal? Frequency range? Does the amplitude
need controlling?

Can you generate a single phase already? If so your code would be handy to see as
a guide to extending it.

sinusoidal. controllable with frequency with range 0-1khz. i've never tried to generate 1 phase.

To generate 1kHz sine wave you probably want to run your PWM at something like
50kHz.

If you are driving an inverter the output signals are not independent though, and for
sinusoidal is the most complex case. I think with a little care you can use the current
angle within the current 60-degree sector to determine the ratio of time spent in
two active drive states (and the zero drive state).

Think of the 8 inverter drive states as two zero-drives (all phases the same), and
six drive patterns arranged around a hexagon in phasor-space.

The problem this gives is that each PWM period needs to split into 3 parts,
2 different drive patterns and a zero-drive part. This requires at least two PWM
pins if you use the hardware, and you then need to read these pins and convert
the the right pattern (pin-change interrupts can handle that).

50kHz is ambitious, thinking about it, on a 328 based board at least.

There is another approach, hysteresis drive, where you examine the error
on a regular clock tick and change the output pattern based on the error - this
is simpler but needs to run even faster for good smooth sinusoid response.

This can work with actual current feedback from the inverter as well as with
a model of the average

I've been thinking about this some more and looking at what the Arduino
timers can do.

I've realised the Mega has some timers with 3 output pins, and this allows a
clever way to modulate 3 output signals for doing this.

It relies on having an inverter module that takes one input per phase (in
other words it generates its own high/low gate signals with appropriate
dead-time automatically generated - without this you'd need some
circuitry to do this).

Using phase-correct mode you can effectively get two control pulses per
cycle (where the pulses are differential between separate phase outputs).

So here's some test code - it drives pins 6/7/8 which are the pins for timer4.

// For the MEGA!

#define HALF 400
#define PERIOD 800

void setup ()
{
  setup_cosines () ;
  TCCR4A = 0xFE ;  // phase correct (mode 1010)
  TCCR4B = 0x11 ;  // prescale by 1
  TIMSK4 = 0x01 ;  // overflow interrupt
  ICR4  = PERIOD+1 ;    // 100us cycle time, so effectively 50us.
  OCR4A = HALF ;
  OCR4B = HALF ;
  OCR4C = HALF ;
  pinMode (6, OUTPUT) ;  // the OCR4A pin, our U phase
  pinMode (7, OUTPUT) ;  // the OCR4B pin, our V phase
  pinMode (8, OUTPUT) ;  // the OCR4C pin, our W phase
}

// control time values, in units of 62.5ns
volatile int u = HALF ;
volatile int v = HALF ;
volatile int w = HALF ;


// every complete cycle we update the registers.
ISR (TIMER4_OVF_vect)
{
  OCR4A = u ;
  OCR4B = v ;
  OCR4C = w ;
}

// a cosine table, 1024 entries in range +/-127
char cosine [0x400] ;

void setup_cosines ()
{
  for (int i = 0 ; i < 0x400 ; i++)
  {
    float a = PI * i / 0x200 ;
    cosine [i] = round (127.0 * cos (a)) ;
  }
}

int phase = 0 ; // taken modulo 1024
int amplitude = 200 ;  // 201 is maximum value without overflow.

void loop ()
{
  phase ++ ;  // phase increment, normally this would be done by DDS loop
  int newu = (cosine [phase & 0x3FF] * amplitude + 0x1F) >> 6 ;
  int newv = (cosine [(phase + 0x155) & 0x3FF] * amplitude + 0x1F) >> 6 ;
  int neww = - newu - newv ;
  newu += HALF ;
  newv += HALF ;
  neww += HALF ;
  noInterrupts () ;  // interrupt-safe updating of u,v,w
  u = newu ;
  v = newv ;
  w = neww ;
  interrupts () ;
  delay (2) ;
}

[ edited to fix mistake in the ISR ]

can you recommend me a code agree with my problem?

baso_syahrul:
can you recommend me a code agree with my problem?

Well you mentioned the IR21362 which looks like you can strap the HIN and LIN inputs
together per-phase as it has dead-time generation and shoot-through prevention. Then
the technique in my code above would be suitable. You might need to add stronger pull-down
resistors to park the bridge in all-low-side on configuration, needed to charge the bootstrap caps.

[ This link might be useful: Redirect Notice ]

i'm sorry about my PM i've never read your signature. i will try it and thanks. i'll be post the result(works or not).

thx mark i simulate the program ( 2 pics ) but it varying automatique

( work in this topic plz : 3 PWM for 3 phase inventer - Motors, Mechanics, Power and CNC - Arduino Forum )

Have you understood anything about DDS that I mentioned in the other thread?

Does this code not immediately say "DDS sinewave generation":

  phase ++ ;  // phase increment, normally this would be done by DDS loop
  int newu = (cosine [phase & 0x3FF] * amplitude + 0x1F) >> 6 ;
  int newv = (cosine [(phase + 0x155) & 0x3FF] * amplitude + 0x1F) >> 6 ;

Can you not see how to adapt this for different requirements?

MarkT:
Have you understood anything about DDS that I mentioned in the other thread?

Does this code not immediately say "DDS sinewave generation":

  phase ++ ;  // phase increment, normally this would be done by DDS loop

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




Can you not see how to adapt this for different requirements?

thx mark i understand the dds
but here the code it varing varing automatique . can you explain me ?

Well clearly you don't. I'm incrementing a phase variable and recomputing some
cosines. That's DDS and that's why its changing. Its test code to show it working.

MarkT:
Well clearly you don't. I'm incrementing a phase variable and recomputing some
cosines. That's DDS and that's why its changing. Its test code to show it working.

oki mark really thx know i undertand .

in your code above is for mega, how about for UNO? is code in adelo14's thread suitable?

As I said:

I've realised the Mega has some timers with 3 output pins, and this allows a
clever way to modulate 3 output signals for doing this.

The Uno timers only have two output pins each.

[ Just checked, the ATmega32U4 has 3 outputs on timer1, that's
used on the Leonardo, and they correspond to pins 9/10/11 ]

Is this 3-phase inverter for driving a motor, or to create a three phase power supply for something other than a motor ?

michinyon:
Is this 3-phase inverter for driving a motor, or to create a three phase power supply for something other than a motor ?

this 3-phase inverter for driving a motor

That means the load is intrinsically balanced and there is a lot of inductance
(meaning a lower PWM frequency is feasible).

BTW I've worked out how to synchronize timer1 and timer2 on the Uno, another
sample sketch provided:

// For the UNO et al.
#define PERIOD 250   // 250 cycles = 15.625us for each half of waveform, 32kHz
#define HALF 125     // half the period is the default PWM threshold - gives square wave.
#define MAXAMP 31
  
void setup_timers ()
{
  TCCR1A = 0xF2 ;  // phase correct (mode 1010, ICR1 controls period)
  TCCR1B = 0x11 ;  // prescale by 1, change to 0x12 for prescale by 8
  TIMSK1 = 0x01 ;  // overflow interrupt
  TCCR2A = 0x31 ;  // phase correct (mode 101, OCR2A controls period)
  TCCR2B = 0x09 ; // prescale by 1, change to 0x0A for prescale by 8
  
  ICR1  = PERIOD ;    // 31.25us cycle time, 32kHz PWM but 64kHz drive pulses (differential)
  OCR2A = PERIOD ;
  
  OCR1A = HALF-100 ;    // example U drive
  OCR1B = HALF ;
  OCR2B = HALF+100 ;    // example W drive
  
  GTCCR = 0x83 ; // clear and halt prescalers
  TCNT1 = 0xFFFF ;  // synchronize counters exactly.
  TCNT2 = 0 ;
  GTCCR = 0x00 ; // allow prescalers to fly
}


void setup ()
{
  setup_cosines () ;
  setup_timers () ;

  pinMode (9, OUTPUT) ; // OC1A pin = U drive
  pinMode (10, OUTPUT) ; // OC1B pin = V drive
  pinMode (3, OUTPUT) ; // OC2B pin = W drive
}

volatile byte u = HALF ;
volatile byte v = HALF ;
volatile byte w = HALF ;


ISR (TIMER1_OVF_vect)   // overflow triggers at BOTTOM, update all the compare regs, which are sampled at TOP
{
  OCR1A = u ;
  OCR1B = v ;
  OCR2B = w ;
}

#define  DEG_360  0x200
#define  DEG_120  0x0AB

char cosine_tab [DEG_360+1] ;  // fails to work if less than 0x202.

void setup_cosines ()
{
  for (int i = 0 ; i < 0x202 ; i++)
  {
    float a = i * 6.283185 / 0x200 ;
    if (i <= DEG_360)
      cosine_tab [i] = round (127.0 * cos (a)) ;
  }
}

unsigned int phase = 0 ;
int freq  = 1 ;
int amplitude = MAXAMP ;


int my_cosine (int ph)
{
  ph &= DEG_360-1 ;
  int res = cosine_tab [ph] ;
  res *= amplitude ;
  return (res + 0x0F) >> 5 ;
}

void loop ()
{
  phase += freq ;
  int newu = my_cosine (phase) ;
  int newv = my_cosine (phase - DEG_120) ;
  int neww = - newu - newv ;
  newu += HALF ;
  newv += HALF ;
  neww += HALF ;
  u = newu ;  // no masking of interrupts needed as u,v,w variables are single byte each.
  v = newv ;
  w = neww ;
  delayMicroseconds (1000) ;
}

this is my code that i used

 float port1 = 11;
 float port2 = 10;
 float port3 = 9;
 int f = 50;

void setup() {
Serial.begin(9600);
pinMode(port1, OUTPUT);
pinMode(port2, OUTPUT);
pinMode(port3, OUTPUT);

}

void loop() {
  //sesi 1
  digitalWrite(port1, HIGH);
  digitalWrite(port2, LOW);
  digitalWrite(port3, HIGH);
  Serial.println("1 0 1");
  delay(1/(6*f));
  
  //sesi 2
  digitalWrite(port1, HIGH);
  digitalWrite(port2, LOW);
  digitalWrite(port3, LOW);
  Serial.println("1 0 0");
  delay(1/(6*f));
  
  //sesi 3
  digitalWrite(port1, HIGH);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, LOW);
  Serial.println("1 1 0");
  delay(1/(6*f));
  
   //sesi 4
  digitalWrite(port1, LOW);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, LOW);
  Serial.println("0 1 0");
  delay(1/(6*f));
  
   //sesi 5
  digitalWrite(port1, LOW);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, HIGH);
  Serial.println("0 1 1");
  delay(1/(6*f));
  
   //sesi 6
  digitalWrite(port1, LOW);
  digitalWrite(port2, LOW);
  digitalWrite(port3, HIGH);
  Serial.println("0 0 1");
  delay(1/(6*f));
  
}

baso_syahrul:
this is my code that i used

 float port1 = 11;

float port2 = 10;
float port3 = 9;
int f = 50;

void setup() {
Serial.begin(9600);
pinMode(port1, OUTPUT);
pinMode(port2, OUTPUT);
pinMode(port3, OUTPUT);

}

void loop() {
  //sesi 1
  digitalWrite(port1, HIGH);
  digitalWrite(port2, LOW);
  digitalWrite(port3, HIGH);
  Serial.println("1 0 1");
  delay(1/(6f));
 
  //sesi 2
  digitalWrite(port1, HIGH);
  digitalWrite(port2, LOW);
  digitalWrite(port3, LOW);
  Serial.println("1 0 0");
  delay(1/(6
f));
 
  //sesi 3
  digitalWrite(port1, HIGH);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, LOW);
  Serial.println("1 1 0");
  delay(1/(6f));
 
   //sesi 4
  digitalWrite(port1, LOW);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, LOW);
  Serial.println("0 1 0");
  delay(1/(6
f));
 
   //sesi 5
  digitalWrite(port1, LOW);
  digitalWrite(port2, HIGH);
  digitalWrite(port3, HIGH);
  Serial.println("0 1 1");
  delay(1/(6f));
 
   //sesi 6
  digitalWrite(port1, LOW);
  digitalWrite(port2, LOW);
  digitalWrite(port3, HIGH);
  Serial.println("0 0 1");
  delay(1/(6
f));
 
}

u simulate or not ?

not simulate and my motor is work but i cannot control frequency...