AVR Timers – PWM Mode – Partea II

AVR Seriesacest articol este în continuare cu postul anterior PWM. Aflați cum să programați cronometrele pentru a funcționa în modul PWM! Deci, să începem!

Bună ziua oameni buni! Nu ne-am văzut de mult! 🙂

în postarea mea anterioară, am discutat conceptele de bază ale PWM. Să o rezumăm mai întâi:

  • PWM reprezintă modulația lățimii impulsurilor.
  • poate fi generat prin compararea formei de undă predeterminate cu un nivel de tensiune de referință sau prin realizarea unor circuite analogice simple.
  • ciclul de funcționare al unei forme de undă PWM este dat de următoarea relație.
ciclul de funcționare
  • există trei moduri de funcționare PWM-rapid PWM, faza corectă PWM și frecvența și faza corectă PWM
  • Cum de a alege timer, Modul de funcționare și compara modul de ieșire pentru generarea PWM dorit.
Deci, acum, fără prea multă bătaie de cap, să vedem cum să o implementăm folosind microcontrolerele AVR. Înainte de a continua, Vă sugerez să treceți prin postările mele anterioare pe cronometre și PWM.

să luăm o declarație de problemă. Trebuie să generăm un semnal PWM de 50 Hz având un ciclu de funcționare de 45%.

analiză

având în vedere că

Frequency = 50 Hz

cu alte cuvinte, perioada de timp, T

T = T(on) + T(off) = 1/50 = 0.02 s = 20 ms

de asemenea, având în vedere că

Duty Cycle = 45%

astfel, rezolvând conform ecuației date mai sus, obținem

T(on) = 9 msT(off) = 11 ms

acum, acest lucru poate fi realizat în două moduri:

  1. utilizați cronometrul în modul CTC
  2. utilizați cronometrul în modul PWM

metodologie – modul CTC

bine, așa că nu voi scrie niciun cod aici (doar pseudo-codul). Presupun că după ce ați citit postările mele anterioare, sunteți suficient de inteligent pentru a scrie unul singur! Vom discuta doar conceptele.

în primul rând, alegeți un cronometru adecvat. Pentru această aplicație, putem alege oricare dintre cele trei cronometre disponibile în ATMEGA32. Alegeți un prescaler adecvat. Apoi configurați cronometrul și continuați ca de obicei. Captura se află aici este că aveți nevoie pentru a actualiza valoarea compara ocrx înregistra de fiecare dată. Un astfel de mod este discutat în pseudo codul de mai jos.

acest lucru este analog cu LED-ul tradițional flasher, cu excepția faptului că timpii de pornire și oprire sunt diferiți.

Pseudo cod

#include <avr/io.h>#include <avr/interrupt.h> uint8_t count = 0; // global counter // initialize timer, interrupt and variablevoid timerX_init(){ // set up timerX with suitable prescaler and CTC mode // initialize counter // initialize compare value // enable compare interrupt // enable global interrupts} // process the ISR that is firedISR (TIMERx_COMPA_vect){ // do whatever you want to do here // say, increment the global counter count++; // check for the global counter // if count == odd, delay required = 11 ms // if count == even, delay required = 9 ms // thus, the value of the OCRx should be constantly updated if (count % 2 == 0) OCRx = 9999; // calculate and substitute appropriate value else OCRx = 10999; // calculate and substitute appropriate value} int main(void){ // initialize the output pin, say PC0 DDRC |= (1 << 0); // initialize timerX timerX_init(); // loop forever while(1) { // do nothing }}

acum, aceasta este o metodă. Și este foarte ineficient. Puteți crește eficiența acestuia scriind un cod C mai bun (în sintaxă), cu toate acestea conceptul rămâne același. Dacă aveți orice altă metodă / concept, sunteți cel mai binevenit să-l împărtășească aici! 🙂

actualizare: unul dintre cititorii maxEmbedded, „coolpales” a scris acest cod și a funcționat pentru el.

vă rugăm să rețineți că acest cod nu a fost testat încă! Deci, dacă vreunul dintre voi încearcă, postați rezultatele aici, aș fi fericit să le văd! 🙂

metodologie – modul PWM

bine, deci acum vă permite să învețe despre modul PWM. Modul PWM din AVR este controlat hardware. Aceasta înseamnă că totul, prin tot ceea ce vreau să spun „totul”, este realizat de procesorul AVR. Tot ce trebuie să faceți este să inițializați și să porniți cronometrul și să setați ciclul de funcționare! Mișto, nu?! Să învățăm cum!

aici, am folosit Timer0 de ATMEGA32 pentru demonstrație. Puteți alege orice alt cronometru sau microcontroler AVR, de asemenea. Acum să aruncăm o privire la registre.

TCCR0 – Timer/Counter0 control Register

am întâlnit acest registru în tutorialul meu Timer0. Aici, vom învăța cum să setați biți adecvați pentru a rula cronometrul în modul PWM.

TCCR0 Register

TCCR0 Register

vom discuta doar acele biți care ne interesează acum.

  • Bit 6,3 – WGM01:0 – modul de generare a formei de undă – acești biți pot fi setați fie la „00”, fie la „01”, în funcție de tipul de PWM pe care doriți să îl generați. Iată tabelul de căutare în sus.
     modul de generare a formei de undă descrierea bitului

    modul de generare a formei de undă descrierea bitului

  • Bit 5,4 – COM01:0 – compara modul de ieșire meci-acești biți sunt setate pentru a controla comportamentul de ieșire compara pin (OC0, pin 4 în ATMEGA32), în conformitate cu wgm01:0 biți. Următorul tabel de căutare determină operațiile pinului OC0 pentru modul PWM rapid.
    comparați modul de ieșire, modul rapid PWM

    comparați modul de ieșire, modul rapid PWM

    acum vă permite să aruncați o privire la formele de undă rapide PWM. Explicații detaliate pot fi găsite în tutorialul meu anterior.

    Fast PWM

    Fast PWM

    acum, permiteți-mi să vă reamintesc că AVR PWM este complet hardware controlat, ceea ce înseamnă că, chiar timer compara operațiunea se face de CPU AVR. Tot ce trebuie să facem este să spunem procesorului ce să facă odată ce apare un meci. Pinii COM01: 0 intră în joc aici. Vedem că setându-l la „10” sau „11”, pinul de ieșire OC0 este fie setat, fie șters (cu alte cuvinte, determină dacă PWM este în modul inversat sau în modul non-inversat).

    în mod similar pentru PWM corect de fază, tabelul de căutare și formele de undă merg astfel.

     comparați modul de ieșire, modul PWM corect în fază

    comparați modul de ieșire, modul PWM corect în fază

    Phase Correct PWM

    Phase Correct PWM

    chiar și aici, setarea COM01:0 la „10” sau „11” determină comportamentul pinului OC0. După cum se arată în formele de undă, există două cazuri – una în timpul numărării în sus și alta în timpul numărării în jos. Comportamentul este descris în mod clar în tabelul de căutare.

    vă rugăm să rețineți că OC0 este un cod pin de ieșire. Astfel, efectele WGM și COM nu vor intra în joc decât dacă registrul DDRx este setat corect. Consultați acest tutorial pentru mai multe informații.

  • Bit 2:0 – CS02:0 – Ceas Selectați biți – acești biți sunt deja discutate în tutorial Timer0.

OCR0 – ieșire compara registru

am venit peste chiar și acest registru în tutorialul meu Timer0. Folosim acest registru pentru a stoca valoarea de comparare. Dar când folosim Timer0 în modul PWM, valoarea stocată în acesta acționează ca ciclu de funcționare (evident!). În declarația de problemă, având în vedere că ciclul de funcționare este de 45%, ceea ce înseamnă

OCR0 = 45% of 255 = 114.75 = 115

și asta este! Acum suntem gata să scriem un cod pentru asta! 🙂

Edit: notă

următorul cod discută despre cum să creați un semnal PWM al unui ciclu de funcționare dorit. Dacă doriți să modificați frecvența, trebuie să modificați valoarea de TOP, care se poate face folosind registrul ICRx (care nu este acceptat de cronometre pe 8 biți). Pentru Timer1 pe 16 biți, poate fi variat folosind ICR1A. voi discuta despre acest lucru în curând când vom discuta despre servo control.

Cod

deci, aici merge codul. Pentru a afla despre operațiunile portuare I/O în AVR, vizualizați acest lucru. Pentru a afla despre manipulările de biți, vizualizați acest lucru. Pentru a afla cum să utilizați AVR Studio 5, vizualizați acest lucru. Pentru a afla cum este structurat acest cod, vizualizați postarea TIMER0 anterioară.

#include <avr/io.h>#include <util/delay.h>void pwm_init(){ // initialize TCCR0 as per requirement, say as follows TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00); // make sure to make OC0 pin (pin PB3 for atmega32) as output pin DDRB |= (1<<PB3);} void main(){ uint8_t duty; duty = 115; // duty cycle = 45% of 255 = 114.75 = 115 // initialize timer in PWM mode pwm_init(); // run forever while(1) { OCR0 = duty; }}

declarație problemă

deci, acum, să luăm o altă declarație problemă. Acesta va fi un lucru mai practic, spre deosebire de cel precedent!

să luăm tradiționalul LED flasher unde trebuie să clipim un LED la o anumită frecvență. Dar hei, stai, nu am discuta mult timp înapoi în acest post (derulați în jos spre sfârșitul anului)? Hmm, deci să-l modifice, astfel încât să includă PWM. Spre deosebire de tradiționale LED flasher (în cazul în care LED-urile sunt fie ON sau OFF), vă permite să-l strălucească la luminozitatea maximă, și apoi reduce încet luminozitatea până când ajunge la zero, și apoi din nou crește luminozitatea încet până când acesta devine maxim.

analiză și cod

Deci, cum o facem? Da, ai ghicit bine! Reduceți încet ciclul de funcționare de la 255 la zero, apoi creșteți-l de la zero la 255. În funcție de ciclul de funcționare, tensiunea aplicată LED-ului variază și, prin urmare, luminozitatea. Următoarea formulă oferă relația dintre tensiune și ciclul de funcționare.

ecuația V_out

deci, aici merge codul. Nu voi explica, poți să-l decodezi singur. Pentru a afla despre operațiunile portuare I/O în AVR, vizualizați acest lucru. Pentru a afla despre manipulările de biți, vizualizați acest lucru. Pentru a afla cum să utilizați AVR Studio 5, vizualizați acest lucru. Pentru a afla cum este structurat acest cod, vizualizați postarea TIMER0 anterioară.

// program to change brightness of an LED// demonstration of PWM #include <avr/io.h>#include <util/delay.h> // initialize PWMvoid pwm_init(){ // initialize timer0 in PWM mode TCCR0 |= (1<<WGM00)|(1<<COM01)|(1<<WGM01)|(1<<CS00); // make sure to make OC0 pin (pin PB3 for atmega32) as output pin DDRB |= (1<<PB3);} void main(){ uint8_t brightness; // initialize timer0 in PWM mode pwm_init(); // run forever while(1) { // increasing brightness for (brightness = 0; brightness < 255; ++brightness) { // set the brightness as duty cycle OCR0 = brightness; // delay so as to make the user "see" the change in brightness _delay_ms(10); } // decreasing brightness for (brightness = 255; brightness > 0; --brightness) { // set the brightness as duty cycle OCR0 = brightness; // delay so as to make the user "see" the change in brightness _delay_ms(10); } // repeat this forever }}

Deci, aici se termină meu un alt tutorial mult așteptat și lung! Următorul.. Comunicare Serială! Ne mai vedem!! 🙂

și da, dacă aveți sugestii, îndoieli, critici constructive, etc, sunteți cel mai binevenit să renunțe la o notă de mai jos! Aboneaza-te la blog-ul meu sau apuca RSS feed-uri pentru a rămâne actualizat!

Write a Comment

Adresa ta de email nu va fi publicată.