July 03, 2024 Tahmid
I have previously discussed the benefits of using the Raspberry Pi Pico, leveraging the easy-to-program Micropython along with the capable hardware peripherals: 2023 Updates: PhD; Pi Pico and Quick and dirty PIC16F72 programmer with the Pico
Several articles in the blog have previously described applications of PWM in embedded systems, such as Stereo audio player using the PIC32, MCP4822, microSD card and the MDDFS library, DC motor control with PIC16F877A - Practical example of PIC PWM and Generation of sine wave using SPWM in PIC16F684.
Complementary PWM signals with 500ns deadtime
The underlying RP2040 in the Pi Pico provides an easy-to-use and still powerful and flexible set of PWM peripherals that can enable a wide range of applications. A common requirement in power electronics is the generation of complementary PWM signals. For example, the complementary PWM signals are used in a synchronous buck converter, shown below, to drive the switches S1 and S2.
Synchronous Buck Converter
Image source: https://commons.wikimedia.org/wiki/File:300px-Synch_buck.PNG
To prevent shoot-through, a deadtime is employed. Shoot-through is the event when both S1 and S2 are turned on, causing a large current through them due to shorting across the supply voltage. Even if the generated PWM signals are non-overlapping (ie S1 and S2 are never turned on together), delays in the circuitry - through the gate drivers and the power switches themselves - can still result in shoot-through conditions. In a severe case, the shoot-through can cause damage to the switches, blowing them out. In a more moderate case, small overlap times can result in reduced efficiency due to wasted power, but not necessarily damage to the switches. To combat this issue, a deadtime is inserted between the S1 and S2 driving signals. This is an amount of time when both switch control signals are zero allowing for system transients to settle out. How large it should be depends on the circuit parameters and behavior.
In many cases, gate drivers can have built-in features to insert dead-times, such as the
LM5106. However, the ability to generate this in the microcontroller itself gives greater flexibility in the selection of gate driver. Further, it allows tuning the time easily in software rather than needing to change hardware components to change deadtimes.
Fortunately, the Pi Pico makes it fairly straightforward to do this!
The corresponding source code is copied below for convenience:
The RP2040 has 8x PWM slices, each with 2 channels. The complementary PWM waveforms are produced on these 2 channels (A and B) on a given slice. The slice to GPIO mapping is shown below.
PWM slice+channel mapping to GPIO pins
Image source: RP2040 datasheet section 4.5.2
The key aspects of the code are:
- Use Micropython to init PWM for the associated GPIO pins (A and B).
- Alter RP2040 registers to configure for complementary PWM. This consists of two settings being changed.
- Invert channel B relative to channel A.
- Use center-aligned (phase-correct) PWM instead of edge-aligned. This ensures that the deadtime is applied to both rising and falling edges of channel A. If edge-aligned PWM was used, the channels would be set high together and the deadtime would only be applied on the falling edge of channel A.
- The deadtime is applied to channel B such that channel A's duty cycle corresponds to the desired/set duty cycle.
- The duty cycles corresponding to both channels A and B are updated with one register write.
- The frequency of the output PWM using center-aligned/phase-correct mode is half that when using the default edge-aligned mode.
An example of phase-correct/center-aligned PWM operation is shown in the figure below, taken from the RP2040 datasheet.
Image source: RP2040 datasheet section 4.5.2.1
To generate the complementary signal on channel B, the duty cycle is computed as the sum of the pulse count corresponding to the desired duty cycle and the desired deadtime ticks. With a 125MHz clock for the RP2040 and default clock/divider settings, this corresponds to 8ns per tick. The inversion of channel B then ensures the production of the desired complementary setting.
Shown below are waveforms for GP16 (PWM 0A) and GP17 (PWM0B) for 100kHz PWM, 25% duty cycle and a 504ns deadtime. The deadtime can be verified by recognizing that it corresponds to one horizontal division, which is set to 500ns.
Complementary PWM signals with 500ns deadtime
Taking a zoomed out view of the waveform highlights the 100kHz frequency and illustrates the complementary PWM generation over multiple cycles, as shown below.
3 cycles of 100kHz PWM complementary signals
A natural use case for this generated complementary PWM signal is to control a synchronous buck converter to achieve high efficiency step down operation. Since Micropython will limit the design of a fast control loop, applications where the high frequency PWM is coupled with a low-bandwidth controller make for a natural home for this use case. A solar MPPT battery charger would make for an ideal usage scenario. Alternately, if the same configuration technique is applied in C, fast control loops can then be designed and implemented.
Very interesting article, Vaiya
ReplyDeletenice
ReplyDeleteDear Tahmid, excellent article,
ReplyDeleteI have a question: if I have a pwm with variable duty cycle (e.g. in the case of an inverter), how can I proceed to obtain a complementary pwm? Thank you in advance for your reply.
Greetings, Costantino
In the example provided, you can keep calling the duty function with the updated duty cycle.
DeleteSo you would call pwm16.duty(0.25, 63) for 25% duty cycle with 63 ticks deadtime. You can change the first argument with your desired duty cycle: eg pwm16.duty(0.35, 63) for 35% duty cycle.