PDA

View Full Version : A slicker way to be constantly changing the duty cycle?



HankMcSpank
- 22nd April 2011, 19:20
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.

rmteo
- 23rd April 2011, 00:37
The most effective way would be to directly alter the values in the CCPRxL (and CCPxCON, bits 1:0) registers.

HankMcSpank
- 23rd April 2011, 10:52
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)

HenrikOlsson
- 23rd April 2011, 11:13
Hi Hank,
Usually it's done like this:

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.

HankMcSpank
- 23rd April 2011, 11:19
Hi Hank,
Usually it's done like this:

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.

HenrikOlsson
- 23rd April 2011, 13:03
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?

HankMcSpank
- 23rd April 2011, 13:52
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...



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.

rmteo
- 23rd April 2011, 15:30
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.


http://www.youtube.com/watch?v=YtH0BTmyaFM

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.

HankMcSpank
- 24th April 2011, 01:03
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)



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")...



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...



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?)

cncmachineguy
- 24th April 2011, 03:04
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:



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 :)

HenrikOlsson
- 24th April 2011, 07:52
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:

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

HankMcSpank
- 24th April 2011, 10:35
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...




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.

HankMcSpank
- 24th April 2011, 21:36
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....



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 :)

rmteo
- 24th April 2011, 21:48
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.

HankMcSpank
- 24th April 2011, 22:03
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)...




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)