Forum: Mikrocontroller und Digitale Elektronik eeprom & bootloader


von Frank Z. (frankza)


Lesenswert?

Hallo zusammen,
ich habe möchte gerne im Bootloader ( 
http://www.mikrocontroller.net/articles/Konzept_f%C3%BCr_einen_ATtiny-Bootloader_in_C) 
in das eeprom schreiben und lesen. Leider klappt das aber nicht. 
Schreibe ich den Code ohne den linkerzusatz 
"-Wl,--section-start=.text=0xBC0" klappt es.

Muss ich das eeprom anders addressieren oder was mache ich falsch?

Prozessor : Attiny45

Vielen Dank vorab.

Gruß

Frank

von knollo (Gast)


Lesenswert?

Hallo !
Ich hab' zwar nicht so viel Ahnung, aber gehört der Bootloader nicht 
in's Flash und der ATiny hat keine FUSES für einen Bootbereich, also 
geht das überhaupt ?

von Mein grosses V. (vorbild)


Lesenswert?

Frank Z. schrieb:
> ich habe möchte gerne im Bootloader (
> 
http://www.mikrocontroller.net/articles/Konzept_f%C3%BCr_einen_ATtiny-Bootloader_in_C)
> in das eeprom schreiben und lesen. Leider klappt das aber nicht.
> Schreibe ich den Code ohne den linkerzusatz
> "-Wl,--section-start=.text=0xBC0" klappt es.

Ich hab in diesem Projekt nicht gesehen, daß das R/W des EEPROM 
überhaupt implementiert ist. Was dann plötzlich funktionieren soll, ist 
mir daher auch rätselhaft.

Frank Z. schrieb:
> Muss ich das eeprom anders addressieren oder was mache ich falsch?

Du mußt die EEPROM-Funktionen implementieren. EEPROM und Flash sind zwei 
verschiedene Paar Schuhe.

knollo schrieb:
> Ich hab' zwar nicht so viel Ahnung, aber gehört der Bootloader nicht
> in's Flash und der ATiny hat keine FUSES für einen Bootbereich, also
> geht das überhaupt ?

Ja, mit ein bißchen Trickserei und Reset-/Interrupt-Vektoren verbiegen 
geht das.

: Bearbeitet durch User
von Axel S. (a-za-z0-9)


Lesenswert?

Frank Z. schrieb:
> ich habe möchte gerne im Bootloader (
> 
http://www.mikrocontroller.net/articles/Konzept_f%C3%BCr_einen_ATtiny-Bootloader_in_C)
> in das eeprom schreiben und lesen. Leider klappt das aber nicht.

Eine sehr ausführliche Beschreibung deines Problems hast du da 
abgeliefert. Ich liefere dir gerne eine ebenso ausführliche 
Ursachenanalyse: "du machst etwas falsch".

> Schreibe ich den Code ohne den linkerzusatz
> "-Wl,--section-start=.text=0xBC0" klappt es.
>
> Muss ich das eeprom anders addressieren oder was mache ich falsch?

Das Verschieben deines Codes an eine andere Adresse im Flash wird nicht 
die Funktion des Codes verändern. Das passiert auch in normalen 
Programmen laufend. Je nach Größe einzelner Programmteile werden 
Funktionen mal an die eine, mal an eine andere Adresse gelinkt. Trotzdem 
funktionieren diese Funktionen immer gleich. Du machst also ganz 
offensichtlich etwas falsch. Was genau, kann dir natürlich mangels 
Informationen keiner sagen.

von Frank Z. (frankza)


Lesenswert?

Hallo zusammen,

vielen Dank für Eure Antworten. Ich sehe ein, dass es tatsächlich nicht 
ganz so ausführlich ist. Dies möchte ich gerne hier noch einmal 
nachholen.

Wie oben gesagt, habe ich den bootloader aus dem Artikel implementiert. 
Es funktioniert auch alles einbandfrei. Da ich aber eine Variable 
innerhalb des Bootloader ins eeprom schreiben möchte, habe ich die 
folgenden Funktionen benutzt:
1
...
2
int main(void) {
3
....
4
 uint8_t eeFooByte  = 0x00;
5
 unsigned char myByte = 0x33;
6
 eeprom_write_byte(&eeFooByte, myByte);
7
....
8
}
ebenfalls habe ich folgendes wie im Datasheet beschrieben ausprobiert:
1
void EEPUT(int location, unsigned char byte)  {
2
 while (EECR & (1<<EEPE)); /* Check if EEPROM is ready*/
3
 EEAR = location; /* Write EEPROM address register*/
4
 EEDR = byte; /* Write EEPROM data register*/
5
 EECR |= (1<<EEMPE); /* Set master write enable signal*/
6
 EECR |= (1<<EEPE); /* Set write strobe*/
7
}
8
...
9
int main(void) {
10
....
11
 EEPUT(0x00, 0x33);
12
....
13
}

Beide Implementierungen funktionieren, wenn ich den Code nicht in einen 
höheren Bereich verschiebe. Dies machen ich per Linkeranweisung mit:
-Wl,--section-start=.text=0xBC0 -Wl,--section-start=.bootreset=0x00 
-Wl,--section-start=.data=0x800080

Wenn ich nur das nachfolgenen Elementes aus der Anweisung entferne, 
funktioniert der Code : -Wl,--section-start=.text=0xBC0 ( neue 
Linkeranweisung: -Wl,--section-start=.bootreset=0x00 
-Wl,--section-start=.data=0x800080)

Warum schreiben die o.a. Funktionen nach der Verschiebung des Codes 
nicht mehr ins eeprom.

Ich debuge das ganzen mit Atmel Studio und dem Dragon im OneWireDebug 
Modus.

Vielen dank noch mal vorab.

Gruß

Frank

von Mein grosses V. (vorbild)


Lesenswert?

Frank Z. schrieb:
> uint8_t eeFooByte  = 0x00;
>  unsigned char myByte = 0x33;
>  eeprom_write_byte(&eeFooByte, myByte);

eeFooByte ist eine Variable im RAM. Die hat zwar auch eine Adresse, die 
auf das EEPROM passen könnte, der Adressbereich fängt aber nicht bei 0 
an, sondern bei deinem Tiny bei 0x60. Mit einer RAM-Adresse sind die 
ersten 96 Byte des EEPROM gar nicht adressierbar und Zugriffe auf die 
letzten 96 Byte erfolgen im Nirwana.

Du musst die Variable also im EEPROM anlegen:
1
uint8_t eeFooByte EEMEM = 0;

Oder du sparst dir das und verwaltest die Adressen selbst:
1
eeprom_write_byte((unsigned char*)E2END, myByte);

Damit schreibst du bspw. auf die letzte Speicherstelle im EEPROM. Aber 
du schiesst dir ins Knie, wenn du statt char ein int dahin schreibst. 
Also überlasse es lieber dem Compiler.

Frank Z. schrieb:
> Warum schreiben die o.a. Funktionen nach der Verschiebung des Codes
> nicht mehr ins eeprom.

Keine Ahnung. Ist auch nicht wichtig, da du es grundsätzlich falsch 
machst.

von Axel S. (a-za-z0-9)


Lesenswert?

Frank Z. schrieb:
1
> ...
2
> int main(void) {
3
> ....
4
>  uint8_t eeFooByte  = 0x00;
5
>  unsigned char myByte = 0x33;
6
>  eeprom_write_byte(&eeFooByte, myByte);
7
> ....
8
> }

Das ist natürlich falsch, wie dein (unser?) Vorbild schon sagte. Der 
erste Parameter für eeprom_write_byte() ist ein Pointer (vulgo: eine 
Adresse) im EEPROM. Dazu kannst du die entsprechende Variable entweder 
in die entsprechende section legen (z.B. mit dem EEMEM Makro) oder du 
verwaltest die paar Adressen im EEPROM manuell und übergibst hier eine 
Zahl. Die erste Variante hat den Vorteil, daß der Compiler ein HEX File 
erzeugen kann mit dem du die initialen Werte im EEPROM setzen kannst.

> ebenfalls habe ich folgendes wie im Datasheet beschrieben ausprobiert:
1
> void EEPUT(int location, unsigned char byte)  {
2
>  while (EECR & (1<<EEPE)); /* Check if EEPROM is ready*/
3
>  EEAR = location; /* Write EEPROM address register*/
4
>  EEDR = byte; /* Write EEPROM data register*/
5
>  EECR |= (1<<EEMPE); /* Set master write enable signal*/
6
>  EECR |= (1<<EEPE); /* Set write strobe*/
7
> }
8
> ...
9
> int main(void) {
10
> ....
11
>  EEPUT(0x00, 0x33);
12
> ....
13
> }

Das hingegen könnte funktionieren; es schreibt deine 0x33 an die erste 
Adresse (0x00) des EEPROMs. Hier ist das Problem, daß du möglicherweise 
die Zeitanforderung von maximal 4 Takten zwischen dem Setzen von EEMPE 
und EEPE nicht einhältst. Deswegen ist dieser Teil besser in 
Inline-Assembler zu erledigen.

> Ich debuge das ganzen mit Atmel Studio und dem Dragon im OneWireDebug
> Modus.

Unter Debugger-Kontrolle ist es noch wahrscheinlicher, daß du die 
Zeitanforderung nicht einhältst.

Wenn du diesen Teil gefixt hast und es trotzdem nicht funktioniert, wäre 
es hilfreich, wenn du dein "geht nicht" noch etwas präzisierst. Was 
passiert genau? Steht nach dem Schreiben noch der alte Wert im EEPROM? 
Oder gar ein ganz anderer Wert? Mein Vorschlag wäre, das EEPROM einmal 
mit 0x00 und einmal mit 0xFF zu initialisieren (z.B. über ISP) und dann 
mit deinem Bootloader auf 0x33 zu schreiben. Danach haben wir einen 
etwas besseren Überblick was da passiert.

von Frank Z. (frankza)


Lesenswert?

Einen ganz grossen Dank für Eure Anmerkungen. Hat mir echt geholfen.

Der Fehler lag aber nicht an den eeprom Funktionen. Es lag daran, dass 
ich den Code zu weit nach hinten geschoben hatte. Grösse neue berechnet 
- und schon klappt es mit dem eeprom.

Wie gesagt - vielen Dank. Hat mir echt geholfen.

Gruß

Frank

von Walter S. (avatar)


Lesenswert?

Axel S. schrieb:
> Hier ist das Problem, daß du möglicherweise
> die Zeitanforderung von maximal 4 Takten zwischen dem Setzen von EEMPE
> und EEPE nicht einhältst. Deswegen ist dieser Teil besser in
> Inline-Assembler zu erledigen.

das könnte von moby oder chater sein ;-)
ne, das schafft der Compiler mit links in 4 Takten

von Axel S. (a-za-z0-9)


Lesenswert?

Frank Z. schrieb:
> Der Fehler lag ... nicht an den eeprom Funktionen. Es lag daran, dass
> ich den Code zu weit nach hinten geschoben hatte. Grösse neue berechnet
> - und schon klappt es mit dem eeprom.

Autsch. Dann hast du da noch einen Fehler in deiner Build-Umgebung. Wenn 
der Linker ein Executable erzeugt, das zu groß für den Ziel-µC ist, dann 
muß da eine Fehlermeldung aufpoppen.

von Axel S. (a-za-z0-9)


Lesenswert?

Walter S. schrieb:
> Axel S. schrieb:
>> Hier ist das Problem, daß du möglicherweise
>> die Zeitanforderung von maximal 4 Takten zwischen dem Setzen von EEMPE
>> und EEPE nicht einhältst. Deswegen ist dieser Teil besser in
>> Inline-Assembler zu erledigen.
>
> das könnte von moby oder chater sein ;-)

Also, das war ja wohl weit unter der Gürtellinie, Freundchen!
Ich erwarte eine schriftliche Entschuldigung bis morgen 9:00 Uhr!

> ne, das schafft der Compiler mit links in 4 Takten

Du kannst dich aber nicht darauf verlassen, daß das immer und mit allen 
Compilerschaltern so ist. So wie er dasteht, verläßt sich der Code auf 
ein Verhalten des Compilers, das nirgendwo zugesichert wird. Und weil C 
gar keine Ausdrucksmittel hat, um ein bestimmtes Zeitverhalten zu 
erzwingen, kann man das in reinem C gar nicht sauber implementieren. 
Deswegen mein Vorschlag für Inline-Assembler. Streng genommen gehört das 
auch in einen nicht-unterbrechbaren Block.

Und jetzt darfst du genau einmal raten, wie das in avr-libc 
implementiert ist. In C oder in Assembler?

von Walter S. (avatar)


Lesenswert?

Axel S. schrieb:
> Und jetzt darfst du genau einmal raten, wie das in avr-libc
> implementiert ist. In C oder in Assembler?

du darfst auch einmal raten wie es z.B. im Datenblatt des mega8 
vorgeschlagen wird
Theoretisch hast du ja recht, aber es gibt halt keinen üblichen 
C-Compiler der derart schlechten Code erzeugt

Axel S. schrieb:
> Streng genommen gehört das
> auch in einen nicht-unterbrechbaren Block.

das dagegen ist unbedingt notwendig

von Axel S. (a-za-z0-9)


Lesenswert?

Walter S. schrieb:
> Axel S. schrieb:
>> Und jetzt darfst du genau einmal raten, wie das in avr-libc
>> implementiert ist. In C oder in Assembler?
>
> du darfst auch einmal raten wie es z.B. im Datenblatt des mega8
> vorgeschlagen wird

Das muß ich nicht raten, das weiß ich. Aber der Beispielcode im 
Datenblatt ist auch eher als Illustration zum nebenstehenden Prosatext 
zu sehen denn als Anleitung, wie man das produktionsreif implementiert.

>> Streng genommen gehört das
>> auch in einen nicht-unterbrechbaren Block.
>
> das dagegen ist unbedingt notwendig

Und was sagt dir das jetzt, daß das im Datenblatt-Beispielcode auch 
nicht so gemacht wird? "Der da macht es aber auch falsch" ist keine 
akzeptable Entschuldigung.

von Walter S. (avatar)


Lesenswert?

Axel S. schrieb:
> Und was sagt dir das jetzt, daß das im Datenblatt-Beispielcode auch
> nicht so gemacht wird?

muss man ja auch nicht machen wenn man weiß dass keine Unterbrechungen 
vorkommen.
Aber ich bin dann hier raus, du darfst das letzte Wort haben.

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.