PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Wie einen Datensatz in C passend ablegen?



the_Ghost666
11.05.2008, 18:19
Moin zusammen.
Ich möchte eine Datenstruktur in einem Atmega168 verwenden, es geht dabei um Sternenkarten, die ich nach der Form "1. Byte Index, 2.-21. Byte Name, 22.-38. Byte Daten" aufgebaut sind, es gibt ingesamt 22 Einträge, die ich am liebsten mit einer Variable zugreifen will. Zuerst hab ich an ein Array einer Struktur gedacht sozusagen:
typdef struct Karten{...};
Karten Sternbilder[22];
Dabei machte mir der GCC aber irgendwelche Striche durch die Rechnung. Dann wollte ich es als Array mit 22x37 Zeichen speichern, sodass ich die Karte mit der X-Koordinate auswähle, das Objekt dann mit der Y-Koordinate.
Das ganze sind Konstanten, ich benötige noch 2 dieser Strukturen als Variablen (ein Abbild der angezeigten Karte, ein Buffer).
Die Variablen lege ich natürlich im RAM ab, aber was mache ich am besten mit den Konstanten? Das EEPROM ist für den kompletten Datensatz zu klein, würde ich alles in den RAM legen und initialisieren lassen, wäre der schon damit zu 76% belegt.
Wie kann ich sowas schön im Flash ablegen und zugreifen?
Per Assembler wäre das kein Problem, damit weiß ich, was ich machen muss. Aber in C fehlt mir der beste Ansatz.
MfG
Björn

SprinterSB
11.05.2008, 19:33
Was in der Richtung?


#include <avr/pgmspace.h>

typedef struct
{
foo1_t foo1;
foo2_t foo2;
...
} karte_t;


const karte_t karten[] PROGMEM =
{
{ .foo1 = ..., .foo2 = ...},
{ .foo1 = ...},
...
}

karte_t k_buf;

void foo (void)
{
uint8_t i;
for (i=0; i < sizeof (karten) / sizeof (karte_t); i++)
{
memcpy_P (& k_buf, & karten[i], sizeof (karte_t));
}
}

the_Ghost666
11.05.2008, 20:48
Ah, ich glaub schon. Eine bessere Lösung gibts wohl nicht? Das einzige, was mich stört, ist, dass man ne spezielle Funktion braucht um die Werte abzuholen, aber das ist ja kein Problem. Ich hab parallel das Tutorial bei Mikrocontroller.net gefunden, in dem die es auch so machen. kannst du das Initialisieren der const struct karten[] nochmal genauer erläutern? das macht dann der Compiler vor dem brennen, nicht das Programm richtig? Ich hatte mit der Initialisierung des Structs Probleme.

the_Ghost666
11.05.2008, 22:07
Ok, das hab ich soweit hinbekommen, noch eine Frage:


typedef struct
{
unsigned char Name[Name_Length];
unsigned char Bitfield[Daten_Length];
} Daten;

Daten Buffer,Image;

Wie weise ich einem Element vom typ struct an der stelle Name einen String zu, bzw eine Reihe von Chars. Bei der Initialisierung hab ich das geschaft, aber nun möchte ich
Destination.Name[]="Zuweisungstext";
machen. Er sagt mir ../Sternenhimmel.c:92: error: incompatible types in assignment
als Fehler.
Wie bekomme ich sonst so eine Zuweisung? ich möchte ungern 20 Zuweisungen hintereinander schreiben, nur wenn nicht anders geht.


Ich habe mir nun nach etwas Probieren eine weitere const char[] deklariert, die im Flash liegt und nun einfach per for-Schleife zugewiesen wird.

SprinterSB
12.05.2008, 09:48
Der Initializer enthält für jede Komponente einen Wert, wobei Unterkomonenten in {} gefasst werden. Zusätzlich kann man mit .name=...explizit eine Komponente nennen, ist aber "nur" GNU-C und kein ANSI-C. Ansonsten müssen die Initializer in der richtigen Reihenfolge stehen. Inits in einem Array kann man auch angeben mit zB [0]=...

Strings zu initialisieren ist etwas lästig wenn sie im Flash liegen sollen, weil das Makro PSTR eine lokale Variable braucht.

Beispiele sind etwa in https://www.roboternetz.de/wissen/index.php/Avr-gcc#Sprungtabelle

Die Daten, die im Flash bleiben, erhalten das Attribut progmem und landen in der speziellen Section .progmem, die vom Startup-Code der vor main() läuft ins RAM kopiert werden.

https://www.roboternetz.de/wissen/index.php/Avr-gcc/Interna#Sections

the_Ghost666
12.05.2008, 10:19
Moment, würde das nicht bedeuten, dass diese Geschichte doch wieder den SRAM zumüllt? Der Compiler zeigt aber keinen gestiegenen Bedarf an.
Das Verhalten hab ich gesehen, wenn ich eine normale Variable initialisiert habe, also das erscheinen im SRAM, mit der PROGMEM-Attribut passiert das nichtmehr.

SprinterSB
12.05.2008, 15:32
ah sorry, fehler meinerseits. Das so besprochene initialisieren .pgmspace -> .data betrifft natürlich nur normale variablen.

the_Ghost666
22.05.2008, 17:40
Moin, hier nochmal ein Nachsatz:
Ich habe nun einen Quellcode geschrieben und in mein Hauptprogramm eingebunden, in der Header-Datei steht


#define Name_Length 20 //Länge des Namen der Sternbildstruktur
#define Daten_Length 16 //Länge der Bitfield-Variablen der Sternbildstruktur

//Struktur der Sternenbilder
typedef struct
{
unsigned char Name[Name_Length];
unsigned char Bitfield[Daten_Length];
} Daten;

//Hilfsvariablen, Buffer zum Arbeiten, Image als Kopie des Hardwarezustands
Daten Buffer;
Daten Image;

Ich habe nun eine Funktion geschrieben, die aus einer anderen Struktur im Flash die Daten in eine Variable vom Typ Daten schreiben soll:

//Fügt die Konstanten für das Sternbild an stelle Source_Address in die Variable Destination ein
void Set_Image (volatile Daten Destination, unsigned char Source_Address)
{
unsigned char i=0;

for (i=0;i<Name_Length;i++)
{
Destination.Name[i] = pgm_read_byte(&Sternbilder[Source_Address].Name[i]);
}
for (i=0;i<Daten_Length;i++)
{
Destination.Bitfield[i] = pgm_read_byte(&Sternbilder[Source_Address].Bitfield[i]);
}
}


Wenn ich das nun im Hauptprogramm so aufrufe:
Set_Image(Image,0);
Set_Image(Buffer,3);
Dann läuft das erste schon richtig ab, aber er schreibt an der zweiten Stelle in den gleichen Speicherbereich, als ob Image und Buffer identisch wären!

Woran könnte das liegen bzw wie bekomm ichs weg?

Mir fiel noch auf: Halte ich den Cursor über dem Funktionsnamen, so blendet AVRStudio ein, dass Image die SRAM -Adresse 0x101 besitzt, Buffer 0x125. Die Daten werden jedoch im SRAM ab der Stelle 0x4DA abgelegt, und dort auch überschrieben. Der Inhalt der SRAM -Variablen 0x101-0x125 ist und bleibt 0

SprinterSB
22.05.2008, 22:32
Destination wird per Wert übergeben, du schreibst nur in eine lokale Kopie.


void Set_Image (volatile Daten * dest, unsigned char Source_Address)
{
unsigned char i=0;

for (i=0;i<Name_Length;i++)
{
dest->Name[i] = pgm_read_byte(&Sternbilder[Source_Address].Name[i]);
}
for (i=0;i<Daten_Length;i++)
{
dest->Bitfield[i] = pgm_read_byte(&Sternbilder[Source_Address].Bitfield[i]);
}
}

the_Ghost666
22.05.2008, 22:52
Ah, sowas hab ich mir schon gedacht, hab auch einiges probiert um das zu lösen. Übergabe von &Image und dann auch * im Prototyp, allerdings kannte ich den -> operator nicht, was ist der unterschied zu dem und meine *dest.Name[i]??
Kannst du mir das erläutern?
Schonmal besten Dank, klappt jetzt endlich

Wenn ichs richtig verstehe, dann kann ich pointer auf variablen zeigen lassen,
z.b. eine Variable vom Typ int oder Typ struct Daten.
Während ich bei nem Pointer auf ne einfache Variable direkt auf den Wert zugreifen kann, muss ich das element bei einer komplexeren Variable, meiner Struct, erst noch mit dem Pfeil eindeutig identifizieren.. dh ich benutze * um mein Array aus Structs nach einer bestimmten zu durchsuchen und dann -> um das Element der per Pointer ausgewählten Struct zu nutzen. richtig?

SprinterSB
23.05.2008, 00:17
Ja

foo-> ist gleichbedeutend mit (*foo). aber besser lesbar, vor allem bei Mehrfach-Dereferenzierungen.

Ist aber keine Geheimwissenschaft das... ;-)

the_Ghost666
01.06.2008, 19:34
Moin, nochmal ein Nachtrag:
Das klappt soweit alles wunderbar.
Die Definition der Struktur steht in Header A

typedef struct
{
unsigned char Name[Name_Length];
unsigned char Bitfield[Daten_Length];
} Daten;

Heute hab ich ein weiteres Source- und Headerfile angelegt, in dem eine Funktion einen Pointer vom Typ Daten erwarten, so wie auch schon eine Funktion aus Header A.
In Header B produziert er mir nun aber nen Fehler,

../pca-routines.h:48: warning: type defaults to 'int' in declaration of 'Daten'
und
../pca-routines.h:48: error: expected ';', ',' or ')' before '*' token

wobei die Funktion
void PCA_Table (volatile Daten * Source)
{
char temp=0;
...
}
ist und einen solchen protoypen in Header B hat.
//Create Table from Image
void PCA_Table (volatile Daten * Source);

Ich habe auch schon "extern" versucht, brachte aber auch keine Lösung. Bei der Einbindung des Headers A in Header B oder Source B, gibts Fehler wegen Doppeldefinition..

Weiß jemand wo das Problem liegen könnte?

sternst
10.07.2008, 23:38
Natürlich muss auch in Header B das typedef bekannt sein (was es bei dir offensichtlich nicht ist). Also entweder schreibst du auch dort die komplette typedef-Anweisung rein (nicht so gut, weil du dann bei Änderungen an mehreren Stellen was ändern musst), oder du inkludierst Header A in Header B (auch nicht wirklich das Wahre), oder du packst das typedef in eine separate Header-Datei (z.B. types.h oder global.h) und inkludierst diese dann sowohl in A wie auch in B (wohl die beste Lösung).