PWM for 3-phase Inverter

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...

Not even by changing f?

[incidentally that's a crude trapezoidal drive, not sinusoidal that a true inverter
would produce]

i want to maintain the constant speed and torque of the 3 phase induction motor.For that the voltage and the frequency of the supply must be varied linearly(v/f ratio=constant) through the inverter.pls send me arduino code to generate the pwm pulses for the inverter with proteus simulation
E-mail id:sabthagiri.ananth@gmail.com

I've already provide general code for a 3-phase sinusoid, including amplitude and frequency control in #24

I can't get it. Pls provide the code again with the proteus simulation

hello friends, it's good to know how to apply a turn sensor with TCST2103, it's a device that when attached to a bycicle brake disc placed on the motor shaft, sends the number of turns to the input of Arduino, this information is used to gain more stability in the gain of the AC motor, friends I'm still gathering information. thanks!

1 Like

MarkT:
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) ;
}

HI MarkT,

I have been going over this code very carefully:
You very carefully synchronize the timers so the PWM are all in sync.
You use interrupts to update the PWM registers at appropriate times.
You carefully use volatile byte for u, v and w.

Now the dumb question:
Why are updating your phase word in the loop() part of the code? Shouldn't the variable "phase" be carefully clocked so that the frequency is as accurate as your crystal? As far as I understand loop() runs asynchronously and may take longer or shorter depending on if an interrupt occurs. (You just set up Timer 2 to control the updating of the PWM registers...) Isn't your frequency going to fluctuate because the period of updating phase varies. I believe delayMicroseconds() waits for exactly 1000us and doesn't consider the overhead of the code in loop() as well as the interrupts that may occur.

Second dumb question:
Hypothetically, what if an ISR interrupt occurs between setting u but before v and w are set. The three phase will have a new value for u but old values for v and w. What prevents that from happening?

The intention is this is all driven by control loops, which yes should run synchronously, but I haven't
provided all those levels, its just test code for driving the PWM stuff.

Yes you could wrap u, v, w updates in noInterrupts()/interrupts(), in practice you'll very rarely see then
updated late (depends how fast your motor spins reletive to PWM frequency if this is significant - for
instance if you are Celeroton driving a 500,000rpm brushless motor this matters a great deal, but at
4000rpm and 16kHz PWM its no a big deal).

Thanks.

Hi,
Thank you for developing and sharing that code, Mark. I'll try it on one of my minis.

I want to make a VFD some day, and this might be a good start.

Perhaps to control the v/f ratio a good idea might be to have a potentiometer which the arduino reads to set the frequency and a TL494 (or SG3525 or other dc converter controller) also uses it but to control the DC rail voltage, hence using less processor power.

Or, if the v/f ratio is included in the code, three dc converter controllers could be fed with one properly filtered phase each into their reference inputs so as to tightly control the generated voltages on the inverter.

A next step would be joining an mppt algorithm with a vfd motor drive, to power water pumps with the sun.

Id like to make those things one day but I wouldn't mind of you want to develop and share those answers anyway haha

I'll keep on working on building my mpp tracker first, though. Maybe I'll post about it when finished.

Regards,
Willy

As an alternative to DDS this code shows a way to generate 3 phase sine PWM using a recursion equation rather than a lookup table. Because computation is slower than a lookup table it cannot go as high as 1KHz for the sine output. I have tested it to 500Hz and it can probably go a little higher.

// Generates three equally spaced phases of a PWM sine signal
//    on an Arduino Mega

#include <TimerOne.h>

#define phase0  11    // Timer1 sets pwm on 11, 12, and 13 on Mega (9 and 10 on Uno)
#define phase1  12
#define phase2  13
#define delayPin  10    // use this optional signal for info on compute time

const float pi = 3.14159 ;
float T = 150 ;    // sample time in microseconds
volatile float freq = 50 ;  // frequency of tone in hertz
float omega = 2*pi*freq ;
float A = 490 ;  // amplitude
float omegaT = omega*T/1000000.0 ;
// next line initializes oscillation with amplitude A
volatile float a[]={0.0, A*sin(omegaT),0.0}; 
float bb0 = A*sin(2.0*pi/3.0);
float bb1 = A*sin(omegaT + 2.0*pi/3.0);
float cc0 = A*sin(4.0*pi/3.0);
float cc1 = A*sin(omegaT + 4.0*pi/3.0);
volatile float b[]={bb0, bb1 ,0.0}; 
volatile float c[]={cc0, cc1 ,0.0};
// c1 is the difference equation coefficient
float c1 = (8.0 - 2.0*pow(omegaT,2))/(4.0+pow(omegaT,2));


void setup()                 
{
  Timer1.initialize(T);  // set sample time for discrete tone signal
  Timer1.pwm(phase0, 0,T);
  Timer1.pwm(phase1, 0,T);
  Timer1.pwm(phase2, 0,T);    
  Timer1.attachInterrupt(compute);
  pinMode(delayPin, OUTPUT);
}

void loop()                   
{ 
  delay(1000);
  changeFreq(10);
  delay(1000);
  changeFreq(50);
  delay(1000);
  changeFreq(200);
  
}

void compute()    // called by Timer Interrupt
{
  digitalWrite(delayPin, HIGH);
  
  a[2] = c1*a[1] - a[0] ;  // recursion equation
  a[0] = a[1] ;            // shift
  a[1] = a[2] ; 

  b[2] = c1*b[1] - b[0] ;  // recursion equation
  b[0] = b[1] ;
  b[1] = b[2] ; 
  
  c[2] = c1*c[1] - c[0] ;  // recursion equation
  c[0] = c[1] ;
  c[1] = c[2] ;  
     
  Timer1.setPwmDuty(phase0, int(a[2])+512);
  Timer1.setPwmDuty(phase1, int(b[2])+512);
  Timer1.setPwmDuty(phase2, int(c[2])+512);    
  digitalWrite(delayPin, LOW); 
}

void changeFreq(float _freq){  // changes frequency
  noInterrupts();
  freq = _freq;
  omega = 2*pi*freq ;
  omegaT = omega*T/1000000.0 ;
  a[0] = 0.0 ;
  a[1] = A*sin(omegaT);
  b[0] = A*sin(2.0*pi/3.0);
  b[1] = A*sin(omegaT + 2.0*pi/3.0);
  c[0] = A*sin(4.0*pi/3.0);
  c[1] = A*sin(omegaT + 4.0*pi/3.0);
  c1 = (8.0 - 2.0*pow(omegaT,2))/(4.0+pow(omegaT,2));    
  interrupts();
}

MarkT:
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 ]

I have tested this Code and it really works very good. That was a great favor for my project.
Please help to define the base frequency of this code. I mean I have connected to 3 phase motor and it run with approx 0.5Hz. How it happens? any justification???

Yes, that's easy - the phase variable wraps at 1024, so every 1024 times through the loop() function is
one cycle of the waveform.

I put a 2ms delay in the loop, so it will take about 2.05s per cycle as written.

As I've noted above this loop() is purely to demonstrate the principle - you are free to update
u,v,w whenever you want [ guarded by noInterrupts()/interrupts() ]