Forum: Mikrocontroller und Digitale Elektronik Cordic-Algorithmus in AVR-Assembler


von Ka Suh (Gast)


Lesenswert?

Ich benötige die Sinus- und Cosinusfunktion in AVR-Assembler und dachte 
an den Cordic-Algorithmus, konnte aber keinen Code dazu finden. Ich habe 
jede Menge Implementierungen gefunden entweder für andere 
Prozessor-Familien oder in anderen Programmier-Sprachen, aber nichts in 
AVR-Assembler.

von Olaf (Gast)


Lesenswert?

Ist es vollkommen aus der Mode gekommen ein Programm das man
benoetigt auch selber zu schreiben?

Olaf

von Mark B. (markbrandis)


Lesenswert?

Ist es vollkommen in Mode das Rad neu zu erfinden?
Vielleicht tut's aber auch eine lookup table.

von Olaf (Gast)


Lesenswert?

> Ist es vollkommen in Mode das Rad neu zu erfinden?

Wenn Millionen Raeder am Wegesrand liegen dann nicht,
aber leider liegen da nur kaputte Radkappen aus Plastik
von Aldi im Alufelgenlook....

> Vielleicht tut's aber auch eine lookup table.

Stimmt. Manchmal kann man so eine Radkappe doch verwenden. :-D

Olaf

von Ka Suh (Gast)


Lesenswert?

Eine Lookup-Table ist für meine Anwendung Speicherverschwendung. Wenn 
ich 10tel-Grad eingeben möchte brauche ich 900 Einträge. Bei 
ausreichender Genauigkeit sind das 1800 Byte. Mein Programm soll aber 
noch mehr machen, also bin ich mit Speicher geizig. Vielleicht möchte 
ich später auch auf 100tel Grad gehen, dann sieht es noch schlimmer aus.

Rechenzeit spielt in meinem Fall übrigens keine so große Rolle.

(Mark hat richtig erkannt: Ich möchte das Rad nicht neu erfinden.
Olaf's Aussage steht nicht im Widerspruch dazu. Wenn für mich über 
diesen Thread keine Implementierung auffindbar ist, mache ich es 
natürlich selbst.
Nicht ganz verstehe ich seine polemische Äußerung. Warum gibt es diese 
Forum überhaupt, wenn man nicht darüber Wissen austauschen möchte.
Aber vielleicht habe ich Ihn ja falsch verstanden.)

von Ka Suh (Gast)


Angehängte Dateien:

Lesenswert?

Nachdem sich hier niemand mit einer Implementierung gemeldet hat, habe 
ich mich selbst daran versucht. Hier das erste Resultat für eine 
Sinus/Cosinusberechnung nach Cordic.

Eingegeben wird der Winkel in 10tel Grad als 16-bit vorzeichenbehaftete 
Ganzzahl im Bereich -91 bis +91 Grad, also -910 bis +910.
Als Ergebnis erhält man den Sinus und Cosinus als 16-bit 
vorzeichenbehaftete Ganzzahl.

Ich hoffe jetzt auf reges Feedback zur Verbesserung des Codes.

Hier zwei Ansatzpunkte:

Gegenüber einer 16bit-Tabelle gibt es kleine Aproximationsfehler.

Die meiste Zeit frisst der Barrel-Shifter, der beim AVR leider nicht in 
Hardware existiert und als Software-Schleife realisiert wurde. Etwas 
optimiert habe ich dadurch, dass ich bei Shifts größer 8 erst einmal 
ganze Register verschiebe.

von Christoph db1uq K. (christoph_kessler)


Angehängte Dateien:

Lesenswert?

Ich habe sowas auch mal angefangen, bin aber noch nicht weit gekommen. 
Der Barrel-shifter braucht den meisten Platz, das stimmt.

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

Was noch fehlt, ist unter anderem die Umrechnung auf den 180 Grad 
Konvergenzbereich. Zuerst muss ein DDS-Phasenakkumulator eine 
"Dreieckschwingung", also eine auf- und absteigende Treppe erzeugen, und 
am Ende muß man die Vorzeichen für Sinus und Cosinus passend dazufügen.

von Ich (Gast)


Lesenswert?

Hallo.
Vielleicht hilft dir das hier weiter

http://www.mikrocontroller.net/articles/AVR-CORDIC

von Ka Suh (Gast)


Lesenswert?

Hallo Christoph,

danke für Deinen Code-Beitrag. Ist vom Prinzip ähnlich meiner Lösung.

Einen Phasenakkumulator brauche ich nicht, da ich keine Signale erzeugen 
will, sondern nur den Sinus und Cosinus berechnen muss. Deshalb auch die 
andere Skalierung, damit man den Winkel dezimal eingeben kann. Dein 
Programm ist aber ein gutes Beispiel für den Phasenakkumulator. Hebe ich 
mir auf, wenn ich mal so etwas brauche.

Der Barrel-Shifter braucht nicht nur viel Platz, sondern auch Zeit.
Du könntest Deinen Code optimieren, wenn Du den Barrel-Shifter von mir 
verwenden würdest. Damit würde auch der Table-Lookup für den arctan 
einfacher. Größe und Laufzeit würden erheblich kürzer. - Ich sehe 
allerdings, dass Du die iterationen 8 bis 15 auskommentiert hast, dann 
relativiert sich das Ganze natürlich. Ich benötigte eine möglichst hohe 
Genauigkeit und brauche die 15 Iterationen, erwäge sogar auf 
24bit-Arithmetik zu erweitern.

von Christoph db1uq K. (christoph_kessler)


Angehängte Dateien:

Lesenswert?

Ich wollte die Unterprogrammaufrufe vermeiden, das kostet jedesmal 7 
oder 8 Takte, daher die Sprungtabelle. Der Artikel im Wiki war damals 
auch von mir begonnen worden.
Ob der Phasenakkumulator so funktioniert weiß ich nicht. Eine 
ausführliche Beschreibung zum Cordic und Phasenakkumulator stammt aus 
einer leider gelöschten Seite:
http://cnmat.cnmat.berkeley.edu/~norbert/cordic/node4.html
die ich noch auf archive.org finden konnte, hier als PDF.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ka Suh schrieb:
> Nachdem sich hier niemand mit einer Implementierung gemeldet hat, habe
> ich mich selbst daran versucht. Hier das erste Resultat für eine
> Sinus/Cosinusberechnung nach Cordic.
>
> Ich hoffe jetzt auf reges Feedback zur Verbesserung des Codes.
>
> Hier zwei Ansatzpunkte:
>
> Gegenüber einer 16bit-Tabelle gibt es kleine Aproximationsfehler.
>
> Die meiste Zeit frisst der Barrel-Shifter, der beim AVR leider nicht in
> Hardware existiert und als Software-Schleife realisiert wurde. Etwas
> optimiert habe ich dadurch, dass ich bei Shifts größer 8 erst einmal
> ganze Register verschiebe.

Interessant wäre, das ganze so zu schreiben, daß es auf das avr-gcc ABI 
passt, d.h. daß man die Funktionen komfortabel von avr-gcc aus aufrufen 
und in C/C++ Programmen verwenden könnte.

Dann enthält das Programm einen Fehler, von dem ich aber nicht weiß, ob 
er sich auf die Genauigkeit auswirkt. Es ist nämlich
Beispiel: wenn du 1 um 1 nach rechts schiebst, dann wird das 0. Aber 
wenn du -1 nach rechts schiebst, dann bleibt das -1. Egal, wie oft du 
eine negative Zahl nach rechts schiebst, sie wird nie 0.

Es müsste also vor dem (Barrel-)Shift der Betrag genpmmen werden, und 
nach dem Shift für negative Zahlen das Vorzeichen wie der angepasst 
werden.

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Der Barrel-Shift lässt sich vielleicht durch explizite 
Fallunterscheidung beschleunigen, zumindest im Mittel:
1
__tmp_reg__ = 0
2
__zero_reg__ = 1
3
  .section  .progmem.data,"a",@progbits
4
  .size  l.0, 16
5
labels.0:
6
  .word  pm(.L.sh0)
7
  .word  pm(.L.sh1)
8
  .word  pm(.L.sh2)
9
  .word  pm(.L.sh3)
10
  .word  pm(.L.sh4)
11
  .word  pm(.L.sh5)
12
  .word  pm(.L.sh6)
13
  .word  pm(.L.sh7)
14
15
.text
16
17
;; r25:r24 >>= r22 (unsigned)
18
;; kaputt: r26, r27, r30, r31    
19
.global  shift
20
shift:
21
  cpi r22, 8
22
  brlo .less.than.8
23
        mov r24, r25
24
  clr r25
25
  sbrc r22,2
26
  swap r24
27
  sbrc r22,2
28
  andi r24, 0xf
29
  sbrc r22,1
30
  lsr  r24
31
  sbrc r22,1
32
  lsr  r24
33
  sbrc r22,0
34
  lsr  r24
35
      ret
36
    
37
.less.than.8:
38
  mov r30, r22
39
  lsl r30
40
  clr r31
41
  subi r30, lo8(-(labels.0))
42
  sbci r31, hi8(-(labels.0))
43
44
  lpm r26, Z+
45
  lpm r27, Z
46
        movw r30, r26
47
        ijmp
48
.L.sh7:
49
  lsr r25
50
  ror r24
51
.L.sh6:
52
  lsr r25
53
  ror r24
54
.L.sh5:
55
  lsr r25
56
  ror r24
57
.L.sh4:
58
  lsr r25
59
  ror r24
60
.L.sh3:
61
  lsr r25
62
  ror r24
63
.L.sh2:
64
  lsr r25
65
  ror r24
66
.L.sh1:
67
  lsr r25
68
  ror r24
69
.L.sh0:
70
  ret

Johann

von Hagen R. (hagen)


Lesenswert?

auf Megas könnte man mit FMUL auch den Rechtsshift beschleunigen.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ka Suh schrieb:
> Nachdem sich hier niemand mit einer Implementierung gemeldet hat, habe
> ich mich selbst daran versucht. Hier das erste Resultat für eine
> Sinus/Cosinusberechnung nach Cordic.
>
> Eingegeben wird der Winkel in 10tel Grad als 16-bit vorzeichenbehaftete
> Ganzzahl im Bereich -91 bis +91 Grad, also -910 bis +910.
> Als Ergebnis erhält man den Sinus und Cosinus als 16-bit
> vorzeichenbehaftete Ganzzahl.
>
> Ich hoffe jetzt auf reges Feedback zur Verbesserung des Codes.
>
> Hier zwei Ansatzpunkte:
>
> Gegenüber einer 16bit-Tabelle gibt es kleine Aproximationsfehler.
>
> Die meiste Zeit frisst der Barrel-Shifter, der beim AVR leider nicht in
> Hardware existiert und als Software-Schleife realisiert wurde. Etwas
> optimiert habe ich dadurch, dass ich bei Shifts größer 8 erst einmal
> ganze Register verschiebe.

Hast du mal ne Zeitmessung gemacht? Würd mich interessieren, wieviel 
Ticks die Berechnung braucht.

Ich hab den CORDIC jetzt mit Multiplikation anstatt AShift 
implementiert, und der braucht pro Aufruf recht konstant ca. 690 Ticks 
inclusive Aufruf von C aus. Gemessen wirde auf der Hardware.

Die Implementierung ist avr-gcc ABI-konform, und Eingabe und Ausgabe 
sind skalierbar. Die Eingabe muss in -sqrt(3)...sqrt(3) liegen. Das 
entsprich -99°...99° bzw. -1.1*Pi/2...1.1*Pi/2, und die Ausgabe ist auf 
1.15 signed Q-Format. Ist wie gesagt über zwei Makros skalierbar: eins 
für den Bereich der Eingabe und einen für die Ausgabe.

Welche Genauigkeiten sind erreichbar? Wenn man von 16 
Schleifendurchläufen ausgeht und in jedem Durchlauf 1 Bit verliert, 
verliert man doch insgesamt 4 Bit an Genauigkeit wegen Rundungsfehlern, 
oder? Der Fehler scheint bei ca. 1-4 Incrementen zu liegen.

Wenn Interesse an der Implementierung da ist und sie von der 
Geschwindigkeit her nicht ausser Konkurrenz ist, würde ich's aufhübschen 
und hier reinstellen. Codegröße ist 222 Bytes + 34 Bytes atan-Tabelle.

Johann

von Hagen R. (hagen)


Lesenswert?

Einige Verbesserungen:
wenn CoTmp und SiTmp beide 0 sind kann die Iteration abgebrochen werden. 
Der Gain des CORDIC darf dann aber erst am Ende der Iteration 
berücksichtigt werden. Denn dieser ist ja abhängig von der Anzahl der 
Iterationen. Man sollte also bei dieser Beschleunigung eine weitere 
Tabelle mit max. 16 Einträgen haben um den Gain an die Iterationstiefe 
anzupassen.

Mit wenigen Änderungen kann man Wurzel(X^2+Y^2) und ArcTan2(X, Y) 
ebenfalls mit dieser Funktion ausrechnen. Beides in einem Rutsch und 
sehr hilfreich wenn man zb. bei einer nicht kohärenten IQ Demodulation 
den Betrag und die Phase des demodulierten Signales ausrechnen möchte. 
Statt über den Angle zu gehen wird man dann die Y Koordiante abfragen.

Gruß Hagen

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hagen Re schrieb:
> Einige Verbesserungen:
> wenn CoTmp und SiTmp beide 0 sind kann die Iteration abgebrochen werden.

Lohnt das? Dürfte selten der Fall sein das, und im Mittel die Abfrage 
mehr kosten als sie bringt.

Momentan scheint eher die Genauigkeit ein Problem zu sein, und ich bin 
mir nicht sicher, ob ich das fmul-Zeug richtig verstanden hab und immer 
korrekt runde.

Anbei eine Grafik. Die roten Punkte sind vom PC berechnet. Die schwarzen 
kommen vom AVR und sind darübergemalt (rechts einer weniger als vom PC).

Die Schrittweite ist 0.25°, also 0.00436, im Bereich -91°...91°. Sieht 
aus als wär damit die Auflösung am Ende der Fahnenstange, oder ich hab 
was Grunsätzliches verpeilt...

Johann

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Hier noch der Kreis selbst, mit einer Schrittweite von 0.01°, wieder 
kommen die Daten direkt vom AVR. Der Abstand zweier Pixel auf dem 
Kreisrand entspricht bei der Bildauflösung ca. 0.036°.

Sieht sauber aus und ohne Ausreisser. Der Genauigkeitsverlust ist nicht 
in der r-Komponente sondern in phi.

Die Ausgaberoutine auf AVR bringt natürlich auch nen Rundungsfehler, 
aber der dürfte deutlich unter dem des Cordic liegen, nämlich bei einem 
Inkrement bzw 1/32768 ~ 0.00003. Fehler darin würde man auch in r sehen, 
nicht nur in phi.

Johann

von Hagen R. (hagen)


Lesenswert?

>Lohnt das? Dürfte selten der Fall sein das, und im Mittel die Abfrage
>mehr kosten als sie bringt.

Kannst einfach ausrechnen. Die Iterationsanzahl hängt von der Bitgröße 
der beiden Operanden X,Y ab. Bei zb. X,Y=8 = 2^3 also 3 Bits und somit 
minimal 3 Iterationen nötig. Ausgehend von 16Bit Wertebereich sind es 
also

Iterationen, Wahrscheinlichkeit
 2=0.003%
 3=0.006%
 4=0.012%
 5=0.024%
 6=0.048%
 7=0.097%
 8=0.195%
 9=0.390%
10=0.781%
11=1.562%
12=3.125%
13=6.25%
14=12.5%
15=25%
16=50%

Das ist aber nur die Bitwahrscheinlichkeit, also wenn man annimmt das 
gleichverteilt alle möglichen bis zu 16Bit Werte vorkommen.

Ich würde erstmal versuchen den Algo. wenn möglich mit immer 
gleichbleibender Taktanzahl zu programieren, da man so das Gesamtsytem 
besser vorhesagbar macht.

Erst wenn es auf den letzten Takt ankäme würde ich in diese Richtung 
weiter entwickeln. Meistens wird man dann eher eine Funktion bauen die 
mit konstanten Winkeln etc.pp. arbeitet.

Interssanter wäre da schon die Möglichkeit die Rechtsshifts durch 
Linksshifts austauschen zu können. Linksshift sind bei einfacher MUL 
noch besser zu optimieren als Rechtsshifts. Grundsätzlich geht das sogar 
auch. Man kann zahlentheoretsich den Rechtsshift per Multipikation als 
Linksshift bauen. Angenommen man wollte 3 Bits Rechtsshiften dann kann 
man auch X << (8-3) / 2^8 rechnen. Dh. man multipliziert mit 2^(8-3) und 
verwirft das unterste Byte der Multiplikation. Für kleine Shifts ist die 
Multiplikation langsammer aber alles an Shift mit größer 1 oder 2 wird 
durch die Multipikation in jedem Fall schneller. Für diesen Trick 
müsstest du die Zählervariable einfach von 15 nach 0 runterzählen 
lassen, würde ich mal so aus dem Stegreif sagen.

Gruß Hagen

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Hagen Re schrieb:

> Interssanter wäre da schon die Möglichkeit die Rechtsshifts durch
> Linksshifts austauschen zu können. Linksshift sind bei einfacher MUL
> noch besser zu optimieren als Rechtsshifts. Grundsätzlich geht das sogar
> auch. Man kann zahlentheoretsich den Rechtsshift per Multipikation als
> Linksshift bauen. Angenommen man wollte 3 Bits Rechtsshiften dann kann
> man auch X << (8-3) / 2^8 rechnen. Dh. man multipliziert mit 2^(8-3) und
> verwirft das unterste Byte der Multiplikation. Für kleine Shifts ist die
> Multiplikation langsammer aber alles an Shift mit größer 1 oder 2 wird
> durch die Multipikation in jedem Fall schneller. Für diesen Trick
> müsstest du die Zählervariable einfach von 15 nach 0 runterzählen
> lassen, würde ich mal so aus dem Stegreif sagen.

Ja, so mach ich das. Mit der Fallunterscheidung ob es mehr oder weniger 
als 8 Bits zu schieben sind.

Bin jetzt bei 550 Ticks und 268 Bytes. Immerhin um 20% schneller als 
meine erste Version :-)) Geht also voran...

Johann

von Ka Suh (Gast)


Lesenswert?

Da ich nur in Assembler programmiere, habe ich bisher keine Ahnung von 
avr-gcc ABI-Konformität, bin aber daran interessiert in Zukunft nach 
Möglichkeit konformen Assembler-Code zu erstellen. Kann mir jemand 
helfen und einen Link zu einer einfachen Anleitung oder Spezifikation 
nennen oder kurz die Konformitätsregeln beschreiben? (Ich benutze 
AVR-Studio)

Gruß

Ka

von Ka Suh (Gast)


Lesenswert?

Hallo Johann

Zu Deinen Fragen vom 28.06.2009 12:06:

Mein Code vom 06.2009 benötigt  konstant 886 Takte und 70 Worte. Da ich 
mit 16bit signed arbeite genügen genau 15 Iterationen. Enthalten ist die 
Skalierung des Eingangswertes. Ich orientiere mich bei den Angaben an 
den Applicationnotes von Atmel, d.h. ohne Aufruf und return.

Meine Optimierungsschwerpunkt lag eher auf minimaler Wortzahl und 
konstante, vom eingegeben Winkel unabhängige Taktzahl. Trotzdem habe ich 
eine einfache Takt-Optimierung am Barrelshifter vorgenommen, da sie bei 
geringer Größenerhöhung doch einiges an Geschwindigkeit brachte. Bei 
Shifts >= 8 wird erst über mov ein ganzes Byte verschoben und der dann 
die restlichen Shifts ausgeführt. Lässt man diese nämlich weg, kommt man 
auf 1261 Takte bei 63 Worten.

Zieht man beim optimierten Code die Skalierung des Eingabewertes ab und 
verzichtet auf konstante Abarbeitungszeit  kommt man auf 60 Worte und 
schwankende Taktzahl (beobachtet: 854-873 Takte)

Du macht aber 16 Iterationen, stimmt dass? Dann bist Du mit Deinen 
derzeit 550 Ticks konkurrenzlos besser. bei mir wären es 976 Ticks. Der 
Größenverlust von 268 Bytes
bei Dir zu 120 Bytes bei mir ist dafür durchaus tragbar.

Kurzum, Johann, ich wäre an Deinem Code interessiert, kannst Du Ihn hier 
veröffentlichen, auch wenn noch vorläufig.


Gruß

Ka

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ka Suh schrieb:
> Da ich nur in Assembler programmiere, habe ich bisher keine Ahnung von
> avr-gcc ABI-Konformität, bin aber daran interessiert in Zukunft nach
> Möglichkeit konformen Assembler-Code zu erstellen. Kann mir jemand
> helfen und einen Link zu einer einfachen Anleitung oder Spezifikation
> nennen oder kurz die Konformitätsregeln beschreiben?

http://www.rn-wissen.de/index.php/Avr-gcc/Interna#Registerverwendung
http://avr-libc.nongnu.org/user-manual/FAQ.html#faq_reg_usage

Johann

von Ka Suh (Gast)


Lesenswert?

Hallo Johann,

danke für die Info. Ergänzende Frage: wie wird in meinem Fall der 
return-Wert gebildet, wenn Sinus und Cosinus zusammen zurückgegeben 
werden sollen, damit es mit gcc kompatibel ist? Aus den Beschreibungen 
ist mir nicht klar, wie Arrays oder Strukturen zurückgegeben werden? Ich 
hätte jetzt einfach den Sinus in r25:r24 gepackt und den cosinus in 
r22:r23. Geht das?

Ka

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ka Suh schrieb:
> Hallo Johann,
>
> danke für die Info. Ergänzende Frage: wie wird in meinem Fall der
> return-Wert gebildet, wenn Sinus und Cosinus zusammen zurückgegeben
> werden sollen, damit es mit gcc kompatibel ist? Aus den Beschreibungen
> ist mir nicht klar, wie Arrays oder Strukturen zurückgegeben werden? Ich
> hätte jetzt einfach den Sinus in r25:r24 gepackt und den cosinus in
> r22:r23. Geht das?

Ja, genau dort gebe ich sie auch zurück. Passend zum Assembler-Modul 
gibt's dann nen Header mit den Prototypen für die Funktionen, damit 
(avr-)gcc weiß, wie die Funktionen aufzurufen sind. Als Returnwert
1
#include <stdint.h>
2
3
typedef struct
4
{
5
    uint16_t cos;
6
    uint16_t sin;
7
} __attribute__((packed)) cossin_t;

Dann wird cos in r23:r22 und sin in r25:24 übergeben.

Ka Suh schrieb:
> Du macht aber 16 Iterationen, stimmt dass? Dann bist Du mit Deinen
> derzeit 550 Ticks konkurrenzlos besser. bei mir wären es 976 Ticks. Der
> Größenverlust von 268 Bytes
> bei Dir zu 120 Bytes bei mir ist dafür durchaus tragbar.

Mir ging es eher um Geschwindigkeit; nicht bis aufs Messer, aber in 500 
Ticks sind schon Weltreiche aufgestiegen und wieder zerfallen...

> Kurzum, Johann, ich wäre an Deinem Code interessiert, kannst Du Ihn hier
> veröffentlichen, auch wenn noch vorläufig.

Vorher muss ich noch die Arithmetik testen. Zum Shift ist der Abstand 
der Multplikation nie größer als 1 LSB, was plausibel ist. Muss aber 
noch gegen die Standard-Multiplikation testen, weil natürlich auch die 
Präzision so hoch wie unter den Umständen möglich sein soll. 
Möglicherweise vertögle ich bei den fmul* irgendwo ein LSB, bin aber 
noch nicht sicher. Bis dahen gibt's also kein Code.

Johann

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

So, hier ist der Code.

-- Code:  278 Bytes
-- Ticks: weniger als 560
-- Eingabe in (-sqrt3, sqrt3), das entspricht -99°...99°
-- Ausgabe als signed 1.15 Q-Format

Johann

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

Der Berechnungsfehler ist (Standardabweichung)

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

...und für den Cosinus

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

...und den Radius

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

...und für den Winkel

Die Fehler sind wie zu erwarten gut Normalverteilt in einem Intervall 
von ca 16 Inkrementen. Das ist auch zu erwarten bei 16 
Iterationschritten und Fehlerfortpflanzung.

Aus der Reihe fällt der Sinus; der Fehler schein aus der Summe zweier 
Gauß-Glocken zu bestehen. Ich weiß aber nicht, wie die 2 Komponenten 
zustandekommen oder wie man die Kurve schmäler (d.h. die 
Standardabweichung kleiner) bekommt.

Vielleicht findet ja jemand nen Rundungsfehler im Algorithmus, wobei 
erstaunt, daß das Phänomen nur den Sinus betrifft.

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Zu Beginn kann man noch ausnutzen, daß sin=0 ist:
1
cordic:
2
    ;; end prolog
3
    movw    _phi, R24
4
    clr     _zero
5
    ;; Z = cordic_atan_tab[]
6
    ldi     r30, lo8 (cordic_atan_tab)
7
    ldi     r31, hi8 (cordic_atan_tab)
8
    ;;  cos = 1 / CORDIC_GAIN
9
    lpm     _cos,   z+
10
    lpm     _cos+1, z+
11
    
12
    ldi     _pow2, 0x80
13
    
14
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
15
    ;; Shift = 0
16
    movw        _sin,   _cos
17
18
    tst         _phi+1
19
    ;; phi >= 0: sin = 0+cos
20
    brpl        .L_phi_pos_1_7
21
    
22
    ;; phi < 0 : sin = 0-cos
23
    com         _sin+1
24
    neg         _sin
25
    sbci        _sin+1, -1
26
    rjmp        .L_phi_neg_1_7

von Ka Suh (Gast)


Lesenswert?

Hallo Johann,

kleine Nebenfrage. Ich benutze AVRstudio und programmiere nur in 
Assembler. Die Syntax, die Du verwendest, insbesondere Sprungadressen, 
Makros, Precompiler-Direktive etc. versteht mein Compiler nicht. Ich 
kann zwar den Code anpassen, aber ist das der richtige Weg?

Gruß Ka

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ka Suh schrieb:
> Hallo Johann,
>
> kleine Nebenfrage. Ich benutze AVRstudio und programmiere nur in
> Assembler. Die Syntax, die Du verwendest, insbesondere Sprungadressen,
> Makros, Precompiler-Direktive etc. versteht mein Compiler nicht. Ich
> kann zwar den Code anpassen, aber ist das der richtige Weg?

Kann man machen. Fast einfacher ist es da, den Code aus nem Disassemble 
raus neu zu erzeugen.

Du kannst einfach die Kommandozeile wie in cordic-demo.c ausführen und 
ein Disassembler erstellen. AVR-Tools gibt's frei und kostenlos.

Ich verwende den GNU-Assembler. Zum einen ist der "mächtiger" als der 
Atmel-Assembler, zum zweiten arbeitet der als AVR-Tool mit Linker und 
Compiler zusammen und erzeugt richtige Objekte in elf32-avr gegen die 
man linken kann. Damit kann man gegen andere Objekte in elf32-avr 
linken, egel ob sie aus einer Assembler-Quelle kommen, aus C, aus C++, 
AVR-Ada, ... und Biblioteken erstellen, ...

Johann

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Ich hab Sinus und Cosinus mal per linearer Interpolation implementiert.

Der Code ist mit 300 Bytes zwar größer als Cordic, dafür ist er mit 60 
Ticks für Sinus bzw. 74 Ticks für Cosinus (incl. CALL+RET) deutlich 
schneller als CORDIC.

Ebenfalls besser ist die Genauigkeit sowie eigenschaften wie Monotonie 
(CORDIC-Ausgaben sind idR "verrauscht" und nicht monoton).
   http://www.mikrocontroller.net/articles/AVR_Arithmetik/Sinus_und_Cosinus_%28Lineare_Interpolation%29

Johann

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.