Inverting PWM signal with direct port addressing

Hi everyone, I hope you can help me with this. I have an UNO board with this code:

void setup()
{

     pinMode(3,OUTPUT);
     TCCR2A=0x23;
     TCCR2B=0x09;
     OCR2A=63;
     OCR2B=31;

}

This sends a 250kHz 50% duty cycle square wave through pin 3. I would really love to have the same wave but inverted at pin 11, also driven by Timer2.

Thank you very much for the help.

You may say "why don't you use a NOT gate like the 7404", well, if I can avoid using it, then I will avoid it and learn some port manipulation by the way.

neukyhm:
I would really love to have the same wave but inverted at pin 11, also driven by Timer2.

Too bad. The architecture doesn't support it.

Not? Don’t you do this with

pinMode(3,OUTPUT);
pinMode(11,OUTPUT);
TCCR2A=0xE3;
TCCR2B=0x09;[/quote]

And writing the same value to the OCR2x registers?

The Compare Output Mode bits in control register A can select between Normal and Inverted PWM. To both set the frequency AND use both PWM pins you have to select Mode 14 which stores TOP in the Input Compare Register instead of Output Compare Register A (Mode 15). This allows use of both OCRA and OCRB for PWM duty cycle.

I found this:

TCCR2A = _BV(COM2A1)  // non-inverting PWM on OC2A
       | _BV(COM2B0)  // PWM on OC2B:
       | _BV(COM2B1)  //    inverting mode
       | _BV(WGM20)   // fast PWM
       | _BV(WGM21);  // ditto
TCCR2B = _BV(CS10);   // clock at F_CPU / 1
OCR2A  = 84;          // duty cycle ~ 1/3
OCR2B  = 84;          // same signal, complemented

This sends two square waves of 1/3 duty cycle and ~60kHz, one of them is inverted. Now I would like to know how to change frequency and duty cycle to 50%.

septillion:
Not? Don’t you do this with

pinMode(3,OUTPUT);

pinMode(11,OUTPUT);
TCCR2A=0xE3;
TCCR2B=0x09;

And writing the same value to the OCR2x registers?

Measured with the scope, didn’t work.

Quote:

OCR2A  = 84;          // duty cycle ~ 1/3

256* 1/3 = 85.

neukyhm:
I found this:

TCCR2A = _BV(COM2A1)  // non-inverting PWM on OC2A

| _BV(COM2B0)  // PWM on OC2B:
      | _BV(COM2B1)  //    inverting mode
      | _BV(WGM20)  // fast PWM
      | _BV(WGM21);  // ditto
TCCR2B = _BV(CS10);  // clock at F_CPU / 1
OCR2A  = 84;          // duty cycle ~ 1/3
OCR2B  = 84;          // same signal, complemented




This sends two square waves of 1/3 duty cycle and ~60kHz, one of them is inverted. Now I would like to know how to change frequency and duty cycle to 50%.

Measured with the scope, didn't work.

This is my code for setting the pulse width interval for timer1 16 bit UPDATE you will need to adapt it for timer2 8 Bit Not Possible See johnwasser post #8

void setPeriod(long microseconds)    // AR modified for atomic access
{
	long cycles = (F_CPU / 2000000) * microseconds;                                // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
	if (cycles < RESOLUTION)              clockSelectBits = _BV(CS10);             // no prescale, full xtal
	else if ((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11);             // prescale by /8
	else if ((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64
	else if ((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12);             // prescale by /256
	else if ((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024
	else      cycles = RESOLUTION - 1,    clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum

	char oldSREG = SREG;
	cli();								// Disable interrupts for 16 bit register access
	ICR1 = pwmPeriod = cycles;			// ICR1 is TOP in p & f correct pwm mode
	SREG = oldSREG;
	TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
	TCCR1B |= clockSelectBits;			// reset clock select register, and starts the clock
	
}

128 is half of 256 for 50% duty cycle:

OCR2A  = 128;          // duty cycle ~ 1/2
OCR2B  = 128;          // same signal, complemented

Z

This might be helpful for timer 1 :slight_smile:

/*
TCCR1A WGM11 WGM10
TCCR1B WGM13 WGM12
Mode WGM13	WGM12	WGM11	WGM10
0	 0		0		0		0		Normal 0xFFFF Immediate MAX
1	 0		0		0		1		PWM, Phase Correct, 8-bit 0x00FF TOP BOTTOM
2	 0		0		1		0		PWM, Phase Correct, 9-bit 0x01FF TOP BOTTOM
3	 0		0		1		1		PWM, Phase Correct, 10-bit 0x03FF TOP BOTTOM
4	 0		1		0		0		CTC OCR1A Immediate MAX
5	 0		1		0		1		Fast PWM, 8-bit 0x00FF BOTTOM TOP
6	 0		1		1		0		Fast PWM, 9-bit 0x01FF BOTTOM TOP
7	 0		1		1		1		Fast PWM, 10-bit 0x03FF BOTTOM TOP
8	 1		0		0		0		PWM, Phase and Frequency Correct ICR1 BOTTOM BOTTOM
9	 1		0		0		1		PWM, Phase and Frequency Correct OCR1A BOTTOM BOTTOM
10	 1		0		1		0		PWM, Phase Correct ICR1 TOP BOTTOM
11	 1		0		1		1		PWM, Phase Correct OCR1A TOP BOTTOM
12	 1		1		0		0		CTC ICR1 Immediate MAX
13	 1		1		0		1		(Reserved) 
14	 1		1		1		0		Fast PWM ICR1 BOTTOM TOP
15	 1		1		1		1		Fast PWM OCR1A BOTTOM TOP
*/
void SetMode(int mode)
{
	//Clear all timer mode bits
	TCCR1A &= ~(_BV(WGM10) | _BV(WGM11));
	TCCR1B &= ~(_BV(WGM13) | _BV(WGM12));
	switch(mode){
		case 1:
		TCCR1A |= _BV(WGM10);				// PWM, Phase Correct, 8-bit 0x00FF TOP BOTTOM
		break;
		case 2:
		TCCR1A |= _BV(WGM11);				// PWM, Phase Correct, 9-bit 0x01FF TOP BOTTOM
		break;
		case 3:
		TCCR1A |= _BV(WGM10) | _BV(WGM11);	// PWM, Phase Correct, 10-bit 0x03FF TOP BOTTOM
		break;
		case 4:
		TCCR1B |= _BV(WGM12) ;				// CTC OCR1A Immediate MAX
		break;
		case 5:
		TCCR1B |= _BV(WGM12) ;				// Fast PWM, 8-bit 0x00FF BOTTOM TOP
		TCCR1A |= _BV(WGM10);				// ditto
		break;
		case 6:
		TCCR1B |= _BV(WGM12) ;				// Fast PWM, 9-bit 0x01FF BOTTOM TOP
		TCCR1A |= _BV(WGM11);				// ditto
		break;
		case 7:
		TCCR1B |= _BV(WGM12) ;				// Fast PWM, 10-bit 0x03FF BOTTOM TOP
		TCCR1A |= _BV(WGM10) | _BV(WGM11);	// ditto
		break;
		case 8:
		TCCR1B |= _BV(WGM13) ;				// PWM, Phase and Frequency Correct ICR1 BOTTOM BOTTOM
		break;
		case 9:
		TCCR1B |= _BV(WGM13) ;				// PWM, Phase and Frequency Correct OCR1A BOTTOM BOTTOM
		TCCR1A |= _BV(WGM10);				// ditto
		break;
		case 10:
		TCCR1B |= _BV(WGM13) ;				// PWM, Phase Correct ICR1 TOP BOTTOM
		TCCR1A |= _BV(WGM11);				// ditto
		break;
		case 11:
		TCCR1B |= _BV(WGM13) ;				// PWM, Phase Correct OCR1A TOP BOTTOM
		TCCR1A |= _BV(WGM10) | _BV(WGM11);	// ditto
		break;
		case 12:
		TCCR1B |= _BV(WGM12) |_BV(WGM13) ;	// CTC ICR1 Immediate MAX
		break;
		case 14:
		TCCR1B |= _BV(WGM12) |_BV(WGM13) ;	// Fast PWM ICR1 BOTTOM TOP
		TCCR1A |=  _BV(WGM11);				// ditto
		break;
		case 15:
		TCCR1B |= _BV(WGM12) |_BV(WGM13) ;	// Fast PWM OCR1A BOTTOM TOP
		TCCR1A |= _BV(WGM10) | _BV(WGM11);	// ditto
		break;				
	}
	
}

neukyhm:
I found this:

TCCR2A = _BV(COM2A1)  // non-inverting PWM on OC2A

| _BV(COM2B0)  // PWM on OC2B:
      | _BV(COM2B1)  //    inverting mode
      | _BV(WGM20)   // fast PWM
      | _BV(WGM21);  // ditto
TCCR2B = _BV(CS10);   // clock at F_CPU / 1
OCR2A  = 84;          // duty cycle ~ 1/3
OCR2B  = 84;          // same signal, complemented




This sends two square waves of 1/3 duty cycle and ~60kHz, one of them is inverted. Now I would like to know how to change frequency and duty cycle to 50%.

It can't be done with Timer2. The way to change the PWM frequency is to change the prescale or change the TOP value. There are only 7 choices of prescale so that doesn't give much choice of frequency. To change the frequency more smoothly you need to change TOP. To do that on Timer2 you have to use Mode 7 but that uses OCR2A to store TOP so you can't use the A channel for PWM.
Use Timer1 in Mode 14. Mode 14 uses the ICR1 register for TOP. That leaves both OCR1A and OCR1B for PWM duty cycle values. Study the Timer1 section of the datasheet for more details.

I finally managed to make it work, I found this code: Arduino Push-Pull PWM – The Smell of Molten Projects in the Morning

It works perfect. I have change it a bit to make the function use nano instead of microseconds.

The 16MHz clock has a 62.5nS period. I believe the fastest you can make an output toggle under s/w control is 8 MHz, 130nS period. I’d be interested to see how you achieve nanoseconds vs microseconds

CrossRoads:
The 16MHz clock has a 62.5nS period. I believe the fastest you can make an output toggle under s/w control is 8 MHz, 130nS period. I'd be interested to see how you achieve nanoseconds vs microseconds

You are right. The fastest PWM wave I can get is 8MHz, altogh I'm already happy with ~500kHz.

Timer1 is used, since you guys have said that Timer2 would not work for my purpose.