Go Down

Topic: PWM for AC asynchronous motors (Read 1 time) previous topic - next topic

simon_r

Feb 05, 2015, 09:00 pm Last Edit: Feb 05, 2015, 09:18 pm by simon_r
Hey everybody

I'm having a problem with programming. I am trying to make a PWM controlled drive for AC motors. In fact I want my arduino to sent out the pulses to a driver and IGBT's (that I'm going to try to reuse from an old AC drive). So I made a triangular carrier function that I compared with a sine function. I wrote this data to an SD card in csv format for logging in Excel. I will add the file below. The PWM I became already looks quite good but now I'm having the next problem. In my program I never really use a real 'time' because I don't really know how. Until now I just used the variable 'i' for both the triangular function and the sine function. So now the 'real life' frequency is just dependent on the time that is needed for each loop. I would like to try to replace 'i' by the time (let's call it 't') because in fact the sine function should have frequencies between 0 and let's say 70Hz. I don't know if anyone has any suggestions or tips for doing this because I'm a little bit stuck...

Thanks in advance,
An electrical engineer exploring the Arduino world!

simon_r

//I added the code below. Notice that the SD card is only used during this test phase, afterwards it will be //of no use

#include <SD.h>
int CS_pin=10;
int pow_pin=8;
int counter=0;


void setup(){
Serial.begin(9600);

Serial.print("Initializing SD card...");
pinMode(CS_pin, OUTPUT);
pinMode(pow_pin,OUTPUT);
digitalWrite(pow_pin,HIGH);
 
if (!SD.begin(CS_pin)) {
    Serial.println("Card failed, or not present");
    return;}
  Serial.println("card initialized.");
}

void loop (){
 
String dataString = "";
const int out =1;
const float pi =3.14;
pinMode(out, OUTPUT);

int y_max=3, test, n=1000, m_f=10;
float y=0.0, a=2.0, f_sin=1.0/n, f_tri=f_sin*m_f;
float z,tri_ampl=y_max/2, sin_ampl=1, mod_ampl = sin_ampl/tri_ampl;
int T_tri_int = (int) 1/f_tri;


/*Stel dat we nu ipv met x te werken, steeds via i werken, bv van i= 0 tot i <1000, dan hebben we 1000 punten berekend*/
for(int i=0; i<n;i++)
{ counter=counter++;
  y = y_max*abs((i%T_tri_int)*(a*f_tri)-1.0);
  z= y_max/2+sin(2*pi*f_sin*i);
  if (y<z) {digitalWrite(out,HIGH); test =1;}
  else     {digitalWrite(out,LOW); test =0;}

  /*Serial.print("i:  "); Serial.println(i);
  Serial.print("x_act:  "); Serial.println(x_act);
  Serial.print("y_act:  "); Serial.println(y_act);
  Serial.print("z:  "); Serial.println(z);
*/
  dataString += String(counter);
  dataString += ",";
  dataString += String(i);
  dataString += ",";
  dataString += String((int)(y*1000.0));
  dataString += ",";
  dataString += String((int)(z*1000.0));
  dataString += ",";
  dataString += String(test);
  dataString += ",";   
 
  File dataFile = SD.open("datalog7.txt", FILE_WRITE);
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    Serial.println(dataString);}
  else { Serial.println("error opening datalog.txt");  }   
  dataString="";
 
}

}

-dev

Quote
I would like to try to replace 'i' by the time (let's call it 't')
I think you're asking about timers.  millis() and micros() will be your best friends:

    uint32_t t;
    ...
    t = millis();  // elapsed time in milliseconds, or
    t = micros();


You should probably move most of the code before the for statement into the setup() function.  Then your for loop will become the main loop(), and it will test 't' as it becomes t0 + 5ms, t0 + 10ms, etc. (for 200HZ samples). 

Code: [Select]
uint32_t dt = 1;
uint32_t next_time;

const int out =1;
const float pi =3.14;

int y_max=3, test, n=1000, m_f=10;
float y=0.0, a=2.0, f_sin=1.0/n, f_tri=f_sin*m_f;
float z,tri_ampl=y_max/2, sin_ampl=1, mod_ampl = sin_ampl/tri_ampl;
int T_tri_int = (int) 1/f_tri;

int i = 0;

void setup()
{
  Serial.begin(9600);

  Serial.print("Initializing SD card...");
  pinMode(CS_pin, OUTPUT);
  pinMode(pow_pin,OUTPUT);
  digitalWrite(pow_pin,HIGH);
 
  if (!SD.begin(CS_pin)) {
      Serial.println("Card failed, or not present");
  } else {
      Serial.println("card initialized.");
  }

  pinMode(out, OUTPUT);

  // Calculate dt based on freg?  Freq * samples?
  dt = ???;

  next_time = millis();  // This is t0
}


void loop()
{
  //  The timer functions will roll over to zero, so you have to
  //  subtract and cast to a signed int, then compare to zero.

  while ( ((int32_t) (next_time - millis())) > 0)
      ; // wait...

  // Time to do something now...

  y = y_max * abs( (i%T_tri_int) * (a*f_tri) - 1.0 );
  z = y_max/2 + sin( 2*pi * f_sin * i );

  if (y<z) {digitalWrite(out,HIGH); test =1;}
  else     {digitalWrite(out,LOW); test =0;}

  //  Get ready for the next interval...
  next_time += dt;

  //  Make i roll over to zero
  i++;
  if (i >= n)
    i = 0;

}


Also remember that SD and Serial operations take a long time, so you can't do those things at the same time.  For testing, you can set your frequency lower and confirm the logic and calculations are correct.  Then remove the Serial and SD stuff and set your frequency back up.

You should also learn about using the PWM functions that let the Arduino generate a waveform in the background.  It's only good for fairly constant timings, though.

Hope that helps!
/dev

P.S.  Please put your code in a [ code ] [ /code ] section.  See item 7 here, among others.
Really, I used to be /dev.  :(

simon_r

OK, thanks a lot for your help!

But I do have some questions.

1/ What exactly is a uint32_t? And why do we have to use for time? Is it because millis() can become a very big number?

2/
Quote
You should also learn about using the PWM functions that let the Arduino generate a waveform in the background.  It's only good for fairly constant timings, though.
Could you explain me a little bit more because I don't really see what you're pointing at? :)

Simon

MorganS

uint32_t is an abbreviation for "unsigned integer, 32 bits". In normal C code this would be written "unsigned long" but because the C standard doesn't explicitly say there are 32 bits in a long, Arduino programmers use the abbreviation because it is explicit.

It is unsigned because time can never be negative. It is long because you might want to measure a lot of time, not just 65,536 milliseconds (which would be the maximum for a 16-bit unsigned integer.)

The Arduino (more specifically the ATMEL ATMega328) has a lot of hardware specifically built to do exactly what you want to do. It has a "waveform generation" mode that allows enormous control over the output with only a few instructions in your code. However the hardware has so many different things it can do, the standard Arduino functions don't use most of them. You will have to read the datasheet for the ATMega and write your own code to configure the PWM hardware exactly how you want.
"The problem is in the code you didn't post."

MarkT

Here's how I do this sort of thing:

1) Setup enough timers running in lock-step to generate the necessary
signals (usually 3 or 6 signals for AC motors).

2) use one of the timer's overflow ISR to copy global variables into the
OCR registers of the timers to set the duty cycle of every signal on every
cycle.

3) programmatically update the global values as needed (with interrupts
disabled).  The values will be generated by looking up phase values
in a sine table (and optionally multiplying by an amplitude).

Note with a 3-wire 3-phase AC motor you have to use synchronous PWM
clocking as they are not independent - all 3 phases are actively driven
HIGH or LOW all the time (never float as in trapezoidal BLDC mode).

It is not the duty cycle of a phase that matters, its the difference in duty
cycles between phases that matters.  The idle state is all 3 phases at
50% duty cycle in perfect lock step (no voltage difference between
windings).
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

Go Up