Electronic – PIC16F1783 PSMC PWM jitter problem

picpwm

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.

[Edit]
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.

waveform example #2
waveform example #1

Best Answer

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.