Sida 1 av 1

3fas PWM med arduino

Postat: 17 september 2011, 11:38:22
av Tekko
Då jag e helt grön och inte vet vad jag ska söka på så ställer jag frågan här:

Hur får man till 3st 120 grader från varann faser ut, altså det är 3st sinusar efter filtrering jag menar, vad ändrar/lägger man till i denna kod ?

Kod: Markera allt

#include "avr/pgmspace.h"

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM  prog_uchar 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 ledPin = 13;                 // LED pin 7
int testPin = 7;
int t2Pin = 6;
byte bb;

double dfreq;
// const double refclk=64000;  // =16MHz / 250
const double refclk=64000;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt;              // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
  Serial.begin(115200);        // connect to the serial port
  Serial.println("DDS Test");

  pinMode(6, OUTPUT);      // sets the digital pin as output
  pinMode(7, OUTPUT);      // sets the digital pin as output
  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output
  pinMode(12, OUTPUT);     // pin12= PWM  output / frequency output +120
  pinMode(13, OUTPUT);     // pin13= PWM  output / frequency output +240
  
  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt

  dfreq=1000.0;                    // initial output frequency = 1000.o Hz
  tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 

}
void loop()
{
  while(1) {
     if (c4ms > 250) {                 // timer / wait fou a full second
      c4ms=0;
      dfreq=analogRead(0);             // read Poti on analog pin 0 to adjust output frequency from 0..1023 Hz

      cbi (TIMSK2,TOIE2);              // disble Timer2 Interrupt
      tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 

      Serial.print(dfreq);
      Serial.print("  ");
      Serial.println(tword_m);
    }

   sbi(PORTD,6); // Test / set PORTD,7 high to observe timing with a scope
   cbi(PORTD,6); // Test /reset PORTD,7 high to observe timing with a scope
  }
 }
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/250 = 64000 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

// Timer2 Interrupt Service at 64000 Hz = 15.625uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {

  sbi(PORTD,7);          // Test / set PORTD,7 high to observe timing with a oscope

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  icnt=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
  OCR2A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;
   }   

 cbi(PORTD,7);            // reset PORTD,7
 }

Re: 3fas PWM med arduino

Postat: 17 september 2011, 11:48:42
av tecno
Kanske detta kan vägleda, sökte med google

http://www.arduino.cc/cgi-bin/yabb2/YaB ... 1244372691

Re: 3fas PWM med arduino

Postat: 17 september 2011, 12:08:22
av Tekko
Det där är en vanlig 6-stegs trapetzoid drift vilket EJ är det jag söker.

Re: 3fas PWM med arduino

Postat: 17 september 2011, 12:16:34
av sodjan
> Hur får man till 3st 120 grader från varann faser ut,

"I princip" är det en tabell med sinus-värden och tre pekare
som ligger med 120 graders förskjutning i tabellen. Sedan läser
du ut värden till utgångarna på lämpligt sätt (d.v.s "som vanligt").
Så skillnaden mot en lösning med en sinus är tre pekare istället för en.

Re: 3fas PWM med arduino

Postat: 17 september 2011, 12:31:21
av Tekko
Jag antar då att detta är pekaren i detta fallet

Kod: Markera allt

OCR2A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;
   }   

Re: 3fas PWM med arduino

Postat: 17 september 2011, 13:15:39
av Tekko
Verkár detta vettigt ? Tog höfsta värdet i sine lookup tabellen och delade med 3.

Kod: Markera allt

OCR1A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;

OCR2A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt2++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt2=84;

OCR3A=pgm_read_byte_near(sine256 + icnt);    

  if(icnt3++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt3=170;

Re: 3fas PWM med arduino

Postat: 17 september 2011, 13:53:34
av sodjan
Nejm, dete tror jag inte.
icnt1 är väl till för att få rätt *frekvens*.
Du behöver 3 st icnt.

(Skit variabelnamn dessutom).

Sen så framgår det ju inte vad de andra prylarna är.
T.ex:
sin256 (sannolikt startadressen för sinus-tabellen).
icnt (sannolikt pekaren in i sinus-tabellen, den du behöver tre av).
icnt1 (sannolikt en räknare för att få 4 ms intervall, borde fortfarande endast behövas en).
c4ms (inte en susning vad den är till för...).

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:09:20
av Tekko
Nåt sånnt här kanske:

Kod: Markera allt

OCR1A=pgm_read_byte_near(sine256 + icnt);    

  if(icntl++ == 0) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icntl=0;
}

OCR1B=pgm_read_byte_near(sine256 + icnt);    

  if(icntl++ == 84) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icntl=0;
}

OCR2A=pgm_read_byte_near(sine256 + icnt);    

  if(icntl++ == 170) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icntl=0;

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:19:41
av sodjan
Nej, det ger tre signaler med 3 olika frekvens, det var
väl inte det du skulle ha ? Om jag fattar koden rätt...

Snarare något i denna stil (ungefär, om jag har förstått koden rätt, som sagt) :

Kod: Markera allt

OCR1A=pgm_read_byte_near(sine256 + icnt_1);   
OCR1B=pgm_read_byte_near(sine256 + icnt_2);   
OCR2A=pgm_read_byte_near(sine256 + icnt_3);   

  if(icntl++ == 0) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icntl=0;
}
icnt_1 - icnt_3 ska initialiseras någonstans innan denna kod för att
få rätt fasförhållande. Kanske med 0, 84 resp 170. Se det som tre pekare
som pekar på var sitt ställe i tabellen med samma intervall hela tiden.

Sedan så måste icnt_1 - icnt_3 också jämföras med "slut-av-tabellen"
(sannolikt sine256+256) så att de kan ställas tillbaka till början igen.
Det verkar också göras någon annanstans.

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:21:24
av Tekko

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:31:58
av sodjan
OK. så icnt är en "byte" och kan så klart bara adressera 256 bytes i tabellen.
Den "wrap around" med automatik, så att säga.

*Kanske* något i denna stil, du får testa själv :

Kod: Markera allt

// variables used inside interrupt service declared as voilatile
volatile byte icnt_1;            // var inside interrupt
volatile byte icnt_2;            // var inside interrupt
volatile byte icnt_3;            // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile unsigned long phaccu;   // pahse accumulator
volatile unsigned long tword_m;  // dds tuning word m

Kod: Markera allt

  phaccu=phaccu+tword_m;       // soft DDS, phase accu with 32 bits
  icnt_1=phaccu     >> 24;     // use upper 8 bits for phase accu as frequency information
  icnt_2=phaccu+84  >> 24;     // use upper 8 bits for phase accu as frequency information
  icnt_3=phaccu+170 >> 24;     // use upper 8 bits for phase accu as frequency information

                               // read value fron ROM sine table and send to PWM DAC
  OCR1A=pgm_read_byte_near(sine256 + icnt_1);
  OCR1B=pgm_read_byte_near(sine256 + icnt_2);
  OCR2A=pgm_read_byte_near(sine256 + icnt_3);     

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:40:00
av Tekko
Med det avklarat, nu e bara frågan hur man adresserar dessa till varsin utgångsport, eller sker det per automatik ?

Kod: Markera allt

#include "avr/pgmspace.h"

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM  prog_uchar 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 ledPin = 13;                 // LED pin 7
int testPin = 7;
int t2Pin = 6;
byte bb;

double dfreq;
// const double refclk=64000;  // =16MHz / 250
const double refclk=64000;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte icnt_1;             // var inside interrupt
volatile byte icnt_2;             // var inside interrupt
volatile byte icnt_3;             // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile unsigned long phaccu;   // phase accumulator
volatile unsigned long tword_m;  // dds tuning word m

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
  Serial.begin(115200);        // connect to the serial port
  Serial.println("DDS Test");

  pinMode(6, OUTPUT);      // sets the digital pin as output
  pinMode(7, OUTPUT);      // sets the digital pin as output
  pinMode(11, OUTPUT);     // pin11= PWM  output / frequency output
  pinMode(12, OUTPUT);     // pin12= PWM  output / frequency output +120
  pinMode(13, OUTPUT);     // pin13= PWM  output / frequency output +240
  
  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt

  dfreq=1000.0;                    // initial output frequency = 1000.0 Hz
  tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 

}
void loop()
{
  while(1) {
     if (c4ms > 250) {                 // timer / wait fou a full second
      c4ms=0;
      dfreq=analogRead(0);             // read Poti on analog pin 0 to adjust output frequency from 0..1023 Hz

      cbi (TIMSK2,TOIE2);              // disble Timer2 Interrupt
      tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 

      Serial.print(dfreq);
      Serial.print("  ");
      Serial.println(tword_m);
    }

   sbi(PORTD,6); // Test / set PORTD,7 high to observe timing with a scope
   cbi(PORTD,6); // Test /reset PORTD,7 high to observe timing with a scope
  }
 }
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/250 = 64000 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

// Timer2 Interrupt Service at 64000 Hz = 15.625uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {

  sbi(PORTD,7);          // Test / set PORTD,7 high to observe timing with a oscope

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  icnt_1=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
  icnt_2=phaccu+84 >> 24;    // use upper 8 bits for phase accu as frequency information
  icnt_3=phaccu+170 >> 24;   // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
OCR1A=pgm_read_byte_near(sine256 + icnt_1);    
OCR1B=pgm_read_byte_near(sine256 + icnt_2);    
OCR2A=pgm_read_byte_near(sine256 + icnt_3);    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;  
 } 
    
 cbi(PORTD,7);            // reset PORTD,7
 }

Re: 3fas PWM med arduino

Postat: 17 september 2011, 14:48:26
av sodjan
> Med det avklarat,

Hur kan du vara så säkert på att det är avklarat ?
Det är rena gissningar från min sida som du måste verifiera.

> nu e bara frågan hur man adresserar dessa till varsin utgångsport, eller sker det per automatik ?

Det finns dokumentation som tar upp dom detaljerna.

Re: 3fas PWM med arduino

Postat: 17 september 2011, 15:40:59
av Tekko
Nu fattas bara att skaffa hem en Arduino Duemilanove, brassa in koden å se om skiten funkar.

Koden kompilerar iaf utan fel:

Kod: Markera allt

#include "avr/pgmspace.h"

// table of 256 sine values / one sine period / stored in flash memory
PROGMEM  prog_uchar 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 ledPin = 13;                 // LED pin 7
int testPin = 7;
int t2Pin = 6;
byte bb;

double dfreq;
// const double refclk=64000;  // =16MHz / 250
const double refclk=64000;      // measured

// variables used inside interrupt service declared as voilatile
volatile byte U;             // var inside interrupt
volatile byte V;             // var inside interrupt
volatile byte W;             // var inside interrupt
volatile byte icnt1;             // var inside interrupt
volatile byte c4ms;              // counter incremented all 4ms
volatile unsigned long phaccu;   // phase accumulator
volatile unsigned long tword_m;  // dds tuning word m

const int phaseout = 11;
const int phaseout2 = 12;
const int phaseout3 = 13;

void setup()
{
  pinMode(ledPin, OUTPUT);      // sets the digital pin as output
  Serial.begin(115200);        // connect to the serial port
  Serial.println("DDS Test");
  
  pinMode(6, OUTPUT);      // sets the digital pin as output
  pinMode(7, OUTPUT);      // sets the digital pin as output
  pinMode(phaseout, OUTPUT);     // pin11= PWM  output / frequency output U
  pinMode(phaseout2, OUTPUT);    // pin12= PWM  output / frequency output V
  pinMode(phaseout3, OUTPUT);    // pin13= PWM  output / frequency output W
  
  Setup_timer2();

  // disable interrupts to avoid timing distortion
  cbi (TIMSK0,TOIE0);              // disable Timer0 !!! delay() is now not available
  sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt

  dfreq=1000.0;                    // initial output frequency = 1000.0 Hz
  tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word 

}
void loop()
{
  while(1) {
     if (c4ms > 250) {                 // timer / wait fou a full second
      c4ms=0;
      dfreq=analogRead(0);             // read Poti on analog pin 0 to adjust output frequency from 0..1023 Hz

      cbi (TIMSK2,TOIE2);              // disble Timer2 Interrupt
      tword_m=pow(2,32)*dfreq/refclk;  // calulate DDS new tuning word
      sbi (TIMSK2,TOIE2);              // enable Timer2 Interrupt 

      Serial.print(dfreq);
      Serial.print("  ");
   
     Serial.println(tword_m);
     }
// three phase PWM outputs
for (int OCR1A; OCR1B; OCR2A) {
analogWrite(phaseout, OCR1A); (phaseout2, OCR1B); (phaseout3, OCR2A);
  }
   

   sbi(PORTD,6); // Test / set PORTD,7 high to observe timing with a scope
   cbi(PORTD,6); // Test /reset PORTD,7 high to observe timing with a scope
  }
 }
// timer2 setup
// set prscaler to 1, PWM mode to phase correct PWM,  16000000/250 = 64000 Hz clock
void Setup_timer2() {

// Timer2 Clock Prescaler to : 1
  sbi (TCCR2B, CS20);
  cbi (TCCR2B, CS21);
  cbi (TCCR2B, CS22);

  // Timer2 PWM Mode set to Phase Correct PWM
  cbi (TCCR2A, COM2A0);  // clear Compare Match
  sbi (TCCR2A, COM2A1);

  sbi (TCCR2A, WGM20);  // Mode 1  / Phase Correct PWM
  cbi (TCCR2A, WGM21);
  cbi (TCCR2B, WGM22);
}

// Timer2 Interrupt Service at 64000 Hz = 15.625uSec
// this is the timebase REFCLOCK for the DDS generator
// FOUT = (M (REFCLK)) / (2 exp 32)
// runtime : 8 microseconds ( inclusive push and pop)
ISR(TIMER2_OVF_vect) {

  sbi(PORTD,7);          // Test / set PORTD,7 high to observe timing with a oscope

  phaccu=phaccu+tword_m; // soft DDS, phase accu with 32 bits
  U=phaccu >> 24;     // use upper 8 bits for phase accu as frequency information
  V=phaccu+84 >> 24;    // use upper 8 bits for phase accu as frequency information
  W=phaccu+170 >> 24;   // use upper 8 bits for phase accu as frequency information
                         // read value fron ROM sine table and send to PWM DAC
OCR1A=pgm_read_byte_near(sine256 + U);    
OCR1B=pgm_read_byte_near(sine256 + V);    
OCR2A=pgm_read_byte_near(sine256 + W);    

  if(icnt1++ == 125) {  // increment variable c4ms all 4 milliseconds
    c4ms++;
    icnt1=0;  
 } 
    
 cbi(PORTD,7);            // reset PORTD,7
 }
Jag antar att det är de OCRxA/B som representerar PWM utgångarna.