Beim Betrieb mit Clock Prescaler von 64 auf ATmega328 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 Fast PWM on OC0B
und ist wie folgt aufgebaut:
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 Zyklen einen Austastimpuls zu erzeugen, TIMER0
und ist wie folgt aufgebaut:
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 eine ISR bei Überlauf auf (alle 256 Zyklen). Der ISR erzeugt manuell einen Blanking-Impuls und ggf. einen Latch-Impuls:
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 sieht die Schleife in der main()
Funktion aus: Senden Sie einige serielle Daten, warten Sie, bis ISR sich um das Latchen kümmert, und wiederholen Sie es dann:
for (;;) {
if (!fLatch) {
sendSerial();
fLatch = 1;
_delay_ms(1);
}
nop();
}
sendSerial()
sendet einige SPI ( Code auf Pastebin der Kürze halber ). Mein Problem ist, dass der Zeitgeber nach sendSerial()
Abschluss, während er darauf wartet , dass er auf niedrig (verarbeitet) eingestellt wird, beschleunigt wird. fLatch
Hier ist die Spur des Logikanalysators (ich habe die Bereiche herausgeschnitten, in denen dasselbe Signal die Grafik weiterhin 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 Takt wie erwartet vor sich hin. Genau dort, wo sich die Lücke im Bild befindet, fLatch
wird die Routine auf 1
innen gesetzt. main()
Und bald darauf TIMER0
um etwa den Faktor 4 beschleunigt. Schließlich werden der Austastimpuls und der Verriegelungsimpuls ausgeführt (Kanäle 3 und 4, rechtes Drittel des Bildes), und jetzt nimmt der Taktimpuls wieder seine reguläre Frequenz an, und serielle Daten sind erneut gesendet. Ich habe versucht, die delay_ms(1);
Leitung in herauszunehmen main()
, aber es werden die gleichen Ergebnisse 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 das ist: Ich experimentiere mit der Steuerung des LED-Treibers TLC5940 : Diese Chips erfordern einen externen Takt plus einen Reset am Ende des Taktzyklus.
Für eine schnelle Fehlerbehebung würde ich versuchen, dasselbe mit der Arduino-Bibliothek für TLC5940 zu tun und zu sehen, ob es schnell wird oder nicht. Wenn es mit der Bibliothek funktioniert, können Sie seine Quelle überprüfen und mit Ihrer vergleichen. Da Sie mit AVR vertraut sind, sollten Sie die Arduino-Quelle problemlos in nativen AVR konvertieren.
Nur für den Fall, dass Sie nicht wissen, wie Sie kompilierte Arduino-Skizzen in AVR hochladen: Wenn Sie Ihre Skizze kompilieren, wird eine Hex-Datei erstellt (Sie können den genauen Speicherort der Datei sehen, indem Sie den ausführlichen Modus in den Einstellungen aktivieren). Sie können dieses Hex mit Ihrem bevorzugten Programmierer auf Ihren AVR hochladen.
Ich hoffe es hilft
Blup1980
Engelgroß
Blup1980
PeterJ
Engelgroß
sendSerial()
ist mein Code, der Daten über SPI sendet: Er berührt nicht dieTCCR
(Timer-Steuerungs-) Register.Blup1980
Engelgroß