Forum: Projekte & Code Gui System fuer GLCDs


von Stephan K. (dustpuppy)


Angehängte Dateien:

Lesenswert?

Hi und Dergleichen.

Im Rahmen der Erstellung eines neuen analog Fahrpultes fuer meine 
Modellbahn
hab ich mir Gedanken ueber die Anzeige und das Eingeben von Daten 
gemacht. Dabei entsteht grade ein sehr umfangreiches GUI und Dialog 
System fuer Grafik Displays. Das Ganze ist zwar noch nicht aufgereift, 
aber Grundlegende Funktionen sind bereits vorhanden. Im Moment arbeite 
ich an der Eingabe von Texten ueber ein virtuelles Keyboard und dem 
Message System.
Der Code ist zwar noch nicht aufgeraeumt und die Dokumentation ist mehr 
als duerftig, aber da ich im Source reichlich Komentare hab, muesste es 
trotzdem einigermassen verstaendlich sein. Das Ganze braucht mindestens 
4kb ram. Es laeuft auf einem Mega328 ohne Probleme.
Ich stell mal einfach den Source des Ganzen beim aktuellen Stand rein. 
Wer will, kann ja mal rein schnueffeln.
Ich habe unteranderem einige Libs von Peter Danneberger, Christian R. 
und anderen verwendet. Man soll ja das Rad nicht neu erfinden.

Gruesse

Dusty

P.S.: Wenn wieder was funzt, dann kommt's auch hier rein, wenn ihr 
wollt.

von Werner B. (werner-b)


Lesenswert?

Upps, böse Falle.

alloca alloziert seine Objekte auf dem Stack, darum kannst/darft du 
NIEMALS per alloca erzeugte Objekte zurückgeben.
1
GUI_OBJECT GUI_OBJECT_NEW(int type, int x, int y, int w, int h, char *label)
2
{
3
  GUI_OBJECT *object = (GUI_OBJECT *)alloca (sizeof (GUI_DIALOG));
4
  ...
5
  return *object;
6
}
Das verlassen der Funktion ist ein explizites "free(object);".
Wundere dich also nicht wenn es ab und zu knallt.
Besser mit malloc und ein GUI_OBJECT_DESTROY(GUI_OBJECT obj);

http://www.mkssoftware.com/docs/man3/alloca.3.asp

von Sven P. (Gast)


Lesenswert?

Du betreibst da aber ganz schön viel Speicherverschwendung. Wenn du 
ohnehin den Typ des Objektes in der GUI_OBJECT-Struktur hinterlegst, 
kannst du auch ganz bequem den Rest mit einer union erschlagen. Oder mit 
einem Element variabler Länge am Ende, du erzeugst die Objekte ja eh auf 
dem Heap.

von Stephan K. (dustpuppy)


Lesenswert?

@Werner B.
Komischer weise funktioniert das so. Wenn ich mit malloc den Platz 
allokiere, stuerzt es sofort ab.

@Sven P.
Wie koennte ich den Speicherverbrauch reduzieren, ohne an 
Funktionalitaet zu verlieren?

Waere klasse, wenn ihr mir unter die Arme greift.

Gruesse

Dusty

von Werner B. (werner-b)


Lesenswert?

Sehe ich erst jetzt.

Als erste Änderung, einen Zeiger zurückliefern, nicht eine komplette 
Struktur.
==>
1
GUI_OBJECT * GUI_OBJECT_NEW(int type, int x, int y, int w, int h, char *label)
2
{
3
  GUI_OBJECT *object = (GUI_OBJECT *)malloc(sizeof(GUI_DIALOG));
4
  ...
5
  return object;
6
}
Auch im Header File anpassen.
Das wird allerdings einen ganzen Rattenschwanz anderer Anpassungen nach 
sich ziehen. Du wirst zuerst einmal mit Fehlern und Warnungen 
überschwemmt werden.

von Arne (Gast)


Lesenswert?

Stephan Kempa schrieb:
> @Werner B.
> Komischer weise funktioniert das so. Wenn ich mit malloc den Platz
> allokiere, stuerzt es sofort ab.

Ist trotzdem falsch ;)
Wir haben neulich Code von einem Zulieferer erhalten, der (stark 
vereinfacht) wie folgt implementiert war:
1
void Perform(void)
2
{
3
  uin32_t*  myPointer_PU32;
4
  
5
  switch(ausdruck) {
6
    case 1:
7
      myPointer_PU32 = ...  /* Zuweisung an den Pointer  */
8
      break;
9
    case 2:
10
      blabal = myPointer_PU32[..];
11
      /* jetzt blabla verwenden */
12
      break;
13
    }
14
}
Die Perform-Funktion wird regelmäßig aufgerufen.
Hat funktioniert, weil der Stackframe immer an derselben Stelle angelegt 
wurde. PC-Lint hat den Fehler gefunden.

von Stephan K. (dustpuppy)


Lesenswert?

Klappt jetzt mit malloc. Allerdings ist der Speicherverbrauch jetzt so 
gross, dass nix mehr uebrig bleibt auf dem Mega328 :-(
Na ja, ein 1280er ist bestellt, da is 4mal so viel ram drauf, trotzdem 
waer es nicht schlecht den Verbrauch zu reduzieren.

von Werner B. (werner-b)


Lesenswert?

Ein kleiner Bug der nur bei nicht symetrischen LCD auffällt.

Statt
#define GLCD_BYTES_PER_ROW     (GLCD_XMAY/GLCD_FONT_WIDTH)

muss
#define GLCD_BYTES_PER_ROW     (GLCD_XMAX/GLCD_FONT_WIDTH)

in lib/t6963c.h stehen.

von Werner B. (werner-b)


Lesenswert?

Werner B. schrieb:
> Ein kleiner Bug der nur bei nicht symetrischen LCD auffällt.
>
> Statt
> #define GLCD_BYTES_PER_ROW     (GLCD_XMAY/GLCD_FONT_WIDTH)
>
> muss
> #define GLCD_BYTES_PER_ROW     (GLCD_XMAX/GLCD_FONT_WIDTH)
>
> in lib/t6963c.h stehen.

Wenn man nicht aufpasst. Falsch ist natürlich GLCD_YMAX, nicht 
GLCD_XMAY.

von Stephan K. (dustpuppy)


Lesenswert?

Das ist nicht das einzige Problem. Ich hab mittlerweile meinen 1280 und 
der Speicher ist sofort voll. Ich suche noch, warum der Krempel sofort 
den Speicher belegt.

von Werner B. (werner-b)


Angehängte Dateien:

Lesenswert?

Eigentlich wollte ich noch warten bis alles getestet und der Code 
aufgeräumt ist.
Aber so habe ich einen Tester ;-)

Im Anhang eine zum Teil völlig überarbeitete Version deines Programmes 
(siehe Change.log).
Auf einem ATmega32 werden damit ca. 22kB FLASH und 60 Byte RAM statisch 
belegt. Der RAM Verbrauch im Betrieb ist (wegen Stack und malloc) 
natürlich wesentlich größer. Aber auf dem Mega32 sind nach Programmstart 
von den 2kB RAM noch ca. 1500 Byte frei.
Im Makefile MCU, F_CPU und AVRDUDE_PROGRAMMER anpassen.
Da ich an meinem Test-Board die Input Möglichkeiten noch nicht angebaut 
habe kann ich nicht sagen ob diese Funktioniert. Aber die graphischen 
Komponenten sind gestest.
Falls das Menuesystem nicht funktioniert bitte mit der (um ca. 500 byte 
Flash größeren) gui.c-25092011 testen.

von W.S. (Gast)


Angehängte Dateien:

Lesenswert?

Also, mit Verlaub, euer GUI finde ich grauenvoll. Es geht alles 
durcheinander und ist nicht strukturiert.

Ich sehe das Ganze anders, nämlich so:
1. Man sollte unbedingt das grafische System, also das, was man zumeist 
als 'GDI' bezeichnet, strikt trennen vom Menüsystem. Im GDI sollten all 
die Funktionen sein, die man zum Zeichnen der grafischen Primitiven so 
braucht:
Pixel setzen, Linien zeichnen, Rechtecke zeichnen, Text zeichnen.  Man 
braucht sicherlich Fonts, also sind im GDI auch Fonts und deren 
Verwaltung angesiedelt. Außer dem Speicher für den Bildschirm braucht 
dieses GDI eigentlich gar keinen RAM, wenn man mal von ein paar 
Variablen auf dem Stack absieht.

2. Für ein auf diesem GDI aufsetzendes Menüsystem sollte man nach 
Lösungen suchen, die eben kein malloc und Konsorten benötigen. Das ist 
schwierig in C, denn echte objektorientierte Programmierung, die sich 
hier anbieten würde, gibt es in C nicht.

Also wären struct's als Quasi-Objekte im Code angesagt, die aber 
ihrerseits keine variablen Daten enthalten dürfen. Ein Ausweg wäre es, 
jedem "Objekt" einen void Zeiger mitzugeben, der auf ein Stück RAM 
zeigen darf, wenn dieses Objekt sowas braucht. Diese müßten etwa so 
aussehen:
struct guiobjekt
{ struct guiobject* davor;
  struct guiobject* danach;
  struct guiobject* Owner;
  struct guiobject* Members;
  TRect  bounds;
  void*   Pointer;
  ... Flags, PenColor, BrushColor,...
  und dann 3 Funktionen:
  ... OnEvent(struct guiobject* self, int* Event);
  ... OnKey  (struct guiobject* self, int* Key);
  ... OnDraw (struct guiobject* self);
};
Diese Struktur muß dann für alle grafischen Objekte gleich sein, 
lediglich in der jeweiligen OnDraw-Funktion unterscheiden sie sich. 
Bedient wird das Ganze dann, indem man Events (geht alle Objekte an) und 
Bedienereignisse (OnKey, geht nur das Fokussierte an) über die OnEvent 
und OnKey-Funktion des "Mutter"-Objekts hineinwirft.
OK, sowas ist komplex genug, um nicht mit den GDI-Primitiven vermengt 
werden zu dürfen. Aber es ist auch abgehoben genug, um einen kompletten 
Wechsel des GDI (z.B. von Farbe auf S/W) ohne jegliche Änderung zu 
vertragen. Ich hab mir für meine Zwecke ein Menü-Konstruier-Programm 
geschrieben, womit ich am PC mit direkter grafischer Darstellung die 
Menüs konstruieren und dann als fertige C-Quelle ausgeben kann. Ist auch 
nötig, um all die Zeiger der Objekte im ROM richtig in die Reihe zu 
bekommen.

Ich habe hier mal ein "Bonsai"-Beispiel für ein simples GDI angehängt. 
Bloß so, um meine Art des Herangehens zu zeigen. Es ist eine für ne 
kleine Anwendung mit einem LPC2103 auf's Minimum zusammengestrichene 
Variante meines eigentlichen GDI's, das allerdings aus eher historischen 
Gründen zum Teil in Assembler geschrieben ist (Fujitsu, für euch 
sicherlich uninteressant). Deswegen greifen die diversen Routinen aus 
Faulheit auf die Pixelfunktion zurück. Für nen kleinen S/W-Screen geht 
das, für QVGA in Farbe macht man's jeweils direkt.

Ach ja, wie die Fonts gebaut sind, seht ihr sicherlich selber. Den 
Compiler und zwei Beispielfonts leg ich bei.

W.S.

von Stephan K. (dustpuppy)


Lesenswert?

Hi,
was du angehaengt hast, ist nur ein reines GDI mit fonts. Und da hast du 
exakt gegen deine Ausfuehrungen des Trennens verstossen. Font Funktionen 
und Grafik Funktionen sind in einer Datei.
Ich habe im source die Funktionen fuer das Display, dem GDI, den Fonts 
und, und ,und von einander getrennt. Tausche die t6963c.c und den Header 
gegen Display Funktionen fuer einen anderen Type aus und es aendert sich 
nix. Oder nimm eigene Grafik Funktionen und tausche graphic.c mit Header 
aus. Guck es dir mal richtig an. Dass die Grafik auf den Lowlevel Teil 
aufsetzt ist wohl klar, genauso, wie die Fonts die Grafik Funktionen, 
wie z.b. Set_pixel brauchen.

Gruesse

Dusty

von W.S. (Gast)


Lesenswert?

Stephan Kempa schrieb:
> was du angehaengt hast, ist nur ein reines GDI mit fonts.

Ja, genau.
Ist wie beschrieben ein Beispiel, Zitat meiner selbst: "Ich habe hier 
mal ein "Bonsai"-Beispiel für ein simples GDI angehängt. Bloß so, um 
meine Art des Herangehens zu zeigen."


> Und da hast du
> exakt gegen deine Ausfuehrungen des Trennens verstossen. Font Funktionen
> und Grafik Funktionen sind in einer Datei.

Genau DAS ist volle Absicht, kein Verstoß gegen mich selber und von mir 
auch ebenso beschrieben. Das Zeichnen von Schrift ist eine zentrale 
Aufgabe eines GDI - und deswegen gehört es auch dort hinein. Es gehört 
nicht in irgendwelche Fonts, die haben gefälligst passive Daten zu sein.

In meinem Fullsize-GDI arbeite ich mit DC's (Device Kontexten). Für alle 
Grafikoperationen muß so ein DC als Parameter mitgeliefert werden, denn 
er enthält die aktuellen Farben, den aktuellen Style und Font und den 
aktuellen Screen - und da man in den DC auch einen beliebigen Font laden 
kann (so man ihn hat), kann das GDI ihn auch zeichnen.

Die Fonts als solche sind universell und recht praktisch. Bislang aber 
nur in 8 Bit und nicht als Unicode. Ich benutze die C-Quellen in 
diversen recht unterschiedlichen Anwendungen mit unterschiedlichen 
CPU's. Manche little und andere big endian. Allerdings keine Atmel AVR, 
wo man ständig mit Zeigern aller Art kämpfen muß und deswegen 2 
Funktionen für manches braucht: Für Zeugs, was im Flash liegt und für 
Zeugs, was nicht im Flash liegt (z.B. dein GLCD_STRLEN_P/GLCD_STRLEN). 
Genau DAS ist auch einer meiner Gründe, Atmels nicht sonderlich zu 
mögen.

Also, du magst es ignorieren oder benutzen, das ist deine Sache. 
Vermutlich ist mein Zeugs für Atmels auch nicht geeignet, eben wegen 
dieser Flash/NonFlash - Problematik, die man woanders nicht hat.

W.S.

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.