Forum: Compiler & IDEs ATtiny: Daten ins Flash schreiben.


von daniel (Gast)


Lesenswert?

Hi,

ich möchte gerne eine Tabelle in den Flash eines ATtiny44 schreiben. 
Diese Tabelle wird nur einmal geschrieben und dann nur wieder 
ausgelesen.

Das Lesen klappt schon wunderbar nur beim schreiben läuft es nicht so 
richtig. Folgender Code:
1
void write_page() {
2
  uint16_t wert = 0x55;
3
4
  //     Löschen der Page, testweise Page 12
5
  asm (
6
    "ldi r30, 0x80 \n\t"
7
    "ldi r31, 0x01 \n\t"
8
  );
9
  
10
  SPMCSR = 0b00000011;  // Kommando löschen
11
  asm volatile ("spm");  // ausführen
12
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
13
14
15
16
  //     Füllen des Pagebuffers
17
  for(uint8_t i=0; i <= SPM_PAGESIZE; i+=2) {  
18
     asm volatile(
19
       "movw r30, %[page_adresse] \n\t"      // adresse innerhalb der page
20
      "movw r0, %[tmp] \n\t"            // daten, testweise einfach 0x55
21
      :
22
      :[page_adresse] "w" (i), [tmp] "w" (wert)
23
     );
24
25
     SPMCSR = 0b00000001;          // kommando schreiben
26
     asm volatile("spm");          // ausführen
27
     while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
28
  }
29
30
31
  //     Schreiben der Page, testweise Page 12
32
  asm volatile (
33
    "ldi r30, 0x80 \n\t"  // hier wird die page ins Z-Register geladen Page: 12 
34
    "ldi r31, 0x01 \n\t"
35
  );
36
37
  SPMCSR = 0b00000101;      // Kommando für Page schreiben
38
  asm volatile ("spm");  // ausführen
39
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
40
}


Die Funktion läuft ohne Probleme durch, aber im Hex-File ist die ganze 
Page leer d.h. 0xFF ! Ich habe ganz nach dem Datenblatt gearbeitet 
(Self-Programming) => Löschen, Page befüllen und dann Page wegschreiben.

Die boot.h-Lib klappt auf den Tiny's nicht, habe ich schon ausprobiert.
Sieht irgendjemand den Fehler?


Gruss Daniel

: Verschoben durch Moderator
von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

daniel schrieb:
> Die boot.h-Lib klappt auf den Tiny's nicht, habe ich schon ausprobiert.

Was heißt das?

von daniel (Gast)


Lesenswert?

Die Funktion boot_rww_enable () aus der boot.h wird nicht 
durchkompiliert. Grund ist, dass es das RWWSRE Bit auf den Tiny's nicht 
gibt.
Alle AVR's die im Datenblatt nicht den Unterpunkt "Bootloader" haben, 
können diese Funktionen nach meinen Wissen nicht nutzen, somit ist man 
auf ein bisschen Assembler angewiesen.

Für Vorschläge bin ich natürlich gerne offen :)

von Knut B. (Firma: TravelRec.) (travelrec) Benutzerseite


Lesenswert?

daniel schrieb:
> ich möchte gerne eine Tabelle in den Flash eines ATtiny44 schreiben.
> Diese Tabelle wird nur einmal geschrieben und dann nur wieder
> ausgelesen.

Warum bindest Du die Tabelle nicht in den Programmcode ein? Muss sie zur 
Laufzeit erstellt werden? Es gäbe da auch noch das EEPROM...

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

daniel schrieb:
> Die Funktion boot_rww_enable () aus der boot.h wird nicht
> durchkompiliert. Grund ist, dass es das RWWSRE Bit auf den Tiny's nicht
> gibt.

Bitte schreib' einen Bugreport:

https://savannah.nongnu.org/bugs/?group=avr-libc

Bugs, die keiner kennt, können auch nicht repariert werden.

> Alle AVR's die im Datenblatt nicht den Unterpunkt "Bootloader" haben,
> können diese Funktionen nach meinen Wissen nicht nutzen, somit ist man
> auf ein bisschen Assembler angewiesen.

Der ganze Bootloader-Code braucht natürlich an manchen Stellen den
inline-Assembler, aber eben dafür ist ja <avr/boot.h> gedacht, dass
sich dieser Code dort befinden kann und ihn nicht jeder aufs Neue
selbst schreiben muss.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Als Würgaround: probier's mal mit:
1
#define RWWSRE CTPB

irgendwo oben im Code (vor dem ersten Aufruf von boot_rww_enable().

Wenn ich die Beschreibung zwischen einem ATmega48 und einem ATtinyX4
vergleiche, dann scheinen sie beide der gleichen Prozedur zu folgen,
aber dieses Bit (obwohl an der gleichen Stelle) ist umbenannt worden.

von daniel (Gast)


Lesenswert?

Super, vielen Dank für eure Antworten !
Es geht um eine Korrekturtabelle, welche beim flashen des Controllers 
einmal mit reingeladen wird. Zurzeit wird die Tabelle ins EEprom 
geladen, aber dies ist zu klein, weil ich jetzt gerne eine höhere 
Auflösung haben möchte.
Habe gerade gelesen, dass für das Selfprogramming ein Fuse-Bit gesetzt 
werden, damit das SPMRC Register aktiviert wird. Leider ohne Erfolg.

Ist mein Code denn soweit korrekt ?

Gruss und 73 Daniel, do1yds

von daniel (Gast)


Lesenswert?

FUNKTIONIERT =>
1
#include <avr/io.h>
2
#include <avr/boot.h>
3
4
#define RWWSRE CTPB
5
6
__attribute__ ((section (".bootloader")))
7
void write_page() {
8
  uint32_t page = 0x500;
9
  uint16_t wert = 0x3322;
10
   
11
  boot_page_erase(page);
12
  boot_spm_busy_wait();
13
  
14
  for(uint16_t i=0; i <SPM_PAGESIZE; i+=2) {
15
    boot_page_fill (page + i, wert);
16
  } 
17
18
  boot_page_write(page);
19
  boot_spm_busy_wait();
20
21
  boot_rww_enable(); 
22
}

War aber noch ein ziemlich blöder Fehler dabei, den ich hier nicht 
nennen möchte :)

Gruss Daniel

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

daniel schrieb:
> War aber noch ein ziemlich blöder Fehler dabei, den ich hier nicht
> nennen möchte :)

Warum nicht?  Aber bitte unabhängig davon den avr-libc-Bugreport
einkippen, damit das nicht vergessen wird.

von daniel (Gast)


Lesenswert?

Da meine Memory-Fenster im AVR-Studio nicht funktionieren (aus welchen 
Gründen auch immer), habe ich die Hex-Datei mit einem externen Programm 
eingelesen. Problem war: ich habe immer die Hex-Files mir angeguckt, 
welche auch auf den Attiny vom AVR-Studio programmiert wurden. Aber ich 
musste nachdem das Programm durchgelaufen ist, die HEX-Datei aus dem 
Flash lesen und dann mit HexViewer angucken. Das war eine kleine 
Dummheit.

Bugreport schreibe ich noch, gerade etwas wenig Zeit :)

73 de Daniel

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Danke für die Erklärung, ich dachte, es wäre am Beispielcode der
avr-libc was falsch oder dergleichen.

Wenn derartige kleine Dummheiten nicht immer im Wege wären, wäre
Programmierung ja auch ganz einfach. ;-)

von BlaBla (Gast)


Lesenswert?

Hallo!
Ich möchte den Thread wiederbeleben.
Leider komme ich nicht zum gewünschten Ergebnis. Der Flash wird zwar aus 
dem Programm beschrieben, aber eben nicht mit &H55. Ich verwende hier 
den ATtiny85.
1
#include <avr/io.h>
2
3
void write_page() {
4
  uint16_t wert = 0x55;
5
6
  //     Löschen der Page, testweise Page 12
7
  asm (
8
  "ldi r30, 0x80 \n\t"
9
  "ldi r31, 0x01 \n\t"
10
  );
11
  
12
  SPMCSR = (1 << PGERS) + (1 << SPMEN);  // Kommando löschen
13
  asm volatile ("spm");  // ausführen
14
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
15
16
17
18
  //     Füllen des Pagebuffers
19
  for(uint8_t i=0; i <= SPM_PAGESIZE; i+=2) {
20
    asm volatile(
21
    "movw r30, %[page_adresse] \n\t"      // adresse innerhalb der page
22
    "movw r0, %[tmp] \n\t"            // daten, testweise einfach 0x55
23
    :
24
    :[page_adresse] "w" (i), [tmp] "w" (wert)
25
    );
26
27
    SPMCSR = (1<<SPMEN);          // kommando schreiben
28
    asm volatile("spm");          // ausführen
29
    while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
30
  }
31
32
33
  //     Schreiben der Page, testweise Page 12
34
  asm volatile (
35
  "ldi r30, 0x80 \n\t"  // hier wird die page ins Z-Register geladen Page: 12
36
  "ldi r31, 0x01 \n\t"
37
  );
38
39
  SPMCSR = (1 << PGWRT) + (1 << SPMEN);      // Kommando für Page schreiben
40
  asm volatile ("spm");  // ausführen
41
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
42
}
43
44
int main(void)
45
{  
46
    write_page(); 
47
    while (1)
48
    {
49
    }
50
     
51
}
1
Und das ist das Ergebnis:
2
:10011000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF
3
:10012000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDF
4
:10013000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFCF
5
:10014000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBF
6
:10015000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFAF
7
:10016000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9F
8
:10017000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF8F
9
:10018000000102010401060108010A010C010E012F
10
:10019000100112011401160118011A011C011E019F
11
:1001A000200122012401260128012A012C012E010F
12
:1001B000300132013401360138013A013C013E017F
13
:1001C000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF3F
14
:1001D000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2F
15
:1001E000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF1F
16
:1001F000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0F

Ich würde mich freuen, wenn wir jemand einen Hinweis oder eine Korrektur 
geben könnte.

Gruß Bernd
aus dem sehr heißen Berlin.

von BlaBla (Gast)


Lesenswert?

Gelöst!

Die Clobber-Liste fehlt:
1
#include <avr/io.h>
2
3
void write_page() {
4
  uint16_t wert = 0xAA55;
5
6
  //     Löschen der Page, testweise Page 12
7
  asm (
8
  "ldi r30, 0x80 \n\t"
9
  "ldi r31, 0x01 \n\t"
10
  :
11
  :
12
  :"r30", "r31"
13
  );
14
  
15
  SPMCSR = (1 << PGERS) + (1 << SPMEN);  // Kommando löschen
16
  asm volatile ("spm");  // ausführen
17
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
18
19
20
21
  //     Füllen des Pagebuffers
22
  for(uint8_t i=0; i <= SPM_PAGESIZE; i+=2) {
23
    asm volatile(
24
    "movw r30, %[page_adresse] \n\t"      // adresse innerhalb der page
25
    "movw r0, %[tmp] \n\t"            // daten, testweise einfach 0x0055
26
    :
27
    :[page_adresse] "w" (i), [tmp] "w" (wert)
28
    :"r0", "r1", "r30", "r31"
29
    );
30
31
    SPMCSR = (1<<SPMEN);          // kommando schreiben
32
    asm volatile("spm");          // ausführen
33
    while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
34
  }
35
36
37
  //     Schreiben der Page, testweise Page 12
38
  asm volatile (
39
  "ldi r30, 0x80 \n\t"  // hier wird die page ins Z-Register geladen Page: 12
40
  "ldi r31, 0x01 \n\t"
41
  :
42
  :
43
  :"r30", "r31"
44
  );
45
46
  SPMCSR = (1 << PGWRT) + (1 << SPMEN);      // Kommando für Page schreiben
47
  asm volatile ("spm");  // ausführen
48
  while ((SPMCSR & (1<<SPMEN)) == 1);  // warten bis fertig
49
}
50
51
int main(void)
52
{  
53
    write_page(); 
54
    while (1)
55
    {
56
    }
57
     
58
}

von Falk B. (falk)


Lesenswert?

Naja, schön ist auch was anderes. Ich würde die Funktion rein in ASM 
schreiben, ist einfacher und deutlich übersichtlicher. Mit doc42055 von 
ex. Atmel geht das recht leicht.

von c-hater (Gast)


Lesenswert?

Falk B. schrieb:

> Naja, schön ist auch was anderes. Ich würde die Funktion rein in ASM
> schreiben, ist einfacher und deutlich übersichtlicher.

Ich bin echt überrascht, dass ein Regular von µc.net den Mut hat, sowas 
so offen zu schreiben. Das ist neu.

Es gab mehr als genug Threads, wo ähnliche Vorschläge meinerseits 
einfach wegzensiert wurden...

von BlaBla (Gast)


Lesenswert?

Falk B. schrieb:
> Naja, schön ist auch was anderes.

Das stimmt, aber hier ging es nicht um Schönheit, sondern um Funktion. 
Und der ursprüngliche Code funktionierte bei mir nicht. Über die Art und 
Weise unter GCC ASM-Code einzubinden muß man kein Wort verlieren. Ist 
wie alles in "C" unausgegorener Mist.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

BlaBla schrieb:
> Über die Art und Weise unter GCC ASM-Code einzubinden muß man kein Wort
> verlieren.

Vor allem nicht, wenn man keine Ahnung davon hat: dann fehlen einem
für solch eine Diskussion nämlich ganz eindeutig die Grundlagen.

Dann macht man das, was der TE am Ende auch getan hat, und lässt
die entsprechenden inline asm statements andere schreiben.  (Und
diejenigen, die sie dann schreiben dürfen, können sich freuen, dass
GCC einem so viel Flexibilität bei inline asm in die Hand gibt, dass
man den Optimierungen damit nicht im Weg herumstehen muss.)

von c-hater (Gast)


Lesenswert?

Jörg W. schrieb:

> Vor allem nicht, wenn man keine Ahnung davon hat: dann fehlen einem
> für solch eine Diskussion nämlich ganz eindeutig die Grundlagen.

Das glaube ich nicht. Tatsache ist vielmehr: Die Runtime-Umgebung des 
gcc verunmöglicht viele Optimierungen des Codes von ISRs, weil sie 
frecherweise alles an sich reißt, was das System an Resourcen (im 
konkreten Fall AVR8 natürlich vor allem: Registern) bietet und es keine 
einfachen Möglichkeiten gibt, ihr diesen Scheiss abzugewöhnen.

Globally reserved registers wären aber nunmal das, was dem 
Interruptsystem der AVR8 am allermeisten helfen könnte, schneller und 
bezüglich der Latenz unkritischer zu werden.

Ganz klar: das kann natürlich nur funktionieren, wenn man für den 
kompletten Code einer Anwendung die Quelltexte hat und nicht auf 
irgendwelche Binaries angewiesen ist (also hinzugelinkte libs)

Dass aber der gcc nichtmal für den Fall, das man tatsächlich vollkommen 
über den Code herrscht,  Möglichkeiten für die potentiell effektivsten 
Optimierungen von ISRs bereitstellt, ist eine absolute Schande.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> Das glaube ich nicht.

Ich auch nicht – ich weiß es. :-)

Unser Nutzer Blabla hat einfach keine Ahnung, was der Inline Assembler
des GCC kann und warum er so extrem parametrierbar ist.  Das muss er
auch nicht, denn diese Parametrierbarkeit ist in erster Linie für die
Macher der Bibliothek(en) gut – aber dann muss er diese natürlich auch
benutzen, statt irgendwelchen suboptimalen Code selbst zu hacken (oder
von anderen zu kopieren).

Wie gesagt: er hätte diesen Thread gar nicht aus der Kiste ausbuddeln
müssen, wenn er ihn sich zuvor bis zu Ende durchgelesen hätte.  Die
Auflösung des Knotens (die dem TE damals geholfen hatte) steht ja drin.

von Falk B. (falk)


Lesenswert?

@Jörg W. (dl8dtl) (Moderator) Benutzerseite

>Unser Nutzer Blabla hat einfach keine Ahnung, was der Inline Assembler
>des GCC kann und warum er so extrem parametrierbar ist.  Das muss er
>auch nicht, denn diese Parametrierbarkeit ist in erster Linie für die
>Macher der Bibliothek(en) gut – aber dann muss er diese natürlich auch
>benutzen, statt irgendwelchen suboptimalen Code selbst zu hacken (oder
>von anderen zu kopieren).

avr gcc inline assembler- Separates the boys from the men.

;-)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Falk B. schrieb:
> avr gcc inline assembler- Separates the boys from the men.

Smiley gesehen, dennoch: seine Parametrierung erlaubt es, den damit
injizierten Assemblercode so gut wie's geht an das anzupassen, was
der Compiler selbst erzeugt.  Je freigiebiger man dabei die Constraints
formulieren kann, um so größer die Chance, dass der Compiler unnützes
Umspeichern etc. vermeiden kann, weil er die Operanden bereits passend
in den Registern halten kann.

Auf einem klassischen x86 gibt's da ohnehin nicht viel Freizügigkeit,
aber gerade beim AVR-GCC mit seinen vielen Registern kann man an dieser
Stelle durchaus gewinnen.

von c-hater (Gast)


Lesenswert?

Jörg W. schrieb:

> aber gerade beim AVR-GCC mit seinen vielen Registern kann man an dieser
> Stelle durchaus gewinnen.

Und man könnte noch sehr viel mehr gewinnen, wenn man C im Allgemeinen 
und den gcc im Besonderen nicht zum Mass aller Dinge und zur unbedingten 
Notwendigkeit hochstilisieren würde...

Nein, schon konkurrierende C-Compiler zeigen die furchtbaren 
Einschränkungen des gcc insbesondere effizienten Code in ISRs 
betreffend.

Pure Asm natürlich sowieso...

Da hilft auch die Parametrierung des Code für Inline-Asm nicht. Es fehlt 
einfach nur die Möglichkeit, dem Codegenerator Register komplett zu 
entziehen, um wirklich effiziente ISRs schreiben zu können. Die 
Konkurrenz kann das seit langem, warum also nicht der gcc?

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

c-hater schrieb:
> furchtbaren Einschränkungen des gcc insbesondere effizienten Code in
> ISRs betreffend

… um die es in diesem Thread bloß absolut nicht geht.  (Davon abgesehen:
wirklich ineffizient ist er eigentlich nur für extrem kurze ISRs, die
man in der Praxis gar nicht so oft hat, und wenn, dann spricht nichts
dagegen, diese tatsächlich gleich als Assemblercode reinzuwerfen.)

Ansonsten habe ich auch schon „konkurrierende“ Inline-Assembler
gesehen, bei denen man erstmal merkt, was einem so ein GCC wirklich
bietet.  Da war nämlich das einzig garantierte Übergabeelement
zwischen C- und Assemblercode globale Variablen.  Auf Register kann
man natürlich zugreifen, aber irgendeine Garantie dafür, was man
in welchem Register findet, gab's gar nicht.  Kann also schon mit der
nächsten Compilerversion anders sein.

Du kannst mir gern andere Hochsprachen zeigen, die das viel besser
gelöst haben – über pure Assemblerprogrammierung für komplette Projekte
brauchen wir aber nicht mehr zu reden, die Zeiten sind vorbei, den
Aufwand kann und will keiner bezahlen.

ps:
> Die Konkurrenz kann das seit langem, warum also nicht der gcc?

Klar kann der das auch. Das Einzige, was eben nicht geht ist, dabei
eine vorcompilierte Bibliothek dazu zu linken, denn die müsste die
gleiche globale Reservierung des Registers natürlich zu ihrer
Compilezeit gesehen haben.  Es steht dir aber allemal frei, die
Bibliothek(en) mit der gleichen globalen Reservierung neu zu
compilieren, wenn du das willst.

: Bearbeitet durch Moderator
von Carl D. (jcw2)


Lesenswert?

Jörg W. schrieb:
> c-hater schrieb:
>> furchtbaren Einschränkungen
also fast lebensbedrohend, oder wie?

>> des gcc insbesondere effizienten Code in ISRs betreffend
>
> … um die es in diesem Thread bloß absolut nicht geht.  (Davon abgesehen:
> wirklich ineffizient ist er eigentlich nur für extrem kurze ISRs, die
> man in der Praxis gar nicht so oft hat, und wenn, dann spricht nichts
> dagegen, diese tatsächlich gleich als Assemblercode reinzuwerfen.)

Oder man benutzt 8.x, der kann leere ISR's zu einem IRET eindampfen.

Vielleicht lâst man das mit dem Interrupt baer auch gleich, wenn man eh 
nur das INT-Flag von a nach b kopieren will, um b (volatile) in der 
main-Loop abzufragen. Jede sinnvolle ISR braucht den "overhead" eh.
Ich vermute das ist in 8.x nur drin, weil damit die ewigen Nörgeler 
beruhigt werden sollen und weil der Maintaier zeigen wollte, daß es 
geht. Beides ist ok, nur bis dIe Zielgruppe das mitbekommt, wird noch 
einige Zeit vergehen.

: Bearbeitet durch User
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.