Forum: Mikrocontroller und Digitale Elektronik AVR Atmega1284P - langsam?!


von Daniel M. (maxone)


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

von Uwe (de0508)


Lesenswert?

Hallo,

Daniel M. schrieb:
> Nach meiner Berechnung mit x,y,z sollten das doch ungefähr
> Schrittanzahl= 255*255*3= 195075 Schritte sein, oder?

Nach dem ersten Durchlauf mag das so sein, aber im 2.ten sind alle 
Variabel x,y,z = 0 und dann ?

Kannst Du bestimmt selbst Rechnen ?

Ticks = 256³

von schleife (Gast)


Lesenswert?

ich weiss ja nicht was Du damit bezwecken willst, aber die laufen x * 
y*x * z*y*x, oder so in dem Sinne

von Daniel M. (maxone)


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. :-(

von Peter II (Gast)


Lesenswert?

Daniel M. schrieb:
> Ändert aber nichts daran, dass er trotzdem für das Durchlaufen der
> Schleifen etwa 3s benötigt. :-(

das sollte in 0 Sekunden erledigt sein, hast du die optimierung 
eingeschaltet? Dann sollte nichts mehr von den schleifen übrig bleiben.

von Daniel M. (maxone)


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

von Oliver S. (oliverso)


Lesenswert?

Wie lange braucht das Programm denn ohne den Aufruf von uart0_data(x) ?

Oliver

von Peter II (Gast)


Lesenswert?

Daniel M. schrieb:
> In der Toolchain habe ich Optimization Level = Optimize (-O1).

dann stellt mal auf -Os um.

Und poste uns mal das LSS file.

von Daniel M. (maxone)


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

von Peter II (Gast)


Lesenswert?

Daniel M. schrieb:
> Der limitierende Faktor sind die Schleifen, aber wieso? Wieso braucht
> der uC für die 3 Schleifen so lange?

wie schnell läuft denn der µC? Welcher Quarz welche Fuse sind gesetzt?

Und bitte mal das LSS file senden, dort steht drin was er macht.

von Peter II (Gast)


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!

von Patrick C. (pcrom)


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

von Uwe (de0508)


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:

von Karl H. (kbuchegg)


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.

von Daniel M. (maxone)


Angehängte Dateien:

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

von Karl H. (kbuchegg)


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.

von Peter II (Gast)


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.

von Marcel B. (Gast)


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.

von Daniel M. (maxone)


Lesenswert?

@Karl Heinz Buchegger

das neu initialisieren habe ich mittlerweile korrigiert; [siehe Beitrag] 
-Beitrag "Re: AVR Atmega1284P - langsam?!"

von Karl H. (kbuchegg)


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.

von Karl H. (kbuchegg)


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)?

von Karl H. (kbuchegg)


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.

von Daniel M. (maxone)


Lesenswert?

@Karl Heinz Buchegger

Du hast absolut Recht ... ich initialisiere die Werte in der 
darüberliegenden Schleife nicht neu! Asche auf mein Haupt.

Das werde ich nun mal einfügen und testen .. bis gleich :-)

von Karl H. (kbuchegg)


Lesenswert?

Und das nächste mal schreibst du es so

   for( x = 255; x > 0; x-- )
   {
     for( y = 255; y > 0; y-- )
     {
       for( z = 3; z > 0; z-- )
       {
       }
     }
   }


und dann passiert dir deser Fehler nicht mehr. Denn durch das for musst 
du dir automtisch Gedanken über Schleifenstartwerte machen.

von Karl H. (kbuchegg)


Lesenswert?

Ganz abgesehen davon:
Wozu soll diese Beschäftigungstherapie für den µC gut sein?

von Daniel M. (maxone)


Lesenswert?

-- Das war die Lösung, vielen Dank an Karl Heinz und alle anderen für 
die schnelle Hilfe!

Viele Grüße,
Daniel

ps: der Atmega ist ja doch nicht so langsam...und ich hab geschimpft 
grml :-D

von Karl H. (kbuchegg)


Lesenswert?

Daniel M. schrieb:

> ps: der Atmega ist ja doch nicht so langsam...und ich hab geschimpft
> grml :-D

In mehr als 95% aller Fälle sitzt das Problem vor dem Bildschirm

von Daniel M. (maxone)


Lesenswert?

@ Karl Heinz

Der µC soll empfangene DMX-Daten auf 4 Kanälen auswerten und diese für 
192 (8x8 RGB-)LEDs aufbereiten. Die aufbereiteten Daten sollen dann an 
einen zweiten µC per UART gesendet werden, welcher die LEDs per PWM 
ansteuert.

von Daniel M. (maxone)


Lesenswert?

Karl Heinz Buchegger schrieb:
> In mehr als 95% aller Fälle sitzt das Problem vor dem Bildschirm

Da kann ich dir nur zustimmen, davon sind wir zum Anfang auch 
ausgegangen und haben den Fehler einfach nicht gesehen ... Also war zu 
späterer Stunde (heute Nacht 3Uhr) der Atmega schuld ;-)

von Karl H. (kbuchegg)


Lesenswert?

Daniel M. schrieb:
> @ Karl Heinz
>
> Der µC soll empfangene DMX-Daten auf 4 Kanälen auswerten und diese für
> 192 (8x8 RGB-)LEDs aufbereiten.

Du wirst doch nicht 195-kByte in einem Rutsch übertragen! Oder doch?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Daniel M. schrieb:
> Der µC soll empfangene DMX-Daten auf 4 Kanälen auswerten und diese für
> 192 (8x8 RGB-)LEDs aufbereiten.

Und dafür willst du ihn so aufwändig Däumchen drehen lassen?

von Daniel M. (maxone)


Lesenswert?

Nein, das muss natürlich noch alles angepasst werden. Aber wie gesagt, 
gestern haben wir erstmal an dem Problem gehangen und sind einfach nicht 
darauf gekommen. Vielen Dank nochmal dafür.

Wer keine Probleme hat, der schafft sich welche ;-)

Viele Grüße,
Daniel

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.