Arduino Push-Pull PWM

Push-Pull Drive: OC1A top, OC1B bottom

Push-Pull PWM Drive: OC1A top, OC1B bottom

Most of the time you need just a single PWM output, but when you’re driving a transformer (or some such) and need twice the primary voltage, you can use a pair of PWM outputs in push-pull mode to get twice the output voltage.

A single PWM output varies between 0 and +5 V (well, Vcc: adjust as needed), so the peak-to-peak value is 5 V. Drive a transformer from two out-of-phase PWM outputs, such that one is high while the other is low, and the transformer sees a voltage of 5 V one way and 5 V the other, for a net 10 V peak-to-peak excursion.

A 50% duty cycle will keep DC out of the primary winding, but a blocking capacitor is always a good idea with software-controlled hardware. A primary winding with one PWM output stuck high and the other stuck low is a short circuit that won’t do your output drivers or power supply any good at all.

An Arduino (ATMega168 and relatives) can do this without any additional circuitry if you meddle with the default PWM firmware setup. You must use a related pair of PWM outputs that share an internal timer; I used PWM 9 and 10.

#define PIN_PRI_A   9    // OCR1A - high-active primary drive
#define PIN_PRI_B   10   // OCR1B - low-active primary drive

I set push-pull mode as a compile-time option, but you can change it on the fly.
#define PUSH_PULL   true // false = OCR1A only, true = OCR1A + OCR1B

The timer tick rate depends on what you’re trying accomplish. I needed something between 10 & 50 kHz, so I set the prescaler to 1 to get decent resolution: 62.5 ns.
// Times in microseconds, converted to timer ticks
//  ticks depend on 16 MHz clock, so ticks = 62.5 ns
//  prescaler can divide that, but we're running fast enough to not need it

#define TIMER1_PRESCALE   1     // clock prescaler value
#define TCCR1B_CS20       0x01  // CS2:0 bits = prescaler selection

Phase-Frequency Correct mode (WGM1 = 8) runs at half-speed (it counts both up and down in each cycle), so the number of ticks is half what you’d expect. You could use Fast PWM mode or anything else, as long as you get the counts right; see page 133 of the Fine Manual.
// Phase-freq Correct timer mode -> runs at half the usual rate

#define PERIOD_US   60
#define PERIOD_TICK (microsecondsToClockCycles(PERIOD_US / 2) / TIMER1_PRESCALE)

The phase of the PWM outputs comes from the Compare Output Mode register settings. Normally the output pin goes high when the PWM count resets to zero and goes low when it passes the duty cycle setting, but you can flip that around. The key bits are COM1A and COM1B in TCCR1A, as documented on page 131.

As always, it’s easiest to let the Arduino firmware do its usual setup, then mercilessly bash the timer configuration registers…

// Configure Timer 1 for Freq-Phase Correct PWM
//   Timer 1 + output on OC1A, chip pin 15, Arduino PWM9
//   Timer 1 - output on OC1B, chip pin 16, Arduino PWM10

 analogWrite(PIN_PRI_A,128);    // let Arduino setup do its thing
 analogWrite(PIN_PRI_B,128);

 TCCR1B = 0x00;                 // stop Timer1 clock for register updates

// Clear OC1A on match, P-F Corr PWM Mode: lower WGM1x = 00
 TCCR1A = 0x80 | 0x00;

// If push-pull drive, set OC1B on match
#if PUSH_PULL
 TCCR1A |= 0x30;
#endif

 ICR1 = PERIOD_TICKS;           // PWM period
 OCR1A = PERIOD_TICKS / 2;      // ON duration = drive pulse width = 50% duty cycle
 OCR1B = PERIOD_TICKS / 2;      //  ditto - use separate load due to temp buffer reg
 TCNT1 = OCR1A - 1;             // force immediate OCR1x compare on next tick

// upper WGM1x = 10, Clock Sel = prescaler, start Timer 1 running
 TCCR1B = 0x10 | TCCR1B_CS20;

And now you’ll see PWM9 and PWM10 running in opposition!

Memo to Self: remember that “true” does not equal “TRUE” in the Arduino realm.

,

  1. #1 by daggilliDavid Gillies on 25-June-2011 - 09:22

    I am having so much fun with my new Uno board, but a lack of documentation is holding me back. Is there a reference for all the Atmel control registers etc. that are exposed to the Arduino IDE? For example, I’m playing with the remote control that came with my kit, and I see a reference to TCCR1A. I’ve got the Atmega168 datasheet and it’s very enlightening, but where do I find the definitive reference on how much of the guts of the microcontroller is hanging out there in userspace?

    • #2 by Ed on 25-June-2011 - 10:30

      the definitive reference on how much of the guts of the microcontroller is hanging out there in userspace

      As nearly as I can tell, the gcc-avr headers define all those constants, so you can use them as you see fit. Just avoid clobbering Timer 0 and the serial configuration: everything else is available.

      However, don’t get too clever with the source code, as the Arduino compile chain pretty much assumes you’re not doing exotic language tricks.

      It’s a great way to make stuff work, that’s for sure!

      • #3 by daggilliDavid Gillies on 31-July-2011 - 11:34

        Yikes, I’ve been tiptoeing around the Arduino compile chain not really knowing how far I could step out of bounds but that was a very informative illustration of how delicate things are. It still strikes me as bizarre that I can write C++ and target it on something that looks like it came from the 1980′s (shame no new and delete but at least there’s still malloc and free, although their utility on a device with 2K of SRAM is questionable.) I have ventured into the headers and the fog is clearing somewhat. The main stumbling block is realising you CAN do something, not HOW to do it. Even so there’s a lot faster feedback into blinky lights and whizzy stepper motors than back in the dark ages of ABEL and CUPL and – saints preserve us – PALASM (yes, I’m old.)

        • #4 by Ed on 31-July-2011 - 11:45

          something that looks like it came from the 1980′s

          Microcontrollers are where fully depreciated fab lines go to die and embedded systems are where PC-wannabe architectures go to die…

          That said, it’s amazing what you can stuff into a silicon splinter these days.

          I trust you’ve read my preprocessor horror story. My rule of thumb: any Arduino project can have one clever hack that goes outside the normal bounds, but more than that will land you in a world of hurt.

Comments, thoughts, notes, corrections: what do you think?

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 51 other followers