News:

;) This forum is the property of Proton software developers

Main Menu

Positron8 - HPWM Library for PIC18F16F376 device

Started by top204, Aug 10, 2023, 05:30 PM

Previous topic - Next topic

top204

Below is a tested library to output 6 PWM waveforms from a PIC16F15376 device.

$ifndef _HPWM_16F15376_INC_
$define _HPWM_16F15376_INC_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Hardware PWM library for the PIC16F15376 device.
' Because the CCP amd PWM peripherals all share the same timer (Timer2), they must all share the same operating frequency.
'
' Written for the Positron8 BASIC Compiler by Les Johnson.
'
$if (_device <> _16F15376)
    $error "The HPWM_16F15376 library is only suitable for a 16F15376 device"
$endif

'-------------------------------------
' Procedure:
' HPWM_SetFreq(pFrequency)
'
' Sets the frequency used by all the HPWM channels, because they all share the same timer (Timer2)
'
' pFrequency alters the frequency (in Hz) of a CCP or PWM peripheral and can be any value from 0 to 1000000,
' however, lower or upper frequencies depend on the microcontroller's operating frequency.
'
'-------------------------------------
' Procedures:
' HPWM1_Duty(pDuty)
' HPWM2_Duty(pDuty)
' HPWM3_Duty(pDuty)
' HPWM4_Duty(pDuty)
' HPWM5_Duty(pDuty)
' HPWM6_Duty(pDuty)
'
' Alters the duty of the PWM waveform
'
' pDuty alters the duty cycle of a waveform and can be any value from 0 to 255.
'
'-------------------------------------
' Procedures:
' HPWM1_Start()
' HPWM2_Start()
' HPWM3_Start()
' HPWM4_Start()
' HPWM5_Start()
' HPWM6_Start()
'
' Enables a PWM peripheral
'
'-------------------------------------
' Procedures:
' HPWM1_Stop()
' HPWM2_Stop()
' HPWM3_Stop()
' HPWM4_Stop()
' HPWM5_Stop()
' HPWM6_Stop()
'
' Disables a PWM peripheral
'
'-------------------------------------
' Procedures:
' HPWM3_PolInvert()
' HPWM4_PolInvert()
' HPWM5_PolInvert()
' HPWM6_PolInvert()
'
' Invert the polarity of a PWM waveform
'
' HPWM1 and HPWM2 channels cannot be inverted because they use the CCP peripherals
'
'-------------------------------------
' Procedures:
' HPWM3_PolNormal()
' HPWM4_PolNormal()
' HPWM5_PolNormal()
' HPWM6_PolNormal()
'
' Normal polarity for a PWM waveform
'
' HPWM1 and HPWM2 channels cannot be inverted because they use the CCP peripherals
'
'-------------------------------------
' Preprocessor Meta-Macros
' HPWM1_PPS(pPin)
' HPWM2_PPS(pPin)
' HPWM3_PPS(pPin)
' HPWM4_PPS(pPin)
' HPWM5_PPS(pPin)
' HPWM6_PPS(pPin)
'
' Each meta-macro sets up the PPS for a particular PWM channel
'
' pPin is a constant value that represents the pin. i.e. Pin_A0, Pin_B1, Pin_C4, Pin_D4 etc...
'
' The CCP and PWM peripherals are only allowed to be on certain ports, so if an incorrect pin is used in the  meta-macro, an error will be given.
'
'-------------------------------------
    Declare Onboard_HPWM = 6                                ' Set the device to have 6 PWM channels (regardless what is in the device's .PPI file)
'-------------------------------------------------------------------------------------------------------
'
' Create some PPS defines to make the CCP types look like PWM types
'
$define PPS_Fn_PWM1 PPS_Fn_CCP1
$define PPS_Fn_PWM2 PPS_Fn_CCP2
$define PPS_Fn_PWM3 PPS_Fn_PWM3OUT
$define PPS_Fn_PWM4 PPS_Fn_PWM4OUT
$define PPS_Fn_PWM5 PPS_Fn_PWM5OUT
$define PPS_Fn_PWM6 PPS_Fn_PWM6OUT

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the CCP1 peripheral operating as PWM
' Input     : pPin holds the pin to use. i.e. Pin_B0, Pin_C0, Pin_C2 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM1_PPS(pPin)      '
    $if pPin = Pin_B0        '
        RB0PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B1    '
        RB1PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B2    '
        RB2PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B3    '
        RB3PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B4    '
        RB4PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B5    '
        RB5PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B6    '
        RB6PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_B7    '
        RB7PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C0    '
        RC0PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C1    '
        RC1PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C2    '
        RC2PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C3    '
        RC3PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C4    '
        RC4PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C5    '
        RC5PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C6    '
        RC6PPS = PPS_Fn_PWM1 '
    $elseif pPin = Pin_C7    '
        RC7PPS = PPS_Fn_PWM1 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the CCP1 peripheral operating as PWM. Only PORTB and PORTC are suitable" '
    $endif

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the CCP2 peripheral operating as PWM
' Input     : pPin holds the pin to use. i.e. Pin_B0, Pin_C0, Pin_C2 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM2_PPS(pPin)      '
    $if pPin = Pin_B0        '
        RB0PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B1    '
        RB1PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B2    '
        RB2PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B3    '
        RB3PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B4    '
        RB4PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B5    '
        RB5PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B6    '
        RB6PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_B7    '
        RB7PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C0    '
        RC0PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C1    '
        RC1PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C2    '
        RC2PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C3    '
        RC3PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C4    '
        RC4PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C5    '
        RC5PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C6    '
        RC6PPS = PPS_Fn_PWM2 '
    $elseif pPin = Pin_C7    '
        RC7PPS = PPS_Fn_PWM2 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the CCP2 peripheral operating as PWM. Only PORTB and PORTC are suitable" '
    $endif

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the PWM3 peripheral
' Input     : pPin holds the pin to use. i.e. Pin_B0, Pin_D0, Pin_D2 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM3_PPS(pPin)      '
    $if pPin = Pin_B0        '
        RB0PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B1    '
        RB1PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B2    '
        RB2PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B3    '
        RB3PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B4    '
        RB4PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B5    '
        RB5PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B6    '
        RB6PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_B7    '
        RB7PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D0    '
        RD0PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D1    '
        RD1PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D2    '
        RD2PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D3    '
        RD3PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D4    '
        RD4PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D5    '
        RD5PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D6    '
        RD6PPS = PPS_Fn_PWM3 '
    $elseif pPin = Pin_D7    '
        RD7PPS = PPS_Fn_PWM3 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the PWM3 peripheral. Only PORTB and PORTD are suitable" '
    $endif

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the PWM4 peripheral
' Input     : pPin holds the pin to use. i.e. Pin_B0, Pin_D0, Pin_D2 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM4_PPS(pPin)      '
    $if pPin = Pin_B0        '
        RB0PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B1    '
        RB1PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B2    '
        RB2PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B3    '
        RB3PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B4    '
        RB4PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B5    '
        RB5PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B6    '
        RB6PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_B7    '
        RB7PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D0    '
        RD0PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D1    '
        RD1PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D2    '
        RD2PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D3    '
        RD3PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D4    '
        RD4PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D5    '
        RD5PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D6    '
        RD6PPS = PPS_Fn_PWM4 '
    $elseif pPin = Pin_D7    '
        RD7PPS = PPS_Fn_PWM4 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the PWM4 peripheral. Only PORTB and PORTD are suitable" '
    $endif

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the PWM5 peripheral
' Input     : pPin holds the pin to use. i.e. Pin_A0, Pin_C0, Pin_C2 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM5_PPS(pPin)      '
    $if pPin = Pin_A0        '
        RA0PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A1    '
        RA1PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A2    '
        RA2PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A3    '
        RA3PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A4    '
        RA4PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A5    '
        RA5PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A6    '
        RA6PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_A7    '
        RA7PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C0    '
        RC0PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C1    '
        RC1PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C2    '
        RC2PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C3    '
        RC3PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C4    '
        RC4PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C5    '
        RC5PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C6    '
        RC6PPS = PPS_Fn_PWM5 '
    $elseif pPin = Pin_C7    '
        RC7PPS = PPS_Fn_PWM5 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the PWM5 peripheral. Only PORTA and PORTC are suitable" '
    $endif

'---------------------------------------------------------------------------
' Set the PPS for the pin suitable for the PWM6 peripheral
' Input     : pPin holds the pin to use. i.e. Pin_A0, Pin_D0 etc...
' Output    : Produces an error if an incorrect pin is assigned
' Notes     : Only certain Port.Pin setting are allowed for a particular peripheral using PPS
'
$define HPWM6_PPS(pPin)      '
    $if pPin = Pin_A0        '
        RA0PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A1    '
        RA1PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A2    '
        RA2PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A3    '
        RA3PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A4    '
        RA4PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A5    '
        RA5PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A6    '
        RA6PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_A7    '
        RA7PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D0    '
        RD0PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D1    '
        RD1PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D2    '
        RD2PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D3    '
        RD3PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D4    '
        RD4PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D5    '
        RD5PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D6    '
        RD6PPS = PPS_Fn_PWM6 '
    $elseif pPin = Pin_D7    '
        RD7PPS = PPS_Fn_PWM6 '
    $else                    '
        $error "The chosen Port.Pin is not suitable for the PWM5 peripheral. Only PORTA and PORTD are suitable" '
    $endif

$define HPWM_cFosc $eval (_xtal * 1000)
'
' Create variables used by the library routines
'
    Dim HPWM_dFrequency      As Dword                                   ' Used to pass the 32-bit frequency value as a parameter
    Dim HPWM_bDuty           As Byte                                    ' Used to pass the 8-bit duty cycle as a paramater
    Dim HPWM_bTimerReq       As Byte                                    ' Used to pass the timer required as a parameter
    Dim HPWM_bPrescaleSelect As Byte                                    ' Holds the timer's prescaler chosen for a particular frequency
    Dim HPWM_dCalcDuty       As Dword                                   ' Used to calcualte the duty cycle value
    Dim HPWM_wCalcDuty       As HPWM_dCalcDuty.Word0                    ' Used to pass the duty cycle value to other procedures

'-----------------------------------------------------------------------------
' A helper routine to calculate a duty cycle required for a given PWM frequency
' Input     : HPWM_dFrequency holds the 32-bit frequency of the PWM
'           : HPWM_bPrescaleSelect holds the 8-bit prescale value
'           : HPWM_bDuty holds the 8-bit duty cycle (0 to 255)
' Output    : HPWM_wCalcDuty holds the 16-bit duty cycle value
' Notes     : None
'

Proc HPWM_hCalculateDuty()
    Dim dMaxDuty As Dword

    HPWM_dCalcDuty = HPWM_dFrequency * HPWM_bPrescaleSelect             ' \
    HPWM_wCalcDuty = HPWM_cFosc / HPWM_dCalcDuty                        ' / Determine the maximum duty
    dMaxDuty = HPWM_wCalcDuty * HPWM_bDuty                              ' \
    HPWM_wCalcDuty = dMaxDuty / 256                                     ' / Calculate the duty value
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for Timer2, frequency and duty cycle
' Input     : pFrequency holds the 32-bit frequency (in Hz)
'           : Global variable HPWM_bDuty holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
'           : Global variable HPWM_bPrescaleSelect holds the timer's prescaler value for the required frequency
' Notes     : None
'
Proc HPWM_SetFreq(pFrequency As HPWM_dFrequency), STATUSbits_C
    Dim wPRTemp    As Word                                              ' A temporary variable for calculations
    Dim bTConValue As Byte                                              ' Holds the value to place into Timer2's T2CON SFR
    Dim wPRValue   As Word                                              ' Holds the value that is placed into Timer2's T2PR SFR

    T2HLT    = %00000001                                                ' Setup Timer2 for Not Synchronised, Software control, Rising Edge
    T2CLKCON = %00000001                                                ' Setup Timer2 for FOSC/4
    T2RST    = %00000000
    PIR4bits_TMR2IF = 0

    pFrequency = pFrequency / 1000
    wPRTemp = HPWM_cFosc / pFrequency
    wPRTemp = wPRTemp / 4
'
' Loop through all the valid prescalers for Timer2
'
    HPWM_bPrescaleSelect = 1
    Repeat
        wPRValue = wPRTemp / HPWM_bPrescaleSelect                       ' \
        wPRValue = wPRValue - 1                                         ' / Calculate Timer2's T2PR value
        If wPRValue <= 255 Then                                         ' \ Is the value to place into Timer2's T2PR valid?
            If wPRValue > 1 Then                                        ' /
                bTConValue = %10000000                                  ' Yes. So Default to T2CON loaded with 0 (1:1 Prescaler) and enable Timer2
                If HPWM_bPrescaleSelect = 2 Then                        ' Is HPWM_bPrescaleSelect 2?
                    bTConValue = %10010000                              ' Yes. So load bTConValue for a 1:2 Prescaler and enable Timer2
                ElseIf HPWM_bPrescaleSelect = 4 Then                    ' Is HPWM_bPrescaleSelect 4?
                    bTConValue = %10100000                              ' Yes. So load bTConValue for a 1:4 Prescaler and enable Timer2
                Else If HPWM_bPrescaleSelect = 8 Then                   ' Is HPWM_bPrescaleSelect 8?
                    bTConValue = %10110000                              ' Yes. So load bTConValue for a 1:8 Prescaler and enable Timer2
                Else If HPWM_bPrescaleSelect = 16 Then                  ' Is HPWM_bPrescaleSelect 16?
                    bTConValue = %11000000                              ' Yes. So load bTConValue for a 1:16 Prescaler and enable Timer2
                Else If HPWM_bPrescaleSelect = 32 Then                  ' Is HPWM_bPrescaleSelect 32?
                    bTConValue = %11010000                              ' Yes. So load bTConValue for a 1:32 Prescaler and enable Timer2
                Else If HPWM_bPrescaleSelect = 64 Then                  ' Is HPWM_bPrescaleSelect 64?
                    bTConValue = %11100000                              ' Yes. So load bTConValue for a 1:64 Prescaler and enable Timer2
                Else If HPWM_bPrescaleSelect = 128 Then                 ' Is HPWM_bPrescaleSelect 128?
                    bTConValue = %11110000                              ' Yes. So adjust T2CON for a 1:128 Prescaler and enable Timer2
                EndIf
                HPWM_hCalculateDuty()                                   ' Calculate the duty value based upon global variable HPWM_bDuty
                T2PR  = wPRValue.Byte0                                  ' Load Timer2's T2PR SFR
                T2CON = bTConValue                                      ' Load Timer2's T2CON SFR
                Result = 1                                              ' Return the Carry flag holding 1 if successful
                ExitProc                                                ' Exit the procedure
            EndIf
        EndIf
        HPWM_bPrescaleSelect = HPWM_bPrescaleSelect * 2                 ' Move the prescaler select value
    Until HPWM_bPrescaleSelect > 128
    Result = 0                                                          ' Return the Carry flag holding 0 if unsuccesful
EndProc

'*****************************************************************************
$ifdef _CCP1CON
'-----------------------------------------------------------------------------
' Start CCP1 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM1_Start()
    CCP1CON = %10001111
#ifSym __CCP1_PIN
    PinOutput __CCP1_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop CCP1 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM1_Stop()
#ifSym __CCP1_PIN
    PinInput __CCP1_PORT_PIN
#endIfSym
   CCP1CON = %00001111
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the CCPR1L and CCPR1H SFRs
' Input     : HPWM_dCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
$define HPWM1_hSetDuty()                                    '
    HPWM_dCalcDuty.Byte1 = HPWM_dCalcDuty.Byte1 & %00000011 '
    CCPR1H = HPWM_dCalcDuty.Byte1                           '
    CCPR1L = HPWM_dCalcDuty.Byte0

'-----------------------------------------------------------------------------
' Adjust the duty cycle of CCP1
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM1_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()
    HPWM1_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP1
' Input     : pFrequency (HPWM_dFrequency) holds the 32-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
' Notes     : Also starts the PWM for CCP1
'
Proc HPWM1(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                        ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                        ' Was the frequency accepted?
        HPWM1_hSetDuty()                                            ' Yes. So load the CCP1 registers with the appropriate value
        HPWM1_Start()                                               ' Start CCP1
        Result = 1                                                  ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' HPWM1 polarity
'
$define HPWM1_PolInvert() $error "Cannot invert the polarity of the CCP1 peripheral operating as PWM. Only suitable with the PWM3, PWM4, PWM5 and PWM6 peripherals"
$define HPWM1_PolNormal()

$endif ' _CCP1CON

'*****************************************************************************
$ifdef _CCP2CON
'-----------------------------------------------------------------------------
' Start CCP2 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM2_Start()
    CCP2CON = %10001111
#ifSym __CCP2_PIN
    PinOutput __CCP2_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop CCP2 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM2_Stop()
#ifSym __CCP2_PIN
    PinInput __CCP2_PORT_PIN
#endIfSym
   CCP2CON = %00001111
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the CCPR2L and CCPR2H SFRs
' Input     : HPWM_dCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
$define HPWM2_hSetDuty()                                    '
    HPWM_dCalcDuty.Byte1 = HPWM_dCalcDuty.Byte1 & %00000011 '
    CCPR2H = HPWM_dCalcDuty.Byte1                           '
    CCPR2L = HPWM_dCalcDuty.Byte0

'-----------------------------------------------------------------------------
' Adjust the duty cycle of CCP2
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM2_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()
    HPWM2_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP2
' Input     : pFrequency holds the 32-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
' Notes     : Also starts the PWM for CCP2
'
Proc HPWM2(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                    ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                    ' Was the frequency accepted?
        HPWM2_hSetDuty()                                        ' Yes. So load the CCP2 registers with the appropriate value
        HPWM2_Start()                                           ' Start CCP2
        Result = 1                                              ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' HPWM2 polarity
'
$define HPWM2_PolInvert() $error "Cannot invert the polarity of the CCP2 peripheral operating as PWM. Only suitable with the PWM3, PWM4, PWM5 and PWM6 peripherals"
$define HPWM2_PolNormal()

$endif ' _CCP2CON

'*****************************************************************************
$ifdef _PWM3CON
    Dim HPWM_tPolarity3 As Bit = 0                              ' Holds 1 if the PWM3 waveform is to be inverted

'-----------------------------------------------------------------------------
' Start PWM3 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM3_Start()
    If HPWM_tPolarity3 = 1 Then                                 ' Is the PWM to be inverted?
        PWM3CON = %10010000                                     ' Yes. So set the PWM3POL bit
    Else                                                        ' Otherwise... Not inverted...
        PWM3CON = %10000000                                     ' So... Clear the PWM3POL bit
    EndIf
#ifSym __HPWM3_PIN
    PinOutput __HPWM3_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop PWM3 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM3_Stop()
#ifSym __HPWM3_PIN
   PinInput __HPWM3_PORT_PIN
#endIfSym
   PWM3CON = %00000000
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the PWM3DCL and PWM3DCH SFRs
' Input     : HPWM_wCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
Proc HPWM3_hSetDuty()
    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    PWM3DCL = WREG
    PWM3DCH = HPWM_dCalcDuty.Byte0
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM3
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM3_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM3_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM3
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM5
'
Proc HPWM3(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                    ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                    ' Was the frequency accepted?
        HPWM3_hSetDuty()                                        ' Yes. So load the PWM3DC registers with the appropriate value
        HPWM3_Start()                                           ' Start PWM3
        Result = 1                                              ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' Invert the polarity for HPWM3
' Input     : None
' Output    : HPWM_tPolarity3 holds 1 for inverted
' Notes     : Also sets the POL bit of PWM3CON
'
Proc HPWM3_PolInvert()
    PWM3CONbits_POL = 1
    HPWM_tPolarity3 = 1
EndProc

'-----------------------------------------------------------------------------
' Do not Invert the polarity for HPWM3
' Input     : None
' Output    : HPWM_tPolarity3 holds 0 for not inverted
' Notes     : Also clears the POL bit of PWM3CON
'
Proc HPWM3_PolNormal()
    PWM3CONbits_POL = 0
    HPWM_tPolarity3 = 0
EndProc

$endif  ' _PWM3CON

'*****************************************************************************
$ifdef _PWM4CON
    Dim HPWM_tPolarity4 As Bit = 0                              ' Holds 1 if the PWM4 waveform is to be inverted

'-----------------------------------------------------------------------------
' Start PWM4 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM4_Start()
    If HPWM_tPolarity4 = 1 Then                                 ' Is the PWM to be inverted?
        PWM4CON = %10010000                                     ' Yes. So set the PWM4POL bit
    Else                                                        ' Otherwise... Not inverted...
        PWM4CON = %10000000                                     ' So... Clear the PWM4POL bit
    EndIf
#ifSym __HPWM4_PIN
    PinOutput __HPWM4_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop PWM4 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM4_Stop()
#ifSym __HPWM4_PIN
   PinInput __HPWM4_PORT_PIN
#endIfSym
   PWM4CON = %00000000
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the PWM4DCL and PWM4DCH SFRs
' Input     : HPWM_wCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
Proc HPWM4_hSetDuty()
    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    PWM4DCL = WREG
    PWM4DCH = HPWM_dCalcDuty.Byte0
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM4
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM4_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM4_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM5
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM4
'
Proc HPWM4(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                    ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                    ' Was the frequency accepted?
        HPWM4_hSetDuty()                                        ' Yes. So load the PWM4DC registers with the appropriate value
        HPWM4_Start()                                           ' Start PWM4
        Result = 1                                              ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' Invert the polarity for HPWM4
' Input     : None
' Output    : HPWM_tPolarity4 holds 1 for inverted
' Notes     : Also sets the POL bit of PWM4CON
'
Proc HPWM4_PolInvert()
    PWM4CONbits_POL = 1
    HPWM_tPolarity4 = 1
EndProc

'-----------------------------------------------------------------------------
' Do not Invert the polarity for HPWM4
' Input     : None
' Output    : HPWM_tPolarity4 holds 0 for not inverted
' Notes     : Also clears the POL bit of PWM4CON
'
Proc HPWM4_PolNormal()
    PWM4CONbits_POL = 0
    HPWM_tPolarity4 = 0
EndProc

$endif  ' _PWM4CON

'*****************************************************************************
$ifdef _PWM5CON
    Dim HPWM_tPolarity5 As Bit = 0                              ' Holds 1 if the PWM5 waveform is to be inverted

'-----------------------------------------------------------------------------
' Start PWM5 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM5_Start()
    If HPWM_tPolarity5 = 1 Then                                 ' Is the PWM to be inverted?
        PWM5CON = %10010000                                     ' Yes. So set the PWM5POL bit
    Else                                                        ' Otherwise... Not inverted...
        PWM5CON = %10000000                                     ' So... Clear the PWM5POL bit
    EndIf
#ifSym __HPWM5_PIN
    PinOutput __HPWM5_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop PWM5 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM5_Stop()
#ifSym __HPWM5_PIN
   PinInput __HPWM5_PORT_PIN
#endIfSym
   PWM5CON = %00000000
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the PWM5DCL and PWM5DCH SFRs
' Input     : HPWM_wCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
Proc HPWM5_hSetDuty()
    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    PWM5DCL = WREG
    PWM5DCH = HPWM_dCalcDuty.Byte0
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM5
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM5_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM5_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM5
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM5
'
Proc HPWM5(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                    ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                    ' Was the frequency accepted?
        HPWM5_hSetDuty()                                        ' Yes. So load the PWM5DC registers with the appropriate value
        HPWM5_Start()                                           ' Start PWM5
        Result = 1                                              ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' Invert the polarity for HPWM5
' Input     : None
' Output    : HPWM_tPolarity5 holds 1 for inverted
' Notes     : Also sets the POL bit of PWM5CON
'
Proc HPWM5_PolInvert()
    PWM5CONbits_POL = 1
    HPWM_tPolarity5 = 1
EndProc

'-----------------------------------------------------------------------------
' Do not Invert the polarity for HPWM5
' Input     : None
' Output    : HPWM_tPolarity5 holds 0 for not inverted
' Notes     : Also clears the POL bit of PWM5CON
'
Proc HPWM5_PolNormal()
    PWM5CONbits_POL = 0
    HPWM_tPolarity5 = 0
EndProc
$endif  ' _PWM5CON

'*****************************************************************************
$ifdef _PWM6CON
    Dim HPWM_tPolarity6 As Bit = 0                              ' Holds 1 if the PWM3 waveform is to be inverted

'-----------------------------------------------------------------------------
' Start PWM6 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM6_Start()
    If HPWM_tPolarity6 = 1 Then                                 ' Is the PWM to be inverted?
        PWM6CON = %10010000                                     ' Yes. So set the PWM6POL bit
    Else                                                        ' Otherwise... Not inverted...
        PWM6CON = %10000000                                     ' So... Clear the PWM6POL bit
    EndIf
#ifSym __HPWM6_PIN
    PinOutput __HPWM6_PORT_PIN
#endIfSym
EndProc

'-----------------------------------------------------------------------------
' Stop PWM6 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM6_Stop()
#ifSym __HPWM6_PIN
   PinInput __HPWM6_PORT_PIN
#endIfSym
   PWM6CON = %00000000
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the PWM6DCL and PWM6DCH SFRs
' Input     : HPWM_wCalcDuty holds the duty cycle value
' Output    : None
' Notes     : None
'
Proc HPWM6_hSetDuty()
    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    Ror HPWM_wCalcDuty                                          ' Rotate the Least Significant Bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant Bit
    PWM6DCL = WREG
    PWM6DCH = HPWM_dCalcDuty.Byte0
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM6
' Input     : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM6_Duty(pDuty As HPWM_bDuty)
    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM6_hSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM6
' Input     : pFrequency holds the 32-bit frequency (in Hz)
'           : pDuty (HPWM_bDuty) holds the 8-bit duty cycle (0 to 255)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM6
'
Proc HPWM6(pFrequency As HPWM_dFrequency, pDuty As HPWM_bDuty), STATUSbits_C
    HPWM_SetFreq(pFrequency)                                    ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                                    ' Was the frequency accepted?
        HPWM6_hSetDuty()                                        ' Yes. So load the PWM6DC registers with the appropriate value
        HPWM6_Start()                                           ' Start PWM6
        Result = 1                                              ' Indicate everything OK
    EndIf
EndProc

'-----------------------------------------------------------------------------
' Invert the polarity for HPWM6
' Input     : None
' Output    : HPWM_tPolarity6 holds 1 for inverted
' Notes     : Also sets the POL bit of PWM6CON
'
Proc HPWM6_PolInvert()
    PWM6CONbits_POL = 1
    HPWM_tPolarity6 = 1
EndProc

'-----------------------------------------------------------------------------
' Do not Invert the polarity for HPWM6
' Input     : None
' Output    : HPWM_tPolarity6 holds 0 for not inverted
' Notes     : Also clears the POL bit of PWM6CON
'
Proc HPWM6_PolNormal()
    PWM6CONbits_POL = 0
    HPWM_tPolarity6 = 0
EndProc
$endif  ' _PWM6CON

$endif  ' _HPWM_16F15376_INC_

Copy the above listing into the IDE and name it "HPWM_16F15376.inc"

The big limit with the PIC16F15376 device, and others of its family, is that the CCP and PWM peripherals all share the same timer, which is Timer2. So they will all share the same frequency of the waveform, but their duty cycles can be altered independently.

A demonstration program for the library is listed below that has been tested on a Curiousity Nano board:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Independently alter the duty cycles of the 6 HPWM channels on a PIC16F15376 device.
' Because the CCP and PWM peripherals all use Timer2, each must share the same frequency.
'
' Written for the Positron8 BASIC Compiler by Les Johnson.
'
    Device = 16F15376                                   ' Tell the compiler what device to compile for
    Declare Xtal = 32                                   ' Tell the compiler what frequency the device is operating at (in MHz)
'
' Setup which pins are used by the CCP and PWM peripherals
'
    Declare HPWM1_Pin = PORTB.0                         ' Set the pin to use for the HPWM1 channel (must also be setup with PPS)
    Declare HPWM2_Pin = PORTB.1                         ' Set the pin to use for the HPWM2 channel (must also be setup with PPS)
    Declare HPWM3_Pin = PORTB.2                         ' Set the pin to use for the HPWM3 channel (must also be setup with PPS)
    Declare HPWM4_Pin = PORTB.3                         ' Set the pin to use for the HPWM4 channel (must also be setup with PPS)
    Declare HPWM5_Pin = PORTC.2                         ' Set the pin to use for the HPWM5 channel (must also be setup with PPS)
    Declare HPWM6_Pin = PORTD.0                         ' Set the pin to use for the HPWM6 channel (must also be setup with PPS)

    Include "HPWM_16F15376.inc"                         ' Load the 16F15376 PWM library into the program
'
' Create a variable
'
    Dim bDutyValue As Byte                              ' Holds the 8-bit duty cycle value

'---------------------------------------------------------------------------
' The main program starts here
' Alter the duty cycles of the PWM channels
' Because the PWM peripherals all use Timer2, each must share the same frequency
'
Main:
    Setup()                                             ' Setup the program and the peripherals

    HPWM_SetFreq(2000)                                  ' Set PWM frequencies to 2KHz
    HPWM1_Start()                                       ' Start PWM channel 1
    HPWM2_Start()                                       ' Start PWM channel 2
    HPWM3_Start()                                       ' Start PWM channel 3
    HPWM4_Start()                                       ' Start PWM channel 4
    HPWM5_Start()                                       ' Start PWM channel 5
    HPWM6_Start()                                       ' Start PWM channel 6
'
' Adjust the duty cycles of all the PWM channels
'
    Do                                                  ' Create a loop
        For bDutyValue = 0 To 255                       ' Create a duty cycle loop
            HPWM1_Duty(bDutyValue)                      ' \
            HPWM2_Duty(bDutyValue)                      ' |
            HPWM3_Duty(bDutyValue)                      ' | Adjust the duty cycle of the PWM channels
            HPWM4_Duty(bDutyValue)                      ' |
            HPWM5_Duty(bDutyValue)                      ' |
            HPWM6_Duty(bDutyValue)                      ' /
            DelayMS 10                                  ' A small delay so the duty cycle can be seen changing
        Next
    Loop                                                ' Do it forever

'---------------------------------------------------------------------------
' Setup the program and the peripherals
' Input     : None
' Output    : None
' Notes     : Also sets the PPS (Peripheral Pin Select) for the pins used for HPWM
'
Proc Setup()
    Osc_Int32MHz()                                      ' Setup the internal oscillator for 32MHz operation on a PIC16F15376 device

    PPS_Unlock()                                        ' Unlock the PPS mechanism
    HPWM1_PPS(Pin_B0)                                   ' Configure the PPS for the pin to use for PWM channel 1
    HPWM2_PPS(Pin_B1)                                   ' Configure the PPS for the pin to use for PWM channel 2
    HPWM3_PPS(Pin_B2)                                   ' Configure the PPS for the pin to use for PWM channel 3
    HPWM4_PPS(Pin_B3)                                   ' Configure the PPS for the pin to use for PWM channel 4
    HPWM5_PPS(Pin_C2)                                   ' Configure the PPS for the pin to use for PWM channel 5
    HPWM6_PPS(Pin_D0)                                   ' Configure the PPS for the pin to use for PWM channel 6
EndProc

'---------------------------------------------------------------------------
' Setup the internal oscillator for 32MHz operation on a PIC16F15376 device
' Input     : None
' Output    : None
' Notes     : None
'
Proc Osc_Int32MHz()
    OSCCON1 = %01100000
    OSCCON3 = $00
    OSCEN   = $00
    OSCFRQ  = %00000110
    OSCSTAT = $00
    OSCTUNE = $00
EndProc

'---------------------------------------------------------------------------
' Setup the fuses to use the internal oscillator on a PIC16F15376 device at 32MHz
'
     Config1 FEXTOSC_OFF,_                              ' External Oscillator not enabled
             RSTOSC_HFINT32,_                           ' HFINTOSC (32MHz)
             CLKOUTEN_OFF,_                             ' CLKOUT function is disabled
             CSWEN_ON,_                                 ' Writing to NOSC and NDIV is allowed
             FCMEN_ON                                   ' FSCM timer enabled

     Config2 MCLRE_ON,_                                 ' MCLR pin is Master Clear function
             PWRTE_OFF,_                                ' PWRT disabled
             LPBOREN_OFF,_                              ' Low-Power BOR disabled
             BOREN_ON,_                                 ' Brown-out Reset Enabled, SBOREN bit is ignored
             BORV_LO,_                                  ' Brown-out Reset Voltage (VBOR) set to 2.45V
             ZCD_OFF,_                                  ' Zero-cross detect circuit is disabled
             PPS1WAY_OFF,_                              ' The PPSLOCK bit can be cleared and set repeatedly in software
             STVREN_ON                                  ' Stack Overflow or Underflow will cause a reset

     Config3 WDTCPS_WDTCPS_31,_                         ' Divider ratio 1:65536. Software control of WDTPS
             WDTE_OFF,_                                 ' WDT Disabled, SWDTEN is ignored
             WDTCWS_WDTCWS_7,_                          ' Window always open (100%). Software control. Keyed access not required
             WDTCCS_SC                                  ' WDT input clock selector software controlled

     Config4 BBSIZE_BB512,_                             ' 512 words boot block size
             BBEN_OFF,_                                 ' Boot Block disabled
             SAFEN_OFF,_                                ' SAF disabled
             WRTAPP_OFF,_                               ' Application Block not write protected
             WRTB_OFF,_                                 ' Boot Block not write protected
             WRTC_OFF,_                                 ' Configuration Register not write protected
             WRTSAF_OFF,_                               ' SAF not write protected
             LVP_OFF                                    ' Low Voltage Programming disabled

     Config5 CP_OFF                                     ' User NVM code protection disabled