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?
- 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
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.
@ 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
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.
>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
Ü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.
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.
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
@ Bernd Weeber (smiley46)
>Konstruktive Kritik bitte!
Hat er doch gemacht. Es wurde nach dem ARCUStangens gefragt
(Umkehrfunktion des Tangens), nicht dem COtangens!
MFG
Falk
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
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.
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
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?
@ 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
>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[][];
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 :)
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.
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.
>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.
> 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.
@ 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.
@ 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.
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
floattest(floata,floatb)
2
{
3
return(a*3.21+4.73)/b;
4
}
ergibt:
1
GCC4.3.0
2
textdatabssdechexfilename
3
2564802572a0ctest.out
Die 4 Grundrechenarten passen also definitiv nicht in den ATtiny2313.
Peter
> 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_ttper,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.
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.
yalu wrote:
> Peter Dannegger schrieb:>>> Die 4 Grundrechenarten passen also definitiv nicht in den ATtiny2313.>> Dann hast du das -lm vergessen
Stimmt, jetzt gehts:
@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.
@ 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!
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
@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.
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.