Verwenden Sie AVR Timer1 gleichzeitig zum Vergleichen von Interrupt UND PWM

Ist es möglich, Timer1 auf einem ATMega zu verwenden, um einen Vergleichs-Interrupt auf OCR1A und einen PWM-Ausgang auf OCR1B zu erzeugen? Ich habe es ähnlich wie folgt versucht, aber beides funktioniert nicht mit allem, was ich versucht habe.

TCCR1A = _BV(COM1B1) | _BV(WGM11) | _BV(WGM10);
TCCR1B = PRESCALE1_8;
OCR1B = 0;


// set compare match register for 1000Hz 
OCR1A = 2000;
// turn on CTC mode
TCCR1B |= _BV(WGM12);

Nebenbemerkung: Ich weiß, dass dies einer anderen meiner eigenen Fragen sehr ähnlich ist , aber das betraf PWM bei einem Timer und Interrupts bei einem anderen. Diesmal habe ich Probleme, beide aus dem GLEICHEN Timer zu bekommen.

Antworten (1)

Ihr Code setzt den Zähler für den phasenrichtigen PWM-Betrieb mit dem Höchstwert 0x3FF, sodass das erste Problem, mit dem Sie konfrontiert werden, darin besteht, dass die Interrupts zum Vergleichen von Übereinstimmungen aufgrund der Funktionsweise dieses Modus, der nach oben zählt, nicht in gleichen Intervallen ausgelöst werden dann Richtung ändern und rückwärts zählen.

Das andere Problem ist, dass der Zähler bis 1023 (0x3ff) und zurück auf 0 zählt, sodass es keine Möglichkeit gibt, jemals einen Match-Interrupt für einen Wert von 2000 (OCR1A = 2000) zu erhalten.

phasenrichtiger PWM-Betrieb:

Geben Sie hier die Bildbeschreibung ein


Dies ist ein Mindestcode für Mega328, der bei 16 MHz läuft, der Timer 1 so einstellt, dass er eine schnelle PWM mit einer Frequenz von etwa 1 kHz erzeugt (Frequenz von ICR1 eingestellt und Tastverhältnis von OCR1B eingestellt) und auch einen Timer 1-Überlauf-Interrupt mit einer Rate von 1 kHz ausgibt

#include <avr/io.h>
#include <avr/interrupt.h>

// Timer1 overflow interrupt service routine
ISR(TIMER1_OVF_vect)
{
    PORTB ^= 1; // invert PORTB.0
}

int main(void)
{

    // Port B initialization
    // Function: Bit7=In Bit6=In Bit5=In Bit4=In Bit3=In Bit2=Out Bit1=In Bit0=Out
    DDRB = 0x05;

    // Timer/Counter 1 initialization
    // Clock source: System Clock
    // Clock value: 2000,000 kHz
    // Mode: Fast PWM top=ICR1
    // OC1A output: Disconnected
    // OC1B output: Non-Inverted PWM
    // Noise Canceler: Off
    // Input Capture on Falling Edge
    // Timer Period: 1,0245 ms
    // Output Pulse(s):
    // OC1B Period: 1,0245 ms Width: 0,12806 ms
    // Timer1 Overflow Interrupt: On
    // Input Capture Interrupt: Off
    // Compare A Match Interrupt: Off
    // Compare B Match Interrupt: Off
    TCCR1A = (0 << COM1A1) | (0 << COM1A0) | (1 << COM1B1) | (0 << COM1B0) | (1 << WGM11) | (0 << WGM10);
    TCCR1B = (0 << ICNC1) | (0 << ICES1) | (1 << WGM13) | (1 << WGM12) | (0 << CS12) | (1 << CS11) | (0 << CS10);
    TCNT1 = 0;
    ICR1 = 2048;
    OCR1A = 0;
    OCR1B = 256;

    // Timer/Counter 1 Interrupt(s) initialization
    TIMSK1 = (0 << ICIE1) | (0 << OCIE1B) | (0 << OCIE1A) | (1 << TOIE1);

    // Global enable interrupts
    sei();

    while(1)
    {
        // Place your code here

    }
}
Könnte ich stattdessen Fast PWM verwenden? Nicht, dass ich mir ganz sicher wäre, wie das geht :P
@AdamHaile Hängt davon ab, was Sie tun möchten, wenn Sie gleiche Intervalle für die Vergleichsübereinstimmung erhalten möchten, dann ja, wenn Sie nur einen höheren Spitzenwert einstellen möchten (was zu einer niedrigeren PWM-Frequenz führt), können Sie beide Modi mit verwenden ein benutzerdefinierter Höchstwert, der für das ICR1-Register festgelegt wird, indem der entsprechende PWM-Modus ausgewählt wird (unter Verwendung der WGM-Registerwerte)
Ja ... möchte auf jeden Fall gleiche Intervalle. Der Vergleichsabgleich dient zum Multiplexen zwischen verschiedenen LEDs unter Verwendung der PWM-Signale. Könntest du vielleicht einen Beispielcode zeigen?
@AdamHaile, was ist Ihre spezifische MCU? Und was ist mit der PWM-Frequenz? PWM kann einmal pro Periode einen Timer-Überlauf-Interrupt geben (TOV1-Flag), vielleicht können Sie das für Ihre Aktualisierung verwenden
ATMega328P-PU. Ich habe keine Ahnung, was die PWM-Frequenz ist. Wurde 8 Prescaler verwendet, also 2MHz? Ich versuche, einen Interrupt bei ungefähr 1000 Hz zu bekommen.
@AdamHaile Im Phasenkorrektur- oder Phasen- und Frequenzkorrekturmodus mit top = 0x3ff ist die Ausgangs-PWM-Frequenz = Timer-Takt / (1023 * 2) also 2 MHz / 2046 = 917 Hz, wenn Sie den TOV1-Flag (Überlauf-Flag) verwenden Interrupt können Sie erhalten Unterbrechungen mit dieser Rate
Ich bin verwirrt darüber, was Sie mit top=0x3ff meinen, welches Register ist das? Könntest du vielleicht ein Beispiel zeigen?
@AdamHaile Das ist kein Register, es ist der Spitzenwert der PWM, der Modus, den Sie in Ihrem ursprünglichen Code ausgewählt haben, ist TCCR1A = _BV(WGM11) | _BV(WGM10);PWM, Phase Correct, 10-Bit , diese 10 Bit bedeuten, dass der TOP-Wert 0x03FF ist (es steht im Datenblatt) und stellt tatsächlich die PWM-Frequenz ein.
Ahh... aber wie genau würde man den TOV1 ISR verwenden?
Randnotiz. Ich mache auch PWM an Timer/Zähler 2 ... werde ich ein Problem damit haben, dass die PWM die gleiche Frequenz/das gleiche Tastverhältnis hat?
@AdamHaile Aktivieren Sie einfach den Timer1-Überlauf-Interrupt TIMSK1=(1<<TOIE1);und fügen Sie den Interrupt-Handler hinzu. Er wird jedes Mal mit einer Rate von 917 Hz aufgerufen, wenn der Zähler den unteren Rand (0) erreicht. Timer2 ist unabhängig von Timer1, keiner der oben genannten Effekte wirkt sich darauf aus.
Habe jetzt folgendes, mache aber definitiv kein PWM: TCCR1A = _BV(WGM11) | _BV(WGM10); TCCR1B = PRESCALE1_8; OCR1B = 0; TIMSK1=_BV(TOIE1);
@AdamHaile, ich habe in der Antwort einen Mindestarbeitscode hinzugefügt
@AdamHaile Hast du den Code getestet?
@alexan_e, Gehen wir für diesen Code davon aus, dass eine Schaltung (z. B. Transistor) mit dem OCR1B-Pin verbunden ist?
@DavidNorman Der Code benötigt keine externe Komponente, um die Impulse bei PORTB.0 und OC1B zu erzeugen. Die erzeugten Impulse können dann jede vom Designer gewünschte Schaltung ansteuern (innerhalb der AVR-I/O-Spezifikationen).
@alexan_e, ich verwende einen Transceiver, um das Signal zu modulieren. Muss ich entweder die TX- oder die RX-Leitung am Transceiver an den OC1B-Pin anschließen? Im Moment habe ich es mit meinem RX0 und TX0 auf dem Mikrocontroller (atmega328p) verbunden. Gibt es eine Möglichkeit, weiterhin Signale von OC1B (intern, nicht extern) zum Ansteuern von RX0- und TX0-Pins zu verwenden? Bitte beachten Sie, dass ich nicht mehr seriell verwende, also sind RX0 und TX0 nur GPIOs
@DavidNorman Überprüfen Sie Abbildung 16.8 Seite 124 im m88-Datenblatt . Sie können ein Ereignis am oberen und unteren Rand der PWM erhalten, indem Sie die Ausgangsvergleichs- und Timer-Überlauf-Interrupts verwenden. Ich bin mir nicht sicher, was Sie tun, daher kann ich Ihnen nicht viel helfen. Vielleicht solltest du das als neue Frage posten.