Forum: PC-Programmierung Anfänger C / C++ Pointerarithmetik


von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Ich arbeite in einer µC-Umgebung, aber ich denke für diese Thematik ist 
dieser Thread besser geeignet.

Ich habe folgende Klasse, welche ich nach Bedarf auch beliebig verändern 
kann:
1
  class FontInfo
2
        {
3
    // This structure describes a single character's display information
4
  public:
5
    class CharacterInfo
6
    {
7
    public:
8
      uint8_t* widthpixel;  // width, in bits (or pixels), of the character
9
      uint8_t* unused;  // emWin creates here an unused value
10
      uint8_t* datawidth;  // width, in bytes of data bitmap
11
      uint8_t* data;    // offset of the character's bitmap, in bytes, into the FONT's data array
12
    };
13
    // Describes a single font
14
    uint8_t* fontName;    // Font name string
15
    uint8_t* charHeight;    // height in pixels
16
    uint8_t* startChar;    // the first character in the font (e.g. in charInfo and data)
17
    uint8_t* endChar;    // the last character in the font
18
    uint8_t* spaceWidth;    // width, in pixels, of space character
19
    CharacterInfo charInfo[256];  // pointer to array of char information
20
        }

Die Daten um diese Klasse zu initialisieren liegen auf dem SDRAM und ich 
möchte möglichst nur einen Pointer auf dem SRAM haben, der auf diese 
Daten zeigt.

Ich mache also zb Folgendes:
1
FontInfo* blubb(uint8_t* data)
2
{
3
    FontInfo ft;
4
    ft.fontName = &data[0]
5
    ...
6
    usw.
7
    ...
8
    return &ft;
9
}
10
11
int main()
12
{
13
    FontInfo* asdf = blubb(blablubbdata);
14
}

Erstmal eine Frage, bevor ich anfange weiter zu fragen:
Wenn ft aus dem scope geht, ist der Zeiger asdf nichts mehr wert, oder?

EDIT: Jetzt hab ich doch das tatsächlich was vergessen:

Vielen Dank schonmal und
Viele Grüße
Reggie

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Reginald L. schrieb:
> Wenn ft aus dem scope geht, ist der Zeiger asdf nichts mehr wert, oder?
Richtig, d.h. der Rückgabewert von blubb ist nutzlos.

Reginald L. schrieb:
> FontInfo* blubb(uint8_t* data)
> {
>     FontInfo ft;
>     ft.fontName = &data[0]
>     ...
Warum so kompliziert? Warum tust du nicht direkt eine FontInfo-Klasse in 
den S(D??)RAM, warum ist deine Font ein uint8_t Array?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Dr. Sommer schrieb:
> Warum so kompliziert? Warum tust du nicht direkt eine FontInfo-Klasse in
> den S(D??)RAM
Ich kann nur statische Variablen im SDRAM zur Compilelaufzeit erstellen. 
Und ich weiß erst während der Programmlaufzeit wieviele Fonts geladen 
werden müssen.

Dr. Sommer schrieb:
> warum ist deine Font ein uint8_t Array?
Weils eine AntiAliasing-Schriftart ist. Um die Speichereffizienz der 
Schriftarten gehts hier auch nicht.

Dr. Sommer schrieb:
> Richtig, d.h. der Rückgabewert von blubb ist nutzlos.
OK, danke. Demnach müsste der Rückgabewert also ein "Non-Pointer" sein. 
Wie schaut es da mit dem Speicherverbrauch aus? Jeder Pointer 
verbraucht, in meinem Fall, 4x8bit, also wäre es schonmal Quatsch 
beispielsweise die charHeight und startChar als Pointer zu definieren?

Im Endeffekt zielt meine Frage darauf ab, ob es möglich ist, einen 
FontInfo-Pointer auf dem SRAM zu erstellen, der auf eine Speicherstelle 
auf dem SDRAM zeigt, in dem die Daten liegen. Dh die Werte von FontInfo 
werden also nicht wirklich initialisiert.

Also im Prinzip so was:
1
FontInfo* ft = (FontInfo*)0xD0000000);
2
// Jetzt kann ich ganz normal spielen:
3
uint8_t asdf = ft->startChar;

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Reginald L. schrieb:
> Im Endeffekt zielt meine Frage darauf ab, ob es möglich ist, einen
> FontInfo-Pointer auf dem SRAM zu erstellen, der auf eine Speicherstelle
> auf dem SDRAM zeigt, in dem die Daten liegen.

Sofern SRAM und SDRAM im gleichen Adressraum liegen (also keine 
Klimmzüge zum Ansprechen des einen oder anderen nötig sind), geht das 
selbstverständlich.

Übrigens hat das Thema mit Pointerarithmetik nichts zu tun, darunter 
versteht man gemeinhin was anderes.

Reginald L. schrieb:
> Jeder Pointer verbraucht, in meinem Fall, 4x8bit

Gib in C/C++ keine Größen in Bit an; Bit sind nicht einzeln adressierbar 
(außerhalb von Bitfeldern). Das kleinste adressierbare Objekt ist das 
Byte.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Rufus Τ. F. schrieb:
> Übrigens hat das Thema mit Pointerarithmetik nichts zu tun, darunter
> versteht man gemeinhin was anderes.
Ich habe mal wieder ein paar Sachen von deinen Posts gelernt, das lässt 
sich nicht abstreiten.
Aber zur Lösung hast du, sofern ich mich recht erinnere, nicht 
beigetragen ;)

Vielleicht kannst du den Titel ja noch nachträglich ändern. Wäre blöd, 
wenn so ein Mist hier im Forum stehen bleibt.

Rufus Τ. F. schrieb:
> Sofern SRAM und SDRAM im gleichen Adressraum liegen (also keine
> Klimmzüge zum Ansprechen des einen oder anderen nötig sind), geht das
> selbstverständlich.
Wie wird der Adressraum hier definiert? Also der Compiler weiß 
eigentlich nicht ob das jetzt der SDRAM oder der SRAM ist. Ich kann ganz 
normal eine 32-bit Adresse angeben.
Und, wenn das selbstverständlich geht, verrätst du mir auch wie?

Rufus Τ. F. schrieb:
> Gib in C/C++ keine Größen in Bit an; Bit sind nicht einzeln adressierbar
> (außerhalb von Bitfeldern). Das kleinste adressierbare Objekt ist das
> Byte.
Wie gesagt, gelernt.

von Dr. Sommer (Gast)


Lesenswert?

Reginald L. schrieb:
> Weils eine AntiAliasing-Schriftart ist.

Was ist das denn für ein Argument o.O

Reginald L. schrieb:
> Und ich weiß erst während der Programmlaufzeit wieviele Fonts geladen
> werden müssen.
Was heißt hier "Laden"? Wo kommen die Fonts überhaupt ursprünglich her? 
Wie wird dein uint8_t Array angelegt & befüllt?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Dr. Sommer schrieb:
> Was ist das denn für ein Argument o.O
In den Arrays sind die Farbinformationen in 4bits kodiert.

Dr. Sommer schrieb:
> Was heißt hier "Laden"?
Da habe ich mich wirklich blöd ausgedrückt. Die Fonts liegen im 
SPI-Flash und werden in den SDRAM kopiert. Der SPI-Flash hat dann seine 
Aufgabe erfüllt.

Dr. Sommer schrieb:
> Wo kommen die Fonts überhaupt ursprünglich her?
emWin.
EDIT: Um genau zu sein emWin FontCvt

Dr. Sommer schrieb:
> Wie wird dein uint8_t Array angelegt & befüllt?
Bisher hatte ich eine ganz andere Methode, bin aber gerade am Code 
überarbeiten. Da möchte ich nun eine gemütliche Methode haben um ganz 
easy neue Objekte in das Projekt einzufügen. Einzige Problematik die ich 
hier habe ist die Font-Klasse. Das liegt wahrscheinlich daran, dass ich 
den Wald vor lauter Bäumen nicht sehe. Mit Bitmaps ist es beispielsweise 
ganz einfach gelaufen, weil ich hier nur ein paar Variablen und die 
Farbinformation in einem Array habe.
Kurz und bündig: Also noch gar nicht. Ich kann die Daten im SDRAM 
natürlich so anordnen wie ich will. Ich versteh nur nicht, wie ich sie 
anordnen könnte, damit ein FontInfo* ausreicht, also ohne 
Initialisierung.
Hier ist so eine Font zu finden:
https://www.mikrocontroller.net/attachment/302236/font_aa_mssans12.cpp

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

OK, ich glaube ich komme auf den Trichter:
1
class FontInfo
2
{
3
    uint8_t charHeight;        // height in pixels
4
    uint8_t startChar;        // the first character in the font (e.g. in charInfo and data)
5
    uint8_t endChar;        // the last character in the font
6
    uint8_t spaceWidth;        // width, in pixels, of space character
7
    CharacterInfo charInfo[256];    // pointer to array of char information
8
}
9
10
int main()
11
{
12
    uint8_t* tmp = ReadData();
13
    FontInfo* ft = (FontInfo*)tmp;
14
}

Ich blick jetzt worauf das hinausläuft.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Reginald L. schrieb:
> Aber zur Lösung hast du, sofern ich mich recht erinnere, nicht
> beigetragen ;)

Als Pointerarithmetik betrachtet man das Rechnen mit Pointern. Du hast 
z.B. einen großen Puffer, in dem Text ist. Ein Pointer zeigt auf ein 
darin gefundendes Wort, ein anderer Pointer auf ein anderes darin 
gefundenes Wort. Mit der Subtraktion beider Pointer voneinander lässt 
sich die Anzahl der Zeichen zwischen dem Anfang des einen und dem Anfang 
des anderen Wortes bestimmen.

Das ist auch bei Pointern, die auf andere Dinge als Zeichen zeigen, 
möglich.

Der Puffer kann auch lauter double-Werte enthalten, und die Pointer 
seien vom Typ double*. Mit einer Subtraktion bestimmt man hier die 
Anzahl double-Werte zwischen den beiden Pointern.

Es ist ebenso legitim, zu einem Pointer einen Ganzzahlen-Wert zu 
addieren. Damit zeigt der Pointer (in einem Array o.ä.) auf das 
entsprechend vielste nachfolgende Element.

Bei einem Array aus doubles und einem Pointer (vom Typ double*) führt 
die Addition vom Wert 2 zum Pointer dazu, daß der Pointer auf den 
übernächsten Wert im Array zeigt.

Das funktioniert mit beliebigen Datentypen, also auch mit 
selbstdefinierten structs.

So etwas nennt man Pointerarithmetik.


> Wie wird der Adressraum hier definiert?

Das legt der verwendete nicht erwähnte Prozessor fest.

> Also der Compiler weiß
> eigentlich nicht ob das jetzt der SDRAM oder der SRAM ist. Ich kann ganz
> normal eine 32-bit Adresse angeben.

Und die gleiche Adresse (der numerische Wert) kann nicht sowohl RAM als 
auch SDRAM ansprechen? D.h. Du kannst durch Betrachten des numerischen 
Werts alleine erkennen, ob SRAM oder SDRAM gemeint sind?
Dann ist das ein gemeinsamer Adressraum,

> Und, wenn das selbstverständlich geht, verrätst du mir auch wie?

Na, einfach machen. Wo liegt da jetzt Dein Problem?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Rufus Τ. F. schrieb:
> Als Pointerarithmetik betrachtet man das Rechnen mit Pointern.
Ja, ich habs mir schon angeschaut. Mal abgesehen davon, dass ich hier 
Pointerarithmetik benutze (was natürlich nichts mit dem Thread-Thema zu 
tun hat):
Wollte halt als Maschinenbauer auch mal hier mit IT-Fachbegriffen 
rumschmeissen. Das war wohl ein Griff ins Klo, da hab ich mich vertan :) 
Ernsthaft: Schande über mich! Gibt nichts schlimmeres als der falsche 
Gebrauch von Fachbegriffen.

Rufus Τ. F. schrieb:
> Und die gleiche Adresse (der numerische Wert) kann nicht sowohl RAM als
> auch SDRAM ansprechen? D.h. Du kannst durch Betrachten des numerischen
> Werts alleine erkennen, ob SRAM oder SDRAM gemeint sind?
Jawohl.

Rufus Τ. F. schrieb:
> Das legt der verwendete nicht erwähnte Prozessor fest
> ...
> Dann ist das ein gemeinsamer Adressraum,
Ah, verstehe. Wie und wo er das festlegt zwar nicht, aber falscher 
Thread. Und zur Lösung meines Problems jetzt nicht sinnvoll :>

Rufus Τ. F. schrieb:
> Na, einfach machen. Wo liegt da jetzt Dein Problem?
Hast du den Threadtitel gelesen? Ach, wäre es möglich mir nach meinem 
Namen, wie bei dir, eine zusätzliche Info einzublenden? So wie bei dir 
"Moderator". Nur sollte da bei mir Maschinenbaustudent stehen. Dann 
hätte sich die Antwort auf deine Frage ergeben :>

Nee, im ernst, ich glaub der Groschen ist gefallen, siehe meinen 
vorherigen Beitrag.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Das blöde ist nur, dass ich die Größe der Arrays in den Klassen 
bestimmen muss, wenn ich das richtig verstehe.

Bäh, wäre ja fast schon besser einen eigenen Converter zu kreieren, der 
mir die von emWin ausgegebenen Font-Bitmaps in für mich passende Arrays 
konvertiert.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Reginald L. schrieb:
> Wie und wo er das festlegt zwar nicht,

Das ist eine Hardwareeigenschaft des Prozessors. Der anscheinend nach 
wie vor ungenannt bleiben muss.

Es gibt Prozessorarchitekturen, die RAM und ROM voneinander trennen, die 
hier beliebten AVRs von Atmel gehören dazu. Da wird von 
"Harvard-Architektur" gesprochen. Der Gegensatz (der das nicht trennt) 
wird"von-Neumann-Architektur" genannt.

Und es hätte ja sein können, daß Dein Prozessor das SDRAM per Software 
(Bitbanging über I/O-Ports) anspricht, ist ja nicht so, daß so etwas 
hier noch niemand gemacht hätte. Dann wären auch da die Adressräume 
sehr deutlich getrennt.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Rufus Τ. F. schrieb:
> a wird von "Harvard-Architektur" gesprochen. Der Gegensatz (der das
> nicht trennt) wird"von-Neumann-Architektur" genannt.
Aaah, das hab ich schon mal iwo gelesen.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Reginald L. schrieb:
> Das blöde ist nur, dass ich die Größe der Arrays in den Klassen
> bestimmen muss, wenn ich das richtig verstehe.

Worauf genau bezieht sich das jetzt?

von Eric B. (beric)


Lesenswert?

Rufus Τ. F. schrieb:
> Es gibt Prozessorarchitekturen, die RAM und ROM voneinander trennen, die
> hier beliebten AVRs von Atmel gehören dazu. Da wird von
> "Harvard-Architektur" gesprochen. Der Gegensatz (der das nicht trennt)
> wird"von-Neumann-Architektur" genannt.

Das ist so nicht ganz richtig. Der Unterschied liegt da drinnen, dass 
beim Harvard die Addressräume für Programm und Daten getrennt sind.

http://www.edgefxkits.com/blog/wp-content/uploads/Differences-between-Von-Neuman-Architecture-and-Harvard-Architecture.jpg

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Eric B. schrieb:
> Der Unterschied liegt da drinnen, dass beim Harvard die Addressräume für
> Programm und Daten getrennt sind.

Und das unterscheidet sich von meiner Formulierung "RAM und ROM trennen" 
jetzt wie?

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Rufus Τ. F. schrieb:
> Worauf genau bezieht sich das jetzt?
Naja, also ich habe die Klasse jetzt wie folgt deklariert:
1
  class FontInfo
2
  {
3
    // This structure describes a single character's display information
4
  public:
5
    class CharacterInfo
6
    {
7
    public:
8
      uint8_t widthpixel;      // width, in bits (or pixels), of the character
9
      uint8_t unused;        // emWin creates here an unused value
10
      uint8_t datawidth;      // width, in bytes of data bitmap
11
      uint8_t data[256];      // offset of the character's bitmap, in bytes, into the FONT's data array
12
    };
13
    // Describes a single font
14
    uint8_t fontName[256];        // Font name string
15
    uint8_t charHeight;        // height in pixels
16
    uint8_t startChar;        // the first character in the font (e.g. in charInfo and data)
17
    uint8_t endChar;        // the last character in the font
18
    uint8_t spaceWidth;        // width, in pixels, of space character
19
    CharacterInfo charInfo[256];  // pointer to array of char information
20
  };
Nun kann ich immerhin schonmal das hier machen um die ersten Werte zu 
füllen:
1
void blubb()
2
{
3
  FontInfo* ft = (FontInfo*)NewSDRamAdressToRead();
4
5
  // Font header
6
  ReadBytes(NewSDRamAdressToWrite(256), Descriptor.NewAddressToRead, 256);
7
  ReadBytes(NewSDRamAdressToWrite(4), Descriptor.NewAddressToRead, 4);
8
}
Mein Problem ist das Array "data" der einzelnen characters. Wie hier zu 
sehen ist 
https://www.mikrocontroller.net/attachment/302236/font_aa_mssans12.cpp, 
unterscheiden sich die Größen der characters. Ich weiß aber erst zur 
Programmlaufzeit wie groß die einzelnen characters sind und ich möchte 
den Heap nicht nutzen.

Hättest du da noch einen Denkanstoß?

: Bearbeitet durch User
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

https://www.mikrocontroller.net/attachment/302236/font_aa_mssans12.cpp

Nun, da hilft Dir möglicherweise tatsächlich die Pointeraritmethik.

Wenn Du Dir

Resource::FontInfo::CharacterInfo msSansSerif_12ptDescriptors[95]

ansiehst, da wird jedes einzelne Zeichen aufgeführt. Gehe das Array 
durch, bestimme die Adresse des Zeichens, und ziehe davon die Adresse 
des vorangehenden Zeichens ab, also z.B. von der Adresse von 
acGUI_Fontff_0021 die von acGUI_Fontff_0020.

Die Differenz sollte die Größe von acGUI_Fontff_0020 sein.

Das ist insgesamt eine eher ziemlich ungünstige Vorgehensweise. Ist 
diese Struktur so festgemeißelt?

Sonst würde ich CharacterInfo noch um ein Element namens "size" oder 
"bytecount" erweitern, und statt
1
   {   4,   4,  2, acGUI_Fontff_0020 } /* code 0020 */
das hier schreiben:
1
   {   4,   4,  2, acGUI_Fontff_0020, sizeof acGUI_Fontff_0020 } /* code 0020 */

Der sizeof-Operator liefert Dir die gewünschte Größe.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Rufus Τ. F. schrieb:
> Nun, da hilft Dir möglicherweise tatsächlich die Pointeraritmethik.
Supi, dann braucht man den Threadtitel nicht mehr ändern :>

Rufus Τ. F. schrieb:
> Resource::FontInfo::CharacterInfo msSansSerif_12ptDescriptors[95]
>
> ansiehst, da wird jedes einzelne Zeichen aufgeführt. Gehe das Array
> durch, bestimme die Adresse des Zeichens, und ziehe davon die Adresse
> des vorangehenden Zeichens ab, also z.B. von der Adresse von
> acGUI_Fontff_0021 die von acGUI_Fontff_0020.
>
> Die Differenz sollte die Größe von acGUI_Fontff_0020 sein.
Das mache ich eigentlich schon so die ganze Zeit, auch mit meinem alten 
Code. Mein Problem ist weniger das "wie bekomme ich die spezifischen 
Stellen zu den characters wieder", sondern das "wie sollte ich die 
Klassenmember deklarieren und wie weise ich die spezifischen Stellen im 
SDRAM diesen Membern korrekt zu". Mit meiner oben geposteten Methode 
gehts ja nicht, da ich "data" mit der Größe 256 deklariere und die 
einzelnen characters mal 20, mal 40, mal 60 usw. groß sind. Ich müsste 
so für diese Schriftart also die Größe von "data" auf die Größe des 
größten characters setzen und die Daten dementsprechend im SDRAM 
anordnen. Es sind allerdings noch andere Schriftarten vorhanden. Da kann 
die Größe eines Character-Arrays dann auf zb. 1300 wachsen. Das erzeugt 
dann einen doch zu großen overhead.
Also, wie kann ich sagen: "Weise ft->Name die Adresse 0xD000000 zu, 
ft->charHeigth 0xD03000000, ft->charinfo[3]->data 0xD03000001". Am 
liebsten wäre es mir, falls das überhaupt möglich ist, einen Pointer ft 
definieren und komplett initialisieren (bis hier her kein Problem), 
deren Member auf die von mir gewünschten Adressen zeigen. Das ganze am 
besten ohne Arrays, die Größen kenne ich ja beim kopieren der Daten vom 
SPI-Flash zum SDRAM.

Rufus Τ. F. schrieb:
> Das ist insgesamt eine eher ziemlich ungünstige Vorgehensweise. Ist
> diese Struktur so festgemeißelt?
Genau da liegt der Hund begraben. emWin erzeugt mir diese ganzen Arrays 
genau so. Daran kann ich nicht drehen. Deshalb habe ich vorher erwähnt, 
dass ich mir vielleicht einen Converter für Windows schreiben sollte, 
der mir die Arrays günstiger legt, weitere Gedanken hierzu habe ich mir 
allerdings nicht gemacht.


Ich habe versucht mich möglichst verständlich auszudrücken :>

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Vielleicht noch etwas, ab 0xD0000000 liegt ein string im SDRAM:
1
class FontInfo
2
{
3
    uint8_t* Name;
4
}
5
6
void blubb()
7
{
8
    FontInfo ft;
9
    ft.Name = (uint8_t*)0xD0000000;
10
}
ft.Name liefert mir dann natürlich die Adresse an der "0xD0000000"im 
µC-Flash liegt. Das versteh ich nicht, da Name ja als Zeiger deklariert 
wurde. Bei ft.Name = *(uint8_t*)0xD0000000 bekomme ich einen 
Compiler-Error.

Vielleicht macht das die ganze Sache auch etwas klarer.

EDIT:
Deklariere ich in der FontInfo-Klasse Name als non-Pointer, also 
"uint8_t Name", funktioniert das ganze. Aber hier habe ich ja das 
Problem, dass ich klare Angaben zur Array-Größe von "data" und 
"charInfo" machen muss (siehe weiter oben im Thread).

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Rufus Τ. F. schrieb:
> Eric B. schrieb:
>> Der Unterschied liegt da drinnen, dass beim Harvard die Addressräume für
>> Programm und Daten getrennt sind.
>
> Und das unterscheidet sich von meiner Formulierung "RAM und ROM trennen"
> jetzt wie?

Wie meinst du? Du wirst doch wissen, was der Unterschied zwischen "RAM" 
und "Datenspeicher" ist. Das sind doch Bezeichnungen auf ganz 
unterschiedlicher Ebene. RAM ist doch nicht automatisch Datenspeicher 
und ROM nicht automatisch Programmspeicher. Gerade dem AVR merkt man 
diesen Unterschied an. Bei ihm ist es als Trennung zwischen RAM und ROM 
ausgefürt. Da man aber auch den ROM als Datenspeicher nutzen kann, hat 
man keine saubere Trennung zwischen Programm- und Datenspeicher. Das ist 
ja auch gerade das, was bei der Programmierung des AVR in C immer zu 
Schwierigkeiten führt. Meist heißt es dann, das läge an der 
Harvard-Architektur und daren, dass C eigentlich nicht gut damit 
harmoniere.
Tatsächlich resultiert das Problem aber gerade daraus, dass der AVR eben 
keine reine Harvard-Architektur ist, weil man auch auf Daten im ROM 
zugreifen kann. Mit einer echten Harvard-Architektur hätte C überhaupt 
keine Probleme. Die ist sogar extra berücksichtigt.

: Bearbeitet durch User
von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Ich glaube (jaja, glauben kann man in der Kirche), dass ichs jetzt doch 
hinbekommen habe mit den Pointern:
1
  class FontInfo
2
  {
3
    // This structure describes a single character's display information
4
  public:
5
    class CharacterInfo
6
    {
7
    public:
8
      uint8_t widthpixel;      // width, in bits (or pixels), of the character
9
      uint8_t unused;        // emWin creates here an unused value
10
      uint8_t datawidth;      // width, in bytes of data bitmap
11
      uint8_t* data;        // offset of the character's bitmap, in bytes, into the FONT's data array
12
    };
13
    // Describes a single font
14
    uint8_t* fontName;        // Font name string
15
    uint8_t charHeight;        // height in pixels
16
    uint8_t startChar;        // the first character in the font (e.g. in charInfo and data)
17
    uint8_t endChar;        // the last character in the font
18
    uint8_t spaceWidth;        // width, in pixels, of space character
19
    CharacterInfo* charInfo;    // pointer to array of char information
20
  };
21
22
void blubb()
23
{
24
  Descriptor.NewAddressToRead = start_adr;
25
  FontInfo ft;
26
  FontInfo::CharacterInfo ci[256];
27
  ft.charInfo = ci;
28
29
  // Font header
30
  ReadBytes(NewSDRamAdressToWrite(256), Descriptor.NewAddressToRead, 256);
31
  ft.fontName = NewSDRamAdressToRead();
32
  ReadBytes(NewSDRamAdressToWrite(4), Descriptor.NewAddressToRead, 4);
33
  ft.charHeight = NewSDRamAdressToRead()[0];
34
  ft.startChar = NewSDRamAdressToRead()[1];
35
  ft.endChar = NewSDRamAdressToRead()[2];
36
  ft.spaceWidth = NewSDRamAdressToRead()[3];
37
38
  // Character headers
39
  uint8_t chars_to_read = ft.endChar - ft.startChar + 1;
40
  ReadBytes(NewSDRamAdressToWrite(chars_to_read), Descriptor.NewAddressToRead, chars_to_read);
41
  for (int i = 0; i < chars_to_read; i++)
42
    ft.charInfo[i].widthpixel = NewSDRamAdressToRead()[i];
43
  ReadBytes(NewSDRamAdressToWrite(chars_to_read), Descriptor.NewAddressToRead, chars_to_read);
44
  for (int i = 0; i < chars_to_read; i++)
45
    ft.charInfo[i].datawidth = NewSDRamAdressToRead()[i];
46
47
  // Character data
48
  uint8_t* chardata[256];
49
  uint32_t bytes_to_read = 0;
50
  for (int i = 0; i < chars_to_read; i++)
51
    bytes_to_read += ft.charHeight * ft.charInfo[i].datawidth;
52
  ReadBytes(NewSDRamAdressToWrite(bytes_to_read), Descriptor.NewAddressToRead, bytes_to_read);
53
  bytes_to_read = 0;
54
  for (int i = 0; i < chars_to_read; i++)
55
  {
56
    ft.charInfo[i].data = NewSDRamAdressToRead() + bytes_to_read;
57
    if (i == (chars_to_read - 1))
58
      break;
59
    bytes_to_read += ft.charHeight * ft.charInfo[i].datawidth;
60
  }
61
}

Das erzeugt mir einen overhead (für die Klasse selber und deren Member 
nehme ich mal an?) von knapp 600Byte. Ich verstehe nur nicht, warum ich 
ft in blubb() nicht als Pointer deklarieren bzw. definieren kann.

von Mark B. (markbrandis)


Lesenswert?

Reginald L. schrieb:
> Das erzeugt mir einen overhead (für die Klasse selber und deren Member
> nehme ich mal an?) von knapp 600Byte. Ich verstehe nur nicht, warum ich
> ft in blubb() nicht als Pointer deklarieren bzw. definieren kann.

Warum sollte das nicht gehen?

So liegt das Ganze auf dem Stack:
1
FontInfo ft;
2
FontInfo::CharacterInfo ci[256];
3
ft.charInfo = ci;

Und so würde es auf dem Heap liegen:
1
FontInfo *ft = new FontInfo;
2
FontInfo::CharacterInfo *ci = new FontInfo::CharacterInfo[256];
3
ft->charInfo = ci;

Ich gehe jetzt mal davon aus, dass Deine geheime Plattform das Anlegen 
von Objekten im Freispeicher ("auf dem Heap") unterstützt. Auf dem PC 
jedenfalls kompiliert dies tadellos.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Mark B. schrieb:
> Ich gehe jetzt mal davon aus, dass Deine geheime Plattform das Anlegen
> von Objekten im Freispeicher ("auf dem Heap") unterstützt.
Ja natürlich, allerdings war das nur ein Beispielcode. Es ist ein STM32, 
Heap soll nicht benutzt werden. Im Stack wird ft lustigerweise nacher 
auch nicht liegen :>
Ich habe offensichtlich eine Denkblockade gehabt: Ich muss ja irgendwo 
Speicher für ft oder *ft festlegen und zwar für die Member. Es wäre wohl 
durchaus möglich, nur mit FontInfo* ft, ohne zusätzliche 
Initialisierungen zu arbeiten, allerdings müssten dann die Größen des 
der Fonts und Characters alle gleich sein und diese müsste ich schon in 
der FontInfo Klasse festlegen. Da das hier nicht möglich ist, muss ich 
Speicherplatz für die Member zuweisen.

Auf jeden Fall habe ich mein Problem jetzt gelöst, zwar anders als 
gedacht, aber ich blicks jetzt wohl, warum es anders nicht geht.

von Student (Gast)


Lesenswert?

Reginald L. schrieb:
> Auf jeden Fall habe ich mein Problem jetzt gelöst, zwar anders als
> gedacht, aber ich blicks jetzt wohl, warum es anders nicht geht.

Ich blicke nicht warum du dir das Leben schwer machst.
"Der Heap ist verboten und der Stack auch". Der Heap löst dein Problem 
des "ft" der am Anfang nicht aufrufbar war.

Prinzipiell kannst du doch mit einem packed struct arbeiten.
Beispiel (Ich hab die C-Syntax nicht mehr so im Blut):
1
struct blah {
2
uint32_t a;
3
uint32_t b;
4
}

Wenn du jetzt zB hast: struct blah* ptr = 0xD70000; und rufst ptr->b 
auf, so wird von 0xD70004 gelesen. Damit sparst du dir irgendwelches 
unübersichtliches Gefrickel und alles liegt tatsächlich im ROM.

Der Vorteil hier ist auch, dass du nichts lädst sondern direkt darauf 
zugreifst. Der Overhead sind hier also uintptr_t Byte (4 Byte) an RAM.

Dein nächstes Problem mit der Größe und einem fixen Array ist wirklich 
ungeschickt. Da gibt es zum einen malloc/Heap (Der DAFÜR da ist und auch 
den Toten Speicher (256 - Größe Array) effizient weiterbenutzt) oder man 
greift in die Trickkiste:

Erweitern wir das struct. Nehmen wir an im Speicher liegt noch die Größe 
des Arrays (Die aber eigentlich nicht nötig ist), dann kann man 
folgendes tun:
1
struct blah {
2
uint32_t a;
3
uint32_t b;
4
uint32_t size;
5
uint8_t byteArray;
6
}

Jetzt musst du nur noch C weiß machen, dass an Adresse byteArray ein 
Array läge:
1
struct blah* ptr = 0xD70000;
2
((uint8_t*)(&ptr->byteArray))[30] = 0x0;

Das ist natürlich hacky und vielleicht geht auch ein 
(uint8_t[ptr->size]), weiß ich nicht.

Wenn die Daten nun aber nicht jedes Mal aus dem ROM gelesen werden 
sollen, dann solltest du wirklich den Heap nutzen.
Einfach uint8_t* byteArray = (uint8_t*)malloc(size);

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Student schrieb:
> Ich blicke nicht warum du dir das Leben schwer machst.
Sicher, es ginge auch einfacher. Aber ich möchte mir später nicht das 
Leben schwer machen, beim Einfügen neuer Objekte (in meinem Fall sind 
das ja Fonts, etc). Das funktioniert jetzt indem ich das Font als cpp 
ins Projekt einfüge und dann nur noch eine Klassenmethode aufrufe. Das 
beinhaltet halt, dass zur Compilelaufzeit die Größen der Objekte 
unbekannt sind.

Student schrieb:
> "Der Heap ist verboten und der Stack auch". Der Heap löst dein Problem
> des "ft" der am Anfang nicht aufrufbar war.
Du hast natürlich absolut recht, ich programmiere auch am PC, natürlich 
auch viel mit Heap. Aber hier ist es ein wenig komplizierter, schau mal 
hier:
Beitrag "STM32F4 GCC Linker section nur global?"

Student schrieb:
> Wenn du jetzt zB hast: struct blah* ptr = 0xD70000; und rufst ptr->b
> auf, so wird von 0xD70004 gelesen. Damit sparst du dir irgendwelches
> unübersichtliches Gefrickel und alles liegt tatsächlich im ROM.
Wie gesagt, die Größen der Arrays werden erst beim auslesen des 
SPI-Flashs bekannt.

Student schrieb:
> Jetzt musst du nur noch C weiß machen, dass an Adresse byteArray ein
> Array läge:
> struct blah* ptr = 0xD70000;
> ((uint8_t*)(&ptr->byteArray))[30] = 0x0;
>
> Das ist natürlich hacky und vielleicht geht auch ein
> (uint8_t[ptr->size]), weiß ich nicht.
Das die Größe des Arrays 30 beträgt, weiß ich ja nicht. Siehe oben.

Student schrieb:
> Wenn die Daten nun aber nicht jedes Mal aus dem ROM gelesen werden
> sollen, dann solltest du wirklich den Heap nutzen.
Ja, der Heap wäre hier die Lösung aller Probleme, da hast du recht :) 
Aber ich möchte mir, vor allem als Anfänger, keine weiteren Probleme 
dadurch einholen. Desweiteren funktioniert malloc() bzw. new in meinem 
Fall nicht im SDRAM. Da habe ich noch ein Problem mit den C-Libs und dem 
Provide-End im Linker.

Vielen Dank @all für eure Hilfestellungen!

von Eric B. (beric)


Lesenswert?

Rufus Τ. F. schrieb:
> Eric B. schrieb:
>> Der Unterschied liegt da drinnen, dass beim Harvard die Addressräume für
>> Programm und Daten getrennt sind.
>
> Und das unterscheidet sich von meiner Formulierung "RAM und ROM trennen"
> jetzt wie?

RAM und ROM bezieht sich auf Speichertyp, nicht auf der Adressraum. Ich 
könnte die Addressräume für Programm und Daten jeweils noch in ROM und 
RAM unterteilen.

Programm / ROM --> für normale Programmcode
Programm / RAM --> für sich zur laufzet ändernde Code (Hui!)

Daten / ROM --> Constanten
Daten / RAM --> Stack, Heap, BSS etc.

Wenn ich aber nur separate Adressräume für ROM und RAM habe, aber dann 
in beide Code und Daten abspeichern kann, habe ich kein 
Harvard-architektur mehr.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Eric B. schrieb:
> RAM und ROM bezieht sich auf Speichertyp, nicht auf der Adressraum.

Das ist zwar technisch korrekt, nützt aber bei der Betrachtung des 
vorliegenden Problems nichts.

Betrachtet man verbreitete µC-Systeme wie die 8-Bit-AVRs, dann sind 
"zufälligerweise" RAM und ROM deckungsgleich auf die für Daten und 
Programm separierten Adressräume abgebildet.

von Eric B. (beric)


Lesenswert?

Rufus Τ. F. schrieb:
> Betrachtet man verbreitete µC-Systeme wie die 8-Bit-AVRs, dann sind
> "zufälligerweise" RAM und ROM deckungsgleich auf die für Daten und
> Programm separierten Adressräume abgebildet.

Bein AVR ist das eben nicht ganz sauber den Fall, wie Rolf Magnus oben 
erwähnte:

Beitrag "Re: Anfänger C / C++ Pointerarithmetik"

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.