Go Down

### Topic: 3 phase PLL @47~63Hz (Read 2446 times)previous topic - next topic

#### Simyager

##### Jan 03, 2017, 04:30 pm
So I want to make a 3 phase PLL which will work @ 47Hz to 63Hz. The output and input voltage of the signals are not really important as I will first have to lower max 600Vac to 5Vac ( don't worry I got this   [cheatcode:diff probe]) so I won't fry the Arduino . The output will be 10Vac sine but I can use an opamp( a transformer would be a bit overkill) so that's not the issue.

My question is: is it possible to use an Arduino (I already have an Uno but other types would also be possible) to create three phases. I pick one phase as reference phase A and the other phase is 120 degrees out of phase when compared to phase A and the third one is 240 degrees out of phase when compared with phase A.

And use some PLL magic so I can make sure there are 3 different phases and they can be regulated so they won't collide with each other.

So in short a 3 phase PLL @47~63Hz on Arduino. If possible then where do I start with programming a 3 phase PLL and which Arduino would be the best suited for the job? I have seen DDS but I need to regulate my output at all times to be certain.

#### allanhurst

#1
##### Jan 03, 2017, 05:52 pm
Not very easy.  Particularly the high power side.. If you try and do this yourself you'll fry a lot of electronics.

And working with those voltages is not for a beginner.

You can buy excellent 3-phase variable speed motor drives....  I'd go that way

Regards

Allan

#### Simyager

#2
##### Jan 04, 2017, 08:31 amLast Edit: Jan 04, 2017, 09:07 am by Simyager
Thank you for your answer. However as I said I can manage and handle the high power side, that's not the issue for me. The input voltage to the Arduino would be max 2,2V. And the output from the Arduino can be max 5V. I was more concerned whether the Arduino can handle the necessary computation that is required for a 3 phase PLL.

So to make it easier, the input is 2V~3V and the output from the Arduino is going to be 5V or anything that an Arduino can output max. With a frequency of 47~63Hz.

PS. Im not just a beginner with electronics nor with high power. Im actually an EE student and this is a part of my final project.

#### MarkT

#3
##### Jan 05, 2017, 01:35 amLast Edit: Jan 05, 2017, 01:35 am by MarkT
Generating multiphase is trivial with direct digital synthesis, which uses a phase accumulator, via table-lookup of the top bits of the phase value - if your phase variable is unsigned int (16 bit), for instance:

Code: [Select]
`  phase += frequency/Fsample ;  phase_U = sine_table [phase >> 8] ;  phase_V = sine_table [(phase + 0x5555) >> 8] ;  phase_W = sine_table [(phase + 0xAAAB) >> 8] ;`

And a PLL is like a PID loop for phase and/or frequency - you need a software phase detector to generate the reference phase value.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### charliesixpack

#4
##### Jan 05, 2017, 10:07 pm
You don't need a PLL to create a 3 phase signal.  A PLL is definitely not the way to go.

I have not run this code in a while.  You can try it but no guarantees.

Code: [Select]
`// Generates three equally spaced phases of a PWM sine signal//    on an Arduino Uno// makes use of the fact that sin(x+4pi/3) = - sin(x) - sin(x+2pi/3)// (c) 2016     C. Masenas#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))#define phase0  9    #define phase1  10#define phase2  11#define delayPin  7    // use this optional signal for info on compute timeconst float pi = 3.14159 ;const float bump = .01 ;  // fractional change of frequency per cycle// sample time frequency is 16/MHz / 256 / 8 const float T = 128 ;    // sample time in microsecondsfloat freq = .1 ;  // frequency of tone in hertzconst float A = 125.0 ;  // amplitudefloat omegaT = 2*pi*freq*T/1000000.0 ;// next line initializes oscillation with amplitude Avolatile float a[]={0.0, A*sin(omegaT),0.0}; const float phase1_0 = A*sin(2.0*pi/3.0);  // this will be used again so calculate oncefloat bb0 = phase1_0 ;float bb1 = A*sin(omegaT + 2.0*pi/3.0);volatile float b[]={bb0, bb1 ,0.0}; // c1 is the difference equation coefficientvolatile float c1 = (8.0 - 2.0*pow(omegaT,2))/(4.0+pow(omegaT,2));unsigned int sigma = 0;unsigned int delta = 0x8000; const int freq_c = 100; // frequency separating decimation and coefficient recalculation void setup()                 {  //The base frequency for pins 3, 9, 10, and 11 is 31250 Hz (32 uS)  //The base frequency for pins 5 and 6 is 62500 Hz.//  TCCR1B = TCCR1B & 0b11111000 | 0x02 ;  // timer1, sets pins 9 and 10 to 32*8=256 uS PWM period//  TCCR2B = TCCR2B & 0b11111000 | 0x02 ;  // timer2, sets pins 3 and 11 to 256 uS PWM period//  TIMSK2 |= (1<<TOIE2) ;  // enable ovrflow detect  Setup_timer1();  Setup_timer2();  pinMode(phase0, OUTPUT);  pinMode(phase1, OUTPUT);  pinMode(phase2, OUTPUT);  pinMode(delayPin, OUTPUT);}void loop()                   {     bumpFreq(1000) ;  delay(100);  bumpFreq(60);  delay(100);  }ISR(TIMER2_OVF_vect)    // called by Timer Interrupt{   sigma += delta ;  if ( sigma > 0x7FFF ){     sigma = sigma - 0x8000 ;    sbi(PORTD, delayPin);        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] ;  // calculate second phase    b[0] = b[1] ;    b[1] = b[2] ;       OCR1A = int(a[2])+127 ;  // pin 9    OCR1B = int(b[2])+127 ;     OCR2A = int(-a[2]-b[2])+127 ;             cbi(PORTD, delayPin);  }}void bumpFreq(float _freq){  // changes frequency  noInterrupts();  if( _freq <= freq_c && freq <= freq_c){    delta = 0x8000*(_freq/freq_c);   } else {  // abrupt change in frequency and phase    if( _freq <= freq_c && freq > freq_c){        delta = 0x8000*(_freq/freq_c);       freq = freq_c ;    } else {      delta = 0x8000 ;       freq = _freq ;    }    sigma = 0 ;    omegaT = 2*pi*freq*T/1000000.0 ;    a[0] = 0.0 ;    a[1] = A*sin(omegaT);    b[0] = phase1_0 ;    b[1] = A*sin(omegaT + 2.0*pi/3.0);     c1 = (8.0 - 2.0*pow(omegaT,2))/(4.0+pow(omegaT,2));      }   interrupts();}void bumpAtZero( float _freq){  while( !( a[1]> 0 && a[0] < 0 ));  // look for zero crossing of phase0  bumpFreq( _freq);  return ;}void changeFreq(float newFreq)// this function gradually changes the frequency to the new frequency// attempts to minimize abrupt jumps in frequency and phase{  if (newFreq > freq){    while ( newFreq > freq ){      if( (1.0 + bump)* freq < newFreq ) bumpAtZero( (1.0 +bump)*freq );      else{        bumpAtZero( newFreq );        return ;      }    }  }  if (newFreq < freq){    while ( newFreq < freq ){      if( (1.0 - bump)* freq > newFreq ) bumpAtZero( (1.0 -bump)*freq );      else{        bumpAtZero( newFreq );        return ;      }    }  } }// timer2 setup// set prscaler to 1,  fast PWMvoid Setup_timer2() {// Timer2 Clock Prescaler to : 8  cbi (TCCR2B, CS20);  // set  sbi (TCCR2B, CS21);  // clear  cbi (TCCR2B, CS22);  // Timer2 PWM Mode   cbi (TCCR2A, COM2A0);  // clear OC2A on Compare Match, PWM pin 11  sbi (TCCR2A, COM2A1);  // set to fast PWM  sbi (TCCR2A, WGM20);  // Mode 3, fast PWM  sbi (TCCR2A, WGM21);  cbi (TCCR2B, WGM22);  sbi (TIMSK2,TOIE2);              // enable overflow detect  }// timer1 setup  (sets pins 9 and 10)// set prscaler to 8, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clockvoid Setup_timer1() {// Timer1 Clock Prescaler to : 8  cbi (TCCR1B, CS10);  sbi (TCCR1B, CS11);  cbi (TCCR1B, CS12);  // Timer1 PWM Mode set to Phase Correct PWM  cbi (TCCR1A, COM1A0);  // clear OC1A on Compare Match, PWM pin 9  sbi (TCCR1A, COM1A1);  cbi (TCCR1A, COM1B0);  // clear OC1B on Compare Match, PWM pin 10  sbi (TCCR1A, COM1B1);  sbi (TCCR1A, WGM10);  // Mode 1  / phase correct  cbi (TCCR1A, WGM11);  sbi (TCCR1B, WGM12);  cbi (TCCR1B, WGM13);}`

#### charliesixpack

#5
##### Jan 05, 2017, 10:35 pm
This is another way of creating 3 phases.

Code: [Select]
`// 3 phase PWM sine// (c) 2016 C. Masenas// Modified from original DDS from: // KHM 2009 /  Martin Nawrath// table of 256 sine values / one sine period / stored in flash memoryPROGMEM const unsigned char sine256[]  = {  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124};#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))         int testPin = 7;int enablePin = 6 ;volatile  float freq=1;const float refclk=122.549  ;     //  16 MHz/510/256// variables used inside interrupt service declared as voilatilevolatile unsigned long sigma;   // phase accumulatorvolatile unsigned long delta;  // phase incrementbyte phase0, phase1, phase2;void setup(){  Serial.begin(9600);        // connect to the serial port  Serial.println("DDS Test");  pinMode(enablePin, OUTPUT);      // sets the digital pin as output  pinMode(testPin, OUTPUT);      // sets the digital pin as output  pinMode(9, OUTPUT);     // pin9= PWM  output / frequency output  pinMode(10, OUTPUT);     // pin10= PWM  output / frequency output  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output  Setup_timer2();  Setup_timer1();  digitalWrite(enablePin, HIGH);// the waveform index is the highest 8 bits of sigma// choose refclk as freq to increment the lsb of the 8 highest bits//    for every call to the ISR of timer2 overflow// the lsb of the 8 highest bits is 1<<24 (1LL<<24 for long integer literal)  delta = (1LL<<24)*freq/refclk ;  }void loop(){    changeFreq(20);  delay(10000);  changeFreq(25);  delay(10000);               }void changeFreq(float _freq){  cbi (TIMSK2,TOIE2);              // disable timer2 overflow detect  freq = _freq;  delta=(1LL<<24)*freq/refclk;  // update phase increment  sbi (TIMSK2,TOIE2);              // enable timer2 overflow detect} //******************************************************************// timer2 setup// set prscaler to 1,  fast PWMvoid Setup_timer2() {// Timer2 Clock Prescaler to : 1  sbi (TCCR2B, CS20);  // set  cbi (TCCR2B, CS21);  // clear  cbi (TCCR2B, CS22);  // Timer2 PWM Mode   cbi (TCCR2A, COM2A0);  // clear OC2A on Compare Match, PWM pin 11  sbi (TCCR2A, COM2A1);  // set to fast PWM  sbi (TCCR2A, WGM20);  // Mode 1, phase correct PWM  cbi (TCCR2A, WGM21);  cbi (TCCR2B, WGM22);  sbi (TIMSK2,TOIE2);              // enable overflow detect  }// timer1 setup  (sets pins 9 and 10)// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clockvoid Setup_timer1() {// Timer1 Clock Prescaler to : 1  sbi (TCCR1B, CS10);  cbi (TCCR1B, CS11);  cbi (TCCR1B, CS12);  // Timer1 PWM Mode set to Phase Correct PWM  cbi (TCCR1A, COM1A0);  // clear OC1A on Compare Match, PWM pin 9  sbi (TCCR1A, COM1A1);  cbi (TCCR1A, COM1B0);  // clear OC1B on Compare Match, PWM pin 10  sbi (TCCR1A, COM1B1);  sbi (TCCR1A, WGM10);  // Mode 1  / phase correct PWM  cbi (TCCR1A, WGM11);  cbi (TCCR1B, WGM12);  cbi (TCCR1B, WGM13);}//******************************************************************// Timer2 Interrupt Service at 31372,550 KHz = 32uSec// this is the timebase REFCLOCK for the DDS generator// runtime : 8 microseconds ( inclusive push and pop)// OC2A - pin 11// OC1B - pin 10// OC1A - pin 9// https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWMISR(TIMER2_OVF_vect) {  sbi(PORTD,testPin);            sigma=sigma+delta; // soft DDS, phase accu with 32 bits  phase0=sigma >> 24;     // use upper 8 bits for phase accu as frequency information                         // read value fron ROM sine table and send to PWM DAC  phase1 = phase0 +85 ;  phase2 = phase0 +170 ;  OCR2A=pgm_read_byte_near(sine256 + phase0);  // pwm pin 11  OCR1B=pgm_read_byte_near(sine256 + phase1);  // pwm pin 10  OCR1A=pgm_read_byte_near(sine256 + phase2);  // pwm pin 9  cbi(PORTD,testPin);              }`

#### Simyager

#6
##### Jan 06, 2017, 03:18 pm
Thank you for your answer. Don't get me wrong but those are PWM signals right?I forgot to mention I only need sine waves. I tried with square and triangle waves, but it didn't work. Still this is a good start, thanks again.

#### charliesixpack

#7
##### Jan 06, 2017, 03:42 pm
If you filter the PWM with an RC filter you will get sine waves.  PWM is digital but it is also analog in a sense.  If you filter out the high frequencies the remaining signal is analog.  This is the only way to do it on an Uno unless you use an external DAC.  The Due has built in DAC's if that is what you really want.

#### Simyager

#8
##### Jan 19, 2017, 09:36 am
I only have one question about the last code.

phase1 = phase0 +85 ;
phase2 = phase0 +170 ;

Why is it that by adding 85 and 170 you get them 120 and 240 degrees apart? Can you show me a calculation or an explanation?

#### MarkT

#9
##### Jan 19, 2017, 06:55 pmLast Edit: Jan 19, 2017, 06:56 pm by MarkT
Same reason I use 0x5555 and 0xAAAB in my posting #3.  85 = 0x55, 170 = 0xAA.

0x10000 / 3 = 0x5555.555...
0x100 / 3 = 0x55.5555...

Of course I do it accurately by having a properly sized phase accumulator.

Oh. charliesixpack, a PLL is exactly what is needed, if you know your stuff. A software PLL
in this case. DDS for the oscillator, phase detector to be determined. The tricky bit is the
feedback poles and zeroes of course.
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

#### charliesixpack

#10
##### Jan 20, 2017, 03:06 pmLast Edit: Jan 20, 2017, 03:10 pm by charliesixpack
If you use storage of one byte as the look up table index it will act as an accumulator with a modulus of 256.  A phase of 120 is one third into the index or 255/3 = 85.  A phase of 240 is an index of 170.

No PLL is needed to generate the three phases.  The 3 phases are generated by having the proper spacing between the indices of the 3 signals.

#### Simyager

#11
##### Feb 06, 2017, 08:31 amLast Edit: Feb 06, 2017, 08:44 am by Simyager
When I measure the output voltage of the 3 output pins(D9,D10,D11) I get 1,81Vac and 2.35Vdc. Does anyone have an explanation for that? Because I thought it would be near 5V.

I also programmed pin A0,A1,A2 high, because I needed other 3 5Vdc output voltages and the digital pins were programmed with the timer so I would get the PWM in those pins.

Ofcourse the A0,A1 and the A2 pins have lower voltages(4,7V instead of 5V), which is logical. But the fact that the digital pins output less than half of what I expected is a bit too much really.

#### TomGeorge

#12
##### Feb 06, 2017, 09:20 am
Hi,
You have a PWM signal that is 0 to 5V.
So you have 5Vpeak to peak.
Which is 2.5Vpeak
Which is 2.5/ (sqrt of 2) = 2.5/1.414 = 1.76Vrms
Pretty close if you ask me.
You have a 2.5V offset.

Quote
Im actually an EE student and this is a part of my final project.
What is your level of AC theory?.
I am surprised you have not put an oscilloscope on it, or would  you like us to do  that for you too?

Charliesixpack nice bit of code.

Tom....
Everything runs on smoke, let the smoke out, it stops running....

#### azeyzoul

#13
##### Feb 21, 2017, 07:28 pmLast Edit: Feb 21, 2017, 07:36 pm by azeyzoul
This is another way of creating 3 phases.

Code: [Select]
`// 3 phase PWM sine// (c) 2016 C. Masenas// Modified from original DDS from: // KHM 2009 /  Martin Nawrath// table of 256 sine values / one sine period / stored in flash memoryPROGMEM const unsigned char sine256[]  = {  127,130,133,136,139,143,146,149,152,155,158,161,164,167,170,173,176,178,181,184,187,190,192,195,198,200,203,205,208,210,212,215,217,219,221,223,225,227,229,231,233,234,236,238,239,240,  242,243,244,245,247,248,249,249,250,251,252,252,253,253,253,254,254,254,254,254,254,254,253,253,253,252,252,251,250,249,249,248,247,245,244,243,242,240,239,238,236,234,233,231,229,227,225,223,  221,219,217,215,212,210,208,205,203,200,198,195,192,190,187,184,181,178,176,173,170,167,164,161,158,155,152,149,146,143,139,136,133,130,127,124,121,118,115,111,108,105,102,99,96,93,90,87,84,81,78,  76,73,70,67,64,62,59,56,54,51,49,46,44,42,39,37,35,33,31,29,27,25,23,21,20,18,16,15,14,12,11,10,9,7,6,5,5,4,3,2,2,1,1,1,0,0,0,0,0,0,0,1,1,1,2,2,3,4,5,5,6,7,9,10,11,12,14,15,16,18,20,21,23,25,27,29,31,  33,35,37,39,42,44,46,49,51,54,56,59,62,64,67,70,73,76,78,81,84,87,90,93,96,99,102,105,108,111,115,118,121,124};#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))         int testPin = 7;int enablePin = 6 ;volatile  float freq=1;const float refclk=122.549  ;     //  16 MHz/510/256// variables used inside interrupt service declared as voilatilevolatile unsigned long sigma;   // phase accumulatorvolatile unsigned long delta;  // phase incrementbyte phase0, phase1, phase2;void setup(){  Serial.begin(9600);        // connect to the serial port  Serial.println("DDS Test");  pinMode(enablePin, OUTPUT);      // sets the digital pin as output  pinMode(testPin, OUTPUT);      // sets the digital pin as output  pinMode(9, OUTPUT);     // pin9= PWM  output / frequency output  pinMode(10, OUTPUT);     // pin10= PWM  output / frequency output  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output  Setup_timer2();  Setup_timer1();  digitalWrite(enablePin, HIGH);// the waveform index is the highest 8 bits of sigma// choose refclk as freq to increment the lsb of the 8 highest bits//    for every call to the ISR of timer2 overflow// the lsb of the 8 highest bits is 1<<24 (1LL<<24 for long integer literal)  delta = (1LL<<24)*freq/refclk ;  }void loop(){    changeFreq(20);  delay(10000);  changeFreq(25);  delay(10000);               }void changeFreq(float _freq){  cbi (TIMSK2,TOIE2);              // disable timer2 overflow detect  freq = _freq;  delta=(1LL<<24)*freq/refclk;  // update phase increment  sbi (TIMSK2,TOIE2);              // enable timer2 overflow detect} //******************************************************************// timer2 setup// set prscaler to 1,  fast PWMvoid Setup_timer2() {// Timer2 Clock Prescaler to : 1  sbi (TCCR2B, CS20);  // set  cbi (TCCR2B, CS21);  // clear  cbi (TCCR2B, CS22);  // Timer2 PWM Mode   cbi (TCCR2A, COM2A0);  // clear OC2A on Compare Match, PWM pin 11  sbi (TCCR2A, COM2A1);  // set to fast PWM  sbi (TCCR2A, WGM20);  // Mode 1, phase correct PWM  cbi (TCCR2A, WGM21);  cbi (TCCR2B, WGM22);  sbi (TIMSK2,TOIE2);              // enable overflow detect  }// timer1 setup  (sets pins 9 and 10)// set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clockvoid Setup_timer1() {// Timer1 Clock Prescaler to : 1  sbi (TCCR1B, CS10);  cbi (TCCR1B, CS11);  cbi (TCCR1B, CS12);  // Timer1 PWM Mode set to Phase Correct PWM  cbi (TCCR1A, COM1A0);  // clear OC1A on Compare Match, PWM pin 9  sbi (TCCR1A, COM1A1);  cbi (TCCR1A, COM1B0);  // clear OC1B on Compare Match, PWM pin 10  sbi (TCCR1A, COM1B1);  sbi (TCCR1A, WGM10);  // Mode 1  / phase correct PWM  cbi (TCCR1A, WGM11);  cbi (TCCR1B, WGM12);  cbi (TCCR1B, WGM13);}//******************************************************************// Timer2 Interrupt Service at 31372,550 KHz = 32uSec// this is the timebase REFCLOCK for the DDS generator// runtime : 8 microseconds ( inclusive push and pop)// OC2A - pin 11// OC1B - pin 10// OC1A - pin 9// https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWMISR(TIMER2_OVF_vect) {  sbi(PORTD,testPin);            sigma=sigma+delta; // soft DDS, phase accu with 32 bits  phase0=sigma >> 24;     // use upper 8 bits for phase accu as frequency information                         // read value fron ROM sine table and send to PWM DAC  phase1 = phase0 +85 ;  phase2 = phase0 +170 ;  OCR2A=pgm_read_byte_near(sine256 + phase0);  // pwm pin 11  OCR1B=pgm_read_byte_near(sine256 + phase1);  // pwm pin 10  OCR1A=pgm_read_byte_near(sine256 + phase2);  // pwm pin 9  cbi(PORTD,testPin);              }`
how about to generate 3 phase pwm signal without sine wave? can anyone give the code?

#### MarkT

#14
##### Feb 21, 2017, 09:04 pm
Do you mean trapezoidal drive?
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up