News:

Let's find out together what makes a PIC Tick!

Main Menu

PWM problem

Started by Giuseppe MPO, Oct 10, 2021, 05:20 PM

Previous topic - Next topic

Giuseppe MPO

Hi, I have a problem with a PIC16F1619, I have tried all ways to get the PWM to work without success.
I have reduced the program to a minimum to understand the problem, I cannot understand where I am wrong or if I have forgotten something.
I also tried, as a datasheet, to match the Timer2 and set the registers of the Timer2 and the registers of the PWM,
but nothing ... in output nothing .....

            Device = 16F1619

            Xtal = 32

            All_Digital = On
            Declare Optimiser_Level = 0

            Config1 FOSC_INTOSC, PWRTE_OFF, MCLRE_ON, Cp_OFF, BOREN_On, CLKOUTEN_OFF, IESO_On, FCMEN_On
            Config2 WRT_OFF, PPS1WAY_On, ZCD_OFF, PLLEN_ON, STVREN_On, BORV_LO, LPBOR_OFF, LVP_On
            Config3 WDTCPS_WDTCPS1F, WDTE_OFF, WDTCWS_WDTCWSSW, WDTCCS_SWC

            OSCCON  = 0x78
            OSCTUNE = 0x00
            BORCON  = 0x00
'-------------------------------
            Declare HPWM1_Pin PORTB.6
            RB6PPS = 0x0E
'-------------------------------
            TRISB = 0x00
 ' -----------------------------

Init: While
HPWM 1,20,5000
DelayMS 1000
Wend

End

trastikata

QuoteDeclare HPWM1_Pin PORTB.6

Are the PWM1 and PWM2 available in this device?

Giuseppe MPO

On the datasheet it indicates that it has PWM3 and PWM4 but the compiler,
for this device only accepts PWM1 and PWM2.
I had implied that PWM3 is considered as PWM1 ....
but maybe I was wrong.

trastikata

In the P16F1619 PPI file only HPWM3 and HPWM4 are defined.

As for why the compiler accepted HPWM1 - maybe Les can clarify it.

Giuseppe

#4
If it doesn't respond directly to the HPWM you have to go and manage everything with the registers to get it going
I have an example with a 12f1840 for a half-bridge pwm on
PORT.0 and PORT.2 you can modify the register CCP1CON bit7-6 to make a single pwm output always if the registers match with your mcu

Device = 12F1840
Config1 FOSC_INTOSC, WDTE_OFF, PWRTE_ON, MCLRE_OFF, CP_ON, CPD_ON, BOREN_OFF, CLKOUTEN_OFF, IESO_OFF, FCMEN_OFF
Config2 WRT_OFF, PLLEN_OFF, STVREN_OFF, LVP_OFF'BORV_LO,

;**** End of Fuse Configurator Settings ****
;-------------------------------------------------------------------------------
Xtal 16
OSCCON = %01111000      '
LATA  = %00000000
TRISA = %00001000 'Il Pwm  Porta.0 e Porta.2
WPUA = %00001000  'on pullup  Mclr
CM1CON0.7 = 0     '
SRCON0.7 = 0      '
Clear ANSELA      '

'---------Set PWM -----------------
CCP1CON = %10111100'
CCP1AS.6 = 0       ' Auto-shutdown is disabled 
CCP1AS.5 = 0       ' Auto-shutdown is disabled
CCP1AS.4 = 0       ' Auto-shutdown is disabled
PWM1CON = %00000100'
APFCON.1 = 0       ' RA0  PB1 2 out  Push pull
CCPR1L = %01111111 ' value Pwm 10 bit  511 = 0111111111 (2 bit lsb  CCP1CON bit5/4)
PIE1.2 = 0            ' Off CCP1IE 
T2CON = 0          ' 1:1 Postscaler - Timer2 is off - Prescaler is 1

main:
If flag.0 = 0 Then   
 GIE = 0            'off l'interrupt
 CCPR1L = %01101111 'Set Duty cycle  8 bit Msb(dei 10bit) 
 CCP1CON.5 = 0           'Set 2 bit Lsb Duty cycle (dei 10bit)
 CCP1CON.4 = 0
 PR2 = 222      ' freq. 18khz
 T2CON.2 = 1    ' on Tmr2
 DelayMS 1000
 CCPR1L = %01100100
 CCP1CON.5 = 0
 CCP1CON.4 = 0
 PR2 = 200      ' freq. 20khz
 DelayMS 1000
 T2CON.2 = 0    ' off Tmr2 off pwm
goto main
end

top204

#5
I think this is one of the devices that do not use the standard mechanism for the CCP operation.

With the enhanced 14-bit core devices, especially, Microchip have not quite made their mind up how they each work. :-) So they change their operation from device to device. The compiler has "8" different HPWM library subroutines built into it for the different enhanced 14-bit core types, and the "HPWM_TYPE" directive in the device's .ppi file chooses which one to use for a particular device. How crazy is that for a peripheral that has been around for 20 years and has performed the same tasks for 20 years?????

Try the code below, which sets the HPWM library subroutine to type 6:
    Device = 16F1619                            ' Choose the microcontroller
    Declare Xtal = 16                           ' Inform the compiler we are using a device operating at 16MHz

    Declare HPWM_Type = 6                       ' Select the HPWM library subroutine type 6 for this device
   
    Declare CCP1_Pin = PORTC.5
    Declare CCP2_Pin = PORTC.3
   
'------------------------------------------------------------------
'
Main:   
    HPWM 1, 127, 2000
    HPWM 2, 127, 2000   
    Stop

In your program, you can also try the "Declare HPWM_Type = 6", and if it works on that device, please let me know and I will alter its .ppi file. If it does not work, I will need to make a ninth HPWM library subroutine for the PIC16F161x devices as well. :-) I went through the devices I had to test for the new routines, but I cannot test the devices I do not have, and the logistics of reading every single datasheet for every new, or existing, device for subtle changes, is staggeringly out of reach. LOL

Giuseppe MPO

Hi Les,
I entered the instruction you recommended in the program (Declare HPWM_Type = 6) and the PWM started to work.
Note that the Duty Cicle responds correctly to the values ​​entered, while the frequency behaves abnormally.
I tried to do the tests with two clock frequencies and the result was the following,
Frequenzy     Clock 16MHz     Clock 32MHz
(variable)       (KHz)           (KHz)
63000          31.2            15.6
62000            61.9            62.3
50000            49.9            49.9
30000            60.5            30
20000            39.9            19.9
10000            19.9            19.9
 8000            15.9            15.9
 5000            19.9            10
 4000            15.9             8
 2000             8               8
I tried to enter different values, even outside the allowed values, to help you understand what may be the problem.

top204

#7
Glad the type 6 worked. I'll alter the .ppi files for the next corrections update and compiler upgrade.

The frequency has a maximum of 32768 KHz. See the Frequency parameter text in the compiler's manual:

"Frequency is a variable, constant (0-32767), or expression that specifies the desired frequency
of the PWM signal. Not all frequencies are available at all oscillator settings."

The frequency accuracy, at the higher frequencies, also depends on the speed that the microcontroller is operating at. The compiler uses 16-bit mathematics for the HPWM calculations to keep backward compatability, so the maximum is 32767 KHz.

If higher frequencies are required, a set of procedures should be created to use 32-bit mathematics for the calculations. Quite a few years ago, I did create a library to allow frequencies up to 65535 KHz with the 18FxxKxx devices available at that time. I'll see if I can dig it out and upload it to the forum, but it will need to be changed for the enhanced (that's a laugh) 14-bit core devices because they have their SFRs, and bits within the SFRs changed constantly from device to device. :-)

top204

#8
I found the routines I created to give more control over the CCP peripherals operating as PWM.

The SFRs will, most probably, need to be changed for the particular device used, because the code was originally written for a PIC18F25K20 device and the early enhanced 14-bit core devices that used the same mechanisms:

The listing below should be saved with the name "HPWM.inc":

$ifndef _HPWM_INC_
$define _HPWM_INC_
'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Hardware PWM routines that make use of the extra CCP peripherals contained on some devices.
' It also allows frequencies upto 65535Hz
' 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 peripheral itself.
'
' Note. All PWM peripherals will share a common frequency.
' The code is untested on the newer devices and was originally created for the original enhanced 14-bit core devices
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'
' HPWM1_SetFreq(pFrequency, pDuty)
' HPWM2_SetFreq(pFrequency, pDuty)
' HPWM3_SetFreq(pFrequency, pDuty)
' HPWM4_SetFreq(pFrequency, pDuty)
' HPWM5_SetFreq(pFrequency, pDuty)
'
' Alters the frequency the a PWM peripheral and the duty cycle then starts it.
'
' pFrequency alters the frequency (in Hz) of a PWM peripheral and can be any value from 0 to 65535,
' however, lower or upper frequencies depend on the oscillator.
' pDuty alters the duty cycle of a PWM peripheral and can be any value from 0 to 255.
'
' Note that all PWM peripherals share a common timer, therefore will all operate at a common frequency.
' 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.
'
'-------------------------------------
' HPWM1_Duty(pDuty)
' HPWM2_Duty(pDuty)
' HPWM3_Duty(pDuty)
' HPWM4_Duty(pDuty)
' HPWM5_Duty(pDuty)
'
' Alters the duty of a PWM peripheral.
'
' pDuty alters the duty cycle of a PWM peripheral and can be any value from 0 to 255.
' Note that all PWM peripherals share a common timer, therefore will all operate ata common frequency
'
'-------------------------------------
' HPWM1_Start()
' HPWM2_Start()
' HPWM3_Start()
' HPWM4_Start()
' HPWM5_Start()
'
' Enables a PWM peripheral
'
'-------------------------------------
' HPWM1_Stop()
' HPWM2_Stop()
' HPWM3_Stop()
' HPWM4_Stop()
' HPWM5_Stop()
'
' Disables a PWM peripheral
'
'-------------------------------------------------------------------------------------------------------
    Symbol HPWM_cFosc = _xtal * 1000
'
' Create some global variables for the routines so they can be shared
'
    Dim HPWM_wFrequency As Word         ' Used to pass the 16-bit frequency value as a parameter
    Dim HPWM_bDuty As Byte              ' Used to pass the 8-bit duty cycle as a paramater
    Dim HPWM_bPrescaleSelect As Byte
    Dim HPWM_dMaxDuty As Dword
    Dim HPWM_wCalcDuty As Word
    Dim HPWM_wPR2Value As Word
    Dim HPWM_wPRConst As Word

'-----------------------------------------------------------------------------
' Helper routine to calculate a duty cycle required for a given PWM frequency
' Input     : HPWM_wFrequency holds the 16-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_mCalculateDuty()
    HPWM_wCalcDuty = HPWM_wFrequency * HPWM_bPrescaleSelect ' \
    HPWM_wCalcDuty = HPWM_cFosc / HPWM_wCalcDuty            ' / Determine maximum duty
    HPWM_dMaxDuty = HPWM_wCalcDuty * HPWM_bDuty             ' \
    HPWM_wCalcDuty = HPWM_dMaxDuty / 256                    ' / Calculate duty value
EndProc

'-----------------------------------------------------------------------------
' Helper routine to calculate the values required for a given frequency and duty cycle
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pInitialDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : None
'
Proc HPWM_SetFreq(pFrequency As HPWM_wFrequency, pInitialDuty As HPWM_bDuty)
    pFrequency = pFrequency / 1000
    HPWM_wPRConst = HPWM_cFosc / pFrequency
    HPWM_wPRConst = HPWM_wPRConst / 4
'
' Loop through all the valid prescalers
'
    HPWM_bPrescaleSelect = 1
    Repeat
        HPWM_wPR2Value = HPWM_wPRConst / HPWM_bPrescaleSelect           ' \
        HPWM_wPR2Value = HPWM_wPR2Value - 1                             ' / Calculate a PR2 value
        If HPWM_wPR2Value <= 255 Then                                   ' \ Is the value to place into PR2 valid?
            If HPWM_wPR2Value > 1 Then                                  ' /
                T2CON = %00000000                                       ' Yes. So Default to T2CON loaded with 0
                If HPWM_bPrescaleSelect = 4 Then                        ' Is HPWM_bPrescaleSelect 4?
                    T2CON = %00000001                                   ' Yes. So adjust T2CON accordingly
                Else If HPWM_bPrescaleSelect = 16 Then                  ' Is HPWM_bPrescaleSelect 16?
                    T2CON = %00000011                                   ' Yes. So adjust T2CON accordingly
                EndIf
                PR2 = HPWM_wPR2Value                                    ' Initialise PR2
                HPWM_mCalculateDuty()                                   ' Calculate the duty value
                Set T2CONbits_TMR2ON                                    ' Start Timer 2
                Set STATUSbits_C                                        ' Return Carry flag holding 1 if successful
                Return                                                  ' Exit the subroutine
            EndIf
        EndIf
        HPWM_bPrescaleSelect = HPWM_bPrescaleSelect * 4                 ' Move to the prescaler select value
    Until HPWM_bPrescaleSelect > 16
    Clear STATUSbits_C                                                  ' Return Carry flag holding 0 if unsuccesful
EndProc

'*****************************************************************************
$ifdef _CCP1CON
'-----------------------------------------------------------------------------
' Start CCP1 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM1_Start() '
    CCP1CON = $0C     '
    Output __CCP1_PORT_PIN

'-----------------------------------------------------------------------------
' Stop CCP1 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM1_Stop() '
   Input __CCP1_PORT_PIN  '
   CCP1CON = $00

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle into CCPR1L:CCP1CON<5:4>
' Input     : HPWM_wCalcDuty holds the 10-bit value
' Output    : None
' Notes     : CCPR1L contains the eight MSbs, and CCP1CON<5:4> contains the two LSbs.
'           : This 10-Bit value is represented by CCPR1L:CCP1CON<5:4>.
'
Proc HPWM1_mSetDuty()
    CCP1CON = CCP1CON & %11001111
    WREG = HPWM_wCalcDuty << 4
    WREG = WREG & %00110000
    CCP1CON = CCP1CON | WREG
    CCPR1L = HPWM_wCalcDuty >> 2
EndProc

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

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP1
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : Also starts the PWM for CCP1
'
Proc HPWM1_SetFreq(pFrequency As HPWM_wFrequency, pDuty As HPWM_bDuty)
    HPWM_SetFreq(pFrequency, pDuty)             ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                    ' Was the frequency accepted?
        HPWM1_mSetDuty()                        ' Yes. So load the CCP1 registers with the appropriate value
        HPWM1_Start()                           ' Start PWM1
        Set STATUSbits_C                        ' Indicate everything OK
    EndIf
EndProc
$endif ' _CCP1CON

'*****************************************************************************
$ifdef _CCP2CON
'-----------------------------------------------------------------------------
' Start CCP2 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM2_Start() '
    CCP2CON = $0C     '
    Output __CCP2_PORT_PIN

'-----------------------------------------------------------------------------
' Stop CCP2 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM2_Stop() '
   Input __CCP2_PORT_PIN  '
   CCP2CON = $00

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle into CCPR2L:CCP2CON<5:4>
' Input     : HPWM_wCalcDuty holds the 10-bit value
' Output    : None
' Notes     : CCPR2L contains the eight MSbs, and CCP2CON<5:4> contains the two LSbs.
'           : This 10-Bit value is represented by CCPR2L:CCP2CON<5:4>.
'
Proc HPWM2_mSetDuty()
    CCP2CON = CCP2CON & %11001111
    WREG = HPWM_wCalcDuty << 4
    WREG = WREG & %00110000
    CCP2CON = CCP2CON | WREG
    CCPR2L = HPWM_wCalcDuty >> 2
EndProc

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

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP2
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : Also starts the PWM For CCP2
'
Proc HPWM2_SetFreq(pFrequency As HPWM_wFrequency, pDuty As HPWM_bDuty)
    HPWM_SetFreq(pFrequency, pDuty)             ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                    ' Was the frequency accepted?
        HPWM2_mSetDuty()                        ' Yes. So load the CCP2 registers with the appropriate value
        HPWM2_Start()                           ' Start PWM2
        Set STATUSbits_C                        ' Indicate everything OK
    EndIf
EndProc
$endif ' _CCP2CON

'*****************************************************************************
$ifdef _CCP3CON
'-----------------------------------------------------------------------------
' Start CCP3 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM3_Start() '
    CCP3CON = $0C     '
#ifSym __CCP3_PORT_PIN     '
    Output __CCP3_PORT_PIN '
#endIfSym

'-----------------------------------------------------------------------------
' Stop CCP3 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM3_Stop() '
#ifSym __CCP3_PORT_PIN    '
   Input __CCP3_PORT_PIN  '
#endIfSym            '
   CCP3CON = $00

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle into CCPR3L:CCP3CON<5:4>
' Input     : HPWM_wCalcDuty holds the 10-bit value
' Output    : None
' Notes     : CCPR3L contains the eight MSBs, and CCP3CON<5:4> contains the two LSbs.
'           : This 10-Bit value is represented by CCPR3L:CCP3CON<5:4>.
'
Proc HPWM3_mSetDuty()
    CCP3CON = CCP3CON & %11001111
    WREG = HPWM_wCalcDuty << 4
    WREG = WREG & %00110000
    CCP3CON = CCP3CON | WREG
    CCPR3L = HPWM_wCalcDuty >> 2
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of CCP3
' Input     : WREG holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM3_Duty(pDuty As HPWM_bDuty)
    HPWM_mCalculateDuty()
    HPWM3_mSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP3
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : Also starts the PWM For CCP3
'
Proc HPWM3_SetFreq(pFrequency As HPWM_wFrequency, pDuty As HPWM_bDuty)
    HPWM_SetFreq(pFrequency, pDuty)             ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                    ' Was the frequency accepted?
        HPWM3_mSetDuty()                        ' Yes. So load the CCP3 registers with the appropriate value
        HPWM3_Start()                           ' Start PWM3
        Set STATUSbits_C                        ' Indicate everything OK
    EndIf
EndProc
$endif  ' _CCP3CON

'*****************************************************************************
$ifdef _CCP4CON
'-----------------------------------------------------------------------------
' Start CCP4 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM4_Start() '
    CCP4CON = $0C     '
#ifSym __CCP4_PORT_PIN     '
    Output __CCP4_PORT_PIN '
#endIfSym

'-----------------------------------------------------------------------------
' Stop CCP4 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM4_Stop() '
#ifSym __CCP4_PORT_PIN    '
   Input __CCP4_PORT_PIN  '
#endIfSym            '
   CCP4CON = $00

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle into CCPR4L:CCP4CON<5:4>
' Input     : HPWM_wCalcDuty holds the 10-bit value
' Output    : None
' Notes     : CCPR4L contains the eight MSBs, and CCP4CON<5:4> contains the two LSbs.
'           : This 10-Bit value is represented by CCPR4L:CCP4CON<5:4>.
'
Proc HPWM4_mSetDuty()
    CCP4CON = CCP4CON & %11001111
    WREG = HPWM_wCalcDuty << 4
    WREG = WREG & %00110000
    CCP4CON = CCP4CON | WREG
    CCPR4L = HPWM_wCalcDuty >> 2
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of CCP4
' Input     : WREG holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM4_Duty(pDuty As HPWM_bDuty)
    HPWM_mCalculateDuty()
    HPWM4_mSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP4
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : Also starts the PWM For CCP4
'
Proc HPWM4_SetFreq(pFrequency As HPWM_wFrequency, pDuty As HPWM_bDuty)
    HPWM_SetFreq(pFrequency, pDuty)             ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                    ' Was the frequency accepted?
        HPWM4_mSetDuty()                        ' Yes. So load the CCP4 registers with the appropriate value
        HPWM4_Start()                           ' Start PWM4
        Set STATUSbits_C                        ' Indicate everything OK
    EndIf
EndProc
$endif  ' _CCP4CON

'*****************************************************************************
$ifdef _CCP5CON
'-----------------------------------------------------------------------------
' Start CCP5 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM5_Start() '
    CCP5CON = $0C     '
#ifSym __CCP5_PORT_PIN     '
    Output __CCP5_PORT_PIN '
#endIfSym

'-----------------------------------------------------------------------------
' Stop CCP5 operation
' Input     : None
' Output    : None
' Notes     : None
'
$define HPWM5_Stop() '
#ifSym __CCP5_PORT_PIN    '
   Input __CCP5_PORT_PIN  '
#endIfSym            '
   CCP5CON = $00

'-----------------------------------------------------------------------------
' Helper routine to load the duty cycle into CCPR5L:CCP5CON<5:4>
' Input     : HPWM_wCalcDuty holds the 10-bit value
' Output    : None
' Notes     : CCPR5L contains the eight MSBs, and CCP5CON<5:4> contains the two LSbs.
'           : This 10-Bit value is represented by CCPR5L:CCP5CON<5:4>.
'
Proc HPWM5_mSetDuty()
    CCP5CON = CCP5CON & %11001111
    WREG = HPWM_wCalcDuty << 4
    WREG = WREG & %00110000
    CCP5CON = CCP5CON | WREG
    CCPR5L = HPWM_wCalcDuty >> 2
EndProc

'-----------------------------------------------------------------------------
' Adjust the duty cycle of CCP5
' Input     : WREG holds the 8-bit duty cycle (0 to 255)
' Output    : None
' Notes     : None
'
Proc HPWM5_Duty(pDuty As HPWM_bDuty)
    HPWM_mCalculateDuty()
    HPWM5_mSetDuty()
EndProc

'-----------------------------------------------------------------------------
' Calculate the values required for a required frequency and duty cycle for CCP5
' Input     : pFrequency holds the 16-bit frequency (in Hz)
'           : pDuty holds the 8-bit duty cycle (0 to 255)
' Output    : STATUSbits_C (Carry flag) holds 1 if the frequency is possibly achievable
' Notes     : Also starts the PWM For CCP5
'
Proc HPWM5_SetFreq(pFrequency As HPWM_wFrequency, pDuty As HPWM_bDuty)

    HPWM_SetFreq(pFrequency, pDuty)             ' Calculate the values for a given frequency and duty cycle
    If STATUSbits_C = 1 Then                    ' Was the frequency accepted?
        HPWM5_mSetDuty()                        ' Yes. So load the CCP5 registers with the appropriate value
        HPWM5_Start()                           ' Start PWM5
        Set STATUSbits_C                        ' Indicate everything OK
    EndIf
EndProc
$endif  ' _CCP5CON

$endif      ' _HPWM_INC_

A demonstration listing is below that alters the frequencies of the PWM peripherals. Again, it will need to be tested with the device in the code listing, but I have added the PPS setups for the pins used.

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Demonstrate the Hardware PWM routines that make use of the extra CCP peripherals contained on some enhanced 14-bit core devices.
'
' Note. All PWM peripherals will share a common frequency.
' The code is untested on the newer devices and was originally created for the original enhanced 14-bit core devices
'
' Written by Les Johnson for the Positron8 BASIC compiler.
' https://sites.google.com/view/rosetta-tech/home
'
    Device = 16F1619                                ' Choose an enhanced 14-bit core device
    Declare Xtal = 32                               ' Choose the oscillator
'
' Select the CCP pins for the available peripherals
'
    Declare CCP1_Pin = PORTC.5
    Declare CCP2_Pin = PORTC.3

    Include "HPWM.inc"                              ' Load the HPWM routines into the program
'
' Create some variables for the demo
'
    Dim bDuty As Byte
    Dim wFrequency As Word

'---------------------------------------------------------------------------
' Run the CCP peripherals as PWM with different frequencies and different duty cycles
'
Main:
    RC5PPS = PPS_FN_CCP1                            ' Configure CCP1 PPS for the pin used
    RC3PPS = PPS_FN_CCP2                            ' Configure CCP2 PPS for the pin used
   
    'HPWM1_SetFreq(2000, 127)                       ' Alter frequency with 50% duty cycle
    'HPWM2_SetFreq(2000, 127)                       ' Alter frequency with 50% duty cycle
'
' Adjust the frequency of both the PWM waveforms
'
    For wFrequency = 0 To 65535 Step 64             ' Create a loop for frequency adjustment
        HPWM1_SetFreq(wFrequency, 127)              ' Alter frequency with 50% duty cycle
        HPWM2_SetFreq(wFrequency, 127)              ' Alter frequency with 50% duty cycle
        DelayMS 1
    Next
(*
'
' Adjust the duty cycles of both peripherals
'
    HPWM_SetFreq(4000, 0)                           ' Set the frequency of both PWM peripherals

    HPWM1_Start()                                   ' \
    HPWM2_Start()                                   ' / Enable both peripherals

    Do                                              ' Create a loop
        For bDuty = 0 To 255                        ' Create a duty cycle loop
            HPWM1_Duty(bDuty)                       ' \
            HPWM2_Duty(bDuty)                       ' / Adjust the duty cycle of both PWM waveforms
            DelayMS 10                              ' A delay so the duty cycle can be seen changing
        Next
    Loop                                            ' Loop forever
*)

top204

#9
The "HPWM.inc" code listing above has just been changed to cater for the newer compiler since the code was originally created for the Amicus18 board many years ago. It now uses __CCPx_PORT_PIN, instead of __CCPx_PIN. For example, __CCP1_PIN is now __CCP1_PORT_PIN, which represents the Port.Pin used in the CCP1_Pin declare.

This had to be changed quite a few years ago because issues were given by the later assembler applications. Each Declare also creates directives or constants that can be used in a BASIC program listing. If you look in the assembler listing, you will see them as #define directives. For example, the CCPx_Pin declares used in the above listing produce:

; ALIAS VARIABLES
#define __CCP1_PORT PORTC
#define __HPWM1_PORT PORTC
#define __CCP1_PORT_PIN PORTC,5
#define __HPWM1_PORT_PIN PORTC,5
#define __CCP2_PORT PORTC
#define __HPWM2_PORT PORTC
#define __CCP2_PORT_PIN PORTC,3
#define __HPWM2_PORT_PIN PORTC,3
; CONSTANTS
#define __xtal 32
#define __CCP1_PIN 5
#define __HPWM1_PIN 5
#define __CCP2_PIN 3
#define __HPWM2_PIN 3

top204

Using the same principles as the above library code and using 32-bit variables throughout, I was able to get a PWM waveform upto 1.2MHz from a PIC18F27K42 device operating at 64MHz, so it will be possible on other devices that have better prescalers for the timer used.