AVR-Timer-Beschleunigung auf ATmega328

9

Wenn ich auf ATmega328 mit einem Clock Prescaler von 64 laufe, beschleunigt einer meiner Timer aus unbekannten Gründen zu einem bestimmten Zeitpunkt in der Ausführung.

Ich verwende zwei Timer auf ATmega328, um die von TLC5940 benötigte Taktung zu erzeugen (siehe unten, warum; dies ist für die Frage unerheblich). TIMER0Erzeugt ein Taktsignal mit eingeschaltetem Fast PWM OC0Bund ist wie folgt eingerichtet:

TCCR0A = 0
    |(0<<COM0A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM0A0)    // 
    |(1<<COM0B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM0B0)
    |(1<<WGM01)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(1<<WGM00)
    ;
TCCR0B = 0
    |(0<<FOC0A)     // Force Output Compare A
    |(0<<FOC0B)     // Force Output Compare B
    |(1<<WGM02)     // Bit 3 – WGM02: Waveform Generation Mode
    |(0<<CS02)      // Bits 2:0 – CS02:0: Clock Select
    |(1<<CS01)
    |(0<<CS00)      // 010 = clock/8
    ;
OCR0A = 8;
OCR0B = 4;
TIMSK0 = 0;

TIMER2dreht eine Datenleitung, um alle 256 TIMER0Zyklen einen Austastimpuls zu erzeugen, und ist wie folgt eingerichtet:

ASSR = 0;
TCCR2A = 0
    |(0<<COM2A1)    // Bits 7:6 – COM0A1:0: Compare Match Output A Mode
    |(0<<COM2A0)    // 
    |(0<<COM2B1)    // Bits 5:4 – COM0B1:0: Compare Match Output B Mode
    |(0<<COM2B0)
    |(0<<WGM21)     // Bits 1:0 – WGM01:0: Waveform Generation Mode
    |(0<<WGM20)
    ;
TCCR2B = 0
    |(0<<FOC2A)     // Force Output Compare A
    |(0<<FOC2B)     // Force Output Compare B
    |(0<<WGM22)     // Bit 3 – WGM02: Waveform Generation Mode
    |(1<<CS22)      // Bits 2:0 – CS02:0: Clock Select
    |(0<<CS21)
    |(0<<CS20)      // 100 = 64
    ;
OCR2A = 255;
OCR2B = 255;
TIMSK2 = 0
    |(1<<TOIE2);    // Timer/Counter0 Overflow Interrupt Enable

TIMER2ruft bei Überlauf einen ISR auf (alle 256 Zyklen). Der ISR erzeugt manuell einen Austastimpuls und gegebenenfalls einen Verriegelungsimpuls:

volatile uint8_t fLatch;

ISR(TIMER2_OVF_vect) {
    if (fLatch) {
        fLatch = 0;
        TLC5940_XLAT_PORT |=  (1<<TLC5940_XLAT_BIT);        // XLAT -> high
        for (int i=0;i<10;i++)
            nop();
        TLC5940_XLAT_PORT &= ~(1<<TLC5940_XLAT_BIT);        // XLAT -> high
    }
    // Blank
    TLC5940_BLANK_PORT |= (1<<TLC5940_BLANK_BIT);
    for (int i=0;i<10;i++)
        nop();
    TLC5940_BLANK_PORT &= ~(1<<TLC5940_BLANK_BIT);
}

Die nop()Verzögerung im obigen Code dient nur dazu, den Impuls auf der Spur des Logikanalysators deutlicher zu machen. So main()sieht die Schleife in der Funktion aus: Senden Sie einige serielle Daten, warten Sie, bis ISR sich um das Latching gekümmert hat, und wiederholen Sie den Vorgang:

for (;;) {
    if (!fLatch) {
        sendSerial();
        fLatch = 1;
        _delay_ms(1);
    }
    nop();
}

sendSerial()führt einige SPI-Sendungen aus ( der Kürze halber Code auf dem Pastebin ). Mein Problem ist, dass nach sendSerial()Abschluss fLatchdes Vorgangs der Taktgeber beschleunigt wird , während darauf gewartet wird , dass er auf niedrig (verarbeitet) gesetzt wird. Hier ist die Spur des Logikanalysators (ich habe die Bereiche herausgeschnitten, in denen das gleiche Signal die Grafik weiter verkleinert):

Geben Sie hier die Bildbeschreibung ein

Auf der linken Seite zeigen die Kanäle 0 und 1 das Ende der gesendeten SPI-Daten. Ebenfalls links auf Kanal 4 sehen Sie einen Austastimpuls. Auf Kanal 2 tuckert der Taktimpuls wie erwartet mit. Genau dort, wo sich die Lücke im Bild befindet, fLatchwird 1innerhalb der main()Routine eingestellt. Und bald danach TIMER0beschleunigt sich um etwa den Faktor 4. Schließlich werden der Austastimpuls und der Verriegelungsimpuls ausgeführt (Kanäle 3 und 4, rechtes Drittel des Bildes), und jetzt nimmt der Taktimpuls seine reguläre Frequenz wieder auf, und serielle Daten sind erneut gesendet. Ich habe versucht, die delay_ms(1);Leitung herauszunehmen main(), aber die gleichen Ergebnisse werden erzielt. Was ist los? Ich sollte beachten, dass der ATmega von einem 20-MHz-Kristall getaktet und dann mit dem folgenden Code um das 64-fache verlangsamt wird:

CLKPR = 1<<CLKPCE;
CLKPR = (0<<CLKPS3)|(1<<CLKPS2)|(1<<CLKPS1)|(0<<CLKPS0);

Wofür dies ist: Ich experimentiere mit der Steuerung des LED-Treibers TLC5940 : Diese Chips erfordern eine externe Uhr plus einen Reset am Ende des Taktzyklus.

Angelatlarge
quelle
Wenn Sie einen Debugger haben, versuchen Sie, Ihren Code zu stoppen, wenn der Timer zu schnell ist, und lesen Sie das Konfigurationsregister dieses Timers zurück. Wenn Sie den mit dem falschen Wert gefunden haben, lösen Sie einen Haltepunkt bei dieser Registeränderung aus und sehen Sie, welcher Teil Ihres Codes falsch funktioniert. Ich vermute, dass sich das Problem in einer externen Bibliothek befindet, die Sie möglicherweise verwenden, und die diesen Timer für interne Dinge wie Verzögerungen verwendet.
Blup1980
Zwei Probleme: a) Ich habe keinen JTAG-Programmierer, daher habe ich keine Möglichkeit, den Chip zu debuggen. B) Ich ändere den Timer-Registerwert nach dem oben gezeigten Setup nie mehr, daher erwarte ich keine Timer-Registerwerte tatsächlich ändern. Ist das naiv?
Angelatlarge
1
Tatsächlich könnte eine von Ihnen verwendete Bibliothek die UART-Einstellungen ändern. Ich sehe, dass Sie eine sendSerial () -Funktion verwenden. Ist es Teil Ihres Codes oder eine externe Bibliothek? Möglicherweise ändern nicht Sie die Einstellungen, sondern ein Teil des Codes in einer aufgerufenen Bibliothek. Ich schlage vor, dass Sie Ihre serielle Schnittstelle verwenden, um die Konfigurationsparameter auszugeben und herauszufinden, was geändert wurde. Sie können auch die Quelle der verwendeten Bibliotheken (falls vorhanden) anzeigen und sicherstellen, dass sie diesen Timer nicht verwenden.
Blup1980
1
Abgesehen von dem, was @ Blup1980 vorgeschlagen hat, ist es möglicherweise einen Versuch wert, den TLC5940 zu entfernen, um sicherzustellen, dass er nichts Seltsames mit der Taktleitung macht.
PeterJ
@ Blup1980 Ich bin mir nicht sicher, ob ich die Relevanz von UART sehe: Ich verwende USART nicht für SPI, sondern nur die "regulären" SPI-Einrichtungen. sendSerial()ist mein Code, der Daten über SPI sendet: Er berührt nicht die TCCR(Timer-Steuer-) Register.
Angelatlarge

Antworten:

1

Für ein schnelles Debugging würde ich versuchen, dasselbe mit Arduino Library für TLC5940 zu tun und zu sehen, ob es schnell wird oder nicht. Wenn es mit der Bibliothek funktioniert, können Sie die Quelle überprüfen und mit Ihrer vergleichen. Da Sie mit AVR vertraut sind, sollten Sie die Arduino-Quelle problemlos in native AVR konvertieren.

Nur für den Fall, dass Sie nicht wissen, wie kompilierte Arduino-Skizzen in AVR hochgeladen werden sollen: Wenn Sie Ihre Skizze kompilieren, wird eine Hex-Datei erstellt (Sie können den genauen Speicherort der Datei anzeigen, indem Sie den ausführlichen Modus in den Einstellungen aktivieren). Sie können dieses Hex mit Ihrem Lieblingsprogrammierer auf Ihren AVR hochladen.

Ich hoffe es hilft

Superkeci
quelle