Forum: Mikrocontroller und Digitale Elektronik 200 ns delay mit Timer0 (AVR32)


von Bernd (Gast)


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?

von Karl H. (kbuchegg)


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.

: Bearbeitet durch User
von chris (Gast)


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

von Bernd (Gast)


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!

von Karl H. (kbuchegg)


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.

von Falk B. (falk)


Lesenswert?

Oder man macht es gleich richtig wie in der libc vom AVR (8 Bit), mit 
einem gescheiten Assemblermacro!

Gibt es kein fertiges _delay_us() oder ähnlich bei deinem Compiler?

von Bernd (Gast)


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");

von Falk B. (falk)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.