PDA

View Full Version : Modifying Darrel's SSPWM resolution?



retepsnikrep
- 18th May 2011, 01:16
This is probably a question for Darrel.

Anyway i'm using his excellent SSPWM to generate a 2khz pwm output on a 16F88 pic running at 8mhz.

The 1% Duty resolution available as standard works fine.
But can that be increased to 0.1% resolution? Or perhaps 0.5% resolution.

What would I need to change? I see DutyCycle is a byte variable so that needs to be a word if DutyCycle was going to be expanded to 1000 units. It looks like simply a matter of the maths to calculate the TMR1 duty periods but that's not my strong point :(

http://www.pbpgroup.com/modules/wfsection/download.php?fileid=3

Thanks Peter

retepsnikrep
- 10th June 2011, 09:03
Still looking for a bit of advice/guidance re this if possible please.

Darrel Taylor
- 10th June 2011, 16:23
The CalcSPWM: routine calculates the prescaler and ON/OFF times for the PWM.
Then the Timer interrupts do the rest.

You can calculate those times any way you want.
SSPWM calculates for different OSC frequencies, PWM frequencies and dutycycles.
But if you know the OSC and PWM frequency ahead of time, it will be easier for you to do the math.

retepsnikrep
- 10th June 2011, 17:35
In my application the osc is always 8mhz and the frequency is always 2khz, the duty changes from 10-90%.
Your routine works very well I just want to try and increase the resolution to 0.1%

retepsnikrep
- 14th June 2011, 01:34
I'm going to try using a few different values for the duty and get my software to display your calculated values for T1CON, TICKS, TMR1_ON_TICKS, TMR1_OFF_TICKS. Then I will use TICKS & T1CON as constants and remove all the calculation stuff does that sound reasonable? My maths & pic knowledge is not good enough to see how all your calculation routines work :(

I'll just leave the final duty calculation part in



W1 = Ticks * DutyCycle ' Calc # of Ticks for ON and OFF periods
TMR1_ON_TICKS = div32 100
TMR1_OFF_TICKS = Ticks - TMR1_ON_TICKS

retepsnikrep
- 15th June 2011, 16:03
I tinkered around and found that with my 8mhz int clock and 2khz pwm the prescaler was being set to 1 and ticks was 1000 per cycle. 50% duty Time on/off was 500 ticks each. This suggests that resolution can be increased to 0.1% by changing DutyCycle to a word variable and using 500 instead of 50 to represent 50% duty. I have adjsted the calculation so it now ranges from 0-1000 in steps of 1 instead of steps of 10. I also removed all the calculation routines as ticks are now known as is prescaler etc. That save quite a few bytes as well :)

Darrel any comments?

Peter

Darrel Taylor
- 15th June 2011, 17:02
Sounds like you have it under control Peter.

The numbers look good to me.

Tobias
- 24th June 2011, 13:35
Is it possible to keep duty cycle a constant and vary the frequency?

Darrel Taylor
- 24th June 2011, 21:00
That wouldn't be PWM.
It would be a variable frequency generator.

Much easier to do, but you don't need SSPWM.

retepsnikrep
- 25th June 2011, 06:18
Just as a final follow up I hacked SSPWM down to a fraction of it's original size and it works great.

I use the 8mhz internal osc and for my reqd 2000hz freq that works out at a very simple 1000 ticks per cycle :)




DEFINE INTHAND INT_CODE ' Tell PBP Where the code starts on an interrupt

wsave VAR BYTE $20 SYSTEM '$20 Save location for the W register if in bank0
wsave1 VAR BYTE $A0 SYSTEM ' Save location for the W register if in bank1
wsave2 VAR BYTE $120 SYSTEM ' Save location for the W register if in bank2
wsave3 VAR BYTE $1A0 SYSTEM ' Save location for the W register if in bank3
ssave VAR BYTE Bank0 SYSTEM ' Save location for the STATUS register
psave VAR BYTE Bank0 SYSTEM ' Save location for the PCLATH register

TMR1_ON_TICKS var word Bank0 ' # of Tmr ticks for On Time
TMR1_OFF_TICKS var word Bank0 ' # of Tmr ticks for Off Time
TMR1_ON_VAL var word Bank0 ' # to load TMR1 for On Time
TMR1_OFF_VAL var word Bank0 ' # to load TMR1 for Off Time

DataFlags var byte Bank0
SPWMstate var DataFlags.2 ' Current state of SPWM output high or low

GIE var INTCON.7
PEIE var INTCON.6
TMR1IE var PIE1.0
TMR1ON var T1CON.0

clear 'Clear All Variables

goto Start

' ------------------------------------------------------------------------
asm
INT_CODE
if (CODE_SIZE <= 2)
movwf wsave ; copy W to wsave register
swapf STATUS,W ; swap status reg to be saved into W
clrf STATUS ; change to bank 0 regardless of current bank
movwf ssave ; save status reg to a bank 0 register
movf PCLATH,w ; move PCLATH reg to be saved into W reg
movwf psave ;6 ; save PCLATH reg to a bank 0 register
endif

btfss PIR1, TMR1IF ; is TMR1IF set? Timer1 Interrupt Flag
GOTO NoTimerInt ; No. Bypass timer load
btfss _SPWMstate ; Is Output High?
GOTO TurnON ;9/15 ; No.

TurnOFF
bcf _CmdPwr ; Set CmdPwr Low
bcf _SPWMstate ;
BCF T1CON,TMR1ON ; Turn off timer
MOVF _TMR1_OFF_VAL,W ; 1
ADDWF TMR1L,F ; 1 ; reload timer with correct value
BTFSC STATUS,C ; 1/2
INCF TMR1H,F ; 1
MOVF _TMR1_OFF_VAL+1,W ; 1
ADDWF TMR1H,F ; 1
BSF T1CON,TMR1ON ; 1 ; Turn it back on
GOTO TimerDone ;12/27

TurnON
bsf _CmdPwr ; Set CmdPwr High
bsf _SPWMstate ;
bcf T1CON,TMR1ON ; Turn off timer
MOVF _TMR1_ON_VAL,W ; 1
ADDWF TMR1L,F ; 1 ; reload timer with correct value
BTFSC STATUS,C ; 1/2
INCF TMR1H,F ; 1
MOVF _TMR1_ON_VAL+1,W ; 1
ADDWF TMR1H,F ; 1
bsf T1CON,TMR1ON ; 1 ; Turn it back on

TimerDone
bcf PIR1, TMR1IF ; 1/28 ; Clear Timer1 Interrupt Flag

NoTimerInt
Movf psave,w ; Restore the PCLATH reg
Movwf PCLATH
swapf ssave,w ; Restore the STATUS reg
movwf STATUS
swapf wsave,f
swapf wsave,w ; 6/34 ; Restore W reg

Retfie ; Exit the interrupt routine

endasm
' ------------------------------------------------------------------------

StartSPWM: 'Set Freq and DutyCycle before calling
'For 2khz pwm and 8mhz clock Ticks = 1000 per cycle
GIE = 1
PEIE = 1
TMR1H = 255 'Load TMR1 with 65535, First tick will cause
TMR1L = 255 'an interrupt that will load TMR1_???_VAL
TMR1_ON_TICKS = DutyCycle '(Must be between 100 & 900 (10-90%)
TMR1_OFF_TICKS = 1000 - TMR1_ON_TICKS
TMR1_ON_VAL = 65535 - TMR1_ON_TICKS + 8
TMR1_OFF_VAL = 65535 - TMR1_OFF_TICKS + 8
TMR1IE = 1
T1CON = 1 'Set Timer1 prescaler to 1 and turn Timer1 on
return

'************************************************* ************************************
'*********************************** Main Program ************************************


I use a word variable for DutyCycle between 0-1000 which gives me a 0.1% resolution now a tenfold increase over the original.
I just load DutyCyle with my reqd duty and call StartSPWM:

Thanks to Darrel for the original code which I now understand as well making some small personal inroads into Pic Assembler :)

retepsnikrep
- 11th August 2011, 09:14
A couple more questions about this Darrel as i'm trying to understand the code.

In your variable definitions we have



wsave1 VAR BYTE $A0 SYSTEM ' Save location for the W register if in bank1
wsave2 VAR BYTE $120 SYSTEM ' Save location for the W register if in bank2
wsave3 VAR BYTE $1A0 SYSTEM ' Save location for the W register if in bank3


These are not anywhere else in the code? so i don't understand how they are used?
I tried removing them and the program compiled correctly but started giving an eroneous display?

In your Int code you have



asm
INT_CODE
if (CODE_SIZE <= 2)
movwf wsave ; copy W to wsave register
swapf STATUS,W ; swap status reg to be saved into W
clrf STATUS ; change to bank 0 regardless of current bank
movwf ssave ; save status reg to a bank 0 register
movf PCLATH,w ; move PCLATH reg to be saved into W reg
movwf psave ;6 ; save PCLATH reg to a bank 0 register
endif


What does " if (CODE_SIZE <= 2)" do?
Clearly it jumps over the register saving stuff but where does CODE_SIZE come from?

2 does that mean 2k or 2 words, bytes?

Thanks Peter

PS I have bought PBP3 looking forward to trying it later ;)

Darrel Taylor
- 11th August 2011, 15:11
This post should cover both of those questions.
http://www.picbasic.co.uk/forum/showthread.php?t=13185&p=89101#post89101

And yes, 2 means 2K.
It is set in the .bas or .pbpinc files.

retepsnikrep
- 18th August 2011, 06:40
Thanks for that interesting link.

Finally to confirm. using a 16F88 which has 4k of code space I can dispense with.


if (CODE_SIZE <= 2)
movwf wsave ; copy W to wsave register
swapf STATUS,W ; swap status reg to be saved into W
clrf STATUS ; change to bank 0 regardless of current bank
movwf ssave ; save status reg to a bank 0 register
movf PCLATH,w ; move PCLATH reg to be saved into W reg
movwf psave ;6 ; save PCLATH reg to a bank 0 register
endif


But with a 12F683 2k space i need it.

I just bough & installed PBP3 looks good and after tweaking configs looks ok. Does this config look alright for a 16F88?



#config
__config _CONFIG1, _INTRC_IO & _WDT_OFF & _LVP_OFF & _MCLR_ON & _CCP1_RB3
__config _CONFIG2, _IESO_OFF & _FCMEN_OFF
#endconfig
it compiles ok.

Darrel Taylor
- 18th August 2011, 07:02
Yes you could dispense with it if you really wanted.

But the assembler will do that for you if the chip has more than 2K of program space.
Then by leaving it in, it will still work on any other 14-bit core chip too.

The #config block looks great.

HenrikOlsson
- 18th August 2011, 07:17
Hi,
CONFIGs look alright to me.

As for the context save stub I don't think you need to remove or do anything. The IF(CODE_SIZE<=2k) tells the assembler to automatically insert that piece code when and only when you HAVE a chip with 2k or less, if the chip has more than 2k the statement evalutates false and the code won't get it inserted. Automagically....

Obviously you CAN remove it when using the F88 but there's really no need to as I see it.

/Henrik.

EDIT: OK that made my post pretty redundant, went for a cup of coffee and Darrel beat me to it.