📈 Qu'est-ce que le PWM ?

Le PWM (Pulse Width Modulation — Modulation de Largeur d'Impulsion) consiste à faire varier le rapport cyclique d’un signal rectangulaire pour contrôler la puissance moyenne délivrée à un composant.

Au lieu de fournir une tension continue (ex : 2,5V), le PIC alterne rapidement entre 0V et 5V. Avec un rapport cyclique de 50%, la charge reçoit en moyenne ~2,5V. En faisant varier ce rapport de 0% à 100%, on contrôle la luminosité d'une LED ou la vitesse d'un moteur.

Le PIC16F877A intègre des modules CCP (Capture/Compare/PWM) capables de générer un PWM matériel, sans charger inutilement le processeur.

✅ Sortie PWM sur PIC16F877A

Sur le PIC16F877A, le PWM matériel CCP1 sort généralement sur la broche RC2/CCP1. Pense à mettre la broche en sortie via TRISC et à configurer correctement Timer2.

⚙️ Configuration CCP1 en PWM (PIC16F877A)

Registres utilisés

RegistreRôle
CCP1CONMode CCP1 (PWM = 0b00001100) + 2 bits LSB du duty
PR2Période PWM (Timer2)
CCPR1LDuty cycle (8 bits MSB)
T2CONPrescaler / activation de Timer2
TRISCDirection des broches (RC2 en sortie)
ADCON1Configuration analogique / numérique (utile selon le montage)

⚠️ Point important

  • Le PWM matériel CCP1 utilise généralement RC2.
  • La broche PWM doit être configurée en sortie : TRISCbits.TRISC2 = 0;
  • Timer2 est indispensable pour générer le PWM.

📝 Exemple complet : Contrôler la luminosité d'une LED (PWM matériel CCP1)

But : sortir un PWM sur RC2/CCP1 et faire un effet “respiration” (fade in / fade out).

C (XC8) — PIC16F877A
/*
 * Projet : PWM matériel CCP1 sur PIC16F877A (RC2)
 * Auteur : JLQDeveloppement
 * Compilateur : XC8
 */

#include <xc.h>
#define _XTAL_FREQ 4000000

// (Optionnel) Ajoute tes #pragma config selon ton projet

static void initPWM_CCP1(void) {
    // Configure les broches analogiques si nécessaire
    ADCON1 = 0x06;

    // RC2 en sortie (broche CCP1)
    TRISCbits.TRISC2 = 0;

    // Période PWM via Timer2
    // Exemple : PR2 = 249, prescaler 1:4 -> ~1kHz à Fosc=4MHz
    PR2 = 249;

    // Duty initial ~50% (10 bits : 0..1023)
    CCPR1L = 125;
    CCP1CONbits.DC1B = 0;

    // Activer CCP1 en mode PWM
    CCP1CONbits.CCP1M = 0b1100;

    // Timer2 ON, prescaler 1:4
    T2CONbits.T2CKPS = 0b01;
    T2CONbits.TMR2ON = 1;

    // Petite attente pour stabiliser Timer2/PWM
    __delay_ms(10);
}

static void setDuty10bits(unsigned int duty) {
    // duty : 0..1023 (10 bits)
    if(duty > 1023) duty = 1023;

    CCPR1L = duty >> 2;
    CCP1CONbits.DC1B = duty & 0x03;
}

void main(void) {
    initPWM_CCP1();

    unsigned int i;

    while(1) {
        // Fade in
        for(i = 0; i <= 1023; i += 6) {
            setDuty10bits(i);
            __delay_ms(5);
        }

        // Fade out
        for(i = 1023; i > 0; i -= 6) {
            setDuty10bits(i);
            __delay_ms(5);
        }
    }
}

⚠️ Erreurs fréquentes

  • Oublier Timer2 : sans TMR2ON=1, pas de PWM.
  • Oublier TRISC : la broche doit être en sortie (TRISC2=0).
  • Mauvaise broche : le PWM CCP1 sort sur RC2, pas sur une broche aléatoire.
  • Fréquence mal calculée : vérifie toujours PR2 et le prescaler.

🧮 Calcul de la fréquence PWM (Timer2)

Formule (PWM via Timer2)

FPWM = Fosc / (4 × Prescaler × (PR2 + 1))

Exemple : Fosc = 4 MHz, Prescaler = 4, PR2 = 249 :

F = 4 000 000 / (4 × 4 × 250) = 1000 Hz (1 kHz)

Le rapport cyclique est sur 10 bits (0–1023), soit 1024 niveaux de réglage.

🔧 Applications courantes du PWM