A slicker way to be constantly changing the duty cycle?


Closed Thread
Results 1 to 15 of 15
  1. #1
    Join Date
    Mar 2009
    Posts
    653

    Default A slicker way to be constantly changing the duty cycle?

    Having setup HPWM, what's the least invasive way to change the duty cycle?

    it may well be that the HPWM command is it...but i'd betom have affirmation from a HPWM expert!

    It's in the back of my mind that the PicBAsic HPWM is likely to be doing several things, whereas having got HPWM up & running - I just want to update the duty cycle throught my program on the fly.

    Any top tips on the slickest way to update the duty cycle without causing any possible stuttereing 'behind the scenes'?

    If it helps, I'm dabbling with an 16f1824 ...specifically wanting leds to fade niceley without any flickering.

  2. #2
    Join Date
    May 2007
    Posts
    604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    The most effective way would be to directly alter the values in the CCPRxL (and CCPxCON, bits 1:0) registers.
    Why pay for overpriced toys when you can have
    professional grade tools for FREE!!!

  3. #3
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Thanks rmteo (I was kind of figuring that's what you might say!)

    I'm only wanting to use 8 bit PWM, but it seems I'm still going to have to update two registers to control the duty cycle (what were microchip thinking of?!)...

    CCP2 Duty Cycle = CCPR2L:CCP2CON<5:4>

    To my next question....how do I load a decimal value (0-255) across two registers...I've got to take the bottom two bits of the decimal to binary conversion & put them into CCP2CON<5:4> with the remaining bits going into CCPR2L.

    Never done this before - hand holding most welcome!

    (it may well be the solution is simple - dunno, but my immediate thought is wouldn't it be cool if you could construct a pseudo variable that relate to bits of target registers)

  4. #4
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Hi Hank,
    Usually it's done like this:
    Code:
    CCP2CON.4 = Duty.0       'Bit 0
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L = Duty >> 2        'Bit 2-7
    Now, the resolution you'll actually get depends on the frequency you're using as well as the prescaler ratio. You MAY end up with 9 or 10 bits resolution in which case you must scale up "your" 8 bit dutycycle value to match the resolution of the hardware. Otherwise you might not be able to "swing" the dutycyle all the way to 100% - if that's what you need.

    Have you checkes out Darrels MIBAM routine? Never played with myself but it's supposed to do wonders for multiple fading LEDs.

    /Henrik.

  5. #5
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Quote Originally Posted by HenrikOlsson View Post
    Hi Hank,
    Usually it's done like this:
    Code:
    CCP2CON.4 = Duty.0       'Bit 0
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L = Duty >> 2        'Bit 2-7
    Now, the resolution you'll actually get depends on the frequency you're using as well as the prescaler ratio. You MAY end up with 9 or 10 bits resolution in which case you must scale up "your" 8 bit dutycycle value to match the resolution of the hardware. Otherwise you might not be able to "swing" the dutycyle all the way to 100% - if that's what you need.

    Have you checkes out Darrels MIBAM routine? Never played with myself but it's supposed to do wonders for multiple fading LEDs.

    /Henrik.
    Many thanks Henrik........Deep down I knew the answer would be splendidly (embarrasingly!) simple - thanks for the lightning response.

    Sigh, as one case closes another opens.... I'm running a 16f1824 @4mhz oscillator, and I need the frequency of the PWM to be inaudible (therefore say about 20khz+) ....how can I be sure that my resolution remains at 8 bits.

    No, I was not aware of Darrels MIBAM routine....I'll seek it out.

  6. #6
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Hi Hank,
    I usually cheat and use MisteE's PICMultiCalc tool to determine things like prescaler ratio and PWM resolution but I'll try to explain it. If nothing else as a learning exercise for myself....

    The PWM period start when TMR2 is 0 and ends when TMR2 matches PR2. When you're running at 4MHz TMR2 "ticks" at 1MHz (with a prescaler of 1:1) so if you write 100 (or 99 actually because we're starting at 0) to PR2 you'll get a PWM period of 100us so the PWM frequency is 10kHz. Put 49 in PR2 and you'll get a PWM period of 50us (20Khz) and so on.

    Now, you would think that if you put 99 in PR2 there are 100 "steps" to the "cycle"/period and that you therefor get 100 discrete dutycyles but this is not the case because TMR2 creates the 8 high order bits and the internal instruction clock creates the 2 low order bits of a 10bit timebase (alternatively 2 bits from the prescaler when using anything but 1:1).

    What this means is that the number of discrete dutycyles ratios you get is basically (PR2+1)*4 so if you put 99 in PR2 (for a PWM frequency of 10kHz) you'll get (99+1)*4=400 "steps" which you'll have to have 9 bits to store. If you put 49 in PR2 the frequency will be 20kHz and the number of dutycycle ratios will be (49+1)*4=200 which fits nicely in a byte.

    If you want to maximize the resolution while still keeping it inside a byte you could set PR2 to 63 which will give you a frequency of 15625Hz and a resolution of 8 full bits ( (63+1)*4=256 )

    Well, I hope I've got this close to reallity (if not I hope someone will correct me) and that it makes at least a bit of sense.

    /Henrik.

    PS. If "all" you're doing with the PWM output is fading LEDs why do you need it to be inaudible?

  7. #7
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Thanks Henrik...really gracious of you to make the effort - I'm gonna have to read that one a few times, but in the meantime, here's something I can answer...

    Quote Originally Posted by HenrikOlsson View Post
    PS. If "all" you're doing with the PWM output is fading LEDs why do you need it to be inaudible?
    .....because ultimately (after I've got on top of all this by playing/dabbling), I intend integrating what I've learnt in some audio circuits (ie using PWM'ed LEDs for visual indicators - flashing/fading, on/off etc) ....the little bit of playing I done with HPWM to date, I've noticed the HPWM carrier can find/bleed its way into the audio signal chain ...hence wanting to put it the HPWM carrier outside the hearing band. I'm quite shocked at the audible clicking too, so I'm trying to dial the clicking out by ramping up HPWM very quickly rather than hard off/on switching.
    Last edited by HankMcSpank; - 23rd April 2011 at 13:59.

  8. #8
    Join Date
    May 2007
    Posts
    604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    This contest http://www.picbasic.co.uk/forum/showthread.php?t=14652 contains some elements of what you are wanting to do.

    I did it using PWM and here is a video of 2 of those channels. The contest requires 4 independent, variable on-the-fly (blink-rate) channels.



    The timebase is 10uS so the PWM frequency 25KHz. The duty cycle of the upper trace is ramped from 0-100-0% - to give pulsating effect - at about 0.5Hz. The lower one is at 1.0Hz. The required blink rate should be continuously variable in the range of 0.5-8.0Hz on each of the 4 channels/LEDs for the contest.
    Last edited by rmteo; - 23rd April 2011 at 15:34.
    Why pay for overpriced toys when you can have
    professional grade tools for FREE!!!

  9. #9
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    thanks rmteo...just seen your reply, I shall look at all tha tomorrow.

    ok, so I got too big for my boots, thought I'd go hardcore & try to get HPWM working with the PIC's regsiters (vs the HPWM command) - eeeugghh! (it was worth doing - just to remind myself that I'll always be the type who graps in the dark!)

    I've got it working, but only by directly loading the PWM 'period' registers directly like this (important bits in red)

    Code:
               
    PR2= 63                               'THIS SETS THE PWM FREQUENCY (15.625KHZ @4MHZ)
    CCPTMRS0 = %11110011     'CCP Timer select register - select CCP2 USING TIMER2
    T2CON = %00000100           'TIMER2 ON 1:1 PRESCALER 1:1 POSTSCALER
    TRISC.3 = 0                         'Enable the CCP2 pin output driver by setting the bit
     
    temp:
    CCP2CON.4 = 1       ' these three lines set the duty cycle to be 255   
    CCP2CON.5 = 1       ' these three lines set the duty cycle to be 255  
    CCPR2L = 255         ' these three lines set the duty cycle to be 255 (2 MSBs ignored)
    pause 1000
    CCP2CON.4 = 0       ' these three lines set the duty cycle to be 0   
    CCP2CON.5 = 0       ' these three lines set the duty cycle to be 0  
    CCPR2L = 0             'these three lines set the duty cycle to be 255 (2 MSBs ignored)
    pause 1000
    goto temp
    the problem with that method (& it works - get a flashing LED at about 1 sec on, 1 sec off), is that setting the duty cycle is brainache. So I wanted to use the method discussed earlier with Henrik (where I map the bts to my easier to massage variable called "duty")...

    Code:
    Duty var byte
    CCP2CON.4 = Duty.0       'Bit 0
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L = Duty >> 2       'Bit 2-7
     
    temp:
    duty = 255         ' these three lines set the duty cycle to be 255 (2 MSBs ignored)
    pause 1000
    duty = 0           'these three lines set the duty cycle to be 255 (2 MSBs ignored)
    pause 1000
    goto temp
    Problem is now ...the LED he no flashee!!

    Anyone got a clue why I can't map the PWM 'period' bits to my variable called Duty? (or more to the point why it doesn't appear o work, whereas the top code does)

    Edit: Ok, think I've got a kludge, but this isn't working how I'd imagined...

    Code:
    temp10:
    DUTY = 0
    CCP2CON.4 = Duty.0       'Bit 0
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L = Duty >> 2       'Bit 2-7
    pause 1000
    DUTY = 1
    CCP2CON.4 = Duty.0       'Bit 0
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L = Duty >> 2       'Bit 2-7
    pause 1000
    goto temp10
    in other words, I'm having to update the 'mapped' period register bits every time I change the duty variable? (I'd have thought Henrik's way of mapping them would mean if I change the contents of the duty variable then the mapped bits would automatically change too?)
    Last edited by HankMcSpank; - 24th April 2011 at 01:18.

  10. #10
    Join Date
    Aug 2010
    Location
    Maryland, USA
    Posts
    869


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Hi Hank, Let me take a stab at this for you.

    In your first example, you set the duty directly with constants. No surprizes there I don't think.

    In the second example, you are changing the variable named DUTY, but this is not associated with the ccp reg's, so they never get changed.

    In the third example, you are using a variable named DUTY to to assign values to the ccp reg's. This is the method I would use as it seem the easiest to me. It takes care of all the bit switching and all your code needs to do is change the value of DUTY.

    To combine the second and third example, make the ccp assigns in the third example a subroutine. Then in the second example, before each pause, call the sub. I think in this way it will seem like it does exactly what you want.

    To do what I think you are trying, I think you need to alais the ccp reg's. for example:

    Code:
    CCP2CON.4    VAR Duty.0       'Bit 0
    CCP2CON.5    VAR Duty.1       'Bit 1
    CCPR2L.0    VAR Duty.2
    CCPR2L.1    VAR Duty.3
    CCPR2L.2    VAR Duty.4
    CCPR2L.3    VAR Duty.5
    CCPR2L.4    VAR Duty.6
    CCPR2L.5    VAR Duty.7
    Now I have NO idea if this will actually work, Try at you own risk
    -Bert

    The glass is not half full or half empty, Its twice as big as needed for the job!

    http://foamcasualty.com/ - Warbird R/C scratch building with foam!

  11. #11
    Join Date
    Oct 2005
    Location
    Sweden
    Posts
    3,604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Hi Hank, Bert,

    Bert already gave you the answer, but just to clarify. The code I showed you doesn't alias anything. It is code that needs to execute when ever you want to change the dutycycle - as you've discovered. The easiest way IMHO is to simply create a subroutine and GOSUB it when you want the dutycycle changed, like:
    Code:
    Duty VAR BYTE
    i VAR Byte
     
    Main:
    For i = 0 to 255              'Fade up
      Gosub SetIt
      Pause 2
    Next
     
    For i = 255 to 0 Step -1  'Fade down
      Gosub SetIt
      Pause 2
    Next
     
    Goto Main
     
    SetIt:
      CCP2CON.4 = Duty.0
      CCP2CON.5 = Duty.1
      CCPR2L = Duty >> 2
    RETURN

  12. #12
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Hi Guys.

    Firstly Bert....apologies I didn't post the full code (ironically, I thought for simplicity's sake)....further up my program, I had exactly as you'd outlined, namely...

    Code:
     
    CCP2CON.4 =  duty.0
    CCP2CON.5 =  duty.1
    CCPR2L.0   =  duty.2 
    CCPR2L.1   =  duty.3
    CCPR2L.2  =   duty.4
    CCPR2L.3  =   duty.5
    CCPR2L.4  =   duty.6
    CCPR2L.5  =   duty.7
    in fact I tried Henrik's simpler way first, but when that didn't work (ie period register didn't update as I thought they should), I mapped them longhand as above.

    Apologies for not showing the full picture....and many thanks for helping me out (again!)

    Henrik,

    Sometimes I get so wrapped up that I lose sight of the obvious (I had many things on the go yesterday ....ultimately cracking a long standing coding issue (yay) ....but while doing so, I think my brain got fogged).....yep, my variable 'Duty' is not 'tracked' in real time as it were, which means I do need to update the period setting bits bt y pointing at my variable everytime I wish to change the duty...so yes, your sub routine is the order of the day.

    Many, many thanks

    ....thanks heavens for forums like this with people so willing to help.
    Last edited by HankMcSpank; - 24th April 2011 at 10:40.

  13. #13
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Just as a footnote to this, going the longhand way with HPWM & setting the registers manually (vs copping out & using the easy HPWM command), has rescued back a fair bit of program space ...and my LEDS are fading a bit smoother too.

    So to sum up, this will get your HPWM running on CCP2 (Pin 7) on a 16f1824....

    Code:
    DEFINE CCP2_REG PORTC     'route CCP2 output to PortC.3  Pin7 (16f1824)
    DEFINE CCP2_BIT 3         'route CCP2 ouput to PortC.3  Pin7 (16f1824)
    duty var byte
     
    CCP2CON    = %00001100   ' Turn HPWM on on CCP2
    TRISC.3   = 1            'Disable the CCP2 pin output driver by setting the bit
    CCPR2L.6 = 0             'I'm only using 8 bit PWM so clear the top two bits (these would be using for 9/10 bit HPWM)
    CCPR2L.7 = 0             'I'm only using 8 bit PWM so clear the top two bits (these would be using for 9/10 bit HPWM)
    PR2          = 63        'This sets the PWM period (frequency) to 15.625KHZ @4MHZ  (I hope so at least - I've  not scoped it!) and yields the full 255 bits of resolution
    CCPTMRS0 = %11110011     'This is the CCP Timer select register - these bits select 'CCP2 USING TIMER2'
    T2CON = %00000100        'TIMER2 ON 1:1 PRESCALER 1:1 POSTSCALER
    TRISC.3   = 0            'Enable the CCP2 pin output driver by clearing the bit
     
    Main:
    duty = 255
    gosub Change_PWM
    pause 500
    duty = 0
    gosub Change_PWM
    pause 500
    goto Main
     
    Change_PWM:              'the following three lines for 8 bit PWM
    CCP2CON.4 = Duty.0       'Bit 0    
    CCP2CON.5 = Duty.1       'Bit 1
    CCPR2L    = Duty >> 2    'Bit 2-7
    return
    the above code will give you some LED blinkage (you can never have enough LED blinkage) to prove it's working ....all you have to do is change the variable "Duty" & then a Gosub to change the HPWM duty cycle.

    Many thanks to all who helped
    Last edited by HankMcSpank; - 24th April 2011 at 21:48.

  14. #14
    Join Date
    May 2007
    Posts
    604


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    No all you have to do is get it to ramp 0>255>0 and you will be one step closer to your original intent - specifically wanting leds to fade niceley without any flickering.
    Why pay for overpriced toys when you can have
    professional grade tools for FREE!!!

  15. #15
    Join Date
    Mar 2009
    Posts
    653


    Did you find this post helpful? Yes | No

    Default Re: A slicker way to be constantly changing the duty cycle?

    Quote Originally Posted by rmteo View Post
    No all you have to do is get it to ramp 0>255>0 and you will be one step closer to your original intent - specifically wanting leds to fade niceley without any flickering.
    Oh, I've done that bit (& that wasn't the intent of my little bit of code above....which was simply to help others that may want to explore setting the HPWM registers manually)...

    Code:
     
    Main:
     
    decrement:
    if duty > 20 then 
    duty = duty -1
    gosub Change_PWM
    pause 3
    goto  decrement
    endif
    if duty >8 then 
    duty = duty -1
    gosub Change_PWM
    pause 15
    goto decrement
    endif
    if duty >1 then 
    duty = duty -1
    gosub Change_PWM
    pause 25
    goto decrement
    endif
     
    pause 100
     
    increment:
    if duty <10 then 
    duty = duty +1
    gosub Change_PWM
    pause 25
    goto increment
    endif
    if duty <20 then 
    duty = duty +1
    gosub Change_PWM
    pause 15
    goto increment
    endif
    if duty <255 then 
    duty = duty +1
    gosub Change_PWM
    pause 3
    goto increment
    endif
     
    goto Main
    now I'm sure that's ugly to those who program regularly ....I'm a self confessed 'kludger', but it works (what I found was that simply increasing the duty linearly didn't result in a linear fade .....in particular the lowest 20 of the 8 bit resolution has the most dramatic change in brightness, hence the longer pauses as the duty value approaches zero)

    There's no flickering of the LEDS ...but then again I'm not sure there was using HPWM command...this was a mini project to mainly wrap my head around the HPWM registers as I had an inkling that using the HPWM command was doing far more 'under the covers' each time I called the command than I really wanted)
    Last edited by HankMcSpank; - 24th April 2011 at 22:26.

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts