Zeilenweise keine Optimierung AVr Studio

#2675529
Lesenswert?

Es gibt Wege und Tricks, wie man bestimmte Optimierungen gezielt 
verhindet. Beispielsweise indem die Variablen volatile sind. Andere 
Tipps setzen einen grösseren Kontext voraus.

Ausserdem wäre noch offen, ob du grad die richtige Frage stellst, oder 
schon ein Weilchen auf dem Holzweg unterwegs bist und an der falschen 
Stelle fragst.
Gast #2675540
Lesenswert?

A. K. schrieb:
> Ausserdem wäre noch offen, ob du grad die richtige Frage stellst, oder
> schon ein Weilchen auf dem Holzweg unterwegs bist und an der falschen
> Stelle fragst.

Es handelt sich hier um eine Zeitkritische verarbeitung. Die wirklichen 
Zeilen in meinem Code lauten:
1
value = TCNT1;
2
capture._int[x*2] = value;  //Dieser Code verbraucht ca. 30 byte mehr als der obere, deutlich länger als der obere Code.

Ich möchte erst den zeitkritischen Transfer in value machen, dann den 
Zeitaufwendigerendarunter.
Gast #2675568
Lesenswert?

1
[...]
2
  unsigned int value;
3
  
4
  union{
5
    unsigned char _char[NULL_N*4];
6
    unsigned int _int[NULL_N*2];
7
    unsigned long _long[NULL_N];
8
  }capture;
9
[...]
10
  capture._int[x*2] = TCNT1;
11
  caapture._int[x*2+1] = (int)overflow;
12
[...]


capture._int[x*2] = TCNT1; Diese Zeile dauert viel zu lange. 30 Bytes 
(warum soviel?).
#2675610
Lesenswert?

1
void test( uint8_t x )
2
{
3
  capture._int[x*2] = TCNT1;
4
  82:   2c b5           in      r18, 0x2c       ; 44
5
  84:   3d b5           in      r19, 0x2d       ; 45
6
  86:   e8 2f           mov     r30, r24
7
  88:   f0 e0           ldi     r31, 0x00       ; 0
8
  8a:   ee 0f           add     r30, r30
9
  8c:   ff 1f           adc     r31, r31
10
  8e:   ee 0f           add     r30, r30
11
  90:   ff 1f           adc     r31, r31
12
  92:   e0 5a           subi    r30, 0xA0       ; 160
13
  94:   ff 4f           sbci    r31, 0xFF       ; 255
14
  96:   31 83           std     Z+1, r19        ; 0x01
15
  98:   20 83           st      Z, r18
16
}
17
  9a:   08 95           ret

TCNT1 wird doch zuerst eingelesen.
Und der Rest ist eben den Index aufdröseln. Das geht nicht besser.

30 Byte = 15 Befehle ist fürn nen RISC garnichts.
Besser wirds nur mit nem CISC, der entsprechende Adressierungsbefehle 
hat.


Peter
#2675617
Lesenswert?

Es kann passieren, dass eine vorherige Berechnung von x erst an dieser 
Stelle durchgeführt wird. Das entspricht dann exakt dem Problem im oben 
angeführten Thread.

Abhilfe wäre dann wie ebendort:
  #define barrier(var) asm volatile ("" : "+r"(var))
  ...
  barrier(x);
  capture._int[x*2] = TCNT1;

Effektiver wäre aber wohl sowas wie
  unsigned *p = &capture._int[x*2];
  barrier(p);
  p[0] = TCNT1;
  p[1] = (int)overflow;
weil dann die Adressrechnung ebenfalls vorgezogen wird.
Gast #2675672
Lesenswert?

ALso es geht um Frequenzmessung. Wenn ich 55 Hz erzeuge messe ich ca. 
55,01.. Hz und bei 4000HZ messe ich 4100.

Der Timerwert spiegelt die Frequenz wieder. Es scheint ein konstanter 
Verarbeitungswert des Timerwertes um 30-40 Taktzyklen vorzuliegen, 
welcher den Timerwert verfälscht. Bei tiefen Frequenzen sind diese +30 
kaum erwähnenswert, da dem gegenüber ein Timerwert von 100.000+ steht. 
Bei sehr hohen Frequenzen liegt allerdings ein Timerwert von 4000- vor, 
da sind dann diese 30 Taktzyklen verarbeitungszeit katastrophal.

Also vom feststellen, dass ein Nulldurchgang passiert ist, bis zu dem 
Zeitpunkt, wo der Timerwert abgeseichert ist vergeht zuviel Zeit. Die 
Schleife, ob ein Nulldurchgang passiert braucht ca. 14 Byte. Das kann 
natürlich auch die Fehlerquelle sein.

Ich hab mal einen schnelleren Quarz verwendet und schon ist der Fehler 
deutlich geringer. Bin aber schon bei 16Mhz.
Gast #2675840
Lesenswert?

Ich verwende jetzt mal den ICP, kriege aber identische Werte heraus.
Der Entsprechende Code sieht ungefähr so aus:
1
for(x = 0; x < NULL_N ; x++){    //N Nulldurchgänge messen  
2
  if(TIFR & (1<<ICF1)) TIFR |= (1<<ICF1); 
3
  TCNT1 = 0;        
4
  overflow = 0;
5
  if(TIFR & (1<<TOV1)) TIFR |= (1<<TOV1);
6

7
  do{    
8
    if(TIFR & (1<<TOV1)){    
9
      overflow++;      
10
      TIFR |= (1<<TOV1);
11
      if(overflow > 4) break;
12
    }
13
  }while(!(TIFR & (1<<ICF1)));
14

15
  capture._int[x*2] = ICR1;
16
  capture._int[x*2+1] = (int)overflow;  
17

18
  if(overflow > 4) break;
19
}

Kann vielleicht die Software die ich verwende doch ungenaue Frequenzen 
erzeugen? Oder habe ich bei der Konfiguration des ICP etwas verkehr 
tgemacht? Ich benutze den Analog Komparator als Trigger.
Gast #2675992
Lesenswert?

Peter Dannegger schrieb:
> Damit holst Du doch wieder nen Jitter rein.
> Du mußt den Timer durchlaufen lassen und die Differenz von einem zum
> nächsten ICP nehmen.

Natürlich kostet das Zeit, aber diese Befehle werden bei einer maximal 
zu messenden Frequenz von ca. 4400Hz doch locker innherlab einer halben 
Periode durchgeführt.

Also erst die neue Messung initialisieren, dann warten, bis das Flag vom 
ICP gesetzt wird und darauf reagieren. Bis das Warten auf das Flag 
beginnt ist doch bei weitem noch keine halbe Periode vergangen. Nach dem 
Warten wird ebenfalls wieder keine halbe Periode vergehen, bis wann auf 
den nächsten Nulldurchgang wartet.

Bzw. da der ICP auf steigende oder fallende Flanke reagiert und nicht 
auf einen Flankenwechsel, kann man nur ganze Perioden messen oder? So 
hab ich es eben gemacht.
#2676000
Lesenswert?

Nils schrieb:

> Natürlich kostet das Zeit, aber diese Befehle werden bei einer maximal
> zu messenden Frequenz von ca. 4400Hz doch locker innherlab einer halben
> Periode durchgeführt.

Es bringt nichts, den Timer am Ende der Messzeit per Capture taktgenau 
einzufangen, aber dessen Anfangswert per Software mit Jitter zu setzen. 
Das kann sogar weniger genau sein als zweimal per Software.

Da du den Timer nicht per Capture-Event auf 0 setzen kannst, musst du 
statt dessen die Differenz zwischen zwei Captures verwenden. Nur so wird 
das taktgenau. Der Anfang der Messung ist genauso wichtig wie das Ende.
#2676019
Lesenswert?

Nils schrieb:
> Ich verwende jetzt mal den ICP, kriege aber identische Werte heraus.
> Der Entsprechende Code sieht ungefähr so aus:
>
> [c]for(x = 0; x < NULL_N ; x++){    //N Nulldurchgänge messen
>   if(TIFR & (1<<ICF1)) TIFR |= (1<<ICF1);
>   TCNT1 = 0;
>   overflow = 0;
>   if(TIFR & (1<<TOV1)) TIFR |= (1<<TOV1);


Hä?

Gemeint war: verwende den Input Capture Interrupt in Form einer ISR! 
Hier hast du doch schon wieder Latenzen.

es gibt doch wirklich genügend Frequenzzähler-Beiträge hier im Forum und 
in der Codesammlung. Mal ansehen wie andere das machen?
#2676029
Lesenswert?

Jörg Wunsch schrieb:

> bedenkenlos voneinander subtrahieren kann, solange man sich
> sicher ist, dass zwischen beiden Werte höchstens ein timer overflow
> dazwischen liegt.

Und genau da liegt bei ihm der Hase im Pfeffer, denn der Code zählt auch 
die Overflows mit, weil er sonst offenbar mit dem Frequenzbereich nicht 
klar kommt. Das geht zwar, aber weil da hässliche Gleichzeitigkeiten 
auftreten können muss man deutlich Hirnschmalz in eine Korrektur 
investieren, um nicht ab und zu Fehlmessungen zu bekommen.

Weniger anstrengend für die grauen Zellen: den Prescaler adaptiv 
einstellen. Wenn mehr als ein Overflow reinrutscht, dann war der 
Prescaler zu klein, also vergrössern und nochmal messen. Wenn die 
gemessene Differenz zu klein ist, dann war der Prescaler zu gross.
Gast #2676049
Lesenswert?

A. K. schrieb:
> Und genau da liegt bei ihm der Hase im Pfeffer, denn der Code zählt auch
> die Overflows mit, weil er sonst offenbar mit dem Frequenzbereich nicht
> klar kommt. Das geht zwar, aber weil da hässliche Gleichzeitigkeiten
> auftreten können muss man deutlich Hirnschmalz in eine Korrektur
> investieren, um nicht ab und zu Fehlmessungen zu bekommen.
>
> Weniger anstrengend für die grauen Zellen: den Prescaler adaptiv
> einstellen. Wenn mehr als ein Overflow reinrutscht, dann war der
> Prescaler zu klein, also vergrössern und nochmal messen. Wenn die
> gemessene Differenz zu klein ist, dann war der Prescaler zu gross.

So ist es. Das mit dem Prescaler wechseln, auf die IDee bin ich bis 
jetzt noch nicht gekommen. Ich habe den Code jetzt soweit verbessert, 
ohne ICP, dass ich bei 4000Hz ein Pendeln zwischen 4011-4018 angezeigt 
bekomme.

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