Dieser Artikel wird mit dem vorherigen PWM-Beitrag fortgesetzt. Erfahren Sie, wie Sie die Timer für den Betrieb im PWM-Modus programmieren! Also fangen wir an!
Hallo Leute! Lange nicht gesehen! 🙂
In meinem vorherigen Beitrag haben wir die grundlegenden Konzepte von PWM diskutiert. Fassen wir es zuerst zusammen:
- PWM steht für Pulsweitenmodulation.
- Es kann durch Vergleichen einer vorbestimmten Wellenform mit einem Referenzspannungspegel oder durch Erstellen einfacher analoger Schaltungen erzeugt werden.
- Das Tastverhältnis einer PWM-Wellenform ist durch die folgende Beziehung gegeben.
- Es gibt drei modi von PWM betrieb-Schnelle PWM, Phase Richtige PWM und Frequenz und Phase Richtige PWM
- Wie zu wählen timer, bedienung modus und vergleichen ausgang modus für die erzeugung der gewünschten PWM.
Nehmen wir eine Problemstellung. Wir müssen ein 50 Hz PWM-Signal mit 45% Tastverhältnis erzeugen.
Analyse
In Anbetracht dessen, dass
Frequency = 50 Hz
Mit anderen Worten, der Zeitraum, T
T = T(on) + T(off) = 1/50 = 0.02 s = 20 ms
Auch in Anbetracht dessen, dass
Duty Cycle = 45%
Somit erhalten wir gemäß der oben angegebenen Gleichung
T(on) = 9 msT(off) = 11 ms
auf zwei Arten erreicht:
- Timer im CTC-Modus verwenden
- Timer im PWM-Modus verwenden
Nicht – CTC-Modus
Okay, ich werde hier keinen Code schreiben (nur der Pseudocode). Ich gehe davon aus, dass Sie nach dem Lesen meiner vorherigen Beiträge klug genug sind, selbst einen zu schreiben! Wir werden nur die Konzepte diskutieren.
Wählen Sie zunächst einen geeigneten Timer. Für diese Anwendung können wir einen der drei in ATMEGA32 verfügbaren Timer auswählen. Wählen Sie einen geeigneten Vorteiler. Stellen Sie dann den Timer ein und fahren Sie wie gewohnt fort. Der Haken dabei ist, dass Sie den Vergleichswert des OCRx-Registers jedes Mal aktualisieren müssen. Ein solcher Weg wird in dem unten angegebenen Pseudocode diskutiert.
Dies ist analog zum herkömmlichen LED-Blinker, mit der Ausnahme, dass die Ein- und Ausschaltzeiten unterschiedlich sind.
Pseudocode
#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 }}
Dies ist nun eine Methode. Und es ist sehr ineffizient. Sie können die Effizienz erhöhen, indem Sie einen besseren C-Code schreiben (syntaxbezogen), das Konzept bleibt jedoch gleich. Wenn Sie eine andere Methode / ein anderes Konzept haben, können Sie es gerne hier teilen! 🙂
UPDATE: Einer der Leser von maxEmbedded, „coolpales“, hat diesen Code geschrieben und es hat für ihn funktioniert.
Bitte beachten Sie, dass dieser Code noch nicht getestet wurde! Also, wenn jemand von euch es ausprobiert, poste deine Ergebnisse hier, ich würde mich freuen, sie zu sehen! 🙂
Methodik – PWM-Modus
Okay, jetzt lernen wir den PWM-Modus kennen. Der PWM-Modus in AVR ist hardwaresteuert. Dies bedeutet, dass alles, mit allem meine ich „alles“, von der AVR-CPU erledigt wird. Alles, was Sie tun müssen, ist, den Timer zu initialisieren und zu starten und den Arbeitszyklus einzustellen! Cool, was?! Lass uns lernen wie!
Hier habe ich Timer0 von ATMEGA32 zur Demonstration verwendet. Sie können auch einen anderen Timer oder AVR-Mikrocontroller auswählen. Schauen wir uns nun die Register an.
TCCR0 – Timer / Counter0-Steuerregister
Wir sind in meinem Timer0-Tutorial auf dieses Register gestoßen. Hier erfahren Sie, wie Sie geeignete Bits einstellen, um den Timer im PWM-Modus auszuführen.
TCCR0-Register
Wir werden nur die Bits diskutieren, die uns jetzt interessieren.
- Bit 6,3 – WGM01:0 – Waveform Generation Mode – Diese Bits können entweder auf „00“ oder „01“ eingestellt werden, abhängig von der Art der PWM, die Sie erzeugen möchten. Hier ist die Nachschlagetabelle.
Wellenform Generation Modus Bit Beschreibung
- Bit 5,4 – COM01: 0 – Compare Match Ausgabemodus – Diese Bits werden gesetzt, um das Verhalten des Ausgangsvergleichs-Pins (OC0, Pin 4 in ATMEGA32) gemäß den WGM01: 0-Bits zu steuern. Die folgende look up tabelle bestimmen die operationen von OC0 pin für Schnelle PWM modus.
Vergleichen Ausgang Modus, Schnelle PWM Modus
Jetzt können haben einen blick auf die Schnelle PWM wellenformen. Eine detaillierte Erklärung finden Sie in meinem vorherigen Tutorial.
Schnelle PWM
Jetzt lassen sie mich erinnern sie, dass die AVR PWM ist voll hardware gesteuert, was bedeutet, dass auch die timer vergleichen betrieb ist getan durch die AVR CPU. Alles, was wir tun müssen, ist, der CPU mitzuteilen, was zu tun ist, sobald eine Übereinstimmung auftritt. Hier kommen die COM01:0 Pins ins Spiel. Wir sehen, dass durch Einstellen auf „10“ oder „11“ der Ausgangspin OC0 entweder gesetzt oder gelöscht wird (mit anderen Worten, er bestimmt, ob sich die PWM im invertierten Modus oder im nicht invertierten Modus befindet).
Ähnlich für phasenkorrekte PWM, die Nachschlagetabelle und die Wellenformen gehen so.
Vergleichen Ausgang Modus, Phase Richtige PWM Modus
Phase Richtige PWM
Auch hier, einstellung COM01: 0 zu „10“ oder „11“ bestimmt das verhalten von OC0 pin. Wie in den Wellenformen gezeigt, gibt es zwei Instanzen – eine während des Aufwärtszählens und eine andere während des Abwärtszählens. Das Verhalten ist in der Nachschlagetabelle klar beschrieben.
Bitte beachten sie, dass OC0 ist ein ausgang pin. Daher kommen die Effekte von WGM und COM nur ins Spiel, wenn das DDRx-Register richtig eingestellt ist. Weitere Informationen finden Sie in diesem Tutorial.
- Bit 2:0 – CS02:0 – Clock Select Bits – Diese Bits werden bereits im Timer0 Tutorial besprochen.
OCR0 – Output Compare Register
In meinem Timer0-Tutorial sind wir sogar auf dieses Register gestoßen. Wir verwenden dieses Register, um den Vergleichswert zu speichern. Wenn wir jedoch Timer0 im PWM-Modus verwenden, fungiert der darin gespeicherte Wert als Tastverhältnis (offensichtlich!). In der Problemstellung wird angenommen, dass das Tastverhältnis 45% beträgt, was bedeutet
OCR0 = 45% of 255 = 114.75 = 115
Und das war’s! Jetzt sind wir bereit, einen Code dafür zu schreiben! 🙂
Bearbeiten: Hinweis
Der folgende Code beschreibt, wie ein PWM-Signal mit einem gewünschten Tastverhältnis erstellt wird. Wenn Sie die Frequenz ändern möchten, müssen Sie den OBEREN Wert ändern, was mit dem ICRx-Register erfolgen kann (das von 8-Bit-Timern nicht unterstützt wird). Für 16-bit Timer1, es kann variiert werden mit ICR1A. ICH werde diskutieren über diese bald, wenn wir diskutieren über servo control.
Code
Also hier geht der Code. Weitere Informationen zu E / A-Port-Vorgängen in AVR finden Sie hier. Um mehr über Bitmanipulationen zu erfahren, sehen Sie sich dies an. Weitere Informationen zur Verwendung von AVR Studio 5 finden Sie hier. Um zu erfahren, wie dieser Code strukturiert ist, sehen Sie sich den vorherigen TIMER0-Beitrag an.
#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; }}
Problemstellung
Nehmen wir nun eine weitere Problemstellung. Dieser wird im Gegensatz zum vorherigen eher ein praktisches Zeug sein!
Nehmen wir den traditionellen LED-Blinker, bei dem eine LED mit einer bestimmten Frequenz blinken muss. Aber hey, warte, haben wir es nicht schon lange in diesem Beitrag besprochen (gegen Ende nach unten scrollen)? Hmm, also lasst es uns modifizieren, um PWM zu integrieren. Im Gegensatz zum herkömmlichen LED-Blinker (bei dem die LEDs entweder EIN- oder ausgeschaltet sind) leuchtet er bei maximaler Helligkeit und verringert dann langsam seine Helligkeit, bis er Null erreicht, und erhöht dann seine Helligkeit langsam wieder, bis er maximal wird.
Analyse und Code
Wie machen wir das? Ja, Sie haben es richtig erraten! Verringern Sie das Tastverhältnis langsam von 255 auf Null und erhöhen Sie es dann von Null auf 255. Je nach Tastverhältnis variiert die an die LED angelegte Spannung und damit die Helligkeit. Die folgende Formel gibt die Beziehung zwischen Spannung und Tastverhältnis an.
Also hier geht der Code. Ich werde es nicht erklären, Sie können es selbst entschlüsseln. Weitere Informationen zu E / A-Port-Vorgängen in AVR finden Sie hier. Um mehr über Bitmanipulationen zu erfahren, sehen Sie sich dies an. Weitere Informationen zur Verwendung von AVR Studio 5 finden Sie hier. Um zu erfahren, wie dieser Code strukturiert ist, sehen Sie sich den vorherigen TIMER0-Beitrag an.
// 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 }}
Hier endet also mein weiteres lang erwartetes und langes Tutorial! Nächstes.. Serielle Kommunikation! Wir sehen uns!! 🙂
Und ja, wenn Sie Vorschläge, Zweifel, konstruktive Kritik usw. haben, können Sie gerne unten eine Notiz hinterlassen! Abonnieren Sie meinen Blog oder nutzen Sie die RSS-Feeds, um auf dem Laufenden zu bleiben!