Arduino 2560 - Three Square Pulses - 120 degree phase shift - varying frequency

Hi everyone,

I'm working on a project where I need to generate three square pulses with a 120-degree phase shift using the PWM outputs of an Arduino 2560. The frequency should vary between 0 and 1.5 kHz, depending on the ADC values or digital inputs received.

Additionally, I would like to generate three more similar signals on another three PWM outputs.

Could anyone help me out with this?

Did your research suggest that this is even possible with the Mega2560?

If you wanted to get adventerous you could generate the phases using a 4 bit counter ic and a couple of Not and And gates.

Then vary the counter clock signal to change the putput frequency of the phases

Edit

Even easier you could use a shit register. Initially load the shift register with 10000000.

Then take the 4th bit out to the in of the shift register.

Then pulse the clock pin at the frequency you want. And your first 3 bits out of the shift registers should cycle around, with 1 bit sequanitally being high, and the other 2 low

You can get a time budget if you do the math: At 1500Hz, three square waves need 6 transitions, so 1000000us/1500/6=111us per transition.

Seems like it might be feasible with plainly coded state machine. Here's two square waves with a 180° shift for 0-20kHz:

// Quadrature Generator
// https://wokwi.com/projects/403218793081155585

// Define the output pins
const int pinA = 2;
const int pinB = 3;

// Define the state machine states
enum State {STATE0, STATE1, STATE2, STATE3};
State currentState = STATE0;

// Timing variables
unsigned long previousMicros = 0;
unsigned long interval = 357; // 357 microseconds for each state
bool on = false;
void setup() {
  // Initialize the output pins
  pinMode(pinA, OUTPUT);
  pinMode(pinB, OUTPUT);

  // Initialize the pins to a known state
  digitalWrite(pinA, LOW);
  digitalWrite(pinB, LOW);
  Serial.begin(115200);
}

void readPot(void) {
  static int lastADC = -1;
  int pot = analogRead(A0);
  if (pot != lastADC) { // change detection on potentiometer
    lastADC = pot;
    long speed = map(pot, 0, 1023, 0, 200000);
    if (speed == 0) {
      on = false;
    } else {
      on = true;
      interval = 10000000.0 / speed / 4;
      previousMicros = micros();
    }
    Serial.print(pot);
    Serial.print(", ");
    Serial.print(speed/10.0);
    Serial.print("Hz us:");
    Serial.println(interval);
  }
}



void loop() {
  // Get the current time in microseconds
  unsigned long currentMicros = micros();

  static unsigned long lastScan;
  if (millis() - lastScan > 128) {
    lastScan += 128;
    readPot();
  }

  // Check if the interval has passed
  if (on && currentMicros - previousMicros >= interval) {
    // Save the current time for the next interval
    //    previousMicros = currentMicros;
    previousMicros += interval;

    // Advance the state machine
    switch (currentState) {
      case STATE0:
        digitalWrite(pinA, HIGH);
        // digitalWrite(pinB, LOW);
        currentState = STATE1;
        break;

      case STATE1:
        //digitalWrite(pinA, HIGH);
        digitalWrite(pinB, HIGH);
        currentState = STATE2;
        break;

      case STATE2:
        digitalWrite(pinA, LOW);
        //digitalWrite(pinB, HIGH);
        currentState = STATE3;
        break;

      case STATE3:
        //digitalWrite(pinA, LOW);
        digitalWrite(pinB, LOW);
        currentState = STATE0;
        break;
    }
  }
}

You could use a CD4017B as a divide by 3 counter and drive it with any reasonable frequency.

Why a pwm pin if you want a square wave?

try using timer interrupts
e.g. using an ESP32

// ESP32  1KHz three phase square wave

#define phase1 19  // signal output pins
#define phase2 18
#define phase3 17

hw_timer_t *timer = NULL;   // hardware timer 

volatile int counter = 0;  // interrupt counter
void ARDUINO_ISR_ATTR onTimer() {
  static byte state = 0;  // determines which phase to invert
  if (state == 0) digitalWrite(phase1, !digitalRead(phase1)); // invert phase 1
  if (state == 1) digitalWrite(phase2, !digitalRead(phase2));
  if (state == 2) digitalWrite(phase3, !digitalRead(phase3));
  if (++state >= 3) state = 0;    // reset state ?
  counter++;
}

void setup() {
  Serial.begin(115200);
  pinMode(phase1, OUTPUT);
  pinMode(phase2, OUTPUT);
  pinMode(phase3, OUTPUT);
  // setup timer interrupts for 1KHz three phase
  timer = timerBegin(10000000);           // Set timer frequency to 10Mhz
  timerAttachInterrupt(timer, &onTimer);  // Attach onTimer function to our timer.
  // Set alarm to call onTimer function every second (value in 10 microseconds).
  // Repeat the alarm (third parameter) with unlimited count = 0 (fourth parameter).
  timerAlarm(timer, 5000 / 3, true, 0);
}

// display interrupt coun ter every seconds
void loop() {
  static unsigned long timert = millis();
  if (millis() - timert >= 1000) {
    Serial.println(counter);
    counter = 0;
    timert = millis();
  }
}

oscilloscope displays
image