WebElektronika

PWM jel előállítása a Timer1 modul segítségével az ATmega8-as mikrovezérlőnél

person access_time 2016.02.09.
Az ATmega8-as család Timer1 moduljának a megismerését folytatjuk most, de úgy, hogy PWM-ként fogjuk alkalmazni ezt a perifériát.


Nézzük meg az ATmega8-as mikrovezérlő lábkisztását (1. ábra)

kep
1. ábra   ATmega8-as lábkiosztása
 

Látjuk, hogy a 15-ös lábon nem csak a B port 1-es bitje található, hanem az OC1A kimenet is. A PWM jel ezen a kimeneten jelenik meg.
A Timer1 blokkvázlatán láthatjuk, hogy ha a TCNT1 regiszter értéke megegyezik az OCR1A regiszter értékével, akkor a komparátor kimenetén lévő logikai 1 megjelenik a Waveform Generation bemenetén. 

A TCCR1A és a TCCR1B regiszterekben található WGM10,11,12,13 bitek segítségével tudjuk beállítani a haszálni kívánt PWM lehetőségeket (2. ábra).

kep
2. ábra   A PWM lehetőségek a TCCR1A és a TCCR1B regiszterekben
 

Ha használni kívánjuk az OC1A kimenetet (ezen jelenik meg a PWM jel), akkor a TCNT1 regiszter kimenetét komparáljuk az OCR1A regiszter értékével (3. ábra).

kep
3. ábra   Az OC1A kimenet beállítása TCNT1 = OCR1A esetén
 

Nézzünk most meg néhány példát, amely a tesztpanelon kipróbálható, az egyik LED fényerején fogjuk a PWM működést tesztelni.

 

1. példa

Az OCR1A regiszterben található érték segítségével tudjuk a PWM jel kitöltési tényezőjét megváltoztatni. A mintakódban kiemelésre került az OCR1A regiszter értékadása. Próbáljuk ki mind a két értékadást, látni fogjuk a LED fényerején, hogy különböző az előállított PWM jel kitöltési tényezője. Ez a PWM jel a B port 1. lábán jelenik meg. Ezért ezt a portbitet kimenetre állítjuk (DDRB |= 1 <<DDRB1).

#include <avr/io.h>

#define CLKDIV1()        { TCCR1B |= 1 << CS10;}
#define CLKDIV8()        { TCCR1B |= 1 << CS11;}
#define CLKDIV64()        { TCCR1B |= (1 << CS10) | (1 << CS11);}
#define CLKDIV256()        { TCCR1B |= 1 << CS12;}
#define CLKDIV1024()    { TCCR1B |= (1 << CS10) | (1 << CS12);}

int main(void)
{
    DDRB |= 1 << DDRB1;
    OCR1A = 0x0001;        // OCR1A = 0x0123;​
    TCCR1A |= (1 << COM1A1) | (1 << WGM11) | (1 << WGM10);
    CLKDIV8();
    
    while(1);
}

 

 

2. példa

Gyakorolva a makrók készítését, valósítsuk meg az első példát makrók segítségével.

#include <avr/io.h>

#define CLKDIV1()        { TCCR1B |= 1 << CS10;}
#define CLKDIV8()        { TCCR1B |= 1 << CS11;}
#define CLKDIV64()        { TCCR1B |= (1 << CS10) | (1 << CS11);}
#define CLKDIV256()        { TCCR1B |= 1 << CS12;}
#define CLKDIV1024()    { TCCR1B |= (1 << CS10) | (1 << CS12);}

#define ClearOC1A()        { TCCR1A |= (1 << COM1A1); }
#define PWMPhC8bit()    { TCCR1A |= (1 << WGM10); }
#define PWMPhC9bit()    { TCCR1A |= (1 << WGM11); }
#define PWMPhC10bit()    { TCCR1A |= (1 << WGM11) | (1 << WGM10); }
#define CTC()            { TCCR1A |= (1 << WGM12); }
#define PWMFast8bit()    { TCCR1A |= (1 << WGM12) | (1 << WGM10); }
#define PWMFast9bit()    { TCCR1A |= (1 << WGM12) | (1 << WGM11); }

int main(void)
{
    DDRB |= 1 << DDRB1;
    OCR1A = 0x0123;
    ClearOC1A();
    PWMPhC10bit();
    CLKDIV8();
    
    while(1);
}

 

3. példa

Most vegyük ki a forrásfile-ból a makrókat és tegyük be egy külön file-ba, amelynek a neve legyen "makrok.h". Továbbá, módosítsuk úgy a programunkat, hogy az egyik potenciométerrel tudjuk beállítani a kitöltési tényezőt. Ehhez be kell állítani az AD átalakítót, ezzel már korábban foglalkoztunk az egyik cikkünkben.

#define CLKDIV1()        { TCCR1B |= 1 << CS10;}
#define CLKDIV8()        { TCCR1B |= 1 << CS11;}
#define CLKDIV64()        { TCCR1B |= (1 << CS10) | (1 << CS11);}
#define CLKDIV256()        { TCCR1B |= 1 << CS12;}
#define CLKDIV1024()    { TCCR1B |= (1 << CS10) | (1 << CS12);}

#define ClearOC1A()        { TCCR1A |= (1 << COM1A1); }
#define PWMPhC8bit()    { TCCR1A |= (1 << WGM10); }
#define PWMPhC9bit()    { TCCR1A |= (1 << WGM11); }
#define PWMPhC10bit()    { TCCR1A |= (1 << WGM11) | (1 << WGM10); }
#define CTC()            { TCCR1A |= (1 << WGM12); }
#define PWMFast8bit()    { TCCR1A |= (1 << WGM12) | (1 << WGM10); }
#define PWMFast9bit()    { TCCR1A |= (1 << WGM12) | (1 << WGM11); }

#define ADBe()            {ADCSRA |= 1 << ADSC;}

 

Ezt a header file-t nem szükséges a projektünkhöz hozzáadni, elég az "include" segítségével "behívni" a forrásfile-ba.

#include <avr/io.h>
#include "makrok.h"

int main(void)
{
    DDRB |= 1 << DDRB1;
    ClearOC1A();
    PWMPhC10bit();
    CLKDIV8();
    
    DDRC = 0b11111110;
    ADCSRA = 0b10000111;
    ADMUX = 0b01100000;
    
    while(1)
    {
        while(ADCSRA & (1 << ADSC));
        OCR1A = ADCH;
        ADBe();
    }
}