PWM Startup Time

techElder

Well-known member
Are there any specs on how quickly a PWM output will start once the command is executed in a program?

EDIT: I'm using a 28X2 or maybe a 40X2.

Can it be measured in a certain number of cycles?

I'm trying to decide if I can eliminate a gate in a frequency counting project by just starting and stopping the PWM (which is providing the counted frequency). A couple of hundred uSecs would be acceptable as rounding to mSecs is happening anyway.

PS. Yes I know ... RTFDS ... RTFM ... TIOARP (Try It On A Real Picaxe) ... I will once I get somewhere to do that.
 
Last edited:

BeanieBots

Moderator
I don't know for sure but I would estimate it to take 300 - 400uS.
This is based on a nominal 250uS per command at 4Mhz and the fact that PWM has a quite a bit to do when it is first initialised.
It may well be quicker to start PWM and then use pwmduty to turn it on/off.
Also, don't forget that command timings also depend on WHERE they sit within your code.
It's one of the few commands I never bothered to time because 'normally' it's only ever executed the once.
As you say - TIOARP to know for sure!
 

techElder

Well-known member
Thanks.

I forgot to mention that I'm running at 32MHz (8MHz crystal). That should cut down on those guesstimates that you provided.

The suggestion on PWMDUTY sounds like the best way to try it, too.

Also, the plan would be that this PWM ON/OFF would be the primary focus for the program.

I'll have to set something up soon.
 

Goeytex

Senior Member
I would try initializing PWMOUT at zero duty at the start of the program, and then starting/stopping with "PWMDUTY PIN, VALUE" and "PWMDUTY PIN, 0". I would then compare the results to starting/stopping the PWM with PWMOUT and select the fastest method.

I seldom mess about with the SFR registers on Picaxe Chips due to the limitations, but it may be possible to "pokesfr" the timer enable bit (TxCON.2) on/off and do it even faster. Won't hurt to try.

You could also try changing the PWM PIN from INPUT/OUTPUT after the PWM is initially started and see if that works.
 

hippy

Technical Support
Staff member
"Pretty quickly" is all I can offer and "try it and see".

PWMDUTY should have a lower overhead than PWMOUT and a POKESFR to the duty register may be quicker still.

I am not sure if a PWM cycle will complete before it uses a new value and that may affect the timing and at what point in its cycle you update the duty.

If using HPWM out you can possibly kill an output fairly quickly with a POKESFR to the output routing register, but that could mean outputting shorter pulses depending on when routing switches.
 

techElder

Well-known member
Thanks, Hippy.

All of these suggestions sound like they could occur within the 500 uSec. rounding period.

Time for some data. More coming I'm sure.
 

fernando_g

Senior Member
A dumb question. Or perhaps not.

How are you going to measure the period from the time that the command has finalized executing, to the time the PWM output actually becomes active?
 

Goeytex

Senior Member
A dumb question. Or perhaps not.

How are you going to measure the period from the time that the command has finalized executing, to the time the PWM output actually becomes active?
Probably not what some others would do, but I usually issue a "Pulsout pin, 100us" prior to what I am measuring, (in this case starting the PWM). Then with either a dual trace scope or a Saleae Logic analyzer, imeasure the time from the trailing edge of the pulsout signal to the leading edge of the first PWM pulse.
 

techElder

Well-known member
Believe it or not, I was thinking along the same lines. Have to have some sort of trigger.

My project already has a "start" signal so I was going to use that, because that's really where I want to see how much delay there is. Adding the Pulsout will give me a midpoint.

Thanks for that, Goeytex. Good question, fernando_g.
 

AllyCat

Senior Member
Hi,

Make sure that you test both PWMOUT and PWMDUTY. IIRC PWMDUTY codes to an enormous number of Program bytes and I suspect might be relatively slow. Maybe also try the equivalent POKESFR, but I think PWMOUT basically just codes as a POKESFR anyway.

Also bear in mind that that the actual PWM Frequency and Duty Cycle may affect the delay before the first pulse is generated.

Cheers, Alan.
 

techElder

Well-known member
Thanks, Alan. That comparison will be made.

I'm not sure about PWM Freq and Duty changes getting tested, because I'm only interested in one setting. However, it will be a lot easier to do now while I have it set up.
 

techElder

Well-known member
These results are from two of the methods suggested above. I tried to decipher the Microchip datasheet to find "SFR" for "duty register", but my brain just doesn't wrap around it.

The screen shots attached were expanded on screen to obtain a better reading on the actual delays.


TEST#1: Using PWMDUTY to change a running PWM 1MHz clock's DUTYCYCLE from 0 to pwmDutyCycle. The resulting delay was 830 uSecs. Screen jitter was +/- 2 uSecs.

TEST#2: Using PWMOUT to change a running PWM 1MHz clock's DUTYCYCLE from 0 to pwmDutyCycle. The resulting delay was 112 uSecs. Screen jitter was +/- 2 uSecs.

TEST#5: Duplicating TEST#2 except that the PWM frequency was changed to 500KHz. The resulting delay was 112 uSecs. Screen jitter was +/- 2 uSecs.


Code:
[color=Green]'--------------------------
[PLAIN]'------[ SYMBOL DEFS ]-----[/PLAIN]
'--------------------------
'[/color]
[color=Blue]symbol [/color][color=Purple]pwmDutyCycle     [/color][color=DarkCyan]= [/color][color=Purple]b2[/color]
[color=Blue]symbol [/color][color=Purple]pwmPeriod        [/color][color=DarkCyan]= [/color][color=Purple]b3[/color]

[color=Green]'--------------------------
[PLAIN]'------[ DIRECTIVES ]------[/PLAIN]
'--------------------------[/color]
[color=Navy]#picaxe [/color][color=Black]28X2[/color]

[color=Navy]#NO_TABLE

#DEFINE [/color][color=Black]test2     [/color][color=Green]; <<<<< ******** CHANGE FOR EACH TEST ******** >>>>>

'--------------------------
[PLAIN]'------[ INITIALISE ]------[/PLAIN]
'--------------------------
'
'pwmDutyCycle      = 31  ; default 31 for 500KHz at 50% @ 32MHz
'pwmPeriod         = 15   ; default 15 for 500KHz at 50% @ 32MHz[/color]
[color=Purple]pwmDutyCycle      [/color][color=DarkCyan]= [/color][color=Navy]15  [/color][color=Green]; default 15 for 1MHz at 50% @ 32MHz[/color]
[color=Purple]pwmPeriod         [/color][color=DarkCyan]= [/color][color=Navy]7   [/color][color=Green]; default 7 for 1MHz at 50% @ 32MHz[/color]
[color=Blue]setfreq em32[/color]

[color=Green]' Set the freq & duty-cycle.
'     Calculated for 1uSec period; 50% duty-cycle with a 32MHz system clock.[/color]

[color=Blue]pwmout C.1[/color][color=Black], [/color][color=Purple]pwmPeriod[/color][color=Black], [/color][color=Purple]pwmDutyCycle             [/color][color=Green]; 1MHz at 50% @ 32MHz
'pwmout C.1, 15, 31                              ; 500KHz at 50% @ 32MHz[/color]
[color=Blue]pause [/color][color=Navy]500

#ifdef [/color][color=Black]test1[/color]
[color=Green]'--------------------------
[PLAIN]'------[ TEST #1 ]---------[/PLAIN]
'--------------------------
' Initialize PWM at 1MHz. Stop PWM with PWMDUTY.
' Trigger scope with PULSOUT C.0, 100uSec
' Turn on PWM with PWMDUTY
' Repeat[/color]

[color=Black]test_one:[/color]

[color=Blue]pwmduty c.1[/color][color=Black], [/color][color=Navy]0[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]pulsout c.0[/color][color=Black], [/color][color=Navy]80   [/color][color=Green]; 100 uSec. @ 32 MHz[/color]

[color=Blue]pwmduty c.1[/color][color=Black], [/color][color=Purple]pwmDutyCycle[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]goto [/color][color=Black]test_one[/color]

[color=Navy]#endif

#ifdef [/color][color=Black]test2[/color]
[color=Green]'--------------------------
[PLAIN]'------[ TEST #2 ]---------[/PLAIN]
'--------------------------
' Initialize PWM at 1MHz. Stop PWM with PWMOUT duty cycle = zero.
' Trigger scope with PULSOUT C.0, 100uSec
' Turn on PWM with PWMOUT duty cycle = default
' Repeat[/color]

[color=Black]test_two:[/color]

[color=Blue]pwmout C.1[/color][color=Black], [/color][color=Purple]pwmPeriod[/color][color=Black], [/color][color=Navy]0             [/color][color=Green]; 1MHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]pulsout c.0[/color][color=Black], [/color][color=Navy]80   [/color][color=Green]; 100 uSec. @ 32 MHz[/color]

[color=Blue]pwmout C.1[/color][color=Black], [/color][color=Purple]pwmPeriod[/color][color=Black], [/color][color=Purple]pwmDutyCycle             [/color][color=Green]; 1MHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]goto [/color][color=Black]test_two[/color]

[color=Navy]#endif

#ifdef [/color][color=Black]test3[/color]
[color=Green]'--------------------------
[PLAIN]'------[ TEST #3 ]---------[/PLAIN]
'--------------------------
' Initialize PWM at 1MHz.
' Do POKESFR to "duty" register
' Repeat[/color]

[color=Black]test_three:[/color]

[color=Green]'pwmout C.1, pwmPeriod, 0             ; 1MHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Green]'pulsout c.0, 80   ; 100 uSec. @ 32 MHz

'pwmout C.1, pwmPeriod, pwmDutyCycle             ; 1MHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]goto [/color][color=Black]test_three[/color]

[color=Navy]#endif

#ifdef [/color][color=Black]test4[/color]
[color=Green]'--------------------------
[PLAIN]'------[ TEST #4 ]---------[/PLAIN]
'--------------------------
' Initialize PWM at 500KHz. Stop PWM with PWMOUT duty cycle = zero.
' Trigger scope with PULSOUT C.0, 100uSec
' Turn on PWM with PWMOUT duty cycle = default
' Repeat[/color]

[color=Black]test_four:[/color]

[color=Blue]pwmout C.1[/color][color=Black], [/color][color=Purple]pwmPeriod[/color][color=Black], [/color][color=Navy]0             [/color][color=Green]; 500KHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]pulsout c.0[/color][color=Black], [/color][color=Navy]80   [/color][color=Green]; 100 uSec. @ 32 MHz[/color]

[color=Blue]pwmout C.1[/color][color=Black], [/color][color=Purple]pwmPeriod[/color][color=Black], [/color][color=Purple]pwmDutyCycle             [/color][color=Green]; 500KHz at 50% @ 32MHz[/color]

[color=Blue]pause [/color][color=Navy]1[/color]

[color=Blue]goto [/color][color=Black]test_four[/color]

[color=Navy]#endif[/color]
SCREEN SHOT OF TEST#1
test_one_1MHz.png

SCREEN SHOT OF TEST#2
test_two_1MHz.png
 

Goeytex

Senior Member
The results kinda make sense. Picaxe uses 10-bit pwm duty resolution. This means that at least 2 writes are needed to set the duty cycle. How this is done by PWMDuty only the programmers really know.

Have a look at the PIC Datasheet Section 14.3.2 to see the steps typically required to setup/start pwm. Section 14.3.5 indicates what registers are used for duty cycle.
 

techElder

Well-known member
This is exactly why I've been using a PICAXE for years! :)

14.3.5 PWM DUTY CYCLE
The PWM duty cycle is specified by writing a 10-bit value to multiple registers: CCPRxL register and DCxB<1:0> bits of the CCPxCON register. The CCPRxL contains the eight MSbs and the DCxB<1:0> bits of the CCPxCON register contain the two LSbs. CCPRxL and DCxB<1:0> bits of the CCPxCON register can be written to at any time. The duty cycle value is not latched into CCPRxH until after the period completes (i.e., a match between PRx and TMRx registers occurs). While using the PWM, the CCPRxH register is read-only.
 

hippy

Technical Support
Staff member
As Goeytex notes there are quite a number of registers used to control PWM with control bits and control values spread throughout the registers. This means multiple peeks and pokes and shifts with masking to preserve existing bits are often required to change a setting without affecting other things.

The 8-bit MSB of the duty value is stored in a single SFR (CCPRxL) so it is possible to change duty with just a single POKESFR to CCPRxL. The best way to do that is to issue a PWMOUT with appropriate frequency and a zero duty at the start then use POKESFR to change the upper 8 bits of the duty while leaving the two lower bits at zero.
 
Top