Led brightness control using PWM in PIC16F877A microcontroller

In: Project Code: PE000006

In this project we will use pulse width modulation to control the brightness of led connected to a PIC16F877A microcontroller. If you are new to pulse width modulation technique go through article before executing this project. If you are new to PIC microcontroller programming we suggest you to execute the basic led blinking project before trying this project. If you don't have access to your own hardware for this project, you can and execute it remotely.

course thumb

Project Content

Required hardware and software

The required hardware and software for this project is as below:

  • A breadboard
  • 1 x 4mmLED
  • 1 x resistor between 220 Ohm to 1 kOhm
  • Hookup cables
  • Development board hosting PIC16F877A 8-bit microcontroller
  • MPLAB IDE(v5.05) from Microchip
  • PICKIT3 Programmer, connecting cable from PICKIT3 to your computer.
  • Power supply for the development board

  • Project Connections

    Led has two leads. The longer lead is +ve and shorter lead is -ve. Connect PORTC PIN 2 of the microcontroller to one end of the resistor and other end of the resistor to +ve end of the led. Connect the -ve end of the led to the ground on the development board.

    PIC16F877A PWM channels description

    PIC16F877A microcontroller has two CCP(Capture/Compare/PWM) modules, such as CCP1 and CCP2. Each Capture/Compare/PWM (CCP) module contains a 16-bit register which can operate as a:

    We will be using CCP1 as PWM Master register in this project. PWM Master register is a 10-bit register and hence can have values between 0 to 1023 only. When CCP mode is configured as PWM it will use timer2 of PIC microcontroller. The 8-Msb bits are stored in CCPRxL and remaining 2-bits in CCPxCON register.

    PWM channels detailed description

    PWM channel PORT output Pins Control Register Duty Cycle Register Period Register

    Prepare source code, building program source code, creating binary

    Prepare source code
     * File:   pwm.c
     * IDE: MPLAB X 5.05
     * Programmer: PICKIT3
     * Compiler: XC8
    #include "xc.h"
    #define _XTAL_FREQ 11059200  //Device Crystal Frequency
    #pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
    #pragma config WDTE = OFF        // Watchdog Timer Enable bit (WDT enabled)
    #pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
    #pragma config CP = OFF         // FLASH Program Memory Code Protection bits (Code protection off)
    #pragma config BOREN = ON       // Brown-out Reset Enable bit (BOR enabled)
    #pragma config LVP = OFF        // Low Voltage In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
    #pragma config CPD = OFF        // Data EE Memory Code Protection (Code Protection off)
    #pragma config WRT = OFF        // FLASH Program Memory Write Enable (Unprotected program memory may not be written to by EECON control)
    #define TMR2PRESCALE 4
    long PWM_freq = 5000;
    void PWM_Initialize();
    void msDelay(unsigned int x);
    void PWM_Duty(unsigned int duty);
    void main()
    void PWM_Initialize()
        PR2 = (_XTAL_FREQ/(PWM_freq*4*TMR2PRESCALE)) - 1; //Setting the PR2 formulae using Datasheet // Makes the PWM work in 5KHZ
        CCP1M3 = 1; 
        CCP1M2 = 1;  //Configure the CCP1 module 
        T2CKPS0 = 1;
        T2CKPS1 = 0; 
        TMR2ON = 1; //Configure the Timer module
        TRISC2 = 0; // make port C pin 2 on C as output
    void PWM_Duty(unsigned int duty)
      if(duty < 1023)
        duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_freq*TMR2PRESCALE)); // On reducing //duty = (((float)duty/1023)*(1/PWM_freq)) / ((1/_XTAL_FREQ) * TMR2PRESCALE);
        CCP1X = duty & 1; //Store the oth bit
        CCP1Y = duty & 2; //Store the 1th bit
        CCPR1L = duty>>2;// Store the remaining 8 bit
    void msDelay(unsigned int x)
        unsigned int i;
        unsigned char j;
        for(i = 0; i < x; i++)
            for(j = 0; j < 165; j++);

    Let me explain about the above source code developed for this project. In the PWM_Initilize function we used PWM frequency to 5kHz. We configured the CCP1 module first. Then we used an 8-bit timer Timer2 which has a prescaler and a postscaler. The Timer2 module has an 8-bit period register, PR2. Timer2 increments from 00h until it matches PR2 and then resets to 00h on the next increment cycle. PR2 is a readable and writable register. It can be used as the PWPR2 is a Timer2 Period Register. We configured port C pin 2 as output by setting TRISC2 to 0.

    PWM_Duty function is used to set the right duty cycle as per user given data. The LSB 2bits of the duty cycle provided by the user are configured on CCP1X and CCP1Y. The remaining 8bits are set on the CCP1L register.

    Follow the procedure as described in to load the .hex file in the microcontroller. Once programmed you should observe that led at PORTC pin 2 is set with higher brightness. In this case the duty cycle is set to around 90%. Now change the pwm duty cycle from PWM_Duty(900) to PWM_Duty(100), then compile the program again and load it on the microcontroller. You should observe that the PORTC pin2 is set with lower brightness. In this case the pwm duty cycle is around 10%. So pwm duty cycle 0% to 100% is mapped to values of 0 to 1023. You can change the duty cycle settings accordingly.

    Looking for a new project? Write and upload your project information .


    C Programming

    More Articles Categories

    Technical articles

    Prepare for software jobs

    Test your skills