Phase shift between 2 PWM

I'm trying to generate 2 PWM signal shifted 180 degrees from each other. I was able to generate them using FAST PWM mode using timer1, but I can not apply phase shift between them. I have tried to add interrupt, but it is not working.

void setup() {
  
interrupts();
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

  //InitTimer1
  TCNT1  = 0;
  TCCR1A = 0;
  TCCR1B = 0;

  //set to mode 14 fast pwm to ICR1 top value
  //non inverted output on pins 9 (A) and 10 (B)
  //ICR1 set frequency OCR1A and OCR1B set duty cycle
  //prescaler 8 gives .5 microseconds per timer tick

  // TCCR1A
  // Bit                 7      6      5       4     3      2      1      0
  // Bit Name          COM1A1 COM1A0 COM1B1 COM1B0 -----  ----- WGM11  WGM10
  // Initial Value       0      0      0       0     0      0      0      0
  // changed to          1      0      1       0     0      0      1      0

  TCCR1A = B10100010;

  // TCCR1B  prescaler 8 .5us/tick
  // Bit             7     6      5       4       3      2        1       0
  // Bit Name      ICNC1  ICES1 ----   WGM13    WGM12    CS12    CS11     CS10
  // Initial Value    0     0     0        0       0      0        0       0
  // changed to       0     0     0       1       1      0        1       0

  TCCR1B = B00011010;

  ICR1 = 99;//Set Top Value, -> frequency 20 kHz with 16 MHz internal clock, count is 0 referenced for 100 ticks
  OCR1A = 50; //duty cycle pin 9 value 0-99
  OCR1B = 10; //duty cycle pin 10 value 0-99
 
}
 ISR(TIMER1_COMPA_vect) {// Timer1 interrupt service routine
  int toggleState; 
    if (toggleState == false) {
        digitalWrite(9, HIGH);    
        delayMicroseconds(0.025);     
        digitalWrite(10, HIGH);    
        toggleState = true;    
    } 
    else 
    {        
        digitalWrite(9, LOW);    
        digitalWrite(10, LOW);   
        toggleState = false;        
    }    
}
void loop() {}

That's a FAQ. See the Related Topics below.

TC1 has two chnnels: Ch-A and Ch-B. You can generate inverted PWM signal on Ch-B by consulting the bits' meanings of the following Register (Fig-1) . What is the frequency of your PWM signal?
TCCR1Ax
Figure-1:

Sketch to genertae 5 kHz PWM (Mode-14) signal: Ch-A and inverted Ch-B

#define OC1A 9
#define OC1B 10

void setup()
{
  Serial.begin(9600);
  pinMode(OC1A, OUTPUT); //Ch-A
  pinMode(OC1B, OUTPUT); //Ch-B

  TCCR1A = 0x00;   //reset
  TCCR1B = 0x00;   //TC1 reset and OFF
  //fOC1A = clckSys/(N*(1+ICR1)); Mode-14 FPWM; OCR1A/OCR1B controls duty cycle of Ch-A/Ch-B
  // 5 kHz = 16000000/(8*(1+ICR1)) ==> ICR1 = 399, N = 8

  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Ch-A non-inverting, Mode-14
  TCCR1A |= (1 << COM1B1) | (1 << COM1B0); //Ch-B inverting
  TCCR1A |= (1 << WGM11);
  TCCR1B |= (1 << WGM13) | (1 << WGM12); //Mode-14 Fast PWM
  ICR1 = 399;  //changes frequency as ICR changes
  OCR1A = 200;   //~= 50% duty cycle; Ch-A
  OCR1B = 200;    //~= 50% duty cycle Ch-B
  TCCR1B |= (1 << CS11); //N = 8   //Start TC1 with division factor 8
  delay(200);
}

void loop()
{
  
}

I want to generate 20Khz fast PWM, inverting the pwm will not work for duties less than 50%. For example, at 40% duty cycle I want PWM A to be on 40/100 then off 60/100. for PWM B to be on 40/100 and 60/100 also. there is a period of 20/100 where both of them is off.

at 40% duty cycle I want PWM A to be on 40/100 then off 60/100. for PWM B to be on 40/100 and 60/100 also. there is a period of 20/100 where both of them is off.

I think think this dual slope (mode10) timer setup will do what you want. Compiles but not actually tested.

//Timer1 Mode 10 PWM to ICR1
//Dual pin 20KHz PWM generator
//40% duty cycle 20% dead band between pulses

void setup() {
  pinMode(9, OUTPUT); //output A
  pinMode(10, OUTPUT); //output B

  TCCR1A = 0; //clear timer registers
  TCCR1B = 0;
  TCNT1 = 0;

  //ICR1 and Prescaler sets frequency
  //no prescaler .0625 us per count @ 16Mh
  //800 counts x .0625 = 50 us = 20Khz 400 up 400 down
  //40% = 320 counts
  //deadband 160 counts

  TCCR1B |= _BV(CS10); //no prescaler
  ICR1 = 400;//PWM mode counts up and back down for 800 counts 20Khz

  OCR1A = 240 ; //Pin 9 match
  //output A set rising/clear falling
  //40% Duty Cycle Pulse centered on TCNT 400.
  //Rise at TCNT 240 upslope, High 320 counts, Fall at TCNT 240 downslope
  TCCR1A |= _BV(COM1A1) | _BV(COM1A0); //output A set rising/clear falling

  OCR1B = 160; //Pin 10 match
  //output B clear rising/set falling
  //40% Duty Cycle Pulse centered on TCNT 0
  //Fall at TCNT 160 upslope, Low 480, Rise at TCNT 160 downslope
  //40% Duty Cycle Pulse centered on TCNT 0
  TCCR1A |= _BV(COM1B1); //output B clear rising/set falling

  TCCR1B |= _BV(WGM13); //PWM mode with ICR1 Mode 10
  TCCR1A |= _BV(WGM11); //WGM13:WGM10 set 1010

}

void loop() {}

Looks OK dropped into UnoScope - Wokwi ESP32, STM32, Arduino Simulator

'WMG13' was not declared in this scope
'WMG11' was not declared in this scope
these errors appear while compiling

What board are you selecting?

The posted code compiles for a Uno.

yes, Uno

Maybe WGM:

Thanks, That works fine. But if I want to vary duty cycle how can I manage to do it?

Change the OCR1A and OCR1B values.

1 Like

OP was NOT reading post #3 for the sysmbolic names: WGM1X?

TCCR1Ax

I have actually referred to datasheet, but I didn't notice it. It is very deceiving.

Changing OCR1A and OCR1B with the same values doesn't work properly. For example I want duty of PWM 1 30% and PWM 2 30%. I have tried OCR1A= 200 and OCR1B = 120 but the output is that each PWM is with different duty ratio

Study the math used in the example I posted. How many counts for 30%. A counts centered around 400. B counts centered around 0.

1 Like

For phase shift PWM, you want to use Mode-10 Phase. Fast PWM will not line up correctly for 180 PSPWM except maybe at 50% duty. Assuming you need duty that is variable (at run-time), you'll want Mode-10 Phase PWM with A-channel in Normal compare and B-channel in Complimentary compare. Another gotcha is the Complimentary channel must use 'mirrored' duty-cycle (if A=22%, B=[100-22] % ). In this way, both PWM pulses are positive going but exactly 180 degrees out of phase.

Here's some sample code to run an Uno at 180 degrees PSPWM using Timer1. If you change the A-channel duty, you must change the B-channel to the mirror of A.

// RTM_TimerCalc 1.40,  RuntimeMicro.com
// Timer-1 16-bit, Mode-10 Phase, Top=ICR
// 1,000 Hz Frequency, Clock is 16 MHz

TCCR1B = 0x10; // 0001 0000, Disable Timer 
TCCR1A = 0xB2; // 1011 0010

ICR1 = 8000-0;
OCR1A = (int) (ICR1 * 0.25);
OCR1B = (int) (ICR1 * 0.75); // mirrored duty (A+B always add to 100%)
TCNT1=0x0;

// UnComment following lines for UNO-NANO Timer-1 Pins 
pinMode(9, OUTPUT);  // OC1a
pinMode(10, OUTPUT); // OC1b

// UnComment following lines for 2560 Timer-1 Pins 
// pinMode(11, OUTPUT);  // OC1a
// pinMode(12, OUTPUT);  // OC1b

TCCR1B |= 1; // Prescale=1, Enable Timer

hth

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