Modifying Darrel's SSPWM resolution?
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/wfse...d.php?fileid=3
Thanks Peter
Re: Modifying Darrel's SSPWM resolution?
Still looking for a bit of advice/guidance re this if possible please.
Re: Modifying Darrel's SSPWM resolution?
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.
Re: Modifying Darrel's SSPWM resolution?
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%
Re: Modifying Darrel's SSPWM resolution?
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
Quote:
W1 = Ticks * DutyCycle ' Calc # of Ticks for ON and OFF periods
TMR1_ON_TICKS = div32 100
TMR1_OFF_TICKS = Ticks - TMR1_ON_TICKS
Re: Modifying Darrel's SSPWM resolution?
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
Re: Modifying Darrel's SSPWM resolution?
Sounds like you have it under control Peter.
The numbers look good to me.
Re: Modifying Darrel's SSPWM resolution?
Is it possible to keep duty cycle a constant and vary the frequency?
Re: Modifying Darrel's SSPWM resolution?
That wouldn't be PWM.
It would be a variable frequency generator.
Much easier to do, but you don't need SSPWM.
Re: Modifying Darrel's SSPWM resolution?
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 :)
Quote:
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 :)
Re: Modifying Darrel's SSPWM resolution?
A couple more questions about this Darrel as i'm trying to understand the code.
In your variable definitions we have
Code:
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
Code:
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 ;)
Re: Modifying Darrel's SSPWM resolution?
This post should cover both of those questions.
http://www.picbasic.co.uk/forum/show...9101#post89101
And yes, 2 means 2K.
It is set in the .bas or .pbpinc files.
Re: Modifying Darrel's SSPWM resolution?
Thanks for that interesting link.
Finally to confirm. using a 16F88 which has 4k of code space I can dispense with.
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
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?
Code:
#config
__config _CONFIG1, _INTRC_IO & _WDT_OFF & _LVP_OFF & _MCLR_ON & _CCP1_RB3
__config _CONFIG2, _IESO_OFF & _FCMEN_OFF
#endconfig
it compiles ok.
Re: Modifying Darrel's SSPWM resolution?
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.