Ein PWM-Timer auf zwei Ausgänge?

erster Beitrag hier, also sorry für das schlechte Styling.

Ich arbeite an einem Gruppenprojekt und verwende einen Atmega328, ohne auf Einzelheiten einzugehen. Ich muss zwei Lüfter, zwei Servos und 1 CTC-basierte Funktion von 3 Timern ansteuern. Zu meiner Frage dann; Es gibt die Register OCRx A und B, die ich verwende, um die Lüfter mit variablen Geschwindigkeiten anzutreiben, und eine ähnliche Methode für die Servos. Beim Einrichten eines sehr einfachen PWM-Skripts habe ich festgestellt, dass beide Register einen Lüfter antreiben. Wenn ich jedoch versuche, eines der Register unabhängig voneinander zu verwenden (Lüfter 1 ein, 2 aus, dann ändern), erzeugt OCRxB keine Ausgabe.

Hier ist ein Ausschnitt meines Setup-Codes:

void setup_timers () {
// Set Timer0 to Fast PWM mode
TCCR0A |= (1 << WGM01) | (1 << WGM00);

// Set Timer0 to clear OCR0 A/B on match
TCCR0A |= (1 << COM0A1) | (1 << COM0A0) | (1 << COM0B1) | (1 << COM0B0);
TCCR0B |= (1 << CS01);
TCNT0 = 0;

// Set OCR0 A/B
DDRD = 0xFF;}

Idealerweise werden dadurch beide Register in Timer 0 für Ausgänge eingerichtet. Ich sende dieses Signal an einen Transistor, der dann die Lüfter antreibt, wie durch dieses nächste Bit bestimmt:

int main () {
setup_timers();

while (1) {

    /* Timer 0; OCR0A (i = 0, L), OCR0B (i = 1, R), i = 2 denotes both fans */

    for (uint8_t i = 0; i <= 3; i++) {
        // Cycle Fans Left/Right/Both

        for (uint8_t j = 1; j <= 4; j++) {
            // Cycle Speed 

            if (i == 2) {
                // Check motor; run timer 0 A/B as required
                OCR0A = 0xFF * j / 4;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
            else if (i == 1) {
                OCR0A = 0xFF * j / 4;
                OCR0B = 0;
                _delay_ms(2000);
            }
            else if (i == 0) {
                OCR0A = 0;
                OCR0B = 0xFF * j / 4;
                _delay_ms(2000);
            }
        }
    }
}
return 1; }

Jetzt weiß ich, dass dies besser gemacht werden könnte, dieses Segment hat einen Haufen anderer Tests, die ausgeschnitten wurden. Das Hauptproblem hier ist, dass Lüfter 1 läuft (gesteuert von OCR0A), Lüfter 2 jedoch vollständig ignoriert wird. Ich habe das Datenblatt ein paar Mal durchgelesen, aber es war etwas unklar, ob dies tatsächlich möglich ist, es stand etwas darüber, dass OCRxB nicht gespeichert wird, und ich habe andere Beiträge gesehen, die danach fragten. Soweit ich weiß, handelt es sich um unabhängige Register, aber ist es möglich, dass OCRxA beibehalten wird, aber nach dem Schreiben von OCRxB nicht mehr ausgeführt werden kann?

Oder vielleicht liege ich auch komplett falsch, wäre sicherlich nicht das erste Mal.

Ihre Ausgangspins teilen möglicherweise nicht denselben Timer. Überprüfen Sie die Timer-Ausgangszuordnung zu den von Ihnen verwendeten physischen Pins.
Es sollte funktionieren. Sind Sie sicher, dass der Lüfter an (PCINT21/OC0B/T1) PD5- Pin angeschlossen ist ? Übrigens: Ich denke, die äußere Schleife sollte nur zu <3 gehen und 0xFF * j / 4 liefert wahrscheinlich nicht 0xFF für j=4, sondern 252 .
@TMa - Vielleicht möchten Sie Ihren Kommentar zu "j" überdenken. Wenn j=4 und beachtet wird, dass 0xFF=255 ist, folgt daraus, dass (255*4)/4 == 255, nicht 252.
Ich denke, es ist als 255/4 * j=63.75-->63 * j kompiliert . Und zur Laufzeit ausgewertet nur 63 * j , also 63 * 4 = 252 . Hängt zumindest von der Compiler-Optimierung ab. Aber es ist kein wichtiges Thema im Test. Tut mir leid, wenn ich falsch liege.
@KyranF, mir ist bekannt, dass es zwei Ausgangspins gibt. Ich konnte jeden dazu bringen, ausschließlich von OCR0A aus zu arbeiten, aber OCR0B (D6) funktioniert nicht unabhängig von OCR0A (D5).
@Oman sie können auch verschiedene Timer verwenden!
@KyranF, was meinst du? Ich habe 3 Timer, und ich benutze sie alle, aber ich muss die Dinge auf einen Timer stellen.
Wissen Sie, ob beide PWM-Peripheriegeräte Timer 0 verwenden können?
@KyranF das ist das Problem. Ich kann PWM über beide Ports ausführen, indem ich OCR0B nicht setze, aber wenn ich versuche, OCR0B und alle Com-Register usw. einzustellen, gibt es nichts vom Ausgangspin. Weißt du als Worst-Case-Szenario, ob ich ein Servo mit Digital betreiben kann, nicht mit PWM?
Ja, natürlich. Ich würde niemals ein Servo mit PWM betreiben, es ist völlig ungeeignet. Wenn Sie Arduino nicht bereits für Ihr Atmega328-Projekt verwenden, können Sie zumindest sehen, wie sie es tun, und den C-Code zurückentwickeln. arduino.cc/en/reference/servo Sie verwenden Timer und ISRs (ich denke, ansonsten einfache Zeitmessung und Verwendung der verstrichenen Zeit, um die Ausgangsänderungen vorzunehmen), um den digitalen Ausgangspin angemessen zu warten. Dies ist weitaus besser als PWM, bei dem es mit einer 8-Bit-Auflösung und einem festgefahrenen Zeitrahmen von 1 bis 2 ms sehr schwierig ist, PWM mit niedriger Geschwindigkeit zu erhalten. Bei 500 Hz haben Sie nur 10 Positionen für 8-Bit-PWM
(Fortsetzung von der Sache mit den 10 Positionen) Da Sie von 1/50 Sekunde auf 2/50 Sekunde gehen, ist Ihr Servobefehlsfenster, und 500 Hz PWM gibt Ihnen 0-255 Schritte in 1/500 Sekunde. Also 10/500 und 20/500 haben Sie eine sehr granulare Positionskontrolle. Natürlich können Sie die PWM-Frequenz hochdrehen, aber ja ... machen Sie es einfach mit einfachen Timern und digitalem IO, im Land der Mikrocontroller ist es nicht schwer, alle 20 ms etwas zu tun
Hm, Timer sind eine ziemlich gute Idee, wie könnte ich zwei CTCs von einem Timer ausführen? Im Moment ist mein Zeitfenster sehr klein, daher wäre etwas Einfaches super. Danke für die ganze Hilfe Mann.
Oh, und ich verwende Atmel Studio, betreibe einen eigenen Atmega328 und schließe ihn an eine benutzerdefinierte Schaltung an.

Antworten (1)

Es scheint, dass es ein Problem mit den vorgegebenen Werten gab, durch Einstellen von COM1A0 und COM1B0 kann Timer 1 so konfiguriert werden, dass er unabhängige Signale gibt. Dies ist der Setup-Code, der funktioniert:

void setup_timers () {

TCCR1A |= (1 << WGM11) | (1 << WGM10);
TCCR1A |= (1 << COM1A1) | (1 << COM1A0) | (1 << COM1B1) | (1 << COM1B0);
TCCR1B |= (1 << CS11);
TCNT1 = 0;

DDRB = 0xFF;}

Ich habe immer noch Probleme damit, die Motoren richtig zu steuern, aber die Servos funktionieren gut, und es kann getan werden.

Hinweis: Timer 1 hat tatsächlich zwei getrennte Ausgänge, Timer 0/2 jedoch nicht. Um zwei Ausgänge zu erhalten, müssen Sie WGMx2 und die COMxB 0/1-Register umschalten, wenn WGMx2 eingestellt ist, wird OCRxA nicht richtig ausgegeben, OCRxB jedoch mit etwas Fummelei. Wenn WGMx2 nicht eingestellt ist, wird OCRxB deaktiviert und OCRxA funktioniert normal. Um unabhängige Ausgaben zu erhalten, müssen Sie dies berücksichtigen.