200 ns delay mit Timer0 (AVR32)

Gast #3996709
Lesenswert?

Hallo!
Ich möchte für eine 1-Wire Kommunikation einen kleinsten Delay von 200 
ns erzeugen. Controller ist ein AT32UC3B0265

Meine Herangehensweise:
Takt ist 60 MHz, Timer0 nimmt 30 MHz. Das RC Register wird geladen und 
ein Interrupt bei erreichen des Wertes ausgelöst. Im Interrupt wird ein 
Flag gesetzt, im Hauptcode wird in einer While-Schleife auf das Flag 
gewartet. Bei 30 MHz zählt er alle 33,3 ns hoch, RC auf 6 geladen müsste 
also circa 200 ns warten. Code:
1
uint8_t volatile TC_INT=0;
2

3
void warte(void){
4
tc_write_rc(&AVR32_TC, 0, 6);
5
tc_start(&AVR32_TC,0);
6
while (TC_INT==0) {}
7
tc_stop(&AVR32_TC,0);
8
}
9

10
__attribute__((__interrupt__))
11
static void tc_irq(void) {
12
TC_INT=1;
13
tc_read_sr(&AVR32_TC, 0);
14
}

Kann man das überhaupt so machen, oder muss ich im Assembler rum 
fummeln?
#3996718
Lesenswert?

Wenn ich mich nicht verrechnet habe, dann sind 200ns bei 60Mhz ganze 12 
Takte. Da dauert dir das ganze Aufsetzen und INterrupt Handling schon 
länger.

Schau mal nach wieviele Takte ein NOP benötigt und füg dann die 
entsprechende Anzahl ein, wenn _delay_us das nicht mehr richtig 
gerechnet kriegt.

Auch wenn die generelle Aussage lautet "delay ist von Übel", so gibt es 
doch eine untere Zeit Grenze, an der ein alternativer Ansatz keinen Sinn 
mehr macht.
Gast #3996722
Lesenswert?

Bernd schrieb:
> Im Interrupt wird ein
> Flag gesetzt, im Hauptcode wird in einer While-Schleife auf das Flag
> gewartet.

Wozu dann den Interrupt, wenn du sowieso wartest?
Du kannst ja auch gleich entweder in einer Schleife auf einen bestimmten 
Zählerstand des Timerregisters warten, oder aber auf das Interrupt-Flag. 
Den Interrupt musst du dann nicht freigeben, das Flag wird ja auch so 
gesetzt. (zumindest bei 8bit AVR, aber das wird hier wsl. ähnlich sein 
nehme ich an)

Aber bei 12 Takten sind wohl ein paar nop() einfacher / sinnvoller
Gast #3997771
Lesenswert?

Dankeschön! Mit einer Schleife und nop gehts jetzt.
1
 
2
for (i=0;i<10;i++) {
3
  asm volatile ("nop");
4
}

Im Disassembly habe ich dazu Breakpoints gesetzt und mir die Befehle 
angeschaut, die dort aufgerufen wurden. Leider habe ich kein Instruction 
Set für AVR32 gefunden. Mit Abschätzen und ausprobieren funktioniert die 
Kommunikation jetzt aber.

Nochmal Danke!
#3997791
Lesenswert?

Bernd schrieb:
> Dankeschön! Mit einer Schleife und nop gehts jetzt.
>
>
1
> for (i=0;i<10;i++) {
2
>   asm volatile ("nop");
3
> }
4
>
>

wobei man hier sagen muss, dass du dir das Assembler Ergebnis ansehen 
musst.

Denn du hast hier den Fall, dass die Schleifensteuerung vom Prinzip her 
mehr Zeit braucht, als der NOP.
Mit einer Ausnahme: Wenn der Optimizer Loop-Unrolling macht, dann fällt 
die Schleifensteuerung weg und im Code tauchen nur noch 10 NOP 
hintereinander auf.

D.h. du bist hier in der unglücklichen Situation, dass du extrem vom 
Compiler abhängig bist, welchen Zeitbedarf das Konstrukt tatsächlich 
hat. Eine gewisse Mindestzeit ist garantiert (denn einfach komplett 
wegoptimieren kann der Compiler das Konstrukt nicht). Aber je nach 
Compilereinstellung kann sich der Zeitbedarf gut und gerne auch 
mindestens verdreifachen (ich schätze das jetzt mal). Wenn das in deiner 
Anwendung kein Problem ist - gut. Wenn das ein Problem darstellt, bist 
du besser beraten, das Loop-Unrolling selbst zu machen und dann eine 
genau definierte Zeitspanne zu haben.
Gast #3998236
Lesenswert?

Falk Brunner schrieb:
> Gibt es kein fertiges _delay_us() oder ähnlich bei deinem Compiler?

Ich nutze das AtmelStudio 6.2 mit SoftwareFramework. Da gibt es zwar 
eine delay routine, allerdings als kleinste Zeiteinheit nur 
Mikrosekunden. Und ich brauch es eben kürzer.

Karl Heinz schrieb:
> Mit einer Ausnahme: Wenn der Optimizer Loop-Unrolling macht, dann fällt
> die Schleifensteuerung weg und im Code tauchen nur noch 10 NOP
> hintereinander auf.

Bei mir ist "Optimization (-O1)" an. Welche Auswirkungen die anderen 
haben, kann ich morgen erst nachschauen.

"Manuelles Loop-Unrooling"? Könnte ich das dann mit einem #define so 
machen?
1
#define PAUSE1 asm volatile ("nop");
2

3
#define PAUSE2 asm volatile ("nop"); /
4
               asm volatile ("nop");
#3998248
Lesenswert?

@ Bernd (Gast)

>Ich nutze das AtmelStudio 6.2 mit SoftwareFramework. Da gibt es zwar
>eine delay routine, allerdings als kleinste Zeiteinheit nur
>Mikrosekunden. Und ich brauch es eben kürzer.

Aha. Sicher dass die Funktion nicht doch Bruchteile von us verarbeiten 
kann? Beim 8 Bit AVR kann sie das!

>"Manuelles Loop-Unrooling"? Könnte ich das dann mit einem #define so
>machen?

>#define PAUSE1 asm volatile ("nop");

>#define PAUSE2 asm volatile ("nop"); /
>               asm volatile ("nop");

Sicher. Damit ist es taktgenau. Und da du keine große Flexibilität 
brauchst, kann man auch eine handvoll Makros hinschreiben und gut.

Oder man wühlt das Makro der bestehenden Delay-Funktion aus den Includes 
und strickt es um. Beim 8-Bit AVR gibt es auch eine Funktion, die ein 
bestimmte Anzahl Takte warten kann.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren