Forum: Mikrocontroller und Digitale Elektronik ARM malloc / Heap - problem lpc2378


von Marco .. (chico)


Lesenswert?

Ich nutze einen lpc2378 und als Basis das FatFS System von Chan / Martin 
Thomas 
http://www.siwawi.arubi.uni-kl.de/avr_projects/arm_projects/efsl_arm/lpc23xx_chan_mci_20080914.zip

Da der ARM seine Aufträge in Form einer doppelt verketteten Liste 
speichert (es sind structs) und diese Liste gleichzeitig eine 
Prioritätswarteschlange ist, muss ich sie leider mit malloc erstellen, 
da ich nur so effektiv das jeweils erste löschen und auf das nächste 
Objekt verlinken kann.

Problem: malloc liefert immer einen NULL pointer zurück.

Habe mir dann die LPC2368-ROM.ld angeschaut, dort finde ich folgenden 
Abschnitt:
1
.stack ALIGN(256) :
2
  {
3
    *(.stack)
4
  *(.STACK)
5
    PROVIDE (_stack = .);
6
    . = ALIGN(4);
7
  } > RAM
8
9
  _end = . ;
10
  PROVIDE (end = .);
11
  
12
  .heap :
13
  {
14
  *(.HEAP)
15
    . = ALIGN(4);
16
  } > RAM

Und in der startup_gnu.S
1
@@Heap_Size       EQU     0x00000000
2
@@                AREA    HEAP, NOINIT, READWRITE, ALIGN=3
3
@@Heap_Mem        SPACE   Heap_Size
4
5
.equ Heap_Size,   0x00001000
6
.section .HEAP, "w"
7
.align 3
8
HeapMem:
9
.if (Heap_Size>0)
10
.space Heap_Size
11
.endif

.equ Heap_Size,   0x00001000 stand vorher auf .equ Heap_Size, 
0x00000000, das habe ich nun geändert.
Beim Compilieren bekomme ich nun folgendes zurück:
1
section             size         addr
2
.text              25496            0
3
.data               2076   1073741824
4
.bss                5144   1073743900
5
.stack             10240   1073749248
6
.heap               4096   1073759488
7
.comment            1044            0
8
.debug_aranges      3040            0
9
.debug_pubnames     5726            0
10
.debug_info       109632            0
11
.debug_abbrev      21918            0
12
.debug_line        24580            0
13
.debug_frame        8784            0
14
.debug_str         12124            0
15
.debug_loc         35754            0
16
.ARM.attributes       16            0
17
.debug_ranges       3600            0
18
Total             273270
Gebracht hat es aber nichts.
mallinfo() gibt immernoch überall nur 0 0 0 0 0 0 0 ... zurück und die 
Pointer nach malloc bleiben bei NULL.

Habe ich noch etwas vergessen?

von Marco .. (chico)


Lesenswert?

Ich weiß man soll kein malloc benutzen, aber es gibt nur eine Funktion 
die malloc benutzt und auch nur einen struct, somit ist es recht 
überschaubar einen Überlauf zu verhindern. Man kann es später immernoch 
auf ein selbstverwaltetes array umschreiben, das dauert in meinen Augen 
aber ewig und vermiest die Codelesbarkeit.

Hab auch nirgendwo ein funktionierendes malloc Beispielprojekt im Netz 
gefunden was auf GCC basiert...

von Karl H. (kbuchegg)


Lesenswert?

Marco .. wrote:
> Ich weiß man soll kein malloc benutzen, aber es gibt nur eine Funktion
> die malloc benutzt und auch nur einen struct, somit ist es recht
> überschaubar einen Überlauf zu verhindern. Man kann es später immernoch
> auf ein selbstverwaltetes array umschreiben, das dauert in meinen Augen
> aber ewig und vermiest die Codelesbarkeit.

Nicht wirklich.

Wenn du nur einen einzigen Typ dieser 'dynamischen' Strukturen hast, 
dann ist es trivial sich einen malloc Ersatz zu bauen.
Du legst dir ein Array deiner Strukturen an, verpointerst sie 
entsprechend. Zusätzlich hast du noch einen Pointer, der die Freelist 
verkörpert, also alle Strukturen die vorrätig sind. Bei jeder 
Allokierung rückst du eine Struktur raus (sofern noch was da ist) und 
setzt den Freelist Pointer entsprechend nach. Bei jedem Free wird die 
Stuktur wieder in die Freelist eingekettet.
1
struct MyData {
2
3
   ...
4
   struct MyData* next;
5
};
6
7
#define MEMORY_SIZE 1024
8
struct MyData  memory[MEMORY_SIZE];    // Vorrat an 'Objekten'
9
struct MyData* freeList;
10
11
void initMemory()
12
{
13
  int i;
14
  for( i = 0; i < MEMORY_SIZE - 1; ++i )
15
    memory[i].next = &memory[i];
16
  memory[MEMORY_SIZE-1].next = NULL;
17
18
  freeList = &memory[0];
19
}
20
21
struct MyData* allocate()
22
{
23
  struct MyData* retVal = freeList;
24
  if( freeList )
25
    freeList = freeList->next;
26
  return retVal;
27
}
28
29
void dispose( struct MyData* garbage )
30
{
31
  if( garbage ) {
32
    garbage->next = freeList;
33
    freeList = garbage;
34
  }
35
}

Wenn man nicht allgemein sein muss, ist es leicht, schnelle Funktionen 
zu schreiben. Interessant wird es erst, wenn die Allokierung mit 
beliebigen Größen zurecht kommen muss :-)

von Marco .. (chico)


Lesenswert?

Vielen Dank!

Das sieht wirklich sehr gut aus. Gestern konnte ich mir einfach nicht 
vorstellen wie ich die free Liste verwalten soll, aber das hier ist ein 
wirklich schönes Beispiel.
Ich habe mir mal die Freiheit genommen es in meinen Code zu integrieren! 
;) Ein paar kleinere Fehler muss ich noch beheben bei mir, dann bin ich 
froh endlich zum nächsten Punkt auf der TODO-Liste überzugehen.

Falls ich trotzdem nochmal irgendwann in die Verlegenheit eines mallocs 
kommen sollte, vielleicht weiß noch jemand die Lösung warum es nicht 
geht?

Grüße

von Christian J. (elektroniker68)


Lesenswert?

Hallo,

warum soll malloc nicht funktionieren beim GCC? Ich habe bald die 
gleiche Aufgabe vor mir die Chan Fat ans Laufen zu bekommen, mir graust 
jetzt schon davor.

Es ist ein neues Sample Projekt auf der Homepage aufgetaucht, was auf 
einem 23xx laufen soll. Mir scheint der Code sehr stark von MT 
abgeschrieben zu sein aber wesentlich komnpakter und vor allem von allen 
Keil Funktionen befreit.

Mal eine allgemeine Frage:

Was macht dieses Demoprogramm? Ich habe so den Eindruck, dass es in 
einem Terminalfenster bestimmte Funktionen bereitstellt, so dass man 
Befehle eingeben kann, die auf die Karte bezogen werden.

von Marco .. (chico)


Lesenswert?

Ich weiß nicht ob es am GCC liegt, kann auch ein Fehler im Startup-Code 
des Projektes sein, dass das Heap anlegen verhindert. Ich muss nochmal 
die Variante von Chan ausprobieren. Er hat im Linker auch die 
Unterstützung für den Ethernet-RAM drin, was ich sowieso noch 
integrieren muss.

Hatte ich noch garnicht gesehen mit .0.07a, die wichtigste Neuerung in 
meinen Augen scheint die Unterstützung von langen Dateinamen zu sein, 
was mich aus Lizenzrechtlichen Gründen eher stutzig macht. Packt man es 
in ein kommerzielles Projekt, würde ich es eher bei der 0.06 belassen. 
Multitaskunterstützung ist natürlich auch nett.

Ja das ist richtig, du hast eine schicke Kommandozeile mit der du zum 
Beispiel die SD-Karte formatieren kannst (laut Empfehlung ist dann der 
Zugriff schneller als wenn man es auf einem PC formatiert), Datei 
anlegen und reinschreiben und auch einige Funktionen wie Uhrzeit setzen 
geben dir einen guten Einstieg um die Prozeduraufrufe besser zu 
verstehen. Nachdem ich meine Hardwareprobleme gelöst hatte und gemerkt 
habe, dass ich nur FlashMagic an UART0 bei meinem Olimex benutzen kann 
wegen der de-assert Mäglichkeit von RTS und CTS lief es eigentlich sogar 
auf Anhieb. LED-Adresse hab ich spaßeshalber noch angepasst und das 
wars.

Es wird 1000Mal pro Sekunde der Timer aufgerufen für diskio. In 
Wirklichkeit tut sich dort aber nix mehr, außer dass glaub ich eine 
Variable hochgezählt wird, die nirgendwo benutzt wird. Früher war das 
anders, ich werds demnächst mal rausnehmen und schauen was passiert.

von Marco .. (chico)


Lesenswert?

Ich vermisse irgendwo die Startadresse wo der Heap angelegt wird, oder 
ist das nicht nötig? Man kann nur die Größe angeben.

von Christian J. (elektroniker1968)


Lesenswert?

hallo,

ich habe ggrad mal versucht das Sample Projekt für den 2368 zu laden, 
was am 14.4. hochgeladen wurde. Erzeugt in der Rowley Oberfläche 
cryptische Fehler im Assemberteil, absolut keine Ahnung was da mit wem 
kollidiert, angeblich wäre das .vector Segment voll usw. Es ist zwar 
schön, wenn Leute ihre Codes ins Netz stellen aber mit Assemberdateien 
werden diese einfach inkompatibel zudem so gut wie keiner heute mehr 
Risc Assembler kann. Keine Ahnung wie ich das in Rowley importieren 
soll, die Oberfläche stellt startup.s, makefile usw bereit, der User 
sieht nur das Fronend aber nichts was auf Maschinenebene abläuft.

Wozu der Timer da läuft weiss ich auch nicht, für das Projekt absolut 
unsinnig, da der Betreib einer SD karte keine derart ressourcen 
fressende Sache braucht.

Trotzdem gut, dass Chan ein übersichtliches Projekt zusammen gestellt 
hat und diese 5 Diskio Funktionen mit fertigem Code gefüllt hat.

Heap kann ich bei mir in einem GDI einstellen, daraus wird das Makefile 
erzeugt, hilft Dir aber auch nicht weiter.

PS: Malloc klappt bei mir einwandfrei.

.heap 4000000c4 - 400004c3 (1024 Bytes)

testvec = (void*)malloc(100)
setzt testvec auf Adresse 0x40000458, also am oberen Rand des Heap

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

nochmal rauf damit. Wiese meckert der GCC diese Zeile an:

typedef enum { FALSE = 0, TRUE  } BOOL;

"expected identifier before ( token"

Abgesehen davon dass ich sowas gar nicht erst schreiben würde, was ist 
daran falsch? BOOL Funktionen können TRUE oder FALSE zurückliefern, also 
0 oder 1.

Habe das mal durch
typedef unsigned char BOOL

ersetzt, dann ist Ruhe aber ob das das Gleiche ist....

Ich drehe bald ab.......

von Marco .. (chico)


Lesenswert?

In der type.h bei Martin wird BOOL auch als unsigned char definiert, 
TRUE und FALSE dann noch als define. Ansonsten seh ich an dem enum 
eigentlich auch keinen Fehler. Vielleicht war TRUE und FALSE schon an 
anderer Stelle definiert und der Compiler weiß nur mal wieder nicht den 
wirklichen Fehler auszuspucken. Wäre zumindest möglich, denn sonst würde 
er ja jetzt sagen, dass er TRUE und FALSE nicht kennt.

Früher wurden über den Timer wohl Timeouts erkannt, aber jetzt...

das ist ja wirklich sehr ärgerlich (für mich) mit dem malloc, dann werd 
ich sein Beispiel auch mal ausprobieren. Aber ansonsten belasse ich es 
mit meinem Array. Ich muss noch Inkonsistenzen rausbekommen, da ich das 
erste Element in einem Interrupt entferne, einfügen findet aber im 
normalen Programmablauf statt. Ganz böse...

Ich hab grad gesehen, dass Chan zwar den Ethernet-RAM definiert, der 
Ethernet-Block aber in der startup.s überhaupt nicht aktiviert wird. 
Naja... bei KEIL gibts nen Beispiel, werd ich am Wochenende mal 
einbauen...

von Christian J. (elektroniker1968)


Lesenswert?

Hallo....

(4h später)

nachdem ich das Beispiel geradezu vergewaltigt habe, um es in mein 
Basisprojekt für mein Thailand-Board als Unterverzeichnis einzubauen bin 
ich zumindest soweit, dass die Karte initialisiert wird. Ausserdem habe 
ich alle WORD,DWORD und sonsstigen Murks durch die stdint.h Definitionen 
ersetzt usw. Kompiliert einwandfrei durch aber erzeugt mal eben 50kb 
neuen Code.

Wieso der GCC den nicht benutzen Code nicht ausblendet weiss ich nicht. 
Optimierung geht wegen eines Bugs (laut Rowley FAQ) nicht, sonst sind 
meine ISRs futsch. Ich erhalte dann ständig die Fehlermeldung: Writeback 
of Base registers is unpredictable.
1
Q: I'm getting a "Warning: writeback of base register is UNPREDICTABLE" 
2
message when compiling an interrupt handler. Why is this?
3
4
This indicates that you are compiling an interrupt handler function with 
5
optimization enabled.
6
7
It is a known problem with GCC that the optimizer often breaks interrupt 
8
handler function code so we recommend disabling optimization when 
9
compiling these functions. Note that this only applies to interrupt 
10
handler functions (i.e. functions marked with the __interrupt__ function 
11
attribute), it does not apply to regular functions that the interrupt 
12
handler may call.



Diese Funktion da unten ist sehr ärgerlich, weil sie jede Menge 
Rechenzeit frisst aber wenn Du Dich nicht detailliert mit den IO 
Funktionen in diskio.c auseinandersetzen willst, dann lass sie drin, die 
bedient die Timeout Timer, die dutzendfach verwendet werden.

Es werden wohl noch einige Tage ins Land ziehen, bevor ich das am Rennen 
habe.....
1
*-----------------------------------------------------------------------*/
2
/* Device Timer Interrupt Procedure  (Platform dependent)                */
3
/*-----------------------------------------------------------------------*/
4
/* This function must be called in period of 1ms                         */
5
6
void disk_timerproc (void)
7
{
8
  static uint8_t pv;
9
  uint8_t s, p;
10
  uint16_t n;
11
12
13
  /* 1000Hz decrement timers */
14
  if ((n = Timer[0]) > 0) Timer[0] = --n;
15
  if ((n = Timer[1]) > 0) Timer[1] = --n;
16
17
  p = pv;
18
  pv = SOCKPORT & (SOCKINS | SOCKWP);  /* Sample socket switch */
19
20
  if (p == pv) {          /* Contact stabled? */
21
    s = Stat;
22
23
    if (pv & SOCKWP)      /* WP is H (write protected) */
24
      s |= STA_PROTECT;
25
    else            /* WP is L (write enabled) */
26
      s &= ~STA_PROTECT;
27
28
    if (p & SOCKINS)      /* INS = H (Socket empty) */
29
      s |= (STA_NODISK | STA_NOINIT);
30
    else            /* INS = L (Card inserted) */
31
      s &= ~STA_NODISK;
32
33
    Stat = s;
34
  }
35
}

von Marco .. (chico)


Lesenswert?

Ich lern gern dazu, was für ein Thailand-Board?

Schau dir mal das selbe disk_timerproc bei Martin Thomas an, alles unter 
dem Timer decrement ist mit einem #if 0 auskommentiert. Timer1 wird 
verringert, aber Timer1 wird nirgendwo im Projekt auch nur einmal 
ausgelesen.
Somit nach einmal drüber sehen nutzlos. Auf jeden Fall hab ich hier im 
Forum schonmal gelesen, dass man zumindest das Timerintervall verringern 
kann und das bezog sich auf den Chan Originalcode.

von Christian J. (elektroniker1968)


Lesenswert?

http://cgi.ebay.de/ws/eBayISAPI.dll?ViewItem&item=170303047473

Schlage mich grad mit Fast GPIO und Slow GPIO herum, weil man die wohl 
nicht mischen kann und mein LCD Slow GPIO benutzt und ich keine Lust 
habe den nfertigen Code wieder umzuschreiben.... derzeit entweder SD 
Karte oder LCD.

Ich nutze den Code von MT nicht, ist mir zu undurchsichtig. Der von Chan 
im Sample projekt für LPC23xxx (wurde erst gestern hochgeladen!) ist 
besser strukturiert und basiert nicht auf Keil.

(kipp ins Bett)

von Christian J. (elektroniker1968)


Angehängte Dateien:

Lesenswert?

Hier isser....

von Marco .. (chico)


Lesenswert?

Hm, also da weiß ich, dass das geht.
SD-Karten Zugriff ist bei mir unabhängig ob GPIOM gesetzt ist oder 
nicht. Hab ich mit der Status LED probiert die statt 500ms, 100ms 
blinken sollte. GPIO war zu träge so dass sie nicht an ging, mit FGPIO 
lief es. SD-Karten Zugriff war zu keiner Zeit beeinflusst. MCI ist ja 
auch ne Zweitfunktion und ist völlig unabhängig von der GPIO-Config. 
Sonst müsste man ja auch Output-Richtung setzen etc.

Gruß, Marco

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

es läuft.....teilweise...

Die Chan Fat schmiert hier ab, wenn ich eine 2GB SD karte (Type: 4) 
einstecke, verliert sich vermutlich in einer Rekursion von diskio_ctl.

ergebnis = disk_ioctl(0, GET_BLOCK_SIZE, &p2);

Bei einer anderen 2GB (Type:2) läuft sie durch.

Tja, wird wohl kaum zu beheben sein das Problem.

von Marco .. (chico)


Lesenswert?

Schon beim Einstecken, Formatieren ist also garnicht erst möglich? Sehr 
merkwürdig. Ich nutze eine 4GB non-SDHC von Transcend und es tut ohne 
Probleme bisher, einen Dauertest setze ich aber für nächste Woche an.

Die hier: Conrad 411848   TRANSCEND SD KARTE 4GB 133X ULTRA S

Class 6... hat vor ner Weile aber noch deutlich weniger gekostet.

von Tilo (Gast)


Lesenswert?

Wie viel Ram hast du?
Benutzt du auch die Newlib?

Eventuell hast du das selbe Problem, dass ich hatte:
Beitrag "Problem mit malloc() und -O2"

von Christian J. (elektroniker1968)


Lesenswert?

Hallo,

ich habe die neue LFN Chan Fat auf meinem LPC2368 nachts durchlaufen 
lassen, die Karte mit Uhrzeiten in einem File beschrieben. Klappt soweit 
(ohne LFN!). MMC karten werden immer erkannt, bei "Typ 4" Karten (was 
immer das auch ist meckert er manchmal, dass er "diskio_ctl get block 
size" nicht ausführen kann, es kommt ein FP_NO_PATH Fehler aber eben nur 
manchmal. File öffnen, schreiben, zumachen geht aber. Die Gschwindigkeit 
habe ich auf 15 Mhz runtergedreht, ich halte 25 bei meinem Board bei 
5-7cm Leitungslänge für zuviel.

Heute werde ich mal den Code aufräumen, alles nur drangeklatscht 
gestern, hauptsache läuft. Wirklich befriedigend ist es leider nicht, 
die disk_io Funktionen für den LPC2368 verstehe ich nicht mal im Ansatz, 
Chan arbeitet sehr viel mit 2-Buchstaben Variablen, was es nicht gerade 
lesbarer macht.
Der ganze Ramsch mit dem SD Interface ist viel komplizierter als es mit 
der SPI war. Da war es eindeutig, Kommando raus, Datensatz oder response 
rein, habe ich damals selbst geschrieben und lief super. In die ff.c 
habe ich erst gar nicht reingeschaut, LFN läuft aber nicht, weil er 
ff_convert anmeckert, da ist ein chinesischer Zeichensatz in einer 
Datei, ich habe jedoch die Codepage auf US 437 eingestellt. Chan hat 
hier vermutlich einen Fehler gemacht, ff_convert wird ständig benutzt.

Hat schonmal jemand von Euch das LPC2368 SD Interface in seiner Tiefe 
durchdrungen und weiss es anzuwenden? Oder benutzt ihr auch Code, den 
ihr zwar nicht versteht, dessen API aber läuft?

Können wir evtl. einen neuen Thread aufmachen für die neue Chan FAT auf 
LPC ARM7 Controllern?

von Christian J. (elektroniker1968)


Angehängte Dateien:

Lesenswert?

Anbei mal ein Bild meines Bastel- Systems, vielleicht posten ja auch 
andere mal ihre.

von Marco .. (chico)


Lesenswert?

Ist übrigens schonmal jemandem aufgefallen, dass die Definition von
typedef unsigned short WORD
komischerweise beim compilieren nur 8 statt 16-Bit sind? Ein 
schwerwiegender Fehler! Da aber nirgendwo WORD benutzt wird, ist es wohl 
noch keinem aufgefallen. Liegt das am Compiler?


Christian J. wrote:
> Können wir evtl. einen neuen Thread aufmachen für die neue Chan FAT auf
> LPC ARM7 Controllern?

Da hab ich nix dagegen. Wenn du es eröffnet hast, dann kannst du es ja 
hier verlinken damit ich es nicht übersehe.

> Hat schonmal jemand von Euch das LPC2368 SD Interface in seiner Tiefe
> durchdrungen und weiss es anzuwenden? Oder benutzt ihr auch Code, den
> ihr zwar nicht versteht, dessen API aber läuft?

Naja, MCI, 4-Bit pro Takt da 4 Leitungen und das wars dann schon. Ich 
kenne aber einen der den Code von Martin durchgearbeitet und 
Fehlerbereinigt hat (er wird auch im Changelog erwähnt). Deswegen 
benutze ich diese stabile Version und nicht die direkt von Chan.


Tilo Lutz wrote:
> Wie viel Ram hast du?
> Benutzt du auch die Newlib?
>
> Eventuell hast du das selbe Problem, dass ich hatte:
> Beitrag "Problem mit malloc() und -O2"

32KByte RAM von denen 6,5KByte voll sind, 10KByte Stack. Das mit der 
Newlib ist ne gute Frage, benutze das yagarto Paket, ist das da mit bei?
Hm das Topic hatte ich schonmal über die Suche gefunden, aber hab leider 
vergessen das mal mit den Compiler-flags auszuprobieren. Werde ich jetzt 
mal tun.

von Marco .. (chico)


Lesenswert?

Marco .. wrote:
> Ist übrigens schonmal jemandem aufgefallen, dass die Definition von
> typedef unsigned short WORD
> komischerweise beim compilieren nur 8 statt 16-Bit sind? Ein
> schwerwiegender Fehler! Da aber nirgendwo WORD benutzt wird, ist es wohl
> noch keinem aufgefallen. Liegt das am Compiler?

Ok, Kommando zurück. Hab mich dadurch irritieren lassen, dass er wohl in 
structs bei BYTE ein WORD reserviert.

von Marco .. (chico)


Lesenswert?

Das Beispiel von Chan funktioniert bei mir überhaupt nicht.
Bekomme immer FR_NO_PATH oder FR_NOT_READY zurück.
Die Initialisierung bei ihm ist ja ein echter Krampf. Vor allem warum 
gibt er selbst diskio.c vor für die Implementierung der Routinen und 
packt selbst alles in die mci.c?

Hat es jemand zum laufen bekommen? Bei Martins Code musste ich auf der 
Konsole nur fi 0 eingeben und dann konnte ich schon mit fo 10 hallo eine 
datei öffnen und reinschreiben. Hier hagelt es auf meinem Olimex nur 
Fehler.

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.