Forum: Mikrocontroller und Digitale Elektronik Term vereinfachen / ATMega48


von Fred (Gast)


Lesenswert?

Hallo,

bin mit meinem ATMega48 an der Speichergrenze angelangt und bräuchte 
aber noch Platz für ein paar Zeilen.

Versuche nun noch etwas Speicher freizubekommen. Habe öfter Terme wie 
diesen hier:
1
if(((driving_old <= 127) && (driving >= 128)) || ((driving <= 127) && (driving_old >= 128)))

Diese nehmen doch erheblich Speicher in Anspruch. Hat jemand ne Idee wie 
man diese Terme etwas Speichereffizienter gestalten kann.



Gruß Fred

von µCnoob (Gast)


Lesenswert?

So einfach geht das nicht,
ich weiß jetzt nicht für was diese Bedingung gedacht ist, in welchem 
Zusammenhang diese steht??

Die einzige Optimierungsmöglichkeit die ich hier (im Moment) sehe ist 
den Assemblercode anzusehen und zu hoffen dass der Compiler nicht 
optimal Compiliert hat.

Aber schick doch mal EINE komplette Beispielfunktion, so dass man sich 
mal die Zusammenhänge des Ganzen ansehen kann und evtl. tipps für deren 
Optimierung geben kann...

Gruß

von Moi (Gast)


Lesenswert?

if ((driving_old ^ driving) & 0x80)

von Moi (Gast)


Lesenswert?

@ µCnoob: Deine Name ist Programm, oder? :-P

von Tobi (Gast)


Lesenswert?

Handelt es sich um 8-Bit Werte? Das ist nicht ersichtlich aus deinem 
Code hier. Sonst klappt mois Vorschlag nicht!
Bei 8 - Bit Werten entspricht <=127 einer Prüfung des MSB auf '0'
>= 128 hesst hingegen, dass das MSB '1' ist.

Gruß Tobi

von Moi (Gast)


Lesenswert?

Das mit den 8 Bit war eine Annahme von mir, da die Namen der Variablen 
darauf schließen lassen, dass 127 die Mitte der Wertebereichs ist.

von gast (Gast)


Lesenswert?

Mein Vorposter hat das schon sehr richtig geschrieben!

Das ganze nur nochmal zur Erklaerung:

Ausgangspukt:
if(
        ((driving_old <= 127) && (driving >= 128))
     || ((driving <= 127) && (driving_old >= 128))
)

uebersetzt:

wenn alter wert kleinerGLEICH 127 UND neuer Wert groesserGLEICH 128
ODER
wenn alter wert kleinerGLEICH 127 UND alter Wert groesserGLEICH 128

fuer sowas empfehle ich digital Logik
definiere:
kleinerGLEICH 127 entspricht "logisch null"
groesserGLEICH 128 entspricht "logisch eins"

stelle eine wahrheitstabelle auf:
DO: Driving old
D:  Driving

DO   |   D      | Ausgang
0        1        WAHR
1        0        WAHR
0        0        FALSCH
1        1        FALSCH


Welchem LogikTyp entspricht das?
Ich wuerde sagen, dass ist ein klassisches Exklusiv-Oder (EXOR).

128 = 0x80

Ein Wert muss als immer unter 128 sein und ein Wert ueber 127.

x = (DO & 0x80) ^ (D & 0x80);

jetzt kann man 0x80 ausklammener und erhaelt

x = (DO ^ D) & 0x80;

von Morin (Gast)


Lesenswert?

Der Compiler hat möglicherweise nicht erkannt, dass "x <= 127" und "x >= 
128" logische Komplemente sind. Mir fällt aber (außer auf Assembler 
runterzugehen) im Moment keine Möglichkeit ein, das auszunutzen.

von gast (Gast)


Lesenswert?

Jetzt waere noch interessant was hier fuer den Compiler als 
"optimierter" erscheint...

>Ein Wert muss als immer unter 128 sein und ein Wert ueber 127.

>x = (DO & 0x80) ^ (D & 0x80);

hier wird schon mal das hoechste Bit maskiert.
Die EXOR Operation koennte so optimiert sein, dass sie nur noch das 
hoechste Bit vergleicht...

>jetzt kann man 0x80 ausklammener und erhaelt

>x = (DO ^ D) & 0x80;

hier werden beide Bytewerte bitweise "ver-exort" und DANN das hoechste 
Bit ausmarkiert..

Ich denke es kommt aufs selbe raus

von gast (Gast)


Lesenswert?

hab gerade nochmal den topic gelesen..

er versucht speicher freizumachen..

ich denke solche if-bedingen kann der compiler schon selbst ganz gut 
optimieren..

such doch mal nach großen konstanten Strukturen, die evtl. 
ueberdimensioniert sind, oder auch evtl im RAM liegen können.

vielleichst du unnoetige (nicht benutze) Funktionen, auch raus damit 
oder auskommentieren...

hatte letzt auch so ein Problem mit meinem Controller, da war 
(lizenzbedingt) der Speicher ausgeschoepft...
32 K ueberschritten...
haben jetzt ne Lizenz fuer 64 K ;-)

von Moi (Gast)


Lesenswert?

In Assembler wird immer das ganze Byte ver xor't (1 Taktzyklus), 
ausmaskieren ist auch nur ein Befehl (also auch 1 Takt)

eor r16, r17
andi r16, 0x80
breq 'zieladresse nach if-block'


Meine Lösung kann also durch 3 Assembler Befehle ersetzt werden und 
sollte somit optimal sein.

von Fred (Gast)


Lesenswert?

Danke für die Antworten!

Ja, es sind 8-Bit Werte.  Was mir nicht ganz klar ist auf was die if 
Abfrage sich bei
1
if ((driving_old ^ driving) & 0x80)
 bezieht.

Zuerst wird ja eine "Exclusiv Oder Verknüpfung" und anschließend eine 
Und-Verknüpfung durcheführt. Doch auf was fragt die if-Anweisung das 
Ergebnis ab?


Hier mal der komplette Code der Routine. Wahrscheinlich gibt es da noch 
einiges zu verbessern und zu vereinfachen.
1
//********************************************************************************
2
//* Description: calculate accelaration and driving speed                        *
3
//********************************************************************************
4
void driving_routine(void)
5
{
6
  
7
  if((driving <= 130) && (driving >= 128))
8
  {
9
    driving = 128;
10
  }
11
  
12
  if(driving_release == 0x05)
13
  {
14
    //diff decides over deceleration or accelerating
15
    int diff = (driving - driving_old);
16
      
17
    if(((driving_old <= 127) && (driving >= 128)) || ((driving <= 127) && (driving_old >= 128)))
18
    {          
19
      char h = 0;
20
21
      while(h <= 8)
22
      {
23
        wait_n_10us(250);
24
        h++;
25
      }
26
27
    }
28
29
30
    //forward drive
31
     if(driving >= 128)
32
     {
33
        //accelerate
34
      if( diff > 50 )          
35
        {
36
          diff = 50;
37
        driving_old += diff;
38
        }
39
40
      //decelerate
41
      else if( diff < -50 )      
42
         {
43
        diff = -50;
44
        driving_old += diff;
45
      }
46
47
      //maintain speed
48
      else            
49
        driving_old = driving;
50
    }
51
52
    //backwards drive
53
      else                
54
      {
55
      //decelerate
56
        if( diff > 50 )          
57
        {
58
           diff = 50;
59
        driving_old += diff;
60
        }
61
62
      //accelerate
63
        else if( diff < -50 )      
64
        {
65
        diff = -50;
66
        driving_old += diff;
67
      }
68
69
      //maintain speed
70
        else              
71
          driving_old = driving;
72
    }
73
          
74
    driving = driving_old;
75
76
    //calculate driving value
77
    if(driving <= 127)
78
    {
79
      driving = (-(driving - 127));
80
    }  
81
  }
82
83
  driving_release = 0x00;
84
}

von Moi (Gast)


Lesenswert?

Zitat vom OP: "Diese nehmen doch erheblich Speicher in Anspruch."

Es ging also explizit um diesen Term. Er selber schreibt, dass diese 
Terme einiges an Speicher in Anspruch nehmen. Also wird er es auch 
geprüft haben.

Ansonsten: Verwendest Du schon den Optimierungsgrad "size" -Os ?!

von Moi (Gast)


Lesenswert?

Die if Bedinung ist dann erfüllt, wenn der Ausdruck in der Klammer 
ungleich 0 ist.

Übersetzt könnte man sagen, der if-Block wird ausgeführt, wenn das 
Höchstwertigste Bit von driving_old und driving unterschiedlich sind.

von Moi (Gast)


Lesenswert?

if((driving <= 130) && (driving >= 128))
  {
    driving = 128;
  }


Diese Bedingung kann nicht wahr werden. Also raus!

von gast (Gast)


Lesenswert?

if ((driving_old ^ driving) & 0x80)

die If-Bedingung wertet dann schluß-endlich nur noch aus
ob das hoechste Bit 1 ist.

Und das ist schon ok so!
Da die Werte vorher EXKLUSIV oder verknuepft wurden.
Damit werden deine beiden FALSCH Faelle ausgeschlossen...

x = (DO & 0x80) ^ (D & 0x80);
 oder  so, wenn das besser verstaendlich ist..


 >   if(driving <= 127)
 >   {
 >     driving = (-(driving - 127));
 >   }


sind das unsigned oder signed Werte?
Denn falls das unsigned Werte sind... ist das letzte -(...) unnoetig 
bzw. falsch....

is das zufaellig ein ASURO ;-)

von Moi (Gast)


Lesenswert?

Ich bin mir nicht sicher ob der Compiler selber so intelligent ist. 
Ersetze mal:

      char h = 0;

      while(h <= 8)
      {
        wait_n_10us(250);
        h++;
      }


durch

      char h = 8;

      while(h)
      {
        wait_n_10us(250);
        h--;
      }

von gast (Gast)


Lesenswert?

@ Moi

was ist daran jetzt besser`?

von Moi (Gast)


Lesenswert?

Dass der Vergleich auf 0 in Assembler schneller geht, als der Vergleich 
auf 8.

von gast (Gast)


Lesenswert?

stimmt da gibts ja nen eigenen Assembler Befehl

von Fred (Gast)


Lesenswert?

Vielen vielen Dank für ausführliche Hilfe!

Bringt pro Zeile nen halben Prozent zusätzlichen Speicher.

>>  if(driving <= 127)
>>  {
>>    driving = (-(driving - 127));
>>  }

>sind das unsigned oder signed Werte?
>Denn falls das unsigned Werte sind... ist das letzte -(...) unnoetig
>bzw. falsch....

>is das zufaellig ein ASURO ;-)

Ja, "driving" ist ein unsigned char. Bei der Abfrage so der Wert gedreht 
werden. Aus einer 127 ne 0 und aus ner 0 ne 127:

Und ne, ist kein Azuro;-)


Einen Term hätt ich noch:
if((test < 190) && (test >= 150) && (driving >= 128) && (driving <= 
130))
[/c]

Kann man den auch noch irgendwie vereinfachen?

von gast (Gast)


Lesenswert?

Einen Term hätt ich noch:
1
if((test < 190) && (test >= 150) && (driving >= 128) && (driving <=
2
130))


Mhm, das ist bei den Zahlen ein bisschen schwierig ;-)

190 ist 1011 1110
150 ist 1001 0110

UND kommt wieder 150 raus...
und bei EXOR 40 (also die Differenz)....

wuesste aber jetzt nicht auf Anhieb wie das zur Optimierung beitraegt...

vielleicht sind einzelene (verschaltete if bedingungen) besser

von Moi (Gast)


Lesenswert?

if((test < 190) && (test >= 150) && (driving >= 128) && (driving <= 
130))

Keine Ahnung, ob es was bringt. Probier mal:

if (((test - 150) < 40) && ((driving - 128) < 3))

von Olaf D. (Firma: O.D.I.S.) (dreyero)


Lesenswert?

Hallo,

bei den if-Abfragen
1
if(((driving_old <= 127) && (driving >= 128)) || ((driving <= 127) && (driving_old >= 128)))
 sollte man bedenken, dass die überprüften Variablen unsigned char sind, 
der Compiler aber aus Konstanten erstmal ein int macht.
D. h. Es werden immer 16bit Vergleiche ausgeführt.

Besser wäre vermutlich
1
if(((driving_old <= (unsigned char)127) && (driving >= (unsigned char)128)) || ((driving <= (unsigned char)127) && (driving_old >= (unsigned char)128)))
.


Gruß

Olaf

von Moi (Gast)


Lesenswert?

Der Term ist doch schon lange optimiert! =)

von gast (Gast)


Lesenswert?

naja mit den Variablen angaben hat er schon Recht.

Ich denke der Compiler rechnet intern mit höherer genauigkeit. was dazu 
führt dass größere Konstanten im Code verwendet werden.

also durch konkrete Größen-Angaben wird der optimierte Code noch 
optimierter ;-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Fred schrob:
1
//********************************************************************************
2
> //* Description: calculate accelaration and driving speed
3
> *
4
> //********************************************************************************
5
> void driving_routine(void)
6
> {
7
> 
8
>   if((driving <= 130) && (driving >= 128))
9
>   {
10
>     driving = 128;
11
>   }
12
> ...
13
>

Offenbar verwendest Du mehrfach die gleiche(n) globalen/statischen 
Variablen.
Ein Rumknaupen an den verwendeten Ausdrücken bringt kaum wass, denn bei 
jedem Zugriff werden 4 Byte verplampert. Also:

Zu Beginn der Routine in lokalte Variablen kopieren und auf diesen 
arbeiten. Geänderte Variablen am Ende natürlich zurückspeichern.

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.