C Zeichnen mit einem GLCD

Janiiix3

Aktives Mitglied
28. Sep. 2013
1.333
10
38
Hannover
Sprachen
  1. ANSI C
  2. C#
Endlich mal wieder Freitag und ein bisschen Zeit zum basteln :p

Aktuell möchte Ich mich mal tiefer mit den GLCD´s beschäftigen.
Meine Frage dazu lautet...

Wie zeichne Ich am besten? Arbeite Ich auf dem quell µC mit einem VRAM oder zeichne Ich direkt in den RAM vom GLCD µC?

Als erstes möchte ich ein ganz einfaches Rechteck zeichnen..

Der Nachteil von einem VRAM für den quell µC ist der Speicherbedarf... Wenn ich jetzt ein 64 x 128 GLCD habe, brauche ich ja schon ->



CodeBox C
char VRAM[64][128];
 
Grundsätzlich gehe ich davon aus, daß Du kein "intelligentes" Display mit eingebauten Zeichenroutinen meinst.
Wie zeichne Ich am besten?
Das kommt darauf an...;)

... zB, ob Du Daten vom Display überhaupt zurücklesen kannst. Üblicherweise adressierst Du ja erstmal eine Page (Zeile mit acht untereinanderliegenden Pixeln) und eine Spalte, und schreibst mit einem Datenbyte die acht Pixel. Um nur einen dieser Pixel zu setzen (oder zu löschen), müßtest Du also erstmal den Ist-Zustand dieser acht Pixel auslesen (->read), dann den konkreten Pixel setzen (oder löschen -> modify), und anschließend das ganze zum Display senden (->write). RMW.
Du hast also mehr Traffic auf dem Display-Bus, den Du Dir bei Verwendung von seperatem Speicher sparen kannst.
Es gibt (wie angedeutet) Displays, bei denen gar nicht zurückgelesen werden kann (zB weil MISO bei SPI nicht verdrahtet/kontaktiert ist...).
 
Willst du also darauf anspielen, dass ich lieber einen VRAM nehmen sollte?
 
Nein, das kommt darauf an...

Wenn Du keine Daten zurücklesen kannst, mußt Du den Ist-Zustand anders herleiten. Entweder mittels Abbild (VRAM), oder "irgendwie anders".
Wenn Du keine Daten zurücklesen willst (weil Dir das z.B. zu lange dauert...), willst Du den Ist-Zustand anders ermitteln.

Bei meinen kleinen OLEDs zB könnte ich zwar den Speicher auslesen, aber bei 16x96 Pixeln macht das nicht viel Sinn. Da kommt erstmal nur ein 12pt-Text drauf, und drunter 3 Pixel für'n Balken.
Bei Textänderungen muß eh immer alles geschrieben werden, da kann ich den Wert des Balkens auch jedesmal mitverORren.
 
Wenn ich jetzt ein 64 x 128 GLCD habe, brauche ich ja schon ->


CodeBox C
char VRAM[64][128];
Wenn es sich um ein einfarbiges Display handelt:


CodeBox C
char VRAM[8][128];

Auch das lohnt sich nur für einen µC, der deutlich mehr als 1 kB RAM hat.
 
Wenn es sich um ein einfarbiges Display handelt:


CodeBox C
char VRAM[8][128];
Stimmt natürlich, 8 Byte für 64 Pixel pro Spalte...
Auch das lohnt sich nur für einen µC, der deutlich mehr als 1 kB RAM hat.
Man könnte ja auch einen Teil des Displays im SRAM virtualisieren, und einen Teil fest beschreiben. Kommt halt auf die konkrete Anwendung an.
Wenn der Controller-Speicher nicht reicht, kann auch externer Speicher 'ne Option sein...
 
Man könnte ja auch einen Teil des Displays im SRAM virtualisieren, und einen Teil fest beschreiben. Kommt halt auf die konkrete Anwendung an.
Wenn der Controller-Speicher nicht reicht, kann auch externer Speicher 'ne Option sein...
Genau.
Und wenn es nicht zeitkritisch ist, kann man auch das ganze Bild neu schreiben, dann braucht man gar kein VRAM.
 
Mit dem Array


CodeBox C
char VRAM[64][128]

Wollte Ich eigentlich ein komplettes Abbild vom GLCD erstellen.

Texte kann Ich schon darstellen.

Was noch nicht so klappt ist eben zeichnen. Erstmal wollte Ich ein einfaches Rechteck zeichnen.
Das haut noch nicht ganz so sauber hin wie Ich es mir vorstelle.

Mit diesen beiden Funktionen habe Ich versucht ein Rechteck zu zeichen.
Sind diese Funktionen überhaupt korrekt? Ganz speziell was Ich mir da zusammengebaut habe, mit den Parametern laut Datenblatt?



CodeBox C
void glcd_column(uint8_t column)
{
   uint8_t LowByte       = 0;
   uint8_t HighByte   = 0; 
 
   /* Build High Byte */
   HighByte |= 0x10;
   if (column & 1<<7)
       HighByte |= 1<<3;
   if (column & 1<<6)
       HighByte |= 1<<2;
   if (column & 1<<5)
       HighByte |= 1<<1;
   if (column & 1<<4)
       HighByte |= 1<<0;
 
   /* Build Low Byte */
   if (column & 1<<3)
       LowByte |= 1<<3;
   if (column & 1<<2)
       LowByte |= 1<<2;
   if (column & 1<<1)
       LowByte |= 1<<1;
   if (column & 1<<0)
       LowByte |= 1<<0;
 
   glcd_sendcmd(HighByte);
   glcd_sendcmd(LowByte);
}

void glcd_row(uint8_t row)
{
   glcd_sendcmd(0x40 | (row));
}


https://www.lcd-module.de/eng/pdf/zubehoer/st7565r.pdf
ab Seite 41 geht es los.

Mit diesen Funktionen die Ich mir geschrieben habe, müsste Ich doch an die Position "x,y" vom Display kommen und schreiben können oder?
 
kann man auch das ganze Bild neu schreiben, dann braucht man gar kein VRAM.

Die Displays besitzen ja ihren eigenen Speicher, den man zum setzen oder löschen einzelner Pixel auslesen muß (da immer ein ganzes Byte geschrieben wird).
Wenn man den nicht auslesen kann oder will, muß man die Bilddaten woanders Speichern, ein Abbild des Speichers im Controller (oder zusätzlicher externer Speicher) zB.
Wenn einfach jedesmal das ganze Display neu gezeichnet werden soll, mußt Du Dir aber auch irgendwo die zu zeichnenden Inhalte merken.
Wenn man (wie in meinem Fall) das Bild aus einigen Werten berechnen/von Speicheradressen laden kann, wird das recht einfach - aber wenn der Benutzer zB zur Laufzeit irgendwas draufzeichnen soll, wirds komplexer.

Kommt drauf an, wie das Display genutzt werden soll, was letztendlich darauf dargestellt werden soll...

Sind diese Funktionen überhaupt korrekt? Ganz speziell was Ich mir da zusammengebaut habe, mit den Parametern laut Datenblatt?
Das Datenblatt bekomm ich am Tablet grad nicht geladen... aber warum diese komplizierte If-Abfragerei? -> Bits schubsen...

Was soll da eigentlich geschehen?
Es ist ein Byte Column gegeben.
Es soll ein Byte Highbyte berechnet werden - genauer: Highbyte soll das obere Nibble von Column rechtsorientiert enthalten, zusätzlich soll Bit 5 gesetzt sein, also Highbyte= Column>>4 OR 0x10 (oder column>>4 + 1<<4)
Das Lowbyte soll einfach nur das untere Nibble von Column sein? Lowbyte=Column AND 0x0F ?

Noch'n Nachtrag: unter ASM würde man statt viermal rechtsschieben einfach die Nibbles SWAPpen (eine Instruktion), und anschließend das obere Nibble wegmaskieren (ANDI mit 0x0F, eine Instruktion). Das OR bzw die Addition für die "1" im oberen Nibble bleibt zusätzlich, klar.
Inwiefern C SWAP unterstützt, muß ein C-Profi sagen...
 
Zuletzt bearbeitet:
Was soll da eigentlich geschehen?
Es ist ein Byte Column gegeben.
Es soll ein Byte Highbyte berechnet werden - genauer: Highbyte soll das obere Nibble von Column rechtsorientiert enthalten, zusätzlich soll Bit 5 gesetzt sein, also Highbyte= Column>>4 OR 0x10 (oder column>>4 + 1<<4)
Das Lowbyte soll einfach nur das untere Nibble von Column sein? Lowbyte=Column AND 0x0F ?

Du hast total Recht, das war viel zu aufwendig^^


CodeBox C
void glcdSetColumn(uint8_t column)
{
   uint8_t lowByte       = 0;
   uint8_t highByte   = 0;  
  
   /* Build High Byte */
   highByte = ((column>>4) | 0x10);
  
   /* Build Low Byte */
   lowByte = (column & 0x0F);
  
   glcd_sendcmd(highByte);
   glcd_sendcmd(lowByte);
}
 
Zuletzt bearbeitet:
Sorry, wenn ich hier einfach so "rein platze", wo ich jetzt was vom ST7565 lese ...

Ich hatte ja schon einmal auf ein Projekt bei den Ressourcen hingewiesen.

Da verwende ich ein Display mit ST7565R-G Controller
Es wäre eventuell sinnvoll das man anzuschauen.


Das Projekt nutzt einen Atmega32

Im SRAM habe ich den Grafikspeicher des Displays abgebildet (unabhängig von der Organisation des Grafikspeichers im Display, also Pages zum Beispiel, unabhängig vom Display, 1 Bit pro Pixel).

Es gibt eine Funktion DrawPixel, die kann an der Position (X;Y) des Grafikspeichers im SRAM ein Pixel sezten/löschen.

Diese Funktion wird von weiteren Grafikfunktionen genutzt. Linie, Rechteck, Kreis, Font, Bildschirm löschen/füllen, Zahlen ...

Landscape und Portrait Modus wird unterstützt, auch diverse Funktionen wir OR XOR AND beim Schreiben in den Grafikspeicher.

Das ganze ist also erst mal unabhängig vom verwendeten "einfarbigem" Display und eigentlich auch unabgängig vom verwendeten Mikrocontroller oder der Mikrocontrollerfamilie.

Nun gibt zwei Bereich die angepasst werden müssen:
(1) SPI Schnittstelle
Im Projekt habe ich wahlweise SoftwareSPI und HardwareSPI AVR/Xmega verwendet. Das muss an den Mikrocontroller angepasst werden.

(2) Display Protokoll
Funktion Display_Update, diese Funktion kopiert den Grafikspeicher vom SRAM in den Grafikspeicher des Displays. Das muss an das Display angepasst werden.
Das Display hat den Controller ST7565R-G, also anpassen muss man nicht viel,
ausser vielleicht die Initialisierung des Displays, Kontrast...
 
Sorry, wenn ich hier einfach so "rein platze", wo ich jetzt was vom ST7565 lese ...

Ich hatte ja schon einmal auf ein Projekt bei den Ressourcen hingewiesen.

Da verwende ich ein Display mit ST7565R-G Controller
Es wäre eventuell sinnvoll das man anzuschauen.


Das Projekt nutzt einen Atmega32

Im SRAM habe ich den Grafikspeicher des Displays abgebildet (unabhängig von der Organisation des Grafikspeichers im Display, also Pages zum Beispiel, unabhängig vom Display, 1 Bit pro Pixel).

Es gibt eine Funktion DrawPixel, die kann an der Position (X;Y) des Grafikspeichers im SRAM ein Pixel sezten/löschen.

Diese Funktion wird von weiteren Grafikfunktionen genutzt. Linie, Rechteck, Kreis, Font, Bildschirm löschen/füllen, Zahlen ...

Landscape und Portrait Modus wird unterstützt, auch diverse Funktionen wir OR XOR AND beim Schreiben in den Grafikspeicher.

Das ganze ist also erst mal unabhängig vom verwendeten "einfarbigem" Display und eigentlich auch unabgängig vom verwendeten Mikrocontroller oder der Mikrocontrollerfamilie.

Nun gibt zwei Bereich die angepasst werden müssen:
(1) SPI Schnittstelle
Im Projekt habe ich wahlweise SoftwareSPI und HardwareSPI AVR/Xmega verwendet. Das muss an den Mikrocontroller angepasst werden.

(2) Display Protokoll
Funktion Display_Update, diese Funktion kopiert den Grafikspeicher vom SRAM in den Grafikspeicher des Displays. Das muss an das Display angepasst werden.
Das Display hat den Controller ST7565R-G, also anpassen muss man nicht viel,
ausser vielleicht die Initialisierung des Displays, Kontrast...

Danke @Dirk .
Ich will es lernen und versuchen das mal selbst auf die Beine zu stellen.
 
upload_2018-1-20_12-2-59.png

Für mich hat das Display Physikalisch gesehen "63x127" Bildpunkte.
Wenn ich jetzt ganz unten ans Display schreiben möchte, reicht dann nicht einfach Column auf sagen wir mal "0" und "Start Line" auf "63" zu setzen? Oder muss ich auch was mit den Pages machen?
 
Ist es vill. so besser zu verstehen und effektiver?


CodeBox C
void glcdSetAddress(uint8_t column, uint8_t row)
{
   /*
   *   Baue das Kommando für die Zeile
   */
   glcdSendCmd((column>>4)|1<<4);
   
   /*
   *   Adresse
   */
   glcdSendCmd(column & 0x0F);
   
   /*
   *   Setze die Reihe
   */
   glcdSendCmd((row)|1<<6);
}
 
Für mich hat das Display Physikalisch gesehen "63x127" Bildpunkte.
Vermutlich sind es 64 Zeilen (von 0 bis 63 durchnummeriert) und 128 Spalten (von 0 bis 127),
wobei die 64 Zeilen in 8 Seiten unterteilt sind.
Wenn ich jetzt ganz unten ans Display schreiben möchte, reicht dann nicht einfach Column auf sagen wir mal "0" und "Start Line" auf "63" zu setzen?
Start Line benutzt man üblicherweise zum scrollen.
Oder muss ich auch was mit den Pages machen?
Aber sicher. Als erstes solltest Du den Speicheraufbau auf Seite 27 verstanden haben.
Um einen Punkt zu setzen braucht man die Befehle Page Address Set, Column Address Set und Display Data Write.

Wenn Du das wirklich alles selbst neu schreiben willst, wirst Du wohl nicht umhinkommen, Dich mal ein paar Tage mit dem Datenblatt zu beschäftigen und wenn Du den Bresenham-Algorithmus noch nicht programmiert hast, werden wohl noch mal ein paar Tage dazukommen. ;)
 
Zuletzt bearbeitet:
Ob ich nun die "Page" Adressiere oder die "Page Line" ist doch relativ oder?

upload_2018-1-20_21-7-15.png
 
Was meinst Du mit Page Line? Den Befehl gibt es nicht im Datenblatt.
Du kannst Page und Column nur setzten.
Display Start Line Set verschiebt nur die Startadresse des internen RAM.
Keiner der Set-Befehle schreibt überhaupt irgendwelche Daten ins RAM.
 
Folgendes..
Jede Page hat ja "8 Lines" (D0-D7).. Mit "Display Start Line Set" kann Ich doch auswählen wo auf der "x" Achse Ich anfangen will zu schreiben? Das dass keine Schreibbefehle sind ist mir schon klar.
Wenn ich jetzt den Befehl für die "Display Start Line Set" dem Display sende z.B glcdSendCmd(1<<6 | LINE_ADDRESS); Müsste ich doch an "LINE_ADDRESS" sein und könnte da anfangen in den RAM zu schreiben?
 
Daß mit Data Write zu schreiben ist, wird Jan klar gewesen sein. Ihm gings eher um das Ziel wohin geschrieben werden soll.

Ich vermute(!), Jans Gedankengang war folgender: Wenn die Page (default) auf Page0 gesetzt ist, kann man Page1 (Zeile2 im physischen Display) vielleicht beschreiben, indem man die Daten nach Page0 schreibt, und die Startline verschiebt. Damit könnte der Inhalt korrekt in der zweiten Zeile (des Displays) landen - aber eben auch das ganze Display verschoben sein. Insbesondere würde der Inhalt von Page8 in der ersten Displayzeile erscheinen.

Bei einem Display mit max 32 Zeilen kann man dann auch zwei "Bilder" in den Speicher schreiben, und mit der Startline zwischen beiden wechseln?
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)