PWM for AC asynchronous motors

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!

test_PWM.ino (1.66 KB)

test.doc (128 KB)

//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_maxabs((i%T_tri_int)(af_tri)-1.0);
z= y_max/2+sin(2
pif_sini);
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="";

}

}

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

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.

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/

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? :slight_smile:

Simon

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.

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