Teensy 4.0 eFlexPWM delay/phase shift

hnei43

Member
Trying to create a 30kHz square wave with a variable phase shift or delay in the signal rise using Teensy 4.0. I am able to see a signal using eFlexPWM on an oscilloscope however can't get the phase shift to work correctly. Is there a better option for creating a variable delayed square wave with consistent frequency and rise time? The following code is the closest I've got.

#include <Arduino.h>
#include <eFlexPwm.h>
using namespace eFlex;
const uint8_t PIN_A = 5; // PWM1_2_A
const uint8_t PIN_B = 6; // PWM1_2_B
SubModule pwmA(PIN_A);
SubModule pwmB(PIN_B);
void setup() {
Config cfg;
cfg.setClockSource(kPWM_BusClock); // 150 MHz clock
cfg.setPrescale(kPWM_Prescale_Divide_1); // No prescale
cfg.setMode(kPWM_EdgeAligned);
cfg.setReloadLogic(kPWM_ReloadPwmFullCycle);
cfg.setPwmFreqHz(30000); // 30 kHz
pwmA.configure(cfg);
pwmB.configure(cfg);
// Manually calculate MOD value (150MHz / 30kHz = 5000 ticks)
const uint16_t mod = 5000;
const uint16_t dutyTicks = mod / 2; // 50% duty
const uint16_t phaseTicks = mod / 4; // 25% phase shift = 90°
pwmA.setInitValue(0); // No phase shift
pwmA.updateDutyCycle(dutyTicks, ChanA);
pwmB.setInitValue(phaseTicks); // Shifted start
pwmB.updateDutyCycle(dutyTicks, ChanB);
// Start timers together — crucial for alignment
pwmA.timer().begin();
pwmB.timer().begin();
}
void loop() {
// Nothing here — PWM runs in hardware
}
 
Standard PWM either modulates one edge only, or both edges in opposite directions (phase-correct PWM), specifically keeping the phase constant.

To modulate both edges in the same direction requires two control registers one for each edge and probably needs to be done with direct register manipulation as its not a desirable thing for PWM normally. Also there is a strict limit to the amount of phase change, for a 50% duty cycle square wave you can only shift the phase just under 180 degree range.

Can you confirm what you are trying to do, what is this waveform for?
 
Standard PWM either modulates one edge only, or both edges in opposite directions (phase-correct PWM), specifically keeping the phase constant.

To modulate both edges in the same direction requires two control registers one for each edge and probably needs to be done with direct register manipulation as its not a desirable thing for PWM normally. Also there is a strict limit to the amount of phase change, for a 50% duty cycle square wave you can only shift the phase just under 180 degree range.

Can you confirm what you are trying to do, what is this waveform for?
Okay thank you. I am setting up a controller to modulate the input for an ultrasonic photo elastic imaging camera. A variable delay would allow to view an ultrasonic wave propagate through a glass sample. Delaying the rise of the signal compared to that of when the ultrasonic wave is created will allow the camera to capture the wave at different points in its propagation. Right now the delay is done through a physical nob that doesn't allow fine control.
 
Maybe, generate 2 PWM signals from the same timer. This will ensure the frequency and base edge are hardware timed. Then set the 2 pwm according to the delay you wand on the second edge delay.
 
For phase-shifted square waves (or any duty cycle), search the forum for the eFlexPWM library. It has a couple of examples of 3-phase, complementary, center-aligned PWM, but even if you want something else, it's a good starting point.
 
Maybe, generate 2 PWM signals from the same timer. This will ensure the frequency and base edge are hardware timed. Then set the 2 pwm according to the delay you wand on the second edge delay.
this code is the closest I've got using separate timers. When viewing on an oscilloscope I see 2 waves in phase when I should be seeing a 90 degree shift. I tried a simple analogwrite phase shift to verify and could see a phase shift no problem on the scope.

#include <Arduino.h>
#include <eFlexPwm.h>
using namespace eFlex;
// === CONFIGURATION ===
const uint32_t PwmFreq = 18000; // 18 kHz PWM frequency
const float PhaseDegrees = 90.0; // Desired phase shift
const uint8_t DutyPercent = 40; // Duty cycle < 50% for visibility
// === OUTPUT PINS ===
// PWM2_SM0A (Pin 4) - Reference
// PWM4_SM0A (Pin 2) - Phase shifted
SubModule Sm20(4, 33); // PWM2_SM0A
SubModule Sm40(2, 3); // PWM4_SM0A
Timer &Tm2 = Sm20.timer(); // PWM2 timer
Timer &Tm4 = Sm40.timer(); // PWM4 timer
void setup() {
Serial.begin(115200);
while (!Serial);
Serial.println("PWM2 (Pin 4) = Reference");
Serial.println("PWM4 (Pin 2) = Phase-shifted");
// === COMMON CONFIGURATION ===
Config cfg;
cfg.setReloadLogic(kPWM_ReloadPwmFullCycle);
cfg.setPairOperation(kPWM_ComplementaryPwmA);
cfg.setPwmFreqHz(PwmFreq);
cfg.setInitializationControl(kPWM_Initialize_LocalSync); // Required
// === CONFIGURE BOTH MODULES ===
if (!Sm20.configure(cfg)) { Serial.println("Sm20 config failed"); while (1); }
if (!Sm40.configure(cfg)) { Serial.println("Sm40 config failed"); while (1); }
// === CALCULATE MOD AND PHASE DELAY ===
uint32_t mod = Tm2.srcClockHz() / PwmFreq;
uint32_t phaseTicks = (mod * PhaseDegrees) / 360.0;
float delayUs = (1.0 / PwmFreq) * (PhaseDegrees / 360.0) * 1e6;
// === SET INIT VALUES ===
Sm20.setInitValue(0); // Reference wave starts at 0
Sm40.setInitValue(phaseTicks); // Delayed wave starts offset
// === SET DUTY CYCLES ===
Sm20.updateDutyCyclePercent(DutyPercent, ChanA);
Sm40.updateDutyCyclePercent(DutyPercent, ChanA);
// === APPLY SETTINGS AND START ===
Tm2.setPwmLdok(); // Load new values
Tm4.setPwmLdok();
Tm2.begin(); // Start reference PWM
Tm4.begin(); // Start delayed PWM
// === Debug Info ===
Serial.printf("MOD: %u ticks\n", mod);
Serial.printf("Phase shift: %u ticks (%.1f°) ≈ %.2f µs\n", phaseTicks, PhaseDegrees, delayUs);
}
void loop() {
// Nothing to do — hardware PWM handles everything
}
 
When posting code can you use the </> button on the form.
It preserves code formatting and makes it easier to read and understand.
I have pasted your formatted code below.

Code:
#include <Arduino.h>
#include <eFlexPwm.h>
using namespace eFlex;
// === CONFIGURATION ===
const uint32_t PwmFreq = 18000; // 18 kHz PWM frequency
const float PhaseDegrees = 90.0; // Desired phase shift
const uint8_t DutyPercent = 40; // Duty cycle < 50% for visibility
// === OUTPUT PINS ===
// PWM2_SM0A (Pin 4) - Reference
// PWM4_SM0A (Pin 2) - Phase shifted
SubModule Sm20(4, 33); // PWM2_SM0A
SubModule Sm40(2, 3); // PWM4_SM0A
Timer& Tm2 = Sm20.timer(); // PWM2 timer
Timer& Tm4 = Sm40.timer(); // PWM4 timer
void setup() {
    Serial.begin(115200);
    while (!Serial);
    Serial.println("PWM2 (Pin 4) = Reference");
    Serial.println("PWM4 (Pin 2) = Phase-shifted");
    // === COMMON CONFIGURATION ===
    Config cfg;
    cfg.setReloadLogic(kPWM_ReloadPwmFullCycle);
    cfg.setPairOperation(kPWM_ComplementaryPwmA);
    cfg.setPwmFreqHz(PwmFreq);
    cfg.setInitializationControl(kPWM_Initialize_LocalSync); // Required
    // === CONFIGURE BOTH MODULES ===
    if (!Sm20.configure(cfg)) { Serial.println("Sm20 config failed"); while (1); }
    if (!Sm40.configure(cfg)) { Serial.println("Sm40 config failed"); while (1); }
    // === CALCULATE MOD AND PHASE DELAY ===
    uint32_t mod = Tm2.srcClockHz() / PwmFreq;
    uint32_t phaseTicks = (mod * PhaseDegrees) / 360.0;
    float delayUs = (1.0 / PwmFreq) * (PhaseDegrees / 360.0) * 1e6;
    // === SET INIT VALUES ===
    Sm20.setInitValue(0); // Reference wave starts at 0
    Sm40.setInitValue(phaseTicks); // Delayed wave starts offset
    // === SET DUTY CYCLES ===
    Sm20.updateDutyCyclePercent(DutyPercent, ChanA);
    Sm40.updateDutyCyclePercent(DutyPercent, ChanA);
    // === APPLY SETTINGS AND START ===
    Tm2.setPwmLdok(); // Load new values
    Tm4.setPwmLdok();
    Tm2.begin(); // Start reference PWM
    Tm4.begin(); // Start delayed PWM
    // === Debug Info ===
    Serial.printf("MOD: %u ticks\n", mod);
    Serial.printf("Phase shift: %u ticks (%.1f°) ≈ %.2f µs\n", phaseTicks, PhaseDegrees, delayUs);
}
void loop() {
    // Nothing to do — hardware PWM handles everything
}
 
Did you mean to put in a delayMicroseconds()?
I need fine control in nanoseconds unfortunately. As a proof of concept I used digitalwritefast and delaymicroseconds to view a wave being delayed on an oscilloscope. I run into a wall when I move to hardware based functions and trying to achieve tunable control in nanoseconds.
 
The eFlexPWM examples show that you can control phase between PWM from sub-modules of the same flexPWM timer. Are you using two different timers because you have already chosen pins and you can't change them?
 
The eFlexPWM examples show that you can control phase between PWM from sub-modules of the same flexPWM timer. Are you using two different timers because you have already chosen pins and you can't change them?
I've tried both the same and separate timers but haven't been able to figure it out. I can use any pins for this as it is the priority.
 
here is the basic project. I currently have a teensy 4.0.

1. Ultrasonic Instrument pulses a transducer. Every time the transducer is pulsed the instrument outputs a trigger signal from its IO port. This signal is consistent but can be changed in the settings. Will range from about 1 to 30 kHz.

2. This trigger signal is read into the teensy board to measure its frequency. Can be done constantly or with a button press.

3. The teensy board takes the measured frequency and creates a square wave with identical frequency.

4. The teensy output square wave is sent to a light source that is connected to a camera system. the square wave will trigger the light on. Variable delay and pulse width on this output square wave will allow the control of when the light is on and for how long allowing us to capture images of the ultrasonic wave created by the transducer at different points throughout its travel.

5. The speed of sound in our case is about 3.25mm/us. In order view smooth small incremental movements of the wave we need control of less than a mm hence the nanoseconds.
 
Okay, thanks. It's very helpful to understand what you are trying to do. A few thoughts. In number 3 you say the Teensy will output a square wave of identical frequency to the input square wave. Instead of doing this with PWM, you should probably do it with input capture and output compare, something like this:

a) input capture on rising edge of input signal, with interrupt
b) in the input capture ISR, set up the rising and falling edges of the output

That would allow you to get the same frequency, with variable delay and pulse width. You would not be able to have a delay of zero, but maybe that is okay. You could control the delay to a resolution of 1 in 150M, or about 6.6 ns. With overclocking of the Teensy, you could do a little better. The max frequency of the timer will likely be F_CPU/4, so 150 MHz @ 600, 200 MHz @ 800, etc.
 
Back
Top