Introduction
Intentional instability. According to Wikipedia, this is a highly sought after feature in fighter aircraft design. I think, based on my experiments so far, I could say the same for flyback transformers!
As with many other flyback transformers on the market, the ones we were provided with all typically require switching speeds in the micro to nano second range. This is not trivial and requires either a fast enough clock signal on a microcontroller together with some low-level PWM clock timer code or a dedicated PWM component that can be suitably powered within the high voltage environment.
So in this blog, I am focusing on the MOSFET to learn more about how well they can switch on and off at these high frequencies because, as they say, rubbish in equates to rubbish out.
Generating +100kHz PWM signals
I like to keep things simple. So I tend to start testing things out using Arduino code. This way I don’t have to buy additional components, if I don’t really need them.
However, so as not to leave a stone unturned, I did do a quick search for some possible PWM components. I found two examples of dedicated PWM components. Both were from Texas Instruments. The first was the TL3844BP PWM controller, which switches at up to 500kHz with a 50% duty cycle, and the other was the TL494, which provides a max PWM frequency of 300kHz.
To start me off on the microcontroller route, I thought to check out the Arduino documentation, which provides a handy tutorial on how to generate PWM signals.
https://docs.arduino.cc/learn/microcontrollers/analog-output/
In this tutorial, it shows that the “AnalogWrite()” function is used to generate a steady rectangular wave at a specified duty cycle. Neat.
However, before getting too excited by the simplicity of it all, it always pays to check the details. For this I went to the specific reference page for the AnalogWrite function. Here it provides a table with all the maximum PWM frequencies possible for all the different Arduino boards.
Basically, the Arduino AnalogWrite function is not up to my task as you cannot even get 1kHz using this function with most boards.
So another method was required, if sticking with Arduino.
Well, Arduino has already thought of it, and there is another handy documentation page titled “Secrets of Arduino PWM” on their website, which demonstrates how to achieve higher PWM frequencies. However, the tips offered are only meant for ATmega328 or the older ATmega168 chipsets.
I was not going to give up that easily with my favourite prototyping platform. I decided to stick with the Arduino IDE but went with a Raspberry Pi Pico instead as I could then test out the PICO SDK PWM example using the RP2040 PIO functionality. To do this, I simply took the raw PIO code from the existing Pico Examples written in C code (as shown here):
; ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. ; ; SPDX-License-Identifier: BSD-3-Clause ; ; Side-set pin 0 is used for PWM output .program pwm .side_set 1 opt pull noblock side 0 ; Pull from FIFO to OSR if available, else copy X to OSR. mov x, osr ; Copy most-recently-pulled value back to scratch X mov y, isr ; ISR contains PWM period. Y used as counter. countloop: jmp x!=y noset ; Set pin high if X == Y, keep the two paths length matched jmp skip side 1 noset: nop ; Single dummy cycle to keep the two paths the same length skip: jmp y-- countloop ; Loop until Y hits 0, then pull a fresh PWM value from FIFO % c-sdk { static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) { pio_gpio_init(pio, pin); pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); pio_sm_config c = pwm_program_get_default_config(offset); sm_config_set_sideset_pins(&c, pin); pio_sm_init(pio, sm, offset, &c); } %}
I then popped this code in the online picoasm utility tool offered by Wokwi to generate my header file.
// -------------------------------------------------- // // This file is autogenerated by pioasm; do not edit! // // -------------------------------------------------- // #pragma once #if !PICO_NO_HARDWARE #include "hardware/pio.h" #endif // --- // // pwm // // --- // #define pwm_wrap_target 0 #define pwm_wrap 6 static const uint16_t pwm_program_instructions[] = { // .wrap_target 0x9080, // 0: pull noblock side 0 0xa027, // 1: mov x, osr 0xa046, // 2: mov y, isr 0x00a5, // 3: jmp x != y, 5 0x1806, // 4: jmp 6 side 1 0xa042, // 5: nop 0x0083, // 6: jmp y--, 3 // .wrap }; #if !PICO_NO_HARDWARE static const struct pio_program pwm_program = { .instructions = pwm_program_instructions, .length = 7, .origin = -1, }; static inline pio_sm_config pwm_program_get_default_config(uint offset) { pio_sm_config c = pio_get_default_sm_config(); sm_config_set_wrap(&c, offset + pwm_wrap_target, offset + pwm_wrap); sm_config_set_sideset(&c, 2, true, false); return c; } static inline void pwm_program_init(PIO pio, uint sm, uint offset, uint pin) { pio_gpio_init(pio, pin); pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); pio_sm_config c = pwm_program_get_default_config(offset); sm_config_set_sideset_pins(&c, pin); pio_sm_init(pio, sm, offset, &c); } #endif
And then based on the Pico SDK example, I came up with some very very simple Arduino code to generate my PWM signals:
#include "hardware/clocks.h" #include "hardware/pio.h" #include "pwm_pio.pio.h" // GPIO for PIO generated PWM output: const uint PWM_PIN = 10; // (1u << 16) is 65,536 or 256 * 256 etc. // 20kHz = 256 * 8 = 2048 // 40kHz = 1024 // 81kHz = 512 // 120kHz = 344 // 162kHz = 256 const uint16_t FREQ = (344); // Choose PIO instance (0 or 1) PIO pio = pio0; uint sm = -1; uint offset = -1; void setup() { Serial.begin(115200); //while (!Serial) continue; // Get first free state machine in PIO 0 sm = pio_claim_unused_sm(pio, true); // Add PIO program to PIO instruction memory. SDK will find location and // return with the memory offset of the program. offset = pio_add_program(pio, &pwm_program); Serial.println("Loaded PIO script at offset " + String(offset,HEX)); pwm_program_init(pio, sm, offset, PWM_PIN); pio_pwm_set_period(pio, sm, FREQ - 1); Serial.println("PIO PWM Enabled"); Serial.println("Setting PWM Duty Cycle to 50%"); pio_pwm_set_level(pio, sm, FREQ*0.5); } void loop() { } // Write `period` to the input shift register void pio_pwm_set_period(PIO pio, uint sm, uint32_t period) { pio_sm_set_enabled(pio, sm, false); pio_sm_put_blocking(pio, sm, period); pio_sm_exec(pio, sm, pio_encode_pull(false, false)); pio_sm_exec(pio, sm, pio_encode_out(pio_isr, 32)); pio_sm_set_enabled(pio, sm, true); } // Write `level` to TX FIFO. State machine will copy this into X. void pio_pwm_set_level(PIO pio, uint sm, uint32_t level) { pio_sm_put_blocking(pio, sm, level); }
This code can generate a PWM frequency of your choice and you can also set the duty cycle using these two functions pio_pwm_set_period() and pio_pwm_set_level().
And here is a 162kHz PWM frequency with a 50% duty cycle seen on the assigned GPIO:
Success!
I now had a simple means of generating high speed PWM signals in a normal low voltage safe environment.
I could now move onto the next part of testing out some MOSFET’s using this PWM signal, as a switch, to see what happens to the voltage levels across the source of the MOSFET and ground.
Switching MOSFETs using +100kHz PWM signals
I started with the first one I could find on my desk. It was a n-channel MOSFET from ONSemi (NTD5867NL) nicely packaged as a breakout board from Freetronics, which also included the necessary resistors.
For my MOSFET testing, I started with a low PWM frequency and then increased the frequency until I reached my target frequency of 120kHz. I was keen to see at what frequency the square wave changed shape. In all cases I kept the duty cycle at 50%.
{gallery:autoplay=false}N-Channel MOSFET |
---|
So, based on my tests (as shown) I was able to reach 80kHz PWM before the NTD5867NL n-channel MOSFET could no longer replicate the clean square wave.
Does this match the specification? I’ll leave it up to you to decide.
Experimenting with a P-Channel MOSFET
I had forgotten to order in some through-hole P-Channel MOSFET’s so I was left soldering on wires to a SOT-23 SMD component. Thankfully, I had a small enough soldering tip to make short work of this little hack.
The tiny MOSFET in question was the NTR1P02LT1-D from Onsemi. According to the datasheet, typical applications for these miniature SMD are DC−DC converters and power management in portable and battery−powered products.
The test circuit a fairly standard p-channel switch circuit which included an additional transistor to switch the MOSFET with a 3V3 microcontroller:
{gallery:autoplay=false}P-channel Circuit |
---|
The results were fairly encouraging as I was able to reach 120kHz. Although it was not ideal.
{gallery:autoplay=false}P-Channel PWM Results |
---|
Then, after a little pondering, I came up with an improvement. By adjusting the PWM duty cycle to 30%:
I was now able to get the desired square wave through this p-channel MOSFET. Happy days.
I’ll leave it up to you to decide whether this output meets expectations based on specifications shown above.
To close off my P-channel experiment, I decided as a quick exercise to compare this P-Channel MOSFET with others, based on timings alone. It seems there are some better than others.
Closing Remarks
Now that I have obtained a better understanding of the behaviour of this specific P-Channel MOSFET, I feel reasonably confident that I can effectively handle the inherent instability of these flyback transformers in a properly controlled manner. The assumption here, of course, is that I can generate the required +100kHz PWM frequencies in a high voltage environment. For this I plan to use an opto-coupler. We'll how that goes in the next blog.
So to conclude, I have now moved beyond the stumbling blocks and onto the stepping stones. Hopefully I can find the time to achieve my objectives before the deadline.