Forum: Compiler & IDEs Sinusfunktion und glcd


von Adam (Gast)


Lesenswert?

Hallo Profis.

Ich habe erstmal folgende Frage an Euch.

Ich möchte an meinem 128x64 glcd eine Sinus-Kurve darstellen.

Bis jetzt habe ich eine Funktion mit der ich nur einen Punkt darstellen 
kann.

set_pixel(x,y);  //schreibt einen Pixel auf dem LCD

Hat jemand vielleicht eine Funktion/Lib mit der ich eine gesamte Sinus 
Kurve
darstellen kann ???
Wäre es möglich meine set_pixel Funktion in die Sinus Funktion zu 
implementieren?

Für Antworten sowie Anregungen bin ich sehr Dankbar.

Liebe Grüße

Adam

von Ralf (Gast)


Lesenswert?

Hi,

ich denke die einfachste Möglichkeit wäre, eine Tabelle anzulegen, in 
der die Sinuswerte für 90° eingetragen sind. Durch Spiegelung etc. 
erreichst du dann die anderen Werte. Die Anzahl der Einträge bestimmt 
die Genauigkeit, also z.B. 0.5°- oder 1°-Schritte. Über die Gradangabe 
ermittelst du den Sinuswert.

Ralf

von Adam (Gast)


Lesenswert?

Danke Ralf.

Hast Du vielleicht ein Link bzw ein Beispiel dafür ?

Wie Du merkst ist das Darstellen von Kurven nicht meine Stärke.

Vieln Dank nochmal für die Antwort.

Liebe grüße

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Ich möchte an meinem 128x64 glcd eine Sinus-Kurve darstellen.
Soso, mit welchem Prozessor denn? Mit welcher Sprache denn?

Es gibt in allen handelsüblichen Programmiersprachen eine Funktion zur 
Berechnung eines Sinus-Wertes ( sowas wie sin() ). Die liefert dann 
einen Wert zwischen -1 und +1 zurück, den mußt du nur noch entsprechend 
skalieren und einen Offset dazuaddieren. Und dann dein set_pixel() 
aufrufen.

von Adam (Gast)


Lesenswert?

Hallo Lothar.

Danke erstmal für die rasche Antwort.

Controller : Atmega32
Sprache : C
GLCD : 128x64

ich kann also einen Punkt am glcd "zeichnen". Soweit bin ich :o)
In wieweit kann ich denn math.h miteinbeziehen ???

Habe außerdem noch folgendes gefunden :


for(fx=0;fx<=129;fx=fx+1)
{
   fy=((sin(fx/10)*20)+ 64);
   LCD_pixel_write8((int8)(fx), (int8)(fy),BLACK);
   fy=((sin(fx/10+128)*20)+ 64);
   LCD_pixel_write8((int8)(fx), (int8)(fy),BLACK);
}

Dies gibt mir jedoch nur "Quatsch" aus...
Ich komme da einfach nicht weiter

Liebe Grüße

von Karl H. (kbuchegg)


Lesenswert?

Adam wrote:
> Danke Ralf.
>
> Hast Du vielleicht ein Link bzw ein Beispiel dafür ?
>
> Wie Du merkst ist das Darstellen von Kurven nicht meine Stärke.


Du musst dir eine Funktion ausdenken, die deine Kurve realisiert, indem 
du ein x vorgibst.
Dann lässt du das x in einer for-Schleife von 0 bis 127 laufen, rufst 
deine Funktion dafür auf (und erhältst so den y-Wert) und rufst deine 
set_pixel mit den so bekannten Werten auf.

Deine Funktion muss jetzt nur noch dafür sorgen, dass die errechneten 
y-Werte in einem sinnvollen Bereich (bei dir 0 bis 63) liegen. Das 
kannst du aber durch Mutliplikationen bzw. Addition von Werten leicht 
erreichen.

zb. liefert ein Sinus ja Werte zwischen -1 und +1
Damit die in den Bereich 0 bis 63 zu liegen kommen, kannst du zb. mit 31 
multiplizieren (dadurch wird der Wertebereich zu -31 bis +31) und dann 
noch 32 addieren (was den Wertebereich auf 0 bis 63 verschiebt)

   y = sin( x ) * 31 + 32;

indem du das Argument for x noch mit einem Faktor multiplizierst oder 
dividierst, kannst du jetzt den Sinus in die Länge ziehen
1
#include <math.h>
2
3
....
4
5
6
void plot_sinus()
7
{
8
  int x;
9
  int y;
10
11
  for( x = 0; x < 128; ++x ) {
12
    y = sin( x / 0.5 ) * 31 + 32;
13
14
    set_pixel( x, y );
15
  }
16
}

Du wirst wahrscheinlich auch schnell sehen, wo die Grenzen dieses 
einfachen Verfahrens liegen: WEnn sich deine Kurve nur schnell genug 
zwischen 2 benachbarten X-Werten verändert, entsteht eine Lücke, weil ja 
die zugehörigen Y-Werte mehr als 1 Pixel auseinander liegen. In dem Fall 
müsste mann dann einfach Linien zwischen jeweils 2 benachbarten (x,y) 
Wertepaaren zeichnen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

>  y = sin( x ) * 31 + 32;
wobei anzumerken ist, das sin() einen Wert zwischen 0 und 2*PI für einen 
vollständigen Umlauf erwartet.

Also wäre für
> x in einer for-Schleife von 0 bis 127...
die Funktion besser mit einen skalierten Wert aufzurufen:
  y = sin( x*2*PI/128 ) * 31 + 32;

EDIT:
> y = sin( x / 0.5 ) * 31 + 32;
Liege ich so falsch?  :-/

von Adam (Gast)


Lesenswert?

Vielen Dank Euch Beiden !

@Karl heinz Buchegger
@Lothar Miller

Beide Beiträge waren sehr hilfreich und haben zum Ergebnis geführt..

Lösung :

void plot_sinus()
{
  int x;
  int y;

  for( x = 0; x < 128; ++x ) {
    y = sin( x*2*3.14/128 ) * 31 + 32;

    set_pixel( x, y );
  }
}

Anstatt PI musste ich jedoch mit 3.14 angeben. (PI wird nicht akzeptiert 
)

Nun habe ich eine Sinuskurve :o)

Vielen Dank für die Hilfe

Liebe Grüße

von Karl H. (kbuchegg)


Lesenswert?

Lothar Miller wrote:
>
> EDIT:
>> y = sin( x / 0.5 ) * 31 + 32;
> Liege ich so falsch?  :-/

Nein, überhaupt nicht. Hängt davon ab, wieviele Sinus-Schwingungen er 
sehen will. Und ein bischen was zum Nachdenken soll für den TO ja 
schliesslich auch noch bleiben.
Der Hinweis mit dem Winkelargument in Radianten war schon wichtig.

von Adam (Gast)


Lesenswert?

Hallo nochmal!

Die Sinuskurve wird von der Mitte des LCD´s gezeichnet sprich in der
rechten Hälfte des LCD´s bis zum rechten Rand.
Dann wird sie weiter in der linken Hälfte der LCD´s bis zu Mitte 
gzeichnet.

Warum (wahrscheinlich gibt es dafür eine einfache Erklärung )????

Am Chipselect liegt es 100%-ig nicht. Das kann ich außschliessen.

Liebe Grüße
Adam

von Karl H. (kbuchegg)


Lesenswert?

Ein paar Experimente und daraus folgende Schlussfolgerungen solltest du 
schon selbst machen.

Zb. Welcher Punkt wird bei setpixel( 0, 0 ) eingeschaltet?

von Adam (Gast)


Lesenswert?

Hallo  Karl heinz.
Bitte überbewerte meine AVR-kenntnisse nicht. Bin ein blutiger
Anfänger :o) sorry

laut :

  for( x = 0; x < 128; ++x ) {
    y = sin( x*2*3.14/128 ) * 31 + 32;

    set_pixel( x, y );

wird der erste Punkt bei x=0 und y=32 gesetzt.
Das wäre die absolute linke Hälfte des LCD´s nicht die Mitte.

irgendwie tappe ich noch im Dunkeln.


Gruß

von Karl H. (kbuchegg)


Lesenswert?

Adam wrote:
> Hallo  Karl heinz.
> Bitte überbewerte meine AVR-kenntnisse nicht. Bin ein blutiger
> Anfänger :o) sorry

Das hat nichts mit AVR zu tun.

> laut :
>
>   for( x = 0; x < 128; ++x ) {
>     y = sin( x*2*3.14/128 ) * 31 + 32;
>
>     set_pixel( x, y );
>
> wird der erste Punkt bei x=0 und y=32 gesetzt.
> Das wäre die absolute linke Hälfte des LCD´s nicht die Mitte.

Sagt wer?
Was macht denn die set_pixel Funktion?
Die kann ihren 0-Punkt überall haben, wo sie will.
Links/oben
Links/unten
Mitte/Mitte
Mitte/oben
Mitte/unten
....

und ja nachdem, wo die set_pixel ihren 0-Punkt hat, verschieben sich 
dann natürlich alle andern Funktionen, die auf set_pixel aufbauen.

Also gilt es erst mal rauszufinden, welches Pixel von set_pixel 
eingeschaltet wird, wenn set_pixel(0,0) aufgerufen wird.

Und genau das meine ich mit: Die siehst einen seltsamen Effekt, 
überlegst dir was es sein könnte, klärst ihn ab und hast etwas ganz auf 
eigene Fasut gelernt. Ganz zu schweigen vom Erkentnissgewinn, dass 
höhere Funktionen (wie das Zeichnen einer Kurve) genau dann von den 
genauen Eigenschaften einer niedrigern Funktion (wie set_pixel) abhängen 
und man daher oft auch deren Details kennen sollte.

Vielleicht ist aber auch einfach nur dein GLCD verdreht und das was du 
als X-Achse ansiehst, ist in Wirklichkeit die Y-Achse.

Oder ...

von Olaf (Gast)


Lesenswert?

> Mitte/Mitte

Hat sowas schonmal irgendwo einer gesehen? Und werden dann Pixel
ueber Radius und Gon adressiert? :-D

Olaf

von Karl H. (kbuchegg)


Lesenswert?

Olaf wrote:
>> Mitte/Mitte
>
> Hat sowas schonmal irgendwo einer gesehen?

Macht man sogar relativ oft, dass bei einer Window/Viewport 
Transformation das default Window [-1..+1, -1..+1] lautet, der 0-Punkt 
also in der Mitte liegt.

> Und werden dann Pixel
> ueber Radius und Gon adressiert? :-D

LOL
Wenns dir Spass macht kannst du das auch so kriegen.

von Adam (Gast)


Lesenswert?

Hallo.

@Karl heinz Buchegger

Also. Ich habe den "Fehler" gefunden.
Es hatte doch (peinlich) was mit den Display-Controllern zu tun.
Die CS1 und CS2 Anschlüsse waren zwar korrekt verdrahtet aber
in der set_pixel Funktion habe ich einen Dreher gehabt und habe
nun CS1 und CS2 vertauscht.

<i>Am Chipselect liegt es 100%-ig nicht. Das kann ich außschliessen.</i>

Die Aussage bezog sich nur auf die Verdrahtung und nicht auf die
softwaremäßige Auswahl der Chipselect.

Aus Fehlern lernt man.

Jetzt versuche ich mal eine Variablenabhängige Sinus Funktion
darzustellen.

Ich komme wieder :o)


Danke mal wieder für die Hilfe.

Grüße

von Richard H. (Gast)


Lesenswert?

Hi,

habe folgendes Problem mit der hier diskutierten Routine. Compiler ist 
AVR Studio 4.16 und Chip ein ATmega644 mit 16MHz. Display ist ein 
320x240 Grafik-LCD mit SSD2119 LCD-Controller.
1
void plot_sinus(void)
2
{
3
  int x;
4
  int y;
5
6
  for(x=0; x<128; ++x) 
7
  {
8
    y = sin(x*2*3.14/128) * 31 + 32;
9
    LCDD_DrawPixel(hor_left_aligned, x, y, WHITE);
10
  }
11
}

Diese Funktion generiert im Bereich von x=14 bis x=48 fehlerhafte Werte. 
y nimmt in diesem Bereich nur Werte von 2, 32 oder 36 an. Außerhalb des 
Bereichs stimmen die Werte von y. Setze ich jetzt z.B. x=19 ohne 
for-Schleife, dann passt das Ergebnis mit y=56. In der for-Schleife ist 
aber bei x=19 das Ergebnis für y=2 ?!?

Kann mir da jemand weiterhelfen?

Gruß
Richard

von Paul W. (dantor)


Lesenswert?

Ohne Gewähr, liegt das vielleicht an Rundungsfehlern, hervorgerufen 
durch die ints?

von Karl H. (kbuchegg)


Lesenswert?

Paul W. schrieb:
> Ohne Gewähr, liegt das vielleicht an Rundungsfehlern, hervorgerufen
> durch die ints?

Nicht in der Größenordnung.
Irgendetwas läuft da mächtig schief, aber ich kann nicht sagen was.
Der Codeausschnitt ist auf jeden Fall in Ordnung.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Adam schrieb:
>
> Anstatt PI musste ich jedoch mit 3.14 angeben. (PI wird nicht akzeptiert)
>

In der math.h gibt es einen Define M_PI geben, der Pi auf einige 
Dezimalstellen genau angibt:
1
#define M_PI 3.141592653589793238462643

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.