Forum: Mikrocontroller und Digitale Elektronik Probleme beim Schreiben in den Flash-Speicher


von jacky (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei einen Bootloader zu schreiben. Um erst einmal zu 
lernen man mit dem Flash-Speicher umgeht, habe ich folgende Funktion im 
Bootloader, die eine Test-Applikation (LED blinken lassen) in den 
Anwendungs-Speicher-Bereich des Flash schreiben soll:
1
void write_app()
2
{
3
  int data[] = 
4
  {
5
    0x12C0,0x19C0,0x18C0,0x17C0,0x16C0,0x15C0,0x14C0,0x13C0,
6
    0x12C0,0x11C0,0x10C0,0x0FC0,0x0EC0,0x0DC0,0x0CC0,0x0BC0,
7
    0x0AC0,0x09C0,0x08C0,0x1124,0x1FBE,0xCFE5,0xD4E0,0xDEBF,
8
    0xCDBF,0x02D0,0x1FC0,0xE4CF,0x84B3,0x8462,0x84BB,0xAA98,
9
    0xA09A,0x8A9A,0xA898,0x84B3,0x8861,0x84BB,0xAB98,0x28EC,
10
    0x30E0,0xAD9A,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
11
    0xD9F7,0xAD98,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
12
    0xD9F7,0xEFCF,0xF894,0xFFCF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
13
  };
14
15
  cli();
16
  char i = 0;
17
  char n = 0;
18
  for (i = 0; i < 4; i++)
19
  {
20
    boot_spm_busy_wait();
21
    for (n = 0; n < 32; n+=2)
22
    {
23
      boot_page_fill(i * 32 + n, data[32 * i + n]);
24
    }
25
    boot_page_write(i);
26
  }
27
  sei();
28
}

Wenn ich allerdings den Flash auslese, nachdem der Bootloader das 
Programm geschrieben hat, erhalte ich Folgendes:
1
:100000000000400040000014001200100006000430
2
:100010000002000000054004038DC012800080042F
3
:10002000800080004000800AC000C0000008B611B7
4
:10003000E0D0AD800019B380B38090A098A861880B
5
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
6
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
7
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
8
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

anstelle von
1
:1000000012C019C018C017C016C015C014C013C044
2
:1000100012C011C010C00FC00EC00DC00CC00BC06C
3
:100020000AC009C008C011241FBECFE5D4E0DEBF5E
4
:10003000CDBF02D01FC0E4CF84B3846284BBAA9832
5
:10004000A09A8A9AA89884B3886184BBAB9828EC5C
6
:1000500030E0AD9A80ED97E0F9013197F1F7019723
7
:10006000D9F7AD9880ED97E0F9013197F1F7019755
8
:10007000D9F7EFCFF894FFCFFFFFFFFFFFFFFFFFA0
(So sieht es aus, wenn ich das Test-Progamm über den ISP-Programmierer 
aufspiele und anschließend den Flash-Speicher auslese)

von jacky (Gast)


Lesenswert?

Es handelt sich doch sicher hierbei um ein Adressen-Problem.

Dem Profi dürfte hierbei kaum eine Minute abverlagt werden.

von Stefan E. (sternst)


Lesenswert?

1) boot_page_write will eine richtige Adresse haben, und keine 
"Page-Nummer".

2) Du greifst nur auf jedes zweite Word im Array zu.

von jacky (Gast)


Lesenswert?

Erst einmal Danke, Stefan. :-)

Was für eine Adresse will boot_page_write(...) denn haben? Eine Byte- 
oder eine Word-Adresse? Geschrieben wird ja immer ein Word, wenn ich das 
richtig sehe.

von Stefan E. (sternst)


Lesenswert?

jacky schrieb:

> Was für eine Adresse will boot_page_write(...) denn haben? Eine Byte-
> oder eine Word-Adresse?

Byte-Adresse.

von jacky (Gast)


Lesenswert?

Ok,

ich habe meinen Code entsprechend geändert:
1
void write_app()
2
{
3
  int data[] = 
4
  {
5
    0x12C0,0x19C0,0x18C0,0x17C0,0x16C0,0x15C0,0x14C0,0x13C0,
6
    0x12C0,0x11C0,0x10C0,0x0FC0,0x0EC0,0x0DC0,0x0CC0,0x0BC0,
7
    0x0AC0,0x09C0,0x08C0,0x1124,0x1FBE,0xCFE5,0xD4E0,0xDEBF,
8
    0xCDBF,0x02D0,0x1FC0,0xE4CF,0x84B3,0x8462,0x84BB,0xAA98,
9
    0xA09A,0x8A9A,0xA898,0x84B3,0x8861,0x84BB,0xAB98,0x28EC,
10
    0x30E0,0xAD9A,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
11
    0xD9F7,0xAD98,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
12
    0xD9F7,0xEFCF,0xF894,0xFFCF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
13
  };
14
15
  cli();
16
  char i = 0;
17
  for (i = 1; i < 128; i+=2)
18
  {
19
20
    boot_page_fill(i, data[i/2]);
21
  }
22
  boot_spm_busy_wait();
23
  boot_page_write(0);
24
  boot_page_write(32);
25
  boot_page_write(64);
26
  boot_page_write(96);
27
28
  sei();
29
}

leider enthält die Hex-Datei jetzt folgenden (falschen) Inhalt:
1
:100000008000800880088004400080048000C000D8
2
:10001000C0108001C000C00700088001C00080013E
3
:10002000C0088009C000201100198501E0D09700A8
4
:10003000B7C9C0028018CFE4B3846284BB8498AA95
5
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
6
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
7
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
8
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

von Stefan E. (sternst)


Lesenswert?

Du kannst nicht erst alles füllen und dann alles schreiben. Oder 
erwartest du tatsächlich, dass es einen Zwischenpuffer in der vollen 
Größe des Flash gibt?

von jacky (Gast)


Lesenswert?

Ich bin am verzweifeln,

diese Version funktioniert auch nicht:
1
void write_app()
2
{
3
  int data[] = 
4
  {
5
    0x12C0,0x19C0,0x18C0,0x17C0,0x16C0,0x15C0,0x14C0,0x13C0,
6
    0x12C0,0x11C0,0x10C0,0x0FC0,0x0EC0,0x0DC0,0x0CC0,0x0BC0,
7
    0x0AC0,0x09C0,0x08C0,0x1124,0x1FBE,0xCFE5,0xD4E0,0xDEBF,
8
    0xCDBF,0x02D0,0x1FC0,0xE4CF,0x84B3,0x8462,0x84BB,0xAA98,
9
    0xA09A,0x8A9A,0xA898,0x84B3,0x8861,0x84BB,0xAB98,0x28EC,
10
    0x30E0,0xAD9A,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
11
    0xD9F7,0xAD98,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
12
    0xD9F7,0xEFCF,0xF894,0xFFCF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
13
  };
14
15
  cli();
16
  char i = 0;
17
  for (i = 0; i < 128; i+=2)
18
  {
19
20
    boot_page_fill(i, data[i/2]);
21
    if (i % 32 == 0 && i > 0)
22
    {
23
      boot_page_write(i / 32);
24
    }
25
    boot_spm_busy_wait();
26
  }
27
  
28
  sei();
29
}

Ergebnis:
1
:100000008000800880088004400080048000C000D8
2
:10001000C0108001C000C00700088001C00080013E
3
:10002000C008C009C0082411BE1FE5CFE0D4BFDE60
4
:10003000BFCDD002C01FCFE4B3846284BB8498AA32
5
:10004000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC0
6
:10005000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFB0
7
:10006000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA0
8
:10007000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF90

von Stefan E. (sternst)


Lesenswert?

Jetzt hast du beim page_write ja wieder eine "Page-Nummer". Und warum 
eigentlich immer "32"? Deine Rückleseversuche legen eigentlich nahe, 
dass eine Page 64 Bytes hat.

von jacky (Gast)


Lesenswert?

Ok,

jetzt habe ich nochmal auf Deinen Rat hin den Code geändert:
1
void write_app()
2
{
3
  int data[] = 
4
  {
5
    0x12C0,0x19C0,0x18C0,0x17C0,0x16C0,0x15C0,0x14C0,0x13C0,
6
    0x12C0,0x11C0,0x10C0,0x0FC0,0x0EC0,0x0DC0,0x0CC0,0x0BC0,
7
    0x0AC0,0x09C0,0x08C0,0x1124,0x1FBE,0xCFE5,0xD4E0,0xDEBF,
8
    0xCDBF,0x02D0,0x1FC0,0xE4CF,0x84B3,0x8462,0x84BB,0xAA98,
9
    0xA09A,0x8A9A,0xA898,0x84B3,0x8861,0x84BB,0xAB98,0x28EC,
10
    0x30E0,0xAD9A,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
11
    0xD9F7,0xAD98,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
12
    0xD9F7,0xEFCF,0xF894,0xFFCF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
13
  };
14
15
  cli();
16
  char i = 0;
17
  for (i = 0; i < 128; i+=2)
18
  {
19
20
    boot_page_fill(i, data[i/2]);
21
    if (i % 64 == 0 && i > 0)
22
    {
23
      boot_page_write(i-64);
24
    }
25
    boot_spm_busy_wait();
26
  }
27
  boot_page_write(128-64);
28
  boot_spm_busy_wait();
29
30
  sei();
31
}

Ergebnis: Falscher Flash-Inhalt. :-(
1
:100000008000C019C018C017C016C015C014C01396
2
:10001000C012C011C010C00FC00EC00DC00CC00B6C
3
:10002000C00AC009C0082411BE1FE5CFE0D4BFDE5E
4
:10003000BFCDD002C01FCFE4B3846284BB8498AA32
5
:10004000FFFF9A8A98A8B3846188BB8498ABEC2898
6
:10005000E0309AADED80E09701F99731F7F1970123
7
:10006000F7D998ADED80E09701F99731F7F1970155
8
:10007000F7D9CFEF94F8CFFFFFFFFFFFFFFFFFFFA0

von Stefan E. (sternst)


Lesenswert?

jacky schrieb:

> jetzt habe ich nochmal auf Deinen Rat hin den Code geändert:

Und mittlerweile bist du bei einer etwas kruden Struktur angekommen. 
Warum hast du nicht die Struktur deiner ersten Version beibehalten?

> Ergebnis: Falscher Flash-Inhalt. :-(

Ja, die ersten beiden Bytes beider Pages sind falsch. Und ehrlich gesagt 
sehe ich so auf Anhieb nicht, warum. Vielleicht ein merkwürdiger 
Seiteneffekt des unnötigen boot_spm_busy_wait() nach jedem 
boot_page_fill()? (kann ich mir aber eigentlich nicht vorstellen)

von jacky (Gast)


Lesenswert?

Es ist mir etwas unangehm, ja fast peinlich, dich zu bitten, nachdem Du 
mir schon so viel Unterstützung gegeben hast, meinen Code einmal 
"sauber" aufzuschreiben. Vielleicht gibt es ja schon eine vordefinierte 
Konstante für die Page-Größe oder ähnliches.

Danke im Voraus,

der überaus dankbare Jacky

von Stefan E. (sternst)


Lesenswert?

> meinen Code einmal "sauber" aufzuschreiben

Ok, aber nur weil Wochenende ist. ;-)

1
    uint16_t page_addr;
2
3
    for (page_addr = 0; page_addr < sizeof(data)*2; page_addr += SPM_PAGESIZE) {
4
5
        boot_page_erase (page_addr);
6
        boot_spm_busy_wait ();
7
8
        for (uint8_t i = 0; i < SPM_PAGESIZE/2; i++)
9
            boot_page_fill (page_addr + (i * 2), data[i]);
10
11
        boot_page_write (page_addr);
12
        boot_spm_busy_wait ();
13
    }

So ganz "sauber" ist das aber auch nicht, denn der Code geht davon aus, 
dass data ein ganzzahliges Vielfaches der Page-Größe enthält, 
ansonsten wird die letzte Page mit Müll aufgefüllt.

von matze (Gast)


Lesenswert?

Erst einmal vielen Dank für die Mühe! :-)

Leider immernoch erfolglos:
1
void write_app()
2
{
3
  int data[] = 
4
  {
5
    0x12C0,0x19C0,0x18C0,0x17C0,0x16C0,0x15C0,0x14C0,0x13C0,
6
    0x12C0,0x11C0,0x10C0,0x0FC0,0x0EC0,0x0DC0,0x0CC0,0x0BC0,
7
    0x0AC0,0x09C0,0x08C0,0x1124,0x1FBE,0xCFE5,0xD4E0,0xDEBF,
8
    0xCDBF,0x02D0,0x1FC0,0xE4CF,0x84B3,0x8462,0x84BB,0xAA98,
9
    0xA09A,0x8A9A,0xA898,0x84B3,0x8861,0x84BB,0xAB98,0x28EC,
10
    0x30E0,0xAD9A,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
11
    0xD9F7,0xAD98,0x80ED,0x97E0,0xF901,0x3197,0xF1F7,0x0197,
12
    0xD9F7,0xEFCF,0xF894,0xFFCF,0xFFFF,0xFFFF,0xFFFF,0xFFFF
13
  };
14
15
  cli();
16
17
  uint16_t page_addr;
18
19
    for (page_addr = 0; page_addr < sizeof(data)*2; page_addr += SPM_PAGESIZE)
20
    {
21
        boot_page_erase (page_addr);
22
        boot_spm_busy_wait ();
23
24
        for (uint8_t i = 0; i < SPM_PAGESIZE/2; i++)
25
        {
26
            boot_page_fill (page_addr + (i * 2), data[i]);
27
        }
28
29
        boot_page_write (page_addr);
30
        boot_spm_busy_wait ();
31
    }
32
33
  sei();
34
}

Ergebnis im Flash:
1
:10000000C012C019C018C017C016C015C014C01344
2
:10001000C012C011C010C00FC00EC00DC00CC00B6C
3
:10002000C00AC009C0082411BE1FE5CFE0D4BFDE5E
4
:10003000BFCDD002C01FCFE4B3846284BB8498AA32
5
:10004000C012C019C018C017C016C015C014C01304
6
:10005000C012C011C010C00FC00EC00DC00CC00B2C
7
:10006000C00AC009C0082411BE1FE5CFE0D4BFDE1E
8
:10007000BFCDD002C01FCFE4B3846284BB8498AAF2
9
:10008000C012C019C018C017C016C015C014C013C4
10
:10009000C012C011C010C00FC00EC00DC00CC00BEC
11
:1000A000C00AC009C0082411BE1FE5CFE0D4BFDEDE
12
:1000B000BFCDD002C01FCFE4B3846284BB8498AAB2
13
:1000C000C012C019C018C017C016C015C014C01384
14
:1000D000C012C011C010C00FC00EC00DC00CC00BAC
15
:1000E000C00AC009C0082411BE1FE5CFE0D4BFDE9E
16
:1000F000BFCDD002C01FCFE4B3846284BB8498AA72

Daraufhin habe ich einen kleinen Fehler korrigiert, denn sizeof(data) 
gibt ja die größe des Arrays in Byte zurück und habe die äußere 
For-Schleife folgendermaßen modifiziert:

Aus
1
for (page_addr = 0; page_addr < sizeof(data)*2; page_addr += SPM_PAGESIZE)
wird
1
for (page_addr = 0; page_addr < sizeof(data)/sizeof(int)*2; page_addr += SPM_PAGESIZE)

Ergebnis im Flash:
1
:10000000C012C019C018C017C016C015C014C01344
2
:10001000C012C011C010C00FC00EC00DC00CC00B6C
3
:10002000C00AC009C0082411BE1FE5CFE0D4BFDE5E
4
:10003000BFCDD002C01FCFE4B3846284BB8498AA32
5
:10004000C012C019C018C017C016C015C014C01304
6
:10005000C012C011C010C00FC00EC00DC00CC00B2C
7
:10006000C00AC009C0082411BE1FE5CFE0D4BFDE1E
8
:10007000BFCDD002C01FCFE4B3846284BB8498AAF2

Leider immernoch falsch. :-(

von Stefan E. (sternst)


Lesenswert?

matze schrieb:

> Daraufhin habe ich einen kleinen Fehler korrigiert, denn sizeof(data)
> gibt ja die größe des Arrays in Byte zurück und habe die äußere
> For-Schleife folgendermaßen modifiziert:

Ups, sorry, als Korrektur solltest du aber einfach das *2 weglassen.

> Leider immernoch falsch. :-(

Ja.
data[i]
->
data[page_addr/2+i];

von jacky (Gast)


Lesenswert?

hmm... irgendwo ist noch der Wurm drin.

Jetzt erhalte ich:
1
:10000000C012C019C018C017C016C015C014C01344
2
:10001000C012C011C010C00FC00EC00DC00CC00B6C
3
:10002000C00AC009C0082411BE1FE5CFE0D4BFDE5E
4
:10003000BFCDD002C01FCFE4B3846284BB8498AA32
5
:100040009AA09A8A98A8B3846188BB8498ABEC285C
6
:10005000E0309AADED80E09701F99731F7F1970123
7
:10006000F7D998ADED80E09701F99731F7F1970155
8
:10007000F7D9CFEF94F8CFFFFFFFFFFFFFFFFFFFA0

wenn ich mein Test-Programm aber über den ISP aufspiele und dann den 
Flash auslese, dann erhalte ich:
1
:1000000012C019C018C017C016C015C014C013C044
2
:1000100012C011C010C00FC00EC00DC00CC00BC06C
3
:100020000AC009C008C011241FBECFE5D4E0DEBF5E
4
:10003000CDBF02D01FC0E4CF84B3846284BBAA9832
5
:10004000A09A8A9AA89884B3886184BBAB9828EC5C
6
:1000500030E0AD9A80ED97E0F9013197F1F7019723
7
:10006000D9F7AD9880ED97E0F9013197F1F7019755
8
:10007000D9F7EFCFF894FFCFFFFFFFFFFFFFFFFFA0

was ist das los?

von jacky (Gast)


Lesenswert?

Sind da etwa alle Bytes in den Wörtern verdreht?

von jacky (Gast)


Lesenswert?

Ja!

ich habe noch schnell ein paar Zeilen zum Drehen der Bytes eingefügt:
1
unsigned int word = data[page_addr/2+i];
2
unsigned int new_word = (word >> 8) | ((word << 8) & 0xFF00);
3
boot_page_fill (page_addr + (i * 2), new_word);

und jetzt funktioniert es!

Tausend Dank!!

von Stefan E. (sternst)


Lesenswert?

jacky schrieb:
> Sind da etwa alle Bytes in den Wörtern verdreht?

Natürlich, der AVR ist Little Endian.

> ich habe noch schnell ein paar Zeilen zum Drehen der Bytes eingefügt:

Wie wäre es statt dessen, data passend zu füllen? Oder das ganze auf 
Byte-Basis zu machen, statt auf Word-Basis?

von jacky (Gast)


Lesenswert?

Wie gesagt, das ist ja erstmal zum testweise beschreiben des AVR. Jetzt 
wird dann langsam ein "richtiger" Bootloader draus. :-)

Aber eine Frage noch: Warum funktioniert
1
void (*jump_to_app)(void) = 0x0000;
2
3
void write_app()
4
{
5
  int data[] = 
6
  {
7
    // ...
8
  };
9
10
  cli();
11
12
  for (...)
13
  {
14
      //...
15
  }
16
17
  jump_to_app();
18
  sei();
19
}
20
nicht?
21
22
Das hat zur Folge, dass der Bootloader immer wieder neustartet. Wenn ich dann allerdings das Fusebit BOOTRST lösche und den ATmega neustarte, dann startet die Anwendung ganz normal. Es scheint, als würde er an den Anfang des Bootloaders springen. Warum?

von jacky (Gast)


Lesenswert?

Nochmal, diesmal korrekt formatiert:
------------------------------------
Wie gesagt, das ist ja erstmal zum testweise beschreiben des AVR. Jetzt
wird dann langsam ein "richtiger" Bootloader draus. :-)

Aber eine Frage noch: Warum funktioniert
1
void (*jump_to_app)(void) = 0x0000;
2
3
void write_app()
4
{
5
  int data[] = 
6
  {
7
    // ...
8
  };
9
10
  cli();
11
12
  for (...)
13
  {
14
      //...
15
  }
16
17
  jump_to_app();
18
  sei();
19
}
nicht?

Das hat zur Folge, dass der Bootloader immer wieder neustartet. Wenn ich 
dann allerdings das Fusebit BOOTRST lösche und den ATmega neustarte, 
dann startet die Anwendung ganz normal. Es scheint, als würde er an den 
Anfang des Bootloaders springen. Warum?

von Stephan W. (stephan-w)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe bei mir ebenfalls ein Problem mit dem schreiben der Seite. Ich 
möchte ein Word-Muster (0x1234) eine Seite lang an den Anfang des Flashs 
schreiben... wenn ich mir abschließend einen Hexdump vom Flash erstelle 
(per AVR-Studio), dann kann ich dieses Word-Muster allerdings nicht 
wiederfinden :-(

Mal abgesehen davon, dass bei mir wahrscheinlich auch das little-Endian 
Problem zuschlägt, hätte ich zunächst mal gern irgend etwas in meinem 
Flash stehen gesehen...

wo liegt mein Fehler?

Danke für Eure Hilfe,

Gruß Stephan

von subitus (Gast)


Lesenswert?

Die generelle Vorgehensweise beim Umgang mit EEPROM/ Flash:

- Page löschen
- warten bis Löschvorgang fertig
- Page schreiben
- warten bis Schreibvorgang fertig
(- ggf. Sektion aktivieren - siehe AVR/ boot_rww_enable())
- lesen

MfG,
subitus

von Stephan (Gast)


Lesenswert?

danke Dir, genau da lag mein Fehler, habe die Reihenfolge nicht 
eingehalten!

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.