denna artikel är i fortsättning med föregående PWM inlägg. Lär dig hur du programmerar timers för att fungera i PWM-läge! Så låt oss börja!
Hej folk! Lång tid ingen se! 🙂
i mitt tidigare inlägg har vi diskuterat de grundläggande begreppen PWM. Låt oss sammanfatta det först:
- PWM står för Pulse Width Modulation.
- det kan genereras genom att jämföra förutbestämd vågform med en referensspänningsnivå eller genom att göra enkla analoga kretsar.
- arbetscykel för en PWM-vågform ges av följande relation.
- det finns tre lägen för PWM – Drift-Snabb PWM, Faskorrigering PWM och frekvens och Faskorrigering PWM
- hur man väljer timer, driftläge och jämför utgångsläge för att generera önskad PWM.
Låt oss ta ett problem uttalande. Vi måste generera en 50 Hz PWM-signal med 45% arbetscykel.
analys
med tanke på att
Frequency = 50 Hz
med andra ord, tidsperioden, T
T = T(on) + T(off) = 1/50 = 0.02 s = 20 ms
även med tanke på att
Duty Cycle = 45%
således löser vi enligt ekvationen ovan, vi får
T(on) = 9 msT(off) = 11 ms
nu kan detta vara uppnås på två sätt:
- Använd Timer i CTC-läge
- Använd Timer i PWM – läge
metodik – CTC-läge
Okej, så jag kommer inte att skriva någon kod här (bara pseudokoden). Jag antar att efter att ha läst mina tidigare inlägg är du smart nog att skriva en själv! Vi kommer bara att diskutera begreppen.
Välj först en lämplig timer. För den här applikationen kan vi välja någon av de tre timers som finns i ATMEGA32. Välj en lämplig prescaler. Ställ sedan in timern och fortsätt som vanligt. Fångsten ligger här är att du måste uppdatera jämförelsevärdet för OCRx-registret varje gång. Ett sådant sätt diskuteras i pseudokoden nedan.
detta är analogt med den traditionella LED-blinkaren, förutom att på-och avtiderna är olika.
pseudokod
#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 }}
nu är detta en metod. Och det är väldigt ineffektivt. Du kan öka effektiviteten genom att skriva en bättre C-kod (syntaxvis), men konceptet förblir detsamma. Om du har någon annan metod/ koncept är du varmt välkommen att dela den här! 🙂
Uppdatering: en av läsarna av maxEmbedded, ”coolpales” har skrivit denna kod, och det fungerade för honom.
Observera att denna kod inte testat ännu! Så, om någon av er försöker det, posta dina resultat här, jag skulle gärna se dem! 🙂
metodik-PWM-läge
Okej, så nu kan vi lära oss om PWM-läget. PWM-läget i ljud – / videomottagare är maskinvarustyrt. Det betyder att allt, med allt jag menar ”allt”, görs av AVR CPU. Allt du behöver göra är att initiera och starta timern och ställa in arbetscykeln! Coolt, va?! Låt oss lära oss hur!
Här har jag använt Timer0 av ATMEGA32 för demonstration. Du kan också välja någon annan timer eller AVR-mikrokontroller. Låt oss nu titta på registren.
Tccr0-Timer / Counter0 kontroll Register
vi har stött på detta register i min Timer0 handledning. Här lär vi oss att ställa in lämpliga bitar för att köra timern i PWM-läge.
Tccr0 registrera
vi kommer att diskutera endast de bitar som är av intresse för oss nu.
- Bit 6,3 – WGM01:0 – Vågformsgenereringsläge – dessa bitar kan ställas in på antingen ”00” eller ”01” beroende på vilken typ av PWM du vill generera. Här är uppslagstabellen.
Vågformsgenereringsläge Bit beskrivning
- Bit 5,4-COM01:0 – Jämför Matchutgångsläge – dessa bitar är inställda för att styra beteendet hos utgången jämför stift (OC0, stift 4 i ATMEGA32) i enlighet med wgm01: 0 bitar. Följande uppslagstabell bestämmer operationerna för OC0 pin för snabb PWM-läge.
jämför utgångsläge, snabbt PWM-läge
låt oss nu titta på de snabba PWM-vågformerna. Detaljerad förklaring finns i min tidigare handledning.
snabb PWM
låt mig nu påminna dig om att AVR PWM är helt hårdvarustyrd, vilket innebär att även timern jämför operationen görs av AVR CPU. Allt vi behöver göra är att berätta CPU vad man ska göra när en match inträffar. Com01: 0-stiften spelar in här. Vi ser att genom att ställa in den till ”10” eller ”11”, är utgångsstiftet OC0 antingen inställt eller rensat (med andra ord bestämmer det om PWM är i inverterat läge eller i icke-inverterat läge).
på samma sätt för Faskorrigerad PWM går uppslagstabellen och vågformerna så här.
jämför utgångsläge, fas korrekt PWM läge
fas korrekt PWM
även här, inställning COM01:0 till ”10” eller ”11” bestämmer beteendet hos OC0 pin. Som visas i vågformerna finns det två instanser – en under uppräkning och andra under nedräkning. Beteendet beskrivs tydligt i uppslagstabellen.
Observera att OC0 är en utgångsstift. Således kommer effekterna av WGM och COM inte att spela om inte ddrx-registret är korrekt inställt. Se denna handledning för mer info.
- Bit 2:0 – CS02: 0 – klocka Välj bitar-dessa bitar diskuteras redan i Timer0 handledning.
OCR0-Output jämför Register
vi har stött på även detta register i min Timer0 handledning. Vi använder detta register för att lagra jämförelsevärdet. Men när vi använder Timer0 i PWM-läge fungerar värdet som lagras i det som arbetscykeln (uppenbarligen!). I problemförklaringen är det givet att arbetscykeln är 45%, vilket betyder
OCR0 = 45% of 255 = 114.75 = 115
och det är det! Nu är vi redo att skriva en kod för det! 🙂
redigera: notera
följande kod diskuterar hur man skapar en PWM-signal för en önskad arbetscykel. Om du vill ändra frekvensen måste du ändra toppvärdet, vilket kan göras med hjälp av ICRx-registret (som inte stöds av 8-bitars timers). För 16-bitars Timer1, det kan varieras med ICR1A. jag kommer att diskutera om detta snart när vi diskuterar om servostyrning.
kod
så här går koden. Om du vill veta mer om i/O-portoperationer i ljud – / videomottagare, se detta. För att veta om bitmanipulationer, se detta. För att lära dig hur du använder AVR Studio 5, Se detta. För att lära dig hur den här koden är strukturerad, se föregående TIMER0-inlägg.
#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; }}
Problem uttalande
så nu, låt oss ta ett annat problem uttalande. Den här kommer att bli en mer praktisk sak till skillnad från den tidigare!
Låt oss ta den traditionella LED-blinkaren där vi behöver blinka en LED vid en viss frekvens. Men Hej, vänta, diskuterade vi inte det länge tillbaka i det här inlägget (rulla ner mot slutet)? Hmm, så låt oss ändra det för att införliva PWM. Till skillnad från den traditionella LED flasher (där lysdioder är antingen på eller av), låter göra det lyser vid maximal ljusstyrka, och sedan sakta minska dess ljusstyrka tills den når noll, och sedan igen öka dess ljusstyrka långsamt tills det blir maximal.
analys och kod
så hur gör vi det? Ja, du gissade rätt! Minska arbetscykeln långsamt från 255 till noll och öka den sedan från noll till 255. Beroende på arbetscykeln varierar spänningen som appliceras på lysdioden och därmed ljusstyrkan. Följande formel ger förhållandet mellan spänning och arbetscykel.
så här går koden. Jag kommer inte att förklara det, du kan avkoda det själv. Om du vill veta mer om i/O-portoperationer i ljud – / videomottagare, se detta. För att veta om bitmanipulationer, se detta. För att lära dig hur du använder AVR Studio 5, Se detta. För att lära dig hur den här koden är strukturerad, se föregående TIMER0-inlägg.
// 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 }}
så här slutar min andra mycket efterlängtade och långa handledning! Nästa upp.. Seriell Kommunikation! Vi ses!! 🙂
och ja, om du har några förslag, tvivel, konstruktiv kritik, etc, är du välkommen att släppa en anteckning nedan! Prenumerera på min blogg eller ta RSS-flöden för att hålla dig uppdaterad!