# PIC16F1783 PSMC PWM jitter problem

I am using the PSMC module in a PIC16F1782 to create a single-output PWM signal at 100 Hz. I am having problems with the duty cycle glitching on a periodic basis.

The code main loop runs every 1.024 ms and the duty-cycle registers are updated on every pass of the loop. The datasheet is quite specific in saying:

The PSMCxLD bit of the PSMCxCON register is provided to synchronize changes to the event Count registers. Changes are withheld from taking action until the first period event Reset after the PSMCxLD bit is set. For example, to change the PWM frequency, while maintaining the same effective duty cycle, the Period and Duty Cycle registers need to be changed. The changes to all four registers take effect simultaneously on the period event Reset after the PSMCxLD bit is set.

In my case, the duty-cycle register may or may not be changed on each pass through the loop but the LD bit is set on every pass through the loop regardless if any changes were needed or not.

I suspect this is where my problems occur but I need to understand why this might be happening. In particular, the datasheet appears to say that I can change any of the registers associated with the duty-cycle or period or phase at any time without affecting the current operating parameters. Only after setting the LD bit do any changes made to those registers take effect.

Can anyone confirm this is indeed the case? If this is NOT the case, can someone provide guidance on how and when to update the registers and/or the LD bit?

In particular, there is a several microsecond time period where the duty-cycle registers are being manipulated. That is: initial values are loaded into the duty-cycle HI and LO registers, then those values are manipulated. This might be a total time of 30us or so. Only after the register values are stable is the LD bit asserted.

Obviously, the PWM output is active during this time.

The glitches are regular (not periodic) but random. That might imply beating between the main loop period and PWM period - but then I would not expect the glitches to be random.

Any guidance appreciated.

Other information: no interrupts.

 One section of the relevant code is as follows:

SetPWM_Auto
incf        SPEED,W         ;add offset (can't have zero duty-cycle
skpnz
movlw     0xFF            ;don't allow speed to wrap to 0x00
SETBSR      PSMC1DCH
movwf       RBS(PSMC1DCL)   ;save speed in lo byte
MULX132     RBS(PSMC1DCH), RBS(PSMC1DCL)    ;w *132 -> dcH:dcL

bsf         BBS(_PSMC1LD)   ;load steering and time buffers
rampg0
goto        SetPWMdone


The multiply macro:

MULX132         MACRO   ACC1, ACC0      ;W * 132 -> ACC1:ACC0
; enters with multiplicand in W
; exits with result in ACC1:ACC0
; Written by Mike Rigby-Jones
clrf        ACC1            ; Clear MSB
movwf       ACC0            ; save W in ACC0
;    clrc                        ; use these instead of "lslf" for
;    rlf         ACC0,F          ;   non-enhanced PICs
lslf        ACC0,F          ;C <- b7..b0 <- 0
rlf         ACC1,F
rlf         ACC0,F
rlf         ACC1,F
rlf         ACC0,F
rlf         ACC1,F          ; ACC1:ACC0 = 8 * input
addwf       ACC1,F          ; C + ACC1:ACC0 = 264 * input
rrf         ACC1,F          ; divide by 2
rrf         ACC0,F          ; ACC1:ACC0 = 132 * input  11 cy
endm


My guess is that the registers within the PSMC are NOT properly double-buffered and that the duty-cycle is changing because the value in the DC register has intermediate values while the math is being done. About to try fixing that.

• "the duty-cycle registers are updated on every pass" =- What happens if you don't update the duty cycle registers on every pass? Have you tried synchronizing the update to the PWM? Can you show us the relevant code? – Bruce Abbott Mar 3 at 5:59

It appears that my guess is correct - the PSMC module can't handle having intermediate values stored in the duty-cycle registers. I modified my code by doing the math in temporary registers, then copying the results to the duty-cycle registers. The glitching is gone.

SetPWM_Auto
incf        SPEED,W         ;add offset (can't have zero duty-cycle
skpnz
movlw     0xFF            ;don't allow speed to wrap to 0x00
movwf       TMP0            ;*** use temp registers for math
MULX132     TMP1, TMP0
SETBSR      PSMC1DCH
movfw       TMP1
movwf       RBS(PSMC1DCH)
movfw       TMP0
movwf       RBS(PSMC1DCL)

;    movwf       RBS(PSMC1DCL)   ;save speed in lo byte
;    MULX132     RBS(PSMC1DCH), RBS(PSMC1DCL)    ;w *132 -> dcH:dcL

bsf         BBS(_PSMC1LD)   ;load steering and time buffers
rampg0
goto        SetPWMdone


Note that the temporary registers are in shared RAM.

I'm leaving the question and answer up for reference in case someone else has a similar problem in the future.