PWM complementary outputs

I need a pair of out of phase outputs for a PWM H bridge driver. That is, when one pin goes high, the other goes low. I believe the STM32 chip is capable of this with the TIM1 or TIM8 pwm peripherals but I don’t understand how to configure the chip to get it. Any help would be appreciated.

Why not just do this in code?

void phaseOutput(int value)
{
  analogWrite(FIRSTPIN, value);
  analogWrite(SECONDPIN, 256 - value);
}


I’m not positive, but my preliminary guess is that it’s not possible. While TIM1 and TIM8 do support the complementary output and I could probably figure out how to enable the outputs, there’s a hardware issue.

Normally you’d use a timer channel, say TIM1_CH1 and its complement TIM1_CH1N. Unfortunately, I can’t find a pair of timer channels on TIM1 or TIM8 that have both pins exposed on the Photon.

This table shows the timer channel and the pins its supported on. The STM32F205 pins are things like PA7 and the Photon pin numbers are in parentheses when exposed.

TIM1_CH1N PA7 (A5), PB13, PE8
TIM1_CH1  PE9, PA8
TIM1_CH2N PB0, PE10, PB14
TIM1_CH2  PE11, PA9 (TX)
TIM1_CH3N PB1, PE12, PB15
TIM1_CH3  PE13, PA10 (RX)
TIM1_CH4  PE14, PA11

TIM8_CH1  PC6, PI5
TIM8_CH1N PA5 (A3), PA7 (A5), PH13
TIM8_CH2  PC7 (SETUP), PI6
TIM8_CH2N PB0, PB14 (USB DATA -), PH14
TIM8_CH3  PC8, PI7
TIM8_CN3N PB1, PB15, PH15
TIM8_CH4  PC9, PI2

Maybe it’s possible to use two timer channels, say TIM1_CH2 and TIM1_CH1N, but I’m not sure. I’m also not positive that TIM1 and TIM8 aren’t used for something else internally. And I’m not sure you can really override the complementary output bit setting. I checked the exposed pin part first and that didn’t look very promising so I didn’t pursue checking the other things.

2 Likes

Thanks for your response, but this doesn’t quite get there. The two analog writes provide the correct opposite duty cycles, but not the phase relationship. Consider for a moment, when value=128. Then both FIRSTPIN and SECONDPIN end up with essentially the same output, not oppositely phased.

2 Likes

I must have misinterpreted your question then. :wink:

Thanks for your helpful response. There are three sets of documents that I haven’t been able to find, and perhaps you could point me to them. 1. The pin mapping of the STM32F205 peripherals. 2. The programming model (registers and what they do) for the peripherals. 3. (I don’t know if this exists) What peripherals are used by the Particle firmware that an application needs to stay away from.

Here are the documents that I often reference:

STM32F205xx Data Sheet

STM32F2xx Standard Library

STM32F205 Programming Manual

STM32F205 Reference Manual

The Photon/Electron include the STM32F2xx standard peripheral library and you can call it from user code. Thus you can use the code examples for the STM32F development boards on the Photon/Electron often with minimal changes.

The only caveat is that sometimes the system firmware will get in the way of making certain changes. This can happen with interrupt handlers, for example. That pretty much requires experimentation and reading the system firmware source code

For an example, the audio3 code here directly access the STM32F205 ADC and sets it up in DMA mode. It uses the standard peripheral library.

And finally the Particle data sheets show the mappings between the STM32F pin names and the physical pins on the devices.
https://docs.particle.io/datasheets/photon-datasheet/#pin-out-diagrams

Good luck!

2 Likes

@davidcane, if high frequency is not required, you could look at the SoftPWM library on the web IDE. It uses the SparkIntervalTimer library to create hardware timer based interrupts to drive the PWM output. It don’t see any reason why you couldn’t have synchronized (in phase) outputs using this library.

1 Like

Hi @davidcane

You should read this STM32 app note:

Assuming that you always want a complimentary signal… couldn’t you just use an inverter?

Hi @BulldogLowell

The inverter has some nominal propagation delay that would need to balanced out on the other wire. There are ways to do this but it is generally not the way to go.

In real motor control PWM, you generally want some controlled dead time between the outputs switching for transient power and momentum control. They are not simply complementary all of the time.

2 Likes

Many thanks to all who responded. Getting the links to the docs from Rickkas7 was especially useful…

The inverter solution doesn’t work for my application because I need to keep both outputs low during initialization prior to powering on my H bridge driver. I’m not driving a motor with it so the dead time is not an issue.

I didn’t see a solution from the app note that was suggested by bko.

At the present time, I think my best bet is to use two channels of a timer in PWM mode (e,g, CH1/CH2 of Timer 3 come out on Photon pins D3/D2) set to the same duty cycle. Then I can use the STM standard peripheral library to set the output polarity bits to opposite states (bits CCnP in register TIMx_CCER). It looks as though output polarity is independently settable for each channel of a timer.

If anyone quickly sees a reason why this can’t work I would appreciate knowing it before I start plowing through how to use the standard library. Once I have it working I will post my code for others to see and use.

Section 4 and Table 4 in particular in that App Note explains how to set up the timer for complementary output PWM. The device can do 2-phase complementary or 3-phase (120 degree) with and with-out dead time.

Unless I have missed something, the problem with using the complementary outputs as described in the App Note is that the Photon doesn’t bring the needed pins out to the connector. The complementary feature is available for Timers 1 & 8, but as near as I can tell, there are no pins on the Photon connector that have both the TIMx_CHy and TIMx_CHyN pins from the STM chip.

I now have this working, using the Standard Peripheral IO Library. The code placed in setup() looks like:

int freq = 2000;
int dutyCycle = 128;
pinMode(D3, OUTPUT);
pinMode(D2, OUTPUT);
analogWrite(D3, dutyCycle, freq); // Timer 3 channel 1
analogWrite(D2, dutyCycle, freq); // Timer 3 channel 2
// Now we want to invert he output of channel 2. Particle firmware doesn’t give access, use IO library
TIM_OC2PolarityConfig(TIM3, TIM_OCPolarity_Low);

Note that the Photon PWM outputs on D3, D2 use Timer 3 channels 1 & 2, which is what I use here. D0/D1 use Timer 4 channels 1&2, and the TX/RX pins use Timer1 channels 2&3 so the same strategy could be used for those. As noted earlier, these are true complementary outputs without a deadband.

2 Likes