Hi, Vermutung: eine Flash Konstante (also z.B. eine abgelegte Bitmap Grafik) darf nicht größer also 32.768 Byte sein. Ist dies korrekt? Weil (Config: WinAVR, Atmega128): const prog_uint16_t Grafik[16384] ={ 0x0000, 0x0000, 0x0000,..... (hier sind dann 16384 Worte abgelegt: 16384 x 2 = 32768 Byte) ergibt einen Fehler "error: size of variable 'Grafik' is too large" Ein Byte weniger: const prog_uint16_t Grafik[16383] ={ 0x0000, 0x0000, 0x0000,..... ist aber OK und funzt auch einwandfrei. http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Speicherzugriffe ist keine Hilfe. Mit "Static" habe ich vorsichthalber auch ausprobiert: keine Änderung. Jetzt stellen sich mir 2 Fragen: a) gibt es einen Workaround (außer die Daten auf mehrere Arrays aufzuteilen, was ich unbedingt vermeiden möchte)? b) Warum ist dies so? Vielleicht weil der Pointer intern mit als signed integer behandelt wird. Wenn ja: was für ein Blödsinn, wofür braucht man negative Zahlen bei einem Pointer. Wenn nein, was sonst? Also: b) wäre nett zu wissen, aber eine Antwort auf a) wäre mit vieeeel lieber. In Hoffnung auf Hilfe sage ich schonmal Danke!!!
Selbes Problem habe ich auch. Ich nehme 2 Arrays nacheinander und hoffe darauf das AVR-GCC sie mir nicht zerpflückt. Bisher hat das immer funktioniert. Worans liegt ? Wird wohl schon so sein das man intern mit signed 16 Bit arbeitet. Und die Programmierer dachten sich wohl das es niemals 32K große Daten im FLASH gibt weil es niemals 32K FLASHs geben kann, also ohne Weitblick drauflos. Eine Lösung/Erklärung habe ich bisher nicht gefunden. Gruß Hagen
Hagen Re wrote:
> Eine Lösung/Erklärung habe ich bisher nicht gefunden.
Ganz einfach, LPM arbeitet mit 2 Byte-Adressen und damit lassen sich nur
65536 Byte (= 32768 Worte) adressieren.
Darüber braucht man 3 Byte Pointer und ELPM.
Ups sehe gerade, es sind ja nur 16384 Worte, dann sollte es eigentlich
passen.
Peter
Das erklärt aber nicht, warum es bereits ab 32768 Bytes nicht klappt. Eine Grenze von 65536 wäre klar. Zwar nicht wegen LPM (damit wären erst 65537 Bytes ein Problem), sondern weil bei einem Array der Grösse N die Adresse &a[N] noch definiert und grösser als &a[N-1] sein muss.
Aus der InstructionSet Manual: Loads one byte pointed to by the Z-register into the destination register Rd. This instruction features a 100% space effective constant initialization or constant data fetch. The Program memory is organized in 16-bit words while the Z-pointer is a byte address. Thus, the least significant bit of the Z-pointer selects either low byte (ZLSB = 0) or high byte (ZLSB = 1). This instruction can address the first 64K bytes (32K words) of Program memory.
@Ingo: und was sagt uns das nun bei einer Variablen von 32768 Bytes?
Meine Vermutung: Der Array-Index darf maximal INT_MAX (== 32767) betragen; das durch 2 geteilt gibt 16383,5 (als Integer also 16383)...
Ich glaube, es hängt damit zusammen, dass die Indizierung eines Arrays entsprechend dem C-Standard implizit vom Typ "int" ist und damit vorzeichenbehaftet. Ich müsste aber nachlesen, wo das genau im Standard steht.
Ich denke auch dass es dort irgendwo zu suchen ist. Nur betrifft das Problem ja alle 16bit Maschinen, und dass PCs anno 80er und die PDP-11 auf der C entstand auch dahingehend beschrnäkt gewesen seien, daran kann ich mich nicht erinnern.
Ich kann's mir nur so erklären: 1/ Wie Peter schreibt geht der ROM-Zugriff über LPM. Damit kann man über Z grundsätzlich 64 KB Bytes adressieren (0 - 0xFFFF). 2/ Bei der Definition von Arrays gibt man eine nichtleere Anzahl von Elementen N an (C-Standard), d.h. unsigned. 3/ Beim Zugriff auf ein Array kann der Index allerdings signed sein. a[10] ist ebenso erlaubt wie a[-10]. Bei der Codeerzeugung müssen alle drei Constraints gleichzeitig eingehalten werden. Ich vermute: Wegen 3/ wird &a[0] nicht auf Z=0 gelegt sondern auf Z=0xFFFF/2. Dann ist nach 2/ N max. bei Bytezugriff 0xFFFF/2 und bei Wordzugriff 0xFFFF/2/2
@Hagen: Du sagst :"ich nehme 2 Arrays nacheinander und hoffe darauf das AVR-GCC sie mir nicht zerpflückt." Warum sollte GCC die nicht zerpflücken? Sie werden doch nicht zwangsläufig hintereinandergelegt. Ich habe es gerade mal ausprobiert: Wenn ich zwei Blöcke anlege (direkt hintereinander)z.B. const prog_uint16_t Test1[12000] = { 0x0000, 0x0000 ......... } const prog_uint16_t Test2[12000] = { 0x0000, 0x0000 ......... } anlege, und dann Test1 (mit pgm_read_word) auslese, dann sind bis zu Wort 12000 alle Daten OK. Lese ich weiter, kommt lange nur Unsinn und dann, viel später erst, fangen die Daten von Test2 an - dazwischen irgendwas anderes. Legst du die Daten vielleicht anders ab? Oder gibt es eine spezielle Option? sonst irgendein Tipp von jemanden, wie man mehr als 32768 Byte am Stück ablegen (meinetwegen auch 2 kleinere Arrays) und dann alles auf einmal am Stück auslesen kann?
Reserviere einfach einen Speicherbereich deiner Wahl und definiere eigene Pointer bastele ne Struktur und verwalte sie unabängig von der stdlib.lib. Schon bist die Sorgen los. Irgend wer schrieb Pointer vom Typ Int. Das dürfte es treffen. Und auf DOS-PCs war es genauso. Quickbasic hatte die gleiche Schranke und auch PDS Basic ich meine auch MS C.
Der Compiler liegt richtig. Die Zielmaschine muss bei einem Array mit N Elementen sowohl a[N]-a[0] als auch a[0]-a[N] berechnen können. Und zwar mit dem Typ "ptrdiff_t", d.h. mit "int". Da sie zuerst die Differenz ausrechnet, dann durch die Grösse eines Elementes dividiert, muss das Zwischenergebnis immer im Bereich -32768..+32767 liegen. Folge: Arrays können maximal 32767 Bytes lang sein, unabhängig vom Datentyp. Abhilfe: uint16_t a1[4096]; uint16_t a2[4096]; uint16_t a3[4096]; uint16_t a4[4096]; uint16_t *a[4] = { a1, a2, a3, a4 }; #define A(n) a[(n)/4096U][(n)%4096U]; Nicht schön, sollte aber funktionieren.
> ... a[N]-a[0] als auch a[0]-a[N] ...
... &a[N]-&a[0] als auch &a[0]-&a[N] ...
Das geht schief:
1 | #include <inttypes.h> |
2 | #include <avr/pgmspace.h> |
3 | |
4 | const prog_uint16_t Grafik[16384] = {[16383] = 0}; |
5 | |
6 | int main( void ) |
7 | {
|
8 | return 1; |
9 | }
|
Das funktioniert:
1 | #include <inttypes.h> |
2 | #include <avr/pgmspace.h> |
3 | |
4 | const prog_uint8_t Grafik[16384] = {[16383] = 0}; |
5 | |
6 | int main( void ) |
7 | {
|
8 | return 1; |
9 | }
|
Es liegt also nicht an der puren Größe des Feldes, sondern auch am Datentyp, da die Adressrechnung ja &a[0]-&a[N] intern als &a - &a+N*sizeof(T) durchgeführt wird. Eigentlich sollte das beim AVR Flash speicher auch mit uint16_t funktionieren, da der ja 16 Bit breit ist, tut es aber nicht. Warum? Da bin ich überfragt. BTW, das zweite Codesegment scheitert bei 32768.
> da die Adressrechnung ja intern als &a - &a+N*sizeof(T) > durchgeführt wird Kaum. Jedenfalls wenn die Adressen nicht konstant sind - das war nicht ganz so wörtlich gemein. Ausführlicher: uint16_t *p1 = ?; // Wert: &a[0], dem Compiler nicht bekannt uint16_t *p2 = ?; // Wert: &a[N], dem Compiler nicht bekannt Wenn nun p2 - p1 berechnet wird, dann wird erst subtrahiert, dann dividiert. Das Zwischenergebnis muss in ptrdiff_t passen. Im übrigen ist in deinem Beispiel prog_uint8_t Grafik[16384] die Gesamtgrösse trivialerweise < 32768 und somit problemlos. Wie ich oben schon schrieb, geht es um die Anzahl Bytes, nicht die Anzahl Elemente.
Hmm. Dass das im zweiten Beispiel problemlos ist, war ja gerade mein Punkt: Die Größe des Feldelementes spielt eben bei der Adressierung von Feldern eine Rolle. für a[n] muss eben erst einmal der Pointerwert (signed, da n auch <0 sein kann) bestimmt werden. Für Dein Beispiel p2-p1 ist die Größe des Datenelementes ja irrelevant. Aber ich fürchte wir kommen um das Lesen des Standards, wie Jörg vorgeschlagen hat, nicht herum.
Abschnitt 6.5.2.1 und 6.5.6 im Standard. Es wird mit der Größe des Typs multipliziert, wobei das Ergebnis in einen signed integer passen muss
Nach lesen und verstehen von A.K.s posting habe ich (=Dummbatz) festgestellt, dass wir dasselbe gemeint haben, nur mit anderen Worten.
hallo jungs, ich verstehe den zugriff von A.K. nicht. ich habe es mir so gedacht (in anlehnung an A.K.'s beispiel): const unsigned char sinus_1[20480] PROGMEM = {blabla}; const unsigned char sinus_2[20480] PROGMEM = {blublu}; const unsigned char *sinus[2] = {sinus_1, sinus_2}; #define SINUS(n) sinus[(n)/20480U][(n)%20480U] zugriff: PORTA = pgm_read_byte(SINUS(ptr_sinus)); ich weiss nicht ob es überhaupt so funktionieren kann. zudem ist mir das #define etwas schleierhaft, wie funktioniert das (makro-mäßig?)? gänzlich unklar ist mir der zugriff über pgm_read_byte (wegen dem 'makro') - der compiler schluckt es, aber rauskommen tut nur unsinn (bzw. garnix). kann mir das bitte jemand erklären oder korrigieren? grüße | pumpkin
nachtrag: ptr_sinus ist ein unsigned integer. pumpkin
pgm_read_byte will die Adresse: PORTA = pgm_read_byte(&SINUS(ptr_sinus)); Besser wär's aber, du verwendest eine Zweierpotenz statt 20480. Dividiert sich entschieden leichter.
moin A.K., hab ich versucht, hat gerade auch funktioniert. dann habe ich deinen tip beherzigt und meine arrays in 2er-potenzen zerteilt (10 mal 4096). nun nölt der compiler rum: ... Error: value of 73728 too large for field of 2 bytes at 1736 ... ... ... Error: Object file not found on exp loction... ... is schon klar, nebenan ( Beitrag "pgm_read_byte(); --> Nur für Lower 64k Flash (mega128)???" ) geht es ja um genau dieses problem (adresspointer reicht wohl nicht aus). also habe ich pgm_read_byte_far() bemüht. ohne ergebnis, er macht es nicht (da gibt er noch die warnung eines bedenklichen cast's aus). ich fummle schon den ganzen tag daran, so langsam hab ich keine lust mehr % ) . freue mich über jeden konstruktiven vorschlag. pumpkin
ok, komando zurück, er macht es, aber nur wenn ich ausschließlich diesen 40960er (10 * 4096) table reinlade. kredenze ich das ganze noch mir einem zweiten 40960er table blökt er rum (was mich doch sehr stutzig macht, ich denke ich kann so oder so nur bis 2^15 adressieren - da bin ich in beiden fällen drüber oder sehe ich hier was falsch?). woran liegt's? entnervter pumpkin
Mit 16 Bits lassen sich >64KB nun einmal nicht adressieren. 32 Bits nötig. Dazu gibt es die _far Versionen. Aber du bewegst dich in einem Bereich, für den AVR und insbesondere WinAVR nicht einfach gebaut sind, das wird daher hässlich.
okäse, ich dachte schon ich bin zu doof für den kram. das macht mir mut ; ) was würde abhilfe schaffen? den zweiten table komplett im upper 64k ablegen und dann mit _far drauf zugreifen? kann ich mit _far auch in den lower zugreifen (so wie ich es sehe ja, aber ich werd nicht so recht schlau aus der doku ( http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html )). hat jemand ein paar gute links? pumpkin
Wie deutlich muss ich eigentlich noch werden? Das Naheliegende wären 32-Bit Variablen für den Index.
moin, sei mir nicht böse, aber scheinbar musst du noch deutlicher werden. das problem ist ja, dass er es nicht in den progmem legen will wenn ich über 64k komme. das sollte er doch erstmal machen, um den zugriff kann ich mich noch später kümmern. was hat ein 32bit index mit dem ablegen zutun? pumpkin
Ich habs mal kurz getestet und konnte problemlos 4x20KB ablegen. Zugriff über pgm_read_byte_far und 32bit-Rechnung sah vom erzeugten Code her gut aus. Gelinkt hat er es auch ohne Meldung.
habe das update auf die neue WinAVR version gemacht. er meckert jetzt nicht mehr wegen dem fehlenden .elf rum. aber ablegen tut er es dennoch nicht. wiederum der gleiche fehler. seh ich den wald vor lauter bäumen nicht mehr? folgendes: - ich deklariere die tables im progmem aus externen textdateien (2 mal 10*4096 unsigned chars, also table1[40960] und table2[40960]) - danach füge ich diese zusammen nach deiner vorgabe: const unsigned char *sinus[10] = { sinus_40960_1, ... sinus_40960_10 }; const unsigned char *dreieck[10] = { dreieck_40960_1, ... dreieck_40960_10 }; - auf beide tables zugriff per pgm_read_byte_far( &DREIECK(ptr_ch_1) ); inklusive deines makros nun nölt der compiler rum dass im der cast im pgm__far nicht schmeckt. mir ist klar dass er eine long-adresse sehen will. ist mir doch aber relativ egal, weil er sich die adressen selber hinlegt wie er sie braucht - ich habe schließlich keinen einfluss auf den speicherbereich in welchen er die elemente legt?!? was &DREIECK(ptr_ch_1) auswirft kann ich imho nicht weissagen...oder doch? kann es sein dass er sich daran stört, dass ein_teil von dreieck[] oberhalb von 64k liegt und ein teil darunter? dümmlicher pumpkin
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.