Hallo,
ich habe folgendes Problem:
Umgebung:
ATmega48
8000000 Hz interne Clock
Aufgabe:
3 Impulse mit einer Impulsdauer von 1 us
in einer Frequenz von 190 kHz erzeugen
Anschließend Daten verarbeiten und wieder von vorne.
1ter Ansatz waren "delays"
1
PORTB|=(1<<PORTB1);
2
_delay_us(1);
3
4
PORTB&=~(1<<PORTB1);
5
_delay_us(4.26);
6
7
PORTB|=(1<<PORTB1);
8
_delay_us(1);
9
10
PORTB&=~(1<<PORTB1);
11
_delay_us(4.26);
12
13
PORTB|=(1<<PORTB1);
14
_delay_us(1);
15
16
PORTB&=~(1<<PORTB1);
Funktioniert zwar einigermaße die Zeiten sind aber nicht sehr genau (mit
Oszi nachgemessen.
2ter Ansatz "Timer"
Timer einstellen auf gewünschte Frequenz und Pulsbreite
Timer aktivieren
Im Interrup Impulse Zählen
Timer deaktivieren
Daten Verarbeiten
Der Code des Timersansatzes ist im Anhang.
Mein Problem besteht darin wie ich den Timer deaktiviere?
Oder gibt es für solch eine Anwendung eine elegantere Lösung.
Ich hoffe irgendjemand kann mir da weiterhelfen.
Danke für den Tipp @johnny-m
Nach ein par weiteren Fehlern die sich da noch eingeschlichen haben und
deinem Tipp funktioniert das schon fast.
Jetzt wird allerdings der letzten Impuls nicht immer zurückgesetzt.
Gibt es da noch einen Lösungsansatz?
@ Norbert S. (norton)
>Umgebung:>ATmega48>8000000 Hz interne Clock>Aufgabe:>3 Impulse mit einer Impulsdauer von 1 us>in einer Frequenz von 190 kHz erzeugen
Ich hoffe dir ist klar dass
- der interen RC-Oszillator nur mässig genau ist (+/-5% wenn er
kalibriert ist)
- der uC nur Pulse erzeugen kann, die einer ganzzahligen Anzahl Takte
entspricht.
>1ter Ansatz waren "delays"
Guter Ansatz. hast du aber auch die Optimierung eingeschaltet?
Sonst sitmmen deine Zeiten hinten und vore nicht.
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Warteschleifen_.28delay.h.29>Funktioniert zwar einigermaße die Zeiten sind aber nicht sehr genau (mit>Oszi nachgemessen.
Wie gross ist denn der Fehler? Denk dran dass die Befehle zum setzen der
Ports und die Schleife auch Zeit brauchen, die du in deinen Wartezeiten
abziehen musst.
MfG
Falk
Danke @falk
Optimierung ist eingeschaltet
Vieleicht hab ich bei der Genauigkeit einfach ein wenig zu viel
erwartet.
1 us --> 1,1
4,24 us --> 4,5 us
bei der Frequenz macht sich das allerdings sehr schnell bemerkbar.
Der Fehler wird sich wahrscheinlich aus den von dir schon genannten
Gründen ergeben.
Funktioniert die _delay_us Funktion prinzipiell überhaupt mit
Nachkommastellen, oder werden die sowieso weggeschnitten?
Ist ein Timer für solch eine Anwendung auch sinnvoll oder sollte man da
bei den delays bleiben?
@ Norbert S. (norton)
>1 us --> 1,1>4,24 us --> 4,5 us
Das sind die Toleranzen des RC-Oszillators. Nimm einen Quarz.
>Funktioniert die _delay_us Funktion prinzipiell überhaupt mit>Nachkommastellen,
Ja.
> oder werden die sowieso weggeschnitten?
Nein. Aber am Ende kann nur eine ganzzahligge Anzahl Takte rauskommen
(Berechnung über F_CPU). Die wird grundet oder abgeschnitten, weiss ich
jetzt nicht.
>Ist ein Timer für solch eine Anwendung auch sinnvoll oder sollte man da>bei den delays bleiben?
Das dein uC nebenbei sowieso nichts anderes macht ist die Delayvarinate
OK und ggf. sogar flexibler.
MFG
Falk
Bei einem Systemtakt von 8MHz (wir nehmen mal an, dass der Oszillator
genau ist), kannst du nur auf ein Vielfaches von 125nS kommen. Bei 1 µs
(gemessen 1,1) denke ich mal entweder ein Messfehler oder sowas. Aber,
4,25µs sollten machbar sein, 4,24 geht nicht.
MW
Falk Brunner wrote:
> @ Norbert S. (norton)>>>1 us --> 1,1>>4,24 us --> 4,5 us>> Das sind die Toleranzen des RC-Oszillators. Nimm einen Quarz.
Sehe ich nicht so: 1us warten sind 8 Takte, plus einen zum Ausgeben der
Daten macht 1.125 ns.
4.26 us werden auf die nächsthöhere Taktanzahl aufgerundet, also 4.375
us = 35 Takte plus einen Takt zum Ausgeben macht 4.5 us.
Ergo: 0.874 und 4.124 als Delay-Werte angeben (zur Sicherheit letzte
Stelle abgerundet auf 4) und die Werte stimmen im Rahmen des möglichen
und der Genauigkeit des RC-Oszillators.
Die Verzögerungsschleife _delay_loop_1 (die _delay_us zu Grunde liegt)
hat übrigens eine Granulariät von 3 Takten. Ich bin mir gerade auch
nicht ganz sicher, wie viel Zeit sie zum Einrichten benötigt, auf
jeden Fall benötigen die Befehle dazwischen ja noch Zeit. Das läuft
auf Erbsenzählen hinaus...
Eine reine Hardwarelösung (CPLD) wäre u. U. angebrachter.
@Jörg Wunsch:
Gut zu wissen, ich hatte nicht nachgeschaut. Wenn für Norbert die
erreichbare Frequenz von 190.5 (+- RC / Quartz-Ungenaugikeit) kHz genau
genug ist, würde ich aber noch nicht zu einer Hardware-Lösung greifen.
Zur Not, wenn sich zeigt, dass die Verwendung von delay-Funktionen nicht
genau genug sind, sollte es auch ein kurzer inline-assembler-Abschnitt
tun.
@ Jan M. (mueschel)
>> Das sind die Toleranzen des RC-Oszillators. Nimm einen Quarz.>Sehe ich nicht so: 1us warten sind 8 Takte, plus einen zum Ausgeben der>Daten macht 1.125 ns.
Hast recht. Ein Quarz ist aber dennoch nicht verkehrt.
> Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite>jeden Fall benötigen die Befehle dazwischen ja noch Zeit. Das läuft>auf Erbsenzählen hinaus...>Eine reine Hardwarelösung (CPLD) wäre u. U. angebrachter.
Naja, voher würde ich einfach ASM nehem und die Ebsen, ähhh nops zählen.
MFG
Falk
neuer wrote:
> ....- der uC nur Pulse erzeugen kann, die einer ganzzahligen Anzahl> Takte> entspricht....>>> gibt es auch halbe takte???
Na eben nicht.
....Funktioniert die _delay_us Funktion prinzipiell überhaupt mit
Nachkommastellen, oder werden die sowieso weggeschnitten?....
funktioniert mit nachkommastellen.
es gibt aber minimal immer ein schleifendurchlauf. nop's werden nicht
ergänzt, die hier evtl gebraucht werden , wenn ein schleifendurchlauf
zuviel ist und man dann einen weniger reinsetzt und dann evtl mit 3xnop
ergänzt . darum die ungenauigkeit.
Danke nochmals für die vielen Antworten und Ratschläge.
Ich werde meine Impulse also mit den "delay" Funktionen realisieren. Die
Genauigkeit des internen Oszillators sollte für meine Anwendung reichen.
Danke Norton
Hi,
kann er nicht an Stelle des delays ein fach "inline" ein paar asm( nop
); schreiben ??? (Habe die richtige Syntax im Moment nicht zur Hand)
Gruß
Andreas
@ UBoot-Stocki (Gast)
>kann er nicht an Stelle des delays ein fach "inline" ein paar asm( nop>); schreiben ??? (Habe die richtige Syntax im Moment nicht zur Hand)
Ja.
>kann er nicht an Stelle des delays ein fach "inline" ein paar asm( nop>); schreiben ??? (Habe die richtige Syntax im Moment nicht zur Hand)
Ich würde sogar sagen, dass dies die einzige gute Lösung ist.
Bei gewünschten 1 µs Impulslänge und 8 MHz Taktfrequenz müssen zwischen
dem H-Setzen des Pins und dem L-Setzen 1 µs später genau >>> 8 <<<
Taktzyklen vergehen. Das Aufrufen einer Delay-Funktion beinhaltet
mindestens ein "rcall" sowie ein "ret". Das benötigt aber schon 7 Takte
(rcall 3, ret 4)! Was soll die Delay-Funktion im Innern in dem einen
verbleibenden Takt machen?
190 kHz Frequenz sind 5.263 µs, abzüglich der 1 µs für die Impulse
ergeben sich 4.263 µs Wartezeit für den "L"-Zustand. Das ist genau
nicht hizukriegen, weil 4.263 µs kein ganzzahliges Vielfaches von 1/8
MHz = 0.125 µs sind. Der am nächsten liegende realisierbare Wert ist
4.25 µs. Das entspricht 4.25/0.125 = 34 Taktzyklen. Da könnte man an
den Aufruf einer Delay-Funktion in Erwägung ziehen, aber man könnte auch
einfach 33 "nop"s hintereinanderschreiben. Letztere Variante hat den
Vorteil, dass man sich keinen einzigen Gedanken daran verschwenden muss,
wie genau die verwendete Delay-Funktion arbeitet (compilerspezifische,
vielleicht noch versionsabhängige Optimierungen etc.). Damit die
Impulse garantiert korrekt generiert werden, muss man lediglich die
"nop"s richtig abzählen.
@ AVRFan (Gast)
>Ich würde sogar sagen, dass dies die einzige gute Lösung ist.
Nicht ganz.
>Bei gewünschten 1 µs Impulslänge und 8 MHz Taktfrequenz müssen zwischen>dem H-Setzen des Pins und dem L-Setzen 1 µs später genau >>> 8 <<<>Taktzyklen vergehen.
Ja.
> Das Aufrufen einer Delay-Funktion beinhaltet>mindestens ein "rcall" sowie ein "ret". Das benötigt aber schon 7 Takte>(rcall 3, ret 4)!
Nein. Die _delay_us() ist ein Inline ASM Makro.
>Vorteil, dass man sich keinen einzigen Gedanken daran verschwenden muss,>wie genau die verwendete Delay-Funktion arbeitet (compilerspezifische,
_delay_us() hat eine Granularität von 3 Takten, _delay_ms() von vier.
>vielleicht noch versionsabhängige Optimierungen etc.). Damit die>Impulse garantiert korrekt generiert werden, muss man lediglich die>"nop"s richtig abzählen.
Ja. Damit kann man dann auf einen Takt genau arbeiten.
MFg
Falk
>> Das Aufrufen einer Delay-Funktion beinhaltet>>mindestens ein "rcall" sowie ein "ret". Das benötigt aber schon 7 Takte>>(rcall 3, ret 4)!>Nein. Die _delay_us() ist ein Inline ASM Makro.
Was heißt da 'nein'? Hab nix falsches erzählt. Dass _delay_us() gar
keine Funktion, sondern ein Inlinemakro ist, kann ja kein Mensch
ahnen... lach.
>_delay_us() hat eine Granularität von 3 Takten.
Dann gurkt es intern wahrscheinlich gemäß
ldi temp, [n]
Loop:
dec temp
brne Loop
Das benötigt zum Durchlaufen nämlich genau 3 n Takte.
Achja, danke für den Hinweis mit dem Inlinemakro... ;-)
Es ist kein Inline/makro/, sondern eine Inline/funktion/.
Wie die zu Grunde liegenden Schleifen aufgebaut sind, das brauchst
du nicht mutmaßen, das kannst du einfach in den beiden Headerdateien
nachlesen. ;-)
>Es ist kein Inline/makro/, sondern eine Inline/funktion/.
Wird ja immer doller...
>kannst du einfach in den beiden Headerdateien nachlesen. ;-)
Headerdateien? Nie gehört... gg