mikrocontroller.net

Forum: Compiler & IDEs Arrays können max 32768 Byte groß sein. Warum nicht 65534?


Autor: Peek&Poke (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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-Tu... 
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!!!

Autor: Hagen Re (hagen)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Ingo Elsen (ogni42)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Ingo: und was sagt uns das nun bei einer Variablen von 32768 Bytes?

Autor: Patrick Dohmen (oldbug) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Meine Vermutung:

Der Array-Index darf maximal INT_MAX (== 32767) betragen; das durch 2 
geteilt gibt 16383,5 (als Integer also 16383)...

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Stefan (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Peek&Poke (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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?

Autor: Winfried J. (Firma: Nisch-Aufzüge) (winne)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> ... a[N]-a[0] als auch a[0]-a[N] ...

... &a[N]-&a[0] als auch &a[0]-&a[N] ...

Autor: Ingo Elsen (ogni42)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das geht schief:
#include <inttypes.h>
#include <avr/pgmspace.h>

const prog_uint16_t Grafik[16384] = {[16383] = 0};

int main( void )
{
  return 1;
}

Das funktioniert:
#include <inttypes.h>
#include <avr/pgmspace.h>

const prog_uint8_t Grafik[16384] = {[16383] = 0};

int main( void )
{
  return 1;
}

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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: Ingo Elsen (ogni42)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Ingo Elsen (ogni42)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Ingo Elsen (ogni42)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nach lesen und verstehen von A.K.s posting habe ich (=Dummbatz) 
festgestellt, dass wir dasselbe gemeint haben, nur mit anderen Worten.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
nachtrag:

ptr_sinus ist ein unsigned integer.

pumpkin

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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__... )).

hat jemand ein paar gute links?

pumpkin

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie deutlich muss ich eigentlich noch werden? Das Naheliegende wären 
32-Bit Variablen für den Index.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PS: WinAVR Jan/2007.

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.