Impulzus-szélesség moduláció (PWM)

A fejezet tartalma:

Az impulzus-szélesség moduláció alapjai

A mikrovezérlők alkalmazásainál gyakran előforduló feladat, hogy hogy valamilyen mennyiséget (egy fogyasztó teljesítményét) folyamatosan vagy fokozatosan kell szabályozni.

Az egyik kézenfekvő megoldás egy digitális-analóg átalakító (DAC) alkalmazása. A Freescale Kinetis MKL25Z128VLK4 mikrovezérlő is rendelkezik egy 12-bites felbontású DAC periférifériával, amelynek kimenete a mikrovezérlő PTE30 kivezetésén (a FRDM-KL25Z kártya J10-11 kivezetése) érhető el. Ez azonban csupán egyetlen kimenet vezérlésére képes.

Egy másik lehetőség az impulzus-szélesség moduláció (Pulse Width Modulation - PWM) alkalmazása. Ennek elve az, hogy az analóg kimenő feszültségjelek helyettesíthetők digitális impulzussorozat-jelekkel, amelyek hosszabb időtartamra vonatkoztatott átlagfeszültsége egyenértékű az analóg feszültségjellel. Ilyen jeleket hardveresen a mikrovezérlő időzítő és digitális összehasonlító perifériáinak felhasználásával állíthatunk elő. A digitális impulzussorozat frekvenciáját úgy kell (elegendően nagyra) megválasztani, hogy az, a vezérelt vagy szabályozott eszköz megfelelő működését biztosítsa. Például a szabályozott fényforrás folyamatos működésűnek látsszon (a szemünk ne vegyen észre villogást), vagy egy egyenáramú motor ne lökésszerűen változó szögsebességgel forogjon.

Az impulzus-szélesség moduláció (PWM) lényege tehát az, hogy olyan állandó periódusidejű (frekvenciájú) jeleket keltünk, ahol a szabályozás a jel kitöltési tényezőjének változtatásával történik. Néhány különböző kitöltési tényezőjű PWM jelet mutat be a következő ábra:


1. ábra: Azonos periódusú, de különböző kitöltésű impulzus-sorozatok


A kitöltési tényező az impulzusszélesség (azaz a bekapcsolt állapot ideje) és a periódusidő hányadosa. Ha százalákosan akarjuk megadni, akkor a hányadost még meg kell szorozni 100 %-kal.

kitöltési tényező = impulzusszélesség/periódusidő

Ha a kitöltési tényezőt százalékosan akarjuk megadni, akkor a hányadost még meg kell szorozni 100 %-kal:

kitöltési tényező [%] = 100 % * impulzusszélesség/periódusidő


Habár a PWM jeleket szoftveresen is előállíthatjuk (egyszerű I/O parancsok és megfelelő késleltetések alkalmazásával), a mikrovezérlők hardveres támogatást is nyújtanak a PWM jelek előállításához. A hardveres támogatás előnye az, hogy (a beállítás után) a periodikus jelek keltése nem vesz igénybe CPU időt, s "hardveres pontosságú", azaz a programmegszakítások, vagy a programfutás menete nem befolyásolják a jelek időzítési viszonyait.

A Freescale Kinetis MKL25Z128VLK4 mikrovezérlő esetében a három 16 bites időzítő/számláló (Timer0, Timer1, Timer2) és a hozzájuk kapcsolódó multifunkciós csatornák segítségével állíthatunk elő PWM jelet. A csatornák száma összesen 10 db (TPM0: CH0..CH5, TPM1: CH0..CH1, TPM2: CH0..CH1), tehát egyidejűleg legfeljebb ennyi PWM jelet tudunk előállítani.  Az egyidejűleg keltett PWM jelek nem teljesen függetlenek egymástól: az azonos Timer-hez kapcsolódó PWM csatornák periódusideje  közös, s  a jelek kezdete (a felfutás) azonos idejű (Edge Aligned mód).

Egy Timer/PWM modul (TPM)  vázlatos felépítése az alábbi ábrán látható.  A TPM modul központi eleme az órajelválasztót és az előszámlálót követő 16 bites számláló (az ábrán "Module counter") és a változó darabszámú (legfeljebb 6 db) csatorna, amelyek üzemmódja bemeneti jelelfogás (Input Capture), kimeneti digitális komparátor (Output Compare), illetve PWM lehet.



2. ábra: Egy Timer/PWM Modul (TPM) blokkvázlata


A FRDM-KL25Z kártya azon kivezetései használhatók impulzus-szélesség modulált (PWM) kimenetként, amelyek az alábbi ábrán PWM feliratú lila címkével vannak megjelölve. Fentieken kívül PWM kimenetként használhatók a kártyán található RGB LED-hez tartozó kivezetések is: LED_RED (PTB18), LED_GREEN (PTB19), LED_BLUE (PTD1).

Azt, hogy a fenti ábrán lila címkével megjelölt kivezetések melyik TPM modul melyik csatornájához vannak rendelve, s ennek megfelelően mely kivezetések nem használhatók egyidejűleg független PWM kimenetként, a FRDM-KL25Z Pinouts dokumentumban  ellenőrizhető: az ALT3 oszlopból azok az FTMx_CHn (ahol x:0,1,2; n:0..5) jelű kivezetések használhatók PWM kimenetként, amelyek zöld alapszínű cellában vannak).



2. ábra: A lila címkével megjelölt kivezetések használhatók PWM kimenetként

A PWM kimenetek kezelése mbed környezetben

Az mbed környezetben a PwmOut objektumosztály szolgál a PWM kimenetek konfigurálására és kezelésére. A tagfüggvények felsorolása az alábbi táblázatban található.

Függvény
Felhasználás
PwmOut név(PinName pin)
A konstruktor, amely létrehoz egy "név" nevű PwmOut  objektumot és a pin paraméterrel megadott kimenethez rendeli
write(float adat)
A kitöltési tényező beállítása a megadott (0.0 - 1.0  közötti) értékre
read()
Az utoljára beállított kitöltési tényező adja meg (0.0 - 1.0 közötti float)
period(float sec)
A periódusidő megadása másodpercben (float típusú paraméter)
period_ms(int ms) A periódusidő megadása milliszekundumban (int típusú paraméter)
period_us(int us) A periódusidő megadása mikroszekundumban (int típusú paraméter)
pulsewidth(float sec)
Az impulzusszélesség (a magas szintű állapot ideje) megadása másodpercben
pulsewidth_ms(int ms) Az impulzusszélesség megadása milliszekundumban (int típusú paraméter)
pulsewidth_us(int us) Az impulzusszélesség megadása mikroszekundumban (int típusú paraméter)
operator = adat
Rövidített alak név.write(adat) helyett
operator int()
Rövidített alak név.read() helyett

Mintapélda: LED fényerejének vezérlése soros kapcsolaton

Az alábbi 03_pwm_led nevű programban a számítógépről, soros kapcsolaton keresztül vezéreljük a FRDM-KL25Z kártyán a vörös fényerejét. A soros terminálról (Hyperterminal, PuTTY, stb.) az '1', '5', vagy '9' karakter elküldése a LED_RED kimenet 0.99, 0.5, 0.01 kitöltésű 50 Hz-es négyszögjelre állítja be, ami a LED negatív logikája miatt 1 %-os, 50 %-os, illetve 99 %-os teljesítménynek felel meg.

Hardver követelmények:
 

1. lista: A 03_pwm_led/main.cpp  program listája
#include "mbed.h"

PwmOut myled(LED_RED);
Serial pc(USBTX, USBRX); // tx, rx

int main() {
myled.period_ms(20); //Period = 20 ms
myled.write(1.0); //Led off at start

while(1) {
char c = pc.getc(); //read next character
if(c=='1') {
myled = 0.99f; //LED has negative logic!
pc.printf("Duty cycle = 0.01\r\n");
}
else if(c=='5') {
myled = 0.5f;
pc.printf("Duty cycle = 0.5\r\n");
}
else if(c=='9') {
myled = 0.01f; //LED has negative logic!
pc.printf("Duty cycle = 0.99\r\n");
}
wait(0.2);
}
}

Mintapélda: dallam lejátszása piezo csipogón

Az alábbi 03_pwm_music nevű programmal egy rövid zeneszámot játszunk le. A hang megszólaltatáshoz egy piezo csipogót (piezo buzzer) hajtunk meg a keltett PWM jellel. Ezt a mintaprogramot, amely az Oranges and Lemons c. régi angol népdalt játssza le Rob Toulson és Tim Wilmshurst: ARM mbed Course Material - Pulse Width Modulation tananyagából vettük kölcsön.


3. ábra: A lejátszani kívánt dallam kottája

A fenti kottát az alábbi táblázattal is megadhatjuk, ahol a hangok frekvenciáját is feltüntettük.

A hangok tartamáról elég annyit tudnunk, hogy a nyolcadhang fele annyi ideig tart, mint egy negyedhang, a félhang pedig a negyedhang idejének kétszereséig tart. Ha a negyedhang idejét félmásodpercnek vesszük (ez a percenkénti 120 negyedhangos Allegretto tempónak felel meg), akkor a negyedhang ideje 0.25 s, a félhangé pedig 1 s lesz.

A hangkeltés módja

Egy PwmOut kimenetet úgy konfigurálunk, hogy a periódusidő a kívánt hangmagasságnak megfelelő frekvencia reciproka legyen, a kitöltést pedig 50 %-ra (0.5-re) állítjuk be. Például egy normál A hang (440 Hz) megszólaltatása így néz ki:
PwmOut buzzer(D3);
buzzer.period(1.0/440);
buzzer = 0.5;
A hangkeltés időtartamát az egyszerűség kedvéért a szokásos késleltető függvényekkel (wait(), wait_ms() stb.) állíthatjuk be. A 120-as tempó esetén 0.25, 0.5 vagy 1.0 s időtartam felel meg a nyolcad, negyed,  illetve fél értéknek.
wait(0.5*beat);    //ahol beat = 1/2, 1, vagy 2

Megjegyzés: Bár a zene lejátszásához a hangokat egy PwmOut kimenet (és a hozzá tartozó TPM modul) segítségével keltjük, valójában most nem moduláljuk az impulzusok szélességét, hanem állandó, 50 %-os kitöltéssel dolgozunk, s csak a frekvenciát változtatjuk.

Hardver követelmények:
2. lista: A 03_pwm_music/main.cpp  program listája
//Borrowed from Rob Toulson and Tim Wilmshurst: ARM mbed Course Material - PWM
//Link: https://developer.mbed.org/cookbook/Course-Notes
#include "mbed.h"

PwmOut buzzer(D3);
float frequency[]={659,554,659,554,550,494,554,587,494,659,554,440}; //frequency array
float beat[]={1,1,1,1,1,0.5,0.5,1,1,1,1,2}; //beat array

int main() {
while (1) {
for (int i=0; i<12; i++) {
buzzer.period(1/(frequency[i])); // set PWM period
buzzer=0.5; // set duty cycle
wait(0.5*beat[i]); // hold for beat period
}
}
}
A frequency nevű tömbben tároljuk a lejátszandó dallam hangjainak frekvenciáját. A hangok időtartamát pedig a beat nevű tömbben tároljuk (1: negyed, 0.5: nyolcad, 2: fél). A lejátszandó hangok számát itt fix értéknek vettük a for ciklusban (12 db. hangunk van). Általánosabb esetben a tömbökben szereplő elemek n száma így adható meg:
n = sizeof(frequency)/sizeof(frequency[0]);
Szavakban megfogalmazva: a tömbelemek száma = a tömb mérete bájtokban / egy elem mérete bájtokban.

Szervó motorok vezérlése


Az impulzusszélesség-moduláció egy speciális esete a szervó motorok vezérlőjele. Mivel főleg a rádiótávirányítású modellekben terjedt el  a használatuk, ezeket többnyire "RC servo" néven emlegeti az angol nyelvű szakirodalom (RC = Radio Controlled). A szervó motorok felépítése és tipikus jelalakja az alábbi ábrákon látható (a képek forrása a ServoCity honlapja).



4. ábra: A szervó motor "robbantott" képe

A szervó egy kisméretű egyenáramú motort tartalmaz, amely a fogaskerék áttételen keresztül mechanikai összeköttetésben áll egy potméter tengelyével. A beérkező vezérlőjelet feldolgozó beépített elektronika úgy irányítja a motor forgását előre vagy hátra, hogy a potméterrel összekötött tengely mindig az előírt szögállásba kerüljön.


5. ábra: A szervó vezérlőjele 20 ms periódusidejű, 5-10 %-os kitöltésű jel

A különböző gyártmányú szervók vezérlőjele nem egységes, de a bejövő jel általában 50 Hz-es ismétlődési frekvenciájú (periódusidő = 20 ms), 5 - 10 %-os kitöltésű impulzus, amelynél az impulzus szélessége szabja meg a kívánt szögállást. Az 1,5 ms szélességű jel tartozik a középső, úgynevezett nyugalmi helyzethez, s 0,5-1 ms szélességű jel felel meg a bal szélső helyzetnek, valamint 2 - 2,5 ms szélességű jel felel meg a jobb szélső véghelyzetnek.


6. ábra: Tipikus jelalakok szervó motor vezérléséhez


Mintapélda: Szervo motor vezérlése

Az alábbi programban két szélső helyzet között oda-vissza mozgatjuk a szervo motort. A vezérlés egy PwmOut kivezetés felhasználásával történik, amelyet a D3 (PTA12) kivezetéshez rendeltünk hozzá. A periódusidőt 20 ms-ra állítjuk be, az impulzusszélességet pedig 1000 és 2000 µs között változtatjuk, 20 µs-os lépésekben.

Hardver követelmények:
3. lista: A 03_pwm_servo/main.cpp  program listája
#include "mbed.h"
PwmOut servo(D3);

int main() {
servo.period_ms(20); //Period = 20 ms (f=50 Hz)
while(true) {
for(int pw=1000; pw <= 2000; pw=pw+20) {
servo.pulsewidth_us(pw); //Set new servo position
wait_ms(200);
}
wait_ms(1000); //Wait before reverse direction
for(int pw=2000; pw >= 1000; pw=pw-20) {
servo.pulsewidth_us(pw); //Set new servo position
wait_ms(200);
}
wait_ms(1000);
}
}