Ich habe einen Arduino Nano mit einem 328P und brauche alle 6 PWM-Pins.
Daher musste ich den Prescaler und den WGM-Modus von Timer0 anpassen.
Es befindet sich jetzt im phasenkorrekten PWM-Modus mit einem Vorteiler von 1.
TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);
TCCR0B = _BV(CS00);
Jetzt brauche ich eine Arbeitszeitberechnung für andere Bibliotheken, aber da Timer0 diese Aufgabe hatte, ist jetzt alles außer Betrieb.
Ich habe versucht, die Verkabelung anzupassen. C.
// the prescaler is set so that timer0 ticks every 64 clock cycles, and the
// the overflow handler is called every 256 ticks.
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))
dazu
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(1 * 510))
Aber es ist, als hätte ich nichts geändert. (testete andere Einstellungen, die geändert wurden, damit sie neu kompiliert wurden)
Ganzer Code:
void setup() {
// Set Timer 0, 1 and 2
// Register A: Output A and B to non-inverted PWM and PWM mode to phase correct.
// Register B: Pre Scaler to 1.
TCCR0A = _BV(COM0A1) | _BV(COM0B1) | _BV(WGM00);
TCCR0B = _BV(CS00);
TCCR1A = _BV(COM1A1) | _BV(COM1B1) | _BV(WGM10);
TCCR1B = _BV(CS10);
TCCR2A = _BV(COM2A1) | _BV(COM2B1) | _BV(WGM20);
TCCR2B = _BV(CS20);
pinMode(8, OUTPUT);
}
void loop() {
digitalWrite(8, LOW);
delay(65000);
digitalWrite(8, HIGH);
delay(65000);
}
#define SCALE_UP(x) x<<6
und es dann so verwendendelay(SCALE_UP(1000))
Antworten:
Das Korrigieren der Zeitnehmungsfunktionen mit Ihren PWM-Einstellungen ist nicht so einfach. Sie sollten zumindest versuchen , neu zu schreiben
ISR(TIMER0_OVF_vect)
,micros()
und wahrscheinlichdelay()
. Hier ist warum:Erstens gibt es ein Rundungsproblem. Die Zeit wird mit zwei globalen Variablen gehalten:
Der erste ist, was
millis()
zurückkehrt. Der zweite zeichnet auf, wie viel Zeit seit der letzten vollen Millisekunde vergangen ist, und zwar in Einheiten von 8 µs. Die beiden Variablen werden folgendermaßen inkrementiertISR(TIMER0_OVF_vect)
:Bei einer normalen Uno-Konfiguration wird der ISR alle 1024 µs aufgerufen. Dann
MILLIS_INC
ist 1 undFRACT_INC
ist 3. Bei Ihrer Timer-Konfiguration wird der ISR alle 31,875 µs (510 Zyklen) aufgerufen,MILLIS_INC
sollte dann 0 undFRACT_INC
3,984375 sein. Da es sich jedoch um Ganzzahlen handelt, wird diese auf 3 abgerundet, und Siemillis()
ticken etwa 25% zu langsam.Eine einfache Lösung wäre zu
um
FRACT_INC
4 undmillis()
0,4% zu schnell zu sein. Oder Sie könnentimer0_fract
eine 16-Bit-Variable erstellen und Taktzyklen zählen lassen, um diesen Fehler zu vermeiden. Beide Optionen sollten behoben werdenmillis()
, aber Sie haben immer noch ein Problem mitmicros()
.micros()
funktioniert durch Lesen sowohl destimer0_overflow_count
(im ISR um 1 inkrementierten) als auch des tatsächlichen Zählerwerts. Da Ihr Zähler jetzt alternativ auf und ab geht, ist es schwieriger, aus diesen Messwerten eine Mikrosekundenzahl zu berechnen. Vielleicht könnten Sie zwei aufeinanderfolgende Ablesungen des Zählers vornehmen, um zu wissen, ob er nach oben oder unten geht ...Und dann gibt es das
delay()
, worauf man sich verlässtmicros()
. Wenn Sie behebenmicros()
,delay()
sollte gut funktionieren. Wenn nicht, können Sie diedelay()
Verwendungmillis()
stattdessen umschreiben , was einfach sein sollte, aber Sie verlieren etwas an Genauigkeit.quelle
Since your counter is now going alternatively up and down
Warum geht dasTCNT0
rauf oder runter? Es steigt und jedes Mal, wenn es überläuft, wird der ISR aufgerufen. Vermisse ich etwas Ich habe versucht, dies zu lösen, indem ich dietimer0_overflow_count
Zeit nur alle 64 Mal erhöht habe. Bei dermicros()
Funktion wird die Zeit als zurückgegeben.return ((m << 8) + (t % 64)) * (64 / clockCyclesPerMicrosecond());
Also mache ich einen Mod auf demTCNT0
Zähler, da mein Timer 64-mal schneller tickt. Aber obwohl dasmicros()
scheinbar in Ordnungdelay()
ist, läuft es immer noch schneller und ich verstehe nicht warum ...return ((m << 8) + (t/64) ) * (64 / clockCyclesPerMicrosecond());
Trotzdem bleibt das Problem bei der Verzögerungsfunktion, die ich nicht versteheTCTN0
erhöht sich das Register mit jedem CLK / 64-Tick. Es geht also nur hoch. Es wird jedes Mal zurückgesetzt, wenn es überläuft undTIMER0_OVF_vect
ISR aufgerufen wird. Ich verstehe also immer noch nicht, welche phasenrichtige PWM damit zu tun hat.Sie haben
MICROSECONDS_PER_TIMER0_OVERFLOW
einen richtigen Wert eingestellt, dieser wird jedoch immer nur von verwendetMILLIS_INC
, was wiederum immer nur von verwendet wirdmillis()
. Dies bedeutet, dass die anderen Timing-Funktionen, wie z. B.micros()
, unterbrochen werdendelay()
,delayMicroseconds()
wenn timer0 geändert wird. Dies ist eine Art Fehler, der möglicherweise in einer zukünftigen Version behoben wird. Derzeit erwarten die Arduino-Bibliotheken jedoch, dass Sie timer0 in Ruhe lassen. Die beste Problemumgehung besteht darin, sie nurmillis()
für Ihre zeitkritischen Funktionen zu verwenden.quelle
Dies ist eine unvollständige Antwort - Edgar weiß, wovon er spricht, hör ihm zu
Ich arbeite derzeit mit der gleichen Sache, aber in einem ATMega2560. Diese Internetseitehat mir geholfen, die millis () -Funktion besser zu verstehen. Der Timer läuft alle 510 Zählungen über (siehe Datenblatt, S. 123 für die atmega2560). Ich glaube, es ist 510 und nicht 512, weil es hoch und runter zählt, aber keine Zählung oben oder unten wiederholt - zum Beispiel, wenn Sie von 1 bis 10 zählen und dann wieder runter (ohne 10 zu wiederholen), haben Sie 19-mal gezählt, nicht 20. Dieser Zähler beginnt bei 0, zählt bis 255 und dann zurück bis 0, wodurch ein Überlauf ausgelöst wird, bevor erneut 0 erreicht wird. Dies ist 256 (Countdown einschließlich 0 und 255) + 254 (Countdown ohne 255 oder 0) = 510. Ich habe ein Python-Skript geschrieben, um die Auswirkungen dieser Änderungen zu visualisieren und zu versuchen, sie zu berücksichtigen. Dies zählt bis zu 10 Millionen, daher dauert die Ausführung einige Zeit, aber am Ende kann der Fehler, selbst wenn die Skalierung einfach nachträglich angepasst wird, selbst bei der Skalierung ~ 7 Sekunden betragen.
quelle
Dank Edgars Antwort und Nebsies Erklärung, wie der Zähler genau funktioniert, konnte ich mir eine eigene Implementierung zur Korrektur von Verzögerungen und Mikros () einfallen lassen, die - bisher - auf meinem Arduino Uno gut zu funktionieren scheint.
Ich sage nicht, dass dies die genaueste Implementierung ist, insbesondere habe ich meine Zweifel an micros (), wenn zum Beispiel ein Timerüberlauf zwischen dem Lesen von t1 und t2 auftritt, aber es ist eine, die bisher für mich gut funktioniert.
Als erstes - gemäß Edgars Empfehlung habe ich definiert:
Dies hat jedoch keinen Einfluss auf das Timing von micros () oder delay ().
Funktionsmikros () Ich habe zweimal von TCNT0 gelesen, um festzustellen, ob es nach oben oder unten zählt. Dies geschieht mit der Klausel "if (t1> t2)". Die Überlaufzahl "m" wird mit 510 multipliziert, da der Zähler nach 510 Schritten abläuft. Dann wird der berechnete Zählerwert "t" addiert, geteilt durch die Anzahl der Takte pro Mikrosekunde. (Hinweis: Prescaler = 1, daher keine weitere Multiplikation).
JEDOCH - dies scheint gut zu funktionieren, aber ich hatte zunächst Probleme (bevor ich dies erneut bearbeitete). Da der Zähler viel schneller läuft, läuft alle 268 Sekunden der vorzeichenlose lange Datentyp von micros () über und beginnt wieder bei Null. Dies führte zu einer unerwünschten Verzögerung von delay (), insbesondere wenn lange Verzögerungszeiten verwendet werden, wie in meinem Fall Verzögerungen von einer Sekunde pro Schleifeniteration. Daher musste ich der delay () - Funktion in arduino verdrahtung.c auch eine Überlauferkennung hinzufügen.
Wenn der Überlauf auftritt, ist das Timing möglicherweise nicht sehr genau. Da dies jedoch einmal mehr als 268 Sekunden sind, kann dies akzeptabel sein. Bisher funktioniert mit dieser Änderung die Verzögerungsfunktion auf meiner Seite wieder einwandfrei.
quelle