mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Wie viele Takte ergeben 1500 µs?


Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich bin gerade etwas verwirrt. Ich habe einen ATMEGA328P mit einem 
20-MHz-Quarz und möchte etwas trickreich eine gewisse Anzahl von 
Mikrosekunden abwarten. Um den Programmablauf nicht zu unterbrechen, 
soll das per Timer-Interrupt passieren. Aus verschiedenen Gründen steht 
mir nur noch Timer2 zur Verfügung.
Das Ganze dient dazu, einen Servo anzusteuern.

Beim Programmstart initialisiere ich meinen Timer mit:
TCCR2A = 0;
TCCR2B = _BV(CS21) | _BV(CS20); // Prescaler 1/32

Für einen Impuls resette ich TCNT2, setze meinen Ausgang auf High und 
aktiviere den Interrupt.
Im Interrupthandler wird die Zahl der Überlaufe gezählt und bei 
Erreichen der gewünschten Zahl wird der Ausgang wieder auf Low gesetzt 
und der Interrupt deaktiviert.
static volatile uint8_t Overflows;

ISR(TIMER2_OVF_vect) {
  if (--Overflows > 0) return;

  LO(PIN_SERVO); // Ist ein Makro
  TIMSK2 = 0;
}

Ich habe (durch Ausprobieren) herausgefunden, dass mein Servo bei 40000 
CPU-Takten in Mittelstellung (genau zwischen den Endanschlägen) geht:
Overflows = 5;
TCNT2 = 30;
HI(PIN_SERVO); // Ist ein Makro
TIMSK2 = _BV(TOIE2);

Der Timer zählt also insgesamt 5*256-30 = 1250 Mal, also 40000 Takte 
(Prescaler 1/32). 40000 Takte sind bei 20 MHz aber 2000 µs und nicht 
1500 µs (was laut Datenblatt die Mittelstellung sein soll). Es stimmt 
aber definitiv, denn bei der halben Zeit (1000 µs) versucht das Servo 
schon über den Endanschlag zu fahren und rattert und knackt 
besorgniserregend.

Wo habe ich falsch gerechnet?

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich glaube, deine Rechnung ist falsch. Könnte also gut sein, dass der µC 
gar nicht mit 20Mhz läuft (warum auch immer).

Lass mal einfach eine LED mit sleep() Blinken, ohne Timer. Wenn sie dann 
mit der falschen Geschwindigkeit blinkt, stimmt war mit deinem Taktgeber 
nicht.

Autor: Matthias S. (Firma: matzetronics) (mschoeldgen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Geh mal andersrum an die Sache ran. Der Timer läuft in 1/(20E6*32*256) = 
409,6µs einmal durch. 4 komplette Durchläufe sind dann 409,6 * 4 = 
1,638ms - also knapp neben der Mittelstellung.

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit einem der anderen Timer lasse ich eine Uhr laufen und auf einem 
Display anzeigen. Keine Probleme dort.

Leider habe ich kein Oszi hier...

Ich habe mittlerweile das Servo in Verdacht.
https://www.pololu.com/file/0J730/HD-6001HB.pdf

Autor: Matthias S. (Firma: matzetronics) (mschoeldgen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hugo schrieb:
> Ich habe mittlerweile das Servo in Verdacht.

Eine normale RC Fernsteuerung zum Testen nicht vorhanden? Und weil das 
Thema gerade so aktuell ist, es kann bei dir nicht sein, das dein AVR so 
mit ISRs vollgestopft ist, das Timer2 nur selten zum Zug kommt?

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auch dann würde die Uhr nachgehen und das Servo zudem sehr unregelmäßig 
herumhampeln. Ist aber beides nicht der Fall.
Um das auszuschließen, werde ich heute Abend aber nochmal die 
idiotensichere Variante mit _delay_us() probieren.

Was mich im Datenblatt verdutzt, ist die Zeile (Seite 3):

"Operating travel: 90° (1000 -> 2000 µs)"

Das halte ich aber für einen Schreibfehler und eigentlich sollte da 
sicher 180° stehen. Wer solche Fehler macht, baut aber vielleicht auch 
Servos mit Mittelstellung bei 2000 µs?

Autor: Stefanus F. (stefanus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>  Wer solche Fehler macht, baut aber vielleicht auch
> Servos mit Mittelstellung bei 2000 µs?

Dann würde der Servo an normalen Fernbedienungen unbrauchbar sein.

> 4 komplette Durchläufe sind dann 409,6 * 4 = 1,638ms
> also knapp neben der Mittelstellung.

Das ist ja alles schön und gut, aber der TO hat geschrieben, dass er die 
Mittelstellung erst bei fast 5 kompletten Durchläufen erreicht (nicht 
4). Und das sind eben 2µS.

> Um das auszuschließen, werde ich heute Abend aber nochmal
> die idiotensichere Variante mit _delay_us() probieren.

Ja, mach das mal. Ich merke gerade, dass ich fälschlicherweise sleep() 
geschrieben habe. Normalerweise programmiere ich PC's daher kommt das 
wohl. Gut das du trotzdem verstanden hast, was ich meinte.

Idiotensicher ist das allerdings nicht. Manche Leute compilieren mit 
-O0, damit man besser debuggen kann. Und dann stimmen die Timings wieder 
nicht. Vermutlich ist Dir das schon bewusst.

: Bearbeitet durch User
Autor: Wolfgang (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hugo schrieb:
> Leider habe ich kein Oszi hier...

Es ist doch bald Weihnachten.

Für solche digitalen Fragestellungen reicht meist schon ein 
minimalistischer Logikanalysator. In der Bucht kosten die Dinger 
mittlerweile keine 8€ mehr.

Gönn' dir mal was praktischen.

Autor: Achim S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hugo schrieb:
> Leider habe ich kein Oszi hier...

Hast du vielleicht ein Multimeter, das auch Frequenzen messen kann?

Mit dem kannst du die Periode deines PWM-Signals bestimmen. Und dann 
kannst du in der DC-Einstellung die (mittlere) Spannung messen. Deren 
Wert entspricht VDD*Pulsbreite/Pulsperiode

Ist zwar nicht ultrapräzise, aber für den 25% Unterschied von 1,5ms zu 
2ms reicht es vielleicht.

Autor: Kaj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hugo schrieb:
> Der Timer zählt also insgesamt 5*256-30 = 1250 Mal, also 40000 Takte
> (Prescaler 1/32). 40000 Takte sind bei 20 MHz aber 2000 µs und nicht
> 1500 µs
Natürlich sind 40.000 Takte bei 20MHz 2ms, wer hätte das erwartet?!
40.000 * 50ns = 2ms.

Bei f = 20MHz dauert ein Takt 50ns (1/f).
Du willst 1500µs (1.5ms) haben.

1.5ms/50ns = 30.000 Takte

Wenn du jetzt einen Prescaler von 8 nimmst dann kommst du auf
(14 * 256) - 166 = 3.750 * 8 -> 30.000 Takte -> 1.5ms -> 1500µs

Alle Prescaler außer 1 und 8 ergeben krumme Werte.
Prescaler is: [   1]. Result is: 30000.0
Prescaler is: [   8]. Result is:  3750.0
Prescaler is: [  32]. Result is:   937.5
Prescaler is: [  64]. Result is:   468.75
Prescaler is: [ 128]. Result is:   234.375
Prescaler is: [ 256]. Result is:   117.1875
Prescaler is: [1024]. Result is:    29.296875

Die Antwort auf deine Frage ist also:
Bei f = 20MHz sind es 30.000 Takte.

Autor: Thomas O. (kosmos)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn man es ganz ohne Messgerät machen möchte setzt man im AVR-Studio 2 
Breakpoints beim ersten setzt man den Counter auf 0 und beim zweiten 
liest man die Takte oder die Stoppuhr ab.

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh Wunder: Mit _us_delay(1500); fährt das Servo in die Mittelposition... 
Bin gerade etwas ratlos.

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe durch Vergleiche mit _us_delay(1500); gemerkt, dass 1250 
Timer-Durchläufe nicht so ganz exakt die Mittelstellung ergeben. Es sind 
mehr so 1190 nötig. Vorher konnte ich das ja nur Pi mal Daumen 
abschätzen.
Und jetzt kommt's: (1190-256)*32 ist knapp 30000. Mit anderen Worten: Es 
geht irgendwo ein Überlauf "verloren", der eigentlich noch abgewartet 
werden müsste.

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, ich glaube, ich habe die Lösung: Da der Timer ständig im 
Hintergrund läuft und die ISR nur bei Bedarf verwendet wird (nur dann 
ist das TOIE2-Bit gesetzt), starte ich schon mit gesetztem TOV2-Bit. Das 
ja nur bei aktivem Interrupt automatisch gelöscht wird.
Die ISR wird also durch diese Altlast schon direkt nach dem Setzen von 
TIMSK2 aufgerufen. Ich müsste das durch ein TIFR2 = 0; nach dem TCNT2 = 
0; lösen können. Aber das teste ich erst morgen.

Autor: chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hugo schrieb:
> TIFR2 = 0

Nein, Interrupt Flags werden durch Beschreiben mit einer '1' gelöscht.

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also so. Das müsste dann auch die Mittelstellung sein:
Overflows = 4;
TCNT2 = 86;
TIFR2 = 0;
HI(PIN_SERVO);
TIMSK2 = _BV(TOIE2);

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
TIFR2 = _BV(TOV2);

;)

Autor: Hugo (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also, es scheint jetzt zu gehen.

Das Fazit aus diesem Thread lautet (und ist vielleicht für viele andere 
hilfreich): Wenn man Timer-Interrupts "bei Bedarf" aktiviert, für genaue 
Wartezeiten oder auch Zeitmessungen, etc., sollte man die entsprechenden 
Flags auch gleich mit zurücksetzen.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.