News:

;) This forum is the property of Proton software developers

Main Menu

PWM on a PIC18F27/47 Q10 using CCP1, CCP2 and PWM3

Started by TimB, Nov 06, 2025, 11:09 AM

Previous topic - Next topic

TimB

I am at real loss on how to get my code running

I need variable duty on CCP1 CCP2 and PWM3 on ports RC5 - RC7 respectively

Freq is 10khz

This my code so far minus the PWM3 bit.

can anyone see where I'm going wrong?

Thanks Tim



   Device 18f47Q10

'-------------------------------------------------------------------------------
'**** Added by Fuse Configurator ****
' Use the Fuses Tab to change these settings

Config_Start
    FEXTOSC = OFF              'Oscillator not enable
    RSTOSC = HFINTOSC_64MHZ    'HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:
    CLKOUTEN = OFF             'CLKOUT function is disable
    CSWEN = ON                 'Writing to NOSC and NDIV is allowed
    FCMEN = ON                 'Fail-Safe Clock Monitor enable
    MCLRE = EXTMCLR            'MCLR pin (RE3) is MCL
    PWRTE = On                 'Power up timer is on
    LPBOREN = On               'Low power BOR is enabled
    BOREN = SBORDIS            'Brown-out Reset enabled , SBOREN bit is ignore
    BORV = VBOR_190            'Brown-out Reset Voltage (VBOR) set to 1.90
    ZCD = OFF                  'ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCO
    PPS1WAY = ON               'PPSLOCK bit can be cleared and set only once' PPS registers remain locked after one clear/set cycl
    STVREN = ON                'Stack full/underflow will cause Rese
    XINST = OFF                'Extended Instruction Set and Indexed Addressing Mode disable
    WDTCPS = WDTCPS_31         'Divider ratio 1:65536' software control of WDTP
    WDTE = OFF                 'WDT Disable
    WDTCWS = WDTCWS_7          'window always open (100%)' software control' keyed access not require
    WDTCCS = SC                'Software Contro
    WRT0 = OFF                 'Block 0 (000800-003FFFh) not write-protecte
    WRT1 = OFF                 'Block 1 (004000-007FFFh) not write-protecte
    WRT2 = OFF                 'Block 2 (008000-00BFFFh) not write-protecte
    WRT3 = OFF                 'Block 3 (00C000-00FFFFh) not write-protecte
    WRT4 = OFF                 'Block 4 (010000-013FFFh) not write-protecte
    WRT5 = OFF                 'Block 5 (014000-017FFFh) not write-protecte
    WRT6 = OFF                 'Block 6 (018000-01BFFFh) not write-protecte
    WRT7 = OFF                 'Block 7 (01C000-01FFFFh) not write-protecte
    WRTC = OFF                 'Configuration registers (300000-30000Bh) not write-protecte
    WRTB = OFF                 'Boot Block (000000-0007FFh) not write-protecte
    WRTD = OFF                 'Data EEPROM not write-protecte
    SCANE = ON                 'Scanner module is available for use, SCANMD bit can control the modul
    LVP = ON                   'Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignore
    CP = OFF                   'UserNVM code protection disable
    CPD = OFF                  'DataNVM code protection disable
    EBTR0 = OFF                'Block 0 (000800-003FFFh) not protected from table reads executed in other block
    EBTR1 = OFF                'Block 1 (004000-007FFFh) not protected from table reads executed in other block
    EBTR2 = OFF                'Block 2 (008000-00BFFFh) not protected from table reads executed in other block
    EBTR3 = OFF                'Block 3 (00C000-00FFFFh) not protected from table reads executed in other block
    EBTR4 = OFF                'Block 4 (010000-013FFFh) not protected from table reads executed in other block
    EBTR5 = OFF                'Block 5 (014000-017FFFh) not protected from table reads executed in other block
    EBTR6 = OFF                'Block 6 (018000-01BFFFh) not protected from table reads executed in other block
    EBTR7 = OFF                'Block 7 (01C000-01FFFFh) not protected from table reads executed in other block
    EBTRB = OFF                'Boot Block (000000-0007FFh) not protected from table reads executed in other block
Config_End

'**** End of Fuse Configurator Settings ****
'-------------------------------------------------------------------------------
 '**** Compiler Defs *****

    Declare Create_Coff On


    Declare Xtal = 64                           ' Tell the compiler what frequency the device is operating at (in MHz)

    Dim pCuriosityNanoLed as portE.0

    Osc_64MHz()


    Initialise_PWM()

    output pCuriosityNanoLed

    While 1 = 1


        toggle pCuriosityNanoLed       ; toggle the LED on the Nano Curiosity board

        delayms 100



    Wend




'------------------------------------------------------------------------------------
' Setup the PWM regs etc to run at 10khz on RC5-RC7
' Input     : None
' Output    : None
' Notes     : None
''
    Proc Initialise_PWM()

        InitPWMPPS()

        TRISCbits_TRISC5 = 0;
        TRISCbits_TRISC6 = 0;
        TRISCbits_TRISC7 = 0;

        ;Timer2 setup for 10 kHz @ 64 MHz
        T2CON = %11000000                       ; Timer on and 1:16 prescaler
        T2CLKCON = 0x01                         ;  Fosc/4
        T2PR = 99                               ; 10 kHz period (64MHz/(4*16*(99+1))=10kHz)


        CCPTMRS = %00010101                     ; CCP1 CCP2 and PWM3 are linked to Tmr2


        ; CCP1 setup
        CCP1COn = %00011100                     ; Left alined, PWM mode
        CCPR1H = 0                              ;
        CCPR1L = 50                             ; 50% duty to test with
        CCP1CONbits_EN = 1                      ; Turn on the CCP1 PWM


        ; CCP2 setup
        CCP2COn = %00011100                     ;  Left alined, PWM mode
        CCPR2H = 0                              ;
        CCPR2L = 50                             ;  50% duty to test with
        CCP2CONbits_EN = 1                      ;  Turn on the CCP1 PWM

    EndProc

'------------------------------------------------------------------------------------
' Setup the internal oscillator to operate at 64MHz
' Input     : None
' Output    : None
' Notes     : None
''

    Proc Osc_64MHz()
       OSCCON1 = $60               ' NOSC HFINTOSC, NDIV 1
       OSCCON3 = $00               ' CSWHOLD may proceed, SOSCPWR Low power
       OSCEN   = $00               ' MFOEN disabled, LFOEN disabled, ADOEN disabled, SOSCEN disabled, EXTOEN disabled, HFOEN disabled
       OSCFRQ  = $08               ' HFFRQ 64_MHz
       OSCTUNE = $00
    EndProc


    Proc InitPWMPPS()
        PPS_UnLock()
        RC5PPS = PPS_Fn_CCP1   'RC5 CCP1(Pin 17) bi-directional to Module CCP1
        RC6PPS = PPS_Fn_CCP2   'RC6 CCP2(Pin 17) bi-directional to Module CCP2
        RC7PPS = PPS_Fn_PWM3;   // PWM3 -> RC7
        PPS_Lock()
    EndProc




John Lawton

Probably better to use LATE.0 but that may not fix it for you.

John

top204

Here ya go Tim... A complete HPWM library for the PIC18FxxQ10 devices that can operate up into the MHz range, and have a (relative) 10-bit duty cycle on the four PWM peripherals.

The demo program is listed below:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Control 4 PWM waveforms on a PIC18F27Q10 device.
' Each waveform operates with a 10-bit duty cycle (0 to 1023)
' Written for the Positron8 BASIC compiler by Les Johnson
'
    Device = 18F27Q10                                                       ' Tell the compiler what device to compile for
    Declare Xtal = 64                                                       ' Tell the compiler what frequency the device is operating at (in MHz)
    Declare Create_Coff = True                                              ' Tell the compiler to create a COF file for debugging
    Declare Float_Display_Type = Fast                                       ' Tell the compiler to use the faster, and more accurate, library routine for Float to ASCII conversion
    Declare Auto_Heap_Arrays = On                                           ' Make all arrays created in high RAM, above standard variables
    Declare Auto_Heap_Strings = On                                          ' Make all Strings created in high RAM, above standard variables
    Declare Auto_Variable_Bank_Cross = On                                   ' Make sure multi-byte variables stay within a single RAM bank
'
' Setup USART1
'
    Declare Hserial1_Baud = 115200                                          ' Set the Baud rate of USART1
    Declare HRSOut1_Pin  = PORTC.6                                          ' Select the pin for USART1 TX
    Declare HRSIn1_Pin   = PORTC.7                                          ' Select the pin for USART1 RX 
'
' Setup which pins are used by the CCP and PWM peripherals
'
    Declare HPWM1_Pin = PORTC.0                                             ' Select the pin for the PWM1 channel
    Declare HPWM2_Pin = PORTC.1                                             ' Select the pin for the PWM2 channel
    Declare HPWM3_Pin = PORTC.2                                             ' Select the pin for the PWM3 channel
    Declare HPWM4_Pin = PORTC.3                                             ' Select the pin for the PWM4 channel

    Include "HPWM_Q10.inc"                                                  ' Load the HPWM procedures into the program
'
' Create any Global Variables here
'
    Dim dFrequency As Dword                                                 ' Holds the 32-bit frequency of a PWM waveform
    Dim wDuty As Word                                                       ' Holds the 10-bit duty cycle for a PWM channel
   
'------------------------------------------------------------------------------------------------
' The main program starts here
' Setup PWM channel frequencies, and move the 10-bit duty cycle for all 4 channels
'
Main:
    Setup()                                                                 ' Setup the program and any peripherals
    HRSOut1Ln "Started"
   
    HPWM1(HPWM_cTimer2, 1000, 0)                                            ' Setup PWM1 to use Timer2 and operate at 1KHz
    HPWM2(HPWM_cTimer2, 1000, 0)                                            ' Setup PWM2 to use Timer2 and operate at the same frequency as PWM1
    HPWM3(HPWM_cTimer4, 2000, 0)                                            ' Setup PWM2 to use Timer4 and operate at 2KHz
    HPWM4(HPWM_cTimer6, 10000, 0)                                           ' Setup PWM2 to use Timer6 and operate at 10KHz
   
    Do                                                                      ' Create a loop
        For wDuty = 0 To 1023                                               ' Create a loop for the 10-bit duty cycles
            HPWM1_Duty(wDuty)                                               ' Alter the duty cycle for PWM channel 1
            HPWM2_Duty(wDuty)                                               ' Alter the duty cycle for PWM channel 2
            HPWM3_Duty(wDuty)                                               ' Alter the duty cycle for PWM channel 3
            HPWM4_Duty(wDuty)                                               ' Alter the duty cycle for PWM channel 4
            HRSOut1Ln "Duty is ", Dec wDuty                                 ' Transmit the duty cycle value to a serial terminal
            DelayMS 5                                                       ' A small delay to see the duty cycles change
        Next                                                                ' Close the duty cycle loop
    Loop                                                                    ' Do it forever

'------------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : None
'
Proc Setup()
'
' Setup the PWM channel pins
'
    PPS_Unlock()                                                            ' Unlock the PPS mechanism
    RC0PPS = PPS_Fn_PWM1                                                    ' Configure the PPS for the pin to use for PWM channel 1
    RC1PPS = PPS_Fn_PWM2                                                    ' Configure the PPS for the pin to use for PWM channel 2
    RC2PPS = PPS_Fn_PWM3                                                    ' Configure the PPS for the pin to use for PWM channel 3
    RC3PPS = PPS_Fn_PWM4                                                    ' Configure the PPS for the pin to use for PWM channel 4
EndProc

'------------------------------------------------------------------------------------------------
' Setup the config fuses to use the internal oscillator to operate the PIC18FxxQ10 device at 64MHz.
' OSC pins are general purpose I/O.
'
Config_Start
    FEXTOSC = Off                                                           ' External Oscillator not enabled
    RSTOSC = HFINTOSC_64MHZ                                                 ' Power-up default value for COSC bits is 64MHz and CDIV = 1:1
    WDTE = Off                                                              ' Watchdog Timer disabled (Proteus Simulator does not work with Watchdog Enabled on the Q10 devices, for some reason)
    CLKOUTEN = Off                                                          ' CLKOUT function is disabled
    CSWEN = On                                                              ' Writing to NOSC and NDIV is allowed
    FCMEN = Off                                                             ' Fail-Safe Clock Monitor disabled
    MCLRE = EXTMCLR                                                         ' MCLR pin (RE3) is MCLR
    PWRTE = On                                                              ' Power up timer enabled
    LPBOREN = Off                                                           ' Low power BOR is disabled
    BOREN = On                                                              ' Brown-out turned on
    BORV = VBOR_245                                                         ' Brown-out Reset Voltage (VBOR) set to 2.45V
    ZCD = Off                                                               ' ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCON
    PPS1WAY = Off                                                           ' PPSLOCK bit can be cleared and set multiple times
    STVREN = Off                                                            ' Stack full/underflow will not cause Reset
    XINST = Off                                                             ' Extended Instruction Set Disabled
    WDTCPS = WDTCPS_16                                                      ' Watchdog Divider ratio 1:2091752 (64 seconds)
    WDTCWS = WDTCWS_7                                                       ' WDT Window is always open (100%)
    WDTCCS = LFINTOSC                                                       ' WDT reference clock is the 31.2kHz HFINTOSC output
    WRT0 = Off                                                              ' Block 0 (000800-001FFF) not write protected
    WRT1 = Off                                                              ' Block 1 (002000-003FFF) not write protected
    WRT2 = Off                                                              ' Block 2 (004000-005FFF) not write protected
    WRT3 = Off                                                              ' Block 3 (006000-007FFF) not write protected
    WRTC = Off                                                              ' Configuration registers (300000-30000B) not write protected
    WRTB = Off                                                              ' Boot Block (000000-0007FF) not write protected
    WRTD = Off                                                              ' Data EEPROM not write protected
    SCANE = Off                                                             ' Scanner module is not available for use, SCANMD bit can control the module
    LVP = Off                                                               ' HV on MCLR/VPP must be used for programming
    Cp = Off                                                                ' User NVM code protection disabled
    CPD = Off                                                               ' Data NVM code protection disabled
    EBTR0 = Off                                                             ' Block 0 (000800-001FFF) not protected from table reads executed in other blocks
    EBTR1 = Off                                                             ' Block 1 (002000-003FFF) not protected from table reads executed in other blocks
    EBTR2 = Off                                                             ' Block 2 (004000-005FFF) not protected from table reads executed in other blocks
    EBTR3 = Off                                                             ' Block 3 (006000-007FFF) not protected from table reads executed in other blocks
    EBTRB = Off                                                             ' Boot Block (000000-0007FF) not protected from table reads executed in other blocks
Config_End

The duty cycle is a relative 10-bit value, where 0 is lowest, 512 is mid(ish), and 1023 is maximum duty cycle. However, if a PWM frequency is high, the actual resolution of the PWM waveform will not be 10-bit, because the resolution of a PWM waveform, depends on its frequency. So the calculations of the library alway make the duty cycle parameter value 0 to 1023, regardless of the frequency used.

The actual library code listing is below:

$ifndef _HPWM_Q10_INC_
$define _HPWM_Q10_INC_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Hardware PWM library for the PIC18FxxQ10 devices.
' The library uses the CCP and the PWM peripherals, and allows them to use three timers independently.
' Timer2 and Timer4 and Timer6 are available for use with the CCP and PWM peripherals.
'
' This means, three independent PWM frequency waveforms can be generated.
'
' It also allows frequencies up the low MHz level.
' Low end frequencies and high end frequencies depend on the oscillator value used.
' Frequencies may be approximate depending on the oscillator value used and the frequency required.
' This is not due to a limitation of the compiler, but of the CCP and HPWM peripherals and the device's operating frequency.
'
' Written for the Positron8 BASIC Compiler by Les Johnson.
'
$if (_device <> _18F26Q10) And (_device <> _18F27Q10) And (_device <> _18F46Q10) And (_device <> _18F47Q10)
    $error "The HPWM_Q10 library is only tested on 18F26Q10 or 18F27Q10 or 18F46Q10 or 18F47Q10 devices"
$endif
'-------------------------------------
' Procedures:
' HPWM1(pTimer, pFrequency, pDuty)
' HPWM2(pTimer, pFrequency, pDuty)
' HPWM3(pTimer, pFrequency, pDuty)
' HPWM4(pTimer, pFrequency, pDuty)
'
' Alters the frequency the a PWM waveform and the duty cycle then starts it.
'
' pTimer chooses the timer to use for the PWM clock: Timer2, Timer4 or Timer6
' pFrequency alters the 32-bit frequency (in Hz) of a CCP or PWM peripheral and can be any value from 0 to 32-bit maximum,
' however, lower or upper frequencies depend on the microcontroller's oscillator.
' pDuty alters the 10-bit duty cycle of a PWM waveform and can be any value from 0 to 1023.
'
' On completion, the Carry flag (STATUSbits_C) will hold 1 if the frequency is achievable.
' Use this as a guide indication only and not as an absolute indicator.
'
'-------------------------------------
' Procedures:
' HPWM1_Duty(pDuty)
' HPWM2_Duty(pDuty)
' HPWM3_Duty(pDuty)
' HPWM4_Duty(pDuty)
'
' Alters the duty of the PWM waveform
'
' pDuty alters the 10-bit duty cycle of a waveform and can be any value from 0 to 1023.
'
'-------------------------------------
' Procedures:
' HPWM1_Start()
' HPWM2_Start()
' HPWM3_Start()
' HPWM4_Start()
'
' Enable a PWM peripheral
'
'-------------------------------------
' Procedures:
' HPWM1_Stop()
' HPWM2_Stop()
' HPWM3_Stop()
' HPWM4_Stop()
'
' Disable a PWM peripheral
'
'-------------------------------------------------------------------------------------------------------

    Declare Onboard_HPWM = 4                                            ' Set the device to have 4 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
'
' Create defines for the valid timers to use for the PWM generators
'
$define HPWM_cTimer2 2                                                  ' Use Timer2 for the PWM channel's timer
$define HPWM_cTimer4 4                                                  ' Use Timer4 for the PWM channel's timer
$define HPWM_cTimer6 6                                                  ' Use Timer6 for the PWM channel's timer

$define HPWM_cFosc $eval (_xtal * 1000)                                 ' Calculate the oscillator frequency in MHz
'
' Create global variables used by the library routines
'
    Dim HPWM_wTxCON_Addr As Word                                        ' Global variable used to hold the address of a timer's TxCON SFR. i.e. T2CON, T4CON or T6CON
    Dim HPWM_wPR_Addr    As Word                                        ' Global variable used to hold the address of a timer's PR SFR. i.e. T2PR, T4PR or T6PR

'-----------------------------------------------------------------------------
' 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_wDuty holds the 10-Bit duty cycle (0 to 1023)
' Output    : HPWM_dCalcDuty holds the 32-bit duty cycle value
' Notes     : None
'
Proc HPWM_hCalculateDuty()
Global Dim HPWM_dFrequency As Dword Shared                              ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                               ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bPrescaleSelect As Byte Shared                          ' Global shared variable used to Hold the timer's prescaler chosen for a particular frequency
Global Dim HPWM_dCalcDuty  As Dword Shared                              ' Used to calculate the duty cycle value
    Dim dMaxDuty As Dword

    HPWM_dCalcDuty = HPWM_dFrequency * HPWM_bPrescaleSelect             ' \
    HPWM_dCalcDuty = HPWM_cFosc / HPWM_dCalcDuty                        ' / Determine the maximum duty
    dMaxDuty = HPWM_dCalcDuty * HPWM_wDuty                              ' \
    HPWM_dCalcDuty = dMaxDuty / 1024                                    ' / Calculate the duty value
EndProc

'-----------------------------------------------------------------------------
' A helper routine to calculate the values required for a given timer, frequency and duty cycle
' Input     : pFrequency holds the 32-bit frequency (in Hz)
'           : Global variable HPWM_wDuty holds the 10-Bit duty cycle (0 to 1023)
'           : Global Variable HPWM_wTxCON_Addr holds the address of the timer's TxCON SFR. i.e. T2CON, T4CON or T6CON
'           : Global Variable HPWM_wPR_Addr holds the address of the timer's PR SFR. i.e. T2PR, T4PR or T6PR
' 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_hCalcFreq(pFrequency As HPWM_dFrequency), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                              ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                               ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bPrescaleSelect As Byte Shared                          ' Global shared variable used to hold the timer's prescaler chosen for a particular frequency
    Dim wPRxTemp   As Word                                              ' A temporary variable for calculations
    Dim bTConValue As Byte                                              ' Holds the value to place into a timer's TxCON SFR
    Dim wPRxValue  As Word                                              ' Holds the value that is placed into a timer's PR SFR

    pFrequency = pFrequency / 1000                                      ' \
    wPRxTemp = HPWM_cFosc / pFrequency                                  ' | Calculate the value for the timer's PRx SFR
    wPRxTemp = wPRxTemp / 4                                             ' /
'
' Loop through all the valid prescalers
'
    HPWM_bPrescaleSelect = 1
    Repeat
        wPRxValue = wPRxTemp / HPWM_bPrescaleSelect                     ' \
        wPRxValue = wPRxValue - 1                                       ' / Calculate a timer's PRx value
        If wPRxValue <= 255 Then                                        ' \ Is the value to place into the timer's PRx valid?
            If wPRxValue > 1 Then                                       ' /
                bTConValue = %10000000                                  ' Yes. So Default to TxCON loaded with 0 (1:1 Prescaler) and Enable the timer
                If HPWM_bPrescaleSelect = 2 Then                        ' Is HPWM_bPrescaleSelect 2?
                    bTConValue = %10010000                              ' Yes. So load bTConValue for a 1:2 Prescaler and Enable the timer
                ElseIf HPWM_bPrescaleSelect = 4 Then                    ' Is HPWM_bPrescaleSelect 4?
                    bTConValue = %10100000                              ' Yes. So load bTConValue for a 1:4 Prescaler and Enable the timer
                Else If HPWM_bPrescaleSelect = 8 Then                   ' Is HPWM_bPrescaleSelect 8?
                    bTConValue = %10110000                              ' Yes. So load bTConValue for a 1:8 Prescaler and Enable the timer
                Else If HPWM_bPrescaleSelect = 16 Then                  ' Is HPWM_bPrescaleSelect 16?
                    bTConValue = %11000000                              ' Yes. So load bTConValue for a 1:16 Prescaler and Enable the timer
                Else If HPWM_bPrescaleSelect = 32 Then                  ' Is HPWM_bPrescaleSelect 32?
                    bTConValue = %11010000                              ' Yes. So load bTConValue for a 1:32 Prescaler and Enable the timer
                Else If HPWM_bPrescaleSelect = 64 Then                  ' Is HPWM_bPrescaleSelect 64?
                    bTConValue = %11100000                              ' Yes. So load bTConValue for a 1:64 Prescaler and Enable the timer
                Else If HPWM_bPrescaleSelect = 128 Then                 ' Is HPWM_bPrescaleSelect 128?
                    bTConValue = %11110000                              ' Yes. So adjust TxCON for a 1:128 Prescaler and Enable the timer
                EndIf
                HPWM_hCalculateDuty()                                   ' Calculate the duty value based upon global variable HPWM_wDuty

                Ptr8(HPWM_wPR_Addr)    = wPRxValue.Byte0                ' Load the appropriate timer's PRx register. T2PR, T4PR or T6PR
                Ptr8(HPWM_wTxCON_Addr) = bTConValue                     ' Load the appropriate timer's TxCON register

                Result = 1                                              ' Return the Carry flag holding 1 if successful
                ExitProc                                                ' Exit the procedure
            EndIf
        EndIf
        HPWM_bPrescaleSelect = HPWM_bPrescaleSelect * 2                 ' Move to the prescaler select value
    Until HPWM_bPrescaleSelect > 128
    Result = 0                                                          ' Return the Carry flag holding 0 if unsuccesful
EndProc

'-----------------------------------------------------------------------------
' Select the Timer to use for a CCP peripheral operating as PWM, or a PWM peripheral
' Input     : pChannel holds the PWM channel (1 to 8)
'           : pTimer holds the timer to use: HPWM_cTimer2, HPWM_cTimer4 or HPWM_cTimer6
' Output    : HPWM_wTxCON_Addr holds the address of the timer's TxCON SFR. i.e. T2CON, T4CON or T6CON
'           : HPWM_wPR_Addr holds the address of the timer's PR SFR. i.e. T2PR, T4PR or T6PR
' Notes     : The timer chosen has its clock set to fOsc/4
'
Proc HPWM_SelectTimer(pChannel As Byte, pTimer As HPWM_bTimerReq)
Global Dim HPWM_bTimerReq As Byte Shared                                ' Global shared variable used to pass the timer required as a parameter
    Dim bCCPT_BitsValue As Byte                                         ' Holds the bit values required for a particular timer

    If pChannel = 0 Then ExitProc

    If pTimer = HPWM_cTimer2 Then                                       ' Is Timer2 being used for the PWM?
        bCCPT_BitsValue.0 = 1                                           ' \ Yes. So select Timer2
        bCCPT_BitsValue.1 = 0                                           ' /
        T2CLK = %00000001                                               ' T2CS is fOsc/4
        HPWM_wTxCON_Addr = AddressOf(T2CON)                             ' Load the address of T2CON into HPWM_wTxCON_Addr
        HPWM_wPR_Addr    = AddressOf(T2PR)                              ' Load the address of T2PR into HPWM_wPR_Addr

    ElseIf pTimer = HPWM_cTimer4 Then                                   ' Is Timer4 being used for the PWM?
        bCCPT_BitsValue.0 = 0                                           ' \ Yes. So select Timer4
        bCCPT_BitsValue.1 = 1                                           ' /
        T4CLK = %00000001                                               ' T4CS is fOsc/4
        HPWM_wTxCON_Addr = AddressOf(T4CON)                             ' Load the address of T4CON into HPWM_wTxCON_Addr
        HPWM_wPR_Addr    = AddressOf(T4PR)                              ' Load the address of T4PR into HPWM_wPR_Addr

    ElseIf pTimer = HPWM_cTimer6 Then                                   ' Is Timer6 being used for the PWM?
        bCCPT_BitsValue.0 = 1                                           ' \ Yes. So select Timer6
        bCCPT_BitsValue.1 = 1                                           ' /
        T6CLK = %00000001                                               ' T6CS is fOsc/4
        HPWM_wTxCON_Addr = AddressOf(T6CON)                             ' Load the address of T6CON into HPWM_wTxCON_Addr
        HPWM_wPR_Addr    = AddressOf(T6PR)                              ' Load the address of T6PR into HPWM_wPR_Addr
    Else                                                                ' Otherwise... Not a valid timer
        ExitProc                                                        ' So exit the procedure
    EndIf

    If pChannel = 1 Then
        CCPTMRSbits_C1TSEL0 = bCCPT_BitsValue.0                         ' \ Select the timer for the CCP1 peripheral acting as PWM
        CCPTMRSbits_C1TSEL1 = bCCPT_BitsValue.1                         ' /

    ElseIf pChannel = 2 Then
        CCPTMRSbits_C2TSEL0 = bCCPT_BitsValue.0                         ' \ Select the timer for the CCP2 peripheral acting as PWM
        CCPTMRSbits_C2TSEL1 = bCCPT_BitsValue.1                         ' /

    ElseIf pChannel = 3 Then
        CCPTMRSbits_P3TSEL0 = bCCPT_BitsValue.0                         ' \ Select the timer for the PWM3 peripheral
        CCPTMRSbits_P3TSEL1 = bCCPT_BitsValue.1                         ' /

    ElseIf pChannel = 4 Then
        CCPTMRSbits_P4TSEL0 = bCCPT_BitsValue.0                         ' \ Select the timer for the PWM4 peripheral
        CCPTMRSbits_P4TSEL1 = bCCPT_BitsValue.1                         ' /
    EndIf
EndProc

'-----------------------------------------------------------------------------
' Set the frequency of all the PWM channels
' Input     : pFrequency holds the 32-bit frequency (in Hz)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
' Notes     : Uses Timer2 for the clock source for the CCP and PWM peripherals
'
Proc HPWM_SetFreq(pFrequency As HPWM_dFrequency), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                              ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                               ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bPrescaleSelect As Byte Shared                          ' Global shared variable used to Hold the timer's prescaler chosen for a particular frequency
    Dim wPRxTemp  As Word
    Dim wPRxValue As Word

    HPWM_wDuty = 0                                                      ' Set the duty value to 0
    T2CLK = %00000001                                                   ' T2CS is FOSC/4
    CCPTMRS = %01010101                                                 ' Select Timer2 for the PWM and CCP peripherals acting as PWM

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

'*****************************************************************************
$ifdef _CCP1CON
'-----------------------------------------------------------------------------
' Start CCP1 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM1_Start()
    CCP1CON = %10001100
#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 = %00001100
EndProc

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle value into the CCPR1L and CCPR1H SFRs
' Input     : HPWM_wCalcDuty 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_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : None
' Notes     : None
'
Proc HPWM1_Duty(pDuty As HPWM_wDuty)
Global Dim HPWM_wDuty As Word Shared                                    ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_dCalcDuty  As Dword Shared                              ' Used to calculate the duty cycle value

    HPWM_hCalculateDuty()                                               ' Calculate the duty value into HPWM_wCalcDuty
    HPWM1_hSetDuty()                                                    ' Set the duty cycle of the waveform
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_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
' Notes     : Also starts the PWM for CCP1
'
Proc HPWM1(pTimer As HPWM_bTimerReq, pFrequency As HPWM_dFrequency, pDuty As HPWM_wDuty), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                              ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                               ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bTimerReq  As Byte Shared                               ' Global shared variable used to pass the timer required as a parameter
Global Dim HPWM_dCalcDuty  As Dword Shared                              ' Used to calculate the duty cycle value

    HPWM_SelectTimer(1, pTimer)                                         ' Choose which timer to use for the channel
    HPWM_hCalcFreq(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 and PWM4 peripherals"
$define HPWM1_PolNormal() HPWM_tPolarity1 = 0                           ' Set the polarity to normal

$endif ' _CCP1CON

'*****************************************************************************
$ifdef _CCP2CON
'-----------------------------------------------------------------------------
' Start CCP2 operation
' Input     : None
' Output    : None
' Notes     : None
'
Proc HPWM2_Start()
    CCP2CON = %10001100                                     ' So. Clear the FMT bit
#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 = %00001100
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_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : None
' Notes     : None
'
Proc HPWM2_Duty(pDuty As HPWM_wDuty)
Global Dim HPWM_wDuty As Word Shared                            ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_dCalcDuty  As Dword Shared                      ' Used to calculate the duty cycle value

    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM2_hSetDuty()                                            ' Set the duty cycle of the waveform
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_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : Returns 1 if the frequency is possibly achievable, Otherwise, returns a 0
' Notes     : Also starts the PWM for CCP2
'
Proc HPWM2(pTimer As HPWM_bTimerReq, pFrequency As HPWM_dFrequency, pDuty As HPWM_wDuty), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                      ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                       ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bTimerReq  As Byte Shared                       ' Global shared variable used to pass the timer required as a parameter
Global Dim HPWM_dCalcDuty  As Dword Shared                      ' Used to calculate the duty cycle value

    HPWM_SelectTimer(2, pTimer)                                 ' Choose which timer to use for the channel
    HPWM_hCalcFreq(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

'------------------------------------------------------------------------
$define HPWM2_PolInvert() $error "Cannot invert the polarity of the CCP2 peripheral operating as PWM. Only suitable with the PWM3 and PWM4 peripherals"
$define HPWM2_PolNormal() CCP2CONbits_FMT = 0                   ' Set the HPWM2 polarity to normal

$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()
Global Dim HPWM_dCalcDuty As Dword Shared                       ' Used to calculate the duty cycle value

    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_dCalcDuty                                          ' Rotate the Least Significant bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant bit
    Ror HPWM_dCalcDuty                                          ' Rotate the Least Significant bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant bit
    PWM3DCL = WREG                                              ' Place WREG into the low duty SFR
    PWM3DCH = HPWM_dCalcDuty.Byte0                              ' Place HPWM_wCalcDuty.Byte0 into the high duty SFR
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM3
' Input     : pDuty (HPWM_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : None
' Notes     : None
'
Proc HPWM3_Duty(pDuty As HPWM_wDuty)
Global Dim HPWM_wDuty As Word Shared                            ' Global shared variable used to pass the duty cycle as a parameter

    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM3_hSetDuty()                                            ' Set the duty cycle of the waveform
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM3
' Input     : pFrequency holds the 32-Bit frequency (in Hz)
'           : pDuty (HPWM_wDuty) holds the 10-Bit duty cycle (0 to 1023)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM3
'
Proc HPWM3(pTimer As HPWM_bTimerReq, pFrequency As HPWM_dFrequency, pDuty As HPWM_wDuty), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                      ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                       ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bTimerReq  As Byte Shared                       ' Global shared variable used to pass the timer required as a parameter

    HPWM_SelectTimer(3, pTimer)                                 ' Choose which timer to use for the channel
    HPWM_hCalcFreq(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()
Global Dim HPWM_dCalcDuty As Dword Shared                       ' Used to calculate the duty cycle value

    WREG = 0                                                    ' Clear WREG before the shifts
    Ror HPWM_dCalcDuty                                    ' Rotate the Least Significant bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant bit
    Ror HPWM_dCalcDuty                                    ' Rotate the Least Significant bit into the Carry flag
    Ror WREG                                                    ' Rotate the Carry flag into the Most Significant bit
    PWM4DCL = WREG                                              ' Place WREG into the low duty SFR
    PWM4DCH = HPWM_dCalcDuty.Byte0                              ' Place HPWM_wCalcDuty.Byte0 into the high duty SFR
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of PWM4
' Input     : pDuty (HPWM_wDuty) holds the 10-Bit duty cycle (0 To 1023)
' Output    : None
' Notes     : None
'
Proc HPWM4_Duty(pDuty As HPWM_wDuty)
Global Dim HPWM_wDuty As Word Shared                            ' Global shared variable used to pass the duty cycle as a parameter

    HPWM_hCalculateDuty()                                       ' Calculate the duty value into HPWM_wCalcDuty
    HPWM4_hSetDuty()                                            ' Set the duty cycle of the waveform
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for PWM4
' Input     : pFrequency holds the 32-bit frequency (in Hz)
'           : pDuty (HPWM_wDuty) holds the 10-Bit duty cycle (0 to 1023)
' Output    : Returns 1 if the frequency is possibly achievable, otherwise, returns a 0
' Notes     : Also starts the PWM for PWM4
'
Proc HPWM4(pTimer As HPWM_bTimerReq, pFrequency As HPWM_dFrequency, pDuty As HPWM_wDuty), STATUSbits_C
Global Dim HPWM_dFrequency As Dword Shared                      ' Global shared variable used to pass the 32-bit frequency value as a parameter
Global Dim HPWM_wDuty      As Word Shared                       ' Global shared variable used to pass the duty cycle as a parameter
Global Dim HPWM_bTimerReq  As Byte Shared                       ' Global shared variable used to pass the timer required as a parameter

    HPWM_SelectTimer(4, pTimer)                                 ' Choose which timer to use for the channel
    HPWM_hCalcFreq(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

$endif  ' _HPWM_Q10_INC_

Below is the demo running in the proteus simulator, and I have also checked it on a real device:

PIC18fXXQ10_PWM.jpg

The demo and library code sources, and proteus simulation files are also attached below, named: "18FxxQ10_PWM.zip"

Regards
Les

TimB

I just posted the solution and it was a long rant about AI feeding me BS. I cannot be bothered to write it again

John posted just as I went to post and the forum warned me but I just looked at what was posted and lost my post

The issue was the value loaded in to CCPR2H and CCPR2L. AI told me it was right and I belived it.

Anyway code is now working just have to add PWM3

Tim

   Device 18f47Q10

'-------------------------------------------------------------------------------
'**** Added by Fuse Configurator ****
' Use the Fuses Tab to change these settings

Config_Start
    FEXTOSC = OFF              'Oscillator not enable
    RSTOSC = HFINTOSC_64MHZ    'HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:
    CLKOUTEN = OFF             'CLKOUT function is disable
    CSWEN = ON                 'Writing to NOSC and NDIV is allowed
    FCMEN = ON                 'Fail-Safe Clock Monitor enable
    MCLRE = EXTMCLR            'MCLR pin (RE3) is MCL
    PWRTE = On                 'Power up timer is on
    LPBOREN = On               'Low power BOR is enabled
    BOREN = SBORDIS            'Brown-out Reset enabled , SBOREN bit is ignore
    BORV = VBOR_190            'Brown-out Reset Voltage (VBOR) set to 1.90
    ZCD = OFF                  'ZCD disabled. ZCD can be enabled by setting the ZCDSEN bit of ZCDCO
    PPS1WAY = ON               'PPSLOCK bit can be cleared and set only once' PPS registers remain locked after one clear/set cycl
    STVREN = ON                'Stack full/underflow will cause Rese
    XINST = OFF                'Extended Instruction Set and Indexed Addressing Mode disable
    WDTCPS = WDTCPS_31         'Divider ratio 1:65536' software control of WDTP
    WDTE = OFF                 'WDT Disable
    WDTCWS = WDTCWS_7          'window always open (100%)' software control' keyed access not require
    WDTCCS = SC                'Software Contro
    WRT0 = OFF                 'Block 0 (000800-003FFFh) not write-protecte
    WRT1 = OFF                 'Block 1 (004000-007FFFh) not write-protecte
    WRT2 = OFF                 'Block 2 (008000-00BFFFh) not write-protecte
    WRT3 = OFF                 'Block 3 (00C000-00FFFFh) not write-protecte
    WRT4 = OFF                 'Block 4 (010000-013FFFh) not write-protecte
    WRT5 = OFF                 'Block 5 (014000-017FFFh) not write-protecte
    WRT6 = OFF                 'Block 6 (018000-01BFFFh) not write-protecte
    WRT7 = OFF                 'Block 7 (01C000-01FFFFh) not write-protecte
    WRTC = OFF                 'Configuration registers (300000-30000Bh) not write-protecte
    WRTB = OFF                 'Boot Block (000000-0007FFh) not write-protecte
    WRTD = OFF                 'Data EEPROM not write-protecte
    SCANE = ON                 'Scanner module is available for use, SCANMD bit can control the modul
    LVP = ON                   'Low voltage programming enabled. MCLR/VPP pin function is MCLR. MCLRE configuration bit is ignore
    CP = OFF                   'UserNVM code protection disable
    CPD = OFF                  'DataNVM code protection disable
    EBTR0 = OFF                'Block 0 (000800-003FFFh) not protected from table reads executed in other block
    EBTR1 = OFF                'Block 1 (004000-007FFFh) not protected from table reads executed in other block
    EBTR2 = OFF                'Block 2 (008000-00BFFFh) not protected from table reads executed in other block
    EBTR3 = OFF                'Block 3 (00C000-00FFFFh) not protected from table reads executed in other block
    EBTR4 = OFF                'Block 4 (010000-013FFFh) not protected from table reads executed in other block
    EBTR5 = OFF                'Block 5 (014000-017FFFh) not protected from table reads executed in other block
    EBTR6 = OFF                'Block 6 (018000-01BFFFh) not protected from table reads executed in other block
    EBTR7 = OFF                'Block 7 (01C000-01FFFFh) not protected from table reads executed in other block
    EBTRB = OFF                'Boot Block (000000-0007FFh) not protected from table reads executed in other block
Config_End

'**** End of Fuse Configurator Settings ****
'-------------------------------------------------------------------------------
 '**** Compiler Defs *****

    Declare Create_Coff On


    Declare Xtal = 64                           ' Tell the compiler what frequency the device is operating at (in MHz)

    Dim pCuriosityNanoLed as porte.0
    dim pPortc5 as portc.5
    dim pPortc6 as portc.6
    dim  pPortc7 as portc.7

    Osc_64MHz()


    Initialise_PWM()


    output pCuriosityNanoLed




    While 1 = 1


        toggle pCuriosityNanoLed       ; toggle the LED on the Nano Curiosity board

        high portc


        delayms 100




    Wend




'------------------------------------------------------------------------------------
' Setup the PWM regs etc to run at 10khz on RC5-RC7
' Input     : None
' Output    : None
' Notes     : None
''
    Proc Initialise_PWM()

        InitPWMPPS()

        ; PWMS pins as outputs and digital

        TRISCbits_TRISC5 = 0;
        TRISCbits_TRISC6 = 0;
        TRISCbits_TRISC7 = 0;

        ANSELCbits_ANSELC5 = 0
        ANSELCbits_ANSELC6= 0
        ANSELCbits_ANSELC7 = 0

        ;Timer2 setup for 10 kHz @ 64 MHz
        T2CLKCON = 0x01                         ;  Fosc/4
        T2CON = $C0                             ; Timer on and 1:16 prescaler

        T2PR = 99                               ; 10 kHz period (64MHz/(4*16*(99+1))=10kHz)



        CCPTMRS = $55                           ; CCP1 CCP2 PWM3 and PWM4 are linked to Tmr2


        ; CCP1 setup
        CCP1COn = %00111100                     ; Left alined, PWM mode
        CCPR1H = 0x13                              ;
        CCPR1L = 0X88                             ; Random duty to test with
        CCP1CONbits_EN = 1                      ; Turn on the CCP1 PWM


        ; CCP2 setup
        CCP2COn = %00111100                     ;  Left alined, PWM mode
        CCPR2H = 0x13                               ;
        CCPR2L = 0X88                              ;  Some random duty to test with
        CCP2CONbits_EN = 1                      ;  Turn on the CCP1 PWM

    EndProc

'------------------------------------------------------------------------------------
' Setup the internal oscillator to operate at 64MHz
' Input     : None
' Output    : None
' Notes     : None
''

    Proc Osc_64MHz()
       OSCCON1 = $60               ' NOSC HFINTOSC, NDIV 1
       OSCCON3 = $00               ' CSWHOLD may proceed, SOSCPWR Low power
       OSCEN   = $00               ' MFOEN disabled, LFOEN disabled, ADOEN disabled, SOSCEN disabled, EXTOEN disabled, HFOEN disabled
       OSCFRQ  = $08               ' HFFRQ 64_MHz
       OSCTUNE = $00
    EndProc


    Proc InitPWMPPS()
        PPS_UnLock()
        RC5PPS = PPS_Fn_CCP1   'RC5 to Module CCP1
        RC6PPS = PPS_Fn_CCP2   'RC6 to Module CCP2
        RC7PPS = PPS_Fn_PWM3   'Rc7 to Module PWM3
        PPS_Lock()
    EndProc

TimB


Les thanks a million


I only got my code working when I compiled A HPWM commanded and picked over the asm

I'm going to use yours though



TimB



Just a heads up while the VSM says PWM3 works, nothing I can do can make it run in real life.

I tried different pins and Tmrs but PWM3 seems to not work. I have tried figuring out but cannot see any issues.

It may be my Curiosity Nano board..







top204

As I am typing this post, I can see the PWM3 and PWM4 channels working on a real device plugged into one of John's Amicus8 boards. I can see the duty cycles change, and the correct frequencies, on that wonderful Hantek scope I got off you Tim. CCP1 and CCP2 also work nicely, and are showing 1KHz operation.

So pin PORTC.0 is producing a waveform at 1KHz, and so is PORTC.1, and PORTC.2 is producing  a waveform of 2KHz, and PORTC.3 is producing a waveform of 10KHz.

Once I had replaced some of the electrolytic capacitors in the scope's PSU board, and soldered the 'dozens' of dry joints, it works wonderfully. Many thanks. :-)

Best regards
Les