PWM Phase shift

I used PWM library to generate PWM signals of desired frequency. The problem is i am not able to give a phase shift to the PWM. For example i am generating a PWM of frequency of 25Khz on PIN 9 and i need a phase shift of 90 degrees(i.e a phase shift of 40 microseconds) of the same PWM on PIN10. ‘delay’ command is not working. Please suggest a solution and thank you.

fullcontrol_anyduty.ino (478 Bytes)

To control the PWMs with that kind of granularity, you’ll need to do direct register-level programming of the SAM timers. The Arduino PWMs are not setup to do that. They just provide very basic PWM functionality.

Regards,
Ray L.

This example uses Stepper Motor Mode of the PWM Controller (no Timer/Counters are used).

Four 25kHz PWM signals:

Pin Phase 34 0° 36 90° 35 180° 37 270°

void setup() {
  pwmc_setup();
}

void loop() {
}

void pwmc_setup()
{
  //Configure PWM channels 0,1 (PWML0,PWMH0,PWML1,PWMH1), (port C.2,C.3,C.4,C.5), (pins P34,P35,P36,P37)
  REG_PIOC_PDR = 0x3C;                   //B111100, PIO Disable Register
  REG_PIOC_ABSR = REG_PIOC_ABSR | 0x3C;  //B111100, Peripheral AB Select Register
  REG_PMC_PCER1 = REG_PMC_PCER1 | 16;    //Peripheral Clock Enable Register 1 (activate clock for PWM, id36)
  REG_PWM_ENA = REG_PWM_SR | B11;        //PWM Enable Register | PWM Status Register (activate channels 0,1)
  REG_PWM_SMMR = 0x10003;                //Stepper Motor Mode Register
  REG_PWM_CPRD0 = 840;                   //Channel0 Period Register (84mhz/4/3360=25kHz=4µs period)
  REG_PWM_CPRD1 = 840;                   //Channel1 Period Register (84mhz/4/3360=25kHz=4µs period)
  REG_PWM_CDTY0 = 420;                   //Channel0 Duty Cycle Register
  REG_PWM_CDTY1 = 420;                   //Channel1 Duty Cycle Register
}

thanks a lot , can u please help me to shift it not only 90 but can I shift two PWM signlas by 10 degree for example

thanks in advance

dlloyd: This example uses Stepper Motor Mode of the PWM Controller (no Timer/Counters are used).

Four 25kHz PWM signals:

Pin Phase 34 0° 36 90° 35 180° 37 270° ...

Nice sketch, I checked cabling and did measure with 24MHz logic analyzer. Reading definition of phase shift, shouldn't it be this?

Channel Pin  Phase
0       34   0°
2       36   270°
1       35   180°
3       37   90°

|500x236

Hermann.

Nice, works with up to 10.5MHz!

With these changes:

  REG_PWM_CPRD0 = 2;//840;                   //Channel0 Period Register (84mhz/4/3360=25kHz=4µs period)
  REG_PWM_CPRD1 = 2;//840;                   //Channel1 Period Register (84mhz/4/3360=25kHz=4µs period)
  REG_PWM_CDTY0 = 1;//420;                   //Channel0 Duty Cycle Register
  REG_PWM_CDTY1 = 1;//420;                   //Channel1 Duty Cycle Register

Hermann.

can u please help me to shift it not only 90 but can I shift two PWM signlas by 10 degree for example

I cannot tell how to do 10° phase shift on Arduino Due in software.

But I looked into my hardware pool and found a SN74HCU04 hex inverter. I knew that each single logic operation does have some delay. So I just did apply 6 inversions sequentially to channel 3 from above and measured that as channel 4. As can be seen below the signal gets delayed by 40ns. I did use "REG_PWM_CPRD0 = 28;" in the sketch resulting in 1.34μs period. The resulting phase shift is: 360° / (1340 / 40) = 10.75°

So you have to choose some logic gates as in this example and produce the delay needed for 10° phase shift, depending on the frequency you are interested in.

Hermann. |500x211

|500x375 a  

I did shorten the 1Y-2A, 2Y-3A, 3Y-4A, 4Y-5A and 5Y-6A connections between the SN74HCU04 hex inverters as much as possible. And I made sure that all paths from measure point to logic analyzer have same length (besides the lengths added by the hex inverter connections).

Now the 270° phase shift signal from Arduino Due (pin 37) is sent to SN74hcu04 1A. Logic analyzer channel 0/1/2/3 do measure 1A, 2A, 3A and 4A, that is 0-3 hex inverters inserted, at 400Msps: |500x375

1/2/3 inverters add 3/6/8 or 3/7/8 times 2.5ns delay to channel 0 rising edge (channel 1 and 3 are the inverse of signal on channel 0). This is the best a 400Msps logic analyzer can determine: |500x194

Hermann.

I read that electrical signal speed is of the order of speed of light. I bought 250m copper wire to measure the delay with 400Msps logic analyzer. Should be in the range of (1 / 0.5) * 3.3ns/m *250m = 1650ns and 825ns. Today I measured resistance of that wire, showed 89.3Ω. Will do logic analyzer delay measurements tomorrow.

Hermann.

I did the measurements, and I have to say I am a bit surprised on was I saw.

This was the setup (click on any photo below to get it big): |500x375

I configured Arduino Due to generate a 2.5KHz PWM frequency, fast enough to get many pulses, and slow enough to not run into potentially high frequency issues. The same signal was measured directly (channel 0) and with a delay by passing through the 250m of copper wire (channel 1). This is the view on successive pulses. |500x149

It shows a little delay an falling edge, followed by an additional peak. The rising edge does not show a delay (at that resolution).

So let me start with analyzing the falling edge and the peak, zoomed in here: |500x161

The delay on falling edge (2.4μs * 0.6 = 1.44μs) is something I think I can explain. The speed of passing 1m of the copper wire is 1000000/(1.44/250) = 173611111.1m/s or 173,611km/s, slightly less than 60% of speed of light.

The peak occurs 7.44μs after the delayed falling edge. I don't have an explanation, maybe the 250m have some coil effect?

Here I zoomed into the rising edge to get timings at 2.5ns resolution of 400Msps logic analyzer: |500x166

A delay of only 5ns for 250m would correspond to 167 times the speed of light, and Einstein showed that this is not possible. Here the copper cable coil effect must be the cause.

Copper cable, at least in coil form, is not good for getting signal delay and therefore phase shift. I will cut 1m from copper wire and see whether delay is linear (should give a delay like 1.44/250 = 5.76ns).

Explanations for the peak following the delayed falling edge and the super minimal delay for 250m copper wire on rising edge are appreciated.

Going thru that big coil of wire is going to have an impact on the signal integrity. Try looking at the signal with an oscilloscope and see what it looks like.

CrossRoads: Try looking at the signal with an oscilloscope and see what it looks like.

I do have several logic analyzers up to 400Msps. But I only have a SMO DSO150 (toy) oscilloscope that can do 2.250Ksps which is unusable for delays in nanosecond range.

CrossRoads: Going thru that big coil of wire is going to have an impact on the signal integrity.

I eliminated the coil issue in cutting a 10m coper wire first and laying that in a big loop in room without any crossing. Later I did cut the 10m into pieces of 7m and 3m.

This is the falling edge, still 2μs jitter after the "real" delayed falling edge: |500x214

Zooming in on delayed falling edge shows 42.5ns delay:

Zooing in on the delayed rising edge shows 40ns delay (there is no jitter after delayed rising edge). |500x233

These are the measurements I did:

| length [m] | rising [ns] | falling [ns] | | - | - | - | | 10 | 40 | 42.5 | | 7 | 27.5 | 30 | | 3 | 10 | 12.5 |

Signal speed in my copper cable is therefore between 240,000km/s and 250,000km/s (80%-83% of speed of light):

$ bc -ql
1000000/(40/10)
250000.00000000000000000000
1000000/(42.5/10)
235294.11764705882352941176
1000000/(27.5/7)
254545.45454545454545510082
1000000/(30/7)
233333.33333333333333364444
1000000/(10/3)
300000.00000000000000030000
1000000/(12.5/3)
240000.00000000000000038400

The jitter after delayed falling edge makes just copper wire unusable for producing (PWM frequency) phase shift.

The jitter after delayed falling edge makes just copper wire unusable for producing (PWM frequency) phase shift.

Nice work!

Possibly some of the jitter (±11.9ns) is because the PWM channels aren't synchronized. The Due's PWMC has registers for synchronizing the PWM channels but I haven't tried them. Maybe there's some examples somewhere.

Also I think there would be an inductive component in the spool (coil) of wire ... probably creating some ringing on the signal, hence more jitter and measurement error.

The Due’s PWMC has registers for synchronizing the PWM channels…

To synchronize the PWM controller channels you just need to set the PWM Sync Channels Mode register (REG_PWM_SCM) for the given channels:

REG_PWM_SCM |= PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4 |  // Set the PWM channels as synchronous
               PWM_SCM_SYNC3 | PWM_SCM_SYNC2 | PWM_SCM_SYNC1 | PWM_SCM_SYNC0;

Then activate channel 0, (in synchronous mode there’s no need to specify channels 1-7), using the PWM enable register (PWM_ENA):

REG_PWM_ENA = PWM_ENA_CHID0;           // Enable the PWM channels, (only need to set channel 0 for synchronous mode)

Finally, after loading the duty cycle update registers (REG_PWM_CDTYUPDx), trigger an update by setting Update Unlock bit (UPDULOCK) the PWM Sync Channels Update Control Register (REG_PWM_SCUC):

REG_PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period

It’s also possible to set the PWM controller to automatically update the duty cycle every timer cycle, (rather than using the trigger above), by setting the UPDM: Synchronous Channels Update Mode bitfield in the PWM Sync Channels Mode register (REG_PWM_SCM) to MODE 1.

Here’s an example:

// Enable synchronous, centre-aligned, 14-bit resolution PWM at 2kHz on 8 channels
void setup() {
  // PWM set-up on pins DAC1, A8, A9, A10, D9, D8, D7 and D6 for channels 0 through to 8 respectively
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                                               // Enable PWM 
  REG_PIOB_ABSR |= PIO_ABSR_P19 | PIO_ABSR_P18 | PIO_ABSR_P17 | PIO_ABSR_P16;     // Set the port B PWM pins to peripheral type B
  REG_PIOC_ABSR |= PIO_ABSR_P24 | PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21;     // Set the port C PWM pins to peripheral type B
  REG_PIOB_PDR |= PIO_PDR_P19 | PIO_PDR_P18 | PIO_PDR_P17 | PIO_PDR_P16;          // Set the port B PWM pins to outputs
  REG_PIOC_PDR |= PIO_PDR_P24 | PIO_PDR_P23 | PIO_PDR_P22 | PIO_PDR_P21;          // Set the port C PWM pins to outputs
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);                                // Set the PWM clock A rate to 84MHz (84MHz/1)
  REG_PWM_SCM |= PWM_SCM_SYNC7 | PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4 |  // Set the PWM channels as synchronous
                 PWM_SCM_SYNC3 | PWM_SCM_SYNC2 | PWM_SCM_SYNC1 | PWM_SCM_SYNC0;  
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CMR = PWM_CMR_CALG | PWM_CMR_CPRE_CLKA;    // Enable centre aligned PWM and set the clock source as CLKA
    PWM->PWM_CH_NUM[i].PWM_CPRD = 21000;                              // Set the PWM period register 84MHz/(2*2kHz)=21000;
  } 
  REG_PWM_ENA = PWM_ENA_CHID0;           // Enable the PWM channels, (only need to set channel 0 for synchronous mode)
  for (uint8_t i = 0; i < PWMCH_NUM_NUMBER; i++)                      // Loop for each PWM channel (8 in total)
  {
    PWM->PWM_CH_NUM[i].PWM_CDTYUPD = 10500;                           // Set the PWM duty cycle to 50% (21000/2=10500)
  } 
  REG_PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop() {}

I am not sure why you are talking on PWM channel synchronization. Please see this zoomin into my previous setup photo. The whole measurements were done for only one PWM signal (pin34). Logic analyzer channel 0 measured the signal directly, channel 1 after passing through the 250m copper cable: |500x322

Also I think there would be an inductive component in the spool (coil) of wire ... probably creating some ringing on the signal, hence more jitter and measurement error.

As I said, I did the measurements for 10m, 7m and 3m without coil effect. I used the living room to lay the 10m cable in one big loop without any crossings, so no coil effects.

Can someone give the full code for arduino uno of 3 phase pwm with 120 degree phase shift?

... code for arduino uno ...

You post in this thread in Arduino Due forum, the answer is for Arduino Due.

The above software phase shift solution only allows for 0°/90°/180°/270" phase shift.

But I found a solution based on the code provided by MartinL (because for the 0°/90°/180°/270" phase shift code duty settings don't work, always 50%). Find below my sketch, the numbering of the channels is interesting at least. I found out by try and error starting with MartinL's code.

I did create synchronized PWM on pins D9/D8/D7 (logic analyzer channels 0/1/2): |500x375

Below sketch demonstrates as an example how to phase shift PWM signal with 60% duty by 120°.

The shifted PWM signal needs to start at 120° = 33.3% of the PWM signal. It has to end at 33.33% + 60% = 93.33%.

The trick I use is to generate two signals (channel 1 and channel 2), that when combined by XOR gate result in the 120° phase shifted signal: |500x272

The only logic IC I currently have at home is a DIP-14 INVERTER one, so I cannot demonstrate now. I just ordered five DIP-14 2-input XOR for 2.18$ (as well 10 DIP-14 2-input NOR/NAND/INVERTER).

Hermann.

// Enable synchronous, 14-bit resolution PWM on 3 channels
void setup() {
  // PWM set-up on pins D9, D8 and D7 
  REG_PMC_PCER1 |= PMC_PCER1_PID36;                              // Enable PWM 

  REG_PIOC_ABSR |= PIO_ABSR_P23 | PIO_ABSR_P22 | PIO_ABSR_P21;   // Set the port C PWM pins to peripheral type B
  REG_PIOC_PDR  |= PIO_PDR_P23  | PIO_PDR_P22  | PIO_PDR_P21;    // Set the port C PWM pins to outputs
  
  REG_PWM_CLK = PWM_CLK_PREA(0) | PWM_CLK_DIVA(1);               // Set the PWM clock A rate to 84MHz (84MHz/1)
  
  REG_PWM_SCM |= PWM_SCM_SYNC6 | PWM_SCM_SYNC5 | PWM_SCM_SYNC4;  // Set the PWM channels as synchronous

  PWM->PWM_CH_NUM[0].PWM_CMR = PWM_CMR_CPRE_CLKA;                // Enable centre aligned PWM and set the clock source as CLKA
  PWM->PWM_CH_NUM[0].PWM_CPRD = 21000;                           // Set the PWM period register 84MHz/(2*2kHz)=21000;

  PWM->PWM_CH_NUM[1].PWM_CMR = PWM_CMR_CPRE_CLKA;                // Enable centre aligned PWM and set the clock source as CLKA
  PWM->PWM_CH_NUM[1].PWM_CPRD = 21000;                           // Set the PWM period register 84MHz/(2*2kHz)=21000;

  PWM->PWM_CH_NUM[2].PWM_CMR = PWM_CMR_CPRE_CLKA;                // Enable centre aligned PWM and set the clock source as CLKA
  PWM->PWM_CH_NUM[2].PWM_CPRD = 21000;                           // Set the PWM period register 84MHz/(2*2kHz)=21000;

  REG_PWM_ENA = PWM_ENA_CHID0;                                   // Enable the PWM channels, (only need to set channel 0 for synchronous mode)

  PWM->PWM_CH_NUM[0].PWM_CDTYUPD = 0;                            // ?!?!?
  PWM->PWM_CH_NUM[4].PWM_CDTYUPD = 12600;                        // Set the PWM duty cycle to 60%            (21000*0.6=12600)
  PWM->PWM_CH_NUM[5].PWM_CDTYUPD = 7000;                         // Set the PWM duty cycle to 120°=33.33%    (21000/3=7000)
  PWM->PWM_CH_NUM[6].PWM_CDTYUPD = 19600;                        // Set the PWM duty cycle to 33%+60%=93.33% (12600+7000=19600)

  REG_PWM_SCUC = PWM_SCUC_UPDULOCK;      // Set the update unlock bit to trigger an update at the end of the next PWM period
}

void loop() {}

I did not want to wait for the 74HC86 logic XOR to arrive.
The Due does nothing after configuring PWM in setup() of previous sketch.
So why not let Due read channel 1 and channel 2, compute the xor value and set channel 3 signal?

First you can see here that below sketch does work, channel 3 is 120° phase shifted channel 0 signal:

Ardunio Due does not allow pins to be configured for INPUT and OUTPUT at the same time.
Setting a PIN for INPUT does disable the PWM signal configured for it.
What I did is to duplicate D8/D7 (channel 1/2) to D6/D5 and use D4 for channel 3, click for details:

This is the diff to previous sketch:

$ diff sketch_feb22d.ino sketch_feb24a.ino 
0a1,6
> // C.24/C.25/C.26 = D6/D5/D4
> Pio *p = digitalPinToPort(4);
> uint32_t b4 = digitalPinToBitMask(4);
> uint32_t b5 = digitalPinToBitMask(5);
> uint32_t b6 = digitalPinToBitMask(6);
> 
29a36,39
> 
>   pinMode(4, OUTPUT);
>   pinMode(5, INPUT);
>   pinMode(6, INPUT);
32c42,53
< void loop() {}
---
> void loop() {
>   for(;;) {
>     uint32_t v;
>     v = p->PIO_PDSR;
>     
>     if (bool(v & b6) ^ bool(v & b5)) {
>       p->PIO_SODR = b4;
>     } else {
>       p->PIO_CODR = b4;
>     }
>   }
> }
$

Some global variable definitions, configuring pins 4-6 for INPUT/OUTPUT, and a loop computing the signal xor value. I used direct port access to minimize the timing footprint of the endless loop’s body.

Zooming into raising edge of channel 3 shows a delay of 375ns:

Zooming into falling edge of channel 3 shows a delay of 0.4μs:

The difference with 74HC86 logic XOR woud be a delay of only 7-9ns according the datasheets.

Hermann.

https://stamm-wilbrandt.de/en/forum/sketch_feb24a.ino

can u help me for the arduino uno or arduino Mega 2560 coding to create the 3 pwm waveform ?

Sorry, I fried my only Mega, cannot help on that.

I bought a CD74HCT86E XOR IC at an electronics store.

The overall picture is the same as in previous posting: |500x260

Rising edge of 120° phase shifted signal (channel 3) is only 22.5ns delayed: |500x284

Falling edge of 120° phase shifted signal is only 22.5ns delayed: |500x269

Hermann. |500x375