Forum: Compiler & IDEs Tangens sprengt die Codemenge


von Fly (Gast)


Lesenswert?

Ich habe unter anderen folgenden Codeabschnitt geschrieben:
1
tmpDouble1 = atan2( stpNeu.spul2, stpNeu.spul1 );  //den arcus Tangens (Ergebnis als Winkel im Bogenmaß -Pi bis +Pi) aus den beiden ED's berechnen
2
//  tmp16 =  180 / M_PI * tmpDouble1 ;             //in Grad [°] umrechnen
3
if (tmp16<0) tmp16 = 360 + tmp16;                  //negativer Winkel? Umrechenen in positiven.
4
stpNeu.winkel = tmp16;                             //Winkel eintragen

Wenn ich das so compeliere erhalte ich
"Program:    1392 bytes (68.0% Full)"

Wenn ich aber die Auskommentierung // in der zweiten Zeile weg nehme 
kommt
"Program:    4802 bytes (234.5% Full)"!!!

Leider komme ich so auch nicht in den Simulator um nach zu schauen was 
da passiert.

Hat da einer eine Idee wie ich das schreiben kann, dass ich unter 100% 
code komme?

von Roland P. (pram)


Lesenswert?

- Anderen Controller verwenden mit genug Flash

- auf die Fließkommaarithmetik verzichten und das ganze evtl in 
Ganzzahl- oder Fixpunktarithmetik rechnen (kommt auf den Anwendungsfall 
an)

Gruß
Roland

von ARM-Fan (Gast)


Lesenswert?

Ich hab aus dem gleichen Grund mal die Arcustangensfunction aus einer
math-lib, deren sourcecode im Internet vorlag, genommen, weil die ganzen
Bibliotheken sonst den Rahmen gesprengt hätten.

Ist aber schon ne Weile her. Mußt du dich mal umschauen.

Evtl. kann ich am Montag noch was zu dem Thema rauskramen.

von Falk B. (falk)


Lesenswert?

@ ARM-Fan (Gast)

>Ich hab aus dem gleichen Grund mal die Arcustangensfunction aus einer
>math-lib, deren sourcecode im Internet vorlag, genommen, weil die ganzen
>Bibliotheken sonst den Rahmen gesprengt hätten.

Der Compiler ist schlau genug, nur die Funktionen einzubinden, die auch 
wirklich benutzt werden.

MFg
Falk

von yalu (Gast)


Lesenswert?

Der atan2 alleine braucht bei mir etwas über 1100 Bytes. Dass bei dir
das Programm nach Einfügen besagter Zeile so stark explodiert, ist evtl.
damit zu erklären, dass der Compiler bei Nichtbenutzung des Ergebnisses
der atan2-Funktion nicht nur diese, sondern noch eine ganze Menge
weiteren Code wegoptimiert. Wenn du diesen weiteren Code wirklich
brauchst und die darin keine Vereinfachungen vornehmen kannst, bleibt
wohl nur der Weg zu mehr Flash-Speicher.

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

µC-Typ AVR? Mit -lm gelinkt?

von gast (Gast)


Lesenswert?

wie wärs wenn du dir den tan selber baust, schau dir einfach mal die 
summenformel an, kann man nachprogrammieren

von gast (Gast)


Angehängte Dateien:

Lesenswert?

hatten wir mal irgenswann im 1.semester gemacht, evtl kannst das ja 
umsetzen wenns dir schnell genug ist

von Fly (Gast)


Lesenswert?

Danke für die Vorschläge. Ich habe es mal versucht ohne Gleitkomma 
auszukommen:
1
tmp32 = (int32_t) (1000 * atan2( stpNeu.spul2, stpNeu.spul1 ));  
2
tmp32 *= 180;
3
tmp32 /= 3141;
4
tmp16 =  tmp32;
5
if (tmp16<0) tmp16 = 360 + tmp16;      
6
stpNeu.winkel = tmp16;

Lieder hilft es nicht:

Program:    5008 bytes (244.5% Full)

von Helmut L. (helmi1)


Lesenswert?

>Ich habe es mal versucht ohne Gleitkomma
>auszukommen:
>tmp32 = (int32_t) (1000 * atan2( stpNeu.spul2, stpNeu.spul1 ));
                           ^^^^^

Der ist aber nach wie vor in Gleitkomma wenn du ihn nicht selber in 
Integerarithmetik programmiert hast.

Gruss Helmi

von Fly (Gast)


Lesenswert?

@ helmi1


das soll eigentlich der Ausdruck "(int32_t)" machen.
Wie wandelt man richtig ein Double in int32 ?

von Ralf S. (spacedog) Benutzerseite


Lesenswert?

Überleg nochmals: Du machst einen typecast nach int32_t, weil atan2 
einen double ausspuckt. atan2 ist irgendwie so definiert:

double atan2( double y, double x );

Das heisst, dass du immer noch mit double rechnest, auch wenn du das 
ganze nachher nach int32_t castest. Somit wird auch die ganze 
Floatingpoint-Bibliothek eingebunden und der Code wird riesig.

Ich denke nicht, dass es in der C-Bibliothek eine 
Fixkomma-atan2-funktion gibt. Deshalb must du dir die selber 
zusammenwursteln.

von Max (Gast)


Lesenswert?

Der Tangens von (+/-)pi/2 ist (+/-)unendlich. Der arctan ist daher für 
(+/-)pi/2 unbestimmt. Könnte es sein das dein Compiler damit Probleme 
hat?

von yalu (Gast)


Lesenswert?

Nochmals: Es bringt wenig, nur an den paar geposteten Programmzeilen
herumzudoktern, da sie einschließlich hinzugelinkten Bibliotheksfunk-
tionen (FP-Arithmetik und atan2-Funktion) nur etwa 25% des Gesamtcodes
ausmachen. Um unter die 2K-Grenze zu gelangen, musst du den Programmcode
aber um fast 60% reduzieren. Das geht (wenn überhaupt) nur, indem du das
gesamte Programm nach Speicherfressern durchforstest.

von Tavin (Gast)


Lesenswert?

Wie genau brauchst Du eigentlich den Tangens ? macht es vieleicht auch 
eine kleine Tabelle mit Tangenswerten auch ?

von B e r n d W. (smiley46)


Lesenswert?

Hallo Fly

In welchem Wertebereich muss denn der atan() funktionieren? Vielleicht 
lässt sich der Bereich über ein Polynom nachbilden oder durch eine 
Tabelle mit linearer Interpolation.

Bernd

von LOL (Gast)


Lesenswert?

> atan(x) = cos(x) / sin(x);    // für sin(x)=0 -> atan(x)=unendlich

Sechs, setzen.

von B e r n d W. (smiley46)


Lesenswert?

Konstruktive Kritik bitte!

von Falk B. (falk)


Lesenswert?

@  Bernd Weeber (smiley46)

>Konstruktive Kritik bitte!

Hat er doch gemacht. Es wurde nach dem ARCUStangens gefragt 
(Umkehrfunktion des Tangens), nicht dem COtangens!

MFG
Falk

von ABu (Gast)


Lesenswert?

Bei http://de.wikipedia.org/wiki/Arkustangens steht unter 
"Reihenentwicklung", wie man den atan mit einem Polynom annähert. Das 
kann man beliebig lang machen und so die Werte beliebig genau, bzw. den 
Wertebereich beliebig groß machen.

Berechnung dann mit Horner-Schema 
http://de.wikipedia.org/wiki/Hornerschema dann kann man sich die Bildung 
hoher Potenzen sparen.

Grüße

von LOL (Gast)


Lesenswert?

Konstruktiv:
http://www.restena.lu/convict/Jeunes/Math/arctan.htm

Wer aber mit (int32_t) versucht, das Gleitkomma auszutreiben,
wird wohl nichts damit anfangen können.

von ABu (Gast)


Lesenswert?

Nachtrag: Das Polynom konvergiert nur im Bereich -1 bis +1 auf der Wiki 
Seite über den atan steht aber unter der Reihenentwicklung, wie man das 
umgehen kann.

von Peter D. (peda)


Lesenswert?

Bezüglich AVR-GCC ist für float ein 2kB-Chip völlig untauglich, ein 4kB 
absolutes Minimum und erst ab 8kB wirklich sinnvoll.

Ich war es vom 8051 her gewohnt, float auch auf MCs mit 2kB Flash 
(AT89C2051) einzusetzen, aber mit dem AVR-GCC geht das definitiv nicht.

Ich benutze daher als 20-pinner den ATtiny861, ist ohne UART und nur 16 
IOs aber nicht gerade der Brüller.

Vielleicht schafft es ja Microchip endlich den ATtiny2313 als Attiny8313 
mit 8kB zu entwickeln.
Und ein ATmega32313 mit 32kB Flash, 4kB SRAM und 18 IO-Pins wär 
natürlich super.


Peter

von Fly (Gast)


Lesenswert?

Wo das geht ja richtig ab hier!

Das Programm soll aus dem PWM Signal für einen Schrittmotor, der über 
ein Getriebe einen Zeiger dreht, mir die Stellung des Zeigers berechnen. 
Die Hardwarelösung dazu wäre Poti an den Zeiger und die Spannung messen. 
Das fande ich aber uncool.
Den Tangens brauche ich um aus den beiden Spulenströmen den mechanischen 
Winkel zu Berechnen. Die Genauigkeit des Winkels ist mir eigentlich 
nicht so wichtig. Allerdings addiere und subtraiere ich ständig den dazu 
gekommenen Winkel so das sich der Fehler auf die Dauer aufsummiert.
Deswegen hatte ich gerade die Idee lediglich festzustellen in welchen 
Quadranten (oder kleinere Auflösung) sich der Schrittmotor befindet um 
dann nur glatte 90° zu add/sub.
Dazu wäre die Tabellenlösung gut. Macht man das in Form einer SWITCH 
Anweisung?

von Falk B. (falk)


Lesenswert?

@ Fly (Gast)

>Das Programm soll aus dem PWM Signal für einen Schrittmotor, der über
>ein Getriebe einen Zeiger dreht, mir die Stellung des Zeigers berechnen.

Und dazu braucht man den Arcustangens?

>Den Tangens brauche ich um aus den beiden Spulenströmen den mechanischen
>Winkel zu Berechnen. Die Genauigkeit des Winkels ist mir eigentlich
>nicht so wichtig.

Dann nimm ne Tabelle.

> Allerdings addiere und subtraiere ich ständig den dazu
>gekommenen Winkel so das sich der Fehler auf die Dauer aufsummiert.

Bei nen Schrittmotor? Dann machst du was falsch.

>Dazu wäre die Tabellenlösung gut. Macht man das in Form einer SWITCH
>Anweisung?

Um Gottes Willen NEIN! Schon mal was von einem Array gehört?

MFg
Falk

von Fly (Gast)


Lesenswert?

>Und dazu braucht man den Arcustangens?
ähm jup, aber es gibt bestimmt auch andere Lösungen.


>Bei nen Schrittmotor? Dann machst du was falsch.
Der Schrittmotor wird nicht einfach im Vollschritt angesteuert sondern 
jede Spule im PWM betrieben, so das sich ein Sinusförmiger Strom ergibt. 
Die ander Spule entsprechend Cosinus.

>Um Gottes Willen NEIN! Schon mal was von einem Array gehört?
Die Dinger?    meinArry[][];

von yalu (Gast)


Lesenswert?

Peter Dannegger schrieb:

> Bezüglich AVR-GCC ist für float ein 2kB-Chip völlig untauglich, ...

Naja, das ist vielleicht etwas übertrieben. FP-Additonen/Subtraktionen
brauchen weniger als 400 Bytes. Nimmt man noch Multiplikationen hinzu,
sind es knapp 600 Bytes, mit der Division knapp 800 Bytes. Mit den vier
Grundrechenarten kann man schon eine Menge anstellen und hat immer noch
mehr als den halben Flash frei.

Auch die sqrt-Funktion ist mit zusätzlichen 200 Bytes noch recht human.
Die transzendenten Funktion schlagen mit jeweils 200 bis 500 Bytes zu
Buche, wobei Gruppen verwandter Funktionen wie sin/cos, asin/acos oder
log/log10 nur wenig mehr Speicher als die Einzelfunktionen belegen, da
sie viel gemeinsamen Code benutzen.

Nur die pow-Funktion braucht fast 1KB, da sie sowohl log als auch exp
benötigt und auch selbst noch einiges an Code mitbringt.

Ich bin auch kein Freund von FP-Berechnungen auf Mikrocontrollern, aber
bevor man den halben Flash leerstehen lässt, ist es manchmal sinnvoll,
sie mit Bedacht zu verwenden. Natürlich kann auf einem 2K-AVR kein
wissenschaftlicher Taschenrechner mit allen mathematischen Funktionen
programmiert werden, aber es gibt viele Anwendungen, wo nur die
Grundrechenarten und vielleicht noch etwas Trigonometrie benötigt
werden.

Auch Festkommaarithmetik, Look-Up-Tabellen, Interpolationen u.ä. kosten
deutlich Speicher, vor allem dann, wenn sie nicht in Assembler, sondern
C programmiert werden. Und man geht dabei das Risiko zusätzlicher
Programmierfehler ein.


Fly schrieb:

> Der Schrittmotor wird nicht einfach im Vollschritt angesteuert sondern
> jede Spule im PWM betrieben, so das sich ein Sinusförmiger Strom
> ergibt. Die ander Spule entsprechend Cosinus.

Wer erzeugt denn die Sinus/Cosinus-Signale für die Spulen? Der gleiche
Controller? Wenn ja, dann muss der Winkel ja schon irgendwo vorliegen.
Wenn nein, was macht den der Controller sonst noch wildes, dass 4,8KB
Programmcode benötigt werden? Der atan2 alleine bringt das
(Speicher-)Fass sicher nicht zum Überlaufen.

Vielleicht postest du mal den Quellcode der Funktion, aus dem die obigen
4 Zeilen stammen, oder noch besser das ganze Programm. Ich bin mir fast
sicher, dass sich darin noch ein anderer gewaltiger Speicherfresser
versteckt, der leichter zu eliminieren ist als die atan2-Funktion :)

von Fly (Gast)


Angehängte Dateien:

Lesenswert?

So, hier ist nun das Programm. Ich hab mich damit zurückgehalten, weil 
das Programmieren nicht mein täglich Brot ist und damit viel 
"Verbesserungspotential" vorhanden sein wird. Aber aus Kritik kann man 
lernen. Also her damit!
Der Ablauf:

- Interrupt mäßiges einlesen der Eingänge
  * Start mit einer Positiven Flanke an einem der INT0 oder INT1
  * möglichst schnelles einlesen der Eingänge bis zur nächsten positiven 
Flanke

- Auswertung
  * feststellen der Stromrichtung
  * feststellen der Einschaltdauer zwischen den beiden Flanken
  * eliminieren von Fehlmessungen durch vergleich mehrerer Messungen
  * Rückrechnen der Zeigerposition --> dieser Thread
  * Ausgabe über UART

Mit der Rückrechnung habe ich das gerade so, dass lediglich festgestellt 
wird in welchem 1/8 Kreissegment sich der Winkel befindet. So klappt es 
auch mit dem Speicher: Program:    1790 bytes (87.4% Full)

Im folgenden kommt noch ein Flowchart.

von Fly (Gast)


Angehängte Dateien:

Lesenswert?

Hier das Flowchart zum Programm.

von Mario (Gast)


Lesenswert?

Ohne jetzt gleich über das Programm und grundsätzliche Fragen die vorher 
schon aufgeworfen wurden einzugehen:
Die ATAN-Function wird standardmäßig mittels CORDIC algorithmen 
realisiert. Such einfach mal danach im Netz.
Ich hab nur mal schnell hier:
http://de.wikipedia.org/wiki/Cordic

und da was gefunden:
http://www.andraka.com/files/crdcsrvy.pdf
http://www.mathworks.com/matlabcentral/files/19316/content/html/fixed_point_atan2_using_cordic.html

Nimm eine fixed-point Version, dann sollte das mit deutlich weniger 
Flash (und RAM) auskommen.

Mario

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Fly wrote:
> So, hier ist nun das Programm. Ich hab mich damit zurückgehalten, weil
> das Programmieren nicht mein täglich Brot ist

Schöne Ausrede, um nicht mal die Frage nach dem verwendeten µC und den 
verwendeten Compileroptionen zu beantworten. Du verarschst uns hier 
doch.

von Fly (Gast)


Lesenswert?

>Schöne Ausrede, um nicht mal die Frage nach dem verwendeten µC und den
verwendeten Compileroptionen zu beantworten. Du verarschst uns hier
doch.

Ne Kollege, da bist du auf dem Holzweg, verarschen ist das letzte was 
ich tuhe.

Bisher hat es noch keinen interessiert aber ich sag es dir gerne:

Compiler: AVR Studio 4.14 mit AVR GCC plugin
Controller: AT90S2313 ja ich weiß den gibt es nicht mehr aber ich habe 
halt noch welche rum liegen.

von yalu (Gast)


Lesenswert?

> Controller: AT90S2313

So etwas habe ich fast befürchtet ;-) Als du den Code gepostet hast und
ich beim probeweisen Kompilieren über das UCR-Register gestolpert bin,
war klar, dass es sich bei dem Controller um einen Veteran handelt. Und
die Kombination von UCR und 2KB Flash gibt es nur einmal, nämlich beim
AT90S2313 :)

"Befürchtet" schrieb ich deswegen, weil es für diesen Controller keinen
pinkompatiblen Ersatz mit mehr Flash gibt, der dein Problem
wahrscheinlich am leichtesten lösen würde.

> ... weil das Programmieren nicht mein täglich Brot ist

Gerade deswegen ist es deutlich weniger stressig, genügend Flash und RAM
zur Verfügung zu haben, was beim AT90S2313 leider nicht der Fall ist.

Ich habe mir dein Programm durchgesehen mit folgendem Ergebnis:

Mit der atan2-Funktion ist der erzeugte Code tatsächlich sehr groß,
allerdings ist mir nicht ganz klar, wie du auf die 4802 Bytes kommst.
Bei mir (GCC 4.2.4, AVRLibc 1.6.2) sind es nur etwa 3200 Bytes, was aber
natürlich immer noch zu viel ist.

Lässt man den atan2 weg, kommt man zwar unter die 2KB-Grenze, aber es
bleibt kaum Platz für eine Alternativlösung, es sei denn, man ist mit
einer sehr ungenauen Berechnung zufrieden. So etwas hast du ja schon
versucht, indem du die 360° in acht Intervalle von 45° unterteilt hast
und prüfst, in welchem dieser Intervalle der Wert liegt.

Eigentlich hatte ich gehofft, eine Stelle im Programm zu finden, an der
man auf einen Schlag 1KB wegoptimieren kann. Leider verteilt sich der
Speicherfresser auf das ganze Programm, so dass keine schnelle Lösung
möglich ist.

Ein Ansatzpunkt für eine Optimierung wäre, das zweidimensionale Array
stepZustand zu eliminieren. Musst du wirklich die gesampelten
Eingangspegel in ein Array schreiben, um sie hinterher auf die Dauer des
High-Pegels zu analysieren? Das könnte man doch sofort beim Einlesen der
Signale tun, indem man auf die Flanke wartet (entweder gepollt oder per
Interrupt) und auf den Timer schaut, wann sie eintrifft. Arrayzugriffe
kosten auf dem AVR nicht nur viel Programmspeicher, sondern auch Zeit,
was auch die Auflösung deiner Zeitmessung deutlich verschlechtert.

Du startest die Messung mit einem Interrupt auf die steigende Flanke.
Die fallende Flanke fragst du aber in einer Schleife ab. Besser ist es,
für beide Flanken das gleiche Erkennungsverfahren anzuwenden, da sich
dann die Latenzzeit (zumindest teilweise) aufheben. Also entweder beide
Flanken per Interrupt (ich hoffer, dass der AT90S2313 schon auf beide
Flanken reagieren konnte) oder beide per Abfrageschleife. Die
Schleifenmethode ist in diesem Fall wahrscheinlich sogar etwas genauer
und spart auch Programmspeicher, da kein Interrupthandler und keine
Volatile-Variablen benötigt werden.

Leider hat der AT90S2313 keine zwei Input-Capture-Einheiten. Sonst
könntest du die Zeitmessung noch einfacher und noch genauer über diese
abwickeln.

Durch die Messung der Zeit während des Einlesens der Signale wird nicht
nur die Einleseroutine kürzer und schneller. Vor allem bei der
Auswertung sparst du Code und Zeit, weil die ganzen Sampels nicht noch
einmal durchgegangen werden müssen.

Ich würde fast behaupten, dass du auf diese Weise und mit ein paar
kleineren lokalen Optimierungen 1KB oder mehr an Code einsparen kannst,
so dass du bei der Wahl des Verfahrens zur Winkelberechnung aus dem
Sinus- und dem Cosinuswert ausreichend Luft gewinnst.

Trotzdem würde ich an deiner Stelle in Erwägung ziehen, den AT90S2313
durch einen größeren Controller (mindestens einen ATmega8) zu ersetzen
und diesen über eine geeignete Adapterplatine an deine Schaltung
anpassen. Sonst stößt du bei jeder kleinen Erweiterung des Programms
wieder auf Probleme.

von Fly (Gast)


Lesenswert?

@ yalu

Erstmal ganz fetten Dank für die ausführliche Analyse meines Programms!
Das hilft mir echt weiter.

Den Ansatz die Eingänge direkt auszuzählen anstatt erst einmal nur 
stumpf einzulesen überrascht mich. Ich dachte es gibt nichts schnelleres 
als Port einlesen und abspeichern. Trotzdem finde ich es gut, besonders 
wenn man dadurch das speicherintensive Arry stepZustand los wird.

Wo wir auch gleich beim nächsten Thema sind. Ich hatte die Idee die 
Zuordnung in die 360°/8 in einem Arry zu organisieren. So wie ich aber 
eben gelernt habe ist das vielleicht garnicht Speicher sparsamer wegen 
dem Arry oder sehe ich das falsch?

Jetzt muss ich mir wohl ertsmal überlegen was einfacher ist, das 
Programm umschrieben oder einen größeren Controller einzusetzen.

von Bender (Gast)


Lesenswert?

>Vielleicht schafft es ja Microchip endlich den ATtiny2313 als Attiny8313
>mit 8kB zu entwickeln.

ATtiny  von Microchip? ;)

von Hannes J. (Firma: _⌨_) (pnuebergang)


Lesenswert?

Fly wrote:
> Bisher hat es noch keinen interessiert aber ich sag es dir gerne:

Lüg' nicht, das war meine erste Frage.

von Fly (Gast)


Lesenswert?

@ pnuebergang

>µC-Typ AVR? Mit -lm gelinkt?

Ja jetzt sehe ich es auch, sorry habe ich übersehehen. Und jetzt komm 
mal bitte von deinem Wut Trip runter, das hilft hier keinen weiter.

Was aber weiter hilft ist, wenn du mir sagst was mit -lm gemeint ist und 
wie ich das herausfinden kann.

von Peter D. (peda)


Lesenswert?

yalu wrote:
> Peter Dannegger schrieb:
>
>> Bezüglich AVR-GCC ist für float ein 2kB-Chip völlig untauglich, ...
>
> Naja, das ist vielleicht etwas übertrieben. FP-Additonen/Subtraktionen
> brauchen weniger als 400 Bytes. Nimmt man noch Multiplikationen hinzu,
> sind es knapp 600 Bytes, mit der Division knapp 800 Bytes. Mit den vier
> Grundrechenarten kann man schon eine Menge anstellen und hat immer noch
> mehr als den halben Flash frei.

???


Die kleine Testfunktion:
1
float test( float a, float b )
2
{
3
  return (a * 3.21 + 4.73) / b;
4
}
ergibt:
1
GCC 4.3.0
2
   text    data     bss     dec     hex filename
3
   2564       8       0    2572     a0c test.out

Die 4 Grundrechenarten passen also definitiv nicht in den ATtiny2313.


Peter

von yalu (Gast)


Lesenswert?

> Den Ansatz die Eingänge direkt auszuzählen anstatt erst einmal nur
> stumpf einzulesen überrascht mich. Ich dachte es gibt nichts
> schnelleres als Port einlesen und abspeichern.

Nicht, wenn die direkte Messung so einfach ist, wie in diesem Fall.

So oder so ähnlich hätte ich's gemacht:
1
  uint8_t tper, thigh;
2
  // warten auf steigende Flanke und Zeit nehmen
3
  while(!(PIND & (1<<PD0)));
4
  tper = TCNT0;
5
6
  // warten auf fallende Flanke und Zeit nehmen
7
  while(!(PIND & (1<<PD0)));
8
  thigh = TCNT0 - tper;
9
10
  // warten auf steigende Flanke und Zeit nehmen
11
  while(!(PIND & (1<<PD0)));
12
  tper = TCNT0 - tper;

In tper und thigh stehen nun die Periodendauer und die Dauer des
High-Pulses. Du brauchst also ein großes Stück deines Auswertecodes
nicht mehr.

Der Compiler erzeugt daraus folgendes:
1
.L5:
2
  sbis 48-0x20,0
3
  rjmp .L5
4
  in r25,82-0x20
5
.L7:
6
  sbis 48-0x20,0
7
  rjmp .L7
8
  in r22,82-0x20
9
.L9:
10
  sbis 48-0x20,0
11
  rjmp .L9
12
  in r24,82-0x20
13
  sub r22,r25
14
  sub r24,r25

Das sind 22 Bytes, und die CPU benötigt 3 Zyklen pro Schleife. Die
erreichbare Zeitauflösung ist als 3 Taktzyklen.

So sieht der entsprechende Teil in deinem Programm aus (ich habe die
Abfrage auf maxAbt weggelassen, weil das in meinem obigen Code auch
fehlt):
1
  while(!(GIFR & (1<<INTF0))) {
2
    stepZustand[messungen][wdh] = PIND;
3
    messungen ++;
4
  }  
5
  stepZustand[maxAbt][wdh] = messungen;

Er sieht zwar auf den ersten Blick kürzer aus, aber der Compiler erzeugt
daraus folgendes Code-Monster:
1
  in __tmp_reg__,90-0x20
2
  sbrc __tmp_reg__,6
3
  rjmp .L9
4
  lds r20,wdh
5
  mov r18,r20
6
  clr r19
7
  lds r25,messungen
8
.L6:
9
  in r24,48-0x20
10
  mov r30,r25                    ; \
11
  clr r31                        ;  |
12
  lsl r30                        ;  |
13
  rol r31                        ;  |
14
  add r30,r18                    ;   > Arrayzugriff
15
  adc r31,r19                    ;  |
16
  subi r30,lo8(-(stepZustand))   ;  |
17
  sbci r31,hi8(-(stepZustand))   ;  |
18
  st Z,r24                       ; /
19
  subi r25,lo8(-(1))
20
  in __tmp_reg__,90-0x20
21
  sbrs __tmp_reg__,6
22
  rjmp .L6
23
  sts messungen,r25
24
  rjmp .L4
25
.L9:
26
  lds r20,wdh
27
.L4:
28
  mov r30,r20
29
  clr r31
30
  subi r30,lo8(-(stepZustand))
31
  sbci r31,hi8(-(stepZustand))
32
  lds r24,messungen
33
  std Z+20,r24

Das sind schon 70 Bytes, also mehr als das Dreifache. Man sieht
deutlich, welcher Aufwand getrieben werden muss, nur um ein einzelnes
Byte in das Array zu schreiben. Eine Abfrageschleife dauert 16 Zyklen,
also fünfmal so lange, was die erreichbare Zeitauflösung verschlechtert.

Da das Ganze im Interrupthandler läuft kommen nochmal über 40 Bytes für
das Retten und Wiederherstellen der Registerinhalte dazu.

Und die Bestimmung der Periodendauer und der Dauer des High-Pulses kommt
erst noch und frisst weitere 400 Bytes.

Somit belegt die Bestimmung der Zeitdaten alleine schon ein Viertel des
Flash-Speichers


Auweia =8-O, jetzt habe ich in meinem Eifer übersehen, dass die
Schrittmotorsignale nach positivem und negativem Stromfluss
unterschieden werden müssen. Dann ist die Messroutine doch nicht ganz so
einfach, wie oben beschrieben. Das ändere ich heute aber nicht mehr ;-)
Ich schätze aber, sie wird dadurch höchsten um 50% länger, da man die
Unterscheidung wahrscheinlich nur bei der Detektion der ersten Flanke
machen muss.

>>µC-Typ AVR? Mit -lm gelinkt?
>
>Ja jetzt sehe ich es auch, sorry habe ich übersehehen. Und jetzt komm
>mal bitte von deinem Wut Trip runter, das hilft hier keinen weiter.

Bei Problemen jeglicher Art ist es für den Antworter eine große Hilfe,
wenn er das Problem bei sich nachstellen kann. Manchmal geht das
schlecht, weil die notwendigen Hardwaremittel fehlen. In diesem Fall
wird aber nur der Quellcode, der richtige Compiler (Versionsnummer) und
die Kommandozeile, mit der dieser aufgerufen wird (alle übergebenen
Optionen einschließlich derjenigen für den Controllertyp, alternativ ein
Makefile), benötigt. Anstelle des kompletten Quellcodes genügt oft auch
eine abgespeckte Version, die das Fehlerverhalten immer noch zeigt.
Je mehr nützliche Informationen du im Eingangspost gibst, umso größer
ist die Chance, schnelle und hilfreiche Antworten zu erhalten. Also beim
nächsten Mal ... :)

> Was aber weiter hilft ist, wenn du mir sagst was mit -lm gemeint ist
> und wie ich das herausfinden kann.

Die Option -lm veranlasst den Linker, die Mathebibliothek einzubinden.
Diese enthält Funktionen wie sin, atan2, pow und log und bei der
AVR-Libc zusätzlich handoptimierte Versionen von Gleitkommaarithmetik-
routinen (+, -, *, / usw.), die schneller und kompakter als die beim GCC
mitgelieferten sind. Wenn man Floats und Doubles verwendet, ist es
deswegen immer ein ratsam, -lm anzugeben, auch dann, wenn man nichts mit
Sinus und Co. am Hut hat.

von yalu (Gast)


Lesenswert?

Peter Dannegger schrieb:

> Die 4 Grundrechenarten passen also definitiv nicht in den ATtiny2313.

Dann hast du das -lm vergessen (s. letzter Abschnitt in meinem vorigen
Post). Damit braucht das Programm einschließlich eines leeren main
weniger als 1KB.

von Peter D. (peda)


Lesenswert?

yalu wrote:
> Peter Dannegger schrieb:
>
>> Die 4 Grundrechenarten passen also definitiv nicht in den ATtiny2313.
>
> Dann hast du das -lm vergessen

Stimmt, jetzt gehts:
1
   text    data     bss     dec     hex filename
2
    930       0       0     930     3a2 test.out


Peter

von floating point (Gast)


Lesenswert?

@yalu

Du fällst hier ja immer wieder durch erfreuliche, sachgerechte Beiträge 
auf. Erfreulich diesmal ist, daß Du das "Monster" 'floating point' auf 
seine reale Größe gestutzt hast.

Somit gibt es eigentlich keinen zwingenden Grund, auf Integer oder 
'fixpoint' Rechnerei auszuweichen, selbst auf ATtiny25/45/85 nicht. Und 
- wie angedeutet - bekommt man kein Geld vom Hersteller zurück, wenn man 
1-2kB Flash-Speicher ungenutzt läßt.

Jetzt höre ich gleich wieder das Argument der unzureichenden 
Geschwindigkeit, die aber meist an anderen Stellen im Programm zunichte 
gemacht wird.

von Fly (Gast)


Lesenswert?

@ yalu

>Auweia =8-O, jetzt habe ich in meinem Eifer übersehen, dass die
>Schrittmotorsignale nach positivem und negativem Stromfluss
>unterschieden werden müssen. Dann ist die Messroutine doch nicht ganz so
>einfach, wie oben beschrieben. Das ändere ich heute aber nicht mehr ;-)
>Ich schätze aber, sie wird dadurch höchsten um 50% länger, da man die
>Unterscheidung wahrscheinlich nur bei der Detektion der ersten Flanke
>machen muss.

Lass mal, das Prinzip habe ich verstanden. Ich muss es halt für die 4 
Eingänge anpassen. Du hast schon genug getan und dafür ein fettes 
DANKESCHÖN!

von Manni (Gast)


Angehängte Dateien:

Lesenswert?

Hier ist ein atan2 algorithmus, basierend auf der bereits 50 Jahre alten 
atan2 approximation von „Hastings“. Der |Fehler| ist < 0.005 radians und 
sei 3-5x schneller (sagt man, hab's aber nicht ausprobiert) als der gcc 
atan2 algorithmus.

Mit AVR Studio und der unteren main routine belegt er 1124 bytes auf'm 
ATmega32 (mit -Os).

#include <stdio.h>
#include <avr/interrupt.h>
#include <math.h>

float arctan2 (float y, float x);

int main (void)
  {
  float c;

  c = arctan2 (0.65f, 1.34f);

  return 0;
  }

Gruß Manni

von yalu (Gast)


Lesenswert?

@floating point:

Danke, freut mich :)

@Manni:

Das von dir beschriebene Verfahren ist für die wenigen
Rechenoperationen, die es benötigt, erstaunlich genau und deswegen eine
gute Alternative zum atan2 der AVR-Libc, wenn man nicht die volle
Genauigkeit braucht und an anderer Stelle sowieso schon mit Floats
rechnet. Die AVR-Libc berechnet den atan[2] über eine Potenzreihe mit 9
Summanden, was schon deutlich mehr Zeit braucht, die ich allerdings
nicht nachgemessen habe.

Die Genauigkeit ist übrigens nicht ±0,005, sondern etwa ±0,01, was aber
dem Nutzen des Verfahrens keinen Abbruch tut. Es gibt aber auch ein
Verfahren mit ±0,005, das ebenfalls mit wenigen Rechenoperationen
auskommt. Hier sind beide beschrieben:

  http://lists.apple.com/archives/PerfOptimization-dev/2005/Jan/msg00051.html

Welches letztendlich schneller ist, müsste man ausprobieren. Das zweite
(genauere) braucht zwar etwas weniger Additionen und Multiplikationen,
dafür aber eine zusätzliche Division. Der maximale Fehler ist
tatsächlich ±0,01 bzw. ±0,005, das habe ich ausprobiert. Das zweite
Verfahren hat noch den kleinen Schönheitsfehler, dass es bei |x|=|y|
unstetig ist.

von Manni (Gast)


Lesenswert?

Ja du hast recht: genau die zweite Funktion meinte ich auch, denn die 
Graphs beziehen sich auf die verwendete Funktion im zweiten Verfahren 
mit:

atan = z/(1.0f + 0.28f*z*z);

Habe die Info auch von dieser Seite, nur wußte ich den Link nicht mehr, 
da ich mir den Text vor einiger Zeit einfach in ein Dok file kopiert 
hatte.

Im AVR ist dieser Code nur unwesentlich größer: 1220 bytes.

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.