PWM krmiljenje LED diode, DC motorčka in prikaz na LCD-ju

V IZDELAVI!

S pomočjo potenciometra bomo spreminjali PWM (pulzno širinsko muduliran signal), in z njim krmilili svetilnost LED diode. Pri enaki frekvenci PWM signala bomo spreminjali duty cycle in s tem povprečno enosmerno napetost na pinu, kamor je priključena LED dioda. Na LCD displayu se naj izpisuje povprečna enosmerna napetost PWM signala v voltih in duty cycle v procentih.

Ker mikrokontroler PIC16f628a ne vsebuje analogno digitalnega pretvornika (ADC), bomo uporabili mikrokontroler PIC16f877a (datasheet v prilogi). ADC modul bomo uporabili zato, ker imamo na pin RA0/AN0 priključen potenciometer, s katerim spreminjamo napetost na tem pinu od Vss do Vdd. Ker bomo to analogno veličino pretvarjali v digitalno, moramo poznati delovanje ADC, ki je opisano v prejšnjem poglavju.

Za generiranje PWM signala bomo uporabili CCP modul (Capture Compare PWM => modul za zajemanje, primerjavo in generiranje PWM signala). Ločljivost PWM signala je 10-bitna, od 0 ko je duty cycle 0 % do 1024, ko je duty cycle 100 %. Mikrokontroler PIC16f877a vsebuje dva CCP modula, CCP1 in CCP2. To pomeni, da lahko generiramo dva PWM signala, enaga na pinu 17 (CCP1) in drugega na pinu 16 (CCP2). Mi bomo uporabili modul CCP1 in generirali PWM signal na pinu 17, kamor bomo priključili LED diodo. Pri tem bomo uporabili naslednje registre:

V programu bomo brali vrednost analogne napetosti od Vss (0 V) do Vdd (5 V, v mojem primeru 4,68 V, kolikor dobi vezje preko programatorja PICkit 2) na pinu 2 (RA0/AN0), ki jo bomo spreminjali s potenciometrom. Z uporabo ADC modula bomo napetost 0 V - 4,68 V pretvorili v 10-bitno vrednost 0 - 1024. Nato bomo generirali PWM signal frekvence 5 KHz in spreminjali duty cycle na pinu 17 (CCP1) v odvisnosti od vrednosti analogne napetosti na pinu 2 (RA0/AN0).

Pri nastavitvi modula CCP za generiranje PWM signala bomo:

Za prikaz ustreznih podatkov na dvovrstičnem LCD displaju bomo vključili zaglavno datoteko lcd.h, enako kot pri prejšnjih vajah, pri katerih smo uporabljali LCD display (najdemo jo tudi v prilogi).

Slika 1: Načrt priključitve elementov na mikrokontroler PIC16f877a

Program za PWM krmiljenje LED diode:

 /*

  PWM krmiljenje LED diode ter prikaz povprečne enosmerne napetosti PWM signala v voltih in duty cycle v procentih na LCD zaslonu.

  Okolje MPLAB IDE v8.92, HI_TECH compiler for PIC10/12/16 MCUs V9.82, oscilator 4 MHz.

  Avtor: Milan Ivič, feb. 2018.

*/

    #include <htc.h>

    #include <pic.h>

    #define _XTAL_FREQ 4000000            //Kristalni oscilator 4 MHz

    #define TMR2_preddelitev 4            //preddelitev časovnika TMR2 1:4

//Definiranje priključnih pinov za LCD:

    #define RS RB1

    #define EN RB3

    #define D4 RB4

    #define D5 RB5

    #define D6 RB6

    #define D7 RB7

    __CONFIG(FOSC_XT & WDTE_OFF & PWRTE_ON & CP_OFF & BOREN_ON & LVP_OFF);

    

    #include "lcd.h"                                  //Vključitev header (zaglavne) datoteke lcd.h

    unsigned int digit1, digit2, digit3;              //Spremenljivke za prikaz povprečne napetosti PWM signala na LCD displayu

    unsigned int digit4, digit5;                      //Spremenljivke za prikaz duty cicle v % na LCD displayu

    int ADC_vrednost = 0;

    int Napetost_PWM = 0;

    int Izpis_duty = 0;

    long PWM_frekvenca = 5000;

PWM_init()

{

    PR2 = (_XTAL_FREQ/(PWM_frekvenca*4*TMR2_preddelitev))-1;    //Iz datasheet PIC16f628, stran 60. Vrednost PR2 določa frekvenco PWM signala.

     //Določitev ustreznih bitov za PWM način delovanja modula CCP1 mikrokontrolerja PIC16f877a:

    CCP1M2 = 1;

    CCP1M3 = 1;

    T2CKPS0 = 1;

    T2CKPS1 = 0;              //T2CKPS0=1, T2CKPS1=0 => Preddelitev TMR2 je 1:4

    TMR2ON = 1;               //Omogočimo, vklopimo delovanje časovnika TMR2

}

PWM_duty_cycle(unsigned int duty)

{

    if(duty < 1023)

    {

        duty = ((float)duty/1023)*(_XTAL_FREQ/(PWM_frekvenca*TMR2_preddelitev));

       

        //Bita 5-4 registra CCP1CON (CCP1X in CCP1Y) sta najmanj pomembna (LSB => Least Significant Bits) bita duty cycle PWM signala

        CCP1Y = duty & 1;        //Shranimo bit 0

        CCP1X = duty & 2;        //Shranimo bit 1

        CCPR1L = duty >> 2;    //Shranimo preostalih 8 bitov, ki se nahajajo v registru CCPR1L.  Skupaj je sedaj podatek 10-bitni.

    }

}

void ADC_Init()         //Inicializacija modula AD pretvorbe

{

    ADCON0 = 0b10000001;               //AD pretvorba je omogočena, časovni takt za pretvorbo je 32*Tosc

    ADCON1 = 0b10001110;               //RA0/AN0 je analogni, vsi ostali so digitalni. Vref+ = Vdd, Vref- = Vss

}

 unsigned int ADC_beri(unsigned char kanal)

 {

    if(kanal > 0)                      //Zanima nas analogna napetost na AN0 (na kanalu 0)

    {

        return 0;

    }

    ADCON0 &= 0b11000101;              //Nastavitev ustreznih bitov registra ADCON0

    ADCON1 = 0b10001110;               //Izbira analognega vhoda AN0 in izbira desne poravnave rezultata 

    __delay_ms(3);                     //Čas potreben za polnjenje kondenzatorja (dokumentacija za PIC16f877a)

    GO_nDONE = 1;                      //Zaženemo AD pretvorbo

    while(GO_nDONE);                   //Dokler je GO_nDONE = 1 je AD pretvorba v teku, ko je GO_nDONE = 0, je AD pretvorba končana.

    return ((ADRESH<<8)|ADRESL);       //Vrni se iz funkcije z rezultatom AD pretvorbe (10-bitni rezultat)

 }

void main()

{

    TRISC = 0x00;                  //RC2/CCP1 je izhodni pin PWM signala. Na ta pin je priključena LED dioda

    TRISA = 0xFF;                  //RA0/AN0 je vhodni pin kamor je priključen potenciometer

    TRISB = 0x00;                  //Izhodni pini za LCD display

    LCD4bitni_Init();              //Za komunikacijo z LCD-jem uporabimo header file lcd.h

    LCD4bitni_Clear();

    LCD4bitni_Set_Cursor(1,0);

    LCD4bitni_Pisi_niz("Nap. PWM:");

    LCD4bitni_Set_Cursor(1,15);

    LCD4bitni_Pisi_niz("V");

    LCD4bitni_Set_Cursor(2,0);

    LCD4bitni_Pisi_niz("Duty cycle:");

    LCD4bitni_Set_Cursor(2,15);

    LCD4bitni_Pisi_niz("%");

    

    ADC_Init();

    PWM_init();

    do

    {

        ADC_vrednost = ADC_beri(0);         //Klicanje funkcije za branje in AD pretvorbe na RA0/AN0

        Napetost_PWM = ADC_vrednost;        //Spremenljivka za izračun napetosti in duty cycle

        PWM_duty_cycle(ADC_vrednost);       //PWM duty cycle je odvisen od analogne napetosti na RA0/AN0 (potenciometer) oz. njene AD pretvorbe

  //Pretvorba 10-bitnega podatka v napetost na dve decimalki natančno:

        Napetost_PWM = (Napetost_PWM*4.68/1024)*100;        //Moje vezje, tudi mikrokontroler, je prek PICkit 2 napajano z napetostjo 4,68 V

        digit1 = (Napetost_PWM/100);

        digit2 = ((Napetost_PWM - digit1*100)/10);

        digit3 = (Napetost_PWM - (digit1*100+digit2*10));

//Izpis napetosti na LCD display na dve decimalki natančno:

        LCD4bitni_Set_Cursor(1,10);

        LCD4bitni_Pisi_znak(digit1+48);           //48 ASCII => znak 0 => Piši znak od številke 0 naprej

        LCD4bitni_Pisi_znak(0x2E);                //Izpis decimalne pike. ASCII hex. koda za decimalno piko

        LCD4bitni_Pisi_znak(digit2+48);

        LCD4bitni_Pisi_znak(digit3+48);

//Izračun duty cycle v %:

        Izpis_duty = Napetost_PWM/4.66;  //Ker je moje vezje napajano z napetostjo 4,68 V, bo duty cycle imel vrednost 50 % pri napetosti 2,34 V

        digit4 = (Izpis_duty/10);

        digit5 = (Izpis_duty - digit4*10);

//Izpis duty cycle v % na LCD display:

        LCD4bitni_Set_Cursor(2,12);

        LCD4bitni_Pisi_znak(digit4+48);

        LCD4bitni_Pisi_znak(digit5+48);

        __delay_ms(50);

    }

    while(1);

}

Za generiranje PWM signala imamo v programu dve pomembni funkciji, PWM_init() in PWM_duty_cycle(unsigned int duty). V prvi inicializiramo ustrezne registre, potrebne za nastavitev modula PWM in določitev frekvence PWM signala, v drugi pa določamo duty cycle PWM signala.

V prvi funkciji (PWM_init()) smo z določitvijo bitov CCP1M2 in CCP1M3 registra CCP1CON na vrednost 1, določili PWM način delovanja modula CCP (Capture/Compare/PWM => modul za zajemanje, primerjavo in generiranje PWM signala). Z nastavitvijo bitov T2CKPS0 = 1 in T2CKPS1 = 0 registra T2CON smo določili preddelitev časovnika TMR2 1:4, z bitom TMR2ON pa smo omogočili delovanje tega časovnika.

Slika 2: PWM signal

Kakšna bo frekvenca PWM signala je odvisno od vrednosti, ki jo zapišemo v register PR2. Čas trajanja ene periode PWM-signala izračunamo po enačbi:

Če enačbo preuredimo, dobimo:

Če upoštevamo da je TPWM = 1 / PWM_frekvenca in TOSC = 1 / _XTAL_FREQ dobimo enačbo, ki smo jo vpisali v program:

Ko časovnik TMR2 doseže vrednost, ki je nastavljena v registru PR2 (v našem primeru 49), se ponastavi in začne ponovno naraščati od vrednosti 0. V našem primeru bo torej frekvenca PWM signala 5000 Hz, kar vidimo tudi iz oscilograma, pridobljenega s programatorjem PICkit 2 (slika 3).

Slika 3: PWM signal, frekvenca je 5000 Hz

Z drugo funkcijo (PWM_duty_cycle(unsigned int duty) nastavimo duty cycle PWM signala. Ločljivost PWM signala je 10-bitna, zato te vrednosti ne moremo shraniti v enem registru (naš mikrokontroler vsebuje 8-bitne registre in podatkovne linije). Zato smo uporabili še dva druga bita, CCP1Y (bit 5) in CCP1X (bit 4) registra CCP1CON za shranjevanje zadnjih dveh LSB (Least Significant Bits => najmanj pomembna bita) bitov in nato še vseh 8 zgornjih bitov registra CCPR1L.

PWM duty cycle izračunamo po enačbi:

Preuredimo to enačbo tako, da dobimo vrednost registra CCPR1L ter bita 4 in bita 5 registra CCP1CON:

Rezultat ADC (analogno digitalne pretvorbe) je 10-bitni podatek, od 0 do 1024, mi pa potrebujemo območje od 0 % do 100 %. Če je analogna napetost na RA0/AN0 enaka 0 V, mora biti duty cycle 0 %, če je analogna napetost na RA0/AN0 enaka polovica napetosti Vdd (v mojem primeru je Vdd enaka 4,68 V), mora biti duty cycle 50 %, če je analogna napetost na RA0/AN0 enaka tretjini napetosti Vdd, mora biti duty cycle 33 % in tako naprej. Torej je PWM duty cycle = duty/1023. Upoštevamo, da je čas periode PWM signala enak 1/PWM frekvenca (TPWM = 1 / PWM_frekvenca). Vemo tudi, da je TOSC = 1 / _XTAL_FREQ. Če vstavimo te podatke v gornjo enačbo, dobimo:

Gornjo enačbo preuredimo in dobimo enačbo, ki smo jo vpisali v program:

Preverimo duty cycle PWM signala pri analogni napetosti na pinu RA0/AN0 ene tretjine napetosti Vdd (4,68 V / 3 = 1,56 V). Pri tej napetosti bi moral znašati duty cycle 33 % (slika 4).

Iz oscilograma sledi: Duty cycle = širina impulza / PWM perioda = 66 ms / 200 ms * 100 % = 33 %.

Slika 4: Pri napetosti Vdd/3 znaša duty cycle 33 %

Kako naj spremenimo vezje, če želimo s PWM signalom krmiliti hitrost vrtenja enosmernega motorčka?