Forum: Mikrocontroller und Digitale Elektronik Verständnisproblem beim Schreiben ins Flash - Pointer, Adresse und Konsorten


von Fritz Box (Gast)


Lesenswert?

Hallo liebe Gemeinde!

Ich möchte gerne ins Flash meines Controllers schreiben und zwar während 
der Laufzeit, genauer genommen will ich einen Offset aufnehmen und 
diesen ins Flash schreiben.

Ich benutze einen MSP, aber es geht mehr um das generelle Verständnis. 
Ich habe ein Code-Example, an welches ich mich halte, würde es jedoch 
gerne auch komplett verstehen.

Die ganze Konfiguration des Flash-Controllers ist nicht von Bedeutung, 
ich werde es hier nicht anführen, da sich dann wahrscheinlich eh nur die 
MSP-Cracks angesprochen fühlen.

Ich gehe mal von eine Funktion zum Schreiben aus:
1
void write_segment (uint8_t value)
2
{
3
  uint8_t *flash_ptr;
4
5
  flash_ptr = (uint8_t *) 0x01000;
6
7
  *flash_ptr = 0;                    // Dummy-Write
8
9
  *flash_ptr = value;
10
}

Das habe ich jetzt mehr oder weniger abgeschrieben aus dem Example (eher 
mehr) - das ganze Gedöns drumherum mit warten ob busy und so mal nicht 
mit aufgeschrieben...so nun geht es mal wieder um die Pointer und 
Addressen:

Was genau mache ich hier:
1
uint8_t *flash_ptr;
Ich lege eine Variable fest, aber als Pointer. Was genau heisst das 
jetzt? Was ist genau der Unterschied ob mit oder ohne den Stern? Ich 
sage ja quasi dem Compiler, dass es sich hierbei um einen Zeiger 
handelt, nur in wie fern behandelt er ihn jetzt anders?

Die nächste Zeile:
1
flash_ptr = (uint8_t *) 0x01000;
0x01000 ist ja die Adresse, an die ich schreiben will. Wieso benutze ich 
für den flash_ptr jetzt nicht mehr den Stern? Und noch interessanter, 
was macht dieses (uint8_t *)? Das ist ja ein Cast, aber zu was caste ich 
die Adresse da jetzt?

Nun dies:
1
*flash_ptr = value;
Mit dem Stern sage ich, dass die Zuweisung, welche rechts nebem dem "=" 
steht, als Inhalt gewertet werden soll, oder - also quasi an die 
Flash-Pointer-Addresse den Wert von "value" ablege.

Und vor allem, wie würde es aussehen, wenn ich die Adresse mit übergeben 
wollen würde? Wie würde die in der Funktionszeile aussehen?
1
void write_segment (uint16_t *adress, uint8_t value)
2
{
3
  *adress = 0;
4
5
  *adress = value
6
}
Sehr kurz, oder?

Ich komme mit diesen Pointern nicht so wirklich klar, leider auch nicht 
mit literarischer Hilfe. Ich weiß nie, wo "*", wo "&" usw.

Wäre toll, wenn mir einer da mal helfen könnte.

von Fritz Box (Gast)


Lesenswert?

So, ich habe mir jetzt eine Funktion zum Schreiben und eine zum Lesen 
geschrieben - und: FUNKTIONIERT NATÜRLICH NICHT!

Schreiben:
1
void write_dword_flash (uint16_t *adress, int32_t value)
2
{
3
  uint8_t repeat;
4
  
5
  while (FCTL3 & BUSY) {};                 // Wait 
6
  
7
  FCTL3 = FWKEY;                           // Unlock
8
  FCTL1 = FWKEY | WRT;                     // Prepare to write a word
9
  
10
  for (repeat = 0; repeat < 4; repeat++)
11
  {
12
    *adress = 0;                         // Write dummy-bit
13
    *adress++ = (uint8_t) value;         // Copy value to flash
14
    value = (value >> 8);                // Copy value to flash
15
  }
16
  
17
  FCTL1 = FWKEY;                           // Clear WRT-bit
18
  FCTL3 = FWKEY | LOCK;                    // Set LOCK-bit
19
}

Lesen:
1
int32_t read_dword_flash (uint16_t *adress)
2
{
3
  uint8_t repeat;
4
  int32_t return_value = 0;
5
  
6
  while (FCTL3 & BUSY) {};                 // Wait
7
  
8
  for (repeat = 0; repeat < 4; repeat++)
9
  {
10
    return_value |= (uint8_t) *adress++;
11
    
12
    if (repeat < 3)
13
    {
14
      return_value = (return_value << 8);
15
    }
16
  }
17
  
18
  return (int32_t) return_value;
19
}

Und in der main:
1
void test_program (void)
2
{  
3
  int32_t test_snd_value = -698872;
4
  int32_t test_rcv_value = 15;
5
  uint16_t *adress = 0x01000;
6
  
7
  FCTL2 = FWKEY | FSSEL_2 | FN3;
8
  
9
  calib_menu_item = 0;
10
  
11
  while (1)
12
  {
13
    
14
    write_dword_flash (adress, test_snd_value);
15
    
16
    test_rcv_value = read_dword_flash (adress);
17
    
18
    
19
    while (1) {}
20
    
21
  }
22
}

Aber das haut so garnicht hin - der Compiler meckert wieder wegen 
Pointer-Fehlern.

Ich weiß einfach nicht, wo ich was übergeben muss - kann mir da einer 
helfen? Wo mit Stern und wo ohne?

FEHLER:

a value of type "int" cannot be used to initialize an entity of type 
"uint16_t *"

und das bezieht sich hier drauf:
1
uint16_t *adress = 0x01000;

von sunburner (Gast)


Lesenswert?

Was für einen MSP verwendest Du?

Mir scheint, Du solltest deine C-Kenntnisse was Pointer angeht deutlich 
verbessern. Mit welcher "literarischen Hilfe", bzw. Tutorials zu Zeigern 
kommst Du nicht klar?

von Thilo H. (thaala)


Lesenswert?

Das größte Problem im Verständnis ist beim Anfänger oft in der 
Deklaration des Pointers zu suchen.

Generell ist der Stern "*" ein Symbol für:
 "Gib mir den Wert des Speichers auf den der Pointer zeigt".
Ohne Stern ist gemeint:
 "Gib mir die Adresse auf die der Pointer zeigt.

Nicht in der Deklaration. Hier ist es einfach nur ein Merkmal für:
  "Nicht eine Variable, sondern ein Zeiger auf eine Variable"

In der kombinierten Deklaration/initialisierung schreibt man
uint16_t *adress = 0x01000;

Man sollte (als Anfänger, die Initialisierung von der Deklaration 
trennen!

Also besser schreiben:
uint16_t *adress; // lokale Variable die irgendwo hin zeigt.
adress = 0x01000; // Hier wird die Variable initialisiert.

Sie zeigt ab jetzt auf den Speicher an Adresse 0x01000;

mit *adress = 20; werden an Speicherstelle 0x01000 und 0x01001 Daten 
gespeichert die die Zahl 20 repräsentieren.

wäre es ein Pointer vom Typ uint32_t würde die Zahl 20 and die 
Speicherstellen 0x01000 - 0x01003 gespeichert die die Zahl 20 
repräsentieren.

Das ist der Grund warum man dem Pointer überhaupt einen Typ mitgibt.

Noch ein Beispiel:

Was ist das hier ?:

uint16_t* adress, lalla;

Nein das sind nicht zwei Pointer! adress ist ein Pointer, während lalla 
eine simple Variable vom Typ uint16_t ist.

Ich hoffe es hilft ein Bischen,

Gruß THaala

von Stephan (Gast)


Lesenswert?

Hi,
schau dir mal das an:
1
// schreiben
2
for (repeat = 0; repeat < 4; repeat++)
3
  {
4
    *adress = 0;                         // Write dummy-bit
5
    *adress++ = (uint8_t) value;         // Copy value to flash
6
    value = (value >> 8);                // Copy value to flash
7
  }
8
9
// lesen
10
for (repeat = 0; repeat < 4; repeat++)
11
  {
12
    return_value |= (uint8_t) *adress++;
13
    
14
    if (repeat < 3)
15
    {
16
      return_value = (return_value << 8);
17
    }
18
  }

wenn du bei value= 0xaabbccdd einsetzt!

0x1000 dd
0x1001 cc
0x1002 bb
0x1003 aa

und beim lesen:

1. return_value= 0xdd
2. return_value= 0xdd cc
3. return_value= 0xdd cc bb
4. return_value= 0xdd cc bb aa

nimm mal öfters Bleistift und Papier zu Hand und teste deinen Code.
Das hilft.

mfg
Stephan

von Thilo H. (thaala)


Lesenswert?

Nachtrag:

Dein Compiler fehler.....

>> a value of type "int" cannot be used to initialize an entity of type
>> "uint16_t *"

>> und das bezieht sich hier drauf:

>> uint16_t *adress = 0x01000;

... wird mit folgender Modifikation verschwinden:

uint16_t *adress;
adress = (uint16_t *)0x01000;

Gruß THaala

von Fritz Box (Gast)


Lesenswert?

So, da melde ich mich auch mal wieder zurück.

sunburner schrieb:
> Mir scheint, Du solltest deine C-Kenntnisse was Pointer angeht deutlich
> verbessern. Mit welcher "literarischen Hilfe", bzw. Tutorials zu Zeigern
> kommst Du nicht klar?

Also ich habe mir schon alles Mögliche zu Pointern durchgelesen, aber 
allein vol Lesen halten sich meine Erfolge in Grenzen - der praktische 
Erfolg wäre lehrreicher für mich :-) Trotzdem, ich arbeite mit dem Buch 
von Kernigham und Ritchie.

sunburner schrieb:
> Was für einen MSP verwendest Du?

Einen F2418.

Thilo Haala schrieb:
> Also besser schreiben:
> uint16_t *adress; // lokale Variable die irgendwo hin zeigt.
> adress = 0x01000; // Hier wird die Variable initialisiert.
>
> Sie zeigt ab jetzt auf den Speicher an Adresse 0x01000;
>
> mit *adress = 20; werden an Speicherstelle 0x01000 und 0x01001 Daten
> gespeichert die die Zahl 20 repräsentieren.
>
> wäre es ein Pointer vom Typ uint32_t würde die Zahl 20 and die
> Speicherstellen 0x01000 - 0x01003 gespeichert die die Zahl 20
> repräsentieren.

Das macht Sinn. Kurze und gute Erklärung :-)
Da ich ja bei mir die Adresse im Flash beschreiben will, werde ich also 
nach diesem Schema den Pointer initialisieren. Also erst
1
uint16_t *adress;
um ihm zu sagen, dass es einen Zeiger auf einen Speicherbereich gibt, 
welcher 2 Bytes lang ist und dann weiterhin mit
1
adress = 0x01000;
zur Kenntnis gebe, dass die zwei Bytes ab 0x01000 gemeint sind.

Thilo Haala schrieb:
> uint16_t* adress, lalla;

Dass das zwei verschiedene Sachen sind, habe ich auch gelesen.

Stephan schrieb:
> wenn du bei value= 0xaabbccdd einsetzt!
>
> 0x1000 dd
> 0x1001 cc
> 0x1002 bb
> 0x1003 aa
>
> und beim lesen:
>
> 1. return_value= 0xdd
> 2. return_value= 0xdd cc
> 3. return_value= 0xdd cc bb
> 4. return_value= 0xdd cc bb aa

OK, stimmt, wenn ich mal drüber nachdenke, dann hast du natürlich recht, 
alles falsch herum zurückgeholt.

Thilo Haala schrieb:
> Dein Compiler fehler.....
>
>>> a value of type "int" cannot be used to initialize an entity of type
>>> "uint16_t *"
>
>>> und das bezieht sich hier drauf:
>
>>> uint16_t *adress = 0x01000;
>
> ... wird mit folgender Modifikation verschwinden:
>
> uint16_t *adress;
> adress = (uint16_t *)0x01000;

Das verstehe ich wieder nicht! Weiter oben wurde es ja so geschrieben:
1
uint16_t *adress; // lokale Variable die irgendwo hin zeigt.
2
adress = 0x01000; // Hier wird die Variable initialisiert.

Aber da hat der Compiler ja gemeckert.  Was bewirkt also dieses 
vorangestellte, in Klammern gesetzte und wiederholte "adress = (uint16_t 
*) 0x01000"?

von Fritz Box (Gast)


Lesenswert?

Morgen und schönen Sonntag!

Ich wollte den Thread hier nochmal hochholen - sorry, aber ich wär echt 
dankbar, wenn mir einer das erklären könnte.

Danke schön!

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Fritz Box schrieb:
> Aber da hat der Compiler ja gemeckert.  Was bewirkt also dieses
> vorangestellte, in Klammern gesetzte und wiederholte "adress = (uint16_t
> *) 0x01000"?

Wie der Compiler ja schon freundlicherweise mitgeteilt hat, entspricht 
der Ausdruck 0x01000 eben keinem Datentyp, der äquivalent zu einem 
Pointerinhalt ist.

Folglich muss er, bevor er einem Pointer zugewiesen werden kann, in 
einen Datentyp konvertiert werden, der zum Pointer kompatibel ist. Am 
besten sind natürlich gleiche Typen.

Denkfaule schreiben bei so etwas natürlich lieber (void *)0x01000, weil 
das für alle Pointer-Typen passt, aber das solltest Du vorerst 
vermeiden.

von Fritz Box (Gast)


Lesenswert?

Andreas Schweigstill schrieb:
> Folglich muss er, bevor er einem Pointer zugewiesen werden kann, in
> einen Datentyp konvertiert werden, der zum Pointer kompatibel ist. Am
> besten sind natürlich gleiche Typen.

Aber ich habe doch vorher mit "uint16_t *adress;" gesagt, dass ich einen 
unsigned int, also einen Wert zwischen 0 und 65535 habe.

Wenn ich jetzt hexadezimal 0x01000 zuweise, dann entspricht das doch 
4096. Wieso passt das dann nicht, bzw. wieso muss die 4096 erst nochmals 
mit (uint16_t *) initialisiert werden. Und vorallem wieso nochmal mit 
Stern. Ich schreibe ja eigentlich nicht den Inhalt der Variable an der 
Stelle, sondern die Addresse.

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Fritz Box schrieb:
> Aber ich habe doch vorher mit "uint16_t *adress;" gesagt, dass ich einen
> unsigned int, also einen Wert zwischen 0 und 65535 habe.

Nein, das hast Du nicht getan!

Du hast eine Variable definiert, die auf eine Speicherstelle zeigt, in 
der ein uint16_t abgelegt werden kann.

von Fritz Box (Gast)


Lesenswert?

Und warum der Cast mit erneutem Stern?

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Lies Dir noch einmal ein paar Bücher über C durch. Und anschließend noch 
einmal. Und noch einmal.

Probiere ein bisschen herum. Studiere die Beispiele der Lehrbücher.

Verwende anfangs keinen Microcontroller, sondern arbeite auf dem 
Hostsystem, egal ob Windows, Linux, BS/2000 oder VMS.

Es macht doch allmählich keinen Sinn mehr, die Diskussion 
weiterzuführen. Du hast hinreichend viele Hinweise erhalten. Wenn diese 
Hinweise nicht ausreichen und Du nicht in der Lage bist, den 
Lehrinhalten und Hinweisen zu folgen, ist Softwareentwicklung nicht die 
richtige Beschäftigung für Dich.

von Fritz Box (Gast)


Lesenswert?

Andreas Schweigstill schrieb:
> und Du nicht in der Lage bist, den
> Lehrinhalten und Hinweisen zu folgen, ist Softwareentwicklung nicht die
> richtige Beschäftigung für Dich

Na herzlichen Dank! Nur weil ich was nicht direkt verstehe - du hast es 
garantiert von Anfang an gekonnt. Hätte ich eine gute Erklärung, dann 
wäre es ein für alle mal vom Tisch.

Tut mir Leid, wenn ich Leute damit nerve, weil es für diejenigen 
langweilig und trivial ist, ich bin halt noch nicht so weit und zu 
denjenigen, denen das Programmieren in die Krippe gelegt wurde gehöre 
ich leider nicht. Du scheinst ja schon eine Firma zu haben, also auch 
schon etwas länger dabei zu sein - natürlich kannst du dann mehr.

Ich wette, du kannst auch manche Sachen nicht, wäre mal interessant, ob 
du auch jedesmal so abgespeist wirst.

Danke danke!

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.