Pulse Width Modulation on a PIC18F2550 – PWM1 & PWM2, XC8

OK, this is for the PIC18F2550. It should work for most, if not all of the PIC18’s, with a few minor changes – Like the TRIS outputs.

It will not work with the PIC16’s.
It will not work with the PIC12’s.
It will not work with the PIC10’s.

I’ve got an RGB LED directly connected to pins RC1 and RC2 and it’s common connected to GND.

The code… There are four code blocks.

PWM1_example.c: uses PWM1 and RC2
PWM2_example.c: uses PWM2 and RC1
PWM2&PWM2_example.c: duh.
Configurationbits.h: Yeah, you need this…

Using PWM1:

/*
 * File:   pwm1_example.c
 * Author: Dave
 * pic18f2550
 * Created on November 1, 2017, 12:51 AM
 */
//Internal oscillator frequency for __delay_Xs()
#define _XTAL_FREQ  (4000000UL)

#include "configurationbits.h"
#include 

void PWM1_Setup(unsigned char period)
{
    //#1
    //Set PWM period
    //NOTE: PR2 is defined as an unsigned char, which is 0x00-0xFF. Some examples out there say 0x280 is a valid value. Well, it AIN'T.
    PR2 = period;           

    //#2
    //PWM Duty Cycle (0xCF)
    //Duty cycle is set in PWM_DutyCycle()
    
    //#3
    //Set RC2 as output.
    TRISCbits.RC2 = 0;
    
    //#4
    //Set TMR2 pre-scale. Turn timer on.
    //0x00 = Prescaler is 1
    //0x01 = Prescaler is 4
    //0x1x = Prescaler is 16
    T2CONbits.T2CKPS1 = 0;
    T2CONbits.T2CKPS0 = 0;
    T2CONbits.TMR2ON = 1;
    
    //#5
    //Configure the CCPx module for PWM operation
    //0xC = PWM mode: P1A, P1C active-high; P1B, P1D active-high
    //0xD = PWM mode: P1A, P1C active-high; P1B, P1D active-low
    //0xE = PWM mode: P1A, P1C active-low; P1B, P1D active-high
    //0xF = PWM mode: P1A, P1C active-low; P1B, P1D active-low
    CCP1CONbits.CCP1M = 0xC;
}
 
void PWM1_DutyCycle(unsigned long dutyCycle, unsigned long maxDutyCycle)
{
    //Eight MSbs of the 10-bit PWM duty cycle.
    CCPR1L = dutyCycle;
    //Two LSbs of the 10-bit PWM duty cycle. 
    CCP1CONbits.DC1B = dutyCycle>>8;
}
    
void main()
{
    unsigned long dutyCycle;
    unsigned long minDutyCycle = 0;
    unsigned long maxDutyCycle = 256;
    
    //The higher the number, the longer the off state
    PWM1_Setup(254);
    
    while(1)
    {
        for(dutyCycle = minDutyCycle; dutyCycle < maxDutyCycle; dutyCycle++) { PWM1_DutyCycle(dutyCycle, maxDutyCycle); __delay_us(200); } for(dutyCycle = maxDutyCycle-1; dutyCycle > minDutyCycle; dutyCycle--)
        {
            PWM1_DutyCycle(dutyCycle, maxDutyCycle); 
            __delay_us(250);
        }
    }
}

Using PWM2

/*
 * File:   pwm2_example.c
 * Author: Dave
 * pic18f2550
 * Created on November 1, 2017, 12:51 AM
 */
//Internal oscillator frequency for __delay_Xs()
#define _XTAL_FREQ  (4000000UL)

#include "configurationbits.h"
#include 

void PWM2_Setup(unsigned char period)
{
    //#1
    //Set PWM period
    //NOTE: PR2 is defined as an unsigned char, which is 0x00-0xFF. Some examples out there say 0x280 is a valid value. Well, it AIN'T.
    PR2 = period;           

    //#2
    //PWM Duty Cycle (0xCF)
    //Duty cycle is set in PWM_DutyCycle()
    
    //#3
    //Set RC1 as output.
    TRISCbits.RC1 = 0;
    
    //#4
    //Set TMR2 pre-scale. Turn timer on.
    //0x00 = Prescaler is 1
    //0x01 = Prescaler is 4
    //0x1x = Prescaler is 16
    T2CONbits.T2CKPS1 = 0;
    T2CONbits.T2CKPS0 = 0;
    T2CONbits.TMR2ON = 1;
    
    //#5
    //Configure the CCPx module for PWM operation
    //0xC = PWM mode: P1A, P1C active-high; P1B, P1D active-high
    //0xD = PWM mode: P1A, P1C active-high; P1B, P1D active-low
    //0xE = PWM mode: P1A, P1C active-low; P1B, P1D active-high
    //0xF = PWM mode: P1A, P1C active-low; P1B, P1D active-low
    CCP2CONbits.CCP2M = 0xC;
}
 
void PWM2_DutyCycle(unsigned int dutyCycle, unsigned int maxDutyCycle)
{
    //Eight MSbs of the 10-bit PWM duty cycle.
    CCPR2L = dutyCycle;
    //Two LSbs of the 10-bit PWM duty cycle. 
    CCP2CONbits.DC2B = dutyCycle>>8;
}
    
void main()
{
    unsigned long dutyCycle;
    unsigned long minDutyCycle = 0;
    unsigned long maxDutyCycle = 256;
    
    //The higher the number, the longer the off state
    PWM2_Setup(254);
    
    while(1)
    {
        for(dutyCycle = minDutyCycle; dutyCycle < maxDutyCycle; dutyCycle++) { PWM2_DutyCycle(dutyCycle, maxDutyCycle); __delay_us(200); } for(dutyCycle = maxDutyCycle-1; dutyCycle > minDutyCycle; dutyCycle--)
        {
            PWM2_DutyCycle(dutyCycle, maxDutyCycle); 
            __delay_us(250);
        }
    }
}

Using PWM1 & PWM2 at the same time

/*
 * File:   pwm1&2_example.c
 * Author: Dave
 * pic18f2550
 * Created on November 1, 2017, 12:51 AM
 */

//Internal oscillator frequency for __delay_Xs()
#define _XTAL_FREQ  (4000000UL)

#include "configurationbits.h"
#include 

void PWM1_PWM2_Setup(unsigned char period)
{
    //#1
    //Set PWM period
    //NOTE: PR2 is defined as an unsigned char, which is 0x00-0xFF. Some examples out there say 0x280 is a valid value. Well, it AIN'T.
    PR2 = period;           

    //#2
    //PWM Duty Cycle (0xCF)
    //Duty cycle is set in PWM_DutyCycle()
    
    //#3
    //Set RC1 & RC2 as output.
    TRISCbits.RC1 = 0;
    TRISCbits.RC2 = 0;
    
    //#4
    //Set TMR2 pre-scale. Turn timer on.
    //0x00 = Prescaler is 1
    //0x01 = Prescaler is 4
    //0x1x = Prescaler is 16
    T2CONbits.T2CKPS1 = 0;
    T2CONbits.T2CKPS0 = 0;
    T2CONbits.TMR2ON = 1;
    
    //#5
    //Configure the CCPx module for PWM operation
    //0xC = PWM mode: P1A, P1C active-high; P1B, P1D active-high
    //0xD = PWM mode: P1A, P1C active-high; P1B, P1D active-low
    //0xE = PWM mode: P1A, P1C active-low; P1B, P1D active-high
    //0xF = PWM mode: P1A, P1C active-low; P1B, P1D active-low
    CCP1CONbits.CCP1M = 0xC;
    CCP2CONbits.CCP2M = 0xC;
}
 
void PWM1_DutyCycle(unsigned long dutyCycle, unsigned long maxDutyCycle)
{
    //Eight MSbs of the 10-bit PWM duty cycle.
    CCPR1L = dutyCycle;
    //Two LSbs of the 10-bit PWM duty cycle. 
    CCP1CONbits.DC1B = dutyCycle>>8;
}
   
void PWM2_DutyCycle(unsigned long dutyCycle, unsigned long maxDutyCycle)
{
    //Eight MSbs of the 10-bit PWM duty cycle.
    CCPR2L = dutyCycle;
    //Two LSbs of the 10-bit PWM duty cycle. 
    CCP2CONbits.DC2B = dutyCycle>>8;
}

void main()
{
    unsigned long dutyCycle;
    unsigned long minDutyCycle = 0;
    unsigned long maxDutyCycle = 256;
    unsigned long dutyCycle2;
    unsigned long minDutyCycle2 = 0;
    unsigned long maxDutyCycle2 = 256;
    
    //The higher the number, the longer the off state
    PWM1_PWM2_Setup(254);
    
    while(1)
    {
        for(dutyCycle = minDutyCycle,dutyCycle2 = maxDutyCycle2-1; dutyCycle < maxDutyCycle,dutyCycle2 > minDutyCycle2; dutyCycle++,dutyCycle2--)
        {
            PWM1_DutyCycle(dutyCycle, maxDutyCycle);
            PWM2_DutyCycle(dutyCycle2, maxDutyCycle2);
            __delay_us(200);
        }
        
        for(dutyCycle = maxDutyCycle-1,dutyCycle2 = minDutyCycle2; dutyCycle > minDutyCycle,dutyCycle2 < maxDutyCycle2; dutyCycle--,dutyCycle2++)
        {
            PWM1_DutyCycle(dutyCycle, maxDutyCycle); 
            PWM2_DutyCycle(dutyCycle2, maxDutyCycle2);
            __delay_us(250);
        }
    }
}

Configuration.h

/* 
 * File:   configurationbits.h
 * Author: Dave, of course
 * Comments:
 * Revision history: 
 */

// PIC18F2550 Configuration Bit Settings

// CONFIG1L
#pragma config PLLDIV = 1       // PLL Prescaler Selection bits (No prescale (4 MHz oscillator input drives PLL directly))
#pragma config CPUDIV = OSC4_PLL6// System Clock Postscaler Selection bits ([Primary Oscillator Src: /4][96 MHz PLL Src: /6])
#pragma config USBDIV = 1       // USB Clock Selection bit (used in Full-Speed USB mode only; UCFG:FSEN = 1) (USB clock source comes directly from the primary oscillator block with no postscale)

// CONFIG1H
#pragma config FOSC = INTOSCIO_EC// Oscillator Selection bits (Internal oscillator, port function on RA6, EC used by USB (INTIO))
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor disabled)
#pragma config IESO = OFF       // Internal/External Oscillator Switchover bit (Oscillator Switchover mode disabled)

// CONFIG2L
#pragma config PWRT = OFF       // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOR = OFF        // Brown-out Reset Enable bits (Brown-out Reset disabled in hardware and software)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (Minimum setting 2.05V)
#pragma config VREGEN = OFF     // USB Voltage Regulator Enable bit (USB voltage regulator disabled)

// CONFIG2H
#pragma config WDT = OFF        // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
#pragma config WDTPS = 32768    // Watchdog Timer Postscale Select bits (1:32768)

// CONFIG3H
#pragma config CCP2MX = ON      // CCP2 MUX bit (CCP2 input/output is multiplexed with RC1)
#pragma config PBADEN = OFF     // PORTB A/D Enable bit (PORTB<4:0> pins are configured as digital I/O on Reset)
#pragma config LPT1OSC = OFF    // Low-Power Timer 1 Oscillator Enable bit (Timer1 configured for higher power operation)
#pragma config MCLRE = ON       // MCLR Pin Enable bit (MCLR pin enabled; RE3 input pin disabled)

// CONFIG4L
#pragma config STVREN = OFF     // Stack Full/Underflow Reset Enable bit (Stack full/underflow will not cause Reset)
#pragma config LVP = OFF        // Single-Supply ICSP Enable bit (Single-Supply ICSP disabled)
#pragma config XINST = OFF      // Extended Instruction Set Enable bit (Instruction set extension and Indexed Addressing mode disabled (Legacy mode))

// CONFIG5L
#pragma config CP0 = OFF        // Code Protection bit (Block 0 (000800-001FFFh) is not code-protected)
#pragma config CP1 = OFF        // Code Protection bit (Block 1 (002000-003FFFh) is not code-protected)
#pragma config CP2 = OFF        // Code Protection bit (Block 2 (004000-005FFFh) is not code-protected)
#pragma config CP3 = OFF        // Code Protection bit (Block 3 (006000-007FFFh) is not code-protected)

// CONFIG5H
#pragma config CPB = OFF        // Boot Block Code Protection bit (Boot block (000000-0007FFh) is not code-protected)
#pragma config CPD = OFF        // Data EEPROM Code Protection bit (Data EEPROM is not code-protected)

// CONFIG6L
#pragma config WRT0 = OFF       // Write Protection bit (Block 0 (000800-001FFFh) is not write-protected)
#pragma config WRT1 = OFF       // Write Protection bit (Block 1 (002000-003FFFh) is not write-protected)
#pragma config WRT2 = OFF       // Write Protection bit (Block 2 (004000-005FFFh) is not write-protected)
#pragma config WRT3 = OFF       // Write Protection bit (Block 3 (006000-007FFFh) is not write-protected)

// CONFIG6H
#pragma config WRTC = OFF       // Configuration Register Write Protection bit (Configuration registers (300000-3000FFh) are not write-protected)
#pragma config WRTB = OFF       // Boot Block Write Protection bit (Boot block (000000-0007FFh) is not write-protected)
#pragma config WRTD = OFF       // Data EEPROM Write Protection bit (Data EEPROM is not write-protected)

// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protection bit (Block 0 (000800-001FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR1 = OFF      // Table Read Protection bit (Block 1 (002000-003FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR2 = OFF      // Table Read Protection bit (Block 2 (004000-005FFFh) is not protected from table reads executed in other blocks)
#pragma config EBTR3 = OFF      // Table Read Protection bit (Block 3 (006000-007FFFh) is not protected from table reads executed in other blocks)

// CONFIG7H
#pragma config EBTRB = OFF      // Boot Block Table Read Protection bit (Boot block (000000-0007FFh) is not protected from table reads executed in other blocks)

Leave a Reply