AVR Atmega1284P - langsam?!

OP #2933065
Lesenswert?

Hallo,

erst einmal möchte ich mich für das tolle Forum und die umfangreichen 
Tutorials bei der Community, also euch, bedanken. Ihr habt mir den 
Einstieg in die Programierung von uCs sehr vereinfacht.

Mittlerweile konnte ich schon einiges auf verschiedenen Atmegas 
implementieren, doch stehe ich aktuell vor einem Problem bzw. bräuchte 
eure Hilfe.

Für mein aktuelles Projekt benötige ich 3 ineinander verschachtelte 
Schleifen, welche per UART eintreffende Daten verarbeiten und an einen 
zweiten uC per UART senden. Was genau dort passiert ist erstmal 
unrelevant, denn ich musste feststellen, dass mein Atmega1284P mit 
externem 18,432MHz Quarz irgendwie mit Handbremse läuft.

Zum Testen der Geschwindigkeit habe ich folgenden C-Code verwendet:
1
#include "bitOperations.h"
2
#include "uart1284P.h"
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/setbaud.h>
6

7
int main(void)
8
{
9
  uint8_t x = 255;
10
  uint8_t y = 255;
11
  uint8_t z = 3;
12
  
13
  set_bit(DDRB,0);
14
    
15
    while(1)
16
    {
17
    set_bit(PORTB,0);
18
    
19
    do{
20
      do{
21
        do{
22
          
23
        } while(--z);
24
      }while(--y);
25
    }while(--x);
26
    
27
    clear_bit(PORTB,0);
28
    uart0_data(x);                      
29
    }
30
}

Das Programm macht nichts anderes, als:

1. den PINB0 als Ausgang zu definieren und den Ausgang zu togglen 
(set_bit und clear_bit). Am PINB0 hängt eine LED mit Vorwiderstand.

2. Die do-while Schleifen zu durchlaufen

3. Eine Null (x ist nach Durchlauf der do-while-Schleifen gleich Null) 
per UART zu senden. Die Null wird ordnungsgemäß am PC empfangen; UART 
funktioniert.

PROBLEM: Das Durchlaufen der do-while-Schleifen dauert extrem lange. Ich 
empfange am PC circa alle 3 Sekunden ein Zeichen, was bedeutet, dass der 
Atmega1284P mit 18,432MHz für die 3 popeligen verschachtelten Schleifen 
ganze 3 Sekunden braucht.

Nach meiner Berechnung mit x,y,z sollten das doch ungefähr
Schrittanzahl= 255*255*3= 195075 Schritte sein, oder? Gut, noch ein paar 
zusätzliche Schritte, also mal grob das doppelte angenommen =390150 
Schritte bei 18,432MHz sein. Sprich die kompletten Schleifen sollten 
nach meiner worst-case-Rechnung innerhalb von ungefähr 0,02S durchlaufen 
sein.

(1/18,432MHz)*390150 = ~0,02s

Die Fuse-Bits sind richtig gesetzt und der Takt wurde an Pin B1 per 
Oszilloskop geprüft.

Nun zu meinen Fragen:
a) Ist es normal, dass der Atmega1284P für solche 3 Schleifen so extrem 
lange, sprich 3 Sekunden benötigt?

b) Läuft der Atmega vielleicht irgendwo mit Handbremse, wenn ja wie kann 
ich diese lösen?

c)Stromsparmodi müssten doch explizit gesetzt werden, damit diese 
greifen, oder?

Für Hilfestellungen möchte ich mich vorab schon einmal bedanken.

Viele Grüße.
Daniel
OP #2933092
Lesenswert?

Hallo Uwe,

ja na klar...das Zurücksetzen von x,y,z habe ich nicht in der 
while(1)-Schleife gehabt. Habe es nun so angepasst, dass x,y,z nach dem 
senden von x=0 per uart zurück auf 255,255,3 gesetzt werden.
1
#include "bitOperations.h"
2
#include "uart1284P.h"
3
#include <avr/io.h>
4
#include <stdlib.h>
5
#include <util/setbaud.h>
6

7
int main(void)
8
{
9
  uint8_t x = 255;
10
  uint8_t y = 255;
11
  uint8_t z = 3;
12
  
13
  set_bit(DDRB,0);
14
    
15
    while(1)
16
    {
17
    set_bit(PORTB,0);
18
    
19
    do{
20
      do{
21
        do{
22
          
23
        } while(--z);
24
      }while(--y);
25
    }while(--x);
26
    
27
    clear_bit(PORTB,0);
28
    uart0_data(x); 
29

30
    x = 255;
31
    y = 255;
32
    z = 3;                     
33
    }
34
}

Ändert aber nichts daran, dass er trotzdem für das Durchlaufen der 
Schleifen etwa 3s benötigt. :-(
OP #2933099
Lesenswert?

c) Ist meine Annahme, dass der uC 255*255*3 Ticks für die Schleifen 
benötigt falsch?
Die z-Schleife wird von 3 runter auf 0 gezählt und das genau 255mal 
(y-Schleife) und das Ganze wird dann nochmal 255mal gemacht 
(x-Schleife).

@Peter II
Ich verwende das AtmelStudio 6 mit allen updates, und ASF Version 3.4.1 
(weiss nicht, ob das wichtig ist).

In der Toolchain habe ich Optimization Level = Optimize (-O1).

Viele Grüße,
Daniel
OP #2933107
Lesenswert?

@Oliver

Wenn ich den uart rausnehme, dann ändert sich daran nichts, die LED 
blinkt genauso langsam.

Habe gerade mal die Schleifen rausgenommen und sobald diese raus sind, 
rennt der uart und die LED glimmt nur noch (verständlich).

Die Schleifen werden auch bei der höchsten Optimierung (-O3) nicht 
wegoptimiert. Ob ohne oder mit Optimierung, Programm läuft genauso 
langsam.

Der limitierende Faktor sind die Schleifen, aber wieso? Wieso braucht 
der uC für die 3 Schleifen so lange?

Viele Grüße,
Daniel
Gast #2933112
Lesenswert?

Nachtrag:
   hast du nach der umstellung der Optimierung auch mal die anwendung 
neu compilieren lassen? Wenn du nicht weiter am quellcode änderst, dann 
wird es nicht neu compiliert weil die änderung der Option keine 
wirkliche änderung ist. Am besten "make clean" machen!
#2933118
Lesenswert?

Guck noch mal gut, du machst die x=255;y=255;z=3; jetzt irgendwo anders 
aber noch nicht auf der gute stelle.
Vielleicht wird es dir mehr deutlich wenn du die x,y,z werte zum uart 
sendest und dir die mal anschaust

Entweder du findest aus wo die am besten stehen oder wenn du x und y 
beiden 255 lasst kannst du es umdrehen :

do{
  do{
    do{
    } while(--x);
  }while(--y);
}while(--z);
#2933134
Lesenswert?

Hallo,

ich habe den Code mal schnell auf einem atmega32 mit Takt= 14,7456MHz 
laufen lassen.

Ohne Optimierung 12 Sekunden Laufzeit bis zu einem "X".
1
;{39}{ do } ------------------------------------------------------------
2
_do0:
3
;{40}{ x = 255 } -------------------------------------------------------
4
ldi    _HA0,0xFF
5
sts    dVarClassAvrx,_HA0
6
;{41}{ y = 255 } -------------------------------------------------------
7
ldi    _HA0,0xFF
8
sts    dVarClassAvry,_HA0
9
;{42}{ z = 3 } ---------------------------------------------------------
10
ldi    _HA0,0x03
11
sts    dVarClassAvrz,_HA0
12
;{44}{ do } ------------------------------------------------------------
13
_do1:
14
;{45}{ do } ------------------------------------------------------------
15
_do2:
16
;{46}{ do } ------------------------------------------------------------
17
_do3:
18
;{47}{ decr z } --------------------------------------------------------
19
GetVarAddrZ  dVarClassAvrz
20
ld    _HA0,Z
21
dec    _HA0
22
st    Z,_HA0
23
;{48}{ loop until(z = 0) } ---------------------------------------------
24
_do3until:
25
ldi    _HB0,0x00
26
lds    _HA0,dVarClassAvrz
27
cp    _HA0,_HB0
28
xbrne  _do3
29
_do3__brx0:
30
_do3break:
31
;{50}{ decr y } --------------------------------------------------------
32
GetVarAddrZ  dVarClassAvry
33
ld    _HA0,Z
34
dec    _HA0
35
st    Z,_HA0
36
;{51}{ loop until(y = 0) } ---------------------------------------------
37
_do2until:
38
ldi    _HB0,0x00
39
lds    _HA0,dVarClassAvry
40
cp    _HA0,_HB0
41
xbrne  _do2
42
_do2__brx1:
43
_do2break:
44
;{53}{ decr x } --------------------------------------------------------
45
GetVarAddrZ  dVarClassAvrx
46
ld    _HA0,Z
47
dec    _HA0
48
st    Z,_HA0
49
;{54}{ loop until(x = 0) } ---------------------------------------------
50
_do1until:
51
ldi    _HB0,0x00
52
lds    _HA0,dVarClassAvrx
53
cp    _HA0,_HB0
54
xbrne  _do1
55
_do1__brx2:
56
_do1break:
#2933141
Lesenswert?

>     do{
>      do{
>        do{
>
>        } while(--z);
>      }while(--y);
>    }while(--x);

Die innerste Schleife wird rund 16.5 Millionen mal durchlaufen. Also ich 
finde 3 Sekunden dafür nicht schlimm. Zähl du doch mal soweit!


> c) Ist meine Annahme, dass der uC 255*255*3 Ticks für die
> Schleifen benötigt falsch?
> Die z-Schleife wird von 3 runter auf 0 gezählt und das genau
> 255mal (y-Schleife) und das Ganze wird dann nochmal 255mal
> gemacht (x-Schleife).

Jau. Das ist falsch. Die innerste (z) Schleife wird einmal von 3 auf 0 
runtergezählt und danach 255*255 mal von 255 bis auf 0

Tu dir selbst einen Gefallen und schreib 'for', wenn du 'for' meinst.
OP #2933144
Lesenswert?

@Peter II

Optimierung auf -Os geändert; Make clean gemacht und neu compiliert; 
keine Veränderung

1) wie schnell läuft denn der µC? 18,432MHz

2)Welcher Quarz welche Fuse sind gesetzt? 18,432MHz Quarz
Low Fuse = 0xFF (Ext.Crystal Osc.:Freq 8.0- MHz), kein Divide clock by 8
High Fuse = 0xD9
Extended Fuse = 0xFF
Lockbits = 0xFF

LSS-File ist angehängt
Angehängte Dateien:
#2933150
Lesenswert?

Daniel M. schrieb:
> @Peter II
>
> Optimierung auf -Os geändert; Make clean gemacht und neu compiliert;
> keine Veränderung

Nochmal: das passt schon alles.
(Trotzdem solltest du auf -Os bleiben)

Du hast einen Programmfehler gemacht.
Du hast dich verrechnet. Deine innerste Schleife wird run 16.5 Millionen 
mal durchlaufen und nicht, wie du fälschlich ausgerechnet hast, 
195tausend mal.

Und für 16.5 Millionen Durchläufe sind 3 Sekunden angemessen.

Deine Zähler

Durchlauf      x      y     z
  1            255   255    3
  2            255   255    2
  3            255   255    1
  4            255   255    0
  5            255   254  255     <-  hier liegt dein Fehler!
  6            255   254  253
  7            255   254  252

Schreib "for", wenn du "for" meinst!. Dann vergisst du auch nicht, dass 
du die Zähler vor Beginn einer Schleife neu initialisieren musst. 
Speziell dann, wenn Schleifen ineinander geschachtelt sind und die 
innere Schleife eine spezielle Initialisierung braucht.
Gast #2933160
Lesenswert?

Karl Heinz Buchegger schrieb:
> Daniel M. schrieb:
>> @Peter II
>>
>> Optimierung auf -Os geändert; Make clean gemacht und neu compiliert;
>> keine Veränderung
>
> Nochmal das passt schon alles.

ja der code sieht schon etwas optimiert aus, aber warum der compiler 
nicht erkennt das die schleife komplett weg kann ist mir nicht ganz 
klar.
Gast #2933163
Lesenswert?

Hallo,

Ich habe gestern mit Daniel an dem Quelltext gesessen.  Dass die 
do-Schleifen murks sind sehe ich jetzt auch ein,  aber vorher waren das 
for-Schleifen mit dem selben Laufzeitverhalten von ca.  3 Sekunden. Wir 
hatten nur irgendwo noch als Optimierung gelesen,  dass do-Schleifen 
performanter sind und das darum geändert.
#2933169
Lesenswert?

Peter II schrieb:

> ja der code sieht schon etwas optimiert aus, aber warum der compiler
> nicht erkennt das die schleife komplett weg kann ist mir nicht ganz
> klar.

Das wird dir nur Johann L. sagen können. Mag sein, dass der Optimizer 
diesen Fall einfach nicht mitkriegt, weil das die Mehrzahl der 
Programmierer so nicht schreiben würden und es bisher niemandem 
aufgefallen ist.
#2933171
Lesenswert?

Daniel M. schrieb:
> @Karl Heinz Buchegger
>
> das neu initialisieren habe ich mittlerweile korrigiert;

Nein, hast du nicht

sieh dir nochmal die Zählerwerte an, die ich dir tabellarisch angegeben 
habe! Aber diesmal GANZ GENAU!

Durchlauf      x      y     z
  1            255   255    3
  2            255   255    2
  3            255   255    1
  4            255   255    0
  5            255   254  255     <-  hier liegt dein Fehler!
  6            255   254  253
  7            255   254  252


Warum geht es mit dem z-Wert im Durchlauf 5 bei 255 weiter (nachdem y 
das erste mal runtergzählt wurde)?
#2933181
Lesenswert?

Karl Heinz Buchegger schrieb:
> Daniel M. schrieb:
>> @Karl Heinz Buchegger
>>
>> das neu initialisieren habe ich mittlerweile korrigiert;
>
> Nein, hast du nicht
>
> sieh dir nochmal die Zählerwerte an, die ich dir tabellarisch angegeben
> habe! Aber diesmal GANZ GENAU!
>
> Durchlauf      x      y     z
>   1            255   255    3
>   2            255   255    2
>   3            255   255    1
>   4            255   255    0
>   5            255   254  255     <-  hier liegt dein Fehler!
>   6            255   254  253
>   7            255   254  252
>
>
> Warum geht es mit dem z-Wert im Durchlauf 5 bei 255 weiter (nachdem y
> das erste mal runtergzählt wurde)?

Antwort:
Weil das genau das ist, was bei
1
   y = 255;
2
   z = 3;
3

4
   do {
5
    do {
6
    } while( --z );
7
  } while( --y );
zu erwarten ist. Du WOLLTEST zwar z immer wieder bei 3 beginnen lassen, 
also so
1
   y = 255;
2

3
   do {
4

5
    z = 3;
6
    do {
7
    } while( --z );
8

9
  } while( --y );
hast es aber nicht getan.

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