Optimizing SPWM code to run in one clock cycle

I found this great article on SPWM inverters where they used a (albeit faster) FPGA system to generate a SPWM signal to convert DC into AC. I wanted to do the same thing with an Arduino, and my code (which is based off of their approach) can be found here:

// Credit for procedures in code goes to the 2008 paper
// "Digital SPWM synthesis for the design of single phase inverters" 
// By Lucien Ngalamou and Leary Myers

#define A1_PIN 5
#define A2_PIN 4
#define B1_PIN 3
#define B2_PIN 2

#define CLK_SPEED 16000000.0 //MAY NOT BE 16MHZ EXACT
#define FREQUENCY 500.0
#define PULSE_COUNT 512
#define OUTPUT_VOLTAGE_PERCENTAGE 100.0

long N = round(1/(2*FREQUENCY*PULSE_COUNT*(1/CLK_SPEED)));
long m = 0;
long D_m = 0;
long cnt = 0;

long sin_lookup[PULSE_COUNT];

void setup() {
  pin_init();
  generate_lookups();
}

void loop() {
  noInterrupts();

  if(cnt==N){
    cnt=0;
    m%=PULSE_COUNT;
    m++;
    
    D_m=sin_lookup[m];
  }
  
  if(D_m > 0){
    digitalWrite(B1_PIN, LOW);
    digitalWrite(B2_PIN, LOW);
    digitalWrite(A1_PIN, HIGH);
    digitalWrite(A2_PIN, HIGH);
    D_m--;
  }
  else{
    digitalWrite(A1_PIN, LOW);
    digitalWrite(A2_PIN, LOW);
    digitalWrite(B1_PIN, HIGH);
    digitalWrite(B2_PIN, HIGH);
  }
  
  cnt++;
}

void generate_lookups(){
  for(long i=0; i<PULSE_COUNT; i++){
    sin_lookup[i]=round((OUTPUT_VOLTAGE_PERCENTAGE/100)*N*sin((i*PI)/PULSE_COUNT));
  }
}

void pin_init(){
  pinMode(A1_PIN, OUTPUT);
  pinMode(A2_PIN, OUTPUT);
  pinMode(B1_PIN, OUTPUT);
  pinMode(B2_PIN, OUTPUT);
}

The A1, A2, B1, and B2 pins correspond to the sets of diagonals on an h bridge where I have connected a resistor and capacitor to an oscilloscope to measure the readings.

Its a nice sine wave, however the frequency is not 1:1 with what is in my code, and I suspect that this is because the code in my loop() function is not being executed in one clock cycle exactly.

How could I better optimize the section contained in loop() to run faster, other than getting a faster Arduino? (I'm currently using an Arduino mega with a 16MHz clock)

??

Typically, one machine instruction is executed in one clock cycle. The loop() function might be translated into dozens or hundreds of machine instructions.

digitalWrite() alone takes around 4 microseconds, if I recall correctly, or about 64 machine instructions.

Any of the timers can be used to generate PWM signals without intervention of the processor.

oh hey, you helped me with my mpu6050, what's going on jreminton?

anyways, the paper I was referencing used a 'reference clock' in their calculations, which every cycle was based off of said reference clock. If my code takes multiple cycles to execute what would be expected to complete in a single cycle (or close to it), it throws off my calculations. whereas the expected clock frequency is 16MHz, in actuality it might be operating closer to 8Mhz.

And while I could start testing out different clock frequencies to find which best suites my code, I think I should try optimizing it first. I think its unlikely ill be able to get it to one clock cycle exactly, but I know at the very least more could be done to get it closer to said margin.

Select all of your diagonals pins on the same port of controller (for example pins 6, 7, 8, 9 - al on the PORT_H of the atmega2560).

It give you ability to write to the four pins in one-two MC instruction:

#define A1_PIN 6  //PH3
#define A2_PIN 7  //PH4
#define B1_PIN 8  //PH5
#define B2_PIN 9  //PH6

DDRH |= 0x00011110;  // setup pins PH3-6 as OUTPUT

PORTH |= 0x00011000; // set PH3 PH4 as HIGH, others unchanged
PORTH &= 0x11111001; // set PH5 PH6 as LOW, others unchanged




As mentioned above, instead of digitalWrite(), use direct port access (look that up for the details).

Example, instead of digitalWrite(4,1) on an Arduino Uno, use PORTD |= 1<<4;

The latter executes in one machine cycle, about 64x faster.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.