Please critique my PWM code!

Status
Not open for further replies.

bsodmike

New Member
Here is the code:

Code:
	list p = 16F84A

	org	00h		; the reset vector is defined as address 00hex
   	goto	setup		; and we tell it to go to 'setup'

;.......Variables..........................................................

	cblock	0Ch

 	half_t
	sum
	sample1
	overflow
	cnt

	endc

;.......Equates............................................................

w	EQU	00h
_tmr0	EQU	01h
_status	EQU	03h
_option	EQU	01h
_portb	EQU	06h


;=======Subroutines========================================================
;~~~~~~~Delay Loop~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

delay	clrf	_tmr0		; Start tmr0

dloop	movf	_tmr0,w		; Read tmr0 into w, w holds time

	subwf	half_t,w	; half_t - tmr0

	btfss	_status,2	; Check half_t - tmr0 = 0
	goto	dloop		; result is not 0

	retlw	0		; they are equal, return

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

freq	bsf	_portb,0		; Output high...
	call	delay		; for duration of 'half_t'

	bcf	_portb,0		; Output low...
	call	delay		; for duration of 'half_t'

	retlw	0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;**************************************************************************

setup	bsf	_status,5	;move into bank 1

	movlw	b'00000000'	;porta as output
	movwf 	_portb		;

	movlw	b'00000101'	;prescaler is /64
	movwf	_option		;

	bcf	_status,5	;back to bank 0

	clrf 	_portb		;clears porta

;..........................................................................
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

begin	bsf	_portb,1

	movlw	.37
	movwf	half_t
	call	freq

	goto	begin

	end

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;**************************************************************************

And the output:

**broken link removed**

As you can see the Duty cycle increases as the frequency goes up. This is due to a 'small' error in the 'delay' loop.

This is how I am calculating the PWM:


NOTE!


Code:
   movlw   .37 
   movwf   half_t 
   call   freq

The .37 is the PWM loading value, it defines the on/off time. 37*2 = 74 ; 15626/74 = 211hz

As I am clearing tmr0 this takes another extra cycle per clearing? If so, this means on the 'on' it is 38*2 = 76 and on the off another 76 - this still does not explain the duty cycle skew....

Update - 15:43 +5GMT: I changed the TMR0 scaler to 1/32. This gives 31250/105 = 298 counts, but as I am only counting half - 149!
 
As I mentioned before, this comparing tmr0 and W is a strange way to do the delay - for a start it gives you two unnecessary instructions (movf and subwf) during the timing loop. By loading tmr0 with 255 minus the value you require (and also taking account of the 2 instruction cycles lost while writing to tmr0), you only need to check the tmr0 overflow flag.

Doing it this way would reduce your delay 'jitter' time by at least 2uS, and make your code shorter as well.
 
Ahh now I understand.

Code:
**removed

The freq calling loop:

Code:
freq	bsf	_portb,0		; Output high...
	movfw	half_t
	movwf	cnt
	call	delay		; for duration of 'half_t'

	bsf	_portb,0		; Output high...
	movfw	half_t
	movwf	cnt
	call	delay		; for duration of 'half_t'

	retlw	0

Had to waste 2 cycles before the loop as I want to reuse 'half_t' once the first delay finishes....

What do you think?
 
This is the sort of routine I'm talking about, simply load W with the value you want, and 'call Delay' - bear in mind the timer will count upwards from this value till it overflows, so 245 would give a delay of 11 - or 200 would give a delay of 56 - the smaller the value, the longer the delay.

Code:
Delay	MOVWF	TMR0		;load TMR0 from W
	BTFSS	INTCON, T0IF	;wait for overflow
	GOTO	$-1
	RETURN
 
Ahh okay!

Code:
delay	movwf	_tmr0		; load this to the timer

d1	btfss	_intcon,2	; if tmr0 overflow is not set
	goto	d1		; loop, else skip

	bcf	_intcon,2

	retlw	0		; cnt = 0, so return

cleared the overflow flag on leaving the delay (so that for subsequent delays it will not affect them)

Thanks Nigel!

Update 23:33 +5GMT: Tested the following 16F84A code:

Code:
	list p = 16F84A

	org	00h		; the reset vector is defined as address 00hex
   	goto	setup		; and we tell it to go to 'setup'

;.......Variables..........................................................

	cblock	0Ch

	pulsewidth
	sum
	sample1
	overflow
	cnt

	endc

;.......Equates............................................................

w	EQU	00h
_tmr0	EQU	01h
_status	EQU	03h
_option	EQU	01h
_intcon	EQU	0Bh
_portb	EQU	06h


;=======Subroutines========================================================
;~~~~~~~Delay Loop~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

delay	movwf	_tmr0		; load this to the timer

d1	btfss	_intcon,2	; if tmr0 overflow is not set
	goto	d1		; loop, else skip

	bcf	_intcon,2

	return			; cnt = 0, so return

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

freq	bsf	_portb,0	; Output high...
	call	delay		; for duration of 'half_t'

	bcf	_portb,0	; Output low...
	call	delay		; for duration of 'half_t'

	retlw	0

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
;**************************************************************************

setup	bsf	_status,5	;move into bank 1

	movlw	b'00000000'	;porta as output
	movwf 	_portb		;

	movlw	b'00000100'	;prescaler is /32
	movwf	_option		;

	bcf	_status,5	;back to bank 0

	clrf 	_portb		;clears porta

;..........................................................................
;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

begin	movlw	.182
	call	freq

	goto	begin

	end

Now it is varying from 50.15% to 50.30%...... :? :?

All this means is that the on delay is lasting a tad longer thant he off...I'm at a loss in spotting this..hrm!
 
Maybe I should do this:

Code:
delay   btfss   _intcon,2   ; if tmr0 overflow is not set 
        goto   delay        ; loop, else skip 

        bcf   _intcon,2 

        return              ; cnt = 0, so return 

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

freq   movwf   _tmr0        ; load this to the timer 
       bsf   _portb,0       ; Output high... 
       call   delay         ; for duration of 'half_t' 

       movwf   _tmr0        ; load this to the timer 
       bcf   _portb,0       ; Output low... 
       call   delay         ; for duration of 'half_t' 

       retlw   0
 
Doing it this way does this:

105hz - 50.14%
210hz - 50.28%

Any tips?

Code:
delay	btfss	_intcon,2	; if tmr0 overflow is not set
	goto	delay		; loop, else skip

	bcf	_intcon,2

	return			; return

;~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

freq	movwf	_tmr0		; load this to the timer
	bsf	_portb,0	; Output high...
	call	delay		; for duration of '256-w'

	bcf	_portb,0	; Output low...
	movwf	_tmr0		; load this to the timer
	call	delay		; for duration of '256-w'

	retlw	0
 
Perhaps you don't realise, but you're worrying about less than 1% change, as reported by your meter - who's accuracy probably won't be as high as that.

I shouldn't worry about it!.
 
Gotcha... :lol: My requirement is between 49-51% dutycycle but I'm just trying to see how close to 50% dead on I can get it
 
bsodmike said:
Gotcha... :lol: My requirement is between 49-51% dutycycle but I'm just trying to see how close to 50% dead on I can get it


But your meter is only so accurate, you're already closer than the meter can distinguish. Polling TMR0 is always going to give a slight variation, the few microseconds where you test the flag is always going to vary, depending on the exact instant the timer overflows. You could always use interrupts to try and prevent this, or (even easier) use simple software loops - where it takes exactly the same time every time.
 
..

Wy is it normal to overflow???

i mean doesn't that take some more cpu time and fault corrections..??

TKS
 
Re: ..

TKS said:
Wy is it normal to overflow???

i mean doesn't that take some more cpu time and fault corrections..??

It's not a fault, it's simply the timer looping round, it's an eight bit timer, so when it gets to 255 (0xFF) on the next clock it overflows and loops back to zero. This overflow sets a flag that you can check, and also initiates an interrupt - if it's set to do so.
 
Status
Not open for further replies.
Cookies are required to use this site. You must accept them to continue using the site. Learn more…