The PIC16F877A can generate PWM (Pulse Width Modulation) signals using its CCP (Capture/Compare/PWM) module. The CCP module in this microcontroller is versatile, and it provides features to generate both PWM signals as well as perform capture and compare operations.
Unlike the ATmega328P, which has specific modes like Fast PWM mode, Phase Correct PWM mode, and CTC (Clear Timer on Compare Match) mode, the PIC16F877A does not categorize PWM into these exact types. Instead, it has a more straightforward approach to PWM generation. Below are the main details of its PWM mode.
PWM Features of PIC16F877A
The PIC16F877A has two CCP modules: CCP1 and CCP2, which can be used to generate PWM signals. These are controlled via the CCP1CON and CCP2CON registers respectively. Here’s a breakdown of the PWM functionality:
PWM Modes in PIC16F877A
The PWM mode in PIC16F877A is comparatively simple compared to the modes available in ATmega328P. Here are the key modes:
PWM Mode (Pulse Width Modulation):
- This is the standard PWM mode used to generate a square wave where the duty cycle is adjustable.
- The PWM frequency is determined by the Timer2 module.
- The duty cycle is determined by the CCPx module (where x is 1 or 2 for CCP1 and CCP2).
Capture Mode (Not PWM):
- This mode captures the value of the Timer1 register when a certain edge (rising/falling) is detected on the CCP pin.
Compare Mode (Not PWM):
- This mode compares the Timer1 value to a specified value and toggles the CCP pin when they match.
For generating a PWM signal, you’ll primarily be using PWM mode.
How PWM Works in PIC16F877A
In PWM mode, the CCP module automatically generates a square wave by adjusting the pulse width based on the values set in the CCP1/CCP2 registers.
- Timer2 is used to set the frequency of the PWM signal.
- The CCPR1L and CCP1CON registers are used to control the duty cycle.
- The PWM period and duty cycle are calculated based on the frequency of the microcontroller's clock.
PWM Frequency Calculation
The frequency of the PWM signal is determined by the following equation:
Where:- F_osc is the clock frequency.
- PR2 is the value loaded into the Timer2 period register.
- TMR2 Pre_Scaler is the prescaler value for Timer2 (1, 4, or 16).
The PWM duty cycle is set by configuring the CCPRxL register and the two least significant bits in the CCPxCON register. The duty cycle can be adjusted in steps from 0% to 100%.
Comparing PIC16F877A PWM with ATmega328P PWM Modes
Feature | PIC16F877A | ATmega328P |
---|---|---|
PWM Mode | Single PWM mode in CCP modules | Fast PWM, Phase Correct PWM |
Frequency Control | Timer2 and PR2 register control frequency | Different modes (CTC, Fast, etc.) |
Duty Cycle Control | CCPRxL and CCPxCON registers | OCR (Output Compare Register) |
Multiple PWM Outputs | 2 PWM outputs (CCP1, CCP2) | More PWM channels available |
Timer Used for PWM | Timer2 | Timer0, Timer1, Timer2 |
While the ATmega328P offers a variety of PWM modes like Fast PWM, Phase Correct PWM, and CTC, the PIC16F877A focuses on a simpler, single PWM mode that can be configured by controlling Timer2 for frequency and the CCP modules for duty cycle.
Example Code for PWM on PIC16F877A
Here we will generate PWM signal on the RC2 pin as shown in the following circuit diagram:
The PWM frequency and the duty cycle can be dynamically updated. Here’s a basic code example to set up PWM on the PIC16F877A:#define _XTAL_FREQ 8000000 // 8MHz Crystal Frequency
#include <xc.h>
// Function to calculate PR2 based on desired PWM frequency (in Hz)
unsigned char calculate_PR2(unsigned long pwm_freq) {
unsigned char prescaler = 4; // Set your prescaler (1, 4, or 16)
return (_XTAL_FREQ / (4 * pwm_freq * prescaler)) - 1;
}
// Function to initialize PWM with calculated PR2
void PWM_Init(unsigned long pwm_freq) {
TRISCbits.TRISC2 = 0; // Set RC2 as output (PWM pin)
// Set up Timer2 with prescaler = 4
T2CONbits.T2CKPS0 = 1; // Prescaler = 4
T2CONbits.T2CKPS1 = 0;
// Calculate and set PR2 based on user input frequency
PR2 = calculate_PR2(pwm_freq);
// Enable Timer2
T2CONbits.TMR2ON = 1;
// Set up CCP1 for PWM mode
CCP1CONbits.CCP1M3 = 1;
CCP1CONbits.CCP1M2 = 1;
}
void PWM_Duty(unsigned int duty) {
if (duty < 1023) {
unsigned int scaled_duty = ((float)duty / 1023) * (_XTAL_FREQ / (PWM_freq * 4));
CCP1X = scaled_duty & 1; // Store the least significant bit
CCP1Y = scaled_duty & 2; // Store the second bit
CCPR1L = scaled_duty >> 2; // Store the upper 8 bits
}
}
void main(void) {
unsigned long desired_frequency = 1000; // Example: 1 kHz
PWM_Init(desired_frequency); // Initialize PWM with desired frequency
PWM_Duty(512); // Set duty cycle to 50%
while (1) {
// Main loop
}
}
In this example:
- Timer2 is used to generate a PWM frequency.
- CCP1 is set to PWM mode, and the duty cycle is set to 50% (you can modify this by changing CCPR1L and DC1B).
- To learn how to determine the PR2 value, see How to Calculate the PR2 register value in PIC16F877A.
Following is block diagram of how PWM is generated by the PIC microcontroller:
Conclusion
The PIC16F877A supports PWM generation using its CCP modules. While it doesn’t offer modes like Fast PWM or Phase Correct PWM found in the ATmega328P, it provides a simple and effective way to generate PWM signals with full control over the duty cycle and frequency using Timer2 and the CCP module.
This makes the PIC16F877A a powerful choice for projects involving motor control, LED dimming, or any application requiring pulse-width modulation. With two independent CCP modules, you can generate two separate PWM signals, making the PIC16F877A quite versatile.
Further Reading: