Wie lösche ich OC1A und OC1B manuell?

Wie kann ich auf dem Arduino Uno (= ATmega328/P ) das Ausgangsvergleichssignal des Timers 1 manuell löschen?

Der Zweck des Codes besteht darin, nach einem genauen Intervall vom Eingangsimpuls einige Ausgangsimpulse zu erzeugen. Es gibt zwei Ausgangskanäle, die angesteuert werden müssen, möglicherweise mit unterschiedlichen Verzögerungen. Die Impulslänge ist nicht kritisch, sie muss nur mindestens 8 µs lang sein, aber das Timing für die ansteigende Flanke muss genau sein.

Aktuell sieht mein Code so aus:

#define MAINS_PERIOD 16667 // in microseconds

ISR(TIMER1_CAPT_vect) {
    GTCCR = _BV(TSM);
    TCNT1 = TCNT1 - ICR1 - MAINS_PERIOD + 1 + 300;
    GTCCR = 0;
}
ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);
    OC1A = 0; // <---------- what do I do here?
}
ISR(TIMER1_COMPB_vect) {
    delayMicroseconds(8);
    OC1B = 0; // <---------- what do I do here?
}

void setup() {
    TCCR1A = _BV(COM1A0) | _BV(COM1A1) | _BV(COM1B0) | _BV(COM1B1); // set output on compare match, normal mode, prescaler /8
    TCCR1B = _BV(CS11) | _BV(ICNC1) | _BV(ICES1); // input capture noise cancel, positive edge
    TIMSK1 = _BV(ICIE1) | _BV(OCIEA1), | _BV(OCIEB1); // generate interrupts for input capture, A/B compare match

    pinMode(8, INPUT);  //set data direction
    pinMode(9, OUTPUT); 
    pinMode(10, OUTPUT);
}

Ich könnte möglicherweise den Ausgabemodus des Timers neu konfigurieren, um ihn bei Übereinstimmung zu löschen und dann eine Übereinstimmung zu erzwingen, aber es scheint, als müsste es einen einfacheren Weg geben, dies zu tun:

ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);

    TCCR1A &=~ _BV(COM1A0);
    TCCR1C = _BV(FOC1A);
    TCCR1A |= _BV(COM1A0);
}

Das bloße Löschen des Ausgangs-Pin-Registerbits ( PORTB &=~ _BV(PORTB1);) funktioniert in diesem Fall nicht, da das Port-Register ignoriert wird, wenn eine alternative Funktion (wie der Timer-Ausgang) auf dem Pin aktiviert ist.

Alternativ wäre es noch besser, wenn es eine Möglichkeit gäbe, die Timer-Ausgänge bei Überlauf zu löschen (eine Art undokumentierter 16-Bit-PWM-Modus?), Aber ich bezweifle, dass es eine Möglichkeit gibt, dies zu tun.

Mir ist nicht ganz klar, was Sie hier erreichen wollen. Möchten Sie einen Einzelimpuls nach einem externen Trigger? Oder eine Impulsfolge nach einem externen Trigger? Oder nochmal was anderes? Wie oft möchten Sie diesen/diese Puls/e?

Antworten (2)

Es ist möglich, den Timer so zu konfigurieren, dass er OC1A umschaltet

TCCR1A |= (1 << COM1A0);

Verwenden Sie innerhalb der Interrupt-Routine das Steuerregister, um ein zusätzliches Vergleichsereignis auszulösen. Dies hat KEINE Nebeneffekte wie das Ändern des Timerwerts.

TCCR1C |= (1 << FOC1A);

Um sofort zu ändern, stellen Sie den Typ auf den Lösch- oder Set-Modus und lösen Sie eine Änderung aus

TCCR1A = (1 << COM1A0);
TCCR1C |= (1 << FOC1A);

siehe https://www.sparkfun.com/datasheets/Components/SMD/ATMega328.pdf S.137

ISR(TIMER1_COMPA_vect) {
    delayMicroseconds(8);
    OC1A = 0; // <---------- what do I do here?
}

Die Verzögerungen im ISR zu haben, wäre nicht zu gut für die Codierung.

Mehrere Möglichkeiten, dies zu tun. und hier ist einer:

1) Zeitbasis einrichten, Timer noch nicht starten;

2) Richten Sie den Vergleich für den gewünschten Ausgang ein, Interrupt aktiviert. OC1A/B-Pin je nach Bedarf gesetzt/gelöscht;

3) Richten Sie den Interrupt ein, um Ihren Eingangsimpuls zu beobachten.

4) im Eingangsimpuls isr den Timer starten;

5) im Ausgangsvergleichs-ISR den OC1A/B-Pin löschen/setzen, den Ausgangsvergleichs-Interrupt deaktivieren und den Timer zurücksetzen/deaktivieren.

Bearbeiten: Es sollte beachtet werden, dass der obige Ansatz eine geringfügige Menge an ISR-Overhead annimmt. Bei kurzen Laufzeiten oder langsamen Prozessoren ist dies möglicherweise nicht der Fall.

Meine Frage ist, wie man OC1A/B überhaupt löscht. Ich weiß, dass die Verwendung einer Verzögerung in einem Interrupt eine schlechte Idee ist, und ich werde das später ändern, aber ich brauche eine Möglichkeit, die Ausgangsbits tatsächlich zu löschen.