Forum: Mikrocontroller und Digitale Elektronik Ausführungzeit / Zyklen von Schleifen ( Atmega/AVR)


von Tim (Gast)


Lesenswert?

Hallo

Ich würde gerne wissen wie man die Anzahl der Zyklen einer Schleife 
berechnet.
Also wie man berechnet wie lange eine Schleife braucht.
Gelesen habe ich .....

fSchleife = ftakt/Zyklen

Die Zeit ist also abhängig von der Taktfrequenz des uC.
Jetzt würde ich gerne wissen wie man bei dem Bsp auf 19 Zyklen kommt.
Zählt man einfach alle Befehle die ausgeführt werden innerhalb der 
Schleife ?
Haben die Befehle nicht unterschiedliche Ausführungszeiten ?
Ich nutze den Atmega32


1
//**************** DISPLAY STEUERUNG ********************
2
void setDisplay(uint16_t fout, uint8_t modus)
3
{
4
char Buffer[20];
5
lcd_clear(); //LCD löschen
6
// Textzuweisung
7
switch(modus)
8
{
9
case 1:
10
lcd_string("Sinus");
11
break;
12
case 2:
13
lcd_string("Rechteck");
14
break;
15
case 3:
16
lcd_string("Dreieck");
17
break;
18
case 4:
19
lcd_string("Saegezahn");
20
break;
21
}
22
lcd_setcursor(0,2); // Zeilenumbruch
23
lcd_string(itoa(fout,Buffer,10)); lcd_string(" Hz"); // Hz
24
}
25
//********************************************************
26
//*************** HAUPTROUTINE ***************************
27
int main(void)
28
{
29
lcd_init(); // LCD initialisieren
30
uint8_t modus = 1; // Init Modus (Sinus)
31
uint32_t x=0; // Init Phasenraum
32
uint32_t fcw=0; // Init tuning word
33
uint64_t fout = 1000; // Init Output Frequenz
34
uint64_t cycles = 19; // Init Phasenakkumulator-Zyklen
35
// Makros
36
#define fxt1 6553600.0L // Prozessor-Frequenz
37
#define bits 16777216UL // 2^24 Bit Phasenakkumulator
38
DDRD = 0xFF; // PORT D => Digital-Analog-Wandler
39
PORTD = 127; // Init PORT D
40
DDRC = 0x00; // PORT C => Taster
41
PORTC = 0x1F; // PC0..PC4
42
while(1) {
43
setDisplay(fout, modus);
44
//********** PHASENAKKUMULATOR ***********
45
// SINUS
46
if(modus == 1) {
47
cycles = 19;
48
fcw = (fout*bits)/(fxt1/cycles);
49
while(PINC == 0x1F) {
50
x += fcw;
51
PORTD = pgm_read_byte(&sinus[(uint8_t)((x>>16)&0xFF)]);
52
}
53
}
54
// RECHTECK
55
if(modus == 2) {
56
cycles = 19;
57
fcw = (fout*bits)/(fxt1/cycles);
58
while(PINC == 0x1F) {
59
x += fcw;
60
PORTD = pgm_read_byte(&rechteck[(uint8_t)((x>>16)&0xFF)]);
61
}
62
}
63
SEITE | 34
64
E. Programmcode
65
// DREIECK
66
if(modus == 3) {
67
cycles = 19;
68
fcw = (fout*bits)/(fxt1/cycles);
69
while(PINC == 0x1F) {
70
x += fcw;
71
PORTD = pgm_read_byte(&dreieck[(uint8_t)((x>>16)&0xFF)]);
72
}
73
}
74
// SAEGEZAHN
75
if(modus == 4) {
76
cycles = 12;
77
fcw = (fout*bits)/(fxt1/cycles);
78
while(PINC == 0x1F) {
79
x += fcw;
80
PORTD = (uint8_t)((x>>16)&0xFF);
81
}
82
}
83
//*****************************************
84
//*********** TASTENABFRAGE ***********
85
if(!(PINC & (1<<PC4))) { // MODUS
86
modus++;
87
if(modus > 4) modus = 1;
88
x = 0;
89
_delay_ms(300);
90
}
91
else if(!(PINC & (1<<PC3))) { // ++
92
fout += 20;
93
_delay_ms(60);
94
}
95
else if(!(PINC & (1<<PC2))) { // +
96
fout += 1;
97
_delay_ms(120);
98
}
99
else if(!(PINC & (1<<PC1))) { // -
100
fout -= 1;
101
_delay_ms(120);
102
}
103
else if(!(PINC & (1<<PC0))) { // --
104
fout -= 20;
105
_delay_ms(60);
106
}
107
//*************************************
108
//*********** FREQUENZÜBERPRÜFUNG *****
109
if(fout > 24000) { fout = 24000; }
110
//*************************************
111
}
112
return 0;
113
}

von Karl H. (kbuchegg)


Lesenswert?

Tim schrieb:
> Hallo
>
> Ich würde gerne wissen wie man die Anzahl der Zyklen einer Schleife
> berechnet.

In C gar nicht.
Du lässt den Compiler ein Assembler Listing File erzeugen, suchst dir 
das, was vom C-Original übrig geblieben ist, zählst die Takte der 
Anweisungen zusammen je nach Kontroll-Fluss durch den Schleifenkörper.
Alles andere ist sinnlos.


> Haben die Befehle nicht unterschiedliche Ausführungszeiten ?

Haben sie.
Und ein Neuling kommt selten in die Verlegenheit, dies überhaupt wissen 
zu müssen. Ein mit 16Mhz getakteter µC führt (ein gesunder Mix an 
Befehlen angenommen) so ca. 14 Millionen Befehle pro Sekunde aus. Ob 
eine Schleife 56 oder 57 Millionstel Sekunden dauert, spielt meistens 
keine Rolle. Und wenn es doch eine Rolle spiel, dann hast du deine 
ersten 5000 Zeilen C-Code schon lange hinter dir.

Konzetrier dein Augenmerk lieber darauf les- und wartbaren Code zu 
produzieren.

von Martin (Gast)


Lesenswert?

Problem der Grundgedanke ist nicht verkehrt

problem ist nur das c-befehle je nach compiler sehr wild umgesetzt 
werden (nicht immer das optimum), präzise kann man es nur sagen wenn du 
es assembler programmieren würdest, dann müsste man nur noch 
herausfinden wie viele zyklen der µc pro befehl braucht und dann kann 
man anfangen zu rechnen

wenn etwas sehr zeitkritisch (im sinne von sehr schnelle abfolge) ist 
kann man es eigendlich nur in assembler programmieren, wenn es aber zu 
einem fixen punkt wiederholt werden soll kann man am besten einen 
interupt verwenden

von Tim (Gast)


Lesenswert?

Hallo

Danke für die Antworten.
Ich bastel gerade an einem DDS Frequenzgenerator , aus diesem Grund ist 
es für mich auch als Neuling wichtig zu wissen wie lange die Schleife 
braucht.
Ich würde gerne wissen wie lange meine Schleife braucht bzw würde das 
gerne berechnen können wie viele Ticks/Zyklen diese braucht.
Das ganze ist wichtig da ich ja den Prozessortakt durch diese 
Zeit/Zyklen teilen muss um das korrekten FCW ( Freq.Tuningword) zu 
bestimmen.

In meinem Bsp spricht der Autor von 19 Zyklen , wie hat er diese 
ermittelt ?

von Karl H. (kbuchegg)


Lesenswert?

Tim schrieb:

> In meinem Bsp spricht der Autor von 19 Zyklen , wie hat er diese
> ermittelt ?

Er hats in Assembler geschrieben bzw. sich angesehen, was der Compiler 
daraus gemacht hat.

von holger (Gast)


Lesenswert?

>Ich bastel gerade an einem DDS Frequenzgenerator , aus diesem Grund ist
>es für mich auch als Neuling wichtig zu wissen wie lange die Schleife
>braucht.

Bei dem Mist den du da zusammengebaut hast kann das kein Mensch sagen.
Alleine dein setDisplay() wird schon einige 100 Cycles brauchen.

von Tim (Gast)


Lesenswert?

Der Beispiel Code ist nicht von mir , sonst würde ich ja wissen warum es 
19 Zyklen sind !.

Was genau meinst du damit ?
Wie kann ich beim Assembler Code erkennen das die Schleife 19 Zyklen 
brauchen ?
Hast du vieleicht einen Link zu diesem Thema für mich ?

von Karl H. (kbuchegg)


Lesenswert?

Tim schrieb:
> Der Beispiel Code ist nicht von mir , sonst würde ich ja wissen warum es
> 19 Zyklen sind !.
>
> Was genau meinst du damit ?
> Wie kann ich beim Assembler Code erkennen das die Schleife 19 Zyklen
> brauchen ?

In der Instruction Summary vom Prozessor (eigenes Dokument von Atmel) 
steh bei jedem Befehl dabei, wieviele Takte er braucht. Dieselbe Info 
kriegt man auch im AVR-Studio, in der Hilfe zum Assembler. Auch dort ist 
bei jedem Befehl vermerkt wieviele Takte er braucht. Und die meiste 
Assembler Befehle kennt man eh auswendig, wenn man Assembler 
programmiert.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In der Instruction Summary vom Prozessor (eigenes Dokument von Atmel)
> steh bei jedem Befehl dabei, wieviele Takte er braucht.

Richtig. Das ist eines der wichtigsten Dokumente überhaupt. Gleich nach 
dem Datenblatt des jeweils verwendeten Mikrocontrollers.

Hier der Link zur Befehlsübersicht:
http://www.atmel.com/Images/doc0856.pdf

Solch zeitkritische Sachen würde ich entweder rein oder zumindest in 
wesentlichen Teilen in Assembler programmieren.

von Gregor B. (Gast)


Lesenswert?

Am einfachsten nimmst Du Dir einen freien Port am Mikrocontroller, setzt 
diesen Pin zu Anfang der Schleife, das Rücksetzen ist der erste Befehl 
nach Durchlauf der Schleife, danach hängst Du ein Oszilloskop an den 
Ausgang und triggerst auf die steigende Flanke ds Signals, dann kannst 
Du es genau ausmessen.

von Tim (Gast)


Lesenswert?

Hallo

Danke für den Link !
Werde mich mal durch das PDF kämpfen !
Im Atmel Studio bekomme ich nach dem Debuggen auf der rechten Seite 
einen " CycleCounter " angezeigt der allerdings immer auf 31 steht , 
egal welches Programm ich debugge bzw simuliere.Wie kann man sich den im 
AtmelStudi die gesamten Cycles anzeigen lassen und/oder die Anzahl der 
Cycles pro Schleife.Das muss doch möglich sein.

von Tim (Gast)


Lesenswert?

Gregor B. schrieb:
> Am einfachsten nimmst Du Dir einen freien Port am Mikrocontroller, setzt
> diesen Pin zu Anfang der Schleife, das Rücksetzen ist der erste Befehl
> nach Durchlauf der Schleife, danach hängst Du ein Oszilloskop an den
> Ausgang und triggerst auf die steigende Flanke ds Signals, dann kannst
> Du es genau ausmessen.


Das muss doch auch ^weniger umständlich gehen.
Ich beiße mich gerade durchs PDF , ist aber gar nicht so leicht.

Falls jemand einen Tip hat wie es  AtmelStudio geht wäre das super.
Vielleicht hat auch jemand einen Link zu einer Beschreibung der 
generellen Vorgehensweise

von Stefan (Gast)


Lesenswert?

Der Debugger ist nur bedingt geeignet, um C Code auf diesem Level zu 
debuggen. Wirklich gut funktioniert er nur, wenn Du alle 
Code-Optimierungen deaktivierst, dann gibt es für jeden C Befehl einen 
oder mehrere Assember Befehle. Aber ohne die Optimierungen ist der 
erzeugte Code auch viel größer und langsamer.

Mach das ompiler dabei auch ein Assembler-Listing ausgeben. Dann 
addierst Du die Taktzyklen der ganzen befehle zusammen.

von Dominik S. (dasd)


Lesenswert?

Tim schrieb:
> Das muss doch auch ^weniger umständlich gehen.
> Ich beiße mich gerade durchs PDF , ist aber gar nicht so leicht.

Du musst die PDF doch gar nicht komplett lesen?!

Mir fallen so auf Anhieb 3 Möglichkeiten ein.

1. Pin toggeln und mit dem Oszi anschauen -> Ist dir zu umständlich

2. JTAG -> Hast du vermutlich keins

3. Assembler-Listing anschauen

Zu 3.:

Du lässt AtmelStudio das Projekt übersetzen.
Dannach wechselst du in das entsprechende Projektverzeichnis und hälst 
dort die Augen nach einer Datei mit der Endung ".lst" offen.
Dies ist das erzeugte Assembler-File und enthält deinen C-Code übersetzt 
in ASM.
Du suchst die entsprechende Stelle für die Schleife - das ganze setzt 
natürlich zumindest ein minimales Grundverständnis der Assembler-Sprache 
voraus, sonst wird's schwierig :)
Jetzt schaust du welche Befehle in der Schleife stehen, suchst aus der 
verlinkten PDF die Anzahl der benötigten Zyklen, addierst und fertig.
Zumindest in der Theorie :D

von Karl H. (kbuchegg)


Lesenswert?

Zumindest im 4-er Studio hat man auch bei C-Programmierung die Assembler 
Referenz greifbar.

Unter 'Help' / 'AVR Tools Users Guide'
gibt es den Eintrag 'Assembler'.
In dem findet sich wiederrum 'Instructions' und dort ist bei allen 
Befehlen vermerkt, wieviele Takte (Cycles) sie benötigen.

Hat man ein Assembler Projekt, dann geht das noch einfacher, weil man 
einfach den Cursor im Programmtext auf die Instruktion setzt, F1 drückt 
und das Help-System sucht dann die Hilfe für diesen Befehl heraus.

Will man es sich also auf diesem Weg einfach machen, spricht auch nichts 
dagegen, sich den bewussten Abschnitt aus dem Listing-File in ein 
Assembler-Projekt zu kopieren (muss ja gar nicht übersetzbar sein) und 
dann mit F1 die benötigten Takte festzustellen.


Noch einfacher ist es allerdings, wenn man im Simulator die Takte 
mitzählt. Warum da bei dir immer 32 steht weiß ich nicht. Bei mir 
funktioniert das, dass der Simulator die bisher simulierten Takte 
mitzählt.

von Bronco (Gast)


Lesenswert?

Tim schrieb:
> Ich würde gerne wissen wie lange meine Schleife braucht bzw würde das
> gerne berechnen können wie viele Ticks/Zyklen diese braucht.
> Das ganze ist wichtig da ich ja den Prozessortakt durch diese
> Zeit/Zyklen teilen muss um das korrekten FCW ( Freq.Tuningword) zu
> bestimmen.

Aufgaben, die ein genaues Timing erfordern, macht man i.a.R. mit Hilfe 
einer Hardwareressource wie Timer, Capture/Compare usw.
Ein genaues Timing mit einer Schleife (ohne Interrupts) mit haargenauer 
Laufzeit ist bei sehr einfachen Programmen zwar möglich, aber aufwendig, 
da Du für jeden möglichen Programmzweig die genau gleiche Laufzeit 
erzeugen mußt.
In dem Augenblick, wo das Programm etwas aufwendiger wird und wenn 
Interrupts ins Spiel kommen, funktioniert das ganze nicht mehr.

Beispiel:
1
if (bedingung)
2
{ 
3
  dosomething(); 
4
}
Wenn "bedingung" wahr ist, hast Du mehr Laufzeit als wenn nicht.
Du müßtest also in diesem so etwas machen:
1
if (bedingung)
2
{ 
3
  dosomething(); 
4
}
5
else
6
{
7
  delay(GENAUSO_VIELE_TAKTE_WIE_DO_SOMETHING);
8
}
Und das müßtest Du ganz konsequent über alle Funktionen hinweg 
ausbalancieren.

von Tim (Gast)


Lesenswert?

Hallo

Ich werde es parallel mal mit Timern ausprobieren.
Ich habe nun versucht anhand der Assembler Datei die benötigten Cyklen 
zu berechnen.
Folgendes steht in der Datei !
1
int main(void)
2
{
3
  
4
  uint8_t modus = 1; // Init Modus (Sinus)
5
  uint32_t x=0; // Init Phasenraum
6
 1a6:  80 e0         ldi  r24, 0x00  ; 0
7
 1a8:  90 e0         ldi  r25, 0x00  ; 0
8
 1aa:  dc 01         movw  r26, r24
9
    // SINUS
10
    if(modus == 1) {
11
        cycles = 19;
12
        fcw = (fout*bits)/(fxt1/cycles);
13
        while(PINC == 0x1F) {
14
        x += fcw;
15
 1ac:  80 50         subi  r24, 0x00  ; 0
16
 1ae:  92 44         sbci  r25, 0x42  ; 66
17
 1b0:  af 4f         sbci  r26, 0xFF  ; 255
18
 1b2:  bf 4f         sbci  r27, 0xFF  ; 255
19
        PORTD = pgm_read_byte(&sinus[(uint8_t)((x>>16)&0xFF)]);
20
 1b4:  ea 2f         mov  r30, r26
21
 1b6:  f0 e0         ldi  r31, 0x00  ; 0
22
 1b8:  ec 5a         subi  r30, 0xAC  ; 172
23
 1ba:  ff 4f         sbci  r31, 0xFF  ; 255
24
 1bc:  e4 91         lpm  r30, Z
25
 1be:  e2 bb         out  0x12, r30  ; 18
26
    //********** PHASENAKKUMULATOR ***********
27
    // SINUS
28
    if(modus == 1) {
29
        cycles = 19;
30
        fcw = (fout*bits)/(fxt1/cycles);
31
        while(PINC == 0x1F) {
32
 1c0:  23 b3         in  r18, 0x13  ; 19
33
 1c2:  2f 31         cpi  r18, 0x1F  ; 31
34
 1c4:  99 f3         breq  .-26       ; 0x1ac <main+0x1a>
35
36
37
38
39
return 0;
40
 1c6:  80 e0         ldi  r24, 0x00  ; 0
41
 1c8:  90 e0         ldi  r25, 0x00  ; 0
42
 1ca:  08 95         ret
43
44
000001cc <_exit>:
45
 1cc:  f8 94         cli
46
47
000001ce <__stop_program>:
48
 1ce:  ff cf         rjmp  .-2        ; 0x1ce <__stop_program>


Ich habe mal die relevanten Befehle zusammen geschrieben und die 
benötigten Cycles für jeden Befehl.

subi  r24, 0x00  ;1
sbci  r25, 0x42  ;1
sbci  r26, 0xFF  ;1
sbci  r27, 0xFF  ;1

mov  r30, r26 ;1
ldi  r31, 0x00  ;1
subi  r30, 0xAC  ;1
sbci  r31, 0xFF  ;1
lpm  r30,;3
out  0x12, r30  ;1
in  r18, 0x13  ;1
cpi  r18, 0x1F  ;1
breq ; 2

Macht 16 Cycle !
Laut AtmelStudio stimmt das auch !
Jedesmal wenn die Schleife durchläuft springt die Anzeige 
"CycleCounter" um 16 weiter.Was ich nun wiederum nicht verstehe ist 
warum der Autor des Codes von 19 Cycle spricht und nicht von 16.

Es ist zumindest ein Schritt in die richtige Richtung und sich bischen 
mit Assembler zu beschäftigen kann sicher nicht schaden.

von Tim (Gast)


Lesenswert?

Keiner eine Idee ?^^

von Karl H. (kbuchegg)


Lesenswert?

Andere Compiler Version.

Und jetzt weißt du auch, warum die Sache als C Code nicht so sinnvoll 
ist. Mit jeder Compiler Version kann sich die Anzahl ein wenig ändern.

Wenn man gesicherte Taktzahlen benötigt (und du benötigst die 
tatsächlich), dann führt kein zuverlässiger Weg an Assembler vorbei. In 
diesem Fall brauchst du die totale Kontrolle über die Maschine.

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.