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). TIMER0
Erzeugt ein Taktsignal mit eingeschaltetem Fast PWM OC0B
und 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;
TIMER2
dreht eine Datenleitung, um alle 256 TIMER0
Zyklen 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
TIMER2
ruft 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 fLatch
des 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):
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, fLatch
wird 1
innerhalb der main()
Routine eingestellt. Und bald danach TIMER0
beschleunigt 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.
sendSerial()
ist mein Code, der Daten über SPI sendet: Er berührt nicht dieTCCR
(Timer-Steuer-) Register.Antworten:
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
quelle