News:

PROTON pic BASIC Compilers for PIC, PIC24, dsPIC33

Main Menu

Hpwm

Started by Ivano, Oct 16, 2025, 08:15 PM

Previous topic - Next topic

Ivano

Good evening, I'm testing the hpwm command on a PIC 18F25K83, which works very well, but I've noticed that I can't use different frequencies even though the PIC supports it. By correctly setting the various registers and PWM associations to the various timers, I get different frequencies, but not with hpwm. Is this a limitation, or is there some other setting I can do?
Thanks.Ivano

charliecoutas

Show us the HPWM command with some values that don't work Ivano.

Regards
Charlie

Ivano

Good morning, the commands work fine, but they seem to use the same timer, because they only work properly if you set the same frequency for all of them. My question is whether this is a limitation of the hpwm command, or is there another way to have PWMs with separate frequencies using this command?
By setting the various registers, I get separate frequencies for each PWM, but not with hpwm.
Best regards

RGV250

Hi,
Have a look at the HPWM demo in samples\newSamples\Proton_HPWM_macros as that uses 4.

Bob

Ivano

Good evening, I tried the hpwm example, but it doesn't work with the PIC18F25K83. I'll try adapting the registers in the .inc file for this PIC.
Has anyone already used two PWM with different frequencies and the HPWM command. In my project with two PWM, it works fine if I use the same frequency, but in the assembler, it shows that it enables multiple timers, so it assumes I can have two PWM with different frequencies. If I set different frequencies on the two outputs, I get two frequencies equal to the last one.
Ivano

top204

The compiler's HPWM command is very generic, and is now rather outdated, since it has been with the compiler since day 1. i.e. approx 23 years ago, and the CCP peripherals have evolved and changed quite a bit since then, as well as newer PWM peripherals added. However, on all CCP and PWM channels, the same timer used will mean they all share the same frequency, and the default timer is Timer2.

So for a more refined HPWM mechanism, the datasheet needs to be read, and the CCP and/or PWM peripherals need to be understood for the device being used, because microchip have a nasty habit of changing peripherals on different devices. Then a set of procedures can be created, that cater for a particular device type, or a family type if the changes made by microchip are not too drastic.

For example, the code listing below is a demo using a PWM library I have just adapted from an earlier library I created a few years ago, that works on the PIC18FxxK83 devices, and allows all 8 PWM channels to be used, and with frequencies up to 2MHz with a clean waveform, and higher frequencies with more noisy waveforms, all with an 8-bit duty cycle, regardless of the frequency:

'
'   /\\\\\\\\\
'  /\\\///////\\\
'  \/\\\     \/\\\                                                 /\\\          /\\\
'   \/\\\\\\\\\\\/        /\\\\\     /\\\\\\\\\\     /\\\\\\\\   /\\\\\\\\\\\  /\\\\\\\\\\\  /\\\\\\\\\
'    \/\\\//////\\\      /\\\///\\\  \/\\\//////    /\\\/////\\\ \////\\\////  \////\\\////  \////////\\\
'     \/\\\    \//\\\    /\\\  \//\\\ \/\\\\\\\\\\  /\\\\\\\\\\\     \/\\\         \/\\\        /\\\\\\\\\\
'      \/\\\     \//\\\  \//\\\  /\\\  \////////\\\ \//\\///////      \/\\\ /\\     \/\\\ /\\   /\\\/////\\\
'       \/\\\      \//\\\  \///\\\\\/    /\\\\\\\\\\  \//\\\\\\\\\\    \//\\\\\      \//\\\\\   \//\\\\\\\\/\\
'        \///        \///     \/////     \//////////    \//////////      \/////        \/////     \////////\//
'                                  Let's find out together what makes a PIC Tick!
'
' Hardware PWM routines that make use of the CCP and PWM peripherals on the PIC18FxxK83 devices.
' It allows frequencies into the MHz range.
' 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 PWM peripherals.
'
' Note. All CCP and PWM peripherals will share a common frequency, if using the same Timer.
'
' This demo sets all the PWM channels up with different timers and frequencies.
' Demo and library tested on a PIC18F26K83 device.
'
' Written for the Positron8 BASIC Compiler by Les Johnson.
' https://sites.google.com/view/rosetta-tech/positron-compilers-experimenters-notebook
'
    Device = 18F26K83                                           ' Tell the compiler what device to compile for
    Declare Xtal = 64                                           ' Tell the compiler what frequency the device will be operating at (in MHz)
    Declare Auto_Heap_Strings = On                              ' Tell the compiler to create Strings above standard variables, so assembler code is more efficient
    Declare Auto_Variable_Bank_Cross = On                       ' Tell the compiler to create any multi-byte variables in the same RAM bank. For more efficiency
'
' Setup USART1 for debugging
'
    Declare Hserial1_Baud = 9600
    Declare HRSOut1_Pin = PORTC.6
'
' Setup which pins are used by the CCP and PWM peripherals
'
    Declare HPWM1_Pin = PORTC.0                                 ' PWM Channel 1 will be on pin PORTC.0
    Declare HPWM2_Pin = PORTC.1                                 ' PWM Channel 2 will be on pin PORTC.1
    Declare HPWM3_Pin = PORTC.2                                 ' PWM Channel 3 will be on pin PORTC.2
    Declare HPWM4_Pin = PORTC.3                                 ' PWM Channel 4 will be on pin PORTC.3
    Declare HPWM5_Pin = PORTA.0                                 ' PWM Channel 5 will be on pin PORTA.0
    Declare HPWM6_Pin = PORTA.1                                 ' PWM Channel 6 will be on pin PORTA.1
    Declare HPWM7_Pin = PORTA.2                                 ' PWM Channel 7 will be on pin PORTA.2
    Declare HPWM8_Pin = PORTA.3                                 ' PWM Channel 8 will be on pin PORTA.3

    Include "CCP_PWM_K83.inc"                                   ' Load the CCP PWM library into the program
'
' Create any Global variables here
'

'--------------------------------------------------------------------------------------------------
' The main program starts here
' Set all the PWM channels up with different timers and frequencies
'
Main:
    Setup()                                                     ' Setup the program and any peripherals
    HRSOut1Ln "Start"                                           ' Transmit text to make sure the device is operating at the correct frequency

    HPWM1(HPWM_cTimer2, 1000, 127)                              ' Alter PWM1 frequency using Timer2 to 1KHz, with a 50% duty cycle
    HPWM2(HPWM_cTimer2, 1000, 127)                              ' Alter PWM2 frequency using Timer2 to 1KHz, with a 50% duty cycle
    HPWM3(HPWM_cTimer4, 2000, 127)                              ' Alter PWM3 frequency using Timer4 to 2KHz, with a 50% duty cycle
    HPWM4(HPWM_cTimer4, 2000, 127)                              ' Alter PWM4 frequency using Timer4 to 2KHz, with a 50% duty cycle
    HPWM5(HPWM_cTimer4, 2000, 127)                              ' Alter PWM5 frequency using Timer4 to 2KHz, with a 50% duty cycle
    HPWM6(HPWM_cTimer6, 30000, 127)                             ' Alter PWM6 frequency using Timer6 to 30KHz, with a 50% duty cycle
    HPWM7(HPWM_cTimer6, 30000, 127)                             ' Alter PWM7 frequency using Timer6 to 30KHz, with a 50% duty cycle
    HPWM8(HPWM_cTimer6, 30000, 127)                             ' Alter PWM8 frequency using Timer6 to 30KHz, with a 50% duty cycle

'--------------------------------------------------------------------------------------------------
' Setup the program and any peripherals
' Input     : None
' Output    : None
' Notes     : Note that CCP PWM channels can use PORTB and PORTC.
'             But PWM channels can only use PORTA and PORTC.
'
Proc Setup()
    Osc_64MHz()                                                 ' Initialise the device to use its internal oscillator at 64MHz
'
' Setup the PPS to suit the Declares used from the PWM channel pins
'
    PPS_Unlock()                                                ' Unlock PPS
    RC0PPS = PPS_Fn_PWM1                                        ' Configure PWM1 PPS for pin PORTC.0
    RC1PPS = PPS_Fn_PWM2                                        ' Configure PWM2 PPS for pin PORTC.1
    RC2PPS = PPS_Fn_PWM3                                        ' Configure PWM3 PPS for pin PORTC.2
    RC3PPS = PPS_Fn_PWM4                                        ' Configure PWM4 PPS for pin PORTC.3
    RA0PPS = PPS_Fn_PWM5                                        ' Configure PWM5 PPS for pin PORTA.0
    RA1PPS = PPS_Fn_PWM6                                        ' Configure PWM6 PPS for pin PORTA.1
    RA2PPS = PPS_Fn_PWM7                                        ' Configure PWM7 PPS for pin PORTA.2
    RA3PPS = PPS_Fn_PWM8                                        ' Configure PWM8 PPS for pin PORTA.3
EndProc

'--------------------------------------------------------------------------------------------------
' Setup the device to use the internal oscillator at 64MHz
' Input     : None
' Output    : None
' Notes     : None
'
Proc Osc_64MHz()
    OSCCON1 = $60                                               ' Use HFINTOSC. NDIV is 1
    OSCCON3 = $00
    OSCEN = $00
    OSCFRQ = $08                                                ' 64MHz
    OSCTUNE = $00
    DelayMS 100
EndProc

'--------------------------------------------------------------------------------------------------
' Setup the config fuses to operate the PIC18FxxK83 device at 64MHz, using its internal oscillator.
' The OSC pins are general purpose I/O.

Config_Start
    FEXTOSC = Off                                               ' External Oscillator not enabled
    RSTOSC = HFINTOSC_64MHZ                                     ' HFINTOSC with HFFRQ = 64 MHz and CDIV = 1:1
    CLKOUTEN = Off                                              ' CLKOUT function is disabled
    PR1WAY = Off                                                ' PRLOCK bit can be cleared and set repeatedly
    CSWEN = On                                                  ' Clock Switch Writing to NOSC and NDIV is allowed
    FCMEN = On                                                  ' Fail-Safe Clock Monitor enabled
    MCLRE = EXTMCLR                                             ' If LVP = 0, MCLR pin is MCLR. If LVP = 1, RE3 pin function is MCLR
    PWRTS = PWRT_OFF                                            ' Power-up timer is disabled
    MVECEN = Off                                                ' Interrupt contoller does not use vector table to prioritse interrupts
    IVT1WAY = On                                                ' IVTLOCK bit can be cleared and set only once
    LPBOREN = Off                                               ' Low Power BOR disabled
    BOREN = SBORDIS                                             ' Brown-out Reset enabled, SBOREN bit is ignored
    BORV = VBOR_2P45                                            ' Brown-out Reset Voltage is 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 repeatedly
    STVREN = On                                                 ' Stack full/underflow will cause reset
    Debug = Off                                                 ' Background debugger disabled
    XINST = Off                                                 ' Extended Instruction Set disabled
    WDTCPS = WDTCPS_31                                          ' Watchdog Period Divider ratio 1:65536. Software control of WDTPS
    WDTE = Off                                                  ' Watchdog Disabled. SWDTEN is ignored
    WDTCWS = WDTCWS_7                                           ' Watchdog Window always open (100%). Software control. Keyed access not required
    WDTCCS = SC                                                 ' Watchdog input clock is Software Control
    BBSIZE = BBSIZE_512                                         ' Boot Block size is 512 words
    BBEN = Off                                                  ' Boot block disabled
    SAFEN = Off                                                 ' Storage Area Flash disabled
    WRTAPP = Off                                                ' Application Block not write protected
    WRTB = Off                                                  ' Boot Block not write protected
    WRTC = Off                                                  ' Configuration registers (300000-30000B) not write protected
    WRTD = Off                                                  ' Data EEPROM not write protected
    WRTSAF = Off                                                ' SAF not write protected
    LVP = Off                                                   ' Low Voltage Programming disabled. HV on MCLR/VPP must be used for programming
    Cp = Off                                                    ' PFM and Data EEPROM code protection disabled
Config_End

The "CCP_PWM_K83.inc" library, and 4 demos are attached below, and are for the PIC18FxxK83 devices. The demos change the frequencies and duty cycles of PWM channels, and also show the PWM channel operating at 1MHz. All tested with an actual PIC18F26K83 device plugged into an Amicus8 board, from EasyDriver, which makes developing and testing code, so much easier.


Ivano

Thanks Les for the clarification. I saw that the K83 has a different architecture than the K22, so the hpwm.inc file I tried to use wouldn't work on the K83.
With all these changes from Microchip, it's best to develop specific procedures. Thanks also for the attachments; I'll try them.

Fanie

I had a quick look and this is what the data sheet shows P342 of the data sheet

Quote24.1.1  FUNDAMENTAL OPERATION
 The PWM module produces a 10-bit resolution output.
 The PWM timer can be selected using the PxTSEL bits
 in the CCPTMRS1 register. The default selection for
 PWMx is T2TMR. Please note that the PWM module
 operation in the following sections is described with
 respect to T2TMR. Timer2 and T2PR set the period of
 the PWM. The PWMxDCL and PWMxDCH registers
 configure the duty cycle. The period is common to all
 PWM modules
, whereas the duty cycle is inde-
pendently controlled.

Hence, the PWM's are run at the same frequency and are not independent.
To be independent you have to have four timers each serving a PWM, this pic only have 3 timers you can use as Les showed in his code and is so indicated on P346 of the data sheet  CCPTMRS1: CCP TIMERS CONTROL REGISTER 1.

The other thing to watch out for with the 8-bit pics is the resolution, depends what you want to do with PWM.
At 8MHz and 200kHz PWM output the resolution is only 5 bits or 32 steps from OFF to ON, hence if you use this to drive a LED or other device like a ferrite transformer or a stepper motor where you expect a smooth change, these low resolutions will not do it.  With each increase in duty cycle there will be a jump.

For instance the dimable LED lights I make.  If the period is too long the LED's visibly flicker.  And from 1/1000 to 2/1000 and 3/1000 duty cycle is also quite a visible change at night.  I use 1000 for the duty cycle because that is the A/D's 10 bit resolution from a pot. 
I was going to make 50% of the pot value adjust the first 20% of the brightness, and the 2nd 50% to adjust 80% of the brightness.  This way the sensitive part gets a higher resolution while the less sensitive part is adjusted more coarsely, but i haven't had the time yet.

The reason for mentioning this is to encourage you to use the data sheet because it gives insight in what the hardware is and can do when you write your program.

Fanie

Just for fun, have a look at the PWM P342 of the PIC16F17554-55-74-75.
Who think this stuff up ?
My first thought was poor Les !

The  operating modes is Left Aligned– Right Aligned– Center-Aligned– Variable Aligned– Compare Pulsed / Toggled
Use this and you're going to sway all over and risk arrest for drunken programming.

And to top it off, they offer four two-output slices per module, I'll have to show the wife so I can get four two-output slices of tart next time she makes it.  Hopefully soon because my sugar is Low or logic 0.

Then throw in Push-pull operation, 16-bit period timer, clock sources, trigger sources for synchronous duty cycle and period changes, synchronous/asynchronous Reset sources, Reset source polarity control, PWM output polarity control, and the mentioned slices per module.

But wait !  There's more !  PWM pre-scalers a-hand post-scalers, and followed by a lot of diagrams and wave forms I'm sure is there only to impress to everyone.

And just when you think eh that is bad but ok, they throw in the stinger.  External Period Resets, Buffered Period and Parameter Registers, Synchronizing Multiple PWMs, Interrupts, Period Interrupt,  Period Interrupt Postscaler, Operation During Sleep (high risk as you know).

And as expected the usual political threats - to avoid unexpected behavior, less clocks before the end, then the reset will be held off until,  diagrams illustrate the two types as if you're one of them and more...


Ivano

I studied the 18f25k83 datasheet and created two independent PWMs with timer 2 and timer 4 at 50% duty. I successfully set the various registers. It's a shame the timers are 8-bit.
I was curious to try the HPWM command, but as Les said, it's now obsolete. It's worth studying the datasheet and creating the appropriate procedures; it's also fun.