mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Sinus in ASM Realisieren


Autor: Sinusman (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo erstmal,

also ich hab einen etwas lustigen 8051 ähnlichen prozessor der sehr 
langsam ist und..

Addieren
Subrahieren
Shiften
und if abfragen kann.

und ich hab 2 Messwerte die mir X und Y werte darstellen, ich hab noch 
ca. 40 Zeilen an Code frei ( rest komplett verbraucht ), und würde gerne 
Radius ( auflöung von 3~4 Bit reicht vollkommen ) und Entfernung zum 
Mittelpunkt errechnen, Genauigkeit ist nicht ganz so tragisch.

Lookup Table oder gibts einen Mathe Guru der nen gute Trick kennt?

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Joerg Wolfram (joergwolfram)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Radius und Entfernung zum Mittelpunkt ist für mich dasselbe...
Sinus braucht man dafür nicht, dafür aber Pythagoras.

-  X soweit nach rechts schieben, bis nur 4 Bit übrigbleiben
   (bei 8-bit Werten 4x bei 16-Bit Werten da High-Byte 4x)

-  in zwei Register laden, Summe=0
a) solange zweites Register >0, erstes Register zur Summe addieren
   wenn zweites Register >0 dann springe nach a)

-  im Summenregister steht jetzt X^2

-  mit Y und einem anderen Summenregister genauso verfahren

-  beide Quadrate addieren

-  resultat=0, diff=1
   wenn bei der Summe ein Übertrag aufgetreten ist, resultat=16, diff=33

b) wenn diff>low(summe) dann ende
-  resultat=resultat+1, summe(low)=summe(low)-diff, diff=diff+2
-  goto b

Ob das jetzt in 40 "Zeilen" noch reingeht, weiss ich leider nicht.

Gruß Jörg

Autor: Sinusman (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja dürfte sich ausgehen, beim Cordic Algo muss ich soweit ichs bis jetzt 
verstanden hab einen atan Lookup Table bauen, denk nicht das ich soviel 
platz hab.

kannst du das in einem Pseudo C erklären? ( hab 8 Bit eingang und 8 Bit 
ausgang )

Autor: Sinusman (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
meinte nicht Radius sondern Winkel ...

Brauche Winkel & Radius :)

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
diese ganzen erklärungen beruhen alle auf Floating Points, und da sind 
meistens, die Lookuptable für den atan is auch schon zu gross ...

Autor: Pieter (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
moin moin,

anbei mal was in cordic...

  *   *   *   *  
..ca. 40 Zeilen an Code frei ..

das bedarf einer Erklärung, den Rest kann man googeln.

mfg
Pieter

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
danke für die Info,

nunja ich vermute zurzeit eher das die Skalierung meiner zahlen das 
problem ist, meine Eingangswerte für X und Y sind 0 bis 255 und als 
ausgang hätte ich einen Radius von 0 bis 255 und einen Winkel von 0 bis 
8

der Mittelpunkt meines kreises ist x=127 y=127

ich glaub ich schaff es sogar 50 Zeilen rauszuboxen, problem ist mein 
Ram hat nur 16Bytes, wie genau die atan Lookup Table sein muss hab ich 
auch noch nicht abgecheckt ...

Autor: Jankey (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Also anstatt eines sinus hab ich ein "quasi Rechteck geschaffen, das is 
jetzt eine 255x255er Anzeige

code dazu:

uchar sinus( int z )
{
        int x = 255;
        int y = 0;

        int dx = 0;
        int dy = 0;

        int i = 0;

//        Form1->Memo1->Clear();

        for ( i = 0; i < 8 ; i++ )
        {
                        dx = x >> i;
                        dy = y >> i;

                if( z > 127 )
                {
                        x = x - dy;
                        y = y + dx;
                        z = z - atan(128>>i)*64;
                }
                else
                {
                        x = x + dy;
                        y = y - dx;
                        z = z + atan(128>>i)*64;
                }

//                Form1->Memo1->Lines->Add( 
AnsiString().sprintf("x:%dy:%dz:%d",x,y,z) );

        }

        return y;
}

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jankey wrote:
> ausgang hätte ich einen Radius von 0 bis 255 und einen Winkel von 0 bis
> 8

0 bis 8?
0 bis 7 (inklusive) als Integer?

dazu brauchst du keinen atan, das geht mit Koordinatenvergleich
und einer if-then-else Kaskade.

  (px/py ... Koordinaten des Punktes
   mx/my ... Koordinaten des Mittelpunktes
  dx = px - mx
  dy = py - my

  if( dx > 0 )
    if( dy > 0 )
      if( dx > dy )
        Winkel = 0
      else
        Winkel = 1
    else
      if( dx > -dy )
        Winkel = 7
      else
        Winkel = 6
  else
    if( dy > 0 )
      if( -dx > dy )
        Winkel = 3
      else
        Winkel = 2
    else
      if( -dx > -dy )
        Winkel = 4
      else
        Winkel = 5

Einfach mal einen Kreis aufzeichnen, die 8 Sektoren einzeichnen
und überlegen, welche Abhängigkeiten für die X und Y Koordinaten
in jedem Sektor gelten.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
jein, 0 bis 7 und 0 bis 8 sollte möglich sein, 0 bis 248 für Winkel zb 
wäre wieder perfekt.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eigentlich suche ich sowas:

Angle[0~248] = aTan(y/x);

das aTan mit y/x zu realisieren mit Cordic erstellt sich als etwas 
schwierig, zumals ich nur 8 Bit Rechenbreite zur verfügung habe und alle 
mit 16 Bit und mehr rechnen. Koordinatenvergleich is keine Ausreichend 
genaue Methode.

lg

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
so mit dem Ding schafft man einen Sauberen 8 Bit Sinus


Mittelpunkt ist 127




int i = 0;
for ( i = 0; i < 8 ; i++ )
{
      y -= (x >> i);
      x += (y >> i);
}

return y;

Autor: Jankey (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
so, problem bei der Berechnung ist das er aus irgend einem Grund 2x den 
Winkel durchrechnet, hat eigentlich einer von denen die CORDIC hier 
gepostet haben auch wirklich mal versucht in einem uController 
nachzubauen?

uchar phi( int x , int y )
{
        int z = 128;
        int i = 0;
        int X = x;
        int Y = z;

        for (i = 1; i <= 10; ++i)
        {
                x >>= 1;
                z >>= 1;

                if( Z < 0 )
                {
                    Y += x;
                    Z -= z;
                }
                else
                {
                    Y -= x;
                    Z += z;
                }
        }

        return Z;
}

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nein, hab auch bisher noch keinen Cordic benutzt, aber das ist die 
einfachste genaue Methode.
Gehts jetzt um den Radius oder den Winkel? Es gibt noch eine sehr simple 
Näherung für den Radius: Betrag der längeren Koordinate plus 0,4142 * 
der Betrag der kürzeren. Das stimmt exakt alle 45 Grad. Ich habs mal 
benutzt um niederfrequente AM zu demodulieren.

Autor: Christoph Kessler (db1uq) (christoph_kessler)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"des Betrags" sollte das heißen

ja und die ganzzahlige Näherung:
Ich benutze 106/256 als Approximation von (Sqrt(2)-1) = 0,4142

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also Winkel ist vorrangig, wenn der Radius "kann" egal sein, schöner 
wärs natürlich wenn ich ihn noch reinklopfen könnte, zurzeit überlege 
ich mithilfe eines Integrators eine Sinusähnliche Kennlinie nachzubauen 
...

irgendwie komm ich einfach nicht auf den Winkel von 255*atan(y/x)...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jankey wrote:

> irgendwie komm ich einfach nicht auf den Winkel von 255*atan(y/x)...

Rein aus dem Bauch heraus würde ich mal sagen: da gibt es auch
keine vernünftige Näherung. Der tan, und damit auch die
Umkehrfunktion, ist eine sehr unangenehme Funktion.

Mal anders aufgezäumt: Wozu brauchst du den das Ganze. Vielleicht
gibt es eine Möglichkeit ohne den Winkel auszukommen.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich hab 2 Eingangssignale als X und Y Positions und will die Richtung 
anzeigen in die die 2 Werte Zeigen, problem dabei, das system in das ich 
anzeigen will ist Radial ...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jankey wrote:
> ich hab 2 Eingangssignale als X und Y Positions und will die Richtung
> anzeigen in die die 2 Werte Zeigen,

Muss das eine Zahlenanzeige sein oder reicht ein Pfeil, der
in die richtige Richtung weist?

> X und Y Positions

sind das Positionen in einem kartesischen Koordinatensystem?


(Im Moment spukt mir der Begriff 'ähnliche Dreiecke' durch
den Kopf. Erzähl mal mehr über den Input)

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja Karthesische Eingangskoordinaten von 0 bis 255 auf X und Y, ausgang 
ist dann eine Polare sagen wir mal "anzeige" ...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jankey wrote:
> Ja Karthesische Eingangskoordinaten von 0 bis 255 auf X und Y, ausgang
> ist dann eine Polare sagen wir mal "anzeige" ...



Grrrrrr
Kannst du nicht mal ein bischen konkreter werden.
Was ist so schwer daran, zu sagen:

Ich habe einen Joystick und möchte die Auslenkung dem
Benutzer anzeigen.
* Als Anzeige habe ich ein LCD auf dem Auslenkung und Winkel
   in Form von Zahlen ausgegeben werden soll
* Als Anzeige habe ich ein graphisches LCD, auf dem ein Pfeil
   die aktuelle Joystick Stellung symbolisieren soll

Im ersten Fall kommt man um die Arcus-Winkelfunktionen Funktionen
nicht herum. In letzterem aber schon.

(*) Nur so als Beispiel. Ich weis ja nicht was du machen willst.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
kann ich leider nicht ( patent pending ), wie wäre denn letzteres 
möglich?

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
*x = 1;
        float dx;
        *y = 0;
        int i = 0;

        *x = 0.6073*(*x);


        for( i = 0; i < 8 ; i++ )
        {
                dx = (*x);
                if( z>=0 )
                {
                        (*x) -= (*y)  * (1.0/pow(2,i)) ;
                        (*y) += dx * (1.0/pow(2,i)) ;
                        z -= atan((double)(1/pow(2,i)))*(180/M_PI);
                }
                else
                {
                        (*x) += (*y)  * (1.0/pow(2,i)) ;
                        (*y) -= dx * (1.0/pow(2,i)) ;
                        z += atan((double)(1/pow(2,i)))*(180/M_PI);
                }
        }


So hab jetzt diese Funktion hier mehr oder weniger aus dem Web Adaptiert 
und etwas modifiziert. als Input hat sie denk Winkel, als output X & Y, 
die funktion müsste man eigentlich nur umdrehen ... so irgendwie ...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jankey wrote:
> kann ich leider nicht ( patent pending ),

Ja genau. Weil hier hundertausend Leute rumhängen, die nur
darauf warten dass einer die Megaidee postet um sie zu
kopieren. Dafür sollen alle anderen im Nebel rumstochern
welches Problem du eigentlich hast.

> wie wäre denn letzteres
> möglich?

sagte ich doch schon: ähnliche Dreiecke und damit Strahlensatz

sei  Px/Py die Eingangsposition, Wertebereich zb. -1000 ... +1000
in beiden Achsen.

Sei weiters, Qx/Qy die gesuchte Endposition auf einem Kreis am
Display, wobei der Mittelpunkt wieder als 0 angenommen wird.

Dann gilt:

  L = sqrt( Px * Py + Py * Py )

  Qx = Px / L
  Qy = Py / L

und der Vektor QX/Qy ist sogar in der Länge proportional
dem Vektor Px/Py

Dein Display habe nun dem Mittelpunkt bei 127/127 und der
Kreis soll einen Durchmesser von 127 Pixel haben, somit
wird der Einheitsvektor Qx/Qy auf eine Länge von 127 gebracht
und um 127 Einheiten verschoben.

  Qx = ( Px / L ) * 127 + 127
  Qy = ( Py / L ) * 127 + 127

Und schliesslich soll der Pfeil auch noch in der Länge
proportional der Auslenkung in Px/Py sein. Bei einer
Auslenkung in der jeweiligen Achse von 1000 soll der Pfeil
bis an den Kreis heranreichen:

  Qx = ( Px * L  1000  L ) * 127 + 127
  Qy = ( Py * L  1000  L ) * 127 + 127

ein bischen kürzen

  Qx = ( Px / 1000 ) * 127 + 127
  Qy = ( Py / 1000 ) * 127 + 127

und siehe da, sogar die Wurzelberechnung braucht keiner mehr.

Jetzt mag dein Wertebereich für Px/Py nicht -1000 ... 1000 sein
sondern meinetwegen 0 ... 512 mit 256 als Mittenposition und
255 als Auslenkung zu beiden Seiten

  Qx = ( ( Px - 256 ) / 255 ) * 127 + 127
  Qy = ( ( Py - 256 ) / 255 ) * 127 + 127


Da das aber so trivial ist, fürchte ich, das wird dir als
jemand der an einem Patent arbeitet, nicht wirklich weiterhelfen.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
eigentlich eine gute Idee, nur das man /255*127 auch als /2 schreiben 
kann ..und wenn du dann auflößt kommt

Qx = (Px>>1 - 127)+127
Qy = (Py>>1 - 127)+127

Qx = Px>>1
Qy = Py>>1

daran erkennt man aber einen echten denker :) er beschäftigt sich mit 
leidenschaft.

Das mit Patenten ist so: Wenn man irgendwo vor Veröffentlichung des 
Patents etwas darüber öffentlich gestellt hat is es sinnlos & nicht 
schützbar.

Autor: 6635 (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es ist klar, dass das resultat von atan() in radianten sind...

Autor: Matthias Lipinsky (lippy)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Als Potenzreihe?

             x     1y     1y^3    2y^5
cot (y/x) = --- - ---- - ----- - ----- - ...
             y     3x    45x^3   945x^5

Vielleicht reicht es ja, bis ^3 zu rechnen, müsste man testen...?

==>


256cot(y/x) ~= 256(y/x)^-1  -  85(y/x)  -  6(y/x)^3

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Taylor Reihe is glaub ich zu gross ...


void cordic2( int *x , int *y , int *z )
{
        int dx;
        int i = 0;
        int ATAN[8]= { 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1 };


        for( i = 0; i < 8 ; i++ )
        {
                dx = (*x);

//                if( *z>=0 )
                if( -(*y)>=0 )
                {
                        (*x) -= (*y)>>i  ;
                        (*y) += dx>>i ;
                        (*z) -= ATAN[i];
                }
                else
                {
                        (*x) += (*y)>>i;
                        (*y) -= dx>>i;
                        (*z) += ATAN[i];
                }
        }
}


ich glaub die richtung stimmt ...

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
also der Algorithmus funktioniert jetzt ... zwar immer nur auf 1/4tel 
vom Kreis aber mit 4 If Abfragen bin ich ja wieder dort.

Autor: Jankey (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Also nach langem hin und her, auf signed integer funktioniert es richtig 
gut, aber auf unsigned char ( 8 bit unsigned ) funktioniert es einfach 
nicht weils eigentlich in den Negativen bereich gehen müsste ..

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.