3.20.3 16-bit PWM with Compare Mode
16-bit Pulse-Width Modulator with Compare Mode
3.20.3.1 Introduction
This module is a 16-bit Pulse-Width Modulator (PWM) with a compare feature and multiple outputs. The outputs are grouped in slices, where each slice has two outputs. There can be up to four slices in each PWM module.
In the below Tech Brief, some of the same use cases are build up from scratch, using bare metal coding, as well as MCC.
- Related Technical Brief: TB3270: Getting Started with PWM Using CCP on PIC18 (pdf)
3.20.3.2 Supported Device Families
PIC16F171xx | PIC16F181xx | PIC18F-Q20 |
PIC18F-Q40 | PIC18F-Q41 | PIC18F-Q43 |
PIC18F-Q71 | PIC18F-Q83 | PIC18F-Q84 |
3.20.3.3 Required Header Files
#include "mcc_generated_files/pwm/pwm[x]_16bit.h"
3.20.3.4 How to Use the PWMx_16BIT PLIB Driver
The links below provide examples for different use cases of the PWMx_16BIT Peripheral Library (PLIB) driver.
For general instructions common to all examples, refer to this section: PIC PWM Use Case Code Snippet Instructions
- PIC PWM use-case: Signal with 25% duty cycle: This example shows how to use the PWM module to generate an 1Hz signal with a 25% duty cycle.
- PIC PWM use-case: Duty Cycle Update on Timer Callback: This example shows how to use the PWM module to generate an 1Hz signal with a variable duty cycle. There are 5 duty cycles that the application cycles through every 10 seconds.
- PIC PWM use-case: Duty Cycle Update on Button Press: This example shows how to use the PWM module to cycle through different preset duty cycles, when a switch is pressed. If a LED pin is chosen as the PWM output pin, then the LED brightness will change as the button is pressed.
- PIC PWM use-case: Gradually Brightening LED: This example shows how to scaling the percentage brighness of PWM LED, based on current value, max value, e.g., to match a PWM LED to the range of a sensor, or an ADC connected to a potentiometer. Here a timer callback is used to update the PWM_DutyCyclePercentage. The top period count of the PWM is specified, to make the code more readable.
3.20.3.5 Module Documentation
3.20.3.5.1 PWM1_16BIT
This file contains the API prototypes for the PWM1_16BIT driver.
Module description
This file contains the API prototypes for the PWM1_16BIT driver.
Functions
void PWM1_16BIT_Initialize (void)
Initializes the PWM1_16BIT module. This API must be called before any other PWM1_16BIT routine is called.
void PWM1_16BIT_Enable ()
Enables the PWM1_16BIT module.
void PWM1_16BIT_Disable ()
Disables the PWM1_16BIT module.
void PWM1_16BIT_WritePeriodRegister (uint16_t periodCount)
Configures the total PWM1_16BIT period. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output1DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output2DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_LoadBufferRegisters (void)
Reloads the period or duty cycle registers on the next period event.
void PWM1_16BIT_PWMI_ISR (void)
Interrupt handler for PWM1_16BIT parameter interrupt events.
void PWM1_16BIT_PWMPI_ISR (void)
Interrupt handler for PWM1_16BIT period interrupt events.
void PWM1_16BIT_Slice1Output1_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 1 interrupt event.
void PWM1_16BIT_Slice1Output2_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 2 interrupt event.
void PWM1_16BIT_Period_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the period interrupt event.
Function Documentation
PWM1_16BIT_Disable()
void PWM1_16BIT_Disable ( )
Disables the PWM1_16BIT module.
|
None. In case the user wants to reinitialize the PWM1_16BIT, this function must be called before PWM1_16BIT_Initialize(). |
PWM1_16BIT_Enable()
void PWM1_16BIT_Enable ( )
Enables the PWM1_16BIT module.
|
None. |
PWM1_16BIT_Initialize()
void PWM1_16BIT_Initialize (void )
Initializes the PWM1_16BIT module. This API must be called before any other PWM1_16BIT routine is called.
PWM1_16BIT_LoadBufferRegisters()
void PWM1_16BIT_LoadBufferRegisters (void )
Reloads the period or duty cycle registers on the next period event.
|
None. This routine must be called after calling PWM1_16BIT_WritePeriodRegister, PWM1_16BIT_SetSlice1Output1DutyCycleRegister and PWM1_16BIT_SetSlice1Output2DutyCycleRegister. |
PWM1_16BIT_Period_SetInterruptHandler()
void PWM1_16BIT_Period_SetInterruptHandler (void(*)(void) InterruptHandler)
Registers a callback function to be called for the period interrupt event.
|
None. |
PWM1_16BIT_PWMI_ISR()
void PWM1_16BIT_PWMI_ISR (void )
Interrupt handler for PWM1_16BIT parameter interrupt events.
|
None. |
PWM1_16BIT_PWMPI_ISR()
void PWM1_16BIT_PWMPI_ISR (void )
Interrupt handler for PWM1_16BIT period interrupt events.
|
None. |
PWM1_16BIT_SetSlice1Output1DutyCycleRegister()
void PWM1_16BIT_SetSlice1Output1DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
|
None. |
PWM1_16BIT_SetSlice1Output2DutyCycleRegister()
void PWM1_16BIT_SetSlice1Output2DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
|
None. |
PWM1_16BIT_Slice1Output1_SetInterruptHandler()
void PWM1_16BIT_Slice1Output1_SetInterruptHandler (void(*)(void) InterruptHandler)
Registers a callback function to be called for the slice 1, parameter 1 interrupt event.
|
None. |
PWM1_16BIT_Slice1Output2_SetInterruptHandler()
void PWM1_16BIT_Slice1Output2_SetInterruptHandler (void(*)(void) InterruptHandler)
Registers a callback function to be called for the slice 1, parameter 2 interrupt event.
|
None. |
PWM1_16BIT_WritePeriodRegister()
void PWM1_16BIT_WritePeriodRegister (uint16_t periodCount)
Configures the total PWM1_16BIT period. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
|
None. |
3.20.3.6 File Documentation
3.20.3.6.1 source/pwm1_16bit.c File Reference
This file contains the API implementation for the PWM1_16BIT module.
#include <xc.h> #include "../pwm1_16bit.h"
Functions
static void PWM1_16BIT_Slice1Output1_DefaultInterruptHandler (void)
static void PWM1_16BIT_Slice1Output2_DefaultInterruptHandler (void)
static void PWM1_16BIT_Period_DefaultInterruptHandler (void)
void PWM1_16BIT_Initialize (void)
Initializes the PWM1_16BIT module. This API must be called before any other PWM1_16BIT routine is called.
void PWM1_16BIT_Enable ()
Enables the PWM1_16BIT module.
void PWM1_16BIT_Disable ()
Disables the PWM1_16BIT module.
void PWM1_16BIT_WritePeriodRegister (uint16_t periodCount)
Configures the total PWM1_16BIT period. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output1DutyCycleRegister (uint16_t registerValue)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output2DutyCycleRegister (uint16_t registerValue)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_LoadBufferRegisters (void)
Reloads the period or duty cycle registers on the next period event.
void PWM1_16BIT_PWMI_ISR (void)
Interrupt handler for PWM1_16BIT parameter interrupt events.
void PWM1_16BIT_PWMPI_ISR (void)
Interrupt handler for PWM1_16BIT period interrupt events.
void PWM1_16BIT_Slice1Output1_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 1 interrupt event.
void PWM1_16BIT_Slice1Output2_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 2 interrupt event.
void PWM1_16BIT_Period_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the period interrupt event.
Variables
static void(* PWM1_16BIT_Slice1Output1_InterruptHandler )(void)
static void(* PWM1_16BIT_Slice1Output2_InterruptHandler )(void)
static void(* PWM1_16BIT_Period_InterruptHandler )(void)
Detailed Description
This file contains the API implementation for the PWM1_16BIT module.
PWM1_16BIT Generated Driver File.
version PWM1_16BIT Driver Version 1.0.1
Function Documentation
PWM1_16BIT_Period_DefaultInterruptHandler()
static void PWM1_16BIT_Period_DefaultInterruptHandler (void )[static]
PWM1_16BIT_Slice1Output1_DefaultInterruptHandler()
static void PWM1_16BIT_Slice1Output1_DefaultInterruptHandler (void )[static]
PWM1_16BIT_Slice1Output2_DefaultInterruptHandler()
static void PWM1_16BIT_Slice1Output2_DefaultInterruptHandler (void )[static]
Variable Documentation
PWM1_16BIT_Period_InterruptHandler
void(* PWM1_16BIT_Period_InterruptHandler) (void)[static]
PWM1_16BIT_Slice1Output1_InterruptHandler
void(* PWM1_16BIT_Slice1Output1_InterruptHandler) (void)[static]
PWM1_16BIT_Slice1Output2_InterruptHandler
void(* PWM1_16BIT_Slice1Output2_InterruptHandler) (void)[static]
3.20.3.6.2 source/pwm1_16bit.h File Reference
#include <stdint.h> #include <stdbool.h>
Functions
void PWM1_16BIT_Initialize (void)
Initializes the PWM1_16BIT module. This API must be called before any other PWM1_16BIT routine is called.
void PWM1_16BIT_Enable ()
Enables the PWM1_16BIT module.
void PWM1_16BIT_Disable ()
Disables the PWM1_16BIT module.
void PWM1_16BIT_WritePeriodRegister (uint16_t periodCount)
Configures the total PWM1_16BIT period. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output1DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_SetSlice1Output2DutyCycleRegister (uint16_t value)
Sets the active period or duty cycle of the slice 1, parameter 1 output. PWM1_16BIT_LoadBufferRegisters() must be called after this routine.
void PWM1_16BIT_LoadBufferRegisters (void)
Reloads the period or duty cycle registers on the next period event.
void PWM1_16BIT_PWMI_ISR (void)
Interrupt handler for PWM1_16BIT parameter interrupt events.
void PWM1_16BIT_PWMPI_ISR (void)
Interrupt handler for PWM1_16BIT period interrupt events.
void PWM1_16BIT_Slice1Output1_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 1 interrupt event.
void PWM1_16BIT_Slice1Output2_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the slice 1, parameter 2 interrupt event.
void PWM1_16BIT_Period_SetInterruptHandler (void(*InterruptHandler)(void))
Registers a callback function to be called for the period interrupt event.
Detailed Description
PWM1_16BIT Generated Driver API Header File.
3.20.3.7 Module Documentation
3.20.3.7.1 PWMx_16BIT Use Cases
PIC PWM Use Case Code Snippet Instructions
The use cases show example uses of the PWM PLIB Driver, within a MCC Melody project:
-
Add PWMx Component to the project
-
Configure:
-
PWMx_16BIT component as described in the example.
-
Any other components or pins needed for the use case.
-
-
Generate the code
-
Add the code snippet(s) to the application code
-
Program the board
PIC PWM use-case: Signal with 25% duty cycle
This example shows how to use the PWM module to generate an 1Hz signal with a 25% duty cycle.
-
Add PWMx_16BIT
-
Hardware Settings:
-
Clock Source: LFINTOSC.
-
-
Slice 1 Output Settings
-
Requested Frequency (kHz): 1 (Default).
-
Output 1 Duty Cycle (%): 25.
-
-
Pin Grid View:
-
Select a pin for PWM1OUT1 (this is the PWM output pin)
-
-
Pins:
-
PWM1OUT1 output pin: Uncheck Analog, name it PWM.
-
-
Connect PWM to DebugIO PIN (check schematics) using a wire, to see PWM waveform on the MPLAB Data Visualizer
#include "mcc_generated_files/system/system.h"
int main(void) { SYSTEM_Initialize(); while(1){} }
PIC PWM use-case: Duty Cycle Update on Timer Callback
This example shows how to use the PWM module to generate an 1Hz signal with a variable duty cycle. There are 5 duty cycles that the application cycles through every 10 seconds.
-
Add PWMx_16BIT
-
Add TimerX
-
Software Settings
-
Custom Name: PWM (so the user can easily switch between PWM instances)
-
-
Slice 1 Output Settings
-
Requested Frequency (kHz): 1 (default)
-
Invert Output1 Polarity: Yes (If LED is active low: Check schematics)
-
-
Hardware Settings
-
Timer Mode: 16-bit
-
Clock Source: LFINTOSC
-
Requested Period (s): 1(s)
-
-
Interrupt Settings
-
TMR Interrupt: Yes. (Timer callback used to change PWM duty cycle)
-
-
Pin Grid View:
-
Select a pin for PWM1OUT1 (On a LED pin if possible)
-
-
Pins:
-
PWM1OUT1 output pin: Uncheck Analog, name it PWM.
-
-
Connect PWM to DebugIO PIN (check schematics) using a wire, to see PWM waveform on the MPLAB Data Visualizer
#include "mcc_generated_files/system/system.h" #define NO_DUTY_CYCLES (5) /* Create a pointer of type TMR_INTERFACE and assign it to the address of the Timer0 TMR_INTERFACE struct. This enables us to access the portable API interface, ensuring it's easy to change the peripheral instance that the timer runs on. */ const struct TMR_INTERFACE *Timer = &Timer0; static uint8_t currentDutyCyclePos = 1;
/* * Enum that encompasses all the values for PWM's duty cycle register that the application cycles through. */ typedef enum DUTY_CYCLE { DUTY_CYCLE_0 = 0x0000, DUTY_CYCLE_2 = 0x0004, DUTY_CYCLE_5 = 0x000A, DUTY_CYCLE_10 = 0x0014, DUTY_CYCLE_100 = 0x00C8 } duty_cycle_t; /* * PWM duty cycles that the application cycle through. LEDs are non linear and saturate quickly, so the duty values are low. * NOTE: If LED is active low: Invert Output1 Polarity: Yes */ static duty_cycle_t const dutyCycles[NO_DUTY_CYCLES] = { DUTY_CYCLE_0, DUTY_CYCLE_2, DUTY_CYCLE_5, DUTY_CYCLE_10, DUTY_CYCLE_100 //Starts ON fully. };
/* * Wrapper made for the PWM's API to update the duty cycle. */ inline static void UpdateDutyCycle(duty_cycle_t const dutyCycle) { PWM_SetSlice1Output1DutyCycleRegister(dutyCycle); PWM_LoadBufferRegisters(); } void Timer_Callback_SwitchDutyCycle(void){ UpdateDutyCycle(dutyCycles[currentDutyCyclePos]); currentDutyCyclePos = (currentDutyCyclePos + 1) % NO_DUTY_CYCLES; }
int main(void) { SYSTEM_Initialize(); Timer->TimeoutCallbackRegister(Timer_Callback_SwitchDutyCycle); INTERRUPT_GlobalInterruptEnable(); while(1){} }
PIC PWM use-case: Duty Cycle Update on Button Press
This example shows how to use the PWM module to cycle through different preset duty cycles, when a switch is pressed. If a LED pin is chosen as the PWM output pin, then the LED brightness will change as the button is pressed.
-
Add PWM
-
Software Settings
-
Custom Name: PWM (So can easily change PWM hardware instance use case runs on)
-
-
Slice 1 Output Settings
-
Invert Output1 Polarity: Yes (If LED is active low: Check schematics)
-
-
Pin Grid View:
-
Select a pin for PWM1OUT1 (Select a LED pin if possible)
-
Select the input pin for GPIO for BUTTON (check schematic)
-
-
Pins:
-
PWM out pin: Custom name "PWM", uncheck Analog
-
BUTTON pin: Custom name "BUTTON",
-
Enable Weak Pullup if needed (check schematic)
-
Interrupt on Change: Sense negative/positive on BUTTON press (check schematic)
-
-
Connect PWM_OUT to LED using a wire to be able to see the result on the LED (if PWM pin is different from LED pin)
#include "mcc_generated_files/system/system.h" #define NO_DUTY_CYCLES (5) static uint8_t currentDutyCyclePos = 1; volatile bool CHANGE_DUTYCYCLE = false;
/* * Enum that encompasses all the values for PWM's duty cycle register that the application cycles through. */ typedef enum DUTY_CYCLE { DUTY_CYCLE_0 = 0x0000, DUTY_CYCLE_2 = 0x0004, DUTY_CYCLE_5 = 0x000A, DUTY_CYCLE_10 = 0x0014, DUTY_CYCLE_100 = 0x00C8 } duty_cycle_t; /* * PWM duty cycles that the application cycle through. LEDs are non linear and saturate quickly, so the duty values are low. * NOTE: If LED is active low: Invert Output1 Polarity: Yes */ static duty_cycle_t const dutyCycles[NO_DUTY_CYCLES] = { DUTY_CYCLE_0, DUTY_CYCLE_2, DUTY_CYCLE_5, DUTY_CYCLE_10, DUTY_CYCLE_100 //Starts ON fully. };
/* * Wrapper made for the PWM's API to update the duty cycle. */ inline static void UpdateDutyCycle(duty_cycle_t const dutyCycle) { PWM_SetSlice1Output1DutyCycleRegister(dutyCycle); PWM_LoadBufferRegisters(); } void BUTTON_Callback_SwitchDutyCycle(void) { INTERRUPT_GlobalInterruptDisable(); UpdateDutyCycle(dutyCycles[currentDutyCyclePos]); currentDutyCyclePos = (currentDutyCyclePos + 1) % NO_DUTY_CYCLES; CHANGE_DUTYCYCLE = true; }
int main(void) { SYSTEM_Initialize(); RC0_SetInterruptHandler(BUTTON_Callback_SwitchDutyCycle); //Change RC0 to pin connected to BUTTON INTERRUPT_GlobalInterruptEnable(); while(1){ if(CHANGE_DUTYCYCLE) { CHANGE_DUTYCYCLE = false; __delay_ms(50); //Debounce INTERRUPT_GlobalInterruptEnable(); } } }
PIC PWM use-case: Gradually Brightening LED
This example shows how to scaling the percentage brighness of PWM LED, based on current value, max value, e.g., to match a PWM LED to the range of a sensor, or an ADC connected to a potentiometer. Here a timer callback is used to update the PWM_DutyCyclePercentage. The top period count of the PWM is specified, to make the code more readable.
-
Add PWMx_16BIT
-
Add TimerX
-
Software Settings
-
Custom Name: PWM (so the user can easily switch between PWM instances)
-
-
Slice 1 Output Settings
-
Requested Frequency (kHz): 1 (default)
-
-
Device Resources: Add TIMER from Drivers (Select any timer).
-
Hardware Settings:
-
So that Requested Period (s) ≤ Max Period, modify Clock Prescaler/Postscaler/Clock Source
-
Requested Period (s): 0.02 (20 ms)
-
-
Interrupt Settings
-
TMR Interrupt: Yes. (Timer callback used to change PWM duty cycle)
-
-
Pin Grid View:
-
Select a pin for PWM1OUT1 (On a LED pin if possible)
-
-
Pins:
-
PWM1OUT1 output pin: Uncheck Analog, name it IO_PWM.
-
-
Connect PWM_OUT to LED using a wire to be able to see the result on the LED (if PWM pin is different from LED pin)
#include "mcc_generated_files/system/system.h" static void Timer_Callback_20ms(void); static void PWM_DutyCycleSetPercentage_Slice1(uint16_t, uint16_t, uint16_t); static const struct TMR_INTERFACE *Timer = &TimerX; // TODO: Replace Timer0 with name of const struct TMR_INTERFACE, from MCC Generated Files > timer > tmrx.c #define MAXIMUM_INPUT_VALUE 100 // TODO: Replace with the desired maximum value, e.g. a 16 bit ADC connected to a potentiometer could be 4100) static uint16_t Count = 0; static uint16_t periodCountTop = 100;
void PWM_DutyCycleSetPercentage_Slice1(uint16_t current, uint16_t max, uint16_t periodCountTop) { uint16_t PWM_DytyCyclePercentage = (1.0f - ((double) current / (double) max)) * periodCountTop; PWM_SetSlice1Output1DutyCycleRegister(PWM_DytyCyclePercentage); PWM_LoadBufferRegisters(); } static void Timer_Callback_20ms(void) { /* If used with an analog sensor or a simple POT, this function could be called when ADC result available, replacing Count with adcResult, and updating MAXIMUM_INPUT_VALUE*/ PWM_DutyCycleSetPercentage_Slice1(Count, MAXIMUM_INPUT_VALUE, periodCountTop); IO_Debug_Toggle(); if (Count < MAXIMUM_INPUT_VALUE) { Count = Count++; } else { Count = 0; } }
int main(void) { SYSTEM_Initialize(); Timer->TimeoutCallbackRegister(Timer_Callback_20ms); PWM_WritePeriodRegister(periodCountTop); //Set number of counts for PWM period, set here to make code more readable. INTERRUPT_GlobalInterruptEnable(); while (1) { } }