Go Down

Topic: DDS - 3phase signals 120 degrees out of phase (Read 4195 times)previous topic - next topic

gnusmas

Sep 06, 2012, 12:50 pm
Hi

I just completed a project of mine that generates 3 Sine waves 120 degrees out of phase. It took my hours to understand how to generate the sine tables and so forth. Thus I commented in as much detail as possible for future programmers to understand and grasp the idea quicker.

Code:

Code: [Select]
`/* * * DDS Sine Generator mit ATMEGS 328 * Timer2 generates the  31250 KHz Clock Interrupt * Use Timer2 Interrupt to change duty cycle for the output PWM signals * D. Tolken * 120 degress out of phase signals for 3 phase BLDC motor controller * CPUT, South Africaa Huge thumbs up and thanks must be given to Martin Nawrath for the developement of the original code to generate a sine wave using PWM and a LPF.Link:                                         http://interface.khm.de/index.php/lab/experiments/arduino-dds-sinewave-generator/*/#include "avr/pgmspace.h" //Store data in flash (program) memory instead of SRAM// Look Up table of a single sine period divied up into 256 values. Refer to PWM to sine.xls on how the values was calculatedPROGMEM  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 a bit to have the properties of a clear bit operator#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))//define a bit to have the properties of a set bit operatorint PWM1= 11;// PWM1 output, phase 1int PWM2 = 3; //[WM2 ouput, phase 2int PWM3 = 10; //PWM3 output, phase 3int offset_1 = 85; //offset 1 is 120 degrees out of phase with previous phase, Refer to PWM to sine.xlsint offset_2 = 170; //offset 2 is 120 degrees out of phase with offset 1. Refer to PWM to sine.xlsint program_exec_time = 6; //monitor how quickly the interrupt triggerint ISR_exec_time = 7; //monitor how long the interrupt takesdouble dfreq;const double refclk=31376.6;      // measured output frequency// variables used inside interrupt service declared as voilatilevolatile byte current_count;              // Keep track of where the current count is in sine 256 arrayvolatile byte ms4_delay;             //variable used to generate a 4ms delayvolatile byte c4ms;              // after every 4ms this variable is incremented, its used to create a delay of 1 secondvolatile unsigned long phase_accumulator;   // pahse accumulatorvolatile unsigned long tword_m;  // dds tuning word m, refer to DDS_calculator (from Martin Nawrath) for explination.void setup(){  pinMode(PWM1, OUTPUT);      //sets the digital pin as output  pinMode(PWM2, OUTPUT);      //sets the digital pin as output  pinMode(PWM3, OUTPUT);      //sets the digital pin as output  pinMode(program_exec_time, OUTPUT);      //sets the digital pin as output  pinMode(9, OUTPUT);         //sets the digital pin as output  sbi(PORTD,program_exec_time); //Sets the pin  Setup_timer1();  Setup_timer2();    //Disable Timer 1 interrupt to avoid any timing delays  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)   {      sbi(PORTD,program_exec_time); //Sets the pin       if (c4ms > 250) // c4ms = 4ms, thus 4ms *250 = 1 second delay       {                         c4ms=0;                          //Reset c4ms        dfreq=analogRead(0);             //Read voltage on analog 1 to see desired output frequency, 0V = 0Hz, 5V = 1.023kHz        cbi (TIMSK2,TOIE2);              //Disable Timer2 Interrupt        tword_m=pow(2,32)*dfreq/refclk;  //Calulate DDS new tuning word        sbi (TIMSK2,TOIE2);              //Enable Timer2 Interrupt       }  }}//Timer 1 setup//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clockvoid Setup_timer1(void){  // 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);  sbi (TCCR1A, COM1A1);  cbi (TCCR1A, COM1B0);   sbi (TCCR1A, COM1B1);  // Mode 1 / Phase Correct PWM  sbi (TCCR1A, WGM10);   cbi (TCCR1A, WGM11);  cbi (TCCR1B, WGM12);  cbi (TCCR1B, WGM13);}//Timer 1 setup//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clockvoid 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);  cbi (TCCR2A, COM2B0);   sbi (TCCR2A, COM2B1);    // Mode 1  / Phase Correct PWM  sbi (TCCR2A, WGM20);    cbi (TCCR2A, WGM21);  cbi (TCCR2B, WGM22);}//Timer2 Interrupt Service at 31372,550 KHz = 32uSec//This is the timebase REFCLOCK for the DDS generator//FOUT = (M (REFCLK)) / (2 exp 32)//Runtime : 8 microsecondsISR(TIMER2_OVF_vect){  cbi(PORTD,program_exec_time); //Clear the pin  sbi(PORTD,ISR_exec_time);          // Sets the pin  phase_accumulator=phase_accumulator+tword_m; //Adds tuning M word to previoud phase accumulator. refer to DDS_calculator (from Martin Nawrath) for explination.  current_count=phase_accumulator >> 24;     // use upper 8 bits of phase_accumulator as frequency information                          OCR2A=pgm_read_byte_near(sine256 + current_count); // read value fron ROM sine table and send to PWM  OCR2B=pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_1)); // read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM1    OCR1A = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2  OCR1B = pgm_read_byte_near(sine256 + (uint8_t)(current_count + offset_2));// read value fron ROM sine table and send to PWM, 120 Degree out of phase of PWM2    //increment variable ms4_delay every 4mS/125 =  milliseconds 32uS  if(ms4_delay++ == 125)   {      c4ms++;    ms4_delay=0; //reset count   }    cbi(PORTD,ISR_exec_time);            //Clear the pin}`

Sine wave table calculater: PWM to sine.xlsx
DDS Frequency Calc (Not My Document, helps to explain): dds_calc.xls
Measured Results: Output.bmp

Cheers

AWOL

#1
Sep 06, 2012, 03:31 pm
I suspect your phase accumulator handling in the ISR could be improved, if the compiler hasn't already done it for you.
That 24 place shift will probably be slow, so extracting the eight bit integer part of the fixed-point accumulator might be quicker.
Might be worth a peek at the generated code.
"Pete, it's a fool looks for logic in the chambers of the human heart." Ulysses Everett McGill.
Do not send technical questions via personal messaging - they will be ignored.

DuaneB

#2
Sep 08, 2012, 02:04 pm
Quote
That 24 place shift will probably be slow, so extracting the eight bit integer part of the fixed-point accumulator might be quicker.

Good idea, I have a few spots that might benefit from a union in a similar application.

Duane B

rcarduino.blogspot.com
http://rcarduino.blogspot.com/2012/04/servo-problems-with-arduino-part-1.html
then watch this
http://rcarduino.blogspot.com/2012/04/servo-problems-part-2-demonstration.html

Rcarduino.blogspot.com

tmd3

#3
Sep 14, 2012, 11:49 pm
If we're looking for ways to save time, I'll suggest declaring ISR_exec_time and program_exec_time as const.  It's surprising how many cycles it takes to do a cbi() or sbi() with a variable bit number.  With a const bit number, those collapse to single-byte cbi and sbi instructions.

I think there could be additional improvement - but maybe not much - from declaring current_count to be local to the ISR.  It's not used outside the ISR.  It's volatile, so it has to be read or stored each time it's used; if it was a local variable, it might just stay in regsiters, and never make it to memory at all.

dab77

#4
Sep 16, 2012, 01:11 pmLast Edit: Sep 16, 2012, 01:13 pm by dab77 Reason: 1
Quote
//Disable Timer 1 interrupt to avoid any timing delays
cbi (TIMSK0,TOIE0);              //disable Timer0 !!! delay() is now not available
sbi (TIMSK2,TOIE2);              //enable Timer2 Interrupt

what did you mean here?

Quote
//Timer 1 setup
//Set prscaler to 1, PWM mode to phase correct PWM,  16000000/510 = 31372.55 Hz clock
void Setup_timer2()
{
// Timer2 Clock Prescaler to : 1

It seems like there's the need of some code-cleaning..

of course, other than that, that's very interesting!!

#5
Mar 19, 2014, 08:31 am
i want to generate 6 sine wave because i want o fire six pluse igbt so how can i do that??

Go Up

Please enter a valid email to subscribe