Help needed: generation of 2 PWM outputs using the same Timer

Hi to Everybody!
I am experiencing an issue with the generation of 2 PWM outputs (180deg out of phase from each other) using the same timer.
I am disclosing my code here with as many comments as I could.
Any help from any of you with more experience would be greatly appreciated!!!

void setup() {
/* Setting up 2 output pins for the same Timer /
DDRE = (1 << DDE3); // OC3A: (pin D5)
DDRE = (1 << DDE4); // OC3B: (pin D2)
/
Resetting Timer3 Control Registers A & B /
TCCR3A = 0;
TCCR3B = 0;
/
Resetting timer to 0 /
TCNT3 = 0;
/
Setting-up timer TOP (ICR3) and compare match values /
ICR3 = 500;
OCR3A = 250;
OCR3B = 250;
/
Setting waveform generation mode 14 (Fast PWM, TOP = ICR3) /
TCCR3A |= (1<<WGM31);
TCCR3B |= (1<<WGM33) | (1<<WGM32);
/
Setting prescaler to 1 /
TCCR3B |= (1<<CS30);
/
Setting -up Compare Output Modes:
- inverting for one of the output pins
- non-inverting for the other output pin
*/
TCCR3A |= (1<<COM3A1) | (1<<COM3B0) | (1<<COM3B1);
}
void loop() {
}

The code above was generated and tried on an Arduino Mega2560 Rev3.
However, I tried it also on 2 different Arduino UNO after minor changes (including using Timer1, and corresponding OC1A/OC1B pins)... with exactly the same outcome.

The outcome I am referring to is that the output on OC3A is as expected, however the one on OC3B shows little to no voltage amplitude (average of 0.5V) in phase with OC3B. This is unexpected because I was hoping to have OC3B exactly 180deg out of phase, and with 5V amplitude peaks.

FWI: what I tried also include

  • powering up the Arduino board from the power barrel (instead of the USB port)... same outcome
  • connecting each Arduino output to a resistor connected to ground, to see if the issue might have been due to an impedence issue. The interesting thing, is that when I close the circuit with a resistor (5k resistor for each of the 2 outputs), OC3A continues to work normally, but OC3B has 0 mean voltage, with occasional positive 0.5V spikes in correspondence to changes of OC3B... still not as expected. In other words, I noticed a difference in the following 2 cases (even though the outcome was undesired in both of them): 1) voltage measured directly out of the board pins with nothing connected to them (open circuit), 2) voltage measured directly out of the board pins with circuit closed with a simple 5k resistor (one per output, and each connected to the same ground, and ground connected to the Arduino ground pin).
  • I tested my oscilloscope probes and ensure that they both work (this included switching them and switching port on the oscilloscope, to rule out the possibility of an oscilloscope malfunction).
  • The above was experienced on 3 different Arduino boards: 2 UNO and 1 Mega2560. This would rule out a possible malfunction of an individual board.

What have I done wrong in the code above?

No idea. Try this from John Wasser:


// Generating Two 180° Out of Phase Variable-Frequency 
// Square Waves on Timer1 of an Arduino UNO (Pins 9 and 10)
// Good for frequencies from 0.2 Hz to 500 kHz.
// Written June 1st, 2020 by John Wasser

void TwoPhaseBegin()
{
  digitalWrite(9, LOW);
  pinMode(9, OUTPUT);
  digitalWrite(10, LOW);
  pinMode(10, OUTPUT);

  // Stop Timer/Counter1
  TCCR1A = 0;  // Timer/Counter1 Control Register A
  TCCR1B = 0;  // Timer/Counter1 Control Register B
  TIMSK1 = 0;  // Timer/Counter1 Interrupt Mask Register

  // Set Timer/Counter1 to Waveform Generation Mode 8: 
  // Phase and Frequency correct PWM with TOP set by ICR1
  TCCR1B |= (1 << WGM13);  // WGM=8
  TCCR1A |= (1 << COM1A1);  // Normal PWM on Pin 9
  TCCR1A |= (1 << COM1B1) | (1 << COM1B0); // Inverted PWM on Pin 10

  TwoPhaseFrequency(1000.0);  // Default to 1 kHz
}

bool TwoPhaseFrequency(float frequency)
{
  byte prescaleBits; // 1, 2, 3, 4, 5
  uint16_t prescaleFactor;  // 1, 8, 64, 256, 1024
  uint32_t top32;

  // Find the smallest prescale factor that will fit the TOP value within 16 bits.
  // frequency = F_CPU / (2 * prescale *  TOP)
  // TOP = F_CPU / (2UL * prescale * frequency);

  prescaleBits = 1;
  prescaleFactor = 1;  // Used for 123 Hz to 500 kHz
  top32 = F_CPU / (2UL * prescaleFactor * frequency);
  if (top32 > 65535UL) // Too many clocks to count in 16 bits?
  {
    prescaleBits = 2;
    prescaleFactor = 8;  // Used for 16-122 Hz
    top32 = F_CPU / (2UL * prescaleFactor * frequency);
    if (top32 > 65535UL) // Too many clocks to count in 16 bits?
    {
      prescaleBits = 3;
      prescaleFactor = 64;  // Used for 2-15 Hz
      top32 = F_CPU / (2UL * prescaleFactor * frequency);
      if (top32 > 65535UL) // Too many clocks to count in 16 bits?
      {
        prescaleBits = 4;
        prescaleFactor = 256; // Only used for 1 Hz
        top32 = F_CPU / (2UL * prescaleFactor * frequency);
        if (top32 > 65535UL) // Too many clocks to count in 16 bits?
        {
          prescaleBits = 5;
          prescaleFactor = 1024;
          top32 = F_CPU / (2UL * prescaleFactor * frequency);
          if (top32 > 65535UL) // Too many clocks to count in 16 bits?
          {
            return false;
          }
        }
      }
    }
  }

  //  Serial.print("Freq: ");
  //  Serial.print(frequency);
  //  Serial.print(" prescale: ");
  //  Serial.print(prescaleFactor);
  //  Serial.print(" TOP: ");
  //  Serial.println(top32);

  if (top32 < 16)
    return false; // Require at least 16 levels of PWM

  TCCR1B &= ~((1 << CS12) | (1 << CS11) | (1 << CS10)); // Clear the three clock select bits
  TCCR1B |= prescaleBits; // Set clock prescale to prescaleBits

  ICR1 = top32;
  OCR1A = top32 / 2;
  OCR1B = (top32 / 2) + 1;
  return true;
}

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

  TwoPhaseBegin();  // Start output at 1000 Hz
}

void loop()
{
  // Call TwoPhaseFrequency(uint16_t frequency) any time to change the output frequency
}

Your post was MOVED to its current location as it is more suitable.

Please follow the advice given in the link below when posting code. Use code tags (the </> icon above the compose window) to make it easier to read and copy for examination

Aarg,
thank you very much for your help!!
The code that you posted works... not yet sure why.
The code I posted was for a 32kHz wave. I tried the code that you posted with 32000 as the argument of your TwoPhaseFrequency function, and it works: it generates 2 opposite waves at 32kHz. Now I have to figure out why mine doesn't...
However, thanks again!!

Because John Wasser wrote it. :slight_smile:

sorry what I meant is... not sure why mine doesn't work :slight_smile:

For those who may be interested, this is my code for an Arduino UNO (this code does not work... and I need to figure out why to improve my knowledge on the subject):

void setup() {
// Setting up output pins
DDRB = (1 << DDB1); // OC1A: (pin D9)
DDRB = (1 << DDB2); // OC1B: (pin D10)
// Resetting Timer1 Control Registers A & B
TCCR1A = 0;
TCCR1B = 0;
// Resetting timer to 0
TCNT1 = 0;
// Setting output compare match values
ICR1 = 500;
OCR1A = 250;
OCR1B = 250;
// Setting waveform generation mode 14 (Fast PWM, TOP = ICR1)
TCCR1A |= (1<<WGM11);
TCCR1B |= (1<<WGM13) | (1<<WGM12);
// Setting prescaler to 1
TCCR1B |= (1<<CS10);
// Setting Compare Output Modes
TCCR1A |= (1<<COM1A1) | (1<<COM1B0) | (1<<COM1B1);
}
void loop() {
}

That would hinge on how it should work.

Your comments only explain what you are doing, like setting such and such a mode. Not why. "setting up output pins" for example...

The way it should work is as I mentioned it in the first message:
OC1A pulsing at 32kHz, and OC1B pulsing at the same frequency, but inverted w.r.t. OC1A.
Hopefully this makes it clearer...

By the way, what is "not working", what actually happens?

IMO it's sufficient to use a phase correct PWM mode and set COMxA0/B0 inverse in TCCRxA. This will output inverse signals on the OCxA/B pins. The duty cycles have to be adjusted if the HIGH time of the outputs should be the same.

what happens with the code that I posted is that

  1. the 2 outputs appear to be in phase (not out of phase)
  2. one of the 2 outputs has a low peak-to-peak voltage amplitude

I got my code to work...

The trick was replacing the first two lines of code
" // Setting up output pins
DDRB = (1 << DDB1); // OC1A: (pin D9)
DDRB = (1 << DDB2); // OC1B: (pin D10)
"

with the following
" // Setting up output pins
DDRB |= (1 << DDB1); // OC1A: (pin D9)
DDRB |= (1 << DDB2); // OC1B: (pin D10)
"

... I got why.

Thank you all for taking me out of this time-sink hole... Aarg I truly appreciated the code that you posted: I figured out the issue by reverse eng. the one that you posted and comparing with mine! thanks a lot!

That explains the problem. You set OUTPUT mode on Pin 5 (PE3) but when you set OUTPUT mode on Pin 2 (PE4) you also set INPUT mode on the rest of PORTE, including Pin 5 (PE3).

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