PDA

View Full Version : SPWM oddity - first period "slice" is 2x what it should be



picster
- 21st February 2016, 17:38
I have been trying out DT's SPWM module and when I scope the output, I'm consistently seeing that the first time "slice" when stepped through incrementally, is 2x what all the subsequent ones are.

With SPWM Freq set to 50 and RES at 11 (to provide settings of 0-10), I'm seeing the following on a scope:

setting / ON period (approx mS)
0 / 0
1 / 3.7
2 / 5.6
3 / 7.5
4 / 9.3

This is yielding a pretty consistent (as it should) 1.85mS per "slice", except for "setting=1", which is double the value.

The program is doing NOTHING else, just testing this function.

Has anyone else seen this or is there something I've missed here?



DEFINE OSC 8
OSCCON=$70 '8MHz clock rate, internal RC clock
ANSEL=$10
ADCON0=1 'A/D control (set to 1 if using A/D)
ADCON1=0 '(result formatting: left justified 8-bit result)
'---------------------------------------------------------------
TRISA=%00110000 'assign DDRA (1=in, 0=out)
TRISB=%00010110 'assign DDRB (1=in, 0=out)
CMCON=0 'comparators off
CLEAR

INCLUDE "DT_INTS-14.bas" ; Base Interrupt System
INCLUDE "SPWM_INT.bas" ; Software PWM module
wsave VAR BYTE $20 SYSTEM ' location for W if in bank0
wsave1 VAR BYTE $A0 SYSTEM ' location for W if in bank1
wsave2 VAR BYTE $120 SYSTEM ' location for W if in bank2
wsave3 VAR BYTE $1A0 SYSTEM ' location for W if in bank3

DEFINE SPWM_FREQ 50 ; SPWM Frequency
DEFINE SPWM_RES 11 ; SPWM Resolution

DutyVars VAR BYTE[4] ; DutyCycle Variables
DutyVar1 VAR DutyVars[0] ; group them in an array for easy access
DutyVar2 VAR DutyVars[1] ; with FOR loops etc.
DutyVar3 VAR DutyVars[2]
DutyVar4 VAR DutyVars[3]
ASM
SPWM_LIST macro ; Define PIN's to use for SPWM
SPWM_PIN PORTB, 3, _DutyVar1
SPWM_PIN PORTB, 5, _DutyVar2
SPWM_PIN PORTB, 6, _DutyVar3
SPWM_PIN PORTB, 7, _DutyVar4
endm
SPWM_INIT SPWM_LIST ; Initialize the Pins
ENDASM

ASM
INT_LIST macro ; IntSource, Label, Type, ResetFlag?
INT_Handler TMR1_INT, SPWMhandler, ASM, yes
endm
INT_CREATE ; Creates the interrupt processor
ENDASM

@ INT_ENABLE TMR1_INT ; enable Timer 1 interrupts

;_________________________________________________ ____________________________

RandVar VAR WORD : RandVar = 12345
LoopCount VAR WORD

Main:
for loopcount=0 to 10
dutyvars[1]=Loopcount
pause 2000
next loopcount

GOTO Main

picster
- 21st February 2016, 19:40
Btw, this is on the 16F88.

picster
- 21st February 2016, 23:30
Ok, I believe I've located the problem. In SPWM_INT.BAS (the INCLUDE file), it relies on the carry bit to be set when the channel setting is greater than the number of periods passed (the result of "count" MINUS "setting"). As long as the result is negative (rollover) it sets the carry flag. Hence if they're EQUAL, the carry bit remains unset, so the first setting is always neglected by my reckoning (at least that's what I'm seeing on the scope).

I added one line of code (in red) below in the SPWM_INT.BAS file. Not sure what this will do if you're running with 255 levels because it'll roll over - so be cautious.

NotIdle
MOVE?BA _DutyCount ; Copy DutyCount to W reg
ADDLW 1 ; add 1 to dutycount because you're using carry as the flag
CHK?RP DutyVar ; Select proper bank
subwf DutyVar, W ; Subtract DutyVar from DutyCount
MOVE?TT STATUS,C, Port,Pin ; Copy carry bit to Port/Pin